From 82942d9cce6b4c3b93d147bcac8ca4a599017fec Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Dec 1998 22:00:30 +0000 Subject: Initial checkin of distutils source files. --- __init__.py | 0 version.py | 301 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 301 insertions(+) create mode 100644 __init__.py create mode 100644 version.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/version.py b/version.py new file mode 100644 index 00000000..918a1ded --- /dev/null +++ b/version.py @@ -0,0 +1,301 @@ +# +# distutils/version.py +# +# Implements multiple version numbering conventions for the +# Python Module Distribution Utilities. +# +# written by Greg Ward, 1998/12/17 +# +# $Id$ +# + +"""Provides classes to represent module version numbers (one class for +each style of version numbering). There are currently two such classes +implemented: StrictVersion and LooseVersion. + +Every version number class implements the following interface: + * the 'parse' method takes a string and parses it to some internal + representation; if the string is an invalid version number, + 'parse' raises a ValueError exception + * the class constructor takes an optional string argument which, + if supplied, is passed to 'parse' + * __str__ reconstructs the string that was passed to 'parse' (or + an equivalent string -- ie. one that will generate an equivalent + version number instance) + * __repr__ generates Python code to recreate the version number instance + * __cmp__ compares the current instance with either another instance + of the same class or a string (which will be parsed to an instance + of the same class, thus must follow the same rules) +""" + +import string, re +from types import StringType + +class Version: + """Abstract base class for version numbering classes. Just provides + constructor (__init__) and reproducer (__repr__), because those + seem to be the same for all version numbering classes. + """ + + def __init__ (self, vstring=None): + if vstring: + self.parse (vstring) + + def __repr__ (self): + return "%s ('%s')" % (self.__class__.__name__, str (self)) + + +# Interface for version-number classes -- must be implemented +# by the following classes (the concrete ones -- Version should +# be treated as an abstract class). +# __init__ (string) - create and take same action as 'parse' +# (string parameter is optional) +# parse (string) - convert a string representation to whatever +# internal representation is appropriate for +# this style of version numbering +# __str__ (self) - convert back to a string; should be very similar +# (if not identical to) the string supplied to parse +# __repr__ (self) - generate Python code to recreate +# the instance +# __cmp__ (self, other) - compare two version numbers ('other' may +# be an unparsed version string, or another +# instance of your version class) + + +class StrictVersion (Version): + + """Version numbering for anal retentives and software idealists. + Implements the standard interface for version number classes as + described above. A version number consists of two or three + dot-separated numeric components, with an optional "pre-release" tag + on the end. The pre-release tag consists of the letter 'a' or 'b' + followed by a number. If the numeric components of two version + numbers are equal, then one with a pre-release tag will always + be deemed earlier (lesser) than one without. + + The following are valid version numbers (shown in the order that + would be obtained by sorting according to the supplied cmp function): + + 0.4 0.4.0 (these two are equivalent) + 0.4.1 + 0.5a1 + 0.5b3 + 0.5 + 0.9.6 + 1.0 + 1.0.4a3 + 1.0.4b1 + 1.0.4 + + The following are examples of invalid version numbers: + + 1 + 2.7.2.2 + 1.3.a4 + 1.3pl1 + 1.3c4 + + The rationale for this version numbering system will be explained + in the distutils documentation. + """ + + version_re = re.compile (r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', + re.VERBOSE) + + + def parse (self, vstring): + match = self.version_re.match (vstring) + if not match: + raise ValueError, "invalid version number '%s'" % vstring + + (major, minor, patch, prerelease, prerelease_num) = \ + match.group (1, 2, 4, 5, 6) + + if patch: + self.version = tuple (map (string.atoi, [major, minor, patch])) + else: + self.version = tuple (map (string.atoi, [major, minor]) + [0]) + + if prerelease: + self.prerelease = (prerelease[0], string.atoi (prerelease_num)) + else: + self.prerelease = None + + + def __str__ (self): + + if self.version[2] == 0: + vstring = string.join (map (str, self.version[0:2]), '.') + else: + vstring = string.join (map (str, self.version), '.') + + if self.prerelease: + vstring = vstring + self.prerelease[0] + str (self.prerelease[1]) + + return vstring + + + def __cmp__ (self, other): + if isinstance (other, StringType): + other = StrictVersion (other) + + compare = cmp (self.version, other.version) + if (compare == 0): # have to compare prerelease + + # case 1: neither has prerelease; they're equal + # case 2: self has prerelease, other doesn't; other is greater + # case 3: self doesn't have prerelease, other does: self is greater + # case 4: both have prerelease: must compare them! + + if (not self.prerelease and not other.prerelease): + return 0 + elif (self.prerelease and not other.prerelease): + return -1 + elif (not self.prerelease and other.prerelease): + return 1 + elif (self.prerelease and other.prerelease): + return cmp (self.prerelease, other.prerelease) + + else: # numeric versions don't match -- + return compare # prerelease stuff doesn't matter + + +# end class StrictVersion + + +# The rules according to Greg Stein: +# 1) a version number has 1 or more numbers separate by a period or by +# sequences of letters. If only periods, then these are compared +# left-to-right to determine an ordering. +# 2) sequences of letters are part of the tuple for comparison and are +# compared lexicographically +# 3) recognize the numeric components may have leading zeroes +# +# The LooseVersion class below implements these rules: a version number +# string is split up into a tuple of integer and string components, and +# comparison is a simple tuple comparison. This means that version +# numbers behave in a predictable and obvious way, but a way that might +# not necessarily be how people *want* version numbers to behave. There +# wouldn't be a problem if people could stick to purely numeric version +# numbers: just split on period and compare the numbers as tuples. +# However, people insist on putting letters into their version numbers; +# the most common purpose seems to be: +# - indicating a "pre-release" version +# ('alpha', 'beta', 'a', 'b', 'pre', 'p') +# - indicating a post-release patch ('p', 'pl', 'patch') +# but of course this can't cover all version number schemes, and there's +# no way to know what a programmer means without asking him. +# +# The problem is what to do with letters (and other non-numeric +# characters) in a version number. The current implementation does the +# obvious and predictable thing: keep them as strings and compare +# lexically within a tuple comparison. This has the desired effect if +# an appended letter sequence implies something "post-release": +# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002". +# +# However, if letters in a version number imply a pre-release version, +# the "obvious" thing isn't correct. Eg. you would expect that +# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison +# implemented here, this just isn't so. +# +# Two possible solutions come to mind. The first is to tie the +# comparison algorithm to a particular set of semantic rules, as has +# been done in the StrictVersion class above. This works great as long +# as everyone can go along with bondage and discipline. Hopefully a +# (large) subset of Python module programmers will agree that the +# particular flavour of bondage and discipline provided by StrictVersion +# provides enough benefit to be worth using, and will submit their +# version numbering scheme to its domination. The free-thinking +# anarchists in the lot will never give in, though, and something needs +# to be done to accomodate them. +# +# Perhaps a "moderately strict" version class could be implemented that +# lets almost anything slide (syntactically), and makes some heuristic +# assumptions about non-digits in version number strings. This could +# sink into special-case-hell, though; if I was as talented and +# idiosyncratic as Larry Wall, I'd go ahead and implement a class that +# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is +# just as happy dealing with things like "2g6" and "1.13++". I don't +# think I'm smart enough to do it right though. +# +# In any case, I've coded the test suite for this module (see +# ../test/test_version.py) specifically to fail on things like comparing +# "1.2a2" and "1.2". That's not because the *code* is doing anything +# wrong, it's because the simple, obvious design doesn't match my +# complicated, hairy expectations for real-world version numbers. It +# would be a snap to fix the test suite to say, "Yep, LooseVersion does +# the Right Thing" (ie. the code matches the conception). But I'd rather +# have a conception that matches common notions about version numbers. + +class LooseVersion (Version): + + """Version numbering for anarchists and software realists. + Implements the standard interface for version number classes as + described above. A version number consists of a series of numbers, + separated by either periods or strings of letters. When comparing + version numbers, the numeric components will be compared + numerically, and the alphabetic components lexically. The following + are all valid version numbers, in no particular order: + + 1.5.1 + 1.5.2b2 + 161 + 3.10a + 8.02 + 3.4j + 1996.07.12 + 3.2.pl0 + 3.1.1.6 + 2g6 + 11g + 0.960923 + 2.2beta29 + 1.13++ + 5.5.kw + 2.0b1pl0 + + In fact, there is no such thing as an invalid version number under + this scheme; the rules for comparison are simple and predictable, + but may not always give the results you want (for some definition + of "want"). + """ + + component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) + + def __init__ (self, vstring=None): + if vstring: + self.parse (vstring) + + + def parse (self, vstring): + # I've given up on thinking I can reconstruct the version string + # from the parsed tuple -- so I just store the string here for + # use by __str__ + self.vstring = vstring + components = filter (lambda x: x and x != '.', + self.component_re.split (vstring)) + for i in range (len (components)): + try: + components[i] = int (components[i]) + except ValueError: + pass + + self.version = components + + + def __str__ (self): + return self.vstring + + + def __repr__ (self): + return "LooseVersion ('%s')" % str (self) + + + def __cmp__ (self, other): + if isinstance (other, StringType): + other = LooseVersion (other) + + return cmp (self.version, other.version) + + +# end class LooseVersion -- cgit v1.2.1 From 5e72268255a51c78cbeb6ef82a66af3c7b90cef8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Dec 1998 23:46:33 +0000 Subject: Fred's sysconfig module. --- sysconfig.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 sysconfig.py diff --git a/sysconfig.py b/sysconfig.py new file mode 100644 index 00000000..b5fe1874 --- /dev/null +++ b/sysconfig.py @@ -0,0 +1,106 @@ +"""Prototype sysconfig module that loads information when run as a script, +but only defines constants when imported. + +This should be run as a script as one of the last steps of the Python +installation process. + +Written by: Fred L. Drake, Jr. +Email: +Initial date: 17-Dec-1998 +""" + +__version__ = "$Revision$" + + +def _init_posix(): + import os + import re + import sys + + g = globals() + + version = sys.version[:3] + config_dir = os.path.join( + sys.exec_prefix, "lib", "python" + version, "config") + + # load the installed config.h: + define_rx = re.compile("#define ([A-Z][A-Z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Z0-9_]+) [*]/\n") + fp = open(os.path.join(config_dir, "config.h")) + + while 1: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + if v == "1": + g[n] = 1 + else: + g[n] = v + else: + m = undef_rx.match(line) + if m: + g[m.group(1)] = 0 + + # load the installed Makefile.pre.in: + variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)\n") + done = {} + notdone = {} + fp = open(os.path.join(config_dir, "Makefile")) + + while 1: + line = fp.readline() + if not line: + break + m = variable_rx.match(line) + if m: + n, v = m.group(1, 2) + if "$" in v: + notdone[n] = v + else: + done[n] = v + + # do variable interpolation here + findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") + findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + while notdone: + for name in notdone.keys(): + value = notdone[name] + m = findvar1_rx.search(value) + if not m: + m = findvar2_rx.search(value) + if m: + n = m.group(1) + if done.has_key(n): + after = value[m.end():] + value = value[:m.start()] + done[n] + after + if "$" in after: + notdone[name] = value + else: + done[name] = value + del notdone[name] + elif notdone.has_key(n): + # get it on a subsequent round + pass + else: + done[n] = "" + after = value[m.end():] + value = value[:m.start()] + after + if "$" in after: + notdone[name] = value + else: + done[name] = value + del notdone[name] + else: + del notdone[name] + + # save the results in the global dictionary + g.update(done) + + +import os +exec "_init_%s()" % os.name +del os +del _init_posix -- cgit v1.2.1 From 53be0347c57aecd9b1144d71edeb727f86d6ffa0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 22 Dec 1998 12:42:04 +0000 Subject: Applied Fred's patch to fix the bugs that John Skaller noticed. --- sysconfig.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index b5fe1874..3eee9b35 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -15,6 +15,7 @@ __version__ = "$Revision$" def _init_posix(): import os import re + import string import sys g = globals() @@ -35,10 +36,9 @@ def _init_posix(): m = define_rx.match(line) if m: n, v = m.group(1, 2) - if v == "1": - g[n] = 1 - else: - g[n] = v + try: v = string.atoi(v) + except ValueError: pass + g[n] = v else: m = undef_rx.match(line) if m: @@ -57,9 +57,12 @@ def _init_posix(): m = variable_rx.match(line) if m: n, v = m.group(1, 2) + v = string.strip(v) if "$" in v: notdone[n] = v else: + try: v = string.atoi(v) + except ValueError: pass done[n] = v # do variable interpolation here @@ -79,7 +82,9 @@ def _init_posix(): if "$" in after: notdone[name] = value else: - done[name] = value + try: value = string.atoi(value) + except ValueError: pass + done[name] = string.strip(value) del notdone[name] elif notdone.has_key(n): # get it on a subsequent round @@ -91,9 +96,12 @@ def _init_posix(): if "$" in after: notdone[name] = value else: - done[name] = value + try: value = string.atoi(value) + except ValueError: pass + done[name] = string.strip(value) del notdone[name] else: + # bogus variable reference; just drop it since we can't deal del notdone[name] # save the results in the global dictionary -- cgit v1.2.1 From bb0ab0bba658a8f0467ed870bcc687aab0d1d4ff Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 6 Jan 1999 14:46:06 +0000 Subject: Another patch from Fred: factored _init_posix into get_config_h_filename, get_makefile_filename, parse_config_h, and parse_makefile. --- sysconfig.py | 55 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 3eee9b35..1d8231ab 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -11,24 +11,26 @@ Initial date: 17-Dec-1998 __version__ = "$Revision$" +import os +import re +import string +import sys -def _init_posix(): - import os - import re - import string - import sys - g = globals() +def get_config_h_filename(): + return os.path.join(sys.exec_prefix, "lib", "python" + sys.version[:3], + "config", "config.h") - version = sys.version[:3] - config_dir = os.path.join( - sys.exec_prefix, "lib", "python" + version, "config") +def get_makefile_filename(): + return os.path.join(sys.exec_prefix, "lib", "python" + sys.version[:3], + "config", "Makefile") - # load the installed config.h: +def parse_config_h(fp, g=None): + if g is None: + g = {} define_rx = re.compile("#define ([A-Z][A-Z0-9_]+) (.*)\n") undef_rx = re.compile("/[*] #undef ([A-Z][A-Z0-9_]+) [*]/\n") - fp = open(os.path.join(config_dir, "config.h")) - + # while 1: line = fp.readline() if not line: @@ -43,13 +45,15 @@ def _init_posix(): m = undef_rx.match(line) if m: g[m.group(1)] = 0 + return g - # load the installed Makefile.pre.in: +def parse_makefile(fp, g=None): + if g is None: + g = {} variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)\n") done = {} notdone = {} - fp = open(os.path.join(config_dir, "Makefile")) - + # while 1: line = fp.readline() if not line: @@ -106,9 +110,24 @@ def _init_posix(): # save the results in the global dictionary g.update(done) + return g -import os -exec "_init_%s()" % os.name -del os +def _init_posix(): + g = globals() + # load the installed config.h: + parse_config_h(open(get_config_h_filename()), g) + # load the installed Makefile.pre.in: + parse_makefile(open(get_makefile_filename()), g) + + + +try: + exec "_init_" + os.name +except NameError: + # not needed for this platform + pass +else: + exec "_init_%s()" % os.name + del _init_posix -- cgit v1.2.1 From 4698daf69a1f9760e8870609c995bb18c14c7a62 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Wed, 6 Jan 1999 16:28:34 +0000 Subject: Update and add docstrings. --- sysconfig.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 1d8231ab..04551a77 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -1,8 +1,5 @@ -"""Prototype sysconfig module that loads information when run as a script, -but only defines constants when imported. - -This should be run as a script as one of the last steps of the Python -installation process. +"""Provide access to Python's configuration information. The specific names +defined in the module depend heavily on the platform and configuration. Written by: Fred L. Drake, Jr. Email: @@ -18,14 +15,20 @@ import sys def get_config_h_filename(): + """Return full pathname of installed config.h file.""" return os.path.join(sys.exec_prefix, "lib", "python" + sys.version[:3], "config", "config.h") def get_makefile_filename(): + """Return full pathname of installed Makefile from the Python build.""" return os.path.join(sys.exec_prefix, "lib", "python" + sys.version[:3], "config", "Makefile") def parse_config_h(fp, g=None): + """Parse a config.h-style file. A dictionary containing name/value + pairs is returned. If an optional dictionary is passed in as the second + argument, it is used instead of a new dictionary. + """ if g is None: g = {} define_rx = re.compile("#define ([A-Z][A-Z0-9_]+) (.*)\n") @@ -48,6 +51,10 @@ def parse_config_h(fp, g=None): return g def parse_makefile(fp, g=None): + """Parse a Makefile-style file. A dictionary containing name/value + pairs is returned. If an optional dictionary is passed in as the second + argument, it is used instead of a new dictionary. + """ if g is None: g = {} variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)\n") @@ -114,6 +121,7 @@ def parse_makefile(fp, g=None): def _init_posix(): + """Initialize the module as appropriate for POSIX systems.""" g = globals() # load the installed config.h: parse_config_h(open(get_config_h_filename()), g) -- cgit v1.2.1 From 18d23e41a229ed4ec07f17ed7499bd1cdbfab606 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Mon, 11 Jan 1999 15:34:55 +0000 Subject: get_config_h_filename(): Fix to work with current Python installations; it was picking up a stale config.h from an overwritten installation. --- sysconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 04551a77..e71ae466 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -16,8 +16,8 @@ import sys def get_config_h_filename(): """Return full pathname of installed config.h file.""" - return os.path.join(sys.exec_prefix, "lib", "python" + sys.version[:3], - "config", "config.h") + return os.path.join(sys.exec_prefix, "include", "python" + sys.version[:3], + "config.h") def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" -- cgit v1.2.1 From 7c5088ade77bf14bf5ca03309fd684093733c1e9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 13 Jan 1999 16:12:04 +0000 Subject: Added: mems.lib.text_file: provides TextFile class for parsing text files with (optional) comment stripping, blank line skipping, whitespace removal, and line joining with trailing backslashes. --- text_file.py | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 text_file.py diff --git a/text_file.py b/text_file.py new file mode 100644 index 00000000..9fff941e --- /dev/null +++ b/text_file.py @@ -0,0 +1,206 @@ +"""text_file + +provides the TextFile class, which gives an interface to text files +that (optionally) takes care of stripping comments, ignoring blank +lines, and joining lines with backslashes.""" + +# created 1999/01/12, Greg Ward + +__revision__ = "$Id$" + +from types import * +import os, string, re + + +class TextFile: + filename = None + file = None + current_line = None + + default_options = { 'strip_comments': 1, + 'comment_re': re.compile (r'\s*#.*'), + 'skip_blanks': 1, + 'join_lines': 0, + 'lstrip_ws': 0, + 'rstrip_ws': 1, + } + + def __init__ (self, filename=None, **options): + + # set values for all options -- either from client option hash + # or fallback to default_options + for opt in self.default_options.keys(): + if options.has_key (opt): + if opt == 'comment_re' and type (options[opt]) is StringType: + self.comment_re = re.compile (options[opt]) + else: + setattr (self, opt, options[opt]) + + else: + setattr (self, opt, self.default_options[opt]) + + # sanity check client option hash + for opt in options.keys(): + if not self.default_options.has_key (opt): + raise KeyError, "invalid TextFile option '%s'" % opt + + self.filename = filename + if self.filename: + self.open () + + + def open (self, filename=None): + if not self.filename: + if not filename: + raise RuntimeError, "must provide a filename somehow" + + self.filename = filename + + self.file = open (self.filename, 'r') + self.current_line = 0 + + + def close (self): + self.file.close () + self.file = None + self.filename = None + self.current_line = None + + + def readline (self): + + buildup_line = '' + + while 1: + # read the line, optionally strip comments + line = self.file.readline() + if self.strip_comments and line: + line = self.comment_re.sub ('', line) + + # did previous line end with a backslash? then accumulate + if self.join_lines and buildup_line: + # oops: end of file + if not line: + self.warn ("continuation line immediately precedes " + "end-of-file") + return buildup_line + + line = buildup_line + line + + # careful: pay attention to line number when incrementing it + if type (self.current_line) is ListType: + self.current_line[1] = self.current_line[1] + 1 + else: + self.current_line = [self.current_line, self.current_line+1] + # just an ordinary line, read it as usual + else: + if not line: + return None + + # still have to be careful about incrementing the line number! + if type (self.current_line) is ListType: + self.current_line = self.current_line[1] + 1 + else: + self.current_line = self.current_line + 1 + + + # strip whitespace however the client wants (leading and + # trailing, or one or the other, or neither) + if self.lstrip_ws and self.rstrip_ws: + line = string.strip (line) + else: + if self.lstrip_ws: + line = string.lstrip (line) + if self.rstrip_ws: + line = string.rstrip (line) + + # blank line (whether we rstrip'ed or not)? skip to next line + # if appropriate + if line == '' or line == '\n' and self.skip_blanks: + continue + + if self.join_lines: + if line[-1] == '\\': + buildup_line = line[:-1] + continue + + if line[-2:] == '\\\n': + buildup_line = line[0:-2] + '\n' + continue + + # well, I guess there's some actual content there: return it + return line + + # end readline + + + def readlines (self): + lines = [] + while 1: + line = self.readline() + if line is None: + return lines + lines.append (line) + + +if __name__ == "__main__": + test_data = """# test file + +line 3 \\ +continues on next line +""" + + # result 1: no fancy options + result1 = map (lambda x: x + "\n", string.split (test_data, "\n")[0:-1]) + + # result 2: just strip comments + result2 = ["\n", "\n", "line 3 \\\n", "continues on next line\n"] + + # result 3: just strip blank lines + result3 = ["# test file\n", "line 3 \\\n", "continues on next line\n"] + + # result 4: default, strip comments, blank lines, and trailing whitespace + result4 = ["line 3 \\", "continues on next line"] + + # result 5: full processing, strip comments and blanks, plus join lines + result5 = ["line 3 continues on next line"] + + def test_input (count, description, file, expected_result): + result = file.readlines () + # result = string.join (result, '') + if result == expected_result: + print "ok %d (%s)" % (count, description) + else: + print "not ok %d (%s):" % (count, description) + print "** expected:" + print expected_result + print "** received:" + print result + + + filename = "test.txt" + out_file = open (filename, "w") + out_file.write (test_data) + out_file.close () + + in_file = TextFile (filename, strip_comments=0, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + test_input (1, "no processing", in_file, result1) + + in_file = TextFile (filename, strip_comments=1, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + test_input (2, "strip comments", in_file, result2) + + in_file = TextFile (filename, strip_comments=0, skip_blanks=1, + lstrip_ws=0, rstrip_ws=0) + test_input (3, "strip blanks", in_file, result3) + + in_file = TextFile (filename) + test_input (4, "default processing", in_file, result4) + + in_file = TextFile (filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1) + test_input (5, "full processing", in_file, result5) + + os.remove (filename) + -- cgit v1.2.1 From e57683381739ada3b56e7fc02e52b4c81f7defea Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 18 Jan 1999 17:08:16 +0000 Subject: Added 'warn' method. --- text_file.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/text_file.py b/text_file.py index 9fff941e..9e1a73b7 100644 --- a/text_file.py +++ b/text_file.py @@ -9,7 +9,7 @@ lines, and joining lines with backslashes.""" __revision__ = "$Id$" from types import * -import os, string, re +import sys, os, string, re class TextFile: @@ -67,6 +67,15 @@ class TextFile: self.current_line = None + def warn (self, msg): + sys.stderr.write (self.filename + ", ") + if type (self.current_line) is ListType: + sys.stderr.write ("lines %d-%d: " % tuple (self.current_line)) + else: + sys.stderr.write ("line %d: " % self.current_line) + sys.stderr.write (msg + "\n") + + def readline (self): buildup_line = '' -- cgit v1.2.1 From ecab4f612281cac63885b065866195177947e5e3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 8 Mar 1999 21:46:11 +0000 Subject: Added collapse_ws option. --- text_file.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text_file.py b/text_file.py index 9e1a73b7..eab498d7 100644 --- a/text_file.py +++ b/text_file.py @@ -23,6 +23,7 @@ class TextFile: 'join_lines': 0, 'lstrip_ws': 0, 'rstrip_ws': 1, + 'collapse_ws': 0, } def __init__ (self, filename=None, **options): @@ -137,6 +138,10 @@ class TextFile: buildup_line = line[0:-2] + '\n' continue + # collapse internal whitespace (*after* joining lines!) + if self.collapse_ws: + line = re.sub (r'(\S)\s+(\S)', r'\1 \2', line) + # well, I guess there's some actual content there: return it return line -- cgit v1.2.1 From 681a078a9561a64f87802bbe5d7d1e3f7abb4208 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 22 Mar 1999 14:52:19 +0000 Subject: First checkin of real Distutils code. --- core.py | 597 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ errors.py | 63 ++++++ fancy_getopt.py | 115 +++++++++++ options.py | 111 +++++++++++ util.py | 245 +++++++++++++++++++++++ 5 files changed, 1131 insertions(+) create mode 100644 core.py create mode 100644 errors.py create mode 100644 fancy_getopt.py create mode 100644 options.py create mode 100644 util.py diff --git a/core.py b/core.py new file mode 100644 index 00000000..3a7443c7 --- /dev/null +++ b/core.py @@ -0,0 +1,597 @@ +"""distutils.core + +The only module that needs to be imported to use the Distutils; provides +the 'setup' function (which must be called); the 'Distribution' class +(which may be subclassed if additional functionality is desired), and +the 'Command' class (which is used both internally by Distutils, and +may be subclassed by clients for still more flexibility).""" + +# created 1999/03/01, Greg Ward + +__rcsid__ = "$Id$" + +import sys +import string, re +from distutils.errors import * +from distutils.fancy_getopt import fancy_getopt + +# This is not *quite* the same as a Python NAME; I don't allow leading +# underscores. The fact that they're very similar is no coincidence... +command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') + +# Defining this as a global is probably inadequate -- what about +# listing the available options (or even commands, which can vary +# quite late as well) +usage = '%s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]' % sys.argv[0] + + + +def setup (**attrs): + """The gateway to the Distutils: do everything your setup script + needs to do, in a highly flexible and user-driven way. Briefly: + create a Distribution instance; parse the command-line, creating + and customizing instances of the command class for each command + found on the command-line; run each of those commands. + + The Distribution instance might be an instance of a class + supplied via the 'distclass' keyword argument to 'setup'; if no + such class is supplied, then the 'Distribution' class (also in + this module) is instantiated. All other arguments to 'setup' + (except for 'cmdclass') are used to set attributes of the + Distribution instance. + + The 'cmdclass' argument, if supplied, is a dictionary mapping + command names to command classes. Each command encountered on the + command line will be turned into a command class, which is in turn + instantiated; any class found in 'cmdclass' is used in place of the + default, which is (for command 'foo_bar') class 'FooBar' in module + 'distutils.command.foo_bar'. The command object must provide an + 'options' attribute which is a list of option specifiers for + 'distutils.fancy_getopt'. Any command-line options between the + current and the next command are used to set attributes in the + current command object. + + When the entire command-line has been successfully parsed, calls the + 'run' method on each command object in turn. This method will be + driven entirely by the Distribution object (which each command + object has a reference to, thanks to its constructor), and the + command-specific options that became attributes of each command + object.""" + + # Determine the distribution class -- either caller-supplied or + # our Distribution (see below). + klass = attrs.get ('distclass') + if klass: + del attrs['distclass'] + else: + klass = Distribution + + # Create the Distribution instance, using the remaining arguments + # (ie. everything except distclass) to initialize it + dist = klass (attrs) + + # Get it to parse the command line; any command-line errors are + # the end-users fault, so turn them into SystemExit to suppress + # tracebacks. + try: + dist.parse_command_line (sys.argv[1:]) + except DistutilsArgError, msg: + raise SystemExit, msg + + # And finally, run all the commands found on the command line. + dist.run_commands () + +# setup () + + +class Distribution: + """The core of the Distutils. Most of the work hiding behind + 'setup' is really done within a Distribution instance, which + farms the work out to the Distutils commands specified on the + command line. + + Clients will almost never instantiate Distribution directly, + unless the 'setup' function is totally inadequate to their needs. + However, it is conceivable that a client might wish to subclass + Distribution for some specialized purpose, and then pass the + subclass to 'setup' as the 'distclass' keyword argument. If so, + it is necessary to respect the expectations that 'setup' has of + Distribution: it must have a constructor and methods + 'parse_command_line()' and 'run_commands()' with signatures like + those described below.""" + + + # 'global_options' describes the command-line options that may + # be supplied to the client (setup.py) prior to any actual + # commands. Eg. "./setup.py -nv" or "./setup.py --verbose" + # both take advantage of these global options. + global_options = [('verbose', 'v', "run verbosely"), + ('dry-run', 'n', "don't actually do anything"), + ] + + + # -- Creation/initialization methods ------------------------------- + + def __init__ (self, attrs=None): + """Construct a new Distribution instance: initialize all the + attributes of a Distribution, and then uses 'attrs' (a + dictionary mapping attribute names to values) to assign + some of those attributes their "real" values. (Any attributes + not mentioned in 'attrs' will be assigned to some null + value: 0, None, an empty list or dictionary, etc.) Most + importantly, initialize the 'command_obj' attribute + to the empty dictionary; this will be filled in with real + command objects by 'parse_command_line()'.""" + + # Default values for our command-line options + self.verbose = 0 + self.dry_run = 0 + + # And for all other attributes (stuff that might be passed in + # from setup.py, rather than from the end-user) + self.name = None + self.version = None + self.author = None + self.licence = None + self.description = None + + self.cmdclass = {} + + # The rest of these are really the business of various commands, + # rather than of the Distribution itself. However, they have + # to be here as a conduit to the relevant command class. + self.py_modules = None + self.ext_modules = None + self.package = None + + # Now we'll use the attrs dictionary to possibly override + # any or all of these distribution options + if attrs: + for k in attrs.keys(): + setattr (self, k, attrs[k]) + + # And now initialize bookkeeping stuff that can't be supplied by + # the caller at all + self.command_obj = {} + + # __init__ () + + + def parse_command_line (self, args): + """Parse the client's command line: set any Distribution + attributes tied to command-line options, create all command + objects, and set their options from the command-line. 'args' + must be a list of command-line arguments, most likely + 'sys.argv[1:]' (see the 'setup()' function). This list is + first processed for "global options" -- options that set + attributes of the Distribution instance. Then, it is + alternately scanned for Distutils command and options for + that command. Each new command terminates the options for + the previous command. The allowed options for a command are + determined by the 'options' attribute of the command object + -- thus, we instantiate (and cache) every command object + here, in order to access its 'options' attribute. Any error + in that 'options' attribute raises DistutilsGetoptError; any + error on the command-line raises DistutilsArgError. If no + Distutils commands were found on the command line, raises + DistutilsArgError.""" + + # We have to parse the command line a bit at a time -- global + # options, then the first command, then its options, and so on -- + # because each command will be handled by a different class, and + # the options that are valid for a particular class aren't + # known until we instantiate the command class, which doesn't + # happen until we know what the command is. + + self.commands = [] + args = fancy_getopt (self.global_options, self, sys.argv[1:]) + + while args: + # Pull the current command from the head of the command line + command = args[0] + if not command_re.match (command): + raise SystemExit, "invalid command name '%s'" % command + self.commands.append (command) + + # Have to instantiate the command class now, so we have a + # way to get its valid options and somewhere to put the + # results of parsing its share of the command-line + cmd_obj = self.create_command_obj (command) + + # Require that the command class be derived from Command -- + # that way, we can be sure that we at least have the 'run' + # and 'get_option' methods. + if not isinstance (cmd_obj, Command): + raise DistutilsClassError, \ + "command class %s must subclass Command" % \ + cmd_obj.__class__ + + # XXX this assumes that cmd_obj provides an 'options' + # attribute, but we're not enforcing that anywhere! + args = fancy_getopt (cmd_obj.options, cmd_obj, args[1:]) + self.command_obj[command] = cmd_obj + + # while args + + # Oops, no commands found -- an end-user error + if not self.commands: + sys.stderr.write (usage + "\n") + raise DistutilsArgError, "no commands supplied" + + # parse_command_line() + + + # -- Command class/object methods ---------------------------------- + + # This is a method just so it can be overridden if desired; it doesn't + # actually use or change any attributes of the Distribution instance. + def find_command_class (self, command): + """Given a command, derives the names of the module and class + expected to implement the command: eg. 'foo_bar' becomes + 'distutils.command.foo_bar' (the module) and 'FooBar' (the + class within that module). Loads the module, extracts the + class from it, and returns the class object. + + Raises DistutilsModuleError with a semi-user-targeted error + message if the expected module could not be loaded, or the + expected class was not found in it.""" + + module_name = 'distutils.command.' + command + klass_name = string.join \ + (map (string.capitalize, string.split (command, '_')), '') + + try: + __import__ (module_name) + module = sys.modules[module_name] + except ImportError: + raise DistutilsModuleError, \ + "invalid command '%s' (no module named %s)" % \ + (command, module_name) + + try: + klass = vars(module)[klass_name] + except KeyError: + raise DistutilsModuleError, \ + "invalid command '%s' (no class '%s' in module '%s')" \ + % (command, klass_name, module_name) + + return klass + + # find_command_class () + + + def create_command_obj (self, command): + """Figure out the class that should implement a command, + instantiate it, cache and return the new "command object". + The "command class" is determined either by looking it up in + the 'cmdclass' attribute (this is the mechanism whereby + clients may override default Distutils commands or add their + own), or by calling the 'find_command_class()' method (if the + command name is not in 'cmdclass'.""" + + # Determine the command class -- either it's in the command_class + # dictionary, or we have to divine the module and class name + klass = self.cmdclass.get(command) + if not klass: + klass = self.find_command_class (command) + self.cmdclass[command] = klass + + # Found the class OK -- instantiate it + cmd_obj = klass (self) + return cmd_obj + + + def find_command_obj (self, command, create=1): + """Look up and return a command object in the cache maintained by + 'create_command_obj()'. If none found, the action taken + depends on 'create': if true (the default), create a new + command object by calling 'create_command_obj()' and return + it; otherwise, return None.""" + + cmd_obj = self.command_obj.get (command) + if not cmd_obj and create: + cmd_obj = self.create_command_obj (command) + self.command_obj[command] = cmd_obj + + return cmd_obj + + + # -- Methods that operate on the Distribution ---------------------- + + def announce (self, msg, level=1): + """Print 'msg' if 'level' is greater than or equal to the verbosity + level recorded in the 'verbose' attribute (which, currently, + can be only 0 or 1).""" + + if self.verbose >= level: + print msg + + + def run_commands (self): + """Run each command that was seen on the client command line. + Uses the list of commands found and cache of command objects + created by 'create_command_obj()'.""" + + for cmd in self.commands: + self.run_command (cmd) + + + def get_option (self, option): + """Return the value of a distribution option. Raise + DistutilsOptionError if 'option' is not known.""" + + try: + return getattr (self, opt) + except AttributeError: + raise DistutilsOptionError, \ + "unknown distribution option %s" % option + + + def get_options (self, *options): + """Return (as a tuple) the values of several distribution + options. Raise DistutilsOptionError if any element of + 'options' is not known.""" + + values = [] + try: + for opt in options: + values.append (getattr (self, opt)) + except AttributeError, name: + raise DistutilsOptionError, \ + "unknown distribution option %s" % name + + return tuple (values) + + + # -- Methods that operate on its Commands -------------------------- + + def run_command (self, command): + """Create a command object for 'command' if necessary, and + run the command by invoking its 'run()' method.""" + + self.announce ("running " + command) + cmd_obj = self.find_command_obj (command) + cmd_obj.run () + + + def get_command_option (self, command, option): + """Create a command object for 'command' if necessary, finalize + its option values by invoking its 'set_final_options()' + method, and return the value of its 'option' option. Raise + DistutilsOptionError if 'option' is not known for + that 'command'.""" + + cmd_obj = self.find_command_obj (command) + cmd_obj.set_final_options () + return cmd_obj.get_option (option) + try: + return getattr (cmd_obj, option) + except AttributeError: + raise DistutilsOptionError, \ + "command %s: no such option %s" % (command, option) + + + def get_command_options (self, command, *options): + """Create a command object for 'command' if necessary, finalize + its option values by invoking its 'set_final_options()' + method, and return the values of all the options listed in + 'options' for that command. Raise DistutilsOptionError if + 'option' is not known for that 'command'.""" + + cmd_obj = self.find_command_obj (command) + cmd_obj.set_final_options () + values = [] + try: + for opt in options: + values.append (getattr (cmd_obj, option)) + except AttributeError, name: + raise DistutilsOptionError, \ + "command %s: no such option %s" % (command, name) + + return tuple (values) + +# end class Distribution + + +class Command: + """Abstract base class for defining command classes, the "worker bees" + of the Distutils. A useful analogy for command classes is to + think of them as subroutines with local variables called + "options". The options are "declared" in 'set_initial_options()' + and "initialized" (given their real values) in + 'set_final_options()', both of which must be defined by every + command class. The distinction between the two is necessary + because option values might come from the outside world (command + line, option file, ...), and any options dependent on other + options must be computed *after* these outside influences have + been processed -- hence 'set_final_values()'. The "body" of the + subroutine, where it does all its work based on the values of its + options, is the 'run()' method, which must also be implemented by + every command class.""" + + # -- Creation/initialization methods ------------------------------- + + def __init__ (self, dist): + """Create and initialize a new Command object. Most importantly, + invokes the 'set_default_options()' method, which is the + real initializer and depends on the actual command being + instantiated.""" + + if not isinstance (dist, Distribution): + raise TypeError, "dist must be a Distribution instance" + if self.__class__ is Command: + raise RuntimeError, "Command is an abstract class" + + self.distribution = dist + self.set_default_options () + + # end __init__ () + + # Subclasses must define: + # set_default_options() + # provide default values for all options; may be overridden + # by Distutils client, by command-line options, or by options + # from option file + # set_final_options() + # decide on the final values for all options; this is called + # after all possible intervention from the outside world + # (command-line, option file, etc.) has been processed + # run() + # run the command: do whatever it is we're here to do, + # controlled by the command's various option values + + def set_default_options (self): + """Set default values for all the options that this command + supports. Note that these defaults may be overridden + by the command-line supplied by the user; thus, this is + not the place to code dependencies between options; generally, + 'set_default_options()' implementations are just a bunch + of "self.foo = None" assignments. + + This method must be implemented by all command classes.""" + + raise RuntimeError, \ + "abstract method -- subclass %s must override" % self.__class__ + + def set_final_options (self): + """Set final values for all the options that this command + supports. This is always called as late as possible, ie. + after any option assignments from the command-line or from + other commands have been done. Thus, this is the place to to + code option dependencies: if 'foo' depends on 'bar', then it + is safe to set 'foo' from 'bar' as long as 'foo' still has + the same value it was assigned in 'set_default_options()'. + + This method must be implemented by all command classes.""" + + raise RuntimeError, \ + "abstract method -- subclass %s must override" % self.__class__ + + def run (self): + """A command's raison d'etre: carry out the action it exists + to perform, controlled by the options initialized in + 'set_initial_options()', customized by the user and other + commands, and finalized in 'set_final_options()'. All + terminal output and filesystem interaction should be done by + 'run()'. + + This method must be implemented by all command classes.""" + + raise RuntimeError, \ + "abstract method -- subclass %s must override" % self.__class__ + + def announce (self, msg, level=1): + """If the Distribution instance to which this command belongs + has a verbosity level of greater than or equal to 'level' + print 'msg' to stdout.""" + if self.distribution.verbose >= level: + print msg + + + # -- Option query/set methods -------------------------------------- + + def get_option (self, option): + """Return the value of a single option for this command. Raise + DistutilsOptionError if 'option' is not known.""" + try: + return getattr (self, option) + except AttributeError: + raise DistutilsOptionError, \ + "command %s: no such option %s" % \ + (self.command_name(), option) + + + def get_options (self, *options): + """Return (as a tuple) the values of several options for this + command. Raise DistutilsOptionError if any of the options in + 'options' are not known.""" + + values = [] + try: + for opt in options: + values.append (getattr (self, opt)) + except AttributeError, name: + raise DistutilsOptionError, \ + "command %s: no such option %s" % \ + (self.command_name(), name) + + return tuple (values) + + + def set_option (self, option, value): + """Set the value of a single option for this command. Raise + DistutilsOptionError if 'option' is not known.""" + + if not hasattr (self, option): + raise DistutilsOptionError, \ + "command %s: no such option %s" % \ + (self.command_name(), option) + if value is not None: + setattr (self, option, value) + + def set_options (self, **optval): + """Set the values of several options for this command. Raise + DistutilsOptionError if any of the options specified as + keyword arguments are not known.""" + + for k in optval.keys(): + if optval[k] is not None: + self.set_option (k, optval[k]) + + + # -- Convenience methods for commands ------------------------------ + + def set_undefined_options (self, src_cmd, *option_pairs): + """Set the values of any "undefined" options from corresponding + option values in some other command object. "Undefined" here + means "is None", which is the convention used to indicate + that an option has not been changed between + 'set_initial_values()' and 'set_final_values()'. Usually + called from 'set_final_values()' for options that depend on + some other command rather than another option of the same + command. 'src_cmd' is the other command from which option + values will be taken (a command object will be created for it + if necessary); the remaining arguments are + '(src_option,dst_option)' tuples which mean "take the value + of 'src_option' in the 'src_cmd' command object, and copy it + to 'dst_option' in the current command object".""" + + # Option_pairs: list of (src_option, dst_option) tuples + + src_cmd_obj = self.distribution.find_command_obj (src_cmd) + src_cmd_obj.set_final_options () + try: + for (src_option, dst_option) in option_pairs: + if getattr (self, dst_option) is None: + self.set_option (dst_option, + src_cmd_obj.get_option (src_option)) + except AttributeError, name: + # duh, which command? + raise DistutilsOptionError, "unknown option %s" % name + + + def set_peer_option (self, command, option, value): + """Attempt to simulate a command-line override of some option + value in another command. Creates a command object for + 'command' if necessary, sets 'option' to 'value', and invokes + 'set_final_options()' on that command object. This will only + have the desired effect if the command object for 'command' + has not previously been created. Generally this is used to + ensure that the options in 'command' dependent on 'option' + are computed, hopefully (but not necessarily) deriving from + 'value'. It might be more accurate to call this method + 'influence_dependent_peer_options()'.""" + + cmd_obj = self.distribution.find_command_obj (command) + cmd_obj.set_option (option, value) + cmd_obj.set_final_options () + + + def run_peer (self, command): + """Run some other command: uses the 'run_command()' method of + Distribution, which creates the command object if necessary + and then invokes its 'run()' method.""" + + self.distribution.run_command (command) + +# end class Command diff --git a/errors.py b/errors.py new file mode 100644 index 00000000..6605ad2c --- /dev/null +++ b/errors.py @@ -0,0 +1,63 @@ +"""distutils.errors + +Provides exceptions used by the Distutils modules. Note that Distutils +modules may raise standard exceptions; in particular, SystemExit is +usually raised for errors that are obviously the end-user's fault +(eg. bad command-line arguments). + +This module safe to use in "from ... import *" mode; it only exports +symbols whose names start with "Distutils" and end with "Error".""" + +# created 1999/03/03, Greg Ward + +__rcsid__ = "$Id$" + +from types import * + +if type (RuntimeError) is ClassType: + + # DistutilsError is the root of all Distutils evil. + class DistutilsError (Exception): + pass + + # DistutilsModuleError is raised if we are unable to load an expected + # module, or find an expected class within some module + class DistutilsModuleError (DistutilsError): + pass + + # DistutilsClassError is raised if we encounter a distribution or command + # class that's not holding up its end of the bargain. + class DistutilsClassError (DistutilsError): + pass + + # DistutilsGetoptError (help me -- I have JavaProgrammersDisease!) is + # raised if the option table provided to fancy_getopt is bogus. + class DistutilsGetoptError (DistutilsError): + pass + + # DistutilsArgError is raised by fancy_getopt in response to getopt.error; + # distutils.core then turns around and raises SystemExit from that. (Thus + # client code should never see DistutilsArgError.) + class DistutilsArgError (DistutilsError): + pass + + # DistutilsFileError is raised for any problems in the filesystem: + # expected file not found, etc. + class DistutilsFileError (DistutilsError): + pass + + # DistutilsOptionError is raised anytime an attempt is made to access + # (get or set) an option that does not exist for a particular command + # (or for the distribution itself). + class DistutilsOptionError (DistutilsError): + pass + +# String-based exceptions +else: + DistutilsError = 'DistutilsError' + DistutilsModuleError = 'DistutilsModuleError' + DistutilsClassError = 'DistutilsClassError' + DistutilsGetoptError = 'DistutilsGetoptError' + DistutilsArgError = 'DistutilsArgError' + DistutilsFileError = 'DistutilsFileError' + DistutilsOptionError = 'DistutilsOptionError' diff --git a/fancy_getopt.py b/fancy_getopt.py new file mode 100644 index 00000000..c63ce61b --- /dev/null +++ b/fancy_getopt.py @@ -0,0 +1,115 @@ +"""distutils.fancy_getopt + +Wrapper around the standard getopt module that provides the following +additional features: + * short and long options are tied together + * options have help strings, so fancy_getopt could potentially + create a complete usage summary + * options set attributes of a passed-in object +""" + +# created 1999/03/03, Greg Ward + +__rcsid__ = "$Id$" + +import string, re +from types import * +import getopt +from distutils.errors import * + +# Much like command_re in distutils.core, this is close to but not quite +# the same as a Python NAME -- except, in the spirit of most GNU +# utilities, we use '-' in place of '_'. (The spirit of LISP lives on!) +# The similarities to NAME are again not a coincidence... +longopt_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9-]*)$') + +# This is used to translate long options to legitimate Python identifiers +# (for use as attributes of some object). +longopt_xlate = string.maketrans ('-', '_') + + +def fancy_getopt (options, object, args): + + # The 'options' table is a list of 3-tuples: + # (long_option, short_option, help_string) + # if an option takes an argument, its long_option should have '=' + # appended; short_option should just be a single character, no ':' in + # any case. If a long_option doesn't have a corresponding + # short_option, short_option should be None. All option tuples must + # have long options. + + # Build the short_opts string and long_opts list, remembering how + # the two are tied together + + short_opts = [] # we'll join 'em when done + long_opts = [] + short2long = {} + attr_name = {} + takes_arg = {} + + for (long, short, help) in options: + # Type-check the option names + if type (long) is not StringType or len (long) < 2: + raise DistutilsGetoptError, \ + "long option must be a string of length >= 2" + + if (not ((short is None) or + (type (short) is StringType and len (short) == 1))): + raise DistutilsGetoptError, \ + "short option must be None or string of length 1" + + long_opts.append (long) + + if long[-1] == '=': # option takes an argument? + if short: short = short + ':' + long = long[0:-1] + takes_arg[long] = 1 + else: + takes_arg[long] = 0 + + # Now enforce some bondage on the long option name, so we can later + # translate it to an attribute name in 'object'. Have to do this a + # bit late to make sure we've removed any trailing '='. + if not longopt_re.match (long): + raise DistutilsGetoptError, \ + ("invalid long option name '%s' " + + "(must be letters, numbers, hyphens only") % long + + attr_name[long] = string.translate (long, longopt_xlate) + if short: + short_opts.append (short) + short2long[short[0]] = long + + # end loop over 'options' + + short_opts = string.join (short_opts) + try: + (opts, args) = getopt.getopt (args, short_opts, long_opts) + except getopt.error, msg: + raise DistutilsArgError, msg + + for (opt, val) in opts: + if len (opt) == 2 and opt[0] == '-': # it's a short option + opt = short2long[opt[1]] + + elif len (opt) > 2 and opt[0:2] == '--': + opt = opt[2:] + + else: + raise RuntimeError, "getopt lies! (bad option string '%s')" % \ + opt + + attr = attr_name[opt] + if takes_arg[opt]: + setattr (object, attr, val) + else: + if val == '': + setattr (object, attr, 1) + else: + raise RuntimeError, "getopt lies! (bad value '%s')" % value + + # end loop over options found in 'args' + + return args + +# end fancy_getopt() diff --git a/options.py b/options.py new file mode 100644 index 00000000..f6cae82d --- /dev/null +++ b/options.py @@ -0,0 +1,111 @@ +# XXX this is ridiculous! if commands need to pass options around, +# they can just pass them via the 'run' method... what we REALLY need +# is a way for commands to get at each other, via the Distribution! + +class Options: + """Used by Distribution and Command to encapsulate distribution + and command options -- parsing them from command-line arguments, + passing them between the distribution and command objects, etc.""" + + # -- Creation/initialization methods ------------------------------- + + def __init__ (self, owner): + + # 'owner' is the object (presumably either a Distribution + # or Command instance) to which this set of options applies. + self.owner = owner + + # The option table: maps option names to dictionaries, which + # look something like: + # { 'longopt': long command-line option string (optional) + # 'shortopt': short option (1 char) (optional) + # 'type': 'string', 'boolean', or 'list' + # 'description': text description (eg. for help strings) + # 'default': default value for the option + # 'send': list of (cmd,option) tuples: send option down the line + # 'receive': (cmd,option) tuple: pull option from upstream + # } + self.table = {} + + + def set_basic_options (self, *options): + """Add very basic options: no separate longopt, no fancy typing, no + send targets or receive destination. The arguments should just + be {1..4}-tuples of + (name [, shortopt [, description [, default]]]) + If name ends with '=', the option takes a string argument; + otherwise it's boolean.""" + + for opt in options: + if not (type (opt) is TupleType and 1 <= len (opt) <= 4): + raise ValueError, \ + ("invalid basic option record '%s': " + \ + "must be tuple of length 1 .. 4") % opt + + elements = ('name', 'shortopt', 'description', 'default') + name = opt[0] + self.table[name] = {} + for i in range (1,4): + if len (opt) >= i: + self.table[name][elements[i]] = opt[i] + else: + break + + # set_basic_options () + + + def add_option (self, name, **args): + + # XXX should probably sanity-check the keys of args + self.table[name] = args + + + # ------------------------------------------------------------------ + + # These are in the order that they will execute in to ensure proper + # prioritizing of option sources -- the default value is the most + # basic; it can be overridden by "client options" (the keyword args + # passed from setup.py to the 'setup' function); they in turn lose to + # options passed in "from above" (ie. from the Distribution, or from + # higher-level Commands); these in turn may be overridden by + # command-line arguments (which come from the end-user, the runner of + # setup.py). Only when all this is done can we pass options down to + # other Commands. + + # Hmmm, it also matters in which order Commands are processed: should a + # command-line option to 'make_blib' take precedence over the + # corresponding value passed down from its boss, 'build'? + + def set_defaults (self): + pass + + def set_client_options (self, options): + # 'self' should be a Distribution instance for this one -- + # this is to process the kw args passed to 'setup' + pass + + def receive_option (self, option, value): + # do we need to know the identity of the sender? don't + # think we should -- too much B&D + + # oh, 'self' should be anything *but* a Distribution (ie. + # a Command instance) -- only Commands take orders from above! + # (ironically enough) + pass + + def parse_command_line (self, args): + # here, 'self' can usefully be either a Distribution (for parsing + # "global" command-line options) or a Command (for "command-specific" + # options) + pass + + + def send_option (self, option, dest): + # perhaps this should not take a dest, but send the option + # to all possible receivers? + pass + + + # ------------------------------------------------------------------ + +# class Options diff --git a/util.py b/util.py new file mode 100644 index 00000000..7c13abe0 --- /dev/null +++ b/util.py @@ -0,0 +1,245 @@ +"""distutils.util + +General-purpose utility functions used throughout the Distutils +(especially in command classes). Mostly filesystem manipulation, but +not limited to that. The functions in this module generally raise +DistutilsFileError when they have problems with the filesystem, because +os.error in pre-1.5.2 Python only gives the error message and not the +file causing it.""" + +# created 1999/03/08, Greg Ward + +__rcsid__ = "$Id$" + +import os +from distutils.errors import * + + +# I don't use os.makedirs because a) it's new to Python 1.5.2, and +# b) it blows up if the directory already exists (I want to silently +# succeed in that case). +def mkpath (name, mode=0777, verbose=0): + """Create a directory and any missing ancestor directories. If the + directory already exists, return silently. Raise + DistutilsFileError if unable to create some directory along the + way (eg. some sub-path exists, but is a file rather than a + directory). If 'verbose' is true, print a one-line summary of + each mkdir to stdout.""" + + # XXX what's the better way to handle verbosity? print as we create + # each directory in the path (the current behaviour), or only announce + # the creation of the whole path, and force verbose=0 on all sub-calls? + + if os.path.isdir (name): + return + + (head, tail) = os.path.split (name) + tails = [tail] # stack of lone dirs to create + + while head and tail and not os.path.isdir (head): + #print "splitting '%s': " % head, + (head, tail) = os.path.split (head) + #print "to ('%s','%s')" % (head, tail) + tails.insert (0, tail) # push next higher dir onto stack + + #print "stack of tails:", tails + + # now 'head' contains the highest directory that already exists + for d in tails: + #print "head = %s, d = %s: " % (head, d), + head = os.path.join (head, d) + if verbose: + print "creating", head + try: + os.mkdir (head) + except os.error, (errno, errstr): + raise DistutilsFileError, "%s: %s" % (head, errstr) + +# mkpath () + + +def newer (file1, file2): + """Return true if file1 exists and is more recently modified than + file2, or if file1 exists and file2 doesn't. Return false if both + exist and file2 is the same age or younger than file1. Raises + DistutilsFileError if file1 does not exist.""" + + if not os.path.exists (file1): + raise DistutilsFileError, "file '%s' does not exist" % file1 + if not os.path.exists (file2): + return 1 + + from stat import * + mtime1 = os.stat(file1)[ST_MTIME] + mtime2 = os.stat(file2)[ST_MTIME] + + return mtime1 > mtime2 + +# newer () + + +def make_file (src, dst, func, args, + verbose=0, update_message=None, noupdate_message=None): + """Makes 'dst' from 'src' (both filenames) by calling 'func' with + 'args', but only if it needs to: i.e. if 'dst' does not exist or + 'src' is newer than 'dst'.""" + + if newer (src, dst): + if verbose and update_message: + print update_message + apply (func, args) + else: + if verbose and noupdate_message: + print noupdate_message + +# make_file () + + +def _copy_file_contents (src, dst, buffer_size=16*1024): + """Copy the file 'src' to 'dst'; both must be filenames. Any error + opening either file, reading from 'src', or writing to 'dst', + raises DistutilsFileError. Data is read/written in chunks of + 'buffer_size' bytes (default 16k). No attempt is made to handle + anything apart from regular files.""" + + # Stolen from shutil module in the standard library, but with + # custom error-handling added. + + fsrc = None + fdst = None + try: + try: + fsrc = open(src, 'rb') + except os.error, (errno, errstr): + raise DistutilsFileError, "could not open %s: %s" % (src, errstr) + + try: + fdst = open(dst, 'wb') + except os.error, (errno, errstr): + raise DistutilsFileError, "could not create %s: %s" % (dst, errstr) + + while 1: + try: + buf = fsrc.read (buffer_size) + except os.error, (errno, errstr): + raise DistutilsFileError, \ + "could not read from %s: %s" % (src, errstr) + + if not buf: + break + + try: + fdst.write(buf) + except os.error, (errno, errstr): + raise DistutilsFileError, \ + "could not write to %s: %s" % (dst, errstr) + + finally: + if fdst: + fdst.close() + if fsrc: + fsrc.close() + +# _copy_file_contents() + + +def copy_file (src, dst, + preserve_mode=1, + preserve_times=1, + update=0, + verbose=0): + + """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' + is copied there with the same name; otherwise, it must be a + filename. (If the file exists, it will be ruthlessly clobbered.) + If 'preserve_mode' is true (the default), the file's mode (type + and permission bits, or whatever is analogous on the current + platform) is copied. If 'preserve_times' is true (the default), + the last-modified and last-access times are copied as well. If + 'update' is true, 'src' will only be copied if 'dst' does not + exist, or if 'dst' does exist but is older than 'src'. If + 'verbose' is true, then a one-line summary of the copy will be + printed to stdout.""" + + # XXX doesn't copy Mac-specific metadata + + from shutil import copyfile + from stat import * + + if not os.path.isfile (src): + raise DistutilsFileError, \ + "can't copy %s:not a regular file" % src + + if os.path.isdir (dst): + dir = dst + dst = os.path.join (dst, os.path.basename (src)) + else: + dir = os.path.dirname (dst) + + if update and not newer (src, dst): + return + + if verbose: + print "copying %s -> %s" % (src, dir) + + copyfile (src, dst) + if preserve_mode or preserve_times: + st = os.stat (src) + if preserve_mode: + os.chmod (dst, S_IMODE (st[ST_MODE])) + if preserve_times: + os.utime (dst, (st[ST_ATIME], st[ST_MTIME])) + +# copy_file () + + +def copy_tree (src, dst, + preserve_mode=1, + preserve_times=1, + preserve_symlinks=0, + update=0, + verbose=0): + + """Copy an entire directory tree 'src' to a new location 'dst'. Both + 'src' and 'dst' must be directory names. If 'src' is not a + directory, raise DistutilsFileError. If 'dst' does not exist, it + is created with 'mkpath'. The endresult of the copy is that + every file in 'src' is copied to 'dst', and directories under + 'src' are recursively copied to 'dst'. + + 'preserve_mode' and 'preserve_times' are the same as for + 'copy_file'; note that they only apply to regular files, not to + directories. If 'preserve_symlinks' is true, symlinks will be + copied as symlinks (on platforms that support them!); otherwise + (the default), the destination of the symlink will be copied. + 'update' and 'verbose' are the same as for 'copy_file'.""" + + if not os.path.isdir (src): + raise DistutilsFileError, \ + "cannot copy tree %s: not a directory" % src + try: + names = os.listdir (src) + except os.error, (errno, errstr): + raise DistutilsFileError, \ + "error listing files in %s: %s" % (src, errstr) + + + mkpath (dst, verbose=verbose) + + for n in names: + src_name = os.path.join (src, n) + dst_name = os.path.join (dst, n) + + if preserve_symlinks and os.path.islink (src_name): + link_dest = os.readlink (src_name) + os.symlink (link_dest, dst_name) + elif os.path.isdir (src_name): + copy_tree (src_name, dst_name, + preserve_mode, preserve_times, preserve_symlinks, + update, verbose) + else: + copy_file (src_name, dst_name, + preserve_mode, preserve_times, + update, verbose) + +# copy_tree () -- cgit v1.2.1 From cc115bf7157ec6636b0baa75630f61f03ddc6906 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 22 Mar 1999 14:54:09 +0000 Subject: Obsolete source file -- command options are actually implemented in a much less formalistic way. Just keeping this around for possible future reference. --- options.py | 111 ------------------------------------------------------------- 1 file changed, 111 deletions(-) delete mode 100644 options.py diff --git a/options.py b/options.py deleted file mode 100644 index f6cae82d..00000000 --- a/options.py +++ /dev/null @@ -1,111 +0,0 @@ -# XXX this is ridiculous! if commands need to pass options around, -# they can just pass them via the 'run' method... what we REALLY need -# is a way for commands to get at each other, via the Distribution! - -class Options: - """Used by Distribution and Command to encapsulate distribution - and command options -- parsing them from command-line arguments, - passing them between the distribution and command objects, etc.""" - - # -- Creation/initialization methods ------------------------------- - - def __init__ (self, owner): - - # 'owner' is the object (presumably either a Distribution - # or Command instance) to which this set of options applies. - self.owner = owner - - # The option table: maps option names to dictionaries, which - # look something like: - # { 'longopt': long command-line option string (optional) - # 'shortopt': short option (1 char) (optional) - # 'type': 'string', 'boolean', or 'list' - # 'description': text description (eg. for help strings) - # 'default': default value for the option - # 'send': list of (cmd,option) tuples: send option down the line - # 'receive': (cmd,option) tuple: pull option from upstream - # } - self.table = {} - - - def set_basic_options (self, *options): - """Add very basic options: no separate longopt, no fancy typing, no - send targets or receive destination. The arguments should just - be {1..4}-tuples of - (name [, shortopt [, description [, default]]]) - If name ends with '=', the option takes a string argument; - otherwise it's boolean.""" - - for opt in options: - if not (type (opt) is TupleType and 1 <= len (opt) <= 4): - raise ValueError, \ - ("invalid basic option record '%s': " + \ - "must be tuple of length 1 .. 4") % opt - - elements = ('name', 'shortopt', 'description', 'default') - name = opt[0] - self.table[name] = {} - for i in range (1,4): - if len (opt) >= i: - self.table[name][elements[i]] = opt[i] - else: - break - - # set_basic_options () - - - def add_option (self, name, **args): - - # XXX should probably sanity-check the keys of args - self.table[name] = args - - - # ------------------------------------------------------------------ - - # These are in the order that they will execute in to ensure proper - # prioritizing of option sources -- the default value is the most - # basic; it can be overridden by "client options" (the keyword args - # passed from setup.py to the 'setup' function); they in turn lose to - # options passed in "from above" (ie. from the Distribution, or from - # higher-level Commands); these in turn may be overridden by - # command-line arguments (which come from the end-user, the runner of - # setup.py). Only when all this is done can we pass options down to - # other Commands. - - # Hmmm, it also matters in which order Commands are processed: should a - # command-line option to 'make_blib' take precedence over the - # corresponding value passed down from its boss, 'build'? - - def set_defaults (self): - pass - - def set_client_options (self, options): - # 'self' should be a Distribution instance for this one -- - # this is to process the kw args passed to 'setup' - pass - - def receive_option (self, option, value): - # do we need to know the identity of the sender? don't - # think we should -- too much B&D - - # oh, 'self' should be anything *but* a Distribution (ie. - # a Command instance) -- only Commands take orders from above! - # (ironically enough) - pass - - def parse_command_line (self, args): - # here, 'self' can usefully be either a Distribution (for parsing - # "global" command-line options) or a Command (for "command-specific" - # options) - pass - - - def send_option (self, option, dest): - # perhaps this should not take a dest, but send the option - # to all possible receivers? - pass - - - # ------------------------------------------------------------------ - -# class Options -- cgit v1.2.1 From df17f2c56422e29ed2b2bc07dfaf373b2f24eede Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 22 Mar 1999 14:55:25 +0000 Subject: First checkin of real Distutils command modules. --- command/__init__.py | 6 ++ command/build.py | 56 +++++++++++++++ command/build_py.py | 113 +++++++++++++++++++++++++++++ command/install.py | 189 +++++++++++++++++++++++++++++++++++++++++++++++++ command/install_lib.py | 42 +++++++++++ command/install_py.py | 42 +++++++++++ 6 files changed, 448 insertions(+) create mode 100644 command/__init__.py create mode 100644 command/build.py create mode 100644 command/build_py.py create mode 100644 command/install.py create mode 100644 command/install_lib.py create mode 100644 command/install_py.py diff --git a/command/__init__.py b/command/__init__.py new file mode 100644 index 00000000..9567fd13 --- /dev/null +++ b/command/__init__.py @@ -0,0 +1,6 @@ +# this is solely for debugging convenience + +__all__ = ['build', + 'build_py', + 'make_blib', + ] diff --git a/command/build.py b/command/build.py new file mode 100644 index 00000000..78860928 --- /dev/null +++ b/command/build.py @@ -0,0 +1,56 @@ +"""distutils.command.build + +Implements the Distutils 'build' command.""" + +# created 1999/03/08, Greg Ward + +__rcsid__ = "$Id$" + +import os +from distutils.core import Command + + +class Build (Command): + + options = [('basedir=', 'b', "base directory for build library"), + ('libdir=', 'l', "directory for platform-shared files"), + ('platdir=', 'p', "directory for platform-specific files"), + + # Flags for 'build_py' + ('compile-py', None, "compile .py to .pyc"), + ('optimize-py', None, "compile .py to .pyo (optimized)"), + ] + + def set_default_options (self): + self.basedir = 'build' + # these are decided only after 'basedir' has its final value + # (unless overridden by the user or client) + self.libdir = None + self.platdir = None + + self.compile_py = 1 + self.optimize_py = 1 + + def set_final_options (self): + # 'libdir' and 'platdir' just default to 'lib' and 'plat' under + # the base build directory + if self.libdir is None: + self.libdir = os.path.join (self.basedir, 'lib') + if self.platdir is None: + self.platdir = os.path.join (self.basedir, 'plat') + + + def run (self): + + self.set_final_options () + + # For now, "build" means "build_py" then "build_ext". (Eventually + # it should also build documentation.) + + # Invoke the 'build_py' command + self.run_peer ('build_py') + + # And now 'build_ext' + #self.run_peer ('build_ext') + +# end class Build diff --git a/command/build_py.py b/command/build_py.py new file mode 100644 index 00000000..ae133f9c --- /dev/null +++ b/command/build_py.py @@ -0,0 +1,113 @@ +"""distutils.command.build_py + +Implements the Distutils 'build_py' command.""" + +# created 1999/03/08, Greg Ward + +__rcsid__ = "$Id$" + +import string, os +from distutils.core import Command +from distutils.errors import * +from distutils.util import mkpath, newer, make_file, copy_file + + +class BuildPy (Command): + + options = [('dir=', 'd', "directory for platform-shared files"), + ('compile', 'c', "compile .py to .pyc"), + ('optimize', 'o', "compile .py to .pyo (optimized)"), + ] + + + def set_default_options (self): + self.dir = None + self.compile = 1 + self.optimize = 1 + + def set_final_options (self): + self.set_undefined_options ('build', + ('libdir', 'dir'), + ('compile_py', 'compile'), + ('optimize_py', 'optimize')) + + + def run (self): + + # XXX copy_file by default preserves all stat info -- mode, atime, + # and mtime. IMHO this is the right thing to do, but perhaps it + # should be an option -- in particular, a site administrator might + # want installed files to reflect the time of installation rather + # than the last modification time before the installed release. + + # XXX copy_file does *not* preserve MacOS-specific file metadata. + # If this is a problem for building/installing Python modules, then + # we'll have to fix copy_file. (And what about installing scripts, + # when the time comes for that -- does MacOS use its special + # metadata to know that a file is meant to be interpreted by + # Python?) + + self.set_final_options () + + (modules, package) = \ + self.distribution.get_options ('py_modules', 'package') + package = package or '' + + infiles = [] + outfiles = [] + missing = [] + + # Loop over the list of "pure Python" modules, deriving + # input and output filenames and checking for missing + # input files. + + # XXX we should allow for wildcards, so eg. the Distutils setup.py + # file would just have to say + # py_modules = ['distutils.*', 'distutils.command.*'] + # without having to list each one explicitly. + for m in modules: + fn = apply (os.path.join, tuple (string.split (m, '.'))) + '.py' + if not os.path.exists (fn): + missing.append (fn) + else: + infiles.append (fn) + outfiles.append (os.path.join (self.dir, package, fn)) + + # Blow up if any input files were not found. + if missing: + raise DistutilsFileError, \ + "missing files: " + string.join (missing, ' ') + + # Loop over the list of input files, copying them to their + # temporary (build) destination. + created = {} + for i in range (len (infiles)): + outdir = os.path.split (outfiles[i])[0] + if not created.get(outdir): + mkpath (outdir, verbose=self.distribution.verbose) + created[outdir] = 1 + + copy_file (infiles[i], outfiles[i], + update=1, verbose=self.distribution.verbose) + + # (Optionally) compile .py to .pyc + # XXX hey! we can't control whether we optimize or not; that's up + # to the invocation of the current Python interpreter (at least + # according to the py_compile docs). That sucks. + + if self.compile: + from py_compile import compile + + for f in outfiles: + # XXX can't assume this filename mapping! + out_fn = string.replace (f, '.py', '.pyc') + + make_file (f, out_fn, compile, (f,), + verbose=self.distribution.verbose, + update_message="compiling %s" % f) + + # XXX ignore self.optimize for now, since we don't really know if + # we're compiling optimally or not, and couldn't pick what to do + # even if we did know. ;-( + +# end class BuildPy diff --git a/command/install.py b/command/install.py new file mode 100644 index 00000000..1f457807 --- /dev/null +++ b/command/install.py @@ -0,0 +1,189 @@ +"""distutils.command.install + +Implements the Distutils 'install' command.""" + +# created 1999/03/13, Greg Ward + +__rcsid__ = "$Id$" + +import sys, os, string +from distutils import sysconfig +from distutils.core import Command + + +class Install (Command): + + options = [('prefix=', None, "installation prefix"), + ('execprefix=', None, + "prefix for platform-specific files"), + + # Build directories: where to install from + ('build-base=', None, + "base build directory"), + ('build-lib=', None, + "build directory for non-platform-specific library files"), + ('build-platlib=', None, + "build directory for platform-specific library files"), + + # Installation directories: where to put modules and packages + ('install-lib=', None, + "base Python library directory"), + ('install-platlib=', None, + "platform-specific Python library directory"), + ('install-site-lib=', None, + "directory for site-specific packages and modules"), + ('install-site-platlib=', None, + "platform-specific site directory"), + ('install-scheme=', None, + "install to 'system' or 'site' library directory?"), + + # Where to install documentation (eventually!) + ('doc-format=', None, "format of documentation to generate"), + ('install-man=', None, "directory for Unix man pages"), + ('install-html=', None, "directory for HTML documentation"), + ('install-info=', None, "directory for GNU info files"), + + ] + + def set_default_options (self): + + self.build_base = None + self.build_lib = None + self.build_platlib = None + + # Don't define 'prefix' or 'exec_prefix' so we can know when the + # command is run whether the user supplied values + self.prefix = None + self.exec_prefix = None + + # These two, we can supply real values for! (because they're + # not directories, and don't have a confusing multitude of + # possible derivations) + #self.install_scheme = 'site' + self.doc_format = None + + # The actual installation directories are determined only at + # run-time, so the user can supply just prefix (and exec_prefix?) + # as a base for everything else + self.install_lib = None + self.install_platlib = None + self.install_site_lib = None + self.install_site_platlib = None + + self.install_man = None + self.install_html = None + self.install_info = None + + + def set_final_options (self): + + # Figure out the build directories, ie. where to install from + self.set_peer_option ('build', 'basedir', self.build_base) + self.set_undefined_options ('build', + ('basedir', 'build_base'), + ('libdir', 'build_lib'), + ('platdir', 'build_platlib')) + + # Figure out actual installation directories; the basic principle + # is: if the user supplied nothing, then use the directories that + # Python was built and installed with (ie. the compiled-in prefix + # and exec_prefix, and the actual installation directories gleaned + # by sysconfig). If the user supplied a prefix (and possibly + # exec_prefix), then we generate our own installation directories, + # following any pattern gleaned from sysconfig's findings. If no + # such pattern can be gleaned, then we'll just make do and try to + # ape the behaviour of Python's configure script. + + if self.prefix is None: # user didn't override + self.prefix = sys.prefix + if self.exec_prefix is None: + self.exec_prefix = sys.exec_prefix + + if self.install_lib is None: + self.install_lib = \ + self.replace_sys_prefix ('LIBDEST', ('lib','python1.5')) + if self.install_platlib is None: + # XXX this should probably be DESTSHARED -- but why is there no + # equivalent to DESTSHARED for the "site-packages" dir"? + self.install_platlib = \ + self.replace_sys_prefix ('BINLIBDEST', ('lib','python1.5'), 1) + + if self.install_site_lib is None: + self.install_site_lib = \ + os.path.join (self.install_lib, 'site-packages') + if self.install_site_platlib is None: + # XXX ugh! this puts platform-specific files in with shared files, + # with no nice way to override it! (this might be a Python + # problem, though, not a Distutils problem...) + self.install_site_platlib = \ + os.path.join (self.install_lib, 'site-packages') + + #if self.install_scheme == 'site': + # install_lib = self.install_site_lib + # install_platlib = self.install_site_platlib + #elif self.install_scheme == 'system': + # install_lib = self.install_lib + # install_platlib = self.install_platlib + #else: + # # XXX new exception for this kind of misbehaviour? + # raise DistutilsArgError, \ + # "invalid install scheme '%s'" % self.install_scheme + + + # Punt on doc directories for now -- after all, we're punting on + # documentation completely! + + # set_final_options () + + + def replace_sys_prefix (self, config_attr, fallback_postfix, use_exec=0): + """Attempts to glean a simple pattern from an installation + directory available as a 'sysconfig' attribute: if the + directory name starts with the "system prefix" (the one + hard-coded in the Makefile and compiled into Python), + then replace it with the current installation prefix and + return the "relocated" installation directory.""" + + if use_exec: + sys_prefix = sys.exec_prefix + my_prefix = self.exec_prefix + else: + sys_prefix = sys.prefix + my_prefix = self.prefix + + val = getattr (sysconfig, config_attr) + if string.find (val, sys_prefix) == 0: + # If the sysconfig directory starts with the system prefix, + # then we can "relocate" it to the user-supplied prefix -- + # assuming, of course, it is different from the system prefix. + + if sys_prefix == my_prefix: + return val + else: + return my_prefix + val[len(sys_prefix):] + + else: + # Otherwise, just tack the "fallback postfix" onto the + # user-specified prefix. + + return apply (os.join, (my_prefix,) + fallback_postfix) + + # replace_sys_prefix () + + + def run (self): + + self.set_final_options () + + # Install modules in two steps: "platform-shared" files (ie. pure + # python modules) and platform-specific files (compiled C + # extensions). + + self.run_peer ('install_py') + + # don't have an 'install_ext' command just yet! + #self.run_peer ('install_ext')) + + # run () + +# class Install diff --git a/command/install_lib.py b/command/install_lib.py new file mode 100644 index 00000000..f2fa8420 --- /dev/null +++ b/command/install_lib.py @@ -0,0 +1,42 @@ +# created 1999/03/13, Greg Ward + +__rcsid__ = "$Id$" + +import sys +from distutils.core import Command +from distutils.util import copy_tree + +class InstallPy (Command): + + options = [('dir=', 'd', "directory to install to"), + ('build-dir=' 'b', "build directory (where to install from)")] + + def set_default_options (self): + # let the 'install' command dictate our installation directory + self.dir = None + self.build_dir = None + + def set_final_options (self): + # If we don't have a 'dir' value, we'll have to ask the 'install' + # command for one. (This usually means the user ran 'install_py' + # directly, rather than going through 'install' -- so in reality, + # 'find_command_obj()' will create an 'install' command object, + # which we then query. + + self.set_undefined_options ('install', + ('build_lib', 'build_dir'), + ('install_site_lib', 'dir')) + + def run (self): + + self.set_final_options () + + # Dump entire contents of the build directory to the installation + # directory (that's the beauty of having a build directory!) + copy_tree (self.build_dir, self.dir, + verbose=self.distribution.verbose, + update=1) + + # run () + +# class InstallPy diff --git a/command/install_py.py b/command/install_py.py new file mode 100644 index 00000000..f2fa8420 --- /dev/null +++ b/command/install_py.py @@ -0,0 +1,42 @@ +# created 1999/03/13, Greg Ward + +__rcsid__ = "$Id$" + +import sys +from distutils.core import Command +from distutils.util import copy_tree + +class InstallPy (Command): + + options = [('dir=', 'd', "directory to install to"), + ('build-dir=' 'b', "build directory (where to install from)")] + + def set_default_options (self): + # let the 'install' command dictate our installation directory + self.dir = None + self.build_dir = None + + def set_final_options (self): + # If we don't have a 'dir' value, we'll have to ask the 'install' + # command for one. (This usually means the user ran 'install_py' + # directly, rather than going through 'install' -- so in reality, + # 'find_command_obj()' will create an 'install' command object, + # which we then query. + + self.set_undefined_options ('install', + ('build_lib', 'build_dir'), + ('install_site_lib', 'dir')) + + def run (self): + + self.set_final_options () + + # Dump entire contents of the build directory to the installation + # directory (that's the beauty of having a build directory!) + copy_tree (self.build_dir, self.dir, + verbose=self.distribution.verbose, + update=1) + + # run () + +# class InstallPy -- cgit v1.2.1 From 99942c9809e7014fa412f7d73d0121dd9c70f68c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 Mar 1999 14:00:06 +0000 Subject: Changes to allow passing an open file to the constructor (to support ProcessHierarchy's changes to support reading from a remote URL in ProcessDatabase). --- text_file.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/text_file.py b/text_file.py index eab498d7..6153dea3 100644 --- a/text_file.py +++ b/text_file.py @@ -13,9 +13,6 @@ import sys, os, string, re class TextFile: - filename = None - file = None - current_line = None default_options = { 'strip_comments': 1, 'comment_re': re.compile (r'\s*#.*'), @@ -26,7 +23,11 @@ class TextFile: 'collapse_ws': 0, } - def __init__ (self, filename=None, **options): + def __init__ (self, filename=None, file=None, **options): + + if filename is None and file is None: + raise RuntimeError, \ + "you must supply either or both of 'filename' and 'file'" # set values for all options -- either from client option hash # or fallback to default_options @@ -45,18 +46,16 @@ class TextFile: if not self.default_options.has_key (opt): raise KeyError, "invalid TextFile option '%s'" % opt - self.filename = filename - if self.filename: - self.open () - - - def open (self, filename=None): - if not self.filename: - if not filename: - raise RuntimeError, "must provide a filename somehow" - + if file is None: + self.open (filename) + else: self.filename = filename + self.file = file + self.current_line = 0 # assuming that file is at BOF! + + def open (self, filename): + self.filename = filename self.file = open (self.filename, 'r') self.current_line = 0 -- cgit v1.2.1 From 6c18a676e72670f94bd1f1a625d33d627fabb119 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 26 Mar 1999 21:48:59 +0000 Subject: Added 'linestart' array and 'unreadline()' method (makes parsing a lot easier). --- text_file.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/text_file.py b/text_file.py index 6153dea3..1d579565 100644 --- a/text_file.py +++ b/text_file.py @@ -52,6 +52,11 @@ class TextFile: self.filename = filename self.file = file self.current_line = 0 # assuming that file is at BOF! + + # 'linestart' stores the file offset of the start of each logical + # line; it is used to back up the file pointer when the caller + # wants to "unread" a line + self.linestart = [] def open (self, filename): @@ -81,6 +86,11 @@ class TextFile: buildup_line = '' while 1: + # record current file position; this will be appended to + # the linestart array *unless* we're accumulating a + # continued logical line + current_pos = self.file.tell() + # read the line, optionally strip comments line = self.file.readline() if self.strip_comments and line: @@ -101,6 +111,11 @@ class TextFile: self.current_line[1] = self.current_line[1] + 1 else: self.current_line = [self.current_line, self.current_line+1] + + # Forget current position: don't want to save it in the + # middle of a logical line + current_pos = None + # just an ordinary line, read it as usual else: if not line: @@ -111,7 +126,7 @@ class TextFile: self.current_line = self.current_line[1] + 1 else: self.current_line = self.current_line + 1 - + # strip whitespace however the client wants (leading and # trailing, or one or the other, or neither) @@ -128,6 +143,12 @@ class TextFile: if line == '' or line == '\n' and self.skip_blanks: continue + # if we're still here and have kept the current position, + # then this physical line starts a logical line; record its + # starting offset + if current_pos is not None: + self.linestart.append (current_pos) + if self.join_lines: if line[-1] == '\\': buildup_line = line[:-1] @@ -147,6 +168,14 @@ class TextFile: # end readline + def unreadline (self): + if not self.linestart: + raise IOError, "at beginning of file -- can't unreadline" + pos = self.linestart[-1] + del self.linestart[-1] + self.file.seek (pos) + + def readlines (self): lines = [] while 1: -- cgit v1.2.1 From b7509c18d82c99dc663763d51209a091f7306763 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 29 Mar 1999 18:01:49 +0000 Subject: Replaced the last attempt at an "unreadline" with one that actually works on non-seekable file-like objects, such as URLs. (Oops.) --- text_file.py | 47 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/text_file.py b/text_file.py index 1d579565..bc56a490 100644 --- a/text_file.py +++ b/text_file.py @@ -53,10 +53,10 @@ class TextFile: self.file = file self.current_line = 0 # assuming that file is at BOF! - # 'linestart' stores the file offset of the start of each logical - # line; it is used to back up the file pointer when the caller - # wants to "unread" a line - self.linestart = [] + # 'linebuf' is a stack of lines that will be emptied before we + # actually read from the file; it's only populated by an + # 'unreadline()' operation + self.linebuf = [] def open (self, filename): @@ -83,14 +83,18 @@ class TextFile: def readline (self): + # If any "unread" lines waiting in 'linebuf', return the top + # one. (We don't actually buffer read-ahead data -- lines only + # get put in 'linebuf' if the client explicitly does an + # 'unreadline()'. + if self.linebuf: + line = self.linebuf[-1] + del self.linebuf[-1] + return line + buildup_line = '' while 1: - # record current file position; this will be appended to - # the linestart array *unless* we're accumulating a - # continued logical line - current_pos = self.file.tell() - # read the line, optionally strip comments line = self.file.readline() if self.strip_comments and line: @@ -111,11 +115,6 @@ class TextFile: self.current_line[1] = self.current_line[1] + 1 else: self.current_line = [self.current_line, self.current_line+1] - - # Forget current position: don't want to save it in the - # middle of a logical line - current_pos = None - # just an ordinary line, read it as usual else: if not line: @@ -126,7 +125,7 @@ class TextFile: self.current_line = self.current_line[1] + 1 else: self.current_line = self.current_line + 1 - + # strip whitespace however the client wants (leading and # trailing, or one or the other, or neither) @@ -143,12 +142,6 @@ class TextFile: if line == '' or line == '\n' and self.skip_blanks: continue - # if we're still here and have kept the current position, - # then this physical line starts a logical line; record its - # starting offset - if current_pos is not None: - self.linestart.append (current_pos) - if self.join_lines: if line[-1] == '\\': buildup_line = line[:-1] @@ -168,14 +161,6 @@ class TextFile: # end readline - def unreadline (self): - if not self.linestart: - raise IOError, "at beginning of file -- can't unreadline" - pos = self.linestart[-1] - del self.linestart[-1] - self.file.seek (pos) - - def readlines (self): lines = [] while 1: @@ -185,6 +170,10 @@ class TextFile: lines.append (line) + def unreadline (self, line): + self.linebuf.append (line) + + if __name__ == "__main__": test_data = """# test file -- cgit v1.2.1 From a3af0a0d6a3cd60ff4a97d970b7ebe0a2dd6748b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Apr 1999 02:46:29 +0000 Subject: Changed to use the method versions of 'copy_file()', 'copy_tree()', and 'make_file()'-- that way, the verbose and dry-run flags are handled for free. --- command/build_py.py | 11 +++++------ command/install_lib.py | 4 +--- command/install_py.py | 4 +--- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index ae133f9c..0ed8486e 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -84,11 +84,10 @@ class BuildPy (Command): for i in range (len (infiles)): outdir = os.path.split (outfiles[i])[0] if not created.get(outdir): - mkpath (outdir, verbose=self.distribution.verbose) + self.mkpath (outdir) created[outdir] = 1 - copy_file (infiles[i], outfiles[i], - update=1, verbose=self.distribution.verbose) + self.copy_file (infiles[i], outfiles[i]) # (Optionally) compile .py to .pyc # XXX hey! we can't control whether we optimize or not; that's up @@ -102,9 +101,9 @@ class BuildPy (Command): # XXX can't assume this filename mapping! out_fn = string.replace (f, '.py', '.pyc') - make_file (f, out_fn, compile, (f,), - verbose=self.distribution.verbose, - update_message="compiling %s" % f) + self.make_file (f, out_fn, compile, (f,), + "compiling %s -> %s" % (f, out_fn), + "compilation of %s skipped" % f) # XXX ignore self.optimize for now, since we don't really know if # we're compiling optimally or not, and couldn't pick what to do diff --git a/command/install_lib.py b/command/install_lib.py index f2fa8420..22ab71e7 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -33,9 +33,7 @@ class InstallPy (Command): # Dump entire contents of the build directory to the installation # directory (that's the beauty of having a build directory!) - copy_tree (self.build_dir, self.dir, - verbose=self.distribution.verbose, - update=1) + self.copy_tree (self.build_dir, self.dir) # run () diff --git a/command/install_py.py b/command/install_py.py index f2fa8420..22ab71e7 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -33,9 +33,7 @@ class InstallPy (Command): # Dump entire contents of the build directory to the installation # directory (that's the beauty of having a build directory!) - copy_tree (self.build_dir, self.dir, - verbose=self.distribution.verbose, - update=1) + self.copy_tree (self.build_dir, self.dir) # run () -- cgit v1.2.1 From 4e091bdd15624bc17b8f1f45091848e23e8be099 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Apr 1999 02:54:20 +0000 Subject: Added 'dry_run' flag to most functions (to support the "shadow methods" that wrap them in the Command class). Fixed 'copy_file()' to use '_copy_file_contents()', not 'copyfile()' from shutil module -- no reference to shutil anymore. Added "not copying" announcement in 'copy_file()'. Wee comment fix. --- util.py | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/util.py b/util.py index 7c13abe0..f4a1df79 100644 --- a/util.py +++ b/util.py @@ -18,7 +18,7 @@ from distutils.errors import * # I don't use os.makedirs because a) it's new to Python 1.5.2, and # b) it blows up if the directory already exists (I want to silently # succeed in that case). -def mkpath (name, mode=0777, verbose=0): +def mkpath (name, mode=0777, verbose=0, dry_run=0): """Create a directory and any missing ancestor directories. If the directory already exists, return silently. Raise DistutilsFileError if unable to create some directory along the @@ -44,16 +44,20 @@ def mkpath (name, mode=0777, verbose=0): #print "stack of tails:", tails - # now 'head' contains the highest directory that already exists + # now 'head' contains the deepest directory that already exists + # (that is, the child of 'head' in 'name' is the highest directory + # that does *not* exist) for d in tails: #print "head = %s, d = %s: " % (head, d), head = os.path.join (head, d) if verbose: print "creating", head - try: - os.mkdir (head) - except os.error, (errno, errstr): - raise DistutilsFileError, "%s: %s" % (head, errstr) + + if not dry_run: + try: + os.mkdir (head) + except os.error, (errno, errstr): + raise DistutilsFileError, "%s: %s" % (head, errstr) # mkpath () @@ -147,7 +151,8 @@ def copy_file (src, dst, preserve_mode=1, preserve_times=1, update=0, - verbose=0): + verbose=0, + dry_run=0): """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is copied there with the same name; otherwise, it must be a @@ -163,7 +168,6 @@ def copy_file (src, dst, # XXX doesn't copy Mac-specific metadata - from shutil import copyfile from stat import * if not os.path.isfile (src): @@ -177,12 +181,16 @@ def copy_file (src, dst, dir = os.path.dirname (dst) if update and not newer (src, dst): + print "not copying %s (output up-to-date)" % src return if verbose: print "copying %s -> %s" % (src, dir) - copyfile (src, dst) + if dry_run: + return + + _copy_file_contents (src, dst) if preserve_mode or preserve_times: st = os.stat (src) if preserve_mode: @@ -198,7 +206,9 @@ def copy_tree (src, dst, preserve_times=1, preserve_symlinks=0, update=0, - verbose=0): + verbose=0, + dry_run=0): + """Copy an entire directory tree 'src' to a new location 'dst'. Both 'src' and 'dst' must be directory names. If 'src' is not a @@ -223,8 +233,8 @@ def copy_tree (src, dst, raise DistutilsFileError, \ "error listing files in %s: %s" % (src, errstr) - - mkpath (dst, verbose=verbose) + if not dry_run: + mkpath (dst, verbose=verbose) for n in names: src_name = os.path.join (src, n) @@ -232,14 +242,17 @@ def copy_tree (src, dst, if preserve_symlinks and os.path.islink (src_name): link_dest = os.readlink (src_name) - os.symlink (link_dest, dst_name) + if verbose: + print "linking %s -> %s" % (dst_name, link_dest) + if not dry_run: + os.symlink (link_dest, dst_name) elif os.path.isdir (src_name): copy_tree (src_name, dst_name, preserve_mode, preserve_times, preserve_symlinks, - update, verbose) + update, verbose, dry_run) else: copy_file (src_name, dst_name, preserve_mode, preserve_times, - update, verbose) + update, verbose, dry_run) # copy_tree () -- cgit v1.2.1 From 542cc6a75696e8edf46dd30c53b1fe5b73e86a2e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Apr 1999 02:58:07 +0000 Subject: Added all the "external action" methods (to make handling the verbose and dry-run flags consistently painless): 'execute()', 'mkpath()', 'copy_file()', 'copy_tree()', 'make_file()', and stub for 'make_files()' (not sure yet if it's useful). --- core.py | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/core.py b/core.py index 3a7443c7..980ba5b3 100644 --- a/core.py +++ b/core.py @@ -10,10 +10,11 @@ may be subclassed by clients for still more flexibility).""" __rcsid__ = "$Id$" -import sys +import sys, os import string, re from distutils.errors import * from distutils.fancy_getopt import fancy_getopt +from distutils import util # This is not *quite* the same as a Python NAME; I don't allow leading # underscores. The fact that they're very similar is no coincidence... @@ -594,4 +595,133 @@ class Command: self.distribution.run_command (command) + + # -- External world manipulation ----------------------------------- + + def execute (self, func, args, msg=None, level=1): + """Perform some action that affects the outside world (eg. + by writing to the filesystem). Such actions are special because + they should be disabled by the "dry run" flag (carried around by + the Command's Distribution), and should announce themselves if + the current verbosity level is high enough. This method takes + care of all that bureaucracy for you; all you have to do is + supply the funtion to call and an argument tuple for it (to + embody the "external action" being performed), a message to + print if the verbosity level is high enough, and an optional + verbosity threshold.""" + + + # Generate a message if we weren't passed one + if msg is None: + msg = "%s %s" % (func.__name__, `args`) + if msg[-2:] == ',)': # correct for singleton tuple + msg = msg[0:-2] + ')' + + # Print it if verbosity level is high enough + self.announce (msg, level) + + # And do it, as long as we're not in dry-run mode + if not self.distribution.dry_run: + apply (func, args) + + # execute() + + + def mkpath (self, name, mode=0777): + util.mkpath (name, mode, + self.distribution.verbose, self.distribution.dry_run) + + + def copy_file (self, infile, outfile, + preserve_mode=1, preserve_times=1, update=1, level=1): + """Copy a file respecting verbose and dry-run flags.""" + + util.copy_file (infile, outfile, + preserve_mode, preserve_times, + update, self.distribution.verbose >= level, + self.distribution.dry_run) + + + def copy_tree (self, infile, outfile, + preserve_mode=1, preserve_times=1, preserve_symlinks=0, + update=1, level=1): + """Copy an entire directory tree respecting verbose and dry-run + flags.""" + + util.copy_tree (infile, outfile, + preserve_mode, preserve_times, preserve_symlinks, + update, self.distribution.verbose >= level, + self.distribution.dry_run) + + + def make_file (self, infiles, outfile, func, args, + exec_msg=None, skip_msg=None, level=1): + + """Special case of 'execute()' for operations that process one or + more input files and generate one output file. Works just like + 'execute()', except the operation is skipped and a different + message printed if 'outfile' already exists and is newer than + all files listed in 'infiles'.""" + + + if exec_msg is None: + exec_msg = "generating %s from %s" % \ + (outfile, string.join (infiles, ', ')) + if skip_msg is None: + skip_msg = "skipping %s (inputs unchanged)" % outfile + + + # Allow 'infiles' to be a single string + if type (infiles) is StringType: + infiles = (infiles,) + elif type (infiles) not in (ListType, TupleType): + raise TypeError, \ + "'infiles' must be a string, or a list or tuple of strings" + + # XXX this stuff should probably be moved off to a function + # in 'distutils.util' + from stat import * + + if os.path.exists (outfile): + out_mtime = os.stat (outfile)[ST_MTIME] + + # Loop over all infiles. If any infile is newer than outfile, + # then we'll have to regenerate outfile + for f in infiles: + in_mtime = os.stat (f)[ST_MTIME] + if in_mtime > out_mtime: + runit = 1 + break + else: + runit = 0 + + else: + runit = 1 + + # If we determined that 'outfile' must be regenerated, then + # perform the action that presumably regenerates it + if runit: + self.execute (func, args, exec_msg, level) + + # Otherwise, print the "skip" message + else: + self.announce (skip_msg, level) + + # make_file () + + +# def make_files (self, infiles, outfiles, func, args, +# exec_msg=None, skip_msg=None, level=1): + +# """Special case of 'execute()' for operations that process one or +# more input files and generate one or more output files. Works +# just like 'execute()', except the operation is skipped and a +# different message printed if all files listed in 'outfiles' +# already exist and are newer than all files listed in +# 'infiles'.""" + +# pass + + + # end class Command -- cgit v1.2.1 From 8806d89547055f3a8b1246bf100338c5270b9fe9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 15 Apr 1999 17:50:19 +0000 Subject: 'warn()' method now takes an optional line number. --- text_file.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/text_file.py b/text_file.py index bc56a490..a27df06d 100644 --- a/text_file.py +++ b/text_file.py @@ -72,12 +72,14 @@ class TextFile: self.current_line = None - def warn (self, msg): + def warn (self, msg, line=None): + if line is None: + line = self.current_line sys.stderr.write (self.filename + ", ") - if type (self.current_line) is ListType: - sys.stderr.write ("lines %d-%d: " % tuple (self.current_line)) + if type (line) is ListType: + sys.stderr.write ("lines %d-%d: " % tuple (line)) else: - sys.stderr.write ("line %d: " % self.current_line) + sys.stderr.write ("line %d: " % line) sys.stderr.write (msg + "\n") -- cgit v1.2.1 From 4da83200ca7258a4c4d175c53f7b1b310db013cb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 2 May 1999 21:39:13 +0000 Subject: Rearranged things so that compilation of .py files is the responsibility of the 'install_py' command rather than 'build_py'. Obviously, this meant that the 'build_py' and 'install_py' modules had to change; less obviously, so did 'install' and 'build', since these higher-level commands must make options available to control the lower-level commands, and some compilation-related options had to migrate with the code. --- command/build.py | 7 ------- command/build_py.py | 28 +--------------------------- command/install.py | 6 ++++++ command/install_lib.py | 37 +++++++++++++++++++++++++++++++++---- command/install_py.py | 37 +++++++++++++++++++++++++++++++++---- 5 files changed, 73 insertions(+), 42 deletions(-) diff --git a/command/build.py b/command/build.py index 78860928..d0e939e9 100644 --- a/command/build.py +++ b/command/build.py @@ -15,10 +15,6 @@ class Build (Command): options = [('basedir=', 'b', "base directory for build library"), ('libdir=', 'l', "directory for platform-shared files"), ('platdir=', 'p', "directory for platform-specific files"), - - # Flags for 'build_py' - ('compile-py', None, "compile .py to .pyc"), - ('optimize-py', None, "compile .py to .pyo (optimized)"), ] def set_default_options (self): @@ -28,9 +24,6 @@ class Build (Command): self.libdir = None self.platdir = None - self.compile_py = 1 - self.optimize_py = 1 - def set_final_options (self): # 'libdir' and 'platdir' just default to 'lib' and 'plat' under # the base build directory diff --git a/command/build_py.py b/command/build_py.py index 0ed8486e..d956eef3 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -15,21 +15,15 @@ from distutils.util import mkpath, newer, make_file, copy_file class BuildPy (Command): options = [('dir=', 'd', "directory for platform-shared files"), - ('compile', 'c', "compile .py to .pyc"), - ('optimize', 'o', "compile .py to .pyo (optimized)"), ] def set_default_options (self): self.dir = None - self.compile = 1 - self.optimize = 1 def set_final_options (self): self.set_undefined_options ('build', - ('libdir', 'dir'), - ('compile_py', 'compile'), - ('optimize_py', 'optimize')) + ('libdir', 'dir')) def run (self): @@ -88,25 +82,5 @@ class BuildPy (Command): created[outdir] = 1 self.copy_file (infiles[i], outfiles[i]) - - # (Optionally) compile .py to .pyc - # XXX hey! we can't control whether we optimize or not; that's up - # to the invocation of the current Python interpreter (at least - # according to the py_compile docs). That sucks. - - if self.compile: - from py_compile import compile - - for f in outfiles: - # XXX can't assume this filename mapping! - out_fn = string.replace (f, '.py', '.pyc') - - self.make_file (f, out_fn, compile, (f,), - "compiling %s -> %s" % (f, out_fn), - "compilation of %s skipped" % f) - - # XXX ignore self.optimize for now, since we don't really know if - # we're compiling optimally or not, and couldn't pick what to do - # even if we did know. ;-( # end class BuildPy diff --git a/command/install.py b/command/install.py index 1f457807..ec73b1c1 100644 --- a/command/install.py +++ b/command/install.py @@ -43,6 +43,9 @@ class Install (Command): ('install-html=', None, "directory for HTML documentation"), ('install-info=', None, "directory for GNU info files"), + # Flags for 'build_py' + ('compile-py', None, "compile .py to .pyc"), + ('optimize-py', None, "compile .py to .pyo (optimized)"), ] def set_default_options (self): @@ -74,6 +77,9 @@ class Install (Command): self.install_html = None self.install_info = None + self.compile_py = 1 + self.optimize_py = 1 + def set_final_options (self): diff --git a/command/install_lib.py b/command/install_lib.py index 22ab71e7..f147af47 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -2,19 +2,25 @@ __rcsid__ = "$Id$" -import sys +import sys, string from distutils.core import Command from distutils.util import copy_tree class InstallPy (Command): options = [('dir=', 'd', "directory to install to"), - ('build-dir=' 'b', "build directory (where to install from)")] + ('build-dir=' 'b', "build directory (where to install from)"), + ('compile', 'c', "compile .py to .pyc"), + ('optimize', 'o', "compile .py to .pyo (optimized)"), + ] + def set_default_options (self): # let the 'install' command dictate our installation directory self.dir = None self.build_dir = None + self.compile = 1 + self.optimize = 1 def set_final_options (self): # If we don't have a 'dir' value, we'll have to ask the 'install' @@ -25,7 +31,10 @@ class InstallPy (Command): self.set_undefined_options ('install', ('build_lib', 'build_dir'), - ('install_site_lib', 'dir')) + ('install_site_lib', 'dir'), + ('compile_py', 'compile'), + ('optimize_py', 'optimize')) + def run (self): @@ -33,8 +42,28 @@ class InstallPy (Command): # Dump entire contents of the build directory to the installation # directory (that's the beauty of having a build directory!) - self.copy_tree (self.build_dir, self.dir) + outfiles = self.copy_tree (self.build_dir, self.dir) + # (Optionally) compile .py to .pyc + # XXX hey! we can't control whether we optimize or not; that's up + # to the invocation of the current Python interpreter (at least + # according to the py_compile docs). That sucks. + + if self.compile: + from py_compile import compile + + for f in outfiles: + # XXX can't assume this filename mapping! + out_fn = string.replace (f, '.py', '.pyc') + + self.make_file (f, out_fn, compile, (f,), + "compiling %s -> %s" % (f, out_fn), + "compilation of %s skipped" % f) + + # XXX ignore self.optimize for now, since we don't really know if + # we're compiling optimally or not, and couldn't pick what to do + # even if we did know. ;-( + # run () # class InstallPy diff --git a/command/install_py.py b/command/install_py.py index 22ab71e7..f147af47 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -2,19 +2,25 @@ __rcsid__ = "$Id$" -import sys +import sys, string from distutils.core import Command from distutils.util import copy_tree class InstallPy (Command): options = [('dir=', 'd', "directory to install to"), - ('build-dir=' 'b', "build directory (where to install from)")] + ('build-dir=' 'b', "build directory (where to install from)"), + ('compile', 'c', "compile .py to .pyc"), + ('optimize', 'o', "compile .py to .pyo (optimized)"), + ] + def set_default_options (self): # let the 'install' command dictate our installation directory self.dir = None self.build_dir = None + self.compile = 1 + self.optimize = 1 def set_final_options (self): # If we don't have a 'dir' value, we'll have to ask the 'install' @@ -25,7 +31,10 @@ class InstallPy (Command): self.set_undefined_options ('install', ('build_lib', 'build_dir'), - ('install_site_lib', 'dir')) + ('install_site_lib', 'dir'), + ('compile_py', 'compile'), + ('optimize_py', 'optimize')) + def run (self): @@ -33,8 +42,28 @@ class InstallPy (Command): # Dump entire contents of the build directory to the installation # directory (that's the beauty of having a build directory!) - self.copy_tree (self.build_dir, self.dir) + outfiles = self.copy_tree (self.build_dir, self.dir) + # (Optionally) compile .py to .pyc + # XXX hey! we can't control whether we optimize or not; that's up + # to the invocation of the current Python interpreter (at least + # according to the py_compile docs). That sucks. + + if self.compile: + from py_compile import compile + + for f in outfiles: + # XXX can't assume this filename mapping! + out_fn = string.replace (f, '.py', '.pyc') + + self.make_file (f, out_fn, compile, (f,), + "compiling %s -> %s" % (f, out_fn), + "compilation of %s skipped" % f) + + # XXX ignore self.optimize for now, since we don't really know if + # we're compiling optimally or not, and couldn't pick what to do + # even if we did know. ;-( + # run () # class InstallPy -- cgit v1.2.1 From 8e84928345bf830b0a8fe08f37627d315d5f897a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 2 May 1999 21:42:05 +0000 Subject: The 'copy_file()' and 'copy_tree()' functions in util.py now have meaningful return values: respectively, whether the copy was done, and the list of files that were copied. This meant some trivial changes in core.py as well: the Command methods that mirror 'copy_file()' and 'copy_tree()' have to pass on their return values. --- core.py | 16 ++++++++-------- util.py | 42 ++++++++++++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/core.py b/core.py index 980ba5b3..4b8c7838 100644 --- a/core.py +++ b/core.py @@ -636,10 +636,10 @@ class Command: preserve_mode=1, preserve_times=1, update=1, level=1): """Copy a file respecting verbose and dry-run flags.""" - util.copy_file (infile, outfile, - preserve_mode, preserve_times, - update, self.distribution.verbose >= level, - self.distribution.dry_run) + return util.copy_file (infile, outfile, + preserve_mode, preserve_times, + update, self.distribution.verbose >= level, + self.distribution.dry_run) def copy_tree (self, infile, outfile, @@ -648,10 +648,10 @@ class Command: """Copy an entire directory tree respecting verbose and dry-run flags.""" - util.copy_tree (infile, outfile, - preserve_mode, preserve_times, preserve_symlinks, - update, self.distribution.verbose >= level, - self.distribution.dry_run) + return util.copy_tree (infile, outfile, + preserve_mode,preserve_times,preserve_symlinks, + update, self.distribution.verbose >= level, + self.distribution.dry_run) def make_file (self, infiles, outfile, func, args, diff --git a/util.py b/util.py index f4a1df79..7aedc1c6 100644 --- a/util.py +++ b/util.py @@ -164,7 +164,11 @@ def copy_file (src, dst, 'update' is true, 'src' will only be copied if 'dst' does not exist, or if 'dst' does exist but is older than 'src'. If 'verbose' is true, then a one-line summary of the copy will be - printed to stdout.""" + printed to stdout. + + Return true if the file was copied (or would have been copied), + false otherwise (ie. 'update' was true and the destination is + up-to-date).""" # XXX doesn't copy Mac-specific metadata @@ -181,14 +185,15 @@ def copy_file (src, dst, dir = os.path.dirname (dst) if update and not newer (src, dst): - print "not copying %s (output up-to-date)" % src - return + if verbose: + print "not copying %s (output up-to-date)" % src + return 0 if verbose: print "copying %s -> %s" % (src, dir) if dry_run: - return + return 1 _copy_file_contents (src, dst) if preserve_mode or preserve_times: @@ -198,6 +203,8 @@ def copy_file (src, dst, if preserve_times: os.utime (dst, (st[ST_ATIME], st[ST_MTIME])) + return 1 + # copy_file () @@ -213,9 +220,12 @@ def copy_tree (src, dst, """Copy an entire directory tree 'src' to a new location 'dst'. Both 'src' and 'dst' must be directory names. If 'src' is not a directory, raise DistutilsFileError. If 'dst' does not exist, it - is created with 'mkpath'. The endresult of the copy is that + is created with 'mkpath'. The end result of the copy is that every file in 'src' is copied to 'dst', and directories under - 'src' are recursively copied to 'dst'. + 'src' are recursively copied to 'dst'. Return the list of files + copied (under their output names) -- note that if 'update' is true, + this might be less than the list of files considered. Return + value is not affected by 'dry_run'. 'preserve_mode' and 'preserve_times' are the same as for 'copy_file'; note that they only apply to regular files, not to @@ -236,6 +246,8 @@ def copy_tree (src, dst, if not dry_run: mkpath (dst, verbose=verbose) + outputs = [] + for n in names: src_name = os.path.join (src, n) dst_name = os.path.join (dst, n) @@ -246,13 +258,19 @@ def copy_tree (src, dst, print "linking %s -> %s" % (dst_name, link_dest) if not dry_run: os.symlink (link_dest, dst_name) + outputs.append (dst_name) + elif os.path.isdir (src_name): - copy_tree (src_name, dst_name, - preserve_mode, preserve_times, preserve_symlinks, - update, verbose, dry_run) + outputs[-1:] = \ + copy_tree (src_name, dst_name, + preserve_mode, preserve_times, preserve_symlinks, + update, verbose, dry_run) else: - copy_file (src_name, dst_name, - preserve_mode, preserve_times, - update, verbose, dry_run) + if (copy_file (src_name, dst_name, + preserve_mode, preserve_times, + update, verbose, dry_run)): + outputs.append (dst_name) + + return outputs # copy_tree () -- cgit v1.2.1 From 4c91f4ee1b115815b34eb36da15553c41be1ef80 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Jun 1999 01:58:36 +0000 Subject: Now handles NT, through '_init_nt()' function (courtesy of Amos Latteier ). --- sysconfig.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index e71ae466..5c60ca4a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -129,6 +129,16 @@ def _init_posix(): parse_makefile(open(get_makefile_filename()), g) +def _init_nt(): + """Initialize the module as appropriate for NT""" + g=globals() + # load config.h, though I don't know how useful this is + parse_config_h(open( + os.path.join(sys.exec_prefix, "include", "config.h")), g) + # set basic install directories + g['LIBDEST']=os.path.join(sys.exec_prefix, "Lib") + g['BINLIBDEST']=os.path.join(sys.exec_prefix, "Lib") + try: exec "_init_" + os.name @@ -139,3 +149,4 @@ else: exec "_init_%s()" % os.name del _init_posix +del _init_nt -- cgit v1.2.1 From acd36b84863fab989ea5d731f787300f579e7861 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Jun 1999 02:02:00 +0000 Subject: Added the 'have_run' dictionary to Distribution, and changed 'run_command()' to refer to it before attempting to run a command -- that way, command classes can freely invoke other commands without fear of duplicate execution. Beefed up some comments and docstrings. --- core.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/core.py b/core.py index 4b8c7838..8a6cf720 100644 --- a/core.py +++ b/core.py @@ -136,6 +136,10 @@ class Distribution: self.licence = None self.description = None + # 'cmdclass' maps command names to class objects, so we + # can 1) quickly figure out which class to instantiate when + # we need to create a new command object, and 2) have a way + # for the client to override command classes self.cmdclass = {} # The rest of these are really the business of various commands, @@ -152,9 +156,23 @@ class Distribution: setattr (self, k, attrs[k]) # And now initialize bookkeeping stuff that can't be supplied by - # the caller at all + # the caller at all. 'command_obj' maps command names to + # Command instances -- that's how we enforce that every command + # class is a singleton. self.command_obj = {} + # 'have_run' maps command names to boolean values; it keeps track + # of whether we have actually run a particular command, to make it + # cheap to "run" a command whenever we think we might need to -- if + # it's already been done, no need for expensive filesystem + # operations, we just check the 'have_run' dictionary and carry on. + # It's only safe to query 'have_run' for a command class + # that has been instantiated -- a false value will be put inserted + # when the command object is created, and replaced with a true + # value when the command is succesfully run. Thus it's + # probably best to use '.get()' rather than a straight lookup. + self.have_run = {} + # __init__ () @@ -211,6 +229,7 @@ class Distribution: # attribute, but we're not enforcing that anywhere! args = fancy_getopt (cmd_obj.options, cmd_obj, args[1:]) self.command_obj[command] = cmd_obj + self.have_run[command] = 0 # while args @@ -347,12 +366,23 @@ class Distribution: # -- Methods that operate on its Commands -------------------------- def run_command (self, command): - """Create a command object for 'command' if necessary, and - run the command by invoking its 'run()' method.""" + + """Do whatever it takes to run a command (including nothing at all, + if the command has already been run). Specifically: if we have + already created and run the command named by 'command', return + silently without doing anything. If the command named by + 'command' doesn't even have a command object yet, create one. + Then invoke 'run()' on that command object (or an existing + one).""" + + # Already been here, done that? then return silently. + if self.have_run.get (command): + return self.announce ("running " + command) cmd_obj = self.find_command_obj (command) cmd_obj.run () + self.have_run[command] = 1 def get_command_option (self, command, option): -- cgit v1.2.1 From 50fc61eddf6d3a2028dddaeeb106bc783de0efd5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Jun 1999 02:04:36 +0000 Subject: Hacked 'set_final_options()' to set (hopefully) appropriate values for 'install_site_lib' and install_site_platlib' on non-POSIX platforms. Should at least work for NT, as this is adopted from Amos Latteier's NT patches. Also added extensive comments bitching about the inadequacy of the current model, both under POSIX and NT (and probably other) systems. --- command/install.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/command/install.py b/command/install.py index ec73b1c1..54b81879 100644 --- a/command/install.py +++ b/command/install.py @@ -48,6 +48,7 @@ class Install (Command): ('optimize-py', None, "compile .py to .pyo (optimized)"), ] + def set_default_options (self): self.build_base = None @@ -83,6 +84,29 @@ class Install (Command): def set_final_options (self): + # XXX this method is where the default installation directories + # for modules and extension modules are determined. (Someday, + # the default installation directories for scripts, + # documentation, and whatever else the Distutils can build and + # install will also be determined here.) Thus, this is a pretty + # important place to fiddle with for anyone interested in + # installation schemes for the Python library. Issues that + # are not yet resolved to my satisfaction: + # * how much platform dependence should be here, and + # how much can be pushed off to sysconfig (or, better, the + # Makefiles parsed by sysconfig)? + # * how independent of Python version should this be -- ie. + # should we have special cases to handle Python 1.5 and + # older, and then do it "the right way" for 1.6? Or should + # we install a site.py along with Distutils under pre-1.6 + # Python to take care of the current deficiencies in + # Python's library installation scheme? + # + # Currently, this method has hacks to distinguish POSIX from + # non-POSIX systems (for installation of site-local modules), + # and assumes the Python 1.5 installation tree with no site.py + # to fix things. + # Figure out the build directories, ie. where to install from self.set_peer_option ('build', 'basedir', self.build_base) self.set_undefined_options ('build', @@ -114,15 +138,37 @@ class Install (Command): self.install_platlib = \ self.replace_sys_prefix ('BINLIBDEST', ('lib','python1.5'), 1) + + # Here is where we decide where to install most library files: + # on POSIX systems, they go to 'site-packages' under the + # install_lib (determined above -- typically + # /usr/local/lib/python1.x). Unfortunately, both + # platform-independent (.py*) and platform-specific (.so) files + # go to this directory, since there is no site-packages under + # $exec_prefix in the usual way that Python builds sys.path. On + # non-POSIX systems, the situation is even worse: everything + # gets dumped right into $exec_prefix, not even a lib + # subdirectory! But apparently that's what needs to be done to + # make it work under Python 1.5 -- hope we can get this fixed + # for 1.6! + if self.install_site_lib is None: - self.install_site_lib = \ - os.path.join (self.install_lib, 'site-packages') + if os.name == 'posix': + self.install_site_lib = \ + os.path.join (self.install_lib, 'site-packages') + else: + self.install_site_lib = self.exec_prefix + if self.install_site_platlib is None: - # XXX ugh! this puts platform-specific files in with shared files, - # with no nice way to override it! (this might be a Python - # problem, though, not a Distutils problem...) - self.install_site_platlib = \ - os.path.join (self.install_lib, 'site-packages') + if os.name == 'posix': + # XXX ugh! this puts platform-specific files in with + # shared files, with no nice way to override it! (this + # might be a Python problem, though, not a Distutils + # problem...) + self.install_site_platlib = \ + os.path.join (self.install_lib, 'site-packages') + else: + self.install_site_platlib = self.exec_prefix #if self.install_scheme == 'site': # install_lib = self.install_site_lib @@ -181,6 +227,9 @@ class Install (Command): self.set_final_options () + # Obviously have to build before we can install + self.run_peer ('build') + # Install modules in two steps: "platform-shared" files (ie. pure # python modules) and platform-specific files (compiled C # extensions). -- cgit v1.2.1 From 93d1813d1383f5f6c7cd004143fd97c8d18cbecf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Jun 1999 17:05:21 +0000 Subject: On David Ascher's recommendation: reversed order of 'utime()' and 'chmod()' in 'copy_file()'. --- util.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 7aedc1c6..9a299dfd 100644 --- a/util.py +++ b/util.py @@ -198,10 +198,13 @@ def copy_file (src, dst, _copy_file_contents (src, dst) if preserve_mode or preserve_times: st = os.stat (src) - if preserve_mode: - os.chmod (dst, S_IMODE (st[ST_MODE])) + + # According to David Ascher , utime() should be done + # before chmod() (at least under NT). if preserve_times: os.utime (dst, (st[ST_ATIME], st[ST_MTIME])) + if preserve_mode: + os.chmod (dst, S_IMODE (st[ST_MODE])) return 1 -- cgit v1.2.1 From 3cba55ebdce6957864921de0b015f6118afdf2c0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 10 Jul 1999 02:01:44 +0000 Subject: Don't pollute importer's namespace with type objects from types modules. Added DistutilsPlatformError. --- errors.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/errors.py b/errors.py index 6605ad2c..f5ef3851 100644 --- a/errors.py +++ b/errors.py @@ -12,9 +12,9 @@ symbols whose names start with "Distutils" and end with "Error".""" __rcsid__ = "$Id$" -from types import * +import types -if type (RuntimeError) is ClassType: +if type (RuntimeError) is types.ClassType: # DistutilsError is the root of all Distutils evil. class DistutilsError (Exception): @@ -52,6 +52,12 @@ if type (RuntimeError) is ClassType: class DistutilsOptionError (DistutilsError): pass + # DistutilsPlatformError is raised when we find that we don't + # know how to do something on the current platform (but we do + # know how to do it on some platform). + class DistutilsPlatformError (DistutilsError): + pass + # String-based exceptions else: DistutilsError = 'DistutilsError' @@ -61,3 +67,6 @@ else: DistutilsArgError = 'DistutilsArgError' DistutilsFileError = 'DistutilsFileError' DistutilsOptionError = 'DistutilsOptionError' + DistutilsPlatformError = 'DistutilsPlatformError' + +del types -- cgit v1.2.1 From 029a6337d35a2ddabacbf5e00a07bfb87e163fc7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 10 Jul 1999 02:02:31 +0000 Subject: Added a self-berating command relating to installation directories for module distributions that contain platform-specific files. --- command/install.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/command/install.py b/command/install.py index 54b81879..01ea0052 100644 --- a/command/install.py +++ b/command/install.py @@ -165,6 +165,14 @@ class Install (Command): # shared files, with no nice way to override it! (this # might be a Python problem, though, not a Distutils # problem...) + + # NO: the way to fix this is + # * any platform-dependent files in distribution? + # yes: install under exec-prefix + # no: install under prefix + # ...which will require a pretty major rethink of all + # this. Damn. + self.install_site_platlib = \ os.path.join (self.install_lib, 'site-packages') else: -- cgit v1.2.1 From 6c7798416f22b36ecf283e3f379f29173cad60c5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 10 Jul 1999 02:03:53 +0000 Subject: The abstract base class that defines the C/C++ compiler abstraction model. --- ccompiler.py | 313 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 ccompiler.py diff --git a/ccompiler.py b/ccompiler.py new file mode 100644 index 00000000..7be0ba22 --- /dev/null +++ b/ccompiler.py @@ -0,0 +1,313 @@ +"""distutils.ccompiler + +Contains CCompiler, an abstract base class that defines the interface +for the Distutils compiler abstraction model.""" + +# created 1999/07/05, Greg Ward + +__rcsid__ = "$Id$" + +import os +from types import * +from copy import copy +from distutils.errors import * + + +class CCompiler: + """Abstract base class to define the interface that must be implemented + by real compiler abstraction classes. Might have some use as a + place for shared code, but it's not yet clear what code can be + shared between compiler abstraction models for different platforms. + + The basic idea behind a compiler abstraction class is that each + instance can be used for all the compile/link steps in building + a single project. Thus, attributes common to all of those compile + and link steps -- include directories, macros to define, libraries + to link against, etc. -- are attributes of the compiler instance. + To allow for variability in how individual files are treated, + most (all?) of those attributes may be varied on a per-compilation + or per-link basis.""" + + + # XXX things not handled by this compiler abstraction model: + # * client can't provide additional options for a compiler, + # e.g. warning, optimization, debugging flags. Perhaps this + # should be the domain of concrete compiler abstraction classes + # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base + # class should have methods for the common ones. + # * can't put output files (object files, libraries, whatever) + # into a separate directory from their inputs. Should this be + # handled by an 'output_dir' attribute of the whole object, or a + # parameter to the compile/link_* methods, or both? + # * can't completely override the include or library searchg + # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". + # I'm not sure how widely supported this is even by POSIX + # compilers, much less on other platforms. And I'm even less + # sure how useful it is; probably for cross-compiling, but I + # have no intention of supporting that. + # * can't do really freaky things with the library list/library + # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against + # different versions of libfoo.a in different locations. I + # think this is useless without the ability to null out the + # library search path anyways. + # * don't deal with verbose and dry-run flags -- probably a + # CCompiler object should just drag them around the way the + # Distribution object does (either that or we have to drag + # around a Distribution object, which is what Command objects + # do... but might be kind of annoying) + + + def __init__ (self): + + # 'macros': a list of macro definitions (or undefinitions). A + # macro definition is a 2-tuple (name, value), where the value is + # either a string or None (no explicit value). A macro + # undefinition is a 1-tuple (name,). + self.macros = [] + + + # 'include_dirs': a list of directories to search for include files + self.include_dirs = [] + + # 'libraries': a list of libraries to include in any link + # (library names, not filenames: eg. "foo" not "libfoo.a") + self.libraries = [] + + # 'library_dirs': a list of directories to search for libraries + self.library_dirs = [] + + # 'objects': a list of object files (or similar, such as explicitly + # named library files) to include on any link + self.objects = [] + + # __init__ () + + + def _find_macro (self, name): + i = 0 + for defn in self.macros: + if defn[0] == name: + return i + i = i + 1 + + return None + + + def _check_macro_definitions (self, definitions): + """Ensures that every element of 'definitions' is a valid macro + definition, ie. either (name,value) 2-tuple or a (name,) + tuple. Do nothing if all definitions are OK, raise + TypeError otherwise.""" + + for defn in definitions: + if not (type (defn) is TupleType and + (len (defn) == 1 or + (len (defn) == 2 and + (type (defn[1]) is StringType or defn[1] is None))) and + type (defn[0]) is StringType): + raise TypeError, \ + ("invalid macro definition '%s': " % defn) + \ + "must be tuple (string,), (string, string), or " + \ + "(string, None)" + + + # -- Bookkeeping methods ------------------------------------------- + + def define_macro (self, name, value=None): + """Define a preprocessor macro for all compilations driven by + this compiler object. The optional parameter 'value' should be + a string; if it is not supplied, then the macro will be defined + without an explicit value and the exact outcome depends on the + compiler used (XXX true? does ANSI say anything about this?)""" + + # Delete from the list of macro definitions/undefinitions if + # already there (so that this one will take precedence). + i = self._find_macro (name) + if i is not None: + del self.macros[i] + + defn = (name, value) + self.macros.append (defn) + + + def undefine_macro (self, name): + """Undefine a preprocessor macro for all compilations driven by + this compiler object. If the same macro is defined by + 'define_macro()' and undefined by 'undefine_macro()' the last + call takes precedence (including multiple redefinitions or + undefinitions). If the macro is redefined/undefined on a + per-compilation basis (ie. in the call to 'compile()'), then + that takes precedence.""" + + # Delete from the list of macro definitions/undefinitions if + # already there (so that this one will take precedence). + i = self._find_macro (name) + if i is not None: + del self.macros[i] + + undefn = (name,) + self.macros.append (undefn) + + + def add_include_dir (self, dir): + """Add 'dir' to the list of directories that will be searched + for header files. The compiler is instructed to search + directories in the order in which they are supplied by + successive calls to 'add_include_dir()'.""" + self.include_dirs.append (dir) + + def set_include_dirs (self, dirs): + """Set the list of directories that will be searched to 'dirs' + (a list of strings). Overrides any preceding calls to + 'add_include_dir()'; subsequence calls to 'add_include_dir()' + add to the list passed to 'set_include_dirs()'. This does + not affect any list of standard include directories that + the compiler may search by default.""" + self.include_dirs = copy (dirs) + + + def add_library (self, libname): + """Add 'libname' to the list of libraries that will be included + in all links driven by this compiler object. Note that + 'libname' should *not* be the name of a file containing a + library, but the name of the library itself: the actual filename + will be inferred by the linker, the compiler, or the compiler + abstraction class (depending on the platform). + + The linker will be instructed to link against libraries in the + order they were supplied to 'add_library()' and/or + 'set_libraries()'. It is perfectly valid to duplicate library + names; the linker will be instructed to link against libraries + as many times as they are mentioned.""" + self.libraries.append (libname) + + def set_libraries (self, libnames): + """Set the list of libraries to be included in all links driven + by this compiler object to 'libnames' (a list of strings). + This does not affect any standard system libraries that the + linker may include by default.""" + + self.libraries = copy (libnames) + + + def add_library_dir (self, dir): + """Add 'dir' to the list of directories that will be searched for + libraries specified to 'add_library()' and 'set_libraries()'. + The linker will be instructed to search for libraries in the + order they are supplied to 'add_library_dir()' and/or + 'set_library_dirs()'.""" + self.library_dirs.append (dir) + + def set_library_dirs (self, dirs): + """Set the list of library search directories to 'dirs' (a list + of strings). This does not affect any standard library + search path that the linker may search by default.""" + self.library_dirs = copy (dirs) + + + def add_link_object (self, object): + """Add 'object' to the list of object files (or analogues, such + as explictly named library files or the output of "resource + compilers") to be included in every link driven by this + compiler object.""" + self.objects.append (object) + + def set_link_objects (self, objects): + """Set the list of object files (or analogues) to be included + in every link to 'objects'. This does not affect any + standard object files that the linker may include by default + (such as system libraries).""" + self.objects = copy (objects) + + + # -- Worker methods ------------------------------------------------ + # (must be implemented by subclasses) + + def compile (self, + sources, + macros=None, + includes=None): + """Compile one or more C/C++ source files. 'sources' must be + a list of strings, each one the name of a C/C++ source + file. Return a list of the object filenames generated + (one for each source filename in 'sources'). + + 'macros', if given, must be a list of macro definitions. A + macro definition is either a (name, value) 2-tuple or a (name,) + 1-tuple. The former defines a macro; if the value is None, the + macro is defined without an explicit value. The 1-tuple case + undefines a macro. Later definitions/redefinitions/ + undefinitions take precedence. + + 'includes', if given, must be a list of strings, the directories + to add to the default include file search path for this + compilation only.""" + pass + + + # XXX this is kind of useless without 'link_binary()' or + # 'link_executable()' or something -- or maybe 'link_static_lib()' + # should not exist at all, and we just have 'link_binary()'? + def link_static_lib (self, + objects, + output_libname, + libraries=None, + library_dirs=None): + """Link a bunch of stuff together to create a static library + file. The "bunch of stuff" consists of the list of object + files supplied as 'objects', the extra object files supplied + to 'add_link_object()' and/or 'set_link_objects()', the + libraries supplied to 'add_library()' and/or + 'set_libraries()', and the libraries supplied as 'libraries' + (if any). + + 'output_libname' should be a library name, not a filename; + the filename will be inferred from the library name. + + 'library_dirs', if supplied, should be a list of additional + directories to search on top of the system default and those + supplied to 'add_library_dir()' and/or 'set_library_dirs()'.""" + + pass + + + # XXX what's better/more consistent/more universally understood + # terminology: "shared library" or "dynamic library"? + + def link_shared_lib (self, + objects, + output_libname, + libraries=None, + library_dirs=None): + """Link a bunch of stuff together to create a shared library + file. Has the same effect as 'link_static_lib()' except + that the filename inferred from 'output_libname' will most + likely be different, and the type of file generated will + almost certainly be different.""" + pass + + def link_shared_object (self, + objects, + output_filename, + libraries=None, + library_dirs=None): + """Link a bunch of stuff together to create a shared object + file. Much like 'link_shared_lib()', except the output + filename is explicitly supplied as 'output_filename'.""" + pass + +# class CCompiler + + +def new_compiler (plat=None): + """Generate a CCompiler instance for platform 'plat' (or the + current platform, if 'plat' not supplied). Really instantiates + some concrete subclass of CCompiler, of course.""" + + if plat is None: plat = os.name + if plat == 'posix': + from unixccompiler import UnixCCompiler + return UnixCCompiler () + else: + raise DistutilsPlatformError, \ + "don't know how to compile C/C++ code on platform %s" % plat -- cgit v1.2.1 From 49295f84d81b8262a04a374d1c1c33e8bbbff6e2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 10 Jul 1999 02:04:22 +0000 Subject: The first concrete subclass of CCompiler: defines a barebones Unix C compiler. --- unixccompiler.py | 192 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 unixccompiler.py diff --git a/unixccompiler.py b/unixccompiler.py new file mode 100644 index 00000000..fb5ed667 --- /dev/null +++ b/unixccompiler.py @@ -0,0 +1,192 @@ +"""distutils.unixccompiler + +Contains the UnixCCompiler class, a subclass of CCompiler that handles +the "typical" Unix-style command-line C compiler: + * macros defined with -Dname[=value] + * macros undefined with -Uname + * include search directories specified with -Idir + * libraries specified with -lllib + * library search directories specified with -Ldir + * compile handled by 'cc' (or similar) executable with -c option: + compiles .c to .o + * link static library handled by 'ar' command (possibly with 'ranlib') + * link shared library handled by 'cc -shared' +""" + +# created 1999/07/05, Greg Ward + +__rcsid__ = "$Id$" + +import string +from types import * +from sysconfig import \ + CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO +from ccompiler import CCompiler + + +# XXX Things not currently handled: +# * optimization/debug/warning flags; we just use whatever's in Python's +# Makefile and live with it. Is this adequate? If not, we might +# have to have a bunch of subclasses GNUCCompiler, SGICCompiler, +# SunCCompiler, and I suspect down that road lies madness. +# * even if we don't know a warning flag from an optimization flag, +# we need some way for outsiders to feed preprocessor/compiler/linker +# flags in to us -- eg. a sysadmin might want to mandate certain flags +# via a site config file, or a user might want to set something for +# compiling this module distribution only via the setup.py command +# line, whatever. As long as these options come from something on the +# current system, they can be as system-dependent as they like, and we +# should just happily stuff them into the preprocessor/compiler/linker +# options and carry on. + + +class UnixCCompiler (CCompiler): + + # XXX any -I and -D options that we get from Makefile (via sysconfig) + # are preserved, but not treated specially: that is, they are not put + # in the self.include_dirs and self.macros, etc. lists that we inherit + # from CCompiler. I'm not sure if this is right, wrong or indifferent, + # but it should probably be a documented part of the CCompiler API: + # ie. there are *three* kinds of include directories, those from the + # compiler, those from Python's Makefiles, and those supplied to + # {add,set}_include_dirs() -- and 'set_include_dirs()' only overrides + # the last kind! I suspect the same applies to libraries and library + # directories -- anything else? + + def __init__ (self): + + CCompiler.__init__ (self) + + self.preprocess_options = None + self.compile_options = None + + # munge CC and OPT together in case there are flags stuck in CC + (self.cc, self.ccflags) = \ + _split_command (CC + ' ' + OPT) + self.ccflags_shared = string.split (CCSHARED) + + (self.ld_shared, self.ldflags_shared) = \ + _split_command (LDSHARED) + + + def compile (self, + sources, + macros=[], + includes=[]): + + if type (macros) is not ListType: + raise TypeError, \ + "'macros' (if supplied) must be a list of tuples" + if type (includes) is not ListType: + raise TypeError, \ + "'includes' (if supplied) must be a list of strings" + + pp_opts = _gen_preprocess_options (self.macros + macros, + self.include_dirs + includes) + + # use of ccflags_shared means we're blithely assuming that we're + # compiling for inclusion in a shared object! (will have to fix + # this when I add the ability to build a new Python) + cc_args = ['-c'] + pp_opts + \ + self.ccflags + self.ccflags_shared + \ + sources + + # this will change to 'spawn' when I have it! + print string.join ([self.cc] + cc_args, ' ') + + + # XXX punting on 'link_static_lib()' for now -- it might be better for + # CCompiler to mandate just 'link_binary()' or some such to build a new + # Python binary; it would then take care of linking in everything + # needed for the new Python without messing with an intermediate static + # library. + + def link_shared_lib (self, + objects, + output_libname, + libraries=None, + library_dirs=None): + # XXX should we sanity check the library name? (eg. no + # slashes) + self.link_shared_object (objects, "lib%s%s" % (output_libname, SO)) + + + def link_shared_object (self, + objects, + output_filename, + libraries=[], + library_dirs=[]): + + lib_opts = _gen_lib_options (self.libraries + libraries, + self.library_dirs + library_dirs) + ld_args = self.ldflags_shared + lib_opts + \ + objects + ['-o', output_filename] + + print string.join ([self.ld_shared] + ld_args, ' ') + + +# class UnixCCompiler + + +def _split_command (cmd): + """Split a command string up into the progam to run (a string) and + the list of arguments; return them as (cmd, arglist).""" + args = string.split (cmd) + return (args[0], args[1:]) + + +def _gen_preprocess_options (macros, includes): + + # XXX it would be nice (mainly aesthetic, and so we don't generate + # stupid-looking command lines) to go over 'macros' and eliminate + # redundant definitions/undefinitions (ie. ensure that only the + # latest mention of a particular macro winds up on the command + # line). I don't think it's essential, though, since most (all?) + # Unix C compilers only pay attention to the latest -D or -U + # mention of a macro on their command line. Similar situation for + # 'includes'. I'm punting on both for now. Anyways, weeding out + # redundancies like this should probably be the province of + # CCompiler, since the data structures used are inherited from it + # and therefore common to all CCompiler classes. + + + pp_opts = [] + for macro in macros: + if len (macro) == 1: # undefine this macro + pp_opts.append ("-U%s" % macro[0]) + elif len (macro) == 2: + if macro[1] is None: # define with no explicit value + pp_opts.append ("-D%s" % macro[0]) + else: + # XXX *don't* need to be clever about quoting the + # macro value here, because we're going to avoid the + # shell at all costs when we spawn the command! + pp_opts.append ("-D%s=%s" % macro) + + for dir in includes: + pp_opts.append ("-I%s" % dir) + + return pp_opts + +# _gen_preprocess_options () + + +def _gen_lib_options (libraries, library_dirs): + + lib_opts = [] + + for dir in library_dirs: + lib_opts.append ("-L%s" % dir) + + # XXX it's important that we *not* remove redundant library mentions! + # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to + # resolve all symbols. I just hope we never have to say "-lfoo obj.o + # -lbar" to get things to work -- that's certainly a possibility, but a + # pretty nasty way to arrange your C code. + + for lib in libraries: + lib_opts.append ("-l%s" % lib) + + return lib_opts + +# _gen_lib_options () -- cgit v1.2.1 From 8c9d03208b4de8de842b039922fad2373ea6bf0f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 10 Aug 1999 20:09:38 +0000 Subject: Allow comment characters (#) to be escaped: - did away with 'comment_re' option -- it's just not that simple anymore - heavily revised the main logic in 'readline()' to accomodate this Beefed up 'warn()': 'line' can be list or tuple, and 'msg' is automatically converted to a string. --- text_file.py | 51 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/text_file.py b/text_file.py index a27df06d..2e034301 100644 --- a/text_file.py +++ b/text_file.py @@ -15,7 +15,6 @@ import sys, os, string, re class TextFile: default_options = { 'strip_comments': 1, - 'comment_re': re.compile (r'\s*#.*'), 'skip_blanks': 1, 'join_lines': 0, 'lstrip_ws': 0, @@ -33,10 +32,7 @@ class TextFile: # or fallback to default_options for opt in self.default_options.keys(): if options.has_key (opt): - if opt == 'comment_re' and type (options[opt]) is StringType: - self.comment_re = re.compile (options[opt]) - else: - setattr (self, opt, options[opt]) + setattr (self, opt, options[opt]) else: setattr (self, opt, self.default_options[opt]) @@ -76,11 +72,11 @@ class TextFile: if line is None: line = self.current_line sys.stderr.write (self.filename + ", ") - if type (line) is ListType: + if type (line) in (ListType, TupleType): sys.stderr.write ("lines %d-%d: " % tuple (line)) else: sys.stderr.write ("line %d: " % line) - sys.stderr.write (msg + "\n") + sys.stderr.write (str (msg) + "\n") def readline (self): @@ -97,15 +93,42 @@ class TextFile: buildup_line = '' while 1: - # read the line, optionally strip comments + # read the line, make it None if EOF line = self.file.readline() + if line == '': line = None + if self.strip_comments and line: - line = self.comment_re.sub ('', line) + + # Look for the first "#" in the line. If none, never + # mind. If we find one and it's the first character, or + # is not preceded by "\", then it starts a comment -- + # strip the comment, strip whitespace before it, and + # carry on. Otherwise, it's just an escaped "#", so + # unescape it (and any other escaped "#"'s that might be + # lurking in there) and otherwise leave the line alone. + + pos = string.find (line, "#") + if pos == -1: # no "#" -- no comments + pass + elif pos == 0 or line[pos-1] != "\\": # it's a comment + # Have to preserve the trailing newline; if + # stripping comments resulted in an empty line, we'd + # have no way to distinguish end-of-file! (NB. this + # means that if the final line is all comment and + # has to trailing newline, we will think that it's + # EOF; I think that's OK.) + has_newline = (line[-1] == '\n') + line = line[0:pos] + if has_newline: line = line + '\n' + + else: # it's an escaped "#" + line = string.replace (line, "\\#", "#") + # did previous line end with a backslash? then accumulate if self.join_lines and buildup_line: # oops: end of file - if not line: + if line is None: self.warn ("continuation line immediately precedes " "end-of-file") return buildup_line @@ -119,7 +142,7 @@ class TextFile: self.current_line = [self.current_line, self.current_line+1] # just an ordinary line, read it as usual else: - if not line: + if line is None: # eof return None # still have to be careful about incrementing the line number! @@ -217,15 +240,15 @@ continues on next line out_file.close () in_file = TextFile (filename, strip_comments=0, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) + lstrip_ws=0, rstrip_ws=0) test_input (1, "no processing", in_file, result1) in_file = TextFile (filename, strip_comments=1, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) + lstrip_ws=0, rstrip_ws=0) test_input (2, "strip comments", in_file, result2) in_file = TextFile (filename, strip_comments=0, skip_blanks=1, - lstrip_ws=0, rstrip_ws=0) + lstrip_ws=0, rstrip_ws=0) test_input (3, "strip blanks", in_file, result3) in_file = TextFile (filename) -- cgit v1.2.1 From 6c5612c1cbdcc3fe4762d0ef80948a37a7cbdc3b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:43:45 +0000 Subject: Added DistutilsExecError, DistutilsValueError. --- errors.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/errors.py b/errors.py index f5ef3851..17d1abc7 100644 --- a/errors.py +++ b/errors.py @@ -52,12 +52,22 @@ if type (RuntimeError) is types.ClassType: class DistutilsOptionError (DistutilsError): pass + # DistutilsValueError is raised anytime an option value (presumably + # provided by setup.py) is invalid. + class DistutilsValueError (DistutilsError): + pass + # DistutilsPlatformError is raised when we find that we don't # know how to do something on the current platform (but we do # know how to do it on some platform). class DistutilsPlatformError (DistutilsError): pass + # DistutilsExecError is raised if there are any problems executing + # an external program + class DistutilsExecError (DistutilsError): + pass + # String-based exceptions else: DistutilsError = 'DistutilsError' @@ -67,6 +77,8 @@ else: DistutilsArgError = 'DistutilsArgError' DistutilsFileError = 'DistutilsFileError' DistutilsOptionError = 'DistutilsOptionError' + DistutilsValueError = 'DistutilsValueError' DistutilsPlatformError = 'DistutilsPlatformError' - + DistutilsExecError = 'DistutilsExecError' + del types -- cgit v1.2.1 From 08b303ebb482dc760d1f7b793ad61475885b98bc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:44:37 +0000 Subject: Better detection of bad entries in option table. Better error messages for bad entries in option table. --- fancy_getopt.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index c63ce61b..125dceb3 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -47,16 +47,24 @@ def fancy_getopt (options, object, args): attr_name = {} takes_arg = {} - for (long, short, help) in options: + for option in options: + try: + (long, short, help) = option + except ValueError: + raise DistutilsGetoptError, \ + "invalid option tuple " + str (option) + # Type-check the option names if type (long) is not StringType or len (long) < 2: raise DistutilsGetoptError, \ - "long option must be a string of length >= 2" + "long option '%s' must be a string of length >= 2" % \ + long if (not ((short is None) or (type (short) is StringType and len (short) == 1))): raise DistutilsGetoptError, \ - "short option must be None or string of length 1" + "short option '%s' must be None or string of length 1" % \ + short long_opts.append (long) -- cgit v1.2.1 From d1423866abe4c8a4416b514ca8c9f06f9283dcd7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:47:21 +0000 Subject: Comment tweak. --- core.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core.py b/core.py index 8a6cf720..a9c57325 100644 --- a/core.py +++ b/core.py @@ -166,11 +166,11 @@ class Distribution: # cheap to "run" a command whenever we think we might need to -- if # it's already been done, no need for expensive filesystem # operations, we just check the 'have_run' dictionary and carry on. - # It's only safe to query 'have_run' for a command class - # that has been instantiated -- a false value will be put inserted - # when the command object is created, and replaced with a true - # value when the command is succesfully run. Thus it's - # probably best to use '.get()' rather than a straight lookup. + # It's only safe to query 'have_run' for a command class that has + # been instantiated -- a false value will be inserted when the + # command object is created, and replaced with a true value when + # the command is succesfully run. Thus it's probably best to use + # '.get()' rather than a straight lookup. self.have_run = {} # __init__ () -- cgit v1.2.1 From 5cf06667069e8044604eaa23c4d78bd61a31de74 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:50:50 +0000 Subject: Added 'verbose' and 'dry_run' flags to CCompiler constructor and 'new_compiler()' factory function. Added 'runtime_library_dirs' list (for -R linker option) and methods to manipulate it. Deleted some obsolete comments. Added all the filename manglign methods: 'object_filenames()', 'shared_object_filename()', 'library_filename()', 'shared_library_filename()'. Added 'spawn()' method (front end to the "real" spawn). --- ccompiler.py | 79 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 7be0ba22..d5519cab 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -11,6 +11,7 @@ import os from types import * from copy import copy from distutils.errors import * +from distutils.spawn import spawn class CCompiler: @@ -41,23 +42,25 @@ class CCompiler: # parameter to the compile/link_* methods, or both? # * can't completely override the include or library searchg # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". - # I'm not sure how widely supported this is even by POSIX + # I'm not sure how widely supported this is even by Unix # compilers, much less on other platforms. And I'm even less - # sure how useful it is; probably for cross-compiling, but I - # have no intention of supporting that. + # sure how useful it is; maybe for cross-compiling, but + # support for that is a ways off. (And anyways, cross + # compilers probably have a dedicated binary with the + # right paths compiled in. I hope.) # * can't do really freaky things with the library list/library # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against # different versions of libfoo.a in different locations. I # think this is useless without the ability to null out the # library search path anyways. - # * don't deal with verbose and dry-run flags -- probably a - # CCompiler object should just drag them around the way the - # Distribution object does (either that or we have to drag - # around a Distribution object, which is what Command objects - # do... but might be kind of annoying) - def __init__ (self): + def __init__ (self, + verbose=0, + dry_run=0): + + self.verbose = verbose + self.dry_run = dry_run # 'macros': a list of macro definitions (or undefinitions). A # macro definition is a 2-tuple (name, value), where the value is @@ -65,7 +68,6 @@ class CCompiler: # undefinition is a 1-tuple (name,). self.macros = [] - # 'include_dirs': a list of directories to search for include files self.include_dirs = [] @@ -76,6 +78,10 @@ class CCompiler: # 'library_dirs': a list of directories to search for libraries self.library_dirs = [] + # 'runtime_library_dirs': a list of directories to search for + # shared libraries/objects at runtime + self.runtime_library_dirs = [] + # 'objects': a list of object files (or similar, such as explicitly # named library files) to include on any link self.objects = [] @@ -205,6 +211,19 @@ class CCompiler: self.library_dirs = copy (dirs) + def add_runtime_library_dir (self, dir): + """Add 'dir' to the list of directories that will be searched for + shared libraries at runtime.""" + self.runtime_library_dirs.append (dir) + + def set_runtime_library_dirs (self, dirs): + """Set the list of directories to search for shared libraries + at runtime to 'dirs' (a list of strings). This does not affect + any standard search path that the runtime linker may search by + default.""" + self.runtime_library_dirs = copy (dirs) + + def add_link_object (self, object): """Add 'object' to the list of object files (or analogues, such as explictly named library files or the output of "resource @@ -271,9 +290,6 @@ class CCompiler: pass - # XXX what's better/more consistent/more universally understood - # terminology: "shared library" or "dynamic library"? - def link_shared_lib (self, objects, output_libname, @@ -296,10 +312,43 @@ class CCompiler: filename is explicitly supplied as 'output_filename'.""" pass + + # -- Filename mangling methods ------------------------------------- + + def object_filenames (source_filenames): + """Return the list of object filenames corresponding to each + specified source filename.""" + pass + + def shared_object_filename (source_filename): + """Return the shared object filename corresponding to a + specified source filename.""" + pass + + def library_filename (libname): + """Return the static library filename corresponding to the + specified library name.""" + + pass + + def shared_library_filename (libname): + """Return the shared library filename corresponding to the + specified library name.""" + pass + + + # -- Utility methods ----------------------------------------------- + + def spawn (self, cmd): + spawn (cmd, verbose=self.verbose, dry_run=self.dry_run) + + # class CCompiler -def new_compiler (plat=None): +def new_compiler (plat=None, + verbose=0, + dry_run=0): """Generate a CCompiler instance for platform 'plat' (or the current platform, if 'plat' not supplied). Really instantiates some concrete subclass of CCompiler, of course.""" @@ -307,7 +356,7 @@ def new_compiler (plat=None): if plat is None: plat = os.name if plat == 'posix': from unixccompiler import UnixCCompiler - return UnixCCompiler () + return UnixCCompiler (verbose, dry_run) else: raise DistutilsPlatformError, \ "don't know how to compile C/C++ code on platform %s" % plat -- cgit v1.2.1 From 570f701e1116a7f768361b28323f16def9c3da31 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:53:53 +0000 Subject: Changed to use 'spawn()', now that it exists. Added 'verbose' and 'dry_run' parameters to constructor. Changed 'compile()', 'link_*()' to default lists arguments to None rather than empty list. Added implementations of the filename-mangling methods mandated by the CCompiler interface. --- unixccompiler.py | 86 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index fb5ed667..c8468f96 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,7 +17,7 @@ the "typical" Unix-style command-line C compiler: __rcsid__ = "$Id$" -import string +import string, re from types import * from sysconfig import \ CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO @@ -42,25 +42,35 @@ from ccompiler import CCompiler class UnixCCompiler (CCompiler): - # XXX any -I and -D options that we get from Makefile (via sysconfig) - # are preserved, but not treated specially: that is, they are not put - # in the self.include_dirs and self.macros, etc. lists that we inherit - # from CCompiler. I'm not sure if this is right, wrong or indifferent, - # but it should probably be a documented part of the CCompiler API: - # ie. there are *three* kinds of include directories, those from the - # compiler, those from Python's Makefiles, and those supplied to - # {add,set}_include_dirs() -- and 'set_include_dirs()' only overrides - # the last kind! I suspect the same applies to libraries and library - # directories -- anything else? - - def __init__ (self): - - CCompiler.__init__ (self) + # XXX perhaps there should really be *three* kinds of include + # directories: those built in to the preprocessor, those from Python's + # Makefiles, and those supplied to {add,set}_include_dirs(). Currently + # we make no distinction between the latter two at this point; it's all + # up to the client class to select the include directories to use above + # and beyond the compiler's defaults. That is, both the Python include + # directories and any module- or package-specific include directories + # are specified via {add,set}_include_dirs(), and there's no way to + # distinguish them. This might be a bug. + + def __init__ (self, + verbose=0, + dry_run=0): + + CCompiler.__init__ (self, verbose, dry_run) self.preprocess_options = None self.compile_options = None - # munge CC and OPT together in case there are flags stuck in CC + # Munge CC and OPT together in case there are flags stuck in CC. + # Note that using these variables from sysconfig immediately makes + # this module specific to building Python extensions and + # inappropriate as a general-purpose C compiler front-end. So sue + # me. Note also that we use OPT rather than CFLAGS, because CFLAGS + # is the flags used to compile Python itself -- not only are there + # -I options in there, they are the *wrong* -I options. We'll + # leave selection of include directories up to the class using + # UnixCCompiler! + (self.cc, self.ccflags) = \ _split_command (CC + ' ' + OPT) self.ccflags_shared = string.split (CCSHARED) @@ -71,8 +81,13 @@ class UnixCCompiler (CCompiler): def compile (self, sources, - macros=[], - includes=[]): + macros=None, + includes=None): + + if macros is None: + macros = [] + if includes is None: + includes = [] if type (macros) is not ListType: raise TypeError, \ @@ -92,7 +107,8 @@ class UnixCCompiler (CCompiler): sources # this will change to 'spawn' when I have it! - print string.join ([self.cc] + cc_args, ' ') + #print string.join ([self.cc] + cc_args, ' ') + self.spawn ([self.cc] + cc_args) # XXX punting on 'link_static_lib()' for now -- it might be better for @@ -114,17 +130,39 @@ class UnixCCompiler (CCompiler): def link_shared_object (self, objects, output_filename, - libraries=[], - library_dirs=[]): + libraries=None, + library_dirs=None): + + if libraries is None: + libraries = [] + if library_dirs is None: + library_dirs = [] lib_opts = _gen_lib_options (self.libraries + libraries, self.library_dirs + library_dirs) ld_args = self.ldflags_shared + lib_opts + \ objects + ['-o', output_filename] - print string.join ([self.ld_shared] + ld_args, ' ') - - + #print string.join ([self.ld_shared] + ld_args, ' ') + self.spawn ([self.ld_shared] + ld_args) + + + def object_filenames (self, source_filenames): + outnames = [] + for inname in source_filenames: + outnames.append (re.sub (r'\.(c|C|cc|cxx)$', '.o', inname)) + return outnames + + def shared_object_filename (self, source_filename): + return re.sub (r'\.(c|C|cc|cxx)$', SO) + + def library_filename (self, libname): + return "lib%s.a" % libname + + def shared_library_filename (self, libname): + return "lib%s.so" % libname + + # class UnixCCompiler -- cgit v1.2.1 From e78649a4e18096a1722b66030a160e4f8b90431f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:57:17 +0000 Subject: Module to spawn sub-commands in a platform-independent way. Initial revision only includes support for POSIX-style fork-and-exec. --- spawn.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 spawn.py diff --git a/spawn.py b/spawn.py new file mode 100644 index 00000000..24e76ef1 --- /dev/null +++ b/spawn.py @@ -0,0 +1,106 @@ +"""distutils.spawn + +Provides the 'spawn()' function, a front-end to various platform- +specific functions for launching another program in a sub-process.""" + +# created 1999/07/24, Greg Ward + +__rcsid__ = "$Id$" + +import sys, os, string +from distutils.errors import * + + +def spawn (cmd, + search_path=1, + verbose=0, + dry_run=0): + + """Run another program, specified as a command list 'cmd', in a new + process. 'cmd' is just the argument list for the new process, ie. + cmd[0] is the program to run and cmd[1:] are the rest of its + arguments. There is no way to run a program with a name different + from that of its executable. + + If 'search_path' is true (the default), the system's executable + search path will be used to find the program; otherwise, cmd[0] must + be the exact path to the executable. If 'verbose' is true, a + one-line summary of the command will be printed before it is run. + If 'dry_run' is true, the command will not actually be run. + + Raise DistutilsExecError if running the program fails in any way; + just return on success.""" + + if os.name == 'posix': + _spawn_posix (cmd, search_path, verbose, dry_run) + elif os.name == 'windows': # ??? + # XXX should 'args' be cmd[1:] or cmd? + # XXX how do we detect failure? + # XXX how to do this in pre-1.5.2? + # XXX is P_WAIT the correct mode? + # XXX how to make Windows search the path? + if verbose: + print string.join (cmd, ' ') + if not dry_run: + os.spawnv (os.P_WAIT, cmd[0], cmd[1:]) + else: + raise DistutilsPlatformError, \ + "don't know how to spawn programs on platform '%s'" % os.name + +# spawn () + + +def _spawn_posix (cmd, + search_path=1, + verbose=0, + dry_run=0): + + if verbose: + print string.join (cmd, ' ') + if dry_run: + return + exec_fn = search_path and os.execvp or os.execv + + pid = os.fork () + + if pid == 0: # in the child + try: + #print "cmd[0] =", cmd[0] + #print "cmd =", cmd + exec_fn (cmd[0], cmd) + except OSError, e: + sys.stderr.write ("unable to execute %s: %s\n" % + (cmd[0], e.strerror)) + os._exit (1) + + sys.stderr.write ("unable to execute %s for unknown reasons" % cmd[0]) + os._exit (1) + + + else: # in the parent + # Loop until the child either exits or is terminated by a signal + # (ie. keep waiting if it's merely stopped) + while 1: + (pid, status) = os.waitpid (pid, 0) + if os.WIFSIGNALED (status): + raise DistutilsExecError, \ + "command %s terminated by signal %d" % \ + (cmd[0], os.WTERMSIG (status)) + + elif os.WIFEXITED (status): + exit_status = os.WEXITSTATUS (status) + if exit_status == 0: + return # hey, it succeeded! + else: + raise DistutilsExecError, \ + "command %s failed with exit status %d" % \ + (cmd[0], exit_status) + + elif os.WIFSTOPPED (status): + continue + + else: + raise DistutilsExecError, \ + "unknown error executing %s: termination status %d" % \ + (cmd[0], status) +# _spawn_posix () -- cgit v1.2.1 From d8eaecdab17b05bfe965bac07a69b59edba79799 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:57:49 +0000 Subject: Implements the 'build_ext' command for building C/C++ extension modules. --- command/build_ext.py | 192 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 command/build_ext.py diff --git a/command/build_ext.py b/command/build_ext.py new file mode 100644 index 00000000..cb9da68d --- /dev/null +++ b/command/build_ext.py @@ -0,0 +1,192 @@ +"""distutils.command.build_ext + +Implements the Distutils 'build_ext' command, for building extension +modules (currently limited to C extensions, should accomodate C++ +extensions ASAP).""" + +# created 1999/08/09, Greg Ward + +__rcsid__ = "$Id$" + +import sys, os, string, re +from types import * +from distutils.core import Command +from distutils.ccompiler import new_compiler +from distutils.sysconfig import INCLUDEPY, SO, exec_prefix +from distutils.errors import * + + +# This is the same as a Python NAME, since we must accept any +# valid module name for the extension name. +extension_name_re = re.compile (r'^[a-zA-Z_][a-zA-Z_0-9]*$') + + +class BuildExt (Command): + + # XXX thoughts on how to deal with complex command-line options like + # these, i.e. how to make it so fancy_getopt can suck them off the + # command line and make it look like setup.py defined the appropriate + # lists of tuples of what-have-you. + # - each command needs a callback to process its command-line options + # - Command.__init__() needs access to its share of the whole + # command line (must ultimately come from + # Distribution.parse_command_line()) + # - it then calls the current command class' option-parsing + # callback to deal with weird options like -D, which have to + # parse the option text and churn out some custom data + # structure + # - that data structure (in this case, a list of 2-tuples) + # will then be present in the command object by the time + # we get to set_final_options() (i.e. the constructor + # takes care of both command-line and client options + # in between set_default_options() and set_final_options()) + + options = [('dir=', 'd', + "directory for compiled extension modules"), + ('include-dirs=', 'I', + "list of directories to search for header files"), + ('define=', 'D', + "C preprocessor macros to define"), + ('undef=', 'U', + "C preprocessor macros to undefine"), + ('libs=', 'l', + "external C libraries to link with"), + ('library-dirs=', 'L', + "directories to search for external C libraries"), + ('rpath=', 'R', + "directories to search for shared C libraries at runtime"), + ('link-objects=', 'O', + "extra explicit link objects to include in the link"), + ] + + + def set_default_options (self): + self.dir = None + self.include_dirs = None + self.define = None + self.undef = None + self.libs = None + self.library_dirs = None + self.rpath = None + self.link_objects = None + + def set_final_options (self): + self.set_undefined_options ('build', ('platdir', 'dir')) + + # Make sure Python's include directories (for Python.h, config.h, + # etc.) are in the include search path. We have to roll our own + # "exec include dir", because the Makefile parsed by sysconfig + # doesn't have it (sigh). + py_include = INCLUDEPY # prefix + "include" + "python" + ver + exec_py_include = os.path.join (exec_prefix, 'include', + 'python' + sys.version[0:3]) + if self.include_dirs is None: + self.include_dirs = [] + self.include_dirs.insert (0, py_include) + if exec_py_include != py_include: + self.include_dirs.insert (0, exec_py_include) + + + def run (self): + + self.set_final_options () + (extensions, package) = \ + self.distribution.get_options ('ext_modules', 'package') + + # 'extensions', as supplied by setup.py, is a list of 2-tuples. + # Each tuple is simple: + # (ext_name, build_info) + # build_info is a dictionary containing everything specific to + # building this extension. (Info pertaining to all extensions + # should be handled by general distutils options passed from + # setup.py down to right here, but that's not taken care of yet.) + + + # First, sanity-check the 'extensions' list + self.check_extensions_list (extensions) + + # Setup the CCompiler object that we'll use to do all the + # compiling and linking + self.compiler = new_compiler (verbose=self.distribution.verbose, + dry_run=self.distribution.dry_run) + if self.include_dirs is not None: + self.compiler.set_include_dirs (self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name,value) in self.define: + self.compiler.define_macro (name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro (macro) + if self.libs is not None: + self.compiler.set_libraries (self.libs) + if self.library_dirs is not None: + self.compiler.set_library_dirs (self.library_dirs) + if self.rpath is not None: + self.compiler.set_runtime_library_dirs (self.rpath) + if self.link_objects is not None: + self.compiler.set_link_objects (self.link_objects) + + # Now the real loop over extensions + self.build_extensions (extensions) + + + + def check_extensions_list (self, extensions): + + if type (extensions) is not ListType: + raise DistutilsValueError, \ + "'ext_modules' option must be a list of tuples" + + for ext in extensions: + if type (ext) is not TupleType and len (ext) != 2: + raise DistutilsValueError, \ + "each element of 'ext_modules' option must be a 2-tuple" + + if not (type (ext[0]) is StringType and + extension_name_re.match (ext[0])): + raise DistutilsValueError, \ + "first element of each tuple in 'ext_modules' " + \ + "must be the extension name (a string)" + + if type (ext[1]) is not DictionaryType: + raise DistutilsValueError, \ + "second element of each tuple in 'ext_modules' " + \ + "must be a dictionary" + + # end sanity-check for + + # check_extensions_list () + + + def build_extensions (self, extensions): + + for (extension_name, build_info) in extensions: + sources = build_info.get ('sources') + if sources is None or type (sources) is not ListType: + raise DistutilsValueError, \ + "in ext_modules option, 'sources' must be present " + \ + "and must be a list of source filenames" + + macros = build_info.get ('macros') + include_dirs = build_info.get ('include_dirs') + self.compiler.compile (sources, macros, include_dirs) + + objects = self.compiler.object_filenames (sources) + extra_objects = build_info.get ('extra_objects') + if extra_objects: + objects.extend (extra_objects) + libraries = build_info.get ('libraries') + library_dirs = build_info.get ('library_dirs') + + ext_filename = self.extension_filename (extension_name) + self.compiler.link_shared_object (objects, ext_filename, + libraries, library_dirs) + + # build_extensions () + + + def extension_filename (self, ext_name): + return ext_name + SO + +# class BuildExt -- cgit v1.2.1 From e168908a1284d0f1bccbdaa6b34ef9ee56be675f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 19 Aug 1999 20:02:10 +0000 Subject: Oops, call 'os.path.join()'! --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 01ea0052..cf45004f 100644 --- a/command/install.py +++ b/command/install.py @@ -226,7 +226,7 @@ class Install (Command): # Otherwise, just tack the "fallback postfix" onto the # user-specified prefix. - return apply (os.join, (my_prefix,) + fallback_postfix) + return apply (os.path.join, (my_prefix,) + fallback_postfix) # replace_sys_prefix () -- cgit v1.2.1 From 6ad6cbda7915f2134e949639fa5deec460b2338f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:15:07 +0000 Subject: Added msvccompiler module exactly as supplied by Perry Stoll. --- msvccompiler.py | 317 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 msvccompiler.py diff --git a/msvccompiler.py b/msvccompiler.py new file mode 100644 index 00000000..ff66f913 --- /dev/null +++ b/msvccompiler.py @@ -0,0 +1,317 @@ +"""distutils.ccompiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for the Microsoft Visual Studio """ + + +# created 1999/08/19, Perry Stoll +# +__rcsid__ = "$Id$" + +import os +import sys +from distutils.errors import * +from distutils.ccompiler import CCompiler + + +class MSVCCompiler ( CCompiler) : + """Abstract base class to define the interface that must be implemented + by real compiler abstraction classes. Might have some use as a + place for shared code, but it's not yet clear what code can be + shared between compiler abstraction models for different platforms. + + The basic idea behind a compiler abstraction class is that each + instance can be used for all the compile/link steps in building + a single project. Thus, attributes common to all of those compile + and link steps -- include directories, macros to define, libraries + to link against, etc. -- are attributes of the compiler instance. + To allow for variability in how individual files are treated, + most (all?) of those attributes may be varied on a per-compilation + or per-link basis.""" + + def __init__ (self, + verbose=0, + dry_run=0): + + CCompiler.__init__ (self, verbose, dry_run) + + + # XXX This is a nasty dependency to add on something otherwise + # pretty clean. move it to build_ext under an nt + # specific part. + # shared libraries need to link against python15.lib + self.add_library ( "python" + sys.version[0] + sys.version[2] ) + self.add_library_dir( os.path.join( sys.exec_prefix, 'libs' ) ) + + self.cc = "cl.exe" + self.link = "link.exe" + self.preprocess_options = None + self.compile_options = [ '/nologo' ] + + self.ldflags_shared = ['/DLL', '/nologo'] + self.ldflags_static = [ '/nologo'] + + # XXX things not handled by this compiler abstraction model: + # * client can't provide additional options for a compiler, + # e.g. warning, optimization, debugging flags. Perhaps this + # should be the domain of concrete compiler abstraction classes + # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base + # class should have methods for the common ones. + # * can't put output files (object files, libraries, whatever) + # into a separate directory from their inputs. Should this be + # handled by an 'output_dir' attribute of the whole object, or a + # parameter to the compile/link_* methods, or both? + # * can't completely override the include or library searchg + # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". + # I'm not sure how widely supported this is even by Unix + # compilers, much less on other platforms. And I'm even less + # sure how useful it is; maybe for cross-compiling, but + # support for that is a ways off. (And anyways, cross + # compilers probably have a dedicated binary with the + # right paths compiled in. I hope.) + # * can't do really freaky things with the library list/library + # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against + # different versions of libfoo.a in different locations. I + # think this is useless without the ability to null out the + # library search path anyways. + + + # -- Worker methods ------------------------------------------------ + # (must be implemented by subclasses) + + _c_extensions = [ '.c' ] + _cpp_extensions = [ '.cc', 'cpp' ] + + _obj_ext = '.obj' + _exe_ext = 'exe' + _shared_lib_ext = '.dll' + _static_lib_ext = '.lib' + + def compile (self, + sources, + macros=None, + includes=None): + """Compile one or more C/C++ source files. 'sources' must be + a list of strings, each one the name of a C/C++ source + file. Return a list of the object filenames generated + (one for each source filename in 'sources'). + + 'macros', if given, must be a list of macro definitions. A + macro definition is either a (name, value) 2-tuple or a (name,) + 1-tuple. The former defines a macro; if the value is None, the + macro is defined without an explicit value. The 1-tuple case + undefines a macro. Later definitions/redefinitions/ + undefinitions take precedence. + + 'includes', if given, must be a list of strings, the directories + to add to the default include file search path for this + compilation only.""" + + if macros is None: + macros = [] + if includes is None: + includes = [] + + objectFiles = [] + + base_pp_opts = _gen_preprocess_options (self.macros + macros, + self.include_dirs + includes) + + base_pp_opts.append('/c') + + for srcFile in sources: + base,ext = os.path.splitext(srcFile) + objFile = base + ".obj" + + if ext in self._c_extensions: + fileOpt = "/Tc" + elif ext in self._cpp_extensions: + fileOpt = "/Tp" + + inputOpt = fileOpt + srcFile + outputOpt = "/Fo" + objFile + + pp_opts = base_pp_opts + [ outputOpt, inputOpt ] + + returnCode = self.spawn( [ self.cc ] + self.compile_options + pp_opts ) + # XXX check for valid return code + + objectFiles.append( objFile ) + + + return objectFiles + + # XXX this is kind of useless without 'link_binary()' or + # 'link_executable()' or something -- or maybe 'link_static_lib()' + # should not exist at all, and we just have 'link_binary()'? + def link_static_lib (self, + objects, + output_libname, + libraries=None, + library_dirs=None): + """Link a bunch of stuff together to create a static library + file. The "bunch of stuff" consists of the list of object + files supplied as 'objects', the extra object files supplied + to 'add_link_object()' and/or 'set_link_objects()', the + libraries supplied to 'add_library()' and/or + 'set_libraries()', and the libraries supplied as 'libraries' + (if any). + + 'output_libname' should be a library name, not a filename; + the filename will be inferred from the library name. + + 'library_dirs', if supplied, should be a list of additional + directories to search on top of the system default and those + supplied to 'add_library_dir()' and/or 'set_library_dirs()'.""" + + if libraries is None: + libraries = [] + if library_dirs is None: + library_dirs = [] + if build_info is None: + build_info = {} + + lib_opts = _gen_lib_options (self.libraries + libraries, + self.library_dirs + library_dirs) + + if build_info.has_key('def_file') : + lib_opts.append('/DEF:' + build_info['def_file'] ) + + ld_args = self.ldflags_static + lib_opts + \ + objects + ['/OUT:' + output_filename] + + self.spawn ( [ self.link ] + ld_args ) + + + def link_shared_lib (self, + objects, + output_libname, + libraries=None, + library_dirs=None, + build_info=None): + """Link a bunch of stuff together to create a shared library + file. Has the same effect as 'link_static_lib()' except + that the filename inferred from 'output_libname' will most + likely be different, and the type of file generated will + almost certainly be different.""" + # XXX should we sanity check the library name? (eg. no + # slashes) + self.link_shared_object (objects, self.shared_library_name(output_libname), + build_info=build_info ) + + def link_shared_object (self, + objects, + output_filename, + libraries=None, + library_dirs=None, + build_info=None): + """Link a bunch of stuff together to create a shared object + file. Much like 'link_shared_lib()', except the output + filename is explicitly supplied as 'output_filename'.""" + if libraries is None: + libraries = [] + if library_dirs is None: + library_dirs = [] + if build_info is None: + build_info = {} + + lib_opts = _gen_lib_options (self.libraries + libraries, + self.library_dirs + library_dirs) + + if build_info.has_key('def_file') : + lib_opts.append('/DEF:' + build_info['def_file'] ) + + ld_args = self.ldflags_shared + lib_opts + \ + objects + ['/OUT:' + output_filename] + + self.spawn ( [ self.link ] + ld_args ) + + + # -- Filename mangling methods ------------------------------------- + + def _change_extensions( self, filenames, newExtension ): + object_filenames = [] + + for srcFile in filenames: + base,ext = os.path.splitext( srcFile ) + # XXX should we strip off any existing path? + object_filenames.append( base + newExtension ) + + return object_filenames + + def object_filenames (self, source_filenames): + """Return the list of object filenames corresponding to each + specified source filename.""" + return self._change_extensions( source_filenames, self._obj_ext ) + + def shared_object_filename (self, source_filename): + """Return the shared object filename corresponding to a + specified source filename.""" + return self._change_extensions( source_filenames, self._shared_lib_ext ) + + def library_filename (self, libname): + """Return the static library filename corresponding to the + specified library name.""" + return "lib%s%s" %( libname, self._static_lib_ext ) + + def shared_library_filename (self, libname): + """Return the shared library filename corresponding to the + specified library name.""" + return "lib%s%s" %( libname, self._shared_lib_ext ) + +# class MSVCCompiler + +def _gen_preprocess_options (macros, includes): + + # XXX it would be nice (mainly aesthetic, and so we don't generate + # stupid-looking command lines) to go over 'macros' and eliminate + # redundant definitions/undefinitions (ie. ensure that only the + # latest mention of a particular macro winds up on the command + # line). I don't think it's essential, though, since most (all?) + # Unix C compilers only pay attention to the latest -D or -U + # mention of a macro on their command line. Similar situation for + # 'includes'. I'm punting on both for now. Anyways, weeding out + # redundancies like this should probably be the province of + # CCompiler, since the data structures used are inherited from it + # and therefore common to all CCompiler classes. + + + pp_opts = [] + for macro in macros: + if len (macro) == 1: # undefine this macro + pp_opts.append ("-U%s" % macro[0]) + elif len (macro) == 2: + if macro[1] is None: # define with no explicit value + pp_opts.append ("-D%s" % macro[0]) + else: + # XXX *don't* need to be clever about quoting the + # macro value here, because we're going to avoid the + # shell at all costs when we spawn the command! + pp_opts.append ("-D%s=%s" % macro) + + for dir in includes: + pp_opts.append ("-I%s" % dir) + + return pp_opts + +def _gen_lib_options (libraries, library_dirs): + + lib_opts = [] + + for dir in library_dirs: + lib_opts.append ("/LIBPATH:%s" % dir) + + # XXX it's important that we *not* remove redundant library mentions! + # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to + # resolve all symbols. I just hope we never have to say "-lfoo obj.o + # -lbar" to get things to work -- that's certainly a possibility, but a + # pretty nasty way to arrange your C code. + + for lib in libraries: + lib_opts.append ("%s.lib" % lib) # import libraries end in .lib + + return lib_opts + +# _gen_lib_options () + + -- cgit v1.2.1 From 39a6cfad1bbad1865216c88bf6b9a24c0a0a98f7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:17:36 +0000 Subject: Patch from Perry Stoll: - fix some broken abstract methods - kludge: add 'build_info' parameter to link methods - add 'object_name()' and 'shared_library_name()' - support for MSVCCompiler class on NT/Win95 --- ccompiler.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index d5519cab..5f27ebc9 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -294,7 +294,8 @@ class CCompiler: objects, output_libname, libraries=None, - library_dirs=None): + library_dirs=None, + build_info=None): """Link a bunch of stuff together to create a shared library file. Has the same effect as 'link_static_lib()' except that the filename inferred from 'output_libname' will most @@ -306,7 +307,8 @@ class CCompiler: objects, output_filename, libraries=None, - library_dirs=None): + library_dirs=None, + build_info=None): """Link a bunch of stuff together to create a shared object file. Much like 'link_shared_lib()', except the output filename is explicitly supplied as 'output_filename'.""" @@ -315,27 +317,35 @@ class CCompiler: # -- Filename mangling methods ------------------------------------- - def object_filenames (source_filenames): + def object_filenames (self, source_filenames): """Return the list of object filenames corresponding to each specified source filename.""" pass - def shared_object_filename (source_filename): + def shared_object_filename (self, source_filename): """Return the shared object filename corresponding to a specified source filename.""" pass - def library_filename (libname): + def library_filename (self, libname): """Return the static library filename corresponding to the specified library name.""" pass - def shared_library_filename (libname): + def shared_library_filename (self, libname): """Return the shared library filename corresponding to the specified library name.""" pass + + def object_name (self, inname): + """Given a name with no extension, return the name + object extension""" + return inname + self._obj_ext + + def shared_library_name (self, inname): + """Given a name with no extension, return the name + shared object extension""" + return inname + self._shared_lib_ext # -- Utility methods ----------------------------------------------- @@ -357,6 +367,9 @@ def new_compiler (plat=None, if plat == 'posix': from unixccompiler import UnixCCompiler return UnixCCompiler (verbose, dry_run) + elif plat in ['nt', 'win95' ]: + from msvccompiler import MSVCCompiler + return MSVCCompiler ( verbose, dry_run ) else: raise DistutilsPlatformError, \ "don't know how to compile C/C++ code on platform %s" % plat -- cgit v1.2.1 From 0ae40f52f7ec42a97ac5ba7f5a19dc7e61b06427 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:18:26 +0000 Subject: Patch from Perry Stoll: pass 'build_info' to link method. --- command/build_ext.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index cb9da68d..a0464b4e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -178,10 +178,9 @@ class BuildExt (Command): objects.extend (extra_objects) libraries = build_info.get ('libraries') library_dirs = build_info.get ('library_dirs') - ext_filename = self.extension_filename (extension_name) self.compiler.link_shared_object (objects, ext_filename, - libraries, library_dirs) + libraries, library_dirs, build_info) # build_extensions () -- cgit v1.2.1 From 0ddb1bcf7e8dd498d71f29941f3b148dc81f55e4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:19:01 +0000 Subject: Patch from Perry Stoll: OK for list of modules to be empty. --- command/build_py.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/command/build_py.py b/command/build_py.py index d956eef3..d75bf3f6 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -55,6 +55,10 @@ class BuildPy (Command): # input and output filenames and checking for missing # input files. + # it's ok not to have *any* py files, right? + if not modules: + return + # XXX we should allow for wildcards, so eg. the Distutils setup.py # file would just have to say # py_modules = ['distutils.*', 'distutils.command.*'] -- cgit v1.2.1 From 0911797ba33945835d936fde6db2cd1c57fda9f2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:19:37 +0000 Subject: Patch from Perry Stoll: typo fix, make sure we only compile .py files. --- command/install_lib.py | 15 +++++++++------ command/install_py.py | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index f147af47..876a34ce 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -9,7 +9,7 @@ from distutils.util import copy_tree class InstallPy (Command): options = [('dir=', 'd', "directory to install to"), - ('build-dir=' 'b', "build directory (where to install from)"), + ('build-dir=','b', "build directory (where to install from)"), ('compile', 'c', "compile .py to .pyc"), ('optimize', 'o', "compile .py to .pyo (optimized)"), ] @@ -54,12 +54,15 @@ class InstallPy (Command): for f in outfiles: # XXX can't assume this filename mapping! - out_fn = string.replace (f, '.py', '.pyc') - - self.make_file (f, out_fn, compile, (f,), - "compiling %s -> %s" % (f, out_fn), - "compilation of %s skipped" % f) + # only compile the file if it is actually a .py file + if f[-3:] == '.py': + out_fn = string.replace (f, '.py', '.pyc') + + self.make_file (f, out_fn, compile, (f,), + "compiling %s -> %s" % (f, out_fn), + "compilation of %s skipped" % f) + # XXX ignore self.optimize for now, since we don't really know if # we're compiling optimally or not, and couldn't pick what to do # even if we did know. ;-( diff --git a/command/install_py.py b/command/install_py.py index f147af47..876a34ce 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -9,7 +9,7 @@ from distutils.util import copy_tree class InstallPy (Command): options = [('dir=', 'd', "directory to install to"), - ('build-dir=' 'b', "build directory (where to install from)"), + ('build-dir=','b', "build directory (where to install from)"), ('compile', 'c', "compile .py to .pyc"), ('optimize', 'o', "compile .py to .pyo (optimized)"), ] @@ -54,12 +54,15 @@ class InstallPy (Command): for f in outfiles: # XXX can't assume this filename mapping! - out_fn = string.replace (f, '.py', '.pyc') - - self.make_file (f, out_fn, compile, (f,), - "compiling %s -> %s" % (f, out_fn), - "compilation of %s skipped" % f) + # only compile the file if it is actually a .py file + if f[-3:] == '.py': + out_fn = string.replace (f, '.py', '.pyc') + + self.make_file (f, out_fn, compile, (f,), + "compiling %s -> %s" % (f, out_fn), + "compilation of %s skipped" % f) + # XXX ignore self.optimize for now, since we don't really know if # we're compiling optimally or not, and couldn't pick what to do # even if we did know. ;-( -- cgit v1.2.1 From cb5a002f13cc594941024531f7949013f47e598b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:20:32 +0000 Subject: Patch from Perry Stoll: import types module. --- core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core.py b/core.py index a9c57325..2d5d0661 100644 --- a/core.py +++ b/core.py @@ -12,6 +12,7 @@ __rcsid__ = "$Id$" import sys, os import string, re +from types import * from distutils.errors import * from distutils.fancy_getopt import fancy_getopt from distutils import util -- cgit v1.2.1 From 8d83e47358a6a67a91fb5d2d2fe180c717b0a2be Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:20:56 +0000 Subject: Patch from Perry Stoll: support for Windows. --- spawn.py | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/spawn.py b/spawn.py index 24e76ef1..3a0702dc 100644 --- a/spawn.py +++ b/spawn.py @@ -33,23 +33,42 @@ def spawn (cmd, if os.name == 'posix': _spawn_posix (cmd, search_path, verbose, dry_run) - elif os.name == 'windows': # ??? - # XXX should 'args' be cmd[1:] or cmd? - # XXX how do we detect failure? - # XXX how to do this in pre-1.5.2? - # XXX is P_WAIT the correct mode? - # XXX how to make Windows search the path? - if verbose: - print string.join (cmd, ' ') - if not dry_run: - os.spawnv (os.P_WAIT, cmd[0], cmd[1:]) + elif os.name in ( 'nt', 'windows' ): # ??? + _spawn_nt (cmd, search_path, verbose, dry_run) else: raise DistutilsPlatformError, \ "don't know how to spawn programs on platform '%s'" % os.name # spawn () +def _spawn_nt ( cmd, + search_path=1, + verbose=0, + dry_run=0): + import string + executable = cmd[0] + if search_path: + paths = string.split( os.environ['PATH'], os.pathsep) + base,ext = os.path.splitext(executable) + if (ext != '.exe'): + executable = executable + '.exe' + if not os.path.isfile(executable): + paths.reverse() # go over the paths and keep the last one + for p in paths: + f = os.path.join( p, executable ) + if os.path.isfile ( f ): + # the file exists, we have a shot at spawn working + executable = f + if verbose: + print string.join ( [executable] + cmd[1:], ' ') + if not dry_run: + # spawn for NT requires a full path to the .exe + rc = os.spawnv (os.P_WAIT, executable, cmd) + if rc != 0: + raise DistutilsExecError("command failed: %d" % rc) + + def _spawn_posix (cmd, search_path=1, verbose=0, -- cgit v1.2.1 From c152a6391d7f1e688764758c6a156219ab081ce5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:22:13 +0000 Subject: Patch from Perry Stoll: tweaks to Windows support. --- sysconfig.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 5c60ca4a..8eaf17dc 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -137,8 +137,13 @@ def _init_nt(): os.path.join(sys.exec_prefix, "include", "config.h")), g) # set basic install directories g['LIBDEST']=os.path.join(sys.exec_prefix, "Lib") - g['BINLIBDEST']=os.path.join(sys.exec_prefix, "Lib") + g['BINLIBDEST']= os.path.join(sys.exec_prefix, "Lib") + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = os.path.join (sys.prefix, 'include' ) + + g['SO'] = '.dll' + g['exec_prefix'] = sys.exec_prefix try: exec "_init_" + os.name -- cgit v1.2.1 From badcf0591e3b166bd7c6e52926e54390d07a96e0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:23:32 +0000 Subject: Patch from Perry Stoll: caught up with changes in CCompiler necessary (?) for MSVCCompiler. --- unixccompiler.py | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index c8468f96..6594043c 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -51,6 +51,11 @@ class UnixCCompiler (CCompiler): # directories and any module- or package-specific include directories # are specified via {add,set}_include_dirs(), and there's no way to # distinguish them. This might be a bug. + + _obj_ext = '.o' + _exe_ext = '' + _shared_lib_ext = SO + _static_lib_ext = '.a' def __init__ (self, verbose=0, @@ -121,23 +126,29 @@ class UnixCCompiler (CCompiler): objects, output_libname, libraries=None, - library_dirs=None): + library_dirs=None, + build_info=None): # XXX should we sanity check the library name? (eg. no # slashes) - self.link_shared_object (objects, "lib%s%s" % (output_libname, SO)) + self.link_shared_object (objects, "lib%s%s" % \ + (output_libname, self._shared_lib_ext), + build_info=build_info) def link_shared_object (self, objects, output_filename, libraries=None, - library_dirs=None): + library_dirs=None, + build_info=None): if libraries is None: libraries = [] if library_dirs is None: library_dirs = [] - + if build_info is None: + build_info = {} + lib_opts = _gen_lib_options (self.libraries + libraries, self.library_dirs + library_dirs) ld_args = self.ldflags_shared + lib_opts + \ @@ -150,17 +161,19 @@ class UnixCCompiler (CCompiler): def object_filenames (self, source_filenames): outnames = [] for inname in source_filenames: - outnames.append (re.sub (r'\.(c|C|cc|cxx)$', '.o', inname)) + outnames.append ( re.sub (r'\.(c|C|cc|cxx|cpp)$', + self._obj_ext, inname)) return outnames def shared_object_filename (self, source_filename): - return re.sub (r'\.(c|C|cc|cxx)$', SO) + return re.sub (r'\.(c|C|cc|cxx|cpp)$', self._shared_lib_ext) def library_filename (self, libname): - return "lib%s.a" % libname + return "lib%s%s" % (libname, self._static_lib_ext ) def shared_library_filename (self, libname): - return "lib%s.so" % libname + return "lib%s%s" % (libname, self._shared_lib_ext ) + # class UnixCCompiler -- cgit v1.2.1 From 8fb6d44119d8eadaa89e54db50e804726f6fa98f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:23:28 +0000 Subject: [from 1999/08/28] Apparently os.name is "nt" or "posix" or we don't care. Cosmetic tweaks. --- spawn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spawn.py b/spawn.py index 3a0702dc..eee8e7f4 100644 --- a/spawn.py +++ b/spawn.py @@ -33,7 +33,7 @@ def spawn (cmd, if os.name == 'posix': _spawn_posix (cmd, search_path, verbose, dry_run) - elif os.name in ( 'nt', 'windows' ): # ??? + elif os.name == 'nt': _spawn_nt (cmd, search_path, verbose, dry_run) else: raise DistutilsPlatformError, \ @@ -41,11 +41,11 @@ def spawn (cmd, # spawn () + def _spawn_nt ( cmd, search_path=1, verbose=0, dry_run=0): - import string executable = cmd[0] if search_path: paths = string.split( os.environ['PATH'], os.pathsep) -- cgit v1.2.1 From 053838f4381b3616afaa3cd161a81d61969559ab Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:29:08 +0000 Subject: os.name is "posix" or "nt" or we don't care. Added big comment about the kludginess of passing 'build_options' to the link methods and how to fix it. Added 'gen_preprocess_options()' and 'gen_lib_options()' convenience functions -- the two cases are very similar for Unix C Compilers and VC++, so I figured I might as well unify the implementations. --- ccompiler.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 5f27ebc9..b1167ac2 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -290,6 +290,30 @@ class CCompiler: pass + # XXX passing in 'build_info' here is a kludge to deal with the + # oddities of one particular compiler (Visual C++). For some reason, + # it needs to be told about ".def" files, and currently the + # 'build_info' hash allows this through a 'def_file' element. The link + # methods for VC++ look for 'def_file' and transform it into the + # appropriate command-line options. The current code is objectionable + # for a number of reasons: 1) if the link methods take 'build_info', + # why bother passing in libraries, library_dirs, etc.? 2) if the link + # methods do it, why not the compile methods? 3) build_info is part of + # the interface between setup.py and the 'build_ext' command -- it + # should stop there and not be propagated down into the compiler + # classes! and 4) I don't like elevating a platform- and + # compiler-specific oddity to "first-class" status in 'build_info' (oh + # well, at least it's not being reified in the compiler classes -- that + # would be really gross). + # + # Possible solutions: + # - just pass build_info to all the compile/link methods, + # never mind all those other parameters and screw the + # integrity of the interfaces + # - add a mechanism for passing platform-specific and/or + # compiler-specific compiler/linker options from setup.py + # straight through to the appropriate compiler class + def link_shared_lib (self, objects, output_libname, @@ -367,9 +391,81 @@ def new_compiler (plat=None, if plat == 'posix': from unixccompiler import UnixCCompiler return UnixCCompiler (verbose, dry_run) - elif plat in ['nt', 'win95' ]: + elif plat == 'nt': from msvccompiler import MSVCCompiler return MSVCCompiler ( verbose, dry_run ) else: raise DistutilsPlatformError, \ "don't know how to compile C/C++ code on platform %s" % plat + + +def gen_preprocess_options (macros, includes): + """Generate C pre-processor options (-D, -U, -I) as used by at + least two types of compilers: the typical Unix compiler and Visual + C++. 'macros' is the usual thing, a list of 1- or 2-tuples, where + (name,) means undefine (-U) macro 'name', and (name,value) means + define (-D) macro 'name' to 'value'. 'includes' is just a list of + directory names to be added to the header file search path (-I). + Returns a list of command-line options suitable for either + Unix compilers or Visual C++.""" + + # XXX it would be nice (mainly aesthetic, and so we don't generate + # stupid-looking command lines) to go over 'macros' and eliminate + # redundant definitions/undefinitions (ie. ensure that only the + # latest mention of a particular macro winds up on the command + # line). I don't think it's essential, though, since most (all?) + # Unix C compilers only pay attention to the latest -D or -U + # mention of a macro on their command line. Similar situation for + # 'includes'. I'm punting on both for now. Anyways, weeding out + # redundancies like this should probably be the province of + # CCompiler, since the data structures used are inherited from it + # and therefore common to all CCompiler classes. + + pp_opts = [] + for macro in macros: + if len (macro) == 1: # undefine this macro + pp_opts.append ("-U%s" % macro[0]) + elif len (macro) == 2: + if macro[1] is None: # define with no explicit value + pp_opts.append ("-D%s" % macro[0]) + else: + # XXX *don't* need to be clever about quoting the + # macro value here, because we're going to avoid the + # shell at all costs when we spawn the command! + pp_opts.append ("-D%s=%s" % macro) + + for dir in includes: + pp_opts.append ("-I%s" % dir) + + return pp_opts + +# gen_preprocess_options () + + +def gen_lib_options (libraries, library_dirs, lib_format, dir_format): + """Generate linker options for searching library directories and + linking with specific libraries. 'libraries' and 'library_dirs' + are, respectively, lists of library names (not filenames!) and + search directories. 'lib_format' is a format string with exactly + one "%s", into which will be plugged each library name in turn; + 'dir_format' is similar, but directory names will be plugged into + it. Returns a list of command-line options suitable for use with + some compiler (depending on the two format strings passed in).""" + + lib_opts = [] + + for dir in library_dirs: + lib_opts.append (dir_format % dir) + + # XXX it's important that we *not* remove redundant library mentions! + # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to + # resolve all symbols. I just hope we never have to say "-lfoo obj.o + # -lbar" to get things to work -- that's certainly a possibility, but a + # pretty nasty way to arrange your C code. + + for lib in libraries: + lib_opts.append (lib_format % lib) + + return lib_opts + +# _gen_lib_options () -- cgit v1.2.1 From f57477b38afe2a8084580f112be920884c74fcb5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:32:19 +0000 Subject: Ditched '_gen_preprocess_options()' and '_gen_lib_options()' -- they're now provided (minus the leading underscore) by the ccompiler module. Fix 'compile()' to return the list of object files generated. Cosmetic tweaks/delete cruft. --- unixccompiler.py | 76 ++++++-------------------------------------------------- 1 file changed, 8 insertions(+), 68 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 6594043c..a8684514 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -21,7 +21,7 @@ import string, re from types import * from sysconfig import \ CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO -from ccompiler import CCompiler +from ccompiler import CCompiler, gen_preprocess_options, gen_lib_options # XXX Things not currently handled: @@ -101,8 +101,8 @@ class UnixCCompiler (CCompiler): raise TypeError, \ "'includes' (if supplied) must be a list of strings" - pp_opts = _gen_preprocess_options (self.macros + macros, - self.include_dirs + includes) + pp_opts = gen_preprocess_options (self.macros + macros, + self.include_dirs + includes) # use of ccflags_shared means we're blithely assuming that we're # compiling for inclusion in a shared object! (will have to fix @@ -111,10 +111,9 @@ class UnixCCompiler (CCompiler): self.ccflags + self.ccflags_shared + \ sources - # this will change to 'spawn' when I have it! - #print string.join ([self.cc] + cc_args, ' ') self.spawn ([self.cc] + cc_args) - + return self.object_filenames (sources) + # XXX punting on 'link_static_lib()' for now -- it might be better for # CCompiler to mandate just 'link_binary()' or some such to build a new @@ -149,12 +148,12 @@ class UnixCCompiler (CCompiler): if build_info is None: build_info = {} - lib_opts = _gen_lib_options (self.libraries + libraries, - self.library_dirs + library_dirs) + lib_opts = gen_lib_options (self.libraries + libraries, + self.library_dirs + library_dirs, + "-l%s", "-L%s") ld_args = self.ldflags_shared + lib_opts + \ objects + ['-o', output_filename] - #print string.join ([self.ld_shared] + ld_args, ' ') self.spawn ([self.ld_shared] + ld_args) @@ -174,8 +173,6 @@ class UnixCCompiler (CCompiler): def shared_library_filename (self, libname): return "lib%s%s" % (libname, self._shared_lib_ext ) - - # class UnixCCompiler @@ -184,60 +181,3 @@ def _split_command (cmd): the list of arguments; return them as (cmd, arglist).""" args = string.split (cmd) return (args[0], args[1:]) - - -def _gen_preprocess_options (macros, includes): - - # XXX it would be nice (mainly aesthetic, and so we don't generate - # stupid-looking command lines) to go over 'macros' and eliminate - # redundant definitions/undefinitions (ie. ensure that only the - # latest mention of a particular macro winds up on the command - # line). I don't think it's essential, though, since most (all?) - # Unix C compilers only pay attention to the latest -D or -U - # mention of a macro on their command line. Similar situation for - # 'includes'. I'm punting on both for now. Anyways, weeding out - # redundancies like this should probably be the province of - # CCompiler, since the data structures used are inherited from it - # and therefore common to all CCompiler classes. - - - pp_opts = [] - for macro in macros: - if len (macro) == 1: # undefine this macro - pp_opts.append ("-U%s" % macro[0]) - elif len (macro) == 2: - if macro[1] is None: # define with no explicit value - pp_opts.append ("-D%s" % macro[0]) - else: - # XXX *don't* need to be clever about quoting the - # macro value here, because we're going to avoid the - # shell at all costs when we spawn the command! - pp_opts.append ("-D%s=%s" % macro) - - for dir in includes: - pp_opts.append ("-I%s" % dir) - - return pp_opts - -# _gen_preprocess_options () - - -def _gen_lib_options (libraries, library_dirs): - - lib_opts = [] - - for dir in library_dirs: - lib_opts.append ("-L%s" % dir) - - # XXX it's important that we *not* remove redundant library mentions! - # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to - # resolve all symbols. I just hope we never have to say "-lfoo obj.o - # -lbar" to get things to work -- that's certainly a possibility, but a - # pretty nasty way to arrange your C code. - - for lib in libraries: - lib_opts.append ("-l%s" % lib) - - return lib_opts - -# _gen_lib_options () -- cgit v1.2.1 From 74deaef6c7323d3512b25e14cfea5f13fbfcb37e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:36:01 +0000 Subject: Ditched redundant docstrings and comments (overlap with ccompiler.py). Ditched redundant '_gen_preprocess_options()' and '_gen_lib_options()' -- now provided by ccompiler.py. Fixed some filename extension variables -- added missing period. Cosmetic tweaks. --- msvccompiler.py | 131 +++++++++----------------------------------------------- 1 file changed, 20 insertions(+), 111 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index ff66f913..f328232b 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -11,23 +11,13 @@ __rcsid__ = "$Id$" import os import sys from distutils.errors import * -from distutils.ccompiler import CCompiler +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options, gen_lib_options -class MSVCCompiler ( CCompiler) : - """Abstract base class to define the interface that must be implemented - by real compiler abstraction classes. Might have some use as a - place for shared code, but it's not yet clear what code can be - shared between compiler abstraction models for different platforms. - - The basic idea behind a compiler abstraction class is that each - instance can be used for all the compile/link steps in building - a single project. Thus, attributes common to all of those compile - and link steps -- include directories, macros to define, libraries - to link against, etc. -- are attributes of the compiler instance. - To allow for variability in how individual files are treated, - most (all?) of those attributes may be varied on a per-compilation - or per-link basis.""" +class MSVCCompiler (CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" def __init__ (self, verbose=0, @@ -35,11 +25,9 @@ class MSVCCompiler ( CCompiler) : CCompiler.__init__ (self, verbose, dry_run) - # XXX This is a nasty dependency to add on something otherwise - # pretty clean. move it to build_ext under an nt - # specific part. - # shared libraries need to link against python15.lib + # pretty clean. move it to build_ext under an nt specific part. + # shared libraries need to link against python15.lib self.add_library ( "python" + sys.version[0] + sys.version[2] ) self.add_library_dir( os.path.join( sys.exec_prefix, 'libs' ) ) @@ -51,39 +39,15 @@ class MSVCCompiler ( CCompiler) : self.ldflags_shared = ['/DLL', '/nologo'] self.ldflags_static = [ '/nologo'] - # XXX things not handled by this compiler abstraction model: - # * client can't provide additional options for a compiler, - # e.g. warning, optimization, debugging flags. Perhaps this - # should be the domain of concrete compiler abstraction classes - # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base - # class should have methods for the common ones. - # * can't put output files (object files, libraries, whatever) - # into a separate directory from their inputs. Should this be - # handled by an 'output_dir' attribute of the whole object, or a - # parameter to the compile/link_* methods, or both? - # * can't completely override the include or library searchg - # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". - # I'm not sure how widely supported this is even by Unix - # compilers, much less on other platforms. And I'm even less - # sure how useful it is; maybe for cross-compiling, but - # support for that is a ways off. (And anyways, cross - # compilers probably have a dedicated binary with the - # right paths compiled in. I hope.) - # * can't do really freaky things with the library list/library - # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against - # different versions of libfoo.a in different locations. I - # think this is useless without the ability to null out the - # library search path anyways. - # -- Worker methods ------------------------------------------------ # (must be implemented by subclasses) _c_extensions = [ '.c' ] - _cpp_extensions = [ '.cc', 'cpp' ] + _cpp_extensions = [ '.cc', '.cpp' ] _obj_ext = '.obj' - _exe_ext = 'exe' + _exe_ext = '.exe' _shared_lib_ext = '.dll' _static_lib_ext = '.lib' @@ -114,8 +78,8 @@ class MSVCCompiler ( CCompiler) : objectFiles = [] - base_pp_opts = _gen_preprocess_options (self.macros + macros, - self.include_dirs + includes) + base_pp_opts = gen_preprocess_options (self.macros + macros, + self.include_dirs + includes) base_pp_opts.append('/c') @@ -133,14 +97,12 @@ class MSVCCompiler ( CCompiler) : pp_opts = base_pp_opts + [ outputOpt, inputOpt ] - returnCode = self.spawn( [ self.cc ] + self.compile_options + pp_opts ) - # XXX check for valid return code - + self.spawn( [ self.cc ] + self.compile_options + pp_opts) objectFiles.append( objFile ) - return objectFiles - + + # XXX this is kind of useless without 'link_binary()' or # 'link_executable()' or something -- or maybe 'link_static_lib()' # should not exist at all, and we just have 'link_binary()'? @@ -171,8 +133,9 @@ class MSVCCompiler ( CCompiler) : if build_info is None: build_info = {} - lib_opts = _gen_lib_options (self.libraries + libraries, - self.library_dirs + library_dirs) + lib_opts = gen_lib_options (self.libraries + libraries, + self.library_dirs + library_dirs, + "%s.lib", "/LIBPATH:%s") if build_info.has_key('def_file') : lib_opts.append('/DEF:' + build_info['def_file'] ) @@ -215,8 +178,9 @@ class MSVCCompiler ( CCompiler) : if build_info is None: build_info = {} - lib_opts = _gen_lib_options (self.libraries + libraries, - self.library_dirs + library_dirs) + lib_opts = gen_lib_options (self.libraries + libraries, + self.library_dirs + library_dirs, + "%s.lib", "/LIBPATH:%s") if build_info.has_key('def_file') : lib_opts.append('/DEF:' + build_info['def_file'] ) @@ -260,58 +224,3 @@ class MSVCCompiler ( CCompiler) : return "lib%s%s" %( libname, self._shared_lib_ext ) # class MSVCCompiler - -def _gen_preprocess_options (macros, includes): - - # XXX it would be nice (mainly aesthetic, and so we don't generate - # stupid-looking command lines) to go over 'macros' and eliminate - # redundant definitions/undefinitions (ie. ensure that only the - # latest mention of a particular macro winds up on the command - # line). I don't think it's essential, though, since most (all?) - # Unix C compilers only pay attention to the latest -D or -U - # mention of a macro on their command line. Similar situation for - # 'includes'. I'm punting on both for now. Anyways, weeding out - # redundancies like this should probably be the province of - # CCompiler, since the data structures used are inherited from it - # and therefore common to all CCompiler classes. - - - pp_opts = [] - for macro in macros: - if len (macro) == 1: # undefine this macro - pp_opts.append ("-U%s" % macro[0]) - elif len (macro) == 2: - if macro[1] is None: # define with no explicit value - pp_opts.append ("-D%s" % macro[0]) - else: - # XXX *don't* need to be clever about quoting the - # macro value here, because we're going to avoid the - # shell at all costs when we spawn the command! - pp_opts.append ("-D%s=%s" % macro) - - for dir in includes: - pp_opts.append ("-I%s" % dir) - - return pp_opts - -def _gen_lib_options (libraries, library_dirs): - - lib_opts = [] - - for dir in library_dirs: - lib_opts.append ("/LIBPATH:%s" % dir) - - # XXX it's important that we *not* remove redundant library mentions! - # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to - # resolve all symbols. I just hope we never have to say "-lfoo obj.o - # -lbar" to get things to work -- that's certainly a possibility, but a - # pretty nasty way to arrange your C code. - - for lib in libraries: - lib_opts.append ("%s.lib" % lib) # import libraries end in .lib - - return lib_opts - -# _gen_lib_options () - - -- cgit v1.2.1 From 7fc78eeacf803248087fcf97f900c43656e02946 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:41:09 +0000 Subject: Careful rethink of command options, distribution options, distribution attributes, etc. Biggest change was to the Distribution constructor -- it now looks for an 'options' attribute, which contains values (options) that are explicitly farmed out to the commands. Also, certain options supplied to Distribution (ie. in the 'setup()' call in setup.py) are now "command option aliases", meaning they are dropped right into a certain command rather than being distribution options. This is handled by a new Distribution class attribute, 'alias_options'. Various comment changes to reflect the new way-of-thinking. Added 'get_command_name()' method to Command -- was assuming its existence all along as 'command_name()', so changed the code that needs it to call 'get_command_name()'. --- core.py | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 108 insertions(+), 26 deletions(-) diff --git a/core.py b/core.py index 2d5d0661..8d2572af 100644 --- a/core.py +++ b/core.py @@ -72,9 +72,12 @@ def setup (**attrs): # (ie. everything except distclass) to initialize it dist = klass (attrs) - # Get it to parse the command line; any command-line errors are - # the end-users fault, so turn them into SystemExit to suppress - # tracebacks. + # If we had a config file, this is where we would parse it: override + # the client-supplied command options, but be overridden by the + # command line. + + # Parse the command line; any command-line errors are the end-users + # fault, so turn them into SystemExit to suppress tracebacks. try: dist.parse_command_line (sys.argv[1:]) except DistutilsArgError, msg: @@ -111,6 +114,18 @@ class Distribution: ('dry-run', 'n', "don't actually do anything"), ] + # 'alias_options' map distribution options to command options -- the + # idea is that the most common, essential options can be directly + # specified as Distribution attributes, and the rest can go in the + # 'options' dictionary. These aliases are for those common, essential + # options. + alias_options = { 'py_modules': ('build_py', 'modules'), + 'ext_modules': ('build_ext', 'modules'), + 'package': [('build_py', 'package',), + ('build_ext', 'package')], + + } + # -- Creation/initialization methods ------------------------------- @@ -129,11 +144,13 @@ class Distribution: self.verbose = 0 self.dry_run = 0 - # And for all other attributes (stuff that might be passed in - # from setup.py, rather than from the end-user) + # And the "distribution meta-data" options -- these can only + # come from setup.py (the caller), not the command line + # (or a hypothetical config file).. self.name = None self.version = None self.author = None + self.url = None self.licence = None self.description = None @@ -143,18 +160,14 @@ class Distribution: # for the client to override command classes self.cmdclass = {} - # The rest of these are really the business of various commands, - # rather than of the Distribution itself. However, they have - # to be here as a conduit to the relevant command class. - self.py_modules = None - self.ext_modules = None - self.package = None - - # Now we'll use the attrs dictionary to possibly override - # any or all of these distribution options - if attrs: - for k in attrs.keys(): - setattr (self, k, attrs[k]) + # These options are really the business of various commands, rather + # than of the Distribution itself. We provide aliases for them in + # Distribution as a convenience to the developer. + # dictionary. + # XXX not needed anymore! (I think...) + #self.py_modules = None + #self.ext_modules = None + #self.package = None # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to @@ -174,6 +187,49 @@ class Distribution: # '.get()' rather than a straight lookup. self.have_run = {} + # Now we'll use the attrs dictionary (from the client) to possibly + # override any or all of these distribution options + if attrs: + + # Pull out the set of command options and work on them + # specifically. Note that this order guarantees that aliased + # command options will override any supplied redundantly + # through the general options dictionary. + options = attrs.get ('options') + if options: + del attrs['options'] + for (command, cmd_options) in options.items(): + cmd_obj = self.find_command_obj (command) + for (key, val) in cmd_options.items(): + cmd_obj.set_option (key, val) + # loop over commands + # if any command options + + # Now work on the rest of the attributes. Note that some of + # these may be aliases for command options, so we might go + # through some of the above again. + for (key,val) in attrs.items(): + alias = self.alias_options.get (key) + if alias: + if type (alias) is ListType: + for (command, cmd_option) in alias: + cmd_obj = self.find_command_obj (command) + cmd_obj.set_option (cmd_option, val) + elif type (alias) is TupleType: + (command, cmd_option) = alias + cmd_obj = self.find_command_obj (command) + cmd_obj.set_option (cmd_option, val) + else: + raise RuntimeError, \ + ("oops! bad alias option for '%s': " + + "must be tuple or list of tuples") % key + + elif hasattr (self, key): + setattr (self, key, val) + else: + raise DistutilsOptionError, \ + "invalid distribution option '%s'" % key + # __init__ () @@ -213,10 +269,10 @@ class Distribution: raise SystemExit, "invalid command name '%s'" % command self.commands.append (command) - # Have to instantiate the command class now, so we have a - # way to get its valid options and somewhere to put the - # results of parsing its share of the command-line - cmd_obj = self.create_command_obj (command) + # Make sure we have a command object to put the options into + # (this either pulls it out of a cache of command objects, + # or finds and instantiates the command class). + cmd_obj = self.find_command_obj (command) # Require that the command class be derived from Command -- # that way, we can be sure that we at least have the 'run' @@ -226,8 +282,15 @@ class Distribution: "command class %s must subclass Command" % \ cmd_obj.__class__ - # XXX this assumes that cmd_obj provides an 'options' - # attribute, but we're not enforcing that anywhere! + # Also make sure that the command object provides a list of its + # known options + if not (hasattr (cmd_obj, 'options') and + type (cmd_obj.options) is ListType): + raise DistutilsClasserror, \ + ("command class %s must provide an 'options' attribute "+ + "(a list of tuples)") % \ + cmd_obj.__class__ + args = fancy_getopt (cmd_obj.options, cmd_obj, args[1:]) self.command_obj[command] = cmd_obj self.have_run[command] = 0 @@ -376,6 +439,11 @@ class Distribution: Then invoke 'run()' on that command object (or an existing one).""" + # XXX currently, this is the only place where we invoke a + # command object's 'run()' method -- so it might make sense to + # put the 'set_final_options()' call here, too, instead of + # requiring every command's 'run()' to call it first. + # Already been here, done that? then return silently. if self.have_run.get (command): return @@ -530,7 +598,7 @@ class Command: except AttributeError: raise DistutilsOptionError, \ "command %s: no such option %s" % \ - (self.command_name(), option) + (self.get_command_name(), option) def get_options (self, *options): @@ -545,7 +613,7 @@ class Command: except AttributeError, name: raise DistutilsOptionError, \ "command %s: no such option %s" % \ - (self.command_name(), name) + (self.get_command_name(), name) return tuple (values) @@ -557,7 +625,7 @@ class Command: if not hasattr (self, option): raise DistutilsOptionError, \ "command %s: no such option %s" % \ - (self.command_name(), option) + (self.get_command_name(), option) if value is not None: setattr (self, option, value) @@ -573,6 +641,20 @@ class Command: # -- Convenience methods for commands ------------------------------ + def get_command_name (self): + if hasattr (self, 'command_name'): + return self.command_name + else: + class_name = self.__class__.__name__ + + # The re.split here returs empty strings delimited by the + # words we're actually interested in -- e.g. "FooBarBaz" + # splits to ['', 'Foo', '', 'Bar', '', 'Baz', '']. Hence + # the 'filter' to strip out the empties. + words = filter (None, re.split (r'([A-Z][a-z]+)', class_name)) + return string.join (map (string.lower, words), "_") + + def set_undefined_options (self, src_cmd, *option_pairs): """Set the values of any "undefined" options from corresponding option values in some other command object. "Undefined" here -- cgit v1.2.1 From ba6f5bc4f0534bb3ebab3a17f16686f613e42707 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:42:30 +0000 Subject: Changed to reflect the new "command options" regime -- in particular, we no longer explicitly pull distribution options out of our Distribution object, but rather let the Distribution put them into the command object. --- command/build_ext.py | 22 +++++++++++++++------- command/build_py.py | 14 +++++++------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index a0464b4e..a3982c1b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -61,6 +61,7 @@ class BuildExt (Command): def set_default_options (self): + self.extensions = None self.dir = None self.include_dirs = None self.define = None @@ -90,10 +91,14 @@ class BuildExt (Command): def run (self): self.set_final_options () - (extensions, package) = \ - self.distribution.get_options ('ext_modules', 'package') - # 'extensions', as supplied by setup.py, is a list of 2-tuples. + # XXX we should care about the package we compile extensions + # into! + + #(extensions, package) = \ + # self.distribution.get_options ('ext_modules', 'package') + + # 'self.extensions', as supplied by setup.py, is a list of 2-tuples. # Each tuple is simple: # (ext_name, build_info) # build_info is a dictionary containing everything specific to @@ -101,13 +106,16 @@ class BuildExt (Command): # should be handled by general distutils options passed from # setup.py down to right here, but that's not taken care of yet.) + if not self.extensions: + return - # First, sanity-check the 'extensions' list - self.check_extensions_list (extensions) + # First, sanity-check the 'self.extensions' list + self.check_extensions_list (self.extensions) # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler (verbose=self.distribution.verbose, + self.compiler = new_compiler (plat=os.environ.get ('PLAT'), + verbose=self.distribution.verbose, dry_run=self.distribution.dry_run) if self.include_dirs is not None: self.compiler.set_include_dirs (self.include_dirs) @@ -128,7 +136,7 @@ class BuildExt (Command): self.compiler.set_link_objects (self.link_objects) # Now the real loop over extensions - self.build_extensions (extensions) + self.build_extensions (self.extensions) diff --git a/command/build_py.py b/command/build_py.py index d75bf3f6..28aefa9e 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -20,10 +20,14 @@ class BuildPy (Command): def set_default_options (self): self.dir = None + self.modules = None + self.package = None def set_final_options (self): self.set_undefined_options ('build', ('libdir', 'dir')) + if self.package is None: + self.package = '' def run (self): @@ -43,10 +47,6 @@ class BuildPy (Command): self.set_final_options () - (modules, package) = \ - self.distribution.get_options ('py_modules', 'package') - package = package or '' - infiles = [] outfiles = [] missing = [] @@ -56,20 +56,20 @@ class BuildPy (Command): # input files. # it's ok not to have *any* py files, right? - if not modules: + if not self.modules: return # XXX we should allow for wildcards, so eg. the Distutils setup.py # file would just have to say # py_modules = ['distutils.*', 'distutils.command.*'] # without having to list each one explicitly. - for m in modules: + for m in self.modules: fn = apply (os.path.join, tuple (string.split (m, '.'))) + '.py' if not os.path.exists (fn): missing.append (fn) else: infiles.append (fn) - outfiles.append (os.path.join (self.dir, package, fn)) + outfiles.append (os.path.join (self.dir, self.package, fn)) # Blow up if any input files were not found. if missing: -- cgit v1.2.1 From cb448b16d3ad06c7158fe5a63cdb2f66b401935d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 03:03:01 +0000 Subject: Fixed some goofs in 'alias_options'. Error message tweak in Command.set_option(). Added Command.get_peer_option(). Added Command.move_file() wrapper. --- core.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index 8d2572af..a6c378d3 100644 --- a/core.py +++ b/core.py @@ -120,9 +120,10 @@ class Distribution: # 'options' dictionary. These aliases are for those common, essential # options. alias_options = { 'py_modules': ('build_py', 'modules'), - 'ext_modules': ('build_ext', 'modules'), + 'ext_modules': ('build_ext', 'extensions'), 'package': [('build_py', 'package',), ('build_ext', 'package')], + 'include_dirs': ('build_ext', 'include_dirs'), } @@ -624,7 +625,7 @@ class Command: if not hasattr (self, option): raise DistutilsOptionError, \ - "command %s: no such option %s" % \ + "command '%s': no such option '%s'" % \ (self.get_command_name(), option) if value is not None: setattr (self, option, value) @@ -701,6 +702,11 @@ class Command: cmd_obj.set_final_options () + def get_peer_option (self, command, option): + cmd_obj = self.distribution.find_command_obj (command) + return cmd_obj.get_option (option) + + def run_peer (self, command): """Run some other command: uses the 'run_command()' method of Distribution, which creates the command object if necessary @@ -767,6 +773,13 @@ class Command: self.distribution.dry_run) + def move_file (self, src, dst, level=1): + """Move a file respecting verbose and dry-run flags.""" + return util.move_file (src, dst, + self.distribution.verbose >= level, + self.distribution.dry_run) + + def make_file (self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): -- cgit v1.2.1 From e9f33b359886fec9cc43caf900f3f1e8a12a44ae Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 03:07:24 +0000 Subject: Added 'output_dir' attribute, and 'output_dir' parameter to several method signatures, and updated some docstrings to reflect it. Some comments added. Added 'announce()' and 'move_file()' methods. --- ccompiler.py | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index b1167ac2..0e505338 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -12,6 +12,7 @@ from types import * from copy import copy from distutils.errors import * from distutils.spawn import spawn +from distutils.util import move_file class CCompiler: @@ -62,6 +63,10 @@ class CCompiler: self.verbose = verbose self.dry_run = dry_run + # 'output_dir': a common output directory for object, library, + # shared object, and shared library files + self.output_dir = None + # 'macros': a list of macro definitions (or undefinitions). A # macro definition is a 2-tuple (name, value), where the value is # either a string or None (no explicit value). A macro @@ -244,6 +249,7 @@ class CCompiler: def compile (self, sources, + output_dir=None, macros=None, includes=None): """Compile one or more C/C++ source files. 'sources' must be @@ -270,6 +276,7 @@ class CCompiler: def link_static_lib (self, objects, output_libname, + output_dir=None, libraries=None, library_dirs=None): """Link a bunch of stuff together to create a static library @@ -317,6 +324,7 @@ class CCompiler: def link_shared_lib (self, objects, output_libname, + output_dir=None, libraries=None, library_dirs=None, build_info=None): @@ -330,25 +338,37 @@ class CCompiler: def link_shared_object (self, objects, output_filename, + output_dir=None, libraries=None, library_dirs=None, build_info=None): """Link a bunch of stuff together to create a shared object - file. Much like 'link_shared_lib()', except the output - filename is explicitly supplied as 'output_filename'.""" + file. Much like 'link_shared_lib()', except the output filename + is explicitly supplied as 'output_filename'. If 'output_dir' is + supplied, 'output_filename' is relative to it + (i.e. 'output_filename' can provide directoriy components if + needed).""" pass # -- Filename mangling methods ------------------------------------- - def object_filenames (self, source_filenames): + # General principle for the filename-mangling methods: by default, + # don't include a directory component, no matter what the caller + # supplies. Eg. for UnixCCompiler, a source file of "foo/bar/baz.c" + # becomes "baz.o" or "baz.so", etc. (That way, it's easiest for the + # caller to decide where it wants to put/find the output file.) The + # 'output_dir' parameter overrides this, of course -- the directory + # component of the input filenames is replaced by 'output_dir'. + + def object_filenames (self, source_filenames, output_dir=None): """Return the list of object filenames corresponding to each specified source filename.""" pass def shared_object_filename (self, source_filename): """Return the shared object filename corresponding to a - specified source filename.""" + specified source filename (assuming the same directory).""" pass def library_filename (self, libname): @@ -362,7 +382,7 @@ class CCompiler: specified library name.""" pass - + # XXX ugh -- these should go! def object_name (self, inname): """Given a name with no extension, return the name + object extension""" return inname + self._obj_ext @@ -373,9 +393,16 @@ class CCompiler: # -- Utility methods ----------------------------------------------- + def announce (self, msg, level=1): + if self.verbose >= level: + print msg + def spawn (self, cmd): spawn (cmd, verbose=self.verbose, dry_run=self.dry_run) + def move_file (self, src, dst): + return move_file (src, dst, verbose=self.verbose, dry_run=self.dry_run) + # class CCompiler @@ -393,7 +420,7 @@ def new_compiler (plat=None, return UnixCCompiler (verbose, dry_run) elif plat == 'nt': from msvccompiler import MSVCCompiler - return MSVCCompiler ( verbose, dry_run ) + return MSVCCompiler (verbose, dry_run) else: raise DistutilsPlatformError, \ "don't know how to compile C/C++ code on platform %s" % plat -- cgit v1.2.1 From 38e16941af7fee0698b7c2f53508a3a6b461a30c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 03:09:38 +0000 Subject: Added 'newer_pairwise()' and 'newer_group()'. Terminology change in 'newer()'. Made 'copy_tree' respect dry_run flag a little better. Added 'move_file()'. --- util.py | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 133 insertions(+), 16 deletions(-) diff --git a/util.py b/util.py index 9a299dfd..bb790af4 100644 --- a/util.py +++ b/util.py @@ -62,26 +62,75 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): # mkpath () -def newer (file1, file2): - """Return true if file1 exists and is more recently modified than - file2, or if file1 exists and file2 doesn't. Return false if both - exist and file2 is the same age or younger than file1. Raises - DistutilsFileError if file1 does not exist.""" - - if not os.path.exists (file1): - raise DistutilsFileError, "file '%s' does not exist" % file1 - if not os.path.exists (file2): +def newer (source, target): + """Return true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. Return + false if both exist and 'target' is the same age or younger than + 'source'. Raise DistutilsFileError if 'source' does not + exist.""" + + if not os.path.exists (source): + raise DistutilsFileError, "file '%s' does not exist" % source + if not os.path.exists (target): return 1 - from stat import * - mtime1 = os.stat(file1)[ST_MTIME] - mtime2 = os.stat(file2)[ST_MTIME] + from stat import ST_MTIME + mtime1 = os.stat(source)[ST_MTIME] + mtime2 = os.stat(target)[ST_MTIME] return mtime1 > mtime2 # newer () +def newer_pairwise (sources, targets): + + """Walk two filename lists in parallel, testing if each 'target' is + up-to-date relative to its corresponding 'source'. If so, both + are deleted from their respective lists. Return a list of tuples + containing the deleted (source,target) pairs.""" + + if len (sources) != len (targets): + raise ValueError, "'sources' and 'targets' must be same length" + + goners = [] + for i in range (len (sources)-1, -1, -1): + if not newer (sources[i], targets[i]): + goners.append ((sources[i], targets[i])) + del sources[i] + del targets[i] + goners.reverse() + return goners + +# newer_pairwise () + + +def newer_group (sources, target): + """Return true if 'target' is out-of-date with respect to any + file listed in 'sources'. In other words, if 'target' exists and + is newer than every file in 'sources', return false; otherwise + return true.""" + + # If the target doesn't even exist, then it's definitely out-of-date. + if not os.path.exists (target): + return 1 + + # Otherwise we have to find out the hard way: if *any* source file + # is more recent than 'target', then 'target' is out-of-date and + # we can immediately return true. If we fall through to the end + # of the loop, then 'target' is up-to-date and we return false. + from stat import ST_MTIME + target_mtime = os.stat (target)[ST_MTIME] + for source in sources: + source_mtime = os.stat(source)[ST_MTIME] + if source_mtime > target_mtime: + return 1 + else: + return 0 + +# newer_group () + + def make_file (src, dst, func, args, verbose=0, update_message=None, noupdate_message=None): """Makes 'dst' from 'src' (both filenames) by calling 'func' with @@ -176,7 +225,7 @@ def copy_file (src, dst, if not os.path.isfile (src): raise DistutilsFileError, \ - "can't copy %s:not a regular file" % src + "can't copy %s: not a regular file" % src if os.path.isdir (dst): dir = dst @@ -237,14 +286,17 @@ def copy_tree (src, dst, (the default), the destination of the symlink will be copied. 'update' and 'verbose' are the same as for 'copy_file'.""" - if not os.path.isdir (src): + if not dry_run and not os.path.isdir (src): raise DistutilsFileError, \ "cannot copy tree %s: not a directory" % src try: names = os.listdir (src) except os.error, (errno, errstr): - raise DistutilsFileError, \ - "error listing files in %s: %s" % (src, errstr) + if dry_run: + names = [] + else: + raise DistutilsFileError, \ + "error listing files in %s: %s" % (src, errstr) if not dry_run: mkpath (dst, verbose=verbose) @@ -277,3 +329,68 @@ def copy_tree (src, dst, return outputs # copy_tree () + + +# XXX I suspect this is Unix-specific -- need porting help! +def move_file (src, dst, + verbose=0, + dry_run=0): + + """Move a file 'src' to 'dst'. If 'dst' is a directory, the file + will be moved into it with the same name; otherwise, 'src' is + just renamed to 'dst'. Return the new full name of the file. + + Handles cross-device moves on Unix using + 'copy_file()'. What about other systems???""" + + from os.path import exists, isfile, isdir, basename, dirname + + if verbose: + print "moving %s -> %s" % (src, dst) + + if dry_run: + return dst + + if not isfile (src): + raise DistutilsFileError, \ + "can't move '%s': not a regular file" % src + + if isdir (dst): + dst = os.path.join (dst, basename (src)) + elif exists (dst): + raise DistutilsFileError, \ + "can't move '%s': destination '%s' already exists" % \ + (src, dst) + + if not isdir (dirname (dst)): + raise DistutilsFileError, \ + "can't move '%s': destination '%s' not a valid path" % \ + (src, dst) + + copy_it = 0 + try: + os.rename (src, dst) + except os.error, (num, msg): + if num == errno.EXDEV: + copy_it = 1 + else: + raise DistutilsFileError, \ + "couldn't move '%s' to '%s': %s" % (src, dst, msg) + + if copy_it: + copy_file (src, dst) + try: + os.unlink (src) + except os.error, (num, msg): + try: + os.unlink (dst) + except os.error: + pass + raise DistutilsFileError, \ + ("couldn't move '%s' to '%s' by copy/delete: " + + "delete '%s' failed: %s") % \ + (src, dst, src, msg) + + return dst + +# move_file () -- cgit v1.2.1 From d16f82401008b9b8c470bd3506c34fe29cd4f8d7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 03:10:25 +0000 Subject: New command -- install_ext to install extension modules. --- command/install_ext.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 command/install_ext.py diff --git a/command/install_ext.py b/command/install_ext.py new file mode 100644 index 00000000..360a8027 --- /dev/null +++ b/command/install_ext.py @@ -0,0 +1,38 @@ +"""install_ext + +Implement the Distutils "install_ext" command to install extension modules.""" + +# created 1999/09/12, Greg Ward + +__rcsid__ = "$Id$" + +from distutils.core import Command +from distutils.util import copy_tree + +class InstallExt (Command): + + options = [('dir=', 'd', "directory to install to"), + ('build-dir=','b', "build directory (where to install from)"), + ] + + def set_default_options (self): + # let the 'install' command dictate our installation directory + self.dir = None + self.build_dir = None + + def set_final_options (self): + self.set_undefined_options ('install', + ('build_platlib', 'build_dir'), + ('install_site_platlib', 'dir')) + + def run (self): + self.set_final_options () + + # Dump the entire "build/platlib" directory (or whatever it really + # is; "build/platlib" is the default) to the installation target + # (eg. "/usr/local/lib/python1.5/site-packages"). Note that + # putting files in the right package dir is already done when we + # build. + outfiles = self.copy_tree (self.build_dir, self.dir) + +# class InstallExt -- cgit v1.2.1 From 6d41846de7faa855ac30ea57349780d3cc074f96 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 03:12:53 +0000 Subject: Added 'output_dir' parameter to 'compile()' and 'link_shared_object(). Changed those two methods to only compile/link if necessary (according to simplistic timestamp checks). Added 'output_dir' to 'object_filenames()' and 'shared_object_filename()'. --- unixccompiler.py | 105 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 22 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index a8684514..01493131 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,12 +17,13 @@ the "typical" Unix-style command-line C compiler: __rcsid__ = "$Id$" -import string, re +import string, re, os from types import * +from copy import copy from sysconfig import \ CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO from ccompiler import CCompiler, gen_preprocess_options, gen_lib_options - +from util import move_file, newer_pairwise, newer_group # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's @@ -86,9 +87,12 @@ class UnixCCompiler (CCompiler): def compile (self, sources, + output_dir=None, macros=None, includes=None): + if output_dir is None: + output_dir = self.output_dir if macros is None: macros = [] if includes is None: @@ -104,15 +108,48 @@ class UnixCCompiler (CCompiler): pp_opts = gen_preprocess_options (self.macros + macros, self.include_dirs + includes) - # use of ccflags_shared means we're blithely assuming that we're - # compiling for inclusion in a shared object! (will have to fix - # this when I add the ability to build a new Python) - cc_args = ['-c'] + pp_opts + \ - self.ccflags + self.ccflags_shared + \ - sources + # So we can mangle 'sources' without hurting the caller's data + orig_sources = sources + sources = copy (sources) + + # Get the list of expected output (object) files and drop files we + # don't have to recompile. (Simplistic check -- we just compare the + # source and object file, no deep dependency checking involving + # header files. Hmmm.) + objects = self.object_filenames (sources, output_dir) + skipped = newer_pairwise (sources, objects) + for skipped_pair in skipped: + self.announce ("skipping %s (%s up-to-date)" % skipped_pair) + + # If anything left to compile, compile it + if sources: + # XXX use of ccflags_shared means we're blithely assuming + # that we're compiling for inclusion in a shared object! + # (will have to fix this when I add the ability to build a + # new Python) + cc_args = ['-c'] + pp_opts + \ + self.ccflags + self.ccflags_shared + \ + sources + self.spawn ([self.cc] + cc_args) + - self.spawn ([self.cc] + cc_args) - return self.object_filenames (sources) + # Note that compiling multiple source files in the same go like + # we've just done drops the .o file in the current directory, which + # may not be what the caller wants (depending on the 'output_dir' + # parameter). So, if necessary, fix that now by moving the .o + # files into the desired output directory. (The alternative, of + # course, is to compile one-at-a-time with a -o option. 6 of one, + # 12/2 of the other...) + + if output_dir: + for i in range (len (objects)): + src = os.path.basename (objects[i]) + objects[i] = self.move_file (src, output_dir) + + # Have to re-fetch list of object filenames, because we want to + # return *all* of them, including those that weren't recompiled on + # this call! + return self.object_filenames (orig_sources, output_dir) # XXX punting on 'link_static_lib()' for now -- it might be better for @@ -124,23 +161,31 @@ class UnixCCompiler (CCompiler): def link_shared_lib (self, objects, output_libname, + output_dir=None, libraries=None, library_dirs=None, build_info=None): # XXX should we sanity check the library name? (eg. no # slashes) - self.link_shared_object (objects, "lib%s%s" % \ - (output_libname, self._shared_lib_ext), - build_info=build_info) - + self.link_shared_object ( + objects, + "lib%s%s" % (output_libname, self._shared_lib_ext), + output_dir, + libraries, + library_dirs, + build_info) + def link_shared_object (self, objects, output_filename, + output_dir=None, libraries=None, library_dirs=None, build_info=None): + if output_dir is None: + output_dir = self.output_dir if libraries is None: libraries = [] if library_dirs is None: @@ -151,21 +196,37 @@ class UnixCCompiler (CCompiler): lib_opts = gen_lib_options (self.libraries + libraries, self.library_dirs + library_dirs, "-l%s", "-L%s") - ld_args = self.ldflags_shared + lib_opts + \ - objects + ['-o', output_filename] + if output_dir is not None: + output_filename = os.path.join (output_dir, output_filename) + + # If any of the input object files are newer than the output shared + # object, relink. Again, this is a simplistic dependency check: + # doesn't look at any of the libraries we might be linking with. + if newer_group (objects, output_filename): + ld_args = self.ldflags_shared + lib_opts + \ + objects + ['-o', output_filename] - self.spawn ([self.ld_shared] + ld_args) + self.spawn ([self.ld_shared] + ld_args) + else: + self.announce ("skipping %s (up-to-date)" % output_filename) - def object_filenames (self, source_filenames): + def object_filenames (self, source_filenames, output_dir=None): outnames = [] for inname in source_filenames: - outnames.append ( re.sub (r'\.(c|C|cc|cxx|cpp)$', - self._obj_ext, inname)) + outname = re.sub (r'\.(c|C|cc|cxx|cpp)$', self._obj_ext, inname) + outname = os.path.basename (outname) + if output_dir is not None: + outname = os.path.join (output_dir, outname) + outnames.append (outname) return outnames - def shared_object_filename (self, source_filename): - return re.sub (r'\.(c|C|cc|cxx|cpp)$', self._shared_lib_ext) + def shared_object_filename (self, source_filename, output_dir=None): + outname = re.sub (r'\.(c|C|cc|cxx|cpp)$', self._shared_lib_ext) + outname = os.path.basename (outname) + if output_dir is not None: + outname = os.path.join (output_dir, outname) + return outname def library_filename (self, libname): return "lib%s%s" % (libname, self._static_lib_ext ) -- cgit v1.2.1 From 7a95432812567cf0ce8135334619b557229c7a15 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 13:52:12 +0000 Subject: Now run 'build_ext'. Default platform-specific build directory changed to 'build/platlib'. --- command/build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build.py b/command/build.py index d0e939e9..187dddc0 100644 --- a/command/build.py +++ b/command/build.py @@ -30,7 +30,7 @@ class Build (Command): if self.libdir is None: self.libdir = os.path.join (self.basedir, 'lib') if self.platdir is None: - self.platdir = os.path.join (self.basedir, 'plat') + self.platdir = os.path.join (self.basedir, 'platlib') def run (self): @@ -44,6 +44,6 @@ class Build (Command): self.run_peer ('build_py') # And now 'build_ext' - #self.run_peer ('build_ext') + self.run_peer ('build_ext') # end class Build -- cgit v1.2.1 From 3ae6c30ea10386dbf794f794af67fb5fe467d4e2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 13:54:06 +0000 Subject: Comment addition. --- command/build_py.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/build_py.py b/command/build_py.py index 28aefa9e..0bbe339a 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -26,6 +26,9 @@ class BuildPy (Command): def set_final_options (self): self.set_undefined_options ('build', ('libdir', 'dir')) + # 'package' is an alias option in Distribution (hmmm, we + # really should change to "pull" options from Distribution + # rather than "pushing" them out to commands...) if self.package is None: self.package = '' -- cgit v1.2.1 From a66fe4c0aa10aa6856e000034efc58d7369aeee1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 13:55:34 +0000 Subject: Added support for 'package' option, including where to link the actual extension module to. --- command/build_ext.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index a3982c1b..29915812 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -63,6 +63,8 @@ class BuildExt (Command): def set_default_options (self): self.extensions = None self.dir = None + self.package = None + self.include_dirs = None self.define = None self.undef = None @@ -74,6 +76,9 @@ class BuildExt (Command): def set_final_options (self): self.set_undefined_options ('build', ('platdir', 'dir')) + if self.package is None: + self.package = '' + # Make sure Python's include directories (for Python.h, config.h, # etc.) are in the include search path. We have to roll our own # "exec include dir", because the Makefile parsed by sysconfig @@ -92,12 +97,6 @@ class BuildExt (Command): self.set_final_options () - # XXX we should care about the package we compile extensions - # into! - - #(extensions, package) = \ - # self.distribution.get_options ('ext_modules', 'package') - # 'self.extensions', as supplied by setup.py, is a list of 2-tuples. # Each tuple is simple: # (ext_name, build_info) @@ -187,8 +186,12 @@ class BuildExt (Command): libraries = build_info.get ('libraries') library_dirs = build_info.get ('library_dirs') ext_filename = self.extension_filename (extension_name) - self.compiler.link_shared_object (objects, ext_filename, - libraries, library_dirs, build_info) + dest = os.path.dirname ( + os.path.join (self.dir, self.package, ext_filename)) + self.mkpath (dest) + self.compiler.link_shared_object (objects, ext_filename, dest, + libraries, library_dirs, + build_info=build_info) # XXX hack! # build_extensions () -- cgit v1.2.1 From fc9f4cea6fc24a4e9fd19b16fbabdfad394e9742 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 13:57:26 +0000 Subject: Straightened up the selection of installation directories for platform- specific files; it was somewhat broken, and the comments were dead wrong. Now runs 'install_ext' command after 'install_py'. --- command/install.py | 52 +++++++++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/command/install.py b/command/install.py index cf45004f..6fb85361 100644 --- a/command/install.py +++ b/command/install.py @@ -139,42 +139,33 @@ class Install (Command): self.replace_sys_prefix ('BINLIBDEST', ('lib','python1.5'), 1) - # Here is where we decide where to install most library files: - # on POSIX systems, they go to 'site-packages' under the - # install_lib (determined above -- typically - # /usr/local/lib/python1.x). Unfortunately, both - # platform-independent (.py*) and platform-specific (.so) files - # go to this directory, since there is no site-packages under - # $exec_prefix in the usual way that Python builds sys.path. On - # non-POSIX systems, the situation is even worse: everything - # gets dumped right into $exec_prefix, not even a lib - # subdirectory! But apparently that's what needs to be done to - # make it work under Python 1.5 -- hope we can get this fixed - # for 1.6! + # Here is where we decide where to install most library files: on + # POSIX systems, they go to 'site-packages' under the install_lib + # (determined above -- typically /usr/local/lib/python1.x). Note + # that on POSIX systems, platform-specific files belong in + # 'site-packages' under install_platlib. (The actual rule is that + # a module distribution that includes *any* platform-specific files + # -- ie. extension modules -- goes under install_platlib. This + # solves the "can't find extension module in a package" problem.) + # On non-POSIX systems, install_lib and install_platlib are the + # same (eg. "C:\Program Files\Python\Lib" on Windows), as are + # install_site_lib and install_site_platlib (eg. + # "C:\Program Files\Python" on Windows) -- everything will be dumped + # right into one of the install_site directories. (It doesn't + # really matter *which* one, of course, but I'll observe decorum + # and do it properly.) if self.install_site_lib is None: if os.name == 'posix': self.install_site_lib = \ os.path.join (self.install_lib, 'site-packages') else: - self.install_site_lib = self.exec_prefix + self.install_site_lib = self.prefix if self.install_site_platlib is None: if os.name == 'posix': - # XXX ugh! this puts platform-specific files in with - # shared files, with no nice way to override it! (this - # might be a Python problem, though, not a Distutils - # problem...) - - # NO: the way to fix this is - # * any platform-dependent files in distribution? - # yes: install under exec-prefix - # no: install under prefix - # ...which will require a pretty major rethink of all - # this. Damn. - self.install_site_platlib = \ - os.path.join (self.install_lib, 'site-packages') + os.path.join (self.install_platlib, 'site-packages') else: self.install_site_platlib = self.exec_prefix @@ -240,12 +231,11 @@ class Install (Command): # Install modules in two steps: "platform-shared" files (ie. pure # python modules) and platform-specific files (compiled C - # extensions). - + # extensions). Note that 'install_py' is smart enough to install + # pure Python modules in the "platlib" directory if we built any + # extensions. self.run_peer ('install_py') - - # don't have an 'install_ext' command just yet! - #self.run_peer ('install_ext')) + self.run_peer ('install_ext') # run () -- cgit v1.2.1 From 4c89c1907bed072e12306b6e7380b7e46b57285b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 13:58:34 +0000 Subject: Changed selection of installation directories (in 'set_final_options()') so that pure Python modules are installed to the platform-specific directory if there are any extension modules in this distribution. --- command/install_lib.py | 20 ++++++++++++++------ command/install_py.py | 20 ++++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index 876a34ce..a2ba16cc 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -23,15 +23,23 @@ class InstallPy (Command): self.optimize = 1 def set_final_options (self): - # If we don't have a 'dir' value, we'll have to ask the 'install' - # command for one. (This usually means the user ran 'install_py' - # directly, rather than going through 'install' -- so in reality, - # 'find_command_obj()' will create an 'install' command object, - # which we then query. + # Find out from the 'build_ext' command if we were asked to build + # any extensions. If so, that means even pure-Python modules in + # this distribution have to be installed to the "platlib" + # directory. + extensions = self.get_peer_option ('build_ext', 'extensions') + if extensions: + dir_option = 'install_site_platlib' + else: + dir_option = 'install_site_lib' + + # Get all the information we need to install pure Python modules + # from the umbrella 'install' command -- build (source) directory, + # install (target) directory, and whether to compile .py files. self.set_undefined_options ('install', ('build_lib', 'build_dir'), - ('install_site_lib', 'dir'), + (dir_option, 'dir'), ('compile_py', 'compile'), ('optimize_py', 'optimize')) diff --git a/command/install_py.py b/command/install_py.py index 876a34ce..a2ba16cc 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -23,15 +23,23 @@ class InstallPy (Command): self.optimize = 1 def set_final_options (self): - # If we don't have a 'dir' value, we'll have to ask the 'install' - # command for one. (This usually means the user ran 'install_py' - # directly, rather than going through 'install' -- so in reality, - # 'find_command_obj()' will create an 'install' command object, - # which we then query. + # Find out from the 'build_ext' command if we were asked to build + # any extensions. If so, that means even pure-Python modules in + # this distribution have to be installed to the "platlib" + # directory. + extensions = self.get_peer_option ('build_ext', 'extensions') + if extensions: + dir_option = 'install_site_platlib' + else: + dir_option = 'install_site_lib' + + # Get all the information we need to install pure Python modules + # from the umbrella 'install' command -- build (source) directory, + # install (target) directory, and whether to compile .py files. self.set_undefined_options ('install', ('build_lib', 'build_dir'), - ('install_site_lib', 'dir'), + (dir_option, 'dir'), ('compile_py', 'compile'), ('optimize_py', 'optimize')) -- cgit v1.2.1 From cc443544d4b1095c5d358652a98a79c743599abb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:22:34 +0000 Subject: Basically a complete rewrite to support dealing with modules in whole packages and searching for source files by 'package_dir'. --- command/build_py.py | 233 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 197 insertions(+), 36 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 0bbe339a..187e93b5 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -7,6 +7,9 @@ Implements the Distutils 'build_py' command.""" __rcsid__ = "$Id$" import string, os +from types import * +from glob import glob + from distutils.core import Command from distutils.errors import * from distutils.util import mkpath, newer, make_file, copy_file @@ -22,15 +25,17 @@ class BuildPy (Command): self.dir = None self.modules = None self.package = None + self.package_dir = None def set_final_options (self): self.set_undefined_options ('build', ('libdir', 'dir')) - # 'package' is an alias option in Distribution (hmmm, we - # really should change to "pull" options from Distribution - # rather than "pushing" them out to commands...) - if self.package is None: - self.package = '' + + # Get the distribution options that are aliases for build_py + # options -- list of packages and list of modules. + self.packages = self.distribution.packages + self.modules = self.distribution.py_modules + self.package_dir = self.distribution.package_dir def run (self): @@ -54,40 +59,196 @@ class BuildPy (Command): outfiles = [] missing = [] - # Loop over the list of "pure Python" modules, deriving - # input and output filenames and checking for missing - # input files. + # Two options control which modules will be installed: 'packages' + # and 'modules'. The former lets us work with whole packages, not + # specifying individual modules at all; the latter is for + # specifying modules one-at-a-time. Currently they are mutually + # exclusive: you can define one or the other (or neither), but not + # both. It remains to be seen how limiting this is. - # it's ok not to have *any* py files, right? - if not self.modules: + # Dispose of the two "unusual" cases first: no pure Python modules + # at all (no problem, just return silently), and over-specified + # 'packages' and 'modules' options. + + if not self.modules and not self.packages: return + if self.modules and self.packages: + raise DistutilsOptionError, \ + "build_py: supplying both 'packages' and 'modules' " + \ + "options not allowed" + + # Now we're down to two cases: 'modules' only and 'packages' only. + if self.modules: + self.build_modules () + else: + self.build_packages () + + + # run () - # XXX we should allow for wildcards, so eg. the Distutils setup.py - # file would just have to say - # py_modules = ['distutils.*', 'distutils.command.*'] - # without having to list each one explicitly. - for m in self.modules: - fn = apply (os.path.join, tuple (string.split (m, '.'))) + '.py' - if not os.path.exists (fn): - missing.append (fn) + + def get_package_dir (self, package): + """Return the directory, relative to the top of the source + distribution, where package 'package' should be found + (at least according to the 'package_dir' option, if any).""" + + if type (package) is StringType: + path = string.split (package, '.') + elif type (package) in (TupleType, ListType): + path = list (path) + else: + raise TypeError, "'package' must be a string, list, or tuple" + + if not self.package_dir: + return apply (os.path.join, path) + else: + tail = [] + while path: + try: + pdir = self.package_dir[string.join (path, '.')] + except KeyError: + tail.insert (0, path[-1]) + del path[-1] + else: + tail.insert (0, pdir) + return apply (os.path.join, tail) else: - infiles.append (fn) - outfiles.append (os.path.join (self.dir, self.package, fn)) - - # Blow up if any input files were not found. - if missing: - raise DistutilsFileError, \ - "missing files: " + string.join (missing, ' ') - - # Loop over the list of input files, copying them to their - # temporary (build) destination. - created = {} - for i in range (len (infiles)): - outdir = os.path.split (outfiles[i])[0] - if not created.get(outdir): - self.mkpath (outdir) - created[outdir] = 1 - - self.copy_file (infiles[i], outfiles[i]) + # arg! everything failed, we might as well have not even + # looked in package_dir -- oh well + return apply (os.path.join, tail) + + # get_package_dir () + + + def check_package (self, package, package_dir): + + # Empty dir name means current directory, which we can probably + # assume exists. Also, os.path.exists and isdir don't know about + # my "empty string means current dir" convention, so we have to + # circumvent them. + if package_dir != "": + if not os.path.exists (package_dir): + raise DistutilsFileError, \ + "package directory '%s' does not exist" % package_dir + if not os.path.isdir (package_dir): + raise DistutilsFileErorr, \ + ("supposed package directory '%s' exists, " + + "but is not a directory") % package_dir + + # Require __init__.py for all but the "root package" + if package != "": + init_py = os.path.join (package_dir, "__init__.py") + if not os.path.isfile (init_py): + self.warn (("package init file '%s' not found " + + "(or not a regular file)") % init_py) + # check_package () + + + def check_module (self, module, module_file): + if not os.path.isfile (module_file): + self.warn ("file %s (for module %s) not found" % + module_file, module) + return 0 + else: + return 1 + + # check_module () + + + def find_modules (self, package, package_dir): + module_files = glob (os.path.join (package_dir, "*.py")) + module_pairs = [] + for f in module_files: + module = os.path.splitext (os.path.basename (f))[0] + module_pairs.append (module, f) + return module_pairs + + + def build_module (self, module, module_file, package): + + if type (package) is StringType: + package = string.split (package, '.') + + # Now put the module source file into the "build" area -- this + # is easy, we just copy it somewhere under self.dir (the build + # directory for Python source). + outfile_path = package + outfile_path.append (module + ".py") + outfile_path.insert (0, self.dir) + outfile = apply (os.path.join, outfile_path) + + dir = os.path.dirname (outfile) + self.mkpath (dir) + self.copy_file (module_file, outfile) + + + def build_modules (self): + + # Map package names to tuples of useful info about the package: + # (package_dir, checked) + # package_dir - the directory where we'll find source files for + # this package + # checked - true if we have checked that the package directory + # is valid (exists, contains __init__.py, ... ?) + + + packages = {} + + # We treat modules-in-packages almost the same as toplevel modules, + # just the "package" for a toplevel is empty (either an empty + # string or empty list, depending on context). Differences: + # - don't check for __init__.py in directory for empty package + + for module in self.modules: + path = string.split (module, '.') + package = tuple (path[0:-1]) + module = path[-1] + + try: + (package_dir, checked) = packages[package] + except KeyError: + package_dir = self.get_package_dir (package) + checked = 0 + + if not checked: + self.check_package (package, package_dir) + packages[package] = (package_dir, 1) + + # XXX perhaps we should also check for just .pyc files + # (so greedy closed-source bastards can distribute Python + # modules too) + module_file = os.path.join (package_dir, module + ".py") + if not self.check_module (module, module_file): + continue + + # Now "build" the module -- ie. copy the source file to + # self.dir (the build directory for Python source). (Actually, + # it gets copied to the directory for this package under + # self.dir.) + self.build_module (module, module_file, package) + + # build_modules () + + + def build_packages (self): + + for package in self.packages: + package_dir = self.get_package_dir (package) + self.check_package (package, package_dir) + + # Get list of (module, module_file) tuples based on scanning + # the package directory. Here, 'module' is the *unqualified* + # module name (ie. no dots, no package -- we already know its + # package!), and module_file is the path to the .py file, + # relative to the current directory (ie. including + # 'package_dir'). + modules = self.find_modules (package, package_dir) + + # Now loop over the modules we found, "building" each one (just + # copy it to self.dir). + for (module, module_file) in modules: + self.build_module (module, module_file, package) + + # build_packages () # end class BuildPy -- cgit v1.2.1 From 27fd97e6fa9b27d71a675771e3921954fe4f1ae8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:27:12 +0000 Subject: Some option changes: - rename 'dir' to 'build_dir' - take 'package' from distribution option 'ext_package' - take 'extensions' from distribution option 'ext_modules' - take 'include_dirs' from distribution Name keyword args explictly when calling CCompiler methods. Overhauled how we generate extension filenames (in 'extension_filename() and 'build_extension()') to take 'package' option into account. --- command/build_ext.py | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 29915812..a0ab61b3 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -16,9 +16,10 @@ from distutils.sysconfig import INCLUDEPY, SO, exec_prefix from distutils.errors import * -# This is the same as a Python NAME, since we must accept any -# valid module name for the extension name. -extension_name_re = re.compile (r'^[a-zA-Z_][a-zA-Z_0-9]*$') +# An extension name is just a dot-separated list of Python NAMEs (ie. +# the same as a fully-qualified module name). +extension_name_re = re.compile \ + (r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$') class BuildExt (Command): @@ -41,7 +42,7 @@ class BuildExt (Command): # takes care of both command-line and client options # in between set_default_options() and set_final_options()) - options = [('dir=', 'd', + options = [('build-dir=', 'd', "directory for compiled extension modules"), ('include-dirs=', 'I', "list of directories to search for header files"), @@ -57,12 +58,14 @@ class BuildExt (Command): "directories to search for shared C libraries at runtime"), ('link-objects=', 'O', "extra explicit link objects to include in the link"), + ('package=', 'p', + "Python package to put extension modules into"), ] def set_default_options (self): self.extensions = None - self.dir = None + self.build_dir = None self.package = None self.include_dirs = None @@ -74,10 +77,13 @@ class BuildExt (Command): self.link_objects = None def set_final_options (self): - self.set_undefined_options ('build', ('platdir', 'dir')) + self.set_undefined_options ('build', ('platdir', 'build_dir')) if self.package is None: - self.package = '' + self.package = self.distribution.ext_package + + self.extensions = self.distribution.ext_modules + # Make sure Python's include directories (for Python.h, config.h, # etc.) are in the include search path. We have to roll our own @@ -87,7 +93,7 @@ class BuildExt (Command): exec_py_include = os.path.join (exec_prefix, 'include', 'python' + sys.version[0:3]) if self.include_dirs is None: - self.include_dirs = [] + self.include_dirs = self.distribution.include_dirs or [] self.include_dirs.insert (0, py_include) if exec_py_include != py_include: self.include_dirs.insert (0, exec_py_include) @@ -177,7 +183,9 @@ class BuildExt (Command): macros = build_info.get ('macros') include_dirs = build_info.get ('include_dirs') - self.compiler.compile (sources, macros, include_dirs) + self.compiler.compile (sources, + macros=macros, + includes=include_dirs) objects = self.compiler.object_filenames (sources) extra_objects = build_info.get ('extra_objects') @@ -185,18 +193,23 @@ class BuildExt (Command): objects.extend (extra_objects) libraries = build_info.get ('libraries') library_dirs = build_info.get ('library_dirs') - ext_filename = self.extension_filename (extension_name) - dest = os.path.dirname ( - os.path.join (self.dir, self.package, ext_filename)) - self.mkpath (dest) - self.compiler.link_shared_object (objects, ext_filename, dest, - libraries, library_dirs, + ext_filename = self.extension_filename \ + (extension_name, self.package) + ext_filename = os.path.join (self.build_dir, ext_filename) + dest_dir = os.path.dirname (ext_filename) + self.mkpath (dest_dir) + self.compiler.link_shared_object (objects, ext_filename, + libraries=libraries, + library_dirs=library_dirs, build_info=build_info) # XXX hack! # build_extensions () - def extension_filename (self, ext_name): - return ext_name + SO + def extension_filename (self, ext_name, package=None): + if package: + ext_name = package + '.' + ext_name + ext_path = string.split (ext_name, '.') + return apply (os.path.join, ext_path) + SO # class BuildExt -- cgit v1.2.1 From 6b9caf68d52702013783cd2225e4ad562c26e14b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:27:55 +0000 Subject: Only run build_py if we have pure Python modules, and build_ext if we have extension modules. --- command/build.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/command/build.py b/command/build.py index 187dddc0..b74e51cf 100644 --- a/command/build.py +++ b/command/build.py @@ -40,10 +40,14 @@ class Build (Command): # For now, "build" means "build_py" then "build_ext". (Eventually # it should also build documentation.) - # Invoke the 'build_py' command - self.run_peer ('build_py') - - # And now 'build_ext' - self.run_peer ('build_ext') + # Invoke the 'build_py' command to "build" pure Python modules + # (ie. copy 'em into the build tree) + if self.distribution.packages or self.distribution.py_modules: + self.run_peer ('build_py') + + # And now 'build_ext' -- compile extension modules and put them + # into the build tree + if self.distribution.ext_modules: + self.run_peer ('build_ext') # end class Build -- cgit v1.2.1 From 0fd123490a06bd8a56edc91e707293b3b37c3514 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:31:14 +0000 Subject: Added 'install_path' option for giving non-packagized module distributions their own directory (and .pth file). Overhauled how we determine installation directories in 'set_final_options()' to separate platform-dependence and take 'install_path' option into account. Added 'create_path_file()' to create path config file when 'install_path' given. Only run 'install_py' and 'install_ext' when, respectively, there are some pure Python modules and some extension modules in the distribution. --- command/install.py | 94 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 12 deletions(-) diff --git a/command/install.py b/command/install.py index 6fb85361..0e4ad3f0 100644 --- a/command/install.py +++ b/command/install.py @@ -7,8 +7,10 @@ Implements the Distutils 'install' command.""" __rcsid__ = "$Id$" import sys, os, string +from types import * from distutils import sysconfig from distutils.core import Command +from distutils.util import write_file class Install (Command): @@ -36,6 +38,8 @@ class Install (Command): "platform-specific site directory"), ('install-scheme=', None, "install to 'system' or 'site' library directory?"), + ('install-path=', None, + "extra intervening directories to put below install-lib"), # Where to install documentation (eventually!) ('doc-format=', None, "format of documentation to generate"), @@ -73,6 +77,7 @@ class Install (Command): self.install_platlib = None self.install_site_lib = None self.install_site_platlib = None + self.install_path = None self.install_man = None self.install_html = None @@ -155,19 +160,65 @@ class Install (Command): # really matter *which* one, of course, but I'll observe decorum # and do it properly.) - if self.install_site_lib is None: - if os.name == 'posix': - self.install_site_lib = \ - os.path.join (self.install_lib, 'site-packages') + # 'base' and 'platbase' are the base directories for installing + # site-local files, eg. "/usr/local/lib/python1.5/site-packages" + # or "C:\Program Files\Python" + if os.name == 'posix': + self.base = os.path.join (self.install_lib, + 'site-packages') + self.platbase = os.path.join (self.install_platlib, + 'site-packages') + else: + self.base = self.prefix + self.platbase = self.exec_prefix + + # 'path_file' and 'extra_dirs' are how we handle distributions + # that need to be installed to their own directory, but aren't + # package-ized yet. 'extra_dirs' is just a directory under + # 'base' or 'platbase' where toplevel modules will actually be + # installed; 'path_file' is the basename of a .pth file to drop + # in 'base' or 'platbase' (depending on the distribution). Very + # often they will be the same, which is why we allow them to be + # supplied as a string or 1-tuple as well as a 2-element + # comma-separated string or a 2-tuple. + if self.install_path is None: + self.install_path = self.distribution.install_path + + if self.install_path is not None: + if type (self.install_path) is StringType: + self.install_path = string.split (self.install_path, ',') + + if len (self.install_path) == 1: + path_file = extra_dirs = self.install_path[0] + elif len (self.install_path) == 2: + (path_file, extra_dirs) = self.install_path else: - self.install_site_lib = self.prefix + raise DistutilsOptionError, \ + "'install_path' option must be a list, tuple, or " + \ + "comma-separated string with 1 or 2 elements" + + # install path has slashes in it -- might need to convert to + # local form + if string.find (extra_dirs, '/') and os.name != "posix": + extra_dirs = string.split (extra_dirs, '/') + extra_dirs = apply (os.path.join, extra_dirs) + else: + path_file = None + extra_dirs = '' + + # XXX should we warn if path_file and not extra_dirs (in which case + # the path file would be harmless but pointless) + self.path_file = path_file + self.extra_dirs = extra_dirs + + + if self.install_site_lib is None: + self.install_site_lib = os.path.join (self.base, + extra_dirs) if self.install_site_platlib is None: - if os.name == 'posix': - self.install_site_platlib = \ - os.path.join (self.install_platlib, 'site-packages') - else: - self.install_site_platlib = self.exec_prefix + self.install_site_platlib = os.path.join (self.platbase, + extra_dirs) #if self.install_scheme == 'site': # install_lib = self.install_site_lib @@ -234,9 +285,28 @@ class Install (Command): # extensions). Note that 'install_py' is smart enough to install # pure Python modules in the "platlib" directory if we built any # extensions. - self.run_peer ('install_py') - self.run_peer ('install_ext') + if self.distribution.packages or self.distribution.py_modules: + self.run_peer ('install_py') + if self.distribution.ext_modules: + self.run_peer ('install_ext') + + if self.path_file: + self.create_path_file () # run () + + def create_path_file (self): + + if self.distribution.ext_modules: + base = self.platbase + else: + base = self.base + + filename = os.path.join (base, self.path_file + ".pth") + self.execute (write_file, + (filename, [self.extra_dirs]), + "creating %s" % filename) + + # class Install -- cgit v1.2.1 From 36722463169bd14a78f8b0773ed38786fb73cd41 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:33:09 +0000 Subject: Added docstring, brought __all__ up-to-date. --- command/__init__.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/command/__init__.py b/command/__init__.py index 9567fd13..9a5aef20 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -1,6 +1,23 @@ -# this is solely for debugging convenience +"""distutils.command + +Package containing implementation of all the standard Distutils +commands. Currently this means: + + build + build_py + build_ext + install + install_py + install_ext + +but this list will undoubtedly grow with time.""" + +__rcsid__ = "$Id$" __all__ = ['build', 'build_py', - 'make_blib', + 'build_ext', + 'install', + 'install_py', + 'install_ext', ] -- cgit v1.2.1 From 79d143e97dffc76896eb203f2c0303337c051466 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:35:09 +0000 Subject: Typecheck elements of 'macros' parameter in 'gen_preprocess_options(). --- ccompiler.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ccompiler.py b/ccompiler.py index 0e505338..2a80739c 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -450,6 +450,14 @@ def gen_preprocess_options (macros, includes): pp_opts = [] for macro in macros: + + if not (type (macro) is TupleType and + 1 <= len (macro) <= 2): + raise TypeError, \ + ("bad macro definition '%s': " + + "each element of 'macros' list must be a 1- or 2-tuple") % \ + macro + if len (macro) == 1: # undefine this macro pp_opts.append ("-U%s" % macro[0]) elif len (macro) == 2: -- cgit v1.2.1 From b2bb06683462c1b7e9df90413d5af326002d93f3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:36:15 +0000 Subject: In 'link_shared_object()', try to be less sensitive to missing input files in dry-run mode. --- unixccompiler.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 01493131..02fbc66a 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -202,7 +202,17 @@ class UnixCCompiler (CCompiler): # If any of the input object files are newer than the output shared # object, relink. Again, this is a simplistic dependency check: # doesn't look at any of the libraries we might be linking with. - if newer_group (objects, output_filename): + # Note that we have to dance around errors comparing timestamps if + # we're in dry-run mode (yuck). + try: + newer = newer_group (objects, output_filename) + except OSError: + if self.dry_run: + newer = 1 + else: + raise + + if newer: ld_args = self.ldflags_shared + lib_opts + \ objects + ['-o', output_filename] -- cgit v1.2.1 From 602f5dabbeffa74e600e9fca91ef7295521affce Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:37:51 +0000 Subject: Added 'write_file()' function. Added global cache PATH_CREATED used by 'mkpath()' to ensure it doesn't try to create the same path more than once in a session (and, more importantly, to ensure that it doesn't print "creating X" more than once for each X per session!). --- util.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/util.py b/util.py index bb790af4..85b04e7a 100644 --- a/util.py +++ b/util.py @@ -15,6 +15,10 @@ import os from distutils.errors import * +# cache for by mkpath() -- in addition to cheapening redundant calls, +# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode +PATH_CREATED = {} + # I don't use os.makedirs because a) it's new to Python 1.5.2, and # b) it blows up if the directory already exists (I want to silently # succeed in that case). @@ -26,12 +30,17 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): directory). If 'verbose' is true, print a one-line summary of each mkdir to stdout.""" + global PATH_CREATED + # XXX what's the better way to handle verbosity? print as we create # each directory in the path (the current behaviour), or only announce - # the creation of the whole path, and force verbose=0 on all sub-calls? + # the creation of the whole path? (quite easy to do the latter since + # we're not using a recursive algorithm) if os.path.isdir (name): return + if PATH_CREATED.get (name): + return (head, tail) = os.path.split (name) tails = [tail] # stack of lone dirs to create @@ -59,6 +68,8 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): except os.error, (errno, errstr): raise DistutilsFileError, "%s: %s" % (head, errstr) + PATH_CREATED[head] = 1 + # mkpath () @@ -394,3 +405,13 @@ def move_file (src, dst, return dst # move_file () + + +def write_file (filename, contents): + """Create a file with the specified naem and write 'contents' (a + sequence of strings without line terminators) to it.""" + + f = open (filename, "w") + for line in contents: + f.write (line + "\n") + f.close () -- cgit v1.2.1 From 5c182c049b03573ef283d05d1079319d201c669d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:41:36 +0000 Subject: Ditched the whole notion of "alias options": this meant dropping the 'alias_options' table and getting rid of some hairy code in the Distribution constructor. Resurrected the distribution options that describe the modules present in the module distribution ('py_modules', 'ext_modules'), and added a bunch more: 'packages', 'package_dir', 'ext_package', 'include_dirs', 'install_path'. Updated some comments. Added 'warn()' method to Command. 'Command.get_command_name()' now stores generated command name in self.command_name. --- core.py | 60 +++++++++++++++++++++--------------------------------------- 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/core.py b/core.py index a6c378d3..e5339213 100644 --- a/core.py +++ b/core.py @@ -114,19 +114,6 @@ class Distribution: ('dry-run', 'n', "don't actually do anything"), ] - # 'alias_options' map distribution options to command options -- the - # idea is that the most common, essential options can be directly - # specified as Distribution attributes, and the rest can go in the - # 'options' dictionary. These aliases are for those common, essential - # options. - alias_options = { 'py_modules': ('build_py', 'modules'), - 'ext_modules': ('build_ext', 'extensions'), - 'package': [('build_py', 'package',), - ('build_ext', 'package')], - 'include_dirs': ('build_ext', 'include_dirs'), - - } - # -- Creation/initialization methods ------------------------------- @@ -151,6 +138,7 @@ class Distribution: self.name = None self.version = None self.author = None + self.author_email = None self.url = None self.licence = None self.description = None @@ -165,10 +153,13 @@ class Distribution: # than of the Distribution itself. We provide aliases for them in # Distribution as a convenience to the developer. # dictionary. - # XXX not needed anymore! (I think...) - #self.py_modules = None - #self.ext_modules = None - #self.package = None + self.packages = None + self.package_dir = None + self.py_modules = None + self.ext_modules = None + self.ext_package = None + self.include_dirs = None + self.install_path = None # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to @@ -188,8 +179,9 @@ class Distribution: # '.get()' rather than a straight lookup. self.have_run = {} - # Now we'll use the attrs dictionary (from the client) to possibly - # override any or all of these distribution options + # Now we'll use the attrs dictionary (ultimately, keyword args from + # the client) to possibly override any or all of these distribution + # options. if attrs: # Pull out the set of command options and work on them @@ -206,26 +198,10 @@ class Distribution: # loop over commands # if any command options - # Now work on the rest of the attributes. Note that some of - # these may be aliases for command options, so we might go - # through some of the above again. + # Now work on the rest of the attributes. Any attribute that's + # not already defined is invalid! for (key,val) in attrs.items(): - alias = self.alias_options.get (key) - if alias: - if type (alias) is ListType: - for (command, cmd_option) in alias: - cmd_obj = self.find_command_obj (command) - cmd_obj.set_option (cmd_option, val) - elif type (alias) is TupleType: - (command, cmd_option) = alias - cmd_obj = self.find_command_obj (command) - cmd_obj.set_option (cmd_option, val) - else: - raise RuntimeError, \ - ("oops! bad alias option for '%s': " + - "must be tuple or list of tuples") % key - - elif hasattr (self, key): + if hasattr (self, key): setattr (self, key, val) else: raise DistutilsOptionError, \ @@ -653,7 +629,8 @@ class Command: # splits to ['', 'Foo', '', 'Bar', '', 'Baz', '']. Hence # the 'filter' to strip out the empties. words = filter (None, re.split (r'([A-Z][a-z]+)', class_name)) - return string.join (map (string.lower, words), "_") + self.command_name = string.join (map (string.lower, words), "_") + return self.command_name def set_undefined_options (self, src_cmd, *option_pairs): @@ -717,6 +694,11 @@ class Command: # -- External world manipulation ----------------------------------- + def warn (self, msg): + sys.stderr.write ("warning: %s: %s\n" % + (self.get_command_name(), msg)) + + def execute (self, func, args, msg=None, level=1): """Perform some action that affects the outside world (eg. by writing to the filesystem). Such actions are special because -- cgit v1.2.1 From 366fb4718e7bad951328391a0439b6d07852b8fe Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Sep 1999 15:24:04 +0000 Subject: Added docstring and RCS id (apparently some Windows tar extractors ignore zero-byte files: grr...). --- __init__.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/__init__.py b/__init__.py index e69de29b..18deaadf 100644 --- a/__init__.py +++ b/__init__.py @@ -0,0 +1,11 @@ +"""distutils + +The main package for the Python Module Distribtion Utilities. Normally +used from a setup script as + + from distutils.core import setup + + setup (...) +""" + +__rcsid__ = "$Id$" -- cgit v1.2.1 From d2ce9d4fb72071aabbe879af96eb405739ee1dee Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:12:19 +0000 Subject: Added 'ready' flag and 'ensure_ready()' method to Command: together they make sure that 'set_final_options()' has been called, but isn't called redundantly. Changed Distribution to call 'ensure_ready()' where it used to call 'set_final_options()', and in a few extra places as well. Lots of comment/docstring revisions and additions in both classes. New one-liner utility methods in Command: 'find_peer()', 'spawn()'. --- core.py | 81 ++++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/core.py b/core.py index e5339213..f0c97aa3 100644 --- a/core.py +++ b/core.py @@ -416,30 +416,25 @@ class Distribution: Then invoke 'run()' on that command object (or an existing one).""" - # XXX currently, this is the only place where we invoke a - # command object's 'run()' method -- so it might make sense to - # put the 'set_final_options()' call here, too, instead of - # requiring every command's 'run()' to call it first. - # Already been here, done that? then return silently. if self.have_run.get (command): return self.announce ("running " + command) cmd_obj = self.find_command_obj (command) + cmd_obj.ensure_ready () cmd_obj.run () self.have_run[command] = 1 def get_command_option (self, command, option): - """Create a command object for 'command' if necessary, finalize - its option values by invoking its 'set_final_options()' - method, and return the value of its 'option' option. Raise - DistutilsOptionError if 'option' is not known for - that 'command'.""" + """Create a command object for 'command' if necessary, ensure that + its option values are all set to their final values, and return + the value of its 'option' option. Raise DistutilsOptionError if + 'option' is not known for that 'command'.""" cmd_obj = self.find_command_obj (command) - cmd_obj.set_final_options () + cmd_obj.ensure_ready () return cmd_obj.get_option (option) try: return getattr (cmd_obj, option) @@ -449,14 +444,14 @@ class Distribution: def get_command_options (self, command, *options): - """Create a command object for 'command' if necessary, finalize - its option values by invoking its 'set_final_options()' - method, and return the values of all the options listed in - 'options' for that command. Raise DistutilsOptionError if - 'option' is not known for that 'command'.""" + """Create a command object for 'command' if necessary, ensure that + its option values are all set to their final values, and return + a tuple containing the values of all the options listed in + 'options' for that command. Raise DistutilsOptionError if any + invalid option is supplied in 'options'.""" cmd_obj = self.find_command_obj (command) - cmd_obj.set_final_options () + cmd_obj.ensure_ready () values = [] try: for opt in options: @@ -474,14 +469,14 @@ class Command: """Abstract base class for defining command classes, the "worker bees" of the Distutils. A useful analogy for command classes is to think of them as subroutines with local variables called - "options". The options are "declared" in 'set_initial_options()' + "options". The options are "declared" in 'set_default_options()' and "initialized" (given their real values) in 'set_final_options()', both of which must be defined by every command class. The distinction between the two is necessary because option values might come from the outside world (command line, option file, ...), and any options dependent on other options must be computed *after* these outside influences have - been processed -- hence 'set_final_values()'. The "body" of the + been processed -- hence 'set_final_options()'. The "body" of the subroutine, where it does all its work based on the values of its options, is the 'run()' method, which must also be implemented by every command class.""" @@ -502,8 +497,21 @@ class Command: self.distribution = dist self.set_default_options () + # 'ready' records whether or not 'set_final_options()' has been + # called. 'set_final_options()' itself should not pay attention to + # this flag: it is the business of 'ensure_ready()', which always + # calls 'set_final_options()', to respect/update it. + self.ready = 0 + # end __init__ () + + def ensure_ready (self): + if not self.ready: + self.set_final_options () + self.ready = 1 + + # Subclasses must define: # set_default_options() # provide default values for all options; may be overridden @@ -664,22 +672,32 @@ class Command: def set_peer_option (self, command, option, value): """Attempt to simulate a command-line override of some option - value in another command. Creates a command object for - 'command' if necessary, sets 'option' to 'value', and invokes - 'set_final_options()' on that command object. This will only - have the desired effect if the command object for 'command' - has not previously been created. Generally this is used to - ensure that the options in 'command' dependent on 'option' - are computed, hopefully (but not necessarily) deriving from - 'value'. It might be more accurate to call this method - 'influence_dependent_peer_options()'.""" + value in another command. Finds the command object for + 'command', sets its 'option' to 'value', and unconditionally + calls 'set_final_options()' on it: this means that some command + objects may have 'set_final_options()' invoked more than once. + Even so, this is not entirely reliable: the other command may + already be initialized to its satisfaction, in which case the + second 'set_final_options()' invocation will have little or no + effect.""" cmd_obj = self.distribution.find_command_obj (command) cmd_obj.set_option (option, value) cmd_obj.set_final_options () + def find_peer (self, command, create=1): + """Wrapper around Distribution's 'find_command_obj()' method: + find (create if necessary and 'create' is true) the command + object for 'command'..""" + + return self.distribution.find_command_obj (command, create) + + def get_peer_option (self, command, option): + """Find or create the command object for 'command', and return + its 'option' option.""" + cmd_obj = self.distribution.find_command_obj (command) return cmd_obj.get_option (option) @@ -762,6 +780,13 @@ class Command: self.distribution.dry_run) + def spawn (self, cmd, search_path=1, level=1): + from distutils.spawn import spawn + spawn (cmd, search_path, + self.distribution.verbose >= level, + self.distribution.dry_run) + + def make_file (self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): -- cgit v1.2.1 From 9a476d732350a106b3dc6ac876320a51a5d22053 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:14:16 +0000 Subject: More tweaks to 'mkpath()': - deal with empty tail from os.path.split() (eg. from trailing slash, or backslash, or whatever) - check PATH_CREATED hash inside loop as well --- util.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util.py b/util.py index 85b04e7a..aee95d36 100644 --- a/util.py +++ b/util.py @@ -43,6 +43,8 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): return (head, tail) = os.path.split (name) + if not tail: # in case 'name' has trailing slash + (head, tail) = os.path.split (head) tails = [tail] # stack of lone dirs to create while head and tail and not os.path.isdir (head): @@ -59,6 +61,9 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): for d in tails: #print "head = %s, d = %s: " % (head, d), head = os.path.join (head, d) + if PATH_CREATED.get (head): + continue + if verbose: print "creating", head -- cgit v1.2.1 From e748eefe84902c7daee5e2bc2e34400b972fe08e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:20:55 +0000 Subject: Added 'extra_preargs' and 'extra_postargs' parameters to most methods, which allowed us to get rid of the 'build_info' used in some places (a temporary kludge to support MSVC++ "def" files). Deleted big comment whining about that kludge. Added 'compiler_type' class attribute. Overhauled 'new_compiler()': now takes 'compiler' argument along with 'plat' (both optional with sensible defaults), and looks them both up in the new 'default_compiler' and 'compiler_class' dictionaries to figure out where to get the concrete compiler class from. Reordered arguments to 'gen_lib_options()' to match the order in which the arguments are generated (ie. -L before -l). --- ccompiler.py | 141 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 94 insertions(+), 47 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 2a80739c..bfbb50fa 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -7,7 +7,7 @@ for the Distutils compiler abstraction model.""" __rcsid__ = "$Id$" -import os +import sys, os from types import * from copy import copy from distutils.errors import * @@ -30,6 +30,15 @@ class CCompiler: most (all?) of those attributes may be varied on a per-compilation or per-link basis.""" + # 'compiler_type' is a class attribute that identifies this class. It + # keeps code that wants to know what kind of compiler it's dealing with + # from having to import all possible compiler classes just to do an + # 'isinstance'. In concrete CCompiler subclasses, 'compiler_type' + # should really, really be one of the keys of the 'compiler_class' + # dictionary (see below -- used by the 'new_compiler()' factory + # function) -- authors of new compiler interface classes are + # responsible for updating 'compiler_class'! + compiler_type = None # XXX things not handled by this compiler abstraction model: # * client can't provide additional options for a compiler, @@ -251,7 +260,9 @@ class CCompiler: sources, output_dir=None, macros=None, - includes=None): + includes=None, + extra_preargs=None, + extra_postargs=None): """Compile one or more C/C++ source files. 'sources' must be a list of strings, each one the name of a C/C++ source file. Return a list of the object filenames generated @@ -266,7 +277,16 @@ class CCompiler: 'includes', if given, must be a list of strings, the directories to add to the default include file search path for this - compilation only.""" + compilation only. + + 'extra_preargs' and 'extra_postargs' are optional lists of extra + command-line arguments that will be, respectively, prepended or + appended to the generated command line immediately before + execution. These will most likely be peculiar to the particular + platform and compiler being worked with, but are a necessary + escape hatch for those occasions when the abstract compiler + framework doesn't cut the mustard.""" + pass @@ -278,7 +298,9 @@ class CCompiler: output_libname, output_dir=None, libraries=None, - library_dirs=None): + library_dirs=None, + extra_preargs=None, + extra_postargs=None): """Link a bunch of stuff together to create a static library file. The "bunch of stuff" consists of the list of object files supplied as 'objects', the extra object files supplied @@ -292,42 +314,23 @@ class CCompiler: 'library_dirs', if supplied, should be a list of additional directories to search on top of the system default and those - supplied to 'add_library_dir()' and/or 'set_library_dirs()'.""" + supplied to 'add_library_dir()' and/or 'set_library_dirs()'. + + 'extra_preargs' and 'extra_postargs' are as for 'compile()' + (except of course that they supply command-line arguments + for the particular linker being used).""" pass - # XXX passing in 'build_info' here is a kludge to deal with the - # oddities of one particular compiler (Visual C++). For some reason, - # it needs to be told about ".def" files, and currently the - # 'build_info' hash allows this through a 'def_file' element. The link - # methods for VC++ look for 'def_file' and transform it into the - # appropriate command-line options. The current code is objectionable - # for a number of reasons: 1) if the link methods take 'build_info', - # why bother passing in libraries, library_dirs, etc.? 2) if the link - # methods do it, why not the compile methods? 3) build_info is part of - # the interface between setup.py and the 'build_ext' command -- it - # should stop there and not be propagated down into the compiler - # classes! and 4) I don't like elevating a platform- and - # compiler-specific oddity to "first-class" status in 'build_info' (oh - # well, at least it's not being reified in the compiler classes -- that - # would be really gross). - # - # Possible solutions: - # - just pass build_info to all the compile/link methods, - # never mind all those other parameters and screw the - # integrity of the interfaces - # - add a mechanism for passing platform-specific and/or - # compiler-specific compiler/linker options from setup.py - # straight through to the appropriate compiler class - def link_shared_lib (self, objects, output_libname, output_dir=None, libraries=None, library_dirs=None, - build_info=None): + extra_preargs=None, + extra_postargs=None): """Link a bunch of stuff together to create a shared library file. Has the same effect as 'link_static_lib()' except that the filename inferred from 'output_libname' will most @@ -335,18 +338,20 @@ class CCompiler: almost certainly be different.""" pass + def link_shared_object (self, objects, output_filename, output_dir=None, libraries=None, library_dirs=None, - build_info=None): + extra_preargs=None, + extra_postargs=None): """Link a bunch of stuff together to create a shared object file. Much like 'link_shared_lib()', except the output filename is explicitly supplied as 'output_filename'. If 'output_dir' is supplied, 'output_filename' is relative to it - (i.e. 'output_filename' can provide directoriy components if + (i.e. 'output_filename' can provide directory components if needed).""" pass @@ -407,23 +412,65 @@ class CCompiler: # class CCompiler +# Map a platform ('posix', 'nt') to the default compiler type for +# that platform. +default_compiler = { 'posix': 'unix', + 'nt': 'msvc', + } + +# Map compiler types to (module_name, class_name) pairs -- ie. where to +# find the code that implements an interface to this compiler. (The module +# is assumed to be in the 'distutils' package.) +compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler'), + 'msvc': ('msvccompiler', 'MSVCCompiler'), + } + + def new_compiler (plat=None, + compiler=None, verbose=0, dry_run=0): - """Generate a CCompiler instance for platform 'plat' (or the - current platform, if 'plat' not supplied). Really instantiates - some concrete subclass of CCompiler, of course.""" - - if plat is None: plat = os.name - if plat == 'posix': - from unixccompiler import UnixCCompiler - return UnixCCompiler (verbose, dry_run) - elif plat == 'nt': - from msvccompiler import MSVCCompiler - return MSVCCompiler (verbose, dry_run) - else: - raise DistutilsPlatformError, \ - "don't know how to compile C/C++ code on platform %s" % plat + + """Generate an instance of some CCompiler subclass for the supplied + platform/compiler combination. 'plat' defaults to 'os.name' + (eg. 'posix', 'nt'), and 'compiler' defaults to the default + compiler for that platform. Currently only 'posix' and 'nt' + are supported, and the default compilers are "traditional Unix + interface" (UnixCCompiler class) and Visual C++ (MSVCCompiler + class). Note that it's perfectly possible to ask for a Unix + compiler object under Windows, and a Microsoft compiler object + under Unix -- if you supply a value for 'compiler', 'plat' + is ignored.""" + + if plat is None: + plat = os.name + + try: + if compiler is None: + compiler = default_compiler[plat] + + (module_name, class_name) = compiler_class[compiler] + except KeyError: + msg = "don't know how to compile C/C++ code on platform '%s'" % plat + if compiler is not None: + msg = msg + " with '%s' compiler" % compiler + raise DistutilsPlatformError, msg + + try: + module_name = "distutils." + module_name + __import__ (module_name) + module = sys.modules[module_name] + klass = vars(module)[class_name] + except ImportError: + raise DistutilsModuleError, \ + "can't compile C/C++ code: unable to load module '%s'" % \ + module_name + except KeyError: + raise DistutilsModuleError, \ + ("can't compile C/C++ code: unable to find class '%s' " + + "in module '%s'") % (class_name, module_name) + + return klass (verbose, dry_run) def gen_preprocess_options (macros, includes): @@ -477,7 +524,7 @@ def gen_preprocess_options (macros, includes): # gen_preprocess_options () -def gen_lib_options (libraries, library_dirs, lib_format, dir_format): +def gen_lib_options (library_dirs, libraries, dir_format, lib_format): """Generate linker options for searching library directories and linking with specific libraries. 'libraries' and 'library_dirs' are, respectively, lists of library names (not filenames!) and -- cgit v1.2.1 From 5a10a6e29b3f8bab23620ad6385efb0d7f5b4486 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:22:50 +0000 Subject: Catch up with latest changes in CCompiler: - add 'extra_preargs' and 'extra_postargs' parameters (and use them!) - added 'compiler_type' class attribute - respect reordered arguments to 'gen_lib_options()' --- unixccompiler.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 02fbc66a..f3d9591b 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -53,6 +53,8 @@ class UnixCCompiler (CCompiler): # are specified via {add,set}_include_dirs(), and there's no way to # distinguish them. This might be a bug. + compiler_type = 'unix' + _obj_ext = '.o' _exe_ext = '' _shared_lib_ext = SO @@ -89,7 +91,9 @@ class UnixCCompiler (CCompiler): sources, output_dir=None, macros=None, - includes=None): + includes=None, + extra_preargs=None, + extra_postargs=None): if output_dir is None: output_dir = self.output_dir @@ -130,6 +134,10 @@ class UnixCCompiler (CCompiler): cc_args = ['-c'] + pp_opts + \ self.ccflags + self.ccflags_shared + \ sources + if extra_preargs: + cc_args[:0] = extra_preargs + if extra_postargs: + cc_args.extend (extra_postargs) self.spawn ([self.cc] + cc_args) @@ -164,7 +172,8 @@ class UnixCCompiler (CCompiler): output_dir=None, libraries=None, library_dirs=None, - build_info=None): + extra_preargs=None, + extra_postargs=None): # XXX should we sanity check the library name? (eg. no # slashes) self.link_shared_object ( @@ -173,7 +182,8 @@ class UnixCCompiler (CCompiler): output_dir, libraries, library_dirs, - build_info) + extra_preargs, + extra_postargs) def link_shared_object (self, @@ -182,7 +192,8 @@ class UnixCCompiler (CCompiler): output_dir=None, libraries=None, library_dirs=None, - build_info=None): + extra_preargs=None, + extra_postargs=None): if output_dir is None: output_dir = self.output_dir @@ -190,12 +201,10 @@ class UnixCCompiler (CCompiler): libraries = [] if library_dirs is None: library_dirs = [] - if build_info is None: - build_info = {} - lib_opts = gen_lib_options (self.libraries + libraries, - self.library_dirs + library_dirs, - "-l%s", "-L%s") + lib_opts = gen_lib_options (self.library_dirs + library_dirs, + self.libraries + libraries, + "-L%s", "-l%s") if output_dir is not None: output_filename = os.path.join (output_dir, output_filename) @@ -215,7 +224,10 @@ class UnixCCompiler (CCompiler): if newer: ld_args = self.ldflags_shared + lib_opts + \ objects + ['-o', output_filename] - + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend (extra_postargs) self.spawn ([self.ld_shared] + ld_args) else: self.announce ("skipping %s (up-to-date)" % output_filename) -- cgit v1.2.1 From 746c9403521ce115a30d553275740a65af7efb63 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:29:10 +0000 Subject: Catch up with latest changes in CCompiler: - add 'extra_preargs' and 'extra_postargs' parameters (and use them!) - got rid of 'build_info' kludge parameter - added 'compiler_type' class attribute - respect reordered arguments to 'gen_lib_options()' Also added 'output_dir' parameter (catching up with older change in CCompiler) -- BUT this is presently ignored by all methods! Deleted some more docstrings redundant with CCompiler. Dropped generated of "/DEF:" argument --- that's now done by the 'build_ext' command. --- msvccompiler.py | 103 +++++++++++++++++++++++++------------------------------- 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index f328232b..64b27307 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -1,7 +1,7 @@ """distutils.ccompiler Contains MSVCCompiler, an implementation of the abstract CCompiler class -for the Microsoft Visual Studio """ +for the Microsoft Visual Studio.""" # created 1999/08/19, Perry Stoll @@ -19,6 +19,8 @@ class MSVCCompiler (CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, as defined by the CCompiler abstract class.""" + compiler_type = 'msvc' + def __init__ (self, verbose=0, dry_run=0): @@ -50,26 +52,19 @@ class MSVCCompiler (CCompiler) : _exe_ext = '.exe' _shared_lib_ext = '.dll' _static_lib_ext = '.lib' + + # XXX the 'output_dir' parameter is ignored by the methods in this + # class! I just put it in to be consistent with CCompiler and + # UnixCCompiler, but someone who actually knows Visual C++ will + # have to make it work... def compile (self, sources, + output_dir=None, macros=None, - includes=None): - """Compile one or more C/C++ source files. 'sources' must be - a list of strings, each one the name of a C/C++ source - file. Return a list of the object filenames generated - (one for each source filename in 'sources'). - - 'macros', if given, must be a list of macro definitions. A - macro definition is either a (name, value) 2-tuple or a (name,) - 1-tuple. The former defines a macro; if the value is None, the - macro is defined without an explicit value. The 1-tuple case - undefines a macro. Later definitions/redefinitions/ - undefinitions take precedence. - - 'includes', if given, must be a list of strings, the directories - to add to the default include file search path for this - compilation only.""" + includes=None, + extra_preargs=None, + extra_postargs=None): if macros is None: macros = [] @@ -95,11 +90,16 @@ class MSVCCompiler (CCompiler) : inputOpt = fileOpt + srcFile outputOpt = "/Fo" + objFile - pp_opts = base_pp_opts + [ outputOpt, inputOpt ] + cc_args = self.compile_options + \ + base_pp_opts + \ + [outputOpt, inputOpt] + if extra_preargs: + cc_args[:0] = extra_preargs + if extra_postargs: + cc_args.extend (extra_postargs) - self.spawn( [ self.cc ] + self.compile_options + pp_opts) + self.spawn ([self.cc] + cc_args) objectFiles.append( objFile ) - return objectFiles @@ -109,39 +109,27 @@ class MSVCCompiler (CCompiler) : def link_static_lib (self, objects, output_libname, + output_dir=None, libraries=None, - library_dirs=None): - """Link a bunch of stuff together to create a static library - file. The "bunch of stuff" consists of the list of object - files supplied as 'objects', the extra object files supplied - to 'add_link_object()' and/or 'set_link_objects()', the - libraries supplied to 'add_library()' and/or - 'set_libraries()', and the libraries supplied as 'libraries' - (if any). - - 'output_libname' should be a library name, not a filename; - the filename will be inferred from the library name. - - 'library_dirs', if supplied, should be a list of additional - directories to search on top of the system default and those - supplied to 'add_library_dir()' and/or 'set_library_dirs()'.""" - + library_dirs=None, + extra_preargs=None, + extra_postargs=None): + if libraries is None: libraries = [] if library_dirs is None: library_dirs = [] - if build_info is None: - build_info = {} lib_opts = gen_lib_options (self.libraries + libraries, self.library_dirs + library_dirs, "%s.lib", "/LIBPATH:%s") - if build_info.has_key('def_file') : - lib_opts.append('/DEF:' + build_info['def_file'] ) - ld_args = self.ldflags_static + lib_opts + \ objects + ['/OUT:' + output_filename] + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend (extra_postargs) self.spawn ( [ self.link ] + ld_args ) @@ -149,25 +137,25 @@ class MSVCCompiler (CCompiler) : def link_shared_lib (self, objects, output_libname, + output_dir=None, libraries=None, library_dirs=None, - build_info=None): - """Link a bunch of stuff together to create a shared library - file. Has the same effect as 'link_static_lib()' except - that the filename inferred from 'output_libname' will most - likely be different, and the type of file generated will - almost certainly be different.""" + extra_preargs=None, + extra_postargs=None): + # XXX should we sanity check the library name? (eg. no # slashes) - self.link_shared_object (objects, self.shared_library_name(output_libname), - build_info=build_info ) + self.link_shared_object (objects, + self.shared_library_name(output_libname)) def link_shared_object (self, objects, output_filename, + output_dir=None, libraries=None, library_dirs=None, - build_info=None): + extra_preargs=None, + extra_postargs=None): """Link a bunch of stuff together to create a shared object file. Much like 'link_shared_lib()', except the output filename is explicitly supplied as 'output_filename'.""" @@ -175,18 +163,17 @@ class MSVCCompiler (CCompiler) : libraries = [] if library_dirs is None: library_dirs = [] - if build_info is None: - build_info = {} - lib_opts = gen_lib_options (self.libraries + libraries, - self.library_dirs + library_dirs, - "%s.lib", "/LIBPATH:%s") + lib_opts = gen_lib_options (self.library_dirs + library_dirs, + self.libraries + libraries, + "/LIBPATH:%s", "%s.lib") - if build_info.has_key('def_file') : - lib_opts.append('/DEF:' + build_info['def_file'] ) - ld_args = self.ldflags_shared + lib_opts + \ objects + ['/OUT:' + output_filename] + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend (extra_postargs) self.spawn ( [ self.link ] + ld_args ) -- cgit v1.2.1 From ef61002f9b2e49fe27ff73f7da6003844eac6943 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:38:18 +0000 Subject: Renamed many options to be consistent across commands. Tweaked some help strings to be consistent with documentation. Don't call 'set_final_options()' in 'run()' anymore -- that's now guaranteed to be taken care of for us by the Distribution instance. --- command/build.py | 31 ++++++++++++++++--------------- command/install.py | 16 +++++++--------- command/install_ext.py | 9 ++++----- command/install_lib.py | 10 ++++------ command/install_py.py | 10 ++++------ 5 files changed, 35 insertions(+), 41 deletions(-) diff --git a/command/build.py b/command/build.py index b74e51cf..1586e60b 100644 --- a/command/build.py +++ b/command/build.py @@ -12,31 +12,32 @@ from distutils.core import Command class Build (Command): - options = [('basedir=', 'b', "base directory for build library"), - ('libdir=', 'l', "directory for platform-shared files"), - ('platdir=', 'p', "directory for platform-specific files"), + options = [('build-base=', 'b', + "base directory for build library"), + ('build-lib=', 'l', + "directory for platform-shared files"), + ('build-platlib=', 'p', + "directory for platform-specific files"), ] def set_default_options (self): - self.basedir = 'build' - # these are decided only after 'basedir' has its final value + self.build_base = 'build' + # these are decided only after 'build_base' has its final value # (unless overridden by the user or client) - self.libdir = None - self.platdir = None + self.build_lib = None + self.build_platlib = None def set_final_options (self): - # 'libdir' and 'platdir' just default to 'lib' and 'plat' under - # the base build directory - if self.libdir is None: - self.libdir = os.path.join (self.basedir, 'lib') - if self.platdir is None: - self.platdir = os.path.join (self.basedir, 'platlib') + # 'build_lib' and 'build_platlib' just default to 'lib' and + # 'platlib' under the base build directory + if self.build_lib is None: + self.build_lib = os.path.join (self.build_base, 'lib') + if self.build_platlib is None: + self.build_platlib = os.path.join (self.build_base, 'platlib') def run (self): - self.set_final_options () - # For now, "build" means "build_py" then "build_ext". (Eventually # it should also build documentation.) diff --git a/command/install.py b/command/install.py index 0e4ad3f0..cd12f6fc 100644 --- a/command/install.py +++ b/command/install.py @@ -16,16 +16,16 @@ from distutils.util import write_file class Install (Command): options = [('prefix=', None, "installation prefix"), - ('execprefix=', None, + ('exec-prefix=', None, "prefix for platform-specific files"), # Build directories: where to install from ('build-base=', None, "base build directory"), ('build-lib=', None, - "build directory for non-platform-specific library files"), + "build directory for pure Python modules"), ('build-platlib=', None, - "build directory for platform-specific library files"), + "build directory for extension modules"), # Installation directories: where to put modules and packages ('install-lib=', None, @@ -113,11 +113,11 @@ class Install (Command): # to fix things. # Figure out the build directories, ie. where to install from - self.set_peer_option ('build', 'basedir', self.build_base) + self.set_peer_option ('build', 'build_base', self.build_base) self.set_undefined_options ('build', - ('basedir', 'build_base'), - ('libdir', 'build_lib'), - ('platdir', 'build_platlib')) + ('build_base', 'build_base'), + ('build_lib', 'build_lib'), + ('build_platlib', 'build_platlib')) # Figure out actual installation directories; the basic principle # is: if the user supplied nothing, then use the directories that @@ -275,8 +275,6 @@ class Install (Command): def run (self): - self.set_final_options () - # Obviously have to build before we can install self.run_peer ('build') diff --git a/command/install_ext.py b/command/install_ext.py index 360a8027..3fb756c6 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -11,28 +11,27 @@ from distutils.util import copy_tree class InstallExt (Command): - options = [('dir=', 'd', "directory to install to"), + options = [('install-dir=', 'd', "directory to install to"), ('build-dir=','b', "build directory (where to install from)"), ] def set_default_options (self): # let the 'install' command dictate our installation directory - self.dir = None + self.install_dir = None self.build_dir = None def set_final_options (self): self.set_undefined_options ('install', ('build_platlib', 'build_dir'), - ('install_site_platlib', 'dir')) + ('install_site_platlib', 'install_dir')) def run (self): - self.set_final_options () # Dump the entire "build/platlib" directory (or whatever it really # is; "build/platlib" is the default) to the installation target # (eg. "/usr/local/lib/python1.5/site-packages"). Note that # putting files in the right package dir is already done when we # build. - outfiles = self.copy_tree (self.build_dir, self.dir) + outfiles = self.copy_tree (self.build_dir, self.install_dir) # class InstallExt diff --git a/command/install_lib.py b/command/install_lib.py index a2ba16cc..978bb3b9 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -8,7 +8,7 @@ from distutils.util import copy_tree class InstallPy (Command): - options = [('dir=', 'd', "directory to install to"), + options = [('install-dir=', 'd', "directory to install to"), ('build-dir=','b', "build directory (where to install from)"), ('compile', 'c', "compile .py to .pyc"), ('optimize', 'o', "compile .py to .pyo (optimized)"), @@ -17,7 +17,7 @@ class InstallPy (Command): def set_default_options (self): # let the 'install' command dictate our installation directory - self.dir = None + self.install_dir = None self.build_dir = None self.compile = 1 self.optimize = 1 @@ -39,18 +39,16 @@ class InstallPy (Command): # install (target) directory, and whether to compile .py files. self.set_undefined_options ('install', ('build_lib', 'build_dir'), - (dir_option, 'dir'), + (dir_option, 'install_dir'), ('compile_py', 'compile'), ('optimize_py', 'optimize')) def run (self): - self.set_final_options () - # Dump entire contents of the build directory to the installation # directory (that's the beauty of having a build directory!) - outfiles = self.copy_tree (self.build_dir, self.dir) + outfiles = self.copy_tree (self.build_dir, self.install_dir) # (Optionally) compile .py to .pyc # XXX hey! we can't control whether we optimize or not; that's up diff --git a/command/install_py.py b/command/install_py.py index a2ba16cc..978bb3b9 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -8,7 +8,7 @@ from distutils.util import copy_tree class InstallPy (Command): - options = [('dir=', 'd', "directory to install to"), + options = [('install-dir=', 'd', "directory to install to"), ('build-dir=','b', "build directory (where to install from)"), ('compile', 'c', "compile .py to .pyc"), ('optimize', 'o', "compile .py to .pyo (optimized)"), @@ -17,7 +17,7 @@ class InstallPy (Command): def set_default_options (self): # let the 'install' command dictate our installation directory - self.dir = None + self.install_dir = None self.build_dir = None self.compile = 1 self.optimize = 1 @@ -39,18 +39,16 @@ class InstallPy (Command): # install (target) directory, and whether to compile .py files. self.set_undefined_options ('install', ('build_lib', 'build_dir'), - (dir_option, 'dir'), + (dir_option, 'install_dir'), ('compile_py', 'compile'), ('optimize_py', 'optimize')) def run (self): - self.set_final_options () - # Dump entire contents of the build directory to the installation # directory (that's the beauty of having a build directory!) - outfiles = self.copy_tree (self.build_dir, self.dir) + outfiles = self.copy_tree (self.build_dir, self.install_dir) # (Optionally) compile .py to .pyc # XXX hey! we can't control whether we optimize or not; that's up -- cgit v1.2.1 From dc070bb95b5ab859dc3a9fa613a960a62c977f45 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:44:57 +0000 Subject: Renamed 'dir' option to be consistent with other commands. Don't call 'set_final_options()' in 'run()' anymore -- that's now guaranteed to be taken care of for us by the Distribution instance. Rearranged to bit to allow outsiders (specifically, the 'dist' command) to find out what modules we would build: - 'find_modules()' renamed to 'find_package_modules()' - most of 'build_modules()' abstracted out to 'find_modules()' - added 'get_source_files()' (for the 'dist' command to use) - drastically simplified 'build_modules()' -- now just a wrapper around 'find_modules()' and 'build_module()' --- command/build_py.py | 101 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 187e93b5..3a7a43bb 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -17,19 +17,19 @@ from distutils.util import mkpath, newer, make_file, copy_file class BuildPy (Command): - options = [('dir=', 'd', "directory for platform-shared files"), + options = [('build-dir=', 'd', "directory for platform-shared files"), ] def set_default_options (self): - self.dir = None + self.build_dir = None self.modules = None self.package = None self.package_dir = None def set_final_options (self): self.set_undefined_options ('build', - ('libdir', 'dir')) + ('build_lib', 'build_dir')) # Get the distribution options that are aliases for build_py # options -- list of packages and list of modules. @@ -53,8 +53,6 @@ class BuildPy (Command): # metadata to know that a file is meant to be interpreted by # Python?) - self.set_final_options () - infiles = [] outfiles = [] missing = [] @@ -155,7 +153,7 @@ class BuildPy (Command): # check_module () - def find_modules (self, package, package_dir): + def find_package_modules (self, package, package_dir): module_files = glob (os.path.join (package_dir, "*.py")) module_pairs = [] for f in module_files: @@ -164,36 +162,18 @@ class BuildPy (Command): return module_pairs - def build_module (self, module, module_file, package): - - if type (package) is StringType: - package = string.split (package, '.') - - # Now put the module source file into the "build" area -- this - # is easy, we just copy it somewhere under self.dir (the build - # directory for Python source). - outfile_path = package - outfile_path.append (module + ".py") - outfile_path.insert (0, self.dir) - outfile = apply (os.path.join, outfile_path) - - dir = os.path.dirname (outfile) - self.mkpath (dir) - self.copy_file (module_file, outfile) - - - def build_modules (self): - + def find_modules (self): # Map package names to tuples of useful info about the package: # (package_dir, checked) # package_dir - the directory where we'll find source files for # this package # checked - true if we have checked that the package directory # is valid (exists, contains __init__.py, ... ?) - - packages = {} + # List of (module, package, filename) tuples to return + modules = [] + # We treat modules-in-packages almost the same as toplevel modules, # just the "package" for a toplevel is empty (either an empty # string or empty list, depending on context). Differences: @@ -202,7 +182,7 @@ class BuildPy (Command): for module in self.modules: path = string.split (module, '.') package = tuple (path[0:-1]) - module = path[-1] + module_base = path[-1] try: (package_dir, checked) = packages[package] @@ -217,14 +197,65 @@ class BuildPy (Command): # XXX perhaps we should also check for just .pyc files # (so greedy closed-source bastards can distribute Python # modules too) - module_file = os.path.join (package_dir, module + ".py") + module_file = os.path.join (package_dir, module_base + ".py") if not self.check_module (module, module_file): continue + modules.append ((module, package, module_file)) + + return modules + + # find_modules () + + + def get_source_files (self): + + if self.modules: + modules = self.find_modules () + else: + modules = [] + for package in self.packages: + package_dir = self.get_package_dir (package) + m = self.find_package_modules (package, package_dir) + modules.extend (m) + + # Both find_modules() and find_package_modules() return a list of + # tuples where the last element of each tuple is the filename -- + # what a happy coincidence! + filenames = [] + for module in modules: + filenames.append (module[-1]) + + return filenames + + + def build_module (self, module, module_file, package): + + if type (package) is StringType: + package = string.split (package, '.') + + # Now put the module source file into the "build" area -- this is + # easy, we just copy it somewhere under self.build_dir (the build + # directory for Python source). + outfile_path = package + outfile_path.append (module + ".py") + outfile_path.insert (0, self.build_dir) + outfile = apply (os.path.join, outfile_path) + + dir = os.path.dirname (outfile) + self.mkpath (dir) + self.copy_file (module_file, outfile) + + + def build_modules (self): + + modules = self.find_modules() + for (module, package, module_file) in modules: + # Now "build" the module -- ie. copy the source file to - # self.dir (the build directory for Python source). (Actually, - # it gets copied to the directory for this package under - # self.dir.) + # self.build_dir (the build directory for Python source). + # (Actually, it gets copied to the directory for this package + # under self.build_dir.) self.build_module (module, module_file, package) # build_modules () @@ -242,10 +273,10 @@ class BuildPy (Command): # package!), and module_file is the path to the .py file, # relative to the current directory (ie. including # 'package_dir'). - modules = self.find_modules (package, package_dir) + modules = self.find_package_modules (package, package_dir) # Now loop over the modules we found, "building" each one (just - # copy it to self.dir). + # copy it to self.build_dir). for (module, module_file) in modules: self.build_module (module, module_file, package) -- cgit v1.2.1 From 0e1de2192cf0ccfd9196aec77cdae0ae04eda971 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:49:35 +0000 Subject: Added 'package' option. Catch up with renamed 'platdir' -> 'build_platlib' option in 'build'. Don't call 'set_final_options()' in 'run()' anymore -- that's now guaranteed to be taken care of for us by the Distribution instance. If 'include_dirs' is a string, split it on os.pathsep (this is half- hearted -- support for setting compile/link options on the command line is totally lame and probably won't work at all). Added 'get_source_files()' for use by 'dist' command. Added code to 'build_extensions()' to figure out the "def file" to use with MSVC++ and add it to the linker command line as an "extra_postarg". --- command/build_ext.py | 51 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index a0ab61b3..8c5065f6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -58,8 +58,6 @@ class BuildExt (Command): "directories to search for shared C libraries at runtime"), ('link-objects=', 'O', "extra explicit link objects to include in the link"), - ('package=', 'p', - "Python package to put extension modules into"), ] @@ -76,8 +74,9 @@ class BuildExt (Command): self.rpath = None self.link_objects = None + def set_final_options (self): - self.set_undefined_options ('build', ('platdir', 'build_dir')) + self.set_undefined_options ('build', ('build_platlib', 'build_dir')) if self.package is None: self.package = self.distribution.ext_package @@ -94,6 +93,10 @@ class BuildExt (Command): 'python' + sys.version[0:3]) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] + if type (self.include_dirs) is StringType: + self.include_dirs = string.split (self.include_dirs, + os.pathsep) + self.include_dirs.insert (0, py_include) if exec_py_include != py_include: self.include_dirs.insert (0, exec_py_include) @@ -101,8 +104,6 @@ class BuildExt (Command): def run (self): - self.set_final_options () - # 'self.extensions', as supplied by setup.py, is a list of 2-tuples. # Each tuple is simple: # (ext_name, build_info) @@ -172,6 +173,19 @@ class BuildExt (Command): # check_extensions_list () + def get_source_files (self): + + filenames = [] + + # Wouldn't it be neat if we knew the names of header files too... + for (extension_name, build_info) in extensions: + sources = build_info.get ('sources') + if type (sources) in (ListType, TupleType): + filenames.extend (sources) + + return filenames + + def build_extensions (self, extensions): for (extension_name, build_info) in extensions: @@ -180,19 +194,42 @@ class BuildExt (Command): raise DistutilsValueError, \ "in ext_modules option, 'sources' must be present " + \ "and must be a list of source filenames" - + + # First step: compile the source code to object files. This + # drops the object files in the current directory, regardless + # of where the source is (may be a bad thing, but that's how a + # Makefile.pre.in-based system does it, so at least there's a + # precedent!) macros = build_info.get ('macros') include_dirs = build_info.get ('include_dirs') self.compiler.compile (sources, macros=macros, includes=include_dirs) + # Now link the object files together into a "shared object" -- + # of course, first we have to figure out all the other things + # that go into the mix. objects = self.compiler.object_filenames (sources) extra_objects = build_info.get ('extra_objects') if extra_objects: objects.extend (extra_objects) libraries = build_info.get ('libraries') library_dirs = build_info.get ('library_dirs') + extra_args = build_info.get ('extra_link_args') or [] + if self.compiler.compiler_type == 'msvc': + def_file = build_info.get ('def_file') + if def_file is None: + source_dir = os.path.dirname (sources[0]) + ext_base = (string.split (extension_name, '.'))[-1] + def_file = os.path.join (source_dir, "%s.def" % ext_base) + if not os.path.exists (def_file): + self.warn ("file '%s' not found: " % def_file + + "might have problems building DLL") + def_file = None + + if def_file is not None: + extra_args.append ('/DEF:' + def_file) + ext_filename = self.extension_filename \ (extension_name, self.package) ext_filename = os.path.join (self.build_dir, ext_filename) @@ -201,7 +238,7 @@ class BuildExt (Command): self.compiler.link_shared_object (objects, ext_filename, libraries=libraries, library_dirs=library_dirs, - build_info=build_info) # XXX hack! + extra_postargs=extra_args) # build_extensions () -- cgit v1.2.1 From 23e30bcec4cff109e9545d49763f6166688b88f1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:50:13 +0000 Subject: New command to generate source distribution based on a manifest file. --- command/dist.py | 590 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 590 insertions(+) create mode 100644 command/dist.py diff --git a/command/dist.py b/command/dist.py new file mode 100644 index 00000000..222296f3 --- /dev/null +++ b/command/dist.py @@ -0,0 +1,590 @@ +"""distutils.command.dist + +Implements the Distutils 'dist' command (create a source distribution).""" + +# created 1999/09/22, Greg Ward + +__rcsid__ = "$Id$" + +import sys, os, string, re +import fnmatch +from types import * +from glob import glob +from distutils.core import Command +from distutils.text_file import TextFile + + +# Possible modes of operation: +# - require an explicit manifest that lists every single file (presumably +# along with a way to auto-generate the manifest) +# - require an explicit manifest, but allow it to have globs or +# filename patterns of some kind (and also have auto-generation) +# - allow an explict manifest, but automatically augment it at runtime +# with the source files mentioned in 'packages', 'py_modules', and +# 'ext_modules' (and any other such things that might come along) + +# I'm liking the third way. Possible gotchas: +# - redundant specification: 'packages' includes 'foo' and manifest +# includes 'foo/*.py' +# - obvious conflict: 'packages' includes 'foo' and manifest +# includes '! foo/*.py' (can't imagine why you'd want this) +# - subtle conflict: 'packages' includes 'foo' and manifest +# includes '! foo/bar.py' (this could well be desired: eg. exclude +# an experimental module from distribution) + +# Syntax for the manifest file: +# - if a line is just a Unix-style glob by itself, it's a "simple include +# pattern": go find all files that match and add them to the list +# of files +# - if a line is a glob preceded by "!", then it's a "simple exclude +# pattern": go over the current list of files and exclude any that +# match the glob pattern +# - if a line consists of a directory name followed by zero or more +# glob patterns, then we'll recursively explore that directory tree +# - the glob patterns can be include (no punctuation) or exclude +# (prefixed by "!", no space) +# - if no patterns given or the first pattern is not an include pattern, +# then assume "*" -- ie. find everything (and then start applying +# the rest of the patterns) +# - the patterns are given in order of increasing precedence, ie. +# the *last* one to match a given file applies to it +# +# example (ignoring auto-augmentation!): +# distutils/*.py +# distutils/command/*.py +# ! distutils/bleeding_edge.py +# examples/*.py +# examples/README +# +# smarter way (that *will* include distutils/command/bleeding_edge.py!) +# distutils *.py +# ! distutils/bleeding_edge.py +# examples !*~ !*.py[co] (same as: examples * !*~ !*.py[co]) +# test test_* *.txt !*~ !*.py[co] +# README +# setup.py +# +# The actual Distutils manifest (don't need to mention source files, +# README, setup.py -- they're automatically distributed!): +# examples !*~ !*.py[co] +# test !*~ !*.py[co] + +# The algorithm that will make it work: +# files = stuff from 'packages', 'py_modules', 'ext_modules', +# plus README, setup.py, ... ? +# foreach pattern in manifest file: +# if simple-include-pattern: # "distutils/*.py" +# files.append (glob (pattern)) +# elif simple-exclude-pattern: # "! distutils/foo*" +# xfiles = glob (pattern) +# remove all xfiles from files +# elif recursive-pattern: # "examples" (just a directory name) +# patterns = rest-of-words-on-line +# dir_files = list of all files under dir +# if patterns: +# if patterns[0] is an exclude-pattern: +# insert "*" at patterns[0] +# for file in dir_files: +# for dpattern in reverse (patterns): +# if file matches dpattern: +# if dpattern is an include-pattern: +# files.append (file) +# else: +# nothing, don't include it +# next file +# else: +# files.extend (dir_files) # ie. accept all of them + + +# Anyways, this is all implemented below -- BUT it is largely untested; I +# know it works for the simple case of distributing the Distutils, but +# haven't tried it on more complicated examples. Undoubtedly doing so will +# reveal bugs and cause delays, so I'm waiting until after I've released +# Distutils 0.1. + + +# Other things we need to look for in creating a source distribution: +# - make sure there's a README +# - make sure the distribution meta-info is supplied and non-empty +# (*must* have name, version, ((author and author_email) or +# (maintainer and maintainer_email)), url +# +# Frills: +# - make sure the setup script is called "setup.py" +# - make sure the README refers to "setup.py" (ie. has a line matching +# /^\s*python\s+setup\.py/) + +# A crazy idea that conflicts with having/requiring 'version' in setup.py: +# - make sure there's a version number in the "main file" (main file +# is __init__.py of first package, or the first module if no packages, +# or the first extension module if no pure Python modules) +# - XXX how do we look for __version__ in an extension module? +# - XXX do we import and look for __version__? or just scan source for +# /^__version__\s*=\s*"[^"]+"/ ? +# - what about 'version_from' as an alternative to 'version' -- then +# we know just where to search for the version -- no guessing about +# what the "main file" is + + + +class Dist (Command): + + options = [('formats=', 'f', + "formats for source distribution (tar, ztar, gztar, or zip)"), + ('manifest=', 'm', + "name of manifest file"), + ] + + default_format = { 'posix': 'gztar', + 'nt': 'zip' } + + exclude_re = re.compile (r'\s*!\s*(\S+)') # for manifest lines + + + def set_default_options (self): + self.formats = None + self.manifest = None + + + def set_final_options (self): + if self.formats is None: + try: + self.formats = [self.default_format[os.name]] + except KeyError: + raise DistutilsPlatformError, \ + "don't know how to build source distributions on " + \ + "%s platform" % os.name + elif type (self.formats) is StringType: + self.formats = string.split (self.formats, ',') + + if self.manifest is None: + self.manifest = "MANIFEST" + + + def run (self): + + self.check_metadata () + + self.files = [] + self.find_defaults () + self.read_manifest () + + self.make_distribution () + + + def check_metadata (self): + + dist = self.distribution + + missing = [] + for attr in ('name', 'version', 'url'): + if not (hasattr (dist, attr) and getattr (dist, attr)): + missing.append (attr) + + if missing: + self.warn ("missing required meta-data: " + + string.join (missing, ", ")) + + if dist.author: + if not dist.author_email: + self.warn ("missing meta-data: if 'author' supplied, " + + "'author_email' must be supplied too") + elif dist.maintainer: + if not dist.maintainer_email: + self.warn ("missing meta-data: if 'maintainer' supplied, " + + "'maintainer_email' must be supplied too") + else: + self.warn ("missing meta-data: either author (and author_email) " + + "or maintainer (and maintainer_email) " + + "must be supplied") + + # check_metadata () + + + def find_defaults (self): + + standards = ['README', 'setup.py'] + for fn in standards: + if os.path.exists (fn): + self.files.append (fn) + else: + self.warn ("standard file %s not found" % fn) + + optional = ['test/test*.py'] + for pattern in optional: + files = glob (pattern) + if files: + self.files.extend (files) + + if self.distribution.packages or self.distribution.py_modules: + build_py = self.find_peer ('build_py') + build_py.ensure_ready () + self.files.extend (build_py.get_source_files ()) + + if self.distribution.ext_modules: + build_ext = self.find_peer ('build_ext') + build_ext.ensure_ready () + self.files.extend (build_ext.get_source_files ()) + + + + def open_manifest (self, filename): + return TextFile (filename, + strip_comments=1, + skip_blanks=1, + join_lines=1, + lstrip_ws=1, + rstrip_ws=1, + collapse_ws=1) + + + def search_dir (self, dir, patterns): + + allfiles = findall (dir) + if patterns: + if patterns[0][0] == "!": # starts with an exclude spec? + patterns.insert (0, "*")# then accept anything that isn't + # explicitly excluded + + act_patterns = [] # "action-patterns": (include,regexp) + # tuples where include is a boolean + for pattern in patterns: + if pattern[0] == '!': + act_patterns.append \ + ((0, re.compile (fnmatch.translate (pattern[1:])))) + else: + act_patterns.append \ + ((1, re.compile (fnmatch.translate (pattern)))) + act_patterns.reverse() + + + files = [] + for file in allfiles: + for (include,regexp) in act_patterns: + if regexp.match (file): + if include: + files.append (file) + break # continue to next file + else: + files = allfiles + + return files + + # search_dir () + + + def exclude_files (self, pattern): + + regexp = re.compile (fnmatch.translate (pattern)) + for i in range (len (self.files)-1, -1, -1): + if regexp.match (self.files[i]): + del self.files[i] + + + def read_manifest (self): + + # self.files had better already be defined (and hold the + # "automatically found" files -- Python modules and extensions, + # README, setup script, ...) + assert self.files is not None + + manifest = self.open_manifest (self.manifest) + while 1: + + pattern = manifest.readline() + if pattern is None: # end of file + break + + # Cases: + # 1) simple-include: "*.py", "foo/*.py", "doc/*.html", "FAQ" + # 2) simple-exclude: same, prefaced by ! + # 3) recursive: multi-word line, first word a directory + + exclude = self.exclude_re.match (pattern) + if exclude: + pattern = exclude.group (1) + + words = string.split (pattern) + assert words # must have something! + if os.name != 'posix': + words[0] = apply (os.path.join, string.split (words[0], '/')) + + # First word is a directory, possibly with include/exclude + # patterns making up the rest of the line: it's a recursive + # pattern + if os.path.isdir (words[0]): + if exclude: + file.warn ("exclude (!) doesn't apply to " + + "whole directory trees") + continue + + dir_files = self.search_dir (words[0], words[1:]) + self.files.extend (dir_files) + + # Multiple words in pattern: that's a no-no unless the first + # word is a directory name + elif len (words) > 1: + file.warn ("can't have multiple words unless first word " + + "('%s') is a directory name" % words[0]) + continue + + # Single word, no bang: it's a "simple include pattern" + elif not exclude: + matches = glob (pattern) + if matches: + self.files.extend (matches) + else: + manifest.warn ("no matches for '%s' found" % pattern) + + + # Single word prefixed with a bang: it's a "simple exclude pattern" + else: + if self.exclude_files (pattern) == 0: + file.warn ("no files excluded by '%s'" % pattern) + + # if/elif/.../else on 'pattern' + + # loop over lines of 'manifest' + + # read_manifest () + + + def make_release_tree (self, base_dir, files): + + # XXX this is Unix-specific + + # First get the list of directories to create + need_dir = {} + for file in files: + need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1 + need_dirs = need_dir.keys() + need_dirs.sort() + + # Now create them + for dir in need_dirs: + self.mkpath (dir) + + # And walk over the list of files, making a hard link for + # each one that doesn't already exist in its corresponding + # location under 'base_dir' + + self.announce ("making hard links in %s..." % base_dir) + for file in files: + dest = os.path.join (base_dir, file) + if not os.path.exists (dest): + self.execute (os.link, (file, dest), + "linking %s -> %s" % (file, dest)) + # make_release_tree () + + + def make_tarball (self, base_dir): + + # XXX GNU tar 1.13 has a nifty option to add a prefix directory. + # It's pretty new, though, so we certainly can't require it -- but + # it would be nice to take advantage of it to skip the "create a + # tree of hardlinks" step! + + # But I am a lazy bastard, so I require GNU tar anyways. + + archive_name = base_dir + ".tar.gz" + self.spawn (["tar", "-czf", archive_name, base_dir]) + + + def make_zipfile (self, base_dir): + + # This assumes the Unix 'zip' utility -- it could be easily recast + # to use pkzip (or whatever the command-line zip creation utility + # on Redmond's archaic CP/M knockoff is nowadays), but I'll let + # someone who can actually test it do that. + + self.spawn (["zip", "-r", base_dir, base_dir]) + + + def make_distribution (self): + + # Don't warn about missing meta-data here -- should be done + # elsewhere. + name = self.distribution.name or "UNKNOWN" + version = self.distribution.version + + if version: + base_dir = "%s-%s" % (name, version) + else: + base_dir = name + + # Remove any files that match "base_dir" from the fileset -- we + # don't want to go distributing the distribution inside itself! + self.exclude_files (base_dir + "*") + + self.make_release_tree (base_dir, self.files) + if 'gztar' in self.formats: + self.make_tarball (base_dir) + if 'zip' in self.formats: + self.make_zipfile (base_dir) + +# class Dist + + +# ---------------------------------------------------------------------- +# Utility functions + +def findall (dir = os.curdir): + """Find all files under 'dir' and return the sorted list of full + filenames (relative to 'dir').""" + + list = [] + stack = [dir] + pop = stack.pop + push = stack.append + + while stack: + dir = pop() + names = os.listdir (dir) + + for name in names: + fullname = os.path.join (dir, name) + list.append (fullname) + if os.path.isdir (fullname) and not os.path.islink(fullname): + push (fullname) + + list.sort() + return list + + + + + +# ====================================================================== +# Here follows some extensive mental masturbation about how to +# make the manifest file and search algorithm even more complex. +# I think this is all gratuitous, really. + +# Hmm, something extra: want to apply an exclude pattern over a whole +# subtree without necessarily having to explicitly include files from it, +# ie. it should apply after gathering files by other means (simple +# include pattern) +# . !*~ !*.bak !#*# +# and we also want to prune at certain directories: +# . !RCS !CVS +# which again should apply globally. +# +# possible solution: +# - exclude pattern in a directory applies to all files found under that +# directory +# - subdirectories that match an exclude pattern will be pruned +# - hmmm, to be consistent, subdirectories that match an include +# pattern should be recursively included +# - and this should apply to "simple" patterns too +# +# thus: +# +# examples/ +# +# means get everything in examples/ and all subdirs; +# +# examples/ !*~ !#*# !*.py[co] +# +# means get everything under examples/ except files matching those three globs; +# +# ./ !RCS !CVS +# +# means get everything under current dir, but prune RCS/CVS directories; +# +# ./ !*~ !#*# !*.py[co] !RCS !CVS +# ! build/ +# ! experimental/ +# +# means get everything under the distribution directory except the usual +# excludes at all levels; exclude "build" and "experimental" under the +# distribution dir only. +# +# Do the former examples still work? +# +# distutils/ *.py +# ! distutils/bleeding_edge.py +# +# means all .py files recursively found under distutils, except for the one +# explicitly named. +# +# distutils/ *.py !bleeding_edge.py +# +# means the same, except bleeding_edge.py will be excluded wherever it's +# found -- thus this can exclude up to one file per directory under +# distutils. +# +# distutils/*.py +# ! distutils/bleeding_edge.py +# +# gets exactly distutils/*.py, minus the one explicitly mentioned exclude, and +# +# distutils/*.py +# distutils/ !bleeding_edge.py +# +# coincidentally does the same, but only because there can only be one file +# that matches the exclude pattern. Oh, we'd still like +# +# distutils *.py !bleeding*.py +# distutils/bleeding_ledge.py +# +# to include distutils/bleeding_ledge.py -- i.e. it should override the +# earlier exclude pattern by virtue of appearing later in the manifest. Does +# this conflict with the above requirements, ie. that "!RCS" and "!*~" should +# apply everywhere? Hmm, I think it doesn't have to, as long as we're smart +# about it. Consequence: +# +# . !RCS !CVS +# distutils * +# +# will go ahead and include RCS and CVS files under distutils, but +# +# distutils * +# . !RCS !CVS +# +# will do the right thing. Hmmm. I think that's OK, and an inevitable +# consequence of the ability to override exclusions. + +# OK, new crack at the search algorithm. +# +# for pattern in manifest: +# if dir-pattern: # ie. first word is a directory (incl. "."!) +# dir = first word on line +# patterns = rest of line +# if patterns: +# for dpattern in patterns: +# if exclude-pattern: +# remove from files anything matching dpattern (including pruning +# subtrees rooted at directories that match dpattern) +# else: +# files.append (recursive_glob (dir, dpattern)) +# else: +# files.append (recursive_glob (dir, '*') +# +# elif include-pattern: # it's a "simple include pattern" +# files.append (glob (pattern)) +# +# else: # it's a "simple exclude pattern" +# remove from files anything matching pattern + +# The two removal algorithms might be a bit tricky: +# +# "remove simple exclude pattern": +# for f in files: +# if f matches pattern: +# delete it +# +# "remove recursive exclude pattern": +# for f in files: +# +# t = tail (f) +# while t: +# if t matches pattern: +# delete current file +# continue +# t = tail (t) +# +# Well, that was an interesting mental exercise. I'm not completely +# convinced it will work, nor am I convinced this level of complexity +# is necessary. If you want to exclude RCS or CVS directories, just +# don't bloody include them! + + -- cgit v1.2.1 From 3a5f9618d77b220f7e47e6a2c0204472333a7356 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 13:03:32 +0000 Subject: Added all documentation. Slightly improved the code for dealing with newline on a comment line, and for stripping whitespace. --- text_file.py | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 13 deletions(-) diff --git a/text_file.py b/text_file.py index 2e034301..7b29ef4a 100644 --- a/text_file.py +++ b/text_file.py @@ -14,6 +14,65 @@ import sys, os, string, re class TextFile: + """Provides a file-like object that takes care of all the things you + commonly want to do when processing a text file that has some + line-by-line syntax: strip comments (as long as "#" is your comment + character), skip blank lines, join adjacent lines by escaping the + newline (ie. backslash at end of line), strip leading and/or + trailing whitespace, and collapse internal whitespace. All of these + are optional and independently controllable. + + Provides a 'warn()' method so you can generate warning messages that + report physical line number, even if the logical line in question + spans multiple physical lines. Also provides 'unreadline()' for + implementing line-at-a-time lookahead. + + Constructor is called as: + + TextFile (filename=None, file=None, **options) + + It bombs (RuntimeError) if both 'filename' and 'file' are None; + 'filename' should be a string, and 'file' a file object (or + something that provides 'readline()' and 'close()' methods). It is + recommended that you supply at least 'filename', so that TextFile + can include it in warning messages. If 'file' is not supplied, + TextFile creates its own using the 'open()' builtin. + + The options are all boolean, and affect the value returned by + 'readline()': + strip_comments [default: true] + strip from "#" to end-of-line, as well as any whitespace + leading up to the "#" -- unless it is escaped by a backslash + lstrip_ws [default: false] + strip leading whitespace from each line before returning it + rstrip_ws [default: true] + strip trailing whitespace (including line terminator!) from + each line before returning it + skip_blanks [default: true} + skip lines that are empty *after* stripping comments and + whitespace. (If both lstrip_ws and rstrip_ws are true, + then some lines may consist of solely whitespace: these will + *not* be skipped, even if 'skip_blanks' is true.) + join_lines [default: false] + if a backslash is the last non-newline character on a line + after stripping comments and whitespace, join the following line + to it to form one "logical line"; if N consecutive lines end + with a backslash, then N+1 physical lines will be joined to + form one logical line. + collapse_ws [default: false] + after stripping comments and whitespace and joining physical + lines into logical lines, all internal whitespace (strings of + whitespace surrounded by non-whitespace characters, and not at + the beginning or end of the logical line) will be collapsed + to a single space. + + Note that since 'rstrip_ws' can strip the trailing newline, the + semantics of 'readline()' must differ from those of the builtin file + object's 'readline()' method! In particular, 'readline()' returns + None for end-of-file: an empty string might just be a blank line (or + an all-whitespace line), if 'rstrip_ws' is true but 'skip_blanks' is + not.""" + default_options = { 'strip_comments': 1, 'skip_blanks': 1, 'join_lines': 0, @@ -23,6 +82,10 @@ class TextFile: } def __init__ (self, filename=None, file=None, **options): + """Construct a new TextFile object. At least one of 'filename' + (a string) and 'file' (a file-like object) must be supplied. + They keyword argument options are described above and affect + the values returned by 'readline()'.""" if filename is None and file is None: raise RuntimeError, \ @@ -56,12 +119,18 @@ class TextFile: def open (self, filename): + """Open a new file named 'filename'. This overrides both the + 'filename' and 'file' arguments to the constructor.""" + self.filename = filename self.file = open (self.filename, 'r') self.current_line = 0 def close (self): + """Close the current file and forget everything we know about it + (filename, current line number).""" + self.file.close () self.file = None self.filename = None @@ -69,6 +138,14 @@ class TextFile: def warn (self, msg, line=None): + """Print (to stderr) a warning message tied to the current logical + line in the current file. If the current logical line in the + file spans multiple physical lines, the warning refers to the + whole range, eg. "lines 3-5". If 'line' supplied, it overrides + the current line number; it may be a list or tuple to indicate a + range of physical lines, or an integer for a single physical + line.""" + if line is None: line = self.current_line sys.stderr.write (self.filename + ", ") @@ -80,6 +157,15 @@ class TextFile: def readline (self): + """Read and return a single logical line from the current file (or + from an internal buffer if lines have previously been "unread" + with 'unreadline()'). If the 'join_lines' option is true, this + may involve reading multiple physical lines concatenated into a + single string. Updates the current line number, so calling + 'warn()' after 'readline()' emits a warning about the physical + line(s) just read. Returns None on end-of-file, since the empty + string can occur if 'rstrip_ws' is true but 'strip_blanks' is + not.""" # If any "unread" lines waiting in 'linebuf', return the top # one. (We don't actually buffer read-ahead data -- lines only @@ -111,15 +197,15 @@ class TextFile: if pos == -1: # no "#" -- no comments pass elif pos == 0 or line[pos-1] != "\\": # it's a comment - # Have to preserve the trailing newline; if - # stripping comments resulted in an empty line, we'd - # have no way to distinguish end-of-file! (NB. this - # means that if the final line is all comment and - # has to trailing newline, we will think that it's + + # Have to preserve the trailing newline, because it's + # the job of a later step (rstrip_ws) to remove it -- + # and if rstrip_ws is false, we'd better preserve it! + # (NB. this means that if the final line is all comment + # and has no trailing newline, we will think that it's # EOF; I think that's OK.) - has_newline = (line[-1] == '\n') - line = line[0:pos] - if has_newline: line = line + '\n' + eol = (line[-1] == '\n') and '\n' or '' + line = line[0:pos] + eol else: # it's an escaped "#" line = string.replace (line, "\\#", "#") @@ -156,11 +242,10 @@ class TextFile: # trailing, or one or the other, or neither) if self.lstrip_ws and self.rstrip_ws: line = string.strip (line) - else: - if self.lstrip_ws: - line = string.lstrip (line) - if self.rstrip_ws: - line = string.rstrip (line) + elif self.lstrip_ws: + line = string.lstrip (line) + elif self.rstrip_ws: + line = string.rstrip (line) # blank line (whether we rstrip'ed or not)? skip to next line # if appropriate @@ -187,6 +272,9 @@ class TextFile: def readlines (self): + """Read and return the list of all logical lines remaining in the + current file.""" + lines = [] while 1: line = self.readline() @@ -196,6 +284,10 @@ class TextFile: def unreadline (self, line): + """Push 'line' (a string) onto an internal buffer that will be + checked by future 'readline()' calls. Handy for implementing + a parser with line-at-a-time lookahead.""" + self.linebuf.append (line) @@ -206,6 +298,7 @@ line 3 \\ continues on next line """ + # result 1: no fancy options result1 = map (lambda x: x + "\n", string.split (test_data, "\n")[0:-1]) -- cgit v1.2.1 From c0b843e02b1e09998d6a56ce259c88c010287b39 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 13:14:27 +0000 Subject: Added 'list_only' option (and modified 'run()' to respect it). --- command/dist.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/command/dist.py b/command/dist.py index 222296f3..3f309745 100644 --- a/command/dist.py +++ b/command/dist.py @@ -133,6 +133,8 @@ class Dist (Command): "formats for source distribution (tar, ztar, gztar, or zip)"), ('manifest=', 'm', "name of manifest file"), + ('list-only', 'l', + "just list files that would be distributed"), ] default_format = { 'posix': 'gztar', @@ -144,6 +146,7 @@ class Dist (Command): def set_default_options (self): self.formats = None self.manifest = None + self.list_only = 0 def set_final_options (self): @@ -169,7 +172,12 @@ class Dist (Command): self.find_defaults () self.read_manifest () - self.make_distribution () + if self.list_only: + for f in self.files: + print f + + else: + self.make_distribution () def check_metadata (self): -- cgit v1.2.1 From 7bea18d0ecf34847d08afd9af1a2dc40799d8119 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 20:41:02 +0000 Subject: Slight change to the meaning of the 'libraries' list: if a library name has a directory component, then we only search for the library in that one directory, ie. ignore the 'library_dirs' lists for that one library. Changed calling convention to 'gen_lib_options()' again: now, it takes a CCompiler instance and calls methods on it instead of taking format strings. Also implemented the new "library name" semantics using the 'find_library_file()' method in the CCompiler instance. Added 'force' flag to CCompiler; added to constructor and 'new_compiler()'. Added 'warn()' method. --- ccompiler.py | 51 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index bfbb50fa..486f03ad 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -67,10 +67,12 @@ class CCompiler: def __init__ (self, verbose=0, - dry_run=0): + dry_run=0, + force=0): self.verbose = verbose self.dry_run = dry_run + self.force = force # 'output_dir': a common output directory for object, library, # shared object, and shared library files @@ -312,9 +314,19 @@ class CCompiler: 'output_libname' should be a library name, not a filename; the filename will be inferred from the library name. - 'library_dirs', if supplied, should be a list of additional - directories to search on top of the system default and those - supplied to 'add_library_dir()' and/or 'set_library_dirs()'. + 'libraries' is a list of libraries to link against. These are + library names, not filenames, since they're translated into + filenames in a platform-specific way (eg. "foo" becomes + "libfoo.a" on Unix and "foo.lib" on DOS/Windows). However, they + can include a directory component, which means the linker will + look in that specific directory rather than searching all the + normal locations. + + 'library_dirs', if supplied, should be a list of directories to + search for libraries that were specified as bare library names + (ie. no directory component). These are on top of the system + default and those supplied to 'add_library_dir()' and/or + 'set_library_dirs()'. 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except of course that they supply command-line arguments @@ -402,6 +414,9 @@ class CCompiler: if self.verbose >= level: print msg + def warn (self, msg): + sys.stderr.write ("warning: %s\n" % msg) + def spawn (self, cmd): spawn (cmd, verbose=self.verbose, dry_run=self.dry_run) @@ -429,7 +444,8 @@ compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler'), def new_compiler (plat=None, compiler=None, verbose=0, - dry_run=0): + dry_run=0, + force=0): """Generate an instance of some CCompiler subclass for the supplied platform/compiler combination. 'plat' defaults to 'os.name' @@ -470,7 +486,7 @@ def new_compiler (plat=None, ("can't compile C/C++ code: unable to find class '%s' " + "in module '%s'") % (class_name, module_name) - return klass (verbose, dry_run) + return klass (verbose, dry_run, force) def gen_preprocess_options (macros, includes): @@ -524,20 +540,18 @@ def gen_preprocess_options (macros, includes): # gen_preprocess_options () -def gen_lib_options (library_dirs, libraries, dir_format, lib_format): +def gen_lib_options (compiler, library_dirs, libraries): """Generate linker options for searching library directories and linking with specific libraries. 'libraries' and 'library_dirs' are, respectively, lists of library names (not filenames!) and - search directories. 'lib_format' is a format string with exactly - one "%s", into which will be plugged each library name in turn; - 'dir_format' is similar, but directory names will be plugged into - it. Returns a list of command-line options suitable for use with - some compiler (depending on the two format strings passed in).""" + search directories. Returns a list of command-line options suitable + for use with some compiler (depending on the two format strings + passed in).""" lib_opts = [] for dir in library_dirs: - lib_opts.append (dir_format % dir) + lib_opts.append (compiler.library_dir_option (dir)) # XXX it's important that we *not* remove redundant library mentions! # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to @@ -546,7 +560,16 @@ def gen_lib_options (library_dirs, libraries, dir_format, lib_format): # pretty nasty way to arrange your C code. for lib in libraries: - lib_opts.append (lib_format % lib) + (lib_dir, lib_name) = os.path.split (lib) + if lib_dir: + lib_file = compiler.find_library_file ([lib_dir], lib_name) + if lib_file: + lib_opts.append (lib_file) + else: + compiler.warn ("no library file corresponding to " + "'%s' found (skipping)" % lib) + else: + lib_opts.append (compiler.library_option (lib)) return lib_opts -- cgit v1.2.1 From 63c863ce22ff96c294f7e57858730b8a400debc9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 20:45:33 +0000 Subject: Fixed order of link options: object files now precede library stuff. Catch up with changes in 'gen_lib_options()': - change how we call it - added methods 'library_dir_option()', 'library_option()', and 'find_library_file()' that it calls Added 'force' flag and changed compile/link methods to respect it. --- unixccompiler.py | 79 +++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 21 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index f3d9591b..8f689196 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -62,9 +62,10 @@ class UnixCCompiler (CCompiler): def __init__ (self, verbose=0, - dry_run=0): + dry_run=0, + force=0): - CCompiler.__init__ (self, verbose, dry_run) + CCompiler.__init__ (self, verbose, dry_run, force) self.preprocess_options = None self.compile_options = None @@ -121,9 +122,10 @@ class UnixCCompiler (CCompiler): # source and object file, no deep dependency checking involving # header files. Hmmm.) objects = self.object_filenames (sources, output_dir) - skipped = newer_pairwise (sources, objects) - for skipped_pair in skipped: - self.announce ("skipping %s (%s up-to-date)" % skipped_pair) + if not self.force: + skipped = newer_pairwise (sources, objects) + for skipped_pair in skipped: + self.announce ("skipping %s (%s up-to-date)" % skipped_pair) # If anything left to compile, compile it if sources: @@ -202,9 +204,9 @@ class UnixCCompiler (CCompiler): if library_dirs is None: library_dirs = [] - lib_opts = gen_lib_options (self.library_dirs + library_dirs, - self.libraries + libraries, - "-L%s", "-l%s") + lib_opts = gen_lib_options (self, + self.library_dirs + library_dirs, + self.libraries + libraries) if output_dir is not None: output_filename = os.path.join (output_dir, output_filename) @@ -213,17 +215,18 @@ class UnixCCompiler (CCompiler): # doesn't look at any of the libraries we might be linking with. # Note that we have to dance around errors comparing timestamps if # we're in dry-run mode (yuck). - try: - newer = newer_group (objects, output_filename) - except OSError: - if self.dry_run: - newer = 1 - else: - raise - - if newer: - ld_args = self.ldflags_shared + lib_opts + \ - objects + ['-o', output_filename] + if not self.force: + try: + newer = newer_group (objects, output_filename) + except OSError: + if self.dry_run: + newer = 1 + else: + raise + + if self.force or newer: + ld_args = self.ldflags_shared + objects + \ + lib_opts + ['-o', output_filename] if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: @@ -232,6 +235,10 @@ class UnixCCompiler (CCompiler): else: self.announce ("skipping %s (up-to-date)" % output_filename) + # link_shared_object () + + + # -- Filename-mangling (etc.) methods ------------------------------ def object_filenames (self, source_filenames, output_dir=None): outnames = [] @@ -250,11 +257,41 @@ class UnixCCompiler (CCompiler): outname = os.path.join (output_dir, outname) return outname + def library_filename (self, libname): - return "lib%s%s" % (libname, self._static_lib_ext ) + return "lib%s%s" % (libname, self._static_lib_ext) def shared_library_filename (self, libname): - return "lib%s%s" % (libname, self._shared_lib_ext ) + return "lib%s%s" % (libname, self._shared_lib_ext) + + + def library_dir_option (self, dir): + return "-L" + dir + + def library_option (self, lib): + return "-l" + lib + + + def find_library_file (self, dirs, lib): + + for dir in dirs: + shared = os.path.join (dir, self.shared_library_filename (lib)) + static = os.path.join (dir, self.library_filename (lib)) + + # We're second-guessing the linker here, with not much hard + # data to go on: GCC seems to prefer the shared library, so I'm + # assuming that *all* Unix C compilers do. And of course I'm + # ignoring even GCC's "-static" option. So sue me. + if os.path.exists (shared): + return shared + elif os.path.exists (static): + return static + + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # find_library_file () # class UnixCCompiler -- cgit v1.2.1 From dba870e5c21cdf14d45ac7f322c75a59a73d249f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 20:47:52 +0000 Subject: Catch up with changes in 'gen_lib_options()': - change how we call it - added methods 'library_dir_option()', 'library_option()', and 'find_library_file()' that it calls Added 'force' flag; it's automatically "respected", because this class always rebuilds everything! (Which it to say, "force=0" is not respected.) --- msvccompiler.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 64b27307..5ac60b2e 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -23,9 +23,10 @@ class MSVCCompiler (CCompiler) : def __init__ (self, verbose=0, - dry_run=0): + dry_run=0, + force=0): - CCompiler.__init__ (self, verbose, dry_run) + CCompiler.__init__ (self, verbose, dry_run, force) # XXX This is a nasty dependency to add on something otherwise # pretty clean. move it to build_ext under an nt specific part. @@ -164,9 +165,9 @@ class MSVCCompiler (CCompiler) : if library_dirs is None: library_dirs = [] - lib_opts = gen_lib_options (self.library_dirs + library_dirs, - self.libraries + libraries, - "/LIBPATH:%s", "%s.lib") + lib_opts = gen_lib_options (self, + self.library_dirs + library_dirs, + self.libraries + libraries) ld_args = self.ldflags_shared + lib_opts + \ objects + ['/OUT:' + output_filename] @@ -200,6 +201,9 @@ class MSVCCompiler (CCompiler) : specified source filename.""" return self._change_extensions( source_filenames, self._shared_lib_ext ) + # XXX ummm... these aren't right, are they? I thought library 'foo' on + # DOS/Windows was to be found in "foo.lib", not "libfoo.lib"! + def library_filename (self, libname): """Return the static library filename corresponding to the specified library name.""" @@ -210,4 +214,25 @@ class MSVCCompiler (CCompiler) : specified library name.""" return "lib%s%s" %( libname, self._shared_lib_ext ) + + def library_dir_option (self, dir): + return "/LIBPATH:" + dir + + def library_option (self, lib): + return self.library_filename (lib) + + + def find_library_file (self, dirs, lib): + + for dir in dirs: + libfile = os.path.join (dir, self.library_filename (lib)) + if os.path.exists (libfile): + return libfile + + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # find_library_file () + # class MSVCCompiler -- cgit v1.2.1 From 364a45f4e008a7361d45a0fdc9a9161d9710f7db Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 20:48:53 +0000 Subject: Hacked to support the notion of "negative alias" options, to handle -q/--quiet reasonably elegantly. --- fancy_getopt.py | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 125dceb3..3df2d1da 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -21,7 +21,12 @@ from distutils.errors import * # the same as a Python NAME -- except, in the spirit of most GNU # utilities, we use '-' in place of '_'. (The spirit of LISP lives on!) # The similarities to NAME are again not a coincidence... -longopt_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9-]*)$') +longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)' +longopt_re = re.compile (r'^%s$' % longopt_pat) + +# For recognizing "negative alias" options, eg. "quiet=!verbose" +neg_alias_re = re.compile ("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) + # This is used to translate long options to legitimate Python identifiers # (for use as attributes of some object). @@ -46,6 +51,7 @@ def fancy_getopt (options, object, args): short2long = {} attr_name = {} takes_arg = {} + neg_alias = {} for option in options: try: @@ -73,7 +79,26 @@ def fancy_getopt (options, object, args): long = long[0:-1] takes_arg[long] = 1 else: - takes_arg[long] = 0 + + # Is option is a "negative alias" for some other option (eg. + # "quiet=!verbose")? + match = neg_alias_re.match (long) + if match: + (alias_from, alias_to) = match.group (1,2) + if not takes_arg.has_key(alias_to) or takes_arg[alias_to]: + raise DistutilsGetoptError, \ + ("option '%s' is a negative alias for '%s', " + + "which either hasn't been defined yet " + + "or takes an argument") % (alias_from, alias_to) + + long = alias_from + neg_alias[long] = alias_to + long_opts[-1] = long + takes_arg[long] = 0 + + else: + takes_arg[long] = 0 + # Now enforce some bondage on the long option name, so we can later # translate it to an attribute name in 'object'. Have to do this a @@ -112,7 +137,11 @@ def fancy_getopt (options, object, args): setattr (object, attr, val) else: if val == '': - setattr (object, attr, 1) + alias = neg_alias.get (opt) + if alias: + setattr (object, attr_name[alias], 0) + else: + setattr (object, attr, 1) else: raise RuntimeError, "getopt lies! (bad value '%s')" % value -- cgit v1.2.1 From ae141dd4aa010194531808f5c954ca947a9dd8b2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 20:50:41 +0000 Subject: Fixed 'mkpath()' to normalize the path right off the bat -- cleans up the code a bit and should make it work under Windows even with trailing backslash. Fixed a couple of docstrings. Added comment about 'make_file()' possibly being redundant and unnecessary. --- util.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/util.py b/util.py index aee95d36..4f93cd44 100644 --- a/util.py +++ b/util.py @@ -37,14 +37,14 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): # the creation of the whole path? (quite easy to do the latter since # we're not using a recursive algorithm) + name = os.path.normpath (name) + if os.path.isdir (name): return if PATH_CREATED.get (name): return (head, tail) = os.path.split (name) - if not tail: # in case 'name' has trailing slash - (head, tail) = os.path.split (head) tails = [tail] # stack of lone dirs to create while head and tail and not os.path.isdir (head): @@ -100,7 +100,6 @@ def newer (source, target): def newer_pairwise (sources, targets): - """Walk two filename lists in parallel, testing if each 'target' is up-to-date relative to its corresponding 'source'. If so, both are deleted from their respective lists. Return a list of tuples @@ -147,6 +146,9 @@ def newer_group (sources, target): # newer_group () +# XXX this isn't used anywhere, and worse, it has the same name as a method +# in Command with subtly different semantics. (This one just has one +# source -> one dest; that one has many sources -> one dest.) Nuke it? def make_file (src, dst, func, args, verbose=0, update_message=None, noupdate_message=None): """Makes 'dst' from 'src' (both filenames) by calling 'func' with @@ -288,7 +290,7 @@ def copy_tree (src, dst, """Copy an entire directory tree 'src' to a new location 'dst'. Both 'src' and 'dst' must be directory names. If 'src' is not a directory, raise DistutilsFileError. If 'dst' does not exist, it - is created with 'mkpath'. The end result of the copy is that + is created with 'mkpath()'. The end result of the copy is that every file in 'src' is copied to 'dst', and directories under 'src' are recursively copied to 'dst'. Return the list of files copied (under their output names) -- note that if 'update' is true, @@ -413,7 +415,7 @@ def move_file (src, dst, def write_file (filename, contents): - """Create a file with the specified naem and write 'contents' (a + """Create a file with the specified name and write 'contents' (a sequence of strings without line terminators) to it.""" f = open (filename, "w") -- cgit v1.2.1 From bf658c0131376f194bac577d8681e4d8038ae131 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 21:02:48 +0000 Subject: Added 'force' and 'quiet' (negative alias for 'verbose') to the global options table. Every Command instance now has its own copies of the global options, which automatically fallback to the Distribution instance. Changes: - initialize them in constructor - added '__getattr__()' to handle the fallback logic - changed every 'self.distribution.{verbose,dry_run}' in Command to 'self.{verbose,dry_run}'. - filesystem utility methods ('copy_file()' et al) don't take 'update' parameter anymore -- instead we pass 'not force' to the underlying function as 'update' Changed parsing of command line so that global options apply to all commands as well -- that's how (eg.) Command.verbose will be initialized. Simplified 'make_file()' to use 'newer_group()' (from util module). Deleted some cruft. Some docstring tweaks. --- core.py | 167 ++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 84 insertions(+), 83 deletions(-) diff --git a/core.py b/core.py index f0c97aa3..c9ccd794 100644 --- a/core.py +++ b/core.py @@ -106,12 +106,21 @@ class Distribution: those described below.""" - # 'global_options' describes the command-line options that may - # be supplied to the client (setup.py) prior to any actual - # commands. Eg. "./setup.py -nv" or "./setup.py --verbose" - # both take advantage of these global options. - global_options = [('verbose', 'v', "run verbosely"), - ('dry-run', 'n', "don't actually do anything"), + # 'global_options' describes the command-line options that may be + # supplied to the client (setup.py) prior to any actual commands. + # Eg. "./setup.py -nv" or "./setup.py --verbose" both take advantage of + # these global options. This list should be kept to a bare minimum, + # since every global option is also valid as a command option -- and we + # don't want to pollute the commands with too many options that they + # have minimal control over. + global_options = [('verbose', 'v', + "run verbosely"), + ('quiet=!verbose', 'q', + "run quietly (turns verbosity off)"), + ('dry-run', 'n', + "don't actually do anything"), + ('force', 'f', + "skip dependency checking between files"), ] @@ -131,6 +140,7 @@ class Distribution: # Default values for our command-line options self.verbose = 0 self.dry_run = 0 + self.force = 0 # And the "distribution meta-data" options -- these can only # come from setup.py (the caller), not the command line @@ -211,23 +221,22 @@ class Distribution: def parse_command_line (self, args): - """Parse the client's command line: set any Distribution + """Parse the setup script's command line: set any Distribution attributes tied to command-line options, create all command objects, and set their options from the command-line. 'args' must be a list of command-line arguments, most likely - 'sys.argv[1:]' (see the 'setup()' function). This list is - first processed for "global options" -- options that set - attributes of the Distribution instance. Then, it is - alternately scanned for Distutils command and options for - that command. Each new command terminates the options for - the previous command. The allowed options for a command are - determined by the 'options' attribute of the command object - -- thus, we instantiate (and cache) every command object - here, in order to access its 'options' attribute. Any error - in that 'options' attribute raises DistutilsGetoptError; any - error on the command-line raises DistutilsArgError. If no - Distutils commands were found on the command line, raises - DistutilsArgError.""" + 'sys.argv[1:]' (see the 'setup()' function). This list is first + processed for "global options" -- options that set attributes of + the Distribution instance. Then, it is alternately scanned for + Distutils command and options for that command. Each new + command terminates the options for the previous command. The + allowed options for a command are determined by the 'options' + attribute of the command object -- thus, we instantiate (and + cache) every command object here, in order to access its + 'options' attribute. Any error in that 'options' attribute + raises DistutilsGetoptError; any error on the command-line + raises DistutilsArgError. If no Distutils commands were found + on the command line, raises DistutilsArgError.""" # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- @@ -268,7 +277,10 @@ class Distribution: "(a list of tuples)") % \ cmd_obj.__class__ - args = fancy_getopt (cmd_obj.options, cmd_obj, args[1:]) + # Poof! like magic, all commands support the global + # options too, just by adding in 'global_options'. + args = fancy_getopt (self.global_options + cmd_obj.options, + cmd_obj, args[1:]) self.command_obj[command] = cmd_obj self.have_run[command] = 0 @@ -497,6 +509,17 @@ class Command: self.distribution = dist self.set_default_options () + # Per-command versions of the global flags, so that the user can + # customize Distutils' behaviour command-by-command and let some + # commands fallback on the Distribution's behaviour. None means + # "not defined, check self.distribution's copy", while 0 or 1 mean + # false and true (duh). Note that this means figuring out the real + # value of each flag is a touch complicatd -- hence "self.verbose" + # (etc.) will be handled by __getattr__, below. + self._verbose = None + self._dry_run = None + self._force = None + # 'ready' records whether or not 'set_final_options()' has been # called. 'set_final_options()' itself should not pay attention to # this flag: it is the business of 'ensure_ready()', which always @@ -506,6 +529,17 @@ class Command: # end __init__ () + def __getattr__ (self, attr): + if attr in ('verbose', 'dry_run', 'force'): + myval = getattr (self, "_" + attr) + if myval is None: + return getattr (self.distribution, attr) + else: + return myval + else: + raise AttributeError, attr + + def ensure_ready (self): if not self.ready: self.set_final_options () @@ -569,7 +603,8 @@ class Command: """If the Distribution instance to which this command belongs has a verbosity level of greater than or equal to 'level' print 'msg' to stdout.""" - if self.distribution.verbose >= level: + + if self.verbose >= level: print msg @@ -720,15 +755,13 @@ class Command: def execute (self, func, args, msg=None, level=1): """Perform some action that affects the outside world (eg. by writing to the filesystem). Such actions are special because - they should be disabled by the "dry run" flag (carried around by - the Command's Distribution), and should announce themselves if - the current verbosity level is high enough. This method takes - care of all that bureaucracy for you; all you have to do is - supply the funtion to call and an argument tuple for it (to - embody the "external action" being performed), a message to - print if the verbosity level is high enough, and an optional - verbosity threshold.""" - + they should be disabled by the "dry run" flag, and should + announce themselves if the current verbosity level is high + enough. This method takes care of all that bureaucracy for you; + all you have to do is supply the funtion to call and an argument + tuple for it (to embody the "external action" being performed), + a message to print if the verbosity level is high enough, and an + optional verbosity threshold.""" # Generate a message if we weren't passed one if msg is None: @@ -740,7 +773,7 @@ class Command: self.announce (msg, level) # And do it, as long as we're not in dry-run mode - if not self.distribution.dry_run: + if not self.dry_run: apply (func, args) # execute() @@ -748,43 +781,45 @@ class Command: def mkpath (self, name, mode=0777): util.mkpath (name, mode, - self.distribution.verbose, self.distribution.dry_run) + self.verbose, self.dry_run) def copy_file (self, infile, outfile, - preserve_mode=1, preserve_times=1, update=1, level=1): - """Copy a file respecting verbose and dry-run flags.""" + preserve_mode=1, preserve_times=1, level=1): + """Copy a file respecting verbose, dry-run and force flags.""" return util.copy_file (infile, outfile, preserve_mode, preserve_times, - update, self.distribution.verbose >= level, - self.distribution.dry_run) + not self.force, + self.verbose >= level, + self.dry_run) def copy_tree (self, infile, outfile, preserve_mode=1, preserve_times=1, preserve_symlinks=0, - update=1, level=1): - """Copy an entire directory tree respecting verbose and dry-run - flags.""" + level=1): + """Copy an entire directory tree respecting verbose, dry-run, + and force flags.""" return util.copy_tree (infile, outfile, preserve_mode,preserve_times,preserve_symlinks, - update, self.distribution.verbose >= level, - self.distribution.dry_run) + not self.force, + self.verbose >= level, + self.dry_run) def move_file (self, src, dst, level=1): """Move a file respecting verbose and dry-run flags.""" return util.move_file (src, dst, - self.distribution.verbose >= level, - self.distribution.dry_run) + self.verbose >= level, + self.dry_run) def spawn (self, cmd, search_path=1, level=1): from distutils.spawn import spawn spawn (cmd, search_path, - self.distribution.verbose >= level, - self.distribution.dry_run) + self.verbose >= level, + self.dry_run) def make_file (self, infiles, outfile, func, args, @@ -811,29 +846,10 @@ class Command: raise TypeError, \ "'infiles' must be a string, or a list or tuple of strings" - # XXX this stuff should probably be moved off to a function - # in 'distutils.util' - from stat import * - - if os.path.exists (outfile): - out_mtime = os.stat (outfile)[ST_MTIME] - - # Loop over all infiles. If any infile is newer than outfile, - # then we'll have to regenerate outfile - for f in infiles: - in_mtime = os.stat (f)[ST_MTIME] - if in_mtime > out_mtime: - runit = 1 - break - else: - runit = 0 - - else: - runit = 1 - - # If we determined that 'outfile' must be regenerated, then + # If 'outfile' must be regenerated (either because it doesn't + # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if runit: + if self.force or newer_group (infiles, outfile): self.execute (func, args, exec_msg, level) # Otherwise, print the "skip" message @@ -842,19 +858,4 @@ class Command: # make_file () - -# def make_files (self, infiles, outfiles, func, args, -# exec_msg=None, skip_msg=None, level=1): - -# """Special case of 'execute()' for operations that process one or -# more input files and generate one or more output files. Works -# just like 'execute()', except the operation is skipped and a -# different message printed if all files listed in 'outfiles' -# already exist and are newer than all files listed in -# 'infiles'.""" - -# pass - - - # end class Command -- cgit v1.2.1 From 5fb0798f6108306b956fbb5628ab020b00b0f570 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 21:03:26 +0000 Subject: Tweaked verbosity messages for byte-compilation. --- command/install_lib.py | 4 ++-- command/install_py.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index 978bb3b9..35deaa81 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -66,8 +66,8 @@ class InstallPy (Command): out_fn = string.replace (f, '.py', '.pyc') self.make_file (f, out_fn, compile, (f,), - "compiling %s -> %s" % (f, out_fn), - "compilation of %s skipped" % f) + "byte-compiling %s" % f, + "byte-compilation of %s skipped" % f) # XXX ignore self.optimize for now, since we don't really know if # we're compiling optimally or not, and couldn't pick what to do diff --git a/command/install_py.py b/command/install_py.py index 978bb3b9..35deaa81 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -66,8 +66,8 @@ class InstallPy (Command): out_fn = string.replace (f, '.py', '.pyc') self.make_file (f, out_fn, compile, (f,), - "compiling %s -> %s" % (f, out_fn), - "compilation of %s skipped" % f) + "byte-compiling %s" % f, + "byte-compilation of %s skipped" % f) # XXX ignore self.optimize for now, since we don't really know if # we're compiling optimally or not, and couldn't pick what to do -- cgit v1.2.1 From c83db037302c04d9b2510552bcc4e75d5bf6a2ee Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 21:07:21 +0000 Subject: Don't import what we don't use. --- command/build_py.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_py.py b/command/build_py.py index 3a7a43bb..4067ca4c 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -12,7 +12,7 @@ from glob import glob from distutils.core import Command from distutils.errors import * -from distutils.util import mkpath, newer, make_file, copy_file +from distutils.util import mkpath, copy_file class BuildPy (Command): -- cgit v1.2.1 From 04b548270ee36bdff4114fc797e84025925a0ef4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 21:08:42 +0000 Subject: Pass 'force' flag to 'new_compiler()'. --- command/build_ext.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 8c5065f6..f284092b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -121,8 +121,9 @@ class BuildExt (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking self.compiler = new_compiler (plat=os.environ.get ('PLAT'), - verbose=self.distribution.verbose, - dry_run=self.distribution.dry_run) + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) if self.include_dirs is not None: self.compiler.set_include_dirs (self.include_dirs) if self.define is not None: -- cgit v1.2.1 From 95fff421e1c4f3293d15344a07f2361e4742b5b4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 21:09:14 +0000 Subject: Filter 'glob()' results so we only look at regular files. --- command/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/dist.py b/command/dist.py index 3f309745..99a05225 100644 --- a/command/dist.py +++ b/command/dist.py @@ -220,7 +220,7 @@ class Dist (Command): optional = ['test/test*.py'] for pattern in optional: - files = glob (pattern) + files = filter (os.path.isfile, glob (pattern)) if files: self.files.extend (files) @@ -338,7 +338,7 @@ class Dist (Command): # Single word, no bang: it's a "simple include pattern" elif not exclude: - matches = glob (pattern) + matches = filter (os.path.isfile, glob (pattern)) if matches: self.files.extend (matches) else: -- cgit v1.2.1 From 97cc7bbe9d4650bac48da8831dd70d987fb4562e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Oct 1999 19:06:20 +0000 Subject: Fix how we run 'zip' -- give explicit .zip extension. --- command/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/dist.py b/command/dist.py index 99a05225..959413f4 100644 --- a/command/dist.py +++ b/command/dist.py @@ -405,7 +405,7 @@ class Dist (Command): # on Redmond's archaic CP/M knockoff is nowadays), but I'll let # someone who can actually test it do that. - self.spawn (["zip", "-r", base_dir, base_dir]) + self.spawn (["zip", "-r", base_dir + ".zip", base_dir]) def make_distribution (self): -- cgit v1.2.1 From d2649fd50531766567cef19ae50a6760f411cf9b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Oct 1999 19:06:56 +0000 Subject: Qualified use of 'newer_group' function. --- core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.py b/core.py index c9ccd794..23fc3cca 100644 --- a/core.py +++ b/core.py @@ -849,7 +849,7 @@ class Command: # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if self.force or newer_group (infiles, outfile): + if self.force or util.newer_group (infiles, outfile): self.execute (func, args, exec_msg, level) # Otherwise, print the "skip" message -- cgit v1.2.1 From 78805a7cc9ab2cf178276ca4b84b1d61ad29a6f5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Oct 1999 19:10:59 +0000 Subject: Removed massive comment speculating about needlessly complex variations on the manifest file syntax. --- command/dist.py | 139 -------------------------------------------------------- 1 file changed, 139 deletions(-) diff --git a/command/dist.py b/command/dist.py index 959413f4..57db1d78 100644 --- a/command/dist.py +++ b/command/dist.py @@ -457,142 +457,3 @@ def findall (dir = os.curdir): list.sort() return list - - - - - -# ====================================================================== -# Here follows some extensive mental masturbation about how to -# make the manifest file and search algorithm even more complex. -# I think this is all gratuitous, really. - -# Hmm, something extra: want to apply an exclude pattern over a whole -# subtree without necessarily having to explicitly include files from it, -# ie. it should apply after gathering files by other means (simple -# include pattern) -# . !*~ !*.bak !#*# -# and we also want to prune at certain directories: -# . !RCS !CVS -# which again should apply globally. -# -# possible solution: -# - exclude pattern in a directory applies to all files found under that -# directory -# - subdirectories that match an exclude pattern will be pruned -# - hmmm, to be consistent, subdirectories that match an include -# pattern should be recursively included -# - and this should apply to "simple" patterns too -# -# thus: -# -# examples/ -# -# means get everything in examples/ and all subdirs; -# -# examples/ !*~ !#*# !*.py[co] -# -# means get everything under examples/ except files matching those three globs; -# -# ./ !RCS !CVS -# -# means get everything under current dir, but prune RCS/CVS directories; -# -# ./ !*~ !#*# !*.py[co] !RCS !CVS -# ! build/ -# ! experimental/ -# -# means get everything under the distribution directory except the usual -# excludes at all levels; exclude "build" and "experimental" under the -# distribution dir only. -# -# Do the former examples still work? -# -# distutils/ *.py -# ! distutils/bleeding_edge.py -# -# means all .py files recursively found under distutils, except for the one -# explicitly named. -# -# distutils/ *.py !bleeding_edge.py -# -# means the same, except bleeding_edge.py will be excluded wherever it's -# found -- thus this can exclude up to one file per directory under -# distutils. -# -# distutils/*.py -# ! distutils/bleeding_edge.py -# -# gets exactly distutils/*.py, minus the one explicitly mentioned exclude, and -# -# distutils/*.py -# distutils/ !bleeding_edge.py -# -# coincidentally does the same, but only because there can only be one file -# that matches the exclude pattern. Oh, we'd still like -# -# distutils *.py !bleeding*.py -# distutils/bleeding_ledge.py -# -# to include distutils/bleeding_ledge.py -- i.e. it should override the -# earlier exclude pattern by virtue of appearing later in the manifest. Does -# this conflict with the above requirements, ie. that "!RCS" and "!*~" should -# apply everywhere? Hmm, I think it doesn't have to, as long as we're smart -# about it. Consequence: -# -# . !RCS !CVS -# distutils * -# -# will go ahead and include RCS and CVS files under distutils, but -# -# distutils * -# . !RCS !CVS -# -# will do the right thing. Hmmm. I think that's OK, and an inevitable -# consequence of the ability to override exclusions. - -# OK, new crack at the search algorithm. -# -# for pattern in manifest: -# if dir-pattern: # ie. first word is a directory (incl. "."!) -# dir = first word on line -# patterns = rest of line -# if patterns: -# for dpattern in patterns: -# if exclude-pattern: -# remove from files anything matching dpattern (including pruning -# subtrees rooted at directories that match dpattern) -# else: -# files.append (recursive_glob (dir, dpattern)) -# else: -# files.append (recursive_glob (dir, '*') -# -# elif include-pattern: # it's a "simple include pattern" -# files.append (glob (pattern)) -# -# else: # it's a "simple exclude pattern" -# remove from files anything matching pattern - -# The two removal algorithms might be a bit tricky: -# -# "remove simple exclude pattern": -# for f in files: -# if f matches pattern: -# delete it -# -# "remove recursive exclude pattern": -# for f in files: -# -# t = tail (f) -# while t: -# if t matches pattern: -# delete current file -# continue -# t = tail (t) -# -# Well, that was an interesting mental exercise. I'm not completely -# convinced it will work, nor am I convinced this level of complexity -# is necessary. If you want to exclude RCS or CVS directories, just -# don't bloody include them! - - -- cgit v1.2.1 From 86d2c36a34934224784e597575f6d33bf0e426b4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Oct 1999 19:25:05 +0000 Subject: Don't assume GNU tar -- generate tar file and compress in separate steps. Now supports the full range of intended formats (tar, ztar, gztar, zip). "-f" no longer a short option for "--formats" -- conflicts with new global option "--force"! --- command/dist.py | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/command/dist.py b/command/dist.py index 57db1d78..abbd625b 100644 --- a/command/dist.py +++ b/command/dist.py @@ -129,7 +129,7 @@ from distutils.text_file import TextFile class Dist (Command): - options = [('formats=', 'f', + options = [('formats=', None, "formats for source distribution (tar, ztar, gztar, or zip)"), ('manifest=', 'm', "name of manifest file"), @@ -385,17 +385,23 @@ class Dist (Command): # make_release_tree () - def make_tarball (self, base_dir): + def make_tarball (self, base_dir, compress="gzip"): # XXX GNU tar 1.13 has a nifty option to add a prefix directory. - # It's pretty new, though, so we certainly can't require it -- but - # it would be nice to take advantage of it to skip the "create a - # tree of hardlinks" step! + # It's pretty new, though, so we certainly can't require it -- + # but it would be nice to take advantage of it to skip the + # "create a tree of hardlinks" step! (Would also be nice to + # detect GNU tar to use its 'z' option and save a step.) - # But I am a lazy bastard, so I require GNU tar anyways. + if compress is not None and compress not in ('gzip', 'compress'): + raise ValueError, \ + "if given, 'compress' must be 'gzip' or 'compress'" - archive_name = base_dir + ".tar.gz" - self.spawn (["tar", "-czf", archive_name, base_dir]) + archive_name = base_dir + ".tar" + self.spawn (["tar", "-cf", archive_name, base_dir]) + + if compress: + self.spawn ([compress, archive_name]) def make_zipfile (self, base_dir): @@ -425,10 +431,15 @@ class Dist (Command): self.exclude_files (base_dir + "*") self.make_release_tree (base_dir, self.files) - if 'gztar' in self.formats: - self.make_tarball (base_dir) - if 'zip' in self.formats: - self.make_zipfile (base_dir) + for fmt in self.formats: + if fmt == 'gztar': + self.make_tarball (base_dir, compress='gzip') + elif fmt == 'ztar': + self.make_tarball (base_dir, compress='compress') + elif fmt == 'tar': + self.make_tarball (base_dir, compress=None) + elif fmt == 'zip': + self.make_zipfile (base_dir) # class Dist -- cgit v1.2.1 From 2bfd1886680134fd1e76d6260abd7388c35079ca Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 3 Dec 1999 16:18:56 +0000 Subject: [from 1999-11-04] Bunch of little bug fixes that appeared in building non-packagized distributions. Mainly: - brain-slip typo in 'get_package_dir()' - don't try to os.path.join() an empty path tuple -- it doesn't like it - more type-safety in 'build_module()' --- command/build_py.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 4067ca4c..b3cc1e95 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -93,12 +93,15 @@ class BuildPy (Command): if type (package) is StringType: path = string.split (package, '.') elif type (package) in (TupleType, ListType): - path = list (path) + path = list (package) else: raise TypeError, "'package' must be a string, list, or tuple" if not self.package_dir: - return apply (os.path.join, path) + if path: + return apply (os.path.join, path) + else: + return '' else: tail = [] while path: @@ -113,7 +116,10 @@ class BuildPy (Command): else: # arg! everything failed, we might as well have not even # looked in package_dir -- oh well - return apply (os.path.join, tail) + if tail: + return apply (os.path.join, tail) + else: + return '' # get_package_dir () @@ -134,7 +140,7 @@ class BuildPy (Command): "but is not a directory") % package_dir # Require __init__.py for all but the "root package" - if package != "": + if package: init_py = os.path.join (package_dir, "__init__.py") if not os.path.isfile (init_py): self.warn (("package init file '%s' not found " + @@ -233,11 +239,14 @@ class BuildPy (Command): if type (package) is StringType: package = string.split (package, '.') + elif type (package) not in (ListType, TupleType): + raise TypeError, \ + "'package' must be a string (dot-separated), list, or tuple" # Now put the module source file into the "build" area -- this is # easy, we just copy it somewhere under self.build_dir (the build # directory for Python source). - outfile_path = package + outfile_path = list (package) outfile_path.append (module + ".py") outfile_path.insert (0, self.build_dir) outfile = apply (os.path.join, outfile_path) -- cgit v1.2.1 From 948163cdcbc541ae0bd3f7af825adad6062cb0e5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 16:51:44 +0000 Subject: Made "verbose" mode the default; now you have to supply --quiet if you want no output. Still no option for a happy medium though. Added "--help" global option. Changed 'parse_command_line()' to recognize help options (both for the whole distribution and per-command), and to distinguish "regular run" and "user asked for help" by returning false in the latter case. Also in 'parse_command_line()', detect invalid command name on command line by catching DistutilsModuleError. a 'negative_opt' class attribute right after 'global_options'; changed how we call 'fancy_getopt()' accordingly. Initialize 'maintainer' and 'maintainer_email' attributes to Distribution to avoid AttributeError when 'author' and 'author_email' not defined. Initialize 'help' attribute in Command constructor (to avoid AttributeError when user *doesn't* ask for help). In 'setup()': * show usage message before dying when we catch DistutilsArgError * only run commands if 'parse_command_line()' returned true (that way, we exit immediately when a help option is found) * catch KeyboardInterrupt and IOError from running commands Bulked up usage message to show --help options. Comment, docstring, and error message tweaks. --- core.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 18 deletions(-) diff --git a/core.py b/core.py index 23fc3cca..13bf9c7f 100644 --- a/core.py +++ b/core.py @@ -13,19 +13,25 @@ __rcsid__ = "$Id$" import sys, os import string, re from types import * +from copy import copy from distutils.errors import * -from distutils.fancy_getopt import fancy_getopt +from distutils.fancy_getopt import fancy_getopt, print_help from distutils import util -# This is not *quite* the same as a Python NAME; I don't allow leading -# underscores. The fact that they're very similar is no coincidence... +# Regex to define acceptable Distutils command names. This is not *quite* +# the same as a Python NAME -- I don't allow leading underscores. The fact +# that they're very similar is no coincidence; the default naming scheme is +# to look for a Python module named after the command. command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') # Defining this as a global is probably inadequate -- what about # listing the available options (or even commands, which can vary # quite late as well) -usage = '%s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]' % sys.argv[0] - +usage = """\ +usage: %s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] + or: %s --help + or: %s cmd --help +""" % (sys.argv[0], sys.argv[0], sys.argv[0]) def setup (**attrs): @@ -79,12 +85,20 @@ def setup (**attrs): # Parse the command line; any command-line errors are the end-users # fault, so turn them into SystemExit to suppress tracebacks. try: - dist.parse_command_line (sys.argv[1:]) + ok = dist.parse_command_line (sys.argv[1:]) except DistutilsArgError, msg: + sys.stderr.write (usage + "\n") raise SystemExit, msg # And finally, run all the commands found on the command line. - dist.run_commands () + if ok: + try: + dist.run_commands () + except KeyboardInterrupt: + raise SystemExit, "interrupted" + except IOError, exc: + # is this 1.5.2-specific? 1.5-specific? + raise SystemExit, "error: %s: %s" % (exc.filename, exc.strerror) # setup () @@ -114,14 +128,17 @@ class Distribution: # don't want to pollute the commands with too many options that they # have minimal control over. global_options = [('verbose', 'v', - "run verbosely"), - ('quiet=!verbose', 'q', + "run verbosely (default)"), + ('quiet', 'q', "run quietly (turns verbosity off)"), ('dry-run', 'n', "don't actually do anything"), ('force', 'f', "skip dependency checking between files"), + ('help', 'h', + "show this help message"), ] + negative_opt = {'quiet': 'verbose'} # -- Creation/initialization methods ------------------------------- @@ -138,17 +155,20 @@ class Distribution: command objects by 'parse_command_line()'.""" # Default values for our command-line options - self.verbose = 0 + self.verbose = 1 self.dry_run = 0 self.force = 0 + self.help = 0 # And the "distribution meta-data" options -- these can only # come from setup.py (the caller), not the command line - # (or a hypothetical config file).. + # (or a hypothetical config file). self.name = None self.version = None self.author = None self.author_email = None + self.maintainer = None + self.maintainer_email = None self.url = None self.licence = None self.description = None @@ -236,7 +256,11 @@ class Distribution: 'options' attribute. Any error in that 'options' attribute raises DistutilsGetoptError; any error on the command-line raises DistutilsArgError. If no Distutils commands were found - on the command line, raises DistutilsArgError.""" + on the command line, raises DistutilsArgError. Return true if + command-line successfully parsed and we should carry on with + executing commands; false if no errors but we shouldn't execute + commands (currently, this only happens if user asks for + help).""" # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- @@ -246,7 +270,14 @@ class Distribution: # happen until we know what the command is. self.commands = [] - args = fancy_getopt (self.global_options, self, sys.argv[1:]) + args = fancy_getopt (self.global_options, self.negative_opt, + self, sys.argv[1:]) + + if self.help: + print_help (self.global_options, header="Global options:") + print + print usage + return while args: # Pull the current command from the head of the command line @@ -258,7 +289,10 @@ class Distribution: # Make sure we have a command object to put the options into # (this either pulls it out of a cache of command objects, # or finds and instantiates the command class). - cmd_obj = self.find_command_obj (command) + try: + cmd_obj = self.find_command_obj (command) + except DistutilsModuleError, msg: + raise DistutilsArgError, msg # Require that the command class be derived from Command -- # that way, we can be sure that we at least have the 'run' @@ -279,8 +313,24 @@ class Distribution: # Poof! like magic, all commands support the global # options too, just by adding in 'global_options'. - args = fancy_getopt (self.global_options + cmd_obj.options, + negative_opt = self.negative_opt + if hasattr (cmd_obj, 'negative_opt'): + negative_opt = copy (negative_opt) + negative_opt.update (cmd_obj.negative_opt) + + options = self.global_options + cmd_obj.options + args = fancy_getopt (options, negative_opt, cmd_obj, args[1:]) + if cmd_obj.help: + print_help (self.global_options, + header="Global options:") + print + print_help (cmd_obj.options, + header="Options for '%s' command:" % command) + print + print usage + return + self.command_obj[command] = cmd_obj self.have_run[command] = 0 @@ -288,9 +338,11 @@ class Distribution: # Oops, no commands found -- an end-user error if not self.commands: - sys.stderr.write (usage + "\n") raise DistutilsArgError, "no commands supplied" + # All is well: return true + return 1 + # parse_command_line() @@ -318,7 +370,7 @@ class Distribution: module = sys.modules[module_name] except ImportError: raise DistutilsModuleError, \ - "invalid command '%s' (no module named %s)" % \ + "invalid command '%s' (no module named '%s')" % \ (command, module_name) try: @@ -359,7 +411,8 @@ class Distribution: 'create_command_obj()'. If none found, the action taken depends on 'create': if true (the default), create a new command object by calling 'create_command_obj()' and return - it; otherwise, return None.""" + it; otherwise, return None. If 'command' is an invalid + command name, then DistutilsModuleError will be raised.""" cmd_obj = self.command_obj.get (command) if not cmd_obj and create: @@ -520,6 +573,10 @@ class Command: self._dry_run = None self._force = None + # The 'help' flag is just used for command-line parsing, so + # none of that complicated bureaucracy is needed. + self.help = 0 + # 'ready' records whether or not 'set_final_options()' has been # called. 'set_final_options()' itself should not pay attention to # this flag: it is the business of 'ensure_ready()', which always -- cgit v1.2.1 From 8fa8e3f254c307213ed4ebd194454e144578cd64 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 16:54:55 +0000 Subject: Added support for printing out help text from option table: 'print_help()', 'generate_help()', 'wrap_text()' functions, and a little tiny test of 'wrap_text()'. Changed how caller states that one option is the boolean opposite of another: added 'negative_opt' parameter to 'fancy_getopt()', and changed to use it instead of parsing long option name. --- fancy_getopt.py | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 171 insertions(+), 12 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 3df2d1da..86e9f326 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -12,7 +12,7 @@ additional features: __rcsid__ = "$Id$" -import string, re +import sys, string, re from types import * import getopt from distutils.errors import * @@ -33,7 +33,7 @@ neg_alias_re = re.compile ("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) longopt_xlate = string.maketrans ('-', '_') -def fancy_getopt (options, object, args): +def fancy_getopt (options, negative_opt, object, args): # The 'options' table is a list of 3-tuples: # (long_option, short_option, help_string) @@ -51,7 +51,6 @@ def fancy_getopt (options, object, args): short2long = {} attr_name = {} takes_arg = {} - neg_alias = {} for option in options: try: @@ -81,18 +80,15 @@ def fancy_getopt (options, object, args): else: # Is option is a "negative alias" for some other option (eg. - # "quiet=!verbose")? - match = neg_alias_re.match (long) - if match: - (alias_from, alias_to) = match.group (1,2) + # "quiet" == "!verbose")? + alias_to = negative_opt.get(long) + if alias_to is not None: if not takes_arg.has_key(alias_to) or takes_arg[alias_to]: raise DistutilsGetoptError, \ ("option '%s' is a negative alias for '%s', " + "which either hasn't been defined yet " + - "or takes an argument") % (alias_from, alias_to) + "or takes an argument") % (long, alias_to) - long = alias_from - neg_alias[long] = alias_to long_opts[-1] = long takes_arg[long] = 0 @@ -137,7 +133,7 @@ def fancy_getopt (options, object, args): setattr (object, attr, val) else: if val == '': - alias = neg_alias.get (opt) + alias = negative_opt.get (opt) if alias: setattr (object, attr_name[alias], 0) else: @@ -149,4 +145,167 @@ def fancy_getopt (options, object, args): return args -# end fancy_getopt() +# fancy_getopt() + + +WS_TRANS = string.maketrans (string.whitespace, ' ' * len (string.whitespace)) + +def wrap_text (text, width): + + if text is None: + return [] + if len (text) <= width: + return [text] + + text = string.expandtabs (text) + text = string.translate (text, WS_TRANS) + chunks = re.split (r'( +|-+)', text) + chunks = filter (None, chunks) # ' - ' results in empty strings + lines = [] + + while chunks: + + cur_line = [] # list of chunks (to-be-joined) + cur_len = 0 # length of current line + + while chunks: + l = len (chunks[0]) + if cur_len + l <= width: # can squeeze (at least) this chunk in + cur_line.append (chunks[0]) + del chunks[0] + cur_len = cur_len + l + else: # this line is full + # drop last chunk if all space + if cur_line and cur_line[-1][0] == ' ': + del cur_line[-1] + break + + if chunks: # any chunks left to process? + + # if the current line is still empty, then we had a single + # chunk that's too big too fit on a line -- so we break + # down and break it up at the line width + if cur_len == 0: + cur_line.append (chunks[0][0:width]) + chunks[0] = chunks[0][width:] + + # all-whitespace chunks at the end of a line can be discarded + # (and we know from the re.split above that if a chunk has + # *any* whitespace, it is *all* whitespace) + if chunks[0][0] == ' ': + del chunks[0] + + # and store this line in the list-of-all-lines -- as a single + # string, of course! + lines.append (string.join (cur_line, '')) + + # while chunks + + return lines + +# wrap_text () + + +def generate_help (options, header=None): + """Generate help text (a list of strings, one per suggested line of + output) from an option table.""" + + # Blithely assume the option table is good: probably wouldn't call + # 'generate_help()' unless you've already called 'fancy_getopt()'. + + # First pass: determine maximum length of long option names + max_opt = 0 + for option in options: + long = option[0] + short = option[1] + l = len (long) + if long[-1] == '=': + l = l - 1 + if short is not None: + l = l + 5 # " (-x)" where short == 'x' + if l > max_opt: + max_opt = l + + opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter + + # Typical help block looks like this: + # --foo controls foonabulation + # Help block for longest option looks like this: + # --flimflam set the flim-flam level + # and with wrapped text: + # --flimflam set the flim-flam level (must be between + # 0 and 100, except on Tuesdays) + # Options with short names will have the short name shown (but + # it doesn't contribute to max_opt): + # --foo (-f) controls foonabulation + # If adding the short option would make the left column too wide, + # we push the explanation off to the next line + # --flimflam (-l) + # set the flim-flam level + # Important parameters: + # - 2 spaces before option block start lines + # - 2 dashes for each long option name + # - min. 2 spaces between option and explanation (gutter) + # - 5 characters (incl. space) for short option name + + # Now generate lines of help text. + line_width = 78 # if 80 columns were good enough for + text_width = line_width - opt_width # Jesus, then 78 are good enough for me + big_indent = ' ' * opt_width + if header: + lines = [header] + else: + lines = ['Option summary:'] + + for (long,short,help) in options: + + text = wrap_text (help, text_width) + if long[-1] == '=': + long = long[0:-1] + + # Case 1: no short option at all (makes life easy) + if short is None: + if text: + lines.append (" --%-*s %s" % (max_opt, long, text[0])) + else: + lines.append (" --%-*s " % (max_opt, long)) + + for l in text[1:]: + lines.append (big_indent + l) + + # Case 2: we have a short option, so we have to include it + # just after the long option + else: + opt_names = "%s (-%s)" % (long, short) + if text: + lines.append (" --%-*s %s" % + (max_opt, opt_names, text[0])) + else: + lines.append (" --%-*s" % opt_names) + + # for loop over options + + return lines + +# generate_help () + + +def print_help (options, file=None, header=None): + if file is None: + file = sys.stdout + for line in generate_help (options, header): + file.write (line + "\n") +# print_help () + + +if __name__ == "__main__": + text = """\ +Tra-la-la, supercalifragilisticexpialidocious. +How *do* you spell that odd word, anyways? +(Someone ask Mary -- she'll know [or she'll +say, "How should I know?"].)""" + + for w in (10, 20, 30, 40): + print "width: %d" % w + print string.join (wrap_text (text, w), "\n") + print -- cgit v1.2.1 From 6b3ff9eef6bd4d9f99572022a776b7874097466b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 16:57:47 +0000 Subject: In 'compile()' method, renamed 'includes' parameter to 'include_dirs' for consistency with 'build_ext' command option. Changed 'compile()' and 'link_shared_object()' so 'include_dirs', 'libraries', and 'library_dirs' can be lists or tuples. --- unixccompiler.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 8f689196..edff4f0a 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -92,7 +92,7 @@ class UnixCCompiler (CCompiler): sources, output_dir=None, macros=None, - includes=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): @@ -100,18 +100,19 @@ class UnixCCompiler (CCompiler): output_dir = self.output_dir if macros is None: macros = [] - if includes is None: - includes = [] + if include_dirs is None: + include_dirs = [] if type (macros) is not ListType: raise TypeError, \ "'macros' (if supplied) must be a list of tuples" - if type (includes) is not ListType: + if type (include_dirs) not in (ListType, TupleType): raise TypeError, \ - "'includes' (if supplied) must be a list of strings" + "'include_dirs' (if supplied) must be a list of strings" + include_dirs = list (include_dirs) pp_opts = gen_preprocess_options (self.macros + macros, - self.include_dirs + includes) + self.include_dirs + include_dirs) # So we can mangle 'sources' without hurting the caller's data orig_sources = sources @@ -204,6 +205,15 @@ class UnixCCompiler (CCompiler): if library_dirs is None: library_dirs = [] + if type (libraries) not in (ListType, TupleType): + raise TypeError, \ + "'libraries' (if supplied) must be a list of strings" + if type (library_dirs) not in (ListType, TupleType): + raise TypeError, \ + "'library_dirs' (if supplied) must be a list of strings" + libraries = list (libraries) + library_dirs = list (library_dirs) + lib_opts = gen_lib_options (self, self.library_dirs + library_dirs, self.libraries + libraries) -- cgit v1.2.1 From c2cadc8e316e5b60fb44864110d970f068e977b2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 17:01:01 +0000 Subject: Changed 'build_extensions()' so 'sources' can be a list or tuple; and call CCompiler method 'compile()' with 'include_dirs' not 'includes'. Fixed stupid typo in 'get_source_files()'. --- command/build_ext.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index f284092b..d38cb18b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -179,7 +179,7 @@ class BuildExt (Command): filenames = [] # Wouldn't it be neat if we knew the names of header files too... - for (extension_name, build_info) in extensions: + for (extension_name, build_info) in self.extensions: sources = build_info.get ('sources') if type (sources) in (ListType, TupleType): filenames.extend (sources) @@ -191,10 +191,11 @@ class BuildExt (Command): for (extension_name, build_info) in extensions: sources = build_info.get ('sources') - if sources is None or type (sources) is not ListType: + if sources is None or type (sources) not in (ListType, TupleType): raise DistutilsValueError, \ "in ext_modules option, 'sources' must be present " + \ "and must be a list of source filenames" + sources = list (sources) # First step: compile the source code to object files. This # drops the object files in the current directory, regardless @@ -205,7 +206,7 @@ class BuildExt (Command): include_dirs = build_info.get ('include_dirs') self.compiler.compile (sources, macros=macros, - includes=include_dirs) + include_dirs=include_dirs) # Now link the object files together into a "shared object" -- # of course, first we have to figure out all the other things -- cgit v1.2.1 From 749d2ea556fa8790ab5c5188aa163ba170b07dee Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 17:03:59 +0000 Subject: Fixed 'find_package_modules()' to ensure that we never build (and thus install) the setup script itself. Fixed 'build_module()' so we do *not* preserve file mode (which means we can install read-only files, which makes the next installation of this distribution fail -- at least under Unix); added a comment explaining this. --- command/build_py.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index b3cc1e95..6bc5aa80 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -6,7 +6,7 @@ Implements the Distutils 'build_py' command.""" __rcsid__ = "$Id$" -import string, os +import sys, string, os from types import * from glob import glob @@ -40,11 +40,21 @@ class BuildPy (Command): def run (self): - # XXX copy_file by default preserves all stat info -- mode, atime, - # and mtime. IMHO this is the right thing to do, but perhaps it - # should be an option -- in particular, a site administrator might - # want installed files to reflect the time of installation rather - # than the last modification time before the installed release. + # XXX copy_file by default preserves atime and mtime. IMHO this is + # the right thing to do, but perhaps it should be an option -- in + # particular, a site administrator might want installed files to + # reflect the time of installation rather than the last + # modification time before the installed release. + + # XXX copy_file by default preserves mode, which appears to be the + # wrong thing to do: if a file is read-only in the working + # directory, we want it to be installed read/write so that the next + # installation of the same module distribution can overwrite it + # without problems. (This might be a Unix-specific issue.) Thus + # we turn off 'preserve_mode' when copying to the build directory, + # since the build directory is supposed to be exactly what the + # installation will look like (ie. we preserve mode when + # installing). # XXX copy_file does *not* preserve MacOS-specific file metadata. # If this is a problem for building/installing Python modules, then @@ -73,7 +83,7 @@ class BuildPy (Command): if self.modules and self.packages: raise DistutilsOptionError, \ "build_py: supplying both 'packages' and 'modules' " + \ - "options not allowed" + "options is not allowed" # Now we're down to two cases: 'modules' only and 'packages' only. if self.modules: @@ -81,7 +91,6 @@ class BuildPy (Command): else: self.build_packages () - # run () @@ -162,9 +171,13 @@ class BuildPy (Command): def find_package_modules (self, package, package_dir): module_files = glob (os.path.join (package_dir, "*.py")) module_pairs = [] + setup_script = os.path.abspath (sys.argv[0]) + for f in module_files: - module = os.path.splitext (os.path.basename (f))[0] - module_pairs.append (module, f) + abs_f = os.path.abspath (f) + if abs_f != setup_script: + module = os.path.splitext (os.path.basename (f))[0] + module_pairs.append ((module, f)) return module_pairs @@ -253,7 +266,7 @@ class BuildPy (Command): dir = os.path.dirname (outfile) self.mkpath (dir) - self.copy_file (module_file, outfile) + self.copy_file (module_file, outfile, preserve_mode=0) def build_modules (self): -- cgit v1.2.1 From a65fd32d4d8280c553b631c6407010b271634468 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 17:07:22 +0000 Subject: Catch missing MANIFEST file and warn rather than blowing up. Added 'nuke_release_tree()' method to blow away the directory from which the archive file(s) are created, and call it (conditionally) from 'make_distribution()'. Added 'keep_tree' option (false by default) to disable the call to 'nuke_release_tree()'. --- command/dist.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/command/dist.py b/command/dist.py index abbd625b..cdd4dfc8 100644 --- a/command/dist.py +++ b/command/dist.py @@ -10,6 +10,7 @@ import sys, os, string, re import fnmatch from types import * from glob import glob +from shutil import rmtree from distutils.core import Command from distutils.text_file import TextFile @@ -135,6 +136,9 @@ class Dist (Command): "name of manifest file"), ('list-only', 'l', "just list files that would be distributed"), + ('keep-tree', 'k', + "keep the distribution tree around after creating " + + "archive file(s)"), ] default_format = { 'posix': 'gztar', @@ -147,6 +151,7 @@ class Dist (Command): self.formats = None self.manifest = None self.list_only = 0 + self.keep_tree = 0 def set_final_options (self): @@ -202,8 +207,8 @@ class Dist (Command): self.warn ("missing meta-data: if 'maintainer' supplied, " + "'maintainer_email' must be supplied too") else: - self.warn ("missing meta-data: either author (and author_email) " + - "or maintainer (and maintainer_email) " + + self.warn ("missing meta-data: either (author and author_email) " + + "or (maintainer and maintainer_email) " + "must be supplied") # check_metadata () @@ -296,7 +301,18 @@ class Dist (Command): # README, setup script, ...) assert self.files is not None - manifest = self.open_manifest (self.manifest) + try: + manifest = self.open_manifest (self.manifest) + except IOError, exc: + if type (exc) is InstanceType and hasattr (exc, 'strerror'): + msg = "could not open MANIFEST (%s)" % \ + string.lower (exc.strerror) + else: + msg = "could not open MANIFST" + + self.warn (msg + ": using default file list") + return + while 1: pattern = manifest.readline() @@ -385,6 +401,11 @@ class Dist (Command): # make_release_tree () + def nuke_release_tree (self, base_dir): + self.execute (rmtree, (base_dir,), + "removing %s" % base_dir) + + def make_tarball (self, base_dir, compress="gzip"): # XXX GNU tar 1.13 has a nifty option to add a prefix directory. @@ -441,6 +462,9 @@ class Dist (Command): elif fmt == 'zip': self.make_zipfile (base_dir) + if not self.keep_tree: + self.nuke_release_tree (base_dir) + # class Dist -- cgit v1.2.1 From 8ae570b96d2c1a923c26b02f85d2826561638fba Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 17:19:58 +0000 Subject: Catch up with terminology change in UnixCCompiler: 'includes' -> 'include_dirs'. --- ccompiler.py | 12 ++++++------ msvccompiler.py | 11 ++++++----- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 486f03ad..8c2ddf76 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -262,7 +262,7 @@ class CCompiler: sources, output_dir=None, macros=None, - includes=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): """Compile one or more C/C++ source files. 'sources' must be @@ -277,7 +277,7 @@ class CCompiler: undefines a macro. Later definitions/redefinitions/ undefinitions take precedence. - 'includes', if given, must be a list of strings, the directories + 'include_dirs', if given, must be a list of strings, the directories to add to the default include file search path for this compilation only. @@ -489,12 +489,12 @@ def new_compiler (plat=None, return klass (verbose, dry_run, force) -def gen_preprocess_options (macros, includes): +def gen_preprocess_options (macros, include_dirs): """Generate C pre-processor options (-D, -U, -I) as used by at least two types of compilers: the typical Unix compiler and Visual C++. 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,) means undefine (-U) macro 'name', and (name,value) means - define (-D) macro 'name' to 'value'. 'includes' is just a list of + define (-D) macro 'name' to 'value'. 'include_dirs' is just a list of directory names to be added to the header file search path (-I). Returns a list of command-line options suitable for either Unix compilers or Visual C++.""" @@ -506,7 +506,7 @@ def gen_preprocess_options (macros, includes): # line). I don't think it's essential, though, since most (all?) # Unix C compilers only pay attention to the latest -D or -U # mention of a macro on their command line. Similar situation for - # 'includes'. I'm punting on both for now. Anyways, weeding out + # 'include_dirs'. I'm punting on both for now. Anyways, weeding out # redundancies like this should probably be the province of # CCompiler, since the data structures used are inherited from it # and therefore common to all CCompiler classes. @@ -532,7 +532,7 @@ def gen_preprocess_options (macros, includes): # shell at all costs when we spawn the command! pp_opts.append ("-D%s=%s" % macro) - for dir in includes: + for dir in include_dirs: pp_opts.append ("-I%s" % dir) return pp_opts diff --git a/msvccompiler.py b/msvccompiler.py index 5ac60b2e..d93c74c7 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -63,19 +63,20 @@ class MSVCCompiler (CCompiler) : sources, output_dir=None, macros=None, - includes=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): if macros is None: macros = [] - if includes is None: - includes = [] + if include_dirs is None: + include_dirs = [] objectFiles = [] - base_pp_opts = gen_preprocess_options (self.macros + macros, - self.include_dirs + includes) + base_pp_opts = \ + gen_preprocess_options (self.macros + macros, + self.include_dirs + include_dirs) base_pp_opts.append('/c') -- cgit v1.2.1 From fd51c3b988a972a4d21813364202d7e2f04a4c9a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Dec 1999 21:38:57 +0000 Subject: Use 'search', not 'match', on filename pattern regexes. --- command/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/dist.py b/command/dist.py index cdd4dfc8..2e4aacdd 100644 --- a/command/dist.py +++ b/command/dist.py @@ -274,7 +274,7 @@ class Dist (Command): files = [] for file in allfiles: for (include,regexp) in act_patterns: - if regexp.match (file): + if regexp.search (file): if include: files.append (file) break # continue to next file @@ -290,7 +290,7 @@ class Dist (Command): regexp = re.compile (fnmatch.translate (pattern)) for i in range (len (self.files)-1, -1, -1): - if regexp.match (self.files[i]): + if regexp.search (self.files[i]): del self.files[i] -- cgit v1.2.1 From cca87fce7c57072fd109389de01d23572d0d5dec Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 16 Dec 1999 01:14:15 +0000 Subject: Catch errors from 'rmtree' and emit a warning. --- command/dist.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/command/dist.py b/command/dist.py index 2e4aacdd..9931e375 100644 --- a/command/dist.py +++ b/command/dist.py @@ -402,8 +402,16 @@ class Dist (Command): def nuke_release_tree (self, base_dir): - self.execute (rmtree, (base_dir,), - "removing %s" % base_dir) + try: + self.execute (rmtree, (base_dir,), + "removing %s" % base_dir) + except (IOError, OSError), exc: + if exc.filename: + msg = "error removing %s: %s (%s)" % \ + (base_dir, exc.strerror, exc.filename) + else: + msg = "error removing %s: %s" % (base_dir, exc.strerror) + self.warn (msg) def make_tarball (self, base_dir, compress="gzip"): -- cgit v1.2.1 From 29fed792b97b31c545bb4b7f90a0b3a2e8a8ab4a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 16 Dec 1999 01:19:05 +0000 Subject: When emitting a command-line error message, *say* it's an error. --- core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.py b/core.py index 13bf9c7f..fd5ba907 100644 --- a/core.py +++ b/core.py @@ -88,7 +88,7 @@ def setup (**attrs): ok = dist.parse_command_line (sys.argv[1:]) except DistutilsArgError, msg: sys.stderr.write (usage + "\n") - raise SystemExit, msg + raise SystemExit, "error: %s" % msg # And finally, run all the commands found on the command line. if ok: -- cgit v1.2.1 From 1e138ca57132305e61c88abc02f5f42c1f733584 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Jan 2000 22:39:32 +0000 Subject: Typo fix: 'file.warn' should have been 'manifest.warn' in a couple of places. --- command/dist.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/command/dist.py b/command/dist.py index 9931e375..80af9903 100644 --- a/command/dist.py +++ b/command/dist.py @@ -338,8 +338,8 @@ class Dist (Command): # pattern if os.path.isdir (words[0]): if exclude: - file.warn ("exclude (!) doesn't apply to " + - "whole directory trees") + manifest.warn ("exclude (!) doesn't apply to " + + "whole directory trees") continue dir_files = self.search_dir (words[0], words[1:]) @@ -348,8 +348,8 @@ class Dist (Command): # Multiple words in pattern: that's a no-no unless the first # word is a directory name elif len (words) > 1: - file.warn ("can't have multiple words unless first word " + - "('%s') is a directory name" % words[0]) + manifest.warn ("can't have multiple words unless first word " + + "('%s') is a directory name" % words[0]) continue # Single word, no bang: it's a "simple include pattern" @@ -364,7 +364,7 @@ class Dist (Command): # Single word prefixed with a bang: it's a "simple exclude pattern" else: if self.exclude_files (pattern) == 0: - file.warn ("no files excluded by '%s'" % pattern) + manifest.warn ("no files excluded by '%s'" % pattern) # if/elif/.../else on 'pattern' -- cgit v1.2.1 From b4325f5828cd4c7a744909360776702dd72a4a13 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Jan 2000 22:41:02 +0000 Subject: Removed a bunch of irrelevant parameters from 'link_static_lib()' signature. Added 'link_executable()' signature. --- ccompiler.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 8c2ddf76..036cbe9f 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -292,17 +292,10 @@ class CCompiler: pass - # XXX this is kind of useless without 'link_binary()' or - # 'link_executable()' or something -- or maybe 'link_static_lib()' - # should not exist at all, and we just have 'link_binary()'? def link_static_lib (self, objects, output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - extra_preargs=None, - extra_postargs=None): + output_dir=None): """Link a bunch of stuff together to create a static library file. The "bunch of stuff" consists of the list of object files supplied as 'objects', the extra object files supplied @@ -368,6 +361,23 @@ class CCompiler: pass + def link_executable (self, + objects, + output_progname, + output_dir=None, + libraries=None, + library_dirs=None, + extra_preargs=None, + extra_postargs=None): + """Link a bunch of stuff together to create a binary executable + file. The "bunch of stuff" is as for 'link_static_lib()'. + 'output_progname' should be the base name of the executable + program--e.g. on Unix the same as the output filename, but + on DOS/Windows ".exe" will be appended.""" + pass + + + # -- Filename mangling methods ------------------------------------- # General principle for the filename-mangling methods: by default, -- cgit v1.2.1 From c77e34abbcc3b20c0091b26e3a9142599b98753a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Jan 2000 22:47:53 +0000 Subject: Abstracted '_fix_link_args()' out of 'link_shared_object()'. Added 'link_static_lib()' method, and 'archiver' and 'archiver_options' class attributes to support it. Added 'link_executable()' method, and 'ld_exec' instance attribute to support it. 'newer_group()' is now able to handle missing files, so we don't have to kludge it by catching OSError when calling it. 'object_filenames()' and 'shared_object_filename()' now take 'keep_dir' flag parameters. 'library_filename()' and 'shared_library_filename()' now respect a directory component in the library name. Various comment updates/deletions. --- unixccompiler.py | 170 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 133 insertions(+), 37 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index edff4f0a..fb58269c 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -59,7 +59,14 @@ class UnixCCompiler (CCompiler): _exe_ext = '' _shared_lib_ext = SO _static_lib_ext = '.a' - + + # Command to create a static library: seems to be pretty consistent + # across the major Unices. Might have to move down into the + # constructor if we need platform-specific guesswork. + archiver = "ar" + archiver_options = "-cr" + + def __init__ (self, verbose=0, dry_run=0, @@ -87,6 +94,8 @@ class UnixCCompiler (CCompiler): (self.ld_shared, self.ldflags_shared) = \ _split_command (LDSHARED) + self.ld_exec = self.cc + def compile (self, sources, @@ -122,7 +131,7 @@ class UnixCCompiler (CCompiler): # don't have to recompile. (Simplistic check -- we just compare the # source and object file, no deep dependency checking involving # header files. Hmmm.) - objects = self.object_filenames (sources, output_dir) + objects = self.object_filenames (sources, output_dir=output_dir) if not self.force: skipped = newer_pairwise (sources, objects) for skipped_pair in skipped: @@ -161,13 +170,70 @@ class UnixCCompiler (CCompiler): # return *all* of them, including those that weren't recompiled on # this call! return self.object_filenames (orig_sources, output_dir) - - # XXX punting on 'link_static_lib()' for now -- it might be better for - # CCompiler to mandate just 'link_binary()' or some such to build a new - # Python binary; it would then take care of linking in everything - # needed for the new Python without messing with an intermediate static - # library. + + def _fix_link_args (self, output_dir, libraries, library_dirs): + """Fixes up the arguments supplied to the 'link_*' methods: + if output_dir is None, use self.output_dir; ensure that + libraries and library_dirs are both lists (could be None or + tuples on input -- both are converted to lists). Return + a tuple of the three input arguments.""" + + if output_dir is None: + output_dir = self.output_dir + if libraries is None: + libraries = [] + if library_dirs is None: + library_dirs = [] + + if type (libraries) not in (ListType, TupleType): + raise TypeError, \ + "'libraries' (if supplied) must be a list of strings" + if type (library_dirs) not in (ListType, TupleType): + raise TypeError, \ + "'library_dirs' (if supplied) must be a list of strings" + libraries = list (libraries) + library_dirs = list (library_dirs) + + return (output_dir, libraries, library_dirs) + + + def link_static_lib (self, + objects, + output_libname, + output_dir=None): + + if type (objects) not in (ListType, TupleType): + raise TypeError, \ + "'objects' must be a list or tuple of strings" + objects = list (objects) + + if output_dir is None: + output_dir = self.output_dir + + output_filename = self.library_filename (output_libname) + if output_dir is not None: + output_filename = os.path.join (output_dir, output_filename) + + # Check timestamps: if any of the object files are newer than + # the library file, *or* if "force" is true, then we'll + # recreate the library. + if not self.force: + if self.dry_run: + newer = newer_group (objects, output_filename, missing='newer') + else: + newer = newer_group (objects, output_filename) + + if self.force or newer: + self.spawn ([self.archiver, + self.archiver_options, + output_filename] + + objects) + else: + self.announce ("skipping %s (up-to-date)" % output_filename) + + # link_static_lib () + def link_shared_lib (self, objects, @@ -198,21 +264,8 @@ class UnixCCompiler (CCompiler): extra_preargs=None, extra_postargs=None): - if output_dir is None: - output_dir = self.output_dir - if libraries is None: - libraries = [] - if library_dirs is None: - library_dirs = [] - - if type (libraries) not in (ListType, TupleType): - raise TypeError, \ - "'libraries' (if supplied) must be a list of strings" - if type (library_dirs) not in (ListType, TupleType): - raise TypeError, \ - "'library_dirs' (if supplied) must be a list of strings" - libraries = list (libraries) - library_dirs = list (library_dirs) + (output_dir, libraries, library_dirs) = \ + self._fix_link_args (output_dir, libraries, library_dirs) lib_opts = gen_lib_options (self, self.library_dirs + library_dirs, @@ -223,16 +276,12 @@ class UnixCCompiler (CCompiler): # If any of the input object files are newer than the output shared # object, relink. Again, this is a simplistic dependency check: # doesn't look at any of the libraries we might be linking with. - # Note that we have to dance around errors comparing timestamps if - # we're in dry-run mode (yuck). + if not self.force: - try: + if self.dry_run: + newer = newer_group (objects, output_filename, missing='newer') + else: newer = newer_group (objects, output_filename) - except OSError: - if self.dry_run: - newer = 1 - else: - raise if self.force or newer: ld_args = self.ldflags_shared + objects + \ @@ -248,31 +297,78 @@ class UnixCCompiler (CCompiler): # link_shared_object () + def link_executable (self, + objects, + output_progname, + output_dir=None, + libraries=None, + library_dirs=None, + extra_preargs=None, + extra_postargs=None): + + (output_dir, libraries, library_dirs) = \ + self._fix_link_args (output_dir, libraries, library_dirs) + + lib_opts = gen_lib_options (self, + self.library_dirs + library_dirs, + self.libraries + libraries) + output_filename = output_progname # Unix-ism! + if output_dir is not None: + output_filename = os.path.join (output_dir, output_filename) + + # Same ol' simplistic-but-still-useful dependency check. + if not self.force: + if self.dry_run: + newer = newer_group (objects, output_filename, missing='newer') + else: + newer = newer_group (objects, output_filename) + + if self.force or newer: + ld_args = objects + lib_opts + ['-o', output_filename] + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend (extra_postargs) + self.spawn ([self.ld_exec] + ld_args) + else: + self.announce ("skipping %s (up-to-date)" % output_filename) + + # link_executable () + + # -- Filename-mangling (etc.) methods ------------------------------ - def object_filenames (self, source_filenames, output_dir=None): + def object_filenames (self, source_filenames, + keep_dir=0, output_dir=None): outnames = [] for inname in source_filenames: outname = re.sub (r'\.(c|C|cc|cxx|cpp)$', self._obj_ext, inname) - outname = os.path.basename (outname) + if not keep_dir: + outname = os.path.basename (outname) if output_dir is not None: outname = os.path.join (output_dir, outname) outnames.append (outname) return outnames - def shared_object_filename (self, source_filename, output_dir=None): + def shared_object_filename (self, source_filename, + keep_dir=0, output_dir=None): outname = re.sub (r'\.(c|C|cc|cxx|cpp)$', self._shared_lib_ext) - outname = os.path.basename (outname) + if not keep_dir: + outname = os.path.basename (outname) if output_dir is not None: outname = os.path.join (output_dir, outname) return outname def library_filename (self, libname): - return "lib%s%s" % (libname, self._static_lib_ext) + (dirname, basename) = os.path.split (libname) + return os.path.join (dirname, + "lib%s%s" % (basename, self._static_lib_ext)) def shared_library_filename (self, libname): - return "lib%s%s" % (libname, self._shared_lib_ext) + (dirname, basename) = os.path.split (libname) + return os.path.join (dirname, + "lib%s%s" % (basename, self._shared_lib_ext)) def library_dir_option (self, dir): -- cgit v1.2.1 From 070f8639264c4ef9d748a2592cad810d189e8f4e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Jan 2000 22:48:59 +0000 Subject: 'newer_group()' can now deal with missing files, in a way specified by the 'missing' parameter. --- util.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 4f93cd44..953c2e26 100644 --- a/util.py +++ b/util.py @@ -120,11 +120,18 @@ def newer_pairwise (sources, targets): # newer_pairwise () -def newer_group (sources, target): +def newer_group (sources, target, missing='error'): """Return true if 'target' is out-of-date with respect to any file listed in 'sources'. In other words, if 'target' exists and is newer than every file in 'sources', return false; otherwise - return true.""" + return true. 'missing' controls what we do when a source file is + missing; the default ("error") is to blow up with an OSError from + inside 'stat()'; if it is "ignore", we silently drop any missing + source files; if it is "newer", any missing source files make us + assume that 'target' is out-of-date (this is handy in "dry-run" + mode: it'll make you pretend to carry out commands that wouldn't + work because inputs are missing, but that doesn't matter because + you're not actually going to run the commands).""" # If the target doesn't even exist, then it's definitely out-of-date. if not os.path.exists (target): @@ -137,6 +144,14 @@ def newer_group (sources, target): from stat import ST_MTIME target_mtime = os.stat (target)[ST_MTIME] for source in sources: + if not os.path.exists (source): + if missing == 'error': # blow up when we stat() the file + pass + elif missing == 'ignore': # missing source dropped from + continue # target's dependency list + elif missing == 'newer': # missing source means target is + return 1 # out-of-date + source_mtime = os.stat(source)[ST_MTIME] if source_mtime > target_mtime: return 1 -- cgit v1.2.1 From 9b0d91692d8ed2c33df2133484d19b66b4fe6aa4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 16:25:17 +0000 Subject: Ditch unneeded imports. --- command/build_py.py | 1 - 1 file changed, 1 deletion(-) diff --git a/command/build_py.py b/command/build_py.py index 6bc5aa80..e27a36d4 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -12,7 +12,6 @@ from glob import glob from distutils.core import Command from distutils.errors import * -from distutils.util import mkpath, copy_file class BuildPy (Command): -- cgit v1.2.1 From 705babe53bea546b02923e3a54ad837b7fd0f5f4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 16:25:59 +0000 Subject: Always run sys.prefix and sys.exec_prefix through 'os.path.normpath()' before storing or using. --- command/install.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/command/install.py b/command/install.py index cd12f6fc..6f5d6715 100644 --- a/command/install.py +++ b/command/install.py @@ -130,9 +130,9 @@ class Install (Command): # ape the behaviour of Python's configure script. if self.prefix is None: # user didn't override - self.prefix = sys.prefix + self.prefix = os.path.normpath (sys.prefix) if self.exec_prefix is None: - self.exec_prefix = sys.exec_prefix + self.exec_prefix = os.path.normpath (sys.exec_prefix) if self.install_lib is None: self.install_lib = \ @@ -247,10 +247,10 @@ class Install (Command): return the "relocated" installation directory.""" if use_exec: - sys_prefix = sys.exec_prefix + sys_prefix = os.path.normpath (sys.exec_prefix) my_prefix = self.exec_prefix else: - sys_prefix = sys.prefix + sys_prefix = os.path.normpath (sys.prefix) my_prefix = self.prefix val = getattr (sysconfig, config_attr) -- cgit v1.2.1 From dcb45ff5424f902d4e82572b682549b8e1576c89 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 18:00:04 +0000 Subject: Fix library filename methods -- there is no 'lib' prefix under DOS/Windows. --- msvccompiler.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index d93c74c7..48453768 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -202,18 +202,15 @@ class MSVCCompiler (CCompiler) : specified source filename.""" return self._change_extensions( source_filenames, self._shared_lib_ext ) - # XXX ummm... these aren't right, are they? I thought library 'foo' on - # DOS/Windows was to be found in "foo.lib", not "libfoo.lib"! - def library_filename (self, libname): """Return the static library filename corresponding to the specified library name.""" - return "lib%s%s" %( libname, self._static_lib_ext ) + return "%s%s" %( libname, self._static_lib_ext ) def shared_library_filename (self, libname): """Return the shared library filename corresponding to the specified library name.""" - return "lib%s%s" %( libname, self._shared_lib_ext ) + return "%s%s" %( libname, self._shared_lib_ext ) def library_dir_option (self, dir): -- cgit v1.2.1 From e5eb54ac61619eb08aa9c2f2975afe01b747ef5f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 18:04:04 +0000 Subject: Added code to use Jim Ahlstrom's zipfile.py module if the external zip command wasn't found or failed. (Code supplied by Thomas Heller .) --- command/dist.py | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/command/dist.py b/command/dist.py index 80af9903..b588fe95 100644 --- a/command/dist.py +++ b/command/dist.py @@ -435,12 +435,39 @@ class Dist (Command): def make_zipfile (self, base_dir): - # This assumes the Unix 'zip' utility -- it could be easily recast - # to use pkzip (or whatever the command-line zip creation utility - # on Redmond's archaic CP/M knockoff is nowadays), but I'll let - # someone who can actually test it do that. + # This initially assumed the Unix 'zip' utility -- but + # apparently InfoZIP's zip.exe works the same under Windows, so + # no changes needed! - self.spawn (["zip", "-r", base_dir + ".zip", base_dir]) + try: + self.spawn (["zip", "-r", base_dir + ".zip", base_dir]) + except DistutilsExecError: + + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed" -- shouldn't try + # again in the latter case. (I think fixing this will + # require some cooperation from the spawn module -- perhaps + # a utility function to search the path, so we can fallback + # on zipfile.py without the failed spawn.) + try: + import zipfile + except ImportError: + raise DistutilsExecError, \ + ("unable to create zip file '%s.zip':" + + "could neither find a standalone zip utility nor " + + "import the 'zipfile' module") % base_dir + + z = zipfile.ZipFile (base_dir + ".zip", "wb", + compression=zipfile.ZIP_DEFLATED) + + def visit (z, dirname, names): + for name in names: + path = os.path.join (dirname, name) + if os.path.isfile (path): + z.write (path, path) + + os.path.walk (base_dir, visit, z) + z.close() def make_distribution (self): -- cgit v1.2.1 From 102e89700b1f852de2496db33885228989f8161a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 20:23:34 +0000 Subject: Added missing import. Fixed 'make_release_tree()' to copy files if 'os.link()' doesn't exist. --- command/dist.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/command/dist.py b/command/dist.py index b588fe95..2d05f174 100644 --- a/command/dist.py +++ b/command/dist.py @@ -13,6 +13,7 @@ from glob import glob from shutil import rmtree from distutils.core import Command from distutils.text_file import TextFile +from distutils.errors import DistutilsExecError # Possible modes of operation: @@ -388,16 +389,30 @@ class Dist (Command): for dir in need_dirs: self.mkpath (dir) - # And walk over the list of files, making a hard link for - # each one that doesn't already exist in its corresponding - # location under 'base_dir' + # And walk over the list of files, either making a hard link (if + # os.link exists) to each one that doesn't already exist in its + # corresponding location under 'base_dir', or copying each file + # that's out-of-date in 'base_dir'. (Usually, all files will be + # out-of-date, because by default we blow away 'base_dir' when + # we're done making the distribution archives.) - self.announce ("making hard links in %s..." % base_dir) + try: + link = os.link + msg = "making hard links in %s..." % base_dir + except AttributeError: + link = 0 + msg = "copying files to %s..." % base_dir + + self.announce (msg) for file in files: dest = os.path.join (base_dir, file) - if not os.path.exists (dest): - self.execute (os.link, (file, dest), - "linking %s -> %s" % (file, dest)) + if link: + if not os.path.exists (dest): + self.execute (os.link, (file, dest), + "linking %s -> %s" % (file, dest)) + else: + self.copy_file (file, dest) + # make_release_tree () @@ -453,7 +468,7 @@ class Dist (Command): import zipfile except ImportError: raise DistutilsExecError, \ - ("unable to create zip file '%s.zip':" + + ("unable to create zip file '%s.zip': " + "could neither find a standalone zip utility nor " + "import the 'zipfile' module") % base_dir -- cgit v1.2.1 From 696403a48232186753c41267731124d0105892e3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 20:40:48 +0000 Subject: Added compiler flags suggested by Thomas Heller: optimize, use multi-threaded RT library. --- msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index 48453768..50fd4622 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -37,7 +37,7 @@ class MSVCCompiler (CCompiler) : self.cc = "cl.exe" self.link = "link.exe" self.preprocess_options = None - self.compile_options = [ '/nologo' ] + self.compile_options = [ '/nologo', '/Ox', '/MD', '/GD' ] self.ldflags_shared = ['/DLL', '/nologo'] self.ldflags_static = [ '/nologo'] -- cgit v1.2.1 From ab853dd09598817baa291d816f22a8a0b0c784c8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 21:57:17 +0000 Subject: Removed /GD switch -- currently ignored by MSVC. --- msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index 50fd4622..a07b4b8e 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -37,7 +37,7 @@ class MSVCCompiler (CCompiler) : self.cc = "cl.exe" self.link = "link.exe" self.preprocess_options = None - self.compile_options = [ '/nologo', '/Ox', '/MD', '/GD' ] + self.compile_options = [ '/nologo', '/Ox', '/MD' ] self.ldflags_shared = ['/DLL', '/nologo'] self.ldflags_static = [ '/nologo'] -- cgit v1.2.1 From 57794609e7637324ea1c7c31686e8889571bccd6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 21:57:55 +0000 Subject: Catch OSError from 'spawnv()' in '_spawn_nt()'. Tweaked error messages in '_spawn_posix()'. --- spawn.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/spawn.py b/spawn.py index eee8e7f4..9a88ac89 100644 --- a/spawn.py +++ b/spawn.py @@ -63,9 +63,16 @@ def _spawn_nt ( cmd, print string.join ( [executable] + cmd[1:], ' ') if not dry_run: # spawn for NT requires a full path to the .exe - rc = os.spawnv (os.P_WAIT, executable, cmd) + try: + rc = os.spawnv (os.P_WAIT, executable, cmd) + except OSError, exc: + # this seems to happen when the command isn't found + raise DistutilsExecError, \ + "command '%s' failed: %s" % (cmd[0], exc[-1]) if rc != 0: - raise DistutilsExecError("command failed: %d" % rc) + # and this reflects the command running but failing + raise DistutilsExecError, \ + "command '%s' failed with exit status %d" % (cmd[0], rc) @@ -103,7 +110,7 @@ def _spawn_posix (cmd, (pid, status) = os.waitpid (pid, 0) if os.WIFSIGNALED (status): raise DistutilsExecError, \ - "command %s terminated by signal %d" % \ + "command '%s' terminated by signal %d" % \ (cmd[0], os.WTERMSIG (status)) elif os.WIFEXITED (status): @@ -112,7 +119,7 @@ def _spawn_posix (cmd, return # hey, it succeeded! else: raise DistutilsExecError, \ - "command %s failed with exit status %d" % \ + "command '%s' failed with exit status %d" % \ (cmd[0], exit_status) elif os.WIFSTOPPED (status): @@ -120,6 +127,6 @@ def _spawn_posix (cmd, else: raise DistutilsExecError, \ - "unknown error executing %s: termination status %d" % \ + "unknown error executing '%s': termination status %d" % \ (cmd[0], status) # _spawn_posix () -- cgit v1.2.1 From 0026c2ec06718cd6767b38cb5cc72bb77ed548b7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 21:58:07 +0000 Subject: Fix indentation bug. --- command/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/dist.py b/command/dist.py index 2d05f174..0f9e30ba 100644 --- a/command/dist.py +++ b/command/dist.py @@ -472,8 +472,8 @@ class Dist (Command): "could neither find a standalone zip utility nor " + "import the 'zipfile' module") % base_dir - z = zipfile.ZipFile (base_dir + ".zip", "wb", - compression=zipfile.ZIP_DEFLATED) + z = zipfile.ZipFile (base_dir + ".zip", "wb", + compression=zipfile.ZIP_DEFLATED) def visit (z, dirname, names): for name in names: -- cgit v1.2.1 From 6de13bfacf9bf7d1722fc1ff5c5d8e801512ae04 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 15:07:56 +0000 Subject: Added missing run of corresponding 'build' command. --- command/install_ext.py | 3 +++ command/install_lib.py | 3 +++ command/install_py.py | 3 +++ 3 files changed, 9 insertions(+) diff --git a/command/install_ext.py b/command/install_ext.py index 3fb756c6..b5673d23 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -27,6 +27,9 @@ class InstallExt (Command): def run (self): + # Make sure we have built all extension modules first + self.run_peer ('build_ext') + # Dump the entire "build/platlib" directory (or whatever it really # is; "build/platlib" is the default) to the installation target # (eg. "/usr/local/lib/python1.5/site-packages"). Note that diff --git a/command/install_lib.py b/command/install_lib.py index 35deaa81..50939a34 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -46,6 +46,9 @@ class InstallPy (Command): def run (self): + # Make sure we have "built" all pure Python modules first + self.run_peer ('build_py') + # Dump entire contents of the build directory to the installation # directory (that's the beauty of having a build directory!) outfiles = self.copy_tree (self.build_dir, self.install_dir) diff --git a/command/install_py.py b/command/install_py.py index 35deaa81..50939a34 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -46,6 +46,9 @@ class InstallPy (Command): def run (self): + # Make sure we have "built" all pure Python modules first + self.run_peer ('build_py') + # Dump entire contents of the build directory to the installation # directory (that's the beauty of having a build directory!) outfiles = self.copy_tree (self.build_dir, self.install_dir) -- cgit v1.2.1 From f8864bbadfdd49102b589272837a8ec1a93ca0fc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 18:30:32 +0000 Subject: Improvements to the help system: * "--help" can now come either before or after particular commands to get help on and can give help on multiple commands, eg. "--help install dist" gives help on those two commands * added "--help-commands" option, implemented by the 'print_commands()' and 'print_command_list()' methods --- core.py | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 5 deletions(-) diff --git a/core.py b/core.py index fd5ba907..7a646dad 100644 --- a/core.py +++ b/core.py @@ -30,8 +30,9 @@ command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') usage = """\ usage: %s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: %s --help + or: %s --help-commands or: %s cmd --help -""" % (sys.argv[0], sys.argv[0], sys.argv[0]) +""" % ((sys.argv[0],) * 4) def setup (**attrs): @@ -159,6 +160,7 @@ class Distribution: self.dry_run = 0 self.force = 0 self.help = 0 + self.help_commands = 0 # And the "distribution meta-data" options -- these can only # come from setup.py (the caller), not the command line @@ -270,15 +272,21 @@ class Distribution: # happen until we know what the command is. self.commands = [] - args = fancy_getopt (self.global_options, self.negative_opt, + options = self.global_options + \ + [('help-commands', None, + "list all available commands")] + args = fancy_getopt (options, self.negative_opt, self, sys.argv[1:]) - if self.help: - print_help (self.global_options, header="Global options:") + # User just wants a list of commands -- we'll print it out and stop + # processing now (ie. if they ran "setup --help-commands foo bar", + # we ignore "foo bar"). + if self.help_commands: + self.print_commands () print print usage return - + while args: # Pull the current command from the head of the command line command = args[0] @@ -336,6 +344,25 @@ class Distribution: # while args + # If the user wants help -- ie. they gave the "--help" option -- + # give it to 'em. We do this *after* processing the commands in + # case they want help on any particular command, eg. + # "setup.py --help foo". (This isn't the documented way to + # get help on a command, but I support it because that's how + # CVS does it -- might as well be consistent.) + if self.help: + print_help (self.global_options, header="Global options:") + print + + for command in self.commands: + klass = self.find_command_class (command) + print_help (klass.options, + header="Options for '%s' command:" % command) + print + + print usage + return + # Oops, no commands found -- an end-user error if not self.commands: raise DistutilsArgError, "no commands supplied" @@ -346,6 +373,63 @@ class Distribution: # parse_command_line() + def print_command_list (self, commands, header, max_length): + """Print a subset of the list of all commands -- used by + 'print_commands()'.""" + + print header + ":" + + for cmd in commands: + klass = self.cmdclass.get (cmd) + if not klass: + klass = self.find_command_class (cmd) + try: + description = klass.description + except AttributeError: + description = "(no description available)" + + print " %-*s %s" % (max_length, cmd, description) + + # print_command_list () + + + def print_commands (self): + """Print out a help message listing all available commands with + a description of each. The list is divided into "standard + commands" (listed in distutils.command.__all__) and "extra + commands" (mentioned in self.cmdclass, but not a standard + command). The descriptions come from the command class + attribute 'description'.""" + + import distutils.command + std_commands = distutils.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass.keys(): + if not is_std.get(cmd): + extra_commands.append (cmd) + + max_length = 0 + for cmd in (std_commands + extra_commands): + if len (cmd) > max_length: + max_length = len (cmd) + + self.print_command_list (std_commands, + "Standard commands", + max_length) + if extra_commands: + print + self.print_command_list (extra_commands, + "Extra commands", + max_length) + + # print_commands () + + + # -- Command class/object methods ---------------------------------- # This is a method just so it can be overridden if desired; it doesn't -- cgit v1.2.1 From 40b35bdeccac9d3b970f084d10fd7d0502bca864 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 18:31:34 +0000 Subject: Added 'dist' command. --- command/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/__init__.py b/command/__init__.py index 9a5aef20..8659307c 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -9,6 +9,7 @@ commands. Currently this means: install install_py install_ext + dist but this list will undoubtedly grow with time.""" @@ -20,4 +21,5 @@ __all__ = ['build', 'install', 'install_py', 'install_ext', + 'dist', ] -- cgit v1.2.1 From fb6034d5ef6f4eccc9ee98e7aa353a4b2ae51235 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 18:34:15 +0000 Subject: Added 'description' class attribute to every command class (to help the '--help-commands' option). Shuffled imports around in a few command modules to avoid expensive up-front import of sysconfig (and resulting delays in generating list of all commands). --- command/build.py | 2 ++ command/build_ext.py | 15 ++++++++++----- command/build_py.py | 2 ++ command/dist.py | 2 ++ command/install.py | 5 ++++- command/install_ext.py | 2 ++ command/install_lib.py | 2 ++ command/install_py.py | 2 ++ 8 files changed, 26 insertions(+), 6 deletions(-) diff --git a/command/build.py b/command/build.py index 1586e60b..e6c87bfc 100644 --- a/command/build.py +++ b/command/build.py @@ -12,6 +12,8 @@ from distutils.core import Command class Build (Command): + description = "build everything needed to install" + options = [('build-base=', 'b', "base directory for build library"), ('build-lib=', 'l', diff --git a/command/build_ext.py b/command/build_ext.py index d38cb18b..4f7e53ff 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -11,8 +11,6 @@ __rcsid__ = "$Id$" import sys, os, string, re from types import * from distutils.core import Command -from distutils.ccompiler import new_compiler -from distutils.sysconfig import INCLUDEPY, SO, exec_prefix from distutils.errors import * @@ -24,6 +22,8 @@ extension_name_re = re.compile \ class BuildExt (Command): + description = "build C/C++ extensions (compile/link to build directory)" + # XXX thoughts on how to deal with complex command-line options like # these, i.e. how to make it so fancy_getopt can suck them off the # command line and make it look like setup.py defined the appropriate @@ -76,6 +76,8 @@ class BuildExt (Command): def set_final_options (self): + from distutils import sysconfig + self.set_undefined_options ('build', ('build_platlib', 'build_dir')) if self.package is None: @@ -88,8 +90,8 @@ class BuildExt (Command): # etc.) are in the include search path. We have to roll our own # "exec include dir", because the Makefile parsed by sysconfig # doesn't have it (sigh). - py_include = INCLUDEPY # prefix + "include" + "python" + ver - exec_py_include = os.path.join (exec_prefix, 'include', + py_include = sysconfig.INCLUDEPY # prefix + "include" + "python" + ver + exec_py_include = os.path.join (sysconfig.exec_prefix, 'include', 'python' + sys.version[0:3]) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] @@ -104,6 +106,8 @@ class BuildExt (Command): def run (self): + from distutils.ccompiler import new_compiler + # 'self.extensions', as supplied by setup.py, is a list of 2-tuples. # Each tuple is simple: # (ext_name, build_info) @@ -246,9 +250,10 @@ class BuildExt (Command): def extension_filename (self, ext_name, package=None): + from distutils import sysconfig if package: ext_name = package + '.' + ext_name ext_path = string.split (ext_name, '.') - return apply (os.path.join, ext_path) + SO + return apply (os.path.join, ext_path) + sysconfig.SO # class BuildExt diff --git a/command/build_py.py b/command/build_py.py index e27a36d4..57ddf7e7 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -16,6 +16,8 @@ from distutils.errors import * class BuildPy (Command): + description = "\"build\" pure Python modules (copy to build directory)" + options = [('build-dir=', 'd', "directory for platform-shared files"), ] diff --git a/command/dist.py b/command/dist.py index 0f9e30ba..76332b27 100644 --- a/command/dist.py +++ b/command/dist.py @@ -131,6 +131,8 @@ from distutils.errors import DistutilsExecError class Dist (Command): + description = "create a source distribution (tarball, zip file, etc.)" + options = [('formats=', None, "formats for source distribution (tar, ztar, gztar, or zip)"), ('manifest=', 'm', diff --git a/command/install.py b/command/install.py index 6f5d6715..0e5b01cc 100644 --- a/command/install.py +++ b/command/install.py @@ -8,13 +8,14 @@ __rcsid__ = "$Id$" import sys, os, string from types import * -from distutils import sysconfig from distutils.core import Command from distutils.util import write_file class Install (Command): + description = "install everything from build directory" + options = [('prefix=', None, "installation prefix"), ('exec-prefix=', None, "prefix for platform-specific files"), @@ -246,6 +247,8 @@ class Install (Command): then replace it with the current installation prefix and return the "relocated" installation directory.""" + from distutils import sysconfig + if use_exec: sys_prefix = os.path.normpath (sys.exec_prefix) my_prefix = self.exec_prefix diff --git a/command/install_ext.py b/command/install_ext.py index b5673d23..599a37e1 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -11,6 +11,8 @@ from distutils.util import copy_tree class InstallExt (Command): + description = "install C/C++ extension modules" + options = [('install-dir=', 'd', "directory to install to"), ('build-dir=','b', "build directory (where to install from)"), ] diff --git a/command/install_lib.py b/command/install_lib.py index 50939a34..2e8a6706 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -8,6 +8,8 @@ from distutils.util import copy_tree class InstallPy (Command): + description = "install pure Python modules" + options = [('install-dir=', 'd', "directory to install to"), ('build-dir=','b', "build directory (where to install from)"), ('compile', 'c', "compile .py to .pyc"), diff --git a/command/install_py.py b/command/install_py.py index 50939a34..2e8a6706 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -8,6 +8,8 @@ from distutils.util import copy_tree class InstallPy (Command): + description = "install pure Python modules" + options = [('install-dir=', 'd', "directory to install to"), ('build-dir=','b', "build directory (where to install from)"), ('compile', 'c', "compile .py to .pyc"), -- cgit v1.2.1 From 4cf86996ee90c2109a98829865f736683ca5cfac Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 19:57:48 +0000 Subject: Fixed broken list extend in 'copy_tree()'. --- util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 953c2e26..58d58439 100644 --- a/util.py +++ b/util.py @@ -349,10 +349,10 @@ def copy_tree (src, dst, outputs.append (dst_name) elif os.path.isdir (src_name): - outputs[-1:] = \ + outputs.extend ( copy_tree (src_name, dst_name, preserve_mode, preserve_times, preserve_symlinks, - update, verbose, dry_run) + update, verbose, dry_run)) else: if (copy_file (src_name, dst_name, preserve_mode, preserve_times, -- cgit v1.2.1 From 6be52b45d1a442faf400b853e190db26b718d1c9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 20:22:27 +0000 Subject: Allow either README or README.txt as a "standard file". --- command/dist.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/command/dist.py b/command/dist.py index 76332b27..ea61a480 100644 --- a/command/dist.py +++ b/command/dist.py @@ -219,12 +219,24 @@ class Dist (Command): def find_defaults (self): - standards = ['README', 'setup.py'] + standards = [('README', 'README.txt'), 'setup.py'] for fn in standards: - if os.path.exists (fn): - self.files.append (fn) + if type (fn) is TupleType: + alts = fn + for fn in alts: + if os.path.exists (fn): + got_it = 1 + self.files.append (fn) + break + + if not got_it: + self.warn ("standard file not found: should have one of " + + string.join (alts, ', ')) else: - self.warn ("standard file %s not found" % fn) + if os.path.exists (fn): + self.files.append (fn) + else: + self.warn ("standard file %s not found" % fn) optional = ['test/test*.py'] for pattern in optional: -- cgit v1.2.1 From 439aa60615c8ada638e789a78159d26091838ffe Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Feb 2000 00:05:14 +0000 Subject: Comment fix. Always use normalized (with os.path.normpath()) versions of prefix and exec_prefix. --- sysconfig.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 8eaf17dc..0e40cbc3 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -13,15 +13,20 @@ import re import string import sys +prefix = os.path.normpath (sys.prefix) +exec_prefix = os.path.normpath (sys.exec_prefix) + def get_config_h_filename(): """Return full pathname of installed config.h file.""" - return os.path.join(sys.exec_prefix, "include", "python" + sys.version[:3], + return os.path.join(exec_prefix, + "include", "python" + sys.version[:3], "config.h") def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" - return os.path.join(sys.exec_prefix, "lib", "python" + sys.version[:3], + return os.path.join(exec_prefix, + "lib", "python" + sys.version[:3], "config", "Makefile") def parse_config_h(fp, g=None): @@ -125,7 +130,7 @@ def _init_posix(): g = globals() # load the installed config.h: parse_config_h(open(get_config_h_filename()), g) - # load the installed Makefile.pre.in: + # load the installed Makefile: parse_makefile(open(get_makefile_filename()), g) @@ -134,16 +139,16 @@ def _init_nt(): g=globals() # load config.h, though I don't know how useful this is parse_config_h(open( - os.path.join(sys.exec_prefix, "include", "config.h")), g) + os.path.join(exec_prefix, "include", "config.h")), g) # set basic install directories - g['LIBDEST']=os.path.join(sys.exec_prefix, "Lib") - g['BINLIBDEST']= os.path.join(sys.exec_prefix, "Lib") + g['LIBDEST']=os.path.join(exec_prefix, "Lib") + g['BINLIBDEST']= os.path.join(exec_prefix, "Lib") # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = os.path.join (sys.prefix, 'include' ) + g['INCLUDEPY'] = os.path.join (prefix, 'include' ) g['SO'] = '.dll' - g['exec_prefix'] = sys.exec_prefix + g['exec_prefix'] = exec_prefix try: exec "_init_" + os.name -- cgit v1.2.1 From 0298b16d8f10a056dbd74b2b68c425fcf873d6e2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Feb 2000 00:07:14 +0000 Subject: Patch from Joe Van Andel: fix arg to % operator in warning. --- command/build_py.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 57ddf7e7..048962b2 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -160,8 +160,8 @@ class BuildPy (Command): def check_module (self, module, module_file): if not os.path.isfile (module_file): - self.warn ("file %s (for module %s) not found" % - module_file, module) + self.warn ("file %s (for module %s) not found" % + (module_file, module)) return 0 else: return 1 -- cgit v1.2.1 From b97cf9ec1ea46ad32766cabf2f8b0697be681944 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 3 Feb 2000 23:07:19 +0000 Subject: Changed 'compile()' method to compile files one-at-a-time -- gives better feedback and, theoretically, the opportunity to set compiler flags on a per-file basis. --- unixccompiler.py | 49 +++++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index fb58269c..9ace9860 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -137,34 +137,27 @@ class UnixCCompiler (CCompiler): for skipped_pair in skipped: self.announce ("skipping %s (%s up-to-date)" % skipped_pair) - # If anything left to compile, compile it - if sources: - # XXX use of ccflags_shared means we're blithely assuming - # that we're compiling for inclusion in a shared object! - # (will have to fix this when I add the ability to build a - # new Python) - cc_args = ['-c'] + pp_opts + \ - self.ccflags + self.ccflags_shared + \ - sources - if extra_preargs: - cc_args[:0] = extra_preargs - if extra_postargs: - cc_args.extend (extra_postargs) - self.spawn ([self.cc] + cc_args) - - - # Note that compiling multiple source files in the same go like - # we've just done drops the .o file in the current directory, which - # may not be what the caller wants (depending on the 'output_dir' - # parameter). So, if necessary, fix that now by moving the .o - # files into the desired output directory. (The alternative, of - # course, is to compile one-at-a-time with a -o option. 6 of one, - # 12/2 of the other...) - - if output_dir: - for i in range (len (objects)): - src = os.path.basename (objects[i]) - objects[i] = self.move_file (src, output_dir) + # Build list of (source,object) tuples for convenience + srcobj = [] + for i in range (len (sources)): + srcobj.append ((sources[i], objects[i])) + + # Compile all source files that weren't eliminated by + # 'newer_pairwise()'. + # XXX use of ccflags_shared means we're blithely assuming + # that we're compiling for inclusion in a shared object! + # (will have to fix this when I add the ability to build a + # new Python) + cc_args = ['-c'] + pp_opts + self.ccflags + self.ccflags_shared + if extra_preargs: + cc_args[:0] = extra_preargs + if extra_postargs is None: + extra_postargs = [] + + for (source,object) in srcobj: + self.spawn ([self.cc] + cc_args + + [source, '-o', object] + + extra_postargs) # Have to re-fetch list of object filenames, because we want to # return *all* of them, including those that weren't recompiled on -- cgit v1.2.1 From 7c7b43f5c4d40dd9eb5524a4babd50f526cee53d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 3 Feb 2000 23:07:54 +0000 Subject: Improved an error message. Announce when we start building each extension (better feedback). --- command/build_ext.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 4f7e53ff..126cf60a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -197,10 +197,13 @@ class BuildExt (Command): sources = build_info.get ('sources') if sources is None or type (sources) not in (ListType, TupleType): raise DistutilsValueError, \ - "in ext_modules option, 'sources' must be present " + \ - "and must be a list of source filenames" + ("in ext_modules option (extension '%s'), " + + "'sources' must be present and must be " + + "a list of source filenames") % extension_name sources = list (sources) + self.announce ("building '%s' extension" % extension_name) + # First step: compile the source code to object files. This # drops the object files in the current directory, regardless # of where the source is (may be a bad thing, but that's how a -- cgit v1.2.1 From 139d5ff59d0b42ffd4a768d3fffe2b8f0320bc9d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Feb 2000 02:23:16 +0000 Subject: Tweaked various comments, docstrings, and error messages. --- command/build_ext.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 126cf60a..b253c55d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -103,6 +103,11 @@ class BuildExt (Command): if exec_py_include != py_include: self.include_dirs.insert (0, exec_py_include) + # XXX how the heck are 'self.define' and 'self.undef' supposed to + # be set? + + # set_final_options () + def run (self): @@ -152,6 +157,11 @@ class BuildExt (Command): def check_extensions_list (self, extensions): + """Ensure that the list of extensions (presumably provided as a + command option 'extensions') is valid, i.e. it is a list of + 2-tuples, where the tuples are (extension_name, build_info_dict). + Raise DistutilsValueError if the structure is invalid anywhere; + just returns otherwise.""" if type (extensions) is not ListType: raise DistutilsValueError, \ @@ -171,7 +181,7 @@ class BuildExt (Command): if type (ext[1]) is not DictionaryType: raise DistutilsValueError, \ "second element of each tuple in 'ext_modules' " + \ - "must be a dictionary" + "must be a dictionary (build info)" # end sanity-check for @@ -197,7 +207,7 @@ class BuildExt (Command): sources = build_info.get ('sources') if sources is None or type (sources) not in (ListType, TupleType): raise DistutilsValueError, \ - ("in ext_modules option (extension '%s'), " + + ("in 'ext_modules' option (extension '%s'), " + "'sources' must be present and must be " + "a list of source filenames") % extension_name sources = list (sources) -- cgit v1.2.1 From 57d9abc5b58cf0fdc8a19692abdb934f5e07f52b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Feb 2000 02:23:59 +0000 Subject: New command to build C (and C++, hopefully) libraries needed by extensions in the current distribution: motivated by PIL's libImaging. --- command/build_clib.py | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++ command/build_lib.py | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 command/build_clib.py create mode 100644 command/build_lib.py diff --git a/command/build_clib.py b/command/build_clib.py new file mode 100644 index 00000000..c638fd5f --- /dev/null +++ b/command/build_clib.py @@ -0,0 +1,161 @@ +"""distutils.command.build_lib + +Implements the Distutils 'build_lib' command, to build a C/C++ library +that is included in the module distribution and needed by an extension +module.""" + +# created (an empty husk) 1999/12/18, Greg Ward +# fleshed out 2000/02/03-04 + +__rcsid__ = "$Id$" + + +# XXX this module has *lots* of code ripped-off quite transparently from +# build_ext.py -- not surprisingly really, as the work required to build +# a static library from a collection of C source files is not really all +# that different from what's required to build a shared object file from +# a collection of C source files. Nevertheless, I haven't done the +# necessary refactoring to account for the overlap in code between the +# two modules, mainly because a number of subtle details changed in the +# cut 'n paste. Sigh. + +import os, string +from types import * +from distutils.core import Command +from distutils.errors import * +from distutils.ccompiler import new_compiler + + +class BuildLib (Command): + + options = [] + + def set_default_options (self): + # List of libraries to build + self.libraries = None + + # Compilation options for all libraries + self.include_dirs = None + self.define = None + self.undef = None + + # set_default_options() + + def set_final_options (self): + self.libraries = self.distribution.libraries + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + if type (self.include_dirs) is StringType: + self.include_dirs = string.split (self.include_dirs, + os.pathsep) + + # XXX same as for build_ext -- what about 'self.define' and + # 'self.undef' ? + + # set_final_options() + + + def run (self): + + if not self.libraries: + return + self.check_library_list (self.libraries) + + # Yech -- this is cut 'n pasted from build_ext.py! + self.compiler = new_compiler (plat=os.environ.get ('PLAT'), + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) + if self.include_dirs is not None: + self.compiler.set_include_dirs (self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name,value) in self.define: + self.compiler.define_macro (name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro (macro) + + self.build_libraries (self.libraries) + + # run() + + + def check_library_list (self, libraries): + """Ensure that the list of libraries (presumably provided as a + command option 'libraries') is valid, i.e. it is a list of + 2-tuples, where the tuples are (library_name, build_info_dict). + Raise DistutilsValueError if the structure is invalid anywhere; + just returns otherwise.""" + + # Yechh, blecch, ackk: this is ripped straight out of build_ext.py, + # with only names changed to protect the innocent! + + if type (libraries) is not ListType: + raise DistutilsValueError, \ + "'libraries' option must be a list of tuples" + + for lib in libraries: + if type (lib) is not TupleType and len (lib) != 2: + raise DistutilsValueError, \ + "each element of 'libraries' must a 2-tuple" + + if type (lib[0]) is not StringType: + raise DistutilsValueError, \ + "first element of each tuple in 'libraries' " + \ + "must be a string (the library name)" + if type (lib[1]) is not DictionaryType: + raise DistutilsValueError, \ + "second element of each tuple in 'libraries' " + \ + "must be a dictionary (build info)" + # for lib + + # check_library_list () + + + def build_libraries (self, libraries): + + compiler = self.compiler + + for (lib_name, build_info) in libraries: + sources = build_info.get ('sources') + if sources is None or type (sources) not in (ListType, TupleType): + raise DistutilsValueError, \ + ("in 'libraries' option (library '%s'), " + + "'sources' must be present and must be " + + "a list of source filenames") % lib_name + sources = list (sources) + + self.announce ("building '%s' library" % lib_name) + + # Extract the directory the library is intended to go in -- + # note translation from "universal" slash-separated form to + # current platform's pathname convention (so we can use the + # string for actual filesystem use). + path = tuple (string.split (lib_name, '/')[:-1]) + if path: + lib_dir = apply (os.path.join, path) + else: + lib_dir = '' + + # First, compile the source code to object files in the library + # directory. (This should probably change to putting object + # files in a temporary build directory.) + macros = build_info.get ('macros') + include_dirs = build_info.get ('include_dirs') + objects = self.compiler.compile (sources, + macros=macros, + include_dirs=include_dirs, + output_dir=lib_dir) + + # Now "link" the object files together into a static library. + # (On Unix at least, this isn't really linking -- it just + # builds an archive. Whatever.) + self.compiler.link_static_lib (objects, lib_name) + + # for libraries + + # build_libraries () + + +# class BuildLib diff --git a/command/build_lib.py b/command/build_lib.py new file mode 100644 index 00000000..c638fd5f --- /dev/null +++ b/command/build_lib.py @@ -0,0 +1,161 @@ +"""distutils.command.build_lib + +Implements the Distutils 'build_lib' command, to build a C/C++ library +that is included in the module distribution and needed by an extension +module.""" + +# created (an empty husk) 1999/12/18, Greg Ward +# fleshed out 2000/02/03-04 + +__rcsid__ = "$Id$" + + +# XXX this module has *lots* of code ripped-off quite transparently from +# build_ext.py -- not surprisingly really, as the work required to build +# a static library from a collection of C source files is not really all +# that different from what's required to build a shared object file from +# a collection of C source files. Nevertheless, I haven't done the +# necessary refactoring to account for the overlap in code between the +# two modules, mainly because a number of subtle details changed in the +# cut 'n paste. Sigh. + +import os, string +from types import * +from distutils.core import Command +from distutils.errors import * +from distutils.ccompiler import new_compiler + + +class BuildLib (Command): + + options = [] + + def set_default_options (self): + # List of libraries to build + self.libraries = None + + # Compilation options for all libraries + self.include_dirs = None + self.define = None + self.undef = None + + # set_default_options() + + def set_final_options (self): + self.libraries = self.distribution.libraries + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + if type (self.include_dirs) is StringType: + self.include_dirs = string.split (self.include_dirs, + os.pathsep) + + # XXX same as for build_ext -- what about 'self.define' and + # 'self.undef' ? + + # set_final_options() + + + def run (self): + + if not self.libraries: + return + self.check_library_list (self.libraries) + + # Yech -- this is cut 'n pasted from build_ext.py! + self.compiler = new_compiler (plat=os.environ.get ('PLAT'), + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) + if self.include_dirs is not None: + self.compiler.set_include_dirs (self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name,value) in self.define: + self.compiler.define_macro (name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro (macro) + + self.build_libraries (self.libraries) + + # run() + + + def check_library_list (self, libraries): + """Ensure that the list of libraries (presumably provided as a + command option 'libraries') is valid, i.e. it is a list of + 2-tuples, where the tuples are (library_name, build_info_dict). + Raise DistutilsValueError if the structure is invalid anywhere; + just returns otherwise.""" + + # Yechh, blecch, ackk: this is ripped straight out of build_ext.py, + # with only names changed to protect the innocent! + + if type (libraries) is not ListType: + raise DistutilsValueError, \ + "'libraries' option must be a list of tuples" + + for lib in libraries: + if type (lib) is not TupleType and len (lib) != 2: + raise DistutilsValueError, \ + "each element of 'libraries' must a 2-tuple" + + if type (lib[0]) is not StringType: + raise DistutilsValueError, \ + "first element of each tuple in 'libraries' " + \ + "must be a string (the library name)" + if type (lib[1]) is not DictionaryType: + raise DistutilsValueError, \ + "second element of each tuple in 'libraries' " + \ + "must be a dictionary (build info)" + # for lib + + # check_library_list () + + + def build_libraries (self, libraries): + + compiler = self.compiler + + for (lib_name, build_info) in libraries: + sources = build_info.get ('sources') + if sources is None or type (sources) not in (ListType, TupleType): + raise DistutilsValueError, \ + ("in 'libraries' option (library '%s'), " + + "'sources' must be present and must be " + + "a list of source filenames") % lib_name + sources = list (sources) + + self.announce ("building '%s' library" % lib_name) + + # Extract the directory the library is intended to go in -- + # note translation from "universal" slash-separated form to + # current platform's pathname convention (so we can use the + # string for actual filesystem use). + path = tuple (string.split (lib_name, '/')[:-1]) + if path: + lib_dir = apply (os.path.join, path) + else: + lib_dir = '' + + # First, compile the source code to object files in the library + # directory. (This should probably change to putting object + # files in a temporary build directory.) + macros = build_info.get ('macros') + include_dirs = build_info.get ('include_dirs') + objects = self.compiler.compile (sources, + macros=macros, + include_dirs=include_dirs, + output_dir=lib_dir) + + # Now "link" the object files together into a static library. + # (On Unix at least, this isn't really linking -- it just + # builds an archive. Whatever.) + self.compiler.link_static_lib (objects, lib_name) + + # for libraries + + # build_libraries () + + +# class BuildLib -- cgit v1.2.1 From ed153d7c608a30aeda273198189cd1539d01be2b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Feb 2000 02:24:16 +0000 Subject: Run the 'build_lib' command before building extensions, if necessary. --- command/build.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/command/build.py b/command/build.py index e6c87bfc..768db120 100644 --- a/command/build.py +++ b/command/build.py @@ -48,6 +48,12 @@ class Build (Command): if self.distribution.packages or self.distribution.py_modules: self.run_peer ('build_py') + # Build any standalone C libraries next -- they're most likely to + # be needed by extension modules, so obviously have to be done + # first! + if self.distribution.libraries: + self.run_peer ('build_lib') + # And now 'build_ext' -- compile extension modules and put them # into the build tree if self.distribution.ext_modules: -- cgit v1.2.1 From 47356dea6774b33b37dc74067ef67696a40567e6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Feb 2000 02:24:52 +0000 Subject: Added 'libraries' option for use by the 'build_lib' command. Typo fix. --- core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core.py b/core.py index 7a646dad..88e889b2 100644 --- a/core.py +++ b/core.py @@ -188,6 +188,7 @@ class Distribution: self.packages = None self.package_dir = None self.py_modules = None + self.libraries = None self.ext_modules = None self.ext_package = None self.include_dirs = None @@ -314,7 +315,7 @@ class Distribution: # known options if not (hasattr (cmd_obj, 'options') and type (cmd_obj.options) is ListType): - raise DistutilsClasserror, \ + raise DistutilsClassError, \ ("command class %s must provide an 'options' attribute "+ "(a list of tuples)") % \ cmd_obj.__class__ -- cgit v1.2.1 From b08bf9ee895d020cd5431304cc50aead7c0ba272 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Feb 2000 02:37:15 +0000 Subject: Ditch .def file kludge for (much smaller) /export option kludge. --- command/build_ext.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index b253c55d..0f2d1a7b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -236,18 +236,7 @@ class BuildExt (Command): library_dirs = build_info.get ('library_dirs') extra_args = build_info.get ('extra_link_args') or [] if self.compiler.compiler_type == 'msvc': - def_file = build_info.get ('def_file') - if def_file is None: - source_dir = os.path.dirname (sources[0]) - ext_base = (string.split (extension_name, '.'))[-1] - def_file = os.path.join (source_dir, "%s.def" % ext_base) - if not os.path.exists (def_file): - self.warn ("file '%s' not found: " % def_file + - "might have problems building DLL") - def_file = None - - if def_file is not None: - extra_args.append ('/DEF:' + def_file) + extra_args.append ('/export:init%s' % extension_name) ext_filename = self.extension_filename \ (extension_name, self.package) -- cgit v1.2.1 From 23cd2df59e48224e36288f7437d180356b55da39 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Feb 2000 02:39:44 +0000 Subject: Revised version (thank to Thomas Heller and Robin Becker) that tries a lot harder to find the MSVC compiler (mainly by using the registry). --- msvccompiler.py | 101 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index a07b4b8e..10cd6081 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -10,10 +10,96 @@ __rcsid__ = "$Id$" import os import sys +import string from distutils.errors import * from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options +def _devstudio_versions(): + "Get a list of devstudio versions" + try: + import win32api + import win32con + except ImportError: + return None + + K = 'Software\\Microsoft\\Devstudio' + L = [] + for base in (win32con.HKEY_CLASSES_ROOT,win32con.HKEY_LOCAL_MACHINE,win32con.HKEY_CURRENT_USER,win32con.HKEY_USERS): + try: + k = win32api.RegOpenKeyEx(base,K) + i = 0 + while 1: + try: + p = win32api.RegEnumKey(k,i) + if p[0] in '123456789' and p not in L: + L.append(p) + except win32api.error: + break + i = i + 1 + except win32api.error: + pass + L.sort() + L.reverse() + return L + +def _msvc_get_paths(path, vNum='6.0', platform='x86'): + "Get a devstudio path (include, lib or path)" + try: + import win32api + import win32con + except ImportError: + return None + + L = [] + if path=='lib': path= 'Library' + path = string.upper(path + ' Dirs') + K = 'Software\\Microsoft\\Devstudio\\%s\\Build System\\Components\\Platforms\\Win32 (%s)\\Directories' \ + % (vNum,platform) + for base in (win32con.HKEY_CLASSES_ROOT,win32con.HKEY_LOCAL_MACHINE,win32con.HKEY_CURRENT_USER,win32con.HKEY_USERS): + try: + k = win32api.RegOpenKeyEx(base,K) + i = 0 + while 1: + try: + (p,v,t) = win32api.RegEnumValue(k,i) + if string.upper(p)==path: + V = string.split(v,';') + for v in V: + if v=='' or v in L: continue + L.append(v) + break + i = i + 1 + except win32api.error: + break + except win32api.error: + pass + return L + +def _find_exe(exe): + for v in _devstudio_versions(): + for p in _msvc_get_paths('path',v): + fn=os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): return fn + + #didn't find it; try existing path + try: + for p in string.split(os.environ['Path'],';'): + fn=os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): return fn + except: + pass + return exe #last desperate hope + +def _find_SET(n): + for v in _devstudio_versions(): + p=_msvc_get_paths(n,v) + if p!=[]: return p + return [] + +def _do_SET(n): + p=_find_SET(n) + if p!=[]: os.environ[n]=string.join(p,';') class MSVCCompiler (CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, @@ -34,12 +120,21 @@ class MSVCCompiler (CCompiler) : self.add_library ( "python" + sys.version[0] + sys.version[2] ) self.add_library_dir( os.path.join( sys.exec_prefix, 'libs' ) ) - self.cc = "cl.exe" - self.link = "link.exe" + self.cc = _find_exe("cl.exe") + self.link = _find_exe("link.exe") + _do_SET('lib') + _do_SET('include') + path=_find_SET('path') + try: + for p in string.split(os.environ['path'],';'): + path.append(p) + except KeyError: + pass + os.environ['path'] = string.join(path,';') self.preprocess_options = None self.compile_options = [ '/nologo', '/Ox', '/MD' ] - self.ldflags_shared = ['/DLL', '/nologo'] + self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] self.ldflags_static = [ '/nologo'] -- cgit v1.2.1 From 7a31064ca3c8fed8497ec3db7cdb3ee89aba2731 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 8 Feb 2000 15:55:42 +0000 Subject: get_config_h_filename(): Support NT as well as Posix systems. _init_nt(): Use get_config_h_filename() instead of figuring out the name directly. g['SO'] should be set to '.pyd'. Adjust some minor coding nits. --- sysconfig.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 0e40cbc3..e291aec2 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -19,9 +19,12 @@ exec_prefix = os.path.normpath (sys.exec_prefix) def get_config_h_filename(): """Return full pathname of installed config.h file.""" - return os.path.join(exec_prefix, - "include", "python" + sys.version[:3], - "config.h") + if os.name == "nt": + return os.path.join(exec_prefix, "include", "config.h") + else: + return os.path.join(exec_prefix, + "include", "python" + sys.version[:3], + "config.h") def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" @@ -136,20 +139,20 @@ def _init_posix(): def _init_nt(): """Initialize the module as appropriate for NT""" - g=globals() + g = globals() # load config.h, though I don't know how useful this is - parse_config_h(open( - os.path.join(exec_prefix, "include", "config.h")), g) + parse_config_h(open(get_config_h_filename()), g) # set basic install directories - g['LIBDEST']=os.path.join(exec_prefix, "Lib") - g['BINLIBDEST']= os.path.join(exec_prefix, "Lib") + g['LIBDEST'] = os.path.join(exec_prefix, "Lib") + g['BINLIBDEST'] = os.path.join(exec_prefix, "Lib") # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = os.path.join (prefix, 'include' ) + g['INCLUDEPY'] = os.path.join(prefix, 'include') - g['SO'] = '.dll' + g['SO'] = '.pyd' g['exec_prefix'] = exec_prefix + try: exec "_init_" + os.name except NameError: @@ -158,5 +161,6 @@ except NameError: else: exec "_init_%s()" % os.name + del _init_posix del _init_nt -- cgit v1.2.1 From 0c9bccee1cec8339ed0fa0f331f2730ad2c41d21 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 9 Feb 2000 02:16:14 +0000 Subject: Added 'debug' flag to compile and link method signatures. Doc fix: several paragraphs under 'link_static_lib()' moved to 'link_shared_lib()', where they belong. --- ccompiler.py | 66 +++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 036cbe9f..7d2d9f58 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -263,6 +263,7 @@ class CCompiler: output_dir=None, macros=None, include_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): """Compile one or more C/C++ source files. 'sources' must be @@ -277,9 +278,12 @@ class CCompiler: undefines a macro. Later definitions/redefinitions/ undefinitions take precedence. - 'include_dirs', if given, must be a list of strings, the directories - to add to the default include file search path for this - compilation only. + 'include_dirs', if given, must be a list of strings, the + directories to add to the default include file search path for + this compilation only. + + 'debug' is a boolean; if true, the compiler will be instructed + to output debug symbols in (or alongside) the object file(s). 'extra_preargs' and 'extra_postargs' are optional lists of extra command-line arguments that will be, respectively, prepended or @@ -295,7 +299,8 @@ class CCompiler: def link_static_lib (self, objects, output_libname, - output_dir=None): + output_dir=None, + debug=0): """Link a bunch of stuff together to create a static library file. The "bunch of stuff" consists of the list of object files supplied as 'objects', the extra object files supplied @@ -304,8 +309,32 @@ class CCompiler: 'set_libraries()', and the libraries supplied as 'libraries' (if any). - 'output_libname' should be a library name, not a filename; - the filename will be inferred from the library name. + 'output_libname' should be a library name, not a filename; the + filename will be inferred from the library name. 'output_dir' + is the directory where the library file will be put. + + 'debug' is a boolean; if true, debugging information will be + included in the library (note that on most platforms, it is the + compile step where this matters: the 'debug' flag is included + here just for consistency).""" + + pass + + + def link_shared_lib (self, + objects, + output_libname, + output_dir=None, + libraries=None, + library_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None): + """Link a bunch of stuff together to create a shared library + file. Has the same effect as 'link_static_lib()' except + that the filename inferred from 'output_libname' will most + likely be different, and the type of file generated will + almost certainly be different 'libraries' is a list of libraries to link against. These are library names, not filenames, since they're translated into @@ -321,26 +350,15 @@ class CCompiler: default and those supplied to 'add_library_dir()' and/or 'set_library_dirs()'. + 'debug' is as for 'compile()' and 'link_static_lib()', with the + slight distinction that it actually matters on most platforms + (as opposed to 'link_static_lib()', which includes a 'debug' + flag mostly for form's sake). + 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except of course that they supply command-line arguments - for the particular linker being used).""" - - pass - + for the particular linker being used).""" - def link_shared_lib (self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - extra_preargs=None, - extra_postargs=None): - """Link a bunch of stuff together to create a shared library - file. Has the same effect as 'link_static_lib()' except - that the filename inferred from 'output_libname' will most - likely be different, and the type of file generated will - almost certainly be different.""" pass @@ -350,6 +368,7 @@ class CCompiler: output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): """Link a bunch of stuff together to create a shared object @@ -367,6 +386,7 @@ class CCompiler: output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): """Link a bunch of stuff together to create a binary executable -- cgit v1.2.1 From 8c5bff29a20dcd4e9ce9b6bd2b4e96f8e6b31624 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 9 Feb 2000 02:17:00 +0000 Subject: Added 'debug' flags to compile and link methods, and modified code to add '-g' flag to compiler/linker command lines when it's true. --- unixccompiler.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 9ace9860..cc0d7723 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -102,6 +102,7 @@ class UnixCCompiler (CCompiler): output_dir=None, macros=None, include_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -149,6 +150,8 @@ class UnixCCompiler (CCompiler): # (will have to fix this when I add the ability to build a # new Python) cc_args = ['-c'] + pp_opts + self.ccflags + self.ccflags_shared + if debug: + cc_args[:0] = ['-g'] if extra_preargs: cc_args[:0] = extra_preargs if extra_postargs is None: @@ -194,7 +197,8 @@ class UnixCCompiler (CCompiler): def link_static_lib (self, objects, output_libname, - output_dir=None): + output_dir=None, + debug=0): if type (objects) not in (ListType, TupleType): raise TypeError, \ @@ -234,6 +238,7 @@ class UnixCCompiler (CCompiler): output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): # XXX should we sanity check the library name? (eg. no @@ -244,6 +249,7 @@ class UnixCCompiler (CCompiler): output_dir, libraries, library_dirs, + debug, extra_preargs, extra_postargs) @@ -254,6 +260,7 @@ class UnixCCompiler (CCompiler): output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -279,6 +286,8 @@ class UnixCCompiler (CCompiler): if self.force or newer: ld_args = self.ldflags_shared + objects + \ lib_opts + ['-o', output_filename] + if debug: + ld_args[:0] = ['-g'] if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: @@ -296,6 +305,7 @@ class UnixCCompiler (CCompiler): output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -318,6 +328,8 @@ class UnixCCompiler (CCompiler): if self.force or newer: ld_args = objects + lib_opts + ['-o', output_filename] + if debug: + ld_args[:0] = ['-g'] if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: -- cgit v1.2.1 From 04c8638c9348a33880f8025b4cce8dc488117aa1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 9 Feb 2000 02:18:39 +0000 Subject: Added 'debug' flags to compile and link methods, and added dummy code for someone who knows Windows/MSVC++ to come along and add the right flags. Comment noting that 'link_static_lib()' signature is inconsistent with the other compiler classes (uh-oh!) --- msvccompiler.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 10cd6081..cf4be0dc 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -159,6 +159,7 @@ class MSVCCompiler (CCompiler) : output_dir=None, macros=None, include_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -190,6 +191,8 @@ class MSVCCompiler (CCompiler) : cc_args = self.compile_options + \ base_pp_opts + \ [outputOpt, inputOpt] + if debug: + pass # XXX what goes here? if extra_preargs: cc_args[:0] = extra_preargs if extra_postargs: @@ -200,15 +203,17 @@ class MSVCCompiler (CCompiler) : return objectFiles - # XXX this is kind of useless without 'link_binary()' or - # 'link_executable()' or something -- or maybe 'link_static_lib()' - # should not exist at all, and we just have 'link_binary()'? + # XXX the signature of this method is different from CCompiler and + # UnixCCompiler -- but those extra parameters (libraries, library_dirs) + # are actually used. So: are they really *needed*, or can they be + # ditched? If needed, the CCompiler API will have to change... def link_static_lib (self, objects, output_libname, output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -223,6 +228,8 @@ class MSVCCompiler (CCompiler) : ld_args = self.ldflags_static + lib_opts + \ objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: @@ -237,6 +244,7 @@ class MSVCCompiler (CCompiler) : output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -267,6 +275,8 @@ class MSVCCompiler (CCompiler) : ld_args = self.ldflags_shared + lib_opts + \ objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: -- cgit v1.2.1 From f0b2eb38689af9acf61db1e126458b28f36dff7d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 9 Feb 2000 02:19:49 +0000 Subject: Added 'debug' option (just there for 'build_ext' and 'build_lib' commands to fallback to if the user doesn't set it for those commands. --- command/build.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/build.py b/command/build.py index 768db120..82a4e6c2 100644 --- a/command/build.py +++ b/command/build.py @@ -20,6 +20,8 @@ class Build (Command): "directory for platform-shared files"), ('build-platlib=', 'p', "directory for platform-specific files"), + ('debug', 'g', + "compile extensions and libraries with debugging information"), ] def set_default_options (self): @@ -28,6 +30,7 @@ class Build (Command): # (unless overridden by the user or client) self.build_lib = None self.build_platlib = None + self.debug = None def set_final_options (self): # 'build_lib' and 'build_platlib' just default to 'lib' and -- cgit v1.2.1 From bc5bb15bc3510ccab5986fd22151e75d33776a46 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 9 Feb 2000 02:20:14 +0000 Subject: Added 'debug' option, and changed compile/link calls to use it. --- command/build_clib.py | 12 +++++++++--- command/build_ext.py | 16 ++++++++++++---- command/build_lib.py | 12 +++++++++--- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index c638fd5f..65712818 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -28,7 +28,9 @@ from distutils.ccompiler import new_compiler class BuildLib (Command): - options = [] + options = [('debug', 'g', + "compile with debugging information"), + ] def set_default_options (self): # List of libraries to build @@ -38,10 +40,13 @@ class BuildLib (Command): self.include_dirs = None self.define = None self.undef = None + self.debug = None # set_default_options() def set_final_options (self): + self.set_undefined_options ('build', + ('debug', 'debug')) self.libraries = self.distribution.libraries if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] @@ -146,12 +151,13 @@ class BuildLib (Command): objects = self.compiler.compile (sources, macros=macros, include_dirs=include_dirs, - output_dir=lib_dir) + output_dir=lib_dir, + debug=self.debug) # Now "link" the object files together into a static library. # (On Unix at least, this isn't really linking -- it just # builds an archive. Whatever.) - self.compiler.link_static_lib (objects, lib_name) + self.compiler.link_static_lib (objects, lib_name, debug=self.debug) # for libraries diff --git a/command/build_ext.py b/command/build_ext.py index 0f2d1a7b..14c2234e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -58,6 +58,8 @@ class BuildExt (Command): "directories to search for shared C libraries at runtime"), ('link-objects=', 'O', "extra explicit link objects to include in the link"), + ('debug', 'g', + "compile/link with debugging information"), ] @@ -73,12 +75,15 @@ class BuildExt (Command): self.library_dirs = None self.rpath = None self.link_objects = None + self.debug = None def set_final_options (self): from distutils import sysconfig - self.set_undefined_options ('build', ('build_platlib', 'build_dir')) + self.set_undefined_options ('build', + ('build_platlib', 'build_dir'), + ('debug', 'debug')) if self.package is None: self.package = self.distribution.ext_package @@ -223,7 +228,8 @@ class BuildExt (Command): include_dirs = build_info.get ('include_dirs') self.compiler.compile (sources, macros=macros, - include_dirs=include_dirs) + include_dirs=include_dirs, + debug=self.debug) # Now link the object files together into a "shared object" -- # of course, first we have to figure out all the other things @@ -236,7 +242,8 @@ class BuildExt (Command): library_dirs = build_info.get ('library_dirs') extra_args = build_info.get ('extra_link_args') or [] if self.compiler.compiler_type == 'msvc': - extra_args.append ('/export:init%s' % extension_name) + mod_name = string.split (extension_name, '.')[-1] + extra_args.append ('/export:init%s' % mod_name) ext_filename = self.extension_filename \ (extension_name, self.package) @@ -246,7 +253,8 @@ class BuildExt (Command): self.compiler.link_shared_object (objects, ext_filename, libraries=libraries, library_dirs=library_dirs, - extra_postargs=extra_args) + extra_postargs=extra_args, + debug=self.debug) # build_extensions () diff --git a/command/build_lib.py b/command/build_lib.py index c638fd5f..65712818 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -28,7 +28,9 @@ from distutils.ccompiler import new_compiler class BuildLib (Command): - options = [] + options = [('debug', 'g', + "compile with debugging information"), + ] def set_default_options (self): # List of libraries to build @@ -38,10 +40,13 @@ class BuildLib (Command): self.include_dirs = None self.define = None self.undef = None + self.debug = None # set_default_options() def set_final_options (self): + self.set_undefined_options ('build', + ('debug', 'debug')) self.libraries = self.distribution.libraries if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] @@ -146,12 +151,13 @@ class BuildLib (Command): objects = self.compiler.compile (sources, macros=macros, include_dirs=include_dirs, - output_dir=lib_dir) + output_dir=lib_dir, + debug=self.debug) # Now "link" the object files together into a static library. # (On Unix at least, this isn't really linking -- it just # builds an archive. Whatever.) - self.compiler.link_static_lib (objects, lib_name) + self.compiler.link_static_lib (objects, lib_name, debug=self.debug) # for libraries -- cgit v1.2.1 From b1228096c7a2efeaab3d677ec72cdb912c0d5252 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 10 Feb 2000 02:15:52 +0000 Subject: Patch from Thomas heller: * don't need to mention python.lib -- it's done by a pragma * add debug flags for compile and link, and use them * fix 'link_shared_library()' to pass everything to 'link_shared_object()' * change filename when shared object with debug info (ugh) --- msvccompiler.py | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index cf4be0dc..7c179782 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -114,10 +114,6 @@ class MSVCCompiler (CCompiler) : CCompiler.__init__ (self, verbose, dry_run, force) - # XXX This is a nasty dependency to add on something otherwise - # pretty clean. move it to build_ext under an nt specific part. - # shared libraries need to link against python15.lib - self.add_library ( "python" + sys.version[0] + sys.version[2] ) self.add_library_dir( os.path.join( sys.exec_prefix, 'libs' ) ) self.cc = _find_exe("cl.exe") @@ -133,8 +129,14 @@ class MSVCCompiler (CCompiler) : os.environ['path'] = string.join(path,';') self.preprocess_options = None self.compile_options = [ '/nologo', '/Ox', '/MD' ] + self.compile_options_debug = [ + '/nologo', '/Od', '/MDd', '/Z7', '/D_DEBUG' + ] self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG' + ] self.ldflags_static = [ '/nologo'] @@ -175,6 +177,11 @@ class MSVCCompiler (CCompiler) : self.include_dirs + include_dirs) base_pp_opts.append('/c') + + if debug: + compile_options = self.compile_options_debug + else: + compile_options = self.compile_options for srcFile in sources: base,ext = os.path.splitext(srcFile) @@ -188,11 +195,10 @@ class MSVCCompiler (CCompiler) : inputOpt = fileOpt + srcFile outputOpt = "/Fo" + objFile - cc_args = self.compile_options + \ + cc_args = compile_options + \ base_pp_opts + \ [outputOpt, inputOpt] - if debug: - pass # XXX what goes here? + if extra_preargs: cc_args[:0] = extra_preargs if extra_postargs: @@ -251,7 +257,14 @@ class MSVCCompiler (CCompiler) : # XXX should we sanity check the library name? (eg. no # slashes) self.link_shared_object (objects, - self.shared_library_name(output_libname)) + self.shared_library_name(output_libname), + output_dir=output_dir, + libraries=libraries, + library_dirs=library_dirs, + debug=debug, + extra_preargs=extra_preargs, + extra_postargs=extra_postargs) + def link_shared_object (self, objects, @@ -259,6 +272,7 @@ class MSVCCompiler (CCompiler) : output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): """Link a bunch of stuff together to create a shared object @@ -273,10 +287,18 @@ class MSVCCompiler (CCompiler) : self.library_dirs + library_dirs, self.libraries + libraries) - ld_args = self.ldflags_shared + lib_opts + \ - objects + ['/OUT:' + output_filename] if debug: - pass # XXX what goes here? + ldflags = self.ldflags_shared_debug + basename, ext = os.path.splitext (output_filename) + #XXX not sure this belongs here + # extensions in debug_mode are named 'module_d.pyd' + output_filename = basename + '_d' + ext + else: + ldflags = self.ldflags_shared + + ld_args = ldflags + lib_opts + \ + objects + ['/OUT:' + output_filename] + if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: -- cgit v1.2.1 From e9cac8bea6e2f62aa1dd28e6c91958394ba0aecf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 10 Feb 2000 02:17:06 +0000 Subject: Path from Thomas Heller: resurrect the .def file kludge while preserving the /export option mini-kludge. --- command/build_ext.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 14c2234e..42eab479 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -241,9 +241,21 @@ class BuildExt (Command): libraries = build_info.get ('libraries') library_dirs = build_info.get ('library_dirs') extra_args = build_info.get ('extra_link_args') or [] + if self.compiler.compiler_type == 'msvc': - mod_name = string.split (extension_name, '.')[-1] - extra_args.append ('/export:init%s' % mod_name) + def_file = build_info.get ('def_file') + if def_file is None: + source_dir = os.path.dirname (sources[0]) + ext_base = (string.split (extension_name, '.'))[-1] + def_file = os.path.join (source_dir, "%s.def" % ext_base) + if not os.path.exists (def_file): + def_file = None + + if def_file is not None: + extra_args.append ('/DEF:' + def_file) + else: + modname = string.split (extension_name, '.')[-1] + extra_args.append('/export:init%s'%modname) ext_filename = self.extension_filename \ (extension_name, self.package) -- cgit v1.2.1 From 5d779ebd6d24ee05e11c670f6ed2bac639b33a6d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 10 Feb 2000 02:51:32 +0000 Subject: Typecheck 'output_dir' argument to compile/link methods. --- unixccompiler.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/unixccompiler.py b/unixccompiler.py index cc0d7723..770a543c 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -106,6 +106,8 @@ class UnixCCompiler (CCompiler): extra_preargs=None, extra_postargs=None): + if type (output_dir) not in (StringType, NoneType): + raise TypeError, "'output_dir' must be a string or None" if output_dir is None: output_dir = self.output_dir if macros is None: @@ -205,6 +207,8 @@ class UnixCCompiler (CCompiler): "'objects' must be a list or tuple of strings" objects = list (objects) + if type (output_dir) not in (StringType, NoneType): + raise TypeError, "'output_dir' must be a string or None" if output_dir is None: output_dir = self.output_dir @@ -270,6 +274,8 @@ class UnixCCompiler (CCompiler): lib_opts = gen_lib_options (self, self.library_dirs + library_dirs, self.libraries + libraries) + if type (output_dir) not in (StringType, NoneType): + raise TypeError, "'output_dir' must be a string or None" if output_dir is not None: output_filename = os.path.join (output_dir, output_filename) -- cgit v1.2.1 From 7fffec2291d4e832411729c8d653c2cbce6a17f7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 10 Feb 2000 02:52:42 +0000 Subject: Stylistic changes to the registry-grovelling code: code formatting, changed function names, dbetter (hopefully) ocstrings, and comments. --- msvccompiler.py | 74 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 7c179782..90ebae0a 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -15,17 +15,26 @@ from distutils.errors import * from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options -def _devstudio_versions(): - "Get a list of devstudio versions" + +def get_devstudio_versions (): + + """Get list of devstudio versions from the Windows registry. Return a + list of strings (???) containing version numbers; the list will be + empty if we were unable to access the registry (eg. couldn't import + a registry-access module) or the appropriate registry keys weren't + found. (XXX is this correct???)""" try: import win32api import win32con except ImportError: - return None + return [] K = 'Software\\Microsoft\\Devstudio' L = [] - for base in (win32con.HKEY_CLASSES_ROOT,win32con.HKEY_LOCAL_MACHINE,win32con.HKEY_CURRENT_USER,win32con.HKEY_USERS): + for base in (win32con.HKEY_CLASSES_ROOT, + win32con.HKEY_LOCAL_MACHINE, + win32con.HKEY_CURRENT_USER, + win32con.HKEY_USERS): try: k = win32api.RegOpenKeyEx(base,K) i = 0 @@ -43,8 +52,11 @@ def _devstudio_versions(): L.reverse() return L -def _msvc_get_paths(path, vNum='6.0', platform='x86'): - "Get a devstudio path (include, lib or path)" +# get_devstudio_versions () + + +def get_msvc_paths (path, version='6.0', platform='x86'): + """Get a devstudio path (include, lib or path).""" try: import win32api import win32con @@ -52,21 +64,26 @@ def _msvc_get_paths(path, vNum='6.0', platform='x86'): return None L = [] - if path=='lib': path= 'Library' + if path=='lib': + path= 'Library' path = string.upper(path + ' Dirs') - K = 'Software\\Microsoft\\Devstudio\\%s\\Build System\\Components\\Platforms\\Win32 (%s)\\Directories' \ - % (vNum,platform) - for base in (win32con.HKEY_CLASSES_ROOT,win32con.HKEY_LOCAL_MACHINE,win32con.HKEY_CURRENT_USER,win32con.HKEY_USERS): + K = ('Software\\Microsoft\\Devstudio\\%s\\' + + 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \ + (version,platform) + for base in (win32con.HKEY_CLASSES_ROOT, + win32con.HKEY_LOCAL_MACHINE, + win32con.HKEY_CURRENT_USER, + win32con.HKEY_USERS): try: k = win32api.RegOpenKeyEx(base,K) i = 0 while 1: try: (p,v,t) = win32api.RegEnumValue(k,i) - if string.upper(p)==path: + if string.upper(p) == path: V = string.split(v,';') for v in V: - if v=='' or v in L: continue + if v == '' or v in L: continue L.append(v) break i = i + 1 @@ -76,30 +93,41 @@ def _msvc_get_paths(path, vNum='6.0', platform='x86'): pass return L +# get_msvc_paths() + + def _find_exe(exe): - for v in _devstudio_versions(): - for p in _msvc_get_paths('path',v): - fn=os.path.join(os.path.abspath(p),exe) - if os.path.isfile(fn): return fn + for v in get_devstudio_versions(): + for p in get_msvc_paths('path',v): + fn = os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): + return fn #didn't find it; try existing path try: for p in string.split(os.environ['Path'],';'): fn=os.path.join(os.path.abspath(p),exe) - if os.path.isfile(fn): return fn - except: + if os.path.isfile(fn): + return fn + # XXX BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD !!!!!!!!!!!!!!!! + except: # XXX WHAT'S BEING CAUGHT HERE?!?!? pass return exe #last desperate hope + def _find_SET(n): - for v in _devstudio_versions(): - p=_msvc_get_paths(n,v) - if p!=[]: return p + for v in get_devstudio_versions(): + p = get_msvc_paths(n,v) + if p: + return p return [] + def _do_SET(n): - p=_find_SET(n) - if p!=[]: os.environ[n]=string.join(p,';') + p = _find_SET(n) + if p: + os.environ[n] = string.join(p,';') + class MSVCCompiler (CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, -- cgit v1.2.1 From 8224dcdac16e8f63eb008892d84bd192e245b11f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 11 Feb 2000 02:47:15 +0000 Subject: Latest patch from Thomas Heller/Robin Becker: * tweak my docstrings * fix None returns to empty list * reshuffle responsibilities between '_find_exe()', '_find_SET()', and the MSVCCompiler constructor -- now the constructor worries about fetching the version list and determining the most recent one * added "/W3" compile option Also, I added/tweaked some docstrings. --- msvccompiler.py | 115 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 68 insertions(+), 47 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 90ebae0a..3073aaea 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -17,12 +17,12 @@ from distutils.ccompiler import \ def get_devstudio_versions (): - """Get list of devstudio versions from the Windows registry. Return a - list of strings (???) containing version numbers; the list will be + list of strings containing version numbers; the list will be empty if we were unable to access the registry (eg. couldn't import a registry-access module) or the appropriate registry keys weren't - found. (XXX is this correct???)""" + found.""" + try: import win32api import win32con @@ -56,12 +56,15 @@ def get_devstudio_versions (): def get_msvc_paths (path, version='6.0', platform='x86'): - """Get a devstudio path (include, lib or path).""" + """Get a list of devstudio directories (include, lib or path). Return + a list of strings; will be empty list if unable to access the + registry or appropriate registry keys not found.""" + try: import win32api import win32con except ImportError: - return None + return [] L = [] if path=='lib': @@ -96,37 +99,45 @@ def get_msvc_paths (path, version='6.0', platform='x86'): # get_msvc_paths() -def _find_exe(exe): - for v in get_devstudio_versions(): - for p in get_msvc_paths('path',v): - fn = os.path.join(os.path.abspath(p),exe) - if os.path.isfile(fn): - return fn +def find_exe (exe, version_number): + """Try to find an MSVC executable program 'exe' (from version + 'version_number' of MSVC) in several places: first, one of the MSVC + program search paths from the registry; next, the directories in the + PATH environment variable. If any of those work, return an absolute + path that is known to exist. If none of them work, just return the + original program name, 'exe'.""" - #didn't find it; try existing path - try: - for p in string.split(os.environ['Path'],';'): - fn=os.path.join(os.path.abspath(p),exe) - if os.path.isfile(fn): - return fn - # XXX BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD !!!!!!!!!!!!!!!! - except: # XXX WHAT'S BEING CAUGHT HERE?!?!? - pass - return exe #last desperate hope - - -def _find_SET(n): - for v in get_devstudio_versions(): - p = get_msvc_paths(n,v) - if p: - return p - return [] - - -def _do_SET(n): - p = _find_SET(n) + for p in get_msvc_paths ('path', version_number): + fn = os.path.join (os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in string.split (os.environ['Path'],';'): + fn = os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): + return fn + + return exe # last desperate hope + + +def _find_SET(name,version_number): + """looks up in the registry and returns a list of values suitable for + use in a SET command eg SET name=value. Normally the value will be a + ';' separated list similar to a path list. + + name is the name of an MSVC path and version_number is a version_number + of an MSVC installation.""" + return get_msvc_paths(name, version_number) + + +def _do_SET(name, version_number): + """sets os.environ[name] to an MSVC path type value obtained from + _find_SET. This is equivalent to a SET command prior to execution of + spawned commands.""" + p=_find_SET(name, version_number) if p: - os.environ[n] = string.join(p,';') + os.environ[name]=string.join(p,';') class MSVCCompiler (CCompiler) : @@ -144,21 +155,31 @@ class MSVCCompiler (CCompiler) : self.add_library_dir( os.path.join( sys.exec_prefix, 'libs' ) ) - self.cc = _find_exe("cl.exe") - self.link = _find_exe("link.exe") - _do_SET('lib') - _do_SET('include') - path=_find_SET('path') - try: - for p in string.split(os.environ['path'],';'): - path.append(p) - except KeyError: - pass - os.environ['path'] = string.join(path,';') + vNum = get_devstudio_versions () + + if vNum: + vNum = vNum[0] # highest version + + self.cc = _find_exe("cl.exe", vNum) + self.link = _find_exe("link.exe", vNum) + _do_SET('lib', vNum) + _do_SET('include', vNum) + path=_find_SET('path', vNum) + try: + for p in string.split(os.environ['path'],';'): + path.append(p) + except KeyError: + pass + os.environ['path'] = string.join(path,';') + else: + # devstudio not found in the registry + self.cc = "cl.exe" + self.link = "link.exe" + self.preprocess_options = None - self.compile_options = [ '/nologo', '/Ox', '/MD' ] + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3' ] self.compile_options_debug = [ - '/nologo', '/Od', '/MDd', '/Z7', '/D_DEBUG' + '/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG' ] self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] -- cgit v1.2.1 From eed228691703b5775b5ad0a6f626d20e73b72091 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 11 Feb 2000 02:52:39 +0000 Subject: Ditched '_find_SET()', since it was a no-value-added wrapper around 'get_msvc_paths()'. Renamed '_do_SET()' to 'set_path_env_var()', tweaked docstring, and cosmetically tweaked code. Stylistic changes to MSVCCompiler constructor (variable renaming and type consistency). --- msvccompiler.py | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 3073aaea..e489c1c8 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -121,23 +121,14 @@ def find_exe (exe, version_number): return exe # last desperate hope -def _find_SET(name,version_number): - """looks up in the registry and returns a list of values suitable for - use in a SET command eg SET name=value. Normally the value will be a - ';' separated list similar to a path list. +def set_path_env_var (name, version_number): + """Set environment variable 'name' to an MSVC path type value obtained + from 'get_msvc_paths()'. This is equivalent to a SET command prior + to execution of spawned commands.""" - name is the name of an MSVC path and version_number is a version_number - of an MSVC installation.""" - return get_msvc_paths(name, version_number) - - -def _do_SET(name, version_number): - """sets os.environ[name] to an MSVC path type value obtained from - _find_SET. This is equivalent to a SET command prior to execution of - spawned commands.""" - p=_find_SET(name, version_number) + p = get_msvc_paths (name, version_number) if p: - os.environ[name]=string.join(p,';') + os.environ[name] = string.join (p,';') class MSVCCompiler (CCompiler) : @@ -155,16 +146,16 @@ class MSVCCompiler (CCompiler) : self.add_library_dir( os.path.join( sys.exec_prefix, 'libs' ) ) - vNum = get_devstudio_versions () + versions = get_devstudio_versions () - if vNum: - vNum = vNum[0] # highest version + if versions: + version = versions[0] # highest version - self.cc = _find_exe("cl.exe", vNum) - self.link = _find_exe("link.exe", vNum) - _do_SET('lib', vNum) - _do_SET('include', vNum) - path=_find_SET('path', vNum) + self.cc = _find_exe("cl.exe", version) + self.link = _find_exe("link.exe", version) + set_path_env_var ('lib', version) + set_path_env_var ('include', version) + path=get_msvc_paths('path', version) try: for p in string.split(os.environ['path'],';'): path.append(p) -- cgit v1.2.1 From 333dbe885f8da8229084192e350fcf4d4881ada8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 17 Feb 2000 23:54:55 +0000 Subject: The 'dist' command is dead -- long live the 'sdist' command! --- command/dist.py | 558 -------------------------------------------------------- 1 file changed, 558 deletions(-) delete mode 100644 command/dist.py diff --git a/command/dist.py b/command/dist.py deleted file mode 100644 index ea61a480..00000000 --- a/command/dist.py +++ /dev/null @@ -1,558 +0,0 @@ -"""distutils.command.dist - -Implements the Distutils 'dist' command (create a source distribution).""" - -# created 1999/09/22, Greg Ward - -__rcsid__ = "$Id$" - -import sys, os, string, re -import fnmatch -from types import * -from glob import glob -from shutil import rmtree -from distutils.core import Command -from distutils.text_file import TextFile -from distutils.errors import DistutilsExecError - - -# Possible modes of operation: -# - require an explicit manifest that lists every single file (presumably -# along with a way to auto-generate the manifest) -# - require an explicit manifest, but allow it to have globs or -# filename patterns of some kind (and also have auto-generation) -# - allow an explict manifest, but automatically augment it at runtime -# with the source files mentioned in 'packages', 'py_modules', and -# 'ext_modules' (and any other such things that might come along) - -# I'm liking the third way. Possible gotchas: -# - redundant specification: 'packages' includes 'foo' and manifest -# includes 'foo/*.py' -# - obvious conflict: 'packages' includes 'foo' and manifest -# includes '! foo/*.py' (can't imagine why you'd want this) -# - subtle conflict: 'packages' includes 'foo' and manifest -# includes '! foo/bar.py' (this could well be desired: eg. exclude -# an experimental module from distribution) - -# Syntax for the manifest file: -# - if a line is just a Unix-style glob by itself, it's a "simple include -# pattern": go find all files that match and add them to the list -# of files -# - if a line is a glob preceded by "!", then it's a "simple exclude -# pattern": go over the current list of files and exclude any that -# match the glob pattern -# - if a line consists of a directory name followed by zero or more -# glob patterns, then we'll recursively explore that directory tree -# - the glob patterns can be include (no punctuation) or exclude -# (prefixed by "!", no space) -# - if no patterns given or the first pattern is not an include pattern, -# then assume "*" -- ie. find everything (and then start applying -# the rest of the patterns) -# - the patterns are given in order of increasing precedence, ie. -# the *last* one to match a given file applies to it -# -# example (ignoring auto-augmentation!): -# distutils/*.py -# distutils/command/*.py -# ! distutils/bleeding_edge.py -# examples/*.py -# examples/README -# -# smarter way (that *will* include distutils/command/bleeding_edge.py!) -# distutils *.py -# ! distutils/bleeding_edge.py -# examples !*~ !*.py[co] (same as: examples * !*~ !*.py[co]) -# test test_* *.txt !*~ !*.py[co] -# README -# setup.py -# -# The actual Distutils manifest (don't need to mention source files, -# README, setup.py -- they're automatically distributed!): -# examples !*~ !*.py[co] -# test !*~ !*.py[co] - -# The algorithm that will make it work: -# files = stuff from 'packages', 'py_modules', 'ext_modules', -# plus README, setup.py, ... ? -# foreach pattern in manifest file: -# if simple-include-pattern: # "distutils/*.py" -# files.append (glob (pattern)) -# elif simple-exclude-pattern: # "! distutils/foo*" -# xfiles = glob (pattern) -# remove all xfiles from files -# elif recursive-pattern: # "examples" (just a directory name) -# patterns = rest-of-words-on-line -# dir_files = list of all files under dir -# if patterns: -# if patterns[0] is an exclude-pattern: -# insert "*" at patterns[0] -# for file in dir_files: -# for dpattern in reverse (patterns): -# if file matches dpattern: -# if dpattern is an include-pattern: -# files.append (file) -# else: -# nothing, don't include it -# next file -# else: -# files.extend (dir_files) # ie. accept all of them - - -# Anyways, this is all implemented below -- BUT it is largely untested; I -# know it works for the simple case of distributing the Distutils, but -# haven't tried it on more complicated examples. Undoubtedly doing so will -# reveal bugs and cause delays, so I'm waiting until after I've released -# Distutils 0.1. - - -# Other things we need to look for in creating a source distribution: -# - make sure there's a README -# - make sure the distribution meta-info is supplied and non-empty -# (*must* have name, version, ((author and author_email) or -# (maintainer and maintainer_email)), url -# -# Frills: -# - make sure the setup script is called "setup.py" -# - make sure the README refers to "setup.py" (ie. has a line matching -# /^\s*python\s+setup\.py/) - -# A crazy idea that conflicts with having/requiring 'version' in setup.py: -# - make sure there's a version number in the "main file" (main file -# is __init__.py of first package, or the first module if no packages, -# or the first extension module if no pure Python modules) -# - XXX how do we look for __version__ in an extension module? -# - XXX do we import and look for __version__? or just scan source for -# /^__version__\s*=\s*"[^"]+"/ ? -# - what about 'version_from' as an alternative to 'version' -- then -# we know just where to search for the version -- no guessing about -# what the "main file" is - - - -class Dist (Command): - - description = "create a source distribution (tarball, zip file, etc.)" - - options = [('formats=', None, - "formats for source distribution (tar, ztar, gztar, or zip)"), - ('manifest=', 'm', - "name of manifest file"), - ('list-only', 'l', - "just list files that would be distributed"), - ('keep-tree', 'k', - "keep the distribution tree around after creating " + - "archive file(s)"), - ] - - default_format = { 'posix': 'gztar', - 'nt': 'zip' } - - exclude_re = re.compile (r'\s*!\s*(\S+)') # for manifest lines - - - def set_default_options (self): - self.formats = None - self.manifest = None - self.list_only = 0 - self.keep_tree = 0 - - - def set_final_options (self): - if self.formats is None: - try: - self.formats = [self.default_format[os.name]] - except KeyError: - raise DistutilsPlatformError, \ - "don't know how to build source distributions on " + \ - "%s platform" % os.name - elif type (self.formats) is StringType: - self.formats = string.split (self.formats, ',') - - if self.manifest is None: - self.manifest = "MANIFEST" - - - def run (self): - - self.check_metadata () - - self.files = [] - self.find_defaults () - self.read_manifest () - - if self.list_only: - for f in self.files: - print f - - else: - self.make_distribution () - - - def check_metadata (self): - - dist = self.distribution - - missing = [] - for attr in ('name', 'version', 'url'): - if not (hasattr (dist, attr) and getattr (dist, attr)): - missing.append (attr) - - if missing: - self.warn ("missing required meta-data: " + - string.join (missing, ", ")) - - if dist.author: - if not dist.author_email: - self.warn ("missing meta-data: if 'author' supplied, " + - "'author_email' must be supplied too") - elif dist.maintainer: - if not dist.maintainer_email: - self.warn ("missing meta-data: if 'maintainer' supplied, " + - "'maintainer_email' must be supplied too") - else: - self.warn ("missing meta-data: either (author and author_email) " + - "or (maintainer and maintainer_email) " + - "must be supplied") - - # check_metadata () - - - def find_defaults (self): - - standards = [('README', 'README.txt'), 'setup.py'] - for fn in standards: - if type (fn) is TupleType: - alts = fn - for fn in alts: - if os.path.exists (fn): - got_it = 1 - self.files.append (fn) - break - - if not got_it: - self.warn ("standard file not found: should have one of " + - string.join (alts, ', ')) - else: - if os.path.exists (fn): - self.files.append (fn) - else: - self.warn ("standard file %s not found" % fn) - - optional = ['test/test*.py'] - for pattern in optional: - files = filter (os.path.isfile, glob (pattern)) - if files: - self.files.extend (files) - - if self.distribution.packages or self.distribution.py_modules: - build_py = self.find_peer ('build_py') - build_py.ensure_ready () - self.files.extend (build_py.get_source_files ()) - - if self.distribution.ext_modules: - build_ext = self.find_peer ('build_ext') - build_ext.ensure_ready () - self.files.extend (build_ext.get_source_files ()) - - - - def open_manifest (self, filename): - return TextFile (filename, - strip_comments=1, - skip_blanks=1, - join_lines=1, - lstrip_ws=1, - rstrip_ws=1, - collapse_ws=1) - - - def search_dir (self, dir, patterns): - - allfiles = findall (dir) - if patterns: - if patterns[0][0] == "!": # starts with an exclude spec? - patterns.insert (0, "*")# then accept anything that isn't - # explicitly excluded - - act_patterns = [] # "action-patterns": (include,regexp) - # tuples where include is a boolean - for pattern in patterns: - if pattern[0] == '!': - act_patterns.append \ - ((0, re.compile (fnmatch.translate (pattern[1:])))) - else: - act_patterns.append \ - ((1, re.compile (fnmatch.translate (pattern)))) - act_patterns.reverse() - - - files = [] - for file in allfiles: - for (include,regexp) in act_patterns: - if regexp.search (file): - if include: - files.append (file) - break # continue to next file - else: - files = allfiles - - return files - - # search_dir () - - - def exclude_files (self, pattern): - - regexp = re.compile (fnmatch.translate (pattern)) - for i in range (len (self.files)-1, -1, -1): - if regexp.search (self.files[i]): - del self.files[i] - - - def read_manifest (self): - - # self.files had better already be defined (and hold the - # "automatically found" files -- Python modules and extensions, - # README, setup script, ...) - assert self.files is not None - - try: - manifest = self.open_manifest (self.manifest) - except IOError, exc: - if type (exc) is InstanceType and hasattr (exc, 'strerror'): - msg = "could not open MANIFEST (%s)" % \ - string.lower (exc.strerror) - else: - msg = "could not open MANIFST" - - self.warn (msg + ": using default file list") - return - - while 1: - - pattern = manifest.readline() - if pattern is None: # end of file - break - - # Cases: - # 1) simple-include: "*.py", "foo/*.py", "doc/*.html", "FAQ" - # 2) simple-exclude: same, prefaced by ! - # 3) recursive: multi-word line, first word a directory - - exclude = self.exclude_re.match (pattern) - if exclude: - pattern = exclude.group (1) - - words = string.split (pattern) - assert words # must have something! - if os.name != 'posix': - words[0] = apply (os.path.join, string.split (words[0], '/')) - - # First word is a directory, possibly with include/exclude - # patterns making up the rest of the line: it's a recursive - # pattern - if os.path.isdir (words[0]): - if exclude: - manifest.warn ("exclude (!) doesn't apply to " + - "whole directory trees") - continue - - dir_files = self.search_dir (words[0], words[1:]) - self.files.extend (dir_files) - - # Multiple words in pattern: that's a no-no unless the first - # word is a directory name - elif len (words) > 1: - manifest.warn ("can't have multiple words unless first word " + - "('%s') is a directory name" % words[0]) - continue - - # Single word, no bang: it's a "simple include pattern" - elif not exclude: - matches = filter (os.path.isfile, glob (pattern)) - if matches: - self.files.extend (matches) - else: - manifest.warn ("no matches for '%s' found" % pattern) - - - # Single word prefixed with a bang: it's a "simple exclude pattern" - else: - if self.exclude_files (pattern) == 0: - manifest.warn ("no files excluded by '%s'" % pattern) - - # if/elif/.../else on 'pattern' - - # loop over lines of 'manifest' - - # read_manifest () - - - def make_release_tree (self, base_dir, files): - - # XXX this is Unix-specific - - # First get the list of directories to create - need_dir = {} - for file in files: - need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1 - need_dirs = need_dir.keys() - need_dirs.sort() - - # Now create them - for dir in need_dirs: - self.mkpath (dir) - - # And walk over the list of files, either making a hard link (if - # os.link exists) to each one that doesn't already exist in its - # corresponding location under 'base_dir', or copying each file - # that's out-of-date in 'base_dir'. (Usually, all files will be - # out-of-date, because by default we blow away 'base_dir' when - # we're done making the distribution archives.) - - try: - link = os.link - msg = "making hard links in %s..." % base_dir - except AttributeError: - link = 0 - msg = "copying files to %s..." % base_dir - - self.announce (msg) - for file in files: - dest = os.path.join (base_dir, file) - if link: - if not os.path.exists (dest): - self.execute (os.link, (file, dest), - "linking %s -> %s" % (file, dest)) - else: - self.copy_file (file, dest) - - # make_release_tree () - - - def nuke_release_tree (self, base_dir): - try: - self.execute (rmtree, (base_dir,), - "removing %s" % base_dir) - except (IOError, OSError), exc: - if exc.filename: - msg = "error removing %s: %s (%s)" % \ - (base_dir, exc.strerror, exc.filename) - else: - msg = "error removing %s: %s" % (base_dir, exc.strerror) - self.warn (msg) - - - def make_tarball (self, base_dir, compress="gzip"): - - # XXX GNU tar 1.13 has a nifty option to add a prefix directory. - # It's pretty new, though, so we certainly can't require it -- - # but it would be nice to take advantage of it to skip the - # "create a tree of hardlinks" step! (Would also be nice to - # detect GNU tar to use its 'z' option and save a step.) - - if compress is not None and compress not in ('gzip', 'compress'): - raise ValueError, \ - "if given, 'compress' must be 'gzip' or 'compress'" - - archive_name = base_dir + ".tar" - self.spawn (["tar", "-cf", archive_name, base_dir]) - - if compress: - self.spawn ([compress, archive_name]) - - - def make_zipfile (self, base_dir): - - # This initially assumed the Unix 'zip' utility -- but - # apparently InfoZIP's zip.exe works the same under Windows, so - # no changes needed! - - try: - self.spawn (["zip", "-r", base_dir + ".zip", base_dir]) - except DistutilsExecError: - - # XXX really should distinguish between "couldn't find - # external 'zip' command" and "zip failed" -- shouldn't try - # again in the latter case. (I think fixing this will - # require some cooperation from the spawn module -- perhaps - # a utility function to search the path, so we can fallback - # on zipfile.py without the failed spawn.) - try: - import zipfile - except ImportError: - raise DistutilsExecError, \ - ("unable to create zip file '%s.zip': " + - "could neither find a standalone zip utility nor " + - "import the 'zipfile' module") % base_dir - - z = zipfile.ZipFile (base_dir + ".zip", "wb", - compression=zipfile.ZIP_DEFLATED) - - def visit (z, dirname, names): - for name in names: - path = os.path.join (dirname, name) - if os.path.isfile (path): - z.write (path, path) - - os.path.walk (base_dir, visit, z) - z.close() - - - def make_distribution (self): - - # Don't warn about missing meta-data here -- should be done - # elsewhere. - name = self.distribution.name or "UNKNOWN" - version = self.distribution.version - - if version: - base_dir = "%s-%s" % (name, version) - else: - base_dir = name - - # Remove any files that match "base_dir" from the fileset -- we - # don't want to go distributing the distribution inside itself! - self.exclude_files (base_dir + "*") - - self.make_release_tree (base_dir, self.files) - for fmt in self.formats: - if fmt == 'gztar': - self.make_tarball (base_dir, compress='gzip') - elif fmt == 'ztar': - self.make_tarball (base_dir, compress='compress') - elif fmt == 'tar': - self.make_tarball (base_dir, compress=None) - elif fmt == 'zip': - self.make_zipfile (base_dir) - - if not self.keep_tree: - self.nuke_release_tree (base_dir) - -# class Dist - - -# ---------------------------------------------------------------------- -# Utility functions - -def findall (dir = os.curdir): - """Find all files under 'dir' and return the sorted list of full - filenames (relative to 'dir').""" - - list = [] - stack = [dir] - pop = stack.pop - push = stack.append - - while stack: - dir = pop() - names = os.listdir (dir) - - for name in names: - fullname = os.path.join (dir, name) - list.append (fullname) - if os.path.isdir (fullname) and not os.path.islink(fullname): - push (fullname) - - list.sort() - return list -- cgit v1.2.1 From 5caa9f1cf7ce67b78a7db4f2a177ac5a7e5cb16b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 17 Feb 2000 23:56:15 +0000 Subject: The 'sdist' command to create a source distribution. This is derived from the old 'dist' command, but the code for dealing with manifests is completely redone -- and renaming the command to 'sdist' is more symmetric with the soon-to-exist 'bdist' command. --- command/sdist.py | 716 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 716 insertions(+) create mode 100644 command/sdist.py diff --git a/command/sdist.py b/command/sdist.py new file mode 100644 index 00000000..c8101a70 --- /dev/null +++ b/command/sdist.py @@ -0,0 +1,716 @@ +"""distutils.command.sdist + +Implements the Distutils 'sdist' command (create a source distribution).""" + +# created 1999/09/22, Greg Ward + +__rcsid__ = "$Id$" + +import sys, os, string, re +import fnmatch +from types import * +from glob import glob +from shutil import rmtree +from distutils.core import Command +from distutils.util import newer +from distutils.text_file import TextFile +from distutils.errors import DistutilsExecError + + +class Sdist (Command): + + description = "create a source distribution (tarball, zip file, etc.)" + + options = [('template=', 't', + "name of manifest template file [default: MANIFEST.in]"), + ('manifest=', 'm', + "name of manifest file [default: MANIFEST]"), + ('use-defaults', None, + "include the default file set in the manifest " + "[default; disable with --no-defaults]"), + ('manifest-only', None, + "just regenerate the manifest and then stop"), + ('force-manifest', None, + "forcibly regenerate the manifest and carry on as usual"), + + ('formats=', None, + "formats for source distribution (tar, ztar, gztar, or zip)"), + ('list-only', 'l', + "just list files that would be distributed"), + ('keep-tree', 'k', + "keep the distribution tree around after creating " + + "archive file(s)"), + ] + negative_opts = {'use-defaults': 'no-defaults'} + + default_format = { 'posix': 'gztar', + 'nt': 'zip' } + + exclude_re = re.compile (r'\s*!\s*(\S+)') # for manifest lines + + + def set_default_options (self): + # 'template' and 'manifest' are, respectively, the names of + # the manifest template and manifest file. + self.template = None + self.manifest = None + + # 'use_defaults': if true, we will include the default file set + # in the manifest + self.use_defaults = 1 + + self.manifest_only = 0 + self.force_manifest = 0 + + self.formats = None + self.list_only = 0 + self.keep_tree = 0 + + + def set_final_options (self): + if self.manifest is None: + self.manifest = "MANIFEST" + if self.template is None: + self.template = "MANIFEST.in" + + if self.formats is None: + try: + self.formats = [self.default_format[os.name]] + except KeyError: + raise DistutilsPlatformError, \ + "don't know how to build source distributions on " + \ + "%s platform" % os.name + elif type (self.formats) is StringType: + self.formats = string.split (self.formats, ',') + + + def run (self): + + # 'files' is the list of files that will make up the manifest + self.files = [] + + # Ensure that all required meta-data is given; warn if not (but + # don't die, it's not *that* serious!) + self.check_metadata () + + # Do whatever it takes to get the list of files to process + # (process the manifest template, read an existing manifest, + # whatever). File list is put into 'self.files'. + self.get_file_list () + + # If user just wanted us to regenerate the manifest, stop now. + if self.manifest_only: + return + + # Otherwise, go ahead and create the source distribution tarball, + # or zipfile, or whatever. + self.make_distribution () + + + def check_metadata (self): + + dist = self.distribution + + missing = [] + for attr in ('name', 'version', 'url'): + if not (hasattr (dist, attr) and getattr (dist, attr)): + missing.append (attr) + + if missing: + self.warn ("missing required meta-data: " + + string.join (missing, ", ")) + + if dist.author: + if not dist.author_email: + self.warn ("missing meta-data: if 'author' supplied, " + + "'author_email' must be supplied too") + elif dist.maintainer: + if not dist.maintainer_email: + self.warn ("missing meta-data: if 'maintainer' supplied, " + + "'maintainer_email' must be supplied too") + else: + self.warn ("missing meta-data: either (author and author_email) " + + "or (maintainer and maintainer_email) " + + "must be supplied") + + # check_metadata () + + + def get_file_list (self): + """Figure out the list of files to include in the source + distribution, and put it in 'self.files'. This might + involve reading the manifest template (and writing the + manifest), or just reading the manifest, or just using + the default file set -- it all depends on the user's + options and the state of the filesystem.""" + + + template_exists = os.path.isfile (self.template) + if template_exists: + template_newer = newer (self.template, self.manifest) + + # Regenerate the manifest if necessary (or if explicitly told to) + if ((template_exists and template_newer) or + self.force_manifest or + self.manifest_only): + + if not template_exists: + self.warn (("manifest template '%s' does not exist " + + "(using default file list)") % + self.template) + + # Add default file set to 'files' + if self.use_defaults: + self.find_defaults () + + # Read manifest template if it exists + if template_exists: + self.read_template () + + # File list now complete -- sort it so that higher-level files + # come first + sortable_files = map (os.path.split, self.files) + sortable_files.sort () + self.files = [] + for sort_tuple in sortable_files: + self.files.append (apply (os.path.join, sort_tuple)) + + # Remove duplicates from the file list + for i in range (len(self.files)-1, 0, -1): + if self.files[i] == self.files[i-1]: + del self.files[i] + + # And write complete file list (including default file set) to + # the manifest. + self.write_manifest () + + # Don't regenerate the manifest, just read it in. + else: + self.read_manifest () + + # get_file_list () + + + def find_defaults (self): + + standards = [('README', 'README.txt'), 'setup.py'] + for fn in standards: + if type (fn) is TupleType: + alts = fn + for fn in alts: + if os.path.exists (fn): + got_it = 1 + self.files.append (fn) + break + + if not got_it: + self.warn ("standard file not found: should have one of " + + string.join (alts, ', ')) + else: + if os.path.exists (fn): + self.files.append (fn) + else: + self.warn ("standard file '%s' not found" % fn) + + optional = ['test/test*.py'] + for pattern in optional: + files = filter (os.path.isfile, glob (pattern)) + if files: + self.files.extend (files) + + if self.distribution.packages or self.distribution.py_modules: + build_py = self.find_peer ('build_py') + build_py.ensure_ready () + self.files.extend (build_py.get_source_files ()) + + if self.distribution.ext_modules: + build_ext = self.find_peer ('build_ext') + build_ext.ensure_ready () + self.files.extend (build_ext.get_source_files ()) + + + + def search_dir (self, dir, pattern=None): + """Recursively find files under 'dir' matching 'pattern' (a string + containing a Unix-style glob pattern). If 'pattern' is None, + find all files under 'dir'. Return the list of found + filenames.""" + + allfiles = findall (dir) + if pattern is None: + return allfiles + + pattern_re = translate_pattern (pattern) + files = [] + for file in allfiles: + if pattern_re.match (os.path.basename (file)): + files.append (file) + + return files + + # search_dir () + + + def exclude_pattern (self, pattern): + """Remove filenames from 'self.files' that match 'pattern'.""" + print "exclude_pattern: pattern=%s" % pattern + pattern_re = translate_pattern (pattern) + for i in range (len (self.files)-1, -1, -1): + if pattern_re.match (self.files[i]): + print "removing %s" % self.files[i] + del self.files[i] + + + def recursive_exclude_pattern (self, dir, pattern=None): + """Remove filenames from 'self.files' that are under 'dir' + and whose basenames match 'pattern'.""" + + print "recursive_exclude_pattern: dir=%s, pattern=%s" % (dir, pattern) + if pattern is None: + pattern_re = None + else: + pattern_re = translate_pattern (pattern) + + for i in range (len (self.files)-1, -1, -1): + (cur_dir, cur_base) = os.path.split (self.files[i]) + if (cur_dir == dir and + (pattern_re is None or pattern_re.match (cur_base))): + print "removing %s" % self.files[i] + del self.files[i] + + + def read_template (self): + """Read and parse the manifest template file named by + 'self.template' (usually "MANIFEST.in"). Process all file + specifications (include and exclude) in the manifest template + and add the resulting filenames to 'self.files'.""" + + assert self.files is not None and type (self.files) is ListType + + template = TextFile (self.template, + strip_comments=1, + skip_blanks=1, + join_lines=1, + lstrip_ws=1, + rstrip_ws=1, + collapse_ws=1) + + all_files = findall () + + while 1: + + line = template.readline() + if line is None: # end of file + break + + words = string.split (line) + action = words[0] + + # First, check that the right number of words are present + # for the given action (which is the first word) + if action in ('include','exclude', + 'global-include','global-exclude'): + if len (words) != 2: + template.warn \ + ("invalid manifest template line: " + + "'%s' expects a single " % + action) + continue + + pattern = words[1] + + elif action in ('recursive-include','recursive-exclude'): + if len (words) != 3: + template.warn \ + ("invalid manifest template line: " + + "'%s' expects " % + action) + continue + + (dir, pattern) = words[1:3] + + elif action in ('graft','prune'): + if len (words) != 2: + template.warn \ + ("invalid manifest template line: " + + "'%s' expects a single " % + action) + continue + + dir_pattern = words[1] + + else: + template.warn ("invalid manifest template line: " + + "unknown action '%s'" % action) + continue + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. Also, we have + # defined either 'patter', 'dir' and 'pattern', or + # 'dir_pattern' -- so we don't have to spend any time digging + # stuff up out of 'words'. + + if action == 'include': + print "include", pattern + files = select_pattern (all_files, pattern, anchor=1) + if not files: + template.warn ("no files found matching '%s'" % pattern) + else: + self.files.extend (files) + + elif action == 'exclude': + print "exclude", pattern + num = exclude_pattern (self.files, pattern, anchor=1) + if num == 0: + template.warn \ + ("no previously-included files found matching '%s'" % + pattern) + + elif action == 'global-include': + print "global-include", pattern + files = select_pattern (all_files, pattern, anchor=0) + if not files: + template.warn (("no files found matching '%s' " + + "anywhere in distribution") % + pattern) + else: + self.files.extend (files) + + elif action == 'global-exclude': + print "global-exclude", pattern + num = exclude_pattern (self.files, pattern, anchor=0) + if num == 0: + template.warn \ + (("no previously-included files matching '%s' " + + "found anywhere in distribution") % + pattern) + + elif action == 'recursive-include': + print "recursive-include", dir, pattern + files = select_pattern (all_files, pattern, prefix=dir) + if not files: + template.warn (("no files found matching '%s' " + + "under directory '%s'") % + (pattern, dir)) + else: + self.files.extend (files) + + elif action == 'recursive-exclude': + print "recursive-exclude", dir, pattern + num = exclude_pattern (self.files, pattern, prefix=dir) + if num == 0: + template.warn \ + (("no previously-included files matching '%s' " + + "found under directory '%s'") % + (pattern, dir)) + + elif action == 'graft': + print "graft", dir_pattern + files = select_pattern (all_files, None, prefix=dir_pattern) + if not files: + template.warn ("no directories found matching '%s'" % + dir_pattern) + else: + self.files.extend (files) + + elif action == 'prune': + print "prune", dir_pattern + num = exclude_pattern (self.files, None, prefix=dir_pattern) + if num == 0: + template.warn \ + (("no previously-included directories found " + + "matching '%s'") % + dir_pattern) + else: + raise RuntimeError, \ + "this cannot happen: invalid action '%s'" % action + + # while loop over lines of template file + + # read_template () + + + def write_manifest (self): + """Write the file list in 'self.files' (presumably as filled in + by 'find_defaults()' and 'read_template()') to the manifest file + named by 'self.manifest'.""" + + manifest = open (self.manifest, "w") + for fn in self.files: + manifest.write (fn + '\n') + manifest.close () + + # write_manifest () + + + def read_manifest (self): + """Read the manifest file (named by 'self.manifest') and use + it to fill in 'self.files', the list of files to include + in the source distribution.""" + + manifest = open (self.manifest) + while 1: + line = manifest.readline () + if line == '': # end of file + break + if line[-1] == '\n': + line = line[0:-1] + self.files.append (line) + + # read_manifest () + + + + def make_release_tree (self, base_dir, files): + + # First get the list of directories to create + need_dir = {} + for file in files: + need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1 + need_dirs = need_dir.keys() + need_dirs.sort() + + # Now create them + for dir in need_dirs: + self.mkpath (dir) + + # And walk over the list of files, either making a hard link (if + # os.link exists) to each one that doesn't already exist in its + # corresponding location under 'base_dir', or copying each file + # that's out-of-date in 'base_dir'. (Usually, all files will be + # out-of-date, because by default we blow away 'base_dir' when + # we're done making the distribution archives.) + + try: + link = os.link + msg = "making hard links in %s..." % base_dir + except AttributeError: + link = 0 + msg = "copying files to %s..." % base_dir + + self.announce (msg) + for file in files: + dest = os.path.join (base_dir, file) + if link: + if not os.path.exists (dest): + self.execute (os.link, (file, dest), + "linking %s -> %s" % (file, dest)) + else: + self.copy_file (file, dest) + + # make_release_tree () + + + def nuke_release_tree (self, base_dir): + try: + self.execute (rmtree, (base_dir,), + "removing %s" % base_dir) + except (IOError, OSError), exc: + if exc.filename: + msg = "error removing %s: %s (%s)" % \ + (base_dir, exc.strerror, exc.filename) + else: + msg = "error removing %s: %s" % (base_dir, exc.strerror) + self.warn (msg) + + + def make_tarball (self, base_dir, compress="gzip"): + + # XXX GNU tar 1.13 has a nifty option to add a prefix directory. + # It's pretty new, though, so we certainly can't require it -- + # but it would be nice to take advantage of it to skip the + # "create a tree of hardlinks" step! (Would also be nice to + # detect GNU tar to use its 'z' option and save a step.) + + if compress is not None and compress not in ('gzip', 'compress'): + raise ValueError, \ + "if given, 'compress' must be 'gzip' or 'compress'" + + archive_name = base_dir + ".tar" + self.spawn (["tar", "-cf", archive_name, base_dir]) + + if compress: + self.spawn ([compress, archive_name]) + + + def make_zipfile (self, base_dir): + + # This initially assumed the Unix 'zip' utility -- but + # apparently InfoZIP's zip.exe works the same under Windows, so + # no changes needed! + + try: + self.spawn (["zip", "-r", base_dir + ".zip", base_dir]) + except DistutilsExecError: + + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed" -- shouldn't try + # again in the latter case. (I think fixing this will + # require some cooperation from the spawn module -- perhaps + # a utility function to search the path, so we can fallback + # on zipfile.py without the failed spawn.) + try: + import zipfile + except ImportError: + raise DistutilsExecError, \ + ("unable to create zip file '%s.zip': " + + "could neither find a standalone zip utility nor " + + "import the 'zipfile' module") % base_dir + + z = zipfile.ZipFile (base_dir + ".zip", "wb", + compression=zipfile.ZIP_DEFLATED) + + def visit (z, dirname, names): + for name in names: + path = os.path.join (dirname, name) + if os.path.isfile (path): + z.write (path, path) + + os.path.walk (base_dir, visit, z) + z.close() + + + def make_distribution (self): + + # Don't warn about missing meta-data here -- should be done + # elsewhere. + name = self.distribution.name or "UNKNOWN" + version = self.distribution.version + + if version: + base_dir = "%s-%s" % (name, version) + else: + base_dir = name + + # Remove any files that match "base_dir" from the fileset -- we + # don't want to go distributing the distribution inside itself! + self.exclude_pattern (base_dir + "*") + + self.make_release_tree (base_dir, self.files) + for fmt in self.formats: + if fmt == 'gztar': + self.make_tarball (base_dir, compress='gzip') + elif fmt == 'ztar': + self.make_tarball (base_dir, compress='compress') + elif fmt == 'tar': + self.make_tarball (base_dir, compress=None) + elif fmt == 'zip': + self.make_zipfile (base_dir) + + if not self.keep_tree: + self.nuke_release_tree (base_dir) + +# class Dist + + +# ---------------------------------------------------------------------- +# Utility functions + +def findall (dir = os.curdir): + """Find all files under 'dir' and return the list of full + filenames (relative to 'dir').""" + + list = [] + stack = [dir] + pop = stack.pop + push = stack.append + + while stack: + dir = pop() + names = os.listdir (dir) + + for name in names: + if dir != os.curdir: # avoid the dreaded "./" syndrome + fullname = os.path.join (dir, name) + else: + fullname = name + list.append (fullname) + if os.path.isdir (fullname) and not os.path.islink(fullname): + push (fullname) + + return list + + +def select_pattern (files, pattern, anchor=1, prefix=None): + """Select strings (presumably filenames) from 'files' that match + 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not + quite the same as implemented by the 'fnmatch' module: '*' and '?' + match non-special characters, where "special" is platform-dependent: + slash on Unix, colon, slash, and backslash on DOS/Windows, and colon + on Mac OS. + + If 'anchor' is true (the default), then the pattern match is more + stringent: "*.py" will match "foo.py" but not "foo/bar.py". If + 'anchor' is false, both of these will match. + + If 'prefix' is supplied, then only filenames starting with 'prefix' + (itself a pattern) and ending with 'pattern', with anything in + between them, will match. 'anchor' is ignored in this case. + + Return the list of matching strings, possibly empty.""" + + matches = [] + pattern_re = translate_pattern (pattern, anchor, prefix) + print "select_pattern: applying re %s" % pattern_re.pattern + for name in files: + if pattern_re.search (name): + matches.append (name) + print " adding", name + + return matches + +# select_pattern () + + +def exclude_pattern (files, pattern, anchor=1, prefix=None): + + pattern_re = translate_pattern (pattern, anchor, prefix) + print "exclude_pattern: applying re %s" % pattern_re.pattern + for i in range (len(files)-1, -1, -1): + if pattern_re.search (files[i]): + print " removing", files[i] + del files[i] + +# exclude_pattern () + + +def glob_to_re (pattern): + """Translate a shell-like glob pattern to a regular expression; + return a string containing the regex. Differs from + 'fnmatch.translate()' in that '*' does not match "special + characters" (which are platform-specific).""" + pattern_re = fnmatch.translate (pattern) + + # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which + # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, + # and by extension they shouldn't match such "special characters" under + # any OS. So change all non-escaped dots in the RE to match any + # character except the special characters. + # XXX currently the "special characters" are just slash -- i.e. this is + # Unix-only. + pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re) + return pattern_re + +# glob_to_re () + + +def translate_pattern (pattern, anchor=1, prefix=None): + """Translate a shell-like wildcard pattern to a compiled regular + expression. Return the compiled regex.""" + + if pattern: + pattern_re = glob_to_re (pattern) + else: + pattern_re = '' + + if prefix is not None: + prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $ + pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re) + else: # no prefix -- respect anchor flag + if anchor: + pattern_re = "^" + pattern_re + + return re.compile (pattern_re) + +# translate_pattern () -- cgit v1.2.1 From d03109b3f6d31ba77bdcee2559d786776a9430ce Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:11:52 +0000 Subject: Changed 'dist' to 'sdist'. --- command/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/__init__.py b/command/__init__.py index 8659307c..ea979f9b 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -21,5 +21,5 @@ __all__ = ['build', 'install', 'install_py', 'install_ext', - 'dist', + 'sdist', ] -- cgit v1.2.1 From 0fe8bb260644b6dfaf64c07189108d2a045a1895 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:13:53 +0000 Subject: Renamed all command classes so they're exactly the same as the name of the command itself: no more of this "FooBar class for foo_bar command" silliness. --- command/build.py | 2 +- command/build_clib.py | 2 +- command/build_ext.py | 2 +- command/build_lib.py | 2 +- command/build_py.py | 2 +- command/install.py | 2 +- command/install_ext.py | 2 +- command/install_lib.py | 2 +- command/install_py.py | 2 +- command/sdist.py | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/command/build.py b/command/build.py index 82a4e6c2..bcbb9332 100644 --- a/command/build.py +++ b/command/build.py @@ -10,7 +10,7 @@ import os from distutils.core import Command -class Build (Command): +class build (Command): description = "build everything needed to install" diff --git a/command/build_clib.py b/command/build_clib.py index 65712818..749b0c2b 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -26,7 +26,7 @@ from distutils.errors import * from distutils.ccompiler import new_compiler -class BuildLib (Command): +class build_lib (Command): options = [('debug', 'g', "compile with debugging information"), diff --git a/command/build_ext.py b/command/build_ext.py index 42eab479..ea0e294a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -20,7 +20,7 @@ extension_name_re = re.compile \ (r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$') -class BuildExt (Command): +class build_ext (Command): description = "build C/C++ extensions (compile/link to build directory)" diff --git a/command/build_lib.py b/command/build_lib.py index 65712818..749b0c2b 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -26,7 +26,7 @@ from distutils.errors import * from distutils.ccompiler import new_compiler -class BuildLib (Command): +class build_lib (Command): options = [('debug', 'g', "compile with debugging information"), diff --git a/command/build_py.py b/command/build_py.py index 048962b2..f492ee37 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -14,7 +14,7 @@ from distutils.core import Command from distutils.errors import * -class BuildPy (Command): +class build_py (Command): description = "\"build\" pure Python modules (copy to build directory)" diff --git a/command/install.py b/command/install.py index 0e5b01cc..b57ec6f2 100644 --- a/command/install.py +++ b/command/install.py @@ -12,7 +12,7 @@ from distutils.core import Command from distutils.util import write_file -class Install (Command): +class install (Command): description = "install everything from build directory" diff --git a/command/install_ext.py b/command/install_ext.py index 599a37e1..ba343d2e 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -9,7 +9,7 @@ __rcsid__ = "$Id$" from distutils.core import Command from distutils.util import copy_tree -class InstallExt (Command): +class install_ext (Command): description = "install C/C++ extension modules" diff --git a/command/install_lib.py b/command/install_lib.py index 2e8a6706..e898ce0c 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,7 +6,7 @@ import sys, string from distutils.core import Command from distutils.util import copy_tree -class InstallPy (Command): +class install_py (Command): description = "install pure Python modules" diff --git a/command/install_py.py b/command/install_py.py index 2e8a6706..e898ce0c 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -6,7 +6,7 @@ import sys, string from distutils.core import Command from distutils.util import copy_tree -class InstallPy (Command): +class install_py (Command): description = "install pure Python modules" diff --git a/command/sdist.py b/command/sdist.py index c8101a70..d8908328 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -17,7 +17,7 @@ from distutils.text_file import TextFile from distutils.errors import DistutilsExecError -class Sdist (Command): +class sdist (Command): description = "create a source distribution (tarball, zip file, etc.)" -- cgit v1.2.1 From d393e804e26ad082c98d3070238a37e416e9e0af Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:14:21 +0000 Subject: Command classes are now named identically to their commands, so reflect this in 'find_command_class()' method. --- core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core.py b/core.py index 88e889b2..f3951ac7 100644 --- a/core.py +++ b/core.py @@ -447,8 +447,7 @@ class Distribution: expected class was not found in it.""" module_name = 'distutils.command.' + command - klass_name = string.join \ - (map (string.capitalize, string.split (command, '_')), '') + klass_name = command try: __import__ (module_name) -- cgit v1.2.1 From 76c84dcb7f05dd257f26c74e2949e9020e1f633d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:25:39 +0000 Subject: Renamed all 'options' class attributes to 'user_options'. --- command/build.py | 19 ++++++------- command/build_clib.py | 7 ++--- command/build_ext.py | 39 ++++++++++++++------------- command/build_lib.py | 7 ++--- command/build_py.py | 5 ++-- command/install.py | 73 +++++++++++++++++++++++++------------------------- command/install_ext.py | 7 ++--- command/install_lib.py | 11 ++++---- command/install_py.py | 11 ++++---- command/sdist.py | 41 ++++++++++++++-------------- 10 files changed, 115 insertions(+), 105 deletions(-) diff --git a/command/build.py b/command/build.py index bcbb9332..d46132f3 100644 --- a/command/build.py +++ b/command/build.py @@ -14,15 +14,16 @@ class build (Command): description = "build everything needed to install" - options = [('build-base=', 'b', - "base directory for build library"), - ('build-lib=', 'l', - "directory for platform-shared files"), - ('build-platlib=', 'p', - "directory for platform-specific files"), - ('debug', 'g', - "compile extensions and libraries with debugging information"), - ] + user_options = [ + ('build-base=', 'b', + "base directory for build library"), + ('build-lib=', 'l', + "directory for platform-shared files"), + ('build-platlib=', 'p', + "directory for platform-specific files"), + ('debug', 'g', + "compile extensions and libraries with debugging information"), + ] def set_default_options (self): self.build_base = 'build' diff --git a/command/build_clib.py b/command/build_clib.py index 749b0c2b..26b89b3a 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -28,9 +28,10 @@ from distutils.ccompiler import new_compiler class build_lib (Command): - options = [('debug', 'g', - "compile with debugging information"), - ] + user_options = [ + ('debug', 'g', + "compile with debugging information"), + ] def set_default_options (self): # List of libraries to build diff --git a/command/build_ext.py b/command/build_ext.py index ea0e294a..963abc29 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -42,25 +42,26 @@ class build_ext (Command): # takes care of both command-line and client options # in between set_default_options() and set_final_options()) - options = [('build-dir=', 'd', - "directory for compiled extension modules"), - ('include-dirs=', 'I', - "list of directories to search for header files"), - ('define=', 'D', - "C preprocessor macros to define"), - ('undef=', 'U', - "C preprocessor macros to undefine"), - ('libs=', 'l', - "external C libraries to link with"), - ('library-dirs=', 'L', - "directories to search for external C libraries"), - ('rpath=', 'R', - "directories to search for shared C libraries at runtime"), - ('link-objects=', 'O', - "extra explicit link objects to include in the link"), - ('debug', 'g', - "compile/link with debugging information"), - ] + user_options = [ + ('build-dir=', 'd', + "directory for compiled extension modules"), + ('include-dirs=', 'I', + "list of directories to search for header files"), + ('define=', 'D', + "C preprocessor macros to define"), + ('undef=', 'U', + "C preprocessor macros to undefine"), + ('libs=', 'l', + "external C libraries to link with"), + ('library-dirs=', 'L', + "directories to search for external C libraries"), + ('rpath=', 'R', + "directories to search for shared C libraries at runtime"), + ('link-objects=', 'O', + "extra explicit link objects to include in the link"), + ('debug', 'g', + "compile/link with debugging information"), + ] def set_default_options (self): diff --git a/command/build_lib.py b/command/build_lib.py index 749b0c2b..26b89b3a 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -28,9 +28,10 @@ from distutils.ccompiler import new_compiler class build_lib (Command): - options = [('debug', 'g', - "compile with debugging information"), - ] + user_options = [ + ('debug', 'g', + "compile with debugging information"), + ] def set_default_options (self): # List of libraries to build diff --git a/command/build_py.py b/command/build_py.py index f492ee37..eecf88b9 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -18,8 +18,9 @@ class build_py (Command): description = "\"build\" pure Python modules (copy to build directory)" - options = [('build-dir=', 'd', "directory for platform-shared files"), - ] + user_options = [ + ('build-dir=', 'd', "directory for platform-shared files"), + ] def set_default_options (self): diff --git a/command/install.py b/command/install.py index b57ec6f2..798460b2 100644 --- a/command/install.py +++ b/command/install.py @@ -16,42 +16,43 @@ class install (Command): description = "install everything from build directory" - options = [('prefix=', None, "installation prefix"), - ('exec-prefix=', None, - "prefix for platform-specific files"), - - # Build directories: where to install from - ('build-base=', None, - "base build directory"), - ('build-lib=', None, - "build directory for pure Python modules"), - ('build-platlib=', None, - "build directory for extension modules"), - - # Installation directories: where to put modules and packages - ('install-lib=', None, - "base Python library directory"), - ('install-platlib=', None, - "platform-specific Python library directory"), - ('install-site-lib=', None, - "directory for site-specific packages and modules"), - ('install-site-platlib=', None, - "platform-specific site directory"), - ('install-scheme=', None, - "install to 'system' or 'site' library directory?"), - ('install-path=', None, - "extra intervening directories to put below install-lib"), - - # Where to install documentation (eventually!) - ('doc-format=', None, "format of documentation to generate"), - ('install-man=', None, "directory for Unix man pages"), - ('install-html=', None, "directory for HTML documentation"), - ('install-info=', None, "directory for GNU info files"), - - # Flags for 'build_py' - ('compile-py', None, "compile .py to .pyc"), - ('optimize-py', None, "compile .py to .pyo (optimized)"), - ] + user_options = [ + ('prefix=', None, "installation prefix"), + ('exec-prefix=', None, + "prefix for platform-specific files"), + + # Build directories: where to install from + ('build-base=', None, + "base build directory"), + ('build-lib=', None, + "build directory for pure Python modules"), + ('build-platlib=', None, + "build directory for extension modules"), + + # Installation directories: where to put modules and packages + ('install-lib=', None, + "base Python library directory"), + ('install-platlib=', None, + "platform-specific Python library directory"), + ('install-site-lib=', None, + "directory for site-specific packages and modules"), + ('install-site-platlib=', None, + "platform-specific site directory"), + ('install-scheme=', None, + "install to 'system' or 'site' library directory?"), + ('install-path=', None, + "extra intervening directories to put below install-lib"), + + # Where to install documentation (eventually!) + ('doc-format=', None, "format of documentation to generate"), + ('install-man=', None, "directory for Unix man pages"), + ('install-html=', None, "directory for HTML documentation"), + ('install-info=', None, "directory for GNU info files"), + + # Flags for 'build_py' + ('compile-py', None, "compile .py to .pyc"), + ('optimize-py', None, "compile .py to .pyo (optimized)"), + ] def set_default_options (self): diff --git a/command/install_ext.py b/command/install_ext.py index ba343d2e..016a514e 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -13,9 +13,10 @@ class install_ext (Command): description = "install C/C++ extension modules" - options = [('install-dir=', 'd', "directory to install to"), - ('build-dir=','b', "build directory (where to install from)"), - ] + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ('build-dir=','b', "build directory (where to install from)"), + ] def set_default_options (self): # let the 'install' command dictate our installation directory diff --git a/command/install_lib.py b/command/install_lib.py index e898ce0c..6dfebfbe 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -10,11 +10,12 @@ class install_py (Command): description = "install pure Python modules" - options = [('install-dir=', 'd', "directory to install to"), - ('build-dir=','b', "build directory (where to install from)"), - ('compile', 'c', "compile .py to .pyc"), - ('optimize', 'o', "compile .py to .pyo (optimized)"), - ] + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ('build-dir=','b', "build directory (where to install from)"), + ('compile', 'c', "compile .py to .pyc"), + ('optimize', 'o', "compile .py to .pyo (optimized)"), + ] def set_default_options (self): diff --git a/command/install_py.py b/command/install_py.py index e898ce0c..6dfebfbe 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -10,11 +10,12 @@ class install_py (Command): description = "install pure Python modules" - options = [('install-dir=', 'd', "directory to install to"), - ('build-dir=','b', "build directory (where to install from)"), - ('compile', 'c', "compile .py to .pyc"), - ('optimize', 'o', "compile .py to .pyo (optimized)"), - ] + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ('build-dir=','b', "build directory (where to install from)"), + ('compile', 'c', "compile .py to .pyc"), + ('optimize', 'o', "compile .py to .pyo (optimized)"), + ] def set_default_options (self): diff --git a/command/sdist.py b/command/sdist.py index d8908328..aaedb304 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -21,26 +21,27 @@ class sdist (Command): description = "create a source distribution (tarball, zip file, etc.)" - options = [('template=', 't', - "name of manifest template file [default: MANIFEST.in]"), - ('manifest=', 'm', - "name of manifest file [default: MANIFEST]"), - ('use-defaults', None, - "include the default file set in the manifest " - "[default; disable with --no-defaults]"), - ('manifest-only', None, - "just regenerate the manifest and then stop"), - ('force-manifest', None, - "forcibly regenerate the manifest and carry on as usual"), - - ('formats=', None, - "formats for source distribution (tar, ztar, gztar, or zip)"), - ('list-only', 'l', - "just list files that would be distributed"), - ('keep-tree', 'k', - "keep the distribution tree around after creating " + - "archive file(s)"), - ] + user_options = [ + ('template=', 't', + "name of manifest template file [default: MANIFEST.in]"), + ('manifest=', 'm', + "name of manifest file [default: MANIFEST]"), + ('use-defaults', None, + "include the default file set in the manifest " + "[default; disable with --no-defaults]"), + ('manifest-only', None, + "just regenerate the manifest and then stop"), + ('force-manifest', None, + "forcibly regenerate the manifest and carry on as usual"), + + ('formats=', None, + "formats for source distribution (tar, ztar, gztar, or zip)"), + ('list-only', 'l', + "just list files that would be distributed"), + ('keep-tree', 'k', + "keep the distribution tree around after creating " + + "archive file(s)"), + ] negative_opts = {'use-defaults': 'no-defaults'} default_format = { 'posix': 'gztar', -- cgit v1.2.1 From 42a137f33452bf81103e2960c6c33836b8260dda Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:26:23 +0000 Subject: Changed references to the command class 'options' attribute to 'user_options'. Related docstring changes. Unrelated comment changes. --- core.py | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/core.py b/core.py index f3951ac7..6463bb49 100644 --- a/core.py +++ b/core.py @@ -24,9 +24,10 @@ from distutils import util # to look for a Python module named after the command. command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') -# Defining this as a global is probably inadequate -- what about -# listing the available options (or even commands, which can vary -# quite late as well) +# This is a barebones help message generated displayed when the user +# runs the setup script with no arguments at all. More useful help +# is generated with various --help options: global help, list commands, +# and per-command help. usage = """\ usage: %s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: %s --help @@ -50,22 +51,22 @@ def setup (**attrs): Distribution instance. The 'cmdclass' argument, if supplied, is a dictionary mapping - command names to command classes. Each command encountered on the - command line will be turned into a command class, which is in turn - instantiated; any class found in 'cmdclass' is used in place of the - default, which is (for command 'foo_bar') class 'FooBar' in module - 'distutils.command.foo_bar'. The command object must provide an - 'options' attribute which is a list of option specifiers for - 'distutils.fancy_getopt'. Any command-line options between the - current and the next command are used to set attributes in the - current command object. - - When the entire command-line has been successfully parsed, calls the - 'run' method on each command object in turn. This method will be - driven entirely by the Distribution object (which each command - object has a reference to, thanks to its constructor), and the - command-specific options that became attributes of each command - object.""" + command names to command classes. Each command encountered on + the command line will be turned into a command class, which is in + turn instantiated; any class found in 'cmdclass' is used in place + of the default, which is (for command 'foo_bar') class 'foo_bar' + in module 'distutils.command.foo_bar'. The command class must + provide a 'user_options' attribute which is a list of option + specifiers for 'distutils.fancy_getopt'. Any command-line + options between the current and the next command are used to set + attributes of the current command object. + + When the entire command-line has been successfully parsed, calls + the 'run()' method on each command object in turn. This method + will be driven entirely by the Distribution object (which each + command object has a reference to, thanks to its constructor), + and the command-specific options that became attributes of each + command object.""" # Determine the distribution class -- either caller-supplied or # our Distribution (see below). @@ -313,11 +314,11 @@ class Distribution: # Also make sure that the command object provides a list of its # known options - if not (hasattr (cmd_obj, 'options') and - type (cmd_obj.options) is ListType): + if not (hasattr (cmd_obj, 'user_options') and + type (cmd_obj.user_options) is ListType): raise DistutilsClassError, \ - ("command class %s must provide an 'options' attribute "+ - "(a list of tuples)") % \ + ("command class %s must provide " + + "'user_options' attribute (a list of tuples)") % \ cmd_obj.__class__ # Poof! like magic, all commands support the global @@ -327,14 +328,14 @@ class Distribution: negative_opt = copy (negative_opt) negative_opt.update (cmd_obj.negative_opt) - options = self.global_options + cmd_obj.options + options = self.global_options + cmd_obj.user_options args = fancy_getopt (options, negative_opt, cmd_obj, args[1:]) if cmd_obj.help: print_help (self.global_options, header="Global options:") print - print_help (cmd_obj.options, + print_help (cmd_obj.user_options, header="Options for '%s' command:" % command) print print usage @@ -357,7 +358,7 @@ class Distribution: for command in self.commands: klass = self.find_command_class (command) - print_help (klass.options, + print_help (klass.user_options, header="Options for '%s' command:" % command) print -- cgit v1.2.1 From 7697546dd8208145f70f4d5dd97d6fc268842aaa Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:35:22 +0000 Subject: Renamed 'set_default_options()' to 'initialize_options()', and 'set_final_options()' to 'finalize_options()'. --- command/build.py | 4 ++-- command/build_clib.py | 8 ++++---- command/build_ext.py | 10 +++++----- command/build_lib.py | 8 ++++---- command/build_py.py | 4 ++-- command/install.py | 6 +++--- command/install_ext.py | 4 ++-- command/install_lib.py | 4 ++-- command/install_py.py | 4 ++-- command/sdist.py | 4 ++-- 10 files changed, 28 insertions(+), 28 deletions(-) diff --git a/command/build.py b/command/build.py index d46132f3..28241e10 100644 --- a/command/build.py +++ b/command/build.py @@ -25,7 +25,7 @@ class build (Command): "compile extensions and libraries with debugging information"), ] - def set_default_options (self): + def initialize_options (self): self.build_base = 'build' # these are decided only after 'build_base' has its final value # (unless overridden by the user or client) @@ -33,7 +33,7 @@ class build (Command): self.build_platlib = None self.debug = None - def set_final_options (self): + def finalize_options (self): # 'build_lib' and 'build_platlib' just default to 'lib' and # 'platlib' under the base build directory if self.build_lib is None: diff --git a/command/build_clib.py b/command/build_clib.py index 26b89b3a..9cf53da0 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -33,7 +33,7 @@ class build_lib (Command): "compile with debugging information"), ] - def set_default_options (self): + def initialize_options (self): # List of libraries to build self.libraries = None @@ -43,9 +43,9 @@ class build_lib (Command): self.undef = None self.debug = None - # set_default_options() + # initialize_options() - def set_final_options (self): + def finalize_options (self): self.set_undefined_options ('build', ('debug', 'debug')) self.libraries = self.distribution.libraries @@ -58,7 +58,7 @@ class build_lib (Command): # XXX same as for build_ext -- what about 'self.define' and # 'self.undef' ? - # set_final_options() + # finalize_options() def run (self): diff --git a/command/build_ext.py b/command/build_ext.py index 963abc29..8ed3a973 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -38,9 +38,9 @@ class build_ext (Command): # structure # - that data structure (in this case, a list of 2-tuples) # will then be present in the command object by the time - # we get to set_final_options() (i.e. the constructor + # we get to finalize_options() (i.e. the constructor # takes care of both command-line and client options - # in between set_default_options() and set_final_options()) + # in between initialize_options() and finalize_options()) user_options = [ ('build-dir=', 'd', @@ -64,7 +64,7 @@ class build_ext (Command): ] - def set_default_options (self): + def initialize_options (self): self.extensions = None self.build_dir = None self.package = None @@ -79,7 +79,7 @@ class build_ext (Command): self.debug = None - def set_final_options (self): + def finalize_options (self): from distutils import sysconfig self.set_undefined_options ('build', @@ -112,7 +112,7 @@ class build_ext (Command): # XXX how the heck are 'self.define' and 'self.undef' supposed to # be set? - # set_final_options () + # finalize_options () def run (self): diff --git a/command/build_lib.py b/command/build_lib.py index 26b89b3a..9cf53da0 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -33,7 +33,7 @@ class build_lib (Command): "compile with debugging information"), ] - def set_default_options (self): + def initialize_options (self): # List of libraries to build self.libraries = None @@ -43,9 +43,9 @@ class build_lib (Command): self.undef = None self.debug = None - # set_default_options() + # initialize_options() - def set_final_options (self): + def finalize_options (self): self.set_undefined_options ('build', ('debug', 'debug')) self.libraries = self.distribution.libraries @@ -58,7 +58,7 @@ class build_lib (Command): # XXX same as for build_ext -- what about 'self.define' and # 'self.undef' ? - # set_final_options() + # finalize_options() def run (self): diff --git a/command/build_py.py b/command/build_py.py index eecf88b9..7ae207b8 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -23,13 +23,13 @@ class build_py (Command): ] - def set_default_options (self): + def initialize_options (self): self.build_dir = None self.modules = None self.package = None self.package_dir = None - def set_final_options (self): + def finalize_options (self): self.set_undefined_options ('build', ('build_lib', 'build_dir')) diff --git a/command/install.py b/command/install.py index 798460b2..ffd68797 100644 --- a/command/install.py +++ b/command/install.py @@ -55,7 +55,7 @@ class install (Command): ] - def set_default_options (self): + def initialize_options (self): self.build_base = None self.build_lib = None @@ -89,7 +89,7 @@ class install (Command): self.optimize_py = 1 - def set_final_options (self): + def finalize_options (self): # XXX this method is where the default installation directories # for modules and extension modules are determined. (Someday, @@ -237,7 +237,7 @@ class install (Command): # Punt on doc directories for now -- after all, we're punting on # documentation completely! - # set_final_options () + # finalize_options () def replace_sys_prefix (self, config_attr, fallback_postfix, use_exec=0): diff --git a/command/install_ext.py b/command/install_ext.py index 016a514e..b046dfbf 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -18,12 +18,12 @@ class install_ext (Command): ('build-dir=','b', "build directory (where to install from)"), ] - def set_default_options (self): + def initialize_options (self): # let the 'install' command dictate our installation directory self.install_dir = None self.build_dir = None - def set_final_options (self): + def finalize_options (self): self.set_undefined_options ('install', ('build_platlib', 'build_dir'), ('install_site_platlib', 'install_dir')) diff --git a/command/install_lib.py b/command/install_lib.py index 6dfebfbe..919873c6 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -18,14 +18,14 @@ class install_py (Command): ] - def set_default_options (self): + def initialize_options (self): # let the 'install' command dictate our installation directory self.install_dir = None self.build_dir = None self.compile = 1 self.optimize = 1 - def set_final_options (self): + def finalize_options (self): # Find out from the 'build_ext' command if we were asked to build # any extensions. If so, that means even pure-Python modules in diff --git a/command/install_py.py b/command/install_py.py index 6dfebfbe..919873c6 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -18,14 +18,14 @@ class install_py (Command): ] - def set_default_options (self): + def initialize_options (self): # let the 'install' command dictate our installation directory self.install_dir = None self.build_dir = None self.compile = 1 self.optimize = 1 - def set_final_options (self): + def finalize_options (self): # Find out from the 'build_ext' command if we were asked to build # any extensions. If so, that means even pure-Python modules in diff --git a/command/sdist.py b/command/sdist.py index aaedb304..042b1fc5 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -50,7 +50,7 @@ class sdist (Command): exclude_re = re.compile (r'\s*!\s*(\S+)') # for manifest lines - def set_default_options (self): + def initialize_options (self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. self.template = None @@ -68,7 +68,7 @@ class sdist (Command): self.keep_tree = 0 - def set_final_options (self): + def finalize_options (self): if self.manifest is None: self.manifest = "MANIFEST" if self.template is None: -- cgit v1.2.1 From 18f73d2efe63baed2730c5d607901c429f7460ed Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:36:20 +0000 Subject: Changed all references to command methods 'set_default_options()' and 'set_final_options()' to 'initialize_options()' and 'finalize_options()'. --- core.py | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/core.py b/core.py index 6463bb49..ddd39a2c 100644 --- a/core.py +++ b/core.py @@ -619,14 +619,14 @@ class Command: """Abstract base class for defining command classes, the "worker bees" of the Distutils. A useful analogy for command classes is to think of them as subroutines with local variables called - "options". The options are "declared" in 'set_default_options()' - and "initialized" (given their real values) in - 'set_final_options()', both of which must be defined by every + "options". The options are "declared" in 'initialize_options()' + and "defined" (given their final values, aka "finalized") in + 'finalize_options()', both of which must be defined by every command class. The distinction between the two is necessary because option values might come from the outside world (command line, option file, ...), and any options dependent on other options must be computed *after* these outside influences have - been processed -- hence 'set_final_options()'. The "body" of the + been processed -- hence 'finalize_options()'. The "body" of the subroutine, where it does all its work based on the values of its options, is the 'run()' method, which must also be implemented by every command class.""" @@ -635,7 +635,7 @@ class Command: def __init__ (self, dist): """Create and initialize a new Command object. Most importantly, - invokes the 'set_default_options()' method, which is the + invokes the 'initialize_options()' method, which is the real initializer and depends on the actual command being instantiated.""" @@ -645,7 +645,7 @@ class Command: raise RuntimeError, "Command is an abstract class" self.distribution = dist - self.set_default_options () + self.initialize_options () # Per-command versions of the global flags, so that the user can # customize Distutils' behaviour command-by-command and let some @@ -662,10 +662,10 @@ class Command: # none of that complicated bureaucracy is needed. self.help = 0 - # 'ready' records whether or not 'set_final_options()' has been - # called. 'set_final_options()' itself should not pay attention to + # 'ready' records whether or not 'finalize_options()' has been + # called. 'finalize_options()' itself should not pay attention to # this flag: it is the business of 'ensure_ready()', which always - # calls 'set_final_options()', to respect/update it. + # calls 'finalize_options()', to respect/update it. self.ready = 0 # end __init__ () @@ -684,16 +684,16 @@ class Command: def ensure_ready (self): if not self.ready: - self.set_final_options () + self.finalize_options () self.ready = 1 # Subclasses must define: - # set_default_options() + # initialize_options() # provide default values for all options; may be overridden # by Distutils client, by command-line options, or by options # from option file - # set_final_options() + # finalize_options() # decide on the final values for all options; this is called # after all possible intervention from the outside world # (command-line, option file, etc.) has been processed @@ -701,12 +701,12 @@ class Command: # run the command: do whatever it is we're here to do, # controlled by the command's various option values - def set_default_options (self): + def initialize_options (self): """Set default values for all the options that this command supports. Note that these defaults may be overridden by the command-line supplied by the user; thus, this is not the place to code dependencies between options; generally, - 'set_default_options()' implementations are just a bunch + 'initialize_options()' implementations are just a bunch of "self.foo = None" assignments. This method must be implemented by all command classes.""" @@ -714,14 +714,14 @@ class Command: raise RuntimeError, \ "abstract method -- subclass %s must override" % self.__class__ - def set_final_options (self): + def finalize_options (self): """Set final values for all the options that this command supports. This is always called as late as possible, ie. after any option assignments from the command-line or from other commands have been done. Thus, this is the place to to code option dependencies: if 'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as long as 'foo' still has - the same value it was assigned in 'set_default_options()'. + the same value it was assigned in 'initialize_options()'. This method must be implemented by all command classes.""" @@ -731,8 +731,8 @@ class Command: def run (self): """A command's raison d'etre: carry out the action it exists to perform, controlled by the options initialized in - 'set_initial_options()', customized by the user and other - commands, and finalized in 'set_final_options()'. All + 'initialize_options()', customized by the user and other + commands, and finalized in 'finalize_options()'. All terminal output and filesystem interaction should be done by 'run()'. @@ -836,7 +836,7 @@ class Command: # Option_pairs: list of (src_option, dst_option) tuples src_cmd_obj = self.distribution.find_command_obj (src_cmd) - src_cmd_obj.set_final_options () + src_cmd_obj.finalize_options () try: for (src_option, dst_option) in option_pairs: if getattr (self, dst_option) is None: @@ -851,16 +851,16 @@ class Command: """Attempt to simulate a command-line override of some option value in another command. Finds the command object for 'command', sets its 'option' to 'value', and unconditionally - calls 'set_final_options()' on it: this means that some command - objects may have 'set_final_options()' invoked more than once. + calls 'finalize_options()' on it: this means that some command + objects may have 'finalize_options()' invoked more than once. Even so, this is not entirely reliable: the other command may already be initialized to its satisfaction, in which case the - second 'set_final_options()' invocation will have little or no + second 'finalize_options()' invocation will have little or no effect.""" cmd_obj = self.distribution.find_command_obj (command) cmd_obj.set_option (option, value) - cmd_obj.set_final_options () + cmd_obj.finalize_options () def find_peer (self, command, create=1): -- cgit v1.2.1 From d7fe6215f8ae6b39c44a475925c6ae0aa005157a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 24 Feb 2000 03:17:43 +0000 Subject: Fix from est@hyperreal.org: missing initialize in 'find_defaults()'. --- command/sdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/sdist.py b/command/sdist.py index 042b1fc5..6b838bde 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -198,6 +198,7 @@ class sdist (Command): for fn in standards: if type (fn) is TupleType: alts = fn + got_it = 0 for fn in alts: if os.path.exists (fn): got_it = 1 -- cgit v1.2.1 From 0e7f7e1c36820e01bde9edbbcd9acc623445b045 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 26 Feb 2000 00:49:04 +0000 Subject: Unfinished, untested implementation of the lovely baroque installation scheme cooked up by Fred Drake and me. Only saved for posterity (whoever posterity is), as it is about to be ditched in favour of GvR's much simpler design. --- command/install.py | 259 ++++++++++++++++++++----------------------------- command/install_ext.py | 2 +- command/install_lib.py | 20 ++-- command/install_py.py | 20 ++-- 4 files changed, 118 insertions(+), 183 deletions(-) diff --git a/command/install.py b/command/install.py index ffd68797..a89bc025 100644 --- a/command/install.py +++ b/command/install.py @@ -10,7 +10,7 @@ import sys, os, string from types import * from distutils.core import Command from distutils.util import write_file - +from distutils.errors import DistutilsOptionError class install (Command): @@ -21,66 +21,52 @@ class install (Command): ('exec-prefix=', None, "prefix for platform-specific files"), - # Build directories: where to install from - ('build-base=', None, - "base build directory"), - ('build-lib=', None, - "build directory for pure Python modules"), - ('build-platlib=', None, - "build directory for extension modules"), - # Installation directories: where to put modules and packages ('install-lib=', None, "base Python library directory"), ('install-platlib=', None, "platform-specific Python library directory"), - ('install-site-lib=', None, - "directory for site-specific packages and modules"), - ('install-site-platlib=', None, - "platform-specific site directory"), - ('install-scheme=', None, - "install to 'system' or 'site' library directory?"), ('install-path=', None, "extra intervening directories to put below install-lib"), + # Build directories: where to find the files to install + ('build-base=', None, + "base build directory"), + ('build-lib=', None, + "build directory for pure Python modules"), + ('build-platlib=', None, + "build directory for extension modules"), + # Where to install documentation (eventually!) - ('doc-format=', None, "format of documentation to generate"), - ('install-man=', None, "directory for Unix man pages"), - ('install-html=', None, "directory for HTML documentation"), - ('install-info=', None, "directory for GNU info files"), + #('doc-format=', None, "format of documentation to generate"), + #('install-man=', None, "directory for Unix man pages"), + #('install-html=', None, "directory for HTML documentation"), + #('install-info=', None, "directory for GNU info files"), # Flags for 'build_py' - ('compile-py', None, "compile .py to .pyc"), - ('optimize-py', None, "compile .py to .pyo (optimized)"), + #('compile-py', None, "compile .py to .pyc"), + #('optimize-py', None, "compile .py to .pyo (optimized)"), ] def initialize_options (self): - self.build_base = None - self.build_lib = None - self.build_platlib = None - # Don't define 'prefix' or 'exec_prefix' so we can know when the # command is run whether the user supplied values self.prefix = None self.exec_prefix = None - # These two, we can supply real values for! (because they're - # not directories, and don't have a confusing multitude of - # possible derivations) - #self.install_scheme = 'site' - self.doc_format = None - # The actual installation directories are determined only at # run-time, so the user can supply just prefix (and exec_prefix?) # as a base for everything else self.install_lib = None self.install_platlib = None - self.install_site_lib = None - self.install_site_platlib = None self.install_path = None + self.build_base = None + self.build_lib = None + self.build_platlib = None + self.install_man = None self.install_html = None self.install_info = None @@ -114,75 +100,90 @@ class install (Command): # and assumes the Python 1.5 installation tree with no site.py # to fix things. - # Figure out the build directories, ie. where to install from - self.set_peer_option ('build', 'build_base', self.build_base) - self.set_undefined_options ('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib'), - ('build_platlib', 'build_platlib')) - # Figure out actual installation directories; the basic principle - # is: if the user supplied nothing, then use the directories that - # Python was built and installed with (ie. the compiled-in prefix - # and exec_prefix, and the actual installation directories gleaned - # by sysconfig). If the user supplied a prefix (and possibly - # exec_prefix), then we generate our own installation directories, - # following any pattern gleaned from sysconfig's findings. If no - # such pattern can be gleaned, then we'll just make do and try to - # ape the behaviour of Python's configure script. - - if self.prefix is None: # user didn't override - self.prefix = os.path.normpath (sys.prefix) + # is: ... + + sys_prefix = os.path.normpath (sys.prefix) + sys_exec_prefix = os.path.normpath (sys.exec_prefix) + + if self.prefix is None: + if self.exec_prefix is not None: + raise DistutilsOptionError, \ + "you may not supply exec_prefix without prefix" + self.prefix = sys_prefix + else: + # This is handy to guarantee that self.prefix is normalized -- + # but it could be construed as rude to go normalizing a + # user-supplied path (they might like to see their "../" or + # symlinks in the installation feedback). + self.prefix = os.path.normpath (self.prefix) + if self.exec_prefix is None: - self.exec_prefix = os.path.normpath (sys.exec_prefix) - - if self.install_lib is None: - self.install_lib = \ - self.replace_sys_prefix ('LIBDEST', ('lib','python1.5')) - if self.install_platlib is None: - # XXX this should probably be DESTSHARED -- but why is there no - # equivalent to DESTSHARED for the "site-packages" dir"? - self.install_platlib = \ - self.replace_sys_prefix ('BINLIBDEST', ('lib','python1.5'), 1) - - - # Here is where we decide where to install most library files: on - # POSIX systems, they go to 'site-packages' under the install_lib - # (determined above -- typically /usr/local/lib/python1.x). Note - # that on POSIX systems, platform-specific files belong in - # 'site-packages' under install_platlib. (The actual rule is that - # a module distribution that includes *any* platform-specific files - # -- ie. extension modules -- goes under install_platlib. This - # solves the "can't find extension module in a package" problem.) - # On non-POSIX systems, install_lib and install_platlib are the - # same (eg. "C:\Program Files\Python\Lib" on Windows), as are - # install_site_lib and install_site_platlib (eg. - # "C:\Program Files\Python" on Windows) -- everything will be dumped - # right into one of the install_site directories. (It doesn't - # really matter *which* one, of course, but I'll observe decorum - # and do it properly.) - - # 'base' and 'platbase' are the base directories for installing - # site-local files, eg. "/usr/local/lib/python1.5/site-packages" - # or "C:\Program Files\Python" + if self.prefix == sys_prefix: + self.exec_prefix = sys_exec_prefix + else: + self.exec_prefix = self.prefix + else: + # Same as above about handy versus rude to normalize user's + # exec_prefix. + self.exec_prefix = os.path.normpath (self.exec_prefix) + + if self.distribution.ext_modules: # any extensions to install? + effective_prefix = self.exec_prefix + else: + effective_prefix = self.prefix + if os.name == 'posix': - self.base = os.path.join (self.install_lib, - 'site-packages') - self.platbase = os.path.join (self.install_platlib, - 'site-packages') + if self.install_lib is None: + if self.prefix == sys_prefix: + self.install_lib = \ + os.path.join (effective_prefix, + "lib", + "python" + sys.version[:3], + "site-packages") + else: + self.install_lib = \ + os.path.join (effective_prefix, + "lib", + "python") # + sys.version[:3] ??? + # end if self.install_lib ... + + if self.install_platlib is None: + if self.exec_prefix == sys_exec_prefix: + self.install_platlib = \ + os.path.join (effective_prefix, + "lib", + "python" + sys.version[:3], + "site-packages") + else: + self.install_platlib = \ + os.path.join (effective_prefix, + "lib", + "python") # + sys.version[:3] ??? + # end if self.install_platlib ... + else: - self.base = self.prefix - self.platbase = self.exec_prefix - - # 'path_file' and 'extra_dirs' are how we handle distributions - # that need to be installed to their own directory, but aren't + raise DistutilsPlatformError, \ + "duh, I'm clueless (for now) about installing on %s" % os.name + + # end if/else on os.name + + + # 'path_file' and 'extra_dirs' are how we handle distributions that + # want to be installed to their own directory, but aren't # package-ized yet. 'extra_dirs' is just a directory under - # 'base' or 'platbase' where toplevel modules will actually be - # installed; 'path_file' is the basename of a .pth file to drop - # in 'base' or 'platbase' (depending on the distribution). Very - # often they will be the same, which is why we allow them to be - # supplied as a string or 1-tuple as well as a 2-element - # comma-separated string or a 2-tuple. + # 'install_lib' or 'install_platlib' where top-level modules will + # actually be installed; 'path_file' is the basename of a .pth file + # to drop in 'install_lib' or 'install_platlib' (depending on the + # distribution). Very often they will be the same, which is why we + # allow them to be supplied as a string or 1-tuple as well as a + # 2-element comma-separated string or a 2-tuple. + + # XXX this will drop a .pth file in install_{lib,platlib} even if + # they're not one of the site-packages directories: this is wrong! + # we need to suppress path_file in those cases, and warn if + # "install_lib/extra_dirs" is not in sys.path. + if self.install_path is None: self.install_path = self.distribution.install_path @@ -214,25 +215,12 @@ class install (Command): self.extra_dirs = extra_dirs - if self.install_site_lib is None: - self.install_site_lib = os.path.join (self.base, - extra_dirs) - - if self.install_site_platlib is None: - self.install_site_platlib = os.path.join (self.platbase, - extra_dirs) - - #if self.install_scheme == 'site': - # install_lib = self.install_site_lib - # install_platlib = self.install_site_platlib - #elif self.install_scheme == 'system': - # install_lib = self.install_lib - # install_platlib = self.install_platlib - #else: - # # XXX new exception for this kind of misbehaviour? - # raise DistutilsArgError, \ - # "invalid install scheme '%s'" % self.install_scheme - + # Figure out the build directories, ie. where to install from + self.set_peer_option ('build', 'build_base', self.build_base) + self.set_undefined_options ('build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib'), + ('build_platlib', 'build_platlib')) # Punt on doc directories for now -- after all, we're punting on # documentation completely! @@ -240,50 +228,13 @@ class install (Command): # finalize_options () - def replace_sys_prefix (self, config_attr, fallback_postfix, use_exec=0): - """Attempts to glean a simple pattern from an installation - directory available as a 'sysconfig' attribute: if the - directory name starts with the "system prefix" (the one - hard-coded in the Makefile and compiled into Python), - then replace it with the current installation prefix and - return the "relocated" installation directory.""" - - from distutils import sysconfig - - if use_exec: - sys_prefix = os.path.normpath (sys.exec_prefix) - my_prefix = self.exec_prefix - else: - sys_prefix = os.path.normpath (sys.prefix) - my_prefix = self.prefix - - val = getattr (sysconfig, config_attr) - if string.find (val, sys_prefix) == 0: - # If the sysconfig directory starts with the system prefix, - # then we can "relocate" it to the user-supplied prefix -- - # assuming, of course, it is different from the system prefix. - - if sys_prefix == my_prefix: - return val - else: - return my_prefix + val[len(sys_prefix):] - - else: - # Otherwise, just tack the "fallback postfix" onto the - # user-specified prefix. - - return apply (os.path.join, (my_prefix,) + fallback_postfix) - - # replace_sys_prefix () - - def run (self): # Obviously have to build before we can install self.run_peer ('build') # Install modules in two steps: "platform-shared" files (ie. pure - # python modules) and platform-specific files (compiled C + # Python modules) and platform-specific files (compiled C # extensions). Note that 'install_py' is smart enough to install # pure Python modules in the "platlib" directory if we built any # extensions. diff --git a/command/install_ext.py b/command/install_ext.py index b046dfbf..f1a3e247 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -26,7 +26,7 @@ class install_ext (Command): def finalize_options (self): self.set_undefined_options ('install', ('build_platlib', 'build_dir'), - ('install_site_platlib', 'install_dir')) + ('install_platlib', 'install_dir')) def run (self): diff --git a/command/install_lib.py b/command/install_lib.py index 919873c6..ab82e04b 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -27,22 +27,12 @@ class install_py (Command): def finalize_options (self): - # Find out from the 'build_ext' command if we were asked to build - # any extensions. If so, that means even pure-Python modules in - # this distribution have to be installed to the "platlib" - # directory. - extensions = self.get_peer_option ('build_ext', 'extensions') - if extensions: - dir_option = 'install_site_platlib' - else: - dir_option = 'install_site_lib' - # Get all the information we need to install pure Python modules # from the umbrella 'install' command -- build (source) directory, # install (target) directory, and whether to compile .py files. self.set_undefined_options ('install', ('build_lib', 'build_dir'), - (dir_option, 'install_dir'), + ('install_lib', 'install_dir'), ('compile_py', 'compile'), ('optimize_py', 'optimize')) @@ -52,8 +42,9 @@ class install_py (Command): # Make sure we have "built" all pure Python modules first self.run_peer ('build_py') - # Dump entire contents of the build directory to the installation - # directory (that's the beauty of having a build directory!) + # Install everything: simply dump the entire contents of the build + # directory to the installation directory (that's the beauty of + # having a build directory!) outfiles = self.copy_tree (self.build_dir, self.install_dir) # (Optionally) compile .py to .pyc @@ -65,7 +56,8 @@ class install_py (Command): from py_compile import compile for f in outfiles: - # XXX can't assume this filename mapping! + # XXX can't assume this filename mapping! (what if + # we're running under "python -O"?) # only compile the file if it is actually a .py file if f[-3:] == '.py': diff --git a/command/install_py.py b/command/install_py.py index 919873c6..ab82e04b 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -27,22 +27,12 @@ class install_py (Command): def finalize_options (self): - # Find out from the 'build_ext' command if we were asked to build - # any extensions. If so, that means even pure-Python modules in - # this distribution have to be installed to the "platlib" - # directory. - extensions = self.get_peer_option ('build_ext', 'extensions') - if extensions: - dir_option = 'install_site_platlib' - else: - dir_option = 'install_site_lib' - # Get all the information we need to install pure Python modules # from the umbrella 'install' command -- build (source) directory, # install (target) directory, and whether to compile .py files. self.set_undefined_options ('install', ('build_lib', 'build_dir'), - (dir_option, 'install_dir'), + ('install_lib', 'install_dir'), ('compile_py', 'compile'), ('optimize_py', 'optimize')) @@ -52,8 +42,9 @@ class install_py (Command): # Make sure we have "built" all pure Python modules first self.run_peer ('build_py') - # Dump entire contents of the build directory to the installation - # directory (that's the beauty of having a build directory!) + # Install everything: simply dump the entire contents of the build + # directory to the installation directory (that's the beauty of + # having a build directory!) outfiles = self.copy_tree (self.build_dir, self.install_dir) # (Optionally) compile .py to .pyc @@ -65,7 +56,8 @@ class install_py (Command): from py_compile import compile for f in outfiles: - # XXX can't assume this filename mapping! + # XXX can't assume this filename mapping! (what if + # we're running under "python -O"?) # only compile the file if it is actually a .py file if f[-3:] == '.py': -- cgit v1.2.1 From 06c2f98eb01493396435bb99b6e0264e3888353a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 26 Feb 2000 00:49:40 +0000 Subject: Try to deal with pre-1.5.2 IOError exception objects. --- core.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index ddd39a2c..a92bff95 100644 --- a/core.py +++ b/core.py @@ -99,8 +99,12 @@ def setup (**attrs): except KeyboardInterrupt: raise SystemExit, "interrupted" except IOError, exc: - # is this 1.5.2-specific? 1.5-specific? - raise SystemExit, "error: %s: %s" % (exc.filename, exc.strerror) + # arg, try to work with Python pre-1.5.2 + if hasattr (exc, 'filename') and hasattr (exc, 'strerror'): + raise SystemExit, \ + "error: %s: %s" % (exc.filename, exc.strerror) + else: + raise SystemExit, str (exc) # setup () -- cgit v1.2.1 From 23c5fe111b4770b4ec04b5be7fbbd081928df5c4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 01:19:37 +0000 Subject: Build reorg: change 'build_dir' option to 'build_lib'. --- command/build_py.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 7ae207b8..05a1beff 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -19,19 +19,19 @@ class build_py (Command): description = "\"build\" pure Python modules (copy to build directory)" user_options = [ - ('build-dir=', 'd', "directory for platform-shared files"), + ('build-lib=', 'd', "directory to \"build\" (copy) to"), ] def initialize_options (self): - self.build_dir = None + self.build_lib = None self.modules = None self.package = None self.package_dir = None def finalize_options (self): self.set_undefined_options ('build', - ('build_lib', 'build_dir')) + ('build_lib', 'build_lib')) # Get the distribution options that are aliases for build_py # options -- list of packages and list of modules. @@ -259,11 +259,11 @@ class build_py (Command): "'package' must be a string (dot-separated), list, or tuple" # Now put the module source file into the "build" area -- this is - # easy, we just copy it somewhere under self.build_dir (the build + # easy, we just copy it somewhere under self.build_lib (the build # directory for Python source). outfile_path = list (package) outfile_path.append (module + ".py") - outfile_path.insert (0, self.build_dir) + outfile_path.insert (0, self.build_lib) outfile = apply (os.path.join, outfile_path) dir = os.path.dirname (outfile) @@ -277,9 +277,9 @@ class build_py (Command): for (module, package, module_file) in modules: # Now "build" the module -- ie. copy the source file to - # self.build_dir (the build directory for Python source). + # self.build_lib (the build directory for Python source). # (Actually, it gets copied to the directory for this package - # under self.build_dir.) + # under self.build_lib.) self.build_module (module, module_file, package) # build_modules () @@ -300,7 +300,7 @@ class build_py (Command): modules = self.find_package_modules (package, package_dir) # Now loop over the modules we found, "building" each one (just - # copy it to self.build_dir). + # copy it to self.build_lib). for (module, module_file) in modules: self.build_module (module, module_file, package) -- cgit v1.2.1 From 403c4a31e1ceb3e900a8e1241d5ab9e1093d0171 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 01:26:45 +0000 Subject: Build reorg: * 'build_lib' -> 'build_purelib' * new 'build_lib' and 'build_temp' options * use 'get_platform()' to initialize 'build_platlib' and 'build_temp' --- command/build.py | 55 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/command/build.py b/command/build.py index 28241e10..e25ef81c 100644 --- a/command/build.py +++ b/command/build.py @@ -6,9 +6,9 @@ Implements the Distutils 'build' command.""" __rcsid__ = "$Id$" -import os +import sys, os from distutils.core import Command - +from distutils.util import get_platform class build (Command): @@ -17,10 +17,15 @@ class build (Command): user_options = [ ('build-base=', 'b', "base directory for build library"), - ('build-lib=', 'l', - "directory for platform-shared files"), - ('build-platlib=', 'p', - "directory for platform-specific files"), + ('build-purelib=', None, + "build directory for platform-neutral distributions"), + ('build-platlib=', None, + "build directory for platform-specific distributions"), + ('build-lib=', None, + "build directory for all distribution (defaults to either " + + "build-purelib or build-platlib"), + ('build-temp=', 't', + "temporary build directory"), ('debug', 'g', "compile extensions and libraries with debugging information"), ] @@ -29,17 +34,43 @@ class build (Command): self.build_base = 'build' # these are decided only after 'build_base' has its final value # (unless overridden by the user or client) - self.build_lib = None + self.build_purelib = None self.build_platlib = None + self.build_lib = None + self.build_temp = None self.debug = None def finalize_options (self): - # 'build_lib' and 'build_platlib' just default to 'lib' and - # 'platlib' under the base build directory - if self.build_lib is None: - self.build_lib = os.path.join (self.build_base, 'lib') + + # Need this to name platform-specific directories, but sys.platform + # is not enough -- it only names the OS and version, not the + # hardware architecture! + self.plat = get_platform () + + # 'build_purelib' and 'build_platlib' just default to 'lib' and + # 'lib.' under the base build directory. We only use one of + # them for a given distribution, though -- + if self.build_purelib is None: + self.build_purelib = os.path.join (self.build_base, 'lib') if self.build_platlib is None: - self.build_platlib = os.path.join (self.build_base, 'platlib') + self.build_platlib = os.path.join (self.build_base, + 'lib.' + self.plat) + + # 'build_lib' is the actual directory that we will use for this + # particular module distribution -- if user didn't supply it, pick + # one of 'build_purelib' or 'build_platlib'. + if self.build_lib is None: + if self.distribution.ext_modules: + self.build_lib = self.build_platlib + else: + self.build_lib = self.build_purelib + + # 'build_temp' -- temporary directory for compiler turds, + # "build/temp." + if self.build_temp is None: + self.build_temp = os.path.join (self.build_base, + 'temp.' + self.plat) + # finalize_options () def run (self): -- cgit v1.2.1 From d762d7cd369f81c7e0a359929d90a8a617d8dfeb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 01:43:28 +0000 Subject: Build reorg: * 'build_dir' -> 'build_lib', which by default takes its value straight from 'build_lib' in the 'build' command * added 'build_temp' and 'inplace' options * change 'build_extensions()' to put object files (compiler turds) in 'build_temp' dir * complicated the name-of-extension-file shenanigans in 'build_extensions()' to support "in-place" extension building, i.e. put the extension right into the source tree (handy for developers) * added 'get_ext_fullname()', renamed 'extension_filename()' to 'get_ext_filename()', and tweaked the latter a bit -- all to support the new filename shenanigans --- command/build_ext.py | 57 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 8ed3a973..2bbd89d8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -43,8 +43,13 @@ class build_ext (Command): # in between initialize_options() and finalize_options()) user_options = [ - ('build-dir=', 'd', + ('build-lib=', 'b', "directory for compiled extension modules"), + ('build-temp=', 't', + "directory for temporary files (build by-products)"), + ('inplace', 'i', + "ignore build-lib and put compiled extensions into the source" + + "directory alongside your pure Python modules"), ('include-dirs=', 'I', "list of directories to search for header files"), ('define=', 'D', @@ -66,7 +71,9 @@ class build_ext (Command): def initialize_options (self): self.extensions = None - self.build_dir = None + self.build_lib = None + self.build_temp = None + self.inplace = 0 self.package = None self.include_dirs = None @@ -83,7 +90,8 @@ class build_ext (Command): from distutils import sysconfig self.set_undefined_options ('build', - ('build_platlib', 'build_dir'), + ('build_lib', 'build_lib'), + ('build_temp', 'build_temp'), ('debug', 'debug')) if self.package is None: @@ -227,15 +235,15 @@ class build_ext (Command): # precedent!) macros = build_info.get ('macros') include_dirs = build_info.get ('include_dirs') - self.compiler.compile (sources, - macros=macros, - include_dirs=include_dirs, - debug=self.debug) + objects = self.compiler.compile (sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=include_dirs, + debug=self.debug) # Now link the object files together into a "shared object" -- # of course, first we have to figure out all the other things # that go into the mix. - objects = self.compiler.object_filenames (sources) extra_objects = build_info.get ('extra_objects') if extra_objects: objects.extend (extra_objects) @@ -257,12 +265,25 @@ class build_ext (Command): else: modname = string.split (extension_name, '.')[-1] extra_args.append('/export:init%s'%modname) + # end if MSVC + + fullname = self.get_ext_fullname (extension_name) + if self.inplace: + # ignore build-lib -- put the compiled extension into + # the source tree along with pure Python modules + + modpath = string.split (fullname, '.') + package = string.join (modpath[0:-1], '.') + base = modpath[-1] + + build_py = self.find_peer ('build_py') + package_dir = build_py.get_package_dir (package) + ext_filename = os.path.join (package_dir, + self.get_ext_filename(base)) + else: + ext_filename = os.path.join (self.build_lib, + self.get_ext_filename(fullname)) - ext_filename = self.extension_filename \ - (extension_name, self.package) - ext_filename = os.path.join (self.build_dir, ext_filename) - dest_dir = os.path.dirname (ext_filename) - self.mkpath (dest_dir) self.compiler.link_shared_object (objects, ext_filename, libraries=libraries, library_dirs=library_dirs, @@ -272,10 +293,14 @@ class build_ext (Command): # build_extensions () - def extension_filename (self, ext_name, package=None): + def get_ext_fullname (self, ext_name): + if self.package is None: + return ext_name + else: + return self.package + '.' + ext_name + + def get_ext_filename (self, ext_name): from distutils import sysconfig - if package: - ext_name = package + '.' + ext_name ext_path = string.split (ext_name, '.') return apply (os.path.join, ext_path) + sysconfig.SO -- cgit v1.2.1 From f5aaad4ab595030d314a3f2d815f3533ba7364e9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 14:40:15 +0000 Subject: Added 'get_platform()' to construct a string that identifies the current platform, using 'os.uname()' or 'sys.platform'. --- util.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/util.py b/util.py index 58d58439..641a35af 100644 --- a/util.py +++ b/util.py @@ -11,7 +11,7 @@ file causing it.""" __rcsid__ = "$Id$" -import os +import os, string from distutils.errors import * @@ -437,3 +437,21 @@ def write_file (filename, contents): for line in contents: f.write (line + "\n") f.close () + + +def get_platform (): + """Return a string (suitable for tacking onto directory names) that + identifies the current platform. Under Unix, identifies both the OS + and hardware architecture, e.g. "linux-i586", "solaris-sparc", + "irix-mips". For Windows and Mac OS, just returns 'sys.platform' -- + i.e. "???" or "???".""" + + if os.name == 'posix': + uname = os.uname() + OS = uname[0] + arch = uname[4] + return "%s-%s" % (string.lower (OS), string.lower (arch)) + else: + return sys.platform + +# get_platform() -- cgit v1.2.1 From 8aea04d670e4b002754734358b5ebdfda20d8666 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 14:42:07 +0000 Subject: Added call to 'ensure_ready()' on the command object in 'Distribution.find_command_obj()'. --- core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core.py b/core.py index a92bff95..83a98a84 100644 --- a/core.py +++ b/core.py @@ -506,6 +506,7 @@ class Distribution: cmd_obj = self.command_obj.get (command) if not cmd_obj and create: cmd_obj = self.create_command_obj (command) + cmd_obj.ensure_ready () self.command_obj[command] = cmd_obj return cmd_obj -- cgit v1.2.1 From bacc83b29ce5519b298bcb9ec9d558a2fec94701 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 14:43:12 +0000 Subject: Added 'mkpath()' method: convenience wrapper around 'util.mkpath()' that adds the compiler objects 'verbose' and 'dry_run' flags. --- ccompiler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 7d2d9f58..72b77573 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -12,7 +12,7 @@ from types import * from copy import copy from distutils.errors import * from distutils.spawn import spawn -from distutils.util import move_file +from distutils.util import move_file, mkpath class CCompiler: @@ -453,6 +453,9 @@ class CCompiler: def move_file (self, src, dst): return move_file (src, dst, verbose=self.verbose, dry_run=self.dry_run) + def mkpath (self, name, mode=0777): + mkpath (name, mode, self.verbose, self.dry_run) + # class CCompiler -- cgit v1.2.1 From cbad9d0a7c7889f0593b839d4a87966fd54e5a81 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 14:43:49 +0000 Subject: In compile/link methods: ensure that the directory we expect to be writing to exists before calling the compiler/linker. --- unixccompiler.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 770a543c..77d12d32 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -159,6 +159,8 @@ class UnixCCompiler (CCompiler): if extra_postargs is None: extra_postargs = [] + if output_dir is not None: + self.mkpath (output_dir) for (source,object) in srcobj: self.spawn ([self.cc] + cc_args + [source, '-o', object] + @@ -167,7 +169,7 @@ class UnixCCompiler (CCompiler): # Have to re-fetch list of object filenames, because we want to # return *all* of them, including those that weren't recompiled on # this call! - return self.object_filenames (orig_sources, output_dir) + return self.object_filenames (orig_sources, output_dir=output_dir) def _fix_link_args (self, output_dir, libraries, library_dirs): @@ -226,6 +228,7 @@ class UnixCCompiler (CCompiler): newer = newer_group (objects, output_filename) if self.force or newer: + self.mkpath (os.path.dirname (output_filename)) self.spawn ([self.archiver, self.archiver_options, output_filename] + @@ -298,6 +301,7 @@ class UnixCCompiler (CCompiler): ld_args[:0] = extra_preargs if extra_postargs: ld_args.extend (extra_postargs) + self.mkpath (os.path.dirname (output_filename)) self.spawn ([self.ld_shared] + ld_args) else: self.announce ("skipping %s (up-to-date)" % output_filename) @@ -340,6 +344,7 @@ class UnixCCompiler (CCompiler): ld_args[:0] = extra_preargs if extra_postargs: ld_args.extend (extra_postargs) + self.mkpath (os.path.dirname (output_filename)) self.spawn ([self.ld_exec] + ld_args) else: self.announce ("skipping %s (up-to-date)" % output_filename) -- cgit v1.2.1 From 872379e98b29803bb5ec360686fe3365456c7313 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 2 Mar 2000 01:21:54 +0000 Subject: In the 'compile()' method: preserve the directory portion of source filenames when constructing object filenames, even if output_dir given -- eg. "foo/bar.c" will compile to "foo/bar.o" without an output_dir, and to "temp/foo/bar.o" if output_dir is "temp". --- unixccompiler.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 77d12d32..518132f6 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -100,6 +100,7 @@ class UnixCCompiler (CCompiler): def compile (self, sources, output_dir=None, + keep_dir=0, macros=None, include_dirs=None, debug=0, @@ -134,7 +135,11 @@ class UnixCCompiler (CCompiler): # don't have to recompile. (Simplistic check -- we just compare the # source and object file, no deep dependency checking involving # header files. Hmmm.) - objects = self.object_filenames (sources, output_dir=output_dir) + objects = self.object_filenames (sources, + output_dir=output_dir, + keep_dir=keep_dir) + all_objects = copy (objects) # preserve full list to return + if not self.force: skipped = newer_pairwise (sources, objects) for skipped_pair in skipped: @@ -169,7 +174,7 @@ class UnixCCompiler (CCompiler): # Have to re-fetch list of object filenames, because we want to # return *all* of them, including those that weren't recompiled on # this call! - return self.object_filenames (orig_sources, output_dir=output_dir) + return all_objects def _fix_link_args (self, output_dir, libraries, library_dirs): -- cgit v1.2.1 From 76ec448446c6c95e8942e53ffc6d5fae0b90a690 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 2 Mar 2000 01:27:36 +0000 Subject: Added command description. Added 'build_clib' and 'build_temp' options (where to put C libraries and where to put temporary compiler by-products, ie. object files). Moved the call to 'check_library_list()' from 'run()' to 'finalize_options()' -- that way, if we're going to crash we do so earlier, and we guarantee that the library list is valid before we do anything (not just run). Disallow directory separators in library names -- the compiled library always goes right in 'build_clib'. Added 'get_library_names()', so the "build_ext" command knows what libraries to link every extension with. --- command/build_clib.py | 62 +++++++++++++++++++++++++++++++++++++++------------ command/build_lib.py | 62 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 96 insertions(+), 28 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index 9cf53da0..9cb584a1 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -28,12 +28,21 @@ from distutils.ccompiler import new_compiler class build_lib (Command): + description = "build C/C++ libraries used by Python extensions" + user_options = [ + ('build-clib', 'b', + "directory to build C/C++ libraries to"), + ('build-temp', 't', + "directory to put temporary build by-products"), ('debug', 'g', "compile with debugging information"), ] def initialize_options (self): + self.build_clib = None + self.build_temp = None + # List of libraries to build self.libraries = None @@ -45,10 +54,23 @@ class build_lib (Command): # initialize_options() + def finalize_options (self): + + # This might be confusing: both build-clib and build-temp default + # to build-temp as defined by the "build" command. This is because + # I think that C libraries are really just temporary build + # by-products, at least from the point of view of building Python + # extensions -- but I want to keep my options open. self.set_undefined_options ('build', + ('build_temp', 'build_clib'), + ('build_temp', 'build_temp'), ('debug', 'debug')) + self.libraries = self.distribution.libraries + if self.libraries: + self.check_library_list (self.libraries) + if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if type (self.include_dirs) is StringType: @@ -65,7 +87,6 @@ class build_lib (Command): if not self.libraries: return - self.check_library_list (self.libraries) # Yech -- this is cut 'n pasted from build_ext.py! self.compiler = new_compiler (plat=os.environ.get ('PLAT'), @@ -110,6 +131,12 @@ class build_lib (Command): raise DistutilsValueError, \ "first element of each tuple in 'libraries' " + \ "must be a string (the library name)" + if '/' in lib[0] or (os.sep != '/' and os.sep in lib[0]): + raise DistutilsValueError, \ + ("bad library name '%s': " + + "may not contain directory separators") % \ + lib[0] + if type (lib[1]) is not DictionaryType: raise DistutilsValueError, \ "second element of each tuple in 'libraries' " + \ @@ -119,6 +146,21 @@ class build_lib (Command): # check_library_list () + def get_library_names (self): + # Assume the library list is valid -- 'check_library_list()' is + # called from 'finalize_options()', so it should be! + + if not self.libraries: + return None + + lib_names = [] + for (lib_name, build_info) in self.libraries: + lib_names.append (lib_name) + return lib_names + + # get_library_names () + + def build_libraries (self, libraries): compiler = self.compiler @@ -134,35 +176,27 @@ class build_lib (Command): self.announce ("building '%s' library" % lib_name) - # Extract the directory the library is intended to go in -- - # note translation from "universal" slash-separated form to - # current platform's pathname convention (so we can use the - # string for actual filesystem use). - path = tuple (string.split (lib_name, '/')[:-1]) - if path: - lib_dir = apply (os.path.join, path) - else: - lib_dir = '' - # First, compile the source code to object files in the library # directory. (This should probably change to putting object # files in a temporary build directory.) macros = build_info.get ('macros') include_dirs = build_info.get ('include_dirs') objects = self.compiler.compile (sources, + output_dir=self.build_temp, + keep_dir=1, macros=macros, include_dirs=include_dirs, - output_dir=lib_dir, debug=self.debug) # Now "link" the object files together into a static library. # (On Unix at least, this isn't really linking -- it just # builds an archive. Whatever.) - self.compiler.link_static_lib (objects, lib_name, debug=self.debug) + self.compiler.link_static_lib (objects, lib_name, + output_dir=self.build_clib, + debug=self.debug) # for libraries # build_libraries () - # class BuildLib diff --git a/command/build_lib.py b/command/build_lib.py index 9cf53da0..9cb584a1 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -28,12 +28,21 @@ from distutils.ccompiler import new_compiler class build_lib (Command): + description = "build C/C++ libraries used by Python extensions" + user_options = [ + ('build-clib', 'b', + "directory to build C/C++ libraries to"), + ('build-temp', 't', + "directory to put temporary build by-products"), ('debug', 'g', "compile with debugging information"), ] def initialize_options (self): + self.build_clib = None + self.build_temp = None + # List of libraries to build self.libraries = None @@ -45,10 +54,23 @@ class build_lib (Command): # initialize_options() + def finalize_options (self): + + # This might be confusing: both build-clib and build-temp default + # to build-temp as defined by the "build" command. This is because + # I think that C libraries are really just temporary build + # by-products, at least from the point of view of building Python + # extensions -- but I want to keep my options open. self.set_undefined_options ('build', + ('build_temp', 'build_clib'), + ('build_temp', 'build_temp'), ('debug', 'debug')) + self.libraries = self.distribution.libraries + if self.libraries: + self.check_library_list (self.libraries) + if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if type (self.include_dirs) is StringType: @@ -65,7 +87,6 @@ class build_lib (Command): if not self.libraries: return - self.check_library_list (self.libraries) # Yech -- this is cut 'n pasted from build_ext.py! self.compiler = new_compiler (plat=os.environ.get ('PLAT'), @@ -110,6 +131,12 @@ class build_lib (Command): raise DistutilsValueError, \ "first element of each tuple in 'libraries' " + \ "must be a string (the library name)" + if '/' in lib[0] or (os.sep != '/' and os.sep in lib[0]): + raise DistutilsValueError, \ + ("bad library name '%s': " + + "may not contain directory separators") % \ + lib[0] + if type (lib[1]) is not DictionaryType: raise DistutilsValueError, \ "second element of each tuple in 'libraries' " + \ @@ -119,6 +146,21 @@ class build_lib (Command): # check_library_list () + def get_library_names (self): + # Assume the library list is valid -- 'check_library_list()' is + # called from 'finalize_options()', so it should be! + + if not self.libraries: + return None + + lib_names = [] + for (lib_name, build_info) in self.libraries: + lib_names.append (lib_name) + return lib_names + + # get_library_names () + + def build_libraries (self, libraries): compiler = self.compiler @@ -134,35 +176,27 @@ class build_lib (Command): self.announce ("building '%s' library" % lib_name) - # Extract the directory the library is intended to go in -- - # note translation from "universal" slash-separated form to - # current platform's pathname convention (so we can use the - # string for actual filesystem use). - path = tuple (string.split (lib_name, '/')[:-1]) - if path: - lib_dir = apply (os.path.join, path) - else: - lib_dir = '' - # First, compile the source code to object files in the library # directory. (This should probably change to putting object # files in a temporary build directory.) macros = build_info.get ('macros') include_dirs = build_info.get ('include_dirs') objects = self.compiler.compile (sources, + output_dir=self.build_temp, + keep_dir=1, macros=macros, include_dirs=include_dirs, - output_dir=lib_dir, debug=self.debug) # Now "link" the object files together into a static library. # (On Unix at least, this isn't really linking -- it just # builds an archive. Whatever.) - self.compiler.link_static_lib (objects, lib_name, debug=self.debug) + self.compiler.link_static_lib (objects, lib_name, + output_dir=self.build_clib, + debug=self.debug) # for libraries # build_libraries () - # class BuildLib -- cgit v1.2.1 From 720accdc1c3e3d7926cfd26d58c368b87b551c12 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 2 Mar 2000 01:32:21 +0000 Subject: If the "build_lib" command built any C libraries, link with them all when building extensions (uses build_lib's 'get_library_names()' method). Ensure that the relative structure of source filenames is preserved in the temporary build tree, eg. foo/bar.c compiles to build/temp./foo/bar.o. --- command/build_ext.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 2bbd89d8..fd5cd7a5 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -164,7 +164,15 @@ class build_ext (Command): self.compiler.set_runtime_library_dirs (self.rpath) if self.link_objects is not None: self.compiler.set_link_objects (self.link_objects) - + + if self.distribution.libraries: + build_lib = self.find_peer ('build_lib') + self.libraries = build_lib.get_library_names () or [] + self.library_dirs = [build_lib.build_clib] + else: + self.libraries = [] + self.library_dirs = [] + # Now the real loop over extensions self.build_extensions (self.extensions) @@ -237,6 +245,7 @@ class build_ext (Command): include_dirs = build_info.get ('include_dirs') objects = self.compiler.compile (sources, output_dir=self.build_temp, + keep_dir=1, macros=macros, include_dirs=include_dirs, debug=self.debug) @@ -247,8 +256,8 @@ class build_ext (Command): extra_objects = build_info.get ('extra_objects') if extra_objects: objects.extend (extra_objects) - libraries = build_info.get ('libraries') - library_dirs = build_info.get ('library_dirs') + libraries = self.libraries + build_info.get ('libraries') + library_dirs = self.library_dirs + build_info.get ('library_dirs') extra_args = build_info.get ('extra_link_args') or [] if self.compiler.compiler_type == 'msvc': -- cgit v1.2.1 From 41b233728bac2f952bf2cc9b5ffb19409b9a48e9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 2 Mar 2000 01:49:45 +0000 Subject: Changed '__rcsid__' to '__revision__'. --- __init__.py | 2 +- ccompiler.py | 2 +- command/__init__.py | 2 +- command/build.py | 2 +- command/build_clib.py | 2 +- command/build_ext.py | 2 +- command/build_lib.py | 2 +- command/build_py.py | 2 +- command/install.py | 2 +- command/install_ext.py | 2 +- command/install_lib.py | 2 +- command/install_py.py | 2 +- command/sdist.py | 2 +- core.py | 2 +- errors.py | 2 +- fancy_getopt.py | 2 +- msvccompiler.py | 2 +- spawn.py | 2 +- unixccompiler.py | 2 +- util.py | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/__init__.py b/__init__.py index 18deaadf..1f23b972 100644 --- a/__init__.py +++ b/__init__.py @@ -8,4 +8,4 @@ used from a setup script as setup (...) """ -__rcsid__ = "$Id$" +__revision__ = "$Id$" diff --git a/ccompiler.py b/ccompiler.py index 72b77573..4819b23c 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -5,7 +5,7 @@ for the Distutils compiler abstraction model.""" # created 1999/07/05, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, os from types import * diff --git a/command/__init__.py b/command/__init__.py index ea979f9b..40595ba9 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -13,7 +13,7 @@ commands. Currently this means: but this list will undoubtedly grow with time.""" -__rcsid__ = "$Id$" +__revision__ = "$Id$" __all__ = ['build', 'build_py', diff --git a/command/build.py b/command/build.py index e25ef81c..d81bc885 100644 --- a/command/build.py +++ b/command/build.py @@ -4,7 +4,7 @@ Implements the Distutils 'build' command.""" # created 1999/03/08, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, os from distutils.core import Command diff --git a/command/build_clib.py b/command/build_clib.py index 9cb584a1..955cf565 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -7,7 +7,7 @@ module.""" # created (an empty husk) 1999/12/18, Greg Ward # fleshed out 2000/02/03-04 -__rcsid__ = "$Id$" +__revision__ = "$Id$" # XXX this module has *lots* of code ripped-off quite transparently from diff --git a/command/build_ext.py b/command/build_ext.py index fd5cd7a5..6da02fed 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -6,7 +6,7 @@ extensions ASAP).""" # created 1999/08/09, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, os, string, re from types import * diff --git a/command/build_lib.py b/command/build_lib.py index 9cb584a1..955cf565 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -7,7 +7,7 @@ module.""" # created (an empty husk) 1999/12/18, Greg Ward # fleshed out 2000/02/03-04 -__rcsid__ = "$Id$" +__revision__ = "$Id$" # XXX this module has *lots* of code ripped-off quite transparently from diff --git a/command/build_py.py b/command/build_py.py index 05a1beff..2d0ad1ce 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -4,7 +4,7 @@ Implements the Distutils 'build_py' command.""" # created 1999/03/08, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, string, os from types import * diff --git a/command/install.py b/command/install.py index a89bc025..1df55843 100644 --- a/command/install.py +++ b/command/install.py @@ -4,7 +4,7 @@ Implements the Distutils 'install' command.""" # created 1999/03/13, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, os, string from types import * diff --git a/command/install_ext.py b/command/install_ext.py index f1a3e247..8d23fa4c 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -4,7 +4,7 @@ Implement the Distutils "install_ext" command to install extension modules.""" # created 1999/09/12, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" from distutils.core import Command from distutils.util import copy_tree diff --git a/command/install_lib.py b/command/install_lib.py index ab82e04b..33cf6894 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -1,6 +1,6 @@ # created 1999/03/13, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, string from distutils.core import Command diff --git a/command/install_py.py b/command/install_py.py index ab82e04b..33cf6894 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -1,6 +1,6 @@ # created 1999/03/13, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, string from distutils.core import Command diff --git a/command/sdist.py b/command/sdist.py index 6b838bde..726458a2 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -4,7 +4,7 @@ Implements the Distutils 'sdist' command (create a source distribution).""" # created 1999/09/22, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, os, string, re import fnmatch diff --git a/core.py b/core.py index 83a98a84..a31e60ce 100644 --- a/core.py +++ b/core.py @@ -8,7 +8,7 @@ may be subclassed by clients for still more flexibility).""" # created 1999/03/01, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, os import string, re diff --git a/errors.py b/errors.py index 17d1abc7..86d91dd6 100644 --- a/errors.py +++ b/errors.py @@ -10,7 +10,7 @@ symbols whose names start with "Distutils" and end with "Error".""" # created 1999/03/03, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import types diff --git a/fancy_getopt.py b/fancy_getopt.py index 86e9f326..3110ab30 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -10,7 +10,7 @@ additional features: # created 1999/03/03, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, string, re from types import * diff --git a/msvccompiler.py b/msvccompiler.py index e489c1c8..2dd8dc10 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -6,7 +6,7 @@ for the Microsoft Visual Studio.""" # created 1999/08/19, Perry Stoll # -__rcsid__ = "$Id$" +__revision__ = "$Id$" import os import sys diff --git a/spawn.py b/spawn.py index 9a88ac89..847346cc 100644 --- a/spawn.py +++ b/spawn.py @@ -5,7 +5,7 @@ specific functions for launching another program in a sub-process.""" # created 1999/07/24, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import sys, os, string from distutils.errors import * diff --git a/unixccompiler.py b/unixccompiler.py index 518132f6..7765faf8 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ the "typical" Unix-style command-line C compiler: # created 1999/07/05, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import string, re, os from types import * diff --git a/util.py b/util.py index 641a35af..85f3a345 100644 --- a/util.py +++ b/util.py @@ -9,7 +9,7 @@ file causing it.""" # created 1999/03/08, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import os, string from distutils.errors import * -- cgit v1.2.1 From e67e55e012bc55359655cabd78e2bf9e4ec0d340 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 2 Mar 2000 01:57:12 +0000 Subject: Renamed 'build_lib' command to 'build_clib': * replaced build_lib.py with build_clib.py * renamed the class in build_clib.py * changed all references to 'build_lib' command in other command classes --- command/build.py | 2 +- command/build_clib.py | 6 +- command/build_ext.py | 6 +- command/build_lib.py | 202 -------------------------------------------------- 4 files changed, 7 insertions(+), 209 deletions(-) delete mode 100644 command/build_lib.py diff --git a/command/build.py b/command/build.py index d81bc885..97466fdb 100644 --- a/command/build.py +++ b/command/build.py @@ -87,7 +87,7 @@ class build (Command): # be needed by extension modules, so obviously have to be done # first! if self.distribution.libraries: - self.run_peer ('build_lib') + self.run_peer ('build_clib') # And now 'build_ext' -- compile extension modules and put them # into the build tree diff --git a/command/build_clib.py b/command/build_clib.py index 955cf565..7a6ef7a8 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -1,6 +1,6 @@ -"""distutils.command.build_lib +"""distutils.command.build_clib -Implements the Distutils 'build_lib' command, to build a C/C++ library +Implements the Distutils 'build_clib' command, to build a C/C++ library that is included in the module distribution and needed by an extension module.""" @@ -26,7 +26,7 @@ from distutils.errors import * from distutils.ccompiler import new_compiler -class build_lib (Command): +class build_clib (Command): description = "build C/C++ libraries used by Python extensions" diff --git a/command/build_ext.py b/command/build_ext.py index 6da02fed..bbdf062b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -166,9 +166,9 @@ class build_ext (Command): self.compiler.set_link_objects (self.link_objects) if self.distribution.libraries: - build_lib = self.find_peer ('build_lib') - self.libraries = build_lib.get_library_names () or [] - self.library_dirs = [build_lib.build_clib] + build_clib = self.find_peer ('build_clib') + self.libraries = build_clib.get_library_names () or [] + self.library_dirs = [build_clib.build_clib] else: self.libraries = [] self.library_dirs = [] diff --git a/command/build_lib.py b/command/build_lib.py deleted file mode 100644 index 955cf565..00000000 --- a/command/build_lib.py +++ /dev/null @@ -1,202 +0,0 @@ -"""distutils.command.build_lib - -Implements the Distutils 'build_lib' command, to build a C/C++ library -that is included in the module distribution and needed by an extension -module.""" - -# created (an empty husk) 1999/12/18, Greg Ward -# fleshed out 2000/02/03-04 - -__revision__ = "$Id$" - - -# XXX this module has *lots* of code ripped-off quite transparently from -# build_ext.py -- not surprisingly really, as the work required to build -# a static library from a collection of C source files is not really all -# that different from what's required to build a shared object file from -# a collection of C source files. Nevertheless, I haven't done the -# necessary refactoring to account for the overlap in code between the -# two modules, mainly because a number of subtle details changed in the -# cut 'n paste. Sigh. - -import os, string -from types import * -from distutils.core import Command -from distutils.errors import * -from distutils.ccompiler import new_compiler - - -class build_lib (Command): - - description = "build C/C++ libraries used by Python extensions" - - user_options = [ - ('build-clib', 'b', - "directory to build C/C++ libraries to"), - ('build-temp', 't', - "directory to put temporary build by-products"), - ('debug', 'g', - "compile with debugging information"), - ] - - def initialize_options (self): - self.build_clib = None - self.build_temp = None - - # List of libraries to build - self.libraries = None - - # Compilation options for all libraries - self.include_dirs = None - self.define = None - self.undef = None - self.debug = None - - # initialize_options() - - - def finalize_options (self): - - # This might be confusing: both build-clib and build-temp default - # to build-temp as defined by the "build" command. This is because - # I think that C libraries are really just temporary build - # by-products, at least from the point of view of building Python - # extensions -- but I want to keep my options open. - self.set_undefined_options ('build', - ('build_temp', 'build_clib'), - ('build_temp', 'build_temp'), - ('debug', 'debug')) - - self.libraries = self.distribution.libraries - if self.libraries: - self.check_library_list (self.libraries) - - if self.include_dirs is None: - self.include_dirs = self.distribution.include_dirs or [] - if type (self.include_dirs) is StringType: - self.include_dirs = string.split (self.include_dirs, - os.pathsep) - - # XXX same as for build_ext -- what about 'self.define' and - # 'self.undef' ? - - # finalize_options() - - - def run (self): - - if not self.libraries: - return - - # Yech -- this is cut 'n pasted from build_ext.py! - self.compiler = new_compiler (plat=os.environ.get ('PLAT'), - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force) - if self.include_dirs is not None: - self.compiler.set_include_dirs (self.include_dirs) - if self.define is not None: - # 'define' option is a list of (name,value) tuples - for (name,value) in self.define: - self.compiler.define_macro (name, value) - if self.undef is not None: - for macro in self.undef: - self.compiler.undefine_macro (macro) - - self.build_libraries (self.libraries) - - # run() - - - def check_library_list (self, libraries): - """Ensure that the list of libraries (presumably provided as a - command option 'libraries') is valid, i.e. it is a list of - 2-tuples, where the tuples are (library_name, build_info_dict). - Raise DistutilsValueError if the structure is invalid anywhere; - just returns otherwise.""" - - # Yechh, blecch, ackk: this is ripped straight out of build_ext.py, - # with only names changed to protect the innocent! - - if type (libraries) is not ListType: - raise DistutilsValueError, \ - "'libraries' option must be a list of tuples" - - for lib in libraries: - if type (lib) is not TupleType and len (lib) != 2: - raise DistutilsValueError, \ - "each element of 'libraries' must a 2-tuple" - - if type (lib[0]) is not StringType: - raise DistutilsValueError, \ - "first element of each tuple in 'libraries' " + \ - "must be a string (the library name)" - if '/' in lib[0] or (os.sep != '/' and os.sep in lib[0]): - raise DistutilsValueError, \ - ("bad library name '%s': " + - "may not contain directory separators") % \ - lib[0] - - if type (lib[1]) is not DictionaryType: - raise DistutilsValueError, \ - "second element of each tuple in 'libraries' " + \ - "must be a dictionary (build info)" - # for lib - - # check_library_list () - - - def get_library_names (self): - # Assume the library list is valid -- 'check_library_list()' is - # called from 'finalize_options()', so it should be! - - if not self.libraries: - return None - - lib_names = [] - for (lib_name, build_info) in self.libraries: - lib_names.append (lib_name) - return lib_names - - # get_library_names () - - - def build_libraries (self, libraries): - - compiler = self.compiler - - for (lib_name, build_info) in libraries: - sources = build_info.get ('sources') - if sources is None or type (sources) not in (ListType, TupleType): - raise DistutilsValueError, \ - ("in 'libraries' option (library '%s'), " + - "'sources' must be present and must be " + - "a list of source filenames") % lib_name - sources = list (sources) - - self.announce ("building '%s' library" % lib_name) - - # First, compile the source code to object files in the library - # directory. (This should probably change to putting object - # files in a temporary build directory.) - macros = build_info.get ('macros') - include_dirs = build_info.get ('include_dirs') - objects = self.compiler.compile (sources, - output_dir=self.build_temp, - keep_dir=1, - macros=macros, - include_dirs=include_dirs, - debug=self.debug) - - # Now "link" the object files together into a static library. - # (On Unix at least, this isn't really linking -- it just - # builds an archive. Whatever.) - self.compiler.link_static_lib (objects, lib_name, - output_dir=self.build_clib, - debug=self.debug) - - # for libraries - - # build_libraries () - -# class BuildLib -- cgit v1.2.1 From e5881f84002247373f54ef2108e050b03f30fcf2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 3 Mar 2000 03:00:02 +0000 Subject: Fixed 'mkpath()' to accept empty string silently (it's just the current dir). Fixed all DistutilsFileError messages to wrap file/dir names in quotes. --- util.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/util.py b/util.py index 85f3a345..b20f5be9 100644 --- a/util.py +++ b/util.py @@ -39,7 +39,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): name = os.path.normpath (name) - if os.path.isdir (name): + if os.path.isdir (name) or name == '': return if PATH_CREATED.get (name): return @@ -71,7 +71,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): try: os.mkdir (head) except os.error, (errno, errstr): - raise DistutilsFileError, "%s: %s" % (head, errstr) + raise DistutilsFileError, "'%s': %s" % (head, errstr) PATH_CREATED[head] = 1 @@ -197,19 +197,21 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): try: fsrc = open(src, 'rb') except os.error, (errno, errstr): - raise DistutilsFileError, "could not open %s: %s" % (src, errstr) + raise DistutilsFileError, \ + "could not open '%s': %s" % (src, errstr) try: fdst = open(dst, 'wb') except os.error, (errno, errstr): - raise DistutilsFileError, "could not create %s: %s" % (dst, errstr) + raise DistutilsFileError, \ + "could not create '%s': %s" % (dst, errstr) while 1: try: buf = fsrc.read (buffer_size) except os.error, (errno, errstr): raise DistutilsFileError, \ - "could not read from %s: %s" % (src, errstr) + "could not read from '%s': %s" % (src, errstr) if not buf: break @@ -218,7 +220,7 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): fdst.write(buf) except os.error, (errno, errstr): raise DistutilsFileError, \ - "could not write to %s: %s" % (dst, errstr) + "could not write to '%s': %s" % (dst, errstr) finally: if fdst: @@ -258,7 +260,7 @@ def copy_file (src, dst, if not os.path.isfile (src): raise DistutilsFileError, \ - "can't copy %s: not a regular file" % src + "can't copy '%s': not a regular file" % src if os.path.isdir (dst): dir = dst @@ -321,7 +323,7 @@ def copy_tree (src, dst, if not dry_run and not os.path.isdir (src): raise DistutilsFileError, \ - "cannot copy tree %s: not a directory" % src + "cannot copy tree '%s': not a directory" % src try: names = os.listdir (src) except os.error, (errno, errstr): @@ -329,7 +331,7 @@ def copy_tree (src, dst, names = [] else: raise DistutilsFileError, \ - "error listing files in %s: %s" % (src, errstr) + "error listing files in '%s': %s" % (src, errstr) if not dry_run: mkpath (dst, verbose=verbose) -- cgit v1.2.1 From bea7ea527619fd6e4360157daea66bb5f7bf946e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 3 Mar 2000 03:00:27 +0000 Subject: Typo fix. --- command/build_py.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_py.py b/command/build_py.py index 2d0ad1ce..4e5255a6 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -146,7 +146,7 @@ class build_py (Command): raise DistutilsFileError, \ "package directory '%s' does not exist" % package_dir if not os.path.isdir (package_dir): - raise DistutilsFileErorr, \ + raise DistutilsFileError, \ ("supposed package directory '%s' exists, " + "but is not a directory") % package_dir -- cgit v1.2.1 From 0b68db614df691a1089dc6c6a290bf0d8b81f424 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 6 Mar 2000 03:36:50 +0000 Subject: Don't pass 'keep_dir' to 'compile()' method of CCompiler -- no longer used. Don't assume that the 'libraries' and 'library_dirs' elements of the build info dict are always lists. --- command/build_ext.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index bbdf062b..050a5c65 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -245,7 +245,6 @@ class build_ext (Command): include_dirs = build_info.get ('include_dirs') objects = self.compiler.compile (sources, output_dir=self.build_temp, - keep_dir=1, macros=macros, include_dirs=include_dirs, debug=self.debug) @@ -256,8 +255,10 @@ class build_ext (Command): extra_objects = build_info.get ('extra_objects') if extra_objects: objects.extend (extra_objects) - libraries = self.libraries + build_info.get ('libraries') - library_dirs = self.library_dirs + build_info.get ('library_dirs') + libraries = (self.libraries + + (build_info.get ('libraries') or [])) + library_dirs = (self.library_dirs + + (build_info.get ('library_dirs') or [])) extra_args = build_info.get ('extra_link_args') or [] if self.compiler.compiler_type == 'msvc': -- cgit v1.2.1 From 4ffaf24264f6c274ece3c928bdcf717fb12543cd Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 6 Mar 2000 03:37:45 +0000 Subject: Don't pass 'keep_dir' to 'compile()' method of CCompiler -- no longer used. --- command/build_clib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/command/build_clib.py b/command/build_clib.py index 7a6ef7a8..c49e3ca7 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -183,7 +183,6 @@ class build_clib (Command): include_dirs = build_info.get ('include_dirs') objects = self.compiler.compile (sources, output_dir=self.build_temp, - keep_dir=1, macros=macros, include_dirs=include_dirs, debug=self.debug) -- cgit v1.2.1 From 5696561cb0643ab61ee1e719217ba98f5029f674 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 6 Mar 2000 03:40:29 +0000 Subject: Serious overhaul of the C compiler interface and the two classes that implement it (so far): * moved filename generation methods into CCompiler base class, driven by data supplied by implementation classes * moved a bunch of common code from UnixCCompiler to convenience methods in CCompiler * overhauled MSVCCompiler's compile/link methods to look and act as much as possible like UnixCCompiler's, in order to regularize both interface and behaviour (especially by using those new convenience methods) --- ccompiler.py | 290 ++++++++++++++++++++++++++++++++++++++++++++++--------- msvccompiler.py | 235 ++++++++++++++++++++------------------------ unixccompiler.py | 237 ++++++++++----------------------------------- 3 files changed, 400 insertions(+), 362 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 4819b23c..2336e965 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -12,7 +12,7 @@ from types import * from copy import copy from distutils.errors import * from distutils.spawn import spawn -from distutils.util import move_file, mkpath +from distutils.util import move_file, mkpath, newer_pairwise, newer_group class CCompiler: @@ -65,6 +65,18 @@ class CCompiler: # library search path anyways. + # Subclasses that rely on the standard filename generation methods + # implemented below should override these; see the comment near + # those methods ('object_filenames()' et. al.) for details: + src_extensions = None # list of strings + obj_extension = None # string + static_lib_extension = None + shared_lib_extension = None # string + static_lib_format = None # format string + shared_lib_format = None # prob. same as static_lib_format + exe_extension = None # string + + def __init__ (self, verbose=0, dry_run=0, @@ -255,6 +267,138 @@ class CCompiler: self.objects = copy (objects) + # -- Priviate utility methods -------------------------------------- + # (here for the convenience of subclasses) + + def _fix_compile_args (self, output_dir, macros, include_dirs): + """Typecheck and fix-up some of the arguments to the 'compile()' method, + and return fixed-up values. Specifically: if 'output_dir' is + None, replaces it with 'self.output_dir'; ensures that 'macros' + is a list, and augments it with 'self.macros'; ensures that + 'include_dirs' is a list, and augments it with + 'self.include_dirs'. Guarantees that the returned values are of + the correct type, i.e. for 'output_dir' either string or None, + and for 'macros' and 'include_dirs' either list or None.""" + + if output_dir is None: + output_dir = self.output_dir + elif type (output_dir) is not StringType: + raise TypeError, "'output_dir' must be a string or None" + + if macros is None: + macros = self.macros + elif type (macros) is ListType: + macros = macros + (self.macros or []) + else: + raise TypeError, \ + "'macros' (if supplied) must be a list of tuples" + + if include_dirs is None: + include_dirs = self.include_dirs + elif type (include_dirs) in (ListType, TupleType): + include_dirs = list (include_dirs) + (self.include_dirs or []) + else: + raise TypeError, \ + "'include_dirs' (if supplied) must be a list of strings" + + return (output_dir, macros, include_dirs) + + # _fix_compile_args () + + + def _prep_compile (self, sources, output_dir): + """Determine the list of object files corresponding to 'sources', and + figure out which ones really need to be recompiled. Return a list + of all object files and a dictionary telling which source files can + be skipped.""" + + # Get the list of expected output (object) files + objects = self.object_filenames (sources, + output_dir=output_dir) + + if self.force: + skip_source = {} # rebuild everything + for source in sources: + skip_source[source] = 0 + else: + # Figure out which source files we have to recompile according + # to a simplistic check -- we just compare the source and + # object file, no deep dependency checking involving header + # files. + skip_source = {} # rebuild everything + for source in sources: # no wait, rebuild nothing + skip_source[source] = 1 + + (n_sources, n_objects) = newer_pairwise (sources, objects) + for source in n_sources: # no really, only rebuild what's out-of-date + skip_source[source] = 0 + + return (objects, skip_source) + + # _prep_compile () + + + def _fix_link_args (self, objects, output_dir, + takes_libs=0, libraries=None, library_dirs=None): + """Typecheck and fix up some of the arguments supplied to the + 'link_*' methods and return the fixed values. Specifically: + ensure that 'objects' is a list; if output_dir is None, use + self.output_dir; ensure that 'libraries' and 'library_dirs' are + both lists, and augment them with 'self.libraries' and + 'self.library_dirs'. If 'takes_libs' is true, return a tuple + (objects, output_dir, libraries, library_dirs; else return + (objects, output_dir).""" + + if type (objects) not in (ListType, TupleType): + raise TypeError, \ + "'objects' must be a list or tuple of strings" + objects = list (objects) + + if output_dir is None: + output_dir = self.output_dir + elif type (output_dir) is not StringType: + raise TypeError, "'output_dir' must be a string or None" + + if takes_libs: + if libraries is None: + libraries = self.libraries + elif type (libraries) in (ListType, TupleType): + libraries = list (libraries) + (self.libraries or []) + else: + raise TypeError, \ + "'libraries' (if supplied) must be a list of strings" + + if library_dirs is None: + library_dirs = self.library_dirs + elif type (library_dirs) in (ListType, TupleType): + library_dirs = list (library_dirs) + (self.library_dirs or []) + else: + raise TypeError, \ + "'library_dirs' (if supplied) must be a list of strings" + + return (objects, output_dir, libraries, library_dirs) + else: + return (objects, output_dir) + + # _fix_link_args () + + + def _need_link (self, objects, output_file): + """Return true if we need to relink the files listed in 'objects' to + recreate 'output_file'.""" + + if self.force: + return 1 + else: + if self.dry_run: + newer = newer_group (objects, output_file, missing='newer') + else: + newer = newer_group (objects, output_file) + return newer + + # _need_link () + + # -- Worker methods ------------------------------------------------ # (must be implemented by subclasses) @@ -268,8 +412,16 @@ class CCompiler: extra_postargs=None): """Compile one or more C/C++ source files. 'sources' must be a list of strings, each one the name of a C/C++ source - file. Return a list of the object filenames generated - (one for each source filename in 'sources'). + file. Return a list of object filenames, one per source + filename in 'sources'. Depending on the implementation, + not all source files will necessarily be compiled, but + all corresponding object filenames will be returned. + + If 'output_dir' is given, object files will be put under it, + while retaining their original path component. That is, + "foo/bar.c" normally compiles to "foo/bar.o" (for a Unix + implementation); if 'output_dir' is "build", then it would + compile to "build/foo/bar.o". 'macros', if given, must be a list of macro definitions. A macro definition is either a (name, value) 2-tuple or a (name,) @@ -285,11 +437,12 @@ class CCompiler: 'debug' is a boolean; if true, the compiler will be instructed to output debug symbols in (or alongside) the object file(s). - 'extra_preargs' and 'extra_postargs' are optional lists of extra - command-line arguments that will be, respectively, prepended or - appended to the generated command line immediately before - execution. These will most likely be peculiar to the particular - platform and compiler being worked with, but are a necessary + 'extra_preargs' and 'extra_postargs' are implementation- + dependent. On platforms that have the notion of a command-line + (e.g. Unix, DOS/Windows), they are most likely lists of strings: + extra command-line arguments to prepand/append to the compiler + command line. On other platforms, consult the implementation + class documentation. In any event, they are intended as an escape hatch for those occasions when the abstract compiler framework doesn't cut the mustard.""" @@ -398,45 +551,88 @@ class CCompiler: - # -- Filename mangling methods ------------------------------------- - - # General principle for the filename-mangling methods: by default, - # don't include a directory component, no matter what the caller - # supplies. Eg. for UnixCCompiler, a source file of "foo/bar/baz.c" - # becomes "baz.o" or "baz.so", etc. (That way, it's easiest for the - # caller to decide where it wants to put/find the output file.) The - # 'output_dir' parameter overrides this, of course -- the directory - # component of the input filenames is replaced by 'output_dir'. - - def object_filenames (self, source_filenames, output_dir=None): - """Return the list of object filenames corresponding to each - specified source filename.""" - pass - - def shared_object_filename (self, source_filename): - """Return the shared object filename corresponding to a - specified source filename (assuming the same directory).""" - pass - - def library_filename (self, libname): - """Return the static library filename corresponding to the - specified library name.""" - - pass - - def shared_library_filename (self, libname): - """Return the shared library filename corresponding to the - specified library name.""" - pass - - # XXX ugh -- these should go! - def object_name (self, inname): - """Given a name with no extension, return the name + object extension""" - return inname + self._obj_ext + # -- Filename generation methods ----------------------------------- + + # The default implementation of the filename generating methods are + # prejudiced towards the Unix/DOS/Windows view of the world: + # * object files are named by replacing the source file extension + # (eg. .c/.cpp -> .o/.obj) + # * library files (shared or static) are named by plugging the + # library name and extension into a format string, eg. + # "lib%s.%s" % (lib_name, ".a") for Unix static libraries + # * executables are named by appending an extension (possibly + # empty) to the program name: eg. progname + ".exe" for + # Windows + # + # To reduce redundant code, these methods expect to find + # several attributes in the current object (presumably defined + # as class attributes): + # * src_extensions - + # list of C/C++ source file extensions, eg. ['.c', '.cpp'] + # * obj_extension - + # object file extension, eg. '.o' or '.obj' + # * static_lib_extension - + # extension for static library files, eg. '.a' or '.lib' + # * shared_lib_extension - + # extension for shared library/object files, eg. '.so', '.dll' + # * static_lib_format - + # format string for generating static library filenames, + # eg. 'lib%s.%s' or '%s.%s' + # * shared_lib_format + # format string for generating shared library filenames + # (probably same as static_lib_format, since the extension + # is one of the intended parameters to the format string) + # * exe_extension - + # extension for executable files, eg. '' or '.exe' + + def object_filenames (self, + source_filenames, + strip_dir=0, + output_dir=''): + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext (src_name) + if ext not in self.src_extensions: + continue + if strip_dir: + base = os.path.basename (base) + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + # object_filenames () + + + def shared_object_filename (self, + basename, + strip_dir=0, + output_dir=''): + if output_dir is None: output_dir = '' + if strip_dir: + basename = os.path.basename (basename) + return os.path.join (output_dir, basename + self.shared_lib_extension) + + + def library_filename (self, + libname, + lib_type='static', # or 'shared' + strip_dir=0, + output_dir=''): + + if output_dir is None: output_dir = '' + if lib_type not in ("static","shared"): + raise ValueError, "'lib_type' must be \"static\" or \"shared\"" + fmt = getattr (self, lib_type + "_lib_format") + ext = getattr (self, lib_type + "_lib_extension") + + (dir, base) = os.path.split (libname) + filename = fmt % (base, ext) + if strip_dir: + dir = '' + + return os.path.join (output_dir, dir, filename) - def shared_library_name (self, inname): - """Given a name with no extension, return the name + shared object extension""" - return inname + self._shared_lib_ext # -- Utility methods ----------------------------------------------- @@ -606,4 +802,4 @@ def gen_lib_options (compiler, library_dirs, libraries): return lib_opts -# _gen_lib_options () +# gen_lib_options () diff --git a/msvccompiler.py b/msvccompiler.py index 2dd8dc10..847c611d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -5,12 +5,13 @@ for the Microsoft Visual Studio.""" # created 1999/08/19, Perry Stoll -# +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) + __revision__ = "$Id$" -import os -import sys -import string +import sys, os, string +from types import * from distutils.errors import * from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options @@ -137,6 +138,20 @@ class MSVCCompiler (CCompiler) : compiler_type = 'msvc' + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc','.cpp'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = _c_extensions + _cpp_extensions + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + def __init__ (self, verbose=0, dry_run=0, @@ -169,9 +184,7 @@ class MSVCCompiler (CCompiler) : self.preprocess_options = None self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3' ] - self.compile_options_debug = [ - '/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG' - ] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG'] self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] self.ldflags_shared_debug = [ @@ -181,21 +194,7 @@ class MSVCCompiler (CCompiler) : # -- Worker methods ------------------------------------------------ - # (must be implemented by subclasses) - _c_extensions = [ '.c' ] - _cpp_extensions = [ '.cc', '.cpp' ] - - _obj_ext = '.obj' - _exe_ext = '.exe' - _shared_lib_ext = '.dll' - _static_lib_ext = '.lib' - - # XXX the 'output_dir' parameter is ignored by the methods in this - # class! I just put it in to be consistent with CCompiler and - # UnixCCompiler, but someone who actually knows Visual C++ will - # have to make it work... - def compile (self, sources, output_dir=None, @@ -205,48 +204,43 @@ class MSVCCompiler (CCompiler) : extra_preargs=None, extra_postargs=None): - if macros is None: - macros = [] - if include_dirs is None: - include_dirs = [] - - objectFiles = [] + (output_dir, macros, include_dirs) = \ + self._fix_compile_args (output_dir, macros, include_dirs) + (objects, skip_sources) = self._prep_compile (sources, output_dir) - base_pp_opts = \ - gen_preprocess_options (self.macros + macros, - self.include_dirs + include_dirs) - - base_pp_opts.append('/c') + if extra_postargs is None: + extra_postargs = [] + pp_opts = gen_preprocess_options (macros, include_dirs) + compile_opts = extra_preargs or [] + compile_opts.append ('/c') if debug: - compile_options = self.compile_options_debug + compile_opts.extend (self.compile_options_debug) else: - compile_options = self.compile_options + compile_opts.extend (self.compile_options) - for srcFile in sources: - base,ext = os.path.splitext(srcFile) - objFile = base + ".obj" + for i in range (len (sources)): + src = sources[i] ; obj = objects[i] + ext = (os.path.splitext (src))[1] - if ext in self._c_extensions: - fileOpt = "/Tc" - elif ext in self._cpp_extensions: - fileOpt = "/Tp" + if skip_sources[src]: + self.announce ("skipping %s (%s up-to-date)" % (src, obj)) + else: + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src - inputOpt = fileOpt + srcFile - outputOpt = "/Fo" + objFile + output_opt = "/Fo" + obj - cc_args = compile_options + \ - base_pp_opts + \ - [outputOpt, inputOpt] + self.mkpath (os.path.dirname (obj)) + self.spawn ([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) - if extra_preargs: - cc_args[:0] = extra_preargs - if extra_postargs: - cc_args.extend (extra_postargs) + return objects - self.spawn ([self.cc] + cc_args) - objectFiles.append( objFile ) - return objectFiles + # compile () # XXX the signature of this method is different from CCompiler and @@ -263,25 +257,30 @@ class MSVCCompiler (CCompiler) : extra_preargs=None, extra_postargs=None): - if libraries is None: - libraries = [] - if library_dirs is None: - library_dirs = [] + (objects, output_dir, libraries, library_dirs) = \ + self._fix_link_args (objects, output_dir, takes_libs=1, + libraries=libraries, + library_dirs=library_dirs) - lib_opts = gen_lib_options (self.libraries + libraries, - self.library_dirs + library_dirs, - "%s.lib", "/LIBPATH:%s") - - ld_args = self.ldflags_static + lib_opts + \ - objects + ['/OUT:' + output_filename] - if debug: - pass # XXX what goes here? - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend (extra_postargs) + output_filename = \ + self.library_filename (output_libname, output_dir=output_dir) + + if self._need_link (objects, output_filename): + lib_opts = gen_lib_options (libraries, library_dirs, + "%s.lib", "/LIBPATH:%s") + ld_args = self.ldflags_static + lib_opts + \ + objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend (extra_postargs) + self.spawn ([self.link] + ld_args) + else: + self.announce ("skipping %s (up-to-date)" % output_filename) - self.spawn ( [ self.link ] + ld_args ) + # link_static_lib () def link_shared_lib (self, @@ -294,8 +293,6 @@ class MSVCCompiler (CCompiler) : extra_preargs=None, extra_postargs=None): - # XXX should we sanity check the library name? (eg. no - # slashes) self.link_shared_object (objects, self.shared_library_name(output_libname), output_dir=output_dir, @@ -315,70 +312,48 @@ class MSVCCompiler (CCompiler) : debug=0, extra_preargs=None, extra_postargs=None): - """Link a bunch of stuff together to create a shared object - file. Much like 'link_shared_lib()', except the output - filename is explicitly supplied as 'output_filename'.""" - if libraries is None: - libraries = [] - if library_dirs is None: - library_dirs = [] - - lib_opts = gen_lib_options (self, - self.library_dirs + library_dirs, - self.libraries + libraries) - - if debug: - ldflags = self.ldflags_shared_debug - basename, ext = os.path.splitext (output_filename) - #XXX not sure this belongs here - # extensions in debug_mode are named 'module_d.pyd' - output_filename = basename + '_d' + ext - else: - ldflags = self.ldflags_shared - - ld_args = ldflags + lib_opts + \ - objects + ['/OUT:' + output_filename] - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend (extra_postargs) - - self.spawn ( [ self.link ] + ld_args ) - - # -- Filename mangling methods ------------------------------------- - - def _change_extensions( self, filenames, newExtension ): - object_filenames = [] - - for srcFile in filenames: - base,ext = os.path.splitext( srcFile ) - # XXX should we strip off any existing path? - object_filenames.append( base + newExtension ) - - return object_filenames + (objects, output_dir, libraries, library_dirs) = \ + self._fix_link_args (objects, output_dir, takes_libs=1, + libraries=libraries, library_dirs=library_dirs) + + lib_opts = gen_lib_options (self, library_dirs, libraries) + if type (output_dir) not in (StringType, NoneType): + raise TypeError, "'output_dir' must be a string or None" + if output_dir is not None: + output_filename = os.path.join (output_dir, output_filename) + + if self._need_link (objects, output_filename): + + if debug: + ldflags = self.ldflags_shared_debug + # XXX not sure this belongs here + # extensions in debug_mode are named 'module_d.pyd' + basename, ext = os.path.splitext (output_filename) + output_filename = basename + '_d' + ext + else: + ldflags = self.ldflags_shared + + ld_args = ldflags + lib_opts + \ + objects + ['/OUT:' + output_filename] - def object_filenames (self, source_filenames): - """Return the list of object filenames corresponding to each - specified source filename.""" - return self._change_extensions( source_filenames, self._obj_ext ) + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend (extra_postargs) - def shared_object_filename (self, source_filename): - """Return the shared object filename corresponding to a - specified source filename.""" - return self._change_extensions( source_filenames, self._shared_lib_ext ) + self.mkpath (os.path.dirname (output_filename)) + self.spawn ([self.link] + ld_args) - def library_filename (self, libname): - """Return the static library filename corresponding to the - specified library name.""" - return "%s%s" %( libname, self._static_lib_ext ) + else: + self.announce ("skipping %s (up-to-date)" % output_filename) - def shared_library_filename (self, libname): - """Return the shared library filename corresponding to the - specified library name.""" - return "%s%s" %( libname, self._shared_lib_ext ) + # link_shared_object () + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. def library_dir_option (self, dir): return "/LIBPATH:" + dir diff --git a/unixccompiler.py b/unixccompiler.py index 7765faf8..35390de0 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -20,10 +20,9 @@ __revision__ = "$Id$" import string, re, os from types import * from copy import copy -from sysconfig import \ +from distutils.sysconfig import \ CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO -from ccompiler import CCompiler, gen_preprocess_options, gen_lib_options -from util import move_file, newer_pairwise, newer_group +from distutils.ccompiler import CCompiler, gen_preprocess_options, gen_lib_options # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's @@ -55,10 +54,13 @@ class UnixCCompiler (CCompiler): compiler_type = 'unix' - _obj_ext = '.o' - _exe_ext = '' - _shared_lib_ext = SO - _static_lib_ext = '.a' + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = [".c",".C",".cc",".cxx",".cpp"] + obj_extension = ".o" + static_lib_extension = ".a" + shared_lib_extension = ".so" + static_lib_format = shared_lib_format = "lib%s%s" # Command to create a static library: seems to be pretty consistent # across the major Unices. Might have to move down into the @@ -96,66 +98,24 @@ class UnixCCompiler (CCompiler): self.ld_exec = self.cc + # __init__ () + def compile (self, sources, output_dir=None, - keep_dir=0, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): - if type (output_dir) not in (StringType, NoneType): - raise TypeError, "'output_dir' must be a string or None" - if output_dir is None: - output_dir = self.output_dir - if macros is None: - macros = [] - if include_dirs is None: - include_dirs = [] - - if type (macros) is not ListType: - raise TypeError, \ - "'macros' (if supplied) must be a list of tuples" - if type (include_dirs) not in (ListType, TupleType): - raise TypeError, \ - "'include_dirs' (if supplied) must be a list of strings" - include_dirs = list (include_dirs) - - pp_opts = gen_preprocess_options (self.macros + macros, - self.include_dirs + include_dirs) - - # So we can mangle 'sources' without hurting the caller's data - orig_sources = sources - sources = copy (sources) - - # Get the list of expected output (object) files and drop files we - # don't have to recompile. (Simplistic check -- we just compare the - # source and object file, no deep dependency checking involving - # header files. Hmmm.) - objects = self.object_filenames (sources, - output_dir=output_dir, - keep_dir=keep_dir) - all_objects = copy (objects) # preserve full list to return - - if not self.force: - skipped = newer_pairwise (sources, objects) - for skipped_pair in skipped: - self.announce ("skipping %s (%s up-to-date)" % skipped_pair) - - # Build list of (source,object) tuples for convenience - srcobj = [] - for i in range (len (sources)): - srcobj.append ((sources[i], objects[i])) + (output_dir, macros, include_dirs) = \ + self._fix_compile_args (output_dir, macros, include_dirs) + (objects, skip_sources) = self._prep_compile (sources, output_dir) - # Compile all source files that weren't eliminated by - # 'newer_pairwise()'. - # XXX use of ccflags_shared means we're blithely assuming - # that we're compiling for inclusion in a shared object! - # (will have to fix this when I add the ability to build a - # new Python) + # Figure out the options for the compiler command line. + pp_opts = gen_preprocess_options (macros, include_dirs) cc_args = ['-c'] + pp_opts + self.ccflags + self.ccflags_shared if debug: cc_args[:0] = ['-g'] @@ -164,44 +124,21 @@ class UnixCCompiler (CCompiler): if extra_postargs is None: extra_postargs = [] - if output_dir is not None: - self.mkpath (output_dir) - for (source,object) in srcobj: - self.spawn ([self.cc] + cc_args + - [source, '-o', object] + - extra_postargs) - - # Have to re-fetch list of object filenames, because we want to - # return *all* of them, including those that weren't recompiled on - # this call! - return all_objects - - - def _fix_link_args (self, output_dir, libraries, library_dirs): - """Fixes up the arguments supplied to the 'link_*' methods: - if output_dir is None, use self.output_dir; ensure that - libraries and library_dirs are both lists (could be None or - tuples on input -- both are converted to lists). Return - a tuple of the three input arguments.""" - - if output_dir is None: - output_dir = self.output_dir - if libraries is None: - libraries = [] - if library_dirs is None: - library_dirs = [] - - if type (libraries) not in (ListType, TupleType): - raise TypeError, \ - "'libraries' (if supplied) must be a list of strings" - if type (library_dirs) not in (ListType, TupleType): - raise TypeError, \ - "'library_dirs' (if supplied) must be a list of strings" - libraries = list (libraries) - library_dirs = list (library_dirs) + # Compile all source files that weren't eliminated by + # '_prep_compile()'. + for i in range (len (sources)): + src = sources[i] ; obj = objects[i] + if skip_sources[src]: + self.announce ("skipping %s (%s up-to-date)" % (src, obj)) + else: + self.mkpath (os.path.dirname (obj)) + self.spawn ([self.cc] + cc_args + [src, '-o', obj] + extra_postargs) - return (output_dir, libraries, library_dirs) + # Return *all* object filenames, not just the ones we just built. + return objects + # compile () + def link_static_lib (self, objects, @@ -209,35 +146,17 @@ class UnixCCompiler (CCompiler): output_dir=None, debug=0): - if type (objects) not in (ListType, TupleType): - raise TypeError, \ - "'objects' must be a list or tuple of strings" - objects = list (objects) - - if type (output_dir) not in (StringType, NoneType): - raise TypeError, "'output_dir' must be a string or None" - if output_dir is None: - output_dir = self.output_dir + (objects, output_dir) = self._fix_link_args (objects, output_dir, takes_libs=0) - output_filename = self.library_filename (output_libname) - if output_dir is not None: - output_filename = os.path.join (output_dir, output_filename) + output_filename = \ + self.library_filename (output_libname, output_dir=output_dir) - # Check timestamps: if any of the object files are newer than - # the library file, *or* if "force" is true, then we'll - # recreate the library. - if not self.force: - if self.dry_run: - newer = newer_group (objects, output_filename, missing='newer') - else: - newer = newer_group (objects, output_filename) - - if self.force or newer: + if self._need_link (objects, output_filename): self.mkpath (os.path.dirname (output_filename)) self.spawn ([self.archiver, self.archiver_options, output_filename] + - objects) + objects + self.objects) else: self.announce ("skipping %s (up-to-date)" % output_filename) @@ -253,11 +172,9 @@ class UnixCCompiler (CCompiler): debug=0, extra_preargs=None, extra_postargs=None): - # XXX should we sanity check the library name? (eg. no - # slashes) self.link_shared_object ( objects, - "lib%s%s" % (output_libname, self._shared_lib_ext), + self.shared_library_filename (output_libname), output_dir, libraries, library_dirs, @@ -276,30 +193,19 @@ class UnixCCompiler (CCompiler): extra_preargs=None, extra_postargs=None): - (output_dir, libraries, library_dirs) = \ - self._fix_link_args (output_dir, libraries, library_dirs) + (objects, output_dir, libraries, library_dirs) = \ + self._fix_link_args (objects, output_dir, takes_libs=1, + libraries=libraries, library_dirs=library_dirs) - lib_opts = gen_lib_options (self, - self.library_dirs + library_dirs, - self.libraries + libraries) + lib_opts = gen_lib_options (self, library_dirs, libraries) if type (output_dir) not in (StringType, NoneType): raise TypeError, "'output_dir' must be a string or None" if output_dir is not None: output_filename = os.path.join (output_dir, output_filename) - # If any of the input object files are newer than the output shared - # object, relink. Again, this is a simplistic dependency check: - # doesn't look at any of the libraries we might be linking with. - - if not self.force: - if self.dry_run: - newer = newer_group (objects, output_filename, missing='newer') - else: - newer = newer_group (objects, output_filename) - - if self.force or newer: - ld_args = self.ldflags_shared + objects + \ - lib_opts + ['-o', output_filename] + if self._need_link (objects, output_filename): + ld_args = (self.ldflags_shared + objects + self.objects + + lib_opts + ['-o', output_filename]) if debug: ld_args[:0] = ['-g'] if extra_preargs: @@ -324,25 +230,17 @@ class UnixCCompiler (CCompiler): extra_preargs=None, extra_postargs=None): - (output_dir, libraries, library_dirs) = \ - self._fix_link_args (output_dir, libraries, library_dirs) + (objects, output_dir, libraries, library_dirs) = \ + self._fix_link_args (objects, output_dir, takes_libs=1, + libraries=libraries, library_dirs=library_dirs) - lib_opts = gen_lib_options (self, - self.library_dirs + library_dirs, - self.libraries + libraries) + lib_opts = gen_lib_options (self, library_dirs, libraries) output_filename = output_progname # Unix-ism! if output_dir is not None: output_filename = os.path.join (output_dir, output_filename) - # Same ol' simplistic-but-still-useful dependency check. - if not self.force: - if self.dry_run: - newer = newer_group (objects, output_filename, missing='newer') - else: - newer = newer_group (objects, output_filename) - - if self.force or newer: - ld_args = objects + lib_opts + ['-o', output_filename] + if self._need_link (objects, output_filename): + ld_args = objects + self.objects + lib_opts + ['-o', output_filename] if debug: ld_args[:0] = ['-g'] if extra_preargs: @@ -357,41 +255,10 @@ class UnixCCompiler (CCompiler): # link_executable () - # -- Filename-mangling (etc.) methods ------------------------------ - - def object_filenames (self, source_filenames, - keep_dir=0, output_dir=None): - outnames = [] - for inname in source_filenames: - outname = re.sub (r'\.(c|C|cc|cxx|cpp)$', self._obj_ext, inname) - if not keep_dir: - outname = os.path.basename (outname) - if output_dir is not None: - outname = os.path.join (output_dir, outname) - outnames.append (outname) - return outnames - - def shared_object_filename (self, source_filename, - keep_dir=0, output_dir=None): - outname = re.sub (r'\.(c|C|cc|cxx|cpp)$', self._shared_lib_ext) - if not keep_dir: - outname = os.path.basename (outname) - if output_dir is not None: - outname = os.path.join (output_dir, outname) - return outname - - - def library_filename (self, libname): - (dirname, basename) = os.path.split (libname) - return os.path.join (dirname, - "lib%s%s" % (basename, self._static_lib_ext)) - - def shared_library_filename (self, libname): - (dirname, basename) = os.path.split (libname) - return os.path.join (dirname, - "lib%s%s" % (basename, self._shared_lib_ext)) - - + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + def library_dir_option (self, dir): return "-L" + dir -- cgit v1.2.1 From 5cf4767279d301842dc5fef525414412320ca947 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 6 Mar 2000 03:44:32 +0000 Subject: Rewrote 'newer_pairwise(): more natural (and incompatible) interface, simpler implementation. --- util.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/util.py b/util.py index b20f5be9..03c0c88c 100644 --- a/util.py +++ b/util.py @@ -100,22 +100,23 @@ def newer (source, target): def newer_pairwise (sources, targets): - """Walk two filename lists in parallel, testing if each 'target' is - up-to-date relative to its corresponding 'source'. If so, both - are deleted from their respective lists. Return a list of tuples - containing the deleted (source,target) pairs.""" + """Walk two filename lists in parallel, testing if each source is newer + than its corresponding target. Return a pair of lists (sources, + targets) where source is newer than target, according to the + semantics of 'newer()'.""" if len (sources) != len (targets): raise ValueError, "'sources' and 'targets' must be same length" - goners = [] - for i in range (len (sources)-1, -1, -1): - if not newer (sources[i], targets[i]): - goners.append ((sources[i], targets[i])) - del sources[i] - del targets[i] - goners.reverse() - return goners + # build a pair of lists (sources, targets) where source is newer + n_sources = [] + n_targets = [] + for i in range (len (sources)): + if newer (sources[i], targets[i]): + n_sources.append (sources[i]) + n_targets.append (targets[i]) + + return (n_sources, n_targets) # newer_pairwise () -- cgit v1.2.1 From 7155260372c2a9d8cecb81dd908df0adcafd8d01 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 7 Mar 2000 03:25:20 +0000 Subject: Added '_nt_quote_args()' to deal with whitespace in command-line arguments in a rather half-assed, but probably effective, way. --- spawn.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/spawn.py b/spawn.py index 847346cc..ae3d09fa 100644 --- a/spawn.py +++ b/spawn.py @@ -42,11 +42,28 @@ def spawn (cmd, # spawn () -def _spawn_nt ( cmd, - search_path=1, - verbose=0, - dry_run=0): +def _nt_quote_args (args): + """Obscure quoting command line arguments on NT. + Simply quote every argument which contains blanks.""" + + # XXX this doesn't seem very robust to me -- but if the Windows guys + # say it'll work, I guess I'll have to accept it. (What if an arg + # contains quotes? What other magic characters, other than spaces, + # have to be escaped? Is there an escaping mechanism other than + # quoting?) + + for i in range (len (args)): + if string.find (args[i], ' ') == -1: + args[i] = '"%s"' % args[i] + + +def _spawn_nt (cmd, + search_path=1, + verbose=0, + dry_run=0): + executable = cmd[0] + cmd = _nt_quote_args (cmd) if search_path: paths = string.split( os.environ['PATH'], os.pathsep) base,ext = os.path.splitext(executable) @@ -60,7 +77,7 @@ def _spawn_nt ( cmd, # the file exists, we have a shot at spawn working executable = f if verbose: - print string.join ( [executable] + cmd[1:], ' ') + print string.join ([executable] + cmd[1:], ' ') if not dry_run: # spawn for NT requires a full path to the .exe try: -- cgit v1.2.1 From 0e7330777448758b059ab24d6db4b527c0269001 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 7 Mar 2000 03:27:08 +0000 Subject: Added 'native_path()' for use on pathnames from the setup script: split on slashes, and put back together again using the local directory separator. --- util.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/util.py b/util.py index 03c0c88c..683d1676 100644 --- a/util.py +++ b/util.py @@ -458,3 +458,31 @@ def get_platform (): return sys.platform # get_platform() + + +def native_path (pathname): + """Return 'pathname' as a name that will work on the native + filesystem, i.e. split it on '/' and put it back together again + using the current directory separator. Needed because filenames in + the setup script are always supplied in Unix style, and have to be + converted to the local convention before we can actually use them in + the filesystem. Raises DistutilsValueError if 'pathname' is + absolute (starts with '/') or contains local directory separators + (unless the local separator is '/', of course).""" + + if pathname[0] == '/': + raise DistutilsValueError, "path '%s' cannot be absolute" % pathname + if pathname[-1] == '/': + raise DistutilsValueError, "path '%s' cannot end with '/'" % pathname + if os.sep != '/': + if os.sep in pathname: + raise DistutilsValueError, \ + "path '%s' cannot contain '%c' character" % \ + (pathname, os.sep) + + paths = string.split (pathname, '/') + return apply (os.path.join, paths) + else: + return pathname + +# native_path () -- cgit v1.2.1 From 01efeb23f10c8b27f9d961264d930d5e3f187248 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 7 Mar 2000 03:30:09 +0000 Subject: Patch from Corran Webster : add '_init_mac()'. --- sysconfig.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index e291aec2..2107ffe4 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -153,6 +153,27 @@ def _init_nt(): g['exec_prefix'] = exec_prefix +def _init_mac(): + """Initialize the module as appropriate for Macintosh systems""" + g = globals() + # load the installed config.h (what if not installed? - still need to + # be able to install packages which don't require compilation) + parse_config_h(open( + os.path.join(sys.exec_prefix, "Mac", "Include", "config.h")), g) + # set basic install directories + g['LIBDEST']=os.path.join(sys.exec_prefix, "Lib") + g['BINLIBDEST']= os.path.join(sys.exec_prefix, "Mac", "Plugins") + + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = os.path.join (sys.prefix, 'Include' ) + + g['SO'] = '.ppc.slb' + g['exec_prefix'] = sys.exec_prefix + print sys.prefix + g['install_lib'] = os.path.join(sys.exec_prefix, "Lib") + g['install_platlib'] = os.path.join(sys.exec_prefix, "Mac", "Lib") + + try: exec "_init_" + os.name except NameError: @@ -164,3 +185,4 @@ else: del _init_posix del _init_nt +del _init_mac -- cgit v1.2.1 From 922f2b62bed5285c5a0d6e6d42f77fe7086e303e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 7 Mar 2000 03:34:16 +0000 Subject: Patch from Corran Webster (tweaked for style by me): changed 'copy_file()' to use the native Mac file copy routine. --- util.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/util.py b/util.py index 683d1676..2f193fb4 100644 --- a/util.py +++ b/util.py @@ -280,6 +280,17 @@ def copy_file (src, dst, if dry_run: return 1 + # On a Mac, use the native file copy routine + if os.name == 'mac': + import macostools + try: + macostools.copy (src, dst, 0, preserve_times) + except OSError, exc: + raise DistutilsFileError, \ + "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) + return 1 + + # Otherwise use custom routine _copy_file_contents (src, dst) if preserve_mode or preserve_times: st = os.stat (src) -- cgit v1.2.1 From 86ff65237970cb480db4f9f909902892973fdf02 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 9 Mar 2000 03:16:05 +0000 Subject: Added Joe Van Andel's 'get_python_inc()', adapted by me to supply the platform-neutral include dir by default and with Mac support. Added 'get_python_lib()', inspired by 'get_python_inc()'. Rewrote 'get_config_h_filename()' and 'get_makefile_filename()' in terms of 'get_python_inc()' and 'get_python_lib()'. Changed '_init_nt()' and '_init_mac()' to use 'get_python_inc()' and 'get_python_lib()' for directory names. --- sysconfig.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 2107ffe4..49e58eb9 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -17,20 +17,83 @@ prefix = os.path.normpath (sys.prefix) exec_prefix = os.path.normpath (sys.exec_prefix) +def get_python_inc (plat_specific=0): + """Return the directory containing installed Python header files. + If 'plat_specific' is false (the default), this is the path to the + non-platform-specific header files, i.e. Python.h and so on; + otherwise, this is the path to platform-specific header files + (namely config.h).""" + + the_prefix = (plat_specific and exec_prefix or prefix) + if os.name == "posix": + return os.path.join (the_prefix, "include", "python" + sys.version[:3]) + elif os.name == "nt": + return os.path.join (the_prefix, "Include") # include or Include? + elif os.name == "mac": + return os.path.join (the_prefix, "Include") + else: + raise DistutilsPlatformError, \ + ("I don't know where Python installs its C header files " + + "on platform '%s'") % os.name + + +def get_python_lib (plat_specific=0, standard_lib=0): + """Return the directory containing the Python library (standard or + site additions). If 'plat_specific' is true, return the directory + containing platform-specific modules, i.e. any module from a + non-pure-Python module distribution; otherwise, return the + platform-shared library directory. If 'standard_lib' is true, + return the directory containing standard Python library modules; + otherwise, return the directory for site-specific modules.""" + + the_prefix = (plat_specific and exec_prefix or prefix) + + if os.name == "posix": + libpython = os.path.join (the_prefix, + "lib", "python" + sys.version[:3]) + if standard_lib: + return libpython + else: + return os.path.join (libpython, "site-packages") + + elif os.name == "nt": + if standard_lib: + return os.path.join (the_prefix, "Lib") + else: + return the_prefix + + elif os.name == "mac": + if platform_specific: + if standard_lib: + return os.path.join (exec_prefix, "Mac", "Plugins") + else: + raise DistutilsPlatformError, \ + "OK, where DO site-specific extensions go on the Mac?" + else: + if standard_lib: + return os.path.join (prefix, "Lib") + else: + raise DistutilsPlatformError, \ + "OK, where DO site-specific modules go on the Mac?" + else: + raise DistutilsPlatformError, \ + ("I don't know where Python installs its library " + + "on platform '%s'") % os.name + +# get_python_lib () + + def get_config_h_filename(): """Return full pathname of installed config.h file.""" - if os.name == "nt": - return os.path.join(exec_prefix, "include", "config.h") - else: - return os.path.join(exec_prefix, - "include", "python" + sys.version[:3], - "config.h") + inc_dir = get_python_inc (plat_specific=1) + return os.path.join (inc_dir, "config.h") + def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" - return os.path.join(exec_prefix, - "lib", "python" + sys.version[:3], - "config", "Makefile") + lib_dir = get_python_lib (plat_specific=1, standard_lib=1) + return os.path.join (lib_dir, "config", "Makefile") + def parse_config_h(fp, g=None): """Parse a config.h-style file. A dictionary containing name/value @@ -143,11 +206,11 @@ def _init_nt(): # load config.h, though I don't know how useful this is parse_config_h(open(get_config_h_filename()), g) # set basic install directories - g['LIBDEST'] = os.path.join(exec_prefix, "Lib") - g['BINLIBDEST'] = os.path.join(exec_prefix, "Lib") + g['LIBDEST'] = get_python_lib (plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib (plat_specific=1, standard_lib=1) # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = os.path.join(prefix, 'include') + g['INCLUDEPY'] = get_python_inc (plat_specific=0) g['SO'] = '.pyd' g['exec_prefix'] = exec_prefix @@ -161,15 +224,17 @@ def _init_mac(): parse_config_h(open( os.path.join(sys.exec_prefix, "Mac", "Include", "config.h")), g) # set basic install directories - g['LIBDEST']=os.path.join(sys.exec_prefix, "Lib") - g['BINLIBDEST']= os.path.join(sys.exec_prefix, "Mac", "Plugins") + g['LIBDEST'] = get_python_lib (plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib (plat_specific=1, standard_lib=1) # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = os.path.join (sys.prefix, 'Include' ) + g['INCLUDEPY'] = get_python_inc (plat_specific=0) g['SO'] = '.ppc.slb' g['exec_prefix'] = sys.exec_prefix print sys.prefix + + # XXX are these used anywhere? g['install_lib'] = os.path.join(sys.exec_prefix, "Lib") g['install_platlib'] = os.path.join(sys.exec_prefix, "Mac", "Lib") -- cgit v1.2.1 From 13a17ccd50ded25c15ce6065dd516d85fb52bd7b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 9 Mar 2000 15:54:52 +0000 Subject: There are a few places which can raise DistutilsPlatformError; make sure it's imported! ;) Re-wrap the docstrings on get_python_inc() and get_python_lib() to be closer to the "normal" Python style. See GvR's "style guide" on the essays page (http://www.python.org/doc/essays/). There should never be a space between a function name and the '(' that opens the argument list (see the style guide again). --- sysconfig.py | 95 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 54 insertions(+), 41 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 49e58eb9..2c7318c0 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -13,65 +13,73 @@ import re import string import sys -prefix = os.path.normpath (sys.prefix) -exec_prefix = os.path.normpath (sys.exec_prefix) +from errors import DistutilsPlatformError -def get_python_inc (plat_specific=0): +prefix = os.path.normpath(sys.prefix) +exec_prefix = os.path.normpath(sys.exec_prefix) + + +def get_python_inc(plat_specific=0): """Return the directory containing installed Python header files. - If 'plat_specific' is false (the default), this is the path to the - non-platform-specific header files, i.e. Python.h and so on; - otherwise, this is the path to platform-specific header files - (namely config.h).""" - + + If 'plat_specific' is false (the default), this is the path to the + non-platform-specific header files, i.e. Python.h and so on; + otherwise, this is the path to platform-specific header files + (namely config.h). + + """ the_prefix = (plat_specific and exec_prefix or prefix) if os.name == "posix": - return os.path.join (the_prefix, "include", "python" + sys.version[:3]) + return os.path.join(the_prefix, "include", "python" + sys.version[:3]) elif os.name == "nt": - return os.path.join (the_prefix, "Include") # include or Include? + return os.path.join(the_prefix, "Include") # include or Include? elif os.name == "mac": - return os.path.join (the_prefix, "Include") + return os.path.join(the_prefix, "Include") else: raise DistutilsPlatformError, \ ("I don't know where Python installs its C header files " + "on platform '%s'") % os.name -def get_python_lib (plat_specific=0, standard_lib=0): +def get_python_lib(plat_specific=0, standard_lib=0): """Return the directory containing the Python library (standard or - site additions). If 'plat_specific' is true, return the directory - containing platform-specific modules, i.e. any module from a - non-pure-Python module distribution; otherwise, return the - platform-shared library directory. If 'standard_lib' is true, - return the directory containing standard Python library modules; - otherwise, return the directory for site-specific modules.""" + site additions). + + If 'plat_specific' is true, return the directory containing + platform-specific modules, i.e. any module from a non-pure-Python + module distribution; otherwise, return the platform-shared library + directory. If 'standard_lib' is true, return the directory + containing standard Python library modules; otherwise, return the + directory for site-specific modules. + """ the_prefix = (plat_specific and exec_prefix or prefix) if os.name == "posix": - libpython = os.path.join (the_prefix, - "lib", "python" + sys.version[:3]) + libpython = os.path.join(the_prefix, + "lib", "python" + sys.version[:3]) if standard_lib: return libpython else: - return os.path.join (libpython, "site-packages") + return os.path.join(libpython, "site-packages") elif os.name == "nt": if standard_lib: - return os.path.join (the_prefix, "Lib") + return os.path.join(the_prefix, "Lib") else: return the_prefix elif os.name == "mac": if platform_specific: if standard_lib: - return os.path.join (exec_prefix, "Mac", "Plugins") + return os.path.join(exec_prefix, "Mac", "Plugins") else: raise DistutilsPlatformError, \ "OK, where DO site-specific extensions go on the Mac?" else: if standard_lib: - return os.path.join (prefix, "Lib") + return os.path.join(prefix, "Lib") else: raise DistutilsPlatformError, \ "OK, where DO site-specific modules go on the Mac?" @@ -80,25 +88,27 @@ def get_python_lib (plat_specific=0, standard_lib=0): ("I don't know where Python installs its library " + "on platform '%s'") % os.name -# get_python_lib () +# get_python_lib() def get_config_h_filename(): """Return full pathname of installed config.h file.""" - inc_dir = get_python_inc (plat_specific=1) - return os.path.join (inc_dir, "config.h") + inc_dir = get_python_inc(plat_specific=1) + return os.path.join(inc_dir, "config.h") def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" - lib_dir = get_python_lib (plat_specific=1, standard_lib=1) - return os.path.join (lib_dir, "config", "Makefile") + lib_dir = get_python_lib(plat_specific=1, standard_lib=1) + return os.path.join(lib_dir, "config", "Makefile") def parse_config_h(fp, g=None): - """Parse a config.h-style file. A dictionary containing name/value - pairs is returned. If an optional dictionary is passed in as the second - argument, it is used instead of a new dictionary. + """Parse a config.h-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. """ if g is None: g = {} @@ -122,9 +132,12 @@ def parse_config_h(fp, g=None): return g def parse_makefile(fp, g=None): - """Parse a Makefile-style file. A dictionary containing name/value - pairs is returned. If an optional dictionary is passed in as the second - argument, it is used instead of a new dictionary. + """Parse a Makefile-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ if g is None: g = {} @@ -206,11 +219,11 @@ def _init_nt(): # load config.h, though I don't know how useful this is parse_config_h(open(get_config_h_filename()), g) # set basic install directories - g['LIBDEST'] = get_python_lib (plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib (plat_specific=1, standard_lib=1) + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc (plat_specific=0) + g['INCLUDEPY'] = get_python_inc(plat_specific=0) g['SO'] = '.pyd' g['exec_prefix'] = exec_prefix @@ -224,11 +237,11 @@ def _init_mac(): parse_config_h(open( os.path.join(sys.exec_prefix, "Mac", "Include", "config.h")), g) # set basic install directories - g['LIBDEST'] = get_python_lib (plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib (plat_specific=1, standard_lib=1) + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc (plat_specific=0) + g['INCLUDEPY'] = get_python_inc(plat_specific=0) g['SO'] = '.ppc.slb' g['exec_prefix'] = sys.exec_prefix -- cgit v1.2.1 From 970b9e96ac35abc8a46525a25c5d0e7dd5c2c53a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 10 Mar 2000 01:48:32 +0000 Subject: Renamed 'link_static_lib() to 'create_static_lib()'. --- ccompiler.py | 25 +++++++++++++------------ unixccompiler.py | 12 ++++++------ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 2336e965..46dabade 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -449,11 +449,11 @@ class CCompiler: pass - def link_static_lib (self, - objects, - output_libname, - output_dir=None, - debug=0): + def create_static_lib (self, + objects, + output_libname, + output_dir=None, + debug=0): """Link a bunch of stuff together to create a static library file. The "bunch of stuff" consists of the list of object files supplied as 'objects', the extra object files supplied @@ -484,10 +484,11 @@ class CCompiler: extra_preargs=None, extra_postargs=None): """Link a bunch of stuff together to create a shared library - file. Has the same effect as 'link_static_lib()' except - that the filename inferred from 'output_libname' will most - likely be different, and the type of file generated will - almost certainly be different + file. Similar semantics to 'create_static_lib()', with the + addition of other libraries to link against and directories to + search for them. Also, of course, the type and name of + the generated file will almost certainly be different, as will + the program used to create it. 'libraries' is a list of libraries to link against. These are library names, not filenames, since they're translated into @@ -503,9 +504,9 @@ class CCompiler: default and those supplied to 'add_library_dir()' and/or 'set_library_dirs()'. - 'debug' is as for 'compile()' and 'link_static_lib()', with the + 'debug' is as for 'compile()' and 'create_static_lib()', with the slight distinction that it actually matters on most platforms - (as opposed to 'link_static_lib()', which includes a 'debug' + (as opposed to 'create_static_lib()', which includes a 'debug' flag mostly for form's sake). 'extra_preargs' and 'extra_postargs' are as for 'compile()' @@ -543,7 +544,7 @@ class CCompiler: extra_preargs=None, extra_postargs=None): """Link a bunch of stuff together to create a binary executable - file. The "bunch of stuff" is as for 'link_static_lib()'. + file. The "bunch of stuff" is as for 'link_shared_lib()'. 'output_progname' should be the base name of the executable program--e.g. on Unix the same as the output filename, but on DOS/Windows ".exe" will be appended.""" diff --git a/unixccompiler.py b/unixccompiler.py index 35390de0..0d2858de 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -140,11 +140,11 @@ class UnixCCompiler (CCompiler): # compile () - def link_static_lib (self, - objects, - output_libname, - output_dir=None, - debug=0): + def create_static_lib (self, + objects, + output_libname, + output_dir=None, + debug=0): (objects, output_dir) = self._fix_link_args (objects, output_dir, takes_libs=0) @@ -160,7 +160,7 @@ class UnixCCompiler (CCompiler): else: self.announce ("skipping %s (up-to-date)" % output_filename) - # link_static_lib () + # create_static_lib () def link_shared_lib (self, -- cgit v1.2.1 From 475a7ee2b018683744a4d4d739ce14b735fb11e8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 10 Mar 2000 01:49:26 +0000 Subject: Renamed 'link_static_lib() to 'create_static_lib()', and rewrote it create a static library (using lib.exe as found by '__init__()', hopefully through registry entries pointing to DevStudio). --- msvccompiler.py | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 847c611d..bf5257f1 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -168,6 +168,7 @@ class MSVCCompiler (CCompiler) : self.cc = _find_exe("cl.exe", version) self.link = _find_exe("link.exe", version) + self.lib = _find_exe("lib.exe", version) set_path_env_var ('lib', version) set_path_env_var ('include', version) path=get_msvc_paths('path', version) @@ -181,6 +182,7 @@ class MSVCCompiler (CCompiler) : # devstudio not found in the registry self.cc = "cl.exe" self.link = "link.exe" + self.lib = "lib.exe" self.preprocess_options = None self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3' ] @@ -243,44 +245,32 @@ class MSVCCompiler (CCompiler) : # compile () - # XXX the signature of this method is different from CCompiler and - # UnixCCompiler -- but those extra parameters (libraries, library_dirs) - # are actually used. So: are they really *needed*, or can they be - # ditched? If needed, the CCompiler API will have to change... - def link_static_lib (self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): + def create_static_lib (self, + objects, + output_libname, + output_dir=None, + debug=0, + extra_preargs=None, + extra_postargs=None): - (objects, output_dir, libraries, library_dirs) = \ - self._fix_link_args (objects, output_dir, takes_libs=1, - libraries=libraries, - library_dirs=library_dirs) - + (objects, output_dir) = \ + self._fix_link_args (objects, output_dir, takes_libs=0) output_filename = \ self.library_filename (output_libname, output_dir=output_dir) if self._need_link (objects, output_filename): - lib_opts = gen_lib_options (libraries, library_dirs, - "%s.lib", "/LIBPATH:%s") - ld_args = self.ldflags_static + lib_opts + \ - objects + ['/OUT:' + output_filename] + lib_args = objects + ['/OUT:' + output_filename] if debug: pass # XXX what goes here? if extra_preargs: - ld_args[:0] = extra_preargs + lib_args[:0] = extra_preargs if extra_postargs: - ld_args.extend (extra_postargs) + lib_args.extend (extra_postargs) self.spawn ([self.link] + ld_args) else: self.announce ("skipping %s (up-to-date)" % output_filename) - # link_static_lib () + # create_static_lib () def link_shared_lib (self, -- cgit v1.2.1 From f148d847825da4d45f345c97a6e7ad8a48b02774 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 10 Mar 2000 02:02:44 +0000 Subject: Catch up with change to CCompiler API: call 'create_static_lib()', not 'link_static_lib()'. --- command/build_clib.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index c49e3ca7..6560fb47 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -190,9 +190,9 @@ class build_clib (Command): # Now "link" the object files together into a static library. # (On Unix at least, this isn't really linking -- it just # builds an archive. Whatever.) - self.compiler.link_static_lib (objects, lib_name, - output_dir=self.build_clib, - debug=self.debug) + self.compiler.create_static_lib (objects, lib_name, + output_dir=self.build_clib, + debug=self.debug) # for libraries -- cgit v1.2.1 From a447a64793487f03b2f059298a2a1bc1b069a9e2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 15:19:51 +0000 Subject: Changed to pay attention to the 'runtime_library_dirs' list (= 'rpath' option in the 'build_ext' command): * in ccompiler.py: 'gen_lib_options()' now takes 'runtime_library_dirs' parameter * in unixccompiler.py and msvccompiler.py: now pass 'self.runtime_library_dirs' to 'gen_lib_options()', and define 'runtime_library_dir_option()' (although in msvccompiler.py it blows up with a DistutilsPlatformError right now!) --- ccompiler.py | 5 ++++- msvccompiler.py | 8 +++++++- unixccompiler.py | 11 +++++++++-- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 46dabade..4a8c1d38 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -770,7 +770,7 @@ def gen_preprocess_options (macros, include_dirs): # gen_preprocess_options () -def gen_lib_options (compiler, library_dirs, libraries): +def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): """Generate linker options for searching library directories and linking with specific libraries. 'libraries' and 'library_dirs' are, respectively, lists of library names (not filenames!) and @@ -783,6 +783,9 @@ def gen_lib_options (compiler, library_dirs, libraries): for dir in library_dirs: lib_opts.append (compiler.library_dir_option (dir)) + for dir in runtime_library_dirs: + lib_opts.append (compiler.runtime_library_dir_option (dir)) + # XXX it's important that we *not* remove redundant library mentions! # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to # resolve all symbols. I just hope we never have to say "-lfoo obj.o diff --git a/msvccompiler.py b/msvccompiler.py index bf5257f1..7324b8e1 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -307,7 +307,9 @@ class MSVCCompiler (CCompiler) : self._fix_link_args (objects, output_dir, takes_libs=1, libraries=libraries, library_dirs=library_dirs) - lib_opts = gen_lib_options (self, library_dirs, libraries) + lib_opts = gen_lib_options (self, + library_dirs, self.runtime_library_dirs, + libraries) if type (output_dir) not in (StringType, NoneType): raise TypeError, "'output_dir' must be a string or None" if output_dir is not None: @@ -348,6 +350,10 @@ class MSVCCompiler (CCompiler) : def library_dir_option (self, dir): return "/LIBPATH:" + dir + def runtime_library_dir_option (self, dir): + raise DistutilsPlatformError, \ + "don't know how to set runtime library search path for MSVC++" + def library_option (self, lib): return self.library_filename (lib) diff --git a/unixccompiler.py b/unixccompiler.py index 0d2858de..ec85571d 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -197,7 +197,9 @@ class UnixCCompiler (CCompiler): self._fix_link_args (objects, output_dir, takes_libs=1, libraries=libraries, library_dirs=library_dirs) - lib_opts = gen_lib_options (self, library_dirs, libraries) + lib_opts = gen_lib_options (self, + library_dirs, self.runtime_library_dirs, + libraries) if type (output_dir) not in (StringType, NoneType): raise TypeError, "'output_dir' must be a string or None" if output_dir is not None: @@ -234,7 +236,9 @@ class UnixCCompiler (CCompiler): self._fix_link_args (objects, output_dir, takes_libs=1, libraries=libraries, library_dirs=library_dirs) - lib_opts = gen_lib_options (self, library_dirs, libraries) + lib_opts = gen_lib_options (self, + library_dirs, self.runtime_library_dirs, + libraries) output_filename = output_progname # Unix-ism! if output_dir is not None: output_filename = os.path.join (output_dir, output_filename) @@ -262,6 +266,9 @@ class UnixCCompiler (CCompiler): def library_dir_option (self, dir): return "-L" + dir + def runtime_library_dir_option (self, dir): + return "-R" + dir + def library_option (self, lib): return "-l" + lib -- cgit v1.2.1 From f11f6f3246f288422067b7c49aa0ab7d20992048 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 15:21:03 +0000 Subject: In 'finalize_options()': if 'self.libs' is a string, make it a singleton list. --- command/build_ext.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index 050a5c65..dfe64a11 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -117,6 +117,9 @@ class build_ext (Command): if exec_py_include != py_include: self.include_dirs.insert (0, exec_py_include) + if type (self.libs) is StringType: + self.libs = [self.libs] + # XXX how the heck are 'self.define' and 'self.undef' supposed to # be set? -- cgit v1.2.1 From 52b70f2aefe7f937e5090dce65c43eef3f8e6051 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 15:37:26 +0000 Subject: Contribution from Bastian Kleineidam : the Distutils 'clean' command. --- command/clean.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 command/clean.py diff --git a/command/clean.py b/command/clean.py new file mode 100644 index 00000000..9785de99 --- /dev/null +++ b/command/clean.py @@ -0,0 +1,44 @@ +"""distutils.command.clean + +Implements the Distutils 'clean' command.""" + +# contributed by Bastian Kleineidam , added 2000-03-18 + +__revision__ = "$Id$" + +import os +from distutils.core import Command +from distutils.util import remove_tree + +class clean (Command): + + description = "clean files we built" + user_options = [ + ('build-base=', 'b', "base directory for build library"), + ('build-lib=', None, + "build directory for all distribution (defaults to either " + + "build-purelib or build-platlib"), + ('build-temp=', 't', "temporary build directory"), + ('all', 'a', + "remove all build output, not just temporary by-products") + ] + + def initialize_options(self): + self.build_base = None + self.build_lib = None + self.build_temp = None + self.all = None + + def finalize_options(self): + self.set_undefined_options('build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib'), + ('build_temp', 'build_temp')) + + def run(self): + # remove the build/temp. directory + remove_tree (self.build_temp, self.verbose, self.dry_run) + + if self.all: + # remove the build/lib resp. build/platlib directory + remove_tree (self.build_lib, self.verbose, self.dry_run) -- cgit v1.2.1 From b0b51892b77115a91a1cbf12dc63faba193de7a3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 15:42:22 +0000 Subject: Patch from Bastian Kleineidam : added 'remove_tree()'. --- util.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/util.py b/util.py index 2f193fb4..7a07b656 100644 --- a/util.py +++ b/util.py @@ -11,7 +11,7 @@ file causing it.""" __revision__ = "$Id$" -import os, string +import os, string, shutil from distutils.errors import * @@ -378,6 +378,25 @@ def copy_tree (src, dst, # copy_tree () +def remove_tree (directory, verbose=0, dry_run=0): + """Recursively remove an entire directory tree. Any errors are ignored + (apart from being reported to stdout if 'verbose' is true).""" + + if verbose: + print "removing '%s' (and everything under it)" % directory + if dry_run: + return + try: + shutil.rmtree(directory,1) + except (IOError, OSError), exc: + if verbose: + if exc.filename: + print "error removing %s: %s (%s)" % \ + (directory, exc.strerror, exc.filename) + else: + print "error removing %s: %s" % (directory, exc.strerror) + + # XXX I suspect this is Unix-specific -- need porting help! def move_file (src, dst, verbose=0, -- cgit v1.2.1 From d8cd9e2b90b4f3ea1603bc44cbf1a156172c0f76 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 15:43:42 +0000 Subject: Patch from Bastian Kleineidam : use 'util.remove_tree()' instead of 'nuke_release_tree()'. --- command/sdist.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 726458a2..0c151778 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -10,9 +10,8 @@ import sys, os, string, re import fnmatch from types import * from glob import glob -from shutil import rmtree from distutils.core import Command -from distutils.util import newer +from distutils.util import newer, remove_tree from distutils.text_file import TextFile from distutils.errors import DistutilsExecError @@ -504,19 +503,6 @@ class sdist (Command): # make_release_tree () - def nuke_release_tree (self, base_dir): - try: - self.execute (rmtree, (base_dir,), - "removing %s" % base_dir) - except (IOError, OSError), exc: - if exc.filename: - msg = "error removing %s: %s (%s)" % \ - (base_dir, exc.strerror, exc.filename) - else: - msg = "error removing %s: %s" % (base_dir, exc.strerror) - self.warn (msg) - - def make_tarball (self, base_dir, compress="gzip"): # XXX GNU tar 1.13 has a nifty option to add a prefix directory. @@ -601,7 +587,7 @@ class sdist (Command): self.make_zipfile (base_dir) if not self.keep_tree: - self.nuke_release_tree (base_dir) + remove_tree (base_dir, self.verbose, self.dry_run) # class Dist -- cgit v1.2.1 From 9403029df5514984078943c729e96ba310319613 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 17:33:18 +0000 Subject: Tweaked all over: * improve help strings * warn if user supplies non-existing directories * don't try to 'remove_tree()' non-existing directories * try to remove the build_base after cleanup (but don't do or say anything if it fails -- this is just in case we made it empty) --- command/clean.py | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/command/clean.py b/command/clean.py index 9785de99..4f0f7e3e 100644 --- a/command/clean.py +++ b/command/clean.py @@ -12,13 +12,14 @@ from distutils.util import remove_tree class clean (Command): - description = "clean files we built" + description = "clean up output of 'build' command" user_options = [ - ('build-base=', 'b', "base directory for build library"), + ('build-base=', 'b', + "base build directory (default: 'build.build-base')"), ('build-lib=', None, - "build directory for all distribution (defaults to either " + - "build-purelib or build-platlib"), - ('build-temp=', 't', "temporary build directory"), + "build directory for all modules (default: 'build.build-lib')"), + ('build-temp=', 't', + "temporary build directory (default: 'build.build-temp')"), ('all', 'a', "remove all build output, not just temporary by-products") ] @@ -30,15 +31,34 @@ class clean (Command): self.all = None def finalize_options(self): + if self.build_lib and not os.path.exists (self.build_lib): + self.warn ("'%s' does not exist -- can't clean it" % + self.build_lib) + if self.build_temp and not os.path.exists (self.build_temp): + self.warn ("'%s' does not exist -- can't clean it" % + self.build_temp) + self.set_undefined_options('build', ('build_base', 'build_base'), ('build_lib', 'build_lib'), ('build_temp', 'build_temp')) def run(self): - # remove the build/temp. directory - remove_tree (self.build_temp, self.verbose, self.dry_run) + # remove the build/temp. directory (unless it's already + # gone) + if os.path.exists (self.build_temp): + remove_tree (self.build_temp, self.verbose, self.dry_run) if self.all: - # remove the build/lib resp. build/platlib directory - remove_tree (self.build_lib, self.verbose, self.dry_run) + # remove the module build directory (unless already gone) + if os.path.exists (self.build_lib): + remove_tree (self.build_lib, self.verbose, self.dry_run) + + # just for the heck of it, try to remove the base build directory: + # we might have emptied it right now, but if not we don't care + if not self.dry_run: + try: + os.rmdir (self.build_base) + self.announce ("removing '%s'" % self.build_base) + except OSError: + pass -- cgit v1.2.1 From 8f7de70136acae5a467212cbb16c09ce2c364fdb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 17:35:12 +0000 Subject: Oops! Don't call 'ensure_ready()' in 'Distribution.find_command_obj()' -- that broke parsing command-line options. Instead call it in 'Command.find_peer()', which is why I added it to 'find_command_obj()' in the first place. --- core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index a31e60ce..dfe411b0 100644 --- a/core.py +++ b/core.py @@ -506,7 +506,6 @@ class Distribution: cmd_obj = self.command_obj.get (command) if not cmd_obj and create: cmd_obj = self.create_command_obj (command) - cmd_obj.ensure_ready () self.command_obj[command] = cmd_obj return cmd_obj @@ -873,7 +872,9 @@ class Command: find (create if necessary and 'create' is true) the command object for 'command'..""" - return self.distribution.find_command_obj (command, create) + cmd_obj = self.distribution.find_command_obj (command, create) + cmd_obj.ensure_ready () + return cmd_obj def get_peer_option (self, command, option): -- cgit v1.2.1 From bfc2b54d5a0bc8ba8a3b115aa8abaf3911cfc9c9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 17:36:09 +0000 Subject: Simplified doc string. Added 'clean' to list of commands. --- command/__init__.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/command/__init__.py b/command/__init__.py index 40595ba9..d2b37a8c 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -1,17 +1,7 @@ """distutils.command Package containing implementation of all the standard Distutils -commands. Currently this means: - - build - build_py - build_ext - install - install_py - install_ext - dist - -but this list will undoubtedly grow with time.""" +commands.""" __revision__ = "$Id$" @@ -21,5 +11,6 @@ __all__ = ['build', 'install', 'install_py', 'install_ext', + 'clean', 'sdist', ] -- cgit v1.2.1 From 2176088ac051cb11a5b4aee4928f32b831ec2063 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:11:21 +0000 Subject: Took out what looks like old debugging code that probably should never have been checked in: was passing the PLAT environment variable as the 'plat' argument to 'new_compiler()'. --- command/build_ext.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index dfe64a11..1d8794a6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -146,8 +146,7 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler (plat=os.environ.get ('PLAT'), - verbose=self.verbose, + self.compiler = new_compiler (verbose=self.verbose, dry_run=self.dry_run, force=self.force) if self.include_dirs is not None: -- cgit v1.2.1 From d852781fe93c7a37b90387e2b388bf18ed3823d1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:12:51 +0000 Subject: Fix how we set 'build_dir' and 'install_dir' options from 'install' options -- irrelevant because this file is about to go away, but oh well. --- command/install_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/install_ext.py b/command/install_ext.py index 8d23fa4c..14730909 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -25,8 +25,8 @@ class install_ext (Command): def finalize_options (self): self.set_undefined_options ('install', - ('build_platlib', 'build_dir'), - ('install_platlib', 'install_dir')) + ('build_lib', 'build_dir'), + ('install_lib', 'install_dir')) def run (self): -- cgit v1.2.1 From d80b9efb03fd6cb33c60de06877d7cc04405b576 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:15:45 +0000 Subject: Yet another complete rewrite. Hopefully the *last* complete rewrite of this command for a while; this implements roughly the plan cooked up by Guido, Fred, and me. Seems to strike a nice balance between usability in the common cases (just set one option), expandability for more types of files to install in future, and customizability of installation directories. This revision isn't completely working: standard and alternate installations work fine, but there are still some kinks to work out of customized installations. --- command/install.py | 428 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 299 insertions(+), 129 deletions(-) diff --git a/command/install.py b/command/install.py index 1df55843..13baa1a7 100644 --- a/command/install.py +++ b/command/install.py @@ -9,33 +9,76 @@ __revision__ = "$Id$" import sys, os, string from types import * from distutils.core import Command -from distutils.util import write_file +from distutils.util import write_file, native_path, subst_vars from distutils.errors import DistutilsOptionError +INSTALL_SCHEMES = { + 'unix_prefix': { + 'purelib': '$base/lib/python$py_version_short/site-packages', + 'platlib': '$platbase/lib/python$py_version_short/site-packages', + 'scripts': '$base/bin', + 'data' : '$base/share', + }, + 'unix_home': { + 'purelib': '$base/lib/python', + 'platlib': '$base/lib/python', + 'scripts': '$base/bin', + 'data' : '$base/share', + }, + 'nt': { + 'purelib': '$base', + 'platlib': '$base', + 'scripts': '$base\\Scripts', + 'data' : '$base\\Data', + }, + 'mac': { + 'purelib': '$base:Lib', + 'platlib': '$base:Mac:PlugIns', + 'scripts': '$base:Scripts', + 'data' : '$base:Data', + } + } + + class install (Command): description = "install everything from build directory" user_options = [ - ('prefix=', None, "installation prefix"), + # Select installation scheme and set base director(y|ies) + ('prefix=', None, + "installation prefix"), ('exec-prefix=', None, - "prefix for platform-specific files"), - - # Installation directories: where to put modules and packages - ('install-lib=', None, - "base Python library directory"), + "(Unix only) prefix for platform-specific files"), + ('home=', None, + "(Unix only) home directory to install under"), + + # Or, just set the base director(y|ies) + ('install-base=', None, + "base installation directory (instead of --prefix or --home)"), + ('install-platbase=', None, + "base installation directory for platform-specific files " + + "(instead of --exec-prefix or --home)"), + + # Or, explicitly set the installation scheme + ('install-purelib=', None, + "installation directory for pure Python module distributions"), ('install-platlib=', None, - "platform-specific Python library directory"), - ('install-path=', None, - "extra intervening directories to put below install-lib"), + "installation directory for non-pure module distributions"), + ('install-lib=', None, + "installation directory for all module distributions " + + "(overrides --install-purelib and --install-platlib)"), + + ('install-scripts=', None, + "installation directory for Python scripts"), + ('install-data=', None, + "installation directory for data files"), # Build directories: where to find the files to install ('build-base=', None, "base build directory"), ('build-lib=', None, - "build directory for pure Python modules"), - ('build-platlib=', None, - "build directory for extension modules"), + "build directory for all Python modules"), # Where to install documentation (eventually!) #('doc-format=', None, "format of documentation to generate"), @@ -55,21 +98,31 @@ class install (Command): # command is run whether the user supplied values self.prefix = None self.exec_prefix = None + self.home = None + + self.install_base = None + self.install_platbase = None # The actual installation directories are determined only at # run-time, so the user can supply just prefix (and exec_prefix?) # as a base for everything else - self.install_lib = None + self.install_purelib = None self.install_platlib = None - self.install_path = None + self.install_lib = None + + self.install_scripts = None + self.install_data = None self.build_base = None self.build_lib = None self.build_platlib = None - self.install_man = None - self.install_html = None - self.install_info = None + self.extra_path = None + self.install_path_file = 0 + + #self.install_man = None + #self.install_html = None + #self.install_info = None self.compile_py = 1 self.optimize_py = 1 @@ -103,129 +156,234 @@ class install (Command): # Figure out actual installation directories; the basic principle # is: ... + + + # Logic: + # * any: (prefix or exec-prefix or home) and (base or platbase) + # supplied: error + # * Windows/Mac OS: exec-prefix or home supplied: warn and ignore + # + # * Unix: home set + # (select the unix_home scheme) + # * Unix: neither prefix nor home set + # (set prefix=sys_prefix and carry on) + # * Unix: prefix set but not exec-prefix + # (set exec-prefix=prefix and carry on) + # * Unix: prefix set + # (select the unix_prefix scheme) + # + # * Windows/Mac OS: prefix not set + # (set prefix = sys_prefix and carry on) + # * Windows/Mac OS: prefix set + # (select the appropriate scheme) + + # "select a scheme" means: + # - set install-base and install-platbase + # - subst. base/platbase/version into the values of the + # particular scheme dictionary + # - use the resultings strings to set install-lib, etc. + sys_prefix = os.path.normpath (sys.prefix) sys_exec_prefix = os.path.normpath (sys.exec_prefix) - if self.prefix is None: - if self.exec_prefix is not None: + # Check for errors/inconsistencies in the options + if ((self.prefix or self.exec_prefix or self.home) and + (self.install_base or self.install_platbase)): + raise DistutilsOptionError, \ + ("must supply either prefix/exec-prefix/home or " + + "install-base/install-platbase -- not both") + + if os.name == 'posix': + if self.home and (self.prefix or self.exec_prefix): raise DistutilsOptionError, \ - "you may not supply exec_prefix without prefix" - self.prefix = sys_prefix - else: - # This is handy to guarantee that self.prefix is normalized -- - # but it could be construed as rude to go normalizing a - # user-supplied path (they might like to see their "../" or - # symlinks in the installation feedback). - self.prefix = os.path.normpath (self.prefix) - - if self.exec_prefix is None: - if self.prefix == sys_prefix: - self.exec_prefix = sys_exec_prefix - else: - self.exec_prefix = self.prefix + ("must supply either home or prefix/exec-prefix -- " + + "not both") else: - # Same as above about handy versus rude to normalize user's - # exec_prefix. - self.exec_prefix = os.path.normpath (self.exec_prefix) + if self.exec_prefix: + self.warn ("exec-prefix option ignored on this platform") + self.exec_prefix = None + if self.home: + self.warn ("home option ignored on this platform") + self.home = None + + # Now the interesting logic -- so interesting that we farm it out + # to other methods. The goal of these methods is to set the final + # values for the install_{lib,scripts,data,...} options, using as + # input a heady brew of prefix, exec_prefix, home, install_base, + # install_platbase, user-supplied versions of + # install_{purelib,platlib,lib,scripts,data,...}, and the + # INSTALL_SCHEME dictionary above. Phew! - if self.distribution.ext_modules: # any extensions to install? - effective_prefix = self.exec_prefix + if os.name == 'posix': + self.finalize_unix () else: - effective_prefix = self.prefix + self.finalize_other () + + # Expand "~" and configuration variables in the installation + # directories. + self.expand_dirs () + + # Pick the actual directory to install all modules to: either + # install_purelib or install_platlib, depending on whether this + # module distribution is pure or not. Of course, if the user + # already specified install_lib, use their selection. + if self.install_lib is None: + if self.distribution.ext_modules: # has extensions: non-pure + self.install_lib = self.install_platlib + else: + self.install_lib = self.install_purelib + + # Well, we're not actually fully completely finalized yet: we still + # have to deal with 'extra_path', which is the hack for allowing + # non-packagized module distributions (hello, Numerical Python!) to + # get their own directories. + self.handle_extra_path () + self.install_libbase = self.install_lib # needed for .pth file + self.install_lib = os.path.join (self.install_lib, self.extra_dirs) - if os.name == 'posix': - if self.install_lib is None: - if self.prefix == sys_prefix: - self.install_lib = \ - os.path.join (effective_prefix, - "lib", - "python" + sys.version[:3], - "site-packages") - else: - self.install_lib = \ - os.path.join (effective_prefix, - "lib", - "python") # + sys.version[:3] ??? - # end if self.install_lib ... - - if self.install_platlib is None: - if self.exec_prefix == sys_exec_prefix: - self.install_platlib = \ - os.path.join (effective_prefix, - "lib", - "python" + sys.version[:3], - "site-packages") - else: - self.install_platlib = \ - os.path.join (effective_prefix, - "lib", - "python") # + sys.version[:3] ??? - # end if self.install_platlib ... + # Figure out the build directories, ie. where to install from + self.set_peer_option ('build', 'build_base', self.build_base) + self.set_undefined_options ('build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib'), + ('build_platlib', 'build_platlib')) + + # Punt on doc directories for now -- after all, we're punting on + # documentation completely! + # finalize_options () + + + def finalize_unix (self): + + if self.install_base is not None or self.install_platbase is not None: + if ((self.install_lib is None and + self.install_purelib is None and + self.install_platlib is None) or + self.install_scripts is None or + self.install_data is None): + raise DistutilsOptionError, \ + "install-base or install-platbase supplied, but " + \ + "installation scheme is incomplete" + + return + + if self.home is not None: + self.install_base = self.install_platbase = self.home + self.select_scheme ("unix_home") else: + if self.prefix is None: + if self.exec_prefix is not None: + raise DistutilsOptionError, \ + "must not supply exec-prefix without prefix" + + self.prefix = os.path.normpath (sys.prefix) + self.exec_prefix = os.path.normpath (sys.exec_prefix) + self.install_path_file = 1 + + else: + if self.exec_prefix is None: + self.exec_prefix = self.prefix + + + # XXX since we don't *know* that a user-supplied prefix really + # points to another Python installation, we can't be sure that + # writing a .pth file there will actually work -- so we don't + # try. That is, we only set 'install_path_file' if the user + # didn't supply prefix. There are certainly circumstances + # under which we *should* install a .pth file when the user + # supplies a prefix, namely when that prefix actually points to + # another Python installation. Hmmm. + + self.install_base = self.prefix + self.install_platbase = self.exec_prefix + self.select_scheme ("unix_prefix") + + # finalize_unix () + + + def finalize_other (self): # Windows and Mac OS for now + + if self.prefix is None: + self.prefix = os.path.normpath (sys.prefix) + self.install_path_file = 1 + + # XXX same caveat regarding 'install_path_file' as in + # 'finalize_unix()'. + + self.install_base = self.install_platbase = self.prefix + try: + self.select_scheme (os.name) + except KeyError: raise DistutilsPlatformError, \ - "duh, I'm clueless (for now) about installing on %s" % os.name + "I don't know how to install stuff on '%s'" % os.name - # end if/else on os.name - + # finalize_other () + + + def select_scheme (self, name): + + # it's the caller's problem if they supply a bad name! + scheme = INSTALL_SCHEMES[name] + + vars = { 'base': self.install_base, + 'platbase': self.install_platbase, + 'py_version_short': sys.version[0:3], + } + + for key in ('purelib', 'platlib', 'scripts', 'data'): + val = subst_vars (scheme[key], vars) + setattr (self, 'install_' + key, val) + + + def expand_dirs (self): + + # XXX probably don't want to 'expanduser()' on Windows or Mac + # XXX should use 'util.subst_vars()' with our own set of + # configuration variables + + for att in ('base', 'platbase', + 'purelib', 'platlib', 'lib', + 'scripts', 'data'): + fullname = "install_" + att + val = getattr (self, fullname) + if val is not None: + setattr (self, fullname, + os.path.expandvars (os.path.expanduser (val))) + + + def handle_extra_path (self): - # 'path_file' and 'extra_dirs' are how we handle distributions that - # want to be installed to their own directory, but aren't - # package-ized yet. 'extra_dirs' is just a directory under - # 'install_lib' or 'install_platlib' where top-level modules will - # actually be installed; 'path_file' is the basename of a .pth file - # to drop in 'install_lib' or 'install_platlib' (depending on the - # distribution). Very often they will be the same, which is why we - # allow them to be supplied as a string or 1-tuple as well as a - # 2-element comma-separated string or a 2-tuple. - - # XXX this will drop a .pth file in install_{lib,platlib} even if - # they're not one of the site-packages directories: this is wrong! - # we need to suppress path_file in those cases, and warn if - # "install_lib/extra_dirs" is not in sys.path. - - if self.install_path is None: - self.install_path = self.distribution.install_path - - if self.install_path is not None: - if type (self.install_path) is StringType: - self.install_path = string.split (self.install_path, ',') - - if len (self.install_path) == 1: - path_file = extra_dirs = self.install_path[0] - elif len (self.install_path) == 2: - (path_file, extra_dirs) = self.install_path + if self.extra_path is None: + self.extra_path = self.distribution.extra_path + + if self.extra_path is not None: + if type (self.extra_path) is StringType: + self.extra_path = string.split (self.extra_path, ',') + + if len (self.extra_path) == 1: + path_file = extra_dirs = self.extra_path[0] + elif len (self.extra_path) == 2: + (path_file, extra_dirs) = self.extra_path else: raise DistutilsOptionError, \ - "'install_path' option must be a list, tuple, or " + \ + "'extra_path' option must be a list, tuple, or " + \ "comma-separated string with 1 or 2 elements" - # install path has slashes in it -- might need to convert to - # local form - if string.find (extra_dirs, '/') and os.name != "posix": - extra_dirs = string.split (extra_dirs, '/') - extra_dirs = apply (os.path.join, extra_dirs) + # convert to local form in case Unix notation used (as it + # should be in setup scripts) + extra_dirs = native_path (extra_dirs) + else: path_file = None extra_dirs = '' - # XXX should we warn if path_file and not extra_dirs (in which case - # the path file would be harmless but pointless) + # XXX should we warn if path_file and not extra_dirs? (in which + # case the path file would be harmless but pointless) self.path_file = path_file self.extra_dirs = extra_dirs - - # Figure out the build directories, ie. where to install from - self.set_peer_option ('build', 'build_base', self.build_base) - self.set_undefined_options ('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib'), - ('build_platlib', 'build_platlib')) - - # Punt on doc directories for now -- after all, we're punting on - # documentation completely! - - # finalize_options () + # handle_extra_path () def run (self): @@ -238,28 +396,40 @@ class install (Command): # extensions). Note that 'install_py' is smart enough to install # pure Python modules in the "platlib" directory if we built any # extensions. + + # XXX this should become one command, 'install_lib', since + # all modules are "built" into the same directory now + if self.distribution.packages or self.distribution.py_modules: self.run_peer ('install_py') - if self.distribution.ext_modules: - self.run_peer ('install_ext') + #if self.distribution.ext_modules: + # self.run_peer ('install_ext') if self.path_file: self.create_path_file () + normalized_path = map (os.path.normpath, sys.path) + if (not (self.path_file and self.install_path_file) and + os.path.normpath (self.install_lib) not in normalized_path): + self.warn (("modules installed to '%s', which is not in " + + "Python's module search path (sys.path) -- " + + "you'll have to change the search path yourself") % + self.install_lib) + # run () def create_path_file (self): - - if self.distribution.ext_modules: - base = self.platbase + filename = os.path.join (self.install_libbase, + self.path_file + ".pth") + if self.install_path_file: + self.execute (write_file, + (filename, [self.extra_dirs]), + "creating %s" % filename) else: - base = self.base - - filename = os.path.join (base, self.path_file + ".pth") - self.execute (write_file, - (filename, [self.extra_dirs]), - "creating %s" % filename) - + self.warn (("path file '%s' not created for alternate or custom " + + "installation (path files only work with standard " + + "installations)") % + filename) # class Install -- cgit v1.2.1 From 4b8aee534fe757703ad35e112a716bd732ae0cc6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:20:46 +0000 Subject: Renamed 'install_path' to 'extra_path'. Fix 'Command.set_undefined_option()' to call 'ensure_ready()' rather than 'finalize_options()' (which is only supposed to be called once, which is the whole point of 'ensure_ready()'). Added comment to 'set_peer_option()' to remind myself that this method cannot work and is fundamentally wrong-headed. --- core.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index dfe411b0..2f3a36d7 100644 --- a/core.py +++ b/core.py @@ -197,7 +197,7 @@ class Distribution: self.ext_modules = None self.ext_package = None self.include_dirs = None - self.install_path = None + self.extra_path = None # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to @@ -840,7 +840,7 @@ class Command: # Option_pairs: list of (src_option, dst_option) tuples src_cmd_obj = self.distribution.find_command_obj (src_cmd) - src_cmd_obj.finalize_options () + src_cmd_obj.ensure_ready () try: for (src_option, dst_option) in option_pairs: if getattr (self, dst_option) is None: @@ -862,6 +862,10 @@ class Command: second 'finalize_options()' invocation will have little or no effect.""" + # XXX this won't work -- must call finalize_option to work, but + # calling finalize_option is wrong (it's only supposed to be called + # once). Where is this needed?!??! + cmd_obj = self.distribution.find_command_obj (command) cmd_obj.set_option (option, value) cmd_obj.finalize_options () -- cgit v1.2.1 From bcfea0afe66af5445e7660e4828a422161cf4126 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:22:44 +0000 Subject: Improved an error message in 'mkpath()'. Tightened up some logic in 'native_path()'. Added 'subst_vars()' and '_check_environ()'. --- util.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/util.py b/util.py index 7a07b656..b308b90d 100644 --- a/util.py +++ b/util.py @@ -11,7 +11,7 @@ file causing it.""" __revision__ = "$Id$" -import os, string, shutil +import os, string, re, shutil from distutils.errors import * @@ -71,7 +71,8 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): try: os.mkdir (head) except os.error, (errno, errstr): - raise DistutilsFileError, "'%s': %s" % (head, errstr) + raise DistutilsFileError, \ + "could not create '%s': %s" % (head, errstr) PATH_CREATED[head] = 1 @@ -504,11 +505,10 @@ def native_path (pathname): raise DistutilsValueError, "path '%s' cannot be absolute" % pathname if pathname[-1] == '/': raise DistutilsValueError, "path '%s' cannot end with '/'" % pathname - if os.sep != '/': - if os.sep in pathname: - raise DistutilsValueError, \ - "path '%s' cannot contain '%c' character" % \ - (pathname, os.sep) + if os.sep != '/' and os.sep in pathname: + raise DistutilsValueError, \ + "path '%s' cannot contain '%c' character" % \ + (pathname, os.sep) paths = string.split (pathname, '/') return apply (os.path.join, paths) @@ -516,3 +516,43 @@ def native_path (pathname): return pathname # native_path () + + +def _check_environ (): + """Ensure that 'os.environ' has all the environment variables we + guarantee that users can use in config files, command-line + options, etc. Currently this includes: + HOME - user's home directory (Unix only) + PLAT - desription of the current platform, including hardware + and OS (see 'get_platform()') + """ + + if os.name == 'posix' and not os.environ.has_key('HOME'): + import pwd + os.environ['HOME'] = pwd.getpwuid (os.getuid())[5] + + if not os.environ.has_key('PLAT'): + os.environ['PLAT'] = get_platform () + + +def subst_vars (str, local_vars): + """Perform shell/Perl-style variable substitution on 'string'. + Every occurence of '$' followed by a name, or a name enclosed in + braces, is considered a variable. Every variable is substituted by + the value found in the 'local_vars' dictionary, or in 'os.environ' + if it's not in 'local_vars'. 'os.environ' is first checked/ + augmented to guarantee that it contains certain values: see + '_check_environ()'. Raise ValueError for any variables not found in + either 'local_vars' or 'os.environ'.""" + + _check_environ () + def _subst (match, local_vars=local_vars): + var_name = match.group(1) + if local_vars.has_key (var_name): + return str (local_vars[var_name]) + else: + return os.environ[var_name] + + return re.sub (r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str) + +# subst_vars () -- cgit v1.2.1 From 03db2e6f44914364b5e32fa45de7002e13e08142 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:30:54 +0000 Subject: Dropped any notion of allowing the user to specify the build directories: these must come from the 'build' command. This means we no longer need the misconceived 'set_peer_option()' method in Command and, more importantly, sweeps away a bunch of potential future complexity to handle this tricky case. --- command/install.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/command/install.py b/command/install.py index 13baa1a7..a1b5d06a 100644 --- a/command/install.py +++ b/command/install.py @@ -74,12 +74,6 @@ class install (Command): ('install-data=', None, "installation directory for data files"), - # Build directories: where to find the files to install - ('build-base=', None, - "base build directory"), - ('build-lib=', None, - "build directory for all Python modules"), - # Where to install documentation (eventually!) #('doc-format=', None, "format of documentation to generate"), #('install-man=', None, "directory for Unix man pages"), @@ -113,13 +107,18 @@ class install (Command): self.install_scripts = None self.install_data = None - self.build_base = None - self.build_lib = None - self.build_platlib = None - self.extra_path = None self.install_path_file = 0 + # These are only here as a conduit from the 'build' command to the + # 'install_*' commands that do the real work. ('build_base' isn't + # actually used anywhere, but it might be useful in future.) They + # are not user options, because if the user told the install + # command where the build directory is, that wouldn't affect the + # build command. + self.build_base = None + self.build_lib = None + #self.install_man = None #self.install_html = None #self.install_info = None @@ -242,11 +241,9 @@ class install (Command): self.install_lib = os.path.join (self.install_lib, self.extra_dirs) # Figure out the build directories, ie. where to install from - self.set_peer_option ('build', 'build_base', self.build_base) self.set_undefined_options ('build', ('build_base', 'build_base'), - ('build_lib', 'build_lib'), - ('build_platlib', 'build_platlib')) + ('build_lib', 'build_lib')) # Punt on doc directories for now -- after all, we're punting on # documentation completely! -- cgit v1.2.1 From 6ec82e3176107f789ba8386705b60c2f65bf0999 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:31:37 +0000 Subject: Dropped the evil and misguided 'set_peer_option()' method -- it's no longer needed, and can't possibly work anyways. --- core.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/core.py b/core.py index 2f3a36d7..df364676 100644 --- a/core.py +++ b/core.py @@ -851,26 +851,6 @@ class Command: raise DistutilsOptionError, "unknown option %s" % name - def set_peer_option (self, command, option, value): - """Attempt to simulate a command-line override of some option - value in another command. Finds the command object for - 'command', sets its 'option' to 'value', and unconditionally - calls 'finalize_options()' on it: this means that some command - objects may have 'finalize_options()' invoked more than once. - Even so, this is not entirely reliable: the other command may - already be initialized to its satisfaction, in which case the - second 'finalize_options()' invocation will have little or no - effect.""" - - # XXX this won't work -- must call finalize_option to work, but - # calling finalize_option is wrong (it's only supposed to be called - # once). Where is this needed?!??! - - cmd_obj = self.distribution.find_command_obj (command) - cmd_obj.set_option (option, value) - cmd_obj.finalize_options () - - def find_peer (self, command, create=1): """Wrapper around Distribution's 'find_command_obj()' method: find (create if necessary and 'create' is true) the command -- cgit v1.2.1 From eb49a5318baf4799d7b24e8ec6e4b9012b6ad0b1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:35:16 +0000 Subject: Obsolete command -- no longer relevant since we now build all modules, pure Python and extensions, into the same directory. --- command/install_ext.py | 43 ------------------------------------------- 1 file changed, 43 deletions(-) delete mode 100644 command/install_ext.py diff --git a/command/install_ext.py b/command/install_ext.py deleted file mode 100644 index 14730909..00000000 --- a/command/install_ext.py +++ /dev/null @@ -1,43 +0,0 @@ -"""install_ext - -Implement the Distutils "install_ext" command to install extension modules.""" - -# created 1999/09/12, Greg Ward - -__revision__ = "$Id$" - -from distutils.core import Command -from distutils.util import copy_tree - -class install_ext (Command): - - description = "install C/C++ extension modules" - - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ('build-dir=','b', "build directory (where to install from)"), - ] - - def initialize_options (self): - # let the 'install' command dictate our installation directory - self.install_dir = None - self.build_dir = None - - def finalize_options (self): - self.set_undefined_options ('install', - ('build_lib', 'build_dir'), - ('install_lib', 'install_dir')) - - def run (self): - - # Make sure we have built all extension modules first - self.run_peer ('build_ext') - - # Dump the entire "build/platlib" directory (or whatever it really - # is; "build/platlib" is the default) to the installation target - # (eg. "/usr/local/lib/python1.5/site-packages"). Note that - # putting files in the right package dir is already done when we - # build. - outfiles = self.copy_tree (self.build_dir, self.install_dir) - -# class InstallExt -- cgit v1.2.1 From d3774f58433e614f683b466370188b4281c35735 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:37:16 +0000 Subject: Renamed install_py.py to install_lib.py, since it now handles installing all Python modules, pure and extensions. --- command/install_py.py | 76 --------------------------------------------------- 1 file changed, 76 deletions(-) delete mode 100644 command/install_py.py diff --git a/command/install_py.py b/command/install_py.py deleted file mode 100644 index 33cf6894..00000000 --- a/command/install_py.py +++ /dev/null @@ -1,76 +0,0 @@ -# created 1999/03/13, Greg Ward - -__revision__ = "$Id$" - -import sys, string -from distutils.core import Command -from distutils.util import copy_tree - -class install_py (Command): - - description = "install pure Python modules" - - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ('build-dir=','b', "build directory (where to install from)"), - ('compile', 'c', "compile .py to .pyc"), - ('optimize', 'o', "compile .py to .pyo (optimized)"), - ] - - - def initialize_options (self): - # let the 'install' command dictate our installation directory - self.install_dir = None - self.build_dir = None - self.compile = 1 - self.optimize = 1 - - def finalize_options (self): - - # Get all the information we need to install pure Python modules - # from the umbrella 'install' command -- build (source) directory, - # install (target) directory, and whether to compile .py files. - self.set_undefined_options ('install', - ('build_lib', 'build_dir'), - ('install_lib', 'install_dir'), - ('compile_py', 'compile'), - ('optimize_py', 'optimize')) - - - def run (self): - - # Make sure we have "built" all pure Python modules first - self.run_peer ('build_py') - - # Install everything: simply dump the entire contents of the build - # directory to the installation directory (that's the beauty of - # having a build directory!) - outfiles = self.copy_tree (self.build_dir, self.install_dir) - - # (Optionally) compile .py to .pyc - # XXX hey! we can't control whether we optimize or not; that's up - # to the invocation of the current Python interpreter (at least - # according to the py_compile docs). That sucks. - - if self.compile: - from py_compile import compile - - for f in outfiles: - # XXX can't assume this filename mapping! (what if - # we're running under "python -O"?) - - # only compile the file if it is actually a .py file - if f[-3:] == '.py': - out_fn = string.replace (f, '.py', '.pyc') - - self.make_file (f, out_fn, compile, (f,), - "byte-compiling %s" % f, - "byte-compilation of %s skipped" % f) - - # XXX ignore self.optimize for now, since we don't really know if - # we're compiling optimally or not, and couldn't pick what to do - # even if we did know. ;-( - - # run () - -# class InstallPy -- cgit v1.2.1 From 3603215dbb368ef0ffa0375174e2aeafc2d32fb1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:40:16 +0000 Subject: Run 'install_lib' instead of 'install_py', and ditch 'install_ext' completely (was already commented-out). --- command/install.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/command/install.py b/command/install.py index a1b5d06a..00aa0720 100644 --- a/command/install.py +++ b/command/install.py @@ -388,19 +388,10 @@ class install (Command): # Obviously have to build before we can install self.run_peer ('build') - # Install modules in two steps: "platform-shared" files (ie. pure - # Python modules) and platform-specific files (compiled C - # extensions). Note that 'install_py' is smart enough to install - # pure Python modules in the "platlib" directory if we built any - # extensions. - - # XXX this should become one command, 'install_lib', since - # all modules are "built" into the same directory now - - if self.distribution.packages or self.distribution.py_modules: - self.run_peer ('install_py') - #if self.distribution.ext_modules: - # self.run_peer ('install_ext') + # Now install all Python modules -- don't bother to make this + # conditional; why would someone distribute a Python module + # distribution without Python modules? + self.run_peer ('install_lib') if self.path_file: self.create_path_file () -- cgit v1.2.1 From 2f194a8f5701e42aef8801075f4b72da7a482fd8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:51:18 +0000 Subject: Revised tons of comments to reflect the current state of affairs better. Deleted some crufty code. --- command/install.py | 111 +++++++++++++++++++---------------------------------- 1 file changed, 39 insertions(+), 72 deletions(-) diff --git a/command/install.py b/command/install.py index 00aa0720..ed74971e 100644 --- a/command/install.py +++ b/command/install.py @@ -79,34 +79,38 @@ class install (Command): #('install-man=', None, "directory for Unix man pages"), #('install-html=', None, "directory for HTML documentation"), #('install-info=', None, "directory for GNU info files"), - - # Flags for 'build_py' - #('compile-py', None, "compile .py to .pyc"), - #('optimize-py', None, "compile .py to .pyo (optimized)"), ] def initialize_options (self): - # Don't define 'prefix' or 'exec_prefix' so we can know when the - # command is run whether the user supplied values + # High-level options: these select both an installation base + # and scheme. self.prefix = None self.exec_prefix = None self.home = None + # These select only the installation base; it's up to the user to + # specify the installation scheme (currently, that means supplying + # the --install-{platlib,purelib,scripts,data} options). self.install_base = None self.install_platbase = None - # The actual installation directories are determined only at - # run-time, so the user can supply just prefix (and exec_prefix?) - # as a base for everything else - self.install_purelib = None - self.install_platlib = None - self.install_lib = None - + # These options are the actual installation directories; if not + # supplied by the user, they are filled in using the installation + # scheme implied by prefix/exec-prefix/home and the contents of + # that installation scheme. + self.install_purelib = None # for pure module distributions + self.install_platlib = None # non-pure (dists w/ extensions) + self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None + # These two are for putting non-packagized distributions into their + # own directory and creating a .pth file if it makes sense. + # 'extra_path' comes from the setup file; 'install_path_file' is + # set only if we determine that it makes sense to install a path + # file. self.extra_path = None self.install_path_file = 0 @@ -119,79 +123,36 @@ class install (Command): self.build_base = None self.build_lib = None + # Not defined yet because we don't know anything about + # documentation yet. #self.install_man = None #self.install_html = None #self.install_info = None - self.compile_py = 1 - self.optimize_py = 1 - def finalize_options (self): - # XXX this method is where the default installation directories - # for modules and extension modules are determined. (Someday, - # the default installation directories for scripts, - # documentation, and whatever else the Distutils can build and - # install will also be determined here.) Thus, this is a pretty - # important place to fiddle with for anyone interested in - # installation schemes for the Python library. Issues that - # are not yet resolved to my satisfaction: - # * how much platform dependence should be here, and - # how much can be pushed off to sysconfig (or, better, the - # Makefiles parsed by sysconfig)? - # * how independent of Python version should this be -- ie. - # should we have special cases to handle Python 1.5 and - # older, and then do it "the right way" for 1.6? Or should - # we install a site.py along with Distutils under pre-1.6 - # Python to take care of the current deficiencies in - # Python's library installation scheme? - # - # Currently, this method has hacks to distinguish POSIX from - # non-POSIX systems (for installation of site-local modules), - # and assumes the Python 1.5 installation tree with no site.py - # to fix things. - - # Figure out actual installation directories; the basic principle - # is: ... - - - - # Logic: - # * any: (prefix or exec-prefix or home) and (base or platbase) - # supplied: error - # * Windows/Mac OS: exec-prefix or home supplied: warn and ignore - # - # * Unix: home set - # (select the unix_home scheme) - # * Unix: neither prefix nor home set - # (set prefix=sys_prefix and carry on) - # * Unix: prefix set but not exec-prefix - # (set exec-prefix=prefix and carry on) - # * Unix: prefix set - # (select the unix_prefix scheme) - # - # * Windows/Mac OS: prefix not set - # (set prefix = sys_prefix and carry on) - # * Windows/Mac OS: prefix set - # (select the appropriate scheme) + # This method (and its pliant slaves, like 'finalize_unix()', + # 'finalize_other()', and 'select_scheme()') is where the default + # installation directories for modules, extension modules, and + # anything else we care to install from a Python module + # distribution. Thus, this code makes a pretty important policy + # statement about how third-party stuff is added to a Python + # installation! Note that the actual work of installation is done + # by the relatively simple 'install_*' commands; they just take + # their orders from the installation directory options determined + # here. - # "select a scheme" means: - # - set install-base and install-platbase - # - subst. base/platbase/version into the values of the - # particular scheme dictionary - # - use the resultings strings to set install-lib, etc. + # Check for errors/inconsistencies in the options; first, stuff + # that's wrong on any platform. - sys_prefix = os.path.normpath (sys.prefix) - sys_exec_prefix = os.path.normpath (sys.exec_prefix) - - # Check for errors/inconsistencies in the options if ((self.prefix or self.exec_prefix or self.home) and (self.install_base or self.install_platbase)): raise DistutilsOptionError, \ ("must supply either prefix/exec-prefix/home or " + "install-base/install-platbase -- not both") + # Next, stuff that's wrong (or dubious) only on certain platforms. if os.name == 'posix': if self.home and (self.prefix or self.exec_prefix): raise DistutilsOptionError, \ @@ -240,7 +201,7 @@ class install (Command): self.install_libbase = self.install_lib # needed for .pth file self.install_lib = os.path.join (self.install_lib, self.extra_dirs) - # Figure out the build directories, ie. where to install from + # Find out the build directories, ie. where to install from. self.set_undefined_options ('build', ('build_base', 'build_base'), ('build_lib', 'build_lib')) @@ -320,6 +281,12 @@ class install (Command): def select_scheme (self, name): + # "select a scheme" means: + # - set install-base and install-platbase + # - subst. base/platbase/version into the values of the + # particular scheme dictionary + # - use the resultings strings to set install-lib, etc. + # it's the caller's problem if they supply a bad name! scheme = INSTALL_SCHEMES[name] -- cgit v1.2.1 From 51d7237f93809f4d6de8ed358a525777793e74d7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 23 Mar 2000 04:37:11 +0000 Subject: Fixed the class name. --- command/install_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index 33cf6894..eaaa1c7d 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,7 +6,7 @@ import sys, string from distutils.core import Command from distutils.util import copy_tree -class install_py (Command): +class install_lib (Command): description = "install pure Python modules" -- cgit v1.2.1 From 89cec6fde3704c17ce83732bee0c0da4ef54a7a4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 23 Mar 2000 04:38:36 +0000 Subject: Fixed '_nt_quote_args()': backwards logic reversed, and now it actually returns a value. --- spawn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spawn.py b/spawn.py index ae3d09fa..4e6f2069 100644 --- a/spawn.py +++ b/spawn.py @@ -53,9 +53,9 @@ def _nt_quote_args (args): # quoting?) for i in range (len (args)): - if string.find (args[i], ' ') == -1: + if string.find (args[i], ' ') != -1: args[i] = '"%s"' % args[i] - + return def _spawn_nt (cmd, search_path=1, -- cgit v1.2.1 From 890ea186e16b49a61d19f9a9055c5e67a106ce3d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 23 Mar 2000 04:39:16 +0000 Subject: Import fix. --- util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.py b/util.py index b308b90d..caf99062 100644 --- a/util.py +++ b/util.py @@ -11,7 +11,7 @@ file causing it.""" __revision__ = "$Id$" -import os, string, re, shutil +import sys, os, string, re, shutil from distutils.errors import * -- cgit v1.2.1 From db89798d96147692062b0ecad0513f2476082c3e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:37:09 +0000 Subject: Added 'runtime_library_dirs' parameter to 'link_*()' methods. Split '_fix_link_args()' up into '_fix_object_args()' (for use of 'create_static_lib() and link methods) and '_fix_lib_args()' (for the link methods only). --- ccompiler.py | 71 ++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 4a8c1d38..ffad2947 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -338,16 +338,11 @@ class CCompiler: # _prep_compile () - def _fix_link_args (self, objects, output_dir, - takes_libs=0, libraries=None, library_dirs=None): - """Typecheck and fix up some of the arguments supplied to the - 'link_*' methods and return the fixed values. Specifically: - ensure that 'objects' is a list; if output_dir is None, use - self.output_dir; ensure that 'libraries' and 'library_dirs' are - both lists, and augment them with 'self.libraries' and - 'self.library_dirs'. If 'takes_libs' is true, return a tuple - (objects, output_dir, libraries, library_dirs; else return - (objects, output_dir).""" + def _fix_object_args (self, objects, output_dir): + """Typecheck and fix up some arguments supplied to various + methods. Specifically: ensure that 'objects' is a list; if + output_dir is None, replace with self.output_dir. Return fixed + versions of 'objects' and 'output_dir'.""" if type (objects) not in (ListType, TupleType): raise TypeError, \ @@ -359,28 +354,45 @@ class CCompiler: elif type (output_dir) is not StringType: raise TypeError, "'output_dir' must be a string or None" - if takes_libs: - if libraries is None: - libraries = self.libraries - elif type (libraries) in (ListType, TupleType): - libraries = list (libraries) + (self.libraries or []) - else: - raise TypeError, \ - "'libraries' (if supplied) must be a list of strings" + return (objects, output_dir) - if library_dirs is None: - library_dirs = self.library_dirs - elif type (library_dirs) in (ListType, TupleType): - library_dirs = list (library_dirs) + (self.library_dirs or []) - else: - raise TypeError, \ - "'library_dirs' (if supplied) must be a list of strings" - return (objects, output_dir, libraries, library_dirs) + def _fix_lib_args (self, libraries, library_dirs, runtime_library_dirs): + """Typecheck and fix up some of the arguments supplied to the + 'link_*' methods. Specifically: ensure that all arguments are + lists, and augment them with their permanent versions + (eg. 'self.libraries' augments 'libraries'). Return a tuple + with fixed versions of all arguments.""" + + if libraries is None: + libraries = self.libraries + elif type (libraries) in (ListType, TupleType): + libraries = list (libraries) + (self.libraries or []) + else: + raise TypeError, \ + "'libraries' (if supplied) must be a list of strings" + + if library_dirs is None: + library_dirs = self.library_dirs + elif type (library_dirs) in (ListType, TupleType): + library_dirs = list (library_dirs) + (self.library_dirs or []) else: - return (objects, output_dir) + raise TypeError, \ + "'library_dirs' (if supplied) must be a list of strings" + + if runtime_library_dirs is None: + runtime_library_dirs = self.runtime_library_dirs + elif type (runtime_library_dirs) in (ListType, TupleType): + runtime_library_dirs = (list (runtime_library_dirs) + + (self.runtime_library_dirs or [])) + else: + raise TypeError, \ + "'runtime_library_dirs' (if supplied) " + \ + "must be a list of strings" + + return (libraries, library_dirs, runtime_library_dirs) - # _fix_link_args () + # _fix_lib_args () def _need_link (self, objects, output_file): @@ -480,6 +492,7 @@ class CCompiler: output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -522,6 +535,7 @@ class CCompiler: output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -540,6 +554,7 @@ class CCompiler: output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): -- cgit v1.2.1 From 3aa1d946cf86103148fc70e553115286b9777694 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:40:19 +0000 Subject: Added 'runtime_library_dirs' parameter to 'link_*()' methods, and changed to use it when linking. Call '_fix_object_args()' and/or '_fix_lib_args()' as appropriate, rather than just '_fix_link_args()'. --- unixccompiler.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index ec85571d..ec766f54 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -146,7 +146,7 @@ class UnixCCompiler (CCompiler): output_dir=None, debug=0): - (objects, output_dir) = self._fix_link_args (objects, output_dir, takes_libs=0) + (objects, output_dir) = self._fix_object_args (objects, output_dir) output_filename = \ self.library_filename (output_libname, output_dir=output_dir) @@ -169,6 +169,7 @@ class UnixCCompiler (CCompiler): output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -178,6 +179,7 @@ class UnixCCompiler (CCompiler): output_dir, libraries, library_dirs, + runtime_library_dirs, debug, extra_preargs, extra_postargs) @@ -189,16 +191,17 @@ class UnixCCompiler (CCompiler): output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): - (objects, output_dir, libraries, library_dirs) = \ - self._fix_link_args (objects, output_dir, takes_libs=1, - libraries=libraries, library_dirs=library_dirs) + (objects, output_dir) = self._fix_object_args (objects, output_dir) + (libraries, library_dirs, runtime_library_dirs) = \ + self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) lib_opts = gen_lib_options (self, - library_dirs, self.runtime_library_dirs, + library_dirs, runtime_library_dirs, libraries) if type (output_dir) not in (StringType, NoneType): raise TypeError, "'output_dir' must be a string or None" @@ -228,16 +231,17 @@ class UnixCCompiler (CCompiler): output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): - (objects, output_dir, libraries, library_dirs) = \ - self._fix_link_args (objects, output_dir, takes_libs=1, - libraries=libraries, library_dirs=library_dirs) + (objects, output_dir) = self._fix_object_args (objects, output_dir) + (libraries, library_dirs, runtime_library_dirs) = \ + self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) lib_opts = gen_lib_options (self, - library_dirs, self.runtime_library_dirs, + library_dirs, runtime_library_dirs, libraries) output_filename = output_progname # Unix-ism! if output_dir is not None: -- cgit v1.2.1 From f5e23859b6bba32958929ab54176f28e01a5b9f0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:42:28 +0000 Subject: Added 'runtime_library_dirs' parameter to 'link_*()' methods, and warn that we don't know what to do with it when we see it. Call '_fix_object_args()' and/or '_fix_lib_args()' as appropriate, rather than just '_fix_link_args()'. --- msvccompiler.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 7324b8e1..bc27cea9 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -253,8 +253,7 @@ class MSVCCompiler (CCompiler) : extra_preargs=None, extra_postargs=None): - (objects, output_dir) = \ - self._fix_link_args (objects, output_dir, takes_libs=0) + (objects, output_dir) = self._fix_object_args (objects, output_dir) output_filename = \ self.library_filename (output_libname, output_dir=output_dir) @@ -279,6 +278,7 @@ class MSVCCompiler (CCompiler) : output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -299,16 +299,21 @@ class MSVCCompiler (CCompiler) : output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): - (objects, output_dir, libraries, library_dirs) = \ - self._fix_link_args (objects, output_dir, takes_libs=1, - libraries=libraries, library_dirs=library_dirs) + (objects, output_dir) = self._fix_object_args (objects, output_dir) + (libraries, library_dirs, runtime_library_dirs) = \ + self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) + + if self.runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) lib_opts = gen_lib_options (self, - library_dirs, self.runtime_library_dirs, + library_dirs, runtime_library_dirs, libraries) if type (output_dir) not in (StringType, NoneType): raise TypeError, "'output_dir' must be a string or None" -- cgit v1.2.1 From 362881283944c95f424b461590f9d2f81e378d0a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:45:14 +0000 Subject: Fixed a bunch of screwed-up logic and inconsistent terminology. Fixed 'build_extensions()' to pay attention to the 'rpath' element of the 'build_info' dictionary. --- command/build_ext.py | 52 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 1d8794a6..ff680fa9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -56,7 +56,7 @@ class build_ext (Command): "C preprocessor macros to define"), ('undef=', 'U', "C preprocessor macros to undefine"), - ('libs=', 'l', + ('libraries=', 'l', "external C libraries to link with"), ('library-dirs=', 'L', "directories to search for external C libraries"), @@ -79,7 +79,7 @@ class build_ext (Command): self.include_dirs = None self.define = None self.undef = None - self.libs = None + self.libraries = None self.library_dirs = None self.rpath = None self.link_objects = None @@ -117,8 +117,8 @@ class build_ext (Command): if exec_py_include != py_include: self.include_dirs.insert (0, exec_py_include) - if type (self.libs) is StringType: - self.libs = [self.libs] + if type (self.libraries) is StringType: + self.libraries = [self.libraries] # XXX how the heck are 'self.define' and 'self.undef' supposed to # be set? @@ -144,11 +144,33 @@ class build_ext (Command): # First, sanity-check the 'self.extensions' list self.check_extensions_list (self.extensions) + # Simplify the following logic (eg. don't have to worry about + # appending to None) + if self.libraries is None: + self.libraries = [] + if self.library_dirs is None: + self.library_dirs = [] + if self.rpath is None: + self.rpath = [] + + # If we were asked to build any C/C++ libraries, make sure that the + # directory where we put them is in the library search path for + # linking extensions. + if self.distribution.libraries: + build_clib = self.find_peer ('build_clib') + self.libraries.extend (build_clib.get_library_names() or []) + self.library_dirs.append (build_clib.build_clib) + # Setup the CCompiler object that we'll use to do all the # compiling and linking self.compiler = new_compiler (verbose=self.verbose, dry_run=self.dry_run, force=self.force) + + # And make sure that any compile/link-related options (which might + # come from the command-line or from the setup script) are set in + # that CCompiler object -- that way, they automatically apply to + # all compiling and linking done here. if self.include_dirs is not None: self.compiler.set_include_dirs (self.include_dirs) if self.define is not None: @@ -158,8 +180,8 @@ class build_ext (Command): if self.undef is not None: for macro in self.undef: self.compiler.undefine_macro (macro) - if self.libs is not None: - self.compiler.set_libraries (self.libs) + if self.libraries is not None: + self.compiler.set_libraries (self.libraries) if self.library_dirs is not None: self.compiler.set_library_dirs (self.library_dirs) if self.rpath is not None: @@ -167,15 +189,7 @@ class build_ext (Command): if self.link_objects is not None: self.compiler.set_link_objects (self.link_objects) - if self.distribution.libraries: - build_clib = self.find_peer ('build_clib') - self.libraries = build_clib.get_library_names () or [] - self.library_dirs = [build_clib.build_clib] - else: - self.libraries = [] - self.library_dirs = [] - - # Now the real loop over extensions + # Now actually compile and link everything. self.build_extensions (self.extensions) @@ -257,10 +271,9 @@ class build_ext (Command): extra_objects = build_info.get ('extra_objects') if extra_objects: objects.extend (extra_objects) - libraries = (self.libraries + - (build_info.get ('libraries') or [])) - library_dirs = (self.library_dirs + - (build_info.get ('library_dirs') or [])) + libraries = build_info.get ('libraries') + library_dirs = build_info.get ('library_dirs') + rpath = build_info.get ('rpath') extra_args = build_info.get ('extra_link_args') or [] if self.compiler.compiler_type == 'msvc': @@ -299,6 +312,7 @@ class build_ext (Command): self.compiler.link_shared_object (objects, ext_filename, libraries=libraries, library_dirs=library_dirs, + runtime_library_dirs=rpath, extra_postargs=extra_args, debug=self.debug) -- cgit v1.2.1 From 5da6867520bab8d1abc3f7699207ac55bc6a03f9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:47:00 +0000 Subject: Duh, it helps if '_nt_quote_args()' actually returns the mutated list, rather than None. --- spawn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spawn.py b/spawn.py index 4e6f2069..86ea3dfb 100644 --- a/spawn.py +++ b/spawn.py @@ -55,7 +55,7 @@ def _nt_quote_args (args): for i in range (len (args)): if string.find (args[i], ' ') != -1: args[i] = '"%s"' % args[i] - return + return args def _spawn_nt (cmd, search_path=1, -- cgit v1.2.1 From ece89d8b1b55c795e1de3e8842e22ca3eebd3f3d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:48:43 +0000 Subject: Beefed up error-handling in 'setup()' a smidge: handle OSError and DistutilsExecError now. --- core.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core.py b/core.py index df364676..5090f25b 100644 --- a/core.py +++ b/core.py @@ -98,13 +98,15 @@ def setup (**attrs): dist.run_commands () except KeyboardInterrupt: raise SystemExit, "interrupted" - except IOError, exc: + except (OSError, IOError), exc: # arg, try to work with Python pre-1.5.2 if hasattr (exc, 'filename') and hasattr (exc, 'strerror'): raise SystemExit, \ "error: %s: %s" % (exc.filename, exc.strerror) else: raise SystemExit, str (exc) + except DistutilsExecError, msg: + raise SystemExit, "error: " + str (msg) # setup () -- cgit v1.2.1 From 05135cd1557034010a85409726c822d34dcc5043 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:10:51 +0000 Subject: Deleted some crufty comments and code. A host of improvements in preparation for the 'bdist' command: - added 'get_outputs()' method (all the other improvements were to support this addition) - made 'find_package_modules()' and 'find_modules()' return similar values (list of (package, module, module_filename) tuples) - factored 'find_all_modules()' out of 'get_source_files()' (needed by 'get_outputs()') - factored 'get_module_outfile()' out of 'build_module()' (also needed by 'get_outputs()') - various little tweaks, improvements, comment/doc updates --- command/build_py.py | 85 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 4e5255a6..3baddc62 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -58,17 +58,6 @@ class build_py (Command): # installation will look like (ie. we preserve mode when # installing). - # XXX copy_file does *not* preserve MacOS-specific file metadata. - # If this is a problem for building/installing Python modules, then - # we'll have to fix copy_file. (And what about installing scripts, - # when the time comes for that -- does MacOS use its special - # metadata to know that a file is meant to be interpreted by - # Python?) - - infiles = [] - outfiles = [] - missing = [] - # Two options control which modules will be installed: 'packages' # and 'modules'. The former lets us work with whole packages, not # specifying individual modules at all; the latter is for @@ -171,16 +160,17 @@ class build_py (Command): def find_package_modules (self, package, package_dir): + self.check_package (package, package_dir) module_files = glob (os.path.join (package_dir, "*.py")) - module_pairs = [] + modules = [] setup_script = os.path.abspath (sys.argv[0]) for f in module_files: abs_f = os.path.abspath (f) if abs_f != setup_script: module = os.path.splitext (os.path.basename (f))[0] - module_pairs.append ((module, f)) - return module_pairs + modules.append ((package, module, f)) + return modules def find_modules (self): @@ -222,14 +212,19 @@ class build_py (Command): if not self.check_module (module, module_file): continue - modules.append ((module, package, module_file)) + modules.append ((package, module, module_file)) return modules # find_modules () - def get_source_files (self): + def find_all_modules (self): + """Compute the list of all modules that will be built, whether + they are specified one-module-at-a-time ('self.modules') or + by whole packages ('self.packages'). Return a list of tuples + (package, module, module_file), just like 'find_modules()' and + 'find_package_modules()' do.""" if self.modules: modules = self.find_modules () @@ -240,18 +235,37 @@ class build_py (Command): m = self.find_package_modules (package, package_dir) modules.extend (m) - # Both find_modules() and find_package_modules() return a list of - # tuples where the last element of each tuple is the filename -- - # what a happy coincidence! + return modules + + # find_all_modules () + + + def get_source_files (self): + + modules = self.find_all_modules () filenames = [] for module in modules: filenames.append (module[-1]) - return filenames + return filenames - def build_module (self, module, module_file, package): + def get_module_outfile (self, build_dir, package, module): + outfile_path = [build_dir] + list(package) + [module + ".py"] + return apply (os.path.join, outfile_path) + + + def get_outputs (self): + modules = self.find_all_modules () + outputs = [] + for (package, module, module_file) in modules: + package = string.split (package, '.') + outputs.append (self.get_module_outfile (self.build_lib, + package, module)) + return outputs + + def build_module (self, module, module_file, package): if type (package) is StringType: package = string.split (package, '.') elif type (package) not in (ListType, TupleType): @@ -261,11 +275,7 @@ class build_py (Command): # Now put the module source file into the "build" area -- this is # easy, we just copy it somewhere under self.build_lib (the build # directory for Python source). - outfile_path = list (package) - outfile_path.append (module + ".py") - outfile_path.insert (0, self.build_lib) - outfile = apply (os.path.join, outfile_path) - + outfile = self.get_module_outfile (self.build_lib, package, module) dir = os.path.dirname (outfile) self.mkpath (dir) self.copy_file (module_file, outfile, preserve_mode=0) @@ -274,7 +284,7 @@ class build_py (Command): def build_modules (self): modules = self.find_modules() - for (module, package, module_file) in modules: + for (package, module, module_file) in modules: # Now "build" the module -- ie. copy the source file to # self.build_lib (the build directory for Python source). @@ -288,20 +298,23 @@ class build_py (Command): def build_packages (self): for package in self.packages: + + # Get list of (package, module, module_file) tuples based on + # scanning the package directory. 'package' is only included + # in the tuple so that 'find_modules()' and + # 'find_package_tuples()' have a consistent interface; it's + # ignored here (apart from a sanity check). Also, 'module' is + # the *unqualified* module name (ie. no dots, no package -- we + # already know its package!), and 'module_file' is the path to + # the .py file, relative to the current directory + # (ie. including 'package_dir'). package_dir = self.get_package_dir (package) - self.check_package (package, package_dir) - - # Get list of (module, module_file) tuples based on scanning - # the package directory. Here, 'module' is the *unqualified* - # module name (ie. no dots, no package -- we already know its - # package!), and module_file is the path to the .py file, - # relative to the current directory (ie. including - # 'package_dir'). modules = self.find_package_modules (package, package_dir) # Now loop over the modules we found, "building" each one (just # copy it to self.build_lib). - for (module, module_file) in modules: + for (package_, module, module_file) in modules: + assert package == package_ self.build_module (module, module_file, package) # build_packages () -- cgit v1.2.1 From be2cdd814051b3e077646a20ad98330a52e2f707 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:13:09 +0000 Subject: Added 'get_outputs()' in prepartion for the 'bdist' command. Changed signature of 'build_extensions()': no longer takes the extension list, but uses 'self.extensions' (just like 'get_outputs()' has to) Moved call to 'check_extensions_list()' from 'run()' to 'build_extensions()', again for consistency with 'get_outputs()'. --- command/build_ext.py | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index ff680fa9..7d88c063 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -141,9 +141,6 @@ class build_ext (Command): if not self.extensions: return - # First, sanity-check the 'self.extensions' list - self.check_extensions_list (self.extensions) - # Simplify the following logic (eg. don't have to worry about # appending to None) if self.libraries is None: @@ -156,7 +153,7 @@ class build_ext (Command): # If we were asked to build any C/C++ libraries, make sure that the # directory where we put them is in the library search path for # linking extensions. - if self.distribution.libraries: + if self.distribution.has_c_libraries(): build_clib = self.find_peer ('build_clib') self.libraries.extend (build_clib.get_library_names() or []) self.library_dirs.append (build_clib.build_clib) @@ -190,9 +187,10 @@ class build_ext (Command): self.compiler.set_link_objects (self.link_objects) # Now actually compile and link everything. - self.build_extensions (self.extensions) + self.build_extensions () + + # run () - def check_extensions_list (self, extensions): """Ensure that the list of extensions (presumably provided as a @@ -239,9 +237,32 @@ class build_ext (Command): return filenames + def get_outputs (self): + + # Sanity check the 'extensions' list -- can't assume this is being + # done in the same run as a 'build_extensions()' call (in fact, we + # can probably assume that it *isn't*!). + self.check_extensions_list (self.extensions) + + # And build the list of output (built) filenames. Note that this + # ignores the 'inplace' flag, and assumes everything goes in the + # "build" tree. + outputs = [] + for (extension_name, build_info) in self.extensions: + fullname = self.get_ext_fullname (extension_name) + outputs.append (os.path.join (self.build_lib, + self.get_ext_filename(fullname))) + return outputs + + # get_outputs () + + def build_extensions (self, extensions): - for (extension_name, build_info) in extensions: + # First, sanity-check the 'extensions' list + self.check_extensions_list (self.extensions) + + for (extension_name, build_info) in self.extensions: sources = build_info.get ('sources') if sources is None or type (sources) not in (ListType, TupleType): raise DistutilsValueError, \ @@ -290,7 +311,7 @@ class build_ext (Command): else: modname = string.split (extension_name, '.')[-1] extra_args.append('/export:init%s'%modname) - # end if MSVC + # if MSVC fullname = self.get_ext_fullname (extension_name) if self.inplace: -- cgit v1.2.1 From d71b7f8620d2a8953c38ccc08541db8a7f087c51 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:14:21 +0000 Subject: Use the new 'has_pure_modules()', 'has_ext_modules()', 'has_c_libraries()' methods of Distribution instead of grovelling directly in self.distribution. --- command/build.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/build.py b/command/build.py index 97466fdb..2da589ab 100644 --- a/command/build.py +++ b/command/build.py @@ -80,18 +80,18 @@ class build (Command): # Invoke the 'build_py' command to "build" pure Python modules # (ie. copy 'em into the build tree) - if self.distribution.packages or self.distribution.py_modules: + if self.distribution.has_pure_modules(): self.run_peer ('build_py') # Build any standalone C libraries next -- they're most likely to # be needed by extension modules, so obviously have to be done # first! - if self.distribution.libraries: + if self.distribution.has_c_libraries(): self.run_peer ('build_clib') # And now 'build_ext' -- compile extension modules and put them # into the build tree - if self.distribution.ext_modules: + if self.distribution.has_ext_modules(): self.run_peer ('build_ext') # end class Build -- cgit v1.2.1 From 5d534b66bc25e3d1455e98944b5e5b6dfb1b7eb1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:15:57 +0000 Subject: Changed so the sub-commands we rely on to do the real work is specified in a class attribute 'sub_commands', rather than hard-coded in 'run()'. This should make it easier to subclass 'install', and also makes it easier to keep 'run()' and the new 'get_outputs()' consistent. Added 'get_outputs()' in preparation for the 'bdist' command. --- command/install.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/command/install.py b/command/install.py index ed74971e..ac9ec865 100644 --- a/command/install.py +++ b/command/install.py @@ -82,6 +82,14 @@ class install (Command): ] + # 'sub_commands': a list of commands this command might have to run + # to get its work done. Each command is represented as a tuple + # (func, command) where 'func' is a function to call that returns + # true if 'command' (the sub-command name, a string) needs to be + # run. If 'func' is None, assume that 'command' must always be run. + sub_commands = [(None, 'install_lib')] + + def initialize_options (self): # High-level options: these select both an installation base @@ -355,10 +363,11 @@ class install (Command): # Obviously have to build before we can install self.run_peer ('build') - # Now install all Python modules -- don't bother to make this - # conditional; why would someone distribute a Python module - # distribution without Python modules? - self.run_peer ('install_lib') + # Run all sub-commands: currently this just means install all + # Python modules using 'install_lib'. + for (func, cmd) in self.sub_commands: + if func is None or func(): + self.run_peer (cmd) if self.path_file: self.create_path_file () @@ -374,6 +383,17 @@ class install (Command): # run () + def get_outputs (self): + # This command doesn't have any outputs of its own, so just + # get the outputs of all its sub-commands. + outputs = [] + for (func, cmd) in self.sub_commands: + if func is None or func(): + outputs.extend (self.run_peer (cmd)) + + return outputs + + def create_path_file (self): filename = os.path.join (self.install_libbase, self.path_file + ".pth") -- cgit v1.2.1 From 66878bbd6f4d2c871a13c3eb34a8c8a988f7eaa3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:17:42 +0000 Subject: Be sure to run both 'build_py' and 'build_ext', now that this command is responsible for installing all Python modules (pure and extensions). Added 'get_outputs()' in preparation for the 'bdist' command, and '_mutate_outputs()' to support 'get_outputs()'. --- command/install_lib.py | 52 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index eaaa1c7d..7e1f2e2a 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -2,7 +2,7 @@ __revision__ = "$Id$" -import sys, string +import sys, os, string from distutils.core import Command from distutils.util import copy_tree @@ -39,14 +39,17 @@ class install_lib (Command): def run (self): - # Make sure we have "built" all pure Python modules first - self.run_peer ('build_py') + # Make sure we have built everything we need first + if self.distribution.has_pure_modules(): + self.run_peer ('build_py') + if self.distribution.has_ext_modules(): + self.run_peer ('build_ext') # Install everything: simply dump the entire contents of the build # directory to the installation directory (that's the beauty of # having a build directory!) outfiles = self.copy_tree (self.build_dir, self.install_dir) - + # (Optionally) compile .py to .pyc # XXX hey! we can't control whether we optimize or not; that's up # to the invocation of the current Python interpreter (at least @@ -73,4 +76,43 @@ class install_lib (Command): # run () -# class InstallPy + + def _mutate_outputs (self, has_any, build_cmd, cmd_option, output_dir): + + if not has_any: + return [] + + build_cmd = self.find_peer (build_cmd) + build_files = build_cmd.get_outputs() + build_dir = build_cmd.get_option (cmd_option) + + prefix_len = len (build_dir) + len (os.sep) + outputs = [] + for file in build_files: + outputs.append (os.path.join (output_dir, file[prefix_len:])) + + return outputs + + # _mutate_outputs () + + def get_outputs (self): + """Return the list of files that would be installed if this command + were actually run. Not affected by the "dry-run" flag or whether + modules have actually been built yet.""" + + pure_outputs = \ + self._mutate_outputs (self.distribution.has_pure_modules(), + 'build_py', 'build_lib', + self.install_dir) + + + ext_outputs = \ + self._mutate_outputs (self.distribution.has_ext_modules(), + 'build_ext', 'build_lib', + self.install_dir) + + return pure_outputs + ext_outputs + + # get_outputs () + +# class install_lib -- cgit v1.2.1 From f925cbf975d85ad247f6d790159af67f3967d7b2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:18:39 +0000 Subject: Added 'build_clib'; replaced 'install_py' and 'install_ext' with 'install_lib'. --- command/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/__init__.py b/command/__init__.py index d2b37a8c..b7973c61 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -8,9 +8,9 @@ __revision__ = "$Id$" __all__ = ['build', 'build_py', 'build_ext', + 'build_clib', 'install', - 'install_py', - 'install_ext', + 'install_lib', 'clean', 'sdist', ] -- cgit v1.2.1 From 104be39562c08bd0c87944ce3a13014a30459c43 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:20:45 +0000 Subject: Added the "distribution query" methods: 'has_pure_modules()', 'has_ext_modules()', 'has_c_libraries()', 'has_modules()', and 'is_pure()'. --- core.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index 5090f25b..08a1d641 100644 --- a/core.py +++ b/core.py @@ -618,7 +618,27 @@ class Distribution: return tuple (values) -# end class Distribution + + # -- Distribution query methods ------------------------------------ + + def has_pure_modules (self): + return len (self.packages or self.py_modules or []) > 0 + + def has_ext_modules (self): + return self.ext_modules and len (self.ext_modules) > 0 + + def has_c_libraries (self): + return self.libraries and len (self.libraries) > 0 + + def has_modules (self): + return self.has_pure_modules() or self.has_ext_modules() + + def is_pure (self): + return (self.has_pure_modules() and + not self.has_ext_modules() and + not self.has_c_libraries()) + +# class Distribution class Command: @@ -992,4 +1012,4 @@ class Command: # make_file () -# end class Command +# class Command -- cgit v1.2.1 From 4aa19c2a2cd09156c6706104f484738302ca7901 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:47:29 +0000 Subject: Moved the guts of 'make_tarball()' and 'make_zipfile()' to distutils.util in preparation for the 'bdist_dumb' command; these methods remain as trivial wrappers around the versions in distutils.util. --- command/sdist.py | 57 ++++---------------------------------------------------- 1 file changed, 4 insertions(+), 53 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 0c151778..19e17731 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,7 +11,7 @@ import fnmatch from types import * from glob import glob from distutils.core import Command -from distutils.util import newer, remove_tree +from distutils.util import newer, remove_tree, make_tarball, make_zipfile from distutils.text_file import TextFile from distutils.errors import DistutilsExecError @@ -503,60 +503,11 @@ class sdist (Command): # make_release_tree () - def make_tarball (self, base_dir, compress="gzip"): - - # XXX GNU tar 1.13 has a nifty option to add a prefix directory. - # It's pretty new, though, so we certainly can't require it -- - # but it would be nice to take advantage of it to skip the - # "create a tree of hardlinks" step! (Would also be nice to - # detect GNU tar to use its 'z' option and save a step.) - - if compress is not None and compress not in ('gzip', 'compress'): - raise ValueError, \ - "if given, 'compress' must be 'gzip' or 'compress'" - - archive_name = base_dir + ".tar" - self.spawn (["tar", "-cf", archive_name, base_dir]) - - if compress: - self.spawn ([compress, archive_name]) - + def make_tarball (self, base_dir, compress): + make_tarball (base_dir, compress, self.verbose, self.dry_run) def make_zipfile (self, base_dir): - - # This initially assumed the Unix 'zip' utility -- but - # apparently InfoZIP's zip.exe works the same under Windows, so - # no changes needed! - - try: - self.spawn (["zip", "-r", base_dir + ".zip", base_dir]) - except DistutilsExecError: - - # XXX really should distinguish between "couldn't find - # external 'zip' command" and "zip failed" -- shouldn't try - # again in the latter case. (I think fixing this will - # require some cooperation from the spawn module -- perhaps - # a utility function to search the path, so we can fallback - # on zipfile.py without the failed spawn.) - try: - import zipfile - except ImportError: - raise DistutilsExecError, \ - ("unable to create zip file '%s.zip': " + - "could neither find a standalone zip utility nor " + - "import the 'zipfile' module") % base_dir - - z = zipfile.ZipFile (base_dir + ".zip", "wb", - compression=zipfile.ZIP_DEFLATED) - - def visit (z, dirname, names): - for name in names: - path = os.path.join (dirname, name) - if os.path.isfile (path): - z.write (path, path) - - os.path.walk (base_dir, visit, z) - z.close() + make_zipfile (base_dir, self.verbose, self.dry_run) def make_distribution (self): -- cgit v1.2.1 From 872994b9087f38871f5fba8b6b0062db9a2714cf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:48:40 +0000 Subject: Added 'make_tarball()' and 'make_zipfile()' functions in preparation for the 'bdist_dumb' command. Adapted, with tweakage, from the 'sdist' command. --- util.py | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index caf99062..b40373c4 100644 --- a/util.py +++ b/util.py @@ -13,7 +13,7 @@ __revision__ = "$Id$" import sys, os, string, re, shutil from distutils.errors import * - +from distutils.spawn import spawn # cache for by mkpath() -- in addition to cheapening redundant calls, # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode @@ -316,7 +316,6 @@ def copy_tree (src, dst, verbose=0, dry_run=0): - """Copy an entire directory tree 'src' to a new location 'dst'. Both 'src' and 'dst' must be directory names. If 'src' is not a directory, raise DistutilsFileError. If 'dst' does not exist, it @@ -556,3 +555,92 @@ def subst_vars (str, local_vars): return re.sub (r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str) # subst_vars () + + +def make_tarball (base_dir, compress="gzip", verbose=0, dry_run=0): + """Create a (possibly compressed) tar file from all the files under + 'base_dir'. 'compress' must be "gzip" (the default), "compress", or + None. Both "tar" and the compression utility named by 'compress' + must be on the default program search path, so this is probably + Unix-specific. The output tar file will be named 'base_dir' + + ".tar", possibly plus the appropriate compression extension + (".gz" or ".Z"). Return the output filename.""" + + # XXX GNU tar 1.13 has a nifty option to add a prefix directory. + # It's pretty new, though, so we certainly can't require it -- + # but it would be nice to take advantage of it to skip the + # "create a tree of hardlinks" step! (Would also be nice to + # detect GNU tar to use its 'z' option and save a step.) + + compress_ext = { 'gzip': ".gz", + 'compress': ".Z" } + + if compress is not None and compress not in ('gzip', 'compress'): + raise ValueError, \ + "bad value for 'compress': must be None, 'gzip', or 'compress'" + + archive_name = base_dir + ".tar" + cmd = ["tar", "-cf", archive_name, base_dir] + spawn (cmd, verbose=verbose, dry_run=dry_run) + + if compress: + spawn ([compress, archive_name], verbose=verbose, dry_run=dry_run) + return archive_name + compress_ext[compress] + else: + return archive_name + +# make_tarball () + + +def make_zipfile (base_dir, verbose=0, dry_run=0): + """Create a ZIP file from all the files under 'base_dir'. The + output ZIP file will be named 'base_dir' + ".zip". Uses either the + InfoZIP "zip" utility (if installed and found on the default search + path) or the "zipfile" Python module (if available). If neither + tool is available, raises DistutilsExecError. Returns the name + of the output ZIP file.""" + + # This initially assumed the Unix 'zip' utility -- but + # apparently InfoZIP's zip.exe works the same under Windows, so + # no changes needed! + + zip_filename = base_dir + ".zip" + try: + spawn (["zip", "-r", zip_filename, base_dir], + verbose=verbose, dry_run=dry_run) + except DistutilsExecError: + + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed" -- shouldn't try + # again in the latter case. (I think fixing this will + # require some cooperation from the spawn module -- perhaps + # a utility function to search the path, so we can fallback + # on zipfile.py without the failed spawn.) + try: + import zipfile + except ImportError: + raise DistutilsExecError, \ + ("unable to create zip file '%s': " + + "could neither find a standalone zip utility nor " + + "import the 'zipfile' module") % zip_filename + + if verbose: + print "creating '%s' and adding '%s' to it" % \ + (zip_filename, base_dir) + + def visit (z, dirname, names): + for name in names: + path = os.path.join (dirname, name) + if os.path.isfile (path): + z.write (path, path) + + if not dry_run: + z = zipfile.ZipFile (zip_filename, "wb", + compression=zipfile.ZIP_DEFLATED) + + os.path.walk (base_dir, visit, z) + z.close() + + return zip_filename + +# make_zipfile () -- cgit v1.2.1 From 4546cfbf7ed2f8292aebd4f795132e0656a518d7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:53:02 +0000 Subject: Patch from Bastian Kleineidam : make 'mkdir()' return list of directories created. --- util.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/util.py b/util.py index b40373c4..6351bc7e 100644 --- a/util.py +++ b/util.py @@ -38,11 +38,11 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): # we're not using a recursive algorithm) name = os.path.normpath (name) - + created_dirs = [] if os.path.isdir (name) or name == '': - return + return created_dirs if PATH_CREATED.get (name): - return + return created_dirs (head, tail) = os.path.split (name) tails = [tail] # stack of lone dirs to create @@ -70,11 +70,13 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): if not dry_run: try: os.mkdir (head) + created_dirs.append(head) except os.error, (errno, errstr): raise DistutilsFileError, \ "could not create '%s': %s" % (head, errstr) PATH_CREATED[head] = 1 + return created_dirs # mkpath () -- cgit v1.2.1 From 408127fea3cd246776bfaac7c16cce3e250f0bee Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:56:34 +0000 Subject: Documented Bastian's patch. Made handling OSError in 'mkpath()' more standard. --- util.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/util.py b/util.py index 6351bc7e..8242e10f 100644 --- a/util.py +++ b/util.py @@ -24,11 +24,13 @@ PATH_CREATED = {} # succeed in that case). def mkpath (name, mode=0777, verbose=0, dry_run=0): """Create a directory and any missing ancestor directories. If the - directory already exists, return silently. Raise - DistutilsFileError if unable to create some directory along the - way (eg. some sub-path exists, but is a file rather than a - directory). If 'verbose' is true, print a one-line summary of - each mkdir to stdout.""" + directory already exists (or if 'name' is the empty string, which + means the current directory, which of course exists), then do + nothing. Raise DistutilsFileError if unable to create some + directory along the way (eg. some sub-path exists, but is a file + rather than a directory). If 'verbose' is true, print a one-line + summary of each mkdir to stdout. Return the list of directories + actually created.""" global PATH_CREATED @@ -71,9 +73,9 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): try: os.mkdir (head) created_dirs.append(head) - except os.error, (errno, errstr): + except OSError, exc: raise DistutilsFileError, \ - "could not create '%s': %s" % (head, errstr) + "could not create '%s': %s" % (head, exc[-1]) PATH_CREATED[head] = 1 return created_dirs -- cgit v1.2.1 From 6d6e77e2094148c5f828f314e7858209a4c78d25 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 03:27:45 +0000 Subject: Changed 'copy_tree()' so it returns the list of all files that were copied or might have been copied, regardless of the 'update' flag. --- util.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/util.py b/util.py index 8242e10f..27296194 100644 --- a/util.py +++ b/util.py @@ -322,13 +322,14 @@ def copy_tree (src, dst, """Copy an entire directory tree 'src' to a new location 'dst'. Both 'src' and 'dst' must be directory names. If 'src' is not a - directory, raise DistutilsFileError. If 'dst' does not exist, it - is created with 'mkpath()'. The end result of the copy is that - every file in 'src' is copied to 'dst', and directories under - 'src' are recursively copied to 'dst'. Return the list of files - copied (under their output names) -- note that if 'update' is true, - this might be less than the list of files considered. Return - value is not affected by 'dry_run'. + directory, raise DistutilsFileError. If 'dst' does not exist, it is + created with 'mkpath()'. The end result of the copy is that every + file in 'src' is copied to 'dst', and directories under 'src' are + recursively copied to 'dst'. Return the list of files that were + copied or might have been copied, using their output name. The + return value is unaffected by 'update' or 'dry_run': it is simply + the list of all files under 'src', with the names changed to be + under 'dst'. 'preserve_mode' and 'preserve_times' are the same as for 'copy_file'; note that they only apply to regular files, not to @@ -372,10 +373,10 @@ def copy_tree (src, dst, preserve_mode, preserve_times, preserve_symlinks, update, verbose, dry_run)) else: - if (copy_file (src_name, dst_name, - preserve_mode, preserve_times, - update, verbose, dry_run)): - outputs.append (dst_name) + copy_file (src_name, dst_name, + preserve_mode, preserve_times, + update, verbose, dry_run) + outputs.append (dst_name) return outputs -- cgit v1.2.1 From 90d8d95cef6f3195a62348dfcaf06afa616cdb65 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 03:29:34 +0000 Subject: Patch inspired by Bastian Kleineidam : use global __debug__ flag to determine if compiled files will be ".pyc" or ".pyo". Tweaked compilation output messages too. --- command/install_lib.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index 7e1f2e2a..64f7cbcf 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -59,21 +59,16 @@ class install_lib (Command): from py_compile import compile for f in outfiles: - # XXX can't assume this filename mapping! (what if - # we're running under "python -O"?) - # only compile the file if it is actually a .py file if f[-3:] == '.py': - out_fn = string.replace (f, '.py', '.pyc') - + out_fn = f + (__debug__ and "c" or "o") + compile_msg = "byte-compiling %s to %s" % \ + (f, os.path.basename (out_fn)) + skip_msg = "byte-compilation of %s skipped" % f self.make_file (f, out_fn, compile, (f,), - "byte-compiling %s" % f, - "byte-compilation of %s skipped" % f) - - # XXX ignore self.optimize for now, since we don't really know if - # we're compiling optimally or not, and couldn't pick what to do - # even if we did know. ;-( - + compile_msg, skip_msg) + + # run () -- cgit v1.2.1 From 93bb283e4ed96e98f1203996045abe8ddd0b69fa Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 04:13:00 +0000 Subject: Call 'find_exe()', not '_find_exe()'. --- msvccompiler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index bc27cea9..29899104 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -166,9 +166,9 @@ class MSVCCompiler (CCompiler) : if versions: version = versions[0] # highest version - self.cc = _find_exe("cl.exe", version) - self.link = _find_exe("link.exe", version) - self.lib = _find_exe("lib.exe", version) + self.cc = find_exe("cl.exe", version) + self.link = find_exe("link.exe", version) + self.lib = find_exe("lib.exe", version) set_path_env_var ('lib', version) set_path_env_var ('include', version) path=get_msvc_paths('path', version) -- cgit v1.2.1 From c8a944d9a695816880e7f7aec21b351aa7df6185 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 04:13:49 +0000 Subject: Put the Python "system" include dir last, rather than first. --- command/build_ext.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 7d88c063..96e7ce52 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -113,7 +113,9 @@ class build_ext (Command): self.include_dirs = string.split (self.include_dirs, os.pathsep) - self.include_dirs.insert (0, py_include) + # Put the Python "system" include dir at the end, so that + # any local include dirs take precedence. + self.include_dirs.append (py_include) if exec_py_include != py_include: self.include_dirs.insert (0, exec_py_include) -- cgit v1.2.1 From 97eeb322fc5bdf19b0fa8fe387c09b11addb040e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 30 Mar 2000 19:47:22 +0000 Subject: Oops: 'build_extensions()' no longer takes an 'extensions' list. --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 96e7ce52..f2e0b319 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -259,7 +259,7 @@ class build_ext (Command): # get_outputs () - def build_extensions (self, extensions): + def build_extensions (self): # First, sanity-check the 'extensions' list self.check_extensions_list (self.extensions) -- cgit v1.2.1 From 986617164899c1f6a746c99318c4bebe6a0a8646 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:50:04 +0000 Subject: Changed to use the new 'has_pure_modules()' and 'has_ext_modules()' methods provided by Distribution. Cosmetic and error message tweaks. Simplified 'make_release_tree()': * extracted 'distutils.util.create_tree()' * don't have to do hard-linking ourselves -- it's now handled by 'distutils.util.copy_file()' (although the detection of whether hard linking is available still needs to be factored out) Removed 'make_tarball()' and 'make_zipfile()' entirely -- their role is now amply filled by 'distutils.util.make_archive()'. Simplified 'make_distribution()': * use Distribution's new 'get_full_name()' method * use 'make_archive()' instead of if/elif/.../else on the archive format --- command/sdist.py | 71 +++++++++++++++----------------------------------------- 1 file changed, 19 insertions(+), 52 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 19e17731..7dbd4013 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,7 +11,8 @@ import fnmatch from types import * from glob import glob from distutils.core import Command -from distutils.util import newer, remove_tree, make_tarball, make_zipfile +from distutils.util import \ + newer, remove_tree, make_tarball, make_zipfile, create_tree from distutils.text_file import TextFile from distutils.errors import DistutilsExecError @@ -78,8 +79,8 @@ class sdist (Command): self.formats = [self.default_format[os.name]] except KeyError: raise DistutilsPlatformError, \ - "don't know how to build source distributions on " + \ - "%s platform" % os.name + "don't know how to create source distributions " + \ + "on platform %s" % os.name elif type (self.formats) is StringType: self.formats = string.split (self.formats, ',') @@ -219,18 +220,15 @@ class sdist (Command): if files: self.files.extend (files) - if self.distribution.packages or self.distribution.py_modules: + if self.distribution.has_pure_modules(): build_py = self.find_peer ('build_py') - build_py.ensure_ready () self.files.extend (build_py.get_source_files ()) - if self.distribution.ext_modules: + if self.distribution.has_ext_modules(): build_ext = self.find_peer ('build_ext') - build_ext.ensure_ready () self.files.extend (build_ext.get_source_files ()) - def search_dir (self, dir, pattern=None): """Recursively find files under 'dir' matching 'pattern' (a string containing a Unix-style glob pattern). If 'pattern' is None, @@ -465,16 +463,10 @@ class sdist (Command): def make_release_tree (self, base_dir, files): - # First get the list of directories to create - need_dir = {} - for file in files: - need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1 - need_dirs = need_dir.keys() - need_dirs.sort() - - # Now create them - for dir in need_dirs: - self.mkpath (dir) + # Create all the directories under 'base_dir' necessary to + # put 'files' there. + create_tree (base_dir, files, + verbose=self.verbose, dry_run=self.dry_run) # And walk over the list of files, either making a hard link (if # os.link exists) to each one that doesn't already exist in its @@ -483,44 +475,26 @@ class sdist (Command): # out-of-date, because by default we blow away 'base_dir' when # we're done making the distribution archives.) - try: - link = os.link + if hasattr (os, 'link'): # can make hard links on this system + link = 'hard' msg = "making hard links in %s..." % base_dir - except AttributeError: - link = 0 + else: # nope, have to copy + link = None msg = "copying files to %s..." % base_dir self.announce (msg) for file in files: dest = os.path.join (base_dir, file) - if link: - if not os.path.exists (dest): - self.execute (os.link, (file, dest), - "linking %s -> %s" % (file, dest)) - else: - self.copy_file (file, dest) + self.copy_file (file, dest, link=link) # make_release_tree () - def make_tarball (self, base_dir, compress): - make_tarball (base_dir, compress, self.verbose, self.dry_run) - - def make_zipfile (self, base_dir): - make_zipfile (base_dir, self.verbose, self.dry_run) - - def make_distribution (self): - # Don't warn about missing meta-data here -- should be done - # elsewhere. - name = self.distribution.name or "UNKNOWN" - version = self.distribution.version - - if version: - base_dir = "%s-%s" % (name, version) - else: - base_dir = name + # Don't warn about missing meta-data here -- should be (and is!) + # done elsewhere. + base_dir = self.distribution.get_full_name() # Remove any files that match "base_dir" from the fileset -- we # don't want to go distributing the distribution inside itself! @@ -528,14 +502,7 @@ class sdist (Command): self.make_release_tree (base_dir, self.files) for fmt in self.formats: - if fmt == 'gztar': - self.make_tarball (base_dir, compress='gzip') - elif fmt == 'ztar': - self.make_tarball (base_dir, compress='compress') - elif fmt == 'tar': - self.make_tarball (base_dir, compress=None) - elif fmt == 'zip': - self.make_zipfile (base_dir) + self.make_archive (base_dir, fmt, base_dir=base_dir) if not self.keep_tree: remove_tree (base_dir, self.verbose, self.dry_run) -- cgit v1.2.1 From fa293d87ba55e60937559c9647bcef24827cba77 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:52:02 +0000 Subject: Fixed 'get_outputs()' so it actually works. Added 'get_inputs()' (which is strikingly similar to 'get_outputs()' - sigh). Cosmetic tweaks. --- command/install.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/command/install.py b/command/install.py index ac9ec865..995fd875 100644 --- a/command/install.py +++ b/command/install.py @@ -365,9 +365,9 @@ class install (Command): # Run all sub-commands: currently this just means install all # Python modules using 'install_lib'. - for (func, cmd) in self.sub_commands: + for (func, cmd_name) in self.sub_commands: if func is None or func(): - self.run_peer (cmd) + self.run_peer (cmd_name) if self.path_file: self.create_path_file () @@ -387,13 +387,25 @@ class install (Command): # This command doesn't have any outputs of its own, so just # get the outputs of all its sub-commands. outputs = [] - for (func, cmd) in self.sub_commands: + for (func, cmd_name) in self.sub_commands: if func is None or func(): - outputs.extend (self.run_peer (cmd)) + cmd = self.find_peer (cmd_name) + outputs.extend (cmd.get_outputs()) return outputs + def get_inputs (self): + # XXX gee, this looks familiar ;-( + inputs = [] + for (func, cmd_name) in self.sub_commands: + if func is None or func(): + cmd = self.find_peer (cmd_name) + inputs.extend (cmd.get_inputs()) + + return inputs + + def create_path_file (self): filename = os.path.join (self.install_libbase, self.path_file + ".pth") -- cgit v1.2.1 From 3e2f02534c35d876d827a766dac2cf397092d04f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:53:07 +0000 Subject: Added 'get_inputs()'. --- command/install_lib.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/command/install_lib.py b/command/install_lib.py index 64f7cbcf..5740c5ee 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -110,4 +110,24 @@ class install_lib (Command): # get_outputs () + def get_inputs (self): + """Get the list of files that are input to this command, ie. the + files that get installed as they are named in the build tree. + The files in this list correspond one-to-one to the output + filenames returned by 'get_outputs()'.""" + + inputs = [] + + if self.distribution.has_pure_modules(): + build_py = self.find_peer ('build_py') + inputs.extend (build_py.get_outputs()) + + if self.distribution.has_ext_modules(): + build_ext = self.find_peer ('build_ext') + inputs.extend (build_ext.get_outputs()) + + return inputs + + + # class install_lib -- cgit v1.2.1 From bdd1b78665f1d2ad66df4add652548e04474a315 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:55:12 +0000 Subject: The 'bdist' command, for creating "built" (binary) distributions. Initial revision is pretty limited; it only knows how to generate "dumb" binary distributions, i.e. a tarball on Unix and a zip file on Windows. Also, due to limitations in the installation code, it only knows how to distribute Python library code. But hey, it's a start. --- command/bdist.py | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 command/bdist.py diff --git a/command/bdist.py b/command/bdist.py new file mode 100644 index 00000000..56308285 --- /dev/null +++ b/command/bdist.py @@ -0,0 +1,70 @@ +"""distutils.command.bdist + +Implements the Distutils 'bdist' command (create a built [binary] +distribution).""" + +# created 2000/03/29, Greg Ward + +__revision__ = "$Id$" + +import os, string +from distutils.core import Command + + +class bdist (Command): + + description = "create a built (binary) distribution" + + user_options = [('formats=', 'f', + "formats for distribution (tar, ztar, gztar, zip, ... )"), + ] + + # This won't do in reality: will need to distinguish RPM-ish Linux, + # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. + default_format = { 'posix': 'gztar', + 'nt': 'zip', } + + format_command = { 'gztar': 'bdist_dumb', + 'zip': 'bdist_dumb', } + + + def initialize_options (self): + self.formats = None + + # initialize_options() + + + def finalize_options (self): + if self.formats is None: + try: + self.formats = [self.default_format[os.name]] + except KeyError: + raise DistutilsPlatformError, \ + "don't know how to create built distributions " + \ + "on platform %s" % os.name + elif type (self.formats) is StringType: + self.formats = string.split (self.formats, ',') + + + # finalize_options() + + + def run (self): + + for format in self.formats: + cmd_name = self.format_command[format] + sub_cmd = self.find_peer (cmd_name) + sub_cmd.set_option ('format', format) + + # XXX blecchhh!! should formalize this: at least a + # 'forget_run()' (?) method, possibly complicate the + # 'have_run' dictionary to include some command state as well + # as the command name -- eg. in this case we might want + # ('bdist_dumb','zip') to be marked "have run", but not + # ('bdist_dumb','gztar'). + self.distribution.have_run[cmd_name] = 0 + self.run_peer (cmd_name) + + # run() + +# class bdist -- cgit v1.2.1 From 24d3df10c8c218a584fd5f3d5f1701fd31dc9730 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:56:34 +0000 Subject: The 'bdist_dumb' command, the first worker bee for use by 'bdist'. This is the command that actually creates "dumb" binary distributions, ie. tarballs and zip files that you just unpack under or . Very limited, but it's a start. --- command/bdist_dumb.py | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 command/bdist_dumb.py diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py new file mode 100644 index 00000000..4383a8fa --- /dev/null +++ b/command/bdist_dumb.py @@ -0,0 +1,131 @@ +"""distutils.command.bdist_dumb + +Implements the Distutils 'bdist_dumb' command (create a "dumb" built +distribution -- i.e., just an archive to be unpacked under $prefix or +$exec_prefix).""" + +# created 2000/03/29, Greg Ward + +__revision__ = "$Id$" + +import os +from distutils.core import Command +from distutils.util import get_platform, create_tree + + +class bdist_dumb (Command): + + description = "create a \"dumb\" built distribution" + + user_options = [('format=', 'f', + "archive format to create (tar, ztar, gztar, zip)"), + ] + + default_format = { 'posix': 'gztar', + 'nt': 'zip', } + + + def initialize_options (self): + self.format = None + + # initialize_options() + + + def finalize_options (self): + if self.format is None: + try: + self.format = self.default_format[os.name] + except KeyError: + raise DistutilsPlatformError, \ + ("don't know how to create dumb built distributions " + + "on platform %s") % os.name + + # finalize_options() + + + def run (self): + + self.run_peer ('build') + install = self.find_peer ('install') + inputs = install.get_inputs () + outputs = install.get_outputs () + assert (len (inputs) == len (outputs)) + + # First, strip the installation base directory (prefix or + # exec-prefix) from all the output filenames. + self.strip_base_dirs (outputs, install) + + # Figure out where to copy them to: "build/bdist" by default; this + # directory masquerades as prefix/exec-prefix (ie. we'll make the + # archive from 'output_dir'). + build_base = self.get_peer_option ('build', 'build_base') + output_dir = os.path.join (build_base, "bdist") + + # Copy the built files to the pseudo-installation tree. + self.make_install_tree (output_dir, inputs, outputs) + + # And make an archive relative to the root of the + # pseudo-installation tree. + archive_basename = "%s.%s" % (self.distribution.get_full_name(), + get_platform()) + self.make_archive (archive_basename, self.format, + root_dir=output_dir) + + # run() + + + def strip_base_dirs (self, outputs, install_cmd): + # XXX this throws away the prefix/exec-prefix distinction, and + # means we can only correctly install the resulting archive on a + # system where prefix == exec-prefix (but at least we can *create* + # it on one where they differ). I don't see a way to fix this + # without either 1) generating two archives, one for prefix and one + # for exec-prefix, or 2) putting absolute paths in the archive + # rather than making them relative to one of the prefixes. + + base = install_cmd.install_base + os.sep + platbase = install_cmd.install_platbase + os.sep + b_len = len (base) + pb_len = len (platbase) + for i in range (len (outputs)): + if outputs[i][0:b_len] == base: + outputs[i] = outputs[i][b_len:] + elif outputs[i][0:pb_len] == platbase: + outputs[i] = outputs[i][pb_len:] + else: + raise DistutilsInternalError, \ + ("installation output filename '%s' doesn't start " + + "with either install_base ('%s') or " + + "install_platbase ('%s')") % \ + (outputs[i], base, platbase) + + # strip_base_dirs() + + + def make_install_tree (self, output_dir, inputs, outputs): + + assert (len(inputs) == len(outputs)) + + # Create all the directories under 'output_dir' necessary to + # put 'outputs' there. + create_tree (output_dir, outputs, + verbose=self.verbose, dry_run=self.dry_run) + + + # XXX this bit of logic is duplicated in sdist.make_release_tree(): + # would be nice to factor it out... + if hasattr (os, 'link'): # can make hard links on this system + link = 'hard' + msg = "making hard links in %s..." % output_dir + else: # nope, have to copy + link = None + msg = "copying files to %s..." % output_dir + + for i in range (len(inputs)): + output = os.path.join (output_dir, outputs[i]) + self.copy_file (inputs[i], output, link=link) + + # make_install_tree () + + +# class bdist_dumb -- cgit v1.2.1 From 55e4b812a77be3b24b104184c9c8970a874c805c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:57:31 +0000 Subject: Added DistutilsInternalError. --- errors.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/errors.py b/errors.py index 86d91dd6..f9d5c8de 100644 --- a/errors.py +++ b/errors.py @@ -68,6 +68,11 @@ if type (RuntimeError) is types.ClassType: class DistutilsExecError (DistutilsError): pass + # DistutilsInternalError is raised on internal inconsistencies + # or impossibilities + class DistutilsInternalError (DistutilsError): + pass + # String-based exceptions else: DistutilsError = 'DistutilsError' @@ -80,5 +85,6 @@ else: DistutilsValueError = 'DistutilsValueError' DistutilsPlatformError = 'DistutilsPlatformError' DistutilsExecError = 'DistutilsExecError' + DistutilsInternalError = 'DistutilsInternalError' del types -- cgit v1.2.1 From 2ffc581e227586358f5f8e262afe5e6aac8e3cc8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:02:22 +0000 Subject: Added 'create_tree()'. Changes to 'copy_file()': * added support for making hard links and symlinks * noted that it silently clobbers existing files when copying, but blows up if destination exists when linking -- hmmm... * error message tweak Added 'base_name' parameter to 'make_tarball()' and 'make_zipfile()'. Added 'make_archive()' -- wrapper around 'make_tarball()' or 'make_zipfile()' to take care of the archive "root directory". --- util.py | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 135 insertions(+), 25 deletions(-) diff --git a/util.py b/util.py index 27296194..6674d0aa 100644 --- a/util.py +++ b/util.py @@ -19,6 +19,11 @@ from distutils.spawn import spawn # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode PATH_CREATED = {} +# for generating verbose output in 'copy_file()' +_copy_action = { None: 'copying', + 'hard': 'hard linking', + 'sym': 'symbolically linking' } + # I don't use os.makedirs because a) it's new to Python 1.5.2, and # b) it blows up if the directory already exists (I want to silently # succeed in that case). @@ -83,6 +88,30 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): # mkpath () +def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0): + + """Create all the empty directories under 'base_dir' needed to + put 'files' there. 'base_dir' is just the a name of a directory + which doesn't necessarily exist yet; 'files' is a list of filenames + to be interpreted relative to 'base_dir'. 'base_dir' + the + directory portion of every file in 'files' will be created if it + doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as + for 'mkpath()'.""" + + # First get the list of directories to create + need_dir = {} + for file in files: + need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1 + need_dirs = need_dir.keys() + need_dirs.sort() + + # Now create them + for dir in need_dirs: + mkpath (dir, mode, verbose, dry_run) + +# create_tree () + + def newer (source, target): """Return true if 'source' exists and is more recently modified than 'target', or if 'source' exists and 'target' doesn't. Return @@ -241,6 +270,7 @@ def copy_file (src, dst, preserve_mode=1, preserve_times=1, update=0, + link=None, verbose=0, dry_run=0): @@ -256,17 +286,32 @@ def copy_file (src, dst, 'verbose' is true, then a one-line summary of the copy will be printed to stdout. + 'link' allows you to make hard links (os.link) or symbolic links + (os.symlink) instead of copying: set it to "hard" or "sym"; if it + is None (the default), files are copied. Don't set 'link' on + systems that don't support it: 'copy_file()' doesn't check if + hard or symbolic linking is availalble. + + Under Mac OS, uses the native file copy function in macostools; + on other systems, uses '_copy_file_contents()' to copy file + contents. + Return true if the file was copied (or would have been copied), false otherwise (ie. 'update' was true and the destination is up-to-date).""" - # XXX doesn't copy Mac-specific metadata - + # XXX if the destination file already exists, we clobber it if + # copying, but blow up if linking. Hmmm. And I don't know what + # macostools.copyfile() does. Should definitely be consistent, and + # should probably blow up if destination exists and we would be + # changing it (ie. it's not already a hard/soft link to src OR + # (not update) and (src newer than dst). + from stat import * if not os.path.isfile (src): raise DistutilsFileError, \ - "can't copy '%s': not a regular file" % src + "can't copy '%s': doesn't exist or not a regular file" % src if os.path.isdir (dst): dir = dst @@ -279,8 +324,13 @@ def copy_file (src, dst, print "not copying %s (output up-to-date)" % src return 0 + try: + action = _copy_action[link] + except KeyError: + raise ValueError, \ + "invalid value '%s' for 'link' argument" % link if verbose: - print "copying %s -> %s" % (src, dir) + print "%s %s -> %s" % (action, src, dir) if dry_run: return 1 @@ -293,19 +343,29 @@ def copy_file (src, dst, except OSError, exc: raise DistutilsFileError, \ "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) - return 1 - # Otherwise use custom routine - _copy_file_contents (src, dst) - if preserve_mode or preserve_times: - st = os.stat (src) - - # According to David Ascher , utime() should be done - # before chmod() (at least under NT). - if preserve_times: - os.utime (dst, (st[ST_ATIME], st[ST_MTIME])) - if preserve_mode: - os.chmod (dst, S_IMODE (st[ST_MODE])) + # If linking (hard or symbolic), use the appropriate system call + # (Unix only, of course, but that's the caller's responsibility) + elif link == 'hard': + if not (os.path.exists (dst) and os.path.samefile (src, dst)): + os.link (src, dst) + elif link == 'sym': + if not (os.path.exists (dst) and os.path.samefile (src, dst)): + os.symlink (src, dst) + + # Otherwise (non-Mac, not linking), copy the file contents and + # (optionally) copy the times and mode. + else: + _copy_file_contents (src, dst) + if preserve_mode or preserve_times: + st = os.stat (src) + + # According to David Ascher , utime() should be done + # before chmod() (at least under NT). + if preserve_times: + os.utime (dst, (st[ST_ATIME], st[ST_MTIME])) + if preserve_mode: + os.chmod (dst, S_IMODE (st[ST_MODE])) return 1 @@ -375,7 +435,7 @@ def copy_tree (src, dst, else: copy_file (src_name, dst_name, preserve_mode, preserve_times, - update, verbose, dry_run) + update, None, verbose, dry_run) outputs.append (dst_name) return outputs @@ -562,7 +622,8 @@ def subst_vars (str, local_vars): # subst_vars () -def make_tarball (base_dir, compress="gzip", verbose=0, dry_run=0): +def make_tarball (base_name, base_dir, compress="gzip", + verbose=0, dry_run=0): """Create a (possibly compressed) tar file from all the files under 'base_dir'. 'compress' must be "gzip" (the default), "compress", or None. Both "tar" and the compression utility named by 'compress' @@ -584,7 +645,7 @@ def make_tarball (base_dir, compress="gzip", verbose=0, dry_run=0): raise ValueError, \ "bad value for 'compress': must be None, 'gzip', or 'compress'" - archive_name = base_dir + ".tar" + archive_name = base_name + ".tar" cmd = ["tar", "-cf", archive_name, base_dir] spawn (cmd, verbose=verbose, dry_run=dry_run) @@ -597,21 +658,21 @@ def make_tarball (base_dir, compress="gzip", verbose=0, dry_run=0): # make_tarball () -def make_zipfile (base_dir, verbose=0, dry_run=0): - """Create a ZIP file from all the files under 'base_dir'. The - output ZIP file will be named 'base_dir' + ".zip". Uses either the +def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): + """Create a zip file from all the files under 'base_dir'. The + output zip file will be named 'base_dir' + ".zip". Uses either the InfoZIP "zip" utility (if installed and found on the default search path) or the "zipfile" Python module (if available). If neither tool is available, raises DistutilsExecError. Returns the name - of the output ZIP file.""" + of the output zip file.""" # This initially assumed the Unix 'zip' utility -- but # apparently InfoZIP's zip.exe works the same under Windows, so # no changes needed! - zip_filename = base_dir + ".zip" + zip_filename = base_name + ".zip" try: - spawn (["zip", "-r", zip_filename, base_dir], + spawn (["zip", "-rq", zip_filename, base_dir], verbose=verbose, dry_run=dry_run) except DistutilsExecError: @@ -649,3 +710,52 @@ def make_zipfile (base_dir, verbose=0, dry_run=0): return zip_filename # make_zipfile () + + +def make_archive (base_name, format, + root_dir=None, base_dir=None, + verbose=0, dry_run=0): + + """Create an archive file (eg. zip or tar). 'base_name' is the name + of the file to create, minus any format-specific extension; 'format' + is the archive format: one of "zip", "tar", "ztar", or "gztar". + 'root_dir' is a directory that will be the root directory of the + archive; ie. we typically chdir into 'root_dir' before creating the + archive. 'base_dir' is the directory where we start archiving from; + ie. 'base_dir' will be the common prefix of all files and + directories in the archive. 'root_dir' and 'base_dir' both default + to the current directory.""" + + save_cwd = os.getcwd() + if root_dir is not None: + if verbose: + print "changing into '%s'" % root_dir + base_name = os.path.abspath (base_name) + if not dry_run: + os.chdir (root_dir) + + if base_dir is None: + base_dir = os.curdir + + kwargs = { 'verbose': verbose, + 'dry_run': dry_run } + + if format == 'gztar': + func = make_tarball + kwargs['compress'] = 'gzip' + elif format == 'ztar': + func = make_tarball + kwargs['compress'] = 'compress' + elif format == 'tar': + func = make_tarball + elif format == 'zip': + func = make_zipfile + + apply (func, (base_name, base_dir), kwargs) + + if root_dir is not None: + if verbose: + print "changing back to '%s'" % save_cwd + os.chdir (save_cwd) + +# make_archive () -- cgit v1.2.1 From 5b64d0853985580ffcb024ead685aac6a42f25af Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:05:18 +0000 Subject: Added 'get_name()' and 'get_full_name()' methods to Distribution. Simplified 'Command.get_peer_option()' a tad -- just call 'find_peer()' to get the peer command object. Updated 'Command.copy_file()' to take a 'link' parameter, just like 'util.copy_file()' does now. Added 'Command.make_archive()' to wrap 'util.make_archive()'. --- core.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index 08a1d641..025e1c0d 100644 --- a/core.py +++ b/core.py @@ -638,6 +638,13 @@ class Distribution: not self.has_ext_modules() and not self.has_c_libraries()) + def get_name (self): + return self.name or "UNKNOWN" + + def get_full_name (self): + return "%s-%s" % ((self.name or "UNKNOWN"), (self.version or "???")) + + # class Distribution @@ -887,7 +894,7 @@ class Command: """Find or create the command object for 'command', and return its 'option' option.""" - cmd_obj = self.distribution.find_command_obj (command) + cmd_obj = self.find_peer (command) return cmd_obj.get_option (option) @@ -939,12 +946,13 @@ class Command: def copy_file (self, infile, outfile, - preserve_mode=1, preserve_times=1, level=1): + preserve_mode=1, preserve_times=1, link=None, level=1): """Copy a file respecting verbose, dry-run and force flags.""" return util.copy_file (infile, outfile, preserve_mode, preserve_times, not self.force, + link, self.verbose >= level, self.dry_run) @@ -976,6 +984,12 @@ class Command: self.dry_run) + def make_archive (self, base_name, format, + root_dir=None, base_dir=None): + util.make_archive (base_name, format, root_dir, base_dir, + self.verbose, self.dry_run) + + def make_file (self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): -- cgit v1.2.1 From 71554a0d965d88e26b49b9352b9f6c16d9dcdd4b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:14:51 +0000 Subject: Added 'bdist' and 'bdist_dumb'. --- command/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/__init__.py b/command/__init__.py index b7973c61..385330b5 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -13,4 +13,6 @@ __all__ = ['build', 'install_lib', 'clean', 'sdist', + 'bdist', + 'bdist_dumb', ] -- cgit v1.2.1 From a65325be6c89763d12b4948bfccb2f5e51171a4e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:34:09 +0000 Subject: Don't put Python's library directory into the library search path -- that's specific to building Python extensions. --- msvccompiler.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 29899104..c906e114 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -158,9 +158,6 @@ class MSVCCompiler (CCompiler) : force=0): CCompiler.__init__ (self, verbose, dry_run, force) - - self.add_library_dir( os.path.join( sys.exec_prefix, 'libs' ) ) - versions = get_devstudio_versions () if versions: -- cgit v1.2.1 From 62173c6e6b912a50267e06e7ad96074a8aa75a41 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:50:23 +0000 Subject: Patch (mostly) from Thomas Heller for building on Windows: * build to "Debug" or "Release" temp directory * put linker turds (.lib and .exp files) in the build temp directory * tack on "_d" to extensions built with debugging * added 'get_ext_libname()' help in putting linker turds to temp dir Also, moved the code that simplifies None to empty list for a bunch of options to 'finalize_options()' instead of 'run()'. --- command/build_ext.py | 47 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index f2e0b319..2cb1c610 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -122,9 +122,24 @@ class build_ext (Command): if type (self.libraries) is StringType: self.libraries = [self.libraries] - # XXX how the heck are 'self.define' and 'self.undef' supposed to - # be set? + # Life is easier if we're not forever checking for None, so + # simplify these options to empty lists if unset + if self.libraries is None: + self.libraries = [] + if self.library_dirs is None: + self.library_dirs = [] + if self.rpath is None: + self.rpath = [] + # for extensions under windows use different directories + # for Release and Debug builds. + # also Python's library directory must be appended to library_dirs + if os.name == 'nt': + self.library_dirs.append (os.path.join(sys.exec_prefix, 'libs')) + if self.debug: + self.build_temp = os.path.join (self.build_temp, "Debug") + else: + self.build_temp = os.path.join (self.build_temp, "Release") # finalize_options () @@ -143,15 +158,6 @@ class build_ext (Command): if not self.extensions: return - # Simplify the following logic (eg. don't have to worry about - # appending to None) - if self.libraries is None: - self.libraries = [] - if self.library_dirs is None: - self.library_dirs = [] - if self.rpath is None: - self.rpath = [] - # If we were asked to build any C/C++ libraries, make sure that the # directory where we put them is in the library search path for # linking extensions. @@ -313,6 +319,14 @@ class build_ext (Command): else: modname = string.split (extension_name, '.')[-1] extra_args.append('/export:init%s'%modname) + + # The MSVC linker generates unneeded .lib and .exp files, + # which cannot be suppressed by any linker switches. So + # make sure they are generated in the temporary build + # directory. + implib_dir = os.path.join(self.build_temp,\ + self.get_ext_libname(extension_name)) + extra_args.append ('/IMPLIB:' + implib_dir) # if MSVC fullname = self.get_ext_fullname (extension_name) @@ -351,6 +365,17 @@ class build_ext (Command): def get_ext_filename (self, ext_name): from distutils import sysconfig ext_path = string.split (ext_name, '.') + # extensions in debug_mode are named 'module_d.pyd' under windows + if os.name == 'nt' and self.debug: + return apply (os.path.join, ext_path) + '_d' + sysconfig.SO return apply (os.path.join, ext_path) + sysconfig.SO + def get_ext_libname (self, ext_name): + # create a filename for the (unneeded) lib-file. + # extensions in debug_mode are named 'module_d.pyd' under windows + ext_path = string.split (ext_name, '.') + if os.name == 'nt' and self.debug: + return apply (os.path.join, ext_path) + '_d.lib' + return apply (os.path.join, ext_path) + '.lib' + # class BuildExt -- cgit v1.2.1 From ac24d4ea40f9c79d063743d6b454f801a81c93e7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:52:16 +0000 Subject: Don't perpetrate the "_d" hack for naming debugging extensions -- that's now done in the 'build_ext' command. --- msvccompiler.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index c906e114..2828711e 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -321,10 +321,6 @@ class MSVCCompiler (CCompiler) : if debug: ldflags = self.ldflags_shared_debug - # XXX not sure this belongs here - # extensions in debug_mode are named 'module_d.pyd' - basename, ext = os.path.splitext (output_filename) - output_filename = basename + '_d' + ext else: ldflags = self.ldflags_shared -- cgit v1.2.1 From acb7e0a92ecd47d4fb0e64424123a136b23a2b6c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 04:37:40 +0000 Subject: Removed some old test code: don't set 'plat' when calling 'new_compiler()'. --- command/build_clib.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index 6560fb47..f48e6476 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -89,8 +89,7 @@ class build_clib (Command): return # Yech -- this is cut 'n pasted from build_ext.py! - self.compiler = new_compiler (plat=os.environ.get ('PLAT'), - verbose=self.verbose, + self.compiler = new_compiler (verbose=self.verbose, dry_run=self.dry_run, force=self.force) if self.include_dirs is not None: -- cgit v1.2.1 From f382f8cd5035da5ab29cf6d33f00f4f6a43c0c21 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 04:40:25 +0000 Subject: Tweaked 'get_platform()' to include the first character of the OS release: eg. sunos5, linux2, irix5. --- util.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/util.py b/util.py index 6674d0aa..63b6bec0 100644 --- a/util.py +++ b/util.py @@ -545,10 +545,8 @@ def get_platform (): i.e. "???" or "???".""" if os.name == 'posix': - uname = os.uname() - OS = uname[0] - arch = uname[4] - return "%s-%s" % (string.lower (OS), string.lower (arch)) + (OS, _, rel, _, arch) = os.uname() + return "%s%c-%s" % (string.lower (OS), rel[0], string.lower (arch)) else: return sys.platform -- cgit v1.2.1 From ca78e832a4c8ecb5a9308ed104166e71b393d729 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 04:53:41 +0000 Subject: Import from 'types' module. Added 'ztar', 'tar' to 'format_command' dictionary. --- command/bdist.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/bdist.py b/command/bdist.py index 56308285..12397fca 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -8,6 +8,7 @@ distribution).""" __revision__ = "$Id$" import os, string +from types import * from distutils.core import Command @@ -25,6 +26,8 @@ class bdist (Command): 'nt': 'zip', } format_command = { 'gztar': 'bdist_dumb', + 'ztar': 'bdist_dumb', + 'tar': 'bdist_dumb', 'zip': 'bdist_dumb', } -- cgit v1.2.1 From 39f9a0206365461772e5c76d01a3cde0be9d1c10 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 05:08:50 +0000 Subject: Rename 'formats' option to 'format', and remove the ability to generate multiple built distributions in one run -- it seemed a bit dodgy and I'd rather remove it than try to beat it into submission right now. --- command/bdist.py | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 12397fca..889cdba8 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -10,14 +10,15 @@ __revision__ = "$Id$" import os, string from types import * from distutils.core import Command +from distutils.errors import * class bdist (Command): description = "create a built (binary) distribution" - user_options = [('formats=', 'f', - "formats for distribution (tar, ztar, gztar, zip, ... )"), + user_options = [('format=', 'f', + "format for distribution (tar, ztar, gztar, zip, ... )"), ] # This won't do in reality: will need to distinguish RPM-ish Linux, @@ -32,21 +33,21 @@ class bdist (Command): def initialize_options (self): - self.formats = None + self.format = None # initialize_options() def finalize_options (self): - if self.formats is None: + if self.format is None: try: - self.formats = [self.default_format[os.name]] + self.format = self.default_format[os.name] except KeyError: raise DistutilsPlatformError, \ "don't know how to create built distributions " + \ "on platform %s" % os.name - elif type (self.formats) is StringType: - self.formats = string.split (self.formats, ',') + #elif type (self.format) is StringType: + # self.format = string.split (self.format, ',') # finalize_options() @@ -54,19 +55,14 @@ class bdist (Command): def run (self): - for format in self.formats: - cmd_name = self.format_command[format] - sub_cmd = self.find_peer (cmd_name) - sub_cmd.set_option ('format', format) - - # XXX blecchhh!! should formalize this: at least a - # 'forget_run()' (?) method, possibly complicate the - # 'have_run' dictionary to include some command state as well - # as the command name -- eg. in this case we might want - # ('bdist_dumb','zip') to be marked "have run", but not - # ('bdist_dumb','gztar'). - self.distribution.have_run[cmd_name] = 0 - self.run_peer (cmd_name) + try: + cmd_name = self.format_command[self.format] + except KeyError: + raise DistutilsOptionError, \ + "invalid archive format '%s'" % self.format + + sub_cmd = self.find_peer (cmd_name) + sub_cmd.set_option ('format', self.format) # run() -- cgit v1.2.1 From 23873c733a5189d48d90a3ab638d0e7d155fa234 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 05:20:27 +0000 Subject: Fixed 'make_archive()' to explicitly turn of compression when format is "tar". --- util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/util.py b/util.py index 63b6bec0..22fc4375 100644 --- a/util.py +++ b/util.py @@ -746,6 +746,7 @@ def make_archive (base_name, format, kwargs['compress'] = 'compress' elif format == 'tar': func = make_tarball + kwargs['compress'] = None elif format == 'zip': func = make_zipfile -- cgit v1.2.1 From f35f3bd982e3447106ba061b91ebe6f72a47d20a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 05:21:27 +0000 Subject: Oops, got a little too enthusiastic deleting code in that last revision: we still have to *run* the sub-command that creates a built distribution. --- command/bdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/bdist.py b/command/bdist.py index 889cdba8..685f5250 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -63,6 +63,7 @@ class bdist (Command): sub_cmd = self.find_peer (cmd_name) sub_cmd.set_option ('format', self.format) + self.run_peer (cmd_name) # run() -- cgit v1.2.1 From f9e57445d36b3438f083a1b69b5793e8b9f8145f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 05:22:47 +0000 Subject: Added code to blow away the pseudo-installation tree and a 'keep_tree' option to disable this (by default, it's false and we clean up). --- command/bdist_dumb.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 4383a8fa..5456e57a 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -10,7 +10,7 @@ __revision__ = "$Id$" import os from distutils.core import Command -from distutils.util import get_platform, create_tree +from distutils.util import get_platform, create_tree, remove_tree class bdist_dumb (Command): @@ -19,6 +19,9 @@ class bdist_dumb (Command): user_options = [('format=', 'f', "archive format to create (tar, ztar, gztar, zip)"), + ('keep-tree', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), ] default_format = { 'posix': 'gztar', @@ -27,6 +30,7 @@ class bdist_dumb (Command): def initialize_options (self): self.format = None + self.keep_tree = 0 # initialize_options() @@ -68,9 +72,14 @@ class bdist_dumb (Command): # pseudo-installation tree. archive_basename = "%s.%s" % (self.distribution.get_full_name(), get_platform()) + print "output_dir = %s" % output_dir + print "self.format = %s" % self.format self.make_archive (archive_basename, self.format, root_dir=output_dir) + if not self.keep_tree: + remove_tree (output_dir, self.verbose, self.dry_run) + # run() -- cgit v1.2.1 From 802b36518e79ab0103afe61bb5c1ee9e92c2f433 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 16:47:40 +0000 Subject: Patch from Thomas Heller: use the new winreg module if available. --- msvccompiler.py | 71 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 2828711e..07096e95 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -17,6 +17,35 @@ from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options +_can_read_reg = 0 +try: + import winreg + _HKEY_CLASSES_ROOT = winreg.HKEY_CLASSES_ROOT + _HKEY_LOCAL_MACHINE = winreg.HKEY_LOCAL_MACHINE + _HKEY_CURRENT_USER = winreg.HKEY_CURRENT_USER + _HKEY_USERS = winreg.HKEY_USERS + _RegOpenKeyEx = winreg.OpenKeyEx + _RegEnumKey = winreg.EnumKey + _RegEnumValue = winreg.EnumValue + _RegError = winreg.error + _can_read_reg = 1 +except ImportError: + try: + import win32api + import win32con + _HKEY_CLASSES_ROOT = win32con.HKEY_CLASSES_ROOT + _HKEY_LOCAL_MACHINE = win32con.HKEY_LOCAL_MACHINE + _HKEY_CURRENT_USER = win32con.HKEY_CURRENT_USER + _HKEY_USERS = win32con.HKEY_USERS + _RegOpenKeyEx = win32api.RegOpenKeyEx + _RegEnumKey = win32api.RegEnumKey + _RegEnumValue = win32api.RegEnumValue + _RegError = win32api.error + _can_read_reg = 1 + except ImportError: + pass + + def get_devstudio_versions (): """Get list of devstudio versions from the Windows registry. Return a list of strings containing version numbers; the list will be @@ -24,30 +53,27 @@ def get_devstudio_versions (): a registry-access module) or the appropriate registry keys weren't found.""" - try: - import win32api - import win32con - except ImportError: + if not _can_read_reg: return [] K = 'Software\\Microsoft\\Devstudio' L = [] - for base in (win32con.HKEY_CLASSES_ROOT, - win32con.HKEY_LOCAL_MACHINE, - win32con.HKEY_CURRENT_USER, - win32con.HKEY_USERS): + for base in (_HKEY_CLASSES_ROOT, + _HKEY_LOCAL_MACHINE, + _HKEY_CURRENT_USER, + _HKEY_USERS): try: - k = win32api.RegOpenKeyEx(base,K) + k = _RegOpenKeyEx(base,K) i = 0 while 1: try: - p = win32api.RegEnumKey(k,i) + p = _RegEnumKey(k,i) if p[0] in '123456789' and p not in L: L.append(p) - except win32api.error: + except _RegError: break i = i + 1 - except win32api.error: + except _RegError: pass L.sort() L.reverse() @@ -61,10 +87,7 @@ def get_msvc_paths (path, version='6.0', platform='x86'): a list of strings; will be empty list if unable to access the registry or appropriate registry keys not found.""" - try: - import win32api - import win32con - except ImportError: + if not _can_read_reg: return [] L = [] @@ -74,16 +97,16 @@ def get_msvc_paths (path, version='6.0', platform='x86'): K = ('Software\\Microsoft\\Devstudio\\%s\\' + 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \ (version,platform) - for base in (win32con.HKEY_CLASSES_ROOT, - win32con.HKEY_LOCAL_MACHINE, - win32con.HKEY_CURRENT_USER, - win32con.HKEY_USERS): + for base in (_HKEY_CLASSES_ROOT, + _HKEY_LOCAL_MACHINE, + _HKEY_CURRENT_USER, + _HKEY_USERS): try: - k = win32api.RegOpenKeyEx(base,K) + k = _RegOpenKeyEx(base,K) i = 0 while 1: try: - (p,v,t) = win32api.RegEnumValue(k,i) + (p,v,t) = _RegEnumValue(k,i) if string.upper(p) == path: V = string.split(v,';') for v in V: @@ -91,9 +114,9 @@ def get_msvc_paths (path, version='6.0', platform='x86'): L.append(v) break i = i + 1 - except win32api.error: + except _RegError: break - except win32api.error: + except _RegError: pass return L -- cgit v1.2.1 From 8b2185f27ae8706e420bcf52ee071a8c3f4ee5dd Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 16:53:42 +0000 Subject: Simplified Thomas Heller's registry patch: just assign all those HKEY_* and Reg* names once, rather than having near-duplicate code in the two import attempts. Also dropped the leading underscore on all the imported symbols, as it's not appropriate (they're not local to this module). --- msvccompiler.py | 64 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 07096e95..b38aadbe 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -20,30 +20,30 @@ from distutils.ccompiler import \ _can_read_reg = 0 try: import winreg - _HKEY_CLASSES_ROOT = winreg.HKEY_CLASSES_ROOT - _HKEY_LOCAL_MACHINE = winreg.HKEY_LOCAL_MACHINE - _HKEY_CURRENT_USER = winreg.HKEY_CURRENT_USER - _HKEY_USERS = winreg.HKEY_USERS - _RegOpenKeyEx = winreg.OpenKeyEx - _RegEnumKey = winreg.EnumKey - _RegEnumValue = winreg.EnumValue - _RegError = winreg.error _can_read_reg = 1 + hkey_mod = winreg # module that provides HKEY_* stuff + reg_mod = winreg # provides other registry stuff except ImportError: try: import win32api import win32con - _HKEY_CLASSES_ROOT = win32con.HKEY_CLASSES_ROOT - _HKEY_LOCAL_MACHINE = win32con.HKEY_LOCAL_MACHINE - _HKEY_CURRENT_USER = win32con.HKEY_CURRENT_USER - _HKEY_USERS = win32con.HKEY_USERS - _RegOpenKeyEx = win32api.RegOpenKeyEx - _RegEnumKey = win32api.RegEnumKey - _RegEnumValue = win32api.RegEnumValue - _RegError = win32api.error _can_read_reg = 1 + hkey_mod = win32con + reg_mod = win32api except ImportError: pass + +if _can_read_reg: + HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT + HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE + HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER + HKEY_USERS = hkey_mod.HKEY_USERS + RegOpenKeyEx = reg_mod.RegOpenKeyEx + RegEnumKey = reg_mod.RegEnumKey + RegEnumValue = reg_mod.RegEnumValue + RegError = reg_mod.error + _can_read_reg = 1 + def get_devstudio_versions (): @@ -58,22 +58,22 @@ def get_devstudio_versions (): K = 'Software\\Microsoft\\Devstudio' L = [] - for base in (_HKEY_CLASSES_ROOT, - _HKEY_LOCAL_MACHINE, - _HKEY_CURRENT_USER, - _HKEY_USERS): + for base in (HKEY_CLASSES_ROOT, + HKEY_LOCAL_MACHINE, + HKEY_CURRENT_USER, + HKEY_USERS): try: - k = _RegOpenKeyEx(base,K) + k = RegOpenKeyEx(base,K) i = 0 while 1: try: - p = _RegEnumKey(k,i) + p = RegEnumKey(k,i) if p[0] in '123456789' and p not in L: L.append(p) - except _RegError: + except RegError: break i = i + 1 - except _RegError: + except RegError: pass L.sort() L.reverse() @@ -97,16 +97,16 @@ def get_msvc_paths (path, version='6.0', platform='x86'): K = ('Software\\Microsoft\\Devstudio\\%s\\' + 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \ (version,platform) - for base in (_HKEY_CLASSES_ROOT, - _HKEY_LOCAL_MACHINE, - _HKEY_CURRENT_USER, - _HKEY_USERS): + for base in (HKEY_CLASSES_ROOT, + HKEY_LOCAL_MACHINE, + HKEY_CURRENT_USER, + HKEY_USERS): try: - k = _RegOpenKeyEx(base,K) + k = RegOpenKeyEx(base,K) i = 0 while 1: try: - (p,v,t) = _RegEnumValue(k,i) + (p,v,t) = RegEnumValue(k,i) if string.upper(p) == path: V = string.split(v,';') for v in V: @@ -114,9 +114,9 @@ def get_msvc_paths (path, version='6.0', platform='x86'): L.append(v) break i = i + 1 - except _RegError: + except RegError: break - except _RegError: + except RegError: pass return L -- cgit v1.2.1 From 373df8bfb5f251907217641652a46ad18603f4b1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 19:04:25 +0000 Subject: Fixed my simplification to Thomas' patch: winreg and win32api export the same functions, but with different names. --- msvccompiler.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index b38aadbe..43a85960 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -21,15 +21,25 @@ _can_read_reg = 0 try: import winreg _can_read_reg = 1 - hkey_mod = winreg # module that provides HKEY_* stuff - reg_mod = winreg # provides other registry stuff + hkey_mod = winreg + + RegOpenKeyEx = winreg.OpenKeyEx + RegEnumKey = winreg.EnumKey + RegEnumValue = winreg.EnumValue + RegError = winreg.error + except ImportError: try: import win32api import win32con _can_read_reg = 1 hkey_mod = win32con - reg_mod = win32api + + RegOpenKeyEx = win32api.RegOpenKeyEx + RegEnumKey = win32api.RegEnumKey + RegEnumValue = win32api.RegEnumValue + RegError = win32api.error + except ImportError: pass @@ -38,11 +48,6 @@ if _can_read_reg: HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER HKEY_USERS = hkey_mod.HKEY_USERS - RegOpenKeyEx = reg_mod.RegOpenKeyEx - RegEnumKey = reg_mod.RegEnumKey - RegEnumValue = reg_mod.RegEnumValue - RegError = reg_mod.error - _can_read_reg = 1 -- cgit v1.2.1 From fe6451bacbd0075114cc247c568bde94f2b61caf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 4 Apr 2000 01:40:52 +0000 Subject: Reorganization: moved the Distribution class from core.py to dist.py, and the Command class from core.py to cmd.py. No other code needs changing though; distutils.core still provides the Command and Distribution classes, although indirectly now. --- cmd.py | 390 +++++++++++++++++++++++++++ core.py | 939 +--------------------------------------------------------------- dist.py | 567 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 963 insertions(+), 933 deletions(-) create mode 100644 cmd.py create mode 100644 dist.py diff --git a/cmd.py b/cmd.py new file mode 100644 index 00000000..ad787037 --- /dev/null +++ b/cmd.py @@ -0,0 +1,390 @@ +"""distutils.cmd + +Provides the Command class, the base class for the command classes +in the distutils.command package.""" + +# created 2000/04/03, Greg Ward +# (extricated from core.py; actually dates back to the beginning) + +__revision__ = "$Id$" + +import sys, string +from types import * +from distutils.errors import * +from distutils import util + + +class Command: + """Abstract base class for defining command classes, the "worker bees" + of the Distutils. A useful analogy for command classes is to + think of them as subroutines with local variables called + "options". The options are "declared" in 'initialize_options()' + and "defined" (given their final values, aka "finalized") in + 'finalize_options()', both of which must be defined by every + command class. The distinction between the two is necessary + because option values might come from the outside world (command + line, option file, ...), and any options dependent on other + options must be computed *after* these outside influences have + been processed -- hence 'finalize_options()'. The "body" of the + subroutine, where it does all its work based on the values of its + options, is the 'run()' method, which must also be implemented by + every command class.""" + + # -- Creation/initialization methods ------------------------------- + + def __init__ (self, dist): + """Create and initialize a new Command object. Most importantly, + invokes the 'initialize_options()' method, which is the + real initializer and depends on the actual command being + instantiated.""" + + # late import because of mutual dependence between these classes + from distutils.dist import Distribution + + if not isinstance (dist, Distribution): + raise TypeError, "dist must be a Distribution instance" + if self.__class__ is Command: + raise RuntimeError, "Command is an abstract class" + + self.distribution = dist + self.initialize_options () + + # Per-command versions of the global flags, so that the user can + # customize Distutils' behaviour command-by-command and let some + # commands fallback on the Distribution's behaviour. None means + # "not defined, check self.distribution's copy", while 0 or 1 mean + # false and true (duh). Note that this means figuring out the real + # value of each flag is a touch complicatd -- hence "self.verbose" + # (etc.) will be handled by __getattr__, below. + self._verbose = None + self._dry_run = None + self._force = None + + # The 'help' flag is just used for command-line parsing, so + # none of that complicated bureaucracy is needed. + self.help = 0 + + # 'ready' records whether or not 'finalize_options()' has been + # called. 'finalize_options()' itself should not pay attention to + # this flag: it is the business of 'ensure_ready()', which always + # calls 'finalize_options()', to respect/update it. + self.ready = 0 + + # __init__ () + + + def __getattr__ (self, attr): + if attr in ('verbose', 'dry_run', 'force'): + myval = getattr (self, "_" + attr) + if myval is None: + return getattr (self.distribution, attr) + else: + return myval + else: + raise AttributeError, attr + + + def ensure_ready (self): + if not self.ready: + self.finalize_options () + self.ready = 1 + + + # Subclasses must define: + # initialize_options() + # provide default values for all options; may be overridden + # by Distutils client, by command-line options, or by options + # from option file + # finalize_options() + # decide on the final values for all options; this is called + # after all possible intervention from the outside world + # (command-line, option file, etc.) has been processed + # run() + # run the command: do whatever it is we're here to do, + # controlled by the command's various option values + + def initialize_options (self): + """Set default values for all the options that this command + supports. Note that these defaults may be overridden + by the command-line supplied by the user; thus, this is + not the place to code dependencies between options; generally, + 'initialize_options()' implementations are just a bunch + of "self.foo = None" assignments. + + This method must be implemented by all command classes.""" + + raise RuntimeError, \ + "abstract method -- subclass %s must override" % self.__class__ + + def finalize_options (self): + """Set final values for all the options that this command + supports. This is always called as late as possible, ie. + after any option assignments from the command-line or from + other commands have been done. Thus, this is the place to to + code option dependencies: if 'foo' depends on 'bar', then it + is safe to set 'foo' from 'bar' as long as 'foo' still has + the same value it was assigned in 'initialize_options()'. + + This method must be implemented by all command classes.""" + + raise RuntimeError, \ + "abstract method -- subclass %s must override" % self.__class__ + + def run (self): + """A command's raison d'etre: carry out the action it exists + to perform, controlled by the options initialized in + 'initialize_options()', customized by the user and other + commands, and finalized in 'finalize_options()'. All + terminal output and filesystem interaction should be done by + 'run()'. + + This method must be implemented by all command classes.""" + + raise RuntimeError, \ + "abstract method -- subclass %s must override" % self.__class__ + + def announce (self, msg, level=1): + """If the Distribution instance to which this command belongs + has a verbosity level of greater than or equal to 'level' + print 'msg' to stdout.""" + + if self.verbose >= level: + print msg + + + # -- Option query/set methods -------------------------------------- + + def get_option (self, option): + """Return the value of a single option for this command. Raise + DistutilsOptionError if 'option' is not known.""" + try: + return getattr (self, option) + except AttributeError: + raise DistutilsOptionError, \ + "command %s: no such option %s" % \ + (self.get_command_name(), option) + + + def get_options (self, *options): + """Return (as a tuple) the values of several options for this + command. Raise DistutilsOptionError if any of the options in + 'options' are not known.""" + + values = [] + try: + for opt in options: + values.append (getattr (self, opt)) + except AttributeError, name: + raise DistutilsOptionError, \ + "command %s: no such option %s" % \ + (self.get_command_name(), name) + + return tuple (values) + + + def set_option (self, option, value): + """Set the value of a single option for this command. Raise + DistutilsOptionError if 'option' is not known.""" + + if not hasattr (self, option): + raise DistutilsOptionError, \ + "command '%s': no such option '%s'" % \ + (self.get_command_name(), option) + if value is not None: + setattr (self, option, value) + + def set_options (self, **optval): + """Set the values of several options for this command. Raise + DistutilsOptionError if any of the options specified as + keyword arguments are not known.""" + + for k in optval.keys(): + if optval[k] is not None: + self.set_option (k, optval[k]) + + + # -- Convenience methods for commands ------------------------------ + + def get_command_name (self): + if hasattr (self, 'command_name'): + return self.command_name + else: + return self.__class__.__name__ + + + def set_undefined_options (self, src_cmd, *option_pairs): + """Set the values of any "undefined" options from corresponding + option values in some other command object. "Undefined" here + means "is None", which is the convention used to indicate + that an option has not been changed between + 'set_initial_values()' and 'set_final_values()'. Usually + called from 'set_final_values()' for options that depend on + some other command rather than another option of the same + command. 'src_cmd' is the other command from which option + values will be taken (a command object will be created for it + if necessary); the remaining arguments are + '(src_option,dst_option)' tuples which mean "take the value + of 'src_option' in the 'src_cmd' command object, and copy it + to 'dst_option' in the current command object".""" + + # Option_pairs: list of (src_option, dst_option) tuples + + src_cmd_obj = self.distribution.find_command_obj (src_cmd) + src_cmd_obj.ensure_ready () + try: + for (src_option, dst_option) in option_pairs: + if getattr (self, dst_option) is None: + self.set_option (dst_option, + src_cmd_obj.get_option (src_option)) + except AttributeError, name: + # duh, which command? + raise DistutilsOptionError, "unknown option %s" % name + + + def find_peer (self, command, create=1): + """Wrapper around Distribution's 'find_command_obj()' method: + find (create if necessary and 'create' is true) the command + object for 'command'..""" + + cmd_obj = self.distribution.find_command_obj (command, create) + cmd_obj.ensure_ready () + return cmd_obj + + + def get_peer_option (self, command, option): + """Find or create the command object for 'command', and return + its 'option' option.""" + + cmd_obj = self.find_peer (command) + return cmd_obj.get_option (option) + + + def run_peer (self, command): + """Run some other command: uses the 'run_command()' method of + Distribution, which creates the command object if necessary + and then invokes its 'run()' method.""" + + self.distribution.run_command (command) + + + # -- External world manipulation ----------------------------------- + + def warn (self, msg): + sys.stderr.write ("warning: %s: %s\n" % + (self.get_command_name(), msg)) + + + def execute (self, func, args, msg=None, level=1): + """Perform some action that affects the outside world (eg. + by writing to the filesystem). Such actions are special because + they should be disabled by the "dry run" flag, and should + announce themselves if the current verbosity level is high + enough. This method takes care of all that bureaucracy for you; + all you have to do is supply the funtion to call and an argument + tuple for it (to embody the "external action" being performed), + a message to print if the verbosity level is high enough, and an + optional verbosity threshold.""" + + # Generate a message if we weren't passed one + if msg is None: + msg = "%s %s" % (func.__name__, `args`) + if msg[-2:] == ',)': # correct for singleton tuple + msg = msg[0:-2] + ')' + + # Print it if verbosity level is high enough + self.announce (msg, level) + + # And do it, as long as we're not in dry-run mode + if not self.dry_run: + apply (func, args) + + # execute() + + + def mkpath (self, name, mode=0777): + util.mkpath (name, mode, + self.verbose, self.dry_run) + + + def copy_file (self, infile, outfile, + preserve_mode=1, preserve_times=1, link=None, level=1): + """Copy a file respecting verbose, dry-run and force flags.""" + + return util.copy_file (infile, outfile, + preserve_mode, preserve_times, + not self.force, + link, + self.verbose >= level, + self.dry_run) + + + def copy_tree (self, infile, outfile, + preserve_mode=1, preserve_times=1, preserve_symlinks=0, + level=1): + """Copy an entire directory tree respecting verbose, dry-run, + and force flags.""" + + return util.copy_tree (infile, outfile, + preserve_mode,preserve_times,preserve_symlinks, + not self.force, + self.verbose >= level, + self.dry_run) + + + def move_file (self, src, dst, level=1): + """Move a file respecting verbose and dry-run flags.""" + return util.move_file (src, dst, + self.verbose >= level, + self.dry_run) + + + def spawn (self, cmd, search_path=1, level=1): + from distutils.spawn import spawn + spawn (cmd, search_path, + self.verbose >= level, + self.dry_run) + + + def make_archive (self, base_name, format, + root_dir=None, base_dir=None): + util.make_archive (base_name, format, root_dir, base_dir, + self.verbose, self.dry_run) + + + def make_file (self, infiles, outfile, func, args, + exec_msg=None, skip_msg=None, level=1): + + """Special case of 'execute()' for operations that process one or + more input files and generate one output file. Works just like + 'execute()', except the operation is skipped and a different + message printed if 'outfile' already exists and is newer than + all files listed in 'infiles'.""" + + + if exec_msg is None: + exec_msg = "generating %s from %s" % \ + (outfile, string.join (infiles, ', ')) + if skip_msg is None: + skip_msg = "skipping %s (inputs unchanged)" % outfile + + + # Allow 'infiles' to be a single string + if type (infiles) is StringType: + infiles = (infiles,) + elif type (infiles) not in (ListType, TupleType): + raise TypeError, \ + "'infiles' must be a string, or a list or tuple of strings" + + # If 'outfile' must be regenerated (either because it doesn't + # exist, is out-of-date, or the 'force' flag is true) then + # perform the action that presumably regenerates it + if self.force or util.newer_group (infiles, outfile): + self.execute (func, args, exec_msg, level) + + # Otherwise, print the "skip" message + else: + self.announce (skip_msg, level) + + # make_file () + +# class Command diff --git a/core.py b/core.py index 025e1c0d..3df54a5e 100644 --- a/core.py +++ b/core.py @@ -1,28 +1,19 @@ """distutils.core The only module that needs to be imported to use the Distutils; provides -the 'setup' function (which must be called); the 'Distribution' class -(which may be subclassed if additional functionality is desired), and -the 'Command' class (which is used both internally by Distutils, and -may be subclassed by clients for still more flexibility).""" +the 'setup' function (which is to be called from the setup script). Also +indirectly provides the Distribution and Command classes, although they are +really defined in distutils.dist and distutils.cmd.""" # created 1999/03/01, Greg Ward __revision__ = "$Id$" -import sys, os -import string, re +import sys from types import * -from copy import copy from distutils.errors import * -from distutils.fancy_getopt import fancy_getopt, print_help -from distutils import util - -# Regex to define acceptable Distutils command names. This is not *quite* -# the same as a Python NAME -- I don't allow leading underscores. The fact -# that they're very similar is no coincidence; the default naming scheme is -# to look for a Python module named after the command. -command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') +from distutils.dist import Distribution +from distutils.cmd import Command # This is a barebones help message generated displayed when the user # runs the setup script with no arguments at all. More useful help @@ -109,921 +100,3 @@ def setup (**attrs): raise SystemExit, "error: " + str (msg) # setup () - - -class Distribution: - """The core of the Distutils. Most of the work hiding behind - 'setup' is really done within a Distribution instance, which - farms the work out to the Distutils commands specified on the - command line. - - Clients will almost never instantiate Distribution directly, - unless the 'setup' function is totally inadequate to their needs. - However, it is conceivable that a client might wish to subclass - Distribution for some specialized purpose, and then pass the - subclass to 'setup' as the 'distclass' keyword argument. If so, - it is necessary to respect the expectations that 'setup' has of - Distribution: it must have a constructor and methods - 'parse_command_line()' and 'run_commands()' with signatures like - those described below.""" - - - # 'global_options' describes the command-line options that may be - # supplied to the client (setup.py) prior to any actual commands. - # Eg. "./setup.py -nv" or "./setup.py --verbose" both take advantage of - # these global options. This list should be kept to a bare minimum, - # since every global option is also valid as a command option -- and we - # don't want to pollute the commands with too many options that they - # have minimal control over. - global_options = [('verbose', 'v', - "run verbosely (default)"), - ('quiet', 'q', - "run quietly (turns verbosity off)"), - ('dry-run', 'n', - "don't actually do anything"), - ('force', 'f', - "skip dependency checking between files"), - ('help', 'h', - "show this help message"), - ] - negative_opt = {'quiet': 'verbose'} - - - # -- Creation/initialization methods ------------------------------- - - def __init__ (self, attrs=None): - """Construct a new Distribution instance: initialize all the - attributes of a Distribution, and then uses 'attrs' (a - dictionary mapping attribute names to values) to assign - some of those attributes their "real" values. (Any attributes - not mentioned in 'attrs' will be assigned to some null - value: 0, None, an empty list or dictionary, etc.) Most - importantly, initialize the 'command_obj' attribute - to the empty dictionary; this will be filled in with real - command objects by 'parse_command_line()'.""" - - # Default values for our command-line options - self.verbose = 1 - self.dry_run = 0 - self.force = 0 - self.help = 0 - self.help_commands = 0 - - # And the "distribution meta-data" options -- these can only - # come from setup.py (the caller), not the command line - # (or a hypothetical config file). - self.name = None - self.version = None - self.author = None - self.author_email = None - self.maintainer = None - self.maintainer_email = None - self.url = None - self.licence = None - self.description = None - - # 'cmdclass' maps command names to class objects, so we - # can 1) quickly figure out which class to instantiate when - # we need to create a new command object, and 2) have a way - # for the client to override command classes - self.cmdclass = {} - - # These options are really the business of various commands, rather - # than of the Distribution itself. We provide aliases for them in - # Distribution as a convenience to the developer. - # dictionary. - self.packages = None - self.package_dir = None - self.py_modules = None - self.libraries = None - self.ext_modules = None - self.ext_package = None - self.include_dirs = None - self.extra_path = None - - # And now initialize bookkeeping stuff that can't be supplied by - # the caller at all. 'command_obj' maps command names to - # Command instances -- that's how we enforce that every command - # class is a singleton. - self.command_obj = {} - - # 'have_run' maps command names to boolean values; it keeps track - # of whether we have actually run a particular command, to make it - # cheap to "run" a command whenever we think we might need to -- if - # it's already been done, no need for expensive filesystem - # operations, we just check the 'have_run' dictionary and carry on. - # It's only safe to query 'have_run' for a command class that has - # been instantiated -- a false value will be inserted when the - # command object is created, and replaced with a true value when - # the command is succesfully run. Thus it's probably best to use - # '.get()' rather than a straight lookup. - self.have_run = {} - - # Now we'll use the attrs dictionary (ultimately, keyword args from - # the client) to possibly override any or all of these distribution - # options. - if attrs: - - # Pull out the set of command options and work on them - # specifically. Note that this order guarantees that aliased - # command options will override any supplied redundantly - # through the general options dictionary. - options = attrs.get ('options') - if options: - del attrs['options'] - for (command, cmd_options) in options.items(): - cmd_obj = self.find_command_obj (command) - for (key, val) in cmd_options.items(): - cmd_obj.set_option (key, val) - # loop over commands - # if any command options - - # Now work on the rest of the attributes. Any attribute that's - # not already defined is invalid! - for (key,val) in attrs.items(): - if hasattr (self, key): - setattr (self, key, val) - else: - raise DistutilsOptionError, \ - "invalid distribution option '%s'" % key - - # __init__ () - - - def parse_command_line (self, args): - """Parse the setup script's command line: set any Distribution - attributes tied to command-line options, create all command - objects, and set their options from the command-line. 'args' - must be a list of command-line arguments, most likely - 'sys.argv[1:]' (see the 'setup()' function). This list is first - processed for "global options" -- options that set attributes of - the Distribution instance. Then, it is alternately scanned for - Distutils command and options for that command. Each new - command terminates the options for the previous command. The - allowed options for a command are determined by the 'options' - attribute of the command object -- thus, we instantiate (and - cache) every command object here, in order to access its - 'options' attribute. Any error in that 'options' attribute - raises DistutilsGetoptError; any error on the command-line - raises DistutilsArgError. If no Distutils commands were found - on the command line, raises DistutilsArgError. Return true if - command-line successfully parsed and we should carry on with - executing commands; false if no errors but we shouldn't execute - commands (currently, this only happens if user asks for - help).""" - - # We have to parse the command line a bit at a time -- global - # options, then the first command, then its options, and so on -- - # because each command will be handled by a different class, and - # the options that are valid for a particular class aren't - # known until we instantiate the command class, which doesn't - # happen until we know what the command is. - - self.commands = [] - options = self.global_options + \ - [('help-commands', None, - "list all available commands")] - args = fancy_getopt (options, self.negative_opt, - self, sys.argv[1:]) - - # User just wants a list of commands -- we'll print it out and stop - # processing now (ie. if they ran "setup --help-commands foo bar", - # we ignore "foo bar"). - if self.help_commands: - self.print_commands () - print - print usage - return - - while args: - # Pull the current command from the head of the command line - command = args[0] - if not command_re.match (command): - raise SystemExit, "invalid command name '%s'" % command - self.commands.append (command) - - # Make sure we have a command object to put the options into - # (this either pulls it out of a cache of command objects, - # or finds and instantiates the command class). - try: - cmd_obj = self.find_command_obj (command) - except DistutilsModuleError, msg: - raise DistutilsArgError, msg - - # Require that the command class be derived from Command -- - # that way, we can be sure that we at least have the 'run' - # and 'get_option' methods. - if not isinstance (cmd_obj, Command): - raise DistutilsClassError, \ - "command class %s must subclass Command" % \ - cmd_obj.__class__ - - # Also make sure that the command object provides a list of its - # known options - if not (hasattr (cmd_obj, 'user_options') and - type (cmd_obj.user_options) is ListType): - raise DistutilsClassError, \ - ("command class %s must provide " + - "'user_options' attribute (a list of tuples)") % \ - cmd_obj.__class__ - - # Poof! like magic, all commands support the global - # options too, just by adding in 'global_options'. - negative_opt = self.negative_opt - if hasattr (cmd_obj, 'negative_opt'): - negative_opt = copy (negative_opt) - negative_opt.update (cmd_obj.negative_opt) - - options = self.global_options + cmd_obj.user_options - args = fancy_getopt (options, negative_opt, - cmd_obj, args[1:]) - if cmd_obj.help: - print_help (self.global_options, - header="Global options:") - print - print_help (cmd_obj.user_options, - header="Options for '%s' command:" % command) - print - print usage - return - - self.command_obj[command] = cmd_obj - self.have_run[command] = 0 - - # while args - - # If the user wants help -- ie. they gave the "--help" option -- - # give it to 'em. We do this *after* processing the commands in - # case they want help on any particular command, eg. - # "setup.py --help foo". (This isn't the documented way to - # get help on a command, but I support it because that's how - # CVS does it -- might as well be consistent.) - if self.help: - print_help (self.global_options, header="Global options:") - print - - for command in self.commands: - klass = self.find_command_class (command) - print_help (klass.user_options, - header="Options for '%s' command:" % command) - print - - print usage - return - - # Oops, no commands found -- an end-user error - if not self.commands: - raise DistutilsArgError, "no commands supplied" - - # All is well: return true - return 1 - - # parse_command_line() - - - def print_command_list (self, commands, header, max_length): - """Print a subset of the list of all commands -- used by - 'print_commands()'.""" - - print header + ":" - - for cmd in commands: - klass = self.cmdclass.get (cmd) - if not klass: - klass = self.find_command_class (cmd) - try: - description = klass.description - except AttributeError: - description = "(no description available)" - - print " %-*s %s" % (max_length, cmd, description) - - # print_command_list () - - - def print_commands (self): - """Print out a help message listing all available commands with - a description of each. The list is divided into "standard - commands" (listed in distutils.command.__all__) and "extra - commands" (mentioned in self.cmdclass, but not a standard - command). The descriptions come from the command class - attribute 'description'.""" - - import distutils.command - std_commands = distutils.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append (cmd) - - max_length = 0 - for cmd in (std_commands + extra_commands): - if len (cmd) > max_length: - max_length = len (cmd) - - self.print_command_list (std_commands, - "Standard commands", - max_length) - if extra_commands: - print - self.print_command_list (extra_commands, - "Extra commands", - max_length) - - # print_commands () - - - - # -- Command class/object methods ---------------------------------- - - # This is a method just so it can be overridden if desired; it doesn't - # actually use or change any attributes of the Distribution instance. - def find_command_class (self, command): - """Given a command, derives the names of the module and class - expected to implement the command: eg. 'foo_bar' becomes - 'distutils.command.foo_bar' (the module) and 'FooBar' (the - class within that module). Loads the module, extracts the - class from it, and returns the class object. - - Raises DistutilsModuleError with a semi-user-targeted error - message if the expected module could not be loaded, or the - expected class was not found in it.""" - - module_name = 'distutils.command.' + command - klass_name = command - - try: - __import__ (module_name) - module = sys.modules[module_name] - except ImportError: - raise DistutilsModuleError, \ - "invalid command '%s' (no module named '%s')" % \ - (command, module_name) - - try: - klass = vars(module)[klass_name] - except KeyError: - raise DistutilsModuleError, \ - "invalid command '%s' (no class '%s' in module '%s')" \ - % (command, klass_name, module_name) - - return klass - - # find_command_class () - - - def create_command_obj (self, command): - """Figure out the class that should implement a command, - instantiate it, cache and return the new "command object". - The "command class" is determined either by looking it up in - the 'cmdclass' attribute (this is the mechanism whereby - clients may override default Distutils commands or add their - own), or by calling the 'find_command_class()' method (if the - command name is not in 'cmdclass'.""" - - # Determine the command class -- either it's in the command_class - # dictionary, or we have to divine the module and class name - klass = self.cmdclass.get(command) - if not klass: - klass = self.find_command_class (command) - self.cmdclass[command] = klass - - # Found the class OK -- instantiate it - cmd_obj = klass (self) - return cmd_obj - - - def find_command_obj (self, command, create=1): - """Look up and return a command object in the cache maintained by - 'create_command_obj()'. If none found, the action taken - depends on 'create': if true (the default), create a new - command object by calling 'create_command_obj()' and return - it; otherwise, return None. If 'command' is an invalid - command name, then DistutilsModuleError will be raised.""" - - cmd_obj = self.command_obj.get (command) - if not cmd_obj and create: - cmd_obj = self.create_command_obj (command) - self.command_obj[command] = cmd_obj - - return cmd_obj - - - # -- Methods that operate on the Distribution ---------------------- - - def announce (self, msg, level=1): - """Print 'msg' if 'level' is greater than or equal to the verbosity - level recorded in the 'verbose' attribute (which, currently, - can be only 0 or 1).""" - - if self.verbose >= level: - print msg - - - def run_commands (self): - """Run each command that was seen on the client command line. - Uses the list of commands found and cache of command objects - created by 'create_command_obj()'.""" - - for cmd in self.commands: - self.run_command (cmd) - - - def get_option (self, option): - """Return the value of a distribution option. Raise - DistutilsOptionError if 'option' is not known.""" - - try: - return getattr (self, opt) - except AttributeError: - raise DistutilsOptionError, \ - "unknown distribution option %s" % option - - - def get_options (self, *options): - """Return (as a tuple) the values of several distribution - options. Raise DistutilsOptionError if any element of - 'options' is not known.""" - - values = [] - try: - for opt in options: - values.append (getattr (self, opt)) - except AttributeError, name: - raise DistutilsOptionError, \ - "unknown distribution option %s" % name - - return tuple (values) - - - # -- Methods that operate on its Commands -------------------------- - - def run_command (self, command): - - """Do whatever it takes to run a command (including nothing at all, - if the command has already been run). Specifically: if we have - already created and run the command named by 'command', return - silently without doing anything. If the command named by - 'command' doesn't even have a command object yet, create one. - Then invoke 'run()' on that command object (or an existing - one).""" - - # Already been here, done that? then return silently. - if self.have_run.get (command): - return - - self.announce ("running " + command) - cmd_obj = self.find_command_obj (command) - cmd_obj.ensure_ready () - cmd_obj.run () - self.have_run[command] = 1 - - - def get_command_option (self, command, option): - """Create a command object for 'command' if necessary, ensure that - its option values are all set to their final values, and return - the value of its 'option' option. Raise DistutilsOptionError if - 'option' is not known for that 'command'.""" - - cmd_obj = self.find_command_obj (command) - cmd_obj.ensure_ready () - return cmd_obj.get_option (option) - try: - return getattr (cmd_obj, option) - except AttributeError: - raise DistutilsOptionError, \ - "command %s: no such option %s" % (command, option) - - - def get_command_options (self, command, *options): - """Create a command object for 'command' if necessary, ensure that - its option values are all set to their final values, and return - a tuple containing the values of all the options listed in - 'options' for that command. Raise DistutilsOptionError if any - invalid option is supplied in 'options'.""" - - cmd_obj = self.find_command_obj (command) - cmd_obj.ensure_ready () - values = [] - try: - for opt in options: - values.append (getattr (cmd_obj, option)) - except AttributeError, name: - raise DistutilsOptionError, \ - "command %s: no such option %s" % (command, name) - - return tuple (values) - - - # -- Distribution query methods ------------------------------------ - - def has_pure_modules (self): - return len (self.packages or self.py_modules or []) > 0 - - def has_ext_modules (self): - return self.ext_modules and len (self.ext_modules) > 0 - - def has_c_libraries (self): - return self.libraries and len (self.libraries) > 0 - - def has_modules (self): - return self.has_pure_modules() or self.has_ext_modules() - - def is_pure (self): - return (self.has_pure_modules() and - not self.has_ext_modules() and - not self.has_c_libraries()) - - def get_name (self): - return self.name or "UNKNOWN" - - def get_full_name (self): - return "%s-%s" % ((self.name or "UNKNOWN"), (self.version or "???")) - - -# class Distribution - - -class Command: - """Abstract base class for defining command classes, the "worker bees" - of the Distutils. A useful analogy for command classes is to - think of them as subroutines with local variables called - "options". The options are "declared" in 'initialize_options()' - and "defined" (given their final values, aka "finalized") in - 'finalize_options()', both of which must be defined by every - command class. The distinction between the two is necessary - because option values might come from the outside world (command - line, option file, ...), and any options dependent on other - options must be computed *after* these outside influences have - been processed -- hence 'finalize_options()'. The "body" of the - subroutine, where it does all its work based on the values of its - options, is the 'run()' method, which must also be implemented by - every command class.""" - - # -- Creation/initialization methods ------------------------------- - - def __init__ (self, dist): - """Create and initialize a new Command object. Most importantly, - invokes the 'initialize_options()' method, which is the - real initializer and depends on the actual command being - instantiated.""" - - if not isinstance (dist, Distribution): - raise TypeError, "dist must be a Distribution instance" - if self.__class__ is Command: - raise RuntimeError, "Command is an abstract class" - - self.distribution = dist - self.initialize_options () - - # Per-command versions of the global flags, so that the user can - # customize Distutils' behaviour command-by-command and let some - # commands fallback on the Distribution's behaviour. None means - # "not defined, check self.distribution's copy", while 0 or 1 mean - # false and true (duh). Note that this means figuring out the real - # value of each flag is a touch complicatd -- hence "self.verbose" - # (etc.) will be handled by __getattr__, below. - self._verbose = None - self._dry_run = None - self._force = None - - # The 'help' flag is just used for command-line parsing, so - # none of that complicated bureaucracy is needed. - self.help = 0 - - # 'ready' records whether or not 'finalize_options()' has been - # called. 'finalize_options()' itself should not pay attention to - # this flag: it is the business of 'ensure_ready()', which always - # calls 'finalize_options()', to respect/update it. - self.ready = 0 - - # end __init__ () - - - def __getattr__ (self, attr): - if attr in ('verbose', 'dry_run', 'force'): - myval = getattr (self, "_" + attr) - if myval is None: - return getattr (self.distribution, attr) - else: - return myval - else: - raise AttributeError, attr - - - def ensure_ready (self): - if not self.ready: - self.finalize_options () - self.ready = 1 - - - # Subclasses must define: - # initialize_options() - # provide default values for all options; may be overridden - # by Distutils client, by command-line options, or by options - # from option file - # finalize_options() - # decide on the final values for all options; this is called - # after all possible intervention from the outside world - # (command-line, option file, etc.) has been processed - # run() - # run the command: do whatever it is we're here to do, - # controlled by the command's various option values - - def initialize_options (self): - """Set default values for all the options that this command - supports. Note that these defaults may be overridden - by the command-line supplied by the user; thus, this is - not the place to code dependencies between options; generally, - 'initialize_options()' implementations are just a bunch - of "self.foo = None" assignments. - - This method must be implemented by all command classes.""" - - raise RuntimeError, \ - "abstract method -- subclass %s must override" % self.__class__ - - def finalize_options (self): - """Set final values for all the options that this command - supports. This is always called as late as possible, ie. - after any option assignments from the command-line or from - other commands have been done. Thus, this is the place to to - code option dependencies: if 'foo' depends on 'bar', then it - is safe to set 'foo' from 'bar' as long as 'foo' still has - the same value it was assigned in 'initialize_options()'. - - This method must be implemented by all command classes.""" - - raise RuntimeError, \ - "abstract method -- subclass %s must override" % self.__class__ - - def run (self): - """A command's raison d'etre: carry out the action it exists - to perform, controlled by the options initialized in - 'initialize_options()', customized by the user and other - commands, and finalized in 'finalize_options()'. All - terminal output and filesystem interaction should be done by - 'run()'. - - This method must be implemented by all command classes.""" - - raise RuntimeError, \ - "abstract method -- subclass %s must override" % self.__class__ - - def announce (self, msg, level=1): - """If the Distribution instance to which this command belongs - has a verbosity level of greater than or equal to 'level' - print 'msg' to stdout.""" - - if self.verbose >= level: - print msg - - - # -- Option query/set methods -------------------------------------- - - def get_option (self, option): - """Return the value of a single option for this command. Raise - DistutilsOptionError if 'option' is not known.""" - try: - return getattr (self, option) - except AttributeError: - raise DistutilsOptionError, \ - "command %s: no such option %s" % \ - (self.get_command_name(), option) - - - def get_options (self, *options): - """Return (as a tuple) the values of several options for this - command. Raise DistutilsOptionError if any of the options in - 'options' are not known.""" - - values = [] - try: - for opt in options: - values.append (getattr (self, opt)) - except AttributeError, name: - raise DistutilsOptionError, \ - "command %s: no such option %s" % \ - (self.get_command_name(), name) - - return tuple (values) - - - def set_option (self, option, value): - """Set the value of a single option for this command. Raise - DistutilsOptionError if 'option' is not known.""" - - if not hasattr (self, option): - raise DistutilsOptionError, \ - "command '%s': no such option '%s'" % \ - (self.get_command_name(), option) - if value is not None: - setattr (self, option, value) - - def set_options (self, **optval): - """Set the values of several options for this command. Raise - DistutilsOptionError if any of the options specified as - keyword arguments are not known.""" - - for k in optval.keys(): - if optval[k] is not None: - self.set_option (k, optval[k]) - - - # -- Convenience methods for commands ------------------------------ - - def get_command_name (self): - if hasattr (self, 'command_name'): - return self.command_name - else: - class_name = self.__class__.__name__ - - # The re.split here returs empty strings delimited by the - # words we're actually interested in -- e.g. "FooBarBaz" - # splits to ['', 'Foo', '', 'Bar', '', 'Baz', '']. Hence - # the 'filter' to strip out the empties. - words = filter (None, re.split (r'([A-Z][a-z]+)', class_name)) - self.command_name = string.join (map (string.lower, words), "_") - return self.command_name - - - def set_undefined_options (self, src_cmd, *option_pairs): - """Set the values of any "undefined" options from corresponding - option values in some other command object. "Undefined" here - means "is None", which is the convention used to indicate - that an option has not been changed between - 'set_initial_values()' and 'set_final_values()'. Usually - called from 'set_final_values()' for options that depend on - some other command rather than another option of the same - command. 'src_cmd' is the other command from which option - values will be taken (a command object will be created for it - if necessary); the remaining arguments are - '(src_option,dst_option)' tuples which mean "take the value - of 'src_option' in the 'src_cmd' command object, and copy it - to 'dst_option' in the current command object".""" - - # Option_pairs: list of (src_option, dst_option) tuples - - src_cmd_obj = self.distribution.find_command_obj (src_cmd) - src_cmd_obj.ensure_ready () - try: - for (src_option, dst_option) in option_pairs: - if getattr (self, dst_option) is None: - self.set_option (dst_option, - src_cmd_obj.get_option (src_option)) - except AttributeError, name: - # duh, which command? - raise DistutilsOptionError, "unknown option %s" % name - - - def find_peer (self, command, create=1): - """Wrapper around Distribution's 'find_command_obj()' method: - find (create if necessary and 'create' is true) the command - object for 'command'..""" - - cmd_obj = self.distribution.find_command_obj (command, create) - cmd_obj.ensure_ready () - return cmd_obj - - - def get_peer_option (self, command, option): - """Find or create the command object for 'command', and return - its 'option' option.""" - - cmd_obj = self.find_peer (command) - return cmd_obj.get_option (option) - - - def run_peer (self, command): - """Run some other command: uses the 'run_command()' method of - Distribution, which creates the command object if necessary - and then invokes its 'run()' method.""" - - self.distribution.run_command (command) - - - # -- External world manipulation ----------------------------------- - - def warn (self, msg): - sys.stderr.write ("warning: %s: %s\n" % - (self.get_command_name(), msg)) - - - def execute (self, func, args, msg=None, level=1): - """Perform some action that affects the outside world (eg. - by writing to the filesystem). Such actions are special because - they should be disabled by the "dry run" flag, and should - announce themselves if the current verbosity level is high - enough. This method takes care of all that bureaucracy for you; - all you have to do is supply the funtion to call and an argument - tuple for it (to embody the "external action" being performed), - a message to print if the verbosity level is high enough, and an - optional verbosity threshold.""" - - # Generate a message if we weren't passed one - if msg is None: - msg = "%s %s" % (func.__name__, `args`) - if msg[-2:] == ',)': # correct for singleton tuple - msg = msg[0:-2] + ')' - - # Print it if verbosity level is high enough - self.announce (msg, level) - - # And do it, as long as we're not in dry-run mode - if not self.dry_run: - apply (func, args) - - # execute() - - - def mkpath (self, name, mode=0777): - util.mkpath (name, mode, - self.verbose, self.dry_run) - - - def copy_file (self, infile, outfile, - preserve_mode=1, preserve_times=1, link=None, level=1): - """Copy a file respecting verbose, dry-run and force flags.""" - - return util.copy_file (infile, outfile, - preserve_mode, preserve_times, - not self.force, - link, - self.verbose >= level, - self.dry_run) - - - def copy_tree (self, infile, outfile, - preserve_mode=1, preserve_times=1, preserve_symlinks=0, - level=1): - """Copy an entire directory tree respecting verbose, dry-run, - and force flags.""" - - return util.copy_tree (infile, outfile, - preserve_mode,preserve_times,preserve_symlinks, - not self.force, - self.verbose >= level, - self.dry_run) - - - def move_file (self, src, dst, level=1): - """Move a file respecting verbose and dry-run flags.""" - return util.move_file (src, dst, - self.verbose >= level, - self.dry_run) - - - def spawn (self, cmd, search_path=1, level=1): - from distutils.spawn import spawn - spawn (cmd, search_path, - self.verbose >= level, - self.dry_run) - - - def make_archive (self, base_name, format, - root_dir=None, base_dir=None): - util.make_archive (base_name, format, root_dir, base_dir, - self.verbose, self.dry_run) - - - def make_file (self, infiles, outfile, func, args, - exec_msg=None, skip_msg=None, level=1): - - """Special case of 'execute()' for operations that process one or - more input files and generate one output file. Works just like - 'execute()', except the operation is skipped and a different - message printed if 'outfile' already exists and is newer than - all files listed in 'infiles'.""" - - - if exec_msg is None: - exec_msg = "generating %s from %s" % \ - (outfile, string.join (infiles, ', ')) - if skip_msg is None: - skip_msg = "skipping %s (inputs unchanged)" % outfile - - - # Allow 'infiles' to be a single string - if type (infiles) is StringType: - infiles = (infiles,) - elif type (infiles) not in (ListType, TupleType): - raise TypeError, \ - "'infiles' must be a string, or a list or tuple of strings" - - # If 'outfile' must be regenerated (either because it doesn't - # exist, is out-of-date, or the 'force' flag is true) then - # perform the action that presumably regenerates it - if self.force or util.newer_group (infiles, outfile): - self.execute (func, args, exec_msg, level) - - # Otherwise, print the "skip" message - else: - self.announce (skip_msg, level) - - # make_file () - -# class Command diff --git a/dist.py b/dist.py new file mode 100644 index 00000000..50e75569 --- /dev/null +++ b/dist.py @@ -0,0 +1,567 @@ +"""distutils.dist + +Provides the Distribution class, which represents the module distribution +being built/installed/distributed.""" + +# created 2000/04/03, Greg Ward +# (extricated from core.py; actually dates back to the beginning) + +__revision__ = "$Id$" + +import sys, string, re +from types import * +from copy import copy +from distutils.errors import * +from distutils.fancy_getopt import fancy_getopt, print_help + + +# Regex to define acceptable Distutils command names. This is not *quite* +# the same as a Python NAME -- I don't allow leading underscores. The fact +# that they're very similar is no coincidence; the default naming scheme is +# to look for a Python module named after the command. +command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') + + +class Distribution: + """The core of the Distutils. Most of the work hiding behind + 'setup' is really done within a Distribution instance, which + farms the work out to the Distutils commands specified on the + command line. + + Clients will almost never instantiate Distribution directly, + unless the 'setup' function is totally inadequate to their needs. + However, it is conceivable that a client might wish to subclass + Distribution for some specialized purpose, and then pass the + subclass to 'setup' as the 'distclass' keyword argument. If so, + it is necessary to respect the expectations that 'setup' has of + Distribution: it must have a constructor and methods + 'parse_command_line()' and 'run_commands()' with signatures like + those described below.""" + + + # 'global_options' describes the command-line options that may be + # supplied to the client (setup.py) prior to any actual commands. + # Eg. "./setup.py -nv" or "./setup.py --verbose" both take advantage of + # these global options. This list should be kept to a bare minimum, + # since every global option is also valid as a command option -- and we + # don't want to pollute the commands with too many options that they + # have minimal control over. + global_options = [('verbose', 'v', + "run verbosely (default)"), + ('quiet', 'q', + "run quietly (turns verbosity off)"), + ('dry-run', 'n', + "don't actually do anything"), + ('force', 'f', + "skip dependency checking between files"), + ('help', 'h', + "show this help message"), + ] + negative_opt = {'quiet': 'verbose'} + + + # -- Creation/initialization methods ------------------------------- + + def __init__ (self, attrs=None): + """Construct a new Distribution instance: initialize all the + attributes of a Distribution, and then uses 'attrs' (a + dictionary mapping attribute names to values) to assign + some of those attributes their "real" values. (Any attributes + not mentioned in 'attrs' will be assigned to some null + value: 0, None, an empty list or dictionary, etc.) Most + importantly, initialize the 'command_obj' attribute + to the empty dictionary; this will be filled in with real + command objects by 'parse_command_line()'.""" + + # Default values for our command-line options + self.verbose = 1 + self.dry_run = 0 + self.force = 0 + self.help = 0 + self.help_commands = 0 + + # And the "distribution meta-data" options -- these can only + # come from setup.py (the caller), not the command line + # (or a hypothetical config file). + self.name = None + self.version = None + self.author = None + self.author_email = None + self.maintainer = None + self.maintainer_email = None + self.url = None + self.licence = None + self.description = None + + # 'cmdclass' maps command names to class objects, so we + # can 1) quickly figure out which class to instantiate when + # we need to create a new command object, and 2) have a way + # for the client to override command classes + self.cmdclass = {} + + # These options are really the business of various commands, rather + # than of the Distribution itself. We provide aliases for them in + # Distribution as a convenience to the developer. + # dictionary. + self.packages = None + self.package_dir = None + self.py_modules = None + self.libraries = None + self.ext_modules = None + self.ext_package = None + self.include_dirs = None + self.extra_path = None + + # And now initialize bookkeeping stuff that can't be supplied by + # the caller at all. 'command_obj' maps command names to + # Command instances -- that's how we enforce that every command + # class is a singleton. + self.command_obj = {} + + # 'have_run' maps command names to boolean values; it keeps track + # of whether we have actually run a particular command, to make it + # cheap to "run" a command whenever we think we might need to -- if + # it's already been done, no need for expensive filesystem + # operations, we just check the 'have_run' dictionary and carry on. + # It's only safe to query 'have_run' for a command class that has + # been instantiated -- a false value will be inserted when the + # command object is created, and replaced with a true value when + # the command is succesfully run. Thus it's probably best to use + # '.get()' rather than a straight lookup. + self.have_run = {} + + # Now we'll use the attrs dictionary (ultimately, keyword args from + # the client) to possibly override any or all of these distribution + # options. + if attrs: + + # Pull out the set of command options and work on them + # specifically. Note that this order guarantees that aliased + # command options will override any supplied redundantly + # through the general options dictionary. + options = attrs.get ('options') + if options: + del attrs['options'] + for (command, cmd_options) in options.items(): + cmd_obj = self.find_command_obj (command) + for (key, val) in cmd_options.items(): + cmd_obj.set_option (key, val) + # loop over commands + # if any command options + + # Now work on the rest of the attributes. Any attribute that's + # not already defined is invalid! + for (key,val) in attrs.items(): + if hasattr (self, key): + setattr (self, key, val) + else: + raise DistutilsOptionError, \ + "invalid distribution option '%s'" % key + + # __init__ () + + + def parse_command_line (self, args): + """Parse the setup script's command line: set any Distribution + attributes tied to command-line options, create all command + objects, and set their options from the command-line. 'args' + must be a list of command-line arguments, most likely + 'sys.argv[1:]' (see the 'setup()' function). This list is first + processed for "global options" -- options that set attributes of + the Distribution instance. Then, it is alternately scanned for + Distutils command and options for that command. Each new + command terminates the options for the previous command. The + allowed options for a command are determined by the 'options' + attribute of the command object -- thus, we instantiate (and + cache) every command object here, in order to access its + 'options' attribute. Any error in that 'options' attribute + raises DistutilsGetoptError; any error on the command-line + raises DistutilsArgError. If no Distutils commands were found + on the command line, raises DistutilsArgError. Return true if + command-line successfully parsed and we should carry on with + executing commands; false if no errors but we shouldn't execute + commands (currently, this only happens if user asks for + help).""" + + # late import because of mutual dependence between these classes + from distutils.cmd import Command + + + # We have to parse the command line a bit at a time -- global + # options, then the first command, then its options, and so on -- + # because each command will be handled by a different class, and + # the options that are valid for a particular class aren't + # known until we instantiate the command class, which doesn't + # happen until we know what the command is. + + self.commands = [] + options = self.global_options + \ + [('help-commands', None, + "list all available commands")] + args = fancy_getopt (options, self.negative_opt, + self, sys.argv[1:]) + + # User just wants a list of commands -- we'll print it out and stop + # processing now (ie. if they ran "setup --help-commands foo bar", + # we ignore "foo bar"). + if self.help_commands: + self.print_commands () + print + print usage + return + + while args: + # Pull the current command from the head of the command line + command = args[0] + if not command_re.match (command): + raise SystemExit, "invalid command name '%s'" % command + self.commands.append (command) + + # Make sure we have a command object to put the options into + # (this either pulls it out of a cache of command objects, + # or finds and instantiates the command class). + try: + cmd_obj = self.find_command_obj (command) + except DistutilsModuleError, msg: + raise DistutilsArgError, msg + + # Require that the command class be derived from Command -- + # that way, we can be sure that we at least have the 'run' + # and 'get_option' methods. + if not isinstance (cmd_obj, Command): + raise DistutilsClassError, \ + "command class %s must subclass Command" % \ + cmd_obj.__class__ + + # Also make sure that the command object provides a list of its + # known options + if not (hasattr (cmd_obj, 'user_options') and + type (cmd_obj.user_options) is ListType): + raise DistutilsClassError, \ + ("command class %s must provide " + + "'user_options' attribute (a list of tuples)") % \ + cmd_obj.__class__ + + # Poof! like magic, all commands support the global + # options too, just by adding in 'global_options'. + negative_opt = self.negative_opt + if hasattr (cmd_obj, 'negative_opt'): + negative_opt = copy (negative_opt) + negative_opt.update (cmd_obj.negative_opt) + + options = self.global_options + cmd_obj.user_options + args = fancy_getopt (options, negative_opt, + cmd_obj, args[1:]) + if cmd_obj.help: + print_help (self.global_options, + header="Global options:") + print + print_help (cmd_obj.user_options, + header="Options for '%s' command:" % command) + print + print usage + return + + self.command_obj[command] = cmd_obj + self.have_run[command] = 0 + + # while args + + # If the user wants help -- ie. they gave the "--help" option -- + # give it to 'em. We do this *after* processing the commands in + # case they want help on any particular command, eg. + # "setup.py --help foo". (This isn't the documented way to + # get help on a command, but I support it because that's how + # CVS does it -- might as well be consistent.) + if self.help: + print_help (self.global_options, header="Global options:") + print + + for command in self.commands: + klass = self.find_command_class (command) + print_help (klass.user_options, + header="Options for '%s' command:" % command) + print + + print usage + return + + # Oops, no commands found -- an end-user error + if not self.commands: + raise DistutilsArgError, "no commands supplied" + + # All is well: return true + return 1 + + # parse_command_line() + + + def print_command_list (self, commands, header, max_length): + """Print a subset of the list of all commands -- used by + 'print_commands()'.""" + + print header + ":" + + for cmd in commands: + klass = self.cmdclass.get (cmd) + if not klass: + klass = self.find_command_class (cmd) + try: + description = klass.description + except AttributeError: + description = "(no description available)" + + print " %-*s %s" % (max_length, cmd, description) + + # print_command_list () + + + def print_commands (self): + """Print out a help message listing all available commands with + a description of each. The list is divided into "standard + commands" (listed in distutils.command.__all__) and "extra + commands" (mentioned in self.cmdclass, but not a standard + command). The descriptions come from the command class + attribute 'description'.""" + + import distutils.command + std_commands = distutils.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass.keys(): + if not is_std.get(cmd): + extra_commands.append (cmd) + + max_length = 0 + for cmd in (std_commands + extra_commands): + if len (cmd) > max_length: + max_length = len (cmd) + + self.print_command_list (std_commands, + "Standard commands", + max_length) + if extra_commands: + print + self.print_command_list (extra_commands, + "Extra commands", + max_length) + + # print_commands () + + + + # -- Command class/object methods ---------------------------------- + + # This is a method just so it can be overridden if desired; it doesn't + # actually use or change any attributes of the Distribution instance. + def find_command_class (self, command): + """Given a command, derives the names of the module and class + expected to implement the command: eg. 'foo_bar' becomes + 'distutils.command.foo_bar' (the module) and 'FooBar' (the + class within that module). Loads the module, extracts the + class from it, and returns the class object. + + Raises DistutilsModuleError with a semi-user-targeted error + message if the expected module could not be loaded, or the + expected class was not found in it.""" + + module_name = 'distutils.command.' + command + klass_name = command + + try: + __import__ (module_name) + module = sys.modules[module_name] + except ImportError: + raise DistutilsModuleError, \ + "invalid command '%s' (no module named '%s')" % \ + (command, module_name) + + try: + klass = vars(module)[klass_name] + except KeyError: + raise DistutilsModuleError, \ + "invalid command '%s' (no class '%s' in module '%s')" \ + % (command, klass_name, module_name) + + return klass + + # find_command_class () + + + def create_command_obj (self, command): + """Figure out the class that should implement a command, + instantiate it, cache and return the new "command object". + The "command class" is determined either by looking it up in + the 'cmdclass' attribute (this is the mechanism whereby + clients may override default Distutils commands or add their + own), or by calling the 'find_command_class()' method (if the + command name is not in 'cmdclass'.""" + + # Determine the command class -- either it's in the command_class + # dictionary, or we have to divine the module and class name + klass = self.cmdclass.get(command) + if not klass: + klass = self.find_command_class (command) + self.cmdclass[command] = klass + + # Found the class OK -- instantiate it + cmd_obj = klass (self) + return cmd_obj + + + def find_command_obj (self, command, create=1): + """Look up and return a command object in the cache maintained by + 'create_command_obj()'. If none found, the action taken + depends on 'create': if true (the default), create a new + command object by calling 'create_command_obj()' and return + it; otherwise, return None. If 'command' is an invalid + command name, then DistutilsModuleError will be raised.""" + + cmd_obj = self.command_obj.get (command) + if not cmd_obj and create: + cmd_obj = self.create_command_obj (command) + self.command_obj[command] = cmd_obj + + return cmd_obj + + + # -- Methods that operate on the Distribution ---------------------- + + def announce (self, msg, level=1): + """Print 'msg' if 'level' is greater than or equal to the verbosity + level recorded in the 'verbose' attribute (which, currently, + can be only 0 or 1).""" + + if self.verbose >= level: + print msg + + + def run_commands (self): + """Run each command that was seen on the client command line. + Uses the list of commands found and cache of command objects + created by 'create_command_obj()'.""" + + for cmd in self.commands: + self.run_command (cmd) + + + def get_option (self, option): + """Return the value of a distribution option. Raise + DistutilsOptionError if 'option' is not known.""" + + try: + return getattr (self, opt) + except AttributeError: + raise DistutilsOptionError, \ + "unknown distribution option %s" % option + + + def get_options (self, *options): + """Return (as a tuple) the values of several distribution + options. Raise DistutilsOptionError if any element of + 'options' is not known.""" + + values = [] + try: + for opt in options: + values.append (getattr (self, opt)) + except AttributeError, name: + raise DistutilsOptionError, \ + "unknown distribution option %s" % name + + return tuple (values) + + + # -- Methods that operate on its Commands -------------------------- + + def run_command (self, command): + + """Do whatever it takes to run a command (including nothing at all, + if the command has already been run). Specifically: if we have + already created and run the command named by 'command', return + silently without doing anything. If the command named by + 'command' doesn't even have a command object yet, create one. + Then invoke 'run()' on that command object (or an existing + one).""" + + # Already been here, done that? then return silently. + if self.have_run.get (command): + return + + self.announce ("running " + command) + cmd_obj = self.find_command_obj (command) + cmd_obj.ensure_ready () + cmd_obj.run () + self.have_run[command] = 1 + + + def get_command_option (self, command, option): + """Create a command object for 'command' if necessary, ensure that + its option values are all set to their final values, and return + the value of its 'option' option. Raise DistutilsOptionError if + 'option' is not known for that 'command'.""" + + cmd_obj = self.find_command_obj (command) + cmd_obj.ensure_ready () + return cmd_obj.get_option (option) + try: + return getattr (cmd_obj, option) + except AttributeError: + raise DistutilsOptionError, \ + "command %s: no such option %s" % (command, option) + + + def get_command_options (self, command, *options): + """Create a command object for 'command' if necessary, ensure that + its option values are all set to their final values, and return + a tuple containing the values of all the options listed in + 'options' for that command. Raise DistutilsOptionError if any + invalid option is supplied in 'options'.""" + + cmd_obj = self.find_command_obj (command) + cmd_obj.ensure_ready () + values = [] + try: + for opt in options: + values.append (getattr (cmd_obj, option)) + except AttributeError, name: + raise DistutilsOptionError, \ + "command %s: no such option %s" % (command, name) + + return tuple (values) + + + # -- Distribution query methods ------------------------------------ + + def has_pure_modules (self): + return len (self.packages or self.py_modules or []) > 0 + + def has_ext_modules (self): + return self.ext_modules and len (self.ext_modules) > 0 + + def has_c_libraries (self): + return self.libraries and len (self.libraries) > 0 + + def has_modules (self): + return self.has_pure_modules() or self.has_ext_modules() + + def is_pure (self): + return (self.has_pure_modules() and + not self.has_ext_modules() and + not self.has_c_libraries()) + + def get_name (self): + return self.name or "UNKNOWN" + + def get_full_name (self): + return "%s-%s" % ((self.name or "UNKNOWN"), (self.version or "???")) + +# class Distribution + + +if __name__ == "__main__": + dist = Distribution () + print "ok" -- cgit v1.2.1 From f3cceb54cf92355a4d0ce52a5b7b568c490753b3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 4 Apr 2000 02:05:59 +0000 Subject: Reorganization: ripped util.py to shreds, creating in the process: - file_util.py: operations on single files - dir_util.py: operations on whole directories or directory trees - dep_util.py: simple timestamp-based dependency analysis - archive_util.py: creation of archive (tar, zip, ...) files The functions left in util.py are miscellany that don't fit in any of the new files. --- archive_util.py | 152 +++++++++++++ dep_util.py | 116 ++++++++++ dir_util.py | 196 +++++++++++++++++ file_util.py | 248 +++++++++++++++++++++ util.py | 671 +------------------------------------------------------- 5 files changed, 719 insertions(+), 664 deletions(-) create mode 100644 archive_util.py create mode 100644 dep_util.py create mode 100644 dir_util.py create mode 100644 file_util.py diff --git a/archive_util.py b/archive_util.py new file mode 100644 index 00000000..a28eed1a --- /dev/null +++ b/archive_util.py @@ -0,0 +1,152 @@ +"""distutils.archive_util + +Utility functions for creating archive files (tarballs, zip files, +that sort of thing).""" + +# created 2000/04/03, Greg Ward (extracted from util.py) + +__revision__ = "$Id$" + +import os +from distutils.errors import DistutilsExecError +from distutils.spawn import spawn + + +def make_tarball (base_name, base_dir, compress="gzip", + verbose=0, dry_run=0): + """Create a (possibly compressed) tar file from all the files under + 'base_dir'. 'compress' must be "gzip" (the default), "compress", or + None. Both "tar" and the compression utility named by 'compress' + must be on the default program search path, so this is probably + Unix-specific. The output tar file will be named 'base_dir' + + ".tar", possibly plus the appropriate compression extension + (".gz" or ".Z"). Return the output filename.""" + + # XXX GNU tar 1.13 has a nifty option to add a prefix directory. + # It's pretty new, though, so we certainly can't require it -- + # but it would be nice to take advantage of it to skip the + # "create a tree of hardlinks" step! (Would also be nice to + # detect GNU tar to use its 'z' option and save a step.) + + compress_ext = { 'gzip': ".gz", + 'compress': ".Z" } + + if compress is not None and compress not in ('gzip', 'compress'): + raise ValueError, \ + "bad value for 'compress': must be None, 'gzip', or 'compress'" + + archive_name = base_name + ".tar" + cmd = ["tar", "-cf", archive_name, base_dir] + spawn (cmd, verbose=verbose, dry_run=dry_run) + + if compress: + spawn ([compress, archive_name], verbose=verbose, dry_run=dry_run) + return archive_name + compress_ext[compress] + else: + return archive_name + +# make_tarball () + + +def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): + """Create a zip file from all the files under 'base_dir'. The + output zip file will be named 'base_dir' + ".zip". Uses either the + InfoZIP "zip" utility (if installed and found on the default search + path) or the "zipfile" Python module (if available). If neither + tool is available, raises DistutilsExecError. Returns the name + of the output zip file.""" + + # This initially assumed the Unix 'zip' utility -- but + # apparently InfoZIP's zip.exe works the same under Windows, so + # no changes needed! + + zip_filename = base_name + ".zip" + try: + spawn (["zip", "-rq", zip_filename, base_dir], + verbose=verbose, dry_run=dry_run) + except DistutilsExecError: + + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed" -- shouldn't try + # again in the latter case. (I think fixing this will + # require some cooperation from the spawn module -- perhaps + # a utility function to search the path, so we can fallback + # on zipfile.py without the failed spawn.) + try: + import zipfile + except ImportError: + raise DistutilsExecError, \ + ("unable to create zip file '%s': " + + "could neither find a standalone zip utility nor " + + "import the 'zipfile' module") % zip_filename + + if verbose: + print "creating '%s' and adding '%s' to it" % \ + (zip_filename, base_dir) + + def visit (z, dirname, names): + for name in names: + path = os.path.join (dirname, name) + if os.path.isfile (path): + z.write (path, path) + + if not dry_run: + z = zipfile.ZipFile (zip_filename, "wb", + compression=zipfile.ZIP_DEFLATED) + + os.path.walk (base_dir, visit, z) + z.close() + + return zip_filename + +# make_zipfile () + + +def make_archive (base_name, format, + root_dir=None, base_dir=None, + verbose=0, dry_run=0): + + """Create an archive file (eg. zip or tar). 'base_name' is the name + of the file to create, minus any format-specific extension; 'format' + is the archive format: one of "zip", "tar", "ztar", or "gztar". + 'root_dir' is a directory that will be the root directory of the + archive; ie. we typically chdir into 'root_dir' before creating the + archive. 'base_dir' is the directory where we start archiving from; + ie. 'base_dir' will be the common prefix of all files and + directories in the archive. 'root_dir' and 'base_dir' both default + to the current directory.""" + + save_cwd = os.getcwd() + if root_dir is not None: + if verbose: + print "changing into '%s'" % root_dir + base_name = os.path.abspath (base_name) + if not dry_run: + os.chdir (root_dir) + + if base_dir is None: + base_dir = os.curdir + + kwargs = { 'verbose': verbose, + 'dry_run': dry_run } + + if format == 'gztar': + func = make_tarball + kwargs['compress'] = 'gzip' + elif format == 'ztar': + func = make_tarball + kwargs['compress'] = 'compress' + elif format == 'tar': + func = make_tarball + kwargs['compress'] = None + elif format == 'zip': + func = make_zipfile + + apply (func, (base_name, base_dir), kwargs) + + if root_dir is not None: + if verbose: + print "changing back to '%s'" % save_cwd + os.chdir (save_cwd) + +# make_archive () diff --git a/dep_util.py b/dep_util.py new file mode 100644 index 00000000..97812eda --- /dev/null +++ b/dep_util.py @@ -0,0 +1,116 @@ +"""distutils.dep_util + +Utility functions for simple, timestamp-based dependency of files +and groups of files; also, function based entirely on such +timestamp dependency analysis.""" + +# created 2000/04/03, Greg Ward (extracted from util.py) + +__revision__ = "$Id$" + +import os +from distutils.errors import DistutilsFileError + + +def newer (source, target): + """Return true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. Return + false if both exist and 'target' is the same age or younger than + 'source'. Raise DistutilsFileError if 'source' does not + exist.""" + + if not os.path.exists (source): + raise DistutilsFileError, "file '%s' does not exist" % source + if not os.path.exists (target): + return 1 + + from stat import ST_MTIME + mtime1 = os.stat(source)[ST_MTIME] + mtime2 = os.stat(target)[ST_MTIME] + + return mtime1 > mtime2 + +# newer () + + +def newer_pairwise (sources, targets): + """Walk two filename lists in parallel, testing if each source is newer + than its corresponding target. Return a pair of lists (sources, + targets) where source is newer than target, according to the + semantics of 'newer()'.""" + + if len (sources) != len (targets): + raise ValueError, "'sources' and 'targets' must be same length" + + # build a pair of lists (sources, targets) where source is newer + n_sources = [] + n_targets = [] + for i in range (len (sources)): + if newer (sources[i], targets[i]): + n_sources.append (sources[i]) + n_targets.append (targets[i]) + + return (n_sources, n_targets) + +# newer_pairwise () + + +def newer_group (sources, target, missing='error'): + """Return true if 'target' is out-of-date with respect to any + file listed in 'sources'. In other words, if 'target' exists and + is newer than every file in 'sources', return false; otherwise + return true. 'missing' controls what we do when a source file is + missing; the default ("error") is to blow up with an OSError from + inside 'stat()'; if it is "ignore", we silently drop any missing + source files; if it is "newer", any missing source files make us + assume that 'target' is out-of-date (this is handy in "dry-run" + mode: it'll make you pretend to carry out commands that wouldn't + work because inputs are missing, but that doesn't matter because + you're not actually going to run the commands).""" + + # If the target doesn't even exist, then it's definitely out-of-date. + if not os.path.exists (target): + return 1 + + # Otherwise we have to find out the hard way: if *any* source file + # is more recent than 'target', then 'target' is out-of-date and + # we can immediately return true. If we fall through to the end + # of the loop, then 'target' is up-to-date and we return false. + from stat import ST_MTIME + target_mtime = os.stat (target)[ST_MTIME] + for source in sources: + if not os.path.exists (source): + if missing == 'error': # blow up when we stat() the file + pass + elif missing == 'ignore': # missing source dropped from + continue # target's dependency list + elif missing == 'newer': # missing source means target is + return 1 # out-of-date + + source_mtime = os.stat(source)[ST_MTIME] + if source_mtime > target_mtime: + return 1 + else: + return 0 + +# newer_group () + + +# XXX this isn't used anywhere, and worse, it has the same name as a method +# in Command with subtly different semantics. (This one just has one +# source -> one dest; that one has many sources -> one dest.) Nuke it? +def make_file (src, dst, func, args, + verbose=0, update_message=None, noupdate_message=None): + """Makes 'dst' from 'src' (both filenames) by calling 'func' with + 'args', but only if it needs to: i.e. if 'dst' does not exist or + 'src' is newer than 'dst'.""" + + if newer (src, dst): + if verbose and update_message: + print update_message + apply (func, args) + else: + if verbose and noupdate_message: + print noupdate_message + +# make_file () diff --git a/dir_util.py b/dir_util.py new file mode 100644 index 00000000..c049bbd2 --- /dev/null +++ b/dir_util.py @@ -0,0 +1,196 @@ +"""distutils.dir_util + +Utility functions for manipulating directories and directory trees.""" + +# created 2000/04/03, Greg Ward (extracted from util.py) + +__revision__ = "$Id$" + +import os +from distutils.errors import DistutilsFileError + + +# cache for by mkpath() -- in addition to cheapening redundant calls, +# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode +PATH_CREATED = {} + +# I don't use os.makedirs because a) it's new to Python 1.5.2, and +# b) it blows up if the directory already exists (I want to silently +# succeed in that case). +def mkpath (name, mode=0777, verbose=0, dry_run=0): + """Create a directory and any missing ancestor directories. If the + directory already exists (or if 'name' is the empty string, which + means the current directory, which of course exists), then do + nothing. Raise DistutilsFileError if unable to create some + directory along the way (eg. some sub-path exists, but is a file + rather than a directory). If 'verbose' is true, print a one-line + summary of each mkdir to stdout. Return the list of directories + actually created.""" + + global PATH_CREATED + + # XXX what's the better way to handle verbosity? print as we create + # each directory in the path (the current behaviour), or only announce + # the creation of the whole path? (quite easy to do the latter since + # we're not using a recursive algorithm) + + name = os.path.normpath (name) + created_dirs = [] + if os.path.isdir (name) or name == '': + return created_dirs + if PATH_CREATED.get (name): + return created_dirs + + (head, tail) = os.path.split (name) + tails = [tail] # stack of lone dirs to create + + while head and tail and not os.path.isdir (head): + #print "splitting '%s': " % head, + (head, tail) = os.path.split (head) + #print "to ('%s','%s')" % (head, tail) + tails.insert (0, tail) # push next higher dir onto stack + + #print "stack of tails:", tails + + # now 'head' contains the deepest directory that already exists + # (that is, the child of 'head' in 'name' is the highest directory + # that does *not* exist) + for d in tails: + #print "head = %s, d = %s: " % (head, d), + head = os.path.join (head, d) + if PATH_CREATED.get (head): + continue + + if verbose: + print "creating", head + + if not dry_run: + try: + os.mkdir (head) + created_dirs.append(head) + except OSError, exc: + raise DistutilsFileError, \ + "could not create '%s': %s" % (head, exc[-1]) + + PATH_CREATED[head] = 1 + return created_dirs + +# mkpath () + + +def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0): + + """Create all the empty directories under 'base_dir' needed to + put 'files' there. 'base_dir' is just the a name of a directory + which doesn't necessarily exist yet; 'files' is a list of filenames + to be interpreted relative to 'base_dir'. 'base_dir' + the + directory portion of every file in 'files' will be created if it + doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as + for 'mkpath()'.""" + + # First get the list of directories to create + need_dir = {} + for file in files: + need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1 + need_dirs = need_dir.keys() + need_dirs.sort() + + # Now create them + for dir in need_dirs: + mkpath (dir, mode, verbose, dry_run) + +# create_tree () + + +def copy_tree (src, dst, + preserve_mode=1, + preserve_times=1, + preserve_symlinks=0, + update=0, + verbose=0, + dry_run=0): + + """Copy an entire directory tree 'src' to a new location 'dst'. Both + 'src' and 'dst' must be directory names. If 'src' is not a + directory, raise DistutilsFileError. If 'dst' does not exist, it is + created with 'mkpath()'. The end result of the copy is that every + file in 'src' is copied to 'dst', and directories under 'src' are + recursively copied to 'dst'. Return the list of files that were + copied or might have been copied, using their output name. The + return value is unaffected by 'update' or 'dry_run': it is simply + the list of all files under 'src', with the names changed to be + under 'dst'. + + 'preserve_mode' and 'preserve_times' are the same as for + 'copy_file'; note that they only apply to regular files, not to + directories. If 'preserve_symlinks' is true, symlinks will be + copied as symlinks (on platforms that support them!); otherwise + (the default), the destination of the symlink will be copied. + 'update' and 'verbose' are the same as for 'copy_file'.""" + + from distutils.file_util import copy_file + + if not dry_run and not os.path.isdir (src): + raise DistutilsFileError, \ + "cannot copy tree '%s': not a directory" % src + try: + names = os.listdir (src) + except os.error, (errno, errstr): + if dry_run: + names = [] + else: + raise DistutilsFileError, \ + "error listing files in '%s': %s" % (src, errstr) + + if not dry_run: + mkpath (dst, verbose=verbose) + + outputs = [] + + for n in names: + src_name = os.path.join (src, n) + dst_name = os.path.join (dst, n) + + if preserve_symlinks and os.path.islink (src_name): + link_dest = os.readlink (src_name) + if verbose: + print "linking %s -> %s" % (dst_name, link_dest) + if not dry_run: + os.symlink (link_dest, dst_name) + outputs.append (dst_name) + + elif os.path.isdir (src_name): + outputs.extend ( + copy_tree (src_name, dst_name, + preserve_mode, preserve_times, preserve_symlinks, + update, verbose, dry_run)) + else: + copy_file (src_name, dst_name, + preserve_mode, preserve_times, + update, None, verbose, dry_run) + outputs.append (dst_name) + + return outputs + +# copy_tree () + + +def remove_tree (directory, verbose=0, dry_run=0): + """Recursively remove an entire directory tree. Any errors are ignored + (apart from being reported to stdout if 'verbose' is true).""" + + from shutil import rmtree + + if verbose: + print "removing '%s' (and everything under it)" % directory + if dry_run: + return + try: + rmtree(directory,1) + except (IOError, OSError), exc: + if verbose: + if exc.filename: + print "error removing %s: %s (%s)" % \ + (directory, exc.strerror, exc.filename) + else: + print "error removing %s: %s" % (directory, exc.strerror) diff --git a/file_util.py b/file_util.py new file mode 100644 index 00000000..91545abb --- /dev/null +++ b/file_util.py @@ -0,0 +1,248 @@ +"""distutils.file_util + +Utility functions for operating on single files.""" + +# created 2000/04/03, Greg Ward (extracted from util.py) + +__revision__ = "$Id$" + +import os +from distutils.errors import DistutilsFileError + + +# for generating verbose output in 'copy_file()' +_copy_action = { None: 'copying', + 'hard': 'hard linking', + 'sym': 'symbolically linking' } + + +def _copy_file_contents (src, dst, buffer_size=16*1024): + """Copy the file 'src' to 'dst'; both must be filenames. Any error + opening either file, reading from 'src', or writing to 'dst', + raises DistutilsFileError. Data is read/written in chunks of + 'buffer_size' bytes (default 16k). No attempt is made to handle + anything apart from regular files.""" + + # Stolen from shutil module in the standard library, but with + # custom error-handling added. + + fsrc = None + fdst = None + try: + try: + fsrc = open(src, 'rb') + except os.error, (errno, errstr): + raise DistutilsFileError, \ + "could not open '%s': %s" % (src, errstr) + + try: + fdst = open(dst, 'wb') + except os.error, (errno, errstr): + raise DistutilsFileError, \ + "could not create '%s': %s" % (dst, errstr) + + while 1: + try: + buf = fsrc.read (buffer_size) + except os.error, (errno, errstr): + raise DistutilsFileError, \ + "could not read from '%s': %s" % (src, errstr) + + if not buf: + break + + try: + fdst.write(buf) + except os.error, (errno, errstr): + raise DistutilsFileError, \ + "could not write to '%s': %s" % (dst, errstr) + + finally: + if fdst: + fdst.close() + if fsrc: + fsrc.close() + +# _copy_file_contents() + + +def copy_file (src, dst, + preserve_mode=1, + preserve_times=1, + update=0, + link=None, + verbose=0, + dry_run=0): + + """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' + is copied there with the same name; otherwise, it must be a + filename. (If the file exists, it will be ruthlessly clobbered.) + If 'preserve_mode' is true (the default), the file's mode (type + and permission bits, or whatever is analogous on the current + platform) is copied. If 'preserve_times' is true (the default), + the last-modified and last-access times are copied as well. If + 'update' is true, 'src' will only be copied if 'dst' does not + exist, or if 'dst' does exist but is older than 'src'. If + 'verbose' is true, then a one-line summary of the copy will be + printed to stdout. + + 'link' allows you to make hard links (os.link) or symbolic links + (os.symlink) instead of copying: set it to "hard" or "sym"; if it + is None (the default), files are copied. Don't set 'link' on + systems that don't support it: 'copy_file()' doesn't check if + hard or symbolic linking is availalble. + + Under Mac OS, uses the native file copy function in macostools; + on other systems, uses '_copy_file_contents()' to copy file + contents. + + Return true if the file was copied (or would have been copied), + false otherwise (ie. 'update' was true and the destination is + up-to-date).""" + + # XXX if the destination file already exists, we clobber it if + # copying, but blow up if linking. Hmmm. And I don't know what + # macostools.copyfile() does. Should definitely be consistent, and + # should probably blow up if destination exists and we would be + # changing it (ie. it's not already a hard/soft link to src OR + # (not update) and (src newer than dst). + + from stat import * + from distutils.dep_util import newer + + if not os.path.isfile (src): + raise DistutilsFileError, \ + "can't copy '%s': doesn't exist or not a regular file" % src + + if os.path.isdir (dst): + dir = dst + dst = os.path.join (dst, os.path.basename (src)) + else: + dir = os.path.dirname (dst) + + if update and not newer (src, dst): + if verbose: + print "not copying %s (output up-to-date)" % src + return 0 + + try: + action = _copy_action[link] + except KeyError: + raise ValueError, \ + "invalid value '%s' for 'link' argument" % link + if verbose: + print "%s %s -> %s" % (action, src, dir) + + if dry_run: + return 1 + + # On a Mac, use the native file copy routine + if os.name == 'mac': + import macostools + try: + macostools.copy (src, dst, 0, preserve_times) + except OSError, exc: + raise DistutilsFileError, \ + "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) + + # If linking (hard or symbolic), use the appropriate system call + # (Unix only, of course, but that's the caller's responsibility) + elif link == 'hard': + if not (os.path.exists (dst) and os.path.samefile (src, dst)): + os.link (src, dst) + elif link == 'sym': + if not (os.path.exists (dst) and os.path.samefile (src, dst)): + os.symlink (src, dst) + + # Otherwise (non-Mac, not linking), copy the file contents and + # (optionally) copy the times and mode. + else: + _copy_file_contents (src, dst) + if preserve_mode or preserve_times: + st = os.stat (src) + + # According to David Ascher , utime() should be done + # before chmod() (at least under NT). + if preserve_times: + os.utime (dst, (st[ST_ATIME], st[ST_MTIME])) + if preserve_mode: + os.chmod (dst, S_IMODE (st[ST_MODE])) + + return 1 + +# copy_file () + + +# XXX I suspect this is Unix-specific -- need porting help! +def move_file (src, dst, + verbose=0, + dry_run=0): + + """Move a file 'src' to 'dst'. If 'dst' is a directory, the file + will be moved into it with the same name; otherwise, 'src' is + just renamed to 'dst'. Return the new full name of the file. + + Handles cross-device moves on Unix using + 'copy_file()'. What about other systems???""" + + from os.path import exists, isfile, isdir, basename, dirname + + if verbose: + print "moving %s -> %s" % (src, dst) + + if dry_run: + return dst + + if not isfile (src): + raise DistutilsFileError, \ + "can't move '%s': not a regular file" % src + + if isdir (dst): + dst = os.path.join (dst, basename (src)) + elif exists (dst): + raise DistutilsFileError, \ + "can't move '%s': destination '%s' already exists" % \ + (src, dst) + + if not isdir (dirname (dst)): + raise DistutilsFileError, \ + "can't move '%s': destination '%s' not a valid path" % \ + (src, dst) + + copy_it = 0 + try: + os.rename (src, dst) + except os.error, (num, msg): + if num == errno.EXDEV: + copy_it = 1 + else: + raise DistutilsFileError, \ + "couldn't move '%s' to '%s': %s" % (src, dst, msg) + + if copy_it: + copy_file (src, dst) + try: + os.unlink (src) + except os.error, (num, msg): + try: + os.unlink (dst) + except os.error: + pass + raise DistutilsFileError, \ + ("couldn't move '%s' to '%s' by copy/delete: " + + "delete '%s' failed: %s") % \ + (src, dst, src, msg) + + return dst + +# move_file () + + +def write_file (filename, contents): + """Create a file with the specified name and write 'contents' (a + sequence of strings without line terminators) to it.""" + + f = open (filename, "w") + for line in contents: + f.write (line + "\n") + f.close () diff --git a/util.py b/util.py index 22fc4375..ddcf0d28 100644 --- a/util.py +++ b/util.py @@ -1,11 +1,7 @@ """distutils.util -General-purpose utility functions used throughout the Distutils -(especially in command classes). Mostly filesystem manipulation, but -not limited to that. The functions in this module generally raise -DistutilsFileError when they have problems with the filesystem, because -os.error in pre-1.5.2 Python only gives the error message and not the -file causing it.""" +Miscellaneous utility functions -- anything that doesn't fit into +one of the other *util.py modules.""" # created 1999/03/08, Greg Ward @@ -15,526 +11,11 @@ import sys, os, string, re, shutil from distutils.errors import * from distutils.spawn import spawn -# cache for by mkpath() -- in addition to cheapening redundant calls, -# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode -PATH_CREATED = {} - -# for generating verbose output in 'copy_file()' -_copy_action = { None: 'copying', - 'hard': 'hard linking', - 'sym': 'symbolically linking' } - -# I don't use os.makedirs because a) it's new to Python 1.5.2, and -# b) it blows up if the directory already exists (I want to silently -# succeed in that case). -def mkpath (name, mode=0777, verbose=0, dry_run=0): - """Create a directory and any missing ancestor directories. If the - directory already exists (or if 'name' is the empty string, which - means the current directory, which of course exists), then do - nothing. Raise DistutilsFileError if unable to create some - directory along the way (eg. some sub-path exists, but is a file - rather than a directory). If 'verbose' is true, print a one-line - summary of each mkdir to stdout. Return the list of directories - actually created.""" - - global PATH_CREATED - - # XXX what's the better way to handle verbosity? print as we create - # each directory in the path (the current behaviour), or only announce - # the creation of the whole path? (quite easy to do the latter since - # we're not using a recursive algorithm) - - name = os.path.normpath (name) - created_dirs = [] - if os.path.isdir (name) or name == '': - return created_dirs - if PATH_CREATED.get (name): - return created_dirs - - (head, tail) = os.path.split (name) - tails = [tail] # stack of lone dirs to create - - while head and tail and not os.path.isdir (head): - #print "splitting '%s': " % head, - (head, tail) = os.path.split (head) - #print "to ('%s','%s')" % (head, tail) - tails.insert (0, tail) # push next higher dir onto stack - - #print "stack of tails:", tails - - # now 'head' contains the deepest directory that already exists - # (that is, the child of 'head' in 'name' is the highest directory - # that does *not* exist) - for d in tails: - #print "head = %s, d = %s: " % (head, d), - head = os.path.join (head, d) - if PATH_CREATED.get (head): - continue - - if verbose: - print "creating", head - - if not dry_run: - try: - os.mkdir (head) - created_dirs.append(head) - except OSError, exc: - raise DistutilsFileError, \ - "could not create '%s': %s" % (head, exc[-1]) - - PATH_CREATED[head] = 1 - return created_dirs - -# mkpath () - - -def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0): - - """Create all the empty directories under 'base_dir' needed to - put 'files' there. 'base_dir' is just the a name of a directory - which doesn't necessarily exist yet; 'files' is a list of filenames - to be interpreted relative to 'base_dir'. 'base_dir' + the - directory portion of every file in 'files' will be created if it - doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as - for 'mkpath()'.""" - - # First get the list of directories to create - need_dir = {} - for file in files: - need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1 - need_dirs = need_dir.keys() - need_dirs.sort() - - # Now create them - for dir in need_dirs: - mkpath (dir, mode, verbose, dry_run) - -# create_tree () - - -def newer (source, target): - """Return true if 'source' exists and is more recently modified than - 'target', or if 'source' exists and 'target' doesn't. Return - false if both exist and 'target' is the same age or younger than - 'source'. Raise DistutilsFileError if 'source' does not - exist.""" - - if not os.path.exists (source): - raise DistutilsFileError, "file '%s' does not exist" % source - if not os.path.exists (target): - return 1 - - from stat import ST_MTIME - mtime1 = os.stat(source)[ST_MTIME] - mtime2 = os.stat(target)[ST_MTIME] - - return mtime1 > mtime2 - -# newer () - - -def newer_pairwise (sources, targets): - """Walk two filename lists in parallel, testing if each source is newer - than its corresponding target. Return a pair of lists (sources, - targets) where source is newer than target, according to the - semantics of 'newer()'.""" - - if len (sources) != len (targets): - raise ValueError, "'sources' and 'targets' must be same length" - - # build a pair of lists (sources, targets) where source is newer - n_sources = [] - n_targets = [] - for i in range (len (sources)): - if newer (sources[i], targets[i]): - n_sources.append (sources[i]) - n_targets.append (targets[i]) - - return (n_sources, n_targets) - -# newer_pairwise () - - -def newer_group (sources, target, missing='error'): - """Return true if 'target' is out-of-date with respect to any - file listed in 'sources'. In other words, if 'target' exists and - is newer than every file in 'sources', return false; otherwise - return true. 'missing' controls what we do when a source file is - missing; the default ("error") is to blow up with an OSError from - inside 'stat()'; if it is "ignore", we silently drop any missing - source files; if it is "newer", any missing source files make us - assume that 'target' is out-of-date (this is handy in "dry-run" - mode: it'll make you pretend to carry out commands that wouldn't - work because inputs are missing, but that doesn't matter because - you're not actually going to run the commands).""" - - # If the target doesn't even exist, then it's definitely out-of-date. - if not os.path.exists (target): - return 1 - - # Otherwise we have to find out the hard way: if *any* source file - # is more recent than 'target', then 'target' is out-of-date and - # we can immediately return true. If we fall through to the end - # of the loop, then 'target' is up-to-date and we return false. - from stat import ST_MTIME - target_mtime = os.stat (target)[ST_MTIME] - for source in sources: - if not os.path.exists (source): - if missing == 'error': # blow up when we stat() the file - pass - elif missing == 'ignore': # missing source dropped from - continue # target's dependency list - elif missing == 'newer': # missing source means target is - return 1 # out-of-date - - source_mtime = os.stat(source)[ST_MTIME] - if source_mtime > target_mtime: - return 1 - else: - return 0 - -# newer_group () - - -# XXX this isn't used anywhere, and worse, it has the same name as a method -# in Command with subtly different semantics. (This one just has one -# source -> one dest; that one has many sources -> one dest.) Nuke it? -def make_file (src, dst, func, args, - verbose=0, update_message=None, noupdate_message=None): - """Makes 'dst' from 'src' (both filenames) by calling 'func' with - 'args', but only if it needs to: i.e. if 'dst' does not exist or - 'src' is newer than 'dst'.""" - - if newer (src, dst): - if verbose and update_message: - print update_message - apply (func, args) - else: - if verbose and noupdate_message: - print noupdate_message - -# make_file () - - -def _copy_file_contents (src, dst, buffer_size=16*1024): - """Copy the file 'src' to 'dst'; both must be filenames. Any error - opening either file, reading from 'src', or writing to 'dst', - raises DistutilsFileError. Data is read/written in chunks of - 'buffer_size' bytes (default 16k). No attempt is made to handle - anything apart from regular files.""" - - # Stolen from shutil module in the standard library, but with - # custom error-handling added. - - fsrc = None - fdst = None - try: - try: - fsrc = open(src, 'rb') - except os.error, (errno, errstr): - raise DistutilsFileError, \ - "could not open '%s': %s" % (src, errstr) - - try: - fdst = open(dst, 'wb') - except os.error, (errno, errstr): - raise DistutilsFileError, \ - "could not create '%s': %s" % (dst, errstr) - - while 1: - try: - buf = fsrc.read (buffer_size) - except os.error, (errno, errstr): - raise DistutilsFileError, \ - "could not read from '%s': %s" % (src, errstr) - - if not buf: - break - - try: - fdst.write(buf) - except os.error, (errno, errstr): - raise DistutilsFileError, \ - "could not write to '%s': %s" % (dst, errstr) - - finally: - if fdst: - fdst.close() - if fsrc: - fsrc.close() - -# _copy_file_contents() - - -def copy_file (src, dst, - preserve_mode=1, - preserve_times=1, - update=0, - link=None, - verbose=0, - dry_run=0): - - """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' - is copied there with the same name; otherwise, it must be a - filename. (If the file exists, it will be ruthlessly clobbered.) - If 'preserve_mode' is true (the default), the file's mode (type - and permission bits, or whatever is analogous on the current - platform) is copied. If 'preserve_times' is true (the default), - the last-modified and last-access times are copied as well. If - 'update' is true, 'src' will only be copied if 'dst' does not - exist, or if 'dst' does exist but is older than 'src'. If - 'verbose' is true, then a one-line summary of the copy will be - printed to stdout. - - 'link' allows you to make hard links (os.link) or symbolic links - (os.symlink) instead of copying: set it to "hard" or "sym"; if it - is None (the default), files are copied. Don't set 'link' on - systems that don't support it: 'copy_file()' doesn't check if - hard or symbolic linking is availalble. - - Under Mac OS, uses the native file copy function in macostools; - on other systems, uses '_copy_file_contents()' to copy file - contents. - - Return true if the file was copied (or would have been copied), - false otherwise (ie. 'update' was true and the destination is - up-to-date).""" - - # XXX if the destination file already exists, we clobber it if - # copying, but blow up if linking. Hmmm. And I don't know what - # macostools.copyfile() does. Should definitely be consistent, and - # should probably blow up if destination exists and we would be - # changing it (ie. it's not already a hard/soft link to src OR - # (not update) and (src newer than dst). - - from stat import * - - if not os.path.isfile (src): - raise DistutilsFileError, \ - "can't copy '%s': doesn't exist or not a regular file" % src - - if os.path.isdir (dst): - dir = dst - dst = os.path.join (dst, os.path.basename (src)) - else: - dir = os.path.dirname (dst) - - if update and not newer (src, dst): - if verbose: - print "not copying %s (output up-to-date)" % src - return 0 - - try: - action = _copy_action[link] - except KeyError: - raise ValueError, \ - "invalid value '%s' for 'link' argument" % link - if verbose: - print "%s %s -> %s" % (action, src, dir) - - if dry_run: - return 1 - - # On a Mac, use the native file copy routine - if os.name == 'mac': - import macostools - try: - macostools.copy (src, dst, 0, preserve_times) - except OSError, exc: - raise DistutilsFileError, \ - "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) - - # If linking (hard or symbolic), use the appropriate system call - # (Unix only, of course, but that's the caller's responsibility) - elif link == 'hard': - if not (os.path.exists (dst) and os.path.samefile (src, dst)): - os.link (src, dst) - elif link == 'sym': - if not (os.path.exists (dst) and os.path.samefile (src, dst)): - os.symlink (src, dst) - - # Otherwise (non-Mac, not linking), copy the file contents and - # (optionally) copy the times and mode. - else: - _copy_file_contents (src, dst) - if preserve_mode or preserve_times: - st = os.stat (src) - - # According to David Ascher , utime() should be done - # before chmod() (at least under NT). - if preserve_times: - os.utime (dst, (st[ST_ATIME], st[ST_MTIME])) - if preserve_mode: - os.chmod (dst, S_IMODE (st[ST_MODE])) - - return 1 - -# copy_file () - - -def copy_tree (src, dst, - preserve_mode=1, - preserve_times=1, - preserve_symlinks=0, - update=0, - verbose=0, - dry_run=0): - - """Copy an entire directory tree 'src' to a new location 'dst'. Both - 'src' and 'dst' must be directory names. If 'src' is not a - directory, raise DistutilsFileError. If 'dst' does not exist, it is - created with 'mkpath()'. The end result of the copy is that every - file in 'src' is copied to 'dst', and directories under 'src' are - recursively copied to 'dst'. Return the list of files that were - copied or might have been copied, using their output name. The - return value is unaffected by 'update' or 'dry_run': it is simply - the list of all files under 'src', with the names changed to be - under 'dst'. - - 'preserve_mode' and 'preserve_times' are the same as for - 'copy_file'; note that they only apply to regular files, not to - directories. If 'preserve_symlinks' is true, symlinks will be - copied as symlinks (on platforms that support them!); otherwise - (the default), the destination of the symlink will be copied. - 'update' and 'verbose' are the same as for 'copy_file'.""" - - if not dry_run and not os.path.isdir (src): - raise DistutilsFileError, \ - "cannot copy tree '%s': not a directory" % src - try: - names = os.listdir (src) - except os.error, (errno, errstr): - if dry_run: - names = [] - else: - raise DistutilsFileError, \ - "error listing files in '%s': %s" % (src, errstr) - - if not dry_run: - mkpath (dst, verbose=verbose) - - outputs = [] - - for n in names: - src_name = os.path.join (src, n) - dst_name = os.path.join (dst, n) - - if preserve_symlinks and os.path.islink (src_name): - link_dest = os.readlink (src_name) - if verbose: - print "linking %s -> %s" % (dst_name, link_dest) - if not dry_run: - os.symlink (link_dest, dst_name) - outputs.append (dst_name) - - elif os.path.isdir (src_name): - outputs.extend ( - copy_tree (src_name, dst_name, - preserve_mode, preserve_times, preserve_symlinks, - update, verbose, dry_run)) - else: - copy_file (src_name, dst_name, - preserve_mode, preserve_times, - update, None, verbose, dry_run) - outputs.append (dst_name) - - return outputs - -# copy_tree () - - -def remove_tree (directory, verbose=0, dry_run=0): - """Recursively remove an entire directory tree. Any errors are ignored - (apart from being reported to stdout if 'verbose' is true).""" - - if verbose: - print "removing '%s' (and everything under it)" % directory - if dry_run: - return - try: - shutil.rmtree(directory,1) - except (IOError, OSError), exc: - if verbose: - if exc.filename: - print "error removing %s: %s (%s)" % \ - (directory, exc.strerror, exc.filename) - else: - print "error removing %s: %s" % (directory, exc.strerror) - - -# XXX I suspect this is Unix-specific -- need porting help! -def move_file (src, dst, - verbose=0, - dry_run=0): - - """Move a file 'src' to 'dst'. If 'dst' is a directory, the file - will be moved into it with the same name; otherwise, 'src' is - just renamed to 'dst'. Return the new full name of the file. - - Handles cross-device moves on Unix using - 'copy_file()'. What about other systems???""" - - from os.path import exists, isfile, isdir, basename, dirname - - if verbose: - print "moving %s -> %s" % (src, dst) - - if dry_run: - return dst - - if not isfile (src): - raise DistutilsFileError, \ - "can't move '%s': not a regular file" % src - - if isdir (dst): - dst = os.path.join (dst, basename (src)) - elif exists (dst): - raise DistutilsFileError, \ - "can't move '%s': destination '%s' already exists" % \ - (src, dst) - - if not isdir (dirname (dst)): - raise DistutilsFileError, \ - "can't move '%s': destination '%s' not a valid path" % \ - (src, dst) - - copy_it = 0 - try: - os.rename (src, dst) - except os.error, (num, msg): - if num == errno.EXDEV: - copy_it = 1 - else: - raise DistutilsFileError, \ - "couldn't move '%s' to '%s': %s" % (src, dst, msg) - - if copy_it: - copy_file (src, dst) - try: - os.unlink (src) - except os.error, (num, msg): - try: - os.unlink (dst) - except os.error: - pass - raise DistutilsFileError, \ - ("couldn't move '%s' to '%s' by copy/delete: " + - "delete '%s' failed: %s") % \ - (src, dst, src, msg) - - return dst - -# move_file () - - -def write_file (filename, contents): - """Create a file with the specified name and write 'contents' (a - sequence of strings without line terminators) to it.""" - - f = open (filename, "w") - for line in contents: - f.write (line + "\n") - f.close () +# for backwards compatibility: +from distutils.file_util import * +from distutils.dir_util import * +from distutils.dep_util import * +from distutils.archive_util import * def get_platform (): @@ -620,141 +101,3 @@ def subst_vars (str, local_vars): # subst_vars () -def make_tarball (base_name, base_dir, compress="gzip", - verbose=0, dry_run=0): - """Create a (possibly compressed) tar file from all the files under - 'base_dir'. 'compress' must be "gzip" (the default), "compress", or - None. Both "tar" and the compression utility named by 'compress' - must be on the default program search path, so this is probably - Unix-specific. The output tar file will be named 'base_dir' + - ".tar", possibly plus the appropriate compression extension - (".gz" or ".Z"). Return the output filename.""" - - # XXX GNU tar 1.13 has a nifty option to add a prefix directory. - # It's pretty new, though, so we certainly can't require it -- - # but it would be nice to take advantage of it to skip the - # "create a tree of hardlinks" step! (Would also be nice to - # detect GNU tar to use its 'z' option and save a step.) - - compress_ext = { 'gzip': ".gz", - 'compress': ".Z" } - - if compress is not None and compress not in ('gzip', 'compress'): - raise ValueError, \ - "bad value for 'compress': must be None, 'gzip', or 'compress'" - - archive_name = base_name + ".tar" - cmd = ["tar", "-cf", archive_name, base_dir] - spawn (cmd, verbose=verbose, dry_run=dry_run) - - if compress: - spawn ([compress, archive_name], verbose=verbose, dry_run=dry_run) - return archive_name + compress_ext[compress] - else: - return archive_name - -# make_tarball () - - -def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): - """Create a zip file from all the files under 'base_dir'. The - output zip file will be named 'base_dir' + ".zip". Uses either the - InfoZIP "zip" utility (if installed and found on the default search - path) or the "zipfile" Python module (if available). If neither - tool is available, raises DistutilsExecError. Returns the name - of the output zip file.""" - - # This initially assumed the Unix 'zip' utility -- but - # apparently InfoZIP's zip.exe works the same under Windows, so - # no changes needed! - - zip_filename = base_name + ".zip" - try: - spawn (["zip", "-rq", zip_filename, base_dir], - verbose=verbose, dry_run=dry_run) - except DistutilsExecError: - - # XXX really should distinguish between "couldn't find - # external 'zip' command" and "zip failed" -- shouldn't try - # again in the latter case. (I think fixing this will - # require some cooperation from the spawn module -- perhaps - # a utility function to search the path, so we can fallback - # on zipfile.py without the failed spawn.) - try: - import zipfile - except ImportError: - raise DistutilsExecError, \ - ("unable to create zip file '%s': " + - "could neither find a standalone zip utility nor " + - "import the 'zipfile' module") % zip_filename - - if verbose: - print "creating '%s' and adding '%s' to it" % \ - (zip_filename, base_dir) - - def visit (z, dirname, names): - for name in names: - path = os.path.join (dirname, name) - if os.path.isfile (path): - z.write (path, path) - - if not dry_run: - z = zipfile.ZipFile (zip_filename, "wb", - compression=zipfile.ZIP_DEFLATED) - - os.path.walk (base_dir, visit, z) - z.close() - - return zip_filename - -# make_zipfile () - - -def make_archive (base_name, format, - root_dir=None, base_dir=None, - verbose=0, dry_run=0): - - """Create an archive file (eg. zip or tar). 'base_name' is the name - of the file to create, minus any format-specific extension; 'format' - is the archive format: one of "zip", "tar", "ztar", or "gztar". - 'root_dir' is a directory that will be the root directory of the - archive; ie. we typically chdir into 'root_dir' before creating the - archive. 'base_dir' is the directory where we start archiving from; - ie. 'base_dir' will be the common prefix of all files and - directories in the archive. 'root_dir' and 'base_dir' both default - to the current directory.""" - - save_cwd = os.getcwd() - if root_dir is not None: - if verbose: - print "changing into '%s'" % root_dir - base_name = os.path.abspath (base_name) - if not dry_run: - os.chdir (root_dir) - - if base_dir is None: - base_dir = os.curdir - - kwargs = { 'verbose': verbose, - 'dry_run': dry_run } - - if format == 'gztar': - func = make_tarball - kwargs['compress'] = 'gzip' - elif format == 'ztar': - func = make_tarball - kwargs['compress'] = 'compress' - elif format == 'tar': - func = make_tarball - kwargs['compress'] = None - elif format == 'zip': - func = make_zipfile - - apply (func, (base_name, base_dir), kwargs) - - if root_dir is not None: - if verbose: - print "changing back to '%s'" % save_cwd - os.chdir (save_cwd) - -# make_archive () -- cgit v1.2.1 From 668f7321264af838cecd5c2b2fb57a01ece71857 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 6 Apr 2000 02:07:41 +0000 Subject: Add missing import of 'usage' string. --- dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist.py b/dist.py index 50e75569..7246b740 100644 --- a/dist.py +++ b/dist.py @@ -183,9 +183,9 @@ class Distribution: commands (currently, this only happens if user asks for help).""" - # late import because of mutual dependence between these classes + # late import because of mutual dependence between these modules from distutils.cmd import Command - + from distutils.core import usage # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- -- cgit v1.2.1 From 1941b15adc3750d84b149fb84af75e36a633ad61 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Apr 2000 02:31:45 +0000 Subject: This little note is to clarify things for people who go poking around the Python library hoping to find out more about the Distutils. --- README | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 README diff --git a/README b/README new file mode 100644 index 00000000..f1db3c6c --- /dev/null +++ b/README @@ -0,0 +1,18 @@ +This directory contains only a subset of the Distutils, specifically the +Python modules in the 'distutils' and 'distutils.command' packages. +Technically, this is all you need to distribute and install Python modules +using the Distutils. Most people will want some documentation and other +help, though. Currently, everything can be found at the Distutils web page: + + http://www.python.org/sigs/distutils-sig/ + +From there you can access the latest documentation, or download a standalone +Distutils release that includes all the code in this directory, plus +documentation, test scripts, examples, etc. + +The Distutils documentation isn't yet part of the standard Python +documentation set, but will be soon. + + Greg Ward (gward@python.net) + +$Id$ -- cgit v1.2.1 From 79cc38ef59bfe78ac6a3eae934cebb9ef10798ba Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Apr 2000 03:48:37 +0000 Subject: Added (currently) pointless and trivial main body (for future tests). --- cmd.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd.py b/cmd.py index ad787037..b2fb517e 100644 --- a/cmd.py +++ b/cmd.py @@ -388,3 +388,7 @@ class Command: # make_file () # class Command + + +if __name__ == "__main__": + print "ok" -- cgit v1.2.1 From 1633033ac04ecddfdb450f8e60efa54c7eb894d5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Apr 2000 03:49:20 +0000 Subject: Catch DistutilsFileError in addition to DistutilsExecError in 'setup()'. --- core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.py b/core.py index 3df54a5e..9a801063 100644 --- a/core.py +++ b/core.py @@ -96,7 +96,7 @@ def setup (**attrs): "error: %s: %s" % (exc.filename, exc.strerror) else: raise SystemExit, str (exc) - except DistutilsExecError, msg: + except (DistutilsExecError, DistutilsFileError), msg: raise SystemExit, "error: " + str (msg) # setup () -- cgit v1.2.1 From 4973bedd82d22fe0b181d0a045565eac3e93a37f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Apr 2000 03:51:40 +0000 Subject: Ditched the unused 'list_only' option. Added code to include source files from 'build_clib' command to default file list -- currently this won't work, since 'build_clib' doesn't have a 'get_source_files()' method! --- command/sdist.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 7dbd4013..349a824a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -36,8 +36,6 @@ class sdist (Command): ('formats=', None, "formats for source distribution (tar, ztar, gztar, or zip)"), - ('list-only', 'l', - "just list files that would be distributed"), ('keep-tree', 'k', "keep the distribution tree around after creating " + "archive file(s)"), @@ -64,7 +62,6 @@ class sdist (Command): self.force_manifest = 0 self.formats = None - self.list_only = 0 self.keep_tree = 0 @@ -228,6 +225,10 @@ class sdist (Command): build_ext = self.find_peer ('build_ext') self.files.extend (build_ext.get_source_files ()) + if self.distribution.has_c_libraries(): + build_clib = self.find_peer ('build_clib') + self.files.extend (build_clib.get_source_files ()) + def search_dir (self, dir, pattern=None): """Recursively find files under 'dir' matching 'pattern' (a string -- cgit v1.2.1 From 4bd72ccc78221cffe25ef0048895e24c4a63ba6b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 00:02:16 +0000 Subject: Added __version__ to store the Distutils version number. --- __init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/__init__.py b/__init__.py index 1f23b972..93055549 100644 --- a/__init__.py +++ b/__init__.py @@ -9,3 +9,5 @@ used from a setup script as """ __revision__ = "$Id$" + +__version__ = "0.8" -- cgit v1.2.1 From 515669ce62c1021d1cecfadc1c006bb074beb202 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 00:18:16 +0000 Subject: Removed global '--force' option -- just too vague a concept to be applicable to all commands in the same way. Several Command methods now either expect 'self.force' to be defined, or check if it is defined and assume it's false if not. --- cmd.py | 24 ++++++++++++++---------- dist.py | 3 --- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/cmd.py b/cmd.py index b2fb517e..76a76919 100644 --- a/cmd.py +++ b/cmd.py @@ -58,7 +58,6 @@ class Command: # (etc.) will be handled by __getattr__, below. self._verbose = None self._dry_run = None - self._force = None # The 'help' flag is just used for command-line parsing, so # none of that complicated bureaucracy is needed. @@ -74,7 +73,7 @@ class Command: def __getattr__ (self, attr): - if attr in ('verbose', 'dry_run', 'force'): + if attr in ('verbose', 'dry_run'): myval = getattr (self, "_" + attr) if myval is None: return getattr (self.distribution, attr) @@ -308,7 +307,8 @@ class Command: def copy_file (self, infile, outfile, preserve_mode=1, preserve_times=1, link=None, level=1): - """Copy a file respecting verbose, dry-run and force flags.""" + """Copy a file respecting verbose, dry-run and force flags (this + should only be used by commands that define 'self.force'!).""" return util.copy_file (infile, outfile, preserve_mode, preserve_times, @@ -322,7 +322,8 @@ class Command: preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1): """Copy an entire directory tree respecting verbose, dry-run, - and force flags.""" + and force flags (again, should only be used by commands + that define 'self.force').""" return util.copy_tree (infile, outfile, preserve_mode,preserve_times,preserve_symlinks, @@ -352,13 +353,15 @@ class Command: def make_file (self, infiles, outfile, func, args, - exec_msg=None, skip_msg=None, level=1): + exec_msg=None, skip_msg=None, level=1): """Special case of 'execute()' for operations that process one or - more input files and generate one output file. Works just like - 'execute()', except the operation is skipped and a different - message printed if 'outfile' already exists and is newer than - all files listed in 'infiles'.""" + more input files and generate one output file. Works just like + 'execute()', except the operation is skipped and a different + message printed if 'outfile' already exists and is newer than all + files listed in 'infiles'. If the command defined 'self.force', + and it is true, then the command is unconditionally run -- does no + timestamp checks.""" if exec_msg is None: @@ -378,7 +381,8 @@ class Command: # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if self.force or util.newer_group (infiles, outfile): + if ((hasattr(self,'force') and self.force) or + util.newer_group (infiles, outfile)): self.execute (func, args, exec_msg, level) # Otherwise, print the "skip" message diff --git a/dist.py b/dist.py index 7246b740..cb180314 100644 --- a/dist.py +++ b/dist.py @@ -52,8 +52,6 @@ class Distribution: "run quietly (turns verbosity off)"), ('dry-run', 'n', "don't actually do anything"), - ('force', 'f', - "skip dependency checking between files"), ('help', 'h', "show this help message"), ] @@ -76,7 +74,6 @@ class Distribution: # Default values for our command-line options self.verbose = 1 self.dry_run = 0 - self.force = 0 self.help = 0 self.help_commands = 0 -- cgit v1.2.1 From 5f99c51af634e6d53353955add8bda432fa2d9ad Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 00:19:42 +0000 Subject: Added '--force' option -- very clear what it means for building (ignore timestamps), so every build_* command has 'self.force', which follows the 'build' command if not set by the user. --- command/build.py | 5 +++++ command/build_clib.py | 6 +++++- command/build_ext.py | 6 +++++- command/build_py.py | 7 ++++++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/command/build.py b/command/build.py index 2da589ab..bca0ece8 100644 --- a/command/build.py +++ b/command/build.py @@ -28,6 +28,8 @@ class build (Command): "temporary build directory"), ('debug', 'g', "compile extensions and libraries with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps"), ] def initialize_options (self): @@ -39,9 +41,12 @@ class build (Command): self.build_lib = None self.build_temp = None self.debug = None + self.force = 0 def finalize_options (self): + print "build.finalize: force=%s" % self.force + # Need this to name platform-specific directories, but sys.platform # is not enough -- it only names the OS and version, not the # hardware architecture! diff --git a/command/build_clib.py b/command/build_clib.py index f48e6476..23111874 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -37,6 +37,8 @@ class build_clib (Command): "directory to put temporary build by-products"), ('debug', 'g', "compile with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps"), ] def initialize_options (self): @@ -51,6 +53,7 @@ class build_clib (Command): self.define = None self.undef = None self.debug = None + self.force = 0 # initialize_options() @@ -65,7 +68,8 @@ class build_clib (Command): self.set_undefined_options ('build', ('build_temp', 'build_clib'), ('build_temp', 'build_temp'), - ('debug', 'debug')) + ('debug', 'debug'), + ('force', 'force')) self.libraries = self.distribution.libraries if self.libraries: diff --git a/command/build_ext.py b/command/build_ext.py index 2cb1c610..ddb01d4e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -66,6 +66,8 @@ class build_ext (Command): "extra explicit link objects to include in the link"), ('debug', 'g', "compile/link with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps"), ] @@ -84,6 +86,7 @@ class build_ext (Command): self.rpath = None self.link_objects = None self.debug = None + self.force = None def finalize_options (self): @@ -92,7 +95,8 @@ class build_ext (Command): self.set_undefined_options ('build', ('build_lib', 'build_lib'), ('build_temp', 'build_temp'), - ('debug', 'debug')) + ('debug', 'debug'), + ('force', 'force')) if self.package is None: self.package = self.distribution.ext_package diff --git a/command/build_py.py b/command/build_py.py index 3baddc62..d719cec8 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -20,6 +20,7 @@ class build_py (Command): user_options = [ ('build-lib=', 'd', "directory to \"build\" (copy) to"), + ('force', 'f', "forcibly build everything (ignore file timestamps"), ] @@ -28,10 +29,14 @@ class build_py (Command): self.modules = None self.package = None self.package_dir = None + self.force = None def finalize_options (self): + print "build_py.finalize: force=%s" % self.force self.set_undefined_options ('build', - ('build_lib', 'build_lib')) + ('build_lib', 'build_lib'), + ('force', 'force')) + print "build_py.finalize: force=%s" % self.force # Get the distribution options that are aliases for build_py # options -- list of packages and list of modules. -- cgit v1.2.1 From b3602e53092a10f2c8fd167c7621150b6c71249a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 01:15:06 +0000 Subject: Better variable names here and there. --- sysconfig.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 2c7318c0..9cf9540d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -16,8 +16,8 @@ import sys from errors import DistutilsPlatformError -prefix = os.path.normpath(sys.prefix) -exec_prefix = os.path.normpath(sys.exec_prefix) +PREFIX = os.path.normpath(sys.prefix) +EXEC_PREFIX = os.path.normpath(sys.exec_prefix) def get_python_inc(plat_specific=0): @@ -29,13 +29,13 @@ def get_python_inc(plat_specific=0): (namely config.h). """ - the_prefix = (plat_specific and exec_prefix or prefix) + prefix = (plat_specific and EXEC_PREFIX or PREFIX) if os.name == "posix": - return os.path.join(the_prefix, "include", "python" + sys.version[:3]) + return os.path.join(prefix, "include", "python" + sys.version[:3]) elif os.name == "nt": - return os.path.join(the_prefix, "Include") # include or Include? + return os.path.join(prefix, "Include") # include or Include? elif os.name == "mac": - return os.path.join(the_prefix, "Include") + return os.path.join(prefix, "Include") else: raise DistutilsPlatformError, \ ("I don't know where Python installs its C header files " + @@ -54,10 +54,10 @@ def get_python_lib(plat_specific=0, standard_lib=0): directory for site-specific modules. """ - the_prefix = (plat_specific and exec_prefix or prefix) + prefix = (plat_specific and EXEC_PREFIX or PREFIX) if os.name == "posix": - libpython = os.path.join(the_prefix, + libpython = os.path.join(prefix, "lib", "python" + sys.version[:3]) if standard_lib: return libpython @@ -66,20 +66,20 @@ def get_python_lib(plat_specific=0, standard_lib=0): elif os.name == "nt": if standard_lib: - return os.path.join(the_prefix, "Lib") + return os.path.join(PREFIX, "Lib") else: - return the_prefix + return prefix elif os.name == "mac": if platform_specific: if standard_lib: - return os.path.join(exec_prefix, "Mac", "Plugins") + return os.path.join(EXEC_PREFIX, "Mac", "Plugins") else: raise DistutilsPlatformError, \ "OK, where DO site-specific extensions go on the Mac?" else: if standard_lib: - return os.path.join(prefix, "Lib") + return os.path.join(PREFIX, "Lib") else: raise DistutilsPlatformError, \ "OK, where DO site-specific modules go on the Mac?" @@ -226,7 +226,7 @@ def _init_nt(): g['INCLUDEPY'] = get_python_inc(plat_specific=0) g['SO'] = '.pyd' - g['exec_prefix'] = exec_prefix + g['exec_prefix'] = EXEC_PREFIX def _init_mac(): @@ -235,7 +235,7 @@ def _init_mac(): # load the installed config.h (what if not installed? - still need to # be able to install packages which don't require compilation) parse_config_h(open( - os.path.join(sys.exec_prefix, "Mac", "Include", "config.h")), g) + os.path.join(EXEC_PREFIX, "Mac", "Include", "config.h")), g) # set basic install directories g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) @@ -244,12 +244,12 @@ def _init_mac(): g['INCLUDEPY'] = get_python_inc(plat_specific=0) g['SO'] = '.ppc.slb' - g['exec_prefix'] = sys.exec_prefix - print sys.prefix + g['exec_prefix'] = EXEC_PREFIX + print sys.prefix, PREFIX # XXX are these used anywhere? - g['install_lib'] = os.path.join(sys.exec_prefix, "Lib") - g['install_platlib'] = os.path.join(sys.exec_prefix, "Mac", "Lib") + g['install_lib'] = os.path.join(EXEC_PREFIX, "Lib") + g['install_platlib'] = os.path.join(EXEC_PREFIX, "Mac", "Lib") try: -- cgit v1.2.1 From 82199403a5964745b104e69851f05de78387c2e5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 01:17:49 +0000 Subject: Added optional 'prefix' arguments to 'get_python_inc()' and 'get_python_lib()'. --- sysconfig.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 9cf9540d..3f345fbd 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -20,7 +20,7 @@ PREFIX = os.path.normpath(sys.prefix) EXEC_PREFIX = os.path.normpath(sys.exec_prefix) -def get_python_inc(plat_specific=0): +def get_python_inc(plat_specific=0, prefix=None): """Return the directory containing installed Python header files. If 'plat_specific' is false (the default), this is the path to the @@ -28,8 +28,11 @@ def get_python_inc(plat_specific=0): otherwise, this is the path to platform-specific header files (namely config.h). + If 'prefix' is supplied, use it instead of sys.prefix or + sys.exec_prefix -- i.e., ignore 'plat_specific'. """ - prefix = (plat_specific and EXEC_PREFIX or PREFIX) + if prefix is None: + prefix = (plat_specific and EXEC_PREFIX or PREFIX) if os.name == "posix": return os.path.join(prefix, "include", "python" + sys.version[:3]) elif os.name == "nt": @@ -42,7 +45,7 @@ def get_python_inc(plat_specific=0): "on platform '%s'") % os.name -def get_python_lib(plat_specific=0, standard_lib=0): +def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): """Return the directory containing the Python library (standard or site additions). @@ -53,8 +56,11 @@ def get_python_lib(plat_specific=0, standard_lib=0): containing standard Python library modules; otherwise, return the directory for site-specific modules. + If 'prefix' is supplied, use it instead of sys.prefix or + sys.exec_prefix -- i.e., ignore 'plat_specific'. """ - prefix = (plat_specific and EXEC_PREFIX or PREFIX) + if prefix is None: + prefix = (plat_specific and EXEC_PREFIX or PREFIX) if os.name == "posix": libpython = os.path.join(prefix, -- cgit v1.2.1 From 888eb4fbd70664a685df5f61ea3e4acb4fb738ca Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 01:30:44 +0000 Subject: Added a check for the 'force' attribute in '__getattr__()' -- better than crashing when self.force not defined. Revise 'copy_file()' and 'copy_tree()' docstrings accordingly. Remove 'hasattr()' check for 'self.force' from 'make_file()'. --- cmd.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd.py b/cmd.py index 76a76919..16008c4f 100644 --- a/cmd.py +++ b/cmd.py @@ -79,6 +79,11 @@ class Command: return getattr (self.distribution, attr) else: return myval + + # Needed because some Command methods assume 'self.force' exists, + # but not all commands define 'self.force'. Ugh. + elif attr == 'force': + return None else: raise AttributeError, attr @@ -307,8 +312,9 @@ class Command: def copy_file (self, infile, outfile, preserve_mode=1, preserve_times=1, link=None, level=1): - """Copy a file respecting verbose, dry-run and force flags (this - should only be used by commands that define 'self.force'!).""" + """Copy a file respecting verbose, dry-run and force flags. (The + former two default to whatever is in the Distribution object, and + the latter defaults to false for commands that don't define it.)""" return util.copy_file (infile, outfile, preserve_mode, preserve_times, @@ -322,8 +328,7 @@ class Command: preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1): """Copy an entire directory tree respecting verbose, dry-run, - and force flags (again, should only be used by commands - that define 'self.force').""" + and force flags.""" return util.copy_tree (infile, outfile, preserve_mode,preserve_times,preserve_symlinks, @@ -381,8 +386,7 @@ class Command: # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if ((hasattr(self,'force') and self.force) or - util.newer_group (infiles, outfile)): + if self.force or util.newer_group (infiles, outfile): self.execute (func, args, exec_msg, level) # Otherwise, print the "skip" message -- cgit v1.2.1 From fa60eae5bb33a7b14d744cf26b0c0565e727db8a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 01:31:58 +0000 Subject: Delete some debugging print statements. --- command/build.py | 2 -- command/build_py.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/command/build.py b/command/build.py index bca0ece8..7fb0b566 100644 --- a/command/build.py +++ b/command/build.py @@ -45,8 +45,6 @@ class build (Command): def finalize_options (self): - print "build.finalize: force=%s" % self.force - # Need this to name platform-specific directories, but sys.platform # is not enough -- it only names the OS and version, not the # hardware architecture! diff --git a/command/build_py.py b/command/build_py.py index d719cec8..2a1fdd62 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -32,11 +32,9 @@ class build_py (Command): self.force = None def finalize_options (self): - print "build_py.finalize: force=%s" % self.force self.set_undefined_options ('build', ('build_lib', 'build_lib'), ('force', 'force')) - print "build_py.finalize: force=%s" % self.force # Get the distribution options that are aliases for build_py # options -- list of packages and list of modules. -- cgit v1.2.1 From a37b56ceefcc8d1b790a3c4061251b11daf1d26f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 13:11:51 +0000 Subject: Define 'self.force' in the constructor and remove the hack in '__getattr__()' to account for it not being defined in the constructor. --- cmd.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd.py b/cmd.py index 16008c4f..b3d16648 100644 --- a/cmd.py +++ b/cmd.py @@ -59,6 +59,12 @@ class Command: self._verbose = None self._dry_run = None + # Some commands define a 'self.force' option to ignore file + # timestamps, but methods defined *here* assume that + # 'self.force' exists for all commands. So define it here + # just to be safe. + self.force = None + # The 'help' flag is just used for command-line parsing, so # none of that complicated bureaucracy is needed. self.help = 0 @@ -79,11 +85,6 @@ class Command: return getattr (self.distribution, attr) else: return myval - - # Needed because some Command methods assume 'self.force' exists, - # but not all commands define 'self.force'. Ugh. - elif attr == 'force': - return None else: raise AttributeError, attr -- cgit v1.2.1 From b40196ff52fb4a503022ee6e91b53ad5ec9c7317 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 14 Apr 2000 00:39:31 +0000 Subject: Don't bother reading config.h on NT or Mac OS. (It's not really needed on Unix either, so should probably disappear entirely.) --- sysconfig.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 3f345fbd..f7c2e783 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -222,8 +222,6 @@ def _init_posix(): def _init_nt(): """Initialize the module as appropriate for NT""" g = globals() - # load config.h, though I don't know how useful this is - parse_config_h(open(get_config_h_filename()), g) # set basic install directories g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) @@ -238,10 +236,6 @@ def _init_nt(): def _init_mac(): """Initialize the module as appropriate for Macintosh systems""" g = globals() - # load the installed config.h (what if not installed? - still need to - # be able to install packages which don't require compilation) - parse_config_h(open( - os.path.join(EXEC_PREFIX, "Mac", "Include", "config.h")), g) # set basic install directories g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) -- cgit v1.2.1 From 0a8038198900ef7afaafc5e7c4a5b0fa584cf823 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 14 Apr 2000 00:48:15 +0000 Subject: Cleaned up use of sysconfig module a bit: don't import more names than we actually use, and do actually use AR and SO. Run ranlib on static libraries. (Should probably have a platform-check so we don't run ranlib when it's not necessary, ie. on most modern Unices.) --- unixccompiler.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index ec766f54..0944461b 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -20,8 +20,7 @@ __revision__ = "$Id$" import string, re, os from types import * from copy import copy -from distutils.sysconfig import \ - CC, CCSHARED, CFLAGS, OPT, LDSHARED, LDFLAGS, RANLIB, AR, SO +from distutils import sysconfig from distutils.ccompiler import CCompiler, gen_preprocess_options, gen_lib_options # XXX Things not currently handled: @@ -59,14 +58,15 @@ class UnixCCompiler (CCompiler): src_extensions = [".c",".C",".cc",".cxx",".cpp"] obj_extension = ".o" static_lib_extension = ".a" - shared_lib_extension = ".so" + shared_lib_extension = sysconfig.SO static_lib_format = shared_lib_format = "lib%s%s" # Command to create a static library: seems to be pretty consistent # across the major Unices. Might have to move down into the # constructor if we need platform-specific guesswork. - archiver = "ar" + archiver = sysconfig.AR archiver_options = "-cr" + ranlib = sysconfig.RANLIB def __init__ (self, @@ -90,11 +90,11 @@ class UnixCCompiler (CCompiler): # UnixCCompiler! (self.cc, self.ccflags) = \ - _split_command (CC + ' ' + OPT) - self.ccflags_shared = string.split (CCSHARED) + _split_command (sysconfig.CC + ' ' + sysconfig.OPT) + self.ccflags_shared = string.split (sysconfig.CCSHARED) (self.ld_shared, self.ldflags_shared) = \ - _split_command (LDSHARED) + _split_command (sysconfig.LDSHARED) self.ld_exec = self.cc @@ -157,6 +157,12 @@ class UnixCCompiler (CCompiler): self.archiver_options, output_filename] + objects + self.objects) + + # Not many Unices required ranlib anymore -- SunOS 4.x is, + # I think the only major Unix that does. Probably should + # have some platform intelligence here to skip ranlib if + # it's not needed. + self.spawn ([self.ranlib, output_filename]) else: self.announce ("skipping %s (up-to-date)" % output_filename) -- cgit v1.2.1 From a213af0d45d592238ea3d6e56beca78271100d20 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 14 Apr 2000 00:49:30 +0000 Subject: Coerce all paths in the manifest template to the local path syntax with 'native_path()'. --- command/sdist.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 349a824a..f3cc041e 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -12,7 +12,7 @@ from types import * from glob import glob from distutils.core import Command from distutils.util import \ - newer, remove_tree, make_tarball, make_zipfile, create_tree + newer, create_tree, remove_tree, make_tarball, make_zipfile, native_path from distutils.text_file import TextFile from distutils.errors import DistutilsExecError @@ -317,7 +317,7 @@ class sdist (Command): action) continue - pattern = words[1] + pattern = native_path (words[1]) elif action in ('recursive-include','recursive-exclude'): if len (words) != 3: @@ -327,7 +327,7 @@ class sdist (Command): action) continue - (dir, pattern) = words[1:3] + (dir, pattern) = map (native_path, words[1:3]) elif action in ('graft','prune'): if len (words) != 2: @@ -337,7 +337,7 @@ class sdist (Command): action) continue - dir_pattern = words[1] + dir_pattern = native_path (words[1]) else: template.warn ("invalid manifest template line: " + @@ -347,9 +347,9 @@ class sdist (Command): # OK, now we know that the action is valid and we have the # right number of words on the line for that action -- so we # can proceed with minimal error-checking. Also, we have - # defined either 'patter', 'dir' and 'pattern', or - # 'dir_pattern' -- so we don't have to spend any time digging - # stuff up out of 'words'. + # defined either (pattern), (dir and pattern), or + # (dir_pattern) -- so we don't have to spend any time + # digging stuff up out of 'words'. if action == 'include': print "include", pattern -- cgit v1.2.1 From 860fb8e70fb18c06ef9e82438fa2b5af33f926c0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 14 Apr 2000 00:50:49 +0000 Subject: Use 'get_python_inc()' to figure out the Python include directories rather than cobbling them togethere here. --- command/build_ext.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index ddb01d4e..f08c97e9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -105,12 +105,9 @@ class build_ext (Command): # Make sure Python's include directories (for Python.h, config.h, - # etc.) are in the include search path. We have to roll our own - # "exec include dir", because the Makefile parsed by sysconfig - # doesn't have it (sigh). - py_include = sysconfig.INCLUDEPY # prefix + "include" + "python" + ver - exec_py_include = os.path.join (sysconfig.exec_prefix, 'include', - 'python' + sys.version[0:3]) + # etc.) are in the include search path. + py_include = sysconfig.get_python_inc() + plat_py_include = sysconfig.get_python_inc(plat_specific=1) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if type (self.include_dirs) is StringType: @@ -120,8 +117,8 @@ class build_ext (Command): # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. self.include_dirs.append (py_include) - if exec_py_include != py_include: - self.include_dirs.insert (0, exec_py_include) + if plat_py_include != py_include: + self.include_dirs.append (plat_py_include) if type (self.libraries) is StringType: self.libraries = [self.libraries] -- cgit v1.2.1 From cde4940c51df0bc44586a948ba99d32218cbc75d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 14 Apr 2000 13:53:34 +0000 Subject: Don't run "ranlib" if sysconfig's RANLIB (from Python's Makefile) starts with ":". --- unixccompiler.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 0944461b..5e1524c6 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -158,11 +158,13 @@ class UnixCCompiler (CCompiler): output_filename] + objects + self.objects) - # Not many Unices required ranlib anymore -- SunOS 4.x is, - # I think the only major Unix that does. Probably should - # have some platform intelligence here to skip ranlib if - # it's not needed. - self.spawn ([self.ranlib, output_filename]) + # Not many Unices required ranlib anymore -- SunOS 4.x is, I + # think the only major Unix that does. Maybe we need some + # platform intelligence here to skip ranlib if it's not + # needed -- or maybe Python's configure script took care of + # it for us, hence the check for leading colon. + if self.ranlib[0] != ':': + self.spawn ([self.ranlib, output_filename]) else: self.announce ("skipping %s (up-to-date)" % output_filename) -- cgit v1.2.1 From 046fbde66e644516ce1dbe2b4decbc2348b0a137 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 15 Apr 2000 22:15:07 +0000 Subject: Cleaned up/simplified error-handling: - DistutilsOptionError is now documented as it's actually used, ie. to indicate bogus option values (usually user options, eg. from the command-line) - added DistutilsSetupError to indicate errors that definitely arise in the setup script - got rid of DistutilsValueError, and changed all usage of it to either DistutilsSetupError or ValueError as appropriate - simplified a bunch of option get/set methods in Command and Distribution classes -- just pass on AttributeError most of the time, rather than turning it into something else --- cmd.py | 42 ++++++++++++++---------------------------- command/build_clib.py | 14 +++++++------- command/build_ext.py | 12 ++++++------ dist.py | 36 +++++++++--------------------------- errors.py | 16 +++++++++------- msvccompiler.py | 3 +++ util.py | 8 ++++---- 7 files changed, 52 insertions(+), 79 deletions(-) diff --git a/cmd.py b/cmd.py index b3d16648..d8e41379 100644 --- a/cmd.py +++ b/cmd.py @@ -161,38 +161,28 @@ class Command: def get_option (self, option): """Return the value of a single option for this command. Raise - DistutilsOptionError if 'option' is not known.""" - try: - return getattr (self, option) - except AttributeError: - raise DistutilsOptionError, \ - "command %s: no such option %s" % \ - (self.get_command_name(), option) + AttributeError if 'option' is not known.""" + return getattr (self, option) def get_options (self, *options): """Return (as a tuple) the values of several options for this - command. Raise DistutilsOptionError if any of the options in + command. Raise AttributeError if any of the options in 'options' are not known.""" values = [] - try: - for opt in options: - values.append (getattr (self, opt)) - except AttributeError, name: - raise DistutilsOptionError, \ - "command %s: no such option %s" % \ - (self.get_command_name(), name) - + for opt in options: + values.append (getattr (self, opt)) + return tuple (values) - + def set_option (self, option, value): """Set the value of a single option for this command. Raise - DistutilsOptionError if 'option' is not known.""" + AttributeError if 'option' is not known.""" if not hasattr (self, option): - raise DistutilsOptionError, \ + raise AttributeError, \ "command '%s': no such option '%s'" % \ (self.get_command_name(), option) if value is not None: @@ -200,7 +190,7 @@ class Command: def set_options (self, **optval): """Set the values of several options for this command. Raise - DistutilsOptionError if any of the options specified as + AttributeError if any of the options specified as keyword arguments are not known.""" for k in optval.keys(): @@ -236,14 +226,10 @@ class Command: src_cmd_obj = self.distribution.find_command_obj (src_cmd) src_cmd_obj.ensure_ready () - try: - for (src_option, dst_option) in option_pairs: - if getattr (self, dst_option) is None: - self.set_option (dst_option, - src_cmd_obj.get_option (src_option)) - except AttributeError, name: - # duh, which command? - raise DistutilsOptionError, "unknown option %s" % name + for (src_option, dst_option) in option_pairs: + if getattr (self, dst_option) is None: + self.set_option (dst_option, + src_cmd_obj.get_option (src_option)) def find_peer (self, command, create=1): diff --git a/command/build_clib.py b/command/build_clib.py index 23111874..31170730 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -115,33 +115,33 @@ class build_clib (Command): """Ensure that the list of libraries (presumably provided as a command option 'libraries') is valid, i.e. it is a list of 2-tuples, where the tuples are (library_name, build_info_dict). - Raise DistutilsValueError if the structure is invalid anywhere; + Raise DistutilsSetupError if the structure is invalid anywhere; just returns otherwise.""" # Yechh, blecch, ackk: this is ripped straight out of build_ext.py, # with only names changed to protect the innocent! if type (libraries) is not ListType: - raise DistutilsValueError, \ + raise DistutilsSetupError, \ "'libraries' option must be a list of tuples" for lib in libraries: if type (lib) is not TupleType and len (lib) != 2: - raise DistutilsValueError, \ + raise DistutilsSetupError, \ "each element of 'libraries' must a 2-tuple" if type (lib[0]) is not StringType: - raise DistutilsValueError, \ + raise DistutilsSetupError, \ "first element of each tuple in 'libraries' " + \ "must be a string (the library name)" if '/' in lib[0] or (os.sep != '/' and os.sep in lib[0]): - raise DistutilsValueError, \ + raise DistutilsSetupError, \ ("bad library name '%s': " + "may not contain directory separators") % \ lib[0] if type (lib[1]) is not DictionaryType: - raise DistutilsValueError, \ + raise DistutilsSetupError, \ "second element of each tuple in 'libraries' " + \ "must be a dictionary (build info)" # for lib @@ -171,7 +171,7 @@ class build_clib (Command): for (lib_name, build_info) in libraries: sources = build_info.get ('sources') if sources is None or type (sources) not in (ListType, TupleType): - raise DistutilsValueError, \ + raise DistutilsSetupError, \ ("in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames") % lib_name diff --git a/command/build_ext.py b/command/build_ext.py index f08c97e9..422b8cae 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -205,26 +205,26 @@ class build_ext (Command): """Ensure that the list of extensions (presumably provided as a command option 'extensions') is valid, i.e. it is a list of 2-tuples, where the tuples are (extension_name, build_info_dict). - Raise DistutilsValueError if the structure is invalid anywhere; + Raise DistutilsSetupError if the structure is invalid anywhere; just returns otherwise.""" if type (extensions) is not ListType: - raise DistutilsValueError, \ + raise DistutilsSetupError, \ "'ext_modules' option must be a list of tuples" for ext in extensions: if type (ext) is not TupleType and len (ext) != 2: - raise DistutilsValueError, \ + raise DistutilsSetupError, \ "each element of 'ext_modules' option must be a 2-tuple" if not (type (ext[0]) is StringType and extension_name_re.match (ext[0])): - raise DistutilsValueError, \ + raise DistutilsSetupError, \ "first element of each tuple in 'ext_modules' " + \ "must be the extension name (a string)" if type (ext[1]) is not DictionaryType: - raise DistutilsValueError, \ + raise DistutilsSetupError, \ "second element of each tuple in 'ext_modules' " + \ "must be a dictionary (build info)" @@ -274,7 +274,7 @@ class build_ext (Command): for (extension_name, build_info) in self.extensions: sources = build_info.get ('sources') if sources is None or type (sources) not in (ListType, TupleType): - raise DistutilsValueError, \ + raise DistutilsSetupError, \ ("in 'ext_modules' option (extension '%s'), " + "'sources' must be present and must be " + "a list of source filenames") % extension_name diff --git a/dist.py b/dist.py index cb180314..408b9f5c 100644 --- a/dist.py +++ b/dist.py @@ -152,7 +152,7 @@ class Distribution: if hasattr (self, key): setattr (self, key, val) else: - raise DistutilsOptionError, \ + raise DistutilsSetupError, \ "invalid distribution option '%s'" % key # __init__ () @@ -447,27 +447,18 @@ class Distribution: def get_option (self, option): """Return the value of a distribution option. Raise - DistutilsOptionError if 'option' is not known.""" - - try: - return getattr (self, opt) - except AttributeError: - raise DistutilsOptionError, \ - "unknown distribution option %s" % option + AttributeError if 'option' is not known.""" + return getattr (self, opt) def get_options (self, *options): """Return (as a tuple) the values of several distribution - options. Raise DistutilsOptionError if any element of + options. Raise AttributeError if any element of 'options' is not known.""" values = [] - try: - for opt in options: - values.append (getattr (self, opt)) - except AttributeError, name: - raise DistutilsOptionError, \ - "unknown distribution option %s" % name + for opt in options: + values.append (getattr (self, opt)) return tuple (values) @@ -498,17 +489,12 @@ class Distribution: def get_command_option (self, command, option): """Create a command object for 'command' if necessary, ensure that its option values are all set to their final values, and return - the value of its 'option' option. Raise DistutilsOptionError if + the value of its 'option' option. Raise AttributeError if 'option' is not known for that 'command'.""" cmd_obj = self.find_command_obj (command) cmd_obj.ensure_ready () return cmd_obj.get_option (option) - try: - return getattr (cmd_obj, option) - except AttributeError: - raise DistutilsOptionError, \ - "command %s: no such option %s" % (command, option) def get_command_options (self, command, *options): @@ -521,12 +507,8 @@ class Distribution: cmd_obj = self.find_command_obj (command) cmd_obj.ensure_ready () values = [] - try: - for opt in options: - values.append (getattr (cmd_obj, option)) - except AttributeError, name: - raise DistutilsOptionError, \ - "command %s: no such option %s" % (command, name) + for opt in options: + values.append (getattr (cmd_obj, option)) return tuple (values) diff --git a/errors.py b/errors.py index f9d5c8de..61cdb72c 100644 --- a/errors.py +++ b/errors.py @@ -46,15 +46,18 @@ if type (RuntimeError) is types.ClassType: class DistutilsFileError (DistutilsError): pass - # DistutilsOptionError is raised anytime an attempt is made to access - # (get or set) an option that does not exist for a particular command - # (or for the distribution itself). + # DistutilsOptionError is raised for syntactic/semantic errors in + # command options, such as use of mutually conflicting options, or + # inconsistent options, badly-spelled values, etc. No distinction is + # made between option values originating in the setup script, the + # command line, config files, or what-have-you. class DistutilsOptionError (DistutilsError): pass - # DistutilsValueError is raised anytime an option value (presumably - # provided by setup.py) is invalid. - class DistutilsValueError (DistutilsError): + # DistutilsSetupError is raised for errors that can be definitely + # blamed on the setup script, such as invalid keyword arguments to + # 'setup()'. + class DistutilsSetupError (DistutilsError): pass # DistutilsPlatformError is raised when we find that we don't @@ -82,7 +85,6 @@ else: DistutilsArgError = 'DistutilsArgError' DistutilsFileError = 'DistutilsFileError' DistutilsOptionError = 'DistutilsOptionError' - DistutilsValueError = 'DistutilsValueError' DistutilsPlatformError = 'DistutilsPlatformError' DistutilsExecError = 'DistutilsExecError' DistutilsInternalError = 'DistutilsInternalError' diff --git a/msvccompiler.py b/msvccompiler.py index 43a85960..c7a69c35 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -360,6 +360,9 @@ class MSVCCompiler (CCompiler) : if extra_postargs: ld_args.extend (extra_postargs) + print "link_shared_object():" + print " output_filename =", output_filename + print " mkpath'ing:", os.path.dirname (output_filename) self.mkpath (os.path.dirname (output_filename)) self.spawn ([self.link] + ld_args) diff --git a/util.py b/util.py index ddcf0d28..be3a1d6e 100644 --- a/util.py +++ b/util.py @@ -40,16 +40,16 @@ def native_path (pathname): using the current directory separator. Needed because filenames in the setup script are always supplied in Unix style, and have to be converted to the local convention before we can actually use them in - the filesystem. Raises DistutilsValueError if 'pathname' is + the filesystem. Raises ValueError if 'pathname' is absolute (starts with '/') or contains local directory separators (unless the local separator is '/', of course).""" if pathname[0] == '/': - raise DistutilsValueError, "path '%s' cannot be absolute" % pathname + raise ValueError, "path '%s' cannot be absolute" % pathname if pathname[-1] == '/': - raise DistutilsValueError, "path '%s' cannot end with '/'" % pathname + raise ValueError, "path '%s' cannot end with '/'" % pathname if os.sep != '/' and os.sep in pathname: - raise DistutilsValueError, \ + raise ValueError, \ "path '%s' cannot contain '%c' character" % \ (pathname, os.sep) -- cgit v1.2.1 From 30e154ec0f2489c3376e6cfc371e25a26a1fed70 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 15 Apr 2000 22:23:47 +0000 Subject: Reformatted all exception documentation as docstrings. --- errors.py | 55 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/errors.py b/errors.py index 61cdb72c..d66043a1 100644 --- a/errors.py +++ b/errors.py @@ -16,64 +16,65 @@ import types if type (RuntimeError) is types.ClassType: - # DistutilsError is the root of all Distutils evil. class DistutilsError (Exception): + """The root of all Distutils evil.""" pass - # DistutilsModuleError is raised if we are unable to load an expected - # module, or find an expected class within some module class DistutilsModuleError (DistutilsError): + """Unable to load an expected module, or to find an expected class + within some module (in particular, command modules and classes).""" pass - # DistutilsClassError is raised if we encounter a distribution or command - # class that's not holding up its end of the bargain. class DistutilsClassError (DistutilsError): + """Some command class (or possibly distribution class, if anyone + feels a need to subclass Distribution) is found not to be holding + up its end of the bargain, ie. implementing some part of the + "command "interface.""" pass - # DistutilsGetoptError (help me -- I have JavaProgrammersDisease!) is - # raised if the option table provided to fancy_getopt is bogus. class DistutilsGetoptError (DistutilsError): + """The option table provided to 'fancy_getopt()' is bogus.""" pass - # DistutilsArgError is raised by fancy_getopt in response to getopt.error; - # distutils.core then turns around and raises SystemExit from that. (Thus - # client code should never see DistutilsArgError.) class DistutilsArgError (DistutilsError): + """Raised by fancy_getopt in response to getopt.error -- ie. an + error in the command line usage.""" pass - # DistutilsFileError is raised for any problems in the filesystem: - # expected file not found, etc. class DistutilsFileError (DistutilsError): + """Any problems in the filesystem: expected file not found, etc. + Typically this is for problems that we detect before IOError or + OSError could be raised.""" pass - # DistutilsOptionError is raised for syntactic/semantic errors in - # command options, such as use of mutually conflicting options, or - # inconsistent options, badly-spelled values, etc. No distinction is - # made between option values originating in the setup script, the - # command line, config files, or what-have-you. class DistutilsOptionError (DistutilsError): + """Syntactic/semantic errors in command options, such as use of + mutually conflicting options, or inconsistent options, + badly-spelled values, etc. No distinction is made between option + values originating in the setup script, the command line, config + files, or what-have-you -- but if we *know* something originated in + the setup script, we'll raise DistutilsSetupError instead.""" pass - # DistutilsSetupError is raised for errors that can be definitely - # blamed on the setup script, such as invalid keyword arguments to - # 'setup()'. class DistutilsSetupError (DistutilsError): + """For errors that can be definitely blamed on the setup script, + such as invalid keyword arguments to 'setup()'.""" pass - # DistutilsPlatformError is raised when we find that we don't - # know how to do something on the current platform (but we do - # know how to do it on some platform). class DistutilsPlatformError (DistutilsError): + """We don't know how to do something on the current platform (but + we do know how to do it on some platform) -- eg. trying to compile + C files on a platform not supported by a CCompiler subclass.""" pass - # DistutilsExecError is raised if there are any problems executing - # an external program class DistutilsExecError (DistutilsError): + """Any problems executing an external program (such as the C + compiler, when compiling C files).""" pass - # DistutilsInternalError is raised on internal inconsistencies - # or impossibilities class DistutilsInternalError (DistutilsError): + """Internal inconsistencies or impossibilities (obviously, this + should never be seen if the code is working!).""" pass # String-based exceptions -- cgit v1.2.1 From 02d779e3f493d0fe4faebc32f4dfc9636b0de179 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 19 Apr 2000 02:16:49 +0000 Subject: Added 'link_executable()' method (Berthold Hoellmann). Two small fixes to 'link_shared_object()'. --- msvccompiler.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index c7a69c35..3667afcd 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -333,15 +333,13 @@ class MSVCCompiler (CCompiler) : (libraries, library_dirs, runtime_library_dirs) = \ self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) - if self.runtime_library_dirs: + if runtime_library_dirs: self.warn ("I don't know what to do with 'runtime_library_dirs': " + str (runtime_library_dirs)) lib_opts = gen_lib_options (self, library_dirs, runtime_library_dirs, libraries) - if type (output_dir) not in (StringType, NoneType): - raise TypeError, "'output_dir' must be a string or None" if output_dir is not None: output_filename = os.path.join (output_dir, output_filename) @@ -370,6 +368,53 @@ class MSVCCompiler (CCompiler) : self.announce ("skipping %s (up-to-date)" % output_filename) # link_shared_object () + + + def link_executable (self, + objects, + output_progname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None): + + (objects, output_dir) = self._fix_object_args (objects, output_dir) + (libraries, library_dirs, runtime_library_dirs) = \ + self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) + + if runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) + + lib_opts = gen_lib_options (self, + library_dirs, runtime_library_dirs, + libraries) + output_filename = output_progname + self.exe_extension + if output_dir is not None: + output_filename = os.path.join (output_dir, output_filename) + + if self._need_link (objects, output_filename): + + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + + ld_args = ldflags + lib_opts + \ + objects + ['/OUT:' + output_filename] + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend (extra_postargs) + + self.mkpath (os.path.dirname (output_filename)) + self.spawn ([self.link] + ld_args) + else: + self.announce ("skipping %s (up-to-date)" % output_filename) # -- Miscellaneous methods ----------------------------------------- -- cgit v1.2.1 From 32b92081ec16b3958ef792eff9e6588dcdff9e8c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 19 Apr 2000 02:18:09 +0000 Subject: Don't load the config.h file, even under Unix, because we never use the information from config.h. Code is still there in case someone in the future needs to parse an autoconf-generated config.h file. --- sysconfig.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index f7c2e783..c6341c10 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -213,8 +213,6 @@ def parse_makefile(fp, g=None): def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" g = globals() - # load the installed config.h: - parse_config_h(open(get_config_h_filename()), g) # load the installed Makefile: parse_makefile(open(get_makefile_filename()), g) -- cgit v1.2.1 From 1822bd769bbf61b61f469351555b24c6765f08a9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 19 Apr 2000 02:22:07 +0000 Subject: Added kludge to deal with the "./ld_so_aix" problem: force all strings in the Makefile that start with "./" to be absolute paths (with the implied root being the directory where the Makefile itself was found). --- sysconfig.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index c6341c10..5cc71dc0 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -205,6 +205,21 @@ def parse_makefile(fp, g=None): # bogus variable reference; just drop it since we can't deal del notdone[name] + # "Fix" all pathnames in the Makefile that are explicitly relative, + # ie. that start with "./". This is a kludge to fix the "./ld_so_aix" + # problem, the nature of which is that Python's installed Makefile + # refers to "./ld_so_aix", but when we are building extensions we are + # far from the directory where Python's Makefile (and ld_so_aix, for + # that matter) is installed. Unfortunately, there are several other + # relative pathnames in the Makefile, and this fix doesn't fix them, + # because the layout of Python's source tree -- which is what the + # Makefile refers to -- is not fully preserved in the Python + # installation. Grumble. + from os.path import normpath, join, dirname + for (name, value) in done.items(): + if value[0:2] == "./": + done[name] = normpath(join(dirname(fp.name), value)) + # save the results in the global dictionary g.update(done) return g -- cgit v1.2.1 From 4b2a001255107048e4117ac88b0a3d1aa371fd47 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 19 Apr 2000 02:23:21 +0000 Subject: Bumped version to 0.8.1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 93055549..5b8a6628 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.8" +__version__ = "0.8.1" -- cgit v1.2.1 From 8b97bc929e1390ec9ad9998d10c173923b0b91a0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 01:41:54 +0000 Subject: Hefty refactoring: converted 'fancy_getopt()' function into FancyGetopt class. (Mainly this was to support the ability to go back after the getopt operation is done and get extra information about the parse, in particular the original order of options seen on the command line. But it's a big improvement and should make it a lot easier to add functionality in the future.) --- fancy_getopt.py | 281 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 266 insertions(+), 15 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 3110ab30..94e654e8 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -27,11 +27,244 @@ longopt_re = re.compile (r'^%s$' % longopt_pat) # For recognizing "negative alias" options, eg. "quiet=!verbose" neg_alias_re = re.compile ("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) - # This is used to translate long options to legitimate Python identifiers # (for use as attributes of some object). longopt_xlate = string.maketrans ('-', '_') +# This records (option, value) pairs in the order seen on the command line; +# it's close to what getopt.getopt() returns, but with short options +# expanded. (Ugh, this module should be OO-ified.) +_option_order = None + + +class FancyGetopt: + """Wrapper around the standard 'getopt()' module that provides some + handy extra functionality: + * short and long options are tied together + * options have help strings, and help text can be assembled + from them + * options set attributes of a passed-in object + * boolean options can have "negative aliases" -- eg. if + --quiet is the "negative alias" of --verbose, then "--quiet" + on the command line sets 'verbose' to false + """ + + def __init__ (self, option_table=None): + + # The option table is (currently) a list of 3-tuples: + # (long_option, short_option, help_string) + # if an option takes an argument, its long_option should have '=' + # appended; short_option should just be a single character, no ':' + # in any case. If a long_option doesn't have a corresponding + # short_option, short_option should be None. All option tuples + # must have long options. + self.option_table = option_table + + # 'option_index' maps long option names to entries in the option + # table (ie. those 3-tuples). + self.option_index = {} + if self.option_table: + self.build_index() + + # 'negative_alias' keeps track of options that are the boolean + # opposite of some other option + self.negative_alias = {} + + # These keep track of the information in the option table. We + # don't actually populate these structures until we're ready to + # parse the command-line, since the 'option_table' passed in here + # isn't necessarily the final word. + self.short_opts = [] + self.long_opts = [] + self.short2long = {} + self.attr_name = {} + self.takes_arg = {} + + # And 'option_order' is filled up in 'getopt()'; it records the + # original order of options (and their values) on the command-line, + # but expands short options, converts aliases, etc. + self.option_order = [] + + # __init__ () + + + def build_index (self): + for option in self.option_table: + self.option_index[option[0]] = option + + def add_option (self, long_option, short_option=None, help_string=None): + if self.option_index.has_key(long_option): + raise DistutilsGetoptError, \ + "option conflict: already an option '%s'" % long_option + else: + option = (long_option, short_option, help_string) + self.option_table.append (option) + self.option_index[long_option] = option + + def set_negative_aliases (self, negative_alias): + """Set the negative aliases for this option parser. + 'negative_alias' should be a dictionary mapping option names to + option names, both the key and value must already be defined + in the option table.""" + + assert type(negative_alias) is DictionaryType + for (negopt, opt) in negative_alias.items(): + if not self.option_index.has_key(negopt): + raise DistutilsGetoptError, \ + ("invalid negative alias '%s': " + "option '%s' not defined") % (negopt, negopt) + if not self.option_index.has_key(opt): + raise DistutilsGetoptError, \ + ("invalid negative alias '%s': " + "aliased option '%s' not defined") % (negopt, opt) + + self.negative_alias = negative_alias + + + def _grok_option_table (self): + """Populate the various data structures that keep tabs on + the option table. Called by 'getopt()' before it can do + anything worthwhile.""" + + for option in self.option_table: + try: + (long, short, help) = option + except ValueError: + raise DistutilsGetoptError, \ + "invalid option tuple " + str (option) + + # Type- and value-check the option names + if type(long) is not StringType or len(long) < 2: + raise DistutilsGetoptError, \ + ("invalid long option '%s': " + "must be a string of length >= 2") % long + + if (not ((short is None) or + (type (short) is StringType and len (short) == 1))): + raise DistutilsGetoptError, \ + ("invalid short option '%s': " + "must a single character or None") % short + + self.long_opts.append (long) + + if long[-1] == '=': # option takes an argument? + if short: short = short + ':' + long = long[0:-1] + self.takes_arg[long] = 1 + else: + + # Is option is a "negative alias" for some other option (eg. + # "quiet" == "!verbose")? + alias_to = self.negative_alias.get(long) + if alias_to is not None: + if self.takes_arg[alias_to]: + raise DistutilsGetoptError, \ + ("invalid negative alias '%s': " + "aliased option '%s' takes a value") % \ + (long, alias_to) + + self.long_opts[-1] = long # XXX redundant?! + self.takes_arg[long] = 0 + + else: + self.takes_arg[long] = 0 + + + # Now enforce some bondage on the long option name, so we can + # later translate it to an attribute name on some object. Have + # to do this a bit late to make sure we've removed any trailing + # '='. + if not longopt_re.match (long): + raise DistutilsGetoptError, \ + ("invalid long option name '%s' " + + "(must be letters, numbers, hyphens only") % long + + self.attr_name[long] = string.translate (long, longopt_xlate) + if short: + self.short_opts.append (short) + self.short2long[short[0]] = long + + # for option_table + + # _grok_option_table() + + + def getopt (self, args=None, object=None): + + """Parse the command-line options in 'args' and store the results + as attributes of 'object'. If 'args' is None or not supplied, uses + 'sys.argv[1:]'. If 'object' is None or not supplied, creates a new + OptionDummy object, stores option values there, and returns a tuple + (args, object). If 'object' is supplied, it is modified in place + and 'getopt()' just returns 'args'; in both cases, the returned + 'args' is a modified copy of the passed-in 'args' list, which is + left untouched.""" + + if args is None: + args = sys.argv[1:] + if object is None: + object = OptionDummy() + created_object = 1 + else: + created_object = 0 + + self._grok_option_table() + + short_opts = string.join (self.short_opts) + try: + (opts, args) = getopt.getopt (args, short_opts, self.long_opts) + except getopt.error, msg: + raise DistutilsArgError, msg + + for (opt, val) in opts: + if len (opt) == 2 and opt[0] == '-': # it's a short option + opt = self.short2long[opt[1]] + + elif len (opt) > 2 and opt[0:2] == '--': + opt = opt[2:] + + else: + raise DistutilsInternalError, \ + "this can't happen: bad option string '%s'" % opt + + if not self.takes_arg[opt]: # boolean option? + if val != '': # shouldn't have a value! + raise DistutilsInternalError, \ + "this can't happen: bad option value '%s'" % value + + alias = self.negative_alias.get (opt) + if alias: + opt = alias + val = 0 + else: + val = 1 + + attr = self.attr_name[opt] + setattr (object, attr, val) + self.option_order.append ((opt, val)) + + # for opts + + if created_object: + return (args, object) + else: + return args + + # getopt() + + + def get_option_order (): + """Returns the list of (option, value) tuples processed by the + previous run of 'fancy_getopt()'. Raises RuntimeError if + 'fancy_getopt()' hasn't been called yet.""" + + if self.option_order is None: + raise RuntimeError, "'fancy_getopt()' hasn't been called yet" + else: + return self.option_order + +# class FancyGetopt + def fancy_getopt (options, negative_opt, object, args): @@ -117,6 +350,9 @@ def fancy_getopt (options, negative_opt, object, args): except getopt.error, msg: raise DistutilsArgError, msg + global _option_order # blechh! should OO-ify this module + _option_order = [] + for (opt, val) in opts: if len (opt) == 2 and opt[0] == '-': # it's a short option opt = short2long[opt[1]] @@ -125,29 +361,38 @@ def fancy_getopt (options, negative_opt, object, args): opt = opt[2:] else: - raise RuntimeError, "getopt lies! (bad option string '%s')" % \ - opt + raise DistutilsInternalError, \ + "this can't happen: bad option string '%s'" % opt + + if not takes_arg[opt]: # boolean option? + if val != '': # shouldn't have a value! + raise DistutilsInternalError, \ + "this can't happen: bad option value '%s'" % value + + alias = negative_opt.get (opt) + if alias: + opt = alias + val = 0 + else: + val = 1 attr = attr_name[opt] - if takes_arg[opt]: - setattr (object, attr, val) - else: - if val == '': - alias = negative_opt.get (opt) - if alias: - setattr (object, attr_name[alias], 0) - else: - setattr (object, attr, 1) - else: - raise RuntimeError, "getopt lies! (bad value '%s')" % value + setattr (object, attr, val) + _option_order.append ((opt, val)) - # end loop over options found in 'args' + # for opts return args # fancy_getopt() +def fancy_getopt (options, negative_opt, object, args): + parser = FancyGetopt (options) + parser.set_negative_aliases (negative_opt) + return parser.getopt (args, object) + + WS_TRANS = string.maketrans (string.whitespace, ' ' * len (string.whitespace)) def wrap_text (text, width): @@ -296,6 +541,12 @@ def print_help (options, file=None, header=None): for line in generate_help (options, header): file.write (line + "\n") # print_help () + + +class OptionDummy: + """Dummy class just used as a place to hold command-line option + values as instance attributes.""" + pass if __name__ == "__main__": -- cgit v1.2.1 From bacfe767f341ed061b5cc1c0ac1454d7f3a72d2e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 01:44:00 +0000 Subject: Continuing the refactoring: deleted the old 'fancy_getopt()' function, leaving in its place a tiny wrapper around the FancyGetopt class for backwards compatibility. --- fancy_getopt.py | 121 -------------------------------------------------------- 1 file changed, 121 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 94e654e8..8ee27748 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -266,127 +266,6 @@ class FancyGetopt: # class FancyGetopt -def fancy_getopt (options, negative_opt, object, args): - - # The 'options' table is a list of 3-tuples: - # (long_option, short_option, help_string) - # if an option takes an argument, its long_option should have '=' - # appended; short_option should just be a single character, no ':' in - # any case. If a long_option doesn't have a corresponding - # short_option, short_option should be None. All option tuples must - # have long options. - - # Build the short_opts string and long_opts list, remembering how - # the two are tied together - - short_opts = [] # we'll join 'em when done - long_opts = [] - short2long = {} - attr_name = {} - takes_arg = {} - - for option in options: - try: - (long, short, help) = option - except ValueError: - raise DistutilsGetoptError, \ - "invalid option tuple " + str (option) - - # Type-check the option names - if type (long) is not StringType or len (long) < 2: - raise DistutilsGetoptError, \ - "long option '%s' must be a string of length >= 2" % \ - long - - if (not ((short is None) or - (type (short) is StringType and len (short) == 1))): - raise DistutilsGetoptError, \ - "short option '%s' must be None or string of length 1" % \ - short - - long_opts.append (long) - - if long[-1] == '=': # option takes an argument? - if short: short = short + ':' - long = long[0:-1] - takes_arg[long] = 1 - else: - - # Is option is a "negative alias" for some other option (eg. - # "quiet" == "!verbose")? - alias_to = negative_opt.get(long) - if alias_to is not None: - if not takes_arg.has_key(alias_to) or takes_arg[alias_to]: - raise DistutilsGetoptError, \ - ("option '%s' is a negative alias for '%s', " + - "which either hasn't been defined yet " + - "or takes an argument") % (long, alias_to) - - long_opts[-1] = long - takes_arg[long] = 0 - - else: - takes_arg[long] = 0 - - - # Now enforce some bondage on the long option name, so we can later - # translate it to an attribute name in 'object'. Have to do this a - # bit late to make sure we've removed any trailing '='. - if not longopt_re.match (long): - raise DistutilsGetoptError, \ - ("invalid long option name '%s' " + - "(must be letters, numbers, hyphens only") % long - - attr_name[long] = string.translate (long, longopt_xlate) - if short: - short_opts.append (short) - short2long[short[0]] = long - - # end loop over 'options' - - short_opts = string.join (short_opts) - try: - (opts, args) = getopt.getopt (args, short_opts, long_opts) - except getopt.error, msg: - raise DistutilsArgError, msg - - global _option_order # blechh! should OO-ify this module - _option_order = [] - - for (opt, val) in opts: - if len (opt) == 2 and opt[0] == '-': # it's a short option - opt = short2long[opt[1]] - - elif len (opt) > 2 and opt[0:2] == '--': - opt = opt[2:] - - else: - raise DistutilsInternalError, \ - "this can't happen: bad option string '%s'" % opt - - if not takes_arg[opt]: # boolean option? - if val != '': # shouldn't have a value! - raise DistutilsInternalError, \ - "this can't happen: bad option value '%s'" % value - - alias = negative_opt.get (opt) - if alias: - opt = alias - val = 0 - else: - val = 1 - - attr = attr_name[opt] - setattr (object, attr, val) - _option_order.append ((opt, val)) - - # for opts - - return args - -# fancy_getopt() - - def fancy_getopt (options, negative_opt, object, args): parser = FancyGetopt (options) parser.set_negative_aliases (negative_opt) -- cgit v1.2.1 From 97f6eb18591a6e8a62dc727e4900ef036c801637 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 02:09:26 +0000 Subject: Made 'generate_help()' and 'print_help()' methods of FancyGetopt. Added 'set_option_table()' method. Added missing 'self' to 'get_option_order()'. Cosmetic/comment/docstring tweaks. --- fancy_getopt.py | 201 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 102 insertions(+), 99 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 8ee27748..c8112331 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -64,7 +64,7 @@ class FancyGetopt: # table (ie. those 3-tuples). self.option_index = {} if self.option_table: - self.build_index() + self._build_index() # 'negative_alias' keeps track of options that are the boolean # opposite of some other option @@ -88,10 +88,14 @@ class FancyGetopt: # __init__ () - def build_index (self): + def _build_index (self): for option in self.option_table: self.option_index[option[0]] = option + def set_option_table (self, option_table): + self.option_table = option_table + self._build_index() + def add_option (self, long_option, short_option=None, help_string=None): if self.option_index.has_key(long_option): raise DistutilsGetoptError, \ @@ -190,7 +194,6 @@ class FancyGetopt: def getopt (self, args=None, object=None): - """Parse the command-line options in 'args' and store the results as attributes of 'object'. If 'args' is None or not supplied, uses 'sys.argv[1:]'. If 'object' is None or not supplied, creates a new @@ -253,16 +256,108 @@ class FancyGetopt: # getopt() - def get_option_order (): + def get_option_order (self): """Returns the list of (option, value) tuples processed by the - previous run of 'fancy_getopt()'. Raises RuntimeError if - 'fancy_getopt()' hasn't been called yet.""" + previous run of 'getopt()'. Raises RuntimeError if + 'getopt()' hasn't been called yet.""" if self.option_order is None: - raise RuntimeError, "'fancy_getopt()' hasn't been called yet" + raise RuntimeError, "'getopt()' hasn't been called yet" else: return self.option_order + + def generate_help (header=None): + """Generate help text (a list of strings, one per suggested line of + output) from the option table for this FancyGetopt object.""" + + # Blithely assume the option table is good: probably wouldn't call + # 'generate_help()' unless you've already called 'getopt()'. + + # First pass: determine maximum length of long option names + max_opt = 0 + for option in self.option_table: + long = option[0] + short = option[1] + l = len (long) + if long[-1] == '=': + l = l - 1 + if short is not None: + l = l + 5 # " (-x)" where short == 'x' + if l > max_opt: + max_opt = l + + opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter + + # Typical help block looks like this: + # --foo controls foonabulation + # Help block for longest option looks like this: + # --flimflam set the flim-flam level + # and with wrapped text: + # --flimflam set the flim-flam level (must be between + # 0 and 100, except on Tuesdays) + # Options with short names will have the short name shown (but + # it doesn't contribute to max_opt): + # --foo (-f) controls foonabulation + # If adding the short option would make the left column too wide, + # we push the explanation off to the next line + # --flimflam (-l) + # set the flim-flam level + # Important parameters: + # - 2 spaces before option block start lines + # - 2 dashes for each long option name + # - min. 2 spaces between option and explanation (gutter) + # - 5 characters (incl. space) for short option name + + # Now generate lines of help text. (If 80 columns were good enough + # for Jesus, then 78 columns are good enough for me!) + line_width = 78 + text_width = line_width - opt_width + big_indent = ' ' * opt_width + if header: + lines = [header] + else: + lines = ['Option summary:'] + + for (long,short,help) in self.option_table: + + text = wrap_text (help, text_width) + if long[-1] == '=': + long = long[0:-1] + + # Case 1: no short option at all (makes life easy) + if short is None: + if text: + lines.append (" --%-*s %s" % (max_opt, long, text[0])) + else: + lines.append (" --%-*s " % (max_opt, long)) + + for l in text[1:]: + lines.append (big_indent + l) + + # Case 2: we have a short option, so we have to include it + # just after the long option + else: + opt_names = "%s (-%s)" % (long, short) + if text: + lines.append (" --%-*s %s" % + (max_opt, opt_names, text[0])) + else: + lines.append (" --%-*s" % opt_names) + + # for self.option_table + + return lines + + # generate_help () + + def print_help (self, file=None, header=None): + if file is None: + file = sys.stdout + for line in self.generate_help (header): + file.write (line + "\n") + # print_help () + # class FancyGetopt @@ -330,98 +425,6 @@ def wrap_text (text, width): # wrap_text () -def generate_help (options, header=None): - """Generate help text (a list of strings, one per suggested line of - output) from an option table.""" - - # Blithely assume the option table is good: probably wouldn't call - # 'generate_help()' unless you've already called 'fancy_getopt()'. - - # First pass: determine maximum length of long option names - max_opt = 0 - for option in options: - long = option[0] - short = option[1] - l = len (long) - if long[-1] == '=': - l = l - 1 - if short is not None: - l = l + 5 # " (-x)" where short == 'x' - if l > max_opt: - max_opt = l - - opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter - - # Typical help block looks like this: - # --foo controls foonabulation - # Help block for longest option looks like this: - # --flimflam set the flim-flam level - # and with wrapped text: - # --flimflam set the flim-flam level (must be between - # 0 and 100, except on Tuesdays) - # Options with short names will have the short name shown (but - # it doesn't contribute to max_opt): - # --foo (-f) controls foonabulation - # If adding the short option would make the left column too wide, - # we push the explanation off to the next line - # --flimflam (-l) - # set the flim-flam level - # Important parameters: - # - 2 spaces before option block start lines - # - 2 dashes for each long option name - # - min. 2 spaces between option and explanation (gutter) - # - 5 characters (incl. space) for short option name - - # Now generate lines of help text. - line_width = 78 # if 80 columns were good enough for - text_width = line_width - opt_width # Jesus, then 78 are good enough for me - big_indent = ' ' * opt_width - if header: - lines = [header] - else: - lines = ['Option summary:'] - - for (long,short,help) in options: - - text = wrap_text (help, text_width) - if long[-1] == '=': - long = long[0:-1] - - # Case 1: no short option at all (makes life easy) - if short is None: - if text: - lines.append (" --%-*s %s" % (max_opt, long, text[0])) - else: - lines.append (" --%-*s " % (max_opt, long)) - - for l in text[1:]: - lines.append (big_indent + l) - - # Case 2: we have a short option, so we have to include it - # just after the long option - else: - opt_names = "%s (-%s)" % (long, short) - if text: - lines.append (" --%-*s %s" % - (max_opt, opt_names, text[0])) - else: - lines.append (" --%-*s" % opt_names) - - # for loop over options - - return lines - -# generate_help () - - -def print_help (options, file=None, header=None): - if file is None: - file = sys.stdout - for line in generate_help (options, header): - file.write (line + "\n") -# print_help () - - class OptionDummy: """Dummy class just used as a place to hold command-line option values as instance attributes.""" -- cgit v1.2.1 From cae8078a1df69595f030390f134fd59452b6db77 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 02:28:14 +0000 Subject: Patch, originally from Bastian Kleineidam and savagely mutilated by me, to add the "display metadata" options: --name, --version, --author, and so forth. Main changes: * added 'display_options' class attribute to list all the "display only" options (--help-commands plus the metadata options) * added DistributionMetadata class as a place to put the actual metadata information from the setup script (not to be confused with the metadata display options); the logic dealing with metadata (eg. return self.name or "UNKNOWN") is now in this class * changed 'parse_command_line()' to use the new OO interface provided by fancy_getopt, mainly so we can get at the original order of options on the command line, so we can print multiple lines of distribution meta-data in the order specified by the user * added 'handle_display_options()' to handle display-only options Also fixed some crufty old comments/docstrings. --- dist.py | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 193 insertions(+), 50 deletions(-) diff --git a/dist.py b/dist.py index 408b9f5c..bedd9d25 100644 --- a/dist.py +++ b/dist.py @@ -12,7 +12,7 @@ import sys, string, re from types import * from copy import copy from distutils.errors import * -from distutils.fancy_getopt import fancy_getopt, print_help +from distutils.fancy_getopt import FancyGetopt, longopt_xlate # Regex to define acceptable Distutils command names. This is not *quite* @@ -40,8 +40,8 @@ class Distribution: # 'global_options' describes the command-line options that may be - # supplied to the client (setup.py) prior to any actual commands. - # Eg. "./setup.py -nv" or "./setup.py --verbose" both take advantage of + # supplied to the setup script prior to any actual commands. + # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of # these global options. This list should be kept to a bare minimum, # since every global option is also valid as a command option -- and we # don't want to pollute the commands with too many options that they @@ -53,8 +53,47 @@ class Distribution: ('dry-run', 'n', "don't actually do anything"), ('help', 'h', - "show this help message"), + "show this help message, plus help for any commands " + + "given on the command-line"), ] + + # options that are not propagated to the commands + display_options = [ + ('help-commands', None, + "list all available commands"), + ('name', None, + "print package name"), + ('version', 'V', + "print package version"), + ('fullname', None, + "print -"), + ('author', None, + "print the author's name"), + ('author-email', None, + "print the author's email address"), + ('maintainer', None, + "print the maintainer's name"), + ('maintainer-email', None, + "print the maintainer's email address"), + ('contact', None, + "print the name of the maintainer if present, " + "else author"), + ('contact-email', None, + "print the email of the maintainer if present, " + "else author"), + ('url', None, + "print the URL for this package"), + ('licence', None, + "print the licence of the package"), + ('license', None, + "alias for --licence"), + ('description', None, + "print the package description"), + ] + display_option_names = map(lambda x: string.translate(x[0], longopt_xlate), + display_options) + + # negative options are options that exclude other options negative_opt = {'quiet': 'verbose'} @@ -75,31 +114,28 @@ class Distribution: self.verbose = 1 self.dry_run = 0 self.help = 0 - self.help_commands = 0 - - # And the "distribution meta-data" options -- these can only - # come from setup.py (the caller), not the command line - # (or a hypothetical config file). - self.name = None - self.version = None - self.author = None - self.author_email = None - self.maintainer = None - self.maintainer_email = None - self.url = None - self.licence = None - self.description = None + for attr in self.display_option_names: + setattr(self, attr, 0) + + # Store the distribution meta-data (name, version, author, and so + # forth) in a separate object -- we're getting to have enough + # information here (and enough command-line options) that it's + # worth it. Also delegate 'get_XXX()' methods to the 'metadata' + # object in a sneaky and underhanded (but efficient!) way. + self.metadata = DistributionMetadata () + for attr in dir(self.metadata): + meth_name = "get_" + attr + setattr(self, meth_name, getattr(self.metadata, meth_name)) # 'cmdclass' maps command names to class objects, so we # can 1) quickly figure out which class to instantiate when # we need to create a new command object, and 2) have a way - # for the client to override command classes + # for the setup script to override command classes self.cmdclass = {} # These options are really the business of various commands, rather # than of the Distribution itself. We provide aliases for them in # Distribution as a convenience to the developer. - # dictionary. self.packages = None self.package_dir = None self.py_modules = None @@ -128,8 +164,9 @@ class Distribution: self.have_run = {} # Now we'll use the attrs dictionary (ultimately, keyword args from - # the client) to possibly override any or all of these distribution - # options. + # the setup script) to possibly override any or all of these + # distribution options. + if attrs: # Pull out the set of command options and work on them @@ -149,7 +186,9 @@ class Distribution: # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! for (key,val) in attrs.items(): - if hasattr (self, key): + if hasattr (self.metadata, key): + setattr (self.metadata, key, val) + elif hasattr (self, key): setattr (self, key, val) else: raise DistutilsSetupError, \ @@ -192,19 +231,17 @@ class Distribution: # happen until we know what the command is. self.commands = [] - options = self.global_options + \ - [('help-commands', None, - "list all available commands")] - args = fancy_getopt (options, self.negative_opt, - self, sys.argv[1:]) + parser = FancyGetopt (self.global_options + self.display_options) + parser.set_negative_aliases (self.negative_opt) + args = parser.getopt (object=self) + option_order = parser.get_option_order() - # User just wants a list of commands -- we'll print it out and stop - # processing now (ie. if they ran "setup --help-commands foo bar", - # we ignore "foo bar"). - if self.help_commands: - self.print_commands () - print - print usage + # Handle aliases (license == licence) + if self.license: + self.licence = 1 + + # for display options we return immediately + if self.handle_display_options(option_order): return while args: @@ -246,15 +283,17 @@ class Distribution: negative_opt = copy (negative_opt) negative_opt.update (cmd_obj.negative_opt) - options = self.global_options + cmd_obj.user_options - args = fancy_getopt (options, negative_opt, - cmd_obj, args[1:]) + parser.set_option_table (self.global_options + + cmd_obj.user_options) + parser.set_negative_aliases (negative_opt) + args = parser.getopt (args[1:], cmd_obj) if cmd_obj.help: - print_help (self.global_options, - header="Global options:") + parser.set_option_table (self.global_options) + parser.print_help ("Global options:") print - print_help (cmd_obj.user_options, - header="Options for '%s' command:" % command) + + parser.set_option_table (cmd_obj.user_options) + parser.print_help ("Options for '%s' command:" % command) print print usage return @@ -271,13 +310,23 @@ class Distribution: # get help on a command, but I support it because that's how # CVS does it -- might as well be consistent.) if self.help: - print_help (self.global_options, header="Global options:") + parser.set_option_table (self.global_options) + parser.print_help ( + "Global options (apply to all commands, " + + "or can be used per command):") print + if not self.commands: + parser.set_option_table (self.display_options) + parser.print_help ( + "Information display options (just display " + + "information, ignore any commands)") + print + for command in self.commands: klass = self.find_command_class (command) - print_help (klass.user_options, - header="Options for '%s' command:" % command) + parser.set_option_table (klass.user_options) + parser.print_help ("Options for '%s' command:" % command) print print usage @@ -292,6 +341,40 @@ class Distribution: # parse_command_line() + def handle_display_options (self, option_order): + """If there were any non-global "display-only" options + (--help-commands or the metadata display options) on the command + line, display the requested info and return true; else return + false.""" + + from distutils.core import usage + + # User just wants a list of commands -- we'll print it out and stop + # processing now (ie. if they ran "setup --help-commands foo bar", + # we ignore "foo bar"). + if self.help_commands: + self.print_commands () + print + print usage + return 1 + + # If user supplied any of the "display metadata" options, then + # display that metadata in the order in which the user supplied the + # metadata options. + any_display_options = 0 + is_display_option = {} + for option in self.display_options: + is_display_option[option[0]] = 1 + + for (opt, val) in option_order: + if val and is_display_option.get(opt): + opt = string.translate (opt, longopt_xlate) + print getattr(self.metadata, "get_"+opt)() + any_display_options = 1 + + return any_display_options + + # handle_display_options() def print_command_list (self, commands, header, max_length): """Print a subset of the list of all commands -- used by @@ -437,7 +520,7 @@ class Distribution: def run_commands (self): - """Run each command that was seen on the client command line. + """Run each command that was seen on the setup script command line. Uses the list of commands found and cache of command objects created by 'create_command_obj()'.""" @@ -532,14 +615,74 @@ class Distribution: not self.has_ext_modules() and not self.has_c_libraries()) + # -- Metadata query methods ---------------------------------------- + + # If you're looking for 'get_name()', 'get_version()', and so forth, + # they are defined in a sneaky way: the constructor binds self.get_XXX + # to self.metadata.get_XXX. The actual code is in the + # DistributionMetadata class, below. + +# class Distribution + + +class DistributionMetadata: + """Dummy class to hold the distribution meta-data: name, version, + author, and so forth.""" + + def __init__ (self): + self.name = None + self.version = None + self.author = None + self.author_email = None + self.maintainer = None + self.maintainer_email = None + self.url = None + self.licence = None + self.description = None + + # -- Metadata query methods ---------------------------------------- + def get_name (self): return self.name or "UNKNOWN" - def get_full_name (self): - return "%s-%s" % ((self.name or "UNKNOWN"), (self.version or "???")) - -# class Distribution + def get_version(self): + return self.version or "???" + + def get_fullname (self): + return "%s-%s" % (self.get_name(), self.get_version()) + + def get_author(self): + return self.author or "UNKNOWN" + def get_author_email(self): + return self.author_email or "UNKNOWN" + + def get_maintainer(self): + return self.maintainer or "UNKNOWN" + + def get_maintainer_email(self): + return self.maintainer_email or "UNKNOWN" + + def get_contact(self): + return (self.maintainer or + self.author or + "UNKNOWN") + + def get_contact_email(self): + return (self.maintainer_email or + self.author_email or + "UNKNOWN") + + def get_url(self): + return self.url or "UNKNOWN" + + def get_licence(self): + return self.licence or "UNKNOWN" + + def get_description(self): + return self.description or "UNKNOWN" + +# class DistributionMetadata if __name__ == "__main__": dist = Distribution () -- cgit v1.2.1 From 56ff5710cb6403a6135fdb2104a3545a27cdff64 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 02:31:07 +0000 Subject: Added 'has_option()', 'get_attr_name()' methods. --- fancy_getopt.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index c8112331..cfebab49 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -105,6 +105,19 @@ class FancyGetopt: self.option_table.append (option) self.option_index[long_option] = option + + def has_option (self, long_option): + """Return true if the option table for this parser has an + option with long name 'long_option'.""" + return self.option_index.has_key(long_option) + + def get_attr_name (self, long_option): + """Translate long option name 'long_option' to the form it + has as an attribute of some object: ie., translate hyphens + to underscores.""" + return string.translate (long_option, longopt_xlate) + + def set_negative_aliases (self, negative_alias): """Set the negative aliases for this option parser. 'negative_alias' should be a dictionary mapping option names to @@ -183,7 +196,7 @@ class FancyGetopt: ("invalid long option name '%s' " + "(must be letters, numbers, hyphens only") % long - self.attr_name[long] = string.translate (long, longopt_xlate) + self.attr_name[long] = self.get_attr_name (long) if short: self.short_opts.append (short) self.short2long[short[0]] = long -- cgit v1.2.1 From fe34d8a966eede0315681b03ec003d9b8fb0c681 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 04:22:01 +0000 Subject: Added the capability for alias options. --- fancy_getopt.py | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index cfebab49..629da29e 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -66,6 +66,10 @@ class FancyGetopt: if self.option_table: self._build_index() + # 'alias' records (duh) alias options; {'foo': 'bar'} means + # --foo is an alias for --bar + self.alias = {} + # 'negative_alias' keeps track of options that are the boolean # opposite of some other option self.negative_alias = {} @@ -118,23 +122,29 @@ class FancyGetopt: return string.translate (long_option, longopt_xlate) + def _check_alias_dict (self, aliases, what): + assert type(aliases) is DictionaryType + for (alias, opt) in aliases.items(): + if not self.option_index.has_key(alias): + raise DistutilsGetoptError, \ + ("invalid %s '%s': " + "option '%s' not defined") % (what, alias, alias) + if not self.option_index.has_key(opt): + raise DistutilsGetoptError, \ + ("invalid %s '%s': " + "aliased option '%s' not defined") % (what, alias, opt) + + def set_aliases (self, alias): + """Set the aliases for this option parser.""" + self._check_alias_dict (alias, "alias") + self.alias = alias + def set_negative_aliases (self, negative_alias): """Set the negative aliases for this option parser. 'negative_alias' should be a dictionary mapping option names to option names, both the key and value must already be defined in the option table.""" - - assert type(negative_alias) is DictionaryType - for (negopt, opt) in negative_alias.items(): - if not self.option_index.has_key(negopt): - raise DistutilsGetoptError, \ - ("invalid negative alias '%s': " - "option '%s' not defined") % (negopt, negopt) - if not self.option_index.has_key(opt): - raise DistutilsGetoptError, \ - ("invalid negative alias '%s': " - "aliased option '%s' not defined") % (negopt, opt) - + self._check_alias_dict (negative_alias, "negative alias") self.negative_alias = negative_alias @@ -186,6 +196,16 @@ class FancyGetopt: else: self.takes_arg[long] = 0 + # If this is an alias option, make sure its "takes arg" flag is + # the same as the option it's aliased to. + alias_to = self.alias.get(long) + if alias_to is not None: + if self.takes_arg[long] != self.takes_arg[alias_to]: + raise DistutilsGetoptError, \ + ("invalid alias '%s': inconsistent with " + "aliased option '%s' (one of them takes a value, " + "the other doesn't") % (long, alias_to) + # Now enforce some bondage on the long option name, so we can # later translate it to an attribute name on some object. Have @@ -243,6 +263,10 @@ class FancyGetopt: raise DistutilsInternalError, \ "this can't happen: bad option string '%s'" % opt + alias = self.alias.get(opt) + if alias: + opt = alias + if not self.takes_arg[opt]: # boolean option? if val != '': # shouldn't have a value! raise DistutilsInternalError, \ -- cgit v1.2.1 From 7ee4ef65de0dc64230420e7ed910385384094967 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 04:22:49 +0000 Subject: Fixed the '--license' option so it's officially an alias for '--licence', and now actually works. --- dist.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dist.py b/dist.py index bedd9d25..a2092127 100644 --- a/dist.py +++ b/dist.py @@ -233,13 +233,10 @@ class Distribution: self.commands = [] parser = FancyGetopt (self.global_options + self.display_options) parser.set_negative_aliases (self.negative_opt) + parser.set_aliases ({'license': 'licence'}) args = parser.getopt (object=self) option_order = parser.get_option_order() - # Handle aliases (license == licence) - if self.license: - self.licence = 1 - # for display options we return immediately if self.handle_display_options(option_order): return -- cgit v1.2.1 From e90d86fe9ee732ff165ac44bf174312ac828e9c9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 04:31:10 +0000 Subject: Patch from Andrew Kuchling: allow multiple include/exclude patterns for all commands except 'prune' and 'graft'. --- command/sdist.py | 103 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index f3cc041e..59dd6248 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -310,24 +310,25 @@ class sdist (Command): # for the given action (which is the first word) if action in ('include','exclude', 'global-include','global-exclude'): - if len (words) != 2: + if len (words) < 2: template.warn \ ("invalid manifest template line: " + - "'%s' expects a single " % + "'%s' expects ..." % action) continue - pattern = native_path (words[1]) + pattern_list = map(native_path, words[1:]) elif action in ('recursive-include','recursive-exclude'): - if len (words) != 3: + if len (words) < 3: template.warn \ ("invalid manifest template line: " + - "'%s' expects " % + "'%s' expects ..." % action) continue - (dir, pattern) = map (native_path, words[1:3]) + dir = native_path(words[1]) + pattern_list = map (native_path, words[2:]) elif action in ('graft','prune'): if len (words) != 2: @@ -352,58 +353,64 @@ class sdist (Command): # digging stuff up out of 'words'. if action == 'include': - print "include", pattern - files = select_pattern (all_files, pattern, anchor=1) - if not files: - template.warn ("no files found matching '%s'" % pattern) - else: - self.files.extend (files) + print "include", string.join(pattern_list) + for pattern in pattern_list: + files = select_pattern (all_files, pattern, anchor=1) + if not files: + template.warn ("no files found matching '%s'" % pattern) + else: + self.files.extend (files) elif action == 'exclude': - print "exclude", pattern - num = exclude_pattern (self.files, pattern, anchor=1) - if num == 0: - template.warn \ - ("no previously-included files found matching '%s'" % - pattern) + print "exclude", string.join(pattern_list) + for pattern in pattern_list: + num = exclude_pattern (self.files, pattern, anchor=1) + if num == 0: + template.warn ( + "no previously-included files found matching '%s'"% + pattern) elif action == 'global-include': - print "global-include", pattern - files = select_pattern (all_files, pattern, anchor=0) - if not files: - template.warn (("no files found matching '%s' " + - "anywhere in distribution") % - pattern) - else: - self.files.extend (files) + print "global-include", string.join(pattern_list) + for pattern in pattern_list: + files = select_pattern (all_files, pattern, anchor=0) + if not files: + template.warn (("no files found matching '%s' " + + "anywhere in distribution") % + pattern) + else: + self.files.extend (files) elif action == 'global-exclude': - print "global-exclude", pattern - num = exclude_pattern (self.files, pattern, anchor=0) - if num == 0: - template.warn \ - (("no previously-included files matching '%s' " + - "found anywhere in distribution") % - pattern) + print "global-exclude", string.join(pattern_list) + for pattern in pattern_list: + num = exclude_pattern (self.files, pattern, anchor=0) + if num == 0: + template.warn \ + (("no previously-included files matching '%s' " + + "found anywhere in distribution") % + pattern) elif action == 'recursive-include': - print "recursive-include", dir, pattern - files = select_pattern (all_files, pattern, prefix=dir) - if not files: - template.warn (("no files found matching '%s' " + - "under directory '%s'") % - (pattern, dir)) - else: - self.files.extend (files) + print "recursive-include", dir, string.join(pattern_list) + for pattern in pattern_list: + files = select_pattern (all_files, pattern, prefix=dir) + if not files: + template.warn (("no files found matching '%s' " + + "under directory '%s'") % + (pattern, dir)) + else: + self.files.extend (files) elif action == 'recursive-exclude': - print "recursive-exclude", dir, pattern - num = exclude_pattern (self.files, pattern, prefix=dir) - if num == 0: - template.warn \ - (("no previously-included files matching '%s' " + - "found under directory '%s'") % - (pattern, dir)) + print "recursive-exclude", dir, string.join(pattern_list) + for pattern in pattern_list: + num = exclude_pattern (self.files, pattern, prefix=dir) + if num == 0: + template.warn \ + (("no previously-included files matching '%s' " + + "found under directory '%s'") % + (pattern, dir)) elif action == 'graft': print "graft", dir_pattern -- cgit v1.2.1 From bf586ddbeea4df44ebf9a21a17e12243bad0a327 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 04:37:12 +0000 Subject: Fix 'check_metadata()' so it grovels through the distribution's metadata object, rather than through the distribution itself (since I moved the meta- data out to a DistributionMetadata instance). --- command/sdist.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 59dd6248..43007d59 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -107,23 +107,23 @@ class sdist (Command): def check_metadata (self): - dist = self.distribution + metadata = self.distribution.metadata missing = [] for attr in ('name', 'version', 'url'): - if not (hasattr (dist, attr) and getattr (dist, attr)): + if not (hasattr (metadata, attr) and getattr (metadata, attr)): missing.append (attr) if missing: self.warn ("missing required meta-data: " + string.join (missing, ", ")) - if dist.author: - if not dist.author_email: + if metadata.author: + if not metadata.author_email: self.warn ("missing meta-data: if 'author' supplied, " + "'author_email' must be supplied too") - elif dist.maintainer: - if not dist.maintainer_email: + elif metadata.maintainer: + if not metadata.maintainer_email: self.warn ("missing meta-data: if 'maintainer' supplied, " + "'maintainer_email' must be supplied too") else: -- cgit v1.2.1 From 5a78411da74f438590a1d76d71213a5b667c379e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 02:51:25 +0000 Subject: Changed to call 'get_fullname()', not 'get_full_name()', on Distribution object. --- command/bdist_dumb.py | 2 +- command/sdist.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 5456e57a..0a68d00a 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -70,7 +70,7 @@ class bdist_dumb (Command): # And make an archive relative to the root of the # pseudo-installation tree. - archive_basename = "%s.%s" % (self.distribution.get_full_name(), + archive_basename = "%s.%s" % (self.distribution.get_fullname(), get_platform()) print "output_dir = %s" % output_dir print "self.format = %s" % self.format diff --git a/command/sdist.py b/command/sdist.py index 43007d59..2cf8e385 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -502,7 +502,7 @@ class sdist (Command): # Don't warn about missing meta-data here -- should be (and is!) # done elsewhere. - base_dir = self.distribution.get_full_name() + base_dir = self.distribution.get_fullname() # Remove any files that match "base_dir" from the fileset -- we # don't want to go distributing the distribution inside itself! -- cgit v1.2.1 From 9a32e742ff8308ffcf088fe46eb527fbffd0e812 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 02:52:44 +0000 Subject: Fix how we generate the meta-data query methods to include 'get_fullname()' and the other "composite meta-data" methods. --- dist.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dist.py b/dist.py index a2092127..4a83c794 100644 --- a/dist.py +++ b/dist.py @@ -123,9 +123,11 @@ class Distribution: # worth it. Also delegate 'get_XXX()' methods to the 'metadata' # object in a sneaky and underhanded (but efficient!) way. self.metadata = DistributionMetadata () - for attr in dir(self.metadata): - meth_name = "get_" + attr - setattr(self, meth_name, getattr(self.metadata, meth_name)) + method_basenames = dir(self.metadata) + \ + ['fullname', 'contact', 'contact_email'] + for basename in method_basenames: + method_name = "get_" + basename + setattr(self, method_name, getattr(self.metadata, method_name)) # 'cmdclass' maps command names to class objects, so we # can 1) quickly figure out which class to instantiate when -- cgit v1.2.1 From 71ceb366185124bc76998bd06c7ef6676f7d7827 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 03:09:56 +0000 Subject: Extracted the "what-do-I-do-for-this-format" logic from code in 'make_archive()' to a global static dictionary, ARCHIVE_FORMATS. Added 'check_archive_formats()', which obviously makes good use of this dictionary. --- archive_util.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/archive_util.py b/archive_util.py index a28eed1a..bae425be 100644 --- a/archive_util.py +++ b/archive_util.py @@ -102,6 +102,20 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): # make_zipfile () +ARCHIVE_FORMATS = { + 'gztar': (make_tarball, [('compress', 'gzip')]), + 'ztar': (make_tarball, [('compress', 'compress')]), + 'tar': (make_tarball, [('compress', None)]), + 'zip': (make_zipfile, []) + } + +def check_archive_formats (formats): + for format in formats: + if not ARCHIVE_FORMATS.has_key(format): + return format + else: + return None + def make_archive (base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=0): @@ -130,18 +144,14 @@ def make_archive (base_name, format, kwargs = { 'verbose': verbose, 'dry_run': dry_run } - if format == 'gztar': - func = make_tarball - kwargs['compress'] = 'gzip' - elif format == 'ztar': - func = make_tarball - kwargs['compress'] = 'compress' - elif format == 'tar': - func = make_tarball - kwargs['compress'] = None - elif format == 'zip': - func = make_zipfile + try: + format_info = ARCHIVE_FORMATS[format] + except KeyError: + raise ValueError, "unknown archive format '%s'" % format + func = format_info[0] + for (arg,val) in format_info[1]: + kwargs[arg] = val apply (func, (base_name, base_dir), kwargs) if root_dir is not None: -- cgit v1.2.1 From ff4f4d515fa616a7480b7a3be0d42fb6b10cf7d5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 03:11:17 +0000 Subject: Catch DistutilsOptionError in 'setup()' -- it's thrown either because of errors in the setup script or on the command line, so shouldn't result in a traceback. --- core.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core.py b/core.py index 9a801063..814f4418 100644 --- a/core.py +++ b/core.py @@ -96,7 +96,9 @@ def setup (**attrs): "error: %s: %s" % (exc.filename, exc.strerror) else: raise SystemExit, str (exc) - except (DistutilsExecError, DistutilsFileError), msg: + except (DistutilsExecError, + DistutilsFileError, + DistutilsOptionError), msg: raise SystemExit, "error: " + str (msg) # setup () -- cgit v1.2.1 From b106a335675ced0bd73d4a70addc388feef883e4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 03:11:55 +0000 Subject: Check that 'self.formats' is good early on. --- command/sdist.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 2cf8e385..8aa4618e 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,10 +11,10 @@ import fnmatch from types import * from glob import glob from distutils.core import Command -from distutils.util import \ - newer, create_tree, remove_tree, make_tarball, make_zipfile, native_path +from distutils.util import newer, create_tree, remove_tree, native_path +from distutils.archive_util import check_archive_formats from distutils.text_file import TextFile -from distutils.errors import DistutilsExecError +from distutils.errors import DistutilsExecError, DistutilsOptionError class sdist (Command): @@ -81,6 +81,11 @@ class sdist (Command): elif type (self.formats) is StringType: self.formats = string.split (self.formats, ',') + bad_format = check_archive_formats (self.formats) + if bad_format: + raise DistutilsOptionError, \ + "unknown archive format '%s'" % bad_format + def run (self): -- cgit v1.2.1 From b99aaeebb214b61283c589940ed59489353062f8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 03:20:49 +0000 Subject: Merged in code from the 0.1.5 release to handle IOError and OSError exceptions better. --- core.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/core.py b/core.py index 814f4418..4b696928 100644 --- a/core.py +++ b/core.py @@ -9,7 +9,7 @@ really defined in distutils.dist and distutils.cmd.""" __revision__ = "$Id$" -import sys +import sys, os from types import * from distutils.errors import * from distutils.dist import Distribution @@ -89,13 +89,19 @@ def setup (**attrs): dist.run_commands () except KeyboardInterrupt: raise SystemExit, "interrupted" - except (OSError, IOError), exc: - # arg, try to work with Python pre-1.5.2 + except (IOError, os.error), exc: + # check for Python 1.5.2-style {IO,OS}Error exception objects if hasattr (exc, 'filename') and hasattr (exc, 'strerror'): - raise SystemExit, \ - "error: %s: %s" % (exc.filename, exc.strerror) + if exc.filename: + raise SystemExit, \ + "error: %s: %s" % (exc.filename, exc.strerror) + else: + # two-argument functions in posix module don't + # include the filename in the exception object! + raise SystemExit, \ + "error: %s" % exc.strerror else: - raise SystemExit, str (exc) + raise SystemExit, "error: " + exc[-1] except (DistutilsExecError, DistutilsFileError, DistutilsOptionError), msg: -- cgit v1.2.1 From c0aaf184cfb98c03241a7a455720f56074e1877d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 15:14:58 +0000 Subject: Merged in Python 1.5.1 compatibility changes from the 0.1.3 branch: added 'abspath()' and 'extend()'. --- util.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/util.py b/util.py index be3a1d6e..9c436b94 100644 --- a/util.py +++ b/util.py @@ -18,6 +18,30 @@ from distutils.dep_util import * from distutils.archive_util import * +# Need to define 'abspath()', because it was new with Python 1.5.2 +if hasattr (os.path, 'abspath'): + abspath = os.path.abspath +else: + def abspath(path): + if not os.path.isabs(path): + path = os.path.join(os.getcwd(), path) + return os.path.normpath(path) + + +# More backwards compatability hacks +def extend (list, new_list): + """Appends the list 'new_list' to 'list', just like the 'extend()' + list method does in Python 1.5.2 -- but this works on earlier + versions of Python too.""" + + if hasattr (list, 'extend'): + list.extend (new_list) + else: + list[len(list):] = new_list + +# extend () + + def get_platform (): """Return a string (suitable for tacking onto directory names) that identifies the current platform. Under Unix, identifies both the OS -- cgit v1.2.1 From 32155114f6d362c883a9d0f7e337ab91bf7a2187 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 15:17:14 +0000 Subject: Sporadic, untested Python 1.5.1 compatibility changes. --- file_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file_util.py b/file_util.py index 91545abb..32245109 100644 --- a/file_util.py +++ b/file_util.py @@ -141,7 +141,7 @@ def copy_file (src, dst, import macostools try: macostools.copy (src, dst, 0, preserve_times) - except OSError, exc: + except os.error, exc: raise DistutilsFileError, \ "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) -- cgit v1.2.1 From cc06f983649b572614316eeaf64846a5dcecd5bf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 23 Apr 2000 02:50:45 +0000 Subject: Patch from Harry Henry Gebel: fix two stupid bugs in help-printing stuff. --- fancy_getopt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 629da29e..39450e80 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -304,7 +304,7 @@ class FancyGetopt: return self.option_order - def generate_help (header=None): + def generate_help (self, header=None): """Generate help text (a list of strings, one per suggested line of output) from the option table for this FancyGetopt object.""" @@ -388,7 +388,7 @@ class FancyGetopt: # generate_help () - def print_help (self, file=None, header=None): + def print_help (self, header=None, file=None): if file is None: file = sys.stdout for line in self.generate_help (header): -- cgit v1.2.1 From 2db555c9b072cb91541f1f9a98d3398fde1a8601 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 25 Apr 2000 01:33:11 +0000 Subject: Lyle Johnson: fixed broken logic in 'native_path()'. --- util.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/util.py b/util.py index 9c436b94..ca20ae04 100644 --- a/util.py +++ b/util.py @@ -72,13 +72,13 @@ def native_path (pathname): raise ValueError, "path '%s' cannot be absolute" % pathname if pathname[-1] == '/': raise ValueError, "path '%s' cannot end with '/'" % pathname - if os.sep != '/' and os.sep in pathname: - raise ValueError, \ - "path '%s' cannot contain '%c' character" % \ - (pathname, os.sep) - - paths = string.split (pathname, '/') - return apply (os.path.join, paths) + if os.sep != '/': + if os.sep in pathname: + raise ValueError, \ + "path '%s' cannot contain '%c' character" % (pathname, os.sep) + else: + paths = string.split (pathname, '/') + return apply (os.path.join, paths) else: return pathname -- cgit v1.2.1 From b2fe902127ca56285d80dbc3befd671ad467ba7c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 25 Apr 2000 01:38:20 +0000 Subject: Harry Henry Gebel: Adds bztar format to generate .tar.bz2 tarballs Uses the -f argument to overright old tarballs automatically, I am assuming that if the old tarball was wanted it would have been moved or else the version number would have been changed. Uses the -9 argument to bzip2 and gzip to use maximum compression. Compress uses the maximum compression by default. Tests for correct value for the 'compress' argument of make_tarball. This is one less place for someone adding new compression programs to forget to change. --- archive_util.py | 24 ++++++++++++++++-------- command/bdist.py | 4 +++- command/sdist.py | 3 +-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/archive_util.py b/archive_util.py index bae425be..050ea417 100644 --- a/archive_util.py +++ b/archive_util.py @@ -15,12 +15,12 @@ from distutils.spawn import spawn def make_tarball (base_name, base_dir, compress="gzip", verbose=0, dry_run=0): """Create a (possibly compressed) tar file from all the files under - 'base_dir'. 'compress' must be "gzip" (the default), "compress", or - None. Both "tar" and the compression utility named by 'compress' - must be on the default program search path, so this is probably - Unix-specific. The output tar file will be named 'base_dir' + - ".tar", possibly plus the appropriate compression extension - (".gz" or ".Z"). Return the output filename.""" + 'base_dir'. 'compress' must be "gzip" (the default), "compress", + "bzip2", or None. Both "tar" and the compression utility named by + 'compress' must be on the default program search path, so this is + probably Unix-specific. The output tar file will be named 'base_dir' + + ".tar", possibly plus the appropriate compression extension (".gz", + ".bz2" or ".Z"). Return the output filename.""" # XXX GNU tar 1.13 has a nifty option to add a prefix directory. # It's pretty new, though, so we certainly can't require it -- @@ -29,9 +29,15 @@ def make_tarball (base_name, base_dir, compress="gzip", # detect GNU tar to use its 'z' option and save a step.) compress_ext = { 'gzip': ".gz", + 'bzip2': '.bz2', 'compress': ".Z" } + + # flags for compression program, each element of list will be an argument + compress_flags = {'gzip': ["-f9"], + 'compress': ["-f"], + 'bzip2': ['-f9']} - if compress is not None and compress not in ('gzip', 'compress'): + if compress is not None and compress not in compress_ext.keys(): raise ValueError, \ "bad value for 'compress': must be None, 'gzip', or 'compress'" @@ -40,7 +46,8 @@ def make_tarball (base_name, base_dir, compress="gzip", spawn (cmd, verbose=verbose, dry_run=dry_run) if compress: - spawn ([compress, archive_name], verbose=verbose, dry_run=dry_run) + spawn ([compress] + compress_flags[compress] + [archive_name], + verbose=verbose, dry_run=dry_run) return archive_name + compress_ext[compress] else: return archive_name @@ -104,6 +111,7 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): ARCHIVE_FORMATS = { 'gztar': (make_tarball, [('compress', 'gzip')]), + 'bztar': (make_tarball, [('compress', 'bzip2')]), 'ztar': (make_tarball, [('compress', 'compress')]), 'tar': (make_tarball, [('compress', None)]), 'zip': (make_zipfile, []) diff --git a/command/bdist.py b/command/bdist.py index 685f5250..cde8dd63 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -18,7 +18,8 @@ class bdist (Command): description = "create a built (binary) distribution" user_options = [('format=', 'f', - "format for distribution (tar, ztar, gztar, zip, ... )"), + "format for distribution " + + "(tar, ztar, gztar, bztar, zip, ... )"), ] # This won't do in reality: will need to distinguish RPM-ish Linux, @@ -27,6 +28,7 @@ class bdist (Command): 'nt': 'zip', } format_command = { 'gztar': 'bdist_dumb', + 'bztar': 'bdist_dumb', 'ztar': 'bdist_dumb', 'tar': 'bdist_dumb', 'zip': 'bdist_dumb', } diff --git a/command/sdist.py b/command/sdist.py index 8aa4618e..8d9a4650 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -33,9 +33,8 @@ class sdist (Command): "just regenerate the manifest and then stop"), ('force-manifest', None, "forcibly regenerate the manifest and carry on as usual"), - ('formats=', None, - "formats for source distribution (tar, ztar, gztar, or zip)"), + "formats for source distribution (tar, ztar, gztar, bztar, or zip)"), ('keep-tree', 'k', "keep the distribution tree around after creating " + "archive file(s)"), -- cgit v1.2.1 From 9bf82f12921aae17ef321f30eb613aa3678ca513 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 25 Apr 2000 01:55:58 +0000 Subject: Bumped version to 0.8.2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 5b8a6628..32fc276c 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.8.1" +__version__ = "0.8.2" -- cgit v1.2.1 From 622804da554351a32c6a379badb7520673a38060 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 26 Apr 2000 01:12:40 +0000 Subject: Harry Henry Gebel: Fix 'sdist.write_manifest()' to respect the value of dry_run. --- command/sdist.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 8d9a4650..67917188 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,7 +11,8 @@ import fnmatch from types import * from glob import glob from distutils.core import Command -from distutils.util import newer, create_tree, remove_tree, native_path +from distutils.util import newer, create_tree, remove_tree, native_path, \ + write_file from distutils.archive_util import check_archive_formats from distutils.text_file import TextFile from distutils.errors import DistutilsExecError, DistutilsOptionError @@ -447,10 +448,9 @@ class sdist (Command): by 'find_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'.""" - manifest = open (self.manifest, "w") - for fn in self.files: - manifest.write (fn + '\n') - manifest.close () + self.execute(write_file, + (self.manifest, self.files), + "writing manifest file") # write_manifest () -- cgit v1.2.1 From ed0fd8fbc0c9d218ea3c1dfd6d64936c790080d9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 26 Apr 2000 01:14:33 +0000 Subject: Supply short form for --manifest-only (-o) and --force-manifest (-f) options. --- command/sdist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 67917188..f644e9fb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -30,9 +30,9 @@ class sdist (Command): ('use-defaults', None, "include the default file set in the manifest " "[default; disable with --no-defaults]"), - ('manifest-only', None, + ('manifest-only', 'o', "just regenerate the manifest and then stop"), - ('force-manifest', None, + ('force-manifest', 'f', "forcibly regenerate the manifest and carry on as usual"), ('formats=', None, "formats for source distribution (tar, ztar, gztar, bztar, or zip)"), -- cgit v1.2.1 From 36f1d229a8213decc8b304e83dc49b81739b8e4d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 26 Apr 2000 02:26:55 +0000 Subject: Harry Henry Gebel: add 'long_description' to DistributionMetadata. --- dist.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 4a83c794..ae95009f 100644 --- a/dist.py +++ b/dist.py @@ -89,6 +89,8 @@ class Distribution: "alias for --licence"), ('description', None, "print the package description"), + ('long-description', None, + "print the long package description"), ] display_option_names = map(lambda x: string.translate(x[0], longopt_xlate), display_options) @@ -638,6 +640,7 @@ class DistributionMetadata: self.url = None self.licence = None self.description = None + self.long_description = None # -- Metadata query methods ---------------------------------------- @@ -680,7 +683,10 @@ class DistributionMetadata: def get_description(self): return self.description or "UNKNOWN" - + + def get_long_description(self): + return self.long_description or "UNKNOWN" + # class DistributionMetadata if __name__ == "__main__": -- cgit v1.2.1 From c5f056b71b4498a249e5af284d31ea665ebd2a52 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 26 Apr 2000 02:29:51 +0000 Subject: Harry Henry Gebel: import exception classes. --- command/bdist_dumb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 0a68d00a..23672a63 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -11,7 +11,7 @@ __revision__ = "$Id$" import os from distutils.core import Command from distutils.util import get_platform, create_tree, remove_tree - +from distutils.errors import * class bdist_dumb (Command): -- cgit v1.2.1 From 48f223a6efaadc98c4e9dd36f41f5f769a0db99d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 26 Apr 2000 02:38:01 +0000 Subject: Hacked things up a bit so that configuration variables are expanded in command-line options, and in two phases at that: first, we expand 'install_base' and 'install_platbase', and then the other 'install_*' options. This lets us do tricky stuff like install --prefix='/tmp$sys_prefix' ...oooh, neat. Simplified 'select_scheme()' -- it's no longer responsible for expanding config vars, tildes, etc. Define installation-specific config vars in 'self.config_vars', rather than in a local dictionary of one method. Also factored '_expand_attrs()' out of 'expand_dirs()' and added 'expand_basedirs()'. Added a bunch of debugging output so I (and others) can judge the success of this crazy scheme through direct feedback. --- command/install.py | 79 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/command/install.py b/command/install.py index 995fd875..98b08cd7 100644 --- a/command/install.py +++ b/command/install.py @@ -9,6 +9,7 @@ __revision__ = "$Id$" import sys, os, string from types import * from distutils.core import Command +from distutils import sysconfig from distutils.util import write_file, native_path, subst_vars from distutils.errors import DistutilsOptionError @@ -182,15 +183,47 @@ class install (Command): # install_{purelib,platlib,lib,scripts,data,...}, and the # INSTALL_SCHEME dictionary above. Phew! + from pprint import pprint + print "pre-finalize:" + pprint (self.__dict__) + if os.name == 'posix': self.finalize_unix () else: self.finalize_other () + print "post-finalize:" + pprint (self.__dict__) + + # Expand configuration variables, tilde, etc. in self.install_base + # and self.install_platbase -- that way, we can use $base or + # $platbase in the other installation directories and not worry + # about needing recursive variable expansion (shudder). + + self.config_vars = {'py_version_short': sys.version[0:3], + 'sys_prefix': sysconfig.PREFIX, + 'sys_exec_prefix': sysconfig.EXEC_PREFIX, + } + self.expand_basedirs () + + print "post-expand_basedirs:" + pprint (self.__dict__) + + # Now define config vars for the base directories so we can expand + # everything else. + self.config_vars['base'] = self.install_base + self.config_vars['platbase'] = self.install_platbase + + print "config vars:" + pprint (self.config_vars) + # Expand "~" and configuration variables in the installation # directories. self.expand_dirs () + print "post-expand:" + pprint (self.__dict__) + # Pick the actual directory to install all modules to: either # install_purelib or install_platlib, depending on whether this # module distribution is pure or not. Of course, if the user @@ -288,40 +321,32 @@ class install (Command): def select_scheme (self, name): - - # "select a scheme" means: - # - set install-base and install-platbase - # - subst. base/platbase/version into the values of the - # particular scheme dictionary - # - use the resultings strings to set install-lib, etc. - # it's the caller's problem if they supply a bad name! scheme = INSTALL_SCHEMES[name] - - vars = { 'base': self.install_base, - 'platbase': self.install_platbase, - 'py_version_short': sys.version[0:3], - } - for key in ('purelib', 'platlib', 'scripts', 'data'): - val = subst_vars (scheme[key], vars) - setattr (self, 'install_' + key, val) + setattr (self, 'install_' + key, scheme[key]) - def expand_dirs (self): + def _expand_attrs (self, attrs): + for attr in attrs: + val = getattr (self, attr) + if val is not None: + if os.name == 'posix': + val = os.path.expanduser (val) + val = subst_vars (val, self.config_vars) + setattr (self, attr, val) - # XXX probably don't want to 'expanduser()' on Windows or Mac - # XXX should use 'util.subst_vars()' with our own set of - # configuration variables - for att in ('base', 'platbase', - 'purelib', 'platlib', 'lib', - 'scripts', 'data'): - fullname = "install_" + att - val = getattr (self, fullname) - if val is not None: - setattr (self, fullname, - os.path.expandvars (os.path.expanduser (val))) + def expand_basedirs (self): + self._expand_attrs (['install_base', + 'install_platbase']) + + def expand_dirs (self): + self._expand_attrs (['install_purelib', + 'install_platlib', + 'install_lib', + 'install_scripts', + 'install_data',]) def handle_extra_path (self): -- cgit v1.2.1 From 6becff488178f722e192b09ba308afb6417ec772 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Apr 2000 01:53:46 +0000 Subject: Added 'change_root()' to forcibly slap a new root directory onto a pathname, even if it's already absolute. Currently only implemented for Unix; I'm not entirely sure of the right thing to do for DOS/Windows, and have no clue what to do for Mac OS. --- util.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/util.py b/util.py index ca20ae04..8e642e16 100644 --- a/util.py +++ b/util.py @@ -85,6 +85,32 @@ def native_path (pathname): # native_path () +def change_root (new_root, pathname): + + """Return 'pathname' with 'new_root' prepended. If 'pathname' is + relative, this is equivalent to "os.path.join(new_root,pathname)". + Otherwise, it requires making 'pathname' relative and then joining the + two, which is tricky on DOS/Windows and Mac OS.""" + + if not abspath (pathname): + return os.path.join (new_root, pathname) + + elif os.name == 'posix': + return os.path.join (new_root, pathname[1:]) + + elif os.name == 'nt': + (root_drive, root_path) = os.path.splitdrive (new_root) + (drive, path) = os.path.splitdrive (pathname) + raise RuntimeError, "I give up -- not sure how to do this on Windows" + + elif os.name == 'mac': + raise RuntimeError, "no clue how to do this on Mac OS" + + else: + raise DistutilsPlatformError, \ + "nothing known about platform '%s'" % os.name + + def _check_environ (): """Ensure that 'os.environ' has all the environment variables we guarantee that users can use in config files, command-line -- cgit v1.2.1 From edd515dffffa31cf704c25c1bd4138ba4f6c85d1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Apr 2000 01:56:38 +0000 Subject: Added the "--root" option as a sort of meta-install-base; if supplied, it is forcibly prepended onto all installation directories, even if they are already absolute. Added 'dump_dirs()' to clean up the debug output a bit. --- command/install.py | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/command/install.py b/command/install.py index 98b08cd7..c70ed9a1 100644 --- a/command/install.py +++ b/command/install.py @@ -10,7 +10,7 @@ import sys, os, string from types import * from distutils.core import Command from distutils import sysconfig -from distutils.util import write_file, native_path, subst_vars +from distutils.util import write_file, native_path, subst_vars, change_root from distutils.errors import DistutilsOptionError INSTALL_SCHEMES = { @@ -60,6 +60,8 @@ class install (Command): ('install-platbase=', None, "base installation directory for platform-specific files " + "(instead of --exec-prefix or --home)"), + ('root=', None, + "install everything relative to this alternate root directory"), # Or, explicitly set the installation scheme ('install-purelib=', None, @@ -104,6 +106,7 @@ class install (Command): # the --install-{platlib,purelib,scripts,data} options). self.install_base = None self.install_platbase = None + self.root = None # These options are the actual installation directories; if not # supplied by the user, they are filled in using the installation @@ -183,17 +186,14 @@ class install (Command): # install_{purelib,platlib,lib,scripts,data,...}, and the # INSTALL_SCHEME dictionary above. Phew! - from pprint import pprint - print "pre-finalize:" - pprint (self.__dict__) + self.dump_dirs ("pre-finalize_xxx") if os.name == 'posix': self.finalize_unix () else: self.finalize_other () - print "post-finalize:" - pprint (self.__dict__) + self.dump_dirs ("post-finalize_xxx()") # Expand configuration variables, tilde, etc. in self.install_base # and self.install_platbase -- that way, we can use $base or @@ -206,14 +206,14 @@ class install (Command): } self.expand_basedirs () - print "post-expand_basedirs:" - pprint (self.__dict__) + self.dump_dirs ("post-expand_basedirs()") # Now define config vars for the base directories so we can expand # everything else. self.config_vars['base'] = self.install_base self.config_vars['platbase'] = self.install_platbase + from pprint import pprint print "config vars:" pprint (self.config_vars) @@ -221,8 +221,7 @@ class install (Command): # directories. self.expand_dirs () - print "post-expand:" - pprint (self.__dict__) + self.dump_dirs ("post-expand_dirs()") # Pick the actual directory to install all modules to: either # install_purelib or install_platlib, depending on whether this @@ -242,6 +241,16 @@ class install (Command): self.install_libbase = self.install_lib # needed for .pth file self.install_lib = os.path.join (self.install_lib, self.extra_dirs) + # If a new root directory was supplied, make all the installation + # dirs relative to it. + if self.root is not None: + for name in ('lib', 'purelib', 'platlib', 'scripts', 'data'): + attr = "install_" + name + new_val = change_root (self.root, getattr (self, attr)) + setattr (self, attr, new_val) + + self.dump_dirs ("after prepending root") + # Find out the build directories, ie. where to install from. self.set_undefined_options ('build', ('build_base', 'build_base'), @@ -253,6 +262,16 @@ class install (Command): # finalize_options () + # hack for debugging output + def dump_dirs (self, msg): + from distutils.fancy_getopt import longopt_xlate + print msg + ":" + for opt in self.user_options: + opt_name = string.translate (opt[0][0:-1], longopt_xlate) + val = getattr (self, opt_name) + print " %s: %s" % (opt_name, val) + + def finalize_unix (self): if self.install_base is not None or self.install_platbase is not None: @@ -339,7 +358,8 @@ class install (Command): def expand_basedirs (self): self._expand_attrs (['install_base', - 'install_platbase']) + 'install_platbase', + 'root']) def expand_dirs (self): self._expand_attrs (['install_purelib', -- cgit v1.2.1 From 671dda81fc33e545e873e0e5c3b8d81db93a2deb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 6 May 2000 13:12:59 +0000 Subject: Added the ability to sneak extra flags onto the C compiler command line via an 'extra_compile_args' option in the 'build_info' dictionary. --- command/build_ext.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 422b8cae..a430c2b3 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -289,11 +289,13 @@ class build_ext (Command): # precedent!) macros = build_info.get ('macros') include_dirs = build_info.get ('include_dirs') + extra_args = build_info.get ('extra_compile_args') objects = self.compiler.compile (sources, output_dir=self.build_temp, macros=macros, include_dirs=include_dirs, - debug=self.debug) + debug=self.debug, + extra_postargs=extra_args) # Now link the object files together into a "shared object" -- # of course, first we have to figure out all the other things -- cgit v1.2.1 From d5ac3b7d4bbd40e5f118c04f768a4ece54b43d55 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 7 May 2000 15:29:15 +0000 Subject: Got rid of some little-used and not-very-useful methods: 'get_option()' and 'get_options()'. --- cmd.py | 47 +++-------------------------------------------- 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/cmd.py b/cmd.py index d8e41379..abb23c9f 100644 --- a/cmd.py +++ b/cmd.py @@ -157,47 +157,6 @@ class Command: print msg - # -- Option query/set methods -------------------------------------- - - def get_option (self, option): - """Return the value of a single option for this command. Raise - AttributeError if 'option' is not known.""" - return getattr (self, option) - - - def get_options (self, *options): - """Return (as a tuple) the values of several options for this - command. Raise AttributeError if any of the options in - 'options' are not known.""" - - values = [] - for opt in options: - values.append (getattr (self, opt)) - - return tuple (values) - - - def set_option (self, option, value): - """Set the value of a single option for this command. Raise - AttributeError if 'option' is not known.""" - - if not hasattr (self, option): - raise AttributeError, \ - "command '%s': no such option '%s'" % \ - (self.get_command_name(), option) - if value is not None: - setattr (self, option, value) - - def set_options (self, **optval): - """Set the values of several options for this command. Raise - AttributeError if any of the options specified as - keyword arguments are not known.""" - - for k in optval.keys(): - if optval[k] is not None: - self.set_option (k, optval[k]) - - # -- Convenience methods for commands ------------------------------ def get_command_name (self): @@ -228,8 +187,8 @@ class Command: src_cmd_obj.ensure_ready () for (src_option, dst_option) in option_pairs: if getattr (self, dst_option) is None: - self.set_option (dst_option, - src_cmd_obj.get_option (src_option)) + setattr (self, dst_option, + getattr (src_cmd_obj, src_option)) def find_peer (self, command, create=1): @@ -247,7 +206,7 @@ class Command: its 'option' option.""" cmd_obj = self.find_peer (command) - return cmd_obj.get_option (option) + return getattr(cmd_obj, option) def run_peer (self, command): -- cgit v1.2.1 From f1d400d10a1f601cc70a56c403a039bc3e9661b1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 7 May 2000 15:30:31 +0000 Subject: Got rid of several little-used and not-very-useful methods: 'get_option()', 'get_options()', 'get_command_option()', 'get_command_options()'. --- dist.py | 49 ++----------------------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) diff --git a/dist.py b/dist.py index ae95009f..f1ac35e4 100644 --- a/dist.py +++ b/dist.py @@ -261,8 +261,8 @@ class Distribution: raise DistutilsArgError, msg # Require that the command class be derived from Command -- - # that way, we can be sure that we at least have the 'run' - # and 'get_option' methods. + # want to be sure that the basic "command" interface is + # implemented. if not isinstance (cmd_obj, Command): raise DistutilsClassError, \ "command class %s must subclass Command" % \ @@ -529,24 +529,6 @@ class Distribution: self.run_command (cmd) - def get_option (self, option): - """Return the value of a distribution option. Raise - AttributeError if 'option' is not known.""" - return getattr (self, opt) - - - def get_options (self, *options): - """Return (as a tuple) the values of several distribution - options. Raise AttributeError if any element of - 'options' is not known.""" - - values = [] - for opt in options: - values.append (getattr (self, opt)) - - return tuple (values) - - # -- Methods that operate on its Commands -------------------------- def run_command (self, command): @@ -570,33 +552,6 @@ class Distribution: self.have_run[command] = 1 - def get_command_option (self, command, option): - """Create a command object for 'command' if necessary, ensure that - its option values are all set to their final values, and return - the value of its 'option' option. Raise AttributeError if - 'option' is not known for that 'command'.""" - - cmd_obj = self.find_command_obj (command) - cmd_obj.ensure_ready () - return cmd_obj.get_option (option) - - - def get_command_options (self, command, *options): - """Create a command object for 'command' if necessary, ensure that - its option values are all set to their final values, and return - a tuple containing the values of all the options listed in - 'options' for that command. Raise DistutilsOptionError if any - invalid option is supplied in 'options'.""" - - cmd_obj = self.find_command_obj (command) - cmd_obj.ensure_ready () - values = [] - for opt in options: - values.append (getattr (cmd_obj, option)) - - return tuple (values) - - # -- Distribution query methods ------------------------------------ def has_pure_modules (self): -- cgit v1.2.1 From f60583c2d16f6fce44276326164967fcb0ccd965 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 7 May 2000 15:32:13 +0000 Subject: Don't use 'set_option()' or 'get_option()' method -- direct attribute access, or getattr/setattr, is all that's needed. --- command/bdist.py | 2 +- command/install_lib.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index cde8dd63..01b4e162 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -64,7 +64,7 @@ class bdist (Command): "invalid archive format '%s'" % self.format sub_cmd = self.find_peer (cmd_name) - sub_cmd.set_option ('format', self.format) + sub_cmd.format = self.format self.run_peer (cmd_name) # run() diff --git a/command/install_lib.py b/command/install_lib.py index 5740c5ee..852e3f63 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -79,7 +79,7 @@ class install_lib (Command): build_cmd = self.find_peer (build_cmd) build_files = build_cmd.get_outputs() - build_dir = build_cmd.get_option (cmd_option) + build_dir = getattr (build_cmd, cmd_option) prefix_len = len (build_dir) + len (os.sep) outputs = [] -- cgit v1.2.1 From 6ef49c947019b2eb196a1e735e0e231ad60cc8f6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 9 May 2000 01:50:41 +0000 Subject: Added comment about the MSVC-specific kludge. --- command/build_ext.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index a430c2b3..b234b919 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -308,6 +308,16 @@ class build_ext (Command): rpath = build_info.get ('rpath') extra_args = build_info.get ('extra_link_args') or [] + # XXX this is a kludge! Knowledge of specific compilers or + # platforms really doesn't belong here; in an ideal world, the + # CCompiler interface would provide access to everything in a + # compiler/linker system needs to build Python extensions, and + # we would just do everything nicely and cleanly through that + # interface. However, this is a not an ideal world and the + # CCompiler interface doesn't handle absolutely everything. + # Thus, kludges like this slip in occasionally. (This is no + # excuse for committing more platform- and compiler-specific + # kludges; they are to be avoided if possible!) if self.compiler.compiler_type == 'msvc': def_file = build_info.get ('def_file') if def_file is None: -- cgit v1.2.1 From 46983102defd6428d66f50100e05d7746325b419 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:33:14 +0000 Subject: Fix from Lyle Johnson: add the '--compiler' option. --- command/build.py | 5 ++++- command/build_clib.py | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/command/build.py b/command/build.py index 7fb0b566..7d753b11 100644 --- a/command/build.py +++ b/command/build.py @@ -26,10 +26,12 @@ class build (Command): "build-purelib or build-platlib"), ('build-temp=', 't', "temporary build directory"), + ('compiler=', 'c', + "specify the compiler type"), ('debug', 'g', "compile extensions and libraries with debugging information"), ('force', 'f', - "forcibly build everything (ignore file timestamps"), + "forcibly build everything (ignore file timestamps)"), ] def initialize_options (self): @@ -40,6 +42,7 @@ class build (Command): self.build_platlib = None self.build_lib = None self.build_temp = None + self.compiler = None self.debug = None self.force = 0 diff --git a/command/build_clib.py b/command/build_clib.py index 31170730..681cd76b 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -38,7 +38,9 @@ class build_clib (Command): ('debug', 'g', "compile with debugging information"), ('force', 'f', - "forcibly build everything (ignore file timestamps"), + "forcibly build everything (ignore file timestamps)"), + ('compiler=', 'c', + "specify the compiler type"), ] def initialize_options (self): @@ -54,6 +56,7 @@ class build_clib (Command): self.undef = None self.debug = None self.force = 0 + self.compiler = None # initialize_options() @@ -68,6 +71,7 @@ class build_clib (Command): self.set_undefined_options ('build', ('build_temp', 'build_clib'), ('build_temp', 'build_temp'), + ('compiler', 'compiler'), ('debug', 'debug'), ('force', 'force')) @@ -93,9 +97,11 @@ class build_clib (Command): return # Yech -- this is cut 'n pasted from build_ext.py! - self.compiler = new_compiler (verbose=self.verbose, + self.compiler = new_compiler (compiler=self.compiler, + verbose=self.verbose, dry_run=self.dry_run, force=self.force) + if self.include_dirs is not None: self.compiler.set_include_dirs (self.include_dirs) if self.define is not None: -- cgit v1.2.1 From 7a4246d402981cf4382d566232f9f7100ed00572 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:34:12 +0000 Subject: Fix from Lyle Johnson: add the '--compiler' option. Also added creation of 'implib_dir', a temporary directory specific to MSVC++ -- but I checked in two ways of fixing it (Lyle's and mine), because I'm not sure which is right. --- command/build_ext.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index b234b919..2142d05f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -67,7 +67,9 @@ class build_ext (Command): ('debug', 'g', "compile/link with debugging information"), ('force', 'f', - "forcibly build everything (ignore file timestamps"), + "forcibly build everything (ignore file timestamps)"), + ('compiler=', 'c', + "specify the compiler type"), ] @@ -87,6 +89,7 @@ class build_ext (Command): self.link_objects = None self.debug = None self.force = None + self.compiler = None def finalize_options (self): @@ -95,6 +98,7 @@ class build_ext (Command): self.set_undefined_options ('build', ('build_lib', 'build_lib'), ('build_temp', 'build_temp'), + ('compiler', 'compiler'), ('debug', 'debug'), ('force', 'force')) @@ -169,7 +173,8 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler (verbose=self.verbose, + self.compiler = new_compiler (compiler=self.compiler, + verbose=self.verbose, dry_run=self.dry_run, force=self.force) @@ -340,6 +345,10 @@ class build_ext (Command): implib_dir = os.path.join(self.build_temp,\ self.get_ext_libname(extension_name)) extra_args.append ('/IMPLIB:' + implib_dir) + + # XXX arg, which of these is correct? + self.mkpath(implib_dir) + self.mkpath(os.path.dirname(implib_dir)) # if MSVC fullname = self.get_ext_fullname (extension_name) -- cgit v1.2.1 From 61b34f69cabd9929f8c6c7d3d7141acec8fb817c Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:40:00 +0000 Subject: Made 'check_environ()' "public" by stripping the leading underscore; added a global '_environ_checked' so we know if it's already been called. --- util.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 8e642e16..b660b4bf 100644 --- a/util.py +++ b/util.py @@ -111,7 +111,8 @@ def change_root (new_root, pathname): "nothing known about platform '%s'" % os.name -def _check_environ (): +_environ_checked = 0 +def check_environ (): """Ensure that 'os.environ' has all the environment variables we guarantee that users can use in config files, command-line options, etc. Currently this includes: @@ -120,6 +121,10 @@ def _check_environ (): and OS (see 'get_platform()') """ + global _environ_checked + if _environ_checked: + return + if os.name == 'posix' and not os.environ.has_key('HOME'): import pwd os.environ['HOME'] = pwd.getpwuid (os.getuid())[5] @@ -127,6 +132,8 @@ def _check_environ (): if not os.environ.has_key('PLAT'): os.environ['PLAT'] = get_platform () + _environ_checked = 1 + def subst_vars (str, local_vars): """Perform shell/Perl-style variable substitution on 'string'. @@ -138,7 +145,7 @@ def subst_vars (str, local_vars): '_check_environ()'. Raise ValueError for any variables not found in either 'local_vars' or 'os.environ'.""" - _check_environ () + check_environ () def _subst (match, local_vars=local_vars): var_name = match.group(1) if local_vars.has_key (var_name): -- cgit v1.2.1 From a5a461bdbf26a573b747000752c03ff9dbf58819 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:41:33 +0000 Subject: Preliminary support for config files: - added 'find_config_files()' and 'parse_config_files()' methods - added 'command_options' attribute Comment/docstring updates. --- dist.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 12 deletions(-) diff --git a/dist.py b/dist.py index f1ac35e4..a0c6c9ea 100644 --- a/dist.py +++ b/dist.py @@ -8,11 +8,12 @@ being built/installed/distributed.""" __revision__ = "$Id$" -import sys, string, re +import sys, os, string, re from types import * from copy import copy from distutils.errors import * from distutils.fancy_getopt import FancyGetopt, longopt_xlate +from distutils.util import check_environ # Regex to define acceptable Distutils command names. This is not *quite* @@ -137,6 +138,11 @@ class Distribution: # for the setup script to override command classes self.cmdclass = {} + # Store options for commands here between parsing them (from config + # files, the command-line, etc.) and actually putting them into the + # command object that needs them. + self.command_options = {} + # These options are really the business of various commands, rather # than of the Distribution itself. We provide aliases for them in # Distribution as a convenience to the developer. @@ -201,6 +207,74 @@ class Distribution: # __init__ () + def find_config_files (self): + """Find as many configuration files as should be processed for this + platform, and return a list of filenames in the order in which they + should be parsed. The filenames returned are guaranteed to exist + (modulo nasty race conditions). + + On Unix, there are three possible config files: pydistutils.cfg in + the Distutils installation directory (ie. where the top-level + Distutils __inst__.py file lives), .pydistutils.cfg in the user's + home directory, and setup.cfg in the current directory. + + On Windows and Mac OS, there are two possible config files: + pydistutils.cfg in the Python installation directory (sys.prefix) + and setup.cfg in the current directory.""" + + files = [] + if os.name == "posix": + check_environ() + + sys_dir = os.path.dirname(sys.modules['distutils'].__file__) + sys_file = os.path.join(sys_dir, "pydistutils.cfg") + if os.path.isfile(sys_file): + files.append(sys_file) + + user_file = os.path.join(os.environ.get('HOME'), + ".pydistutils.cfg") + if os.path.isfile(user_file): + files.append(user_file) + + else: + sys_file = os.path.join (sysconfig.PREFIX, "pydistutils.cfg") + if os.path.isfile(sys_file): + files.append(sys_file) + + # All platforms support local setup.cfg + local_file = "setup.cfg" + if os.path.isfile(local_file): + files.append(local_file) + + return files + + # find_config_files () + + + def parse_config_files (self, filenames=None): + + from ConfigParser import ConfigParser + + if filenames is None: + filenames = self.find_config_files() + + parser = ConfigParser() + parser.read(filenames) + for section in parser.sections(): + options = parser.options(section) + if not self.command_options.has_key(section) is None: + self.command_options[section] = {} + cmd_opts = self.command_options[section] + + for opt in options: + if opt != '__name__': + cmd_opts[opt] = parser.get(section,opt) + + from pprint import pprint + print "configuration options:" + pprint (self.command_options) + + def parse_command_line (self, args): """Parse the setup script's command line: set any Distribution attributes tied to command-line options, create all command @@ -436,18 +510,14 @@ class Distribution: # -- Command class/object methods ---------------------------------- - # This is a method just so it can be overridden if desired; it doesn't - # actually use or change any attributes of the Distribution instance. def find_command_class (self, command): - """Given a command, derives the names of the module and class - expected to implement the command: eg. 'foo_bar' becomes - 'distutils.command.foo_bar' (the module) and 'FooBar' (the - class within that module). Loads the module, extracts the - class from it, and returns the class object. - - Raises DistutilsModuleError with a semi-user-targeted error - message if the expected module could not be loaded, or the - expected class was not found in it.""" + """Given a command name, attempts to load the module and class that + implements that command. This is done by importing a module + "distutils.command." + command, and a class named 'command' in that + module. + + Raises DistutilsModuleError if the expected module could not be + found, or if that module does not define the expected class.""" module_name = 'distutils.command.' + command klass_name = command -- cgit v1.2.1 From a228f136965785d37f5d53b274e92a9fb73ba007 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:42:19 +0000 Subject: Call 'parse_config_files()' at the appropriate point. Tweaked error-generating code. --- core.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core.py b/core.py index 4b696928..a39bccd3 100644 --- a/core.py +++ b/core.py @@ -71,11 +71,11 @@ def setup (**attrs): # (ie. everything except distclass) to initialize it dist = klass (attrs) - # If we had a config file, this is where we would parse it: override - # the client-supplied command options, but be overridden by the - # command line. - - # Parse the command line; any command-line errors are the end-users + # Find and parse the config file(s): they will override options from + # the setup script, but be overridden by the command line. + dist.parse_config_files() + + # Parse the command line; any command-line errors are the end user's # fault, so turn them into SystemExit to suppress tracebacks. try: ok = dist.parse_command_line (sys.argv[1:]) @@ -101,10 +101,10 @@ def setup (**attrs): raise SystemExit, \ "error: %s" % exc.strerror else: - raise SystemExit, "error: " + exc[-1] + raise SystemExit, "error: " + str(exc[-1]) except (DistutilsExecError, DistutilsFileError, DistutilsOptionError), msg: - raise SystemExit, "error: " + str (msg) + raise SystemExit, "error: " + str(msg) # setup () -- cgit v1.2.1 From 316a284532e000c26f077afbbdfbd6640b077210 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:52:23 +0000 Subject: Patch from Bastien Kleineidam: adds the 'install_data' and 'install_scripts' commands; these two are trivial thanks to the 'install_misc' base class in cmd.py. (Minor tweaks and commentary by me; the code is untested so far.) --- cmd.py | 30 ++++++++++++++++++++++++++++++ command/__init__.py | 2 ++ command/install.py | 5 ++++- command/install_data.py | 14 ++++++++++++++ command/install_scripts.py | 16 ++++++++++++++++ dist.py | 2 ++ 6 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 command/install_data.py create mode 100644 command/install_scripts.py diff --git a/cmd.py b/cmd.py index abb23c9f..39373440 100644 --- a/cmd.py +++ b/cmd.py @@ -344,5 +344,35 @@ class Command: # class Command +class install_misc (Command): + """Common base class for installing some files in a subdirectory. + Currently used by install_data and install_scripts. + """ + + user_options = [('install-dir=', 'd', "directory to install the files to")] + + def initialize_options (self): + self.install_dir = None + self.outfiles = None + + def _install_dir_from(self, dirname): + self.set_undefined_options('install', (dirname, 'install_dir')) + + def _copydata(self, filelist): + self.outfiles = [] + if not filelist: + return + self.mkpath(self.install_dir) + for f in filelist: + self.outfiles.append(self.copy_file (f, self.install_dir)) + + def _outputdata(self, filelist): + if self.outfiles is not None: + return self.outfiles + # XXX de-lambda-fy + return map(lambda x: os.path.join(self.install_dir, x), filelist) + + + if __name__ == "__main__": print "ok" diff --git a/command/__init__.py b/command/__init__.py index 385330b5..573ae513 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -11,6 +11,8 @@ __all__ = ['build', 'build_clib', 'install', 'install_lib', + 'install_scripts', + 'install_data', 'clean', 'sdist', 'bdist', diff --git a/command/install.py b/command/install.py index c70ed9a1..3f6fa33f 100644 --- a/command/install.py +++ b/command/install.py @@ -90,7 +90,10 @@ class install (Command): # (func, command) where 'func' is a function to call that returns # true if 'command' (the sub-command name, a string) needs to be # run. If 'func' is None, assume that 'command' must always be run. - sub_commands = [(None, 'install_lib')] + sub_commands = [(None, 'install_lib'), + (None, 'install_scripts'), + (None, 'install_data'), + ] def initialize_options (self): diff --git a/command/install_data.py b/command/install_data.py new file mode 100644 index 00000000..f86d95e5 --- /dev/null +++ b/command/install_data.py @@ -0,0 +1,14 @@ +from distutils.cmd import install_misc + +class install_data (install_misc): + + description = "install data files" + + def finalize_options (self): + self._install_dir_from('install_data') + + def run (self): + self._copydata(self.distribution.data) + + def get_outputs (self): + return self._outputdata(self.distribution.data) diff --git a/command/install_scripts.py b/command/install_scripts.py new file mode 100644 index 00000000..665208eb --- /dev/null +++ b/command/install_scripts.py @@ -0,0 +1,16 @@ +from distutils.cmd import install_misc + +class install_scripts(install_misc): + + description = "install scripts" + # XXX needed? + user_options = [('install-dir=', 'd', "directory to install to")] + + def finalize_options (self): + self._install_dir_from('install_scripts') + + def run (self): + self._copydata(self.distribution.scripts) + + def get_outputs(self): + return self._outputdata(self.distribution.scripts) diff --git a/dist.py b/dist.py index a0c6c9ea..998cff71 100644 --- a/dist.py +++ b/dist.py @@ -154,6 +154,8 @@ class Distribution: self.ext_package = None self.include_dirs = None self.extra_path = None + self.scripts = None + self.data = None # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to -- cgit v1.2.1 From 593bfaaa0c09ea0939a54bdb050a2e91ab0e295e Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:31:37 +0000 Subject: In 'install_misc' class: - renamed '_copydata()' to 'copy_files()' - changed it to record complete output filenames - dropped '_outputdata()' in favour of much simpler 'get_outputs()' --- cmd.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/cmd.py b/cmd.py index 39373440..28d86179 100644 --- a/cmd.py +++ b/cmd.py @@ -8,7 +8,7 @@ in the distutils.command package.""" __revision__ = "$Id$" -import sys, string +import sys, os, string from types import * from distutils.errors import * from distutils import util @@ -355,23 +355,20 @@ class install_misc (Command): self.install_dir = None self.outfiles = None - def _install_dir_from(self, dirname): + def _install_dir_from (self, dirname): self.set_undefined_options('install', (dirname, 'install_dir')) - def _copydata(self, filelist): + def _copy_files (self, filelist): self.outfiles = [] if not filelist: return self.mkpath(self.install_dir) for f in filelist: - self.outfiles.append(self.copy_file (f, self.install_dir)) - - def _outputdata(self, filelist): - if self.outfiles is not None: - return self.outfiles - # XXX de-lambda-fy - return map(lambda x: os.path.join(self.install_dir, x), filelist) + self.copy_file(f, self.install_dir) + self.outfiles.append(os.path.join(self.install_dir, f)) + def get_outputs (self): + return self.outfiles if __name__ == "__main__": -- cgit v1.2.1 From a6ee82aab954f10c941860047e35bb26dcc2866b Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:32:30 +0000 Subject: Deleted some cruft. Caught up with renaming in 'install_misc' base class. Changed 'run()' to chmod installed scripts under Unix. --- command/install_scripts.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/command/install_scripts.py b/command/install_scripts.py index 665208eb..5c3f2fb4 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -1,16 +1,26 @@ +import os from distutils.cmd import install_misc +from stat import ST_MODE class install_scripts(install_misc): description = "install scripts" - # XXX needed? - user_options = [('install-dir=', 'd', "directory to install to")] def finalize_options (self): self._install_dir_from('install_scripts') def run (self): - self._copydata(self.distribution.scripts) + self._copy_files(self.distribution.scripts) + if os.name == 'posix': + # Set the executable bits (owner, group, and world) on + # all the scripts we just installed. + files = self.get_outputs() + for file in files: + if self.dry_run: + self.announce("changing mode of %s" % file) + else: + mode = (os.stat(file)[ST_MODE]) | 0111 + self.announce("changing mode of %s to %o" % (file, mode)) + os.chmod(file, mode) - def get_outputs(self): - return self._outputdata(self.distribution.scripts) +# class install_scripts -- cgit v1.2.1 From d5eb448fcce25d02a65b8ebaabbc119d21be5672 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:34:33 +0000 Subject: Caught up with renaming in 'install_misc' base class. --- command/install_data.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/command/install_data.py b/command/install_data.py index f86d95e5..d96a1def 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -8,7 +8,4 @@ class install_data (install_misc): self._install_dir_from('install_data') def run (self): - self._copydata(self.distribution.data) - - def get_outputs (self): - return self._outputdata(self.distribution.data) + self._copy_files(self.distribution.data) -- cgit v1.2.1 From 58a11985342e4e261381916e6b43210b58ae0908 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:46:47 +0000 Subject: Added --skip-build option, so lazy debuggers/testers (mainly me) don't have to wade through all the 'build' output when testing installation. --- command/install.py | 15 +++++++++++++-- command/install_lib.py | 15 ++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/command/install.py b/command/install.py index 3f6fa33f..4e68e00e 100644 --- a/command/install.py +++ b/command/install.py @@ -77,6 +77,11 @@ class install (Command): ('install-data=', None, "installation directory for data files"), + # For lazy debuggers who just want to test the install + # commands without rerunning "build" all the time + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + # Where to install documentation (eventually!) #('doc-format=', None, "format of documentation to generate"), #('install-man=', None, "directory for Unix man pages"), @@ -129,6 +134,8 @@ class install (Command): self.extra_path = None self.install_path_file = 0 + self.skip_build = 0 + # These are only here as a conduit from the 'build' command to the # 'install_*' commands that do the real work. ('build_base' isn't # actually used anywhere, but it might be useful in future.) They @@ -270,7 +277,10 @@ class install (Command): from distutils.fancy_getopt import longopt_xlate print msg + ":" for opt in self.user_options: - opt_name = string.translate (opt[0][0:-1], longopt_xlate) + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + opt_name = string.translate (opt_name, longopt_xlate) val = getattr (self, opt_name) print " %s: %s" % (opt_name, val) @@ -409,7 +419,8 @@ class install (Command): def run (self): # Obviously have to build before we can install - self.run_peer ('build') + if not self.skip_build: + self.run_peer ('build') # Run all sub-commands: currently this just means install all # Python modules using 'install_lib'. diff --git a/command/install_lib.py b/command/install_lib.py index 852e3f63..2d0a7190 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -15,6 +15,7 @@ class install_lib (Command): ('build-dir=','b', "build directory (where to install from)"), ('compile', 'c', "compile .py to .pyc"), ('optimize', 'o', "compile .py to .pyo (optimized)"), + ('skip-build', None, "skip the build steps"), ] @@ -24,6 +25,7 @@ class install_lib (Command): self.build_dir = None self.compile = 1 self.optimize = 1 + self.skip_build = None def finalize_options (self): @@ -34,16 +36,19 @@ class install_lib (Command): ('build_lib', 'build_dir'), ('install_lib', 'install_dir'), ('compile_py', 'compile'), - ('optimize_py', 'optimize')) + ('optimize_py', 'optimize'), + ('skip_build', 'skip_build'), + ) def run (self): # Make sure we have built everything we need first - if self.distribution.has_pure_modules(): - self.run_peer ('build_py') - if self.distribution.has_ext_modules(): - self.run_peer ('build_ext') + if not self.skip_build: + if self.distribution.has_pure_modules(): + self.run_peer ('build_py') + if self.distribution.has_ext_modules(): + self.run_peer ('build_ext') # Install everything: simply dump the entire contents of the build # directory to the installation directory (that's the beauty of -- cgit v1.2.1 From 5ff2cdf00f98ecaf7ed6140145e7253425171252 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:54:50 +0000 Subject: Fixed 'select_scheme()' so it doesn't override a directory attribute that's already been set (eg. by a command-line option). --- command/install.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 4e68e00e..ba4110cb 100644 --- a/command/install.py +++ b/command/install.py @@ -356,7 +356,9 @@ class install (Command): # it's the caller's problem if they supply a bad name! scheme = INSTALL_SCHEMES[name] for key in ('purelib', 'platlib', 'scripts', 'data'): - setattr (self, 'install_' + key, scheme[key]) + attrname = 'install_' + key + if getattr(self, attrname) is None: + setattr(self, attrname, scheme[key]) def _expand_attrs (self, attrs): -- cgit v1.2.1 From 23b8295f824f1520612add07320e9bf62bc71709 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:58:29 +0000 Subject: Added comment/docstring/revision header. --- command/install_data.py | 9 +++++++++ command/install_scripts.py | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/command/install_data.py b/command/install_data.py index d96a1def..95ef4231 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -1,3 +1,12 @@ +"""distutils.command.install_data + +Implements the Distutils 'install_data' command, for installing +platform-independent data files.""" + +# contributed by Bastian Kleineidam + +__revision__ = "$Id$" + from distutils.cmd import install_misc class install_data (install_misc): diff --git a/command/install_scripts.py b/command/install_scripts.py index 5c3f2fb4..4e23efc8 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -1,3 +1,12 @@ +"""distutils.command.install_scripts + +Implements the Distutils 'install_scripts' command, for installing +Python scripts.""" + +# contributed by Bastian Kleineidam + +__revision__ = "$Id$" + import os from distutils.cmd import install_misc from stat import ST_MODE -- cgit v1.2.1 From ea70d0b5ebf3197a83a0965f25125f27d0b05d88 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:35:33 +0000 Subject: From Lyle Johnson: renamed 'implib_dir' to 'implib_file', and correctly ensure that it's 'dirname' exists. --- command/build_ext.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 2142d05f..deb3b130 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -342,13 +342,11 @@ class build_ext (Command): # which cannot be suppressed by any linker switches. So # make sure they are generated in the temporary build # directory. - implib_dir = os.path.join(self.build_temp,\ - self.get_ext_libname(extension_name)) - extra_args.append ('/IMPLIB:' + implib_dir) - - # XXX arg, which of these is correct? - self.mkpath(implib_dir) - self.mkpath(os.path.dirname(implib_dir)) + implib_file = os.path.join ( + self.build_temp, + self.get_ext_libname (extension_name)) + extra_args.append ('/IMPLIB:' + implib_file) + self.mkpath (os.path.dirname (implib_file)) # if MSVC fullname = self.get_ext_fullname (extension_name) -- cgit v1.2.1 From 0ad597128936e67ed4025f170e46d66a61a61ce3 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:48:15 +0000 Subject: Harry Henry Gebel: add 'bdist_rpm' command. --- command/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/__init__.py b/command/__init__.py index 573ae513..cd7753fe 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -17,4 +17,5 @@ __all__ = ['build', 'sdist', 'bdist', 'bdist_dumb', + 'bdist_rpm', ] -- cgit v1.2.1 From 68504c5151605eb5da935805f7973fcb70e6f3aa Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:49:56 +0000 Subject: Harry Henry Gebel: add support for the 'bdist_rpm' command, specifically the 'no_format_option' class attribute. --- command/bdist.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 01b4e162..970779dc 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -22,6 +22,9 @@ class bdist (Command): "(tar, ztar, gztar, bztar, zip, ... )"), ] + # The following commands do not take a format option from bdist + no_format_option = ('bdist_rpm',) + # This won't do in reality: will need to distinguish RPM-ish Linux, # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. default_format = { 'posix': 'gztar', @@ -31,6 +34,7 @@ class bdist (Command): 'bztar': 'bdist_dumb', 'ztar': 'bdist_dumb', 'tar': 'bdist_dumb', + 'rpm': 'bdist_rpm', 'zip': 'bdist_dumb', } @@ -63,8 +67,9 @@ class bdist (Command): raise DistutilsOptionError, \ "invalid archive format '%s'" % self.format - sub_cmd = self.find_peer (cmd_name) - sub_cmd.format = self.format + if cmd_name not in self.no_format_option: + sub_cmd = self.find_peer (cmd_name) + sub_cmd.format = self.format self.run_peer (cmd_name) # run() -- cgit v1.2.1 From e1e8fd9c6397cccb920d93434f7b7393b361b564 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:52:14 +0000 Subject: Harry Henry Gebel: get extra compiler flags from the CFLAGS environment variable. (Is this really needed? Can we drop it when the config file mechanism allows users to set compiler flags in setup.cfg?) --- command/build_ext.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index deb3b130..aa9ea0d0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -295,6 +295,14 @@ class build_ext (Command): macros = build_info.get ('macros') include_dirs = build_info.get ('include_dirs') extra_args = build_info.get ('extra_compile_args') + # honor CFLAGS enviroment variable + # XXX do we *really* need this? or is it just a hack until + # the user can put compiler flags in setup.cfg? + if os.environ.has_key('CFLAGS'): + if not extra_args: + extra_args = [] + extra_args = string.split(os.environ['CFLAGS']) + extra_args + objects = self.compiler.compile (sources, output_dir=self.build_temp, macros=macros, -- cgit v1.2.1 From 25c47ab8b0225c1a451b36eb720cfe73954af9b1 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:56:55 +0000 Subject: Added the 'build_bdist' option and code to clean it up -- this is the top-level temporary directory for creating built distributions. (Won't work yet, since the "build" command doesn't yet have a 'build_bdist' option, and none of the "bdist" commands support it yet.) --- command/clean.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/command/clean.py b/command/clean.py index 4f0f7e3e..78166f26 100644 --- a/command/clean.py +++ b/command/clean.py @@ -20,6 +20,8 @@ class clean (Command): "build directory for all modules (default: 'build.build-lib')"), ('build-temp=', 't', "temporary build directory (default: 'build.build-temp')"), + ('build-bdist=', None, + "temporary directory for built distributions"), ('all', 'a', "remove all build output, not just temporary by-products") ] @@ -28,6 +30,7 @@ class clean (Command): self.build_base = None self.build_lib = None self.build_temp = None + self.build_bdist = None self.all = None def finalize_options(self): @@ -39,9 +42,10 @@ class clean (Command): self.build_temp) self.set_undefined_options('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib'), - ('build_temp', 'build_temp')) + ('build_base', 'build_base'), + ('build_lib', 'build_lib'), + ('build_temp', 'build_temp'), + ('build_bdist', 'build_bdist')) def run(self): # remove the build/temp. directory (unless it's already @@ -53,6 +57,13 @@ class clean (Command): # remove the module build directory (unless already gone) if os.path.exists (self.build_lib): remove_tree (self.build_lib, self.verbose, self.dry_run) + # remove the temporary directory used for creating built + # distributions (default "build/bdist") -- eg. type of + # built distribution will have its own subdirectory under + # "build/bdist", but they'll be taken care of by + # 'remove_tree()'. + if os.path.exists (self.build_bdist) + remove_tree (self.build_bdist, self.verbose, self.dry_run) # just for the heck of it, try to remove the base build directory: # we might have emptied it right now, but if not we don't care -- cgit v1.2.1 From 714539777115782361abaa82992217496a2d932e Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:58:19 +0000 Subject: Harry Henry Gebel: add the "--record" option to write the list of installed files to INSTALLED_FILES. --- command/install.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index ba4110cb..e6ed9841 100644 --- a/command/install.py +++ b/command/install.py @@ -12,6 +12,7 @@ from distutils.core import Command from distutils import sysconfig from distutils.util import write_file, native_path, subst_vars, change_root from distutils.errors import DistutilsOptionError +from glob import glob INSTALL_SCHEMES = { 'unix_prefix': { @@ -87,8 +88,10 @@ class install (Command): #('install-man=', None, "directory for Unix man pages"), #('install-html=', None, "directory for HTML documentation"), #('install-info=', None, "directory for GNU info files"), - ] + ('record', None, + "make a record of installation"), + ] # 'sub_commands': a list of commands this command might have to run # to get its work done. Each command is represented as a tuple @@ -151,6 +154,7 @@ class install (Command): #self.install_html = None #self.install_info = None + self.record = None def finalize_options (self): @@ -441,6 +445,22 @@ class install (Command): "you'll have to change the search path yourself") % self.install_lib) + # write list of installed files, if requested. + if self.record: + outputs = self.get_outputs() + for counter in xrange (len (outputs)): # include ".pyc" and ".pyo" + if outputs[counter][-3:] == ".py": + byte_code = glob(outputs[counter] + '[co]') + outputs.extend(byte_code) + outputs.sort() # just makes it look nicer + if self.root: # strip any package prefix + root_len = len(self.root) + for counter in xrange (len (outputs)): + outputs[counter] = outputs[counter][root_len:] + self.execute(write_file, + ("INSTALLED_FILES", outputs), + "Writing list of installed files") + # run () -- cgit v1.2.1 From 1c914c4988122ad3436aac8bb4e2ab833aef511f Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:01:22 +0000 Subject: Moved check for installation to non-sys.path location so it comes last (after writing list of installed files) -- that way, the warning is more visible. --- command/install.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/command/install.py b/command/install.py index e6ed9841..163b0187 100644 --- a/command/install.py +++ b/command/install.py @@ -437,14 +437,6 @@ class install (Command): if self.path_file: self.create_path_file () - normalized_path = map (os.path.normpath, sys.path) - if (not (self.path_file and self.install_path_file) and - os.path.normpath (self.install_lib) not in normalized_path): - self.warn (("modules installed to '%s', which is not in " + - "Python's module search path (sys.path) -- " + - "you'll have to change the search path yourself") % - self.install_lib) - # write list of installed files, if requested. if self.record: outputs = self.get_outputs() @@ -459,7 +451,15 @@ class install (Command): outputs[counter] = outputs[counter][root_len:] self.execute(write_file, ("INSTALLED_FILES", outputs), - "Writing list of installed files") + "writing list of installed files") + + normalized_path = map (os.path.normpath, sys.path) + if (not (self.path_file and self.install_path_file) and + os.path.normpath (self.install_lib) not in normalized_path): + self.warn (("modules installed to '%s', which is not in " + + "Python's module search path (sys.path) -- " + + "you'll have to change the search path yourself") % + self.install_lib) # run () -- cgit v1.2.1 From 6406c80d46402cc93bdfd1fa565acba9a7a33213 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:11:10 +0000 Subject: Added '_bytecode_filenames()' method, and use it in 'get_outputs()' to ensure that compiled bytecode files are considered part of the output of the "install_lib" command. --- command/install_lib.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index 2d0a7190..63c7a6bf 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -72,8 +72,6 @@ class install_lib (Command): skip_msg = "byte-compilation of %s skipped" % f self.make_file (f, out_fn, compile, (f,), compile_msg, skip_msg) - - # run () @@ -94,6 +92,14 @@ class install_lib (Command): return outputs # _mutate_outputs () + + def _bytecode_filenames (self, py_filenames): + bytecode_files = [] + for py_file in py_filenames: + bytecode = py_file + (__debug__ and "c" or "o") + bytecode_files.append(bytecode) + + return bytecode_files def get_outputs (self): """Return the list of files that would be installed if this command @@ -104,14 +110,17 @@ class install_lib (Command): self._mutate_outputs (self.distribution.has_pure_modules(), 'build_py', 'build_lib', self.install_dir) - + if self.compile: + bytecode_outputs = self._bytecode_filenames(pure_outputs) + else: + bytecode_outputs = [] ext_outputs = \ self._mutate_outputs (self.distribution.has_ext_modules(), 'build_ext', 'build_lib', self.install_dir) - return pure_outputs + ext_outputs + return pure_outputs + bytecode_outputs + ext_outputs # get_outputs () -- cgit v1.2.1 From b9143e831d39d846dd31c84f8e2252161355b346 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:13:53 +0000 Subject: Ditch the explicit search for *.py[co] files -- they're now included in the list returned by 'get_outputs()', thanks to changes in the "install_lib" command. --- command/install.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/command/install.py b/command/install.py index 163b0187..2429f1b1 100644 --- a/command/install.py +++ b/command/install.py @@ -440,12 +440,7 @@ class install (Command): # write list of installed files, if requested. if self.record: outputs = self.get_outputs() - for counter in xrange (len (outputs)): # include ".pyc" and ".pyo" - if outputs[counter][-3:] == ".py": - byte_code = glob(outputs[counter] + '[co]') - outputs.extend(byte_code) - outputs.sort() # just makes it look nicer - if self.root: # strip any package prefix + if self.root: # strip any package prefix root_len = len(self.root) for counter in xrange (len (outputs)): outputs[counter] = outputs[counter][root_len:] -- cgit v1.2.1 From 1f4bdba5ba8806643d7ff97ae965cb0edf1d99fe Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:16:45 +0000 Subject: Made the '--record' option take an argument, which is the name of the file to write the list of installed files to. --- command/install.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/command/install.py b/command/install.py index 2429f1b1..a4986e5d 100644 --- a/command/install.py +++ b/command/install.py @@ -89,8 +89,8 @@ class install (Command): #('install-html=', None, "directory for HTML documentation"), #('install-info=', None, "directory for GNU info files"), - ('record', None, - "make a record of installation"), + ('record=', None, + "filename in which to record list of installed files"), ] # 'sub_commands': a list of commands this command might have to run @@ -445,8 +445,9 @@ class install (Command): for counter in xrange (len (outputs)): outputs[counter] = outputs[counter][root_len:] self.execute(write_file, - ("INSTALLED_FILES", outputs), - "writing list of installed files") + (self.record, outputs), + "writing list of installed files to '%s'" % + self.record) normalized_path = map (os.path.normpath, sys.path) if (not (self.path_file and self.install_path_file) and -- cgit v1.2.1 From 2d34b0395476d92bd2fbec6f4fcd99cae6d2a383 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:20:43 +0000 Subject: Typo fix. --- command/clean.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/clean.py b/command/clean.py index 78166f26..8706e57a 100644 --- a/command/clean.py +++ b/command/clean.py @@ -62,7 +62,7 @@ class clean (Command): # built distribution will have its own subdirectory under # "build/bdist", but they'll be taken care of by # 'remove_tree()'. - if os.path.exists (self.build_bdist) + if os.path.exists (self.build_bdist): remove_tree (self.build_bdist, self.verbose, self.dry_run) # just for the heck of it, try to remove the base build directory: -- cgit v1.2.1 From b7d9b1267bfe9638d965a16556a7ca408f6e6ff2 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:30:15 +0000 Subject: Rename 'build_bdist' to 'bdist_base', and get it by default from the "bdist" command rather than "build". --- command/clean.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/command/clean.py b/command/clean.py index 8706e57a..d6999060 100644 --- a/command/clean.py +++ b/command/clean.py @@ -20,7 +20,7 @@ class clean (Command): "build directory for all modules (default: 'build.build-lib')"), ('build-temp=', 't', "temporary build directory (default: 'build.build-temp')"), - ('build-bdist=', None, + ('bdist-base=', None, "temporary directory for built distributions"), ('all', 'a', "remove all build output, not just temporary by-products") @@ -30,7 +30,7 @@ class clean (Command): self.build_base = None self.build_lib = None self.build_temp = None - self.build_bdist = None + self.bdist_base = None self.all = None def finalize_options(self): @@ -44,8 +44,9 @@ class clean (Command): self.set_undefined_options('build', ('build_base', 'build_base'), ('build_lib', 'build_lib'), - ('build_temp', 'build_temp'), - ('build_bdist', 'build_bdist')) + ('build_temp', 'build_temp')) + self.set_undefined_options('bdist', + ('bdist_base', 'bdist_base')) def run(self): # remove the build/temp. directory (unless it's already @@ -62,8 +63,8 @@ class clean (Command): # built distribution will have its own subdirectory under # "build/bdist", but they'll be taken care of by # 'remove_tree()'. - if os.path.exists (self.build_bdist): - remove_tree (self.build_bdist, self.verbose, self.dry_run) + if os.path.exists (self.bdist_base): + remove_tree (self.bdist_base, self.verbose, self.dry_run) # just for the heck of it, try to remove the base build directory: # we might have emptied it right now, but if not we don't care -- cgit v1.2.1 From 75835e5b552eacae52c565cb1f2172bc0c03405f Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:06:56 +0000 Subject: Drastically simplified by taking advantage of the "install" command's new flexibility, specifically the 'root' option. Now, we just use "install" to do a fake installation into a temporary directory (the 'bdist_dir' option, which derives from the 'bdist_base' option of "bdist"), and then tar/zip up that directory. This means that dumb built distributions are now relative to the root directory, rather than the prefix or exec-prefix; this is probably a feature, but does make them slightly less flexible. --- command/bdist_dumb.py | 95 ++++++++++++--------------------------------------- 1 file changed, 21 insertions(+), 74 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 23672a63..2de2befc 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -17,7 +17,9 @@ class bdist_dumb (Command): description = "create a \"dumb\" built distribution" - user_options = [('format=', 'f', + user_options = [('bdist-dir=', 'd', + "temporary directory for creating the distribution"), + ('format=', 'f', "archive format to create (tar, ztar, gztar, zip)"), ('keep-tree', 'k', "keep the pseudo-installation tree around after " + @@ -29,6 +31,7 @@ class bdist_dumb (Command): def initialize_options (self): + self.bdist_dir = None self.format = None self.keep_tree = 0 @@ -36,6 +39,10 @@ class bdist_dumb (Command): def finalize_options (self): + if self.bdist_dir is None: + bdist_base = self.get_peer_option('bdist', 'bdist_base') + self.bdist_dir = os.path.join(bdist_base, 'dumb') + if self.format is None: try: self.format = self.default_format[os.name] @@ -50,91 +57,31 @@ class bdist_dumb (Command): def run (self): self.run_peer ('build') - install = self.find_peer ('install') - inputs = install.get_inputs () - outputs = install.get_outputs () - assert (len (inputs) == len (outputs)) - - # First, strip the installation base directory (prefix or - # exec-prefix) from all the output filenames. - self.strip_base_dirs (outputs, install) - # Figure out where to copy them to: "build/bdist" by default; this - # directory masquerades as prefix/exec-prefix (ie. we'll make the - # archive from 'output_dir'). - build_base = self.get_peer_option ('build', 'build_base') - output_dir = os.path.join (build_base, "bdist") + # XXX don't use 'self.find_peer()', because it always runs + # 'ensure_ready()' on the command object; we explictly want a + # command object that has *not* been finalized, so we can set + # options on it! (The option we set, 'root', is so that we can do + # a proper "fake install" using this install command object.) + install = self.distribution.find_command_obj('install') + install.root = self.bdist_dir - # Copy the built files to the pseudo-installation tree. - self.make_install_tree (output_dir, inputs, outputs) + self.announce ("installing to %s" % self.bdist_dir) + install.ensure_ready() + install.run() # And make an archive relative to the root of the # pseudo-installation tree. archive_basename = "%s.%s" % (self.distribution.get_fullname(), get_platform()) - print "output_dir = %s" % output_dir + print "self.bdist_dir = %s" % self.bdist_dir print "self.format = %s" % self.format self.make_archive (archive_basename, self.format, - root_dir=output_dir) + root_dir=self.bdist_dir) if not self.keep_tree: - remove_tree (output_dir, self.verbose, self.dry_run) + remove_tree (self.bdist_dir, self.verbose, self.dry_run) # run() - - def strip_base_dirs (self, outputs, install_cmd): - # XXX this throws away the prefix/exec-prefix distinction, and - # means we can only correctly install the resulting archive on a - # system where prefix == exec-prefix (but at least we can *create* - # it on one where they differ). I don't see a way to fix this - # without either 1) generating two archives, one for prefix and one - # for exec-prefix, or 2) putting absolute paths in the archive - # rather than making them relative to one of the prefixes. - - base = install_cmd.install_base + os.sep - platbase = install_cmd.install_platbase + os.sep - b_len = len (base) - pb_len = len (platbase) - for i in range (len (outputs)): - if outputs[i][0:b_len] == base: - outputs[i] = outputs[i][b_len:] - elif outputs[i][0:pb_len] == platbase: - outputs[i] = outputs[i][pb_len:] - else: - raise DistutilsInternalError, \ - ("installation output filename '%s' doesn't start " + - "with either install_base ('%s') or " + - "install_platbase ('%s')") % \ - (outputs[i], base, platbase) - - # strip_base_dirs() - - - def make_install_tree (self, output_dir, inputs, outputs): - - assert (len(inputs) == len(outputs)) - - # Create all the directories under 'output_dir' necessary to - # put 'outputs' there. - create_tree (output_dir, outputs, - verbose=self.verbose, dry_run=self.dry_run) - - - # XXX this bit of logic is duplicated in sdist.make_release_tree(): - # would be nice to factor it out... - if hasattr (os, 'link'): # can make hard links on this system - link = 'hard' - msg = "making hard links in %s..." % output_dir - else: # nope, have to copy - link = None - msg = "copying files to %s..." % output_dir - - for i in range (len(inputs)): - output = os.path.join (output_dir, outputs[i]) - self.copy_file (inputs[i], output, link=link) - - # make_install_tree () - - # class bdist_dumb -- cgit v1.2.1 From 046f4ee3aab641fbb6c72931d116e1c58bb99511 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:07:53 +0000 Subject: Added 'get_inputs()' methods, needed by the "install" command's 'get_inputs()'. --- command/install_data.py | 3 +++ command/install_scripts.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/command/install_data.py b/command/install_data.py index 95ef4231..448614b0 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -18,3 +18,6 @@ class install_data (install_misc): def run (self): self._copy_files(self.distribution.data) + + def get_inputs (self): + return self.distribution.data or [] diff --git a/command/install_scripts.py b/command/install_scripts.py index 4e23efc8..43e5fc18 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -32,4 +32,7 @@ class install_scripts(install_misc): self.announce("changing mode of %s to %o" % (file, mode)) os.chmod(file, mode) + def get_inputs (self): + return self.distribution.scripts or [] + # class install_scripts -- cgit v1.2.1 From 783f98d5381de82ad91e71bb529bf001d0e69456 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:08:28 +0000 Subject: Added the 'bdist_base' option, the base temp directory for all bdist commands. --- command/bdist.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 970779dc..892712ec 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -11,13 +11,16 @@ import os, string from types import * from distutils.core import Command from distutils.errors import * +from distutils.util import get_platform class bdist (Command): description = "create a built (binary) distribution" - user_options = [('format=', 'f', + user_options = [('bdist-base=', 'b', + "temporary directory for creating built distributions"), + ('format=', 'f', "format for distribution " + "(tar, ztar, gztar, bztar, zip, ... )"), ] @@ -39,12 +42,21 @@ class bdist (Command): def initialize_options (self): + self.bdist_base = None self.format = None # initialize_options() def finalize_options (self): + # 'bdist_base' -- parent of per-built-distribution-format + # temporary directories (eg. we'll probably have + # "build/bdist./dumb", "build/bdist./rpm", etc.) + if self.bdist_base is None: + build_base = self.find_peer('build').build_base + plat = get_platform() + self.bdist_base = os.path.join (build_base, 'bdist.' + plat) + if self.format is None: try: self.format = self.default_format[os.name] @@ -55,7 +67,6 @@ class bdist (Command): #elif type (self.format) is StringType: # self.format = string.split (self.format, ',') - # finalize_options() -- cgit v1.2.1 From d3e1d51b64dc3cbddc78212a358a69cb51737e8e Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:09:50 +0000 Subject: List data files are listed in the Distribution attribute 'data_files', rather than 'data'. --- command/install_data.py | 4 ++-- dist.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/command/install_data.py b/command/install_data.py index 448614b0..fd9836b6 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -17,7 +17,7 @@ class install_data (install_misc): self._install_dir_from('install_data') def run (self): - self._copy_files(self.distribution.data) + self._copy_files(self.distribution.data_files) def get_inputs (self): - return self.distribution.data or [] + return self.distribution.data_files or [] diff --git a/dist.py b/dist.py index 998cff71..24c8d2b8 100644 --- a/dist.py +++ b/dist.py @@ -155,7 +155,7 @@ class Distribution: self.include_dirs = None self.extra_path = None self.scripts = None - self.data = None + self.data_files = None # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to -- cgit v1.2.1 From cc5ee731de127fa418aa290fa486531f9ce2e9e8 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:10:30 +0000 Subject: In 'install_misc': 'self.outfiles' defaults to the empty list, so we don't have to worry about "or []" in 'get_outputs()'. --- cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd.py b/cmd.py index 28d86179..c544b864 100644 --- a/cmd.py +++ b/cmd.py @@ -353,7 +353,7 @@ class install_misc (Command): def initialize_options (self): self.install_dir = None - self.outfiles = None + self.outfiles = [] def _install_dir_from (self, dirname): self.set_undefined_options('install', (dirname, 'install_dir')) -- cgit v1.2.1 From 6c282fc092bc44258e4d614b541ff59d15bb6582 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:11:40 +0000 Subject: Contribution from Harry Henry Gebel: the 'bdist_rpm' command. (Completely uninspected and untested by me, this is just to get the code into CVS!) --- command/bdist_rpm.py | 390 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 command/bdist_rpm.py diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py new file mode 100644 index 00000000..9c3aca3b --- /dev/null +++ b/command/bdist_rpm.py @@ -0,0 +1,390 @@ +"""distutils.command.bdist_rpm + +Implements the Distutils 'bdist_rpm' command (create RPM source and binary +distributions.""" + +# created 2000/04/25, by Harry Henry Gebel + +__revision__ = "$Id$" + +from os.path import exists, basename +import os +from distutils.core import Command +from distutils.util import mkpath, write_file, copy_file +from distutils.errors import * +from string import join, lower +from types import StringType, DictType, LongType, FloatType, IntType, \ + ListType, TupleType + +class bdist_rpm (Command): + + description = "create an RPM distribution" + + user_options = [ + ('spec-only', None, + "Only regenerate spec file"), + ('source-only', None, + "Only generate source RPM"), + ('binary-only', None, + "Only generate binary RPM"), + ('use-bzip2', None, + "Use bzip2 instead of gzip to create source distribution"), + ('no-clean', None, + "Do not clean RPM build directory"), + ('no-rpm-opt-flags', None, + "Do not pass any RPM CFLAGS to compiler") + ] + + + def initialize_options (self): + self.spec_only = None + self.binary_only = None + self.source_only = None + self.use_bzip2 = None + self.no_clean = None + self.no_rpm_opt_flags = None + + # initialize_options() + + + def finalize_options (self): + if os.name != 'posix': + raise DistutilsPlatformError, \ + ("don't know how to create RPM " + "distributions on platform %s" % os.name) + if self.binary_only and self.source_only: + raise DistutilsOptionsError, \ + "Cannot supply both '--source-only' and '--binary-only'" + # don't pass CFLAGS to pure python distributions + if not self.distribution.has_ext_modules(): + self.no_rpm_opt_flags = 1 + + # finalize_options() + + + def run (self): + self._get_package_data() # get packaging info + + + # make directories + if self.spec_only: + self.execute(mkpath, ('redhat',), "Created './redhat' directory") + else: + self.execute(mkpath, ('build/rpm/SOURCES',), + "Created RPM source directory") + self.execute(mkpath, ('build/rpm/SPECS',), + "Created RPM source directory") + self.execute(mkpath, ('build/rpm/BUILD',), + "Created RPM source directory") + self.execute(mkpath, ('build/rpm/RPMS',), + "Created RPM source directory") + self.execute(mkpath, ('build/rpm/SRPMS',), + "Created RPM source directory") + + # spec file goes into .redhat directory if '--spec-only specified', + # into build/rpm/spec otherwisu + if self.spec_only: + spec_path = './redhat/%s.spec' % self.distribution.get_name() + else: + spec_path = ('build/rpm/SPECS/%s.spec' % + self.distribution.get_name()) + self.execute(write_file, + (spec_path, + self._make_spec_file()), + 'Writing .spec file') + + if self.spec_only: # stop if requested + return + + # make a source distribution and copy to SOURCES directory with + # optional icon + sdist = self.find_peer ('sdist') + if self.use_bzip2: + sdist.formats = ['bztar'] + else: + sdist.formats = ['gztar'] + self.run_peer('sdist') + if self.use_bzip2: + source = self.distribution.get_fullname() + '.tar.bz2' + else: + source = self.distribution.get_fullname() + '.tar.gz' + self.execute(copy_file, (source, 'build/rpm/SOURCES'), + 'Copying source distribution to SOURCES') + if self.icon: + if exists(self.icon): + self.execute(copy_file, (self.icon, 'build/rpm/SOURCES'), + 'Copying icon to SOURCES') + else: + raise DistutilsFileError, \ + "Unable to find icon file '%s'" % self.icon + + + # build package + self.announce('Building RPMs') + rpm_args = ['rpm',] + if self.source_only: # what kind of RPMs? + rpm_args.append('-bs') + elif self.binary_only: + rpm_args.append('-bb') + else: + rpm_args.append('-ba') + topdir = os.getcwd() + 'build/rpm' + rpm_args.extend(['--define', + '_topdir ' + os.getcwd() + '/build/rpm',]) + if not self.no_clean: + rpm_args.append('--clean') + rpm_args.append(spec_path) + self.spawn(rpm_args) + + # run() + + + def _get_package_data(self): + ''' Get data needed to generate spec file, first from the + DistributionMetadata class, then from the package_data file, which is + Python code read with execfile() ''' + + package_type = 'rpm' + + # read in package data, if any + if exists('package_data'): + try: + exec(open('package_data')) + except: + raise DistutilsOptionError, 'Unable to parse package data file' + + # set instance variables, supplying default value if not provided in + # package data file + self.package_data = locals() + + # the following variables must be {string (len() = 2): string} + self.summaries = self._check_string_dict('summaries') + self.descriptions = self._check_string_dict('descriptions') + + # The following variable must be an ordinary number or a string + self.release = self._check_number_or_string('release', '1') + self.serial = self._check_number_or_string('serial') + + # The following variables must be strings + self.group = self._check_string('group', 'Development/Libraries') + self.vendor = self._check_string('vendor') + self.packager = self._check_string('packager') + self.changelog = self._check_string('changelog') + self.icon = self._check_string('icon') + self.distribution_name = self._check_string('distribution_name') + self.pre = self._check_string('pre') + self.post = self._check_string('post') + self.preun = self._check_string('preun') + self.postun = self._check_string('postun') + self.prep = self._check_string('prep', '%setup') + if not self.no_rpm_opt_flags: + self.build = (self._check_string( + 'build', + 'env CFLAGS="$RPM_OPT_FLAGS" python setup.py build')) + else: + self.build = (self._check_string('build', 'python setup.py build')) + self.install = self._check_string( + 'install', + 'python setup.py install --root=$RPM_BUILD_ROOT --record') + self.clean = self._check_string( + 'clean', + 'rm -rf $RPM_BUILD_ROOT') + + # The following variables must be a list or tuple of strings, or a + # string + self.doc = self._check_string_list('doc') + if type(self.doc) == ListType: + for readme in ('README', 'README.txt'): + if exists(readme) and readme not in self.doc: + self.doc.append(readme) + self.doc = join(self.doc) + self.provides = join(self._check_string_list('provides')) + self.requires = join(self._check_string_list('requires')) + self.conflicts = join(self._check_string_list('conflicts')) + self.build_requires = join(self._check_string_list('build_requires')) + self.obsoletes = join(self._check_string_list('obsoletes')) + + def _make_spec_file(self): + ''' Generate an RPM spec file ''' + + # definitons and headers + spec_file = [ + '%define name ' + self.distribution.get_name(), + '%define version ' + self.distribution.get_version(), + '%define release ' + self.release, + '', + 'Summary: ' + self.distribution.get_description(), + ] + + # put locale summaries into spec file + for locale in self.summaries.keys(): + spec_file.append('Summary(%s): %s' % (locale, + self.summaries[locale])) + + spec_file.extend([ + 'Name: %{name}', + 'Version: %{version}', + 'Release: %{release}',]) + if self.use_bzip2: + spec_file.append('Source0: %{name}-%{version}.tar.bz2') + else: + spec_file.append('Source0: %{name}-%{version}.tar.gz') + spec_file.extend([ + 'Copyright: ' + self.distribution.get_licence(), + 'Group: ' + self.group, + 'BuildRoot: %{_tmppath}/%{name}-buildroot', + 'Prefix: %{_prefix}', ]) + + # noarch if no extension modules + if not self.distribution.has_ext_modules(): + spec_file.append('BuildArchitectures: noarch') + + for field in ('Vendor', + 'Packager', + 'Provides', + 'Requires', + 'Conflicts', + 'Obsoletes', + ): + if getattr(self, lower(field)): + spec_file.append('%s: %s' % (field, getattr(self, + lower(field)))) + + if self.distribution.get_url() != 'UNKNOWN': + spec_file.append('Url: ' + self.distribution.get_url()) + + if self.distribution_name: + spec_file.append('Distribution: ' + self.distribution_name) + + if self.build_requires: + spec_file.append('BuildRequires: ' + self.build_requires) + + if self.icon: + spec_file.append('Icon: ' + basename(self.icon)) + + spec_file.extend([ + '', + '%description', + self.distribution.get_long_description() + ]) + + # put locale descriptions into spec file + for locale in self.descriptions.keys(): + spec_file.extend([ + '', + '%description -l ' + locale, + self.descriptions[locale], + ]) + + # rpm scripts + for script in ('prep', + 'build', + 'install', + 'clean', + 'pre', + 'post', + 'preun', + 'postun', + ): + if getattr(self, script): + spec_file.extend([ + '', + '%' + script, + getattr(self, script), + ]) + + + # files section + spec_file.extend([ + '', + '%files -f INSTALLED_FILES', + '%defattr(-,root,root)', + ]) + + if self.doc: + spec_file.append('%doc ' + self.doc) + + if self.changelog: + spec_file.extend([ + '', + '%changelog', + self.changelog + ]) + + return spec_file + + def _check_string_dict(self, var_name, default_value = {}): + ''' Tests a wariable to determine if it is {string: string}, + var_name is the name of the wariable. Return the value if it is valid, + returns default_value if the variable does not exist, raises + DistutilsOptionError if the variable is not valid''' + if self.package_data.has_key(var_name): + pass_test = 1 # set to 0 if fails test + value = self.package_data[var_name] + if type(value) == DictType: + for locale in value.keys(): + if (type(locale) != StringType) or (type(value[locale]) != + StringType): + pass_test = 0 + break + if pass_test: + return test_me + raise DistutilsOptionError, \ + ("Error in package_data: '%s' must be dictionary: " + '{string: string}' % var_name) + else: + return default_value + + def _check_string(self, var_name, default_value = None): + ''' Tests a variable in package_data to determine if it is a string, + var_name is the name of the wariable. Return the value if it is a + string, returns default_value if the variable does not exist, raises + DistutilsOptionError if the variable is not a string''' + if self.package_data.has_key(var_name): + if type(self.package_data[var_name]) == StringType: + return self.package_data[var_name] + else: + raise DistutilsOptionError, \ + "Error in package_data: '%s' must be a string" % var_name + else: + return default_value + + def _check_number_or_string(self, var_name, default_value = None): + ''' Tests a variable in package_data to determine if it is a number or + a string, var_name is the name of the wariable. Return the value if it + is valid, returns default_value if the variable does not exist, raises + DistutilsOptionError if the variable is not valid''' + if self.package_data.has_key(var_name): + if type(self.package_data[var_name]) in (StringType, LongType, + IntType, FloatType): + return str(self.package_data[var_name]) + else: + raise DistutilsOptionError, \ + ("Error in package_data: '%s' must be a string or a " + 'number' % var_name) + else: + return default_value + + def _check_string_list(self, var_name, default_value = []): + ''' Tests a variable in package_data to determine if it is a string or + a list or tuple of strings, var_name is the name of the wariable. + Return the value as a string or a list if it is valid, returns + default_value if the variable does not exist, raises + DistutilsOptionError if the variable is not valid''' + if self.package_data.has_key(var_name): + value = self.package_data[var_name] + pass_test = 1 + if type(value) == StringType: + return value + elif type(value) in (ListType, TupleType): + for item in value: + if type(item) != StringType: + pass_test = 0 + break + if pass_test: + return list(value) + raise DistutilsOptionError, \ + ("Error in package_data: '%s' must be a string or a " + 'list or tuple of strings' % var_name) + else: + return default_value -- cgit v1.2.1 From e46022cedb21e05ca1292aae4f598817b0c195c6 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:32:36 +0000 Subject: Template for writing Distutils command modules. --- command/command_template | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 command/command_template diff --git a/command/command_template b/command/command_template new file mode 100644 index 00000000..f0329aa9 --- /dev/null +++ b/command/command_template @@ -0,0 +1,39 @@ +"""distutils.command.x + +Implements the Distutils 'x' command.""" + +# created 2000/mm/dd, Greg Ward + +__revision__ = "$Id$" + +from distutils.core import Command + + +class x (Command): + + description = "" + + user_options = [('', '', + ""), + ] + + + def initialize_options (self): + self. = None + self. = None + self. = None + + # initialize_options() + + + def finalize_options (self): + + # finalize_options() + + + def run (self): + + + # run() + +# class x -- cgit v1.2.1 From ed02ed8aaba4efefd9e172b8f39e4f82e93a627c Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:35:05 +0000 Subject: Changed default developer name. Added some guiding comments. --- command/command_template | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/command/command_template b/command/command_template index f0329aa9..73c1c190 100644 --- a/command/command_template +++ b/command/command_template @@ -2,7 +2,7 @@ Implements the Distutils 'x' command.""" -# created 2000/mm/dd, Greg Ward +# created 2000/mm/dd, John Doe __revision__ = "$Id$" @@ -11,8 +11,11 @@ from distutils.core import Command class x (Command): + # Brief (40-50 characters) description of the command description = "" + # List of option tuples: long name, short name (None if no short + # name), and help string. user_options = [('', '', ""), ] @@ -27,6 +30,8 @@ class x (Command): def finalize_options (self): + if self.x is None: + self.x = # finalize_options() -- cgit v1.2.1 From 33eb8cbf408e379e5e965c32b1365d3f961003a4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 13:23:21 +0000 Subject: Added support for the 'export_symbols' parameter to 'link_shared_object()' and 'link_shared_lib()'. In MSVCCompiler, this is meaningful: it adds /EXPORT: options to the linker command line. In UnixCCompiler, it is ignored. --- ccompiler.py | 10 +++++++++- msvccompiler.py | 12 ++++++++++-- unixccompiler.py | 3 +++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index ffad2947..51f5ae02 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -493,6 +493,7 @@ class CCompiler: libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -515,7 +516,13 @@ class CCompiler: search for libraries that were specified as bare library names (ie. no directory component). These are on top of the system default and those supplied to 'add_library_dir()' and/or - 'set_library_dirs()'. + 'set_library_dirs()'. 'runtime_library_dirs' is a list of + directories that will be embedded into the shared library and + used to search for other shared libraries that *it* depends on + at run-time. (This may only be relevant on Unix.) + + 'export_symbols' is a list of symbols that the shared library + will export. (This appears to be relevant only on Windows.) 'debug' is as for 'compile()' and 'create_static_lib()', with the slight distinction that it actually matters on most platforms @@ -536,6 +543,7 @@ class CCompiler: libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): diff --git a/msvccompiler.py b/msvccompiler.py index 3667afcd..b6ff432c 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -304,6 +304,7 @@ class MSVCCompiler (CCompiler) : libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -313,6 +314,8 @@ class MSVCCompiler (CCompiler) : output_dir=output_dir, libraries=libraries, library_dirs=library_dirs, + runtime_library_dirs=runtime_library_dirs, + export_symbols=export_symbols, debug=debug, extra_preargs=extra_preargs, extra_postargs=extra_postargs) @@ -325,6 +328,7 @@ class MSVCCompiler (CCompiler) : libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -350,8 +354,12 @@ class MSVCCompiler (CCompiler) : else: ldflags = self.ldflags_shared - ld_args = ldflags + lib_opts + \ - objects + ['/OUT:' + output_filename] + export_opts = [] + for sym in (export_symbols or []): + export_opts.append("/EXPORT:" + sym) + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) if extra_preargs: ld_args[:0] = extra_preargs diff --git a/unixccompiler.py b/unixccompiler.py index 5e1524c6..40f564ab 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -178,6 +178,7 @@ class UnixCCompiler (CCompiler): libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -188,6 +189,7 @@ class UnixCCompiler (CCompiler): libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, extra_preargs, extra_postargs) @@ -200,6 +202,7 @@ class UnixCCompiler (CCompiler): libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): -- cgit v1.2.1 From 05fa00ab977197b8e02c7a99443e3541cdfc3624 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 13:31:32 +0000 Subject: Lyle Johnson: added stubs for the four miscellaneous methods that must be implemented by subclasses, since they are needed by 'gen_lib_options()'. --- ccompiler.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/ccompiler.py b/ccompiler.py index 51f5ae02..42221769 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -575,6 +575,33 @@ class CCompiler: + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function; there is + # no appropriate default implementation so subclasses should + # implement all of these. + + def library_dir_option (self, dir): + """Return the compiler option to add 'dir' to the list of directories + searched for libraries.""" + raise NotImplementedError + + def runtime_library_dir_option (self, dir): + """Return the compiler option to add 'dir' to the list of directories + searched for runtime libraries.""" + raise NotImplementedError + + def library_option (self, lib): + """Return the compiler option to add 'dir' to the list of libraries + linked into the shared library or executable.""" + raise NotImplementedError + + def find_library_file (self, dirs, lib): + """Search the specified list of directories for a static or shared + library file 'lib' and return the full path to that file. Return + None if it wasn't found in any of the specified directories.""" + raise NotImplementedError + + # -- Filename generation methods ----------------------------------- # The default implementation of the filename generating methods are -- cgit v1.2.1 From 022c5e143c2e7f68bc794a3f108a70193b302533 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 14:07:59 +0000 Subject: Added missing import. --- dist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dist.py b/dist.py index 24c8d2b8..0f131980 100644 --- a/dist.py +++ b/dist.py @@ -12,6 +12,7 @@ import sys, os, string, re from types import * from copy import copy from distutils.errors import * +from distutils import sysconfig from distutils.fancy_getopt import FancyGetopt, longopt_xlate from distutils.util import check_environ -- cgit v1.2.1 From dcc3e4d4cbc4029ba8c71691613faef73a4c1c4d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 15:06:48 +0000 Subject: Added 'has_scripts()', 'has_data_files()' methods. --- dist.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dist.py b/dist.py index 0f131980..03b20e1f 100644 --- a/dist.py +++ b/dist.py @@ -639,6 +639,12 @@ class Distribution: def has_modules (self): return self.has_pure_modules() or self.has_ext_modules() + def has_scripts (self): + return self.scripts and len(self.scripts) > 0 + + def has_data_files (self): + return self.data_files and len(self.data_files) > 0 + def is_pure (self): return (self.has_pure_modules() and not self.has_ext_modules() and -- cgit v1.2.1 From 81213a2c15fe76d6d0a05621cad1959ac8deecad Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 15:08:57 +0000 Subject: Check if the claimed build directory doesn't exist, and warn that we don't have any Python modules to install (rather than bomb when we try to copy a non-existent directory). --- command/install_lib.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index 63c7a6bf..5b3d77a3 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -53,7 +53,12 @@ class install_lib (Command): # Install everything: simply dump the entire contents of the build # directory to the installation directory (that's the beauty of # having a build directory!) - outfiles = self.copy_tree (self.build_dir, self.install_dir) + if os.path.isdir(self.build_dir): + outfiles = self.copy_tree (self.build_dir, self.install_dir) + else: + self.warn("'%s' does not exist -- no Python modules to install" % + self.build_dir) + return # (Optionally) compile .py to .pyc # XXX hey! we can't control whether we optimize or not; that's up -- cgit v1.2.1 From 8e79f197fb13982a17cfc1dfa4187444f290f7a5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 15:17:09 +0000 Subject: Changed the semantics of the 'sub_commands' list: instead of function objects, it now has method names. Added three methods, 'has_lib()', 'has_scripts()', and 'has_data()' to determine if we need to run each of the three possible sub-commands. Added 'get_sub_commands()' to take care of finding the methods named in 'sub_commands', running them, and interpreting the results to build a list of sub-commands that actually have to be run. --- command/install.py | 65 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/command/install.py b/command/install.py index a4986e5d..392fe507 100644 --- a/command/install.py +++ b/command/install.py @@ -93,14 +93,14 @@ class install (Command): "filename in which to record list of installed files"), ] - # 'sub_commands': a list of commands this command might have to run - # to get its work done. Each command is represented as a tuple - # (func, command) where 'func' is a function to call that returns - # true if 'command' (the sub-command name, a string) needs to be - # run. If 'func' is None, assume that 'command' must always be run. - sub_commands = [(None, 'install_lib'), - (None, 'install_scripts'), - (None, 'install_data'), + # 'sub_commands': a list of commands this command might have to run to + # get its work done. Each command is represented as a tuple (method, + # command) where 'method' is the name of a method to call that returns + # true if 'command' (the sub-command name, a string) needs to be run. + # If 'method' is None, assume that 'command' must always be run. + sub_commands = [('has_lib', 'install_lib'), + ('has_scripts', 'install_scripts'), + ('has_data', 'install_data'), ] @@ -422,17 +422,29 @@ class install (Command): # handle_extra_path () + def get_sub_commands (self): + """Return the list of subcommands that we need to run. This is + based on the 'subcommands' class attribute: each tuple in that list + can name a method that we call to determine if the subcommand needs + to be run for the current distribution.""" + commands = [] + for (method, cmd_name) in self.sub_commands: + if method is not None: + method = getattr(self, method) + if method is None or method(): + commands.append(cmd_name) + return commands + + def run (self): # Obviously have to build before we can install if not self.skip_build: self.run_peer ('build') - # Run all sub-commands: currently this just means install all - # Python modules using 'install_lib'. - for (func, cmd_name) in self.sub_commands: - if func is None or func(): - self.run_peer (cmd_name) + # Run all sub-commands (at least those that need to be run) + for cmd_name in self.get_sub_commands(): + self.run_peer (cmd_name) if self.path_file: self.create_path_file () @@ -460,14 +472,26 @@ class install (Command): # run () + def has_lib (self): + """Return true if the current distribution has any Python + modules to install.""" + return (self.distribution.has_pure_modules() or + self.distribution.has_ext_modules()) + + def has_scripts (self): + return self.distribution.has_scripts() + + def has_data (self): + return self.distribution.has_data_files() + + def get_outputs (self): # This command doesn't have any outputs of its own, so just # get the outputs of all its sub-commands. outputs = [] - for (func, cmd_name) in self.sub_commands: - if func is None or func(): - cmd = self.find_peer (cmd_name) - outputs.extend (cmd.get_outputs()) + for cmd_name in self.get_sub_commands(): + cmd = self.find_peer (cmd_name) + outputs.extend (cmd.get_outputs()) return outputs @@ -475,10 +499,9 @@ class install (Command): def get_inputs (self): # XXX gee, this looks familiar ;-( inputs = [] - for (func, cmd_name) in self.sub_commands: - if func is None or func(): - cmd = self.find_peer (cmd_name) - inputs.extend (cmd.get_inputs()) + for cmd_name in self.get_sub_commands(): + cmd = self.find_peer (cmd_name) + inputs.extend (cmd.get_inputs()) return inputs -- cgit v1.2.1 From a745efa83a06a337f54b8a80362957bdb4cdf4bc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 16:05:34 +0000 Subject: Tweaked output of 'copy_file()': if copying to a new name, show the whole destination path, otherwise show just the directory. --- file_util.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/file_util.py b/file_util.py index 32245109..a73db42b 100644 --- a/file_util.py +++ b/file_util.py @@ -90,7 +90,7 @@ def copy_file (src, dst, (os.symlink) instead of copying: set it to "hard" or "sym"; if it is None (the default), files are copied. Don't set 'link' on systems that don't support it: 'copy_file()' doesn't check if - hard or symbolic linking is availalble. + hard or symbolic linking is available. Under Mac OS, uses the native file copy function in macostools; on other systems, uses '_copy_file_contents()' to copy file @@ -131,8 +131,11 @@ def copy_file (src, dst, raise ValueError, \ "invalid value '%s' for 'link' argument" % link if verbose: - print "%s %s -> %s" % (action, src, dir) - + if os.path.basename(dst) == os.path.basename(src): + print "%s %s -> %s" % (action, src, dir) + else: + print "%s %s -> %s" % (action, src, dst) + if dry_run: return 1 -- cgit v1.2.1 From 0f61807acb702df144c2266a92fc7a60988c9581 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 01:42:17 +0000 Subject: Marching towards full support of config files: thoroughly overhauled the command-line parsing code, splitting it up into several methods (new methods: '_parse_command_opts()', '_show_help()') and making it put options into the 'command_options' dictionary rather than instantiating command objects and putting them there. Lots of other little changes: * merged 'find_command_class()' and 'create_command_obj()' and called the result 'get_command_class()' * renamed 'find_command_obj()' to 'get_command_obj()', and added command object creation and maintenance of the command object cache to its responsibilities (taken over from 'create_command_obj()') * parse config files one-at-a-time, so we can keep track of the filename for later error reporting * tweaked some help messages * fixed up many obsolete comments and docstrings --- dist.py | 447 ++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 238 insertions(+), 209 deletions(-) diff --git a/dist.py b/dist.py index 03b20e1f..3fd29d9f 100644 --- a/dist.py +++ b/dist.py @@ -48,15 +48,10 @@ class Distribution: # since every global option is also valid as a command option -- and we # don't want to pollute the commands with too many options that they # have minimal control over. - global_options = [('verbose', 'v', - "run verbosely (default)"), - ('quiet', 'q', - "run quietly (turns verbosity off)"), - ('dry-run', 'n', - "don't actually do anything"), - ('help', 'h', - "show this help message, plus help for any commands " + - "given on the command-line"), + global_options = [('verbose', 'v', "run verbosely (default)"), + ('quiet', 'q', "run quietly (turns verbosity off)"), + ('dry-run', 'n', "don't actually do anything"), + ('help', 'h', "show detailed help message"), ] # options that are not propagated to the commands @@ -78,11 +73,9 @@ class Distribution: ('maintainer-email', None, "print the maintainer's email address"), ('contact', None, - "print the name of the maintainer if present, " - "else author"), + "print the maintainer's name if known, else the author's"), ('contact-email', None, - "print the email of the maintainer if present, " - "else author"), + "print the maintainer's email address if known, else the author's"), ('url', None, "print the URL for this package"), ('licence', None, @@ -139,9 +132,11 @@ class Distribution: # for the setup script to override command classes self.cmdclass = {} - # Store options for commands here between parsing them (from config - # files, the command-line, etc.) and actually putting them into the - # command object that needs them. + # 'command_options' is where we store command options between + # parsing them (from config files, the command-line, etc.) and when + # they are actually needed -- ie. when the command in question is + # instantiated. It is a dictionary of dictionaries of 2-tuples: + # command_options = { command_name : { option : (source, value) } } self.command_options = {} # These options are really the business of various commands, rather @@ -190,7 +185,7 @@ class Distribution: if options: del attrs['options'] for (command, cmd_options) in options.items(): - cmd_obj = self.find_command_obj (command) + cmd_obj = self.get_command_obj (command) for (key, val) in cmd_options.items(): cmd_obj.set_option (key, val) # loop over commands @@ -210,6 +205,8 @@ class Distribution: # __init__ () + # -- Config file finding/parsing methods --------------------------- + def find_config_files (self): """Find as many configuration files as should be processed for this platform, and return a list of filenames in the order in which they @@ -223,8 +220,8 @@ class Distribution: On Windows and Mac OS, there are two possible config files: pydistutils.cfg in the Python installation directory (sys.prefix) - and setup.cfg in the current directory.""" - + and setup.cfg in the current directory. + """ files = [] if os.name == "posix": check_environ() @@ -262,54 +259,50 @@ class Distribution: filenames = self.find_config_files() parser = ConfigParser() - parser.read(filenames) - for section in parser.sections(): - options = parser.options(section) - if not self.command_options.has_key(section) is None: - self.command_options[section] = {} - cmd_opts = self.command_options[section] - - for opt in options: - if opt != '__name__': - cmd_opts[opt] = parser.get(section,opt) + for filename in filenames: + parser.read(filename) + for section in parser.sections(): + options = parser.options(section) + if not self.command_options.has_key(section): + self.command_options[section] = {} + opts = self.command_options[section] + + for opt in options: + if opt != '__name__': + opts[opt] = (filename, parser.get(section,opt)) from pprint import pprint - print "configuration options:" + print "options (after parsing config files):" pprint (self.command_options) - def parse_command_line (self, args): - """Parse the setup script's command line: set any Distribution - attributes tied to command-line options, create all command - objects, and set their options from the command-line. 'args' - must be a list of command-line arguments, most likely - 'sys.argv[1:]' (see the 'setup()' function). This list is first - processed for "global options" -- options that set attributes of - the Distribution instance. Then, it is alternately scanned for - Distutils command and options for that command. Each new - command terminates the options for the previous command. The - allowed options for a command are determined by the 'options' - attribute of the command object -- thus, we instantiate (and - cache) every command object here, in order to access its - 'options' attribute. Any error in that 'options' attribute - raises DistutilsGetoptError; any error on the command-line - raises DistutilsArgError. If no Distutils commands were found - on the command line, raises DistutilsArgError. Return true if - command-line successfully parsed and we should carry on with - executing commands; false if no errors but we shouldn't execute - commands (currently, this only happens if user asks for - help).""" - - # late import because of mutual dependence between these modules - from distutils.cmd import Command - from distutils.core import usage + # -- Command-line parsing methods ---------------------------------- + def parse_command_line (self, args): + """Parse the setup script's command line. 'args' must be a list + of command-line arguments, most likely 'sys.argv[1:]' (see the + 'setup()' function). This list is first processed for "global + options" -- options that set attributes of the Distribution + instance. Then, it is alternately scanned for Distutils + commands and options for that command. Each new command + terminates the options for the previous command. The allowed + options for a command are determined by the 'user_options' + attribute of the command class -- thus, we have to be able to + load command classes in order to parse the command line. Any + error in that 'options' attribute raises DistutilsGetoptError; + any error on the command-line raises DistutilsArgError. If no + Distutils commands were found on the command line, raises + DistutilsArgError. Return true if command-line were + successfully parsed and we should carry on with executing + commands; false if no errors but we shouldn't execute commands + (currently, this only happens if user asks for help). + """ # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- # because each command will be handled by a different class, and - # the options that are valid for a particular class aren't - # known until we instantiate the command class, which doesn't - # happen until we know what the command is. + # the options that are valid for a particular class aren't known + # until we have loaded the command class, which doesn't happen + # until we know what the command is. self.commands = [] parser = FancyGetopt (self.global_options + self.display_options) @@ -323,91 +316,21 @@ class Distribution: return while args: - # Pull the current command from the head of the command line - command = args[0] - if not command_re.match (command): - raise SystemExit, "invalid command name '%s'" % command - self.commands.append (command) - - # Make sure we have a command object to put the options into - # (this either pulls it out of a cache of command objects, - # or finds and instantiates the command class). - try: - cmd_obj = self.find_command_obj (command) - except DistutilsModuleError, msg: - raise DistutilsArgError, msg - - # Require that the command class be derived from Command -- - # want to be sure that the basic "command" interface is - # implemented. - if not isinstance (cmd_obj, Command): - raise DistutilsClassError, \ - "command class %s must subclass Command" % \ - cmd_obj.__class__ - - # Also make sure that the command object provides a list of its - # known options - if not (hasattr (cmd_obj, 'user_options') and - type (cmd_obj.user_options) is ListType): - raise DistutilsClassError, \ - ("command class %s must provide " + - "'user_options' attribute (a list of tuples)") % \ - cmd_obj.__class__ - - # Poof! like magic, all commands support the global - # options too, just by adding in 'global_options'. - negative_opt = self.negative_opt - if hasattr (cmd_obj, 'negative_opt'): - negative_opt = copy (negative_opt) - negative_opt.update (cmd_obj.negative_opt) - - parser.set_option_table (self.global_options + - cmd_obj.user_options) - parser.set_negative_aliases (negative_opt) - args = parser.getopt (args[1:], cmd_obj) - if cmd_obj.help: - parser.set_option_table (self.global_options) - parser.print_help ("Global options:") - print - - parser.set_option_table (cmd_obj.user_options) - parser.print_help ("Options for '%s' command:" % command) - print - print usage + args = self._parse_command_opts(parser, args) + if args is None: # user asked for help (and got it) return - - self.command_obj[command] = cmd_obj - self.have_run[command] = 0 - - # while args - - # If the user wants help -- ie. they gave the "--help" option -- - # give it to 'em. We do this *after* processing the commands in - # case they want help on any particular command, eg. - # "setup.py --help foo". (This isn't the documented way to - # get help on a command, but I support it because that's how - # CVS does it -- might as well be consistent.) - if self.help: - parser.set_option_table (self.global_options) - parser.print_help ( - "Global options (apply to all commands, " + - "or can be used per command):") - print - if not self.commands: - parser.set_option_table (self.display_options) - parser.print_help ( - "Information display options (just display " + - "information, ignore any commands)") - print - - for command in self.commands: - klass = self.find_command_class (command) - parser.set_option_table (klass.user_options) - parser.print_help ("Options for '%s' command:" % command) - print - - print usage + # Handle the cases of --help as a "global" option, ie. + # "setup.py --help" and "setup.py --help command ...". For the + # former, we show global options (--verbose, --dry-run, etc.) + # and display-only options (--name, --version, etc.); for the + # latter, we omit the display-only options and show help for + # each command listed on the command line. + if self.help: + print "showing 'global' help; commands=", self.commands + self._show_help(parser, + display_options=len(self.commands) == 0, + commands=self.commands) return # Oops, no commands found -- an end-user error @@ -419,12 +342,133 @@ class Distribution: # parse_command_line() + def _parse_command_opts (self, parser, args): + + """Parse the command-line options for a single command. + 'parser' must be a FancyGetopt instance; 'args' must be the list + of arguments, starting with the current command (whose options + we are about to parse). Returns a new version of 'args' with + the next command at the front of the list; will be the empty + list if there are no more commands on the command line. Returns + None if the user asked for help on this command. + """ + # late import because of mutual dependence between these modules + from distutils.cmd import Command + + # Pull the current command from the head of the command line + command = args[0] + if not command_re.match (command): + raise SystemExit, "invalid command name '%s'" % command + self.commands.append (command) + + # Dig up the command class that implements this command, so we + # 1) know that it's a valid command, and 2) know which options + # it takes. + try: + cmd_class = self.get_command_class (command) + except DistutilsModuleError, msg: + raise DistutilsArgError, msg + + # Require that the command class be derived from Command -- want + # to be sure that the basic "command" interface is implemented. + if not issubclass (cmd_class, Command): + raise DistutilsClassError, \ + "command class %s must subclass Command" % cmd_class + + # Also make sure that the command object provides a list of its + # known options. + if not (hasattr (cmd_class, 'user_options') and + type (cmd_class.user_options) is ListType): + raise DistutilsClassError, \ + ("command class %s must provide " + + "'user_options' attribute (a list of tuples)") % \ + cmd_class + + # If the command class has a list of negative alias options, + # merge it in with the global negative aliases. + negative_opt = self.negative_opt + if hasattr (cmd_class, 'negative_opt'): + negative_opt = copy (negative_opt) + negative_opt.update (cmd_class.negative_opt) + + # All commands support the global options too, just by adding + # in 'global_options'. + parser.set_option_table (self.global_options + + cmd_class.user_options) + parser.set_negative_aliases (negative_opt) + (args, opts) = parser.getopt (args[1:]) + if opts.help: + print "showing help for command", cmd_class + self._show_help(parser, display_options=0, commands=[cmd_class]) + return + + # Put the options from the command-line into their official + # holding pen, the 'command_options' dictionary. + if not self.command_options.has_key(command): + self.command_options[command] = {} + cmd_opts = self.command_options[command] + for (name, value) in vars(opts).items(): + cmd_opts[command] = ("command line", value) + + return args + + # _parse_command_opts () + + + def _show_help (self, + parser, + global_options=1, + display_options=1, + commands=[]): + """Show help for the setup script command-line in the form of + several lists of command-line options. 'parser' should be a + FancyGetopt instance; do not expect it to be returned in the + same state, as its option table will be reset to make it + generate the correct help text. + + If 'global_options' is true, lists the global options: + --verbose, --dry-run, etc. If 'display_options' is true, lists + the "display-only" options: --name, --version, etc. Finally, + lists per-command help for every command name or command class + in 'commands'. + """ + # late import because of mutual dependence between these modules + from distutils.core import usage + from distutils.cmd import Command + + if global_options: + parser.set_option_table (self.global_options) + parser.print_help ("Global options:") + print + + if display_options: + parser.set_option_table (self.display_options) + parser.print_help ( + "Information display options (just display " + + "information, ignore any commands)") + print + + for command in self.commands: + if type(command) is ClassType and issubclass(klass, Command): + klass = command + else: + klass = self.get_command_class (command) + parser.set_option_table (klass.user_options) + parser.print_help ("Options for '%s' command:" % klass.__name__) + print + + print usage + return + + # _show_help () + + def handle_display_options (self, option_order): """If there were any non-global "display-only" options - (--help-commands or the metadata display options) on the command - line, display the requested info and return true; else return - false.""" - + (--help-commands or the metadata display options) on the command + line, display the requested info and return true; else return + false. + """ from distutils.core import usage # User just wants a list of commands -- we'll print it out and stop @@ -456,14 +500,15 @@ class Distribution: def print_command_list (self, commands, header, max_length): """Print a subset of the list of all commands -- used by - 'print_commands()'.""" + 'print_commands()'. + """ print header + ":" for cmd in commands: klass = self.cmdclass.get (cmd) if not klass: - klass = self.find_command_class (cmd) + klass = self.get_command_class (cmd) try: description = klass.description except AttributeError: @@ -475,12 +520,13 @@ class Distribution: def print_commands (self): - """Print out a help message listing all available commands with - a description of each. The list is divided into "standard - commands" (listed in distutils.command.__all__) and "extra - commands" (mentioned in self.cmdclass, but not a standard - command). The descriptions come from the command class - attribute 'description'.""" + """Print out a help message listing all available commands with a + description of each. The list is divided into "standard commands" + (listed in distutils.command.__all__) and "extra commands" + (mentioned in self.cmdclass, but not a standard command). The + descriptions come from the command class attribute + 'description'. + """ import distutils.command std_commands = distutils.command.__all__ @@ -508,19 +554,25 @@ class Distribution: max_length) # print_commands () - # -- Command class/object methods ---------------------------------- - def find_command_class (self, command): - """Given a command name, attempts to load the module and class that - implements that command. This is done by importing a module - "distutils.command." + command, and a class named 'command' in that - module. + def get_command_class (self, command): + """Return the class that implements the Distutils command named by + 'command'. First we check the 'cmdclass' dictionary; if the + command is mentioned there, we fetch the class object from the + dictionary and return it. Otherwise we load the command module + ("distutils.command." + command) and fetch the command class from + the module. The loaded class is also stored in 'cmdclass' + to speed future calls to 'get_command_class()'. Raises DistutilsModuleError if the expected module could not be - found, or if that module does not define the expected class.""" + found, or if that module does not define the expected class. + """ + klass = self.cmdclass.get(command) + if klass: + return klass module_name = 'distutils.command.' + command klass_name = command @@ -534,50 +586,28 @@ class Distribution: (command, module_name) try: - klass = vars(module)[klass_name] - except KeyError: + klass = getattr(module, klass_name) + except AttributeError: raise DistutilsModuleError, \ "invalid command '%s' (no class '%s' in module '%s')" \ % (command, klass_name, module_name) + self.cmdclass[command] = klass return klass - # find_command_class () - - - def create_command_obj (self, command): - """Figure out the class that should implement a command, - instantiate it, cache and return the new "command object". - The "command class" is determined either by looking it up in - the 'cmdclass' attribute (this is the mechanism whereby - clients may override default Distutils commands or add their - own), or by calling the 'find_command_class()' method (if the - command name is not in 'cmdclass'.""" - - # Determine the command class -- either it's in the command_class - # dictionary, or we have to divine the module and class name - klass = self.cmdclass.get(command) - if not klass: - klass = self.find_command_class (command) - self.cmdclass[command] = klass + # get_command_class () - # Found the class OK -- instantiate it - cmd_obj = klass (self) - return cmd_obj - - - def find_command_obj (self, command, create=1): - """Look up and return a command object in the cache maintained by - 'create_command_obj()'. If none found, the action taken - depends on 'create': if true (the default), create a new - command object by calling 'create_command_obj()' and return - it; otherwise, return None. If 'command' is an invalid - command name, then DistutilsModuleError will be raised.""" - - cmd_obj = self.command_obj.get (command) + def get_command_obj (self, command, create=1): + """Return the command object for 'command'. Normally this object + is cached on a previous call to 'get_command_obj()'; if no comand + object for 'command' is in the cache, then we either create and + return it (if 'create' is true) or return None. + """ + cmd_obj = self.command_obj.get(command) if not cmd_obj and create: - cmd_obj = self.create_command_obj (command) - self.command_obj[command] = cmd_obj + klass = self.get_command_class(command) + cmd_obj = self.command_obj[command] = klass() + self.command_run[command] = 0 return cmd_obj @@ -586,17 +616,17 @@ class Distribution: def announce (self, msg, level=1): """Print 'msg' if 'level' is greater than or equal to the verbosity - level recorded in the 'verbose' attribute (which, currently, - can be only 0 or 1).""" - + level recorded in the 'verbose' attribute (which, currently, can be + only 0 or 1). + """ if self.verbose >= level: print msg def run_commands (self): """Run each command that was seen on the setup script command line. - Uses the list of commands found and cache of command objects - created by 'create_command_obj()'.""" + Uses the list of commands found and cache of command objects + created by 'get_command_obj()'.""" for cmd in self.commands: self.run_command (cmd) @@ -605,21 +635,20 @@ class Distribution: # -- Methods that operate on its Commands -------------------------- def run_command (self, command): - """Do whatever it takes to run a command (including nothing at all, - if the command has already been run). Specifically: if we have - already created and run the command named by 'command', return - silently without doing anything. If the command named by - 'command' doesn't even have a command object yet, create one. - Then invoke 'run()' on that command object (or an existing - one).""" + if the command has already been run). Specifically: if we have + already created and run the command named by 'command', return + silently without doing anything. If the command named by 'command' + doesn't even have a command object yet, create one. Then invoke + 'run()' on that command object (or an existing one). + """ # Already been here, done that? then return silently. if self.have_run.get (command): return self.announce ("running " + command) - cmd_obj = self.find_command_obj (command) + cmd_obj = self.get_command_obj (command) cmd_obj.ensure_ready () cmd_obj.run () self.have_run[command] = 1 -- cgit v1.2.1 From 1ea22ad71cc61f4f7467262c768ec43fabbb81d2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 01:43:08 +0000 Subject: Tweaked usage message. --- core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index a39bccd3..3eeba1df 100644 --- a/core.py +++ b/core.py @@ -21,10 +21,10 @@ from distutils.cmd import Command # and per-command help. usage = """\ usage: %s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] - or: %s --help + or: %s --help [cmd1 cmd2 ...] or: %s --help-commands or: %s cmd --help -""" % ((sys.argv[0],) * 4) +""" % ((os.path.basename(sys.argv[0]),) * 4) def setup (**attrs): -- cgit v1.2.1 From 138ae247bb47e34672d909a46fa998a4b86b90f6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 01:44:20 +0000 Subject: OptionDummy now has a constructor that takes a list of options: each string in the option list is an attribute of the OptionDummy that will be initialized to None. --- fancy_getopt.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 39450e80..588c6ba7 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -239,7 +239,7 @@ class FancyGetopt: if args is None: args = sys.argv[1:] if object is None: - object = OptionDummy() + object = OptionDummy(self.attr_name.values()) created_object = 1 else: created_object = 0 @@ -465,7 +465,14 @@ def wrap_text (text, width): class OptionDummy: """Dummy class just used as a place to hold command-line option values as instance attributes.""" - pass + + def __init__ (self, options=[]): + """Create a new OptionDummy instance. The attributes listed in + 'options' will be initialized to None.""" + for opt in options: + setattr(self, opt, None) + +# class OptionDummy if __name__ == "__main__": -- cgit v1.2.1 From 3f5a39b346e9ed623bba60da5b773e238721ef71 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 01:55:01 +0000 Subject: Use 'get_command_obj()' instead of 'find_command_obj()'. --- cmd.py | 6 +++--- command/bdist_dumb.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd.py b/cmd.py index c544b864..0cf88d41 100644 --- a/cmd.py +++ b/cmd.py @@ -183,7 +183,7 @@ class Command: # Option_pairs: list of (src_option, dst_option) tuples - src_cmd_obj = self.distribution.find_command_obj (src_cmd) + src_cmd_obj = self.distribution.get_command_obj (src_cmd) src_cmd_obj.ensure_ready () for (src_option, dst_option) in option_pairs: if getattr (self, dst_option) is None: @@ -192,11 +192,11 @@ class Command: def find_peer (self, command, create=1): - """Wrapper around Distribution's 'find_command_obj()' method: + """Wrapper around Distribution's 'get_command_obj()' method: find (create if necessary and 'create' is true) the command object for 'command'..""" - cmd_obj = self.distribution.find_command_obj (command, create) + cmd_obj = self.distribution.get_command_obj (command, create) cmd_obj.ensure_ready () return cmd_obj diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 2de2befc..eaa19273 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -63,7 +63,7 @@ class bdist_dumb (Command): # command object that has *not* been finalized, so we can set # options on it! (The option we set, 'root', is so that we can do # a proper "fake install" using this install command object.) - install = self.distribution.find_command_obj('install') + install = self.distribution.get_command_obj('install') install.root = self.bdist_dir self.announce ("installing to %s" % self.bdist_dir) -- cgit v1.2.1 From ed327f4988e7fe976ba91586893e4babb655409c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 01:55:16 +0000 Subject: Fixed command description. --- command/install_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index 5b3d77a3..e9cd80db 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -8,7 +8,7 @@ from distutils.util import copy_tree class install_lib (Command): - description = "install pure Python modules" + description = "install all Python modules (extensions and pure Python)" user_options = [ ('install-dir=', 'd', "directory to install to"), -- cgit v1.2.1 From 684f4fcf396f5109aae531d4229699b55419b9e6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 03:47:35 +0000 Subject: Fixed so options from config files and command lines actually work: * 'get_command_obj()' now sets command attributes based on the 'command_options' dictionary * some typos fixed * kludged 'parse_config_files()' to re-initialize the ConfigParser instance after each file, so we know for sure which config file each option comes form * added lots of handy debugging output --- dist.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/dist.py b/dist.py index 3fd29d9f..3ceadf19 100644 --- a/dist.py +++ b/dist.py @@ -258,8 +258,11 @@ class Distribution: if filenames is None: filenames = self.find_config_files() + print "Distribution.parse_config_files():" + parser = ConfigParser() for filename in filenames: + print " reading", filename parser.read(filename) for section in parser.sections(): options = parser.options(section) @@ -271,9 +274,11 @@ class Distribution: if opt != '__name__': opts[opt] = (filename, parser.get(section,opt)) - from pprint import pprint - print "options (after parsing config files):" - pprint (self.command_options) + # Make the ConfigParser forget everything (so we retain + # the original filenames that options come from) -- gag, + # retch, puke -- another good reason for a distutils- + # specific config parser (sigh...) + parser.__init__() # -- Command-line parsing methods ---------------------------------- @@ -397,7 +402,7 @@ class Distribution: cmd_class.user_options) parser.set_negative_aliases (negative_opt) (args, opts) = parser.getopt (args[1:]) - if opts.help: + if hasattr(opts, 'help') and opts.help: print "showing help for command", cmd_class self._show_help(parser, display_options=0, commands=[cmd_class]) return @@ -408,7 +413,7 @@ class Distribution: self.command_options[command] = {} cmd_opts = self.command_options[command] for (name, value) in vars(opts).items(): - cmd_opts[command] = ("command line", value) + cmd_opts[name] = ("command line", value) return args @@ -605,9 +610,24 @@ class Distribution: """ cmd_obj = self.command_obj.get(command) if not cmd_obj and create: + print "Distribution.get_command_obj(): " \ + "creating '%s' command object" % command + klass = self.get_command_class(command) - cmd_obj = self.command_obj[command] = klass() - self.command_run[command] = 0 + cmd_obj = self.command_obj[command] = klass(self) + self.have_run[command] = 0 + + # Set any options that were supplied in config files + # or on the command line. (NB. support for error + # reporting is lame here: any errors aren't reported + # until 'finalize_options()' is called, which means + # we won't report the source of the error.) + options = self.command_options.get(command) + if options: + print " setting options:" + for (option, (source, value)) in options.items(): + print " %s = %s (from %s)" % (option, value, source) + setattr(cmd_obj, option, value) return cmd_obj -- cgit v1.2.1 From 9e14daca28b2a95ede58edbb6a4a87ff81ac803f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 03:53:10 +0000 Subject: Don't take advantage of OptionDummy's new "auto-initialization" feature after all -- turns out it doesn't buy us much after all... --- fancy_getopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 588c6ba7..a593354c 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -239,7 +239,7 @@ class FancyGetopt: if args is None: args = sys.argv[1:] if object is None: - object = OptionDummy(self.attr_name.values()) + object = OptionDummy() created_object = 1 else: created_object = 0 -- cgit v1.2.1 From ecde8dd1d2bee1ac529e903b23c1a874c220b846 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 03:54:16 +0000 Subject: Added some debuging output (actually moved here from dist.py) -- dump the Distribution's 'command_options' dict after parsing config files, and then after parsing the command line. --- core.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core.py b/core.py index 3eeba1df..e22db939 100644 --- a/core.py +++ b/core.py @@ -59,6 +59,8 @@ def setup (**attrs): and the command-specific options that became attributes of each command object.""" + from pprint import pprint # for debugging output + # Determine the distribution class -- either caller-supplied or # our Distribution (see below). klass = attrs.get ('distclass') @@ -75,6 +77,9 @@ def setup (**attrs): # the setup script, but be overridden by the command line. dist.parse_config_files() + print "options (after parsing config files):" + pprint (dist.command_options) + # Parse the command line; any command-line errors are the end user's # fault, so turn them into SystemExit to suppress tracebacks. try: @@ -83,6 +88,9 @@ def setup (**attrs): sys.stderr.write (usage + "\n") raise SystemExit, "error: %s" % msg + print "options (after parsing command line):" + pprint (dist.command_options) + # And finally, run all the commands found on the command line. if ok: try: -- cgit v1.2.1 From a5620289f67a4feeb2f0cb54e6c7b481da3f5ea4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 04:11:14 +0000 Subject: Fix 'get_command_obj()' so it checks if a command object has an attribute before setting it -- this will catch bad options (eg. typos) in config files. --- dist.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dist.py b/dist.py index 3ceadf19..7bdd9aa0 100644 --- a/dist.py +++ b/dist.py @@ -627,6 +627,10 @@ class Distribution: print " setting options:" for (option, (source, value)) in options.items(): print " %s = %s (from %s)" % (option, value, source) + if not hasattr(cmd_obj, option): + raise DistutilsOptionError, \ + ("%s: command '%s' has no such option '%s'") % \ + (source, command, option) setattr(cmd_obj, option, value) return cmd_obj -- cgit v1.2.1 From 9449a2faa1832da6ab9fae2b656333f6548ebfb6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 23:14:00 +0000 Subject: Catch failure to open installed Makefile, and report it as a DistutilsPlatformError: "invalid Python installation". (This will happen on Red Hat-ish systems where the python-devel package is not installed.) --- sysconfig.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 5cc71dc0..a5f3816a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -229,7 +229,17 @@ def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" g = globals() # load the installed Makefile: - parse_makefile(open(get_makefile_filename()), g) + try: + filename = get_makefile_filename() + file = open(filename) + except IOError, msg: + my_msg = "invalid Python installation: unable to open %s" % filename + if hasattr(msg, "strerror"): + my_msg = my_msg + " (%s)" % msg.strerror + + raise DistutilsPlatformError, my_msg + + parse_makefile(file, g) def _init_nt(): -- cgit v1.2.1 From 9cd0dff83db07c62d3a941dbd82cbeb8b2652bcf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 01:10:04 +0000 Subject: Normalized all the end-of-class lines. --- command/bdist_rpm.py | 2 ++ command/build.py | 2 +- command/build_clib.py | 2 +- command/build_ext.py | 2 +- command/build_py.py | 2 +- command/clean.py | 2 ++ command/install.py | 2 +- command/sdist.py | 2 +- 8 files changed, 10 insertions(+), 6 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 9c3aca3b..d10d0762 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -388,3 +388,5 @@ class bdist_rpm (Command): 'list or tuple of strings' % var_name) else: return default_value + +# class bdist_rpm diff --git a/command/build.py b/command/build.py index 7d753b11..aab0d6f1 100644 --- a/command/build.py +++ b/command/build.py @@ -100,4 +100,4 @@ class build (Command): if self.distribution.has_ext_modules(): self.run_peer ('build_ext') -# end class Build +# class build diff --git a/command/build_clib.py b/command/build_clib.py index 681cd76b..dba9a40a 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -207,4 +207,4 @@ class build_clib (Command): # build_libraries () -# class BuildLib +# class build_lib diff --git a/command/build_ext.py b/command/build_ext.py index aa9ea0d0..4fb51ac7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -406,4 +406,4 @@ class build_ext (Command): return apply (os.path.join, ext_path) + '_d.lib' return apply (os.path.join, ext_path) + '.lib' -# class BuildExt +# class build_ext diff --git a/command/build_py.py b/command/build_py.py index 2a1fdd62..72c6157c 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -322,4 +322,4 @@ class build_py (Command): # build_packages () -# end class BuildPy +# class build_py diff --git a/command/clean.py b/command/clean.py index d6999060..62307a19 100644 --- a/command/clean.py +++ b/command/clean.py @@ -74,3 +74,5 @@ class clean (Command): self.announce ("removing '%s'" % self.build_base) except OSError: pass + +# class clean diff --git a/command/install.py b/command/install.py index 392fe507..5398175f 100644 --- a/command/install.py +++ b/command/install.py @@ -519,4 +519,4 @@ class install (Command): "installations)") % filename) -# class Install +# class install diff --git a/command/sdist.py b/command/sdist.py index f644e9fb..56bc4c95 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -519,7 +519,7 @@ class sdist (Command): if not self.keep_tree: remove_tree (base_dir, self.verbose, self.dry_run) -# class Dist +# class sdist # ---------------------------------------------------------------------- -- cgit v1.2.1 From 6a76fd2a072f87312fcd0affe20e4715d4ec0a98 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 01:19:18 +0000 Subject: Bastian Kleineidam: the "build_scripts" command and changes necessary to support it. Details: - build command additionally calls build_scripts - build_scripts builds your scripts in 'build/scripts' and adjusts the first line if it begins with "#!" and ends with "python", optionally ending with commandline options (like -O, -t ...). Adjusting means we write the current path to the Python interpreter in the first line. - install_scripts copies the scripts to the install_scripts dir - install_data copies your data_files in install_data. You can supply individual directories for your data_files: data_files = ['doc/info.txt', # copy this file in install_scripts dir ('testdata', ['a.dat', 'b.dat']), # copy these files in # install_scripts/testdata ('/etc', ['packagerc']), # copy this in /etc. When --root is # given, copy this in rootdir/etc ] So you can use the --root option with absolute data paths. --- command/__init__.py | 1 + command/build.py | 8 ++++++++ command/install_data.py | 45 ++++++++++++++++++++++++++++++++++++++++----- command/install_scripts.py | 31 +++++++++++++++++++++++++------ 4 files changed, 74 insertions(+), 11 deletions(-) diff --git a/command/__init__.py b/command/__init__.py index cd7753fe..229c8a34 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -9,6 +9,7 @@ __all__ = ['build', 'build_py', 'build_ext', 'build_clib', + 'build_scripts', 'install', 'install_lib', 'install_scripts', diff --git a/command/build.py b/command/build.py index aab0d6f1..96d41d5e 100644 --- a/command/build.py +++ b/command/build.py @@ -24,6 +24,8 @@ class build (Command): ('build-lib=', None, "build directory for all distribution (defaults to either " + "build-purelib or build-platlib"), + ('build-scripts=', None, + "build directory for scripts"), ('build-temp=', 't', "temporary build directory"), ('compiler=', 'c', @@ -42,6 +44,7 @@ class build (Command): self.build_platlib = None self.build_lib = None self.build_temp = None + self.build_scripts = None self.compiler = None self.debug = None self.force = 0 @@ -76,6 +79,8 @@ class build (Command): if self.build_temp is None: self.build_temp = os.path.join (self.build_base, 'temp.' + self.plat) + if self.build_scripts is None: + self.build_scripts = os.path.join (self.build_base, 'scripts') # finalize_options () @@ -100,4 +105,7 @@ class build (Command): if self.distribution.has_ext_modules(): self.run_peer ('build_ext') + if self.distribution.scripts: + self.run_peer ('build_scripts') + # class build diff --git a/command/install_data.py b/command/install_data.py index fd9836b6..65d188f7 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -7,17 +7,52 @@ platform-independent data files.""" __revision__ = "$Id$" -from distutils.cmd import install_misc +import os +from types import StringType +from distutils.core import Command -class install_data (install_misc): +class install_data (Command): description = "install data files" + user_options = [ + ('install-dir=', 'd', + "directory to install the files to"), + ('root=', None, + "install everything relative to this alternate root directory"), + ] + + def initialize_options (self): + self.install_dir = None + self.outfiles = None + self.root = None + self.data_files = self.distribution.data_files + def finalize_options (self): - self._install_dir_from('install_data') + self.set_undefined_options('install', + ('install_data', 'install_dir'), + ('root', 'root'), + ) def run (self): - self._copy_files(self.distribution.data_files) + self.mkpath(self.install_dir) + for f in self.data_files: + if type(f) == StringType: + # its a simple file, so copy it + self.copy_file(f, self.install_dir) + else: + # its a tuple with path to install to and a list of files + dir = f[0] + if not os.path.isabs(dir): + dir = os.path.join(self.install_dir, dir) + elif self.root: + dir = os.path.join(self.root, dir[1:]) + self.mkpath(dir) + for data in f[1]: + self.copy_file(data, dir) def get_inputs (self): - return self.distribution.data_files or [] + return self.data_files or [] + + def get_outputs (self): + return self.outfiles diff --git a/command/install_scripts.py b/command/install_scripts.py index 43e5fc18..9b783263 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -8,23 +8,39 @@ Python scripts.""" __revision__ = "$Id$" import os -from distutils.cmd import install_misc +from distutils.core import Command from stat import ST_MODE -class install_scripts(install_misc): +class install_scripts(Command): description = "install scripts" + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ('build-dir=','b', "build directory (where to install from)"), + ('skip-build', None, "skip the build steps"), + ] + + def initialize_options (self): + self.install_dir = None + self.build_dir = None + self.skip_build = None + def finalize_options (self): - self._install_dir_from('install_scripts') + self.set_undefined_options('build', ('build_scripts', 'build_dir')) + self.set_undefined_options ('install', + ('install_scripts', 'install_dir'), + ('skip_build', 'skip_build'), + ) def run (self): - self._copy_files(self.distribution.scripts) + if not self.skip_build: + self.run_peer('build_scripts') + self.outfiles = self.copy_tree (self.build_dir, self.install_dir) if os.name == 'posix': # Set the executable bits (owner, group, and world) on # all the scripts we just installed. - files = self.get_outputs() - for file in files: + for file in self.get_outputs(): if self.dry_run: self.announce("changing mode of %s" % file) else: @@ -35,4 +51,7 @@ class install_scripts(install_misc): def get_inputs (self): return self.distribution.scripts or [] + def get_outputs(self): + return self.outfiles or [] + # class install_scripts -- cgit v1.2.1 From a561b7d0b579bb9f7bdf5e861daf54b20145a8c1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 01:20:15 +0000 Subject: Bastian Kleineidam: the "build_scripts" command. --- command/build_scripts.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 command/build_scripts.py diff --git a/command/build_scripts.py b/command/build_scripts.py new file mode 100644 index 00000000..1e7279d0 --- /dev/null +++ b/command/build_scripts.py @@ -0,0 +1,71 @@ +"""distutils.command.build_scripts + +Implements the Distutils 'build_scripts' command.""" + +# created 2000/05/23, Bastian Kleineidam + +__revision__ = "$Id$" + +import sys,os,re +from distutils.core import Command + +# check if Python is called on the first line with this expression +first_line_re = re.compile(r"^#!.+python(\s-\w+)*") + +class build_scripts (Command): + + description = "\"build\" scripts" + + user_options = [ + ('build-dir=', 'd', "directory to \"build\" (copy) to"), + ('force', 'f', "forcibly build everything (ignore file timestamps"), + ] + + + def initialize_options (self): + self.build_dir = None + self.scripts = None + self.force = None + self.outfiles = None + + def finalize_options (self): + self.set_undefined_options ('build', + ('build_scripts', 'build_dir'), + ('force', 'force')) + self.scripts = self.distribution.scripts + + + def run (self): + if not self.scripts: + return + self._copy_files() + self._adjust_files() + + def _copy_files(self): + """Copy all the scripts to the build dir""" + self.outfiles = [] + self.mkpath(self.build_dir) + for f in self.scripts: + print self.build_dir + if self.copy_file(f, self.build_dir): + self.outfiles.append(os.path.join(self.build_dir, f)) + + def _adjust_files(self): + """If the first line begins with #! and ends with python + replace it with the current python interpreter""" + for f in self.outfiles: + if not self.dry_run: + data = open(f, "r").readlines() + if not data: + self.warn("%s is an empty file!" % f) + continue + mo = first_line_re.match(data[0]) + if mo: + self.announce("Adjusting first line of file %s" % f) + data[0] = "#!"+sys.executable + # add optional command line options + if mo.group(1): + data[0] = data[0] + mo.group(1) + else: + data[0] = data[0] + "\n" + open(f, "w").writelines(data) -- cgit v1.2.1 From dec17861b3cc97457f3eef132a26ebe5c55d976a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 01:21:54 +0000 Subject: Use Distribution's 'has_scripts()' method instead of directly accessing its 'scripts' attribute. --- command/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build.py b/command/build.py index 96d41d5e..d9eed768 100644 --- a/command/build.py +++ b/command/build.py @@ -105,7 +105,7 @@ class build (Command): if self.distribution.has_ext_modules(): self.run_peer ('build_ext') - if self.distribution.scripts: + if self.distribution.has_scripts(): self.run_peer ('build_scripts') # class build -- cgit v1.2.1 From ef17c6ecdc8fcc0285ed78c359093824cc040d50 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 01:26:23 +0000 Subject: Added comment to remind us of the (temporary?) obsolescense of the 'install_misc' class. --- cmd.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd.py b/cmd.py index 0cf88d41..09727331 100644 --- a/cmd.py +++ b/cmd.py @@ -344,6 +344,11 @@ class Command: # class Command +# XXX 'install_misc' class not currently used -- it was the base class for +# both 'install_scripts' and 'install_data', but they outgrew it. It might +# still be useful for 'install_headers', though, so I'm keeping it around +# for the time being. + class install_misc (Command): """Common base class for installing some files in a subdirectory. Currently used by install_data and install_scripts. -- cgit v1.2.1 From eaf0c26b00302e889666ceeff1ebfddf519cef59 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 02:03:56 +0000 Subject: Improvements to Bastian's build_scripts command: * 'first_line_re' loosened up * command description improved * replaced '_copy_files()' and '_adjust_files()' with one method that does everything, 'copy_scripts()' -- this should be more efficient than Bastian's version, should behave better in dry-run mode, and does timestamp dependency-checking --- command/build_scripts.py | 89 +++++++++++++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 1e7279d0..18297348 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -6,15 +6,16 @@ Implements the Distutils 'build_scripts' command.""" __revision__ = "$Id$" -import sys,os,re +import sys, os, re from distutils.core import Command +from distutils.dep_util import newer # check if Python is called on the first line with this expression -first_line_re = re.compile(r"^#!.+python(\s-\w+)*") +first_line_re = re.compile(r'^#!.*python(\s+.*)?') class build_scripts (Command): - description = "\"build\" scripts" + description = "\"build\" scripts (copy and fixup #! line)" user_options = [ ('build-dir=', 'd', "directory to \"build\" (copy) to"), @@ -38,34 +39,60 @@ class build_scripts (Command): def run (self): if not self.scripts: return - self._copy_files() - self._adjust_files() + self.copy_scripts() + - def _copy_files(self): - """Copy all the scripts to the build dir""" - self.outfiles = [] + def copy_scripts (self): + """Copy each script listed in 'self.scripts'; if it's marked as a + Python script in the Unix way (first line matches 'first_line_re', + ie. starts with "\#!" and contains "python"), then adjust the first + line to refer to the current Python intepreter as we copy. + """ + outfiles = [] self.mkpath(self.build_dir) - for f in self.scripts: - print self.build_dir - if self.copy_file(f, self.build_dir): - self.outfiles.append(os.path.join(self.build_dir, f)) - - def _adjust_files(self): - """If the first line begins with #! and ends with python - replace it with the current python interpreter""" - for f in self.outfiles: - if not self.dry_run: - data = open(f, "r").readlines() - if not data: - self.warn("%s is an empty file!" % f) + for script in self.scripts: + adjust = 0 + outfile = os.path.join(self.build_dir, script) + + if not self.force and not newer(script, outfile): + self.announce("not copying %s (output up-to-date)" % script) + continue + + # Always open the file, but ignore failures in dry-run mode -- + # that way, we'll get accurate feedback if we can read the + # script. + try: + f = open(script, "r") + except IOError: + if not self.dry_run: + raise + f = None + else: + first_line = f.readline() + if not first_line: + self.warn("%s is an empty file (skipping)" % script) continue - mo = first_line_re.match(data[0]) - if mo: - self.announce("Adjusting first line of file %s" % f) - data[0] = "#!"+sys.executable - # add optional command line options - if mo.group(1): - data[0] = data[0] + mo.group(1) - else: - data[0] = data[0] + "\n" - open(f, "w").writelines(data) + + match = first_line_re.match(first_line) + if match: + adjust = 1 + post_interp = match.group(1) + + if adjust: + self.announce("copying and adjusting %s -> %s" % + (script, self.build_dir)) + if not self.dry_run: + outf = open(outfile, "w") + outf.write("#!%s%s\n" % + (os.path.normpath(sys.executable), post_interp)) + outf.writelines(f.readlines()) + outf.close() + if f: + f.close() + else: + f.close() + self.copy_file(script, outfile) + + # copy_scripts () + +# class build_scripts -- cgit v1.2.1 From 14a7a370319757cb72bc70c020c6944077f555e3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 02:14:26 +0000 Subject: Fix to use 'change_root()' rather than directly mangling path. --- command/install_data.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/install_data.py b/command/install_data.py index 65d188f7..acc89aa3 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -10,6 +10,7 @@ __revision__ = "$Id$" import os from types import StringType from distutils.core import Command +from distutils.util import change_root class install_data (Command): @@ -46,7 +47,7 @@ class install_data (Command): if not os.path.isabs(dir): dir = os.path.join(self.install_dir, dir) elif self.root: - dir = os.path.join(self.root, dir[1:]) + dir = change_root(self.root, dir) self.mkpath(dir) for data in f[1]: self.copy_file(data, dir) -- cgit v1.2.1 From 62bcf92f91582ef01086ae94e446a1aee8b82274 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 20:05:52 +0000 Subject: Take the basename of the script before concatenating it with the build dir. --- command/build_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 18297348..6467e65b 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -52,7 +52,7 @@ class build_scripts (Command): self.mkpath(self.build_dir) for script in self.scripts: adjust = 0 - outfile = os.path.join(self.build_dir, script) + outfile = os.path.join(self.build_dir, os.path.basename(script)) if not self.force and not newer(script, outfile): self.announce("not copying %s (output up-to-date)" % script) -- cgit v1.2.1 From 514f94d06bedf2f945a7ec777616e0c47ec881a3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 26 May 2000 00:44:06 +0000 Subject: Fixed a couple of long-hidden bugs (amazing what you find when you attempt to verify the bold assertions in the documentation): * entries for the "root package" in 'package_dir' didn't work -- fixed by improving the fall-through code in 'get_package_dir()' * __init__.py files weren't installed when modules-in-packages were listed individually (ie. in 'py_modules' in the setup script); fixed by making 'check_package()' return the name of the __init__ file if it exists, and making 'find_modules()' add an entry to the module list for __init__ if applicable --- command/build_py.py | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 72c6157c..92a37f20 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -117,8 +117,17 @@ class build_py (Command): tail.insert (0, pdir) return apply (os.path.join, tail) else: - # arg! everything failed, we might as well have not even - # looked in package_dir -- oh well + # Oops, got all the way through 'path' without finding a + # match in package_dir. If package_dir defines a directory + # for the root (nameless) package, then fallback on it; + # otherwise, we might as well have not consulted + # package_dir at all, as we just use the directory implied + # by 'tail' (which should be the same as the original value + # of 'path' at this point). + pdir = self.package_dir.get('') + if pdir is not None: + tail.insert(0, pdir) + if tail: return apply (os.path.join, tail) else: @@ -145,9 +154,16 @@ class build_py (Command): # Require __init__.py for all but the "root package" if package: init_py = os.path.join (package_dir, "__init__.py") - if not os.path.isfile (init_py): + if os.path.isfile (init_py): + return init_py + else: self.warn (("package init file '%s' not found " + "(or not a regular file)") % init_py) + + # Either not in a package at all (__init__.py not expected), or + # __init__.py doesn't exist -- so don't return the filename. + return + # check_package () @@ -177,6 +193,15 @@ class build_py (Command): def find_modules (self): + """Finds individually-specified Python modules, ie. those listed by + module name in 'self.modules'. Returns a list of tuples (package, + module_base, filename): 'package' is a tuple of the path through + package-space to the module; 'module_base' is the bare (no + packages, no dots) module name, and 'filename' is the path to the + ".py" file (relative to the distribution root) that implements the + module. + """ + # Map package names to tuples of useful info about the package: # (package_dir, checked) # package_dir - the directory where we'll find source files for @@ -185,7 +210,7 @@ class build_py (Command): # is valid (exists, contains __init__.py, ... ?) packages = {} - # List of (module, package, filename) tuples to return + # List of (package, module, filename) tuples to return modules = [] # We treat modules-in-packages almost the same as toplevel modules, @@ -205,8 +230,10 @@ class build_py (Command): checked = 0 if not checked: - self.check_package (package, package_dir) + init_py = self.check_package (package, package_dir) packages[package] = (package_dir, 1) + if init_py: + modules.append((package, "__init__", init_py)) # XXX perhaps we should also check for just .pyc files # (so greedy closed-source bastards can distribute Python @@ -215,7 +242,7 @@ class build_py (Command): if not self.check_module (module, module_file): continue - modules.append ((package, module, module_file)) + modules.append ((package, module_base, module_file)) return modules -- cgit v1.2.1 From a9e47e475f9420277cc20e1eccf5b67c42adf8b7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 26 May 2000 00:54:52 +0000 Subject: Added the DEBUG global (set from the DISTUTILS_DEBUG environment variable). Changed the exception-handling code in 'setup()' to re-raise exceptions if DEBUG is true. --- core.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/core.py b/core.py index e22db939..cd0f8e96 100644 --- a/core.py +++ b/core.py @@ -27,6 +27,11 @@ usage: %s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] """ % ((os.path.basename(sys.argv[0]),) * 4) +# If DISTUTILS_DEBUG is anything other than the empty string, we run in +# debug mode. +DEBUG = os.environ.get('DISTUTILS_DEBUG') + + def setup (**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: @@ -101,18 +106,26 @@ def setup (**attrs): # check for Python 1.5.2-style {IO,OS}Error exception objects if hasattr (exc, 'filename') and hasattr (exc, 'strerror'): if exc.filename: - raise SystemExit, \ - "error: %s: %s" % (exc.filename, exc.strerror) + error = "error: %s: %s" % (exc.filename, exc.strerror) else: # two-argument functions in posix module don't # include the filename in the exception object! - raise SystemExit, \ - "error: %s" % exc.strerror + error = "error: %s" % exc.strerror else: - raise SystemExit, "error: " + str(exc[-1]) + error = "error: " + str(exc[-1]) + + if DEBUG: + sys.stderr.write(error + "\n") + raise + else: + raise SystemExit, error + except (DistutilsExecError, DistutilsFileError, DistutilsOptionError), msg: - raise SystemExit, "error: " + str(msg) + if DEBUG: + raise + else: + raise SystemExit, "error: " + str(msg) # setup () -- cgit v1.2.1 From bab7e2589a1ecb58045e27f6bce0938d7fee5377 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 26 May 2000 01:00:15 +0000 Subject: Factored out code for extracting-or-creating one of the option dictionaries in 'self.command_options' to 'get_option_dict()'. Simplified code in 'parse_config_files()' and 'parse_command_line()' accordingly. Fixed code in constructor that processes the 'options' dictionary from the setup script so it actually works: uses the new 'self.command_options' dictionary rather than creating command objects and calling 'set_option()' on them. --- dist.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/dist.py b/dist.py index 7bdd9aa0..33b3b657 100644 --- a/dist.py +++ b/dist.py @@ -185,11 +185,9 @@ class Distribution: if options: del attrs['options'] for (command, cmd_options) in options.items(): - cmd_obj = self.get_command_obj (command) - for (key, val) in cmd_options.items(): - cmd_obj.set_option (key, val) - # loop over commands - # if any command options + opt_dict = self.get_option_dict(command) + for (opt, val) in cmd_options.items(): + opt_dict[opt] = ("setup script", val) # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! @@ -205,6 +203,19 @@ class Distribution: # __init__ () + def get_option_dict (self, command): + """Get the option dictionary for a given command. If that + command's option dictionary hasn't been created yet, then create it + and return the new dictionary; otherwise, return the existing + option dictionary. + """ + + dict = self.command_options.get(command) + if dict is None: + dict = self.command_options[command] = {} + return dict + + # -- Config file finding/parsing methods --------------------------- def find_config_files (self): @@ -266,13 +277,11 @@ class Distribution: parser.read(filename) for section in parser.sections(): options = parser.options(section) - if not self.command_options.has_key(section): - self.command_options[section] = {} - opts = self.command_options[section] + opt_dict = self.get_option_dict(section) for opt in options: if opt != '__name__': - opts[opt] = (filename, parser.get(section,opt)) + opt_dict[opt] = (filename, parser.get(section,opt)) # Make the ConfigParser forget everything (so we retain # the original filenames that options come from) -- gag, @@ -409,11 +418,9 @@ class Distribution: # Put the options from the command-line into their official # holding pen, the 'command_options' dictionary. - if not self.command_options.has_key(command): - self.command_options[command] = {} - cmd_opts = self.command_options[command] + opt_dict = self.get_option_dict(command) for (name, value) in vars(opts).items(): - cmd_opts[name] = ("command line", value) + opt_dict[name] = ("command line", value) return args -- cgit v1.2.1 From b960e574912ef3567a40ec67e3684d2f07d772a8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 26 May 2000 01:31:53 +0000 Subject: Rene Liebscher: check if the extension file (.so or .pyd) is up-to-date with respect to the source files; that way, we don't needlessly rebuild just because object files go away. --- command/build_ext.py | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 4fb51ac7..acf1d7b7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -12,7 +12,7 @@ import sys, os, string, re from types import * from distutils.core import Command from distutils.errors import * - +from distutils.dep_util import newer_group # An extension name is just a dot-separated list of Python NAMEs (ie. # the same as a fully-qualified module name). @@ -285,7 +285,29 @@ class build_ext (Command): "a list of source filenames") % extension_name sources = list (sources) - self.announce ("building '%s' extension" % extension_name) + fullname = self.get_ext_fullname (extension_name) + if self.inplace: + # ignore build-lib -- put the compiled extension into + # the source tree along with pure Python modules + + modpath = string.split (fullname, '.') + package = string.join (modpath[0:-1], '.') + base = modpath[-1] + + build_py = self.find_peer ('build_py') + package_dir = build_py.get_package_dir (package) + ext_filename = os.path.join (package_dir, + self.get_ext_filename(base)) + else: + ext_filename = os.path.join (self.build_lib, + self.get_ext_filename(fullname)) + + if not newer_group(sources, ext_filename, 'newer'): + self.announce ("skipping '%s' extension (up-to-date)" % + extension_name) + continue # 'for' loop over all extensions + else: + self.announce ("building '%s' extension" % extension_name) # First step: compile the source code to object files. This # drops the object files in the current directory, regardless @@ -357,23 +379,6 @@ class build_ext (Command): self.mkpath (os.path.dirname (implib_file)) # if MSVC - fullname = self.get_ext_fullname (extension_name) - if self.inplace: - # ignore build-lib -- put the compiled extension into - # the source tree along with pure Python modules - - modpath = string.split (fullname, '.') - package = string.join (modpath[0:-1], '.') - base = modpath[-1] - - build_py = self.find_peer ('build_py') - package_dir = build_py.get_package_dir (package) - ext_filename = os.path.join (package_dir, - self.get_ext_filename(base)) - else: - ext_filename = os.path.join (self.build_lib, - self.get_ext_filename(fullname)) - self.compiler.link_shared_object (objects, ext_filename, libraries=libraries, library_dirs=library_dirs, -- cgit v1.2.1 From 4fb6d85e29cc25c036f2ecf88b746dd7de51387d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 01:25:16 +0000 Subject: Added 'install_headers' command to install C/C++ header files. --- command/__init__.py | 1 + command/install_headers.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 command/install_headers.py diff --git a/command/__init__.py b/command/__init__.py index 229c8a34..56c26fe1 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -12,6 +12,7 @@ __all__ = ['build', 'build_scripts', 'install', 'install_lib', + 'install_headers', 'install_scripts', 'install_data', 'clean', diff --git a/command/install_headers.py b/command/install_headers.py new file mode 100644 index 00000000..33edb945 --- /dev/null +++ b/command/install_headers.py @@ -0,0 +1,40 @@ +"""distutils.command.install_headers + +Implements the Distutils 'install_headers' command, to install C/C++ header +files to the Python include directory.""" + +# created 2000/05/26, Greg Ward + +__revision__ = "$Id$" + +from distutils.core import Command + + +class install_headers (Command): + + description = "install C/C++ header files" + + user_options = [('install-dir=', 'd', + "directory to install header files to"), + ] + + + def initialize_options (self): + self.install_dir = None + + def finalize_options (self): + self.set_undefined_options('install', + ('install_headers', 'install_dir')) + + def run (self): + headers = self.distribution.headers + if not headers: + return + + self.mkpath(self.install_dir) + for header in headers: + self.copy_file(header, self.install_dir) + + # run() + +# class install_headers -- cgit v1.2.1 From 2f4fee98094cc689a133ac05186a61fdffc0ea7f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 01:33:12 +0000 Subject: Support for the "install_headers" command: * 'headers' entry added to all the install schemes * '--install-headers' option added * 'install_headers' added to 'sub_commands' * added 'dist_name' to configuration variables (along with a few others that seem handy: 'dist_version', 'dist_fullname', and 'py_version' * in 'finalize_unix()', make sure 'install_headers' defined if user specified 'install_base' and/or 'install_platbase' * added 'has_headers()' * a few other small changes --- command/install.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/command/install.py b/command/install.py index 5398175f..d89ce4d2 100644 --- a/command/install.py +++ b/command/install.py @@ -18,24 +18,28 @@ INSTALL_SCHEMES = { 'unix_prefix': { 'purelib': '$base/lib/python$py_version_short/site-packages', 'platlib': '$platbase/lib/python$py_version_short/site-packages', + 'headers': '$base/include/python/$py_version_short/$dist_name', 'scripts': '$base/bin', 'data' : '$base/share', }, 'unix_home': { 'purelib': '$base/lib/python', 'platlib': '$base/lib/python', + 'headers': '$base/include/python/$dist_name', 'scripts': '$base/bin', 'data' : '$base/share', }, 'nt': { 'purelib': '$base', 'platlib': '$base', + 'headers': '$base\\Include\\$dist_name', 'scripts': '$base\\Scripts', 'data' : '$base\\Data', }, 'mac': { 'purelib': '$base:Lib', 'platlib': '$base:Mac:PlugIns', + 'headers': '$base:Include:$dist_name', 'scripts': '$base:Scripts', 'data' : '$base:Data', } @@ -73,6 +77,8 @@ class install (Command): "installation directory for all module distributions " + "(overrides --install-purelib and --install-platlib)"), + ('install-headers=', None, + "installation directory for C/C++ headers"), ('install-scripts=', None, "installation directory for Python scripts"), ('install-data=', None, @@ -99,6 +105,7 @@ class install (Command): # true if 'command' (the sub-command name, a string) needs to be run. # If 'method' is None, assume that 'command' must always be run. sub_commands = [('has_lib', 'install_lib'), + ('has_headers', 'install_headers'), ('has_scripts', 'install_scripts'), ('has_data', 'install_data'), ] @@ -125,6 +132,7 @@ class install (Command): # that installation scheme. self.install_purelib = None # for pure module distributions self.install_platlib = None # non-pure (dists w/ extensions) + self.install_headers = None # for C/C++ headers self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None @@ -200,21 +208,26 @@ class install (Command): # install_{purelib,platlib,lib,scripts,data,...}, and the # INSTALL_SCHEME dictionary above. Phew! - self.dump_dirs ("pre-finalize_xxx") + self.dump_dirs ("pre-finalize_{unix,other}") if os.name == 'posix': self.finalize_unix () else: self.finalize_other () - self.dump_dirs ("post-finalize_xxx()") + self.dump_dirs ("post-finalize_{unix,other}()") # Expand configuration variables, tilde, etc. in self.install_base # and self.install_platbase -- that way, we can use $base or # $platbase in the other installation directories and not worry # about needing recursive variable expansion (shudder). - self.config_vars = {'py_version_short': sys.version[0:3], + py_version = (string.split(sys.version))[0] + self.config_vars = {'dist_name': self.distribution.get_name(), + 'dist_version': self.distribution.get_version(), + 'dist_fullname': self.distribution.get_fullname(), + 'py_version': py_version, + 'py_version_short': py_version[0:3], 'sys_prefix': sysconfig.PREFIX, 'sys_exec_prefix': sysconfig.EXEC_PREFIX, } @@ -295,12 +308,12 @@ class install (Command): if ((self.install_lib is None and self.install_purelib is None and self.install_platlib is None) or + self.install_headers is None or self.install_scripts is None or self.install_data is None): raise DistutilsOptionError, \ "install-base or install-platbase supplied, but " + \ "installation scheme is incomplete" - return if self.home is not None: @@ -359,7 +372,7 @@ class install (Command): def select_scheme (self, name): # it's the caller's problem if they supply a bad name! scheme = INSTALL_SCHEMES[name] - for key in ('purelib', 'platlib', 'scripts', 'data'): + for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): attrname = 'install_' + key if getattr(self, attrname) is None: setattr(self, attrname, scheme[key]) @@ -384,6 +397,7 @@ class install (Command): self._expand_attrs (['install_purelib', 'install_platlib', 'install_lib', + 'install_headers', 'install_scripts', 'install_data',]) @@ -478,6 +492,9 @@ class install (Command): return (self.distribution.has_pure_modules() or self.distribution.has_ext_modules()) + def has_headers (self): + return self.distribution.has_headers() + def has_scripts (self): return self.distribution.has_scripts() -- cgit v1.2.1 From 3269156427c4908f5388f8f214436797d1b41810 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 01:33:49 +0000 Subject: Tweaked description, help text. --- command/install_scripts.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/install_scripts.py b/command/install_scripts.py index 9b783263..5888cafd 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -11,12 +11,12 @@ import os from distutils.core import Command from stat import ST_MODE -class install_scripts(Command): +class install_scripts (Command): - description = "install scripts" + description = "install scripts (Python or otherwise)" user_options = [ - ('install-dir=', 'd', "directory to install to"), + ('install-dir=', 'd', "directory to install scripts to"), ('build-dir=','b', "build directory (where to install from)"), ('skip-build', None, "skip the build steps"), ] -- cgit v1.2.1 From 1b401b74ff3cac71a809383c5fde85fe31a26ce7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 01:35:27 +0000 Subject: 'mkpath()' now detects non-string 'name' arguments -- this is a fairly common bug when adding new code, so I thought I'd make it blow up earlier than deep in posix.py. --- dir_util.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dir_util.py b/dir_util.py index c049bbd2..194183ae 100644 --- a/dir_util.py +++ b/dir_util.py @@ -7,7 +7,8 @@ Utility functions for manipulating directories and directory trees.""" __revision__ = "$Id$" import os -from distutils.errors import DistutilsFileError +from types import * +from distutils.errors import DistutilsFileError, DistutilsInternalError # cache for by mkpath() -- in addition to cheapening redundant calls, @@ -29,6 +30,11 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): global PATH_CREATED + # Detect a common bug -- name is None + if type(name) is not StringType: + raise DistutilsInternalError, \ + "mkpath: 'name' must be a string (got %s)" % `name` + # XXX what's the better way to handle verbosity? print as we create # each directory in the path (the current behaviour), or only announce # the creation of the whole path? (quite easy to do the latter since -- cgit v1.2.1 From 20cf8c4a9698ff498f8ed64b06734a86f11af75c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 01:36:14 +0000 Subject: Support for the "install_headers" command: distribution option 'headers' and method 'has_headers()'. --- dist.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dist.py b/dist.py index 33b3b657..1e8c6324 100644 --- a/dist.py +++ b/dist.py @@ -146,6 +146,7 @@ class Distribution: self.package_dir = None self.py_modules = None self.libraries = None + self.headers = None self.ext_modules = None self.ext_package = None self.include_dirs = None @@ -699,6 +700,9 @@ class Distribution: def has_modules (self): return self.has_pure_modules() or self.has_ext_modules() + def has_headers (self): + return self.headers and len(self.headers) > 0 + def has_scripts (self): return self.scripts and len(self.scripts) > 0 -- cgit v1.2.1 From 0bf1c3e9874ed43aa7353c5e03884fc6775486c3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 03:03:23 +0000 Subject: Patch from Andrew Kuchling: prune out the build and source distribution directories after all is said and done, so we don't accidentally include those files in the source distribution. (This is the quick and easy way to fix this; Andrew says: "Changing findall() looked like it was going to be messy, so I tried this instead. The only problem is that redundant directory traversals are being done, walking through build/ only to throw out all the files found at the end."). --- command/sdist.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/command/sdist.py b/command/sdist.py index 56bc4c95..0a57ba36 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -440,6 +440,13 @@ class sdist (Command): # while loop over lines of template file + # Prune away the build and source distribution directories + build = self.find_peer ('build') + exclude_pattern (self.files, None, prefix=build.build_base) + + base_dir = self.distribution.get_fullname() + exclude_pattern (self.files, None, prefix=base_dir) + # read_template () -- cgit v1.2.1 From 8b3691a0dc7439ba4ce8c91bc7cd112c3ad9b49f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 17:27:23 +0000 Subject: Some far-reaching naming changes: * Command method 'find_peer()' -> 'get_finalized_command()' * Command method 'run_peer()' -> 'run_command()' Also deleted the 'get_command_option()' method from Command, and fixed the one place where it was used (in "bdist_dumb"). --- cmd.py | 30 +++++++++++----------------- command/bdist.py | 6 +++--- command/bdist_dumb.py | 10 +++++----- command/bdist_rpm.py | 49 ++++++++++++++++++---------------------------- command/build.py | 8 ++++---- command/build_ext.py | 4 ++-- command/install.py | 8 ++++---- command/install_lib.py | 10 +++++----- command/install_scripts.py | 2 +- command/sdist.py | 8 ++++---- dist.py | 2 +- 11 files changed, 59 insertions(+), 78 deletions(-) diff --git a/cmd.py b/cmd.py index 09727331..c21ea03e 100644 --- a/cmd.py +++ b/cmd.py @@ -69,11 +69,11 @@ class Command: # none of that complicated bureaucracy is needed. self.help = 0 - # 'ready' records whether or not 'finalize_options()' has been + # 'finalized' records whether or not 'finalize_options()' has been # called. 'finalize_options()' itself should not pay attention to - # this flag: it is the business of 'ensure_ready()', which always - # calls 'finalize_options()', to respect/update it. - self.ready = 0 + # this flag: it is the business of 'ensure_finalized()', which + # always calls 'finalize_options()', to respect/update it. + self.finalized = 0 # __init__ () @@ -89,10 +89,10 @@ class Command: raise AttributeError, attr - def ensure_ready (self): - if not self.ready: + def ensure_finalized (self): + if not self.finalized: self.finalize_options () - self.ready = 1 + self.finalized = 1 # Subclasses must define: @@ -184,32 +184,24 @@ class Command: # Option_pairs: list of (src_option, dst_option) tuples src_cmd_obj = self.distribution.get_command_obj (src_cmd) - src_cmd_obj.ensure_ready () + src_cmd_obj.ensure_finalized () for (src_option, dst_option) in option_pairs: if getattr (self, dst_option) is None: setattr (self, dst_option, getattr (src_cmd_obj, src_option)) - def find_peer (self, command, create=1): + def get_finalized_command (self, command, create=1): """Wrapper around Distribution's 'get_command_obj()' method: find (create if necessary and 'create' is true) the command object for 'command'..""" cmd_obj = self.distribution.get_command_obj (command, create) - cmd_obj.ensure_ready () + cmd_obj.ensure_finalized () return cmd_obj - def get_peer_option (self, command, option): - """Find or create the command object for 'command', and return - its 'option' option.""" - - cmd_obj = self.find_peer (command) - return getattr(cmd_obj, option) - - - def run_peer (self, command): + def run_command (self, command): """Run some other command: uses the 'run_command()' method of Distribution, which creates the command object if necessary and then invokes its 'run()' method.""" diff --git a/command/bdist.py b/command/bdist.py index 892712ec..df4e3a03 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -53,7 +53,7 @@ class bdist (Command): # temporary directories (eg. we'll probably have # "build/bdist./dumb", "build/bdist./rpm", etc.) if self.bdist_base is None: - build_base = self.find_peer('build').build_base + build_base = self.get_finalized_command('build').build_base plat = get_platform() self.bdist_base = os.path.join (build_base, 'bdist.' + plat) @@ -79,9 +79,9 @@ class bdist (Command): "invalid archive format '%s'" % self.format if cmd_name not in self.no_format_option: - sub_cmd = self.find_peer (cmd_name) + sub_cmd = self.get_finalized_command (cmd_name) sub_cmd.format = self.format - self.run_peer (cmd_name) + self.run_command (cmd_name) # run() diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index eaa19273..51e35e4b 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -40,7 +40,7 @@ class bdist_dumb (Command): def finalize_options (self): if self.bdist_dir is None: - bdist_base = self.get_peer_option('bdist', 'bdist_base') + bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'dumb') if self.format is None: @@ -56,10 +56,10 @@ class bdist_dumb (Command): def run (self): - self.run_peer ('build') + self.run_command ('build') - # XXX don't use 'self.find_peer()', because it always runs - # 'ensure_ready()' on the command object; we explictly want a + # XXX don't use 'self.get_finalized_command()', because it always runs + # 'ensure_finalized()' on the command object; we explictly want a # command object that has *not* been finalized, so we can set # options on it! (The option we set, 'root', is so that we can do # a proper "fake install" using this install command object.) @@ -67,7 +67,7 @@ class bdist_dumb (Command): install.root = self.bdist_dir self.announce ("installing to %s" % self.bdist_dir) - install.ensure_ready() + install.ensure_finalized() install.run() # And make an archive relative to the root of the diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index d10d0762..d07f9249 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -1,20 +1,17 @@ """distutils.command.bdist_rpm Implements the Distutils 'bdist_rpm' command (create RPM source and binary -distributions.""" +distributions).""" # created 2000/04/25, by Harry Henry Gebel __revision__ = "$Id$" -from os.path import exists, basename -import os +import os, string +from types import * from distutils.core import Command from distutils.util import mkpath, write_file, copy_file from distutils.errors import * -from string import join, lower -from types import StringType, DictType, LongType, FloatType, IntType, \ - ListType, TupleType class bdist_rpm (Command): @@ -68,23 +65,15 @@ class bdist_rpm (Command): # make directories if self.spec_only: - self.execute(mkpath, ('redhat',), "Created './redhat' directory") + self.mkpath('redhat') else: - self.execute(mkpath, ('build/rpm/SOURCES',), - "Created RPM source directory") - self.execute(mkpath, ('build/rpm/SPECS',), - "Created RPM source directory") - self.execute(mkpath, ('build/rpm/BUILD',), - "Created RPM source directory") - self.execute(mkpath, ('build/rpm/RPMS',), - "Created RPM source directory") - self.execute(mkpath, ('build/rpm/SRPMS',), - "Created RPM source directory") + for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): + self.mkpath(os.path.join('build/rpm', d)) # spec file goes into .redhat directory if '--spec-only specified', - # into build/rpm/spec otherwisu + # into build/rpm/spec otherwise if self.spec_only: - spec_path = './redhat/%s.spec' % self.distribution.get_name() + spec_path = 'redhat/%s.spec' % self.distribution.get_name() else: spec_path = ('build/rpm/SPECS/%s.spec' % self.distribution.get_name()) @@ -98,12 +87,12 @@ class bdist_rpm (Command): # make a source distribution and copy to SOURCES directory with # optional icon - sdist = self.find_peer ('sdist') + sdist = self.get_finalized_command ('sdist') if self.use_bzip2: sdist.formats = ['bztar'] else: sdist.formats = ['gztar'] - self.run_peer('sdist') + self.run_command('sdist') if self.use_bzip2: source = self.distribution.get_fullname() + '.tar.bz2' else: @@ -111,7 +100,7 @@ class bdist_rpm (Command): self.execute(copy_file, (source, 'build/rpm/SOURCES'), 'Copying source distribution to SOURCES') if self.icon: - if exists(self.icon): + if os.path.exists(self.icon): self.execute(copy_file, (self.icon, 'build/rpm/SOURCES'), 'Copying icon to SOURCES') else: @@ -144,10 +133,12 @@ class bdist_rpm (Command): DistributionMetadata class, then from the package_data file, which is Python code read with execfile() ''' + from string import join + package_type = 'rpm' # read in package data, if any - if exists('package_data'): + if os.path.exists('package_data'): try: exec(open('package_data')) except: @@ -195,7 +186,7 @@ class bdist_rpm (Command): self.doc = self._check_string_list('doc') if type(self.doc) == ListType: for readme in ('README', 'README.txt'): - if exists(readme) and readme not in self.doc: + if os.path.exists(readme) and readme not in self.doc: self.doc.append(readme) self.doc = join(self.doc) self.provides = join(self._check_string_list('provides')) @@ -246,9 +237,9 @@ class bdist_rpm (Command): 'Conflicts', 'Obsoletes', ): - if getattr(self, lower(field)): - spec_file.append('%s: %s' % (field, getattr(self, - lower(field)))) + if getattr(self, string.lower(field)): + spec_file.append('%s: %s' % + (field, getattr(self, string.lower(field)))) if self.distribution.get_url() != 'UNKNOWN': spec_file.append('Url: ' + self.distribution.get_url()) @@ -260,7 +251,7 @@ class bdist_rpm (Command): spec_file.append('BuildRequires: ' + self.build_requires) if self.icon: - spec_file.append('Icon: ' + basename(self.icon)) + spec_file.append('Icon: ' + os.path.basename(self.icon)) spec_file.extend([ '', @@ -388,5 +379,3 @@ class bdist_rpm (Command): 'list or tuple of strings' % var_name) else: return default_value - -# class bdist_rpm diff --git a/command/build.py b/command/build.py index d9eed768..b0894b83 100644 --- a/command/build.py +++ b/command/build.py @@ -92,20 +92,20 @@ class build (Command): # Invoke the 'build_py' command to "build" pure Python modules # (ie. copy 'em into the build tree) if self.distribution.has_pure_modules(): - self.run_peer ('build_py') + self.run_command ('build_py') # Build any standalone C libraries next -- they're most likely to # be needed by extension modules, so obviously have to be done # first! if self.distribution.has_c_libraries(): - self.run_peer ('build_clib') + self.run_command ('build_clib') # And now 'build_ext' -- compile extension modules and put them # into the build tree if self.distribution.has_ext_modules(): - self.run_peer ('build_ext') + self.run_command ('build_ext') if self.distribution.has_scripts(): - self.run_peer ('build_scripts') + self.run_command ('build_scripts') # class build diff --git a/command/build_ext.py b/command/build_ext.py index acf1d7b7..cef3ca82 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -167,7 +167,7 @@ class build_ext (Command): # directory where we put them is in the library search path for # linking extensions. if self.distribution.has_c_libraries(): - build_clib = self.find_peer ('build_clib') + build_clib = self.get_finalized_command ('build_clib') self.libraries.extend (build_clib.get_library_names() or []) self.library_dirs.append (build_clib.build_clib) @@ -294,7 +294,7 @@ class build_ext (Command): package = string.join (modpath[0:-1], '.') base = modpath[-1] - build_py = self.find_peer ('build_py') + build_py = self.get_finalized_command ('build_py') package_dir = build_py.get_package_dir (package) ext_filename = os.path.join (package_dir, self.get_ext_filename(base)) diff --git a/command/install.py b/command/install.py index d89ce4d2..bc5d8f33 100644 --- a/command/install.py +++ b/command/install.py @@ -454,11 +454,11 @@ class install (Command): # Obviously have to build before we can install if not self.skip_build: - self.run_peer ('build') + self.run_command ('build') # Run all sub-commands (at least those that need to be run) for cmd_name in self.get_sub_commands(): - self.run_peer (cmd_name) + self.run_command (cmd_name) if self.path_file: self.create_path_file () @@ -507,7 +507,7 @@ class install (Command): # get the outputs of all its sub-commands. outputs = [] for cmd_name in self.get_sub_commands(): - cmd = self.find_peer (cmd_name) + cmd = self.get_finalized_command (cmd_name) outputs.extend (cmd.get_outputs()) return outputs @@ -517,7 +517,7 @@ class install (Command): # XXX gee, this looks familiar ;-( inputs = [] for cmd_name in self.get_sub_commands(): - cmd = self.find_peer (cmd_name) + cmd = self.get_finalized_command (cmd_name) inputs.extend (cmd.get_inputs()) return inputs diff --git a/command/install_lib.py b/command/install_lib.py index e9cd80db..d866d8cc 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -46,9 +46,9 @@ class install_lib (Command): # Make sure we have built everything we need first if not self.skip_build: if self.distribution.has_pure_modules(): - self.run_peer ('build_py') + self.run_command ('build_py') if self.distribution.has_ext_modules(): - self.run_peer ('build_ext') + self.run_command ('build_ext') # Install everything: simply dump the entire contents of the build # directory to the installation directory (that's the beauty of @@ -85,7 +85,7 @@ class install_lib (Command): if not has_any: return [] - build_cmd = self.find_peer (build_cmd) + build_cmd = self.get_finalized_command (build_cmd) build_files = build_cmd.get_outputs() build_dir = getattr (build_cmd, cmd_option) @@ -138,11 +138,11 @@ class install_lib (Command): inputs = [] if self.distribution.has_pure_modules(): - build_py = self.find_peer ('build_py') + build_py = self.get_finalized_command ('build_py') inputs.extend (build_py.get_outputs()) if self.distribution.has_ext_modules(): - build_ext = self.find_peer ('build_ext') + build_ext = self.get_finalized_command ('build_ext') inputs.extend (build_ext.get_outputs()) return inputs diff --git a/command/install_scripts.py b/command/install_scripts.py index 5888cafd..3eb3963c 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -35,7 +35,7 @@ class install_scripts (Command): def run (self): if not self.skip_build: - self.run_peer('build_scripts') + self.run_command('build_scripts') self.outfiles = self.copy_tree (self.build_dir, self.install_dir) if os.name == 'posix': # Set the executable bits (owner, group, and world) on diff --git a/command/sdist.py b/command/sdist.py index 0a57ba36..6626b9e6 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -223,15 +223,15 @@ class sdist (Command): self.files.extend (files) if self.distribution.has_pure_modules(): - build_py = self.find_peer ('build_py') + build_py = self.get_finalized_command ('build_py') self.files.extend (build_py.get_source_files ()) if self.distribution.has_ext_modules(): - build_ext = self.find_peer ('build_ext') + build_ext = self.get_finalized_command ('build_ext') self.files.extend (build_ext.get_source_files ()) if self.distribution.has_c_libraries(): - build_clib = self.find_peer ('build_clib') + build_clib = self.get_finalized_command ('build_clib') self.files.extend (build_clib.get_source_files ()) @@ -441,7 +441,7 @@ class sdist (Command): # while loop over lines of template file # Prune away the build and source distribution directories - build = self.find_peer ('build') + build = self.get_finalized_command ('build') exclude_pattern (self.files, None, prefix=build.build_base) base_dir = self.distribution.get_fullname() diff --git a/dist.py b/dist.py index 1e8c6324..ece23a6b 100644 --- a/dist.py +++ b/dist.py @@ -681,7 +681,7 @@ class Distribution: self.announce ("running " + command) cmd_obj = self.get_command_obj (command) - cmd_obj.ensure_ready () + cmd_obj.ensure_finalized () cmd_obj.run () self.have_run[command] = 1 -- cgit v1.2.1 From e98b1cb14ebf43c54f0ec74be9483e02d69d900e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 28 May 2000 23:47:00 +0000 Subject: Moved warnings out of 'finalize_options()' into 'run()'. Added a warning for 'bdist_base' directory. --- command/clean.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/command/clean.py b/command/clean.py index 62307a19..31147b58 100644 --- a/command/clean.py +++ b/command/clean.py @@ -34,13 +34,6 @@ class clean (Command): self.all = None def finalize_options(self): - if self.build_lib and not os.path.exists (self.build_lib): - self.warn ("'%s' does not exist -- can't clean it" % - self.build_lib) - if self.build_temp and not os.path.exists (self.build_temp): - self.warn ("'%s' does not exist -- can't clean it" % - self.build_temp) - self.set_undefined_options('build', ('build_base', 'build_base'), ('build_lib', 'build_lib'), @@ -53,11 +46,21 @@ class clean (Command): # gone) if os.path.exists (self.build_temp): remove_tree (self.build_temp, self.verbose, self.dry_run) + else: + self.warn ("'%s' does not exist -- can't clean it" % + self.build_temp) + + + if self.all: # remove the module build directory (unless already gone) if os.path.exists (self.build_lib): remove_tree (self.build_lib, self.verbose, self.dry_run) + else: + self.warn ("'%s' does not exist -- can't clean it" % + self.build_lib) + # remove the temporary directory used for creating built # distributions (default "build/bdist") -- eg. type of # built distribution will have its own subdirectory under @@ -65,6 +68,9 @@ class clean (Command): # 'remove_tree()'. if os.path.exists (self.bdist_base): remove_tree (self.bdist_base, self.verbose, self.dry_run) + else: + self.warn ("'%s' does not exist -- can't clean it" % + self.bdist_base) # just for the heck of it, try to remove the base build directory: # we might have emptied it right now, but if not we don't care -- cgit v1.2.1 From 5988ea06ee6f64f6b1da4397b66b1ea9b227484c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 28 May 2000 23:47:31 +0000 Subject: Only print debugging output if DEBUG (imported from distutils.core) is true. --- command/install.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/command/install.py b/command/install.py index bc5d8f33..13fa88ee 100644 --- a/command/install.py +++ b/command/install.py @@ -8,7 +8,7 @@ __revision__ = "$Id$" import sys, os, string from types import * -from distutils.core import Command +from distutils.core import Command, DEBUG from distutils import sysconfig from distutils.util import write_file, native_path, subst_vars, change_root from distutils.errors import DistutilsOptionError @@ -240,9 +240,10 @@ class install (Command): self.config_vars['base'] = self.install_base self.config_vars['platbase'] = self.install_platbase - from pprint import pprint - print "config vars:" - pprint (self.config_vars) + if DEBUG: + from pprint import pprint + print "config vars:" + pprint (self.config_vars) # Expand "~" and configuration variables in the installation # directories. @@ -289,17 +290,17 @@ class install (Command): # finalize_options () - # hack for debugging output def dump_dirs (self, msg): - from distutils.fancy_getopt import longopt_xlate - print msg + ":" - for opt in self.user_options: - opt_name = opt[0] - if opt_name[-1] == "=": - opt_name = opt_name[0:-1] - opt_name = string.translate (opt_name, longopt_xlate) - val = getattr (self, opt_name) - print " %s: %s" % (opt_name, val) + if DEBUG: + from distutils.fancy_getopt import longopt_xlate + print msg + ":" + for opt in self.user_options: + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + opt_name = string.translate (opt_name, longopt_xlate) + val = getattr (self, opt_name) + print " %s: %s" % (opt_name, val) def finalize_unix (self): -- cgit v1.2.1 From 691c62d56f771c3a2f1f914bc44f9958ad2d7c28 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 28 May 2000 23:49:03 +0000 Subject: Changed order so 'clean' is right after the 'build' commands. --- command/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/__init__.py b/command/__init__.py index 56c26fe1..95bce8d8 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -10,12 +10,12 @@ __all__ = ['build', 'build_ext', 'build_clib', 'build_scripts', + 'clean', 'install', 'install_lib', 'install_headers', 'install_scripts', 'install_data', - 'clean', 'sdist', 'bdist', 'bdist_dumb', -- cgit v1.2.1 From b09a8fe0d18535ae8db3f8f6c980d5f2fde3e19f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 28 May 2000 23:53:06 +0000 Subject: Factored '_set_command_options()' out of 'get_command_obj()'. Added 'reinitialize_command()' -- lets us "push" option values in a controlled, safe way; this is a small change to the code, but a big change to the Distutils philosophy of passing option values around. The preferred mode is still definitely to "pull" options from another command (eg. "install" fetches the base build directory from "build"), but it is now feasible to "push" options onto another command, when you know what's best for it. One possible application will be a "config" command, which pokes around the system and pushes values (eg. include and library directories) onto the "build" command. Added 'dump_option_dicts()' method (for debugging output). --- dist.py | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/dist.py b/dist.py index ece23a6b..3391e53f 100644 --- a/dist.py +++ b/dist.py @@ -217,6 +217,35 @@ class Distribution: return dict + def dump_option_dicts (self, header=None, commands=None, indent=""): + from pprint import pformat + + if commands is None: # dump all command option dicts + commands = self.command_options.keys() + commands.sort() + + if header is not None: + print indent + header + indent = indent + " " + + if not commands: + print indent + "no commands known yet" + return + + for cmd_name in commands: + opt_dict = self.command_options.get(cmd_name) + if opt_dict is None: + print indent + "no option dict for '%s' command" % cmd_name + else: + print indent + "option dict for '%s' command:" % cmd_name + out = pformat(opt_dict) + for line in string.split(out, "\n"): + print indent + " " + line + + # dump_option_dicts () + + + # -- Config file finding/parsing methods --------------------------- def find_config_files (self): @@ -632,17 +661,62 @@ class Distribution: # we won't report the source of the error.) options = self.command_options.get(command) if options: - print " setting options:" - for (option, (source, value)) in options.items(): - print " %s = %s (from %s)" % (option, value, source) - if not hasattr(cmd_obj, option): - raise DistutilsOptionError, \ - ("%s: command '%s' has no such option '%s'") % \ - (source, command, option) - setattr(cmd_obj, option, value) + self._set_command_options(cmd_obj, options) return cmd_obj + def _set_command_options (self, command_obj, option_dict=None): + + """Set the options for 'command_obj' from 'option_dict'. Basically + this means copying elements of a dictionary ('option_dict') to + attributes of an instance ('command'). + + 'command_obj' must be a Commnd instance. If 'option_dict' is not + supplied, uses the standard option dictionary for this command + (from 'self.command_options'). + """ + from distutils.core import DEBUG + + command_name = command_obj.get_command_name() + if option_dict is None: + option_dict = self.get_option_dict(command_name) + + if DEBUG: print " setting options for '%s' command:" % command_name + for (option, (source, value)) in option_dict.items(): + if DEBUG: print " %s = %s (from %s)" % (option, value, source) + if not hasattr(command_obj, option): + raise DistutilsOptionError, \ + ("error in %s: command '%s' has no such option '%s'") % \ + (source, command_name, option) + setattr(command_obj, option, value) + + def reinitialize_command (self, command): + """Reinitializes a command to the state it was in when first + returned by 'get_command_obj()': ie., initialized but not yet + finalized. This gives provides the opportunity to sneak option + values in programmatically, overriding or supplementing + user-supplied values from the config files and command line. + You'll have to re-finalize the command object (by calling + 'finalize_options()' or 'ensure_finalized()') before using it for + real. + + 'command' should be a command name (string) or command object. + Returns the reinitialized command object. + """ + from distutils.cmd import Command + if not isinstance(command, Command): + command_name = command + command = self.get_command_obj(command_name) + else: + command_name = command.get_command_name() + + if not command.finalized: + return + command.initialize_options() + command.finalized = 0 + self._set_command_options(command) + return command + # -- Methods that operate on the Distribution ---------------------- -- cgit v1.2.1 From d7dd557d81c46cd532939cdbf4a420f34d46bb1c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 28 May 2000 23:54:00 +0000 Subject: Added 'dump_options()' for debugging output. --- cmd.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd.py b/cmd.py index c21ea03e..f8033892 100644 --- a/cmd.py +++ b/cmd.py @@ -135,6 +135,21 @@ class Command: raise RuntimeError, \ "abstract method -- subclass %s must override" % self.__class__ + + def dump_options (self, header=None, indent=""): + from distutils.fancy_getopt import longopt_xlate + if header is None: + header = "command options for '%s':" % self.get_command_name() + print indent + header + indent = indent + " " + for (option, _, _) in self.user_options: + option = string.translate(option, longopt_xlate) + if option[-1] == "=": + option = option[:-1] + value = getattr(self, option) + print indent + "%s = %s" % (option, value) + + def run (self): """A command's raison d'etre: carry out the action it exists to perform, controlled by the options initialized in -- cgit v1.2.1 From 73ce40de7dd0c8ef02a2f2b99d3cc308d9062916 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 01:56:44 +0000 Subject: Changed to catch compile/link failures and raise CompileError, LibError, or LinkError (exception classes defined in ccompiler.py). --- ccompiler.py | 36 +++++++++++++++++++++++++++++++----- msvccompiler.py | 28 +++++++++++++++++++++------- unixccompiler.py | 27 ++++++++++++++++++++++----- 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 42221769..33caf868 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,6 +15,22 @@ from distutils.spawn import spawn from distutils.util import move_file, mkpath, newer_pairwise, newer_group +# Exception classes used by the CCompiler implementation classes +class CCompilerError (Exception): + """Failure doing some compile/link operation.""" + +class CompileError (CCompilerError): + """Failure to compile one or more C/C++ source files.""" + +class LibError (CCompilerError): + """Failure to create a static library from one or more C/C++ object + files.""" + +class LinkError (CCompilerError): + """Failure to link one or more C/C++ object files into an executable + or shared library file.""" + + class CCompiler: """Abstract base class to define the interface that must be implemented by real compiler abstraction classes. Might have some use as a @@ -456,7 +472,9 @@ class CCompiler: command line. On other platforms, consult the implementation class documentation. In any event, they are intended as an escape hatch for those occasions when the abstract compiler - framework doesn't cut the mustard.""" + framework doesn't cut the mustard. + + Raises CompileError on failure.""" pass @@ -481,7 +499,9 @@ class CCompiler: 'debug' is a boolean; if true, debugging information will be included in the library (note that on most platforms, it is the compile step where this matters: the 'debug' flag is included - here just for consistency).""" + here just for consistency). + + Raises LibError on failure.""" pass @@ -531,7 +551,9 @@ class CCompiler: 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except of course that they supply command-line arguments - for the particular linker being used).""" + for the particular linker being used). + + Raises LinkError on failure.""" pass @@ -552,7 +574,9 @@ class CCompiler: is explicitly supplied as 'output_filename'. If 'output_dir' is supplied, 'output_filename' is relative to it (i.e. 'output_filename' can provide directory components if - needed).""" + needed). + + Raises LinkError on failure.""" pass @@ -570,7 +594,9 @@ class CCompiler: file. The "bunch of stuff" is as for 'link_shared_lib()'. 'output_progname' should be the base name of the executable program--e.g. on Unix the same as the output filename, but - on DOS/Windows ".exe" will be appended.""" + on DOS/Windows ".exe" will be appended. + + Raises LinkError on failure.""" pass diff --git a/msvccompiler.py b/msvccompiler.py index b6ff432c..06b415eb 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -14,7 +14,8 @@ import sys, os, string from types import * from distutils.errors import * from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options + CCompiler, gen_preprocess_options, gen_lib_options, \ + CompileError, LibError, LinkError _can_read_reg = 0 @@ -261,9 +262,12 @@ class MSVCCompiler (CCompiler) : output_opt = "/Fo" + obj self.mkpath (os.path.dirname (obj)) - self.spawn ([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs) + try: + self.spawn ([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg return objects @@ -290,7 +294,11 @@ class MSVCCompiler (CCompiler) : lib_args[:0] = extra_preargs if extra_postargs: lib_args.extend (extra_postargs) - self.spawn ([self.link] + ld_args) + try: + self.spawn ([self.link] + ld_args) + except DistutilsExecError, msg: + raise LibError, msg + else: self.announce ("skipping %s (up-to-date)" % output_filename) @@ -370,7 +378,10 @@ class MSVCCompiler (CCompiler) : print " output_filename =", output_filename print " mkpath'ing:", os.path.dirname (output_filename) self.mkpath (os.path.dirname (output_filename)) - self.spawn ([self.link] + ld_args) + try: + self.spawn ([self.link] + ld_args) + except DistutilsExecError, msg: + raise LinkError, msg else: self.announce ("skipping %s (up-to-date)" % output_filename) @@ -420,7 +431,10 @@ class MSVCCompiler (CCompiler) : ld_args.extend (extra_postargs) self.mkpath (os.path.dirname (output_filename)) - self.spawn ([self.link] + ld_args) + try: + self.spawn ([self.link] + ld_args) + except DistutilsExecError, msg: + raise LinkError, msg else: self.announce ("skipping %s (up-to-date)" % output_filename) diff --git a/unixccompiler.py b/unixccompiler.py index 40f564ab..c2f841ff 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -21,7 +21,10 @@ import string, re, os from types import * from copy import copy from distutils import sysconfig -from distutils.ccompiler import CCompiler, gen_preprocess_options, gen_lib_options +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options, gen_lib_options, \ + CompileError, LibError, LinkError +from distutils.errors import DistutilsExecError # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's @@ -132,7 +135,12 @@ class UnixCCompiler (CCompiler): self.announce ("skipping %s (%s up-to-date)" % (src, obj)) else: self.mkpath (os.path.dirname (obj)) - self.spawn ([self.cc] + cc_args + [src, '-o', obj] + extra_postargs) + try: + self.spawn ([self.cc] + cc_args + + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg # Return *all* object filenames, not just the ones we just built. return objects @@ -164,7 +172,10 @@ class UnixCCompiler (CCompiler): # needed -- or maybe Python's configure script took care of # it for us, hence the check for leading colon. if self.ranlib[0] != ':': - self.spawn ([self.ranlib, output_filename]) + try: + self.spawn ([self.ranlib, output_filename]) + except DistutilsExecError, msg: + raise LibError, msg else: self.announce ("skipping %s (up-to-date)" % output_filename) @@ -229,7 +240,10 @@ class UnixCCompiler (CCompiler): if extra_postargs: ld_args.extend (extra_postargs) self.mkpath (os.path.dirname (output_filename)) - self.spawn ([self.ld_shared] + ld_args) + try: + self.spawn ([self.ld_shared] + ld_args) + except DistutilsExecError, msg: + raise LinkError, msg else: self.announce ("skipping %s (up-to-date)" % output_filename) @@ -267,7 +281,10 @@ class UnixCCompiler (CCompiler): if extra_postargs: ld_args.extend (extra_postargs) self.mkpath (os.path.dirname (output_filename)) - self.spawn ([self.ld_exec] + ld_args) + try: + self.spawn ([self.ld_exec] + ld_args) + except DistutilsExecError, msg: + raise LinkError, msg else: self.announce ("skipping %s (up-to-date)" % output_filename) -- cgit v1.2.1 From 8f4b133d35191a4e6efc52c8a743c01cc7be8cd7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 02:02:14 +0000 Subject: Moved the CCompiler exceptions here, to avoid having to import ccompiler.py just to get a little exception class. No more string-based exceptions. --- errors.py | 157 +++++++++++++++++++++++++++++++------------------------------- 1 file changed, 78 insertions(+), 79 deletions(-) diff --git a/errors.py b/errors.py index d66043a1..16170563 100644 --- a/errors.py +++ b/errors.py @@ -12,82 +12,81 @@ symbols whose names start with "Distutils" and end with "Error".""" __revision__ = "$Id$" -import types - -if type (RuntimeError) is types.ClassType: - - class DistutilsError (Exception): - """The root of all Distutils evil.""" - pass - - class DistutilsModuleError (DistutilsError): - """Unable to load an expected module, or to find an expected class - within some module (in particular, command modules and classes).""" - pass - - class DistutilsClassError (DistutilsError): - """Some command class (or possibly distribution class, if anyone - feels a need to subclass Distribution) is found not to be holding - up its end of the bargain, ie. implementing some part of the - "command "interface.""" - pass - - class DistutilsGetoptError (DistutilsError): - """The option table provided to 'fancy_getopt()' is bogus.""" - pass - - class DistutilsArgError (DistutilsError): - """Raised by fancy_getopt in response to getopt.error -- ie. an - error in the command line usage.""" - pass - - class DistutilsFileError (DistutilsError): - """Any problems in the filesystem: expected file not found, etc. - Typically this is for problems that we detect before IOError or - OSError could be raised.""" - pass - - class DistutilsOptionError (DistutilsError): - """Syntactic/semantic errors in command options, such as use of - mutually conflicting options, or inconsistent options, - badly-spelled values, etc. No distinction is made between option - values originating in the setup script, the command line, config - files, or what-have-you -- but if we *know* something originated in - the setup script, we'll raise DistutilsSetupError instead.""" - pass - - class DistutilsSetupError (DistutilsError): - """For errors that can be definitely blamed on the setup script, - such as invalid keyword arguments to 'setup()'.""" - pass - - class DistutilsPlatformError (DistutilsError): - """We don't know how to do something on the current platform (but - we do know how to do it on some platform) -- eg. trying to compile - C files on a platform not supported by a CCompiler subclass.""" - pass - - class DistutilsExecError (DistutilsError): - """Any problems executing an external program (such as the C - compiler, when compiling C files).""" - pass - - class DistutilsInternalError (DistutilsError): - """Internal inconsistencies or impossibilities (obviously, this - should never be seen if the code is working!).""" - pass - -# String-based exceptions -else: - DistutilsError = 'DistutilsError' - DistutilsModuleError = 'DistutilsModuleError' - DistutilsClassError = 'DistutilsClassError' - DistutilsGetoptError = 'DistutilsGetoptError' - DistutilsArgError = 'DistutilsArgError' - DistutilsFileError = 'DistutilsFileError' - DistutilsOptionError = 'DistutilsOptionError' - DistutilsPlatformError = 'DistutilsPlatformError' - DistutilsExecError = 'DistutilsExecError' - DistutilsInternalError = 'DistutilsInternalError' - -del types +class DistutilsError (Exception): + """The root of all Distutils evil.""" + pass + +class DistutilsModuleError (DistutilsError): + """Unable to load an expected module, or to find an expected class + within some module (in particular, command modules and classes).""" + pass + +class DistutilsClassError (DistutilsError): + """Some command class (or possibly distribution class, if anyone + feels a need to subclass Distribution) is found not to be holding + up its end of the bargain, ie. implementing some part of the + "command "interface.""" + pass + +class DistutilsGetoptError (DistutilsError): + """The option table provided to 'fancy_getopt()' is bogus.""" + pass + +class DistutilsArgError (DistutilsError): + """Raised by fancy_getopt in response to getopt.error -- ie. an + error in the command line usage.""" + pass + +class DistutilsFileError (DistutilsError): + """Any problems in the filesystem: expected file not found, etc. + Typically this is for problems that we detect before IOError or + OSError could be raised.""" + pass + +class DistutilsOptionError (DistutilsError): + """Syntactic/semantic errors in command options, such as use of + mutually conflicting options, or inconsistent options, + badly-spelled values, etc. No distinction is made between option + values originating in the setup script, the command line, config + files, or what-have-you -- but if we *know* something originated in + the setup script, we'll raise DistutilsSetupError instead.""" + pass + +class DistutilsSetupError (DistutilsError): + """For errors that can be definitely blamed on the setup script, + such as invalid keyword arguments to 'setup()'.""" + pass + +class DistutilsPlatformError (DistutilsError): + """We don't know how to do something on the current platform (but + we do know how to do it on some platform) -- eg. trying to compile + C files on a platform not supported by a CCompiler subclass.""" + pass + +class DistutilsExecError (DistutilsError): + """Any problems executing an external program (such as the C + compiler, when compiling C files).""" + pass + +class DistutilsInternalError (DistutilsError): + """Internal inconsistencies or impossibilities (obviously, this + should never be seen if the code is working!).""" + pass + + +# Exception classes used by the CCompiler implementation classes +class CCompilerError (Exception): + """Some compile/link operation failed.""" + +class CompileError (CCompilerError): + """Failure to compile one or more C/C++ source files.""" + +class LibError (CCompilerError): + """Failure to create a static library from one or more C/C++ object + files.""" + +class LinkError (CCompilerError): + """Failure to link one or more C/C++ object files into an executable + or shared library file.""" + + -- cgit v1.2.1 From 16536d61d54c05d033267117413465693856737e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 02:02:48 +0000 Subject: Removed exceptions -- now in errors.py to avoid expensive import of ccompiler. --- ccompiler.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 33caf868..834d543e 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,22 +15,6 @@ from distutils.spawn import spawn from distutils.util import move_file, mkpath, newer_pairwise, newer_group -# Exception classes used by the CCompiler implementation classes -class CCompilerError (Exception): - """Failure doing some compile/link operation.""" - -class CompileError (CCompilerError): - """Failure to compile one or more C/C++ source files.""" - -class LibError (CCompilerError): - """Failure to create a static library from one or more C/C++ object - files.""" - -class LinkError (CCompilerError): - """Failure to link one or more C/C++ object files into an executable - or shared library file.""" - - class CCompiler: """Abstract base class to define the interface that must be implemented by real compiler abstraction classes. Might have some use as a -- cgit v1.2.1 From 665fe5e7211490f9540308db3a3c625d0303ddc4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 02:02:49 +0000 Subject: Import exceptions from errors.py, not ccompiler.py. --- msvccompiler.py | 8 ++++---- unixccompiler.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 06b415eb..06d8501a 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -12,11 +12,11 @@ __revision__ = "$Id$" import sys, os, string from types import * -from distutils.errors import * -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options, \ +from distutils.errors import \ + DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError - +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options, gen_lib_options _can_read_reg = 0 try: diff --git a/unixccompiler.py b/unixccompiler.py index c2f841ff..4d38e4d8 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -22,9 +22,9 @@ from types import * from copy import copy from distutils import sysconfig from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options, \ - CompileError, LibError, LinkError -from distutils.errors import DistutilsExecError + CCompiler, gen_preprocess_options, gen_lib_options +from distutils.errors import \ + DistutilsExecError, CompileError, LibError, LinkError # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's -- cgit v1.2.1 From ad92da0dcc01f36dfd7c4b5084c77a20a7352f7b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 02:04:29 +0000 Subject: Catch CCompiler exceptions in 'setup()'. --- core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core.py b/core.py index cd0f8e96..15a88147 100644 --- a/core.py +++ b/core.py @@ -122,7 +122,8 @@ def setup (**attrs): except (DistutilsExecError, DistutilsFileError, - DistutilsOptionError), msg: + DistutilsOptionError, + CCompilerError), msg: if DEBUG: raise else: -- cgit v1.2.1 From 4c3307aa998ac380beb50662b75648fa6e5d80ea Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 02:04:54 +0000 Subject: Cosmetic tweak. --- command/command_template | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/command_template b/command/command_template index 73c1c190..50bbab7b 100644 --- a/command/command_template +++ b/command/command_template @@ -1,6 +1,7 @@ """distutils.command.x -Implements the Distutils 'x' command.""" +Implements the Distutils 'x' command. +""" # created 2000/mm/dd, John Doe -- cgit v1.2.1 From 31ba84d5006258eea4f524076aebb085a1168aac Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 01:05:35 +0000 Subject: Provides the Extension class, a nicer way to describe C/C++ extensions than the old (ext_name, build_info) tuple. --- extension.py | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 extension.py diff --git a/extension.py b/extension.py new file mode 100644 index 00000000..9d2a6fa1 --- /dev/null +++ b/extension.py @@ -0,0 +1,115 @@ +"""distutils.extension + +Provides the Extension class, used to describe C/C++ extension +modules in setup scripts.""" + +# created 2000/05/30, Greg Ward + +__revision__ = "$Id$" + +from types import * + + +# This class is really only used by the "build_ext" command, so it might +# make sense to put it in distutils.command.build_ext. However, that +# module is already big enough, and I want to make this class a bit more +# complex to simplify some common cases ("foo" module in "foo.c") and do +# better error-checking ("foo.c" actually exists). +# +# Also, putting this in build_ext.py means every setup script would have to +# import that large-ish module (indirectly, through distutils.core) in +# order to do anything. + +class Extension: + """Just a collection of attributes that describes an extension + module and everything needed to build it (hopefully in a portable + way, but there are hooks that let you can be as unportable as you + need). + + Instance attributes: + name : string + the full name of the extension, including any packages -- ie. + *not* a filename or pathname, but Python dotted name + sources : [string] + list of C/C++ source filenames, relative to the distribution + root (where the setup script lives), in Unix form + (slash-separated) for portability + include_dirs : [string] + list of directories to search for C/C++ header files (in Unix + form for portability) + define_macros : [(name : string, value : string|None)] + list of macros to define; each macro is defined using a 2-tuple, + where 'value' is either the string to define it to or None to + define it without a particular value (equivalent of "#define + FOO" in source or -DFOO on Unix C compiler command line) + undef_macros : [string] + list of macros to undefine explicitly + library_dirs : [string] + list of directories to search for C/C++ libraries at link time + libraries : [string] + list of library names (not filenames or paths) to link against + runtime_library_dirs : [string] + list of directories to search for C/C++ libraries at run time + (for shared extensions, this is when the extension is loaded) + extra_objects : [string] + list of extra files to link with (eg. object files not implied + by 'sources', static library that must be explicitly specified, + binary resource files, etc.) + extra_compile_args : [string] + any extra platform- and compiler-specific information to use + when compiling the source files in 'sources'. For platforms and + compilers where "command line" makes sense, this is typically a + list of command-line arguments, but for other platforms it could + be anything. + extra_link_args : [string] + any extra platform- and compiler-specific information to use + when linking object files together to create the extension (or + to create a new static Python interpreter). Similar + interpretation as for 'extra_compile_args'. + export_symbols : [string] + list of symbols to be exported from a shared extension. Not + used on all platforms, and not generally necessary for Python + extensions, which typically export exactly one symbol: "init" + + extension_name. + export_symbol_file : string + name of file that lists symbols to export; the format of this + file is platform- and compiler-specific. This is even more + gratuitous and unnecessary than 'export_symbols'; I'll be happy + when it goes away forever. + """ + + def __init__ (self, name, sources, + include_dirs=None, + define_macros=None, + undef_macros=None, + library_dirs=None, + libraries=None, + runtime_library_dirs=None, + extra_objects=None, + extra_compile_args=None, + extra_link_args=None, + export_symbols=None, + export_symbol_file=None, + ): + + assert type(name) is StringType, "'name' must be a string" + assert (type(sources) is ListType and + len(sources) >= 1 and + map(type, sources) == [StringType]*len(sources)), \ + "'sources' must be a non-empty list of strings" + + self.name = name + self.sources = sources + self.include_dirs = include_dirs or [] + self.define_macros = define_macros or [] + self.undef_macros = undef_macros or [] + self.library_dirs = library_dirs or [] + self.libraries = libraries or [] + self.runtime_library_dirs = runtime_library_dirs or [] + self.extra_objects = extra_objects or [] + self.extra_compile_args = extra_compile_args or [] + self.extra_link_args = extra_link_args or [] + self.export_symbols = export_symbols or [] + self.export_symbol_file = export_symbol_file + +# class Extension -- cgit v1.2.1 From 69fc9746d19d0bdb9cf5d2d18eaebe561da1210b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 01:09:52 +0000 Subject: Overhauled to expect 'self.extensions' (taken from 'ext_modules' in the setup script) to be a list of Extension instances, rather than a list of of (ext_name, build_info) tuples. This is mostly a simplification, but 'check_extension_list()' got a lot more complicated because of the need to convert the old-style tuples to Extension instances. Temporarily dropped support for defining/undefining macros in the 'extensions' list -- I want to change the interface, but haven't yet made the required changes in CCompiler and friends to support this nicely. Also neatened up the code that merges 'extra_compile_flags' and the CFLAGS environment variable. --- command/build_ext.py | 194 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 128 insertions(+), 66 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index cef3ca82..f487a68c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -13,6 +13,7 @@ from types import * from distutils.core import Command from distutils.errors import * from distutils.dep_util import newer_group +from distutils.extension import Extension # An extension name is just a dot-separated list of Python NAMEs (ie. # the same as a fully-qualified module name). @@ -152,13 +153,17 @@ class build_ext (Command): from distutils.ccompiler import new_compiler - # 'self.extensions', as supplied by setup.py, is a list of 2-tuples. - # Each tuple is simple: + # 'self.extensions', as supplied by setup.py, is a list of + # Extension instances. See the documentation for Extension (in + # distutils.core) for details. + # + # For backwards compatibility with Distutils 0.8.2 and earlier, we + # also allow the 'extensions' list to be a list of tuples: # (ext_name, build_info) - # build_info is a dictionary containing everything specific to - # building this extension. (Info pertaining to all extensions - # should be handled by general distutils options passed from - # setup.py down to right here, but that's not taken care of yet.) + # where build_info is a dictionary containing everything that + # Extension instances do except the name, with a few things being + # differently named. We convert these 2-tuples to Extension + # instances as needed. if not self.extensions: return @@ -208,32 +213,82 @@ class build_ext (Command): def check_extensions_list (self, extensions): """Ensure that the list of extensions (presumably provided as a - command option 'extensions') is valid, i.e. it is a list of - 2-tuples, where the tuples are (extension_name, build_info_dict). - Raise DistutilsSetupError if the structure is invalid anywhere; - just returns otherwise.""" - - if type (extensions) is not ListType: + command option 'extensions') is valid, i.e. it is a list of + Extension objects. We also support the old-style list of 2-tuples, + where the tuples are (ext_name, build_info), which are converted to + Extension instances here. + + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ + if type(extensions) is not ListType: raise DistutilsSetupError, \ - "'ext_modules' option must be a list of tuples" + "'ext_modules' option must be a list of Extension instances" - for ext in extensions: - if type (ext) is not TupleType and len (ext) != 2: + for i in range(len(extensions)): + ext = extensions[i] + if isinstance(ext, Extension): + continue # OK! (assume type-checking done + # by Extension constructor) + + (ext_name, build_info) = ext + self.warn(("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s'" + "-- please convert to Extension instance" % ext_name)) + if type(ext) is not TupleType and len(ext) != 2: raise DistutilsSetupError, \ - "each element of 'ext_modules' option must be a 2-tuple" + ("each element of 'ext_modules' option must be an " + "Extension instance or 2-tuple") - if not (type (ext[0]) is StringType and - extension_name_re.match (ext[0])): + if not (type(ext_name) is StringType and + extension_name_re.match(ext_name)): raise DistutilsSetupError, \ - "first element of each tuple in 'ext_modules' " + \ - "must be the extension name (a string)" + ("first element of each tuple in 'ext_modules' " + "must be the extension name (a string)") - if type (ext[1]) is not DictionaryType: + if type(build_info) is not DictionaryType: raise DistutilsSetupError, \ - "second element of each tuple in 'ext_modules' " + \ - "must be a dictionary (build info)" - - # end sanity-check for + ("second element of each tuple in 'ext_modules' " + "must be a dictionary (build info)") + + # OK, the (ext_name, build_info) dict is type-safe: convert it + # to an Extension instance. + ext = Extension(ext_name, build_info['sources']) + + # Easy stuff: one-to-one mapping from dict elements to + # instance attributes. + for key in ('include_dirs', + 'library_dirs', + 'libraries', + 'extra_objects', + 'extra_compile_args', + 'extra_link_args'): + setattr(ext, key, build_info.get(key)) + + # Medium-easy stuff: same syntax/semantics, different names. + ext.runtime_library_dirs = build_info.get('rpath') + ext.export_symbol_file = build_info.get('def_file') + + # Non-trivial stuff: 'macros' split into 'define_macros' + # and 'undef_macros'. + macros = build_info.get('macros') + if macros: + ext.define_macros = [] + ext.undef_macros = [] + for macro in macros: + if not (type(macro) is TupleType and + 1 <= len(macros) <= 2): + raise DistutilsSetupError, \ + ("'macros' element of build info dict " + "must be 1- or 2-tuple") + if len(macro) == 1: + ext.undef_macros.append(macro[0]) + elif len(macro) == 2: + ext.define_macros.append(macro) + + extensions[i] = ext + + # for extensions # check_extensions_list () @@ -243,10 +298,8 @@ class build_ext (Command): filenames = [] # Wouldn't it be neat if we knew the names of header files too... - for (extension_name, build_info) in self.extensions: - sources = build_info.get ('sources') - if type (sources) in (ListType, TupleType): - filenames.extend (sources) + for ext in self.extensions: + filenames.extend (ext.sources) return filenames @@ -262,8 +315,8 @@ class build_ext (Command): # ignores the 'inplace' flag, and assumes everything goes in the # "build" tree. outputs = [] - for (extension_name, build_info) in self.extensions: - fullname = self.get_ext_fullname (extension_name) + for ext in self.extensions: + fullname = self.get_ext_fullname (ext.name) outputs.append (os.path.join (self.build_lib, self.get_ext_filename(fullname))) return outputs @@ -276,16 +329,16 @@ class build_ext (Command): # First, sanity-check the 'extensions' list self.check_extensions_list (self.extensions) - for (extension_name, build_info) in self.extensions: - sources = build_info.get ('sources') + for ext in self.extensions: + sources = ext.sources if sources is None or type (sources) not in (ListType, TupleType): raise DistutilsSetupError, \ ("in 'ext_modules' option (extension '%s'), " + "'sources' must be present and must be " + - "a list of source filenames") % extension_name + "a list of source filenames") % ext.name sources = list (sources) - fullname = self.get_ext_fullname (extension_name) + fullname = self.get_ext_fullname (ext.name) if self.inplace: # ignore build-lib -- put the compiled extension into # the source tree along with pure Python modules @@ -302,46 +355,54 @@ class build_ext (Command): ext_filename = os.path.join (self.build_lib, self.get_ext_filename(fullname)) - if not newer_group(sources, ext_filename, 'newer'): + if not (self.force or newer_group(sources, ext_filename, 'newer')): self.announce ("skipping '%s' extension (up-to-date)" % - extension_name) + ext.name) continue # 'for' loop over all extensions else: - self.announce ("building '%s' extension" % extension_name) + self.announce ("building '%s' extension" % ext.name) # First step: compile the source code to object files. This # drops the object files in the current directory, regardless # of where the source is (may be a bad thing, but that's how a # Makefile.pre.in-based system does it, so at least there's a # precedent!) - macros = build_info.get ('macros') - include_dirs = build_info.get ('include_dirs') - extra_args = build_info.get ('extra_compile_args') - # honor CFLAGS enviroment variable - # XXX do we *really* need this? or is it just a hack until - # the user can put compiler flags in setup.cfg? + + # XXX not honouring 'define_macros' or 'undef_macros' -- the + # CCompiler API needs to change to accomodate this, and I + # want to do one thing at a time! + + # Two possible sources for extra compiler arguments: + # - 'extra_compile_args' in Extension object + # - CFLAGS environment variable (not particularly + # elegant, but people seem to expect it and I + # guess it's useful) + # The environment variable should take precedence, and + # any sensible compiler will give precendence to later + # command line args. Hence we combine them in order: + extra_args = ext.extra_compile_args + + # XXX and if we support CFLAGS, why not CC (compiler + # executable), CPPFLAGS (pre-processor options), and LDFLAGS + # (linker options) too? + # XXX should we use shlex to properly parse CFLAGS? + if os.environ.has_key('CFLAGS'): - if not extra_args: - extra_args = [] - extra_args = string.split(os.environ['CFLAGS']) + extra_args + extra_args.extend(string.split(os.environ['CFLAGS'])) objects = self.compiler.compile (sources, output_dir=self.build_temp, - macros=macros, - include_dirs=include_dirs, + #macros=macros, + include_dirs=ext.include_dirs, debug=self.debug, extra_postargs=extra_args) # Now link the object files together into a "shared object" -- # of course, first we have to figure out all the other things # that go into the mix. - extra_objects = build_info.get ('extra_objects') - if extra_objects: - objects.extend (extra_objects) - libraries = build_info.get ('libraries') - library_dirs = build_info.get ('library_dirs') - rpath = build_info.get ('rpath') - extra_args = build_info.get ('extra_link_args') or [] + if ext.extra_objects: + objects.extend (ext.extra_objects) + extra_args = ext.extra_link_args # XXX this is a kludge! Knowledge of specific compilers or # platforms really doesn't belong here; in an ideal world, the @@ -354,10 +415,10 @@ class build_ext (Command): # excuse for committing more platform- and compiler-specific # kludges; they are to be avoided if possible!) if self.compiler.compiler_type == 'msvc': - def_file = build_info.get ('def_file') + def_file = ext.export_symbol_file if def_file is None: source_dir = os.path.dirname (sources[0]) - ext_base = (string.split (extension_name, '.'))[-1] + ext_base = (string.split (ext.name, '.'))[-1] def_file = os.path.join (source_dir, "%s.def" % ext_base) if not os.path.exists (def_file): def_file = None @@ -365,7 +426,7 @@ class build_ext (Command): if def_file is not None: extra_args.append ('/DEF:' + def_file) else: - modname = string.split (extension_name, '.')[-1] + modname = string.split (ext.name, '.')[-1] extra_args.append('/export:init%s'%modname) # The MSVC linker generates unneeded .lib and .exp files, @@ -374,17 +435,18 @@ class build_ext (Command): # directory. implib_file = os.path.join ( self.build_temp, - self.get_ext_libname (extension_name)) + self.get_ext_libname (ext.name)) extra_args.append ('/IMPLIB:' + implib_file) self.mkpath (os.path.dirname (implib_file)) # if MSVC - self.compiler.link_shared_object (objects, ext_filename, - libraries=libraries, - library_dirs=library_dirs, - runtime_library_dirs=rpath, - extra_postargs=extra_args, - debug=self.debug) + self.compiler.link_shared_object ( + objects, ext_filename, + libraries=ext.libraries, + library_dirs=ext.library_dirs, + runtime_library_dirs=ext.runtime_library_dirs, + extra_postargs=extra_args, + debug=self.debug) # build_extensions () -- cgit v1.2.1 From 56c6ea7b26cbbbd156403430021ae019172da3ff Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 01:11:20 +0000 Subject: Import the new Extension class, so setup scripts can "from distutils.core import" it. --- core.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core.py b/core.py index 15a88147..89036039 100644 --- a/core.py +++ b/core.py @@ -12,8 +12,12 @@ __revision__ = "$Id$" import sys, os from types import * from distutils.errors import * + +# Mainly import these so setup scripts can "from distutils.core import" them. from distutils.dist import Distribution from distutils.cmd import Command +from distutils.extension import Extension + # This is a barebones help message generated displayed when the user # runs the setup script with no arguments at all. More useful help -- cgit v1.2.1 From 794b1fe1cd5521c67511325600ef8679c48a53e6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 02:14:32 +0000 Subject: Fixed 'change_root() to work at all on Windows, and to work correctly on Unix. --- util.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/util.py b/util.py index b660b4bf..6063aa68 100644 --- a/util.py +++ b/util.py @@ -86,22 +86,22 @@ def native_path (pathname): def change_root (new_root, pathname): - """Return 'pathname' with 'new_root' prepended. If 'pathname' is relative, this is equivalent to "os.path.join(new_root,pathname)". Otherwise, it requires making 'pathname' relative and then joining the - two, which is tricky on DOS/Windows and Mac OS.""" - - if not abspath (pathname): - return os.path.join (new_root, pathname) - - elif os.name == 'posix': - return os.path.join (new_root, pathname[1:]) + two, which is tricky on DOS/Windows and Mac OS. + """ + if os.name == 'posix': + if not os.path.isabs (pathname): + return os.path.join (new_root, pathname) + else: + return os.path.join (new_root, pathname[1:]) elif os.name == 'nt': - (root_drive, root_path) = os.path.splitdrive (new_root) (drive, path) = os.path.splitdrive (pathname) - raise RuntimeError, "I give up -- not sure how to do this on Windows" + if path[0] == '\\': + path = path[1:] + return os.path.join (new_root, path) elif os.name == 'mac': raise RuntimeError, "no clue how to do this on Mac OS" -- cgit v1.2.1 From f96f4e6d4a60e6d7c8cddc8656e6affbe456c61a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 02:17:19 +0000 Subject: Normalize paths before writing them to a zipfile. --- archive_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archive_util.py b/archive_util.py index 050ea417..218450a6 100644 --- a/archive_util.py +++ b/archive_util.py @@ -93,7 +93,7 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): def visit (z, dirname, names): for name in names: - path = os.path.join (dirname, name) + path = os.path.normpath(os.path.join(dirname, name)) if os.path.isfile (path): z.write (path, path) -- cgit v1.2.1 From 378e81b1c72a9d54682c8e53a433d8d5cb2f81f7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 02:32:10 +0000 Subject: Renamed 'native_path()' to 'convert_path()'. Also changed it so it doesn't barf if the path is already in native format (ie. contains os.sep). --- command/install.py | 4 ++-- command/sdist.py | 10 +++++----- util.py | 12 ++++-------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/command/install.py b/command/install.py index 13fa88ee..7176fab8 100644 --- a/command/install.py +++ b/command/install.py @@ -10,7 +10,7 @@ import sys, os, string from types import * from distutils.core import Command, DEBUG from distutils import sysconfig -from distutils.util import write_file, native_path, subst_vars, change_root +from distutils.util import write_file, convert_path, subst_vars, change_root from distutils.errors import DistutilsOptionError from glob import glob @@ -423,7 +423,7 @@ class install (Command): # convert to local form in case Unix notation used (as it # should be in setup scripts) - extra_dirs = native_path (extra_dirs) + extra_dirs = convert_path (extra_dirs) else: path_file = None diff --git a/command/sdist.py b/command/sdist.py index 6626b9e6..c06860fb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,7 +11,7 @@ import fnmatch from types import * from glob import glob from distutils.core import Command -from distutils.util import newer, create_tree, remove_tree, native_path, \ +from distutils.util import newer, create_tree, remove_tree, convert_path, \ write_file from distutils.archive_util import check_archive_formats from distutils.text_file import TextFile @@ -322,7 +322,7 @@ class sdist (Command): action) continue - pattern_list = map(native_path, words[1:]) + pattern_list = map(convert_path, words[1:]) elif action in ('recursive-include','recursive-exclude'): if len (words) < 3: @@ -332,8 +332,8 @@ class sdist (Command): action) continue - dir = native_path(words[1]) - pattern_list = map (native_path, words[2:]) + dir = convert_path(words[1]) + pattern_list = map (convert_path, words[2:]) elif action in ('graft','prune'): if len (words) != 2: @@ -343,7 +343,7 @@ class sdist (Command): action) continue - dir_pattern = native_path (words[1]) + dir_pattern = convert_path (words[1]) else: template.warn ("invalid manifest template line: " + diff --git a/util.py b/util.py index 6063aa68..57546384 100644 --- a/util.py +++ b/util.py @@ -58,7 +58,7 @@ def get_platform (): # get_platform() -def native_path (pathname): +def convert_path (pathname): """Return 'pathname' as a name that will work on the native filesystem, i.e. split it on '/' and put it back together again using the current directory separator. Needed because filenames in @@ -73,16 +73,12 @@ def native_path (pathname): if pathname[-1] == '/': raise ValueError, "path '%s' cannot end with '/'" % pathname if os.sep != '/': - if os.sep in pathname: - raise ValueError, \ - "path '%s' cannot contain '%c' character" % (pathname, os.sep) - else: - paths = string.split (pathname, '/') - return apply (os.path.join, paths) + paths = string.split (pathname, '/') + return apply (os.path.join, paths) else: return pathname -# native_path () +# convert_path () def change_root (new_root, pathname): -- cgit v1.2.1 From c0ed34f0d17691d413a195bb719cc004e50fd3fd Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 23:56:45 +0000 Subject: Regularize options a bit: * help strings start with lowercase * added affirmative version of '--no-clean' and '--no-rpm-opt-flags', which are the default (thus the attributes that correspond to the options are now 'clean' and 'use_rpm_opt_flags') --- command/bdist_rpm.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index d07f9249..e7e4ca7e 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -19,27 +19,34 @@ class bdist_rpm (Command): user_options = [ ('spec-only', None, - "Only regenerate spec file"), + "only regenerate spec file"), ('source-only', None, - "Only generate source RPM"), + "only generate source RPM"), ('binary-only', None, - "Only generate binary RPM"), + "only generate binary RPM"), ('use-bzip2', None, - "Use bzip2 instead of gzip to create source distribution"), + "use bzip2 instead of gzip to create source distribution"), + ('clean', None, + "clean up RPM build directory [default]"), ('no-clean', None, - "Do not clean RPM build directory"), + "don't clean up RPM build directory"), + ('use-rpm-opt-flags', None, + "compile with RPM_OPT_FLAGS when building from source RPM"), ('no-rpm-opt-flags', None, - "Do not pass any RPM CFLAGS to compiler") - ] + "do not pass any RPM CFLAGS to compiler"), + ] + negative_opt = {'no-clean': 'clean', + 'no-rpm-opt-flags': 'use-rpm-opt-flags'} + def initialize_options (self): self.spec_only = None self.binary_only = None self.source_only = None self.use_bzip2 = None - self.no_clean = None - self.no_rpm_opt_flags = None + self.clean = 1 + self.use_rpm_opt_flags = 1 # initialize_options() @@ -54,7 +61,7 @@ class bdist_rpm (Command): "Cannot supply both '--source-only' and '--binary-only'" # don't pass CFLAGS to pure python distributions if not self.distribution.has_ext_modules(): - self.no_rpm_opt_flags = 1 + self.use_rpm_opt_flags = 0 # finalize_options() @@ -120,7 +127,7 @@ class bdist_rpm (Command): topdir = os.getcwd() + 'build/rpm' rpm_args.extend(['--define', '_topdir ' + os.getcwd() + '/build/rpm',]) - if not self.no_clean: + if self.clean: rpm_args.append('--clean') rpm_args.append(spec_path) self.spawn(rpm_args) @@ -168,7 +175,7 @@ class bdist_rpm (Command): self.preun = self._check_string('preun') self.postun = self._check_string('postun') self.prep = self._check_string('prep', '%setup') - if not self.no_rpm_opt_flags: + if self.use_rpm_opt_flags: self.build = (self._check_string( 'build', 'env CFLAGS="$RPM_OPT_FLAGS" python setup.py build')) -- cgit v1.2.1 From 0534a48d54c3fbf99ad65bff7b472b26eae70534 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 1 Jun 2000 00:40:25 +0000 Subject: More tweaking to make this command act like other Distutils commands: * added "--bdist-base" option to parameterize where we build the RPM (comes from "bdist" by default: "build/bdist.") * simplified/cleaned up some code in 'run()' in the process of removing (most) hard-coded directory names * if "--spec-only", drop spec file in "dist" rather than "redhat" (directory name still hard-coded, though) * use 'reinitialize_command()' to fetch the "sdist" object to tweak before running "sdist" command * use 'self.copy_file()' method rather than 'copy_file()' function * cosmetic tweaks to comments, error messages --- command/bdist_rpm.py | 55 +++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index e7e4ca7e..c84fda87 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -10,7 +10,7 @@ __revision__ = "$Id$" import os, string from types import * from distutils.core import Command -from distutils.util import mkpath, write_file, copy_file +from distutils.util import get_platform, write_file from distutils.errors import * class bdist_rpm (Command): @@ -18,6 +18,8 @@ class bdist_rpm (Command): description = "create an RPM distribution" user_options = [ + ('bdist-base', None, + "base directory for creating built distributions"), ('spec-only', None, "only regenerate spec file"), ('source-only', None, @@ -41,6 +43,7 @@ class bdist_rpm (Command): def initialize_options (self): + self.bdist_base = None self.spec_only = None self.binary_only = None self.source_only = None @@ -52,13 +55,14 @@ class bdist_rpm (Command): def finalize_options (self): + self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) if os.name != 'posix': raise DistutilsPlatformError, \ ("don't know how to create RPM " "distributions on platform %s" % os.name) if self.binary_only and self.source_only: raise DistutilsOptionsError, \ - "Cannot supply both '--source-only' and '--binary-only'" + "cannot supply both '--source-only' and '--binary-only'" # don't pass CFLAGS to pure python distributions if not self.distribution.has_ext_modules(): self.use_rpm_opt_flags = 0 @@ -69,50 +73,49 @@ class bdist_rpm (Command): def run (self): self._get_package_data() # get packaging info - # make directories if self.spec_only: - self.mkpath('redhat') + spec_dir = "dist" + self.mkpath(spec_dir) # XXX should be configurable else: + rpm_base = os.path.join(self.bdist_base, "rpm") + rpm_dir = {} for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): - self.mkpath(os.path.join('build/rpm', d)) - - # spec file goes into .redhat directory if '--spec-only specified', - # into build/rpm/spec otherwise - if self.spec_only: - spec_path = 'redhat/%s.spec' % self.distribution.get_name() - else: - spec_path = ('build/rpm/SPECS/%s.spec' % - self.distribution.get_name()) + rpm_dir[d] = os.path.join(rpm_base, d) + self.mkpath(rpm_dir[d]) + spec_dir = rpm_dir['SPECS'] + + # Spec file goes into 'dist' directory if '--spec-only specified', + # into build/rpm. otherwise. + spec_path = os.path.join(spec_dir, + "%s.spec" % self.distribution.get_name()) self.execute(write_file, (spec_path, self._make_spec_file()), - 'Writing .spec file') + "writing '%s'" % spec_path) if self.spec_only: # stop if requested return - # make a source distribution and copy to SOURCES directory with - # optional icon - sdist = self.get_finalized_command ('sdist') + # Make a source distribution and copy to SOURCES directory with + # optional icon. + sdist = self.reinitialize_command ('sdist') if self.use_bzip2: sdist.formats = ['bztar'] else: sdist.formats = ['gztar'] self.run_command('sdist') - if self.use_bzip2: - source = self.distribution.get_fullname() + '.tar.bz2' - else: - source = self.distribution.get_fullname() + '.tar.gz' - self.execute(copy_file, (source, 'build/rpm/SOURCES'), - 'Copying source distribution to SOURCES') + + source = sdist.get_archive_files()[0] + source_dir = rpm_dir['SOURCES'] + self.copy_file(source, source_dir) + if self.icon: if os.path.exists(self.icon): - self.execute(copy_file, (self.icon, 'build/rpm/SOURCES'), - 'Copying icon to SOURCES') + self.copy_file(self.icon, source_dir) else: raise DistutilsFileError, \ - "Unable to find icon file '%s'" % self.icon + "icon file '%s' does not exist" % self.icon # build package -- cgit v1.2.1 From 7b2992eb1483e6c551d33f6e896569a420b1c88b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 1 Jun 2000 01:07:55 +0000 Subject: Ensure that 'make_archive()' returns the name of the new archive file. --- archive_util.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/archive_util.py b/archive_util.py index 218450a6..3159c284 100644 --- a/archive_util.py +++ b/archive_util.py @@ -127,7 +127,6 @@ def check_archive_formats (formats): def make_archive (base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=0): - """Create an archive file (eg. zip or tar). 'base_name' is the name of the file to create, minus any format-specific extension; 'format' is the archive format: one of "zip", "tar", "ztar", or "gztar". @@ -136,8 +135,8 @@ def make_archive (base_name, format, archive. 'base_dir' is the directory where we start archiving from; ie. 'base_dir' will be the common prefix of all files and directories in the archive. 'root_dir' and 'base_dir' both default - to the current directory.""" - + to the current directory. Returns the name of the archive file. + """ save_cwd = os.getcwd() if root_dir is not None: if verbose: @@ -160,11 +159,13 @@ def make_archive (base_name, format, func = format_info[0] for (arg,val) in format_info[1]: kwargs[arg] = val - apply (func, (base_name, base_dir), kwargs) + filename = apply (func, (base_name, base_dir), kwargs) if root_dir is not None: if verbose: print "changing back to '%s'" % save_cwd os.chdir (save_cwd) + return filename + # make_archive () -- cgit v1.2.1 From 71848a5af214fca715bf55b482e778cb4763c81b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 1 Jun 2000 01:08:52 +0000 Subject: Added 'reinitialize_command()' method -- delegated to Distribution instance. Ensure 'make_archive()' method returns archive filename. --- cmd.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd.py b/cmd.py index f8033892..852acadb 100644 --- a/cmd.py +++ b/cmd.py @@ -215,6 +215,10 @@ class Command: cmd_obj.ensure_finalized () return cmd_obj + # XXX rename to 'get_reinitialized_command()'? (should do the + # same in dist.py, if so) + def reinitialize_command (self, command): + return self.distribution.reinitialize_command(command) def run_command (self, command): """Run some other command: uses the 'run_command()' method of @@ -306,8 +310,8 @@ class Command: def make_archive (self, base_name, format, root_dir=None, base_dir=None): - util.make_archive (base_name, format, root_dir, base_dir, - self.verbose, self.dry_run) + return util.make_archive (base_name, format, root_dir, base_dir, + self.verbose, self.dry_run) def make_file (self, infiles, outfile, func, args, -- cgit v1.2.1 From 2d35a4ef38fc12bf2e61791fe1ba5bd2b99015ab Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 1 Jun 2000 01:09:47 +0000 Subject: Oops, 'reinitialize_command()' forgot to return the command object if didn't need to be reinitialized -- fixed. --- dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 3391e53f..de4f7ef0 100644 --- a/dist.py +++ b/dist.py @@ -711,7 +711,7 @@ class Distribution: command_name = command.get_command_name() if not command.finalized: - return + return command command.initialize_options() command.finalized = 0 self._set_command_options(command) -- cgit v1.2.1 From d9112d61365041f3bd448c3084b2e53a99b432bc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 1 Jun 2000 01:10:56 +0000 Subject: Remember the list of archive files created in 'make_distribution()'. Added 'get_archive_files()' so outsiders can get their hands on that list. --- command/sdist.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index c06860fb..03de85b1 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -64,6 +64,8 @@ class sdist (Command): self.formats = None self.keep_tree = 0 + self.archive_files = None + def finalize_options (self): if self.manifest is None: @@ -520,12 +522,22 @@ class sdist (Command): self.exclude_pattern (base_dir + "*") self.make_release_tree (base_dir, self.files) + archive_files = [] # remember names of files we create for fmt in self.formats: - self.make_archive (base_dir, fmt, base_dir=base_dir) + file = self.make_archive (base_dir, fmt, base_dir=base_dir) + archive_files.append(file) + + self.archive_files = archive_files if not self.keep_tree: remove_tree (base_dir, self.verbose, self.dry_run) + def get_archive_files (self): + """Return the list of archive files created when the command + was run, or None if the command hasn't run yet. + """ + return self.archive_files + # class sdist -- cgit v1.2.1 From fcdde577abf375ececdcf97aed74e3d0d5e73463 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 00:44:53 +0000 Subject: Reformatted and updated many docstrings. --- cmd.py | 168 ++++++++++++++++++++++++++++++++-------------------------------- core.py | 64 +++++++++++++------------ dist.py | 47 +++++++++--------- 3 files changed, 140 insertions(+), 139 deletions(-) diff --git a/cmd.py b/cmd.py index 852acadb..b0a4b959 100644 --- a/cmd.py +++ b/cmd.py @@ -1,7 +1,8 @@ """distutils.cmd Provides the Command class, the base class for the command classes -in the distutils.command package.""" +in the distutils.command package. +""" # created 2000/04/03, Greg Ward # (extricated from core.py; actually dates back to the beginning) @@ -16,28 +17,28 @@ from distutils import util class Command: """Abstract base class for defining command classes, the "worker bees" - of the Distutils. A useful analogy for command classes is to - think of them as subroutines with local variables called - "options". The options are "declared" in 'initialize_options()' - and "defined" (given their final values, aka "finalized") in - 'finalize_options()', both of which must be defined by every - command class. The distinction between the two is necessary - because option values might come from the outside world (command - line, option file, ...), and any options dependent on other - options must be computed *after* these outside influences have - been processed -- hence 'finalize_options()'. The "body" of the - subroutine, where it does all its work based on the values of its - options, is the 'run()' method, which must also be implemented by - every command class.""" + of the Distutils. A useful analogy for command classes is to think of + them as subroutines with local variables called "options". The options + are "declared" in 'initialize_options()' and "defined" (given their + final values, aka "finalized") in 'finalize_options()', both of which + must be defined by every command class. The distinction between the + two is necessary because option values might come from the outside + world (command line, config file, ...), and any options dependent on + other options must be computed *after* these outside influences have + been processed -- hence 'finalize_options()'. The "body" of the + subroutine, where it does all its work based on the values of its + options, is the 'run()' method, which must also be implemented by every + command class. + """ # -- Creation/initialization methods ------------------------------- def __init__ (self, dist): """Create and initialize a new Command object. Most importantly, - invokes the 'initialize_options()' method, which is the - real initializer and depends on the actual command being - instantiated.""" - + invokes the 'initialize_options()' method, which is the real + initializer and depends on the actual command being + instantiated. + """ # late import because of mutual dependence between these classes from distutils.dist import Distribution @@ -97,9 +98,9 @@ class Command: # Subclasses must define: # initialize_options() - # provide default values for all options; may be overridden - # by Distutils client, by command-line options, or by options - # from option file + # provide default values for all options; may be customized by + # setup script, by options from config file(s), or by command-line + # options # finalize_options() # decide on the final values for all options; this is called # after all possible intervention from the outside world @@ -110,28 +111,28 @@ class Command: def initialize_options (self): """Set default values for all the options that this command - supports. Note that these defaults may be overridden - by the command-line supplied by the user; thus, this is - not the place to code dependencies between options; generally, - 'initialize_options()' implementations are just a bunch - of "self.foo = None" assignments. - - This method must be implemented by all command classes.""" + supports. Note that these defaults may be overridden by other + commands, by the setup script, by config files, or by the + command-line. Thus, this is not the place to code dependencies + between options; generally, 'initialize_options()' implementations + are just a bunch of "self.foo = None" assignments. + This method must be implemented by all command classes. + """ raise RuntimeError, \ "abstract method -- subclass %s must override" % self.__class__ def finalize_options (self): - """Set final values for all the options that this command - supports. This is always called as late as possible, ie. - after any option assignments from the command-line or from - other commands have been done. Thus, this is the place to to - code option dependencies: if 'foo' depends on 'bar', then it - is safe to set 'foo' from 'bar' as long as 'foo' still has - the same value it was assigned in 'initialize_options()'. - - This method must be implemented by all command classes.""" - + """Set final values for all the options that this command supports. + This is always called as late as possible, ie. after any option + assignments from the command-line or from other commands have been + done. Thus, this is the place to to code option dependencies: if + 'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as + long as 'foo' still has the same value it was assigned in + 'initialize_options()'. + + This method must be implemented by all command classes. + """ raise RuntimeError, \ "abstract method -- subclass %s must override" % self.__class__ @@ -151,23 +152,23 @@ class Command: def run (self): - """A command's raison d'etre: carry out the action it exists - to perform, controlled by the options initialized in - 'initialize_options()', customized by the user and other - commands, and finalized in 'finalize_options()'. All - terminal output and filesystem interaction should be done by - 'run()'. + """A command's raison d'etre: carry out the action it exists to + perform, controlled by the options initialized in + 'initialize_options()', customized by other commands, the setup + script, the command-line, and config files, and finalized in + 'finalize_options()'. All terminal output and filesystem + interaction should be done by 'run()'. - This method must be implemented by all command classes.""" + This method must be implemented by all command classes. + """ raise RuntimeError, \ "abstract method -- subclass %s must override" % self.__class__ def announce (self, msg, level=1): - """If the Distribution instance to which this command belongs - has a verbosity level of greater than or equal to 'level' - print 'msg' to stdout.""" - + """If the current verbosity level is of greater than or equal to + 'level' print 'msg' to stdout. + """ if self.verbose >= level: print msg @@ -183,18 +184,18 @@ class Command: def set_undefined_options (self, src_cmd, *option_pairs): """Set the values of any "undefined" options from corresponding - option values in some other command object. "Undefined" here - means "is None", which is the convention used to indicate - that an option has not been changed between - 'set_initial_values()' and 'set_final_values()'. Usually - called from 'set_final_values()' for options that depend on - some other command rather than another option of the same - command. 'src_cmd' is the other command from which option - values will be taken (a command object will be created for it - if necessary); the remaining arguments are - '(src_option,dst_option)' tuples which mean "take the value - of 'src_option' in the 'src_cmd' command object, and copy it - to 'dst_option' in the current command object".""" + option values in some other command object. "Undefined" here means + "is None", which is the convention used to indicate that an option + has not been changed between 'initialize_options()' and + 'finalize_options()'. Usually called from 'finalize_options()' for + options that depend on some other command rather than another + option of the same command. 'src_cmd' is the other command from + which option values will be taken (a command object will be created + for it if necessary); the remaining arguments are + '(src_option,dst_option)' tuples which mean "take the value of + 'src_option' in the 'src_cmd' command object, and copy it to + 'dst_option' in the current command object". + """ # Option_pairs: list of (src_option, dst_option) tuples @@ -207,10 +208,11 @@ class Command: def get_finalized_command (self, command, create=1): - """Wrapper around Distribution's 'get_command_obj()' method: - find (create if necessary and 'create' is true) the command - object for 'command'..""" - + """Wrapper around Distribution's 'get_command_obj()' method: find + (create if necessary and 'create' is true) the command object for + 'command', call its 'ensure_finalized()' method, and return the + finalized command object. + """ cmd_obj = self.distribution.get_command_obj (command, create) cmd_obj.ensure_finalized () return cmd_obj @@ -222,9 +224,9 @@ class Command: def run_command (self, command): """Run some other command: uses the 'run_command()' method of - Distribution, which creates the command object if necessary - and then invokes its 'run()' method.""" - + Distribution, which creates and finalizes the command object if + necessary and then invokes its 'run()' method. + """ self.distribution.run_command (command) @@ -236,15 +238,16 @@ class Command: def execute (self, func, args, msg=None, level=1): - """Perform some action that affects the outside world (eg. - by writing to the filesystem). Such actions are special because - they should be disabled by the "dry run" flag, and should - announce themselves if the current verbosity level is high - enough. This method takes care of all that bureaucracy for you; - all you have to do is supply the funtion to call and an argument - tuple for it (to embody the "external action" being performed), - a message to print if the verbosity level is high enough, and an - optional verbosity threshold.""" + """Perform some action that affects the outside world (eg. by + writing to the filesystem). Such actions are special because they + should be disabled by the "dry run" flag, and should announce + themselves if the current verbosity level is high enough. This + method takes care of all that bureaucracy for you; all you have to + do is supply the funtion to call and an argument tuple for it (to + embody the "external action" being performed), a message to print + if the verbosity level is high enough, and an optional verbosity + threshold. + """ # Generate a message if we weren't passed one if msg is None: @@ -285,8 +288,8 @@ class Command: preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1): """Copy an entire directory tree respecting verbose, dry-run, - and force flags.""" - + and force flags. + """ return util.copy_tree (infile, outfile, preserve_mode,preserve_times,preserve_symlinks, not self.force, @@ -302,6 +305,7 @@ class Command: def spawn (self, cmd, search_path=1, level=1): + """Spawn an external command respecting verbose and dry-run flags.""" from distutils.spawn import spawn spawn (cmd, search_path, self.verbose >= level, @@ -316,16 +320,14 @@ class Command: def make_file (self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): - """Special case of 'execute()' for operations that process one or more input files and generate one output file. Works just like 'execute()', except the operation is skipped and a different message printed if 'outfile' already exists and is newer than all files listed in 'infiles'. If the command defined 'self.force', and it is true, then the command is unconditionally run -- does no - timestamp checks.""" - - + timestamp checks. + """ if exec_msg is None: exec_msg = "generating %s from %s" % \ (outfile, string.join (infiles, ', ')) diff --git a/core.py b/core.py index 89036039..9e390ac1 100644 --- a/core.py +++ b/core.py @@ -3,7 +3,8 @@ The only module that needs to be imported to use the Distutils; provides the 'setup' function (which is to be called from the setup script). Also indirectly provides the Distribution and Command classes, although they are -really defined in distutils.dist and distutils.cmd.""" +really defined in distutils.dist and distutils.cmd. +""" # created 1999/03/01, Greg Ward @@ -37,36 +38,37 @@ DEBUG = os.environ.get('DISTUTILS_DEBUG') def setup (**attrs): - """The gateway to the Distutils: do everything your setup script - needs to do, in a highly flexible and user-driven way. Briefly: - create a Distribution instance; parse the command-line, creating - and customizing instances of the command class for each command - found on the command-line; run each of those commands. - - The Distribution instance might be an instance of a class - supplied via the 'distclass' keyword argument to 'setup'; if no - such class is supplied, then the 'Distribution' class (also in - this module) is instantiated. All other arguments to 'setup' - (except for 'cmdclass') are used to set attributes of the - Distribution instance. - - The 'cmdclass' argument, if supplied, is a dictionary mapping - command names to command classes. Each command encountered on - the command line will be turned into a command class, which is in - turn instantiated; any class found in 'cmdclass' is used in place - of the default, which is (for command 'foo_bar') class 'foo_bar' - in module 'distutils.command.foo_bar'. The command class must - provide a 'user_options' attribute which is a list of option - specifiers for 'distutils.fancy_getopt'. Any command-line - options between the current and the next command are used to set - attributes of the current command object. - - When the entire command-line has been successfully parsed, calls - the 'run()' method on each command object in turn. This method - will be driven entirely by the Distribution object (which each - command object has a reference to, thanks to its constructor), - and the command-specific options that became attributes of each - command object.""" + """The gateway to the Distutils: do everything your setup script needs + to do, in a highly flexible and user-driven way. Briefly: create a + Distribution instance; find and parse config files; parse the command + line; run each of those commands using the options supplied to + 'setup()' (as keyword arguments), in config files, and on the command + line. + + The Distribution instance might be an instance of a class supplied via + the 'distclass' keyword argument to 'setup'; if no such class is + supplied, then the Distribution class (in dist.py) is instantiated. + All other arguments to 'setup' (except for 'cmdclass') are used to set + attributes of the Distribution instance. + + The 'cmdclass' argument, if supplied, is a dictionary mapping command + names to command classes. Each command encountered on the command line + will be turned into a command class, which is in turn instantiated; any + class found in 'cmdclass' is used in place of the default, which is + (for command 'foo_bar') class 'foo_bar' in module + 'distutils.command.foo_bar'. The command class must provide a + 'user_options' attribute which is a list of option specifiers for + 'distutils.fancy_getopt'. Any command-line options between the current + and the next command are used to set attributes of the current command + object. + + When the entire command-line has been successfully parsed, calls the + 'run()' method on each command object in turn. This method will be + driven entirely by the Distribution object (which each command object + has a reference to, thanks to its constructor), and the + command-specific options that became attributes of each command + object. + """ from pprint import pprint # for debugging output diff --git a/dist.py b/dist.py index de4f7ef0..6acc0544 100644 --- a/dist.py +++ b/dist.py @@ -1,7 +1,8 @@ """distutils.dist Provides the Distribution class, which represents the module distribution -being built/installed/distributed.""" +being built/installed/distributed. +""" # created 2000/04/03, Greg Ward # (extricated from core.py; actually dates back to the beginning) @@ -25,20 +26,18 @@ command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') class Distribution: - """The core of the Distutils. Most of the work hiding behind - 'setup' is really done within a Distribution instance, which - farms the work out to the Distutils commands specified on the - command line. - - Clients will almost never instantiate Distribution directly, - unless the 'setup' function is totally inadequate to their needs. - However, it is conceivable that a client might wish to subclass - Distribution for some specialized purpose, and then pass the - subclass to 'setup' as the 'distclass' keyword argument. If so, - it is necessary to respect the expectations that 'setup' has of - Distribution: it must have a constructor and methods - 'parse_command_line()' and 'run_commands()' with signatures like - those described below.""" + """The core of the Distutils. Most of the work hiding behind 'setup' + is really done within a Distribution instance, which farms the work out + to the Distutils commands specified on the command line. + + Setup scripts will almost never instantiate Distribution directly, + unless the 'setup()' function is totally inadequate to their needs. + However, it is conceivable that a setup script might wish to subclass + Distribution for some specialized purpose, and then pass the subclass + to 'setup()' as the 'distclass' keyword argument. If so, it is + necessary to respect the expectations that 'setup' has of Distribution. + See the code for 'setup()', in core.py, for details. + """ # 'global_options' describes the command-line options that may be @@ -98,14 +97,14 @@ class Distribution: def __init__ (self, attrs=None): """Construct a new Distribution instance: initialize all the - attributes of a Distribution, and then uses 'attrs' (a - dictionary mapping attribute names to values) to assign - some of those attributes their "real" values. (Any attributes - not mentioned in 'attrs' will be assigned to some null - value: 0, None, an empty list or dictionary, etc.) Most - importantly, initialize the 'command_obj' attribute - to the empty dictionary; this will be filled in with real - command objects by 'parse_command_line()'.""" + attributes of a Distribution, and then use 'attrs' (a dictionary + mapping attribute names to values) to assign some of those + attributes their "real" values. (Any attributes not mentioned in + 'attrs' will be assigned to some null value: 0, None, an empty list + or dictionary, etc.) Most importantly, initialize the + 'command_obj' attribute to the empty dictionary; this will be + filled in with real command objects by 'parse_command_line()'. + """ # Default values for our command-line options self.verbose = 1 @@ -387,7 +386,6 @@ class Distribution: # parse_command_line() def _parse_command_opts (self, parser, args): - """Parse the command-line options for a single command. 'parser' must be a FancyGetopt instance; 'args' must be the list of arguments, starting with the current command (whose options @@ -666,7 +664,6 @@ class Distribution: return cmd_obj def _set_command_options (self, command_obj, option_dict=None): - """Set the options for 'command_obj' from 'option_dict'. Basically this means copying elements of a dictionary ('option_dict') to attributes of an instance ('command'). -- cgit v1.2.1 From c857db21dbfe9234d637d3b65ffe37f49464d7d5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 01:49:58 +0000 Subject: Fairly massive overhaul to support getting RPM inputs (extra meta-data, prep/build/etc. scripts, doc files, dependency info) from a config file rather than the dedicated "package_info" file. (The idea is that developers will provide RPM-specific info in the "[bdist_rpm]" section of setup.cfg, but of course it could also be supplied in the other config files, on the command line, or in the setup script -- or any mix of the above.) Major changes: * added a boatload of options to 'user_options' and 'initialize_options()': 'distribution_name', 'group', 'release', ... * added 'finalize_package_data()', which takes the place of '_get_package_data()' -- except it's called from 'finalize_options()', not 'run()', so we have everything figured out before we actually run the command * added 'ensure_string()', 'ensure_string_list()', 'ensure_filename()'; these take the place of '_check_string()' and friends. (These actually look like really useful type-checking methods that could come in handy all over the Distutils; should consider moving them up to Command and using them in other command classes' 'finalize_options()' method for error-checking). * various cleanup, commentary, and adaptation to the new way of storing RPM info in '_make_spec_file()' --- command/bdist_rpm.py | 262 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 230 insertions(+), 32 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index c84fda87..b09b657f 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -7,7 +7,7 @@ distributions).""" __revision__ = "$Id$" -import os, string +import os, string, re from types import * from distutils.core import Command from distutils.util import get_platform, write_file @@ -28,6 +28,63 @@ class bdist_rpm (Command): "only generate binary RPM"), ('use-bzip2', None, "use bzip2 instead of gzip to create source distribution"), + + # More meta-data: too RPM-specific to put in the setup script, + # but needs to go in the .spec file -- so we make these options + # to "bdist_rpm". The idea is that packagers would put this + # info in setup.cfg, although they are of course free to + # supply it on the command line. + ('distribution-name', None, + "name of the (Linux) distribution name to which this " + "RPM applies (*not* the name of the module distribution!)"), + ('group', None, + "package classification [default: \"Development/Libraries\"]"), + ('release', None, + "RPM release number"), + ('serial', None, + "???"), + ('vendor', None, + "RPM \"vendor\" (eg. \"Joe Blow \") " + "[default: maintainer or author from setup script]"), + ('packager', None, + "RPM packager (eg. \"Jane Doe \")" + "[default: vendor]"), + ('doc-files', None, + "list of documentation files (space or comma-separated)"), + ('changelog', None, + "RPM changelog"), + ('icon', None, + "name of icon file"), + + ('prep-cmd', None, + "?? pre-build command(s) ??"), + ('build-cmd', None, + "?? build command(s) ??"), + ('install-cmd', None, + "?? installation command(s) ??"), + ('clean-cmd', None, + "?? clean command(s) ??"), + ('pre-install', None, + "pre-install script (Bourne shell code)"), + ('post-install', None, + "post-install script (Bourne shell code)"), + ('pre-uninstall', None, + "pre-uninstall script (Bourne shell code)"), + ('post-uninstall', None, + "post-uninstall script (Bourne shell code)"), + + ('provides', None, + "???"), + ('requires', None, + "???"), + ('conflicts', None, + "???"), + ('build-requires', None, + "???"), + ('obsoletes', None, + "???"), + + # Actions to take when building RPM ('clean', None, "clean up RPM build directory [default]"), ('no-clean', None, @@ -48,6 +105,32 @@ class bdist_rpm (Command): self.binary_only = None self.source_only = None self.use_bzip2 = None + + self.distribution_name = None + self.group = None + self.release = None + self.serial = None + self.vendor = None + self.packager = None + self.doc_files = None + self.changelog = None + self.icon = None + + self.prep_cmd = None + self.build_cmd = None + self.install_cmd = None + self.clean_cmd = None + self.pre_install = None + self.post_install = None + self.pre_uninstall = None + self.post_uninstall = None + self.prep = None + self.provides = None + self.requires = None + self.conflicts = None + self.build_requires = None + self.obsoletes = None + self.clean = 1 self.use_rpm_opt_flags = 1 @@ -63,15 +146,112 @@ class bdist_rpm (Command): if self.binary_only and self.source_only: raise DistutilsOptionsError, \ "cannot supply both '--source-only' and '--binary-only'" + # don't pass CFLAGS to pure python distributions if not self.distribution.has_ext_modules(): self.use_rpm_opt_flags = 0 + self.finalize_package_data() + # finalize_options() + def finalize_package_data (self): + self.ensure_string('group', "Development/Libraries") + self.ensure_string('vendor', + "%s <%s>" % (self.distribution.get_contact(), + self.distribution.get_contact_email())) + self.ensure_string('packager', self.vendor) # or nothing? + self.ensure_string_list('doc_files') + if type(self.doc_files) is ListType: + for readme in ('README', 'README.txt'): + if os.path.exists(readme) and readme not in self.doc_files: + self.doc.append(readme) + + self.ensure_string('release', "1") # should it be an int? + self.ensure_string('serial') # should it be an int? + + self.ensure_string('icon') + self.ensure_string('distribution_name') + + self.ensure_string('prep_cmd', "%setup") # string or filename? + + if self.use_rpm_opt_flags: + def_build = 'env CFLAGS="$RPM_OPT_FLAGS" python setup.py build' + else: + def_build = 'python setup.py build' + self.ensure_string('build_cmd', def_build) + self.ensure_string('install_cmd', + "python setup.py install --root=$RPM_BUILD_ROOT " + "--record=INSTALLED_FILES") + self.ensure_string('clean_cmd', + "rm -rf $RPM_BUILD_ROOT") + self.ensure_filename('pre_install') + self.ensure_filename('post_install') + self.ensure_filename('pre_uninstall') + self.ensure_filename('post_uninstall') + + # XXX don't forget we punted on summaries and descriptions -- they + # should be handled here eventually! + + # Now *this* is some meta-data that belongs in the setup script... + self.ensure_string_list('provides') + self.ensure_string_list('requires') + self.ensure_string_list('conflicts') + self.ensure_string_list('build_requires') + self.ensure_string_list('obsoletes') + + # finalize_package_data () + + + # XXX these look awfully handy: should probably move them + # up to Command and use more widely. + def _ensure_stringlike (self, option, what, default=None): + val = getattr(self, option) + if val is None: + setattr(self, option, default) + return default + elif type(val) is not StringType: + raise DistutilsOptionError, \ + "'%s' must be a %s (got `%s`)" % (option, what, val) + return val + + def ensure_string (self, option, default=None): + self._ensure_stringlike(option, "string", default) + + def ensure_string_list (self, option): + val = getattr(self, option) + if val is None: + return + elif type(val) is StringType: + setattr(self, option, re.split(r',\s*|\s+', val)) + else: + if type(val) is ListType: + types = map(type, val) + ok = (types == [StringType] * len(val)) + else: + ok = 0 + + if not ok: + raise DistutilsOptionError, \ + "'%s' must be a list of strings (got %s)" % \ + (option, `val`) + + def ensure_filename (self, option, default=None): + val = self._ensure_stringlike(option, "filename", None) + if val is not None and not os.path.exists(val): + raise DistutilsOptionError, \ + "error in '%s' option: file '%s' does not exist" % \ + (option, val) + + def run (self): - self._get_package_data() # get packaging info + + print "before _get_package_data():" + print "vendor =", self.vendor + print "packager =", self.packager + print "doc_files =", self.doc_files + print "changelog =", self.changelog # make directories if self.spec_only: @@ -206,9 +386,10 @@ class bdist_rpm (Command): self.obsoletes = join(self._check_string_list('obsoletes')) def _make_spec_file(self): - ''' Generate an RPM spec file ''' - - # definitons and headers + """Generate the text of an RPM spec file and return it as a + list of strings (one per line). + """ + # definitions and headers spec_file = [ '%define name ' + self.distribution.get_name(), '%define version ' + self.distribution.get_version(), @@ -218,18 +399,25 @@ class bdist_rpm (Command): ] # put locale summaries into spec file - for locale in self.summaries.keys(): - spec_file.append('Summary(%s): %s' % (locale, - self.summaries[locale])) + # XXX not supported for now (hard to put a dictionary + # in a config file -- arg!) + #for locale in self.summaries.keys(): + # spec_file.append('Summary(%s): %s' % (locale, + # self.summaries[locale])) spec_file.extend([ 'Name: %{name}', 'Version: %{version}', 'Release: %{release}',]) + + # XXX yuck! this filename is available from the "sdist" command, + # but only after it has run: and we create the spec file before + # running "sdist", in case of --spec-only. if self.use_bzip2: spec_file.append('Source0: %{name}-%{version}.tar.bz2') else: spec_file.append('Source0: %{name}-%{version}.tar.gz') + spec_file.extend([ 'Copyright: ' + self.distribution.get_licence(), 'Group: ' + self.group, @@ -247,9 +435,12 @@ class bdist_rpm (Command): 'Conflicts', 'Obsoletes', ): - if getattr(self, string.lower(field)): - spec_file.append('%s: %s' % - (field, getattr(self, string.lower(field)))) + val = getattr(self, string.lower(field)) + if type(val) is ListType: + spec_file.append('%s: %s' % (field, string.join(val))) + elif val is not None: + spec_file.append('%s: %s' % (field, val)) + if self.distribution.get_url() != 'UNKNOWN': spec_file.append('Url: ' + self.distribution.get_url()) @@ -258,7 +449,8 @@ class bdist_rpm (Command): spec_file.append('Distribution: ' + self.distribution_name) if self.build_requires: - spec_file.append('BuildRequires: ' + self.build_requires) + spec_file.append('BuildRequires: ' + + string.join(self.build_requires)) if self.icon: spec_file.append('Icon: ' + os.path.basename(self.icon)) @@ -270,28 +462,34 @@ class bdist_rpm (Command): ]) # put locale descriptions into spec file - for locale in self.descriptions.keys(): - spec_file.extend([ - '', - '%description -l ' + locale, - self.descriptions[locale], - ]) + # XXX again, suppressed because config file syntax doesn't + # easily support this ;-( + #for locale in self.descriptions.keys(): + # spec_file.extend([ + # '', + # '%description -l ' + locale, + # self.descriptions[locale], + # ]) # rpm scripts - for script in ('prep', - 'build', - 'install', - 'clean', - 'pre', - 'post', - 'preun', - 'postun', - ): - if getattr(self, script): + for (rpm_opt, attr) in (('prep', 'prep_cmd'), + ('build', 'build_cmd'), + ('install', 'install_cmd'), + ('clean', 'clean_cmd'), + ('pre', 'pre_install'), + ('post', 'post_install'), + ('preun', 'pre_uninstall'), + ('postun', 'post_uninstall')): + # XXX oops, this doesn't distinguish between "raw code" + # options and "script filename" options -- well, we probably + # should settle on one or the other, and not make the + # distinction! + val = getattr(self, attr) + if val: spec_file.extend([ '', - '%' + script, - getattr(self, script), + '%' + rpm_opt, + val ]) @@ -302,8 +500,8 @@ class bdist_rpm (Command): '%defattr(-,root,root)', ]) - if self.doc: - spec_file.append('%doc ' + self.doc) + if self.doc_files: + spec_file.append('%doc ' + string.join(self.doc_files)) if self.changelog: spec_file.extend([ -- cgit v1.2.1 From 4e66ce5352c3529cb79bf189c35da0b990b0993b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 01:52:04 +0000 Subject: Ditched the obsolete '_get_package_data()' method and its '_check_*()' helpers. --- command/bdist_rpm.py | 145 ++------------------------------------------------- 1 file changed, 3 insertions(+), 142 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index b09b657f..d37d6090 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -318,73 +318,6 @@ class bdist_rpm (Command): # run() - def _get_package_data(self): - ''' Get data needed to generate spec file, first from the - DistributionMetadata class, then from the package_data file, which is - Python code read with execfile() ''' - - from string import join - - package_type = 'rpm' - - # read in package data, if any - if os.path.exists('package_data'): - try: - exec(open('package_data')) - except: - raise DistutilsOptionError, 'Unable to parse package data file' - - # set instance variables, supplying default value if not provided in - # package data file - self.package_data = locals() - - # the following variables must be {string (len() = 2): string} - self.summaries = self._check_string_dict('summaries') - self.descriptions = self._check_string_dict('descriptions') - - # The following variable must be an ordinary number or a string - self.release = self._check_number_or_string('release', '1') - self.serial = self._check_number_or_string('serial') - - # The following variables must be strings - self.group = self._check_string('group', 'Development/Libraries') - self.vendor = self._check_string('vendor') - self.packager = self._check_string('packager') - self.changelog = self._check_string('changelog') - self.icon = self._check_string('icon') - self.distribution_name = self._check_string('distribution_name') - self.pre = self._check_string('pre') - self.post = self._check_string('post') - self.preun = self._check_string('preun') - self.postun = self._check_string('postun') - self.prep = self._check_string('prep', '%setup') - if self.use_rpm_opt_flags: - self.build = (self._check_string( - 'build', - 'env CFLAGS="$RPM_OPT_FLAGS" python setup.py build')) - else: - self.build = (self._check_string('build', 'python setup.py build')) - self.install = self._check_string( - 'install', - 'python setup.py install --root=$RPM_BUILD_ROOT --record') - self.clean = self._check_string( - 'clean', - 'rm -rf $RPM_BUILD_ROOT') - - # The following variables must be a list or tuple of strings, or a - # string - self.doc = self._check_string_list('doc') - if type(self.doc) == ListType: - for readme in ('README', 'README.txt'): - if os.path.exists(readme) and readme not in self.doc: - self.doc.append(readme) - self.doc = join(self.doc) - self.provides = join(self._check_string_list('provides')) - self.requires = join(self._check_string_list('requires')) - self.conflicts = join(self._check_string_list('conflicts')) - self.build_requires = join(self._check_string_list('build_requires')) - self.obsoletes = join(self._check_string_list('obsoletes')) - def _make_spec_file(self): """Generate the text of an RPM spec file and return it as a list of strings (one per line). @@ -512,78 +445,6 @@ class bdist_rpm (Command): return spec_file - def _check_string_dict(self, var_name, default_value = {}): - ''' Tests a wariable to determine if it is {string: string}, - var_name is the name of the wariable. Return the value if it is valid, - returns default_value if the variable does not exist, raises - DistutilsOptionError if the variable is not valid''' - if self.package_data.has_key(var_name): - pass_test = 1 # set to 0 if fails test - value = self.package_data[var_name] - if type(value) == DictType: - for locale in value.keys(): - if (type(locale) != StringType) or (type(value[locale]) != - StringType): - pass_test = 0 - break - if pass_test: - return test_me - raise DistutilsOptionError, \ - ("Error in package_data: '%s' must be dictionary: " - '{string: string}' % var_name) - else: - return default_value - - def _check_string(self, var_name, default_value = None): - ''' Tests a variable in package_data to determine if it is a string, - var_name is the name of the wariable. Return the value if it is a - string, returns default_value if the variable does not exist, raises - DistutilsOptionError if the variable is not a string''' - if self.package_data.has_key(var_name): - if type(self.package_data[var_name]) == StringType: - return self.package_data[var_name] - else: - raise DistutilsOptionError, \ - "Error in package_data: '%s' must be a string" % var_name - else: - return default_value - - def _check_number_or_string(self, var_name, default_value = None): - ''' Tests a variable in package_data to determine if it is a number or - a string, var_name is the name of the wariable. Return the value if it - is valid, returns default_value if the variable does not exist, raises - DistutilsOptionError if the variable is not valid''' - if self.package_data.has_key(var_name): - if type(self.package_data[var_name]) in (StringType, LongType, - IntType, FloatType): - return str(self.package_data[var_name]) - else: - raise DistutilsOptionError, \ - ("Error in package_data: '%s' must be a string or a " - 'number' % var_name) - else: - return default_value - - def _check_string_list(self, var_name, default_value = []): - ''' Tests a variable in package_data to determine if it is a string or - a list or tuple of strings, var_name is the name of the wariable. - Return the value as a string or a list if it is valid, returns - default_value if the variable does not exist, raises - DistutilsOptionError if the variable is not valid''' - if self.package_data.has_key(var_name): - value = self.package_data[var_name] - pass_test = 1 - if type(value) == StringType: - return value - elif type(value) in (ListType, TupleType): - for item in value: - if type(item) != StringType: - pass_test = 0 - break - if pass_test: - return list(value) - raise DistutilsOptionError, \ - ("Error in package_data: '%s' must be a string or a " - 'list or tuple of strings' % var_name) - else: - return default_value + # _make_spec_file () + +# class bdist_rpm -- cgit v1.2.1 From 1870024f295ec8b39849c562a75f6a11deb725b7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 01:55:36 +0000 Subject: Use Distribution method 'dump_option_dicts()' for debugging output, and only do so if DEBUG is true. --- core.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core.py b/core.py index 9e390ac1..2bad10e4 100644 --- a/core.py +++ b/core.py @@ -70,8 +70,6 @@ def setup (**attrs): object. """ - from pprint import pprint # for debugging output - # Determine the distribution class -- either caller-supplied or # our Distribution (see below). klass = attrs.get ('distclass') @@ -88,8 +86,9 @@ def setup (**attrs): # the setup script, but be overridden by the command line. dist.parse_config_files() - print "options (after parsing config files):" - pprint (dist.command_options) + if DEBUG: + print "options (after parsing config files):" + dist.dump_option_dicts() # Parse the command line; any command-line errors are the end user's # fault, so turn them into SystemExit to suppress tracebacks. @@ -99,8 +98,9 @@ def setup (**attrs): sys.stderr.write (usage + "\n") raise SystemExit, "error: %s" % msg - print "options (after parsing command line):" - pprint (dist.command_options) + if DEBUG: + print "options (after parsing command line):" + dist.dump_option_dicts() # And finally, run all the commands found on the command line. if ok: -- cgit v1.2.1 From 1d343163373ad5f9fa7fe5e11e44d4f7af9eabae Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 01:59:33 +0000 Subject: Only print debugging output if DEBUG true (and deleted some of the more extraneous debug prints). --- dist.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dist.py b/dist.py index 6acc0544..64f63add 100644 --- a/dist.py +++ b/dist.py @@ -294,15 +294,16 @@ class Distribution: def parse_config_files (self, filenames=None): from ConfigParser import ConfigParser + from distutils.core import DEBUG if filenames is None: filenames = self.find_config_files() - print "Distribution.parse_config_files():" + if DEBUG: print "Distribution.parse_config_files():" parser = ConfigParser() for filename in filenames: - print " reading", filename + if DEBUG: print " reading", filename parser.read(filename) for section in parser.sections(): options = parser.options(section) @@ -370,7 +371,6 @@ class Distribution: # latter, we omit the display-only options and show help for # each command listed on the command line. if self.help: - print "showing 'global' help; commands=", self.commands self._show_help(parser, display_options=len(self.commands) == 0, commands=self.commands) @@ -440,7 +440,6 @@ class Distribution: parser.set_negative_aliases (negative_opt) (args, opts) = parser.getopt (args[1:]) if hasattr(opts, 'help') and opts.help: - print "showing help for command", cmd_class self._show_help(parser, display_options=0, commands=[cmd_class]) return @@ -643,10 +642,12 @@ class Distribution: object for 'command' is in the cache, then we either create and return it (if 'create' is true) or return None. """ + from distutils.core import DEBUG cmd_obj = self.command_obj.get(command) if not cmd_obj and create: - print "Distribution.get_command_obj(): " \ - "creating '%s' command object" % command + if DEBUG: + print "Distribution.get_command_obj(): " \ + "creating '%s' command object" % command klass = self.get_command_class(command) cmd_obj = self.command_obj[command] = klass(self) -- cgit v1.2.1 From a3be3dbd094620fea37917c49fd6d93cabd4a16c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 02:01:51 +0000 Subject: Only print debugging output if DEBUG true. --- command/bdist_rpm.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index d37d6090..aecf5c9d 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -9,7 +9,7 @@ __revision__ = "$Id$" import os, string, re from types import * -from distutils.core import Command +from distutils.core import Command, DEBUG from distutils.util import get_platform, write_file from distutils.errors import * @@ -247,11 +247,12 @@ class bdist_rpm (Command): def run (self): - print "before _get_package_data():" - print "vendor =", self.vendor - print "packager =", self.packager - print "doc_files =", self.doc_files - print "changelog =", self.changelog + if DEBUG: + print "before _get_package_data():" + print "vendor =", self.vendor + print "packager =", self.packager + print "doc_files =", self.doc_files + print "changelog =", self.changelog # make directories if self.spec_only: -- cgit v1.2.1 From 9057f17f11ad3f506bab940ada1bd488f9add36a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 02:23:42 +0000 Subject: Bumped version number to 0.9pre (there will be a couple of code snapshots before the real release, but I want to make it clear that a major new release is on the way). --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 32fc276c..2ba82bd5 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.8.2" +__version__ = "0.9pre" -- cgit v1.2.1 From 821ff992cc9ca03f511e1e7d69a71c37f1e80938 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 3 Jun 2000 00:44:30 +0000 Subject: Added a bunch of new globals in '_init_mac()' -- these will be needed to support the forthcoming Cygwin/Mingw32 GCC-on-Windows patch. Standardized CVS id line. --- sysconfig.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index a5f3816a..53da4826 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -6,7 +6,7 @@ Email: Initial date: 17-Dec-1998 """ -__version__ = "$Revision$" +__revision__ = "$Id$" import os import re @@ -255,6 +255,20 @@ def _init_nt(): g['SO'] = '.pyd' g['exec_prefix'] = EXEC_PREFIX + # These are needed for the CygwinCCompiler and Mingw32CCompiler + # classes, which are just UnixCCompiler classes that happen to work on + # Windows. UnixCCompiler expects to find these values in sysconfig, so + # here they are. The fact that other Windows compilers don't need + # these values is pure luck (hmmm). + g['CC'] = "cc" # not gcc? + g['RANLIB'] = "ranlib" + g['AR'] = "ar" + g['OPT'] = "-O2" + g['SO'] = ".pyd" + g['LDSHARED'] = "ld" + g['CCSHARED'] = "" + g['EXE'] = ".exe" + def _init_mac(): """Initialize the module as appropriate for Macintosh systems""" -- cgit v1.2.1 From c5268b3d895061e69e712f4e375e8997a03de8e3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 3 Jun 2000 01:02:06 +0000 Subject: Catch DistutilSetupError from the Distribution constructor. --- core.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core.py b/core.py index 2bad10e4..8bcf2a87 100644 --- a/core.py +++ b/core.py @@ -80,7 +80,10 @@ def setup (**attrs): # Create the Distribution instance, using the remaining arguments # (ie. everything except distclass) to initialize it - dist = klass (attrs) + try: + dist = klass (attrs) + except DistutilsSetupError, msg: + raise SystemExit, "error in setup script: %s" % msg # Find and parse the config file(s): they will override options from # the setup script, but be overridden by the command line. -- cgit v1.2.1 From 85ef7f4621c78621e4a2858b21c950cede345f8f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 3 Jun 2000 01:03:55 +0000 Subject: Patch from Harry Henry Gebel: fixes a bit of code that slipped by my overhaul last night. --- command/bdist_rpm.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index aecf5c9d..9bce04c1 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -308,9 +308,8 @@ class bdist_rpm (Command): rpm_args.append('-bb') else: rpm_args.append('-ba') - topdir = os.getcwd() + 'build/rpm' rpm_args.extend(['--define', - '_topdir ' + os.getcwd() + '/build/rpm',]) + '_topdir %s/%s' % (os.getcwd(), rpm_base),]) if self.clean: rpm_args.append('--clean') rpm_args.append(spec_path) -- cgit v1.2.1 From 4e412ef49e4dc89329ceb1a213f4b695f611e080 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 13:42:52 +0000 Subject: Renamed 'modules' option to 'py_modules', for consistency with Distribution (and in order to generate a more sensible error message cleanly). --- command/build_py.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 92a37f20..0405d392 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -26,7 +26,7 @@ class build_py (Command): def initialize_options (self): self.build_lib = None - self.modules = None + self.py_modules = None self.package = None self.package_dir = None self.force = None @@ -39,7 +39,7 @@ class build_py (Command): # Get the distribution options that are aliases for build_py # options -- list of packages and list of modules. self.packages = self.distribution.packages - self.modules = self.distribution.py_modules + self.py_modules = self.distribution.py_modules self.package_dir = self.distribution.package_dir @@ -62,7 +62,7 @@ class build_py (Command): # installing). # Two options control which modules will be installed: 'packages' - # and 'modules'. The former lets us work with whole packages, not + # and 'py_modules'. The former lets us work with whole packages, not # specifying individual modules at all; the latter is for # specifying modules one-at-a-time. Currently they are mutually # exclusive: you can define one or the other (or neither), but not @@ -70,17 +70,17 @@ class build_py (Command): # Dispose of the two "unusual" cases first: no pure Python modules # at all (no problem, just return silently), and over-specified - # 'packages' and 'modules' options. + # 'packages' and 'py_modules' options. - if not self.modules and not self.packages: + if not self.py_modules and not self.packages: return - if self.modules and self.packages: + if self.py_modules and self.packages: raise DistutilsOptionError, \ - "build_py: supplying both 'packages' and 'modules' " + \ + "build_py: supplying both 'packages' and 'py_modules' " + \ "options is not allowed" - # Now we're down to two cases: 'modules' only and 'packages' only. - if self.modules: + # Now we're down to two cases: 'py_modules' only and 'packages' only. + if self.py_modules: self.build_modules () else: self.build_packages () @@ -194,7 +194,7 @@ class build_py (Command): def find_modules (self): """Finds individually-specified Python modules, ie. those listed by - module name in 'self.modules'. Returns a list of tuples (package, + module name in 'self.py_modules'. Returns a list of tuples (package, module_base, filename): 'package' is a tuple of the path through package-space to the module; 'module_base' is the bare (no packages, no dots) module name, and 'filename' is the path to the @@ -218,7 +218,7 @@ class build_py (Command): # string or empty list, depending on context). Differences: # - don't check for __init__.py in directory for empty package - for module in self.modules: + for module in self.py_modules: path = string.split (module, '.') package = tuple (path[0:-1]) module_base = path[-1] @@ -251,12 +251,12 @@ class build_py (Command): def find_all_modules (self): """Compute the list of all modules that will be built, whether - they are specified one-module-at-a-time ('self.modules') or + they are specified one-module-at-a-time ('self.py_modules') or by whole packages ('self.packages'). Return a list of tuples (package, module, module_file), just like 'find_modules()' and 'find_package_modules()' do.""" - if self.modules: + if self.py_modules: modules = self.find_modules () else: modules = [] -- cgit v1.2.1 From 32b0296e148d27447ca6b7154f8fd2d94b6a6a5e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 14:20:57 +0000 Subject: Removed the 'ensure_*' methods -- they're just too handy too keep in one command class, so they're now in the Command base class. --- command/bdist_rpm.py | 44 +------------------------------------------- 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 9bce04c1..4e7675e8 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -7,7 +7,7 @@ distributions).""" __revision__ = "$Id$" -import os, string, re +import os, string from types import * from distutils.core import Command, DEBUG from distutils.util import get_platform, write_file @@ -203,48 +203,6 @@ class bdist_rpm (Command): # finalize_package_data () - # XXX these look awfully handy: should probably move them - # up to Command and use more widely. - def _ensure_stringlike (self, option, what, default=None): - val = getattr(self, option) - if val is None: - setattr(self, option, default) - return default - elif type(val) is not StringType: - raise DistutilsOptionError, \ - "'%s' must be a %s (got `%s`)" % (option, what, val) - return val - - def ensure_string (self, option, default=None): - self._ensure_stringlike(option, "string", default) - - def ensure_string_list (self, option): - val = getattr(self, option) - if val is None: - return - elif type(val) is StringType: - setattr(self, option, re.split(r',\s*|\s+', val)) - else: - if type(val) is ListType: - types = map(type, val) - ok = (types == [StringType] * len(val)) - else: - ok = 0 - - if not ok: - raise DistutilsOptionError, \ - "'%s' must be a list of strings (got %s)" % \ - (option, `val`) - - def ensure_filename (self, option, default=None): - val = self._ensure_stringlike(option, "filename", None) - if val is not None and not os.path.exists(val): - raise DistutilsOptionError, \ - "error in '%s' option: file '%s' does not exist" % \ - (option, val) - - - def run (self): if DEBUG: -- cgit v1.2.1 From f126db7e1143b197ef275f5545fc39258124fa87 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 14:21:28 +0000 Subject: Added the 'ensure_*' methods from bdist_rpm; refactored 'ensure_filename()' and added 'ensure_dirname()'. --- cmd.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/cmd.py b/cmd.py index b0a4b959..9f63f838 100644 --- a/cmd.py +++ b/cmd.py @@ -9,7 +9,7 @@ in the distutils.command package. __revision__ = "$Id$" -import sys, os, string +import sys, os, string, re from types import * from distutils.errors import * from distutils import util @@ -173,6 +173,77 @@ class Command: print msg + # -- Option validation methods ------------------------------------- + # (these are very handy in writing the 'finalize_options()' method) + # + # NB. the general philosophy here is to ensure that a particular option + # value meets certain type and value constraints. If not, we try to + # force it into conformance (eg. if we expect a list but have a string, + # split the string on comma and/or whitespace). If we can't force the + # option into conformance, raise DistutilsOptionError. Thus, command + # classes need do nothing more than (eg.) + # self.ensure_string_list('foo') + # and they can be guaranteed that thereafter, self.foo will be + # a list of strings. + + def _ensure_stringlike (self, option, what, default=None): + val = getattr(self, option) + if val is None: + setattr(self, option, default) + return default + elif type(val) is not StringType: + raise DistutilsOptionError, \ + "'%s' must be a %s (got `%s`)" % (option, what, val) + return val + + def ensure_string (self, option, default=None): + """Ensure that 'option' is a string; if not defined, set it to + 'default'. + """ + self._ensure_stringlike(option, "string", default) + + def ensure_string_list (self, option): + """Ensure that 'option' is a list of strings. If 'option' is + currently a string, we split it either on /,\s*/ or /\s+/, so + "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become + ["foo", "bar", "baz"]. + """ + val = getattr(self, option) + if val is None: + return + elif type(val) is StringType: + setattr(self, option, re.split(r',\s*|\s+', val)) + else: + if type(val) is ListType: + types = map(type, val) + ok = (types == [StringType] * len(val)) + else: + ok = 0 + + if not ok: + raise DistutilsOptionError, \ + "'%s' must be a list of strings (got %s)" % \ + (option, `val`) + + def _ensure_tested_string (self, option, tester, + what, error_fmt, default=None): + val = self._ensure_stringlike(option, what, default) + if val is not None and not tester(val): + raise DistutilsOptionError, \ + ("error in '%s' option: " + error_fmt) % (option, val) + + def ensure_filename (self, option): + """Ensure that 'option' is the name of an existing file.""" + self._ensure_tested_string(option, os.path.isfile, + "filename", + "'%s' does not exist or is not a file") + + def ensure_dirname (self, option): + self._ensure_tested_string(option, os.path.isdir, + "directory name", + "'%s' does not exist or is not a directory") + + # -- Convenience methods for commands ------------------------------ def get_command_name (self): -- cgit v1.2.1 From bf680c9cda3ae15b841455b9b8f8b858eb9fffd4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 15:00:34 +0000 Subject: Patch from Harry Henry Gebel: Fills in question marks in help Reads scripts in from files rather than strings Adds RPM 2 compatibility mode (untested). Use of this mode requires that --bdist-base be specified because bdist_rpm has no way of detecting where RPM wants to find spec files and source files. An unmodified RedHat 5.0 system would require '--bdist-base=/usr/src/RedHat'. (You would also have to be root.) If the rpmrc file has been modified to allow RPMs to be built by normal users then --build-base would need to be changed accordingly. Formats the changelog. GPW: tweaked formatting, added some editorial comments. --- command/bdist_rpm.py | 162 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 103 insertions(+), 59 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 4e7675e8..612629fe 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -42,7 +42,7 @@ class bdist_rpm (Command): ('release', None, "RPM release number"), ('serial', None, - "???"), + "RPM serial number"), ('vendor', None, "RPM \"vendor\" (eg. \"Joe Blow \") " "[default: maintainer or author from setup script]"), @@ -52,18 +52,17 @@ class bdist_rpm (Command): ('doc-files', None, "list of documentation files (space or comma-separated)"), ('changelog', None, - "RPM changelog"), + "path to RPM changelog"), ('icon', None, "name of icon file"), - - ('prep-cmd', None, - "?? pre-build command(s) ??"), - ('build-cmd', None, - "?? build command(s) ??"), - ('install-cmd', None, - "?? installation command(s) ??"), - ('clean-cmd', None, - "?? clean command(s) ??"), + ('prep-script', None, + "pre-build script (Bourne shell code)"), + ('build-script', None, + "build script (Bourne shell code)"), + ('install-script', None, + "installation script (Bourne shell code)"), + ('clean-script', None, + "clean script (Bourne shell code)"), ('pre-install', None, "pre-install script (Bourne shell code)"), ('post-install', None, @@ -72,17 +71,16 @@ class bdist_rpm (Command): "pre-uninstall script (Bourne shell code)"), ('post-uninstall', None, "post-uninstall script (Bourne shell code)"), - ('provides', None, - "???"), + "capabilities provided by this package"), ('requires', None, - "???"), + "capabilities required by this package"), ('conflicts', None, - "???"), + "capabilities which conflict with this package"), ('build-requires', None, - "???"), + "capabilities required to build this package"), ('obsoletes', None, - "???"), + "capabilities made obsolete by this package"), # Actions to take when building RPM ('clean', None, @@ -93,10 +91,15 @@ class bdist_rpm (Command): "compile with RPM_OPT_FLAGS when building from source RPM"), ('no-rpm-opt-flags', None, "do not pass any RPM CFLAGS to compiler"), + ('rpm3-mode', None, + "RPM 3 compatibility mode (default)"), + ('rpm2-mode', None, + "RPM 2 compatibility mode"), ] negative_opt = {'no-clean': 'clean', - 'no-rpm-opt-flags': 'use-rpm-opt-flags'} + 'no-rpm-opt-flags': 'use-rpm-opt-flags', + 'rpm2-mode': 'rpm3-mode'} def initialize_options (self): @@ -116,10 +119,10 @@ class bdist_rpm (Command): self.changelog = None self.icon = None - self.prep_cmd = None - self.build_cmd = None - self.install_cmd = None - self.clean_cmd = None + self.prep_script = None + self.build_script = None + self.install_script = None + self.clean_script = None self.pre_install = None self.post_install = None self.pre_uninstall = None @@ -133,6 +136,7 @@ class bdist_rpm (Command): self.clean = 1 self.use_rpm_opt_flags = 1 + self.rpm3_mode = 1 # initialize_options() @@ -160,31 +164,28 @@ class bdist_rpm (Command): self.ensure_string('vendor', "%s <%s>" % (self.distribution.get_contact(), self.distribution.get_contact_email())) - self.ensure_string('packager', self.vendor) # or nothing? + self.ensure_string('packager') self.ensure_string_list('doc_files') if type(self.doc_files) is ListType: for readme in ('README', 'README.txt'): if os.path.exists(readme) and readme not in self.doc_files: self.doc.append(readme) - self.ensure_string('release', "1") # should it be an int? + self.ensure_string('release', "1") self.ensure_string('serial') # should it be an int? - self.ensure_string('icon') self.ensure_string('distribution_name') - self.ensure_string('prep_cmd', "%setup") # string or filename? + self.ensure_string('changelog') + # Format changelog correctly + self.changelog = self._format_changelog(self.changelog) - if self.use_rpm_opt_flags: - def_build = 'env CFLAGS="$RPM_OPT_FLAGS" python setup.py build' - else: - def_build = 'python setup.py build' - self.ensure_string('build_cmd', def_build) - self.ensure_string('install_cmd', - "python setup.py install --root=$RPM_BUILD_ROOT " - "--record=INSTALLED_FILES") - self.ensure_string('clean_cmd', - "rm -rf $RPM_BUILD_ROOT") + self.ensure_filename('icon') + + self.ensure_filename('prep_script') + self.ensure_filename('build_script') + self.ensure_filename('install_script') + self.ensure_filename('clean_script') self.ensure_filename('pre_install') self.ensure_filename('post_install') self.ensure_filename('pre_uninstall') @@ -217,7 +218,11 @@ class bdist_rpm (Command): spec_dir = "dist" self.mkpath(spec_dir) # XXX should be configurable else: - rpm_base = os.path.join(self.bdist_base, "rpm") + if self.rpm3_mode: + rpm_base = os.path.join(self.bdist_base, "rpm") + else: + # complete path must be specified in RPM 2 mode + rpm_base = self.bdist_base rpm_dir = {} for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): rpm_dir[d] = os.path.join(rpm_base, d) @@ -266,8 +271,9 @@ class bdist_rpm (Command): rpm_args.append('-bb') else: rpm_args.append('-ba') - rpm_args.extend(['--define', - '_topdir %s/%s' % (os.getcwd(), rpm_base),]) + if self.rpm3_mode: + rpm_args.extend(['--define', + '_topdir %s/%s' % (os.getcwd(), rpm_base),]) if self.clean: rpm_args.append('--clean') rpm_args.append(spec_path) @@ -363,27 +369,45 @@ class bdist_rpm (Command): # ]) # rpm scripts - for (rpm_opt, attr) in (('prep', 'prep_cmd'), - ('build', 'build_cmd'), - ('install', 'install_cmd'), - ('clean', 'clean_cmd'), - ('pre', 'pre_install'), - ('post', 'post_install'), - ('preun', 'pre_uninstall'), - ('postun', 'post_uninstall')): - # XXX oops, this doesn't distinguish between "raw code" - # options and "script filename" options -- well, we probably - # should settle on one or the other, and not make the - # distinction! + # figure out default build script + if self.use_rpm_opt_flags: + def_build = 'env CFLAGS="$RPM_OPT_FLAGS" python setup.py build' + else: + def_build = 'python setup.py build' + # insert contents of files + + # XXX this is kind of misleading: user-supplied options are files + # that we open and interpolate into the spec file, but the defaults + # are just text that we drop in as-is. Hmmm. + + script_options = [ + ('prep', 'prep_script', "%setup"), + ('build', 'build_script', def_build), + ('install', 'install_script', + "python setup.py install " + "--root=$RPM_BUILD_ROOT " + "--record=INSTALLED_FILES"), + ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), + ('pre', 'pre_install', None), + ('post', 'post_install', None), + ('preun', 'pre_uninstall', None), + ('postun', 'post_uninstall', None)) + ] + + for (rpm_opt, attr, default) in script_options: + # Insert contents of file refered to, if no file is refered to + # use 'default' as contents of script val = getattr(self, attr) - if val: + if val or default: spec_file.extend([ '', - '%' + rpm_opt, - val - ]) + '%' + rpm_opt,]) + if val: + spec_file.extend(string.split(open(val, 'r').read(), '\n')) + else: + spec_file.append(default) + - # files section spec_file.extend([ '', @@ -397,12 +421,32 @@ class bdist_rpm (Command): if self.changelog: spec_file.extend([ '', - '%changelog', - self.changelog - ]) + '%changelog',]) + spec_file.extend(self.changelog) return spec_file # _make_spec_file () + def _format_changelog(self, changelog): + """Format the changelog correctly and convert it to a list of strings + """ + new_changelog = [] + for line in string.split(string.strip(changelog), '\n'): + line = string.strip(line) + if line[0] == '*': + new_changelog.extend(['', line]) + elif line[0] == '-': + new_changelog.append(line) + else: + new_changelog.append(' ' + line) + + # strip trailing newline inserted by first changelog entry + if not new_changelog[0]: + del new_changelog[0] + + return new_changelog + + # _format_changelog() + # class bdist_rpm -- cgit v1.2.1 From 2b0f2098a922464c8891848b2ef9da402ed0398b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 15:12:51 +0000 Subject: Use 'ensure_string_list()' for 'formats' option, so that it can be spelled sensibly in a config file. --- command/sdist.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 03de85b1..af88eba0 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -73,6 +73,7 @@ class sdist (Command): if self.template is None: self.template = "MANIFEST.in" + self.ensure_string_list('formats') if self.formats is None: try: self.formats = [self.default_format[os.name]] @@ -80,8 +81,6 @@ class sdist (Command): raise DistutilsPlatformError, \ "don't know how to create source distributions " + \ "on platform %s" % os.name - elif type (self.formats) is StringType: - self.formats = string.split (self.formats, ',') bad_format = check_archive_formats (self.formats) if bad_format: -- cgit v1.2.1 From a74d191319d246a424b5de2e1ca55dce5cd4d799 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 15:30:35 +0000 Subject: Fixed syntax error. Half-fixed RPM 2 compatibility:added 'rpm_base' option, which must be set (to eg. /usr/src/redhat on a stock Red Hat system) if rpm2_mode is on. Still not quite working, though. --- command/bdist_rpm.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 612629fe..0959ad8f 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -18,8 +18,11 @@ class bdist_rpm (Command): description = "create an RPM distribution" user_options = [ - ('bdist-base', None, + ('bdist-base=', None, "base directory for creating built distributions"), + ('rpm-base=', None, + "base directory for creating RPMs (defaults to \"rpm\" under " + "--bdist-base; must be specified for RPM 2)"), ('spec-only', None, "only regenerate spec file"), ('source-only', None, @@ -104,6 +107,7 @@ class bdist_rpm (Command): def initialize_options (self): self.bdist_base = None + self.rpm_base = None self.spec_only = None self.binary_only = None self.source_only = None @@ -143,6 +147,12 @@ class bdist_rpm (Command): def finalize_options (self): self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) + if self.rpm_base is None: + if not self.rpm3_mode: + raise DistutilsOptionError, \ + "you must specify --rpm-base in RPM 2 mode" + self.rpm_base = os.path.join(self.bdist_base, "rpm") + if os.name != 'posix': raise DistutilsPlatformError, \ ("don't know how to create RPM " @@ -218,14 +228,9 @@ class bdist_rpm (Command): spec_dir = "dist" self.mkpath(spec_dir) # XXX should be configurable else: - if self.rpm3_mode: - rpm_base = os.path.join(self.bdist_base, "rpm") - else: - # complete path must be specified in RPM 2 mode - rpm_base = self.bdist_base rpm_dir = {} for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): - rpm_dir[d] = os.path.join(rpm_base, d) + rpm_dir[d] = os.path.join(self.rpm_base, d) self.mkpath(rpm_dir[d]) spec_dir = rpm_dir['SPECS'] @@ -273,7 +278,7 @@ class bdist_rpm (Command): rpm_args.append('-ba') if self.rpm3_mode: rpm_args.extend(['--define', - '_topdir %s/%s' % (os.getcwd(), rpm_base),]) + '_topdir %s/%s' % (os.getcwd(), self.rpm_base),]) if self.clean: rpm_args.append('--clean') rpm_args.append(spec_path) @@ -391,7 +396,7 @@ class bdist_rpm (Command): ('pre', 'pre_install', None), ('post', 'post_install', None), ('preun', 'pre_uninstall', None), - ('postun', 'post_uninstall', None)) + ('postun', 'post_uninstall', None), ] for (rpm_opt, attr, default) in script_options: -- cgit v1.2.1 From 448d6882a1ad6730e07c74b34be274cfadfbd514 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 6 Jun 2000 02:18:13 +0000 Subject: 'get_outputs()' now returns an empty list instead of None. --- command/install_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_data.py b/command/install_data.py index acc89aa3..5481dabe 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -56,4 +56,4 @@ class install_data (Command): return self.data_files or [] def get_outputs (self): - return self.outfiles + return self.outfiles or [] -- cgit v1.2.1 From 6a1d2a4ef333b3c52fbd11a6f4ba261349aae97c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 6 Jun 2000 02:51:38 +0000 Subject: Support for multiple distribution formats in one run. --- command/bdist.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index df4e3a03..3cd1eb03 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -20,9 +20,9 @@ class bdist (Command): user_options = [('bdist-base=', 'b', "temporary directory for creating built distributions"), - ('format=', 'f', - "format for distribution " + - "(tar, ztar, gztar, bztar, zip, ... )"), + ('formats=', None, + "formats for distribution " + + "(gztar, bztar, zip, rpm, ... )"), ] # The following commands do not take a format option from bdist @@ -43,7 +43,7 @@ class bdist (Command): def initialize_options (self): self.bdist_base = None - self.format = None + self.formats = None # initialize_options() @@ -57,31 +57,32 @@ class bdist (Command): plat = get_platform() self.bdist_base = os.path.join (build_base, 'bdist.' + plat) - if self.format is None: + self.ensure_string_list('formats') + if self.formats is None: try: - self.format = self.default_format[os.name] + self.formats = [self.default_format[os.name]] except KeyError: raise DistutilsPlatformError, \ "don't know how to create built distributions " + \ "on platform %s" % os.name - #elif type (self.format) is StringType: - # self.format = string.split (self.format, ',') # finalize_options() def run (self): - try: - cmd_name = self.format_command[self.format] - except KeyError: - raise DistutilsOptionError, \ - "invalid archive format '%s'" % self.format + for format in self.formats: - if cmd_name not in self.no_format_option: - sub_cmd = self.get_finalized_command (cmd_name) - sub_cmd.format = self.format - self.run_command (cmd_name) + try: + cmd_name = self.format_command[self.format] + except KeyError: + raise DistutilsOptionError, \ + "invalid format '%s'" % self.format + + sub_cmd = self.reinitialize_command(cmd_name) + if cmd_name not in self.no_format_option: + sub_cmd.format = self.format + self.run_command (cmd_name) # run() -- cgit v1.2.1 From ea60038bb5f5d80fefb16772d62e90d2d2508dc5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 6 Jun 2000 02:52:36 +0000 Subject: Fix 'reinitialize_command()' so it resets the 'have_run' flag for the command being reinitialized to false. --- dist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dist.py b/dist.py index 64f63add..44f5c880 100644 --- a/dist.py +++ b/dist.py @@ -712,6 +712,7 @@ class Distribution: return command command.initialize_options() command.finalized = 0 + self.have_run[command_name] = 0 self._set_command_options(command) return command -- cgit v1.2.1 From 356f7580cd5f33456c296a1c8addd207d67a4548 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 6 Jun 2000 02:57:07 +0000 Subject: First crack at the Distutils "config" command. Unlike other commands, this one doesn't *do* anything by default; it's just there as a conduit for data (eg. include dirs, libraries) from the user to the "build" commands. However, it provides a couple of Autoconf-ish methods ('try_compile()', 'try_link()', 'try_run()') that derived, per-distribution "config" commands can use to poke around the target system and see what's available. Initial experimenst with mxDateTime indicate that higher-level methods are necessary: analogs of Autoconf's AC_CHECK_HEADER, AC_CHECK_LIB will be needed too (and that's just to probe the C/C++ system: how to probe the Python system is wide open, and someday we'll have to worry about probing a Java system too). --- command/config.py | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 command/config.py diff --git a/command/config.py b/command/config.py new file mode 100644 index 00000000..cad75bcb --- /dev/null +++ b/command/config.py @@ -0,0 +1,180 @@ +"""distutils.command.config + +Implements the Distutils 'config' command, a (mostly) empty command class +that exists mainly to be sub-classed by specific module distributions and +applications. The idea is that while every "config" command is different, +at least they're all named the same, and users always see "config" in the +list of standard commands. Also, this is a good place to put common +configure-like tasks: "try to compile this C code", or "figure out where +this header file lives". +""" + +# created 2000/05/29, Greg Ward + +__revision__ = "$Id$" + +import os, string +from distutils.core import Command +from distutils.errors import DistutilsExecError + + +LANG_EXT = {'c': '.c', + 'c++': '.cxx'} + +class config (Command): + + description = "prepare to build" + + user_options = [ + ('compiler=', None, + "specify the compiler type"), + ('cc=', None, + "specify the compiler executable"), + ('include-dirs=', 'I', + "list of directories to search for header files"), + ('define=', 'D', + "C preprocessor macros to define"), + ('undef=', 'U', + "C preprocessor macros to undefine"), + ('libraries=', 'l', + "external C libraries to link with"), + ('library-dirs=', 'L', + "directories to search for external C libraries"), + ] + + + # The three standard command methods: since the "config" command + # does nothing by default, these are empty. + + def initialize_options (self): + self.compiler = None + self.cc = None + self.include_dirs = None + #self.define = None + #self.undef = None + self.libraries = None + self.library_dirs = None + + def finalize_options (self): + pass + + def run (self): + pass + + + # Utility methods for actual "config" commands. The interfaces are + # loosely based on Autoconf macros of similar names. Sub-classes + # may use these freely. + + def _check_compiler (self): + """Check that 'self.compiler' really is a CCompiler object; + if not, make it one. + """ + # We do this late, and only on-demand, because this is an expensive + # import. + from distutils.ccompiler import CCompiler, new_compiler + if not isinstance(self.compiler, CCompiler): + self.compiler = new_compiler (compiler=self.compiler, + verbose=self.verbose, # for now + dry_run=self.dry_run, + force=1) + if self.include_dirs: + self.compiler.set_include_dirs(self.include_dirs) + if self.libraries: + self.compiler.set_libraries(self.libraries) + if self.library_dirs: + self.compiler.set_library_dirs(self.library_dirs) + + + def _gen_temp_sourcefile (self, body, lang): + filename = "_configtest" + LANG_EXT[lang] + file = open(filename, "w") + file.write(body) + file.close() + return filename + + def _compile (self, body, lang): + src = self._gen_temp_sourcefile(body, lang) + (obj,) = self.compiler.compile([src]) + return (src, obj) + + def _link (self, body, lang): + (src, obj) = self._compile(body, lang) + exe = os.path.splitext(os.path.basename(src))[0] + self.compiler.link_executable([obj], exe) + return (src, obj, exe) + + def _clean (self, *filenames): + self.announce("removing: " + string.join(filenames)) + for filename in filenames: + try: + os.remove(filename) + except OSError: + pass + + + # XXX no 'try_cpp()' or 'search_cpp()' since the CCompiler interface + # does not provide access to the preprocessor. This is an oversight + # that should be fixed. + + # XXX these ignore the dry-run flag: what to do, what to do? even if + # you want a dry-run build, you still need some sort of configuration + # info. My inclination is to make it up to the real config command to + # consult 'dry_run', and assume a default (minimal) configuration if + # true. The problem with trying to do it here is that you'd have to + # return either true or false from all the 'try' methods, neither of + # which is correct. + + def try_compile (self, body, lang="c"): + """Try to compile a source file that consists of the text in 'body' + (a multi-line string). Return true on success, false + otherwise. + """ + from distutils.ccompiler import CompileError + self._check_compiler() + try: + (src, obj) = self._compile(body, lang) + ok = 1 + except CompileError: + ok = 0 + + self.announce(ok and "success!" or "failure.") + self._clean(src, obj) + return ok + + def try_link (self, body, lang="c"): + """Try to compile and link a source file (to an executable) that + consists of the text in 'body' (a multi-line string). Return true + on success, false otherwise. + """ + from distutils.ccompiler import CompileError, LinkError + self._check_compiler() + try: + (src, obj, exe) = self._link(body, lang) + ok = 1 + except (CompileError, LinkError): + ok = 0 + + self.announce(ok and "success!" or "failure.") + self._clean(src, obj, exe) + return ok + + def try_run (self, body, lang="c"): + """Try to compile, link to an executable, and run a program that + consists of the text in 'body'. Return true on success, false + otherwise. + """ + from distutils.ccompiler import CompileError, LinkError + self._check_compiler() + try: + (src, obj, exe) = self._link(body, lang) + self.spawn([exe]) + ok = 1 + except (CompileError, LinkError, DistutilsExecError): + ok = 0 + + self.announce(ok and "success!" or "failure.") + self._clean(src, obj, exe) + return ok + +# class config -- cgit v1.2.1 From 86b8b566efbb265aaa76d90658759dad90a5d31c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 7 Jun 2000 02:26:19 +0000 Subject: Patch from Rene Liebscher: Look for personal config file in /home/greg on Windows, too: users will have to set /home/greg to use this, so it's not something that many people will use. But if python-dev comes up with the "right way" to divine a home directory on Windows, we can use that to set /home/greg and poof! -- personal Distutils config files on Windows. --- dist.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/dist.py b/dist.py index 44f5c880..ea4ec2e7 100644 --- a/dist.py +++ b/dist.py @@ -263,24 +263,25 @@ class Distribution: and setup.cfg in the current directory. """ files = [] - if os.name == "posix": - check_environ() - - sys_dir = os.path.dirname(sys.modules['distutils'].__file__) - sys_file = os.path.join(sys_dir, "pydistutils.cfg") - if os.path.isfile(sys_file): - files.append(sys_file) - - user_file = os.path.join(os.environ.get('HOME'), - ".pydistutils.cfg") + check_environ() + + if os.name=='posix': + sys_dir = os.path.dirname(sys.modules['distutils'].__file__) + user_filename = ".pydistutils.cfg" + else: + sys_dir = sysconfig.PREFIX + user_filename = "pydistutils.cfg" + + sys_file = os.path.join(sys_dir, "pydistutils.cfg") + if os.path.isfile(sys_file): + files.append(sys_file) + + if os.environ.has_key('HOME'): + user_file = os.path.join(os.environ.get('HOME'), + user_filename) if os.path.isfile(user_file): files.append(user_file) - else: - sys_file = os.path.join (sysconfig.PREFIX, "pydistutils.cfg") - if os.path.isfile(sys_file): - files.append(sys_file) - # All platforms support local setup.cfg local_file = "setup.cfg" if os.path.isfile(local_file): -- cgit v1.2.1 From daadfabdb5b0395712504823a574111262fd5361 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 7 Jun 2000 02:29:03 +0000 Subject: Always look for the system config file in the Distutils module directory, and call it "distutils.cfg" instead of "pydistutils.cfg" (personal config files are still ".pydistutils.cfg" or "pydistutils.cfg", though). --- dist.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/dist.py b/dist.py index ea4ec2e7..0b8a99e1 100644 --- a/dist.py +++ b/dist.py @@ -265,20 +265,23 @@ class Distribution: files = [] check_environ() - if os.name=='posix': - sys_dir = os.path.dirname(sys.modules['distutils'].__file__) + # Where to look for the system-wide Distutils config file + sys_dir = os.path.dirname(sys.modules['distutils'].__file__) + + # Look for the system config file + sys_file = os.path.join(sys_dir, "distutils.cfg") + if os.path.isfile(sys_file): + files.append(sys_file) + + # What to call the per-user config file + if os.name == 'posix': user_filename = ".pydistutils.cfg" else: - sys_dir = sysconfig.PREFIX user_filename = "pydistutils.cfg" - sys_file = os.path.join(sys_dir, "pydistutils.cfg") - if os.path.isfile(sys_file): - files.append(sys_file) - + # And look for the user config file if os.environ.has_key('HOME'): - user_file = os.path.join(os.environ.get('HOME'), - user_filename) + user_file = os.path.join(os.environ.get('HOME'), user_filename) if os.path.isfile(user_file): files.append(user_file) -- cgit v1.2.1 From daf86a83ea964ce51f67cff37b365f4129e93134 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 7 Jun 2000 03:00:06 +0000 Subject: Patch from Rene Liebscher: this adds "--help-foo" options to list the values that "--foo" can take for various commands: eg. what formats for "sdist" and "bdist", what compilers for "build_ext" and "build_clib". I have *not* reviewed this patch; I'm checking it in as-is because it also fixes a paper-bag-over-head bug in bdist.py, and because I won't have time to review it properly for several days: so someone else can test it for me, instead! --- archive_util.py | 10 +++++----- ccompiler.py | 18 +++++++++++++++--- command/bdist.py | 37 ++++++++++++++++++++++++++----------- command/build.py | 5 +++++ command/build_clib.py | 6 +++++- command/build_ext.py | 5 +++++ command/sdist.py | 19 +++++++++++++++++-- dist.py | 32 +++++++++++++++++++++++++++++--- 8 files changed, 107 insertions(+), 25 deletions(-) diff --git a/archive_util.py b/archive_util.py index 3159c284..27aa8c0b 100644 --- a/archive_util.py +++ b/archive_util.py @@ -110,11 +110,11 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): ARCHIVE_FORMATS = { - 'gztar': (make_tarball, [('compress', 'gzip')]), - 'bztar': (make_tarball, [('compress', 'bzip2')]), - 'ztar': (make_tarball, [('compress', 'compress')]), - 'tar': (make_tarball, [('compress', None)]), - 'zip': (make_zipfile, []) + 'gztar': (make_tarball, [('compress', 'gzip')],"gzipped tar-file"), + 'bztar': (make_tarball, [('compress', 'bzip2')],"bzip2-ed tar-file"), + 'ztar': (make_tarball, [('compress', 'compress')],"compressed tar-file"), + 'tar': (make_tarball, [('compress', None)],"uncompressed tar-file"), + 'zip': (make_zipfile, [],"zip-file") } def check_archive_formats (formats): diff --git a/ccompiler.py b/ccompiler.py index 834d543e..b146f890 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -726,10 +726,22 @@ default_compiler = { 'posix': 'unix', # Map compiler types to (module_name, class_name) pairs -- ie. where to # find the code that implements an interface to this compiler. (The module # is assumed to be in the 'distutils' package.) -compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler'), - 'msvc': ('msvccompiler', 'MSVCCompiler'), +compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler',"standard UNIX-style compiler"), + 'msvc': ('msvccompiler', 'MSVCCompiler',"Microsoft Visual C++"), + 'cygwin': ('cygwinccompiler', 'CygwinCCompiler',"Cygwin-Gnu-Win32-C-Compiler"), + 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler',"MinGW32-C-Compiler (or cygwin in this mode)"), } +# prints all possible arguments to --compiler +def show_compilers(): + from distutils.fancy_getopt import FancyGetopt + list_of_compilers=[] + for compiler in compiler_class.keys(): + list_of_compilers.append(("compiler="+compiler,None,compiler_class[compiler][2])) + list_of_compilers.sort() + pretty_printer=FancyGetopt(list_of_compilers) + pretty_printer.print_help("List of available compilers:") + def new_compiler (plat=None, compiler=None, @@ -755,7 +767,7 @@ def new_compiler (plat=None, if compiler is None: compiler = default_compiler[plat] - (module_name, class_name) = compiler_class[compiler] + (module_name, class_name,long_description) = compiler_class[compiler] except KeyError: msg = "don't know how to compile C/C++ code on platform '%s'" % plat if compiler is not None: diff --git a/command/bdist.py b/command/bdist.py index 3cd1eb03..66ef1133 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -21,8 +21,7 @@ class bdist (Command): user_options = [('bdist-base=', 'b', "temporary directory for creating built distributions"), ('formats=', None, - "formats for distribution " + - "(gztar, bztar, zip, rpm, ... )"), + "formats for distribution"), ] # The following commands do not take a format option from bdist @@ -33,12 +32,28 @@ class bdist (Command): default_format = { 'posix': 'gztar', 'nt': 'zip', } - format_command = { 'gztar': 'bdist_dumb', - 'bztar': 'bdist_dumb', - 'ztar': 'bdist_dumb', - 'tar': 'bdist_dumb', - 'rpm': 'bdist_rpm', - 'zip': 'bdist_dumb', } + format_command = { 'gztar': ('bdist_dumb',"gzipped tar-file"), + 'bztar': ('bdist_dumb',"bzip2-ed tar-file"), + 'ztar': ('bdist_dumb',"compressed tar-file"), + 'tar': ('bdist_dumb',"tar-file"), + 'rpm': ('bdist_rpm',"rpm distribution"), + 'zip': ('bdist_dumb',"zip-file"), + } + + # prints all possible arguments to --format + def show_formats(): + from distutils.fancy_getopt import FancyGetopt + list_of_formats=[] + for format in bdist.format_command.keys(): + list_of_formats.append(("formats="+format,None,bdist.format_command[format][1])) + list_of_formats.sort() + pretty_printer=FancyGetopt(list_of_formats) + pretty_printer.print_help("List of available distribution formats:") + + help_options = [ + ('help-formats', None, + "lists available distribution formats",show_formats), + ] def initialize_options (self): @@ -74,14 +89,14 @@ class bdist (Command): for format in self.formats: try: - cmd_name = self.format_command[self.format] + cmd_name = self.format_command[format][0] except KeyError: raise DistutilsOptionError, \ - "invalid format '%s'" % self.format + "invalid format '%s'" % format sub_cmd = self.reinitialize_command(cmd_name) if cmd_name not in self.no_format_option: - sub_cmd.format = self.format + sub_cmd.format = format self.run_command (cmd_name) # run() diff --git a/command/build.py b/command/build.py index b0894b83..c064f839 100644 --- a/command/build.py +++ b/command/build.py @@ -9,6 +9,7 @@ __revision__ = "$Id$" import sys, os from distutils.core import Command from distutils.util import get_platform +from distutils.ccompiler import show_compilers class build (Command): @@ -35,6 +36,10 @@ class build (Command): ('force', 'f', "forcibly build everything (ignore file timestamps)"), ] + help_options = [ + ('help-compiler', None, + "lists available compilers",show_compilers), + ] def initialize_options (self): self.build_base = 'build' diff --git a/command/build_clib.py b/command/build_clib.py index dba9a40a..72df372f 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -23,7 +23,7 @@ import os, string from types import * from distutils.core import Command from distutils.errors import * -from distutils.ccompiler import new_compiler +from distutils.ccompiler import new_compiler,show_compilers class build_clib (Command): @@ -42,6 +42,10 @@ class build_clib (Command): ('compiler=', 'c', "specify the compiler type"), ] + help_options = [ + ('help-compiler', None, + "lists available compilers",show_compilers), + ] def initialize_options (self): self.build_clib = None diff --git a/command/build_ext.py b/command/build_ext.py index f487a68c..53a265cc 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -14,6 +14,7 @@ from distutils.core import Command from distutils.errors import * from distutils.dep_util import newer_group from distutils.extension import Extension +from distutils.ccompiler import show_compilers # An extension name is just a dot-separated list of Python NAMEs (ie. # the same as a fully-qualified module name). @@ -72,6 +73,10 @@ class build_ext (Command): ('compiler=', 'c', "specify the compiler type"), ] + help_options = [ + ('help-compiler', None, + "lists available compilers",show_compilers), + ] def initialize_options (self): diff --git a/command/sdist.py b/command/sdist.py index af88eba0..221a4d99 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -13,7 +13,7 @@ from glob import glob from distutils.core import Command from distutils.util import newer, create_tree, remove_tree, convert_path, \ write_file -from distutils.archive_util import check_archive_formats +from distutils.archive_util import check_archive_formats,ARCHIVE_FORMATS from distutils.text_file import TextFile from distutils.errors import DistutilsExecError, DistutilsOptionError @@ -35,11 +35,26 @@ class sdist (Command): ('force-manifest', 'f', "forcibly regenerate the manifest and carry on as usual"), ('formats=', None, - "formats for source distribution (tar, ztar, gztar, bztar, or zip)"), + "formats for source distribution"), ('keep-tree', 'k', "keep the distribution tree around after creating " + "archive file(s)"), ] + # prints all possible arguments to --formats + def show_formats(): + from distutils.fancy_getopt import FancyGetopt + list_of_formats=[] + for format in ARCHIVE_FORMATS.keys(): + list_of_formats.append(("formats="+format,None,ARCHIVE_FORMATS[format][2])) + list_of_formats.sort() + pretty_printer=FancyGetopt(list_of_formats) + pretty_printer.print_help("List of available distribution formats:") + + help_options = [ + ('help-formats', None, + "lists available distribution formats",show_formats), + ] + negative_opts = {'use-defaults': 'no-defaults'} default_format = { 'posix': 'gztar', diff --git a/dist.py b/dist.py index 0b8a99e1..88bd94a4 100644 --- a/dist.py +++ b/dist.py @@ -437,16 +437,38 @@ class Distribution: negative_opt = copy (negative_opt) negative_opt.update (cmd_class.negative_opt) + # Check for help_options in command class + # They have a different format (tuple of four) so we need to preprocess them here + help_options = [] + if hasattr(cmd_class,"help_options") and type (cmd_class.help_options) is ListType: + help_options = map(lambda x:(x[0],x[1],x[2]),cmd_class.help_options) + # All commands support the global options too, just by adding # in 'global_options'. parser.set_option_table (self.global_options + - cmd_class.user_options) + cmd_class.user_options + help_options) parser.set_negative_aliases (negative_opt) (args, opts) = parser.getopt (args[1:]) if hasattr(opts, 'help') and opts.help: self._show_help(parser, display_options=0, commands=[cmd_class]) return + if hasattr(cmd_class,"help_options") and type (cmd_class.help_options) is ListType: + help_option_found=0 + for help_option in cmd_class.help_options: + if hasattr(opts, parser.get_attr_name(help_option[0])): + help_option_found=1 + #print "showing help for option %s of command %s" % (help_option[0],cmd_class) + if callable(help_option[3]): + help_option[3]() + else: + raise DistutilsClassError, \ + ("command class %s must provide " + + "a callable object for help_option '%s'") % \ + (cmd_class,help_option[0]) + if help_option_found: + return + # Put the options from the command-line into their official # holding pen, the 'command_options' dictionary. opt_dict = self.get_option_dict(command) @@ -496,7 +518,11 @@ class Distribution: klass = command else: klass = self.get_command_class (command) - parser.set_option_table (klass.user_options) + if hasattr(klass,"help_options") and type (klass.help_options) is ListType: + parser.set_option_table (klass.user_options+ + map(lambda x:(x[0],x[1],x[2]),klass.help_options)) + else: + parser.set_option_table (klass.user_options) parser.print_help ("Options for '%s' command:" % klass.__name__) print @@ -504,7 +530,7 @@ class Distribution: return # _show_help () - + def handle_display_options (self, option_order): """If there were any non-global "display-only" options -- cgit v1.2.1 From e2136df03285e1476e460aab0d646aa929b69651 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:02:36 +0000 Subject: Added 'debug_print()' method (driven by DEBUG global from distutils.core). --- cmd.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmd.py b/cmd.py index 9f63f838..c9b5624f 100644 --- a/cmd.py +++ b/cmd.py @@ -172,6 +172,15 @@ class Command: if self.verbose >= level: print msg + def debug_print (self, msg): + """Print 'msg' to stdout if the global DEBUG (taken from the + DISTUTILS_DEBUG environment variable) flag is true. + """ + from distutils.core import DEBUG + if DEBUG: + print msg + + # -- Option validation methods ------------------------------------- # (these are very handy in writing the 'finalize_options()' method) -- cgit v1.2.1 From 33a33497961e25ab9467ff6de472a4c8fb2e6230 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:08:14 +0000 Subject: Made all debug output go through the 'debug_print()' method instead of directly printing to stdout. This was a bit more work than it sounds like it should have been: * turned 'select_pattern()' and 'exclude_pattern()' from functions into methods, so they can refer to 'self' to access the method * commented out the *other* 'exclude_pattern()' method, which appears to be vestigial code that was never cleaned up when the 'exclude_pattern()' function was created * changed the one use of the old 'exclude_pattern()' method to use the new 'exclude_pattern()' (same behaviour, slightly different args) * some code and docstring reformatting * and, of course, changed all the debugging prints to 'debug_print()' calls Added/tweaked some regular ('self.announce()') output for better runtime feedback. --- command/sdist.py | 165 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 90 insertions(+), 75 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 221a4d99..b06c5156 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -272,21 +272,22 @@ class sdist (Command): # search_dir () - def exclude_pattern (self, pattern): - """Remove filenames from 'self.files' that match 'pattern'.""" - print "exclude_pattern: pattern=%s" % pattern - pattern_re = translate_pattern (pattern) - for i in range (len (self.files)-1, -1, -1): - if pattern_re.match (self.files[i]): - print "removing %s" % self.files[i] - del self.files[i] +# def exclude_pattern (self, pattern): +# """Remove filenames from 'self.files' that match 'pattern'.""" +# self.debug_print("exclude_pattern: pattern=%s" % pattern) +# pattern_re = translate_pattern (pattern) +# for i in range (len (self.files)-1, -1, -1): +# if pattern_re.match (self.files[i]): +# self.debug_print("removing %s" % self.files[i]) +# del self.files[i] def recursive_exclude_pattern (self, dir, pattern=None): """Remove filenames from 'self.files' that are under 'dir' and whose basenames match 'pattern'.""" - print "recursive_exclude_pattern: dir=%s, pattern=%s" % (dir, pattern) + self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" % + (dir, pattern)) if pattern is None: pattern_re = None else: @@ -296,7 +297,7 @@ class sdist (Command): (cur_dir, cur_base) = os.path.split (self.files[i]) if (cur_dir == dir and (pattern_re is None or pattern_re.match (cur_base))): - print "removing %s" % self.files[i] + self.debug_print("removing %s" % self.files[i]) del self.files[i] @@ -307,6 +308,7 @@ class sdist (Command): and add the resulting filenames to 'self.files'.""" assert self.files is not None and type (self.files) is ListType + self.announce("reading manifest template '%s'" % self.template) template = TextFile (self.template, strip_comments=1, @@ -374,27 +376,28 @@ class sdist (Command): # digging stuff up out of 'words'. if action == 'include': - print "include", string.join(pattern_list) + self.debug_print("include " + string.join(pattern_list)) for pattern in pattern_list: - files = select_pattern (all_files, pattern, anchor=1) + files = self.select_pattern (all_files, pattern, anchor=1) if not files: - template.warn ("no files found matching '%s'" % pattern) + template.warn ("no files found matching '%s'" % + pattern) else: self.files.extend (files) elif action == 'exclude': - print "exclude", string.join(pattern_list) + self.debug_print("exclude " + string.join(pattern_list)) for pattern in pattern_list: - num = exclude_pattern (self.files, pattern, anchor=1) + num = self.exclude_pattern (self.files, pattern, anchor=1) if num == 0: template.warn ( "no previously-included files found matching '%s'"% pattern) elif action == 'global-include': - print "global-include", string.join(pattern_list) + self.debug_print("global-include " + string.join(pattern_list)) for pattern in pattern_list: - files = select_pattern (all_files, pattern, anchor=0) + files = self.select_pattern (all_files, pattern, anchor=0) if not files: template.warn (("no files found matching '%s' " + "anywhere in distribution") % @@ -403,9 +406,9 @@ class sdist (Command): self.files.extend (files) elif action == 'global-exclude': - print "global-exclude", string.join(pattern_list) + self.debug_print("global-exclude " + string.join(pattern_list)) for pattern in pattern_list: - num = exclude_pattern (self.files, pattern, anchor=0) + num = self.exclude_pattern (self.files, pattern, anchor=0) if num == 0: template.warn \ (("no previously-included files matching '%s' " + @@ -413,9 +416,11 @@ class sdist (Command): pattern) elif action == 'recursive-include': - print "recursive-include", dir, string.join(pattern_list) + self.debug_print("recursive-include %s %s" % + (dir, string.join(pattern_list))) for pattern in pattern_list: - files = select_pattern (all_files, pattern, prefix=dir) + files = self.select_pattern ( + all_files, pattern, prefix=dir) if not files: template.warn (("no files found matching '%s' " + "under directory '%s'") % @@ -424,9 +429,11 @@ class sdist (Command): self.files.extend (files) elif action == 'recursive-exclude': - print "recursive-exclude", dir, string.join(pattern_list) + self.debug_print("recursive-exclude %s %s" % + (dir, string.join(pattern_list))) for pattern in pattern_list: - num = exclude_pattern (self.files, pattern, prefix=dir) + num = self.exclude_pattern( + self.files, pattern, prefix=dir) if num == 0: template.warn \ (("no previously-included files matching '%s' " + @@ -434,8 +441,9 @@ class sdist (Command): (pattern, dir)) elif action == 'graft': - print "graft", dir_pattern - files = select_pattern (all_files, None, prefix=dir_pattern) + self.debug_print("graft " + dir_pattern) + files = self.select_pattern( + all_files, None, prefix=dir_pattern) if not files: template.warn ("no directories found matching '%s'" % dir_pattern) @@ -443,8 +451,9 @@ class sdist (Command): self.files.extend (files) elif action == 'prune': - print "prune", dir_pattern - num = exclude_pattern (self.files, None, prefix=dir_pattern) + self.debug_print("prune " + dir_pattern) + num = self.exclude_pattern( + self.files, None, prefix=dir_pattern) if num == 0: template.warn \ (("no previously-included directories found " + @@ -458,14 +467,63 @@ class sdist (Command): # Prune away the build and source distribution directories build = self.get_finalized_command ('build') - exclude_pattern (self.files, None, prefix=build.build_base) + self.exclude_pattern (self.files, None, prefix=build.build_base) base_dir = self.distribution.get_fullname() - exclude_pattern (self.files, None, prefix=base_dir) + self.exclude_pattern (self.files, None, prefix=base_dir) # read_template () + def select_pattern (self, files, pattern, anchor=1, prefix=None): + """Select strings (presumably filenames) from 'files' that match + 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not + quite the same as implemented by the 'fnmatch' module: '*' and '?' + match non-special characters, where "special" is platform-dependent: + slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on + Mac OS. + + If 'anchor' is true (the default), then the pattern match is more + stringent: "*.py" will match "foo.py" but not "foo/bar.py". If + 'anchor' is false, both of these will match. + + If 'prefix' is supplied, then only filenames starting with 'prefix' + (itself a pattern) and ending with 'pattern', with anything in between + them, will match. 'anchor' is ignored in this case. + + Return the list of matching strings, possibly empty. + """ + matches = [] + pattern_re = translate_pattern (pattern, anchor, prefix) + self.debug_print("select_pattern: applying regex r'%s'" % + pattern_re.pattern) + for name in files: + if pattern_re.search (name): + matches.append (name) + self.debug_print(" adding " + name) + + return matches + + # select_pattern () + + + def exclude_pattern (self, files, pattern, anchor=1, prefix=None): + """Remove strings (presumably filenames) from 'files' that match + 'pattern'. 'pattern', 'anchor', 'and 'prefix' are the same + as for 'select_pattern()', above. The list 'files' is modified + in place. + """ + pattern_re = translate_pattern (pattern, anchor, prefix) + self.debug_print("exclude_pattern: applying regex r'%s'" % + pattern_re.pattern) + for i in range (len(files)-1, -1, -1): + if pattern_re.search (files[i]): + self.debug_print(" removing " + files[i]) + del files[i] + + # exclude_pattern () + + def write_manifest (self): """Write the file list in 'self.files' (presumably as filled in by 'find_defaults()' and 'read_template()') to the manifest file @@ -473,7 +531,7 @@ class sdist (Command): self.execute(write_file, (self.manifest, self.files), - "writing manifest file") + "writing manifest file '%s'" % self.manifest) # write_manifest () @@ -483,6 +541,7 @@ class sdist (Command): it to fill in 'self.files', the list of files to include in the source distribution.""" + self.announce("reading manifest file '%s'" % self.manifest) manifest = open (self.manifest) while 1: line = manifest.readline () @@ -495,7 +554,6 @@ class sdist (Command): # read_manifest () - def make_release_tree (self, base_dir, files): # Create all the directories under 'base_dir' necessary to @@ -533,7 +591,7 @@ class sdist (Command): # Remove any files that match "base_dir" from the fileset -- we # don't want to go distributing the distribution inside itself! - self.exclude_pattern (base_dir + "*") + self.exclude_pattern (self.files, base_dir + "*") self.make_release_tree (base_dir, self.files) archive_files = [] # remember names of files we create @@ -583,49 +641,6 @@ def findall (dir = os.curdir): return list -def select_pattern (files, pattern, anchor=1, prefix=None): - """Select strings (presumably filenames) from 'files' that match - 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not - quite the same as implemented by the 'fnmatch' module: '*' and '?' - match non-special characters, where "special" is platform-dependent: - slash on Unix, colon, slash, and backslash on DOS/Windows, and colon - on Mac OS. - - If 'anchor' is true (the default), then the pattern match is more - stringent: "*.py" will match "foo.py" but not "foo/bar.py". If - 'anchor' is false, both of these will match. - - If 'prefix' is supplied, then only filenames starting with 'prefix' - (itself a pattern) and ending with 'pattern', with anything in - between them, will match. 'anchor' is ignored in this case. - - Return the list of matching strings, possibly empty.""" - - matches = [] - pattern_re = translate_pattern (pattern, anchor, prefix) - print "select_pattern: applying re %s" % pattern_re.pattern - for name in files: - if pattern_re.search (name): - matches.append (name) - print " adding", name - - return matches - -# select_pattern () - - -def exclude_pattern (files, pattern, anchor=1, prefix=None): - - pattern_re = translate_pattern (pattern, anchor, prefix) - print "exclude_pattern: applying re %s" % pattern_re.pattern - for i in range (len(files)-1, -1, -1): - if pattern_re.search (files[i]): - print " removing", files[i] - del files[i] - -# exclude_pattern () - - def glob_to_re (pattern): """Translate a shell-like glob pattern to a regular expression; return a string containing the regex. Differs from -- cgit v1.2.1 From 0a3e5710675c4013d56a4f72ecccdc230282ec00 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:14:18 +0000 Subject: Cosmetic tweaks to imports, the 'show_formats()' function, and the 'help_options' list; also added an editorial comment. --- command/sdist.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index b06c5156..eabf5c79 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,9 +11,9 @@ import fnmatch from types import * from glob import glob from distutils.core import Command -from distutils.util import newer, create_tree, remove_tree, convert_path, \ - write_file -from distutils.archive_util import check_archive_formats,ARCHIVE_FORMATS +from distutils.util import \ + convert_path, create_tree, remove_tree, newer, write_file, \ + check_archive_formats, ARCHIVE_FORMATS from distutils.text_file import TextFile from distutils.errors import DistutilsExecError, DistutilsOptionError @@ -40,19 +40,27 @@ class sdist (Command): "keep the distribution tree around after creating " + "archive file(s)"), ] - # prints all possible arguments to --formats - def show_formats(): + + + # XXX ugh: this has to precede the 'help_options' list, because + # it is mentioned there -- also, this is not a method, even though + # it's defined in a class: double-ugh! + def show_formats (): + """Print all possible values for the 'formats' option -- used by + the "--help-formats" command-line option. + """ from distutils.fancy_getopt import FancyGetopt - list_of_formats=[] + formats=[] for format in ARCHIVE_FORMATS.keys(): - list_of_formats.append(("formats="+format,None,ARCHIVE_FORMATS[format][2])) - list_of_formats.sort() - pretty_printer=FancyGetopt(list_of_formats) - pretty_printer.print_help("List of available distribution formats:") + formats.append(("formats="+format,None,ARCHIVE_FORMATS[format][2])) + formats.sort() + pretty_printer = FancyGetopt(formats) + pretty_printer.print_help( + "List of available source distribution formats:") help_options = [ ('help-formats', None, - "lists available distribution formats",show_formats), + "lists available distribution formats", show_formats), ] negative_opts = {'use-defaults': 'no-defaults'} -- cgit v1.2.1 From 3acc171c28a904839b263f8a6cbb617c9894d184 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:24:01 +0000 Subject: Docstring reformatting binge. --- command/sdist.py | 51 +++++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index eabf5c79..cd7f258b 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -68,8 +68,6 @@ class sdist (Command): default_format = { 'posix': 'gztar', 'nt': 'zip' } - exclude_re = re.compile (r'\s*!\s*(\S+)') # for manifest lines - def initialize_options (self): # 'template' and 'manifest' are, respectively, the names of @@ -165,13 +163,11 @@ class sdist (Command): def get_file_list (self): """Figure out the list of files to include in the source - distribution, and put it in 'self.files'. This might - involve reading the manifest template (and writing the - manifest), or just reading the manifest, or just using - the default file set -- it all depends on the user's - options and the state of the filesystem.""" - - + distribution, and put it in 'self.files'. This might involve + reading the manifest template (and writing the manifest), or just + reading the manifest, or just using the default file set -- it all + depends on the user's options and the state of the filesystem. + """ template_exists = os.path.isfile (self.template) if template_exists: template_newer = newer (self.template, self.manifest) @@ -261,10 +257,9 @@ class sdist (Command): def search_dir (self, dir, pattern=None): """Recursively find files under 'dir' matching 'pattern' (a string - containing a Unix-style glob pattern). If 'pattern' is None, - find all files under 'dir'. Return the list of found - filenames.""" - + containing a Unix-style glob pattern). If 'pattern' is None, find + all files under 'dir'. Return the list of found filenames. + """ allfiles = findall (dir) if pattern is None: return allfiles @@ -291,9 +286,9 @@ class sdist (Command): def recursive_exclude_pattern (self, dir, pattern=None): - """Remove filenames from 'self.files' that are under 'dir' - and whose basenames match 'pattern'.""" - + """Remove filenames from 'self.files' that are under 'dir' and + whose basenames match 'pattern'. + """ self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" % (dir, pattern)) if pattern is None: @@ -311,10 +306,10 @@ class sdist (Command): def read_template (self): """Read and parse the manifest template file named by - 'self.template' (usually "MANIFEST.in"). Process all file - specifications (include and exclude) in the manifest template - and add the resulting filenames to 'self.files'.""" - + 'self.template' (usually "MANIFEST.in"). Process all file + specifications (include and exclude) in the manifest template and + add the resulting filenames to 'self.files'. + """ assert self.files is not None and type (self.files) is ListType self.announce("reading manifest template '%s'" % self.template) @@ -533,10 +528,10 @@ class sdist (Command): def write_manifest (self): - """Write the file list in 'self.files' (presumably as filled in - by 'find_defaults()' and 'read_template()') to the manifest file - named by 'self.manifest'.""" - + """Write the file list in 'self.files' (presumably as filled in by + 'find_defaults()' and 'read_template()') to the manifest file named + by 'self.manifest'. + """ self.execute(write_file, (self.manifest, self.files), "writing manifest file '%s'" % self.manifest) @@ -545,10 +540,10 @@ class sdist (Command): def read_manifest (self): - """Read the manifest file (named by 'self.manifest') and use - it to fill in 'self.files', the list of files to include - in the source distribution.""" - + """Read the manifest file (named by 'self.manifest') and use it to + fill in 'self.files', the list of files to include in the source + distribution. + """ self.announce("reading manifest file '%s'" % self.manifest) manifest = open (self.manifest) while 1: -- cgit v1.2.1 From 74f4d48df9b4e3afe675b6fe446c6d4ad1dee325 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:35:33 +0000 Subject: Fixed so we print more than just the first line of help for options with a short form and text that wraps onto multiple lines. --- fancy_getopt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index a593354c..6adfc819 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -369,9 +369,6 @@ class FancyGetopt: else: lines.append (" --%-*s " % (max_opt, long)) - for l in text[1:]: - lines.append (big_indent + l) - # Case 2: we have a short option, so we have to include it # just after the long option else: @@ -382,6 +379,9 @@ class FancyGetopt: else: lines.append (" --%-*s" % opt_names) + for l in text[1:]: + lines.append (big_indent + l) + # for self.option_table return lines -- cgit v1.2.1 From 691fce64def6c4aca873465234a5039c6a6c8480 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:46:45 +0000 Subject: Docstring addition binge. --- command/sdist.py | 59 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index cd7f258b..2a4d346f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -31,7 +31,8 @@ class sdist (Command): "include the default file set in the manifest " "[default; disable with --no-defaults]"), ('manifest-only', 'o', - "just regenerate the manifest and then stop"), + "just regenerate the manifest and then stop " + "(implies --force-manifest)"), ('force-manifest', 'f', "forcibly regenerate the manifest and carry on as usual"), ('formats=', None, @@ -133,7 +134,11 @@ class sdist (Command): def check_metadata (self): - + """Ensure that all required elements of meta-data (name, version, + URL, (author and author_email) or (maintainer and + maintainer_email)) are supplied by the Distribution object; warn if + any are missing. + """ metadata = self.distribution.metadata missing = [] @@ -215,7 +220,16 @@ class sdist (Command): def find_defaults (self): - + """Add all the default files to self.files: + - README or README.txt + - setup.py + - test/test*.py + - all pure Python modules mentioned in setup script + - all C sources listed as part of extensions or C libraries + in the setup script (doesn't catch C headers!) + Warns if (README or README.txt) or setup.py are missing; everything + else is optional. + """ standards = [('README', 'README.txt'), 'setup.py'] for fn in standards: if type (fn) is TupleType: @@ -308,7 +322,8 @@ class sdist (Command): """Read and parse the manifest template file named by 'self.template' (usually "MANIFEST.in"). Process all file specifications (include and exclude) in the manifest template and - add the resulting filenames to 'self.files'. + update 'self.files' accordingly (filenames may be added to + or removed from 'self.files' based on the manifest template). """ assert self.files is not None and type (self.files) is ListType self.announce("reading manifest template '%s'" % self.template) @@ -558,7 +573,14 @@ class sdist (Command): def make_release_tree (self, base_dir, files): - + """Create the directory tree that will become the source + distribution archive. All directories implied by the filenames in + 'files' are created under 'base_dir', and then we hard link or copy + (if hard linking is unavailable) those files into place. + Essentially, this duplicates the developer's source tree, but in a + directory named after the distribution, containing only the files + to be distributed. + """ # Create all the directories under 'base_dir' necessary to # put 'files' there. create_tree (base_dir, files, @@ -587,7 +609,13 @@ class sdist (Command): def make_distribution (self): - + """Create the source distribution(s). First, we create the release + tree with 'make_release_tree()'; then, we create all required + archive files (according to 'self.formats') from the release tree. + Finally, we clean up by blowing away the release tree (unless + 'self.keep_tree' is true). The list of archive files created is + stored so it can be retrieved later by 'get_archive_files()'. + """ # Don't warn about missing meta-data here -- should be (and is!) # done elsewhere. base_dir = self.distribution.get_fullname() @@ -620,9 +648,9 @@ class sdist (Command): # Utility functions def findall (dir = os.curdir): - """Find all files under 'dir' and return the list of full - filenames (relative to 'dir').""" - + """Find all files under 'dir' and return the list of full filenames + (relative to 'dir'). + """ list = [] stack = [dir] pop = stack.pop @@ -645,10 +673,11 @@ def findall (dir = os.curdir): def glob_to_re (pattern): - """Translate a shell-like glob pattern to a regular expression; - return a string containing the regex. Differs from - 'fnmatch.translate()' in that '*' does not match "special - characters" (which are platform-specific).""" + """Translate a shell-like glob pattern to a regular expression; return + a string containing the regex. Differs from 'fnmatch.translate()' in + that '*' does not match "special characters" (which are + platform-specific). + """ pattern_re = fnmatch.translate (pattern) # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which @@ -666,8 +695,8 @@ def glob_to_re (pattern): def translate_pattern (pattern, anchor=1, prefix=None): """Translate a shell-like wildcard pattern to a compiled regular - expression. Return the compiled regex.""" - + expression. Return the compiled regex. + """ if pattern: pattern_re = glob_to_re (pattern) else: -- cgit v1.2.1 From 99ddd15c2fca235eaf905472d648564831458a89 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:52:52 +0000 Subject: Renamed 'find_defaults()' to 'add_defaults()'. Deleted old, commented-out 'exclude_pattern()' method. --- command/sdist.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 2a4d346f..26241353 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -189,7 +189,7 @@ class sdist (Command): # Add default file set to 'files' if self.use_defaults: - self.find_defaults () + self.add_defaults () # Read manifest template if it exists if template_exists: @@ -219,7 +219,7 @@ class sdist (Command): # get_file_list () - def find_defaults (self): + def add_defaults (self): """Add all the default files to self.files: - README or README.txt - setup.py @@ -268,6 +268,8 @@ class sdist (Command): build_clib = self.get_finalized_command ('build_clib') self.files.extend (build_clib.get_source_files ()) + # add_defaults () + def search_dir (self, dir, pattern=None): """Recursively find files under 'dir' matching 'pattern' (a string @@ -289,16 +291,6 @@ class sdist (Command): # search_dir () -# def exclude_pattern (self, pattern): -# """Remove filenames from 'self.files' that match 'pattern'.""" -# self.debug_print("exclude_pattern: pattern=%s" % pattern) -# pattern_re = translate_pattern (pattern) -# for i in range (len (self.files)-1, -1, -1): -# if pattern_re.match (self.files[i]): -# self.debug_print("removing %s" % self.files[i]) -# del self.files[i] - - def recursive_exclude_pattern (self, dir, pattern=None): """Remove filenames from 'self.files' that are under 'dir' and whose basenames match 'pattern'. @@ -544,7 +536,7 @@ class sdist (Command): def write_manifest (self): """Write the file list in 'self.files' (presumably as filled in by - 'find_defaults()' and 'read_template()') to the manifest file named + 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. """ self.execute(write_file, -- cgit v1.2.1 From 5f4a8d08d7029e5d4e199ddb50ca40743d836c2d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 01:06:02 +0000 Subject: Moved the code that prunes the file list after reading the manifest template into a new method 'prune_file_list()', called from 'get_file_list()' rather than 'read_manifest()' -- this keeps 'read_manifest()' more general. Deleted the redundant call to 'exclude_pattern()' in 'make_distribution()' -- this had the same intention as 'prune_file_list()', but was incomplete (only pruned the release tree, not the build tree) and in the wrong place (the prune wouldn't be reflected in the manifest file). --- command/sdist.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 26241353..5a78cc58 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -195,6 +195,9 @@ class sdist (Command): if template_exists: self.read_template () + # Prune away the build and source distribution directories + self.prune_file_list() + # File list now complete -- sort it so that higher-level files # come first sortable_files = map (os.path.split, self.files) @@ -475,15 +478,21 @@ class sdist (Command): # while loop over lines of template file - # Prune away the build and source distribution directories - build = self.get_finalized_command ('build') - self.exclude_pattern (self.files, None, prefix=build.build_base) + # read_template () + + def prune_file_list (self): + """Prune off branches that might slip into the file list as created + by 'read_template()', but really don't belong there: specifically, + the build tree (typically "build") and the release tree itself + (only an issue if we ran "sdist" previously with --keep-tree, or it + aborted). + """ + build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() + self.exclude_pattern (self.files, None, prefix=build.build_base) self.exclude_pattern (self.files, None, prefix=base_dir) - # read_template () - def select_pattern (self, files, pattern, anchor=1, prefix=None): """Select strings (presumably filenames) from 'files' that match @@ -612,10 +621,6 @@ class sdist (Command): # done elsewhere. base_dir = self.distribution.get_fullname() - # Remove any files that match "base_dir" from the fileset -- we - # don't want to go distributing the distribution inside itself! - self.exclude_pattern (self.files, base_dir + "*") - self.make_release_tree (base_dir, self.files) archive_files = [] # remember names of files we create for fmt in self.formats: -- cgit v1.2.1 From 1e78c7432ebfb62f570e7c380f0011ac4eb37e58 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 01:22:48 +0000 Subject: Include setup.cfg in the list of default files to distribute. --- command/sdist.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index 5a78cc58..48e9793e 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -233,6 +233,10 @@ class sdist (Command): Warns if (README or README.txt) or setup.py are missing; everything else is optional. """ + + # XXX name of setup script and config file should be taken + # programmatically from the Distribution object (except + # it doesn't have that capability... yet!) standards = [('README', 'README.txt'), 'setup.py'] for fn in standards: if type (fn) is TupleType: @@ -253,7 +257,7 @@ class sdist (Command): else: self.warn ("standard file '%s' not found" % fn) - optional = ['test/test*.py'] + optional = ['test/test*.py', 'setup.cfg'] for pattern in optional: files = filter (os.path.isfile, glob (pattern)) if files: -- cgit v1.2.1 From 4ea6b39b83cf4405bad1110e81ad7cd240fbecfe Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 14:21:23 +0000 Subject: Harry Henry Gebel: fix '_format_changelog()' so it doesn't bomb if passed None. --- command/bdist_rpm.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 0959ad8f..7f58a1da 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -436,6 +436,8 @@ class bdist_rpm (Command): def _format_changelog(self, changelog): """Format the changelog correctly and convert it to a list of strings """ + if not changelog: + return changelog new_changelog = [] for line in string.split(string.strip(changelog), '\n'): line = string.strip(line) -- cgit v1.2.1 From 524777aabcda3e1fafe2ce35eed3df0ac2be07ef Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 01:34:45 +0000 Subject: Fixed install directory for header files on Unix. --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 7176fab8..21d971b8 100644 --- a/command/install.py +++ b/command/install.py @@ -18,7 +18,7 @@ INSTALL_SCHEMES = { 'unix_prefix': { 'purelib': '$base/lib/python$py_version_short/site-packages', 'platlib': '$platbase/lib/python$py_version_short/site-packages', - 'headers': '$base/include/python/$py_version_short/$dist_name', + 'headers': '$base/include/python$py_version_short/$dist_name', 'scripts': '$base/bin', 'data' : '$base/share', }, -- cgit v1.2.1 From 1f24f7dc91a4145dafa4e7678bd50f47822730b5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 01:58:14 +0000 Subject: Bastian Kleineidam: added 'remove_tree()' function. Needed so that 'remove_tree()' can cooperate with 'mkpath()' in the maintenance of the PATH_CREATED cache: specifically, if a directory is created with 'mkpath()', later removed with 'remove_tree()', and 'mkpath()' is again requested to create it, then it would erroneously think the directory already existed, because it was in the PATH_CREATED cache. The patch (slightly tweaked by me) fixes that. --- dir_util.py | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/dir_util.py b/dir_util.py index 194183ae..e5b24fe4 100644 --- a/dir_util.py +++ b/dir_util.py @@ -180,23 +180,38 @@ def copy_tree (src, dst, # copy_tree () +# Helper for remove_tree() +def _build_cmdtuple(path, cmdtuples): + for f in os.listdir(path): + real_f = os.path.join(path,f) + if os.path.isdir(real_f) and not os.path.islink(real_f): + _build_cmdtuple(real_f, cmdtuples) + else: + cmdtuples.append((os.remove, real_f)) + cmdtuples.append((os.rmdir, path)) + def remove_tree (directory, verbose=0, dry_run=0): """Recursively remove an entire directory tree. Any errors are ignored (apart from being reported to stdout if 'verbose' is true).""" - from shutil import rmtree - + global PATH_CREATED if verbose: print "removing '%s' (and everything under it)" % directory if dry_run: return - try: - rmtree(directory,1) - except (IOError, OSError), exc: - if verbose: - if exc.filename: - print "error removing %s: %s (%s)" % \ + cmdtuples = [] + _build_cmdtuple(directory, cmdtuples) + for cmd in cmdtuples: + try: + apply(cmd[0], (cmd[1],)) + # remove dir from cache if it's already there + if PATH_CREATED.has_key(cmd[1]): + del PATH_CREATED[cmd[1]] + except (IOError, OSError), exc: + if verbose: + if exc.filename: + print "error removing %s: %s (%s)" % \ (directory, exc.strerror, exc.filename) - else: - print "error removing %s: %s" % (directory, exc.strerror) + else: + print "error removing %s: %s" % (directory, exc.strerror) -- cgit v1.2.1 From c5c7d04269747c746f02bfaec075f2710e7cd309 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 02:16:46 +0000 Subject: Added 'grok_environment_error()' function to deal with the various forms that IOError and OSError can take (taken from core.py). --- util.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/util.py b/util.py index 57546384..279f2467 100644 --- a/util.py +++ b/util.py @@ -154,3 +154,23 @@ def subst_vars (str, local_vars): # subst_vars () +def grok_environment_error (exc, prefix="error: "): + """Generate a useful error message from an EnvironmentError (IOError or + OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and + does what it can to deal with exception objects that don't have a + filename (which happens when the error is due to a two-file operation, + such as 'rename()' or 'link()'. Returns the error message as a string + prefixed with 'prefix'. + """ + # check for Python 1.5.2-style {IO,OS}Error exception objects + if hasattr (exc, 'filename') and hasattr (exc, 'strerror'): + if exc.filename: + error = prefix + "%s: %s" % (exc.filename, exc.strerror) + else: + # two-argument functions in posix module don't + # include the filename in the exception object! + error = prefix + "%s" % exc.strerror + else: + error = prefix + str(exc[-1]) + + return error -- cgit v1.2.1 From f0ceb6e0a9eda6011c72bf4b0e3c7ed52eddd271 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 02:17:45 +0000 Subject: Changed to use the new 'grok_environment_error()' function instead of muddling through IOError and OSError exception objects right here. --- core.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/core.py b/core.py index 8bcf2a87..35afa84b 100644 --- a/core.py +++ b/core.py @@ -112,16 +112,7 @@ def setup (**attrs): except KeyboardInterrupt: raise SystemExit, "interrupted" except (IOError, os.error), exc: - # check for Python 1.5.2-style {IO,OS}Error exception objects - if hasattr (exc, 'filename') and hasattr (exc, 'strerror'): - if exc.filename: - error = "error: %s: %s" % (exc.filename, exc.strerror) - else: - # two-argument functions in posix module don't - # include the filename in the exception object! - error = "error: %s" % exc.strerror - else: - error = "error: " + str(exc[-1]) + error = grok_environment_error(exc) if DEBUG: sys.stderr.write(error + "\n") -- cgit v1.2.1 From 3b27bed6ec2871c31925d0f22bfbb8203dc81a5c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 02:18:19 +0000 Subject: Changed 'remove_tree()' to use the new 'grok_environment_error()' function instead of muddling through IOError and OSError exception objects itself. --- dir_util.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/dir_util.py b/dir_util.py index e5b24fe4..838a870d 100644 --- a/dir_util.py +++ b/dir_util.py @@ -193,9 +193,11 @@ def _build_cmdtuple(path, cmdtuples): def remove_tree (directory, verbose=0, dry_run=0): """Recursively remove an entire directory tree. Any errors are ignored - (apart from being reported to stdout if 'verbose' is true).""" - + (apart from being reported to stdout if 'verbose' is true). + """ + from distutils.util import grok_environment_error global PATH_CREATED + if verbose: print "removing '%s' (and everything under it)" % directory if dry_run: @@ -210,8 +212,5 @@ def remove_tree (directory, verbose=0, dry_run=0): del PATH_CREATED[cmd[1]] except (IOError, OSError), exc: if verbose: - if exc.filename: - print "error removing %s: %s (%s)" % \ - (directory, exc.strerror, exc.filename) - else: - print "error removing %s: %s" % (directory, exc.strerror) + print grok_environment_error( + exc, "error removing %s: " % directory) -- cgit v1.2.1 From dab81b6a1e465fdc70dc1051a6bb4b9440737555 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 02:19:30 +0000 Subject: Renamed PATH_CREATED to _path_created, on the grounds that it's private and mutable, rather than public and constant. --- dir_util.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dir_util.py b/dir_util.py index 838a870d..85f8a18d 100644 --- a/dir_util.py +++ b/dir_util.py @@ -13,7 +13,7 @@ from distutils.errors import DistutilsFileError, DistutilsInternalError # cache for by mkpath() -- in addition to cheapening redundant calls, # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode -PATH_CREATED = {} +_path_created = {} # I don't use os.makedirs because a) it's new to Python 1.5.2, and # b) it blows up if the directory already exists (I want to silently @@ -28,7 +28,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): summary of each mkdir to stdout. Return the list of directories actually created.""" - global PATH_CREATED + global _path_created # Detect a common bug -- name is None if type(name) is not StringType: @@ -44,7 +44,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): created_dirs = [] if os.path.isdir (name) or name == '': return created_dirs - if PATH_CREATED.get (name): + if _path_created.get (name): return created_dirs (head, tail) = os.path.split (name) @@ -64,7 +64,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): for d in tails: #print "head = %s, d = %s: " % (head, d), head = os.path.join (head, d) - if PATH_CREATED.get (head): + if _path_created.get (head): continue if verbose: @@ -78,7 +78,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): raise DistutilsFileError, \ "could not create '%s': %s" % (head, exc[-1]) - PATH_CREATED[head] = 1 + _path_created[head] = 1 return created_dirs # mkpath () @@ -196,7 +196,7 @@ def remove_tree (directory, verbose=0, dry_run=0): (apart from being reported to stdout if 'verbose' is true). """ from distutils.util import grok_environment_error - global PATH_CREATED + global _path_created if verbose: print "removing '%s' (and everything under it)" % directory @@ -208,8 +208,8 @@ def remove_tree (directory, verbose=0, dry_run=0): try: apply(cmd[0], (cmd[1],)) # remove dir from cache if it's already there - if PATH_CREATED.has_key(cmd[1]): - del PATH_CREATED[cmd[1]] + if _path_created.has_key(cmd[1]): + del _path_created[cmd[1]] except (IOError, OSError), exc: if verbose: print grok_environment_error( -- cgit v1.2.1 From 0e20e9c1fe3d9a35d483d251ef45a9675a7b36d0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 23:04:31 +0000 Subject: Pulled the MSVC++-specific hackery out to a new method, 'prelink_hook()', and added (empty) 'precompile_hook()' for symmetry. One can envision a much more elaborate hook mechanism, but this looks like it'll do for now. --- command/build_ext.py | 99 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 36 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 53a265cc..3d6d17c8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -160,7 +160,7 @@ class build_ext (Command): # 'self.extensions', as supplied by setup.py, is a list of # Extension instances. See the documentation for Extension (in - # distutils.core) for details. + # distutils.extension) for details. # # For backwards compatibility with Distutils 0.8.2 and earlier, we # also allow the 'extensions' list to be a list of tuples: @@ -395,6 +395,11 @@ class build_ext (Command): if os.environ.has_key('CFLAGS'): extra_args.extend(string.split(os.environ['CFLAGS'])) + # Run any platform/compiler-specific hooks needed before + # compiling (currently none, but any hypothetical subclasses + # might find it useful to override this). + self.precompile_hook() + objects = self.compiler.compile (sources, output_dir=self.build_temp, #macros=macros, @@ -409,41 +414,9 @@ class build_ext (Command): objects.extend (ext.extra_objects) extra_args = ext.extra_link_args - # XXX this is a kludge! Knowledge of specific compilers or - # platforms really doesn't belong here; in an ideal world, the - # CCompiler interface would provide access to everything in a - # compiler/linker system needs to build Python extensions, and - # we would just do everything nicely and cleanly through that - # interface. However, this is a not an ideal world and the - # CCompiler interface doesn't handle absolutely everything. - # Thus, kludges like this slip in occasionally. (This is no - # excuse for committing more platform- and compiler-specific - # kludges; they are to be avoided if possible!) - if self.compiler.compiler_type == 'msvc': - def_file = ext.export_symbol_file - if def_file is None: - source_dir = os.path.dirname (sources[0]) - ext_base = (string.split (ext.name, '.'))[-1] - def_file = os.path.join (source_dir, "%s.def" % ext_base) - if not os.path.exists (def_file): - def_file = None - - if def_file is not None: - extra_args.append ('/DEF:' + def_file) - else: - modname = string.split (ext.name, '.')[-1] - extra_args.append('/export:init%s'%modname) - - # The MSVC linker generates unneeded .lib and .exp files, - # which cannot be suppressed by any linker switches. So - # make sure they are generated in the temporary build - # directory. - implib_file = os.path.join ( - self.build_temp, - self.get_ext_libname (ext.name)) - extra_args.append ('/IMPLIB:' + implib_file) - self.mkpath (os.path.dirname (implib_file)) - # if MSVC + # Run any platform/compiler-specific hooks needed between + # compiling and linking (currently needed only on Windows). + self.prelink_hook() self.compiler.link_shared_object ( objects, ext_filename, @@ -456,6 +429,55 @@ class build_ext (Command): # build_extensions () + # -- Hooks --------------------------------------------------------- + + def precompile_hook (self): + pass + + def prelink_hook (self): + + # XXX this is a kludge! Knowledge of specific compilers or + # platforms really doesn't belong here; in an ideal world, the + # CCompiler interface would provide access to everything in a + # compiler/linker system needs to build Python extensions, and + # we would just do everything nicely and cleanly through that + # interface. However, this is a not an ideal world and the + # CCompiler interface doesn't handle absolutely everything. + # Thus, kludges like this slip in occasionally. (This is no + # excuse for committing more platform- and compiler-specific + # kludges; they are to be avoided if possible!) + if self.compiler.compiler_type == 'msvc': + def_file = ext.export_symbol_file + if def_file is None: + source_dir = os.path.dirname (sources[0]) + ext_base = (string.split (ext.name, '.'))[-1] + def_file = os.path.join (source_dir, "%s.def" % ext_base) + if not os.path.exists (def_file): + def_file = None + + if def_file is not None: + extra_args.append ('/DEF:' + def_file) + else: + modname = string.split (ext.name, '.')[-1] + extra_args.append('/export:init%s'%modname) + + # The MSVC linker generates unneeded .lib and .exp files, + # which cannot be suppressed by any linker switches. So + # make sure they are generated in the temporary build + # directory. + implib_file = os.path.join ( + self.build_temp, + self.get_ext_libname (ext.name)) + extra_args.append ('/IMPLIB:' + implib_file) + self.mkpath (os.path.dirname (implib_file)) + # if MSVC + + # prelink_hook () + + + # -- Name generators ----------------------------------------------- + # (extension names, filenames, whatever) + def get_ext_fullname (self, ext_name): if self.package is None: return ext_name @@ -463,6 +485,11 @@ class build_ext (Command): return self.package + '.' + ext_name def get_ext_filename (self, ext_name): + """Convert the name of an extension (eg. "foo.bar") into the name + of the file from which it will be loaded (eg. "foo/bar.so", or + "foo\bar.pyd"). + """ + from distutils import sysconfig ext_path = string.split (ext_name, '.') # extensions in debug_mode are named 'module_d.pyd' under windows -- cgit v1.2.1 From 895819ab90dd0a4851f356f2153b68f4e2888b9d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 18 Jun 2000 15:45:55 +0000 Subject: 'get_platform()' now just returns 'sys.platform' on all platforms. --- util.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/util.py b/util.py index 279f2467..74df8aa6 100644 --- a/util.py +++ b/util.py @@ -44,18 +44,10 @@ def extend (list, new_list): def get_platform (): """Return a string (suitable for tacking onto directory names) that - identifies the current platform. Under Unix, identifies both the OS - and hardware architecture, e.g. "linux-i586", "solaris-sparc", - "irix-mips". For Windows and Mac OS, just returns 'sys.platform' -- - i.e. "???" or "???".""" - - if os.name == 'posix': - (OS, _, rel, _, arch) = os.uname() - return "%s%c-%s" % (string.lower (OS), rel[0], string.lower (arch)) - else: - return sys.platform - -# get_platform() + identifies the current platform. Currently, this is just + 'sys.platform'. + """ + return sys.platform def convert_path (pathname): -- cgit v1.2.1 From 2591d3a3e93d7f39325d7e0e707f9457c7a1631a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 02:58:46 +0000 Subject: Added 'preprocess()' method to CCompiler interface, and implemented it in UnixCCompiler. Still needs to be implemented in MSVCCompiler (and whatever other compiler classes are lurking out there, waiting to be checked in). --- ccompiler.py | 16 ++++++++++++++++ unixccompiler.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/ccompiler.py b/ccompiler.py index b146f890..53d4fa53 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -414,6 +414,22 @@ class CCompiler: # -- Worker methods ------------------------------------------------ # (must be implemented by subclasses) + def preprocess (self, + source, + output_file=None, + macros=None, + include_dirs=None, + extra_preargs=None, + extra_postargs=None): + """Preprocess a single C/C++ source file, named in 'source'. + Output will be written to file named 'output_file', or stdout if + 'output_file' not supplied. 'macros' is a list of macro + definitions as for 'compile()', which will augment the macros set + with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a + list of directory names that will be added to the default list. + """ + pass + def compile (self, sources, output_dir=None, diff --git a/unixccompiler.py b/unixccompiler.py index 4d38e4d8..47d8ad63 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -21,6 +21,7 @@ import string, re, os from types import * from copy import copy from distutils import sysconfig +from distutils.dep_util import newer from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options from distutils.errors import \ @@ -104,6 +105,37 @@ class UnixCCompiler (CCompiler): # __init__ () + def preprocess (self, + source, + output_file=None, + macros=None, + include_dirs=None, + extra_preargs=None, + extra_postargs=None): + + (_, macros, include_dirs) = \ + self._fix_compile_args (None, macros, include_dirs) + pp_opts = gen_preprocess_options (macros, include_dirs) + cc_args = ['-E'] + pp_opts + if output_file: + cc_args.extend(['-o', output_file]) + if extra_preargs: + cc_args[:0] = extra_preargs + if extra_postargs: + extra_postargs.extend(extra_postargs) + + # We need to preprocess: either we're being forced to, or the + # source file is newer than the target (or the target doesn't + # exist). + if self.force or (output_file and newer(source, output_file)): + if output_file: + self.mkpath(os.path.dirname(output_file)) + try: + self.spawn ([self.cc] + cc_args) + except DistutilsExecError, msg: + raise CompileError, msg + + def compile (self, sources, output_dir=None, -- cgit v1.2.1 From 90843282f35eb7ff25172ea35d52d77e5135d56e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 02:59:14 +0000 Subject: Oops, import 'grok_environment_error()'. --- core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core.py b/core.py index 35afa84b..4c982a07 100644 --- a/core.py +++ b/core.py @@ -13,6 +13,7 @@ __revision__ = "$Id$" import sys, os from types import * from distutils.errors import * +from distutils.util import grok_environment_error # Mainly import these so setup scripts can "from distutils.core import" them. from distutils.dist import Distribution -- cgit v1.2.1 From ebbf910d513041fe74deacf0244b1b79b2bad2f5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:00:50 +0000 Subject: Fleshed out and added a bunch of useful stuff, notably 'check_func()', 'try_cpp()', 'search_cpp()', and 'check_header()'. This is enough that the base config is actually useful for implementing a real config command, specifically one for mxDateTime. --- command/config.py | 208 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 176 insertions(+), 32 deletions(-) diff --git a/command/config.py b/command/config.py index cad75bcb..570f71a5 100644 --- a/command/config.py +++ b/command/config.py @@ -13,7 +13,7 @@ this header file lives". __revision__ = "$Id$" -import os, string +import sys, os, string, re from distutils.core import Command from distutils.errors import DistutilsExecError @@ -40,6 +40,11 @@ class config (Command): "external C libraries to link with"), ('library-dirs=', 'L', "directories to search for external C libraries"), + + ('noisy', None, + "show every action (compile, link, run, ...) taken"), + ('dump-source', None, + "dump generated source files before attempting to compile them"), ] @@ -55,6 +60,14 @@ class config (Command): self.libraries = None self.library_dirs = None + # maximal output for now + self.noisy = 1 + self.dump_source = 1 + + # list of temporary files generated along-the-way that we have + # to clean at some point + self.temp_files = [] + def finalize_options (self): pass @@ -75,7 +88,7 @@ class config (Command): from distutils.ccompiler import CCompiler, new_compiler if not isinstance(self.compiler, CCompiler): self.compiler = new_compiler (compiler=self.compiler, - verbose=self.verbose, # for now + verbose=self.noisy, dry_run=self.dry_run, force=1) if self.include_dirs: @@ -86,25 +99,48 @@ class config (Command): self.compiler.set_library_dirs(self.library_dirs) - def _gen_temp_sourcefile (self, body, lang): + def _gen_temp_sourcefile (self, body, headers, lang): filename = "_configtest" + LANG_EXT[lang] file = open(filename, "w") + if headers: + for header in headers: + file.write("#include <%s>\n" % header) + file.write("\n") file.write(body) + if body[-1] != "\n": + file.write("\n") file.close() return filename - def _compile (self, body, lang): - src = self._gen_temp_sourcefile(body, lang) - (obj,) = self.compiler.compile([src]) + def _preprocess (self, body, headers, lang): + src = self._gen_temp_sourcefile(body, headers, lang) + out = "_configtest.i" + self.temp_files.extend([src, out]) + self.compiler.preprocess(src, out) + return (src, out) + + def _compile (self, body, headers, lang): + src = self._gen_temp_sourcefile(body, headers, lang) + if self.dump_source: + dump_file(src, "compiling '%s':" % src) + (obj,) = self.compiler.object_filenames([src]) + self.temp_files.extend([src, obj]) + self.compiler.compile([src]) return (src, obj) - def _link (self, body, lang): - (src, obj) = self._compile(body, lang) - exe = os.path.splitext(os.path.basename(src))[0] - self.compiler.link_executable([obj], exe) - return (src, obj, exe) + def _link (self, body, headers, libraries, library_dirs, lang): + (src, obj) = self._compile(body, headers, lang) + prog = os.path.splitext(os.path.basename(src))[0] + self.temp_files.append(prog) # XXX should be prog + exe_ext + self.compiler.link_executable([obj], prog, + libraries=libraries, + library_dirs=library_dirs) + return (src, obj, prog) def _clean (self, *filenames): + if not filenames: + filenames = self.temp_files + self.temp_files = [] self.announce("removing: " + string.join(filenames)) for filename in filenames: try: @@ -113,10 +149,6 @@ class config (Command): pass - # XXX no 'try_cpp()' or 'search_cpp()' since the CCompiler interface - # does not provide access to the preprocessor. This is an oversight - # that should be fixed. - # XXX these ignore the dry-run flag: what to do, what to do? even if # you want a dry-run build, you still need some sort of configuration # info. My inclination is to make it up to the real config command to @@ -125,56 +157,168 @@ class config (Command): # return either true or false from all the 'try' methods, neither of # which is correct. - def try_compile (self, body, lang="c"): - """Try to compile a source file that consists of the text in 'body' - (a multi-line string). Return true on success, false - otherwise. + # XXX need access to the header search path and maybe default macros. + + def try_cpp (self, body=None, headers=None, lang="c"): + """Construct a source file from 'body' (a string containing lines + of C/C++ code) and 'headers' (a list of header files to include) + and run it through the preprocessor. Return true if the + preprocessor succeeded, false if there were any errors. + ('body' probably isn't of much use, but what the heck.) """ from distutils.ccompiler import CompileError self._check_compiler() + ok = 1 try: - (src, obj) = self._compile(body, lang) + self._preprocess(body, headers, lang) + except CompileError: + ok = 0 + + self._clean() + return ok + + def search_cpp (self, pattern, body=None, headers=None, lang="c"): + """Construct a source file (just like 'try_cpp()'), run it through + the preprocessor, and return true if any line of the output matches + 'pattern'. 'pattern' should either be a compiled regex object or a + string containing a regex. If both 'body' and 'headers' are None, + preprocesses an empty file -- which can be useful to determine the + symbols the preprocessor and compiler set by default. + """ + + self._check_compiler() + (src, out) = self._preprocess(body, headers, lang) + + if type(pattern) is StringType: + pattern = re.compile(pattern) + + file = open(out) + match = 0 + while 1: + line = file.readline() + if line == '': + break + if pattern.search(pattern): + match = 1 + break + + file.close() + self._clean() + return match + + def try_compile (self, body, headers=None, lang="c"): + """Try to compile a source file built from 'body' and 'headers'. + Return true on success, false otherwise. + """ + from distutils.ccompiler import CompileError + self._check_compiler() + try: + self._compile(body, headers, lang) ok = 1 except CompileError: ok = 0 self.announce(ok and "success!" or "failure.") - self._clean(src, obj) + self._clean() return ok - def try_link (self, body, lang="c"): - """Try to compile and link a source file (to an executable) that - consists of the text in 'body' (a multi-line string). Return true - on success, false otherwise. + def try_link (self, + body, headers=None, + libraries=None, library_dirs=None, + lang="c"): + """Try to compile and link a source file, built from 'body' and + 'headers', to executable form. Return true on success, false + otherwise. """ from distutils.ccompiler import CompileError, LinkError self._check_compiler() try: - (src, obj, exe) = self._link(body, lang) + self._link(body, headers, libraries, library_dirs, lang) ok = 1 except (CompileError, LinkError): ok = 0 self.announce(ok and "success!" or "failure.") - self._clean(src, obj, exe) + self._clean() return ok - def try_run (self, body, lang="c"): - """Try to compile, link to an executable, and run a program that - consists of the text in 'body'. Return true on success, false + def try_run (self, + body, headers=None, + libraries=None, library_dirs=None, + lang="c"): + """Try to compile, link to an executable, and run a program + built from 'body' and 'headers'. Return true on success, false otherwise. """ from distutils.ccompiler import CompileError, LinkError self._check_compiler() try: - (src, obj, exe) = self._link(body, lang) + self._link(body, headers, libraries, library_dirs, lang) self.spawn([exe]) ok = 1 except (CompileError, LinkError, DistutilsExecError): ok = 0 self.announce(ok and "success!" or "failure.") - self._clean(src, obj, exe) + self._clean() return ok + + # -- High-level methods -------------------------------------------- + # (these are the ones that are actually likely to be useful + # when implementing a real-world config command!) + + def check_func (self, func, headers=None, + libraries=None, library_dirs=None, + decl=0, call=0): + + """Determine if function 'func' is available by constructing a + source file that refers to 'func', and compiles and links it. + If everything succeeds, returns true; otherwise returns false. + + The constructed source file starts out by including the header + files listed in 'headers'. If 'decl' is true, it then declares + 'func' (as "int func()"); you probably shouldn't supply 'headers' + and set 'decl' true in the same call, or you might get errors about + a conflicting declarations for 'func'. Finally, the constructed + 'main()' function either references 'func' or (if 'call' is true) + calls it. 'libraries' and 'library_dirs' are used when + linking. + """ + + self._check_compiler() + body = [] + if decl: + body.append("int %s ();" % func) + body.append("int main () {") + if call: + body.append(" %s();" % func) + else: + body.append(" %s;" % func) + body.append("}") + body = string.join(body, "\n") + "\n" + + return self.try_link(body, headers, libraries, library_dirs) + + # check_func () + + def check_header (self, header, lang="c"): + """Determine if the system header file named by 'header_file' + exists and can be found by the preprocessor; return true if so, + false otherwise. + """ + return self.try_cpp(headers=[header]) + + # class config + + +def dump_file (filename, head=None): + if head is None: + print filename + ":" + else: + print head + + file = open(filename) + sys.stdout.write(file.read()) + file.close() -- cgit v1.2.1 From ed71d7548070c87c61c8ac7b2ca5f1f4479d2622 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:09:02 +0000 Subject: Rene Liebscher: when fixing up directories with an alternate root, include 'install_headers'. --- command/install.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 21d971b8..5e8ade8d 100644 --- a/command/install.py +++ b/command/install.py @@ -272,7 +272,8 @@ class install (Command): # If a new root directory was supplied, make all the installation # dirs relative to it. if self.root is not None: - for name in ('lib', 'purelib', 'platlib', 'scripts', 'data'): + for name in ('lib', 'purelib', 'platlib', + 'scripts', 'data', 'headers'): attr = "install_" + name new_val = change_root (self.root, getattr (self, attr)) setattr (self, attr, new_val) -- cgit v1.2.1 From db2316094f167954780793b08eeb1a62dbad543c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:13:51 +0000 Subject: Build the 'outfiles' list so 'get_outputs()' has something to return. (Bug spotted and originally fixed by Rene Liebscher; fix redone by me.) --- command/install_data.py | 10 ++++++---- command/install_headers.py | 10 +++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/command/install_data.py b/command/install_data.py index 5481dabe..f8ed0a75 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -25,7 +25,7 @@ class install_data (Command): def initialize_options (self): self.install_dir = None - self.outfiles = None + self.outfiles = [] self.root = None self.data_files = self.distribution.data_files @@ -40,7 +40,8 @@ class install_data (Command): for f in self.data_files: if type(f) == StringType: # its a simple file, so copy it - self.copy_file(f, self.install_dir) + out = self.copy_file(f, self.install_dir) + self.outfiles.append(out) else: # its a tuple with path to install to and a list of files dir = f[0] @@ -50,10 +51,11 @@ class install_data (Command): dir = change_root(self.root, dir) self.mkpath(dir) for data in f[1]: - self.copy_file(data, dir) + out = self.copy_file(data, dir) + self.outfiles.append(out) def get_inputs (self): return self.data_files or [] def get_outputs (self): - return self.outfiles or [] + return self.outfiles diff --git a/command/install_headers.py b/command/install_headers.py index 33edb945..afcc887a 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -7,6 +7,7 @@ files to the Python include directory.""" __revision__ = "$Id$" +import os from distutils.core import Command @@ -21,6 +22,7 @@ class install_headers (Command): def initialize_options (self): self.install_dir = None + self.outfiles = [] def finalize_options (self): self.set_undefined_options('install', @@ -33,8 +35,14 @@ class install_headers (Command): self.mkpath(self.install_dir) for header in headers: - self.copy_file(header, self.install_dir) + out = self.copy_file(header, self.install_dir) + self.outfiles.append(out) + def get_inputs (self): + return self.distribution.headers or [] + + def get_outputs (self): + return self.outfiles # run() # class install_headers -- cgit v1.2.1 From f888626d57eb7caa92c9b814e81802e2e2fd24c4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:14:27 +0000 Subject: Delete spurious comment. --- command/install_headers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/command/install_headers.py b/command/install_headers.py index afcc887a..991985b4 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -43,6 +43,5 @@ class install_headers (Command): def get_outputs (self): return self.outfiles - # run() # class install_headers -- cgit v1.2.1 From 88259b3d075b18c55429fd5c2413c2e08cbb8a56 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:29:57 +0000 Subject: Fix inspired by Rene Liebscher: if setup script is newer than the manifest, regenerate the manifest. --- command/sdist.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 48e9793e..ded8ec22 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -173,14 +173,37 @@ class sdist (Command): reading the manifest, or just using the default file set -- it all depends on the user's options and the state of the filesystem. """ + + # If we have a manifest template, see if it's newer than the + # manifest; if so, we'll regenerate the manifest. template_exists = os.path.isfile (self.template) if template_exists: template_newer = newer (self.template, self.manifest) + # The contents of the manifest file almost certainly depend on the + # setup script as well as the manifest template -- so if the setup + # script is newer than the manifest, we'll regenerate the manifest + # from the template. (Well, not quite: if we already have a + # manifest, but there's no template -- which will happen if the + # developer elects to generate a manifest some other way -- then we + # can't regenerate the manifest, so we don't.) + setup_newer = newer(sys.argv[0], self.manifest) + + # cases: + # 1) no manifest, template exists: generate manifest + # (covered by 2a: no manifest == template newer) + # 2) manifest & template exist: + # 2a) template or setup script newer than manifest: + # regenerate manifest + # 2b) manifest newer than both: + # do nothing (unless --force or --manifest-only) + # 3) manifest exists, no template: + # do nothing (unless --force or --manifest-only) + # 4) no manifest, no template: generate w/ warning ("defaults only") + # Regenerate the manifest if necessary (or if explicitly told to) - if ((template_exists and template_newer) or - self.force_manifest or - self.manifest_only): + if ((template_exists and (template_newer or setup_newer)) or + self.force_manifest or self.manifest_only): if not template_exists: self.warn (("manifest template '%s' does not exist " + -- cgit v1.2.1 From bed42c8495831545d7e190a1221bb59cacf441f3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:33:03 +0000 Subject: Implementation of the CCompiler class for Cygwin and Mingw32, ie. the two major ports of GCC to Windows. Contributed by Rene Liebscher, and quite untested by me. Apparently requires tweaking Python's installed config.h and adding a libpython.a to build extensions. --- cygwinccompiler.py | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 cygwinccompiler.py diff --git a/cygwinccompiler.py b/cygwinccompiler.py new file mode 100644 index 00000000..cc2ed5db --- /dev/null +++ b/cygwinccompiler.py @@ -0,0 +1,181 @@ +"""distutils.cygwinccompiler + +Contains the CygwinCCompiler class, a subclass of UnixCCompiler that handles +the Gnu Win32 C compiler. +It also contains the Mingw32CCompiler class which handles the mingw32 compiler +(same as cygwin in no-cygwin mode.) + +""" + +# created 2000/05/05, Rene Liebscher + +__revision__ = "$Id$" + +import os,sys,string,tempfile +from distutils import sysconfig +from distutils.unixccompiler import UnixCCompiler + +# Because these compilers aren't configured in Python's config.h file by default +# we should at least warn the user if he used this unmodified version. +def check_if_config_h_is_gcc_ready(): + """ checks, if the gcc-compiler is mentioned in config.h + if it is not, compiling probably doesn't work """ + from distutils import sysconfig + import string,sys + try: + # It would probably better to read single lines to search. + # But we do this only once, and it is fast enough + f=open(sysconfig.get_config_h_filename()) + s=f.read() + f.close() + try: + string.index(s,"__GNUC__") # is somewhere a #ifdef __GNUC__ or something similar + except: + sys.stderr.write ("warning: Python's config.h doesn't seem to support your compiler.\n") + except: # unspecific error => ignore + pass + + +# This is called when the module is imported, so we make this check only once +check_if_config_h_is_gcc_ready() + + +# XXX Things not currently handled: +# * see UnixCCompiler + +class CygwinCCompiler (UnixCCompiler): + + compiler_type = 'cygwin' + + def __init__ (self, + verbose=0, + dry_run=0, + force=0): + + UnixCCompiler.__init__ (self, verbose, dry_run, force) + + # our compiler uses other names + self.cc='gcc' + self.ld_shared='dllwrap' + self.ldflags_shared=[] + + # some variables to manage the differences between cygwin and mingw32 + self.dllwrap_options=["--target=i386-cygwin32"] + # specification of entry point is not necessary + + self.dll_additional_libraries=[ + # cygwin shouldn't need msvcrt, but without the dll's will crash + # perhaps something about initialization (Python uses it, too) + # mingw32 needs it in all cases + "msvcrt" + ] + + # __init__ () + + def link_shared_object (self, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None): + + if libraries==None: + libraries=[] + + python_library=["python"+str(sys.hexversion>>24)+str((sys.hexversion>>16)&0xff)] + libraries=libraries+python_library+self.dll_additional_libraries + + # if you don't need the def-file afterwards, it is + # better to use for it tempfile.mktemp() as its name + # (unix-style compilers don't like backslashes in filenames) + win_dll_def_file=string.replace(tempfile.mktemp(),"\\","/") + #win_dll_def_file=output_filename[:-len(self.shared_lib_extension)]+".def" + #win_dll_exp_file=output_filename[:-len(self.shared_lib_extension)]+".exp" + #win_dll_lib_file=output_filename[:-len(self.shared_lib_extension)]+".a" + + # Make .def file + # (It would probably better to check if we really need this, but for this we had to + # insert some unchanged parts of UnixCCompiler, and this is not what I want.) + f=open(win_dll_def_file,"w") + f.write("EXPORTS\n") # intro + # always export a function "init"+module_name + if not debug: + f.write("init"+os.path.basename(output_filename)[:-len(self.shared_lib_extension)]+"\n") + else: # in debug mode outfile_name is something like XXXXX_d.pyd + f.write("init"+os.path.basename(output_filename)[:-(len(self.shared_lib_extension)+2)]+"\n") + # if there are more symbols to export + # insert code here to write them in f + if export_symbols!=None: + for sym in export_symbols: + f.write(sym+"\n") + f.close() + + if extra_preargs==None: + extra_preargs=[] + + extra_preargs=extra_preargs+[ + #"--verbose", + #"--output-exp",win_dll_exp_file, + #"--output-lib",win_dll_lib_file, + "--def",win_dll_def_file + ]+ self.dllwrap_options + + # who wants symbols and a many times greater output file + # should explicitely switch the debug mode on + # otherwise we let dllwrap strip the outputfile + # (On my machine unstripped_file=stripped_file+254KB + # 10KB < stripped_file < ??100KB ) + if not debug: + extra_preargs=extra_preargs+["-s"] + + try: + UnixCCompiler.link_shared_object(self, + objects, + output_filename, + output_dir, + libraries, + library_dirs, + runtime_library_dirs, + None, # export_symbols, we do this with our def-file + debug, + extra_preargs, + extra_postargs) + finally: + # we don't need the def-file anymore + os.remove(win_dll_def_file) + + # link_shared_object () + +# class CygwinCCompiler + +# the same as cygwin plus some additional parameters +class Mingw32CCompiler (CygwinCCompiler): + + compiler_type = 'mingw32' + + def __init__ (self, + verbose=0, + dry_run=0, + force=0): + + CygwinCCompiler.__init__ (self, verbose, dry_run, force) + + self.ccflags = self.ccflags + ["-mno-cygwin"] + self.dllwrap_options=[ + # mingw32 doesn't really need 'target' + # and cygwin too (it seems, it is enough + # to specify a different entry point) + #"--target=i386-mingw32", + "--entry","_DllMain@12" + ] + # no additional libraries need + # (only msvcrt, which is already added by CygwinCCompiler) + + # __init__ () + +# class Mingw32CCompiler -- cgit v1.2.1 From ae6b225478590a6a5d5462307ff1bb565980619e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 23 Jun 2000 01:42:40 +0000 Subject: Bastian Kleineidam: 'copy_file()' now returns the output filename, rather than a boolean indicating whether it did the copy. --- file_util.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/file_util.py b/file_util.py index a73db42b..2d0148f3 100644 --- a/file_util.py +++ b/file_util.py @@ -96,9 +96,8 @@ def copy_file (src, dst, on other systems, uses '_copy_file_contents()' to copy file contents. - Return true if the file was copied (or would have been copied), - false otherwise (ie. 'update' was true and the destination is - up-to-date).""" + Return the name of the destination file, whether it was actually + copied or not.""" # XXX if the destination file already exists, we clobber it if # copying, but blow up if linking. Hmmm. And I don't know what @@ -123,7 +122,7 @@ def copy_file (src, dst, if update and not newer (src, dst): if verbose: print "not copying %s (output up-to-date)" % src - return 0 + return dst try: action = _copy_action[link] @@ -137,7 +136,7 @@ def copy_file (src, dst, print "%s %s -> %s" % (action, src, dst) if dry_run: - return 1 + return dst # On a Mac, use the native file copy routine if os.name == 'mac': @@ -171,7 +170,7 @@ def copy_file (src, dst, if preserve_mode: os.chmod (dst, S_IMODE (st[ST_MODE])) - return 1 + return dst # copy_file () -- cgit v1.2.1 From 0b55a059cd131e263c36fb412c5556f9e32d2437 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 00:18:24 +0000 Subject: Revised docstring so 'sources' isn't necessarily all C/C++ files (to accomodate SWIG interface files, resource files, etc.). --- extension.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extension.py b/extension.py index 9d2a6fa1..f0f68b93 100644 --- a/extension.py +++ b/extension.py @@ -31,9 +31,11 @@ class Extension: the full name of the extension, including any packages -- ie. *not* a filename or pathname, but Python dotted name sources : [string] - list of C/C++ source filenames, relative to the distribution - root (where the setup script lives), in Unix form - (slash-separated) for portability + list of source filenames, relative to the distribution root + (where the setup script lives), in Unix form (slash-separated) + for portability. Source files may be C, C++, SWIG (.i), + platform-specific resource files, or whatever else is recognized + by the "build_ext" command as source for a Python extension. include_dirs : [string] list of directories to search for C/C++ header files (in Unix form for portability) -- cgit v1.2.1 From 1f3bafb167859eb32d5a0fcec63c92bc72e4e65a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 00:19:35 +0000 Subject: Experimental, completely untested SWIG support. --- command/build_ext.py | 79 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 3d6d17c8..f3ff1571 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -367,11 +367,12 @@ class build_ext (Command): else: self.announce ("building '%s' extension" % ext.name) - # First step: compile the source code to object files. This - # drops the object files in the current directory, regardless - # of where the source is (may be a bad thing, but that's how a - # Makefile.pre.in-based system does it, so at least there's a - # precedent!) + # First, scan the sources for SWIG definition files (.i), run + # SWIG on 'em to create .c files, and modify the sources list + # accordingly. + sources = self.swig_sources(sources) + + # Next, compile the source code to object files. # XXX not honouring 'define_macros' or 'undef_macros' -- the # CCompiler API needs to change to accomodate this, and I @@ -429,6 +430,74 @@ class build_ext (Command): # build_extensions () + def swig_sources (self, sources): + + """Walk the list of source files in 'sources', looking for SWIG + interface (.i) files. Run SWIG on all that are found, and + return a modified 'sources' list with SWIG source files replaced + by the generated C (or C++) files. + """ + + new_sources = [] + swig_sources = [] + swig_targets = {} + + # XXX this drops generated C files into the source tree, which + # is fine for developers who want to distribute the generated + # source -- but there should be an option to put SWIG output in + # the temp dir. + + for source in sources: + (base, ext) = os.path.splitext(source) + if ext in self.swig_ext(): + new_sources.append(base + ".c") # umm, what if it's C++? + swig_files.append(source) + swig_targets[source] = new_sources[-1] + else: + new_sources.append(source) + + if not swig_files: + return new_sources + + swig = self.find_swig() + swig_cmd = [swig, "-python", "-dnone", "-ISWIG"] # again, C++?!? + + for source in swig_sources: + self.announce ("swigging %s to %s" % (src, obj)) + self.spawn(swig_cmd + ["-o", swig_targets[source], source]) + + return new_sources + + # swig_sources () + + def find_swig (self): + """Return the name of the SWIG executable. On Unix, this is + just "swig" -- it should be in the PATH. Tries a bit harder on + Windows. + """ + + if os.name == "posix": + return "swig" + elif os.name == "nt": + + # Look for SWIG in its standard installation directory on + # Windows (or so I presume!). If we find it there, great; + # if not, act like Unix and assume it's in the PATH. + for vers in ("1.3", "1.2", "1.1"): + fn = os.path.join("c:\\swig%s" % vers, "swig.exe") + if os.path.isfile (fn): + return fn + else: + return "swig.exe" + + else: + raise DistutilsPlatformError, \ + ("I don't know how to find (much less run) SWIG " + "on platform '%s'") % os.name + + # find_swig () + + # -- Hooks --------------------------------------------------------- def precompile_hook (self): -- cgit v1.2.1 From 9285ec887624587ca83a53445ce64b0bafd34ea9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 00:23:20 +0000 Subject: Stylistic/formatting changes to Rene Liebscher's '--help-xxx' patch. --- archive_util.py | 8 ++++---- ccompiler.py | 30 ++++++++++++++++++++---------- command/bdist.py | 29 +++++++++++++++-------------- command/build.py | 3 ++- command/build_clib.py | 3 ++- command/sdist.py | 4 ++-- dist.py | 37 +++++++++++++++++++++++++++---------- 7 files changed, 72 insertions(+), 42 deletions(-) diff --git a/archive_util.py b/archive_util.py index 27aa8c0b..08a3c831 100644 --- a/archive_util.py +++ b/archive_util.py @@ -110,10 +110,10 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): ARCHIVE_FORMATS = { - 'gztar': (make_tarball, [('compress', 'gzip')],"gzipped tar-file"), - 'bztar': (make_tarball, [('compress', 'bzip2')],"bzip2-ed tar-file"), - 'ztar': (make_tarball, [('compress', 'compress')],"compressed tar-file"), - 'tar': (make_tarball, [('compress', None)],"uncompressed tar-file"), + 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), + 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), + 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"), + 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"), 'zip': (make_zipfile, [],"zip-file") } diff --git a/ccompiler.py b/ccompiler.py index 53d4fa53..5be8c25a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -742,20 +742,30 @@ default_compiler = { 'posix': 'unix', # Map compiler types to (module_name, class_name) pairs -- ie. where to # find the code that implements an interface to this compiler. (The module # is assumed to be in the 'distutils' package.) -compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler',"standard UNIX-style compiler"), - 'msvc': ('msvccompiler', 'MSVCCompiler',"Microsoft Visual C++"), - 'cygwin': ('cygwinccompiler', 'CygwinCCompiler',"Cygwin-Gnu-Win32-C-Compiler"), - 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler',"MinGW32-C-Compiler (or cygwin in this mode)"), +compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', + "standard UNIX-style compiler"), + 'msvc': ('msvccompiler', 'MSVCCompiler', + "Microsoft Visual C++"), + 'cygwin': ('cygwinccompiler', 'CygwinCCompiler', + "Cygwin port of GNU C Compiler for Win32"), + 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler', + "Mingw32 port of GNU C Compiler for Win32"), } -# prints all possible arguments to --compiler def show_compilers(): + """Print list of available compilers (used by the "--help-compiler" + options to "build", "build_ext", "build_clib"). + """ + # XXX this "knows" that the compiler option it's describing is + # "--compiler", which just happens to be the case for the three + # commands that use it. from distutils.fancy_getopt import FancyGetopt - list_of_compilers=[] + compilers = [] for compiler in compiler_class.keys(): - list_of_compilers.append(("compiler="+compiler,None,compiler_class[compiler][2])) - list_of_compilers.sort() - pretty_printer=FancyGetopt(list_of_compilers) + compilers.append(("compiler="+compiler, None, + compiler_class[compiler][2])) + compilers.sort() + pretty_printer = FancyGetopt(compilers) pretty_printer.print_help("List of available compilers:") @@ -783,7 +793,7 @@ def new_compiler (plat=None, if compiler is None: compiler = default_compiler[plat] - (module_name, class_name,long_description) = compiler_class[compiler] + (module_name, class_name, long_description) = compiler_class[compiler] except KeyError: msg = "don't know how to compile C/C++ code on platform '%s'" % plat if compiler is not None: diff --git a/command/bdist.py b/command/bdist.py index 66ef1133..16469936 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -21,7 +21,7 @@ class bdist (Command): user_options = [('bdist-base=', 'b', "temporary directory for creating built distributions"), ('formats=', None, - "formats for distribution"), + "formats for distribution (comma-separated list)"), ] # The following commands do not take a format option from bdist @@ -32,22 +32,24 @@ class bdist (Command): default_format = { 'posix': 'gztar', 'nt': 'zip', } - format_command = { 'gztar': ('bdist_dumb',"gzipped tar-file"), - 'bztar': ('bdist_dumb',"bzip2-ed tar-file"), - 'ztar': ('bdist_dumb',"compressed tar-file"), - 'tar': ('bdist_dumb',"tar-file"), - 'rpm': ('bdist_rpm',"rpm distribution"), - 'zip': ('bdist_dumb',"zip-file"), + format_command = { 'rpm': ('bdist_rpm', "RPM distribution"), + 'gztar': ('bdist_dumb', "gzip'ed tar file"), + 'bztar': ('bdist_dumb', "bzip2'ed tar file"), + 'ztar': ('bdist_dumb', "compressed tar file"), + 'tar': ('bdist_dumb', "tar file"), + 'zip': ('bdist_dumb', "ZIP file"), } - # prints all possible arguments to --format - def show_formats(): + def show_formats (): + """Print list of available formats (arguments to "--format" option). + """ from distutils.fancy_getopt import FancyGetopt - list_of_formats=[] + formats=[] for format in bdist.format_command.keys(): - list_of_formats.append(("formats="+format,None,bdist.format_command[format][1])) - list_of_formats.sort() - pretty_printer=FancyGetopt(list_of_formats) + formats.append(("formats="+format, None, + bdist.format_command[format][1])) + formats.sort() + pretty_printer = FancyGetopt(formats) pretty_printer.print_help("List of available distribution formats:") help_options = [ @@ -87,7 +89,6 @@ class bdist (Command): def run (self): for format in self.formats: - try: cmd_name = self.format_command[format][0] except KeyError: diff --git a/command/build.py b/command/build.py index c064f839..d5513fc7 100644 --- a/command/build.py +++ b/command/build.py @@ -36,9 +36,10 @@ class build (Command): ('force', 'f', "forcibly build everything (ignore file timestamps)"), ] + help_options = [ ('help-compiler', None, - "lists available compilers",show_compilers), + "list available compilers", show_compilers), ] def initialize_options (self): diff --git a/command/build_clib.py b/command/build_clib.py index 72df372f..9a82ac09 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -42,9 +42,10 @@ class build_clib (Command): ('compiler=', 'c', "specify the compiler type"), ] + help_options = [ ('help-compiler', None, - "lists available compilers",show_compilers), + "list available compilers", show_compilers), ] def initialize_options (self): diff --git a/command/sdist.py b/command/sdist.py index ded8ec22..93e53bbd 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -36,7 +36,7 @@ class sdist (Command): ('force-manifest', 'f', "forcibly regenerate the manifest and carry on as usual"), ('formats=', None, - "formats for source distribution"), + "formats for source distribution (comma-separated list)"), ('keep-tree', 'k', "keep the distribution tree around after creating " + "archive file(s)"), @@ -61,7 +61,7 @@ class sdist (Command): help_options = [ ('help-formats', None, - "lists available distribution formats", show_formats), + "list available distribution formats", show_formats), ] negative_opts = {'use-defaults': 'no-defaults'} diff --git a/dist.py b/dist.py index 88bd94a4..2e4951ff 100644 --- a/dist.py +++ b/dist.py @@ -437,11 +437,14 @@ class Distribution: negative_opt = copy (negative_opt) negative_opt.update (cmd_class.negative_opt) - # Check for help_options in command class - # They have a different format (tuple of four) so we need to preprocess them here - help_options = [] - if hasattr(cmd_class,"help_options") and type (cmd_class.help_options) is ListType: - help_options = map(lambda x:(x[0],x[1],x[2]),cmd_class.help_options) + # Check for help_options in command class. They have a different + # format (tuple of four) so we need to preprocess them here. + if (hasattr(cmd_class, 'help_options') and + type (cmd_class.help_options) is ListType): + help_options = fix_help_options(cmd_class.help_options) + else: + help_optiosn = [] + # All commands support the global options too, just by adding # in 'global_options'. @@ -453,12 +456,14 @@ class Distribution: self._show_help(parser, display_options=0, commands=[cmd_class]) return - if hasattr(cmd_class,"help_options") and type (cmd_class.help_options) is ListType: + if (hasattr(cmd_class, 'help_options') and + type (cmd_class.help_options) is ListType): help_option_found=0 for help_option in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option[0])): help_option_found=1 - #print "showing help for option %s of command %s" % (help_option[0],cmd_class) + #print "showing help for option %s of command %s" % \ + # (help_option[0],cmd_class) if callable(help_option[3]): help_option[3]() else: @@ -518,9 +523,10 @@ class Distribution: klass = command else: klass = self.get_command_class (command) - if hasattr(klass,"help_options") and type (klass.help_options) is ListType: - parser.set_option_table (klass.user_options+ - map(lambda x:(x[0],x[1],x[2]),klass.help_options)) + if (hasattr(klass, 'help_options') and + type (klass.help_options) is ListType): + parser.set_option_table (klass.user_options + + fix_help_options(klass.help_options)) else: parser.set_option_table (klass.user_options) parser.print_help ("Options for '%s' command:" % klass.__name__) @@ -890,6 +896,17 @@ class DistributionMetadata: # class DistributionMetadata + +def fix_help_options (options): + """Convert a 4-tuple 'help_options' list as found in various command + classes to the 3-tuple form required by FancyGetopt. + """ + new_options = [] + for help_tuple in options: + new_options.append(help_tuple[0:3]) + return new_options + + if __name__ == "__main__": dist = Distribution () print "ok" -- cgit v1.2.1 From 4fc435d3e805645f360a11cb97bf4d3e93e5af17 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 01:22:41 +0000 Subject: More stylistic tweaks to the generic '--help-xxx' code. --- dist.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/dist.py b/dist.py index 2e4951ff..287137ee 100644 --- a/dist.py +++ b/dist.py @@ -443,13 +443,14 @@ class Distribution: type (cmd_class.help_options) is ListType): help_options = fix_help_options(cmd_class.help_options) else: - help_optiosn = [] + help_options = [] # All commands support the global options too, just by adding # in 'global_options'. parser.set_option_table (self.global_options + - cmd_class.user_options + help_options) + cmd_class.user_options + + help_options) parser.set_negative_aliases (negative_opt) (args, opts) = parser.getopt (args[1:]) if hasattr(opts, 'help') and opts.help: @@ -459,19 +460,21 @@ class Distribution: if (hasattr(cmd_class, 'help_options') and type (cmd_class.help_options) is ListType): help_option_found=0 - for help_option in cmd_class.help_options: - if hasattr(opts, parser.get_attr_name(help_option[0])): + for (help_option, short, desc, func) in cmd_class.help_options: + if hasattr(opts, parser.get_attr_name(help_option)): help_option_found=1 #print "showing help for option %s of command %s" % \ # (help_option[0],cmd_class) - if callable(help_option[3]): - help_option[3]() - else: - raise DistutilsClassError, \ - ("command class %s must provide " + - "a callable object for help_option '%s'") % \ - (cmd_class,help_option[0]) - if help_option_found: + + if callable(func): + func() + else: + raise DistutilsClassError, \ + ("invalid help function %s for help option '%s': " + "must be a callable object (function, etc.)") % \ + (`func`, help_option) + + if help_option_found: return # Put the options from the command-line into their official -- cgit v1.2.1 From 869c587086b76265a98af7c65def177261b593cc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 01:23:37 +0000 Subject: Changed so all the help-generating functions are defined, at module-level, in the module of the command classes that have command-specific help options. This lets us keep the principle of lazily importing the ccompiler module, and also gets away from defining non-methods at class level. --- command/bdist.py | 38 ++++++++++++++++++++------------------ command/build.py | 7 ++++++- command/build_clib.py | 7 ++++++- command/build_ext.py | 10 +++++++--- command/sdist.py | 35 +++++++++++++++++------------------ 5 files changed, 56 insertions(+), 41 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 16469936..47d4cbc9 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -14,6 +14,18 @@ from distutils.errors import * from distutils.util import get_platform +def show_formats (): + """Print list of available formats (arguments to "--format" option). + """ + from distutils.fancy_getopt import FancyGetopt + formats=[] + for format in bdist.format_commands: + formats.append(("formats=" + format, None, + bdist.format_command[format][1])) + pretty_printer = FancyGetopt(formats) + pretty_printer.print_help("List of available distribution formats:") + + class bdist (Command): description = "create a built (binary) distribution" @@ -24,6 +36,11 @@ class bdist (Command): "formats for distribution (comma-separated list)"), ] + help_options = [ + ('help-formats', None, + "lists available distribution formats", show_formats), + ] + # The following commands do not take a format option from bdist no_format_option = ('bdist_rpm',) @@ -38,24 +55,9 @@ class bdist (Command): 'ztar': ('bdist_dumb', "compressed tar file"), 'tar': ('bdist_dumb', "tar file"), 'zip': ('bdist_dumb', "ZIP file"), - } - - def show_formats (): - """Print list of available formats (arguments to "--format" option). - """ - from distutils.fancy_getopt import FancyGetopt - formats=[] - for format in bdist.format_command.keys(): - formats.append(("formats="+format, None, - bdist.format_command[format][1])) - formats.sort() - pretty_printer = FancyGetopt(formats) - pretty_printer.print_help("List of available distribution formats:") - - help_options = [ - ('help-formats', None, - "lists available distribution formats",show_formats), - ] + } + # establish the preferred order + format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', 'zip'] def initialize_options (self): diff --git a/command/build.py b/command/build.py index d5513fc7..1e87f23b 100644 --- a/command/build.py +++ b/command/build.py @@ -9,7 +9,12 @@ __revision__ = "$Id$" import sys, os from distutils.core import Command from distutils.util import get_platform -from distutils.ccompiler import show_compilers + + +def show_compilers (): + from distutils.ccompiler import show_compilers + show_compilers() + class build (Command): diff --git a/command/build_clib.py b/command/build_clib.py index 9a82ac09..7106882d 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -23,7 +23,11 @@ import os, string from types import * from distutils.core import Command from distutils.errors import * -from distutils.ccompiler import new_compiler,show_compilers + + +def show_compilers (): + from distutils.ccompiler import show_compilers + show_compilers() class build_clib (Command): @@ -102,6 +106,7 @@ class build_clib (Command): return # Yech -- this is cut 'n pasted from build_ext.py! + from distutils.ccompiler import new_compiler self.compiler = new_compiler (compiler=self.compiler, verbose=self.verbose, dry_run=self.dry_run, diff --git a/command/build_ext.py b/command/build_ext.py index f3ff1571..6b7ec741 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -14,7 +14,6 @@ from distutils.core import Command from distutils.errors import * from distutils.dep_util import newer_group from distutils.extension import Extension -from distutils.ccompiler import show_compilers # An extension name is just a dot-separated list of Python NAMEs (ie. # the same as a fully-qualified module name). @@ -22,6 +21,11 @@ extension_name_re = re.compile \ (r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$') +def show_compilers (): + from distutils.ccompiler import show_compilers + show_compilers() + + class build_ext (Command): description = "build C/C++ extensions (compile/link to build directory)" @@ -73,12 +77,12 @@ class build_ext (Command): ('compiler=', 'c', "specify the compiler type"), ] + help_options = [ ('help-compiler', None, - "lists available compilers",show_compilers), + "list available compilers", show_compilers), ] - def initialize_options (self): self.extensions = None self.build_lib = None diff --git a/command/sdist.py b/command/sdist.py index 93e53bbd..5627ebb9 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -13,11 +13,27 @@ from glob import glob from distutils.core import Command from distutils.util import \ convert_path, create_tree, remove_tree, newer, write_file, \ - check_archive_formats, ARCHIVE_FORMATS + check_archive_formats from distutils.text_file import TextFile from distutils.errors import DistutilsExecError, DistutilsOptionError +def show_formats (): + """Print all possible values for the 'formats' option (used by + the "--help-formats" command-line option). + """ + from distutils.fancy_getopt import FancyGetopt + from distutils.archive_util import ARCHIVE_FORMATS + formats=[] + for format in ARCHIVE_FORMATS.keys(): + formats.append(("formats=" + format, None, + ARCHIVE_FORMATS[format][2])) + formats.sort() + pretty_printer = FancyGetopt(formats) + pretty_printer.print_help( + "List of available source distribution formats:") + + class sdist (Command): description = "create a source distribution (tarball, zip file, etc.)" @@ -43,22 +59,6 @@ class sdist (Command): ] - # XXX ugh: this has to precede the 'help_options' list, because - # it is mentioned there -- also, this is not a method, even though - # it's defined in a class: double-ugh! - def show_formats (): - """Print all possible values for the 'formats' option -- used by - the "--help-formats" command-line option. - """ - from distutils.fancy_getopt import FancyGetopt - formats=[] - for format in ARCHIVE_FORMATS.keys(): - formats.append(("formats="+format,None,ARCHIVE_FORMATS[format][2])) - formats.sort() - pretty_printer = FancyGetopt(formats) - pretty_printer.print_help( - "List of available source distribution formats:") - help_options = [ ('help-formats', None, "list available distribution formats", show_formats), @@ -69,7 +69,6 @@ class sdist (Command): default_format = { 'posix': 'gztar', 'nt': 'zip' } - def initialize_options (self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. -- cgit v1.2.1 From 614a927fbf22310f55f5ee3f895ce6a138bf6d35 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 02:22:49 +0000 Subject: Changed 'object_filenames()' to raise exception instead of silently carrying on if it sees a filename with unknown extension. --- ccompiler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 5be8c25a..4dd8645b 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -671,7 +671,9 @@ class CCompiler: for src_name in source_filenames: (base, ext) = os.path.splitext (src_name) if ext not in self.src_extensions: - continue + raise UnknownFileError, \ + "unknown file type '%s' (from '%s')" % \ + (ext, src_name) if strip_dir: base = os.path.basename (base) obj_names.append (os.path.join (output_dir, -- cgit v1.2.1 From 2bfcf462aa13cdd68bc582ec69ed80e2d52d6eb5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 17:22:39 +0000 Subject: Changed the default installation directory for data files (used by the "install_data" command to the installation base, which is usually just sys.prefix. (Any setup scripts out there that specify data files will have to set the installation directory, relative to the base, explicitly.) --- command/install.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/command/install.py b/command/install.py index 5e8ade8d..1be49046 100644 --- a/command/install.py +++ b/command/install.py @@ -20,28 +20,28 @@ INSTALL_SCHEMES = { 'platlib': '$platbase/lib/python$py_version_short/site-packages', 'headers': '$base/include/python$py_version_short/$dist_name', 'scripts': '$base/bin', - 'data' : '$base/share', + 'data' : '$base', }, 'unix_home': { 'purelib': '$base/lib/python', 'platlib': '$base/lib/python', 'headers': '$base/include/python/$dist_name', 'scripts': '$base/bin', - 'data' : '$base/share', + 'data' : '$base', }, 'nt': { 'purelib': '$base', 'platlib': '$base', 'headers': '$base\\Include\\$dist_name', 'scripts': '$base\\Scripts', - 'data' : '$base\\Data', + 'data' : '$base', }, 'mac': { 'purelib': '$base:Lib', 'platlib': '$base:Mac:PlugIns', 'headers': '$base:Include:$dist_name', 'scripts': '$base:Scripts', - 'data' : '$base:Data', + 'data' : '$base', } } -- cgit v1.2.1 From 3cac3f4156d58d1a827929273bb02e8645762484 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 17:36:24 +0000 Subject: Print a warning if we install a data file right in install_dir. Tweaked help text. --- command/install_data.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/command/install_data.py b/command/install_data.py index f8ed0a75..716febb5 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -18,7 +18,8 @@ class install_data (Command): user_options = [ ('install-dir=', 'd', - "directory to install the files to"), + "base directory for installating data files " + "(default: installation base dir)"), ('root=', None, "install everything relative to this alternate root directory"), ] @@ -39,11 +40,14 @@ class install_data (Command): self.mkpath(self.install_dir) for f in self.data_files: if type(f) == StringType: - # its a simple file, so copy it + # it's a simple file, so copy it + self.warn("setup script did not provide a directory for " + "'%s' -- installing right in '%s'" % + (f, self.install_dir)) out = self.copy_file(f, self.install_dir) self.outfiles.append(out) else: - # its a tuple with path to install to and a list of files + # it's a tuple with path to install to and a list of files dir = f[0] if not os.path.isabs(dir): dir = os.path.join(self.install_dir, dir) -- cgit v1.2.1 From 0a5cf577d2738162be426bc2a95b04d55bf3cbed Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 18:10:48 +0000 Subject: Docstring reformatting/tweaking binge. Fixed a few comments. --- ccompiler.py | 502 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 253 insertions(+), 249 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 4dd8645b..9aa41adb 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -17,18 +17,17 @@ from distutils.util import move_file, mkpath, newer_pairwise, newer_group class CCompiler: """Abstract base class to define the interface that must be implemented - by real compiler abstraction classes. Might have some use as a - place for shared code, but it's not yet clear what code can be - shared between compiler abstraction models for different platforms. - - The basic idea behind a compiler abstraction class is that each - instance can be used for all the compile/link steps in building - a single project. Thus, attributes common to all of those compile - and link steps -- include directories, macros to define, libraries - to link against, etc. -- are attributes of the compiler instance. - To allow for variability in how individual files are treated, - most (all?) of those attributes may be varied on a per-compilation - or per-link basis.""" + by real compiler classes. Also has some utility methods used by + several compiler classes. + + The basic idea behind a compiler abstraction class is that each + instance can be used for all the compile/link steps in building a + single project. Thus, attributes common to all of those compile and + link steps -- include directories, macros to define, libraries to link + against, etc. -- are attributes of the compiler instance. To allow for + variability in how individual files are treated, most of those + attributes may be varied on a per-compilation or per-link basis. + """ # 'compiler_type' is a class attribute that identifies this class. It # keeps code that wants to know what kind of compiler it's dealing with @@ -46,10 +45,6 @@ class CCompiler: # should be the domain of concrete compiler abstraction classes # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base # class should have methods for the common ones. - # * can't put output files (object files, libraries, whatever) - # into a separate directory from their inputs. Should this be - # handled by an 'output_dir' attribute of the whole object, or a - # parameter to the compile/link_* methods, or both? # * can't completely override the include or library searchg # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". # I'm not sure how widely supported this is even by Unix @@ -129,10 +124,9 @@ class CCompiler: def _check_macro_definitions (self, definitions): """Ensures that every element of 'definitions' is a valid macro - definition, ie. either (name,value) 2-tuple or a (name,) - tuple. Do nothing if all definitions are OK, raise - TypeError otherwise.""" - + definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do + nothing if all definitions are OK, raise TypeError otherwise. + """ for defn in definitions: if not (type (defn) is TupleType and (len (defn) == 1 or @@ -148,12 +142,12 @@ class CCompiler: # -- Bookkeeping methods ------------------------------------------- def define_macro (self, name, value=None): - """Define a preprocessor macro for all compilations driven by - this compiler object. The optional parameter 'value' should be - a string; if it is not supplied, then the macro will be defined - without an explicit value and the exact outcome depends on the - compiler used (XXX true? does ANSI say anything about this?)""" - + """Define a preprocessor macro for all compilations driven by this + compiler object. The optional parameter 'value' should be a + string; if it is not supplied, then the macro will be defined + without an explicit value and the exact outcome depends on the + compiler used (XXX true? does ANSI say anything about this?) + """ # Delete from the list of macro definitions/undefinitions if # already there (so that this one will take precedence). i = self._find_macro (name) @@ -166,13 +160,13 @@ class CCompiler: def undefine_macro (self, name): """Undefine a preprocessor macro for all compilations driven by - this compiler object. If the same macro is defined by - 'define_macro()' and undefined by 'undefine_macro()' the last - call takes precedence (including multiple redefinitions or - undefinitions). If the macro is redefined/undefined on a - per-compilation basis (ie. in the call to 'compile()'), then - that takes precedence.""" - + this compiler object. If the same macro is defined by + 'define_macro()' and undefined by 'undefine_macro()' the last call + takes precedence (including multiple redefinitions or + undefinitions). If the macro is redefined/undefined on a + per-compilation basis (ie. in the call to 'compile()'), then that + takes precedence. + """ # Delete from the list of macro definitions/undefinitions if # already there (so that this one will take precedence). i = self._find_macro (name) @@ -184,86 +178,94 @@ class CCompiler: def add_include_dir (self, dir): - """Add 'dir' to the list of directories that will be searched - for header files. The compiler is instructed to search - directories in the order in which they are supplied by - successive calls to 'add_include_dir()'.""" + """Add 'dir' to the list of directories that will be searched for + header files. The compiler is instructed to search directories in + the order in which they are supplied by successive calls to + 'add_include_dir()'. + """ self.include_dirs.append (dir) def set_include_dirs (self, dirs): - """Set the list of directories that will be searched to 'dirs' - (a list of strings). Overrides any preceding calls to - 'add_include_dir()'; subsequence calls to 'add_include_dir()' - add to the list passed to 'set_include_dirs()'. This does - not affect any list of standard include directories that - the compiler may search by default.""" + """Set the list of directories that will be searched to 'dirs' (a + list of strings). Overrides any preceding calls to + 'add_include_dir()'; subsequence calls to 'add_include_dir()' add + to the list passed to 'set_include_dirs()'. This does not affect + any list of standard include directories that the compiler may + search by default. + """ self.include_dirs = copy (dirs) def add_library (self, libname): - """Add 'libname' to the list of libraries that will be included - in all links driven by this compiler object. Note that - 'libname' should *not* be the name of a file containing a - library, but the name of the library itself: the actual filename - will be inferred by the linker, the compiler, or the compiler - abstraction class (depending on the platform). - - The linker will be instructed to link against libraries in the - order they were supplied to 'add_library()' and/or - 'set_libraries()'. It is perfectly valid to duplicate library - names; the linker will be instructed to link against libraries - as many times as they are mentioned.""" + """Add 'libname' to the list of libraries that will be included in + all links driven by this compiler object. Note that 'libname' + should *not* be the name of a file containing a library, but the + name of the library itself: the actual filename will be inferred by + the linker, the compiler, or the compiler class (depending on the + platform). + + The linker will be instructed to link against libraries in the + order they were supplied to 'add_library()' and/or + 'set_libraries()'. It is perfectly valid to duplicate library + names; the linker will be instructed to link against libraries as + many times as they are mentioned. + """ self.libraries.append (libname) def set_libraries (self, libnames): - """Set the list of libraries to be included in all links driven - by this compiler object to 'libnames' (a list of strings). - This does not affect any standard system libraries that the - linker may include by default.""" - + """Set the list of libraries to be included in all links driven by + this compiler object to 'libnames' (a list of strings). This does + not affect any standard system libraries that the linker may + include by default. + """ self.libraries = copy (libnames) def add_library_dir (self, dir): """Add 'dir' to the list of directories that will be searched for - libraries specified to 'add_library()' and 'set_libraries()'. - The linker will be instructed to search for libraries in the - order they are supplied to 'add_library_dir()' and/or - 'set_library_dirs()'.""" + libraries specified to 'add_library()' and 'set_libraries()'. The + linker will be instructed to search for libraries in the order they + are supplied to 'add_library_dir()' and/or 'set_library_dirs()'. + """ self.library_dirs.append (dir) def set_library_dirs (self, dirs): - """Set the list of library search directories to 'dirs' (a list - of strings). This does not affect any standard library - search path that the linker may search by default.""" + """Set the list of library search directories to 'dirs' (a list of + strings). This does not affect any standard library search path + that the linker may search by default. + """ self.library_dirs = copy (dirs) def add_runtime_library_dir (self, dir): """Add 'dir' to the list of directories that will be searched for - shared libraries at runtime.""" + shared libraries at runtime. + """ self.runtime_library_dirs.append (dir) def set_runtime_library_dirs (self, dirs): - """Set the list of directories to search for shared libraries - at runtime to 'dirs' (a list of strings). This does not affect - any standard search path that the runtime linker may search by - default.""" + """Set the list of directories to search for shared libraries at + runtime to 'dirs' (a list of strings). This does not affect any + standard search path that the runtime linker may search by + default. + """ self.runtime_library_dirs = copy (dirs) def add_link_object (self, object): - """Add 'object' to the list of object files (or analogues, such - as explictly named library files or the output of "resource - compilers") to be included in every link driven by this - compiler object.""" + """Add 'object' to the list of object files (or analogues, such as + explictly named library files or the output of "resource + compilers") to be included in every link driven by this compiler + object. + """ self.objects.append (object) def set_link_objects (self, objects): - """Set the list of object files (or analogues) to be included - in every link to 'objects'. This does not affect any - standard object files that the linker may include by default - (such as system libraries).""" + """Set the list of object files (or analogues) to be included in + every link to 'objects'. This does not affect any standard object + files that the linker may include by default (such as system + libraries). + """ self.objects = copy (objects) @@ -271,15 +273,15 @@ class CCompiler: # (here for the convenience of subclasses) def _fix_compile_args (self, output_dir, macros, include_dirs): - """Typecheck and fix-up some of the arguments to the 'compile()' method, - and return fixed-up values. Specifically: if 'output_dir' is - None, replaces it with 'self.output_dir'; ensures that 'macros' - is a list, and augments it with 'self.macros'; ensures that - 'include_dirs' is a list, and augments it with - 'self.include_dirs'. Guarantees that the returned values are of - the correct type, i.e. for 'output_dir' either string or None, - and for 'macros' and 'include_dirs' either list or None.""" - + """Typecheck and fix-up some of the arguments to the 'compile()' + method, and return fixed-up values. Specifically: if 'output_dir' + is None, replaces it with 'self.output_dir'; ensures that 'macros' + is a list, and augments it with 'self.macros'; ensures that + 'include_dirs' is a list, and augments it with 'self.include_dirs'. + Guarantees that the returned values are of the correct type, + i.e. for 'output_dir' either string or None, and for 'macros' and + 'include_dirs' either list or None. + """ if output_dir is None: output_dir = self.output_dir elif type (output_dir) is not StringType: @@ -307,11 +309,11 @@ class CCompiler: def _prep_compile (self, sources, output_dir): - """Determine the list of object files corresponding to 'sources', and - figure out which ones really need to be recompiled. Return a list - of all object files and a dictionary telling which source files can - be skipped.""" - + """Determine the list of object files corresponding to 'sources', + and figure out which ones really need to be recompiled. Return a + list of all object files and a dictionary telling which source + files can be skipped. + """ # Get the list of expected output (object) files objects = self.object_filenames (sources, output_dir=output_dir) @@ -330,8 +332,8 @@ class CCompiler: skip_source[source] = 1 (n_sources, n_objects) = newer_pairwise (sources, objects) - for source in n_sources: # no really, only rebuild what's out-of-date - skip_source[source] = 0 + for source in n_sources: # no really, only rebuild what's + skip_source[source] = 0 # out-of-date return (objects, skip_source) @@ -339,11 +341,11 @@ class CCompiler: def _fix_object_args (self, objects, output_dir): - """Typecheck and fix up some arguments supplied to various - methods. Specifically: ensure that 'objects' is a list; if - output_dir is None, replace with self.output_dir. Return fixed - versions of 'objects' and 'output_dir'.""" - + """Typecheck and fix up some arguments supplied to various methods. + Specifically: ensure that 'objects' is a list; if output_dir is + None, replace with self.output_dir. Return fixed versions of + 'objects' and 'output_dir'. + """ if type (objects) not in (ListType, TupleType): raise TypeError, \ "'objects' must be a list or tuple of strings" @@ -359,11 +361,11 @@ class CCompiler: def _fix_lib_args (self, libraries, library_dirs, runtime_library_dirs): """Typecheck and fix up some of the arguments supplied to the - 'link_*' methods. Specifically: ensure that all arguments are - lists, and augment them with their permanent versions - (eg. 'self.libraries' augments 'libraries'). Return a tuple - with fixed versions of all arguments.""" - + 'link_*' methods. Specifically: ensure that all arguments are + lists, and augment them with their permanent versions + (eg. 'self.libraries' augments 'libraries'). Return a tuple with + fixed versions of all arguments. + """ if libraries is None: libraries = self.libraries elif type (libraries) in (ListType, TupleType): @@ -396,9 +398,9 @@ class CCompiler: def _need_link (self, objects, output_file): - """Return true if we need to relink the files listed in 'objects' to - recreate 'output_file'.""" - + """Return true if we need to relink the files listed in 'objects' + to recreate 'output_file'. + """ if self.force: return 1 else: @@ -438,44 +440,44 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None): - """Compile one or more C/C++ source files. 'sources' must be - a list of strings, each one the name of a C/C++ source - file. Return a list of object filenames, one per source - filename in 'sources'. Depending on the implementation, - not all source files will necessarily be compiled, but - all corresponding object filenames will be returned. - - If 'output_dir' is given, object files will be put under it, - while retaining their original path component. That is, - "foo/bar.c" normally compiles to "foo/bar.o" (for a Unix - implementation); if 'output_dir' is "build", then it would - compile to "build/foo/bar.o". - - 'macros', if given, must be a list of macro definitions. A - macro definition is either a (name, value) 2-tuple or a (name,) - 1-tuple. The former defines a macro; if the value is None, the - macro is defined without an explicit value. The 1-tuple case - undefines a macro. Later definitions/redefinitions/ - undefinitions take precedence. - - 'include_dirs', if given, must be a list of strings, the - directories to add to the default include file search path for - this compilation only. - - 'debug' is a boolean; if true, the compiler will be instructed - to output debug symbols in (or alongside) the object file(s). - - 'extra_preargs' and 'extra_postargs' are implementation- - dependent. On platforms that have the notion of a command-line - (e.g. Unix, DOS/Windows), they are most likely lists of strings: - extra command-line arguments to prepand/append to the compiler - command line. On other platforms, consult the implementation - class documentation. In any event, they are intended as an - escape hatch for those occasions when the abstract compiler - framework doesn't cut the mustard. - - Raises CompileError on failure.""" - + """Compile one or more C/C++ source files. 'sources' must be a + list of strings, each one the name of a C/C++ source file. Return + a list of object filenames, one per source filename in 'sources'. + Depending on the implementation, not all source files will + necessarily be compiled, but all corresponding object filenames + will be returned. + + If 'output_dir' is given, object files will be put under it, while + retaining their original path component. That is, "foo/bar.c" + normally compiles to "foo/bar.o" (for a Unix implementation); if + 'output_dir' is "build", then it would compile to + "build/foo/bar.o". + + 'macros', if given, must be a list of macro definitions. A macro + definition is either a (name, value) 2-tuple or a (name,) 1-tuple. + The former defines a macro; if the value is None, the macro is + defined without an explicit value. The 1-tuple case undefines a + macro. Later definitions/redefinitions/ undefinitions take + precedence. + + 'include_dirs', if given, must be a list of strings, the + directories to add to the default include file search path for this + compilation only. + + 'debug' is a boolean; if true, the compiler will be instructed to + output debug symbols in (or alongside) the object file(s). + + 'extra_preargs' and 'extra_postargs' are implementation- dependent. + On platforms that have the notion of a command-line (e.g. Unix, + DOS/Windows), they are most likely lists of strings: extra + command-line arguments to prepand/append to the compiler command + line. On other platforms, consult the implementation class + documentation. In any event, they are intended as an escape hatch + for those occasions when the abstract compiler framework doesn't + cut the mustard. + + Raises CompileError on failure. + """ pass @@ -484,25 +486,24 @@ class CCompiler: output_libname, output_dir=None, debug=0): - """Link a bunch of stuff together to create a static library - file. The "bunch of stuff" consists of the list of object - files supplied as 'objects', the extra object files supplied - to 'add_link_object()' and/or 'set_link_objects()', the - libraries supplied to 'add_library()' and/or - 'set_libraries()', and the libraries supplied as 'libraries' - (if any). - - 'output_libname' should be a library name, not a filename; the - filename will be inferred from the library name. 'output_dir' - is the directory where the library file will be put. - - 'debug' is a boolean; if true, debugging information will be - included in the library (note that on most platforms, it is the - compile step where this matters: the 'debug' flag is included - here just for consistency). - - Raises LibError on failure.""" - + """Link a bunch of stuff together to create a static library file. + The "bunch of stuff" consists of the list of object files supplied + as 'objects', the extra object files supplied to + 'add_link_object()' and/or 'set_link_objects()', the libraries + supplied to 'add_library()' and/or 'set_libraries()', and the + libraries supplied as 'libraries' (if any). + + 'output_libname' should be a library name, not a filename; the + filename will be inferred from the library name. 'output_dir' is + the directory where the library file will be put. + + 'debug' is a boolean; if true, debugging information will be + included in the library (note that on most platforms, it is the + compile step where this matters: the 'debug' flag is included here + just for consistency). + + Raises LibError on failure. + """ pass @@ -517,44 +518,44 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None): - """Link a bunch of stuff together to create a shared library - file. Similar semantics to 'create_static_lib()', with the - addition of other libraries to link against and directories to - search for them. Also, of course, the type and name of - the generated file will almost certainly be different, as will - the program used to create it. - - 'libraries' is a list of libraries to link against. These are - library names, not filenames, since they're translated into - filenames in a platform-specific way (eg. "foo" becomes - "libfoo.a" on Unix and "foo.lib" on DOS/Windows). However, they - can include a directory component, which means the linker will - look in that specific directory rather than searching all the - normal locations. - - 'library_dirs', if supplied, should be a list of directories to - search for libraries that were specified as bare library names - (ie. no directory component). These are on top of the system - default and those supplied to 'add_library_dir()' and/or - 'set_library_dirs()'. 'runtime_library_dirs' is a list of - directories that will be embedded into the shared library and - used to search for other shared libraries that *it* depends on - at run-time. (This may only be relevant on Unix.) - - 'export_symbols' is a list of symbols that the shared library - will export. (This appears to be relevant only on Windows.) - - 'debug' is as for 'compile()' and 'create_static_lib()', with the - slight distinction that it actually matters on most platforms - (as opposed to 'create_static_lib()', which includes a 'debug' - flag mostly for form's sake). - - 'extra_preargs' and 'extra_postargs' are as for 'compile()' - (except of course that they supply command-line arguments - for the particular linker being used). - - Raises LinkError on failure.""" + """Link a bunch of stuff together to create a shared library file. + Similar semantics to 'create_static_lib()', with the addition of + other libraries to link against and directories to search for them. + Also, of course, the type and name of the generated file will + almost certainly be different, as will the program used to create + it. + + 'libraries' is a list of libraries to link against. These are + library names, not filenames, since they're translated into + filenames in a platform-specific way (eg. "foo" becomes "libfoo.a" + on Unix and "foo.lib" on DOS/Windows). However, they can include a + directory component, which means the linker will look in that + specific directory rather than searching all the normal locations. + + 'library_dirs', if supplied, should be a list of directories to + search for libraries that were specified as bare library names + (ie. no directory component). These are on top of the system + default and those supplied to 'add_library_dir()' and/or + 'set_library_dirs()'. 'runtime_library_dirs' is a list of + directories that will be embedded into the shared library and used + to search for other shared libraries that *it* depends on at + run-time. (This may only be relevant on Unix.) + + 'export_symbols' is a list of symbols that the shared library will + export. (This appears to be relevant only on Windows.) + + 'debug' is as for 'compile()' and 'create_static_lib()', with the + slight distinction that it actually matters on most platforms (as + opposed to 'create_static_lib()', which includes a 'debug' flag + mostly for form's sake). + + 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except + of course that they supply command-line arguments for the + particular linker being used). + + Raises LinkError on failure. + """ pass @@ -569,14 +570,15 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None): - """Link a bunch of stuff together to create a shared object - file. Much like 'link_shared_lib()', except the output filename - is explicitly supplied as 'output_filename'. If 'output_dir' is - supplied, 'output_filename' is relative to it - (i.e. 'output_filename' can provide directory components if - needed). - - Raises LinkError on failure.""" + """Link a bunch of stuff together to create a shared object file. + Much like 'link_shared_lib()', except the output filename is + explicitly supplied as 'output_filename'. If 'output_dir' is + supplied, 'output_filename' is relative to it + (i.e. 'output_filename' can provide directory components if + needed). + + Raises LinkError on failure. + """ pass @@ -591,12 +593,13 @@ class CCompiler: extra_preargs=None, extra_postargs=None): """Link a bunch of stuff together to create a binary executable - file. The "bunch of stuff" is as for 'link_shared_lib()'. - 'output_progname' should be the base name of the executable - program--e.g. on Unix the same as the output filename, but - on DOS/Windows ".exe" will be appended. + file. The "bunch of stuff" is as for 'link_shared_lib()'. + 'output_progname' should be the base name of the executable + program--e.g. on Unix the same as the output filename, but on + DOS/Windows ".exe" will be appended. - Raises LinkError on failure.""" + Raises LinkError on failure. + """ pass @@ -607,24 +610,28 @@ class CCompiler: # implement all of these. def library_dir_option (self, dir): - """Return the compiler option to add 'dir' to the list of directories - searched for libraries.""" + """Return the compiler option to add 'dir' to the list of + directories searched for libraries. + """ raise NotImplementedError def runtime_library_dir_option (self, dir): - """Return the compiler option to add 'dir' to the list of directories - searched for runtime libraries.""" + """Return the compiler option to add 'dir' to the list of + directories searched for runtime libraries. + """ raise NotImplementedError def library_option (self, lib): """Return the compiler option to add 'dir' to the list of libraries - linked into the shared library or executable.""" + linked into the shared library or executable. + """ raise NotImplementedError def find_library_file (self, dirs, lib): """Search the specified list of directories for a static or shared - library file 'lib' and return the full path to that file. Return - None if it wasn't found in any of the specified directories.""" + library file 'lib' and return the full path to that file. Return + None if it wasn't found in any of the specified directories. + """ raise NotImplementedError @@ -776,18 +783,16 @@ def new_compiler (plat=None, verbose=0, dry_run=0, force=0): - """Generate an instance of some CCompiler subclass for the supplied - platform/compiler combination. 'plat' defaults to 'os.name' - (eg. 'posix', 'nt'), and 'compiler' defaults to the default - compiler for that platform. Currently only 'posix' and 'nt' - are supported, and the default compilers are "traditional Unix - interface" (UnixCCompiler class) and Visual C++ (MSVCCompiler - class). Note that it's perfectly possible to ask for a Unix - compiler object under Windows, and a Microsoft compiler object - under Unix -- if you supply a value for 'compiler', 'plat' - is ignored.""" - + platform/compiler combination. 'plat' defaults to 'os.name' + (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler + for that platform. Currently only 'posix' and 'nt' are supported, and + the default compilers are "traditional Unix interface" (UnixCCompiler + class) and Visual C++ (MSVCCompiler class). Note that it's perfectly + possible to ask for a Unix compiler object under Windows, and a + Microsoft compiler object under Unix -- if you supply a value for + 'compiler', 'plat' is ignored. + """ if plat is None: plat = os.name @@ -820,15 +825,15 @@ def new_compiler (plat=None, def gen_preprocess_options (macros, include_dirs): - """Generate C pre-processor options (-D, -U, -I) as used by at - least two types of compilers: the typical Unix compiler and Visual - C++. 'macros' is the usual thing, a list of 1- or 2-tuples, where - (name,) means undefine (-U) macro 'name', and (name,value) means - define (-D) macro 'name' to 'value'. 'include_dirs' is just a list of - directory names to be added to the header file search path (-I). - Returns a list of command-line options suitable for either - Unix compilers or Visual C++.""" - + """Generate C pre-processor options (-D, -U, -I) as used by at least + two types of compilers: the typical Unix compiler and Visual C++. + 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,) + means undefine (-U) macro 'name', and (name,value) means define (-D) + macro 'name' to 'value'. 'include_dirs' is just a list of directory + names to be added to the header file search path (-I). Returns a list + of command-line options suitable for either Unix compilers or Visual + C++. + """ # XXX it would be nice (mainly aesthetic, and so we don't generate # stupid-looking command lines) to go over 'macros' and eliminate # redundant definitions/undefinitions (ie. ensure that only the @@ -872,12 +877,11 @@ def gen_preprocess_options (macros, include_dirs): def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): """Generate linker options for searching library directories and - linking with specific libraries. 'libraries' and 'library_dirs' - are, respectively, lists of library names (not filenames!) and - search directories. Returns a list of command-line options suitable - for use with some compiler (depending on the two format strings - passed in).""" - + linking with specific libraries. 'libraries' and 'library_dirs' are, + respectively, lists of library names (not filenames!) and search + directories. Returns a list of command-line options suitable for use + with some compiler (depending on the two format strings passed in). + """ lib_opts = [] for dir in library_dirs: -- cgit v1.2.1 From 57d36e94b071791b0183ad6df383ff293b2ecbc7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 20:40:02 +0000 Subject: Added 'split_quoted()' function to deal with strings that are quoted in Unix shell-like syntax (eg. in Python's Makefile, for one thing -- now that I have this function, I'll probably allow quoted strings in config files too. --- util.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/util.py b/util.py index 74df8aa6..5c1de789 100644 --- a/util.py +++ b/util.py @@ -166,3 +166,70 @@ def grok_environment_error (exc, prefix="error: "): error = prefix + str(exc[-1]) return error + + +# Needed by 'split_quoted()' +_wordchars_re = re.compile(r'[^\\\'\"\ ]*') +_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") +_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') + +def split_quoted (s): + """Split a string up according to Unix shell-like rules for quotes and + backslashes. In short: words are delimited by spaces, as long as those + spaces are not escaped by a backslash, or inside a quoted string. + Single and double quotes are equivalent, and the quote characters can + be backslash-escaped. The backslash is stripped from any two-character + escape sequence, leaving only the escaped character. The quote + characters are stripped from any quoted string. Returns a list of + words. + """ + + # This is a nice algorithm for splitting up a single string, since it + # doesn't require character-by-character examination. It was a little + # bit of a brain-bender to get it working right, though... + + s = string.strip(s) + words = [] + pos = 0 + + while s: + m = _wordchars_re.match(s, pos) + end = m.end() + if end == len(s): + words.append(s[:end]) + break + + if s[end] == ' ': # unescaped, unquoted space: now + words.append(s[:end]) # we definitely have a word delimiter + s = string.lstrip(s[end:]) + pos = 0 + + elif s[end] == '\\': # preserve whatever is being escaped; + # will become part of the current word + s = s[:end] + s[end+1:] + pos = end+1 + + else: + if s[end] == "'": # slurp singly-quoted string + m = _squote_re.match(s, end) + elif s[end] == '"': # slurp doubly-quoted string + m = _dquote_re.match(s, end) + else: + raise RuntimeError, \ + "this can't happen (bad char '%c')" % s[end] + + if m is None: + raise ValueError, \ + "bad string (mismatched %s quotes?)" % s[end] + + (beg, end) = m.span() + s = s[:beg] + s[beg+1:end-1] + s[end:] + pos = m.end() - 2 + + if pos >= len(s): + words.append(s) + break + + return words + +# split_quoted () -- cgit v1.2.1 From d710c3f8dee703894ec6e3e3ab6a4ead1b827be7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:05:29 +0000 Subject: Got rid of direct dependence on the sysconfig module. Mainly, this meant playing along with the new "dictionary of executables" scheme added to CCompiler by adding the 'executables' class attribute, and changing all the compile/link/etc. methods to use the new attributes (which encapsulate both the program to run and its standard arguments, so it was a *little* bit more than just changing some names). --- unixccompiler.py | 102 +++++++++++++++++++------------------------------------ 1 file changed, 35 insertions(+), 67 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 47d8ad63..85ce5df7 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -20,7 +20,6 @@ __revision__ = "$Id$" import string, re, os from types import * from copy import copy -from distutils import sysconfig from distutils.dep_util import newer from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options @@ -45,65 +44,42 @@ from distutils.errors import \ class UnixCCompiler (CCompiler): - # XXX perhaps there should really be *three* kinds of include - # directories: those built in to the preprocessor, those from Python's - # Makefiles, and those supplied to {add,set}_include_dirs(). Currently - # we make no distinction between the latter two at this point; it's all - # up to the client class to select the include directories to use above - # and beyond the compiler's defaults. That is, both the Python include - # directories and any module- or package-specific include directories - # are specified via {add,set}_include_dirs(), and there's no way to - # distinguish them. This might be a bug. - compiler_type = 'unix' - # Needed for the filename generation methods provided by the - # base class, CCompiler. + # These are used by CCompiler in two places: the constructor sets + # instance attributes 'preprocessor', 'compiler', etc. from them, and + # 'set_executable()' allows any of these to be set. The defaults here + # are pretty generic; they will probably have to be set by an outsider + # (eg. using information discovered by the sysconfig about building + # Python extensions). + executables = {'preprocessor' : None, + 'compiler' : ["cc"], + 'compiler_so' : ["cc"], + 'linker_so' : ["cc", "-shared"], + 'linker_exe' : ["cc"], + 'archiver' : ["ar", "-cr"], + 'ranlib' : None, + } + + # Needed for the filename generation methods provided by the base + # class, CCompiler. NB. whoever instantiates/uses a particular + # UnixCCompiler instance should set 'shared_lib_ext' -- we set a + # reasonable common default here, but it's not necessarily used on all + # Unices! + src_extensions = [".c",".C",".cc",".cxx",".cpp"] obj_extension = ".o" static_lib_extension = ".a" - shared_lib_extension = sysconfig.SO + shared_lib_extension = ".so" static_lib_format = shared_lib_format = "lib%s%s" - # Command to create a static library: seems to be pretty consistent - # across the major Unices. Might have to move down into the - # constructor if we need platform-specific guesswork. - archiver = sysconfig.AR - archiver_options = "-cr" - ranlib = sysconfig.RANLIB - def __init__ (self, verbose=0, dry_run=0, force=0): - CCompiler.__init__ (self, verbose, dry_run, force) - self.preprocess_options = None - self.compile_options = None - - # Munge CC and OPT together in case there are flags stuck in CC. - # Note that using these variables from sysconfig immediately makes - # this module specific to building Python extensions and - # inappropriate as a general-purpose C compiler front-end. So sue - # me. Note also that we use OPT rather than CFLAGS, because CFLAGS - # is the flags used to compile Python itself -- not only are there - # -I options in there, they are the *wrong* -I options. We'll - # leave selection of include directories up to the class using - # UnixCCompiler! - - (self.cc, self.ccflags) = \ - _split_command (sysconfig.CC + ' ' + sysconfig.OPT) - self.ccflags_shared = string.split (sysconfig.CCSHARED) - - (self.ld_shared, self.ldflags_shared) = \ - _split_command (sysconfig.LDSHARED) - - self.ld_exec = self.cc - - # __init__ () - def preprocess (self, source, @@ -116,11 +92,11 @@ class UnixCCompiler (CCompiler): (_, macros, include_dirs) = \ self._fix_compile_args (None, macros, include_dirs) pp_opts = gen_preprocess_options (macros, include_dirs) - cc_args = ['-E'] + pp_opts + pp_args = self.preprocessor + pp_opts if output_file: - cc_args.extend(['-o', output_file]) + pp_args.extend(['-o', output_file]) if extra_preargs: - cc_args[:0] = extra_preargs + pp_args[:0] = extra_preargs if extra_postargs: extra_postargs.extend(extra_postargs) @@ -131,7 +107,7 @@ class UnixCCompiler (CCompiler): if output_file: self.mkpath(os.path.dirname(output_file)) try: - self.spawn ([self.cc] + cc_args) + self.spawn (pp_args) except DistutilsExecError, msg: raise CompileError, msg @@ -151,7 +127,7 @@ class UnixCCompiler (CCompiler): # Figure out the options for the compiler command line. pp_opts = gen_preprocess_options (macros, include_dirs) - cc_args = ['-c'] + pp_opts + self.ccflags + self.ccflags_shared + cc_args = pp_opts + ['-c'] if debug: cc_args[:0] = ['-g'] if extra_preargs: @@ -168,7 +144,7 @@ class UnixCCompiler (CCompiler): else: self.mkpath (os.path.dirname (obj)) try: - self.spawn ([self.cc] + cc_args + + self.spawn (self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs) except DistutilsExecError, msg: @@ -193,9 +169,8 @@ class UnixCCompiler (CCompiler): if self._need_link (objects, output_filename): self.mkpath (os.path.dirname (output_filename)) - self.spawn ([self.archiver, - self.archiver_options, - output_filename] + + self.spawn (self.archiver + + [output_filename] + objects + self.objects) # Not many Unices required ranlib anymore -- SunOS 4.x is, I @@ -203,9 +178,9 @@ class UnixCCompiler (CCompiler): # platform intelligence here to skip ranlib if it's not # needed -- or maybe Python's configure script took care of # it for us, hence the check for leading colon. - if self.ranlib[0] != ':': + if self.ranlib: try: - self.spawn ([self.ranlib, output_filename]) + self.spawn (self.ranlib + [output_filename]) except DistutilsExecError, msg: raise LibError, msg else: @@ -263,7 +238,7 @@ class UnixCCompiler (CCompiler): output_filename = os.path.join (output_dir, output_filename) if self._need_link (objects, output_filename): - ld_args = (self.ldflags_shared + objects + self.objects + + ld_args = (objects + self.objects + lib_opts + ['-o', output_filename]) if debug: ld_args[:0] = ['-g'] @@ -273,7 +248,7 @@ class UnixCCompiler (CCompiler): ld_args.extend (extra_postargs) self.mkpath (os.path.dirname (output_filename)) try: - self.spawn ([self.ld_shared] + ld_args) + self.spawn (self.linker_so + ld_args) except DistutilsExecError, msg: raise LinkError, msg else: @@ -314,7 +289,7 @@ class UnixCCompiler (CCompiler): ld_args.extend (extra_postargs) self.mkpath (os.path.dirname (output_filename)) try: - self.spawn ([self.ld_exec] + ld_args) + self.spawn (self.linker_exe + ld_args) except DistutilsExecError, msg: raise LinkError, msg else: @@ -359,10 +334,3 @@ class UnixCCompiler (CCompiler): # find_library_file () # class UnixCCompiler - - -def _split_command (cmd): - """Split a command string up into the progam to run (a string) and - the list of arguments; return them as (cmd, arglist).""" - args = string.split (cmd) - return (args[0], args[1:]) -- cgit v1.2.1 From 69604851149442fe1a0cba66cadf75e9fc6947fb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:08:18 +0000 Subject: Introduced some bureaucracy for setting and tracking the executables that a particular compiler system depends on. This consists of the 'set_executables()' and 'set_executable()' methods, and a few lines in the constructor that expect implementation classes to provide an 'executables' attribute, which we use to initialize several instance attributes. The default implementation is somewhat biased in favour of a Unix/DOS "command-line" view of the world, but it shouldn't be too hard to override this for operating systems with a more sophisticated way of representing programs-to-execute. --- ccompiler.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 9aa41adb..e97c9777 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -12,7 +12,10 @@ from types import * from copy import copy from distutils.errors import * from distutils.spawn import spawn -from distutils.util import move_file, mkpath, newer_pairwise, newer_group +from distutils.file_util import move_file +from distutils.dir_util import mkpath +from distutils.dep_util import newer_pairwise, newer_group +from distutils.util import split_quoted class CCompiler: @@ -109,9 +112,56 @@ class CCompiler: # named library files) to include on any link self.objects = [] + for key in self.executables.keys(): + self.set_executable(key, self.executables[key]) + # __init__ () + def set_executables (self, **args): + + """Define the executables (and options for them) that will be run + to perform the various stages of compilation. The exact set of + executables that may be specified here depends on the compiler + class (via the 'executables' class attribute), but most will have: + compiler the C/C++ compiler + linker_so linker used to create shared objects and libraries + linker_exe linker used to create binary executables + archiver static library creator + + On platforms with a command-line (Unix, DOS/Windows), each of these + is a string that will be split into executable name and (optional) + list of arguments. (Splitting the string is done similarly to how + Unix shells operate: words are delimited by spaces, but quotes and + backslashes can override this. See + 'distutils.util.split_quoted()'.) + """ + + # Note that some CCompiler implementation classes will define class + # attributes 'cpp', 'cc', etc. with hard-coded executable names; + # this is appropriate when a compiler class is for exactly one + # compiler/OS combination (eg. MSVCCompiler). Other compiler + # classes (UnixCCompiler, in particular) are driven by information + # discovered at run-time, since there are many different ways to do + # basically the same things with Unix C compilers. + + for key in args.keys(): + if not self.executables.has_key(key): + raise ValueError, \ + "unknown executable '%s' for class %s" % \ + (key, self.__class__.__name__) + self.set_executable(key, args[key]) + + # set_executables () + + def set_executable(self, key, value): + if type(value) is StringType: + setattr(self, key, split_quoted(value)) + else: + setattr(self, key, value) + + + def _find_macro (self, name): i = 0 for defn in self.macros: @@ -429,6 +479,8 @@ class CCompiler: definitions as for 'compile()', which will augment the macros set with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a list of directory names that will be added to the default list. + + Raises PreprocessError on failure. """ pass @@ -440,8 +492,11 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None): - """Compile one or more C/C++ source files. 'sources' must be a - list of strings, each one the name of a C/C++ source file. Return + + """Compile one or more source files. 'sources' must be a list of + filenames, most likely C/C++ files, but in reality anything that + can be handled by a particular compiler and compiler class + (eg. MSVCCompiler can handle resource files in 'sources'). Return a list of object filenames, one per source filename in 'sources'. Depending on the implementation, not all source files will necessarily be compiled, but all corresponding object filenames -- cgit v1.2.1 From c0d32c47f1960aae8b43c2efd4deeaf996740fc6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:09:14 +0000 Subject: Added the 'customize_compiler()' function, which plugs in the essential information about building Python extensions that we discovered in Python's makefile. Currently only needed on Unix, so does nothing on other systems. --- sysconfig.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index 53da4826..330d3b33 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -97,6 +97,23 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): # get_python_lib() +def customize_compiler (compiler): + """Do any platform-specific customization of the CCompiler instance + 'compiler'. Mainly needed on Unix, so we can plug in the information + that varies across Unices and is stored in Python's Makefile. + """ + if compiler.compiler_type == "unix": + cc_cmd = CC + ' ' + OPT + compiler.set_executables( + preprocessor=CC + " -E", # not always! + compiler=cc_cmd, + compiler_so=cc_cmd + ' ' + CCSHARED, + linker_so=LDSHARED, + linker_exe=CC) + + compiler.shared_lib_extension = SO + + def get_config_h_filename(): """Return full pathname of installed config.h file.""" inc_dir = get_python_inc(plat_specific=1) @@ -260,6 +277,9 @@ def _init_nt(): # Windows. UnixCCompiler expects to find these values in sysconfig, so # here they are. The fact that other Windows compilers don't need # these values is pure luck (hmmm). + + # XXX I think these are now unnecessary... + g['CC'] = "cc" # not gcc? g['RANLIB'] = "ranlib" g['AR'] = "ar" -- cgit v1.2.1 From 9ce4a2b21ef10a62a81c34e979aa56e0f592d2b4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:10:46 +0000 Subject: Fixed a few silly bugs in my SWIG support code. (Hey, I said it was experimental and untested.) Call 'customize_compiler()' after getting CCompiler object. --- command/build_ext.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 6b7ec741..f8df87a7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -12,6 +12,7 @@ import sys, os, string, re from types import * from distutils.core import Command from distutils.errors import * +from distutils.sysconfig import customize_compiler from distutils.dep_util import newer_group from distutils.extension import Extension @@ -191,6 +192,7 @@ class build_ext (Command): verbose=self.verbose, dry_run=self.dry_run, force=self.force) + customize_compiler(self.compiler) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in @@ -453,14 +455,14 @@ class build_ext (Command): for source in sources: (base, ext) = os.path.splitext(source) - if ext in self.swig_ext(): + if ext == ".i": # SWIG interface file new_sources.append(base + ".c") # umm, what if it's C++? - swig_files.append(source) + swig_sources.append(source) swig_targets[source] = new_sources[-1] else: new_sources.append(source) - if not swig_files: + if not swig_sources: return new_sources swig = self.find_swig() -- cgit v1.2.1 From ef29e55a0092ccf9c959cd1cdb5edc96edf4dc8f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:10:58 +0000 Subject: Call 'customize_compiler()' after getting CCompiler object. --- command/build_clib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/build_clib.py b/command/build_clib.py index 7106882d..450dae17 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -23,6 +23,7 @@ import os, string from types import * from distutils.core import Command from distutils.errors import * +from distutils.sysconfig import customize_compiler def show_compilers (): @@ -111,6 +112,7 @@ class build_clib (Command): verbose=self.verbose, dry_run=self.dry_run, force=self.force) + customize_compiler(self.compiler) if self.include_dirs is not None: self.compiler.set_include_dirs (self.include_dirs) -- cgit v1.2.1 From e4b7595cc2c3b3cfb5830579792155aa2b82494a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:12:14 +0000 Subject: Added PreprocessError and UnknownFileError (both used by CCompiler). --- errors.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/errors.py b/errors.py index 16170563..a718f01a 100644 --- a/errors.py +++ b/errors.py @@ -78,6 +78,9 @@ class DistutilsInternalError (DistutilsError): class CCompilerError (Exception): """Some compile/link operation failed.""" +class PreprocessError (CCompilerError): + """Failure to preprocess one or more C/C++ files.""" + class CompileError (CCompilerError): """Failure to compile one or more C/C++ source files.""" @@ -89,4 +92,5 @@ class LinkError (CCompilerError): """Failure to link one or more C/C++ object files into an executable or shared library file.""" - +class UnknownFileError (CCompilerError): + """Attempt to process an unknown file type.""" -- cgit v1.2.1 From b6aafbc5a3d6007b6579cc491ce55c9b34a64620 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:23:11 +0000 Subject: Fixed the "pre-link hook" so it actually works, mainly by renaming it to 'msvc_prelink_hack()', adding the parameters that it actually needs, and only calling it for MSVC compiler objects. Generally gave up on the idea of a general "hook" mechanism: deleted the empty 'precompile_hook()'. --- command/build_ext.py | 74 +++++++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index f8df87a7..0a5fa9cd 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -188,7 +188,8 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler (compiler=self.compiler, + self.compiler = new_compiler (#compiler=self.compiler, + compiler="msvc", verbose=self.verbose, dry_run=self.dry_run, force=self.force) @@ -402,11 +403,6 @@ class build_ext (Command): if os.environ.has_key('CFLAGS'): extra_args.extend(string.split(os.environ['CFLAGS'])) - # Run any platform/compiler-specific hooks needed before - # compiling (currently none, but any hypothetical subclasses - # might find it useful to override this). - self.precompile_hook() - objects = self.compiler.compile (sources, output_dir=self.build_temp, #macros=macros, @@ -421,9 +417,9 @@ class build_ext (Command): objects.extend (ext.extra_objects) extra_args = ext.extra_link_args - # Run any platform/compiler-specific hooks needed between - # compiling and linking (currently needed only on Windows). - self.prelink_hook() + # Bunch of fixing-up we have to do for Microsoft's linker. + if self.compiler.compiler_type == 'msvc': + self.msvc_prelink_hack(sources, ext, extra_args) self.compiler.link_shared_object ( objects, ext_filename, @@ -504,12 +500,9 @@ class build_ext (Command): # find_swig () - # -- Hooks --------------------------------------------------------- - - def precompile_hook (self): - pass + # -- Hooks 'n hacks ------------------------------------------------ - def prelink_hook (self): + def msvc_prelink_hack (self, sources, ext, extra_args): # XXX this is a kludge! Knowledge of specific compilers or # platforms really doesn't belong here; in an ideal world, the @@ -521,33 +514,32 @@ class build_ext (Command): # Thus, kludges like this slip in occasionally. (This is no # excuse for committing more platform- and compiler-specific # kludges; they are to be avoided if possible!) - if self.compiler.compiler_type == 'msvc': - def_file = ext.export_symbol_file - if def_file is None: - source_dir = os.path.dirname (sources[0]) - ext_base = (string.split (ext.name, '.'))[-1] - def_file = os.path.join (source_dir, "%s.def" % ext_base) - if not os.path.exists (def_file): - def_file = None - - if def_file is not None: - extra_args.append ('/DEF:' + def_file) - else: - modname = string.split (ext.name, '.')[-1] - extra_args.append('/export:init%s'%modname) - - # The MSVC linker generates unneeded .lib and .exp files, - # which cannot be suppressed by any linker switches. So - # make sure they are generated in the temporary build - # directory. - implib_file = os.path.join ( - self.build_temp, - self.get_ext_libname (ext.name)) - extra_args.append ('/IMPLIB:' + implib_file) - self.mkpath (os.path.dirname (implib_file)) - # if MSVC - - # prelink_hook () + + def_file = ext.export_symbol_file + if def_file is None: + source_dir = os.path.dirname (sources[0]) + ext_base = (string.split (ext.name, '.'))[-1] + def_file = os.path.join (source_dir, "%s.def" % ext_base) + if not os.path.exists (def_file): + def_file = None + + if def_file is not None: + extra_args.append ('/DEF:' + def_file) + else: + modname = string.split (ext.name, '.')[-1] + extra_args.append('/export:init%s' % modname) + + # The MSVC linker generates unneeded .lib and .exp files, + # which cannot be suppressed by any linker switches. So + # make sure they are generated in the temporary build + # directory. + implib_file = os.path.join ( + self.build_temp, + self.get_ext_libname (ext.name)) + extra_args.append ('/IMPLIB:' + implib_file) + self.mkpath (os.path.dirname (implib_file)) + + # msvc_prelink_hack () # -- Name generators ----------------------------------------------- -- cgit v1.2.1 From 2ae918a80f3a4fd7b6cfad6add81e149bc52e8c4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:30:15 +0000 Subject: Removed some debugging code that slipped into the last checkin. Ensure that 'extra_args' (whether compile or link args) is never None. --- command/build_ext.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 0a5fa9cd..26d69818 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -188,8 +188,7 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler (#compiler=self.compiler, - compiler="msvc", + self.compiler = new_compiler (compiler=self.compiler, verbose=self.verbose, dry_run=self.dry_run, force=self.force) @@ -393,7 +392,7 @@ class build_ext (Command): # The environment variable should take precedence, and # any sensible compiler will give precendence to later # command line args. Hence we combine them in order: - extra_args = ext.extra_compile_args + extra_args = ext.extra_compile_args or [] # XXX and if we support CFLAGS, why not CC (compiler # executable), CPPFLAGS (pre-processor options), and LDFLAGS @@ -415,7 +414,7 @@ class build_ext (Command): # that go into the mix. if ext.extra_objects: objects.extend (ext.extra_objects) - extra_args = ext.extra_link_args + extra_args = ext.extra_link_args or [] # Bunch of fixing-up we have to do for Microsoft's linker. if self.compiler.compiler_type == 'msvc': -- cgit v1.2.1 From 7d785e9c7c3410b9c883c7bab41ebdcdfcab8244 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:31:16 +0000 Subject: Define the 'executables' class attribute so the CCompiler constructor doesn't blow up. We don't currently use the 'set_executables()' bureaucracy, although it would be nice to do so for consistency with UnixCCompiler. --- msvccompiler.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index 06d8501a..2e80ed5f 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -167,6 +167,13 @@ class MSVCCompiler (CCompiler) : compiler_type = 'msvc' + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + # Private class data (need to distinguish C from C++ source for compiler) _c_extensions = ['.c'] _cpp_extensions = ['.cc','.cpp'] @@ -295,7 +302,7 @@ class MSVCCompiler (CCompiler) : if extra_postargs: lib_args.extend (extra_postargs) try: - self.spawn ([self.link] + ld_args) + self.spawn ([self.lib] + lib_args) except DistutilsExecError, msg: raise LibError, msg -- cgit v1.2.1 From 2bc3b231e4cc5a7d5570923a0d49df5500171d35 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:21:22 +0000 Subject: Added 'include_dirs' parameters all over the place. Added 'check_lib()', which provides a subset of the functionality of 'check_func()' with a simpler interface and implementation. --- command/config.py | 58 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/command/config.py b/command/config.py index 570f71a5..5c3f26a7 100644 --- a/command/config.py +++ b/command/config.py @@ -112,24 +112,26 @@ class config (Command): file.close() return filename - def _preprocess (self, body, headers, lang): + def _preprocess (self, body, headers, include_dirs, lang): src = self._gen_temp_sourcefile(body, headers, lang) out = "_configtest.i" self.temp_files.extend([src, out]) - self.compiler.preprocess(src, out) + self.compiler.preprocess(src, out, include_dirs=include_dirs) return (src, out) - def _compile (self, body, headers, lang): + def _compile (self, body, headers, include_dirs, lang): src = self._gen_temp_sourcefile(body, headers, lang) if self.dump_source: dump_file(src, "compiling '%s':" % src) (obj,) = self.compiler.object_filenames([src]) self.temp_files.extend([src, obj]) - self.compiler.compile([src]) + self.compiler.compile([src], include_dirs=include_dirs) return (src, obj) - def _link (self, body, headers, libraries, library_dirs, lang): - (src, obj) = self._compile(body, headers, lang) + def _link (self, body, + headers, include_dirs, + libraries, library_dirs, lang): + (src, obj) = self._compile(body, headers, include_dirs, lang) prog = os.path.splitext(os.path.basename(src))[0] self.temp_files.append(prog) # XXX should be prog + exe_ext self.compiler.link_executable([obj], prog, @@ -159,7 +161,7 @@ class config (Command): # XXX need access to the header search path and maybe default macros. - def try_cpp (self, body=None, headers=None, lang="c"): + def try_cpp (self, body=None, headers=None, include_dirs=None, lang="c"): """Construct a source file from 'body' (a string containing lines of C/C++ code) and 'headers' (a list of header files to include) and run it through the preprocessor. Return true if the @@ -177,7 +179,8 @@ class config (Command): self._clean() return ok - def search_cpp (self, pattern, body=None, headers=None, lang="c"): + def search_cpp (self, pattern, body=None, + headers=None, include_dirs=None, lang="c"): """Construct a source file (just like 'try_cpp()'), run it through the preprocessor, and return true if any line of the output matches 'pattern'. 'pattern' should either be a compiled regex object or a @@ -206,7 +209,7 @@ class config (Command): self._clean() return match - def try_compile (self, body, headers=None, lang="c"): + def try_compile (self, body, headers=None, include_dirs=None, lang="c"): """Try to compile a source file built from 'body' and 'headers'. Return true on success, false otherwise. """ @@ -222,8 +225,8 @@ class config (Command): self._clean() return ok - def try_link (self, - body, headers=None, + def try_link (self, body, + headers=None, include_dirs=None, libraries=None, library_dirs=None, lang="c"): """Try to compile and link a source file, built from 'body' and @@ -233,7 +236,8 @@ class config (Command): from distutils.ccompiler import CompileError, LinkError self._check_compiler() try: - self._link(body, headers, libraries, library_dirs, lang) + self._link(body, headers, include_dirs, + libraries, library_dirs, lang) ok = 1 except (CompileError, LinkError): ok = 0 @@ -242,8 +246,8 @@ class config (Command): self._clean() return ok - def try_run (self, - body, headers=None, + def try_run (self, body, + headers=None, include_dirs=None, libraries=None, library_dirs=None, lang="c"): """Try to compile, link to an executable, and run a program @@ -253,7 +257,8 @@ class config (Command): from distutils.ccompiler import CompileError, LinkError self._check_compiler() try: - self._link(body, headers, libraries, library_dirs, lang) + self._link(body, headers, include_dirs, + libraries, library_dirs, lang) self.spawn([exe]) ok = 1 except (CompileError, LinkError, DistutilsExecError): @@ -268,7 +273,8 @@ class config (Command): # (these are the ones that are actually likely to be useful # when implementing a real-world config command!) - def check_func (self, func, headers=None, + def check_func (self, func, + headers=None, include_dirs=None, libraries=None, library_dirs=None, decl=0, call=0): @@ -298,16 +304,30 @@ class config (Command): body.append("}") body = string.join(body, "\n") + "\n" - return self.try_link(body, headers, libraries, library_dirs) + return self.try_link(body, headers, include_dirs, + libraries, library_dirs) # check_func () - def check_header (self, header, lang="c"): + def check_lib (self, library, library_dirs=None, + headers=None, include_dirs=None): + """Determine if 'library' is available to be linked against, + without actually checking that any particular symbols are provided + by it. 'headers' will be used in constructing the source file to + be compiled, but the only effect of this is to check if all the + header files listed are available. + """ + self._check_compiler() + return self.try_link("int main (void) { }", + headers, include_dirs, [library], library_dirs) + + def check_header (self, header, include_dirs=None, + library_dirs=None, lang="c"): """Determine if the system header file named by 'header_file' exists and can be found by the preprocessor; return true if so, false otherwise. """ - return self.try_cpp(headers=[header]) + return self.try_cpp(headers=[header], include_dirs=include_dirs) # class config -- cgit v1.2.1 From 1930cf43c4152c2db35164b0dca4a8a85f58c37d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:24:07 +0000 Subject: Infrastructure support for the "bdist_wininst" command. --- command/__init__.py | 1 + command/bdist.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/command/__init__.py b/command/__init__.py index 95bce8d8..ef8e9ad6 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -20,4 +20,5 @@ __all__ = ['build', 'bdist', 'bdist_dumb', 'bdist_rpm', + 'bdist_wininst', ] diff --git a/command/bdist.py b/command/bdist.py index 47d4cbc9..3fcbf779 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -55,6 +55,8 @@ class bdist (Command): 'ztar': ('bdist_dumb', "compressed tar file"), 'tar': ('bdist_dumb', "tar file"), 'zip': ('bdist_dumb', "ZIP file"), + 'wininst': ('bdist_wininst', + "Windows executable installer"), } # establish the preferred order format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', 'zip'] -- cgit v1.2.1 From 78fe2e5e7ee63ad56dc426dd2afbd2c3f986d196 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:24:38 +0000 Subject: Thomas Heller's "bdist_wininst" command (unreviewed, untested). --- command/bdist_wininst.py | 448 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 448 insertions(+) create mode 100644 command/bdist_wininst.py diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py new file mode 100644 index 00000000..6a4b4722 --- /dev/null +++ b/command/bdist_wininst.py @@ -0,0 +1,448 @@ +"""distutils.command.bdist_wininst + +Implements the Distutils 'bdist_wininst' command: create a windows installer +exe-program.""" + +# created 2000/06/02, Thomas Heller + +__revision__ = "$Id$" + +import os, sys +from distutils.core import Command +from distutils.util import get_platform, create_tree, remove_tree +from distutils.errors import * + +class bdist_wininst (Command): + + description = "create a \"wininst\" built distribution" + + user_options = [('bdist-dir=', 'd', + "temporary directory for creating the distribution"), + ('keep-tree', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('target-compile', 'c', + "compile to .pyc on the target system"), + ('target-optimize', 'o', + "compile to .pyo on the target system"), + ('target-version=', 'v', + "require a specific python version" + + " on the target system (1.5 or 1.6)"), + ] + + def initialize_options (self): + self.bdist_dir = None + self.keep_tree = 0 + self.target_compile = 0 + self.target_optimize = 0 + self.target_version = None + + # initialize_options() + + + def finalize_options (self): + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'wininst') + if not self.target_version: + self.target_version = "" + else: + if not self.target_version in ("1.5", "1.6"): + raise DistutilsOptionError ( + "target version must be 1.5 or 1.6") + if self.distribution.has_ext_modules(): + short_version = sys.version[:3] + if self.target_version and self.target_version != short_version: + raise DistutilsOptionError ("target version can only be" + + short_version) + self.target_version = short_version + + # finalize_options() + + + def run (self): + + self.run_command ('build') + + # XXX don't use 'self.get_finalized_command()', because it always + # runs 'ensure_finalized()' on the command object; we explictly + # want a command object that has *not* been finalized, so we can set + # options on it! (The option we set, 'root', is so that we can do + # a proper "fake install" using this install command object.) + install = self.distribution.get_command_obj('install') + install.root = self.bdist_dir + + install_lib = self.distribution.get_command_obj('install_lib') + + install_lib.compile = 0 + install_lib.optimize = 0 + + # The packager (correct term in distutils speak?) can choose + # if pyc and pyo files should be created on the TARGET system + # instead at the SOURCE system. + +## # The compilation can only be done on the SOURCE system +## # for one python version (assuming 1.6 and 1.5 have incompatible +## # byte-codes). +## short_version = sys.version[:3] +## if self.target_version == short_version: +## if not self.target_compile: +## install_lib.compile = 1 +## if not self.target_optimize: +## install_lib.optimize = 1 + + install_lib.ensure_finalized() + + self.announce ("installing to %s" % self.bdist_dir) + install.ensure_finalized() + + install.run() + + # And make an archive relative to the root of the + # pseudo-installation tree. + archive_basename = "%s.win32" % self.distribution.get_fullname() + # XXX hack! Our archive MUST be relative to sys.prefix + # XXX What about .install_data, .install_scripts, ...? + root_dir = install.install_lib + arcname = self.make_archive (archive_basename, "zip", + root_dir=root_dir) + + self.create_exe (arcname) + + if not self.keep_tree: + remove_tree (self.bdist_dir, self.verbose, self.dry_run) + + # run() + + def create_inifile (self): + # create an inifile containing data describing the installation. + # This could be done without creating a real file, but + # a file is (at least) usefull for debugging bdist_wininst. + import string + + metadata = self.distribution.metadata + + ini_name = "%s.ini" % self.distribution.get_fullname() + + self.announce ("creating %s" % ini_name) + + inifile = open (ini_name, "w") + + # write the [metadata] section. values are written with repr()[1:], + # so they do not contain unprintable characters, and are not + # surrounded by quote chars + inifile.write ("[metadata]\n") + + # 'info' will be displayed in the installers dialog box, + # describing the items to be installed + info = metadata.long_description + '\n' + + for name in dir (metadata): + if (name != 'long_description'): + data = getattr (metadata, name) + if data: + info = info + ("\n %s: %s" % \ + (string.capitalize (name), data)) + inifile.write ("%s=%s\n" % (name, repr (data)[1:-1])) + + # The [setup] section contains entries controlling + # the installer runtime. + inifile.write ("\n[Setup]\n") + inifile.write ("info=%s\n" % repr (info)[1:-1]) + inifile.write ("pthname=%s.%s\n" % (metadata.name, metadata.version)) + inifile.write ("pyc_compile=%d\n" % self.target_compile) + inifile.write ("pyo_compile=%d\n" % self.target_optimize) + if self.target_version: + vers_minor = string.split (self.target_version, '.')[1] + inifile.write ("vers_minor=%s\n" % vers_minor) + + title = self.distribution.get_fullname() + inifile.write ("title=%s\n" % repr (title)[1:-1]) + inifile.close() + return ini_name + + # create_inifile() + + def create_exe (self, arcname): + import struct, zlib + + cfgdata = open (self.create_inifile()).read() + + comp_method = zlib.DEFLATED + co = zlib.compressobj (zlib.Z_DEFAULT_COMPRESSION, comp_method, -15) + zcfgdata = co.compress (cfgdata) + co.flush() + + installer_name = "%s.win32.exe" % self.distribution.get_fullname() + + self.announce ("creating %s" % installer_name) + + file = open (installer_name, "wb") + file.write (self.get_exe_bytes ()) + file.write (zcfgdata) + crc32 = zlib.crc32 (cfgdata) + header = struct.pack (" Date: Tue, 27 Jun 2000 01:37:10 +0000 Subject: Thomas Heller: added --swig-cpp option and fixed silly typos in SWIG support. Also supposedly made some change to where .lib files wind up under MSVC++, but I don't understand how to code is doing what Thomas says it's doing. --- command/build_ext.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 26d69818..106faccb 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -77,6 +77,8 @@ class build_ext (Command): "forcibly build everything (ignore file timestamps)"), ('compiler=', 'c', "specify the compiler type"), + ('swig-cpp', None, + "make SWIG create C++ files (default is C)"), ] help_options = [ @@ -101,6 +103,7 @@ class build_ext (Command): self.debug = None self.force = None self.compiler = None + self.swig_cpp = None def finalize_options (self): @@ -443,15 +446,20 @@ class build_ext (Command): swig_sources = [] swig_targets = {} - # XXX this drops generated C files into the source tree, which + # XXX this drops generated C/C++ files into the source tree, which # is fine for developers who want to distribute the generated # source -- but there should be an option to put SWIG output in # the temp dir. + if self.swig_cpp: + target_ext = '.cpp' + else: + target_ext = '.c' + for source in sources: (base, ext) = os.path.splitext(source) if ext == ".i": # SWIG interface file - new_sources.append(base + ".c") # umm, what if it's C++? + new_sources.append(base + target_ext) swig_sources.append(source) swig_targets[source] = new_sources[-1] else: @@ -461,11 +469,14 @@ class build_ext (Command): return new_sources swig = self.find_swig() - swig_cmd = [swig, "-python", "-dnone", "-ISWIG"] # again, C++?!? + swig_cmd = [swig, "-python", "-dnone", "-ISWIG"] + if self.swig_cpp: + swig_cmd.append ("-c++") for source in swig_sources: - self.announce ("swigging %s to %s" % (src, obj)) - self.spawn(swig_cmd + ["-o", swig_targets[source], source]) + target = swig_targets[source] + self.announce ("swigging %s to %s" % (source, target)) + self.spawn(swig_cmd + ["-o", target, source]) return new_sources @@ -528,10 +539,11 @@ class build_ext (Command): modname = string.split (ext.name, '.')[-1] extra_args.append('/export:init%s' % modname) - # The MSVC linker generates unneeded .lib and .exp files, - # which cannot be suppressed by any linker switches. So - # make sure they are generated in the temporary build - # directory. + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. implib_file = os.path.join ( self.build_temp, self.get_ext_libname (ext.name)) -- cgit v1.2.1 From f73b0b9df04af727d477c1f326fc1b82639b8ead Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:43:24 +0000 Subject: A-ha! Read Thomas' patch a little more carefully and figured it out: the 'implib_dir' attribute is back (only on NT, of course). --- command/build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 106faccb..f8aa91ca 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -155,6 +155,7 @@ class build_ext (Command): # also Python's library directory must be appended to library_dirs if os.name == 'nt': self.library_dirs.append (os.path.join(sys.exec_prefix, 'libs')) + self.implib_dir = self.build_temp if self.debug: self.build_temp = os.path.join (self.build_temp, "Debug") else: @@ -545,7 +546,7 @@ class build_ext (Command): # directory. Since they have different names for debug and release # builds, they can go into the same directory. implib_file = os.path.join ( - self.build_temp, + self.implib_dir, self.get_ext_libname (ext.name)) extra_args.append ('/IMPLIB:' + implib_file) self.mkpath (os.path.dirname (implib_file)) -- cgit v1.2.1 From 603c444d6b9ead884a9c09ca25b5fb707ca622b6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:59:06 +0000 Subject: Fixed LDSHARED for AIX, based on a patch by Rene Liebscher. Ditched my old code that fixed relative paths in the Makefile -- didn't work, doomed to failure, etc. --- sysconfig.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 330d3b33..9ca94ed9 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -222,21 +222,6 @@ def parse_makefile(fp, g=None): # bogus variable reference; just drop it since we can't deal del notdone[name] - # "Fix" all pathnames in the Makefile that are explicitly relative, - # ie. that start with "./". This is a kludge to fix the "./ld_so_aix" - # problem, the nature of which is that Python's installed Makefile - # refers to "./ld_so_aix", but when we are building extensions we are - # far from the directory where Python's Makefile (and ld_so_aix, for - # that matter) is installed. Unfortunately, there are several other - # relative pathnames in the Makefile, and this fix doesn't fix them, - # because the layout of Python's source tree -- which is what the - # Makefile refers to -- is not fully preserved in the Python - # installation. Grumble. - from os.path import normpath, join, dirname - for (name, value) in done.items(): - if value[0:2] == "./": - done[name] = normpath(join(dirname(fp.name), value)) - # save the results in the global dictionary g.update(done) return g @@ -257,6 +242,18 @@ def _init_posix(): raise DistutilsPlatformError, my_msg parse_makefile(file, g) + + # On AIX, there are wrong paths to the linker scripts in the Makefile + # -- these paths are relative to the Python source, but when installed + # the scripts are in another directory. + if sys.platform: # == 'aix4': # what about AIX 3.x ? + # Linker script is in the config directory, not in Modules as the + # Makefile says. + python_lib = get_python_lib(standard_lib=1) + ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') + python_exp = os.path.join(python_lib, 'config', 'python.exp') + + g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) def _init_nt(): -- cgit v1.2.1 From ecb70087dbb4512152365262d97e3879509cbec1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:59:43 +0000 Subject: Oops, only do that AIX hack on AIX. --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 9ca94ed9..b2aa3f2d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -246,7 +246,7 @@ def _init_posix(): # On AIX, there are wrong paths to the linker scripts in the Makefile # -- these paths are relative to the Python source, but when installed # the scripts are in another directory. - if sys.platform: # == 'aix4': # what about AIX 3.x ? + if sys.platform == 'aix4': # what about AIX 3.x ? # Linker script is in the config directory, not in Modules as the # Makefile says. python_lib = get_python_lib(standard_lib=1) -- cgit v1.2.1 From 9a02fc21cdba079187b15393495606987817c20e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 00:36:40 +0000 Subject: Fixed to use 'reinitialize_command()' to fetch the "install" command object. --- command/bdist_dumb.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 51e35e4b..f9c81fc2 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -58,12 +58,7 @@ class bdist_dumb (Command): self.run_command ('build') - # XXX don't use 'self.get_finalized_command()', because it always runs - # 'ensure_finalized()' on the command object; we explictly want a - # command object that has *not* been finalized, so we can set - # options on it! (The option we set, 'root', is so that we can do - # a proper "fake install" using this install command object.) - install = self.distribution.get_command_obj('install') + install = self.reinitialize_command('install') install.root = self.bdist_dir self.announce ("installing to %s" % self.bdist_dir) -- cgit v1.2.1 From 60c49dce0d05efd5482eb0d8dc99f969a6b981f8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 00:56:20 +0000 Subject: Fixed to use 'reinitialize_command()' to fetch "install" and "install_lib" command objects. Various formatting tweaks, typo fixes in comments. --- command/bdist_wininst.py | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 6a4b4722..d3d17721 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -7,14 +7,14 @@ exe-program.""" __revision__ = "$Id$" -import os, sys +import sys, os, string from distutils.core import Command from distutils.util import get_platform, create_tree, remove_tree from distutils.errors import * class bdist_wininst (Command): - description = "create a \"wininst\" built distribution" + description = "create an executable installer for MS Windows" user_options = [('bdist-dir=', 'd', "temporary directory for creating the distribution"), @@ -64,22 +64,15 @@ class bdist_wininst (Command): self.run_command ('build') - # XXX don't use 'self.get_finalized_command()', because it always - # runs 'ensure_finalized()' on the command object; we explictly - # want a command object that has *not* been finalized, so we can set - # options on it! (The option we set, 'root', is so that we can do - # a proper "fake install" using this install command object.) - install = self.distribution.get_command_obj('install') + install = self.reinitialize_command('install') install.root = self.bdist_dir - install_lib = self.distribution.get_command_obj('install_lib') - + install_lib = self.reinitialize_command('install_lib') install_lib.compile = 0 install_lib.optimize = 0 - # The packager (correct term in distutils speak?) can choose - # if pyc and pyo files should be created on the TARGET system - # instead at the SOURCE system. + # The packager can choose if .pyc and .pyo files should be created + # on the TARGET system instead at the SOURCE system. ## # The compilation can only be done on the SOURCE system ## # for one python version (assuming 1.6 and 1.5 have incompatible @@ -95,7 +88,6 @@ class bdist_wininst (Command): self.announce ("installing to %s" % self.bdist_dir) install.ensure_finalized() - install.run() # And make an archive relative to the root of the @@ -103,10 +95,14 @@ class bdist_wininst (Command): archive_basename = "%s.win32" % self.distribution.get_fullname() # XXX hack! Our archive MUST be relative to sys.prefix # XXX What about .install_data, .install_scripts, ...? + # [Perhaps require that all installation dirs be under sys.prefix + # on Windows? this will be acceptable until we start dealing + # with Python applications, at which point we should zip up + # the application directory -- and again everything can be + # under one dir --GPW] root_dir = install.install_lib arcname = self.make_archive (archive_basename, "zip", - root_dir=root_dir) - + root_dir=root_dir) self.create_exe (arcname) if not self.keep_tree: @@ -115,26 +111,23 @@ class bdist_wininst (Command): # run() def create_inifile (self): - # create an inifile containing data describing the installation. + # Create an inifile containing data describing the installation. # This could be done without creating a real file, but - # a file is (at least) usefull for debugging bdist_wininst. - import string + # a file is (at least) useful for debugging bdist_wininst. metadata = self.distribution.metadata - - ini_name = "%s.ini" % self.distribution.get_fullname() + ini_name = "%s.ini" % metadata.get_fullname() self.announce ("creating %s" % ini_name) - inifile = open (ini_name, "w") - # write the [metadata] section. values are written with repr()[1:], - # so they do not contain unprintable characters, and are not - # surrounded by quote chars + # Write the [metadata] section. Values are written with + # repr()[1:-1], so they do not contain unprintable characters, and + # are not surrounded by quote chars. inifile.write ("[metadata]\n") - # 'info' will be displayed in the installers dialog box, - # describing the items to be installed + # 'info' will be displayed in the installer's dialog box, + # describing the items to be installed. info = metadata.long_description + '\n' for name in dir (metadata): @@ -173,7 +166,6 @@ class bdist_wininst (Command): zcfgdata = co.compress (cfgdata) + co.flush() installer_name = "%s.win32.exe" % self.distribution.get_fullname() - self.announce ("creating %s" % installer_name) file = open (installer_name, "wb") -- cgit v1.2.1 From 834a327e9f342c7903be01c24ecca10ff2c8138a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 01:20:35 +0000 Subject: Lyle Johnson's interface to Borland C++, with a few editorial comments by me. Two major points: * lots of overlap with MSVCCompiler; the common code really should be factored out into a base class, say WindowsCCompiler * it doesn't work: weird problem spawning the linker (see comment for details) --- bcppcompiler.py | 344 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 bcppcompiler.py diff --git a/bcppcompiler.py b/bcppcompiler.py new file mode 100644 index 00000000..6c9ac827 --- /dev/null +++ b/bcppcompiler.py @@ -0,0 +1,344 @@ +"""distutils.bcppcompiler + +Contains BorlandCCompiler, an implementation of the abstract CCompiler class +for the Borland C++ compiler. +""" + +# This implementation by Lyle Johnson, based on the original msvccompiler.py +# module and using the directions originally published by Gordon Williams. + +# XXX looks like there's a LOT of overlap between these two classes: +# someone should sit down and factor out the common code as +# WindowsCCompiler! --GPW + +# XXX Lyle reports that this doesn't quite work yet: +# """...but this is what I've got so far. The compile step works fine but +# when it runs the link step I get an "out of memory" failure. Since +# spawn() echoes the command it's trying to spawn, I can type the link line +# verbatim at the DOS prompt and it links the Windows DLL correctly -- so +# the syntax is correct. There's just some weird interaction going on when +# it tries to "spawn" the link process from within the setup.py script. I'm +# not really sure how to debug this one right off-hand; obviously there's +# nothing wrong with the "spawn()" function since it's working properly for +# the compile stage.""" + +__revision__ = "$Id$" + + +import sys, os, string +from distutils.errors import \ + DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options, gen_lib_options + + +class BCPPCompiler(CCompiler) : + """Concrete class that implements an interface to the Borland C/C++ + compiler, as defined by the CCompiler abstract class. + """ + + compiler_type = 'bcpp' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = _c_extensions + _cpp_extensions + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + + def __init__ (self, + verbose=0, + dry_run=0, + force=0): + + CCompiler.__init__ (self, verbose, dry_run, force) + + # These executables are assumed to all be in the path. + # Borland doesn't seem to use any special registry settings to + # indicate their installation locations. + + self.cc = "bcc32.exe" + self.link = "ilink32.exe" + self.lib = "tlib.exe" + + self.preprocess_options = None + self.compile_options = ['/tWM', '/O2', '/q'] + self.compile_options_debug = ['/tWM', '/Od', '/q'] + + self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x'] + self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x'] + self.ldflags_static = [] + + + # -- Worker methods ------------------------------------------------ + + def compile (self, + sources, + output_dir=None, + macros=None, + include_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None): + + (output_dir, macros, include_dirs) = \ + self._fix_compile_args (output_dir, macros, include_dirs) + (objects, skip_sources) = self._prep_compile (sources, output_dir) + + if extra_postargs is None: + extra_postargs = [] + + pp_opts = gen_preprocess_options (macros, include_dirs) + compile_opts = extra_preargs or [] + compile_opts.append ('-c') + if debug: + compile_opts.extend (self.compile_options_debug) + else: + compile_opts.extend (self.compile_options) + + for i in range (len (sources)): + src = sources[i] ; obj = objects[i] + ext = (os.path.splitext (src))[1] + + if skip_sources[src]: + self.announce ("skipping %s (%s up-to-date)" % (src, obj)) + else: + if ext in self._c_extensions: + input_opt = "" + elif ext in self._cpp_extensions: + input_opt = "-P" + + output_opt = "-o" + obj + + self.mkpath (os.path.dirname (obj)) + + # Compiler command line syntax is: "bcc32 [options] file(s)". + # Note that the source file names must appear at the end of + # the command line. + + try: + self.spawn ([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs + [src]) + except DistutilsExecError, msg: + raise CompileError, msg + + return objects + + # compile () + + + def create_static_lib (self, + objects, + output_libname, + output_dir=None, + debug=0, + extra_preargs=None, + extra_postargs=None): + + (objects, output_dir) = self._fix_object_args (objects, output_dir) + output_filename = \ + self.library_filename (output_libname, output_dir=output_dir) + + if self._need_link (objects, output_filename): + lib_args = [output_filename, '/u'] + objects + if debug: + pass # XXX what goes here? + if extra_preargs: + lib_args[:0] = extra_preargs + if extra_postargs: + lib_args.extend (extra_postargs) + try: + self.spawn ([self.lib] + lib_args) + except DistutilsExecError, msg: + raise LibError, msg + else: + self.announce ("skipping %s (up-to-date)" % output_filename) + + # create_static_lib () + + + def link_shared_lib (self, + objects, + output_libname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): + + self.link_shared_object (objects, + self.shared_library_name(output_libname), + output_dir=output_dir, + libraries=libraries, + library_dirs=library_dirs, + runtime_library_dirs=runtime_library_dirs, + export_symbols=export_symbols, + debug=debug, + extra_preargs=extra_preargs, + extra_postargs=extra_postargs, + build_temp=build_temp) + + + def link_shared_object (self, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): + + (objects, output_dir) = self._fix_object_args (objects, output_dir) + (libraries, library_dirs, runtime_library_dirs) = \ + self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) + + if runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) + + if output_dir is not None: + output_filename = os.path.join (output_dir, output_filename) + + if self._need_link (objects, output_filename): + + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared + + startup_obj = 'c0d32' + + libraries.append ('mypylib') + libraries.append ('import32') + libraries.append ('cw32mt') + + # Create a temporary exports file for use by the linker + head, tail = os.path.split (output_filename) + modname, ext = os.path.splitext (tail) + def_file = os.path.join (build_temp, '%s.def' % modname) + f = open (def_file, 'w') + f.write ('EXPORTS\n') + for sym in (export_symbols or []): + f.write (' %s=_%s\n' % (sym, sym)) + + ld_args = ldflags + [startup_obj] + objects + \ + [',%s,,' % output_filename] + \ + libraries + [',' + def_file] + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend (extra_postargs) + + self.mkpath (os.path.dirname (output_filename)) + try: + self.spawn ([self.link] + ld_args) + except DistutilsExecError, msg: + raise LinkError, msg + + else: + self.announce ("skipping %s (up-to-date)" % output_filename) + + # link_shared_object () + + + def link_executable (self, + objects, + output_progname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None): + + (objects, output_dir) = self._fix_object_args (objects, output_dir) + (libraries, library_dirs, runtime_library_dirs) = \ + self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) + + if runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) + + lib_opts = gen_lib_options (self, + library_dirs, runtime_library_dirs, + libraries) + output_filename = output_progname + self.exe_extension + if output_dir is not None: + output_filename = os.path.join (output_dir, output_filename) + + if self._need_link (objects, output_filename): + + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + + ld_args = ldflags + lib_opts + \ + objects + ['/OUT:' + output_filename] + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend (extra_postargs) + + self.mkpath (os.path.dirname (output_filename)) + try: + self.spawn ([self.link] + ld_args) + except DistutilsExecError, msg: + raise LinkError, msg + else: + self.announce ("skipping %s (up-to-date)" % output_filename) + + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option (self, dir): + return "-L" + dir + + def runtime_library_dir_option (self, dir): + raise DistutilsPlatformError, \ + "don't know how to set runtime library search path for MSVC++" + + def library_option (self, lib): + return self.library_filename (lib) + + + def find_library_file (self, dirs, lib): + + for dir in dirs: + libfile = os.path.join (dir, self.library_filename (lib)) + if os.path.exists (libfile): + return libfile + + else: + # Oops, didn't find it in *any* of 'dirs' + return None + -- cgit v1.2.1 From 6d74cfe58c300042b6a6e67053efb05ef972621e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 01:25:27 +0000 Subject: Typo fix. --- dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 287137ee..bb476065 100644 --- a/dist.py +++ b/dist.py @@ -730,7 +730,7 @@ class Distribution: def reinitialize_command (self, command): """Reinitializes a command to the state it was in when first returned by 'get_command_obj()': ie., initialized but not yet - finalized. This gives provides the opportunity to sneak option + finalized. This provides the opportunity to sneak option values in programmatically, overriding or supplementing user-supplied values from the config files and command line. You'll have to re-finalize the command object (by calling -- cgit v1.2.1 From de30582dec6027d2b0441578781cbbb71033a72f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 01:29:09 +0000 Subject: Lyle Johnson: added 'build_temp' parameter to 'link_shared_{lib,object}()' methods (but not 'link_executable()', hmmm). Currently only used by BCPPCompiler; it's a dummy parameter for UnixCCompiler and MSVCCompiler. Also added 'bcpp' to compiler table used by 'new_compiler()'. --- ccompiler.py | 10 ++++++---- msvccompiler.py | 11 +++++++---- unixccompiler.py | 11 ++++++++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index e97c9777..20a5191b 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -492,7 +492,6 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None): - """Compile one or more source files. 'sources' must be a list of filenames, most likely C/C++ files, but in reality anything that can be handled by a particular compiler and compiler class @@ -572,8 +571,8 @@ class CCompiler: export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None): - + extra_postargs=None, + build_temp=None): """Link a bunch of stuff together to create a shared library file. Similar semantics to 'create_static_lib()', with the addition of other libraries to link against and directories to search for them. @@ -624,7 +623,8 @@ class CCompiler: export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None): + extra_postargs=None, + build_temp=None): """Link a bunch of stuff together to create a shared object file. Much like 'link_shared_lib()', except the output filename is explicitly supplied as 'output_filename'. If 'output_dir' is @@ -814,6 +814,8 @@ compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', "Cygwin port of GNU C Compiler for Win32"), 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler', "Mingw32 port of GNU C Compiler for Win32"), + 'bcpp': ('bcppcompiler', 'BCPPCompiler', + "Borland C++ Compiler"), } def show_compilers(): diff --git a/msvccompiler.py b/msvccompiler.py index 2e80ed5f..db8bd35a 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -1,4 +1,4 @@ -"""distutils.ccompiler +"""distutils.msvccompiler Contains MSVCCompiler, an implementation of the abstract CCompiler class for the Microsoft Visual Studio.""" @@ -322,7 +322,8 @@ class MSVCCompiler (CCompiler) : export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None): + extra_postargs=None, + build_temp=None): self.link_shared_object (objects, self.shared_library_name(output_libname), @@ -333,7 +334,8 @@ class MSVCCompiler (CCompiler) : export_symbols=export_symbols, debug=debug, extra_preargs=extra_preargs, - extra_postargs=extra_postargs) + extra_postargs=extra_postargs, + build_temp=build_temp) def link_shared_object (self, @@ -346,7 +348,8 @@ class MSVCCompiler (CCompiler) : export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None): + extra_postargs=None, + build_temp=None): (objects, output_dir) = self._fix_object_args (objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ diff --git a/unixccompiler.py b/unixccompiler.py index 85ce5df7..4772e1a8 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -74,6 +74,7 @@ class UnixCCompiler (CCompiler): static_lib_format = shared_lib_format = "lib%s%s" + def __init__ (self, verbose=0, dry_run=0, @@ -199,7 +200,9 @@ class UnixCCompiler (CCompiler): export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None): + extra_postargs=None, + build_temp=None): + self.link_shared_object ( objects, self.shared_library_filename (output_libname), @@ -210,7 +213,8 @@ class UnixCCompiler (CCompiler): export_symbols, debug, extra_preargs, - extra_postargs) + extra_postargs, + build_temp) def link_shared_object (self, @@ -223,7 +227,8 @@ class UnixCCompiler (CCompiler): export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None): + extra_postargs=None, + build_temp=None): (objects, output_dir) = self._fix_object_args (objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ -- cgit v1.2.1 From 2a385bb0eca83d0735d51671439c5e1978ca4bd2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 01:29:37 +0000 Subject: Lyle Johnson: pass in temp directory as 'build_temp' argument when calling 'link_shared_object()'. --- command/build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index f8aa91ca..adf85d32 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -430,7 +430,8 @@ class build_ext (Command): library_dirs=ext.library_dirs, runtime_library_dirs=ext.runtime_library_dirs, extra_postargs=extra_args, - debug=self.debug) + debug=self.debug, + build_temp=self.build_temp) # build_extensions () -- cgit v1.2.1 From cce2862dcd7df9f3ed14f8c8277e9ba3fd768d98 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 28 Jun 2000 14:48:01 +0000 Subject: typos fixed by Rob Hooft --- cmd.py | 2 +- command/build_ext.py | 4 ++-- util.py | 2 +- version.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd.py b/cmd.py index c9b5624f..d450ad32 100644 --- a/cmd.py +++ b/cmd.py @@ -323,7 +323,7 @@ class Command: should be disabled by the "dry run" flag, and should announce themselves if the current verbosity level is high enough. This method takes care of all that bureaucracy for you; all you have to - do is supply the funtion to call and an argument tuple for it (to + do is supply the function to call and an argument tuple for it (to embody the "external action" being performed), a message to print if the verbosity level is high enough, and an optional verbosity threshold. diff --git a/command/build_ext.py b/command/build_ext.py index adf85d32..54d48467 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -1,7 +1,7 @@ """distutils.command.build_ext Implements the Distutils 'build_ext' command, for building extension -modules (currently limited to C extensions, should accomodate C++ +modules (currently limited to C extensions, should accommodate C++ extensions ASAP).""" # created 1999/08/09, Greg Ward @@ -385,7 +385,7 @@ class build_ext (Command): # Next, compile the source code to object files. # XXX not honouring 'define_macros' or 'undef_macros' -- the - # CCompiler API needs to change to accomodate this, and I + # CCompiler API needs to change to accommodate this, and I # want to do one thing at a time! # Two possible sources for extra compiler arguments: diff --git a/util.py b/util.py index 5c1de789..ebfdf0ab 100644 --- a/util.py +++ b/util.py @@ -28,7 +28,7 @@ else: return os.path.normpath(path) -# More backwards compatability hacks +# More backwards compatibility hacks def extend (list, new_list): """Appends the list 'new_list' to 'list', just like the 'extend()' list method does in Python 1.5.2 -- but this works on earlier diff --git a/version.py b/version.py index 918a1ded..8b9ef106 100644 --- a/version.py +++ b/version.py @@ -207,7 +207,7 @@ class StrictVersion (Version): # provides enough benefit to be worth using, and will submit their # version numbering scheme to its domination. The free-thinking # anarchists in the lot will never give in, though, and something needs -# to be done to accomodate them. +# to be done to accommodate them. # # Perhaps a "moderately strict" version class could be implemented that # lets almost anything slide (syntactically), and makes some heuristic -- cgit v1.2.1 From 78d7f6ae8b4d39977f5bc4efc043a136d80e9c96 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 02:06:29 +0000 Subject: Fixed 'findall()' so it only returns regular files -- no directories. Changed 'prune_file_list()' so it also prunes out RCS and CVS directories. Added 'is_regex' parameter to 'select_pattern()', 'exclude_pattern()', and 'translate_pattern()', so that you don't have to be constrained by the simple shell-glob-like pattern language, and can escape into full-blown regexes when needed. Currently this is only available in code -- it's not exposed in the manifest template mini-language. Added 'prune' option (controlled by --prune and --no-prune) to determine whether we call 'prune_file_list()' or not -- it's true by default. Fixed 'negative_opt' -- it was misnamed and not being seen by dist.py. Added --no-defaults to the option table, so it's seen by FancyGetopt. --- command/sdist.py | 72 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 5627ebb9..6f867e89 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -46,6 +46,14 @@ class sdist (Command): ('use-defaults', None, "include the default file set in the manifest " "[default; disable with --no-defaults]"), + ('no-defaults', None, + "don't include the default file set"), + ('prune', None, + "specifically exclude files/directories that should not be " + "distributed (build tree, RCS/CVS dirs, etc.) " + "[default; disable with --no-prune]"), + ('no-prune', None, + "don't automatically exclude anything"), ('manifest-only', 'o', "just regenerate the manifest and then stop " "(implies --force-manifest)"), @@ -64,7 +72,8 @@ class sdist (Command): "list available distribution formats", show_formats), ] - negative_opts = {'use-defaults': 'no-defaults'} + negative_opt = {'no-defaults': 'use-defaults', + 'no-prune': 'prune' } default_format = { 'posix': 'gztar', 'nt': 'zip' } @@ -78,6 +87,7 @@ class sdist (Command): # 'use_defaults': if true, we will include the default file set # in the manifest self.use_defaults = 1 + self.prune = 1 self.manifest_only = 0 self.force_manifest = 0 @@ -217,8 +227,10 @@ class sdist (Command): if template_exists: self.read_template () - # Prune away the build and source distribution directories - self.prune_file_list() + # Prune away any directories that don't belong in the source + # distribution + if self.prune: + self.prune_file_list() # File list now complete -- sort it so that higher-level files # come first @@ -509,18 +521,21 @@ class sdist (Command): def prune_file_list (self): """Prune off branches that might slip into the file list as created - by 'read_template()', but really don't belong there: specifically, - the build tree (typically "build") and the release tree itself - (only an issue if we ran "sdist" previously with --keep-tree, or it - aborted). + by 'read_template()', but really don't belong there: + * the build tree (typically "build") + * the release tree itself (only an issue if we ran "sdist" + previously with --keep-tree, or it aborted) + * any RCS or CVS directories """ build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() self.exclude_pattern (self.files, None, prefix=build.build_base) self.exclude_pattern (self.files, None, prefix=base_dir) + self.exclude_pattern (self.files, r'/(RCS|CVS)/.*', is_regex=1) - def select_pattern (self, files, pattern, anchor=1, prefix=None): + def select_pattern (self, files, pattern, + anchor=1, prefix=None, is_regex=0): """Select strings (presumably filenames) from 'files' that match 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not quite the same as implemented by the 'fnmatch' module: '*' and '?' @@ -536,10 +551,15 @@ class sdist (Command): (itself a pattern) and ending with 'pattern', with anything in between them, will match. 'anchor' is ignored in this case. + If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and + 'pattern' is assumed to be either a string containing a regex or a + regex object -- no translation is done, the regex is just compiled + and used as-is. + Return the list of matching strings, possibly empty. """ matches = [] - pattern_re = translate_pattern (pattern, anchor, prefix) + pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) self.debug_print("select_pattern: applying regex r'%s'" % pattern_re.pattern) for name in files: @@ -552,13 +572,14 @@ class sdist (Command): # select_pattern () - def exclude_pattern (self, files, pattern, anchor=1, prefix=None): + def exclude_pattern (self, files, pattern, + anchor=1, prefix=None, is_regex=0): """Remove strings (presumably filenames) from 'files' that match - 'pattern'. 'pattern', 'anchor', 'and 'prefix' are the same - as for 'select_pattern()', above. The list 'files' is modified - in place. + 'pattern'. Other parameters are the same as for + 'select_pattern()', above. The list 'files' is modified in place. """ - pattern_re = translate_pattern (pattern, anchor, prefix) + + pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) self.debug_print("exclude_pattern: applying regex r'%s'" % pattern_re.pattern) for i in range (len(files)-1, -1, -1): @@ -674,6 +695,8 @@ def findall (dir = os.curdir): """Find all files under 'dir' and return the list of full filenames (relative to 'dir'). """ + from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK + list = [] stack = [dir] pop = stack.pop @@ -688,8 +711,13 @@ def findall (dir = os.curdir): fullname = os.path.join (dir, name) else: fullname = name - list.append (fullname) - if os.path.isdir (fullname) and not os.path.islink(fullname): + + # Avoid excess stat calls -- just one will do, thank you! + stat = os.stat(fullname) + mode = stat[ST_MODE] + if S_ISREG(mode): + list.append (fullname) + elif S_ISDIR(mode) and not S_ISLNK(mode): push (fullname) return list @@ -716,10 +744,18 @@ def glob_to_re (pattern): # glob_to_re () -def translate_pattern (pattern, anchor=1, prefix=None): +def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): """Translate a shell-like wildcard pattern to a compiled regular - expression. Return the compiled regex. + expression. Return the compiled regex. If 'is_regex' true, + then 'pattern' is directly compiled to a regex (if it's a string) + or just returned as-is (assumes it's a regex object). """ + if is_regex: + if type(pattern) is StringType: + return re.compile(pattern) + else: + return pattern + if pattern: pattern_re = glob_to_re (pattern) else: -- cgit v1.2.1 From 0350ebe9b29608aada89136c1db880c4f3552330 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 02:16:24 +0000 Subject: Fixed so 'get_source_files()' calls 'check_extension_list()' -- that way, we can run "sdist" on a distribution with old-style extension structures even if we haven't built it yet. Bug spotted by Harry Gebel. --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 54d48467..9bb7e770 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -309,7 +309,7 @@ class build_ext (Command): def get_source_files (self): - + self.check_extension_list() filenames = [] # Wouldn't it be neat if we knew the names of header files too... -- cgit v1.2.1 From 41192ad621b245fd3985b4191348ab27cab4360d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 22:57:55 +0000 Subject: Cleaned up and reformatted by Rene Liebscher. More reformatting by me. Also added some editorial comments. --- cygwinccompiler.py | 208 +++++++++++++++++++++++++++++------------------------ 1 file changed, 113 insertions(+), 95 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index cc2ed5db..7d43f02f 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -1,48 +1,55 @@ """distutils.cygwinccompiler -Contains the CygwinCCompiler class, a subclass of UnixCCompiler that handles -the Gnu Win32 C compiler. -It also contains the Mingw32CCompiler class which handles the mingw32 compiler -(same as cygwin in no-cygwin mode.) - +Provides the CygwinCCompiler class, a subclass of UnixCCompiler that +handles the Cygwin port of the GNU C compiler to Windows. It also contains +the Mingw32CCompiler class which handles the mingw32 port of GCC (same as +cygwin in no-cygwin mode). """ # created 2000/05/05, Rene Liebscher __revision__ = "$Id$" -import os,sys,string,tempfile +import os,sys,string from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler -# Because these compilers aren't configured in Python's config.h file by default -# we should at least warn the user if he used this unmodified version. -def check_if_config_h_is_gcc_ready(): - """ checks, if the gcc-compiler is mentioned in config.h - if it is not, compiling probably doesn't work """ - from distutils import sysconfig - import string,sys +# Because these compilers aren't configured in Python's config.h file by +# default we should at least warn the user if he is using a unmodified +# version. + +def check_config_h(): + """Checks if the GCC compiler is mentioned in config.h. If it is not, + compiling probably doesn't work, so print a warning to stderr. + """ + + # XXX the result of the check should be returned! + + from distutils import sysconfig + import string,sys + try: + # It would probably better to read single lines to search. + # But we do this only once, and it is fast enough + f=open(sysconfig.get_config_h_filename()) + s=f.read() + f.close() try: - # It would probably better to read single lines to search. - # But we do this only once, and it is fast enough - f=open(sysconfig.get_config_h_filename()) - s=f.read() - f.close() - try: - string.index(s,"__GNUC__") # is somewhere a #ifdef __GNUC__ or something similar - except: - sys.stderr.write ("warning: Python's config.h doesn't seem to support your compiler.\n") - except: # unspecific error => ignore - pass + # is somewhere a #ifdef __GNUC__ or something similar + string.index(s,"__GNUC__") + except ValueError: + sys.stderr.write ("warning: "+ + "Python's config.h doesn't seem to support your compiler.\n") + except IOError: + # if we can't read this file, we cannot say it is wrong + # the compiler will complain later about this file as missing + pass # This is called when the module is imported, so we make this check only once -check_if_config_h_is_gcc_ready() +# XXX why not make it only when the compiler is needed? +check_config_h() -# XXX Things not currently handled: -# * see UnixCCompiler - class CygwinCCompiler (UnixCCompiler): compiler_type = 'cygwin' @@ -54,21 +61,22 @@ class CygwinCCompiler (UnixCCompiler): UnixCCompiler.__init__ (self, verbose, dry_run, force) - # our compiler uses other names - self.cc='gcc' - self.ld_shared='dllwrap' - self.ldflags_shared=[] - - # some variables to manage the differences between cygwin and mingw32 - self.dllwrap_options=["--target=i386-cygwin32"] - # specification of entry point is not necessary - - self.dll_additional_libraries=[ - # cygwin shouldn't need msvcrt, but without the dll's will crash - # perhaps something about initialization (Python uses it, too) + # Hard-code GCC because that's what this is all about. + # XXX optimization, warnings etc. should be customizable. + self.set_executables(compiler='gcc -O -Wall', + compiler_so='gcc -O -Wall', + linker_exe='gcc', + linker_so='dllwrap --target=i386-cygwin32') + + # cygwin and mingw32 need different sets of libraries + self.dll_libraries=[ + # cygwin shouldn't need msvcrt, + # but without the dll's will crash + # ( gcc version 2.91.57 ) + # perhaps something about initialization # mingw32 needs it in all cases - "msvcrt" - ] + "msvcrt" + ] # __init__ () @@ -80,79 +88,88 @@ class CygwinCCompiler (UnixCCompiler): library_dirs=None, runtime_library_dirs=None, export_symbols=None, - debug=0, + debug=0, extra_preargs=None, - extra_postargs=None): + extra_postargs=None, + build_temp=None): - if libraries==None: - libraries=[] + if libraries == None: + libraries = [] - python_library=["python"+str(sys.hexversion>>24)+str((sys.hexversion>>16)&0xff)] - libraries=libraries+python_library+self.dll_additional_libraries - - # if you don't need the def-file afterwards, it is - # better to use for it tempfile.mktemp() as its name - # (unix-style compilers don't like backslashes in filenames) - win_dll_def_file=string.replace(tempfile.mktemp(),"\\","/") - #win_dll_def_file=output_filename[:-len(self.shared_lib_extension)]+".def" - #win_dll_exp_file=output_filename[:-len(self.shared_lib_extension)]+".exp" - #win_dll_lib_file=output_filename[:-len(self.shared_lib_extension)]+".a" + # Additional libraries: the python library is always needed on + # Windows we need the python version without the dot, eg. '15' + + pythonlib = ("python%d%d" % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + libraries.append(pythonlib) + libraries.extend(self.dll_libraries) + + # name of extension + + # XXX WRONG WRONG WRONG + # this is NOT the place to make guesses about Python namespaces; + # that MUST be done in build_ext.py + + if not debug: + ext_name = os.path.basename(output_filename)[:-len(".pyd")] + else: + ext_name = os.path.basename(output_filename)[:-len("_d.pyd")] + + def_file = os.path.join(build_temp, ext_name + ".def") + #exp_file = os.path.join(build_temp, ext_name + ".exp") + #lib_file = os.path.join(build_temp, 'lib' + ext_name + ".a") # Make .def file - # (It would probably better to check if we really need this, but for this we had to - # insert some unchanged parts of UnixCCompiler, and this is not what I want.) - f=open(win_dll_def_file,"w") + # (It would probably better to check if we really need this, + # but for this we had to insert some unchanged parts of + # UnixCCompiler, and this is not what we want.) + f = open(def_file,"w") f.write("EXPORTS\n") # intro - # always export a function "init"+module_name - if not debug: - f.write("init"+os.path.basename(output_filename)[:-len(self.shared_lib_extension)]+"\n") - else: # in debug mode outfile_name is something like XXXXX_d.pyd - f.write("init"+os.path.basename(output_filename)[:-(len(self.shared_lib_extension)+2)]+"\n") - # if there are more symbols to export - # insert code here to write them in f - if export_symbols!=None: + if export_symbols == None: + # export a function "init" + ext_name + f.write("init" + ext_name + "\n") + else: + # if there are more symbols to export write them into f for sym in export_symbols: - f.write(sym+"\n") + f.write(sym+"\n") f.close() - if extra_preargs==None: - extra_preargs=[] + if extra_preargs == None: + extra_preargs = [] - extra_preargs=extra_preargs+[ + extra_preargs = extra_preargs + [ #"--verbose", - #"--output-exp",win_dll_exp_file, - #"--output-lib",win_dll_lib_file, - "--def",win_dll_def_file - ]+ self.dllwrap_options + #"--output-exp",exp_file, + #"--output-lib",lib_file, + "--def",def_file + ] - # who wants symbols and a many times greater output file + # who wants symbols and a many times larger output file # should explicitely switch the debug mode on - # otherwise we let dllwrap strip the outputfile - # (On my machine unstripped_file=stripped_file+254KB + # otherwise we let dllwrap strip the output file + # (On my machine unstripped_file = stripped_file + 254KB # 10KB < stripped_file < ??100KB ) if not debug: - extra_preargs=extra_preargs+["-s"] - - try: - UnixCCompiler.link_shared_object(self, + extra_preargs = extra_preargs + ["-s"] + + UnixCCompiler.link_shared_object(self, objects, output_filename, output_dir, libraries, library_dirs, runtime_library_dirs, - None, # export_symbols, we do this with our def-file + None, # export_symbols, we do this with our def-file debug, extra_preargs, - extra_postargs) - finally: - # we don't need the def-file anymore - os.remove(win_dll_def_file) + extra_postargs, + build_temp) # link_shared_object () # class CygwinCCompiler + # the same as cygwin plus some additional parameters class Mingw32CCompiler (CygwinCCompiler): @@ -165,14 +182,15 @@ class Mingw32CCompiler (CygwinCCompiler): CygwinCCompiler.__init__ (self, verbose, dry_run, force) - self.ccflags = self.ccflags + ["-mno-cygwin"] - self.dllwrap_options=[ - # mingw32 doesn't really need 'target' - # and cygwin too (it seems, it is enough - # to specify a different entry point) - #"--target=i386-mingw32", - "--entry","_DllMain@12" - ] + self.set_executables(compiler='gcc -mno-cygwin -O -Wall', + compiler_so='gcc -mno-cygwin -O -Wall', + linker_exe='gcc -mno-cygwin', + linker_so='dllwrap' + + ' --target=i386-mingw32' + + ' --entry _DllMain@12') + # mingw32 doesn't really need 'target' and cygwin too (it seems, + # it is enough to specify a different entry point) + # no additional libraries need # (only msvcrt, which is already added by CygwinCCompiler) -- cgit v1.2.1 From ac971ce5217faf3cac2d2b9bf69d347dac0ca08c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 22:59:10 +0000 Subject: Changed to use _winreg module instead of winreg. --- msvccompiler.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index db8bd35a..6acd4efd 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -20,14 +20,14 @@ from distutils.ccompiler import \ _can_read_reg = 0 try: - import winreg + import _winreg _can_read_reg = 1 - hkey_mod = winreg + hkey_mod = _winreg - RegOpenKeyEx = winreg.OpenKeyEx - RegEnumKey = winreg.EnumKey - RegEnumValue = winreg.EnumValue - RegError = winreg.error + RegOpenKeyEx = _winreg.OpenKeyEx + RegEnumKey = _winreg.EnumKey + RegEnumValue = _winreg.EnumValue + RegError = _winreg.error except ImportError: try: -- cgit v1.2.1 From 9d71d05b4198ad054d20534d5714d1a1a8ce8877 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 23:04:59 +0000 Subject: On second thought, first try for _winreg, and then winreg. Only if both fail do we try for win32api/win32con. If *those* both fail, then we don't have registry access. Phew! --- msvccompiler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index 6acd4efd..ae5e2d76 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -20,7 +20,11 @@ from distutils.ccompiler import \ _can_read_reg = 0 try: - import _winreg + try: + import _winreg + except ImportError: + import winreg # for pre-2000/06/29 CVS Python + _can_read_reg = 1 hkey_mod = _winreg -- cgit v1.2.1 From 6165d95cb15b50fb4e20dd91ae8879229e231f06 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 23:09:20 +0000 Subject: Don't try to guess the name of a .def file -- if one is supplied, use it, otherwise just generate an '/export:' option. --- command/build_ext.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 9bb7e770..4b5aaab8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -528,12 +528,6 @@ class build_ext (Command): # kludges; they are to be avoided if possible!) def_file = ext.export_symbol_file - if def_file is None: - source_dir = os.path.dirname (sources[0]) - ext_base = (string.split (ext.name, '.'))[-1] - def_file = os.path.join (source_dir, "%s.def" % ext_base) - if not os.path.exists (def_file): - def_file = None if def_file is not None: extra_args.append ('/DEF:' + def_file) -- cgit v1.2.1 From 734a9d18a8887ba73bcba4bf355a7fd271b2a7d9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 23:50:19 +0000 Subject: Allow 2.0 on the list of target versions. NB. this isn't enough: the GUI part, misc/install.c, still needs to be updated, and it looks like a non-trivial change. --- command/bdist_wininst.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index d3d17721..b81c4d41 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -27,7 +27,7 @@ class bdist_wininst (Command): "compile to .pyo on the target system"), ('target-version=', 'v', "require a specific python version" + - " on the target system (1.5 or 1.6)"), + " on the target system (1.5 or 1.6/2.0)"), ] def initialize_options (self): @@ -47,9 +47,9 @@ class bdist_wininst (Command): if not self.target_version: self.target_version = "" else: - if not self.target_version in ("1.5", "1.6"): + if not self.target_version in ("1.5", "1.6", "2.0"): raise DistutilsOptionError ( - "target version must be 1.5 or 1.6") + "target version must be 1.5, 1.6, or 2.0") if self.distribution.has_ext_modules(): short_version = sys.version[:3] if self.target_version and self.target_version != short_version: @@ -74,8 +74,8 @@ class bdist_wininst (Command): # The packager can choose if .pyc and .pyo files should be created # on the TARGET system instead at the SOURCE system. -## # The compilation can only be done on the SOURCE system -## # for one python version (assuming 1.6 and 1.5 have incompatible +## # The compilation can only be done on the SOURCE system for one +## # python version (assuming 1.6/2.0 and 1.5 have incompatible ## # byte-codes). ## short_version = sys.version[:3] ## if self.target_version == short_version: -- cgit v1.2.1 From 67211d1e7cc101f970c3053015b8da87d889de28 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 30 Jun 2000 02:54:36 +0000 Subject: Bump version to 0.9. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2ba82bd5..ded0899c 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.9pre" +__version__ = "0.9" -- cgit v1.2.1 From a951e20878ee09dfd00a555b89ca61fabe4f79ff Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 30 Jun 2000 19:37:59 +0000 Subject: Simplify the registry-module-finding code: _winreg or win32api/win32con. This'll work fine with 2.0 or 1.5.2, but is less than ideal for 1.6a1/a2. But the code to accomodate 1.6a1/a2 was released with Distutils 0.9, so it can go away now. --- msvccompiler.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index ae5e2d76..e58e6c10 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -20,10 +20,7 @@ from distutils.ccompiler import \ _can_read_reg = 0 try: - try: - import _winreg - except ImportError: - import winreg # for pre-2000/06/29 CVS Python + import _winreg _can_read_reg = 1 hkey_mod = _winreg -- cgit v1.2.1 From 07f383457ff22083cc4b09c58be345d5d09b80ea Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 5 Jul 2000 03:06:46 +0000 Subject: Added the --dist-dir option to control where the archive(s) are put; defaults to 'dist' (ie. no longer in the distribution root). --- command/sdist.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index 6f867e89..2d31640f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -64,6 +64,9 @@ class sdist (Command): ('keep-tree', 'k', "keep the distribution tree around after creating " + "archive file(s)"), + ('dist-dir=', 'd', + "directory to put the source distribution archive(s) in " + "[default: dist]"), ] @@ -94,6 +97,7 @@ class sdist (Command): self.formats = None self.keep_tree = 0 + self.dist_dir = None self.archive_files = None @@ -118,6 +122,9 @@ class sdist (Command): raise DistutilsOptionError, \ "unknown archive format '%s'" % bad_format + if self.dist_dir is None: + self.dist_dir = "dist" + def run (self): @@ -667,11 +674,14 @@ class sdist (Command): # Don't warn about missing meta-data here -- should be (and is!) # done elsewhere. base_dir = self.distribution.get_fullname() + base_name = os.path.join(self.dist_dir, base_dir) self.make_release_tree (base_dir, self.files) archive_files = [] # remember names of files we create + if self.dist_dir: + self.mkpath(self.dist_dir) for fmt in self.formats: - file = self.make_archive (base_dir, fmt, base_dir=base_dir) + file = self.make_archive (base_name, fmt, base_dir=base_dir) archive_files.append(file) self.archive_files = archive_files -- cgit v1.2.1 From 0b393c97f576061b8565a7b373788f7046f28148 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 5 Jul 2000 03:07:18 +0000 Subject: Added the --dist-dir option that the "bdist_*" will use to control where they place their output files. --- command/bdist.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/command/bdist.py b/command/bdist.py index 3fcbf779..100563a9 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -34,6 +34,9 @@ class bdist (Command): "temporary directory for creating built distributions"), ('formats=', None, "formats for distribution (comma-separated list)"), + ('dist-dir=', 'd', + "directory to put final built distributions in " + "[default: dist]"), ] help_options = [ @@ -65,6 +68,7 @@ class bdist (Command): def initialize_options (self): self.bdist_base = None self.formats = None + self.dist_dir = None # initialize_options() @@ -86,6 +90,9 @@ class bdist (Command): raise DistutilsPlatformError, \ "don't know how to create built distributions " + \ "on platform %s" % os.name + + if self.dist_dir is None: + self.dist_dir = "dist" # finalize_options() -- cgit v1.2.1 From 8efd9f49e8f828de05c505aed52f32b17a9a91ad Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 5 Jul 2000 03:07:37 +0000 Subject: Added --dist-dir option to control where output archive(s) go. --- command/bdist_dumb.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index f9c81fc2..805aa0a9 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -24,6 +24,8 @@ class bdist_dumb (Command): ('keep-tree', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), ] default_format = { 'posix': 'gztar', @@ -34,6 +36,7 @@ class bdist_dumb (Command): self.bdist_dir = None self.format = None self.keep_tree = 0 + self.dist_dir = None # initialize_options() @@ -51,6 +54,8 @@ class bdist_dumb (Command): ("don't know how to create dumb built distributions " + "on platform %s") % os.name + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + # finalize_options() @@ -71,7 +76,8 @@ class bdist_dumb (Command): get_platform()) print "self.bdist_dir = %s" % self.bdist_dir print "self.format = %s" % self.format - self.make_archive (archive_basename, self.format, + self.make_archive (os.path.join(self.dist_dir, archive_basename), + self.format, root_dir=self.bdist_dir) if not self.keep_tree: -- cgit v1.2.1 From 93518e1e37741f335e5aa118799826d5e6b5abbc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 5 Jul 2000 03:08:55 +0000 Subject: Fixed so the ZIP file (which is bundled into an executable) goes in the temporary directory ('bdist_base'). Added --dist-dir option to control where the executable is put. --- command/bdist_wininst.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index b81c4d41..0c53bf9e 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -28,6 +28,8 @@ class bdist_wininst (Command): ('target-version=', 'v', "require a specific python version" + " on the target system (1.5 or 1.6/2.0)"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), ] def initialize_options (self): @@ -36,6 +38,7 @@ class bdist_wininst (Command): self.target_compile = 0 self.target_optimize = 0 self.target_version = None + self.dist_dir = None # initialize_options() @@ -57,6 +60,8 @@ class bdist_wininst (Command): short_version) self.target_version = short_version + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + # finalize_options() @@ -92,7 +97,10 @@ class bdist_wininst (Command): # And make an archive relative to the root of the # pseudo-installation tree. - archive_basename = "%s.win32" % self.distribution.get_fullname() + fullname = self.distribution.get_fullname() + archive_basename = os.path.join(self.bdist_dir, + "%s.win32" % fullname) + # XXX hack! Our archive MUST be relative to sys.prefix # XXX What about .install_data, .install_scripts, ...? # [Perhaps require that all installation dirs be under sys.prefix @@ -103,7 +111,7 @@ class bdist_wininst (Command): root_dir = install.install_lib arcname = self.make_archive (archive_basename, "zip", root_dir=root_dir) - self.create_exe (arcname) + self.create_exe (arcname, fullname) if not self.keep_tree: remove_tree (self.bdist_dir, self.verbose, self.dry_run) @@ -156,7 +164,7 @@ class bdist_wininst (Command): # create_inifile() - def create_exe (self, arcname): + def create_exe (self, arcname, fullname): import struct, zlib cfgdata = open (self.create_inifile()).read() @@ -165,7 +173,8 @@ class bdist_wininst (Command): co = zlib.compressobj (zlib.Z_DEFAULT_COMPRESSION, comp_method, -15) zcfgdata = co.compress (cfgdata) + co.flush() - installer_name = "%s.win32.exe" % self.distribution.get_fullname() + installer_name = os.path.join(self.dist_dir, + "%s.win32.exe" % fullname) self.announce ("creating %s" % installer_name) file = open (installer_name, "wb") -- cgit v1.2.1 From 9f4f35f3c5fe3f538355af050e65e951997b2dfb Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Fri, 7 Jul 2000 20:45:21 +0000 Subject: fix inconsistent use of tabs and spaces --- ccompiler.py | 2 +- command/build_ext.py | 10 +++++----- command/install_headers.py | 2 +- dist.py | 34 +++++++++++++++++----------------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 20a5191b..933060ae 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -828,7 +828,7 @@ def show_compilers(): from distutils.fancy_getopt import FancyGetopt compilers = [] for compiler in compiler_class.keys(): - compilers.append(("compiler="+compiler, None, + compilers.append(("compiler="+compiler, None, compiler_class[compiler][2])) compilers.sort() pretty_printer = FancyGetopt(compilers) diff --git a/command/build_ext.py b/command/build_ext.py index 4b5aaab8..6456ad49 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -370,12 +370,12 @@ class build_ext (Command): ext_filename = os.path.join (self.build_lib, self.get_ext_filename(fullname)) - if not (self.force or newer_group(sources, ext_filename, 'newer')): - self.announce ("skipping '%s' extension (up-to-date)" % + if not (self.force or newer_group(sources, ext_filename, 'newer')): + self.announce ("skipping '%s' extension (up-to-date)" % ext.name) - continue # 'for' loop over all extensions - else: - self.announce ("building '%s' extension" % ext.name) + continue # 'for' loop over all extensions + else: + self.announce ("building '%s' extension" % ext.name) # First, scan the sources for SWIG definition files (.i), run # SWIG on 'em to create .c files, and modify the sources list diff --git a/command/install_headers.py b/command/install_headers.py index 991985b4..2e6ce869 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -22,7 +22,7 @@ class install_headers (Command): def initialize_options (self): self.install_dir = None - self.outfiles = [] + self.outfiles = [] def finalize_options (self): self.set_undefined_options('install', diff --git a/dist.py b/dist.py index bb476065..5f81c887 100644 --- a/dist.py +++ b/dist.py @@ -275,13 +275,13 @@ class Distribution: # What to call the per-user config file if os.name == 'posix': - user_filename = ".pydistutils.cfg" - else: - user_filename = "pydistutils.cfg" + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" # And look for the user config file - if os.environ.has_key('HOME'): - user_file = os.path.join(os.environ.get('HOME'), user_filename) + if os.environ.has_key('HOME'): + user_file = os.path.join(os.environ.get('HOME'), user_filename) if os.path.isfile(user_file): files.append(user_file) @@ -439,7 +439,7 @@ class Distribution: # Check for help_options in command class. They have a different # format (tuple of four) so we need to preprocess them here. - if (hasattr(cmd_class, 'help_options') and + if (hasattr(cmd_class, 'help_options') and type (cmd_class.help_options) is ListType): help_options = fix_help_options(cmd_class.help_options) else: @@ -457,17 +457,17 @@ class Distribution: self._show_help(parser, display_options=0, commands=[cmd_class]) return - if (hasattr(cmd_class, 'help_options') and + if (hasattr(cmd_class, 'help_options') and type (cmd_class.help_options) is ListType): - help_option_found=0 - for (help_option, short, desc, func) in cmd_class.help_options: - if hasattr(opts, parser.get_attr_name(help_option)): - help_option_found=1 + help_option_found=0 + for (help_option, short, desc, func) in cmd_class.help_options: + if hasattr(opts, parser.get_attr_name(help_option)): + help_option_found=1 #print "showing help for option %s of command %s" % \ # (help_option[0],cmd_class) if callable(func): - func() + func() else: raise DistutilsClassError, \ ("invalid help function %s for help option '%s': " @@ -475,7 +475,7 @@ class Distribution: (`func`, help_option) if help_option_found: - return + return # Put the options from the command-line into their official # holding pen, the 'command_options' dictionary. @@ -526,12 +526,12 @@ class Distribution: klass = command else: klass = self.get_command_class (command) - if (hasattr(klass, 'help_options') and + if (hasattr(klass, 'help_options') and type (klass.help_options) is ListType): - parser.set_option_table (klass.user_options + + parser.set_option_table (klass.user_options + fix_help_options(klass.help_options)) - else: - parser.set_option_table (klass.user_options) + else: + parser.set_option_table (klass.user_options) parser.print_help ("Options for '%s' command:" % klass.__name__) print -- cgit v1.2.1 From e522e80e068bcf976636e4454d1e6e127436f551 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 14 Jul 2000 13:35:07 +0000 Subject: Typo fix from Bastian Kleineidam --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 6456ad49..37d19c7d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -309,7 +309,7 @@ class build_ext (Command): def get_source_files (self): - self.check_extension_list() + self.check_extensions_list(self.extensions) filenames = [] # Wouldn't it be neat if we knew the names of header files too... -- cgit v1.2.1 From ac99c62e48886255ec93847fec136b5b81d9122e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Jul 2000 01:21:54 +0000 Subject: Typo fix from David Ascher. --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 37d19c7d..2ede447f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -292,7 +292,7 @@ class build_ext (Command): ext.undef_macros = [] for macro in macros: if not (type(macro) is TupleType and - 1 <= len(macros) <= 2): + 1 <= len(macro) <= 2): raise DistutilsSetupError, \ ("'macros' element of build info dict " "must be 1- or 2-tuple") -- cgit v1.2.1 From 20550ceda600efdce508bd686c887ce88b2c09e9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Jul 2000 01:23:19 +0000 Subject: Fix to call 'library_filename()' instead of the non-existent 'shared_library_filename()'. --- unixccompiler.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 4772e1a8..799560c3 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -205,7 +205,7 @@ class UnixCCompiler (CCompiler): self.link_shared_object ( objects, - self.shared_library_filename (output_libname), + self.library_filename (output_libname, lib_type='shared'), output_dir, libraries, library_dirs, @@ -320,8 +320,10 @@ class UnixCCompiler (CCompiler): def find_library_file (self, dirs, lib): for dir in dirs: - shared = os.path.join (dir, self.shared_library_filename (lib)) - static = os.path.join (dir, self.library_filename (lib)) + shared = os.path.join ( + dir, self.library_filename (lib, lib_type='shared')) + static = os.path.join ( + dir, self.library_filename (lib, lib_type='static')) # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm -- cgit v1.2.1 From ba1b7bf236f18fb1997ae789505856bb315c8f37 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Jul 2000 01:58:45 +0000 Subject: Remove unused 'search_dir()' method. Comment tweak. --- command/sdist.py | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 2d31640f..3b91170c 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -319,26 +319,6 @@ class sdist (Command): # add_defaults () - def search_dir (self, dir, pattern=None): - """Recursively find files under 'dir' matching 'pattern' (a string - containing a Unix-style glob pattern). If 'pattern' is None, find - all files under 'dir'. Return the list of found filenames. - """ - allfiles = findall (dir) - if pattern is None: - return allfiles - - pattern_re = translate_pattern (pattern) - files = [] - for file in allfiles: - if pattern_re.match (os.path.basename (file)): - files.append (file) - - return files - - # search_dir () - - def recursive_exclude_pattern (self, dir, pattern=None): """Remove filenames from 'self.files' that are under 'dir' and whose basenames match 'pattern'. @@ -717,7 +697,7 @@ def findall (dir = os.curdir): names = os.listdir (dir) for name in names: - if dir != os.curdir: # avoid the dreaded "./" syndrome + if dir != os.curdir: # avoid leading "./" fullname = os.path.join (dir, name) else: fullname = name -- cgit v1.2.1 From 01105a3143f4096e4916f9fd95e219dc9d6d3fde Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Jul 2000 02:13:20 +0000 Subject: Fixed a grab-bag of typos spotted by Rob Hooft. --- ccompiler.py | 2 +- cmd.py | 2 +- command/bdist_rpm.py | 2 +- command/build_ext.py | 2 +- command/build_scripts.py | 2 +- command/install_data.py | 2 +- cygwinccompiler.py | 2 +- dist.py | 4 ++-- util.py | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 933060ae..eb6200f6 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -304,7 +304,7 @@ class CCompiler: def add_link_object (self, object): """Add 'object' to the list of object files (or analogues, such as - explictly named library files or the output of "resource + explicitly named library files or the output of "resource compilers") to be included in every link driven by this compiler object. """ diff --git a/cmd.py b/cmd.py index d450ad32..8beb5d44 100644 --- a/cmd.py +++ b/cmd.py @@ -55,7 +55,7 @@ class Command: # commands fallback on the Distribution's behaviour. None means # "not defined, check self.distribution's copy", while 0 or 1 mean # false and true (duh). Note that this means figuring out the real - # value of each flag is a touch complicatd -- hence "self.verbose" + # value of each flag is a touch complicated -- hence "self.verbose" # (etc.) will be handled by __getattr__, below. self._verbose = None self._dry_run = None diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 7f58a1da..3302eea2 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -400,7 +400,7 @@ class bdist_rpm (Command): ] for (rpm_opt, attr, default) in script_options: - # Insert contents of file refered to, if no file is refered to + # Insert contents of file referred to, if no file is refered to # use 'default' as contents of script val = getattr(self, attr) if val or default: diff --git a/command/build_ext.py b/command/build_ext.py index 2ede447f..c04036b0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -394,7 +394,7 @@ class build_ext (Command): # elegant, but people seem to expect it and I # guess it's useful) # The environment variable should take precedence, and - # any sensible compiler will give precendence to later + # any sensible compiler will give precedence to later # command line args. Hence we combine them in order: extra_args = ext.extra_compile_args or [] diff --git a/command/build_scripts.py b/command/build_scripts.py index 6467e65b..17fae8f7 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -46,7 +46,7 @@ class build_scripts (Command): """Copy each script listed in 'self.scripts'; if it's marked as a Python script in the Unix way (first line matches 'first_line_re', ie. starts with "\#!" and contains "python"), then adjust the first - line to refer to the current Python intepreter as we copy. + line to refer to the current Python interpreter as we copy. """ outfiles = [] self.mkpath(self.build_dir) diff --git a/command/install_data.py b/command/install_data.py index 716febb5..9193f919 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -18,7 +18,7 @@ class install_data (Command): user_options = [ ('install-dir=', 'd', - "base directory for installating data files " + "base directory for installing data files " "(default: installation base dir)"), ('root=', None, "install everything relative to this alternate root directory"), diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 7d43f02f..650627f5 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -145,7 +145,7 @@ class CygwinCCompiler (UnixCCompiler): ] # who wants symbols and a many times larger output file - # should explicitely switch the debug mode on + # should explicitly switch the debug mode on # otherwise we let dllwrap strip the output file # (On my machine unstripped_file = stripped_file + 254KB # 10KB < stripped_file < ??100KB ) diff --git a/dist.py b/dist.py index 5f81c887..ed829fe4 100644 --- a/dist.py +++ b/dist.py @@ -167,7 +167,7 @@ class Distribution: # It's only safe to query 'have_run' for a command class that has # been instantiated -- a false value will be inserted when the # command object is created, and replaced with a true value when - # the command is succesfully run. Thus it's probably best to use + # the command is successfully run. Thus it's probably best to use # '.get()' rather than a straight lookup. self.have_run = {} @@ -677,7 +677,7 @@ class Distribution: def get_command_obj (self, command, create=1): """Return the command object for 'command'. Normally this object - is cached on a previous call to 'get_command_obj()'; if no comand + is cached on a previous call to 'get_command_obj()'; if no command object for 'command' is in the cache, then we either create and return it (if 'create' is true) or return None. """ diff --git a/util.py b/util.py index ebfdf0ab..0bff3a5b 100644 --- a/util.py +++ b/util.py @@ -105,7 +105,7 @@ def check_environ (): guarantee that users can use in config files, command-line options, etc. Currently this includes: HOME - user's home directory (Unix only) - PLAT - desription of the current platform, including hardware + PLAT - description of the current platform, including hardware and OS (see 'get_platform()') """ @@ -125,7 +125,7 @@ def check_environ (): def subst_vars (str, local_vars): """Perform shell/Perl-style variable substitution on 'string'. - Every occurence of '$' followed by a name, or a name enclosed in + Every occurrence of '$' followed by a name, or a name enclosed in braces, is considered a variable. Every variable is substituted by the value found in the 'local_vars' dictionary, or in 'os.environ' if it's not in 'local_vars'. 'os.environ' is first checked/ -- cgit v1.2.1 From 5338783b1c404117bb02b80412e4cc94b5380469 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Jul 2000 02:17:40 +0000 Subject: Bump version to 0.9.1pre. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index ded0899c..f6af3c9d 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.9" +__version__ = "0.9.1pre" -- cgit v1.2.1 From e709ad9b04b0dca03c690faa222979297b759f74 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 00:04:17 +0000 Subject: Provides the FileList class for building a list of filenames by exploring the filesystem, and filtering the list by applying various patterns. Initial revision (almost) as supplied in a patch by Rene Liebscher; I just renamed the class from Template to FileList, and the module accordingly. --- filelist.py | 362 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 filelist.py diff --git a/filelist.py b/filelist.py new file mode 100644 index 00000000..ee7051b9 --- /dev/null +++ b/filelist.py @@ -0,0 +1,362 @@ +"""distutils.filelist + +Provides the FileList class, used for poking about the filesystem +and building lists of files. +""" + +# created 2000/07/17, Rene Liebscher (as template.py) +# most parts taken from commands/sdist.py +# renamed 2000/07/29 (to filelist.py) and officially added to +# the Distutils source, Greg Ward + +__revision__ = "$Id$" + +import sys, os, string, re +import fnmatch +from types import * +from glob import glob +from distutils.util import convert_path + +class FileList: + + files = None # reference to files list to mainpulate + allfiles = None # list of all files, if None will be filled + # at first use from directory self.dir + dir = None # directory from which files will be taken + # to fill self.allfiles if it was not set otherwise + + # next both functions (callable objects) can be set by the user + # warn: warning function + # debug_print: debug function + + def __init__(self, + files=[], + dir=os.curdir, + allfiles=None, + warn=None, + debug_print=None): + # use standard warning and debug functions, if no other given + if warn is None: warn = self.__warn + if debug_print is None: debug_print = self.__debug_print + self.warn = warn + self.debug_print = debug_print + self.files = files + self.dir = dir + self.allfiles = allfiles + # if None, it will be filled, when used for first time + + + # standard warning and debug functions, if no other given + def __warn (self, msg): + sys.stderr.write ("warning: template: %s\n" % msg) + + def __debug_print (self, msg): + """Print 'msg' to stdout if the global DEBUG (taken from the + DISTUTILS_DEBUG environment variable) flag is true. + """ + from distutils.core import DEBUG + if DEBUG: + print msg + + + def process_line(self, line): + + words = string.split (line) + action = words[0] + + # First, check that the right number of words are present + # for the given action (which is the first word) + if action in ('include','exclude', + 'global-include','global-exclude'): + if len (words) < 2: + self.warn \ + ("invalid template line: " + + "'%s' expects ..." % + action) + return + + pattern_list = map(convert_path, words[1:]) + + elif action in ('recursive-include','recursive-exclude'): + if len (words) < 3: + self.warn \ + ("invalid template line: " + + "'%s' expects ..." % + action) + return + + dir = convert_path(words[1]) + pattern_list = map (convert_path, words[2:]) + + elif action in ('graft','prune'): + if len (words) != 2: + self.warn \ + ("invalid template line: " + + "'%s' expects a single " % + action) + return + + dir_pattern = convert_path (words[1]) + + else: + self.warn ("invalid template line: " + + "unknown action '%s'" % action) + return + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. Also, we have + # defined either (pattern), (dir and pattern), or + # (dir_pattern) -- so we don't have to spend any time + # digging stuff up out of 'words'. + + if action == 'include': + self.debug_print("include " + string.join(pattern_list)) + for pattern in pattern_list: + if not self.select_pattern (pattern, anchor=1): + self.warn ("no files found matching '%s'" % + pattern) + + elif action == 'exclude': + self.debug_print("exclude " + string.join(pattern_list)) + for pattern in pattern_list: + if not self.exclude_pattern (pattern, anchor=1): + self.warn ( + "no previously-included files found matching '%s'"% + pattern) + + elif action == 'global-include': + self.debug_print("global-include " + string.join(pattern_list)) + for pattern in pattern_list: + if not self.select_pattern (pattern, anchor=0): + self.warn (("no files found matching '%s' " + + "anywhere in distribution") % + pattern) + + elif action == 'global-exclude': + self.debug_print("global-exclude " + string.join(pattern_list)) + for pattern in pattern_list: + if not self.exclude_pattern (pattern, anchor=0): + self.warn \ + (("no previously-included files matching '%s' " + + "found anywhere in distribution") % + pattern) + + elif action == 'recursive-include': + self.debug_print("recursive-include %s %s" % + (dir, string.join(pattern_list))) + for pattern in pattern_list: + if not self.select_pattern (pattern, prefix=dir): + self.warn (("no files found matching '%s' " + + "under directory '%s'") % + (pattern, dir)) + + elif action == 'recursive-exclude': + self.debug_print("recursive-exclude %s %s" % + (dir, string.join(pattern_list))) + for pattern in pattern_list: + if not self.exclude_pattern(pattern, prefix=dir): + self.warn \ + (("no previously-included files matching '%s' " + + "found under directory '%s'") % + (pattern, dir)) + + elif action == 'graft': + self.debug_print("graft " + dir_pattern) + if not self.select_pattern(None, prefix=dir_pattern): + self.warn ("no directories found matching '%s'" % + dir_pattern) + + elif action == 'prune': + self.debug_print("prune " + dir_pattern) + if not self.exclude_pattern(None, prefix=dir_pattern): + self.warn \ + (("no previously-included directories found " + + "matching '%s'") % + dir_pattern) + else: + raise RuntimeError, \ + "this cannot happen: invalid action '%s'" % action + + # process_line () + + + + + def select_pattern (self, pattern, + anchor=1, prefix=None, is_regex=0): + """Select strings (presumably filenames) from 'files' that match + 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not + quite the same as implemented by the 'fnmatch' module: '*' and '?' + match non-special characters, where "special" is platform-dependent: + slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on + Mac OS. + + If 'anchor' is true (the default), then the pattern match is more + stringent: "*.py" will match "foo.py" but not "foo/bar.py". If + 'anchor' is false, both of these will match. + + If 'prefix' is supplied, then only filenames starting with 'prefix' + (itself a pattern) and ending with 'pattern', with anything in between + them, will match. 'anchor' is ignored in this case. + + If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and + 'pattern' is assumed to be either a string containing a regex or a + regex object -- no translation is done, the regex is just compiled + and used as-is. + + Selected strings will be added to self.files. + + Return 1 if files are found. + """ + files_found = 0 + pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) + self.debug_print("select_pattern: applying regex r'%s'" % + pattern_re.pattern) + + # delayed loading of allfiles list + if self.allfiles is None: self.allfiles = findall (self.dir) + + for name in self.allfiles: + if pattern_re.search (name): + self.debug_print(" adding " + name) + self.files.append (name) + files_found = 1 + + return files_found + + # select_pattern () + + + def exclude_pattern (self, pattern, + anchor=1, prefix=None, is_regex=0): + """Remove strings (presumably filenames) from 'files' that match + 'pattern'. Other parameters are the same as for + 'select_pattern()', above. + The list 'self.files' is modified in place. + Return 1 if files are found. + """ + files_found = 0 + pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) + self.debug_print("exclude_pattern: applying regex r'%s'" % + pattern_re.pattern) + for i in range (len(self.files)-1, -1, -1): + if pattern_re.search (self.files[i]): + self.debug_print(" removing " + self.files[i]) + del self.files[i] + files_found = 1 + + return files_found + + # exclude_pattern () + + + def recursive_exclude_pattern (self, dir, pattern=None): + """Remove filenames from 'self.files' that are under 'dir' and + whose basenames match 'pattern'. + Return 1 if files are found. + """ + files_found = 0 + self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" % + (dir, pattern)) + if pattern is None: + pattern_re = None + else: + pattern_re = translate_pattern (pattern) + + for i in range (len (self.files)-1, -1, -1): + (cur_dir, cur_base) = os.path.split (self.files[i]) + if (cur_dir == dir and + (pattern_re is None or pattern_re.match (cur_base))): + self.debug_print("removing %s" % self.files[i]) + del self.files[i] + files_found = 1 + + return files_found + +# class FileList + + +# ---------------------------------------------------------------------- +# Utility functions + +def findall (dir = os.curdir): + """Find all files under 'dir' and return the list of full filenames + (relative to 'dir'). + """ + from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK + + list = [] + stack = [dir] + pop = stack.pop + push = stack.append + + while stack: + dir = pop() + names = os.listdir (dir) + + for name in names: + if dir != os.curdir: # avoid the dreaded "./" syndrome + fullname = os.path.join (dir, name) + else: + fullname = name + + # Avoid excess stat calls -- just one will do, thank you! + stat = os.stat(fullname) + mode = stat[ST_MODE] + if S_ISREG(mode): + list.append (fullname) + elif S_ISDIR(mode) and not S_ISLNK(mode): + push (fullname) + + return list + + +def glob_to_re (pattern): + """Translate a shell-like glob pattern to a regular expression; return + a string containing the regex. Differs from 'fnmatch.translate()' in + that '*' does not match "special characters" (which are + platform-specific). + """ + pattern_re = fnmatch.translate (pattern) + + # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which + # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, + # and by extension they shouldn't match such "special characters" under + # any OS. So change all non-escaped dots in the RE to match any + # character except the special characters. + # XXX currently the "special characters" are just slash -- i.e. this is + # Unix-only. + pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re) + return pattern_re + +# glob_to_re () + + +def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): + """Translate a shell-like wildcard pattern to a compiled regular + expression. Return the compiled regex. If 'is_regex' true, + then 'pattern' is directly compiled to a regex (if it's a string) + or just returned as-is (assumes it's a regex object). + """ + if is_regex: + if type(pattern) is StringType: + return re.compile(pattern) + else: + return pattern + + if pattern: + pattern_re = glob_to_re (pattern) + else: + pattern_re = '' + + if prefix is not None: + prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $ + pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re) + else: # no prefix -- respect anchor flag + if anchor: + pattern_re = "^" + pattern_re + + return re.compile (pattern_re) + +# translate_pattern () -- cgit v1.2.1 From 8c4ad7e3aecd45e6207ee27003cceef9ed92b8d2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 00:08:13 +0000 Subject: Added class docstring and ditched inappropriate class attrs. Indentation/whitspace fixes. --- filelist.py | 249 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 126 insertions(+), 123 deletions(-) diff --git a/filelist.py b/filelist.py index ee7051b9..78f2a8d8 100644 --- a/filelist.py +++ b/filelist.py @@ -19,15 +19,19 @@ from distutils.util import convert_path class FileList: - files = None # reference to files list to mainpulate - allfiles = None # list of all files, if None will be filled - # at first use from directory self.dir - dir = None # directory from which files will be taken - # to fill self.allfiles if it was not set otherwise - - # next both functions (callable objects) can be set by the user - # warn: warning function - # debug_print: debug function + """A list of files built by on exploring the filesystem and filtered by + applying various patterns to what we find there. + + Instance attributes: + dir + directory from which files will be taken -- only used if + 'allfiles' not supplied to constructor + files + list of filenames currently being built/filtered/manipulated + allfiles + complete list of files under consideration (ie. without any + filtering applied) + """ def __init__(self, files=[], @@ -42,13 +46,14 @@ class FileList: self.debug_print = debug_print self.files = files self.dir = dir + + # if None, 'allfiles' will be filled when used for first time self.allfiles = allfiles - # if None, it will be filled, when used for first time # standard warning and debug functions, if no other given def __warn (self, msg): - sys.stderr.write ("warning: template: %s\n" % msg) + sys.stderr.write ("warning: %s\n" % msg) def __debug_print (self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the @@ -59,130 +64,128 @@ class FileList: print msg - def process_line(self, line): + def process_line (self, line): + + words = string.split (line) + action = words[0] + + # First, check that the right number of words are present + # for the given action (which is the first word) + if action in ('include','exclude', + 'global-include','global-exclude'): + if len (words) < 2: + self.warn \ + ("invalid template line: " + + "'%s' expects ..." % + action) + return - words = string.split (line) - action = words[0] + pattern_list = map(convert_path, words[1:]) - # First, check that the right number of words are present - # for the given action (which is the first word) - if action in ('include','exclude', - 'global-include','global-exclude'): - if len (words) < 2: - self.warn \ - ("invalid template line: " + - "'%s' expects ..." % - action) - return + elif action in ('recursive-include','recursive-exclude'): + if len (words) < 3: + self.warn \ + ("invalid template line: " + + "'%s' expects ..." % + action) + return - pattern_list = map(convert_path, words[1:]) + dir = convert_path(words[1]) + pattern_list = map (convert_path, words[2:]) - elif action in ('recursive-include','recursive-exclude'): - if len (words) < 3: - self.warn \ - ("invalid template line: " + - "'%s' expects ..." % - action) - return + elif action in ('graft','prune'): + if len (words) != 2: + self.warn \ + ("invalid template line: " + + "'%s' expects a single " % + action) + return - dir = convert_path(words[1]) - pattern_list = map (convert_path, words[2:]) + dir_pattern = convert_path (words[1]) - elif action in ('graft','prune'): - if len (words) != 2: + else: + self.warn ("invalid template line: " + + "unknown action '%s'" % action) + return + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. Also, we have + # defined either (pattern), (dir and pattern), or + # (dir_pattern) -- so we don't have to spend any time + # digging stuff up out of 'words'. + + if action == 'include': + self.debug_print("include " + string.join(pattern_list)) + for pattern in pattern_list: + if not self.select_pattern (pattern, anchor=1): + self.warn ("no files found matching '%s'" % + pattern) + + elif action == 'exclude': + self.debug_print("exclude " + string.join(pattern_list)) + for pattern in pattern_list: + if not self.exclude_pattern (pattern, anchor=1): + self.warn ( + "no previously-included files found matching '%s'"% + pattern) + + elif action == 'global-include': + self.debug_print("global-include " + string.join(pattern_list)) + for pattern in pattern_list: + if not self.select_pattern (pattern, anchor=0): + self.warn (("no files found matching '%s' " + + "anywhere in distribution") % + pattern) + + elif action == 'global-exclude': + self.debug_print("global-exclude " + string.join(pattern_list)) + for pattern in pattern_list: + if not self.exclude_pattern (pattern, anchor=0): self.warn \ - ("invalid template line: " + - "'%s' expects a single " % - action) - return - - dir_pattern = convert_path (words[1]) - - else: - self.warn ("invalid template line: " + - "unknown action '%s'" % action) - return - - # OK, now we know that the action is valid and we have the - # right number of words on the line for that action -- so we - # can proceed with minimal error-checking. Also, we have - # defined either (pattern), (dir and pattern), or - # (dir_pattern) -- so we don't have to spend any time - # digging stuff up out of 'words'. - - if action == 'include': - self.debug_print("include " + string.join(pattern_list)) - for pattern in pattern_list: - if not self.select_pattern (pattern, anchor=1): - self.warn ("no files found matching '%s'" % - pattern) - - elif action == 'exclude': - self.debug_print("exclude " + string.join(pattern_list)) - for pattern in pattern_list: - if not self.exclude_pattern (pattern, anchor=1): - self.warn ( - "no previously-included files found matching '%s'"% - pattern) - - elif action == 'global-include': - self.debug_print("global-include " + string.join(pattern_list)) - for pattern in pattern_list: - if not self.select_pattern (pattern, anchor=0): - self.warn (("no files found matching '%s' " + - "anywhere in distribution") % - pattern) - - elif action == 'global-exclude': - self.debug_print("global-exclude " + string.join(pattern_list)) - for pattern in pattern_list: - if not self.exclude_pattern (pattern, anchor=0): - self.warn \ - (("no previously-included files matching '%s' " + - "found anywhere in distribution") % - pattern) - - elif action == 'recursive-include': - self.debug_print("recursive-include %s %s" % - (dir, string.join(pattern_list))) - for pattern in pattern_list: - if not self.select_pattern (pattern, prefix=dir): - self.warn (("no files found matching '%s' " + - "under directory '%s'") % - (pattern, dir)) - - elif action == 'recursive-exclude': - self.debug_print("recursive-exclude %s %s" % - (dir, string.join(pattern_list))) - for pattern in pattern_list: - if not self.exclude_pattern(pattern, prefix=dir): - self.warn \ - (("no previously-included files matching '%s' " + - "found under directory '%s'") % - (pattern, dir)) - - elif action == 'graft': - self.debug_print("graft " + dir_pattern) - if not self.select_pattern(None, prefix=dir_pattern): - self.warn ("no directories found matching '%s'" % - dir_pattern) - - elif action == 'prune': - self.debug_print("prune " + dir_pattern) - if not self.exclude_pattern(None, prefix=dir_pattern): + (("no previously-included files matching '%s' " + + "found anywhere in distribution") % + pattern) + + elif action == 'recursive-include': + self.debug_print("recursive-include %s %s" % + (dir, string.join(pattern_list))) + for pattern in pattern_list: + if not self.select_pattern (pattern, prefix=dir): + self.warn (("no files found matching '%s' " + + "under directory '%s'") % + (pattern, dir)) + + elif action == 'recursive-exclude': + self.debug_print("recursive-exclude %s %s" % + (dir, string.join(pattern_list))) + for pattern in pattern_list: + if not self.exclude_pattern(pattern, prefix=dir): self.warn \ - (("no previously-included directories found " + - "matching '%s'") % - dir_pattern) - else: - raise RuntimeError, \ - "this cannot happen: invalid action '%s'" % action + (("no previously-included files matching '%s' " + + "found under directory '%s'") % + (pattern, dir)) + + elif action == 'graft': + self.debug_print("graft " + dir_pattern) + if not self.select_pattern(None, prefix=dir_pattern): + self.warn ("no directories found matching '%s'" % + dir_pattern) + + elif action == 'prune': + self.debug_print("prune " + dir_pattern) + if not self.exclude_pattern(None, prefix=dir_pattern): + self.warn \ + (("no previously-included directories found " + + "matching '%s'") % + dir_pattern) + else: + raise RuntimeError, \ + "this cannot happen: invalid action '%s'" % action # process_line () - - def select_pattern (self, pattern, anchor=1, prefix=None, is_regex=0): """Select strings (presumably filenames) from 'files' that match -- cgit v1.2.1 From aab3b0d0ea6785e13a0bdeb71853544d4302f568 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 00:21:36 +0000 Subject: Renamed 'process_line()' to 'process_template_line()', and factored out '_parse_template_line()'. --- filelist.py | 132 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 63 insertions(+), 69 deletions(-) diff --git a/filelist.py b/filelist.py index 78f2a8d8..5310ae5a 100644 --- a/filelist.py +++ b/filelist.py @@ -16,6 +16,7 @@ import fnmatch from types import * from glob import glob from distutils.util import convert_path +from distutils.errors import DistutilsTemplateError, DistutilsInternalError class FileList: @@ -64,126 +65,119 @@ class FileList: print msg - def process_line (self, line): - + def _parse_template_line (self, line): words = string.split (line) action = words[0] - # First, check that the right number of words are present - # for the given action (which is the first word) - if action in ('include','exclude', - 'global-include','global-exclude'): + patterns = dir = dir_pattern = None + + if action in ('include', 'exclude', + 'global-include', 'global-exclude'): if len (words) < 2: - self.warn \ - ("invalid template line: " + - "'%s' expects ..." % - action) - return + raise DistutilsTemplateError, \ + "'%s' expects ..." % action - pattern_list = map(convert_path, words[1:]) + patterns = map(convert_path, words[1:]) - elif action in ('recursive-include','recursive-exclude'): + elif action in ('recursive-include', 'recursive-exclude'): if len (words) < 3: - self.warn \ - ("invalid template line: " + - "'%s' expects ..." % - action) - return + raise DistutilsTemplateError, \ + "'%s' expects ..." % action dir = convert_path(words[1]) - pattern_list = map (convert_path, words[2:]) + patterns = map(convert_path, words[2:]) - elif action in ('graft','prune'): + elif action in ('graft', 'prune'): if len (words) != 2: - self.warn \ - ("invalid template line: " + - "'%s' expects a single " % - action) - return + raise DistutilsTemplateError, \ + "'%s' expects a single " % action - dir_pattern = convert_path (words[1]) + dir_pattern = convert_path(words[1]) else: - self.warn ("invalid template line: " + - "unknown action '%s'" % action) - return + raise DistutilsTemplateError, "unknown action '%s'" % action + + return (action, pattern, dir, dir_pattern) + + # _parse_template_line () + + + def process_template_line (self, line): + + # Parse the line: split it up, make sure the right number of words + # are there, and return the relevant words. 'action' is always + # defined: it's the first word of the line. Which of the other + # three are defined depends on the action; it'll be either + # patterns, (dir and patterns), or (dir_pattern). + (action, patterns, dir, dir_pattern) = self._parse_template_line(line) # OK, now we know that the action is valid and we have the # right number of words on the line for that action -- so we - # can proceed with minimal error-checking. Also, we have - # defined either (pattern), (dir and pattern), or - # (dir_pattern) -- so we don't have to spend any time - # digging stuff up out of 'words'. - + # can proceed with minimal error-checking. if action == 'include': - self.debug_print("include " + string.join(pattern_list)) - for pattern in pattern_list: + self.debug_print("include " + string.join(patterns)) + for pattern in patterns: if not self.select_pattern (pattern, anchor=1): - self.warn ("no files found matching '%s'" % - pattern) + self.warn("no files found matching '%s'" % pattern) elif action == 'exclude': - self.debug_print("exclude " + string.join(pattern_list)) - for pattern in pattern_list: + self.debug_print("exclude " + string.join(patterns)) + for pattern in patterns: if not self.exclude_pattern (pattern, anchor=1): - self.warn ( + self.warn( "no previously-included files found matching '%s'"% pattern) elif action == 'global-include': - self.debug_print("global-include " + string.join(pattern_list)) - for pattern in pattern_list: + self.debug_print("global-include " + string.join(patterns)) + for pattern in patterns: if not self.select_pattern (pattern, anchor=0): self.warn (("no files found matching '%s' " + - "anywhere in distribution") % - pattern) + "anywhere in distribution") % + pattern) elif action == 'global-exclude': - self.debug_print("global-exclude " + string.join(pattern_list)) - for pattern in pattern_list: + self.debug_print("global-exclude " + string.join(patterns)) + for pattern in patterns: if not self.exclude_pattern (pattern, anchor=0): - self.warn \ - (("no previously-included files matching '%s' " + - "found anywhere in distribution") % - pattern) + self.warn(("no previously-included files matching '%s' " + + "found anywhere in distribution") % + pattern) elif action == 'recursive-include': self.debug_print("recursive-include %s %s" % - (dir, string.join(pattern_list))) - for pattern in pattern_list: + (dir, string.join(patterns))) + for pattern in patterns: if not self.select_pattern (pattern, prefix=dir): self.warn (("no files found matching '%s' " + - "under directory '%s'") % - (pattern, dir)) + "under directory '%s'") % + (pattern, dir)) elif action == 'recursive-exclude': self.debug_print("recursive-exclude %s %s" % - (dir, string.join(pattern_list))) - for pattern in pattern_list: + (dir, string.join(patterns))) + for pattern in patterns: if not self.exclude_pattern(pattern, prefix=dir): - self.warn \ - (("no previously-included files matching '%s' " + - "found under directory '%s'") % - (pattern, dir)) + self.warn(("no previously-included files matching '%s' " + + "found under directory '%s'") % + (pattern, dir)) elif action == 'graft': self.debug_print("graft " + dir_pattern) if not self.select_pattern(None, prefix=dir_pattern): - self.warn ("no directories found matching '%s'" % - dir_pattern) + self.warn ("no directories found matching '%s'" % dir_pattern) elif action == 'prune': self.debug_print("prune " + dir_pattern) if not self.exclude_pattern(None, prefix=dir_pattern): - self.warn \ - (("no previously-included directories found " + - "matching '%s'") % - dir_pattern) + self.warn(("no previously-included directories found " + + "matching '%s'") % + dir_pattern) else: - raise RuntimeError, \ + raise DistutilsInternalError, \ "this cannot happen: invalid action '%s'" % action - # process_line () + # process_template_line () def select_pattern (self, pattern, -- cgit v1.2.1 From ea8670808399ab267f60d8cfcab189102e069b8e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 00:36:25 +0000 Subject: Renamed 'select_pattern()' to 'include_pattern()'. Other cosmetic/doc/comment tweaks. --- filelist.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/filelist.py b/filelist.py index 5310ae5a..749fa612 100644 --- a/filelist.py +++ b/filelist.py @@ -105,7 +105,7 @@ class FileList: def process_template_line (self, line): # Parse the line: split it up, make sure the right number of words - # are there, and return the relevant words. 'action' is always + # is there, and return the relevant words. 'action' is always # defined: it's the first word of the line. Which of the other # three are defined depends on the action; it'll be either # patterns, (dir and patterns), or (dir_pattern). @@ -117,7 +117,7 @@ class FileList: if action == 'include': self.debug_print("include " + string.join(patterns)) for pattern in patterns: - if not self.select_pattern (pattern, anchor=1): + if not self.include_pattern (pattern, anchor=1): self.warn("no files found matching '%s'" % pattern) elif action == 'exclude': @@ -131,7 +131,7 @@ class FileList: elif action == 'global-include': self.debug_print("global-include " + string.join(patterns)) for pattern in patterns: - if not self.select_pattern (pattern, anchor=0): + if not self.include_pattern (pattern, anchor=0): self.warn (("no files found matching '%s' " + "anywhere in distribution") % pattern) @@ -148,7 +148,7 @@ class FileList: self.debug_print("recursive-include %s %s" % (dir, string.join(patterns))) for pattern in patterns: - if not self.select_pattern (pattern, prefix=dir): + if not self.include_pattern (pattern, prefix=dir): self.warn (("no files found matching '%s' " + "under directory '%s'") % (pattern, dir)) @@ -164,7 +164,7 @@ class FileList: elif action == 'graft': self.debug_print("graft " + dir_pattern) - if not self.select_pattern(None, prefix=dir_pattern): + if not self.include_pattern(None, prefix=dir_pattern): self.warn ("no directories found matching '%s'" % dir_pattern) elif action == 'prune': @@ -180,14 +180,15 @@ class FileList: # process_template_line () - def select_pattern (self, pattern, + def include_pattern (self, pattern, anchor=1, prefix=None, is_regex=0): - """Select strings (presumably filenames) from 'files' that match - 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not - quite the same as implemented by the 'fnmatch' module: '*' and '?' - match non-special characters, where "special" is platform-dependent: - slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on - Mac OS. + + """Select strings (presumably filenames) from 'self.files' that + match 'pattern', a Unix-style wildcard (glob) pattern. Patterns + are not quite the same as implemented by the 'fnmatch' module: '*' + and '?' match non-special characters, where "special" is platform- + dependent: slash on Unix; colon, slash, and backslash on + DOS/Windows; and colon on Mac OS. If 'anchor' is true (the default), then the pattern match is more stringent: "*.py" will match "foo.py" but not "foo/bar.py". If @@ -208,7 +209,7 @@ class FileList: """ files_found = 0 pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) - self.debug_print("select_pattern: applying regex r'%s'" % + self.debug_print("include_pattern: applying regex r'%s'" % pattern_re.pattern) # delayed loading of allfiles list @@ -222,14 +223,14 @@ class FileList: return files_found - # select_pattern () + # include_pattern () def exclude_pattern (self, pattern, anchor=1, prefix=None, is_regex=0): """Remove strings (presumably filenames) from 'files' that match 'pattern'. Other parameters are the same as for - 'select_pattern()', above. + 'include_pattern()', above. The list 'self.files' is modified in place. Return 1 if files are found. """ -- cgit v1.2.1 From 86f9edf31c0f992aacc6f9fb0892a57e749e60f4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 00:37:04 +0000 Subject: Ditched the unused 'recursive_exclude_pattern()' method. --- filelist.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/filelist.py b/filelist.py index 749fa612..2eaff837 100644 --- a/filelist.py +++ b/filelist.py @@ -248,30 +248,6 @@ class FileList: # exclude_pattern () - - def recursive_exclude_pattern (self, dir, pattern=None): - """Remove filenames from 'self.files' that are under 'dir' and - whose basenames match 'pattern'. - Return 1 if files are found. - """ - files_found = 0 - self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" % - (dir, pattern)) - if pattern is None: - pattern_re = None - else: - pattern_re = translate_pattern (pattern) - - for i in range (len (self.files)-1, -1, -1): - (cur_dir, cur_base) = os.path.split (self.files[i]) - if (cur_dir == dir and - (pattern_re is None or pattern_re.match (cur_base))): - self.debug_print("removing %s" % self.files[i]) - del self.files[i] - files_found = 1 - - return files_found - # class FileList -- cgit v1.2.1 From 2a8fc1009cee9fe11b303b246f81dfc71806e647 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:03:31 +0000 Subject: Added DistutilsTemplateError. --- errors.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/errors.py b/errors.py index a718f01a..bec34645 100644 --- a/errors.py +++ b/errors.py @@ -73,6 +73,9 @@ class DistutilsInternalError (DistutilsError): should never be seen if the code is working!).""" pass +class DistutilsTemplateError (DistutilsError): + """Syntax error in a file list template.""" + # Exception classes used by the CCompiler implementation classes class CCompilerError (Exception): -- cgit v1.2.1 From 689482195099b5777e8d3ae68f3049df64c46d7a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:04:22 +0000 Subject: Typo fix. --- filelist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filelist.py b/filelist.py index 2eaff837..a8049287 100644 --- a/filelist.py +++ b/filelist.py @@ -97,7 +97,7 @@ class FileList: else: raise DistutilsTemplateError, "unknown action '%s'" % action - return (action, pattern, dir, dir_pattern) + return (action, patterns, dir, dir_pattern) # _parse_template_line () -- cgit v1.2.1 From 29d6ebdae92bac92b53c8cb2d69c710b12c8b1a9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:05:02 +0000 Subject: The other half of Rene Liebscher's patch to add the Template class, which I renamed to FileList: remove all the file-list-generation code from the sdist command and adapt it to use the new FileList class instead. --- command/sdist.py | 320 +++---------------------------------------------------- 1 file changed, 15 insertions(+), 305 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 3b91170c..045054b8 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -6,16 +6,16 @@ Implements the Distutils 'sdist' command (create a source distribution).""" __revision__ = "$Id$" -import sys, os, string, re -import fnmatch +import sys, os, string from types import * from glob import glob from distutils.core import Command from distutils.util import \ - convert_path, create_tree, remove_tree, newer, write_file, \ + create_tree, remove_tree, newer, write_file, \ check_archive_formats from distutils.text_file import TextFile from distutils.errors import DistutilsExecError, DistutilsOptionError +from distutils.filelist import FileList def show_formats (): @@ -319,25 +319,6 @@ class sdist (Command): # add_defaults () - def recursive_exclude_pattern (self, dir, pattern=None): - """Remove filenames from 'self.files' that are under 'dir' and - whose basenames match 'pattern'. - """ - self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" % - (dir, pattern)) - if pattern is None: - pattern_re = None - else: - pattern_re = translate_pattern (pattern) - - for i in range (len (self.files)-1, -1, -1): - (cur_dir, cur_base) = os.path.split (self.files[i]) - if (cur_dir == dir and - (pattern_re is None or pattern_re.match (cur_base))): - self.debug_print("removing %s" % self.files[i]) - del self.files[i] - - def read_template (self): """Read and parse the manifest template file named by 'self.template' (usually "MANIFEST.in"). Process all file @@ -356,152 +337,17 @@ class sdist (Command): rstrip_ws=1, collapse_ws=1) - all_files = findall () + # if we give Template() a list, it modifies this list + filelist = FileList(files=self.files, + warn=self.warn, + debug_print=self.debug_print) while 1: - line = template.readline() if line is None: # end of file break - words = string.split (line) - action = words[0] - - # First, check that the right number of words are present - # for the given action (which is the first word) - if action in ('include','exclude', - 'global-include','global-exclude'): - if len (words) < 2: - template.warn \ - ("invalid manifest template line: " + - "'%s' expects ..." % - action) - continue - - pattern_list = map(convert_path, words[1:]) - - elif action in ('recursive-include','recursive-exclude'): - if len (words) < 3: - template.warn \ - ("invalid manifest template line: " + - "'%s' expects ..." % - action) - continue - - dir = convert_path(words[1]) - pattern_list = map (convert_path, words[2:]) - - elif action in ('graft','prune'): - if len (words) != 2: - template.warn \ - ("invalid manifest template line: " + - "'%s' expects a single " % - action) - continue - - dir_pattern = convert_path (words[1]) - - else: - template.warn ("invalid manifest template line: " + - "unknown action '%s'" % action) - continue - - # OK, now we know that the action is valid and we have the - # right number of words on the line for that action -- so we - # can proceed with minimal error-checking. Also, we have - # defined either (pattern), (dir and pattern), or - # (dir_pattern) -- so we don't have to spend any time - # digging stuff up out of 'words'. - - if action == 'include': - self.debug_print("include " + string.join(pattern_list)) - for pattern in pattern_list: - files = self.select_pattern (all_files, pattern, anchor=1) - if not files: - template.warn ("no files found matching '%s'" % - pattern) - else: - self.files.extend (files) - - elif action == 'exclude': - self.debug_print("exclude " + string.join(pattern_list)) - for pattern in pattern_list: - num = self.exclude_pattern (self.files, pattern, anchor=1) - if num == 0: - template.warn ( - "no previously-included files found matching '%s'"% - pattern) - - elif action == 'global-include': - self.debug_print("global-include " + string.join(pattern_list)) - for pattern in pattern_list: - files = self.select_pattern (all_files, pattern, anchor=0) - if not files: - template.warn (("no files found matching '%s' " + - "anywhere in distribution") % - pattern) - else: - self.files.extend (files) - - elif action == 'global-exclude': - self.debug_print("global-exclude " + string.join(pattern_list)) - for pattern in pattern_list: - num = self.exclude_pattern (self.files, pattern, anchor=0) - if num == 0: - template.warn \ - (("no previously-included files matching '%s' " + - "found anywhere in distribution") % - pattern) - - elif action == 'recursive-include': - self.debug_print("recursive-include %s %s" % - (dir, string.join(pattern_list))) - for pattern in pattern_list: - files = self.select_pattern ( - all_files, pattern, prefix=dir) - if not files: - template.warn (("no files found matching '%s' " + - "under directory '%s'") % - (pattern, dir)) - else: - self.files.extend (files) - - elif action == 'recursive-exclude': - self.debug_print("recursive-exclude %s %s" % - (dir, string.join(pattern_list))) - for pattern in pattern_list: - num = self.exclude_pattern( - self.files, pattern, prefix=dir) - if num == 0: - template.warn \ - (("no previously-included files matching '%s' " + - "found under directory '%s'") % - (pattern, dir)) - - elif action == 'graft': - self.debug_print("graft " + dir_pattern) - files = self.select_pattern( - all_files, None, prefix=dir_pattern) - if not files: - template.warn ("no directories found matching '%s'" % - dir_pattern) - else: - self.files.extend (files) - - elif action == 'prune': - self.debug_print("prune " + dir_pattern) - num = self.exclude_pattern( - self.files, None, prefix=dir_pattern) - if num == 0: - template.warn \ - (("no previously-included directories found " + - "matching '%s'") % - dir_pattern) - else: - raise RuntimeError, \ - "this cannot happen: invalid action '%s'" % action - - # while loop over lines of template file + filelist.process_template_line(line) # read_template () @@ -516,65 +362,14 @@ class sdist (Command): """ build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() - self.exclude_pattern (self.files, None, prefix=build.build_base) - self.exclude_pattern (self.files, None, prefix=base_dir) - self.exclude_pattern (self.files, r'/(RCS|CVS)/.*', is_regex=1) - - - def select_pattern (self, files, pattern, - anchor=1, prefix=None, is_regex=0): - """Select strings (presumably filenames) from 'files' that match - 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not - quite the same as implemented by the 'fnmatch' module: '*' and '?' - match non-special characters, where "special" is platform-dependent: - slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on - Mac OS. - - If 'anchor' is true (the default), then the pattern match is more - stringent: "*.py" will match "foo.py" but not "foo/bar.py". If - 'anchor' is false, both of these will match. - - If 'prefix' is supplied, then only filenames starting with 'prefix' - (itself a pattern) and ending with 'pattern', with anything in between - them, will match. 'anchor' is ignored in this case. - - If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and - 'pattern' is assumed to be either a string containing a regex or a - regex object -- no translation is done, the regex is just compiled - and used as-is. - - Return the list of matching strings, possibly empty. - """ - matches = [] - pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) - self.debug_print("select_pattern: applying regex r'%s'" % - pattern_re.pattern) - for name in files: - if pattern_re.search (name): - matches.append (name) - self.debug_print(" adding " + name) - - return matches - # select_pattern () - - - def exclude_pattern (self, files, pattern, - anchor=1, prefix=None, is_regex=0): - """Remove strings (presumably filenames) from 'files' that match - 'pattern'. Other parameters are the same as for - 'select_pattern()', above. The list 'files' is modified in place. - """ - - pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) - self.debug_print("exclude_pattern: applying regex r'%s'" % - pattern_re.pattern) - for i in range (len(files)-1, -1, -1): - if pattern_re.search (files[i]): - self.debug_print(" removing " + files[i]) - del files[i] - - # exclude_pattern () + # if we give FileList a list, it modifies this list + filelist = FileList(files=self.files, + warn=self.warn, + debug_print=self.debug_print) + filelist.exclude_pattern(None, prefix=build.build_base) + filelist.exclude_pattern(None, prefix=base_dir) + filelist.exclude_pattern(r'/(RCS|CVS)/.*', is_regex=1) def write_manifest (self): @@ -676,88 +471,3 @@ class sdist (Command): return self.archive_files # class sdist - - -# ---------------------------------------------------------------------- -# Utility functions - -def findall (dir = os.curdir): - """Find all files under 'dir' and return the list of full filenames - (relative to 'dir'). - """ - from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK - - list = [] - stack = [dir] - pop = stack.pop - push = stack.append - - while stack: - dir = pop() - names = os.listdir (dir) - - for name in names: - if dir != os.curdir: # avoid leading "./" - fullname = os.path.join (dir, name) - else: - fullname = name - - # Avoid excess stat calls -- just one will do, thank you! - stat = os.stat(fullname) - mode = stat[ST_MODE] - if S_ISREG(mode): - list.append (fullname) - elif S_ISDIR(mode) and not S_ISLNK(mode): - push (fullname) - - return list - - -def glob_to_re (pattern): - """Translate a shell-like glob pattern to a regular expression; return - a string containing the regex. Differs from 'fnmatch.translate()' in - that '*' does not match "special characters" (which are - platform-specific). - """ - pattern_re = fnmatch.translate (pattern) - - # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which - # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, - # and by extension they shouldn't match such "special characters" under - # any OS. So change all non-escaped dots in the RE to match any - # character except the special characters. - # XXX currently the "special characters" are just slash -- i.e. this is - # Unix-only. - pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re) - return pattern_re - -# glob_to_re () - - -def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): - """Translate a shell-like wildcard pattern to a compiled regular - expression. Return the compiled regex. If 'is_regex' true, - then 'pattern' is directly compiled to a regex (if it's a string) - or just returned as-is (assumes it's a regex object). - """ - if is_regex: - if type(pattern) is StringType: - return re.compile(pattern) - else: - return pattern - - if pattern: - pattern_re = glob_to_re (pattern) - else: - pattern_re = '' - - if prefix is not None: - prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $ - pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re) - else: # no prefix -- respect anchor flag - if anchor: - pattern_re = "^" + pattern_re - - return re.compile (pattern_re) - -# translate_pattern () -- cgit v1.2.1 From ab16bbe1a660891123c029e7dbdd66e61a585a1c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:30:31 +0000 Subject: Replaced 'self.files' with 'self.filelist': now we carry around a FileList instance instead of a list of filenames. Simplifies the "sdist" command only a bit, but should allow greater simplification of FileList. --- command/sdist.py | 92 +++++++++++++++++++++++--------------------------------- 1 file changed, 38 insertions(+), 54 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 045054b8..4c2acf67 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -128,8 +128,9 @@ class sdist (Command): def run (self): - # 'files' is the list of files that will make up the manifest - self.files = [] + # 'filelist' contains the list of files that will make up the + # manifest + self.filelist = FileList() # Ensure that all required meta-data is given; warn if not (but # don't die, it's not *that* serious!) @@ -137,7 +138,7 @@ class sdist (Command): # Do whatever it takes to get the list of files to process # (process the manifest template, read an existing manifest, - # whatever). File list is put into 'self.files'. + # whatever). File list is accumulated in 'self.filelist'. self.get_file_list () # If user just wanted us to regenerate the manifest, stop now. @@ -184,7 +185,7 @@ class sdist (Command): def get_file_list (self): """Figure out the list of files to include in the source - distribution, and put it in 'self.files'. This might involve + distribution, and put it in 'self.filelist'. This might involve reading the manifest template (and writing the manifest), or just reading the manifest, or just using the default file set -- it all depends on the user's options and the state of the filesystem. @@ -192,9 +193,9 @@ class sdist (Command): # If we have a manifest template, see if it's newer than the # manifest; if so, we'll regenerate the manifest. - template_exists = os.path.isfile (self.template) + template_exists = os.path.isfile(self.template) if template_exists: - template_newer = newer (self.template, self.manifest) + template_newer = newer(self.template, self.manifest) # The contents of the manifest file almost certainly depend on the # setup script as well as the manifest template -- so if the setup @@ -222,17 +223,17 @@ class sdist (Command): self.force_manifest or self.manifest_only): if not template_exists: - self.warn (("manifest template '%s' does not exist " + - "(using default file list)") % - self.template) + self.warn(("manifest template '%s' does not exist " + + "(using default file list)") % + self.template) # Add default file set to 'files' if self.use_defaults: - self.add_defaults () + self.add_defaults() # Read manifest template if it exists if template_exists: - self.read_template () + self.read_template() # Prune away any directories that don't belong in the source # distribution @@ -241,30 +242,24 @@ class sdist (Command): # File list now complete -- sort it so that higher-level files # come first - sortable_files = map (os.path.split, self.files) - sortable_files.sort () - self.files = [] - for sort_tuple in sortable_files: - self.files.append (apply (os.path.join, sort_tuple)) + self.filelist.sort() # Remove duplicates from the file list - for i in range (len(self.files)-1, 0, -1): - if self.files[i] == self.files[i-1]: - del self.files[i] + self.filelist.remove_duplicates() # And write complete file list (including default file set) to # the manifest. - self.write_manifest () + self.write_manifest() # Don't regenerate the manifest, just read it in. else: - self.read_manifest () + self.read_manifest() # get_file_list () def add_defaults (self): - """Add all the default files to self.files: + """Add all the default files to self.filelist: - README or README.txt - setup.py - test/test*.py @@ -286,7 +281,7 @@ class sdist (Command): for fn in alts: if os.path.exists (fn): got_it = 1 - self.files.append (fn) + self.filelist.append (fn) break if not got_it: @@ -294,7 +289,7 @@ class sdist (Command): string.join (alts, ', ')) else: if os.path.exists (fn): - self.files.append (fn) + self.filelist.append (fn) else: self.warn ("standard file '%s' not found" % fn) @@ -302,33 +297,31 @@ class sdist (Command): for pattern in optional: files = filter (os.path.isfile, glob (pattern)) if files: - self.files.extend (files) + self.filelist.extend (files) if self.distribution.has_pure_modules(): build_py = self.get_finalized_command ('build_py') - self.files.extend (build_py.get_source_files ()) + self.filelist.extend (build_py.get_source_files ()) if self.distribution.has_ext_modules(): build_ext = self.get_finalized_command ('build_ext') - self.files.extend (build_ext.get_source_files ()) + self.filelist.extend (build_ext.get_source_files ()) if self.distribution.has_c_libraries(): build_clib = self.get_finalized_command ('build_clib') - self.files.extend (build_clib.get_source_files ()) + self.filelist.extend (build_clib.get_source_files ()) # add_defaults () def read_template (self): + """Read and parse the manifest template file named by - 'self.template' (usually "MANIFEST.in"). Process all file - specifications (include and exclude) in the manifest template and - update 'self.files' accordingly (filenames may be added to - or removed from 'self.files' based on the manifest template). + 'self.template' (usually "MANIFEST.in"). The parsing and + processing is done by 'self.filelist', which updates itself + accordingly. """ - assert self.files is not None and type (self.files) is ListType self.announce("reading manifest template '%s'" % self.template) - template = TextFile (self.template, strip_comments=1, skip_blanks=1, @@ -337,17 +330,12 @@ class sdist (Command): rstrip_ws=1, collapse_ws=1) - # if we give Template() a list, it modifies this list - filelist = FileList(files=self.files, - warn=self.warn, - debug_print=self.debug_print) - while 1: line = template.readline() if line is None: # end of file break - filelist.process_template_line(line) + self.filelist.process_template_line(line) # read_template () @@ -363,22 +351,18 @@ class sdist (Command): build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() - # if we give FileList a list, it modifies this list - filelist = FileList(files=self.files, - warn=self.warn, - debug_print=self.debug_print) - filelist.exclude_pattern(None, prefix=build.build_base) - filelist.exclude_pattern(None, prefix=base_dir) - filelist.exclude_pattern(r'/(RCS|CVS)/.*', is_regex=1) + self.filelist.exclude_pattern(None, prefix=build.build_base) + self.filelist.exclude_pattern(None, prefix=base_dir) + self.filelist.exclude_pattern(r'/(RCS|CVS)/.*', is_regex=1) def write_manifest (self): - """Write the file list in 'self.files' (presumably as filled in by - 'add_defaults()' and 'read_template()') to the manifest file named - by 'self.manifest'. + """Write the file list in 'self.filelist' (presumably as filled in + by 'add_defaults()' and 'read_template()') to the manifest file + named by 'self.manifest'. """ self.execute(write_file, - (self.manifest, self.files), + (self.manifest, self.filelist.files), "writing manifest file '%s'" % self.manifest) # write_manifest () @@ -386,7 +370,7 @@ class sdist (Command): def read_manifest (self): """Read the manifest file (named by 'self.manifest') and use it to - fill in 'self.files', the list of files to include in the source + fill in 'self.filelist', the list of files to include in the source distribution. """ self.announce("reading manifest file '%s'" % self.manifest) @@ -397,7 +381,7 @@ class sdist (Command): break if line[-1] == '\n': line = line[0:-1] - self.files.append (line) + self.filelist.append (line) # read_manifest () @@ -451,7 +435,7 @@ class sdist (Command): base_dir = self.distribution.get_fullname() base_name = os.path.join(self.dist_dir, base_dir) - self.make_release_tree (base_dir, self.files) + self.make_release_tree (base_dir, self.filelist.files) archive_files = [] # remember names of files we create if self.dist_dir: self.mkpath(self.dist_dir) -- cgit v1.2.1 From d3cb1ab466d37d52bad2f5b44d852bb6009c2214 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:45:42 +0000 Subject: Added list-like methods: 'append()', 'extend()', 'sort()'. Added 'remove_duplicates()'. Simplified constructor: no longer take 'files' or 'allfiles' as args, and no longer have 'dir' attribute at all. Added 'set_allfiles()' and 'findall()' so the client does have a way to set the list of all files. Changed 'include_pattern()' to use the 'findall()' method instead of the external function. (Of course, the method is just a trivial wrapper around the function.) --- filelist.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/filelist.py b/filelist.py index a8049287..84f36d2c 100644 --- a/filelist.py +++ b/filelist.py @@ -35,24 +35,25 @@ class FileList: """ def __init__(self, - files=[], - dir=os.curdir, - allfiles=None, warn=None, debug_print=None): - # use standard warning and debug functions, if no other given - if warn is None: warn = self.__warn - if debug_print is None: debug_print = self.__debug_print - self.warn = warn - self.debug_print = debug_print - self.files = files - self.dir = dir + # use standard warning and debug functions if no other given + self.warn = warn or self.__warn + self.debug_print = debug_print or self.__debug_print - # if None, 'allfiles' will be filled when used for first time - self.allfiles = allfiles + self.allfiles = None + self.files = [] - # standard warning and debug functions, if no other given + def set_allfiles (self, allfiles): + self.allfiles = allfiles + + def findall (self, dir=os.curdir): + self.allfiles = findall(dir) + + + # -- Fallback warning/debug functions ------------------------------ + def __warn (self, msg): sys.stderr.write ("warning: %s\n" % msg) @@ -64,6 +65,34 @@ class FileList: if DEBUG: print msg + + # -- List-like methods --------------------------------------------- + + def append (self, item): + self.files.append(item) + + def extend (self, items): + self.files.extend(items) + + def sort (self): + # Not a strict lexical sort! + sortable_files = map(os.path.split, self.files) + sortable_files.sort() + self.files = [] + for sort_tuple in sortable_files: + self.files.append(apply(os.path.join, sort_tuple)) + + + # -- Other miscellaneous utility methods --------------------------- + + def remove_duplicates (self): + # Assumes list has been sorted! + for i in range (len(self.files)-1, 0, -1): + if self.files[i] == self.files[i-1]: + del self.files[i] + + + # -- "File template" methods --------------------------------------- def _parse_template_line (self, line): words = string.split (line) @@ -180,6 +209,8 @@ class FileList: # process_template_line () + # -- Filtering/selection methods ----------------------------------- + def include_pattern (self, pattern, anchor=1, prefix=None, is_regex=0): @@ -213,7 +244,8 @@ class FileList: pattern_re.pattern) # delayed loading of allfiles list - if self.allfiles is None: self.allfiles = findall (self.dir) + if self.allfiles is None: + self.findall() for name in self.allfiles: if pattern_re.search (name): -- cgit v1.2.1 From c27f92a2c9bbf5835159d9b0d3e56ae258ef31be Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:47:16 +0000 Subject: Catch syntax errors from processing template lines and turn them into mere warnings. Call 'findall()' on our FileList object before we start using it seriously. --- command/sdist.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 4c2acf67..4765d7fa 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -14,7 +14,7 @@ from distutils.util import \ create_tree, remove_tree, newer, write_file, \ check_archive_formats from distutils.text_file import TextFile -from distutils.errors import DistutilsExecError, DistutilsOptionError +from distutils.errors import * from distutils.filelist import FileList @@ -227,6 +227,8 @@ class sdist (Command): "(using default file list)") % self.template) + self.filelist.findall() + # Add default file set to 'files' if self.use_defaults: self.add_defaults() @@ -335,7 +337,12 @@ class sdist (Command): if line is None: # end of file break - self.filelist.process_template_line(line) + try: + self.filelist.process_template_line(line) + except DistutilsTemplateError, msg: + self.warn("%s, line %d: %s" % (template.filename, + template.current_line, + msg)) # read_template () -- cgit v1.2.1 From 06830ada1cda3595d2a1f222fd8cbed35bdb691e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 1 Aug 2000 23:54:29 +0000 Subject: Patch from Rene Liebscher, tweaked by me: - 'export_symbol_file' (and corresponding 'def_file' in the old "build info" dict) are gone; warn if we see 'def_file' in the dict - the MSVC "pre-link hack" is gone -- all that stuff is now handled elsewhere (eg. by using 'export_symbols', etc.) - add 'get_export_symbols()' and 'get_libraries()' methods -- needed because on Windows, both of those things are a tad more complicated than fetching them from the Extension instance --- command/build_ext.py | 85 +++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index c04036b0..1ffe3234 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -84,7 +84,7 @@ class build_ext (Command): help_options = [ ('help-compiler', None, "list available compilers", show_compilers), - ] + ] def initialize_options (self): self.extensions = None @@ -282,7 +282,9 @@ class build_ext (Command): # Medium-easy stuff: same syntax/semantics, different names. ext.runtime_library_dirs = build_info.get('rpath') - ext.export_symbol_file = build_info.get('def_file') + if build_info.has_key('def_file'): + self.warn("'def_file' element of build info dict " + "no longer supported") # Non-trivial stuff: 'macros' split into 'define_macros' # and 'undef_macros'. @@ -420,16 +422,14 @@ class build_ext (Command): objects.extend (ext.extra_objects) extra_args = ext.extra_link_args or [] - # Bunch of fixing-up we have to do for Microsoft's linker. - if self.compiler.compiler_type == 'msvc': - self.msvc_prelink_hack(sources, ext, extra_args) self.compiler.link_shared_object ( objects, ext_filename, - libraries=ext.libraries, + libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, runtime_library_dirs=ext.runtime_library_dirs, extra_postargs=extra_args, + export_symbols=self.get_export_symbols(ext), debug=self.debug, build_temp=self.build_temp) @@ -511,44 +511,6 @@ class build_ext (Command): # find_swig () - - # -- Hooks 'n hacks ------------------------------------------------ - - def msvc_prelink_hack (self, sources, ext, extra_args): - - # XXX this is a kludge! Knowledge of specific compilers or - # platforms really doesn't belong here; in an ideal world, the - # CCompiler interface would provide access to everything in a - # compiler/linker system needs to build Python extensions, and - # we would just do everything nicely and cleanly through that - # interface. However, this is a not an ideal world and the - # CCompiler interface doesn't handle absolutely everything. - # Thus, kludges like this slip in occasionally. (This is no - # excuse for committing more platform- and compiler-specific - # kludges; they are to be avoided if possible!) - - def_file = ext.export_symbol_file - - if def_file is not None: - extra_args.append ('/DEF:' + def_file) - else: - modname = string.split (ext.name, '.')[-1] - extra_args.append('/export:init%s' % modname) - - # The MSVC linker generates .lib and .exp files, which cannot be - # suppressed by any linker switches. The .lib files may even be - # needed! Make sure they are generated in the temporary build - # directory. Since they have different names for debug and release - # builds, they can go into the same directory. - implib_file = os.path.join ( - self.implib_dir, - self.get_ext_libname (ext.name)) - extra_args.append ('/IMPLIB:' + implib_file) - self.mkpath (os.path.dirname (implib_file)) - - # msvc_prelink_hack () - - # -- Name generators ----------------------------------------------- # (extension names, filenames, whatever) @@ -579,4 +541,39 @@ class build_ext (Command): return apply (os.path.join, ext_path) + '_d.lib' return apply (os.path.join, ext_path) + '.lib' + + def get_export_symbols (self, ext): + """Return the list of symbols that a shared extension has to + export. This either uses 'ext.export_symbols' or, if it's not + provided, "init" + module_name. Only relevant on Windows, where + the .pyd file (DLL) must export the module "init" function. + """ + + # XXX what if 'export_symbols' defined but it doesn't contain + # "init" + module_name? Should we add it? warn? or just carry + # on doing nothing? + + if ext.export_symbols is None: + return ["init" + string.split(ext.name,'.')[-1]] + else: + return ext.export_symbols + + def get_libraries (self, ext): + """Return the list of libraries to link against when building a + shared extension. On most platforms, this is just 'ext.libraries'; + on Windows, we add the Python library (eg. python20.dll). + """ + # The python library is always needed on Windows. For MSVC, this + # is redundant, since the library is mentioned in a pragma in + # config.h that MSVC groks. The other Windows compilers all seem + # to need it mentioned explicitly, though, so that's what we do. + if sys.platform == "win32": + pythonlib = ("python%d%d" % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] + else: + return ext.libraries + # class build_ext -- cgit v1.2.1 From 7ef5e9f9cda6b4b01a7f64ef3b23052c0303e23f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 00:00:30 +0000 Subject: Patch from Rene Liebscher: generate an /IMPLIB: option to ensure that the linker leaves the (temporary) .lib file in the temporary dir. (Moved from 'msvc_prelink_hack()' method in build_ext.py.) --- msvccompiler.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index e58e6c10..b86e0b30 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -380,10 +380,22 @@ class MSVCCompiler (CCompiler) : ld_args = (ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]) + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + os.path.dirname(objects[0]), + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: - ld_args.extend (extra_postargs) + ld_args.extend(extra_postargs) print "link_shared_object():" print " output_filename =", output_filename -- cgit v1.2.1 From 247bbf98667922b35c599456d86afe9b69664773 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 00:01:56 +0000 Subject: Ditched some debugging prints. --- msvccompiler.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index b86e0b30..a1dedb0e 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -397,9 +397,6 @@ class MSVCCompiler (CCompiler) : if extra_postargs: ld_args.extend(extra_postargs) - print "link_shared_object():" - print " output_filename =", output_filename - print " mkpath'ing:", os.path.dirname (output_filename) self.mkpath (os.path.dirname (output_filename)) try: self.spawn ([self.link] + ld_args) -- cgit v1.2.1 From d43aeac154d817eefdde7f84243bd273d5542254 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 00:04:13 +0000 Subject: Removed 'export_symbol_file'. 'export_symbols' can be None (not sure this is a good idea: it's inconsistent with every other instance attribute of Extension). --- extension.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/extension.py b/extension.py index f0f68b93..95a2ece8 100644 --- a/extension.py +++ b/extension.py @@ -73,11 +73,6 @@ class Extension: used on all platforms, and not generally necessary for Python extensions, which typically export exactly one symbol: "init" + extension_name. - export_symbol_file : string - name of file that lists symbols to export; the format of this - file is platform- and compiler-specific. This is even more - gratuitous and unnecessary than 'export_symbols'; I'll be happy - when it goes away forever. """ def __init__ (self, name, sources, @@ -91,7 +86,6 @@ class Extension: extra_compile_args=None, extra_link_args=None, export_symbols=None, - export_symbol_file=None, ): assert type(name) is StringType, "'name' must be a string" @@ -111,7 +105,6 @@ class Extension: self.extra_objects = extra_objects or [] self.extra_compile_args = extra_compile_args or [] self.extra_link_args = extra_link_args or [] - self.export_symbols = export_symbols or [] - self.export_symbol_file = export_symbol_file + self.export_symbols = export_symbols # class Extension -- cgit v1.2.1 From 336876f5fbc5664836629ae225b680d31020eb55 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 00:37:32 +0000 Subject: Ditched 'abspath()' -- don't need 1.5.1 compatability hacks anymore. --- util.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/util.py b/util.py index 0bff3a5b..d69626e1 100644 --- a/util.py +++ b/util.py @@ -18,16 +18,6 @@ from distutils.dep_util import * from distutils.archive_util import * -# Need to define 'abspath()', because it was new with Python 1.5.2 -if hasattr (os.path, 'abspath'): - abspath = os.path.abspath -else: - def abspath(path): - if not os.path.isabs(path): - path = os.path.join(os.getcwd(), path) - return os.path.normpath(path) - - # More backwards compatibility hacks def extend (list, new_list): """Appends the list 'new_list' to 'list', just like the 'extend()' -- cgit v1.2.1 From b4ff40df511d1477c3457931578bb2ab9630ab1b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:03:23 +0000 Subject: Patch from Rene Liebscher. Some ugly changes, but supposedly this makes it so BCPPCompiler actually works, so I'm provisionally accepting it -- ugly and working is better than not working! Major changes: - normalize paths (apparently BC++ doesn't like slashes) - overhauled how we search for and specify libraries on the linker command-line - hacked up 'find_library_file()' so it knows about "debug" library naming convention as well as "bcpp_xxx.lib" -- the question is, is this a well-established and sensible convention? Also: - change to use 'util.write_file()' to write the .def file --- bcppcompiler.py | 99 ++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 29 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 6c9ac827..7daa597b 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,17 +11,6 @@ for the Borland C++ compiler. # someone should sit down and factor out the common code as # WindowsCCompiler! --GPW -# XXX Lyle reports that this doesn't quite work yet: -# """...but this is what I've got so far. The compile step works fine but -# when it runs the link step I get an "out of memory" failure. Since -# spawn() echoes the command it's trying to spawn, I can type the link line -# verbatim at the DOS prompt and it links the Windows DLL correctly -- so -# the syntax is correct. There's just some weird interaction going on when -# it tries to "spawn" the link process from within the setup.py script. I'm -# not really sure how to debug this one right off-hand; obviously there's -# nothing wrong with the "spawn()" function since it's working properly for -# the compile stage.""" - __revision__ = "$Id$" @@ -31,6 +20,7 @@ from distutils.errors import \ CompileError, LibError, LinkError from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options +from distutils.file_util import write_file class BCPPCompiler(CCompiler) : @@ -123,14 +113,15 @@ class BCPPCompiler(CCompiler) : elif ext in self._cpp_extensions: input_opt = "-P" + src = os.path.normpath(src) + obj = os.path.normpath(obj) + output_opt = "-o" + obj - - self.mkpath (os.path.dirname (obj)) + self.mkpath(os.path.dirname(obj)) # Compiler command line syntax is: "bcc32 [options] file(s)". # Note that the source file names must appear at the end of # the command line. - try: self.spawn ([self.cc] + compile_opts + pp_opts + [input_opt, output_opt] + @@ -212,6 +203,9 @@ class BCPPCompiler(CCompiler) : extra_postargs=None, build_temp=None): + # XXX this ignores 'build_temp'! should follow the lead of + # msvccompiler.py + (objects, output_dir) = self._fix_object_args (objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) @@ -226,33 +220,63 @@ class BCPPCompiler(CCompiler) : if self._need_link (objects, output_filename): if debug: - ldflags = self.ldflags_shared_debug + ld_args = self.ldflags_shared_debug[:] else: - ldflags = self.ldflags_shared + ld_args = self.ldflags_shared[:] + # Borland C++ has problems with '/' in paths + objects = map(os.path.normpath, objects) startup_obj = 'c0d32' + objects.insert(0, startup_obj) - libraries.append ('mypylib') + # either exchange python15.lib in the python libs directory against + # a Borland-like one, or create one with name bcpp_python15.lib + # there and remove the pragmas from config.h + #libraries.append ('mypylib') libraries.append ('import32') libraries.append ('cw32mt') # Create a temporary exports file for use by the linker head, tail = os.path.split (output_filename) modname, ext = os.path.splitext (tail) - def_file = os.path.join (build_temp, '%s.def' % modname) - f = open (def_file, 'w') - f.write ('EXPORTS\n') + temp_dir = os.path.dirname(objects[0]) # preserve tree structure + def_file = os.path.join (temp_dir, '%s.def' % modname) + contents = ['EXPORTS'] for sym in (export_symbols or []): - f.write (' %s=_%s\n' % (sym, sym)) - - ld_args = ldflags + [startup_obj] + objects + \ - [',%s,,' % output_filename] + \ - libraries + [',' + def_file] + contents.append(' %s=_%s' % (sym, sym)) + self.execute(write_file, (def_file, contents), + "writing %s" % def_file) + + # Start building command line flags and options. + + for l in library_dirs: + ld_args.append("/L%s" % os.path.normpath(l)) + + ld_args.extend(objects) # list of object files + + # name of dll file + ld_args.extend([',',output_filename]) + # no map file and start libraries + ld_args.extend([',', ',']) + + for lib in libraries: + # see if we find it and if there is a bcpp specific lib + # (bcpp_xxx.lib) + libfile = self.find_library_file(library_dirs, lib, debug) + if libfile is None: + ld_args.append(lib) + # probably a BCPP internal library -- don't warn + # self.warn('library %s not found.' % lib) + else: + # full name which prefers bcpp_xxx.lib over xxx.lib + ld_args.append(libfile) + # def file for export symbols + ld_args.extend([',',def_file]) if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: - ld_args.extend (extra_postargs) + ld_args.extend(extra_postargs) self.mkpath (os.path.dirname (output_filename)) try: @@ -325,15 +349,32 @@ class BCPPCompiler(CCompiler) : def runtime_library_dir_option (self, dir): raise DistutilsPlatformError, \ - "don't know how to set runtime library search path for MSVC++" + ("don't know how to set runtime library search path " + "for Borland C++") def library_option (self, lib): return self.library_filename (lib) - def find_library_file (self, dirs, lib): - + def find_library_file (self, dirs, lib, debug=0): + # find library file + # bcpp_xxx.lib is better than xxx.lib + # and xxx_d.lib is better than xxx.lib if debug is set for dir in dirs: + if debug: + libfile = os.path.join ( + dir, self.library_filename ("bcpp_" + lib + "_d")) + if os.path.exists (libfile): + return libfile + libfile = os.path.join ( + dir, self.library_filename ("bcpp_" + lib)) + if os.path.exists (libfile): + return libfile + if debug: + libfile = os.path.join ( + dir, self.library_filename(lib + '_d')) + if os.path.exists (libfile): + return libfile libfile = os.path.join (dir, self.library_filename (lib)) if os.path.exists (libfile): return libfile -- cgit v1.2.1 From 6904a1cecea7706215802d00436350ed15771192 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:08:02 +0000 Subject: Rene Liebscher: factor 'find_executable()' out of '_spawn_nt()'. --- spawn.py | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/spawn.py b/spawn.py index 86ea3dfb..651124d6 100644 --- a/spawn.py +++ b/spawn.py @@ -1,7 +1,9 @@ """distutils.spawn Provides the 'spawn()' function, a front-end to various platform- -specific functions for launching another program in a sub-process.""" +specific functions for launching another program in a sub-process. +Also provides the 'find_executable()' to search the path for a given +executable name. """ # created 1999/07/24, Greg Ward @@ -65,17 +67,8 @@ def _spawn_nt (cmd, executable = cmd[0] cmd = _nt_quote_args (cmd) if search_path: - paths = string.split( os.environ['PATH'], os.pathsep) - base,ext = os.path.splitext(executable) - if (ext != '.exe'): - executable = executable + '.exe' - if not os.path.isfile(executable): - paths.reverse() # go over the paths and keep the last one - for p in paths: - f = os.path.join( p, executable ) - if os.path.isfile ( f ): - # the file exists, we have a shot at spawn working - executable = f + # either we find one or it stays the same + executable = find_executable(executable) or executable if verbose: print string.join ([executable] + cmd[1:], ' ') if not dry_run: @@ -91,7 +84,6 @@ def _spawn_nt (cmd, raise DistutilsExecError, \ "command '%s' failed with exit status %d" % (cmd[0], rc) - def _spawn_posix (cmd, search_path=1, @@ -147,3 +139,28 @@ def _spawn_posix (cmd, "unknown error executing '%s': termination status %d" % \ (cmd[0], status) # _spawn_posix () + + +def find_executable(executable, path=None): + """Try to find 'executable' in the directories listed in 'path' (a + string listing directories separated by 'os.pathsep'; defaults to + os.environ['PATH']). Returns the complete filename or None if not + found. + """ + if path is None: + path = os.environ['PATH'] + paths = string.split(path, os.pathsep) + (base, ext) = os.path.splitext(executable) + if (sys.platform == 'win32') and (ext != '.exe'): + executable = executable + '.exe' + if not os.path.isfile(executable): + for p in paths: + f = os.path.join(p, executable) + if os.path.isfile(f): + # the file exists, we have a shot at spawn working + return f + return None + else: + return executable + +# find_executable() -- cgit v1.2.1 From 8be2f9c465909ad6d71cc1bb116df7f87fa25422 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:09:11 +0000 Subject: Rene Liebscher: deleted unneeded hard-coded assignments of CC, RANLIB, etc. in '_init_nt()' (they were kludges for CygwinCCompiler and no longer needed). --- sysconfig.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index b2aa3f2d..f6d941ac 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -267,24 +267,8 @@ def _init_nt(): g['INCLUDEPY'] = get_python_inc(plat_specific=0) g['SO'] = '.pyd' - g['exec_prefix'] = EXEC_PREFIX - - # These are needed for the CygwinCCompiler and Mingw32CCompiler - # classes, which are just UnixCCompiler classes that happen to work on - # Windows. UnixCCompiler expects to find these values in sysconfig, so - # here they are. The fact that other Windows compilers don't need - # these values is pure luck (hmmm). - - # XXX I think these are now unnecessary... - - g['CC'] = "cc" # not gcc? - g['RANLIB'] = "ranlib" - g['AR'] = "ar" - g['OPT'] = "-O2" - g['SO'] = ".pyd" - g['LDSHARED'] = "ld" - g['CCSHARED'] = "" g['EXE'] = ".exe" + g['exec_prefix'] = EXEC_PREFIX def _init_mac(): -- cgit v1.2.1 From cc5431ba2d4c6e6fb0ab740721ea8734ca9dc3b8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:31:56 +0000 Subject: Latest version from Rene Liebscher; major changes: - added big comment describing possible problems - look for and react to versions of gcc, ld, and dlltool; mainly this is done by the 'get_versions()' function and the CygwinCCompiler and Mingw32CCompiler constructors - move 'check_config_h()' to end of file and defer calling it until we need to (ie. in the CygwinCCompiler constructor) - lots of changes in 'link_shared_object()' -- mostly seems to be library and DLL stuff, but I don't follow it entirely --- cygwinccompiler.py | 337 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 230 insertions(+), 107 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 650627f5..3f9a5bd5 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -6,53 +6,54 @@ the Mingw32CCompiler class which handles the mingw32 port of GCC (same as cygwin in no-cygwin mode). """ +# problems: +# +# * if you use a msvc compiled python version (1.5.2) +# 1. you have to insert a __GNUC__ section in its config.h +# 2. you have to generate a import library for its dll +# - create a def-file for python??.dll +# - create a import library using +# dlltool --dllname python15.dll --def python15.def \ +# --output-lib libpython15.a +# +# see also http://starship.python.net/crew/kernr/mingw32/Notes.html +# +# * We use put export_symbols in a def-file, and don't use +# --export-all-symbols because it doesn't worked reliable in some +# tested configurations. And because other windows compilers also +# need their symbols specified this no serious problem. +# +# tested configurations: +# +# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works +# (after patching python's config.h and for C++ some other include files) +# see also http://starship.python.net/crew/kernr/mingw32/Notes.html +# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works +# (ld doesn't support -shared, so we use dllwrap) +# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now +# - its dllwrap doesn't work, there is a bug in binutils 2.10.90 +# see also ..... +# - using gcc -mdll instead dllwrap doesn't work without -static because +# it tries to link against dlls instead their import libraries. (If +# it finds the dll first.) +# By specifying -static we force ld to link against the import libraries, +# this is windows standard and there are normally not the necessary symbols +# in the dlls. + # created 2000/05/05, Rene Liebscher __revision__ = "$Id$" -import os,sys,string -from distutils import sysconfig +import os,sys from distutils.unixccompiler import UnixCCompiler - -# Because these compilers aren't configured in Python's config.h file by -# default we should at least warn the user if he is using a unmodified -# version. - -def check_config_h(): - """Checks if the GCC compiler is mentioned in config.h. If it is not, - compiling probably doesn't work, so print a warning to stderr. - """ - - # XXX the result of the check should be returned! - - from distutils import sysconfig - import string,sys - try: - # It would probably better to read single lines to search. - # But we do this only once, and it is fast enough - f=open(sysconfig.get_config_h_filename()) - s=f.read() - f.close() - try: - # is somewhere a #ifdef __GNUC__ or something similar - string.index(s,"__GNUC__") - except ValueError: - sys.stderr.write ("warning: "+ - "Python's config.h doesn't seem to support your compiler.\n") - except IOError: - # if we can't read this file, we cannot say it is wrong - # the compiler will complain later about this file as missing - pass - - -# This is called when the module is imported, so we make this check only once -# XXX why not make it only when the compiler is needed? -check_config_h() - +from distutils.file_util import write_file class CygwinCCompiler (UnixCCompiler): compiler_type = 'cygwin' + gcc_version = None + dllwrap_version = None + ld_version = None def __init__ (self, verbose=0, @@ -61,22 +62,45 @@ class CygwinCCompiler (UnixCCompiler): UnixCCompiler.__init__ (self, verbose, dry_run, force) + if check_config_h()<=0: + self.warn( + "Python's config.h doesn't seem to support your compiler. " + "Compiling may fail because of undefined preprocessor macros.") + + (self.gcc_version, self.ld_version, self.dllwrap_version) = \ + get_versions() + sys.stderr.write(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % + (self.gcc_version, + self.ld_version, + self.dllwrap_version) ) + + # ld_version >= "2.10.90" should also be able to use + # gcc -mdll instead of dllwrap + # Older dllwraps had own version numbers, newer ones use the + # same as the rest of binutils ( also ld ) + # dllwrap 2.10.90 is buggy + if self.ld_version >= "2.10.90": + self.linker = "gcc" + else: + self.linker = "dllwrap" + # Hard-code GCC because that's what this is all about. # XXX optimization, warnings etc. should be customizable. - self.set_executables(compiler='gcc -O -Wall', - compiler_so='gcc -O -Wall', - linker_exe='gcc', - linker_so='dllwrap --target=i386-cygwin32') + self.set_executables(compiler='gcc -mcygwin -O -Wall', + compiler_so='gcc -mcygwin -mdll -O -Wall', + linker_exe='gcc -mcygwin', + linker_so=('%s -mcygwin -mdll -static' % + self.linker)) # cygwin and mingw32 need different sets of libraries - self.dll_libraries=[ - # cygwin shouldn't need msvcrt, - # but without the dll's will crash - # ( gcc version 2.91.57 ) - # perhaps something about initialization - # mingw32 needs it in all cases - "msvcrt" - ] + if self.gcc_version == "2.91.57": + # cygwin shouldn't need msvcrt, but without the dlls will crash + # (gcc version 2.91.57) -- perhaps something about initialization + self.dll_libraries=["msvcrt"] + self.warn( + "Consider upgrading to a newer version of gcc") + else: + self.dll_libraries=[] # __init__ () @@ -93,64 +117,67 @@ class CygwinCCompiler (UnixCCompiler): extra_postargs=None, build_temp=None): - if libraries == None: - libraries = [] + # use separate copies, so can modify the lists + extra_preargs = list(extra_preargs or []) + libraries = list(libraries or []) - # Additional libraries: the python library is always needed on - # Windows we need the python version without the dot, eg. '15' - - pythonlib = ("python%d%d" % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - libraries.append(pythonlib) + # Additional libraries libraries.extend(self.dll_libraries) + + # we want to put some files in the same directory as the + # object files are, build_temp doesn't help much - # name of extension + # where are the object files + temp_dir = os.path.dirname(objects[0]) - # XXX WRONG WRONG WRONG - # this is NOT the place to make guesses about Python namespaces; - # that MUST be done in build_ext.py + # name of dll to give the helper files (def, lib, exp) the same name + (dll_name, dll_extension) = os.path.splitext( + os.path.basename(output_filename)) - if not debug: - ext_name = os.path.basename(output_filename)[:-len(".pyd")] - else: - ext_name = os.path.basename(output_filename)[:-len("_d.pyd")] + # generate the filenames for these files + def_file = None # this will be done later, if necessary + exp_file = os.path.join(temp_dir, dll_name + ".exp") + lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") - def_file = os.path.join(build_temp, ext_name + ".def") - #exp_file = os.path.join(build_temp, ext_name + ".exp") - #lib_file = os.path.join(build_temp, 'lib' + ext_name + ".a") - - # Make .def file - # (It would probably better to check if we really need this, - # but for this we had to insert some unchanged parts of - # UnixCCompiler, and this is not what we want.) - f = open(def_file,"w") - f.write("EXPORTS\n") # intro - if export_symbols == None: - # export a function "init" + ext_name - f.write("init" + ext_name + "\n") + #extra_preargs.append("--verbose") + if self.linker == "dllwrap": + extra_preargs.extend([#"--output-exp",exp_file, + "--output-lib",lib_file, + ]) else: - # if there are more symbols to export write them into f + # doesn't work: bfd_close build\...\libfoo.a: Invalid operation + extra_preargs.extend([#"-Wl,--out-implib,%s" % lib_file, + ]) + + # check what we got in export_symbols + if export_symbols is not None: + # Make .def file + # (It would probably better to check if we really need this, + # but for this we had to insert some unchanged parts of + # UnixCCompiler, and this is not what we want.) + def_file = os.path.join(temp_dir, dll_name + ".def") + contents = [ + "LIBRARY %s" % os.path.basename(output_filename), + "EXPORTS"] for sym in export_symbols: - f.write(sym+"\n") - f.close() - - if extra_preargs == None: - extra_preargs = [] - - extra_preargs = extra_preargs + [ - #"--verbose", - #"--output-exp",exp_file, - #"--output-lib",lib_file, - "--def",def_file - ] - + contents.append(sym) + self.execute(write_file, (def_file, contents), + "writing %s" % def_file) + + if def_file: + if self.linker == "dllwrap": + # for dllwrap we have to use a special option + extra_preargs.append("--def") + # for gcc/ld it is specified as any other object file + extra_preargs.append(def_file) + # who wants symbols and a many times larger output file # should explicitly switch the debug mode on - # otherwise we let dllwrap strip the output file + # otherwise we let dllwrap/ld strip the output file # (On my machine unstripped_file = stripped_file + 254KB # 10KB < stripped_file < ??100KB ) if not debug: - extra_preargs = extra_preargs + ["-s"] + extra_preargs.append("-s") UnixCCompiler.link_shared_object(self, objects, @@ -159,7 +186,7 @@ class CygwinCCompiler (UnixCCompiler): libraries, library_dirs, runtime_library_dirs, - None, # export_symbols, we do this with our def-file + None, # export_symbols, we do this in our def-file debug, extra_preargs, extra_postargs, @@ -181,19 +208,115 @@ class Mingw32CCompiler (CygwinCCompiler): force=0): CygwinCCompiler.__init__ (self, verbose, dry_run, force) + + # A real mingw32 doesn't need to specify a different entry point, + # but cygwin 2.91.57 in no-cygwin-mode needs it. + if self.gcc_version <= "2.91.57": + entry_point = '--entry _DllMain@12' + else: + entry_point = '' self.set_executables(compiler='gcc -mno-cygwin -O -Wall', - compiler_so='gcc -mno-cygwin -O -Wall', + compiler_so='gcc -mno-cygwin -mdll -O -Wall', linker_exe='gcc -mno-cygwin', - linker_so='dllwrap' - + ' --target=i386-mingw32' - + ' --entry _DllMain@12') - # mingw32 doesn't really need 'target' and cygwin too (it seems, - # it is enough to specify a different entry point) - - # no additional libraries need - # (only msvcrt, which is already added by CygwinCCompiler) - + linker_so='%s -mno-cygwin -mdll -static %s' + % (self.linker, entry_point)) + # Maybe we should also append -mthreads, but then the finished + # dlls need another dll (mingwm10.dll see Mingw32 docs) + # (-mthreads: Support thread-safe exception handling on `Mingw32') + + # no additional libraries needed + self.dll_libraries=[] + # __init__ () - + # class Mingw32CCompiler + +# Because these compilers aren't configured in Python's config.h file by +# default, we should at least warn the user if he is using a unmodified +# version. + +def check_config_h(): + """Checks if the GCC compiler is mentioned in config.h. If it is not, + compiling probably doesn't work. + """ + # return values + # 2: OK, python was compiled with GCC + # 1: OK, python's config.h mentions __GCC__ + # 0: uncertain, because we couldn't check it + # -1: probably not OK, because we didn't found it in config.h + # You could check check_config_h()>0 => OK + + from distutils import sysconfig + import string,sys + # if sys.version contains GCC then python was compiled with + # GCC, and the config.h file should be OK + if -1 == string.find(sys.version,"GCC"): + pass # go to the next test + else: + return 2 + + try: + # It would probably better to read single lines to search. + # But we do this only once, and it is fast enough + f=open(sysconfig.get_config_h_filename()) + s=f.read() + f.close() + + # is somewhere a #ifdef __GNUC__ or something similar + if -1 == string.find(s,"__GNUC__"): + return -1 + else: + return 1 + except IOError: + # if we can't read this file, we cannot say it is wrong + # the compiler will complain later about this file as missing + pass + return 0 + +def get_versions(): + """ Try to find out the versions of gcc, ld and dllwrap. + If not possible it returns None for it. + """ + from distutils.version import StrictVersion + from distutils.spawn import find_executable + import re + + gcc_exe = find_executable('gcc') + if gcc_exe: + out = os.popen(gcc_exe + ' -dumpversion','r') + out_string = out.read() + out.close() + result = re.search('(\d+\.\d+\.\d+)',out_string) + if result: + gcc_version = StrictVersion(result.group(1)) + else: + gcc_version = None + else: + gcc_version = None + ld_exe = find_executable('ld') + if ld_exe: + out = os.popen(ld_exe + ' -v','r') + out_string = out.read() + out.close() + result = re.search('(\d+\.\d+\.\d+)',out_string) + if result: + ld_version = StrictVersion(result.group(1)) + else: + ld_version = None + else: + ld_version = None + dllwrap_exe = find_executable('dllwrap') + if dllwrap_exe: + out = os.popen(dllwrap_exe + ' --version','r') + out_string = out.read() + out.close() + result = re.search(' (\d+\.\d+\.\d+)',out_string) + if result: + dllwrap_version = StrictVersion(result.group(1)) + else: + dllwrap_version = None + else: + dllwrap_version = None + return (gcc_version, ld_version, dllwrap_version) + -- cgit v1.2.1 From 8370736a2f17bc52b2aae9f0497cbf39a4851987 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:34:18 +0000 Subject: Rene Liebscher: fix 'skipping byte-compilation' message for grammatical consistency. --- command/install_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index d866d8cc..1c15db14 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -74,7 +74,7 @@ class install_lib (Command): out_fn = f + (__debug__ and "c" or "o") compile_msg = "byte-compiling %s to %s" % \ (f, os.path.basename (out_fn)) - skip_msg = "byte-compilation of %s skipped" % f + skip_msg = "skipping byte-compilation of %s" % f self.make_file (f, out_fn, compile, (f,), compile_msg, skip_msg) # run () -- cgit v1.2.1 From 9624e74733a8888f50d4cc7bef25e7e3c78de50e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:37:30 +0000 Subject: Added the 'execute()' function (moved here from cmd.py with minor tweakage). --- util.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/util.py b/util.py index d69626e1..37cd4b55 100644 --- a/util.py +++ b/util.py @@ -223,3 +223,29 @@ def split_quoted (s): return words # split_quoted () + + +def execute (func, args, msg=None, verbose=0, dry_run=0): + """Perform some action that affects the outside world (eg. by writing + to the filesystem). Such actions are special because they are disabled + by the 'dry_run' flag, and announce themselves if 'verbose' is true. + This method takes care of all that bureaucracy for you; all you have to + do is supply the function to call and an argument tuple for it (to + embody the "external action" being performed), and an optional message + to print. + """ + # Generate a message if we weren't passed one + if msg is None: + msg = "%s%s" % (func.__name__, `args`) + if msg[-2:] == ',)': # correct for singleton tuple + msg = msg[0:-2] + ')' + + # Print it if verbosity level is high enough + if verbose: + print msg + + # And do it, as long as we're not in dry-run mode + if not dry_run: + apply(func, args) + +# execute() -- cgit v1.2.1 From 20b387f6de0ca9835df40e2b4728a0bd9e629521 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:37:53 +0000 Subject: Replaced 'execute()' method with a thin wrapper around 'util.execute()'. --- cmd.py | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/cmd.py b/cmd.py index 8beb5d44..c905edec 100644 --- a/cmd.py +++ b/cmd.py @@ -318,31 +318,7 @@ class Command: def execute (self, func, args, msg=None, level=1): - """Perform some action that affects the outside world (eg. by - writing to the filesystem). Such actions are special because they - should be disabled by the "dry run" flag, and should announce - themselves if the current verbosity level is high enough. This - method takes care of all that bureaucracy for you; all you have to - do is supply the function to call and an argument tuple for it (to - embody the "external action" being performed), a message to print - if the verbosity level is high enough, and an optional verbosity - threshold. - """ - - # Generate a message if we weren't passed one - if msg is None: - msg = "%s %s" % (func.__name__, `args`) - if msg[-2:] == ',)': # correct for singleton tuple - msg = msg[0:-2] + ')' - - # Print it if verbosity level is high enough - self.announce (msg, level) - - # And do it, as long as we're not in dry-run mode - if not self.dry_run: - apply (func, args) - - # execute() + util.execute(func, args, msg, self.verbose >= level, self.dry_run) def mkpath (self, name, mode=0777): -- cgit v1.2.1 From edbbc73e92e3889f5f3ea0ac9f5a2e9445c5751d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:38:20 +0000 Subject: Added 'execute()' method, a thin wrapper around 'util.execute() (just like the one in cmd.py). --- ccompiler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index eb6200f6..d8d8ab91 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,7 +15,7 @@ from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath from distutils.dep_util import newer_pairwise, newer_group -from distutils.util import split_quoted +from distutils.util import split_quoted, execute class CCompiler: @@ -784,6 +784,9 @@ class CCompiler: def warn (self, msg): sys.stderr.write ("warning: %s\n" % msg) + def execute (self, func, args, msg=None, level=1): + execute(func, args, msg, self.verbose >= level, self.dry_run) + def spawn (self, cmd): spawn (cmd, verbose=self.verbose, dry_run=self.dry_run) -- cgit v1.2.1 From bb7acec3a19a3e0488ef95e2d5e41c0df0ed446b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:44:44 +0000 Subject: Added 'wininst' to the 'format_commands' list, so it's included in the --help-formats output. Also moved that list up so it's more obvious when adding formats. --- command/bdist.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 100563a9..1c862d58 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -52,17 +52,20 @@ class bdist (Command): default_format = { 'posix': 'gztar', 'nt': 'zip', } + # Establish the preferred order (for the --help-formats option). + format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', + 'wininst', 'zip'] + + # And the real information. format_command = { 'rpm': ('bdist_rpm', "RPM distribution"), 'gztar': ('bdist_dumb', "gzip'ed tar file"), 'bztar': ('bdist_dumb', "bzip2'ed tar file"), 'ztar': ('bdist_dumb', "compressed tar file"), 'tar': ('bdist_dumb', "tar file"), - 'zip': ('bdist_dumb', "ZIP file"), 'wininst': ('bdist_wininst', "Windows executable installer"), + 'zip': ('bdist_dumb', "ZIP file"), } - # establish the preferred order - format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', 'zip'] def initialize_options (self): -- cgit v1.2.1 From a509be9cada73fce73f71a7cf74a175eac6d530a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:49:40 +0000 Subject: Typo fix. --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index f6d941ac..22463277 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -77,7 +77,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): return prefix elif os.name == "mac": - if platform_specific: + if plat_specific: if standard_lib: return os.path.join(EXEC_PREFIX, "Mac", "Plugins") else: -- cgit v1.2.1 From e989444d5f0c7def7be57bbfa1d1529298fa1b07 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 4 Aug 2000 01:28:39 +0000 Subject: Added 'debug' flag to 'find_library_file()'. --- ccompiler.py | 8 +++++--- unixccompiler.py | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index d8d8ab91..141af796 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -682,10 +682,12 @@ class CCompiler: """ raise NotImplementedError - def find_library_file (self, dirs, lib): + def find_library_file (self, dirs, lib, debug=0): """Search the specified list of directories for a static or shared - library file 'lib' and return the full path to that file. Return - None if it wasn't found in any of the specified directories. + library file 'lib' and return the full path to that file. If + 'debug' true, look for a debugging version (if that makes sense on + the current platform). Return None if 'lib' wasn't found in any of + the specified directories. """ raise NotImplementedError diff --git a/unixccompiler.py b/unixccompiler.py index 799560c3..93398501 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -317,7 +317,7 @@ class UnixCCompiler (CCompiler): return "-l" + lib - def find_library_file (self, dirs, lib): + def find_library_file (self, dirs, lib, debug=0): for dir in dirs: shared = os.path.join ( -- cgit v1.2.1 From be27b1c8bcaa43999f0008d56c4f5c502495e346 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 4 Aug 2000 01:29:27 +0000 Subject: Added 'debug' flag to 'find_library_file()', and changed code to handle it. --- msvccompiler.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index a1dedb0e..eecbb620 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -474,13 +474,18 @@ class MSVCCompiler (CCompiler) : return self.library_filename (lib) - def find_library_file (self, dirs, lib): - + def find_library_file (self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] for dir in dirs: - libfile = os.path.join (dir, self.library_filename (lib)) - if os.path.exists (libfile): - return libfile - + for name in try_names: + libfile = os.path.join(dir, self.library_filename (name)) + if os.path.exists(libfile): + return libfile else: # Oops, didn't find it in *any* of 'dirs' return None -- cgit v1.2.1 From 8e0f15f62975ef32643724e05c08eebf8376db4b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 4 Aug 2000 01:30:03 +0000 Subject: Rewrote 'find_library_file()' much more cleanly (and consistently with MSVCCompiler's version, to aid in factoring common code out of the two classes when the time comes). --- bcppcompiler.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 7daa597b..1d897de6 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -357,28 +357,26 @@ class BCPPCompiler(CCompiler) : def find_library_file (self, dirs, lib, debug=0): - # find library file + # List of effective library names to try, in order of preference: # bcpp_xxx.lib is better than xxx.lib # and xxx_d.lib is better than xxx.lib if debug is set + # + # The "bcpp_" prefix is to handle a Python installation for people + # with multiple compilers (primarily Distutils hackers, I suspect + # ;-). The idea is they'd have one static library for each + # compiler they care about, since (almost?) every Windows compiler + # seems to have a different format for static libraries. + if debug: + dlib = (lib + "_d") + try_names = ("bcpp_" + dlib, "bcpp_" + lib, dlib, lib) + else: + try_names = ("bcpp_" + lib, lib) + for dir in dirs: - if debug: - libfile = os.path.join ( - dir, self.library_filename ("bcpp_" + lib + "_d")) - if os.path.exists (libfile): + for name in try_names: + libfile = os.path.join(dir, self.library_filename(name)) + if os.path.exists(libfile): return libfile - libfile = os.path.join ( - dir, self.library_filename ("bcpp_" + lib)) - if os.path.exists (libfile): - return libfile - if debug: - libfile = os.path.join ( - dir, self.library_filename(lib + '_d')) - if os.path.exists (libfile): - return libfile - libfile = os.path.join (dir, self.library_filename (lib)) - if os.path.exists (libfile): - return libfile - else: # Oops, didn't find it in *any* of 'dirs' return None -- cgit v1.2.1 From 7555345e8cd473cae9a0b4ee91fb265885bb35c5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 4 Aug 2000 01:31:13 +0000 Subject: Added 'debug_print()'. --- ccompiler.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ccompiler.py b/ccompiler.py index 141af796..ce3f2be6 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -783,6 +783,11 @@ class CCompiler: if self.verbose >= level: print msg + def debug_print (self, msg): + from distutils.core import DEBUG + if DEBUG: + print msg + def warn (self, msg): sys.stderr.write ("warning: %s\n" % msg) -- cgit v1.2.1 From 242c59cb2be299e3229b9bcde18dd47eff67fd4d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Aug 2000 01:25:24 +0000 Subject: Drop the 'extend()' function -- old 1.5.1 compatibility hack that wasn't actually used anywhere. Drop the "from xxx_util import*" backwards compability hacks. --- util.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/util.py b/util.py index 37cd4b55..96266d26 100644 --- a/util.py +++ b/util.py @@ -11,26 +11,6 @@ import sys, os, string, re, shutil from distutils.errors import * from distutils.spawn import spawn -# for backwards compatibility: -from distutils.file_util import * -from distutils.dir_util import * -from distutils.dep_util import * -from distutils.archive_util import * - - -# More backwards compatibility hacks -def extend (list, new_list): - """Appends the list 'new_list' to 'list', just like the 'extend()' - list method does in Python 1.5.2 -- but this works on earlier - versions of Python too.""" - - if hasattr (list, 'extend'): - list.extend (new_list) - else: - list[len(list):] = new_list - -# extend () - def get_platform (): """Return a string (suitable for tacking onto directory names) that -- cgit v1.2.1 From 177d3705cc1ffcd1b068776baea88dd5f44259cb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Aug 2000 01:31:54 +0000 Subject: Fixed imports from '*util' modules to not just import everything from util. --- command/bdist_dumb.py | 3 ++- command/bdist_rpm.py | 3 ++- command/bdist_wininst.py | 3 ++- command/clean.py | 2 +- command/install.py | 3 ++- command/install_lib.py | 2 +- command/sdist.py | 18 ++++++++---------- 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 805aa0a9..e93c6b72 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -10,7 +10,8 @@ __revision__ = "$Id$" import os from distutils.core import Command -from distutils.util import get_platform, create_tree, remove_tree +from distutils.util import get_platform +from distutils.dir_util import create_tree, remove_tree from distutils.errors import * class bdist_dumb (Command): diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 3302eea2..e45d7a36 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -10,7 +10,8 @@ __revision__ = "$Id$" import os, string from types import * from distutils.core import Command, DEBUG -from distutils.util import get_platform, write_file +from distutils.util import get_platform +from distutils.file_util import write_file from distutils.errors import * class bdist_rpm (Command): diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 0c53bf9e..38e973b6 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -9,7 +9,8 @@ __revision__ = "$Id$" import sys, os, string from distutils.core import Command -from distutils.util import get_platform, create_tree, remove_tree +from distutils.util import get_platform +from distutils.dir_util import create_tree, remove_tree from distutils.errors import * class bdist_wininst (Command): diff --git a/command/clean.py b/command/clean.py index 31147b58..1d8c7b22 100644 --- a/command/clean.py +++ b/command/clean.py @@ -8,7 +8,7 @@ __revision__ = "$Id$" import os from distutils.core import Command -from distutils.util import remove_tree +from distutils.dir_util import remove_tree class clean (Command): diff --git a/command/install.py b/command/install.py index 1be49046..6385fdc2 100644 --- a/command/install.py +++ b/command/install.py @@ -10,7 +10,8 @@ import sys, os, string from types import * from distutils.core import Command, DEBUG from distutils import sysconfig -from distutils.util import write_file, convert_path, subst_vars, change_root +from distutils.file_util import write_file +from distutils.util import convert_path, subst_vars, change_root from distutils.errors import DistutilsOptionError from glob import glob diff --git a/command/install_lib.py b/command/install_lib.py index 1c15db14..879a7d00 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -4,7 +4,7 @@ __revision__ = "$Id$" import sys, os, string from distutils.core import Command -from distutils.util import copy_tree +from distutils.dir_util import copy_tree class install_lib (Command): diff --git a/command/sdist.py b/command/sdist.py index 4765d7fa..b2d7a86f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -10,9 +10,7 @@ import sys, os, string from types import * from glob import glob from distutils.core import Command -from distutils.util import \ - create_tree, remove_tree, newer, write_file, \ - check_archive_formats +from distutils import dir_util, dep_util, file_util, archive_util from distutils.text_file import TextFile from distutils.errors import * from distutils.filelist import FileList @@ -117,7 +115,7 @@ class sdist (Command): "don't know how to create source distributions " + \ "on platform %s" % os.name - bad_format = check_archive_formats (self.formats) + bad_format = archive_util.check_archive_formats (self.formats) if bad_format: raise DistutilsOptionError, \ "unknown archive format '%s'" % bad_format @@ -195,7 +193,7 @@ class sdist (Command): # manifest; if so, we'll regenerate the manifest. template_exists = os.path.isfile(self.template) if template_exists: - template_newer = newer(self.template, self.manifest) + template_newer = dep_util.newer(self.template, self.manifest) # The contents of the manifest file almost certainly depend on the # setup script as well as the manifest template -- so if the setup @@ -204,7 +202,7 @@ class sdist (Command): # manifest, but there's no template -- which will happen if the # developer elects to generate a manifest some other way -- then we # can't regenerate the manifest, so we don't.) - setup_newer = newer(sys.argv[0], self.manifest) + setup_newer = dep_util.newer(sys.argv[0], self.manifest) # cases: # 1) no manifest, template exists: generate manifest @@ -368,7 +366,7 @@ class sdist (Command): by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. """ - self.execute(write_file, + self.execute(file_util.write_file, (self.manifest, self.filelist.files), "writing manifest file '%s'" % self.manifest) @@ -404,8 +402,8 @@ class sdist (Command): """ # Create all the directories under 'base_dir' necessary to # put 'files' there. - create_tree (base_dir, files, - verbose=self.verbose, dry_run=self.dry_run) + dir_util.create_tree (base_dir, files, + verbose=self.verbose, dry_run=self.dry_run) # And walk over the list of files, either making a hard link (if # os.link exists) to each one that doesn't already exist in its @@ -453,7 +451,7 @@ class sdist (Command): self.archive_files = archive_files if not self.keep_tree: - remove_tree (base_dir, self.verbose, self.dry_run) + dir_util.remove_tree (base_dir, self.verbose, self.dry_run) def get_archive_files (self): """Return the list of archive files created when the command -- cgit v1.2.1 From d6bdb1ca69a126e1c651d06d304c283ca304254a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 7 Aug 2000 00:45:51 +0000 Subject: Fix so we clear or reinitialize various data structures before populating (allows the same FancyGetopt object to be used multiple times with different option tables). --- fancy_getopt.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fancy_getopt.py b/fancy_getopt.py index 6adfc819..a62bc0df 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -93,6 +93,7 @@ class FancyGetopt: def _build_index (self): + self.option_index.clear() for option in self.option_table: self.option_index[option[0]] = option @@ -153,6 +154,10 @@ class FancyGetopt: the option table. Called by 'getopt()' before it can do anything worthwhile.""" + self.long_opts = [] + self.short_opts = [] + self.short2long.clear() + for option in self.option_table: try: (long, short, help) = option -- cgit v1.2.1 From f777456bab4933786d6890e00ccd603912e382b1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 7 Aug 2000 00:48:04 +0000 Subject: Fix so the 'install_libbase' directory -- where .pth files are installed -- participates in the "--root" hack, ie. it also has a new root directory hacked on at the very last minute (essential if the .pth file is to be included in an RPM or other smart installer!). --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 6385fdc2..2a59e16b 100644 --- a/command/install.py +++ b/command/install.py @@ -273,7 +273,7 @@ class install (Command): # If a new root directory was supplied, make all the installation # dirs relative to it. if self.root is not None: - for name in ('lib', 'purelib', 'platlib', + for name in ('libbase', 'lib', 'purelib', 'platlib', 'scripts', 'data', 'headers'): attr = "install_" + name new_val = change_root (self.root, getattr (self, attr)) -- cgit v1.2.1 From 5dcbeda034a291131d3b1106ea80896749366a9e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Aug 2000 14:38:13 +0000 Subject: Fix so 'split_quoted()' handles any whitespace delimiter (not just space). --- util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 96266d26..2487f6da 100644 --- a/util.py +++ b/util.py @@ -139,7 +139,7 @@ def grok_environment_error (exc, prefix="error: "): # Needed by 'split_quoted()' -_wordchars_re = re.compile(r'[^\\\'\"\ ]*') +_wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') @@ -169,7 +169,7 @@ def split_quoted (s): words.append(s[:end]) break - if s[end] == ' ': # unescaped, unquoted space: now + if s[end] in string.whitespace: # unescaped, unquoted whitespace: now words.append(s[:end]) # we definitely have a word delimiter s = string.lstrip(s[end:]) pos = 0 -- cgit v1.2.1 From bb6a4ba2745a9747b1a152f6d6a1dbf5cc20574f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:36:47 +0000 Subject: Fix references to functions formerly imported from 'util'. --- cmd.py | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/cmd.py b/cmd.py index c905edec..474f8f32 100644 --- a/cmd.py +++ b/cmd.py @@ -12,7 +12,7 @@ __revision__ = "$Id$" import sys, os, string, re from types import * from distutils.errors import * -from distutils import util +from distutils import util, dir_util, file_util, archive_util, dep_util class Command: @@ -322,8 +322,8 @@ class Command: def mkpath (self, name, mode=0777): - util.mkpath (name, mode, - self.verbose, self.dry_run) + dir_util.mkpath(name, mode, + self.verbose, self.dry_run) def copy_file (self, infile, outfile, @@ -332,12 +332,13 @@ class Command: former two default to whatever is in the Distribution object, and the latter defaults to false for commands that don't define it.)""" - return util.copy_file (infile, outfile, - preserve_mode, preserve_times, - not self.force, - link, - self.verbose >= level, - self.dry_run) + return file_util.copy_file( + infile, outfile, + preserve_mode, preserve_times, + not self.force, + link, + self.verbose >= level, + self.dry_run) def copy_tree (self, infile, outfile, @@ -346,18 +347,19 @@ class Command: """Copy an entire directory tree respecting verbose, dry-run, and force flags. """ - return util.copy_tree (infile, outfile, - preserve_mode,preserve_times,preserve_symlinks, - not self.force, - self.verbose >= level, - self.dry_run) + return dir_util.copy_tree( + infile, outfile, + preserve_mode,preserve_times,preserve_symlinks, + not self.force, + self.verbose >= level, + self.dry_run) def move_file (self, src, dst, level=1): """Move a file respecting verbose and dry-run flags.""" - return util.move_file (src, dst, - self.verbose >= level, - self.dry_run) + return file_util.move_file (src, dst, + self.verbose >= level, + self.dry_run) def spawn (self, cmd, search_path=1, level=1): @@ -370,8 +372,9 @@ class Command: def make_archive (self, base_name, format, root_dir=None, base_dir=None): - return util.make_archive (base_name, format, root_dir, base_dir, - self.verbose, self.dry_run) + return archive_util.make_archive( + base_name, format, root_dir, base_dir, + self.verbose, self.dry_run) def make_file (self, infiles, outfile, func, args, @@ -401,7 +404,7 @@ class Command: # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if self.force or util.newer_group (infiles, outfile): + if self.force or dep_util.newer_group (infiles, outfile): self.execute (func, args, exec_msg, level) # Otherwise, print the "skip" message -- cgit v1.2.1 From 5351623c97c7a5adc612074a46d601dbd9f948c5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:38:58 +0000 Subject: Typo fix in docstring. --- extension.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extension.py b/extension.py index 95a2ece8..f0021d0e 100644 --- a/extension.py +++ b/extension.py @@ -23,8 +23,7 @@ from types import * class Extension: """Just a collection of attributes that describes an extension module and everything needed to build it (hopefully in a portable - way, but there are hooks that let you can be as unportable as you - need). + way, but there are hooks that let you be as unportable as you need). Instance attributes: name : string -- cgit v1.2.1 From f04ac5dd09157b550218a607a0721aa1bb86e821 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:41:40 +0000 Subject: Rene Liebscher: ext.export_symbols is now always a list (added 'or []'). --- extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.py b/extension.py index f0021d0e..1d3112b6 100644 --- a/extension.py +++ b/extension.py @@ -104,6 +104,6 @@ class Extension: self.extra_objects = extra_objects or [] self.extra_compile_args = extra_compile_args or [] self.extra_link_args = extra_link_args or [] - self.export_symbols = export_symbols + self.export_symbols = export_symbols or [] # class Extension -- cgit v1.2.1 From dff17c86072b76f6491300166e4b9670149ddd9b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:42:35 +0000 Subject: get_export_symbols() changed, adds now module init function if not given by the user. --- command/build_ext.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 1ffe3234..aca6dac4 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -549,14 +549,10 @@ class build_ext (Command): the .pyd file (DLL) must export the module "init" function. """ - # XXX what if 'export_symbols' defined but it doesn't contain - # "init" + module_name? Should we add it? warn? or just carry - # on doing nothing? - - if ext.export_symbols is None: - return ["init" + string.split(ext.name,'.')[-1]] - else: - return ext.export_symbols + initfunc_name = "init" + string.split(ext.name,'.')[-1] + if initfunc_name not in ext.export_symbols: + ext.export_symbols.append(initfunc_name) + return ext.export_symbols def get_libraries (self, ext): """Return the list of libraries to link against when building a -- cgit v1.2.1 From 9ef0502074aa149aa35ac81368f89253308a8012 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:43:16 +0000 Subject: Rene Liebscher: * changed some list.extend([...]) to list.append(...) * added '/g0' to compiler_options, so compiler doesn't stop after 100 warnings --- bcppcompiler.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 1d897de6..93049024 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -67,8 +67,8 @@ class BCPPCompiler(CCompiler) : self.lib = "tlib.exe" self.preprocess_options = None - self.compile_options = ['/tWM', '/O2', '/q'] - self.compile_options_debug = ['/tWM', '/Od', '/q'] + self.compile_options = ['/tWM', '/O2', '/q', '/g0'] + self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0'] self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x'] self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x'] @@ -232,7 +232,6 @@ class BCPPCompiler(CCompiler) : # either exchange python15.lib in the python libs directory against # a Borland-like one, or create one with name bcpp_python15.lib # there and remove the pragmas from config.h - #libraries.append ('mypylib') libraries.append ('import32') libraries.append ('cw32mt') @@ -257,7 +256,7 @@ class BCPPCompiler(CCompiler) : # name of dll file ld_args.extend([',',output_filename]) # no map file and start libraries - ld_args.extend([',', ',']) + ld_args.append(',,') for lib in libraries: # see if we find it and if there is a bcpp specific lib -- cgit v1.2.1 From 2c9c53b17589f71e434be1a8dae4ead6dd6e3f1e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:43:56 +0000 Subject: Rene Liebscher: * use self.debug_print() for debug messages * uses now copy.copy() to copy lists * added 'shared_lib_extension=".dll"', ... , this is necessary if you want use the compiler class outside of the standard distutils build process. * changed result type of check_config_h() from int to string --- cygwinccompiler.py | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 3f9a5bd5..da0d60b0 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -44,16 +44,19 @@ cygwin in no-cygwin mode). __revision__ = "$Id$" -import os,sys +import os,sys,copy from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file class CygwinCCompiler (UnixCCompiler): compiler_type = 'cygwin' - gcc_version = None - dllwrap_version = None - ld_version = None + obj_extension = ".o" + static_lib_extension = ".a" + shared_lib_extension = ".dll" + static_lib_format = "lib%s%s" + shared_lib_format = "%s%s" + exe_extension = ".exe" def __init__ (self, verbose=0, @@ -62,14 +65,16 @@ class CygwinCCompiler (UnixCCompiler): UnixCCompiler.__init__ (self, verbose, dry_run, force) - if check_config_h()<=0: + check_result = check_config_h() + self.debug_print("Python's GCC status: %s" % check_result) + if check_result[:2] <> "OK": self.warn( "Python's config.h doesn't seem to support your compiler. " "Compiling may fail because of undefined preprocessor macros.") (self.gcc_version, self.ld_version, self.dllwrap_version) = \ get_versions() - sys.stderr.write(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % + self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % (self.gcc_version, self.ld_version, self.dllwrap_version) ) @@ -117,9 +122,9 @@ class CygwinCCompiler (UnixCCompiler): extra_postargs=None, build_temp=None): - # use separate copies, so can modify the lists - extra_preargs = list(extra_preargs or []) - libraries = list(libraries or []) + # use separate copies, so we can modify the lists + extra_preargs = copy.copy(extra_preargs or []) + libraries = copy.copy(libraries or []) # Additional libraries libraries.extend(self.dll_libraries) @@ -241,11 +246,11 @@ def check_config_h(): compiling probably doesn't work. """ # return values - # 2: OK, python was compiled with GCC - # 1: OK, python's config.h mentions __GCC__ - # 0: uncertain, because we couldn't check it - # -1: probably not OK, because we didn't found it in config.h - # You could check check_config_h()>0 => OK + # "OK, python was compiled with GCC" + # "OK, python's config.h mentions __GCC__" + # "uncertain, because we couldn't check it" + # "not OK, because we didn't found __GCC__ in config.h" + # You could check check_config_h()[:2] == "OK" from distutils import sysconfig import string,sys @@ -254,7 +259,7 @@ def check_config_h(): if -1 == string.find(sys.version,"GCC"): pass # go to the next test else: - return 2 + return "OK, python was compiled with GCC" try: # It would probably better to read single lines to search. @@ -265,14 +270,14 @@ def check_config_h(): # is somewhere a #ifdef __GNUC__ or something similar if -1 == string.find(s,"__GNUC__"): - return -1 + return "not OK, because we didn't found __GCC__ in config.h" else: - return 1 + return "OK, python's config.h mentions __GCC__" except IOError: # if we can't read this file, we cannot say it is wrong # the compiler will complain later about this file as missing pass - return 0 + return "uncertain, because we couldn't check it" def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. -- cgit v1.2.1 From b9d00036db2020f271d9509152265dc170ac729b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:54:39 +0000 Subject: Added a whinging comment about the ugliness of constructing the BCPP argument list. --- bcppcompiler.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bcppcompiler.py b/bcppcompiler.py index 93049024..8ad9e4f2 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -253,6 +253,16 @@ class BCPPCompiler(CCompiler) : ld_args.extend(objects) # list of object files + # XXX the command-line syntax for Borland C++ is a bit wonky; + # certain filenames are jammed together in one big string, but + # comma-delimited. This doesn't mesh too well with the + # Unix-centric attitude (with a DOS/Windows quoting hack) of + # 'spawn()', so constructing the argument list is a bit + # awkward. Note that doing the obvious thing and jamming all + # the filenames and commas into one argument would be wrong, + # because 'spawn()' would quote any filenames with spaces in + # them. Arghghh!. Apparently it works fine as coded... + # name of dll file ld_args.extend([',',output_filename]) # no map file and start libraries -- cgit v1.2.1 From 5042724ac26c16206dffec481827d36d8778a182 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 01:18:55 +0000 Subject: Overhauld 'check_config_h()': now returns a (status, details) tuple, and is much better documented to boot. --- cygwinccompiler.py | 71 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index da0d60b0..2ac2678e 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -65,11 +65,13 @@ class CygwinCCompiler (UnixCCompiler): UnixCCompiler.__init__ (self, verbose, dry_run, force) - check_result = check_config_h() - self.debug_print("Python's GCC status: %s" % check_result) - if check_result[:2] <> "OK": + (status, details) = check_config_h() + self.debug_print("Python's GCC status: %s (details: %s)" % + (status, details)) + if status is not CONFIG_H_OK: self.warn( - "Python's config.h doesn't seem to support your compiler. " + "Python's config.h doesn't seem to support your compiler. " + + ("Reason: %s." % details) + "Compiling may fail because of undefined preprocessor macros.") (self.gcc_version, self.ld_version, self.dllwrap_version) = \ @@ -241,43 +243,60 @@ class Mingw32CCompiler (CygwinCCompiler): # default, we should at least warn the user if he is using a unmodified # version. +CONFIG_H_OK = "ok" +CONFIG_H_NOTOK = "not ok" +CONFIG_H_UNCERTAIN = "uncertain" + def check_config_h(): - """Checks if the GCC compiler is mentioned in config.h. If it is not, - compiling probably doesn't work. + + """Check if the current Python installation (specifically, config.h) + appears amenable to building extensions with GCC. Returns a tuple + (status, details), where 'status' is one of the following constants: + CONFIG_H_OK + all is well, go ahead and compile + CONFIG_H_NOTOK + doesn't look good + CONFIG_H_UNCERTAIN + not sure -- unable to read config.h + 'details' is a human-readable string explaining the situation. + + Note there are two ways to conclude "OK": either 'sys.version' contains + the string "GCC" (implying that this Python was built with GCC), or the + installed "config.h" contains the string "__GNUC__". """ - # return values - # "OK, python was compiled with GCC" - # "OK, python's config.h mentions __GCC__" - # "uncertain, because we couldn't check it" - # "not OK, because we didn't found __GCC__ in config.h" - # You could check check_config_h()[:2] == "OK" + + # XXX since this function also checks sys.version, it's not strictly a + # "config.h" check -- should probably be renamed... from distutils import sysconfig import string,sys # if sys.version contains GCC then python was compiled with # GCC, and the config.h file should be OK - if -1 == string.find(sys.version,"GCC"): - pass # go to the next test - else: - return "OK, python was compiled with GCC" + if string.find(sys.version,"GCC") >= 0: + return (CONFIG_H_OK, "sys.version mentions 'GCC'") + fn = sysconfig.get_config_h_filename() try: # It would probably better to read single lines to search. # But we do this only once, and it is fast enough - f=open(sysconfig.get_config_h_filename()) - s=f.read() + f = open(fn) + s = f.read() f.close() - # is somewhere a #ifdef __GNUC__ or something similar - if -1 == string.find(s,"__GNUC__"): - return "not OK, because we didn't found __GCC__ in config.h" - else: - return "OK, python's config.h mentions __GCC__" - except IOError: + except IOError, exc: # if we can't read this file, we cannot say it is wrong # the compiler will complain later about this file as missing - pass - return "uncertain, because we couldn't check it" + return (CONFIG_H_UNCERTAIN, + "couldn't read '%s': %s" % (fn, exc.strerror)) + + else: + # "config.h" contains an "#ifdef __GNUC__" or something similar + if string.find(s,"__GNUC__") >= 0: + return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) + else: + return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) + + def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. -- cgit v1.2.1 From e016e0767f54a5256595679a34f7ce581508d573 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 15 Aug 2000 13:01:25 +0000 Subject: Fix long-hidden inconsistency in internal interface: 'find_modules()' now represents packages as strings, not tuples. This allowed a simplification in 'get_package_dir()', too -- can now assume that 'package' is a string. --- command/build_py.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 0405d392..87e3efa2 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -93,12 +93,7 @@ class build_py (Command): distribution, where package 'package' should be found (at least according to the 'package_dir' option, if any).""" - if type (package) is StringType: - path = string.split (package, '.') - elif type (package) in (TupleType, ListType): - path = list (package) - else: - raise TypeError, "'package' must be a string, list, or tuple" + path = string.split (package, '.') if not self.package_dir: if path: @@ -220,7 +215,7 @@ class build_py (Command): for module in self.py_modules: path = string.split (module, '.') - package = tuple (path[0:-1]) + package = string.join(path[0:-1], '.') module_base = path[-1] try: -- cgit v1.2.1 From 56988c3650a6601eca05bb51e07e84faaf629ca2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 15 Aug 2000 13:03:16 +0000 Subject: Added support for the '--dist-dir' option, including a mildly nasty hack to find the two created RPM files (source and binary) and move them to the "dist dir" (default "dist"). --- command/bdist_rpm.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index e45d7a36..1da0b81f 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -8,6 +8,7 @@ distributions).""" __revision__ = "$Id$" import os, string +import glob from types import * from distutils.core import Command, DEBUG from distutils.util import get_platform @@ -24,6 +25,9 @@ class bdist_rpm (Command): ('rpm-base=', None, "base directory for creating RPMs (defaults to \"rpm\" under " "--bdist-base; must be specified for RPM 2)"), + ('dist-dir=', 'd', + "directory to put final RPM files in " + "(and .spec files if --spec-only)"), ('spec-only', None, "only regenerate spec file"), ('source-only', None, @@ -109,6 +113,7 @@ class bdist_rpm (Command): def initialize_options (self): self.bdist_base = None self.rpm_base = None + self.dist_dir = None self.spec_only = None self.binary_only = None self.source_only = None @@ -166,6 +171,7 @@ class bdist_rpm (Command): if not self.distribution.has_ext_modules(): self.use_rpm_opt_flags = 0 + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) self.finalize_package_data() # finalize_options() @@ -226,8 +232,8 @@ class bdist_rpm (Command): # make directories if self.spec_only: - spec_dir = "dist" - self.mkpath(spec_dir) # XXX should be configurable + spec_dir = self.dist_dir + self.mkpath(spec_dir) else: rpm_dir = {} for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): @@ -235,8 +241,8 @@ class bdist_rpm (Command): self.mkpath(rpm_dir[d]) spec_dir = rpm_dir['SPECS'] - # Spec file goes into 'dist' directory if '--spec-only specified', - # into build/rpm. otherwise. + # Spec file goes into 'dist_dir' if '--spec-only specified', + # build/rpm. otherwise. spec_path = os.path.join(spec_dir, "%s.spec" % self.distribution.get_name()) self.execute(write_file, @@ -285,6 +291,19 @@ class bdist_rpm (Command): rpm_args.append(spec_path) self.spawn(rpm_args) + # XXX this is a nasty hack -- we really should have a proper way to + # find out the names of the RPM files created; also, this assumes + # that RPM creates exactly one source and one binary RPM. + if not self.dry_run: + srpms = glob.glob(os.path.join(rpm_dir['SRPMS'], "*.rpm")) + rpms = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*.rpm")) + assert len(srpms) == 1, \ + "unexpected number of SRPM files found: %s" % srpms + assert len(rpms) == 1, \ + "unexpected number of RPM files found: %s" % rpms + self.move_file(srpms[0], self.dist_dir) + self.move_file(rpms[0], self.dist_dir) + # run() -- cgit v1.2.1 From 802df4b37327ad870f0a6fe0be1551740f138758 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 15 Aug 2000 13:05:35 +0000 Subject: Fixed the move-RPM-files hack so it knows about the '--binary-only' and '--source-only' options. --- command/bdist_rpm.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 1da0b81f..026a3ba4 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -295,14 +295,17 @@ class bdist_rpm (Command): # find out the names of the RPM files created; also, this assumes # that RPM creates exactly one source and one binary RPM. if not self.dry_run: - srpms = glob.glob(os.path.join(rpm_dir['SRPMS'], "*.rpm")) - rpms = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*.rpm")) - assert len(srpms) == 1, \ - "unexpected number of SRPM files found: %s" % srpms - assert len(rpms) == 1, \ - "unexpected number of RPM files found: %s" % rpms - self.move_file(srpms[0], self.dist_dir) - self.move_file(rpms[0], self.dist_dir) + if not self.binary_only: + srpms = glob.glob(os.path.join(rpm_dir['SRPMS'], "*.rpm")) + assert len(srpms) == 1, \ + "unexpected number of SRPM files found: %s" % srpms + self.move_file(srpms[0], self.dist_dir) + + if not self.source_only: + rpms = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*.rpm")) + assert len(rpms) == 1, \ + "unexpected number of RPM files found: %s" % rpms + self.move_file(rpms[0], self.dist_dir) # run() -- cgit v1.2.1 From 91251698bde14e2f5f9697626ff61d6ba3f408cb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 15 Aug 2000 13:14:27 +0000 Subject: Bump version to 0.9.1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f6af3c9d..7fe2f913 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.9.1pre" +__version__ = "0.9.1" -- cgit v1.2.1 From 7ec708f7b894796c2fe1d28216e47819fc9fcf87 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 22 Aug 2000 01:48:54 +0000 Subject: Ensure destination directory exists before trying to create a tarball or ZIP file. --- archive_util.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/archive_util.py b/archive_util.py index 08a3c831..26cd7fb2 100644 --- a/archive_util.py +++ b/archive_util.py @@ -10,7 +10,7 @@ __revision__ = "$Id$" import os from distutils.errors import DistutilsExecError from distutils.spawn import spawn - +from distutils.dir_util import mkpath def make_tarball (base_name, base_dir, compress="gzip", verbose=0, dry_run=0): @@ -42,6 +42,7 @@ def make_tarball (base_name, base_dir, compress="gzip", "bad value for 'compress': must be None, 'gzip', or 'compress'" archive_name = base_name + ".tar" + mkpath(os.path.dirname(archive_name), verbose=verbose, dry_run=dry_run) cmd = ["tar", "-cf", archive_name, base_dir] spawn (cmd, verbose=verbose, dry_run=dry_run) @@ -68,6 +69,7 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): # no changes needed! zip_filename = base_name + ".zip" + mkpath(os.path.dirname(zip_filename), verbose=verbose, dry_run=dry_run) try: spawn (["zip", "-rq", zip_filename, base_dir], verbose=verbose, dry_run=dry_run) @@ -114,7 +116,7 @@ ARCHIVE_FORMATS = { 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"), 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"), - 'zip': (make_zipfile, [],"zip-file") + 'zip': (make_zipfile, [],"ZIP file") } def check_archive_formats (formats): -- cgit v1.2.1 From a535c80ee5091193512f8e7456e92b827d1725df Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 22 Aug 2000 01:49:41 +0000 Subject: Don't bother to 'mkpath()' the 'dist_dir' -- that's now taken care of in archive_util.py. --- command/sdist.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index b2d7a86f..2351ebbe 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -442,8 +442,6 @@ class sdist (Command): self.make_release_tree (base_dir, self.filelist.files) archive_files = [] # remember names of files we create - if self.dist_dir: - self.mkpath(self.dist_dir) for fmt in self.formats: file = self.make_archive (base_name, fmt, base_dir=base_dir) archive_files.append(file) -- cgit v1.2.1 From e1e774a29223c5fafee139a64411fec416e1fdb4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 26 Aug 2000 02:21:55 +0000 Subject: In 'check_extensions_list()': when converting old-style 'buildinfo' dict, don't assign None to any attributes of the Extension object. --- command/build_ext.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index aca6dac4..4d779b82 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -278,7 +278,9 @@ class build_ext (Command): 'extra_objects', 'extra_compile_args', 'extra_link_args'): - setattr(ext, key, build_info.get(key)) + val = build_info.get(key) + if val is not None: + setattr(ext, key, val) # Medium-easy stuff: same syntax/semantics, different names. ext.runtime_library_dirs = build_info.get('rpath') -- cgit v1.2.1 From 43abfd378a945259c7164cabc5fb16b44dc8a5f1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 26 Aug 2000 02:37:07 +0000 Subject: Bumped version to 0.9.2pre. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7fe2f913..afd65545 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.9.1" +__version__ = "0.9.2pre" -- cgit v1.2.1 From 8294ae149b20ee146afc6c57c410979b440f7934 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 26 Aug 2000 02:40:10 +0000 Subject: New release of the Windows installer from Thomas Heller. The known bug (bogus error message when an empty file is extracted) is fixed. Other changes: - The target-compile and target-optimize flags of bdist_wininst are gone. It is no longer possible to compile the python files during installation. - The zlib module is no longer required or used by bdist_wininst. - I moved the decompression/extraction code into a separate file (extract.c). - The installer stub is now compressed by UPX (see http://upx.tsx.org/). This reduces the size of the exe (and thus the overhead of the final installer program) from 40 kB to 16 kB. - The installer displays a more uptodate user wizard-like user interface, also containing a graphic: Just's Python Powered logo. (I could not convince myself to use one of the BeOpen logos). - The installation progress bar now moves correctly. --- command/bdist_wininst.py | 579 ++++++++++++++++++++++++----------------------- 1 file changed, 296 insertions(+), 283 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 38e973b6..9b600a9e 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -22,13 +22,9 @@ class bdist_wininst (Command): ('keep-tree', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), - ('target-compile', 'c', - "compile to .pyc on the target system"), - ('target-optimize', 'o', - "compile to .pyo on the target system"), ('target-version=', 'v', "require a specific python version" + - " on the target system (1.5 or 1.6/2.0)"), + " on the target system"), ('dist-dir=', 'd', "directory to put final built distributions in"), ] @@ -74,22 +70,10 @@ class bdist_wininst (Command): install.root = self.bdist_dir install_lib = self.reinitialize_command('install_lib') + # we do not want to include pyc or pyo files install_lib.compile = 0 install_lib.optimize = 0 - # The packager can choose if .pyc and .pyo files should be created - # on the TARGET system instead at the SOURCE system. - -## # The compilation can only be done on the SOURCE system for one -## # python version (assuming 1.6/2.0 and 1.5 have incompatible -## # byte-codes). -## short_version = sys.version[:3] -## if self.target_version == short_version: -## if not self.target_compile: -## install_lib.compile = 1 -## if not self.target_optimize: -## install_lib.optimize = 1 - install_lib.ensure_finalized() self.announce ("installing to %s" % self.bdist_dir) @@ -137,7 +121,7 @@ class bdist_wininst (Command): # 'info' will be displayed in the installer's dialog box, # describing the items to be installed. - info = metadata.long_description + '\n' + info = metadata.long_description or '' + '\n' for name in dir (metadata): if (name != 'long_description'): @@ -152,11 +136,8 @@ class bdist_wininst (Command): inifile.write ("\n[Setup]\n") inifile.write ("info=%s\n" % repr (info)[1:-1]) inifile.write ("pthname=%s.%s\n" % (metadata.name, metadata.version)) - inifile.write ("pyc_compile=%d\n" % self.target_compile) - inifile.write ("pyo_compile=%d\n" % self.target_optimize) if self.target_version: - vers_minor = string.split (self.target_version, '.')[1] - inifile.write ("vers_minor=%s\n" % vers_minor) + inifile.write ("target_version=%s\n" % self.target_version) title = self.distribution.get_fullname() inifile.write ("title=%s\n" % repr (title)[1:-1]) @@ -166,285 +147,317 @@ class bdist_wininst (Command): # create_inifile() def create_exe (self, arcname, fullname): - import struct, zlib + import struct#, zlib cfgdata = open (self.create_inifile()).read() - comp_method = zlib.DEFLATED - co = zlib.compressobj (zlib.Z_DEFAULT_COMPRESSION, comp_method, -15) - zcfgdata = co.compress (cfgdata) + co.flush() - installer_name = os.path.join(self.dist_dir, "%s.win32.exe" % fullname) self.announce ("creating %s" % installer_name) file = open (installer_name, "wb") file.write (self.get_exe_bytes ()) - file.write (zcfgdata) - crc32 = zlib.crc32 (cfgdata) - header = struct.pack (" Date: Sun, 27 Aug 2000 20:44:13 +0000 Subject: Fix line-endings. Fix bad operator precedence: should be "(metadata or '') + '\n'". --- command/bdist_wininst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 9b600a9e..78a5c9c5 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -121,7 +121,7 @@ class bdist_wininst (Command): # 'info' will be displayed in the installer's dialog box, # describing the items to be installed. - info = metadata.long_description or '' + '\n' + info = (metadata.long_description or '') + '\n' for name in dir (metadata): if (name != 'long_description'): -- cgit v1.2.1 From 23be7d21e3e70fd3a397ce710a25166adfa8a037 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 29 Aug 2000 01:15:18 +0000 Subject: Added 'script_name' and 'script_args' instance attributes to Distribution. Changed 'core.setup()' so it sets them to reasonable defaults. Tweaked how the "usage" string is generated: 'core' now provides 'gen_usage()', which is used instead of 'USAGE'. Modified "build_py" and "sdist" commands to refer to 'self.distribution.script_name' rather than 'sys.argv[0]'. --- command/build_py.py | 4 +++- command/sdist.py | 5 ++++- core.py | 34 ++++++++++++++++++++++------------ dist.py | 52 +++++++++++++++++++++++++++++----------------------- 4 files changed, 58 insertions(+), 37 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 87e3efa2..66f50241 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -177,13 +177,15 @@ class build_py (Command): self.check_package (package, package_dir) module_files = glob (os.path.join (package_dir, "*.py")) modules = [] - setup_script = os.path.abspath (sys.argv[0]) + setup_script = os.path.abspath(self.distribution.script_name) for f in module_files: abs_f = os.path.abspath (f) if abs_f != setup_script: module = os.path.splitext (os.path.basename (f))[0] modules.append ((package, module, f)) + else: + self.debug_print("excluding %s" % setup_script) return modules diff --git a/command/sdist.py b/command/sdist.py index 2351ebbe..06c8f1cb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -202,7 +202,10 @@ class sdist (Command): # manifest, but there's no template -- which will happen if the # developer elects to generate a manifest some other way -- then we # can't regenerate the manifest, so we don't.) - setup_newer = dep_util.newer(sys.argv[0], self.manifest) + self.debug_print("checking if %s newer than %s" % + (self.distribution.script_name, self.manifest)) + setup_newer = dep_util.newer(self.distribution.script_name, + self.manifest) # cases: # 1) no manifest, template exists: generate manifest diff --git a/core.py b/core.py index 4c982a07..39f1f54b 100644 --- a/core.py +++ b/core.py @@ -25,26 +25,30 @@ from distutils.extension import Extension # runs the setup script with no arguments at all. More useful help # is generated with various --help options: global help, list commands, # and per-command help. -usage = """\ -usage: %s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] - or: %s --help [cmd1 cmd2 ...] - or: %s --help-commands - or: %s cmd --help -""" % ((os.path.basename(sys.argv[0]),) * 4) +USAGE = """\ +usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] + or: %(script)s --help [cmd1 cmd2 ...] + or: %(script)s --help-commands + or: %(script)s cmd --help +""" # If DISTUTILS_DEBUG is anything other than the empty string, we run in # debug mode. DEBUG = os.environ.get('DISTUTILS_DEBUG') +def gen_usage (script_name): + script = os.path.basename(script_name) + return USAGE % vars() + def setup (**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a Distribution instance; find and parse config files; parse the command - line; run each of those commands using the options supplied to - 'setup()' (as keyword arguments), in config files, and on the command - line. + line; run each Distutils command found there, customized by the options + supplied to 'setup()' (as keyword arguments), in config files, and on + the command line. The Distribution instance might be an instance of a class supplied via the 'distclass' keyword argument to 'setup'; if no such class is @@ -79,6 +83,11 @@ def setup (**attrs): else: klass = Distribution + if not attrs.has_key('script_name'): + attrs['script_name'] = sys.argv[0] + if not attrs.has_key('script_args'): + attrs['script_args'] = sys.argv[1:] + # Create the Distribution instance, using the remaining arguments # (ie. everything except distclass) to initialize it try: @@ -97,10 +106,11 @@ def setup (**attrs): # Parse the command line; any command-line errors are the end user's # fault, so turn them into SystemExit to suppress tracebacks. try: - ok = dist.parse_command_line (sys.argv[1:]) + ok = dist.parse_command_line() except DistutilsArgError, msg: - sys.stderr.write (usage + "\n") - raise SystemExit, "error: %s" % msg + script = os.path.basename(dist.script_name) + raise SystemExit, \ + gen_usage(dist.script_name) + "\nerror: %s" % msg if DEBUG: print "options (after parsing command line):" diff --git a/dist.py b/dist.py index ed829fe4..1552dc0c 100644 --- a/dist.py +++ b/dist.py @@ -131,6 +131,12 @@ class Distribution: # for the setup script to override command classes self.cmdclass = {} + # 'script_name' and 'script_args' are usually set to sys.argv[0] + # and sys.argv[1:], but they can be overridden when the caller is + # not necessarily a setup script run from the command-line. + self.script_name = None + self.script_args = None + # 'command_options' is where we store command options between # parsing them (from config files, the command-line, etc.) and when # they are actually needed -- ie. when the command in question is @@ -326,24 +332,24 @@ class Distribution: # -- Command-line parsing methods ---------------------------------- - def parse_command_line (self, args): - """Parse the setup script's command line. 'args' must be a list - of command-line arguments, most likely 'sys.argv[1:]' (see the - 'setup()' function). This list is first processed for "global - options" -- options that set attributes of the Distribution - instance. Then, it is alternately scanned for Distutils - commands and options for that command. Each new command - terminates the options for the previous command. The allowed - options for a command are determined by the 'user_options' - attribute of the command class -- thus, we have to be able to - load command classes in order to parse the command line. Any - error in that 'options' attribute raises DistutilsGetoptError; - any error on the command-line raises DistutilsArgError. If no - Distutils commands were found on the command line, raises - DistutilsArgError. Return true if command-line were - successfully parsed and we should carry on with executing - commands; false if no errors but we shouldn't execute commands - (currently, this only happens if user asks for help). + def parse_command_line (self): + """Parse the setup script's command line, taken from the + 'script_args' instance attribute (which defaults to 'sys.argv[1:]' + -- see 'setup()' in core.py). This list is first processed for + "global options" -- options that set attributes of the Distribution + instance. Then, it is alternately scanned for Distutils commands + and options for that command. Each new command terminates the + options for the previous command. The allowed options for a + command are determined by the 'user_options' attribute of the + command class -- thus, we have to be able to load command classes + in order to parse the command line. Any error in that 'options' + attribute raises DistutilsGetoptError; any error on the + command-line raises DistutilsArgError. If no Distutils commands + were found on the command line, raises DistutilsArgError. Return + true if command-line were successfully parsed and we should carry + on with executing commands; false if no errors but we shouldn't + execute commands (currently, this only happens if user asks for + help). """ # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- @@ -356,7 +362,7 @@ class Distribution: parser = FancyGetopt (self.global_options + self.display_options) parser.set_negative_aliases (self.negative_opt) parser.set_aliases ({'license': 'licence'}) - args = parser.getopt (object=self) + args = parser.getopt (args=self.script_args, object=self) option_order = parser.get_option_order() # for display options we return immediately @@ -506,7 +512,7 @@ class Distribution: in 'commands'. """ # late import because of mutual dependence between these modules - from distutils.core import usage + from distutils.core import gen_usage from distutils.cmd import Command if global_options: @@ -535,7 +541,7 @@ class Distribution: parser.print_help ("Options for '%s' command:" % klass.__name__) print - print usage + print gen_usage(self.script_name) return # _show_help () @@ -547,7 +553,7 @@ class Distribution: line, display the requested info and return true; else return false. """ - from distutils.core import usage + from distutils.core import gen_usage # User just wants a list of commands -- we'll print it out and stop # processing now (ie. if they ran "setup --help-commands foo bar", @@ -555,7 +561,7 @@ class Distribution: if self.help_commands: self.print_commands () print - print usage + print gen_usage(self.script_name) return 1 # If user supplied any of the "display metadata" options, then -- cgit v1.2.1 From 67d6f9504294214af2808483dbf15e6246a5a2d4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 30 Aug 2000 17:16:27 +0000 Subject: Added docstring for 'wrap()' function. --- fancy_getopt.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fancy_getopt.py b/fancy_getopt.py index a62bc0df..eaf60737 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -412,6 +412,11 @@ def fancy_getopt (options, negative_opt, object, args): WS_TRANS = string.maketrans (string.whitespace, ' ' * len (string.whitespace)) def wrap_text (text, width): + """wrap_text(text : string, width : int) -> [string] + + Split 'text' into multiple lines of no more than 'width' characters + each, and return the list of strings that results. + """ if text is None: return [] -- cgit v1.2.1 From 630ec75678315f7043e3a14aa21b539d77979854 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 30 Aug 2000 17:32:24 +0000 Subject: Add ".cxx" to the list of known C++ extensions. --- msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index eecbb620..6c3f8dab 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -177,7 +177,7 @@ class MSVCCompiler (CCompiler) : # Private class data (need to distinguish C from C++ source for compiler) _c_extensions = ['.c'] - _cpp_extensions = ['.cc','.cpp'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] # Needed for the filename generation methods provided by the # base class, CCompiler. -- cgit v1.2.1 From 1020b190ea0add7c81905e2bc2814678d0fc414a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 31 Aug 2000 00:31:07 +0000 Subject: Add /GX to 'compile_options'. This is definitely needed for C++ source; according to the MS docs it enables exception-handling, and (according to Alex Martelli ) is needed to compile without getting warnings from standard C++ library headers. Apparently it doesn't cause any problems with C code, so I haven't bothered conditionalizing the use of /GX. --- msvccompiler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 6c3f8dab..ae08e7fd 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -219,8 +219,9 @@ class MSVCCompiler (CCompiler) : self.lib = "lib.exe" self.preprocess_options = None - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3' ] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG'] + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', + '/Z7', '/D_DEBUG'] self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] self.ldflags_shared_debug = [ -- cgit v1.2.1 From 58ad88e9ee05aaf0a3ee7e3d6c23900d3277d399 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 00:52:45 +0000 Subject: Added 'run_setup()' to allow outsiders to run a setup script under fairly tight control, and the '_setup_stop_after' and '_setup_distribution' globals to provide the tight control. This isn't entirely reliable yet: it dies horribly with a NameError on the example PIL setup script in examples/pil_setup.py (at least with Python 1.5.2; untested with current Python). There's some strangeness going on with execfile(), but I don't understand it and don't have time to track it down right now. --- core.py | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/core.py b/core.py index 39f1f54b..5dfe2db8 100644 --- a/core.py +++ b/core.py @@ -42,6 +42,11 @@ def gen_usage (script_name): return USAGE % vars() +# Some mild magic to control the behaviour of 'setup()' from 'run_setup()'. +_setup_stop_after = None +_setup_distribution = None + + def setup (**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a @@ -75,6 +80,8 @@ def setup (**attrs): object. """ + global _setup_stop_after, _setup_distribution + # Determine the distribution class -- either caller-supplied or # our Distribution (see below). klass = attrs.get ('distclass') @@ -91,10 +98,13 @@ def setup (**attrs): # Create the Distribution instance, using the remaining arguments # (ie. everything except distclass) to initialize it try: - dist = klass (attrs) + _setup_distribution = dist = klass (attrs) except DistutilsSetupError, msg: raise SystemExit, "error in setup script: %s" % msg + if _setup_stop_after == "init": + return dist + # Find and parse the config file(s): they will override options from # the setup script, but be overridden by the command line. dist.parse_config_files() @@ -103,6 +113,9 @@ def setup (**attrs): print "options (after parsing config files):" dist.dump_option_dicts() + if _setup_stop_after == "config": + return dist + # Parse the command line; any command-line errors are the end user's # fault, so turn them into SystemExit to suppress tracebacks. try: @@ -116,6 +129,9 @@ def setup (**attrs): print "options (after parsing command line):" dist.dump_option_dicts() + if _setup_stop_after == "commandline": + return dist + # And finally, run all the commands found on the command line. if ok: try: @@ -140,4 +156,76 @@ def setup (**attrs): else: raise SystemExit, "error: " + str(msg) + return dist + # setup () + + +def run_setup (script_name, script_args=None, stop_after="run"): + """Run a setup script in a somewhat controlled environment, and + return the Distribution instance that drives things. This is useful + if you need to find out the distribution meta-data (passed as + keyword args from 'script' to 'setup()', or the contents of the + config files or command-line. + + 'script_name' is a file that will be run with 'execfile()'; + 'sys.argv[0]' will be replaced with 'script' for the duration of the + call. 'script_args' is a list of strings; if supplied, + 'sys.argv[1:]' will be replaced by 'script_args' for the duration of + the call. + + 'stop_after' tells 'setup()' when to stop processing; possible + values: + init + stop after the Distribution instance has been created and + populated with the keyword arguments to 'setup()' + config + stop after config files have been parsed (and their data + stored in the Distribution instance) + commandline + stop after the command-line ('sys.argv[1:]' or 'script_args') + have been parsed (and the data stored in the Distribution) + run [default] + stop after all commands have been run (the same as if 'setup()' + had been called in the usual way + + Returns the Distribution instance, which provides all information + used to drive the Distutils. + """ + if stop_after not in ('init', 'config', 'commandline', 'run'): + raise ValueError, "invalid value for 'stop_after': %s" % `stop_after` + + global _setup_stop_after, _setup_distribution + _setup_stop_after = stop_after + + save_argv = sys.argv + g = {} + l = {} + try: + try: + sys.argv[0] = script_name + if script_args is not None: + sys.argv[1:] = script_args + execfile(script_name, g, l) + finally: + sys.argv = save_argv + _setup_stop_after = None + except SystemExit: + # Hmm, should we do something if exiting with a non-zero code + # (ie. error)? + pass + except: + raise + + if _setup_distribution is None: + raise RuntimeError, \ + ("'distutils.core.setup()' was never called -- " + "perhaps '%s' is not a Distutils setup script?") % \ + script_name + + # I wonder if the setup script's namespace -- g and l -- would be of + # any interest to callers? + #print "_setup_distribution:", _setup_distribution + return _setup_distribution + +# run_setup () -- cgit v1.2.1 From 0a344796c68ac40b4796d2c07fa3b37104a4157b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 01:00:40 +0000 Subject: Bump version to 0.9.2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index afd65545..16a3984b 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.9.2pre" +__version__ = "0.9.2" -- cgit v1.2.1 From 8553e855805f2e75372d582f60ffc9517a91f01e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 01:23:26 +0000 Subject: Rene Liebscher: hack '_init_posix()' to handle the BeOS linker script. (With a worry-wart comment added by me about where we *should* add the Python library to the link.) --- sysconfig.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index 22463277..3774ab25 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -255,6 +255,23 @@ def _init_posix(): g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) + if sys.platform == 'beos': + + # Linker script is in the config directory. In the Makefile it is + # relative to the srcdir, which after installation no longer makes + # sense. + python_lib = get_python_lib(standard_lib=1) + linkerscript_name = os.path.basename(string.split(g['LDSHARED'])[0]) + linkerscript = os.path.join(python_lib, 'config', linkerscript_name) + + # XXX this isn't the right place to do this: adding the Python + # library to the link, if needed, should be in the "build_ext" + # command. (It's also needed for non-MS compilers on Windows, and + # it's taken care of for them by the 'build_ext.get_libraries()' + # method.) + g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % + (linkerscript, sys.prefix, sys.version[0:3])) + def _init_nt(): """Initialize the module as appropriate for NT""" -- cgit v1.2.1 From 18201c35b97437685a0cbf43a86e188d471f22e2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 01:24:31 +0000 Subject: Rene Liebscher: comment fixes. --- cygwinccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 2ac2678e..f547d540 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -18,7 +18,7 @@ cygwin in no-cygwin mode). # # see also http://starship.python.net/crew/kernr/mingw32/Notes.html # -# * We use put export_symbols in a def-file, and don't use +# * We put export_symbols in a def-file, and don't use # --export-all-symbols because it doesn't worked reliable in some # tested configurations. And because other windows compilers also # need their symbols specified this no serious problem. @@ -32,7 +32,7 @@ cygwin in no-cygwin mode). # (ld doesn't support -shared, so we use dllwrap) # * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now # - its dllwrap doesn't work, there is a bug in binutils 2.10.90 -# see also ..... +# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html # - using gcc -mdll instead dllwrap doesn't work without -static because # it tries to link against dlls instead their import libraries. (If # it finds the dll first.) -- cgit v1.2.1 From bb458fa60d179f85c4290c807987cf8f93a52e1d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 01:28:33 +0000 Subject: Rene Liebscher: * reverse library names from bcpp_library to library_bcpp * move some code to the right places, to put the def-files in the right directories again --- bcppcompiler.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 8ad9e4f2..2b73b12f 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -224,17 +224,6 @@ class BCPPCompiler(CCompiler) : else: ld_args = self.ldflags_shared[:] - # Borland C++ has problems with '/' in paths - objects = map(os.path.normpath, objects) - startup_obj = 'c0d32' - objects.insert(0, startup_obj) - - # either exchange python15.lib in the python libs directory against - # a Borland-like one, or create one with name bcpp_python15.lib - # there and remove the pragmas from config.h - libraries.append ('import32') - libraries.append ('cw32mt') - # Create a temporary exports file for use by the linker head, tail = os.path.split (output_filename) modname, ext = os.path.splitext (tail) @@ -246,6 +235,17 @@ class BCPPCompiler(CCompiler) : self.execute(write_file, (def_file, contents), "writing %s" % def_file) + # Borland C++ has problems with '/' in paths + objects = map(os.path.normpath, objects) + startup_obj = 'c0d32' + objects.insert(0, startup_obj) + + # either exchange python15.lib in the python libs directory against + # a Borland-like one, or create one with name bcpp_python15.lib + # there and remove the pragmas from config.h + libraries.append ('import32') + libraries.append ('cw32mt') + # Start building command line flags and options. for l in library_dirs: @@ -377,9 +377,9 @@ class BCPPCompiler(CCompiler) : # seems to have a different format for static libraries. if debug: dlib = (lib + "_d") - try_names = ("bcpp_" + dlib, "bcpp_" + lib, dlib, lib) + try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib) else: - try_names = ("bcpp_" + lib, lib) + try_names = (lib + "_bcpp", lib) for dir in dirs: for name in try_names: -- cgit v1.2.1 From 6d4ed7968e6a6ebde7b50b53361e180c1a1d8f2c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 01:44:45 +0000 Subject: Rene Liebscher/Thomas Heller: * ensure the "dist" directory exists * raise exception if using for modules containing compiled extensions on a non-win32 platform. * don't create an .ini file anymore (it was just for debugging) --- command/bdist_wininst.py | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 78a5c9c5..d51ea0a8 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -63,6 +63,12 @@ class bdist_wininst (Command): def run (self): + if (sys.platform != "win32" and + (self.distribution.has_ext_modules() or + self.distribution.has_c_libraries())): + raise DistutilsPlatformError, \ + ("distribution contains extensions and/or C libraries; " + "must be compiled on a Windows 32 platform") self.run_command ('build') @@ -103,21 +109,16 @@ class bdist_wininst (Command): # run() - def create_inifile (self): - # Create an inifile containing data describing the installation. - # This could be done without creating a real file, but - # a file is (at least) useful for debugging bdist_wininst. + def get_inidata (self): + # Return data describing the installation. + lines = [] metadata = self.distribution.metadata - ini_name = "%s.ini" % metadata.get_fullname() - - self.announce ("creating %s" % ini_name) - inifile = open (ini_name, "w") # Write the [metadata] section. Values are written with # repr()[1:-1], so they do not contain unprintable characters, and # are not surrounded by quote chars. - inifile.write ("[metadata]\n") + lines.append ("[metadata]") # 'info' will be displayed in the installer's dialog box, # describing the items to be installed. @@ -129,27 +130,28 @@ class bdist_wininst (Command): if data: info = info + ("\n %s: %s" % \ (string.capitalize (name), data)) - inifile.write ("%s=%s\n" % (name, repr (data)[1:-1])) + lines.append ("%s=%s" % (name, repr (data)[1:-1])) # The [setup] section contains entries controlling # the installer runtime. - inifile.write ("\n[Setup]\n") - inifile.write ("info=%s\n" % repr (info)[1:-1]) - inifile.write ("pthname=%s.%s\n" % (metadata.name, metadata.version)) + lines.append ("\n[Setup]") + lines.append ("info=%s" % repr (info)[1:-1]) + lines.append ("pthname=%s.%s" % (metadata.name, metadata.version)) if self.target_version: - inifile.write ("target_version=%s\n" % self.target_version) + lines.append ("target_version=%s" % self.target_version) title = self.distribution.get_fullname() - inifile.write ("title=%s\n" % repr (title)[1:-1]) - inifile.close() - return ini_name + lines.append ("title=%s" % repr (title)[1:-1]) + return string.join (lines, "\n") - # create_inifile() + # get_inidata() def create_exe (self, arcname, fullname): - import struct#, zlib + import struct + + self.mkpath(self.dist_dir) - cfgdata = open (self.create_inifile()).read() + cfgdata = self.get_inidata() installer_name = os.path.join(self.dist_dir, "%s.win32.exe" % fullname) -- cgit v1.2.1 From 47192a5d2460a813c4a177c8a2d27339c7fbecc1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 6 Sep 2000 02:06:27 +0000 Subject: Typo fix. --- command/build_ext.py | 2 +- command/build_py.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 4d779b82..3f714c54 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -55,7 +55,7 @@ class build_ext (Command): ('build-temp=', 't', "directory for temporary files (build by-products)"), ('inplace', 'i', - "ignore build-lib and put compiled extensions into the source" + + "ignore build-lib and put compiled extensions into the source " + "directory alongside your pure Python modules"), ('include-dirs=', 'I', "list of directories to search for header files"), diff --git a/command/build_py.py b/command/build_py.py index 66f50241..5fcd18e7 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -20,7 +20,7 @@ class build_py (Command): user_options = [ ('build-lib=', 'd', "directory to \"build\" (copy) to"), - ('force', 'f', "forcibly build everything (ignore file timestamps"), + ('force', 'f', "forcibly build everything (ignore file timestamps)"), ] -- cgit v1.2.1 From 1565bb9efd2c7943171ecd4c07774d02d6785038 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 6 Sep 2000 02:08:24 +0000 Subject: Reorganized logic in 'get_file_list()' so it's easier to read, and fixed a bug to boot: now works even if both MANIFEST and MANIFEST.in don't exist. Don't hardcode setup.py, use 'self.distribution.script_name'. --- command/sdist.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 06c8f1cb..1bf297fd 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -219,10 +219,14 @@ class sdist (Command): # do nothing (unless --force or --manifest-only) # 4) no manifest, no template: generate w/ warning ("defaults only") - # Regenerate the manifest if necessary (or if explicitly told to) - if ((template_exists and (template_newer or setup_newer)) or - self.force_manifest or self.manifest_only): + manifest_outofdate = (template_exists and + (template_newer or setup_newer)) + force_regen = self.force_manifest or self.manifest_only + manifest_exists = os.path.isfile(self.manifest) + neither_exists = (not template_exists and not manifest_exists) + # Regenerate the manifest if necessary (or if explicitly told to) + if manifest_outofdate or neither_exists or force_regen: if not template_exists: self.warn(("manifest template '%s' does not exist " + "(using default file list)") % @@ -273,10 +277,7 @@ class sdist (Command): else is optional. """ - # XXX name of setup script and config file should be taken - # programmatically from the Distribution object (except - # it doesn't have that capability... yet!) - standards = [('README', 'README.txt'), 'setup.py'] + standards = [('README', 'README.txt'), self.distribution.script_name] for fn in standards: if type (fn) is TupleType: alts = fn -- cgit v1.2.1 From 6df7b0fff005b77bbf39fc0dc1c0e50284d60ede Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 6 Sep 2000 02:18:59 +0000 Subject: Bullet-proofing of 'make_release_tree()': - 'mkpath()' the distribution dir in case of empty manifest - warn if empty manifest - detect, warn about, and skip non-regular files in manifest --- command/sdist.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 1bf297fd..6de703ec 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -405,9 +405,11 @@ class sdist (Command): to be distributed. """ # Create all the directories under 'base_dir' necessary to - # put 'files' there. - dir_util.create_tree (base_dir, files, - verbose=self.verbose, dry_run=self.dry_run) + # put 'files' there; the 'mkpath()' is just so we don't die + # if the manifest happens to be empty. + self.mkpath(base_dir) + dir_util.create_tree(base_dir, files, + verbose=self.verbose, dry_run=self.dry_run) # And walk over the list of files, either making a hard link (if # os.link exists) to each one that doesn't already exist in its @@ -423,10 +425,16 @@ class sdist (Command): link = None msg = "copying files to %s..." % base_dir - self.announce (msg) + if not files: + self.warn("no files to distribute -- empty manifest?") + else: + self.announce (msg) for file in files: - dest = os.path.join (base_dir, file) - self.copy_file (file, dest, link=link) + if not os.path.isfile(file): + self.warn("'%s' not a regular file -- skipping" % file) + else: + dest = os.path.join (base_dir, file) + self.copy_file (file, dest, link=link) # make_release_tree () -- cgit v1.2.1 From 9f06911a43f19a6b026ffb25fbe8858d330b1e69 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 7 Sep 2000 02:38:42 +0000 Subject: Typo fix. --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 026a3ba4..daf55b76 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -43,7 +43,7 @@ class bdist_rpm (Command): # info in setup.cfg, although they are of course free to # supply it on the command line. ('distribution-name', None, - "name of the (Linux) distribution name to which this " + "name of the (Linux) distribution to which this " "RPM applies (*not* the name of the module distribution!)"), ('group', None, "package classification [default: \"Development/Libraries\"]"), -- cgit v1.2.1 From c8a74343d6d9f35457e17ff5dbf40f15ac533ec7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 7 Sep 2000 15:59:22 +0000 Subject: Changes: distutils/command/bdist_wininst.py: - the windows installer is again able to compile after installing the files. Note: The default has changed, the packager has to give --no-target-compile/--no-target-optimize to NOT compile on the target system. (Another note: install_lib's --compile --optimize options have the same semantics to switch off the compilation. Shouldn't the names change?) - All references to specific python versions are gone. - A small bug: raise DistutilsPlatformError ("...") instead of raise DistutilsPlatformError, ("...") - When bdist_wininst creates an installer for one specific python version, this is reflected in the name: Distutils-0.9.2.win32-py15.exe instead of Distutils-0.9.2.win32.exe - bdist_wininst, when run as script, reads the wininst.exe file and rewrites itself. Previously this was done by hand. misc/install.c - All the changes needed for compilation - Deleted a lot of debug/dead code --- command/bdist_wininst.py | 571 +++++++++++++++++++++++++---------------------- 1 file changed, 298 insertions(+), 273 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index d51ea0a8..0ebc5668 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -25,6 +25,10 @@ class bdist_wininst (Command): ('target-version=', 'v', "require a specific python version" + " on the target system"), + ('no-target-compile', 'c', + "do not compile .py to .pyc on the target system"), + ('no-target-optimize', 'o', + "do not compile .py to .pyo (optimized) on the target system"), ('dist-dir=', 'd', "directory to put final built distributions in"), ] @@ -32,8 +36,8 @@ class bdist_wininst (Command): def initialize_options (self): self.bdist_dir = None self.keep_tree = 0 - self.target_compile = 0 - self.target_optimize = 0 + self.no_target_compile = 0 + self.no_target_optimize = 0 self.target_version = None self.dist_dir = None @@ -46,10 +50,6 @@ class bdist_wininst (Command): self.bdist_dir = os.path.join(bdist_base, 'wininst') if not self.target_version: self.target_version = "" - else: - if not self.target_version in ("1.5", "1.6", "2.0"): - raise DistutilsOptionError ( - "target version must be 1.5, 1.6, or 2.0") if self.distribution.has_ext_modules(): short_version = sys.version[:3] if self.target_version and self.target_version != short_version: @@ -66,7 +66,7 @@ class bdist_wininst (Command): if (sys.platform != "win32" and (self.distribution.has_ext_modules() or self.distribution.has_c_libraries())): - raise DistutilsPlatformError, \ + raise DistutilsPlatformError \ ("distribution contains extensions and/or C libraries; " "must be compiled on a Windows 32 platform") @@ -137,6 +137,8 @@ class bdist_wininst (Command): lines.append ("\n[Setup]") lines.append ("info=%s" % repr (info)[1:-1]) lines.append ("pthname=%s.%s" % (metadata.name, metadata.version)) + lines.append ("target_compile=%d" % (not self.no_target_compile)) + lines.append ("target_optimize=%d" % (not self.no_target_optimize)) if self.target_version: lines.append ("target_version=%s" % self.target_version) @@ -153,8 +155,15 @@ class bdist_wininst (Command): cfgdata = self.get_inidata() - installer_name = os.path.join(self.dist_dir, - "%s.win32.exe" % fullname) + if self.target_version: + # if we create an installer for a specific python version, + # it's better to include this in the name + installer_name = os.path.join(self.dist_dir, + "%s.win32-py%s.exe" % + (fullname, self.target_version)) + else: + installer_name = os.path.join(self.dist_dir, + "%s.win32.exe" % fullname) self.announce ("creating %s" % installer_name) file = open (installer_name, "wb") @@ -175,291 +184,307 @@ class bdist_wininst (Command): # class bdist_wininst if __name__ == '__main__': - import base64 - file = r"..\..\misc\wininst.exe" - data = open (file, "rb").read() - bdata = base64.encodestring (data) - open ("EXEDATA", "w").write (bdata) - print "%d %d" % (len (data), len (bdata)) + # recreate EXEDATA from wininst.exe by rewriting this file + import re, base64 + moddata = open ("bdist_wininst.py", "r").read() + exedata = open ("../../misc/wininst.exe", "rb").read() + print "wininst.exe length is %d bytes" % len (exedata) + print "wininst.exe encoded length is %d bytes" % len (base64.encodestring (exedata)) + exp = re.compile ('EXE'+'DATA = """\\\\(\n.*)*\n"""', re.M) + data = exp.sub ('EXE' + 'DATA = """\\\\\n%s"""' % + base64.encodestring (exedata), moddata) + open ("bdist_wininst.py", "w").write (data) + print "bdist_wininst.py recreated" EXEDATA = """\ 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 -AACAAAAAAAAAAAAAAAAAAAABAG4AAAA4AACAAAAAAAAAAAAAAAAAAAABAAAAAABQAAAAMJEAAAgK -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABABrAAAAkAAAgGwAAAC4AACAbQAAAOAAAIBuAAAACAEA -gAAAAAAAAAAAAAAAAAAAAQAJBAAAqAAAADibAAB+AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA -CQQAANAAAAC4nAAAbgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAAD4AAAAKJ4AAFoCAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJBAAAIAEAAIigAABcAQAAAAAAAAAAAAAAAAAAAAAAAAAA -AAD00QAAvNEAAAAAAAAAAAAAAAAAAAHSAADM0QAAAAAAAAAAAAAAAAAADtIAANTRAAAAAAAAAAAA -AAAAAAAb0gAA3NEAAAAAAAAAAAAAAAAAACXSAADk0QAAAAAAAAAAAAAAAAAAMNIAAOzRAAAAAAAA -AAAAAAAAAAAAAAAAAAAAADrSAABI0gAAWNIAAAAAAABm0gAAAAAAAHTSAAAAAAAAhNIAAAAAAACO -0gAAAAAAAJTSAAAAAAAAS0VSTkVMMzIuRExMAEFEVkFQSTMyLmRsbABDT01DVEwzMi5kbGwAR0RJ -MzIuZGxsAE1TVkNSVC5kbGwAVVNFUjMyLmRsbAAATG9hZExpYnJhcnlBAABHZXRQcm9jQWRkcmVz -cwAARXhpdFByb2Nlc3MAAABSZWdDbG9zZUtleQAAAFByb3BlcnR5U2hlZXRBAABUZXh0T3V0QQAA -ZXhpdAAARW5kUGFpbnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAIAAAAgAACABQAAAGAA +AIAAAAAAAAAAAAAAAAAAAAEAbgAAADgAAIAAAAAAAAAAAAAAAAAAAAEAAAAAAFAAAAAwoQAACAoA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAGsAAACQAACAbAAAALgAAIBtAAAA4AAAgG4AAAAIAQCA +AAAAAAAAAAAAAAAAAAABAAkEAACoAAAAOKsAAH4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJ +BAAA0AAAALisAABuAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAAPgAAAAorgAAWgIAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAAAgAQAAiLAAAFwBAAAAAAAAAAAAAAAAAAAAAAAAAAAA +APThAAC84QAAAAAAAAAAAAAAAAAAAeIAAMzhAAAAAAAAAAAAAAAAAAAO4gAA1OEAAAAAAAAAAAAA +AAAAABviAADc4QAAAAAAAAAAAAAAAAAAJeIAAOThAAAAAAAAAAAAAAAAAAAw4gAA7OEAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAOuIAAEjiAABY4gAAAAAAAGbiAAAAAAAAdOIAAAAAAACE4gAAAAAAAI7i +AAAAAAAAlOIAAAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIuZGxsAENPTUNUTDMyLmRsbABHREkz +Mi5kbGwATVNWQ1JULmRsbABVU0VSMzIuZGxsAABMb2FkTGlicmFyeUEAAEdldFByb2NBZGRyZXNz +AABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEAAFRleHRPdXRBAABl +eGl0AABFbmRQYWludAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== """ + +# --- EOF --- -- cgit v1.2.1 From b5474c8672e2a3114d5225e07dfc855424ddc737 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Sat, 9 Sep 2000 19:52:49 +0000 Subject: The windows installer must also look under the HKEY_CURRENT_USER key for python installations, not only under HKEY_LOCAL_MACHINE. --- command/bdist_wininst.py | 480 +++++++++++++++++++++++------------------------ 1 file changed, 240 insertions(+), 240 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 0ebc5668..5e94047c 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -201,7 +201,7 @@ TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v ZGUuDQ0KJAAAAAAAAADqs5WMrtL7367S+9+u0vvf1c7336/S+98tzvXfrNL731Hy/9+s0vvfzM3o 36bS+9+u0vrf89L7367S+9+j0vvfUfLx36PS+99p1P3fr9L731JpY2iu0vvfAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwDytrc5AAAAAAAAAADgAA8BCwEGAABAAAAAEAAAAJAAAPDUAAAA +AAAAAAAAAAAAAAAAUEUAAEwBAwBmkbo5AAAAAAAAAADgAA8BCwEGAABAAAAAEAAAAJAAABDVAAAA oAAAAOAAAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAA8AAAAAQAAAAAAAACAAAAAAAQAAAQ AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAw4QAAcAEAAADgAAAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -214,7 +214,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAkSW5mbzogVGhpcyBmaWxlIGlz IHBhY2tlZCB3aXRoIHRoZSBVUFggZXhlY3V0YWJsZSBwYWNrZXIgaHR0cDovL3VweC50c3gub3Jn ICQKACRJZDogVVBYIDEuMDEgQ29weXJpZ2h0IChDKSAxOTk2LTIwMDAgdGhlIFVQWCBUZWFtLiBB -bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCgXl0CO4+gFz0bYAAPA0AAAAsAAAJgEAyf+/ +bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCjUEmknnJr9G0bYAAAU1AAAAsAAAJgEABf+/ /f9TVVaLdCQUhfZXdHaLfAi9EHBAAIA+AHRoalxWbP/29v8V/GANi/BZHll0V4AmAFcRmP833//Y g/v/dR5qD5SFwHUROUQkHHQLV9na//9VagX/VCQog8QM9sMQdR1otwAAJJD/T9itg10cACHGBlxG dZNqAVhfXr/7//9dW8NVi+yD7BCLRRRTVleLQBaLPXg0iUX4M/a79u7+d0PAOXUIdQfHRQgBDFZo @@ -222,244 +222,244 @@ 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 -- cgit v1.2.1 From fdee9cba1357af7cf490d43b5a994fb215155582 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Sat, 9 Sep 2000 21:15:12 +0000 Subject: The installer now displays info about version of distutils used to create the distribution and the creation date. Takes care of the extra_path argument to the setup function, installs the modules into /extra_path and creates a -pth file (like install_lib does). --- command/bdist_wininst.py | 541 ++++++++++++++++++++++++----------------------- 1 file changed, 278 insertions(+), 263 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 5e94047c..2e535d51 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -84,6 +84,13 @@ class bdist_wininst (Command): self.announce ("installing to %s" % self.bdist_dir) install.ensure_finalized() + + # save the path_file and extra_dirs options + # created by the install command if an extra_path + # argument has been supplied + self.extra_dirs = install.extra_dirs + self.path_file = install.path_file + install.run() # And make an archive relative to the root of the @@ -136,14 +143,22 @@ class bdist_wininst (Command): # the installer runtime. lines.append ("\n[Setup]") lines.append ("info=%s" % repr (info)[1:-1]) - lines.append ("pthname=%s.%s" % (metadata.name, metadata.version)) lines.append ("target_compile=%d" % (not self.no_target_compile)) lines.append ("target_optimize=%d" % (not self.no_target_optimize)) if self.target_version: lines.append ("target_version=%s" % self.target_version) + if (self.path_file): + lines.append ("path_file=%s" % self.path_file) + if (self.extra_dirs): + lines.append ("extra_dirs=%s" % self.extra_dirs) title = self.distribution.get_fullname() lines.append ("title=%s" % repr (title)[1:-1]) + import time + import distutils + build_info = "Build %s with distutils-%s" % \ + (time.ctime (time.time()), distutils.__version__) + lines.append ("build_info=%s" % build_info) return string.join (lines, "\n") # get_inidata() @@ -198,287 +213,287 @@ if __name__ == '__main__': EXEDATA = """\ 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 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAGsAAACQAACAbAAAALgAAIBtAAAA4AAAgG4AAAAIAQCA -AAAAAAAAAAAAAAAAAAABAAkEAACoAAAAOKsAAH4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJ -BAAA0AAAALisAABuAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAAPgAAAAorgAAWgIAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAAAgAQAAiLAAAFwBAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAABAAkEAACoAAAAOKsAAKABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJ +BAAA0AAAANisAABiAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAAPgAAABArgAAWgIAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAAAgAQAAoLAAAFwBAAAAAAAAAAAAAAAAAAAAAAAAAAAA APThAAC84QAAAAAAAAAAAAAAAAAAAeIAAMzhAAAAAAAAAAAAAAAAAAAO4gAA1OEAAAAAAAAAAAAA AAAAABviAADc4QAAAAAAAAAAAAAAAAAAJeIAAOThAAAAAAAAAAAAAAAAAAAw4gAA7OEAAAAAAAAA AAAAAAAAAAAAAAAAAAAAOuIAAEjiAABY4gAAAAAAAGbiAAAAAAAAdOIAAAAAAACE4gAAAAAAAI7i AAAAAAAAlOIAAAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIuZGxsAENPTUNUTDMyLmRsbABHREkz Mi5kbGwATVNWQ1JULmRsbABVU0VSMzIuZGxsAABMb2FkTGlicmFyeUEAAEdldFByb2NBZGRyZXNz -AABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEAAFRleHRPdXRBAABl -eGl0AABFbmRQYWludAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEAAFRleHRPdXRBAABm +cmVlAABFbmRQYWludAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -- cgit v1.2.1 From cde132acfb8b07bd59966d7392c82c4fb2de2b2b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 10 Sep 2000 01:21:27 +0000 Subject: Added --python and --fix-python options for better control over what interpreter the .spec file refers to. Cosmetic tweaks. --- command/bdist_rpm.py | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index daf55b76..9bddddce 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -7,7 +7,7 @@ distributions).""" __revision__ = "$Id$" -import os, string +import sys, os, string import glob from types import * from distutils.core import Command, DEBUG @@ -28,6 +28,12 @@ class bdist_rpm (Command): ('dist-dir=', 'd', "directory to put final RPM files in " "(and .spec files if --spec-only)"), + ('python=', None, + "path to Python interpreter to hard-code in the .spec file " + "(default: \"python\")"), + ('fix-python', None, + "hard-code the exact path to the current Python interpreter in " + "the .spec file"), ('spec-only', None, "only regenerate spec file"), ('source-only', None, @@ -114,6 +120,8 @@ class bdist_rpm (Command): self.bdist_base = None self.rpm_base = None self.dist_dir = None + self.python = None + self.fix_python = None self.spec_only = None self.binary_only = None self.source_only = None @@ -159,6 +167,15 @@ class bdist_rpm (Command): "you must specify --rpm-base in RPM 2 mode" self.rpm_base = os.path.join(self.bdist_base, "rpm") + if self.python is None: + if self.fix_python: + self.python = sys.executable + else: + self.python = "python" + elif self.fix_python: + raise DistutilsOptionError, \ + "--python and --fix-python are mutually exclusive options" + if os.name != 'posix': raise DistutilsPlatformError, \ ("don't know how to create RPM " @@ -275,21 +292,21 @@ class bdist_rpm (Command): # build package - self.announce('Building RPMs') - rpm_args = ['rpm',] + self.announce('building RPMs') + rpm_cmd = ['rpm'] if self.source_only: # what kind of RPMs? - rpm_args.append('-bs') + rpm_cmd.append('-bs') elif self.binary_only: - rpm_args.append('-bb') + rpm_cmd.append('-bb') else: - rpm_args.append('-ba') + rpm_cmd.append('-ba') if self.rpm3_mode: - rpm_args.extend(['--define', + rpm_cmd.extend(['--define', '_topdir %s/%s' % (os.getcwd(), self.rpm_base),]) if self.clean: - rpm_args.append('--clean') - rpm_args.append(spec_path) - self.spawn(rpm_args) + rpm_cmd.append('--clean') + rpm_cmd.append(spec_path) + self.spawn(rpm_cmd) # XXX this is a nasty hack -- we really should have a proper way to # find out the names of the RPM files created; also, this assumes @@ -398,10 +415,10 @@ class bdist_rpm (Command): # rpm scripts # figure out default build script + def_build = "%s setup.py build" % self.python if self.use_rpm_opt_flags: - def_build = 'env CFLAGS="$RPM_OPT_FLAGS" python setup.py build' - else: - def_build = 'python setup.py build' + def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build + # insert contents of files # XXX this is kind of misleading: user-supplied options are files @@ -412,9 +429,9 @@ class bdist_rpm (Command): ('prep', 'prep_script', "%setup"), ('build', 'build_script', def_build), ('install', 'install_script', - "python setup.py install " - "--root=$RPM_BUILD_ROOT " - "--record=INSTALLED_FILES"), + ("%s setup.py install " + "--root=$RPM_BUILD_ROOT " + "--record=INSTALLED_FILES") % self.python), ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), ('pre', 'pre_install', None), ('post', 'post_install', None), -- cgit v1.2.1 From 9a1e68da5031587d22fa58f20a36e3d12f76a9aa Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 11 Sep 2000 00:47:35 +0000 Subject: Added --plat-name option to override sysconfig.get_platform() in generated filenames. --- command/bdist.py | 11 +++++++++-- command/bdist_dumb.py | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 1c862d58..cc0c3985 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -32,6 +32,9 @@ class bdist (Command): user_options = [('bdist-base=', 'b', "temporary directory for creating built distributions"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), ('formats=', None, "formats for distribution (comma-separated list)"), ('dist-dir=', 'd', @@ -70,6 +73,7 @@ class bdist (Command): def initialize_options (self): self.bdist_base = None + self.plat_name = None self.formats = None self.dist_dir = None @@ -77,13 +81,16 @@ class bdist (Command): def finalize_options (self): + # have to finalize 'plat_name' before 'bdist_base' + if self.plat_name is None: + self.plat_name = get_platform() + # 'bdist_base' -- parent of per-built-distribution-format # temporary directories (eg. we'll probably have # "build/bdist./dumb", "build/bdist./rpm", etc.) if self.bdist_base is None: build_base = self.get_finalized_command('build').build_base - plat = get_platform() - self.bdist_base = os.path.join (build_base, 'bdist.' + plat) + self.bdist_base = os.path.join(build_base, 'bdist.' + self.plat_name) self.ensure_string_list('formats') if self.formats is None: diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index e93c6b72..0fb8e83b 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -20,6 +20,9 @@ class bdist_dumb (Command): user_options = [('bdist-dir=', 'd', "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), ('format=', 'f', "archive format to create (tar, ztar, gztar, zip)"), ('keep-tree', 'k', @@ -35,6 +38,7 @@ class bdist_dumb (Command): def initialize_options (self): self.bdist_dir = None + self.plat_name = None self.format = None self.keep_tree = 0 self.dist_dir = None @@ -43,6 +47,7 @@ class bdist_dumb (Command): def finalize_options (self): + if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'dumb') @@ -55,7 +60,9 @@ class bdist_dumb (Command): ("don't know how to create dumb built distributions " + "on platform %s") % os.name - self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name')) # finalize_options() @@ -74,7 +81,7 @@ class bdist_dumb (Command): # And make an archive relative to the root of the # pseudo-installation tree. archive_basename = "%s.%s" % (self.distribution.get_fullname(), - get_platform()) + self.plat_name) print "self.bdist_dir = %s" % self.bdist_dir print "self.format = %s" % self.format self.make_archive (os.path.join(self.dist_dir, archive_basename), -- cgit v1.2.1 From 445af86d1bb43c3904c1f0542efb72d1fe21ba1e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 11 Sep 2000 00:50:37 +0000 Subject: Delete some debugging prints. --- command/bdist_dumb.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 0fb8e83b..2da9c487 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -82,8 +82,6 @@ class bdist_dumb (Command): # pseudo-installation tree. archive_basename = "%s.%s" % (self.distribution.get_fullname(), self.plat_name) - print "self.bdist_dir = %s" % self.bdist_dir - print "self.format = %s" % self.format self.make_archive (os.path.join(self.dist_dir, archive_basename), self.format, root_dir=self.bdist_dir) -- cgit v1.2.1 From 561b706a78a2adb9f8909bf17b31c031f42a8cc9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 12 Sep 2000 00:07:49 +0000 Subject: Bastian Kleineidam: fix so it cleans up the temporary script-building directory too. Also generally cleaned up the code. --- command/clean.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/command/clean.py b/command/clean.py index 1d8c7b22..2f3597fd 100644 --- a/command/clean.py +++ b/command/clean.py @@ -20,6 +20,8 @@ class clean (Command): "build directory for all modules (default: 'build.build-lib')"), ('build-temp=', 't', "temporary build directory (default: 'build.build-temp')"), + ('build-scripts=', None, + "build directory for scripts (default: 'build.build-scripts')"), ('bdist-base=', None, "temporary directory for built distributions"), ('all', 'a', @@ -30,6 +32,7 @@ class clean (Command): self.build_base = None self.build_lib = None self.build_temp = None + self.build_scripts = None self.bdist_base = None self.all = None @@ -37,6 +40,7 @@ class clean (Command): self.set_undefined_options('build', ('build_base', 'build_base'), ('build_lib', 'build_lib'), + ('build_scripts', 'build_scripts'), ('build_temp', 'build_temp')) self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) @@ -50,27 +54,16 @@ class clean (Command): self.warn ("'%s' does not exist -- can't clean it" % self.build_temp) - - - if self.all: - # remove the module build directory (unless already gone) - if os.path.exists (self.build_lib): - remove_tree (self.build_lib, self.verbose, self.dry_run) - else: - self.warn ("'%s' does not exist -- can't clean it" % - self.build_lib) - - # remove the temporary directory used for creating built - # distributions (default "build/bdist") -- eg. type of - # built distribution will have its own subdirectory under - # "build/bdist", but they'll be taken care of by - # 'remove_tree()'. - if os.path.exists (self.bdist_base): - remove_tree (self.bdist_base, self.verbose, self.dry_run) - else: - self.warn ("'%s' does not exist -- can't clean it" % - self.bdist_base) + # remove build directories + for directory in (self.build_lib, + self.bdist_base, + self.build_scripts): + if os.path.exists (directory): + remove_tree (directory, self.verbose, self.dry_run) + else: + self.warn ("'%s' does not exist -- can't clean it" % + directory) # just for the heck of it, try to remove the base build directory: # we might have emptied it right now, but if not we don't care -- cgit v1.2.1 From e4b265d05af821755af79ff534c0afb4f4cdfedb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 13 Sep 2000 00:12:11 +0000 Subject: Fix install directories on Mac OS: now everything goes to :Lib:site-packages. --- command/install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/install.py b/command/install.py index 2a59e16b..2cb7871f 100644 --- a/command/install.py +++ b/command/install.py @@ -38,8 +38,8 @@ INSTALL_SCHEMES = { 'data' : '$base', }, 'mac': { - 'purelib': '$base:Lib', - 'platlib': '$base:Mac:PlugIns', + 'purelib': '$base:Lib:site-packages', + 'platlib': '$base:Lib:site-packages', 'headers': '$base:Include:$dist_name', 'scripts': '$base:Scripts', 'data' : '$base', -- cgit v1.2.1 From f322172314e80fb68beeb9438853db40cef0e116 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 13 Sep 2000 00:44:09 +0000 Subject: Bump version to 0.9.3pre. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 16a3984b..34f55a2f 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.9.2" +__version__ = "0.9.3pre" -- cgit v1.2.1 From 1d693fbcc485be61709c3a033b0b915a97cd31ea Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 13 Sep 2000 01:02:25 +0000 Subject: Added --force (-f) option to force installation (including bytecode compilation). --- command/install.py | 6 ++++-- command/install_data.py | 3 +++ command/install_headers.py | 7 ++++++- command/install_lib.py | 3 +++ command/install_scripts.py | 5 ++++- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/command/install.py b/command/install.py index 2cb7871f..2332770c 100644 --- a/command/install.py +++ b/command/install.py @@ -85,8 +85,9 @@ class install (Command): ('install-data=', None, "installation directory for data files"), - # For lazy debuggers who just want to test the install - # commands without rerunning "build" all the time + # Miscellaneous control options + ('force', 'f', + "force installation (overwrite any existing files)"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), @@ -146,6 +147,7 @@ class install (Command): self.extra_path = None self.install_path_file = 0 + self.force = 0 self.skip_build = 0 # These are only here as a conduit from the 'build' command to the diff --git a/command/install_data.py b/command/install_data.py index 9193f919..6cfc7d41 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -22,18 +22,21 @@ class install_data (Command): "(default: installation base dir)"), ('root=', None, "install everything relative to this alternate root directory"), + ('force', 'f', "force installation (overwrite existing files)"), ] def initialize_options (self): self.install_dir = None self.outfiles = [] self.root = None + self.force = 0 self.data_files = self.distribution.data_files def finalize_options (self): self.set_undefined_options('install', ('install_data', 'install_dir'), ('root', 'root'), + ('force', 'force'), ) def run (self): diff --git a/command/install_headers.py b/command/install_headers.py index 2e6ce869..5c06d574 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -17,16 +17,21 @@ class install_headers (Command): user_options = [('install-dir=', 'd', "directory to install header files to"), + ('force', 'f', + "force installation (overwrite existing files)"), ] def initialize_options (self): self.install_dir = None + self.force = 0 self.outfiles = [] def finalize_options (self): self.set_undefined_options('install', - ('install_headers', 'install_dir')) + ('install_headers', 'install_dir'), + ('force', 'force')) + def run (self): headers = self.distribution.headers diff --git a/command/install_lib.py b/command/install_lib.py index 879a7d00..2d19e5b9 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -13,6 +13,7 @@ class install_lib (Command): user_options = [ ('install-dir=', 'd', "directory to install to"), ('build-dir=','b', "build directory (where to install from)"), + ('force', 'f', "force installation (overwrite existing files)"), ('compile', 'c', "compile .py to .pyc"), ('optimize', 'o', "compile .py to .pyo (optimized)"), ('skip-build', None, "skip the build steps"), @@ -23,6 +24,7 @@ class install_lib (Command): # let the 'install' command dictate our installation directory self.install_dir = None self.build_dir = None + self.force = 0 self.compile = 1 self.optimize = 1 self.skip_build = None @@ -35,6 +37,7 @@ class install_lib (Command): self.set_undefined_options ('install', ('build_lib', 'build_dir'), ('install_lib', 'install_dir'), + ('force', 'force'), ('compile_py', 'compile'), ('optimize_py', 'optimize'), ('skip_build', 'skip_build'), diff --git a/command/install_scripts.py b/command/install_scripts.py index 3eb3963c..d506f90f 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -18,11 +18,13 @@ class install_scripts (Command): user_options = [ ('install-dir=', 'd', "directory to install scripts to"), ('build-dir=','b', "build directory (where to install from)"), + ('force', 'f', "force installation (overwrite existing files)"), ('skip-build', None, "skip the build steps"), ] def initialize_options (self): self.install_dir = None + self.force = 0 self.build_dir = None self.skip_build = None @@ -30,13 +32,14 @@ class install_scripts (Command): self.set_undefined_options('build', ('build_scripts', 'build_dir')) self.set_undefined_options ('install', ('install_scripts', 'install_dir'), + ('force', 'force'), ('skip_build', 'skip_build'), ) def run (self): if not self.skip_build: self.run_command('build_scripts') - self.outfiles = self.copy_tree (self.build_dir, self.install_dir) + self.outfiles = self.copy_tree(self.build_dir, self.install_dir) if os.name == 'posix': # Set the executable bits (owner, group, and world) on # all the scripts we just installed. -- cgit v1.2.1 From 5f191c24e8ad0103340029f2d342a8d643f8e1fc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 00:03:13 +0000 Subject: Fixed so 'parse_makefile()' uses the TextFile class to ensure that comments are stripped and lines are joined according to the backslash convention. --- sysconfig.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 3774ab25..27c08dbf 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -154,7 +154,7 @@ def parse_config_h(fp, g=None): g[m.group(1)] = 0 return g -def parse_makefile(fp, g=None): +def parse_makefile(fn, g=None): """Parse a Makefile-style file. A dictionary containing name/value pairs is returned. If an @@ -162,15 +162,18 @@ def parse_makefile(fp, g=None): used instead of a new dictionary. """ + from distutils.text_file import TextFile + fp = TextFile(fn, strip_comments=1, join_lines=1) + if g is None: g = {} - variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)\n") + variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") done = {} notdone = {} - # + while 1: line = fp.readline() - if not line: + if line is None: break m = variable_rx.match(line) if m: @@ -233,7 +236,7 @@ def _init_posix(): # load the installed Makefile: try: filename = get_makefile_filename() - file = open(filename) + parse_makefile(filename, g) except IOError, msg: my_msg = "invalid Python installation: unable to open %s" % filename if hasattr(msg, "strerror"): @@ -241,7 +244,6 @@ def _init_posix(): raise DistutilsPlatformError, my_msg - parse_makefile(file, g) # On AIX, there are wrong paths to the linker scripts in the Makefile # -- these paths are relative to the Python source, but when installed -- cgit v1.2.1 From c0c287bddf8631bd8bd8ea78a02b9e4bc2724a8a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 01:15:08 +0000 Subject: Changed from eager parsing of Makefile (at import time) to lazy: only do all that work when someone asks for a "configuration variable" from the Makefile. Details: - added 'get_config_vars()': responsible for calling one of the '_init_*()' functions to figure things out for this platform, and to provide an interface to the resulting dictionary - added 'get_config_var()' as a simple interface to the dictionary loaded by 'get_config_vars()' - changed the '_init_*()' functions so they load the global dictionary '_config_vars', rather than spewing their findings all over the module namespace - don't delete the '_init_*()' functions when done importing - adjusted 'customize_compiler()' to the new regime --- sysconfig.py | 88 +++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 27c08dbf..de35d96a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -15,7 +15,7 @@ import sys from errors import DistutilsPlatformError - +# These are needed in a couple of spots, so just compute them once. PREFIX = os.path.normpath(sys.prefix) EXEC_PREFIX = os.path.normpath(sys.exec_prefix) @@ -103,15 +103,18 @@ def customize_compiler (compiler): that varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - cc_cmd = CC + ' ' + OPT + (cc, opt, ccshared, ldshared, so_ext) = \ + get_config_vars('CC', 'OPT', 'CCSHARED', 'LDSHARED', 'SO') + + cc_cmd = cc + ' ' + opt compiler.set_executables( - preprocessor=CC + " -E", # not always! + preprocessor=cc + " -E", # not always! compiler=cc_cmd, - compiler_so=cc_cmd + ' ' + CCSHARED, - linker_so=LDSHARED, - linker_exe=CC) + compiler_so=cc_cmd + ' ' + ccshared, + linker_so=ldshared, + linker_exe=cc) - compiler.shared_lib_extension = SO + compiler.shared_lib_extension = so_ext def get_config_h_filename(): @@ -230,9 +233,11 @@ def parse_makefile(fn, g=None): return g +_config_vars = None + def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" - g = globals() + g = {} # load the installed Makefile: try: filename = get_makefile_filename() @@ -257,7 +262,7 @@ def _init_posix(): g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) - if sys.platform == 'beos': + elif sys.platform == 'beos': # Linker script is in the config directory. In the Makefile it is # relative to the srcdir, which after installation no longer makes @@ -272,12 +277,15 @@ def _init_posix(): # it's taken care of for them by the 'build_ext.get_libraries()' # method.) g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % - (linkerscript, sys.prefix, sys.version[0:3])) + (linkerscript, PREFIX, sys.version[0:3])) + + global _config_vars + _config_vars = g def _init_nt(): """Initialize the module as appropriate for NT""" - g = globals() + g = {} # set basic install directories g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) @@ -287,12 +295,14 @@ def _init_nt(): g['SO'] = '.pyd' g['EXE'] = ".exe" - g['exec_prefix'] = EXEC_PREFIX + + global _config_vars + _config_vars = g def _init_mac(): """Initialize the module as appropriate for Macintosh systems""" - g = globals() + g = {} # set basic install directories g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) @@ -301,23 +311,51 @@ def _init_mac(): g['INCLUDEPY'] = get_python_inc(plat_specific=0) g['SO'] = '.ppc.slb' - g['exec_prefix'] = EXEC_PREFIX - print sys.prefix, PREFIX # XXX are these used anywhere? g['install_lib'] = os.path.join(EXEC_PREFIX, "Lib") g['install_platlib'] = os.path.join(EXEC_PREFIX, "Mac", "Lib") + global _config_vars + _config_vars = g -try: - exec "_init_" + os.name -except NameError: - # not needed for this platform - pass -else: - exec "_init_%s()" % os.name +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. Generally this includes + everything needed to build extensions and install both pure modules and + extensions. On Unix, this means every variable defined in Python's + installed Makefile; on Windows and Mac OS it's a much smaller set. -del _init_posix -del _init_nt -del _init_mac + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _config_vars + if _config_vars is None: + from pprint import pprint + func = globals().get("_init_" + os.name) + if func: + func() + else: + _config_vars = {} + + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # Distutils. + _config_vars['prefix'] = PREFIX + _config_vars['exec_prefix'] = EXEC_PREFIX + + if args: + vals = [] + for name in args: + vals.append(_config_vars.get(name)) + return vals + else: + return _config_vars + +def get_config_var(name): + """Return the value of a single variable using the dictionary + returned by 'get_config_vars()'. Equivalent to + get_config_vars().get(name) + """ + return get_config_vars().get(name) -- cgit v1.2.1 From 4f4854922673817fb057229ff42e7c47fded3f2c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 01:16:14 +0000 Subject: Revamped 'get_platform()' to try and do something reasonably smart on POSIX platforms, ie. get a little more detail than 'sys.platform' gives. --- util.py | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/util.py b/util.py index 2487f6da..46888712 100644 --- a/util.py +++ b/util.py @@ -13,11 +13,49 @@ from distutils.spawn import spawn def get_platform (): - """Return a string (suitable for tacking onto directory names) that - identifies the current platform. Currently, this is just - 'sys.platform'. + """Return a string that identifies the current platform. This is used + mainly to distinguish platform-specific build directories and + platform-specific built distributions. Typically includes the OS name + and version and the architecture (as supplied by 'os.uname()'), + although the exact information included depends on the OS; eg. for IRIX + the architecture isn't particularly important (IRIX only runs on SGI + hardware), but for Linux the kernel version isn't particularly + important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + irix-5.3 + irix64-6.2 + + For non-POSIX platforms, currently just returns 'sys.platform'. """ - return sys.platform + if os.name != "posix": + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + + (osname, host, release, version, machine) = os.uname() + osname = string.lower(osname) + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # fall through to standard osname-release-machine representation + elif osname[:4] == "irix": # could be "irix64"! + return "%s-%s" % (osname, release) + + return "%s-%s-%s" % (osname, release, machine) + +# get_platform () def convert_path (pathname): -- cgit v1.2.1 From 4959d02600d04cf81f26e89cf083cc7455c5b736 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 01:19:03 +0000 Subject: Adjust to the new sysconfig regime: use 'get_config_var()' instead of globals from sysconfig. --- command/build_ext.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 3f714c54..76da0047 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -528,12 +528,13 @@ class build_ext (Command): "foo\bar.pyd"). """ - from distutils import sysconfig + from distutils.sysconfig import get_config_var ext_path = string.split (ext_name, '.') # extensions in debug_mode are named 'module_d.pyd' under windows + so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: - return apply (os.path.join, ext_path) + '_d' + sysconfig.SO - return apply (os.path.join, ext_path) + sysconfig.SO + return apply (os.path.join, ext_path) + '_d' + so_ext + return apply (os.path.join, ext_path) + so_ext def get_ext_libname (self, ext_name): # create a filename for the (unneeded) lib-file. -- cgit v1.2.1 From 637280f9c621536fbea5a6030b991aa0c3b2364f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 01:20:10 +0000 Subject: Adjust to the new sysconfig regime: use 'get_config_vars()' instead of globals from sysconfig. Added 'prefix' and 'exec_prefix' to the list of variables that can be expanded in installation directories (preserving the stupid old names of 'sys_prefix' and 'sys_exec_prefix, though). --- command/install.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/command/install.py b/command/install.py index 2332770c..938922a6 100644 --- a/command/install.py +++ b/command/install.py @@ -9,7 +9,7 @@ __revision__ = "$Id$" import sys, os, string from types import * from distutils.core import Command, DEBUG -from distutils import sysconfig +from distutils.sysconfig import get_config_vars from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root from distutils.errors import DistutilsOptionError @@ -226,13 +226,16 @@ class install (Command): # about needing recursive variable expansion (shudder). py_version = (string.split(sys.version))[0] + prefix = get_config_vars('prefix', 'exec_prefix') self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), 'dist_fullname': self.distribution.get_fullname(), 'py_version': py_version, 'py_version_short': py_version[0:3], - 'sys_prefix': sysconfig.PREFIX, - 'sys_exec_prefix': sysconfig.EXEC_PREFIX, + 'sys_prefix': prefix, + 'prefix': prefix, + 'sys_exec_prefix': exec_prefix, + 'exec_prefix': exec_prefix, } self.expand_basedirs () -- cgit v1.2.1 From 4018076a7ebe9adf03891e2eef88f35b3f6b2a1f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 01:21:07 +0000 Subject: Added 'warn_dir' option so other code can sneak in and disable the sometimes inappropriate warning about where we're installing data files. --- command/install_data.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/command/install_data.py b/command/install_data.py index 6cfc7d41..af348f51 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -30,7 +30,9 @@ class install_data (Command): self.outfiles = [] self.root = None self.force = 0 + self.data_files = self.distribution.data_files + self.warn_dir = 1 def finalize_options (self): self.set_undefined_options('install', @@ -44,9 +46,10 @@ class install_data (Command): for f in self.data_files: if type(f) == StringType: # it's a simple file, so copy it - self.warn("setup script did not provide a directory for " - "'%s' -- installing right in '%s'" % - (f, self.install_dir)) + if self.warn_dir: + self.warn("setup script did not provide a directory for " + "'%s' -- installing right in '%s'" % + (f, self.install_dir)) out = self.copy_file(f, self.install_dir) self.outfiles.append(out) else: -- cgit v1.2.1 From 1507bd55580f4662f9319edd26efdcc698a33cb2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 01:44:45 +0000 Subject: Document the directory separatory for include dir and library dir lists. --- command/build_ext.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 76da0047..f880a7a8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -49,6 +49,7 @@ class build_ext (Command): # takes care of both command-line and client options # in between initialize_options() and finalize_options()) + sep_by = " (separated by '%s')" % os.pathsep user_options = [ ('build-lib=', 'b', "directory for compiled extension modules"), @@ -58,7 +59,7 @@ class build_ext (Command): "ignore build-lib and put compiled extensions into the source " + "directory alongside your pure Python modules"), ('include-dirs=', 'I', - "list of directories to search for header files"), + "list of directories to search for header files" + sep_by), ('define=', 'D', "C preprocessor macros to define"), ('undef=', 'U', @@ -66,7 +67,7 @@ class build_ext (Command): ('libraries=', 'l', "external C libraries to link with"), ('library-dirs=', 'L', - "directories to search for external C libraries"), + "directories to search for external C libraries" + sep_by), ('rpath=', 'R', "directories to search for shared C libraries at runtime"), ('link-objects=', 'O', -- cgit v1.2.1 From 8a0e231dcf1cd16a0075cca6c78a7996144eb6d2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 01:54:46 +0000 Subject: Include the Python version in the platform-specific build directories: with the recent change in 'get_platform()', we now have directory names like "build/lib-1.5-linux-i586". Idea and original patch by Rene Liebscher. --- command/build.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/command/build.py b/command/build.py index 1e87f23b..1d901282 100644 --- a/command/build.py +++ b/command/build.py @@ -67,6 +67,8 @@ class build (Command): # hardware architecture! self.plat = get_platform () + plat_specifier = sys.version[0:3] + '-' + self.plat + # 'build_purelib' and 'build_platlib' just default to 'lib' and # 'lib.' under the base build directory. We only use one of # them for a given distribution, though -- @@ -74,7 +76,7 @@ class build (Command): self.build_purelib = os.path.join (self.build_base, 'lib') if self.build_platlib is None: self.build_platlib = os.path.join (self.build_base, - 'lib.' + self.plat) + 'lib-' + plat_specifier) # 'build_lib' is the actual directory that we will use for this # particular module distribution -- if user didn't supply it, pick @@ -89,7 +91,7 @@ class build (Command): # "build/temp." if self.build_temp is None: self.build_temp = os.path.join (self.build_base, - 'temp.' + self.plat) + 'temp-' + plat_specifier) if self.build_scripts is None: self.build_scripts = os.path.join (self.build_base, 'scripts') # finalize_options () -- cgit v1.2.1 From df1d24963b20a7532d52a441e7f39cb3c9b7c892 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 01:59:06 +0000 Subject: Typo fix. --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 938922a6..7182857d 100644 --- a/command/install.py +++ b/command/install.py @@ -226,7 +226,7 @@ class install (Command): # about needing recursive variable expansion (shudder). py_version = (string.split(sys.version))[0] - prefix = get_config_vars('prefix', 'exec_prefix') + (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), 'dist_fullname': self.distribution.get_fullname(), -- cgit v1.2.1 From e24f5d6128e2325fa37f0f37819c253b35fbaae0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 02:06:45 +0000 Subject: Tweaked the build temp dir names again. --- command/build.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/command/build.py b/command/build.py index 1d901282..15476ca1 100644 --- a/command/build.py +++ b/command/build.py @@ -62,12 +62,7 @@ class build (Command): def finalize_options (self): - # Need this to name platform-specific directories, but sys.platform - # is not enough -- it only names the OS and version, not the - # hardware architecture! - self.plat = get_platform () - - plat_specifier = sys.version[0:3] + '-' + self.plat + plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3]) # 'build_purelib' and 'build_platlib' just default to 'lib' and # 'lib.' under the base build directory. We only use one of @@ -76,7 +71,7 @@ class build (Command): self.build_purelib = os.path.join (self.build_base, 'lib') if self.build_platlib is None: self.build_platlib = os.path.join (self.build_base, - 'lib-' + plat_specifier) + 'lib' + plat_specifier) # 'build_lib' is the actual directory that we will use for this # particular module distribution -- if user didn't supply it, pick @@ -91,9 +86,10 @@ class build (Command): # "build/temp." if self.build_temp is None: self.build_temp = os.path.join (self.build_base, - 'temp-' + plat_specifier) + 'temp' + plat_specifier) if self.build_scripts is None: self.build_scripts = os.path.join (self.build_base, 'scripts') + # finalize_options () -- cgit v1.2.1 From fe2e79b7e47c5f822e28eb0aebf83e652fa9192b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:06:57 +0000 Subject: Factored the "sub-command" machinery out to Command. Mainly, this meant removing 'get_sub_commands()', and moving the 'sub_commands' class attribute to the end and restructuring it to conform to the new regime. --- command/install.py | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/command/install.py b/command/install.py index 7182857d..04b325a6 100644 --- a/command/install.py +++ b/command/install.py @@ -101,17 +101,6 @@ class install (Command): "filename in which to record list of installed files"), ] - # 'sub_commands': a list of commands this command might have to run to - # get its work done. Each command is represented as a tuple (method, - # command) where 'method' is the name of a method to call that returns - # true if 'command' (the sub-command name, a string) needs to be run. - # If 'method' is None, assume that 'command' must always be run. - sub_commands = [('has_lib', 'install_lib'), - ('has_headers', 'install_headers'), - ('has_scripts', 'install_scripts'), - ('has_data', 'install_data'), - ] - def initialize_options (self): @@ -444,20 +433,6 @@ class install (Command): # handle_extra_path () - def get_sub_commands (self): - """Return the list of subcommands that we need to run. This is - based on the 'subcommands' class attribute: each tuple in that list - can name a method that we call to determine if the subcommand needs - to be run for the current distribution.""" - commands = [] - for (method, cmd_name) in self.sub_commands: - if method is not None: - method = getattr(self, method) - if method is None or method(): - commands.append(cmd_name) - return commands - - def run (self): # Obviously have to build before we can install @@ -494,6 +469,8 @@ class install (Command): # run () + # -- Predicates for sub-command list ------------------------------- + def has_lib (self): """Return true if the current distribution has any Python modules to install.""" @@ -544,4 +521,12 @@ class install (Command): "installations)") % filename) + # 'sub_commands': a list of commands this command might have to run to + # get its work done. See cmd.py for more info. + sub_commands = [('install_lib', has_lib), + ('install_headers', has_headers), + ('install_scripts', has_scripts), + ('install_data', has_data), + ] + # class install -- cgit v1.2.1 From 2534b87e2eb30b3657de351d70266460069506dd Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:09:17 +0000 Subject: Added the "sub-command" machinery to formalize the notion of "command families" -- eg. install and its brood, build and its brood, and so forth. Specifically: added the 'sub_commands' class attribute (empty list, sub- classes must override it) and a comment describing it, and the 'get_sub_commands()' method. --- cmd.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/cmd.py b/cmd.py index 474f8f32..61d234bb 100644 --- a/cmd.py +++ b/cmd.py @@ -31,6 +31,23 @@ class Command: command class. """ + # 'sub_commands' formalizes the notion of a "family" of commands, + # eg. "install" as the parent with sub-commands "install_lib", + # "install_headers", etc. The parent of a family of commands + # defines 'sub_commands' as a class attribute; it's a list of + # (command_name : string, predicate : unbound_method | string | None) + # tuples, where 'predicate' is a method of the parent command that + # determines whether the corresponding command is applicable in the + # current situation. (Eg. we "install_headers" is only applicable if + # we have any C header files to install.) If 'predicate' is None, + # that command is always applicable. + # + # 'sub_commands' is usually defined at the *end* of a class, because + # predicates can be unbound methods, so they must already have been + # defined. The canonical example is the "install" command. + sub_commands = [] + + # -- Creation/initialization methods ------------------------------- def __init__ (self, dist): @@ -310,6 +327,20 @@ class Command: self.distribution.run_command (command) + def get_sub_commands (self): + """Determine the sub-commands that are relevant in the current + distribution (ie., that need to be run). This is based on the + 'sub_commands' class attribute: each tuple in that list may include + a method that we call to determine if the subcommand needs to be + run for the current distribution. Return a list of command names. + """ + commands = [] + for (cmd_name, method) in self.sub_commands: + if method is None or method(self): + commands.append(cmd_name) + return commands + + # -- External world manipulation ----------------------------------- def warn (self, msg): -- cgit v1.2.1 From 09dbe7ef0f1498e27bfdeef00ffd238e1c13b62f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:23:28 +0000 Subject: Generalized 'reinitialize_command()' so it can optionally reinitialize the command's sub-commands as well (off by default). This is essential if we want to be be able to run (eg.) "install" twice in one run, as happens when generating multiple built distributions in one run. --- dist.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/dist.py b/dist.py index 1552dc0c..b126a997 100644 --- a/dist.py +++ b/dist.py @@ -733,7 +733,7 @@ class Distribution: (source, command_name, option) setattr(command_obj, option, value) - def reinitialize_command (self, command): + def reinitialize_command (self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first returned by 'get_command_obj()': ie., initialized but not yet finalized. This provides the opportunity to sneak option @@ -743,9 +743,18 @@ class Distribution: 'finalize_options()' or 'ensure_finalized()') before using it for real. - 'command' should be a command name (string) or command object. + 'command' should be a command name (string) or command object. If + 'reinit_subcommands' is true, also reinitializes the command's + sub-commands, as declared by the 'sub_commands' class attribute (if + it has one). See the "install" command for an example. Only + reinitializes the sub-commands that actually matter, ie. those + whose test predicates return true. + Returns the reinitialized command object. """ + print "reinitialize_command: command=%s" % command + print " before: have_run =", self.have_run + from distutils.cmd import Command if not isinstance(command, Command): command_name = command @@ -759,6 +768,15 @@ class Distribution: command.finalized = 0 self.have_run[command_name] = 0 self._set_command_options(command) + + print " after: have_run =", self.have_run + + if reinit_subcommands: + print (" reinitializing sub-commands: %s" % + command.get_sub_commands()) + for sub in command.get_sub_commands(): + self.reinitialize_command(sub, reinit_subcommands) + return command -- cgit v1.2.1 From 927edd6665097270bd2930210f37501f692bbe55 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:25:55 +0000 Subject: In 'reinitialize_subcommand()', pass 'reinit_subcommands' flag on to the real implementation in Distribution. --- cmd.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd.py b/cmd.py index 61d234bb..7866d1b6 100644 --- a/cmd.py +++ b/cmd.py @@ -316,8 +316,9 @@ class Command: # XXX rename to 'get_reinitialized_command()'? (should do the # same in dist.py, if so) - def reinitialize_command (self, command): - return self.distribution.reinitialize_command(command) + def reinitialize_command (self, command, reinit_subcommands=0): + return self.distribution.reinitialize_command( + command, reinit_subcommands) def run_command (self, command): """Run some other command: uses the 'run_command()' method of -- cgit v1.2.1 From 8db8a763ae8c5c0456a161c93a765483887d9130 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:27:17 +0000 Subject: Remove some debugging output from the last change. --- dist.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dist.py b/dist.py index b126a997..c5fe86eb 100644 --- a/dist.py +++ b/dist.py @@ -752,9 +752,6 @@ class Distribution: Returns the reinitialized command object. """ - print "reinitialize_command: command=%s" % command - print " before: have_run =", self.have_run - from distutils.cmd import Command if not isinstance(command, Command): command_name = command @@ -769,11 +766,7 @@ class Distribution: self.have_run[command_name] = 0 self._set_command_options(command) - print " after: have_run =", self.have_run - if reinit_subcommands: - print (" reinitializing sub-commands: %s" % - command.get_sub_commands()) for sub in command.get_sub_commands(): self.reinitialize_command(sub, reinit_subcommands) -- cgit v1.2.1 From ba1539c0c415bebf7bf237cb91e0dc0c007b00bd Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:30:47 +0000 Subject: Ensure sub-commands of "install" are reinitialized too. Run "install" the right way, by calling 'run_command()'. --- command/bdist_dumb.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 2da9c487..afeb4ad3 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -71,12 +71,11 @@ class bdist_dumb (Command): self.run_command ('build') - install = self.reinitialize_command('install') + install = self.reinitialize_command('install', reinit_subcommands=1) install.root = self.bdist_dir self.announce ("installing to %s" % self.bdist_dir) - install.ensure_finalized() - install.run() + self.run_command('install') # And make an archive relative to the root of the # pseudo-installation tree. -- cgit v1.2.1 From 7275ee4bb9a86ab4d196242acc902697a70caa67 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:53:41 +0000 Subject: Renamed --keep-tree option to --keep-temp. --- command/bdist_dumb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index afeb4ad3..1fdbf425 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -25,7 +25,7 @@ class bdist_dumb (Command): "(default: %s)" % get_platform()), ('format=', 'f', "archive format to create (tar, ztar, gztar, zip)"), - ('keep-tree', 'k', + ('keep-temp', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), ('dist-dir=', 'd', @@ -40,7 +40,7 @@ class bdist_dumb (Command): self.bdist_dir = None self.plat_name = None self.format = None - self.keep_tree = 0 + self.keep_temp = 0 self.dist_dir = None # initialize_options() @@ -85,7 +85,7 @@ class bdist_dumb (Command): self.format, root_dir=self.bdist_dir) - if not self.keep_tree: + if not self.keep_temp: remove_tree (self.bdist_dir, self.verbose, self.dry_run) # run() -- cgit v1.2.1 From cedc5acb59abb40151af3df16d4e1c7751608c78 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:54:18 +0000 Subject: Renamed --clean to --no-keep-temp and --noclean to --keep-temp. --- command/bdist_rpm.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 9bddddce..2afc7146 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -97,10 +97,10 @@ class bdist_rpm (Command): "capabilities made obsolete by this package"), # Actions to take when building RPM - ('clean', None, - "clean up RPM build directory [default]"), - ('no-clean', None, + ('keep-temp', 'k', "don't clean up RPM build directory"), + ('no-keep-temp', None, + "clean up RPM build directory [default]"), ('use-rpm-opt-flags', None, "compile with RPM_OPT_FLAGS when building from source RPM"), ('no-rpm-opt-flags', None, @@ -111,7 +111,7 @@ class bdist_rpm (Command): "RPM 2 compatibility mode"), ] - negative_opt = {'no-clean': 'clean', + negative_opt = {'no-keep-temp': 'keep-temp', 'no-rpm-opt-flags': 'use-rpm-opt-flags', 'rpm2-mode': 'rpm3-mode'} @@ -152,7 +152,7 @@ class bdist_rpm (Command): self.build_requires = None self.obsoletes = None - self.clean = 1 + self.keep_temp = 0 self.use_rpm_opt_flags = 1 self.rpm3_mode = 1 @@ -303,7 +303,7 @@ class bdist_rpm (Command): if self.rpm3_mode: rpm_cmd.extend(['--define', '_topdir %s/%s' % (os.getcwd(), self.rpm_base),]) - if self.clean: + if not self.keep_temp: rpm_cmd.append('--clean') rpm_cmd.append(spec_path) self.spawn(rpm_cmd) -- cgit v1.2.1 From 55c0d1f2782c95996bf76de2418aa62fe28de33a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:56:32 +0000 Subject: Renamed --keep-tree to --keep-temp. --- command/bdist_wininst.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 2e535d51..5030ef90 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -19,7 +19,7 @@ class bdist_wininst (Command): user_options = [('bdist-dir=', 'd', "temporary directory for creating the distribution"), - ('keep-tree', 'k', + ('keep-temp', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), ('target-version=', 'v', @@ -35,7 +35,7 @@ class bdist_wininst (Command): def initialize_options (self): self.bdist_dir = None - self.keep_tree = 0 + self.keep_temp = 0 self.no_target_compile = 0 self.no_target_optimize = 0 self.target_version = None @@ -111,7 +111,7 @@ class bdist_wininst (Command): root_dir=root_dir) self.create_exe (arcname, fullname) - if not self.keep_tree: + if not self.keep_temp: remove_tree (self.bdist_dir, self.verbose, self.dry_run) # run() -- cgit v1.2.1 From 8e1b5544e34c5186aa2c8a1c63fd426d2dfae782 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 16:04:59 +0000 Subject: Rene Liebscher: if we have to run the same sub-command multiple times (eg. "bdist_dumb", to generate both ZIP and tar archives in the same run), tell all but the last run to keep temp files -- this just gets rid of the need to pseudo-install the same files multiple times. --- command/bdist.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index cc0c3985..8651e709 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -90,7 +90,8 @@ class bdist (Command): # "build/bdist./dumb", "build/bdist./rpm", etc.) if self.bdist_base is None: build_base = self.get_finalized_command('build').build_base - self.bdist_base = os.path.join(build_base, 'bdist.' + self.plat_name) + self.bdist_base = os.path.join(build_base, + 'bdist.' + self.plat_name) self.ensure_string_list('formats') if self.formats is None: @@ -109,16 +110,28 @@ class bdist (Command): def run (self): + # Figure out which sub-commands we need to run. + commands = [] for format in self.formats: try: - cmd_name = self.format_command[format][0] + commands.append(self.format_command[format][0]) except KeyError: - raise DistutilsOptionError, \ - "invalid format '%s'" % format + raise DistutilsOptionError, "invalid format '%s'" % format + # Reinitialize and run each command. + for i in range(len(self.formats)): + cmd_name = commands[i] sub_cmd = self.reinitialize_command(cmd_name) if cmd_name not in self.no_format_option: - sub_cmd.format = format + sub_cmd.format = self.formats[i] + + print ("bdist.run: format=%s, command=%s, rest=%s" % + (self.formats[i], cmd_name, commands[i+1:])) + + # If we're going to need to run this command again, tell it to + # keep its temporary files around so subsequent runs go faster. + if cmd_name in commands[i+1:]: + sub_cmd.keep_temp = 1 self.run_command (cmd_name) # run() -- cgit v1.2.1 From 41ea88ac7aefa3d488f04485e277ad53e8c023f6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 18:04:55 +0000 Subject: [change from 2000/04/17, propagating now to distutils copy] Dropped the 'collapse_ws' option and replaced it with 'collapse_join' -- it's *much* faster (no 're.sub()') and this is the reason I really added 'collapse_ws', ie. to remove leading whitespace from a line being joined to the previous line. --- text_file.py | 56 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/text_file.py b/text_file.py index 7b29ef4a..f22b3e91 100644 --- a/text_file.py +++ b/text_file.py @@ -9,18 +9,18 @@ lines, and joining lines with backslashes.""" __revision__ = "$Id$" from types import * -import sys, os, string, re +import sys, os, string class TextFile: """Provides a file-like object that takes care of all the things you commonly want to do when processing a text file that has some - line-by-line syntax: strip comments (as long as "#" is your comment - character), skip blank lines, join adjacent lines by escaping the - newline (ie. backslash at end of line), strip leading and/or - trailing whitespace, and collapse internal whitespace. All of these - are optional and independently controllable. + line-by-line syntax: strip comments (as long as "#" is your + comment character), skip blank lines, join adjacent lines by + escaping the newline (ie. backslash at end of line), strip + leading and/or trailing whitespace. All of these are optional + and independently controllable. Provides a 'warn()' method so you can generate warning messages that report physical line number, even if the logical line in question @@ -50,7 +50,7 @@ class TextFile: each line before returning it skip_blanks [default: true} skip lines that are empty *after* stripping comments and - whitespace. (If both lstrip_ws and rstrip_ws are true, + whitespace. (If both lstrip_ws and rstrip_ws are false, then some lines may consist of solely whitespace: these will *not* be skipped, even if 'skip_blanks' is true.) join_lines [default: false] @@ -59,12 +59,9 @@ class TextFile: to it to form one "logical line"; if N consecutive lines end with a backslash, then N+1 physical lines will be joined to form one logical line. - collapse_ws [default: false] - after stripping comments and whitespace and joining physical - lines into logical lines, all internal whitespace (strings of - whitespace surrounded by non-whitespace characters, and not at - the beginning or end of the logical line) will be collapsed - to a single space. + collapse_join [default: false] + strip leading whitespace from lines that are joined to their + predecessor; only matters if (join_lines and not lstrip_ws) Note that since 'rstrip_ws' can strip the trailing newline, the semantics of 'readline()' must differ from those of the builtin file @@ -75,10 +72,10 @@ class TextFile: default_options = { 'strip_comments': 1, 'skip_blanks': 1, - 'join_lines': 0, 'lstrip_ws': 0, 'rstrip_ws': 1, - 'collapse_ws': 0, + 'join_lines': 0, + 'collapse_join': 0, } def __init__ (self, filename=None, file=None, **options): @@ -219,6 +216,8 @@ class TextFile: "end-of-file") return buildup_line + if self.collapse_join: + line = string.lstrip (line) line = buildup_line + line # careful: pay attention to line number when incrementing it @@ -261,10 +260,6 @@ class TextFile: buildup_line = line[0:-2] + '\n' continue - # collapse internal whitespace (*after* joining lines!) - if self.collapse_ws: - line = re.sub (r'(\S)\s+(\S)', r'\1 \2', line) - # well, I guess there's some actual content there: return it return line @@ -295,7 +290,7 @@ if __name__ == "__main__": test_data = """# test file line 3 \\ -continues on next line + continues on next line """ @@ -303,16 +298,21 @@ continues on next line result1 = map (lambda x: x + "\n", string.split (test_data, "\n")[0:-1]) # result 2: just strip comments - result2 = ["\n", "\n", "line 3 \\\n", "continues on next line\n"] + result2 = ["\n", "\n", "line 3 \\\n", " continues on next line\n"] # result 3: just strip blank lines - result3 = ["# test file\n", "line 3 \\\n", "continues on next line\n"] + result3 = ["# test file\n", "line 3 \\\n", " continues on next line\n"] # result 4: default, strip comments, blank lines, and trailing whitespace - result4 = ["line 3 \\", "continues on next line"] + result4 = ["line 3 \\", " continues on next line"] - # result 5: full processing, strip comments and blanks, plus join lines - result5 = ["line 3 continues on next line"] + # result 5: strip comments and blanks, plus join lines (but don't + # "collapse" joined lines + result5 = ["line 3 continues on next line"] + + # result 6: strip comments and blanks, plus join lines (and + # "collapse" joined lines + result6 = ["line 3 continues on next line"] def test_input (count, description, file, expected_result): result = file.readlines () @@ -349,7 +349,11 @@ continues on next line in_file = TextFile (filename, strip_comments=1, skip_blanks=1, join_lines=1, rstrip_ws=1) - test_input (5, "full processing", in_file, result5) + test_input (5, "join lines without collapsing", in_file, result5) + + in_file = TextFile (filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1, collapse_join=1) + test_input (6, "join lines with collapsing", in_file, result6) os.remove (filename) -- cgit v1.2.1 From c21a9987e9f21985cccaa2e3a244a0c456091692 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 18:06:31 +0000 Subject: [change from 2000/08/11, propagating now to distutils copy] Factored the guts of 'warn()' out to 'gen_error()', and added the 'error()' method (trivial thanks to the refactoring). --- text_file.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/text_file.py b/text_file.py index f22b3e91..b7317622 100644 --- a/text_file.py +++ b/text_file.py @@ -134,6 +134,22 @@ class TextFile: self.current_line = None + def gen_error (self, msg, line=None): + outmsg = [] + if line is None: + line = self.current_line + outmsg.append(self.filename + ", ") + if type (line) in (ListType, TupleType): + outmsg.append("lines %d-%d: " % tuple (line)) + else: + outmsg.append("line %d: " % line) + outmsg.append(str(msg)) + return string.join(outmsg, "") + + + def error (self, msg, line=None): + raise ValueError, "error: " + self.gen_error(msg, line) + def warn (self, msg, line=None): """Print (to stderr) a warning message tied to the current logical line in the current file. If the current logical line in the @@ -142,15 +158,7 @@ class TextFile: the current line number; it may be a list or tuple to indicate a range of physical lines, or an integer for a single physical line.""" - - if line is None: - line = self.current_line - sys.stderr.write (self.filename + ", ") - if type (line) in (ListType, TupleType): - sys.stderr.write ("lines %d-%d: " % tuple (line)) - else: - sys.stderr.write ("line %d: " % line) - sys.stderr.write (str (msg) + "\n") + sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n") def readline (self): -- cgit v1.2.1 From 878b6ad60e8f5e4e163ae13074582e08f4ae4b3b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 18:09:22 +0000 Subject: Andrew Kuchling: Fixed precendence bug that meant setting skip_blanks to false didn't work under some circumstances. --- text_file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text_file.py b/text_file.py index b7317622..c983afa4 100644 --- a/text_file.py +++ b/text_file.py @@ -256,7 +256,7 @@ class TextFile: # blank line (whether we rstrip'ed or not)? skip to next line # if appropriate - if line == '' or line == '\n' and self.skip_blanks: + if (line == '' or line == '\n') and self.skip_blanks: continue if self.join_lines: -- cgit v1.2.1 From 0bd2aefe51af28256934aa5b567efb49d09f2f7a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 18:33:36 +0000 Subject: Changed so lines that are all comment (or just whitespace + comment) are completely skipped, rather than being treated as blank lines (and then subject to the 'skip_blanks' flag). This allows us to process old-style Setup files, which rely on hello \\ # boo! there coming out as "hello there". --- text_file.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/text_file.py b/text_file.py index c983afa4..37bffe61 100644 --- a/text_file.py +++ b/text_file.py @@ -201,8 +201,10 @@ class TextFile: pos = string.find (line, "#") if pos == -1: # no "#" -- no comments pass - elif pos == 0 or line[pos-1] != "\\": # it's a comment - + + # It's definitely a comment -- either "#" is the first + # character, or it's elsewhere and unescaped. + elif pos == 0 or line[pos-1] != "\\": # Have to preserve the trailing newline, because it's # the job of a later step (rstrip_ws) to remove it -- # and if rstrip_ws is false, we'd better preserve it! @@ -212,6 +214,16 @@ class TextFile: eol = (line[-1] == '\n') and '\n' or '' line = line[0:pos] + eol + # If all that's left is whitespace, then skip line + # *now*, before we try to join it to 'buildup_line' -- + # that way constructs like + # hello \\ + # # comment that should be ignored + # there + # result in "hello there". + if string.strip(line) == "": + continue + else: # it's an escaped "#" line = string.replace (line, "\\#", "#") @@ -232,7 +244,8 @@ class TextFile: if type (self.current_line) is ListType: self.current_line[1] = self.current_line[1] + 1 else: - self.current_line = [self.current_line, self.current_line+1] + self.current_line = [self.current_line, + self.current_line+1] # just an ordinary line, read it as usual else: if line is None: # eof @@ -271,7 +284,7 @@ class TextFile: # well, I guess there's some actual content there: return it return line - # end readline + # readline () def readlines (self): @@ -298,21 +311,26 @@ if __name__ == "__main__": test_data = """# test file line 3 \\ +# intervening comment continues on next line """ - - # result 1: no fancy options result1 = map (lambda x: x + "\n", string.split (test_data, "\n")[0:-1]) # result 2: just strip comments - result2 = ["\n", "\n", "line 3 \\\n", " continues on next line\n"] + result2 = ["\n", + "line 3 \\\n", + " continues on next line\n"] # result 3: just strip blank lines - result3 = ["# test file\n", "line 3 \\\n", " continues on next line\n"] + result3 = ["# test file\n", + "line 3 \\\n", + "# intervening comment\n", + " continues on next line\n"] # result 4: default, strip comments, blank lines, and trailing whitespace - result4 = ["line 3 \\", " continues on next line"] + result4 = ["line 3 \\", + " continues on next line"] # result 5: strip comments and blanks, plus join lines (but don't # "collapse" joined lines -- cgit v1.2.1 From 7774801f0603d1cb9323ece86720102e31eaac79 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 17 Sep 2000 00:45:18 +0000 Subject: Added 'read_setup_file()' to read old-style Setup files. Could make life easier for people porting Makefile.pre.in-based extensions to Distutils. Also loosened argument-checking in Extension constructor to make life easier for 'read_setup_file()'. --- extension.py | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/extension.py b/extension.py index 1d3112b6..a63ede23 100644 --- a/extension.py +++ b/extension.py @@ -7,6 +7,7 @@ modules in setup scripts.""" __revision__ = "$Id$" +import os, string from types import * @@ -89,9 +90,8 @@ class Extension: assert type(name) is StringType, "'name' must be a string" assert (type(sources) is ListType and - len(sources) >= 1 and map(type, sources) == [StringType]*len(sources)), \ - "'sources' must be a non-empty list of strings" + "'sources' must be a list of strings" self.name = name self.sources = sources @@ -107,3 +107,111 @@ class Extension: self.export_symbols = export_symbols or [] # class Extension + + +def read_setup_file (filename): + from distutils.sysconfig import \ + parse_makefile, expand_makefile_vars, _variable_rx + from distutils.text_file import TextFile + from distutils.util import split_quoted + + # First pass over the file to gather "VAR = VALUE" assignments. + vars = parse_makefile(filename) + + # Second pass to gobble up the real content: lines of the form + # ... [ ...] [ ...] [ ...] + file = TextFile(filename, + strip_comments=1, skip_blanks=1, join_lines=1, + lstrip_ws=1, rstrip_ws=1) + extensions = [] + + while 1: + line = file.readline() + if line is None: # eof + break + if _variable_rx.match(line): # VAR=VALUE, handled in first pass + continue + + if line[0] == line[-1] == "*": + file.warn("'%s' lines not handled yet" % line) + continue + + #print "original line: " + line + line = expand_makefile_vars(line, vars) + words = split_quoted(line) + #print "expanded line: " + line + + # NB. this parses a slightly different syntax than the old + # makesetup script: here, there must be exactly one extension per + # line, and it must be the first word of the line. I have no idea + # why the old syntax supported multiple extensions per line, as + # they all wind up being the same. + + module = words[0] + ext = Extension(module, []) + append_next_word = None + + for word in words[1:]: + if append_next_word is not None: + append_next_word.append(word) + append_next_word = None + continue + + suffix = os.path.splitext(word)[1] + switch = word[0:2] ; value = word[2:] + + if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++"): + # hmm, should we do something about C vs. C++ sources? + # or leave it up to the CCompiler implementation to + # worry about? + ext.sources.append(word) + elif switch == "-I": + ext.include_dirs.append(value) + elif switch == "-D": + equals = string.find(value, "=") + if equals == -1: # bare "-DFOO" -- no value + ext.define_macros.append((value, None)) + else: # "-DFOO=blah" + ext.define_macros.append((value[0:equals], + value[equals+2:])) + elif switch == "-U": + ext.undef_macros.append(value) + elif switch == "-C": # only here 'cause makesetup has it! + ext.extra_compile_args.append(word) + elif switch == "-l": + ext.libraries.append(value) + elif switch == "-L": + ext.library_dirs.append(value) + elif switch == "-R": + ext.runtime_library_dirs.append(value) + elif word == "-rpath": + append_next_word = ext.runtime_library_dirs + elif word == "-Xlinker": + append_next_word = ext.extra_link_args + elif switch == "-u": + ext.extra_link_args.append(word) + if not value: + append_next_word = ext.extra_link_args + elif suffix in (".a", ".so", ".sl", ".o"): + # NB. a really faithful emulation of makesetup would + # append a .o file to extra_objects only if it + # had a slash in it; otherwise, it would s/.o/.c/ + # and append it to sources. Hmmmm. + ext.extra_objects.append(word) + else: + file.warn("unrecognized argument '%s'" % word) + + extensions.append(ext) + + #print "module:", module + #print "source files:", source_files + #print "cpp args:", cpp_args + #print "lib args:", library_args + + #extensions[module] = { 'sources': source_files, + # 'cpp_args': cpp_args, + # 'lib_args': library_args } + + return extensions + +# read_setup_file () -- cgit v1.2.1 From 0e6eb0bf665aed42a3e42d4319a636b3ca6fa38c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 17 Sep 2000 00:53:02 +0000 Subject: Added 'expand_makefile_vars()' to (duh) expand make-style variables in a string (gives you something to do with the dictionary returned by 'parse_makefile()'). Pulled the regexes in 'parse_makefile()' out -- they're now globals, as 'expand_makefile_vars()' needs (two of) them. Cosmetic tweaks to 'parse_makefile()'. --- sysconfig.py | 48 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index de35d96a..605e95df 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -157,6 +157,13 @@ def parse_config_h(fp, g=None): g[m.group(1)] = 0 return g + +# Regexes needed for parsing Makefile (and similar syntaxes, +# like old-style Setup files). +_variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") +_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") +_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + def parse_makefile(fn, g=None): """Parse a Makefile-style file. @@ -166,19 +173,18 @@ def parse_makefile(fn, g=None): """ from distutils.text_file import TextFile - fp = TextFile(fn, strip_comments=1, join_lines=1) + fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1) if g is None: g = {} - variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") done = {} notdone = {} while 1: line = fp.readline() - if line is None: + if line is None: # eof break - m = variable_rx.match(line) + m = _variable_rx.match(line) if m: n, v = m.group(1, 2) v = string.strip(v) @@ -190,14 +196,10 @@ def parse_makefile(fn, g=None): done[n] = v # do variable interpolation here - findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") - findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") while notdone: for name in notdone.keys(): value = notdone[name] - m = findvar1_rx.search(value) - if not m: - m = findvar2_rx.search(value) + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) if m: n = m.group(1) if done.has_key(n): @@ -228,11 +230,39 @@ def parse_makefile(fn, g=None): # bogus variable reference; just drop it since we can't deal del notdone[name] + fp.close() + # save the results in the global dictionary g.update(done) return g +def expand_makefile_vars(s, vars): + """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in + 'string' according to 'vars' (a dictionary mapping variable names to + values). Variables not present in 'vars' are silently expanded to the + empty string. The variable values in 'vars' should not contain further + variable expansions; if 'vars' is the output of 'parse_makefile()', + you're fine. Returns a variable-expanded version of 's'. + """ + + # This algorithm does multiple expansion, so if vars['foo'] contains + # "${bar}", it will expand ${foo} to ${bar}, and then expand + # ${bar}... and so forth. This is fine as long as 'vars' comes from + # 'parse_makefile()', which takes care of such expansions eagerly, + # according to make's variable expansion semantics. + + while 1: + m = _findvar1_rx.search(s) or _findvar2_rx.search(s) + if m: + name = m.group(1) + (beg, end) = m.span() + s = s[0:beg] + vars.get(m.group(1)) + s[end:] + else: + break + return s + + _config_vars = None def _init_posix(): -- cgit v1.2.1 From 7ce5bceb8f36d5b1b416bde08b9f161ca4bf51d4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 17 Sep 2000 00:54:58 +0000 Subject: Fixed to respect 'define_macros' and 'undef_macros' on Extension object. --- command/build_ext.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index f880a7a8..d578b846 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -403,6 +403,10 @@ class build_ext (Command): # command line args. Hence we combine them in order: extra_args = ext.extra_compile_args or [] + macros = ext.define_macros[:] + for undef in ext.undef_macros: + macros.append((undef,)) + # XXX and if we support CFLAGS, why not CC (compiler # executable), CPPFLAGS (pre-processor options), and LDFLAGS # (linker options) too? @@ -413,7 +417,7 @@ class build_ext (Command): objects = self.compiler.compile (sources, output_dir=self.build_temp, - #macros=macros, + macros=macros, include_dirs=ext.include_dirs, debug=self.debug, extra_postargs=extra_args) -- cgit v1.2.1 From 7d833392261181af33c821afed6dff05ed17b3b0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 18 Sep 2000 00:41:10 +0000 Subject: Catch up to recent changes in TextFile (spotted by Bastian Kleineidam). --- command/sdist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index 6de703ec..9b9f6064 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -332,7 +332,7 @@ class sdist (Command): join_lines=1, lstrip_ws=1, rstrip_ws=1, - collapse_ws=1) + collapse_join=1) while 1: line = template.readline() -- cgit v1.2.1 From 3d12a4154867139b39890b8debcf250286f6b5e7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 19 Sep 2000 11:10:23 +0000 Subject: Set the 'nt' installation scheme for the install command even if run on other systems, so that data, headers, scripts are included in the installer. --- command/bdist_wininst.py | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 5030ef90..4637d451 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -74,6 +74,17 @@ class bdist_wininst (Command): install = self.reinitialize_command('install') install.root = self.bdist_dir + if os.name != 'nt': + # must force install to use the 'nt' scheme + install.select_scheme ('nt') + # change the backslash to the current pathname separator + for key in ('purelib', 'platlib', 'headers', 'scripts', + 'data'): + attrname = 'install_' + key + attr = getattr (install, attrname) + if attr: + attr = string.replace (attr, '\\', os.sep) + setattr (install, attrname, attr) install_lib = self.reinitialize_command('install_lib') # we do not want to include pyc or pyo files @@ -99,14 +110,20 @@ class bdist_wininst (Command): archive_basename = os.path.join(self.bdist_dir, "%s.win32" % fullname) - # XXX hack! Our archive MUST be relative to sys.prefix - # XXX What about .install_data, .install_scripts, ...? - # [Perhaps require that all installation dirs be under sys.prefix - # on Windows? this will be acceptable until we start dealing - # with Python applications, at which point we should zip up - # the application directory -- and again everything can be - # under one dir --GPW] - root_dir = install.install_lib + # Our archive MUST be relative to sys.prefix, which is the + # same as install_lib in the 'nt' scheme. + root_dir = os.path.normpath (install.install_lib) + + # Sanity check: Make sure everything is included + for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): + attrname = 'install_' + key + install_x = getattr (install, attrname) + # (Use normpath so that we can string.find to look for + # subdirectories) + install_x = os.path.normpath (install_x) + if string.find (install_x, root_dir) != 0: + raise DistutilsInternalError \ + ("'%s' not included in install_lib" % key) arcname = self.make_archive (archive_basename, "zip", root_dir=root_dir) self.create_exe (arcname, fullname) -- cgit v1.2.1 From 5054d1229b94a8fe66d72b69de38e62955997685 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 19 Sep 2000 23:56:43 +0000 Subject: *Very* belated application of Thomas Heller's patch to handle resource files. The gist of the patch is to treat ".rc" and ".mc" files as source files; ".mc" files are compiled to ".rc" and then ".res", and ".rc" files are compiled to ".res". Wish I knew what all these things stood for... --- msvccompiler.py | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index ae08e7fd..ea58a79c 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -178,10 +178,14 @@ class MSVCCompiler (CCompiler) : # Private class data (need to distinguish C from C++ source for compiler) _c_extensions = ['.c'] _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] # Needed for the filename generation methods provided by the # base class, CCompiler. - src_extensions = _c_extensions + _cpp_extensions + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' obj_extension = '.obj' static_lib_extension = '.lib' shared_lib_extension = '.dll' @@ -203,6 +207,8 @@ class MSVCCompiler (CCompiler) : self.cc = find_exe("cl.exe", version) self.link = find_exe("link.exe", version) self.lib = find_exe("lib.exe", version) + self.rc = find_exe("rc.exe", version) # resource compiler + self.mc = find_exe("mc.exe", version) # message compiler set_path_env_var ('lib', version) set_path_env_var ('include', version) path=get_msvc_paths('path', version) @@ -217,6 +223,8 @@ class MSVCCompiler (CCompiler) : self.cc = "cl.exe" self.link = "link.exe" self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" self.preprocess_options = None self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ] @@ -232,6 +240,37 @@ class MSVCCompiler (CCompiler) : # -- Worker methods ------------------------------------------------ + def object_filenames (self, + source_filenames, + strip_dir=0, + output_dir=''): + # Copied from ccompiler.py, extended to return .res as 'object'-file + # for .rc input file + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext (src_name) + if ext not in self.src_extensions: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError ("Don't know how to compile %s" % src_name) + if strip_dir: + base = os.path.basename (base) + if ext in self._rc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + elif ext in self._mc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + # object_filenames () + + def compile (self, sources, output_dir=None, @@ -263,14 +302,58 @@ class MSVCCompiler (CCompiler) : if skip_sources[src]: self.announce ("skipping %s (%s up-to-date)" % (src, obj)) else: + self.mkpath (os.path.dirname (obj)) + if ext in self._c_extensions: input_opt = "/Tc" + src elif ext in self._cpp_extensions: input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn ([self.rc] + + [output_opt] + [input_opt]) + except DistutilsExecError, msg: + raise CompileError, msg + continue + elif ext in self._mc_extensions: + + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + + h_dir = os.path.dirname (src) + rc_dir = os.path.dirname (obj) + try: + # first compile .MC to .RC and .H file + self.spawn ([self.mc] + + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext (os.path.basename (src)) + rc_file = os.path.join (rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn ([self.rc] + + ["/fo" + obj] + [rc_file]) + + except DistutilsExecError, msg: + raise CompileError, msg + continue + else: + # how to handle this file? + raise CompileError ( + "Don't know how to compile %s to %s" % \ + (src, obj)) output_opt = "/Fo" + obj - - self.mkpath (os.path.dirname (obj)) try: self.spawn ([self.cc] + compile_opts + pp_opts + [input_opt, output_opt] + -- cgit v1.2.1 From ec1f37f7faad9e09826aa1b6003f006676f0a9ef Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 21 Sep 2000 01:23:35 +0000 Subject: Corran Webster: fix 'change_root()' to handle Mac OS paths. --- util.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/util.py b/util.py index 46888712..b60e39c7 100644 --- a/util.py +++ b/util.py @@ -100,7 +100,13 @@ def change_root (new_root, pathname): return os.path.join (new_root, path) elif os.name == 'mac': - raise RuntimeError, "no clue how to do this on Mac OS" + if not os.path.isabs(pathname): + return os.path.join(new_root, pathname) + else: + # Chop off volume name from start of path + elements = string.split(pathname, ":", 1) + pathname = ":" + elements[1] + return os.path.join(new_root, pathname) else: raise DistutilsPlatformError, \ -- cgit v1.2.1 From d56ce29dfa83433ccaa14e7fc1cd3d732cc08955 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 22 Sep 2000 01:05:43 +0000 Subject: Fix 'convert_path()' so it returns immediately under Unix -- prevents blowing up when the pathname starts with '/', which is needed when converting installation directories in the "install" command. --- util.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util.py b/util.py index b60e39c7..3f068078 100644 --- a/util.py +++ b/util.py @@ -68,15 +68,15 @@ def convert_path (pathname): absolute (starts with '/') or contains local directory separators (unless the local separator is '/', of course).""" + if os.sep == '/': + return pathname if pathname[0] == '/': raise ValueError, "path '%s' cannot be absolute" % pathname if pathname[-1] == '/': raise ValueError, "path '%s' cannot end with '/'" % pathname - if os.sep != '/': - paths = string.split (pathname, '/') - return apply (os.path.join, paths) - else: - return pathname + + paths = string.split(pathname, '/') + return apply(os.path.join, paths) # convert_path () -- cgit v1.2.1 From ade6e34965cd938919f4177788b89e96c6f16d99 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 22 Sep 2000 01:31:08 +0000 Subject: Changed all paths in the INSTALL_SCHEMES dict to Unix syntax, and added 'convert_paths()' method to convert them all to the local syntax (backslash or colon or whatever) at the appropriate time. Added SCHEME_KEYS to get rid of one hard-coded list of attributes (in 'select_scheme()'). Default 'install_path_file' to true, and never set it false (it's just there in case some outsider somewhere wants to disable installation of the .pth file for whatever reason). Toned down the warning emitted when 'install_path_file' is false, since we no longer know why it might be false. Added 'warn_dir' flag to suppress warning when installing to a directory not in sys.path (again, we never set this false -- it's there for outsiders to use, specifically the "bdist_*" commands). Pulled the loop of 'change_root()' calls out to new method 'change_roots()'. Comment updates/deletions/additions. --- command/install.py | 99 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 43 deletions(-) diff --git a/command/install.py b/command/install.py index 04b325a6..573e0740 100644 --- a/command/install.py +++ b/command/install.py @@ -33,19 +33,24 @@ INSTALL_SCHEMES = { 'nt': { 'purelib': '$base', 'platlib': '$base', - 'headers': '$base\\Include\\$dist_name', - 'scripts': '$base\\Scripts', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', 'data' : '$base', }, 'mac': { - 'purelib': '$base:Lib:site-packages', - 'platlib': '$base:Lib:site-packages', - 'headers': '$base:Include:$dist_name', - 'scripts': '$base:Scripts', + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', 'data' : '$base', } } +# The keys to an installation scheme; if any new types of files are to be +# installed, be sure to add an entry to every installation scheme above, +# and to SCHEME_KEYS here. +SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') + class install (Command): @@ -130,14 +135,24 @@ class install (Command): # These two are for putting non-packagized distributions into their # own directory and creating a .pth file if it makes sense. - # 'extra_path' comes from the setup file; 'install_path_file' is - # set only if we determine that it makes sense to install a path - # file. + # 'extra_path' comes from the setup file; 'install_path_file' can + # be turned off if it makes no sense to install a .pth file. (But + # better to install it uselessly than to guess wrong and not + # install it when it's necessary and would be used!) Currently, + # 'install_path_file' is always true unless some outsider meddles + # with it. self.extra_path = None - self.install_path_file = 0 - + self.install_path_file = 1 + + # 'force' forces installation, even if target files are not + # out-of-date. 'skip_build' skips running the "build" command, + # handy if you know it's not necessary. 'warn_dir' (which is *not* + # a user option, it's just there so the bdist_* commands can turn + # it off) determines whether we warn about installing to a + # directory not in sys.path. self.force = 0 self.skip_build = 0 + self.warn_dir = 1 # These are only here as a conduit from the 'build' command to the # 'install_*' commands that do the real work. ('build_base' isn't @@ -256,6 +271,12 @@ class install (Command): else: self.install_lib = self.install_purelib + + # Convert directories from Unix /-separated syntax to the local + # convention. + self.convert_paths('lib', 'purelib', 'platlib', + 'scripts', 'data', 'headers') + # Well, we're not actually fully completely finalized yet: we still # have to deal with 'extra_path', which is the hack for allowing # non-packagized module distributions (hello, Numerical Python!) to @@ -267,11 +288,8 @@ class install (Command): # If a new root directory was supplied, make all the installation # dirs relative to it. if self.root is not None: - for name in ('libbase', 'lib', 'purelib', 'platlib', - 'scripts', 'data', 'headers'): - attr = "install_" + name - new_val = change_root (self.root, getattr (self, attr)) - setattr (self, attr, new_val) + self.change_roots('libbase', 'lib', 'purelib', 'platlib', + 'scripts', 'data', 'headers') self.dump_dirs ("after prepending root") @@ -324,22 +342,11 @@ class install (Command): self.prefix = os.path.normpath (sys.prefix) self.exec_prefix = os.path.normpath (sys.exec_prefix) - self.install_path_file = 1 else: if self.exec_prefix is None: self.exec_prefix = self.prefix - - # XXX since we don't *know* that a user-supplied prefix really - # points to another Python installation, we can't be sure that - # writing a .pth file there will actually work -- so we don't - # try. That is, we only set 'install_path_file' if the user - # didn't supply prefix. There are certainly circumstances - # under which we *should* install a .pth file when the user - # supplies a prefix, namely when that prefix actually points to - # another Python installation. Hmmm. - self.install_base = self.prefix self.install_platbase = self.exec_prefix self.select_scheme ("unix_prefix") @@ -351,10 +358,6 @@ class install (Command): if self.prefix is None: self.prefix = os.path.normpath (sys.prefix) - self.install_path_file = 1 - - # XXX same caveat regarding 'install_path_file' as in - # 'finalize_unix()'. self.install_base = self.install_platbase = self.prefix try: @@ -369,7 +372,7 @@ class install (Command): def select_scheme (self, name): # it's the caller's problem if they supply a bad name! scheme = INSTALL_SCHEMES[name] - for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): + for key in SCHEME_KEYS: attrname = 'install_' + key if getattr(self, attrname) is None: setattr(self, attrname, scheme[key]) @@ -399,6 +402,12 @@ class install (Command): 'install_data',]) + def convert_paths (self, *names): + for name in names: + attr = "install_" + name + setattr(self, attr, convert_path(getattr(self, attr))) + + def handle_extra_path (self): if self.extra_path is None: @@ -433,6 +442,12 @@ class install (Command): # handle_extra_path () + def change_roots (self, *names): + for name in names: + attr = "install_" + name + setattr(self, attr, change_root(self.root, getattr(self, attr))) + + def run (self): # Obviously have to build before we can install @@ -458,13 +473,14 @@ class install (Command): "writing list of installed files to '%s'" % self.record) - normalized_path = map (os.path.normpath, sys.path) - if (not (self.path_file and self.install_path_file) and - os.path.normpath (self.install_lib) not in normalized_path): - self.warn (("modules installed to '%s', which is not in " + - "Python's module search path (sys.path) -- " + - "you'll have to change the search path yourself") % - self.install_lib) + normalized_path = map(os.path.normpath, sys.path) + if (self.warn_dir and + not (self.path_file and self.install_path_file) and + os.path.normpath(self.install_lib) not in normalized_path): + self.warn(("modules installed to '%s', which is not in " + + "Python's module search path (sys.path) -- " + + "you'll have to change the search path yourself") % + self.install_lib) # run () @@ -516,10 +532,7 @@ class install (Command): (filename, [self.extra_dirs]), "creating %s" % filename) else: - self.warn (("path file '%s' not created for alternate or custom " + - "installation (path files only work with standard " + - "installations)") % - filename) + self.warn("path file '%s' not created" % filename) # 'sub_commands': a list of commands this command might have to run to # get its work done. See cmd.py for more info. -- cgit v1.2.1 From 7d6f876b662e94221ed957643e07cacff9352e7b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 22 Sep 2000 01:32:34 +0000 Subject: Tweak what happens when run on non-Windows platforms: set install prefix as well as scheme, and don't convert all installation paths (that's now done by the "install" command for us). --- command/bdist_wininst.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 4637d451..b4c6d9b4 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -75,16 +75,11 @@ class bdist_wininst (Command): install = self.reinitialize_command('install') install.root = self.bdist_dir if os.name != 'nt': - # must force install to use the 'nt' scheme - install.select_scheme ('nt') - # change the backslash to the current pathname separator - for key in ('purelib', 'platlib', 'headers', 'scripts', - 'data'): - attrname = 'install_' + key - attr = getattr (install, attrname) - if attr: - attr = string.replace (attr, '\\', os.sep) - setattr (install, attrname, attr) + # Must force install to use the 'nt' scheme; we set the + # prefix too just because it looks silly to put stuff + # in (eg.) ".../usr/Scripts", "usr/Include", etc. + install.prefix = "Python" + install.select_scheme('nt') install_lib = self.reinitialize_command('install_lib') # we do not want to include pyc or pyo files -- cgit v1.2.1 From 3790253376ae5bac233c686320b37e6789fe51f5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Sep 2000 00:59:34 +0000 Subject: Reformat docstrings. Standardize use of whitespace on function calls. --- file_util.py | 128 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/file_util.py b/file_util.py index 2d0148f3..0e85a74b 100644 --- a/file_util.py +++ b/file_util.py @@ -18,11 +18,11 @@ _copy_action = { None: 'copying', def _copy_file_contents (src, dst, buffer_size=16*1024): """Copy the file 'src' to 'dst'; both must be filenames. Any error - opening either file, reading from 'src', or writing to 'dst', - raises DistutilsFileError. Data is read/written in chunks of - 'buffer_size' bytes (default 16k). No attempt is made to handle - anything apart from regular files.""" - + opening either file, reading from 'src', or writing to 'dst', raises + DistutilsFileError. Data is read/written in chunks of 'buffer_size' + bytes (default 16k). No attempt is made to handle anything apart from + regular files. + """ # Stolen from shutil module in the standard library, but with # custom error-handling added. @@ -43,7 +43,7 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): while 1: try: - buf = fsrc.read (buffer_size) + buf = fsrc.read(buffer_size) except os.error, (errno, errstr): raise DistutilsFileError, \ "could not read from '%s': %s" % (src, errstr) @@ -74,31 +74,29 @@ def copy_file (src, dst, verbose=0, dry_run=0): - """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' - is copied there with the same name; otherwise, it must be a - filename. (If the file exists, it will be ruthlessly clobbered.) - If 'preserve_mode' is true (the default), the file's mode (type - and permission bits, or whatever is analogous on the current - platform) is copied. If 'preserve_times' is true (the default), - the last-modified and last-access times are copied as well. If - 'update' is true, 'src' will only be copied if 'dst' does not - exist, or if 'dst' does exist but is older than 'src'. If - 'verbose' is true, then a one-line summary of the copy will be - printed to stdout. - - 'link' allows you to make hard links (os.link) or symbolic links - (os.symlink) instead of copying: set it to "hard" or "sym"; if it - is None (the default), files are copied. Don't set 'link' on - systems that don't support it: 'copy_file()' doesn't check if - hard or symbolic linking is available. - - Under Mac OS, uses the native file copy function in macostools; - on other systems, uses '_copy_file_contents()' to copy file - contents. - - Return the name of the destination file, whether it was actually - copied or not.""" - + """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is + copied there with the same name; otherwise, it must be a filename. (If + the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' + is true (the default), the file's mode (type and permission bits, or + whatever is analogous on the current platform) is copied. If + 'preserve_times' is true (the default), the last-modified and + last-access times are copied as well. If 'update' is true, 'src' will + only be copied if 'dst' does not exist, or if 'dst' does exist but is + older than 'src'. If 'verbose' is true, then a one-line summary of the + copy will be printed to stdout. + + 'link' allows you to make hard links (os.link) or symbolic links + (os.symlink) instead of copying: set it to "hard" or "sym"; if it is + None (the default), files are copied. Don't set 'link' on systems that + don't support it: 'copy_file()' doesn't check if hard or symbolic + linking is available. + + Under Mac OS, uses the native file copy function in macostools; on + other systems, uses '_copy_file_contents()' to copy file contents. + + Return the name of the destination file, whether it was actually copied + or not. + """ # XXX if the destination file already exists, we clobber it if # copying, but blow up if linking. Hmmm. And I don't know what # macostools.copyfile() does. Should definitely be consistent, and @@ -109,17 +107,17 @@ def copy_file (src, dst, from stat import * from distutils.dep_util import newer - if not os.path.isfile (src): + if not os.path.isfile(src): raise DistutilsFileError, \ "can't copy '%s': doesn't exist or not a regular file" % src - if os.path.isdir (dst): + if os.path.isdir(dst): dir = dst - dst = os.path.join (dst, os.path.basename (src)) + dst = os.path.join(dst, os.path.basename(src)) else: - dir = os.path.dirname (dst) + dir = os.path.dirname(dst) - if update and not newer (src, dst): + if update and not newer(src, dst): if verbose: print "not copying %s (output up-to-date)" % src return dst @@ -142,7 +140,7 @@ def copy_file (src, dst, if os.name == 'mac': import macostools try: - macostools.copy (src, dst, 0, preserve_times) + macostools.copy(src, dst, 0, preserve_times) except os.error, exc: raise DistutilsFileError, \ "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) @@ -150,25 +148,25 @@ def copy_file (src, dst, # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) elif link == 'hard': - if not (os.path.exists (dst) and os.path.samefile (src, dst)): - os.link (src, dst) + if not (os.path.exists(dst) and os.path.samefile(src, dst)): + os.link(src, dst) elif link == 'sym': - if not (os.path.exists (dst) and os.path.samefile (src, dst)): - os.symlink (src, dst) + if not (os.path.exists(dst) and os.path.samefile(src, dst)): + os.symlink(src, dst) # Otherwise (non-Mac, not linking), copy the file contents and # (optionally) copy the times and mode. else: - _copy_file_contents (src, dst) + _copy_file_contents(src, dst) if preserve_mode or preserve_times: - st = os.stat (src) + st = os.stat(src) # According to David Ascher , utime() should be done # before chmod() (at least under NT). if preserve_times: - os.utime (dst, (st[ST_ATIME], st[ST_MTIME])) + os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) if preserve_mode: - os.chmod (dst, S_IMODE (st[ST_MODE])) + os.chmod(dst, S_IMODE(st[ST_MODE])) return dst @@ -180,13 +178,13 @@ def move_file (src, dst, verbose=0, dry_run=0): - """Move a file 'src' to 'dst'. If 'dst' is a directory, the file - will be moved into it with the same name; otherwise, 'src' is - just renamed to 'dst'. Return the new full name of the file. - - Handles cross-device moves on Unix using - 'copy_file()'. What about other systems???""" + """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will + be moved into it with the same name; otherwise, 'src' is just renamed + to 'dst'. Return the new full name of the file. + Handles cross-device moves on Unix using 'copy_file()'. What about + other systems??? + """ from os.path import exists, isfile, isdir, basename, dirname if verbose: @@ -195,25 +193,25 @@ def move_file (src, dst, if dry_run: return dst - if not isfile (src): + if not isfile(src): raise DistutilsFileError, \ "can't move '%s': not a regular file" % src - if isdir (dst): - dst = os.path.join (dst, basename (src)) - elif exists (dst): + if isdir(dst): + dst = os.path.join(dst, basename(src)) + elif exists(dst): raise DistutilsFileError, \ "can't move '%s': destination '%s' already exists" % \ (src, dst) - if not isdir (dirname (dst)): + if not isdir(dirname(dst)): raise DistutilsFileError, \ "can't move '%s': destination '%s' not a valid path" % \ (src, dst) copy_it = 0 try: - os.rename (src, dst) + os.rename(src, dst) except os.error, (num, msg): if num == errno.EXDEV: copy_it = 1 @@ -222,12 +220,12 @@ def move_file (src, dst, "couldn't move '%s' to '%s': %s" % (src, dst, msg) if copy_it: - copy_file (src, dst) + copy_file(src, dst) try: - os.unlink (src) + os.unlink(src) except os.error, (num, msg): try: - os.unlink (dst) + os.unlink(dst) except os.error: pass raise DistutilsFileError, \ @@ -242,9 +240,9 @@ def move_file (src, dst, def write_file (filename, contents): """Create a file with the specified name and write 'contents' (a - sequence of strings without line terminators) to it.""" - - f = open (filename, "w") + sequence of strings without line terminators) to it. + """ + f = open(filename, "w") for line in contents: - f.write (line + "\n") - f.close () + f.write(line + "\n") + f.close() -- cgit v1.2.1 From 2d5928fb5d508496758ddafa463e5a55712f637c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Sep 2000 01:10:10 +0000 Subject: Whitespace tweaks. --- command/install_lib.py | 67 ++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index 2d19e5b9..dac644ee 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -34,30 +34,29 @@ class install_lib (Command): # Get all the information we need to install pure Python modules # from the umbrella 'install' command -- build (source) directory, # install (target) directory, and whether to compile .py files. - self.set_undefined_options ('install', - ('build_lib', 'build_dir'), - ('install_lib', 'install_dir'), - ('force', 'force'), - ('compile_py', 'compile'), - ('optimize_py', 'optimize'), - ('skip_build', 'skip_build'), - ) - + self.set_undefined_options('install', + ('build_lib', 'build_dir'), + ('install_lib', 'install_dir'), + ('force', 'force'), + ('compile_py', 'compile'), + ('optimize_py', 'optimize'), + ('skip_build', 'skip_build'), + ) def run (self): # Make sure we have built everything we need first if not self.skip_build: if self.distribution.has_pure_modules(): - self.run_command ('build_py') + self.run_command('build_py') if self.distribution.has_ext_modules(): - self.run_command ('build_ext') + self.run_command('build_ext') # Install everything: simply dump the entire contents of the build # directory to the installation directory (that's the beauty of # having a build directory!) if os.path.isdir(self.build_dir): - outfiles = self.copy_tree (self.build_dir, self.install_dir) + outfiles = self.copy_tree(self.build_dir, self.install_dir) else: self.warn("'%s' does not exist -- no Python modules to install" % self.build_dir) @@ -76,10 +75,10 @@ class install_lib (Command): if f[-3:] == '.py': out_fn = f + (__debug__ and "c" or "o") compile_msg = "byte-compiling %s to %s" % \ - (f, os.path.basename (out_fn)) + (f, os.path.basename(out_fn)) skip_msg = "skipping byte-compilation of %s" % f - self.make_file (f, out_fn, compile, (f,), - compile_msg, skip_msg) + self.make_file(f, out_fn, compile, (f,), + compile_msg, skip_msg) # run () @@ -88,14 +87,14 @@ class install_lib (Command): if not has_any: return [] - build_cmd = self.get_finalized_command (build_cmd) + build_cmd = self.get_finalized_command(build_cmd) build_files = build_cmd.get_outputs() - build_dir = getattr (build_cmd, cmd_option) + build_dir = getattr(build_cmd, cmd_option) - prefix_len = len (build_dir) + len (os.sep) + prefix_len = len(build_dir) + len(os.sep) outputs = [] for file in build_files: - outputs.append (os.path.join (output_dir, file[prefix_len:])) + outputs.append(os.path.join(output_dir, file[prefix_len:])) return outputs @@ -112,21 +111,21 @@ class install_lib (Command): def get_outputs (self): """Return the list of files that would be installed if this command were actually run. Not affected by the "dry-run" flag or whether - modules have actually been built yet.""" - + modules have actually been built yet. + """ pure_outputs = \ - self._mutate_outputs (self.distribution.has_pure_modules(), - 'build_py', 'build_lib', - self.install_dir) + self._mutate_outputs(self.distribution.has_pure_modules(), + 'build_py', 'build_lib', + self.install_dir) if self.compile: bytecode_outputs = self._bytecode_filenames(pure_outputs) else: bytecode_outputs = [] ext_outputs = \ - self._mutate_outputs (self.distribution.has_ext_modules(), - 'build_ext', 'build_lib', - self.install_dir) + self._mutate_outputs(self.distribution.has_ext_modules(), + 'build_ext', 'build_lib', + self.install_dir) return pure_outputs + bytecode_outputs + ext_outputs @@ -136,20 +135,18 @@ class install_lib (Command): """Get the list of files that are input to this command, ie. the files that get installed as they are named in the build tree. The files in this list correspond one-to-one to the output - filenames returned by 'get_outputs()'.""" - + filenames returned by 'get_outputs()'. + """ inputs = [] if self.distribution.has_pure_modules(): - build_py = self.get_finalized_command ('build_py') - inputs.extend (build_py.get_outputs()) + build_py = self.get_finalized_command('build_py') + inputs.extend(build_py.get_outputs()) if self.distribution.has_ext_modules(): - build_ext = self.get_finalized_command ('build_ext') - inputs.extend (build_ext.get_outputs()) + build_ext = self.get_finalized_command('build_ext') + inputs.extend(build_ext.get_outputs()) return inputs - - # class install_lib -- cgit v1.2.1 From 5b0913a16d97aa227bbe0bcc2aaf6d39bc502934 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Sep 2000 01:20:19 +0000 Subject: Split 'run()' up into 'build()', 'install()', and 'bytecompile()' (for easier extensibility). --- command/install_lib.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index dac644ee..b104fa9c 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -46,31 +46,46 @@ class install_lib (Command): def run (self): # Make sure we have built everything we need first + self.build() + + # Install everything: simply dump the entire contents of the build + # directory to the installation directory (that's the beauty of + # having a build directory!) + outfiles = self.install() + + # (Optionally) compile .py to .pyc + self.bytecompile(outfiles) + + # run () + + + # -- Top-level worker functions ------------------------------------ + # (called from 'run()') + + def build (self): if not self.skip_build: if self.distribution.has_pure_modules(): self.run_command('build_py') if self.distribution.has_ext_modules(): self.run_command('build_ext') - - # Install everything: simply dump the entire contents of the build - # directory to the installation directory (that's the beauty of - # having a build directory!) + + def install (self): if os.path.isdir(self.build_dir): outfiles = self.copy_tree(self.build_dir, self.install_dir) else: self.warn("'%s' does not exist -- no Python modules to install" % self.build_dir) return + return outfiles - # (Optionally) compile .py to .pyc + def bytecompile (self, files): # XXX hey! we can't control whether we optimize or not; that's up # to the invocation of the current Python interpreter (at least # according to the py_compile docs). That sucks. - if self.compile: from py_compile import compile - for f in outfiles: + for f in files: # only compile the file if it is actually a .py file if f[-3:] == '.py': out_fn = f + (__debug__ and "c" or "o") @@ -79,9 +94,10 @@ class install_lib (Command): skip_msg = "skipping byte-compilation of %s" % f self.make_file(f, out_fn, compile, (f,), compile_msg, skip_msg) - # run () + # -- Utility methods ----------------------------------------------- + def _mutate_outputs (self, has_any, build_cmd, cmd_option, output_dir): if not has_any: @@ -108,6 +124,10 @@ class install_lib (Command): return bytecode_files + + # -- External interface -------------------------------------------- + # (called by outsiders) + def get_outputs (self): """Return the list of files that would be installed if this command were actually run. Not affected by the "dry-run" flag or whether -- cgit v1.2.1 From 16f0821b07cee481a0ed13bf10ce67c589acd618 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:23:52 +0000 Subject: Fixed some bugs and mis-features in handling config files: * options can now be spelled "foo-bar" or "foo_bar" (handled in 'parse_config_files()', just after we parse a file) * added a "[global]" section so there's a place to set global options like verbose/quiet and dry-run * respect the "negative alias" dictionary so (eg.) "quiet=1" is the same as "verbose=0" (this had to be done twice: once in 'parse_config_file()' for global options, and once in '_set_command_options()' for per-command options) * the other half of handling boolean options correctly: allow commands to list their boolean options in a 'boolean_options' class attribute, and use it to translate strings (like "yes", "1", "no", "0", etc) to true or false --- dist.py | 53 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/dist.py b/dist.py index c5fe86eb..f821e0aa 100644 --- a/dist.py +++ b/dist.py @@ -15,7 +15,7 @@ from copy import copy from distutils.errors import * from distutils import sysconfig from distutils.fancy_getopt import FancyGetopt, longopt_xlate -from distutils.util import check_environ +from distutils.util import check_environ, strtobool # Regex to define acceptable Distutils command names. This is not *quite* @@ -321,7 +321,9 @@ class Distribution: for opt in options: if opt != '__name__': - opt_dict[opt] = (filename, parser.get(section,opt)) + val = parser.get(section,opt) + opt = string.replace(opt, '-', '_') + opt_dict[opt] = (filename, val) # Make the ConfigParser forget everything (so we retain # the original filenames that options come from) -- gag, @@ -329,6 +331,22 @@ class Distribution: # specific config parser (sigh...) parser.__init__() + # If there was a "global" section in the config file, use it + # to set Distribution options. + + if self.command_options.has_key('global'): + for (opt, (src, val)) in self.command_options['global'].items(): + alias = self.negative_opt.get(opt) + try: + if alias: + setattr(self, alias, not strtobool(val)) + elif opt in ('verbose', 'dry_run'): # ugh! + setattr(self, opt, strtobool(val)) + except ValueError, msg: + raise DistutilsOptionError, msg + + # parse_config_files () + # -- Command-line parsing methods ---------------------------------- @@ -346,7 +364,7 @@ class Distribution: attribute raises DistutilsGetoptError; any error on the command-line raises DistutilsArgError. If no Distutils commands were found on the command line, raises DistutilsArgError. Return - true if command-line were successfully parsed and we should carry + true if command-line was successfully parsed and we should carry on with executing commands; false if no errors but we shouldn't execute commands (currently, this only happens if user asks for help). @@ -714,7 +732,7 @@ class Distribution: this means copying elements of a dictionary ('option_dict') to attributes of an instance ('command'). - 'command_obj' must be a Commnd instance. If 'option_dict' is not + 'command_obj' must be a Command instance. If 'option_dict' is not supplied, uses the standard option dictionary for this command (from 'self.command_options'). """ @@ -727,11 +745,28 @@ class Distribution: if DEBUG: print " setting options for '%s' command:" % command_name for (option, (source, value)) in option_dict.items(): if DEBUG: print " %s = %s (from %s)" % (option, value, source) - if not hasattr(command_obj, option): - raise DistutilsOptionError, \ - ("error in %s: command '%s' has no such option '%s'") % \ - (source, command_name, option) - setattr(command_obj, option, value) + try: + bool_opts = command_obj.boolean_options + except AttributeError: + bool_opts = [] + try: + neg_opt = command_obj.negative_opt + except AttributeError: + neg_opt = {} + + try: + if neg_opt.has_key(option): + setattr(command_obj, neg_opt[option], not strtobool(value)) + elif option in bool_opts: + setattr(command_obj, option, strtobool(value)) + elif hasattr(command_obj, option): + setattr(command_obj, option, value) + else: + raise DistutilsOptionError, \ + ("error in %s: command '%s' has no such option '%s'" + % (source, command_name, option)) + except ValueError, msg: + raise DistutilsOptionError, msg def reinitialize_command (self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first -- cgit v1.2.1 From a9f1b90db48367bd932b4d19310d184a2e5cf317 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:25:06 +0000 Subject: Added 'strtobool()' function: convert strings like "yes", "1", "no", "0", etc. to true/false. --- util.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/util.py b/util.py index 3f068078..367985ac 100644 --- a/util.py +++ b/util.py @@ -273,3 +273,18 @@ def execute (func, args, msg=None, verbose=0, dry_run=0): apply(func, args) # execute() + + +def strtobool (val): + """Convert a string representation of truth to true (1) or false (0). + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + """ + val = string.lower(val) + if val in ('y', 'yes', 't', 'true', 'on', '1'): + return 1 + elif val in ('n', 'no', 'f', 'false', 'off', '0'): + return 0 + else: + raise ValueError, "invalid truth value %s" % `val` -- cgit v1.2.1 From 0881101deede0e48530b1245c88f396acc1cbf6f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:41:15 +0000 Subject: Added 'boolean_options' list to support config file parsing. --- command/bdist_dumb.py | 2 ++ command/bdist_rpm.py | 2 ++ command/bdist_wininst.py | 2 ++ command/build.py | 2 ++ command/build_clib.py | 2 ++ command/build_ext.py | 2 ++ command/build_py.py | 2 ++ command/build_scripts.py | 2 ++ command/clean.py | 2 ++ command/install.py | 2 ++ command/install_data.py | 2 ++ command/install_headers.py | 1 + command/install_lib.py | 2 ++ command/install_scripts.py | 3 +++ command/sdist.py | 3 +++ 15 files changed, 31 insertions(+) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 1fdbf425..520098db 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -32,6 +32,8 @@ class bdist_dumb (Command): "directory to put final built distributions in"), ] + boolean_options = ['keep-temp'] + default_format = { 'posix': 'gztar', 'nt': 'zip', } diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 2afc7146..d585e8c6 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -111,6 +111,8 @@ class bdist_rpm (Command): "RPM 2 compatibility mode"), ] + boolean_options = ['keep-temp', 'rpm2-mode'] + negative_opt = {'no-keep-temp': 'keep-temp', 'no-rpm-opt-flags': 'use-rpm-opt-flags', 'rpm2-mode': 'rpm3-mode'} diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index b4c6d9b4..16dd8022 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -33,6 +33,8 @@ class bdist_wininst (Command): "directory to put final built distributions in"), ] + boolean_options = ['keep-temp'] + def initialize_options (self): self.bdist_dir = None self.keep_temp = 0 diff --git a/command/build.py b/command/build.py index 15476ca1..f30f4ee1 100644 --- a/command/build.py +++ b/command/build.py @@ -42,6 +42,8 @@ class build (Command): "forcibly build everything (ignore file timestamps)"), ] + boolean_options = ['debug', 'force'] + help_options = [ ('help-compiler', None, "list available compilers", show_compilers), diff --git a/command/build_clib.py b/command/build_clib.py index 450dae17..775b7ade 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -48,6 +48,8 @@ class build_clib (Command): "specify the compiler type"), ] + boolean_options = ['debug', 'force'] + help_options = [ ('help-compiler', None, "list available compilers", show_compilers), diff --git a/command/build_ext.py b/command/build_ext.py index d578b846..7fdfd145 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -82,6 +82,8 @@ class build_ext (Command): "make SWIG create C++ files (default is C)"), ] + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp'] + help_options = [ ('help-compiler', None, "list available compilers", show_compilers), diff --git a/command/build_py.py b/command/build_py.py index 5fcd18e7..ea92c2be 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -23,6 +23,8 @@ class build_py (Command): ('force', 'f', "forcibly build everything (ignore file timestamps)"), ] + boolean_options = ['force'] + def initialize_options (self): self.build_lib = None diff --git a/command/build_scripts.py b/command/build_scripts.py index 17fae8f7..eacf7989 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -22,6 +22,8 @@ class build_scripts (Command): ('force', 'f', "forcibly build everything (ignore file timestamps"), ] + boolean_options = ['force'] + def initialize_options (self): self.build_dir = None diff --git a/command/clean.py b/command/clean.py index 2f3597fd..4f04f08b 100644 --- a/command/clean.py +++ b/command/clean.py @@ -28,6 +28,8 @@ class clean (Command): "remove all build output, not just temporary by-products") ] + boolean_options = ['all'] + def initialize_options(self): self.build_base = None self.build_lib = None diff --git a/command/install.py b/command/install.py index 573e0740..4ad652d9 100644 --- a/command/install.py +++ b/command/install.py @@ -106,6 +106,8 @@ class install (Command): "filename in which to record list of installed files"), ] + boolean_options = ['force', 'skip-build'] + def initialize_options (self): diff --git a/command/install_data.py b/command/install_data.py index af348f51..9ce11839 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -25,6 +25,8 @@ class install_data (Command): ('force', 'f', "force installation (overwrite existing files)"), ] + boolean_options = ['force'] + def initialize_options (self): self.install_dir = None self.outfiles = [] diff --git a/command/install_headers.py b/command/install_headers.py index 5c06d574..ec0cf441 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -21,6 +21,7 @@ class install_headers (Command): "force installation (overwrite existing files)"), ] + boolean_options = ['force'] def initialize_options (self): self.install_dir = None diff --git a/command/install_lib.py b/command/install_lib.py index b104fa9c..a603b4f5 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -19,6 +19,8 @@ class install_lib (Command): ('skip-build', None, "skip the build steps"), ] + boolean_options = ['force', 'compile', 'optimize', 'skip-build'] + def initialize_options (self): # let the 'install' command dictate our installation directory diff --git a/command/install_scripts.py b/command/install_scripts.py index d506f90f..b8938c48 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -22,6 +22,9 @@ class install_scripts (Command): ('skip-build', None, "skip the build steps"), ] + boolean_options = ['force', 'skip-build'] + + def initialize_options (self): self.install_dir = None self.force = 0 diff --git a/command/sdist.py b/command/sdist.py index 9b9f6064..ec443a30 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -67,6 +67,9 @@ class sdist (Command): "[default: dist]"), ] + boolean_options = ['use-defaults', 'prune', + 'manifest-only', 'force-manifest', + 'keep-tree'] help_options = [ ('help-formats', None, -- cgit v1.2.1 From 0dc98e6007d2b9a56fa302fd96b0c043e2e3da05 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:51:01 +0000 Subject: Renamed '--keep-tree' option to '--keep-temp', for consistency with the bdist_* commands. --- command/sdist.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index ec443a30..adaf4925 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -59,7 +59,7 @@ class sdist (Command): "forcibly regenerate the manifest and carry on as usual"), ('formats=', None, "formats for source distribution (comma-separated list)"), - ('keep-tree', 'k', + ('keep-temp', 'k', "keep the distribution tree around after creating " + "archive file(s)"), ('dist-dir=', 'd', @@ -69,7 +69,7 @@ class sdist (Command): boolean_options = ['use-defaults', 'prune', 'manifest-only', 'force-manifest', - 'keep-tree'] + 'keep-temp'] help_options = [ ('help-formats', None, @@ -97,7 +97,7 @@ class sdist (Command): self.force_manifest = 0 self.formats = None - self.keep_tree = 0 + self.keep_temp = 0 self.dist_dir = None self.archive_files = None @@ -357,7 +357,7 @@ class sdist (Command): by 'read_template()', but really don't belong there: * the build tree (typically "build") * the release tree itself (only an issue if we ran "sdist" - previously with --keep-tree, or it aborted) + previously with --keep-temp, or it aborted) * any RCS or CVS directories """ build = self.get_finalized_command('build') @@ -447,7 +447,7 @@ class sdist (Command): tree with 'make_release_tree()'; then, we create all required archive files (according to 'self.formats') from the release tree. Finally, we clean up by blowing away the release tree (unless - 'self.keep_tree' is true). The list of archive files created is + 'self.keep_temp' is true). The list of archive files created is stored so it can be retrieved later by 'get_archive_files()'. """ # Don't warn about missing meta-data here -- should be (and is!) @@ -463,7 +463,7 @@ class sdist (Command): self.archive_files = archive_files - if not self.keep_tree: + if not self.keep_temp: dir_util.remove_tree (base_dir, self.verbose, self.dry_run) def get_archive_files (self): -- cgit v1.2.1 From 680d30f3685bcd7d4230598198edcf5bb634d692 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:53:01 +0000 Subject: Added a bunch of missing "=" signs in the option table. Removed script options -- don't think they ever worked, weren't very well thought through, etc. --- command/bdist_rpm.py | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index d585e8c6..c293f1f5 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -48,52 +48,36 @@ class bdist_rpm (Command): # to "bdist_rpm". The idea is that packagers would put this # info in setup.cfg, although they are of course free to # supply it on the command line. - ('distribution-name', None, + ('distribution-name=', None, "name of the (Linux) distribution to which this " "RPM applies (*not* the name of the module distribution!)"), - ('group', None, + ('group=', None, "package classification [default: \"Development/Libraries\"]"), - ('release', None, + ('release=', None, "RPM release number"), - ('serial', None, + ('serial=', None, "RPM serial number"), - ('vendor', None, + ('vendor=', None, "RPM \"vendor\" (eg. \"Joe Blow \") " "[default: maintainer or author from setup script]"), - ('packager', None, + ('packager=', None, "RPM packager (eg. \"Jane Doe \")" "[default: vendor]"), - ('doc-files', None, + ('doc-files=', None, "list of documentation files (space or comma-separated)"), - ('changelog', None, + ('changelog=', None, "path to RPM changelog"), - ('icon', None, + ('icon=', None, "name of icon file"), - ('prep-script', None, - "pre-build script (Bourne shell code)"), - ('build-script', None, - "build script (Bourne shell code)"), - ('install-script', None, - "installation script (Bourne shell code)"), - ('clean-script', None, - "clean script (Bourne shell code)"), - ('pre-install', None, - "pre-install script (Bourne shell code)"), - ('post-install', None, - "post-install script (Bourne shell code)"), - ('pre-uninstall', None, - "pre-uninstall script (Bourne shell code)"), - ('post-uninstall', None, - "post-uninstall script (Bourne shell code)"), - ('provides', None, + ('provides=', None, "capabilities provided by this package"), - ('requires', None, + ('requires=', None, "capabilities required by this package"), - ('conflicts', None, + ('conflicts=', None, "capabilities which conflict with this package"), - ('build-requires', None, + ('build-requires=', None, "capabilities required to build this package"), - ('obsoletes', None, + ('obsoletes=', None, "capabilities made obsolete by this package"), # Actions to take when building RPM -- cgit v1.2.1 From e0a10947148eeed6a56ef67c5ea170315fbe3c48 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:58:07 +0000 Subject: Change to use the new 'translate_longopt()' function from fancy_getopt, rather than rolling our own with fancy_getopt's 'longopt_xlate' global. --- dist.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dist.py b/dist.py index f821e0aa..0069b5fb 100644 --- a/dist.py +++ b/dist.py @@ -14,7 +14,7 @@ from types import * from copy import copy from distutils.errors import * from distutils import sysconfig -from distutils.fancy_getopt import FancyGetopt, longopt_xlate +from distutils.fancy_getopt import FancyGetopt, translate_longopt from distutils.util import check_environ, strtobool @@ -86,8 +86,8 @@ class Distribution: ('long-description', None, "print the long package description"), ] - display_option_names = map(lambda x: string.translate(x[0], longopt_xlate), - display_options) + display_option_names = map(lambda x: translate_longopt(x[0]), + display_options) # negative options are options that exclude other options negative_opt = {'quiet': 'verbose'} @@ -592,7 +592,7 @@ class Distribution: for (opt, val) in option_order: if val and is_display_option.get(opt): - opt = string.translate (opt, longopt_xlate) + opt = translate_longopt(opt) print getattr(self.metadata, "get_"+opt)() any_display_options = 1 @@ -746,7 +746,7 @@ class Distribution: for (option, (source, value)) in option_dict.items(): if DEBUG: print " %s = %s (from %s)" % (option, value, source) try: - bool_opts = command_obj.boolean_options + bool_opts = map(translate_longopt, command_obj.boolean_options) except AttributeError: bool_opts = [] try: -- cgit v1.2.1 From d5e007365aa1d4b127c7eee4826590ca86aeb3b6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:58:31 +0000 Subject: Added 'translate_longopt()' function. --- fancy_getopt.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fancy_getopt.py b/fancy_getopt.py index eaf60737..f9352001 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -470,6 +470,13 @@ def wrap_text (text, width): return lines # wrap_text () + + +def translate_longopt (opt): + """Convert a long option name to a valid Python identifier by + changing "-" to "_". + """ + return string.translate(opt, longopt_xlate) class OptionDummy: -- cgit v1.2.1 From e67685fc403dc1e6cc25b0381aac29230aca571d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 01:52:25 +0000 Subject: Standardize whitespace in function calls and docstrings. --- dist.py | 124 ++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/dist.py b/dist.py index 0069b5fb..91b820e9 100644 --- a/dist.py +++ b/dist.py @@ -118,7 +118,7 @@ class Distribution: # information here (and enough command-line options) that it's # worth it. Also delegate 'get_XXX()' methods to the 'metadata' # object in a sneaky and underhanded (but efficient!) way. - self.metadata = DistributionMetadata () + self.metadata = DistributionMetadata() method_basenames = dir(self.metadata) + \ ['fullname', 'contact', 'contact_email'] for basename in method_basenames: @@ -187,7 +187,7 @@ class Distribution: # specifically. Note that this order guarantees that aliased # command options will override any supplied redundantly # through the general options dictionary. - options = attrs.get ('options') + options = attrs.get('options') if options: del attrs['options'] for (command, cmd_options) in options.items(): @@ -198,10 +198,10 @@ class Distribution: # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! for (key,val) in attrs.items(): - if hasattr (self.metadata, key): - setattr (self.metadata, key, val) - elif hasattr (self, key): - setattr (self, key, val) + if hasattr(self.metadata, key): + setattr(self.metadata, key, val) + elif hasattr(self, key): + setattr(self, key, val) else: raise DistutilsSetupError, \ "invalid distribution option '%s'" % key @@ -377,10 +377,10 @@ class Distribution: # until we know what the command is. self.commands = [] - parser = FancyGetopt (self.global_options + self.display_options) - parser.set_negative_aliases (self.negative_opt) - parser.set_aliases ({'license': 'licence'}) - args = parser.getopt (args=self.script_args, object=self) + parser = FancyGetopt(self.global_options + self.display_options) + parser.set_negative_aliases(self.negative_opt) + parser.set_aliases({'license': 'licence'}) + args = parser.getopt(args=self.script_args, object=self) option_order = parser.get_option_order() # for display options we return immediately @@ -427,28 +427,28 @@ class Distribution: # Pull the current command from the head of the command line command = args[0] - if not command_re.match (command): + if not command_re.match(command): raise SystemExit, "invalid command name '%s'" % command - self.commands.append (command) + self.commands.append(command) # Dig up the command class that implements this command, so we # 1) know that it's a valid command, and 2) know which options # it takes. try: - cmd_class = self.get_command_class (command) + cmd_class = self.get_command_class(command) except DistutilsModuleError, msg: raise DistutilsArgError, msg # Require that the command class be derived from Command -- want # to be sure that the basic "command" interface is implemented. - if not issubclass (cmd_class, Command): + if not issubclass(cmd_class, Command): raise DistutilsClassError, \ "command class %s must subclass Command" % cmd_class # Also make sure that the command object provides a list of its # known options. - if not (hasattr (cmd_class, 'user_options') and - type (cmd_class.user_options) is ListType): + if not (hasattr(cmd_class, 'user_options') and + type(cmd_class.user_options) is ListType): raise DistutilsClassError, \ ("command class %s must provide " + "'user_options' attribute (a list of tuples)") % \ @@ -457,14 +457,14 @@ class Distribution: # If the command class has a list of negative alias options, # merge it in with the global negative aliases. negative_opt = self.negative_opt - if hasattr (cmd_class, 'negative_opt'): - negative_opt = copy (negative_opt) - negative_opt.update (cmd_class.negative_opt) + if hasattr(cmd_class, 'negative_opt'): + negative_opt = copy(negative_opt) + negative_opt.update(cmd_class.negative_opt) # Check for help_options in command class. They have a different # format (tuple of four) so we need to preprocess them here. if (hasattr(cmd_class, 'help_options') and - type (cmd_class.help_options) is ListType): + type(cmd_class.help_options) is ListType): help_options = fix_help_options(cmd_class.help_options) else: help_options = [] @@ -472,17 +472,17 @@ class Distribution: # All commands support the global options too, just by adding # in 'global_options'. - parser.set_option_table (self.global_options + - cmd_class.user_options + - help_options) - parser.set_negative_aliases (negative_opt) - (args, opts) = parser.getopt (args[1:]) + parser.set_option_table(self.global_options + + cmd_class.user_options + + help_options) + parser.set_negative_aliases(negative_opt) + (args, opts) = parser.getopt(args[1:]) if hasattr(opts, 'help') and opts.help: self._show_help(parser, display_options=0, commands=[cmd_class]) return if (hasattr(cmd_class, 'help_options') and - type (cmd_class.help_options) is ListType): + type(cmd_class.help_options) is ListType): help_option_found=0 for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): @@ -534,13 +534,13 @@ class Distribution: from distutils.cmd import Command if global_options: - parser.set_option_table (self.global_options) - parser.print_help ("Global options:") + parser.set_option_table(self.global_options) + parser.print_help("Global options:") print if display_options: - parser.set_option_table (self.display_options) - parser.print_help ( + parser.set_option_table(self.display_options) + parser.print_help( "Information display options (just display " + "information, ignore any commands)") print @@ -549,14 +549,14 @@ class Distribution: if type(command) is ClassType and issubclass(klass, Command): klass = command else: - klass = self.get_command_class (command) + klass = self.get_command_class(command) if (hasattr(klass, 'help_options') and - type (klass.help_options) is ListType): - parser.set_option_table (klass.user_options + - fix_help_options(klass.help_options)) + type(klass.help_options) is ListType): + parser.set_option_table(klass.user_options + + fix_help_options(klass.help_options)) else: - parser.set_option_table (klass.user_options) - parser.print_help ("Options for '%s' command:" % klass.__name__) + parser.set_option_table(klass.user_options) + parser.print_help("Options for '%s' command:" % klass.__name__) print print gen_usage(self.script_name) @@ -577,7 +577,7 @@ class Distribution: # processing now (ie. if they ran "setup --help-commands foo bar", # we ignore "foo bar"). if self.help_commands: - self.print_commands () + self.print_commands() print print gen_usage(self.script_name) return 1 @@ -608,9 +608,9 @@ class Distribution: print header + ":" for cmd in commands: - klass = self.cmdclass.get (cmd) + klass = self.cmdclass.get(cmd) if not klass: - klass = self.get_command_class (cmd) + klass = self.get_command_class(cmd) try: description = klass.description except AttributeError: @@ -639,21 +639,21 @@ class Distribution: extra_commands = [] for cmd in self.cmdclass.keys(): if not is_std.get(cmd): - extra_commands.append (cmd) + extra_commands.append(cmd) max_length = 0 for cmd in (std_commands + extra_commands): - if len (cmd) > max_length: - max_length = len (cmd) + if len(cmd) > max_length: + max_length = len(cmd) - self.print_command_list (std_commands, - "Standard commands", - max_length) + self.print_command_list(std_commands, + "Standard commands", + max_length) if extra_commands: print - self.print_command_list (extra_commands, - "Extra commands", - max_length) + self.print_command_list(extra_commands, + "Extra commands", + max_length) # print_commands () @@ -822,10 +822,10 @@ class Distribution: def run_commands (self): """Run each command that was seen on the setup script command line. Uses the list of commands found and cache of command objects - created by 'get_command_obj()'.""" - + created by 'get_command_obj()'. + """ for cmd in self.commands: - self.run_command (cmd) + self.run_command(cmd) # -- Methods that operate on its Commands -------------------------- @@ -838,28 +838,27 @@ class Distribution: doesn't even have a command object yet, create one. Then invoke 'run()' on that command object (or an existing one). """ - # Already been here, done that? then return silently. - if self.have_run.get (command): + if self.have_run.get(command): return - self.announce ("running " + command) - cmd_obj = self.get_command_obj (command) - cmd_obj.ensure_finalized () - cmd_obj.run () + self.announce("running " + command) + cmd_obj = self.get_command_obj(command) + cmd_obj.ensure_finalized() + cmd_obj.run() self.have_run[command] = 1 # -- Distribution query methods ------------------------------------ def has_pure_modules (self): - return len (self.packages or self.py_modules or []) > 0 + return len(self.packages or self.py_modules or []) > 0 def has_ext_modules (self): - return self.ext_modules and len (self.ext_modules) > 0 + return self.ext_modules and len(self.ext_modules) > 0 def has_c_libraries (self): - return self.libraries and len (self.libraries) > 0 + return self.libraries and len(self.libraries) > 0 def has_modules (self): return self.has_pure_modules() or self.has_ext_modules() @@ -890,7 +889,8 @@ class Distribution: class DistributionMetadata: """Dummy class to hold the distribution meta-data: name, version, - author, and so forth.""" + author, and so forth. + """ def __init__ (self): self.name = None @@ -963,5 +963,5 @@ def fix_help_options (options): if __name__ == "__main__": - dist = Distribution () + dist = Distribution() print "ok" -- cgit v1.2.1 From cb2e23a951132584f35e3f5ab94e42034b4c4e28 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 01:56:15 +0000 Subject: Standardize whitespace in function calls. --- core.py | 6 ++-- unixccompiler.py | 104 +++++++++++++++++++++++++++---------------------------- util.py | 24 ++++++------- version.py | 50 +++++++++++++------------- 4 files changed, 92 insertions(+), 92 deletions(-) diff --git a/core.py b/core.py index 5dfe2db8..2aab7c4d 100644 --- a/core.py +++ b/core.py @@ -84,7 +84,7 @@ def setup (**attrs): # Determine the distribution class -- either caller-supplied or # our Distribution (see below). - klass = attrs.get ('distclass') + klass = attrs.get('distclass') if klass: del attrs['distclass'] else: @@ -98,7 +98,7 @@ def setup (**attrs): # Create the Distribution instance, using the remaining arguments # (ie. everything except distclass) to initialize it try: - _setup_distribution = dist = klass (attrs) + _setup_distribution = dist = klass(attrs) except DistutilsSetupError, msg: raise SystemExit, "error in setup script: %s" % msg @@ -135,7 +135,7 @@ def setup (**attrs): # And finally, run all the commands found on the command line. if ok: try: - dist.run_commands () + dist.run_commands() except KeyboardInterrupt: raise SystemExit, "interrupted" except (IOError, os.error), exc: diff --git a/unixccompiler.py b/unixccompiler.py index 93398501..ff0341a5 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -91,8 +91,8 @@ class UnixCCompiler (CCompiler): extra_postargs=None): (_, macros, include_dirs) = \ - self._fix_compile_args (None, macros, include_dirs) - pp_opts = gen_preprocess_options (macros, include_dirs) + self._fix_compile_args(None, macros, include_dirs) + pp_opts = gen_preprocess_options(macros, include_dirs) pp_args = self.preprocessor + pp_opts if output_file: pp_args.extend(['-o', output_file]) @@ -108,7 +108,7 @@ class UnixCCompiler (CCompiler): if output_file: self.mkpath(os.path.dirname(output_file)) try: - self.spawn (pp_args) + self.spawn(pp_args) except DistutilsExecError, msg: raise CompileError, msg @@ -123,11 +123,11 @@ class UnixCCompiler (CCompiler): extra_postargs=None): (output_dir, macros, include_dirs) = \ - self._fix_compile_args (output_dir, macros, include_dirs) - (objects, skip_sources) = self._prep_compile (sources, output_dir) + self._fix_compile_args(output_dir, macros, include_dirs) + (objects, skip_sources) = self._prep_compile(sources, output_dir) # Figure out the options for the compiler command line. - pp_opts = gen_preprocess_options (macros, include_dirs) + pp_opts = gen_preprocess_options(macros, include_dirs) cc_args = pp_opts + ['-c'] if debug: cc_args[:0] = ['-g'] @@ -138,16 +138,16 @@ class UnixCCompiler (CCompiler): # Compile all source files that weren't eliminated by # '_prep_compile()'. - for i in range (len (sources)): + for i in range(len(sources)): src = sources[i] ; obj = objects[i] if skip_sources[src]: - self.announce ("skipping %s (%s up-to-date)" % (src, obj)) + self.announce("skipping %s (%s up-to-date)" % (src, obj)) else: - self.mkpath (os.path.dirname (obj)) + self.mkpath(os.path.dirname(obj)) try: - self.spawn (self.compiler_so + cc_args + - [src, '-o', obj] + - extra_postargs) + self.spawn(self.compiler_so + cc_args + + [src, '-o', obj] + + extra_postargs) except DistutilsExecError, msg: raise CompileError, msg @@ -163,16 +163,16 @@ class UnixCCompiler (CCompiler): output_dir=None, debug=0): - (objects, output_dir) = self._fix_object_args (objects, output_dir) + (objects, output_dir) = self._fix_object_args(objects, output_dir) output_filename = \ - self.library_filename (output_libname, output_dir=output_dir) + self.library_filename(output_libname, output_dir=output_dir) - if self._need_link (objects, output_filename): - self.mkpath (os.path.dirname (output_filename)) - self.spawn (self.archiver + - [output_filename] + - objects + self.objects) + if self._need_link(objects, output_filename): + self.mkpath(os.path.dirname(output_filename)) + self.spawn(self.archiver + + [output_filename] + + objects + self.objects) # Not many Unices required ranlib anymore -- SunOS 4.x is, I # think the only major Unix that does. Maybe we need some @@ -181,11 +181,11 @@ class UnixCCompiler (CCompiler): # it for us, hence the check for leading colon. if self.ranlib: try: - self.spawn (self.ranlib + [output_filename]) + self.spawn(self.ranlib + [output_filename]) except DistutilsExecError, msg: raise LibError, msg else: - self.announce ("skipping %s (up-to-date)" % output_filename) + self.announce("skipping %s (up-to-date)" % output_filename) # create_static_lib () @@ -203,9 +203,9 @@ class UnixCCompiler (CCompiler): extra_postargs=None, build_temp=None): - self.link_shared_object ( + self.link_shared_object( objects, - self.library_filename (output_libname, lib_type='shared'), + self.library_filename(output_libname, lib_type='shared'), output_dir, libraries, library_dirs, @@ -230,19 +230,19 @@ class UnixCCompiler (CCompiler): extra_postargs=None, build_temp=None): - (objects, output_dir) = self._fix_object_args (objects, output_dir) + (objects, output_dir) = self._fix_object_args(objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ - self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) + self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) - lib_opts = gen_lib_options (self, - library_dirs, runtime_library_dirs, - libraries) - if type (output_dir) not in (StringType, NoneType): + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if type(output_dir) not in (StringType, NoneType): raise TypeError, "'output_dir' must be a string or None" if output_dir is not None: - output_filename = os.path.join (output_dir, output_filename) + output_filename = os.path.join(output_dir, output_filename) - if self._need_link (objects, output_filename): + if self._need_link(objects, output_filename): ld_args = (objects + self.objects + lib_opts + ['-o', output_filename]) if debug: @@ -250,14 +250,14 @@ class UnixCCompiler (CCompiler): if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: - ld_args.extend (extra_postargs) - self.mkpath (os.path.dirname (output_filename)) + ld_args.extend(extra_postargs) + self.mkpath(os.path.dirname(output_filename)) try: - self.spawn (self.linker_so + ld_args) + self.spawn(self.linker_so + ld_args) except DistutilsExecError, msg: raise LinkError, msg else: - self.announce ("skipping %s (up-to-date)" % output_filename) + self.announce("skipping %s (up-to-date)" % output_filename) # link_shared_object () @@ -273,32 +273,32 @@ class UnixCCompiler (CCompiler): extra_preargs=None, extra_postargs=None): - (objects, output_dir) = self._fix_object_args (objects, output_dir) + (objects, output_dir) = self._fix_object_args(objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ - self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) + self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) - lib_opts = gen_lib_options (self, - library_dirs, runtime_library_dirs, - libraries) + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) output_filename = output_progname # Unix-ism! if output_dir is not None: - output_filename = os.path.join (output_dir, output_filename) + output_filename = os.path.join(output_dir, output_filename) - if self._need_link (objects, output_filename): + if self._need_link(objects, output_filename): ld_args = objects + self.objects + lib_opts + ['-o', output_filename] if debug: ld_args[:0] = ['-g'] if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: - ld_args.extend (extra_postargs) - self.mkpath (os.path.dirname (output_filename)) + ld_args.extend(extra_postargs) + self.mkpath(os.path.dirname(output_filename)) try: - self.spawn (self.linker_exe + ld_args) + self.spawn(self.linker_exe + ld_args) except DistutilsExecError, msg: raise LinkError, msg else: - self.announce ("skipping %s (up-to-date)" % output_filename) + self.announce("skipping %s (up-to-date)" % output_filename) # link_executable () @@ -320,18 +320,18 @@ class UnixCCompiler (CCompiler): def find_library_file (self, dirs, lib, debug=0): for dir in dirs: - shared = os.path.join ( - dir, self.library_filename (lib, lib_type='shared')) - static = os.path.join ( - dir, self.library_filename (lib, lib_type='static')) + shared = os.path.join( + dir, self.library_filename(lib, lib_type='shared')) + static = os.path.join( + dir, self.library_filename(lib, lib_type='static')) # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm # assuming that *all* Unix C compilers do. And of course I'm # ignoring even GCC's "-static" option. So sue me. - if os.path.exists (shared): + if os.path.exists(shared): return shared - elif os.path.exists (static): + elif os.path.exists(static): return static else: diff --git a/util.py b/util.py index 367985ac..d52c69b4 100644 --- a/util.py +++ b/util.py @@ -88,16 +88,16 @@ def change_root (new_root, pathname): two, which is tricky on DOS/Windows and Mac OS. """ if os.name == 'posix': - if not os.path.isabs (pathname): - return os.path.join (new_root, pathname) + if not os.path.isabs(pathname): + return os.path.join(new_root, pathname) else: - return os.path.join (new_root, pathname[1:]) + return os.path.join(new_root, pathname[1:]) elif os.name == 'nt': - (drive, path) = os.path.splitdrive (pathname) + (drive, path) = os.path.splitdrive(pathname) if path[0] == '\\': path = path[1:] - return os.path.join (new_root, path) + return os.path.join(new_root, path) elif os.name == 'mac': if not os.path.isabs(pathname): @@ -129,10 +129,10 @@ def check_environ (): if os.name == 'posix' and not os.environ.has_key('HOME'): import pwd - os.environ['HOME'] = pwd.getpwuid (os.getuid())[5] + os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] if not os.environ.has_key('PLAT'): - os.environ['PLAT'] = get_platform () + os.environ['PLAT'] = get_platform() _environ_checked = 1 @@ -147,15 +147,15 @@ def subst_vars (str, local_vars): '_check_environ()'. Raise ValueError for any variables not found in either 'local_vars' or 'os.environ'.""" - check_environ () + check_environ() def _subst (match, local_vars=local_vars): var_name = match.group(1) - if local_vars.has_key (var_name): - return str (local_vars[var_name]) + if local_vars.has_key(var_name): + return str(local_vars[var_name]) else: return os.environ[var_name] - return re.sub (r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str) + return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str) # subst_vars () @@ -169,7 +169,7 @@ def grok_environment_error (exc, prefix="error: "): prefixed with 'prefix'. """ # check for Python 1.5.2-style {IO,OS}Error exception objects - if hasattr (exc, 'filename') and hasattr (exc, 'strerror'): + if hasattr(exc, 'filename') and hasattr(exc, 'strerror'): if exc.filename: error = prefix + "%s: %s" % (exc.filename, exc.strerror) else: diff --git a/version.py b/version.py index 8b9ef106..9d3d1724 100644 --- a/version.py +++ b/version.py @@ -39,10 +39,10 @@ class Version: def __init__ (self, vstring=None): if vstring: - self.parse (vstring) + self.parse(vstring) def __repr__ (self): - return "%s ('%s')" % (self.__class__.__name__, str (self)) + return "%s ('%s')" % (self.__class__.__name__, str(self)) # Interface for version-number classes -- must be implemented @@ -99,25 +99,25 @@ class StrictVersion (Version): in the distutils documentation. """ - version_re = re.compile (r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', - re.VERBOSE) + version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', + re.VERBOSE) def parse (self, vstring): - match = self.version_re.match (vstring) + match = self.version_re.match(vstring) if not match: raise ValueError, "invalid version number '%s'" % vstring (major, minor, patch, prerelease, prerelease_num) = \ - match.group (1, 2, 4, 5, 6) + match.group(1, 2, 4, 5, 6) if patch: - self.version = tuple (map (string.atoi, [major, minor, patch])) + self.version = tuple(map(string.atoi, [major, minor, patch])) else: - self.version = tuple (map (string.atoi, [major, minor]) + [0]) + self.version = tuple(map(string.atoi, [major, minor]) + [0]) if prerelease: - self.prerelease = (prerelease[0], string.atoi (prerelease_num)) + self.prerelease = (prerelease[0], string.atoi(prerelease_num)) else: self.prerelease = None @@ -125,21 +125,21 @@ class StrictVersion (Version): def __str__ (self): if self.version[2] == 0: - vstring = string.join (map (str, self.version[0:2]), '.') + vstring = string.join(map(str, self.version[0:2]), '.') else: - vstring = string.join (map (str, self.version), '.') + vstring = string.join(map(str, self.version), '.') if self.prerelease: - vstring = vstring + self.prerelease[0] + str (self.prerelease[1]) + vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) return vstring def __cmp__ (self, other): - if isinstance (other, StringType): - other = StrictVersion (other) + if isinstance(other, StringType): + other = StrictVersion(other) - compare = cmp (self.version, other.version) + compare = cmp(self.version, other.version) if (compare == 0): # have to compare prerelease # case 1: neither has prerelease; they're equal @@ -154,7 +154,7 @@ class StrictVersion (Version): elif (not self.prerelease and other.prerelease): return 1 elif (self.prerelease and other.prerelease): - return cmp (self.prerelease, other.prerelease) + return cmp(self.prerelease, other.prerelease) else: # numeric versions don't match -- return compare # prerelease stuff doesn't matter @@ -264,7 +264,7 @@ class LooseVersion (Version): def __init__ (self, vstring=None): if vstring: - self.parse (vstring) + self.parse(vstring) def parse (self, vstring): @@ -272,11 +272,11 @@ class LooseVersion (Version): # from the parsed tuple -- so I just store the string here for # use by __str__ self.vstring = vstring - components = filter (lambda x: x and x != '.', - self.component_re.split (vstring)) - for i in range (len (components)): + components = filter(lambda x: x and x != '.', + self.component_re.split(vstring)) + for i in range(len(components)): try: - components[i] = int (components[i]) + components[i] = int(components[i]) except ValueError: pass @@ -288,14 +288,14 @@ class LooseVersion (Version): def __repr__ (self): - return "LooseVersion ('%s')" % str (self) + return "LooseVersion ('%s')" % str(self) def __cmp__ (self, other): - if isinstance (other, StringType): - other = LooseVersion (other) + if isinstance(other, StringType): + other = LooseVersion(other) - return cmp (self.version, other.version) + return cmp(self.version, other.version) # end class LooseVersion -- cgit v1.2.1 From f5d5642c6b067bcbf6f82020cc1fda18dbb74511 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 02:00:51 +0000 Subject: Reformat docstrings. Standardize whitespace in function calls. --- spawn.py | 79 +++++++++++++++++++++++++++++++++------------------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/spawn.py b/spawn.py index 651124d6..1eed7a8a 100644 --- a/spawn.py +++ b/spawn.py @@ -3,7 +3,8 @@ Provides the 'spawn()' function, a front-end to various platform- specific functions for launching another program in a sub-process. Also provides the 'find_executable()' to search the path for a given -executable name. """ +executable name. +""" # created 1999/07/24, Greg Ward @@ -19,24 +20,24 @@ def spawn (cmd, dry_run=0): """Run another program, specified as a command list 'cmd', in a new - process. 'cmd' is just the argument list for the new process, ie. - cmd[0] is the program to run and cmd[1:] are the rest of its - arguments. There is no way to run a program with a name different - from that of its executable. - - If 'search_path' is true (the default), the system's executable - search path will be used to find the program; otherwise, cmd[0] must - be the exact path to the executable. If 'verbose' is true, a - one-line summary of the command will be printed before it is run. - If 'dry_run' is true, the command will not actually be run. - - Raise DistutilsExecError if running the program fails in any way; - just return on success.""" - + process. 'cmd' is just the argument list for the new process, ie. + cmd[0] is the program to run and cmd[1:] are the rest of its arguments. + There is no way to run a program with a name different from that of its + executable. + + If 'search_path' is true (the default), the system's executable search + path will be used to find the program; otherwise, cmd[0] must be the + exact path to the executable. If 'verbose' is true, a one-line summary + of the command will be printed before it is run. If 'dry_run' is true, + the command will not actually be run. + + Raise DistutilsExecError if running the program fails in any way; just + return on success. + """ if os.name == 'posix': - _spawn_posix (cmd, search_path, verbose, dry_run) + _spawn_posix(cmd, search_path, verbose, dry_run) elif os.name == 'nt': - _spawn_nt (cmd, search_path, verbose, dry_run) + _spawn_nt(cmd, search_path, verbose, dry_run) else: raise DistutilsPlatformError, \ "don't know how to spawn programs on platform '%s'" % os.name @@ -45,8 +46,10 @@ def spawn (cmd, def _nt_quote_args (args): - """Obscure quoting command line arguments on NT. - Simply quote every argument which contains blanks.""" + """Quote command-line arguments for DOS/Windows conventions: just + wraps every argument which contains blanks in double quotes, and + returns a new argument list. + """ # XXX this doesn't seem very robust to me -- but if the Windows guys # say it'll work, I guess I'll have to accept it. (What if an arg @@ -54,8 +57,8 @@ def _nt_quote_args (args): # have to be escaped? Is there an escaping mechanism other than # quoting?) - for i in range (len (args)): - if string.find (args[i], ' ') != -1: + for i in range(len(args)): + if string.find(args[i], ' ') != -1: args[i] = '"%s"' % args[i] return args @@ -65,16 +68,16 @@ def _spawn_nt (cmd, dry_run=0): executable = cmd[0] - cmd = _nt_quote_args (cmd) + cmd = _nt_quote_args(cmd) if search_path: # either we find one or it stays the same executable = find_executable(executable) or executable if verbose: - print string.join ([executable] + cmd[1:], ' ') + print string.join([executable] + cmd[1:], ' ') if not dry_run: # spawn for NT requires a full path to the .exe try: - rc = os.spawnv (os.P_WAIT, executable, cmd) + rc = os.spawnv(os.P_WAIT, executable, cmd) except OSError, exc: # this seems to happen when the command isn't found raise DistutilsExecError, \ @@ -91,39 +94,39 @@ def _spawn_posix (cmd, dry_run=0): if verbose: - print string.join (cmd, ' ') + print string.join(cmd, ' ') if dry_run: return exec_fn = search_path and os.execvp or os.execv - pid = os.fork () + pid = os.fork() if pid == 0: # in the child try: #print "cmd[0] =", cmd[0] #print "cmd =", cmd - exec_fn (cmd[0], cmd) + exec_fn(cmd[0], cmd) except OSError, e: - sys.stderr.write ("unable to execute %s: %s\n" % - (cmd[0], e.strerror)) - os._exit (1) + sys.stderr.write("unable to execute %s: %s\n" % + (cmd[0], e.strerror)) + os._exit(1) - sys.stderr.write ("unable to execute %s for unknown reasons" % cmd[0]) - os._exit (1) + sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0]) + os._exit(1) else: # in the parent # Loop until the child either exits or is terminated by a signal # (ie. keep waiting if it's merely stopped) while 1: - (pid, status) = os.waitpid (pid, 0) - if os.WIFSIGNALED (status): + (pid, status) = os.waitpid(pid, 0) + if os.WIFSIGNALED(status): raise DistutilsExecError, \ "command '%s' terminated by signal %d" % \ - (cmd[0], os.WTERMSIG (status)) + (cmd[0], os.WTERMSIG(status)) - elif os.WIFEXITED (status): - exit_status = os.WEXITSTATUS (status) + elif os.WIFEXITED(status): + exit_status = os.WEXITSTATUS(status) if exit_status == 0: return # hey, it succeeded! else: @@ -131,7 +134,7 @@ def _spawn_posix (cmd, "command '%s' failed with exit status %d" % \ (cmd[0], exit_status) - elif os.WIFSTOPPED (status): + elif os.WIFSTOPPED(status): continue else: -- cgit v1.2.1 From 310173b2ba5b272a6ef87f9057c36381712d42c5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 02:03:34 +0000 Subject: Whitespace fix. --- file_util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/file_util.py b/file_util.py index 0e85a74b..d05b4561 100644 --- a/file_util.py +++ b/file_util.py @@ -1,6 +1,7 @@ """distutils.file_util -Utility functions for operating on single files.""" +Utility functions for operating on single files. +""" # created 2000/04/03, Greg Ward (extracted from util.py) -- cgit v1.2.1 From b7ed5395cfb4acb46268fe41f6f8188e2f3da77d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 02:12:31 +0000 Subject: Standardize whitespace in function calls. --- cmd.py | 54 ++++++++++++++-------------- dir_util.py | 60 +++++++++++++++---------------- fancy_getopt.py | 109 ++++++++++++++++++++++++++++---------------------------- filelist.py | 69 ++++++++++++++++++----------------- 4 files changed, 145 insertions(+), 147 deletions(-) diff --git a/cmd.py b/cmd.py index 7866d1b6..ce448294 100644 --- a/cmd.py +++ b/cmd.py @@ -59,13 +59,13 @@ class Command: # late import because of mutual dependence between these classes from distutils.dist import Distribution - if not isinstance (dist, Distribution): + if not isinstance(dist, Distribution): raise TypeError, "dist must be a Distribution instance" if self.__class__ is Command: raise RuntimeError, "Command is an abstract class" self.distribution = dist - self.initialize_options () + self.initialize_options() # Per-command versions of the global flags, so that the user can # customize Distutils' behaviour command-by-command and let some @@ -98,9 +98,9 @@ class Command: def __getattr__ (self, attr): if attr in ('verbose', 'dry_run'): - myval = getattr (self, "_" + attr) + myval = getattr(self, "_" + attr) if myval is None: - return getattr (self.distribution, attr) + return getattr(self.distribution, attr) else: return myval else: @@ -109,7 +109,7 @@ class Command: def ensure_finalized (self): if not self.finalized: - self.finalize_options () + self.finalize_options() self.finalized = 1 @@ -273,7 +273,7 @@ class Command: # -- Convenience methods for commands ------------------------------ def get_command_name (self): - if hasattr (self, 'command_name'): + if hasattr(self, 'command_name'): return self.command_name else: return self.__class__.__name__ @@ -296,12 +296,12 @@ class Command: # Option_pairs: list of (src_option, dst_option) tuples - src_cmd_obj = self.distribution.get_command_obj (src_cmd) - src_cmd_obj.ensure_finalized () + src_cmd_obj = self.distribution.get_command_obj(src_cmd) + src_cmd_obj.ensure_finalized() for (src_option, dst_option) in option_pairs: - if getattr (self, dst_option) is None: - setattr (self, dst_option, - getattr (src_cmd_obj, src_option)) + if getattr(self, dst_option) is None: + setattr(self, dst_option, + getattr(src_cmd_obj, src_option)) def get_finalized_command (self, command, create=1): @@ -310,8 +310,8 @@ class Command: 'command', call its 'ensure_finalized()' method, and return the finalized command object. """ - cmd_obj = self.distribution.get_command_obj (command, create) - cmd_obj.ensure_finalized () + cmd_obj = self.distribution.get_command_obj(command, create) + cmd_obj.ensure_finalized() return cmd_obj # XXX rename to 'get_reinitialized_command()'? (should do the @@ -325,7 +325,7 @@ class Command: Distribution, which creates and finalizes the command object if necessary and then invokes its 'run()' method. """ - self.distribution.run_command (command) + self.distribution.run_command(command) def get_sub_commands (self): @@ -345,8 +345,8 @@ class Command: # -- External world manipulation ----------------------------------- def warn (self, msg): - sys.stderr.write ("warning: %s: %s\n" % - (self.get_command_name(), msg)) + sys.stderr.write("warning: %s: %s\n" % + (self.get_command_name(), msg)) def execute (self, func, args, msg=None, level=1): @@ -389,17 +389,17 @@ class Command: def move_file (self, src, dst, level=1): """Move a file respecting verbose and dry-run flags.""" - return file_util.move_file (src, dst, - self.verbose >= level, - self.dry_run) + return file_util.move_file(src, dst, + self.verbose >= level, + self.dry_run) def spawn (self, cmd, search_path=1, level=1): """Spawn an external command respecting verbose and dry-run flags.""" from distutils.spawn import spawn - spawn (cmd, search_path, - self.verbose >= level, - self.dry_run) + spawn(cmd, search_path, + self.verbose >= level, + self.dry_run) def make_archive (self, base_name, format, @@ -421,15 +421,15 @@ class Command: """ if exec_msg is None: exec_msg = "generating %s from %s" % \ - (outfile, string.join (infiles, ', ')) + (outfile, string.join(infiles, ', ')) if skip_msg is None: skip_msg = "skipping %s (inputs unchanged)" % outfile # Allow 'infiles' to be a single string - if type (infiles) is StringType: + if type(infiles) is StringType: infiles = (infiles,) - elif type (infiles) not in (ListType, TupleType): + elif type(infiles) not in (ListType, TupleType): raise TypeError, \ "'infiles' must be a string, or a list or tuple of strings" @@ -437,11 +437,11 @@ class Command: # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it if self.force or dep_util.newer_group (infiles, outfile): - self.execute (func, args, exec_msg, level) + self.execute(func, args, exec_msg, level) # Otherwise, print the "skip" message else: - self.announce (skip_msg, level) + self.announce(skip_msg, level) # make_file () diff --git a/dir_util.py b/dir_util.py index 85f8a18d..768cb4eb 100644 --- a/dir_util.py +++ b/dir_util.py @@ -40,21 +40,21 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): # the creation of the whole path? (quite easy to do the latter since # we're not using a recursive algorithm) - name = os.path.normpath (name) + name = os.path.normpath(name) created_dirs = [] - if os.path.isdir (name) or name == '': + if os.path.isdir(name) or name == '': return created_dirs - if _path_created.get (name): + if _path_created.get(name): return created_dirs - (head, tail) = os.path.split (name) + (head, tail) = os.path.split(name) tails = [tail] # stack of lone dirs to create - while head and tail and not os.path.isdir (head): + while head and tail and not os.path.isdir(head): #print "splitting '%s': " % head, - (head, tail) = os.path.split (head) + (head, tail) = os.path.split(head) #print "to ('%s','%s')" % (head, tail) - tails.insert (0, tail) # push next higher dir onto stack + tails.insert(0, tail) # push next higher dir onto stack #print "stack of tails:", tails @@ -63,8 +63,8 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): # that does *not* exist) for d in tails: #print "head = %s, d = %s: " % (head, d), - head = os.path.join (head, d) - if _path_created.get (head): + head = os.path.join(head, d) + if _path_created.get(head): continue if verbose: @@ -72,7 +72,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): if not dry_run: try: - os.mkdir (head) + os.mkdir(head) created_dirs.append(head) except OSError, exc: raise DistutilsFileError, \ @@ -97,13 +97,13 @@ def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0): # First get the list of directories to create need_dir = {} for file in files: - need_dir[os.path.join (base_dir, os.path.dirname (file))] = 1 + need_dir[os.path.join(base_dir, os.path.dirname(file))] = 1 need_dirs = need_dir.keys() need_dirs.sort() # Now create them for dir in need_dirs: - mkpath (dir, mode, verbose, dry_run) + mkpath(dir, mode, verbose, dry_run) # create_tree () @@ -136,11 +136,11 @@ def copy_tree (src, dst, from distutils.file_util import copy_file - if not dry_run and not os.path.isdir (src): + if not dry_run and not os.path.isdir(src): raise DistutilsFileError, \ "cannot copy tree '%s': not a directory" % src try: - names = os.listdir (src) + names = os.listdir(src) except os.error, (errno, errstr): if dry_run: names = [] @@ -149,32 +149,32 @@ def copy_tree (src, dst, "error listing files in '%s': %s" % (src, errstr) if not dry_run: - mkpath (dst, verbose=verbose) + mkpath(dst, verbose=verbose) outputs = [] for n in names: - src_name = os.path.join (src, n) - dst_name = os.path.join (dst, n) + src_name = os.path.join(src, n) + dst_name = os.path.join(dst, n) - if preserve_symlinks and os.path.islink (src_name): - link_dest = os.readlink (src_name) + if preserve_symlinks and os.path.islink(src_name): + link_dest = os.readlink(src_name) if verbose: print "linking %s -> %s" % (dst_name, link_dest) if not dry_run: - os.symlink (link_dest, dst_name) - outputs.append (dst_name) + os.symlink(link_dest, dst_name) + outputs.append(dst_name) - elif os.path.isdir (src_name): - outputs.extend ( - copy_tree (src_name, dst_name, - preserve_mode, preserve_times, preserve_symlinks, - update, verbose, dry_run)) + elif os.path.isdir(src_name): + outputs.extend( + copy_tree(src_name, dst_name, + preserve_mode, preserve_times, preserve_symlinks, + update, verbose, dry_run)) else: - copy_file (src_name, dst_name, - preserve_mode, preserve_times, - update, None, verbose, dry_run) - outputs.append (dst_name) + copy_file(src_name, dst_name, + preserve_mode, preserve_times, + update, None, verbose, dry_run) + outputs.append(dst_name) return outputs diff --git a/fancy_getopt.py b/fancy_getopt.py index f9352001..6f8b8c06 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -22,14 +22,14 @@ from distutils.errors import * # utilities, we use '-' in place of '_'. (The spirit of LISP lives on!) # The similarities to NAME are again not a coincidence... longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)' -longopt_re = re.compile (r'^%s$' % longopt_pat) +longopt_re = re.compile(r'^%s$' % longopt_pat) # For recognizing "negative alias" options, eg. "quiet=!verbose" -neg_alias_re = re.compile ("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) +neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) # This is used to translate long options to legitimate Python identifiers # (for use as attributes of some object). -longopt_xlate = string.maketrans ('-', '_') +longopt_xlate = string.maketrans('-', '_') # This records (option, value) pairs in the order seen on the command line; # it's close to what getopt.getopt() returns, but with short options @@ -107,7 +107,7 @@ class FancyGetopt: "option conflict: already an option '%s'" % long_option else: option = (long_option, short_option, help_string) - self.option_table.append (option) + self.option_table.append(option) self.option_index[long_option] = option @@ -120,7 +120,7 @@ class FancyGetopt: """Translate long option name 'long_option' to the form it has as an attribute of some object: ie., translate hyphens to underscores.""" - return string.translate (long_option, longopt_xlate) + return string.translate(long_option, longopt_xlate) def _check_alias_dict (self, aliases, what): @@ -137,7 +137,7 @@ class FancyGetopt: def set_aliases (self, alias): """Set the aliases for this option parser.""" - self._check_alias_dict (alias, "alias") + self._check_alias_dict(alias, "alias") self.alias = alias def set_negative_aliases (self, negative_alias): @@ -145,15 +145,15 @@ class FancyGetopt: 'negative_alias' should be a dictionary mapping option names to option names, both the key and value must already be defined in the option table.""" - self._check_alias_dict (negative_alias, "negative alias") + self._check_alias_dict(negative_alias, "negative alias") self.negative_alias = negative_alias def _grok_option_table (self): - """Populate the various data structures that keep tabs on - the option table. Called by 'getopt()' before it can do - anything worthwhile.""" - + """Populate the various data structures that keep tabs on the + option table. Called by 'getopt()' before it can do anything + worthwhile. + """ self.long_opts = [] self.short_opts = [] self.short2long.clear() @@ -163,7 +163,7 @@ class FancyGetopt: (long, short, help) = option except ValueError: raise DistutilsGetoptError, \ - "invalid option tuple " + str (option) + "invalid option tuple " + str(option) # Type- and value-check the option names if type(long) is not StringType or len(long) < 2: @@ -172,12 +172,12 @@ class FancyGetopt: "must be a string of length >= 2") % long if (not ((short is None) or - (type (short) is StringType and len (short) == 1))): + (type(short) is StringType and len(short) == 1))): raise DistutilsGetoptError, \ ("invalid short option '%s': " "must a single character or None") % short - self.long_opts.append (long) + self.long_opts.append(long) if long[-1] == '=': # option takes an argument? if short: short = short + ':' @@ -216,14 +216,14 @@ class FancyGetopt: # later translate it to an attribute name on some object. Have # to do this a bit late to make sure we've removed any trailing # '='. - if not longopt_re.match (long): + if not longopt_re.match(long): raise DistutilsGetoptError, \ ("invalid long option name '%s' " + "(must be letters, numbers, hyphens only") % long - self.attr_name[long] = self.get_attr_name (long) + self.attr_name[long] = self.get_attr_name(long) if short: - self.short_opts.append (short) + self.short_opts.append(short) self.short2long[short[0]] = long # for option_table @@ -239,8 +239,8 @@ class FancyGetopt: (args, object). If 'object' is supplied, it is modified in place and 'getopt()' just returns 'args'; in both cases, the returned 'args' is a modified copy of the passed-in 'args' list, which is - left untouched.""" - + left untouched. + """ if args is None: args = sys.argv[1:] if object is None: @@ -251,17 +251,17 @@ class FancyGetopt: self._grok_option_table() - short_opts = string.join (self.short_opts) + short_opts = string.join(self.short_opts) try: - (opts, args) = getopt.getopt (args, short_opts, self.long_opts) + (opts, args) = getopt.getopt(args, short_opts, self.long_opts) except getopt.error, msg: raise DistutilsArgError, msg for (opt, val) in opts: - if len (opt) == 2 and opt[0] == '-': # it's a short option + if len(opt) == 2 and opt[0] == '-': # it's a short option opt = self.short2long[opt[1]] - elif len (opt) > 2 and opt[0:2] == '--': + elif len(opt) > 2 and opt[0:2] == '--': opt = opt[2:] else: @@ -277,7 +277,7 @@ class FancyGetopt: raise DistutilsInternalError, \ "this can't happen: bad option value '%s'" % value - alias = self.negative_alias.get (opt) + alias = self.negative_alias.get(opt) if alias: opt = alias val = 0 @@ -285,8 +285,8 @@ class FancyGetopt: val = 1 attr = self.attr_name[opt] - setattr (object, attr, val) - self.option_order.append ((opt, val)) + setattr(object, attr, val) + self.option_order.append((opt, val)) # for opts @@ -301,8 +301,8 @@ class FancyGetopt: def get_option_order (self): """Returns the list of (option, value) tuples processed by the previous run of 'getopt()'. Raises RuntimeError if - 'getopt()' hasn't been called yet.""" - + 'getopt()' hasn't been called yet. + """ if self.option_order is None: raise RuntimeError, "'getopt()' hasn't been called yet" else: @@ -311,8 +311,8 @@ class FancyGetopt: def generate_help (self, header=None): """Generate help text (a list of strings, one per suggested line of - output) from the option table for this FancyGetopt object.""" - + output) from the option table for this FancyGetopt object. + """ # Blithely assume the option table is good: probably wouldn't call # 'generate_help()' unless you've already called 'getopt()'. @@ -321,7 +321,7 @@ class FancyGetopt: for option in self.option_table: long = option[0] short = option[1] - l = len (long) + l = len(long) if long[-1] == '=': l = l - 1 if short is not None: @@ -363,29 +363,29 @@ class FancyGetopt: for (long,short,help) in self.option_table: - text = wrap_text (help, text_width) + text = wrap_text(help, text_width) if long[-1] == '=': long = long[0:-1] # Case 1: no short option at all (makes life easy) if short is None: if text: - lines.append (" --%-*s %s" % (max_opt, long, text[0])) + lines.append(" --%-*s %s" % (max_opt, long, text[0])) else: - lines.append (" --%-*s " % (max_opt, long)) + lines.append(" --%-*s " % (max_opt, long)) # Case 2: we have a short option, so we have to include it # just after the long option else: opt_names = "%s (-%s)" % (long, short) if text: - lines.append (" --%-*s %s" % - (max_opt, opt_names, text[0])) + lines.append(" --%-*s %s" % + (max_opt, opt_names, text[0])) else: - lines.append (" --%-*s" % opt_names) + lines.append(" --%-*s" % opt_names) for l in text[1:]: - lines.append (big_indent + l) + lines.append(big_indent + l) # for self.option_table @@ -396,20 +396,19 @@ class FancyGetopt: def print_help (self, header=None, file=None): if file is None: file = sys.stdout - for line in self.generate_help (header): - file.write (line + "\n") - # print_help () + for line in self.generate_help(header): + file.write(line + "\n") # class FancyGetopt def fancy_getopt (options, negative_opt, object, args): - parser = FancyGetopt (options) - parser.set_negative_aliases (negative_opt) - return parser.getopt (args, object) + parser = FancyGetopt(options) + parser.set_negative_aliases(negative_opt) + return parser.getopt(args, object) -WS_TRANS = string.maketrans (string.whitespace, ' ' * len (string.whitespace)) +WS_TRANS = string.maketrans(string.whitespace, ' ' * len(string.whitespace)) def wrap_text (text, width): """wrap_text(text : string, width : int) -> [string] @@ -420,13 +419,13 @@ def wrap_text (text, width): if text is None: return [] - if len (text) <= width: + if len(text) <= width: return [text] - text = string.expandtabs (text) - text = string.translate (text, WS_TRANS) - chunks = re.split (r'( +|-+)', text) - chunks = filter (None, chunks) # ' - ' results in empty strings + text = string.expandtabs(text) + text = string.translate(text, WS_TRANS) + chunks = re.split(r'( +|-+)', text) + chunks = filter(None, chunks) # ' - ' results in empty strings lines = [] while chunks: @@ -435,9 +434,9 @@ def wrap_text (text, width): cur_len = 0 # length of current line while chunks: - l = len (chunks[0]) + l = len(chunks[0]) if cur_len + l <= width: # can squeeze (at least) this chunk in - cur_line.append (chunks[0]) + cur_line.append(chunks[0]) del chunks[0] cur_len = cur_len + l else: # this line is full @@ -452,7 +451,7 @@ def wrap_text (text, width): # chunk that's too big too fit on a line -- so we break # down and break it up at the line width if cur_len == 0: - cur_line.append (chunks[0][0:width]) + cur_line.append(chunks[0][0:width]) chunks[0] = chunks[0][width:] # all-whitespace chunks at the end of a line can be discarded @@ -463,7 +462,7 @@ def wrap_text (text, width): # and store this line in the list-of-all-lines -- as a single # string, of course! - lines.append (string.join (cur_line, '')) + lines.append(string.join(cur_line, '')) # while chunks @@ -501,5 +500,5 @@ say, "How should I know?"].)""" for w in (10, 20, 30, 40): print "width: %d" % w - print string.join (wrap_text (text, w), "\n") + print string.join(wrap_text(text, w), "\n") print diff --git a/filelist.py b/filelist.py index 84f36d2c..211b65f8 100644 --- a/filelist.py +++ b/filelist.py @@ -55,7 +55,7 @@ class FileList: # -- Fallback warning/debug functions ------------------------------ def __warn (self, msg): - sys.stderr.write ("warning: %s\n" % msg) + sys.stderr.write("warning: %s\n" % msg) def __debug_print (self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the @@ -87,7 +87,7 @@ class FileList: def remove_duplicates (self): # Assumes list has been sorted! - for i in range (len(self.files)-1, 0, -1): + for i in range(len(self.files)-1, 0, -1): if self.files[i] == self.files[i-1]: del self.files[i] @@ -95,21 +95,21 @@ class FileList: # -- "File template" methods --------------------------------------- def _parse_template_line (self, line): - words = string.split (line) + words = string.split(line) action = words[0] patterns = dir = dir_pattern = None if action in ('include', 'exclude', 'global-include', 'global-exclude'): - if len (words) < 2: + if len(words) < 2: raise DistutilsTemplateError, \ "'%s' expects ..." % action patterns = map(convert_path, words[1:]) elif action in ('recursive-include', 'recursive-exclude'): - if len (words) < 3: + if len(words) < 3: raise DistutilsTemplateError, \ "'%s' expects ..." % action @@ -117,7 +117,7 @@ class FileList: patterns = map(convert_path, words[2:]) elif action in ('graft', 'prune'): - if len (words) != 2: + if len(words) != 2: raise DistutilsTemplateError, \ "'%s' expects a single " % action @@ -146,13 +146,13 @@ class FileList: if action == 'include': self.debug_print("include " + string.join(patterns)) for pattern in patterns: - if not self.include_pattern (pattern, anchor=1): + if not self.include_pattern(pattern, anchor=1): self.warn("no files found matching '%s'" % pattern) elif action == 'exclude': self.debug_print("exclude " + string.join(patterns)) for pattern in patterns: - if not self.exclude_pattern (pattern, anchor=1): + if not self.exclude_pattern(pattern, anchor=1): self.warn( "no previously-included files found matching '%s'"% pattern) @@ -160,15 +160,15 @@ class FileList: elif action == 'global-include': self.debug_print("global-include " + string.join(patterns)) for pattern in patterns: - if not self.include_pattern (pattern, anchor=0): - self.warn (("no files found matching '%s' " + - "anywhere in distribution") % - pattern) + if not self.include_pattern(pattern, anchor=0): + self.warn(("no files found matching '%s' " + + "anywhere in distribution") % + pattern) elif action == 'global-exclude': self.debug_print("global-exclude " + string.join(patterns)) for pattern in patterns: - if not self.exclude_pattern (pattern, anchor=0): + if not self.exclude_pattern(pattern, anchor=0): self.warn(("no previously-included files matching '%s' " + "found anywhere in distribution") % pattern) @@ -177,8 +177,8 @@ class FileList: self.debug_print("recursive-include %s %s" % (dir, string.join(patterns))) for pattern in patterns: - if not self.include_pattern (pattern, prefix=dir): - self.warn (("no files found matching '%s' " + + if not self.include_pattern(pattern, prefix=dir): + self.warn(("no files found matching '%s' " + "under directory '%s'") % (pattern, dir)) @@ -190,11 +190,11 @@ class FileList: self.warn(("no previously-included files matching '%s' " + "found under directory '%s'") % (pattern, dir)) - + elif action == 'graft': self.debug_print("graft " + dir_pattern) if not self.include_pattern(None, prefix=dir_pattern): - self.warn ("no directories found matching '%s'" % dir_pattern) + self.warn("no directories found matching '%s'" % dir_pattern) elif action == 'prune': self.debug_print("prune " + dir_pattern) @@ -212,8 +212,7 @@ class FileList: # -- Filtering/selection methods ----------------------------------- def include_pattern (self, pattern, - anchor=1, prefix=None, is_regex=0): - + anchor=1, prefix=None, is_regex=0): """Select strings (presumably filenames) from 'self.files' that match 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not quite the same as implemented by the 'fnmatch' module: '*' @@ -239,7 +238,7 @@ class FileList: Return 1 if files are found. """ files_found = 0 - pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) + pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) self.debug_print("include_pattern: applying regex r'%s'" % pattern_re.pattern) @@ -248,9 +247,9 @@ class FileList: self.findall() for name in self.allfiles: - if pattern_re.search (name): + if pattern_re.search(name): self.debug_print(" adding " + name) - self.files.append (name) + self.files.append(name) files_found = 1 return files_found @@ -267,11 +266,11 @@ class FileList: Return 1 if files are found. """ files_found = 0 - pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) + pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) self.debug_print("exclude_pattern: applying regex r'%s'" % pattern_re.pattern) - for i in range (len(self.files)-1, -1, -1): - if pattern_re.search (self.files[i]): + for i in range(len(self.files)-1, -1, -1): + if pattern_re.search(self.files[i]): self.debug_print(" removing " + self.files[i]) del self.files[i] files_found = 1 @@ -299,11 +298,11 @@ def findall (dir = os.curdir): while stack: dir = pop() - names = os.listdir (dir) + names = os.listdir(dir) for name in names: if dir != os.curdir: # avoid the dreaded "./" syndrome - fullname = os.path.join (dir, name) + fullname = os.path.join(dir, name) else: fullname = name @@ -311,9 +310,9 @@ def findall (dir = os.curdir): stat = os.stat(fullname) mode = stat[ST_MODE] if S_ISREG(mode): - list.append (fullname) + list.append(fullname) elif S_ISDIR(mode) and not S_ISLNK(mode): - push (fullname) + push(fullname) return list @@ -324,7 +323,7 @@ def glob_to_re (pattern): that '*' does not match "special characters" (which are platform-specific). """ - pattern_re = fnmatch.translate (pattern) + pattern_re = fnmatch.translate(pattern) # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, @@ -333,7 +332,7 @@ def glob_to_re (pattern): # character except the special characters. # XXX currently the "special characters" are just slash -- i.e. this is # Unix-only. - pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re) + pattern_re = re.sub(r'(^|[^\\])\.', r'\1[^/]', pattern_re) return pattern_re # glob_to_re () @@ -352,17 +351,17 @@ def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): return pattern if pattern: - pattern_re = glob_to_re (pattern) + pattern_re = glob_to_re(pattern) else: pattern_re = '' if prefix is not None: - prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $ - pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re) + prefix_re = (glob_to_re(prefix))[0:-1] # ditch trailing $ + pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) else: # no prefix -- respect anchor flag if anchor: pattern_re = "^" + pattern_re - return re.compile (pattern_re) + return re.compile(pattern_re) # translate_pattern () -- cgit v1.2.1 From 208764c9d80a704b06463dbadf392709af561b57 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 02:13:49 +0000 Subject: Reformat docstrings. Standardize whitespace in function calls. --- archive_util.py | 56 ++++++++++++++++++++++----------------------- dep_util.py | 71 ++++++++++++++++++++++++++++----------------------------- 2 files changed, 63 insertions(+), 64 deletions(-) diff --git a/archive_util.py b/archive_util.py index 26cd7fb2..61bc25ea 100644 --- a/archive_util.py +++ b/archive_util.py @@ -15,13 +15,13 @@ from distutils.dir_util import mkpath def make_tarball (base_name, base_dir, compress="gzip", verbose=0, dry_run=0): """Create a (possibly compressed) tar file from all the files under - 'base_dir'. 'compress' must be "gzip" (the default), "compress", - "bzip2", or None. Both "tar" and the compression utility named by - 'compress' must be on the default program search path, so this is - probably Unix-specific. The output tar file will be named 'base_dir' - + ".tar", possibly plus the appropriate compression extension (".gz", - ".bz2" or ".Z"). Return the output filename.""" - + 'base_dir'. 'compress' must be "gzip" (the default), "compress", + "bzip2", or None. Both "tar" and the compression utility named by + 'compress' must be on the default program search path, so this is + probably Unix-specific. The output tar file will be named 'base_dir' + + ".tar", possibly plus the appropriate compression extension (".gz", + ".bz2" or ".Z"). Return the output filename. + """ # XXX GNU tar 1.13 has a nifty option to add a prefix directory. # It's pretty new, though, so we certainly can't require it -- # but it would be nice to take advantage of it to skip the @@ -44,11 +44,11 @@ def make_tarball (base_name, base_dir, compress="gzip", archive_name = base_name + ".tar" mkpath(os.path.dirname(archive_name), verbose=verbose, dry_run=dry_run) cmd = ["tar", "-cf", archive_name, base_dir] - spawn (cmd, verbose=verbose, dry_run=dry_run) + spawn(cmd, verbose=verbose, dry_run=dry_run) if compress: - spawn ([compress] + compress_flags[compress] + [archive_name], - verbose=verbose, dry_run=dry_run) + spawn([compress] + compress_flags[compress] + [archive_name], + verbose=verbose, dry_run=dry_run) return archive_name + compress_ext[compress] else: return archive_name @@ -57,13 +57,13 @@ def make_tarball (base_name, base_dir, compress="gzip", def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): - """Create a zip file from all the files under 'base_dir'. The - output zip file will be named 'base_dir' + ".zip". Uses either the - InfoZIP "zip" utility (if installed and found on the default search - path) or the "zipfile" Python module (if available). If neither - tool is available, raises DistutilsExecError. Returns the name - of the output zip file.""" - + """Create a zip file from all the files under 'base_dir'. The output + zip file will be named 'base_dir' + ".zip". Uses either the InfoZIP + "zip" utility (if installed and found on the default search path) or + the "zipfile" Python module (if available). If neither tool is + available, raises DistutilsExecError. Returns the name of the output + zip file. + """ # This initially assumed the Unix 'zip' utility -- but # apparently InfoZIP's zip.exe works the same under Windows, so # no changes needed! @@ -71,8 +71,8 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): zip_filename = base_name + ".zip" mkpath(os.path.dirname(zip_filename), verbose=verbose, dry_run=dry_run) try: - spawn (["zip", "-rq", zip_filename, base_dir], - verbose=verbose, dry_run=dry_run) + spawn(["zip", "-rq", zip_filename, base_dir], + verbose=verbose, dry_run=dry_run) except DistutilsExecError: # XXX really should distinguish between "couldn't find @@ -96,14 +96,14 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): def visit (z, dirname, names): for name in names: path = os.path.normpath(os.path.join(dirname, name)) - if os.path.isfile (path): - z.write (path, path) + if os.path.isfile(path): + z.write(path, path) if not dry_run: - z = zipfile.ZipFile (zip_filename, "wb", - compression=zipfile.ZIP_DEFLATED) + z = zipfile.ZipFile(zip_filename, "wb", + compression=zipfile.ZIP_DEFLATED) - os.path.walk (base_dir, visit, z) + os.path.walk(base_dir, visit, z) z.close() return zip_filename @@ -143,9 +143,9 @@ def make_archive (base_name, format, if root_dir is not None: if verbose: print "changing into '%s'" % root_dir - base_name = os.path.abspath (base_name) + base_name = os.path.abspath(base_name) if not dry_run: - os.chdir (root_dir) + os.chdir(root_dir) if base_dir is None: base_dir = os.curdir @@ -161,12 +161,12 @@ def make_archive (base_name, format, func = format_info[0] for (arg,val) in format_info[1]: kwargs[arg] = val - filename = apply (func, (base_name, base_dir), kwargs) + filename = apply(func, (base_name, base_dir), kwargs) if root_dir is not None: if verbose: print "changing back to '%s'" % save_cwd - os.chdir (save_cwd) + os.chdir(save_cwd) return filename diff --git a/dep_util.py b/dep_util.py index 97812eda..4b93ed02 100644 --- a/dep_util.py +++ b/dep_util.py @@ -14,14 +14,13 @@ from distutils.errors import DistutilsFileError def newer (source, target): """Return true if 'source' exists and is more recently modified than - 'target', or if 'source' exists and 'target' doesn't. Return - false if both exist and 'target' is the same age or younger than - 'source'. Raise DistutilsFileError if 'source' does not - exist.""" - - if not os.path.exists (source): + 'target', or if 'source' exists and 'target' doesn't. Return false if + both exist and 'target' is the same age or younger than 'source'. + Raise DistutilsFileError if 'source' does not exist. + """ + if not os.path.exists(source): raise DistutilsFileError, "file '%s' does not exist" % source - if not os.path.exists (target): + if not os.path.exists(target): return 1 from stat import ST_MTIME @@ -35,20 +34,20 @@ def newer (source, target): def newer_pairwise (sources, targets): """Walk two filename lists in parallel, testing if each source is newer - than its corresponding target. Return a pair of lists (sources, - targets) where source is newer than target, according to the - semantics of 'newer()'.""" - - if len (sources) != len (targets): + than its corresponding target. Return a pair of lists (sources, + targets) where source is newer than target, according to the semantics + of 'newer()'. + """ + if len(sources) != len(targets): raise ValueError, "'sources' and 'targets' must be same length" # build a pair of lists (sources, targets) where source is newer n_sources = [] n_targets = [] - for i in range (len (sources)): - if newer (sources[i], targets[i]): - n_sources.append (sources[i]) - n_targets.append (targets[i]) + for i in range(len(sources)): + if newer(sources[i], targets[i]): + n_sources.append(sources[i]) + n_targets.append(targets[i]) return (n_sources, n_targets) @@ -56,20 +55,20 @@ def newer_pairwise (sources, targets): def newer_group (sources, target, missing='error'): - """Return true if 'target' is out-of-date with respect to any - file listed in 'sources'. In other words, if 'target' exists and - is newer than every file in 'sources', return false; otherwise - return true. 'missing' controls what we do when a source file is - missing; the default ("error") is to blow up with an OSError from - inside 'stat()'; if it is "ignore", we silently drop any missing - source files; if it is "newer", any missing source files make us - assume that 'target' is out-of-date (this is handy in "dry-run" - mode: it'll make you pretend to carry out commands that wouldn't - work because inputs are missing, but that doesn't matter because - you're not actually going to run the commands).""" - + """Return true if 'target' is out-of-date with respect to any file + listed in 'sources'. In other words, if 'target' exists and is newer + than every file in 'sources', return false; otherwise return true. + 'missing' controls what we do when a source file is missing; the + default ("error") is to blow up with an OSError from inside 'stat()'; + if it is "ignore", we silently drop any missing source files; if it is + "newer", any missing source files make us assume that 'target' is + out-of-date (this is handy in "dry-run" mode: it'll make you pretend to + carry out commands that wouldn't work because inputs are missing, but + that doesn't matter because you're not actually going to run the + commands). + """ # If the target doesn't even exist, then it's definitely out-of-date. - if not os.path.exists (target): + if not os.path.exists(target): return 1 # Otherwise we have to find out the hard way: if *any* source file @@ -77,9 +76,9 @@ def newer_group (sources, target, missing='error'): # we can immediately return true. If we fall through to the end # of the loop, then 'target' is up-to-date and we return false. from stat import ST_MTIME - target_mtime = os.stat (target)[ST_MTIME] + target_mtime = os.stat(target)[ST_MTIME] for source in sources: - if not os.path.exists (source): + if not os.path.exists(source): if missing == 'error': # blow up when we stat() the file pass elif missing == 'ignore': # missing source dropped from @@ -102,13 +101,13 @@ def newer_group (sources, target, missing='error'): def make_file (src, dst, func, args, verbose=0, update_message=None, noupdate_message=None): """Makes 'dst' from 'src' (both filenames) by calling 'func' with - 'args', but only if it needs to: i.e. if 'dst' does not exist or - 'src' is newer than 'dst'.""" - - if newer (src, dst): + 'args', but only if it needs to: i.e. if 'dst' does not exist or 'src' + is newer than 'dst'. + """ + if newer(src, dst): if verbose and update_message: print update_message - apply (func, args) + apply(func, args) else: if verbose and noupdate_message: print noupdate_message -- cgit v1.2.1 From 60381acee5ec5b115938eff6e66cb4277ec2a84d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 02:51:09 +0000 Subject: Bump version to 0.9.3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 34f55a2f..b2b9b569 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.9.3pre" +__version__ = "0.9.3" -- cgit v1.2.1 From f2886a0174116e2d8dfd3a5275de79233b5481b4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 27 Sep 2000 00:15:37 +0000 Subject: Fix '_set_command_options()' so it only calls 'strtobool()' on strings (was crashing on any boolean command-line option!). --- dist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dist.py b/dist.py index 91b820e9..92d390f7 100644 --- a/dist.py +++ b/dist.py @@ -755,9 +755,10 @@ class Distribution: neg_opt = {} try: - if neg_opt.has_key(option): + is_string = type(value) is StringType + if neg_opt.has_key(option) and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) - elif option in bool_opts: + elif option in bool_opts and is_string: setattr(command_obj, option, strtobool(value)) elif hasattr(command_obj, option): setattr(command_obj, option, value) -- cgit v1.2.1 From 3baf36cad7c938199bb27b95d25bcb5db2bb6cae Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 27 Sep 2000 00:17:08 +0000 Subject: Bump version to 0.9.4. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b2b9b569..6113dcf8 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.9.3" +__version__ = "0.9.4" -- cgit v1.2.1 From 5937fb6402fd3826b37192fc61b0869f7c3e441d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 27 Sep 2000 02:08:14 +0000 Subject: Big patch from Rene Liebscher to simplify the CCompiler API and implementations. Details: * replace 'link_shared_object()', 'link_shared_lib()', and 'link_executable()' with 'link()', which is (roughly) the union of the three methods it replaces * in all implementation classes (UnixCCompiler, MSVCCompiler, etc.), ditch the old 'link_*()' methods and replace them with 'link()' * in the abstract base class (CCompiler), add the old 'link_*()' methods as wrappers around the new 'link()' (they also print a warning of the deprecated interface) Also increases consistency between MSVCCompiler and BCPPCompiler, hopefully to make it easier to factor out the mythical WindowsCCompiler class. Details: * use 'self.linker' instead of 'self.link' * add ability to compile resource files to BCPPCompiler * added (redundant?) 'object_filename()' method to BCPPCompiler * only generate a .def file if 'export_symbols' defined --- bcppcompiler.py | 263 +++++++++++++++++++++++++---------------------------- ccompiler.py | 111 ++++++++++++++-------- cygwinccompiler.py | 230 ++++++++++++++++++++++++++++++++-------------- msvccompiler.py | 135 +++++++-------------------- unixccompiler.py | 100 ++++---------------- 5 files changed, 409 insertions(+), 430 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 2b73b12f..bb9557fb 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -63,7 +63,7 @@ class BCPPCompiler(CCompiler) : # indicate their installation locations. self.cc = "bcc32.exe" - self.link = "ilink32.exe" + self.linker = "ilink32.exe" self.lib = "tlib.exe" self.preprocess_options = None @@ -73,6 +73,8 @@ class BCPPCompiler(CCompiler) : self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x'] self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x'] self.ldflags_static = [] + self.ldflags_exe = ['/Gn', '/q', '/x'] + self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r'] # -- Worker methods ------------------------------------------------ @@ -108,16 +110,33 @@ class BCPPCompiler(CCompiler) : if skip_sources[src]: self.announce ("skipping %s (%s up-to-date)" % (src, obj)) else: + src = os.path.normpath(src) + obj = os.path.normpath(obj) + self.mkpath(os.path.dirname(obj)) + + if ext == '.res': + # This is already a binary file -- skip it. + continue # the 'for' loop + if ext == '.rc': + # This needs to be compiled to a .res file -- do it now. + try: + self.spawn (["brcc32", "-fo", obj, src]) + except DistutilsExecError, msg: + raise CompileError, msg + continue # the 'for' loop + + # The next two are both for the real compiler. if ext in self._c_extensions: input_opt = "" elif ext in self._cpp_extensions: input_opt = "-P" + else: + # Unknown file type -- no extra options. The compiler + # will probably fail, but let it just in case this is a + # file the compiler recognizes even if we don't. + input_opt = "" - src = os.path.normpath(src) - obj = os.path.normpath(obj) - output_opt = "-o" + obj - self.mkpath(os.path.dirname(obj)) # Compiler command line syntax is: "bcc32 [options] file(s)". # Note that the source file names must appear at the end of @@ -163,45 +182,20 @@ class BCPPCompiler(CCompiler) : # create_static_lib () - - def link_shared_lib (self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None): - - self.link_shared_object (objects, - self.shared_library_name(output_libname), - output_dir=output_dir, - libraries=libraries, - library_dirs=library_dirs, - runtime_library_dirs=runtime_library_dirs, - export_symbols=export_symbols, - debug=debug, - extra_preargs=extra_preargs, - extra_postargs=extra_postargs, - build_temp=build_temp) - - def link_shared_object (self, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None): + def link (self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): # XXX this ignores 'build_temp'! should follow the lead of # msvccompiler.py @@ -213,45 +207,61 @@ class BCPPCompiler(CCompiler) : if runtime_library_dirs: self.warn ("I don't know what to do with 'runtime_library_dirs': " + str (runtime_library_dirs)) - + if output_dir is not None: output_filename = os.path.join (output_dir, output_filename) if self._need_link (objects, output_filename): - if debug: - ld_args = self.ldflags_shared_debug[:] + # Figure out linker args based on type of target. + if target_desc == CCompiler.EXECUTABLE: + startup_obj = 'c0w32' + if debug: + ld_args = self.ldflags_exe_debug[:] + else: + ld_args = self.ldflags_exe[:] else: - ld_args = self.ldflags_shared[:] + startup_obj = 'c0d32' + if debug: + ld_args = self.ldflags_shared_debug[:] + else: + ld_args = self.ldflags_shared[:] + # Create a temporary exports file for use by the linker - head, tail = os.path.split (output_filename) - modname, ext = os.path.splitext (tail) - temp_dir = os.path.dirname(objects[0]) # preserve tree structure - def_file = os.path.join (temp_dir, '%s.def' % modname) - contents = ['EXPORTS'] - for sym in (export_symbols or []): - contents.append(' %s=_%s' % (sym, sym)) - self.execute(write_file, (def_file, contents), - "writing %s" % def_file) + if export_symbols is None: + def_file = '' + else: + head, tail = os.path.split (output_filename) + modname, ext = os.path.splitext (tail) + temp_dir = os.path.dirname(objects[0]) # preserve tree structure + def_file = os.path.join (temp_dir, '%s.def' % modname) + contents = ['EXPORTS'] + for sym in (export_symbols or []): + contents.append(' %s=_%s' % (sym, sym)) + self.execute(write_file, (def_file, contents), + "writing %s" % def_file) # Borland C++ has problems with '/' in paths - objects = map(os.path.normpath, objects) - startup_obj = 'c0d32' - objects.insert(0, startup_obj) - - # either exchange python15.lib in the python libs directory against - # a Borland-like one, or create one with name bcpp_python15.lib - # there and remove the pragmas from config.h - libraries.append ('import32') - libraries.append ('cw32mt') - - # Start building command line flags and options. - + objects2 = map(os.path.normpath, objects) + # split objects in .obj and .res files + # Borland C++ needs them at different positions in the command line + objects = [startup_obj] + resources = [] + for file in objects2: + (base, ext) = os.path.splitext(os.path.normcase(file)) + if ext == '.res': + resources.append(file) + else: + objects.append(file) + + for l in library_dirs: ld_args.append("/L%s" % os.path.normpath(l)) - - ld_args.extend(objects) # list of object files + ld_args.append("/L.") # we sometimes use relative paths + + # list of object files + ld_args.extend(objects) # XXX the command-line syntax for Borland C++ is a bit wonky; # certain filenames are jammed together in one big string, but @@ -263,14 +273,14 @@ class BCPPCompiler(CCompiler) : # because 'spawn()' would quote any filenames with spaces in # them. Arghghh!. Apparently it works fine as coded... - # name of dll file + # name of dll/exe file ld_args.extend([',',output_filename]) # no map file and start libraries ld_args.append(',,') for lib in libraries: # see if we find it and if there is a bcpp specific lib - # (bcpp_xxx.lib) + # (xxx_bcpp.lib) libfile = self.find_library_file(library_dirs, lib, debug) if libfile is None: ld_args.append(lib) @@ -279,8 +289,17 @@ class BCPPCompiler(CCompiler) : else: # full name which prefers bcpp_xxx.lib over xxx.lib ld_args.append(libfile) + + # some default libraries + ld_args.append ('import32') + ld_args.append ('cw32mt') + # def file for export symbols ld_args.extend([',',def_file]) + # add resource files + ld_args.append(',') + ld_args.extend(resources) + if extra_preargs: ld_args[:0] = extra_preargs @@ -289,88 +308,24 @@ class BCPPCompiler(CCompiler) : self.mkpath (os.path.dirname (output_filename)) try: - self.spawn ([self.link] + ld_args) + self.spawn ([self.linker] + ld_args) except DistutilsExecError, msg: raise LinkError, msg else: self.announce ("skipping %s (up-to-date)" % output_filename) - # link_shared_object () - - - def link_executable (self, - objects, - output_progname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): - - (objects, output_dir) = self._fix_object_args (objects, output_dir) - (libraries, library_dirs, runtime_library_dirs) = \ - self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) - - if runtime_library_dirs: - self.warn ("I don't know what to do with 'runtime_library_dirs': " - + str (runtime_library_dirs)) - - lib_opts = gen_lib_options (self, - library_dirs, runtime_library_dirs, - libraries) - output_filename = output_progname + self.exe_extension - if output_dir is not None: - output_filename = os.path.join (output_dir, output_filename) - - if self._need_link (objects, output_filename): - - if debug: - ldflags = self.ldflags_shared_debug[1:] - else: - ldflags = self.ldflags_shared[1:] - - ld_args = ldflags + lib_opts + \ - objects + ['/OUT:' + output_filename] - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend (extra_postargs) - - self.mkpath (os.path.dirname (output_filename)) - try: - self.spawn ([self.link] + ld_args) - except DistutilsExecError, msg: - raise LinkError, msg - else: - self.announce ("skipping %s (up-to-date)" % output_filename) - + # link () # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option (self, dir): - return "-L" + dir - - def runtime_library_dir_option (self, dir): - raise DistutilsPlatformError, \ - ("don't know how to set runtime library search path " - "for Borland C++") - - def library_option (self, lib): - return self.library_filename (lib) def find_library_file (self, dirs, lib, debug=0): # List of effective library names to try, in order of preference: - # bcpp_xxx.lib is better than xxx.lib + # xxx_bcpp.lib is better than xxx.lib # and xxx_d.lib is better than xxx.lib if debug is set # - # The "bcpp_" prefix is to handle a Python installation for people + # The "_bcpp" suffix is to handle a Python installation for people # with multiple compilers (primarily Distutils hackers, I suspect # ;-). The idea is they'd have one static library for each # compiler they care about, since (almost?) every Windows compiler @@ -390,3 +345,31 @@ class BCPPCompiler(CCompiler) : # Oops, didn't find it in *any* of 'dirs' return None + # overwrite the one from CCompiler to support rc and res-files + def object_filenames (self, + source_filenames, + strip_dir=0, + output_dir=''): + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + # use normcase to make sure '.rc' is really '.rc' and not '.RC' + (base, ext) = os.path.splitext (os.path.normcase(src_name)) + if ext not in (self.src_extensions + ['.rc','.res']): + raise UnknownFileError, \ + "unknown file type '%s' (from '%s')" % \ + (ext, src_name) + if strip_dir: + base = os.path.basename (base) + if ext == '.res': + # these can go unchanged + obj_names.append (os.path.join (output_dir, base + ext)) + elif ext == '.rc': + # these need to be compiled to .res-files + obj_names.append (os.path.join (output_dir, base + '.res')) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + # object_filenames () diff --git a/ccompiler.py b/ccompiler.py index ce3f2be6..97949060 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -561,24 +561,32 @@ class CCompiler: pass - def link_shared_lib (self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None): - """Link a bunch of stuff together to create a shared library file. - Similar semantics to 'create_static_lib()', with the addition of - other libraries to link against and directories to search for them. - Also, of course, the type and name of the generated file will - almost certainly be different, as will the program used to create - it. + # values for target_desc parameter in link() + SHARED_OBJECT = "shared_object" + SHARED_LIBRARY = "shared_library" + EXECUTABLE = "executable" + + def link (self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): + """Link a bunch of stuff together to create an executable or + shared library file. + + The "bunch of stuff" consists of the list of object files supplied + as 'objects'. 'output_filename' should be a filename. If + 'output_dir' is supplied, 'output_filename' is relative to it + (i.e. 'output_filename' can provide directory components if + needed). 'libraries' is a list of libraries to link against. These are library names, not filenames, since they're translated into @@ -610,7 +618,31 @@ class CCompiler: Raises LinkError on failure. """ - pass + raise NotImplementedError + + + # old methods, rewritten to use the new link() method. + + def link_shared_lib (self, + objects, + output_libname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): + self.warn("link_shared_lib(..) is deprecated, please " + "use link(CCompiler.SHARED_LIBRARY, ...) instead") + self.link(CCompiler.SHARED_LIBRARY, objects, + self.library_filename(output_libname, lib_type='shared'), + output_dir, + libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, + extra_preargs, extra_postargs, build_temp) def link_shared_object (self, @@ -625,16 +657,13 @@ class CCompiler: extra_preargs=None, extra_postargs=None, build_temp=None): - """Link a bunch of stuff together to create a shared object file. - Much like 'link_shared_lib()', except the output filename is - explicitly supplied as 'output_filename'. If 'output_dir' is - supplied, 'output_filename' is relative to it - (i.e. 'output_filename' can provide directory components if - needed). - - Raises LinkError on failure. - """ - pass + self.warn("link_shared_object(...) is deprecated, please " + "use link(CCompiler.SHARED_OBJECT,...) instead.") + self.link(CCompiler.SHARED_OBJECT, objects, + output_filename, output_dir, + libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, + extra_preargs, extra_postargs, build_temp) def link_executable (self, @@ -647,16 +676,12 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None): - """Link a bunch of stuff together to create a binary executable - file. The "bunch of stuff" is as for 'link_shared_lib()'. - 'output_progname' should be the base name of the executable - program--e.g. on Unix the same as the output filename, but on - DOS/Windows ".exe" will be appended. - - Raises LinkError on failure. - """ - pass - + self.warn("link_executable(...) is deprecated, please " + "use link(CCompiler.EXECUTABLE,...) instead.") + self.link (CCompiler.EXECUTABLE, objects, + self.executable_filename(output_progname), output_dir, + libraries, library_dirs, runtime_library_dirs, None, + debug, extra_preargs, extra_postargs, None) # -- Miscellaneous methods ----------------------------------------- @@ -756,6 +781,14 @@ class CCompiler: basename = os.path.basename (basename) return os.path.join (output_dir, basename + self.shared_lib_extension) + def executable_filename (self, + basename, + strip_dir=0, + output_dir=''): + if output_dir is None: output_dir = '' + if strip_dir: + basename = os.path.basename (basename) + return os.path.join(output_dir, basename + (self.exe_extension or '')) def library_filename (self, libname, diff --git a/cygwinccompiler.py b/cygwinccompiler.py index f547d540..5b06d3d7 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -39,14 +39,17 @@ cygwin in no-cygwin mode). # By specifying -static we force ld to link against the import libraries, # this is windows standard and there are normally not the necessary symbols # in the dlls. +# *** only the version of June 2000 shows these problems # created 2000/05/05, Rene Liebscher __revision__ = "$Id$" import os,sys,copy +from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file +from distutils.errors import DistutilsExecError, CompileError, UnknownFileError class CygwinCCompiler (UnixCCompiler): @@ -87,9 +90,9 @@ class CygwinCCompiler (UnixCCompiler): # same as the rest of binutils ( also ld ) # dllwrap 2.10.90 is buggy if self.ld_version >= "2.10.90": - self.linker = "gcc" + self.linker_dll = "gcc" else: - self.linker = "dllwrap" + self.linker_dll = "dllwrap" # Hard-code GCC because that's what this is all about. # XXX optimization, warnings etc. should be customizable. @@ -97,7 +100,7 @@ class CygwinCCompiler (UnixCCompiler): compiler_so='gcc -mcygwin -mdll -O -Wall', linker_exe='gcc -mcygwin', linker_so=('%s -mcygwin -mdll -static' % - self.linker)) + self.linker_dll)) # cygwin and mingw32 need different sets of libraries if self.gcc_version == "2.91.57": @@ -111,58 +114,108 @@ class CygwinCCompiler (UnixCCompiler): # __init__ () - def link_shared_object (self, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None): + # not much different of the compile method in UnixCCompiler, + # but we have to insert some lines in the middle of it, so + # we put here a adapted version of it. + # (If we would call compile() in the base class, it would do some + # initializations a second time, this is why all is done here.) + def compile (self, + sources, + output_dir=None, + macros=None, + include_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None): + + (output_dir, macros, include_dirs) = \ + self._fix_compile_args (output_dir, macros, include_dirs) + (objects, skip_sources) = self._prep_compile (sources, output_dir) + + # Figure out the options for the compiler command line. + pp_opts = gen_preprocess_options (macros, include_dirs) + cc_args = pp_opts + ['-c'] + if debug: + cc_args[:0] = ['-g'] + if extra_preargs: + cc_args[:0] = extra_preargs + if extra_postargs is None: + extra_postargs = [] + + # Compile all source files that weren't eliminated by + # '_prep_compile()'. + for i in range (len (sources)): + src = sources[i] ; obj = objects[i] + ext = (os.path.splitext (src))[1] + if skip_sources[src]: + self.announce ("skipping %s (%s up-to-date)" % (src, obj)) + else: + self.mkpath (os.path.dirname (obj)) + if ext == '.rc' or ext == '.res': + # gcc needs '.res' and '.rc' compiled to object files !!! + try: + self.spawn (["windres","-i",src,"-o",obj]) + except DistutilsExecError, msg: + raise CompileError, msg + else: # for other files use the C-compiler + try: + self.spawn (self.compiler_so + cc_args + + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg + + # Return *all* object filenames, not just the ones we just built. + return objects + + # compile () + + + def link (self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): # use separate copies, so we can modify the lists extra_preargs = copy.copy(extra_preargs or []) libraries = copy.copy(libraries or []) - + objects = copy.copy(objects or []) + # Additional libraries libraries.extend(self.dll_libraries) - - # we want to put some files in the same directory as the - # object files are, build_temp doesn't help much - - # where are the object files - temp_dir = os.path.dirname(objects[0]) - - # name of dll to give the helper files (def, lib, exp) the same name - (dll_name, dll_extension) = os.path.splitext( - os.path.basename(output_filename)) - - # generate the filenames for these files - def_file = None # this will be done later, if necessary - exp_file = os.path.join(temp_dir, dll_name + ".exp") - lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") - - #extra_preargs.append("--verbose") - if self.linker == "dllwrap": - extra_preargs.extend([#"--output-exp",exp_file, - "--output-lib",lib_file, - ]) - else: - # doesn't work: bfd_close build\...\libfoo.a: Invalid operation - extra_preargs.extend([#"-Wl,--out-implib,%s" % lib_file, - ]) - - # check what we got in export_symbols - if export_symbols is not None: - # Make .def file - # (It would probably better to check if we really need this, + + # handle export symbols by creating a def-file + # with executables this only works with gcc/ld as linker + if ((export_symbols is not None) and + (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): + # (The linker doesn't do anything if output is up-to-date. + # So it would probably better to check if we really need this, # but for this we had to insert some unchanged parts of # UnixCCompiler, and this is not what we want.) + + # we want to put some files in the same directory as the + # object files are, build_temp doesn't help much + # where are the object files + temp_dir = os.path.dirname(objects[0]) + # name of dll to give the helper files the same base name + (dll_name, dll_extension) = os.path.splitext( + os.path.basename(output_filename)) + + # generate the filenames for these files def_file = os.path.join(temp_dir, dll_name + ".def") + exp_file = os.path.join(temp_dir, dll_name + ".exp") + lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") + + # Generate .def file contents = [ "LIBRARY %s" % os.path.basename(output_filename), "EXPORTS"] @@ -171,35 +224,78 @@ class CygwinCCompiler (UnixCCompiler): self.execute(write_file, (def_file, contents), "writing %s" % def_file) - if def_file: - if self.linker == "dllwrap": + # next add options for def-file and to creating import libraries + + # dllwrap uses different options than gcc/ld + if self.linker_dll == "dllwrap": + extra_preargs.extend([#"--output-exp",exp_file, + "--output-lib",lib_file, + ]) # for dllwrap we have to use a special option - extra_preargs.append("--def") - # for gcc/ld it is specified as any other object file - extra_preargs.append(def_file) + extra_preargs.extend(["--def", def_file]) + # we use gcc/ld here and can be sure ld is >= 2.9.10 + else: + # doesn't work: bfd_close build\...\libfoo.a: Invalid operation + #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) + # for gcc/ld the def-file is specified as any other object files + objects.append(def_file) + + #end: if ((export_symbols is not None) and + # (target_desc <> self.EXECUTABLE or self.linker_dll == "gcc")): # who wants symbols and a many times larger output file # should explicitly switch the debug mode on # otherwise we let dllwrap/ld strip the output file - # (On my machine unstripped_file = stripped_file + 254KB - # 10KB < stripped_file < ??100KB ) + # (On my machine: 10KB < stripped_file < ??100KB + # unstripped_file = stripped_file + XXX KB + # ( XXX=254 for a typical python extension)) if not debug: extra_preargs.append("-s") - UnixCCompiler.link_shared_object(self, - objects, - output_filename, - output_dir, - libraries, - library_dirs, - runtime_library_dirs, - None, # export_symbols, we do this in our def-file - debug, - extra_preargs, - extra_postargs, - build_temp) + UnixCCompiler.link(self, + target_desc, + objects, + output_filename, + output_dir, + libraries, + library_dirs, + runtime_library_dirs, + None, # export_symbols, we do this in our def-file + debug, + extra_preargs, + extra_postargs, + build_temp) - # link_shared_object () + # link () + + # -- Miscellaneous methods ----------------------------------------- + + # overwrite the one from CCompiler to support rc and res-files + def object_filenames (self, + source_filenames, + strip_dir=0, + output_dir=''): + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + # use normcase to make sure '.rc' is really '.rc' and not '.RC' + (base, ext) = os.path.splitext (os.path.normcase(src_name)) + if ext not in (self.src_extensions + ['.rc','.res']): + raise UnknownFileError, \ + "unknown file type '%s' (from '%s')" % \ + (ext, src_name) + if strip_dir: + base = os.path.basename (base) + if ext == '.res' or ext == '.rc': + # these need to be compiled to object files + obj_names.append (os.path.join (output_dir, + base + ext + self.obj_extension)) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + # object_filenames () # class CygwinCCompiler @@ -227,7 +323,7 @@ class Mingw32CCompiler (CygwinCCompiler): compiler_so='gcc -mno-cygwin -mdll -O -Wall', linker_exe='gcc -mno-cygwin', linker_so='%s -mno-cygwin -mdll -static %s' - % (self.linker, entry_point)) + % (self.linker_dll, entry_point)) # Maybe we should also append -mthreads, but then the finished # dlls need another dll (mingwm10.dll see Mingw32 docs) # (-mthreads: Support thread-safe exception handling on `Mingw32') diff --git a/msvccompiler.py b/msvccompiler.py index ea58a79c..0325b485 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -205,7 +205,7 @@ class MSVCCompiler (CCompiler) : version = versions[0] # highest version self.cc = find_exe("cl.exe", version) - self.link = find_exe("link.exe", version) + self.linker = find_exe("link.exe", version) self.lib = find_exe("lib.exe", version) self.rc = find_exe("rc.exe", version) # resource compiler self.mc = find_exe("mc.exe", version) # message compiler @@ -221,7 +221,7 @@ class MSVCCompiler (CCompiler) : else: # devstudio not found in the registry self.cc = "cl.exe" - self.link = "link.exe" + self.linker = "link.exe" self.lib = "lib.exe" self.rc = "rc.exe" self.mc = "mc.exe" @@ -396,45 +396,19 @@ class MSVCCompiler (CCompiler) : # create_static_lib () - - def link_shared_lib (self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None): - - self.link_shared_object (objects, - self.shared_library_name(output_libname), - output_dir=output_dir, - libraries=libraries, - library_dirs=library_dirs, - runtime_library_dirs=runtime_library_dirs, - export_symbols=export_symbols, - debug=debug, - extra_preargs=extra_preargs, - extra_postargs=extra_postargs, - build_temp=build_temp) - - - def link_shared_object (self, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None): + def link (self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): (objects, output_dir) = self._fix_object_args (objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ @@ -452,10 +426,16 @@ class MSVCCompiler (CCompiler) : if self._need_link (objects, output_filename): - if debug: - ldflags = self.ldflags_shared_debug + if target_desc == CCompiler.EXECUTABLE: + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] else: - ldflags = self.ldflags_shared + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared export_opts = [] for sym in (export_symbols or []): @@ -469,12 +449,13 @@ class MSVCCompiler (CCompiler) : # needed! Make sure they are generated in the temporary build # directory. Since they have different names for debug and release # builds, they can go into the same directory. - (dll_name, dll_ext) = os.path.splitext( - os.path.basename(output_filename)) - implib_file = os.path.join( - os.path.dirname(objects[0]), - self.library_filename(dll_name)) - ld_args.append ('/IMPLIB:' + implib_file) + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + os.path.dirname(objects[0]), + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) if extra_preargs: ld_args[:0] = extra_preargs @@ -483,66 +464,16 @@ class MSVCCompiler (CCompiler) : self.mkpath (os.path.dirname (output_filename)) try: - self.spawn ([self.link] + ld_args) + self.spawn ([self.linker] + ld_args) except DistutilsExecError, msg: raise LinkError, msg else: self.announce ("skipping %s (up-to-date)" % output_filename) - # link_shared_object () + # link () - def link_executable (self, - objects, - output_progname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): - - (objects, output_dir) = self._fix_object_args (objects, output_dir) - (libraries, library_dirs, runtime_library_dirs) = \ - self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) - - if runtime_library_dirs: - self.warn ("I don't know what to do with 'runtime_library_dirs': " - + str (runtime_library_dirs)) - - lib_opts = gen_lib_options (self, - library_dirs, runtime_library_dirs, - libraries) - output_filename = output_progname + self.exe_extension - if output_dir is not None: - output_filename = os.path.join (output_dir, output_filename) - - if self._need_link (objects, output_filename): - - if debug: - ldflags = self.ldflags_shared_debug[1:] - else: - ldflags = self.ldflags_shared[1:] - - ld_args = ldflags + lib_opts + \ - objects + ['/OUT:' + output_filename] - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend (extra_postargs) - - self.mkpath (os.path.dirname (output_filename)) - try: - self.spawn ([self.link] + ld_args) - except DistutilsExecError, msg: - raise LinkError, msg - else: - self.announce ("skipping %s (up-to-date)" % output_filename) - - # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in # ccompiler.py. diff --git a/unixccompiler.py b/unixccompiler.py index ff0341a5..f7eb93ae 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -190,45 +190,19 @@ class UnixCCompiler (CCompiler): # create_static_lib () - def link_shared_lib (self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None): - - self.link_shared_object( - objects, - self.library_filename(output_libname, lib_type='shared'), - output_dir, - libraries, - library_dirs, - runtime_library_dirs, - export_symbols, - debug, - extra_preargs, - extra_postargs, - build_temp) - - - def link_shared_object (self, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None): + def link (self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): (objects, output_dir) = self._fix_object_args(objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ @@ -253,54 +227,16 @@ class UnixCCompiler (CCompiler): ld_args.extend(extra_postargs) self.mkpath(os.path.dirname(output_filename)) try: - self.spawn(self.linker_so + ld_args) + if target_desc == CCompiler.EXECUTABLE: + self.spawn(self.linker_exe + ld_args) + else: + self.spawn(self.linker_so + ld_args) except DistutilsExecError, msg: raise LinkError, msg else: self.announce("skipping %s (up-to-date)" % output_filename) - # link_shared_object () - - - def link_executable (self, - objects, - output_progname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): - - (objects, output_dir) = self._fix_object_args(objects, output_dir) - (libraries, library_dirs, runtime_library_dirs) = \ - self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) - - lib_opts = gen_lib_options(self, - library_dirs, runtime_library_dirs, - libraries) - output_filename = output_progname # Unix-ism! - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - ld_args = objects + self.objects + lib_opts + ['-o', output_filename] - if debug: - ld_args[:0] = ['-g'] - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - self.mkpath(os.path.dirname(output_filename)) - try: - self.spawn(self.linker_exe + ld_args) - except DistutilsExecError, msg: - raise LinkError, msg - else: - self.announce("skipping %s (up-to-date)" % output_filename) - - # link_executable () + # link () # -- Miscellaneous methods ----------------------------------------- -- cgit v1.2.1 From a50a92cb127350ef11b3bf567d9724e61ce063af Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 27 Sep 2000 02:24:21 +0000 Subject: Remove deprecation warnings on old 'link_*()' methods, ie. they're not deprecated after all. But now they're only implemented once, instead of N times. --- ccompiler.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 97949060..b10ee67e 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -621,7 +621,7 @@ class CCompiler: raise NotImplementedError - # old methods, rewritten to use the new link() method. + # Old 'link_*()' methods, rewritten to use the new 'link()' method. def link_shared_lib (self, objects, @@ -635,8 +635,6 @@ class CCompiler: extra_preargs=None, extra_postargs=None, build_temp=None): - self.warn("link_shared_lib(..) is deprecated, please " - "use link(CCompiler.SHARED_LIBRARY, ...) instead") self.link(CCompiler.SHARED_LIBRARY, objects, self.library_filename(output_libname, lib_type='shared'), output_dir, @@ -657,8 +655,6 @@ class CCompiler: extra_preargs=None, extra_postargs=None, build_temp=None): - self.warn("link_shared_object(...) is deprecated, please " - "use link(CCompiler.SHARED_OBJECT,...) instead.") self.link(CCompiler.SHARED_OBJECT, objects, output_filename, output_dir, libraries, library_dirs, runtime_library_dirs, @@ -676,12 +672,10 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None): - self.warn("link_executable(...) is deprecated, please " - "use link(CCompiler.EXECUTABLE,...) instead.") - self.link (CCompiler.EXECUTABLE, objects, - self.executable_filename(output_progname), output_dir, - libraries, library_dirs, runtime_library_dirs, None, - debug, extra_preargs, extra_postargs, None) + self.link(CCompiler.EXECUTABLE, objects, + self.executable_filename(output_progname), output_dir, + libraries, library_dirs, runtime_library_dirs, None, + debug, extra_preargs, extra_postargs, None) # -- Miscellaneous methods ----------------------------------------- -- cgit v1.2.1 From 117c5cbfe52d2dfc01e823e96051f24f80f60038 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 27 Sep 2000 02:26:57 +0000 Subject: Bump version to 1.0pre. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6113dcf8..23315cde 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "0.9.4" +__version__ = "1.0pre" -- cgit v1.2.1 From b0ce6a9888ab8fc85326e696c0e114dddfdeaeaa Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 28 Sep 2000 19:28:35 +0000 Subject: Removed the implib_dir instance variable because it is unused. Removed get_ext_libname() because it is unused. Fixed get_libraries() to append an '_d' to the python debug import library. If MSVC is used, do not add 'pythonxx.lib' to the list of libraries, because this is handled better by a pragma in config.h. This should fix bug #115595, but it needs some more testing. --- command/build_ext.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 7fdfd145..9147c3d0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -158,7 +158,6 @@ class build_ext (Command): # also Python's library directory must be appended to library_dirs if os.name == 'nt': self.library_dirs.append (os.path.join(sys.exec_prefix, 'libs')) - self.implib_dir = self.build_temp if self.debug: self.build_temp = os.path.join (self.build_temp, "Debug") else: @@ -543,15 +542,6 @@ class build_ext (Command): return apply (os.path.join, ext_path) + '_d' + so_ext return apply (os.path.join, ext_path) + so_ext - def get_ext_libname (self, ext_name): - # create a filename for the (unneeded) lib-file. - # extensions in debug_mode are named 'module_d.pyd' under windows - ext_path = string.split (ext_name, '.') - if os.name == 'nt' and self.debug: - return apply (os.path.join, ext_path) + '_d.lib' - return apply (os.path.join, ext_path) + '.lib' - - def get_export_symbols (self, ext): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not @@ -573,9 +563,15 @@ class build_ext (Command): # is redundant, since the library is mentioned in a pragma in # config.h that MSVC groks. The other Windows compilers all seem # to need it mentioned explicitly, though, so that's what we do. - if sys.platform == "win32": - pythonlib = ("python%d%d" % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # Append '_d' to the python import library on debug builds. + from distutils.msvccompiler import MSVCCompiler + if sys.platform == "win32" and \ + not isinstance(self.compiler, MSVCCompiler): + template = "python%d%d" + if self.debug: + template = template + '_d' + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] -- cgit v1.2.1 From 96979143f471579f54c80afc1aedf0831a98e58a Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 29 Sep 2000 11:36:55 +0000 Subject: Removed the extra_dirs and path_file metadata options. They are unneeded: All this stuff is already done by the install command which is run by bdist_wininst. One bug has been fixed: The root of the fake install tree is install.install_purelib, not install.install_lib! They are different if the extra_path option is used in the setup function. Rebuild after the changes to wininst.exe. --- command/bdist_wininst.py | 530 +++++++++++++++++++++++------------------------ 1 file changed, 260 insertions(+), 270 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 16dd8022..3251bac0 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -28,7 +28,8 @@ class bdist_wininst (Command): ('no-target-compile', 'c', "do not compile .py to .pyc on the target system"), ('no-target-optimize', 'o', - "do not compile .py to .pyo (optimized) on the target system"), + "do not compile .py to .pyo (optimized)" + "on the target system"), ('dist-dir=', 'd', "directory to put final built distributions in"), ] @@ -92,13 +93,6 @@ class bdist_wininst (Command): self.announce ("installing to %s" % self.bdist_dir) install.ensure_finalized() - - # save the path_file and extra_dirs options - # created by the install command if an extra_path - # argument has been supplied - self.extra_dirs = install.extra_dirs - self.path_file = install.path_file - install.run() # And make an archive relative to the root of the @@ -108,8 +102,8 @@ class bdist_wininst (Command): "%s.win32" % fullname) # Our archive MUST be relative to sys.prefix, which is the - # same as install_lib in the 'nt' scheme. - root_dir = os.path.normpath (install.install_lib) + # same as install_purelib in the 'nt' scheme. + root_dir = os.path.normpath (install.install_purelib) # Sanity check: Make sure everything is included for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): @@ -161,10 +155,6 @@ class bdist_wininst (Command): lines.append ("target_optimize=%d" % (not self.no_target_optimize)) if self.target_version: lines.append ("target_version=%s" % self.target_version) - if (self.path_file): - lines.append ("path_file=%s" % self.path_file) - if (self.extra_dirs): - lines.append ("extra_dirs=%s" % self.extra_dirs) title = self.distribution.get_fullname() lines.append ("title=%s" % repr (title)[1:-1]) @@ -227,271 +217,271 @@ if __name__ == '__main__': EXEDATA = """\ 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 +HoPu/BHbEcl1IEEB23UHix6D7vwR2xHJAdtz73UJix6D7vwR23Pkg8ECgf0A8///g9EBjRQvg/38 +dg+KAkKIB0dJdffpY////5CLAoPCBIkHg8cEg+kEd/EBz+lM////Xon3uZAAAACKB0cs6DwBd/eA +PwF18osHil8EZsHoCMHAEIbEKfiA6+gB8IkHg8cFidji2Y2+ALAAAIsHCcB0PItfBI2EMDDRAAAB +81CDxwj/lrzRAACVigdHCMB03In5V0jyrlX/lsDRAAAJwHQHiQODwwTr4f+WxNEAAGHpmHj//wAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -506,8 +496,8 @@ AAAAABviAADc4QAAAAAAAAAAAAAAAAAAJeIAAOThAAAAAAAAAAAAAAAAAAAw4gAA7OEAAAAAAAAA AAAAAAAAAAAAAAAAAAAAOuIAAEjiAABY4gAAAAAAAGbiAAAAAAAAdOIAAAAAAACE4gAAAAAAAI7i AAAAAAAAlOIAAAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIuZGxsAENPTUNUTDMyLmRsbABHREkz Mi5kbGwATVNWQ1JULmRsbABVU0VSMzIuZGxsAABMb2FkTGlicmFyeUEAAEdldFByb2NBZGRyZXNz -AABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEAAFRleHRPdXRBAABm -cmVlAABFbmRQYWludAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEAAFRleHRPdXRBAABl +eGl0AABFbmRQYWludAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -- cgit v1.2.1 From 2ab90e20a50ed93e808c40772d010c8427c283c6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:05:37 +0000 Subject: Moved some things around for better organization. --- command/install.py | 62 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/command/install.py b/command/install.py index 4ad652d9..8c6aa1cf 100644 --- a/command/install.py +++ b/command/install.py @@ -173,6 +173,13 @@ class install (Command): self.record = None + + # -- Option finalizing methods ------------------------------------- + # (This is rather more involved than for most commands, + # because this is where the policy for installing third- + # party Python modules on various platforms given a wide + # array of user input is decided. Yes, it's quite complex!) + def finalize_options (self): # This method (and its pliant slaves, like 'finalize_unix()', @@ -450,6 +457,8 @@ class install (Command): setattr(self, attr, change_root(self.root, getattr(self, attr))) + # -- Command execution methods ------------------------------------- + def run (self): # Obviously have to build before we can install @@ -486,24 +495,18 @@ class install (Command): # run () + def create_path_file (self): + filename = os.path.join (self.install_libbase, + self.path_file + ".pth") + if self.install_path_file: + self.execute (write_file, + (filename, [self.extra_dirs]), + "creating %s" % filename) + else: + self.warn("path file '%s' not created" % filename) - # -- Predicates for sub-command list ------------------------------- - - def has_lib (self): - """Return true if the current distribution has any Python - modules to install.""" - return (self.distribution.has_pure_modules() or - self.distribution.has_ext_modules()) - - def has_headers (self): - return self.distribution.has_headers() - - def has_scripts (self): - return self.distribution.has_scripts() - - def has_data (self): - return self.distribution.has_data_files() + # -- Reporting methods --------------------------------------------- def get_outputs (self): # This command doesn't have any outputs of its own, so just @@ -515,7 +518,6 @@ class install (Command): return outputs - def get_inputs (self): # XXX gee, this looks familiar ;-( inputs = [] @@ -526,15 +528,23 @@ class install (Command): return inputs - def create_path_file (self): - filename = os.path.join (self.install_libbase, - self.path_file + ".pth") - if self.install_path_file: - self.execute (write_file, - (filename, [self.extra_dirs]), - "creating %s" % filename) - else: - self.warn("path file '%s' not created" % filename) + # -- Predicates for sub-command list ------------------------------- + + def has_lib (self): + """Return true if the current distribution has any Python + modules to install.""" + return (self.distribution.has_pure_modules() or + self.distribution.has_ext_modules()) + + def has_headers (self): + return self.distribution.has_headers() + + def has_scripts (self): + return self.distribution.has_scripts() + + def has_data (self): + return self.distribution.has_data_files() + # 'sub_commands': a list of commands this command might have to run to # get its work done. See cmd.py for more info. -- cgit v1.2.1 From 157e91886f9d060d88fc35c34e2f5239d43c6af3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:08:12 +0000 Subject: Changed to use the 'sub-commands' machinery: - added 'sub_commands' class attr - added 'has_*()' predicates referenced by the sub-command list - rewrote 'run()' so it's a trivial loop over relevant sub-commands --- command/build.py | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/command/build.py b/command/build.py index f30f4ee1..1f785996 100644 --- a/command/build.py +++ b/command/build.py @@ -97,26 +97,34 @@ class build (Command): def run (self): - # For now, "build" means "build_py" then "build_ext". (Eventually - # it should also build documentation.) - - # Invoke the 'build_py' command to "build" pure Python modules - # (ie. copy 'em into the build tree) - if self.distribution.has_pure_modules(): - self.run_command ('build_py') - - # Build any standalone C libraries next -- they're most likely to - # be needed by extension modules, so obviously have to be done - # first! - if self.distribution.has_c_libraries(): - self.run_command ('build_clib') - - # And now 'build_ext' -- compile extension modules and put them - # into the build tree - if self.distribution.has_ext_modules(): - self.run_command ('build_ext') - - if self.distribution.has_scripts(): - self.run_command ('build_scripts') + # Run all relevant sub-commands. This will be some subset of: + # - build_py - pure Python modules + # - build_clib - standalone C libraries + # - build_ext - Python extensions + # - build_scripts - (Python) scripts + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + + # -- Predicates for the sub-command list --------------------------- + + def has_pure_modules (self): + return self.distribution.has_pure_modules() + + def has_c_libraries (self): + return self.distribution.has_c_libraries() + + def has_ext_modules (self): + return self.distribution.has_ext_modules() + + def has_scripts (self): + return self.distribution.has_scripts() + + + sub_commands = [('build_py', has_pure_modules), + ('build_clib', has_c_libraries), + ('build_ext', has_ext_modules), + ('build_scripts', has_scripts), + ] # class build -- cgit v1.2.1 From 939159b5a3ded23234a997e930543af87d19c524 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:09:39 +0000 Subject: In 'get_platform()', handle so-called POSIX systems that don't have 'uname()' -- specifically NeXTSTEP. --- util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.py b/util.py index d52c69b4..016119ce 100644 --- a/util.py +++ b/util.py @@ -31,7 +31,7 @@ def get_platform (): For non-POSIX platforms, currently just returns 'sys.platform'. """ - if os.name != "posix": + if os.name != "posix" or not hasattr(os, 'uname'): # XXX what about the architecture? NT is Intel or Alpha, # Mac OS is M68k or PPC, etc. return sys.platform -- cgit v1.2.1 From 3e90f681ce2c2f2bfb9b5015e78a0f207135997a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:29:35 +0000 Subject: Changed 'copy_file()' so it returns a tuple (dest_name, copied) -- hopefully, this will please everyone (as if that's possible). --- file_util.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/file_util.py b/file_util.py index d05b4561..a9e12ce3 100644 --- a/file_util.py +++ b/file_util.py @@ -95,8 +95,9 @@ def copy_file (src, dst, Under Mac OS, uses the native file copy function in macostools; on other systems, uses '_copy_file_contents()' to copy file contents. - Return the name of the destination file, whether it was actually copied - or not. + Return a tuple (dest_name, copied): 'dest_name' is the actual name of + the output file, and 'copied' is true if the file was copied (or would + have been copied, if 'dry_run' true). """ # XXX if the destination file already exists, we clobber it if # copying, but blow up if linking. Hmmm. And I don't know what @@ -121,7 +122,7 @@ def copy_file (src, dst, if update and not newer(src, dst): if verbose: print "not copying %s (output up-to-date)" % src - return dst + return (dst, 0) try: action = _copy_action[link] @@ -135,9 +136,9 @@ def copy_file (src, dst, print "%s %s -> %s" % (action, src, dst) if dry_run: - return dst + return (dst, 1) - # On a Mac, use the native file copy routine + # On Mac OS, use the native file copy routine if os.name == 'mac': import macostools try: @@ -169,7 +170,7 @@ def copy_file (src, dst, if preserve_mode: os.chmod(dst, S_IMODE(st[ST_MODE])) - return dst + return (dst, 1) # copy_file () -- cgit v1.2.1 From 4e493b610cddc4325efb115e2ebf61d2aaba8068 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:33:05 +0000 Subject: Changed 'build_module()' so it returns the result of 'copy_file()' on the module file -- could be useful for subclasses overriding it. --- command/build_py.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_py.py b/command/build_py.py index ea92c2be..ebe30e81 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -307,7 +307,7 @@ class build_py (Command): outfile = self.get_module_outfile (self.build_lib, package, module) dir = os.path.dirname (outfile) self.mkpath (dir) - self.copy_file (module_file, outfile, preserve_mode=0) + return self.copy_file (module_file, outfile, preserve_mode=0) def build_modules (self): -- cgit v1.2.1 From 5b94288789b79b3b730f5eb2d40eb5e617f93f2f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:34:50 +0000 Subject: Expect a tuple (dest_name, copied) from 'copy_file()'. --- command/install_data.py | 4 ++-- command/install_headers.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/command/install_data.py b/command/install_data.py index 9ce11839..d9a48693 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -52,7 +52,7 @@ class install_data (Command): self.warn("setup script did not provide a directory for " "'%s' -- installing right in '%s'" % (f, self.install_dir)) - out = self.copy_file(f, self.install_dir) + (out, _) = self.copy_file(f, self.install_dir) self.outfiles.append(out) else: # it's a tuple with path to install to and a list of files @@ -63,7 +63,7 @@ class install_data (Command): dir = change_root(self.root, dir) self.mkpath(dir) for data in f[1]: - out = self.copy_file(data, dir) + (out, _) = self.copy_file(data, dir) self.outfiles.append(out) def get_inputs (self): diff --git a/command/install_headers.py b/command/install_headers.py index ec0cf441..2d72a07b 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -41,7 +41,7 @@ class install_headers (Command): self.mkpath(self.install_dir) for header in headers: - out = self.copy_file(header, self.install_dir) + (out, _) = self.copy_file(header, self.install_dir) self.outfiles.append(out) def get_inputs (self): -- cgit v1.2.1 From 28c8c4911fe821cda508e839dbeca6781e9dd924 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:35:26 +0000 Subject: Fixed 'run()' so it doesn't call 'bytecompile()' if 'install()' returned None. --- command/install_lib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index a603b4f5..2c92f3fe 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -56,7 +56,8 @@ class install_lib (Command): outfiles = self.install() # (Optionally) compile .py to .pyc - self.bytecompile(outfiles) + if outfiles is not None: + self.bytecompile(outfiles) # run () -- cgit v1.2.1 From 48647d7afb29872232fd752601c017a428c47233 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:47:17 +0000 Subject: Andrew Kuchling: changed so the '_path_created' dictionary is keyed on absolute pathnames; this lets it keep working in the face of chdir'ing around. --- dir_util.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dir_util.py b/dir_util.py index 768cb4eb..a1578bed 100644 --- a/dir_util.py +++ b/dir_util.py @@ -44,7 +44,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): created_dirs = [] if os.path.isdir(name) or name == '': return created_dirs - if _path_created.get(name): + if _path_created.get(os.path.abspath(name)): return created_dirs (head, tail) = os.path.split(name) @@ -64,7 +64,9 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): for d in tails: #print "head = %s, d = %s: " % (head, d), head = os.path.join(head, d) - if _path_created.get(head): + abs_head = os.path.abspath(head) + + if _path_created.get(abs_head): continue if verbose: @@ -78,7 +80,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): raise DistutilsFileError, \ "could not create '%s': %s" % (head, exc[-1]) - _path_created[head] = 1 + _path_created[abs_head] = 1 return created_dirs # mkpath () @@ -208,8 +210,9 @@ def remove_tree (directory, verbose=0, dry_run=0): try: apply(cmd[0], (cmd[1],)) # remove dir from cache if it's already there - if _path_created.has_key(cmd[1]): - del _path_created[cmd[1]] + abspath = os.path.abspath(cmd[1]) + if _path_created.has_key(abspath): + del _path_created[abspath] except (IOError, OSError), exc: if verbose: print grok_environment_error( -- cgit v1.2.1 From cade95e31186a5f2c4119d538e60ea58a44fa920 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 18:27:54 +0000 Subject: Standardized whitespace around function calls. --- command/bdist.py | 2 +- command/bdist_dumb.py | 12 ++--- command/bdist_rpm.py | 2 +- command/bdist_wininst.py | 88 +++++++++++++++---------------- command/build.py | 12 ++--- command/build_clib.py | 74 +++++++++++++------------- command/build_ext.py | 127 ++++++++++++++++++++++----------------------- command/build_py.py | 108 +++++++++++++++++++------------------- command/build_scripts.py | 6 +-- command/clean.py | 20 +++---- command/config.py | 8 +-- command/install.py | 112 +++++++++++++++++++-------------------- command/install_scripts.py | 10 ++-- command/sdist.py | 96 +++++++++++++++++----------------- 14 files changed, 338 insertions(+), 339 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 8651e709..c0cb1d3a 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -132,7 +132,7 @@ class bdist (Command): # keep its temporary files around so subsequent runs go faster. if cmd_name in commands[i+1:]: sub_cmd.keep_temp = 1 - self.run_command (cmd_name) + self.run_command(cmd_name) # run() diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 520098db..8dfc3271 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -71,24 +71,24 @@ class bdist_dumb (Command): def run (self): - self.run_command ('build') + self.run_command('build') install = self.reinitialize_command('install', reinit_subcommands=1) install.root = self.bdist_dir - self.announce ("installing to %s" % self.bdist_dir) + self.announce("installing to %s" % self.bdist_dir) self.run_command('install') # And make an archive relative to the root of the # pseudo-installation tree. archive_basename = "%s.%s" % (self.distribution.get_fullname(), self.plat_name) - self.make_archive (os.path.join(self.dist_dir, archive_basename), - self.format, - root_dir=self.bdist_dir) + self.make_archive(os.path.join(self.dist_dir, archive_basename), + self.format, + root_dir=self.bdist_dir) if not self.keep_temp: - remove_tree (self.bdist_dir, self.verbose, self.dry_run) + remove_tree(self.bdist_dir, self.verbose, self.dry_run) # run() diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index c293f1f5..f4215904 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -258,7 +258,7 @@ class bdist_rpm (Command): # Make a source distribution and copy to SOURCES directory with # optional icon. - sdist = self.reinitialize_command ('sdist') + sdist = self.reinitialize_command('sdist') if self.use_bzip2: sdist.formats = ['bztar'] else: diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 3251bac0..afe6955e 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -56,8 +56,8 @@ class bdist_wininst (Command): if self.distribution.has_ext_modules(): short_version = sys.version[:3] if self.target_version and self.target_version != short_version: - raise DistutilsOptionError ("target version can only be" + - short_version) + raise DistutilsOptionError, \ + "target version can only be" + short_version self.target_version = short_version self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) @@ -73,7 +73,7 @@ class bdist_wininst (Command): ("distribution contains extensions and/or C libraries; " "must be compiled on a Windows 32 platform") - self.run_command ('build') + self.run_command('build') install = self.reinitialize_command('install') install.root = self.bdist_dir @@ -91,7 +91,7 @@ class bdist_wininst (Command): install_lib.ensure_finalized() - self.announce ("installing to %s" % self.bdist_dir) + self.announce("installing to %s" % self.bdist_dir) install.ensure_finalized() install.run() @@ -103,24 +103,24 @@ class bdist_wininst (Command): # Our archive MUST be relative to sys.prefix, which is the # same as install_purelib in the 'nt' scheme. - root_dir = os.path.normpath (install.install_purelib) + root_dir = os.path.normpath(install.install_purelib) # Sanity check: Make sure everything is included for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): attrname = 'install_' + key - install_x = getattr (install, attrname) + install_x = getattr(install, attrname) # (Use normpath so that we can string.find to look for # subdirectories) - install_x = os.path.normpath (install_x) - if string.find (install_x, root_dir) != 0: + install_x = os.path.normpath(install_x) + if string.find(install_x, root_dir) != 0: raise DistutilsInternalError \ ("'%s' not included in install_lib" % key) - arcname = self.make_archive (archive_basename, "zip", - root_dir=root_dir) - self.create_exe (arcname, fullname) + arcname = self.make_archive(archive_basename, "zip", + root_dir=root_dir) + self.create_exe(arcname, fullname) if not self.keep_temp: - remove_tree (self.bdist_dir, self.verbose, self.dry_run) + remove_tree(self.bdist_dir, self.verbose, self.dry_run) # run() @@ -133,37 +133,37 @@ class bdist_wininst (Command): # Write the [metadata] section. Values are written with # repr()[1:-1], so they do not contain unprintable characters, and # are not surrounded by quote chars. - lines.append ("[metadata]") + lines.append("[metadata]") # 'info' will be displayed in the installer's dialog box, # describing the items to be installed. info = (metadata.long_description or '') + '\n' - for name in dir (metadata): + for name in dir(metadata): if (name != 'long_description'): - data = getattr (metadata, name) + data = getattr(metadata, name) if data: info = info + ("\n %s: %s" % \ - (string.capitalize (name), data)) - lines.append ("%s=%s" % (name, repr (data)[1:-1])) + (string.capitalize(name), data)) + lines.append("%s=%s" % (name, repr(data)[1:-1])) # The [setup] section contains entries controlling # the installer runtime. - lines.append ("\n[Setup]") - lines.append ("info=%s" % repr (info)[1:-1]) - lines.append ("target_compile=%d" % (not self.no_target_compile)) - lines.append ("target_optimize=%d" % (not self.no_target_optimize)) + lines.append("\n[Setup]") + lines.append("info=%s" % repr(info)[1:-1]) + lines.append("target_compile=%d" % (not self.no_target_compile)) + lines.append("target_optimize=%d" % (not self.no_target_optimize)) if self.target_version: - lines.append ("target_version=%s" % self.target_version) + lines.append("target_version=%s" % self.target_version) title = self.distribution.get_fullname() - lines.append ("title=%s" % repr (title)[1:-1]) + lines.append("title=%s" % repr(title)[1:-1]) import time import distutils build_info = "Build %s with distutils-%s" % \ - (time.ctime (time.time()), distutils.__version__) - lines.append ("build_info=%s" % build_info) - return string.join (lines, "\n") + (time.ctime(time.time()), distutils.__version__) + lines.append("build_info=%s" % build_info) + return string.join(lines, "\n") # get_inidata() @@ -183,36 +183,36 @@ class bdist_wininst (Command): else: installer_name = os.path.join(self.dist_dir, "%s.win32.exe" % fullname) - self.announce ("creating %s" % installer_name) + self.announce("creating %s" % installer_name) - file = open (installer_name, "wb") - file.write (self.get_exe_bytes ()) - file.write (cfgdata) - header = struct.pack ("' under the base build directory. We only use one of # them for a given distribution, though -- if self.build_purelib is None: - self.build_purelib = os.path.join (self.build_base, 'lib') + self.build_purelib = os.path.join(self.build_base, 'lib') if self.build_platlib is None: - self.build_platlib = os.path.join (self.build_base, - 'lib' + plat_specifier) + self.build_platlib = os.path.join(self.build_base, + 'lib' + plat_specifier) # 'build_lib' is the actual directory that we will use for this # particular module distribution -- if user didn't supply it, pick @@ -87,10 +87,10 @@ class build (Command): # 'build_temp' -- temporary directory for compiler turds, # "build/temp." if self.build_temp is None: - self.build_temp = os.path.join (self.build_base, - 'temp' + plat_specifier) + self.build_temp = os.path.join(self.build_base, + 'temp' + plat_specifier) if self.build_scripts is None: - self.build_scripts = os.path.join (self.build_base, 'scripts') + self.build_scripts = os.path.join(self.build_base, 'scripts') # finalize_options () diff --git a/command/build_clib.py b/command/build_clib.py index 775b7ade..2726b975 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -80,22 +80,22 @@ class build_clib (Command): # I think that C libraries are really just temporary build # by-products, at least from the point of view of building Python # extensions -- but I want to keep my options open. - self.set_undefined_options ('build', - ('build_temp', 'build_clib'), - ('build_temp', 'build_temp'), - ('compiler', 'compiler'), - ('debug', 'debug'), - ('force', 'force')) + self.set_undefined_options('build', + ('build_temp', 'build_clib'), + ('build_temp', 'build_temp'), + ('compiler', 'compiler'), + ('debug', 'debug'), + ('force', 'force')) self.libraries = self.distribution.libraries if self.libraries: - self.check_library_list (self.libraries) + self.check_library_list(self.libraries) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - if type (self.include_dirs) is StringType: - self.include_dirs = string.split (self.include_dirs, - os.pathsep) + if type(self.include_dirs) is StringType: + self.include_dirs = string.split(self.include_dirs, + os.pathsep) # XXX same as for build_ext -- what about 'self.define' and # 'self.undef' ? @@ -110,23 +110,23 @@ class build_clib (Command): # Yech -- this is cut 'n pasted from build_ext.py! from distutils.ccompiler import new_compiler - self.compiler = new_compiler (compiler=self.compiler, - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force) + self.compiler = new_compiler(compiler=self.compiler, + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) customize_compiler(self.compiler) if self.include_dirs is not None: - self.compiler.set_include_dirs (self.include_dirs) + self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name,value) in self.define: - self.compiler.define_macro (name, value) + self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self.compiler.undefine_macro (macro) + self.compiler.undefine_macro(macro) - self.build_libraries (self.libraries) + self.build_libraries(self.libraries) # run() @@ -141,16 +141,16 @@ class build_clib (Command): # Yechh, blecch, ackk: this is ripped straight out of build_ext.py, # with only names changed to protect the innocent! - if type (libraries) is not ListType: + if type(libraries) is not ListType: raise DistutilsSetupError, \ "'libraries' option must be a list of tuples" for lib in libraries: - if type (lib) is not TupleType and len (lib) != 2: + if type(lib) is not TupleType and len(lib) != 2: raise DistutilsSetupError, \ "each element of 'libraries' must a 2-tuple" - if type (lib[0]) is not StringType: + if type(lib[0]) is not StringType: raise DistutilsSetupError, \ "first element of each tuple in 'libraries' " + \ "must be a string (the library name)" @@ -160,7 +160,7 @@ class build_clib (Command): "may not contain directory separators") % \ lib[0] - if type (lib[1]) is not DictionaryType: + if type(lib[1]) is not DictionaryType: raise DistutilsSetupError, \ "second element of each tuple in 'libraries' " + \ "must be a dictionary (build info)" @@ -178,7 +178,7 @@ class build_clib (Command): lib_names = [] for (lib_name, build_info) in self.libraries: - lib_names.append (lib_name) + lib_names.append(lib_name) return lib_names # get_library_names () @@ -189,33 +189,33 @@ class build_clib (Command): compiler = self.compiler for (lib_name, build_info) in libraries: - sources = build_info.get ('sources') - if sources is None or type (sources) not in (ListType, TupleType): + sources = build_info.get('sources') + if sources is None or type(sources) not in (ListType, TupleType): raise DistutilsSetupError, \ ("in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames") % lib_name - sources = list (sources) + sources = list(sources) - self.announce ("building '%s' library" % lib_name) + self.announce("building '%s' library" % lib_name) # First, compile the source code to object files in the library # directory. (This should probably change to putting object # files in a temporary build directory.) - macros = build_info.get ('macros') - include_dirs = build_info.get ('include_dirs') - objects = self.compiler.compile (sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=include_dirs, - debug=self.debug) + macros = build_info.get('macros') + include_dirs = build_info.get('include_dirs') + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=include_dirs, + debug=self.debug) # Now "link" the object files together into a static library. # (On Unix at least, this isn't really linking -- it just # builds an archive. Whatever.) - self.compiler.create_static_lib (objects, lib_name, - output_dir=self.build_clib, - debug=self.debug) + self.compiler.create_static_lib(objects, lib_name, + output_dir=self.build_clib, + debug=self.debug) # for libraries diff --git a/command/build_ext.py b/command/build_ext.py index 9147c3d0..70e8a734 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -112,12 +112,12 @@ class build_ext (Command): def finalize_options (self): from distutils import sysconfig - self.set_undefined_options ('build', - ('build_lib', 'build_lib'), - ('build_temp', 'build_temp'), - ('compiler', 'compiler'), - ('debug', 'debug'), - ('force', 'force')) + self.set_undefined_options('build', + ('build_lib', 'build_lib'), + ('build_temp', 'build_temp'), + ('compiler', 'compiler'), + ('debug', 'debug'), + ('force', 'force')) if self.package is None: self.package = self.distribution.ext_package @@ -131,17 +131,16 @@ class build_ext (Command): plat_py_include = sysconfig.get_python_inc(plat_specific=1) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - if type (self.include_dirs) is StringType: - self.include_dirs = string.split (self.include_dirs, - os.pathsep) + if type(self.include_dirs) is StringType: + self.include_dirs = string.split(self.include_dirs, os.pathsep) # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. - self.include_dirs.append (py_include) + self.include_dirs.append(py_include) if plat_py_include != py_include: - self.include_dirs.append (plat_py_include) + self.include_dirs.append(plat_py_include) - if type (self.libraries) is StringType: + if type(self.libraries) is StringType: self.libraries = [self.libraries] # Life is easier if we're not forever checking for None, so @@ -157,11 +156,11 @@ class build_ext (Command): # for Release and Debug builds. # also Python's library directory must be appended to library_dirs if os.name == 'nt': - self.library_dirs.append (os.path.join(sys.exec_prefix, 'libs')) + self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) if self.debug: - self.build_temp = os.path.join (self.build_temp, "Debug") + self.build_temp = os.path.join(self.build_temp, "Debug") else: - self.build_temp = os.path.join (self.build_temp, "Release") + self.build_temp = os.path.join(self.build_temp, "Release") # finalize_options () @@ -188,16 +187,16 @@ class build_ext (Command): # directory where we put them is in the library search path for # linking extensions. if self.distribution.has_c_libraries(): - build_clib = self.get_finalized_command ('build_clib') - self.libraries.extend (build_clib.get_library_names() or []) - self.library_dirs.append (build_clib.build_clib) + build_clib = self.get_finalized_command('build_clib') + self.libraries.extend(build_clib.get_library_names() or []) + self.library_dirs.append(build_clib.build_clib) # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler (compiler=self.compiler, - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force) + self.compiler = new_compiler(compiler=self.compiler, + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) customize_compiler(self.compiler) # And make sure that any compile/link-related options (which might @@ -205,25 +204,25 @@ class build_ext (Command): # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self.compiler.set_include_dirs (self.include_dirs) + self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name,value) in self.define: - self.compiler.define_macro (name, value) + self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self.compiler.undefine_macro (macro) + self.compiler.undefine_macro(macro) if self.libraries is not None: - self.compiler.set_libraries (self.libraries) + self.compiler.set_libraries(self.libraries) if self.library_dirs is not None: - self.compiler.set_library_dirs (self.library_dirs) + self.compiler.set_library_dirs(self.library_dirs) if self.rpath is not None: - self.compiler.set_runtime_library_dirs (self.rpath) + self.compiler.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self.compiler.set_link_objects (self.link_objects) + self.compiler.set_link_objects(self.link_objects) # Now actually compile and link everything. - self.build_extensions () + self.build_extensions() # run () @@ -320,7 +319,7 @@ class build_ext (Command): # Wouldn't it be neat if we knew the names of header files too... for ext in self.extensions: - filenames.extend (ext.sources) + filenames.extend(ext.sources) return filenames @@ -330,16 +329,16 @@ class build_ext (Command): # Sanity check the 'extensions' list -- can't assume this is being # done in the same run as a 'build_extensions()' call (in fact, we # can probably assume that it *isn't*!). - self.check_extensions_list (self.extensions) + self.check_extensions_list(self.extensions) # And build the list of output (built) filenames. Note that this # ignores the 'inplace' flag, and assumes everything goes in the # "build" tree. outputs = [] for ext in self.extensions: - fullname = self.get_ext_fullname (ext.name) - outputs.append (os.path.join (self.build_lib, - self.get_ext_filename(fullname))) + fullname = self.get_ext_fullname(ext.name) + outputs.append(os.path.join(self.build_lib, + self.get_ext_filename(fullname))) return outputs # get_outputs () @@ -348,40 +347,40 @@ class build_ext (Command): def build_extensions (self): # First, sanity-check the 'extensions' list - self.check_extensions_list (self.extensions) + self.check_extensions_list(self.extensions) for ext in self.extensions: sources = ext.sources - if sources is None or type (sources) not in (ListType, TupleType): + if sources is None or type(sources) not in (ListType, TupleType): raise DistutilsSetupError, \ ("in 'ext_modules' option (extension '%s'), " + "'sources' must be present and must be " + "a list of source filenames") % ext.name - sources = list (sources) + sources = list(sources) - fullname = self.get_ext_fullname (ext.name) + fullname = self.get_ext_fullname(ext.name) if self.inplace: # ignore build-lib -- put the compiled extension into # the source tree along with pure Python modules - modpath = string.split (fullname, '.') - package = string.join (modpath[0:-1], '.') + modpath = string.split(fullname, '.') + package = string.join(modpath[0:-1], '.') base = modpath[-1] - build_py = self.get_finalized_command ('build_py') - package_dir = build_py.get_package_dir (package) - ext_filename = os.path.join (package_dir, - self.get_ext_filename(base)) + build_py = self.get_finalized_command('build_py') + package_dir = build_py.get_package_dir(package) + ext_filename = os.path.join(package_dir, + self.get_ext_filename(base)) else: - ext_filename = os.path.join (self.build_lib, - self.get_ext_filename(fullname)) + ext_filename = os.path.join(self.build_lib, + self.get_ext_filename(fullname)) if not (self.force or newer_group(sources, ext_filename, 'newer')): - self.announce ("skipping '%s' extension (up-to-date)" % - ext.name) + self.announce("skipping '%s' extension (up-to-date)" % + ext.name) continue # 'for' loop over all extensions else: - self.announce ("building '%s' extension" % ext.name) + self.announce("building '%s' extension" % ext.name) # First, scan the sources for SWIG definition files (.i), run # SWIG on 'em to create .c files, and modify the sources list @@ -416,22 +415,22 @@ class build_ext (Command): if os.environ.has_key('CFLAGS'): extra_args.extend(string.split(os.environ['CFLAGS'])) - objects = self.compiler.compile (sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args) + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args) # Now link the object files together into a "shared object" -- # of course, first we have to figure out all the other things # that go into the mix. if ext.extra_objects: - objects.extend (ext.extra_objects) + objects.extend(ext.extra_objects) extra_args = ext.extra_link_args or [] - self.compiler.link_shared_object ( + self.compiler.link_shared_object( objects, ext_filename, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, @@ -481,11 +480,11 @@ class build_ext (Command): swig = self.find_swig() swig_cmd = [swig, "-python", "-dnone", "-ISWIG"] if self.swig_cpp: - swig_cmd.append ("-c++") + swig_cmd.append("-c++") for source in swig_sources: target = swig_targets[source] - self.announce ("swigging %s to %s" % (source, target)) + self.announce("swigging %s to %s" % (source, target)) self.spawn(swig_cmd + ["-o", target, source]) return new_sources @@ -507,7 +506,7 @@ class build_ext (Command): # if not, act like Unix and assume it's in the PATH. for vers in ("1.3", "1.2", "1.1"): fn = os.path.join("c:\\swig%s" % vers, "swig.exe") - if os.path.isfile (fn): + if os.path.isfile(fn): return fn else: return "swig.exe" @@ -535,12 +534,12 @@ class build_ext (Command): """ from distutils.sysconfig import get_config_var - ext_path = string.split (ext_name, '.') + ext_path = string.split(ext_name, '.') # extensions in debug_mode are named 'module_d.pyd' under windows so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: - return apply (os.path.join, ext_path) + '_d' + so_ext - return apply (os.path.join, ext_path) + so_ext + return apply(os.path.join, ext_path) + '_d' + so_ext + return apply(os.path.join, ext_path) + so_ext def get_export_symbols (self, ext): """Return the list of symbols that a shared extension has to diff --git a/command/build_py.py b/command/build_py.py index ebe30e81..6a8a7f43 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -34,9 +34,9 @@ class build_py (Command): self.force = None def finalize_options (self): - self.set_undefined_options ('build', - ('build_lib', 'build_lib'), - ('force', 'force')) + self.set_undefined_options('build', + ('build_lib', 'build_lib'), + ('force', 'force')) # Get the distribution options that are aliases for build_py # options -- list of packages and list of modules. @@ -83,9 +83,9 @@ class build_py (Command): # Now we're down to two cases: 'py_modules' only and 'packages' only. if self.py_modules: - self.build_modules () + self.build_modules() else: - self.build_packages () + self.build_packages() # run () @@ -95,24 +95,24 @@ class build_py (Command): distribution, where package 'package' should be found (at least according to the 'package_dir' option, if any).""" - path = string.split (package, '.') + path = string.split(package, '.') if not self.package_dir: if path: - return apply (os.path.join, path) + return apply(os.path.join, path) else: return '' else: tail = [] while path: try: - pdir = self.package_dir[string.join (path, '.')] + pdir = self.package_dir[string.join(path, '.')] except KeyError: - tail.insert (0, path[-1]) + tail.insert(0, path[-1]) del path[-1] else: - tail.insert (0, pdir) - return apply (os.path.join, tail) + tail.insert(0, pdir) + return apply(os.path.join, tail) else: # Oops, got all the way through 'path' without finding a # match in package_dir. If package_dir defines a directory @@ -126,7 +126,7 @@ class build_py (Command): tail.insert(0, pdir) if tail: - return apply (os.path.join, tail) + return apply(os.path.join, tail) else: return '' @@ -140,22 +140,22 @@ class build_py (Command): # my "empty string means current dir" convention, so we have to # circumvent them. if package_dir != "": - if not os.path.exists (package_dir): + if not os.path.exists(package_dir): raise DistutilsFileError, \ "package directory '%s' does not exist" % package_dir - if not os.path.isdir (package_dir): + if not os.path.isdir(package_dir): raise DistutilsFileError, \ ("supposed package directory '%s' exists, " + "but is not a directory") % package_dir # Require __init__.py for all but the "root package" if package: - init_py = os.path.join (package_dir, "__init__.py") - if os.path.isfile (init_py): + init_py = os.path.join(package_dir, "__init__.py") + if os.path.isfile(init_py): return init_py else: - self.warn (("package init file '%s' not found " + - "(or not a regular file)") % init_py) + self.warn(("package init file '%s' not found " + + "(or not a regular file)") % init_py) # Either not in a package at all (__init__.py not expected), or # __init__.py doesn't exist -- so don't return the filename. @@ -165,9 +165,9 @@ class build_py (Command): def check_module (self, module, module_file): - if not os.path.isfile (module_file): - self.warn ("file %s (for module %s) not found" % - (module_file, module)) + if not os.path.isfile(module_file): + self.warn("file %s (for module %s) not found" % + (module_file, module)) return 0 else: return 1 @@ -176,16 +176,16 @@ class build_py (Command): def find_package_modules (self, package, package_dir): - self.check_package (package, package_dir) - module_files = glob (os.path.join (package_dir, "*.py")) + self.check_package(package, package_dir) + module_files = glob(os.path.join(package_dir, "*.py")) modules = [] setup_script = os.path.abspath(self.distribution.script_name) for f in module_files: - abs_f = os.path.abspath (f) + abs_f = os.path.abspath(f) if abs_f != setup_script: - module = os.path.splitext (os.path.basename (f))[0] - modules.append ((package, module, f)) + module = os.path.splitext(os.path.basename(f))[0] + modules.append((package, module, f)) else: self.debug_print("excluding %s" % setup_script) return modules @@ -218,18 +218,18 @@ class build_py (Command): # - don't check for __init__.py in directory for empty package for module in self.py_modules: - path = string.split (module, '.') + path = string.split(module, '.') package = string.join(path[0:-1], '.') module_base = path[-1] try: (package_dir, checked) = packages[package] except KeyError: - package_dir = self.get_package_dir (package) + package_dir = self.get_package_dir(package) checked = 0 if not checked: - init_py = self.check_package (package, package_dir) + init_py = self.check_package(package, package_dir) packages[package] = (package_dir, 1) if init_py: modules.append((package, "__init__", init_py)) @@ -237,11 +237,11 @@ class build_py (Command): # XXX perhaps we should also check for just .pyc files # (so greedy closed-source bastards can distribute Python # modules too) - module_file = os.path.join (package_dir, module_base + ".py") - if not self.check_module (module, module_file): + module_file = os.path.join(package_dir, module_base + ".py") + if not self.check_module(module, module_file): continue - modules.append ((package, module_base, module_file)) + modules.append((package, module_base, module_file)) return modules @@ -256,13 +256,13 @@ class build_py (Command): 'find_package_modules()' do.""" if self.py_modules: - modules = self.find_modules () + modules = self.find_modules() else: modules = [] for package in self.packages: - package_dir = self.get_package_dir (package) - m = self.find_package_modules (package, package_dir) - modules.extend (m) + package_dir = self.get_package_dir(package) + m = self.find_package_modules(package, package_dir) + modules.extend(m) return modules @@ -271,43 +271,43 @@ class build_py (Command): def get_source_files (self): - modules = self.find_all_modules () + modules = self.find_all_modules() filenames = [] for module in modules: - filenames.append (module[-1]) + filenames.append(module[-1]) return filenames def get_module_outfile (self, build_dir, package, module): outfile_path = [build_dir] + list(package) + [module + ".py"] - return apply (os.path.join, outfile_path) + return apply(os.path.join, outfile_path) def get_outputs (self): - modules = self.find_all_modules () + modules = self.find_all_modules() outputs = [] for (package, module, module_file) in modules: - package = string.split (package, '.') - outputs.append (self.get_module_outfile (self.build_lib, - package, module)) + package = string.split(package, '.') + outputs.append(self.get_module_outfile(self.build_lib, + package, module)) return outputs def build_module (self, module, module_file, package): - if type (package) is StringType: - package = string.split (package, '.') - elif type (package) not in (ListType, TupleType): + if type(package) is StringType: + package = string.split(package, '.') + elif type(package) not in (ListType, TupleType): raise TypeError, \ "'package' must be a string (dot-separated), list, or tuple" # Now put the module source file into the "build" area -- this is # easy, we just copy it somewhere under self.build_lib (the build # directory for Python source). - outfile = self.get_module_outfile (self.build_lib, package, module) - dir = os.path.dirname (outfile) - self.mkpath (dir) - return self.copy_file (module_file, outfile, preserve_mode=0) + outfile = self.get_module_outfile(self.build_lib, package, module) + dir = os.path.dirname(outfile) + self.mkpath(dir) + return self.copy_file(module_file, outfile, preserve_mode=0) def build_modules (self): @@ -319,7 +319,7 @@ class build_py (Command): # self.build_lib (the build directory for Python source). # (Actually, it gets copied to the directory for this package # under self.build_lib.) - self.build_module (module, module_file, package) + self.build_module(module, module_file, package) # build_modules () @@ -337,14 +337,14 @@ class build_py (Command): # already know its package!), and 'module_file' is the path to # the .py file, relative to the current directory # (ie. including 'package_dir'). - package_dir = self.get_package_dir (package) - modules = self.find_package_modules (package, package_dir) + package_dir = self.get_package_dir(package) + modules = self.find_package_modules(package, package_dir) # Now loop over the modules we found, "building" each one (just # copy it to self.build_lib). for (package_, module, module_file) in modules: assert package == package_ - self.build_module (module, module_file, package) + self.build_module(module, module_file, package) # build_packages () diff --git a/command/build_scripts.py b/command/build_scripts.py index eacf7989..495f4c37 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -32,9 +32,9 @@ class build_scripts (Command): self.outfiles = None def finalize_options (self): - self.set_undefined_options ('build', - ('build_scripts', 'build_dir'), - ('force', 'force')) + self.set_undefined_options('build', + ('build_scripts', 'build_dir'), + ('force', 'force')) self.scripts = self.distribution.scripts diff --git a/command/clean.py b/command/clean.py index 4f04f08b..fb8822f7 100644 --- a/command/clean.py +++ b/command/clean.py @@ -50,29 +50,29 @@ class clean (Command): def run(self): # remove the build/temp. directory (unless it's already # gone) - if os.path.exists (self.build_temp): - remove_tree (self.build_temp, self.verbose, self.dry_run) + if os.path.exists(self.build_temp): + remove_tree(self.build_temp, self.verbose, self.dry_run) else: - self.warn ("'%s' does not exist -- can't clean it" % - self.build_temp) + self.warn("'%s' does not exist -- can't clean it" % + self.build_temp) if self.all: # remove build directories for directory in (self.build_lib, self.bdist_base, self.build_scripts): - if os.path.exists (directory): - remove_tree (directory, self.verbose, self.dry_run) + if os.path.exists(directory): + remove_tree(directory, self.verbose, self.dry_run) else: - self.warn ("'%s' does not exist -- can't clean it" % - directory) + self.warn("'%s' does not exist -- can't clean it" % + directory) # just for the heck of it, try to remove the base build directory: # we might have emptied it right now, but if not we don't care if not self.dry_run: try: - os.rmdir (self.build_base) - self.announce ("removing '%s'" % self.build_base) + os.rmdir(self.build_base) + self.announce("removing '%s'" % self.build_base) except OSError: pass diff --git a/command/config.py b/command/config.py index 5c3f26a7..a13055cf 100644 --- a/command/config.py +++ b/command/config.py @@ -87,10 +87,10 @@ class config (Command): # import. from distutils.ccompiler import CCompiler, new_compiler if not isinstance(self.compiler, CCompiler): - self.compiler = new_compiler (compiler=self.compiler, - verbose=self.noisy, - dry_run=self.dry_run, - force=1) + self.compiler = new_compiler(compiler=self.compiler, + verbose=self.noisy, + dry_run=self.dry_run, + force=1) if self.include_dirs: self.compiler.set_include_dirs(self.include_dirs) if self.libraries: diff --git a/command/install.py b/command/install.py index 8c6aa1cf..e9528c63 100644 --- a/command/install.py +++ b/command/install.py @@ -210,10 +210,10 @@ class install (Command): "not both") else: if self.exec_prefix: - self.warn ("exec-prefix option ignored on this platform") + self.warn("exec-prefix option ignored on this platform") self.exec_prefix = None if self.home: - self.warn ("home option ignored on this platform") + self.warn("home option ignored on this platform") self.home = None # Now the interesting logic -- so interesting that we farm it out @@ -224,14 +224,14 @@ class install (Command): # install_{purelib,platlib,lib,scripts,data,...}, and the # INSTALL_SCHEME dictionary above. Phew! - self.dump_dirs ("pre-finalize_{unix,other}") + self.dump_dirs("pre-finalize_{unix,other}") if os.name == 'posix': - self.finalize_unix () + self.finalize_unix() else: - self.finalize_other () + self.finalize_other() - self.dump_dirs ("post-finalize_{unix,other}()") + self.dump_dirs("post-finalize_{unix,other}()") # Expand configuration variables, tilde, etc. in self.install_base # and self.install_platbase -- that way, we can use $base or @@ -250,9 +250,9 @@ class install (Command): 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, } - self.expand_basedirs () + self.expand_basedirs() - self.dump_dirs ("post-expand_basedirs()") + self.dump_dirs("post-expand_basedirs()") # Now define config vars for the base directories so we can expand # everything else. @@ -262,13 +262,13 @@ class install (Command): if DEBUG: from pprint import pprint print "config vars:" - pprint (self.config_vars) + pprint(self.config_vars) # Expand "~" and configuration variables in the installation # directories. - self.expand_dirs () + self.expand_dirs() - self.dump_dirs ("post-expand_dirs()") + self.dump_dirs("post-expand_dirs()") # Pick the actual directory to install all modules to: either # install_purelib or install_platlib, depending on whether this @@ -290,9 +290,9 @@ class install (Command): # have to deal with 'extra_path', which is the hack for allowing # non-packagized module distributions (hello, Numerical Python!) to # get their own directories. - self.handle_extra_path () + self.handle_extra_path() self.install_libbase = self.install_lib # needed for .pth file - self.install_lib = os.path.join (self.install_lib, self.extra_dirs) + self.install_lib = os.path.join(self.install_lib, self.extra_dirs) # If a new root directory was supplied, make all the installation # dirs relative to it. @@ -300,12 +300,12 @@ class install (Command): self.change_roots('libbase', 'lib', 'purelib', 'platlib', 'scripts', 'data', 'headers') - self.dump_dirs ("after prepending root") + self.dump_dirs("after prepending root") # Find out the build directories, ie. where to install from. - self.set_undefined_options ('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib')) + self.set_undefined_options('build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib')) # Punt on doc directories for now -- after all, we're punting on # documentation completely! @@ -321,8 +321,8 @@ class install (Command): opt_name = opt[0] if opt_name[-1] == "=": opt_name = opt_name[0:-1] - opt_name = string.translate (opt_name, longopt_xlate) - val = getattr (self, opt_name) + opt_name = string.translate(opt_name, longopt_xlate) + val = getattr(self, opt_name) print " %s: %s" % (opt_name, val) @@ -342,15 +342,15 @@ class install (Command): if self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme ("unix_home") + self.select_scheme("unix_home") else: if self.prefix is None: if self.exec_prefix is not None: raise DistutilsOptionError, \ "must not supply exec-prefix without prefix" - self.prefix = os.path.normpath (sys.prefix) - self.exec_prefix = os.path.normpath (sys.exec_prefix) + self.prefix = os.path.normpath(sys.prefix) + self.exec_prefix = os.path.normpath(sys.exec_prefix) else: if self.exec_prefix is None: @@ -358,7 +358,7 @@ class install (Command): self.install_base = self.prefix self.install_platbase = self.exec_prefix - self.select_scheme ("unix_prefix") + self.select_scheme("unix_prefix") # finalize_unix () @@ -366,11 +366,11 @@ class install (Command): def finalize_other (self): # Windows and Mac OS for now if self.prefix is None: - self.prefix = os.path.normpath (sys.prefix) + self.prefix = os.path.normpath(sys.prefix) self.install_base = self.install_platbase = self.prefix try: - self.select_scheme (os.name) + self.select_scheme(os.name) except KeyError: raise DistutilsPlatformError, \ "I don't know how to install stuff on '%s'" % os.name @@ -389,26 +389,26 @@ class install (Command): def _expand_attrs (self, attrs): for attr in attrs: - val = getattr (self, attr) + val = getattr(self, attr) if val is not None: if os.name == 'posix': - val = os.path.expanduser (val) - val = subst_vars (val, self.config_vars) - setattr (self, attr, val) + val = os.path.expanduser(val) + val = subst_vars(val, self.config_vars) + setattr(self, attr, val) def expand_basedirs (self): - self._expand_attrs (['install_base', - 'install_platbase', - 'root']) + self._expand_attrs(['install_base', + 'install_platbase', + 'root']) def expand_dirs (self): - self._expand_attrs (['install_purelib', - 'install_platlib', - 'install_lib', - 'install_headers', - 'install_scripts', - 'install_data',]) + self._expand_attrs(['install_purelib', + 'install_platlib', + 'install_lib', + 'install_headers', + 'install_scripts', + 'install_data',]) def convert_paths (self, *names): @@ -423,12 +423,12 @@ class install (Command): self.extra_path = self.distribution.extra_path if self.extra_path is not None: - if type (self.extra_path) is StringType: - self.extra_path = string.split (self.extra_path, ',') + if type(self.extra_path) is StringType: + self.extra_path = string.split(self.extra_path, ',') - if len (self.extra_path) == 1: + if len(self.extra_path) == 1: path_file = extra_dirs = self.extra_path[0] - elif len (self.extra_path) == 2: + elif len(self.extra_path) == 2: (path_file, extra_dirs) = self.extra_path else: raise DistutilsOptionError, \ @@ -437,7 +437,7 @@ class install (Command): # convert to local form in case Unix notation used (as it # should be in setup scripts) - extra_dirs = convert_path (extra_dirs) + extra_dirs = convert_path(extra_dirs) else: path_file = None @@ -463,21 +463,21 @@ class install (Command): # Obviously have to build before we can install if not self.skip_build: - self.run_command ('build') + self.run_command('build') # Run all sub-commands (at least those that need to be run) for cmd_name in self.get_sub_commands(): - self.run_command (cmd_name) + self.run_command(cmd_name) if self.path_file: - self.create_path_file () + self.create_path_file() # write list of installed files, if requested. if self.record: outputs = self.get_outputs() if self.root: # strip any package prefix root_len = len(self.root) - for counter in xrange (len (outputs)): + for counter in xrange(len(outputs)): outputs[counter] = outputs[counter][root_len:] self.execute(write_file, (self.record, outputs), @@ -496,12 +496,12 @@ class install (Command): # run () def create_path_file (self): - filename = os.path.join (self.install_libbase, - self.path_file + ".pth") + filename = os.path.join(self.install_libbase, + self.path_file + ".pth") if self.install_path_file: - self.execute (write_file, - (filename, [self.extra_dirs]), - "creating %s" % filename) + self.execute(write_file, + (filename, [self.extra_dirs]), + "creating %s" % filename) else: self.warn("path file '%s' not created" % filename) @@ -513,8 +513,8 @@ class install (Command): # get the outputs of all its sub-commands. outputs = [] for cmd_name in self.get_sub_commands(): - cmd = self.get_finalized_command (cmd_name) - outputs.extend (cmd.get_outputs()) + cmd = self.get_finalized_command(cmd_name) + outputs.extend(cmd.get_outputs()) return outputs @@ -522,8 +522,8 @@ class install (Command): # XXX gee, this looks familiar ;-( inputs = [] for cmd_name in self.get_sub_commands(): - cmd = self.get_finalized_command (cmd_name) - inputs.extend (cmd.get_inputs()) + cmd = self.get_finalized_command(cmd_name) + inputs.extend(cmd.get_inputs()) return inputs diff --git a/command/install_scripts.py b/command/install_scripts.py index b8938c48..3bc23e74 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -33,11 +33,11 @@ class install_scripts (Command): def finalize_options (self): self.set_undefined_options('build', ('build_scripts', 'build_dir')) - self.set_undefined_options ('install', - ('install_scripts', 'install_dir'), - ('force', 'force'), - ('skip_build', 'skip_build'), - ) + self.set_undefined_options('install', + ('install_scripts', 'install_dir'), + ('force', 'force'), + ('skip_build', 'skip_build'), + ) def run (self): if not self.skip_build: diff --git a/command/sdist.py b/command/sdist.py index adaf4925..5116868e 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -118,7 +118,7 @@ class sdist (Command): "don't know how to create source distributions " + \ "on platform %s" % os.name - bad_format = archive_util.check_archive_formats (self.formats) + bad_format = archive_util.check_archive_formats(self.formats) if bad_format: raise DistutilsOptionError, \ "unknown archive format '%s'" % bad_format @@ -135,12 +135,12 @@ class sdist (Command): # Ensure that all required meta-data is given; warn if not (but # don't die, it's not *that* serious!) - self.check_metadata () + self.check_metadata() # Do whatever it takes to get the list of files to process # (process the manifest template, read an existing manifest, # whatever). File list is accumulated in 'self.filelist'. - self.get_file_list () + self.get_file_list() # If user just wanted us to regenerate the manifest, stop now. if self.manifest_only: @@ -148,7 +148,7 @@ class sdist (Command): # Otherwise, go ahead and create the source distribution tarball, # or zipfile, or whatever. - self.make_distribution () + self.make_distribution() def check_metadata (self): @@ -161,25 +161,25 @@ class sdist (Command): missing = [] for attr in ('name', 'version', 'url'): - if not (hasattr (metadata, attr) and getattr (metadata, attr)): - missing.append (attr) + if not (hasattr(metadata, attr) and getattr(metadata, attr)): + missing.append(attr) if missing: - self.warn ("missing required meta-data: " + - string.join (missing, ", ")) + self.warn("missing required meta-data: " + + string.join(missing, ", ")) if metadata.author: if not metadata.author_email: - self.warn ("missing meta-data: if 'author' supplied, " + - "'author_email' must be supplied too") + self.warn("missing meta-data: if 'author' supplied, " + + "'author_email' must be supplied too") elif metadata.maintainer: if not metadata.maintainer_email: - self.warn ("missing meta-data: if 'maintainer' supplied, " + - "'maintainer_email' must be supplied too") + self.warn("missing meta-data: if 'maintainer' supplied, " + + "'maintainer_email' must be supplied too") else: - self.warn ("missing meta-data: either (author and author_email) " + - "or (maintainer and maintainer_email) " + - "must be supplied") + self.warn("missing meta-data: either (author and author_email) " + + "or (maintainer and maintainer_email) " + + "must be supplied") # check_metadata () @@ -282,41 +282,41 @@ class sdist (Command): standards = [('README', 'README.txt'), self.distribution.script_name] for fn in standards: - if type (fn) is TupleType: + if type(fn) is TupleType: alts = fn got_it = 0 for fn in alts: - if os.path.exists (fn): + if os.path.exists(fn): got_it = 1 - self.filelist.append (fn) + self.filelist.append(fn) break if not got_it: - self.warn ("standard file not found: should have one of " + - string.join (alts, ', ')) + self.warn("standard file not found: should have one of " + + string.join(alts, ', ')) else: - if os.path.exists (fn): - self.filelist.append (fn) + if os.path.exists(fn): + self.filelist.append(fn) else: - self.warn ("standard file '%s' not found" % fn) + self.warn("standard file '%s' not found" % fn) optional = ['test/test*.py', 'setup.cfg'] for pattern in optional: - files = filter (os.path.isfile, glob (pattern)) + files = filter(os.path.isfile, glob(pattern)) if files: - self.filelist.extend (files) + self.filelist.extend(files) if self.distribution.has_pure_modules(): - build_py = self.get_finalized_command ('build_py') - self.filelist.extend (build_py.get_source_files ()) + build_py = self.get_finalized_command('build_py') + self.filelist.extend(build_py.get_source_files()) if self.distribution.has_ext_modules(): - build_ext = self.get_finalized_command ('build_ext') - self.filelist.extend (build_ext.get_source_files ()) + build_ext = self.get_finalized_command('build_ext') + self.filelist.extend(build_ext.get_source_files()) if self.distribution.has_c_libraries(): - build_clib = self.get_finalized_command ('build_clib') - self.filelist.extend (build_clib.get_source_files ()) + build_clib = self.get_finalized_command('build_clib') + self.filelist.extend(build_clib.get_source_files()) # add_defaults () @@ -329,13 +329,13 @@ class sdist (Command): accordingly. """ self.announce("reading manifest template '%s'" % self.template) - template = TextFile (self.template, - strip_comments=1, - skip_blanks=1, - join_lines=1, - lstrip_ws=1, - rstrip_ws=1, - collapse_join=1) + template = TextFile(self.template, + strip_comments=1, + skip_blanks=1, + join_lines=1, + lstrip_ws=1, + rstrip_ws=1, + collapse_join=1) while 1: line = template.readline() @@ -386,14 +386,14 @@ class sdist (Command): distribution. """ self.announce("reading manifest file '%s'" % self.manifest) - manifest = open (self.manifest) + manifest = open(self.manifest) while 1: - line = manifest.readline () + line = manifest.readline() if line == '': # end of file break if line[-1] == '\n': line = line[0:-1] - self.filelist.append (line) + self.filelist.append(line) # read_manifest () @@ -421,7 +421,7 @@ class sdist (Command): # out-of-date, because by default we blow away 'base_dir' when # we're done making the distribution archives.) - if hasattr (os, 'link'): # can make hard links on this system + if hasattr(os, 'link'): # can make hard links on this system link = 'hard' msg = "making hard links in %s..." % base_dir else: # nope, have to copy @@ -431,13 +431,13 @@ class sdist (Command): if not files: self.warn("no files to distribute -- empty manifest?") else: - self.announce (msg) + self.announce(msg) for file in files: if not os.path.isfile(file): self.warn("'%s' not a regular file -- skipping" % file) else: - dest = os.path.join (base_dir, file) - self.copy_file (file, dest, link=link) + dest = os.path.join(base_dir, file) + self.copy_file(file, dest, link=link) # make_release_tree () @@ -455,16 +455,16 @@ class sdist (Command): base_dir = self.distribution.get_fullname() base_name = os.path.join(self.dist_dir, base_dir) - self.make_release_tree (base_dir, self.filelist.files) + self.make_release_tree(base_dir, self.filelist.files) archive_files = [] # remember names of files we create for fmt in self.formats: - file = self.make_archive (base_name, fmt, base_dir=base_dir) + file = self.make_archive(base_name, fmt, base_dir=base_dir) archive_files.append(file) self.archive_files = archive_files if not self.keep_temp: - dir_util.remove_tree (base_dir, self.verbose, self.dry_run) + dir_util.remove_tree(base_dir, self.verbose, self.dry_run) def get_archive_files (self): """Return the list of archive files created when the command -- cgit v1.2.1 From e125b16ff055a9b864f749cc48dcc94e8191a123 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 18:40:42 +0000 Subject: Reformat docstrings. --- util.py | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/util.py b/util.py index 016119ce..4f972ffd 100644 --- a/util.py +++ b/util.py @@ -59,15 +59,15 @@ def get_platform (): def convert_path (pathname): - """Return 'pathname' as a name that will work on the native - filesystem, i.e. split it on '/' and put it back together again - using the current directory separator. Needed because filenames in - the setup script are always supplied in Unix style, and have to be - converted to the local convention before we can actually use them in - the filesystem. Raises ValueError if 'pathname' is - absolute (starts with '/') or contains local directory separators - (unless the local separator is '/', of course).""" - + """Return 'pathname' as a name that will work on the native filesystem, + i.e. split it on '/' and put it back together again using the current + directory separator. Needed because filenames in the setup script are + always supplied in Unix style, and have to be converted to the local + convention before we can actually use them in the filesystem. Raises + ValueError if 'pathname' is absolute (starts with '/') or contains + local directory separators (unless the local separator is '/', of + course). + """ if os.sep == '/': return pathname if pathname[0] == '/': @@ -116,13 +116,12 @@ def change_root (new_root, pathname): _environ_checked = 0 def check_environ (): """Ensure that 'os.environ' has all the environment variables we - guarantee that users can use in config files, command-line - options, etc. Currently this includes: - HOME - user's home directory (Unix only) - PLAT - description of the current platform, including hardware - and OS (see 'get_platform()') + guarantee that users can use in config files, command-line options, + etc. Currently this includes: + HOME - user's home directory (Unix only) + PLAT - description of the current platform, including hardware + and OS (see 'get_platform()') """ - global _environ_checked if _environ_checked: return @@ -138,15 +137,15 @@ def check_environ (): def subst_vars (str, local_vars): - """Perform shell/Perl-style variable substitution on 'string'. - Every occurrence of '$' followed by a name, or a name enclosed in - braces, is considered a variable. Every variable is substituted by - the value found in the 'local_vars' dictionary, or in 'os.environ' - if it's not in 'local_vars'. 'os.environ' is first checked/ - augmented to guarantee that it contains certain values: see - '_check_environ()'. Raise ValueError for any variables not found in - either 'local_vars' or 'os.environ'.""" - + """Perform shell/Perl-style variable substitution on 'string'. Every + occurrence of '$' followed by a name, or a name enclosed in braces, is + considered a variable. Every variable is substituted by the value + found in the 'local_vars' dictionary, or in 'os.environ' if it's not in + 'local_vars'. 'os.environ' is first checked/ augmented to guarantee + that it contains certain values: see '_check_environ()'. Raise + ValueError for any variables not found in either 'local_vars' or + 'os.environ'. + """ check_environ() def _subst (match, local_vars=local_vars): var_name = match.group(1) -- cgit v1.2.1 From 2cefa8512551672a267241aa0c7c783c272325e0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 18:49:14 +0000 Subject: Various docstring tweaks. Fixed 'subst_vars()' so it actually blows up like the docstring claims (and fixed the docstring not to claim it handles ${var}, which it doesn't). --- util.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/util.py b/util.py index 4f972ffd..1a1ec6d8 100644 --- a/util.py +++ b/util.py @@ -1,7 +1,8 @@ """distutils.util Miscellaneous utility functions -- anything that doesn't fit into -one of the other *util.py modules.""" +one of the other *util.py modules. +""" # created 1999/03/08, Greg Ward @@ -64,9 +65,8 @@ def convert_path (pathname): directory separator. Needed because filenames in the setup script are always supplied in Unix style, and have to be converted to the local convention before we can actually use them in the filesystem. Raises - ValueError if 'pathname' is absolute (starts with '/') or contains - local directory separators (unless the local separator is '/', of - course). + ValueError on non-Unix-ish systems if 'pathname' either starts or + ends with a slash. """ if os.sep == '/': return pathname @@ -138,13 +138,12 @@ def check_environ (): def subst_vars (str, local_vars): """Perform shell/Perl-style variable substitution on 'string'. Every - occurrence of '$' followed by a name, or a name enclosed in braces, is - considered a variable. Every variable is substituted by the value - found in the 'local_vars' dictionary, or in 'os.environ' if it's not in - 'local_vars'. 'os.environ' is first checked/ augmented to guarantee - that it contains certain values: see '_check_environ()'. Raise - ValueError for any variables not found in either 'local_vars' or - 'os.environ'. + occurrence of '$' followed by a name is considered a variable, and + variable is substituted by the value found in the 'local_vars' + dictionary, or in 'os.environ' if it's not in 'local_vars'. + 'os.environ' is first checked/augmented to guarantee that it contains + certain values: see 'check_environ()'. Raise ValueError for any + variables not found in either 'local_vars' or 'os.environ'. """ check_environ() def _subst (match, local_vars=local_vars): @@ -154,7 +153,10 @@ def subst_vars (str, local_vars): else: return os.environ[var_name] - return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str) + try: + return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str) + except KeyError, var: + raise ValueError, "invalid variable '$%s'" % var # subst_vars () -- cgit v1.2.1 From 016c9e3595524f13467fe2192e5509f9469725a6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 20:37:56 +0000 Subject: Added 'byte_compile(): an all-singing, all-dancing wrapper around the standard 'py_compile.compile()' function. Laundry list of features: - handles standard Distutils 'force', 'verbose', 'dry_run' flags - handles various levels of optimization: can compile directly in this interpreter process, or write a temporary script that is then executed by a new interpreter with the appropriate flags - can rewrite the source filename by stripping an optional prefix and preprending an optional base dir. --- util.py | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 1a1ec6d8..03234847 100644 --- a/util.py +++ b/util.py @@ -8,8 +8,9 @@ one of the other *util.py modules. __revision__ = "$Id$" -import sys, os, string, re, shutil -from distutils.errors import * +import sys, os, string, re +from distutils.errors import DistutilsPlatformError +from distutils.dep_util import newer from distutils.spawn import spawn @@ -289,3 +290,129 @@ def strtobool (val): return 0 else: raise ValueError, "invalid truth value %s" % `val` + + +def byte_compile (py_files, + optimize=0, force=0, + prefix=None, base_dir=None, + verbose=1, dry_run=0, + direct=None): + """Byte-compile a collection of Python source files to either + .pyc or .pyo files in the same directory. 'optimize' must be + one of the following: + 0 - don't optimize (generate .pyc) + 1 - normal optimization (like "python -O") + 2 - extra optimization (like "python -OO") + If 'force' is true, all files are recompiled regardless of + timestamps. + + The source filename encoded in each bytecode file defaults to the + filenames listed in 'py_files'; you can modify these with 'prefix' and + 'basedir'. 'prefix' is a string that will be stripped off of each + source filename, and 'base_dir' is a directory name that will be + prepended (after 'prefix' is stripped). You can supply either or both + (or neither) of 'prefix' and 'base_dir', as you wish. + + If 'verbose' is true, prints out a report of each file. If 'dry_run' + is true, doesn't actually do anything that would affect the filesystem. + + Byte-compilation is either done directly in this interpreter process + with the standard py_compile module, or indirectly by writing a + temporary script and executing it. Normally, you should let + 'byte_compile()' figure out to use direct compilation or not (see + the source for details). The 'direct' flag is used by the script + generated in indirect mode; unless you know what you're doing, leave + it set to None. + """ + + # First, if the caller didn't force us into direct or indirect mode, + # figure out which mode we should be in. We take a conservative + # approach: choose direct mode *only* if the current interpreter is + # in debug mode and optimize is 0. If we're not in debug mode (-O + # or -OO), we don't know which level of optimization this + # interpreter is running with, so we can't do direct + # byte-compilation and be certain that it's the right thing. Thus, + # always compile indirectly if the current interpreter is in either + # optimize mode, or if either optimization level was requested by + # the caller. + if direct is None: + direct = (__debug__ and optimize == 0) + + # "Indirect" byte-compilation: write a temporary script and then + # run it with the appropriate flags. + if not direct: + from tempfile import mktemp + script_name = mktemp(".py") + if verbose: + print "writing byte-compilation script '%s'" % script_name + if not dry_run: + script = open(script_name, "w") + + script.write("""\ +from distutils.util import byte_compile +files = [ +""") + script.write(string.join(map(repr, py_files), ",\n") + "]\n") + script.write(""" +byte_compile(files, optimize=%s, force=%s, + prefix=%s, base_dir=%s, + verbose=%s, dry_run=0, + direct=1) +""" % (`optimize`, `force`, `prefix`, `base_dir`, `verbose`)) + + script.close() + + cmd = [sys.executable, script_name] + if optimize == 1: + cmd.insert(1, "-O") + elif optimize == 2: + cmd.insert(1, "-OO") + spawn(cmd, verbose=verbose, dry_run=dry_run) + + # "Direct" byte-compilation: use the py_compile module to compile + # right here, right now. Note that the script generated in indirect + # mode simply calls 'byte_compile()' in direct mode, a weird sort of + # cross-process recursion. Hey, it works! + else: + from py_compile import compile + + for file in py_files: + if file[-3:] != ".py": + raise ValueError, \ + "invalid filename: %s doesn't end with '.py'" % `file` + + # Terminology from the py_compile module: + # cfile - byte-compiled file + # dfile - purported source filename (same as 'file' by default) + cfile = file + (__debug__ and "c" or "o") + dfile = file + if prefix: + if file[:len(prefix)] != prefix: + raise ValueError, \ + ("invalid prefix: filename %s doesn't start with %s" + % (`file`, `prefix`)) + dfile = dfile[len(prefix):] + if base_dir: + dfile = os.path.join(base_dir, dfile) + + cfile_base = os.path.basename(cfile) + if direct: + if force or newer(file, cfile): + if verbose: + print "byte-compiling %s to %s" % (file, cfile_base) + if not dry_run: + compile(file, cfile, dfile) + else: + if verbose: + print "skipping byte-compilation of %s to %s" % \ + (file, cfile_base) + +# byte_compile () + + +if __name__ == "__main__": + import glob + f = glob.glob("command/*.py") + byte_compile(f, optimize=0, prefix="command/", base_dir="/usr/lib/python") + #byte_compile(f, optimize=1) + #byte_compile(f, optimize=2) -- cgit v1.2.1 From c2c1bbc37b9dea94262111b0d050c6d6e9fd024e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 20:39:09 +0000 Subject: Reduced the 'bytecompile()' method to a one-line wrapper around 'util.byte_compile()'. Currently just reproduces the existing functionality -- doesn't use any of the fancy features in the new 'byte_compile()'. --- command/install_lib.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index 2c92f3fe..6ad0a54b 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -5,6 +5,7 @@ __revision__ = "$Id$" import sys, os, string from distutils.core import Command from distutils.dir_util import copy_tree +from distutils.util import byte_compile class install_lib (Command): @@ -82,21 +83,9 @@ class install_lib (Command): return outfiles def bytecompile (self, files): - # XXX hey! we can't control whether we optimize or not; that's up - # to the invocation of the current Python interpreter (at least - # according to the py_compile docs). That sucks. - if self.compile: - from py_compile import compile - - for f in files: - # only compile the file if it is actually a .py file - if f[-3:] == '.py': - out_fn = f + (__debug__ and "c" or "o") - compile_msg = "byte-compiling %s to %s" % \ - (f, os.path.basename(out_fn)) - skip_msg = "skipping byte-compilation of %s" % f - self.make_file(f, out_fn, compile, (f,), - compile_msg, skip_msg) + byte_compile(files, + force=self.force, + verbose=self.verbose, dry_run=self.dry_run) # -- Utility methods ----------------------------------------------- -- cgit v1.2.1 From 9a39df987d712514e23769e43651dd72768e0289 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 1 Oct 2000 23:49:30 +0000 Subject: Tweaked 'byte_compile()' so it silently skips non-Python files, rather than blowing up. --- util.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/util.py b/util.py index 03234847..563e47c4 100644 --- a/util.py +++ b/util.py @@ -297,9 +297,10 @@ def byte_compile (py_files, prefix=None, base_dir=None, verbose=1, dry_run=0, direct=None): - """Byte-compile a collection of Python source files to either - .pyc or .pyo files in the same directory. 'optimize' must be - one of the following: + """Byte-compile a collection of Python source files to either .pyc + or .pyo files in the same directory. 'py_files' is a list of files + to compile; any files that don't end in ".py" are silently skipped. + 'optimize' must be one of the following: 0 - don't optimize (generate .pyc) 1 - normal optimization (like "python -O") 2 - extra optimization (like "python -OO") @@ -378,8 +379,9 @@ byte_compile(files, optimize=%s, force=%s, for file in py_files: if file[-3:] != ".py": - raise ValueError, \ - "invalid filename: %s doesn't end with '.py'" % `file` + # This lets us be lazy and not filter filenames in + # the "install_lib" command. + continue # Terminology from the py_compile module: # cfile - byte-compiled file -- cgit v1.2.1 From 1e229b65b2fd026127f9507e1a5eb2bf5716b6d3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 1 Oct 2000 23:50:13 +0000 Subject: From 'run()', only call 'bytecompile()' if we actually have pure Python modules to compile. --- command/install_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index 6ad0a54b..2396eeda 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -57,7 +57,7 @@ class install_lib (Command): outfiles = self.install() # (Optionally) compile .py to .pyc - if outfiles is not None: + if outfiles is not None and self.distribution.has_pure_modules(): self.bytecompile(outfiles) # run () -- cgit v1.2.1 From 0c6e44515410c371cbb4f4d3589f8554e7f217ab Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 2 Oct 2000 02:09:55 +0000 Subject: Remove the temporary byte-compilation script when we're done with it. --- util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/util.py b/util.py index 563e47c4..8a8b77af 100644 --- a/util.py +++ b/util.py @@ -369,6 +369,7 @@ byte_compile(files, optimize=%s, force=%s, elif optimize == 2: cmd.insert(1, "-OO") spawn(cmd, verbose=verbose, dry_run=dry_run) + os.remove(script_name) # "Direct" byte-compilation: use the py_compile module to compile # right here, right now. Note that the script generated in indirect -- cgit v1.2.1 From af0b04bbeb4b4811fcf2892856cfc4154841bda7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 2 Oct 2000 02:15:08 +0000 Subject: Finished the overhaul of byte-compilation options: there's now a 6-way choice between (compile, no-compile) * (optimize=0, optimize=1, optimize=2). Details: - added --no-compile option to complement --compile, which has been there for ages - changed --optimize (which never worked) to a value option, which expects 0, 1, or 2 - renamed 'bytecompile()' method to 'byte_compile()', and beefed it up to handle both 'compile' and 'optimize' options - fix '_bytecode_filenames()' to respect the new options --- command/install_lib.py | 81 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 15 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index 2396eeda..80da3acc 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -3,24 +3,44 @@ __revision__ = "$Id$" import sys, os, string +from types import IntType from distutils.core import Command +from distutils.errors import DistutilsOptionError from distutils.dir_util import copy_tree -from distutils.util import byte_compile class install_lib (Command): description = "install all Python modules (extensions and pure Python)" + # The byte-compilation options are a tad confusing. Here are the + # possible scenarios: + # 1) no compilation at all (--no-compile --no-optimize) + # 2) compile .pyc only (--compile --no-optimize; default) + # 3) compile .pyc and "level 1" .pyo (--compile --optimize) + # 4) compile "level 1" .pyo only (--no-compile --optimize) + # 5) compile .pyc and "level 2" .pyo (--compile --optimize-more) + # 6) compile "level 2" .pyo only (--no-compile --optimize-more) + # + # The UI for this is two option, 'compile' and 'optimize'. + # 'compile' is strictly boolean, and only decides whether to + # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and + # decides both whether to generate .pyo files and what level of + # optimization to use. + user_options = [ ('install-dir=', 'd', "directory to install to"), ('build-dir=','b', "build directory (where to install from)"), ('force', 'f', "force installation (overwrite existing files)"), - ('compile', 'c', "compile .py to .pyc"), - ('optimize', 'o', "compile .py to .pyo (optimized)"), + ('compile', 'c', "compile .py to .pyc [default]"), + ('no-compile', None, "don't compile .py files"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), ('skip-build', None, "skip the build steps"), ] - boolean_options = ['force', 'compile', 'optimize', 'skip-build'] + boolean_options = ['force', 'compile', 'skip-build'] + negative_opt = {'no-compile' : 'compile'} def initialize_options (self): @@ -28,8 +48,8 @@ class install_lib (Command): self.install_dir = None self.build_dir = None self.force = 0 - self.compile = 1 - self.optimize = 1 + self.compile = None + self.optimize = None self.skip_build = None def finalize_options (self): @@ -41,11 +61,25 @@ class install_lib (Command): ('build_lib', 'build_dir'), ('install_lib', 'install_dir'), ('force', 'force'), - ('compile_py', 'compile'), - ('optimize_py', 'optimize'), + ('compile', 'compile'), + ('optimize', 'optimize'), ('skip_build', 'skip_build'), ) + if self.compile is None: + self.compile = 1 + if self.optimize is None: + self.optimize = 0 + + print "install_lib: compile=%s, optimize=%s" % \ + (`self.compile`, `self.optimize`) + if type(self.optimize) is not IntType: + try: + self.optimize = int(self.optimize) + assert 0 <= self.optimize <= 2 + except (ValueError, AssertionError): + raise DistutilsOptionError, "optimize must be 0, 1, or 2" + def run (self): # Make sure we have built everything we need first @@ -58,7 +92,7 @@ class install_lib (Command): # (Optionally) compile .py to .pyc if outfiles is not None and self.distribution.has_pure_modules(): - self.bytecompile(outfiles) + self.byte_compile(outfiles) # run () @@ -82,10 +116,25 @@ class install_lib (Command): return return outfiles - def bytecompile (self, files): - byte_compile(files, - force=self.force, - verbose=self.verbose, dry_run=self.dry_run) + def byte_compile (self, files): + from distutils.util import byte_compile + + # Get the "--root" directory supplied to the "install" command, + # and use it as a prefix to strip off the purported filename + # encoded in bytecode files. This is far from complete, but it + # should at least generate usable bytecode in RPM distributions. + install_root = self.get_finalized_command('install').root + + if self.compile: + byte_compile(files, optimize=0, + force=self.force, + prefix=install_root, + verbose=self.verbose, dry_run=self.dry_run) + if self.optimize > 0: + byte_compile(files, optimize=self.optimize, + force=self.force, + prefix=install_root, + verbose=self.verbose, dry_run=self.dry_run) # -- Utility methods ----------------------------------------------- @@ -111,8 +160,10 @@ class install_lib (Command): def _bytecode_filenames (self, py_filenames): bytecode_files = [] for py_file in py_filenames: - bytecode = py_file + (__debug__ and "c" or "o") - bytecode_files.append(bytecode) + if self.compile: + bytecode_files.append(py_file + "c") + if self.optmize > 0: + bytecode_files.append(py_file + "o") return bytecode_files -- cgit v1.2.1 From 6bccb12b568be281d7e54b5ef3c5ff67ca6163bc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 2 Oct 2000 02:16:04 +0000 Subject: Added --compile, --optimize options so users have an easy way to instruct the "install_lib" command from the command-line. --- command/install.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/command/install.py b/command/install.py index e9528c63..303ae4c0 100644 --- a/command/install.py +++ b/command/install.py @@ -90,6 +90,15 @@ class install (Command): ('install-data=', None, "installation directory for data files"), + # Byte-compilation options -- see install_lib.py for details, as + # these are duplicated from there (but only install_lib does + # anything with them). + ('compile', 'c', "compile .py to .pyc [default]"), + ('no-compile', None, "don't compile .py files"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + # Miscellaneous control options ('force', 'f', "force installation (overwrite any existing files)"), @@ -135,6 +144,9 @@ class install (Command): self.install_scripts = None self.install_data = None + self.compile = None + self.optimize = None + # These two are for putting non-packagized distributions into their # own directory and creating a .pth file if it makes sense. # 'extra_path' comes from the setup file; 'install_path_file' can -- cgit v1.2.1 From 7acf599bd33799d9e5b000b8d7e233a13464211d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 2 Oct 2000 02:19:04 +0000 Subject: Added the ability to do byte-compilation at build time, currently off by default (since compiling at install time works just fine). Details: - added 'compile' and 'optimize' options - added 'byte_compile()' method - changed 'get_outputs()' so it includes bytecode files A lot of the code added is very similar to code in install_lib.py; would be nice to factor it out further. --- command/build_py.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 6a8a7f43..6fd4417f 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -20,10 +20,16 @@ class build_py (Command): user_options = [ ('build-lib=', 'd', "directory to \"build\" (copy) to"), + ('compile', 'c', "compile .py to .pyc"), + ('no-compile', None, "don't compile .py files [default]"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), ('force', 'f', "forcibly build everything (ignore file timestamps)"), ] - boolean_options = ['force'] + boolean_options = ['compile', 'force'] + negative_opt = {'no-compile' : 'compile'} def initialize_options (self): @@ -31,6 +37,8 @@ class build_py (Command): self.py_modules = None self.package = None self.package_dir = None + self.compile = 0 + self.optimize = 0 self.force = None def finalize_options (self): @@ -44,6 +52,14 @@ class build_py (Command): self.py_modules = self.distribution.py_modules self.package_dir = self.distribution.package_dir + # Ick, copied straight from install_lib.py (fancy_getopt needs a + # type system! Hell, *everything* needs a type system!!!) + if type(self.optimize) is not IntType: + try: + self.optimize = int(self.optimize) + assert 0 <= self.optimize <= 2 + except (ValueError, AssertionError): + raise DistutilsOptionError, "optimize must be 0, 1, or 2" def run (self): @@ -87,6 +103,8 @@ class build_py (Command): else: self.build_packages() + self.byte_compile(self.get_outputs(include_bytecode=0)) + # run () @@ -284,13 +302,19 @@ class build_py (Command): return apply(os.path.join, outfile_path) - def get_outputs (self): + def get_outputs (self, include_bytecode=1): modules = self.find_all_modules() outputs = [] for (package, module, module_file) in modules: package = string.split(package, '.') - outputs.append(self.get_module_outfile(self.build_lib, - package, module)) + filename = self.get_module_outfile(self.build_lib, package, module) + outputs.append(filename) + if include_bytecode: + if self.compile: + outputs.append(filename + "c") + if self.optimize > 0: + outputs.append(filename + "o") + return outputs @@ -347,5 +371,27 @@ class build_py (Command): self.build_module(module, module_file, package) # build_packages () - + + + def byte_compile (self, files): + from distutils.util import byte_compile + prefix = self.build_lib + if prefix[-1] != os.sep: + prefix = prefix + os.sep + + # XXX this code is essentially the same as the 'byte_compile() + # method of the "install_lib" command, except for the determination + # of the 'prefix' string. Hmmm. + + if self.compile: + byte_compile(files, optimize=0, + force=self.force, + prefix=prefix, + verbose=self.verbose, dry_run=self.dry_run) + if self.optimize > 0: + byte_compile(files, optimize=self.optimize, + force=self.force, + prefix=prefix, + verbose=self.verbose, dry_run=self.dry_run) + # class build_py -- cgit v1.2.1 From d2497ea9a9cfcf3a2869d5251437cfea6ff473cc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 2 Oct 2000 02:25:51 +0000 Subject: Typo fix. --- command/install_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index 80da3acc..804dcffa 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -162,7 +162,7 @@ class install_lib (Command): for py_file in py_filenames: if self.compile: bytecode_files.append(py_file + "c") - if self.optmize > 0: + if self.optimize > 0: bytecode_files.append(py_file + "o") return bytecode_files -- cgit v1.2.1 From 94052cd86e69fe242219496b66427f1e429994df Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 3 Oct 2000 03:31:05 +0000 Subject: Added a long-winded comment (and commented-out comment to go with out) about how it would be nice to write absolute paths to the temporary byte-compilation script, but this doesn't work because it screws up the trailing-slash trickery done to 'prefix' in build_py's 'byte_compile()' method. Fixed to use 'execute()' instead of 'os.remove()' to remove the temporary script: now it doesn't blow up in dry-run mode! --- util.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/util.py b/util.py index 8a8b77af..7522ee24 100644 --- a/util.py +++ b/util.py @@ -353,6 +353,21 @@ def byte_compile (py_files, from distutils.util import byte_compile files = [ """) + + # XXX would be nice to write absolute filenames, just for + # safety's sake (script should be more robust in the face of + # chdir'ing before running it). But this requires abspath'ing + # 'prefix' as well, and that breaks the hack in build_lib's + # 'byte_compile()' method that carefully tacks on a trailing + # slash (os.sep really) to make sure the prefix here is "just + # right". This whole prefix business is rather delicate -- the + # problem is that it's really a directory, but I'm treating it + # as a dumb string, so trailing slashes and so forth matter. + + #py_files = map(os.path.abspath, py_files) + #if prefix: + # prefix = os.path.abspath(prefix) + script.write(string.join(map(repr, py_files), ",\n") + "]\n") script.write(""" byte_compile(files, optimize=%s, force=%s, @@ -369,7 +384,8 @@ byte_compile(files, optimize=%s, force=%s, elif optimize == 2: cmd.insert(1, "-OO") spawn(cmd, verbose=verbose, dry_run=dry_run) - os.remove(script_name) + execute(os.remove, (script_name,), "removing %s" % script_name, + verbose=verbose, dry_run=dry_run) # "Direct" byte-compilation: use the py_compile module to compile # right here, right now. Note that the script generated in indirect -- cgit v1.2.1 From 5e53de4c1f29689ed46a3edcedab0594b2ab9b7e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 3 Oct 2000 03:31:52 +0000 Subject: Fixed so --no-compile is a negative alias for --compile. --- command/install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/install.py b/command/install.py index 303ae4c0..330f3248 100644 --- a/command/install.py +++ b/command/install.py @@ -116,6 +116,7 @@ class install (Command): ] boolean_options = ['force', 'skip-build'] + negative_opt = {'no-compile' : 'compile'} def initialize_options (self): -- cgit v1.2.1 From 7bfd1e33100e73e990f07bffe02e06f5f8cd2e60 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 3 Oct 2000 03:32:37 +0000 Subject: Remove some debugging prints. --- command/install_lib.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index 804dcffa..6c4c7fe9 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -71,8 +71,6 @@ class install_lib (Command): if self.optimize is None: self.optimize = 0 - print "install_lib: compile=%s, optimize=%s" % \ - (`self.compile`, `self.optimize`) if type(self.optimize) is not IntType: try: self.optimize = int(self.optimize) -- cgit v1.2.1 From f07662ad4804864ca561414b1aeab71901df8c64 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 3 Oct 2000 03:48:43 +0000 Subject: Bump version to 1.0. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 23315cde..20736176 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "1.0pre" +__version__ = "1.0" -- cgit v1.2.1 From aa3edfa3ec76a4cc13fca1dee25fda6f0baeb771 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 12 Oct 2000 19:31:13 +0000 Subject: Recreated after installer source code changes. This should close SF bug (patch) http://sourceforge.net/patch/?func=detailpatch&patch_id=101844&group_id=5470 --- command/bdist_wininst.py | 492 +++++++++++++++++++++++------------------------ 1 file changed, 246 insertions(+), 246 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index afe6955e..b45089b7 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -218,9 +218,9 @@ if __name__ == '__main__': EXEDATA = """\ TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v -ZGUuDQ0KJAAAAAAAAADqs5WMrtL7367S+9+u0vvf1c7336/S+98tzvXfrNL731Hy/9+s0vvfzM3o -36bS+9+u0vrf89L7367S+9+j0vvfUfLx36PS+99p1P3fr9L731JpY2iu0vvfAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwD9e9Q5AAAAAAAAAADgAA8BCwEGAABAAAAAEAAAAJAAAADVAAAA +ZGUuDQ0KJAAAAAAAAABwj7aMNO7Y3zTu2N807tjfT/LU3zXu2N+38tbfNu7Y39zx3N827tjfVvHL +3zzu2N807tnfae7Y3zTu2N857tjf3PHS3znu2N+M6N7fNe7Y31JpY2g07tjfAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAUEUAAEwBAwAu6+E5AAAAAAAAAADgAA8BCwEGAABAAAAAEAAAAJAAAPDUAAAA oAAAAOAAAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAA8AAAAAQAAAAAAAACAAAAAAAQAAAQ AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAw4QAAcAEAAADgAAAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -233,251 +233,251 @@ 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 -- cgit v1.2.1 From 3440959467b0aa5fa7f21378e1657a9bbc047b67 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Oct 2000 03:40:20 +0000 Subject: Bastian Kleineidam: fix up any supplied command-line options. --- command/config.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/command/config.py b/command/config.py index a13055cf..18d7992a 100644 --- a/command/config.py +++ b/command/config.py @@ -14,6 +14,7 @@ this header file lives". __revision__ = "$Id$" import sys, os, string, re +from types import * from distutils.core import Command from distutils.errors import DistutilsExecError @@ -69,7 +70,21 @@ class config (Command): self.temp_files = [] def finalize_options (self): - pass + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + elif type(self.include_dirs) is StringType: + self.include_dirs = string.split(self.include_dirs, os.pathsep) + + if self.libraries is None: + self.libraries = [] + elif type(self.libraries) is StringType: + self.libraries = [self.libraries] + + if self.library_dirs is None: + self.library_dirs = [] + elif type(self.library_dirs) is StringType: + self.library_dirs = string.split(self.library_dirs, os.pathsep) + def run (self): pass -- cgit v1.2.1 From 806c2d4c0c20efab95c6519462cc891f93d96c0c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Oct 2000 03:47:07 +0000 Subject: Lyle Johnson: use 'normcase()' in addition to 'normpath()' when testing if we actually installed modules to a directory in sys.path. --- command/install.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/command/install.py b/command/install.py index 330f3248..012b9f45 100644 --- a/command/install.py +++ b/command/install.py @@ -497,10 +497,12 @@ class install (Command): "writing list of installed files to '%s'" % self.record) - normalized_path = map(os.path.normpath, sys.path) + sys_path = map(os.path.normpath, sys.path) + sys_path = map(os.path.normcase, sys_path) + install_lib = os.path.normcase(os.path.normpath(self.install_lib)) if (self.warn_dir and not (self.path_file and self.install_path_file) and - os.path.normpath(self.install_lib) not in normalized_path): + install_lib not in sys_path): self.warn(("modules installed to '%s', which is not in " + "Python's module search path (sys.path) -- " + "you'll have to change the search path yourself") % -- cgit v1.2.1 From 89b5ec9d38d646bd3b9e1c7f746f3ba1801315fb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Oct 2000 03:56:42 +0000 Subject: Bastian Kleineidam: make 'check_lib()' more like AC_CHECK_LIB by adding an 'other_libraries()' parameter. --- command/config.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/command/config.py b/command/config.py index 18d7992a..ce6cff8c 100644 --- a/command/config.py +++ b/command/config.py @@ -325,16 +325,19 @@ class config (Command): # check_func () def check_lib (self, library, library_dirs=None, - headers=None, include_dirs=None): + headers=None, include_dirs=None, other_libraries=[]): """Determine if 'library' is available to be linked against, without actually checking that any particular symbols are provided by it. 'headers' will be used in constructing the source file to be compiled, but the only effect of this is to check if all the - header files listed are available. + header files listed are available. Any libraries listed in + 'other_libraries' will be included in the link, in case 'library' + has symbols that depend on other libraries. """ self._check_compiler() return self.try_link("int main (void) { }", - headers, include_dirs, [library], library_dirs) + headers, include_dirs, + [library]+other_libraries, library_dirs) def check_header (self, header, include_dirs=None, library_dirs=None, lang="c"): -- cgit v1.2.1 From 760b926a79252cf94800f28d378990773ed82bde Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Oct 2000 04:06:40 +0000 Subject: Untabified. --- command/bdist.py | 2 +- command/build.py | 2 +- command/build_clib.py | 2 +- command/clean.py | 2 +- command/install.py | 2 +- command/install_data.py | 6 +++--- command/sdist.py | 2 +- dist.py | 10 +++++----- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index c0cb1d3a..a75303e6 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -45,7 +45,7 @@ class bdist (Command): help_options = [ ('help-formats', None, "lists available distribution formats", show_formats), - ] + ] # The following commands do not take a format option from bdist no_format_option = ('bdist_rpm',) diff --git a/command/build.py b/command/build.py index 0fed6b48..cf35b45d 100644 --- a/command/build.py +++ b/command/build.py @@ -47,7 +47,7 @@ class build (Command): help_options = [ ('help-compiler', None, "list available compilers", show_compilers), - ] + ] def initialize_options (self): self.build_base = 'build' diff --git a/command/build_clib.py b/command/build_clib.py index 2726b975..063da915 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -53,7 +53,7 @@ class build_clib (Command): help_options = [ ('help-compiler', None, "list available compilers", show_compilers), - ] + ] def initialize_options (self): self.build_clib = None diff --git a/command/clean.py b/command/clean.py index fb8822f7..b4a9be45 100644 --- a/command/clean.py +++ b/command/clean.py @@ -60,7 +60,7 @@ class clean (Command): # remove build directories for directory in (self.build_lib, self.bdist_base, - self.build_scripts): + self.build_scripts): if os.path.exists(directory): remove_tree(directory, self.verbose, self.dry_run) else: diff --git a/command/install.py b/command/install.py index 012b9f45..6aee1b35 100644 --- a/command/install.py +++ b/command/install.py @@ -498,7 +498,7 @@ class install (Command): self.record) sys_path = map(os.path.normpath, sys.path) - sys_path = map(os.path.normcase, sys_path) + sys_path = map(os.path.normcase, sys_path) install_lib = os.path.normcase(os.path.normpath(self.install_lib)) if (self.warn_dir and not (self.path_file and self.install_path_file) and diff --git a/command/install_data.py b/command/install_data.py index d9a48693..dba108af 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -38,10 +38,10 @@ class install_data (Command): def finalize_options (self): self.set_undefined_options('install', - ('install_data', 'install_dir'), - ('root', 'root'), + ('install_data', 'install_dir'), + ('root', 'root'), ('force', 'force'), - ) + ) def run (self): self.mkpath(self.install_dir) diff --git a/command/sdist.py b/command/sdist.py index 5116868e..1f9e9184 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -74,7 +74,7 @@ class sdist (Command): help_options = [ ('help-formats', None, "list available distribution formats", show_formats), - ] + ] negative_opt = {'no-defaults': 'use-defaults', 'no-prune': 'prune' } diff --git a/dist.py b/dist.py index 92d390f7..abbc1600 100644 --- a/dist.py +++ b/dist.py @@ -284,7 +284,7 @@ class Distribution: user_filename = ".pydistutils.cfg" else: user_filename = "pydistutils.cfg" - + # And look for the user config file if os.environ.has_key('HOME'): user_file = os.path.join(os.environ.get('HOME'), user_filename) @@ -461,8 +461,8 @@ class Distribution: negative_opt = copy(negative_opt) negative_opt.update(cmd_class.negative_opt) - # Check for help_options in command class. They have a different - # format (tuple of four) so we need to preprocess them here. + # Check for help_options in command class. They have a different + # format (tuple of four) so we need to preprocess them here. if (hasattr(cmd_class, 'help_options') and type(cmd_class.help_options) is ListType): help_options = fix_help_options(cmd_class.help_options) @@ -487,7 +487,7 @@ class Distribution: for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): help_option_found=1 - #print "showing help for option %s of command %s" % \ + #print "showing help for option %s of command %s" % \ # (help_option[0],cmd_class) if callable(func): @@ -563,7 +563,7 @@ class Distribution: return # _show_help () - + def handle_display_options (self, option_order): """If there were any non-global "display-only" options -- cgit v1.2.1 From ac94b8701f445c75e671f29ed64ea3b577a9b3a5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Oct 2000 04:07:39 +0000 Subject: Removed debugging code at bottom. --- util.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/util.py b/util.py index 7522ee24..dd9de854 100644 --- a/util.py +++ b/util.py @@ -427,11 +427,3 @@ byte_compile(files, optimize=%s, force=%s, (file, cfile_base) # byte_compile () - - -if __name__ == "__main__": - import glob - f = glob.glob("command/*.py") - byte_compile(f, optimize=0, prefix="command/", base_dir="/usr/lib/python") - #byte_compile(f, optimize=1) - #byte_compile(f, optimize=2) -- cgit v1.2.1 From dc35143fa2af5382aa1428c9da4b140237bf9391 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 15 Oct 2000 19:20:20 +0000 Subject: Bump version to 1.0.1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 20736176..55951090 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "1.0" +__version__ = "1.0.1" -- cgit v1.2.1 From f64d6b6db2fa6882b19b75d53156c4a541b53588 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 11 Nov 2000 02:47:11 +0000 Subject: Jack Jansen: added 'get_command_list()' method, and Mac-specific code to use it to generate a dialog for users to specify the command-line (because providing a command-line with MacPython is so awkward). --- dist.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/dist.py b/dist.py index abbc1600..fe728c3b 100644 --- a/dist.py +++ b/dist.py @@ -172,6 +172,12 @@ class Distribution: # operations, we just check the 'have_run' dictionary and carry on. # It's only safe to query 'have_run' for a command class that has # been instantiated -- a false value will be inserted when the + if sys.platform == 'mac': + import EasyDialogs + cmdlist = self.get_command_list() + self.script_args = EasyDialogs.GetArgv( + self.global_options + self.display_options, cmdlist) + # command object is created, and replaced with a true value when # the command is successfully run. Thus it's probably best to use # '.get()' rather than a straight lookup. @@ -657,6 +663,38 @@ class Distribution: # print_commands () + def get_command_list (self): + """Get a list of (command, description) tuples. + The list is divided into "standard commands" (listed in + distutils.command.__all__) and "extra commands" (mentioned in + self.cmdclass, but not a standard command). The descriptions come + from the command class attribute 'description'. + """ + # Currently this is only used on Mac OS, for the Mac-only GUI + # Distutils interface (by Jack Jansen) + + import distutils.command + std_commands = distutils.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass.keys(): + if not is_std.get(cmd): + extra_commands.append(cmd) + + rv = [] + for cmd in (std_commands + extra_commands): + klass = self.cmdclass.get(cmd) + if not klass: + klass = self.get_command_class(cmd) + try: + description = klass.description + except AttributeError: + description = "(no description available)" + rv.append((cmd, description)) + return rv # -- Command class/object methods ---------------------------------- -- cgit v1.2.1 From 41f2923e4cb8e646dd04aea546f43752850c091b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 12 Dec 2000 23:11:42 +0000 Subject: Update the code to better reflect recommended style: Use != instead of <> since <> is documented as "obsolescent". Use "is" and "is not" when comparing with None or type objects. --- cygwinccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 5b06d3d7..f40d1a2d 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -241,7 +241,7 @@ class CygwinCCompiler (UnixCCompiler): objects.append(def_file) #end: if ((export_symbols is not None) and - # (target_desc <> self.EXECUTABLE or self.linker_dll == "gcc")): + # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): # who wants symbols and a many times larger output file # should explicitly switch the debug mode on -- cgit v1.2.1 From f749040280c9b9f4151fc1852e7d3ec524d2a1b4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 20 Dec 2000 00:48:12 +0000 Subject: Add forgotten initialization. Fixes bug #120994, "Traceback with DISTUTILS_DEBUG set" --- command/install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/install.py b/command/install.py index 6aee1b35..806da01a 100644 --- a/command/install.py +++ b/command/install.py @@ -146,6 +146,7 @@ class install (Command): self.install_data = None self.compile = None + self.no_compile = None self.optimize = None # These two are for putting non-packagized distributions into their -- cgit v1.2.1 From aa73c5a0fd86980d14bb7bfc07fb41189c2f58cb Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 9 Jan 2001 03:15:47 +0000 Subject: Check in patch #102971: if library_dirs is a string, split it using os.pathsep --- command/build_ext.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index 70e8a734..936a5f63 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -149,6 +149,8 @@ class build_ext (Command): self.libraries = [] if self.library_dirs is None: self.library_dirs = [] + elif type(self.library_dirs) is StringType: + self.library_dirs = string.split(self.library_dirs, os.pathsep) if self.rpath is None: self.rpath = [] -- cgit v1.2.1 From abec99bc72e4002a5348f4a6822f7891bb4f4589 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 11 Jan 2001 15:35:16 +0000 Subject: Delete unused import of pprint module --- sysconfig.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 605e95df..d57b9159 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -362,7 +362,6 @@ def get_config_vars(*args): """ global _config_vars if _config_vars is None: - from pprint import pprint func = globals().get("_init_" + os.name) if func: func() -- cgit v1.2.1 From 12123de064e383654dafdd825e35948ef01c2825 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 15 Jan 2001 16:09:35 +0000 Subject: Fix from Jack Jansen for the Mac and the Metrowerks compiler, posted to the Distutils-SIG and archived at http://mail.python.org/pipermail/distutils-sig/2000-November/001755.html --- ccompiler.py | 3 + dist.py | 16 +++-- mwerkscompiler.py | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+), 6 deletions(-) create mode 100644 mwerkscompiler.py diff --git a/ccompiler.py b/ccompiler.py index b10ee67e..53901b33 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -838,6 +838,7 @@ class CCompiler: # that platform. default_compiler = { 'posix': 'unix', 'nt': 'msvc', + 'mac': 'mwerks', } # Map compiler types to (module_name, class_name) pairs -- ie. where to @@ -853,6 +854,8 @@ compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', "Mingw32 port of GNU C Compiler for Win32"), 'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"), + 'mwerks': ('mwerkscompiler', 'MWerksCompiler', + "MetroWerks CodeWarrior"), } def show_compilers(): diff --git a/dist.py b/dist.py index fe728c3b..41d5dbbc 100644 --- a/dist.py +++ b/dist.py @@ -172,12 +172,6 @@ class Distribution: # operations, we just check the 'have_run' dictionary and carry on. # It's only safe to query 'have_run' for a command class that has # been instantiated -- a false value will be inserted when the - if sys.platform == 'mac': - import EasyDialogs - cmdlist = self.get_command_list() - self.script_args = EasyDialogs.GetArgv( - self.global_options + self.display_options, cmdlist) - # command object is created, and replaced with a true value when # the command is successfully run. Thus it's probably best to use # '.get()' rather than a straight lookup. @@ -375,6 +369,16 @@ class Distribution: execute commands (currently, this only happens if user asks for help). """ + # + # We now have enough information to show the Macintosh dialog that allows + # the user to interactively specify the "command line". + # + if sys.platform == 'mac': + import EasyDialogs + cmdlist = self.get_command_list() + self.script_args = EasyDialogs.GetArgv( + self.global_options + self.display_options, cmdlist) + # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- # because each command will be handled by a different class, and diff --git a/mwerkscompiler.py b/mwerkscompiler.py new file mode 100644 index 00000000..2edc8259 --- /dev/null +++ b/mwerkscompiler.py @@ -0,0 +1,203 @@ +"""distutils.mwerkscompiler + +Contains MWerksCompiler, an implementation of the abstract CCompiler class +for MetroWerks CodeWarrior on the Macintosh. Needs work to support CW on +Windows.""" + +import sys, os, string +from types import * +from distutils.errors import \ + DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options, gen_lib_options +import distutils.util +import distutils.dir_util +import mkcwproject + +class MWerksCompiler (CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'mwerks' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.r'] + _exp_extension = '.exp' + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions) + res_extension = '.rsrc' + obj_extension = '.obj' # Not used, really + static_lib_extension = '.lib' + shared_lib_extension = '.slb' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '' + + + def __init__ (self, + verbose=0, + dry_run=0, + force=0): + + CCompiler.__init__ (self, verbose, dry_run, force) + + + def compile (self, + sources, + output_dir=None, + macros=None, + include_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None): + self.__sources = sources + self.__macros = macros + self.__include_dirs = include_dirs + # Don't need extra_preargs and extra_postargs for CW + + def link (self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): + # First examine a couple of options for things that aren't implemented yet + if not target_desc in (self.SHARED_LIBRARY, self.SHARED_OBJECT): + raise DistutilsPlatformError, 'Can only make SHARED_LIBRARY or SHARED_OBJECT targets on the Mac' + if runtime_library_dirs: + raise DistutilsPlatformError, 'Runtime library dirs not implemented yet' + if extra_preargs or extra_postargs: + raise DistutilsPlatformError, 'Runtime library dirs not implemented yet' + if len(export_symbols) != 1: + raise DistutilsPlatformError, 'Need exactly one export symbol' + # Next there are various things for which we need absolute pathnames. + # This is because we (usually) create the project in a subdirectory of + # where we are now, and keeping the paths relative is too much work right + # now. + sources = map(self._filename_to_abs, self.__sources) + include_dirs = map(self._filename_to_abs, self.__include_dirs) + if objects: + objects = map(self._filename_to_abs, objects) + else: + objects = [] + if build_temp: + build_temp = self._filename_to_abs(build_temp) + else: + build_temp = os.curdir() + if output_dir: + output_filename = os.path.join(output_dir, output_filename) + # The output filename needs special handling: splitting it into dir and + # filename part. Actually I'm not sure this is really needed, but it + # can't hurt. + output_filename = self._filename_to_abs(output_filename) + output_dir, output_filename = os.path.split(output_filename) + # Now we need the short names of a couple of things for putting them + # into the project. + if output_filename[-8:] == '.ppc.slb': + basename = output_filename[:-8] + else: + basename = os.path.strip(output_filename)[0] + projectname = basename + '.mcp' + targetname = basename + xmlname = basename + '.xml' + exportname = basename + '.mcp.exp' + prefixname = 'mwerks_%s_config.h'%basename + # Create the directories we need + distutils.dir_util.mkpath(build_temp, self.verbose, self.dry_run) + distutils.dir_util.mkpath(output_dir, self.verbose, self.dry_run) + # And on to filling in the parameters for the project builder + settings = {} + settings['mac_exportname'] = exportname + settings['mac_outputdir'] = output_dir + settings['mac_dllname'] = output_filename + settings['mac_targetname'] = targetname + settings['sysprefix'] = sys.prefix + settings['mac_sysprefixtype'] = 'Absolute' + sourcefilenames = [] + sourcefiledirs = [] + for filename in sources + objects: + dirname, filename = os.path.split(filename) + sourcefilenames.append(filename) + if not dirname in sourcefiledirs: + sourcefiledirs.append(dirname) + settings['sources'] = sourcefilenames + settings['extrasearchdirs'] = sourcefiledirs + include_dirs + library_dirs + if self.dry_run: + print 'CALLING LINKER IN', os.getcwd() + for key, value in settings.items(): + print '%20.20s %s'%(key, value) + return + # Build the export file + exportfilename = os.path.join(build_temp, exportname) + if self.verbose: + print '\tCreate export file', exportfilename + fp = open(exportfilename, 'w') + fp.write('%s\n'%export_symbols[0]) + fp.close() + # Generate the prefix file, if needed, and put it in the settings + if self.__macros: + prefixfilename = os.path.join(os.getcwd(), os.path.join(build_temp, prefixname)) + fp = open(prefixfilename, 'w') + fp.write('#include "mwerks_plugin_config.h"\n') + for name, value in self.__macros: + if value is None: + fp.write('#define %s\n'%name) + else: + fp.write('#define %s "%s"\n'%(name, value)) + fp.close() + settings['prefixname'] = prefixname + + # Build the XML file. We need the full pathname (only lateron, really) + # because we pass this pathname to CodeWarrior in an AppleEvent, and CW + # doesn't have a clue about our working directory. + xmlfilename = os.path.join(os.getcwd(), os.path.join(build_temp, xmlname)) + if self.verbose: + print '\tCreate XML file', xmlfilename + xmlbuilder = mkcwproject.cwxmlgen.ProjectBuilder(settings) + xmlbuilder.generate() + xmldata = settings['tmp_projectxmldata'] + fp = open(xmlfilename, 'w') + fp.write(xmldata) + fp.close() + # Generate the project. Again a full pathname. + projectfilename = os.path.join(os.getcwd(), os.path.join(build_temp, projectname)) + if self.verbose: + print '\tCreate project file', projectfilename + mkcwproject.makeproject(xmlfilename, projectfilename) + # And build it + if self.verbose: + print '\tBuild project' + mkcwproject.buildproject(projectfilename) + + def _filename_to_abs(self, filename): + # Some filenames seem to be unix-like. Convert to Mac names. +## if '/' in filename and ':' in filename: +## raise DistutilsPlatformError, 'Filename may be Unix or Mac style: %s'%filename +## if '/' in filename: +## filename = macurl2path(filename) + filename = distutils.util.convert_path(filename) + if not os.path.isabs(filename): + curdir = os.getcwd() + filename = os.path.join(curdir, filename) + return filename + + -- cgit v1.2.1 From cacfb4f1472c9b83df8d0b1799834f9f5e322e47 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 16 Jan 2001 03:10:43 +0000 Subject: Add strip_dir argument to the single call to .object_filenames(), to prevent creating files such as build/lib.whatever/Modules/foo.o when given a source filename such as Modules/foo.c. --- ccompiler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ccompiler.py b/ccompiler.py index 53901b33..5e0b3280 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -366,6 +366,7 @@ class CCompiler: """ # Get the list of expected output (object) files objects = self.object_filenames (sources, + strip_dir=1, output_dir=output_dir) if self.force: -- cgit v1.2.1 From 3f4a8e719b6611b77347bb1f8970b5a0141bd400 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 16 Jan 2001 16:16:03 +0000 Subject: Modified version of a patch from Jeremy Kloth, to make .get_outputs() produce a list of unique filenames: "While attempting to build an RPM using distutils on Python 2.0, rpm complained about duplicate files. The following patch fixed that problem. --- command/install.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 806da01a..40e00705 100644 --- a/command/install.py +++ b/command/install.py @@ -530,7 +530,11 @@ class install (Command): outputs = [] for cmd_name in self.get_sub_commands(): cmd = self.get_finalized_command(cmd_name) - outputs.extend(cmd.get_outputs()) + # Add the contents of cmd.get_outputs(), ensuring + # that outputs doesn't contain duplicate entries + for filename in cmd.get_outputs(): + if filename not in outputs: + outputs.append(filename) return outputs -- cgit v1.2.1 From e26cdf35269dc6e049dd19c5b39bd312cddeb81a Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 16 Jan 2001 16:33:28 +0000 Subject: Fix bugs with integer-valued variables when parsing Makefiles. Values for done[n] can be integers as well as strings, but the code concatenates them with strings (fixed by adding a str()) and calls string.strip() on them (fixed by rearranging the logic) (Presumably this wasn't noticed previously because parse_makefile() was only called on Modules/Makefile, which contains no integer-valued variables.) --- sysconfig.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index d57b9159..4da37689 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -204,13 +204,15 @@ def parse_makefile(fn, g=None): n = m.group(1) if done.has_key(n): after = value[m.end():] - value = value[:m.start()] + done[n] + after + value = value[:m.start()] + str(done[n]) + after if "$" in after: notdone[name] = value else: try: value = string.atoi(value) - except ValueError: pass - done[name] = string.strip(value) + except ValueError: + done[name] = string.strip(value) + else: + done[name] = value del notdone[name] elif notdone.has_key(n): # get it on a subsequent round @@ -223,8 +225,10 @@ def parse_makefile(fn, g=None): notdone[name] = value else: try: value = string.atoi(value) - except ValueError: pass - done[name] = string.strip(value) + except ValueError: + done[name] = string.strip(value) + else: + done[name] = value del notdone[name] else: # bogus variable reference; just drop it since we can't deal -- cgit v1.2.1 From 6aef8efc56535f695fdbbddcb651bf715bfbc79e Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 17 Jan 2001 15:16:52 +0000 Subject: Patch #103279: sysconfig.py always looks for versions of files in sys.prefix + 'config/Makefile'. When building Python for the first time, these files aren't there, so the files from the build tree have to be used instead; this file adds an entry point for specifying that the build tree files should be used. (Perhaps 'set_python_build' should should be preceded with an underscore?) --- sysconfig.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 4da37689..3ae0a5f9 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -19,6 +19,19 @@ from errors import DistutilsPlatformError PREFIX = os.path.normpath(sys.prefix) EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +# Boolean; if it's true, we're still building Python, so +# we use different (hard-wired) directories. + +python_build = 0 + +def set_python_build(): + """Set the python_build flag to true; this means that we're + building Python itself. Only called from the setup.py script + shipped with Python. + """ + + global python_build + python_build = 1 def get_python_inc(plat_specific=0, prefix=None): """Return the directory containing installed Python header files. @@ -34,6 +47,8 @@ def get_python_inc(plat_specific=0, prefix=None): if prefix is None: prefix = (plat_specific and EXEC_PREFIX or PREFIX) if os.name == "posix": + if python_build: + return "Include/" return os.path.join(prefix, "include", "python" + sys.version[:3]) elif os.name == "nt": return os.path.join(prefix, "Include") # include or Include? @@ -119,12 +134,15 @@ def customize_compiler (compiler): def get_config_h_filename(): """Return full pathname of installed config.h file.""" - inc_dir = get_python_inc(plat_specific=1) + if python_build: inc_dir = '.' + else: inc_dir = get_python_inc(plat_specific=1) return os.path.join(inc_dir, "config.h") def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" + if python_build: + return './Modules/Makefile' lib_dir = get_python_lib(plat_specific=1, standard_lib=1) return os.path.join(lib_dir, "config", "Makefile") -- cgit v1.2.1 From dd5ccc0082a1bf655dd242d3881620cd88f0d980 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 19 Jan 2001 16:26:12 +0000 Subject: Patch #103220 from Jason Tishler: This patch adds support for Cygwin to util.get_platform(). A Cygwin specific case is needed due to the format of Cygwin's uname command, which contains '/' characters. --- util.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util.py b/util.py index dd9de854..80e48143 100644 --- a/util.py +++ b/util.py @@ -54,6 +54,11 @@ def get_platform (): # fall through to standard osname-release-machine representation elif osname[:4] == "irix": # could be "irix64"! return "%s-%s" % (osname, release) + elif osname[:6] == "cygwin": + rel_re = re.compile (r'[\d.]+') + m = rel_re.match(release) + if m: + release = m.group() return "%s-%s-%s" % (osname, release, machine) -- cgit v1.2.1 From 86f47bdacaf41dd4535e115c74162bfcfb87f267 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 24 Jan 2001 15:43:09 +0000 Subject: Part of patch #102409: special cases for Cygwin: Lib/distutils/command/build_ext.py(build_ext.finalize_options): Add Cygwin specific code to append Python's library directory to the extension's list of library directories. (build_ext.get_libraries): Add Cygwin specific code to append Python's (import) library to the extension's list of libraries. --- command/build_ext.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index 936a5f63..2876c359 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -163,6 +163,17 @@ class build_ext (Command): self.build_temp = os.path.join(self.build_temp, "Debug") else: self.build_temp = os.path.join(self.build_temp, "Release") + + # for extensions under Cygwin Python's library directory must be + # appended to library_dirs + if sys.platform[:6] == 'cygwin': + if string.find(sys.executable, sys.exec_prefix) != -1: + # building third party extensions + self.library_dirs.append(os.path.join(sys.prefix, "lib", "python" + sys.version[:3], "config")) + else: + # building python standard extensions + self.library_dirs.append('.') + # finalize_options () @@ -576,6 +587,13 @@ class build_ext (Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] + elif sys.platform[:6] == "cygwin": + template = "python%d.%d" + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] else: return ext.libraries -- cgit v1.2.1 From 11b83157df263f7aa40e1d42536200ee07a34b32 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Wed, 24 Jan 2001 17:17:20 +0000 Subject: There is no more Modules/Makefile, use toplevel Makefile. --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 3ae0a5f9..ae9b37f3 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -142,7 +142,7 @@ def get_config_h_filename(): def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: - return './Modules/Makefile' + return './Makefile' lib_dir = get_python_lib(plat_specific=1, standard_lib=1) return os.path.join(lib_dir, "config", "Makefile") -- cgit v1.2.1 From 5634233ca85a0f27b1feca9ccf899b688c276cbf Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 25 Jan 2001 20:10:32 +0000 Subject: In subst_vars(), change the name of the argument from str to s to prevent binding for str from masking use of builtin str in nested function. (This is the only case I found in the standard library where a local shadows a global or builtin. There may be others, but the regression test doesn't catch them.) --- util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 80e48143..550d94ab 100644 --- a/util.py +++ b/util.py @@ -142,7 +142,7 @@ def check_environ (): _environ_checked = 1 -def subst_vars (str, local_vars): +def subst_vars (s, local_vars): """Perform shell/Perl-style variable substitution on 'string'. Every occurrence of '$' followed by a name is considered a variable, and variable is substituted by the value found in the 'local_vars' @@ -160,7 +160,7 @@ def subst_vars (str, local_vars): return os.environ[var_name] try: - return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, str) + return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) except KeyError, var: raise ValueError, "invalid variable '$%s'" % var -- cgit v1.2.1 From 0cb7c3124d31ecef94db04ea71eb2d7b38a54ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Fri, 26 Jan 2001 18:00:48 +0000 Subject: Added an execution layer to be able to customize per-extension building. --- command/build_ext.py | 183 ++++++++++++++++++++++++++------------------------- 1 file changed, 92 insertions(+), 91 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 2876c359..8f595239 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -356,104 +356,105 @@ class build_ext (Command): # get_outputs () - - def build_extensions (self): + def build_extensions(self): # First, sanity-check the 'extensions' list self.check_extensions_list(self.extensions) for ext in self.extensions: - sources = ext.sources - if sources is None or type(sources) not in (ListType, TupleType): - raise DistutilsSetupError, \ - ("in 'ext_modules' option (extension '%s'), " + - "'sources' must be present and must be " + - "a list of source filenames") % ext.name - sources = list(sources) + self.build_extension(ext) - fullname = self.get_ext_fullname(ext.name) - if self.inplace: - # ignore build-lib -- put the compiled extension into - # the source tree along with pure Python modules - - modpath = string.split(fullname, '.') - package = string.join(modpath[0:-1], '.') - base = modpath[-1] - - build_py = self.get_finalized_command('build_py') - package_dir = build_py.get_package_dir(package) - ext_filename = os.path.join(package_dir, - self.get_ext_filename(base)) - else: - ext_filename = os.path.join(self.build_lib, - self.get_ext_filename(fullname)) + def build_extension(self, ext): - if not (self.force or newer_group(sources, ext_filename, 'newer')): - self.announce("skipping '%s' extension (up-to-date)" % - ext.name) - continue # 'for' loop over all extensions - else: - self.announce("building '%s' extension" % ext.name) - - # First, scan the sources for SWIG definition files (.i), run - # SWIG on 'em to create .c files, and modify the sources list - # accordingly. - sources = self.swig_sources(sources) - - # Next, compile the source code to object files. - - # XXX not honouring 'define_macros' or 'undef_macros' -- the - # CCompiler API needs to change to accommodate this, and I - # want to do one thing at a time! - - # Two possible sources for extra compiler arguments: - # - 'extra_compile_args' in Extension object - # - CFLAGS environment variable (not particularly - # elegant, but people seem to expect it and I - # guess it's useful) - # The environment variable should take precedence, and - # any sensible compiler will give precedence to later - # command line args. Hence we combine them in order: - extra_args = ext.extra_compile_args or [] - - macros = ext.define_macros[:] - for undef in ext.undef_macros: - macros.append((undef,)) - - # XXX and if we support CFLAGS, why not CC (compiler - # executable), CPPFLAGS (pre-processor options), and LDFLAGS - # (linker options) too? - # XXX should we use shlex to properly parse CFLAGS? - - if os.environ.has_key('CFLAGS'): - extra_args.extend(string.split(os.environ['CFLAGS'])) - - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args) - - # Now link the object files together into a "shared object" -- - # of course, first we have to figure out all the other things - # that go into the mix. - if ext.extra_objects: - objects.extend(ext.extra_objects) - extra_args = ext.extra_link_args or [] - - - self.compiler.link_shared_object( - objects, ext_filename, - libraries=self.get_libraries(ext), - library_dirs=ext.library_dirs, - runtime_library_dirs=ext.runtime_library_dirs, - extra_postargs=extra_args, - export_symbols=self.get_export_symbols(ext), - debug=self.debug, - build_temp=self.build_temp) - - # build_extensions () + sources = ext.sources + if sources is None or type(sources) not in (ListType, TupleType): + raise DistutilsSetupError, \ + ("in 'ext_modules' option (extension '%s'), " + + "'sources' must be present and must be " + + "a list of source filenames") % ext.name + sources = list(sources) + + fullname = self.get_ext_fullname(ext.name) + if self.inplace: + # ignore build-lib -- put the compiled extension into + # the source tree along with pure Python modules + + modpath = string.split(fullname, '.') + package = string.join(modpath[0:-1], '.') + base = modpath[-1] + + build_py = self.get_finalized_command('build_py') + package_dir = build_py.get_package_dir(package) + ext_filename = os.path.join(package_dir, + self.get_ext_filename(base)) + else: + ext_filename = os.path.join(self.build_lib, + self.get_ext_filename(fullname)) + + if not (self.force or newer_group(sources, ext_filename, 'newer')): + self.announce("skipping '%s' extension (up-to-date)" % + ext.name) + return + else: + self.announce("building '%s' extension" % ext.name) + + # First, scan the sources for SWIG definition files (.i), run + # SWIG on 'em to create .c files, and modify the sources list + # accordingly. + sources = self.swig_sources(sources) + + # Next, compile the source code to object files. + + # XXX not honouring 'define_macros' or 'undef_macros' -- the + # CCompiler API needs to change to accommodate this, and I + # want to do one thing at a time! + + # Two possible sources for extra compiler arguments: + # - 'extra_compile_args' in Extension object + # - CFLAGS environment variable (not particularly + # elegant, but people seem to expect it and I + # guess it's useful) + # The environment variable should take precedence, and + # any sensible compiler will give precedence to later + # command line args. Hence we combine them in order: + extra_args = ext.extra_compile_args or [] + + macros = ext.define_macros[:] + for undef in ext.undef_macros: + macros.append((undef,)) + + # XXX and if we support CFLAGS, why not CC (compiler + # executable), CPPFLAGS (pre-processor options), and LDFLAGS + # (linker options) too? + # XXX should we use shlex to properly parse CFLAGS? + + if os.environ.has_key('CFLAGS'): + extra_args.extend(string.split(os.environ['CFLAGS'])) + + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args) + + # Now link the object files together into a "shared object" -- + # of course, first we have to figure out all the other things + # that go into the mix. + if ext.extra_objects: + objects.extend(ext.extra_objects) + extra_args = ext.extra_link_args or [] + + + self.compiler.link_shared_object( + objects, ext_filename, + libraries=self.get_libraries(ext), + library_dirs=ext.library_dirs, + runtime_library_dirs=ext.runtime_library_dirs, + extra_postargs=extra_args, + export_symbols=self.get_export_symbols(ext), + debug=self.debug, + build_temp=self.build_temp) def swig_sources (self, sources): -- cgit v1.2.1 From a2946a75cbbeb464dbe57bba2a67327381da73ac Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sun, 28 Jan 2001 12:22:14 +0000 Subject: Data pathnames were not converted from URL-style to local style. Fixed. --- command/install_data.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/command/install_data.py b/command/install_data.py index dba108af..503c1aa8 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -10,7 +10,7 @@ __revision__ = "$Id$" import os from types import StringType from distutils.core import Command -from distutils.util import change_root +from distutils.util import change_root, convert_path class install_data (Command): @@ -48,6 +48,7 @@ class install_data (Command): for f in self.data_files: if type(f) == StringType: # it's a simple file, so copy it + f = convert_path(f) if self.warn_dir: self.warn("setup script did not provide a directory for " "'%s' -- installing right in '%s'" % @@ -56,13 +57,14 @@ class install_data (Command): self.outfiles.append(out) else: # it's a tuple with path to install to and a list of files - dir = f[0] + dir = convert_path(f[0]) if not os.path.isabs(dir): dir = os.path.join(self.install_dir, dir) elif self.root: dir = change_root(self.root, dir) self.mkpath(dir) for data in f[1]: + data = convert_path(f[1]) (out, _) = self.copy_file(data, dir) self.outfiles.append(out) -- cgit v1.2.1 From d14a7b0d085ee7397a87141e7cd2ea0503def6b8 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sun, 28 Jan 2001 12:23:32 +0000 Subject: Remove single "." components from pathnames, and return os.curdir if the resulting path is empty. --- util.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util.py b/util.py index 550d94ab..d4891b3c 100644 --- a/util.py +++ b/util.py @@ -82,6 +82,10 @@ def convert_path (pathname): raise ValueError, "path '%s' cannot end with '/'" % pathname paths = string.split(pathname, '/') + while '.' in paths: + paths.remove('.') + if not paths: + return os.curdir return apply(os.path.join, paths) # convert_path () -- cgit v1.2.1 From 2b9227d208d3f9196b66758a601352a58ae4253f Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 31 Jan 2001 20:07:17 +0000 Subject: move "from stat import *" to module level --- file_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file_util.py b/file_util.py index a9e12ce3..39f6eea2 100644 --- a/file_util.py +++ b/file_util.py @@ -8,6 +8,7 @@ Utility functions for operating on single files. __revision__ = "$Id$" import os +from stat import * from distutils.errors import DistutilsFileError @@ -106,7 +107,6 @@ def copy_file (src, dst, # changing it (ie. it's not already a hard/soft link to src OR # (not update) and (src newer than dst). - from stat import * from distutils.dep_util import newer if not os.path.isfile(src): -- cgit v1.2.1 From b0762d40180e6930b7f5fa403e7c1084c1f598da Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 5 Feb 2001 17:43:11 +0000 Subject: Patch #103587: Fix typo that broke the install_data command; caught by Uche Ogbuji --- command/install_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_data.py b/command/install_data.py index 503c1aa8..28f59386 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -64,7 +64,7 @@ class install_data (Command): dir = change_root(self.root, dir) self.mkpath(dir) for data in f[1]: - data = convert_path(f[1]) + data = convert_path(data) (out, _) = self.copy_file(data, dir) self.outfiles.append(out) -- cgit v1.2.1 From 6b6d7c364eec3b44b99f1f1b5a41be2920183783 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 9 Feb 2001 11:14:08 +0000 Subject: String method conversion. (This one was trivial -- no actual string. references in it!) --- bcppcompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index bb9557fb..00ccec54 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -14,7 +14,7 @@ for the Borland C++ compiler. __revision__ = "$Id$" -import sys, os, string +import sys, os from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError -- cgit v1.2.1 From 877b037a3e40bfb3285be20db1d161ca44b1e8db Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 9 Feb 2001 11:51:27 +0000 Subject: String method conversion. --- cmd.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd.py b/cmd.py index ce448294..cec4bff2 100644 --- a/cmd.py +++ b/cmd.py @@ -9,7 +9,7 @@ in the distutils.command package. __revision__ = "$Id$" -import sys, os, string, re +import sys, os, re from types import * from distutils.errors import * from distutils import util, dir_util, file_util, archive_util, dep_util @@ -161,7 +161,7 @@ class Command: print indent + header indent = indent + " " for (option, _, _) in self.user_options: - option = string.translate(option, longopt_xlate) + option = option.translate(longopt_xlate) if option[-1] == "=": option = option[:-1] value = getattr(self, option) @@ -421,7 +421,7 @@ class Command: """ if exec_msg is None: exec_msg = "generating %s from %s" % \ - (outfile, string.join(infiles, ', ')) + (outfile, ', '.join(infiles)) if skip_msg is None: skip_msg = "skipping %s (inputs unchanged)" % outfile -- cgit v1.2.1 From 31bfd423882c406175d12a99256a75411487bb93 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 9 Feb 2001 12:20:51 +0000 Subject: String method conversion. --- cygwinccompiler.py | 6 +++--- extension.py | 4 ++-- version.py | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index f40d1a2d..42318ad3 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -365,10 +365,10 @@ def check_config_h(): # "config.h" check -- should probably be renamed... from distutils import sysconfig - import string,sys + import sys # if sys.version contains GCC then python was compiled with # GCC, and the config.h file should be OK - if string.find(sys.version,"GCC") >= 0: + if sys.version.find("GCC") >= 0: return (CONFIG_H_OK, "sys.version mentions 'GCC'") fn = sysconfig.get_config_h_filename() @@ -387,7 +387,7 @@ def check_config_h(): else: # "config.h" contains an "#ifdef __GNUC__" or something similar - if string.find(s,"__GNUC__") >= 0: + if s.find("__GNUC__") >= 0: return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) else: return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) diff --git a/extension.py b/extension.py index a63ede23..f49abad0 100644 --- a/extension.py +++ b/extension.py @@ -7,7 +7,7 @@ modules in setup scripts.""" __revision__ = "$Id$" -import os, string +import os from types import * @@ -168,7 +168,7 @@ def read_setup_file (filename): elif switch == "-I": ext.include_dirs.append(value) elif switch == "-D": - equals = string.find(value, "=") + equals = value.find("=") if equals == -1: # bare "-DFOO" -- no value ext.define_macros.append((value, None)) else: # "-DFOO=blah" diff --git a/version.py b/version.py index 9d3d1724..2916eb79 100644 --- a/version.py +++ b/version.py @@ -112,12 +112,12 @@ class StrictVersion (Version): match.group(1, 2, 4, 5, 6) if patch: - self.version = tuple(map(string.atoi, [major, minor, patch])) + self.version = tuple(map(int, [major, minor, patch])) else: - self.version = tuple(map(string.atoi, [major, minor]) + [0]) + self.version = tuple(map(int, [major, minor]) + [0]) if prerelease: - self.prerelease = (prerelease[0], string.atoi(prerelease_num)) + self.prerelease = (prerelease[0], int(prerelease_num)) else: self.prerelease = None @@ -125,9 +125,9 @@ class StrictVersion (Version): def __str__ (self): if self.version[2] == 0: - vstring = string.join(map(str, self.version[0:2]), '.') + vstring = '.'.join(map(str, self.version[0:2])) else: - vstring = string.join(map(str, self.version), '.') + vstring = '.'.join(map(str, self.version)) if self.prerelease: vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) -- cgit v1.2.1 From e5fb9e92092d08b97107b3ae88108bc404a00bdb Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Fri, 16 Feb 2001 03:31:13 +0000 Subject: Linking just got simpiler on AIX and BeOS (closes SF patch #103679). --- sysconfig.py | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index ae9b37f3..2b0bce33 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -305,31 +305,8 @@ def _init_posix(): # On AIX, there are wrong paths to the linker scripts in the Makefile # -- these paths are relative to the Python source, but when installed # the scripts are in another directory. - if sys.platform == 'aix4': # what about AIX 3.x ? - # Linker script is in the config directory, not in Modules as the - # Makefile says. - python_lib = get_python_lib(standard_lib=1) - ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') - python_exp = os.path.join(python_lib, 'config', 'python.exp') - - g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) - - elif sys.platform == 'beos': - - # Linker script is in the config directory. In the Makefile it is - # relative to the srcdir, which after installation no longer makes - # sense. - python_lib = get_python_lib(standard_lib=1) - linkerscript_name = os.path.basename(string.split(g['LDSHARED'])[0]) - linkerscript = os.path.join(python_lib, 'config', linkerscript_name) - - # XXX this isn't the right place to do this: adding the Python - # library to the link, if needed, should be in the "build_ext" - # command. (It's also needed for non-MS compilers on Windows, and - # it's taken care of for them by the 'build_ext.get_libraries()' - # method.) - g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % - (linkerscript, PREFIX, sys.version[0:3])) + if python_build: + g['LDSHARED'] = g['BLDSHARED'] global _config_vars _config_vars = g -- cgit v1.2.1 From abba167c662eed5cea8b58a24df09483eac2fd96 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 17 Feb 2001 04:48:41 +0000 Subject: Split the rpath argument into multiple paths, turning it into a list. This partially fixes bug #128930. --- command/build_ext.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index 8f595239..14cfebf7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -151,8 +151,11 @@ class build_ext (Command): self.library_dirs = [] elif type(self.library_dirs) is StringType: self.library_dirs = string.split(self.library_dirs, os.pathsep) + if self.rpath is None: self.rpath = [] + elif type(self.rpath) is StringType: + self.rpath = string.split(self.rpath, os.pathsep) # for extensions under windows use different directories # for Release and Debug builds. -- cgit v1.2.1 From 7122d9720a99b46bb0f52a89f2e24375d21514f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Mon, 19 Feb 2001 09:20:04 +0000 Subject: This patch makes the default compiler determination more flexible and also takes the sys.platform name into account. This helps on platforms where there are multiple possible compiler backends (the one with which Python itself was compiled is preferred over others in this case). The patch uses this new technique to enable using cygwin compiler per default for cygwin compiled Pythons. Written by Marc-Andre Lemburg. Copyright assigned to Guido van Rossum. --- ccompiler.py | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 5e0b3280..0a30640f 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -7,7 +7,7 @@ for the Distutils compiler abstraction model.""" __revision__ = "$Id$" -import sys, os +import sys, os, re from types import * from copy import copy from distutils.errors import * @@ -835,12 +835,44 @@ class CCompiler: # class CCompiler -# Map a platform ('posix', 'nt') to the default compiler type for -# that platform. -default_compiler = { 'posix': 'unix', - 'nt': 'msvc', - 'mac': 'mwerks', - } +# Map a sys.platform/os.name ('posix', 'nt') to the default compiler +# type for that platform. Keys are interpreted as re match +# patterns. Order is important; platform mappings are preferred over +# OS names. +_default_compilers = ( + + # Platform string mappings + ('cygwin.*', 'cygwin'), + + # OS name mappings + ('posix', 'unix'), + ('nt', 'msvc'), + ('mac', 'mwerks'), + + ) + +def get_default_compiler(osname=None, platform=None): + + """ Determine the default compiler to use for the given platform. + + osname should be one of the standard Python OS names (i.e. the + ones returned by os.name) and platform the common value + returned by sys.platform for the platform in question. + + The default values are os.name and sys.platform in case the + parameters are not given. + + """ + if osname is None: + osname = os.name + if platform is None: + platform = sys.platform + for pattern, compiler in _default_compilers: + if re.match(pattern, platform) is not None or \ + re.match(pattern, osname) is not None: + return compiler + # Default to Unix compiler + return 'unix' # Map compiler types to (module_name, class_name) pairs -- ie. where to # find the code that implements an interface to this compiler. (The module @@ -896,7 +928,7 @@ def new_compiler (plat=None, try: if compiler is None: - compiler = default_compiler[plat] + compiler = get_default_compiler(plat) (module_name, class_name, long_description) = compiler_class[compiler] except KeyError: -- cgit v1.2.1 From a8b3af8796b6d404864ae381ffb958e46632d836 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 19 Feb 2001 09:20:30 +0000 Subject: Enhancements to the bdist_wininst command: --bitmap command line option allows to use a different bitmap file instead of the build-in python powered logo. --title lets you specify the text to display on the background. The editbox in the first screen now longer is selected (highlighted), it had the WS_TABSTOP flag. This is the patch http://sourceforge.net/patch/?func=detailpatch&patch_id=103687&group_id=5470 with two changes: 1. No messagebox displayed when the compilation to .pyc or .pyo files failes, this will only confuse the user (and it will fail under certain cases, where sys.path contains garbage). 2. A debugging print statement was removed from bdist_wininst.py. --- command/bdist_wininst.py | 547 ++++++++++++++++++++++++----------------------- 1 file changed, 282 insertions(+), 265 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index b45089b7..e3964b6b 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -32,6 +32,10 @@ class bdist_wininst (Command): "on the target system"), ('dist-dir=', 'd', "directory to put final built distributions in"), + ('bitmap=', 'b', + "bitmap to use for the installer instead of python-powered logo"), + ('title=', 't', + "title to display on the installer background instead of default"), ] boolean_options = ['keep-temp'] @@ -43,6 +47,8 @@ class bdist_wininst (Command): self.no_target_optimize = 0 self.target_version = None self.dist_dir = None + self.bitmap = None + self.title = None # initialize_options() @@ -117,7 +123,7 @@ class bdist_wininst (Command): ("'%s' not included in install_lib" % key) arcname = self.make_archive(archive_basename, "zip", root_dir=root_dir) - self.create_exe(arcname, fullname) + self.create_exe(arcname, fullname, self.bitmap) if not self.keep_temp: remove_tree(self.bdist_dir, self.verbose, self.dry_run) @@ -156,7 +162,7 @@ class bdist_wininst (Command): if self.target_version: lines.append("target_version=%s" % self.target_version) - title = self.distribution.get_fullname() + title = self.title or self.distribution.get_fullname() lines.append("title=%s" % repr(title)[1:-1]) import time import distutils @@ -167,7 +173,7 @@ class bdist_wininst (Command): # get_inidata() - def create_exe (self, arcname, fullname): + def create_exe (self, arcname, fullname, bitmap=None): import struct self.mkpath(self.dist_dir) @@ -185,12 +191,23 @@ class bdist_wininst (Command): "%s.win32.exe" % fullname) self.announce("creating %s" % installer_name) + if bitmap: + bitmapdata = open(bitmap, "rb").read() + bitmaplen = len(bitmapdata) + else: + bitmaplen = 0 + file = open(installer_name, "wb") file.write(self.get_exe_bytes()) + if bitmap: + file.write(bitmapdata) + file.write(cfgdata) - header = struct.pack(" Date: Tue, 27 Feb 2001 18:48:00 +0000 Subject: Patch #404275: generate a reasonable platform string for AIX --- util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util.py b/util.py index d4891b3c..009ba6cb 100644 --- a/util.py +++ b/util.py @@ -54,6 +54,8 @@ def get_platform (): # fall through to standard osname-release-machine representation elif osname[:4] == "irix": # could be "irix64"! return "%s-%s" % (osname, release) + elif osname[:3] == "aix": + return "%s-%s.%s" % (osname, version, release) elif osname[:6] == "cygwin": rel_re = re.compile (r'[\d.]+') m = rel_re.match(release) -- cgit v1.2.1 From e49a11577577efa7493fe079aec5b257a1796d00 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 27 Feb 2001 19:13:15 +0000 Subject: Patch #403947: On Cygwin, use the Unix compiler class, and not the Cygwin-specific compiler class. (According to Jason Tishler, cygwinccompiler needs some work to handle the differences in Cygwin- and MSVC-Python. Makefile and config files are currently ignored by cygwinccompiler, as it was written to support cygwin for extensions which are intended to be used with the standard MSVC built Python.) --- ccompiler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 0a30640f..4a282d4c 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -842,7 +842,10 @@ class CCompiler: _default_compilers = ( # Platform string mappings - ('cygwin.*', 'cygwin'), + + # on a cygwin built python we can use gcc like an ordinary UNIXish + # compiler + ('cygwin.*', 'unix'), # OS name mappings ('posix', 'unix'), -- cgit v1.2.1 From 9a2d4238fc67219b220c86cdea11776e83cf8371 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 27 Feb 2001 19:25:42 +0000 Subject: Bug #229280: remove '/' characters from the OS name (for BSD/OS :) ) --- util.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util.py b/util.py index 009ba6cb..e5961504 100644 --- a/util.py +++ b/util.py @@ -41,7 +41,12 @@ def get_platform (): # Try to distinguish various flavours of Unix (osname, host, release, version, machine) = os.uname() - osname = string.lower(osname) + + # Convert the OS name to lowercase and remove '/' characters + # (to accommodate BSD/OS) + osname = string.lower(osname) + osname = string.replace(osname, '/', '') + if osname[:5] == "linux": # At least on Linux/Intel, 'machine' is the processor -- # i386, etc. -- cgit v1.2.1 From a491120a1f3f62675249b45d01c90e690c13000f Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 28 Feb 2001 19:40:27 +0000 Subject: Placate tabnanny --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 2b0bce33..91f9279d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -306,7 +306,7 @@ def _init_posix(): # -- these paths are relative to the Python source, but when installed # the scripts are in another directory. if python_build: - g['LDSHARED'] = g['BLDSHARED'] + g['LDSHARED'] = g['BLDSHARED'] global _config_vars _config_vars = g -- cgit v1.2.1 From fde8019f9e24a1b85b69bbdddc0073996df538aa Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 28 Feb 2001 20:59:33 +0000 Subject: Leave #! lines featuring /usr/bin/env alone --- command/build_scripts.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 495f4c37..1f68899c 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -10,8 +10,10 @@ import sys, os, re from distutils.core import Command from distutils.dep_util import newer -# check if Python is called on the first line with this expression -first_line_re = re.compile(r'^#!.*python(\s+.*)?') +# check if Python is called on the first line with this expression. +# This expression will leave lines using /usr/bin/env alone; presumably +# the script author knew what they were doing...) +first_line_re = re.compile(r'^#!(?!\s*/usr/bin/env\b).*python(\s+.*)?') class build_scripts (Command): -- cgit v1.2.1 From 925f20a7baabbecc1a696330747546e2f2d8e2a3 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 2 Mar 2001 07:28:03 +0000 Subject: When not copying a file because the output is up to date, make the message slightly more brief, and more like the message that an extension will not be built because the built copy is up to date. --- command/build_scripts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 1f68899c..ee7f4e2d 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -45,7 +45,7 @@ class build_scripts (Command): return self.copy_scripts() - + def copy_scripts (self): """Copy each script listed in 'self.scripts'; if it's marked as a Python script in the Unix way (first line matches 'first_line_re', @@ -59,7 +59,7 @@ class build_scripts (Command): outfile = os.path.join(self.build_dir, os.path.basename(script)) if not self.force and not newer(script, outfile): - self.announce("not copying %s (output up-to-date)" % script) + self.announce("not copying %s (up-to-date)" % script) continue # Always open the file, but ignore failures in dry-run mode -- -- cgit v1.2.1 From baf6ba89cd2afee636c0590384219bf555f3b02f Mon Sep 17 00:00:00 2001 From: Ka-Ping Yee Date: Sat, 10 Mar 2001 09:33:14 +0000 Subject: Make docstrings raw, since they contain literal backslashes. --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 14cfebf7..86669757 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -545,7 +545,7 @@ class build_ext (Command): return self.package + '.' + ext_name def get_ext_filename (self, ext_name): - """Convert the name of an extension (eg. "foo.bar") into the name + r"""Convert the name of an extension (eg. "foo.bar") into the name of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ -- cgit v1.2.1 From c30a37b5c788097c52695b55e76916e109d32ac4 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 16 Mar 2001 20:57:37 +0000 Subject: The bdist_wininst.py command has been recreated after wininst.exe has been changed to include an uninstaller. I forgot to mention in the uninstaller checkin that the logfile name (used for uninstalling) has been changed from .log to -wininst.log. This should prevent conflicts with a distutils logfile serving the same purpose. The short form of the --bdist-dir (-d) option has been removed because it caused conflicts with the short form of the --dist-dir option. --- command/bdist_wininst.py | 580 +++++++++++++++++++++++++---------------------- 1 file changed, 308 insertions(+), 272 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index e3964b6b..f1dd6332 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -17,7 +17,7 @@ class bdist_wininst (Command): description = "create an executable installer for MS Windows" - user_options = [('bdist-dir=', 'd', + user_options = [('bdist-dir=', None, "temporary directory for creating the distribution"), ('keep-temp', 'k', "keep the pseudo-installation tree around after " + @@ -235,292 +235,328 @@ if __name__ == '__main__': EXEDATA = """\ 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 -AAAAAAAAAAAAAAAAAAABAAkEAACoAAAAOKsAAKABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJ -BAAA0AAAANisAABiAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAAPgAAABArgAAWgIAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAAAgAQAAoLAAAFwBAAAAAAAAAAAAAAAAAAAAAAAAAAAA -APThAAC84QAAAAAAAAAAAAAAAAAAAeIAAMzhAAAAAAAAAAAAAAAAAAAO4gAA1OEAAAAAAAAAAAAA -AAAAABviAADc4QAAAAAAAAAAAAAAAAAAJeIAAOThAAAAAAAAAAAAAAAAAAAw4gAA7OEAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAOuIAAEjiAABY4gAAAAAAAGbiAAAAAAAAdOIAAAAAAACE4gAAAAAAAI7i -AAAAAAAAlOIAAAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIuZGxsAENPTUNUTDMyLmRsbABHREkz -Mi5kbGwATVNWQ1JULmRsbABVU0VSMzIuZGxsAABMb2FkTGlicmFyeUEAAEdldFByb2NBZGRyZXNz -AABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEAAFRleHRPdXRBAABl -eGl0AABHZXREQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgAAACAAAIAFAAAAYAAAgAAA +AAAAAAAAAAAAAAAAAQBuAAAAOAAAgAAAAAAAAAAAAAAAAAAAAQAAAAAAUAAAADCxAAAICgAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAQAawAAAJAAAIBsAAAAuAAAgG0AAADgAACAbgAAAAgBAIAAAAAA +AAAAAAAAAAAAAAEACQQAAKgAAAA4uwAAoAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAADQ +AAAA2LwAAGIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJBAAA+AAAAEC+AABaAgAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAEACQQAACABAACgwAAAXAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9PEA +ALzxAAAAAAAAAAAAAAAAAAAB8gAAzPEAAAAAAAAAAAAAAAAAAA7yAADU8QAAAAAAAAAAAAAAAAAA +G/IAANzxAAAAAAAAAAAAAAAAAAAl8gAA5PEAAAAAAAAAAAAAAAAAADDyAADs8QAAAAAAAAAAAAAA +AAAAAAAAAAAAAAA68gAASPIAAFjyAAAAAAAAZvIAAAAAAAB08gAAAAAAAITyAAAAAAAAjvIAAAAA +AACU8gAAAAAAAEtFUk5FTDMyLkRMTABBRFZBUEkzMi5kbGwAQ09NQ1RMMzIuZGxsAEdESTMyLmRs +bABNU1ZDUlQuZGxsAFVTRVIzMi5kbGwAAExvYWRMaWJyYXJ5QQAAR2V0UHJvY0FkZHJlc3MAAEV4 +aXRQcm9jZXNzAAAAUmVnQ2xvc2VLZXkAAABQcm9wZXJ0eVNoZWV0QQAAVGV4dE91dEEAAGV4aXQA +AEdldERDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAA """ # --- EOF --- -- cgit v1.2.1 From 517c0d40da7055c00cdc68e76207ae86a72756bf Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 16 Mar 2001 21:00:18 +0000 Subject: Distutils version number has been changed from 1.0.1 to 1.0.2pre before this get forgotten again. Should probably be set to 1.0.2 before final release of python 2.1 Does someone still release distutils separate from python? --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 55951090..bd9761cc 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "1.0.1" +__version__ = "1.0.2pre" -- cgit v1.2.1 From 59c4620e1fd084da8d9067c4eeadf763feb4760d Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 17 Mar 2001 19:59:26 +0000 Subject: Bug #409403: Signal an error if the distribution's metadata has no version --- dist.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dist.py b/dist.py index 41d5dbbc..6bda869e 100644 --- a/dist.py +++ b/dist.py @@ -206,6 +206,10 @@ class Distribution: raise DistutilsSetupError, \ "invalid distribution option '%s'" % key + if self.metadata.version is None: + raise DistutilsSetupError, \ + "No version number specified for distribution" + # __init__ () -- cgit v1.2.1 From d23ca0098c0ef28b79e4833fcf6ed85fae2aa11a Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 17 Mar 2001 20:15:41 +0000 Subject: Fix bug #233253: the --define and --undef options didn't work, whether specified on the command-line or in setup.cfg. The option processing leaves them as strings, but they're supposed to be lists. --- command/build_ext.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index 86669757..f732373e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -177,6 +177,21 @@ class build_ext (Command): # building python standard extensions self.library_dirs.append('.') + # The argument parsing will result in self.define being a string, but + # it has to be a list of 2-tuples. All the preprocessor symbols + # specified by the 'define' option will be set to '1'. Multiple + # symbols can be separated with commas. + + if self.define: + defines = string.split(self.define, ',') + self.define = map(lambda symbol: (symbol, '1'), defines) + + # The option for macros to undefine is also a string from the + # option parsing, but has to be a list. Multiple symbols can also + # be separated with commas here. + if self.undef: + self.undef = string.split(self.undef, ',') + # finalize_options () -- cgit v1.2.1 From 61fe3191e144fa397832ea37932156cdf904320c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 03:03:41 +0000 Subject: Patch #407434: add rfc822_escape utility function --- util.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/util.py b/util.py index e5961504..010db9a3 100644 --- a/util.py +++ b/util.py @@ -443,3 +443,13 @@ byte_compile(files, optimize=%s, force=%s, (file, cfile_base) # byte_compile () + +def rfc822_escape (header): + """Return a version of the string escaped for inclusion in an + RFC-822 header, by adding a space after each newline. + """ + header = string.rstrip(header) + header = string.replace(header, '\n', '\n ') + return header + + -- cgit v1.2.1 From c5fc89cfdabbf37241d499f96c4c1bf08973b436 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 03:06:52 +0000 Subject: Add 'platforms' and 'keywords' attributes to the DistributionMetadata class, along with options to print them. Add a finalize_options() method to Distribution to do final processing on the platform and keyword attributes Add DistributionMetadata.write_pkg_info() method to write a PKG-INFO file into the release tree. --- dist.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/dist.py b/dist.py index 6bda869e..23466965 100644 --- a/dist.py +++ b/dist.py @@ -15,7 +15,7 @@ from copy import copy from distutils.errors import * from distutils import sysconfig from distutils.fancy_getopt import FancyGetopt, translate_longopt -from distutils.util import check_environ, strtobool +from distutils.util import check_environ, strtobool, rfc822_escape # Regex to define acceptable Distutils command names. This is not *quite* @@ -85,6 +85,10 @@ class Distribution: "print the package description"), ('long-description', None, "print the long package description"), + ('platforms', None, + "print the list of platforms"), + ('keywords', None, + "print the list of keywords"), ] display_option_names = map(lambda x: translate_longopt(x[0]), display_options) @@ -206,9 +210,7 @@ class Distribution: raise DistutilsSetupError, \ "invalid distribution option '%s'" % key - if self.metadata.version is None: - raise DistutilsSetupError, \ - "No version number specified for distribution" + self.finalize_options() # __init__ () @@ -526,6 +528,28 @@ class Distribution: # _parse_command_opts () + def finalize_options (self): + """Set final values for all the options on the Distribution + instance, analogous to the .finalize_options() method of Command + objects. + """ + + if self.metadata.version is None: + raise DistutilsSetupError, \ + "No version number specified for distribution" + + keywords = self.metadata.keywords + if keywords is not None: + if type(keywords) is StringType: + keywordlist = string.split(keywords, ',') + self.metadata.keywords = map(string.strip, keywordlist) + + platforms = self.metadata.platforms + if platforms is not None: + if type(platforms) is StringType: + platformlist = string.split(platforms, ',') + self.metadata.platforms = map(string.strip, platformlist) + def _show_help (self, parser, global_options=1, @@ -607,7 +631,11 @@ class Distribution: for (opt, val) in option_order: if val and is_display_option.get(opt): opt = translate_longopt(opt) - print getattr(self.metadata, "get_"+opt)() + value = getattr(self.metadata, "get_"+opt)() + if opt in ['keywords', 'platforms']: + print string.join(value, ',') + else: + print value any_display_options = 1 return any_display_options @@ -950,7 +978,38 @@ class DistributionMetadata: self.licence = None self.description = None self.long_description = None + self.keywords = None + self.platforms = None + def write_pkg_info (self, base_dir): + """Write the PKG-INFO file into the release tree. + """ + + pkg_info = open( os.path.join(base_dir, 'PKG-INFO'), 'w') + + pkg_info.write('Metadata-Version: 1.0\n') + pkg_info.write('Name: %s\n' % self.get_name() ) + pkg_info.write('Version: %s\n' % self.get_version() ) + pkg_info.write('Summary: %s\n' % self.get_description() ) + pkg_info.write('Home-page: %s\n' % self.get_url() ) + pkg_info.write('Author: %s\n' % self.get_maintainer() ) + pkg_info.write('Author-email: %s\n' % self.get_maintainer_email() ) + pkg_info.write('License: %s\n' % self.get_licence() ) + + long_desc = rfc822_escape( self.get_long_description() ) + pkg_info.write('Description: %s\n' % long_desc) + + keywords = string.join( self.get_keywords(), ',') + if keywords: + pkg_info.write('Keywords: %s\n' % keywords ) + + for platform in self.get_platforms(): + pkg_info.write('Platform: %s\n' % platform ) + + pkg_info.close() + + # write_pkg_info () + # -- Metadata query methods ---------------------------------------- def get_name (self): @@ -996,6 +1055,12 @@ class DistributionMetadata: def get_long_description(self): return self.long_description or "UNKNOWN" + def get_keywords(self): + return self.keywords or [] + + def get_platforms(self): + return self.platforms or ["UNKNOWN"] + # class DistributionMetadata -- cgit v1.2.1 From adced399695befb6c925c42e5ccae882a1f4924f Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 03:10:05 +0000 Subject: Call the write_pkg_info method --- command/sdist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 1f9e9184..894d7d44 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -31,7 +31,6 @@ def show_formats (): pretty_printer.print_help( "List of available source distribution formats:") - class sdist (Command): description = "create a source distribution (tarball, zip file, etc.)" @@ -439,9 +438,10 @@ class sdist (Command): dest = os.path.join(base_dir, file) self.copy_file(file, dest, link=link) + self.distribution.metadata.write_pkg_info(base_dir) + # make_release_tree () - def make_distribution (self): """Create the source distribution(s). First, we create the release tree with 'make_release_tree()'; then, we create all required -- cgit v1.2.1 From 047c580a97cb9ef9101c572e241cb5f33773c502 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 03:48:31 +0000 Subject: Back out conversion to string methods; the Distutils is intended to work with 1.5.2 --- cmd.py | 6 +++--- cygwinccompiler.py | 6 +++--- extension.py | 4 ++-- version.py | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd.py b/cmd.py index cec4bff2..ce448294 100644 --- a/cmd.py +++ b/cmd.py @@ -9,7 +9,7 @@ in the distutils.command package. __revision__ = "$Id$" -import sys, os, re +import sys, os, string, re from types import * from distutils.errors import * from distutils import util, dir_util, file_util, archive_util, dep_util @@ -161,7 +161,7 @@ class Command: print indent + header indent = indent + " " for (option, _, _) in self.user_options: - option = option.translate(longopt_xlate) + option = string.translate(option, longopt_xlate) if option[-1] == "=": option = option[:-1] value = getattr(self, option) @@ -421,7 +421,7 @@ class Command: """ if exec_msg is None: exec_msg = "generating %s from %s" % \ - (outfile, ', '.join(infiles)) + (outfile, string.join(infiles, ', ')) if skip_msg is None: skip_msg = "skipping %s (inputs unchanged)" % outfile diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 42318ad3..f40d1a2d 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -365,10 +365,10 @@ def check_config_h(): # "config.h" check -- should probably be renamed... from distutils import sysconfig - import sys + import string,sys # if sys.version contains GCC then python was compiled with # GCC, and the config.h file should be OK - if sys.version.find("GCC") >= 0: + if string.find(sys.version,"GCC") >= 0: return (CONFIG_H_OK, "sys.version mentions 'GCC'") fn = sysconfig.get_config_h_filename() @@ -387,7 +387,7 @@ def check_config_h(): else: # "config.h" contains an "#ifdef __GNUC__" or something similar - if s.find("__GNUC__") >= 0: + if string.find(s,"__GNUC__") >= 0: return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) else: return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) diff --git a/extension.py b/extension.py index f49abad0..a63ede23 100644 --- a/extension.py +++ b/extension.py @@ -7,7 +7,7 @@ modules in setup scripts.""" __revision__ = "$Id$" -import os +import os, string from types import * @@ -168,7 +168,7 @@ def read_setup_file (filename): elif switch == "-I": ext.include_dirs.append(value) elif switch == "-D": - equals = value.find("=") + equals = string.find(value, "=") if equals == -1: # bare "-DFOO" -- no value ext.define_macros.append((value, None)) else: # "-DFOO=blah" diff --git a/version.py b/version.py index 2916eb79..9d3d1724 100644 --- a/version.py +++ b/version.py @@ -112,12 +112,12 @@ class StrictVersion (Version): match.group(1, 2, 4, 5, 6) if patch: - self.version = tuple(map(int, [major, minor, patch])) + self.version = tuple(map(string.atoi, [major, minor, patch])) else: - self.version = tuple(map(int, [major, minor]) + [0]) + self.version = tuple(map(string.atoi, [major, minor]) + [0]) if prerelease: - self.prerelease = (prerelease[0], int(prerelease_num)) + self.prerelease = (prerelease[0], string.atoi(prerelease_num)) else: self.prerelease = None @@ -125,9 +125,9 @@ class StrictVersion (Version): def __str__ (self): if self.version[2] == 0: - vstring = '.'.join(map(str, self.version[0:2])) + vstring = string.join(map(str, self.version[0:2]), '.') else: - vstring = '.'.join(map(str, self.version)) + vstring = string.join(map(str, self.version), '.') if self.prerelease: vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) -- cgit v1.2.1 From 7e76bc2645746985a3025843e7476b540c8c7316 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 03:50:09 +0000 Subject: Remove redundant import --- cygwinccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index f40d1a2d..92def164 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -365,7 +365,7 @@ def check_config_h(): # "config.h" check -- should probably be renamed... from distutils import sysconfig - import string,sys + import string # if sys.version contains GCC then python was compiled with # GCC, and the config.h file should be OK if string.find(sys.version,"GCC") >= 0: -- cgit v1.2.1 From 52e9a5d7a9f246c15dfefe605c6a4e37a706001d Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 15:32:23 +0000 Subject: Use the get_contact*() accessors instead of get_maintainer*() --- dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist.py b/dist.py index 23466965..7ef3a42b 100644 --- a/dist.py +++ b/dist.py @@ -992,8 +992,8 @@ class DistributionMetadata: pkg_info.write('Version: %s\n' % self.get_version() ) pkg_info.write('Summary: %s\n' % self.get_description() ) pkg_info.write('Home-page: %s\n' % self.get_url() ) - pkg_info.write('Author: %s\n' % self.get_maintainer() ) - pkg_info.write('Author-email: %s\n' % self.get_maintainer_email() ) + pkg_info.write('Author: %s\n' % self.get_contact() ) + pkg_info.write('Author-email: %s\n' % self.get_contact_email() ) pkg_info.write('License: %s\n' % self.get_licence() ) long_desc = rfc822_escape( self.get_long_description() ) -- cgit v1.2.1 From 9f790bc4fe4815ba2d112430a83d527a8b13f8b0 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 23 Mar 2001 17:30:26 +0000 Subject: Change rfc822_escape() to ensure there's a consistent amount of whitespace after each newline, instead of just blindly inserting a space at the start of each line. (Improvement suggested by Thomas Wouters) --- util.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/util.py b/util.py index 010db9a3..01abd346 100644 --- a/util.py +++ b/util.py @@ -446,10 +446,11 @@ byte_compile(files, optimize=%s, force=%s, def rfc822_escape (header): """Return a version of the string escaped for inclusion in an - RFC-822 header, by adding a space after each newline. + RFC-822 header, by ensuring there are 8 spaces space after each newline. """ - header = string.rstrip(header) - header = string.replace(header, '\n', '\n ') + lines = string.split(header, '\n') + lines = map(string.strip, lines) + header = string.join(lines, '\n' + 8*' ') return header -- cgit v1.2.1 From 3d12c8d570fc54ac95ec9d25869b79b5b862c24a Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 31 Mar 2001 02:41:01 +0000 Subject: Back out the requirement to supply a version number --- dist.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dist.py b/dist.py index 7ef3a42b..1ac9786d 100644 --- a/dist.py +++ b/dist.py @@ -534,10 +534,6 @@ class Distribution: objects. """ - if self.metadata.version is None: - raise DistutilsSetupError, \ - "No version number specified for distribution" - keywords = self.metadata.keywords if keywords is not None: if type(keywords) is StringType: -- cgit v1.2.1 From 74314af647d7117a502d009c6c465851867e00e4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 5 Apr 2001 15:46:48 +0000 Subject: Patch #413912 from Steve Majewski: Add .m to the list of extensions in order to support Objective-C. --- unixccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index f7eb93ae..9ecfb6d1 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -67,7 +67,7 @@ class UnixCCompiler (CCompiler): # reasonable common default here, but it's not necessarily used on all # Unices! - src_extensions = [".c",".C",".cc",".cxx",".cpp"] + src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"] obj_extension = ".o" static_lib_extension = ".a" shared_lib_extension = ".so" -- cgit v1.2.1 From c6c4c5c787dad0ef56656ef9123aa7ae49be9c45 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 10 Apr 2001 18:57:07 +0000 Subject: Since bdist_wininst.py contains the installer executable, it had to be rebuild. --- command/bdist_wininst.py | 524 +++++++++++++++++++++++------------------------ 1 file changed, 262 insertions(+), 262 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index f1dd6332..477b733c 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -237,7 +237,7 @@ TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA4AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v ZGUuDQ0KJAAAAAAAAABwv7aMNN7Y3zTe2N803tjfT8LU3zXe2N+3wtbfNt7Y39zB3N823tjfVsHL 3zze2N803tnfSN7Y3zTe2N853tjf3MHS3zne2N+M2N7fNd7Y31JpY2g03tjfAAAAAAAAAABQRQAA -TAEDAE55sjoAAAAAAAAAAOAADwELAQYAAEAAAAAQAAAAoAAAsOwAAACwAAAA8AAAAABAAAAQAAAA +TAEDABAF0joAAAAAAAAAAOAADwELAQYAAEAAAAAQAAAAoAAA8OwAAACwAAAA8AAAAABAAAAQAAAA AgAABAAAAAAAAAAEAAAAAAAAAAAAAQAABAAAAAAAAAIAAAAAABAAABAAAAAAEAAAEAAAAAAAABAA AAAAAAAAAAAAADDxAABsAQAAAPAAADABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -250,7 +250,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAkSW5mbzogVGhpcyBmaWxlIGlz IHBhY2tlZCB3aXRoIHRoZSBVUFggZXhlY3V0YWJsZSBwYWNrZXIgaHR0cDovL3VweC50c3gub3Jn ICQKACRJZDogVVBYIDEuMDEgQ29weXJpZ2h0IChDKSAxOTk2LTIwMDAgdGhlIFVQWCBUZWFtLiBB -bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCoh1qAd/HPj7S8gAAKg8AAAAsAAAJgEATP/b +bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCtCN63fHS7mJS8gAAOo8AAAAsAAAJgEAbP/b //9TVVaLdCQUhfZXdH2LbCQci3wMgD4AdHBqXFb/5vZv/xU0YUAAi/BZHVl0X4AmAFcRvGD9v/n+ 2IP7/3Unag+4hcB1E4XtdA9XaBBw/d/+vw1qBf/Vg8QM6wdXagEJWVn2wxB1HGi3ABOyna0ALbQp Dcb3/3/7BlxGdYssWF9eXVvDVYvsg+wMU1ZXiz3ALe/uf3cz9rs5wDl1CHUHx0UIAQxWaIBMsf9v @@ -258,18 +258,18 @@ bxFWVlMFDP/Xg/j/iUX8D4WIY26+vZnUEQN1GyEg/3UQ6Bf/b7s31wBopw+EA0HrsR9QdAmPbduz UI/rL1wgGOpTDGoCrM2W7f9VIPDALmcQZronYy91JS67aFTH6Xbf891TAes7B1kO8yR0Cq3QHvkT A41F9G4GAgx7n4UYQqh9/BIDvO7NNEjMNBR1CQvIlgbTfTN/DlZqBFYQxBD7GlyEyHyJfg9hOIKz 3drmPOsmpSsCUyqs+b5tW1OnCCWLBDvGdRcnEMKGNuEoco4KM8BsC+3/5FvJOIN9EAhTi10IaUOS -druwffI4k8jdUOjISZJFsnzb3AwvUMgIFEBqAcz+c7ftGF4G2CVoqFEq8VCJXdS/sLDtLSA81xw7 -dGn/dChQaO72+b6QmBlLBCFcjnQTGnOd+5YNfIsEyYr2IR8byFn3H+w6Lh9kQ+2w0VoDxUUSPsgP -3ea+U5eMGY1e8NAUxtHd8GHOgewY4YtNENAM/3/D30RUC/qNRAvquCtIDCvKg+kWA9GBOFBLBeP/ +druwffI4k8jdUOjISeJFsnzb3AwvUMgIFEBqAcz+c7ftGF4G2CVoqFEq8VCJXdS/sLDtLSCM1xw7 +dGn/dChQaO72+b6QmBlLBCGsjnQTGnOd+5YNfIsEyYr2IR8byFn3IDw6Lh9kQ+2w0VoDxUUSPsgP +3ea+U5fcGY1e8NAUxtHd8GHOgewY4YtNENAM/3/D30RUC/qNRAvquCtIDCvKg+kWA9GBOFBLBeP/ 3fYGiU307mUsg2UMAGaDeAoAD45OHdv//+0GPfSLVRCLRBoqjTQajTwIA/uBPjEBY7lttgIuNoE/ CwMEKou23Zq/D79OIIPCLokwA9ME8BHNW7f7Vh4DygUcAxHRCE8cicG/3LYvVxoD0BP0jRoe7I2F -6P7dsxEalGKkC2iw81BmC7Z8254QH96Uzb1t742EBQ02+EZHGlAbJexkZvcDtXXwHkRhSwRoV9y9 -1AaYRsq8BecPXHRG4WDdd0xmi0YMUAQOQfd2TlB2hZIJ6S0AjbtrOPe6Hie0Pht2FFENbTTbb+xL +6P7dsxEalGL0C2iw81BmC7Z82+4QH96Uzb1t742EBQ02+EZHGlAbJexkZvcDtXXwHkRhSwRoV9y9 +1AboRsq8BecPXHRG4WDdd0xmi0YMUAQOQfd2TlB2hZIJ6S0AjbtrOPe6Hie0Pht2FFENbTTbb+xL AfodGDkqFO5Nd2wbGBNAUItyv0AKUEJf69zGagZVFLQS/xoVOcbh5HYGjLR51ppw/3934ev3USQE RBGKCITJdAuA+S91A8YAXLlbOeNAde+HQDR0F4AjNRkmtlUWYV8F19gbrVkRJsBXUBTUlt9sBcfY jOIM0GoKmVn39222/PkzyWjocFEAHmi8AgAN0SyZhkVAPzBQbramtjLXGiEUFUi+oI5oS0fYBFYv WVBCDwFwct3dOR04GP/TaDbk+9qBNQdgIwoBFdOpM2e61xhfPJ+edm+tmcD08fbCEADauACare7b -BgA9rOFOO5SBu8P92AkQQIWsDKd9CFchbdjVvgYzoTiiPH90FJdoctO5B94iaAEEAGmgVQbodHz4 +BgA9/OFOO5SBu8P92AkQQIWsDKd9CFchbdjVvgYzoTiiPH90FJdoctO5B94iaAEEAGmgVQbodHz4 X1Aqf8cEJECg/QDwPfSZ3GfhGuQ12AUg3f5dmhpN6ANB1mgAjxZo/W8jm4MMwKEABF+sXusnutbu TeGBeAg49XUZS35wNfO95x3RdFzgKWwbrvDVg0unCkIIOKBr0Pp1Oa1GKaQ3/vbGMaEdQItQCo1I DvZRUveehUvLUVZGRKMwLA0nNEsM/F78EN7wW4TYILDD1yjWui61C6yL+AWQ/IuC9CrdNt4RK9Ar @@ -277,260 +277,260 @@ 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 -- cgit v1.2.1 From 4d5c70781a289e136da686d05bcb1a0767e08cea Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sat, 14 Apr 2001 16:17:00 +0000 Subject: Pete Shinners discovered that zipfile.ZipFile() is called with mode argument "wb", while the only valid modes are "r", "w" or "a". Fix this by changing the mode to "w". --- archive_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archive_util.py b/archive_util.py index 61bc25ea..4c5641b9 100644 --- a/archive_util.py +++ b/archive_util.py @@ -100,7 +100,7 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): z.write(path, path) if not dry_run: - z = zipfile.ZipFile(zip_filename, "wb", + z = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED) os.path.walk(base_dir, visit, z) -- cgit v1.2.1 From 48343f32fa28556d2d229b5a661bfe5e0b4b0634 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 23 Apr 2001 16:01:06 +0000 Subject: Bump version # for final release --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index bd9761cc..16d387a4 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "1.0.2pre" +__version__ = "1.0.2" -- cgit v1.2.1 From 09b62d24b0c662a7e9146fcafe7a4f29bff0d463 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 23 Apr 2001 17:13:03 +0000 Subject: Fix typo in docstring --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 16d387a4..27e79a54 100644 --- a/__init__.py +++ b/__init__.py @@ -1,6 +1,6 @@ """distutils -The main package for the Python Module Distribtion Utilities. Normally +The main package for the Python Module Distribution Utilities. Normally used from a setup script as from distutils.core import setup -- cgit v1.2.1 From 59653f9ebdb6ead7454c56aadfb40bfcb33b0bfa Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Thu, 17 May 2001 12:52:01 +0000 Subject: Made distutils understand the MacPython Carbon runtime model. Distutils will build for the runtime model you are currently using for the interpreter. --- mwerkscompiler.py | 2 ++ sysconfig.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 2edc8259..981fd95f 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -114,6 +114,8 @@ class MWerksCompiler (CCompiler) : # into the project. if output_filename[-8:] == '.ppc.slb': basename = output_filename[:-8] + elif output_filename[-11:] == '.carbon.slb': + basename = output_filename[:-11] else: basename = os.path.strip(output_filename)[0] projectname = basename + '.mcp' diff --git a/sysconfig.py b/sysconfig.py index 91f9279d..473faea8 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -339,7 +339,11 @@ def _init_mac(): # XXX hmmm.. a normal install puts include files here g['INCLUDEPY'] = get_python_inc(plat_specific=0) + import MacOS + if not hasattr(MacOS, 'runtimemodel'): g['SO'] = '.ppc.slb' + else: + g['SO'] = '.%s.slb' % MacOS.runtimemodel # XXX are these used anywhere? g['install_lib'] = os.path.join(EXEC_PREFIX, "Lib") -- cgit v1.2.1 From 9ac36316de9ed5d0c147f7493e9b472480c7b4f7 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 17 May 2001 15:03:14 +0000 Subject: Fixed botched indent in _init_mac() code. (It may never be executed, but it still can't have any syntax errors. Went a little too fast there, Jack? :-) --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 473faea8..d013d1b8 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -341,7 +341,7 @@ def _init_mac(): import MacOS if not hasattr(MacOS, 'runtimemodel'): - g['SO'] = '.ppc.slb' + g['SO'] = '.ppc.slb' else: g['SO'] = '.%s.slb' % MacOS.runtimemodel -- cgit v1.2.1 From 4fb616ab1845cdb8a67b68055a8a348db3c6f99c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 21 May 2001 20:34:38 +0000 Subject: Fix bug #418369: typo in bdist_rpm --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index f4215904..46594945 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -189,7 +189,7 @@ class bdist_rpm (Command): if type(self.doc_files) is ListType: for readme in ('README', 'README.txt'): if os.path.exists(readme) and readme not in self.doc_files: - self.doc.append(readme) + self.doc_files.append(readme) self.ensure_string('release', "1") self.ensure_string('serial') # should it be an int? -- cgit v1.2.1 From e7ab99d0e57404a593cd9e44f9381d3f08b0925c Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Tue, 19 Jun 2001 19:44:02 +0000 Subject: Fixed -D emulation for symbols with a value, as specified with the define_macros Extension argument. --- mwerkscompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 981fd95f..1b416715 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -164,7 +164,7 @@ class MWerksCompiler (CCompiler) : if value is None: fp.write('#define %s\n'%name) else: - fp.write('#define %s "%s"\n'%(name, value)) + fp.write('#define %s %s\n'%(name, value)) fp.close() settings['prefixname'] = prefixname -- cgit v1.2.1 From 37936adf51754abcc600a93964e105b20a052e20 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Tue, 19 Jun 2001 21:23:11 +0000 Subject: - _filename_to_abs() didn't cater for .. components in the pathname. Fixed. - compile() didn't return a (empty) list of objects. Fixed. - the various _fix_xxx_args() methods weren't called (are they new or did I overlook them?). Fixed. --- mwerkscompiler.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 1b416715..46e16e2e 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -62,10 +62,13 @@ class MWerksCompiler (CCompiler) : debug=0, extra_preargs=None, extra_postargs=None): + (output_dir, macros, include_dirs) = \ + self._fix_compile_args (output_dir, macros, include_dirs) self.__sources = sources self.__macros = macros self.__include_dirs = include_dirs # Don't need extra_preargs and extra_postargs for CW + return [] def link (self, target_desc, @@ -80,6 +83,11 @@ class MWerksCompiler (CCompiler) : extra_preargs=None, extra_postargs=None, build_temp=None): + # First fixup. + (objects, output_dir) = self._fix_object_args (objects, output_dir) + (libraries, library_dirs, runtime_library_dirs) = \ + self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) + # First examine a couple of options for things that aren't implemented yet if not target_desc in (self.SHARED_LIBRARY, self.SHARED_OBJECT): raise DistutilsPlatformError, 'Can only make SHARED_LIBRARY or SHARED_OBJECT targets on the Mac' @@ -200,6 +208,11 @@ class MWerksCompiler (CCompiler) : if not os.path.isabs(filename): curdir = os.getcwd() filename = os.path.join(curdir, filename) - return filename + # Finally remove .. components + components = string.split(filename, ':') + for i in range(1, len(components)): + if components[i] == '..': + components[i] = '' + return string.join(components, ':') -- cgit v1.2.1 From debc45c4d667d5573afe333e9ac6d9ede0d07da1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Jul 2001 16:52:02 +0000 Subject: dummy checkin for testing, please ignore --- command/bdist_wininst.py | 1 - 1 file changed, 1 deletion(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 477b733c..16a6cc41 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -216,7 +216,6 @@ class bdist_wininst (Command): def get_exe_bytes (self): import base64 return base64.decodestring(EXEDATA) - # class bdist_wininst if __name__ == '__main__': -- cgit v1.2.1 From 78fe57542d08d417655af49304c2b266b993a2ff Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 16 Jul 2001 14:19:20 +0000 Subject: [Bug #441527] Fixes for preprocessor support, contributed by Tarn Weisner Burton --- unixccompiler.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 9ecfb6d1..91e7d5e0 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -99,12 +99,13 @@ class UnixCCompiler (CCompiler): if extra_preargs: pp_args[:0] = extra_preargs if extra_postargs: - extra_postargs.extend(extra_postargs) + pp_args.extend(extra_postargs) - # We need to preprocess: either we're being forced to, or the - # source file is newer than the target (or the target doesn't + # We need to preprocess: either we're being forced to, or we're + # generating output to stdout, or there's a target output file and + # the source file is newer than the target (or the target doesn't # exist). - if self.force or (output_file and newer(source, output_file)): + if self.force or output_file is None or newer(source, output_file)): if output_file: self.mkpath(os.path.dirname(output_file)) try: -- cgit v1.2.1 From 4490899b03853d52c7f718b6a2c5a54a2e865829 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 16 Jul 2001 14:46:13 +0000 Subject: Fix a mismatched parenthesis in the last patch. --- unixccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 91e7d5e0..da1f2a4e 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -105,7 +105,7 @@ class UnixCCompiler (CCompiler): # generating output to stdout, or there's a target output file and # the source file is newer than the target (or the target doesn't # exist). - if self.force or output_file is None or newer(source, output_file)): + if self.force or output_file is None or newer(source, output_file): if output_file: self.mkpath(os.path.dirname(output_file)) try: -- cgit v1.2.1 From 89b261c72363d96d8c2e380370cff3396b22b488 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Wed, 18 Jul 2001 18:39:56 +0000 Subject: Minor changes for stylistic cleanliness and consistency. --- sysconfig.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index d013d1b8..16e80231 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -1,5 +1,9 @@ -"""Provide access to Python's configuration information. The specific names -defined in the module depend heavily on the platform and configuration. +"""Provide access to Python's configuration information. The specific +configuration variables available depend heavily on the platform and +configuration. The values may be retrieved using +get_config_var(name), and the list of variables is available via +get_config_vars().keys(). Additional convenience functions are also +available. Written by: Fred L. Drake, Jr. Email: @@ -45,7 +49,7 @@ def get_python_inc(plat_specific=0, prefix=None): sys.exec_prefix -- i.e., ignore 'plat_specific'. """ if prefix is None: - prefix = (plat_specific and EXEC_PREFIX or PREFIX) + prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": if python_build: return "Include/" @@ -55,9 +59,9 @@ def get_python_inc(plat_specific=0, prefix=None): elif os.name == "mac": return os.path.join(prefix, "Include") else: - raise DistutilsPlatformError, \ - ("I don't know where Python installs its C header files " + - "on platform '%s'") % os.name + raise DistutilsPlatformError( + "I don't know where Python installs its C header files " + "on platform '%s'" % os.name) def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): @@ -75,7 +79,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): sys.exec_prefix -- i.e., ignore 'plat_specific'. """ if prefix is None: - prefix = (plat_specific and EXEC_PREFIX or PREFIX) + prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": libpython = os.path.join(prefix, @@ -96,23 +100,23 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): if standard_lib: return os.path.join(EXEC_PREFIX, "Mac", "Plugins") else: - raise DistutilsPlatformError, \ - "OK, where DO site-specific extensions go on the Mac?" + raise DistutilsPlatformError( + "OK, where DO site-specific extensions go on the Mac?") else: if standard_lib: return os.path.join(PREFIX, "Lib") else: - raise DistutilsPlatformError, \ - "OK, where DO site-specific modules go on the Mac?" + raise DistutilsPlatformError( + "OK, where DO site-specific modules go on the Mac?") else: - raise DistutilsPlatformError, \ - ("I don't know where Python installs its library " + - "on platform '%s'") % os.name + raise DistutilsPlatformError( + "I don't know where Python installs its library " + "on platform '%s'" % os.name) # get_python_lib() -def customize_compiler (compiler): +def customize_compiler(compiler): """Do any platform-specific customization of the CCompiler instance 'compiler'. Mainly needed on Unix, so we can plug in the information that varies across Unices and is stored in Python's Makefile. @@ -299,7 +303,7 @@ def _init_posix(): if hasattr(msg, "strerror"): my_msg = my_msg + " (%s)" % msg.strerror - raise DistutilsPlatformError, my_msg + raise DistutilsPlatformError(my_msg) # On AIX, there are wrong paths to the linker scripts in the Makefile -- cgit v1.2.1 From cfca016e6101c3590ecefd1731c56be76f4f6b83 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 20 Jul 2001 19:29:04 +0000 Subject: Patch #429442 from Jason Tishler: Corrects sys.platform and distutils.util.get_platform() problems caused by the cruft contained in Cygwin's uname -s. --- util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/util.py b/util.py index 01abd346..25ddbdf4 100644 --- a/util.py +++ b/util.py @@ -62,6 +62,7 @@ def get_platform (): elif osname[:3] == "aix": return "%s-%s.%s" % (osname, version, release) elif osname[:6] == "cygwin": + osname = "cygwin" rel_re = re.compile (r'[\d.]+') m = rel_re.match(release) if m: -- cgit v1.2.1 From 9dad3ab68accd527b9d7027b9d005303abfbdc35 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 25 Jul 2001 19:48:03 +0000 Subject: Don't "import *" from stat at all -- just import what's needed, and do it back in copy_file() (not at module level). --- file_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file_util.py b/file_util.py index 39f6eea2..a3767bbd 100644 --- a/file_util.py +++ b/file_util.py @@ -8,7 +8,6 @@ Utility functions for operating on single files. __revision__ = "$Id$" import os -from stat import * from distutils.errors import DistutilsFileError @@ -108,6 +107,7 @@ def copy_file (src, dst, # (not update) and (src newer than dst). from distutils.dep_util import newer + from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE if not os.path.isfile(src): raise DistutilsFileError, \ -- cgit v1.2.1 From 49b8a8e81d33296018e5454c1d42c9a8321510da Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 25 Jul 2001 20:20:11 +0000 Subject: Undo revision 1.7: always mangle a #! line containing "python" to point to the current Python interpreter (ie. the one used for building/installation), even (especially!) if "/usr/bin/env" appears in the #! line. Rationale: installing scripts with "#!/usr/bin/env python" is asking for trouble, because 1) it might pick the wrong interpreter (not the one used to build/install the script) 2) it doesn't work on all platforms (try it on IRIX 5, or on Linux with command-line options for python) 3) "env" might not be in /usr/bin --- command/build_scripts.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index ee7f4e2d..653b8d8a 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -10,10 +10,8 @@ import sys, os, re from distutils.core import Command from distutils.dep_util import newer -# check if Python is called on the first line with this expression. -# This expression will leave lines using /usr/bin/env alone; presumably -# the script author knew what they were doing...) -first_line_re = re.compile(r'^#!(?!\s*/usr/bin/env\b).*python(\s+.*)?') +# check if Python is called on the first line with this expression +first_line_re = re.compile(r'^#!.*python(\s+.*)?') class build_scripts (Command): -- cgit v1.2.1 From 853724a093730bff94e89528c3117b032c8af6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 26 Jul 2001 13:41:06 +0000 Subject: Patch #411138: Rename config.h to pyconfig.h. Closes bug #231774. --- command/build_ext.py | 4 ++-- cygwinccompiler.py | 16 ++++++++-------- sysconfig.py | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index f732373e..259a8447 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -125,7 +125,7 @@ class build_ext (Command): self.extensions = self.distribution.ext_modules - # Make sure Python's include directories (for Python.h, config.h, + # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. py_include = sysconfig.get_python_inc() plat_py_include = sysconfig.get_python_inc(plat_specific=1) @@ -592,7 +592,7 @@ class build_ext (Command): """ # The python library is always needed on Windows. For MSVC, this # is redundant, since the library is mentioned in a pragma in - # config.h that MSVC groks. The other Windows compilers all seem + # pyconfig.h that MSVC groks. The other Windows compilers all seem # to need it mentioned explicitly, though, so that's what we do. # Append '_d' to the python import library on debug builds. from distutils.msvccompiler import MSVCCompiler diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 92def164..07e16655 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -73,7 +73,7 @@ class CygwinCCompiler (UnixCCompiler): (status, details)) if status is not CONFIG_H_OK: self.warn( - "Python's config.h doesn't seem to support your compiler. " + + "Python's pyconfig.h doesn't seem to support your compiler. " + ("Reason: %s." % details) + "Compiling may fail because of undefined preprocessor macros.") @@ -335,7 +335,7 @@ class Mingw32CCompiler (CygwinCCompiler): # class Mingw32CCompiler -# Because these compilers aren't configured in Python's config.h file by +# Because these compilers aren't configured in Python's pyconfig.h file by # default, we should at least warn the user if he is using a unmodified # version. @@ -345,7 +345,7 @@ CONFIG_H_UNCERTAIN = "uncertain" def check_config_h(): - """Check if the current Python installation (specifically, config.h) + """Check if the current Python installation (specifically, pyconfig.h) appears amenable to building extensions with GCC. Returns a tuple (status, details), where 'status' is one of the following constants: CONFIG_H_OK @@ -353,21 +353,21 @@ def check_config_h(): CONFIG_H_NOTOK doesn't look good CONFIG_H_UNCERTAIN - not sure -- unable to read config.h + not sure -- unable to read pyconfig.h 'details' is a human-readable string explaining the situation. Note there are two ways to conclude "OK": either 'sys.version' contains the string "GCC" (implying that this Python was built with GCC), or the - installed "config.h" contains the string "__GNUC__". + installed "pyconfig.h" contains the string "__GNUC__". """ # XXX since this function also checks sys.version, it's not strictly a - # "config.h" check -- should probably be renamed... + # "pyconfig.h" check -- should probably be renamed... from distutils import sysconfig import string # if sys.version contains GCC then python was compiled with - # GCC, and the config.h file should be OK + # GCC, and the pyconfig.h file should be OK if string.find(sys.version,"GCC") >= 0: return (CONFIG_H_OK, "sys.version mentions 'GCC'") @@ -386,7 +386,7 @@ def check_config_h(): "couldn't read '%s': %s" % (fn, exc.strerror)) else: - # "config.h" contains an "#ifdef __GNUC__" or something similar + # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar if string.find(s,"__GNUC__") >= 0: return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) else: diff --git a/sysconfig.py b/sysconfig.py index 16e80231..529d0e6d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -43,7 +43,7 @@ def get_python_inc(plat_specific=0, prefix=None): If 'plat_specific' is false (the default), this is the path to the non-platform-specific header files, i.e. Python.h and so on; otherwise, this is the path to platform-specific header files - (namely config.h). + (namely pyconfig.h). If 'prefix' is supplied, use it instead of sys.prefix or sys.exec_prefix -- i.e., ignore 'plat_specific'. @@ -137,10 +137,10 @@ def customize_compiler(compiler): def get_config_h_filename(): - """Return full pathname of installed config.h file.""" + """Return full pathname of installed pyconfig.h file.""" if python_build: inc_dir = '.' else: inc_dir = get_python_inc(plat_specific=1) - return os.path.join(inc_dir, "config.h") + return os.path.join(inc_dir, "pyconfig.h") def get_makefile_filename(): -- cgit v1.2.1 From 80b512fee5d1ee8342100e305a8582f4b121d978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Thu, 26 Jul 2001 18:06:58 +0000 Subject: Add backwards compatibility. --- sysconfig.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 529d0e6d..bbf7c4af 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -140,7 +140,12 @@ def get_config_h_filename(): """Return full pathname of installed pyconfig.h file.""" if python_build: inc_dir = '.' else: inc_dir = get_python_inc(plat_specific=1) - return os.path.join(inc_dir, "pyconfig.h") + if sys.version < '2.2': + config_h = 'config.h' + else: + # The name of the config.h file changed in 2.2 + config_h = 'pyconfig.h' + return os.path.join(inc_dir, config_h) def get_makefile_filename(): -- cgit v1.2.1 From 6f9753a79698a3fd9c882c8567cd75a351e6e04a Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 29 Jul 2001 21:39:18 +0000 Subject: Do convert_path() on script paths (now PyXML builds out of the box under MacOS.) --- command/build_scripts.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/build_scripts.py b/command/build_scripts.py index 653b8d8a..611767e7 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -9,6 +9,7 @@ __revision__ = "$Id$" import sys, os, re from distutils.core import Command from distutils.dep_util import newer +from distutils.util import convert_path # check if Python is called on the first line with this expression first_line_re = re.compile(r'^#!.*python(\s+.*)?') @@ -54,6 +55,7 @@ class build_scripts (Command): self.mkpath(self.build_dir) for script in self.scripts: adjust = 0 + script = convert_path(script) outfile = os.path.join(self.build_dir, os.path.basename(script)) if not self.force and not newer(script, outfile): -- cgit v1.2.1 From 27487d75c92e72a5a28d25d0b75bbb26c49ec5b4 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 2 Aug 2001 20:03:12 +0000 Subject: Miscellaneous minor cleanups. --- sysconfig.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index bbf7c4af..1f0d1453 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -29,14 +29,15 @@ EXEC_PREFIX = os.path.normpath(sys.exec_prefix) python_build = 0 def set_python_build(): - """Set the python_build flag to true; this means that we're - building Python itself. Only called from the setup.py script - shipped with Python. + """Set the python_build flag to true. + + This means that we're building Python itself. Only called from + the setup.py script shipped with Python. """ - global python_build python_build = 1 + def get_python_inc(plat_specific=0, prefix=None): """Return the directory containing installed Python header files. @@ -55,7 +56,7 @@ def get_python_inc(plat_specific=0, prefix=None): return "Include/" return os.path.join(prefix, "include", "python" + sys.version[:3]) elif os.name == "nt": - return os.path.join(prefix, "Include") # include or Include? + return os.path.join(prefix, "include") elif os.name == "mac": return os.path.join(prefix, "Include") else: @@ -80,7 +81,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): """ if prefix is None: prefix = plat_specific and EXEC_PREFIX or PREFIX - + if os.name == "posix": libpython = os.path.join(prefix, "lib", "python" + sys.version[:3]) @@ -91,20 +92,20 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): elif os.name == "nt": if standard_lib: - return os.path.join(PREFIX, "Lib") + return os.path.join(prefix, "Lib") else: return prefix elif os.name == "mac": if plat_specific: if standard_lib: - return os.path.join(EXEC_PREFIX, "Mac", "Plugins") + return os.path.join(prefix, "Mac", "Plugins") else: raise DistutilsPlatformError( "OK, where DO site-specific extensions go on the Mac?") else: if standard_lib: - return os.path.join(PREFIX, "Lib") + return os.path.join(prefix, "Lib") else: raise DistutilsPlatformError( "OK, where DO site-specific modules go on the Mac?") @@ -113,13 +114,12 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): "I don't know where Python installs its library " "on platform '%s'" % os.name) -# get_python_lib() - def customize_compiler(compiler): - """Do any platform-specific customization of the CCompiler instance - 'compiler'. Mainly needed on Unix, so we can plug in the information - that varies across Unices and is stored in Python's Makefile. + """Do any platform-specific customization of a CCompiler instance. + + Mainly needed on Unix, so we can plug in the information that + varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": (cc, opt, ccshared, ldshared, so_ext) = \ @@ -138,8 +138,10 @@ def customize_compiler(compiler): def get_config_h_filename(): """Return full pathname of installed pyconfig.h file.""" - if python_build: inc_dir = '.' - else: inc_dir = get_python_inc(plat_specific=1) + if python_build: + inc_dir = os.curdir + else: + inc_dir = get_python_inc(plat_specific=1) if sys.version < '2.2': config_h = 'config.h' else: @@ -197,7 +199,6 @@ def parse_makefile(fn, g=None): A dictionary containing name/value pairs is returned. If an optional dictionary is passed in as the second argument, it is used instead of a new dictionary. - """ from distutils.text_file import TextFile fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1) @@ -309,8 +310,8 @@ def _init_posix(): my_msg = my_msg + " (%s)" % msg.strerror raise DistutilsPlatformError(my_msg) - - + + # On AIX, there are wrong paths to the linker scripts in the Makefile # -- these paths are relative to the Python source, but when installed # the scripts are in another directory. @@ -397,6 +398,6 @@ def get_config_vars(*args): def get_config_var(name): """Return the value of a single variable using the dictionary returned by 'get_config_vars()'. Equivalent to - get_config_vars().get(name) + get_config_vars().get(name) """ return get_config_vars().get(name) -- cgit v1.2.1 From 6daca2f31cd60949e816919a9c1163b016c02a71 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 9 Aug 2001 20:57:46 +0000 Subject: Use correct variable name --- fancy_getopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 6f8b8c06..83d07216 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -275,7 +275,7 @@ class FancyGetopt: if not self.takes_arg[opt]: # boolean option? if val != '': # shouldn't have a value! raise DistutilsInternalError, \ - "this can't happen: bad option value '%s'" % value + "this can't happen: bad option value '%s'" % val alias = self.negative_alias.get(opt) if alias: -- cgit v1.2.1 From db61a5d336c081391a6621918a63df03e9b64e04 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 9 Aug 2001 20:59:53 +0000 Subject: Import the errno module --- file_util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/file_util.py b/file_util.py index a3767bbd..991d8357 100644 --- a/file_util.py +++ b/file_util.py @@ -188,7 +188,8 @@ def move_file (src, dst, other systems??? """ from os.path import exists, isfile, isdir, basename, dirname - + import errno + if verbose: print "moving %s -> %s" % (src, dst) -- cgit v1.2.1 From ff1cb1dae11e3fb8f12fba68a62f9c0085ff9e50 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 9 Aug 2001 21:02:34 +0000 Subject: Import UnknownFileError --- bcppcompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 00ccec54..2742b5fe 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -17,7 +17,7 @@ __revision__ = "$Id$" import sys, os from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError + CompileError, LibError, LinkError, UnknownFileError from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options from distutils.file_util import write_file -- cgit v1.2.1 From 34771a9b3649f5b2762c5090a5587d49dc7e9698 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 18:49:23 +0000 Subject: [Bug #412271, bug #449009] Use 'license' as the attribute name, though 'licence' is still supported for backward-compatibility (Should I add a warning to get_licence(), or not bother?) Also fixes an UnboundLocalError noticed by PyChecker --- dist.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/dist.py b/dist.py index 1ac9786d..824a0d3e 100644 --- a/dist.py +++ b/dist.py @@ -13,7 +13,6 @@ import sys, os, string, re from types import * from copy import copy from distutils.errors import * -from distutils import sysconfig from distutils.fancy_getopt import FancyGetopt, translate_longopt from distutils.util import check_environ, strtobool, rfc822_escape @@ -77,10 +76,10 @@ class Distribution: "print the maintainer's email address if known, else the author's"), ('url', None, "print the URL for this package"), - ('licence', None, - "print the licence of the package"), ('license', None, - "alias for --licence"), + "print the license of the package"), + ('licence', None, + "alias for --license"), ('description', None, "print the package description"), ('long-description', None, @@ -395,7 +394,7 @@ class Distribution: self.commands = [] parser = FancyGetopt(self.global_options + self.display_options) parser.set_negative_aliases(self.negative_opt) - parser.set_aliases({'license': 'licence'}) + parser.set_aliases({'licence': 'license'}) args = parser.getopt(args=self.script_args, object=self) option_order = parser.get_option_order() @@ -580,7 +579,7 @@ class Distribution: print for command in self.commands: - if type(command) is ClassType and issubclass(klass, Command): + if type(command) is ClassType and issubclass(command, Command): klass = command else: klass = self.get_command_class(command) @@ -971,7 +970,7 @@ class DistributionMetadata: self.maintainer = None self.maintainer_email = None self.url = None - self.licence = None + self.license = None self.description = None self.long_description = None self.keywords = None @@ -990,7 +989,7 @@ class DistributionMetadata: pkg_info.write('Home-page: %s\n' % self.get_url() ) pkg_info.write('Author: %s\n' % self.get_contact() ) pkg_info.write('Author-email: %s\n' % self.get_contact_email() ) - pkg_info.write('License: %s\n' % self.get_licence() ) + pkg_info.write('License: %s\n' % self.get_license() ) long_desc = rfc822_escape( self.get_long_description() ) pkg_info.write('Description: %s\n' % long_desc) @@ -1042,9 +1041,10 @@ class DistributionMetadata: def get_url(self): return self.url or "UNKNOWN" - def get_licence(self): - return self.licence or "UNKNOWN" - + def get_license(self): + return self.license or "UNKNOWN" + get_licence = get_license + def get_description(self): return self.description or "UNKNOWN" -- cgit v1.2.1 From 404bfc2ce6499d3f1e0f3f7bc318805fb1f53feb Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 18:50:11 +0000 Subject: Use .get_license() --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 46594945..92de9358 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -347,7 +347,7 @@ class bdist_rpm (Command): spec_file.append('Source0: %{name}-%{version}.tar.gz') spec_file.extend([ - 'Copyright: ' + self.distribution.get_licence(), + 'Copyright: ' + self.distribution.get_license(), 'Group: ' + self.group, 'BuildRoot: %{_tmppath}/%{name}-buildroot', 'Prefix: %{_prefix}', ]) -- cgit v1.2.1 From a23893242a626332e830cf29243efe00df23c38f Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 10 Aug 2001 18:59:30 +0000 Subject: Wrap a comment to fit in 80 columns. Use construction-syntax for an exception to make the argument easier to read. --- dist.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dist.py b/dist.py index 824a0d3e..3803f5cc 100644 --- a/dist.py +++ b/dist.py @@ -375,8 +375,8 @@ class Distribution: help). """ # - # We now have enough information to show the Macintosh dialog that allows - # the user to interactively specify the "command line". + # We now have enough information to show the Macintosh dialog + # that allows the user to interactively specify the "command line". # if sys.platform == 'mac': import EasyDialogs @@ -508,10 +508,10 @@ class Distribution: if callable(func): func() else: - raise DistutilsClassError, \ - ("invalid help function %s for help option '%s': " - "must be a callable object (function, etc.)") % \ - (`func`, help_option) + raise DistutilsClassError( + "invalid help function %s for help option '%s': " + "must be a callable object (function, etc.)" + % (`func`, help_option)) if help_option_found: return -- cgit v1.2.1 From 67be7feb397ea9c635a125a3efa771e38454e78c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 18:59:59 +0000 Subject: Add forgotten import --- command/install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/install.py b/command/install.py index 40e00705..1d0a34e4 100644 --- a/command/install.py +++ b/command/install.py @@ -10,6 +10,7 @@ import sys, os, string from types import * from distutils.core import Command, DEBUG from distutils.sysconfig import get_config_vars +from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root from distutils.errors import DistutilsOptionError -- cgit v1.2.1 From cd27545f369affed2ce5537416fd463fb1180c1c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 19:00:15 +0000 Subject: Fix typo caught by PyChecker --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 92de9358..150fdeca 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -167,7 +167,7 @@ class bdist_rpm (Command): ("don't know how to create RPM " "distributions on platform %s" % os.name) if self.binary_only and self.source_only: - raise DistutilsOptionsError, \ + raise DistutilsOptionError, \ "cannot supply both '--source-only' and '--binary-only'" # don't pass CFLAGS to pure python distributions -- cgit v1.2.1 From f7cb031597baffc1532b640a2c5fb2d08f306524 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 19:00:41 +0000 Subject: Remove unused variable --- command/build_scripts.py | 1 - 1 file changed, 1 deletion(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 611767e7..16024e5f 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -51,7 +51,6 @@ class build_scripts (Command): ie. starts with "\#!" and contains "python"), then adjust the first line to refer to the current Python interpreter as we copy. """ - outfiles = [] self.mkpath(self.build_dir) for script in self.scripts: adjust = 0 -- cgit v1.2.1 From 45de3e623e5f498ee233b53b22a76a48fc542a08 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 20:24:33 +0000 Subject: [Bug #414032] Make the 'sdist' command work when the distribution contains libraries. This is done by adding a .get_source_files() method, contributed by Rene Liebscher and slightly modified. Remove an unused local variable spotted by PyChecker --- command/build_clib.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index 063da915..69ed0445 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -184,9 +184,25 @@ class build_clib (Command): # get_library_names () - def build_libraries (self, libraries): + def get_source_files (self): + self.check_library_list(self.libraries) + filenames = [] + for (lib_name, build_info) in self.libraries: + sources = build_info.get('sources') + if (sources is None or + type(sources) not in (ListType, TupleType) ): + raise DistutilsSetupError, \ + ("in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames") % lib_name + + filenames.extend(sources) + + return filenames + # get_source_files () - compiler = self.compiler + + def build_libraries (self, libraries): for (lib_name, build_info) in libraries: sources = build_info.get('sources') -- cgit v1.2.1 From 33c706bf78fbd8f9aad6ec837164a703b8a9a2e2 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 13 Aug 2001 13:56:24 +0000 Subject: Fix for NameError caught by PyChecker. (This command seems to be essentially untested; should fix that...) --- command/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/config.py b/command/config.py index ce6cff8c..d4096918 100644 --- a/command/config.py +++ b/command/config.py @@ -272,8 +272,8 @@ class config (Command): from distutils.ccompiler import CompileError, LinkError self._check_compiler() try: - self._link(body, headers, include_dirs, - libraries, library_dirs, lang) + src, obj, exe = self._link(body, headers, include_dirs, + libraries, library_dirs, lang) self.spawn([exe]) ok = 1 except (CompileError, LinkError, DistutilsExecError): -- cgit v1.2.1 From dbdaae041996677d97be3ed6bed2b0bd59643989 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 16 Aug 2001 13:56:40 +0000 Subject: [Patch #442530 from twburton] Provide include_dirs argument to all calls to ._preprocess and ._compile Fix typo: pattern.search(pattern) should be pattern.search(line) --- command/config.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/command/config.py b/command/config.py index d4096918..2df42a57 100644 --- a/command/config.py +++ b/command/config.py @@ -187,7 +187,7 @@ class config (Command): self._check_compiler() ok = 1 try: - self._preprocess(body, headers, lang) + self._preprocess(body, headers, include_dirs, lang) except CompileError: ok = 0 @@ -205,7 +205,7 @@ class config (Command): """ self._check_compiler() - (src, out) = self._preprocess(body, headers, lang) + (src, out) = self._preprocess(body, headers, include_dirs, lang) if type(pattern) is StringType: pattern = re.compile(pattern) @@ -216,7 +216,7 @@ class config (Command): line = file.readline() if line == '': break - if pattern.search(pattern): + if pattern.search(line): match = 1 break @@ -231,7 +231,7 @@ class config (Command): from distutils.ccompiler import CompileError self._check_compiler() try: - self._compile(body, headers, lang) + self._compile(body, headers, include_dirs, lang) ok = 1 except CompileError: ok = 0 -- cgit v1.2.1 From 95bdf9afbb9a6455ee440381fc6226fd00aede8f Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 16 Aug 2001 14:08:02 +0000 Subject: [Patch #444854 from twburton] Add executable extension, needed to get the program name right on Win32 --- command/config.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/command/config.py b/command/config.py index 2df42a57..b89997a2 100644 --- a/command/config.py +++ b/command/config.py @@ -148,10 +148,13 @@ class config (Command): libraries, library_dirs, lang): (src, obj) = self._compile(body, headers, include_dirs, lang) prog = os.path.splitext(os.path.basename(src))[0] - self.temp_files.append(prog) # XXX should be prog + exe_ext self.compiler.link_executable([obj], prog, libraries=libraries, library_dirs=library_dirs) + + prog = prog + self.compiler.exe_extension + self.temp_files.append(prog) + return (src, obj, prog) def _clean (self, *filenames): -- cgit v1.2.1 From d786b1a936ba0f38409c95d2906c908d1ba245a1 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 16 Aug 2001 20:17:41 +0000 Subject: [Patch #441691] preprocess() method for Borland C compiler. I have no way of testing this. --- bcppcompiler.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 2742b5fe..5c0fae8b 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -21,7 +21,7 @@ from distutils.errors import \ from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options from distutils.file_util import write_file - +from distutils.dep_util import newer class BCPPCompiler(CCompiler) : """Concrete class that implements an interface to the Borland C/C++ @@ -373,3 +373,37 @@ class BCPPCompiler(CCompiler) : return obj_names # object_filenames () + + def preprocess (self, + source, + output_file=None, + macros=None, + include_dirs=None, + extra_preargs=None, + extra_postargs=None): + + (_, macros, include_dirs) = \ + self._fix_compile_args(None, macros, include_dirs) + pp_opts = gen_preprocess_options(macros, include_dirs) + pp_args = ['cpp32.exe'] + pp_opts + if output_file is not None: + pp_args.append('-o' + output_file) + if extra_preargs: + pp_args[:0] = extra_preargs + if extra_postargs: + pp_args.extend(extra_postargs) + pp_args.append(source) + + # We need to preprocess: either we're being forced to, or the + # source file is newer than the target (or the target doesn't + # exist). + if self.force or output_file is None or newer(source, output_file): + if output_file: + self.mkpath(os.path.dirname(output_file)) + try: + self.spawn(pp_args) + except DistutilsExecError, msg: + print msg + raise CompileError, msg + + # preprocess() -- cgit v1.2.1 From f5528a1e2f78f25f3208d3365f6b0ed3f5e7e74e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 23 Aug 2001 20:53:27 +0000 Subject: Patch #449054 to implement PEP 250. The standard install directory for modules and extensions on Windows is now $PREFIX/Lib/site-packages. Includes backwards compatibility code for pre-2.2 Pythons. Contributed by Paul Moore. --- command/install.py | 25 ++++++++++++++++++------- sysconfig.py | 5 ++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/command/install.py b/command/install.py index 1d0a34e4..5af4cf10 100644 --- a/command/install.py +++ b/command/install.py @@ -16,6 +16,23 @@ from distutils.util import convert_path, subst_vars, change_root from distutils.errors import DistutilsOptionError from glob import glob +if sys.version < "2.2": + WINDOWS_SCHEME = { + 'purelib': '$base', + 'platlib': '$base', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } +else: + WINDOWS_SCHEME = { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } + INSTALL_SCHEMES = { 'unix_prefix': { 'purelib': '$base/lib/python$py_version_short/site-packages', @@ -31,13 +48,7 @@ INSTALL_SCHEMES = { 'scripts': '$base/bin', 'data' : '$base', }, - 'nt': { - 'purelib': '$base', - 'platlib': '$base', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - }, + 'nt': WINDOWS_SCHEME, 'mac': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', diff --git a/sysconfig.py b/sysconfig.py index 1f0d1453..558ff293 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -94,7 +94,10 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): if standard_lib: return os.path.join(prefix, "Lib") else: - return prefix + if sys.version < "2.2": + return prefix + else: + return os.path.join(PREFIX, "Lib", "site-packages") elif os.name == "mac": if plat_specific: -- cgit v1.2.1 From 679698bbb368dc2cee79992418c7720f2acce34a Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Mon, 27 Aug 2001 15:08:16 +0000 Subject: Patch by Bill Noon: added 'dylib' as a library type along with 'static' and 'shared'. This fixes extension building for dynamic Pythons on MacOSX. --- ccompiler.py | 4 ++-- unixccompiler.py | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 4a282d4c..4efd9340 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -792,8 +792,8 @@ class CCompiler: output_dir=''): if output_dir is None: output_dir = '' - if lib_type not in ("static","shared"): - raise ValueError, "'lib_type' must be \"static\" or \"shared\"" + if lib_type not in ("static","shared","dylib"): + raise ValueError, "'lib_type' must be \"static\", \"shared\" or \"dylib\"" fmt = getattr (self, lib_type + "_lib_format") ext = getattr (self, lib_type + "_lib_extension") diff --git a/unixccompiler.py b/unixccompiler.py index da1f2a4e..a4f0ac4d 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -71,7 +71,8 @@ class UnixCCompiler (CCompiler): obj_extension = ".o" static_lib_extension = ".a" shared_lib_extension = ".so" - static_lib_format = shared_lib_format = "lib%s%s" + dylib_lib_extension = ".dylib" + static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" @@ -259,6 +260,8 @@ class UnixCCompiler (CCompiler): for dir in dirs: shared = os.path.join( dir, self.library_filename(lib, lib_type='shared')) + dylib = os.path.join( + dir, self.library_filename(lib, lib_type='dylib')) static = os.path.join( dir, self.library_filename(lib, lib_type='static')) @@ -266,7 +269,9 @@ class UnixCCompiler (CCompiler): # data to go on: GCC seems to prefer the shared library, so I'm # assuming that *all* Unix C compilers do. And of course I'm # ignoring even GCC's "-static" option. So sue me. - if os.path.exists(shared): + if os.path.exists(dylib): + return dylib + elif os.path.exists(shared): return shared elif os.path.exists(static): return static -- cgit v1.2.1 From 31ba5afaf948150804377ad1f4a508e9578d1549 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Wed, 29 Aug 2001 23:57:22 +0000 Subject: Flush output more aggressively. This makes things look better if the setup script is running from inside Vim. --- cmd.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd.py b/cmd.py index ce448294..85a7f461 100644 --- a/cmd.py +++ b/cmd.py @@ -188,6 +188,7 @@ class Command: """ if self.verbose >= level: print msg + sys.stdout.flush() def debug_print (self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the @@ -196,6 +197,7 @@ class Command: from distutils.core import DEBUG if DEBUG: print msg + sys.stdout.flush() -- cgit v1.2.1 From 8035ea689daa1273bbcb133c80e51c70a4fa3ca6 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Mon, 3 Sep 2001 15:47:21 +0000 Subject: Don't use dir() to find instance attribute names. --- dist.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dist.py b/dist.py index 3803f5cc..40dcc96e 100644 --- a/dist.py +++ b/dist.py @@ -122,9 +122,7 @@ class Distribution: # worth it. Also delegate 'get_XXX()' methods to the 'metadata' # object in a sneaky and underhanded (but efficient!) way. self.metadata = DistributionMetadata() - method_basenames = dir(self.metadata) + \ - ['fullname', 'contact', 'contact_email'] - for basename in method_basenames: + for basename in self.metadata._METHOD_BASENAMES: method_name = "get_" + basename setattr(self, method_name, getattr(self.metadata, method_name)) @@ -962,6 +960,12 @@ class DistributionMetadata: author, and so forth. """ + _METHOD_BASENAMES = ("name", "version", "author", "author_email", + "maintainer", "maintainer_email", "url", + "license", "description", "long_description", + "keywords", "platforms", "fullname", "contact", + "contact_email", "licence") + def __init__ (self): self.name = None self.version = None -- cgit v1.2.1 From 66b732945438e944b73f2417ac97195af45c49d7 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Tue, 4 Sep 2001 12:01:49 +0000 Subject: On the mac some library paths returned were outdated, some were outright funny. Fixed. --- sysconfig.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 558ff293..cc663a83 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -102,16 +102,14 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): elif os.name == "mac": if plat_specific: if standard_lib: - return os.path.join(prefix, "Mac", "Plugins") + return os.path.join(prefix, "Lib", "lib-dynload") else: - raise DistutilsPlatformError( - "OK, where DO site-specific extensions go on the Mac?") + return os.path.join(prefix, "Lib", "site-packages") else: if standard_lib: return os.path.join(prefix, "Lib") else: - raise DistutilsPlatformError( - "OK, where DO site-specific modules go on the Mac?") + return os.path.join(prefix, "Lib", "site-packages") else: raise DistutilsPlatformError( "I don't know where Python installs its library " -- cgit v1.2.1 From 6d5c86825f122583f4fec4be228f10e2c712e61e Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 4 Sep 2001 20:06:43 +0000 Subject: [Bug #436732] install.py does not record a created *.pth file in the INSTALLED_FILES output. Modified version of a patch from Jon Nelson (jnelson) --- command/install.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/command/install.py b/command/install.py index 5af4cf10..022e34a2 100644 --- a/command/install.py +++ b/command/install.py @@ -537,8 +537,7 @@ class install (Command): # -- Reporting methods --------------------------------------------- def get_outputs (self): - # This command doesn't have any outputs of its own, so just - # get the outputs of all its sub-commands. + # Assemble the outputs of all the sub-commands. outputs = [] for cmd_name in self.get_sub_commands(): cmd = self.get_finalized_command(cmd_name) @@ -548,6 +547,10 @@ class install (Command): if filename not in outputs: outputs.append(filename) + if self.path_file and self.install_path_file: + outputs.append(os.path.join(self.install_libbase, + self.path_file + ".pth")) + return outputs def get_inputs (self): -- cgit v1.2.1 From 33914718f28f60a0d0ff5f79bc6662748fd1dc6b Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 4 Sep 2001 20:42:08 +0000 Subject: [Bug #444589] Record empty directories in the install_data command Slightly modified version of patch from Jon Nelson (jnelson). --- command/install_data.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/command/install_data.py b/command/install_data.py index 28f59386..d0091ce2 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -63,10 +63,18 @@ class install_data (Command): elif self.root: dir = change_root(self.root, dir) self.mkpath(dir) - for data in f[1]: - data = convert_path(data) - (out, _) = self.copy_file(data, dir) - self.outfiles.append(out) + + if f[1] == []: + # If there are no files listed, the user must be + # trying to create an empty directory, so add the + # directory to the list of output files. + self.outfiles.append(dir) + else: + # Copy files, adding them to the list of output files. + for data in f[1]: + data = convert_path(data) + (out, _) = self.copy_file(data, dir) + self.outfiles.append(out) def get_inputs (self): return self.data_files or [] -- cgit v1.2.1 From 4a7fbb7560c4f661bf8314166f6d26c0bfd25882 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 5 Sep 2001 12:02:59 +0000 Subject: [Bug #404274] Restore some special-case code for AIX and BeOS under 1.5.2. This will have to stay until we decide to drop 1.5.2 compatibility completely. --- sysconfig.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index cc663a83..935372cd 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -318,7 +318,34 @@ def _init_posix(): # the scripts are in another directory. if python_build: g['LDSHARED'] = g['BLDSHARED'] - + + elif sys.version < '2.1': + # The following two branches are for 1.5.2 compatibility. + if sys.platform == 'aix4': # what about AIX 3.x ? + # Linker script is in the config directory, not in Modules as the + # Makefile says. + python_lib = get_python_lib(standard_lib=1) + ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') + python_exp = os.path.join(python_lib, 'config', 'python.exp') + + g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) + + elif sys.platform == 'beos': + # Linker script is in the config directory. In the Makefile it is + # relative to the srcdir, which after installation no longer makes + # sense. + python_lib = get_python_lib(standard_lib=1) + linkerscript_name = os.path.basename(string.split(g['LDSHARED'])[0]) + linkerscript = os.path.join(python_lib, 'config', linkerscript_name) + + # XXX this isn't the right place to do this: adding the Python + # library to the link, if needed, should be in the "build_ext" + # command. (It's also needed for non-MS compilers on Windows, and + # it's taken care of for them by the 'build_ext.get_libraries()' + # method.) + g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % + (linkerscript, PREFIX, sys.version[0:3])) + global _config_vars _config_vars = g -- cgit v1.2.1 From e9d279e9c8eb2b7b6bbdf3f9c6e01eb27684dce7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 5 Sep 2001 13:00:40 +0000 Subject: Implement PEP250: Use Lib/site-packages under windows. bdist_wininst doesn't use the NT SCHEME any more, instead a custom SCHEME is used, which is exchanged at installation time, depending on the python version used. Avoid a bogus warning frpom install_lib about installing into a directory not on sys.path. --- command/bdist_wininst.py | 617 +++++++++++++++++++++++------------------------ 1 file changed, 306 insertions(+), 311 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 16a6cc41..8e4a7964 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -83,46 +83,41 @@ class bdist_wininst (Command): install = self.reinitialize_command('install') install.root = self.bdist_dir - if os.name != 'nt': - # Must force install to use the 'nt' scheme; we set the - # prefix too just because it looks silly to put stuff - # in (eg.) ".../usr/Scripts", "usr/Include", etc. - install.prefix = "Python" - install.select_scheme('nt') install_lib = self.reinitialize_command('install_lib') # we do not want to include pyc or pyo files install_lib.compile = 0 install_lib.optimize = 0 - install_lib.ensure_finalized() + # Use a custom scheme for the zip-file, because we have to decide + # at installation time which scheme to use. + for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): + value = string.upper(key) + if key == 'headers': + value = value + '/Include/$dist_name' + setattr(install, + 'install_' + key, + value) self.announce("installing to %s" % self.bdist_dir) install.ensure_finalized() + + # avoid warning of 'install_lib' about installing + # into a directory not in sys.path + sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) + install.run() + del sys.path[0] + # And make an archive relative to the root of the # pseudo-installation tree. fullname = self.distribution.get_fullname() archive_basename = os.path.join(self.bdist_dir, "%s.win32" % fullname) - # Our archive MUST be relative to sys.prefix, which is the - # same as install_purelib in the 'nt' scheme. - root_dir = os.path.normpath(install.install_purelib) - - # Sanity check: Make sure everything is included - for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): - attrname = 'install_' + key - install_x = getattr(install, attrname) - # (Use normpath so that we can string.find to look for - # subdirectories) - install_x = os.path.normpath(install_x) - if string.find(install_x, root_dir) != 0: - raise DistutilsInternalError \ - ("'%s' not included in install_lib" % key) arcname = self.make_archive(archive_basename, "zip", - root_dir=root_dir) + root_dir=self.bdist_dir) self.create_exe(arcname, fullname, self.bitmap) if not self.keep_temp: @@ -233,308 +228,308 @@ if __name__ == '__main__': EXEDATA = """\ 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 -- cgit v1.2.1 From 74eebff9df62f424e6be3262778cd50611dd7dbd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 5 Oct 2001 20:40:48 +0000 Subject: Explicitely list the metadata attributes to show in the gui. Updated to include the new exe-file. --- command/bdist_wininst.py | 599 ++++++++++++++++++++++++----------------------- 1 file changed, 304 insertions(+), 295 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 8e4a7964..7cdf3855 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -140,13 +140,13 @@ class bdist_wininst (Command): # describing the items to be installed. info = (metadata.long_description or '') + '\n' - for name in dir(metadata): - if (name != 'long_description'): - data = getattr(metadata, name) - if data: - info = info + ("\n %s: %s" % \ - (string.capitalize(name), data)) - lines.append("%s=%s" % (name, repr(data)[1:-1])) + for name in ["author", "author_email", "description", "maintainer", + "maintainer_email", "name", "url", "version"]: + data = getattr(metadata, name, "") + if data: + info = info + ("\n %s: %s" % \ + (string.capitalize(name), data)) + lines.append("%s=%s" % (name, repr(data)[1:-1])) # The [setup] section contains entries controlling # the installer runtime. @@ -231,20 +231,20 @@ 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 @@ -252,8 +252,8 @@ bxFWVlMFDP/Xg/j/iUX8D4WIY26+vZnUEQN1GyEg/3UQ6Bf/b7s31wBopw+EA0HrsR9QdAmPbduz UI/rL1wgGOpTDGoCrM2W7f9VIPDALmcQZronYy91JS67aFTH6Xbf891TAes7B1kO8yR0Cq3QHvkT A41F9G4GAgx7n4UYQtB9/BIDvO7NNEioNBR1CQvIlgbTfTN/DlZqBFYQxBD7GlyEyHyJfg9hOIKz 3drmPOsmpSsCUyqs+b5tW1OnCCWLBDvGdRcnEMKGNuEoco4KM8BsC+3/5FvJOIN9EAhTi10IaUOS -druwffI4k8jdUOjISxJFsnzb3AwvUMgIFEBqAcz+c7ftGF4G2CVoqFEq8VCJXdS/sLDtLSG81xw7 -dGn/dChQaO72+b6QmBlLBCLcjnQTGnOd+5YNfIsEyYr2IR8byFn3IWw6Lh9kQ+2w0VoDxUUSPsge +druwffI4k8jdUOjITBJFsnzb3AwvUMgIFEBqAcz+c7ftGF4G2CVoqFEq8VCJXdS/sLDtLSK81xw7 +dGn/dChQaO72+b6QmBlLBCPcjnQTGnOd+5YNfIsEyYr2IR8byFn3Imw6Lh9kQ+2w0VoDxUUSPsge uu29U5eUjV7wzBTGxp3hw86B7Cjhq4tVEESN/9v/i0wC+o1cAupXn+ArQwwrwYPoFosb/5fb/8+B O1BLBQaJfejwbwKDZRQAZoN7CgAP/jf33Y5kDusJi03sP8zoi0QRKo00EQNts13eM/qBPgECMD6B Pwt/6z7bAwQ8MsEulIkxA8oPv1Ye2x67bQj0Bk4gDBwDVRXRCNv2pXlPHInBVxoD0JsQFuhGaPzt @@ -263,294 +263,303 @@ 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 -AAAAAAAAAAAAAAAAAQBuAAAAOAAAgAAAAAAAAAAAAAAAAAAAAQAAAAAAUAAAADCxAAAICgAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAQAawAAAJAAAIBsAAAAuAAAgG0AAADgAACAbgAAAAgBAIAAAAAA -AAAAAAAAAAAAAAEACQQAAKgAAAA4uwAAoAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAADQ -AAAA2LwAAGIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJBAAA+AAAAEC+AABaAgAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAEACQQAACABAACgwAAAXAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9PEA -ALzxAAAAAAAAAAAAAAAAAAAB8gAAzPEAAAAAAAAAAAAAAAAAAA7yAADU8QAAAAAAAAAAAAAAAAAA -G/IAANzxAAAAAAAAAAAAAAAAAAAl8gAA5PEAAAAAAAAAAAAAAAAAADDyAADs8QAAAAAAAAAAAAAA -AAAAAAAAAAAAAAA68gAASPIAAFjyAAAAAAAAZvIAAAAAAAB08gAAAAAAAITyAAAAAAAAjvIAAAAA -AACU8gAAAAAAAEtFUk5FTDMyLkRMTABBRFZBUEkzMi5kbGwAQ09NQ1RMMzIuZGxsAEdESTMyLmRs -bABNU1ZDUlQuZGxsAFVTRVIzMi5kbGwAAExvYWRMaWJyYXJ5QQAAR2V0UHJvY0FkZHJlc3MAAEV4 -aXRQcm9jZXNzAAAAUmVnQ2xvc2VLZXkAAABQcm9wZXJ0eVNoZWV0QQAAVGV4dE91dEEAAGV4aXQA -AEdldERDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgACAAAAIAAAgAUAAABgAACAAAAA +AAAAAAAAAAAAAAABAG4AAAA4AACAAAAAAAAAAAAAAAAAAAABAAAAAABQAAAAMLEAAAgKAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAABABrAAAAkAAAgGwAAAC4AACAbQAAAOAAAIBuAAAACAEAgAAAAAAA +AAAAAAAAAAAAAQAJBAAAqAAAADi7AACgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAANAA +AADYvAAABAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAAD4AAAA4L4AAFoCAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAQAJBAAAIAEAAEDBAABcAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAD0AQEA +vAEBAAAAAAAAAAAAAAAAAAECAQDMAQEAAAAAAAAAAAAAAAAADgIBANQBAQAAAAAAAAAAAAAAAAAb +AgEA3AEBAAAAAAAAAAAAAAAAACUCAQDkAQEAAAAAAAAAAAAAAAAAMAIBAOwBAQAAAAAAAAAAAAAA +AAAAAAAAAAAAADoCAQBIAgEAWAIBAAAAAABmAgEAAAAAAHQCAQAAAAAAhAIBAAAAAACOAgEAAAAA +AJQCAQAAAAAAS0VSTkVMMzIuRExMAEFEVkFQSTMyLmRsbABDT01DVEwzMi5kbGwAR0RJMzIuZGxs +AE1TVkNSVC5kbGwAVVNFUjMyLmRsbAAATG9hZExpYnJhcnlBAABHZXRQcm9jQWRkcmVzcwAARXhp +dFByb2Nlc3MAAABSZWdDbG9zZUtleQAAAFByb3BlcnR5U2hlZXRBAABUZXh0T3V0QQAAZXhpdAAA +R2V0REMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAA= """ # --- EOF --- -- cgit v1.2.1 From b00902df9fcae870190f460790a9293c8eda1c61 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 5 Oct 2001 20:43:09 +0000 Subject: With Andrew's blessing: distutils version number is now 1.0.3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 27e79a54..1055aa39 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "1.0.2" +__version__ = "1.0.3" -- cgit v1.2.1 From 333b9da550f41bac8faf5a4b3bfccc53a54da671 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sat, 10 Nov 2001 23:20:22 +0000 Subject: The libraries argument was completely ignored, fixed. Reported by Tom Loredo. --- mwerkscompiler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 46e16e2e..e759456a 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -16,7 +16,7 @@ import distutils.dir_util import mkcwproject class MWerksCompiler (CCompiler) : - """Concrete class that implements an interface to Microsoft Visual C++, + """Concrete class that implements an interface to MetroWerks CodeWarrior, as defined by the CCompiler abstract class.""" compiler_type = 'mwerks' @@ -150,6 +150,7 @@ class MWerksCompiler (CCompiler) : if not dirname in sourcefiledirs: sourcefiledirs.append(dirname) settings['sources'] = sourcefilenames + settings['libraries'] = libraries settings['extrasearchdirs'] = sourcefiledirs + include_dirs + library_dirs if self.dry_run: print 'CALLING LINKER IN', os.getcwd() -- cgit v1.2.1 From 6fdff7d1bce93929138b277904edd9508b635978 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 6 Dec 2001 16:32:05 +0000 Subject: [Bug #459270] Fix incorrect docstring --- dist.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dist.py b/dist.py index 40dcc96e..3e1bc648 100644 --- a/dist.py +++ b/dist.py @@ -262,14 +262,11 @@ class Distribution: should be parsed. The filenames returned are guaranteed to exist (modulo nasty race conditions). - On Unix, there are three possible config files: pydistutils.cfg in - the Distutils installation directory (ie. where the top-level - Distutils __inst__.py file lives), .pydistutils.cfg in the user's - home directory, and setup.cfg in the current directory. - - On Windows and Mac OS, there are two possible config files: - pydistutils.cfg in the Python installation directory (sys.prefix) - and setup.cfg in the current directory. + There are three possible config files: distutils.cfg in the + Distutils installation directory (ie. where the top-level + Distutils __inst__.py file lives), a file in the user's home + directory named .pydistutils.cfg on Unix and pydistutils.cfg + on Windows/Mac, and setup.cfg in the current directory. """ files = [] check_environ() -- cgit v1.2.1 From 1a4ad0259c7382d0520aa9dad7e6af852b3d7ad9 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 6 Dec 2001 20:44:19 +0000 Subject: Use a version number of 0.0.0 instead of ???. The latter leads to invalid filenames on Windows when building without specifying a version number in the setup script. See also http://mail.python.org/pipermail/distutils-sig/2001-November/002656.html Bugfix candidate. --- dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 3e1bc648..d5bfa05d 100644 --- a/dist.py +++ b/dist.py @@ -1012,7 +1012,7 @@ class DistributionMetadata: return self.name or "UNKNOWN" def get_version(self): - return self.version or "???" + return self.version or "0.0.0" def get_fullname (self): return "%s-%s" % (self.get_name(), self.get_version()) -- cgit v1.2.1 From 68cb7fe9ae037b7a517b692213c03c6c0e54f7e4 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 6 Dec 2001 20:51:35 +0000 Subject: Whitespace normalization. --- archive_util.py | 6 ++-- bcppcompiler.py | 32 +++++++++--------- ccompiler.py | 34 +++++++++---------- cmd.py | 20 +++++------ core.py | 4 +-- cygwinccompiler.py | 97 +++++++++++++++++++++++++++--------------------------- dep_util.py | 8 ++--- dir_util.py | 6 ++-- dist.py | 28 ++++++++-------- extension.py | 4 +-- fancy_getopt.py | 12 +++---- file_util.py | 16 ++++----- filelist.py | 28 ++++++++-------- msvccompiler.py | 18 +++++----- mwerkscompiler.py | 32 +++++++++--------- spawn.py | 12 +++---- sysconfig.py | 8 ++--- text_file.py | 13 ++++---- unixccompiler.py | 16 ++++----- util.py | 16 ++++----- version.py | 16 ++++----- 21 files changed, 210 insertions(+), 216 deletions(-) diff --git a/archive_util.py b/archive_util.py index 4c5641b9..58d9a062 100644 --- a/archive_util.py +++ b/archive_util.py @@ -31,7 +31,7 @@ def make_tarball (base_name, base_dir, compress="gzip", compress_ext = { 'gzip': ".gz", 'bzip2': '.bz2', 'compress': ".Z" } - + # flags for compression program, each element of list will be an argument compress_flags = {'gzip': ["-f9"], 'compress': ["-f"], @@ -85,7 +85,7 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): import zipfile except ImportError: raise DistutilsExecError, \ - ("unable to create zip file '%s': " + + ("unable to create zip file '%s': " + "could neither find a standalone zip utility nor " + "import the 'zipfile' module") % zip_filename @@ -152,7 +152,7 @@ def make_archive (base_name, format, kwargs = { 'verbose': verbose, 'dry_run': dry_run } - + try: format_info = ARCHIVE_FORMATS[format] except KeyError: diff --git a/bcppcompiler.py b/bcppcompiler.py index 5c0fae8b..9ebba2d8 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -102,7 +102,7 @@ class BCPPCompiler(CCompiler) : compile_opts.extend (self.compile_options_debug) else: compile_opts.extend (self.compile_options) - + for i in range (len (sources)): src = sources[i] ; obj = objects[i] ext = (os.path.splitext (src))[1] @@ -130,11 +130,11 @@ class BCPPCompiler(CCompiler) : input_opt = "" elif ext in self._cpp_extensions: input_opt = "-P" - else: + else: # Unknown file type -- no extra options. The compiler # will probably fail, but let it just in case this is a # file the compiler recognizes even if we don't. - input_opt = "" + input_opt = "" output_opt = "-o" + obj @@ -174,17 +174,17 @@ class BCPPCompiler(CCompiler) : if extra_postargs: lib_args.extend (extra_postargs) try: - self.spawn ([self.lib] + lib_args) + self.spawn ([self.lib] + lib_args) except DistutilsExecError, msg: - raise LibError, msg + raise LibError, msg else: self.announce ("skipping %s (up-to-date)" % output_filename) # create_static_lib () - - + + def link (self, - target_desc, + target_desc, objects, output_filename, output_dir=None, @@ -254,14 +254,14 @@ class BCPPCompiler(CCompiler) : resources.append(file) else: objects.append(file) - - + + for l in library_dirs: - ld_args.append("/L%s" % os.path.normpath(l)) + ld_args.append("/L%s" % os.path.normpath(l)) ld_args.append("/L.") # we sometimes use relative paths - # list of object files - ld_args.extend(objects) + # list of object files + ld_args.extend(objects) # XXX the command-line syntax for Borland C++ is a bit wonky; # certain filenames are jammed together in one big string, but @@ -275,11 +275,11 @@ class BCPPCompiler(CCompiler) : # name of dll/exe file ld_args.extend([',',output_filename]) - # no map file and start libraries + # no map file and start libraries ld_args.append(',,') for lib in libraries: - # see if we find it and if there is a bcpp specific lib + # see if we find it and if there is a bcpp specific lib # (xxx_bcpp.lib) libfile = self.find_library_file(library_dirs, lib, debug) if libfile is None: @@ -300,7 +300,7 @@ class BCPPCompiler(CCompiler) : ld_args.append(',') ld_args.extend(resources) - + if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: diff --git a/ccompiler.py b/ccompiler.py index 4efd9340..21bf0c17 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -61,7 +61,7 @@ class CCompiler: # different versions of libfoo.a in different locations. I # think this is useless without the ability to null out the # library search path anyways. - + # Subclasses that rely on the standard filename generation methods # implemented below should override these; see the comment near @@ -159,7 +159,7 @@ class CCompiler: setattr(self, key, split_quoted(value)) else: setattr(self, key, value) - + def _find_macro (self, name): @@ -352,7 +352,7 @@ class CCompiler: else: raise TypeError, \ "'include_dirs' (if supplied) must be a list of strings" - + return (output_dir, macros, include_dirs) # _fix_compile_args () @@ -364,7 +364,7 @@ class CCompiler: list of all object files and a dictionary telling which source files can be skipped. """ - # Get the list of expected output (object) files + # Get the list of expected output (object) files objects = self.object_filenames (sources, strip_dir=1, output_dir=output_dir) @@ -401,7 +401,7 @@ class CCompiler: raise TypeError, \ "'objects' must be a list or tuple of strings" objects = list (objects) - + if output_dir is None: output_dir = self.output_dir elif type (output_dir) is not StringType: @@ -560,7 +560,7 @@ class CCompiler: Raises LibError on failure. """ pass - + # values for target_desc parameter in link() SHARED_OBJECT = "shared_object" @@ -621,7 +621,7 @@ class CCompiler: """ raise NotImplementedError - + # Old 'link_*()' methods, rewritten to use the new 'link()' method. def link_shared_lib (self, @@ -636,13 +636,13 @@ class CCompiler: extra_preargs=None, extra_postargs=None, build_temp=None): - self.link(CCompiler.SHARED_LIBRARY, objects, + self.link(CCompiler.SHARED_LIBRARY, objects, self.library_filename(output_libname, lib_type='shared'), output_dir, libraries, library_dirs, runtime_library_dirs, export_symbols, debug, extra_preargs, extra_postargs, build_temp) - + def link_shared_object (self, objects, @@ -673,9 +673,9 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None): - self.link(CCompiler.EXECUTABLE, objects, + self.link(CCompiler.EXECUTABLE, objects, self.executable_filename(output_progname), output_dir, - libraries, library_dirs, runtime_library_dirs, None, + libraries, library_dirs, runtime_library_dirs, None, debug, extra_preargs, extra_postargs, None) @@ -846,12 +846,12 @@ _default_compilers = ( # on a cygwin built python we can use gcc like an ordinary UNIXish # compiler ('cygwin.*', 'unix'), - + # OS name mappings ('posix', 'unix'), ('nt', 'msvc'), ('mac', 'mwerks'), - + ) def get_default_compiler(osname=None, platform=None): @@ -901,7 +901,7 @@ def show_compilers(): # XXX this "knows" that the compiler option it's describing is # "--compiler", which just happens to be the case for the three # commands that use it. - from distutils.fancy_getopt import FancyGetopt + from distutils.fancy_getopt import FancyGetopt compilers = [] for compiler in compiler_class.keys(): compilers.append(("compiler="+compiler, None, @@ -909,7 +909,7 @@ def show_compilers(): compilers.sort() pretty_printer = FancyGetopt(compilers) pretty_printer.print_help("List of available compilers:") - + def new_compiler (plat=None, compiler=None, @@ -932,14 +932,14 @@ def new_compiler (plat=None, try: if compiler is None: compiler = get_default_compiler(plat) - + (module_name, class_name, long_description) = compiler_class[compiler] except KeyError: msg = "don't know how to compile C/C++ code on platform '%s'" % plat if compiler is not None: msg = msg + " with '%s' compiler" % compiler raise DistutilsPlatformError, msg - + try: module_name = "distutils." + module_name __import__ (module_name) diff --git a/cmd.py b/cmd.py index 85a7f461..65060d67 100644 --- a/cmd.py +++ b/cmd.py @@ -41,7 +41,7 @@ class Command: # current situation. (Eg. we "install_headers" is only applicable if # we have any C header files to install.) If 'predicate' is None, # that command is always applicable. - # + # # 'sub_commands' is usually defined at the *end* of a class, because # predicates can be unbound methods, so they must already have been # defined. The canonical example is the "install" command. @@ -111,7 +111,7 @@ class Command: if not self.finalized: self.finalize_options() self.finalized = 1 - + # Subclasses must define: # initialize_options() @@ -133,12 +133,12 @@ class Command: command-line. Thus, this is not the place to code dependencies between options; generally, 'initialize_options()' implementations are just a bunch of "self.foo = None" assignments. - + This method must be implemented by all command classes. """ raise RuntimeError, \ "abstract method -- subclass %s must override" % self.__class__ - + def finalize_options (self): """Set final values for all the options that this command supports. This is always called as late as possible, ie. after any option @@ -198,12 +198,12 @@ class Command: if DEBUG: print msg sys.stdout.flush() - + # -- Option validation methods ------------------------------------- # (these are very handy in writing the 'finalize_options()' method) - # + # # NB. the general philosophy here is to ensure that a particular option # value meets certain type and value constraints. If not, we try to # force it into conformance (eg. if we expect a list but have a string, @@ -252,7 +252,7 @@ class Command: raise DistutilsOptionError, \ "'%s' must be a list of strings (got %s)" % \ (option, `val`) - + def _ensure_tested_string (self, option, tester, what, error_fmt, default=None): val = self._ensure_stringlike(option, what, default) @@ -382,7 +382,7 @@ class Command: and force flags. """ return dir_util.copy_tree( - infile, outfile, + infile, outfile, preserve_mode,preserve_times,preserve_symlinks, not self.force, self.verbose >= level, @@ -426,7 +426,7 @@ class Command: (outfile, string.join(infiles, ', ')) if skip_msg is None: skip_msg = "skipping %s (inputs unchanged)" % outfile - + # Allow 'infiles' to be a single string if type(infiles) is StringType: @@ -459,7 +459,7 @@ class install_misc (Command): """Common base class for installing some files in a subdirectory. Currently used by install_data and install_scripts. """ - + user_options = [('install-dir=', 'd', "directory to install the files to")] def initialize_options (self): diff --git a/core.py b/core.py index 2aab7c4d..97a741c8 100644 --- a/core.py +++ b/core.py @@ -108,7 +108,7 @@ def setup (**attrs): # Find and parse the config file(s): they will override options from # the setup script, but be overridden by the command line. dist.parse_config_files() - + if DEBUG: print "options (after parsing config files):" dist.dump_option_dicts() @@ -146,7 +146,7 @@ def setup (**attrs): raise else: raise SystemExit, error - + except (DistutilsExecError, DistutilsFileError, DistutilsOptionError, diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 07e16655..1d972823 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -18,28 +18,28 @@ cygwin in no-cygwin mode). # # see also http://starship.python.net/crew/kernr/mingw32/Notes.html # -# * We put export_symbols in a def-file, and don't use +# * We put export_symbols in a def-file, and don't use # --export-all-symbols because it doesn't worked reliable in some # tested configurations. And because other windows compilers also # need their symbols specified this no serious problem. # # tested configurations: -# -# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works +# +# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works # (after patching python's config.h and for C++ some other include files) # see also http://starship.python.net/crew/kernr/mingw32/Notes.html -# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works -# (ld doesn't support -shared, so we use dllwrap) +# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works +# (ld doesn't support -shared, so we use dllwrap) # * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now # - its dllwrap doesn't work, there is a bug in binutils 2.10.90 # see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html -# - using gcc -mdll instead dllwrap doesn't work without -static because +# - using gcc -mdll instead dllwrap doesn't work without -static because # it tries to link against dlls instead their import libraries. (If # it finds the dll first.) -# By specifying -static we force ld to link against the import libraries, -# this is windows standard and there are normally not the necessary symbols +# By specifying -static we force ld to link against the import libraries, +# this is windows standard and there are normally not the necessary symbols # in the dlls. -# *** only the version of June 2000 shows these problems +# *** only the version of June 2000 shows these problems # created 2000/05/05, Rene Liebscher @@ -60,7 +60,7 @@ class CygwinCCompiler (UnixCCompiler): static_lib_format = "lib%s%s" shared_lib_format = "%s%s" exe_extension = ".exe" - + def __init__ (self, verbose=0, dry_run=0, @@ -76,20 +76,20 @@ class CygwinCCompiler (UnixCCompiler): "Python's pyconfig.h doesn't seem to support your compiler. " + ("Reason: %s." % details) + "Compiling may fail because of undefined preprocessor macros.") - + (self.gcc_version, self.ld_version, self.dllwrap_version) = \ get_versions() self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % - (self.gcc_version, - self.ld_version, + (self.gcc_version, + self.ld_version, self.dllwrap_version) ) - # ld_version >= "2.10.90" should also be able to use + # ld_version >= "2.10.90" should also be able to use # gcc -mdll instead of dllwrap - # Older dllwraps had own version numbers, newer ones use the + # Older dllwraps had own version numbers, newer ones use the # same as the rest of binutils ( also ld ) # dllwrap 2.10.90 is buggy - if self.ld_version >= "2.10.90": + if self.ld_version >= "2.10.90": self.linker_dll = "gcc" else: self.linker_dll = "dllwrap" @@ -102,22 +102,22 @@ class CygwinCCompiler (UnixCCompiler): linker_so=('%s -mcygwin -mdll -static' % self.linker_dll)) - # cygwin and mingw32 need different sets of libraries + # cygwin and mingw32 need different sets of libraries if self.gcc_version == "2.91.57": # cygwin shouldn't need msvcrt, but without the dlls will crash # (gcc version 2.91.57) -- perhaps something about initialization self.dll_libraries=["msvcrt"] - self.warn( + self.warn( "Consider upgrading to a newer version of gcc") else: self.dll_libraries=[] - + # __init__ () # not much different of the compile method in UnixCCompiler, # but we have to insert some lines in the middle of it, so # we put here a adapted version of it. - # (If we would call compile() in the base class, it would do some + # (If we would call compile() in the base class, it would do some # initializations a second time, this is why all is done here.) def compile (self, sources, @@ -143,7 +143,7 @@ class CygwinCCompiler (UnixCCompiler): extra_postargs = [] # Compile all source files that weren't eliminated by - # '_prep_compile()'. + # '_prep_compile()'. for i in range (len (sources)): src = sources[i] ; obj = objects[i] ext = (os.path.splitext (src))[1] @@ -157,7 +157,7 @@ class CygwinCCompiler (UnixCCompiler): self.spawn (["windres","-i",src,"-o",obj]) except DistutilsExecError, msg: raise CompileError, msg - else: # for other files use the C-compiler + else: # for other files use the C-compiler try: self.spawn (self.compiler_so + cc_args + [src, '-o', obj] + @@ -184,12 +184,12 @@ class CygwinCCompiler (UnixCCompiler): extra_preargs=None, extra_postargs=None, build_temp=None): - + # use separate copies, so we can modify the lists extra_preargs = copy.copy(extra_preargs or []) libraries = copy.copy(libraries or []) objects = copy.copy(objects or []) - + # Additional libraries libraries.extend(self.dll_libraries) @@ -199,10 +199,10 @@ class CygwinCCompiler (UnixCCompiler): (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): # (The linker doesn't do anything if output is up-to-date. # So it would probably better to check if we really need this, - # but for this we had to insert some unchanged parts of - # UnixCCompiler, and this is not what we want.) + # but for this we had to insert some unchanged parts of + # UnixCCompiler, and this is not what we want.) - # we want to put some files in the same directory as the + # we want to put some files in the same directory as the # object files are, build_temp doesn't help much # where are the object files temp_dir = os.path.dirname(objects[0]) @@ -214,7 +214,7 @@ class CygwinCCompiler (UnixCCompiler): def_file = os.path.join(temp_dir, dll_name + ".def") exp_file = os.path.join(temp_dir, dll_name + ".exp") lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") - + # Generate .def file contents = [ "LIBRARY %s" % os.path.basename(output_filename), @@ -237,21 +237,21 @@ class CygwinCCompiler (UnixCCompiler): else: # doesn't work: bfd_close build\...\libfoo.a: Invalid operation #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) - # for gcc/ld the def-file is specified as any other object files + # for gcc/ld the def-file is specified as any other object files objects.append(def_file) #end: if ((export_symbols is not None) and # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): - + # who wants symbols and a many times larger output file - # should explicitly switch the debug mode on + # should explicitly switch the debug mode on # otherwise we let dllwrap/ld strip the output file - # (On my machine: 10KB < stripped_file < ??100KB + # (On my machine: 10KB < stripped_file < ??100KB # unstripped_file = stripped_file + XXX KB - # ( XXX=254 for a typical python extension)) - if not debug: - extra_preargs.append("-s") - + # ( XXX=254 for a typical python extension)) + if not debug: + extra_preargs.append("-s") + UnixCCompiler.link(self, target_desc, objects, @@ -265,7 +265,7 @@ class CygwinCCompiler (UnixCCompiler): extra_preargs, extra_postargs, build_temp) - + # link () # -- Miscellaneous methods ----------------------------------------- @@ -288,7 +288,7 @@ class CygwinCCompiler (UnixCCompiler): base = os.path.basename (base) if ext == '.res' or ext == '.rc': # these need to be compiled to object files - obj_names.append (os.path.join (output_dir, + obj_names.append (os.path.join (output_dir, base + ext + self.obj_extension)) else: obj_names.append (os.path.join (output_dir, @@ -311,7 +311,7 @@ class Mingw32CCompiler (CygwinCCompiler): force=0): CygwinCCompiler.__init__ (self, verbose, dry_run, force) - + # A real mingw32 doesn't need to specify a different entry point, # but cygwin 2.91.57 in no-cygwin-mode needs it. if self.gcc_version <= "2.91.57": @@ -322,15 +322,15 @@ class Mingw32CCompiler (CygwinCCompiler): self.set_executables(compiler='gcc -mno-cygwin -O -Wall', compiler_so='gcc -mno-cygwin -mdll -O -Wall', linker_exe='gcc -mno-cygwin', - linker_so='%s -mno-cygwin -mdll -static %s' + linker_so='%s -mno-cygwin -mdll -static %s' % (self.linker_dll, entry_point)) # Maybe we should also append -mthreads, but then the finished # dlls need another dll (mingwm10.dll see Mingw32 docs) - # (-mthreads: Support thread-safe exception handling on `Mingw32') - - # no additional libraries needed + # (-mthreads: Support thread-safe exception handling on `Mingw32') + + # no additional libraries needed self.dll_libraries=[] - + # __init__ () # class Mingw32CCompiler @@ -370,15 +370,15 @@ def check_config_h(): # GCC, and the pyconfig.h file should be OK if string.find(sys.version,"GCC") >= 0: return (CONFIG_H_OK, "sys.version mentions 'GCC'") - + fn = sysconfig.get_config_h_filename() try: # It would probably better to read single lines to search. - # But we do this only once, and it is fast enough + # But we do this only once, and it is fast enough f = open(fn) s = f.read() f.close() - + except IOError, exc: # if we can't read this file, we cannot say it is wrong # the compiler will complain later about this file as missing @@ -401,7 +401,7 @@ def get_versions(): from distutils.version import StrictVersion from distutils.spawn import find_executable import re - + gcc_exe = find_executable('gcc') if gcc_exe: out = os.popen(gcc_exe + ' -dumpversion','r') @@ -439,4 +439,3 @@ def get_versions(): else: dllwrap_version = None return (gcc_version, ld_version, dllwrap_version) - diff --git a/dep_util.py b/dep_util.py index 4b93ed02..9edba4c8 100644 --- a/dep_util.py +++ b/dep_util.py @@ -70,7 +70,7 @@ def newer_group (sources, target, missing='error'): # If the target doesn't even exist, then it's definitely out-of-date. if not os.path.exists(target): return 1 - + # Otherwise we have to find out the hard way: if *any* source file # is more recent than 'target', then 'target' is out-of-date and # we can immediately return true. If we fall through to the end @@ -80,12 +80,12 @@ def newer_group (sources, target, missing='error'): for source in sources: if not os.path.exists(source): if missing == 'error': # blow up when we stat() the file - pass - elif missing == 'ignore': # missing source dropped from + pass + elif missing == 'ignore': # missing source dropped from continue # target's dependency list elif missing == 'newer': # missing source means target is return 1 # out-of-date - + source_mtime = os.stat(source)[ST_MTIME] if source_mtime > target_mtime: return 1 diff --git a/dir_util.py b/dir_util.py index a1578bed..77007c97 100644 --- a/dir_util.py +++ b/dir_util.py @@ -49,7 +49,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): (head, tail) = os.path.split(name) tails = [tail] # stack of lone dirs to create - + while head and tail and not os.path.isdir(head): #print "splitting '%s': " % head, (head, tail) = os.path.split(head) @@ -140,7 +140,7 @@ def copy_tree (src, dst, if not dry_run and not os.path.isdir(src): raise DistutilsFileError, \ - "cannot copy tree '%s': not a directory" % src + "cannot copy tree '%s': not a directory" % src try: names = os.listdir(src) except os.error, (errno, errstr): @@ -166,7 +166,7 @@ def copy_tree (src, dst, if not dry_run: os.symlink(link_dest, dst_name) outputs.append(dst_name) - + elif os.path.isdir(src_name): outputs.extend( copy_tree(src_name, dst_name, diff --git a/dist.py b/dist.py index d5bfa05d..b648f24e 100644 --- a/dist.py +++ b/dist.py @@ -97,7 +97,7 @@ class Distribution: # -- Creation/initialization methods ------------------------------- - + def __init__ (self, attrs=None): """Construct a new Distribution instance: initialize all the attributes of a Distribution, and then use 'attrs' (a dictionary @@ -208,7 +208,7 @@ class Distribution: "invalid distribution option '%s'" % key self.finalize_options() - + # __init__ () @@ -251,7 +251,7 @@ class Distribution: print indent + " " + line # dump_option_dicts () - + # -- Config file finding/parsing methods --------------------------- @@ -378,7 +378,7 @@ class Distribution: cmdlist = self.get_command_list() self.script_args = EasyDialogs.GetArgv( self.global_options + self.display_options, cmdlist) - + # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- # because each command will be handled by a different class, and @@ -396,7 +396,7 @@ class Distribution: # for display options we return immediately if self.handle_display_options(option_order): return - + while args: args = self._parse_command_opts(parser, args) if args is None: # user asked for help (and got it) @@ -508,7 +508,7 @@ class Distribution: "must be a callable object (function, etc.)" % (`func`, help_option)) - if help_option_found: + if help_option_found: return # Put the options from the command-line into their official @@ -801,7 +801,7 @@ class Distribution: (from 'self.command_options'). """ from distutils.core import DEBUG - + command_name = command_obj.get_command_name() if option_dict is None: option_dict = self.get_option_dict(command_name) @@ -841,7 +841,7 @@ class Distribution: user-supplied values from the config files and command line. You'll have to re-finalize the command object (by calling 'finalize_options()' or 'ensure_finalized()') before using it for - real. + real. 'command' should be a command name (string) or command object. If 'reinit_subcommands' is true, also reinitializes the command's @@ -868,11 +868,11 @@ class Distribution: if reinit_subcommands: for sub in command.get_sub_commands(): - self.reinitialize_command(sub, reinit_subcommands) + self.reinitialize_command(sub, reinit_subcommands) return command - + # -- Methods that operate on the Distribution ---------------------- def announce (self, msg, level=1): @@ -976,7 +976,7 @@ class DistributionMetadata: self.long_description = None self.keywords = None self.platforms = None - + def write_pkg_info (self, base_dir): """Write the PKG-INFO file into the release tree. """ @@ -1003,9 +1003,9 @@ class DistributionMetadata: pkg_info.write('Platform: %s\n' % platform ) pkg_info.close() - + # write_pkg_info () - + # -- Metadata query methods ---------------------------------------- def get_name (self): @@ -1045,7 +1045,7 @@ class DistributionMetadata: def get_license(self): return self.license or "UNKNOWN" get_licence = get_license - + def get_description(self): return self.description or "UNKNOWN" diff --git a/extension.py b/extension.py index a63ede23..fbae7c52 100644 --- a/extension.py +++ b/extension.py @@ -16,7 +16,7 @@ from types import * # module is already big enough, and I want to make this class a bit more # complex to simplify some common cases ("foo" module in "foo.c") and do # better error-checking ("foo.c" actually exists). -# +# # Also, putting this in build_ext.py means every setup script would have to # import that large-ish module (indirectly, through distutils.core) in # order to do anything. @@ -211,7 +211,7 @@ def read_setup_file (filename): #extensions[module] = { 'sources': source_files, # 'cpp_args': cpp_args, # 'lib_args': library_args } - + return extensions # read_setup_file () diff --git a/fancy_getopt.py b/fancy_getopt.py index 83d07216..e65302fc 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -73,7 +73,7 @@ class FancyGetopt: # 'negative_alias' keeps track of options that are the boolean # opposite of some other option self.negative_alias = {} - + # These keep track of the information in the option table. We # don't actually populate these structures until we're ready to # parse the command-line, since the 'option_table' passed in here @@ -90,7 +90,7 @@ class FancyGetopt: self.option_order = [] # __init__ () - + def _build_index (self): self.option_index.clear() @@ -117,7 +117,7 @@ class FancyGetopt: return self.option_index.has_key(long_option) def get_attr_name (self, long_option): - """Translate long option name 'long_option' to the form it + """Translate long option name 'long_option' to the form it has as an attribute of some object: ie., translate hyphens to underscores.""" return string.translate(long_option, longopt_xlate) @@ -134,7 +134,7 @@ class FancyGetopt: raise DistutilsGetoptError, \ ("invalid %s '%s': " "aliased option '%s' not defined") % (what, alias, opt) - + def set_aliases (self, alias): """Set the aliases for this option parser.""" self._check_alias_dict(alias, "alias") @@ -476,7 +476,7 @@ def translate_longopt (opt): changing "-" to "_". """ return string.translate(opt, longopt_xlate) - + class OptionDummy: """Dummy class just used as a place to hold command-line option @@ -489,7 +489,7 @@ class OptionDummy: setattr(self, opt, None) # class OptionDummy - + if __name__ == "__main__": text = """\ diff --git a/file_util.py b/file_util.py index 991d8357..526e4cf5 100644 --- a/file_util.py +++ b/file_util.py @@ -35,20 +35,20 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): except os.error, (errno, errstr): raise DistutilsFileError, \ "could not open '%s': %s" % (src, errstr) - + try: fdst = open(dst, 'wb') except os.error, (errno, errstr): raise DistutilsFileError, \ "could not create '%s': %s" % (dst, errstr) - + while 1: try: buf = fsrc.read(buffer_size) except os.error, (errno, errstr): raise DistutilsFileError, \ "could not read from '%s': %s" % (src, errstr) - + if not buf: break @@ -57,7 +57,7 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): except os.error, (errno, errstr): raise DistutilsFileError, \ "could not write to '%s': %s" % (dst, errstr) - + finally: if fdst: fdst.close() @@ -134,7 +134,7 @@ def copy_file (src, dst, print "%s %s -> %s" % (action, src, dir) else: print "%s %s -> %s" % (action, src, dst) - + if dry_run: return (dst, 1) @@ -146,7 +146,7 @@ def copy_file (src, dst, except os.error, exc: raise DistutilsFileError, \ "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) - + # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) elif link == 'hard': @@ -189,7 +189,7 @@ def move_file (src, dst, """ from os.path import exists, isfile, isdir, basename, dirname import errno - + if verbose: print "moving %s -> %s" % (src, dst) @@ -232,7 +232,7 @@ def move_file (src, dst, except os.error: pass raise DistutilsFileError, \ - ("couldn't move '%s' to '%s' by copy/delete: " + + ("couldn't move '%s' to '%s' by copy/delete: " + "delete '%s' failed: %s") % \ (src, dst, src, msg) diff --git a/filelist.py b/filelist.py index 211b65f8..f7222fd9 100644 --- a/filelist.py +++ b/filelist.py @@ -7,7 +7,7 @@ and building lists of files. # created 2000/07/17, Rene Liebscher (as template.py) # most parts taken from commands/sdist.py # renamed 2000/07/29 (to filelist.py) and officially added to -# the Distutils source, Greg Ward +# the Distutils source, Greg Ward __revision__ = "$Id$" @@ -34,8 +34,8 @@ class FileList: filtering applied) """ - def __init__(self, - warn=None, + def __init__(self, + warn=None, debug_print=None): # use standard warning and debug functions if no other given self.warn = warn or self.__warn @@ -53,10 +53,10 @@ class FileList: # -- Fallback warning/debug functions ------------------------------ - + def __warn (self, msg): sys.stderr.write("warning: %s\n" % msg) - + def __debug_print (self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the DISTUTILS_DEBUG environment variable) flag is true. @@ -93,7 +93,7 @@ class FileList: # -- "File template" methods --------------------------------------- - + def _parse_template_line (self, line): words = string.split(line) action = words[0] @@ -129,9 +129,9 @@ class FileList: return (action, patterns, dir, dir_pattern) # _parse_template_line () - - def process_template_line (self, line): + + def process_template_line (self, line): # Parse the line: split it up, make sure the right number of words # is there, and return the relevant words. 'action' is always @@ -190,7 +190,7 @@ class FileList: self.warn(("no previously-included files matching '%s' " + "found under directory '%s'") % (pattern, dir)) - + elif action == 'graft': self.debug_print("graft " + dir_pattern) if not self.include_pattern(None, prefix=dir_pattern): @@ -251,7 +251,7 @@ class FileList: self.debug_print(" adding " + name) self.files.append(name) files_found = 1 - + return files_found # include_pattern () @@ -261,7 +261,7 @@ class FileList: anchor=1, prefix=None, is_regex=0): """Remove strings (presumably filenames) from 'files' that match 'pattern'. Other parameters are the same as for - 'include_pattern()', above. + 'include_pattern()', above. The list 'self.files' is modified in place. Return 1 if files are found. """ @@ -274,7 +274,7 @@ class FileList: self.debug_print(" removing " + self.files[i]) del self.files[i] files_found = 1 - + return files_found # exclude_pattern () @@ -354,14 +354,14 @@ def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): pattern_re = glob_to_re(pattern) else: pattern_re = '' - + if prefix is not None: prefix_re = (glob_to_re(prefix))[0:-1] # ditch trailing $ pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) else: # no prefix -- respect anchor flag if anchor: pattern_re = "^" + pattern_re - + return re.compile(pattern_re) # translate_pattern () diff --git a/msvccompiler.py b/msvccompiler.py index 0325b485..8a67dfc8 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -50,8 +50,8 @@ if _can_read_reg: HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER HKEY_USERS = hkey_mod.HKEY_USERS - - + + def get_devstudio_versions (): """Get list of devstudio versions from the Windows registry. Return a @@ -93,7 +93,7 @@ def get_msvc_paths (path, version='6.0', platform='x86'): """Get a list of devstudio directories (include, lib or path). Return a list of strings; will be empty list if unable to access the registry or appropriate registry keys not found.""" - + if not _can_read_reg: return [] @@ -149,7 +149,7 @@ def find_exe (exe, version_number): if os.path.isfile(fn): return fn - return exe # last desperate hope + return exe # last desperate hope def set_path_env_var (name, version_number): @@ -294,7 +294,7 @@ class MSVCCompiler (CCompiler) : compile_opts.extend (self.compile_options_debug) else: compile_opts.extend (self.compile_options) - + for i in range (len (sources)): src = sources[i] ; obj = objects[i] ext = (os.path.splitext (src))[1] @@ -390,12 +390,12 @@ class MSVCCompiler (CCompiler) : self.spawn ([self.lib] + lib_args) except DistutilsExecError, msg: raise LibError, msg - + else: self.announce ("skipping %s (up-to-date)" % output_filename) # create_static_lib () - + def link (self, target_desc, objects, @@ -417,7 +417,7 @@ class MSVCCompiler (CCompiler) : if runtime_library_dirs: self.warn ("I don't know what to do with 'runtime_library_dirs': " + str (runtime_library_dirs)) - + lib_opts = gen_lib_options (self, library_dirs, runtime_library_dirs, libraries) @@ -441,7 +441,7 @@ class MSVCCompiler (CCompiler) : for sym in (export_symbols or []): export_opts.append("/EXPORT:" + sym) - ld_args = (ldflags + lib_opts + export_opts + + ld_args = (ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]) # The MSVC linker generates .lib and .exp files, which cannot be diff --git a/mwerkscompiler.py b/mwerkscompiler.py index e759456a..7c77b8bc 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -52,8 +52,8 @@ class MWerksCompiler (CCompiler) : force=0): CCompiler.__init__ (self, verbose, dry_run, force) - - + + def compile (self, sources, output_dir=None, @@ -62,14 +62,14 @@ class MWerksCompiler (CCompiler) : debug=0, extra_preargs=None, extra_postargs=None): - (output_dir, macros, include_dirs) = \ - self._fix_compile_args (output_dir, macros, include_dirs) - self.__sources = sources - self.__macros = macros - self.__include_dirs = include_dirs - # Don't need extra_preargs and extra_postargs for CW - return [] - + (output_dir, macros, include_dirs) = \ + self._fix_compile_args (output_dir, macros, include_dirs) + self.__sources = sources + self.__macros = macros + self.__include_dirs = include_dirs + # Don't need extra_preargs and extra_postargs for CW + return [] + def link (self, target_desc, objects, @@ -198,7 +198,7 @@ class MWerksCompiler (CCompiler) : if self.verbose: print '\tBuild project' mkcwproject.buildproject(projectfilename) - + def _filename_to_abs(self, filename): # Some filenames seem to be unix-like. Convert to Mac names. ## if '/' in filename and ':' in filename: @@ -207,13 +207,11 @@ class MWerksCompiler (CCompiler) : ## filename = macurl2path(filename) filename = distutils.util.convert_path(filename) if not os.path.isabs(filename): - curdir = os.getcwd() - filename = os.path.join(curdir, filename) + curdir = os.getcwd() + filename = os.path.join(curdir, filename) # Finally remove .. components components = string.split(filename, ':') for i in range(1, len(components)): - if components[i] == '..': - components[i] = '' + if components[i] == '..': + components[i] = '' return string.join(components, ':') - - diff --git a/spawn.py b/spawn.py index 1eed7a8a..07dc8148 100644 --- a/spawn.py +++ b/spawn.py @@ -71,7 +71,7 @@ def _spawn_nt (cmd, cmd = _nt_quote_args(cmd) if search_path: # either we find one or it stays the same - executable = find_executable(executable) or executable + executable = find_executable(executable) or executable if verbose: print string.join([executable] + cmd[1:], ' ') if not dry_run: @@ -87,7 +87,7 @@ def _spawn_nt (cmd, raise DistutilsExecError, \ "command '%s' failed with exit status %d" % (cmd[0], rc) - + def _spawn_posix (cmd, search_path=1, verbose=0, @@ -110,11 +110,11 @@ def _spawn_posix (cmd, sys.stderr.write("unable to execute %s: %s\n" % (cmd[0], e.strerror)) os._exit(1) - + sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0]) os._exit(1) - + else: # in the parent # Loop until the child either exits or is terminated by a signal # (ie. keep waiting if it's merely stopped) @@ -133,7 +133,7 @@ def _spawn_posix (cmd, raise DistutilsExecError, \ "command '%s' failed with exit status %d" % \ (cmd[0], exit_status) - + elif os.WIFSTOPPED(status): continue @@ -166,4 +166,4 @@ def find_executable(executable, path=None): else: return executable -# find_executable() +# find_executable() diff --git a/sysconfig.py b/sysconfig.py index 935372cd..feaf318c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -48,7 +48,7 @@ def get_python_inc(plat_specific=0, prefix=None): If 'prefix' is supplied, use it instead of sys.prefix or sys.exec_prefix -- i.e., ignore 'plat_specific'. - """ + """ if prefix is None: prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": @@ -318,7 +318,7 @@ def _init_posix(): # the scripts are in another directory. if python_build: g['LDSHARED'] = g['BLDSHARED'] - + elif sys.version < '2.1': # The following two branches are for 1.5.2 compatibility. if sys.platform == 'aix4': # what about AIX 3.x ? @@ -337,7 +337,7 @@ def _init_posix(): python_lib = get_python_lib(standard_lib=1) linkerscript_name = os.path.basename(string.split(g['LDSHARED'])[0]) linkerscript = os.path.join(python_lib, 'config', linkerscript_name) - + # XXX this isn't the right place to do this: adding the Python # library to the link, if needed, should be in the "build_ext" # command. (It's also needed for non-MS compilers on Windows, and @@ -345,7 +345,7 @@ def _init_posix(): # method.) g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % (linkerscript, PREFIX, sys.version[0:3])) - + global _config_vars _config_vars = g diff --git a/text_file.py b/text_file.py index 37bffe61..7086b1af 100644 --- a/text_file.py +++ b/text_file.py @@ -86,7 +86,7 @@ class TextFile: if filename is None and file is None: raise RuntimeError, \ - "you must supply either or both of 'filename' and 'file'" + "you must supply either or both of 'filename' and 'file'" # set values for all options -- either from client option hash # or fallback to default_options @@ -113,7 +113,7 @@ class TextFile: # actually read from the file; it's only populated by an # 'unreadline()' operation self.linebuf = [] - + def open (self, filename): """Open a new file named 'filename'. This overrides both the @@ -213,7 +213,7 @@ class TextFile: # EOF; I think that's OK.) eol = (line[-1] == '\n') and '\n' or '' line = line[0:pos] + eol - + # If all that's left is whitespace, then skip line # *now*, before we try to join it to 'buildup_line' -- # that way constructs like @@ -226,7 +226,7 @@ class TextFile: else: # it's an escaped "#" line = string.replace (line, "\\#", "#") - + # did previous line end with a backslash? then accumulate if self.join_lines and buildup_line: @@ -256,7 +256,7 @@ class TextFile: self.current_line = self.current_line[1] + 1 else: self.current_line = self.current_line + 1 - + # strip whitespace however the client wants (leading and # trailing, or one or the other, or neither) @@ -351,7 +351,7 @@ line 3 \\ print expected_result print "** received:" print result - + filename = "test.txt" out_file = open (filename, "w") @@ -382,4 +382,3 @@ line 3 \\ test_input (6, "join lines with collapsing", in_file, result6) os.remove (filename) - diff --git a/unixccompiler.py b/unixccompiler.py index a4f0ac4d..a9b5de51 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -103,8 +103,8 @@ class UnixCCompiler (CCompiler): pp_args.extend(extra_postargs) # We need to preprocess: either we're being forced to, or we're - # generating output to stdout, or there's a target output file and - # the source file is newer than the target (or the target doesn't + # generating output to stdout, or there's a target output file and + # the source file is newer than the target (or the target doesn't # exist). if self.force or output_file is None or newer(source, output_file): if output_file: @@ -139,7 +139,7 @@ class UnixCCompiler (CCompiler): extra_postargs = [] # Compile all source files that weren't eliminated by - # '_prep_compile()'. + # '_prep_compile()'. for i in range(len(sources)): src = sources[i] ; obj = objects[i] if skip_sources[src]: @@ -157,7 +157,7 @@ class UnixCCompiler (CCompiler): return objects # compile () - + def create_static_lib (self, objects, @@ -193,7 +193,7 @@ class UnixCCompiler (CCompiler): def link (self, - target_desc, + target_desc, objects, output_filename, output_dir=None, @@ -219,7 +219,7 @@ class UnixCCompiler (CCompiler): output_filename = os.path.join(output_dir, output_filename) if self._need_link(objects, output_filename): - ld_args = (objects + self.objects + + ld_args = (objects + self.objects + lib_opts + ['-o', output_filename]) if debug: ld_args[:0] = ['-g'] @@ -229,7 +229,7 @@ class UnixCCompiler (CCompiler): ld_args.extend(extra_postargs) self.mkpath(os.path.dirname(output_filename)) try: - if target_desc == CCompiler.EXECUTABLE: + if target_desc == CCompiler.EXECUTABLE: self.spawn(self.linker_exe + ld_args) else: self.spawn(self.linker_so + ld_args) @@ -244,7 +244,7 @@ class UnixCCompiler (CCompiler): # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in # ccompiler.py. - + def library_dir_option (self, dir): return "-L" + dir diff --git a/util.py b/util.py index 25ddbdf4..1541e02d 100644 --- a/util.py +++ b/util.py @@ -30,7 +30,7 @@ def get_platform (): solaris-2.6-sun4u irix-5.3 irix64-6.2 - + For non-POSIX platforms, currently just returns 'sys.platform'. """ if os.name != "posix" or not hasattr(os, 'uname'): @@ -44,9 +44,9 @@ def get_platform (): # Convert the OS name to lowercase and remove '/' characters # (to accommodate BSD/OS) - osname = string.lower(osname) + osname = string.lower(osname) osname = string.replace(osname, '/', '') - + if osname[:5] == "linux": # At least on Linux/Intel, 'machine' is the processor -- # i386, etc. @@ -59,7 +59,7 @@ def get_platform (): # fall through to standard osname-release-machine representation elif osname[:4] == "irix": # could be "irix64"! return "%s-%s" % (osname, release) - elif osname[:3] == "aix": + elif osname[:3] == "aix": return "%s-%s.%s" % (osname, version, release) elif osname[:6] == "cygwin": osname = "cygwin" @@ -67,7 +67,7 @@ def get_platform (): m = rel_re.match(release) if m: release = m.group() - + return "%s-%s-%s" % (osname, release, machine) # get_platform () @@ -280,7 +280,7 @@ def execute (func, args, msg=None, verbose=0, dry_run=0): # Generate a message if we weren't passed one if msg is None: msg = "%s%s" % (func.__name__, `args`) - if msg[-2:] == ',)': # correct for singleton tuple + if msg[-2:] == ',)': # correct for singleton tuple msg = msg[0:-2] + ')' # Print it if verbosity level is high enough @@ -403,7 +403,7 @@ byte_compile(files, optimize=%s, force=%s, spawn(cmd, verbose=verbose, dry_run=dry_run) execute(os.remove, (script_name,), "removing %s" % script_name, verbose=verbose, dry_run=dry_run) - + # "Direct" byte-compilation: use the py_compile module to compile # right here, right now. Note that the script generated in indirect # mode simply calls 'byte_compile()' in direct mode, a weird sort of @@ -453,5 +453,3 @@ def rfc822_escape (header): lines = map(string.strip, lines) header = string.join(lines, '\n' + 8*' ') return header - - diff --git a/version.py b/version.py index 9d3d1724..02502dac 100644 --- a/version.py +++ b/version.py @@ -98,7 +98,7 @@ class StrictVersion (Version): The rationale for this version numbering system will be explained in the distutils documentation. """ - + version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', re.VERBOSE) @@ -123,7 +123,7 @@ class StrictVersion (Version): def __str__ (self): - + if self.version[2] == 0: vstring = string.join(map(str, self.version[0:2]), '.') else: @@ -133,7 +133,7 @@ class StrictVersion (Version): vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) return vstring - + def __cmp__ (self, other): if isinstance(other, StringType): @@ -170,7 +170,7 @@ class StrictVersion (Version): # 2) sequences of letters are part of the tuple for comparison and are # compared lexicographically # 3) recognize the numeric components may have leading zeroes -# +# # The LooseVersion class below implements these rules: a version number # string is split up into a tuple of integer and string components, and # comparison is a simple tuple comparison. This means that version @@ -185,7 +185,7 @@ class StrictVersion (Version): # - indicating a post-release patch ('p', 'pl', 'patch') # but of course this can't cover all version number schemes, and there's # no way to know what a programmer means without asking him. -# +# # The problem is what to do with letters (and other non-numeric # characters) in a version number. The current implementation does the # obvious and predictable thing: keep them as strings and compare @@ -208,7 +208,7 @@ class StrictVersion (Version): # version numbering scheme to its domination. The free-thinking # anarchists in the lot will never give in, though, and something needs # to be done to accommodate them. -# +# # Perhaps a "moderately strict" version class could be implemented that # lets almost anything slide (syntactically), and makes some heuristic # assumptions about non-digits in version number strings. This could @@ -217,7 +217,7 @@ class StrictVersion (Version): # somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is # just as happy dealing with things like "2g6" and "1.13++". I don't # think I'm smart enough to do it right though. -# +# # In any case, I've coded the test suite for this module (see # ../test/test_version.py) specifically to fail on things like comparing # "1.2a2" and "1.2". That's not because the *code* is doing anything @@ -296,6 +296,6 @@ class LooseVersion (Version): other = LooseVersion(other) return cmp(self.version, other.version) - + # end class LooseVersion -- cgit v1.2.1 From 4263b3189f9532a313b1bb52e478168f8a545729 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 6 Dec 2001 21:01:19 +0000 Subject: Whitespace normalization. --- command/bdist.py | 4 ++-- command/bdist_rpm.py | 22 +++++++++++----------- command/bdist_wininst.py | 2 +- command/build_clib.py | 4 ++-- command/build_ext.py | 20 ++++++++++---------- command/build_py.py | 6 +++--- command/config.py | 4 ++-- command/install.py | 10 +++++----- command/install_headers.py | 2 +- command/install_lib.py | 12 ++++++------ command/sdist.py | 10 +++++----- 11 files changed, 48 insertions(+), 48 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index a75303e6..2b1951fd 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -17,7 +17,7 @@ from distutils.util import get_platform def show_formats (): """Print list of available formats (arguments to "--format" option). """ - from distutils.fancy_getopt import FancyGetopt + from distutils.fancy_getopt import FancyGetopt formats=[] for format in bdist.format_commands: formats.append(("formats=" + format, None, @@ -104,7 +104,7 @@ class bdist (Command): if self.dist_dir is None: self.dist_dir = "dist" - + # finalize_options() diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 150fdeca..037ed9e8 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -101,7 +101,7 @@ class bdist_rpm (Command): 'no-rpm-opt-flags': 'use-rpm-opt-flags', 'rpm2-mode': 'rpm3-mode'} - + def initialize_options (self): self.bdist_base = None self.rpm_base = None @@ -184,7 +184,7 @@ class bdist_rpm (Command): self.ensure_string('vendor', "%s <%s>" % (self.distribution.get_contact(), self.distribution.get_contact_email())) - self.ensure_string('packager') + self.ensure_string('packager') self.ensure_string_list('doc_files') if type(self.doc_files) is ListType: for readme in ('README', 'README.txt'): @@ -201,7 +201,7 @@ class bdist_rpm (Command): self.changelog = self._format_changelog(self.changelog) self.ensure_filename('icon') - + self.ensure_filename('prep_script') self.ensure_filename('build_script') self.ensure_filename('install_script') @@ -275,7 +275,7 @@ class bdist_rpm (Command): else: raise DistutilsFileError, \ "icon file '%s' does not exist" % self.icon - + # build package self.announce('building RPMs') @@ -368,17 +368,17 @@ class bdist_rpm (Command): spec_file.append('%s: %s' % (field, string.join(val))) elif val is not None: spec_file.append('%s: %s' % (field, val)) - - + + if self.distribution.get_url() != 'UNKNOWN': spec_file.append('Url: ' + self.distribution.get_url()) if self.distribution_name: - spec_file.append('Distribution: ' + self.distribution_name) + spec_file.append('Distribution: ' + self.distribution_name) if self.build_requires: - spec_file.append('BuildRequires: ' + - string.join(self.build_requires)) + spec_file.append('BuildRequires: ' + + string.join(self.build_requires)) if self.icon: spec_file.append('Icon: ' + os.path.basename(self.icon)) @@ -473,11 +473,11 @@ class bdist_rpm (Command): new_changelog.append(line) else: new_changelog.append(' ' + line) - + # strip trailing newline inserted by first changelog entry if not new_changelog[0]: del new_changelog[0] - + return new_changelog # _format_changelog() diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 7cdf3855..357ba519 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -196,7 +196,7 @@ class bdist_wininst (Command): file.write(self.get_exe_bytes()) if bitmap: file.write(bitmapdata) - + file.write(cfgdata) header = struct.pack(" Date: Thu, 6 Dec 2001 21:29:28 +0000 Subject: [Bug #475009] Tighten the pattern for the first line, so we don't adjust it when a versioned interpreter is supplied (#!.../python2 ...) --- command/build_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 16024e5f..31750b7c 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -12,7 +12,7 @@ from distutils.dep_util import newer from distutils.util import convert_path # check if Python is called on the first line with this expression -first_line_re = re.compile(r'^#!.*python(\s+.*)?') +first_line_re = re.compile(r'^#!.*python(\s+.*)?$') class build_scripts (Command): -- cgit v1.2.1 From bf0c7b1e25b1e08c625bb9f5cfd1e6db68360aa6 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 6 Dec 2001 22:59:54 +0000 Subject: Visious hackery to solve a build-control problem related to our use of distutils for the library modules built as shared objects. A better solution appears possible, but with the threat that the distutils becomes more magical ("complex"). This closes SF bug #458343. --- command/build_ext.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index c9e30623..7a39314b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -456,6 +456,17 @@ class build_ext (Command): debug=self.debug, extra_postargs=extra_args) + # XXX -- this is a Vile HACK! + # + # The setup.py script for Python on Unix needs to be able to + # get this list so it can perform all the clean up needed to + # avoid keeping object files around when cleaning out a failed + # build of an extension module. Since Distutils does not + # track dependencies, we have to get rid of intermediates to + # ensure all the intermediates will be properly re-built. + # + self._built_objects = objects[:] + # Now link the object files together into a "shared object" -- # of course, first we have to figure out all the other things # that go into the mix. -- cgit v1.2.1 From 730b95b729f9b7acfb9d84225ce74b7f20b8f263 Mon Sep 17 00:00:00 2001 From: "Michael W. Hudson" Date: Mon, 10 Dec 2001 15:28:30 +0000 Subject: Fix for [ #477371 ] build_scripts can use wrong #! line scripts now get "built" into a directory build/scripts-$(PYTHON_VERSION)/ --- command/build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build.py b/command/build.py index cf35b45d..6bc92a43 100644 --- a/command/build.py +++ b/command/build.py @@ -90,7 +90,8 @@ class build (Command): self.build_temp = os.path.join(self.build_base, 'temp' + plat_specifier) if self.build_scripts is None: - self.build_scripts = os.path.join(self.build_base, 'scripts') + self.build_scripts = os.path.join(self.build_base, + 'scripts-' + sys.version[0:3]) # finalize_options () -- cgit v1.2.1 From 158c496ca9d641e38b8469143db7cf49ff35b8a8 Mon Sep 17 00:00:00 2001 From: "Michael W. Hudson" Date: Mon, 10 Dec 2001 16:15:44 +0000 Subject: Fix for [ #409430 ] pydoc install broken --- command/build_scripts.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 31750b7c..7286bf1f 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -7,6 +7,7 @@ Implements the Distutils 'build_scripts' command.""" __revision__ = "$Id$" import sys, os, re +from distutils import sysconfig from distutils.core import Command from distutils.dep_util import newer from distutils.util import convert_path @@ -86,8 +87,16 @@ class build_scripts (Command): (script, self.build_dir)) if not self.dry_run: outf = open(outfile, "w") - outf.write("#!%s%s\n" % - (os.path.normpath(sys.executable), post_interp)) + if not sysconfig.python_build: + outf.write("#!%s%s\n" % + (os.path.normpath(sys.executable), + post_interp)) + else: + outf.write("#!%s%s" % + (os.path.join( + sysconfig.get_config_var("BINDIR"), + "python" + sysconfig.get_config_var("EXE")), + post_interp)) outf.writelines(f.readlines()) outf.close() if f: -- cgit v1.2.1 From 54b68c95834551d0995f388151974be7b967a471 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 11 Dec 2001 05:04:24 +0000 Subject: When using GCC, use the right option to add a directory to the list of dirs searched for a dependency for runtime linking. This closes SF bug #445902. --- unixccompiler.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index a9b5de51..356587d6 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -20,6 +20,7 @@ __revision__ = "$Id$" import string, re, os from types import * from copy import copy +from distutils import sysconfig from distutils.dep_util import newer from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options @@ -249,7 +250,23 @@ class UnixCCompiler (CCompiler): return "-L" + dir def runtime_library_dir_option (self, dir): - return "-R" + dir + # XXX Hackish, at the very least. See Python bug #445902: + # http://sourceforge.net/tracker/index.php + # ?func=detail&aid=445902&group_id=5470&atid=105470 + # Linkers on different platforms need different options to + # specify that directories need to be added to the list of + # directories searched for dependencies when a dynamic library + # is sought. GCC has to be told to pass the -R option through + # to the linker, whereas other compilers just know this. + # Other compilers may need something slightly different. At + # this time, there's no way to determine this information from + # the configuration data stored in the Python installation, so + # we use this hack. + compiler = os.path.basename(sysconfig.get_config_var("CC")) + if compiler == "gcc" or compiler == "g++": + return "-Wl,-R" + dir + else: + return "-R" + dir def library_option (self, lib): return "-l" + lib -- cgit v1.2.1 From 30bfa264aae2dfa5a6a6db2a3058467c79c7f437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Tue, 11 Dec 2001 20:44:42 +0000 Subject: Joe VanAndel wrote: > > When using 'distutils' (shipped with Python 2.1) I've found that my > Python scripts installed with a first line of: > > #!/usr/bin/python2.1None > > This is caused by distutils trying to patch the first line of the python > script to use the current interpreter. --- command/build_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 7286bf1f..bfa33c3a 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -80,7 +80,7 @@ class build_scripts (Command): match = first_line_re.match(first_line) if match: adjust = 1 - post_interp = match.group(1) + post_interp = match.group(1) or '' if adjust: self.announce("copying and adjusting %s -> %s" % -- cgit v1.2.1 From 5ccf2307126f5e3f989925d2653239860c9b53db Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 18 Dec 2001 20:13:40 +0000 Subject: Second part of fix for bug [#483982] Python 2.2b2 bdist_wininst crashes. If no external zip-utility is found, the archive is created by the zipfile module, which behaves different now than in 2.1: if the zip-file is created in the root directory if the distribution, it will contain an (empty) version of itself. This triggered the above bug - so it's better to create the zip-file far away in the TMP directory. --- command/bdist_wininst.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 357ba519..9ff94613 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -112,13 +112,16 @@ class bdist_wininst (Command): # And make an archive relative to the root of the # pseudo-installation tree. + from tempfile import mktemp + archive_basename = mktemp() fullname = self.distribution.get_fullname() - archive_basename = os.path.join(self.bdist_dir, - "%s.win32" % fullname) - arcname = self.make_archive(archive_basename, "zip", root_dir=self.bdist_dir) + # create an exe containing the zip-file self.create_exe(arcname, fullname, self.bitmap) + # remove the zip-file again + self.announce("removing temporary file '%s'" % arcname) + os.remove(arcname) if not self.keep_temp: remove_tree(self.bdist_dir, self.verbose, self.dry_run) -- cgit v1.2.1 From 74bfeea5bba971e7e23323973affee72cfd37c60 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 18 Dec 2001 21:08:15 +0000 Subject: Recreated after source changes. --- command/bdist_wininst.py | 568 +++++++++++++++++++++++------------------------ 1 file changed, 284 insertions(+), 284 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 9ff94613..7c34cffd 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -234,7 +234,7 @@ TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v ZGUuDQ0KJAAAAAAAAAA/SHa+eykY7XspGO17KRjtADUU7XkpGO0UNhLtcCkY7fg1Fu15KRjtFDYc 7XkpGO0ZNgvtcykY7XspGe0GKRjteykY7XYpGO19ChLteSkY7bwvHu16KRjtUmljaHspGO0AAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwBWGr47AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAAKAAAODuAAAA +AAAAAAAAAAAAAAAAUEUAAEwBAwCUrh88AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAAKAAANDuAAAA sAAAAAABAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAAEAEAAAQAAAAAAAACAAAAAAAQAAAQ AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwAQEAbAEAAAAAAQAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -247,7 +247,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAkSW5mbzogVGhpcyBmaWxlIGlz IHBhY2tlZCB3aXRoIHRoZSBVUFggZXhlY3V0YWJsZSBwYWNrZXIgaHR0cDovL3VweC50c3gub3Jn ICQKACRJZDogVVBYIDEuMDEgQ29weXJpZ2h0IChDKSAxOTk2LTIwMDAgdGhlIFVQWCBUZWFtLiBB -bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCq3wArkET+QFVsgAAN0+AAAAsAAAJgEA4//b +bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCjD69l3lQx/kVsgAAME+AAAAsAAAJgEA4P/b //9TVVaLdCQUhfZXdH2LbCQci3wMgD4AdHBqXFb/5vZv/xU0YUAAi/BZHVl0X4AmAFcRvGD9v/n+ 2IP7/3Unag+4hcB1E4XtdA9XaBBw/d/+vw1qBf/Vg8QM6wdXagEJWVn2wxB1HGi3ABOyna0ALbQp Dcb3/3/7BlxGdYssWF9eXVvDVYvsg+wMU1ZXiz3ALe/uf3cz9rs5wDl1CHUHx0UIAQxWaIBMsf9v @@ -255,288 +255,288 @@ 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 -- cgit v1.2.1 From b8b4137be241dd8aeb636a704150fea716b63a30 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 21 Dec 2001 15:34:17 +0000 Subject: Suggested by Pete Shinners: treat .m and .mm files as source code. Question for Jack Jansen: is this reasonable? Candidate for 2.2 release branch (if Jack thinks it's OK). --- extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.py b/extension.py index fbae7c52..a31ccbce 100644 --- a/extension.py +++ b/extension.py @@ -160,7 +160,7 @@ def read_setup_file (filename): suffix = os.path.splitext(word)[1] switch = word[0:2] ; value = word[2:] - if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++"): + if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): # hmm, should we do something about C vs. C++ sources? # or leave it up to the CCompiler implementation to # worry about? -- cgit v1.2.1 From 533106ffec72f15ce012bff3bae5f24df618c8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 12 Jan 2002 11:27:42 +0000 Subject: Patch #414775: Add --skip-build option to bdist command. --- command/bdist.py | 5 +++++ command/bdist_dumb.py | 9 +++++++-- command/bdist_wininst.py | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 2b1951fd..fc18bd0c 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -40,8 +40,12 @@ class bdist (Command): ('dist-dir=', 'd', "directory to put final built distributions in " "[default: dist]"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), ] + boolean_options = ['skip-build'] + help_options = [ ('help-formats', None, "lists available distribution formats", show_formats), @@ -76,6 +80,7 @@ class bdist (Command): self.plat_name = None self.formats = None self.dist_dir = None + self.skip_build = 0 # initialize_options() diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 8dfc3271..dbe862bb 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -30,9 +30,11 @@ class bdist_dumb (Command): "creating the distribution archive"), ('dist-dir=', 'd', "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), ] - boolean_options = ['keep-temp'] + boolean_options = ['keep-temp', 'skip-build'] default_format = { 'posix': 'gztar', 'nt': 'zip', } @@ -44,6 +46,7 @@ class bdist_dumb (Command): self.format = None self.keep_temp = 0 self.dist_dir = None + self.skip_build = 0 # initialize_options() @@ -71,10 +74,12 @@ class bdist_dumb (Command): def run (self): - self.run_command('build') + if not self.skip_build: + self.run_command('build') install = self.reinitialize_command('install', reinit_subcommands=1) install.root = self.bdist_dir + install.skip_build = self.skip_build self.announce("installing to %s" % self.bdist_dir) self.run_command('install') diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 7c34cffd..4a16eec4 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -36,6 +36,8 @@ class bdist_wininst (Command): "bitmap to use for the installer instead of python-powered logo"), ('title=', 't', "title to display on the installer background instead of default"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), ] boolean_options = ['keep-temp'] @@ -49,6 +51,7 @@ class bdist_wininst (Command): self.dist_dir = None self.bitmap = None self.title = None + self.skip_build = 0 # initialize_options() @@ -79,10 +82,12 @@ class bdist_wininst (Command): ("distribution contains extensions and/or C libraries; " "must be compiled on a Windows 32 platform") - self.run_command('build') + if not self.skip_build: + self.run_command('build') install = self.reinitialize_command('install') install.root = self.bdist_dir + install.skip_build = self.skip_build install_lib = self.reinitialize_command('install_lib') # we do not want to include pyc or pyo files -- cgit v1.2.1 From 877f0c18a6a831ef6f840a30e04d9ad8414d9aef Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 18 Jan 2002 20:30:53 +0000 Subject: SWIGing a source file .i silently overwrites .c if it is present - at least the swigged file should be named _wrap.c as this is also SWIG's default. (Even better would be to generate the wrapped sources in a different location, but I'll leave this for later). Newer versions of SWIG don't accept the -dnone flag any more. Since virtually nobody uses SWIG with distutils, this should do no harm. Suggested be Martin Bless on c.l.p. --- command/build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 7a39314b..98617f7f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -511,7 +511,7 @@ class build_ext (Command): for source in sources: (base, ext) = os.path.splitext(source) if ext == ".i": # SWIG interface file - new_sources.append(base + target_ext) + new_sources.append(base + '_wrap' + target_ext) swig_sources.append(source) swig_targets[source] = new_sources[-1] else: @@ -521,7 +521,7 @@ class build_ext (Command): return new_sources swig = self.find_swig() - swig_cmd = [swig, "-python", "-dnone", "-ISWIG"] + swig_cmd = [swig, "-python"] if self.swig_cpp: swig_cmd.append("-c++") -- cgit v1.2.1 From 9b2752c213bc110bd6b5a123c3aa67314d772711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 29 Jan 2002 10:23:42 +0000 Subject: Encode MSVC paths as mbcs. Fixes #509117. 2.2.1 candidate. --- msvccompiler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/msvccompiler.py b/msvccompiler.py index 8a67dfc8..35336642 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -117,6 +117,10 @@ def get_msvc_paths (path, version='6.0', platform='x86'): if string.upper(p) == path: V = string.split(v,';') for v in V: + try: + v = v.encode("mbcs") + except UnicodeError: + pass if v == '' or v in L: continue L.append(v) break -- cgit v1.2.1 From d6951f1f6bab52d73341651f62cda2dd3f85433c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Thu, 31 Jan 2002 18:56:00 +0000 Subject: OS/2 patches by Andrew I MacIntyre for distutils. Closes patch #435381. --- ccompiler.py | 3 +++ command/bdist.py | 3 ++- command/bdist_dumb.py | 9 ++++++++- command/build_ext.py | 27 ++++++++++++++++++++++++++- command/install.py | 7 +++++++ spawn.py | 31 ++++++++++++++++++++++++++++++- sysconfig.py | 26 ++++++++++++++++++++++++++ util.py | 6 ++++++ 8 files changed, 108 insertions(+), 4 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 21bf0c17..f4fc4bcb 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -846,6 +846,7 @@ _default_compilers = ( # on a cygwin built python we can use gcc like an ordinary UNIXish # compiler ('cygwin.*', 'unix'), + ('os2emx', 'emx'), # OS name mappings ('posix', 'unix'), @@ -892,6 +893,8 @@ compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', "Borland C++ Compiler"), 'mwerks': ('mwerkscompiler', 'MWerksCompiler', "MetroWerks CodeWarrior"), + 'emx': ('emxccompiler', 'EMXCCompiler', + "EMX port of GNU C Compiler for OS/2"), } def show_compilers(): diff --git a/command/bdist.py b/command/bdist.py index fc18bd0c..68609f34 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -57,7 +57,8 @@ class bdist (Command): # This won't do in reality: will need to distinguish RPM-ish Linux, # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. default_format = { 'posix': 'gztar', - 'nt': 'zip', } + 'nt': 'zip', + 'os2': 'zip', } # Establish the preferred order (for the --help-formats option). format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index dbe862bb..b627a86a 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -37,7 +37,8 @@ class bdist_dumb (Command): boolean_options = ['keep-temp', 'skip-build'] default_format = { 'posix': 'gztar', - 'nt': 'zip', } + 'nt': 'zip', + 'os2': 'zip' } def initialize_options (self): @@ -88,6 +89,12 @@ class bdist_dumb (Command): # pseudo-installation tree. archive_basename = "%s.%s" % (self.distribution.get_fullname(), self.plat_name) + + # OS/2 objects to any ":" characters in a filename (such as when + # a timestamp is used in a version) so change them to hyphens. + if os.name == "os2": + archive_basename = archive_basename.replace(":", "-") + self.make_archive(os.path.join(self.dist_dir, archive_basename), self.format, root_dir=self.bdist_dir) diff --git a/command/build_ext.py b/command/build_ext.py index 98617f7f..91fee5ee 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -167,6 +167,11 @@ class build_ext (Command): else: self.build_temp = os.path.join(self.build_temp, "Release") + # OS/2 (EMX) doesn't support Debug vs Release builds, but has the + # import libraries in its "Config" subdirectory + if os.name == 'os2': + self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) + # for extensions under Cygwin Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin': @@ -554,6 +559,10 @@ class build_ext (Command): else: return "swig.exe" + elif os.name == "os2": + # assume swig available in the PATH. + return "swig.exe" + else: raise DistutilsPlatformError, \ ("I don't know how to find (much less run) SWIG " @@ -578,6 +587,9 @@ class build_ext (Command): from distutils.sysconfig import get_config_var ext_path = string.split(ext_name, '.') + # OS/2 has an 8 character module (extension) limit :-( + if os.name == "os2": + ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] # extensions in debug_mode are named 'module_d.pyd' under windows so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: @@ -599,7 +611,7 @@ class build_ext (Command): def get_libraries (self, ext): """Return the list of libraries to link against when building a shared extension. On most platforms, this is just 'ext.libraries'; - on Windows, we add the Python library (eg. python20.dll). + on Windows and OS/2, we add the Python library (eg. python20.dll). """ # The python library is always needed on Windows. For MSVC, this # is redundant, since the library is mentioned in a pragma in @@ -617,6 +629,19 @@ class build_ext (Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] + elif sys.platform == "os2emx": + # EMX/GCC requires the python library explicitly, and I + # believe VACPP does as well (though not confirmed) - AIM Apr01 + template = "python%d%d" + # debug versions of the main DLL aren't supported, at least + # not at this time - AIM Apr01 + #if self.debug: + # template = template + '_d' + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] elif sys.platform[:6] == "cygwin": template = "python%d.%d" pythonlib = (template % diff --git a/command/install.py b/command/install.py index 8755a142..4d78d3aa 100644 --- a/command/install.py +++ b/command/install.py @@ -50,6 +50,13 @@ INSTALL_SCHEMES = { }, 'nt': WINDOWS_SCHEME, 'mac': { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + }, + 'os2': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', 'headers': '$base/Include/$dist_name', diff --git a/spawn.py b/spawn.py index 07dc8148..5b6016e0 100644 --- a/spawn.py +++ b/spawn.py @@ -38,6 +38,8 @@ def spawn (cmd, _spawn_posix(cmd, search_path, verbose, dry_run) elif os.name == 'nt': _spawn_nt(cmd, search_path, verbose, dry_run) + elif os.name == 'os2': + _spawn_os2(cmd, search_path, verbose, dry_run) else: raise DistutilsPlatformError, \ "don't know how to spawn programs on platform '%s'" % os.name @@ -88,6 +90,33 @@ def _spawn_nt (cmd, "command '%s' failed with exit status %d" % (cmd[0], rc) +def _spawn_os2 (cmd, + search_path=1, + verbose=0, + dry_run=0): + + executable = cmd[0] + #cmd = _nt_quote_args(cmd) + if search_path: + # either we find one or it stays the same + executable = find_executable(executable) or executable + if verbose: + print string.join([executable] + cmd[1:], ' ') + if not dry_run: + # spawnv for OS/2 EMX requires a full path to the .exe + try: + rc = os.spawnv(os.P_WAIT, executable, cmd) + except OSError, exc: + # this seems to happen when the command isn't found + raise DistutilsExecError, \ + "command '%s' failed: %s" % (cmd[0], exc[-1]) + if rc != 0: + # and this reflects the command running but failing + print "command '%s' failed with exit status %d" % (cmd[0], rc) + raise DistutilsExecError, \ + "command '%s' failed with exit status %d" % (cmd[0], rc) + + def _spawn_posix (cmd, search_path=1, verbose=0, @@ -154,7 +183,7 @@ def find_executable(executable, path=None): path = os.environ['PATH'] paths = string.split(path, os.pathsep) (base, ext) = os.path.splitext(executable) - if (sys.platform == 'win32') and (ext != '.exe'): + if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): executable = executable + '.exe' if not os.path.isfile(executable): for p in paths: diff --git a/sysconfig.py b/sysconfig.py index feaf318c..d773f149 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -59,6 +59,8 @@ def get_python_inc(plat_specific=0, prefix=None): return os.path.join(prefix, "include") elif os.name == "mac": return os.path.join(prefix, "Include") + elif os.name == "os2": + return os.path.join(prefix, "Include") else: raise DistutilsPlatformError( "I don't know where Python installs its C header files " @@ -110,6 +112,13 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): return os.path.join(prefix, "Lib") else: return os.path.join(prefix, "Lib", "site-packages") + + elif os.name == "os2": + if standard_lib: + return os.path.join(PREFIX, "Lib") + else: + return os.path.join(PREFIX, "Lib", "site-packages") + else: raise DistutilsPlatformError( "I don't know where Python installs its library " @@ -391,6 +400,23 @@ def _init_mac(): _config_vars = g +def _init_os2(): + """Initialize the module as appropriate for OS/2""" + g = {} + # set basic install directories + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) + + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = get_python_inc(plat_specific=0) + + g['SO'] = '.pyd' + g['EXE'] = ".exe" + + global _config_vars + _config_vars = g + + def get_config_vars(*args): """With no arguments, return a dictionary of all configuration variables relevant for the current platform. Generally this includes diff --git a/util.py b/util.py index 1541e02d..a51ce660 100644 --- a/util.py +++ b/util.py @@ -117,6 +117,12 @@ def change_root (new_root, pathname): path = path[1:] return os.path.join(new_root, path) + elif os.name == 'os2': + (drive, path) = os.path.splitdrive(pathname) + if path[0] == os.sep: + path = path[1:] + return os.path.join(new_root, path) + elif os.name == 'mac': if not os.path.isabs(pathname): return os.path.join(new_root, pathname) -- cgit v1.2.1 From f666dda63137a66f6d619529d79c9238dfbae94d Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 31 Jan 2002 22:08:38 +0000 Subject: Restrict the mode to the lowest four octal positions; higher positions contain the type of the file (regular file, socket, link, &c.). This means that install_scripts will now print "changing mode of to 775" instead of "... to 100775". 2.2 bugfix candidate, I suppose, though this isn't actually fixing a bug. --- command/install_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_scripts.py b/command/install_scripts.py index 3bc23e74..d4cbaa3a 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -50,7 +50,7 @@ class install_scripts (Command): if self.dry_run: self.announce("changing mode of %s" % file) else: - mode = (os.stat(file)[ST_MODE]) | 0111 + mode = ((os.stat(file)[ST_MODE]) | 0111) & 07777 self.announce("changing mode of %s to %o" % (file, mode)) os.chmod(file, mode) -- cgit v1.2.1 From 4431a9867079962ba92f8671378c33555fe45a10 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 1 Feb 2002 09:44:09 +0000 Subject: package_dir must be converted from the distutils path conventions to local conventions before being used by build_py. Fixes SF bug #509288, probably a candidate for 2.2.1 --- command/build_py.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/command/build_py.py b/command/build_py.py index 527e81d9..97d094b1 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -12,6 +12,7 @@ from glob import glob from distutils.core import Command from distutils.errors import * +from distutils.util import convert_path class build_py (Command): @@ -50,7 +51,10 @@ class build_py (Command): # options -- list of packages and list of modules. self.packages = self.distribution.packages self.py_modules = self.distribution.py_modules - self.package_dir = self.distribution.package_dir + self.package_dir = {} + if self.distribution.package_dir: + for name, path in self.distribution.package_dir.items(): + self.package_dir[name] = convert_path(path) # Ick, copied straight from install_lib.py (fancy_getopt needs a # type system! Hell, *everything* needs a type system!!!) -- cgit v1.2.1 From 0592edfb320ccaba1ac5478560dab5f5dbdccdd9 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 1 Feb 2002 18:29:34 +0000 Subject: [Bug #220993; may also fix bug #479469] Fix flakiness when old installations are present, by always unlinking the destination file before copying to it. Without the unlink(), the copied file remains owned by its previous UID, causing the subsequent chmod() to fail. Bugfix candidate, though it may cause changes on platforms where file ownership behaves differently. --- file_util.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/file_util.py b/file_util.py index 526e4cf5..14772fb7 100644 --- a/file_util.py +++ b/file_util.py @@ -36,6 +36,13 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): raise DistutilsFileError, \ "could not open '%s': %s" % (src, errstr) + if os.path.exists(dst): + try: + os.unlink(dst) + except os.error, (errno, errstr): + raise DistutilsFileError, \ + "could not delete '%s': %s" % (dst, errstr) + try: fdst = open(dst, 'wb') except os.error, (errno, errstr): -- cgit v1.2.1 From 896685f12e8f24d25586928c66d926c7c095b8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Wed, 6 Feb 2002 18:22:48 +0000 Subject: Forgot to add the new emxccompiler.py from Andrew I. MacIntyre's distutils patch for OS/2. Here it is... --- emxccompiler.py | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 emxccompiler.py diff --git a/emxccompiler.py b/emxccompiler.py new file mode 100644 index 00000000..58a0d812 --- /dev/null +++ b/emxccompiler.py @@ -0,0 +1,334 @@ +"""distutils.emxccompiler + +Provides the EMXCCompiler class, a subclass of UnixCCompiler that +handles the EMX port of the GNU C compiler to OS/2. +""" + +# issues: +# +# * OS/2 insists that DLLs can have names no longer than 8 characters +# We put export_symbols in a def-file, as though the DLL can have +# an arbitrary length name, but truncate the output filename. +# +# * only use OMF objects and use LINK386 as the linker (-Zomf) +# +# * always build for multithreading (-Zmt) as the accompanying OS/2 port +# of Python is only distributed with threads enabled. +# +# tested configurations: +# +# * EMX gcc 2.81/EMX 0.9d fix03 + +# created 2001/5/7, Andrew MacIntyre, from Rene Liebscher's cywinccompiler.py + +__revision__ = "$Id$" + +import os,sys,copy +from distutils.ccompiler import gen_preprocess_options, gen_lib_options +from distutils.unixccompiler import UnixCCompiler +from distutils.file_util import write_file +from distutils.errors import DistutilsExecError, CompileError, UnknownFileError + +class EMXCCompiler (UnixCCompiler): + + compiler_type = 'emx' + obj_extension = ".obj" + static_lib_extension = ".lib" + shared_lib_extension = ".dll" + static_lib_format = "%s%s" + shared_lib_format = "%s%s" + res_extension = ".res" # compiled resource file + exe_extension = ".exe" + + def __init__ (self, + verbose=0, + dry_run=0, + force=0): + + UnixCCompiler.__init__ (self, verbose, dry_run, force) + + (status, details) = check_config_h() + self.debug_print("Python's GCC status: %s (details: %s)" % + (status, details)) + if status is not CONFIG_H_OK: + self.warn( + "Python's pyconfig.h doesn't seem to support your compiler. " + + ("Reason: %s." % details) + + "Compiling may fail because of undefined preprocessor macros.") + + (self.gcc_version, self.ld_version) = \ + get_versions() + self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" % + (self.gcc_version, + self.ld_version) ) + + # Hard-code GCC because that's what this is all about. + # XXX optimization, warnings etc. should be customizable. + self.set_executables(compiler='gcc -Zomf -Zmt -O2 -Wall', + compiler_so='gcc -Zomf -Zmt -O2 -Wall', + linker_exe='gcc -Zomf -Zmt -Zcrtdll', + linker_so='gcc -Zomf -Zmt -Zcrtdll -Zdll') + + # want the gcc library statically linked (so that we don't have + # to distribute a version dependent on the compiler we have) + self.dll_libraries=["gcc"] + + # __init__ () + + # not much different of the compile method in UnixCCompiler, + # but we have to insert some lines in the middle of it, so + # we put here a adapted version of it. + # (If we would call compile() in the base class, it would do some + # initializations a second time, this is why all is done here.) + def compile (self, + sources, + output_dir=None, + macros=None, + include_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None): + + (output_dir, macros, include_dirs) = \ + self._fix_compile_args (output_dir, macros, include_dirs) + (objects, skip_sources) = self._prep_compile (sources, output_dir) + + # Figure out the options for the compiler command line. + pp_opts = gen_preprocess_options (macros, include_dirs) + cc_args = pp_opts + ['-c'] + if debug: + cc_args[:0] = ['-g'] + if extra_preargs: + cc_args[:0] = extra_preargs + if extra_postargs is None: + extra_postargs = [] + + # Compile all source files that weren't eliminated by + # '_prep_compile()'. + for i in range (len (sources)): + src = sources[i] ; obj = objects[i] + ext = (os.path.splitext (src))[1] + if skip_sources[src]: + self.announce ("skipping %s (%s up-to-date)" % (src, obj)) + else: + self.mkpath (os.path.dirname (obj)) + if ext == '.rc': + # gcc requires '.rc' compiled to binary ('.res') files !!! + try: + self.spawn (["rc","-r",src]) + except DistutilsExecError, msg: + raise CompileError, msg + else: # for other files use the C-compiler + try: + self.spawn (self.compiler_so + cc_args + + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg + + # Return *all* object filenames, not just the ones we just built. + return objects + + # compile () + + + def link (self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): + + # use separate copies, so we can modify the lists + extra_preargs = copy.copy(extra_preargs or []) + libraries = copy.copy(libraries or []) + objects = copy.copy(objects or []) + + # Additional libraries + libraries.extend(self.dll_libraries) + + # handle export symbols by creating a def-file + # with executables this only works with gcc/ld as linker + if ((export_symbols is not None) and + (target_desc != self.EXECUTABLE)): + # (The linker doesn't do anything if output is up-to-date. + # So it would probably better to check if we really need this, + # but for this we had to insert some unchanged parts of + # UnixCCompiler, and this is not what we want.) + + # we want to put some files in the same directory as the + # object files are, build_temp doesn't help much + # where are the object files + temp_dir = os.path.dirname(objects[0]) + # name of dll to give the helper files the same base name + (dll_name, dll_extension) = os.path.splitext( + os.path.basename(output_filename)) + + # generate the filenames for these files + def_file = os.path.join(temp_dir, dll_name + ".def") + lib_file = os.path.join(temp_dir, dll_name + ".lib") + + # Generate .def file + contents = [ + "LIBRARY %s INITINSTANCE TERMINSTANCE" % os.path.splitext(os.path.basename(output_filename))[0], + "DATA MULTIPLE NONSHARED", + "EXPORTS"] + for sym in export_symbols: + contents.append(' "%s"' % sym) + self.execute(write_file, (def_file, contents), + "writing %s" % def_file) + + # next add options for def-file and to creating import libraries + # for gcc/ld the def-file is specified as any other object files + objects.append(def_file) + + #end: if ((export_symbols is not None) and + # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): + + # who wants symbols and a many times larger output file + # should explicitly switch the debug mode on + # otherwise we let dllwrap/ld strip the output file + # (On my machine: 10KB < stripped_file < ??100KB + # unstripped_file = stripped_file + XXX KB + # ( XXX=254 for a typical python extension)) + if not debug: + extra_preargs.append("-s") + + UnixCCompiler.link(self, + target_desc, + objects, + output_filename, + output_dir, + libraries, + library_dirs, + runtime_library_dirs, + None, # export_symbols, we do this in our def-file + debug, + extra_preargs, + extra_postargs, + build_temp) + + # link () + + # -- Miscellaneous methods ----------------------------------------- + + # overwrite the one from CCompiler to support rc and res-files + def object_filenames (self, + source_filenames, + strip_dir=0, + output_dir=''): + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + # use normcase to make sure '.rc' is really '.rc' and not '.RC' + (base, ext) = os.path.splitext (os.path.normcase(src_name)) + if ext not in (self.src_extensions + ['.rc']): + raise UnknownFileError, \ + "unknown file type '%s' (from '%s')" % \ + (ext, src_name) + if strip_dir: + base = os.path.basename (base) + if ext == '.rc': + # these need to be compiled to object files + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + # object_filenames () + +# class EMXCCompiler + + +# Because these compilers aren't configured in Python's pyconfig.h file by +# default, we should at least warn the user if he is using a unmodified +# version. + +CONFIG_H_OK = "ok" +CONFIG_H_NOTOK = "not ok" +CONFIG_H_UNCERTAIN = "uncertain" + +def check_config_h(): + + """Check if the current Python installation (specifically, pyconfig.h) + appears amenable to building extensions with GCC. Returns a tuple + (status, details), where 'status' is one of the following constants: + CONFIG_H_OK + all is well, go ahead and compile + CONFIG_H_NOTOK + doesn't look good + CONFIG_H_UNCERTAIN + not sure -- unable to read pyconfig.h + 'details' is a human-readable string explaining the situation. + + Note there are two ways to conclude "OK": either 'sys.version' contains + the string "GCC" (implying that this Python was built with GCC), or the + installed "pyconfig.h" contains the string "__GNUC__". + """ + + # XXX since this function also checks sys.version, it's not strictly a + # "pyconfig.h" check -- should probably be renamed... + + from distutils import sysconfig + import string + # if sys.version contains GCC then python was compiled with + # GCC, and the pyconfig.h file should be OK + if string.find(sys.version,"GCC") >= 0: + return (CONFIG_H_OK, "sys.version mentions 'GCC'") + + fn = sysconfig.get_config_h_filename() + try: + # It would probably better to read single lines to search. + # But we do this only once, and it is fast enough + f = open(fn) + s = f.read() + f.close() + + except IOError, exc: + # if we can't read this file, we cannot say it is wrong + # the compiler will complain later about this file as missing + return (CONFIG_H_UNCERTAIN, + "couldn't read '%s': %s" % (fn, exc.strerror)) + + else: + # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar + if string.find(s,"__GNUC__") >= 0: + return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) + else: + return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) + + +def get_versions(): + """ Try to find out the versions of gcc and ld. + If not possible it returns None for it. + """ + from distutils.version import StrictVersion + from distutils.spawn import find_executable + import re + + gcc_exe = find_executable('gcc') + if gcc_exe: + out = os.popen(gcc_exe + ' -dumpversion','r') + out_string = out.read() + out.close() + result = re.search('(\d+\.\d+\.\d+)',out_string) + if result: + gcc_version = StrictVersion(result.group(1)) + else: + gcc_version = None + else: + gcc_version = None + # EMX ld has no way of reporting version number, and we use GCC + # anyway - so we can link OMF DLLs + ld_version = None + return (gcc_version, ld_version) + -- cgit v1.2.1 From e4a3cf2f6d0118090f23e5ed8b9d61afd9b2b6f5 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 8 Feb 2002 14:41:31 +0000 Subject: Make it 1.5.2 compatible again. --- msvccompiler.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 35336642..79a4901b 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -117,10 +117,11 @@ def get_msvc_paths (path, version='6.0', platform='x86'): if string.upper(p) == path: V = string.split(v,';') for v in V: - try: - v = v.encode("mbcs") - except UnicodeError: - pass + if hasattr(v, "encode"): + try: + v = v.encode("mbcs") + except UnicodeError: + pass if v == '' or v in L: continue L.append(v) break -- cgit v1.2.1 From ab694beb3331ca6d37bf29f0f783c2cd57c52bb5 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 11 Feb 2002 15:31:50 +0000 Subject: on MacOSX/Darwin, use ranlib when building static libs. --- unixccompiler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 356587d6..7e63c56a 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,7 +17,7 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" -import string, re, os +import string, re, os, sys from types import * from copy import copy from distutils import sysconfig @@ -62,6 +62,9 @@ class UnixCCompiler (CCompiler): 'ranlib' : None, } + if sys.platform[:6] == "darwin": + executables['ranlib'] = ["ranlib"] + # Needed for the filename generation methods provided by the base # class, CCompiler. NB. whoever instantiates/uses a particular # UnixCCompiler instance should set 'shared_lib_ext' -- we set a -- cgit v1.2.1 From 32ee66a4511be5b957e95bf1e71f148b611f9736 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 20 Feb 2002 08:01:19 +0000 Subject: First version which runs an install-script (specified by the --install-script ... command line option to bdist_wininst) at the end of the installation and at the start of deinstallation. Output (stdout, stderr) of the script (if any) is displayed in the last screen at installation, or in a simple message box at deinstallation. sys.argv[1] for the script will contain '-install' at installation time or '-remove' at deinstallation time. The installation script runs in an environment (embedded by the bdist_wininst runtime) where an additional function is available as builtin: create_shortcut(path, description, filename, [arguments[, workdir[, iconpath, iconindex]]]) Recreated this file after source changes. --- command/bdist_wininst.py | 668 +++++++++++++++++++++++++---------------------- 1 file changed, 354 insertions(+), 314 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 4a16eec4..fe52f393 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -38,6 +38,9 @@ class bdist_wininst (Command): "title to display on the installer background instead of default"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), + ('install-script=', None, + "installation script to be run after installation" + " or before deinstallation"), ] boolean_options = ['keep-temp'] @@ -52,6 +55,7 @@ class bdist_wininst (Command): self.bitmap = None self.title = None self.skip_build = 0 + self.install_script = None # initialize_options() @@ -71,6 +75,11 @@ class bdist_wininst (Command): self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + if self.install_script and \ + self.install_script not in self.distribution.scripts: + raise DistutilsOptionError, \ + "install_script '%s' not found in scripts" % self.install_script + # finalize_options() @@ -159,6 +168,8 @@ class bdist_wininst (Command): # The [setup] section contains entries controlling # the installer runtime. lines.append("\n[Setup]") + if self.install_script: + lines.append("install_script=%s" % self.install_script) lines.append("info=%s" % repr(info)[1:-1]) lines.append("target_compile=%d" % (not self.no_target_compile)) lines.append("target_optimize=%d" % (not self.no_target_optimize)) @@ -223,6 +234,17 @@ class bdist_wininst (Command): if __name__ == '__main__': # recreate EXEDATA from wininst.exe by rewriting this file + + # If you want to do this at home, you should: + # - checkout the *distutils* source code + # (see also http://sourceforge.net/cvs/?group_id=5470) + # by doing: + # cvs -d:pserver:anonymous@cvs.python.sourceforge.net:/cvsroot/python login + # and + # cvs -z3 -d:pserver:anonymous@cvs.python.sourceforge.net:/cvsroot/python co distutils + # - Built wininst.exe from the MSVC project file distutils/misc/wininst.dsw + # - Execute this file (distutils/distutils/command/bdist_wininst.py) + import re, base64 moddata = open("bdist_wininst.py", "r").read() exedata = open("../../misc/wininst.exe", "rb").read() @@ -236,338 +258,356 @@ if __name__ == '__main__': EXEDATA = """\ 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 -AAAAAAAAAAAAADoCAQBIAgEAWAIBAAAAAABmAgEAAAAAAHQCAQAAAAAAhAIBAAAAAACOAgEAAAAA -AJQCAQAAAAAAS0VSTkVMMzIuRExMAEFEVkFQSTMyLmRsbABDT01DVEwzMi5kbGwAR0RJMzIuZGxs -AE1TVkNSVC5kbGwAVVNFUjMyLmRsbAAATG9hZExpYnJhcnlBAABHZXRQcm9jQWRkcmVzcwAARXhp -dFByb2Nlc3MAAABSZWdDbG9zZUtleQAAAFByb3BlcnR5U2hlZXRBAABUZXh0T3V0QQAAZXhpdAAA -R2V0REMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgAAACAAAIAFAAAAYAAAgAAAAAAA +AAAAAAAAAAAAAQBuAAAAOAAAgAAAAAAAAAAAAAAAAAAAAQAAAAAAUAAAADDBAAAICgAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAQAawAAAJAAAIBsAAAAuAAAgG0AAADgAACAbgAAAAgBAIAAAAAAAAAA +AAAAAAAAAAEACQQAAKgAAAA4ywAAoAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAADQAAAA +2MwAAAQCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJBAAA+AAAAODOAABaAgAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAEACQQAACABAABA0QAAFAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAIBANAB +AQAAAAAAAAAAAAAAAAAdAgEA4AEBAAAAAAAAAAAAAAAAACoCAQDoAQEAAAAAAAAAAAAAAAAANwIB +APABAQAAAAAAAAAAAAAAAABBAgEA+AEBAAAAAAAAAAAAAAAAAEwCAQAAAgEAAAAAAAAAAAAAAAAA +VgIBAAgCAQAAAAAAAAAAAAAAAAAAAAAAAAAAAGACAQBuAgEAfgIBAAAAAACMAgEAAAAAAJoCAQAA +AAAAqgIBAAAAAAC0AgEAAAAAALoCAQAAAAAAyAIBAAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIu +ZGxsAENPTUNUTDMyLmRsbABHREkzMi5kbGwATVNWQ1JULmRsbABvbGUzMi5kbGwAVVNFUjMyLmRs +bAAATG9hZExpYnJhcnlBAABHZXRQcm9jQWRkcmVzcwAARXhpdFByb2Nlc3MAAABSZWdDbG9zZUtl +eQAAAFByb3BlcnR5U2hlZXRBAABUZXh0T3V0QQAAZXhpdAAAQ29Jbml0aWFsaXplAABHZXREQwAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAA= +AAAAAAAAAAAAAAAAAAAAAAAA """ # --- EOF --- -- cgit v1.2.1 From 09c86a0df132784f9ed870fd070a8b4f2438659c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Thu, 28 Feb 2002 09:16:21 +0000 Subject: Allow shebang's which use versioned Python binaries. Fixes bug #521526. --- command/build_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index bfa33c3a..444284f7 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -13,7 +13,7 @@ from distutils.dep_util import newer from distutils.util import convert_path # check if Python is called on the first line with this expression -first_line_re = re.compile(r'^#!.*python(\s+.*)?$') +first_line_re = re.compile(r'^#!.*python[0-9.]*(\s+.*)?$') class build_scripts (Command): -- cgit v1.2.1 From 8fd1b1700cffd1092deeb99b2448e2a42217299a Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Mar 2002 23:27:54 +0000 Subject: [Bug #517451] bdist_rpm didn't list all of its Boolean options. (Someone should check the other commands for this same error.) Bugfix candidate. --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 037ed9e8..4bc25613 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -95,7 +95,7 @@ class bdist_rpm (Command): "RPM 2 compatibility mode"), ] - boolean_options = ['keep-temp', 'rpm2-mode'] + boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode'] negative_opt = {'no-keep-temp': 'keep-temp', 'no-rpm-opt-flags': 'use-rpm-opt-flags', -- cgit v1.2.1 From 1e740e80316e36a442707027850bece78e8eb3af Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Mar 2002 23:44:01 +0000 Subject: Add unlisted Boolean options. Thomas H., can you please check that I got this right? Bugfix candidate, unless Thomas notes a problem. --- command/bdist_wininst.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index fe52f393..33dc28ed 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -43,7 +43,8 @@ class bdist_wininst (Command): " or before deinstallation"), ] - boolean_options = ['keep-temp'] + boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', + 'skip-build'] def initialize_options (self): self.bdist_dir = None -- cgit v1.2.1 From 127d81ce6d8d0d8e6c98fe835467cca676ac6e57 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Mar 2002 23:46:54 +0000 Subject: Add missing Boolean options Remove unused no_compile flag Initialize the Boolean attribute .compile to 0 instead of None Bugfix candidate. --- command/install.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/command/install.py b/command/install.py index 4d78d3aa..2a18fb9d 100644 --- a/command/install.py +++ b/command/install.py @@ -134,7 +134,7 @@ class install (Command): "filename in which to record list of installed files"), ] - boolean_options = ['force', 'skip-build'] + boolean_options = ['compile', 'force', 'skip-build'] negative_opt = {'no-compile' : 'compile'} @@ -164,8 +164,7 @@ class install (Command): self.install_scripts = None self.install_data = None - self.compile = None - self.no_compile = None + self.compile = 0 self.optimize = None # These two are for putting non-packagized distributions into their -- cgit v1.2.1 From b193016d79630c69c5bb7005c4a38a746aa74be2 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 22 Mar 2002 15:35:17 +0000 Subject: Revert part of previous patch: several install_* subcommands expect .compile to be None, and set it to true if it is. Caught by Pearu Peterson. Bugfix candidate, if the previous change is accepted for release22-maint. --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 2a18fb9d..746ca1f2 100644 --- a/command/install.py +++ b/command/install.py @@ -164,7 +164,7 @@ class install (Command): self.install_scripts = None self.install_data = None - self.compile = 0 + self.compile = None self.optimize = None # These two are for putting non-packagized distributions into their -- cgit v1.2.1 From 3f2880bde5a24637ed8dc9e8b534d3855705673c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 29 Mar 2002 18:00:19 +0000 Subject: [Patch #536769] Add -Xcompiler flag for adding arguments and switches for the compiler --- extension.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extension.py b/extension.py index a31ccbce..37377120 100644 --- a/extension.py +++ b/extension.py @@ -188,6 +188,8 @@ def read_setup_file (filename): append_next_word = ext.runtime_library_dirs elif word == "-Xlinker": append_next_word = ext.extra_link_args + elif word == "-Xcompiler": + append_next_word = ext.extra_compile_args elif switch == "-u": ext.extra_link_args.append(word) if not value: -- cgit v1.2.1 From fd007b43987507a5ef19f1ba2d9037d1aebf6c4d Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 4 Apr 2002 22:55:58 +0000 Subject: Convert a pile of obvious "yes/no" functions to return bool. --- command/build_py.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 97d094b1..453ca97a 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -190,9 +190,9 @@ class build_py (Command): if not os.path.isfile(module_file): self.warn("file %s (for module %s) not found" % (module_file, module)) - return 0 + return False else: - return 1 + return True # check_module () -- cgit v1.2.1 From 2b6ab4d227f851daa1f17389e45bc5456c3211e6 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 4 Apr 2002 23:17:31 +0000 Subject: Revert 0/1 -> False/True change; I didn't intend to muck w/ distutils. --- command/build_py.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 453ca97a..97d094b1 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -190,9 +190,9 @@ class build_py (Command): if not os.path.isfile(module_file): self.warn("file %s (for module %s) not found" % (module_file, module)) - return False + return 0 else: - return True + return 1 # check_module () -- cgit v1.2.1 From ae4a213ef2fb41949998f6087eeb9797cad3ee71 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 9 Apr 2002 14:14:38 +0000 Subject: Set the warn_dir option to 0 before running the install command. This suppresses bogus warnings about modules installed into a directory not in sys.path. Bugfix candidate. --- command/bdist_dumb.py | 1 + command/bdist_wininst.py | 1 + 2 files changed, 2 insertions(+) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index b627a86a..a135877a 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -81,6 +81,7 @@ class bdist_dumb (Command): install = self.reinitialize_command('install', reinit_subcommands=1) install.root = self.bdist_dir install.skip_build = self.skip_build + install.warn_dir = 0 self.announce("installing to %s" % self.bdist_dir) self.run_command('install') diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 33dc28ed..1683bb31 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -98,6 +98,7 @@ class bdist_wininst (Command): install = self.reinitialize_command('install') install.root = self.bdist_dir install.skip_build = self.skip_build + install.warn_dir = 0 install_lib = self.reinitialize_command('install_lib') # we do not want to include pyc or pyo files -- cgit v1.2.1 From 0691960a0fb3b6ce43f5d0c58985371ed2a98d48 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 9 Apr 2002 14:16:07 +0000 Subject: Remove unconditional debugging prints. --- command/bdist.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 68609f34..99f7d95e 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -131,9 +131,6 @@ class bdist (Command): if cmd_name not in self.no_format_option: sub_cmd.format = self.formats[i] - print ("bdist.run: format=%s, command=%s, rest=%s" % - (self.formats[i], cmd_name, commands[i+1:])) - # If we're going to need to run this command again, tell it to # keep its temporary files around so subsequent runs go faster. if cmd_name in commands[i+1:]: -- cgit v1.2.1 From 668ed0282eeb48450bfbf7800678dcacd6c2e4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Wed, 17 Apr 2002 20:30:10 +0000 Subject: Patch #531901 by Mark W. Alexander: adds a new distutils packager base class (in bdist_packager) and two subclasses which make use of this base class: bdist_pkgtool (for Solaris) and bdist_sdux (for HP-UX). --- command/__init__.py | 5 + command/bdist.py | 11 +- command/bdist_packager.py | 250 ++++++++++++++++++++++++++++ command/bdist_pkgtool.py | 412 ++++++++++++++++++++++++++++++++++++++++++++++ command/bdist_sdux.py | 302 +++++++++++++++++++++++++++++++++ 5 files changed, 976 insertions(+), 4 deletions(-) create mode 100644 command/bdist_packager.py create mode 100644 command/bdist_pkgtool.py create mode 100644 command/bdist_sdux.py diff --git a/command/__init__.py b/command/__init__.py index ef8e9ad6..81436275 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -21,4 +21,9 @@ __all__ = ['build', 'bdist_dumb', 'bdist_rpm', 'bdist_wininst', + 'bdist_sdux', + 'bdist_pkgtool', + # Note: + # bdist_packager is not included because it only provides + # an abstract base class ] diff --git a/command/bdist.py b/command/bdist.py index 99f7d95e..f61611eb 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -52,7 +52,7 @@ class bdist (Command): ] # The following commands do not take a format option from bdist - no_format_option = ('bdist_rpm',) + no_format_option = ('bdist_rpm', 'bdist_sdux', 'bdist_pkgtool') # This won't do in reality: will need to distinguish RPM-ish Linux, # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. @@ -62,18 +62,21 @@ class bdist (Command): # Establish the preferred order (for the --help-formats option). format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', - 'wininst', 'zip'] + 'wininst', 'zip', 'pkgtool', 'sdux'] # And the real information. format_command = { 'rpm': ('bdist_rpm', "RPM distribution"), - 'gztar': ('bdist_dumb', "gzip'ed tar file"), + 'zip': ('bdist_dumb', "ZIP file"), 'gztar': ('bdist_dumb', "gzip'ed tar file"), 'bztar': ('bdist_dumb', "bzip2'ed tar file"), 'ztar': ('bdist_dumb', "compressed tar file"), 'tar': ('bdist_dumb', "tar file"), 'wininst': ('bdist_wininst', "Windows executable installer"), 'zip': ('bdist_dumb', "ZIP file"), - } + 'pkgtool': ('bdist_pkgtool', + "Solaris pkgtool distribution"), + 'sdux': ('bdist_sdux', "HP-UX swinstall depot"), + } def initialize_options (self): diff --git a/command/bdist_packager.py b/command/bdist_packager.py new file mode 100644 index 00000000..a812307e --- /dev/null +++ b/command/bdist_packager.py @@ -0,0 +1,250 @@ +"""distutils.command.bdist_ packager + +Modified from bdist_dumb by Mark W. Alexander + +Implements the Distutils 'bdist_packager' abstract command +to be subclassed by binary package creation commands.""" + + +__revision__ = "$Id: bdist_packager.py,v 0.1 2001/04/4 mwa" + +import os +from distutils.core import Command +from distutils.util import get_platform +from distutils.dir_util import create_tree, remove_tree +from distutils.file_util import write_file +from distutils.errors import * +import string, sys + +class bdist_packager (Command): + + description = "abstract base for package manager specific bdist commands" + +# XXX update user_options + user_options = [ + ('bdist-base=', None, + "base directory for creating built distributions"), + ('pkg-dir=', None, + "base directory for creating binary packages (defaults to \"binary\" under "), + ('dist-dir=', 'd', + "directory to put final RPM files in " + "(and .spec files if --spec-only)"), + ('category=', None, + "Software category (packager dependent format)"), + ('revision=', None, + "package revision number"), + # the following have moved into the distribution class + #('packager=', None, + #"Package maintainer"), + #('packager-mail=', None, + #"Package maintainer's email address"), + #('author=', None, + #"Package author"), + #('author-mail=', None, + #"Package author's email address"), + #('license=', None, + #"License code"), + #('licence=', None, + #"alias for license"), + ('icon=', None, + "Package icon"), + ('subpackages=', None, + "Comma seperated list of seperately packaged trees"), + ('preinstall=', None, + "preinstall script (Bourne shell code)"), + ('postinstall=', None, + "postinstall script (Bourne shell code)"), + ('preremove=', None, + "preremove script (Bourne shell code)"), + ('postremove=', None, + "postremove script (Bourne shell code)"), + ('requires=', None, + "capabilities required by this package"), + ('keep-temp', 'k', + "don't clean up RPM build directory"), + ('control-only', None, + "Generate package control files and stop"), + ('no-autorelocate', None, + "Inhibit automatic relocation to installed site-packages"), + ] + + boolean_options = ['keep-temp', 'control-only', 'no_autorelocate'] + + def ensure_string_not_none (self,option,default=None): + val = getattr(self,option) + if val is not None: + return + Command.ensure_string(self,option,default) + val = getattr(self,option) + if val is None: + raise DistutilsOptionError, "'%s' must be provided" % option + + def ensure_script (self,arg): + if not arg: + return + try: + self.ensure_string(arg, None) + except: + try: + self.ensure_filename(arg, None) + except: + raise RuntimeError, \ + "cannot decipher script option (%s)" \ + % arg + + def write_script (self,path,attr,default=None): + """ write the script specified in attr to path. if attr is None, + write use default instead """ + val = getattr(self,attr) + if not val: + if not default: + return + else: + setattr(self,attr,default) + val = default + if val!="": + self.announce('Creating %s script', attr) + self.execute(write_file, + (path, self.get_script(attr)), + "writing '%s'" % path) + + def get_binary_name(self): + py_ver = sys.version[0:string.find(sys.version,' ')] + return self.name + '-' + self.version + '-' + \ + self.revision + '-' + py_ver + + def get_script (self,attr): + # accept a script as a string ("line\012line\012..."), + # a filename, or a list + # XXX We could probably get away with copy_file, but I'm + # guessing this will be more flexible later on.... + val = getattr(self,attr) + ret=None + if val: + try: + os.stat(val) + # script is a file + ret=[] + f=open(val) + ret=string.split(f.read(),"\012"); + f.close() + #return ret + except: + if type(val)==type(""): + # script is a string + ret = string.split(val,"\012") + elif type(val)==type([]): + # script is a list + ret = val + else: + raise RuntimeError, \ + "cannot figure out what to do with 'request' option (%s)" \ + % val + return ret + + + def initialize_options (self): + d = self.distribution + self.keep_temp = 0 + self.control_only = 0 + self.no_autorelocate = 0 + self.pkg_dir = None + self.plat_name = None + self.icon = None + self.requires = None + self.subpackages = None + self.category = None + self.revision = None + + # PEP 241 Metadata + self.name = None + self.version = None + #self.url = None + #self.author = None + #self.author_email = None + #self.maintainer = None + #self.maintainer_email = None + #self.description = None + #self.long_description = None + #self.licence = None + #self.platforms = None + #self.keywords = None + self.root_package = None + + # package installation scripts + self.preinstall = None + self.postinstall = None + self.preremove = None + self.postremove = None + # initialize_options() + + + def finalize_options (self): + + if self.pkg_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.pkg_dir = os.path.join(bdist_base, 'binary') + + if not self.plat_name: + d = self.distribution + self.plat = d.get_platforms() + if self.distribution.has_ext_modules(): + self.plat_name = [sys.platform,] + else: + self.plat_name = ["noarch",] + + d = self.distribution + self.ensure_string_not_none('name', d.get_name()) + self.ensure_string_not_none('version', d.get_version()) + self.ensure_string('category') + self.ensure_string('revision',"1") + #self.ensure_string('url',d.get_url()) + if type(self.distribution.packages) == type([]): + self.root_package=self.distribution.packages[0] + else: + self.root_package=self.name + self.ensure_string('root_package',self.root_package) + #self.ensure_string_list('keywords') + #self.ensure_string_not_none('author', d.get_author()) + #self.ensure_string_not_none('author_email', d.get_author_email()) + self.ensure_filename('icon') + #self.ensure_string_not_none('maintainer', d.get_maintainer()) + #self.ensure_string_not_none('maintainer_email', + #d.get_maintainer_email()) + #self.ensure_string_not_none('description', d.get_description()) + #self.ensure_string_not_none('long_description', d.get_long_description()) + #if self.long_description=='UNKNOWN': + #self.long_description=self.description + #self.ensure_string_not_none('license', d.get_license()) + self.ensure_string_list('requires') + self.ensure_filename('preinstall') + self.ensure_filename('postinstall') + self.ensure_filename('preremove') + self.ensure_filename('postremove') + + # finalize_options() + + + def run (self): + + raise RuntimeError, \ + "abstract method -- subclass %s must override" % self.__class__ + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.root = self.pkg_dir + + self.announce("installing to %s" % self.pkg_dir) + self.run_command('install') + + # And make an archive relative to the root of the + # pseudo-installation tree. + archive_basename = "%s.%s" % (self.distribution.get_fullname(), + self.plat_name) + + if not self.keep_temp: + remove_tree(self.pkg_dir, self.verbose, self.dry_run) + + # run() + +# class bdist_packager diff --git a/command/bdist_pkgtool.py b/command/bdist_pkgtool.py new file mode 100644 index 00000000..fc035ebd --- /dev/null +++ b/command/bdist_pkgtool.py @@ -0,0 +1,412 @@ +"""distutils.command.bdist_pkgtool + + +Author: Mark W. Alexander + +Implements the Distutils 'bdist_pkgtool' command (create Solaris pkgtool +distributions).""" + +import os, string, sys, pwd, grp +import glob +from types import * +from distutils.core import Command, DEBUG +from distutils.util import get_platform +from distutils.file_util import write_file +from distutils.errors import * +from distutils.command import bdist_packager +from distutils import sysconfig +import compileall +from commands import getoutput + +__revision__ = "$Id: bdist_pkgtool.py,v 0.3 mwa " + +# default request script - Is also wrapped around user's request script +# unless --no-autorelocate is requested. Finds the python site-packages +# directory and prompts for verification +DEFAULT_REQUEST="""#!/bin/sh +###################################################################### +# Distutils internal package relocation support # +###################################################################### + +PRODUCT="__DISTUTILS_NAME__" + +trap `exit 3` 15 +/usr/bin/which python 2>&1 >/dev/null +if [ $? -ne 0 ]; then + echo "The python interpretor needs to be on your path!" + echo + echo "If you have more than one, make sure the first one is where" + echo "you want this module installed:" + exit 1 +fi + +PY_DIR=`python -c "import sys;print '%s/lib/python%s' % (sys.exec_prefix,sys.version[0:3])" 2>/dev/null` +PY_PKG_DIR=`python -c "import sys;print '%s/lib/python%s/site-packages' % (sys.exec_prefix,sys.version[0:3])" 2>/dev/null` + +echo "" +if [ -z "${PY_DIR}" ]; then + echo "I can't seem to find the python distribution." + echo "I'm assuming the default path for site-packages" +else + BASEDIR="${PY_PKG_DIR}" + cat <&1 >/dev/null +if [ $? -ne 0 ]; then + echo "The python interpretor needs to be on your path!" + echo + echo "If you have more than one, make sure the first one is where" + echo "you want this module removed from" + exit 1 +fi + +/usr/bin/test -d ${BASEDIR}/__DISTUTILS_NAME__ +if [ $? -eq 0 ]; then + find ${BASEDIR}/__DISTUTILS_NAME__ -name "*.pyc" -exec rm {} \; + find ${BASEDIR}/__DISTUTILS_NAME__ -name "*.pyo" -exec rm {} \; +fi +""" + +# default postremove removes the module directory _IF_ no files are +# there (Turns out this isn't needed if the preremove does it's job +# Left for posterity +DEFAULT_POSTREMOVE="""#!/bin/sh + +/usr/bin/test -d ${BASEDIR}/__DISTUTILS_NAME__ +if [ $? -eq 0 ]; then + if [ `find ${BASEDIR}/__DISTUTILS_NAME__ ! -type d | wc -l` -eq 0 ]; then + rm -rf ${BASEDIR}/__DISTUTILS_NAME__ + fi +fi +""" + +class bdist_pkgtool (bdist_packager.bdist_packager): + + description = "create an pkgtool (Solaris) package" + + user_options = bdist_packager.bdist_packager.user_options + [ + ('revision=', None, + "package revision number (PSTAMP)"), + ('pkg-abrev=', None, + "Abbreviation (9 characters or less) of the package name"), + ('compver=', None, + "file containing compatible versions of this package (man compver)"), + ('depend=', None, + "file containing dependencies for this package (man depend)"), + #('category=', None, + #"Software category"), + ('request=', None, + "request script (Bourne shell code)"), + ] + + def initialize_options (self): + # XXX Check for pkgtools on path... + bdist_packager.bdist_packager.initialize_options(self) + self.compver = None + self.depend = None + self.vendor = None + self.classes = None + self.request = None + self.pkg_abrev = None + self.revision = None + # I'm not sure I should need to do this, but the setup.cfg + # settings weren't showing up.... + options = self.distribution.get_option_dict('bdist_packager') + for key in options.keys(): + setattr(self,key,options[key][1]) + + # initialize_options() + + + def finalize_options (self): + global DEFAULT_REQUEST, DEFAULT_POSTINSTALL + global DEFAULT_PREREMOVE, DEFAULT_POSTREMOVE + if self.pkg_dir is None: + dist_dir = self.get_finalized_command('bdist').dist_dir + self.pkg_dir = os.path.join(dist_dir, "pkgtool") + + self.ensure_string('classes', None) + self.ensure_string('revision', "1") + self.ensure_script('request') + self.ensure_script('preinstall') + self.ensure_script('postinstall') + self.ensure_script('preremove') + self.ensure_script('postremove') + self.ensure_string('vendor', None) + if self.__dict__.has_key('author'): + if self.__dict__.has_key('author_email'): + self.ensure_string('vendor', + "%s <%s>" % (self.author, + self.author_email)) + else: + self.ensure_string('vendor', + "%s" % (self.author)) + self.ensure_string('category', "System,application") + self.ensure_script('compver') + self.ensure_script('depend') + bdist_packager.bdist_packager.finalize_options(self) + if self.pkg_abrev is None: + self.pkg_abrev=self.name + if len(self.pkg_abrev)>9: + raise DistutilsOptionError, \ + "pkg-abrev (%s) must be less than 9 characters" % self.pkg_abrev + # Update default scripts with our metadata name + DEFAULT_REQUEST = string.replace(DEFAULT_REQUEST, + "__DISTUTILS_NAME__", self.root_package) + DEFAULT_POSTINSTALL = string.replace(DEFAULT_POSTINSTALL, + "__DISTUTILS_NAME__", self.root_package) + DEFAULT_PREREMOVE = string.replace(DEFAULT_PREREMOVE, + "__DISTUTILS_NAME__", self.root_package) + DEFAULT_POSTREMOVE = string.replace(DEFAULT_POSTREMOVE, + "__DISTUTILS_NAME__", self.root_package) + + # finalize_options() + + + def make_package(self,root=None): + # make directories + self.mkpath(self.pkg_dir) + if root: + pkg_dir = self.pkg_dir+"/"+root + self.mkpath(pkg_dir) + else: + pkg_dir = self.pkg_dir + + install = self.reinitialize_command('install', reinit_subcommands=1) + # build package + self.announce('Building package') + self.run_command('build') + self.announce('Creating pkginfo file') + path = os.path.join(pkg_dir, "pkginfo") + self.execute(write_file, + (path, + self._make_info_file()), + "writing '%s'" % path) + # request script handling + if self.request==None: + self.request = self._make_request_script() + if self.request!="": + path = os.path.join(pkg_dir, "request") + self.execute(write_file, + (path, + self.request), + "writing '%s'" % path) + + # Create installation scripts, since compver & depend are + # user created files, they work just fine as scripts + self.write_script(os.path.join(pkg_dir, "postinstall"), + 'postinstall',DEFAULT_POSTINSTALL) + self.write_script(os.path.join(pkg_dir, "preinstall"), + 'preinstall',None) + self.write_script(os.path.join(pkg_dir, "preremove"), + 'preremove',DEFAULT_PREREMOVE) + self.write_script(os.path.join(pkg_dir, "postremove"), + 'postremove',None) + self.write_script(os.path.join(pkg_dir, "compver"), + 'compver',None) + self.write_script(os.path.join(pkg_dir, "depend"), + 'depend',None) + + self.announce('Creating prototype file') + path = os.path.join(pkg_dir, "prototype") + self.execute(write_file, + (path, + self._make_prototype()), + "writing '%s'" % path) + + + if self.control_only: # stop if requested + return + + + self.announce('Creating package') + pkg_cmd = ['pkgmk', '-o', '-f'] + pkg_cmd.append(path) + pkg_cmd.append('-b') + pkg_cmd.append(os.environ['PWD']) + self.spawn(pkg_cmd) + pkg_cmd = ['pkgtrans', '-s', '/var/spool/pkg'] + path = os.path.join(os.environ['PWD'],pkg_dir, + self.get_binary_name() + ".pkg") + self.announce('Transferring package to ' + pkg_dir) + pkg_cmd.append(path) + pkg_cmd.append(self.pkg_abrev) + self.spawn(pkg_cmd) + os.system("rm -rf /var/spool/pkg/%s" % self.pkg_abrev) + + + def run (self): + if self.subpackages: + self.subpackages=string.split(self.subpackages,",") + for pkg in self.subpackages: + self.make_package(subpackage) + else: + self.make_package() + # run() + + + def _make_prototype(self): + proto_file = ["i pkginfo"] + if self.request: + proto_file.extend(['i request']) + if self.postinstall: + proto_file.extend(['i postinstall']) + if self.postremove: + proto_file.extend(['i postremove']) + if self.preinstall: + proto_file.extend(['i preinstall']) + if self.preremove: + proto_file.extend(['i preremove']) + if self.compver: + proto_file.extend(['i compver']) + if self.requires: + proto_file.extend(['i depend']) + proto_file.extend(['!default 644 root bin']) + build = self.get_finalized_command('build') + + try: + self.distribution.packages[0] + file_list=string.split( + getoutput("pkgproto %s/%s=%s" % (build.build_lib, + self.distribution.packages[0], + self.distribution.packages[0])),"\012") + except: + file_list=string.split( + getoutput("pkgproto %s=" % (build.build_lib)),"\012") + ownership="%s %s" % (pwd.getpwuid(os.getuid())[0], + grp.getgrgid(os.getgid())[0]) + for i in range(len(file_list)): + file_list[i] = string.replace(file_list[i],ownership,"root bin") + proto_file.extend(file_list) + return proto_file + + def _make_request_script(self): + global DEFAULT_REQUEST + # A little different from other scripts, if we are to automatically + # relocate to the target site-packages, we have to wrap any provided + # script with the autorelocation script. If no script is provided, + # The request script will simply be the autorelocate script + if self.no_autorelocate==0: + request=string.split(DEFAULT_REQUEST,"\012") + else: + self.announce('Creating relocation request script') + if self.request: + users_request=self.get_script('request') + if users_request!=None and users_request!=[]: + if self.no_autorelocate==0 and users_request[0][0:2]=="#!": + users_request.remove(users_request[0]) + for i in users_request: + request.append(i) + + if self.no_autorelocate==0: + request.append("#############################################") + request.append("# finalize relocation support #") + request.append("#############################################") + request.append('echo "BASEDIR=\\"${BASEDIR}\\"" >>$1') + return request + + def _make_info_file(self): + """Generate the text of a pkgtool info file and return it as a + list of strings (one per line). + """ + # definitions and headers + # PKG must be alphanumeric, < 9 characters + info_file = [ + 'PKG="%s"' % self.pkg_abrev, + 'NAME="%s"' % self.name, + 'VERSION="%s"' % self.version, + 'PSTAMP="%s"' % self.revision, + ] + info_file.extend(['VENDOR="%s (%s)"' % (self.distribution.maintainer, \ + self.distribution.license) ]) + info_file.extend(['EMAIL="%s"' % self.distribution.maintainer_email ]) + + p = self.distribution.get_platforms() + if p is None or p==['UNKNOWN']: + archs=getoutput('uname -p') + else: + archs=string.join(self.distribution.get_platforms(),',') + #else: + #print "Assuming a sparc architecure" + #archs='sparc' + info_file.extend(['ARCH="%s"' % archs ]) + + if self.distribution.get_url(): + info_file.extend(['HOTLINE="%s"' % self.distribution.get_url() ]) + if self.classes: + info_file.extend(['CLASSES="%s"' % self.classes ]) + if self.category: + info_file.extend(['CATEGORY="%s"' % self.category ]) + site=None + for i in sys.path: + if i[-13:]=="site-packages": + site=i + break + if site: + info_file.extend(['BASEDIR="%s"' % site ]) + + return info_file + + # _make_info_file () + + def _format_changelog(self, changelog): + """Format the changelog correctly and convert it to a list of strings + """ + if not changelog: + return changelog + new_changelog = [] + for line in string.split(string.strip(changelog), '\n'): + line = string.strip(line) + if line[0] == '*': + new_changelog.extend(['', line]) + elif line[0] == '-': + new_changelog.append(line) + else: + new_changelog.append(' ' + line) + + # strip trailing newline inserted by first changelog entry + if not new_changelog[0]: + del new_changelog[0] + + return new_changelog + + # _format_changelog() + +# class bdist_rpm diff --git a/command/bdist_sdux.py b/command/bdist_sdux.py new file mode 100644 index 00000000..985a37a5 --- /dev/null +++ b/command/bdist_sdux.py @@ -0,0 +1,302 @@ +"""distutils.command.bdist_pkgtool + +Implements the Distutils 'bdist_sdux' command to create HP-UX +swinstall depot""" + +# Mark Alexander + +__revision__ = "$Id: bdist_sdux.py,v 0.2 " +import os, string +import glob +from types import * +from distutils.core import Command, DEBUG +from distutils.util import get_platform +from distutils.file_util import write_file +from distutils.errors import * +from distutils.command import bdist_packager +import sys +from commands import getoutput + +DEFAULT_CHECKINSTALL="""#!/bin/sh +/usr/bin/which python 2>&1 >/dev/null +if [ $? -ne 0 ]; then + echo "ERROR: Python must be on your PATH" &>2 + echo "ERROR: (You may need to link it to /usr/bin) " &>2 + exit 1 +fi +PY_DIR=`python -c "import sys;print '%s/lib/python%s' % (sys.exec_prefix,sys.version[0:3])" #2>/dev/null` +PY_PKG_DIR=`python -c "import sys;print '%s/lib/python%s/site-packages' % (sys.exec_prefix,sys.version[0:3])" #2>/dev/null` +PY_LIB_DIR=`dirname $PY_PKG_DIR` + +if [ "`dirname ${SW_LOCATION}`" = "__DISTUTILS_PKG_DIR__" ]; then + # swinstall to default location + if [ "${PY_PKG_DIR}" != "__DISTUTILS_PKG_DIR__" ]; then + echo "ERROR: " &>2 + echo "ERROR: Python is not installed where this package expected!" &>2 + echo "ERROR: You need to manually relocate this package to your python installation." &>2 + echo "ERROR: " &>2 + echo "ERROR: Re-run swinstall specifying the product name:location, e.g.:" &>2 + echo "ERROR: " &>2 + echo "ERROR: swinstall -s [source] __DISTUTILS_NAME__:${PY_PKG_DIR}/__DISTUTILS_DIRNAME__" &>2 + echo "ERROR: " &>2 + echo "ERROR: to relocate this package to match your python installation" &>2 + echo "ERROR: " &>2 + exit 1 + fi +else + if [ "`dirname ${SW_LOCATION}`" != "${PY_PKG_DIR}" -a "`dirname ${SWLOCATION}`" != "${PY_LIB_DIR}" ]; then + echo "WARNING: " &>2 + echo "WARNING: Package is being installed outside the 'normal' python search path!" &>2 + echo "WARNING: Add ${SW_LOCATION} to PYTHONPATH to use this package" &>2 + echo "WARNING: " &>2 + fi +fi +""" + +DEFAULT_POSTINSTALL="""#!/bin/sh +/usr/bin/which python 2>&1 >/dev/null +if [ $? -ne 0 ]; then + echo "ERROR: Python must be on your PATH" &>2 + echo "ERROR: (You may need to link it to /usr/bin) " &>2 + exit 1 +fi +python -c "import compileall;compileall.compile_dir(\\"${SW_LOCATION}\\")" +""" + +DEFAULT_PREREMOVE="""#!/bin/sh +# remove compiled bytecode files +find ${SW_LOCATION} -name "*.pyc" -exec rm {} \; +find ${SW_LOCATION} -name "*.pyo" -exec rm {} \; +""" + +DEFAULT_POSTREMOVE="""#!/bin/sh +if [ `find ${SW_LOCATION} ! -type d | wc -l` -eq 0 ]; then + # remove if there's nothing but empty directories left + rm -rf ${SW_LOCATION} +fi +""" + +class bdist_sdux(bdist_packager.bdist_packager): + + description = "create an HP swinstall depot" + + user_options = bdist_packager.bdist_packager.user_options + [ + #('revision=', None, + #"package revision number (PSTAMP)"), + ('keep-permissions', None, + "Don't reset permissions and ownership to root/bin"), # XXX + ('corequisites=', None, + "corequisites"), # XXX + ('prerequisites=', None, + "prerequisites"), # XXX + #('category=', None, + #"Software category"), + ('checkinstall=', None, # XXX ala request + "checkinstall script (Bourne shell code)"), + ('configure=', None, # XXX + "configure script (Bourne shell code)"), + ('unconfigure=', None, # XXX + "unconfigure script (Bourne shell code)"), + ('verify=', None, # XXX + "verify script (Bourne shell code)"), + ('unpreinstall=', None, # XXX + "unpreinstall script (Bourne shell code)"), + ('unpostinstall=', None, # XXX + "unpostinstall script (Bourne shell code)"), + ] + + boolean_options = ['keep-permissions'] + + def initialize_options (self): + bdist_packager.bdist_packager.initialize_options(self) + self.corequisites = None + self.prerequesites = None + self.checkinstall = None + self.configure = None + self.unconfigure = None + self.verify = None + self.unpreinstall = None + self.unpostinstall = None + # More + self.copyright = None + self.readme = None + self.machine_type = None + self.os_name = None + self.os_release = None + self.directory = None + self.readme = None + self.copyright = None + self.architecture= None + self.keep_permissions= None + options = self.distribution.get_option_dict('bdist_packager') + for key in options.keys(): + setattr(self,key,options[key][1]) + + # initialize_options() + + + def finalize_options (self): + global DEFAULT_CHECKINSTALL, DEFAULT_POSTINSTALL + global DEFAULT_PREREMOVE, DEFAULT_POSTREMOVE + if self.pkg_dir==None: + dist_dir = self.get_finalized_command('bdist').dist_dir + self.pkg_dir = os.path.join(dist_dir, "sdux") + self.ensure_script('corequisites') + self.ensure_script('prerequesites') + self.ensure_script('checkinstall') + self.ensure_script('configure') + self.ensure_script('unconfigure') + self.ensure_script('verify') + self.ensure_script('unpreinstall') + self.ensure_script('unpostinstall') + self.ensure_script('copyright') + self.ensure_script('readme') + self.ensure_string('machine_type','*') + if not self.__dict__.has_key('platforms'): + # This is probably HP, but if it's not, use sys.platform + if sys.platform[0:5] == "hp-ux": + self.platforms = "HP-UX" + else: + self.platforms = string.upper(sys.platform) + else: + # we can only handle one + self.platforms=string.join(self.platforms[0]) + self.ensure_string('os_release','*') + self.ensure_string('directory','%s/lib/python%s/site-packages' % \ + (sys.exec_prefix, sys.version[0:3])) + bdist_packager.bdist_packager.finalize_options(self) + DEFAULT_CHECKINSTALL = string.replace(DEFAULT_CHECKINSTALL, + "__DISTUTILS_NAME__", self.name) + DEFAULT_CHECKINSTALL = string.replace(DEFAULT_CHECKINSTALL, + "__DISTUTILS_DIRNAME__", self.root_package) + DEFAULT_CHECKINSTALL = string.replace(DEFAULT_CHECKINSTALL, + "__DISTUTILS_PKG_DIR__", self.directory) + DEFAULT_POSTINSTALL = string.replace(DEFAULT_POSTINSTALL, + "__DISTUTILS_DIRNAME__", self.root_package) + #DEFAULT_PREREMOVE = string.replace(DEFAULT_PREREMOVE, + #"__DISTUTILS_NAME__", self.root_package) + #DEFAULT_POSTREMOVE = string.replace(DEFAULT_POSTREMOVE, + #"__DISTUTILS_NAME__", self.root_package) + # finalize_options() + + def run (self): + # make directories + self.mkpath(self.pkg_dir) + psf_path = os.path.join(self.pkg_dir, + "%s.psf" % self.get_binary_name()) + # build package + self.announce('Building package') + self.run_command('build') + self.announce('Creating psf file') + self.execute(write_file, + (psf_path, + self._make_control_file()), + "writing '%s'" % psf_path) + if self.control_only: # stop if requested + return + + self.announce('Creating package') + spawn_cmd = ['swpackage', '-s'] + spawn_cmd.append(psf_path) + spawn_cmd.append('-x') + spawn_cmd.append('target_type=tape') + spawn_cmd.append('@') + spawn_cmd.append(self.pkg_dir+"/"+self.get_binary_name()+'.depot') + self.spawn(spawn_cmd) + + # run() + + + def _make_control_file(self): + # Generate a psf file and return it as list of strings (one per line). + # definitions and headers + title = "%s %s" % (self.maintainer,self.maintainer_email) + title=title[0:80] + #top=self.distribution.packages[0] + psf_file = [ + 'vendor', # Vendor information + ' tag %s' % "DISTUTILS", + ' title %s' % title, + ' description Distutils package maintainer (%s)' % self.license, + 'end', # end of vendor + 'product', # Product information + ' tag %s' % self.name, + ' title %s' % self.description, + ' description %s' % self.description, + ' revision %s' % self.version, + ' architecture %s' % self.platforms, + ' machine_type %s' % self.machine_type, + ' os_name %s' % self.platforms, + ' os_release %s' % self.os_release, + ' directory %s' % self.directory + "/" + self.root_package, + ] + + self.write_script(os.path.join(self.pkg_dir, "checkinstall"), + 'checkinstall',DEFAULT_CHECKINSTALL) + psf_file.extend([' checkinstall %s/checkinstall' % self.pkg_dir]) + self.write_script(os.path.join(self.pkg_dir, "postinstall"), + 'postinstall',DEFAULT_POSTINSTALL) + psf_file.extend([' postinstall %s/postinstall' % self.pkg_dir]) + self.write_script(os.path.join(self.pkg_dir, "preremove"), + 'preremove',DEFAULT_PREREMOVE) + psf_file.extend([' preremove %s/preremove' % self.pkg_dir]) + self.write_script(os.path.join(self.pkg_dir, "postremove"), + 'postremove',DEFAULT_POSTREMOVE) + psf_file.extend([' postremove %s/postremove' % self.pkg_dir]) + if self.preinstall: + self.write_script(self.pkg_dir+"/preinstall", 'preinstall', None) + psf_file.extend([' preinstall %s/preinstall' % self.pkg_dir]) + if self.configure: + self.write_script(self.pkg_dir+"/configure", 'configure', None) + psf_file.extend([' configure %s/configure' % self.pkg_dir]) + if self.unconfigure: + self.write_script(self.pkg_dir+"/unconfigure", 'unconfigure', None) + psf_file.extend([' unconfigure %s/unconfigure' % self.pkg_dir]) + if self.verify: + self.write_script(self.pkg_dir+"/verify", 'verify', None) + psf_file.extend([' verify %s/verify' % self.pkg_dir]) + if self.unpreinstall: + self.write_script(self.pkg_dir+"/unpreinstall", 'unpreinstall', None) + psf_file.extend([' unpreinstall %s/unpreinstall' % self.pkg_dir]) + if self.unpostinstall: + self.write_script(self.pkg_dir+"/unpostinstall", 'unpostinstall', None) + psf_file.extend([' unpostinstall %s/unpostinstall' % self.pkg_dir]) + psf_file.extend([' is_locatable true']) + #if self.long_description: + #psf_file.extend([self.long_description]) + if self.copyright: + # XX make a copyright file XXX + write_script('copyright') + psf_file.extend([' copyright Date: Tue, 23 Apr 2002 18:18:43 +0000 Subject: Whitespace normalization. Unka Timmy would be proud. --- command/bdist_packager.py | 12 +++--- command/bdist_pkgtool.py | 102 +++++++++++++++++++++++----------------------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/command/bdist_packager.py b/command/bdist_packager.py index a812307e..667c0306 100644 --- a/command/bdist_packager.py +++ b/command/bdist_packager.py @@ -2,7 +2,7 @@ Modified from bdist_dumb by Mark W. Alexander -Implements the Distutils 'bdist_packager' abstract command +Implements the Distutils 'bdist_packager' abstract command to be subclassed by binary package creation commands.""" @@ -33,7 +33,7 @@ class bdist_packager (Command): "Software category (packager dependent format)"), ('revision=', None, "package revision number"), - # the following have moved into the distribution class + # the following have moved into the distribution class #('packager=', None, #"Package maintainer"), #('packager-mail=', None, @@ -199,10 +199,10 @@ class bdist_packager (Command): self.ensure_string('category') self.ensure_string('revision',"1") #self.ensure_string('url',d.get_url()) - if type(self.distribution.packages) == type([]): - self.root_package=self.distribution.packages[0] - else: - self.root_package=self.name + if type(self.distribution.packages) == type([]): + self.root_package=self.distribution.packages[0] + else: + self.root_package=self.name self.ensure_string('root_package',self.root_package) #self.ensure_string_list('keywords') #self.ensure_string_not_none('author', d.get_author()) diff --git a/command/bdist_pkgtool.py b/command/bdist_pkgtool.py index fc035ebd..a36638a9 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -23,7 +23,7 @@ __revision__ = "$Id: bdist_pkgtool.py,v 0.3 mwa " # default request script - Is also wrapped around user's request script # unless --no-autorelocate is requested. Finds the python site-packages # directory and prompts for verification -DEFAULT_REQUEST="""#!/bin/sh +DEFAULT_REQUEST="""#!/bin/sh ###################################################################### # Distutils internal package relocation support # ###################################################################### @@ -34,7 +34,7 @@ trap `exit 3` 15 /usr/bin/which python 2>&1 >/dev/null if [ $? -ne 0 ]; then echo "The python interpretor needs to be on your path!" - echo + echo echo "If you have more than one, make sure the first one is where" echo "you want this module installed:" exit 1 @@ -45,26 +45,26 @@ PY_PKG_DIR=`python -c "import sys;print '%s/lib/python%s/site-packages' % (sys.e echo "" if [ -z "${PY_DIR}" ]; then - echo "I can't seem to find the python distribution." - echo "I'm assuming the default path for site-packages" + echo "I can't seem to find the python distribution." + echo "I'm assuming the default path for site-packages" else - BASEDIR="${PY_PKG_DIR}" - cat <&1 >/dev/null if [ $? -ne 0 ]; then echo "The python interpretor needs to be on your path!" - echo + echo echo "If you have more than one, make sure the first one is where" echo "you want this module removed from" exit 1 @@ -99,8 +99,8 @@ fi /usr/bin/test -d ${BASEDIR}/__DISTUTILS_NAME__ if [ $? -eq 0 ]; then - find ${BASEDIR}/__DISTUTILS_NAME__ -name "*.pyc" -exec rm {} \; - find ${BASEDIR}/__DISTUTILS_NAME__ -name "*.pyo" -exec rm {} \; + find ${BASEDIR}/__DISTUTILS_NAME__ -name "*.pyc" -exec rm {} \; + find ${BASEDIR}/__DISTUTILS_NAME__ -name "*.pyo" -exec rm {} \; fi """ @@ -111,9 +111,9 @@ DEFAULT_POSTREMOVE="""#!/bin/sh /usr/bin/test -d ${BASEDIR}/__DISTUTILS_NAME__ if [ $? -eq 0 ]; then - if [ `find ${BASEDIR}/__DISTUTILS_NAME__ ! -type d | wc -l` -eq 0 ]; then - rm -rf ${BASEDIR}/__DISTUTILS_NAME__ - fi + if [ `find ${BASEDIR}/__DISTUTILS_NAME__ ! -type d | wc -l` -eq 0 ]; then + rm -rf ${BASEDIR}/__DISTUTILS_NAME__ + fi fi """ @@ -171,13 +171,13 @@ class bdist_pkgtool (bdist_packager.bdist_packager): self.ensure_script('postremove') self.ensure_string('vendor', None) if self.__dict__.has_key('author'): - if self.__dict__.has_key('author_email'): - self.ensure_string('vendor', - "%s <%s>" % (self.author, - self.author_email)) - else: - self.ensure_string('vendor', - "%s" % (self.author)) + if self.__dict__.has_key('author_email'): + self.ensure_string('vendor', + "%s <%s>" % (self.author, + self.author_email)) + else: + self.ensure_string('vendor', + "%s" % (self.author)) self.ensure_string('category', "System,application") self.ensure_script('compver') self.ensure_script('depend') @@ -188,7 +188,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): raise DistutilsOptionError, \ "pkg-abrev (%s) must be less than 9 characters" % self.pkg_abrev # Update default scripts with our metadata name - DEFAULT_REQUEST = string.replace(DEFAULT_REQUEST, + DEFAULT_REQUEST = string.replace(DEFAULT_REQUEST, "__DISTUTILS_NAME__", self.root_package) DEFAULT_POSTINSTALL = string.replace(DEFAULT_POSTINSTALL, "__DISTUTILS_NAME__", self.root_package) @@ -243,7 +243,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): 'compver',None) self.write_script(os.path.join(pkg_dir, "depend"), 'depend',None) - + self.announce('Creating prototype file') path = os.path.join(pkg_dir, "prototype") self.execute(write_file, @@ -263,14 +263,14 @@ class bdist_pkgtool (bdist_packager.bdist_packager): pkg_cmd.append(os.environ['PWD']) self.spawn(pkg_cmd) pkg_cmd = ['pkgtrans', '-s', '/var/spool/pkg'] - path = os.path.join(os.environ['PWD'],pkg_dir, - self.get_binary_name() + ".pkg") + path = os.path.join(os.environ['PWD'],pkg_dir, + self.get_binary_name() + ".pkg") self.announce('Transferring package to ' + pkg_dir) pkg_cmd.append(path) pkg_cmd.append(self.pkg_abrev) self.spawn(pkg_cmd) os.system("rm -rf /var/spool/pkg/%s" % self.pkg_abrev) - + def run (self): if self.subpackages: @@ -281,7 +281,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): self.make_package() # run() - + def _make_prototype(self): proto_file = ["i pkginfo"] if self.request: @@ -302,15 +302,15 @@ class bdist_pkgtool (bdist_packager.bdist_packager): build = self.get_finalized_command('build') try: - self.distribution.packages[0] - file_list=string.split( - getoutput("pkgproto %s/%s=%s" % (build.build_lib, - self.distribution.packages[0], - self.distribution.packages[0])),"\012") + self.distribution.packages[0] + file_list=string.split( + getoutput("pkgproto %s/%s=%s" % (build.build_lib, + self.distribution.packages[0], + self.distribution.packages[0])),"\012") except: - file_list=string.split( - getoutput("pkgproto %s=" % (build.build_lib)),"\012") - ownership="%s %s" % (pwd.getpwuid(os.getuid())[0], + file_list=string.split( + getoutput("pkgproto %s=" % (build.build_lib)),"\012") + ownership="%s %s" % (pwd.getpwuid(os.getuid())[0], grp.getgrgid(os.getgid())[0]) for i in range(len(file_list)): file_list[i] = string.replace(file_list[i],ownership,"root bin") @@ -324,7 +324,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): # script with the autorelocation script. If no script is provided, # The request script will simply be the autorelocate script if self.no_autorelocate==0: - request=string.split(DEFAULT_REQUEST,"\012") + request=string.split(DEFAULT_REQUEST,"\012") else: self.announce('Creating relocation request script') if self.request: @@ -334,7 +334,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): users_request.remove(users_request[0]) for i in users_request: request.append(i) - + if self.no_autorelocate==0: request.append("#############################################") request.append("# finalize relocation support #") @@ -355,12 +355,12 @@ class bdist_pkgtool (bdist_packager.bdist_packager): 'PSTAMP="%s"' % self.revision, ] info_file.extend(['VENDOR="%s (%s)"' % (self.distribution.maintainer, \ - self.distribution.license) ]) + self.distribution.license) ]) info_file.extend(['EMAIL="%s"' % self.distribution.maintainer_email ]) - + p = self.distribution.get_platforms() - if p is None or p==['UNKNOWN']: - archs=getoutput('uname -p') + if p is None or p==['UNKNOWN']: + archs=getoutput('uname -p') else: archs=string.join(self.distribution.get_platforms(),',') #else: @@ -381,7 +381,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): break if site: info_file.extend(['BASEDIR="%s"' % site ]) - + return info_file # _make_info_file () @@ -400,11 +400,11 @@ class bdist_pkgtool (bdist_packager.bdist_packager): new_changelog.append(line) else: new_changelog.append(' ' + line) - + # strip trailing newline inserted by first changelog entry if not new_changelog[0]: del new_changelog[0] - + return new_changelog # _format_changelog() -- cgit v1.2.1 From 6bb5291d452462b4170a9a8d3630dcf66aa020d7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 25 Apr 2002 17:03:30 +0000 Subject: Fix trivial typo. --- ccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index f4fc4bcb..f8d13c0e 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -319,7 +319,7 @@ class CCompiler: self.objects = copy (objects) - # -- Priviate utility methods -------------------------------------- + # -- Private utility methods -------------------------------------- # (here for the convenience of subclasses) def _fix_compile_args (self, output_dir, macros, include_dirs): -- cgit v1.2.1 From cad65ba14e067657c1756b023f522c2a0e3ac0af Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 25 Apr 2002 17:26:37 +0000 Subject: Append the PC specific include 'PC' and library 'PCBuild' directories under NT - this allows distutils to work with the CVS version or the source distribution. Wrap a long line. --- command/build_ext.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 91fee5ee..ddbd03e2 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -167,6 +167,11 @@ class build_ext (Command): else: self.build_temp = os.path.join(self.build_temp, "Release") + # Append the source distribution include and library directories, + # this allows distutils on windows to work in the source tree + self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) + self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) + # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory if os.name == 'os2': @@ -177,7 +182,9 @@ class build_ext (Command): if sys.platform[:6] == 'cygwin': if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions - self.library_dirs.append(os.path.join(sys.prefix, "lib", "python" + sys.version[:3], "config")) + self.library_dirs.append(os.path.join(sys.prefix, "lib", + "python" + sys.version[:3], + "config")) else: # building python standard extensions self.library_dirs.append('.') -- cgit v1.2.1 From 33b72ce75455b7b892efa9aa682a7c35e42ea1da Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 25 Apr 2002 17:29:45 +0000 Subject: Pass the full pathname to MSVC when compiling a debug version. This allows the debugger to find the source without asking the user to browse for it. --- msvccompiler.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/msvccompiler.py b/msvccompiler.py index 79a4901b..73cd4425 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -309,6 +309,12 @@ class MSVCCompiler (CCompiler) : else: self.mkpath (os.path.dirname (obj)) + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + if ext in self._c_extensions: input_opt = "/Tc" + src elif ext in self._cpp_extensions: -- cgit v1.2.1 From f40275a1996a3dd768fd1d2f4e96e4f3e1c3dfd6 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 6 May 2002 13:57:19 +0000 Subject: Prevent convert_path from crashing if the path is an empty string. Bugfix candidate. --- util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index a51ce660..d079588a 100644 --- a/util.py +++ b/util.py @@ -84,9 +84,9 @@ def convert_path (pathname): """ if os.sep == '/': return pathname - if pathname[0] == '/': + if pathname and pathname[0] == '/': raise ValueError, "path '%s' cannot be absolute" % pathname - if pathname[-1] == '/': + if pathname and pathname[-1] == '/': raise ValueError, "path '%s' cannot end with '/'" % pathname paths = string.split(pathname, '/') -- cgit v1.2.1 From e5820caf234ff9c9feaf90519f64a6c7800ca5d6 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 30 May 2002 19:15:16 +0000 Subject: Remove unneeded import --- command/install_lib.py | 1 - 1 file changed, 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index 029528e9..03b44ee9 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,7 +6,6 @@ import sys, os, string from types import IntType from distutils.core import Command from distutils.errors import DistutilsOptionError -from distutils.dir_util import copy_tree class install_lib (Command): -- cgit v1.2.1 From 6b571cfed1d5d0e7d955f4f177e9e4dabc631a1b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 4 Jun 2002 15:28:21 +0000 Subject: When using a Python that has not been installed to build 3rd-party modules, distutils does not understand that the build version of the source tree is needed. This patch fixes distutils.sysconfig to understand that the running Python is part of the build tree and needs to use the appropriate "shape" of the tree. This does not assume anything about the current directory, so can be used to build 3rd-party modules using Python's build tree as well. This is useful since it allows us to use a non-installed debug-mode Python with 3rd-party modules for testing. It as the side-effect that set_python_build() is no longer needed (the hack which was added to allow distutils to be used to build the "standard" extension modules). This closes SF patch #547734. --- sysconfig.py | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index d773f149..3e323533 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -23,19 +23,20 @@ from errors import DistutilsPlatformError PREFIX = os.path.normpath(sys.prefix) EXEC_PREFIX = os.path.normpath(sys.exec_prefix) -# Boolean; if it's true, we're still building Python, so -# we use different (hard-wired) directories. - -python_build = 0 - -def set_python_build(): - """Set the python_build flag to true. - - This means that we're building Python itself. Only called from - the setup.py script shipped with Python. - """ - global python_build +# python_build: (Boolean) if true, we're either building Python or +# building an extension with an un-installed Python, so we use +# different (hard-wired) directories. + +argv0_path = os.path.dirname(os.path.abspath(sys.executable)) +landmark = os.path.join(argv0_path, "Modules", "Setup") +if not os.path.isfile(landmark): + python_build = 0 +elif os.path.isfile(os.path.join(argv0_path, "Lib", "os.py")): python_build = 1 +else: + python_build = os.path.isfile(os.path.join(os.path.dirname(argv0_path), + "Lib", "os.py")) +del argv0_path, landmark def get_python_inc(plat_specific=0, prefix=None): @@ -53,7 +54,14 @@ def get_python_inc(plat_specific=0, prefix=None): prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": if python_build: - return "Include/" + base = os.path.dirname(os.path.abspath(sys.executable)) + if plat_specific: + inc_dir = base + else: + inc_dir = os.path.join(base, "Include") + if not os.path.exists(inc_dir): + inc_dir = os.path.join(os.path.dirname(base), "Include") + return inc_dir return os.path.join(prefix, "include", "python" + sys.version[:3]) elif os.name == "nt": return os.path.join(prefix, "include") @@ -163,7 +171,7 @@ def get_config_h_filename(): def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: - return './Makefile' + return os.path.join(os.path.dirname(sys.executable), "Makefile") lib_dir = get_python_lib(plat_specific=1, standard_lib=1) return os.path.join(lib_dir, "config", "Makefile") -- cgit v1.2.1 From bbf5c4f0008d7aa45284910f87a2ee7179ee8f0d Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 18:55:54 +0000 Subject: The comment said: # XXX this isn't used anywhere, and worse, it has the same name as a method # in Command with subtly different semantics. (This one just has one # source -> one dest; that one has many sources -> one dest.) Nuke it? Yes. Nuke it. --- dep_util.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/dep_util.py b/dep_util.py index 9edba4c8..bbb6d235 100644 --- a/dep_util.py +++ b/dep_util.py @@ -93,23 +93,3 @@ def newer_group (sources, target, missing='error'): return 0 # newer_group () - - -# XXX this isn't used anywhere, and worse, it has the same name as a method -# in Command with subtly different semantics. (This one just has one -# source -> one dest; that one has many sources -> one dest.) Nuke it? -def make_file (src, dst, func, args, - verbose=0, update_message=None, noupdate_message=None): - """Makes 'dst' from 'src' (both filenames) by calling 'func' with - 'args', but only if it needs to: i.e. if 'dst' does not exist or 'src' - is newer than 'dst'. - """ - if newer(src, dst): - if verbose and update_message: - print update_message - apply(func, args) - else: - if verbose and noupdate_message: - print noupdate_message - -# make_file () -- cgit v1.2.1 From 6b2531b43f67643c8bf7cc2552159d8f0aaf4081 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:00:26 +0000 Subject: A simple log mechanism styled after the proposed std library module --- log.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 log.py diff --git a/log.py b/log.py new file mode 100644 index 00000000..f0a78650 --- /dev/null +++ b/log.py @@ -0,0 +1,56 @@ +"""A simple log mechanism styled after PEP 282.""" + +# The class here is styled after PEP 282 so that it could later be +# replaced with a standard Python logging implementation. + +DEBUG = 1 +INFO = 2 +WARN = 3 +ERROR = 4 +FATAL = 5 + +class Log: + + def __init__(self, threshold=WARN): + self.threshold = threshold + + def _log(self, level, msg, args): + if level >= self.threshold: + print msg % args + + def log(self, level, msg, *args): + self._log(level, msg, args) + + def debug(self, msg, *args): + self._log(DEBUG, msg, args) + + def info(self, msg, *args): + self._log(INFO, msg, args) + + def warn(self, msg, *args): + self._log(WARN, msg, args) + + def error(self, msg, *args): + self._log(ERROR, msg, args) + + def fatal(self, msg, *args): + self._log(FATAL, msg, args) + +_global_log = Log() +log = _global_log.log +debug = _global_log.debug +info = _global_log.info +warn = _global_log.warn +error = _global_log.error +fatal = _global_log.fatal + +def set_threshold(level): + _global_log.threshold = level + +def set_verbosity(v): + if v == 0: + set_threshold(WARN) + if v == 1: + set_threshold(INFO) + if v == 2: + set_threshold(DEBUG) -- cgit v1.2.1 From fcaddaa4cfd26c0f07bfbb4dbf41760334d5d788 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:14:43 +0000 Subject: Make setup.py less chatty by default. This is a conservative version of SF patch 504889. It uses the log module instead of calling print in various places, and it ignores the verbose argument passed to many functions and set as an attribute on some objects. Instead, it uses the verbosity set on the logger via the command line. The log module is now preferred over announce() and warn() methods that exist only for backwards compatibility. XXX This checkin changes a lot of modules that have no test suite and aren't exercised by the Python build process. It will need substantial testing. --- archive_util.py | 28 +++++++++++----------- bcppcompiler.py | 12 +++++----- ccompiler.py | 19 ++++++++------- cmd.py | 51 ++++++++++++++++++---------------------- command/bdist_dumb.py | 5 ++-- command/bdist_packager.py | 9 +++---- command/bdist_pkgtool.py | 13 ++++++----- command/bdist_rpm.py | 3 ++- command/bdist_sdux.py | 7 +++--- command/bdist_wininst.py | 7 +++--- command/build_clib.py | 5 ++-- command/build_ext.py | 18 +++++++------- command/build_py.py | 17 +++++--------- command/build_scripts.py | 7 +++--- command/clean.py | 15 ++++++------ command/config.py | 14 +++++------ command/install_lib.py | 8 +++---- command/install_scripts.py | 5 ++-- command/sdist.py | 37 +++++++++-------------------- core.py | 6 ++++- cygwinccompiler.py | 3 ++- dir_util.py | 31 +++++++++++-------------- dist.py | 16 +++++-------- emxccompiler.py | 3 ++- fancy_getopt.py | 57 ++++++++++++++++++++++++--------------------- file_util.py | 23 +++++++----------- filelist.py | 58 +++++++++++++++++++--------------------------- msvccompiler.py | 7 +++--- mwerkscompiler.py | 17 ++++++-------- spawn.py | 20 +++++++--------- unixccompiler.py | 7 +++--- util.py | 45 ++++++++++++++--------------------- 32 files changed, 260 insertions(+), 313 deletions(-) diff --git a/archive_util.py b/archive_util.py index 58d9a062..47fac0cb 100644 --- a/archive_util.py +++ b/archive_util.py @@ -11,6 +11,7 @@ import os from distutils.errors import DistutilsExecError from distutils.spawn import spawn from distutils.dir_util import mkpath +from distutils import log def make_tarball (base_name, base_dir, compress="gzip", verbose=0, dry_run=0): @@ -42,13 +43,13 @@ def make_tarball (base_name, base_dir, compress="gzip", "bad value for 'compress': must be None, 'gzip', or 'compress'" archive_name = base_name + ".tar" - mkpath(os.path.dirname(archive_name), verbose=verbose, dry_run=dry_run) + mkpath(os.path.dirname(archive_name), dry_run=dry_run) cmd = ["tar", "-cf", archive_name, base_dir] - spawn(cmd, verbose=verbose, dry_run=dry_run) + spawn(cmd, dry_run=dry_run) if compress: spawn([compress] + compress_flags[compress] + [archive_name], - verbose=verbose, dry_run=dry_run) + dry_run=dry_run) return archive_name + compress_ext[compress] else: return archive_name @@ -69,10 +70,10 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): # no changes needed! zip_filename = base_name + ".zip" - mkpath(os.path.dirname(zip_filename), verbose=verbose, dry_run=dry_run) + mkpath(os.path.dirname(zip_filename), dry_run=dry_run) try: spawn(["zip", "-rq", zip_filename, base_dir], - verbose=verbose, dry_run=dry_run) + dry_run=dry_run) except DistutilsExecError: # XXX really should distinguish between "couldn't find @@ -89,10 +90,10 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): "could neither find a standalone zip utility nor " + "import the 'zipfile' module") % zip_filename - if verbose: - print "creating '%s' and adding '%s' to it" % \ - (zip_filename, base_dir) - + + log.info("creating '%s' and adding '%s' to it", + zip_filename, base_dir) + def visit (z, dirname, names): for name in names: path = os.path.normpath(os.path.join(dirname, name)) @@ -141,8 +142,7 @@ def make_archive (base_name, format, """ save_cwd = os.getcwd() if root_dir is not None: - if verbose: - print "changing into '%s'" % root_dir + log.debug("changing into '%s'", root_dir) base_name = os.path.abspath(base_name) if not dry_run: os.chdir(root_dir) @@ -150,8 +150,7 @@ def make_archive (base_name, format, if base_dir is None: base_dir = os.curdir - kwargs = { 'verbose': verbose, - 'dry_run': dry_run } + kwargs = { 'dry_run': dry_run } try: format_info = ARCHIVE_FORMATS[format] @@ -164,8 +163,7 @@ def make_archive (base_name, format, filename = apply(func, (base_name, base_dir), kwargs) if root_dir is not None: - if verbose: - print "changing back to '%s'" % save_cwd + log.debug("changing back to '%s'", save_cwd) os.chdir(save_cwd) return filename diff --git a/bcppcompiler.py b/bcppcompiler.py index 9ebba2d8..019244cd 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -22,6 +22,7 @@ from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options from distutils.file_util import write_file from distutils.dep_util import newer +from distutils import log class BCPPCompiler(CCompiler) : """Concrete class that implements an interface to the Borland C/C++ @@ -108,7 +109,7 @@ class BCPPCompiler(CCompiler) : ext = (os.path.splitext (src))[1] if skip_sources[src]: - self.announce ("skipping %s (%s up-to-date)" % (src, obj)) + log.debug("skipping %s (%s up-to-date)", src, obj) else: src = os.path.normpath(src) obj = os.path.normpath(obj) @@ -178,7 +179,7 @@ class BCPPCompiler(CCompiler) : except DistutilsExecError, msg: raise LibError, msg else: - self.announce ("skipping %s (up-to-date)" % output_filename) + log.debug("skipping %s (up-to-date)", output_filename) # create_static_lib () @@ -205,8 +206,8 @@ class BCPPCompiler(CCompiler) : self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) if runtime_library_dirs: - self.warn ("I don't know what to do with 'runtime_library_dirs': " - + str (runtime_library_dirs)) + log.warn("I don't know what to do with 'runtime_library_dirs': %s", + str(runtime_library_dirs)) if output_dir is not None: output_filename = os.path.join (output_dir, output_filename) @@ -285,7 +286,6 @@ class BCPPCompiler(CCompiler) : if libfile is None: ld_args.append(lib) # probably a BCPP internal library -- don't warn - # self.warn('library %s not found.' % lib) else: # full name which prefers bcpp_xxx.lib over xxx.lib ld_args.append(libfile) @@ -313,7 +313,7 @@ class BCPPCompiler(CCompiler) : raise LinkError, msg else: - self.announce ("skipping %s (up-to-date)" % output_filename) + log.debug("skipping %s (up-to-date)", output_filename) # link () diff --git a/ccompiler.py b/ccompiler.py index f8d13c0e..4c8b881c 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -16,7 +16,7 @@ from distutils.file_util import move_file from distutils.dir_util import mkpath from distutils.dep_util import newer_pairwise, newer_group from distutils.util import split_quoted, execute - +from distutils import log class CCompiler: """Abstract base class to define the interface that must be implemented @@ -80,7 +80,6 @@ class CCompiler: dry_run=0, force=0): - self.verbose = verbose self.dry_run = dry_run self.force = force @@ -808,8 +807,7 @@ class CCompiler: # -- Utility methods ----------------------------------------------- def announce (self, msg, level=1): - if self.verbose >= level: - print msg + log.debug(msg) def debug_print (self, msg): from distutils.core import DEBUG @@ -820,16 +818,16 @@ class CCompiler: sys.stderr.write ("warning: %s\n" % msg) def execute (self, func, args, msg=None, level=1): - execute(func, args, msg, self.verbose >= level, self.dry_run) + execute(func, args, msg, self.dry_run) def spawn (self, cmd): - spawn (cmd, verbose=self.verbose, dry_run=self.dry_run) + spawn (cmd, dry_run=self.dry_run) def move_file (self, src, dst): - return move_file (src, dst, verbose=self.verbose, dry_run=self.dry_run) + return move_file (src, dst, dry_run=self.dry_run) def mkpath (self, name, mode=0777): - mkpath (name, mode, self.verbose, self.dry_run) + mkpath (name, mode, self.dry_run) # class CCompiler @@ -957,7 +955,10 @@ def new_compiler (plat=None, ("can't compile C/C++ code: unable to find class '%s' " + "in module '%s'") % (class_name, module_name) - return klass (verbose, dry_run, force) + # XXX The None is necessary to preserve backwards compatibility + # with classes that expect verbose to be the first positional + # argument. + return klass (None, dry_run, force) def gen_preprocess_options (macros, include_dirs): diff --git a/cmd.py b/cmd.py index 65060d67..25ff3025 100644 --- a/cmd.py +++ b/cmd.py @@ -13,7 +13,7 @@ import sys, os, string, re from types import * from distutils.errors import * from distutils import util, dir_util, file_util, archive_util, dep_util - +from distutils import log class Command: """Abstract base class for defining command classes, the "worker bees" @@ -72,11 +72,15 @@ class Command: # commands fallback on the Distribution's behaviour. None means # "not defined, check self.distribution's copy", while 0 or 1 mean # false and true (duh). Note that this means figuring out the real - # value of each flag is a touch complicated -- hence "self.verbose" - # (etc.) will be handled by __getattr__, below. - self._verbose = None + # value of each flag is a touch complicated -- hence "self._dry_run" + # will be handled by __getattr__, below. + # XXX This needs to be fixed. self._dry_run = None + # verbose is largely ignored, but needs to be set for + # backwards compatibility (I think)? + self.verbose = dist.verbose + # Some commands define a 'self.force' option to ignore file # timestamps, but methods defined *here* assume that # 'self.force' exists for all commands. So define it here @@ -96,8 +100,10 @@ class Command: # __init__ () + # XXX A more explicit way to customize dry_run would be better. + def __getattr__ (self, attr): - if attr in ('verbose', 'dry_run'): + if attr == 'dry_run': myval = getattr(self, "_" + attr) if myval is None: return getattr(self.distribution, attr) @@ -186,9 +192,7 @@ class Command: """If the current verbosity level is of greater than or equal to 'level' print 'msg' to stdout. """ - if self.verbose >= level: - print msg - sys.stdout.flush() + log.debug(msg) def debug_print (self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the @@ -352,12 +356,11 @@ class Command: def execute (self, func, args, msg=None, level=1): - util.execute(func, args, msg, self.verbose >= level, self.dry_run) + util.execute(func, args, msg, dry_run=self.dry_run) def mkpath (self, name, mode=0777): - dir_util.mkpath(name, mode, - self.verbose, self.dry_run) + dir_util.mkpath(name, mode, dry_run=self.dry_run) def copy_file (self, infile, outfile, @@ -371,8 +374,7 @@ class Command: preserve_mode, preserve_times, not self.force, link, - self.verbose >= level, - self.dry_run) + dry_run=self.dry_run) def copy_tree (self, infile, outfile, @@ -385,30 +387,21 @@ class Command: infile, outfile, preserve_mode,preserve_times,preserve_symlinks, not self.force, - self.verbose >= level, - self.dry_run) - + dry_run=self.dry_run) def move_file (self, src, dst, level=1): - """Move a file respecting verbose and dry-run flags.""" - return file_util.move_file(src, dst, - self.verbose >= level, - self.dry_run) - + """Move a file respectin dry-run flag.""" + return file_util.move_file(src, dst, dry_run = self.dry_run) def spawn (self, cmd, search_path=1, level=1): - """Spawn an external command respecting verbose and dry-run flags.""" + """Spawn an external command respecting dry-run flag.""" from distutils.spawn import spawn - spawn(cmd, search_path, - self.verbose >= level, - self.dry_run) - + spawn(cmd, search_path, dry_run= self.dry_run) def make_archive (self, base_name, format, root_dir=None, base_dir=None): return archive_util.make_archive( - base_name, format, root_dir, base_dir, - self.verbose, self.dry_run) + base_name, format, root_dir, base_dir, dry_run=self.dry_run) def make_file (self, infiles, outfile, func, args, @@ -443,7 +436,7 @@ class Command: # Otherwise, print the "skip" message else: - self.announce(skip_msg, level) + log.debug(skip_msg) # make_file () diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index a135877a..712fec88 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -13,6 +13,7 @@ from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import create_tree, remove_tree from distutils.errors import * +from distutils import log class bdist_dumb (Command): @@ -83,7 +84,7 @@ class bdist_dumb (Command): install.skip_build = self.skip_build install.warn_dir = 0 - self.announce("installing to %s" % self.bdist_dir) + log.info("installing to %s" % self.bdist_dir) self.run_command('install') # And make an archive relative to the root of the @@ -101,7 +102,7 @@ class bdist_dumb (Command): root_dir=self.bdist_dir) if not self.keep_temp: - remove_tree(self.bdist_dir, self.verbose, self.dry_run) + remove_tree(self.bdist_dir, dry_run=self.dry_run) # run() diff --git a/command/bdist_packager.py b/command/bdist_packager.py index 667c0306..11278ee2 100644 --- a/command/bdist_packager.py +++ b/command/bdist_packager.py @@ -14,6 +14,7 @@ from distutils.util import get_platform from distutils.dir_util import create_tree, remove_tree from distutils.file_util import write_file from distutils.errors import * +from distutils import log import string, sys class bdist_packager (Command): @@ -102,8 +103,8 @@ class bdist_packager (Command): else: setattr(self,attr,default) val = default - if val!="": - self.announce('Creating %s script', attr) + if val != "": + log.info('Creating %s script', attr) self.execute(write_file, (path, self.get_script(attr)), "writing '%s'" % path) @@ -234,7 +235,7 @@ class bdist_packager (Command): install = self.reinitialize_command('install', reinit_subcommands=1) install.root = self.pkg_dir - self.announce("installing to %s" % self.pkg_dir) + log.info("installing to %s", self.pkg_dir) self.run_command('install') # And make an archive relative to the root of the @@ -243,7 +244,7 @@ class bdist_packager (Command): self.plat_name) if not self.keep_temp: - remove_tree(self.pkg_dir, self.verbose, self.dry_run) + remove_tree(self.pkg_dir, dry_run=self.dry_run) # run() diff --git a/command/bdist_pkgtool.py b/command/bdist_pkgtool.py index a36638a9..4fd95012 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -15,6 +15,7 @@ from distutils.file_util import write_file from distutils.errors import * from distutils.command import bdist_packager from distutils import sysconfig +from distutils import log import compileall from commands import getoutput @@ -211,9 +212,9 @@ class bdist_pkgtool (bdist_packager.bdist_packager): install = self.reinitialize_command('install', reinit_subcommands=1) # build package - self.announce('Building package') + log.info('Building package') self.run_command('build') - self.announce('Creating pkginfo file') + log.info('Creating pkginfo file') path = os.path.join(pkg_dir, "pkginfo") self.execute(write_file, (path, @@ -244,7 +245,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): self.write_script(os.path.join(pkg_dir, "depend"), 'depend',None) - self.announce('Creating prototype file') + log.info('Creating prototype file') path = os.path.join(pkg_dir, "prototype") self.execute(write_file, (path, @@ -256,7 +257,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): return - self.announce('Creating package') + log.info('Creating package') pkg_cmd = ['pkgmk', '-o', '-f'] pkg_cmd.append(path) pkg_cmd.append('-b') @@ -265,7 +266,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): pkg_cmd = ['pkgtrans', '-s', '/var/spool/pkg'] path = os.path.join(os.environ['PWD'],pkg_dir, self.get_binary_name() + ".pkg") - self.announce('Transferring package to ' + pkg_dir) + log.info('Transferring package to ' + pkg_dir) pkg_cmd.append(path) pkg_cmd.append(self.pkg_abrev) self.spawn(pkg_cmd) @@ -326,7 +327,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): if self.no_autorelocate==0: request=string.split(DEFAULT_REQUEST,"\012") else: - self.announce('Creating relocation request script') + log.info('Creating relocation request script') if self.request: users_request=self.get_script('request') if users_request!=None and users_request!=[]: diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 4bc25613..808ddc14 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -14,6 +14,7 @@ from distutils.core import Command, DEBUG from distutils.util import get_platform from distutils.file_util import write_file from distutils.errors import * +from distutils import log class bdist_rpm (Command): @@ -278,7 +279,7 @@ class bdist_rpm (Command): # build package - self.announce('building RPMs') + log.info("building RPMs") rpm_cmd = ['rpm'] if self.source_only: # what kind of RPMs? rpm_cmd.append('-bs') diff --git a/command/bdist_sdux.py b/command/bdist_sdux.py index 985a37a5..e4765f97 100644 --- a/command/bdist_sdux.py +++ b/command/bdist_sdux.py @@ -14,6 +14,7 @@ from distutils.util import get_platform from distutils.file_util import write_file from distutils.errors import * from distutils.command import bdist_packager +from distutils import log import sys from commands import getoutput @@ -185,9 +186,9 @@ class bdist_sdux(bdist_packager.bdist_packager): psf_path = os.path.join(self.pkg_dir, "%s.psf" % self.get_binary_name()) # build package - self.announce('Building package') + log.info('Building package') self.run_command('build') - self.announce('Creating psf file') + log.info('Creating psf file') self.execute(write_file, (psf_path, self._make_control_file()), @@ -195,7 +196,7 @@ class bdist_sdux(bdist_packager.bdist_packager): if self.control_only: # stop if requested return - self.announce('Creating package') + log.info('Creating package') spawn_cmd = ['swpackage', '-s'] spawn_cmd.append(psf_path) spawn_cmd.append('-x') diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 1683bb31..6a985f19 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -12,6 +12,7 @@ from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import create_tree, remove_tree from distutils.errors import * +from distutils import log class bdist_wininst (Command): @@ -115,7 +116,7 @@ class bdist_wininst (Command): 'install_' + key, value) - self.announce("installing to %s" % self.bdist_dir) + log.info("installing to %s", self.bdist_dir) install.ensure_finalized() # avoid warning of 'install_lib' about installing @@ -136,11 +137,11 @@ class bdist_wininst (Command): # create an exe containing the zip-file self.create_exe(arcname, fullname, self.bitmap) # remove the zip-file again - self.announce("removing temporary file '%s'" % arcname) + log.debug("removing temporary file '%s'", arcname) os.remove(arcname) if not self.keep_temp: - remove_tree(self.bdist_dir, self.verbose, self.dry_run) + remove_tree(self.bdist_dir, dry_run=self.dry_run) # run() diff --git a/command/build_clib.py b/command/build_clib.py index b659147b..f0207e4e 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -24,7 +24,7 @@ from types import * from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler - +from distutils import log def show_compilers (): from distutils.ccompiler import show_compilers @@ -111,7 +111,6 @@ class build_clib (Command): # Yech -- this is cut 'n pasted from build_ext.py! from distutils.ccompiler import new_compiler self.compiler = new_compiler(compiler=self.compiler, - verbose=self.verbose, dry_run=self.dry_run, force=self.force) customize_compiler(self.compiler) @@ -213,7 +212,7 @@ class build_clib (Command): "a list of source filenames") % lib_name sources = list(sources) - self.announce("building '%s' library" % lib_name) + log.info("building '%s' library", lib_name) # First, compile the source code to object files in the library # directory. (This should probably change to putting object diff --git a/command/build_ext.py b/command/build_ext.py index ddbd03e2..89ca1dc9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -15,6 +15,7 @@ from distutils.errors import * from distutils.sysconfig import customize_compiler from distutils.dep_util import newer_group from distutils.extension import Extension +from distutils import log # An extension name is just a dot-separated list of Python NAMEs (ie. # the same as a fully-qualified module name). @@ -291,9 +292,9 @@ class build_ext (Command): # by Extension constructor) (ext_name, build_info) = ext - self.warn(("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s'" - "-- please convert to Extension instance" % ext_name)) + log.warn(("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s'" + "-- please convert to Extension instance" % ext_name)) if type(ext) is not TupleType and len(ext) != 2: raise DistutilsSetupError, \ ("each element of 'ext_modules' option must be an " @@ -329,8 +330,8 @@ class build_ext (Command): # Medium-easy stuff: same syntax/semantics, different names. ext.runtime_library_dirs = build_info.get('rpath') if build_info.has_key('def_file'): - self.warn("'def_file' element of build info dict " - "no longer supported") + log.warn("'def_file' element of build info dict " + "no longer supported") # Non-trivial stuff: 'macros' split into 'define_macros' # and 'undef_macros'. @@ -422,11 +423,10 @@ class build_ext (Command): self.get_ext_filename(fullname)) if not (self.force or newer_group(sources, ext_filename, 'newer')): - self.announce("skipping '%s' extension (up-to-date)" % - ext.name) + log.debug("skipping '%s' extension (up-to-date)", ext.name) return else: - self.announce("building '%s' extension" % ext.name) + log.info("building '%s' extension", ext.name) # First, scan the sources for SWIG definition files (.i), run # SWIG on 'em to create .c files, and modify the sources list @@ -539,7 +539,7 @@ class build_ext (Command): for source in swig_sources: target = swig_targets[source] - self.announce("swigging %s to %s" % (source, target)) + log.info("swigging %s to %s", source, target) self.spawn(swig_cmd + ["-o", target, source]) return new_sources diff --git a/command/build_py.py b/command/build_py.py index 97d094b1..388d3cbc 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -13,7 +13,7 @@ from glob import glob from distutils.core import Command from distutils.errors import * from distutils.util import convert_path - +from distutils import log class build_py (Command): @@ -176,8 +176,8 @@ class build_py (Command): if os.path.isfile(init_py): return init_py else: - self.warn(("package init file '%s' not found " + - "(or not a regular file)") % init_py) + log.warn(("package init file '%s' not found " + + "(or not a regular file)"), init_py) # Either not in a package at all (__init__.py not expected), or # __init__.py doesn't exist -- so don't return the filename. @@ -188,8 +188,7 @@ class build_py (Command): def check_module (self, module, module_file): if not os.path.isfile(module_file): - self.warn("file %s (for module %s) not found" % - (module_file, module)) + log.warn("file %s (for module %s) not found", module_file, module) return 0 else: return 1 @@ -389,13 +388,9 @@ class build_py (Command): if self.compile: byte_compile(files, optimize=0, - force=self.force, - prefix=prefix, - verbose=self.verbose, dry_run=self.dry_run) + force=self.force, prefix=prefix, dry_run=self.dry_run) if self.optimize > 0: byte_compile(files, optimize=self.optimize, - force=self.force, - prefix=prefix, - verbose=self.verbose, dry_run=self.dry_run) + force=self.force, prefix=prefix, dry_run=self.dry_run) # class build_py diff --git a/command/build_scripts.py b/command/build_scripts.py index 444284f7..211ade40 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -11,6 +11,7 @@ from distutils import sysconfig from distutils.core import Command from distutils.dep_util import newer from distutils.util import convert_path +from distutils import log # check if Python is called on the first line with this expression first_line_re = re.compile(r'^#!.*python[0-9.]*(\s+.*)?$') @@ -59,7 +60,7 @@ class build_scripts (Command): outfile = os.path.join(self.build_dir, os.path.basename(script)) if not self.force and not newer(script, outfile): - self.announce("not copying %s (up-to-date)" % script) + log.debug("not copying %s (up-to-date)", script) continue # Always open the file, but ignore failures in dry-run mode -- @@ -83,8 +84,8 @@ class build_scripts (Command): post_interp = match.group(1) or '' if adjust: - self.announce("copying and adjusting %s -> %s" % - (script, self.build_dir)) + log.info("copying and adjusting %s -> %s", script, + self.build_dir) if not self.dry_run: outf = open(outfile, "w") if not sysconfig.python_build: diff --git a/command/clean.py b/command/clean.py index b4a9be45..8fddeb45 100644 --- a/command/clean.py +++ b/command/clean.py @@ -9,6 +9,7 @@ __revision__ = "$Id$" import os from distutils.core import Command from distutils.dir_util import remove_tree +from distutils import log class clean (Command): @@ -51,10 +52,10 @@ class clean (Command): # remove the build/temp. directory (unless it's already # gone) if os.path.exists(self.build_temp): - remove_tree(self.build_temp, self.verbose, self.dry_run) + remove_tree(self.build_temp, dry_run=self.dry_run) else: - self.warn("'%s' does not exist -- can't clean it" % - self.build_temp) + log.warn("'%s' does not exist -- can't clean it", + self.build_temp) if self.all: # remove build directories @@ -62,17 +63,17 @@ class clean (Command): self.bdist_base, self.build_scripts): if os.path.exists(directory): - remove_tree(directory, self.verbose, self.dry_run) + remove_tree(directory, dry_run=self.dry_run) else: - self.warn("'%s' does not exist -- can't clean it" % - directory) + log.warn("'%s' does not exist -- can't clean it", + directory) # just for the heck of it, try to remove the base build directory: # we might have emptied it right now, but if not we don't care if not self.dry_run: try: os.rmdir(self.build_base) - self.announce("removing '%s'" % self.build_base) + log.info("removing '%s'", self.build_base) except OSError: pass diff --git a/command/config.py b/command/config.py index 27c2cc15..d74aa6a2 100644 --- a/command/config.py +++ b/command/config.py @@ -17,7 +17,7 @@ import sys, os, string, re from types import * from distutils.core import Command from distutils.errors import DistutilsExecError - +from distutils import log LANG_EXT = {'c': '.c', 'c++': '.cxx'} @@ -103,9 +103,7 @@ class config (Command): from distutils.ccompiler import CCompiler, new_compiler if not isinstance(self.compiler, CCompiler): self.compiler = new_compiler(compiler=self.compiler, - verbose=self.noisy, - dry_run=self.dry_run, - force=1) + dry_run=self.dry_run, force=1) if self.include_dirs: self.compiler.set_include_dirs(self.include_dirs) if self.libraries: @@ -161,7 +159,7 @@ class config (Command): if not filenames: filenames = self.temp_files self.temp_files = [] - self.announce("removing: " + string.join(filenames)) + log.info("removing: %s", string.join(filenames)) for filename in filenames: try: os.remove(filename) @@ -239,7 +237,7 @@ class config (Command): except CompileError: ok = 0 - self.announce(ok and "success!" or "failure.") + log.info(ok and "success!" or "failure.") self._clean() return ok @@ -260,7 +258,7 @@ class config (Command): except (CompileError, LinkError): ok = 0 - self.announce(ok and "success!" or "failure.") + log.info(ok and "success!" or "failure.") self._clean() return ok @@ -282,7 +280,7 @@ class config (Command): except (CompileError, LinkError, DistutilsExecError): ok = 0 - self.announce(ok and "success!" or "failure.") + log.info(ok and "success!" or "failure.") self._clean() return ok diff --git a/command/install_lib.py b/command/install_lib.py index 03b44ee9..1e771c61 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -124,13 +124,11 @@ class install_lib (Command): if self.compile: byte_compile(files, optimize=0, - force=self.force, - prefix=install_root, - verbose=self.verbose, dry_run=self.dry_run) + force=self.force, prefix=install_root, + dry_run=self.dry_run) if self.optimize > 0: byte_compile(files, optimize=self.optimize, - force=self.force, - prefix=install_root, + force=self.force, prefix=install_root, verbose=self.verbose, dry_run=self.dry_run) diff --git a/command/install_scripts.py b/command/install_scripts.py index d4cbaa3a..4044ba09 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -9,6 +9,7 @@ __revision__ = "$Id$" import os from distutils.core import Command +from distutils import log from stat import ST_MODE class install_scripts (Command): @@ -48,10 +49,10 @@ class install_scripts (Command): # all the scripts we just installed. for file in self.get_outputs(): if self.dry_run: - self.announce("changing mode of %s" % file) + log.info("changing mode of %s to %o", file, mode) else: mode = ((os.stat(file)[ST_MODE]) | 0111) & 07777 - self.announce("changing mode of %s to %o" % (file, mode)) + log.info("changing mode of %s to %o", file, mode) os.chmod(file, mode) def get_inputs (self): diff --git a/command/sdist.py b/command/sdist.py index fbd3c6d2..082aa88c 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -14,6 +14,7 @@ from distutils import dir_util, dep_util, file_util, archive_util from distutils.text_file import TextFile from distutils.errors import * from distutils.filelist import FileList +from distutils import log def show_formats (): @@ -233,31 +234,17 @@ class sdist (Command): self.warn(("manifest template '%s' does not exist " + "(using default file list)") % self.template) - self.filelist.findall() - # Add default file set to 'files' if self.use_defaults: self.add_defaults() - - # Read manifest template if it exists if template_exists: self.read_template() - - # Prune away any directories that don't belong in the source - # distribution if self.prune: self.prune_file_list() - # File list now complete -- sort it so that higher-level files - # come first self.filelist.sort() - - # Remove duplicates from the file list self.filelist.remove_duplicates() - - # And write complete file list (including default file set) to - # the manifest. self.write_manifest() # Don't regenerate the manifest, just read it in. @@ -321,13 +308,12 @@ class sdist (Command): def read_template (self): + """Read and parse manifest template file named by self.template. - """Read and parse the manifest template file named by - 'self.template' (usually "MANIFEST.in"). The parsing and - processing is done by 'self.filelist', which updates itself - accordingly. + (usually "MANIFEST.in") The parsing and processing is done by + 'self.filelist', which updates itself accordingly. """ - self.announce("reading manifest template '%s'" % self.template) + log.info("reading manifest template '%s'", self.template) template = TextFile(self.template, strip_comments=1, skip_blanks=1, @@ -384,7 +370,7 @@ class sdist (Command): fill in 'self.filelist', the list of files to include in the source distribution. """ - self.announce("reading manifest file '%s'" % self.manifest) + log.info("reading manifest file '%s'", self.manifest) manifest = open(self.manifest) while 1: line = manifest.readline() @@ -410,8 +396,7 @@ class sdist (Command): # put 'files' there; the 'mkpath()' is just so we don't die # if the manifest happens to be empty. self.mkpath(base_dir) - dir_util.create_tree(base_dir, files, - verbose=self.verbose, dry_run=self.dry_run) + dir_util.create_tree(base_dir, files, dry_run=self.dry_run) # And walk over the list of files, either making a hard link (if # os.link exists) to each one that doesn't already exist in its @@ -428,12 +413,12 @@ class sdist (Command): msg = "copying files to %s..." % base_dir if not files: - self.warn("no files to distribute -- empty manifest?") + log.warn("no files to distribute -- empty manifest?") else: - self.announce(msg) + log.info(msg) for file in files: if not os.path.isfile(file): - self.warn("'%s' not a regular file -- skipping" % file) + log.warn("'%s' not a regular file -- skipping" % file) else: dest = os.path.join(base_dir, file) self.copy_file(file, dest, link=link) @@ -464,7 +449,7 @@ class sdist (Command): self.archive_files = archive_files if not self.keep_temp: - dir_util.remove_tree(base_dir, self.verbose, self.dry_run) + dir_util.remove_tree(base_dir, dry_run=self.dry_run) def get_archive_files (self): """Return the list of archive files created when the command diff --git a/core.py b/core.py index 97a741c8..222e6aeb 100644 --- a/core.py +++ b/core.py @@ -100,7 +100,11 @@ def setup (**attrs): try: _setup_distribution = dist = klass(attrs) except DistutilsSetupError, msg: - raise SystemExit, "error in setup script: %s" % msg + if attrs.has_key('name'): + raise SystemExit, "error in %s setup command: %s" % \ + (attrs['name'], msg) + else: + raise SystemExit, "error in setup command: %s" % msg if _setup_stop_after == "init": return dist diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 1d972823..3fb5bc90 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -50,6 +50,7 @@ from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError +from distutils import log class CygwinCCompiler (UnixCCompiler): @@ -148,7 +149,7 @@ class CygwinCCompiler (UnixCCompiler): src = sources[i] ; obj = objects[i] ext = (os.path.splitext (src))[1] if skip_sources[src]: - self.announce ("skipping %s (%s up-to-date)" % (src, obj)) + log.debug("skipping %s (%s up-to-date)", src, obj) else: self.mkpath (os.path.dirname (obj)) if ext == '.rc' or ext == '.res': diff --git a/dir_util.py b/dir_util.py index 77007c97..8b3e06b2 100644 --- a/dir_util.py +++ b/dir_util.py @@ -9,7 +9,7 @@ __revision__ = "$Id$" import os from types import * from distutils.errors import DistutilsFileError, DistutilsInternalError - +from distutils import log # cache for by mkpath() -- in addition to cheapening redundant calls, # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode @@ -69,8 +69,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): if _path_created.get(abs_head): continue - if verbose: - print "creating", head + log.info("creating %s", head) if not dry_run: try: @@ -105,7 +104,7 @@ def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0): # Now create them for dir in need_dirs: - mkpath(dir, mode, verbose, dry_run) + mkpath(dir, mode, dry_run=dry_run) # create_tree () @@ -151,7 +150,7 @@ def copy_tree (src, dst, "error listing files in '%s': %s" % (src, errstr) if not dry_run: - mkpath(dst, verbose=verbose) + mkpath(dst) outputs = [] @@ -161,21 +160,19 @@ def copy_tree (src, dst, if preserve_symlinks and os.path.islink(src_name): link_dest = os.readlink(src_name) - if verbose: - print "linking %s -> %s" % (dst_name, link_dest) + log.info("linking %s -> %s", dst_name, link_dest) if not dry_run: os.symlink(link_dest, dst_name) outputs.append(dst_name) elif os.path.isdir(src_name): outputs.extend( - copy_tree(src_name, dst_name, - preserve_mode, preserve_times, preserve_symlinks, - update, verbose, dry_run)) + copy_tree(src_name, dst_name, preserve_mode, + preserve_times, preserve_symlinks, update, + dry_run=dry_run)) else: - copy_file(src_name, dst_name, - preserve_mode, preserve_times, - update, None, verbose, dry_run) + copy_file(src_name, dst_name, preserve_mode, + preserve_times, update, dry_run=dry_run) outputs.append(dst_name) return outputs @@ -200,8 +197,7 @@ def remove_tree (directory, verbose=0, dry_run=0): from distutils.util import grok_environment_error global _path_created - if verbose: - print "removing '%s' (and everything under it)" % directory + log.info("removing '%s' (and everything under it)", directory) if dry_run: return cmdtuples = [] @@ -214,6 +210,5 @@ def remove_tree (directory, verbose=0, dry_run=0): if _path_created.has_key(abspath): del _path_created[abspath] except (IOError, OSError), exc: - if verbose: - print grok_environment_error( - exc, "error removing %s: " % directory) + log.warn(grok_environment_error( + exc, "error removing %s: " % directory)) diff --git a/dist.py b/dist.py index b648f24e..a84004f4 100644 --- a/dist.py +++ b/dist.py @@ -15,7 +15,7 @@ from copy import copy from distutils.errors import * from distutils.fancy_getopt import FancyGetopt, translate_longopt from distutils.util import check_environ, strtobool, rfc822_escape - +from distutils import log # Regex to define acceptable Distutils command names. This is not *quite* # the same as a Python NAME -- I don't allow leading underscores. The fact @@ -46,7 +46,8 @@ class Distribution: # since every global option is also valid as a command option -- and we # don't want to pollute the commands with too many options that they # have minimal control over. - global_options = [('verbose', 'v', "run verbosely (default)"), + # The fourth entry for verbose means that it can be repeated. + global_options = [('verbose', 'v', "run verbosely (default)", 1), ('quiet', 'q', "run quietly (turns verbosity off)"), ('dry-run', 'n', "don't actually do anything"), ('help', 'h', "show detailed help message"), @@ -392,6 +393,7 @@ class Distribution: parser.set_aliases({'licence': 'license'}) args = parser.getopt(args=self.script_args, object=self) option_order = parser.get_option_order() + log.set_verbosity(self.verbose) # for display options we return immediately if self.handle_display_options(option_order): @@ -876,13 +878,7 @@ class Distribution: # -- Methods that operate on the Distribution ---------------------- def announce (self, msg, level=1): - """Print 'msg' if 'level' is greater than or equal to the verbosity - level recorded in the 'verbose' attribute (which, currently, can be - only 0 or 1). - """ - if self.verbose >= level: - print msg - + log.debug(msg) def run_commands (self): """Run each command that was seen on the setup script command line. @@ -907,7 +903,7 @@ class Distribution: if self.have_run.get(command): return - self.announce("running " + command) + log.info("running %s", command) cmd_obj = self.get_command_obj(command) cmd_obj.ensure_finalized() cmd_obj.run() diff --git a/emxccompiler.py b/emxccompiler.py index 58a0d812..2788209c 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -28,6 +28,7 @@ from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError +from distutils import log class EMXCCompiler (UnixCCompiler): @@ -109,7 +110,7 @@ class EMXCCompiler (UnixCCompiler): src = sources[i] ; obj = objects[i] ext = (os.path.splitext (src))[1] if skip_sources[src]: - self.announce ("skipping %s (%s up-to-date)" % (src, obj)) + log.debug("skipping %s (%s up-to-date)", src, obj) else: self.mkpath (os.path.dirname (obj)) if ext == '.rc': diff --git a/fancy_getopt.py b/fancy_getopt.py index e65302fc..fe9b0d4d 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -157,13 +157,18 @@ class FancyGetopt: self.long_opts = [] self.short_opts = [] self.short2long.clear() + self.repeat = {} for option in self.option_table: - try: - (long, short, help) = option - except ValueError: - raise DistutilsGetoptError, \ - "invalid option tuple " + str(option) + if len(option) == 3: + long, short, help = option + repeat = 0 + elif len(option) == 4: + long, short, help, repeat = option + else: + # the option table is part of the code, so simply + # assert that it is correct + assert "invalid option tuple: %s" % `option` # Type- and value-check the option names if type(long) is not StringType or len(long) < 2: @@ -177,6 +182,7 @@ class FancyGetopt: ("invalid short option '%s': " "must a single character or None") % short + self.repeat[long] = 1 self.long_opts.append(long) if long[-1] == '=': # option takes an argument? @@ -232,14 +238,15 @@ class FancyGetopt: def getopt (self, args=None, object=None): - """Parse the command-line options in 'args' and store the results - as attributes of 'object'. If 'args' is None or not supplied, uses - 'sys.argv[1:]'. If 'object' is None or not supplied, creates a new - OptionDummy object, stores option values there, and returns a tuple - (args, object). If 'object' is supplied, it is modified in place - and 'getopt()' just returns 'args'; in both cases, the returned - 'args' is a modified copy of the passed-in 'args' list, which is - left untouched. + """Parse command-line options in args. Store as attributes on object. + + If 'args' is None or not supplied, uses 'sys.argv[1:]'. If + 'object' is None or not supplied, creates a new OptionDummy + object, stores option values there, and returns a tuple (args, + object). If 'object' is supplied, it is modified in place and + 'getopt()' just returns 'args'; in both cases, the returned + 'args' is a modified copy of the passed-in 'args' list, which + is left untouched. """ if args is None: args = sys.argv[1:] @@ -253,30 +260,23 @@ class FancyGetopt: short_opts = string.join(self.short_opts) try: - (opts, args) = getopt.getopt(args, short_opts, self.long_opts) + opts, args = getopt.getopt(args, short_opts, self.long_opts) except getopt.error, msg: raise DistutilsArgError, msg - for (opt, val) in opts: + for opt, val in opts: if len(opt) == 2 and opt[0] == '-': # it's a short option opt = self.short2long[opt[1]] - - elif len(opt) > 2 and opt[0:2] == '--': - opt = opt[2:] - else: - raise DistutilsInternalError, \ - "this can't happen: bad option string '%s'" % opt + assert len(opt) > 2 and opt[:2] == '--' + opt = opt[2:] alias = self.alias.get(opt) if alias: opt = alias if not self.takes_arg[opt]: # boolean option? - if val != '': # shouldn't have a value! - raise DistutilsInternalError, \ - "this can't happen: bad option value '%s'" % val - + assert val == '', "boolean option can't have value" alias = self.negative_alias.get(opt) if alias: opt = alias @@ -285,13 +285,16 @@ class FancyGetopt: val = 1 attr = self.attr_name[opt] + # The only repeating option at the moment is 'verbose'. + # It has a negative option -q quiet, which should set verbose = 0. + if val and self.repeat.get(attr) is not None: + val = getattr(object, attr, 0) + 1 setattr(object, attr, val) self.option_order.append((opt, val)) # for opts - if created_object: - return (args, object) + return args, object else: return args diff --git a/file_util.py b/file_util.py index 14772fb7..56b1faee 100644 --- a/file_util.py +++ b/file_util.py @@ -9,7 +9,7 @@ __revision__ = "$Id$" import os from distutils.errors import DistutilsFileError - +from distutils import log # for generating verbose output in 'copy_file()' _copy_action = { None: 'copying', @@ -73,7 +73,6 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): # _copy_file_contents() - def copy_file (src, dst, preserve_mode=1, preserve_times=1, @@ -90,8 +89,7 @@ def copy_file (src, dst, 'preserve_times' is true (the default), the last-modified and last-access times are copied as well. If 'update' is true, 'src' will only be copied if 'dst' does not exist, or if 'dst' does exist but is - older than 'src'. If 'verbose' is true, then a one-line summary of the - copy will be printed to stdout. + older than 'src'. 'link' allows you to make hard links (os.link) or symbolic links (os.symlink) instead of copying: set it to "hard" or "sym"; if it is @@ -127,20 +125,18 @@ def copy_file (src, dst, dir = os.path.dirname(dst) if update and not newer(src, dst): - if verbose: - print "not copying %s (output up-to-date)" % src - return (dst, 0) + log.debug("not copying %s (output up-to-date)", src) + return dst, 0 try: action = _copy_action[link] except KeyError: raise ValueError, \ "invalid value '%s' for 'link' argument" % link - if verbose: - if os.path.basename(dst) == os.path.basename(src): - print "%s %s -> %s" % (action, src, dir) - else: - print "%s %s -> %s" % (action, src, dst) + if os.path.basename(dst) == os.path.basename(src): + log.info("%s %s -> %s", action, src, dir) + else: + log.info("%s %s -> %s", action, src, dst) if dry_run: return (dst, 1) @@ -197,8 +193,7 @@ def move_file (src, dst, from os.path import exists, isfile, isdir, basename, dirname import errno - if verbose: - print "moving %s -> %s" % (src, dst) + log.info("moving %s -> %s", src, dst) if dry_run: return dst diff --git a/filelist.py b/filelist.py index f7222fd9..d39c8358 100644 --- a/filelist.py +++ b/filelist.py @@ -37,27 +37,19 @@ class FileList: def __init__(self, warn=None, debug_print=None): - # use standard warning and debug functions if no other given - self.warn = warn or self.__warn - self.debug_print = debug_print or self.__debug_print + # ignore argument to FileList, but keep them for backwards + # compatibility self.allfiles = None self.files = [] - def set_allfiles (self, allfiles): self.allfiles = allfiles def findall (self, dir=os.curdir): self.allfiles = findall(dir) - - # -- Fallback warning/debug functions ------------------------------ - - def __warn (self, msg): - sys.stderr.write("warning: %s\n" % msg) - - def __debug_print (self, msg): + def debug_print (self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the DISTUTILS_DEBUG environment variable) flag is true. """ @@ -65,7 +57,6 @@ class FileList: if DEBUG: print msg - # -- List-like methods --------------------------------------------- def append (self, item): @@ -87,8 +78,8 @@ class FileList: def remove_duplicates (self): # Assumes list has been sorted! - for i in range(len(self.files)-1, 0, -1): - if self.files[i] == self.files[i-1]: + for i in range(len(self.files) - 1, 0, -1): + if self.files[i] == self.files[i - 1]: del self.files[i] @@ -147,61 +138,60 @@ class FileList: self.debug_print("include " + string.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=1): - self.warn("no files found matching '%s'" % pattern) + log.warn("warning: no files found matching '%s'", + pattern) elif action == 'exclude': self.debug_print("exclude " + string.join(patterns)) for pattern in patterns: if not self.exclude_pattern(pattern, anchor=1): - self.warn( - "no previously-included files found matching '%s'"% - pattern) + log.warn(("warning: no previously-included files " + "found matching '%s'"), pattern) elif action == 'global-include': self.debug_print("global-include " + string.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=0): - self.warn(("no files found matching '%s' " + - "anywhere in distribution") % - pattern) + log.warn(("warning: no files found matching '%s' " + + "anywhere in distribution"), pattern) elif action == 'global-exclude': self.debug_print("global-exclude " + string.join(patterns)) for pattern in patterns: if not self.exclude_pattern(pattern, anchor=0): - self.warn(("no previously-included files matching '%s' " + - "found anywhere in distribution") % - pattern) + log.warn(("warning: no previously-included files matching " + "'%s' found anywhere in distribution"), + pattern) elif action == 'recursive-include': self.debug_print("recursive-include %s %s" % (dir, string.join(patterns))) for pattern in patterns: if not self.include_pattern(pattern, prefix=dir): - self.warn(("no files found matching '%s' " + - "under directory '%s'") % - (pattern, dir)) + log.warn(("warngin: no files found matching '%s' " + + "under directory '%s'"), + pattern, dir) elif action == 'recursive-exclude': self.debug_print("recursive-exclude %s %s" % (dir, string.join(patterns))) for pattern in patterns: if not self.exclude_pattern(pattern, prefix=dir): - self.warn(("no previously-included files matching '%s' " + - "found under directory '%s'") % - (pattern, dir)) + log.warn(("warning: no previously-included files matching " + "'%s' found under directory '%s'"), + pattern, dir) elif action == 'graft': self.debug_print("graft " + dir_pattern) if not self.include_pattern(None, prefix=dir_pattern): - self.warn("no directories found matching '%s'" % dir_pattern) + log.warn("warning: no directories found matching '%s'", + dir_pattern) elif action == 'prune': self.debug_print("prune " + dir_pattern) if not self.exclude_pattern(None, prefix=dir_pattern): - self.warn(("no previously-included directories found " + - "matching '%s'") % - dir_pattern) + log.warn(("no previously-included directories found " + + "matching '%s'"), dir_pattern) else: raise DistutilsInternalError, \ "this cannot happen: invalid action '%s'" % action diff --git a/msvccompiler.py b/msvccompiler.py index 73cd4425..ade8172d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -17,6 +17,7 @@ from distutils.errors import \ CompileError, LibError, LinkError from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options +from distutils import log _can_read_reg = 0 try: @@ -305,7 +306,7 @@ class MSVCCompiler (CCompiler) : ext = (os.path.splitext (src))[1] if skip_sources[src]: - self.announce ("skipping %s (%s up-to-date)" % (src, obj)) + log.debug("skipping %s (%s up-to-date)", src, obj) else: self.mkpath (os.path.dirname (obj)) @@ -403,7 +404,7 @@ class MSVCCompiler (CCompiler) : raise LibError, msg else: - self.announce ("skipping %s (up-to-date)" % output_filename) + log.debug("skipping %s (up-to-date)", output_filename) # create_static_lib () @@ -480,7 +481,7 @@ class MSVCCompiler (CCompiler) : raise LinkError, msg else: - self.announce ("skipping %s (up-to-date)" % output_filename) + log.debug("skipping %s (up-to-date)", output_filename) # link () diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 7c77b8bc..6242f12a 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -13,6 +13,7 @@ from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options import distutils.util import distutils.dir_util +from distutils import log import mkcwproject class MWerksCompiler (CCompiler) : @@ -132,8 +133,8 @@ class MWerksCompiler (CCompiler) : exportname = basename + '.mcp.exp' prefixname = 'mwerks_%s_config.h'%basename # Create the directories we need - distutils.dir_util.mkpath(build_temp, self.verbose, self.dry_run) - distutils.dir_util.mkpath(output_dir, self.verbose, self.dry_run) + distutils.dir_util.mkpath(build_temp, dry_run=self.dry_run) + distutils.dir_util.mkpath(output_dir, dry_run=self.dry_run) # And on to filling in the parameters for the project builder settings = {} settings['mac_exportname'] = exportname @@ -159,8 +160,7 @@ class MWerksCompiler (CCompiler) : return # Build the export file exportfilename = os.path.join(build_temp, exportname) - if self.verbose: - print '\tCreate export file', exportfilename + log.debug("\tCreate export file", exportfilename) fp = open(exportfilename, 'w') fp.write('%s\n'%export_symbols[0]) fp.close() @@ -181,8 +181,7 @@ class MWerksCompiler (CCompiler) : # because we pass this pathname to CodeWarrior in an AppleEvent, and CW # doesn't have a clue about our working directory. xmlfilename = os.path.join(os.getcwd(), os.path.join(build_temp, xmlname)) - if self.verbose: - print '\tCreate XML file', xmlfilename + log.debug("\tCreate XML file", xmlfilename) xmlbuilder = mkcwproject.cwxmlgen.ProjectBuilder(settings) xmlbuilder.generate() xmldata = settings['tmp_projectxmldata'] @@ -191,12 +190,10 @@ class MWerksCompiler (CCompiler) : fp.close() # Generate the project. Again a full pathname. projectfilename = os.path.join(os.getcwd(), os.path.join(build_temp, projectname)) - if self.verbose: - print '\tCreate project file', projectfilename + log.debug('\tCreate project file', projectfilename) mkcwproject.makeproject(xmlfilename, projectfilename) # And build it - if self.verbose: - print '\tBuild project' + log.debug('\tBuild project') mkcwproject.buildproject(projectfilename) def _filename_to_abs(self, filename): diff --git a/spawn.py b/spawn.py index 5b6016e0..4df6e097 100644 --- a/spawn.py +++ b/spawn.py @@ -12,7 +12,7 @@ __revision__ = "$Id$" import sys, os, string from distutils.errors import * - +from distutils import log def spawn (cmd, search_path=1, @@ -27,19 +27,18 @@ def spawn (cmd, If 'search_path' is true (the default), the system's executable search path will be used to find the program; otherwise, cmd[0] must be the - exact path to the executable. If 'verbose' is true, a one-line summary - of the command will be printed before it is run. If 'dry_run' is true, + exact path to the executable.If 'dry_run' is true, the command will not actually be run. Raise DistutilsExecError if running the program fails in any way; just return on success. """ if os.name == 'posix': - _spawn_posix(cmd, search_path, verbose, dry_run) + _spawn_posix(cmd, search_path, dry_run=dry_run) elif os.name == 'nt': - _spawn_nt(cmd, search_path, verbose, dry_run) + _spawn_nt(cmd, search_path, dry_run=dry_run) elif os.name == 'os2': - _spawn_os2(cmd, search_path, verbose, dry_run) + _spawn_os2(cmd, search_path, dry_run=dry_run) else: raise DistutilsPlatformError, \ "don't know how to spawn programs on platform '%s'" % os.name @@ -74,8 +73,7 @@ def _spawn_nt (cmd, if search_path: # either we find one or it stays the same executable = find_executable(executable) or executable - if verbose: - print string.join([executable] + cmd[1:], ' ') + log.info(string.join([executable] + cmd[1:], ' ')) if not dry_run: # spawn for NT requires a full path to the .exe try: @@ -100,8 +98,7 @@ def _spawn_os2 (cmd, if search_path: # either we find one or it stays the same executable = find_executable(executable) or executable - if verbose: - print string.join([executable] + cmd[1:], ' ') + log.info(string.join([executable] + cmd[1:], ' ')) if not dry_run: # spawnv for OS/2 EMX requires a full path to the .exe try: @@ -122,8 +119,7 @@ def _spawn_posix (cmd, verbose=0, dry_run=0): - if verbose: - print string.join(cmd, ' ') + log.info(string.join(cmd, ' ')) if dry_run: return exec_fn = search_path and os.execvp or os.execv diff --git a/unixccompiler.py b/unixccompiler.py index 7e63c56a..55a51b32 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -26,6 +26,7 @@ from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options from distutils.errors import \ DistutilsExecError, CompileError, LibError, LinkError +from distutils import log # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's @@ -147,7 +148,7 @@ class UnixCCompiler (CCompiler): for i in range(len(sources)): src = sources[i] ; obj = objects[i] if skip_sources[src]: - self.announce("skipping %s (%s up-to-date)" % (src, obj)) + log.debug("skipping %s (%s up-to-date)", src, obj) else: self.mkpath(os.path.dirname(obj)) try: @@ -191,7 +192,7 @@ class UnixCCompiler (CCompiler): except DistutilsExecError, msg: raise LibError, msg else: - self.announce("skipping %s (up-to-date)" % output_filename) + log.debug("skipping %s (up-to-date)", output_filename) # create_static_lib () @@ -240,7 +241,7 @@ class UnixCCompiler (CCompiler): except DistutilsExecError, msg: raise LinkError, msg else: - self.announce("skipping %s (up-to-date)" % output_filename) + log.debug("skipping %s (up-to-date)", output_filename) # link () diff --git a/util.py b/util.py index d079588a..23c29ebb 100644 --- a/util.py +++ b/util.py @@ -12,7 +12,7 @@ import sys, os, string, re from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer from distutils.spawn import spawn - +from distutils import log def get_platform (): """Return a string that identifies the current platform. This is used @@ -275,33 +275,27 @@ def split_quoted (s): def execute (func, args, msg=None, verbose=0, dry_run=0): - """Perform some action that affects the outside world (eg. by writing - to the filesystem). Such actions are special because they are disabled - by the 'dry_run' flag, and announce themselves if 'verbose' is true. - This method takes care of all that bureaucracy for you; all you have to - do is supply the function to call and an argument tuple for it (to - embody the "external action" being performed), and an optional message - to print. + """Perform some action that affects the outside world (eg. by + writing to the filesystem). Such actions are special because they + are disabled by the 'dry_run' flag. This method takes care of all + that bureaucracy for you; all you have to do is supply the + function to call and an argument tuple for it (to embody the + "external action" being performed), and an optional message to + print. """ - # Generate a message if we weren't passed one if msg is None: msg = "%s%s" % (func.__name__, `args`) if msg[-2:] == ',)': # correct for singleton tuple msg = msg[0:-2] + ')' - # Print it if verbosity level is high enough - if verbose: - print msg - - # And do it, as long as we're not in dry-run mode + log.info(msg) if not dry_run: apply(func, args) -# execute() - def strtobool (val): """Convert a string representation of truth to true (1) or false (0). + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 'val' is anything else. @@ -337,8 +331,8 @@ def byte_compile (py_files, prepended (after 'prefix' is stripped). You can supply either or both (or neither) of 'prefix' and 'base_dir', as you wish. - If 'verbose' is true, prints out a report of each file. If 'dry_run' - is true, doesn't actually do anything that would affect the filesystem. + If 'dry_run' is true, doesn't actually do anything that would + affect the filesystem. Byte-compilation is either done directly in this interpreter process with the standard py_compile module, or indirectly by writing a @@ -367,8 +361,7 @@ def byte_compile (py_files, if not direct: from tempfile import mktemp script_name = mktemp(".py") - if verbose: - print "writing byte-compilation script '%s'" % script_name + log.info("writing byte-compilation script '%s'", script_name) if not dry_run: script = open(script_name, "w") @@ -406,9 +399,9 @@ byte_compile(files, optimize=%s, force=%s, cmd.insert(1, "-O") elif optimize == 2: cmd.insert(1, "-OO") - spawn(cmd, verbose=verbose, dry_run=dry_run) + spawn(cmd, dry_run=dry_run) execute(os.remove, (script_name,), "removing %s" % script_name, - verbose=verbose, dry_run=dry_run) + dry_run=dry_run) # "Direct" byte-compilation: use the py_compile module to compile # right here, right now. Note that the script generated in indirect @@ -440,14 +433,12 @@ byte_compile(files, optimize=%s, force=%s, cfile_base = os.path.basename(cfile) if direct: if force or newer(file, cfile): - if verbose: - print "byte-compiling %s to %s" % (file, cfile_base) + log.info("byte-compiling %s to %s", file, cfile_base) if not dry_run: compile(file, cfile, dfile) else: - if verbose: - print "skipping byte-compilation of %s to %s" % \ - (file, cfile_base) + log.debug("skipping byte-compilation of %s to %s", + file, cfile_base) # byte_compile () -- cgit v1.2.1 From 86d1bd16c0053ee46f0e7d529dec144b611de3ed Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:18:24 +0000 Subject: Remove unused imports caught by pychecker --- command/bdist_pkgtool.py | 2 -- command/bdist_sdux.py | 1 - filelist.py | 2 +- unixccompiler.py | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/command/bdist_pkgtool.py b/command/bdist_pkgtool.py index 4fd95012..9d6e7dc1 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -7,7 +7,6 @@ Implements the Distutils 'bdist_pkgtool' command (create Solaris pkgtool distributions).""" import os, string, sys, pwd, grp -import glob from types import * from distutils.core import Command, DEBUG from distutils.util import get_platform @@ -16,7 +15,6 @@ from distutils.errors import * from distutils.command import bdist_packager from distutils import sysconfig from distutils import log -import compileall from commands import getoutput __revision__ = "$Id: bdist_pkgtool.py,v 0.3 mwa " diff --git a/command/bdist_sdux.py b/command/bdist_sdux.py index e4765f97..a3cbbb8a 100644 --- a/command/bdist_sdux.py +++ b/command/bdist_sdux.py @@ -7,7 +7,6 @@ swinstall depot""" __revision__ = "$Id: bdist_sdux.py,v 0.2 " import os, string -import glob from types import * from distutils.core import Command, DEBUG from distutils.util import get_platform diff --git a/filelist.py b/filelist.py index d39c8358..3ed6f032 100644 --- a/filelist.py +++ b/filelist.py @@ -11,7 +11,7 @@ and building lists of files. __revision__ = "$Id$" -import sys, os, string, re +import os, string, re import fnmatch from types import * from glob import glob diff --git a/unixccompiler.py b/unixccompiler.py index 55a51b32..56d3ee44 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,7 +17,7 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" -import string, re, os, sys +import os, sys from types import * from copy import copy from distutils import sysconfig -- cgit v1.2.1 From ae4a1269c661b5fb842d7d18911d5a0c19ded7dc Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:24:05 +0000 Subject: Set repeat metadata for an option based on repeat local var not constant. --- fancy_getopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index fe9b0d4d..cb89e070 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -182,7 +182,7 @@ class FancyGetopt: ("invalid short option '%s': " "must a single character or None") % short - self.repeat[long] = 1 + self.repeat[long] = repeat self.long_opts.append(long) if long[-1] == '=': # option takes an argument? -- cgit v1.2.1 From c3296eab8b1919b8f4c3fcdbfbd34deca6a22040 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:26:44 +0000 Subject: Fix unused local variables caught by pychecker. Fixes a bug for Solaris pkgtool (bdist_pkgtool) that would have prevented it from building subpackages. --- command/bdist_packager.py | 10 +--------- command/bdist_pkgtool.py | 4 ++-- core.py | 4 +--- cygwinccompiler.py | 7 ++----- emxccompiler.py | 4 ++-- sysconfig.py | 1 - 6 files changed, 8 insertions(+), 22 deletions(-) diff --git a/command/bdist_packager.py b/command/bdist_packager.py index 11278ee2..dde113ce 100644 --- a/command/bdist_packager.py +++ b/command/bdist_packager.py @@ -145,7 +145,6 @@ class bdist_packager (Command): def initialize_options (self): - d = self.distribution self.keep_temp = 0 self.control_only = 0 self.no_autorelocate = 0 @@ -187,8 +186,7 @@ class bdist_packager (Command): self.pkg_dir = os.path.join(bdist_base, 'binary') if not self.plat_name: - d = self.distribution - self.plat = d.get_platforms() + self.plat = self.distribution.get_platforms() if self.distribution.has_ext_modules(): self.plat_name = [sys.platform,] else: @@ -237,12 +235,6 @@ class bdist_packager (Command): log.info("installing to %s", self.pkg_dir) self.run_command('install') - - # And make an archive relative to the root of the - # pseudo-installation tree. - archive_basename = "%s.%s" % (self.distribution.get_fullname(), - self.plat_name) - if not self.keep_temp: remove_tree(self.pkg_dir, dry_run=self.dry_run) diff --git a/command/bdist_pkgtool.py b/command/bdist_pkgtool.py index 9d6e7dc1..3a48d269 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -208,7 +208,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): else: pkg_dir = self.pkg_dir - install = self.reinitialize_command('install', reinit_subcommands=1) + self.reinitialize_command('install', reinit_subcommands=1) # build package log.info('Building package') self.run_command('build') @@ -275,7 +275,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): if self.subpackages: self.subpackages=string.split(self.subpackages,",") for pkg in self.subpackages: - self.make_package(subpackage) + self.make_package(pkg) else: self.make_package() # run() diff --git a/core.py b/core.py index 222e6aeb..fbf5d511 100644 --- a/core.py +++ b/core.py @@ -125,9 +125,7 @@ def setup (**attrs): try: ok = dist.parse_command_line() except DistutilsArgError, msg: - script = os.path.basename(dist.script_name) - raise SystemExit, \ - gen_usage(dist.script_name) + "\nerror: %s" % msg + raise SystemExit, gen_usage(dist.script_name) + "\nerror: %s" % msg if DEBUG: print "options (after parsing command line):" diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 3fb5bc90..443c9bcf 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -213,7 +213,6 @@ class CygwinCCompiler (UnixCCompiler): # generate the filenames for these files def_file = os.path.join(temp_dir, dll_name + ".def") - exp_file = os.path.join(temp_dir, dll_name + ".exp") lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") # Generate .def file @@ -229,16 +228,14 @@ class CygwinCCompiler (UnixCCompiler): # dllwrap uses different options than gcc/ld if self.linker_dll == "dllwrap": - extra_preargs.extend([#"--output-exp",exp_file, - "--output-lib",lib_file, - ]) + extra_preargs.extend(["--output-lib", lib_file]) # for dllwrap we have to use a special option extra_preargs.extend(["--def", def_file]) # we use gcc/ld here and can be sure ld is >= 2.9.10 else: # doesn't work: bfd_close build\...\libfoo.a: Invalid operation #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) - # for gcc/ld the def-file is specified as any other object files + # for gcc/ld the def-file is specified as any object files objects.append(def_file) #end: if ((export_symbols is not None) and diff --git a/emxccompiler.py b/emxccompiler.py index 2788209c..644c6fc3 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -174,11 +174,11 @@ class EMXCCompiler (UnixCCompiler): # generate the filenames for these files def_file = os.path.join(temp_dir, dll_name + ".def") - lib_file = os.path.join(temp_dir, dll_name + ".lib") # Generate .def file contents = [ - "LIBRARY %s INITINSTANCE TERMINSTANCE" % os.path.splitext(os.path.basename(output_filename))[0], + "LIBRARY %s INITINSTANCE TERMINSTANCE" % \ + os.path.splitext(os.path.basename(output_filename))[0], "DATA MULTIPLE NONSHARED", "EXPORTS"] for sym in export_symbols: diff --git a/sysconfig.py b/sysconfig.py index 3e323533..847b8724 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -305,7 +305,6 @@ def expand_makefile_vars(s, vars): while 1: m = _findvar1_rx.search(s) or _findvar2_rx.search(s) if m: - name = m.group(1) (beg, end) = m.span() s = s[0:beg] + vars.get(m.group(1)) + s[end:] else: -- cgit v1.2.1 From c7cb56da7ee4c7f2a29d32f6a15c6e838708419e Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:30:10 +0000 Subject: Fix bug in recent change to logging code. mode is not computed in dry_run mode, so it can't be included in the log message. --- command/install_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_scripts.py b/command/install_scripts.py index 4044ba09..ceece1b6 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -49,7 +49,7 @@ class install_scripts (Command): # all the scripts we just installed. for file in self.get_outputs(): if self.dry_run: - log.info("changing mode of %s to %o", file, mode) + log.info("changing mode of %s", file) else: mode = ((os.stat(file)[ST_MODE]) | 0111) & 07777 log.info("changing mode of %s to %o", file, mode) -- cgit v1.2.1 From 8c19b0c4ec29c8daa3aceddcaabf7ec0f86ed12e Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:35:10 +0000 Subject: global _option_order is not used --- fancy_getopt.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index cb89e070..2ed29a24 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -31,12 +31,6 @@ neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) # (for use as attributes of some object). longopt_xlate = string.maketrans('-', '_') -# This records (option, value) pairs in the order seen on the command line; -# it's close to what getopt.getopt() returns, but with short options -# expanded. (Ugh, this module should be OO-ified.) -_option_order = None - - class FancyGetopt: """Wrapper around the standard 'getopt()' module that provides some handy extra functionality: -- cgit v1.2.1 From d8961739214a05f084e4ee8ac55b0c02deaba3b7 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:39:34 +0000 Subject: get_script() implicitly returned None and also had explicit returns. Make all returns explicit and rearrange logic to avoid extra indentation. --- command/bdist_packager.py | 51 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/command/bdist_packager.py b/command/bdist_packager.py index dde113ce..d57a5940 100644 --- a/command/bdist_packager.py +++ b/command/bdist_packager.py @@ -3,8 +3,8 @@ Modified from bdist_dumb by Mark W. Alexander Implements the Distutils 'bdist_packager' abstract command -to be subclassed by binary package creation commands.""" - +to be subclassed by binary package creation commands. +""" __revision__ = "$Id: bdist_packager.py,v 0.1 2001/04/4 mwa" @@ -114,34 +114,33 @@ class bdist_packager (Command): return self.name + '-' + self.version + '-' + \ self.revision + '-' + py_ver - def get_script (self,attr): + def get_script (self, attr): # accept a script as a string ("line\012line\012..."), # a filename, or a list # XXX We could probably get away with copy_file, but I'm # guessing this will be more flexible later on.... - val = getattr(self,attr) - ret=None - if val: - try: - os.stat(val) - # script is a file - ret=[] - f=open(val) - ret=string.split(f.read(),"\012"); - f.close() - #return ret - except: - if type(val)==type(""): - # script is a string - ret = string.split(val,"\012") - elif type(val)==type([]): - # script is a list - ret = val - else: - raise RuntimeError, \ - "cannot figure out what to do with 'request' option (%s)" \ - % val - return ret + val = getattr(self, attr) + if val is None: + return None + try: + os.stat(val) + # script is a file + ret = [] + f = open(val) + ret = string.split(f.read(), "\012"); + f.close() + except: + if type(val) == type(""): + # script is a string + ret = string.split(val, "\012") + elif type(val) == type([]): + # script is a list + ret = val + else: + raise RuntimeError, \ + "cannot figure out what to do with 'request' option (%s)" \ + % val + return ret def initialize_options (self): -- cgit v1.2.1 From 53d0ff29cbd412eb5505f8044b76544bd7496321 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:40:03 +0000 Subject: Remove (commented out) options that have moved into the distribution. --- command/bdist_packager.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/command/bdist_packager.py b/command/bdist_packager.py index d57a5940..12efeaa0 100644 --- a/command/bdist_packager.py +++ b/command/bdist_packager.py @@ -34,19 +34,6 @@ class bdist_packager (Command): "Software category (packager dependent format)"), ('revision=', None, "package revision number"), - # the following have moved into the distribution class - #('packager=', None, - #"Package maintainer"), - #('packager-mail=', None, - #"Package maintainer's email address"), - #('author=', None, - #"Package author"), - #('author-mail=', None, - #"Package author's email address"), - #('license=', None, - #"License code"), - #('licence=', None, - #"alias for license"), ('icon=', None, "Package icon"), ('subpackages=', None, -- cgit v1.2.1 From d9e8a67208a3d479acb0e19dd9e92b03d6d5aa83 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:42:41 +0000 Subject: Reindent lines to improve readability --- command/bdist_pkgtool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/bdist_pkgtool.py b/command/bdist_pkgtool.py index 3a48d269..998af5e7 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -303,9 +303,9 @@ class bdist_pkgtool (bdist_packager.bdist_packager): try: self.distribution.packages[0] file_list=string.split( - getoutput("pkgproto %s/%s=%s" % (build.build_lib, - self.distribution.packages[0], - self.distribution.packages[0])),"\012") + getoutput("pkgproto %s/%s=%s" % \ + (build.build_lib, self.distribution.packages[0], + self.distribution.packages[0])), "\012") except: file_list=string.split( getoutput("pkgproto %s=" % (build.build_lib)),"\012") -- cgit v1.2.1 From 54e7ddc64aac340cb2a1c87e394caf0a9bdfb3ab Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:45:17 +0000 Subject: ensure_filename() only takes one argument. Call ensure_string() with one arg too, since the second value passed was the default. --- command/bdist_packager.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/command/bdist_packager.py b/command/bdist_packager.py index 12efeaa0..19604255 100644 --- a/command/bdist_packager.py +++ b/command/bdist_packager.py @@ -67,18 +67,16 @@ class bdist_packager (Command): if val is None: raise DistutilsOptionError, "'%s' must be provided" % option - def ensure_script (self,arg): + def ensure_script(self, arg): if not arg: return try: - self.ensure_string(arg, None) + self.ensure_string(arg) except: try: - self.ensure_filename(arg, None) + self.ensure_filename(arg) except: - raise RuntimeError, \ - "cannot decipher script option (%s)" \ - % arg + raise RuntimeError, "cannot decipher script option (%s)" % arg def write_script (self,path,attr,default=None): """ write the script specified in attr to path. if attr is None, -- cgit v1.2.1 From 58596909c7673897cc8563e233ba6b71547a3ab4 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:55:10 +0000 Subject: import base64 at the top to avoid two different imports at other times --- command/bdist_wininst.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 6a985f19..f018f461 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -8,6 +8,7 @@ exe-program.""" __revision__ = "$Id$" import sys, os, string +import base64 from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import create_tree, remove_tree @@ -231,7 +232,6 @@ class bdist_wininst (Command): # create_exe() def get_exe_bytes (self): - import base64 return base64.decodestring(EXEDATA) # class bdist_wininst @@ -248,7 +248,7 @@ if __name__ == '__main__': # - Built wininst.exe from the MSVC project file distutils/misc/wininst.dsw # - Execute this file (distutils/distutils/command/bdist_wininst.py) - import re, base64 + import re moddata = open("bdist_wininst.py", "r").read() exedata = open("../../misc/wininst.exe", "rb").read() print "wininst.exe length is %d bytes" % len(exedata) -- cgit v1.2.1 From 6ff1777df1ce884b828e5e6bcf6a562512d5d543 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:00:20 +0000 Subject: Make None return explicit --- command/build_py.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_py.py b/command/build_py.py index 388d3cbc..0ab72c08 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -181,7 +181,7 @@ class build_py (Command): # Either not in a package at all (__init__.py not expected), or # __init__.py doesn't exist -- so don't return the filename. - return + return None # check_package () -- cgit v1.2.1 From 0d435a0fec6aed4828aaf1f692d34dc0c213570b Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:00:33 +0000 Subject: Remove unused imports --- command/bdist_pkgtool.py | 1 - command/bdist_sdux.py | 1 - 2 files changed, 2 deletions(-) diff --git a/command/bdist_pkgtool.py b/command/bdist_pkgtool.py index 998af5e7..51b89d97 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -8,7 +8,6 @@ distributions).""" import os, string, sys, pwd, grp from types import * -from distutils.core import Command, DEBUG from distutils.util import get_platform from distutils.file_util import write_file from distutils.errors import * diff --git a/command/bdist_sdux.py b/command/bdist_sdux.py index a3cbbb8a..ee3822e9 100644 --- a/command/bdist_sdux.py +++ b/command/bdist_sdux.py @@ -8,7 +8,6 @@ swinstall depot""" __revision__ = "$Id: bdist_sdux.py,v 0.2 " import os, string from types import * -from distutils.core import Command, DEBUG from distutils.util import get_platform from distutils.file_util import write_file from distutils.errors import * -- cgit v1.2.1 From 2c4e48b301ddef1ea26454a694220d56c98a644f Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:02:26 +0000 Subject: Use module-level import of DEBUG instead of many function-level imports. --- dist.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dist.py b/dist.py index a84004f4..f995d58e 100644 --- a/dist.py +++ b/dist.py @@ -16,6 +16,7 @@ from distutils.errors import * from distutils.fancy_getopt import FancyGetopt, translate_longopt from distutils.util import check_environ, strtobool, rfc822_escape from distutils import log +from distutils.core import DEBUG # Regex to define acceptable Distutils command names. This is not *quite* # the same as a Python NAME -- I don't allow leading underscores. The fact @@ -305,7 +306,6 @@ class Distribution: def parse_config_files (self, filenames=None): from ConfigParser import ConfigParser - from distutils.core import DEBUG if filenames is None: filenames = self.find_config_files() @@ -771,7 +771,6 @@ class Distribution: object for 'command' is in the cache, then we either create and return it (if 'create' is true) or return None. """ - from distutils.core import DEBUG cmd_obj = self.command_obj.get(command) if not cmd_obj and create: if DEBUG: @@ -802,8 +801,6 @@ class Distribution: supplied, uses the standard option dictionary for this command (from 'self.command_options'). """ - from distutils.core import DEBUG - command_name = command_obj.get_command_name() if option_dict is None: option_dict = self.get_option_dict(command_name) -- cgit v1.2.1 From 309de7b19b6bcfe387a274af9a2412921364ea0e Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:04:03 +0000 Subject: Add missing import of log. --- filelist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/filelist.py b/filelist.py index 3ed6f032..e2e2457c 100644 --- a/filelist.py +++ b/filelist.py @@ -17,6 +17,7 @@ from types import * from glob import glob from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsInternalError +from distutils import log class FileList: -- cgit v1.2.1 From dfeac87d4fb807b342d27a6d19626aef80a8de17 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:05:05 +0000 Subject: Define DEBUG as early as possible to avoid import problems. --- core.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core.py b/core.py index fbf5d511..8a348ce3 100644 --- a/core.py +++ b/core.py @@ -12,6 +12,11 @@ __revision__ = "$Id$" import sys, os from types import * + +# If DISTUTILS_DEBUG is anything other than the empty string, we run in +# debug mode. +DEBUG = os.environ.get('DISTUTILS_DEBUG') + from distutils.errors import * from distutils.util import grok_environment_error @@ -32,11 +37,6 @@ usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: %(script)s cmd --help """ - -# If DISTUTILS_DEBUG is anything other than the empty string, we run in -# debug mode. -DEBUG = os.environ.get('DISTUTILS_DEBUG') - def gen_usage (script_name): script = os.path.basename(script_name) return USAGE % vars() -- cgit v1.2.1 From 6dc1366e1df26a7247b81f0d1ac19e278ad4fcb7 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:06:16 +0000 Subject: Replace bogus bare variables with attribute access. --- command/bdist_sdux.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/command/bdist_sdux.py b/command/bdist_sdux.py index ee3822e9..875f3d88 100644 --- a/command/bdist_sdux.py +++ b/command/bdist_sdux.py @@ -1,7 +1,8 @@ """distutils.command.bdist_pkgtool Implements the Distutils 'bdist_sdux' command to create HP-UX -swinstall depot""" +swinstall depot. +""" # Mark Alexander @@ -265,11 +266,11 @@ class bdist_sdux(bdist_packager.bdist_packager): #psf_file.extend([self.long_description]) if self.copyright: # XX make a copyright file XXX - write_script('copyright') + self.write_script('copyright') psf_file.extend([' copyright Date: Tue, 4 Jun 2002 21:10:35 +0000 Subject: Track extra arg to option_table to all uses of it --- fancy_getopt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 2ed29a24..a11b4d58 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -358,8 +358,8 @@ class FancyGetopt: else: lines = ['Option summary:'] - for (long,short,help) in self.option_table: - + for option in self.option_table: + long, short, help = option_table[:3] text = wrap_text(help, text_width) if long[-1] == '=': long = long[0:-1] -- cgit v1.2.1 From 03599c1b7e1719cbc27bef76603807002c384aec Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:11:56 +0000 Subject: Test changes before checking them in. --- fancy_getopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index a11b4d58..5de64e15 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -359,7 +359,7 @@ class FancyGetopt: lines = ['Option summary:'] for option in self.option_table: - long, short, help = option_table[:3] + long, short, help = option[:3] text = wrap_text(help, text_width) if long[-1] == '=': long = long[0:-1] -- cgit v1.2.1 From 1d89b23767470db537211e8d3daa12d435d5effb Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:20:08 +0000 Subject: Move warning about directory not on sys.path to debug level. Fix a bunch of multiline string constants that used +. --- command/install.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/command/install.py b/command/install.py index 746ca1f2..322177f4 100644 --- a/command/install.py +++ b/command/install.py @@ -2,6 +2,8 @@ Implements the Distutils 'install' command.""" +from distutils import log + # created 1999/03/13, Greg Ward __revision__ = "$Id$" @@ -368,8 +370,8 @@ class install (Command): self.install_scripts is None or self.install_data is None): raise DistutilsOptionError, \ - "install-base or install-platbase supplied, but " + \ - "installation scheme is incomplete" + ("install-base or install-platbase supplied, but " + "installation scheme is incomplete") return if self.home is not None: @@ -464,8 +466,8 @@ class install (Command): (path_file, extra_dirs) = self.extra_path else: raise DistutilsOptionError, \ - "'extra_path' option must be a list, tuple, or " + \ - "comma-separated string with 1 or 2 elements" + ("'extra_path' option must be a list, tuple, or " + "comma-separated string with 1 or 2 elements") # convert to local form in case Unix notation used (as it # should be in setup scripts) @@ -522,10 +524,10 @@ class install (Command): if (self.warn_dir and not (self.path_file and self.install_path_file) and install_lib not in sys_path): - self.warn(("modules installed to '%s', which is not in " + - "Python's module search path (sys.path) -- " + - "you'll have to change the search path yourself") % - self.install_lib) + log.debug(("modules installed to '%s', which is not in " + "Python's module search path (sys.path) -- " + "you'll have to change the search path yourself"), + self.install_lib) # run () -- cgit v1.2.1 From 6b97787eb0832dabe581cb2b8e55a709bab0d75f Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 6 Jun 2002 14:54:56 +0000 Subject: Change warning to debug level; it's a very minor issue. The specific warning is that clean didn't find a directory that should be removed if it exists. --- command/clean.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/clean.py b/command/clean.py index 8fddeb45..36252d51 100644 --- a/command/clean.py +++ b/command/clean.py @@ -54,8 +54,8 @@ class clean (Command): if os.path.exists(self.build_temp): remove_tree(self.build_temp, dry_run=self.dry_run) else: - log.warn("'%s' does not exist -- can't clean it", - self.build_temp) + log.debug("'%s' does not exist -- can't clean it", + self.build_temp) if self.all: # remove build directories -- cgit v1.2.1 From 8ad35754e1684d02471a0be1b22e0e14c2b78592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 11 Jun 2002 06:22:31 +0000 Subject: Patch #488073: AtheOS port. --- command/build_ext.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 89ca1dc9..d8f3dc8a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -178,9 +178,9 @@ class build_ext (Command): if os.name == 'os2': self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) - # for extensions under Cygwin Python's library directory must be + # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs - if sys.platform[:6] == 'cygwin': + if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", @@ -656,6 +656,22 @@ class build_ext (Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] + elif sys.platform[:6] == "atheos": + from distutils import sysconfig + + template = "python%d.%d" + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # Get SHLIBS from Makefile + extra = [] + for lib in sysconfig.get_config_var('SHLIBS').split(): + if lib.startswith('-l'): + extra.append(lib[2:]) + else: + extra.append(lib) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib, "m"] + extra else: return ext.libraries -- cgit v1.2.1 From c21ebfc2107cf837e5efa2958dc1c66e2db057fc Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 12 Jun 2002 20:08:56 +0000 Subject: Add a new definition to Extension objects: depends. depends is a list of files that the target depends, but aren't direct sources of the target. think .h files. --- command/build_ext.py | 3 ++- extension.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index d8f3dc8a..21dd0dcd 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -422,7 +422,8 @@ class build_ext (Command): ext_filename = os.path.join(self.build_lib, self.get_ext_filename(fullname)) - if not (self.force or newer_group(sources, ext_filename, 'newer')): + depends = sources + ext.depends + if not (self.force or newer_group(depends, ext_filename, 'newer')): log.debug("skipping '%s' extension (up-to-date)", ext.name) return else: diff --git a/extension.py b/extension.py index 37377120..d73bb08e 100644 --- a/extension.py +++ b/extension.py @@ -73,6 +73,8 @@ class Extension: used on all platforms, and not generally necessary for Python extensions, which typically export exactly one symbol: "init" + extension_name. + depends : [string] + list of files that the extension depends on """ def __init__ (self, name, sources, @@ -86,6 +88,7 @@ class Extension: extra_compile_args=None, extra_link_args=None, export_symbols=None, + depends=None, ): assert type(name) is StringType, "'name' must be a string" @@ -105,6 +108,7 @@ class Extension: self.extra_compile_args = extra_compile_args or [] self.extra_link_args = extra_link_args or [] self.export_symbols = export_symbols or [] + self.depends = depends or [] # class Extension -- cgit v1.2.1 From bcb36e840f82d68473ff4c48f3fd9b53fcebf9eb Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 14:58:30 +0000 Subject: Python style conformance: Delete spaces between name of function and arglist. Making the world better a little bit at a time . --- unixccompiler.py | 82 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 56d3ee44..e584007c 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -81,20 +81,20 @@ class UnixCCompiler (CCompiler): - def __init__ (self, - verbose=0, - dry_run=0, - force=0): + def __init__(self, + verbose=0, + dry_run=0, + force=0): CCompiler.__init__ (self, verbose, dry_run, force) - def preprocess (self, - source, - output_file=None, - macros=None, - include_dirs=None, - extra_preargs=None, - extra_postargs=None): + def preprocess(self, + source, + output_file=None, + macros=None, + include_dirs=None, + extra_preargs=None, + extra_postargs=None): (_, macros, include_dirs) = \ self._fix_compile_args(None, macros, include_dirs) @@ -120,14 +120,14 @@ class UnixCCompiler (CCompiler): raise CompileError, msg - def compile (self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): + def compile(self, + sources, + output_dir=None, + macros=None, + include_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None): (output_dir, macros, include_dirs) = \ self._fix_compile_args(output_dir, macros, include_dirs) @@ -164,11 +164,11 @@ class UnixCCompiler (CCompiler): # compile () - def create_static_lib (self, - objects, - output_libname, - output_dir=None, - debug=0): + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0): (objects, output_dir) = self._fix_object_args(objects, output_dir) @@ -197,19 +197,19 @@ class UnixCCompiler (CCompiler): # create_static_lib () - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None): + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None): (objects, output_dir) = self._fix_object_args(objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ @@ -250,10 +250,10 @@ class UnixCCompiler (CCompiler): # These are all used by the 'gen_lib_options() function, in # ccompiler.py. - def library_dir_option (self, dir): + def library_dir_option(self, dir): return "-L" + dir - def runtime_library_dir_option (self, dir): + def runtime_library_dir_option(self, dir): # XXX Hackish, at the very least. See Python bug #445902: # http://sourceforge.net/tracker/index.php # ?func=detail&aid=445902&group_id=5470&atid=105470 @@ -272,11 +272,11 @@ class UnixCCompiler (CCompiler): else: return "-R" + dir - def library_option (self, lib): + def library_option(self, lib): return "-l" + lib - def find_library_file (self, dirs, lib, debug=0): + def find_library_file(self, dirs, lib, debug=0): for dir in dirs: shared = os.path.join( -- cgit v1.2.1 From 80830960c062d2faed9375840f036eefe109c991 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 15:01:38 +0000 Subject: Some more style improvements --- unixccompiler.py | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index e584007c..1cfeebd1 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -18,8 +18,9 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" import os, sys -from types import * +from types import StringType, NoneType from copy import copy + from distutils import sysconfig from distutils.dep_util import newer from distutils.ccompiler import \ @@ -43,8 +44,7 @@ from distutils import log # should just happily stuff them into the preprocessor/compiler/linker # options and carry on. - -class UnixCCompiler (CCompiler): +class UnixCCompiler(CCompiler): compiler_type = 'unix' @@ -85,8 +85,7 @@ class UnixCCompiler (CCompiler): verbose=0, dry_run=0, force=0): - CCompiler.__init__ (self, verbose, dry_run, force) - + CCompiler.__init__(self, verbose, dry_run, force) def preprocess(self, source, @@ -95,7 +94,6 @@ class UnixCCompiler (CCompiler): include_dirs=None, extra_preargs=None, extra_postargs=None): - (_, macros, include_dirs) = \ self._fix_compile_args(None, macros, include_dirs) pp_opts = gen_preprocess_options(macros, include_dirs) @@ -119,7 +117,6 @@ class UnixCCompiler (CCompiler): except DistutilsExecError, msg: raise CompileError, msg - def compile(self, sources, output_dir=None, @@ -128,7 +125,6 @@ class UnixCCompiler (CCompiler): debug=0, extra_preargs=None, extra_postargs=None): - (output_dir, macros, include_dirs) = \ self._fix_compile_args(output_dir, macros, include_dirs) (objects, skip_sources) = self._prep_compile(sources, output_dir) @@ -161,15 +157,11 @@ class UnixCCompiler (CCompiler): # Return *all* object filenames, not just the ones we just built. return objects - # compile () - - def create_static_lib(self, objects, output_libname, output_dir=None, debug=0): - (objects, output_dir) = self._fix_object_args(objects, output_dir) output_filename = \ @@ -194,9 +186,6 @@ class UnixCCompiler (CCompiler): else: log.debug("skipping %s (up-to-date)", output_filename) - # create_static_lib () - - def link(self, target_desc, objects, @@ -210,7 +199,6 @@ class UnixCCompiler (CCompiler): extra_preargs=None, extra_postargs=None, build_temp=None): - (objects, output_dir) = self._fix_object_args(objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) @@ -243,9 +231,6 @@ class UnixCCompiler (CCompiler): else: log.debug("skipping %s (up-to-date)", output_filename) - # link () - - # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in # ccompiler.py. @@ -275,9 +260,7 @@ class UnixCCompiler (CCompiler): def library_option(self, lib): return "-l" + lib - def find_library_file(self, dirs, lib, debug=0): - for dir in dirs: shared = os.path.join( dir, self.library_filename(lib, lib_type='shared')) @@ -300,7 +283,3 @@ class UnixCCompiler (CCompiler): else: # Oops, didn't find it in *any* of 'dirs' return None - - # find_library_file () - -# class UnixCCompiler -- cgit v1.2.1 From 26e7e90cb2e973453758e43324128205af177296 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 15:14:10 +0000 Subject: More style changes and little cleanups. Remove __init__ that just called base class __init__ with same args. Fold long argument lists into fewer, shorter lines. Remove parens in tuple unpacks. Don't put multiple statements on one line with a semicolon. In find_library_file() compute the library_filename() upfront. --- unixccompiler.py | 96 +++++++++++++++++++------------------------------------- 1 file changed, 33 insertions(+), 63 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 1cfeebd1..abf7a264 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -79,22 +79,10 @@ class UnixCCompiler(CCompiler): dylib_lib_extension = ".dylib" static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" - - - def __init__(self, - verbose=0, - dry_run=0, - force=0): - CCompiler.__init__(self, verbose, dry_run, force) - - def preprocess(self, - source, - output_file=None, - macros=None, - include_dirs=None, - extra_preargs=None, - extra_postargs=None): - (_, macros, include_dirs) = \ + def preprocess(self, source, + output_file=None, macros=None, include_dirs=None, + extra_preargs=None, extra_postargs=None): + ignore, macros, include_dirs = \ self._fix_compile_args(None, macros, include_dirs) pp_opts = gen_preprocess_options(macros, include_dirs) pp_args = self.preprocessor + pp_opts @@ -117,17 +105,12 @@ class UnixCCompiler(CCompiler): except DistutilsExecError, msg: raise CompileError, msg - def compile(self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): - (output_dir, macros, include_dirs) = \ + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None): + output_dir, macros, include_dirs = \ self._fix_compile_args(output_dir, macros, include_dirs) - (objects, skip_sources) = self._prep_compile(sources, output_dir) + objects, skip_sources = self._prep_compile(sources, output_dir) # Figure out the options for the compiler command line. pp_opts = gen_preprocess_options(macros, include_dirs) @@ -142,27 +125,24 @@ class UnixCCompiler(CCompiler): # Compile all source files that weren't eliminated by # '_prep_compile()'. for i in range(len(sources)): - src = sources[i] ; obj = objects[i] + src = sources[i] + obj = objects[i] if skip_sources[src]: log.debug("skipping %s (%s up-to-date)", src, obj) else: self.mkpath(os.path.dirname(obj)) try: self.spawn(self.compiler_so + cc_args + - [src, '-o', obj] + - extra_postargs) + [src, '-o', obj] + extra_postargs) except DistutilsExecError, msg: raise CompileError, msg # Return *all* object filenames, not just the ones we just built. return objects - def create_static_lib(self, - objects, - output_libname, - output_dir=None, - debug=0): - (objects, output_dir) = self._fix_object_args(objects, output_dir) + def create_static_lib(self, objects, output_libname, + output_dir=None, debug=0): + objects, output_dir = self._fix_object_args(objects, output_dir) output_filename = \ self.library_filename(output_libname, output_dir=output_dir) @@ -186,25 +166,16 @@ class UnixCCompiler(CCompiler): else: log.debug("skipping %s (up-to-date)", output_filename) - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None): - (objects, output_dir) = self._fix_object_args(objects, output_dir) - (libraries, library_dirs, runtime_library_dirs) = \ + def link(self, target_desc, objects, + output_filename, output_dir=None, libraries=None, + library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None): + objects, output_dir = self._fix_object_args(objects, output_dir) + libraries, library_dirs, runtime_library_dirs = \ self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) - lib_opts = gen_lib_options(self, - library_dirs, runtime_library_dirs, + lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) if type(output_dir) not in (StringType, NoneType): raise TypeError, "'output_dir' must be a string or None" @@ -261,14 +232,14 @@ class UnixCCompiler(CCompiler): return "-l" + lib def find_library_file(self, dirs, lib, debug=0): + shared_f = self.library_filename(lib, lib_type='shared') + dylib_f = self.library_filename(lib, lib_type='dylib') + static_f = self.library_filename(lib, lib_type='static') + for dir in dirs: - shared = os.path.join( - dir, self.library_filename(lib, lib_type='shared')) - dylib = os.path.join( - dir, self.library_filename(lib, lib_type='dylib')) - static = os.path.join( - dir, self.library_filename(lib, lib_type='static')) - + shared = os.path.join(dir, shared_f) + dylib = os.path.join(dir, dylib_f) + static = os.path.join(dir, static_f) # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm # assuming that *all* Unix C compilers do. And of course I'm @@ -279,7 +250,6 @@ class UnixCCompiler(CCompiler): return shared elif os.path.exists(static): return static - - else: - # Oops, didn't find it in *any* of 'dirs' - return None + + # Oops, didn't find it in *any* of 'dirs' + return None -- cgit v1.2.1 From 538305bafac46ceda3d7702e7cf88a19a757caef Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 17:26:30 +0000 Subject: Extend compiler() method with optional depends argument. This change is not backwards compatible. If a compiler subclass exists outside the distutils package, it may get called with the unexpected keyword arg. It's easy to extend that compiler by having it ignore the argument, and not much harder to do the right thing. If this ends up being burdensome, we can change it before 2.3 final to work harder at compatibility. Also add _setup_compile() and _get_cc_args() helper functions that factor out much of the boilerplate for each concrete compiler class. --- ccompiler.py | 242 +++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 168 insertions(+), 74 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 4c8b881c..50246b89 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -160,7 +160,6 @@ class CCompiler: setattr(self, key, value) - def _find_macro (self, name): i = 0 for defn in self.macros: @@ -321,6 +320,100 @@ class CCompiler: # -- Private utility methods -------------------------------------- # (here for the convenience of subclasses) + # Helper method to prep compiler in subclass compile() methods + + def _setup_compile(self, outdir, macros, incdirs, sources, depends, + extra): + """Process arguments and decide which source files to compile. + + Merges _fix_compile_args() and _prep_compile(). + """ + if outdir is None: + outdir = self.output_dir + elif type(outdir) is not StringType: + raise TypeError, "'output_dir' must be a string or None" + + if macros is None: + macros = self.macros + elif type(macros) is ListType: + macros = macros + (self.macros or []) + else: + raise TypeError, "'macros' (if supplied) must be a list of tuples" + + if incdirs is None: + incdirs = self.include_dirs + elif type(incdirs) in (ListType, TupleType): + incdirs = list(incdirs) + (self.include_dirs or []) + else: + raise TypeError, \ + "'include_dirs' (if supplied) must be a list of strings" + + if extra is None: + extra = [] + + # Get the list of expected output (object) files + objects = self.object_filenames(sources, 1, outdir) + assert len(objects) == len(sources) + + # XXX should redo this code to eliminate skip_source entirely. + # XXX instead create build and issue skip messages inline + + if self.force: + skip_source = {} # rebuild everything + for source in sources: + skip_source[source] = 0 + elif depends is None: + # If depends is None, figure out which source files we + # have to recompile according to a simplistic check. We + # just compare the source and object file, no deep + # dependency checking involving header files. + skip_source = {} # rebuild everything + for source in sources: # no wait, rebuild nothing + skip_source[source] = 1 + + n_sources, n_objects = newer_pairwise(sources, objects) + for source in n_sources: # no really, only rebuild what's + skip_source[source] = 0 # out-of-date + else: + # If depends is a list of files, then do a different + # simplistic check. Assume that each object depends on + # its source and all files in the depends list. + skip_source = {} + # L contains all the depends plus a spot at the end for a + # particular source file + L = depends[:] + [None] + for i in range(len(objects)): + source = sources[i] + L[-1] = source + if newer_group(L, objects[i]): + skip_source[source] = 0 + else: + skip_source[source] = 1 + + pp_opts = gen_preprocess_options(macros, incdirs) + + build = {} + for i in range(len(sources)): + src = sources[i] + obj = objects[i] + ext = os.path.splitext(src)[1] + self.mkpath(os.path.dirname(obj)) + if skip_source[src]: + log.debug("skipping %s (%s up-to-date)", src, obj) + else: + build[obj] = src, ext + + return macros, objects, extra, pp_opts, build + + def _get_cc_args(self, pp_opts, debug, before): + # works for unixccompiler, emxccompiler, cygwinccompiler + cc_args = pp_opts + ['-c'] + if debug: + cc_args[:0] = ['-g'] + if before: + cc_args[:0] = before + return cc_args + def _fix_compile_args (self, output_dir, macros, include_dirs): """Typecheck and fix-up some of the arguments to the 'compile()' method, and return fixed-up values. Specifically: if 'output_dir' @@ -341,8 +434,7 @@ class CCompiler: elif type (macros) is ListType: macros = macros + (self.macros or []) else: - raise TypeError, \ - "'macros' (if supplied) must be a list of tuples" + raise TypeError, "'macros' (if supplied) must be a list of tuples" if include_dirs is None: include_dirs = self.include_dirs @@ -352,40 +444,57 @@ class CCompiler: raise TypeError, \ "'include_dirs' (if supplied) must be a list of strings" - return (output_dir, macros, include_dirs) + return output_dir, macros, include_dirs # _fix_compile_args () - def _prep_compile (self, sources, output_dir): - """Determine the list of object files corresponding to 'sources', - and figure out which ones really need to be recompiled. Return a - list of all object files and a dictionary telling which source - files can be skipped. + def _prep_compile(self, sources, output_dir, depends=None): + """Decide which souce files must be recompiled. + + Determine the list of object files corresponding to 'sources', + and figure out which ones really need to be recompiled. + Return a list of all object files and a dictionary telling + which source files can be skipped. """ # Get the list of expected output (object) files - objects = self.object_filenames (sources, - strip_dir=1, - output_dir=output_dir) + objects = self.object_filenames(sources, strip_dir=1, + output_dir=output_dir) + assert len(objects) == len(sources) if self.force: skip_source = {} # rebuild everything for source in sources: skip_source[source] = 0 - else: - # Figure out which source files we have to recompile according - # to a simplistic check -- we just compare the source and - # object file, no deep dependency checking involving header - # files. + elif depends is None: + # If depends is None, figure out which source files we + # have to recompile according to a simplistic check. We + # just compare the source and object file, no deep + # dependency checking involving header files. skip_source = {} # rebuild everything for source in sources: # no wait, rebuild nothing skip_source[source] = 1 - (n_sources, n_objects) = newer_pairwise (sources, objects) + n_sources, n_objects = newer_pairwise(sources, objects) for source in n_sources: # no really, only rebuild what's skip_source[source] = 0 # out-of-date - - return (objects, skip_source) + else: + # If depends is a list of files, then do a different + # simplistic check. Assume that each object depends on + # its source and all files in the depends list. + skip_source = {} + # L contains all the depends plus a spot at the end for a + # particular source file + L = depends[:] + [None] + for i in range(len(objects)): + source = sources[i] + L[-1] = source + if newer_group(L, objects[i]): + skip_source[source] = 0 + else: + skip_source[source] = 1 + + return objects, skip_source # _prep_compile () @@ -484,22 +593,19 @@ class CCompiler: """ pass - def compile (self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): - """Compile one or more source files. 'sources' must be a list of - filenames, most likely C/C++ files, but in reality anything that - can be handled by a particular compiler and compiler class - (eg. MSVCCompiler can handle resource files in 'sources'). Return - a list of object filenames, one per source filename in 'sources'. - Depending on the implementation, not all source files will - necessarily be compiled, but all corresponding object filenames - will be returned. + def compile(self, sources, output_dir=None, macros=None, + include_dirs=None, debug=0, extra_preargs=None, + extra_postargs=None, depends=None): + """Compile one or more source files. + + 'sources' must be a list of filenames, most likely C/C++ + files, but in reality anything that can be handled by a + particular compiler and compiler class (eg. MSVCCompiler can + handle resource files in 'sources'). Return a list of object + filenames, one per source filename in 'sources'. Depending on + the implementation, not all source files will necessarily be + compiled, but all corresponding object filenames will be + returned. If 'output_dir' is given, object files will be put under it, while retaining their original path component. That is, "foo/bar.c" @@ -530,6 +636,12 @@ class CCompiler: for those occasions when the abstract compiler framework doesn't cut the mustard. + 'depends', if given, is a list of filenames that all targets + depend on. If a source file is older than any file in + depends, then the source file will be recompiled. This + supports dependency tracking, but only at a coarse + granularity. + Raises CompileError on failure. """ pass @@ -710,7 +822,6 @@ class CCompiler: """ raise NotImplementedError - # -- Filename generation methods ----------------------------------- # The default implementation of the filename generating methods are @@ -745,63 +856,46 @@ class CCompiler: # * exe_extension - # extension for executable files, eg. '' or '.exe' - def object_filenames (self, - source_filenames, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' + def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + assert output_dir is not None obj_names = [] for src_name in source_filenames: - (base, ext) = os.path.splitext (src_name) + base, ext = os.path.splitext(src_name) if ext not in self.src_extensions: raise UnknownFileError, \ - "unknown file type '%s' (from '%s')" % \ - (ext, src_name) + "unknown file type '%s' (from '%s')" % (ext, src_name) if strip_dir: - base = os.path.basename (base) - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) + base = os.path.basename(base) + obj_names.append(os.path.join(output_dir, + base + self.obj_extension)) return obj_names - # object_filenames () - - - def shared_object_filename (self, - basename, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' + def shared_object_filename(self, basename, strip_dir=0, output_dir=''): + assert output_dir is not None if strip_dir: basename = os.path.basename (basename) - return os.path.join (output_dir, basename + self.shared_lib_extension) + return os.path.join(output_dir, basename + self.shared_lib_extension) - def executable_filename (self, - basename, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' + def executable_filename(self, basename, strip_dir=0, output_dir=''): + assert output_dir is not None if strip_dir: basename = os.path.basename (basename) return os.path.join(output_dir, basename + (self.exe_extension or '')) - def library_filename (self, - libname, - lib_type='static', # or 'shared' - strip_dir=0, - output_dir=''): - - if output_dir is None: output_dir = '' - if lib_type not in ("static","shared","dylib"): + def library_filename(self, libname, lib_type='static', # or 'shared' + strip_dir=0, output_dir=''): + assert output_dir is not None + if lib_type not in ("static", "shared", "dylib"): raise ValueError, "'lib_type' must be \"static\", \"shared\" or \"dylib\"" - fmt = getattr (self, lib_type + "_lib_format") - ext = getattr (self, lib_type + "_lib_extension") + fmt = getattr(self, lib_type + "_lib_format") + ext = getattr(self, lib_type + "_lib_extension") - (dir, base) = os.path.split (libname) + dir, base = os.path.split (libname) filename = fmt % (base, ext) if strip_dir: dir = '' - return os.path.join (output_dir, dir, filename) + return os.path.join(output_dir, dir, filename) # -- Utility methods ----------------------------------------------- -- cgit v1.2.1 From 155cee1b86894b12c00e663d6d8172b385700127 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 17:27:13 +0000 Subject: Add depends=None to the arglist for compile(). --- mwerkscompiler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 6242f12a..1f71c60c 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -62,7 +62,8 @@ class MWerksCompiler (CCompiler) : include_dirs=None, debug=0, extra_preargs=None, - extra_postargs=None): + extra_postargs=None, + depends=None): (output_dir, macros, include_dirs) = \ self._fix_compile_args (output_dir, macros, include_dirs) self.__sources = sources -- cgit v1.2.1 From 2c28f1d7b189db2ed88625f504345e19d4be2e08 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 17:28:18 +0000 Subject: Refactor compile() method implementations. Always use _setup_compile() to do the grunt work of processing arguments, figuring out which files to compile, and emitting debug messages for files that are up-to-date. Use _get_cc_args() when possible. --- bcppcompiler.py | 101 +++++++++++++++------------------- cygwinccompiler.py | 85 ++++++++++------------------- emxccompiler.py | 69 +++++++++--------------- msvccompiler.py | 155 ++++++++++++++++++++++++----------------------------- unixccompiler.py | 40 +++++--------- 5 files changed, 178 insertions(+), 272 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 019244cd..6e9d6c64 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -80,23 +80,13 @@ class BCPPCompiler(CCompiler) : # -- Worker methods ------------------------------------------------ - def compile (self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): - - (output_dir, macros, include_dirs) = \ - self._fix_compile_args (output_dir, macros, include_dirs) - (objects, skip_sources) = self._prep_compile (sources, output_dir) - - if extra_postargs is None: - extra_postargs = [] - - pp_opts = gen_preprocess_options (macros, include_dirs) + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + macros, objects, extra_postargs, pp_opts, build = \ + self._setup_compile(output_dir, macros, include_dirs, sources, + depends, extra_postargs) compile_opts = extra_preargs or [] compile_opts.append ('-c') if debug: @@ -104,50 +94,47 @@ class BCPPCompiler(CCompiler) : else: compile_opts.extend (self.compile_options) - for i in range (len (sources)): - src = sources[i] ; obj = objects[i] - ext = (os.path.splitext (src))[1] + for obj, (src, ext) in build.items(): + # XXX why do the normpath here? + src = os.path.normpath(src) + obj = os.path.normpath(obj) + # XXX _setup_compile() did a mkpath() too but before the normpath. + # Is it possible to skip the normpath? + self.mkpath(os.path.dirname(obj)) - if skip_sources[src]: - log.debug("skipping %s (%s up-to-date)", src, obj) - else: - src = os.path.normpath(src) - obj = os.path.normpath(obj) - self.mkpath(os.path.dirname(obj)) - - if ext == '.res': - # This is already a binary file -- skip it. - continue # the 'for' loop - if ext == '.rc': - # This needs to be compiled to a .res file -- do it now. - try: - self.spawn (["brcc32", "-fo", obj, src]) - except DistutilsExecError, msg: - raise CompileError, msg - continue # the 'for' loop - - # The next two are both for the real compiler. - if ext in self._c_extensions: - input_opt = "" - elif ext in self._cpp_extensions: - input_opt = "-P" - else: - # Unknown file type -- no extra options. The compiler - # will probably fail, but let it just in case this is a - # file the compiler recognizes even if we don't. - input_opt = "" - - output_opt = "-o" + obj - - # Compiler command line syntax is: "bcc32 [options] file(s)". - # Note that the source file names must appear at the end of - # the command line. + if ext == '.res': + # This is already a binary file -- skip it. + continue # the 'for' loop + if ext == '.rc': + # This needs to be compiled to a .res file -- do it now. try: - self.spawn ([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs + [src]) + self.spawn (["brcc32", "-fo", obj, src]) except DistutilsExecError, msg: raise CompileError, msg + continue # the 'for' loop + + # The next two are both for the real compiler. + if ext in self._c_extensions: + input_opt = "" + elif ext in self._cpp_extensions: + input_opt = "-P" + else: + # Unknown file type -- no extra options. The compiler + # will probably fail, but let it just in case this is a + # file the compiler recognizes even if we don't. + input_opt = "" + + output_opt = "-o" + obj + + # Compiler command line syntax is: "bcc32 [options] file(s)". + # Note that the source file names must appear at the end of + # the command line. + try: + self.spawn ([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs + [src]) + except DistutilsExecError, msg: + raise CompileError, msg return objects diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 443c9bcf..302293ac 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -62,10 +62,7 @@ class CygwinCCompiler (UnixCCompiler): shared_lib_format = "%s%s" exe_extension = ".exe" - def __init__ (self, - verbose=0, - dry_run=0, - force=0): + def __init__ (self, verbose=0, dry_run=0, force=0): UnixCCompiler.__init__ (self, verbose, dry_run, force) @@ -74,11 +71,12 @@ class CygwinCCompiler (UnixCCompiler): (status, details)) if status is not CONFIG_H_OK: self.warn( - "Python's pyconfig.h doesn't seem to support your compiler. " + - ("Reason: %s." % details) + - "Compiling may fail because of undefined preprocessor macros.") + "Python's pyconfig.h doesn't seem to support your compiler. " + "Reason: %s. " + "Compiling may fail because of undefined preprocessor macros." + % details) - (self.gcc_version, self.ld_version, self.dllwrap_version) = \ + self.gcc_version, self.ld_version, self.dllwrap_version = \ get_versions() self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % (self.gcc_version, @@ -120,58 +118,33 @@ class CygwinCCompiler (UnixCCompiler): # we put here a adapted version of it. # (If we would call compile() in the base class, it would do some # initializations a second time, this is why all is done here.) - def compile (self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): - - (output_dir, macros, include_dirs) = \ - self._fix_compile_args (output_dir, macros, include_dirs) - (objects, skip_sources) = self._prep_compile (sources, output_dir) - - # Figure out the options for the compiler command line. - pp_opts = gen_preprocess_options (macros, include_dirs) - cc_args = pp_opts + ['-c'] - if debug: - cc_args[:0] = ['-g'] - if extra_preargs: - cc_args[:0] = extra_preargs - if extra_postargs is None: - extra_postargs = [] - - # Compile all source files that weren't eliminated by - # '_prep_compile()'. - for i in range (len (sources)): - src = sources[i] ; obj = objects[i] - ext = (os.path.splitext (src))[1] - if skip_sources[src]: - log.debug("skipping %s (%s up-to-date)", src, obj) - else: - self.mkpath (os.path.dirname (obj)) - if ext == '.rc' or ext == '.res': - # gcc needs '.res' and '.rc' compiled to object files !!! - try: - self.spawn (["windres","-i",src,"-o",obj]) - except DistutilsExecError, msg: - raise CompileError, msg - else: # for other files use the C-compiler - try: - self.spawn (self.compiler_so + cc_args + - [src, '-o', obj] + - extra_postargs) - except DistutilsExecError, msg: - raise CompileError, msg + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + macros, objects, extra_postargs, pp_opts, build = \ + self._setup_compile(output_dir, macros, include_dirs, sources, + depends, extra_postargs) + cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) + + for obj, (src, ext) in build.items(): + if ext == '.rc' or ext == '.res': + # gcc needs '.res' and '.rc' compiled to object files !!! + try: + self.spawn (["windres","-i",src,"-o",obj]) + except DistutilsExecError, msg: + raise CompileError, msg + else: # for other files use the C-compiler + try: + self.spawn (self.compiler_so + cc_args + + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg # Return *all* object filenames, not just the ones we just built. return objects - # compile () - - def link (self, target_desc, objects, diff --git a/emxccompiler.py b/emxccompiler.py index 644c6fc3..c2c73b0d 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -81,51 +81,30 @@ class EMXCCompiler (UnixCCompiler): # we put here a adapted version of it. # (If we would call compile() in the base class, it would do some # initializations a second time, this is why all is done here.) - def compile (self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): - - (output_dir, macros, include_dirs) = \ - self._fix_compile_args (output_dir, macros, include_dirs) - (objects, skip_sources) = self._prep_compile (sources, output_dir) - - # Figure out the options for the compiler command line. - pp_opts = gen_preprocess_options (macros, include_dirs) - cc_args = pp_opts + ['-c'] - if debug: - cc_args[:0] = ['-g'] - if extra_preargs: - cc_args[:0] = extra_preargs - if extra_postargs is None: - extra_postargs = [] - - # Compile all source files that weren't eliminated by - # '_prep_compile()'. - for i in range (len (sources)): - src = sources[i] ; obj = objects[i] - ext = (os.path.splitext (src))[1] - if skip_sources[src]: - log.debug("skipping %s (%s up-to-date)", src, obj) - else: - self.mkpath (os.path.dirname (obj)) - if ext == '.rc': - # gcc requires '.rc' compiled to binary ('.res') files !!! - try: - self.spawn (["rc","-r",src]) - except DistutilsExecError, msg: - raise CompileError, msg - else: # for other files use the C-compiler - try: - self.spawn (self.compiler_so + cc_args + - [src, '-o', obj] + - extra_postargs) - except DistutilsExecError, msg: - raise CompileError, msg + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + macros, objects, extra_postargs, pp_opts, build = \ + self._setup_compile(output_dir, macros, include_dirs, sources, + depends, extra_postargs) + cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) + + for obj, (src, ext) in build.items(): + if ext == '.rc': + # gcc requires '.rc' compiled to binary ('.res') files !!! + try: + self.spawn (["rc","-r",src]) + except DistutilsExecError, msg: + raise CompileError, msg + else: # for other files use the C-compiler + try: + self.spawn (self.compiler_so + cc_args + + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg # Return *all* object filenames, not just the ones we just built. return objects diff --git a/msvccompiler.py b/msvccompiler.py index ade8172d..8460eea9 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -277,101 +277,84 @@ class MSVCCompiler (CCompiler) : # object_filenames () - def compile (self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None): - - (output_dir, macros, include_dirs) = \ - self._fix_compile_args (output_dir, macros, include_dirs) - (objects, skip_sources) = self._prep_compile (sources, output_dir) - - if extra_postargs is None: - extra_postargs = [] - - pp_opts = gen_preprocess_options (macros, include_dirs) + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + macros, objects, extra_postargs, pp_opts, build = \ + self._setup_compile(output_dir, macros, include_dirs, sources, + depends, extra_postargs) + compile_opts = extra_preargs or [] compile_opts.append ('/c') if debug: - compile_opts.extend (self.compile_options_debug) + compile_opts.extend(self.compile_options_debug) else: - compile_opts.extend (self.compile_options) - - for i in range (len (sources)): - src = sources[i] ; obj = objects[i] - ext = (os.path.splitext (src))[1] - - if skip_sources[src]: - log.debug("skipping %s (%s up-to-date)", src, obj) - else: - self.mkpath (os.path.dirname (obj)) + compile_opts.extend(self.compile_options) - if debug: - # pass the full pathname to MSVC in debug mode, - # this allows the debugger to find the source file - # without asking the user to browse for it - src = os.path.abspath(src) - - if ext in self._c_extensions: - input_opt = "/Tc" + src - elif ext in self._cpp_extensions: - input_opt = "/Tp" + src - elif ext in self._rc_extensions: - # compile .RC to .RES file - input_opt = src - output_opt = "/fo" + obj - try: - self.spawn ([self.rc] + - [output_opt] + [input_opt]) - except DistutilsExecError, msg: - raise CompileError, msg - continue - elif ext in self._mc_extensions: - - # Compile .MC to .RC file to .RES file. - # * '-h dir' specifies the directory for the - # generated include file - # * '-r dir' specifies the target directory of the - # generated RC file and the binary message resource - # it includes - # - # For now (since there are no options to change this), - # we use the source-directory for the include file and - # the build directory for the RC file and message - # resources. This works at least for win32all. - - h_dir = os.path.dirname (src) - rc_dir = os.path.dirname (obj) - try: - # first compile .MC to .RC and .H file - self.spawn ([self.mc] + - ['-h', h_dir, '-r', rc_dir] + [src]) - base, _ = os.path.splitext (os.path.basename (src)) - rc_file = os.path.join (rc_dir, base + '.rc') - # then compile .RC to .RES file - self.spawn ([self.rc] + - ["/fo" + obj] + [rc_file]) - - except DistutilsExecError, msg: - raise CompileError, msg - continue - else: - # how to handle this file? - raise CompileError ( - "Don't know how to compile %s to %s" % \ - (src, obj)) + for obj, (src, ext) in build.items(): + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn ([self.rc] + + [output_opt] + [input_opt]) + except DistutilsExecError, msg: + raise CompileError, msg + continue + elif ext in self._mc_extensions: - output_opt = "/Fo" + obj + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + + h_dir = os.path.dirname (src) + rc_dir = os.path.dirname (obj) try: - self.spawn ([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs) + # first compile .MC to .RC and .H file + self.spawn ([self.mc] + + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext (os.path.basename (src)) + rc_file = os.path.join (rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn ([self.rc] + + ["/fo" + obj] + [rc_file]) + except DistutilsExecError, msg: raise CompileError, msg + continue + else: + # how to handle this file? + raise CompileError ( + "Don't know how to compile %s to %s" % \ + (src, obj)) + + output_opt = "/Fo" + obj + try: + self.spawn ([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg return objects diff --git a/unixccompiler.py b/unixccompiler.py index abf7a264..c887a882 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -107,35 +107,19 @@ class UnixCCompiler(CCompiler): def compile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None): - output_dir, macros, include_dirs = \ - self._fix_compile_args(output_dir, macros, include_dirs) - objects, skip_sources = self._prep_compile(sources, output_dir) + extra_preargs=None, extra_postargs=None, depends=None): + + macros, objects, extra_postargs, pp_opts, build = \ + self._setup_compile(output_dir, macros, include_dirs, sources, + depends, extra_postargs) + cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - # Figure out the options for the compiler command line. - pp_opts = gen_preprocess_options(macros, include_dirs) - cc_args = pp_opts + ['-c'] - if debug: - cc_args[:0] = ['-g'] - if extra_preargs: - cc_args[:0] = extra_preargs - if extra_postargs is None: - extra_postargs = [] - - # Compile all source files that weren't eliminated by - # '_prep_compile()'. - for i in range(len(sources)): - src = sources[i] - obj = objects[i] - if skip_sources[src]: - log.debug("skipping %s (%s up-to-date)", src, obj) - else: - self.mkpath(os.path.dirname(obj)) - try: - self.spawn(self.compiler_so + cc_args + - [src, '-o', obj] + extra_postargs) - except DistutilsExecError, msg: - raise CompileError, msg + for obj, (src, ext) in build.items(): + try: + self.spawn(self.compiler_so + cc_args + + [src, '-o', obj] + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg # Return *all* object filenames, not just the ones we just built. return objects -- cgit v1.2.1 From 2c0f494c5782d2208257d403c1e4eecef2ef3305 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 17:32:20 +0000 Subject: Extend dependency tracking so that .o files are rebuilt. Two new tests are needed: Don't skip building an extension if any of the depends files are newer than the target. Pass ext.depends to compiler.compile() so that it can track individual files. --- command/build_ext.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 21dd0dcd..943f30a4 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -388,7 +388,6 @@ class build_ext (Command): # get_outputs () def build_extensions(self): - # First, sanity-check the 'extensions' list self.check_extensions_list(self.extensions) @@ -396,7 +395,6 @@ class build_ext (Command): self.build_extension(ext) def build_extension(self, ext): - sources = ext.sources if sources is None or type(sources) not in (ListType, TupleType): raise DistutilsSetupError, \ @@ -421,7 +419,6 @@ class build_ext (Command): else: ext_filename = os.path.join(self.build_lib, self.get_ext_filename(fullname)) - depends = sources + ext.depends if not (self.force or newer_group(depends, ext_filename, 'newer')): log.debug("skipping '%s' extension (up-to-date)", ext.name) @@ -467,7 +464,8 @@ class build_ext (Command): macros=macros, include_dirs=ext.include_dirs, debug=self.debug, - extra_postargs=extra_args) + extra_postargs=extra_args, + depends=ext.depends) # XXX -- this is a Vile HACK! # -- cgit v1.2.1 From 39105a6df67fb823a7f86283f0b514cccb7d708c Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 18 Jun 2002 18:40:54 +0000 Subject: Only import msvccompiler on win32 platforms. --- command/build_ext.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 943f30a4..6b6f2c7d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -624,17 +624,17 @@ class build_ext (Command): # pyconfig.h that MSVC groks. The other Windows compilers all seem # to need it mentioned explicitly, though, so that's what we do. # Append '_d' to the python import library on debug builds. - from distutils.msvccompiler import MSVCCompiler - if sys.platform == "win32" and \ - not isinstance(self.compiler, MSVCCompiler): - template = "python%d%d" - if self.debug: - template = template + '_d' - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - # don't extend ext.libraries, it may be shared with other - # extensions, it is a reference to the original list - return ext.libraries + [pythonlib] + if sys.platform == "win32": + from distutils.msvccompiler import MSVCCompiler + if not isinstance(self.compiler, MSVCCompiler): + template = "python%d%d" + if self.debug: + template = template + '_d' + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] elif sys.platform == "os2emx": # EMX/GCC requires the python library explicitly, and I # believe VACPP does as well (though not confirmed) - AIM Apr01 -- cgit v1.2.1 From 34d756600f62023716bf0d11c1d4df69fe434199 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 18 Jun 2002 18:42:41 +0000 Subject: Add a default implementation of compile() to the base class. The default implementation calls _compile() to compile individual files. This method must be implemented by the subclass. This change factors out most of the remaining common code in all the compilers except mwerks. --- ccompiler.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 50246b89..f1a50cbd 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -644,8 +644,27 @@ class CCompiler: Raises CompileError on failure. """ - pass + # A concrete compiler class can either override this method + # entirely or implement _compile(). + + macros, objects, extra_postargs, pp_opts, build = \ + self._setup_compile(output_dir, macros, include_dirs, sources, + depends, extra_postargs) + cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) + + for obj, (src, ext) in build.items(): + self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) + + # Return *all* object filenames, not just the ones we just built. + return objects + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + """Compile 'src' to product 'obj'.""" + + # A concrete compiler class that does not override compile() + # should implement _compile(). + pass def create_static_lib (self, objects, -- cgit v1.2.1 From 0fac9b6c9635aa053f92312b8a489ab8b2fc502f Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 18 Jun 2002 18:48:55 +0000 Subject: Add implementation of _compile() and use default compile() method. --- cygwinccompiler.py | 45 ++++++++++++++------------------------------- emxccompiler.py | 48 +++++++++++++----------------------------------- unixccompiler.py | 24 ++++++------------------ 3 files changed, 33 insertions(+), 84 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 302293ac..9aabd8a6 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -113,37 +113,20 @@ class CygwinCCompiler (UnixCCompiler): # __init__ () - # not much different of the compile method in UnixCCompiler, - # but we have to insert some lines in the middle of it, so - # we put here a adapted version of it. - # (If we would call compile() in the base class, it would do some - # initializations a second time, this is why all is done here.) - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - macros, objects, extra_postargs, pp_opts, build = \ - self._setup_compile(output_dir, macros, include_dirs, sources, - depends, extra_postargs) - cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - - for obj, (src, ext) in build.items(): - if ext == '.rc' or ext == '.res': - # gcc needs '.res' and '.rc' compiled to object files !!! - try: - self.spawn (["windres","-i",src,"-o",obj]) - except DistutilsExecError, msg: - raise CompileError, msg - else: # for other files use the C-compiler - try: - self.spawn (self.compiler_so + cc_args + - [src, '-o', obj] + - extra_postargs) - except DistutilsExecError, msg: - raise CompileError, msg - - # Return *all* object filenames, not just the ones we just built. - return objects + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + if ext == '.rc' or ext == '.res': + # gcc needs '.res' and '.rc' compiled to object files !!! + try: + self.spawn(["windres", "-i", src, "-o", obj]) + except DistutilsExecError, msg: + raise CompileError, msg + else: # for other files use the C-compiler + try: + self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg def link (self, target_desc, diff --git a/emxccompiler.py b/emxccompiler.py index c2c73b0d..2dc4fbd0 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -76,41 +76,19 @@ class EMXCCompiler (UnixCCompiler): # __init__ () - # not much different of the compile method in UnixCCompiler, - # but we have to insert some lines in the middle of it, so - # we put here a adapted version of it. - # (If we would call compile() in the base class, it would do some - # initializations a second time, this is why all is done here.) - - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - macros, objects, extra_postargs, pp_opts, build = \ - self._setup_compile(output_dir, macros, include_dirs, sources, - depends, extra_postargs) - cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - - for obj, (src, ext) in build.items(): - if ext == '.rc': - # gcc requires '.rc' compiled to binary ('.res') files !!! - try: - self.spawn (["rc","-r",src]) - except DistutilsExecError, msg: - raise CompileError, msg - else: # for other files use the C-compiler - try: - self.spawn (self.compiler_so + cc_args + - [src, '-o', obj] + - extra_postargs) - except DistutilsExecError, msg: - raise CompileError, msg - - # Return *all* object filenames, not just the ones we just built. - return objects - - # compile () - + def _compile(self, obj, src, ext, cc_args, extra_postargs): + if ext == '.rc': + # gcc requires '.rc' compiled to binary ('.res') files !!! + try: + self.spawn(["rc", "-r", src]) + except DistutilsExecError, msg: + raise CompileError, msg + else: # for other files use the C-compiler + try: + self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg def link (self, target_desc, diff --git a/unixccompiler.py b/unixccompiler.py index c887a882..d94c3840 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -105,24 +105,12 @@ class UnixCCompiler(CCompiler): except DistutilsExecError, msg: raise CompileError, msg - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - macros, objects, extra_postargs, pp_opts, build = \ - self._setup_compile(output_dir, macros, include_dirs, sources, - depends, extra_postargs) - cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - - for obj, (src, ext) in build.items(): - try: - self.spawn(self.compiler_so + cc_args + - [src, '-o', obj] + extra_postargs) - except DistutilsExecError, msg: - raise CompileError, msg - - # Return *all* object filenames, not just the ones we just built. - return objects + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + try: + self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError, msg: + raise CompileError, msg def create_static_lib(self, objects, output_libname, output_dir=None, debug=0): -- cgit v1.2.1 From 38242c6fdae57a47aeb70eda52f3b6f2b651900d Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 18 Jun 2002 19:08:40 +0000 Subject: Define NDEBUG for releae builds, just like Python. XXX Why doesn't distutils on Windows use the same set of flags as Python? --- msvccompiler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index 8460eea9..65b11435 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -233,7 +233,8 @@ class MSVCCompiler (CCompiler) : self.mc = "mc.exe" self.preprocess_options = None - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ] + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , + '/DNDEBUG'] self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', '/Z7', '/D_DEBUG'] -- cgit v1.2.1 From a0559d181e1cc8a522f7d416edde440d306b4818 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Wed, 26 Jun 2002 15:00:29 +0000 Subject: This module broke on the Mac (where it can't work, but distutils seems to import it anyway) because it imported pwd and grp. Moved the import to inside the routine where they're used. --- command/bdist_pkgtool.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/bdist_pkgtool.py b/command/bdist_pkgtool.py index 51b89d97..3b8ca2d2 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -6,7 +6,7 @@ Author: Mark W. Alexander Implements the Distutils 'bdist_pkgtool' command (create Solaris pkgtool distributions).""" -import os, string, sys, pwd, grp +import os, string, sys from types import * from distutils.util import get_platform from distutils.file_util import write_file @@ -281,6 +281,7 @@ class bdist_pkgtool (bdist_packager.bdist_packager): def _make_prototype(self): + import pwd, grp proto_file = ["i pkginfo"] if self.request: proto_file.extend(['i request']) -- cgit v1.2.1 From 88e0aef39dbc5f117ac936aa266650e7f6ab7079 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Wed, 26 Jun 2002 15:42:49 +0000 Subject: Fixed various MacPython-specific issues found by attempting to use the standard core setup.py for MacPython. --- mwerkscompiler.py | 34 +++++++++++++++++++++++++++++++--- sysconfig.py | 7 ++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 1f71c60c..5f567b99 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -161,7 +161,7 @@ class MWerksCompiler (CCompiler) : return # Build the export file exportfilename = os.path.join(build_temp, exportname) - log.debug("\tCreate export file", exportfilename) + log.debug("\tCreate export file %s", exportfilename) fp = open(exportfilename, 'w') fp.write('%s\n'%export_symbols[0]) fp.close() @@ -182,7 +182,7 @@ class MWerksCompiler (CCompiler) : # because we pass this pathname to CodeWarrior in an AppleEvent, and CW # doesn't have a clue about our working directory. xmlfilename = os.path.join(os.getcwd(), os.path.join(build_temp, xmlname)) - log.debug("\tCreate XML file", xmlfilename) + log.debug("\tCreate XML file %s", xmlfilename) xmlbuilder = mkcwproject.cwxmlgen.ProjectBuilder(settings) xmlbuilder.generate() xmldata = settings['tmp_projectxmldata'] @@ -191,7 +191,7 @@ class MWerksCompiler (CCompiler) : fp.close() # Generate the project. Again a full pathname. projectfilename = os.path.join(os.getcwd(), os.path.join(build_temp, projectname)) - log.debug('\tCreate project file', projectfilename) + log.debug('\tCreate project file %s', projectfilename) mkcwproject.makeproject(xmlfilename, projectfilename) # And build it log.debug('\tBuild project') @@ -213,3 +213,31 @@ class MWerksCompiler (CCompiler) : if components[i] == '..': components[i] = '' return string.join(components, ':') + + def library_dir_option (self, dir): + """Return the compiler option to add 'dir' to the list of + directories searched for libraries. + """ + return # XXXX Not correct... + + def runtime_library_dir_option (self, dir): + """Return the compiler option to add 'dir' to the list of + directories searched for runtime libraries. + """ + # Nothing needed or Mwerks/Mac. + return + + def library_option (self, lib): + """Return the compiler option to add 'dir' to the list of libraries + linked into the shared library or executable. + """ + return + + def find_library_file (self, dirs, lib, debug=0): + """Search the specified list of directories for a static or shared + library file 'lib' and return the full path to that file. If + 'debug' true, look for a debugging version (if that makes sense on + the current platform). Return None if 'lib' wasn't found in any of + the specified directories. + """ + return 0 diff --git a/sysconfig.py b/sysconfig.py index 847b8724..74394abc 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -66,7 +66,10 @@ def get_python_inc(plat_specific=0, prefix=None): elif os.name == "nt": return os.path.join(prefix, "include") elif os.name == "mac": - return os.path.join(prefix, "Include") + if plat_specific: + return os.path.join(prefix, "Mac", "Include") + else: + return os.path.join(prefix, "Include") elif os.name == "os2": return os.path.join(prefix, "Include") else: @@ -403,6 +406,8 @@ def _init_mac(): g['install_lib'] = os.path.join(EXEC_PREFIX, "Lib") g['install_platlib'] = os.path.join(EXEC_PREFIX, "Mac", "Lib") + # These are used by the extension module build + g['srcdir'] = ':' global _config_vars _config_vars = g -- cgit v1.2.1 From a88699b8cec97f6868dbde077637389919a41ae7 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Wed, 26 Jun 2002 22:05:33 +0000 Subject: Whitespace normalization (remove tabs) --- sysconfig.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 74394abc..e9b728ea 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -66,10 +66,10 @@ def get_python_inc(plat_specific=0, prefix=None): elif os.name == "nt": return os.path.join(prefix, "include") elif os.name == "mac": - if plat_specific: - return os.path.join(prefix, "Mac", "Include") - else: - return os.path.join(prefix, "Include") + if plat_specific: + return os.path.join(prefix, "Mac", "Include") + else: + return os.path.join(prefix, "Include") elif os.name == "os2": return os.path.join(prefix, "Include") else: -- cgit v1.2.1 From 04369bde233174b17d0d9494125511641c3e69f9 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Thu, 27 Jun 2002 22:10:19 +0000 Subject: The standard definition file is now called mwerks_shcarbon_plugin.h. --- mwerkscompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 5f567b99..cd66a099 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -169,7 +169,7 @@ class MWerksCompiler (CCompiler) : if self.__macros: prefixfilename = os.path.join(os.getcwd(), os.path.join(build_temp, prefixname)) fp = open(prefixfilename, 'w') - fp.write('#include "mwerks_plugin_config.h"\n') + fp.write('#include "mwerks_shcarbon_config.h"\n') for name, value in self.__macros: if value is None: fp.write('#define %s\n'%name) -- cgit v1.2.1 From b15291038df11afa687d1e12480e0901a6567ed4 Mon Sep 17 00:00:00 2001 From: "Michael W. Hudson" Date: Fri, 12 Jul 2002 09:16:44 +0000 Subject: Well, Fred never did explain why the code to determine whether the calling Python was installed was so complicated, so I simplified it. This should get the snake-farm's build scripts working again. --- sysconfig.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index e9b728ea..48672d6a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -29,13 +29,9 @@ EXEC_PREFIX = os.path.normpath(sys.exec_prefix) argv0_path = os.path.dirname(os.path.abspath(sys.executable)) landmark = os.path.join(argv0_path, "Modules", "Setup") -if not os.path.isfile(landmark): - python_build = 0 -elif os.path.isfile(os.path.join(argv0_path, "Lib", "os.py")): - python_build = 1 -else: - python_build = os.path.isfile(os.path.join(os.path.dirname(argv0_path), - "Lib", "os.py")) + +python_build = os.path.isfile(landmark) + del argv0_path, landmark -- cgit v1.2.1 From 3f0272e6f1efa1a3e4faf5afbf1bdc830ecf404f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 28 Jul 2002 10:49:37 +0000 Subject: Patch #543498: Use License: field instead of Copyright:. --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 808ddc14..dd9dc661 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -348,7 +348,7 @@ class bdist_rpm (Command): spec_file.append('Source0: %{name}-%{version}.tar.gz') spec_file.extend([ - 'Copyright: ' + self.distribution.get_license(), + 'License: ' + self.distribution.get_license(), 'Group: ' + self.group, 'BuildRoot: %{_tmppath}/%{name}-buildroot', 'Prefix: %{_prefix}', ]) -- cgit v1.2.1 From 9ffafc4502cbaf28984a9eb1fa3191b70607a678 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 29 Jul 2002 12:11:18 +0000 Subject: Recompiled the exe and updated bdist_wininst.py. --- command/bdist_wininst.py | 677 ++++++++++++++++++++++++----------------------- 1 file changed, 343 insertions(+), 334 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index f018f461..71e51bfd 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -261,356 +261,365 @@ if __name__ == '__main__': EXEDATA = """\ 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 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgAAACAAAIAFAAAAYAAAgAAAAAAA -AAAAAAAAAAAAAQBuAAAAOAAAgAAAAAAAAAAAAAAAAAAAAQAAAAAAUAAAADDBAAAICgAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAQAawAAAJAAAIBsAAAAuAAAgG0AAADgAACAbgAAAAgBAIAAAAAAAAAA -AAAAAAAAAAEACQQAAKgAAAA4ywAAoAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAADQAAAA -2MwAAAQCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJBAAA+AAAAODOAABaAgAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAEACQQAACABAABA0QAAFAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAIBANAB -AQAAAAAAAAAAAAAAAAAdAgEA4AEBAAAAAAAAAAAAAAAAACoCAQDoAQEAAAAAAAAAAAAAAAAANwIB -APABAQAAAAAAAAAAAAAAAABBAgEA+AEBAAAAAAAAAAAAAAAAAEwCAQAAAgEAAAAAAAAAAAAAAAAA -VgIBAAgCAQAAAAAAAAAAAAAAAAAAAAAAAAAAAGACAQBuAgEAfgIBAAAAAACMAgEAAAAAAJoCAQAA -AAAAqgIBAAAAAAC0AgEAAAAAALoCAQAAAAAAyAIBAAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIu -ZGxsAENPTUNUTDMyLmRsbABHREkzMi5kbGwATVNWQ1JULmRsbABvbGUzMi5kbGwAVVNFUjMyLmRs -bAAATG9hZExpYnJhcnlBAABHZXRQcm9jQWRkcmVzcwAARXhpdFByb2Nlc3MAAABSZWdDbG9zZUtl -eQAAAFByb3BlcnR5U2hlZXRBAABUZXh0T3V0QQAAZXhpdAAAQ29Jbml0aWFsaXplAABHZXREQwAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgACAAAAIAAAgAUAAABgAACAAAAAAAAA +AAAAAAAAAAABAG4AAAA4AACAAAAAAAAAAAAAAAAAAAABAAAAAABQAAAAMNEAAAgKAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAABABrAAAAkAAAgGwAAAC4AACAbQAAAOAAAIBuAAAACAEAgAAAAAAAAAAA +AAAAAAAAAQAJBAAAqAAAADjbAACgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAANAAAADY +3AAABAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAAD4AAAA4N4AAFoCAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAQAJBAAAIAEAAEDhAAAUAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsEgEA5BEB +AAAAAAAAAAAAAAAAADkSAQD0EQEAAAAAAAAAAAAAAAAARhIBAPwRAQAAAAAAAAAAAAAAAABTEgEA +BBIBAAAAAAAAAAAAAAAAAF0SAQAMEgEAAAAAAAAAAAAAAAAAaBIBABQSAQAAAAAAAAAAAAAAAABy +EgEAHBIBAAAAAAAAAAAAAAAAAH4SAQAkEgEAAAAAAAAAAAAAAAAAAAAAAAAAAACIEgEAlhIBAKYS +AQAAAAAAtBIBAAAAAADCEgEAAAAAANISAQAAAAAA3BIBAAAAAADiEgEAAAAAAPASAQAAAAAAChMB +AAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIuZGxsAENPTUNUTDMyLmRsbABHREkzMi5kbGwATVNW +Q1JULmRsbABvbGUzMi5kbGwAU0hFTEwzMi5kbGwAVVNFUjMyLmRsbAAATG9hZExpYnJhcnlBAABH +ZXRQcm9jQWRkcmVzcwAARXhpdFByb2Nlc3MAAABSZWdDbG9zZUtleQAAAFByb3BlcnR5U2hlZXRB +AABUZXh0T3V0QQAAZXhpdAAAQ29Jbml0aWFsaXplAABTSEdldFNwZWNpYWxGb2xkZXJQYXRoQQAA +AEdldERDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAA= """ # --- EOF --- -- cgit v1.2.1 From c715e1d18cfc2b74b8728ab418cb7dc9d7433bdf Mon Sep 17 00:00:00 2001 From: Andrew MacIntyre Date: Sun, 4 Aug 2002 06:17:08 +0000 Subject: add parameter missing following Jeremy's compiler class refactoring --- emxccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emxccompiler.py b/emxccompiler.py index 2dc4fbd0..91920eb3 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -76,7 +76,7 @@ class EMXCCompiler (UnixCCompiler): # __init__ () - def _compile(self, obj, src, ext, cc_args, extra_postargs): + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): if ext == '.rc': # gcc requires '.rc' compiled to binary ('.res') files !!! try: -- cgit v1.2.1 From 3dcfe36f1c82f73bfad7d1eb3c3c8188de577415 Mon Sep 17 00:00:00 2001 From: Andrew MacIntyre Date: Sun, 4 Aug 2002 06:21:25 +0000 Subject: - comment improvement - implement viable library search routine for EMX --- emxccompiler.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/emxccompiler.py b/emxccompiler.py index 91920eb3..9cd9600c 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -177,7 +177,8 @@ class EMXCCompiler (UnixCCompiler): # -- Miscellaneous methods ----------------------------------------- - # overwrite the one from CCompiler to support rc and res-files + # override the object_filenames method from CCompiler to + # support rc and res-files def object_filenames (self, source_filenames, strip_dir=0, @@ -204,6 +205,29 @@ class EMXCCompiler (UnixCCompiler): # object_filenames () + # override the find_library_file method from UnixCCompiler + # to deal with file naming/searching differences + def find_library_file(self, dirs, lib, debug=0): + shortlib = '%s.lib' % lib + longlib = 'lib%s.lib' % lib # this form very rare + + # get EMX's default library directory search path + try: + emx_dirs = os.environ['LIBRARY_PATH'].split(';') + except KeyError: + emx_dirs = [] + + for dir in dirs + emx_dirs: + shortlibp = os.path.join(dir, shortlib) + longlibp = os.path.join(dir, longlib) + if os.path.exists(shortlibp): + return shortlibp + elif os.path.exists(longlibp): + return longlibp + + # Oops, didn't find it in *any* of 'dirs' + return None + # class EMXCCompiler -- cgit v1.2.1 From cec536c16d4f1efa7f0ee0c2cb56805f9aac6b69 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 9 Aug 2002 16:38:32 +0000 Subject: Massive changes from SF 589982 (tempfile.py rewrite, by Zack Weinberg). This changes all uses of deprecated tempfile functions to the recommended ones. --- command/bdist_wininst.py | 7 ++++--- util.py | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 71e51bfd..b5383198 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -130,8 +130,9 @@ class bdist_wininst (Command): # And make an archive relative to the root of the # pseudo-installation tree. - from tempfile import mktemp - archive_basename = mktemp() + from tempfile import NamedTemporaryFile + arc = NamedTemporaryFile(".zip") + archive_basename = arc.name[:-4] fullname = self.distribution.get_fullname() arcname = self.make_archive(archive_basename, "zip", root_dir=self.bdist_dir) @@ -139,7 +140,7 @@ class bdist_wininst (Command): self.create_exe(arcname, fullname, self.bitmap) # remove the zip-file again log.debug("removing temporary file '%s'", arcname) - os.remove(arcname) + arc.close() if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) diff --git a/util.py b/util.py index 23c29ebb..d9c62248 100644 --- a/util.py +++ b/util.py @@ -359,11 +359,11 @@ def byte_compile (py_files, # "Indirect" byte-compilation: write a temporary script and then # run it with the appropriate flags. if not direct: - from tempfile import mktemp - script_name = mktemp(".py") + from tempfile import mkstemp + (script_fd, script_name) = mkstemp(".py") log.info("writing byte-compilation script '%s'", script_name) if not dry_run: - script = open(script_name, "w") + script = os.fdopen(script_fd, "w") script.write("""\ from distutils.util import byte_compile -- cgit v1.2.1 From cf18671a7339251d55d6fcec6653e359574c2599 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Tue, 13 Aug 2002 17:42:57 +0000 Subject: SF bug #574235, convert_path fails with empty pathname --- util.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index d9c62248..8d224154 100644 --- a/util.py +++ b/util.py @@ -84,9 +84,11 @@ def convert_path (pathname): """ if os.sep == '/': return pathname - if pathname and pathname[0] == '/': + if not pathname: + return pathname + if pathname[0] == '/': raise ValueError, "path '%s' cannot be absolute" % pathname - if pathname and pathname[-1] == '/': + if pathname[-1] == '/': raise ValueError, "path '%s' cannot end with '/'" % pathname paths = string.split(pathname, '/') -- cgit v1.2.1 From 6dddf6bec77b5dcf69da39d556471f094eb8ecd8 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 9 Sep 2002 12:10:00 +0000 Subject: Include an empty body when checking for a header file (Bugfix candidate for 2.2, and likely 2.1 as well) --- command/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/config.py b/command/config.py index d74aa6a2..88b15866 100644 --- a/command/config.py +++ b/command/config.py @@ -346,7 +346,8 @@ class config (Command): exists and can be found by the preprocessor; return true if so, false otherwise. """ - return self.try_cpp(headers=[header], include_dirs=include_dirs) + return self.try_cpp(body="/* No body */", headers=[header], + include_dirs=include_dirs) # class config -- cgit v1.2.1 From 23ad69adc2c9dd95e91445962900405da7115026 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 9 Sep 2002 12:16:58 +0000 Subject: The .preprocess() method didn't work, because it didn't add the input file to the command-line arguments. Fix this by adding the source filename. --- unixccompiler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/unixccompiler.py b/unixccompiler.py index d94c3840..831717ba 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -92,6 +92,7 @@ class UnixCCompiler(CCompiler): pp_args[:0] = extra_preargs if extra_postargs: pp_args.extend(extra_postargs) + pp_args.append(source) # We need to preprocess: either we're being forced to, or we're # generating output to stdout, or there's a target output file and -- cgit v1.2.1 From dfe0dcd95667389986923e0815e82009a3137729 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 11 Sep 2002 16:28:52 +0000 Subject: Define DEBUG in a separate module to resolve circular references. --- debug.py | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 debug.py diff --git a/debug.py b/debug.py new file mode 100644 index 00000000..7ca76d6c --- /dev/null +++ b/debug.py @@ -0,0 +1,6 @@ +import os + +# If DISTUTILS_DEBUG is anything other than the empty string, we run in +# debug mode. +DEBUG = os.environ.get('DISTUTILS_DEBUG') + -- cgit v1.2.1 From 788c442e3f321f5824f388132538e2cc1350fb35 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 11 Sep 2002 16:31:53 +0000 Subject: Use distutils.debug.DEBUG instead of distutils.core.DEBUG. Note that distutils.core.DEBUG still works if client code uses it, but the core code avoids circular references by using distutils.debug. --- ccompiler.py | 2 +- cmd.py | 2 +- command/bdist_rpm.py | 3 ++- command/install.py | 3 ++- core.py | 6 +----- dist.py | 2 +- filelist.py | 2 +- 7 files changed, 9 insertions(+), 11 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index f1a50cbd..5a0641ea 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -923,7 +923,7 @@ class CCompiler: log.debug(msg) def debug_print (self, msg): - from distutils.core import DEBUG + from distutils.debug import DEBUG if DEBUG: print msg diff --git a/cmd.py b/cmd.py index 25ff3025..6a268184 100644 --- a/cmd.py +++ b/cmd.py @@ -198,7 +198,7 @@ class Command: """Print 'msg' to stdout if the global DEBUG (taken from the DISTUTILS_DEBUG environment variable) flag is true. """ - from distutils.core import DEBUG + from distutils.debug import DEBUG if DEBUG: print msg sys.stdout.flush() diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index dd9dc661..bbaad7dc 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -10,7 +10,8 @@ __revision__ = "$Id$" import sys, os, string import glob from types import * -from distutils.core import Command, DEBUG +from distutils.core import Command +from distutils.debug import DEBUG from distutils.util import get_platform from distutils.file_util import write_file from distutils.errors import * diff --git a/command/install.py b/command/install.py index 322177f4..67f37893 100644 --- a/command/install.py +++ b/command/install.py @@ -10,7 +10,8 @@ __revision__ = "$Id$" import sys, os, string from types import * -from distutils.core import Command, DEBUG +from distutils.core import Command +from distutils.debug import DEBUG from distutils.sysconfig import get_config_vars from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file diff --git a/core.py b/core.py index 8a348ce3..9a6bff6b 100644 --- a/core.py +++ b/core.py @@ -13,10 +13,7 @@ __revision__ = "$Id$" import sys, os from types import * -# If DISTUTILS_DEBUG is anything other than the empty string, we run in -# debug mode. -DEBUG = os.environ.get('DISTUTILS_DEBUG') - +from distutils.debug import DEBUG from distutils.errors import * from distutils.util import grok_environment_error @@ -25,7 +22,6 @@ from distutils.dist import Distribution from distutils.cmd import Command from distutils.extension import Extension - # This is a barebones help message generated displayed when the user # runs the setup script with no arguments at all. More useful help # is generated with various --help options: global help, list commands, diff --git a/dist.py b/dist.py index f995d58e..92cb8320 100644 --- a/dist.py +++ b/dist.py @@ -16,7 +16,7 @@ from distutils.errors import * from distutils.fancy_getopt import FancyGetopt, translate_longopt from distutils.util import check_environ, strtobool, rfc822_escape from distutils import log -from distutils.core import DEBUG +from distutils.debug import DEBUG # Regex to define acceptable Distutils command names. This is not *quite* # the same as a Python NAME -- I don't allow leading underscores. The fact diff --git a/filelist.py b/filelist.py index e2e2457c..b4f3269c 100644 --- a/filelist.py +++ b/filelist.py @@ -54,7 +54,7 @@ class FileList: """Print 'msg' to stdout if the global DEBUG (taken from the DISTUTILS_DEBUG environment variable) flag is true. """ - from distutils.core import DEBUG + from distutils.debug import DEBUG if DEBUG: print msg -- cgit v1.2.1 From 1e573796bb675500c52d99361415982b0f40f193 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 29 Sep 2002 00:25:51 +0000 Subject: Whitespace normalization (get rid of tabs). --- emxccompiler.py | 57 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/emxccompiler.py b/emxccompiler.py index 9cd9600c..7c3ad025 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -16,7 +16,7 @@ handles the EMX port of the GNU C compiler to OS/2. # of Python is only distributed with threads enabled. # # tested configurations: -# +# # * EMX gcc 2.81/EMX 0.9d fix03 # created 2001/5/7, Andrew MacIntyre, from Rene Liebscher's cywinccompiler.py @@ -40,7 +40,7 @@ class EMXCCompiler (UnixCCompiler): shared_lib_format = "%s%s" res_extension = ".res" # compiled resource file exe_extension = ".exe" - + def __init__ (self, verbose=0, dry_run=0, @@ -56,11 +56,11 @@ class EMXCCompiler (UnixCCompiler): "Python's pyconfig.h doesn't seem to support your compiler. " + ("Reason: %s." % details) + "Compiling may fail because of undefined preprocessor macros.") - + (self.gcc_version, self.ld_version) = \ get_versions() self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" % - (self.gcc_version, + (self.gcc_version, self.ld_version) ) # Hard-code GCC because that's what this is all about. @@ -73,7 +73,7 @@ class EMXCCompiler (UnixCCompiler): # want the gcc library statically linked (so that we don't have # to distribute a version dependent on the compiler we have) self.dll_libraries=["gcc"] - + # __init__ () def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): @@ -83,7 +83,7 @@ class EMXCCompiler (UnixCCompiler): self.spawn(["rc", "-r", src]) except DistutilsExecError, msg: raise CompileError, msg - else: # for other files use the C-compiler + else: # for other files use the C-compiler try: self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs) @@ -103,12 +103,12 @@ class EMXCCompiler (UnixCCompiler): extra_preargs=None, extra_postargs=None, build_temp=None): - + # use separate copies, so we can modify the lists extra_preargs = copy.copy(extra_preargs or []) libraries = copy.copy(libraries or []) objects = copy.copy(objects or []) - + # Additional libraries libraries.extend(self.dll_libraries) @@ -118,10 +118,10 @@ class EMXCCompiler (UnixCCompiler): (target_desc != self.EXECUTABLE)): # (The linker doesn't do anything if output is up-to-date. # So it would probably better to check if we really need this, - # but for this we had to insert some unchanged parts of - # UnixCCompiler, and this is not what we want.) + # but for this we had to insert some unchanged parts of + # UnixCCompiler, and this is not what we want.) - # we want to put some files in the same directory as the + # we want to put some files in the same directory as the # object files are, build_temp doesn't help much # where are the object files temp_dir = os.path.dirname(objects[0]) @@ -131,7 +131,7 @@ class EMXCCompiler (UnixCCompiler): # generate the filenames for these files def_file = os.path.join(temp_dir, dll_name + ".def") - + # Generate .def file contents = [ "LIBRARY %s INITINSTANCE TERMINSTANCE" % \ @@ -144,21 +144,21 @@ class EMXCCompiler (UnixCCompiler): "writing %s" % def_file) # next add options for def-file and to creating import libraries - # for gcc/ld the def-file is specified as any other object files + # for gcc/ld the def-file is specified as any other object files objects.append(def_file) #end: if ((export_symbols is not None) and # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): - + # who wants symbols and a many times larger output file - # should explicitly switch the debug mode on + # should explicitly switch the debug mode on # otherwise we let dllwrap/ld strip the output file - # (On my machine: 10KB < stripped_file < ??100KB + # (On my machine: 10KB < stripped_file < ??100KB # unstripped_file = stripped_file + XXX KB - # ( XXX=254 for a typical python extension)) - if not debug: - extra_preargs.append("-s") - + # ( XXX=254 for a typical python extension)) + if not debug: + extra_preargs.append("-s") + UnixCCompiler.link(self, target_desc, objects, @@ -172,7 +172,7 @@ class EMXCCompiler (UnixCCompiler): extra_preargs, extra_postargs, build_temp) - + # link () # -- Miscellaneous methods ----------------------------------------- @@ -196,7 +196,7 @@ class EMXCCompiler (UnixCCompiler): base = os.path.basename (base) if ext == '.rc': # these need to be compiled to object files - obj_names.append (os.path.join (output_dir, + obj_names.append (os.path.join (output_dir, base + self.res_extension)) else: obj_names.append (os.path.join (output_dir, @@ -216,7 +216,7 @@ class EMXCCompiler (UnixCCompiler): emx_dirs = os.environ['LIBRARY_PATH'].split(';') except KeyError: emx_dirs = [] - + for dir in dirs + emx_dirs: shortlibp = os.path.join(dir, shortlib) longlibp = os.path.join(dir, longlib) @@ -224,7 +224,7 @@ class EMXCCompiler (UnixCCompiler): return shortlibp elif os.path.exists(longlibp): return longlibp - + # Oops, didn't find it in *any* of 'dirs' return None @@ -266,15 +266,15 @@ def check_config_h(): # GCC, and the pyconfig.h file should be OK if string.find(sys.version,"GCC") >= 0: return (CONFIG_H_OK, "sys.version mentions 'GCC'") - + fn = sysconfig.get_config_h_filename() try: # It would probably better to read single lines to search. - # But we do this only once, and it is fast enough + # But we do this only once, and it is fast enough f = open(fn) s = f.read() f.close() - + except IOError, exc: # if we can't read this file, we cannot say it is wrong # the compiler will complain later about this file as missing @@ -296,7 +296,7 @@ def get_versions(): from distutils.version import StrictVersion from distutils.spawn import find_executable import re - + gcc_exe = find_executable('gcc') if gcc_exe: out = os.popen(gcc_exe + ' -dumpversion','r') @@ -313,4 +313,3 @@ def get_versions(): # anyway - so we can link OMF DLLs ld_version = None return (gcc_version, ld_version) - -- cgit v1.2.1 From 4db3246f4f27f3cfd92a72024b56c7c60c6ac083 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 1 Oct 2002 04:14:17 +0000 Subject: Commit fix for SF 603831. Strangely, two out of three patches there seem already committed; but the essential one (get rid of the assert in object_filenames in ccompiler.py) was not yet applied. This makes the build procedure for Twisted work again. This is *not* a backport candidate despite the fact that identical code appears to exist in 2.2.2; Twisted builds fine there, so there must have been a change elsewhere. --- ccompiler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 5a0641ea..43dfa731 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -876,7 +876,8 @@ class CCompiler: # extension for executable files, eg. '' or '.exe' def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): - assert output_dir is not None + if output_dir is None: + output_dir = '' obj_names = [] for src_name in source_filenames: base, ext = os.path.splitext(src_name) -- cgit v1.2.1 From 1d52f144d021e9b66b6610b2dfc1c662182f7248 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Tue, 1 Oct 2002 17:39:59 +0000 Subject: save the verbose argument as an instance attributes. Subclasses of CCompiler may rely on the presence of self.verbose (SciPy's distutils appears to). --- ccompiler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ccompiler.py b/ccompiler.py index 43dfa731..60d1caee 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -82,6 +82,7 @@ class CCompiler: self.dry_run = dry_run self.force = force + self.verbose = verbose # 'output_dir': a common output directory for object, library, # shared object, and shared library files -- cgit v1.2.1 From 464cfeaa1514adce324231af0aba80aac19484d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Fri, 4 Oct 2002 09:30:06 +0000 Subject: Pulling Mark Alexander's contribution from CVS. --- command/__init__.py | 5 +- command/bdist.py | 17 +- command/bdist_packager.py | 227 ------------------------- command/bdist_pkgtool.py | 411 ---------------------------------------------- command/bdist_sdux.py | 302 ---------------------------------- 5 files changed, 14 insertions(+), 948 deletions(-) delete mode 100644 command/bdist_packager.py delete mode 100644 command/bdist_pkgtool.py delete mode 100644 command/bdist_sdux.py diff --git a/command/__init__.py b/command/__init__.py index 81436275..e70429c8 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -21,8 +21,9 @@ __all__ = ['build', 'bdist_dumb', 'bdist_rpm', 'bdist_wininst', - 'bdist_sdux', - 'bdist_pkgtool', + # These two are reserved for future use: + #'bdist_sdux', + #'bdist_pkgtool', # Note: # bdist_packager is not included because it only provides # an abstract base class diff --git a/command/bdist.py b/command/bdist.py index f61611eb..454c9df1 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -52,7 +52,9 @@ class bdist (Command): ] # The following commands do not take a format option from bdist - no_format_option = ('bdist_rpm', 'bdist_sdux', 'bdist_pkgtool') + no_format_option = ('bdist_rpm', + #'bdist_sdux', 'bdist_pkgtool' + ) # This won't do in reality: will need to distinguish RPM-ish Linux, # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. @@ -62,20 +64,23 @@ class bdist (Command): # Establish the preferred order (for the --help-formats option). format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', - 'wininst', 'zip', 'pkgtool', 'sdux'] + 'wininst', 'zip', + #'pkgtool', 'sdux' + ] # And the real information. format_command = { 'rpm': ('bdist_rpm', "RPM distribution"), - 'zip': ('bdist_dumb', "ZIP file"), 'gztar': ('bdist_dumb', "gzip'ed tar file"), + 'zip': ('bdist_dumb', "ZIP file"), + 'gztar': ('bdist_dumb', "gzip'ed tar file"), 'bztar': ('bdist_dumb', "bzip2'ed tar file"), 'ztar': ('bdist_dumb', "compressed tar file"), 'tar': ('bdist_dumb', "tar file"), 'wininst': ('bdist_wininst', "Windows executable installer"), 'zip': ('bdist_dumb', "ZIP file"), - 'pkgtool': ('bdist_pkgtool', - "Solaris pkgtool distribution"), - 'sdux': ('bdist_sdux', "HP-UX swinstall depot"), + #'pkgtool': ('bdist_pkgtool', + # "Solaris pkgtool distribution"), + #'sdux': ('bdist_sdux', "HP-UX swinstall depot"), } diff --git a/command/bdist_packager.py b/command/bdist_packager.py deleted file mode 100644 index 19604255..00000000 --- a/command/bdist_packager.py +++ /dev/null @@ -1,227 +0,0 @@ -"""distutils.command.bdist_ packager - -Modified from bdist_dumb by Mark W. Alexander - -Implements the Distutils 'bdist_packager' abstract command -to be subclassed by binary package creation commands. -""" - -__revision__ = "$Id: bdist_packager.py,v 0.1 2001/04/4 mwa" - -import os -from distutils.core import Command -from distutils.util import get_platform -from distutils.dir_util import create_tree, remove_tree -from distutils.file_util import write_file -from distutils.errors import * -from distutils import log -import string, sys - -class bdist_packager (Command): - - description = "abstract base for package manager specific bdist commands" - -# XXX update user_options - user_options = [ - ('bdist-base=', None, - "base directory for creating built distributions"), - ('pkg-dir=', None, - "base directory for creating binary packages (defaults to \"binary\" under "), - ('dist-dir=', 'd', - "directory to put final RPM files in " - "(and .spec files if --spec-only)"), - ('category=', None, - "Software category (packager dependent format)"), - ('revision=', None, - "package revision number"), - ('icon=', None, - "Package icon"), - ('subpackages=', None, - "Comma seperated list of seperately packaged trees"), - ('preinstall=', None, - "preinstall script (Bourne shell code)"), - ('postinstall=', None, - "postinstall script (Bourne shell code)"), - ('preremove=', None, - "preremove script (Bourne shell code)"), - ('postremove=', None, - "postremove script (Bourne shell code)"), - ('requires=', None, - "capabilities required by this package"), - ('keep-temp', 'k', - "don't clean up RPM build directory"), - ('control-only', None, - "Generate package control files and stop"), - ('no-autorelocate', None, - "Inhibit automatic relocation to installed site-packages"), - ] - - boolean_options = ['keep-temp', 'control-only', 'no_autorelocate'] - - def ensure_string_not_none (self,option,default=None): - val = getattr(self,option) - if val is not None: - return - Command.ensure_string(self,option,default) - val = getattr(self,option) - if val is None: - raise DistutilsOptionError, "'%s' must be provided" % option - - def ensure_script(self, arg): - if not arg: - return - try: - self.ensure_string(arg) - except: - try: - self.ensure_filename(arg) - except: - raise RuntimeError, "cannot decipher script option (%s)" % arg - - def write_script (self,path,attr,default=None): - """ write the script specified in attr to path. if attr is None, - write use default instead """ - val = getattr(self,attr) - if not val: - if not default: - return - else: - setattr(self,attr,default) - val = default - if val != "": - log.info('Creating %s script', attr) - self.execute(write_file, - (path, self.get_script(attr)), - "writing '%s'" % path) - - def get_binary_name(self): - py_ver = sys.version[0:string.find(sys.version,' ')] - return self.name + '-' + self.version + '-' + \ - self.revision + '-' + py_ver - - def get_script (self, attr): - # accept a script as a string ("line\012line\012..."), - # a filename, or a list - # XXX We could probably get away with copy_file, but I'm - # guessing this will be more flexible later on.... - val = getattr(self, attr) - if val is None: - return None - try: - os.stat(val) - # script is a file - ret = [] - f = open(val) - ret = string.split(f.read(), "\012"); - f.close() - except: - if type(val) == type(""): - # script is a string - ret = string.split(val, "\012") - elif type(val) == type([]): - # script is a list - ret = val - else: - raise RuntimeError, \ - "cannot figure out what to do with 'request' option (%s)" \ - % val - return ret - - - def initialize_options (self): - self.keep_temp = 0 - self.control_only = 0 - self.no_autorelocate = 0 - self.pkg_dir = None - self.plat_name = None - self.icon = None - self.requires = None - self.subpackages = None - self.category = None - self.revision = None - - # PEP 241 Metadata - self.name = None - self.version = None - #self.url = None - #self.author = None - #self.author_email = None - #self.maintainer = None - #self.maintainer_email = None - #self.description = None - #self.long_description = None - #self.licence = None - #self.platforms = None - #self.keywords = None - self.root_package = None - - # package installation scripts - self.preinstall = None - self.postinstall = None - self.preremove = None - self.postremove = None - # initialize_options() - - - def finalize_options (self): - - if self.pkg_dir is None: - bdist_base = self.get_finalized_command('bdist').bdist_base - self.pkg_dir = os.path.join(bdist_base, 'binary') - - if not self.plat_name: - self.plat = self.distribution.get_platforms() - if self.distribution.has_ext_modules(): - self.plat_name = [sys.platform,] - else: - self.plat_name = ["noarch",] - - d = self.distribution - self.ensure_string_not_none('name', d.get_name()) - self.ensure_string_not_none('version', d.get_version()) - self.ensure_string('category') - self.ensure_string('revision',"1") - #self.ensure_string('url',d.get_url()) - if type(self.distribution.packages) == type([]): - self.root_package=self.distribution.packages[0] - else: - self.root_package=self.name - self.ensure_string('root_package',self.root_package) - #self.ensure_string_list('keywords') - #self.ensure_string_not_none('author', d.get_author()) - #self.ensure_string_not_none('author_email', d.get_author_email()) - self.ensure_filename('icon') - #self.ensure_string_not_none('maintainer', d.get_maintainer()) - #self.ensure_string_not_none('maintainer_email', - #d.get_maintainer_email()) - #self.ensure_string_not_none('description', d.get_description()) - #self.ensure_string_not_none('long_description', d.get_long_description()) - #if self.long_description=='UNKNOWN': - #self.long_description=self.description - #self.ensure_string_not_none('license', d.get_license()) - self.ensure_string_list('requires') - self.ensure_filename('preinstall') - self.ensure_filename('postinstall') - self.ensure_filename('preremove') - self.ensure_filename('postremove') - - # finalize_options() - - - def run (self): - - raise RuntimeError, \ - "abstract method -- subclass %s must override" % self.__class__ - self.run_command('build') - - install = self.reinitialize_command('install', reinit_subcommands=1) - install.root = self.pkg_dir - - log.info("installing to %s", self.pkg_dir) - self.run_command('install') - if not self.keep_temp: - remove_tree(self.pkg_dir, dry_run=self.dry_run) - - # run() - -# class bdist_packager diff --git a/command/bdist_pkgtool.py b/command/bdist_pkgtool.py deleted file mode 100644 index 3b8ca2d2..00000000 --- a/command/bdist_pkgtool.py +++ /dev/null @@ -1,411 +0,0 @@ -"""distutils.command.bdist_pkgtool - - -Author: Mark W. Alexander - -Implements the Distutils 'bdist_pkgtool' command (create Solaris pkgtool -distributions).""" - -import os, string, sys -from types import * -from distutils.util import get_platform -from distutils.file_util import write_file -from distutils.errors import * -from distutils.command import bdist_packager -from distutils import sysconfig -from distutils import log -from commands import getoutput - -__revision__ = "$Id: bdist_pkgtool.py,v 0.3 mwa " - -# default request script - Is also wrapped around user's request script -# unless --no-autorelocate is requested. Finds the python site-packages -# directory and prompts for verification -DEFAULT_REQUEST="""#!/bin/sh -###################################################################### -# Distutils internal package relocation support # -###################################################################### - -PRODUCT="__DISTUTILS_NAME__" - -trap `exit 3` 15 -/usr/bin/which python 2>&1 >/dev/null -if [ $? -ne 0 ]; then - echo "The python interpretor needs to be on your path!" - echo - echo "If you have more than one, make sure the first one is where" - echo "you want this module installed:" - exit 1 -fi - -PY_DIR=`python -c "import sys;print '%s/lib/python%s' % (sys.exec_prefix,sys.version[0:3])" 2>/dev/null` -PY_PKG_DIR=`python -c "import sys;print '%s/lib/python%s/site-packages' % (sys.exec_prefix,sys.version[0:3])" 2>/dev/null` - -echo "" -if [ -z "${PY_DIR}" ]; then - echo "I can't seem to find the python distribution." - echo "I'm assuming the default path for site-packages" -else - BASEDIR="${PY_PKG_DIR}" - cat <&1 >/dev/null -if [ $? -ne 0 ]; then - echo "The python interpretor needs to be on your path!" - echo - echo "If you have more than one, make sure the first one is where" - echo "you want this module removed from" - exit 1 -fi - -/usr/bin/test -d ${BASEDIR}/__DISTUTILS_NAME__ -if [ $? -eq 0 ]; then - find ${BASEDIR}/__DISTUTILS_NAME__ -name "*.pyc" -exec rm {} \; - find ${BASEDIR}/__DISTUTILS_NAME__ -name "*.pyo" -exec rm {} \; -fi -""" - -# default postremove removes the module directory _IF_ no files are -# there (Turns out this isn't needed if the preremove does it's job -# Left for posterity -DEFAULT_POSTREMOVE="""#!/bin/sh - -/usr/bin/test -d ${BASEDIR}/__DISTUTILS_NAME__ -if [ $? -eq 0 ]; then - if [ `find ${BASEDIR}/__DISTUTILS_NAME__ ! -type d | wc -l` -eq 0 ]; then - rm -rf ${BASEDIR}/__DISTUTILS_NAME__ - fi -fi -""" - -class bdist_pkgtool (bdist_packager.bdist_packager): - - description = "create an pkgtool (Solaris) package" - - user_options = bdist_packager.bdist_packager.user_options + [ - ('revision=', None, - "package revision number (PSTAMP)"), - ('pkg-abrev=', None, - "Abbreviation (9 characters or less) of the package name"), - ('compver=', None, - "file containing compatible versions of this package (man compver)"), - ('depend=', None, - "file containing dependencies for this package (man depend)"), - #('category=', None, - #"Software category"), - ('request=', None, - "request script (Bourne shell code)"), - ] - - def initialize_options (self): - # XXX Check for pkgtools on path... - bdist_packager.bdist_packager.initialize_options(self) - self.compver = None - self.depend = None - self.vendor = None - self.classes = None - self.request = None - self.pkg_abrev = None - self.revision = None - # I'm not sure I should need to do this, but the setup.cfg - # settings weren't showing up.... - options = self.distribution.get_option_dict('bdist_packager') - for key in options.keys(): - setattr(self,key,options[key][1]) - - # initialize_options() - - - def finalize_options (self): - global DEFAULT_REQUEST, DEFAULT_POSTINSTALL - global DEFAULT_PREREMOVE, DEFAULT_POSTREMOVE - if self.pkg_dir is None: - dist_dir = self.get_finalized_command('bdist').dist_dir - self.pkg_dir = os.path.join(dist_dir, "pkgtool") - - self.ensure_string('classes', None) - self.ensure_string('revision', "1") - self.ensure_script('request') - self.ensure_script('preinstall') - self.ensure_script('postinstall') - self.ensure_script('preremove') - self.ensure_script('postremove') - self.ensure_string('vendor', None) - if self.__dict__.has_key('author'): - if self.__dict__.has_key('author_email'): - self.ensure_string('vendor', - "%s <%s>" % (self.author, - self.author_email)) - else: - self.ensure_string('vendor', - "%s" % (self.author)) - self.ensure_string('category', "System,application") - self.ensure_script('compver') - self.ensure_script('depend') - bdist_packager.bdist_packager.finalize_options(self) - if self.pkg_abrev is None: - self.pkg_abrev=self.name - if len(self.pkg_abrev)>9: - raise DistutilsOptionError, \ - "pkg-abrev (%s) must be less than 9 characters" % self.pkg_abrev - # Update default scripts with our metadata name - DEFAULT_REQUEST = string.replace(DEFAULT_REQUEST, - "__DISTUTILS_NAME__", self.root_package) - DEFAULT_POSTINSTALL = string.replace(DEFAULT_POSTINSTALL, - "__DISTUTILS_NAME__", self.root_package) - DEFAULT_PREREMOVE = string.replace(DEFAULT_PREREMOVE, - "__DISTUTILS_NAME__", self.root_package) - DEFAULT_POSTREMOVE = string.replace(DEFAULT_POSTREMOVE, - "__DISTUTILS_NAME__", self.root_package) - - # finalize_options() - - - def make_package(self,root=None): - # make directories - self.mkpath(self.pkg_dir) - if root: - pkg_dir = self.pkg_dir+"/"+root - self.mkpath(pkg_dir) - else: - pkg_dir = self.pkg_dir - - self.reinitialize_command('install', reinit_subcommands=1) - # build package - log.info('Building package') - self.run_command('build') - log.info('Creating pkginfo file') - path = os.path.join(pkg_dir, "pkginfo") - self.execute(write_file, - (path, - self._make_info_file()), - "writing '%s'" % path) - # request script handling - if self.request==None: - self.request = self._make_request_script() - if self.request!="": - path = os.path.join(pkg_dir, "request") - self.execute(write_file, - (path, - self.request), - "writing '%s'" % path) - - # Create installation scripts, since compver & depend are - # user created files, they work just fine as scripts - self.write_script(os.path.join(pkg_dir, "postinstall"), - 'postinstall',DEFAULT_POSTINSTALL) - self.write_script(os.path.join(pkg_dir, "preinstall"), - 'preinstall',None) - self.write_script(os.path.join(pkg_dir, "preremove"), - 'preremove',DEFAULT_PREREMOVE) - self.write_script(os.path.join(pkg_dir, "postremove"), - 'postremove',None) - self.write_script(os.path.join(pkg_dir, "compver"), - 'compver',None) - self.write_script(os.path.join(pkg_dir, "depend"), - 'depend',None) - - log.info('Creating prototype file') - path = os.path.join(pkg_dir, "prototype") - self.execute(write_file, - (path, - self._make_prototype()), - "writing '%s'" % path) - - - if self.control_only: # stop if requested - return - - - log.info('Creating package') - pkg_cmd = ['pkgmk', '-o', '-f'] - pkg_cmd.append(path) - pkg_cmd.append('-b') - pkg_cmd.append(os.environ['PWD']) - self.spawn(pkg_cmd) - pkg_cmd = ['pkgtrans', '-s', '/var/spool/pkg'] - path = os.path.join(os.environ['PWD'],pkg_dir, - self.get_binary_name() + ".pkg") - log.info('Transferring package to ' + pkg_dir) - pkg_cmd.append(path) - pkg_cmd.append(self.pkg_abrev) - self.spawn(pkg_cmd) - os.system("rm -rf /var/spool/pkg/%s" % self.pkg_abrev) - - - def run (self): - if self.subpackages: - self.subpackages=string.split(self.subpackages,",") - for pkg in self.subpackages: - self.make_package(pkg) - else: - self.make_package() - # run() - - - def _make_prototype(self): - import pwd, grp - proto_file = ["i pkginfo"] - if self.request: - proto_file.extend(['i request']) - if self.postinstall: - proto_file.extend(['i postinstall']) - if self.postremove: - proto_file.extend(['i postremove']) - if self.preinstall: - proto_file.extend(['i preinstall']) - if self.preremove: - proto_file.extend(['i preremove']) - if self.compver: - proto_file.extend(['i compver']) - if self.requires: - proto_file.extend(['i depend']) - proto_file.extend(['!default 644 root bin']) - build = self.get_finalized_command('build') - - try: - self.distribution.packages[0] - file_list=string.split( - getoutput("pkgproto %s/%s=%s" % \ - (build.build_lib, self.distribution.packages[0], - self.distribution.packages[0])), "\012") - except: - file_list=string.split( - getoutput("pkgproto %s=" % (build.build_lib)),"\012") - ownership="%s %s" % (pwd.getpwuid(os.getuid())[0], - grp.getgrgid(os.getgid())[0]) - for i in range(len(file_list)): - file_list[i] = string.replace(file_list[i],ownership,"root bin") - proto_file.extend(file_list) - return proto_file - - def _make_request_script(self): - global DEFAULT_REQUEST - # A little different from other scripts, if we are to automatically - # relocate to the target site-packages, we have to wrap any provided - # script with the autorelocation script. If no script is provided, - # The request script will simply be the autorelocate script - if self.no_autorelocate==0: - request=string.split(DEFAULT_REQUEST,"\012") - else: - log.info('Creating relocation request script') - if self.request: - users_request=self.get_script('request') - if users_request!=None and users_request!=[]: - if self.no_autorelocate==0 and users_request[0][0:2]=="#!": - users_request.remove(users_request[0]) - for i in users_request: - request.append(i) - - if self.no_autorelocate==0: - request.append("#############################################") - request.append("# finalize relocation support #") - request.append("#############################################") - request.append('echo "BASEDIR=\\"${BASEDIR}\\"" >>$1') - return request - - def _make_info_file(self): - """Generate the text of a pkgtool info file and return it as a - list of strings (one per line). - """ - # definitions and headers - # PKG must be alphanumeric, < 9 characters - info_file = [ - 'PKG="%s"' % self.pkg_abrev, - 'NAME="%s"' % self.name, - 'VERSION="%s"' % self.version, - 'PSTAMP="%s"' % self.revision, - ] - info_file.extend(['VENDOR="%s (%s)"' % (self.distribution.maintainer, \ - self.distribution.license) ]) - info_file.extend(['EMAIL="%s"' % self.distribution.maintainer_email ]) - - p = self.distribution.get_platforms() - if p is None or p==['UNKNOWN']: - archs=getoutput('uname -p') - else: - archs=string.join(self.distribution.get_platforms(),',') - #else: - #print "Assuming a sparc architecure" - #archs='sparc' - info_file.extend(['ARCH="%s"' % archs ]) - - if self.distribution.get_url(): - info_file.extend(['HOTLINE="%s"' % self.distribution.get_url() ]) - if self.classes: - info_file.extend(['CLASSES="%s"' % self.classes ]) - if self.category: - info_file.extend(['CATEGORY="%s"' % self.category ]) - site=None - for i in sys.path: - if i[-13:]=="site-packages": - site=i - break - if site: - info_file.extend(['BASEDIR="%s"' % site ]) - - return info_file - - # _make_info_file () - - def _format_changelog(self, changelog): - """Format the changelog correctly and convert it to a list of strings - """ - if not changelog: - return changelog - new_changelog = [] - for line in string.split(string.strip(changelog), '\n'): - line = string.strip(line) - if line[0] == '*': - new_changelog.extend(['', line]) - elif line[0] == '-': - new_changelog.append(line) - else: - new_changelog.append(' ' + line) - - # strip trailing newline inserted by first changelog entry - if not new_changelog[0]: - del new_changelog[0] - - return new_changelog - - # _format_changelog() - -# class bdist_rpm diff --git a/command/bdist_sdux.py b/command/bdist_sdux.py deleted file mode 100644 index 875f3d88..00000000 --- a/command/bdist_sdux.py +++ /dev/null @@ -1,302 +0,0 @@ -"""distutils.command.bdist_pkgtool - -Implements the Distutils 'bdist_sdux' command to create HP-UX -swinstall depot. -""" - -# Mark Alexander - -__revision__ = "$Id: bdist_sdux.py,v 0.2 " -import os, string -from types import * -from distutils.util import get_platform -from distutils.file_util import write_file -from distutils.errors import * -from distutils.command import bdist_packager -from distutils import log -import sys -from commands import getoutput - -DEFAULT_CHECKINSTALL="""#!/bin/sh -/usr/bin/which python 2>&1 >/dev/null -if [ $? -ne 0 ]; then - echo "ERROR: Python must be on your PATH" &>2 - echo "ERROR: (You may need to link it to /usr/bin) " &>2 - exit 1 -fi -PY_DIR=`python -c "import sys;print '%s/lib/python%s' % (sys.exec_prefix,sys.version[0:3])" #2>/dev/null` -PY_PKG_DIR=`python -c "import sys;print '%s/lib/python%s/site-packages' % (sys.exec_prefix,sys.version[0:3])" #2>/dev/null` -PY_LIB_DIR=`dirname $PY_PKG_DIR` - -if [ "`dirname ${SW_LOCATION}`" = "__DISTUTILS_PKG_DIR__" ]; then - # swinstall to default location - if [ "${PY_PKG_DIR}" != "__DISTUTILS_PKG_DIR__" ]; then - echo "ERROR: " &>2 - echo "ERROR: Python is not installed where this package expected!" &>2 - echo "ERROR: You need to manually relocate this package to your python installation." &>2 - echo "ERROR: " &>2 - echo "ERROR: Re-run swinstall specifying the product name:location, e.g.:" &>2 - echo "ERROR: " &>2 - echo "ERROR: swinstall -s [source] __DISTUTILS_NAME__:${PY_PKG_DIR}/__DISTUTILS_DIRNAME__" &>2 - echo "ERROR: " &>2 - echo "ERROR: to relocate this package to match your python installation" &>2 - echo "ERROR: " &>2 - exit 1 - fi -else - if [ "`dirname ${SW_LOCATION}`" != "${PY_PKG_DIR}" -a "`dirname ${SWLOCATION}`" != "${PY_LIB_DIR}" ]; then - echo "WARNING: " &>2 - echo "WARNING: Package is being installed outside the 'normal' python search path!" &>2 - echo "WARNING: Add ${SW_LOCATION} to PYTHONPATH to use this package" &>2 - echo "WARNING: " &>2 - fi -fi -""" - -DEFAULT_POSTINSTALL="""#!/bin/sh -/usr/bin/which python 2>&1 >/dev/null -if [ $? -ne 0 ]; then - echo "ERROR: Python must be on your PATH" &>2 - echo "ERROR: (You may need to link it to /usr/bin) " &>2 - exit 1 -fi -python -c "import compileall;compileall.compile_dir(\\"${SW_LOCATION}\\")" -""" - -DEFAULT_PREREMOVE="""#!/bin/sh -# remove compiled bytecode files -find ${SW_LOCATION} -name "*.pyc" -exec rm {} \; -find ${SW_LOCATION} -name "*.pyo" -exec rm {} \; -""" - -DEFAULT_POSTREMOVE="""#!/bin/sh -if [ `find ${SW_LOCATION} ! -type d | wc -l` -eq 0 ]; then - # remove if there's nothing but empty directories left - rm -rf ${SW_LOCATION} -fi -""" - -class bdist_sdux(bdist_packager.bdist_packager): - - description = "create an HP swinstall depot" - - user_options = bdist_packager.bdist_packager.user_options + [ - #('revision=', None, - #"package revision number (PSTAMP)"), - ('keep-permissions', None, - "Don't reset permissions and ownership to root/bin"), # XXX - ('corequisites=', None, - "corequisites"), # XXX - ('prerequisites=', None, - "prerequisites"), # XXX - #('category=', None, - #"Software category"), - ('checkinstall=', None, # XXX ala request - "checkinstall script (Bourne shell code)"), - ('configure=', None, # XXX - "configure script (Bourne shell code)"), - ('unconfigure=', None, # XXX - "unconfigure script (Bourne shell code)"), - ('verify=', None, # XXX - "verify script (Bourne shell code)"), - ('unpreinstall=', None, # XXX - "unpreinstall script (Bourne shell code)"), - ('unpostinstall=', None, # XXX - "unpostinstall script (Bourne shell code)"), - ] - - boolean_options = ['keep-permissions'] - - def initialize_options (self): - bdist_packager.bdist_packager.initialize_options(self) - self.corequisites = None - self.prerequesites = None - self.checkinstall = None - self.configure = None - self.unconfigure = None - self.verify = None - self.unpreinstall = None - self.unpostinstall = None - # More - self.copyright = None - self.readme = None - self.machine_type = None - self.os_name = None - self.os_release = None - self.directory = None - self.readme = None - self.copyright = None - self.architecture= None - self.keep_permissions= None - options = self.distribution.get_option_dict('bdist_packager') - for key in options.keys(): - setattr(self,key,options[key][1]) - - # initialize_options() - - - def finalize_options (self): - global DEFAULT_CHECKINSTALL, DEFAULT_POSTINSTALL - global DEFAULT_PREREMOVE, DEFAULT_POSTREMOVE - if self.pkg_dir==None: - dist_dir = self.get_finalized_command('bdist').dist_dir - self.pkg_dir = os.path.join(dist_dir, "sdux") - self.ensure_script('corequisites') - self.ensure_script('prerequesites') - self.ensure_script('checkinstall') - self.ensure_script('configure') - self.ensure_script('unconfigure') - self.ensure_script('verify') - self.ensure_script('unpreinstall') - self.ensure_script('unpostinstall') - self.ensure_script('copyright') - self.ensure_script('readme') - self.ensure_string('machine_type','*') - if not self.__dict__.has_key('platforms'): - # This is probably HP, but if it's not, use sys.platform - if sys.platform[0:5] == "hp-ux": - self.platforms = "HP-UX" - else: - self.platforms = string.upper(sys.platform) - else: - # we can only handle one - self.platforms=string.join(self.platforms[0]) - self.ensure_string('os_release','*') - self.ensure_string('directory','%s/lib/python%s/site-packages' % \ - (sys.exec_prefix, sys.version[0:3])) - bdist_packager.bdist_packager.finalize_options(self) - DEFAULT_CHECKINSTALL = string.replace(DEFAULT_CHECKINSTALL, - "__DISTUTILS_NAME__", self.name) - DEFAULT_CHECKINSTALL = string.replace(DEFAULT_CHECKINSTALL, - "__DISTUTILS_DIRNAME__", self.root_package) - DEFAULT_CHECKINSTALL = string.replace(DEFAULT_CHECKINSTALL, - "__DISTUTILS_PKG_DIR__", self.directory) - DEFAULT_POSTINSTALL = string.replace(DEFAULT_POSTINSTALL, - "__DISTUTILS_DIRNAME__", self.root_package) - #DEFAULT_PREREMOVE = string.replace(DEFAULT_PREREMOVE, - #"__DISTUTILS_NAME__", self.root_package) - #DEFAULT_POSTREMOVE = string.replace(DEFAULT_POSTREMOVE, - #"__DISTUTILS_NAME__", self.root_package) - # finalize_options() - - def run (self): - # make directories - self.mkpath(self.pkg_dir) - psf_path = os.path.join(self.pkg_dir, - "%s.psf" % self.get_binary_name()) - # build package - log.info('Building package') - self.run_command('build') - log.info('Creating psf file') - self.execute(write_file, - (psf_path, - self._make_control_file()), - "writing '%s'" % psf_path) - if self.control_only: # stop if requested - return - - log.info('Creating package') - spawn_cmd = ['swpackage', '-s'] - spawn_cmd.append(psf_path) - spawn_cmd.append('-x') - spawn_cmd.append('target_type=tape') - spawn_cmd.append('@') - spawn_cmd.append(self.pkg_dir+"/"+self.get_binary_name()+'.depot') - self.spawn(spawn_cmd) - - # run() - - - def _make_control_file(self): - # Generate a psf file and return it as list of strings (one per line). - # definitions and headers - title = "%s %s" % (self.maintainer,self.maintainer_email) - title=title[0:80] - #top=self.distribution.packages[0] - psf_file = [ - 'vendor', # Vendor information - ' tag %s' % "DISTUTILS", - ' title %s' % title, - ' description Distutils package maintainer (%s)' % self.license, - 'end', # end of vendor - 'product', # Product information - ' tag %s' % self.name, - ' title %s' % self.description, - ' description %s' % self.description, - ' revision %s' % self.version, - ' architecture %s' % self.platforms, - ' machine_type %s' % self.machine_type, - ' os_name %s' % self.platforms, - ' os_release %s' % self.os_release, - ' directory %s' % self.directory + "/" + self.root_package, - ] - - self.write_script(os.path.join(self.pkg_dir, "checkinstall"), - 'checkinstall',DEFAULT_CHECKINSTALL) - psf_file.extend([' checkinstall %s/checkinstall' % self.pkg_dir]) - self.write_script(os.path.join(self.pkg_dir, "postinstall"), - 'postinstall',DEFAULT_POSTINSTALL) - psf_file.extend([' postinstall %s/postinstall' % self.pkg_dir]) - self.write_script(os.path.join(self.pkg_dir, "preremove"), - 'preremove',DEFAULT_PREREMOVE) - psf_file.extend([' preremove %s/preremove' % self.pkg_dir]) - self.write_script(os.path.join(self.pkg_dir, "postremove"), - 'postremove',DEFAULT_POSTREMOVE) - psf_file.extend([' postremove %s/postremove' % self.pkg_dir]) - if self.preinstall: - self.write_script(self.pkg_dir+"/preinstall", 'preinstall', None) - psf_file.extend([' preinstall %s/preinstall' % self.pkg_dir]) - if self.configure: - self.write_script(self.pkg_dir+"/configure", 'configure', None) - psf_file.extend([' configure %s/configure' % self.pkg_dir]) - if self.unconfigure: - self.write_script(self.pkg_dir+"/unconfigure", 'unconfigure', None) - psf_file.extend([' unconfigure %s/unconfigure' % self.pkg_dir]) - if self.verify: - self.write_script(self.pkg_dir+"/verify", 'verify', None) - psf_file.extend([' verify %s/verify' % self.pkg_dir]) - if self.unpreinstall: - self.write_script(self.pkg_dir+"/unpreinstall", 'unpreinstall', None) - psf_file.extend([' unpreinstall %s/unpreinstall' % self.pkg_dir]) - if self.unpostinstall: - self.write_script(self.pkg_dir+"/unpostinstall", 'unpostinstall', None) - psf_file.extend([' unpostinstall %s/unpostinstall' % self.pkg_dir]) - psf_file.extend([' is_locatable true']) - #if self.long_description: - #psf_file.extend([self.long_description]) - if self.copyright: - # XX make a copyright file XXX - self.write_script('copyright') - psf_file.extend([' copyright Date: Mon, 7 Oct 2002 05:57:21 +0000 Subject: Patch #619493: Prefer rpmbuild over rpm if available. Backported to 2.2. --- command/bdist_rpm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index bbaad7dc..86a40947 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -282,6 +282,9 @@ class bdist_rpm (Command): # build package log.info("building RPMs") rpm_cmd = ['rpm'] + if os.path.exists('/usr/bin/rpmbuild') or \ + os.path.exists('/bin/rpmbuild'): + rpm_cmd = ['rpmbuild'] if self.source_only: # what kind of RPMs? rpm_cmd.append('-bs') elif self.binary_only: -- cgit v1.2.1 From 6ed654260a646f4c8823e335597316c2a29038e5 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Wed, 9 Oct 2002 21:37:18 +0000 Subject: MacOSX linker doesn't understand -R flag at all, no matter how you feed it the flag. Punt and return a -L flag instead (returning "" gums up the command to be forked). --- unixccompiler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 831717ba..692e3eb4 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -196,7 +196,10 @@ class UnixCCompiler(CCompiler): # the configuration data stored in the Python installation, so # we use this hack. compiler = os.path.basename(sysconfig.get_config_var("CC")) - if compiler == "gcc" or compiler == "g++": + if sys.platform[:6] == "darwin": + # MacOSX's linker doesn't understand the -R flag at all + return "-L" + dir + elif compiler == "gcc" or compiler == "g++": return "-Wl,-R" + dir else: return "-R" + dir -- cgit v1.2.1 From 41f9cf2f3074f7626544b810e8995139bb842be9 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 15 Oct 2002 14:51:58 +0000 Subject: Revert the previous checkin - it didn't work anyway. --- command/bdist_wininst.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index b5383198..71e51bfd 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -130,9 +130,8 @@ class bdist_wininst (Command): # And make an archive relative to the root of the # pseudo-installation tree. - from tempfile import NamedTemporaryFile - arc = NamedTemporaryFile(".zip") - archive_basename = arc.name[:-4] + from tempfile import mktemp + archive_basename = mktemp() fullname = self.distribution.get_fullname() arcname = self.make_archive(archive_basename, "zip", root_dir=self.bdist_dir) @@ -140,7 +139,7 @@ class bdist_wininst (Command): self.create_exe(arcname, fullname, self.bitmap) # remove the zip-file again log.debug("removing temporary file '%s'", arcname) - arc.close() + os.remove(arcname) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) -- cgit v1.2.1 From a3f707d5f06386feabf8d3767faca67ef4a580cd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 15 Oct 2002 19:45:25 +0000 Subject: Recreated after source changes. --- command/bdist_wininst.py | 630 +++++++++++++++++++++++------------------------ 1 file changed, 315 insertions(+), 315 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 71e51bfd..a3526a7a 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -264,7 +264,7 @@ TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v ZGUuDQ0KJAAAAAAAAAAtOHsRaVkVQmlZFUJpWRVCEkUZQmpZFUIGRh9CYlkVQupFG0JrWRVCBkYR QmtZFUJpWRVCZlkVQmlZFELjWRVCC0YGQmRZFUJveh9Ca1kVQq5fE0JoWRVCUmljaGlZFUIAAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwAQIUU9AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAJAFAQAA +AAAAAAAAAAAAAAAAUEUAAEwBAwDeb6w9AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAGAGAQAA wAAAABABAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAAIAEAAAQAAAAAAAACAAAAAAAQAAAQ AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwEQEA5AEAAAAQAQAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -277,7 +277,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAkSW5mbzogVGhpcyBmaWxlIGlz IHBhY2tlZCB3aXRoIHRoZSBVUFggZXhlY3V0YWJsZSBwYWNrZXIgaHR0cDovL3VweC50c3gub3Jn ICQKACRJZDogVVBYIDEuMDEgQ29weXJpZ2h0IChDKSAxOTk2LTIwMDAgdGhlIFVQWCBUZWFtLiBB -bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCsm8FbzjNj4gCekAAIxFAAAA4AAAJgYA2//b +bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCifWKaycxW6nCekAAFxGAAAA4AAAJgYALP/b //9TVVaLdCQUhfZXdH2LbCQci3wMgD4AdHBqXFb/5vZv/xVUcUAAi/BZHVl0X4AmAFcRvHD9v/n+ 2IP7/3Unag/IhcB1E4XtdA9XaBCQ/d/+vw1qBf/Vg8QM6wdXagEJWVn2wxB1HGi3ABOyna0ALcQp Dcb3/3/7BlxGdYssWF9eXVvDVYvsg+wMU1ZXiz2oLe/uf3cz9rs5wDl1CHUHx0UIAQxWaIBMsf9v @@ -285,321 +285,321 @@ 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 -- cgit v1.2.1 From 4d708a479b7ca9f8464af6059bc6afa01ad2bdf7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 16 Oct 2002 17:51:38 +0000 Subject: Recreated after source changes. --- command/bdist_wininst.py | 638 +++++++++++++++++++++++------------------------ 1 file changed, 319 insertions(+), 319 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index a3526a7a..d996bee3 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -264,7 +264,7 @@ TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v ZGUuDQ0KJAAAAAAAAAAtOHsRaVkVQmlZFUJpWRVCEkUZQmpZFUIGRh9CYlkVQupFG0JrWRVCBkYR QmtZFUJpWRVCZlkVQmlZFELjWRVCC0YGQmRZFUJveh9Ca1kVQq5fE0JoWRVCUmljaGlZFUIAAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwDeb6w9AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAGAGAQAA +AAAAAAAAAAAAAAAAUEUAAEwBAwCepq09AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAIAGAQAA wAAAABABAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAAIAEAAAQAAAAAAAACAAAAAAAQAAAQ AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwEQEA5AEAAAAQAQAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -277,7 +277,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAkSW5mbzogVGhpcyBmaWxlIGlz IHBhY2tlZCB3aXRoIHRoZSBVUFggZXhlY3V0YWJsZSBwYWNrZXIgaHR0cDovL3VweC50c3gub3Jn ICQKACRJZDogVVBYIDEuMDEgQ29weXJpZ2h0IChDKSAxOTk2LTIwMDAgdGhlIFVQWCBUZWFtLiBB -bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCifWKaycxW6nCekAAFxGAAAA4AAAJgYALP/b +bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCjYL5dfUlP5rCekAAH1GAAAA4AAAJgYA0//b //9TVVaLdCQUhfZXdH2LbCQci3wMgD4AdHBqXFb/5vZv/xVUcUAAi/BZHVl0X4AmAFcRvHD9v/n+ 2IP7/3Unag/IhcB1E4XtdA9XaBCQ/d/+vw1qBf/Vg8QM6wdXagEJWVn2wxB1HGi3ABOyna0ALcQp Dcb3/3/7BlxGdYssWF9eXVvDVYvsg+wMU1ZXiz2oLe/uf3cz9rs5wDl1CHUHx0UIAQxWaIBMsf9v @@ -285,322 +285,322 @@ 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 @@ -614,7 +614,7 @@ AQAAAAAAtBIBAAAAAADCEgEAAAAAANISAQAAAAAA3BIBAAAAAADiEgEAAAAAAPASAQAAAAAAChMB AAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIuZGxsAENPTUNUTDMyLmRsbABHREkzMi5kbGwATVNW Q1JULmRsbABvbGUzMi5kbGwAU0hFTEwzMi5kbGwAVVNFUjMyLmRsbAAATG9hZExpYnJhcnlBAABH ZXRQcm9jQWRkcmVzcwAARXhpdFByb2Nlc3MAAABSZWdDbG9zZUtleQAAAFByb3BlcnR5U2hlZXRB -AABUZXh0T3V0QQAAZXhpdAAAQ29Jbml0aWFsaXplAABTSEdldFNwZWNpYWxGb2xkZXJQYXRoQQAA +AABUZXh0T3V0QQAAZnJlZQAAQ29Jbml0aWFsaXplAABTSEdldFNwZWNpYWxGb2xkZXJQYXRoQQAA AEdldERDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -- cgit v1.2.1 From c3c5a89898505fe9b1efe3310fc6a666212bd148 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 31 Oct 2002 13:22:41 +0000 Subject: Make the Distribution() constructor forgiving of unknown keyword arguments, triggering a warning instead of raising an exception. (In 1.5.2/2.0, it will print to stderr.) Bugfix candidate for all previous versions. This changes behaviour, but the old behaviour wasn't very useful. If Distutils version X+1 adds a new keyword argument, using the new keyword means your setup.py file won't work with Distutils version X any more. --- dist.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dist.py b/dist.py index 92cb8320..c71cb36a 100644 --- a/dist.py +++ b/dist.py @@ -12,6 +12,12 @@ __revision__ = "$Id$" import sys, os, string, re from types import * from copy import copy + +try: + import warnings +except: + warnings = None + from distutils.errors import * from distutils.fancy_getopt import FancyGetopt, translate_longopt from distutils.util import check_environ, strtobool, rfc822_escape @@ -206,8 +212,11 @@ class Distribution: elif hasattr(self, key): setattr(self, key, val) else: - raise DistutilsSetupError, \ - "invalid distribution option '%s'" % key + msg = "Unknown distribution option: %s" % repr(key) + if warnings is not None: + warnings.warn(msg) + else: + sys.stderr.write(msg + "\n") self.finalize_options() -- cgit v1.2.1 From 00c4a7c3a8e95dcb877acdf8d92421cc853c69cb Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 31 Oct 2002 13:39:33 +0000 Subject: Catch only ImportError --- dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.py b/dist.py index c71cb36a..dbeeb8b1 100644 --- a/dist.py +++ b/dist.py @@ -15,7 +15,7 @@ from copy import copy try: import warnings -except: +except ImportError: warnings = None from distutils.errors import * -- cgit v1.2.1 From 763affe91fa9f53831b26e94b818e34d73c5a2cd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 31 Oct 2002 14:26:37 +0000 Subject: Fixes SF bug#614051: win32 build_ext problem. --- command/build_ext.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index 6b6f2c7d..11ab5952 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -635,6 +635,8 @@ class build_ext (Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] + else: + return ext.libraries elif sys.platform == "os2emx": # EMX/GCC requires the python library explicitly, and I # believe VACPP does as well (though not confirmed) - AIM Apr01 -- cgit v1.2.1 From 42f8752d3cfc16950f11ddc0c462ec9c06cfd822 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 4 Nov 2002 13:33:07 +0000 Subject: [Bug #570655] Fix misleading option text --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 86a40947..597b26c6 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -68,7 +68,7 @@ class bdist_rpm (Command): ('doc-files=', None, "list of documentation files (space or comma-separated)"), ('changelog=', None, - "path to RPM changelog"), + "RPM changelog"), ('icon=', None, "name of icon file"), ('provides=', None, -- cgit v1.2.1 From a5639c31c94c1fe14c1685072399798fa4ed1a82 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 4 Nov 2002 13:45:15 +0000 Subject: Add get_distutil_options(); future setup.py files can use this to check whether the Distutils being used supports a particularly capability. (This idea was originally suggested by Juergen Hermann as a method on the Distribution class. I think it makes more sense as a function in core.py, and that's what this patch implements.) --- core.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core.py b/core.py index 9a6bff6b..d180eb8c 100644 --- a/core.py +++ b/core.py @@ -227,3 +227,12 @@ def run_setup (script_name, script_args=None, stop_after="run"): return _setup_distribution # run_setup () + +def get_distutil_options (): + """Returns a list of strings recording changes to the Distutils. + + setup.py files can then do: + if 'optional-thing' in get_distutil_options(): + ... + """ + return [] -- cgit v1.2.1 From 1bd24104b8ac0fb4707a24204bfde7b5e30c93d4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 4 Nov 2002 14:27:43 +0000 Subject: [Bug #620630] Flush stdout after logging every message. Without it, when output is redirected to a file, compiler error messages show up before Distutils prints the command being invoked. --- log.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/log.py b/log.py index f0a78650..6aeb7c9a 100644 --- a/log.py +++ b/log.py @@ -9,6 +9,8 @@ WARN = 3 ERROR = 4 FATAL = 5 +import sys + class Log: def __init__(self, threshold=WARN): @@ -17,6 +19,7 @@ class Log: def _log(self, level, msg, args): if level >= self.threshold: print msg % args + sys.stdout.flush() def log(self, level, msg, *args): self._log(level, msg, args) -- cgit v1.2.1 From 17cca1e870697be532a44be00137f801faad424d Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 4 Nov 2002 19:50:03 +0000 Subject: [Patch #588809] Remove check of environment variables; sysconfig.py will do that now --- command/build_ext.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 11ab5952..934b4576 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -451,14 +451,6 @@ class build_ext (Command): for undef in ext.undef_macros: macros.append((undef,)) - # XXX and if we support CFLAGS, why not CC (compiler - # executable), CPPFLAGS (pre-processor options), and LDFLAGS - # (linker options) too? - # XXX should we use shlex to properly parse CFLAGS? - - if os.environ.has_key('CFLAGS'): - extra_args.extend(string.split(os.environ['CFLAGS'])) - objects = self.compiler.compile(sources, output_dir=self.build_temp, macros=macros, @@ -485,7 +477,6 @@ class build_ext (Command): objects.extend(ext.extra_objects) extra_args = ext.extra_link_args or [] - self.compiler.link_shared_object( objects, ext_filename, libraries=self.get_libraries(ext), -- cgit v1.2.1 From 4765756222ebf75cbba020854d4550f7e471f2f5 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 4 Nov 2002 19:53:24 +0000 Subject: [Patch #588809] LDFLAGS support for build_ext.py, from Robert Weber customize_compiler() now looks at various environment variables and uses their values to override the configured C compiler/preprocessor/linker binary and flags. --- sysconfig.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 48672d6a..e879fa14 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -142,9 +142,25 @@ def customize_compiler(compiler): (cc, opt, ccshared, ldshared, so_ext) = \ get_config_vars('CC', 'OPT', 'CCSHARED', 'LDSHARED', 'SO') + if os.environ.has_key('CC'): + cc = os.environ['CC'] + if os.environ.has_key('CPP'): + cpp = os.environ['CPP'] + else: + cpp = cc + " -E" # not always + if os.environ.has_key('LDFLAGS'): + ldshared = ldshared + ' ' + os.environ['LDFLAGS'] + if os.environ.has_key('CFLAGS'): + opt = opt + ' ' + os.environ['CFLAGS'] + ldshared = ldshared + ' ' + os.environ['CFLAGS'] + if os.environ.has_key('CPPFLAGS'): + cpp = cpp + ' ' + os.environ['CPPFLAGS'] + opt = opt + ' ' + os.environ['CPPFLAGS'] + ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + cc_cmd = cc + ' ' + opt compiler.set_executables( - preprocessor=cc + " -E", # not always! + preprocessor=cpp, compiler=cc_cmd, compiler_so=cc_cmd + ' ' + ccshared, linker_so=ldshared, -- cgit v1.2.1 From 8c157b7b9e0b11cd6274e6fdbb476e142d61951f Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 5 Nov 2002 10:06:19 +0000 Subject: Must now give the basename - not including directories - of the install-script on the command line. Recreated after recompilation of wininst.exe. --- command/bdist_wininst.py | 704 ++++++++++++++++++++++++----------------------- 1 file changed, 358 insertions(+), 346 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index d996bee3..029a026b 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -41,8 +41,8 @@ class bdist_wininst (Command): ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), ('install-script=', None, - "installation script to be run after installation" - " or before deinstallation"), + "basename of installation script to be run after" + "installation or before deinstallation"), ] boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', @@ -78,11 +78,14 @@ class bdist_wininst (Command): self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) - if self.install_script and \ - self.install_script not in self.distribution.scripts: - raise DistutilsOptionError, \ - "install_script '%s' not found in scripts" % self.install_script - + if self.install_script: + for script in self.distribution.scripts: + if self.install_script == os.path.basename(script): + break + else: + raise DistutilsOptionError, \ + "install_script '%s' not found in scripts" % \ + self.install_script # finalize_options() @@ -264,362 +267,371 @@ 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 -BBIBAAAAAAAAAAAAAAAAAF0SAQAMEgEAAAAAAAAAAAAAAAAAaBIBABQSAQAAAAAAAAAAAAAAAABy -EgEAHBIBAAAAAAAAAAAAAAAAAH4SAQAkEgEAAAAAAAAAAAAAAAAAAAAAAAAAAACIEgEAlhIBAKYS -AQAAAAAAtBIBAAAAAADCEgEAAAAAANISAQAAAAAA3BIBAAAAAADiEgEAAAAAAPASAQAAAAAAChMB -AAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIuZGxsAENPTUNUTDMyLmRsbABHREkzMi5kbGwATVNW -Q1JULmRsbABvbGUzMi5kbGwAU0hFTEwzMi5kbGwAVVNFUjMyLmRsbAAATG9hZExpYnJhcnlBAABH -ZXRQcm9jQWRkcmVzcwAARXhpdFByb2Nlc3MAAABSZWdDbG9zZUtleQAAAFByb3BlcnR5U2hlZXRB -AABUZXh0T3V0QQAAZnJlZQAAQ29Jbml0aWFsaXplAABTSEdldFNwZWNpYWxGb2xkZXJQYXRoQQAA -AEdldERDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +UI/rL1wgGOpTDGoCrM2W7f9VIPDALmcQZronYy91JS67aFTH6Xbf891TAes7B1kO8yR0Cq3QHvkT +A41F9G4GAgx7n4UYQrB9/BIDvO7NNEjMNBR1CQvYlgbTfTN/DlZqBFYQ1BD7GlyE2HyJfg9hOIKz +3drmPOsmpSsCUyrQ+b5tW1OnCCWLBDvGdRcnEMKGNuEoco4KM8BsC+3/5FvJOIN9EAhTi10IaUOS +druwffI4k8jdUOjIVuJFsnzb3AwvUMgIFEBqAcz+c7ftGF4G2CVoqFEq8VCJXdS/sLDtLS2M1xw7 +dGn/dChQaO72+b6QmBlLBC6sjnQTGnOd+5YNfIsEyYr2IR8byFn3LTw6Lh9kQ+2w0VoDxUUSPsgP +3ea+U5fcGY1e8KQUxuPO8GHOgewo4auLVRBExv/tf4tMAvqNXALqV5/gK0MMK8GD6BaLG//L7f/P +gTtQSwUGiX3o8GsCg2UUAGaDewoA/5v77g+OYA7rCYtN7D/M6ItEESqNNBEDttkubzP6gT4BAjA6 +gT8Lv3Wf7QMEPC7BLpSJMQPKD79WbY/dth4I9AZOIAwcA1UV0W370rwITxyJwVcaA9CbEBYjNP72 +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 +gaGnSAIesM/xCi50V+tYkGJ3YV/rECMgFS5yEA4bqQBM+yAD+Flz2QJALiYAiDwAU/bGivswByfA +YIMtbU9z3ADr0E+fW1vJwJgN+Hdj5wAAAGgDACQAAP8AAAAAAAAAAABgvgDAQACNvgBQ//9Xg83/ +6xCQkJCQkJCKBkaIB0cB23UHix6D7vwR23LtuAEAAAAB23UHix6D7vwR2xHAAdtz73UJix6D7vwR +23PkMcmD6ANyDcHgCIoGRoPw/3R0icUB23UHix6D7vwR2xHJAdt1B4seg+78EdsRyXUgQQHbdQeL +HoPu/BHbEckB23PvdQmLHoPu/BHbc+SDwQKB/QDz//+D0QGNFC+D/fx2D4oCQogHR0l19+lj//// +kIsCg8IEiQeDxwSD6QR38QHP6Uz///9eife50QAAAIoHRyzoPAF394A/AXXyiweKXwRmwegIwcAQ +hsQp+IDr6AHwiQeDxwWJ2OLZjb4A4AAAiwcJwHQ8i18EjYQwMAEBAAHzUIPHCP+W5AEBAJWKB0cI +wHTciflXSPKuVf+W6AEBAAnAdAeJA4PDBOvh/5bsAQEAYekyX///AAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAIAAAAgAACABQAAAGAAAIAAAAAAAAAA +AAAAAAAAAAEAbgAAADgAAIAAAAAAAAAAAAAAAAAAAAEAAAAAAFAAAAAw0QAACAoAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAEAGsAAACQAACAbAAAALgAAIBtAAAA4AAAgG4AAAAIAQCAAAAAAAAAAAAA +AAAAAAABAAkEAACoAAAAONsAAKABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJBAAA0AAAANjc +AAAEAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAAPgAAADg3gAAWgIAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAABAAkEAAAgAQAAQOEAABQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwSAQDkEQEA +AAAAAAAAAAAAAAAAORIBAPQRAQAAAAAAAAAAAAAAAABGEgEA/BEBAAAAAAAAAAAAAAAAAFMSAQAE +EgEAAAAAAAAAAAAAAAAAXRIBAAwSAQAAAAAAAAAAAAAAAABoEgEAFBIBAAAAAAAAAAAAAAAAAHIS +AQAcEgEAAAAAAAAAAAAAAAAAfhIBACQSAQAAAAAAAAAAAAAAAAAAAAAAAAAAAIgSAQCWEgEAphIB +AAAAAAC0EgEAAAAAAMISAQAAAAAA0hIBAAAAAADcEgEAAAAAAOISAQAAAAAA8BIBAAAAAAAKEwEA +AAAAAEtFUk5FTDMyLkRMTABBRFZBUEkzMi5kbGwAQ09NQ1RMMzIuZGxsAEdESTMyLmRsbABNU1ZD +UlQuZGxsAG9sZTMyLmRsbABTSEVMTDMyLmRsbABVU0VSMzIuZGxsAABMb2FkTGlicmFyeUEAAEdl +dFByb2NBZGRyZXNzAABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEA +AFRleHRPdXRBAABmcmVlAABDb0luaXRpYWxpemUAAFNIR2V0U3BlY2lhbEZvbGRlclBhdGhBAAAA +R2V0REMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAA= +AAAAAAAAAAAAAAAAAAAAAA== """ # --- EOF --- -- cgit v1.2.1 From 47474c30d9a5ba83990c5370f74eba40d166d84c Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Tue, 5 Nov 2002 16:12:02 +0000 Subject: This patch fixes the following bugs: [#413582] g++ must be called for c++ extensions [#454030] distutils cannot link C++ code with GCC topdir = "Lib/distutils" * bcppcompiler.py (BCPPCompiler.create_static_lib): Fixed prototype, removing extra_preargs and extra_postargs parameters. Included target_lang parameter. (BCPPCompiler.link): Included target_lang parameter. * msvccompiler.py (MSVCCompiler.create_static_lib): Fixed prototype, removing extra_preargs and extra_postargs parameters. Included target_lang parameter. (MSVCCompiler.link): Included target_lang parameter. * ccompiler.py (CCompiler): New language_map and language_order attributes, used by CCompiler.detect_language(). (CCompiler.detect_language): New method, will return the language of a given source, or list of sources. Individual source language is detected using the language_map dict. When mixed sources are used, language_order will stablish the language precedence. (CCompiler.create_static_lib, CCompiler.link, CCompiler.link_executable, CCompiler.link_shared_object, CCompiler.link_shared_lib): Inlcuded target_lang parameter. * cygwinccompiler.py (CygwinCCompiler.link): Included target_lang parameter. * emxccompiler.py (EMXCCompiler.link): Included target_lang parameter. * mwerkscompiler.py (MWerksCompiler.link): Included target_lang parameter. * extension.py (Extension.__init__): New 'language' parameter/attribute, initialized to None by default. If provided will overlap the automatic detection made by CCompiler.detect_language(), in build_ext command. * sysconfig.py (customize_compiler): Check Makefile for CXX option, and also the environment variable CXX. Use the resulting value in the 'compiler_cxx' parameter of compiler.set_executables(). * unixccompiler.py (UnixCCompiler): Included 'compiler_cxx' in executables dict, defaulting to 'cc'. (UnixCCompiler.create_static_lib): Included target_lang parameter. (UnixCCompiler.link): Included target_lang parameter, and made linker command use compiler_cxx, if target_lang is 'c++'. * command/build_ext.py (build_ext.build_extension): Pass new ext.language attribute to compiler.link_shared_object()'s target_lang parameter. If ext.language is not provided, detect language using compiler.detect_language(sources) instead. * command/config.py (config._link): Pass already available lang parameter as target_lang parameter of compiler.link_executable(). --- bcppcompiler.py | 10 +++------ ccompiler.py | 63 +++++++++++++++++++++++++++++++++++++++++++++------- command/build_ext.py | 6 ++++- command/config.py | 3 ++- cygwinccompiler.py | 6 +++-- emxccompiler.py | 6 +++-- extension.py | 5 +++++ msvccompiler.py | 10 +++------ mwerkscompiler.py | 3 ++- sysconfig.py | 7 ++++-- unixccompiler.py | 12 ++++++---- 11 files changed, 96 insertions(+), 35 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 6e9d6c64..abe302a8 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -146,8 +146,7 @@ class BCPPCompiler(CCompiler) : output_libname, output_dir=None, debug=0, - extra_preargs=None, - extra_postargs=None): + target_lang=None): (objects, output_dir) = self._fix_object_args (objects, output_dir) output_filename = \ @@ -157,10 +156,6 @@ class BCPPCompiler(CCompiler) : lib_args = [output_filename, '/u'] + objects if debug: pass # XXX what goes here? - if extra_preargs: - lib_args[:0] = extra_preargs - if extra_postargs: - lib_args.extend (extra_postargs) try: self.spawn ([self.lib] + lib_args) except DistutilsExecError, msg: @@ -183,7 +178,8 @@ class BCPPCompiler(CCompiler) : debug=0, extra_preargs=None, extra_postargs=None, - build_temp=None): + build_temp=None, + target_lang=None): # XXX this ignores 'build_temp'! should follow the lead of # msvccompiler.py diff --git a/ccompiler.py b/ccompiler.py index 60d1caee..317e21ef 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -74,6 +74,19 @@ class CCompiler: shared_lib_format = None # prob. same as static_lib_format exe_extension = None # string + # Default language settings. language_map is used to detect a source + # file or Extension target language, checking source filenames. + # language_order is used to detect the language precedence, when deciding + # what language to use when mixing source types. For example, if some + # extension has two files with ".c" extension, and one with ".cpp", it + # is still linked as c++. + language_map = {".c" : "c", + ".cc" : "c++", + ".cpp" : "c++", + ".cxx" : "c++", + ".m" : "objc", + } + language_order = ["c++", "objc", "c"] def __init__ (self, verbose=0, @@ -572,6 +585,27 @@ class CCompiler: # _need_link () + def detect_language (self, sources): + """Detect the language of a given file, or list of files. Uses + language_map, and language_order to do the job. + """ + if type(sources) is not ListType: + sources = [sources] + lang = None + index = len(self.language_order) + for source in sources: + base, ext = os.path.splitext(source) + extlang = self.language_map.get(ext) + try: + extindex = self.language_order.index(extlang) + if extindex < index: + lang = extlang + index = extindex + except ValueError: + pass + return lang + + # detect_language () # -- Worker methods ------------------------------------------------ # (must be implemented by subclasses) @@ -671,7 +705,8 @@ class CCompiler: objects, output_libname, output_dir=None, - debug=0): + debug=0, + target_lang=None): """Link a bunch of stuff together to create a static library file. The "bunch of stuff" consists of the list of object files supplied as 'objects', the extra object files supplied to @@ -688,6 +723,10 @@ class CCompiler: compile step where this matters: the 'debug' flag is included here just for consistency). + 'target_lang' is the target language for which the given objects + are being compiled. This allows specific linkage time treatment of + certain languages. + Raises LibError on failure. """ pass @@ -710,7 +749,8 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None, - build_temp=None): + build_temp=None, + target_lang=None): """Link a bunch of stuff together to create an executable or shared library file. @@ -748,6 +788,10 @@ class CCompiler: of course that they supply command-line arguments for the particular linker being used). + 'target_lang' is the target language for which the given objects + are being compiled. This allows specific linkage time treatment of + certain languages. + Raises LinkError on failure. """ raise NotImplementedError @@ -766,13 +810,14 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None, - build_temp=None): + build_temp=None, + target_lang=None): self.link(CCompiler.SHARED_LIBRARY, objects, self.library_filename(output_libname, lib_type='shared'), output_dir, libraries, library_dirs, runtime_library_dirs, export_symbols, debug, - extra_preargs, extra_postargs, build_temp) + extra_preargs, extra_postargs, build_temp, target_lang) def link_shared_object (self, @@ -786,12 +831,13 @@ class CCompiler: debug=0, extra_preargs=None, extra_postargs=None, - build_temp=None): + build_temp=None, + target_lang=None): self.link(CCompiler.SHARED_OBJECT, objects, output_filename, output_dir, libraries, library_dirs, runtime_library_dirs, export_symbols, debug, - extra_preargs, extra_postargs, build_temp) + extra_preargs, extra_postargs, build_temp, target_lang) def link_executable (self, @@ -803,11 +849,12 @@ class CCompiler: runtime_library_dirs=None, debug=0, extra_preargs=None, - extra_postargs=None): + extra_postargs=None, + target_lang=None): self.link(CCompiler.EXECUTABLE, objects, self.executable_filename(output_progname), output_dir, libraries, library_dirs, runtime_library_dirs, None, - debug, extra_preargs, extra_postargs, None) + debug, extra_preargs, extra_postargs, None, target_lang) # -- Miscellaneous methods ----------------------------------------- diff --git a/command/build_ext.py b/command/build_ext.py index 934b4576..4bfc20c9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -477,6 +477,9 @@ class build_ext (Command): objects.extend(ext.extra_objects) extra_args = ext.extra_link_args or [] + # Detect target language, if not provided + language = ext.language or self.compiler.detect_language(sources) + self.compiler.link_shared_object( objects, ext_filename, libraries=self.get_libraries(ext), @@ -485,7 +488,8 @@ class build_ext (Command): extra_postargs=extra_args, export_symbols=self.get_export_symbols(ext), debug=self.debug, - build_temp=self.build_temp) + build_temp=self.build_temp, + target_lang=language) def swig_sources (self, sources): diff --git a/command/config.py b/command/config.py index 88b15866..9ebe0d91 100644 --- a/command/config.py +++ b/command/config.py @@ -148,7 +148,8 @@ class config (Command): prog = os.path.splitext(os.path.basename(src))[0] self.compiler.link_executable([obj], prog, libraries=libraries, - library_dirs=library_dirs) + library_dirs=library_dirs, + target_lang=lang) prog = prog + self.compiler.exe_extension self.temp_files.append(prog) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 9aabd8a6..a046ee21 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -140,7 +140,8 @@ class CygwinCCompiler (UnixCCompiler): debug=0, extra_preargs=None, extra_postargs=None, - build_temp=None): + build_temp=None, + target_lang=None): # use separate copies, so we can modify the lists extra_preargs = copy.copy(extra_preargs or []) @@ -218,7 +219,8 @@ class CygwinCCompiler (UnixCCompiler): debug, extra_preargs, extra_postargs, - build_temp) + build_temp, + target_lang) # link () diff --git a/emxccompiler.py b/emxccompiler.py index 7c3ad025..624c0fec 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -102,7 +102,8 @@ class EMXCCompiler (UnixCCompiler): debug=0, extra_preargs=None, extra_postargs=None, - build_temp=None): + build_temp=None, + target_lang=None): # use separate copies, so we can modify the lists extra_preargs = copy.copy(extra_preargs or []) @@ -171,7 +172,8 @@ class EMXCCompiler (UnixCCompiler): debug, extra_preargs, extra_postargs, - build_temp) + build_temp, + target_lang) # link () diff --git a/extension.py b/extension.py index d73bb08e..7fbeb4e1 100644 --- a/extension.py +++ b/extension.py @@ -75,6 +75,9 @@ class Extension: extension_name. depends : [string] list of files that the extension depends on + language : string + extension language (i.e. "c", "c++", "objc"). Will be detected + from the source extensions if not provided. """ def __init__ (self, name, sources, @@ -89,6 +92,7 @@ class Extension: extra_link_args=None, export_symbols=None, depends=None, + language=None, ): assert type(name) is StringType, "'name' must be a string" @@ -109,6 +113,7 @@ class Extension: self.extra_link_args = extra_link_args or [] self.export_symbols = export_symbols or [] self.depends = depends or [] + self.language = language # class Extension diff --git a/msvccompiler.py b/msvccompiler.py index 65b11435..a2459ad0 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -367,8 +367,7 @@ class MSVCCompiler (CCompiler) : output_libname, output_dir=None, debug=0, - extra_preargs=None, - extra_postargs=None): + target_lang=None): (objects, output_dir) = self._fix_object_args (objects, output_dir) output_filename = \ @@ -378,10 +377,6 @@ class MSVCCompiler (CCompiler) : lib_args = objects + ['/OUT:' + output_filename] if debug: pass # XXX what goes here? - if extra_preargs: - lib_args[:0] = extra_preargs - if extra_postargs: - lib_args.extend (extra_postargs) try: self.spawn ([self.lib] + lib_args) except DistutilsExecError, msg: @@ -404,7 +399,8 @@ class MSVCCompiler (CCompiler) : debug=0, extra_preargs=None, extra_postargs=None, - build_temp=None): + build_temp=None, + target_lang=None): (objects, output_dir) = self._fix_object_args (objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ diff --git a/mwerkscompiler.py b/mwerkscompiler.py index cd66a099..8f62bf7d 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -84,7 +84,8 @@ class MWerksCompiler (CCompiler) : debug=0, extra_preargs=None, extra_postargs=None, - build_temp=None): + build_temp=None, + target_lang=None): # First fixup. (objects, output_dir) = self._fix_object_args (objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ diff --git a/sysconfig.py b/sysconfig.py index e879fa14..cc571888 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -139,11 +139,13 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - (cc, opt, ccshared, ldshared, so_ext) = \ - get_config_vars('CC', 'OPT', 'CCSHARED', 'LDSHARED', 'SO') + (cc, cxx, opt, ccshared, ldshared, so_ext) = \ + get_config_vars('CC', 'CXX', 'OPT', 'CCSHARED', 'LDSHARED', 'SO') if os.environ.has_key('CC'): cc = os.environ['CC'] + if os.environ.has_key('CXX'): + cxx = os.environ['CXX'] if os.environ.has_key('CPP'): cpp = os.environ['CPP'] else: @@ -163,6 +165,7 @@ def customize_compiler(compiler): preprocessor=cpp, compiler=cc_cmd, compiler_so=cc_cmd + ' ' + ccshared, + compiler_cxx=cxx, linker_so=ldshared, linker_exe=cc) diff --git a/unixccompiler.py b/unixccompiler.py index 692e3eb4..2f4546e1 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -57,6 +57,7 @@ class UnixCCompiler(CCompiler): executables = {'preprocessor' : None, 'compiler' : ["cc"], 'compiler_so' : ["cc"], + 'compiler_cxx' : ["cc"], 'linker_so' : ["cc", "-shared"], 'linker_exe' : ["cc"], 'archiver' : ["ar", "-cr"], @@ -114,7 +115,7 @@ class UnixCCompiler(CCompiler): raise CompileError, msg def create_static_lib(self, objects, output_libname, - output_dir=None, debug=0): + output_dir=None, debug=0, target_lang=None): objects, output_dir = self._fix_object_args(objects, output_dir) output_filename = \ @@ -143,7 +144,7 @@ class UnixCCompiler(CCompiler): output_filename, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None, build_temp=None): + extra_postargs=None, build_temp=None, target_lang=None): objects, output_dir = self._fix_object_args(objects, output_dir) libraries, library_dirs, runtime_library_dirs = \ self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) @@ -167,9 +168,12 @@ class UnixCCompiler(CCompiler): self.mkpath(os.path.dirname(output_filename)) try: if target_desc == CCompiler.EXECUTABLE: - self.spawn(self.linker_exe + ld_args) + linker = self.linker_exe[:] else: - self.spawn(self.linker_so + ld_args) + linker = self.linker_so[:] + if target_lang == "c++" and self.compiler_cxx: + linker[0] = self.compiler_cxx[0] + self.spawn(linker + ld_args) except DistutilsExecError, msg: raise LinkError, msg else: -- cgit v1.2.1 From bc09b3f743da2691427c0a23f5b5aadfe90c7847 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 5 Nov 2002 20:11:08 +0000 Subject: Remove use of string module and reflow a couple of long lines. --- sysconfig.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index cc571888..222648aa 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -14,7 +14,6 @@ __revision__ = "$Id$" import os import re -import string import sys from errors import DistutilsPlatformError @@ -213,7 +212,7 @@ def parse_config_h(fp, g=None): m = define_rx.match(line) if m: n, v = m.group(1, 2) - try: v = string.atoi(v) + try: v = int(v) except ValueError: pass g[n] = v else: @@ -251,11 +250,11 @@ def parse_makefile(fn, g=None): m = _variable_rx.match(line) if m: n, v = m.group(1, 2) - v = string.strip(v) + v = v.strip() if "$" in v: notdone[n] = v else: - try: v = string.atoi(v) + try: v = int(v) except ValueError: pass done[n] = v @@ -272,9 +271,9 @@ def parse_makefile(fn, g=None): if "$" in after: notdone[name] = value else: - try: value = string.atoi(value) + try: value = int(value) except ValueError: - done[name] = string.strip(value) + done[name] = value.strip() else: done[name] = value del notdone[name] @@ -288,9 +287,9 @@ def parse_makefile(fn, g=None): if "$" in after: notdone[name] = value else: - try: value = string.atoi(value) + try: value = int(value) except ValueError: - done[name] = string.strip(value) + done[name] = value.strip() else: done[name] = value del notdone[name] @@ -369,8 +368,9 @@ def _init_posix(): # relative to the srcdir, which after installation no longer makes # sense. python_lib = get_python_lib(standard_lib=1) - linkerscript_name = os.path.basename(string.split(g['LDSHARED'])[0]) - linkerscript = os.path.join(python_lib, 'config', linkerscript_name) + linkerscript_name = os.path.basename(g['LDSHARED'].split()[0]) + linkerscript = os.path.join(python_lib, 'config', + linkerscript_name) # XXX this isn't the right place to do this: adding the Python # library to the link, if needed, should be in the "build_ext" -- cgit v1.2.1 From 152918876946cf1aba35f1a4e4e7a9ff53f37acc Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 5 Nov 2002 20:27:17 +0000 Subject: Repair inconsistent use of tabs and spaces. --- ccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 317e21ef..8898f51f 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -589,7 +589,7 @@ class CCompiler: """Detect the language of a given file, or list of files. Uses language_map, and language_order to do the job. """ - if type(sources) is not ListType: + if type(sources) is not ListType: sources = [sources] lang = None index = len(self.language_order) -- cgit v1.2.1 From 5211b8478b5bd08e9237dbf35840eee043efb320 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Wed, 6 Nov 2002 18:44:26 +0000 Subject: Fixed bug "[#466200] ability to specify a 'verify' script". * Lib/distutils/command/bdist_rpm.py (bdist_rpm.initialize_options): Included verify_script attribute. (bdist_rpm.finalize_package_data): Ensure that verify_script is a filename. (bdist_rpm._make_spec_file): Included verify_script in script_options tuple. * Misc/NEWS Mention change. --- command/bdist_rpm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 597b26c6..0dad1ac4 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -129,6 +129,7 @@ class bdist_rpm (Command): self.build_script = None self.install_script = None self.clean_script = None + self.verify_script = None self.pre_install = None self.post_install = None self.pre_uninstall = None @@ -208,6 +209,7 @@ class bdist_rpm (Command): self.ensure_filename('build_script') self.ensure_filename('install_script') self.ensure_filename('clean_script') + self.ensure_filename('verify_script') self.ensure_filename('pre_install') self.ensure_filename('post_install') self.ensure_filename('pre_uninstall') @@ -424,6 +426,7 @@ class bdist_rpm (Command): "--root=$RPM_BUILD_ROOT " "--record=INSTALLED_FILES") % self.python), ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), + ('verifyscript', 'verify_script', None), ('pre', 'pre_install', None), ('post', 'post_install', None), ('preun', 'pre_uninstall', None), -- cgit v1.2.1 From 013ee1e07785651f7b0bc75fdd3944b6409e7a31 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 7 Nov 2002 16:41:38 +0000 Subject: Fix a small bug when sys.argv[0] has an absolute path. See http://mail.python.org/pipermail/distutils-sig/2002-November/003039.html --- core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core.py b/core.py index d180eb8c..001e74be 100644 --- a/core.py +++ b/core.py @@ -87,7 +87,7 @@ def setup (**attrs): klass = Distribution if not attrs.has_key('script_name'): - attrs['script_name'] = sys.argv[0] + attrs['script_name'] = os.path.basename(sys.argv[0]) if not attrs.has_key('script_args'): attrs['script_args'] = sys.argv[1:] -- cgit v1.2.1 From 5ebe4f2073b0cfd36cbbd01e7c3bd18ec7a1f773 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 7 Nov 2002 16:46:19 +0000 Subject: Use dynamic linking for the SHGetSpecialFolderPath function, it is not always available on Windows NT. When the function cannot be loaded, get_special_folder_path raises OSError, "function not available". Compiled the exe, and rebuilt bdist_wininst.py. --- command/bdist_wininst.py | 662 +++++++++++++++++++++++------------------------ 1 file changed, 331 insertions(+), 331 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 029a026b..97a04582 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -265,11 +265,11 @@ if __name__ == '__main__': EXEDATA = """\ TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v -ZGUuDQ0KJAAAAAAAAAAtOHsRaVkVQmlZFUJpWRVCEkUZQmpZFUIGRh9CYlkVQupFG0JrWRVCBkYR -QmtZFUJpWRVCZlkVQmlZFELjWRVCC0YGQmRZFUJveh9Ca1kVQq5fE0JoWRVCUmljaGlZFUIAAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwAvl8c9AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAMAGAQAA +ZGUuDQ0KJAAAAAAAAAAjSEomZykkdWcpJHVnKSR1HDUodWQpJHUINi51bCkkdeQ1KnVlKSR1CDYg +dWUpJHVnKSR1aCkkdWcpJXXuKSR1BTY3dWwpJHVhCi51ZSkkdaAvInVmKSR1UmljaGcpJHUAAAAA +AAAAAAAAAAAAAAAAUEUAAEwBAwCllso9AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAAAHAQAA wAAAABABAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAAIAEAAAQAAAAAAAACAAAAAAAQAAAQ -AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwEQEA5AEAAAAQAQAwAQAAAAAAAAAAAAAAAAAAAAAA +AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwEQEAoAEAAAAQAQAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVUFgwAAAAAACwAAAAEAAAAAAAAAAEAAAA AAAAAAAAAAAAAACAAADgVVBYMQAAAAAAUAAAAMAAAABKAAAABAAAAAAAAAAAAAAAAAAAQAAA4C5y @@ -280,332 +280,332 @@ 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 @@ -618,16 +618,16 @@ AAAAAAAAAAEAbgAAADgAAIAAAAAAAAAAAAAAAAAAAAEAAAAAAFAAAAAw0QAACAoAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAEAGsAAACQAACAbAAAALgAAIBtAAAA4AAAgG4AAAAIAQCAAAAAAAAAAAAA AAAAAAABAAkEAACoAAAAONsAAKABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJBAAA0AAAANjc AAAEAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAAPgAAADg3gAAWgIAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAABAAkEAAAgAQAAQOEAABQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwSAQDkEQEA -AAAAAAAAAAAAAAAAORIBAPQRAQAAAAAAAAAAAAAAAABGEgEA/BEBAAAAAAAAAAAAAAAAAFMSAQAE -EgEAAAAAAAAAAAAAAAAAXRIBAAwSAQAAAAAAAAAAAAAAAABoEgEAFBIBAAAAAAAAAAAAAAAAAHIS -AQAcEgEAAAAAAAAAAAAAAAAAfhIBACQSAQAAAAAAAAAAAAAAAAAAAAAAAAAAAIgSAQCWEgEAphIB -AAAAAAC0EgEAAAAAAMISAQAAAAAA0hIBAAAAAADcEgEAAAAAAOISAQAAAAAA8BIBAAAAAAAKEwEA -AAAAAEtFUk5FTDMyLkRMTABBRFZBUEkzMi5kbGwAQ09NQ1RMMzIuZGxsAEdESTMyLmRsbABNU1ZD -UlQuZGxsAG9sZTMyLmRsbABTSEVMTDMyLmRsbABVU0VSMzIuZGxsAABMb2FkTGlicmFyeUEAAEdl -dFByb2NBZGRyZXNzAABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEA -AFRleHRPdXRBAABmcmVlAABDb0luaXRpYWxpemUAAFNIR2V0U3BlY2lhbEZvbGRlclBhdGhBAAAA -R2V0REMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAABAAkEAAAgAQAAQOEAABQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAABASAQDQEQEA +AAAAAAAAAAAAAAAAHRIBAOARAQAAAAAAAAAAAAAAAAAqEgEA6BEBAAAAAAAAAAAAAAAAADcSAQDw +EQEAAAAAAAAAAAAAAAAAQRIBAPgRAQAAAAAAAAAAAAAAAABMEgEAABIBAAAAAAAAAAAAAAAAAFYS +AQAIEgEAAAAAAAAAAAAAAAAAAAAAAAAAAABgEgEAbhIBAH4SAQAAAAAAjBIBAAAAAACaEgEAAAAA +AKoSAQAAAAAAtBIBAAAAAAC6EgEAAAAAAMgSAQAAAAAAS0VSTkVMMzIuRExMAEFEVkFQSTMyLmRs +bABDT01DVEwzMi5kbGwAR0RJMzIuZGxsAE1TVkNSVC5kbGwAb2xlMzIuZGxsAFVTRVIzMi5kbGwA +AExvYWRMaWJyYXJ5QQAAR2V0UHJvY0FkZHJlc3MAAEV4aXRQcm9jZXNzAAAAUmVnQ2xvc2VLZXkA +AABQcm9wZXJ0eVNoZWV0QQAAVGV4dE91dEEAAGZyZWUAAENvSW5pdGlhbGl6ZQAAR2V0REMAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -- cgit v1.2.1 From da858ca9d519e45c814af5f7cf45afc598d30059 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 8 Nov 2002 15:11:42 +0000 Subject: Fix comment typo --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 0dad1ac4..88be5e8b 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -434,7 +434,7 @@ class bdist_rpm (Command): ] for (rpm_opt, attr, default) in script_options: - # Insert contents of file referred to, if no file is refered to + # Insert contents of file referred to, if no file is referred to # use 'default' as contents of script val = getattr(self, attr) if val or default: -- cgit v1.2.1 From b309ef158743a718b302cf7566e03aa79cd600d7 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 8 Nov 2002 16:18:24 +0000 Subject: [Bug #233259] Ugly traceback for DistutilsPlatformError Fixed by catching all exceptions that are subclasses of DistutilsError, so only the error message will be printed. You can still get the whole traceback by enabling the Distutils debugging mode. --- core.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core.py b/core.py index 001e74be..51961454 100644 --- a/core.py +++ b/core.py @@ -145,9 +145,7 @@ def setup (**attrs): else: raise SystemExit, error - except (DistutilsExecError, - DistutilsFileError, - DistutilsOptionError, + except (DistutilsError, CCompilerError), msg: if DEBUG: raise -- cgit v1.2.1 From 8541483a41b056cfb0e5e2bf707f66c3afb1d0e6 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 13 Nov 2002 13:26:59 +0000 Subject: Update file --- README | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/README b/README index f1db3c6c..45c7ca8c 100644 --- a/README +++ b/README @@ -1,17 +1,21 @@ -This directory contains only a subset of the Distutils, specifically the -Python modules in the 'distutils' and 'distutils.command' packages. -Technically, this is all you need to distribute and install Python modules -using the Distutils. Most people will want some documentation and other -help, though. Currently, everything can be found at the Distutils web page: +This directory contains only a subset of the Distutils, specifically +the Python modules in the 'distutils' and 'distutils.command' +packages. This is all you need to distribute and install Python +modules using the Distutils. There is also a separately packaged +standalone version of the Distutils available for people who want to +upgrade the Distutils without upgrading Python, available from the +Distutils web page: http://www.python.org/sigs/distutils-sig/ -From there you can access the latest documentation, or download a standalone -Distutils release that includes all the code in this directory, plus -documentation, test scripts, examples, etc. +The standalone version includes all of the code in this directory, +plus documentation, test scripts, examples, etc. -The Distutils documentation isn't yet part of the standard Python -documentation set, but will be soon. +The Distutils documentation is divided into two documents, "Installing +Python Modules", which explains how to install Python packages, and +"Distributing Python Modules", which explains how to write setup.py +files. Both documents are part of the standard Python documentation +set, and are available from http://www.python.org/doc/current/ . Greg Ward (gward@python.net) -- cgit v1.2.1 From 02d558c61ba43389e4aba498833b2dfd6e55c26d Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 13 Nov 2002 17:03:05 +0000 Subject: Back out part of rev. 1.53, restoring the use of the string module. The two long lines have been reflowed differently; hopefully someone on BeOS can test them. Rev. 1.53 also converted string.atoi() to int(); I've left that alone. --- sysconfig.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 222648aa..3d369b00 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -14,6 +14,7 @@ __revision__ = "$Id$" import os import re +import string import sys from errors import DistutilsPlatformError @@ -250,7 +251,7 @@ def parse_makefile(fn, g=None): m = _variable_rx.match(line) if m: n, v = m.group(1, 2) - v = v.strip() + v = string.strip(v) if "$" in v: notdone[n] = v else: @@ -273,7 +274,7 @@ def parse_makefile(fn, g=None): else: try: value = int(value) except ValueError: - done[name] = value.strip() + done[name] = string.strip(value) else: done[name] = value del notdone[name] @@ -289,7 +290,7 @@ def parse_makefile(fn, g=None): else: try: value = int(value) except ValueError: - done[name] = value.strip() + done[name] = string.strip(value) else: done[name] = value del notdone[name] @@ -368,7 +369,8 @@ def _init_posix(): # relative to the srcdir, which after installation no longer makes # sense. python_lib = get_python_lib(standard_lib=1) - linkerscript_name = os.path.basename(g['LDSHARED'].split()[0]) + linkerscript_path = string.split(g['LDSHARED'])[0] + linkerscript_name = os.path.basename(linkerscript_path) linkerscript = os.path.join(python_lib, 'config', linkerscript_name) -- cgit v1.2.1 From 87b5c0ec3c8aded916170321554f653bafe57030 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 13 Nov 2002 20:54:21 +0000 Subject: Allow unknown keyword arguments to the Extension class, and warn about them. --- extension.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/extension.py b/extension.py index 7fbeb4e1..9dc316a1 100644 --- a/extension.py +++ b/extension.py @@ -10,6 +10,10 @@ __revision__ = "$Id$" import os, string from types import * +try: + import warnings +except ImportError: + warnings = None # This class is really only used by the "build_ext" command, so it might # make sense to put it in distutils.command.build_ext. However, that @@ -93,8 +97,8 @@ class Extension: export_symbols=None, depends=None, language=None, + **kw # To catch unknown keywords ): - assert type(name) is StringType, "'name' must be a string" assert (type(sources) is ListType and map(type, sources) == [StringType]*len(sources)), \ @@ -115,6 +119,15 @@ class Extension: self.depends = depends or [] self.language = language + # If there are unknown keyword options, warn about them + if len(kw): + L = kw.keys() ; L.sort() + L = map(repr, L) + msg = "Unknown Extension options: " + string.join(L, ', ') + if warnings is not None: + warnings.warn(msg) + else: + sys.stderr.write(msg + '\n') # class Extension -- cgit v1.2.1 From 649fdd11b8ff8d08957390b343eca0dbc5a9e3a5 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 14 Nov 2002 01:29:00 +0000 Subject: [Bug #599248] ext module generation problem If you have source files srcdir1/foo.c and srcdir2/foo.c, the temporary .o for both files is written to build/temp./foo.o. This patch sets strip_dir to false for both calls to object_filename, so now the object files are written to temp./srcdir1/foo.o and .../srcdir2/foo.o. 2.2 bugfix candidate --- ccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 8898f51f..8aca058b 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -366,7 +366,7 @@ class CCompiler: extra = [] # Get the list of expected output (object) files - objects = self.object_filenames(sources, 1, outdir) + objects = self.object_filenames(sources, 0, outdir) assert len(objects) == len(sources) # XXX should redo this code to eliminate skip_source entirely. @@ -472,7 +472,7 @@ class CCompiler: which source files can be skipped. """ # Get the list of expected output (object) files - objects = self.object_filenames(sources, strip_dir=1, + objects = self.object_filenames(sources, strip_dir=0, output_dir=output_dir) assert len(objects) == len(sources) -- cgit v1.2.1 From d47c1f5e8455cc049fc8c4ae2c00795e98c3abb7 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 14 Nov 2002 01:43:00 +0000 Subject: [Bug #550364] Add get_python_version() --- sysconfig.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 3d369b00..b12b98de 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -35,6 +35,14 @@ python_build = os.path.isfile(landmark) del argv0_path, landmark +def get_python_version (): + """Return a string containing the major and minor Python version, + leaving off the patchlevel. Sample return values could be '1.5' + or '2.2'. + """ + return sys.version[:3] + + def get_python_inc(plat_specific=0, prefix=None): """Return the directory containing installed Python header files. @@ -93,7 +101,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): if os.name == "posix": libpython = os.path.join(prefix, - "lib", "python" + sys.version[:3]) + "lib", "python" + get_python_version()) if standard_lib: return libpython else: -- cgit v1.2.1 From fb4cdac4953c41880107d487afc61c4b7ca937b2 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 14 Nov 2002 01:44:35 +0000 Subject: [Bug #550364] Use sysconfig.get_python_version() --- command/bdist_wininst.py | 2 +- command/build_ext.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 97a04582..dcc390e6 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -70,7 +70,7 @@ class bdist_wininst (Command): if not self.target_version: self.target_version = "" if self.distribution.has_ext_modules(): - short_version = sys.version[:3] + short_version = get_python_version() if self.target_version and self.target_version != short_version: raise DistutilsOptionError, \ "target version can only be" + short_version diff --git a/command/build_ext.py b/command/build_ext.py index 4bfc20c9..250e5393 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -12,7 +12,7 @@ import sys, os, string, re from types import * from distutils.core import Command from distutils.errors import * -from distutils.sysconfig import customize_compiler +from distutils.sysconfig import customize_compiler, get_python_version from distutils.dep_util import newer_group from distutils.extension import Extension from distutils import log @@ -184,7 +184,7 @@ class build_ext (Command): if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", - "python" + sys.version[:3], + "python" + get_python_version(), "config")) else: # building python standard extensions -- cgit v1.2.1 From fdfa7f054ccb881256c4696ad9dadbb5f0f9aa1c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 14 Nov 2002 01:58:48 +0000 Subject: Fix docstring typo; remove 'created' line --- errors.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/errors.py b/errors.py index bec34645..963d8337 100644 --- a/errors.py +++ b/errors.py @@ -5,11 +5,9 @@ modules may raise standard exceptions; in particular, SystemExit is usually raised for errors that are obviously the end-user's fault (eg. bad command-line arguments). -This module safe to use in "from ... import *" mode; it only exports +This module is safe to use in "from ... import *" mode; it only exports symbols whose names start with "Distutils" and end with "Error".""" -# created 1999/03/03, Greg Ward - __revision__ = "$Id$" class DistutilsError (Exception): -- cgit v1.2.1 From 4461f74828aed6a205722c0d9e697f8425771f8c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 14 Nov 2002 02:25:42 +0000 Subject: Remove 'created by' lines; people can use CVS for this, and the information is often out of date --- archive_util.py | 2 -- ccompiler.py | 2 -- cmd.py | 3 --- command/bdist.py | 2 -- command/bdist_dumb.py | 2 -- command/bdist_rpm.py | 2 -- command/bdist_wininst.py | 2 -- command/build.py | 2 -- command/build_clib.py | 3 --- command/build_ext.py | 2 -- command/build_py.py | 2 -- command/build_scripts.py | 2 -- command/config.py | 2 -- command/install.py | 2 -- command/install_headers.py | 2 -- command/install_lib.py | 2 -- command/sdist.py | 2 -- core.py | 2 -- cygwinccompiler.py | 2 -- dep_util.py | 2 -- dir_util.py | 2 -- dist.py | 3 --- emxccompiler.py | 2 -- extension.py | 2 -- fancy_getopt.py | 2 -- file_util.py | 2 -- filelist.py | 5 ----- msvccompiler.py | 2 +- spawn.py | 2 -- sysconfig.py | 1 - text_file.py | 2 -- unixccompiler.py | 2 -- util.py | 2 -- version.py | 2 -- 34 files changed, 1 insertion(+), 72 deletions(-) diff --git a/archive_util.py b/archive_util.py index 47fac0cb..98c1e559 100644 --- a/archive_util.py +++ b/archive_util.py @@ -3,8 +3,6 @@ Utility functions for creating archive files (tarballs, zip files, that sort of thing).""" -# created 2000/04/03, Greg Ward (extracted from util.py) - __revision__ = "$Id$" import os diff --git a/ccompiler.py b/ccompiler.py index 8aca058b..3084947d 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -3,8 +3,6 @@ Contains CCompiler, an abstract base class that defines the interface for the Distutils compiler abstraction model.""" -# created 1999/07/05, Greg Ward - __revision__ = "$Id$" import sys, os, re diff --git a/cmd.py b/cmd.py index 6a268184..b35eb078 100644 --- a/cmd.py +++ b/cmd.py @@ -4,9 +4,6 @@ Provides the Command class, the base class for the command classes in the distutils.command package. """ -# created 2000/04/03, Greg Ward -# (extricated from core.py; actually dates back to the beginning) - __revision__ = "$Id$" import sys, os, string, re diff --git a/command/bdist.py b/command/bdist.py index 454c9df1..e0648f3b 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -3,8 +3,6 @@ Implements the Distutils 'bdist' command (create a built [binary] distribution).""" -# created 2000/03/29, Greg Ward - __revision__ = "$Id$" import os, string diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 712fec88..7562b708 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,8 +4,6 @@ Implements the Distutils 'bdist_dumb' command (create a "dumb" built distribution -- i.e., just an archive to be unpacked under $prefix or $exec_prefix).""" -# created 2000/03/29, Greg Ward - __revision__ = "$Id$" import os diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 88be5e8b..a7bc45f4 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -3,8 +3,6 @@ Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions).""" -# created 2000/04/25, by Harry Henry Gebel - __revision__ = "$Id$" import sys, os, string diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index dcc390e6..c5cfd6d3 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -3,8 +3,6 @@ Implements the Distutils 'bdist_wininst' command: create a windows installer exe-program.""" -# created 2000/06/02, Thomas Heller - __revision__ = "$Id$" import sys, os, string diff --git a/command/build.py b/command/build.py index 6bc92a43..0643948c 100644 --- a/command/build.py +++ b/command/build.py @@ -2,8 +2,6 @@ Implements the Distutils 'build' command.""" -# created 1999/03/08, Greg Ward - __revision__ = "$Id$" import sys, os diff --git a/command/build_clib.py b/command/build_clib.py index f0207e4e..fe921fb8 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,9 +4,6 @@ Implements the Distutils 'build_clib' command, to build a C/C++ library that is included in the module distribution and needed by an extension module.""" -# created (an empty husk) 1999/12/18, Greg Ward -# fleshed out 2000/02/03-04 - __revision__ = "$Id$" diff --git a/command/build_ext.py b/command/build_ext.py index 250e5393..0b3ef149 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,8 +4,6 @@ Implements the Distutils 'build_ext' command, for building extension modules (currently limited to C extensions, should accommodate C++ extensions ASAP).""" -# created 1999/08/09, Greg Ward - __revision__ = "$Id$" import sys, os, string, re diff --git a/command/build_py.py b/command/build_py.py index 0ab72c08..f61dc17a 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -2,8 +2,6 @@ Implements the Distutils 'build_py' command.""" -# created 1999/03/08, Greg Ward - __revision__ = "$Id$" import sys, string, os diff --git a/command/build_scripts.py b/command/build_scripts.py index 211ade40..e4d6099b 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -2,8 +2,6 @@ Implements the Distutils 'build_scripts' command.""" -# created 2000/05/23, Bastian Kleineidam - __revision__ = "$Id$" import sys, os, re diff --git a/command/config.py b/command/config.py index 9ebe0d91..abfa1385 100644 --- a/command/config.py +++ b/command/config.py @@ -9,8 +9,6 @@ configure-like tasks: "try to compile this C code", or "figure out where this header file lives". """ -# created 2000/05/29, Greg Ward - __revision__ = "$Id$" import sys, os, string, re diff --git a/command/install.py b/command/install.py index 67f37893..9adda468 100644 --- a/command/install.py +++ b/command/install.py @@ -4,8 +4,6 @@ Implements the Distutils 'install' command.""" from distutils import log -# created 1999/03/13, Greg Ward - __revision__ = "$Id$" import sys, os, string diff --git a/command/install_headers.py b/command/install_headers.py index 495d150c..957ed239 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -3,8 +3,6 @@ Implements the Distutils 'install_headers' command, to install C/C++ header files to the Python include directory.""" -# created 2000/05/26, Greg Ward - __revision__ = "$Id$" import os diff --git a/command/install_lib.py b/command/install_lib.py index 1e771c61..5da1c7ae 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -1,5 +1,3 @@ -# created 1999/03/13, Greg Ward - __revision__ = "$Id$" import sys, os, string diff --git a/command/sdist.py b/command/sdist.py index 082aa88c..91807e6a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -2,8 +2,6 @@ Implements the Distutils 'sdist' command (create a source distribution).""" -# created 1999/09/22, Greg Ward - __revision__ = "$Id$" import sys, os, string diff --git a/core.py b/core.py index 51961454..5be9e4ec 100644 --- a/core.py +++ b/core.py @@ -6,8 +6,6 @@ indirectly provides the Distribution and Command classes, although they are really defined in distutils.dist and distutils.cmd. """ -# created 1999/03/01, Greg Ward - __revision__ = "$Id$" import sys, os diff --git a/cygwinccompiler.py b/cygwinccompiler.py index a046ee21..93f88038 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -41,8 +41,6 @@ cygwin in no-cygwin mode). # in the dlls. # *** only the version of June 2000 shows these problems -# created 2000/05/05, Rene Liebscher - __revision__ = "$Id$" import os,sys,copy diff --git a/dep_util.py b/dep_util.py index bbb6d235..f4966548 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,8 +4,6 @@ Utility functions for simple, timestamp-based dependency of files and groups of files; also, function based entirely on such timestamp dependency analysis.""" -# created 2000/04/03, Greg Ward (extracted from util.py) - __revision__ = "$Id$" import os diff --git a/dir_util.py b/dir_util.py index 8b3e06b2..d407e9ac 100644 --- a/dir_util.py +++ b/dir_util.py @@ -2,8 +2,6 @@ Utility functions for manipulating directories and directory trees.""" -# created 2000/04/03, Greg Ward (extracted from util.py) - __revision__ = "$Id$" import os diff --git a/dist.py b/dist.py index dbeeb8b1..3a690696 100644 --- a/dist.py +++ b/dist.py @@ -4,9 +4,6 @@ Provides the Distribution class, which represents the module distribution being built/installed/distributed. """ -# created 2000/04/03, Greg Ward -# (extricated from core.py; actually dates back to the beginning) - __revision__ = "$Id$" import sys, os, string, re diff --git a/emxccompiler.py b/emxccompiler.py index 624c0fec..76bdbae5 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -19,8 +19,6 @@ handles the EMX port of the GNU C compiler to OS/2. # # * EMX gcc 2.81/EMX 0.9d fix03 -# created 2001/5/7, Andrew MacIntyre, from Rene Liebscher's cywinccompiler.py - __revision__ = "$Id$" import os,sys,copy diff --git a/extension.py b/extension.py index 9dc316a1..5b197fa5 100644 --- a/extension.py +++ b/extension.py @@ -3,8 +3,6 @@ Provides the Extension class, used to describe C/C++ extension modules in setup scripts.""" -# created 2000/05/30, Greg Ward - __revision__ = "$Id$" import os, string diff --git a/fancy_getopt.py b/fancy_getopt.py index 5de64e15..f78b0a68 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,8 +8,6 @@ additional features: * options set attributes of a passed-in object """ -# created 1999/03/03, Greg Ward - __revision__ = "$Id$" import sys, string, re diff --git a/file_util.py b/file_util.py index 56b1faee..c2fa086f 100644 --- a/file_util.py +++ b/file_util.py @@ -3,8 +3,6 @@ Utility functions for operating on single files. """ -# created 2000/04/03, Greg Ward (extracted from util.py) - __revision__ = "$Id$" import os diff --git a/filelist.py b/filelist.py index b4f3269c..4b5d47d0 100644 --- a/filelist.py +++ b/filelist.py @@ -4,11 +4,6 @@ Provides the FileList class, used for poking about the filesystem and building lists of files. """ -# created 2000/07/17, Rene Liebscher (as template.py) -# most parts taken from commands/sdist.py -# renamed 2000/07/29 (to filelist.py) and officially added to -# the Distutils source, Greg Ward - __revision__ = "$Id$" import os, string, re diff --git a/msvccompiler.py b/msvccompiler.py index a2459ad0..c2bd77de 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -4,7 +4,7 @@ Contains MSVCCompiler, an implementation of the abstract CCompiler class for the Microsoft Visual Studio.""" -# created 1999/08/19, Perry Stoll +# Written by Perry Stoll # hacked by Robin Becker and Thomas Heller to do a better job of # finding DevStudio (through the registry) diff --git a/spawn.py b/spawn.py index 4df6e097..f94817d4 100644 --- a/spawn.py +++ b/spawn.py @@ -6,8 +6,6 @@ Also provides the 'find_executable()' to search the path for a given executable name. """ -# created 1999/07/24, Greg Ward - __revision__ = "$Id$" import sys, os, string diff --git a/sysconfig.py b/sysconfig.py index b12b98de..aa3636f6 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -7,7 +7,6 @@ available. Written by: Fred L. Drake, Jr. Email: -Initial date: 17-Dec-1998 """ __revision__ = "$Id$" diff --git a/text_file.py b/text_file.py index 7086b1af..67efd65e 100644 --- a/text_file.py +++ b/text_file.py @@ -4,8 +4,6 @@ provides the TextFile class, which gives an interface to text files that (optionally) takes care of stripping comments, ignoring blank lines, and joining lines with backslashes.""" -# created 1999/01/12, Greg Ward - __revision__ = "$Id$" from types import * diff --git a/unixccompiler.py b/unixccompiler.py index 2f4546e1..603dfe90 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -13,8 +13,6 @@ the "typical" Unix-style command-line C compiler: * link shared library handled by 'cc -shared' """ -# created 1999/07/05, Greg Ward - __revision__ = "$Id$" import os, sys diff --git a/util.py b/util.py index 8d224154..9de6077f 100644 --- a/util.py +++ b/util.py @@ -4,8 +4,6 @@ Miscellaneous utility functions -- anything that doesn't fit into one of the other *util.py modules. """ -# created 1999/03/08, Greg Ward - __revision__ = "$Id$" import sys, os, string, re diff --git a/version.py b/version.py index 02502dac..71a56147 100644 --- a/version.py +++ b/version.py @@ -4,8 +4,6 @@ # Implements multiple version numbering conventions for the # Python Module Distribution Utilities. # -# written by Greg Ward, 1998/12/17 -# # $Id$ # -- cgit v1.2.1 From 58e414fbe6bab66065fc5b5a0705980f578f4796 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 19 Nov 2002 13:12:28 +0000 Subject: Add comment to Distutil files about requiring 1.5.2 compatibility, as suggested by PEP 291. --- __init__.py | 2 ++ archive_util.py | 2 ++ bcppcompiler.py | 2 ++ ccompiler.py | 2 ++ cmd.py | 2 ++ command/__init__.py | 2 ++ command/bdist.py | 2 ++ command/bdist_dumb.py | 2 ++ command/bdist_rpm.py | 2 ++ command/bdist_wininst.py | 2 ++ command/build.py | 2 ++ command/build_clib.py | 2 ++ command/build_ext.py | 2 ++ command/build_py.py | 2 ++ command/build_scripts.py | 2 ++ command/clean.py | 2 ++ command/config.py | 2 ++ command/install.py | 2 ++ command/install_data.py | 2 ++ command/install_headers.py | 2 ++ command/install_lib.py | 2 ++ command/install_scripts.py | 2 ++ command/sdist.py | 2 ++ core.py | 2 ++ cygwinccompiler.py | 2 ++ debug.py | 4 ++++ dep_util.py | 2 ++ dir_util.py | 2 ++ dist.py | 2 ++ errors.py | 2 ++ fancy_getopt.py | 2 ++ file_util.py | 2 ++ filelist.py | 2 ++ log.py | 2 ++ msvccompiler.py | 3 ++- mwerkscompiler.py | 4 ++++ spawn.py | 2 ++ 37 files changed, 78 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 1055aa39..7873d297 100644 --- a/__init__.py +++ b/__init__.py @@ -8,6 +8,8 @@ used from a setup script as setup (...) """ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" __version__ = "1.0.3" diff --git a/archive_util.py b/archive_util.py index 98c1e559..d1dc9095 100644 --- a/archive_util.py +++ b/archive_util.py @@ -3,6 +3,8 @@ Utility functions for creating archive files (tarballs, zip files, that sort of thing).""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os diff --git a/bcppcompiler.py b/bcppcompiler.py index abe302a8..cfbe04ac 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,6 +11,8 @@ for the Borland C++ compiler. # someone should sit down and factor out the common code as # WindowsCCompiler! --GPW +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" diff --git a/ccompiler.py b/ccompiler.py index 3084947d..edb9f754 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -3,6 +3,8 @@ Contains CCompiler, an abstract base class that defines the interface for the Distutils compiler abstraction model.""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, re diff --git a/cmd.py b/cmd.py index b35eb078..1165f951 100644 --- a/cmd.py +++ b/cmd.py @@ -4,6 +4,8 @@ Provides the Command class, the base class for the command classes in the distutils.command package. """ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string, re diff --git a/command/__init__.py b/command/__init__.py index e70429c8..fc611716 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -3,6 +3,8 @@ Package containing implementation of all the standard Distutils commands.""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" __all__ = ['build', diff --git a/command/bdist.py b/command/bdist.py index e0648f3b..7c606ffc 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -3,6 +3,8 @@ Implements the Distutils 'bdist' command (create a built [binary] distribution).""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os, string diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 7562b708..d1cce55b 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,6 +4,8 @@ Implements the Distutils 'bdist_dumb' command (create a "dumb" built distribution -- i.e., just an archive to be unpacked under $prefix or $exec_prefix).""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index a7bc45f4..237cc70d 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -3,6 +3,8 @@ Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions).""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index c5cfd6d3..9c9fd109 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -3,6 +3,8 @@ Implements the Distutils 'bdist_wininst' command: create a windows installer exe-program.""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string diff --git a/command/build.py b/command/build.py index 0643948c..78231541 100644 --- a/command/build.py +++ b/command/build.py @@ -2,6 +2,8 @@ Implements the Distutils 'build' command.""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os diff --git a/command/build_clib.py b/command/build_clib.py index fe921fb8..ef03ed72 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,6 +4,8 @@ Implements the Distutils 'build_clib' command, to build a C/C++ library that is included in the module distribution and needed by an extension module.""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" diff --git a/command/build_ext.py b/command/build_ext.py index 0b3ef149..0c377681 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,6 +4,8 @@ Implements the Distutils 'build_ext' command, for building extension modules (currently limited to C extensions, should accommodate C++ extensions ASAP).""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string, re diff --git a/command/build_py.py b/command/build_py.py index f61dc17a..258d6d4c 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -2,6 +2,8 @@ Implements the Distutils 'build_py' command.""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, string, os diff --git a/command/build_scripts.py b/command/build_scripts.py index e4d6099b..b7c11d47 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -2,6 +2,8 @@ Implements the Distutils 'build_scripts' command.""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, re diff --git a/command/clean.py b/command/clean.py index 36252d51..41b22777 100644 --- a/command/clean.py +++ b/command/clean.py @@ -4,6 +4,8 @@ Implements the Distutils 'clean' command.""" # contributed by Bastian Kleineidam , added 2000-03-18 +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os diff --git a/command/config.py b/command/config.py index abfa1385..b6f5ad1d 100644 --- a/command/config.py +++ b/command/config.py @@ -9,6 +9,8 @@ configure-like tasks: "try to compile this C code", or "figure out where this header file lives". """ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string, re diff --git a/command/install.py b/command/install.py index 9adda468..5d5bdaa7 100644 --- a/command/install.py +++ b/command/install.py @@ -4,6 +4,8 @@ Implements the Distutils 'install' command.""" from distutils import log +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string diff --git a/command/install_data.py b/command/install_data.py index d0091ce2..5c1f18a9 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -5,6 +5,8 @@ platform-independent data files.""" # contributed by Bastian Kleineidam +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os diff --git a/command/install_headers.py b/command/install_headers.py index 957ed239..3a37d309 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -3,6 +3,8 @@ Implements the Distutils 'install_headers' command, to install C/C++ header files to the Python include directory.""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os diff --git a/command/install_lib.py b/command/install_lib.py index 5da1c7ae..daf3e010 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -1,3 +1,5 @@ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string diff --git a/command/install_scripts.py b/command/install_scripts.py index ceece1b6..6572e650 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -5,6 +5,8 @@ Python scripts.""" # contributed by Bastian Kleineidam +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os diff --git a/command/sdist.py b/command/sdist.py index 91807e6a..c0b7dd45 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -2,6 +2,8 @@ Implements the Distutils 'sdist' command (create a source distribution).""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string diff --git a/core.py b/core.py index 5be9e4ec..9ab419ef 100644 --- a/core.py +++ b/core.py @@ -6,6 +6,8 @@ indirectly provides the Distribution and Command classes, although they are really defined in distutils.dist and distutils.cmd. """ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 93f88038..18af388c 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -41,6 +41,8 @@ cygwin in no-cygwin mode). # in the dlls. # *** only the version of June 2000 shows these problems +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os,sys,copy diff --git a/debug.py b/debug.py index 7ca76d6c..e195ebdc 100644 --- a/debug.py +++ b/debug.py @@ -1,5 +1,9 @@ import os +# This module should be kept compatible with Python 1.5.2. + +__revision__ = "$Id$" + # If DISTUTILS_DEBUG is anything other than the empty string, we run in # debug mode. DEBUG = os.environ.get('DISTUTILS_DEBUG') diff --git a/dep_util.py b/dep_util.py index f4966548..0746633d 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,6 +4,8 @@ Utility functions for simple, timestamp-based dependency of files and groups of files; also, function based entirely on such timestamp dependency analysis.""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os diff --git a/dir_util.py b/dir_util.py index d407e9ac..ca9fa9dc 100644 --- a/dir_util.py +++ b/dir_util.py @@ -2,6 +2,8 @@ Utility functions for manipulating directories and directory trees.""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os diff --git a/dist.py b/dist.py index 3a690696..faeb7b10 100644 --- a/dist.py +++ b/dist.py @@ -4,6 +4,8 @@ Provides the Distribution class, which represents the module distribution being built/installed/distributed. """ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string, re diff --git a/errors.py b/errors.py index 963d8337..94e83fb5 100644 --- a/errors.py +++ b/errors.py @@ -8,6 +8,8 @@ usually raised for errors that are obviously the end-user's fault This module is safe to use in "from ... import *" mode; it only exports symbols whose names start with "Distutils" and end with "Error".""" +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" class DistutilsError (Exception): diff --git a/fancy_getopt.py b/fancy_getopt.py index f78b0a68..a4a4e797 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,6 +8,8 @@ additional features: * options set attributes of a passed-in object """ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, string, re diff --git a/file_util.py b/file_util.py index c2fa086f..e230ce58 100644 --- a/file_util.py +++ b/file_util.py @@ -3,6 +3,8 @@ Utility functions for operating on single files. """ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os diff --git a/filelist.py b/filelist.py index 4b5d47d0..bfa53d21 100644 --- a/filelist.py +++ b/filelist.py @@ -4,6 +4,8 @@ Provides the FileList class, used for poking about the filesystem and building lists of files. """ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import os, string, re diff --git a/log.py b/log.py index 6aeb7c9a..0442033d 100644 --- a/log.py +++ b/log.py @@ -1,5 +1,7 @@ """A simple log mechanism styled after PEP 282.""" +# This module should be kept compatible with Python 1.5.2. + # The class here is styled after PEP 282 so that it could later be # replaced with a standard Python logging implementation. diff --git a/msvccompiler.py b/msvccompiler.py index c2bd77de..65a50cc7 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -3,11 +3,12 @@ Contains MSVCCompiler, an implementation of the abstract CCompiler class for the Microsoft Visual Studio.""" - # Written by Perry Stoll # hacked by Robin Becker and Thomas Heller to do a better job of # finding DevStudio (through the registry) +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 8f62bf7d..d546de1f 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -4,6 +4,10 @@ Contains MWerksCompiler, an implementation of the abstract CCompiler class for MetroWerks CodeWarrior on the Macintosh. Needs work to support CW on Windows.""" +# This module should be kept compatible with Python 1.5.2. + +__revision__ = "$Id$" + import sys, os, string from types import * from distutils.errors import \ diff --git a/spawn.py b/spawn.py index f94817d4..6e0423b0 100644 --- a/spawn.py +++ b/spawn.py @@ -6,6 +6,8 @@ Also provides the 'find_executable()' to search the path for a given executable name. """ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string -- cgit v1.2.1 From 6ff384d0805ec8da74e606380d9526d73cac0c8b Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 20 Nov 2002 16:10:29 +0000 Subject: Add missing import --- extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.py b/extension.py index 5b197fa5..e4209449 100644 --- a/extension.py +++ b/extension.py @@ -5,7 +5,7 @@ modules in setup scripts.""" __revision__ = "$Id$" -import os, string +import os, string, sys from types import * try: -- cgit v1.2.1 From 66ffcc6481adb327ae0c529f439bed2c418e97ef Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Nov 2002 18:33:28 +0000 Subject: Bug #639118 from Ollie Oldham: archiver should use zipfile before zip Previously archive_util.py attempted to spawn an external 'zip' program for the zip action, if this fails, an attempt to import zipfile.py is made... This bites folks who have 'old' or non-conforming zip programs on windows platforms. This change tries the 'zipfile' module first, falling back to spawning a zip process if the module isn't available. --- archive_util.py | 58 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/archive_util.py b/archive_util.py index d1dc9095..d5b30966 100644 --- a/archive_util.py +++ b/archive_util.py @@ -59,46 +59,48 @@ def make_tarball (base_name, base_dir, compress="gzip", def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): """Create a zip file from all the files under 'base_dir'. The output - zip file will be named 'base_dir' + ".zip". Uses either the InfoZIP - "zip" utility (if installed and found on the default search path) or - the "zipfile" Python module (if available). If neither tool is - available, raises DistutilsExecError. Returns the name of the output - zip file. + zip file will be named 'base_dir' + ".zip". Uses either the "zipfile" + Python module (if available) or the InfoZIP "zip" utility (if installed + and found on the default search path). If neither tool is available, + raises DistutilsExecError. Returns the name of the output zip file. """ - # This initially assumed the Unix 'zip' utility -- but - # apparently InfoZIP's zip.exe works the same under Windows, so - # no changes needed! - + try: + import zipfile + except ImportError: + zipfile = None + zip_filename = base_name + ".zip" mkpath(os.path.dirname(zip_filename), dry_run=dry_run) - try: - spawn(["zip", "-rq", zip_filename, base_dir], - dry_run=dry_run) - except DistutilsExecError: - - # XXX really should distinguish between "couldn't find - # external 'zip' command" and "zip failed" -- shouldn't try - # again in the latter case. (I think fixing this will - # require some cooperation from the spawn module -- perhaps - # a utility function to search the path, so we can fallback - # on zipfile.py without the failed spawn.) + + # If zipfile module is not available, try spawning an external + # 'zip' command. + if zipfile is None: + if verbose: + zipoptions = "-r" + else: + zipoptions = "-rq" + try: - import zipfile - except ImportError: + spawn(["zip", zipoptions, zip_filename, base_dir], + dry_run=dry_run) + except DistutilsExecError: + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed". raise DistutilsExecError, \ - ("unable to create zip file '%s': " + - "could neither find a standalone zip utility nor " + - "import the 'zipfile' module") % zip_filename + ("unable to create zip file '%s': " + "could neither import the 'zipfile' module nor " + "find a standalone zip utility") % zip_filename - - log.info("creating '%s' and adding '%s' to it", + else: + log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) - + def visit (z, dirname, names): for name in names: path = os.path.normpath(os.path.join(dirname, name)) if os.path.isfile(path): z.write(path, path) + log.info("adding '%s'" % path) if not dry_run: z = zipfile.ZipFile(zip_filename, "w", -- cgit v1.2.1 From c8618f4eddcd6ecc5ea54387494cb5d8a00f8dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 21 Nov 2002 20:33:24 +0000 Subject: Patch #642019: Recognize gcc-x.y as gcc. --- unixccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 603dfe90..2a6b1bee 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -201,7 +201,7 @@ class UnixCCompiler(CCompiler): if sys.platform[:6] == "darwin": # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir - elif compiler == "gcc" or compiler == "g++": + elif compiler[:3] == "gcc" or compiler[:3] == "g++": return "-Wl,-R" + dir else: return "-R" + dir -- cgit v1.2.1 From 2fe3abe410be345020e460b3be5860c4d60a2223 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Nov 2002 20:41:07 +0000 Subject: Reflow comment --- spawn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spawn.py b/spawn.py index 6e0423b0..4857ce5e 100644 --- a/spawn.py +++ b/spawn.py @@ -25,9 +25,9 @@ def spawn (cmd, There is no way to run a program with a name different from that of its executable. - If 'search_path' is true (the default), the system's executable search - path will be used to find the program; otherwise, cmd[0] must be the - exact path to the executable.If 'dry_run' is true, + If 'search_path' is true (the default), the system's executable + search path will be used to find the program; otherwise, cmd[0] + must be the exact path to the executable. If 'dry_run' is true, the command will not actually be run. Raise DistutilsExecError if running the program fails in any way; just -- cgit v1.2.1 From 90b1c1a1b6dfb3d5ed0ef835e62d94ea2c45f4fa Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 22 Nov 2002 20:57:20 +0000 Subject: get_python_version was not imported. --- command/bdist_wininst.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 9c9fd109..7609fabe 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -13,6 +13,7 @@ from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import create_tree, remove_tree from distutils.errors import * +from distutils.sysconfig import get_python_version from distutils import log class bdist_wininst (Command): -- cgit v1.2.1 From fc4c1ada98f887c3423daee9c803753513fba0ba Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 22 Nov 2002 21:08:34 +0000 Subject: (This is hopefully the last large, funny checkin message for bdist_wininst.py we will see.) Removed the base64 encoded binary contents, wininst.exe must be in the same directory as this file now. wininst.exe must be recompiled and commited each time the sources in PC/bdist_wininst are changed. --- command/bdist_wininst.py | 404 +---------------------------------------------- command/wininst.exe | Bin 0 -> 20992 bytes 2 files changed, 4 insertions(+), 400 deletions(-) create mode 100755 command/wininst.exe diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 7609fabe..5acca11a 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -8,7 +8,6 @@ exe-program.""" __revision__ = "$Id$" import sys, os, string -import base64 from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import create_tree, remove_tree @@ -236,403 +235,8 @@ class bdist_wininst (Command): # create_exe() def get_exe_bytes (self): - return base64.decodestring(EXEDATA) + # wininst.exe is in the same directory as this file + directory = os.path.dirname(__file__) + filename = os.path.join(directory, "wininst.exe") + return open(filename, "rb").read() # class bdist_wininst - -if __name__ == '__main__': - # recreate EXEDATA from wininst.exe by rewriting this file - - # If you want to do this at home, you should: - # - checkout the *distutils* source code - # (see also http://sourceforge.net/cvs/?group_id=5470) - # by doing: - # cvs -d:pserver:anonymous@cvs.python.sourceforge.net:/cvsroot/python login - # and - # cvs -z3 -d:pserver:anonymous@cvs.python.sourceforge.net:/cvsroot/python co distutils - # - Built wininst.exe from the MSVC project file distutils/misc/wininst.dsw - # - Execute this file (distutils/distutils/command/bdist_wininst.py) - - import re - moddata = open("bdist_wininst.py", "r").read() - exedata = open("../../misc/wininst.exe", "rb").read() - print "wininst.exe length is %d bytes" % len(exedata) - print "wininst.exe encoded length is %d bytes" % len(base64.encodestring(exedata)) - exp = re.compile('EXE'+'DATA = """\\\\(\n.*)*\n"""', re.M) - data = exp.sub('EXE' + 'DATA = """\\\\\n%s"""' % - base64.encodestring(exedata), moddata) - open("bdist_wininst.py", "w").write(data) - print "bdist_wininst.py recreated" - -EXEDATA = """\ -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 -puyNoTAHJ8BPc5LBBlvTAOvQT8C0z62whA3Ud0HnAwAAAAAAABIA/wAAAAAAAAAAYL4AwEAAjb4A -UP//V4PN/+sQkJCQkJCQigZGiAdHAdt1B4seg+78Edty7bgBAAAAAdt1B4seg+78EdsRwAHbc+91 -CYseg+78Edtz5DHJg+gDcg3B4AiKBkaD8P90dInFAdt1B4seg+78EdsRyQHbdQeLHoPu/BHbEcl1 -IEEB23UHix6D7vwR2xHJAdtz73UJix6D7vwR23Pkg8ECgf0A8///g9EBjRQvg/38dg+KAkKIB0dJ -dffpY////5CLAoPCBIkHg8cEg+kEd/EBz+lM////Xon3udEAAACKB0cs6DwBd/eAPwF18osHil8E -ZsHoCMHAEIbEKfiA6+gB8IkHg8cFidji2Y2+AOAAAIsHCcB0PItfBI2EMDABAQAB81CDxwj/ltAB -AQCVigdHCMB03In5V0jyrlX/ltQBAQAJwHQHiQODwwTr4f+W2AEBAGHpMl///wAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAIAAAAgAACABQAAAGAAAIAAAAAAAAAA -AAAAAAAAAAEAbgAAADgAAIAAAAAAAAAAAAAAAAAAAAEAAAAAAFAAAAAw0QAACAoAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAEAGsAAACQAACAbAAAALgAAIBtAAAA4AAAgG4AAAAIAQCAAAAAAAAAAAAA -AAAAAAABAAkEAACoAAAAONsAAKABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJBAAA0AAAANjc -AAAEAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAAPgAAADg3gAAWgIAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAABAAkEAAAgAQAAQOEAABQBAAAAAAAAAAAAAAAAAAAAAAAAAAAAABASAQDQEQEA -AAAAAAAAAAAAAAAAHRIBAOARAQAAAAAAAAAAAAAAAAAqEgEA6BEBAAAAAAAAAAAAAAAAADcSAQDw -EQEAAAAAAAAAAAAAAAAAQRIBAPgRAQAAAAAAAAAAAAAAAABMEgEAABIBAAAAAAAAAAAAAAAAAFYS -AQAIEgEAAAAAAAAAAAAAAAAAAAAAAAAAAABgEgEAbhIBAH4SAQAAAAAAjBIBAAAAAACaEgEAAAAA -AKoSAQAAAAAAtBIBAAAAAAC6EgEAAAAAAMgSAQAAAAAAS0VSTkVMMzIuRExMAEFEVkFQSTMyLmRs -bABDT01DVEwzMi5kbGwAR0RJMzIuZGxsAE1TVkNSVC5kbGwAb2xlMzIuZGxsAFVTRVIzMi5kbGwA -AExvYWRMaWJyYXJ5QQAAR2V0UHJvY0FkZHJlc3MAAEV4aXRQcm9jZXNzAAAAUmVnQ2xvc2VLZXkA -AABQcm9wZXJ0eVNoZWV0QQAAVGV4dE91dEEAAGZyZWUAAENvSW5pdGlhbGl6ZQAAR2V0REMAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAA== -""" - -# --- EOF --- diff --git a/command/wininst.exe b/command/wininst.exe new file mode 100755 index 00000000..6290a936 Binary files /dev/null and b/command/wininst.exe differ -- cgit v1.2.1 From 7e77495f1012854570db4870dcd839ce9ece7854 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 26 Nov 2002 17:42:48 +0000 Subject: Part of the fix for bug #410541: add ensure_relative() function --- dir_util.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/dir_util.py b/dir_util.py index ca9fa9dc..bd1ea0f2 100644 --- a/dir_util.py +++ b/dir_util.py @@ -6,7 +6,7 @@ Utility functions for manipulating directories and directory trees.""" __revision__ = "$Id$" -import os +import os, sys from types import * from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log @@ -212,3 +212,17 @@ def remove_tree (directory, verbose=0, dry_run=0): except (IOError, OSError), exc: log.warn(grok_environment_error( exc, "error removing %s: " % directory)) + + +def ensure_relative (path): + """Take the full path 'path', and make it a relative path so + it can be the second argument to os.path.join(). + """ + drive, path = os.path.splitdrive(path) + if sys.platform == 'mac': + return os.sep + path + else: + if path[0:1] == os.sep: + path = drive + path[1:] + return path + -- cgit v1.2.1 From 56507a08e5a566801e5506f80325a67198e86493 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 26 Nov 2002 17:45:19 +0000 Subject: Fix for bug #410541: bdist builds bogus .zips This adds a --relative option to the bdist_dumb command that defaults to false; if true, the .tar.gz or .zip will be assembled using relative paths. --- command/bdist_dumb.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index d1cce55b..8ee3a5c5 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -11,7 +11,7 @@ __revision__ = "$Id$" import os from distutils.core import Command from distutils.util import get_platform -from distutils.dir_util import create_tree, remove_tree +from distutils.dir_util import create_tree, remove_tree, ensure_relative from distutils.errors import * from distutils import log @@ -33,9 +33,12 @@ class bdist_dumb (Command): "directory to put final built distributions in"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), + ('relative', None, + "build the archive using relative paths" + "(default: false)"), ] - boolean_options = ['keep-temp', 'skip-build'] + boolean_options = ['keep-temp', 'skip-build', 'relative'] default_format = { 'posix': 'gztar', 'nt': 'zip', @@ -49,7 +52,8 @@ class bdist_dumb (Command): self.keep_temp = 0 self.dist_dir = None self.skip_build = 0 - + self.relative = 0 + # initialize_options() @@ -97,9 +101,24 @@ class bdist_dumb (Command): if os.name == "os2": archive_basename = archive_basename.replace(":", "-") - self.make_archive(os.path.join(self.dist_dir, archive_basename), - self.format, - root_dir=self.bdist_dir) + pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) + if not self.relative: + archive_root = self.bdist_dir + else: + if (self.distribution.has_ext_modules() and + (install.install_base != install.install_platbase)): + raise DistutilsPlatformError, \ + ("can't make a dumb built distribution where " + "base and platbase are different (%s, %s)" + % (repr(install.install_base), + repr(install.install_platbase))) + else: + archive_root = os.path.join(self.bdist_dir, + ensure_relative(install.install_base)) + + # Make the archive + self.make_archive(pseudoinstall_root, + self.format, root_dir=archive_root) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) -- cgit v1.2.1 From 6f8ae17986bf88dde5c0f158e4a49b1f9465736b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 26 Nov 2002 21:28:23 +0000 Subject: Use "is" to test type objects, not "==". --- command/install_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_data.py b/command/install_data.py index 5c1f18a9..2fa0da29 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -48,7 +48,7 @@ class install_data (Command): def run (self): self.mkpath(self.install_dir) for f in self.data_files: - if type(f) == StringType: + if type(f) is StringType: # it's a simple file, so copy it f = convert_path(f) if self.warn_dir: -- cgit v1.2.1 From 6efa84626efe12980cd1ec6c649b9c41bff2d6a4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 27 Nov 2002 13:45:26 +0000 Subject: [Part of patch #641685] Add .dylib as an extension for shared libraries --- extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.py b/extension.py index e4209449..94b56e6c 100644 --- a/extension.py +++ b/extension.py @@ -214,7 +214,7 @@ def read_setup_file (filename): ext.extra_link_args.append(word) if not value: append_next_word = ext.extra_link_args - elif suffix in (".a", ".so", ".sl", ".o"): + elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): # NB. a really faithful emulation of makesetup would # append a .o file to extra_objects only if it # had a slash in it; otherwise, it would s/.o/.c/ -- cgit v1.2.1 From 54d6451d9991f43b2f593fee76fe238632027264 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 29 Nov 2002 19:45:58 +0000 Subject: Fix mode on scripts to have the read bit set (noted by Nicholas Riley) --- command/install_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install_scripts.py b/command/install_scripts.py index 6572e650..abe10457 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -53,7 +53,7 @@ class install_scripts (Command): if self.dry_run: log.info("changing mode of %s", file) else: - mode = ((os.stat(file)[ST_MODE]) | 0111) & 07777 + mode = ((os.stat(file)[ST_MODE]) | 0555) & 07777 log.info("changing mode of %s to %o", file, mode) os.chmod(file, mode) -- cgit v1.2.1 From f786b434f3184c64840218756664aae4152f5b6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Tue, 3 Dec 2002 08:45:11 +0000 Subject: Adding Python <= 2.2 support back in. --- util.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/util.py b/util.py index 9de6077f..17fc320a 100644 --- a/util.py +++ b/util.py @@ -359,11 +359,18 @@ def byte_compile (py_files, # "Indirect" byte-compilation: write a temporary script and then # run it with the appropriate flags. if not direct: - from tempfile import mkstemp - (script_fd, script_name) = mkstemp(".py") + try: + from tempfile import mkstemp + (script_fd, script_name) = mkstemp(".py") + except ImportError: + from tempfile import mktemp + (script_fd, script_name) = None, mktemp(".py") log.info("writing byte-compilation script '%s'", script_name) if not dry_run: - script = os.fdopen(script_fd, "w") + if script_fd is not None: + script = os.fdopen(script_fd, "w") + else: + script = open(script_name, "w") script.write("""\ from distutils.util import byte_compile -- cgit v1.2.1 From fb9fc102e11fdf1851470aa6b5dd11b0e2737b6f Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sun, 29 Dec 2002 17:00:57 +0000 Subject: Bug #599248: strip directories when building Python. Out-of-tree builds should work again. --- ccompiler.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index edb9f754..bfcf1279 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,6 +15,7 @@ from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath from distutils.dep_util import newer_pairwise, newer_group +from distutils.sysconfig import python_build from distutils.util import split_quoted, execute from distutils import log @@ -366,7 +367,9 @@ class CCompiler: extra = [] # Get the list of expected output (object) files - objects = self.object_filenames(sources, 0, outdir) + objects = self.object_filenames(sources, + strip_dir=python_build, + output_dir=outdir) assert len(objects) == len(sources) # XXX should redo this code to eliminate skip_source entirely. @@ -472,7 +475,7 @@ class CCompiler: which source files can be skipped. """ # Get the list of expected output (object) files - objects = self.object_filenames(sources, strip_dir=0, + objects = self.object_filenames(sources, strip_dir=python_build, output_dir=output_dir) assert len(objects) == len(sources) -- cgit v1.2.1 From 0f17b9ba75fc0d3d166a058387ca8a7cd2d97a63 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 3 Jan 2003 15:24:36 +0000 Subject: [Patch #658094] PEP 301 implementation Add 'classifiers' keyword to DistributionMetadata --- dist.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index faeb7b10..f15c945c 100644 --- a/dist.py +++ b/dist.py @@ -93,6 +93,8 @@ class Distribution: "print the long package description"), ('platforms', None, "print the list of platforms"), + ('classifiers', None, + "print the list of classifiers"), ('keywords', None, "print the list of keywords"), ] @@ -634,6 +636,8 @@ class Distribution: value = getattr(self.metadata, "get_"+opt)() if opt in ['keywords', 'platforms']: print string.join(value, ',') + elif opt == 'classifiers': + print string.join(value, '\n') else: print value any_display_options = 1 @@ -962,7 +966,7 @@ class DistributionMetadata: "maintainer", "maintainer_email", "url", "license", "description", "long_description", "keywords", "platforms", "fullname", "contact", - "contact_email", "licence") + "contact_email", "licence", "classifiers") def __init__ (self): self.name = None @@ -977,6 +981,7 @@ class DistributionMetadata: self.long_description = None self.keywords = None self.platforms = None + self.classifiers = None def write_pkg_info (self, base_dir): """Write the PKG-INFO file into the release tree. @@ -1003,6 +1008,9 @@ class DistributionMetadata: for platform in self.get_platforms(): pkg_info.write('Platform: %s\n' % platform ) + for classifier in self.get_classifiers(): + pkg_info.write('Classifier: %s\n' % classifier ) + pkg_info.close() # write_pkg_info () @@ -1059,6 +1067,9 @@ class DistributionMetadata: def get_platforms(self): return self.platforms or ["UNKNOWN"] + def get_classifiers(self): + return self.classifiers or [] + # class DistributionMetadata -- cgit v1.2.1 From 68218cefc84e5b95d3cbf4cadccb1742b83623d5 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 3 Jan 2003 15:29:28 +0000 Subject: [Patch #658094 ] PEP 301 implementation Add the 'register' distutils command --- command/register.py | 293 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 command/register.py diff --git a/command/register.py b/command/register.py new file mode 100644 index 00000000..6920d1d1 --- /dev/null +++ b/command/register.py @@ -0,0 +1,293 @@ +"""distutils.command.register + +Implements the Distutils 'register' command (register with the repository). +""" + +# created 2002/10/21, Richard Jones + +__revision__ = "$Id$" + +import sys, os, string, urllib2, getpass, urlparse +import StringIO, ConfigParser + +from distutils.core import Command +from distutils.errors import * + +class register(Command): + + description = "register the distribution with the repository" + + # XXX must update this to python.org before 2.3final! + DEFAULT_REPOSITORY = 'http://www.amk.ca/cgi-bin/pypi.cgi' + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]"%DEFAULT_REPOSITORY), + ('verify', None, + 'verify the package metadata for correctness'), + ('list-classifiers', None, + 'list the valid Trove classifiers'), + ('verbose', None, + 'display full response from server'), + ] + boolean_options = ['verify', 'verbose', 'list-classifiers'] + + def initialize_options(self): + self.repository = None + self.verify = 0 + self.verbose = 0 + self.list_classifiers = 0 + + def finalize_options(self): + if self.repository is None: + self.repository = self.DEFAULT_REPOSITORY + + def run(self): + self.check_metadata() + if self.verify: + self.verify_metadata() + elif self.list_classifiers: + self.classifiers() + else: + self.send_metadata() + + def check_metadata(self): + """Ensure that all required elements of meta-data (name, version, + URL, (author and author_email) or (maintainer and + maintainer_email)) are supplied by the Distribution object; warn if + any are missing. + """ + metadata = self.distribution.metadata + + missing = [] + for attr in ('name', 'version', 'url'): + if not (hasattr(metadata, attr) and getattr(metadata, attr)): + missing.append(attr) + + if missing: + self.warn("missing required meta-data: " + + string.join(missing, ", ")) + + if metadata.author: + if not metadata.author_email: + self.warn("missing meta-data: if 'author' supplied, " + + "'author_email' must be supplied too") + elif metadata.maintainer: + if not metadata.maintainer_email: + self.warn("missing meta-data: if 'maintainer' supplied, " + + "'maintainer_email' must be supplied too") + else: + self.warn("missing meta-data: either (author and author_email) " + + "or (maintainer and maintainer_email) " + + "must be supplied") + + def classifiers(self): + ''' Fetch the list of classifiers from the server. + ''' + response = urllib2.urlopen(self.repository+'?:action=list_classifiers') + print response.read() + + def verify_metadata(self): + ''' Send the metadata to the package index server to be checked. + ''' + # send the info to the server and report the result + (code, result) = self.post_to_server(self.build_post_data('verify')) + print 'Server response (%s): %s'%(code, result) + + def send_metadata(self): + ''' Send the metadata to the package index server. + + Well, do the following: + 1. figure who the user is, and then + 2. send the data as a Basic auth'ed POST. + + First we try to read the username/password from $HOME/.pypirc, + which is a ConfigParser-formatted file with a section + [server-login] containing username and password entries (both + in clear text). Eg: + + [server-login] + username: fred + password: sekrit + + Otherwise, to figure who the user is, we offer the user three + choices: + + 1. use existing login, + 2. register as a new user, or + 3. set the password to a random string and email the user. + + ''' + choice = 'x' + username = password = '' + + # see if we can short-cut and get the username/password from the + # config + config = None + if os.environ.has_key('HOME'): + rc = os.path.join(os.environ['HOME'], '.pypirc') + if os.path.exists(rc): + print 'Using PyPI login from %s'%rc + config = ConfigParser.ConfigParser() + config.read(rc) + username = config.get('server-login', 'username') + password = config.get('server-login', 'password') + choice = '1' + + # get the user's login info + choices = '1 2 3 4'.split() + while choice not in choices: + print '''We need to know who you are, so please choose either: + 1. use your existing login, + 2. register as a new user, + 3. have the server generate a new password for you (and email it to you), or + 4. quit +Your selection [default 1]: ''', + choice = raw_input() + if not choice: + choice = '1' + elif choice not in choices: + print 'Please choose one of the four options!' + + if choice == '1': + # get the username and password + while not username: + username = raw_input('Username: ') + while not password: + password = getpass.getpass('Password: ') + + # set up the authentication + auth = urllib2.HTTPPasswordMgr() + host = urlparse.urlparse(self.repository)[1] + auth.add_password('pypi', host, username, password) + + # send the info to the server and report the result + code, result = self.post_to_server(self.build_post_data('submit'), + auth) + print 'Server response (%s): %s'%(code, result) + + # possibly save the login + if os.environ.has_key('HOME') and config is None and code == 200: + rc = os.path.join(os.environ['HOME'], '.pypirc') + print 'I can store your PyPI login so future submissions will be faster.' + print '(the login will be stored in %s)'%rc + choice = 'X' + while choice.lower() not in 'yn': + choice = raw_input('Save your login (y/N)?') + if not choice: + choice = 'n' + if choice.lower() == 'y': + f = open(rc, 'w') + f.write('[server-login]\nusername:%s\npassword:%s\n'%( + username, password)) + f.close() + try: + os.chmod(rc, 0600) + except: + pass + elif choice == '2': + data = {':action': 'user'} + data['name'] = data['password'] = data['email'] = '' + data['confirm'] = None + while not data['name']: + data['name'] = raw_input('Username: ') + while data['password'] != data['confirm']: + while not data['password']: + data['password'] = getpass.getpass('Password: ') + while not data['confirm']: + data['confirm'] = getpass.getpass(' Confirm: ') + if data['password'] != data['confirm']: + data['password'] = '' + data['confirm'] = None + print "Password and confirm don't match!" + while not data['email']: + data['email'] = raw_input(' EMail: ') + code, result = self.post_to_server(data) + if code != 200: + print 'Server response (%s): %s'%(code, result) + else: + print 'You will receive an email shortly.' + print 'Follow the instructions in it to complete registration.' + elif choice == '3': + data = {':action': 'password_reset'} + data['email'] = '' + while not data['email']: + data['email'] = raw_input('Your email address: ') + code, result = self.post_to_server(data) + print 'Server response (%s): %s'%(code, result) + + def build_post_data(self, action): + # figure the data to send - the metadata plus some additional + # information used by the package server + meta = self.distribution.metadata + data = { + ':action': action, + 'metadata_version' : '1.0', + 'name': meta.get_name(), + 'version': meta.get_version(), + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + } + if hasattr(meta, 'classifiers'): + data['classifiers'] = meta.get_classifiers() + return data + + def post_to_server(self, data, auth=None): + ''' Post a query to the server, and return a string response. + ''' + + # Build up the MIME payload for the urllib2 POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = '\n--' + boundary + end_boundary = sep_boundary + '--' + body = StringIO.StringIO() + for key, value in data.items(): + # handle multiple entries for the same name + if type(value) != type([]): + value = [value] + for value in value: + value = str(value) + body.write(sep_boundary) + body.write('\nContent-Disposition: form-data; name="%s"'%key) + body.write("\n\n") + body.write(value) + if value and value[-1] == '\r': + body.write('\n') # write an extra newline (lurve Macs) + body.write(end_boundary) + body.write("\n") + body = body.getvalue() + + # build the Request + headers = { + 'Content-type': 'multipart/form-data; boundary=%s'%boundary, + 'Content-length': str(len(body)) + } + req = urllib2.Request(self.repository, body, headers) + + # handle HTTP and include the Basic Auth handler + opener = urllib2.build_opener( + urllib2.HTTPBasicAuthHandler(password_mgr=auth) + ) + data = '' + try: + result = opener.open(req) + except urllib2.HTTPError, e: + if self.verbose: + data = e.fp.read() + result = e.code, e.msg + except urllib2.URLError, e: + result = 500, str(e) + else: + if self.verbose: + data = result.read() + result = 200, 'OK' + if self.verbose: + print '-'*75, data, '-'*75 + return result + -- cgit v1.2.1 From bfd055388d345fe1f58cd04f7272f6f7d3963f15 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 6 Jan 2003 13:28:12 +0000 Subject: Translate spaces in the machine name to underscores (Power Macintosh -> Power_Macintosh) --- util.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 17fc320a..dc3183b6 100644 --- a/util.py +++ b/util.py @@ -40,10 +40,11 @@ def get_platform (): (osname, host, release, version, machine) = os.uname() - # Convert the OS name to lowercase and remove '/' characters - # (to accommodate BSD/OS) + # Convert the OS name to lowercase, remove '/' characters + # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") osname = string.lower(osname) osname = string.replace(osname, '/', '') + machine = string.replace(machine, ' ', '_') if osname[:5] == "linux": # At least on Linux/Intel, 'machine' is the processor -- -- cgit v1.2.1 From fc7885b993648b7587683e0e14adecb21011556b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 24 Jan 2003 14:56:52 +0000 Subject: Change the mode of scripts in the build/scripts* directory to executable. --- command/build_scripts.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/command/build_scripts.py b/command/build_scripts.py index b7c11d47..c7bd4a9d 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -7,6 +7,7 @@ Implements the Distutils 'build_scripts' command.""" __revision__ = "$Id$" import sys, os, re +from stat import ST_MODE from distutils import sysconfig from distutils.core import Command from distutils.dep_util import newer @@ -54,10 +55,12 @@ class build_scripts (Command): line to refer to the current Python interpreter as we copy. """ self.mkpath(self.build_dir) + outfiles = [] for script in self.scripts: adjust = 0 script = convert_path(script) outfile = os.path.join(self.build_dir, os.path.basename(script)) + outfiles.append(outfile) if not self.force and not newer(script, outfile): log.debug("not copying %s (up-to-date)", script) @@ -106,6 +109,15 @@ class build_scripts (Command): f.close() self.copy_file(script, outfile) + if os.name == 'posix': + for file in outfiles: + if self.dry_run: + log.info("changing mode of %s", file) + else: + mode = ((os.stat(file)[ST_MODE]) | 0555) & 07777 + log.info("changing mode of %s to %o", file, mode) + os.chmod(file, mode) + # copy_scripts () # class build_scripts -- cgit v1.2.1 From caa7bc3bd88e1b1c50f4f03bda217b0fc6063f2b Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 27 Jan 2003 16:30:36 +0000 Subject: Remove the recently-added get_distutil_options(), and just have two tuples listing the legal keywords for setup() and Extension() --- core.py | 21 +++++++++++++-------- extension.py | 2 ++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/core.py b/core.py index 9ab419ef..29fbc5cd 100644 --- a/core.py +++ b/core.py @@ -42,6 +42,19 @@ def gen_usage (script_name): _setup_stop_after = None _setup_distribution = None +# Legal keyword arguments for the setup() function +setup_keywords = ('distclass', 'script_name', 'script_args', 'options', + 'name', 'version', 'author', 'author_email', + 'maintainer', 'maintainer_email', 'url', 'license', + 'description', 'long_description', 'keywords', + 'platforms', 'classifiers') + +# Legal keyword arguments for the Extension constructor +extension_keywords = ('name', 'sources', 'include_dirs', + 'define_macros', 'undef_macros', + 'library_dirs', 'libraries', 'runtime_library_dirs', + 'extra_objects', 'extra_compile_args', 'extra_link_args', + 'export_symbols', 'depends', 'language') def setup (**attrs): """The gateway to the Distutils: do everything your setup script needs @@ -226,11 +239,3 @@ def run_setup (script_name, script_args=None, stop_after="run"): # run_setup () -def get_distutil_options (): - """Returns a list of strings recording changes to the Distutils. - - setup.py files can then do: - if 'optional-thing' in get_distutil_options(): - ... - """ - return [] diff --git a/extension.py b/extension.py index 94b56e6c..e69f3e93 100644 --- a/extension.py +++ b/extension.py @@ -82,6 +82,8 @@ class Extension: from the source extensions if not provided. """ + # When adding arguments to this constructor, be sure to update + # setup_keywords in core.py. def __init__ (self, name, sources, include_dirs=None, define_macros=None, -- cgit v1.2.1 From 04937364e507dbe9f02d00d0cd79908dbfc9afb7 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 29 Jan 2003 16:58:31 +0000 Subject: Only log a message and chmod() when the mode isn't already what we want it to be. Log both the old and new mode. --- command/build_scripts.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index c7bd4a9d..f61ad37d 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -114,9 +114,12 @@ class build_scripts (Command): if self.dry_run: log.info("changing mode of %s", file) else: - mode = ((os.stat(file)[ST_MODE]) | 0555) & 07777 - log.info("changing mode of %s to %o", file, mode) - os.chmod(file, mode) + oldmode = os.stat(file)[ST_MODE] & 07777 + newmode = (oldmode | 0555) & 07777 + if newmode != oldmode: + log.info("changing mode of %s from %o to %o", + file, oldmode, newmode) + os.chmod(file, newmode) # copy_scripts () -- cgit v1.2.1 From c7d6889f2f4e4f3a0562798e3641d67c09951f35 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 31 Jan 2003 20:40:15 +0000 Subject: Pass the preprocessor options also to the resource compiler when compiling .RC files. From Robin Dunn, fixes SF # 669198. --- msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index 65a50cc7..e07a6d5a 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -310,7 +310,7 @@ class MSVCCompiler (CCompiler) : input_opt = src output_opt = "/fo" + obj try: - self.spawn ([self.rc] + + self.spawn ([self.rc] + pp_opts + [output_opt] + [input_opt]) except DistutilsExecError, msg: raise CompileError, msg -- cgit v1.2.1 From 90ceb5eaaf84551cf39ba48550debf99f9081e7b Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 3 Feb 2003 11:43:54 +0000 Subject: patch #664131, fix config command on OSX and Linux --- command/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/config.py b/command/config.py index b6f5ad1d..3bd537a6 100644 --- a/command/config.py +++ b/command/config.py @@ -151,7 +151,8 @@ class config (Command): library_dirs=library_dirs, target_lang=lang) - prog = prog + self.compiler.exe_extension + if self.compiler.exe_extension is not None: + prog = prog + self.compiler.exe_extension self.temp_files.append(prog) return (src, obj, prog) -- cgit v1.2.1 From 3ea65a4bba9d3ce1086bc983c16c5f0feea8055b Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Mon, 10 Feb 2003 14:02:33 +0000 Subject: Pick up Makefile variable BASECFLAGS too. This is needed since OPT was split into OPT and BASECFLAGS (Makefile.pre.in rev. 1.108), because now there are essential CFLAGS in BASECFLAGS. --- sysconfig.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index aa3636f6..67353a8d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -146,8 +146,8 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - (cc, cxx, opt, ccshared, ldshared, so_ext) = \ - get_config_vars('CC', 'CXX', 'OPT', 'CCSHARED', 'LDSHARED', 'SO') + (cc, cxx, opt, basecflags, ccshared, ldshared, so_ext) = \ + get_config_vars('CC', 'CXX', 'OPT', 'BASECFLAGS', 'CCSHARED', 'LDSHARED', 'SO') if os.environ.has_key('CC'): cc = os.environ['CC'] @@ -159,6 +159,8 @@ def customize_compiler(compiler): cpp = cc + " -E" # not always if os.environ.has_key('LDFLAGS'): ldshared = ldshared + ' ' + os.environ['LDFLAGS'] + if basecflags: + opt = basecflags + ' ' + opt if os.environ.has_key('CFLAGS'): opt = opt + ' ' + os.environ['CFLAGS'] ldshared = ldshared + ' ' + os.environ['CFLAGS'] -- cgit v1.2.1 From a65727b5db3ecf4546e808fcc72eb14fbea9cf42 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 18 Feb 2003 01:28:51 +0000 Subject: [Patch #681504] Call customize_compiler in config command --- command/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/config.py b/command/config.py index 3bd537a6..f18c79ff 100644 --- a/command/config.py +++ b/command/config.py @@ -17,6 +17,7 @@ import sys, os, string, re from types import * from distutils.core import Command from distutils.errors import DistutilsExecError +from distutils.sysconfig import customize_compiler from distutils import log LANG_EXT = {'c': '.c', @@ -104,6 +105,7 @@ class config (Command): if not isinstance(self.compiler, CCompiler): self.compiler = new_compiler(compiler=self.compiler, dry_run=self.dry_run, force=1) + customize_compiler(self.compiler) if self.include_dirs: self.compiler.set_include_dirs(self.include_dirs) if self.libraries: -- cgit v1.2.1 From dae4f7a4a339196ac3fa85ede474b603659e3169 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 18 Feb 2003 21:28:20 +0000 Subject: Use python.org as the repository --- command/register.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/register.py b/command/register.py index 6920d1d1..aac700c4 100644 --- a/command/register.py +++ b/command/register.py @@ -17,8 +17,7 @@ class register(Command): description = "register the distribution with the repository" - # XXX must update this to python.org before 2.3final! - DEFAULT_REPOSITORY = 'http://www.amk.ca/cgi-bin/pypi.cgi' + DEFAULT_REPOSITORY = 'http://www.python.org/pypi' user_options = [ ('repository=', 'r', -- cgit v1.2.1 From 7fa7f24d329793d1d40b4fbdb0d56cb284e4c907 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 19 Feb 2003 13:49:35 +0000 Subject: [Patch #684398] Rename verbose argument to show-response; don't conditionalize the get_classifiers() call --- command/register.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/command/register.py b/command/register.py index aac700c4..328d8d0a 100644 --- a/command/register.py +++ b/command/register.py @@ -26,15 +26,15 @@ class register(Command): 'verify the package metadata for correctness'), ('list-classifiers', None, 'list the valid Trove classifiers'), - ('verbose', None, - 'display full response from server'), + ('show-response', None, + 'display full response text from server'), ] - boolean_options = ['verify', 'verbose', 'list-classifiers'] + boolean_options = ['verify', 'show-response', 'list-classifiers'] def initialize_options(self): self.repository = None self.verify = 0 - self.verbose = 0 + self.show_response = 0 self.list_classifiers = 0 def finalize_options(self): @@ -232,9 +232,8 @@ Your selection [default 1]: ''', 'description': meta.get_long_description(), 'keywords': meta.get_keywords(), 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), } - if hasattr(meta, 'classifiers'): - data['classifiers'] = meta.get_classifiers() return data def post_to_server(self, data, auth=None): @@ -277,16 +276,16 @@ Your selection [default 1]: ''', try: result = opener.open(req) except urllib2.HTTPError, e: - if self.verbose: + if self.show_response: data = e.fp.read() result = e.code, e.msg except urllib2.URLError, e: result = 500, str(e) else: - if self.verbose: + if self.show_response: data = result.read() result = 200, 'OK' - if self.verbose: + if self.show_response: print '-'*75, data, '-'*75 return result -- cgit v1.2.1 From fedab9d677951d29d7e11684ab6978e31cea0a68 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 19 Feb 2003 14:16:01 +0000 Subject: [Patch #683939] Add download_url field to metadata --- core.py | 2 +- dist.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index 29fbc5cd..a463272c 100644 --- a/core.py +++ b/core.py @@ -47,7 +47,7 @@ setup_keywords = ('distclass', 'script_name', 'script_args', 'options', 'name', 'version', 'author', 'author_email', 'maintainer', 'maintainer_email', 'url', 'license', 'description', 'long_description', 'keywords', - 'platforms', 'classifiers') + 'platforms', 'classifiers', 'download_url') # Legal keyword arguments for the Extension constructor extension_keywords = ('name', 'sources', 'include_dirs', diff --git a/dist.py b/dist.py index f15c945c..08e2a4f7 100644 --- a/dist.py +++ b/dist.py @@ -966,7 +966,8 @@ class DistributionMetadata: "maintainer", "maintainer_email", "url", "license", "description", "long_description", "keywords", "platforms", "fullname", "contact", - "contact_email", "licence", "classifiers") + "contact_email", "licence", "classifiers", + "download_url") def __init__ (self): self.name = None @@ -982,6 +983,7 @@ class DistributionMetadata: self.keywords = None self.platforms = None self.classifiers = None + self.download_url = None def write_pkg_info (self, base_dir): """Write the PKG-INFO file into the release tree. @@ -997,6 +999,8 @@ class DistributionMetadata: pkg_info.write('Author: %s\n' % self.get_contact() ) pkg_info.write('Author-email: %s\n' % self.get_contact_email() ) pkg_info.write('License: %s\n' % self.get_license() ) + if self.download_url: + pkg_info.write('Download-URL: %s\n' % self.download_url) long_desc = rfc822_escape( self.get_long_description() ) pkg_info.write('Description: %s\n' % long_desc) @@ -1070,6 +1074,9 @@ class DistributionMetadata: def get_classifiers(self): return self.classifiers or [] + def get_download_url(self): + return self.download_url or "UNKNOWN" + # class DistributionMetadata -- cgit v1.2.1 From 3001d212f66a2cb547d0d3306680e7ab09c87f6e Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 19 Feb 2003 14:27:21 +0000 Subject: Include download_url in the data POSTed to the catalog server --- command/register.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/register.py b/command/register.py index 328d8d0a..29b76cbf 100644 --- a/command/register.py +++ b/command/register.py @@ -233,6 +233,7 @@ Your selection [default 1]: ''', 'keywords': meta.get_keywords(), 'platform': meta.get_platforms(), 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), } return data -- cgit v1.2.1 From 39a95207b183654cd92b4048ed910491ffe1a48a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 20 Feb 2003 02:09:30 +0000 Subject: set_verbosity(): do something reasonable for out-of-range verbosity levels. (Previously, -vvv would be the same as -q!) --- log.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/log.py b/log.py index 0442033d..01420da9 100644 --- a/log.py +++ b/log.py @@ -53,9 +53,9 @@ def set_threshold(level): _global_log.threshold = level def set_verbosity(v): - if v == 0: + if v <= 0: set_threshold(WARN) - if v == 1: + elif v == 1: set_threshold(INFO) - if v == 2: + elif v >= 2: set_threshold(DEBUG) -- cgit v1.2.1 From 72cbcb8b55ce0bd038577685f074418aa61fdea6 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 20 Feb 2003 02:10:08 +0000 Subject: announce(): use the level argument to control the log level. --- cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd.py b/cmd.py index 1165f951..7e7a4cd5 100644 --- a/cmd.py +++ b/cmd.py @@ -191,7 +191,7 @@ class Command: """If the current verbosity level is of greater than or equal to 'level' print 'msg' to stdout. """ - log.debug(msg) + log.log(level, msg) def debug_print (self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the -- cgit v1.2.1 From 5daaf7cd967da08a031cb40e3b668a173b4673a1 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 26 Feb 2003 18:52:07 +0000 Subject: [Bug #668662] Patch from Pearu Pearson: if a C source file is specified with an absolute path, the object file is also written to an absolute path. The patch drops the drive and leading '/' from the source path, so a path like /path/to/foo.c results in an object file like build/temp.i686linux/path/to/foo.o. --- ccompiler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ccompiler.py b/ccompiler.py index bfcf1279..46fb743d 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -932,6 +932,8 @@ class CCompiler: obj_names = [] for src_name in source_filenames: base, ext = os.path.splitext(src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / if ext not in self.src_extensions: raise UnknownFileError, \ "unknown file type '%s' (from '%s')" % (ext, src_name) -- cgit v1.2.1 From cbc89d16f32d66f6b51a0a94cd6a0a00824bdbff Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 28 Feb 2003 22:03:04 +0000 Subject: [Patch #695090 from Bernhard Herzog] Allow specifying both py_modules and packages --- command/build_py.py | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 258d6d4c..6c007c69 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -86,25 +86,11 @@ class build_py (Command): # Two options control which modules will be installed: 'packages' # and 'py_modules'. The former lets us work with whole packages, not # specifying individual modules at all; the latter is for - # specifying modules one-at-a-time. Currently they are mutually - # exclusive: you can define one or the other (or neither), but not - # both. It remains to be seen how limiting this is. - - # Dispose of the two "unusual" cases first: no pure Python modules - # at all (no problem, just return silently), and over-specified - # 'packages' and 'py_modules' options. - - if not self.py_modules and not self.packages: - return - if self.py_modules and self.packages: - raise DistutilsOptionError, \ - "build_py: supplying both 'packages' and 'py_modules' " + \ - "options is not allowed" - - # Now we're down to two cases: 'py_modules' only and 'packages' only. + # specifying modules one-at-a-time. + if self.py_modules: self.build_modules() - else: + if self.packages: self.build_packages() self.byte_compile(self.get_outputs(include_bytecode=0)) @@ -276,10 +262,10 @@ class build_py (Command): (package, module, module_file), just like 'find_modules()' and 'find_package_modules()' do.""" + modules = [] if self.py_modules: - modules = self.find_modules() - else: - modules = [] + modules.extend(self.find_modules()) + if self.packages: for package in self.packages: package_dir = self.get_package_dir(package) m = self.find_package_modules(package, package_dir) -- cgit v1.2.1 From 06f82fdc6015683f349612125cd2e44fea44092b Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 3 Mar 2003 18:26:01 +0000 Subject: Improve description --- command/register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/register.py b/command/register.py index 29b76cbf..e4a37993 100644 --- a/command/register.py +++ b/command/register.py @@ -15,7 +15,7 @@ from distutils.errors import * class register(Command): - description = "register the distribution with the repository" + description = ("register the distribution with the Python package index") DEFAULT_REPOSITORY = 'http://www.python.org/pypi' -- cgit v1.2.1 From 9c00844cfbe8e4f33255f6174c54fe45c266fce8 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 3 Mar 2003 18:37:16 +0000 Subject: [Bug #69389] List register command in __all__, so setup.py --help-commands will now list it --- command/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/__init__.py b/command/__init__.py index fc611716..870005dc 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -19,6 +19,7 @@ __all__ = ['build', 'install_scripts', 'install_data', 'sdist', + 'register', 'bdist', 'bdist_dumb', 'bdist_rpm', -- cgit v1.2.1 From 9766b20bc6d839174db2fb53881c140f110583dd Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 3 Mar 2003 20:07:27 +0000 Subject: [Bug #693470] 'licence' as an alias for 'license' doesn't work. This patch makes it work again. --- dist.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 08e2a4f7..d313e7d1 100644 --- a/dist.py +++ b/dist.py @@ -205,6 +205,15 @@ class Distribution: for (opt, val) in cmd_options.items(): opt_dict[opt] = ("setup script", val) + if attrs.has_key('licence'): + attrs['license'] = attrs['licence'] + del attrs['licence'] + msg = "'licence' distribution option is deprecated; use 'license'" + if warnings is not None: + warnings.warn(msg) + else: + sys.stderr.write(msg + "\n") + # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! for (key,val) in attrs.items(): @@ -966,7 +975,7 @@ class DistributionMetadata: "maintainer", "maintainer_email", "url", "license", "description", "long_description", "keywords", "platforms", "fullname", "contact", - "contact_email", "licence", "classifiers", + "contact_email", "license", "classifiers", "download_url") def __init__ (self): -- cgit v1.2.1 From ad325698f9280631620c2d7aad6619b67ab8d899 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 9 Apr 2003 12:35:51 +0000 Subject: Remove the --verify option in favor of the standard -n/--dry-run option --- command/register.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/command/register.py b/command/register.py index e4a37993..8e347ce6 100644 --- a/command/register.py +++ b/command/register.py @@ -22,8 +22,6 @@ class register(Command): user_options = [ ('repository=', 'r', "url of repository [default: %s]"%DEFAULT_REPOSITORY), - ('verify', None, - 'verify the package metadata for correctness'), ('list-classifiers', None, 'list the valid Trove classifiers'), ('show-response', None, @@ -33,7 +31,6 @@ class register(Command): def initialize_options(self): self.repository = None - self.verify = 0 self.show_response = 0 self.list_classifiers = 0 @@ -43,7 +40,7 @@ class register(Command): def run(self): self.check_metadata() - if self.verify: + if self.dry_run: self.verify_metadata() elif self.list_classifiers: self.classifiers() -- cgit v1.2.1 From 1942fa31ea1b33d7fcccfed31d6cd2ebd46c7f60 Mon Sep 17 00:00:00 2001 From: Jason Tishler Date: Wed, 9 Apr 2003 16:03:57 +0000 Subject: Patch #709178: remove -static option from cygwinccompiler Currently, the cygwinccompiler.py compiler handling in distutils is invoking the cygwin and mingw compilers with the -static option. Logically, this means that the linker should choose to link to static libraries instead of shared/dynamically linked libraries. Current win32 binutils expect import libraries to have a .dll.a suffix and static libraries to have .a suffix. If -static is passed, it will skip the .dll.a libraries. This is pain if one has a tree with both static and dynamic libraries using this naming convention, and wish to use the dynamic libraries. The -static option being passed in distutils is to get around a bug in old versions of binutils where it would get confused when it found the DLLs themselves. The decision to use static or shared libraries is site or package specific, and should be left to the setup script or to command line options. --- cygwinccompiler.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 18af388c..e86dc815 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -33,12 +33,6 @@ cygwin in no-cygwin mode). # * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now # - its dllwrap doesn't work, there is a bug in binutils 2.10.90 # see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html -# - using gcc -mdll instead dllwrap doesn't work without -static because -# it tries to link against dlls instead their import libraries. (If -# it finds the dll first.) -# By specifying -static we force ld to link against the import libraries, -# this is windows standard and there are normally not the necessary symbols -# in the dlls. # *** only the version of June 2000 shows these problems # This module should be kept compatible with Python 1.5.2. @@ -98,7 +92,7 @@ class CygwinCCompiler (UnixCCompiler): self.set_executables(compiler='gcc -mcygwin -O -Wall', compiler_so='gcc -mcygwin -mdll -O -Wall', linker_exe='gcc -mcygwin', - linker_so=('%s -mcygwin -mdll -static' % + linker_so=('%s -mcygwin -mdll' % self.linker_dll)) # cygwin and mingw32 need different sets of libraries @@ -278,7 +272,7 @@ class Mingw32CCompiler (CygwinCCompiler): self.set_executables(compiler='gcc -mno-cygwin -O -Wall', compiler_so='gcc -mno-cygwin -mdll -O -Wall', linker_exe='gcc -mno-cygwin', - linker_so='%s -mno-cygwin -mdll -static %s' + linker_so='%s -mno-cygwin -mdll %s' % (self.linker_dll, entry_point)) # Maybe we should also append -mthreads, but then the finished # dlls need another dll (mingwm10.dll see Mingw32 docs) -- cgit v1.2.1 From 972516d3ad5fba97791d87ba95d126f3d6bcc5a2 Mon Sep 17 00:00:00 2001 From: Jason Tishler Date: Wed, 9 Apr 2003 20:13:59 +0000 Subject: Patch #718551: cygwinccompiler.get_versions() patch The cygwinccompiler.get_versions() function only handles versions numbers of the form "x.y.z". The attached patch enhances get_versions() to handle "x.y" too (i.e., the ".z" is optional). This change causes the unnecessary "--entry _DllMain@12" link option to be suppressed for recent Cygwin and Mingw toolchains. Additionally, it directs recent Mingw toolchains to use gcc instead of dllwrap during linking. --- cygwinccompiler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index e86dc815..794dcdb5 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -357,7 +357,7 @@ def get_versions(): out = os.popen(gcc_exe + ' -dumpversion','r') out_string = out.read() out.close() - result = re.search('(\d+\.\d+\.\d+)',out_string) + result = re.search('(\d+\.\d+(\.\d+)*)',out_string) if result: gcc_version = StrictVersion(result.group(1)) else: @@ -369,7 +369,7 @@ def get_versions(): out = os.popen(ld_exe + ' -v','r') out_string = out.read() out.close() - result = re.search('(\d+\.\d+\.\d+)',out_string) + result = re.search('(\d+\.\d+(\.\d+)*)',out_string) if result: ld_version = StrictVersion(result.group(1)) else: @@ -381,7 +381,7 @@ def get_versions(): out = os.popen(dllwrap_exe + ' --version','r') out_string = out.read() out.close() - result = re.search(' (\d+\.\d+\.\d+)',out_string) + result = re.search(' (\d+\.\d+(\.\d+)*)',out_string) if result: dllwrap_version = StrictVersion(result.group(1)) else: -- cgit v1.2.1 From ac175fdb7176adf51768ab5237c81f7ab421c50d Mon Sep 17 00:00:00 2001 From: Jason Tishler Date: Mon, 14 Apr 2003 12:51:26 +0000 Subject: Patch #709178: remove -static option from cygwinccompiler After some more reflection (and no negative feedback), I am reverting the original patch and applying my version, cygwinccompiler.py-shared.diff, instead. My reasons are the following: 1. support for older toolchains is retained 2. support for new toolchains (i.e., ld -shared) is added The goal of my approach is to avoid breaking older toolchains while adding better support for newer ones. --- cygwinccompiler.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 794dcdb5..94b8b86b 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -33,7 +33,17 @@ cygwin in no-cygwin mode). # * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now # - its dllwrap doesn't work, there is a bug in binutils 2.10.90 # see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html +# - using gcc -mdll instead dllwrap doesn't work without -static because +# it tries to link against dlls instead their import libraries. (If +# it finds the dll first.) +# By specifying -static we force ld to link against the import libraries, +# this is windows standard and there are normally not the necessary symbols +# in the dlls. # *** only the version of June 2000 shows these problems +# * cygwin gcc 3.2/ld 2.13.90 works +# (ld supports -shared) +# * mingw gcc 3.2/ld 2.13 works +# (ld supports -shared) # This module should be kept compatible with Python 1.5.2. @@ -77,7 +87,7 @@ class CygwinCCompiler (UnixCCompiler): self.ld_version, self.dllwrap_version) ) - # ld_version >= "2.10.90" should also be able to use + # ld_version >= "2.10.90" and < "2.13" should also be able to use # gcc -mdll instead of dllwrap # Older dllwraps had own version numbers, newer ones use the # same as the rest of binutils ( also ld ) @@ -87,13 +97,20 @@ class CygwinCCompiler (UnixCCompiler): else: self.linker_dll = "dllwrap" + # ld_version >= "2.13" support -shared so use it instead of + # -mdll -static + if self.ld_version >= "2.13": + shared_option = "-shared" + else: + shared_option = "-mdll -static" + # Hard-code GCC because that's what this is all about. # XXX optimization, warnings etc. should be customizable. self.set_executables(compiler='gcc -mcygwin -O -Wall', compiler_so='gcc -mcygwin -mdll -O -Wall', linker_exe='gcc -mcygwin', - linker_so=('%s -mcygwin -mdll' % - self.linker_dll)) + linker_so=('%s -mcygwin %s' % + (self.linker_dll, shared_option))) # cygwin and mingw32 need different sets of libraries if self.gcc_version == "2.91.57": @@ -262,6 +279,13 @@ class Mingw32CCompiler (CygwinCCompiler): CygwinCCompiler.__init__ (self, verbose, dry_run, force) + # ld_version >= "2.13" support -shared so use it instead of + # -mdll -static + if self.ld_version >= "2.13": + shared_option = "-shared" + else: + shared_option = "-mdll -static" + # A real mingw32 doesn't need to specify a different entry point, # but cygwin 2.91.57 in no-cygwin-mode needs it. if self.gcc_version <= "2.91.57": @@ -272,8 +296,9 @@ class Mingw32CCompiler (CygwinCCompiler): self.set_executables(compiler='gcc -mno-cygwin -O -Wall', compiler_so='gcc -mno-cygwin -mdll -O -Wall', linker_exe='gcc -mno-cygwin', - linker_so='%s -mno-cygwin -mdll %s' - % (self.linker_dll, entry_point)) + linker_so='%s -mno-cygwin %s %s' + % (self.linker_dll, shared_option, + entry_point)) # Maybe we should also append -mthreads, but then the finished # dlls need another dll (mingwm10.dll see Mingw32 docs) # (-mthreads: Support thread-safe exception handling on `Mingw32') -- cgit v1.2.1 From 8e2d2f9fefd61c49a54298e45d4c10be078a5696 Mon Sep 17 00:00:00 2001 From: Jason Tishler Date: Fri, 18 Apr 2003 17:27:47 +0000 Subject: Patch #718049: Setting exe_extension for cygwin On cygwin, the setup.py script uses unixccompiler.py for compiling and linking C extensions. The unixccompiler.py script assumes that executables do not get special extensions, which makes sense for Unix. However, on Cygwin, executables get an .exe extension. This causes a problem during the configuration step (python setup.py config), in which some temporary executables may be generated. As unixccompiler.py does not know about the .exe extension, distutils fails to clean up after itself: it does not remove _configtest.exe but tries to remove _configtest instead. The attached patch to unixccompiler.py sets the correct exe_extension for cygwin by checking if sys.platform is 'cygwin'. With this patch, distutils cleans up after itself correctly. Michiel de Hoon University of Tokyo, Human Genome Center. --- unixccompiler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unixccompiler.py b/unixccompiler.py index 2a6b1bee..e444917f 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -77,6 +77,8 @@ class UnixCCompiler(CCompiler): shared_lib_extension = ".so" dylib_lib_extension = ".dylib" static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" + if sys.platform == "cygwin": + exe_extension = ".exe" def preprocess(self, source, output_file=None, macros=None, include_dirs=None, -- cgit v1.2.1 From d19626f7466f8166af24c6cd5e3aca722395b835 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Thu, 24 Apr 2003 19:49:23 +0000 Subject: new method: has_function() - returns a boolean indicating whether the argument function is available on the current platform --- ccompiler.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/ccompiler.py b/ccompiler.py index 46fb743d..751ec069 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -883,6 +883,51 @@ class CCompiler: """ raise NotImplementedError + def has_function(self, funcname, + includes=None, + include_dirs=None, + libraries=None, + library_dirs=None): + """Return a boolean indicating whether funcname is supported on + the current platform. The optional arguments can be used to + augment the compilation environment. + """ + + # this can't be included at module scope because it tries to + # import math which might not be available at that point - maybe + # the necessary logic should just be inlined? + import tempfile + if includes is None: + includes = [] + if include_dirs is None: + include_dirs = [] + if libraries is None: + libraries = [] + if library_dirs is None: + library_dirs = [] + fd, fname = tempfile.mkstemp(".c", funcname, text=True) + f = os.fdopen(fd, "w") + for incl in includes: + f.write("""#include "%s"\n""" % incl) + f.write("""\ +main (int argc, char **argv) { + %s(); +} +""" % funcname) + f.close() + try: + objects = self.compile([fname], include_dirs=include_dirs) + except CompileError: + return False + + try: + self.link_executable(objects, "a.out", + libraries=libraries, + library_dirs=library_dirs) + except (LinkError, TypeError): + return False + return True + def find_library_file (self, dirs, lib, debug=0): """Search the specified list of directories for a static or shared library file 'lib' and return the full path to that file. If -- cgit v1.2.1 From 70a92cdaba12a893f3c1d19f8c714029e6054081 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Fri, 9 May 2003 16:06:42 +0000 Subject: Variant of SF patch 614770: MSVC 7 support distutils now looks for the compiler version in sys.version, falling back to MSVC 6 if the version isn't listed (Python 2.2 and lower). Add helper routines for reading the registry. Refactor many module functions into methods of the compiler to avoid passing lots of state as arguments. --- msvccompiler.py | 332 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 189 insertions(+), 143 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index e07a6d5a..b4c890d0 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -1,7 +1,8 @@ """distutils.msvccompiler Contains MSVCCompiler, an implementation of the abstract CCompiler class -for the Microsoft Visual Studio.""" +for the Microsoft Visual Studio. +""" # Written by Perry Stoll # hacked by Robin Becker and Thomas Heller to do a better job of @@ -12,7 +13,6 @@ for the Microsoft Visual Studio.""" __revision__ = "$Id$" import sys, os, string -from types import * from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -48,126 +48,116 @@ except ImportError: pass if _can_read_reg: - HKEY_CLASSES_ROOT = hkey_mod.HKEY_CLASSES_ROOT - HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE - HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER - HKEY_USERS = hkey_mod.HKEY_USERS + HKEYS = (hkey_mod.HKEY_USERS, + hkey_mod.HKEY_CURRENT_USER, + hkey_mod.HKEY_LOCAL_MACHINE, + hkey_mod.HKEY_CLASSES_ROOT) +def read_keys(base, key): + """Return list of registry keys.""" - -def get_devstudio_versions (): - """Get list of devstudio versions from the Windows registry. Return a - list of strings containing version numbers; the list will be - empty if we were unable to access the registry (eg. couldn't import - a registry-access module) or the appropriate registry keys weren't - found.""" - - if not _can_read_reg: - return [] - - K = 'Software\\Microsoft\\Devstudio' + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None L = [] - for base in (HKEY_CLASSES_ROOT, - HKEY_LOCAL_MACHINE, - HKEY_CURRENT_USER, - HKEY_USERS): + i = 0 + while 1: try: - k = RegOpenKeyEx(base,K) - i = 0 - while 1: - try: - p = RegEnumKey(k,i) - if p[0] in '123456789' and p not in L: - L.append(p) - except RegError: - break - i = i + 1 + k = RegEnumKey(handle, i) except RegError: - pass - L.sort() - L.reverse() + break + L.append(k) + i = i + 1 return L -# get_devstudio_versions () - - -def get_msvc_paths (path, version='6.0', platform='x86'): - """Get a list of devstudio directories (include, lib or path). Return - a list of strings; will be empty list if unable to access the - registry or appropriate registry keys not found.""" - - if not _can_read_reg: - return [] +def read_values(base, key): + """Return dict of registry keys and values. - L = [] - if path=='lib': - path= 'Library' - path = string.upper(path + ' Dirs') - K = ('Software\\Microsoft\\Devstudio\\%s\\' + - 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \ - (version,platform) - for base in (HKEY_CLASSES_ROOT, - HKEY_LOCAL_MACHINE, - HKEY_CURRENT_USER, - HKEY_USERS): + All names are converted to lowercase. + """ + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + d = {} + i = 0 + while 1: try: - k = RegOpenKeyEx(base,K) - i = 0 - while 1: - try: - (p,v,t) = RegEnumValue(k,i) - if string.upper(p) == path: - V = string.split(v,';') - for v in V: - if hasattr(v, "encode"): - try: - v = v.encode("mbcs") - except UnicodeError: - pass - if v == '' or v in L: continue - L.append(v) - break - i = i + 1 - except RegError: - break + name, value, type = RegEnumValue(handle, i) except RegError: + break + name = name.lower() + d[convert_mbcs(name)] = convert_mbcs(value) + i = i + 1 + return d + +def convert_mbcs(s): + enc = getattr(s, "encode", None) + if enc is not None: + try: + s = enc("mbcs") + except UnicodeError: pass - return L - -# get_msvc_paths() - - -def find_exe (exe, version_number): - """Try to find an MSVC executable program 'exe' (from version - 'version_number' of MSVC) in several places: first, one of the MSVC - program search paths from the registry; next, the directories in the - PATH environment variable. If any of those work, return an absolute - path that is known to exist. If none of them work, just return the - original program name, 'exe'.""" - - for p in get_msvc_paths ('path', version_number): - fn = os.path.join (os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - - # didn't find it; try existing path - for p in string.split (os.environ['Path'],';'): - fn = os.path.join(os.path.abspath(p),exe) - if os.path.isfile(fn): - return fn - - return exe # last desperate hope - - -def set_path_env_var (name, version_number): - """Set environment variable 'name' to an MSVC path type value obtained - from 'get_msvc_paths()'. This is equivalent to a SET command prior - to execution of spawned commands.""" - - p = get_msvc_paths (name, version_number) - if p: - os.environ[name] = string.join (p,';') - + return s + +class MacroExpander: + + def __init__(self, version): + self.macros = {} + self.load_macros(version) + + def set_macro(self, macro, path, key): + for base in HKEYS: + d = read_values(base, path) + if d: + self.macros["$(%s)" % macro] = d[key] + break + + def load_macros(self, version): + vsbase = r"Software\Microsoft\VisualStudio\%s.0" % version + self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") + self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") + net = r"Software\Microsoft\.NETFramework" + self.set_macro("FrameworkDir", net, "installroot") + self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") + + p = r"Software\Microsoft\NET Framework Setup\Product" + for base in HKEYS: + try: + h = RegOpenKeyEx(base, p) + except RegError: + continue + key = RegEnumKey(h, 0) + d = read_values(base, r"%s\%s" % (p, key)) + self.macros["$(FrameworkVersion)"] = d["version"] + + def sub(self, s): + for k, v in self.macros.items(): + s = string.replace(s, k, v) + return s + +def get_build_version(): + """Return the version of MSVC that was used to build Python. + + For Python 2.3 and up, the version number is included in + sys.version. For earlier versions, assume the compiler is MSVC 6. + """ + + prefix = "MSC v." + i = string.find(sys.version, prefix) + if i == -1: + return 6 + i += len(prefix) + s, rest = sys.version[i:].split(" ", 1) + n = int(s[:-2]) + if n == 12: + return 6 + elif n == 13: + return 7 + # else we don't know what version of the compiler this is + return None + class MSVCCompiler (CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, @@ -199,39 +189,31 @@ class MSVCCompiler (CCompiler) : static_lib_format = shared_lib_format = '%s%s' exe_extension = '.exe' - - def __init__ (self, - verbose=0, - dry_run=0, - force=0): - + def __init__ (self, verbose=0, dry_run=0, force=0): CCompiler.__init__ (self, verbose, dry_run, force) - versions = get_devstudio_versions () - - if versions: - version = versions[0] # highest version - - self.cc = find_exe("cl.exe", version) - self.linker = find_exe("link.exe", version) - self.lib = find_exe("lib.exe", version) - self.rc = find_exe("rc.exe", version) # resource compiler - self.mc = find_exe("mc.exe", version) # message compiler - set_path_env_var ('lib', version) - set_path_env_var ('include', version) - path=get_msvc_paths('path', version) - try: - for p in string.split(os.environ['path'],';'): - path.append(p) - except KeyError: - pass - os.environ['path'] = string.join(path,';') + self.__version = get_build_version() + if self.__version == 7: + self.__root = r"Software\Microsoft\VisualStudio" + self.__macros = MacroExpander(self.__version) else: - # devstudio not found in the registry - self.cc = "cl.exe" - self.linker = "link.exe" - self.lib = "lib.exe" - self.rc = "rc.exe" - self.mc = "mc.exe" + self.__root = r"Software\Microsoft\Devstudio" + self.__paths = self.get_msvc_paths("path") + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + self.set_path_env_var('lib') + self.set_path_env_var('include') + + # extend the MSVC path with the current path + try: + for p in string.split(os.environ['path'], ';'): + self.__paths.append(p) + except KeyError: + pass + os.environ['path'] = string.join(self.__paths, ';') self.preprocess_options = None self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , @@ -500,4 +482,68 @@ class MSVCCompiler (CCompiler) : # find_library_file () -# class MSVCCompiler + # Helper methods for using the MSVC registry settings + + def find_exe(self, exe): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + + for p in self.__paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in string.split(os.environ['Path'],';'): + fn = os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): + return fn + + return exe + + def get_msvc_paths(self, path, platform='x86'): + """Get a list of devstudio directories (include, lib or path). + + Return a list of strings. The list will be empty if unable to + access the registry or appropriate registry keys not found. + """ + + if not _can_read_reg: + return [] + + path = path + " dirs" + if self.__version == 7: + key = (r"%s\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" + % (self.__root,)) + else: + key = (r"%s\6.0\Build System\Components\Platforms" + + for base in HKEYS: + d = read_values(base, key) + if d: + if self.__version == 7: + return string.split(self.__macros.sub(d[path]), ";") + else: + return string.split(d[path], ";") + return [] + + def set_path_env_var(self, name): + """Set environment variable 'name' to an MSVC path type value. + + This is equivalent to a SET command prior to execution of spawned + commands. + """ + + if name == "lib": + p = self.get_msvc_paths("library") + else: + p = self.get_msvc_paths(name) + if p: + os.environ[name] = string.join(p, ';') + -- cgit v1.2.1 From c4ab88656cb41c9c7182805868af9ec6800f9874 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Fri, 9 May 2003 16:55:28 +0000 Subject: Replace line somehow deleted before last checkin. --- msvccompiler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/msvccompiler.py b/msvccompiler.py index b4c890d0..4d7159f0 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -523,6 +523,7 @@ class MSVCCompiler (CCompiler) : % (self.__root,)) else: key = (r"%s\6.0\Build System\Components\Platforms" + r"\Win32 (%s)\Directories" % (self.__root, platform)) for base in HKEYS: d = read_values(base, key) -- cgit v1.2.1 From afc49f281192ac64ca549636dec5e8765bb1e0fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Wed, 14 May 2003 19:48:57 +0000 Subject: Restore Python 1.5.2 compatibility. --- msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index 4d7159f0..eeac0ee6 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -148,7 +148,7 @@ def get_build_version(): i = string.find(sys.version, prefix) if i == -1: return 6 - i += len(prefix) + i = i + len(prefix) s, rest = sys.version[i:].split(" ", 1) n = int(s[:-2]) if n == 12: -- cgit v1.2.1 From bd92613fe048b143af67d29b018d525d8d380b46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 31 May 2003 08:09:21 +0000 Subject: Patch #740301: Add +s when linking shared libraries on HP-UX, use -L for the library path. --- unixccompiler.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index e444917f..02db910a 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -203,8 +203,10 @@ class UnixCCompiler(CCompiler): if sys.platform[:6] == "darwin": # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir - elif compiler[:3] == "gcc" or compiler[:3] == "g++": - return "-Wl,-R" + dir + elif sys.platform[:5] == "hp-ux": + return "+s -L" + dir + elif compiler[:3] == "gcc" or compiler[:3] == "g++": + return "-Wl,-R" + dir else: return "-R" + dir -- cgit v1.2.1 From 2e51b32d4660c3e6a8e90ce8e8fa7bb6bed786dc Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sun, 1 Jun 2003 19:27:40 +0000 Subject: Fixed indentation error. Closes bug #746953. --- unixccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 02db910a..11ecb9f6 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -203,9 +203,9 @@ class UnixCCompiler(CCompiler): if sys.platform[:6] == "darwin": # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir - elif sys.platform[:5] == "hp-ux": + elif sys.platform[:5] == "hp-ux": return "+s -L" + dir - elif compiler[:3] == "gcc" or compiler[:3] == "g++": + elif compiler[:3] == "gcc" or compiler[:3] == "g++": return "-Wl,-R" + dir else: return "-R" + dir -- cgit v1.2.1 From 3f5044ac5626bd886b51c905d16a1fcb1aaa6464 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 12 Jun 2003 17:23:58 +0000 Subject: Fix for sf # 749210, wininst isn't build correctly after building zip. The problem was that subcommands were not reinitialized. Bugfix candidate, will backport myself. --- command/bdist_wininst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 5acca11a..3c4c8939 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -100,7 +100,7 @@ class bdist_wininst (Command): if not self.skip_build: self.run_command('build') - install = self.reinitialize_command('install') + install = self.reinitialize_command('install', reinit_subcommands=1) install.root = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 -- cgit v1.2.1 From 59a7a49f88a89af3e6e168da54dcd45ffb374aed Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Fri, 27 Jun 2003 19:33:38 +0000 Subject: Do not add extra "\n" after bang line. --- command/build_scripts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index f61ad37d..8de9cd3f 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -15,7 +15,7 @@ from distutils.util import convert_path from distutils import log # check if Python is called on the first line with this expression -first_line_re = re.compile(r'^#!.*python[0-9.]*(\s+.*)?$') +first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$') class build_scripts (Command): @@ -96,7 +96,7 @@ class build_scripts (Command): (os.path.normpath(sys.executable), post_interp)) else: - outf.write("#!%s%s" % + outf.write("#!%s%s\n" % (os.path.join( sysconfig.get_config_var("BINDIR"), "python" + sysconfig.get_config_var("EXE")), -- cgit v1.2.1 From a17a0b4ac049c984145ad23ccbbe301b98dc6564 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 17 Jul 2003 14:41:07 +0000 Subject: Patch from John Anderson to enable VC 7.1 support. I tested against VC 7.0 and it caused no problems there. --- msvccompiler.py | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index eeac0ee6..5f3d8de8 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -45,6 +45,10 @@ except ImportError: RegError = win32api.error except ImportError: + log.info("Warning: Can't read registry to find the " + "necessary compiler setting\n" + "Make sure that Python modules _winreg, " + "win32api or win32con are installed.") pass if _can_read_reg: @@ -115,12 +119,15 @@ class MacroExpander: break def load_macros(self, version): - vsbase = r"Software\Microsoft\VisualStudio\%s.0" % version + vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") net = r"Software\Microsoft\.NETFramework" self.set_macro("FrameworkDir", net, "installroot") - self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") + if version > 7.0: + self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") + else: + self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") p = r"Software\Microsoft\NET Framework Setup\Product" for base in HKEYS: @@ -150,11 +157,13 @@ def get_build_version(): return 6 i = i + len(prefix) s, rest = sys.version[i:].split(" ", 1) - n = int(s[:-2]) - if n == 12: - return 6 - elif n == 13: - return 7 + majorVersion = int(s[:-2]) - 6 + minorVersion = int(s[2:3]) / 10.0 + # I don't think paths are affected by minor version in version 6 + if majorVersion == 6: + minorVersion = 0 + if majorVersion >= 6: + return majorVersion + minorVersion # else we don't know what version of the compiler this is return None @@ -192,13 +201,19 @@ class MSVCCompiler (CCompiler) : def __init__ (self, verbose=0, dry_run=0, force=0): CCompiler.__init__ (self, verbose, dry_run, force) self.__version = get_build_version() - if self.__version == 7: + if self.__version >= 7: self.__root = r"Software\Microsoft\VisualStudio" self.__macros = MacroExpander(self.__version) else: self.__root = r"Software\Microsoft\Devstudio" self.__paths = self.get_msvc_paths("path") + if len (self.__paths) == 0: + raise DistutilsPlatformError, \ + ("Python was built with version %s of Visual Studio, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." % self.__version) + self.cc = self.find_exe("cl.exe") self.linker = self.find_exe("link.exe") self.lib = self.find_exe("lib.exe") @@ -518,9 +533,9 @@ class MSVCCompiler (CCompiler) : return [] path = path + " dirs" - if self.__version == 7: - key = (r"%s\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" - % (self.__root,)) + if self.__version >= 7: + key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" + % (self.__root, self.__version)) else: key = (r"%s\6.0\Build System\Components\Platforms" r"\Win32 (%s)\Directories" % (self.__root, platform)) @@ -528,7 +543,7 @@ class MSVCCompiler (CCompiler) : for base in HKEYS: d = read_values(base, key) if d: - if self.__version == 7: + if self.__version >= 7: return string.split(self.__macros.sub(d[path]), ";") else: return string.split(d[path], ";") -- cgit v1.2.1 From 9089c83f40086c19cff06f883f152354c953bd4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Walter=20D=C3=B6rwald?= Date: Mon, 20 Oct 2003 14:01:56 +0000 Subject: Fix a bunch of typos in documentation, docstrings and comments. (From SF patch #810751) --- cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd.py b/cmd.py index 7e7a4cd5..6e44221e 100644 --- a/cmd.py +++ b/cmd.py @@ -148,7 +148,7 @@ class Command: """Set final values for all the options that this command supports. This is always called as late as possible, ie. after any option assignments from the command-line or from other commands have been - done. Thus, this is the place to to code option dependencies: if + done. Thus, this is the place to code option dependencies: if 'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as long as 'foo' still has the same value it was assigned in 'initialize_options()'. -- cgit v1.2.1 From f50262ae1518686131d3a0953983ea64b13b1b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Fri, 24 Oct 2003 20:09:23 +0000 Subject: Patch #812378: Normalize white space. --- sysconfig.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 67353a8d..46d1acbb 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -70,9 +70,9 @@ def get_python_inc(plat_specific=0, prefix=None): return os.path.join(prefix, "include") elif os.name == "mac": if plat_specific: - return os.path.join(prefix, "Mac", "Include") + return os.path.join(prefix, "Mac", "Include") else: - return os.path.join(prefix, "Include") + return os.path.join(prefix, "Include") elif os.name == "os2": return os.path.join(prefix, "Include") else: @@ -160,7 +160,7 @@ def customize_compiler(compiler): if os.environ.has_key('LDFLAGS'): ldshared = ldshared + ' ' + os.environ['LDFLAGS'] if basecflags: - opt = basecflags + ' ' + opt + opt = basecflags + ' ' + opt if os.environ.has_key('CFLAGS'): opt = opt + ' ' + os.environ['CFLAGS'] ldshared = ldshared + ' ' + os.environ['CFLAGS'] -- cgit v1.2.1 From 2c9c51ede8cea8c78f14fcc723c5c0a3cbf7b5cf Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 28 Nov 2003 19:42:56 +0000 Subject: See SF #848614: distutils' msvccompiler now tries to detect that MSVC6 is installed but the registry settings are incomplete because the gui has never been run. Already backported to release23-maint. --- msvccompiler.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/msvccompiler.py b/msvccompiler.py index 5f3d8de8..27fb658b 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -547,6 +547,16 @@ class MSVCCompiler (CCompiler) : return string.split(self.__macros.sub(d[path]), ";") else: return string.split(d[path], ";") + # MSVC 6 seems to create the registry entries we need only when + # the GUI is run. + if self.__version == 6: + for base in HKEYS: + if read_values(base, r"%s\6.0" % self.__root) is not None: + self.warn("It seems you have Visual Studio 6 installed, " + "but the expected registry settings are not present.\n" + "You must at least run the Visual Studio GUI once " + "so that these entries are created.") + break return [] def set_path_env_var(self, name): -- cgit v1.2.1 From 2a97217f7fae82ae141e9dc1a426146dca203b04 Mon Sep 17 00:00:00 2001 From: Andrew MacIntyre Date: Tue, 2 Dec 2003 12:17:59 +0000 Subject: use same compiler switches as core for extensions --- emxccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emxccompiler.py b/emxccompiler.py index 76bdbae5..f52e6323 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -63,8 +63,8 @@ class EMXCCompiler (UnixCCompiler): # Hard-code GCC because that's what this is all about. # XXX optimization, warnings etc. should be customizable. - self.set_executables(compiler='gcc -Zomf -Zmt -O2 -Wall', - compiler_so='gcc -Zomf -Zmt -O2 -Wall', + self.set_executables(compiler='gcc -Zomf -Zmt -O3 -fomit-frame-pointer -mprobe -Wall', + compiler_so='gcc -Zomf -Zmt -O3 -fomit-frame-pointer -mprobe -Wall', linker_exe='gcc -Zomf -Zmt -Zcrtdll', linker_so='gcc -Zomf -Zmt -Zcrtdll -Zdll') -- cgit v1.2.1 From 21a1ad1c19eeab489dc344c59f0cd027bda50dd7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 5 Dec 2003 20:12:23 +0000 Subject: Compile the files in the same order they are passed to the compiler. Use case: Sometimes 'compiling' source files (with SWIG, for example) creates additionl files which included by later sources. The win32all setup script requires this. There is no SF item for this, but it was discussed on distutils-sig: http://mail.python.org/pipermail/distutils-sig/2003-November/003514.html --- bcppcompiler.py | 6 +++++- ccompiler.py | 6 +++++- msvccompiler.py | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index cfbe04ac..b0360a24 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -96,7 +96,11 @@ class BCPPCompiler(CCompiler) : else: compile_opts.extend (self.compile_options) - for obj, (src, ext) in build.items(): + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue # XXX why do the normpath here? src = os.path.normpath(src) obj = os.path.normpath(obj) diff --git a/ccompiler.py b/ccompiler.py index 751ec069..ebd93c27 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -691,7 +691,11 @@ class CCompiler: depends, extra_postargs) cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - for obj, (src, ext) in build.items(): + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) # Return *all* object filenames, not just the ones we just built. diff --git a/msvccompiler.py b/msvccompiler.py index 27fb658b..1441ea04 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -291,7 +291,11 @@ class MSVCCompiler (CCompiler) : else: compile_opts.extend(self.compile_options) - for obj, (src, ext) in build.items(): + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue if debug: # pass the full pathname to MSVC in debug mode, # this allows the debugger to find the source file -- cgit v1.2.1 From affdbb99128310385c45cd2562afcb2f0009431f Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 18 Jan 2004 20:29:55 +0000 Subject: Whitespace normalization. --- command/build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 0c377681..7fff4222 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -171,7 +171,8 @@ class build_ext (Command): # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) - self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) + self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VC6')) + #self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory -- cgit v1.2.1 From cdd1c7641bdef9ac123a2879c88c2f4d28a9a9fe Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 18 Jan 2004 20:39:35 +0000 Subject: Revert another local change that snuck into a whitespace normalization patch. --- command/build_ext.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 7fff4222..0c377681 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -171,8 +171,7 @@ class build_ext (Command): # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) - self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VC6')) - #self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) + self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory -- cgit v1.2.1 From 950e6ac2191aa79725d553476bb9cd3211ae0a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Walter=20D=C3=B6rwald?= Date: Thu, 12 Feb 2004 17:35:32 +0000 Subject: Replace backticks with repr() or "%r" From SF patch #852334. --- cmd.py | 4 ++-- core.py | 2 +- dir_util.py | 2 +- dist.py | 4 ++-- fancy_getopt.py | 2 +- util.py | 16 ++++++++-------- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cmd.py b/cmd.py index 6e44221e..fef49390 100644 --- a/cmd.py +++ b/cmd.py @@ -253,8 +253,8 @@ class Command: if not ok: raise DistutilsOptionError, \ - "'%s' must be a list of strings (got %s)" % \ - (option, `val`) + "'%s' must be a list of strings (got %r)" % \ + (option, val) def _ensure_tested_string (self, option, tester, what, error_fmt, default=None): diff --git a/core.py b/core.py index a463272c..eb419721 100644 --- a/core.py +++ b/core.py @@ -202,7 +202,7 @@ def run_setup (script_name, script_args=None, stop_after="run"): used to drive the Distutils. """ if stop_after not in ('init', 'config', 'commandline', 'run'): - raise ValueError, "invalid value for 'stop_after': %s" % `stop_after` + raise ValueError, "invalid value for 'stop_after': %r" % (stop_after,) global _setup_stop_after, _setup_distribution _setup_stop_after = stop_after diff --git a/dir_util.py b/dir_util.py index bd1ea0f2..e479b624 100644 --- a/dir_util.py +++ b/dir_util.py @@ -33,7 +33,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): # Detect a common bug -- name is None if type(name) is not StringType: raise DistutilsInternalError, \ - "mkpath: 'name' must be a string (got %s)" % `name` + "mkpath: 'name' must be a string (got %r)" % (name,) # XXX what's the better way to handle verbosity? print as we create # each directory in the path (the current behaviour), or only announce diff --git a/dist.py b/dist.py index d313e7d1..f63ea973 100644 --- a/dist.py +++ b/dist.py @@ -525,9 +525,9 @@ class Distribution: func() else: raise DistutilsClassError( - "invalid help function %s for help option '%s': " + "invalid help function %r for help option '%s': " "must be a callable object (function, etc.)" - % (`func`, help_option)) + % (func, help_option)) if help_option_found: return diff --git a/fancy_getopt.py b/fancy_getopt.py index a4a4e797..512bc9b6 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -162,7 +162,7 @@ class FancyGetopt: else: # the option table is part of the code, so simply # assert that it is correct - assert "invalid option tuple: %s" % `option` + assert "invalid option tuple: %r" % (option,) # Type- and value-check the option names if type(long) is not StringType or len(long) < 2: diff --git a/util.py b/util.py index dc3183b6..8c3c8df9 100644 --- a/util.py +++ b/util.py @@ -285,7 +285,7 @@ def execute (func, args, msg=None, verbose=0, dry_run=0): print. """ if msg is None: - msg = "%s%s" % (func.__name__, `args`) + msg = "%s%r" % (func.__name__, args) if msg[-2:] == ',)': # correct for singleton tuple msg = msg[0:-2] + ')' @@ -307,7 +307,7 @@ def strtobool (val): elif val in ('n', 'no', 'f', 'false', 'off', '0'): return 0 else: - raise ValueError, "invalid truth value %s" % `val` + raise ValueError, "invalid truth value %r" % (val,) def byte_compile (py_files, @@ -394,11 +394,11 @@ files = [ script.write(string.join(map(repr, py_files), ",\n") + "]\n") script.write(""" -byte_compile(files, optimize=%s, force=%s, - prefix=%s, base_dir=%s, - verbose=%s, dry_run=0, +byte_compile(files, optimize=%r, force=%r, + prefix=%r, base_dir=%r, + verbose=%r, dry_run=0, direct=1) -""" % (`optimize`, `force`, `prefix`, `base_dir`, `verbose`)) +""" % (optimize, force, prefix, base_dir, verbose)) script.close() @@ -432,8 +432,8 @@ byte_compile(files, optimize=%s, force=%s, if prefix: if file[:len(prefix)] != prefix: raise ValueError, \ - ("invalid prefix: filename %s doesn't start with %s" - % (`file`, `prefix`)) + ("invalid prefix: filename %r doesn't start with %r" + % (file, prefix)) dfile = dfile[len(prefix):] if base_dir: dfile = os.path.join(base_dir, dfile) -- cgit v1.2.1 From bc3373b4d89d718b8760d3c8a4afd18316fec86c Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 17 Feb 2004 22:35:19 +0000 Subject: commentary about how bad ConfigParser is doesn't help here, and the suggested approach to dealing with it isn't a good one; we need a better general purpose config reader, not a distutils-specific reader --- dist.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dist.py b/dist.py index f63ea973..586e6bb1 100644 --- a/dist.py +++ b/dist.py @@ -346,9 +346,7 @@ class Distribution: opt_dict[opt] = (filename, val) # Make the ConfigParser forget everything (so we retain - # the original filenames that options come from) -- gag, - # retch, puke -- another good reason for a distutils- - # specific config parser (sigh...) + # the original filenames that options come from) parser.__init__() # If there was a "global" section in the config file, use it -- cgit v1.2.1 From 0d7a3db04fd813544f939ce1d9bbcf135f1a6f35 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 20 Feb 2004 14:43:21 +0000 Subject: Patch #892660 from Mark Hammond, for distutils bdist_wininst command. install.c: support for a 'pre-install-script', run before anything has been installed. Provides a 'message_box' module function for use by either the pre-install or post-install scripts. bdist_wininst.py: support for pre-install script. Typo (build->built), fixes so that --target-version can still work, even when the distribution has extension modules - in this case, we insist on --skip-build, as we still can't actually build other versions. --- command/bdist_wininst.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 3c4c8939..76b1762e 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -43,6 +43,10 @@ class bdist_wininst (Command): ('install-script=', None, "basename of installation script to be run after" "installation or before deinstallation"), + ('pre-install-script=', None, + "Fully qualified filename of a script to be run before " + "any files are installed. This script need not be in the " + "distribution"), ] boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', @@ -59,6 +63,7 @@ class bdist_wininst (Command): self.title = None self.skip_build = 0 self.install_script = None + self.pre_install_script = None # initialize_options() @@ -69,11 +74,12 @@ class bdist_wininst (Command): self.bdist_dir = os.path.join(bdist_base, 'wininst') if not self.target_version: self.target_version = "" - if self.distribution.has_ext_modules(): + if not self.skip_build and self.distribution.has_ext_modules(): short_version = get_python_version() if self.target_version and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be" + short_version + "target version can only be %s, or the '--skip_build'" \ + " option must be specified" % (short_version,) self.target_version = short_version self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) @@ -109,6 +115,21 @@ class bdist_wininst (Command): # we do not want to include pyc or pyo files install_lib.compile = 0 install_lib.optimize = 0 + + # If we are building an installer for a Python version other + # than the one we are currently running, then we need to ensure + # our build_lib reflects the other Python version rather than ours. + # Note that for target_version!=sys.version, we must have skipped the + # build step, so there is no issue with enforcing the build of this + # version. + target_version = self.target_version + if not target_version: + assert self.skip_build, "Should have already checked this" + target_version = sys.version[0:3] + plat_specifier = ".%s-%s" % (get_platform(), target_version) + build = self.get_finalized_command('build') + build.build_lib = os.path.join(build.build_base, + 'lib' + plat_specifier) # Use a custom scheme for the zip-file, because we have to decide # at installation time which scheme to use. @@ -187,7 +208,7 @@ class bdist_wininst (Command): lines.append("title=%s" % repr(title)[1:-1]) import time import distutils - build_info = "Build %s with distutils-%s" % \ + build_info = "Built %s with distutils-%s" % \ (time.ctime(time.time()), distutils.__version__) lines.append("build_info=%s" % build_info) return string.join(lines, "\n") @@ -223,6 +244,11 @@ class bdist_wininst (Command): if bitmap: file.write(bitmapdata) + # Append the pre-install script + cfgdata = cfgdata + "\0" + if self.pre_install_script: + script_data = open(self.pre_install_script, "r").read() + cfgdata = cfgdata + script_data + "\n\0" file.write(cfgdata) header = struct.pack(" Date: Fri, 20 Feb 2004 14:44:32 +0000 Subject: Recompiled the binary wininst.exe. Patch #892660 from Mark Hammond, for distutils bdist_wininst command. install.c: support for a 'pre-install-script', run before anything has been installed. Provides a 'message_box' module function for use by either the pre-install or post-install scripts. bdist_wininst.py: support for pre-install script. Typo (build->built), fixes so that --target-version can still work, even when the distribution has extension modules - in this case, we insist on --skip-build, as we still can't actually build other versions. --- command/wininst.exe | Bin 20992 -> 21504 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst.exe b/command/wininst.exe index 6290a936..bea2bed4 100755 Binary files a/command/wininst.exe and b/command/wininst.exe differ -- cgit v1.2.1 From ab3840c7ff78c37c20f8c36242eba1b3329778e2 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 20 Feb 2004 18:26:55 +0000 Subject: wininst.exe is no longer used - we now need wininst-6.exe or wininst-7.1.exe. --- command/wininst.exe | Bin 21504 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 command/wininst.exe diff --git a/command/wininst.exe b/command/wininst.exe deleted file mode 100755 index bea2bed4..00000000 Binary files a/command/wininst.exe and /dev/null differ -- cgit v1.2.1 From 61a049aec865a04851211b698bd3522c743ff242 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 20 Feb 2004 18:33:38 +0000 Subject: wininst-6.exe and wininst-7.1.exe are in CVS, so that they can be included in Python distributions for systems other than Windows. Windows installers can be build on non-Windows systems as long as they only include pure python module distributions. --- command/wininst-6.exe | Bin 0 -> 21504 bytes command/wininst-7.1.exe | Bin 0 -> 22016 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 command/wininst-6.exe create mode 100644 command/wininst-7.1.exe diff --git a/command/wininst-6.exe b/command/wininst-6.exe new file mode 100644 index 00000000..c40cbcb5 Binary files /dev/null and b/command/wininst-6.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe new file mode 100644 index 00000000..89a78885 Binary files /dev/null and b/command/wininst-7.1.exe differ -- cgit v1.2.1 From b78c1bc106189b7b7ad1013e0d723b4a47fa5d6c Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 20 Feb 2004 19:38:50 +0000 Subject: Use the right wininstXX.exe, depending on msvccompiler.get_build_version(). Distributions without a pre-install-script didn't work any longer, we must at least provide the terminating NUL character. --- command/bdist_wininst.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 76b1762e..324ce31a 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -249,6 +249,9 @@ class bdist_wininst (Command): if self.pre_install_script: script_data = open(self.pre_install_script, "r").read() cfgdata = cfgdata + script_data + "\n\0" + else: + # empty pre-install script + cfgdata = cfgdata + "\0" file.write(cfgdata) header = struct.pack(" Date: Tue, 24 Feb 2004 23:54:17 +0000 Subject: Make _spawn_posix be ready for EINTR. waitpid(2) can be interrupted by SIGCHLD or sth because no signal is masked before. This fixes an optimized installation problem on FreeBSD libpthread. --- spawn.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/spawn.py b/spawn.py index 4857ce5e..67391e88 100644 --- a/spawn.py +++ b/spawn.py @@ -144,7 +144,14 @@ def _spawn_posix (cmd, # Loop until the child either exits or is terminated by a signal # (ie. keep waiting if it's merely stopped) while 1: - (pid, status) = os.waitpid(pid, 0) + try: + (pid, status) = os.waitpid(pid, 0) + except OSError, exc: + import errno + if exc.errno == errno.EINTR: + continue + raise DistutilsExecError, \ + "command '%s' failed: %s" % (cmd[0], exc[-1]) if os.WIFSIGNALED(status): raise DistutilsExecError, \ "command '%s' terminated by signal %d" % \ -- cgit v1.2.1 From 7fcc46c767cdadaa18b2b1111864b92ec4439bd9 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Mon, 22 Mar 2004 22:22:05 +0000 Subject: Basic dependency checking. setup() has two new optional arguments requires and provides. requires is a sequence of strings, of the form 'packagename-version'. The dependency checking so far merely does an '__import__(packagename)' and checks for packagename.__version__ You can also leave off the version, and any version of the package will be installed. There's a special case for the package 'python' - sys.version_info is used, so requires= ( 'python-2.3', ) just works. Provides is of the same format as requires - but if it's not supplied, a provides is generated by adding the version to each entry in packages, or modules if packages isn't there. Provides is currently only used in the PKG-INFO file. Shortly, PyPI will grow the ability to accept these lines, and register will be updated to send them. There's a new command 'checkdep' command that runs these checks. For this version, only greater-than-or-equal checking is done. We'll add the ability to specify an optional operator later. --- command/__init__.py | 1 + command/checkdep.py | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ command/install.py | 13 +++++++++- core.py | 3 ++- dist.py | 60 ++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 command/checkdep.py diff --git a/command/__init__.py b/command/__init__.py index 870005dc..3a9a53ee 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -24,6 +24,7 @@ __all__ = ['build', 'bdist_dumb', 'bdist_rpm', 'bdist_wininst', + 'checkdep', # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', diff --git a/command/checkdep.py b/command/checkdep.py new file mode 100644 index 00000000..729002c7 --- /dev/null +++ b/command/checkdep.py @@ -0,0 +1,70 @@ +"""distutils.command.x + +Implements the Distutils 'x' command. +""" + +# created 2000/mm/dd, John Doe + +__revision__ = "$Id$" + +from distutils.core import Command + +class DependencyFailure(Exception): pass + +class VersionTooOld(DependencyFailure): pass + +class VersionNotKnown(DependencyFailure): pass + +class checkdep (Command): + + # Brief (40-50 characters) description of the command + description = "check package dependencies" + + # List of option tuples: long name, short name (None if no short + # name), and help string. + # Later on, we might have auto-fetch and the like here. Feel free. + user_options = [] + + def initialize_options (self): + self.debug = None + + # initialize_options() + + + def finalize_options (self): + pass + # finalize_options() + + + def run (self): + from distutils.version import LooseVersion + failed = [] + for pkg, ver in self.distribution.metadata.requires: + if pkg == 'python': + if ver is not None: + # Special case the 'python' package + import sys + thisver = LooseVersion('%d.%d.%d'%sys.version_info[:3]) + if thisver < ver: + failed.append(((pkg,ver), VersionTooOld(thisver))) + continue + # Kinda hacky - we should do more here + try: + mod = __import__(pkg) + except Exception, e: + failed.append(((pkg,ver), e)) + continue + if ver is not None: + if hasattr(mod, '__version__'): + thisver = LooseVersion(mod.__version__) + if thisver < ver: + failed.append(((pkg,ver), VersionTooOld(thisver))) + else: + failed.append(((pkg,ver), VersionNotKnown())) + + if failed: + raise DependencyFailure, failed + + # run() + +# class x diff --git a/command/install.py b/command/install.py index 5d5bdaa7..7fb46a74 100644 --- a/command/install.py +++ b/command/install.py @@ -126,6 +126,8 @@ class install (Command): "force installation (overwrite any existing files)"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), + ('skip-checkdep', None, + "skip checking dependencies (use at own risk)"), # Where to install documentation (eventually!) #('doc-format=', None, "format of documentation to generate"), @@ -183,12 +185,15 @@ class install (Command): # 'force' forces installation, even if target files are not # out-of-date. 'skip_build' skips running the "build" command, - # handy if you know it's not necessary. 'warn_dir' (which is *not* + # handy if you know it's not necessary. 'skip_checkdep' skips + # the 'checkdep' command, if you are sure you can work around the + # dependency failure in another way. 'warn_dir' (which is *not* # a user option, it's just there so the bdist_* commands can turn # it off) determines whether we warn about installing to a # directory not in sys.path. self.force = 0 self.skip_build = 0 + self.skip_checkdep = 0 self.warn_dir = 1 # These are only here as a conduit from the 'build' command to the @@ -500,6 +505,12 @@ class install (Command): if not self.skip_build: self.run_command('build') + # We check dependencies before we install + # For now, this is disabled. Before 2.4 is released, this will + # be turned on. + #if not self.skip_checkdep: + # self.run_command('checkdep') + # Run all sub-commands (at least those that need to be run) for cmd_name in self.get_sub_commands(): self.run_command(cmd_name) diff --git a/core.py b/core.py index eb419721..fba463c1 100644 --- a/core.py +++ b/core.py @@ -47,7 +47,8 @@ setup_keywords = ('distclass', 'script_name', 'script_args', 'options', 'name', 'version', 'author', 'author_email', 'maintainer', 'maintainer_email', 'url', 'license', 'description', 'long_description', 'keywords', - 'platforms', 'classifiers', 'download_url') + 'platforms', 'classifiers', 'download_url', + 'provides', 'requires', ) # Legal keyword arguments for the Extension constructor extension_keywords = ('name', 'sources', 'include_dirs', diff --git a/dist.py b/dist.py index 586e6bb1..2795b7bc 100644 --- a/dist.py +++ b/dist.py @@ -214,6 +214,51 @@ class Distribution: else: sys.stderr.write(msg + "\n") + # Build up the requires sequence + from distutils.version import LooseVersion + requires = attrs.get('requires') + if requires: + if isinstance(requires, type('')): + raise DistutilsOptionError, 'requires should be a sequence' + newreq = [] + for req in requires: + if '-' not in req: + # We have a plain package name - any version will do + newreq.append((req,None)) + else: + pkg, ver = string.split(req, '-', 1) + newreq.append((pkg, LooseVersion(ver))) + attrs['requires'] = newreq + + # Build up the provides object. If the setup() has no + # provides line, we use packages or modules and the version + # to synthesise the provides. If no version is provided (no + # pun intended) we don't have a provides entry at all. + provides = attrs.get('provides') + if provides: + if isinstance(provides, type('')): + raise DistutilsOptionError, 'provides should be a sequence' + newprov = [] + for prov in provides: + if '-' not in prov: + # We have a plain package name - any version will do + newprov.append((prov,None)) + else: + pkg, ver = string.split(prov, '-', 1) + newprov.append((pkg, LooseVersion(ver))) + attrs['provides'] = newprov + elif attrs.get('version'): + # Build a provides line + prov = [] + if attrs.get('packages'): + for pkg in attrs['packages']: + pkg = string.replace(pkg, '/', '.') + prov.append('%s-%s'%(pkg, attrs['version'])) + elif attrs.get('modules'): + for mod in attrs['modules']: + prov.append('%s-%s'%(mod, attrs['version'])) + attrs['provides'] = prov + # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! for (key,val) in attrs.items(): @@ -974,7 +1019,7 @@ class DistributionMetadata: "license", "description", "long_description", "keywords", "platforms", "fullname", "contact", "contact_email", "license", "classifiers", - "download_url") + "download_url", "provides", "requires",) def __init__ (self): self.name = None @@ -991,6 +1036,8 @@ class DistributionMetadata: self.platforms = None self.classifiers = None self.download_url = None + self.requires = [] + self.provides = [] def write_pkg_info (self, base_dir): """Write the PKG-INFO file into the release tree. @@ -1006,6 +1053,10 @@ class DistributionMetadata: pkg_info.write('Author: %s\n' % self.get_contact() ) pkg_info.write('Author-email: %s\n' % self.get_contact_email() ) pkg_info.write('License: %s\n' % self.get_license() ) + for req in self.get_requires(): + pkg_info.write('Requires: %s\n' % req ) + for prov in self.get_provides(): + pkg_info.write('Provides: %s\n' % prov ) if self.download_url: pkg_info.write('Download-URL: %s\n' % self.download_url) @@ -1084,6 +1135,13 @@ class DistributionMetadata: def get_download_url(self): return self.download_url or "UNKNOWN" + def get_requires(self): + return [ '%s%s%s'%(x, (y and '-') or '', y or '') + for x,y in self.requires ] + + def get_provides(self): + return self.provides + # class DistributionMetadata -- cgit v1.2.1 From 62e895c42c640dfee627e9ae286a5577654ebdab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 25 Mar 2004 14:58:19 +0000 Subject: Defer compilation of regular expressions until first use. --- util.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/util.py b/util.py index 8c3c8df9..12775d6f 100644 --- a/util.py +++ b/util.py @@ -209,9 +209,12 @@ def grok_environment_error (exc, prefix="error: "): # Needed by 'split_quoted()' -_wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) -_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") -_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') +_wordchars_re = _squote_re = _dquote_re = None +def _init_regex(): + global _wordchars_re, _squote_re, _dquote_re + _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) + _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") + _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') def split_quoted (s): """Split a string up according to Unix shell-like rules for quotes and @@ -227,6 +230,7 @@ def split_quoted (s): # This is a nice algorithm for splitting up a single string, since it # doesn't require character-by-character examination. It was a little # bit of a brain-bender to get it working right, though... + if _wordchars_re is None: _init_regex() s = string.strip(s) words = [] -- cgit v1.2.1 From 74d0b875f5ca2d37ade9f60df8ab09b90db35b57 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 25 Mar 2004 22:04:52 +0000 Subject: make sure the default manifest generation includes files identified as scripts closes SF bug 796042 --- command/build_scripts.py | 2 ++ command/sdist.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/command/build_scripts.py b/command/build_scripts.py index 8de9cd3f..165a009d 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -41,6 +41,8 @@ class build_scripts (Command): ('force', 'force')) self.scripts = self.distribution.scripts + def get_source_files(self): + return self.scripts def run (self): if not self.scripts: diff --git a/command/sdist.py b/command/sdist.py index c0b7dd45..0a29addb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -304,6 +304,10 @@ class sdist (Command): build_clib = self.get_finalized_command('build_clib') self.filelist.extend(build_clib.get_source_files()) + if self.distribution.has_scripts(): + build_scripts = self.get_finalized_command('build_scripts') + self.filelist.extend(build_scripts.get_source_files()) + # add_defaults () -- cgit v1.2.1 From 802e2d1d699ab8830e703ab229c31a7e2749f369 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Tue, 11 May 2004 18:13:10 +0000 Subject: Added 2.3.3 and 2.3.4 to the release table. Added 2004 to the list of copyright years. --- command/build_ext.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 0c377681..83364c74 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -171,9 +171,10 @@ class build_ext (Command): # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) - self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) + self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VC6')) + #self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) - # OS/2 (EMX) doesn't support Debug vs Release builds, but has the + # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory if os.name == 'os2': self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) @@ -636,7 +637,7 @@ class build_ext (Command): # EMX/GCC requires the python library explicitly, and I # believe VACPP does as well (though not confirmed) - AIM Apr01 template = "python%d%d" - # debug versions of the main DLL aren't supported, at least + # debug versions of the main DLL aren't supported, at least # not at this time - AIM Apr01 #if self.debug: # template = template + '_d' -- cgit v1.2.1 From 56255b44272116bc022a3c8b872a6b5441e1177f Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Tue, 11 May 2004 18:18:35 +0000 Subject: Reverting local change checked in by mistake. --- command/build_ext.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 83364c74..0c377681 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -171,10 +171,9 @@ class build_ext (Command): # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) - self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VC6')) - #self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) + self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) - # OS/2 (EMX) doesn't support Debug vs Release builds, but has the + # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory if os.name == 'os2': self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) @@ -637,7 +636,7 @@ class build_ext (Command): # EMX/GCC requires the python library explicitly, and I # believe VACPP does as well (though not confirmed) - AIM Apr01 template = "python%d%d" - # debug versions of the main DLL aren't supported, at least + # debug versions of the main DLL aren't supported, at least # not at this time - AIM Apr01 #if self.debug: # template = template + '_d' -- cgit v1.2.1 From 23064f19614cbb3afc2ce8c908e05d5bc4e11aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Walter=20D=C3=B6rwald?= Date: Mon, 31 May 2004 15:12:27 +0000 Subject: Fix typo (from SF bug #962602) --- filelist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filelist.py b/filelist.py index bfa53d21..0872e966 100644 --- a/filelist.py +++ b/filelist.py @@ -166,7 +166,7 @@ class FileList: (dir, string.join(patterns))) for pattern in patterns: if not self.include_pattern(pattern, prefix=dir): - log.warn(("warngin: no files found matching '%s' " + + log.warn(("warning: no files found matching '%s' " + "under directory '%s'"), pattern, dir) -- cgit v1.2.1 From ffd7d4b009e36c735ec8e7bead8c45c56049f144 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Mon, 31 May 2004 19:27:59 +0000 Subject: SF patch 959726: sdist versus SVN The disutils sdist command now ignores .svn directories. --- command/sdist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 0a29addb..70214965 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -347,14 +347,14 @@ class sdist (Command): * the build tree (typically "build") * the release tree itself (only an issue if we ran "sdist" previously with --keep-temp, or it aborted) - * any RCS or CVS directories + * any RCS, CVS and .svn directories """ build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() self.filelist.exclude_pattern(None, prefix=build.build_base) self.filelist.exclude_pattern(None, prefix=base_dir) - self.filelist.exclude_pattern(r'/(RCS|CVS)/.*', is_regex=1) + self.filelist.exclude_pattern(r'/(RCS|CVS|\.svn)/.*', is_regex=1) def write_manifest (self): -- cgit v1.2.1 From 48fa26d6758e83a5680f95d8795ee6428d752a02 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Thu, 3 Jun 2004 12:41:45 +0000 Subject: Partial fix for #887242 (link extensions with dynamic_lookup in stead of hard linking against the framework). If $MACOSX_DEPLOYMENT_TARGET is set, and >= 10.3, during configure we setup extensions to link with dynamic lookup. We also record the value in the Makefile. Distutils checks whether a value for MACOSX_DEPLOYMENT_TARGET was recorded in the Makefile, and if it was insists that the current value matches. This is only a partial fix because it only applies to 2.4, and the "two python problem" exists with Python 2.3 shipped with MacOSX 10.3, which we have no influence over. --- sysconfig.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 46d1acbb..c912a15c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -355,7 +355,19 @@ def _init_posix(): raise DistutilsPlatformError(my_msg) - + # On MacOSX we need to check the setting of the environment variable + # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so + # it needs to be compatible. + # An alternative would be to force MACOSX_DEPLOYMENT_TARGET to be + # the same as during configure. + if sys.platform == 'darwin' and g.has_key('CONFIGURE_MACOSX_DEPLOYMENT_TARGET'): + cfg_target = g['CONFIGURE_MACOSX_DEPLOYMENT_TARGET'] + cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') + if cfg_target != cur_target: + my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' + % (cur_target, cfg_target)) + raise DistutilsPlatformError(my_msg) + # On AIX, there are wrong paths to the linker scripts in the Makefile # -- these paths are relative to the Python source, but when installed # the scripts are in another directory. -- cgit v1.2.1 From 10fc69e6cf3fed9c530548ec7a8dbc4536ad4576 Mon Sep 17 00:00:00 2001 From: Hye-Shik Chang Date: Sat, 5 Jun 2004 18:37:53 +0000 Subject: SF #877165: Give an info about what C++ compiler command should be used in cygwin and mingw32. (Reported by Michael Droettboom) --- cygwinccompiler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 94b8b86b..0101bae5 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -108,6 +108,7 @@ class CygwinCCompiler (UnixCCompiler): # XXX optimization, warnings etc. should be customizable. self.set_executables(compiler='gcc -mcygwin -O -Wall', compiler_so='gcc -mcygwin -mdll -O -Wall', + compiler_cxx='g++ -mcygwin -O -Wall', linker_exe='gcc -mcygwin', linker_so=('%s -mcygwin %s' % (self.linker_dll, shared_option))) @@ -295,6 +296,7 @@ class Mingw32CCompiler (CygwinCCompiler): self.set_executables(compiler='gcc -mno-cygwin -O -Wall', compiler_so='gcc -mno-cygwin -mdll -O -Wall', + compiler_cxx='g++ -mno-cygwin -O -Wall', linker_exe='gcc -mno-cygwin', linker_so='%s -mno-cygwin %s %s' % (self.linker_dll, shared_option, -- cgit v1.2.1 From 2dc3968b1f396bca0d8732826103427ffa8f3fe8 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Fri, 11 Jun 2004 17:16:46 +0000 Subject: Bug 957381: rpmbuild builds a -debuginfo rpm on recent Redhat and Fedora releases. Ignore it, rather than breaking. Will backport. (and r1.1000 for Misc/NEWS!) --- command/bdist_rpm.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 237cc70d..4be99994 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -313,10 +313,15 @@ class bdist_rpm (Command): if not self.source_only: rpms = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*.rpm")) + debuginfo = glob.glob(os.path.join(rpm_dir['RPMS'], \ + "*/*debuginfo*.rpm")) + if debuginfo: + rpms.remove(debuginfo[0]) assert len(rpms) == 1, \ "unexpected number of RPM files found: %s" % rpms self.move_file(rpms[0], self.dist_dir) - + if debuginfo: + self.move_file(debuginfo[0], self.dist_dir) # run() -- cgit v1.2.1 From f94afed8695dece39c63c0122579a9e7f823d9e0 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 11 Jun 2004 21:50:33 +0000 Subject: Add support for package data. This is basically the support for package data from Phillip Eby's setuptools package. I've changed it only to fit it into the core implementation rather than to live in subclasses, and added documentation. --- command/build_py.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ dist.py | 1 + 2 files changed, 52 insertions(+) diff --git a/command/build_py.py b/command/build_py.py index 6c007c69..329b55a9 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -37,6 +37,7 @@ class build_py (Command): self.build_lib = None self.py_modules = None self.package = None + self.package_data = None self.package_dir = None self.compile = 0 self.optimize = 0 @@ -51,6 +52,8 @@ class build_py (Command): # options -- list of packages and list of modules. self.packages = self.distribution.packages self.py_modules = self.distribution.py_modules + self.package_data = self.distribution.package_data + self.data_files = self.get_data_files() self.package_dir = {} if self.distribution.package_dir: for name, path in self.distribution.package_dir.items(): @@ -92,11 +95,53 @@ class build_py (Command): self.build_modules() if self.packages: self.build_packages() + self.build_package_data() self.byte_compile(self.get_outputs(include_bytecode=0)) # run () + def get_data_files (self): + """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" + data = [] + for package in self.packages: + # Locate package source directory + src_dir = self.get_package_dir(package) + + # Compute package build directory + build_dir = os.path.join(*([self.build_lib] + package.split('.'))) + + # Length of path to strip from found files + plen = len(src_dir)+1 + + # Strip directory from globbed filenames + filenames = [ + file[plen:] for file in self.find_data_files(package, src_dir) + ] + data.append((package, src_dir, build_dir, filenames)) + return data + + def find_data_files (self, package, src_dir): + """Return filenames for package's data files in 'src_dir'""" + globs = (self.package_data.get('', []) + + self.package_data.get(package, [])) + files = [] + for pattern in globs: + # Each pattern has to be converted to a platform-specific path + filelist = glob(os.path.join(src_dir, convert_path(pattern))) + # Files that match more than one pattern are only added once + files.extend([fn for fn in filelist if fn not in files]) + return files + + def build_package_data (self): + """Copy data files into build directory""" + lastdir = None + for package, src_dir, build_dir, filenames in self.data_files: + for filename in filenames: + target = os.path.join(build_dir, filename) + self.mkpath(os.path.dirname(target)) + self.copy_file(os.path.join(src_dir, filename), target, + preserve_mode=False) def get_package_dir (self, package): """Return the directory, relative to the top of the source @@ -304,6 +349,12 @@ class build_py (Command): if self.optimize > 0: outputs.append(filename + "o") + outputs += [ + os.path.join(build_dir, filename) + for package, src_dir, build_dir, filenames in self.data_files + for filename in filenames + ] + return outputs diff --git a/dist.py b/dist.py index 2795b7bc..7d0a7bad 100644 --- a/dist.py +++ b/dist.py @@ -158,6 +158,7 @@ class Distribution: # than of the Distribution itself. We provide aliases for them in # Distribution as a convenience to the developer. self.packages = None + self.package_data = {} self.package_dir = None self.py_modules = None self.libraries = None -- cgit v1.2.1 From c32a93c26b45cb63b89156e60d7ab2218feec3de Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 15 Jun 2004 15:49:46 +0000 Subject: One unit test for distutils is not much, but is more than we had yesterday. We need to write more; hopefully the barrier is a little lower now. --- tests/__init__.py | 35 ++++++++++++++++++++++++++++++++ tests/test_install_scripts.py | 46 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/test_install_scripts.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..7bdb9124 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,35 @@ +"""Test suite for distutils. + +This test suite consists of a collection of test modules in the +distutils.tests package. Each test module has a name starting with +'test' and contains a function test_suite(). The function is expected +to return an initialized unittest.TestSuite instance. + +Tests for the command classes in the distutils.command package are +included in distutils.tests as well, instead of using a separate +distutils.command.tests package, since command identification is done +by import rather than matching pre-defined names. + +""" + +import os +import sys +import unittest + + +here = os.path.dirname(__file__) + + +def test_suite(): + suite = unittest.TestSuite() + for fn in os.listdir(here): + if fn.startswith("test") and fn.endswith(".py"): + modname = "distutils.tests." + fn[:-3] + __import__(modname) + module = sys.modules[modname] + suite.addTest(module.test_suite()) + return suite + + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py new file mode 100644 index 00000000..f9a95ea5 --- /dev/null +++ b/tests/test_install_scripts.py @@ -0,0 +1,46 @@ +"""Tests for distutils.command.install_scripts.""" + +import os +import unittest + +from distutils.command.install_scripts import install_scripts +from distutils.core import Distribution + + +class InstallScriptsTestCase(unittest.TestCase): + + def test_default_settings(self): + dist = Distribution() + dist.command_obj["build"] = DummyCommand(build_scripts="/foo/bar") + dist.command_obj["install"] = DummyCommand( + install_scripts="/splat/funk", + force=1, + skip_build=1, + ) + cmd = install_scripts(dist) + self.assert_(not cmd.force) + self.assert_(not cmd.skip_build) + self.assert_(cmd.build_dir is None) + self.assert_(cmd.install_dir is None) + + cmd.finalize_options() + + self.assert_(cmd.force) + self.assert_(cmd.skip_build) + self.assertEqual(cmd.build_dir, "/foo/bar") + self.assertEqual(cmd.install_dir, "/splat/funk") + + +class DummyCommand: + + def __init__(self, **kwargs): + for kw, val in kwargs.items(): + setattr(self, kw, val) + + def ensure_finalized(self): + pass + + + +def test_suite(): + return unittest.makeSuite(InstallScriptsTestCase) -- cgit v1.2.1 From 05b7bce87c1ab146c6912af1520c1d988fa43959 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 15 Jun 2004 16:55:46 +0000 Subject: add a test that actually installs some scripts --- tests/test_install_scripts.py | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index f9a95ea5..824f7336 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -1,6 +1,8 @@ """Tests for distutils.command.install_scripts.""" import os +import shutil +import tempfile import unittest from distutils.command.install_scripts import install_scripts @@ -9,6 +11,23 @@ from distutils.core import Distribution class InstallScriptsTestCase(unittest.TestCase): + def setUp(self): + self.tempdirs = [] + + def tearDown(self): + while self.tempdirs: + d = self.tempdirs.pop() + shutil.rmtree(d) + + def mkdtemp(self): + """Create a temporary directory that will be cleaned up. + + Returns the path of the directory. + """ + d = tempfile.mkdtemp() + self.tempdirs.append(d) + return d + def test_default_settings(self): dist = Distribution() dist.command_obj["build"] = DummyCommand(build_scripts="/foo/bar") @@ -30,8 +49,45 @@ class InstallScriptsTestCase(unittest.TestCase): self.assertEqual(cmd.build_dir, "/foo/bar") self.assertEqual(cmd.install_dir, "/splat/funk") + def test_installation(self): + source = self.mkdtemp() + expected = [] + + def write_script(name, text): + expected.append(name) + f = open(os.path.join(source, name), "w") + f.write(text) + f.close() + + write_script("script1.py", ("#! /usr/bin/env python2.3\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + write_script("script2.py", ("#!/usr/bin/python\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + write_script("shell.sh", ("#!/bin/sh\n" + "# bogus shell script w/ sh-bang\n" + "exit 0\n")) + + target = self.mkdtemp() + dist = Distribution() + dist.command_obj["build"] = DummyCommand(build_scripts=source) + dist.command_obj["install"] = DummyCommand( + install_scripts=target, + force=1, + skip_build=1, + ) + cmd = install_scripts(dist) + cmd.finalize_options() + cmd.run() + + installed = os.listdir(target) + for name in expected: + self.assert_(name in installed) + class DummyCommand: + """Class to store options for retrieval via set_undefined_options().""" def __init__(self, **kwargs): for kw, val in kwargs.items(): -- cgit v1.2.1 From 8191b39bcc348a1b1f89d7ab3a00e6b2afcf5e94 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 17 Jun 2004 20:14:50 +0000 Subject: move support code to a helper module to ease re-use --- tests/support.py | 41 +++++++++++++++++++++++++++++++++++++++++ tests/test_install_scripts.py | 41 +++++++---------------------------------- 2 files changed, 48 insertions(+), 34 deletions(-) create mode 100644 tests/support.py diff --git a/tests/support.py b/tests/support.py new file mode 100644 index 00000000..cef985d7 --- /dev/null +++ b/tests/support.py @@ -0,0 +1,41 @@ +"""Support code for distutils test cases.""" + +import shutil +import tempfile + + +class TempdirManager(object): + """Mix-in class that handles temporary directories for test cases. + + This is intended to be used with unittest.TestCase. + """ + + def setUp(self): + super(TempdirManager, self).setUp() + self.tempdirs = [] + + def tearDown(self): + super(TempdirManager, self).tearDown() + while self.tempdirs: + d = self.tempdirs.pop() + shutil.rmtree(d) + + def mkdtemp(self): + """Create a temporary directory that will be cleaned up. + + Returns the path of the directory. + """ + d = tempfile.mkdtemp() + self.tempdirs.append(d) + return d + + +class DummyCommand: + """Class to store options for retrieval via set_undefined_options().""" + + def __init__(self, **kwargs): + for kw, val in kwargs.items(): + setattr(self, kw, val) + + def ensure_finalized(self): + pass diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index 824f7336..0a11abf6 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -1,37 +1,21 @@ """Tests for distutils.command.install_scripts.""" import os -import shutil -import tempfile import unittest from distutils.command.install_scripts import install_scripts from distutils.core import Distribution +from distutils.tests import support -class InstallScriptsTestCase(unittest.TestCase): - def setUp(self): - self.tempdirs = [] - - def tearDown(self): - while self.tempdirs: - d = self.tempdirs.pop() - shutil.rmtree(d) - - def mkdtemp(self): - """Create a temporary directory that will be cleaned up. - - Returns the path of the directory. - """ - d = tempfile.mkdtemp() - self.tempdirs.append(d) - return d +class InstallScriptsTestCase(support.TempdirManager, unittest.TestCase): def test_default_settings(self): dist = Distribution() - dist.command_obj["build"] = DummyCommand(build_scripts="/foo/bar") - dist.command_obj["install"] = DummyCommand( + dist.command_obj["build"] = support.DummyCommand( + build_scripts="/foo/bar") + dist.command_obj["install"] = support.DummyCommand( install_scripts="/splat/funk", force=1, skip_build=1, @@ -71,8 +55,8 @@ class InstallScriptsTestCase(unittest.TestCase): target = self.mkdtemp() dist = Distribution() - dist.command_obj["build"] = DummyCommand(build_scripts=source) - dist.command_obj["install"] = DummyCommand( + dist.command_obj["build"] = support.DummyCommand(build_scripts=source) + dist.command_obj["install"] = support.DummyCommand( install_scripts=target, force=1, skip_build=1, @@ -86,17 +70,6 @@ class InstallScriptsTestCase(unittest.TestCase): self.assert_(name in installed) -class DummyCommand: - """Class to store options for retrieval via set_undefined_options().""" - - def __init__(self, **kwargs): - for kw, val in kwargs.items(): - setattr(self, kw, val) - - def ensure_finalized(self): - pass - - def test_suite(): return unittest.makeSuite(InstallScriptsTestCase) -- cgit v1.2.1 From 7e402b740c8846c7b50ce89e2ac4013f7048b35c Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 17 Jun 2004 20:16:19 +0000 Subject: fix bug: list of data files was initialized too soon in build_py --- command/build_py.py | 2 +- tests/test_build_py.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/test_build_py.py diff --git a/command/build_py.py b/command/build_py.py index 329b55a9..cfbab262 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -53,11 +53,11 @@ class build_py (Command): self.packages = self.distribution.packages self.py_modules = self.distribution.py_modules self.package_data = self.distribution.package_data - self.data_files = self.get_data_files() self.package_dir = {} if self.distribution.package_dir: for name, path in self.distribution.package_dir.items(): self.package_dir[name] = convert_path(path) + self.data_files = self.get_data_files() # Ick, copied straight from install_lib.py (fancy_getopt needs a # type system! Hell, *everything* needs a type system!!!) diff --git a/tests/test_build_py.py b/tests/test_build_py.py new file mode 100644 index 00000000..6cdce2c7 --- /dev/null +++ b/tests/test_build_py.py @@ -0,0 +1,50 @@ +"""Tests for distutils.command.build_py.""" + +import os +import unittest + +from distutils.command.build_py import build_py +from distutils.core import Distribution + +from distutils.tests import support + + +class BuildPyTestCase(support.TempdirManager, unittest.TestCase): + + def test_package_data(self): + sources = self.mkdtemp() + f = open(os.path.join(sources, "__init__.py"), "w") + f.write("# Pretend this is a package.") + f.close() + f = open(os.path.join(sources, "README.txt"), "w") + f.write("Info about this package") + f.close() + + destination = self.mkdtemp() + + dist = Distribution({"packages": ["pkg"], + "package_dir": {"pkg": sources}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.command_obj["build"] = support.DummyCommand( + force=0, + build_lib=destination) + dist.packages = ["pkg"] + dist.package_data = {"pkg": ["README.txt"]} + dist.package_dir = {"pkg": sources} + + cmd = build_py(dist) + cmd.ensure_finalized() + self.assertEqual(cmd.package_data, dist.package_data) + + cmd.run() + + self.assertEqual(len(cmd.get_outputs()), 2) + pkgdest = os.path.join(destination, "pkg") + files = os.listdir(pkgdest) + self.assert_("__init__.py" in files) + self.assert_("README.txt" in files) + + +def test_suite(): + return unittest.makeSuite(BuildPyTestCase) -- cgit v1.2.1 From 08d85503d984dae19579aaeeca76e8c4b5322d63 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 18 Jun 2004 18:30:27 +0000 Subject: Rebuild the wininst.exe files. --- command/wininst-6.exe | Bin 21504 -> 61440 bytes command/wininst-7.1.exe | Bin 22016 -> 61440 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-6.exe b/command/wininst-6.exe index c40cbcb5..8ab173b6 100644 Binary files a/command/wininst-6.exe and b/command/wininst-6.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 89a78885..858f6f22 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ -- cgit v1.2.1 From 302974208f3aa3d5b7922d68e8f9026e909e17cb Mon Sep 17 00:00:00 2001 From: Sjoerd Mullender Date: Fri, 18 Jun 2004 20:39:11 +0000 Subject: If self.packages is None (this can happen, I saw it), return immediately (since None is not a sequence you can iterate over). --- command/build_py.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/build_py.py b/command/build_py.py index cfbab262..3079bfcd 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -104,6 +104,8 @@ class build_py (Command): def get_data_files (self): """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" data = [] + if not self.packages: + return data for package in self.packages: # Locate package source directory src_dir = self.get_package_dir(package) -- cgit v1.2.1 From b48d7f0f6502934c6ee3cc6d3d2d2ec6a602c651 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 18 Jun 2004 21:28:28 +0000 Subject: fix typo --- cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd.py b/cmd.py index fef49390..93624104 100644 --- a/cmd.py +++ b/cmd.py @@ -68,7 +68,7 @@ class Command: # Per-command versions of the global flags, so that the user can # customize Distutils' behaviour command-by-command and let some - # commands fallback on the Distribution's behaviour. None means + # commands fall back on the Distribution's behaviour. None means # "not defined, check self.distribution's copy", while 0 or 1 mean # false and true (duh). Note that this means figuring out the real # value of each flag is a touch complicated -- hence "self._dry_run" -- cgit v1.2.1 From b20a6ccc211060644ff3e6f89428420fa59f5a5d Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Mon, 21 Jun 2004 16:15:22 +0000 Subject: add a couple of tests for the build_scripts command --- tests/test_build_scripts.py | 74 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tests/test_build_scripts.py diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py new file mode 100644 index 00000000..0cfef4e5 --- /dev/null +++ b/tests/test_build_scripts.py @@ -0,0 +1,74 @@ +"""Tests for distutils.command.build_scripts.""" + +import os +import unittest + +from distutils.command.build_scripts import build_scripts +from distutils.core import Distribution + +from distutils.tests import support + + +class BuildScriptsTestCase(support.TempdirManager, unittest.TestCase): + + def test_default_settings(self): + cmd = self.get_build_scripts_cmd("/foo/bar", []) + self.assert_(not cmd.force) + self.assert_(cmd.build_dir is None) + + cmd.finalize_options() + + self.assert_(cmd.force) + self.assertEqual(cmd.build_dir, "/foo/bar") + + def test_build(self): + source = self.mkdtemp() + target = self.mkdtemp() + expected = self.write_sample_scripts(source) + + cmd = self.get_build_scripts_cmd(target, + [os.path.join(source, fn) + for fn in expected]) + cmd.finalize_options() + cmd.run() + + built = os.listdir(target) + for name in expected: + self.assert_(name in built) + + def get_build_scripts_cmd(self, target, scripts): + dist = Distribution() + dist.scripts = scripts + dist.command_obj["build"] = support.DummyCommand( + build_scripts=target, + force=1 + ) + return build_scripts(dist) + + def write_sample_scripts(self, dir): + expected = [] + expected.append("script1.py") + self.write_script(dir, "script1.py", + ("#! /usr/bin/env python2.3\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + expected.append("script2.py") + self.write_script(dir, "script2.py", + ("#!/usr/bin/python\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + expected.append("shell.sh") + self.write_script(dir, "shell.sh", + ("#!/bin/sh\n" + "# bogus shell script w/ sh-bang\n" + "exit 0\n")) + return expected + + def write_script(self, dir, name, text): + f = open(os.path.join(dir, name), "w") + f.write(text) + f.close() + + +def test_suite(): + return unittest.makeSuite(BuildScriptsTestCase) -- cgit v1.2.1 From 08c14ecdbb73fbb9036506e74a2bdf742ee5b13a Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 25 Jun 2004 19:04:21 +0000 Subject: add boilerplate so the test modules can be run as scripts --- tests/test_build_py.py | 3 +++ tests/test_build_scripts.py | 3 +++ tests/test_install_scripts.py | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 6cdce2c7..757d757c 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -48,3 +48,6 @@ class BuildPyTestCase(support.TempdirManager, unittest.TestCase): def test_suite(): return unittest.makeSuite(BuildPyTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 0cfef4e5..7ef71cc1 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -72,3 +72,6 @@ class BuildScriptsTestCase(support.TempdirManager, unittest.TestCase): def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index 0a11abf6..2e86dcd8 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -70,6 +70,8 @@ class InstallScriptsTestCase(support.TempdirManager, unittest.TestCase): self.assert_(name in installed) - def test_suite(): return unittest.makeSuite(InstallScriptsTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 1bf1bfa6924e28ef13c43dd27d35ca94b988b36b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 25 Jun 2004 23:02:59 +0000 Subject: Make distutils "install --home" support all platforms. --- command/install.py | 34 +++++++++++++++---------------- tests/test_install.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 tests/test_install.py diff --git a/command/install.py b/command/install.py index 7fb46a74..3c36ede6 100644 --- a/command/install.py +++ b/command/install.py @@ -242,19 +242,15 @@ class install (Command): ("must supply either prefix/exec-prefix/home or " + "install-base/install-platbase -- not both") + if self.home and (self.prefix or self.exec_prefix): + raise DistutilsOptionError, \ + "must supply either home or prefix/exec-prefix -- not both" + # Next, stuff that's wrong (or dubious) only on certain platforms. - if os.name == 'posix': - if self.home and (self.prefix or self.exec_prefix): - raise DistutilsOptionError, \ - ("must supply either home or prefix/exec-prefix -- " + - "not both") - else: + if os.name != "posix": if self.exec_prefix: self.warn("exec-prefix option ignored on this platform") self.exec_prefix = None - if self.home: - self.warn("home option ignored on this platform") - self.home = None # Now the interesting logic -- so interesting that we farm it out # to other methods. The goal of these methods is to set the final @@ -405,15 +401,19 @@ class install (Command): def finalize_other (self): # Windows and Mac OS for now - if self.prefix is None: - self.prefix = os.path.normpath(sys.prefix) + if self.home is not None: + self.install_base = self.install_platbase = self.home + self.select_scheme("unix_home") + else: + if self.prefix is None: + self.prefix = os.path.normpath(sys.prefix) - self.install_base = self.install_platbase = self.prefix - try: - self.select_scheme(os.name) - except KeyError: - raise DistutilsPlatformError, \ - "I don't know how to install stuff on '%s'" % os.name + self.install_base = self.install_platbase = self.prefix + try: + self.select_scheme(os.name) + except KeyError: + raise DistutilsPlatformError, \ + "I don't know how to install stuff on '%s'" % os.name # finalize_other () diff --git a/tests/test_install.py b/tests/test_install.py new file mode 100644 index 00000000..c834b91b --- /dev/null +++ b/tests/test_install.py @@ -0,0 +1,55 @@ +"""Tests for distutils.command.install.""" + +import os +import unittest + +from distutils.command.install import install +from distutils.core import Distribution + +from distutils.tests import support + + +class InstallTestCase(support.TempdirManager, unittest.TestCase): + + def test_home_installation_scheme(self): + # This ensure two things: + # - that --home generates the desired set of directory names + # - test --home is supported on all platforms + builddir = self.mkdtemp() + destination = os.path.join(builddir, "installation") + + dist = Distribution({"name": "foopkg"}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(builddir, "setup.py") + dist.command_obj["build"] = support.DummyCommand( + build_base=builddir, + build_lib=os.path.join(builddir, "lib"), + ) + + cmd = install(dist) + cmd.home = destination + cmd.ensure_finalized() + + self.assertEqual(cmd.install_base, destination) + self.assertEqual(cmd.install_platbase, destination) + + def check_path(got, expected): + got = os.path.normpath(got) + expected = os.path.normpath(expected) + self.assertEqual(got, expected) + + libdir = os.path.join(destination, "lib", "python") + check_path(cmd.install_lib, libdir) + check_path(cmd.install_platlib, libdir) + check_path(cmd.install_purelib, libdir) + check_path(cmd.install_headers, + os.path.join(destination, "include", "python", "foopkg")) + check_path(cmd.install_scripts, os.path.join(destination, "bin")) + check_path(cmd.install_data, destination) + + +def test_suite(): + return unittest.makeSuite(InstallTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From a6850a7120f88d68c4e2f8413120dce52e8b19c5 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 2 Jul 2004 08:02:40 +0000 Subject: Fix for SF 982215: bdist_wininst - Next button not greyed out during file copy. Patch from Mark Hammond. Recompiled binary. Already packported to the 2.3 branch. --- command/wininst-6.exe | Bin 61440 -> 61440 bytes command/wininst-7.1.exe | Bin 61440 -> 61440 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-6.exe b/command/wininst-6.exe index 8ab173b6..efa3bc4f 100644 Binary files a/command/wininst-6.exe and b/command/wininst-6.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 858f6f22..7be9396b 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ -- cgit v1.2.1 From 84314dfe519cc3347826b21074d697949a3754e6 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 6 Jul 2004 19:23:27 +0000 Subject: Fix SF#983164. Patch from Mark Hammond: bdist_wininst attempts to use the correct MSVC runtime for the current version of Python. This doesn't work correctly when --target-version is set. In that case, bdist_wininst still uses the *current* sys.version (ie, 2.4) rather than the version specified as --target-version. Thus, the msvc7 runtime based executable stub is *always* used. This patch "hard-codes" knowledge of earlier Python versions, providing the correct result when Python 2.4 is used to build Python 2.3 and earlier distributions. Remove the short variant (-v) of the --target-version command line options, it conflicts with the --verbose/-v standard distutils switch. --- command/bdist_wininst.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 324ce31a..7c593adc 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -24,7 +24,7 @@ class bdist_wininst (Command): ('keep-temp', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), - ('target-version=', 'v', + ('target-version=', None, "require a specific python version" + " on the target system"), ('no-target-compile', 'c', @@ -265,10 +265,34 @@ class bdist_wininst (Command): def get_exe_bytes (self): from distutils.msvccompiler import get_build_version + # If a target-version other than the current version has been + # specified, then using the MSVC version from *this* build is no good. + # Without actually finding and executing the target version and parsing + # its sys.version, we just hard-code our knowledge of old versions. + # NOTE: Possible alternative is to allow "--target-version" to + # specify a Python executable rather than a simple version string. + # We can then execute this program to obtain any info we need, such + # as the real sys.version string for the build. + cur_version = get_python_version() + if self.target_version and self.target_version != cur_version: + # If the target version is *later* than us, then we assume they + # use what we use + # string compares seem wrong, but are what sysconfig.py itself uses + if self.target_version > cur_version: + bv = get_build_version() + else: + if self.target_version < "2.4": + bv = "6" + else: + bv = "7.1" + else: + # for current version - use authoritative check. + bv = get_build_version() + # wininst-x.y.exe is in the same directory as this file directory = os.path.dirname(__file__) # we must use a wininst-x.y.exe built with the same C compiler # used for python. XXX What about mingw, borland, and so on? - filename = os.path.join(directory, "wininst-%s.exe" % get_build_version()) + filename = os.path.join(directory, "wininst-%s.exe" % bv) return open(filename, "rb").read() # class bdist_wininst -- cgit v1.2.1 From 89f251ec91fbc1a66a06a5aa903f6faa2d44d047 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 14 Jul 2004 15:22:05 +0000 Subject: Recompiled after source file changes. --- command/wininst-6.exe | Bin 61440 -> 61440 bytes command/wininst-7.1.exe | Bin 61440 -> 61440 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-6.exe b/command/wininst-6.exe index efa3bc4f..7e93de88 100644 Binary files a/command/wininst-6.exe and b/command/wininst-6.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 7be9396b..40f80035 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ -- cgit v1.2.1 From 0180948beafd529bb6a0f19e6e7293dd5117aab7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 16 Jul 2004 18:14:37 +0000 Subject: The new distutils features justify a new version number, imo. If someone has other ideas for the numbering scheme, please change to something else (1.1.0 ?). --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7873d297..2e9c90c7 100644 --- a/__init__.py +++ b/__init__.py @@ -12,4 +12,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "1.0.3" +__version__ = "1.0.4" -- cgit v1.2.1 From 3d2ade66dfe5efa2a561cd804d43c35974ed210a Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 18 Jul 2004 06:16:08 +0000 Subject: Whitespace normalization, via reindent.py. --- archive_util.py | 4 ++-- bcppcompiler.py | 2 +- ccompiler.py | 4 ++-- cmd.py | 4 ++-- command/bdist.py | 2 +- command/bdist_dumb.py | 2 +- command/bdist_wininst.py | 2 +- command/build_ext.py | 4 ++-- command/build_scripts.py | 2 +- command/install.py | 2 +- command/register.py | 1 - core.py | 1 - cygwinccompiler.py | 2 +- debug.py | 1 - dir_util.py | 1 - dist.py | 4 ++-- file_util.py | 2 +- filelist.py | 2 +- log.py | 8 ++++---- msvccompiler.py | 7 +++---- spawn.py | 2 +- sysconfig.py | 2 +- unixccompiler.py | 6 +++--- util.py | 2 +- 24 files changed, 32 insertions(+), 37 deletions(-) diff --git a/archive_util.py b/archive_util.py index d5b30966..55babe6f 100644 --- a/archive_util.py +++ b/archive_util.py @@ -68,7 +68,7 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): import zipfile except ImportError: zipfile = None - + zip_filename = base_name + ".zip" mkpath(os.path.dirname(zip_filename), dry_run=dry_run) @@ -79,7 +79,7 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): zipoptions = "-r" else: zipoptions = "-rq" - + try: spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run) diff --git a/bcppcompiler.py b/bcppcompiler.py index b0360a24..f995e367 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -85,7 +85,7 @@ class BCPPCompiler(CCompiler) : def compile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): - + macros, objects, extra_postargs, pp_opts, build = \ self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) diff --git a/ccompiler.py b/ccompiler.py index ebd93c27..a3b1ffa4 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -685,7 +685,7 @@ class CCompiler: # A concrete compiler class can either override this method # entirely or implement _compile(). - + macros, objects, extra_postargs, pp_opts, build = \ self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) @@ -703,7 +703,7 @@ class CCompiler: def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): """Compile 'src' to product 'obj'.""" - + # A concrete compiler class that does not override compile() # should implement _compile(). pass diff --git a/cmd.py b/cmd.py index 93624104..be8e826f 100644 --- a/cmd.py +++ b/cmd.py @@ -79,7 +79,7 @@ class Command: # verbose is largely ignored, but needs to be set for # backwards compatibility (I think)? self.verbose = dist.verbose - + # Some commands define a 'self.force' option to ignore file # timestamps, but methods defined *here* assume that # 'self.force' exists for all commands. So define it here @@ -100,7 +100,7 @@ class Command: # XXX A more explicit way to customize dry_run would be better. - + def __getattr__ (self, attr): if attr == 'dry_run': myval = getattr(self, "_" + attr) diff --git a/command/bdist.py b/command/bdist.py index 7c606ffc..4eca9c34 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -78,7 +78,7 @@ class bdist (Command): 'wininst': ('bdist_wininst', "Windows executable installer"), 'zip': ('bdist_dumb', "ZIP file"), - #'pkgtool': ('bdist_pkgtool', + #'pkgtool': ('bdist_pkgtool', # "Solaris pkgtool distribution"), #'sdux': ('bdist_sdux', "HP-UX swinstall depot"), } diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 8ee3a5c5..3db332d5 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -53,7 +53,7 @@ class bdist_dumb (Command): self.dist_dir = None self.skip_build = 0 self.relative = 0 - + # initialize_options() diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 7c593adc..d91c0893 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -115,7 +115,7 @@ class bdist_wininst (Command): # we do not want to include pyc or pyo files install_lib.compile = 0 install_lib.optimize = 0 - + # If we are building an installer for a Python version other # than the one we are currently running, then we need to ensure # our build_lib reflects the other Python version rather than ours. diff --git a/command/build_ext.py b/command/build_ext.py index 0c377681..04cd742c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -173,7 +173,7 @@ class build_ext (Command): self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) - # OS/2 (EMX) doesn't support Debug vs Release builds, but has the + # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory if os.name == 'os2': self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) @@ -636,7 +636,7 @@ class build_ext (Command): # EMX/GCC requires the python library explicitly, and I # believe VACPP does as well (though not confirmed) - AIM Apr01 template = "python%d%d" - # debug versions of the main DLL aren't supported, at least + # debug versions of the main DLL aren't supported, at least # not at this time - AIM Apr01 #if self.debug: # template = template + '_d' diff --git a/command/build_scripts.py b/command/build_scripts.py index 165a009d..e0fcc23e 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -94,7 +94,7 @@ class build_scripts (Command): if not self.dry_run: outf = open(outfile, "w") if not sysconfig.python_build: - outf.write("#!%s%s\n" % + outf.write("#!%s%s\n" % (os.path.normpath(sys.executable), post_interp)) else: diff --git a/command/install.py b/command/install.py index 3c36ede6..175f7852 100644 --- a/command/install.py +++ b/command/install.py @@ -537,7 +537,7 @@ class install (Command): not (self.path_file and self.install_path_file) and install_lib not in sys_path): log.debug(("modules installed to '%s', which is not in " - "Python's module search path (sys.path) -- " + "Python's module search path (sys.path) -- " "you'll have to change the search path yourself"), self.install_lib) diff --git a/command/register.py b/command/register.py index 8e347ce6..8104ce06 100644 --- a/command/register.py +++ b/command/register.py @@ -286,4 +286,3 @@ Your selection [default 1]: ''', if self.show_response: print '-'*75, data, '-'*75 return result - diff --git a/core.py b/core.py index fba463c1..fef291f6 100644 --- a/core.py +++ b/core.py @@ -239,4 +239,3 @@ def run_setup (script_name, script_args=None, stop_after="run"): return _setup_distribution # run_setup () - diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 0101bae5..a9620076 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -75,7 +75,7 @@ class CygwinCCompiler (UnixCCompiler): (status, details)) if status is not CONFIG_H_OK: self.warn( - "Python's pyconfig.h doesn't seem to support your compiler. " + "Python's pyconfig.h doesn't seem to support your compiler. " "Reason: %s. " "Compiling may fail because of undefined preprocessor macros." % details) diff --git a/debug.py b/debug.py index e195ebdc..2a87eb5d 100644 --- a/debug.py +++ b/debug.py @@ -7,4 +7,3 @@ __revision__ = "$Id$" # If DISTUTILS_DEBUG is anything other than the empty string, we run in # debug mode. DEBUG = os.environ.get('DISTUTILS_DEBUG') - diff --git a/dir_util.py b/dir_util.py index e479b624..77f64c41 100644 --- a/dir_util.py +++ b/dir_util.py @@ -225,4 +225,3 @@ def ensure_relative (path): if path[0:1] == os.sep: path = drive + path[1:] return path - diff --git a/dist.py b/dist.py index 7d0a7bad..b4dd0b91 100644 --- a/dist.py +++ b/dist.py @@ -231,7 +231,7 @@ class Distribution: newreq.append((pkg, LooseVersion(ver))) attrs['requires'] = newreq - # Build up the provides object. If the setup() has no + # Build up the provides object. If the setup() has no # provides line, we use packages or modules and the version # to synthesise the provides. If no version is provided (no # pun intended) we don't have a provides entry at all. @@ -1137,7 +1137,7 @@ class DistributionMetadata: return self.download_url or "UNKNOWN" def get_requires(self): - return [ '%s%s%s'%(x, (y and '-') or '', y or '') + return [ '%s%s%s'%(x, (y and '-') or '', y or '') for x,y in self.requires ] def get_provides(self): diff --git a/file_util.py b/file_util.py index e230ce58..e2b1c515 100644 --- a/file_util.py +++ b/file_util.py @@ -42,7 +42,7 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): except os.error, (errno, errstr): raise DistutilsFileError, \ "could not delete '%s': %s" % (dst, errstr) - + try: fdst = open(dst, 'wb') except os.error, (errno, errstr): diff --git a/filelist.py b/filelist.py index 0872e966..566d87ab 100644 --- a/filelist.py +++ b/filelist.py @@ -167,7 +167,7 @@ class FileList: for pattern in patterns: if not self.include_pattern(pattern, prefix=dir): log.warn(("warning: no files found matching '%s' " + - "under directory '%s'"), + "under directory '%s'"), pattern, dir) elif action == 'recursive-exclude': diff --git a/log.py b/log.py index 01420da9..024e7c28 100644 --- a/log.py +++ b/log.py @@ -28,16 +28,16 @@ class Log: def debug(self, msg, *args): self._log(DEBUG, msg, args) - + def info(self, msg, *args): self._log(INFO, msg, args) - + def warn(self, msg, *args): self._log(WARN, msg, args) - + def error(self, msg, *args): self._log(ERROR, msg, args) - + def fatal(self, msg, *args): self._log(FATAL, msg, args) diff --git a/msvccompiler.py b/msvccompiler.py index 1441ea04..168881ad 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -117,7 +117,7 @@ class MacroExpander: if d: self.macros["$(%s)" % macro] = d[key] break - + def load_macros(self, version): vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") @@ -166,7 +166,7 @@ def get_build_version(): return majorVersion + minorVersion # else we don't know what version of the compiler this is return None - + class MSVCCompiler (CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, @@ -525,7 +525,7 @@ class MSVCCompiler (CCompiler) : return fn return exe - + def get_msvc_paths(self, path, platform='x86'): """Get a list of devstudio directories (include, lib or path). @@ -576,4 +576,3 @@ class MSVCCompiler (CCompiler) : p = self.get_msvc_paths(name) if p: os.environ[name] = string.join(p, ';') - diff --git a/spawn.py b/spawn.py index 67391e88..a89b88ce 100644 --- a/spawn.py +++ b/spawn.py @@ -97,7 +97,7 @@ def _spawn_os2 (cmd, #cmd = _nt_quote_args(cmd) if search_path: # either we find one or it stays the same - executable = find_executable(executable) or executable + executable = find_executable(executable) or executable log.info(string.join([executable] + cmd[1:], ' ')) if not dry_run: # spawnv for OS/2 EMX requires a full path to the .exe diff --git a/sysconfig.py b/sysconfig.py index c912a15c..0a4e14cc 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -367,7 +367,7 @@ def _init_posix(): my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' % (cur_target, cfg_target)) raise DistutilsPlatformError(my_msg) - + # On AIX, there are wrong paths to the linker scripts in the Makefile # -- these paths are relative to the Python source, but when installed # the scripts are in another directory. diff --git a/unixccompiler.py b/unixccompiler.py index 11ecb9f6..24cbcb53 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -206,7 +206,7 @@ class UnixCCompiler(CCompiler): elif sys.platform[:5] == "hp-ux": return "+s -L" + dir elif compiler[:3] == "gcc" or compiler[:3] == "g++": - return "-Wl,-R" + dir + return "-Wl,-R" + dir else: return "-R" + dir @@ -217,7 +217,7 @@ class UnixCCompiler(CCompiler): shared_f = self.library_filename(lib, lib_type='shared') dylib_f = self.library_filename(lib, lib_type='dylib') static_f = self.library_filename(lib, lib_type='static') - + for dir in dirs: shared = os.path.join(dir, shared_f) dylib = os.path.join(dir, dylib_f) @@ -232,6 +232,6 @@ class UnixCCompiler(CCompiler): return shared elif os.path.exists(static): return static - + # Oops, didn't find it in *any* of 'dirs' return None diff --git a/util.py b/util.py index 12775d6f..387e9bdc 100644 --- a/util.py +++ b/util.py @@ -300,7 +300,7 @@ def execute (func, args, msg=None, verbose=0, dry_run=0): def strtobool (val): """Convert a string representation of truth to true (1) or false (0). - + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 'val' is anything else. -- cgit v1.2.1 From eb85239d5cdcf684b6f19ef7cce2cc969accdf6c Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 19 Jul 2004 09:45:46 +0000 Subject: The binary layout of cfgdata has changed, so the magic number has to change as well. Add a comment explaining this. --- command/bdist_wininst.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index d91c0893..79c95ae2 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -253,8 +253,14 @@ class bdist_wininst (Command): # empty pre-install script cfgdata = cfgdata + "\0" file.write(cfgdata) + + # The 'magic number' 0x1234567B is used to make sure that the + # binary layout of 'cfgdata' is what the wininst.exe binary + # expects. If the layout changes, increment that number, make + # the corresponding changes to the wininst.exe sources, and + # recompile them. header = struct.pack(" Date: Mon, 19 Jul 2004 10:07:28 +0000 Subject: The binary layout of cfgdata has changed, so the magic number has to change as well. Recompiled binaries after this change. --- command/wininst-6.exe | Bin 61440 -> 61440 bytes command/wininst-7.1.exe | Bin 61440 -> 61440 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-6.exe b/command/wininst-6.exe index 7e93de88..ed288d6a 100644 Binary files a/command/wininst-6.exe and b/command/wininst-6.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 40f80035..a800678e 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ -- cgit v1.2.1 From ccb3f9c92225e9af8441defe2003ddf96d2b9cd2 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Wed, 21 Jul 2004 18:53:06 +0000 Subject: elaborate package data test to make sure get_outputs() gives the right results when byte-code compilation is requested (in particular, make sure that package data doesn't get a bogus byte-code listing generated) --- tests/test_build_py.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 757d757c..6f727686 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -34,15 +34,21 @@ class BuildPyTestCase(support.TempdirManager, unittest.TestCase): dist.package_dir = {"pkg": sources} cmd = build_py(dist) + cmd.compile = 1 cmd.ensure_finalized() self.assertEqual(cmd.package_data, dist.package_data) cmd.run() - self.assertEqual(len(cmd.get_outputs()), 2) + # This makes sure the list of outputs includes byte-compiled + # files for Python modules but not for package data files + # (there shouldn't *be* byte-code files for those!). + # + self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) self.assert_("__init__.py" in files) + self.assert_("__init__.pyc" in files) self.assert_("README.txt" in files) -- cgit v1.2.1 From c8ecef628841d8f77400d5227236538dd227eab1 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 23 Jul 2004 19:44:29 +0000 Subject: bdist_wininst does now properly handle unicode strings or byte strings with umlauts in the author argument and others. Fixes sf # 993943. --- command/bdist_wininst.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 79c95ae2..506a4a26 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -176,36 +176,38 @@ class bdist_wininst (Command): lines = [] metadata = self.distribution.metadata - # Write the [metadata] section. Values are written with - # repr()[1:-1], so they do not contain unprintable characters, and - # are not surrounded by quote chars. + # Write the [metadata] section. lines.append("[metadata]") # 'info' will be displayed in the installer's dialog box, # describing the items to be installed. info = (metadata.long_description or '') + '\n' + # Escape newline characters + def escape(s): + return string.replace(s, "\n", "\\n") + for name in ["author", "author_email", "description", "maintainer", "maintainer_email", "name", "url", "version"]: data = getattr(metadata, name, "") if data: info = info + ("\n %s: %s" % \ - (string.capitalize(name), data)) - lines.append("%s=%s" % (name, repr(data)[1:-1])) + (string.capitalize(name), escape(data))) + lines.append("%s=%s" % (name, escape(data))) # The [setup] section contains entries controlling # the installer runtime. lines.append("\n[Setup]") if self.install_script: lines.append("install_script=%s" % self.install_script) - lines.append("info=%s" % repr(info)[1:-1]) + lines.append("info=%s" % escape(info)) lines.append("target_compile=%d" % (not self.no_target_compile)) lines.append("target_optimize=%d" % (not self.no_target_optimize)) if self.target_version: lines.append("target_version=%s" % self.target_version) title = self.title or self.distribution.get_fullname() - lines.append("title=%s" % repr(title)[1:-1]) + lines.append("title=%s" % escape(title)) import time import distutils build_info = "Built %s with distutils-%s" % \ @@ -244,6 +246,15 @@ class bdist_wininst (Command): if bitmap: file.write(bitmapdata) + # Convert cfgdata from unicode to ascii, mbcs encoded + try: + unicode + except NameError: + pass + else: + if isinstance(cfgdata, unicode): + cfgdata = cfgdata.encode("mbcs") + # Append the pre-install script cfgdata = cfgdata + "\0" if self.pre_install_script: -- cgit v1.2.1 From 255d561a68712ed1f40f673cbb1c428815a5febd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 23 Jul 2004 19:47:32 +0000 Subject: Make the distutils version number the same as the python version. It must be literally contained here, because it is still possible to install this distutils in older Python versions. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2e9c90c7..2592a6c5 100644 --- a/__init__.py +++ b/__init__.py @@ -12,4 +12,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "1.0.4" +__version__ = "2.4.0" -- cgit v1.2.1 From c1e60ed922d5a869202a3aea3f5c9e7587d063f1 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 23 Jul 2004 19:58:28 +0000 Subject: Factored out a method to determine the final installer filename. --- command/bdist_wininst.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 506a4a26..20bd6138 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -224,15 +224,7 @@ class bdist_wininst (Command): cfgdata = self.get_inidata() - if self.target_version: - # if we create an installer for a specific python version, - # it's better to include this in the name - installer_name = os.path.join(self.dist_dir, - "%s.win32-py%s.exe" % - (fullname, self.target_version)) - else: - installer_name = os.path.join(self.dist_dir, - "%s.win32.exe" % fullname) + installer_name = self.get_installer_filename(fullname) self.announce("creating %s" % installer_name) if bitmap: @@ -280,6 +272,19 @@ class bdist_wininst (Command): # create_exe() + def get_installer_filename(self, fullname): + # Factored out to allow overriding in subclasses + if self.target_version: + # if we create an installer for a specific python version, + # it's better to include this in the name + installer_name = os.path.join(self.dist_dir, + "%s.win32-py%s.exe" % + (fullname, self.target_version)) + else: + installer_name = os.path.join(self.dist_dir, + "%s.win32.exe" % fullname) + # get_installer_filename() + def get_exe_bytes (self): from distutils.msvccompiler import get_build_version # If a target-version other than the current version has been -- cgit v1.2.1 From 372525bfe68c909c7d7a8b5f92bdfc31ef361149 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Wed, 28 Jul 2004 14:55:10 +0000 Subject: Since build_py handles package data installation, the list of outputs can contain more than just .py files. Make sure we only report bytecode files for the .py files. --- command/install_lib.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/command/install_lib.py b/command/install_lib.py index daf3e010..c234117a 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -7,6 +7,11 @@ from types import IntType from distutils.core import Command from distutils.errors import DistutilsOptionError + +# Extension for Python source files. +PYTHON_SOURCE_EXTENSION = os.extsep + "py" + + class install_lib (Command): description = "install all Python modules (extensions and pure Python)" @@ -155,6 +160,12 @@ class install_lib (Command): def _bytecode_filenames (self, py_filenames): bytecode_files = [] for py_file in py_filenames: + # Since build_py handles package data installation, the + # list of outputs can contain more than just .py files. + # Make sure we only report bytecode for the .py files. + ext = os.path.splitext(os.path.normcase(py_file))[1] + if ext != PYTHON_SOURCE_EXTENSION: + continue if self.compile: bytecode_files.append(py_file + "c") if self.optimize > 0: -- cgit v1.2.1 From ab63210e40d4c242c91f03b4ae363e2cdf7a6341 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Mon, 2 Aug 2004 17:58:51 +0000 Subject: - fix description of option table entries - fix broken assert statement; should just raise --- fancy_getopt.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 512bc9b6..6c1134fc 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -45,8 +45,9 @@ class FancyGetopt: def __init__ (self, option_table=None): - # The option table is (currently) a list of 3-tuples: - # (long_option, short_option, help_string) + # The option table is (currently) a list of tuples. The + # tuples may have 3 or four values: + # (long_option, short_option, help_string [, repeatable]) # if an option takes an argument, its long_option should have '=' # appended; short_option should just be a single character, no ':' # in any case. If a long_option doesn't have a corresponding @@ -162,7 +163,7 @@ class FancyGetopt: else: # the option table is part of the code, so simply # assert that it is correct - assert "invalid option tuple: %r" % (option,) + raise ValueError, "invalid option tuple: %r" % (option,) # Type- and value-check the option names if type(long) is not StringType or len(long) < 2: -- cgit v1.2.1 From ab4c054599bc4bcce9deb27b543b6b5465667a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 3 Aug 2004 12:41:42 +0000 Subject: Patch #870382: Automatically add msvcr71 to the list of libraries if Python was built with VC 7.1. --- cygwinccompiler.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index a9620076..acd393d5 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -122,6 +122,17 @@ class CygwinCCompiler (UnixCCompiler): "Consider upgrading to a newer version of gcc") else: self.dll_libraries=[] + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or 7.1. + msc_pos = sys.version.find('MSC v.') + if msc_pos != -1: + msc_ver = sys.version[msc_pos+6:msc_pos+10] + if msc_ver == '1300': + # MSVC 7.0 + self.dll_libraries = ['msvcr70'] + elif msc_ver == '1310': + # MSVC 7.1 + self.dll_libraries = ['msvcr71'] # __init__ () @@ -308,6 +319,18 @@ class Mingw32CCompiler (CygwinCCompiler): # no additional libraries needed self.dll_libraries=[] + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or 7.1. + msc_pos = sys.version.find('MSC v.') + if msc_pos != -1: + msc_ver = sys.version[msc_pos+6:msc_pos+10] + if msc_ver == '1300': + # MSVC 7.0 + self.dll_libraries = ['msvcr70'] + elif msc_ver == '1310': + # MSVC 7.1 + self.dll_libraries = ['msvcr71'] + # __init__ () # class Mingw32CCompiler -- cgit v1.2.1 From 1e8e19184f43761f8645b35e3ceb1bc95edde5b5 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 3 Aug 2004 16:37:40 +0000 Subject: This allows additional commands to be provided for existing setup.py scripts without modifying either the distutils installation or the setup.py scripts of packages with which the new commands will be used. Specifically, an option is added to distutils that allows additional packages to be searched for command implementations in addition to distutils.command. The additional packages can be specified on the command line or via the installation or personal configuration files already loaded by distutils. For discussion, see the thread starting with: http://mail.python.org/pipermail/distutils-sig/2004-August/004112.html This closes SF patch #102241. --- dist.py | 84 ++++++++++++++++++++++++++++++++------------ tests/test_dist.py | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 22 deletions(-) create mode 100644 tests/test_dist.py diff --git a/dist.py b/dist.py index b4dd0b91..53846e93 100644 --- a/dist.py +++ b/dist.py @@ -141,6 +141,14 @@ class Distribution: # for the setup script to override command classes self.cmdclass = {} + # 'command_packages' is a list of packages in which commands + # are searched for. The factory for command 'foo' is expected + # to be named 'foo' in the module 'foo' in one of the packages + # named here. This list is searched from the left; an error + # is raised if no named package provides the command being + # searched for. (Always access using get_command_packages().) + self.command_packages = None + # 'script_name' and 'script_args' are usually set to sys.argv[0] # and sys.argv[1:], but they can be overridden when the caller is # not necessarily a setup script run from the command-line. @@ -406,6 +414,8 @@ class Distribution: setattr(self, alias, not strtobool(val)) elif opt in ('verbose', 'dry_run'): # ugh! setattr(self, opt, strtobool(val)) + else: + setattr(self, opt, val) except ValueError, msg: raise DistutilsOptionError, msg @@ -437,11 +447,12 @@ class Distribution: # We now have enough information to show the Macintosh dialog # that allows the user to interactively specify the "command line". # + toplevel_options = self._get_toplevel_options() if sys.platform == 'mac': import EasyDialogs cmdlist = self.get_command_list() self.script_args = EasyDialogs.GetArgv( - self.global_options + self.display_options, cmdlist) + toplevel_options + self.display_options, cmdlist) # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- @@ -451,7 +462,7 @@ class Distribution: # until we know what the command is. self.commands = [] - parser = FancyGetopt(self.global_options + self.display_options) + parser = FancyGetopt(toplevel_options + self.display_options) parser.set_negative_aliases(self.negative_opt) parser.set_aliases({'licence': 'license'}) args = parser.getopt(args=self.script_args, object=self) @@ -488,6 +499,17 @@ class Distribution: # parse_command_line() + def _get_toplevel_options (self): + """Return the non-display options recognized at the top level. + + This includes options that are recognized *only* at the top + level as well as options recognized for commands. + """ + return self.global_options + [ + ("command-packages=", None, + "list of packages that provide distutils commands"), + ] + def _parse_command_opts (self, parser, args): """Parse the command-line options for a single command. 'parser' must be a FancyGetopt instance; 'args' must be the list @@ -586,7 +608,6 @@ class Distribution: # _parse_command_opts () - def finalize_options (self): """Set final values for all the options on the Distribution instance, analogous to the .finalize_options() method of Command @@ -627,7 +648,11 @@ class Distribution: from distutils.cmd import Command if global_options: - parser.set_option_table(self.global_options) + if display_options: + options = self._get_toplevel_options() + else: + options = self.global_options + parser.set_option_table(options) parser.print_help("Global options:") print @@ -791,6 +816,19 @@ class Distribution: # -- Command class/object methods ---------------------------------- + def get_command_packages (self): + """Return a list of packages from which commands are loaded.""" + pkgs = self.command_packages + if not isinstance(pkgs, type([])): + pkgs = string.split(pkgs or "", ",") + for i in range(len(pkgs)): + pkgs[i] = string.strip(pkgs[i]) + pkgs = filter(None, pkgs) + if "distutils.command" not in pkgs: + pkgs.insert(0, "distutils.command") + self.command_packages = pkgs + return pkgs + def get_command_class (self, command): """Return the class that implements the Distutils command named by 'command'. First we check the 'cmdclass' dictionary; if the @@ -807,26 +845,28 @@ class Distribution: if klass: return klass - module_name = 'distutils.command.' + command - klass_name = command + for pkgname in self.get_command_packages(): + module_name = "%s.%s" % (pkgname, command) + klass_name = command - try: - __import__ (module_name) - module = sys.modules[module_name] - except ImportError: - raise DistutilsModuleError, \ - "invalid command '%s' (no module named '%s')" % \ - (command, module_name) + try: + __import__ (module_name) + module = sys.modules[module_name] + except ImportError: + continue + + try: + klass = getattr(module, klass_name) + except AttributeError: + raise DistutilsModuleError, \ + "invalid command '%s' (no class '%s' in module '%s')" \ + % (command, klass_name, module_name) + + self.cmdclass[command] = klass + return klass + + raise DistutilsModuleError("invalid command '%s'" % command) - try: - klass = getattr(module, klass_name) - except AttributeError: - raise DistutilsModuleError, \ - "invalid command '%s' (no class '%s' in module '%s')" \ - % (command, klass_name, module_name) - - self.cmdclass[command] = klass - return klass # get_command_class () diff --git a/tests/test_dist.py b/tests/test_dist.py new file mode 100644 index 00000000..695f6d81 --- /dev/null +++ b/tests/test_dist.py @@ -0,0 +1,100 @@ +"""Tests for distutils.dist.""" + +import distutils.cmd +import distutils.dist +import os +import shutil +import sys +import tempfile +import unittest + +from test.test_support import TESTFN + + +class test_dist(distutils.cmd.Command): + """Sample distutils extension command.""" + + user_options = [ + ("sample-option=", "S", "help text"), + ] + + def initialize_options(self): + self.sample_option = None + + +class TestDistribution(distutils.dist.Distribution): + """Distribution subclasses that avoids the default search for + configuration files. + + The ._config_files attribute must be set before + .parse_config_files() is called. + """ + + def find_config_files(self): + return self._config_files + + +class DistributionTestCase(unittest.TestCase): + + def setUp(self): + self.argv = sys.argv[:] + del sys.argv[1:] + + def tearDown(self): + sys.argv[:] = self.argv + + def create_distribution(self, configfiles=()): + d = TestDistribution() + d._config_files = configfiles + d.parse_config_files() + d.parse_command_line() + return d + + def test_command_packages_unspecified(self): + sys.argv.append("build") + d = self.create_distribution() + self.assertEqual(d.get_command_packages(), ["distutils.command"]) + + def test_command_packages_cmdline(self): + sys.argv.extend(["--command-packages", + "foo.bar,distutils.tests", + "test_dist", + "-Ssometext", + ]) + d = self.create_distribution() + # let's actually try to load our test command: + self.assertEqual(d.get_command_packages(), + ["distutils.command", "foo.bar", "distutils.tests"]) + cmd = d.get_command_obj("test_dist") + self.assert_(isinstance(cmd, test_dist)) + self.assertEqual(cmd.sample_option, "sometext") + + def test_command_packages_configfile(self): + sys.argv.append("build") + f = open(TESTFN, "w") + try: + print >>f, "[global]" + print >>f, "command_packages = foo.bar, splat" + f.close() + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "foo.bar", "splat"]) + + # ensure command line overrides config: + sys.argv[1:] = ["--command-packages", "spork", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "spork"]) + + # Setting --command-packages to '' should cause the default to + # be used even if a config file specified something else: + sys.argv[1:] = ["--command-packages", "", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), ["distutils.command"]) + + finally: + os.unlink(TESTFN) + + +def test_suite(): + return unittest.makeSuite(DistributionTestCase) -- cgit v1.2.1 From 5f46c973a65daae1e9351c98edd0c4d7e531526b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 3 Aug 2004 18:53:07 +0000 Subject: make sure distutils logging is shut off in tests to avoid spurious output --- log.py | 3 +++ tests/support.py | 13 +++++++++++++ tests/test_build_py.py | 4 +++- tests/test_build_scripts.py | 4 +++- tests/test_install_scripts.py | 4 +++- 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/log.py b/log.py index 024e7c28..bf26302f 100644 --- a/log.py +++ b/log.py @@ -50,7 +50,10 @@ error = _global_log.error fatal = _global_log.fatal def set_threshold(level): + # return the old threshold for use from tests + old = _global_log.threshold _global_log.threshold = level + return old def set_verbosity(v): if v <= 0: diff --git a/tests/support.py b/tests/support.py index cef985d7..475ceee5 100644 --- a/tests/support.py +++ b/tests/support.py @@ -3,6 +3,19 @@ import shutil import tempfile +from distutils import log + + +class LoggingSilencer(object): + + def setUp(self): + super(LoggingSilencer, self).setUp() + self.threshold = log.set_threshold(log.FATAL) + + def tearDown(self): + log.set_threshold(self.threshold) + super(LoggingSilencer, self).tearDown() + class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 6f727686..78e4c55e 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -9,7 +9,9 @@ from distutils.core import Distribution from distutils.tests import support -class BuildPyTestCase(support.TempdirManager, unittest.TestCase): +class BuildPyTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def test_package_data(self): sources = self.mkdtemp() diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 7ef71cc1..bf25b382 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -9,7 +9,9 @@ from distutils.core import Distribution from distutils.tests import support -class BuildScriptsTestCase(support.TempdirManager, unittest.TestCase): +class BuildScriptsTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def test_default_settings(self): cmd = self.get_build_scripts_cmd("/foo/bar", []) diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index 2e86dcd8..fffa6ef2 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -9,7 +9,9 @@ from distutils.core import Distribution from distutils.tests import support -class InstallScriptsTestCase(support.TempdirManager, unittest.TestCase): +class InstallScriptsTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def test_default_settings(self): dist = Distribution() -- cgit v1.2.1 From 4e7fa463934326cdeebc21acd417260cb1dd5da2 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Wed, 4 Aug 2004 02:36:18 +0000 Subject: Whitespace normalization. --- cygwinccompiler.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index acd393d5..31e8e349 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -122,17 +122,17 @@ class CygwinCCompiler (UnixCCompiler): "Consider upgrading to a newer version of gcc") else: self.dll_libraries=[] - # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or 7.1. - msc_pos = sys.version.find('MSC v.') - if msc_pos != -1: - msc_ver = sys.version[msc_pos+6:msc_pos+10] - if msc_ver == '1300': - # MSVC 7.0 - self.dll_libraries = ['msvcr70'] - elif msc_ver == '1310': - # MSVC 7.1 - self.dll_libraries = ['msvcr71'] + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or 7.1. + msc_pos = sys.version.find('MSC v.') + if msc_pos != -1: + msc_ver = sys.version[msc_pos+6:msc_pos+10] + if msc_ver == '1300': + # MSVC 7.0 + self.dll_libraries = ['msvcr70'] + elif msc_ver == '1310': + # MSVC 7.1 + self.dll_libraries = ['msvcr71'] # __init__ () @@ -319,17 +319,17 @@ class Mingw32CCompiler (CygwinCCompiler): # no additional libraries needed self.dll_libraries=[] - # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or 7.1. - msc_pos = sys.version.find('MSC v.') - if msc_pos != -1: - msc_ver = sys.version[msc_pos+6:msc_pos+10] - if msc_ver == '1300': - # MSVC 7.0 - self.dll_libraries = ['msvcr70'] - elif msc_ver == '1310': - # MSVC 7.1 - self.dll_libraries = ['msvcr71'] + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or 7.1. + msc_pos = sys.version.find('MSC v.') + if msc_pos != -1: + msc_ver = sys.version[msc_pos+6:msc_pos+10] + if msc_ver == '1300': + # MSVC 7.0 + self.dll_libraries = ['msvcr70'] + elif msc_ver == '1310': + # MSVC 7.1 + self.dll_libraries = ['msvcr71'] # __init__ () -- cgit v1.2.1 From bf70c77f2d4b915ed174f2484197be059d589916 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 17 Aug 2004 10:15:07 +0000 Subject: The get_installer_filename() method forgot to return the name it calculates. Spotted by Cort Danger Stratton. --- command/bdist_wininst.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 20bd6138..33e15561 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -283,6 +283,7 @@ class bdist_wininst (Command): else: installer_name = os.path.join(self.dist_dir, "%s.win32.exe" % fullname) + return installer_name # get_installer_filename() def get_exe_bytes (self): -- cgit v1.2.1 From 85a862fc5645a070fa1b62884c76210384376912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 25 Aug 2004 11:37:43 +0000 Subject: Patch #736857, #736859: Add -e option to build_scripts. --- command/build.py | 5 +++++ command/build_scripts.py | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/command/build.py b/command/build.py index 78231541..e6b3991f 100644 --- a/command/build.py +++ b/command/build.py @@ -40,6 +40,8 @@ class build (Command): "compile extensions and libraries with debugging information"), ('force', 'f', "forcibly build everything (ignore file timestamps)"), + ('executable=', 'e', + "specify final destination interpreter path (build.py)"), ] boolean_options = ['debug', 'force'] @@ -61,6 +63,7 @@ class build (Command): self.compiler = None self.debug = None self.force = 0 + self.executable = None def finalize_options (self): @@ -93,6 +96,8 @@ class build (Command): self.build_scripts = os.path.join(self.build_base, 'scripts-' + sys.version[0:3]) + if self.executable is None: + self.executable = os.path.normpath(sys.executable) # finalize_options () diff --git a/command/build_scripts.py b/command/build_scripts.py index e0fcc23e..fb73719f 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -24,6 +24,7 @@ class build_scripts (Command): user_options = [ ('build-dir=', 'd', "directory to \"build\" (copy) to"), ('force', 'f', "forcibly build everything (ignore file timestamps"), + ('executable=', 'e', "specify final destination interpreter path"), ] boolean_options = ['force'] @@ -33,12 +34,14 @@ class build_scripts (Command): self.build_dir = None self.scripts = None self.force = None + self.executable = None self.outfiles = None def finalize_options (self): self.set_undefined_options('build', ('build_scripts', 'build_dir'), - ('force', 'force')) + ('force', 'force'), + ('executable', 'executable')) self.scripts = self.distribution.scripts def get_source_files(self): @@ -95,7 +98,7 @@ class build_scripts (Command): outf = open(outfile, "w") if not sysconfig.python_build: outf.write("#!%s%s\n" % - (os.path.normpath(sys.executable), + (self.executable, post_interp)) else: outf.write("#!%s%s\n" % -- cgit v1.2.1 From 5fb221df11270747861f6bbc2dd35e88365935f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 25 Aug 2004 13:00:34 +0000 Subject: Patch #970019: Include version and release in the BuildRoot. --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 4be99994..43181ea0 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -361,7 +361,7 @@ class bdist_rpm (Command): spec_file.extend([ 'License: ' + self.distribution.get_license(), 'Group: ' + self.group, - 'BuildRoot: %{_tmppath}/%{name}-buildroot', + 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', 'Prefix: %{_prefix}', ]) # noarch if no extension modules -- cgit v1.2.1 From e640606ebb5a80db67d17d9e2ec8f82e901fff61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 25 Aug 2004 13:04:53 +0000 Subject: Patch #970015: Replace - by _ in version and release. --- command/bdist_rpm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 43181ea0..5c8a7570 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -332,8 +332,8 @@ class bdist_rpm (Command): # definitions and headers spec_file = [ '%define name ' + self.distribution.get_name(), - '%define version ' + self.distribution.get_version(), - '%define release ' + self.release, + '%define version ' + self.distribution.get_version().replace('-','_'), + '%define release ' + self.release.replace('-','_'), '', 'Summary: ' + self.distribution.get_description(), ] -- cgit v1.2.1 From 78bb6f689a29017a19b4197024fe1ec78031063b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 26 Aug 2004 05:44:02 +0000 Subject: Add missing executable option to DummyCommand. --- tests/test_build_scripts.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index bf25b382..666ca44c 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -39,11 +39,13 @@ class BuildScriptsTestCase(support.TempdirManager, self.assert_(name in built) def get_build_scripts_cmd(self, target, scripts): + import sys dist = Distribution() dist.scripts = scripts dist.command_obj["build"] = support.DummyCommand( build_scripts=target, - force=1 + force=1, + executable=sys.executable ) return build_scripts(dist) -- cgit v1.2.1 From 87a4bf20711940df17932f0c16c05785da706480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 29 Aug 2004 16:40:55 +0000 Subject: Patch #973204: Use -rpath instead of -R on Irix and Tru64. --- ccompiler.py | 6 +++++- unixccompiler.py | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index a3b1ffa4..e5b9d7cc 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1241,7 +1241,11 @@ def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): lib_opts.append (compiler.library_dir_option (dir)) for dir in runtime_library_dirs: - lib_opts.append (compiler.runtime_library_dir_option (dir)) + opt = compiler.runtime_library_dir_option (dir) + if type(opt) is ListType: + lib_opts = lib_opts + opt + else: + lib_opts.append (opt) # XXX it's important that we *not* remove redundant library mentions! # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to diff --git a/unixccompiler.py b/unixccompiler.py index 24cbcb53..56998c35 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -205,6 +205,8 @@ class UnixCCompiler(CCompiler): return "-L" + dir elif sys.platform[:5] == "hp-ux": return "+s -L" + dir + elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": + return ["-rpath", dir] elif compiler[:3] == "gcc" or compiler[:3] == "g++": return "-Wl,-R" + dir else: -- cgit v1.2.1 From 99846e7ee4b5ad74bdbdcc4428b22f51e8a6a082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Fri, 10 Sep 2004 06:25:01 +0000 Subject: Patch #808115: Add script support to bdist_rpm.py. --- command/bdist_rpm.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 5c8a7570..11fd9f18 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -95,6 +95,31 @@ class bdist_rpm (Command): "RPM 3 compatibility mode (default)"), ('rpm2-mode', None, "RPM 2 compatibility mode"), + + # Add the hooks necessary for specifying custom scripts + ('prep-script=', None, + "Specify a script for the PREP phase of RPM building"), + ('build-script=', None, + "Specify a script for the BUILD phase of RPM building"), + + ('pre-install=', None, + "Specify a script for the pre-INSTALL phase of RPM building"), + ('install-script=', None, + "Specify a script for the INSTALL phase of RPM building"), + ('post-install=', None, + "Specify a script for the post-INSTALL phase of RPM building"), + + ('pre-uninstall=', None, + "Specify a script for the pre-UNINSTALL phase of RPM building"), + ('post-uninstall=', None, + "Specify a script for the post-UNINSTALL phase of RPM building"), + + ('clean-script=', None, + "Specify a script for the CLEAN phase of RPM building"), + + ('verify-script=', None, + "Specify a script for the VERIFY phase of the RPM build"), + ] boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode'] -- cgit v1.2.1 From 6d690de8de670b52f88c5d414c1bc2580ade2c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Fri, 10 Sep 2004 06:32:54 +0000 Subject: Patch #808120: Add --force-arch=ARCH to bdist_rpm.py. --- command/bdist_rpm.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 11fd9f18..559fcb9f 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -120,6 +120,9 @@ class bdist_rpm (Command): ('verify-script=', None, "Specify a script for the VERIFY phase of the RPM build"), + # Allow a packager to explicitly force an architecture + ('force-arch=', None, + "Force an architecture onto the RPM build process"), ] boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode'] @@ -170,6 +173,8 @@ class bdist_rpm (Command): self.use_rpm_opt_flags = 1 self.rpm3_mode = 1 + self.force_arch = None + # initialize_options() @@ -250,6 +255,7 @@ class bdist_rpm (Command): self.ensure_string_list('build_requires') self.ensure_string_list('obsoletes') + self.ensure_string('force_arch') # finalize_package_data () @@ -389,9 +395,12 @@ class bdist_rpm (Command): 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', 'Prefix: %{_prefix}', ]) - # noarch if no extension modules - if not self.distribution.has_ext_modules(): - spec_file.append('BuildArchitectures: noarch') + if not self.force_arch: + # noarch if no extension modules + if not self.distribution.has_ext_modules(): + spec_file.append('BuildArch: noarch') + else: + spec_file.append( 'BuildArch: %s' % self.force_arch ) for field in ('Vendor', 'Packager', -- cgit v1.2.1 From d3c88e64e1b45b0c08f081423c191de75c823b05 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 12 Sep 2004 03:49:31 +0000 Subject: Whitespace normalization. --- command/bdist_rpm.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 559fcb9f..22eccb93 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -97,24 +97,24 @@ class bdist_rpm (Command): "RPM 2 compatibility mode"), # Add the hooks necessary for specifying custom scripts - ('prep-script=', None, + ('prep-script=', None, "Specify a script for the PREP phase of RPM building"), - ('build-script=', None, + ('build-script=', None, "Specify a script for the BUILD phase of RPM building"), - ('pre-install=', None, + ('pre-install=', None, "Specify a script for the pre-INSTALL phase of RPM building"), - ('install-script=', None, + ('install-script=', None, "Specify a script for the INSTALL phase of RPM building"), - ('post-install=', None, + ('post-install=', None, "Specify a script for the post-INSTALL phase of RPM building"), - ('pre-uninstall=', None, + ('pre-uninstall=', None, "Specify a script for the pre-UNINSTALL phase of RPM building"), - ('post-uninstall=', None, + ('post-uninstall=', None, "Specify a script for the post-UNINSTALL phase of RPM building"), - ('clean-script=', None, + ('clean-script=', None, "Specify a script for the CLEAN phase of RPM building"), ('verify-script=', None, -- cgit v1.2.1 From 6b853a9c0d4926ef6c628436cdec012241c7912b Mon Sep 17 00:00:00 2001 From: Sean Reifschneider Date: Fri, 17 Sep 2004 08:23:22 +0000 Subject: SF Patch 1022003: Change bdist_rpm _topdir to use os.path.abspath(self.rpm_base) instead of os.getcwd() + '/' + self.rpm_base --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 22eccb93..18546d26 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -326,7 +326,7 @@ class bdist_rpm (Command): rpm_cmd.append('-ba') if self.rpm3_mode: rpm_cmd.extend(['--define', - '_topdir %s/%s' % (os.getcwd(), self.rpm_base),]) + '_topdir %s' % os.path.abspath(self.rpm_base)]) if not self.keep_temp: rpm_cmd.append('--clean') rpm_cmd.append(spec_path) -- cgit v1.2.1 From f9ace2e1ee023402f1c4ba9df06df33a2d5b52ac Mon Sep 17 00:00:00 2001 From: Sean Reifschneider Date: Fri, 17 Sep 2004 08:34:12 +0000 Subject: SF Patch 1022011: Add a command-line argument --no-autoreq, which sets the "AutoReq: 0" to disable automatic dependency searching. --- command/bdist_rpm.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 18546d26..7f1b440e 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -81,6 +81,8 @@ class bdist_rpm (Command): "capabilities required to build this package"), ('obsoletes=', None, "capabilities made obsolete by this package"), + ('no-autoreq', None, + "do not automatically calculate dependencies"), # Actions to take when building RPM ('keep-temp', 'k', @@ -125,7 +127,8 @@ class bdist_rpm (Command): "Force an architecture onto the RPM build process"), ] - boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode'] + boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', + 'no-autoreq'] negative_opt = {'no-keep-temp': 'keep-temp', 'no-rpm-opt-flags': 'use-rpm-opt-flags', @@ -172,6 +175,7 @@ class bdist_rpm (Command): self.keep_temp = 0 self.use_rpm_opt_flags = 1 self.rpm3_mode = 1 + self.no_autoreq = 0 self.force_arch = None @@ -429,6 +433,9 @@ class bdist_rpm (Command): if self.icon: spec_file.append('Icon: ' + os.path.basename(self.icon)) + if self.no_autoreq: + spec_file.append('AutoReq: 0') + spec_file.extend([ '', '%description', -- cgit v1.2.1 From de08fcf3db1b9779a9430c600354bed93dadd90b Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Wed, 13 Oct 2004 12:35:28 +0000 Subject: Backing out the basic dependency checking (from pycon sprint). This support was only a first cut, and doesn't deserve to be in a released version (where we have to support it in an ongoing manner) --- command/__init__.py | 1 - command/checkdep.py | 70 ----------------------------------------------------- command/install.py | 13 +--------- core.py | 3 +-- dist.py | 61 +--------------------------------------------- 5 files changed, 3 insertions(+), 145 deletions(-) delete mode 100644 command/checkdep.py diff --git a/command/__init__.py b/command/__init__.py index 3a9a53ee..870005dc 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -24,7 +24,6 @@ __all__ = ['build', 'bdist_dumb', 'bdist_rpm', 'bdist_wininst', - 'checkdep', # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', diff --git a/command/checkdep.py b/command/checkdep.py deleted file mode 100644 index 729002c7..00000000 --- a/command/checkdep.py +++ /dev/null @@ -1,70 +0,0 @@ -"""distutils.command.x - -Implements the Distutils 'x' command. -""" - -# created 2000/mm/dd, John Doe - -__revision__ = "$Id$" - -from distutils.core import Command - -class DependencyFailure(Exception): pass - -class VersionTooOld(DependencyFailure): pass - -class VersionNotKnown(DependencyFailure): pass - -class checkdep (Command): - - # Brief (40-50 characters) description of the command - description = "check package dependencies" - - # List of option tuples: long name, short name (None if no short - # name), and help string. - # Later on, we might have auto-fetch and the like here. Feel free. - user_options = [] - - def initialize_options (self): - self.debug = None - - # initialize_options() - - - def finalize_options (self): - pass - # finalize_options() - - - def run (self): - from distutils.version import LooseVersion - failed = [] - for pkg, ver in self.distribution.metadata.requires: - if pkg == 'python': - if ver is not None: - # Special case the 'python' package - import sys - thisver = LooseVersion('%d.%d.%d'%sys.version_info[:3]) - if thisver < ver: - failed.append(((pkg,ver), VersionTooOld(thisver))) - continue - # Kinda hacky - we should do more here - try: - mod = __import__(pkg) - except Exception, e: - failed.append(((pkg,ver), e)) - continue - if ver is not None: - if hasattr(mod, '__version__'): - thisver = LooseVersion(mod.__version__) - if thisver < ver: - failed.append(((pkg,ver), VersionTooOld(thisver))) - else: - failed.append(((pkg,ver), VersionNotKnown())) - - if failed: - raise DependencyFailure, failed - - # run() - -# class x diff --git a/command/install.py b/command/install.py index 175f7852..2aaf0105 100644 --- a/command/install.py +++ b/command/install.py @@ -126,8 +126,6 @@ class install (Command): "force installation (overwrite any existing files)"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), - ('skip-checkdep', None, - "skip checking dependencies (use at own risk)"), # Where to install documentation (eventually!) #('doc-format=', None, "format of documentation to generate"), @@ -185,15 +183,12 @@ class install (Command): # 'force' forces installation, even if target files are not # out-of-date. 'skip_build' skips running the "build" command, - # handy if you know it's not necessary. 'skip_checkdep' skips - # the 'checkdep' command, if you are sure you can work around the - # dependency failure in another way. 'warn_dir' (which is *not* + # handy if you know it's not necessary. 'warn_dir' (which is *not* # a user option, it's just there so the bdist_* commands can turn # it off) determines whether we warn about installing to a # directory not in sys.path. self.force = 0 self.skip_build = 0 - self.skip_checkdep = 0 self.warn_dir = 1 # These are only here as a conduit from the 'build' command to the @@ -505,12 +500,6 @@ class install (Command): if not self.skip_build: self.run_command('build') - # We check dependencies before we install - # For now, this is disabled. Before 2.4 is released, this will - # be turned on. - #if not self.skip_checkdep: - # self.run_command('checkdep') - # Run all sub-commands (at least those that need to be run) for cmd_name in self.get_sub_commands(): self.run_command(cmd_name) diff --git a/core.py b/core.py index fef291f6..68675349 100644 --- a/core.py +++ b/core.py @@ -47,8 +47,7 @@ setup_keywords = ('distclass', 'script_name', 'script_args', 'options', 'name', 'version', 'author', 'author_email', 'maintainer', 'maintainer_email', 'url', 'license', 'description', 'long_description', 'keywords', - 'platforms', 'classifiers', 'download_url', - 'provides', 'requires', ) + 'platforms', 'classifiers', 'download_url',) # Legal keyword arguments for the Extension constructor extension_keywords = ('name', 'sources', 'include_dirs', diff --git a/dist.py b/dist.py index 53846e93..a23a773c 100644 --- a/dist.py +++ b/dist.py @@ -223,51 +223,6 @@ class Distribution: else: sys.stderr.write(msg + "\n") - # Build up the requires sequence - from distutils.version import LooseVersion - requires = attrs.get('requires') - if requires: - if isinstance(requires, type('')): - raise DistutilsOptionError, 'requires should be a sequence' - newreq = [] - for req in requires: - if '-' not in req: - # We have a plain package name - any version will do - newreq.append((req,None)) - else: - pkg, ver = string.split(req, '-', 1) - newreq.append((pkg, LooseVersion(ver))) - attrs['requires'] = newreq - - # Build up the provides object. If the setup() has no - # provides line, we use packages or modules and the version - # to synthesise the provides. If no version is provided (no - # pun intended) we don't have a provides entry at all. - provides = attrs.get('provides') - if provides: - if isinstance(provides, type('')): - raise DistutilsOptionError, 'provides should be a sequence' - newprov = [] - for prov in provides: - if '-' not in prov: - # We have a plain package name - any version will do - newprov.append((prov,None)) - else: - pkg, ver = string.split(prov, '-', 1) - newprov.append((pkg, LooseVersion(ver))) - attrs['provides'] = newprov - elif attrs.get('version'): - # Build a provides line - prov = [] - if attrs.get('packages'): - for pkg in attrs['packages']: - pkg = string.replace(pkg, '/', '.') - prov.append('%s-%s'%(pkg, attrs['version'])) - elif attrs.get('modules'): - for mod in attrs['modules']: - prov.append('%s-%s'%(mod, attrs['version'])) - attrs['provides'] = prov - # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! for (key,val) in attrs.items(): @@ -275,7 +230,6 @@ class Distribution: setattr(self.metadata, key, val) elif hasattr(self, key): setattr(self, key, val) - else: msg = "Unknown distribution option: %s" % repr(key) if warnings is not None: warnings.warn(msg) @@ -1060,7 +1014,7 @@ class DistributionMetadata: "license", "description", "long_description", "keywords", "platforms", "fullname", "contact", "contact_email", "license", "classifiers", - "download_url", "provides", "requires",) + "download_url") def __init__ (self): self.name = None @@ -1077,8 +1031,6 @@ class DistributionMetadata: self.platforms = None self.classifiers = None self.download_url = None - self.requires = [] - self.provides = [] def write_pkg_info (self, base_dir): """Write the PKG-INFO file into the release tree. @@ -1094,10 +1046,6 @@ class DistributionMetadata: pkg_info.write('Author: %s\n' % self.get_contact() ) pkg_info.write('Author-email: %s\n' % self.get_contact_email() ) pkg_info.write('License: %s\n' % self.get_license() ) - for req in self.get_requires(): - pkg_info.write('Requires: %s\n' % req ) - for prov in self.get_provides(): - pkg_info.write('Provides: %s\n' % prov ) if self.download_url: pkg_info.write('Download-URL: %s\n' % self.download_url) @@ -1176,13 +1124,6 @@ class DistributionMetadata: def get_download_url(self): return self.download_url or "UNKNOWN" - def get_requires(self): - return [ '%s%s%s'%(x, (y and '-') or '', y or '') - for x,y in self.requires ] - - def get_provides(self): - return self.provides - # class DistributionMetadata -- cgit v1.2.1 From f790936afe6035720d8afde494b9b17d408032c7 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Wed, 13 Oct 2004 13:22:34 +0000 Subject: oops. how did _that_ happen? --- dist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dist.py b/dist.py index a23a773c..1a39022f 100644 --- a/dist.py +++ b/dist.py @@ -230,6 +230,7 @@ class Distribution: setattr(self.metadata, key, val) elif hasattr(self, key): setattr(self, key, val) + else: msg = "Unknown distribution option: %s" % repr(key) if warnings is not None: warnings.warn(msg) -- cgit v1.2.1 From 51311882da9cc8f976e8c669794f9bbcc1860792 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Wed, 13 Oct 2004 15:54:17 +0000 Subject: Patch 983206: distutils obeys LDSHARED env var. Removed the code in Python's own setup.py that did the same thing (and tested on Solaris, where LDSHARED is needed...) --- sysconfig.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index 0a4e14cc..8986dc98 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -153,6 +153,8 @@ def customize_compiler(compiler): cc = os.environ['CC'] if os.environ.has_key('CXX'): cxx = os.environ['CXX'] + if os.environ.has_key('LDSHARED'): + ldshared = os.environ['LDSHARED'] if os.environ.has_key('CPP'): cpp = os.environ['CPP'] else: -- cgit v1.2.1 From 27585e07f272206ef70fe911c5c0f05c78fa9336 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Thu, 14 Oct 2004 10:02:08 +0000 Subject: Patch 1046644 - improved distutils support for SWIG. --- command/build_ext.py | 27 +++++++++++++++++++++++---- core.py | 2 +- extension.py | 5 +++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 04cd742c..07614c6a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -81,6 +81,10 @@ class build_ext (Command): "specify the compiler type"), ('swig-cpp', None, "make SWIG create C++ files (default is C)"), + ('swig-opts=', None, + "list of SWIG command line options"), + ('swig=', None, + "path to the SWIG executable"), ] boolean_options = ['inplace', 'debug', 'force', 'swig-cpp'] @@ -107,8 +111,9 @@ class build_ext (Command): self.debug = None self.force = None self.compiler = None + self.swig = None self.swig_cpp = None - + self.swig_opts = None def finalize_options (self): from distutils import sysconfig @@ -205,6 +210,11 @@ class build_ext (Command): if self.undef: self.undef = string.split(self.undef, ',') + if self.swig_opts is None: + self.swig_opts = [] + else: + self.swig_opts = self.swig_opts.split(' ') + # finalize_options () @@ -429,7 +439,7 @@ class build_ext (Command): # First, scan the sources for SWIG definition files (.i), run # SWIG on 'em to create .c files, and modify the sources list # accordingly. - sources = self.swig_sources(sources) + sources = self.swig_sources(sources, ext) # Next, compile the source code to object files. @@ -492,7 +502,7 @@ class build_ext (Command): target_lang=language) - def swig_sources (self, sources): + def swig_sources (self, sources, extension): """Walk the list of source files in 'sources', looking for SWIG interface (.i) files. Run SWIG on all that are found, and @@ -510,6 +520,9 @@ class build_ext (Command): # the temp dir. if self.swig_cpp: + log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") + + if self.swig_cpp or ('-c++' in self.swig_opts): target_ext = '.cpp' else: target_ext = '.c' @@ -526,11 +539,17 @@ class build_ext (Command): if not swig_sources: return new_sources - swig = self.find_swig() + swig = self.swig or self.find_swig() swig_cmd = [swig, "-python"] + swig_cmd.extend(self.swig_opts) if self.swig_cpp: swig_cmd.append("-c++") + # Do not override commandline arguments + if not self.swig_opts: + for o in extension.swig_opts: + swig_cmd.append(o) + for source in swig_sources: target = swig_targets[source] log.info("swigging %s to %s", source, target) diff --git a/core.py b/core.py index 68675349..8c828010 100644 --- a/core.py +++ b/core.py @@ -54,7 +54,7 @@ extension_keywords = ('name', 'sources', 'include_dirs', 'define_macros', 'undef_macros', 'library_dirs', 'libraries', 'runtime_library_dirs', 'extra_objects', 'extra_compile_args', 'extra_link_args', - 'export_symbols', 'depends', 'language') + 'swig_opts', 'export_symbols', 'depends', 'language') def setup (**attrs): """The gateway to the Distutils: do everything your setup script needs diff --git a/extension.py b/extension.py index e69f3e93..440d128c 100644 --- a/extension.py +++ b/extension.py @@ -75,6 +75,9 @@ class Extension: used on all platforms, and not generally necessary for Python extensions, which typically export exactly one symbol: "init" + extension_name. + swig_opts : [string] + any extra options to pass to SWIG if a source file has the .i + extension. depends : [string] list of files that the extension depends on language : string @@ -95,6 +98,7 @@ class Extension: extra_compile_args=None, extra_link_args=None, export_symbols=None, + swig_opts = None, depends=None, language=None, **kw # To catch unknown keywords @@ -116,6 +120,7 @@ class Extension: self.extra_compile_args = extra_compile_args or [] self.extra_link_args = extra_link_args or [] self.export_symbols = export_symbols or [] + self.swig_opts = swig_opts or [] self.depends = depends or [] self.language = language -- cgit v1.2.1 From dea703ad7656eb0d194d854734483045e0a0be56 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Wed, 27 Oct 2004 21:54:33 +0000 Subject: Fix [1055540 ] bdist_wininst broken for pure Python distributions --- command/bdist_wininst.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 33e15561..f4bab62a 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -116,20 +116,21 @@ class bdist_wininst (Command): install_lib.compile = 0 install_lib.optimize = 0 - # If we are building an installer for a Python version other - # than the one we are currently running, then we need to ensure - # our build_lib reflects the other Python version rather than ours. - # Note that for target_version!=sys.version, we must have skipped the - # build step, so there is no issue with enforcing the build of this - # version. - target_version = self.target_version - if not target_version: - assert self.skip_build, "Should have already checked this" - target_version = sys.version[0:3] - plat_specifier = ".%s-%s" % (get_platform(), target_version) - build = self.get_finalized_command('build') - build.build_lib = os.path.join(build.build_base, - 'lib' + plat_specifier) + if self.distribution.has_ext_modules(): + # If we are building an installer for a Python version other + # than the one we are currently running, then we need to ensure + # our build_lib reflects the other Python version rather than ours. + # Note that for target_version!=sys.version, we must have skipped the + # build step, so there is no issue with enforcing the build of this + # version. + target_version = self.target_version + if not target_version: + assert self.skip_build, "Should have already checked this" + target_version = sys.version[0:3] + plat_specifier = ".%s-%s" % (get_platform(), target_version) + build = self.get_finalized_command('build') + build.build_lib = os.path.join(build.build_base, + 'lib' + plat_specifier) # Use a custom scheme for the zip-file, because we have to decide # at installation time which scheme to use. -- cgit v1.2.1 From d0a2baffd16b3af0caca15a0b5ec9c3449a6abcd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 10 Nov 2004 09:01:41 +0000 Subject: Avoid a linker warning: MSVC 7 doesn't support /pdb:None, the debug info will always be in a .pdb file. --- msvccompiler.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 168881ad..ab92801c 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -237,9 +237,14 @@ class MSVCCompiler (CCompiler) : '/Z7', '/D_DEBUG'] self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] - self.ldflags_shared_debug = [ - '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG' - ] + if self.__version >= 7: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' + ] + else: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG' + ] self.ldflags_static = [ '/nologo'] -- cgit v1.2.1 From 5712b12c54ef78b0e4211337269ae93fdcdb1ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 10 Nov 2004 22:23:15 +0000 Subject: Update compatibility comments to 2.1, corresponding to PEP 291 1.13. --- __init__.py | 2 +- archive_util.py | 2 +- bcppcompiler.py | 2 +- ccompiler.py | 2 +- cmd.py | 2 +- command/__init__.py | 2 +- command/bdist.py | 2 +- command/bdist_dumb.py | 2 +- command/bdist_rpm.py | 2 +- command/bdist_wininst.py | 2 +- command/build.py | 2 +- command/build_clib.py | 2 +- command/build_ext.py | 2 +- command/build_py.py | 2 +- command/build_scripts.py | 2 +- command/clean.py | 2 +- command/config.py | 2 +- command/install.py | 2 +- command/install_data.py | 2 +- command/install_headers.py | 2 +- command/install_lib.py | 2 +- command/install_scripts.py | 2 +- command/sdist.py | 2 +- core.py | 2 +- cygwinccompiler.py | 2 +- debug.py | 2 +- dep_util.py | 2 +- dir_util.py | 2 +- dist.py | 2 +- errors.py | 2 +- fancy_getopt.py | 2 +- file_util.py | 2 +- filelist.py | 2 +- log.py | 2 +- msvccompiler.py | 2 +- mwerkscompiler.py | 2 +- spawn.py | 2 +- 37 files changed, 37 insertions(+), 37 deletions(-) diff --git a/__init__.py b/__init__.py index 2592a6c5..a1dbb4b5 100644 --- a/__init__.py +++ b/__init__.py @@ -8,7 +8,7 @@ used from a setup script as setup (...) """ -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/archive_util.py b/archive_util.py index 55babe6f..6aa5e635 100644 --- a/archive_util.py +++ b/archive_util.py @@ -3,7 +3,7 @@ Utility functions for creating archive files (tarballs, zip files, that sort of thing).""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/bcppcompiler.py b/bcppcompiler.py index f995e367..ca524a5b 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,7 +11,7 @@ for the Borland C++ compiler. # someone should sit down and factor out the common code as # WindowsCCompiler! --GPW -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/ccompiler.py b/ccompiler.py index e5b9d7cc..6dad757a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -3,7 +3,7 @@ Contains CCompiler, an abstract base class that defines the interface for the Distutils compiler abstraction model.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/cmd.py b/cmd.py index be8e826f..3cd58589 100644 --- a/cmd.py +++ b/cmd.py @@ -4,7 +4,7 @@ Provides the Command class, the base class for the command classes in the distutils.command package. """ -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/__init__.py b/command/__init__.py index 870005dc..0888c271 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -3,7 +3,7 @@ Package containing implementation of all the standard Distutils commands.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/bdist.py b/command/bdist.py index 4eca9c34..23c25a55 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -3,7 +3,7 @@ Implements the Distutils 'bdist' command (create a built [binary] distribution).""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 3db332d5..7f498c83 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,7 +4,7 @@ Implements the Distutils 'bdist_dumb' command (create a "dumb" built distribution -- i.e., just an archive to be unpacked under $prefix or $exec_prefix).""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 7f1b440e..8eaaff32 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -3,7 +3,7 @@ Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions).""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index f4bab62a..9b45cf35 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -3,7 +3,7 @@ Implements the Distutils 'bdist_wininst' command: create a windows installer exe-program.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/build.py b/command/build.py index e6b3991f..9ae0a292 100644 --- a/command/build.py +++ b/command/build.py @@ -2,7 +2,7 @@ Implements the Distutils 'build' command.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/build_clib.py b/command/build_clib.py index ef03ed72..69d8c751 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,7 +4,7 @@ Implements the Distutils 'build_clib' command, to build a C/C++ library that is included in the module distribution and needed by an extension module.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/build_ext.py b/command/build_ext.py index 07614c6a..4191c76c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,7 +4,7 @@ Implements the Distutils 'build_ext' command, for building extension modules (currently limited to C extensions, should accommodate C++ extensions ASAP).""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/build_py.py b/command/build_py.py index 3079bfcd..621bcb4a 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -2,7 +2,7 @@ Implements the Distutils 'build_py' command.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/build_scripts.py b/command/build_scripts.py index fb73719f..bda4480c 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -2,7 +2,7 @@ Implements the Distutils 'build_scripts' command.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/clean.py b/command/clean.py index 41b22777..1844ffef 100644 --- a/command/clean.py +++ b/command/clean.py @@ -4,7 +4,7 @@ Implements the Distutils 'clean' command.""" # contributed by Bastian Kleineidam , added 2000-03-18 -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/config.py b/command/config.py index f18c79ff..520c1b0c 100644 --- a/command/config.py +++ b/command/config.py @@ -9,7 +9,7 @@ configure-like tasks: "try to compile this C code", or "figure out where this header file lives". """ -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/install.py b/command/install.py index 2aaf0105..fdbec358 100644 --- a/command/install.py +++ b/command/install.py @@ -4,7 +4,7 @@ Implements the Distutils 'install' command.""" from distutils import log -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/install_data.py b/command/install_data.py index 2fa0da29..1069830f 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -5,7 +5,7 @@ platform-independent data files.""" # contributed by Bastian Kleineidam -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/install_headers.py b/command/install_headers.py index 3a37d309..2bd1b043 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -3,7 +3,7 @@ Implements the Distutils 'install_headers' command, to install C/C++ header files to the Python include directory.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/install_lib.py b/command/install_lib.py index c234117a..22d0ab37 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -1,4 +1,4 @@ -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/install_scripts.py b/command/install_scripts.py index abe10457..fe93ef5a 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -5,7 +5,7 @@ Python scripts.""" # contributed by Bastian Kleineidam -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/command/sdist.py b/command/sdist.py index 70214965..fe6c9139 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -2,7 +2,7 @@ Implements the Distutils 'sdist' command (create a source distribution).""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/core.py b/core.py index 8c828010..eba94559 100644 --- a/core.py +++ b/core.py @@ -6,7 +6,7 @@ indirectly provides the Distribution and Command classes, although they are really defined in distutils.dist and distutils.cmd. """ -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 31e8e349..4fd23e6d 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -45,7 +45,7 @@ cygwin in no-cygwin mode). # * mingw gcc 3.2/ld 2.13 works # (ld supports -shared) -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/debug.py b/debug.py index 2a87eb5d..b67139c7 100644 --- a/debug.py +++ b/debug.py @@ -1,6 +1,6 @@ import os -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/dep_util.py b/dep_util.py index 0746633d..c139c852 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,7 +4,7 @@ Utility functions for simple, timestamp-based dependency of files and groups of files; also, function based entirely on such timestamp dependency analysis.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/dir_util.py b/dir_util.py index 77f64c41..7f145037 100644 --- a/dir_util.py +++ b/dir_util.py @@ -2,7 +2,7 @@ Utility functions for manipulating directories and directory trees.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/dist.py b/dist.py index 1a39022f..9eb2aa42 100644 --- a/dist.py +++ b/dist.py @@ -4,7 +4,7 @@ Provides the Distribution class, which represents the module distribution being built/installed/distributed. """ -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/errors.py b/errors.py index 94e83fb5..e72221bd 100644 --- a/errors.py +++ b/errors.py @@ -8,7 +8,7 @@ usually raised for errors that are obviously the end-user's fault This module is safe to use in "from ... import *" mode; it only exports symbols whose names start with "Distutils" and end with "Error".""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/fancy_getopt.py b/fancy_getopt.py index 6c1134fc..218ed73f 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,7 +8,7 @@ additional features: * options set attributes of a passed-in object """ -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/file_util.py b/file_util.py index e2b1c515..37b152ed 100644 --- a/file_util.py +++ b/file_util.py @@ -3,7 +3,7 @@ Utility functions for operating on single files. """ -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/filelist.py b/filelist.py index 566d87ab..43f9aaaf 100644 --- a/filelist.py +++ b/filelist.py @@ -4,7 +4,7 @@ Provides the FileList class, used for poking about the filesystem and building lists of files. """ -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/log.py b/log.py index bf26302f..cf3ee136 100644 --- a/log.py +++ b/log.py @@ -1,6 +1,6 @@ """A simple log mechanism styled after PEP 282.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. # The class here is styled after PEP 282 so that it could later be # replaced with a standard Python logging implementation. diff --git a/msvccompiler.py b/msvccompiler.py index ab92801c..dd9d8928 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -8,7 +8,7 @@ for the Microsoft Visual Studio. # hacked by Robin Becker and Thomas Heller to do a better job of # finding DevStudio (through the registry) -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/mwerkscompiler.py b/mwerkscompiler.py index d546de1f..0de123d9 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -4,7 +4,7 @@ Contains MWerksCompiler, an implementation of the abstract CCompiler class for MetroWerks CodeWarrior on the Macintosh. Needs work to support CW on Windows.""" -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" diff --git a/spawn.py b/spawn.py index a89b88ce..e5654ff0 100644 --- a/spawn.py +++ b/spawn.py @@ -6,7 +6,7 @@ Also provides the 'find_executable()' to search the path for a given executable name. """ -# This module should be kept compatible with Python 1.5.2. +# This module should be kept compatible with Python 2.1. __revision__ = "$Id$" -- cgit v1.2.1 From 0d815d149d55b3ec5930550e0facf64838f03cb7 Mon Sep 17 00:00:00 2001 From: Fredrik Lundh Date: Wed, 24 Nov 2004 22:31:11 +0000 Subject: SF patch #1071739 (by Christos Georgiou) This patch offers a better explanation in case the MS VC++ (free) toolkit is installed but the .NET Framework SDK is not. --- msvccompiler.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index dd9d8928..ccb62a88 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -124,10 +124,15 @@ class MacroExpander: self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") net = r"Software\Microsoft\.NETFramework" self.set_macro("FrameworkDir", net, "installroot") - if version > 7.0: - self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") - else: - self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") + try: + if version > 7.0: + self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") + else: + self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") + except KeyError, exc: # + raise DistutilsPlatformError, \ + ("The .NET Framework SDK needs to be installed before " + "building extensions for Python.") p = r"Software\Microsoft\NET Framework Setup\Product" for base in HKEYS: -- cgit v1.2.1 From 69251931fb71629f37d7daa3e3765ee802513e74 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 28 Nov 2004 01:10:01 +0000 Subject: Whitespace normalization. --- msvccompiler.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index ccb62a88..89a75b34 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -124,12 +124,12 @@ class MacroExpander: self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") net = r"Software\Microsoft\.NETFramework" self.set_macro("FrameworkDir", net, "installroot") - try: - if version > 7.0: - self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") - else: - self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") - except KeyError, exc: # + try: + if version > 7.0: + self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") + else: + self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") + except KeyError, exc: # raise DistutilsPlatformError, \ ("The .NET Framework SDK needs to be installed before " "building extensions for Python.") -- cgit v1.2.1 From 0713ced9996da5971124d3912d21af46ec2c21ad Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 1 Dec 2004 19:43:34 +0000 Subject: Recompiled binaries after source changes. --- command/wininst-6.exe | Bin 61440 -> 61440 bytes command/wininst-7.1.exe | Bin 61440 -> 61440 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-6.exe b/command/wininst-6.exe index ed288d6a..4683659e 100644 Binary files a/command/wininst-6.exe and b/command/wininst-6.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index a800678e..b8028f44 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ -- cgit v1.2.1 From 543e5a6ca421c514dd517e81bb4d2ba64da772dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Thu, 2 Dec 2004 20:14:16 +0000 Subject: Restore Python 2.1 compatibility (os.extsep was introduced in Python 2.2). --- command/install_lib.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index 22d0ab37..08ff5434 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -9,8 +9,10 @@ from distutils.errors import DistutilsOptionError # Extension for Python source files. -PYTHON_SOURCE_EXTENSION = os.extsep + "py" - +if hasattr(os, 'extsep'): + PYTHON_SOURCE_EXTENSION = os.extsep + "py" +else: + PYTHON_SOURCE_EXTENSION = ".py" class install_lib (Command): -- cgit v1.2.1 From abc818a08898ada7a31b2466a7bf56848798fe86 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 22 Dec 2004 17:24:36 +0000 Subject: Recompiled after source changes. --- command/wininst-6.exe | Bin 61440 -> 61440 bytes command/wininst-7.1.exe | Bin 61440 -> 61440 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-6.exe b/command/wininst-6.exe index 4683659e..6ee28235 100644 Binary files a/command/wininst-6.exe and b/command/wininst-6.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index b8028f44..6ebef046 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ -- cgit v1.2.1 From 9c20e5e7816e7e2f1a132fe8a7458745355141b1 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sun, 26 Dec 2004 23:07:48 +0000 Subject: After discussion on the PythonMac-SIG it was decided that it is better to make using "-undefined dynamic_lookup" for linking extensions more automatic on 10.3 and later. So if we're on that platform and MACOSX_DEPLOYMENT_TARGET is not set we now set it to the current OSX version during configure. Additionally, distutils will pick up the configure-time value by default. Will backport. --- sysconfig.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 8986dc98..d4eb368e 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -360,11 +360,13 @@ def _init_posix(): # On MacOSX we need to check the setting of the environment variable # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so # it needs to be compatible. - # An alternative would be to force MACOSX_DEPLOYMENT_TARGET to be - # the same as during configure. + # If it isn't set we set it to the configure-time value if sys.platform == 'darwin' and g.has_key('CONFIGURE_MACOSX_DEPLOYMENT_TARGET'): cfg_target = g['CONFIGURE_MACOSX_DEPLOYMENT_TARGET'] cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') + if cur_target == '': + cur_target = cfg_target + os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) if cfg_target != cur_target: my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' % (cur_target, cfg_target)) -- cgit v1.2.1 From 4cca0bf734f15ffd714bcb39f781c6e3d768312f Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 20 Jan 2005 19:14:17 +0000 Subject: Fix [ 1103844 ] fix distutils.install.dump_dirs() with negated options. Will backport myself. --- command/install.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/command/install.py b/command/install.py index fdbec358..77237611 100644 --- a/command/install.py +++ b/command/install.py @@ -352,8 +352,13 @@ class install (Command): opt_name = opt[0] if opt_name[-1] == "=": opt_name = opt_name[0:-1] - opt_name = string.translate(opt_name, longopt_xlate) - val = getattr(self, opt_name) + if self.negative_opt.has_key(opt_name): + opt_name = string.translate(self.negative_opt[opt_name], + longopt_xlate) + val = not getattr(self, opt_name) + else: + opt_name = string.translate(opt_name, longopt_xlate) + val = getattr(self, opt_name) print " %s: %s" % (opt_name, val) -- cgit v1.2.1 From 3145cad407f875f453bb252f7aea93597c6e6777 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 3 Feb 2005 20:48:26 +0000 Subject: Recompiled after source changes. --- command/wininst-6.exe | Bin 61440 -> 61440 bytes command/wininst-7.1.exe | Bin 61440 -> 61440 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-6.exe b/command/wininst-6.exe index 6ee28235..bd715250 100644 Binary files a/command/wininst-6.exe and b/command/wininst-6.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 6ebef046..ee357139 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ -- cgit v1.2.1 From 21599f63453eaf27fb5819a7d7706cb23720f323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 3 Mar 2005 08:12:27 +0000 Subject: Patch #1104111: Alter setup.py --help and --help-commands. --- command/clean.py | 2 +- dist.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/command/clean.py b/command/clean.py index 1844ffef..02189c53 100644 --- a/command/clean.py +++ b/command/clean.py @@ -15,7 +15,7 @@ from distutils import log class clean (Command): - description = "clean up output of 'build' command" + description = "clean up temporary files from 'build' command" user_options = [ ('build-base=', 'b', "base build directory (default: 'build.build-base')"), diff --git a/dist.py b/dist.py index 9eb2aa42..4f4bae52 100644 --- a/dist.py +++ b/dist.py @@ -59,6 +59,15 @@ class Distribution: ('help', 'h', "show detailed help message"), ] + # 'common_usage' is a short (2-3 line) string describing the common + # usage of the setup script. + common_usage = """\ +Common commands: (see '--help-commands' for more) + + setup.py build will build the package underneath 'build/' + setup.py install will install the package +""" + # options that are not propagated to the commands display_options = [ ('help-commands', None, @@ -608,7 +617,7 @@ class Distribution: else: options = self.global_options parser.set_option_table(options) - parser.print_help("Global options:") + parser.print_help(self.common_usage + "\nGlobal options:") print if display_options: -- cgit v1.2.1 From b60285cfd0da94171ac6ab69812e9213c9826d70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 3 Mar 2005 11:08:03 +0000 Subject: Patch #1046831: Use get_python_version where appropriate in sysconfig.py. --- sysconfig.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index d4eb368e..aae0f27c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -34,7 +34,7 @@ python_build = os.path.isfile(landmark) del argv0_path, landmark -def get_python_version (): +def get_python_version(): """Return a string containing the major and minor Python version, leaving off the patchlevel. Sample return values could be '1.5' or '2.2'. @@ -65,7 +65,7 @@ def get_python_inc(plat_specific=0, prefix=None): if not os.path.exists(inc_dir): inc_dir = os.path.join(os.path.dirname(base), "Include") return inc_dir - return os.path.join(prefix, "include", "python" + sys.version[:3]) + return os.path.join(prefix, "include", "python" + get_python_version()) elif os.name == "nt": return os.path.join(prefix, "include") elif os.name == "mac": @@ -110,7 +110,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): if standard_lib: return os.path.join(prefix, "Lib") else: - if sys.version < "2.2": + if get_python_version() < "2.2": return prefix else: return os.path.join(PREFIX, "Lib", "site-packages") @@ -189,7 +189,7 @@ def get_config_h_filename(): inc_dir = os.curdir else: inc_dir = get_python_inc(plat_specific=1) - if sys.version < '2.2': + if get_python_version() < '2.2': config_h = 'config.h' else: # The name of the config.h file changed in 2.2 @@ -378,7 +378,7 @@ def _init_posix(): if python_build: g['LDSHARED'] = g['BLDSHARED'] - elif sys.version < '2.1': + elif get_python_version() < '2.1': # The following two branches are for 1.5.2 compatibility. if sys.platform == 'aix4': # what about AIX 3.x ? # Linker script is in the config directory, not in Modules as the @@ -405,7 +405,7 @@ def _init_posix(): # it's taken care of for them by the 'build_ext.get_libraries()' # method.) g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % - (linkerscript, PREFIX, sys.version[0:3])) + (linkerscript, PREFIX, get_python_version())) global _config_vars _config_vars = g -- cgit v1.2.1 From 827d92b58b53f25b8e2579d6f301c51a43780687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Fri, 4 Mar 2005 13:50:17 +0000 Subject: Patch #1075887: Don't require MSVC in distutils if there is nothing to build. Will backport to 2.4 --- msvccompiler.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/msvccompiler.py b/msvccompiler.py index 89a75b34..01613932 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -211,6 +211,9 @@ class MSVCCompiler (CCompiler) : self.__macros = MacroExpander(self.__version) else: self.__root = r"Software\Microsoft\Devstudio" + self.initialized = False + + def initialize(self): self.__paths = self.get_msvc_paths("path") if len (self.__paths) == 0: @@ -290,6 +293,7 @@ class MSVCCompiler (CCompiler) : output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): + if not self.initialized: self.initialize() macros, objects, extra_postargs, pp_opts, build = \ self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) @@ -381,6 +385,7 @@ class MSVCCompiler (CCompiler) : debug=0, target_lang=None): + if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args (objects, output_dir) output_filename = \ self.library_filename (output_libname, output_dir=output_dir) @@ -414,6 +419,7 @@ class MSVCCompiler (CCompiler) : build_temp=None, target_lang=None): + if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args (objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) -- cgit v1.2.1 From ec12da619b7a0be1135564e31e6a9b41d6134437 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 5 Mar 2005 05:28:45 +0000 Subject: Remove a tab so that whitespace usage is consistent. --- msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index 01613932..d2ea008e 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -293,7 +293,7 @@ class MSVCCompiler (CCompiler) : output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): - if not self.initialized: self.initialize() + if not self.initialized: self.initialize() macros, objects, extra_postargs, pp_opts, build = \ self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) -- cgit v1.2.1 From 891c7ebce5c207c73bfcf79ecd51d58ee8fab395 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 5 Mar 2005 05:32:14 +0000 Subject: Remove some more tab usage to prevent an error when run as ``python -tt``. --- msvccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index d2ea008e..8106df61 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -385,7 +385,7 @@ class MSVCCompiler (CCompiler) : debug=0, target_lang=None): - if not self.initialized: self.initialize() + if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args (objects, output_dir) output_filename = \ self.library_filename (output_libname, output_dir=output_dir) @@ -419,7 +419,7 @@ class MSVCCompiler (CCompiler) : build_temp=None, target_lang=None): - if not self.initialized: self.initialize() + if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args (objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) -- cgit v1.2.1 From 8992c0187b1cbc5bf26aa8c686421d0b81e55e4c Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sat, 12 Mar 2005 19:05:58 +0000 Subject: Port bugfix from 2.4 maint. Bug #1160802: Can't build Zope on Windows w/ 2.4.1c1. MSVCCompiler.initialize(): set self.initialized to True, as suggested by AMK. Else we keep growing the PATH endlessly, with each new C extension built, until putenv() complains. No change to NEWS because the patch that created this bug is also new for 2.5a1 (so there's no change here to any code yet released from HEAD). --- msvccompiler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/msvccompiler.py b/msvccompiler.py index 8106df61..b94d35f1 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -255,6 +255,7 @@ class MSVCCompiler (CCompiler) : ] self.ldflags_static = [ '/nologo'] + self.initialized = True # -- Worker methods ------------------------------------------------ -- cgit v1.2.1 From 5a3290c36b8087fc73356362cccb51d523b16167 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Sun, 20 Mar 2005 22:17:02 +0000 Subject: helper code, mostly from Andy Harrington, for PEP 314 completion --- tests/test_versionpredicate.py | 9 ++++ versionpredicate.py | 103 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 tests/test_versionpredicate.py create mode 100644 versionpredicate.py diff --git a/tests/test_versionpredicate.py b/tests/test_versionpredicate.py new file mode 100644 index 00000000..44cb41ee --- /dev/null +++ b/tests/test_versionpredicate.py @@ -0,0 +1,9 @@ +"""Tests harness for distutils.versionpredicate. + +""" + +import distutils.versionpredicate +import doctest + +def test_suite(): + return doctest.DocTestSuite(distutils.versionpredicate) diff --git a/versionpredicate.py b/versionpredicate.py new file mode 100644 index 00000000..5126ebf5 --- /dev/null +++ b/versionpredicate.py @@ -0,0 +1,103 @@ +"""Module for parsing and testing package version predicate strings. +""" +import re +import version +import operator + +re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)") +# (package) (rest) + +re_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses +re_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") +# (comp) (version) + +def splitUp(pred): + """Parse a single version comparison. + Return (comparison string, StrictVersion) + """ + res = re_splitComparison.match(pred) + if not res: + raise ValueError, "Bad package restriction syntax: " + pred + comp, verStr = res.groups() + return (comp, version.StrictVersion(verStr)) + +compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq, + ">": operator.gt, ">=": operator.ge, "!=": operator.ne} + +class VersionPredicate: + """Parse and test package version predicates. + + >>> v = VersionPredicate("pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)") + >>> print v + pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) + >>> v.satisfied_by("1.1") + True + >>> v.satisfied_by("1.4") + True + >>> v.satisfied_by("1.0") + False + >>> v.satisfied_by("4444.4") + False + >>> v.satisfied_by("1555.1b3") + False + >>> v = VersionPredicate("pat( == 0.1 ) ") + >>> v.satisfied_by("0.1") + True + >>> v.satisfied_by("0.2") + False + >>> v = VersionPredicate("p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)") + Traceback (most recent call last): + ... + ValueError: invalid version number '1.2zb3' + + """ + + def __init__(self, versionPredicateStr): + """Parse a version predicate string. + """ + # Fields: + # name: package name + # pred: list of (comparison string, StrictVersion) + + versionPredicateStr = versionPredicateStr.strip() + if not versionPredicateStr: + raise ValueError, "Empty package restriction" + match = re_validPackage.match(versionPredicateStr) + if not match: + raise ValueError, "Bad package name in " + versionPredicateStr + self.name, paren = match.groups() + paren = paren.strip() + if paren: + match = re_paren.match(paren) + if not match: + raise ValueError, "Expected parenthesized list: " + paren + str = match.groups()[0] + self.pred = [splitUp(aPred) for aPred in str.split(",")] + if not self.pred: + raise ValueError("Empty Parenthesized list in %r" + % versionPredicateStr ) + else: + self.pred=[] + + def __str__(self): + if self.pred: + seq = [cond + " " + str(ver) for cond, ver in self.pred] + return self.name + " (" + ", ".join(seq) + ")" + else: + return self.name + + def satisfied_by(self, version): + """True if version is compatible with all the predicates in self. + The parameter version must be acceptable to the StrictVersion + constructor. It may be either a string or StrictVersion. + """ + for cond, ver in self.pred: + if not compmap[cond](version, ver): + return False + return True + + +def check_provision(value): + m = re.match("[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*(\s*\([^)]+\))?$", value) + if not m: + raise ValueError("illegal provides specification: %r" % value) -- cgit v1.2.1 From 1690ef36ec0968c381d0cd02c92863441416daf0 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Sun, 20 Mar 2005 22:19:47 +0000 Subject: PEP 314 implementation (client side): added support for the provides, requires, and obsoletes metadata fields --- command/register.py | 6 +++ core.py | 4 +- dist.py | 109 +++++++++++++++++++++++++++++++++++++++++----------- tests/test_dist.py | 91 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 185 insertions(+), 25 deletions(-) diff --git a/command/register.py b/command/register.py index 8104ce06..6e9a8d42 100644 --- a/command/register.py +++ b/command/register.py @@ -231,7 +231,13 @@ Your selection [default 1]: ''', 'platform': meta.get_platforms(), 'classifiers': meta.get_classifiers(), 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), } + if data['provides'] or data['requires'] or data['obsoletes']: + data['metadata_version'] = '1.1' return data def post_to_server(self, data, auth=None): diff --git a/core.py b/core.py index eba94559..c9c6f037 100644 --- a/core.py +++ b/core.py @@ -47,7 +47,9 @@ setup_keywords = ('distclass', 'script_name', 'script_args', 'options', 'name', 'version', 'author', 'author_email', 'maintainer', 'maintainer_email', 'url', 'license', 'description', 'long_description', 'keywords', - 'platforms', 'classifiers', 'download_url',) + 'platforms', 'classifiers', 'download_url', + 'requires', 'provides', 'obsoletes', + ) # Legal keyword arguments for the Extension constructor extension_keywords = ('name', 'sources', 'include_dirs', diff --git a/dist.py b/dist.py index 4f4bae52..c5dd5cbf 100644 --- a/dist.py +++ b/dist.py @@ -106,6 +106,12 @@ Common commands: (see '--help-commands' for more) "print the list of classifiers"), ('keywords', None, "print the list of keywords"), + ('provides', None, + "print the list of packages/modules provided"), + ('requires', None, + "print the list of packages/modules required"), + ('obsoletes', None, + "print the list of packages/modules made obsolete") ] display_option_names = map(lambda x: translate_longopt(x[0]), display_options) @@ -210,7 +216,6 @@ Common commands: (see '--help-commands' for more) # distribution options. if attrs: - # Pull out the set of command options and work on them # specifically. Note that this order guarantees that aliased # command options will override any supplied redundantly @@ -235,7 +240,9 @@ Common commands: (see '--help-commands' for more) # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! for (key,val) in attrs.items(): - if hasattr(self.metadata, key): + if hasattr(self.metadata, "set_" + key): + getattr(self.metadata, "set_" + key)(val) + elif hasattr(self.metadata, key): setattr(self.metadata, key, val) elif hasattr(self, key): setattr(self, key, val) @@ -678,7 +685,8 @@ Common commands: (see '--help-commands' for more) value = getattr(self.metadata, "get_"+opt)() if opt in ['keywords', 'platforms']: print string.join(value, ',') - elif opt == 'classifiers': + elif opt in ('classifiers', 'provides', 'requires', + 'obsoletes'): print string.join(value, '\n') else: print value @@ -1024,7 +1032,10 @@ class DistributionMetadata: "license", "description", "long_description", "keywords", "platforms", "fullname", "contact", "contact_email", "license", "classifiers", - "download_url") + "download_url", + # PEP 314 + "provides", "requires", "obsoletes", + ) def __init__ (self): self.name = None @@ -1041,40 +1052,58 @@ class DistributionMetadata: self.platforms = None self.classifiers = None self.download_url = None + # PEP 314 + self.provides = None + self.requires = None + self.obsoletes = None def write_pkg_info (self, base_dir): """Write the PKG-INFO file into the release tree. """ - pkg_info = open( os.path.join(base_dir, 'PKG-INFO'), 'w') - pkg_info.write('Metadata-Version: 1.0\n') - pkg_info.write('Name: %s\n' % self.get_name() ) - pkg_info.write('Version: %s\n' % self.get_version() ) - pkg_info.write('Summary: %s\n' % self.get_description() ) - pkg_info.write('Home-page: %s\n' % self.get_url() ) - pkg_info.write('Author: %s\n' % self.get_contact() ) - pkg_info.write('Author-email: %s\n' % self.get_contact_email() ) - pkg_info.write('License: %s\n' % self.get_license() ) + self.write_pkg_file(pkg_info) + + pkg_info.close() + + # write_pkg_info () + + def write_pkg_file (self, file): + """Write the PKG-INFO format data to a file object. + """ + version = '1.0' + if self.provides or self.requires or self.obsoletes: + version = '1.1' + + file.write('Metadata-Version: %s\n' % version) + file.write('Name: %s\n' % self.get_name() ) + file.write('Version: %s\n' % self.get_version() ) + file.write('Summary: %s\n' % self.get_description() ) + file.write('Home-page: %s\n' % self.get_url() ) + file.write('Author: %s\n' % self.get_contact() ) + file.write('Author-email: %s\n' % self.get_contact_email() ) + file.write('License: %s\n' % self.get_license() ) if self.download_url: - pkg_info.write('Download-URL: %s\n' % self.download_url) + file.write('Download-URL: %s\n' % self.download_url) long_desc = rfc822_escape( self.get_long_description() ) - pkg_info.write('Description: %s\n' % long_desc) + file.write('Description: %s\n' % long_desc) keywords = string.join( self.get_keywords(), ',') if keywords: - pkg_info.write('Keywords: %s\n' % keywords ) - - for platform in self.get_platforms(): - pkg_info.write('Platform: %s\n' % platform ) + file.write('Keywords: %s\n' % keywords ) - for classifier in self.get_classifiers(): - pkg_info.write('Classifier: %s\n' % classifier ) + self._write_list(file, 'Platform', self.get_platforms()) + self._write_list(file, 'Classifier', self.get_classifiers()) - pkg_info.close() + # PEP 314 + self._write_list(file, 'Requires', self.get_requires()) + self._write_list(file, 'Provides', self.get_provides()) + self._write_list(file, 'Obsoletes', self.get_obsoletes()) - # write_pkg_info () + def _write_list (self, file, name, values): + for value in values: + file.write('%s: %s\n' % (name, value)) # -- Metadata query methods ---------------------------------------- @@ -1134,6 +1163,40 @@ class DistributionMetadata: def get_download_url(self): return self.download_url or "UNKNOWN" + # PEP 314 + + def get_requires(self): + return self.requires or [] + + def set_requires(self, value): + import distutils.versionpredicate + for v in value: + distutils.versionpredicate.VersionPredicate(v) + self.requires = value + + def get_provides(self): + return self.provides or [] + + def set_provides(self, value): + value = [v.strip() for v in value] + for v in value: + import distutils.versionpredicate + ver = distutils.versionpredicate.check_provision(v) + if ver: + import distutils.version + sv = distutils.version.StrictVersion() + sv.parse(ver.strip()[1:-1]) + self.provides = value + + def get_obsoletes(self): + return self.obsoletes or [] + + def set_obsoletes(self, value): + import distutils.versionpredicate + for v in value: + distutils.versionpredicate.VersionPredicate(v) + self.obsoletes = value + # class DistributionMetadata diff --git a/tests/test_dist.py b/tests/test_dist.py index 695f6d81..7675fbfa 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -4,6 +4,7 @@ import distutils.cmd import distutils.dist import os import shutil +import StringIO import sys import tempfile import unittest @@ -96,5 +97,93 @@ class DistributionTestCase(unittest.TestCase): os.unlink(TESTFN) +class MetadataTestCase(unittest.TestCase): + + def test_simple_metadata(self): + attrs = {"name": "package", + "version": "1.0"} + dist = distutils.dist.Distribution(attrs) + meta = self.format_metadata(dist) + self.assert_("Metadata-Version: 1.0" in meta) + self.assert_("provides:" not in meta.lower()) + self.assert_("requires:" not in meta.lower()) + self.assert_("obsoletes:" not in meta.lower()) + + def test_provides(self): + attrs = {"name": "package", + "version": "1.0", + "provides": ["package", "package.sub"]} + dist = distutils.dist.Distribution(attrs) + self.assertEqual(dist.metadata.get_provides(), + ["package", "package.sub"]) + self.assertEqual(dist.get_provides(), + ["package", "package.sub"]) + meta = self.format_metadata(dist) + self.assert_("Metadata-Version: 1.1" in meta) + self.assert_("requires:" not in meta.lower()) + self.assert_("obsoletes:" not in meta.lower()) + + def test_provides_illegal(self): + self.assertRaises(ValueError, + distutils.dist.Distribution, + {"name": "package", + "version": "1.0", + "provides": ["my.pkg (splat)"]}) + + def test_requires(self): + attrs = {"name": "package", + "version": "1.0", + "requires": ["other", "another (==1.0)"]} + dist = distutils.dist.Distribution(attrs) + self.assertEqual(dist.metadata.get_requires(), + ["other", "another (==1.0)"]) + self.assertEqual(dist.get_requires(), + ["other", "another (==1.0)"]) + meta = self.format_metadata(dist) + self.assert_("Metadata-Version: 1.1" in meta) + self.assert_("provides:" not in meta.lower()) + self.assert_("Requires: other" in meta) + self.assert_("Requires: another (==1.0)" in meta) + self.assert_("obsoletes:" not in meta.lower()) + + def test_requires_illegal(self): + self.assertRaises(ValueError, + distutils.dist.Distribution, + {"name": "package", + "version": "1.0", + "requires": ["my.pkg (splat)"]}) + + def test_obsoletes(self): + attrs = {"name": "package", + "version": "1.0", + "obsoletes": ["other", "another (<1.0)"]} + dist = distutils.dist.Distribution(attrs) + self.assertEqual(dist.metadata.get_obsoletes(), + ["other", "another (<1.0)"]) + self.assertEqual(dist.get_obsoletes(), + ["other", "another (<1.0)"]) + meta = self.format_metadata(dist) + self.assert_("Metadata-Version: 1.1" in meta) + self.assert_("provides:" not in meta.lower()) + self.assert_("requires:" not in meta.lower()) + self.assert_("Obsoletes: other" in meta) + self.assert_("Obsoletes: another (<1.0)" in meta) + + def test_obsoletes_illegal(self): + self.assertRaises(ValueError, + distutils.dist.Distribution, + {"name": "package", + "version": "1.0", + "obsoletes": ["my.pkg (splat)"]}) + + def format_metadata(self, dist): + sio = StringIO.StringIO() + dist.metadata.write_pkg_file(sio) + return sio.getvalue() + + def test_suite(): - return unittest.makeSuite(DistributionTestCase) + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(DistributionTestCase)) + suite.addTest(unittest.makeSuite(MetadataTestCase)) + return suite -- cgit v1.2.1 From b9835f36c86e8b46e92de5b8c6fcd88525bcac21 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Mon, 21 Mar 2005 06:36:32 +0000 Subject: - rename check_provision() to split_revision() - fix indentation to conform to the Python style guide - add more tests and documentation --- dist.py | 6 +- versionpredicate.py | 227 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 145 insertions(+), 88 deletions(-) diff --git a/dist.py b/dist.py index c5dd5cbf..f015874b 100644 --- a/dist.py +++ b/dist.py @@ -1181,11 +1181,7 @@ class DistributionMetadata: value = [v.strip() for v in value] for v in value: import distutils.versionpredicate - ver = distutils.versionpredicate.check_provision(v) - if ver: - import distutils.version - sv = distutils.version.StrictVersion() - sv.parse(ver.strip()[1:-1]) + distutils.versionpredicate.split_provision(v) self.provides = value def get_obsoletes(self): diff --git a/versionpredicate.py b/versionpredicate.py index 5126ebf5..8922b137 100644 --- a/versionpredicate.py +++ b/versionpredicate.py @@ -1,9 +1,10 @@ """Module for parsing and testing package version predicate strings. """ import re -import version +import distutils.version import operator + re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)") # (package) (rest) @@ -11,93 +12,153 @@ re_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses re_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") # (comp) (version) + def splitUp(pred): - """Parse a single version comparison. - Return (comparison string, StrictVersion) - """ - res = re_splitComparison.match(pred) - if not res: - raise ValueError, "Bad package restriction syntax: " + pred - comp, verStr = res.groups() - return (comp, version.StrictVersion(verStr)) + """Parse a single version comparison. + + Return (comparison string, StrictVersion) + """ + res = re_splitComparison.match(pred) + if not res: + raise ValueError("bad package restriction syntax: %r" % pred) + comp, verStr = res.groups() + return (comp, distutils.version.StrictVersion(verStr)) compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq, ">": operator.gt, ">=": operator.ge, "!=": operator.ne} class VersionPredicate: - """Parse and test package version predicates. - - >>> v = VersionPredicate("pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)") - >>> print v - pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) - >>> v.satisfied_by("1.1") - True - >>> v.satisfied_by("1.4") - True - >>> v.satisfied_by("1.0") - False - >>> v.satisfied_by("4444.4") - False - >>> v.satisfied_by("1555.1b3") - False - >>> v = VersionPredicate("pat( == 0.1 ) ") - >>> v.satisfied_by("0.1") - True - >>> v.satisfied_by("0.2") - False - >>> v = VersionPredicate("p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)") - Traceback (most recent call last): - ... - ValueError: invalid version number '1.2zb3' - - """ - - def __init__(self, versionPredicateStr): - """Parse a version predicate string. - """ - # Fields: - # name: package name - # pred: list of (comparison string, StrictVersion) - - versionPredicateStr = versionPredicateStr.strip() - if not versionPredicateStr: - raise ValueError, "Empty package restriction" - match = re_validPackage.match(versionPredicateStr) - if not match: - raise ValueError, "Bad package name in " + versionPredicateStr - self.name, paren = match.groups() - paren = paren.strip() - if paren: - match = re_paren.match(paren) - if not match: - raise ValueError, "Expected parenthesized list: " + paren - str = match.groups()[0] - self.pred = [splitUp(aPred) for aPred in str.split(",")] - if not self.pred: - raise ValueError("Empty Parenthesized list in %r" - % versionPredicateStr ) - else: - self.pred=[] - - def __str__(self): - if self.pred: - seq = [cond + " " + str(ver) for cond, ver in self.pred] - return self.name + " (" + ", ".join(seq) + ")" - else: - return self.name - - def satisfied_by(self, version): - """True if version is compatible with all the predicates in self. - The parameter version must be acceptable to the StrictVersion - constructor. It may be either a string or StrictVersion. - """ - for cond, ver in self.pred: - if not compmap[cond](version, ver): - return False - return True - - -def check_provision(value): - m = re.match("[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*(\s*\([^)]+\))?$", value) + """Parse and test package version predicates. + + >>> v = VersionPredicate('pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)') + + The `name` attribute provides the full dotted name that is given:: + + >>> v.name + 'pyepat.abc' + + The str() of a `VersionPredicate` provides a normalized + human-readable version of the expression:: + + >>> print v + pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) + + The `satisfied_by()` method can be used to determine with a given + version number is included in the set described by the version + restrictions:: + + >>> v.satisfied_by('1.1') + True + >>> v.satisfied_by('1.4') + True + >>> v.satisfied_by('1.0') + False + >>> v.satisfied_by('4444.4') + False + >>> v.satisfied_by('1555.1b3') + False + + `VersionPredicate` is flexible in accepting extra whitespace:: + + >>> v = VersionPredicate(' pat( == 0.1 ) ') + >>> v.name + 'pat' + >>> v.satisfied_by('0.1') + True + >>> v.satisfied_by('0.2') + False + + If any version numbers passed in do not conform to the + restrictions of `StrictVersion`, a `ValueError` is raised:: + + >>> v = VersionPredicate('p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)') + Traceback (most recent call last): + ... + ValueError: invalid version number '1.2zb3' + + It the module or package name given does not conform to what's + allowed as a legal module or package name, `ValueError` is + raised:: + + >>> v = VersionPredicate('foo-bar') + Traceback (most recent call last): + ... + ValueError: expected parenthesized list: '-bar' + + >>> v = VersionPredicate('foo bar (12.21)') + Traceback (most recent call last): + ... + ValueError: expected parenthesized list: 'bar (12.21)' + + """ + + def __init__(self, versionPredicateStr): + """Parse a version predicate string. + """ + # Fields: + # name: package name + # pred: list of (comparison string, StrictVersion) + + versionPredicateStr = versionPredicateStr.strip() + if not versionPredicateStr: + raise ValueError("empty package restriction") + match = re_validPackage.match(versionPredicateStr) + if not match: + raise ValueError("bad package name in %r" % versionPredicateStr) + self.name, paren = match.groups() + paren = paren.strip() + if paren: + match = re_paren.match(paren) + if not match: + raise ValueError("expected parenthesized list: %r" % paren) + str = match.groups()[0] + self.pred = [splitUp(aPred) for aPred in str.split(",")] + if not self.pred: + raise ValueError("empty parenthesized list in %r" + % versionPredicateStr) + else: + self.pred=[] + + def __str__(self): + if self.pred: + seq = [cond + " " + str(ver) for cond, ver in self.pred] + return self.name + " (" + ", ".join(seq) + ")" + else: + return self.name + + def satisfied_by(self, version): + """True if version is compatible with all the predicates in self. + The parameter version must be acceptable to the StrictVersion + constructor. It may be either a string or StrictVersion. + """ + for cond, ver in self.pred: + if not compmap[cond](version, ver): + return False + return True + + +_provision_rx = None + +def split_provision(value): + """Return the name and optional version number of a provision. + + The version number, if given, will be returned as a `StrictVersion` + instance, otherwise it will be `None`. + + >>> split_provision('mypkg') + ('mypkg', None) + >>> split_provision(' mypkg( 1.2 ) ') + ('mypkg', StrictVersion ('1.2')) + """ + global _provision_rx + if _provision_rx is None: + _provision_rx = re.compile( + "([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$") + value = value.strip() + m = _provision_rx.match(value) if not m: raise ValueError("illegal provides specification: %r" % value) + ver = m.group(2) or None + if ver: + ver = distutils.version.StrictVersion(ver) + return m.group(1), ver -- cgit v1.2.1 From 8bd5d36635206ea66de6ec7389408175ea9dea89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 21 Mar 2005 20:56:35 +0000 Subject: Add the upload command. Make all dist commands register their outputs with the distribution object. --- command/bdist_dumb.py | 5 +++-- command/bdist_rpm.py | 3 +++ command/bdist_wininst.py | 2 ++ command/sdist.py | 1 + command/upload.py | 4 ++++ dist.py | 5 +++++ 6 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 command/upload.py diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 7f498c83..59435516 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -117,8 +117,9 @@ class bdist_dumb (Command): ensure_relative(install.install_base)) # Make the archive - self.make_archive(pseudoinstall_root, - self.format, root_dir=archive_root) + filename = self.make_archive(pseudoinstall_root, + self.format, root_dir=archive_root) + self.distribution.dist_files.append(('bdist_dumb', filename)) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 8eaaff32..09bfa43f 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -297,12 +297,14 @@ class bdist_rpm (Command): # Make a source distribution and copy to SOURCES directory with # optional icon. + saved_dist_files = self.distributuion.dist_files[:] sdist = self.reinitialize_command('sdist') if self.use_bzip2: sdist.formats = ['bztar'] else: sdist.formats = ['gztar'] self.run_command('sdist') + self.distribution.dist_files = saved_dist_files source = sdist.get_archive_files()[0] source_dir = rpm_dir['SOURCES'] @@ -355,6 +357,7 @@ class bdist_rpm (Command): assert len(rpms) == 1, \ "unexpected number of RPM files found: %s" % rpms self.move_file(rpms[0], self.dist_dir) + self.distribution.dist_files.append(('bdist_rpm', rpms[0])) if debuginfo: self.move_file(debuginfo[0], self.dist_dir) # run() diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 9b45cf35..a0335fee 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -162,6 +162,8 @@ class bdist_wininst (Command): root_dir=self.bdist_dir) # create an exe containing the zip-file self.create_exe(arcname, fullname, self.bitmap) + self.distribution.dist_files.append(('bdist_wininst', + self.get_installer_filename())) # remove the zip-file again log.debug("removing temporary file '%s'", arcname) os.remove(arcname) diff --git a/command/sdist.py b/command/sdist.py index fe6c9139..8b88f22f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -449,6 +449,7 @@ class sdist (Command): for fmt in self.formats: file = self.make_archive(base_name, fmt, base_dir=base_dir) archive_files.append(file) + self.distribution.dist_files.append(('sdist',file)) self.archive_files = archive_files diff --git a/command/upload.py b/command/upload.py new file mode 100644 index 00000000..d14f1776 --- /dev/null +++ b/command/upload.py @@ -0,0 +1,4 @@ +"""distutils.command.upload + +Implements the Distutils 'upload' subcommand (upload package to PyPI).""" + diff --git a/dist.py b/dist.py index f015874b..c7ec3830 100644 --- a/dist.py +++ b/dist.py @@ -177,6 +177,11 @@ Common commands: (see '--help-commands' for more) # command_options = { command_name : { option : (source, value) } } self.command_options = {} + # 'dist_files' is the list of (command, file) that have been created + # by any dist commands run so far. This is filled regardless + # of whether the run is dry or not. + self.dist_files = [] + # These options are really the business of various commands, rather # than of the Distribution itself. We provide aliases for them in # Distribution as a convenience to the developer. -- cgit v1.2.1 From b793563cf2adb97f0fc84300d8e9f550125c22b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 21 Mar 2005 21:00:59 +0000 Subject: Actually add the implementation of the command. --- command/upload.py | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/command/upload.py b/command/upload.py index d14f1776..aea2e4b9 100644 --- a/command/upload.py +++ b/command/upload.py @@ -2,3 +2,153 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" +from distutils.errors import * +from distutils.core import Command +from md5 import md5 +from distutils.sysconfig import get_python_version +from distutils import log +import os +import platform +import ConfigParser +import httplib +import base64 +import urlparse +import cStringIO as StringIO + +class upload(Command): + + description = "upload binary package to PyPI" + + DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server'), + ] + boolean_options = ['show-response'] + + def initialize_options(self): + self.username = '' + self.password = '' + self.repository = '' + self.show_response = 0 + + def finalize_options(self): + if os.environ.has_key('HOME'): + rc = os.path.join(os.environ['HOME'], '.pypirc') + if os.path.exists(rc): + self.announce('Using PyPI login from %s' % rc) + config = ConfigParser.ConfigParser({ + 'username':'', + 'password':'', + 'repository':''}) + config.read(rc) + if not self.repository: + self.repository = config.get('server-login', 'repository') + if not self.username: + self.username = config.get('server-login', 'username') + if not self.password: + self.password = config.get('server-login', 'password') + if not self.repository: + self.repository = self.DEFAULT_REPOSITORY + + def run(self): + if not self.distribution.dist_files: + raise DistutilsOptionError("No dist file created in earlier command") + for command, filename in self.distribution.dist_files: + self.upload_file(command, filename) + + def upload_file(self, command, filename): + + # Fill in the data + content = open(filename).read() + data = { + ':action':'file_upload', + 'name':self.distribution.get_name(), + 'version':self.distribution.get_version(), + 'content':(os.path.basename(filename),content), + 'filetype':command, + 'pyversion':get_python_version(), + 'md5_digest':md5(content).hexdigest(), + } + comment = '' + if command == 'bdist_rpm': + dist, version, id = platform.dist() + if dist: + comment = 'built for %s %s' % (dist, version) + elif command == 'bdist_dumb': + comment = 'built for %s' % platform.platform(terse=1) + data['comment'] = comment + + # set up the authentication + auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = '\n--' + boundary + end_boundary = sep_boundary + '--' + body = StringIO.StringIO() + for key, value in data.items(): + # handle multiple entries for the same name + if type(value) != type([]): + value = [value] + for value in value: + if type(value) is tuple: + fn = ';filename="%s"' % value[0] + value = value[1] + else: + fn = "" + value = str(value) + body.write(sep_boundary) + body.write('\nContent-Disposition: form-data; name="%s"'%key) + body.write(fn) + body.write("\n\n") + body.write(value) + if value and value[-1] == '\r': + body.write('\n') # write an extra newline (lurve Macs) + body.write(end_boundary) + body.write("\n") + body = body.getvalue() + + self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) + + # build the Request + # We can't use urllib2 since we need to send the Basic + # auth right with the first request + schema, netloc, url, params, query, fragments = \ + urlparse.urlparse(self.repository) + assert not params and not query and not fragments + if schema == 'http': + http = httplib.HTTPConnection(netloc) + elif schema == 'https': + http = httplib.HTTPSConnection(netloc) + else: + raise AssertionError, "unsupported schema "+schema + + data = '' + loglevel = log.INFO + try: + http.connect() + http.putrequest("POST", url) + http.putheader('Content-type', + 'multipart/form-data; boundary=%s'%boundary) + http.putheader('Content-length', str(len(body))) + http.putheader('Authorization', auth) + http.endheaders() + http.send(body) + except socket.error, e: + self.announce(e.msg, log.ERROR) + return + + r = http.getresponse() + if r.status == 200: + self.announce('Server response (%s): %s' % (r.status, r.reason), + log.INFO) + else: + self.announce('Upload failed (%s): %s' % (r.status, r.reason), + log.INFO) + if self.show_response: + print '-'*75, r.read(), '-'*75 + -- cgit v1.2.1 From a2e09746ac9aee2d9002e43e37fd3b8c71379e9f Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 22 Mar 2005 05:43:18 +0000 Subject: fix Python style guide conformance --- versionpredicate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versionpredicate.py b/versionpredicate.py index 8922b137..62d89f8b 100644 --- a/versionpredicate.py +++ b/versionpredicate.py @@ -117,7 +117,7 @@ class VersionPredicate: raise ValueError("empty parenthesized list in %r" % versionPredicateStr) else: - self.pred=[] + self.pred = [] def __str__(self): if self.pred: -- cgit v1.2.1 From ae0315024ea6c6e2a36267ec78988ab4cc8d8047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 22 Mar 2005 15:51:14 +0000 Subject: Upload GPG signature. --- command/upload.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/command/upload.py b/command/upload.py index aea2e4b9..ad0e2304 100644 --- a/command/upload.py +++ b/command/upload.py @@ -4,9 +4,10 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" from distutils.errors import * from distutils.core import Command -from md5 import md5 from distutils.sysconfig import get_python_version +from distutils.spawn import spawn from distutils import log +from md5 import md5 import os import platform import ConfigParser @@ -26,14 +27,17 @@ class upload(Command): "url of repository [default: %s]" % DEFAULT_REPOSITORY), ('show-response', None, 'display full response text from server'), + ('sign', 's', + 'sign files to upload using gpg'), ] - boolean_options = ['show-response'] + boolean_options = ['show-response', 'sign'] def initialize_options(self): self.username = '' self.password = '' self.repository = '' self.show_response = 0 + self.sign = False def finalize_options(self): if os.environ.has_key('HOME'): @@ -61,11 +65,16 @@ class upload(Command): self.upload_file(command, filename) def upload_file(self, command, filename): + # Sign if requested + if self.sign: + spawn(("gpg", "--sign", "-a", filename), + dry_run=self.dry_run) # Fill in the data content = open(filename).read() data = { ':action':'file_upload', + 'protcol_version':'1', 'name':self.distribution.get_name(), 'version':self.distribution.get_version(), 'content':(os.path.basename(filename),content), @@ -82,6 +91,10 @@ class upload(Command): comment = 'built for %s' % platform.platform(terse=1) data['comment'] = comment + if self.sign: + data['gpg_signature'] = (os.path.basename(filename) + ".asc", + open(filename+".asc").read()) + # set up the authentication auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() @@ -148,7 +161,7 @@ class upload(Command): log.INFO) else: self.announce('Upload failed (%s): %s' % (r.status, r.reason), - log.INFO) + log.ERROR) if self.show_response: print '-'*75, r.read(), '-'*75 -- cgit v1.2.1 From a383851362fc46f5c023273f1465611203271c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 22 Mar 2005 20:32:41 +0000 Subject: Don't set the Python version for sdist uploads. --- command/upload.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/upload.py b/command/upload.py index ad0e2304..a62da78a 100644 --- a/command/upload.py +++ b/command/upload.py @@ -89,6 +89,8 @@ class upload(Command): comment = 'built for %s %s' % (dist, version) elif command == 'bdist_dumb': comment = 'built for %s' % platform.platform(terse=1) + elif command == 'sdist': + data['pyversion'] = '' data['comment'] = comment if self.sign: -- cgit v1.2.1 From ec9d7b79d687c8b9954d807c838993912c26499d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 22 Mar 2005 22:23:29 +0000 Subject: Fix registration of output file. --- command/bdist_wininst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index a0335fee..5a83226b 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -163,7 +163,7 @@ class bdist_wininst (Command): # create an exe containing the zip-file self.create_exe(arcname, fullname, self.bitmap) self.distribution.dist_files.append(('bdist_wininst', - self.get_installer_filename())) + self.get_installer_filename(fullname))) # remove the zip-file again log.debug("removing temporary file '%s'", arcname) os.remove(arcname) -- cgit v1.2.1 From ef5d51cd28dc7219e12a42241e132ea86d371392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 22 Mar 2005 23:02:54 +0000 Subject: Make the signature detached. --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index a62da78a..6a4e3b33 100644 --- a/command/upload.py +++ b/command/upload.py @@ -67,7 +67,7 @@ class upload(Command): def upload_file(self, command, filename): # Sign if requested if self.sign: - spawn(("gpg", "--sign", "-a", filename), + spawn(("gpg", "--detach-sign", "-a", filename), dry_run=self.dry_run) # Fill in the data -- cgit v1.2.1 From af6b7fae4c6a9b798e43aa544897d23b2ed013b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 23 Mar 2005 18:54:36 +0000 Subject: Make dist_files a triple, with the Python target version included, so that bdist_wininst can specify 'any'. --- command/bdist_dumb.py | 8 +++++++- command/bdist_rpm.py | 13 ++++++++++++- command/bdist_wininst.py | 6 +++++- command/sdist.py | 2 +- command/upload.py | 11 ++++------- dist.py | 12 +++++++++--- 6 files changed, 38 insertions(+), 14 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 59435516..ccba0095 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -13,6 +13,7 @@ from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import create_tree, remove_tree, ensure_relative from distutils.errors import * +from distutils.sysconfig import get_python_version from distutils import log class bdist_dumb (Command): @@ -119,7 +120,12 @@ class bdist_dumb (Command): # Make the archive filename = self.make_archive(pseudoinstall_root, self.format, root_dir=archive_root) - self.distribution.dist_files.append(('bdist_dumb', filename)) + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + self.distribution.dist_files.append(('bdist_dumb', pyversion, + filename)) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 09bfa43f..c7b94e81 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -15,6 +15,7 @@ from distutils.debug import DEBUG from distutils.util import get_platform from distutils.file_util import write_file from distutils.errors import * +from distutils.sysconfig import get_python_version from distutils import log class bdist_rpm (Command): @@ -346,6 +347,10 @@ class bdist_rpm (Command): srpms = glob.glob(os.path.join(rpm_dir['SRPMS'], "*.rpm")) assert len(srpms) == 1, \ "unexpected number of SRPM files found: %s" % srpms + dist_file = ('bdist_rpm', '', + os.path.join(self.dist_dir, + os.path.basename(srpms[0]))) + self.distribution.dist_files.append(dist_file) self.move_file(srpms[0], self.dist_dir) if not self.source_only: @@ -356,9 +361,15 @@ class bdist_rpm (Command): rpms.remove(debuginfo[0]) assert len(rpms) == 1, \ "unexpected number of RPM files found: %s" % rpms + dist_file = ('bdist_rpm', get_python_version(), + os.path.join(self.dist_dir, + os.path.basename(rpms[0]))) + self.distribution.dist_files.append(dist_file) self.move_file(rpms[0], self.dist_dir) - self.distribution.dist_files.append(('bdist_rpm', rpms[0])) if debuginfo: + dist_file = ('bdist_rpm', get_python_version(), + os.path.join(self.dist_dir, + os.path.basename(debuginfo[0]))) self.move_file(debuginfo[0], self.dist_dir) # run() diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 5a83226b..49afca04 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -162,7 +162,11 @@ class bdist_wininst (Command): root_dir=self.bdist_dir) # create an exe containing the zip-file self.create_exe(arcname, fullname, self.bitmap) - self.distribution.dist_files.append(('bdist_wininst', + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + self.distribution.dist_files.append(('bdist_wininst', pyversion, self.get_installer_filename(fullname))) # remove the zip-file again log.debug("removing temporary file '%s'", arcname) diff --git a/command/sdist.py b/command/sdist.py index 8b88f22f..3dfe6f21 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -449,7 +449,7 @@ class sdist (Command): for fmt in self.formats: file = self.make_archive(base_name, fmt, base_dir=base_dir) archive_files.append(file) - self.distribution.dist_files.append(('sdist',file)) + self.distribution.dist_files.append(('sdist', '', file)) self.archive_files = archive_files diff --git a/command/upload.py b/command/upload.py index 6a4e3b33..266e9b1e 100644 --- a/command/upload.py +++ b/command/upload.py @@ -4,7 +4,6 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" from distutils.errors import * from distutils.core import Command -from distutils.sysconfig import get_python_version from distutils.spawn import spawn from distutils import log from md5 import md5 @@ -61,10 +60,10 @@ class upload(Command): def run(self): if not self.distribution.dist_files: raise DistutilsOptionError("No dist file created in earlier command") - for command, filename in self.distribution.dist_files: - self.upload_file(command, filename) + for command, pyversion, filename in self.distribution.dist_files: + self.upload_file(command, pyversion, filename) - def upload_file(self, command, filename): + def upload_file(self, command, pyversion, filename): # Sign if requested if self.sign: spawn(("gpg", "--detach-sign", "-a", filename), @@ -79,7 +78,7 @@ class upload(Command): 'version':self.distribution.get_version(), 'content':(os.path.basename(filename),content), 'filetype':command, - 'pyversion':get_python_version(), + 'pyversion':pyversion, 'md5_digest':md5(content).hexdigest(), } comment = '' @@ -89,8 +88,6 @@ class upload(Command): comment = 'built for %s %s' % (dist, version) elif command == 'bdist_dumb': comment = 'built for %s' % platform.platform(terse=1) - elif command == 'sdist': - data['pyversion'] = '' data['comment'] = comment if self.sign: diff --git a/dist.py b/dist.py index c7ec3830..ff49886d 100644 --- a/dist.py +++ b/dist.py @@ -177,9 +177,15 @@ Common commands: (see '--help-commands' for more) # command_options = { command_name : { option : (source, value) } } self.command_options = {} - # 'dist_files' is the list of (command, file) that have been created - # by any dist commands run so far. This is filled regardless - # of whether the run is dry or not. + # 'dist_files' is the list of (command, pyversion, file) that + # have been created by any dist commands run so far. This is + # filled regardless of whether the run is dry or not. pyversion + # gives sysconfig.get_python_version() if the dist file is + # specific to a Python version, 'any' if it is good for all + # Python versions on the target platform, and '' for a source + # file. pyversion should not be used to specify minimum or + # maximum required Python versions; use the metainfo for that + # instead. self.dist_files = [] # These options are really the business of various commands, rather -- cgit v1.2.1 From ae46d6954d01ffda7b4fcc5f966bcb9fc9ccb197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 23 Mar 2005 22:16:22 +0000 Subject: Make SRPMs pyversion 'any'. --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index c7b94e81..7968d14c 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -347,7 +347,7 @@ class bdist_rpm (Command): srpms = glob.glob(os.path.join(rpm_dir['SRPMS'], "*.rpm")) assert len(srpms) == 1, \ "unexpected number of SRPM files found: %s" % srpms - dist_file = ('bdist_rpm', '', + dist_file = ('bdist_rpm', 'any', os.path.join(self.dist_dir, os.path.basename(srpms[0]))) self.distribution.dist_files.append(dist_file) -- cgit v1.2.1 From 17df9acf1e48891c158caa9db83cb972377f81b6 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 24 Mar 2005 07:00:05 +0000 Subject: minor cleanup --- command/bdist_rpm.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 7968d14c..98621af9 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -348,31 +348,30 @@ class bdist_rpm (Command): assert len(srpms) == 1, \ "unexpected number of SRPM files found: %s" % srpms dist_file = ('bdist_rpm', 'any', - os.path.join(self.dist_dir, - os.path.basename(srpms[0]))) + self._dist_path(srpms[0])) self.distribution.dist_files.append(dist_file) self.move_file(srpms[0], self.dist_dir) if not self.source_only: rpms = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*.rpm")) - debuginfo = glob.glob(os.path.join(rpm_dir['RPMS'], \ + debuginfo = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*debuginfo*.rpm")) if debuginfo: rpms.remove(debuginfo[0]) assert len(rpms) == 1, \ "unexpected number of RPM files found: %s" % rpms dist_file = ('bdist_rpm', get_python_version(), - os.path.join(self.dist_dir, - os.path.basename(rpms[0]))) + self._dist_path(rpms[0]) self.distribution.dist_files.append(dist_file) self.move_file(rpms[0], self.dist_dir) if debuginfo: dist_file = ('bdist_rpm', get_python_version(), - os.path.join(self.dist_dir, - os.path.basename(debuginfo[0]))) + self._dist_path(debuginfo[0]) self.move_file(debuginfo[0], self.dist_dir) # run() + def _dist_path(self, path): + return os.path.join(self.dist_dir, os.path.basename(path)) def _make_spec_file(self): """Generate the text of an RPM spec file and return it as a -- cgit v1.2.1 From 5c887b6d88da0ef921b9cef12269eeb321a9275f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 24 Mar 2005 19:40:57 +0000 Subject: Add missing socket import --- command/upload.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/upload.py b/command/upload.py index 266e9b1e..d1d5ec60 100644 --- a/command/upload.py +++ b/command/upload.py @@ -8,6 +8,7 @@ from distutils.spawn import spawn from distutils import log from md5 import md5 import os +import socket import platform import ConfigParser import httplib -- cgit v1.2.1 From 31e173cb4da568d9a71645bfd46d416b6d0d406b Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Mon, 28 Mar 2005 01:05:48 +0000 Subject: Two lines in this file had unbalanced parentheses -- couldn't possibly work (SyntaxErrors at compile time). I slammed in what looked like the obvious fixes, but someone who understands this file should check my work. --- command/bdist_rpm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 98621af9..4bc00c3a 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -361,12 +361,12 @@ class bdist_rpm (Command): assert len(rpms) == 1, \ "unexpected number of RPM files found: %s" % rpms dist_file = ('bdist_rpm', get_python_version(), - self._dist_path(rpms[0]) + self._dist_path(rpms[0])) self.distribution.dist_files.append(dist_file) self.move_file(rpms[0], self.dist_dir) if debuginfo: dist_file = ('bdist_rpm', get_python_version(), - self._dist_path(debuginfo[0]) + self._dist_path(debuginfo[0])) self.move_file(debuginfo[0], self.dist_dir) # run() -- cgit v1.2.1 From 462bfbfa899143d92476cc8b1e2e86df2b4b7432 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Mon, 28 Mar 2005 01:08:02 +0000 Subject: Whitespace normalization. --- command/upload.py | 9 ++++----- tests/test_dist.py | 2 +- tests/test_versionpredicate.py | 2 +- versionpredicate.py | 6 +++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/command/upload.py b/command/upload.py index d1d5ec60..7b08336c 100644 --- a/command/upload.py +++ b/command/upload.py @@ -133,7 +133,7 @@ class upload(Command): schema, netloc, url, params, query, fragments = \ urlparse.urlparse(self.repository) assert not params and not query and not fragments - if schema == 'http': + if schema == 'http': http = httplib.HTTPConnection(netloc) elif schema == 'https': http = httplib.HTTPSConnection(netloc) @@ -145,7 +145,7 @@ class upload(Command): try: http.connect() http.putrequest("POST", url) - http.putheader('Content-type', + http.putheader('Content-type', 'multipart/form-data; boundary=%s'%boundary) http.putheader('Content-length', str(len(body))) http.putheader('Authorization', auth) @@ -157,11 +157,10 @@ class upload(Command): r = http.getresponse() if r.status == 200: - self.announce('Server response (%s): %s' % (r.status, r.reason), + self.announce('Server response (%s): %s' % (r.status, r.reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (r.status, r.reason), + self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: print '-'*75, r.read(), '-'*75 - diff --git a/tests/test_dist.py b/tests/test_dist.py index 7675fbfa..4d2a7cdf 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -175,7 +175,7 @@ class MetadataTestCase(unittest.TestCase): {"name": "package", "version": "1.0", "obsoletes": ["my.pkg (splat)"]}) - + def format_metadata(self, dist): sio = StringIO.StringIO() dist.metadata.write_pkg_file(sio) diff --git a/tests/test_versionpredicate.py b/tests/test_versionpredicate.py index 44cb41ee..8a60dbe8 100644 --- a/tests/test_versionpredicate.py +++ b/tests/test_versionpredicate.py @@ -6,4 +6,4 @@ import distutils.versionpredicate import doctest def test_suite(): - return doctest.DocTestSuite(distutils.versionpredicate) + return doctest.DocTestSuite(distutils.versionpredicate) diff --git a/versionpredicate.py b/versionpredicate.py index 62d89f8b..ba8b6c02 100644 --- a/versionpredicate.py +++ b/versionpredicate.py @@ -101,10 +101,10 @@ class VersionPredicate: versionPredicateStr = versionPredicateStr.strip() if not versionPredicateStr: - raise ValueError("empty package restriction") + raise ValueError("empty package restriction") match = re_validPackage.match(versionPredicateStr) if not match: - raise ValueError("bad package name in %r" % versionPredicateStr) + raise ValueError("bad package name in %r" % versionPredicateStr) self.name, paren = match.groups() paren = paren.strip() if paren: @@ -114,7 +114,7 @@ class VersionPredicate: str = match.groups()[0] self.pred = [splitUp(aPred) for aPred in str.split(",")] if not self.pred: - raise ValueError("empty parenthesized list in %r" + raise ValueError("empty parenthesized list in %r" % versionPredicateStr) else: self.pred = [] -- cgit v1.2.1 From 1ad5ca4f1e967b5623b43600007d51dc9c758d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Walter=20D=C3=B6rwald?= Date: Thu, 31 Mar 2005 13:57:38 +0000 Subject: Since PyPI only accepts UTF-8 encoded data now, make sure that the data is properly encoded and include the encoding in the Content-Type header. --- command/register.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/register.py b/command/register.py index 6e9a8d42..dec9aa2b 100644 --- a/command/register.py +++ b/command/register.py @@ -254,7 +254,7 @@ Your selection [default 1]: ''', if type(value) != type([]): value = [value] for value in value: - value = str(value) + value = unicode(value).encode("utf-8") body.write(sep_boundary) body.write('\nContent-Disposition: form-data; name="%s"'%key) body.write("\n\n") @@ -267,7 +267,7 @@ Your selection [default 1]: ''', # build the Request headers = { - 'Content-type': 'multipart/form-data; boundary=%s'%boundary, + 'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary, 'Content-length': str(len(body)) } req = urllib2.Request(self.repository, body, headers) -- cgit v1.2.1 From 7edd1145871db2e4b444114c99e326829edbbc12 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Fri, 15 Apr 2005 06:17:20 +0000 Subject: typo fix, thanks Jeremy Sanders --- command/bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 4bc00c3a..738e3f72 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -298,7 +298,7 @@ class bdist_rpm (Command): # Make a source distribution and copy to SOURCES directory with # optional icon. - saved_dist_files = self.distributuion.dist_files[:] + saved_dist_files = self.distribution.dist_files[:] sdist = self.reinitialize_command('sdist') if self.use_bzip2: sdist.formats = ['bztar'] -- cgit v1.2.1 From 1133a2e0542e3f3e82544ec98095d3799a5921cd Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sun, 24 Apr 2005 22:26:38 +0000 Subject: Introduced EXTRA_CFLAGS as an environment variable used by the Makefile. Meant to be used for flags that change binary compatibility. Distutils was tweaked to also use the variable if used during compilation of the interpreter. --- sysconfig.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index aae0f27c..1bd62097 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -146,8 +146,9 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - (cc, cxx, opt, basecflags, ccshared, ldshared, so_ext) = \ - get_config_vars('CC', 'CXX', 'OPT', 'BASECFLAGS', 'CCSHARED', 'LDSHARED', 'SO') + (cc, cxx, opt, extra_cflags, basecflags, ccshared, ldshared, so_ext) = \ + get_config_vars('CC', 'CXX', 'OPT', 'EXTRA_CFLAGS', 'BASECFLAGS', + 'CCSHARED', 'LDSHARED', 'SO') if os.environ.has_key('CC'): cc = os.environ['CC'] @@ -171,7 +172,7 @@ def customize_compiler(compiler): opt = opt + ' ' + os.environ['CPPFLAGS'] ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] - cc_cmd = cc + ' ' + opt + cc_cmd = ' '.join(str(x) for x in (cc, opt, extra_cflags) if x) compiler.set_executables( preprocessor=cpp, compiler=cc_cmd, -- cgit v1.2.1 From a9aaf538b9b30a25b67802887f107ae74c35eba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 25 Apr 2005 07:14:03 +0000 Subject: Make parse_makefile fallback to environment variables if nothing is defined in the makefile. Get CFLAGS from the Makefile, instead of getting OPT, BASE_CFLAGS and EXTRA_CFLAGS individually. --- sysconfig.py | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 1bd62097..0be5b6b7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -146,8 +146,8 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - (cc, cxx, opt, extra_cflags, basecflags, ccshared, ldshared, so_ext) = \ - get_config_vars('CC', 'CXX', 'OPT', 'EXTRA_CFLAGS', 'BASECFLAGS', + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext) = \ + get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SO') if os.environ.has_key('CC'): @@ -162,17 +162,15 @@ def customize_compiler(compiler): cpp = cc + " -E" # not always if os.environ.has_key('LDFLAGS'): ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if basecflags: - opt = basecflags + ' ' + opt if os.environ.has_key('CFLAGS'): - opt = opt + ' ' + os.environ['CFLAGS'] + cflags = opt + ' ' + os.environ['CFLAGS'] ldshared = ldshared + ' ' + os.environ['CFLAGS'] if os.environ.has_key('CPPFLAGS'): cpp = cpp + ' ' + os.environ['CPPFLAGS'] - opt = opt + ' ' + os.environ['CPPFLAGS'] + cflags = cflags + ' ' + os.environ['CPPFLAGS'] ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] - cc_cmd = ' '.join(str(x) for x in (cc, opt, extra_cflags) if x) + cc_cmd = cc + ' ' + cflags compiler.set_executables( preprocessor=cpp, compiler=cc_cmd, @@ -278,25 +276,20 @@ def parse_makefile(fn, g=None): m = _findvar1_rx.search(value) or _findvar2_rx.search(value) if m: n = m.group(1) + found = True if done.has_key(n): - after = value[m.end():] - value = value[:m.start()] + str(done[n]) + after - if "$" in after: - notdone[name] = value - else: - try: value = int(value) - except ValueError: - done[name] = string.strip(value) - else: - done[name] = value - del notdone[name] + item = str(done[n]) elif notdone.has_key(n): # get it on a subsequent round - pass + found = False + elif os.environ.has_key(n): + # do it like make: fall back to environment + item = os.environ[n] else: - done[n] = "" + done[n] = item = "" + if found: after = value[m.end():] - value = value[:m.start()] + after + value = value[:m.start()] + item + after if "$" in after: notdone[name] = value else: -- cgit v1.2.1 From 3b756201e5bd88f1dac1206c49864e5a286a3497 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Wed, 18 May 2005 02:18:09 +0000 Subject: Whitespace normalization. --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 0be5b6b7..9bdbb16a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -147,7 +147,7 @@ def customize_compiler(compiler): """ if compiler.compiler_type == "unix": (cc, cxx, opt, cflags, ccshared, ldshared, so_ext) = \ - get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', + get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SO') if os.environ.has_key('CC'): -- cgit v1.2.1 From 7c683cbb4a9a15e15bc522c90e018c1fb5707418 Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Thu, 7 Jul 2005 15:36:20 +0000 Subject: Fix "upload" command garbling and truncating files on Windows. If it's a binary file, use 'rb'! --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 7b08336c..3b5a0fc9 100644 --- a/command/upload.py +++ b/command/upload.py @@ -71,7 +71,7 @@ class upload(Command): dry_run=self.dry_run) # Fill in the data - content = open(filename).read() + content = open(filename,'rb').read() data = { ':action':'file_upload', 'protcol_version':'1', -- cgit v1.2.1 From 72fb0955fb8a81449d0a0924faa805fec3fa221f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 7 Aug 2005 20:51:04 +0000 Subject: Patch #827386: Support absolute source paths in msvccompiler.py. Backported to 2.4. --- msvccompiler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/msvccompiler.py b/msvccompiler.py index b94d35f1..85d515b2 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -269,6 +269,8 @@ class MSVCCompiler (CCompiler) : obj_names = [] for src_name in source_filenames: (base, ext) = os.path.splitext (src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / if ext not in self.src_extensions: # Better to raise an exception instead of silently continuing # and later complain about sources and targets having -- cgit v1.2.1 From 1cae8e7c527625752262f72ade9f12e0dcf92214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 24 Aug 2005 14:55:22 +0000 Subject: Patch #1167716: Support Unicode filenames in mkpath. Fixes #1121494. Will backport to 2.4. --- dir_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dir_util.py b/dir_util.py index 7f145037..2248b607 100644 --- a/dir_util.py +++ b/dir_util.py @@ -31,7 +31,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): global _path_created # Detect a common bug -- name is None - if type(name) is not StringType: + if not isinstance(name, StringTypes): raise DistutilsInternalError, \ "mkpath: 'name' must be a string (got %r)" % (name,) -- cgit v1.2.1 From 326aece3c808d1f4a0cec3f37fcca0d5ce4f1223 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Fri, 26 Aug 2005 15:20:46 +0000 Subject: Whitespace normalization (via reindent.py). --- dir_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dir_util.py b/dir_util.py index 2248b607..43994db3 100644 --- a/dir_util.py +++ b/dir_util.py @@ -31,7 +31,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): global _path_created # Detect a common bug -- name is None - if not isinstance(name, StringTypes): + if not isinstance(name, StringTypes): raise DistutilsInternalError, \ "mkpath: 'name' must be a string (got %r)" % (name,) -- cgit v1.2.1 From 7792b51671f50059dd24fffbac70a38b487a0d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 8 Jan 2006 10:48:54 +0000 Subject: Patch #1299675: Pass metadata in upload. --- command/upload.py | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/command/upload.py b/command/upload.py index 3b5a0fc9..62767a34 100644 --- a/command/upload.py +++ b/command/upload.py @@ -70,17 +70,41 @@ class upload(Command): spawn(("gpg", "--detach-sign", "-a", filename), dry_run=self.dry_run) - # Fill in the data + # Fill in the data - send all the meta-data in case we need to + # register a new release content = open(filename,'rb').read() + meta = self.distribution.metadata data = { - ':action':'file_upload', - 'protcol_version':'1', - 'name':self.distribution.get_name(), - 'version':self.distribution.get_version(), - 'content':(os.path.basename(filename),content), - 'filetype':command, - 'pyversion':pyversion, - 'md5_digest':md5(content).hexdigest(), + # action + ':action': 'file_upload', + 'protcol_version': '1', + + # identify release + 'name': meta.get_name(), + 'version': meta.get_version(), + + # file content + 'content': (os.path.basename(filename),content), + 'filetype': command, + 'pyversion': pyversion, + 'md5_digest': md5(content).hexdigest(), + + # additional meta-data + 'metadata_version' : '1.0', + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), } comment = '' if command == 'bdist_rpm': -- cgit v1.2.1 From 2849b2318b136f0a419cd040cc516100eef3a762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 20 Feb 2006 12:15:15 +0000 Subject: Let the SDK setup override distutils logic. --- msvccompiler.py | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index 85d515b2..b65e5286 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -214,21 +214,31 @@ class MSVCCompiler (CCompiler) : self.initialized = False def initialize(self): - self.__paths = self.get_msvc_paths("path") - - if len (self.__paths) == 0: - raise DistutilsPlatformError, \ - ("Python was built with version %s of Visual Studio, " - "and extensions need to be built with the same " - "version of the compiler, but it isn't installed." % self.__version) - - self.cc = self.find_exe("cl.exe") - self.linker = self.find_exe("link.exe") - self.lib = self.find_exe("lib.exe") - self.rc = self.find_exe("rc.exe") # resource compiler - self.mc = self.find_exe("mc.exe") # message compiler - self.set_path_env_var('lib') - self.set_path_env_var('include') + self.__paths = [] + if os.environ.has_key("MSSdk") and self.find_exe("cl.exe"): + # Assume that the SDK set up everything alright; don't try to be + # smarter + self.cc = "cl.exe" + self.linker = "link.exe" + self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" + else: + self.__paths = self.get_msvc_paths("path") + + if len (self.__paths) == 0: + raise DistutilsPlatformError, \ + ("Python was built with version %s of Visual Studio, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." % self.__version) + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + self.set_path_env_var('lib') + self.set_path_env_var('include') # extend the MSVC path with the current path try: -- cgit v1.2.1 From 7a7a2802bcbe8f867b3f0bfd75949ee1a4b9fc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 20 Feb 2006 12:26:58 +0000 Subject: Detect Win64 builds. --- msvccompiler.py | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index b65e5286..aefcf987 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -172,6 +172,20 @@ def get_build_version(): # else we don't know what version of the compiler this is return None +def get_build_architecture(): + """Return the processor architecture. + + Possible results are "Intel", "Itanium", or "AMD64". + """ + + prefix = " bit (" + i = string.find(sys.version, prefix) + if i == -1: + return "Intel" + j = string.find(sys.version, ")", i) + return sys.version[i+len(prefix):j] + + class MSVCCompiler (CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, @@ -206,11 +220,19 @@ class MSVCCompiler (CCompiler) : def __init__ (self, verbose=0, dry_run=0, force=0): CCompiler.__init__ (self, verbose, dry_run, force) self.__version = get_build_version() - if self.__version >= 7: - self.__root = r"Software\Microsoft\VisualStudio" - self.__macros = MacroExpander(self.__version) + self.__arch = get_build_architecture() + if self.__arch == "Intel": + # x86 + if self.__version >= 7: + self.__root = r"Software\Microsoft\VisualStudio" + self.__macros = MacroExpander(self.__version) + else: + self.__root = r"Software\Microsoft\Devstudio" + self.__product = "Visual Studio version %s" % self.__version else: - self.__root = r"Software\Microsoft\Devstudio" + # Win64. Assume this was built with the platform SDK + self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) + self.initialized = False def initialize(self): @@ -228,9 +250,9 @@ class MSVCCompiler (CCompiler) : if len (self.__paths) == 0: raise DistutilsPlatformError, \ - ("Python was built with version %s of Visual Studio, " + ("Python was built with %s, " "and extensions need to be built with the same " - "version of the compiler, but it isn't installed." % self.__version) + "version of the compiler, but it isn't installed." % self.__product) self.cc = self.find_exe("cl.exe") self.linker = self.find_exe("link.exe") @@ -249,10 +271,17 @@ class MSVCCompiler (CCompiler) : os.environ['path'] = string.join(self.__paths, ';') self.preprocess_options = None - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', - '/Z7', '/D_DEBUG'] + if self.__arch == "Intel": + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', + '/Z7', '/D_DEBUG'] + else: + # Win64 + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', + '/Z7', '/D_DEBUG'] self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] if self.__version >= 7: -- cgit v1.2.1 From a1548e541bd1b4b0e54ff449520e393bbfd00a39 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Mon, 20 Feb 2006 21:42:18 +0000 Subject: Whitespace normalization. --- msvccompiler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index aefcf987..f88f3652 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -184,7 +184,7 @@ def get_build_architecture(): return "Intel" j = string.find(sys.version, ")", i) return sys.version[i+len(prefix):j] - + class MSVCCompiler (CCompiler) : @@ -232,7 +232,7 @@ class MSVCCompiler (CCompiler) : else: # Win64. Assume this was built with the platform SDK self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) - + self.initialized = False def initialize(self): @@ -281,7 +281,7 @@ class MSVCCompiler (CCompiler) : self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , '/DNDEBUG'] self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', - '/Z7', '/D_DEBUG'] + '/Z7', '/D_DEBUG'] self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] if self.__version >= 7: -- cgit v1.2.1 From d35ccaa6b4a5e7da4b2888f5f59f3407ce34ae6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 5 Mar 2006 13:36:04 +0000 Subject: Import bdist_msi --- command/bdist_msi.py | 639 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 639 insertions(+) create mode 100644 command/bdist_msi.py diff --git a/command/bdist_msi.py b/command/bdist_msi.py new file mode 100644 index 00000000..6c0982d4 --- /dev/null +++ b/command/bdist_msi.py @@ -0,0 +1,639 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2005 Martin v. Löwis +# Licensed to PSF under a Contributor Agreement. +# The bdist_wininst command proper +# based on bdist_wininst +""" +Implements the bdist_msi command. +""" + +import sys, os, string +from distutils.core import Command +from distutils.util import get_platform +from distutils.dir_util import remove_tree +from distutils.sysconfig import get_python_version +from distutils.version import StrictVersion +from distutils.errors import DistutilsOptionError +from distutils import log +import msilib +from msilib import schema, sequence, uisample +from msilib import Directory, Feature, Dialog, add_data + +class PyDialog(Dialog): + """Dialog class with a fixed layout: controls at the top, then a ruler, + then a list of buttons: back, next, cancel. Optionally a bitmap at the + left.""" + def __init__(self, *args, **kw): + """Dialog(database, name, x, y, w, h, attributes, title, first, + default, cancel, bitmap=true)""" + Dialog.__init__(self, *args) + ruler = self.h - 36 + bmwidth = 152*ruler/328 + #if kw.get("bitmap", True): + # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin") + self.line("BottomLine", 0, ruler, self.w, 0) + + def title(self, title): + "Set the title text of the dialog at the top." + # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix, + # text, in VerdanaBold10 + self.text("Title", 15, 10, 320, 60, 0x30003, + r"{\VerdanaBold10}%s" % title) + + def back(self, title, next, name = "Back", active = 1): + """Add a back button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next) + + def cancel(self, title, next, name = "Cancel", active = 1): + """Add a cancel button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next) + + def next(self, title, next, name = "Next", active = 1): + """Add a Next button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next) + + def xbutton(self, name, title, next, xpos): + """Add a button with a given title, the tab-next button, + its name in the Control table, giving its x position; the + y-position is aligned with the other buttons. + + Return the button, so that events can be associated""" + return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next) + +class bdist_msi (Command): + + description = "create a Microsoft Installer (.msi) binary distribution" + + user_options = [('bdist-dir=', None, + "temporary directory for creating the distribution"), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('target-version=', None, + "require a specific python version" + + " on the target system"), + ('no-target-compile', 'c', + "do not compile .py to .pyc on the target system"), + ('no-target-optimize', 'o', + "do not compile .py to .pyo (optimized)" + "on the target system"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('install-script=', None, + "basename of installation script to be run after" + "installation or before deinstallation"), + ('pre-install-script=', None, + "Fully qualified filename of a script to be run before " + "any files are installed. This script need not be in the " + "distribution"), + ] + + boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', + 'skip-build'] + + def initialize_options (self): + self.bdist_dir = None + self.keep_temp = 0 + self.no_target_compile = 0 + self.no_target_optimize = 0 + self.target_version = None + self.dist_dir = None + self.skip_build = 0 + self.install_script = None + self.pre_install_script = None + + def finalize_options (self): + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'msi') + short_version = get_python_version() + if self.target_version: + if not self.skip_build and self.distribution.has_ext_modules()\ + and self.target_version != short_version: + raise DistutilsOptionError, \ + "target version can only be %s, or the '--skip_build'" \ + " option must be specified" % (short_version,) + else: + self.target_version = short_version + + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + + if self.pre_install_script: + raise DistutilsOptionError, "the pre-install-script feature is not yet implemented" + + if self.install_script: + for script in self.distribution.scripts: + if self.install_script == os.path.basename(script): + break + else: + raise DistutilsOptionError, \ + "install_script '%s' not found in scripts" % \ + self.install_script + self.install_script_key = None + # finalize_options() + + + def run (self): + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.prefix = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + + install_lib = self.reinitialize_command('install_lib') + # we do not want to include pyc or pyo files + install_lib.compile = 0 + install_lib.optimize = 0 + + if self.distribution.has_ext_modules(): + # If we are building an installer for a Python version other + # than the one we are currently running, then we need to ensure + # our build_lib reflects the other Python version rather than ours. + # Note that for target_version!=sys.version, we must have skipped the + # build step, so there is no issue with enforcing the build of this + # version. + target_version = self.target_version + if not target_version: + assert self.skip_build, "Should have already checked this" + target_version = sys.version[0:3] + plat_specifier = ".%s-%s" % (get_platform(), target_version) + build = self.get_finalized_command('build') + build.build_lib = os.path.join(build.build_base, + 'lib' + plat_specifier) + + log.info("installing to %s", self.bdist_dir) + install.ensure_finalized() + + # avoid warning of 'install_lib' about installing + # into a directory not in sys.path + sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) + + install.run() + + del sys.path[0] + + self.mkpath(self.dist_dir) + fullname = self.distribution.get_fullname() + installer_name = self.get_installer_filename(fullname) + installer_name = os.path.abspath(installer_name) + if os.path.exists(installer_name): os.unlink(installer_name) + + metadata = self.distribution.metadata + author = metadata.author + if not author: + author = metadata.maintainer + if not author: + author = "UNKNOWN" + version = metadata.get_version() + # ProductVersion must be strictly numeric + # XXX need to deal with prerelease versions + sversion = "%d.%d.%d" % StrictVersion(version).version + # Prefix ProductName with Python x.y, so that + # it sorts together with the other Python packages + # in Add-Remove-Programs (APR) + product_name = "Python %s %s" % (self.target_version, + self.distribution.get_fullname()) + self.db = msilib.init_database(installer_name, schema, + product_name, msilib.gen_uuid(), + sversion, author) + msilib.add_tables(self.db, sequence) + props = [('DistVersion', version)] + email = metadata.author_email or metadata.maintainer_email + if email: + props.append(("ARPCONTACT", email)) + if metadata.url: + props.append(("ARPURLINFOABOUT", metadata.url)) + if props: + add_data(self.db, 'Property', props) + + self.add_find_python() + self.add_files() + self.add_scripts() + self.add_ui() + self.db.Commit() + + if hasattr(self.distribution, 'dist_files'): + self.distribution.dist_files.append(('bdist_msi', self.target_version, fullname)) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) + + def add_files(self): + db = self.db + cab = msilib.CAB("distfiles") + f = Feature(db, "default", "Default Feature", "Everything", 1, directory="TARGETDIR") + f.set_current() + rootdir = os.path.abspath(self.bdist_dir) + root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir") + db.Commit() + todo = [root] + while todo: + dir = todo.pop() + for file in os.listdir(dir.absolute): + afile = os.path.join(dir.absolute, file) + if os.path.isdir(afile): + newdir = Directory(db, cab, dir, file, file, "%s|%s" % (dir.make_short(file), file)) + todo.append(newdir) + else: + key = dir.add_file(file) + if file==self.install_script: + if self.install_script_key: + raise DistutilsOptionError, "Multiple files with name %s" % file + self.install_script_key = '[#%s]' % key + + cab.commit(db) + + def add_find_python(self): + """Adds code to the installer to compute the location of Python. + Properties PYTHON.MACHINE, PYTHON.USER, PYTHONDIR and PYTHON will be set + in both the execute and UI sequences; PYTHONDIR will be set from + PYTHON.USER if defined, else from PYTHON.MACHINE. + PYTHON is PYTHONDIR\python.exe""" + install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % self.target_version + add_data(self.db, "RegLocator", + [("python.machine", 2, install_path, None, 2), + ("python.user", 1, install_path, None, 2)]) + add_data(self.db, "AppSearch", + [("PYTHON.MACHINE", "python.machine"), + ("PYTHON.USER", "python.user")]) + add_data(self.db, "CustomAction", + [("PythonFromMachine", 51+256, "PYTHONDIR", "[PYTHON.MACHINE]"), + ("PythonFromUser", 51+256, "PYTHONDIR", "[PYTHON.USER]"), + ("PythonExe", 51+256, "PYTHON", "[PYTHONDIR]\\python.exe"), + ("InitialTargetDir", 51+256, "TARGETDIR", "[PYTHONDIR]")]) + add_data(self.db, "InstallExecuteSequence", + [("PythonFromMachine", "PYTHON.MACHINE", 401), + ("PythonFromUser", "PYTHON.USER", 402), + ("PythonExe", None, 403), + ("InitialTargetDir", 'TARGETDIR=""', 404), + ]) + add_data(self.db, "InstallUISequence", + [("PythonFromMachine", "PYTHON.MACHINE", 401), + ("PythonFromUser", "PYTHON.USER", 402), + ("PythonExe", None, 403), + ("InitialTargetDir", 'TARGETDIR=""', 404), + ]) + + def add_scripts(self): + if self.install_script: + add_data(self.db, "CustomAction", + [("install_script", 50, "PYTHON", self.install_script_key)]) + add_data(self.db, "InstallExecuteSequence", + [("install_script", "NOT Installed", 6800)]) + if self.pre_install_script: + scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") + f = open(scriptfn, "w") + # The batch file will be executed with [PYTHON], so that %1 + # is the path to the Python interpreter; %0 will be the path + # of the batch file. + # rem =""" + # %1 %0 + # exit + # """ + # + f.write('rem ="""\n%1 %0\nexit\n"""\n') + f.write(open(self.pre_install_script).read()) + f.close() + add_data(self.db, "Binary", + [("PreInstall", msilib.Binary(scriptfn)) + ]) + add_data(self.db, "CustomAction", + [("PreInstall", 2, "PreInstall", None) + ]) + add_data(self.db, "InstallExecuteSequence", + [("PreInstall", "NOT Installed", 450)]) + + + def add_ui(self): + db = self.db + x = y = 50 + w = 370 + h = 300 + title = "[ProductName] Setup" + + # see "Dialog Style Bits" + modal = 3 # visible | modal + modeless = 1 # visible + track_disk_space = 32 + + # UI customization properties + add_data(db, "Property", + # See "DefaultUIFont Property" + [("DefaultUIFont", "DlgFont8"), + # See "ErrorDialog Style Bit" + ("ErrorDialog", "ErrorDlg"), + ("Progress1", "Install"), # modified in maintenance type dlg + ("Progress2", "installs"), + ("MaintenanceForm_Action", "Repair"), + # possible values: ALL, JUSTME + ("WhichUsers", "ALL") + ]) + + # Fonts, see "TextStyle Table" + add_data(db, "TextStyle", + [("DlgFont8", "Tahoma", 9, None, 0), + ("DlgFontBold8", "Tahoma", 8, None, 1), #bold + ("VerdanaBold10", "Verdana", 10, None, 1), + ("VerdanaRed9", "Verdana", 9, 255, 0), + ]) + + # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table" + # Numbers indicate sequence; see sequence.py for how these action integrate + add_data(db, "InstallUISequence", + [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140), + ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141), + # In the user interface, assume all-users installation if privileged. + ("SelectDirectoryDlg", "Not Installed", 1230), + # XXX no support for resume installations yet + #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240), + ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), + ("ProgressDlg", None, 1280)]) + + add_data(db, 'ActionText', uisample.ActionText) + add_data(db, 'UIText', uisample.UIText) + ##################################################################### + # Standard dialogs: FatalError, UserExit, ExitDialog + fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + fatal.title("[ProductName] Installer ended prematurely") + fatal.back("< Back", "Finish", active = 0) + fatal.cancel("Cancel", "Back", active = 0) + fatal.text("Description1", 15, 70, 320, 80, 0x30003, + "[ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again.") + fatal.text("Description2", 15, 155, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c=fatal.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Exit") + + user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + user_exit.title("[ProductName] Installer was interrupted") + user_exit.back("< Back", "Finish", active = 0) + user_exit.cancel("Cancel", "Back", active = 0) + user_exit.text("Description1", 15, 70, 320, 80, 0x30003, + "[ProductName] setup was interrupted. Your system has not been modified. " + "To install this program at a later time, please run the installation again.") + user_exit.text("Description2", 15, 155, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c = user_exit.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Exit") + + exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + exit_dialog.title("Completing the [ProductName] Installer") + exit_dialog.back("< Back", "Finish", active = 0) + exit_dialog.cancel("Cancel", "Back", active = 0) + exit_dialog.text("Description", 15, 235, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c = exit_dialog.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Return") + + ##################################################################### + # Required dialog: FilesInUse, ErrorDlg + inuse = PyDialog(db, "FilesInUse", + x, y, w, h, + 19, # KeepModeless|Modal|Visible + title, + "Retry", "Retry", "Retry", bitmap=False) + inuse.text("Title", 15, 6, 200, 15, 0x30003, + r"{\DlgFontBold8}Files in Use") + inuse.text("Description", 20, 23, 280, 20, 0x30003, + "Some files that need to be updated are currently in use.") + inuse.text("Text", 20, 55, 330, 50, 3, + "The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.") + inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess", + None, None, None) + c=inuse.back("Exit", "Ignore", name="Exit") + c.event("EndDialog", "Exit") + c=inuse.next("Ignore", "Retry", name="Ignore") + c.event("EndDialog", "Ignore") + c=inuse.cancel("Retry", "Exit", name="Retry") + c.event("EndDialog","Retry") + + # See "Error Dialog". See "ICE20" for the required names of the controls. + error = Dialog(db, "ErrorDlg", + 50, 10, 330, 101, + 65543, # Error|Minimize|Modal|Visible + title, + "ErrorText", None, None) + error.text("ErrorText", 50,9,280,48,3, "") + #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None) + error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo") + error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes") + error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort") + error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel") + error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore") + error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk") + error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry") + + ##################################################################### + # Global "Query Cancel" dialog + cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title, + "No", "No", "No") + cancel.text("Text", 48, 15, 194, 30, 3, + "Are you sure you want to cancel [ProductName] installation?") + #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None, + # "py.ico", None, None) + c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No") + c.event("EndDialog", "Exit") + + c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes") + c.event("EndDialog", "Return") + + ##################################################################### + # Global "Wait for costing" dialog + costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title, + "Return", "Return", "Return") + costing.text("Text", 48, 15, 194, 30, 3, + "Please wait while the installer finishes determining your disk space requirements.") + c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None) + c.event("EndDialog", "Exit") + + ##################################################################### + # Preparation dialog: no user input except cancellation + prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title, + "Cancel", "Cancel", "Cancel") + prep.text("Description", 15, 70, 320, 40, 0x30003, + "Please wait while the Installer prepares to guide you through the installation.") + prep.title("Welcome to the [ProductName] Installer") + c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...") + c.mapping("ActionText", "Text") + c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None) + c.mapping("ActionData", "Text") + prep.back("Back", None, active=0) + prep.next("Next", None, active=0) + c=prep.cancel("Cancel", None) + c.event("SpawnDialog", "CancelDlg") + + ##################################################################### + # Target directory selection + seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title, + "Next", "Next", "Cancel") + seldlg.title("Select Destination Directory") + + version = sys.version[:3]+" " + seldlg.text("Hint", 15, 30, 300, 40, 3, + "The destination directory should contain a Python %sinstallation" % version) + + seldlg.back("< Back", None, active=0) + c = seldlg.next("Next >", "Cancel") + c.event("SetTargetPath", "TARGETDIR", order=1) + c.event("SpawnWaitDialog", "WaitForCostingDlg", order=2) + c.event("EndDialog", "Return", order=3) + + c = seldlg.cancel("Cancel", "DirectoryCombo") + c.event("SpawnDialog", "CancelDlg") + + seldlg.control("DirectoryCombo", "DirectoryCombo", 15, 70, 272, 80, 393219, + "TARGETDIR", None, "DirectoryList", None) + seldlg.control("DirectoryList", "DirectoryList", 15, 90, 308, 136, 3, "TARGETDIR", + None, "PathEdit", None) + seldlg.control("PathEdit", "PathEdit", 15, 230, 306, 16, 3, "TARGETDIR", None, "Next", None) + c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None) + c.event("DirectoryListUp", "0") + c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None) + c.event("DirectoryListNew", "0") + + ##################################################################### + # Disk cost + cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title, + "OK", "OK", "OK", bitmap=False) + cost.text("Title", 15, 6, 200, 15, 0x30003, + "{\DlgFontBold8}Disk Space Requirements") + cost.text("Description", 20, 20, 280, 20, 0x30003, + "The disk space required for the installation of the selected features.") + cost.text("Text", 20, 53, 330, 60, 3, + "The highlighted volumes (if any) do not have enough disk space " + "available for the currently selected features. You can either " + "remove some files from the highlighted volumes, or choose to " + "install less features onto local drive(s), or select different " + "destination drive(s).") + cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223, + None, "{120}{70}{70}{70}{70}", None, None) + cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return") + + ##################################################################### + # WhichUsers Dialog. Only available on NT, and for privileged users. + # This must be run before FindRelatedProducts, because that will + # take into account whether the previous installation was per-user + # or per-machine. We currently don't support going back to this + # dialog after "Next" was selected; to support this, we would need to + # find how to reset the ALLUSERS property, and how to re-run + # FindRelatedProducts. + # On Windows9x, the ALLUSERS property is ignored on the command line + # and in the Property table, but installer fails according to the documentation + # if a dialog attempts to set ALLUSERS. + whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title, + "AdminInstall", "Next", "Cancel") + whichusers.title("Select whether to install [ProductName] for all users of this computer.") + # A radio group with two options: allusers, justme + g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3, + "WhichUsers", "", "Next") + g.add("ALL", 0, 5, 150, 20, "Install for all users") + g.add("JUSTME", 0, 25, 150, 20, "Install just for me") + + whichusers.back("Back", None, active=0) + + c = whichusers.next("Next >", "Cancel") + c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) + c.event("EndDialog", "Return", order = 2) + + c = whichusers.cancel("Cancel", "AdminInstall") + c.event("SpawnDialog", "CancelDlg") + + ##################################################################### + # Installation Progress dialog (modeless) + progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title, + "Cancel", "Cancel", "Cancel", bitmap=False) + progress.text("Title", 20, 15, 200, 15, 0x30003, + "{\DlgFontBold8}[Progress1] [ProductName]") + progress.text("Text", 35, 65, 300, 30, 3, + "Please wait while the Installer [Progress2] [ProductName]. " + "This may take several minutes.") + progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:") + + c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...") + c.mapping("ActionText", "Text") + + #c=progress.text("ActionData", 35, 140, 300, 20, 3, None) + #c.mapping("ActionData", "Text") + + c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537, + None, "Progress done", None, None) + c.mapping("SetProgress", "Progress") + + progress.back("< Back", "Next", active=False) + progress.next("Next >", "Cancel", active=False) + progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg") + + ################################################################### + # Maintenance type: repair/uninstall + maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title, + "Next", "Next", "Cancel") + maint.title("Welcome to the [ProductName] Setup Wizard") + maint.text("BodyText", 15, 63, 330, 42, 3, + "Select whether you want to repair or remove [ProductName].") + g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3, + "MaintenanceForm_Action", "", "Next") + #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]") + g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]") + g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]") + + maint.back("< Back", None, active=False) + c=maint.next("Finish", "Cancel") + # Change installation: Change progress dialog to "Change", then ask + # for feature selection + #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1) + #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2) + + # Reinstall: Change progress dialog to "Repair", then invoke reinstall + # Also set list of reinstalled features to "ALL" + c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5) + c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6) + c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7) + c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8) + + # Uninstall: Change progress to "Remove", then invoke uninstall + # Also set list of removed features to "ALL" + c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11) + c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12) + c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13) + c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14) + + # Close dialog when maintenance action scheduled + c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20) + #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21) + + maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg") + + def get_installer_filename(self, fullname): + # Factored out to allow overriding in subclasses + installer_name = os.path.join(self.dist_dir, + "%s.win32-py%s.msi" % + (fullname, self.target_version)) + return installer_name -- cgit v1.2.1 From c24cffd76bbddf35362ec7ab850b25bea009dbf5 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 9 Mar 2006 01:15:05 +0000 Subject: Whitespace normalization. --- command/bdist_msi.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 6c0982d4..f05d66cb 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -216,10 +216,10 @@ class bdist_msi (Command): # Prefix ProductName with Python x.y, so that # it sorts together with the other Python packages # in Add-Remove-Programs (APR) - product_name = "Python %s %s" % (self.target_version, + product_name = "Python %s %s" % (self.target_version, self.distribution.get_fullname()) self.db = msilib.init_database(installer_name, schema, - product_name, msilib.gen_uuid(), + product_name, msilib.gen_uuid(), sversion, author) msilib.add_tables(self.db, sequence) props = [('DistVersion', version)] @@ -238,7 +238,7 @@ class bdist_msi (Command): self.db.Commit() if hasattr(self.distribution, 'dist_files'): - self.distribution.dist_files.append(('bdist_msi', self.target_version, fullname)) + self.distribution.dist_files.append(('bdist_msi', self.target_version, fullname)) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) @@ -265,14 +265,14 @@ class bdist_msi (Command): if self.install_script_key: raise DistutilsOptionError, "Multiple files with name %s" % file self.install_script_key = '[#%s]' % key - + cab.commit(db) def add_find_python(self): """Adds code to the installer to compute the location of Python. Properties PYTHON.MACHINE, PYTHON.USER, PYTHONDIR and PYTHON will be set in both the execute and UI sequences; PYTHONDIR will be set from - PYTHON.USER if defined, else from PYTHON.MACHINE. + PYTHON.USER if defined, else from PYTHON.MACHINE. PYTHON is PYTHONDIR\python.exe""" install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % self.target_version add_data(self.db, "RegLocator", @@ -497,7 +497,7 @@ class bdist_msi (Command): seldlg.title("Select Destination Directory") version = sys.version[:3]+" " - seldlg.text("Hint", 15, 30, 300, 40, 3, + seldlg.text("Hint", 15, 30, 300, 40, 3, "The destination directory should contain a Python %sinstallation" % version) seldlg.back("< Back", None, active=0) -- cgit v1.2.1 From 9e5258e007776422cab25de64ea538f0056f438f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 15 Mar 2006 04:33:54 +0000 Subject: Use relative imports in a few places where I noticed the need. (Ideally, all packages in Python 2.5 will use the relative import syntax for all their relative import needs.) --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 9bdbb16a..dc603be8 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -16,7 +16,7 @@ import re import string import sys -from errors import DistutilsPlatformError +from .errors import DistutilsPlatformError # These are needed in a couple of spots, so just compute them once. PREFIX = os.path.normpath(sys.prefix) -- cgit v1.2.1 From 68fb7a659320c41ca8949058210394de916a400f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 15 Mar 2006 04:58:47 +0000 Subject: Checkpoint. 218 tests are okay; 53 are failing. Done so far: - all classes are new-style (but ripping out classobject.[ch] isn't done) - int/int -> float - all exceptions must derive from BaseException - absolute import - 'as' and 'with' are keywords --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 9bdbb16a..dc603be8 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -16,7 +16,7 @@ import re import string import sys -from errors import DistutilsPlatformError +from .errors import DistutilsPlatformError # These are needed in a couple of spots, so just compute them once. PREFIX = os.path.normpath(sys.prefix) -- cgit v1.2.1 From 0a9342f5549e6078a83335bee511bed4dcd43480 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 15 Mar 2006 23:08:13 +0000 Subject: Instead of relative imports, use (implicitly) absolute ones. --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index dc603be8..0feb14aa 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -16,7 +16,7 @@ import re import string import sys -from .errors import DistutilsPlatformError +from distutils.errors import DistutilsPlatformError # These are needed in a couple of spots, so just compute them once. PREFIX = os.path.normpath(sys.prefix) -- cgit v1.2.1 From 79b3a97173af4724fe2c4b437a12ce760960f718 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 06:49:51 +0000 Subject: Get rid of a bunch more raw_input references --- command/register.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/command/register.py b/command/register.py index dec9aa2b..f8912621 100644 --- a/command/register.py +++ b/command/register.py @@ -13,6 +13,11 @@ import StringIO, ConfigParser from distutils.core import Command from distutils.errors import * +def raw_input(prompt): + sys.stdout.write(prompt) + sys.stdout.flush() + return sys.stdin.readline() + class register(Command): description = ("register the distribution with the Python package index") -- cgit v1.2.1 From 6811c9d7ad4341ea3769490502cd805ce400eb09 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:00:19 +0000 Subject: Remove apply() --- archive_util.py | 2 +- command/build_ext.py | 4 ++-- command/build_py.py | 8 ++++---- dir_util.py | 2 +- filelist.py | 2 +- util.py | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/archive_util.py b/archive_util.py index 6aa5e635..b725a14b 100644 --- a/archive_util.py +++ b/archive_util.py @@ -162,7 +162,7 @@ def make_archive (base_name, format, func = format_info[0] for (arg,val) in format_info[1]: kwargs[arg] = val - filename = apply(func, (base_name, base_dir), kwargs) + filename = func(base_name, base_dir, **kwargs) if root_dir is not None: log.debug("changing back to '%s'", save_cwd) diff --git a/command/build_ext.py b/command/build_ext.py index 4191c76c..6ea5d579 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -613,8 +613,8 @@ class build_ext (Command): # extensions in debug_mode are named 'module_d.pyd' under windows so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: - return apply(os.path.join, ext_path) + '_d' + so_ext - return apply(os.path.join, ext_path) + so_ext + return os.path.join(*ext_path) + '_d' + so_ext + return os.path.join(*ext_path) + so_ext def get_export_symbols (self, ext): """Return the list of symbols that a shared extension has to diff --git a/command/build_py.py b/command/build_py.py index 621bcb4a..3b7ec62c 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -154,7 +154,7 @@ class build_py (Command): if not self.package_dir: if path: - return apply(os.path.join, path) + return os.path.join(*path) else: return '' else: @@ -167,7 +167,7 @@ class build_py (Command): del path[-1] else: tail.insert(0, pdir) - return apply(os.path.join, tail) + return os.path.join(*tail) else: # Oops, got all the way through 'path' without finding a # match in package_dir. If package_dir defines a directory @@ -181,7 +181,7 @@ class build_py (Command): tail.insert(0, pdir) if tail: - return apply(os.path.join, tail) + return os.path.join(*tail) else: return '' @@ -335,7 +335,7 @@ class build_py (Command): def get_module_outfile (self, build_dir, package, module): outfile_path = [build_dir] + list(package) + [module + ".py"] - return apply(os.path.join, outfile_path) + return os.path.join(*outfile_path) def get_outputs (self, include_bytecode=1): diff --git a/dir_util.py b/dir_util.py index 43994db3..a4aff58e 100644 --- a/dir_util.py +++ b/dir_util.py @@ -204,7 +204,7 @@ def remove_tree (directory, verbose=0, dry_run=0): _build_cmdtuple(directory, cmdtuples) for cmd in cmdtuples: try: - apply(cmd[0], (cmd[1],)) + cmd[0](cmd[1]) # remove dir from cache if it's already there abspath = os.path.abspath(cmd[1]) if _path_created.has_key(abspath): diff --git a/filelist.py b/filelist.py index 43f9aaaf..4bbdd1f0 100644 --- a/filelist.py +++ b/filelist.py @@ -69,7 +69,7 @@ class FileList: sortable_files.sort() self.files = [] for sort_tuple in sortable_files: - self.files.append(apply(os.path.join, sort_tuple)) + self.files.append(os.path.join(*sort_tuple)) # -- Other miscellaneous utility methods --------------------------- diff --git a/util.py b/util.py index 387e9bdc..889bf136 100644 --- a/util.py +++ b/util.py @@ -95,7 +95,7 @@ def convert_path (pathname): paths.remove('.') if not paths: return os.curdir - return apply(os.path.join, paths) + return os.path.join(*paths) # convert_path () @@ -295,7 +295,7 @@ def execute (func, args, msg=None, verbose=0, dry_run=0): log.info(msg) if not dry_run: - apply(func, args) + func(*args) def strtobool (val): -- cgit v1.2.1 From be411d30ce0fadb76610cae16903da8a1f34f200 Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Mon, 27 Mar 2006 21:55:21 +0000 Subject: Patch #1459476: install PKG-INFO metadata alongside distutils-installed packages. --- command/install.py | 1 + command/install_egg_info.py | 75 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 command/install_egg_info.py diff --git a/command/install.py b/command/install.py index 77237611..453151d0 100644 --- a/command/install.py +++ b/command/install.py @@ -601,6 +601,7 @@ class install (Command): ('install_headers', has_headers), ('install_scripts', has_scripts), ('install_data', has_data), + ('install_egg_info', lambda self:True), ] # class install diff --git a/command/install_egg_info.py b/command/install_egg_info.py new file mode 100644 index 00000000..4e472d7c --- /dev/null +++ b/command/install_egg_info.py @@ -0,0 +1,75 @@ +"""distutils.command.install_egg_info + +Implements the Distutils 'install_egg_info' command, for installing +a package's PKG-INFO metadata.""" + + +from distutils.cmd import Command +from distutils import log, dir_util +import os, sys, re + +class install_egg_info(Command): + """Install an .egg-info file for the package""" + + description = "Install package's PKG-INFO metadata as an .egg-info file" + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ] + + def initialize_options(self): + self.install_dir = None + + def finalize_options(self): + self.set_undefined_options('install_lib',('install_dir','install_dir')) + basename = "%s-%s-py%s.egg-info" % ( + to_filename(safe_name(self.distribution.get_name())), + to_filename(safe_version(self.distribution.get_version())), + sys.version[:3] + ) + self.target = os.path.join(self.install_dir, basename) + self.outputs = [self.target] + + def run(self): + target = self.target + if os.path.isdir(target) and not os.path.islink(target): + dir_util.remove_tree(target, dry_run=self.dry_run) + elif os.path.exists(target): + self.execute(os.unlink,(self.target,),"Removing "+target) + log.info("Writing %s", target) + if not self.dry_run: + f = open(target, 'w') + self.distribution.metadata.write_pkg_file(f) + f.close() + + def get_outputs(self): + return self.outputs + + +# The following routines are taken from setuptools' pkg_resources module and +# can be replaced by importing them from pkg_resources once it is included +# in the stdlib. + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """Convert an arbitrary string to a standard version string + + Spaces become dots, and all other non-alphanumeric characters become + dashes, with runs of multiple dashes condensed to a single dash. + """ + version = version.replace(' ','.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-','_') -- cgit v1.2.1 From 9e248daf97b68077781fbfde44293dcaa0104172 Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Thu, 30 Mar 2006 02:12:14 +0000 Subject: Implementation for patch request #1457316: support --identity option for setup.py "upload" command. --- command/upload.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 62767a34..6f4ce81f 100644 --- a/command/upload.py +++ b/command/upload.py @@ -29,6 +29,7 @@ class upload(Command): 'display full response text from server'), ('sign', 's', 'sign files to upload using gpg'), + ('identity=', 'i', 'GPG identity used to sign files'), ] boolean_options = ['show-response', 'sign'] @@ -38,8 +39,13 @@ class upload(Command): self.repository = '' self.show_response = 0 self.sign = False + self.identity = None def finalize_options(self): + if self.identity and not self.sign: + raise DistutilsOptionError( + "Must use --sign for --identity to have meaning" + ) if os.environ.has_key('HOME'): rc = os.path.join(os.environ['HOME'], '.pypirc') if os.path.exists(rc): @@ -67,7 +73,10 @@ class upload(Command): def upload_file(self, command, pyversion, filename): # Sign if requested if self.sign: - spawn(("gpg", "--detach-sign", "-a", filename), + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity] + spawn(gpg_args, dry_run=self.dry_run) # Fill in the data - send all the meta-data in case we need to -- cgit v1.2.1 From c363bcdfa7ebe6448597a5dc7a323ee6887083e8 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Thu, 30 Mar 2006 12:59:11 +0000 Subject: whitespace normalisation --- command/install_egg_info.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/install_egg_info.py b/command/install_egg_info.py index 4e472d7c..c31ac296 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -26,7 +26,7 @@ class install_egg_info(Command): to_filename(safe_version(self.distribution.get_version())), sys.version[:3] ) - self.target = os.path.join(self.install_dir, basename) + self.target = os.path.join(self.install_dir, basename) self.outputs = [self.target] def run(self): @@ -40,7 +40,7 @@ class install_egg_info(Command): f = open(target, 'w') self.distribution.metadata.write_pkg_file(f) f.close() - + def get_outputs(self): return self.outputs -- cgit v1.2.1 From 723767238ee601ade352660d96ceb887a9437417 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 1 Apr 2006 07:46:54 +0000 Subject: Bug #1458017: make distutils.Log._log more forgiving when passing in msg strings with '%', but without format args. --- log.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/log.py b/log.py index cf3ee136..95d4c1c5 100644 --- a/log.py +++ b/log.py @@ -20,7 +20,12 @@ class Log: def _log(self, level, msg, args): if level >= self.threshold: - print msg % args + if not args: + # msg may contain a '%'. If args is empty, + # don't even try to string-format + print msg + else: + print msg % args sys.stdout.flush() def log(self, level, msg, *args): -- cgit v1.2.1 From 6cce429ad7ac2f6ee6d4a2b37817de4296a4a639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 10 Apr 2006 12:39:36 +0000 Subject: Patch #1429775: Link Python modules to libpython on linux if --enable-shared. Fixes #832799. --- command/build_ext.py | 13 +++++++++++-- sysconfig.py | 15 +++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 4191c76c..fbb74768 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -185,7 +185,9 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs - if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': + if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos' or \ + (sys.platform.startswith('linux') and + sysconfig.get_config_var('Py_ENABLE_SHARED')): if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", @@ -688,6 +690,13 @@ class build_ext (Command): # extensions, it is a reference to the original list return ext.libraries + [pythonlib, "m"] + extra else: - return ext.libraries + from distutils import sysconfig + if sysconfig.get_config_var('Py_ENABLE_SHARED'): + template = "python%d.%d" + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + return ext.libraries + [pythonlib] + else: + return ext.libraries # class build_ext diff --git a/sysconfig.py b/sysconfig.py index 0feb14aa..eafd49e7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -213,8 +213,8 @@ def parse_config_h(fp, g=None): """ if g is None: g = {} - define_rx = re.compile("#define ([A-Z][A-Z0-9_]+) (.*)\n") - undef_rx = re.compile("/[*] #undef ([A-Z][A-Z0-9_]+) [*]/\n") + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") # while 1: line = fp.readline() @@ -351,6 +351,17 @@ def _init_posix(): raise DistutilsPlatformError(my_msg) + # load the installed pyconfig.h: + try: + filename = get_config_h_filename() + parse_config_h(file(filename), g) + except IOError, msg: + my_msg = "invalid Python installation: unable to open %s" % filename + if hasattr(msg, "strerror"): + my_msg = my_msg + " (%s)" % msg.strerror + + raise DistutilsPlatformError(my_msg) + # On MacOSX we need to check the setting of the environment variable # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so # it needs to be compatible. -- cgit v1.2.1 From ea42db5cb8c6b047ed510fb937a4110b102b92ea Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Mon, 17 Apr 2006 09:22:35 +0000 Subject: Fix for a bug exposed by r45232: /path/to/uninstalled/python setup.py build_ext now failed with pyconfig.h not found. Prior to r45232 the above command did not look for pyconfig.h, but the bug is really in the look-up code: expecting to find it in os.curdir is a rather fragile idea. --- sysconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index eafd49e7..8d66cc2e 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -31,7 +31,7 @@ landmark = os.path.join(argv0_path, "Modules", "Setup") python_build = os.path.isfile(landmark) -del argv0_path, landmark +del landmark def get_python_version(): @@ -185,7 +185,7 @@ def customize_compiler(compiler): def get_config_h_filename(): """Return full pathname of installed pyconfig.h file.""" if python_build: - inc_dir = os.curdir + inc_dir = argv0_path else: inc_dir = get_python_inc(plat_specific=1) if get_python_version() < '2.2': -- cgit v1.2.1 From 8e8c8486409ae4fe7a424a541307265b07d82753 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Mon, 17 Apr 2006 14:43:30 +0000 Subject: disutils checks if MACOSX_DEPLOYMENT_TARGET is consistent with the value at configure time. The current check is too strict and doesn't allow building extensions that can only run on newer versions of the OS than the version python was build for, that is python build for 10.3 or later and an extension for 10.4. This patch relaxes this check. This turned out to be a reimplementation of patch 1193190. --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 8d66cc2e..72aa5117 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -372,7 +372,7 @@ def _init_posix(): if cur_target == '': cur_target = cfg_target os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) - if cfg_target != cur_target: + elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')): my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' % (cur_target, cfg_target)) raise DistutilsPlatformError(my_msg) -- cgit v1.2.1 From c4c2a4987d1138c0ae217701f994abf29cdd7148 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 10:40:58 +0000 Subject: Merge p3yk branch with the trunk up to revision 45595. This breaks a fair number of tests, all because of the codecs/_multibytecodecs issue described here (it's not a Py3K issue, just something Py3K discovers): http://mail.python.org/pipermail/python-dev/2006-April/064051.html Hye-Shik Chang promised to look for a fix, so no need to fix it here. The tests that are expected to break are: test_codecencodings_cn test_codecencodings_hk test_codecencodings_jp test_codecencodings_kr test_codecencodings_tw test_codecs test_multibytecodec This merge fixes an actual test failure (test_weakref) in this branch, though, so I believe merging is the right thing to do anyway. --- command/build_ext.py | 13 ++++++-- command/install.py | 1 + command/install_egg_info.py | 75 +++++++++++++++++++++++++++++++++++++++++++++ command/upload.py | 11 ++++++- log.py | 7 ++++- sysconfig.py | 21 ++++++++++--- 6 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 command/install_egg_info.py diff --git a/command/build_ext.py b/command/build_ext.py index 6ea5d579..57712521 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -185,7 +185,9 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs - if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': + if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos' or \ + (sys.platform.startswith('linux') and + sysconfig.get_config_var('Py_ENABLE_SHARED')): if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", @@ -688,6 +690,13 @@ class build_ext (Command): # extensions, it is a reference to the original list return ext.libraries + [pythonlib, "m"] + extra else: - return ext.libraries + from distutils import sysconfig + if sysconfig.get_config_var('Py_ENABLE_SHARED'): + template = "python%d.%d" + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + return ext.libraries + [pythonlib] + else: + return ext.libraries # class build_ext diff --git a/command/install.py b/command/install.py index 77237611..453151d0 100644 --- a/command/install.py +++ b/command/install.py @@ -601,6 +601,7 @@ class install (Command): ('install_headers', has_headers), ('install_scripts', has_scripts), ('install_data', has_data), + ('install_egg_info', lambda self:True), ] # class install diff --git a/command/install_egg_info.py b/command/install_egg_info.py new file mode 100644 index 00000000..c31ac296 --- /dev/null +++ b/command/install_egg_info.py @@ -0,0 +1,75 @@ +"""distutils.command.install_egg_info + +Implements the Distutils 'install_egg_info' command, for installing +a package's PKG-INFO metadata.""" + + +from distutils.cmd import Command +from distutils import log, dir_util +import os, sys, re + +class install_egg_info(Command): + """Install an .egg-info file for the package""" + + description = "Install package's PKG-INFO metadata as an .egg-info file" + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ] + + def initialize_options(self): + self.install_dir = None + + def finalize_options(self): + self.set_undefined_options('install_lib',('install_dir','install_dir')) + basename = "%s-%s-py%s.egg-info" % ( + to_filename(safe_name(self.distribution.get_name())), + to_filename(safe_version(self.distribution.get_version())), + sys.version[:3] + ) + self.target = os.path.join(self.install_dir, basename) + self.outputs = [self.target] + + def run(self): + target = self.target + if os.path.isdir(target) and not os.path.islink(target): + dir_util.remove_tree(target, dry_run=self.dry_run) + elif os.path.exists(target): + self.execute(os.unlink,(self.target,),"Removing "+target) + log.info("Writing %s", target) + if not self.dry_run: + f = open(target, 'w') + self.distribution.metadata.write_pkg_file(f) + f.close() + + def get_outputs(self): + return self.outputs + + +# The following routines are taken from setuptools' pkg_resources module and +# can be replaced by importing them from pkg_resources once it is included +# in the stdlib. + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """Convert an arbitrary string to a standard version string + + Spaces become dots, and all other non-alphanumeric characters become + dashes, with runs of multiple dashes condensed to a single dash. + """ + version = version.replace(' ','.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-','_') diff --git a/command/upload.py b/command/upload.py index 62767a34..6f4ce81f 100644 --- a/command/upload.py +++ b/command/upload.py @@ -29,6 +29,7 @@ class upload(Command): 'display full response text from server'), ('sign', 's', 'sign files to upload using gpg'), + ('identity=', 'i', 'GPG identity used to sign files'), ] boolean_options = ['show-response', 'sign'] @@ -38,8 +39,13 @@ class upload(Command): self.repository = '' self.show_response = 0 self.sign = False + self.identity = None def finalize_options(self): + if self.identity and not self.sign: + raise DistutilsOptionError( + "Must use --sign for --identity to have meaning" + ) if os.environ.has_key('HOME'): rc = os.path.join(os.environ['HOME'], '.pypirc') if os.path.exists(rc): @@ -67,7 +73,10 @@ class upload(Command): def upload_file(self, command, pyversion, filename): # Sign if requested if self.sign: - spawn(("gpg", "--detach-sign", "-a", filename), + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity] + spawn(gpg_args, dry_run=self.dry_run) # Fill in the data - send all the meta-data in case we need to diff --git a/log.py b/log.py index cf3ee136..95d4c1c5 100644 --- a/log.py +++ b/log.py @@ -20,7 +20,12 @@ class Log: def _log(self, level, msg, args): if level >= self.threshold: - print msg % args + if not args: + # msg may contain a '%'. If args is empty, + # don't even try to string-format + print msg + else: + print msg % args sys.stdout.flush() def log(self, level, msg, *args): diff --git a/sysconfig.py b/sysconfig.py index dc603be8..49536f0d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -31,7 +31,7 @@ landmark = os.path.join(argv0_path, "Modules", "Setup") python_build = os.path.isfile(landmark) -del argv0_path, landmark +del landmark def get_python_version(): @@ -185,7 +185,7 @@ def customize_compiler(compiler): def get_config_h_filename(): """Return full pathname of installed pyconfig.h file.""" if python_build: - inc_dir = os.curdir + inc_dir = argv0_path else: inc_dir = get_python_inc(plat_specific=1) if get_python_version() < '2.2': @@ -213,8 +213,8 @@ def parse_config_h(fp, g=None): """ if g is None: g = {} - define_rx = re.compile("#define ([A-Z][A-Z0-9_]+) (.*)\n") - undef_rx = re.compile("/[*] #undef ([A-Z][A-Z0-9_]+) [*]/\n") + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") # while 1: line = fp.readline() @@ -351,6 +351,17 @@ def _init_posix(): raise DistutilsPlatformError(my_msg) + # load the installed pyconfig.h: + try: + filename = get_config_h_filename() + parse_config_h(file(filename), g) + except IOError, msg: + my_msg = "invalid Python installation: unable to open %s" % filename + if hasattr(msg, "strerror"): + my_msg = my_msg + " (%s)" % msg.strerror + + raise DistutilsPlatformError(my_msg) + # On MacOSX we need to check the setting of the environment variable # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so # it needs to be compatible. @@ -361,7 +372,7 @@ def _init_posix(): if cur_target == '': cur_target = cfg_target os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) - if cfg_target != cur_target: + elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')): my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' % (cur_target, cfg_target)) raise DistutilsPlatformError(my_msg) -- cgit v1.2.1 From aca28c3c53d3a26bed0130d101ca283bd517f2b9 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 25 Apr 2006 00:34:50 +0000 Subject: Put break at correct level so *all* root HKEYs acutally get checked for an installed VC6. Otherwise only the first such tree gets checked and this warning doesn't get displayed. --- msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index f88f3652..d24d0ac6 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -618,7 +618,7 @@ class MSVCCompiler (CCompiler) : "but the expected registry settings are not present.\n" "You must at least run the Visual Studio GUI once " "so that these entries are created.") - break + break return [] def set_path_env_var(self, name): -- cgit v1.2.1 From a3c755c1adfd523cfd16d54e90c9ef1bb50f9915 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 28 Apr 2006 16:58:52 +0000 Subject: Bug #1478326: don't allow '/' in distutils.util.get_platform machine names since this value is used to name the build directory. --- util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/util.py b/util.py index 387e9bdc..061092b6 100644 --- a/util.py +++ b/util.py @@ -45,6 +45,7 @@ def get_platform (): osname = string.lower(osname) osname = string.replace(osname, '/', '') machine = string.replace(machine, ' ', '_') + machine = string.replace(machine, '/', '-') if osname[:5] == "linux": # At least on Linux/Intel, 'machine' is the processor -- -- cgit v1.2.1 From 9115c7ba3b080c10b08610e08a1e3bcec03352e7 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sat, 29 Apr 2006 11:31:35 +0000 Subject: Patch 1471883: --enable-universalsdk on Mac OS X --- sysconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 72aa5117..2a18d2be 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -366,8 +366,8 @@ def _init_posix(): # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so # it needs to be compatible. # If it isn't set we set it to the configure-time value - if sys.platform == 'darwin' and g.has_key('CONFIGURE_MACOSX_DEPLOYMENT_TARGET'): - cfg_target = g['CONFIGURE_MACOSX_DEPLOYMENT_TARGET'] + if sys.platform == 'darwin' and g.has_key('MACOSX_DEPLOYMENT_TARGET'): + cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') if cur_target == '': cur_target = cfg_target -- cgit v1.2.1 From 91bcdf7042985faabfa0efe6fb435a7d55dca67d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 30 Apr 2006 08:57:35 +0000 Subject: In stdlib, use hashlib instead of deprecated md5 and sha modules. --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 6f4ce81f..4a9ed398 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ from distutils.errors import * from distutils.core import Command from distutils.spawn import spawn from distutils import log -from md5 import md5 +from hashlib import md5 import os import socket import platform -- cgit v1.2.1 From e22f2ec8532760515e2f60c974283a7b9a841fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 1 May 2006 16:14:16 +0000 Subject: Rename parameters to match the documentation (which in turn matches Microsoft's documentation). Drop unused parameter in CAB.append. --- command/bdist_msi.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index f05d66cb..75db8773 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -1,5 +1,5 @@ # -*- coding: iso-8859-1 -*- -# Copyright (C) 2005 Martin v. Löwis +# Copyright (C) 2005, 2006 Martin v. Löwis # Licensed to PSF under a Contributor Agreement. # The bdist_wininst command proper # based on bdist_wininst @@ -16,7 +16,7 @@ from distutils.version import StrictVersion from distutils.errors import DistutilsOptionError from distutils import log import msilib -from msilib import schema, sequence, uisample +from msilib import schema, sequence, text from msilib import Directory, Feature, Dialog, add_data class PyDialog(Dialog): @@ -374,8 +374,8 @@ class bdist_msi (Command): ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), ("ProgressDlg", None, 1280)]) - add_data(db, 'ActionText', uisample.ActionText) - add_data(db, 'UIText', uisample.UIText) + add_data(db, 'ActionText', text.ActionText) + add_data(db, 'UIText', text.UIText) ##################################################################### # Standard dialogs: FatalError, UserExit, ExitDialog fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title, @@ -502,9 +502,9 @@ class bdist_msi (Command): seldlg.back("< Back", None, active=0) c = seldlg.next("Next >", "Cancel") - c.event("SetTargetPath", "TARGETDIR", order=1) - c.event("SpawnWaitDialog", "WaitForCostingDlg", order=2) - c.event("EndDialog", "Return", order=3) + c.event("SetTargetPath", "TARGETDIR", ordering=1) + c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=2) + c.event("EndDialog", "Return", ordering=3) c = seldlg.cancel("Cancel", "DirectoryCombo") c.event("SpawnDialog", "CancelDlg") @@ -561,7 +561,7 @@ class bdist_msi (Command): c = whichusers.next("Next >", "Cancel") c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) - c.event("EndDialog", "Return", order = 2) + c.event("EndDialog", "Return", ordering = 2) c = whichusers.cancel("Cancel", "AdminInstall") c.event("SpawnDialog", "CancelDlg") -- cgit v1.2.1 From eef192824d14409f513b9f2acb842aa6ed2b8f5c Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 23 May 2006 11:47:16 +0000 Subject: Disable linking extensions with -lpython2.5 for darwin. This should fix bug #1487105. --- command/build_ext.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index fbb74768..00f8a6b9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -689,6 +689,11 @@ class build_ext (Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib, "m"] + extra + + elif sys.platform == 'darwin': + # Don't use the default code below + return ext.libraries + else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): -- cgit v1.2.1 From 0856fd358badca3ac456fd79ba1442dfce7118f6 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 23 May 2006 12:01:11 +0000 Subject: Patch #1488098. This patchs makes it possible to create a universal build on OSX 10.4 and use the result to build extensions on 10.3. It also makes it possible to override the '-arch' and '-isysroot' compiler arguments for specific extensions. --- sysconfig.py | 15 +++++++++++++ unixccompiler.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- util.py | 48 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 2a18d2be..2ba3c4db 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -500,6 +500,21 @@ def get_config_vars(*args): _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + if sys.platform == 'darwin': + kernel_version = os.uname()[2] # Kernel version (8.4.3) + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # On Mac OS X before 10.4, check if -arch and -isysroot + # are in CFLAGS or LDFLAGS and remove them if they are. + # This is needed when building extensions on a 10.3 system + # using a universal build of python. + for key in ('LDFLAGS', 'BASECFLAGS'): + flags = _config_vars[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = re.sub('-isysroot [^ \t]* ', ' ', flags) + _config_vars[key] = flags + if args: vals = [] for name in args: diff --git a/unixccompiler.py b/unixccompiler.py index 56998c35..e612cfc2 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -42,6 +42,48 @@ from distutils import log # should just happily stuff them into the preprocessor/compiler/linker # options and carry on. +def _darwin_compiler_fixup(compiler_so, cc_args): + """ + This function will strip '-isysroot PATH' and '-arch ARCH' from the + compile flags if the user has specified one them in extra_compile_flags. + + This is needed because '-arch ARCH' adds another architecture to the + build, without a way to remove an architecture. Furthermore GCC will + barf if multiple '-isysroot' arguments are present. + """ + stripArch = stripSysroot = 0 + + compiler_so = list(compiler_so) + kernel_version = os.uname()[2] # 8.4.3 + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # OSX before 10.4.0, these don't support -arch and -isysroot at + # all. + stripArch = stripSysroot = True + else: + stripArch = '-arch' in cc_args + stripSysroot = '-isysroot' in cc_args + + if stripArch: + while 1: + try: + index = compiler_so.index('-arch') + # Strip this argument and the next one: + del compiler_so[index:index+2] + except ValueError: + break + + if stripSysroot: + try: + index = compiler_so.index('-isysroot') + # Strip this argument and the next one: + del compiler_so[index:index+1] + except ValueError: + pass + + return compiler_so + class UnixCCompiler(CCompiler): compiler_type = 'unix' @@ -108,8 +150,11 @@ class UnixCCompiler(CCompiler): raise CompileError, msg def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + compiler_so = self.compiler_so + if sys.platform == 'darwin': + compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs) try: - self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + + self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) except DistutilsExecError, msg: raise CompileError, msg @@ -172,7 +217,22 @@ class UnixCCompiler(CCompiler): else: linker = self.linker_so[:] if target_lang == "c++" and self.compiler_cxx: - linker[0] = self.compiler_cxx[0] + # skip over environment variable settings if /usr/bin/env + # is used to set up the linker's environment. + # This is needed on OSX. Note: this assumes that the + # normal and C++ compiler have the same environment + # settings. + i = 0 + if os.path.basename(linker[0]) == "env": + i = 1 + while '=' in linker[i]: + i = i + 1 + + linker[i] = self.compiler_cxx[i] + + if sys.platform == 'darwin': + linker = _darwin_compiler_fixup(linker, ld_args) + self.spawn(linker + ld_args) except DistutilsExecError, msg: raise LinkError, msg diff --git a/util.py b/util.py index 061092b6..623c41e2 100644 --- a/util.py +++ b/util.py @@ -67,6 +67,54 @@ def get_platform (): m = rel_re.match(release) if m: release = m.group() + elif osname[:6] == "darwin": + # + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # to. This makes the compatibility story a bit more sane because the + # machine is going to compile and link as if it were + # MACOSX_DEPLOYMENT_TARGET. + from distutils.sysconfig import get_config_vars + cfgvars = get_config_vars() + + macver = os.environ.get('MACOSX_DEPLOYMENT_TARGET') + if not macver: + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + + if not macver: + # Get the system version. Reading this plist is a documented + # way to get the system version (see the documentation for + # the Gestalt Manager) + try: + f = open('/System/Library/CoreServices/SystemVersion.plist') + except IOError: + # We're on a plain darwin box, fall back to the default + # behaviour. + pass + else: + m = re.search( + r'ProductUserVisibleVersion\s*' + + r'(.*?)', f.read()) + f.close() + if m is not None: + macver = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + + if macver: + from distutils.sysconfig import get_config_vars + release = macver + osname = "macosx" + + + if (release + '.') < '10.4.' and \ + get_config_vars().get('UNIVERSALSDK', '').strip(): + # The universal build will build fat binaries, but not on + # systems before 10.4 + machine = 'fat' + + elif machine in ('PowerPC', 'Power_Macintosh'): + # Pick a sane name for the PPC architecture. + machine = 'ppc' return "%s-%s-%s" % (osname, release, machine) -- cgit v1.2.1 From 57e5a01e0a790b43902fc19615d1a81ffa888c7a Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Tue, 23 May 2006 21:54:23 +0000 Subject: Whitespace normalization. --- unixccompiler.py | 2 +- util.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index e612cfc2..324819d4 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -220,7 +220,7 @@ class UnixCCompiler(CCompiler): # skip over environment variable settings if /usr/bin/env # is used to set up the linker's environment. # This is needed on OSX. Note: this assumes that the - # normal and C++ compiler have the same environment + # normal and C++ compiler have the same environment # settings. i = 0 if os.path.basename(linker[0]) == "env": diff --git a/util.py b/util.py index 623c41e2..cfcc6a95 100644 --- a/util.py +++ b/util.py @@ -69,10 +69,10 @@ def get_platform (): release = m.group() elif osname[:6] == "darwin": # - # For our purposes, we'll assume that the system version from - # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set # to. This makes the compatibility story a bit more sane because the - # machine is going to compile and link as if it were + # machine is going to compile and link as if it were # MACOSX_DEPLOYMENT_TARGET. from distutils.sysconfig import get_config_vars cfgvars = get_config_vars() @@ -97,7 +97,7 @@ def get_platform (): r'(.*?)', f.read()) f.close() if m is not None: - macver = '.'.join(m.group(1).split('.')[:2]) + macver = '.'.join(m.group(1).split('.')[:2]) # else: fall back to the default behaviour if macver: -- cgit v1.2.1 From e6ecfaefb58476548380b845c21cffc4a797fcc2 Mon Sep 17 00:00:00 2001 From: Bob Ippolito Date: Fri, 26 May 2006 14:07:23 +0000 Subject: Fix distutils so that libffi will cross-compile between darwin/x86 and darwin/ppc --- ccompiler.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 6dad757a..1349abeb 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,7 +15,6 @@ from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath from distutils.dep_util import newer_pairwise, newer_group -from distutils.sysconfig import python_build from distutils.util import split_quoted, execute from distutils import log @@ -368,7 +367,7 @@ class CCompiler: # Get the list of expected output (object) files objects = self.object_filenames(sources, - strip_dir=python_build, + strip_dir=0, output_dir=outdir) assert len(objects) == len(sources) @@ -475,8 +474,7 @@ class CCompiler: which source files can be skipped. """ # Get the list of expected output (object) files - objects = self.object_filenames(sources, strip_dir=python_build, - output_dir=output_dir) + objects = self.object_filenames(sources, output_dir=output_dir) assert len(objects) == len(sources) if self.force: -- cgit v1.2.1 From 9cc8b6b8399a7a3b3ce1c8fc308f3f01a39ef975 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 27 May 2006 19:21:47 +0000 Subject: Much-needed merge (using svnmerge.py this time) of trunk changes into p3yk. Inherits test_gzip/test_tarfile failures on 64-bit platforms from the trunk, but I don't want the merge to hang around too long (even though the regular p3yk-contributors are/have been busy with other things.) Merged revisions 45621-46490 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r45621 | george.yoshida | 2006-04-21 18:34:17 +0200 (Fri, 21 Apr 2006) | 2 lines Correct the grammar ........ r45622 | tim.peters | 2006-04-21 18:34:54 +0200 (Fri, 21 Apr 2006) | 2 lines Whitespace normalization. ........ r45624 | thomas.heller | 2006-04-21 18:48:56 +0200 (Fri, 21 Apr 2006) | 1 line Merge in changes from ctypes 0.9.9.6 upstream version. ........ r45625 | thomas.heller | 2006-04-21 18:51:04 +0200 (Fri, 21 Apr 2006) | 1 line Merge in changes from ctypes 0.9.9.6 upstream version. ........ r45630 | thomas.heller | 2006-04-21 20:29:17 +0200 (Fri, 21 Apr 2006) | 8 lines Documentation for ctypes. I think that 'generic operating system services' is the best category. Note that the Doc/lib/libctypes.latex file is generated from reST sources. You are welcome to make typo fixes, and I'll try to keep the reST sources in sync, but markup changes would be lost - they should be fixed in the tool that creates the latex file. The conversion script is external/ctypes/docs/manual/mkpydoc.py. ........ r45631 | tim.peters | 2006-04-21 23:18:10 +0200 (Fri, 21 Apr 2006) | 24 lines SF bug #1473760 TempFile can hang on Windows. Python 2.4 changed ntpath.abspath to do an import inside the function. As a result, due to Python's import lock, anything calling abspath on Windows (directly, or indirectly like tempfile.TemporaryFile) hung when it was called from a thread spawned as a side effect of importing a module. This is a depressingly frequent problem, and deserves a more general fix. I'm settling for a micro-fix here because this specific one accounts for a report of Zope Corp's ZEO hanging on Windows, and it was an odd way to change abspath to begin with (ntpath needs a different implementation depending on whether we're actually running on Windows, and the _obvious_ way to arrange for that is not to bury a possibly-failing import _inside_ the function). Note that if/when other micro-fixes of this kind get made, the new Lib/test/threaded_import_hangers.py is a convenient place to add tests for them. ........ r45634 | phillip.eby | 2006-04-21 23:53:37 +0200 (Fri, 21 Apr 2006) | 2 lines Guido wrote contextlib, not me, but thanks anyway. ;) ........ r45636 | andrew.kuchling | 2006-04-22 03:51:41 +0200 (Sat, 22 Apr 2006) | 1 line Typo fixes ........ r45638 | andrew.kuchling | 2006-04-22 03:58:40 +0200 (Sat, 22 Apr 2006) | 1 line Fix comment typo ........ r45639 | andrew.kuchling | 2006-04-22 04:06:03 +0200 (Sat, 22 Apr 2006) | 8 lines Make copy of test_mailbox.py. We'll still want to check the backward compatibility classes in the new mailbox.py that I'll be committing in a few minutes. One change has been made: the tests use len(mbox) instead of len(mbox.boxes). The 'boxes' attribute was never documented and contains some internal state that seems unlikely to have been useful. ........ r45640 | andrew.kuchling | 2006-04-22 04:32:43 +0200 (Sat, 22 Apr 2006) | 16 lines Add Gregory K. Johnson's revised version of mailbox.py (funded by the 2005 Summer of Code). The revision adds a number of new mailbox classes that support adding and removing messages; these classes also support mailbox locking and default to using email.Message instead of rfc822.Message. The old mailbox classes are largely left alone for backward compatibility. The exception is the Maildir class, which was present in the old module and now inherits from the new classes. The Maildir class's interface is pretty simple, though, so I think it'll be compatible with existing code. (The change to the NEWS file also adds a missing word to a different news item, which unfortunately required rewrapping the line.) ........ r45641 | tim.peters | 2006-04-22 07:52:59 +0200 (Sat, 22 Apr 2006) | 2 lines Whitespace normalization. ........ r45642 | neal.norwitz | 2006-04-22 08:07:46 +0200 (Sat, 22 Apr 2006) | 1 line Add libctypes as a dep ........ r45643 | martin.v.loewis | 2006-04-22 13:15:41 +0200 (Sat, 22 Apr 2006) | 1 line Fix more ssize_t problems. ........ r45644 | martin.v.loewis | 2006-04-22 13:40:03 +0200 (Sat, 22 Apr 2006) | 1 line Fix more ssize_t issues. ........ r45645 | george.yoshida | 2006-04-22 17:10:49 +0200 (Sat, 22 Apr 2006) | 2 lines Typo fixes ........ r45647 | martin.v.loewis | 2006-04-22 17:19:54 +0200 (Sat, 22 Apr 2006) | 1 line Port to Python 2.5. Drop .DEF file. Change output file names to .pyd. ........ r45648 | george.yoshida | 2006-04-22 17:27:14 +0200 (Sat, 22 Apr 2006) | 3 lines - add versionadded tag - make arbitrary arguments come last ........ r45649 | hyeshik.chang | 2006-04-22 17:48:15 +0200 (Sat, 22 Apr 2006) | 3 lines Remove $CJKCodecs$ RCS tags. The CJKCodecs isn't maintained outside anymore. ........ r45654 | greg.ward | 2006-04-23 05:47:58 +0200 (Sun, 23 Apr 2006) | 2 lines Update optparse to Optik 1.5.1. ........ r45658 | george.yoshida | 2006-04-23 11:27:10 +0200 (Sun, 23 Apr 2006) | 2 lines wrap SyntaxError with \exception{} ........ r45660 | ronald.oussoren | 2006-04-23 13:59:25 +0200 (Sun, 23 Apr 2006) | 6 lines Patch 1471925 - Weak linking support for OSX This patch causes several symbols in the socket and posix module to be weakly linked on OSX and disables usage of ftime on OSX. These changes make it possible to use a binary build on OSX 10.4 on a 10.3 system. ........ r45661 | ronald.oussoren | 2006-04-23 14:36:23 +0200 (Sun, 23 Apr 2006) | 5 lines Patch 1471761 - test for broken poll at runtime This patch checks if poll is broken when the select module is loaded instead of doing so at configure-time. This functionality is only active on Mac OS X. ........ r45662 | nick.coghlan | 2006-04-23 17:13:32 +0200 (Sun, 23 Apr 2006) | 1 line Add a Context Types section to parallel the Iterator Types section (uses the same terminology as the 2.5a1 implementation) ........ r45663 | nick.coghlan | 2006-04-23 17:14:37 +0200 (Sun, 23 Apr 2006) | 1 line Update contextlib documentation to use the same terminology as the module implementation ........ r45664 | gerhard.haering | 2006-04-23 17:24:26 +0200 (Sun, 23 Apr 2006) | 2 lines Updated the sqlite3 module to the external pysqlite 2.2.2 version. ........ r45666 | nick.coghlan | 2006-04-23 17:39:16 +0200 (Sun, 23 Apr 2006) | 1 line Update with statement documentation to use same terminology as 2.5a1 implementation ........ r45667 | nick.coghlan | 2006-04-23 18:05:04 +0200 (Sun, 23 Apr 2006) | 1 line Add a (very) brief mention of the with statement to the end of chapter 8 ........ r45668 | nick.coghlan | 2006-04-23 18:35:19 +0200 (Sun, 23 Apr 2006) | 1 line Take 2 on mentioning the with statement, this time without inadvertently killing the Unicode examples ........ r45669 | nick.coghlan | 2006-04-23 19:04:07 +0200 (Sun, 23 Apr 2006) | 1 line Backdated NEWS entry to record the implementation of PEP 338 for alpha 1 ........ r45670 | tim.peters | 2006-04-23 20:13:45 +0200 (Sun, 23 Apr 2006) | 2 lines Whitespace normalization. ........ r45671 | skip.montanaro | 2006-04-23 21:14:27 +0200 (Sun, 23 Apr 2006) | 1 line first cut at trace module doc ........ r45672 | skip.montanaro | 2006-04-23 21:26:33 +0200 (Sun, 23 Apr 2006) | 1 line minor tweak ........ r45673 | skip.montanaro | 2006-04-23 21:30:50 +0200 (Sun, 23 Apr 2006) | 1 line it's always helpful if the example works... ........ r45674 | skip.montanaro | 2006-04-23 21:32:14 +0200 (Sun, 23 Apr 2006) | 1 line correct example ........ r45675 | andrew.kuchling | 2006-04-23 23:01:04 +0200 (Sun, 23 Apr 2006) | 1 line Edits to the PEP 343 section ........ r45676 | andrew.kuchling | 2006-04-23 23:51:10 +0200 (Sun, 23 Apr 2006) | 1 line Add two items ........ r45677 | tim.peters | 2006-04-24 04:03:16 +0200 (Mon, 24 Apr 2006) | 5 lines Bug #1337990: clarified that `doctest` does not support examples requiring both expected output and an exception. I'll backport to 2.4 next. ........ r45679 | nick.coghlan | 2006-04-24 05:04:43 +0200 (Mon, 24 Apr 2006) | 1 line Note changes made to PEP 343 related documentation ........ r45681 | nick.coghlan | 2006-04-24 06:17:02 +0200 (Mon, 24 Apr 2006) | 1 line Change PEP 343 related documentation to use the term context specifier instead of context object ........ r45682 | nick.coghlan | 2006-04-24 06:32:47 +0200 (Mon, 24 Apr 2006) | 1 line Add unit tests for the -m and -c command line switches ........ r45683 | nick.coghlan | 2006-04-24 06:37:15 +0200 (Mon, 24 Apr 2006) | 1 line Fix contextlib.nested to cope with exit methods raising and handling exceptions ........ r45685 | nick.coghlan | 2006-04-24 06:59:28 +0200 (Mon, 24 Apr 2006) | 1 line Fix broken contextlib test from last checkin (I'd've sworn I tested that before checking it in. . .) ........ r45686 | nick.coghlan | 2006-04-24 07:24:26 +0200 (Mon, 24 Apr 2006) | 1 line Back out new command line tests (broke buildbot) ........ r45687 | nick.coghlan | 2006-04-24 07:52:15 +0200 (Mon, 24 Apr 2006) | 1 line More reliable version of new command line tests that just checks the exit codes ........ r45688 | thomas.wouters | 2006-04-24 13:37:13 +0200 (Mon, 24 Apr 2006) | 4 lines Stop test_tcl's testLoadTk from leaking the Tk commands 'loadtk' registers. ........ r45690 | andrew.kuchling | 2006-04-24 16:30:47 +0200 (Mon, 24 Apr 2006) | 2 lines Edits, using the new term 'context specifier' in a few places ........ r45697 | phillip.eby | 2006-04-24 22:53:13 +0200 (Mon, 24 Apr 2006) | 2 lines Revert addition of setuptools ........ r45698 | tim.peters | 2006-04-25 00:45:13 +0200 (Tue, 25 Apr 2006) | 2 lines Whitespace normalization. ........ r45700 | trent.mick | 2006-04-25 02:34:50 +0200 (Tue, 25 Apr 2006) | 4 lines Put break at correct level so *all* root HKEYs acutally get checked for an installed VC6. Otherwise only the first such tree gets checked and this warning doesn't get displayed. ........ r45701 | tim.peters | 2006-04-25 05:31:36 +0200 (Tue, 25 Apr 2006) | 3 lines Patch #1475231: add a new SKIP doctest option, thanks to Edward Loper. ........ r45702 | neal.norwitz | 2006-04-25 07:04:35 +0200 (Tue, 25 Apr 2006) | 1 line versionadded for SKIP ........ r45703 | neal.norwitz | 2006-04-25 07:05:03 +0200 (Tue, 25 Apr 2006) | 1 line Restore Walters name ........ r45704 | neal.norwitz | 2006-04-25 07:49:42 +0200 (Tue, 25 Apr 2006) | 1 line Revert previous change, SKIP had a versionadded elsewhere ........ r45706 | nick.coghlan | 2006-04-25 12:56:51 +0200 (Tue, 25 Apr 2006) | 31 lines Move the PEP 343 documentation and implementation closer to the terminology in the alpha 1 documentation. - "context manager" reverts to its alpha 1 definition - the term "context specifier" goes away entirely - contextlib.GeneratorContextManager is renamed GeneratorContext There are still a number of changes relative to alpha 1: - the expression in the with statement is explicitly called the "context expression" in the language reference - the terms 'with statement context', 'context object' or 'with statement context' are used in several places instead of a bare 'context'. The aim of this is to avoid ambiguity in relation to the runtime context set up when the block is executed, and the context objects that already exist in various application domains (such as decimal.Context) - contextlib.contextmanager is renamed to contextfactory This best reflects the nature of the function resulting from the use of that decorator - decimal.ContextManager is renamed to WithStatementContext Simple dropping the 'Manager' part wasn't possible due to the fact that decimal.Context already exists and means something different. WithStatementContext is ugly but workable. A technically unrelated change snuck into this commit: contextlib.closing now avoids the overhead of creating a generator, since it's trivial to implement that particular context manager directly. ........ r45707 | nick.coghlan | 2006-04-25 13:05:56 +0200 (Tue, 25 Apr 2006) | 1 line Fix latex typo ........ r45708 | thomas.wouters | 2006-04-25 14:28:56 +0200 (Tue, 25 Apr 2006) | 4 lines Fix markup glitch in unittest docs. Will backport. ........ r45710 | andrew.kuchling | 2006-04-25 14:31:38 +0200 (Tue, 25 Apr 2006) | 1 line Add two items; easy_install is now off the table, though pkgutil still is ........ r45711 | andrew.kuchling | 2006-04-25 14:47:25 +0200 (Tue, 25 Apr 2006) | 1 line Rework context terminology ........ r45712 | thomas.wouters | 2006-04-25 15:53:23 +0200 (Tue, 25 Apr 2006) | 9 lines SF bug/patch #1433877: string parameter to ioctl not null terminated The new char-array used in ioctl calls wasn't explicitly NUL-terminated; quite probably the cause for the test_pty failures on Solaris that we circumvented earlier. (I wasn't able to reproduce it with this patch, but it has been somewhat elusive to start with.) ........ r45713 | george.yoshida | 2006-04-25 16:09:58 +0200 (Tue, 25 Apr 2006) | 2 lines minor tweak ........ r45714 | thomas.wouters | 2006-04-25 17:08:10 +0200 (Tue, 25 Apr 2006) | 7 lines Fix SF bug #1476111: SystemError in socket sendto. The AF_INET6 and AF_PACKET cases in getsockaddrarg were missing their own checks for tuple-ness of the address argument, which means a confusing SystemError was raised by PyArg_ParseTuple instead. ........ r45715 | thomas.wouters | 2006-04-25 17:29:46 +0200 (Tue, 25 Apr 2006) | 10 lines Define MAXPATHLEN to be at least PATH_MAX, if that's defined. Python uses MAXPATHLEN-sized buffers for various output-buffers (like to realpath()), and that's correct on BSD platforms, but not Linux (which uses PATH_MAX, and does not define MAXPATHLEN.) Cursory googling suggests Linux is following a newer standard than BSD, but in cases like this, who knows. Using the greater of PATH_MAX and 1024 as a fallback for MAXPATHLEN seems to be the most portable solution. ........ r45717 | thomas.heller | 2006-04-25 20:26:08 +0200 (Tue, 25 Apr 2006) | 3 lines Fix compiler warnings on Darwin. Patch by Brett Canon, see https://sourceforge.net/tracker/?func=detail&atid=532156&aid=1475959&group_id=71702 ........ r45718 | guido.van.rossum | 2006-04-25 22:12:45 +0200 (Tue, 25 Apr 2006) | 4 lines Implement MvL's improvement on __context__ in Condition; this can just call __context__ on the underlying lock. (The same change for Semaphore does *not* work!) ........ r45721 | tim.peters | 2006-04-26 03:15:53 +0200 (Wed, 26 Apr 2006) | 13 lines Rev 45706 renamed stuff in contextlib.py, but didn't rename uses of it in test_with.py. As a result, test_with has been skipped (due to failing imports) on all buildbot boxes since. Alas, that's not a test failure -- you have to pay attention to the 1 skip unexpected on PLATFORM: test_with kinds of output at the ends of test runs to notice that this got broken. It's likely that more renaming in test_with.py would be desirable. ........ r45722 | fred.drake | 2006-04-26 07:15:41 +0200 (Wed, 26 Apr 2006) | 1 line markup fixes, cleanup ........ r45723 | fred.drake | 2006-04-26 07:19:39 +0200 (Wed, 26 Apr 2006) | 1 line minor adjustment suggested by Peter Gephardt ........ r45724 | neal.norwitz | 2006-04-26 07:34:03 +0200 (Wed, 26 Apr 2006) | 10 lines Patch from Aldo Cortesi (OpenBSD buildbot owner). After the patch (45590) to add extra debug stats to the gc module, Python was crashing on OpenBSD due to: Fatal Python error: Interpreter not initialized (version mismatch?) This seems to occur due to calling collect() when initialized (in pythonrun.c) is set to 0. Now, the import will occur in the init function which shouldn't suffer this problem. ........ r45725 | neal.norwitz | 2006-04-26 08:26:12 +0200 (Wed, 26 Apr 2006) | 3 lines Fix this test on Solaris. There can be embedded \r, so don't just replace the one at the end. ........ r45727 | nick.coghlan | 2006-04-26 13:50:04 +0200 (Wed, 26 Apr 2006) | 1 line Fix an error in the last contextlib.closing example ........ r45728 | andrew.kuchling | 2006-04-26 14:21:06 +0200 (Wed, 26 Apr 2006) | 1 line [Bug #1475080] Fix example ........ r45729 | andrew.kuchling | 2006-04-26 14:23:39 +0200 (Wed, 26 Apr 2006) | 1 line Add labels to all sections ........ r45730 | thomas.wouters | 2006-04-26 17:53:30 +0200 (Wed, 26 Apr 2006) | 7 lines The result of SF patch #1471578: big-memory tests for strings, lists and tuples. Lots to be added, still, but this will give big-memory people something to play with in 2.5 alpha 2, and hopefully get more people to write these tests. ........ r45731 | tim.peters | 2006-04-26 19:11:16 +0200 (Wed, 26 Apr 2006) | 2 lines Whitespace normalization. ........ r45732 | martin.v.loewis | 2006-04-26 19:19:44 +0200 (Wed, 26 Apr 2006) | 1 line Use GS- and bufferoverlowU.lib where appropriate, for AMD64. ........ r45733 | thomas.wouters | 2006-04-26 20:46:01 +0200 (Wed, 26 Apr 2006) | 5 lines Add tests for += and *= on strings, and fix the memory-use estimate for the list.extend tests (they were estimating half the actual use.) ........ r45734 | thomas.wouters | 2006-04-26 21:14:46 +0200 (Wed, 26 Apr 2006) | 5 lines Some more test-size-estimate fixes: test_append and test_insert trigger a list resize, which overallocates. ........ r45735 | hyeshik.chang | 2006-04-26 21:20:26 +0200 (Wed, 26 Apr 2006) | 3 lines Fix build on MIPS for libffi. I haven't tested this yet because I don't have an access on MIPS machines. Will be tested by buildbot. :) ........ r45737 | fred.drake | 2006-04-27 01:40:32 +0200 (Thu, 27 Apr 2006) | 1 line one more place to use the current Python version ........ r45738 | fred.drake | 2006-04-27 02:02:24 +0200 (Thu, 27 Apr 2006) | 3 lines - update version numbers in file names again, until we have a better way - elaborate instructions for Cygwin support (closes SF #839709) ........ r45739 | fred.drake | 2006-04-27 02:20:14 +0200 (Thu, 27 Apr 2006) | 1 line add missing word ........ r45740 | anthony.baxter | 2006-04-27 04:11:24 +0200 (Thu, 27 Apr 2006) | 2 lines 2.5a2 ........ r45741 | anthony.baxter | 2006-04-27 04:13:13 +0200 (Thu, 27 Apr 2006) | 1 line 2.5a2 ........ r45749 | andrew.kuchling | 2006-04-27 14:22:37 +0200 (Thu, 27 Apr 2006) | 1 line Now that 2.5a2 is out, revert to the current date ........ r45750 | andrew.kuchling | 2006-04-27 14:23:07 +0200 (Thu, 27 Apr 2006) | 1 line Bump document version ........ r45751 | andrew.kuchling | 2006-04-27 14:34:39 +0200 (Thu, 27 Apr 2006) | 6 lines [Bug #1477102] Add necessary import to example This may be a useful style question for the docs -- should examples show the necessary imports, or should it be assumed that the reader will figure it out? In the What's New, I'm not consistent but usually opt for omitting the imports. ........ r45753 | andrew.kuchling | 2006-04-27 14:38:35 +0200 (Thu, 27 Apr 2006) | 1 line [Bug #1477140] Import Error base class ........ r45754 | andrew.kuchling | 2006-04-27 14:42:54 +0200 (Thu, 27 Apr 2006) | 1 line Mention the xmlrpclib.Error base class, which is used in one of the examples ........ r45756 | george.yoshida | 2006-04-27 15:41:07 +0200 (Thu, 27 Apr 2006) | 2 lines markup fix ........ r45757 | thomas.wouters | 2006-04-27 15:46:59 +0200 (Thu, 27 Apr 2006) | 4 lines Some more size-estimate fixes, for large-list-tests. ........ r45758 | thomas.heller | 2006-04-27 17:50:42 +0200 (Thu, 27 Apr 2006) | 3 lines Rerun the libffi configuration if any of the files used for that are newer then fficonfig.py. ........ r45766 | thomas.wouters | 2006-04-28 00:37:50 +0200 (Fri, 28 Apr 2006) | 6 lines Some style fixes and size-calculation fixes. Also do the small-memory run using a prime number, rather than a convenient power-of-2-and-multiple-of-5, so incorrect testing algorithms fail more easily. ........ r45767 | thomas.wouters | 2006-04-28 00:38:32 +0200 (Fri, 28 Apr 2006) | 6 lines Do the small-memory run of big-meormy tests using a prime number, rather than a convenient power-of-2-and-multiple-of-5, so incorrect testing algorithms fail more easily. ........ r45768 | david.goodger | 2006-04-28 00:53:05 +0200 (Fri, 28 Apr 2006) | 1 line Added SVN access for Steven Bethard and Talin, for PEP updating. ........ r45770 | thomas.wouters | 2006-04-28 01:13:20 +0200 (Fri, 28 Apr 2006) | 16 lines - Add new Warning class, ImportWarning - Warn-raise ImportWarning when importing would have picked up a directory as package, if only it'd had an __init__.py. This swaps two tests (for case-ness and __init__-ness), but case-test is not really more expensive, and it's not in a speed-critical section. - Test for the new warning by importing a common non-package directory on sys.path: site-packages - In regrtest.py, silence warnings generated by the build-environment because Modules/ (which is added to sys.path for Setup-created modules) has 'zlib' and '_ctypes' directories without __init__.py's. ........ r45771 | thomas.wouters | 2006-04-28 01:41:27 +0200 (Fri, 28 Apr 2006) | 6 lines Add more ignores of ImportWarnings; these are all just potential triggers (since they won't trigger if zlib is already sucessfully imported); they were found by grepping .py files, instead of looking at warning output :) ........ r45773 | neal.norwitz | 2006-04-28 06:32:20 +0200 (Fri, 28 Apr 2006) | 1 line Add some whitespace to be more consistent. ........ r45774 | neal.norwitz | 2006-04-28 06:34:43 +0200 (Fri, 28 Apr 2006) | 5 lines Try to really fix the slow buildbots this time. Printing to stdout, doesn't mean the data was actually written. It depends on the buffering, so we need to flush. This will hopefully really fix the buildbots getting killed due to no output on the slow bots. ........ r45775 | neal.norwitz | 2006-04-28 07:28:05 +0200 (Fri, 28 Apr 2006) | 1 line Fix some warnings on Mac OS X 10.4 ........ r45776 | neal.norwitz | 2006-04-28 07:28:30 +0200 (Fri, 28 Apr 2006) | 1 line Fix a warning on alpha ........ r45777 | neal.norwitz | 2006-04-28 07:28:54 +0200 (Fri, 28 Apr 2006) | 1 line Fix a warning on ppc (debian) ........ r45778 | george.yoshida | 2006-04-28 18:09:45 +0200 (Fri, 28 Apr 2006) | 2 lines fix markup glitch ........ r45780 | georg.brandl | 2006-04-28 18:31:17 +0200 (Fri, 28 Apr 2006) | 3 lines Add SeaMonkey to the list of Mozilla browsers. ........ r45781 | georg.brandl | 2006-04-28 18:36:55 +0200 (Fri, 28 Apr 2006) | 2 lines Bug #1475009: clarify ntpath.join behavior with absolute components ........ r45783 | george.yoshida | 2006-04-28 18:40:14 +0200 (Fri, 28 Apr 2006) | 2 lines correct a dead link ........ r45785 | georg.brandl | 2006-04-28 18:54:25 +0200 (Fri, 28 Apr 2006) | 4 lines Bug #1472949: stringify IOErrors in shutil.copytree when appending them to the Error errors list. ........ r45786 | georg.brandl | 2006-04-28 18:58:52 +0200 (Fri, 28 Apr 2006) | 3 lines Bug #1478326: don't allow '/' in distutils.util.get_platform machine names since this value is used to name the build directory. ........ r45788 | thomas.heller | 2006-04-28 19:02:18 +0200 (Fri, 28 Apr 2006) | 1 line Remove a duplicated test (the same test is in test_incomplete.py). ........ r45792 | georg.brandl | 2006-04-28 21:09:24 +0200 (Fri, 28 Apr 2006) | 3 lines Bug #1478429: make datetime.datetime.fromtimestamp accept every float, possibly "rounding up" to the next whole second. ........ r45796 | george.yoshida | 2006-04-29 04:43:30 +0200 (Sat, 29 Apr 2006) | 2 lines grammar fix ........ r45800 | ronald.oussoren | 2006-04-29 13:31:35 +0200 (Sat, 29 Apr 2006) | 2 lines Patch 1471883: --enable-universalsdk on Mac OS X ........ r45801 | andrew.kuchling | 2006-04-29 13:53:15 +0200 (Sat, 29 Apr 2006) | 1 line Add item ........ r45802 | andrew.kuchling | 2006-04-29 14:10:28 +0200 (Sat, 29 Apr 2006) | 1 line Make case of 'ZIP' consistent ........ r45803 | andrew.kuchling | 2006-04-29 14:10:43 +0200 (Sat, 29 Apr 2006) | 1 line Add item ........ r45808 | martin.v.loewis | 2006-04-29 14:37:25 +0200 (Sat, 29 Apr 2006) | 3 lines Further changes for #1471883: Edit Misc/NEWS, and add expat_config.h. ........ r45809 | brett.cannon | 2006-04-29 23:29:50 +0200 (Sat, 29 Apr 2006) | 2 lines Fix docstring for contextfactory; mentioned old contextmanager name. ........ r45810 | gerhard.haering | 2006-04-30 01:12:41 +0200 (Sun, 30 Apr 2006) | 3 lines This is the start of documentation for the sqlite3 module. Please feel free to find a better place for the link to it than alongside bsddb & friends. ........ r45811 | andrew.kuchling | 2006-04-30 03:07:09 +0200 (Sun, 30 Apr 2006) | 1 line Add two items ........ r45814 | george.yoshida | 2006-04-30 05:49:56 +0200 (Sun, 30 Apr 2006) | 2 lines Use \versionchanged instead of \versionadded for new parameter support. ........ r45815 | georg.brandl | 2006-04-30 09:06:11 +0200 (Sun, 30 Apr 2006) | 2 lines Patch #1470846: fix urllib2 ProxyBasicAuthHandler. ........ r45817 | georg.brandl | 2006-04-30 10:57:35 +0200 (Sun, 30 Apr 2006) | 3 lines In stdlib, use hashlib instead of deprecated md5 and sha modules. ........ r45819 | georg.brandl | 2006-04-30 11:23:59 +0200 (Sun, 30 Apr 2006) | 3 lines Patch #1470976: don't NLST files when retrieving over FTP. ........ r45821 | georg.brandl | 2006-04-30 13:13:56 +0200 (Sun, 30 Apr 2006) | 6 lines Bug #1473625: stop cPickle making float dumps locale dependent in protocol 0. On the way, add a decorator to test_support to facilitate running single test functions in different locales with automatic cleanup. ........ r45822 | phillip.eby | 2006-04-30 17:59:26 +0200 (Sun, 30 Apr 2006) | 2 lines Fix infinite regress when inspecting or frames. ........ r45824 | georg.brandl | 2006-04-30 19:42:26 +0200 (Sun, 30 Apr 2006) | 3 lines Fix another problem in inspect: if the module for an object cannot be found, don't try to give its __dict__ to linecache. ........ r45825 | georg.brandl | 2006-04-30 20:14:54 +0200 (Sun, 30 Apr 2006) | 3 lines Patch #1472854: make the rlcompleter.Completer class usable on non- UNIX platforms. ........ r45826 | georg.brandl | 2006-04-30 21:34:19 +0200 (Sun, 30 Apr 2006) | 3 lines Patch #1479438: add \keyword markup for "with". ........ r45827 | andrew.kuchling | 2006-04-30 23:19:31 +0200 (Sun, 30 Apr 2006) | 1 line Add urllib2 HOWTO from Michael Foord ........ r45828 | andrew.kuchling | 2006-04-30 23:19:49 +0200 (Sun, 30 Apr 2006) | 1 line Add item ........ r45830 | barry.warsaw | 2006-05-01 05:03:02 +0200 (Mon, 01 May 2006) | 11 lines Port forward from 2.4 branch: Patch #1464708 from William McVey: fixed handling of nested comments in mail addresses. E.g. "Foo ((Foo Bar)) " Fixes for both rfc822.py and email package. This patch needs to be back ported to Python 2.3 for email 2.5. ........ r45832 | fred.drake | 2006-05-01 08:25:58 +0200 (Mon, 01 May 2006) | 4 lines - minor clarification in section title - markup adjustments (there is clearly much to be done in this section) ........ r45833 | martin.v.loewis | 2006-05-01 08:28:01 +0200 (Mon, 01 May 2006) | 2 lines Work around deadlock risk. Will backport. ........ r45836 | andrew.kuchling | 2006-05-01 14:45:02 +0200 (Mon, 01 May 2006) | 1 line Some ElementTree fixes: import from xml, not xmlcore; fix case of module name; mention list() instead of getchildren() ........ r45837 | gerhard.haering | 2006-05-01 17:14:48 +0200 (Mon, 01 May 2006) | 3 lines Further integration of the documentation for the sqlite3 module. There's still quite some content to move over from the pysqlite manual, but it's a start now. ........ r45838 | martin.v.loewis | 2006-05-01 17:56:03 +0200 (Mon, 01 May 2006) | 2 lines Rename uisample to text, drop all non-text tables. ........ r45839 | martin.v.loewis | 2006-05-01 18:12:44 +0200 (Mon, 01 May 2006) | 2 lines Add msilib documentation. ........ r45840 | martin.v.loewis | 2006-05-01 18:14:16 +0200 (Mon, 01 May 2006) | 4 lines Rename parameters to match the documentation (which in turn matches Microsoft's documentation). Drop unused parameter in CAB.append. ........ r45841 | fred.drake | 2006-05-01 18:28:54 +0200 (Mon, 01 May 2006) | 1 line add dependency ........ r45842 | andrew.kuchling | 2006-05-01 18:30:25 +0200 (Mon, 01 May 2006) | 1 line Markup fixes; add some XXX comments noting problems ........ r45843 | andrew.kuchling | 2006-05-01 18:32:49 +0200 (Mon, 01 May 2006) | 1 line Add item ........ r45844 | andrew.kuchling | 2006-05-01 19:06:54 +0200 (Mon, 01 May 2006) | 1 line Markup fixes ........ r45850 | neal.norwitz | 2006-05-02 06:43:14 +0200 (Tue, 02 May 2006) | 3 lines SF #1479181: split open() and file() from being aliases for each other. ........ r45852 | neal.norwitz | 2006-05-02 08:23:22 +0200 (Tue, 02 May 2006) | 1 line Try to fix breakage caused by patch #1479181, r45850 ........ r45853 | fred.drake | 2006-05-02 08:53:59 +0200 (Tue, 02 May 2006) | 3 lines SF #1479988: add methods to allow access to weakrefs for the weakref.WeakKeyDictionary and weakref.WeakValueDictionary ........ r45854 | neal.norwitz | 2006-05-02 09:27:47 +0200 (Tue, 02 May 2006) | 5 lines Fix breakage from patch 1471883 (r45800 & r45808) on OSF/1. The problem was that pyconfig.h was being included before some system headers which caused redefinitions and other breakage. This moves system headers after expat_config.h which includes pyconfig.h. ........ r45855 | vinay.sajip | 2006-05-02 10:35:36 +0200 (Tue, 02 May 2006) | 1 line Replaced my dumb way of calculating seconds to midnight with Tim Peters' much more sensible suggestion. What was I thinking ?!? ........ r45856 | andrew.kuchling | 2006-05-02 13:30:03 +0200 (Tue, 02 May 2006) | 1 line Provide encoding as keyword argument; soften warning paragraph about encodings ........ r45858 | guido.van.rossum | 2006-05-02 19:36:09 +0200 (Tue, 02 May 2006) | 2 lines Fix the formatting of KeyboardInterrupt -- a bad issubclass() call. ........ r45862 | guido.van.rossum | 2006-05-02 21:47:52 +0200 (Tue, 02 May 2006) | 7 lines Get rid of __context__, per the latest changes to PEP 343 and python-dev discussion. There are two places of documentation that still mention __context__: Doc/lib/libstdtypes.tex -- I wasn't quite sure how to rewrite that without spending a whole lot of time thinking about it; and whatsnew, which Andrew usually likes to change himself. ........ r45863 | armin.rigo | 2006-05-02 21:52:32 +0200 (Tue, 02 May 2006) | 4 lines Documentation bug: PySet_Pop() returns a new reference (because the caller becomes the owner of that reference). ........ r45864 | guido.van.rossum | 2006-05-02 22:47:36 +0200 (Tue, 02 May 2006) | 4 lines Hopefully this will fix the spurious failures of test_mailbox.py that I'm experiencing. (This code and mailbox.py itself are full of calls to file() that should be calls to open() -- but I'm not fixing those.) ........ r45865 | andrew.kuchling | 2006-05-02 23:44:33 +0200 (Tue, 02 May 2006) | 1 line Use open() instead of file() ........ r45866 | andrew.kuchling | 2006-05-03 00:47:49 +0200 (Wed, 03 May 2006) | 1 line Update context manager section for removal of __context__ ........ r45867 | fred.drake | 2006-05-03 03:46:52 +0200 (Wed, 03 May 2006) | 1 line remove unnecessary assignment ........ r45868 | fred.drake | 2006-05-03 03:48:24 +0200 (Wed, 03 May 2006) | 4 lines tell LaTeX2HTML to: - use UTF-8 output - not mess with the >>> prompt! ........ r45869 | fred.drake | 2006-05-03 04:04:40 +0200 (Wed, 03 May 2006) | 3 lines avoid ugly markup based on the unfortunate conversions of ">>" and "<<" to guillemets; no need for magic here ........ r45870 | fred.drake | 2006-05-03 04:12:47 +0200 (Wed, 03 May 2006) | 1 line at least comment on why curly-quotes are not enabled ........ r45871 | fred.drake | 2006-05-03 04:27:40 +0200 (Wed, 03 May 2006) | 1 line one more place to avoid extra markup ........ r45872 | fred.drake | 2006-05-03 04:29:09 +0200 (Wed, 03 May 2006) | 1 line one more place to avoid extra markup (how many will there be?) ........ r45873 | fred.drake | 2006-05-03 04:29:39 +0200 (Wed, 03 May 2006) | 1 line fix up whitespace in prompt strings ........ r45876 | tim.peters | 2006-05-03 06:46:14 +0200 (Wed, 03 May 2006) | 2 lines Whitespace normalization. ........ r45877 | martin.v.loewis | 2006-05-03 06:52:04 +0200 (Wed, 03 May 2006) | 2 lines Correct some formulations, fix XXX comments. ........ r45879 | georg.brandl | 2006-05-03 07:05:02 +0200 (Wed, 03 May 2006) | 2 lines Patch #1480067: don't redirect HTTP digest auth in urllib2 ........ r45881 | georg.brandl | 2006-05-03 07:15:10 +0200 (Wed, 03 May 2006) | 3 lines Move network tests from test_urllib2 to test_urllib2net. ........ r45887 | nick.coghlan | 2006-05-03 15:02:47 +0200 (Wed, 03 May 2006) | 1 line Finish bringing SVN into line with latest version of PEP 343 by getting rid of all remaining references to context objects that I could find. Without a __context__() method context objects no longer exist. Also get test_with working again, and adopt a suggestion from Neal for decimal.Context.get_manager() ........ r45888 | nick.coghlan | 2006-05-03 15:17:49 +0200 (Wed, 03 May 2006) | 1 line Get rid of a couple more context object references, fix some markup and clarify what happens when a generator context function swallows an exception. ........ r45889 | georg.brandl | 2006-05-03 19:46:13 +0200 (Wed, 03 May 2006) | 3 lines Add seamonkey to list of Windows browsers too. ........ r45890 | georg.brandl | 2006-05-03 20:03:22 +0200 (Wed, 03 May 2006) | 3 lines RFE #1472176: In httplib, don't encode the netloc and hostname with "idna" if not necessary. ........ r45891 | georg.brandl | 2006-05-03 20:12:33 +0200 (Wed, 03 May 2006) | 2 lines Bug #1472191: convert breakpoint indices to ints before comparing them to ints ........ r45893 | georg.brandl | 2006-05-03 20:18:32 +0200 (Wed, 03 May 2006) | 3 lines Bug #1385040: don't allow "def foo(a=1, b): pass" in the compiler package. ........ r45894 | thomas.heller | 2006-05-03 20:35:39 +0200 (Wed, 03 May 2006) | 1 line Don't fail the tests when libglut.so or libgle.so cannot be loaded. ........ r45895 | georg.brandl | 2006-05-04 07:08:10 +0200 (Thu, 04 May 2006) | 2 lines Bug #1481530: allow "from os.path import ..." with imputil ........ r45897 | martin.v.loewis | 2006-05-04 07:51:03 +0200 (Thu, 04 May 2006) | 2 lines Patch #1475845: Raise IndentationError for unexpected indent. ........ r45898 | martin.v.loewis | 2006-05-04 12:08:42 +0200 (Thu, 04 May 2006) | 1 line Implement os.{chdir,rename,rmdir,remove} using Win32 directly. ........ r45899 | martin.v.loewis | 2006-05-04 14:04:27 +0200 (Thu, 04 May 2006) | 2 lines Drop now-unnecessary arguments to posix_2str. ........ r45900 | martin.v.loewis | 2006-05-04 16:27:52 +0200 (Thu, 04 May 2006) | 1 line Update checks to consider Windows error numbers. ........ r45913 | thomas.heller | 2006-05-05 20:42:14 +0200 (Fri, 05 May 2006) | 2 lines Export the 'free' standard C function for use in the test suite. ........ r45914 | thomas.heller | 2006-05-05 20:43:24 +0200 (Fri, 05 May 2006) | 3 lines Fix memory leaks in the ctypes test suite, reported by valgrind, by free()ing the memory we allocate. ........ r45915 | thomas.heller | 2006-05-05 20:46:27 +0200 (Fri, 05 May 2006) | 1 line oops - the function is exported as 'my_free', not 'free'. ........ r45916 | thomas.heller | 2006-05-05 21:14:24 +0200 (Fri, 05 May 2006) | 2 lines Clean up. ........ r45920 | george.yoshida | 2006-05-06 15:09:45 +0200 (Sat, 06 May 2006) | 2 lines describe optional arguments for DocFileSuite ........ r45924 | george.yoshida | 2006-05-06 16:16:51 +0200 (Sat, 06 May 2006) | 2 lines Use \versionchanged for the feature change ........ r45925 | martin.v.loewis | 2006-05-06 18:32:54 +0200 (Sat, 06 May 2006) | 1 line Port access, chmod, parts of getcwdu, mkdir, and utime to direct Win32 API. ........ r45926 | martin.v.loewis | 2006-05-06 22:04:08 +0200 (Sat, 06 May 2006) | 2 lines Handle ERROR_ALREADY_EXISTS. ........ r45931 | andrew.kuchling | 2006-05-07 19:12:12 +0200 (Sun, 07 May 2006) | 1 line [Patch #1479977] Revised version of urllib2 HOWTO, edited by John J. Lee ........ r45932 | andrew.kuchling | 2006-05-07 19:14:53 +0200 (Sun, 07 May 2006) | 1 line Minor language edit ........ r45934 | georg.brandl | 2006-05-07 22:44:34 +0200 (Sun, 07 May 2006) | 3 lines Patch #1483395: add new TLDs to cookielib ........ r45936 | martin.v.loewis | 2006-05-08 07:25:56 +0200 (Mon, 08 May 2006) | 2 lines Add missing PyMem_Free. ........ r45938 | georg.brandl | 2006-05-08 19:28:47 +0200 (Mon, 08 May 2006) | 3 lines Add test for rev. 45934. ........ r45939 | georg.brandl | 2006-05-08 19:36:08 +0200 (Mon, 08 May 2006) | 3 lines Patch #1479302: Make urllib2 digest auth and basic auth play together. ........ r45940 | georg.brandl | 2006-05-08 19:48:01 +0200 (Mon, 08 May 2006) | 3 lines Patch #1478993: take advantage of BaseException/Exception split in cookielib ........ r45941 | neal.norwitz | 2006-05-09 07:38:56 +0200 (Tue, 09 May 2006) | 5 lines Micro optimization. In the first case, we know that frame->f_exc_type is NULL, so there's no reason to do anything with it. In the second case, we know frame->f_exc_type is not NULL, so we can just do an INCREF. ........ r45943 | thomas.heller | 2006-05-09 22:20:15 +0200 (Tue, 09 May 2006) | 2 lines Disable a test that is unreliable. ........ r45944 | tim.peters | 2006-05-10 04:43:01 +0200 (Wed, 10 May 2006) | 4 lines Variant of patch #1478292. doctest.register_optionflag(name) shouldn't create a new flag when `name` is already the name of an option flag. ........ r45947 | neal.norwitz | 2006-05-10 08:57:58 +0200 (Wed, 10 May 2006) | 14 lines Fix problems found by Coverity. longobject.c: also fix an ssize_t problem could have been NULL, so hoist the size calc to not use . _ssl.c: under fail: self is DECREF'd, but it would have been NULL. _elementtree.c: delete self if there was an error. _csv.c: I'm not sure if lineterminator could have been anything other than a string. However, other string method calls are checked, so check this one too. ........ r45948 | thomas.wouters | 2006-05-10 17:04:11 +0200 (Wed, 10 May 2006) | 4 lines Ignore reflog.txt, too. ........ r45949 | georg.brandl | 2006-05-10 17:59:06 +0200 (Wed, 10 May 2006) | 3 lines Bug #1482988: indicate more prominently that the Stats class is in the pstats module. ........ r45950 | georg.brandl | 2006-05-10 18:09:03 +0200 (Wed, 10 May 2006) | 2 lines Bug #1485447: subprocess: document that the "cwd" parameter isn't used to find the executable. Misc. other markup fixes. ........ r45952 | georg.brandl | 2006-05-10 18:11:44 +0200 (Wed, 10 May 2006) | 2 lines Bug #1484978: curses.panel: clarify that Panel objects are destroyed on garbage collection. ........ r45954 | georg.brandl | 2006-05-10 18:26:03 +0200 (Wed, 10 May 2006) | 4 lines Patch #1484695: Update the tarfile module to version 0.8. This fixes a couple of issues, notably handling of long file names using the GNU LONGNAME extension. ........ r45955 | georg.brandl | 2006-05-10 19:13:20 +0200 (Wed, 10 May 2006) | 4 lines Patch #721464: pdb.Pdb instances can now be given explicit stdin and stdout arguments, making it possible to redirect input and output for remote debugging. ........ r45956 | andrew.kuchling | 2006-05-10 19:19:04 +0200 (Wed, 10 May 2006) | 1 line Clarify description of exception handling ........ r45957 | georg.brandl | 2006-05-10 22:09:23 +0200 (Wed, 10 May 2006) | 2 lines Fix two small errors in argument lists. ........ r45960 | brett.cannon | 2006-05-11 07:11:33 +0200 (Thu, 11 May 2006) | 5 lines Detect if %zd is supported by printf() during configure and sets PY_FORMAT_SIZE_T appropriately. Removes warnings on OS X under gcc 4.0.1 when PY_FORMAT_SIZE_T is set to "" instead of "z" as is needed. ........ r45963 | neal.norwitz | 2006-05-11 09:51:59 +0200 (Thu, 11 May 2006) | 1 line Don't mask a no memory error with a less meaningful one as discussed on python-checkins ........ r45964 | martin.v.loewis | 2006-05-11 15:28:43 +0200 (Thu, 11 May 2006) | 3 lines Change WindowsError to carry the Win32 error code in winerror, and the DOS error code in errno. Revert changes where WindowsError catch blocks unnecessarily special-case OSError. ........ r45965 | george.yoshida | 2006-05-11 17:53:27 +0200 (Thu, 11 May 2006) | 2 lines Grammar fix ........ r45967 | andrew.kuchling | 2006-05-11 18:32:24 +0200 (Thu, 11 May 2006) | 1 line typo fix ........ r45968 | tim.peters | 2006-05-11 18:37:42 +0200 (Thu, 11 May 2006) | 5 lines BaseThreadedTestCase.setup(): stop special-casing WindowsError. Rev 45964 fiddled with WindowsError, and broke test_bsddb3 on all the Windows buildbot slaves as a result. This should repair it. ........ r45969 | georg.brandl | 2006-05-11 21:57:09 +0200 (Thu, 11 May 2006) | 2 lines Typo fix. ........ r45970 | tim.peters | 2006-05-12 03:57:59 +0200 (Fri, 12 May 2006) | 5 lines SF patch #1473132: Improve docs for tp_clear and tp_traverse, by Collin Winter. Bugfix candidate (but I'm not going to bother). ........ r45974 | martin.v.loewis | 2006-05-12 14:27:28 +0200 (Fri, 12 May 2006) | 4 lines Dynamically allocate path name buffer for Unicode path name in listdir. Fixes #1431582. Stop overallocating MAX_PATH characters for ANSI path names. Stop assigning to errno. ........ r45975 | martin.v.loewis | 2006-05-12 15:57:36 +0200 (Fri, 12 May 2006) | 1 line Move icon files into DLLs dir. Fixes #1477968. ........ r45976 | george.yoshida | 2006-05-12 18:40:11 +0200 (Fri, 12 May 2006) | 2 lines At first there were 6 steps, but one was removed after that. ........ r45977 | martin.v.loewis | 2006-05-12 19:22:04 +0200 (Fri, 12 May 2006) | 1 line Fix alignment error on Itanium. ........ r45978 | george.yoshida | 2006-05-12 19:25:26 +0200 (Fri, 12 May 2006) | 3 lines Duplicated description about the illegal continue usage can be found in nearly the same place. They are same, so keep the original one and remove the later-added one. ........ r45980 | thomas.heller | 2006-05-12 20:16:03 +0200 (Fri, 12 May 2006) | 2 lines Add missing svn properties. ........ r45981 | thomas.heller | 2006-05-12 20:47:35 +0200 (Fri, 12 May 2006) | 1 line set svn properties ........ r45982 | thomas.heller | 2006-05-12 21:31:46 +0200 (Fri, 12 May 2006) | 1 line add svn:eol-style native svn:keywords Id ........ r45987 | gerhard.haering | 2006-05-13 01:49:49 +0200 (Sat, 13 May 2006) | 3 lines Integrated the rest of the pysqlite reference manual into the Python documentation. Ready to be reviewed and improved upon. ........ r45988 | george.yoshida | 2006-05-13 08:53:31 +0200 (Sat, 13 May 2006) | 2 lines Add \exception markup ........ r45990 | martin.v.loewis | 2006-05-13 15:34:04 +0200 (Sat, 13 May 2006) | 2 lines Revert 43315: Printing of %zd must be signed. ........ r45992 | tim.peters | 2006-05-14 01:28:20 +0200 (Sun, 14 May 2006) | 11 lines Teach PyString_FromFormat, PyErr_Format, and PyString_FromFormatV about "%u", "%lu" and "%zu" formats. Since PyString_FromFormat and PyErr_Format have exactly the same rules (both inherited from PyString_FromFormatV), it would be good if someone with more LaTeX Fu changed one of them to just point to the other. Their docs were way out of synch before this patch, and I just did a mass copy+paste to repair that. Not a backport candidate (this is a new feature). ........ r45993 | tim.peters | 2006-05-14 01:31:05 +0200 (Sun, 14 May 2006) | 2 lines Typo repair. ........ r45994 | tim.peters | 2006-05-14 01:33:19 +0200 (Sun, 14 May 2006) | 2 lines Remove lie in new comment. ........ r45995 | ronald.oussoren | 2006-05-14 21:56:34 +0200 (Sun, 14 May 2006) | 11 lines Rework the build system for osx applications: * Don't use xcodebuild for building PythonLauncher, but use a normal unix makefile. This makes it a lot easier to use the same build flags as for the rest of python (e.g. make a universal version of python launcher) * Convert the mac makefile-s to makefile.in-s and use configure to set makefile variables instead of forwarding them as command-line arguments * Add a C version of pythonw, that we you can use '#!/usr/local/bin/pythonw' * Build IDLE.app using bundlebuilder instead of BuildApplet, that will allow easier modification of the bundle contents later on. ........ r45996 | ronald.oussoren | 2006-05-14 22:35:41 +0200 (Sun, 14 May 2006) | 6 lines A first cut at replacing the icons on MacOS X. This replaces all icons by icons based on the new python.org logo. These are also the first icons that are "proper" OSX icons. These icons were created by Jacob Rus. ........ r45997 | ronald.oussoren | 2006-05-14 23:07:41 +0200 (Sun, 14 May 2006) | 3 lines I missed one small detail in my rewrite of the osx build files: the path to the Python.app template. ........ r45998 | martin.v.loewis | 2006-05-15 07:51:36 +0200 (Mon, 15 May 2006) | 2 lines Fix memory leak. ........ r45999 | neal.norwitz | 2006-05-15 08:48:14 +0200 (Mon, 15 May 2006) | 1 line Move items implemented after a2 into the new a3 section ........ r46000 | neal.norwitz | 2006-05-15 09:04:36 +0200 (Mon, 15 May 2006) | 5 lines - Bug #1487966: Fix SystemError with conditional expression in assignment Most of the test_syntax changes are just updating the numbers. ........ r46001 | neal.norwitz | 2006-05-15 09:17:23 +0200 (Mon, 15 May 2006) | 1 line Patch #1488312, Fix memory alignment problem on SPARC in unicode. Will backport ........ r46003 | martin.v.loewis | 2006-05-15 11:22:27 +0200 (Mon, 15 May 2006) | 3 lines Remove bogus DECREF of self. Change __str__() functions to METH_O. Change WindowsError__str__ to use PyTuple_Pack. ........ r46005 | georg.brandl | 2006-05-15 21:30:35 +0200 (Mon, 15 May 2006) | 3 lines [ 1488881 ] tarfile.py: support for file-objects and bz2 (cp. #1488634) ........ r46007 | tim.peters | 2006-05-15 22:44:10 +0200 (Mon, 15 May 2006) | 9 lines ReadDetectFileobjTest: repair Windows disasters by opening the file object in binary mode. The Windows buildbot slaves shouldn't swap themselves to death anymore. However, test_tarfile may still fail because of a temp directory left behind from a previous failing run. Windows buildbot owners may need to remove that directory by hand. ........ r46009 | tim.peters | 2006-05-15 23:32:25 +0200 (Mon, 15 May 2006) | 3 lines test_directory(): Remove the leftover temp directory that's making the Windows buildbots fail test_tarfile. ........ r46010 | martin.v.loewis | 2006-05-16 09:05:37 +0200 (Tue, 16 May 2006) | 4 lines - Test for sys/statvfs.h before including it, as statvfs is present on some OSX installation, but its header file is not. Will backport to 2.4 ........ r46012 | georg.brandl | 2006-05-16 09:38:27 +0200 (Tue, 16 May 2006) | 3 lines Patch #1435422: zlib's compress and decompress objects now have a copy() method. ........ r46015 | andrew.kuchling | 2006-05-16 18:11:54 +0200 (Tue, 16 May 2006) | 1 line Add item ........ r46016 | andrew.kuchling | 2006-05-16 18:27:31 +0200 (Tue, 16 May 2006) | 3 lines PEP 243 has been withdrawn, so don't refer to it any more. The PyPI upload material has been moved into the section on PEP314. ........ r46017 | george.yoshida | 2006-05-16 19:42:16 +0200 (Tue, 16 May 2006) | 2 lines Update for 'ImportWarning' ........ r46018 | george.yoshida | 2006-05-16 20:07:00 +0200 (Tue, 16 May 2006) | 4 lines Mention that Exception is now a subclass of BaseException. Remove a sentence that says that BaseException inherits from BaseException. (I guess this is just a copy & paste mistake.) ........ r46019 | george.yoshida | 2006-05-16 20:26:10 +0200 (Tue, 16 May 2006) | 2 lines Document ImportWarning ........ r46020 | tim.peters | 2006-05-17 01:22:20 +0200 (Wed, 17 May 2006) | 2 lines Whitespace normalization. ........ r46021 | tim.peters | 2006-05-17 01:24:08 +0200 (Wed, 17 May 2006) | 2 lines Text files missing the SVN eol-style property. ........ r46022 | tim.peters | 2006-05-17 03:30:11 +0200 (Wed, 17 May 2006) | 2 lines PyZlib_copy(), PyZlib_uncopy(): Repair leaks on the normal-case path. ........ r46023 | georg.brandl | 2006-05-17 16:06:07 +0200 (Wed, 17 May 2006) | 3 lines Remove misleading comment about type-class unification. ........ r46024 | georg.brandl | 2006-05-17 16:11:36 +0200 (Wed, 17 May 2006) | 3 lines Apply patch #1489784 from Michael Foord. ........ r46025 | georg.brandl | 2006-05-17 16:18:20 +0200 (Wed, 17 May 2006) | 3 lines Fix typo in os.utime docstring (patch #1490189) ........ r46026 | georg.brandl | 2006-05-17 16:26:50 +0200 (Wed, 17 May 2006) | 3 lines Patch #1490224: set time.altzone correctly on Cygwin. ........ r46027 | georg.brandl | 2006-05-17 16:45:06 +0200 (Wed, 17 May 2006) | 4 lines Add global debug flag to cookielib to avoid heavy dependency on the logging module. Resolves #1484758. ........ r46028 | georg.brandl | 2006-05-17 16:56:04 +0200 (Wed, 17 May 2006) | 3 lines Patch #1486962: Several bugs in the turtle Tk demo module were fixed and several features added, such as speed and geometry control. ........ r46029 | georg.brandl | 2006-05-17 17:17:00 +0200 (Wed, 17 May 2006) | 4 lines Delay-import some large modules to speed up urllib2 import. (fixes #1484793). ........ r46030 | georg.brandl | 2006-05-17 17:51:16 +0200 (Wed, 17 May 2006) | 3 lines Patch #1180296: improve locale string formatting functions ........ r46032 | tim.peters | 2006-05-18 04:06:40 +0200 (Thu, 18 May 2006) | 2 lines Whitespace normalization. ........ r46033 | georg.brandl | 2006-05-18 08:11:19 +0200 (Thu, 18 May 2006) | 3 lines Amendments to patch #1484695. ........ r46034 | georg.brandl | 2006-05-18 08:18:06 +0200 (Thu, 18 May 2006) | 3 lines Remove unused import. ........ r46035 | georg.brandl | 2006-05-18 08:33:27 +0200 (Thu, 18 May 2006) | 3 lines Fix test_locale for platforms without a default thousands separator. ........ r46036 | neal.norwitz | 2006-05-18 08:51:46 +0200 (Thu, 18 May 2006) | 1 line Little cleanup ........ r46037 | georg.brandl | 2006-05-18 09:01:27 +0200 (Thu, 18 May 2006) | 4 lines Bug #1462152: file() now checks more thoroughly for invalid mode strings and removes a possible "U" before passing the mode to the C library function. ........ r46038 | georg.brandl | 2006-05-18 09:20:05 +0200 (Thu, 18 May 2006) | 3 lines Bug #1490688: properly document %e, %f, %g format subtleties. ........ r46039 | vinay.sajip | 2006-05-18 09:28:58 +0200 (Thu, 18 May 2006) | 1 line Changed status from "beta" to "production"; since logging has been part of the stdlib since 2.3, it should be safe to make this assertion ;-) ........ r46040 | ronald.oussoren | 2006-05-18 11:04:15 +0200 (Thu, 18 May 2006) | 2 lines Fix some minor issues with the generated application bundles on MacOSX ........ r46041 | andrew.kuchling | 2006-05-19 02:03:55 +0200 (Fri, 19 May 2006) | 1 line Typo fix; add clarifying word ........ r46044 | neal.norwitz | 2006-05-19 08:31:23 +0200 (Fri, 19 May 2006) | 3 lines Fix #132 from Coverity, retval could have been derefed if a continue inside a try failed. ........ r46045 | neal.norwitz | 2006-05-19 08:43:50 +0200 (Fri, 19 May 2006) | 2 lines Fix #1474677, non-keyword argument following keyword. ........ r46046 | neal.norwitz | 2006-05-19 09:00:58 +0200 (Fri, 19 May 2006) | 4 lines Bug/Patch #1481770: Use .so extension for shared libraries on HP-UX for ia64. I suppose this could be backported if anyone cares. ........ r46047 | neal.norwitz | 2006-05-19 09:05:01 +0200 (Fri, 19 May 2006) | 7 lines Oops, I forgot to include this file in the last commit (46046): Bug/Patch #1481770: Use .so extension for shared libraries on HP-UX for ia64. I suppose this could be backported if anyone cares. ........ r46050 | ronald.oussoren | 2006-05-19 20:17:31 +0200 (Fri, 19 May 2006) | 6 lines * Change working directory to the users home directory, that makes the file open/save dialogs more useable. * Don't use argv emulator, its not needed for idle. ........ r46052 | tim.peters | 2006-05-19 21:16:34 +0200 (Fri, 19 May 2006) | 2 lines Whitespace normalization. ........ r46054 | ronald.oussoren | 2006-05-20 08:17:01 +0200 (Sat, 20 May 2006) | 9 lines Fix bug #1000914 (again). This patches a file that is generated by bgen, however the code is now the same as a current copy of bgen would generate. Without this patch most types in the Carbon.CF module are unusable. I haven't managed to coax bgen into generating a complete copy of _CFmodule.c yet :-(, hence the manual patching. ........ r46055 | george.yoshida | 2006-05-20 17:36:19 +0200 (Sat, 20 May 2006) | 3 lines - markup fix - add clarifying words ........ r46057 | george.yoshida | 2006-05-20 18:29:14 +0200 (Sat, 20 May 2006) | 3 lines - Add 'as' and 'with' as new keywords in 2.5. - Regenerate keyword lists with reswords.py. ........ r46058 | george.yoshida | 2006-05-20 20:07:26 +0200 (Sat, 20 May 2006) | 2 lines Apply patch #1492147 from Mike Foord. ........ r46059 | andrew.kuchling | 2006-05-20 21:25:16 +0200 (Sat, 20 May 2006) | 1 line Minor edits ........ r46061 | george.yoshida | 2006-05-21 06:22:59 +0200 (Sun, 21 May 2006) | 2 lines Fix the TeX compile error. ........ r46062 | george.yoshida | 2006-05-21 06:40:32 +0200 (Sun, 21 May 2006) | 2 lines Apply patch #1492255 from Mike Foord. ........ r46063 | martin.v.loewis | 2006-05-22 10:48:14 +0200 (Mon, 22 May 2006) | 1 line Patch 1490384: New Icons for the PC build. ........ r46064 | martin.v.loewis | 2006-05-22 11:15:18 +0200 (Mon, 22 May 2006) | 1 line Patch #1492356: Port to Windows CE (patch set 1). ........ r46065 | tim.peters | 2006-05-22 13:29:41 +0200 (Mon, 22 May 2006) | 4 lines Define SIZEOF_{DOUBLE,FLOAT} on Windows. Else Michael Hudson's nice gimmicks for IEEE special values (infinities, NaNs) don't work. ........ r46070 | bob.ippolito | 2006-05-22 16:31:24 +0200 (Mon, 22 May 2006) | 2 lines GzipFile.readline performance improvement (~30-40%), patch #1281707 ........ r46071 | bob.ippolito | 2006-05-22 17:22:46 +0200 (Mon, 22 May 2006) | 1 line Revert gzip readline performance patch #1281707 until a more generic performance improvement can be found ........ r46073 | fredrik.lundh | 2006-05-22 17:35:12 +0200 (Mon, 22 May 2006) | 4 lines docstring tweaks: count counts non-overlapping substrings, not total number of occurences ........ r46075 | bob.ippolito | 2006-05-22 17:59:12 +0200 (Mon, 22 May 2006) | 1 line Apply revised patch for GzipFile.readline performance #1281707 ........ r46076 | fredrik.lundh | 2006-05-22 18:29:30 +0200 (Mon, 22 May 2006) | 3 lines needforspeed: speed up unicode repeat, unicode string copy ........ r46079 | fredrik.lundh | 2006-05-22 19:12:58 +0200 (Mon, 22 May 2006) | 4 lines needforspeed: use memcpy for "long" strings; use a better algorithm for long repeats. ........ r46084 | tim.peters | 2006-05-22 21:17:04 +0200 (Mon, 22 May 2006) | 7 lines PyUnicode_Join(): Recent code changes introduced new compiler warnings on Windows (signed vs unsigned mismatch in comparisons). Cleaned that up by switching more locals to Py_ssize_t. Simplified overflow checking (it can _be_ simpler because while these things are declared as Py_ssize_t, then should in fact never be negative). ........ r46085 | tim.peters | 2006-05-23 07:47:16 +0200 (Tue, 23 May 2006) | 3 lines unicode_repeat(): Change type of local to Py_ssize_t, since that's what it should be. ........ r46094 | fredrik.lundh | 2006-05-23 12:10:57 +0200 (Tue, 23 May 2006) | 3 lines needforspeed: check first *and* last character before doing a full memcmp ........ r46095 | fredrik.lundh | 2006-05-23 12:12:21 +0200 (Tue, 23 May 2006) | 4 lines needforspeed: fixed unicode "in" operator to use same implementation approach as find/index ........ r46096 | richard.jones | 2006-05-23 12:37:38 +0200 (Tue, 23 May 2006) | 7 lines Merge from rjones-funccall branch. Applied patch zombie-frames-2.diff from sf patch 876206 with updates for Python 2.5 and also modified to retain the free_list to avoid the 67% slow-down in pybench recursion test. 5% speed up in function call pybench. ........ r46098 | ronald.oussoren | 2006-05-23 13:04:24 +0200 (Tue, 23 May 2006) | 2 lines Avoid creating a mess when installing a framework for the second time. ........ r46101 | georg.brandl | 2006-05-23 13:17:21 +0200 (Tue, 23 May 2006) | 3 lines PyErr_NewException now accepts a tuple of base classes as its "base" parameter. ........ r46103 | ronald.oussoren | 2006-05-23 13:47:16 +0200 (Tue, 23 May 2006) | 3 lines Disable linking extensions with -lpython2.5 for darwin. This should fix bug #1487105. ........ r46104 | ronald.oussoren | 2006-05-23 14:01:11 +0200 (Tue, 23 May 2006) | 6 lines Patch #1488098. This patchs makes it possible to create a universal build on OSX 10.4 and use the result to build extensions on 10.3. It also makes it possible to override the '-arch' and '-isysroot' compiler arguments for specific extensions. ........ r46108 | andrew.kuchling | 2006-05-23 14:44:36 +0200 (Tue, 23 May 2006) | 1 line Add some items; mention the sprint ........ r46109 | andrew.kuchling | 2006-05-23 14:47:01 +0200 (Tue, 23 May 2006) | 1 line Mention string improvements ........ r46110 | andrew.kuchling | 2006-05-23 14:49:35 +0200 (Tue, 23 May 2006) | 4 lines Use 'speed' instead of 'performance', because I agree with the argument at http://zestyping.livejournal.com/193260.html that 'erformance' really means something more general. ........ r46113 | ronald.oussoren | 2006-05-23 17:09:57 +0200 (Tue, 23 May 2006) | 2 lines An improved script for building the binary distribution on MacOSX. ........ r46128 | richard.jones | 2006-05-23 20:28:17 +0200 (Tue, 23 May 2006) | 3 lines Applied patch 1337051 by Neal Norwitz, saving 4 ints on frame objects. ........ r46129 | richard.jones | 2006-05-23 20:32:11 +0200 (Tue, 23 May 2006) | 1 line fix broken merge ........ r46130 | bob.ippolito | 2006-05-23 20:41:17 +0200 (Tue, 23 May 2006) | 1 line Update Misc/NEWS for gzip patch #1281707 ........ r46131 | bob.ippolito | 2006-05-23 20:43:47 +0200 (Tue, 23 May 2006) | 1 line Update Misc/NEWS for gzip patch #1281707 ........ r46132 | fredrik.lundh | 2006-05-23 20:44:25 +0200 (Tue, 23 May 2006) | 7 lines needforspeed: use append+reverse for rsplit, use "bloom filters" to speed up splitlines and strip with charsets; etc. rsplit is now as fast as split in all our tests (reverse takes no time at all), and splitlines() is nearly as fast as a plain split("\n") in our tests. and we're not done yet... ;-) ........ r46133 | tim.peters | 2006-05-23 20:45:30 +0200 (Tue, 23 May 2006) | 38 lines Bug #1334662 / patch #1335972: int(string, base) wrong answers. In rare cases of strings specifying true values near sys.maxint, and oddball bases (not decimal or a power of 2), int(string, base) could deliver insane answers. This repairs all such problems, and also speeds string->int significantly. On my box, here are % speedups for decimal strings of various lengths: length speedup ------ ------- 1 12.4% 2 15.7% 3 20.6% 4 28.1% 5 33.2% 6 37.5% 7 41.9% 8 46.3% 9 51.2% 10 19.5% 11 19.9% 12 23.9% 13 23.7% 14 23.3% 15 24.9% 16 25.3% 17 28.3% 18 27.9% 19 35.7% Note that the difference between 9 and 10 is the difference between short and long Python ints on a 32-bit box. The patch doesn't actually do anything to speed conversion to long: the speedup is due to detecting "unsigned long" overflow more quickly. This is a bugfix candidate, but it's a non-trivial patch and it would be painful to separate the "bug fix" from the "speed up" parts. ........ r46134 | bob.ippolito | 2006-05-23 20:46:41 +0200 (Tue, 23 May 2006) | 1 line Patch #1493701: performance enhancements for struct module. ........ r46136 | andrew.kuchling | 2006-05-23 21:00:45 +0200 (Tue, 23 May 2006) | 1 line Remove duplicate item ........ r46141 | bob.ippolito | 2006-05-23 21:09:51 +0200 (Tue, 23 May 2006) | 1 line revert #1493701 ........ r46142 | bob.ippolito | 2006-05-23 21:11:34 +0200 (Tue, 23 May 2006) | 1 line patch #1493701: performance enhancements for struct module ........ r46144 | bob.ippolito | 2006-05-23 21:12:41 +0200 (Tue, 23 May 2006) | 1 line patch #1493701: performance enhancements for struct module ........ r46148 | bob.ippolito | 2006-05-23 21:25:52 +0200 (Tue, 23 May 2006) | 1 line fix linking issue, warnings, in struct ........ r46149 | andrew.kuchling | 2006-05-23 21:29:38 +0200 (Tue, 23 May 2006) | 1 line Add two items ........ r46150 | bob.ippolito | 2006-05-23 21:31:23 +0200 (Tue, 23 May 2006) | 1 line forward declaration for PyStructType ........ r46151 | bob.ippolito | 2006-05-23 21:32:25 +0200 (Tue, 23 May 2006) | 1 line fix typo in _struct ........ r46152 | andrew.kuchling | 2006-05-23 21:32:35 +0200 (Tue, 23 May 2006) | 1 line Add item ........ r46153 | tim.peters | 2006-05-23 21:34:37 +0200 (Tue, 23 May 2006) | 3 lines Get the Windows build working again (recover from `struct` module changes). ........ r46155 | fredrik.lundh | 2006-05-23 21:47:35 +0200 (Tue, 23 May 2006) | 3 lines return 0 on misses, not -1. ........ r46156 | tim.peters | 2006-05-23 23:51:35 +0200 (Tue, 23 May 2006) | 4 lines test_struct grew weird behavior under regrtest.py -R, due to a module-level cache. Clearing the cache should make it stop showing up in refleak reports. ........ r46157 | tim.peters | 2006-05-23 23:54:23 +0200 (Tue, 23 May 2006) | 2 lines Whitespace normalization. ........ r46158 | tim.peters | 2006-05-23 23:55:53 +0200 (Tue, 23 May 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r46161 | fredrik.lundh | 2006-05-24 12:20:36 +0200 (Wed, 24 May 2006) | 3 lines use Py_ssize_t for string indexes (thanks, neal!) ........ r46173 | fredrik.lundh | 2006-05-24 16:28:11 +0200 (Wed, 24 May 2006) | 14 lines needforspeed: use "fastsearch" for count and findstring helpers. this results in a 2.5x speedup on the stringbench count tests, and a 20x (!) speedup on the stringbench search/find/contains test, compared to 2.5a2. for more on the algorithm, see: http://effbot.org/zone/stringlib.htm if you get weird results, you can disable the new algoritm by undefining USE_FAST in Objects/unicodeobject.c. enjoy /F ........ r46182 | fredrik.lundh | 2006-05-24 17:11:01 +0200 (Wed, 24 May 2006) | 3 lines needforspeedindeed: use fastsearch also for __contains__ ........ r46184 | bob.ippolito | 2006-05-24 17:32:06 +0200 (Wed, 24 May 2006) | 1 line refactor unpack, add unpack_from ........ r46189 | fredrik.lundh | 2006-05-24 18:35:18 +0200 (Wed, 24 May 2006) | 4 lines needforspeed: refactored the replace code slightly; special-case constant-length changes; use fastsearch to locate the first match. ........ r46198 | andrew.dalke | 2006-05-24 20:55:37 +0200 (Wed, 24 May 2006) | 10 lines Added a slew of test for string replace, based various corner cases from the Need For Speed sprint coding. Includes commented out overflow tests which will be uncommented once the code is fixed. This test will break the 8-bit string tests because "".replace("", "A") == "" when it should == "A" We have a fix for it, which should be added tomorrow. ........ r46200 | tim.peters | 2006-05-24 22:27:18 +0200 (Wed, 24 May 2006) | 2 lines We can't leave the checked-in tests broken. ........ r46201 | tim.peters | 2006-05-24 22:29:44 +0200 (Wed, 24 May 2006) | 2 lines Whitespace normalization. ........ r46202 | tim.peters | 2006-05-24 23:00:45 +0200 (Wed, 24 May 2006) | 4 lines Disable the damn empty-string replace test -- it can't be make to pass now for unicode if it passes for str, or vice versa. ........ r46203 | tim.peters | 2006-05-24 23:10:40 +0200 (Wed, 24 May 2006) | 58 lines Heavily fiddled variant of patch #1442927: PyLong_FromString optimization. ``long(str, base)`` is now up to 6x faster for non-power-of-2 bases. The largest speedup is for inputs with about 1000 decimal digits. Conversion from non-power-of-2 bases remains quadratic-time in the number of input digits (it was and remains linear-time for bases 2, 4, 8, 16 and 32). Speedups at various lengths for decimal inputs, comparing 2.4.3 with current trunk. Note that it's actually a bit slower for 1-digit strings: len speedup ---- ------- 1 -4.5% 2 4.6% 3 8.3% 4 12.7% 5 16.9% 6 28.6% 7 35.5% 8 44.3% 9 46.6% 10 55.3% 11 65.7% 12 77.7% 13 73.4% 14 75.3% 15 85.2% 16 103.0% 17 95.1% 18 112.8% 19 117.9% 20 128.3% 30 174.5% 40 209.3% 50 236.3% 60 254.3% 70 262.9% 80 295.8% 90 297.3% 100 324.5% 200 374.6% 300 403.1% 400 391.1% 500 388.7% 600 440.6% 700 468.7% 800 498.0% 900 507.2% 1000 501.2% 2000 450.2% 3000 463.2% 4000 452.5% 5000 440.6% 6000 439.6% 7000 424.8% 8000 418.1% 9000 417.7% ........ r46204 | andrew.kuchling | 2006-05-25 02:23:03 +0200 (Thu, 25 May 2006) | 1 line Minor edits; add an item ........ r46205 | fred.drake | 2006-05-25 04:42:25 +0200 (Thu, 25 May 2006) | 3 lines fix broken links in PDF (SF patch #1281291, contributed by Rory Yorke) ........ r46208 | walter.doerwald | 2006-05-25 10:53:28 +0200 (Thu, 25 May 2006) | 2 lines Replace tab inside comment with space. ........ r46209 | thomas.wouters | 2006-05-25 13:25:51 +0200 (Thu, 25 May 2006) | 4 lines Fix #1488915, Multiple dots in relative import statement raise SyntaxError. ........ r46210 | thomas.wouters | 2006-05-25 13:26:25 +0200 (Thu, 25 May 2006) | 5 lines Update graminit.c for the fix for #1488915, Multiple dots in relative import statement raise SyntaxError, and add testcase. ........ r46211 | andrew.kuchling | 2006-05-25 14:27:59 +0200 (Thu, 25 May 2006) | 1 line Add entry; and fix a typo ........ r46214 | fredrik.lundh | 2006-05-25 17:22:03 +0200 (Thu, 25 May 2006) | 7 lines needforspeed: speed up upper and lower for 8-bit string objects. (the unicode versions of these are still 2x faster on windows, though...) based on work by Andrew Dalke, with tweaks by yours truly. ........ r46216 | fredrik.lundh | 2006-05-25 17:49:45 +0200 (Thu, 25 May 2006) | 5 lines needforspeed: make new upper/lower work properly for single-character strings too... (thanks to georg brandl for spotting the exact problem faster than anyone else) ........ r46217 | kristjan.jonsson | 2006-05-25 17:53:30 +0200 (Thu, 25 May 2006) | 1 line Added a new macro, Py_IS_FINITE(X). On windows there is an intrinsic for this and it is more efficient than to use !Py_IS_INFINITE(X) && !Py_IS_NAN(X). No change on other platforms ........ r46219 | fredrik.lundh | 2006-05-25 18:10:12 +0200 (Thu, 25 May 2006) | 4 lines needforspeed: _toupper/_tolower is a SUSv2 thing; fall back on ISO C versions if they're not defined. ........ r46220 | andrew.kuchling | 2006-05-25 18:23:15 +0200 (Thu, 25 May 2006) | 1 line Fix comment typos ........ r46221 | andrew.dalke | 2006-05-25 18:30:52 +0200 (Thu, 25 May 2006) | 2 lines Added tests for implementation error we came up with in the need for speed sprint. ........ r46222 | andrew.kuchling | 2006-05-25 18:34:54 +0200 (Thu, 25 May 2006) | 1 line Fix another typo ........ r46223 | kristjan.jonsson | 2006-05-25 18:39:27 +0200 (Thu, 25 May 2006) | 1 line Fix incorrect documentation for the Py_IS_FINITE(X) macro. ........ r46224 | fredrik.lundh | 2006-05-25 18:46:54 +0200 (Thu, 25 May 2006) | 3 lines needforspeed: check for overflow in replace (from Andrew Dalke) ........ r46226 | fredrik.lundh | 2006-05-25 19:08:14 +0200 (Thu, 25 May 2006) | 5 lines needforspeed: new replace implementation by Andrew Dalke. replace is now about 3x faster on my machine, for the replace tests from string- bench. ........ r46227 | tim.peters | 2006-05-25 19:34:03 +0200 (Thu, 25 May 2006) | 5 lines A new table to help string->integer conversion was added yesterday to both mystrtoul.c and longobject.c. Share the table instead. Also cut its size by 64 entries (they had been used for an inscrutable trick originally, but the code no longer tries to use that trick). ........ r46229 | andrew.dalke | 2006-05-25 19:53:00 +0200 (Thu, 25 May 2006) | 11 lines Fixed problem identified by Georg. The special-case in-place code for replace made a copy of the string using PyString_FromStringAndSize(s, n) and modify the copied string in-place. However, 1 (and 0) character strings are shared from a cache. This cause "A".replace("A", "a") to change the cached version of "A" -- used by everyone. Now may the copy with NULL as the string and do the memcpy manually. I've added regression tests to check if this happens in the future. Perhaps there should be a PyString_Copy for this case? ........ r46230 | fredrik.lundh | 2006-05-25 19:55:31 +0200 (Thu, 25 May 2006) | 4 lines needforspeed: use "fastsearch" for count. this results in a 3x speedup for the related stringbench tests. ........ r46231 | andrew.dalke | 2006-05-25 20:03:25 +0200 (Thu, 25 May 2006) | 4 lines Code had returned an ssize_t, upcast to long, then converted with PyInt_FromLong. Now using PyInt_FromSsize_t. ........ r46233 | andrew.kuchling | 2006-05-25 20:11:16 +0200 (Thu, 25 May 2006) | 1 line Comment typo ........ r46234 | andrew.dalke | 2006-05-25 20:18:39 +0200 (Thu, 25 May 2006) | 4 lines Added overflow test for adding two (very) large strings where the new string is over max Py_ssize_t. I have no way to test it on my box or any box I have access to. At least it doesn't break anything. ........ r46235 | bob.ippolito | 2006-05-25 20:20:23 +0200 (Thu, 25 May 2006) | 1 line Faster path for PyLong_FromLongLong, using PyLong_FromLong algorithm ........ r46238 | georg.brandl | 2006-05-25 20:44:09 +0200 (Thu, 25 May 2006) | 3 lines Guard the _active.remove() call to avoid errors when there is no _active list. ........ r46239 | fredrik.lundh | 2006-05-25 20:44:29 +0200 (Thu, 25 May 2006) | 4 lines needforspeed: use fastsearch also for find/index and contains. the related tests are now about 10x faster. ........ r46240 | bob.ippolito | 2006-05-25 20:44:50 +0200 (Thu, 25 May 2006) | 1 line Struct now unpacks to PY_LONG_LONG directly when possible, also include #ifdef'ed out code that will return int instead of long when in bounds (not active since it's an API and doc change) ........ r46241 | jack.diederich | 2006-05-25 20:47:15 +0200 (Thu, 25 May 2006) | 1 line * eliminate warning by reverting tmp_s type to 'const char*' ........ r46242 | bob.ippolito | 2006-05-25 21:03:19 +0200 (Thu, 25 May 2006) | 1 line Fix Cygwin compiler issue ........ r46243 | bob.ippolito | 2006-05-25 21:15:27 +0200 (Thu, 25 May 2006) | 1 line fix a struct regression where long would be returned for short unsigned integers ........ r46244 | georg.brandl | 2006-05-25 21:15:31 +0200 (Thu, 25 May 2006) | 4 lines Replace PyObject_CallFunction calls with only object args with PyObject_CallFunctionObjArgs, which is 30% faster. ........ r46245 | fredrik.lundh | 2006-05-25 21:19:05 +0200 (Thu, 25 May 2006) | 3 lines needforspeed: use insert+reverse instead of append ........ r46246 | bob.ippolito | 2006-05-25 21:33:38 +0200 (Thu, 25 May 2006) | 1 line Use LONG_MIN and LONG_MAX to check Python integer bounds instead of the incorrect INT_MIN and INT_MAX ........ r46248 | bob.ippolito | 2006-05-25 21:56:56 +0200 (Thu, 25 May 2006) | 1 line Use faster struct pack/unpack functions for the endian table that matches the host's ........ r46249 | bob.ippolito | 2006-05-25 21:59:56 +0200 (Thu, 25 May 2006) | 1 line enable darwin/x86 support for libffi and hence ctypes (doesn't yet support --enable-universalsdk) ........ r46252 | georg.brandl | 2006-05-25 22:28:10 +0200 (Thu, 25 May 2006) | 4 lines Someone seems to just have copy-pasted the docs of tp_compare to tp_richcompare ;) ........ r46253 | brett.cannon | 2006-05-25 22:44:08 +0200 (Thu, 25 May 2006) | 2 lines Swap out bare malloc()/free() use for PyMem_MALLOC()/PyMem_FREE() . ........ r46254 | bob.ippolito | 2006-05-25 22:52:38 +0200 (Thu, 25 May 2006) | 1 line squelch gcc4 darwin/x86 compiler warnings ........ r46255 | bob.ippolito | 2006-05-25 23:09:45 +0200 (Thu, 25 May 2006) | 1 line fix test_float regression and 64-bit size mismatch issue ........ r46256 | georg.brandl | 2006-05-25 23:11:56 +0200 (Thu, 25 May 2006) | 3 lines Add a x-ref to newer calling APIs. ........ r46257 | ronald.oussoren | 2006-05-25 23:30:54 +0200 (Thu, 25 May 2006) | 2 lines Fix minor typo in prep_cif.c ........ r46259 | brett.cannon | 2006-05-25 23:33:11 +0200 (Thu, 25 May 2006) | 4 lines Change test_values so that it compares the lowercasing of group names since getgrall() can return all lowercase names while getgrgid() returns proper casing. Discovered on Ubuntu 5.04 (custom). ........ r46261 | tim.peters | 2006-05-25 23:50:17 +0200 (Thu, 25 May 2006) | 7 lines Some Win64 pre-release in 2000 didn't support QueryPerformanceCounter(), but we believe Win64 does support it now. So use in time.clock(). It would be peachy if someone with a Win64 box tried this ;-) ........ r46262 | tim.peters | 2006-05-25 23:52:19 +0200 (Thu, 25 May 2006) | 2 lines Whitespace normalization. ........ r46263 | bob.ippolito | 2006-05-25 23:58:05 +0200 (Thu, 25 May 2006) | 1 line Add missing files from x86 darwin ctypes patch ........ r46264 | brett.cannon | 2006-05-26 00:00:14 +0200 (Fri, 26 May 2006) | 2 lines Move over to use of METH_O and METH_NOARGS. ........ r46265 | tim.peters | 2006-05-26 00:25:25 +0200 (Fri, 26 May 2006) | 3 lines Repair idiot typo, and complete the job of trying to use the Windows time.clock() implementation on Win64. ........ r46266 | tim.peters | 2006-05-26 00:28:46 +0200 (Fri, 26 May 2006) | 9 lines Patch #1494387: SVN longobject.c compiler warnings The SIGCHECK macro defined here has always been bizarre, but it apparently causes compiler warnings on "Sun Studio 11". I believe the warnings are bogus, but it doesn't hurt to make the macro definition saner. Bugfix candidate (but I'm not going to bother). ........ r46268 | fredrik.lundh | 2006-05-26 01:27:53 +0200 (Fri, 26 May 2006) | 8 lines needforspeed: partition for 8-bit strings. for some simple tests, this is on par with a corresponding find, and nearly twice as fast as split(sep, 1) full tests, a unicode version, and documentation will follow to- morrow. ........ r46271 | andrew.kuchling | 2006-05-26 03:46:22 +0200 (Fri, 26 May 2006) | 1 line Add Soc student ........ r46272 | ronald.oussoren | 2006-05-26 10:41:25 +0200 (Fri, 26 May 2006) | 3 lines Without this patch OSX users couldn't add new help sources because the code tried to update one item in a tuple. ........ r46273 | fredrik.lundh | 2006-05-26 10:54:28 +0200 (Fri, 26 May 2006) | 5 lines needforspeed: partition implementation, part two. feel free to improve the documentation and the docstrings. ........ r46274 | georg.brandl | 2006-05-26 11:05:54 +0200 (Fri, 26 May 2006) | 3 lines Clarify docs for str.partition(). ........ r46278 | fredrik.lundh | 2006-05-26 11:46:59 +0200 (Fri, 26 May 2006) | 5 lines needforspeed: use METH_O for argument handling, which made partition some ~15% faster for the current tests (which is noticable faster than a corre- sponding find call). thanks to neal-who-never-sleeps for the tip. ........ r46280 | fredrik.lundh | 2006-05-26 12:27:17 +0200 (Fri, 26 May 2006) | 5 lines needforspeed: use Py_ssize_t for the fastsearch counter and skip length (thanks, neal!). and yes, I've verified that this doesn't slow things down ;-) ........ r46285 | andrew.dalke | 2006-05-26 13:11:38 +0200 (Fri, 26 May 2006) | 2 lines Added a few more test cases for whitespace split. These strings have leading whitespace. ........ r46286 | jack.diederich | 2006-05-26 13:15:17 +0200 (Fri, 26 May 2006) | 1 line use Py_ssize_t in places that may need it ........ r46287 | andrew.dalke | 2006-05-26 13:15:22 +0200 (Fri, 26 May 2006) | 2 lines Added split whitespace checks for characters other than space. ........ r46288 | ronald.oussoren | 2006-05-26 13:17:55 +0200 (Fri, 26 May 2006) | 2 lines Fix buglet in postinstall script, it would generate an invalid .cshrc file. ........ r46290 | georg.brandl | 2006-05-26 13:26:11 +0200 (Fri, 26 May 2006) | 3 lines Add "partition" to UserString. ........ r46291 | fredrik.lundh | 2006-05-26 13:29:39 +0200 (Fri, 26 May 2006) | 5 lines needforspeed: added Py_LOCAL macro, based on the LOCAL macro used for SRE and others. applied Py_LOCAL to relevant portion of ceval, which gives a 1-2% speedup on my machine. ymmv. ........ r46292 | jack.diederich | 2006-05-26 13:37:20 +0200 (Fri, 26 May 2006) | 1 line when generating python code prefer to generate valid python code ........ r46293 | fredrik.lundh | 2006-05-26 13:38:15 +0200 (Fri, 26 May 2006) | 3 lines use Py_LOCAL also for string and unicode objects ........ r46294 | ronald.oussoren | 2006-05-26 13:38:39 +0200 (Fri, 26 May 2006) | 12 lines - Search the sqlite specific search directories after the normal include directories when looking for the version of sqlite to use. - On OSX: * Extract additional include and link directories from the CFLAGS and LDFLAGS, if the user has bothered to specify them we might as wel use them. * Add '-Wl,-search_paths_first' to the extra_link_args for readline and sqlite. This makes it possible to use a static library to override the system provided dynamic library. ........ r46295 | ronald.oussoren | 2006-05-26 13:43:26 +0200 (Fri, 26 May 2006) | 6 lines Integrate installing a framework in the 'make install' target. Until now users had to use 'make frameworkinstall' to install python when it is configured with '--enable-framework'. This tends to confuse users that don't hunt for readme files hidden in platform specific directories :-) ........ r46297 | fredrik.lundh | 2006-05-26 13:54:04 +0200 (Fri, 26 May 2006) | 4 lines needforspeed: added PY_LOCAL_AGGRESSIVE macro to enable "aggressive" LOCAL inlining; also added some missing whitespace ........ r46298 | andrew.kuchling | 2006-05-26 14:01:44 +0200 (Fri, 26 May 2006) | 1 line Typo fixes ........ r46299 | fredrik.lundh | 2006-05-26 14:01:49 +0200 (Fri, 26 May 2006) | 4 lines Py_LOCAL shouldn't be used for data; it works for some .NET 2003 compilers, but Trent's copy thinks that it's an anachronism... ........ r46300 | martin.blais | 2006-05-26 14:03:27 +0200 (Fri, 26 May 2006) | 12 lines Support for buffer protocol for socket and struct. * Added socket.recv_buf() and socket.recvfrom_buf() methods, that use the buffer protocol (send and sendto already did). * Added struct.pack_to(), that is the corresponding buffer compatible method to unpack_from(). * Fixed minor typos in arraymodule. ........ r46302 | ronald.oussoren | 2006-05-26 14:23:20 +0200 (Fri, 26 May 2006) | 6 lines - Remove previous version of the binary distribution script for OSX - Some small bugfixes for the IDLE.app wrapper - Tweaks to build-installer to ensure that python gets build in the right way, including sqlite3. - Updated readme files ........ r46305 | tim.peters | 2006-05-26 14:26:21 +0200 (Fri, 26 May 2006) | 2 lines Whitespace normalization. ........ r46307 | andrew.dalke | 2006-05-26 14:28:15 +0200 (Fri, 26 May 2006) | 7 lines I like tests. The new split functions use a preallocated list. Added tests which exceed the preallocation size, to exercise list appends/resizes. Also added more edge case tests. ........ r46308 | andrew.dalke | 2006-05-26 14:31:00 +0200 (Fri, 26 May 2006) | 2 lines Test cases for off-by-one errors in string split with multicharacter pattern. ........ r46309 | tim.peters | 2006-05-26 14:31:20 +0200 (Fri, 26 May 2006) | 2 lines Whitespace normalization. ........ r46313 | andrew.kuchling | 2006-05-26 14:39:48 +0200 (Fri, 26 May 2006) | 1 line Add str.partition() ........ r46314 | bob.ippolito | 2006-05-26 14:52:53 +0200 (Fri, 26 May 2006) | 1 line quick hack to fix busted binhex test ........ r46316 | andrew.dalke | 2006-05-26 15:05:55 +0200 (Fri, 26 May 2006) | 2 lines Added more rstrip tests, including for prealloc'ed arrays ........ r46320 | bob.ippolito | 2006-05-26 15:15:44 +0200 (Fri, 26 May 2006) | 1 line fix #1229380 No struct.pack exception for some out of range integers ........ r46325 | tim.peters | 2006-05-26 15:39:17 +0200 (Fri, 26 May 2006) | 2 lines Use open() to open files (was using file()). ........ r46327 | andrew.dalke | 2006-05-26 16:00:45 +0200 (Fri, 26 May 2006) | 37 lines Changes to string.split/rsplit on whitespace to preallocate space in the results list. Originally it allocated 0 items and used the list growth during append. Now it preallocates 12 items so the first few appends don't need list reallocs. ("Here are some words ."*2).split(None, 1) is 7% faster ("Here are some words ."*2).split() is is 15% faster (Your milage may vary, see dealership for details.) File parsing like this for line in f: count += len(line.split()) is also about 15% faster. There is a slowdown of about 3% for large strings because of the additional overhead of checking if the append is to a preallocated region of the list or not. This will be the rare case. It could be improved with special case code but we decided it was not useful enough. There is a cost of 12*sizeof(PyObject *) bytes per list. For the normal case of file parsing this is not a problem because of the lists have a short lifetime. We have not come up with cases where this is a problem in real life. I chose 12 because human text averages about 11 words per line in books, one of my data sets averages 6.2 words with a final peak at 11 words per line, and I work with a tab delimited data set with 8 tabs per line (or 9 words per line). 12 encompasses all of these. Also changed the last rstrip code to append then reverse, rather than doing insert(0). The strip() and rstrip() times are now comparable. ........ r46328 | tim.peters | 2006-05-26 16:02:05 +0200 (Fri, 26 May 2006) | 5 lines Explicitly close files. I'm trying to stop the frequent spurious test_tarfile failures on Windows buildbots, but it's hard to know how since the regrtest failure output is useless here, and it never fails when a buildbot slave runs test_tarfile the second time in verbose mode. ........ r46329 | andrew.kuchling | 2006-05-26 16:03:41 +0200 (Fri, 26 May 2006) | 1 line Add buffer support for struct, socket ........ r46330 | andrew.kuchling | 2006-05-26 16:04:19 +0200 (Fri, 26 May 2006) | 1 line Typo fix ........ r46331 | bob.ippolito | 2006-05-26 16:07:23 +0200 (Fri, 26 May 2006) | 1 line Fix distutils so that libffi will cross-compile between darwin/x86 and darwin/ppc ........ r46333 | bob.ippolito | 2006-05-26 16:23:21 +0200 (Fri, 26 May 2006) | 1 line Fix _struct typo that broke some 64-bit platforms ........ r46335 | bob.ippolito | 2006-05-26 16:29:35 +0200 (Fri, 26 May 2006) | 1 line Enable PY_USE_INT_WHEN_POSSIBLE in struct ........ r46343 | andrew.dalke | 2006-05-26 17:21:01 +0200 (Fri, 26 May 2006) | 2 lines Eeked out another 3% or so performance in split whitespace by cleaning up the algorithm. ........ r46352 | andrew.dalke | 2006-05-26 18:22:52 +0200 (Fri, 26 May 2006) | 3 lines Test for more edge strip cases; leading and trailing separator gets removed even with strip(..., 0) ........ r46354 | bob.ippolito | 2006-05-26 18:23:28 +0200 (Fri, 26 May 2006) | 1 line fix signed/unsigned mismatch in struct ........ r46355 | steve.holden | 2006-05-26 18:27:59 +0200 (Fri, 26 May 2006) | 5 lines Add -t option to allow easy test selection. Action verbose option correctly. Tweak operation counts. Add empty and new instances tests. Enable comparisons across different warp factors. Change version. ........ r46356 | fredrik.lundh | 2006-05-26 18:32:42 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: use Py_LOCAL on a few more locals in stringobject.c ........ r46357 | thomas.heller | 2006-05-26 18:42:44 +0200 (Fri, 26 May 2006) | 4 lines For now, I gave up with automatic conversion of reST to Python-latex, so I'm writing this in latex now. Skeleton for the ctypes reference. ........ r46358 | tim.peters | 2006-05-26 18:49:28 +0200 (Fri, 26 May 2006) | 3 lines Repair Windows compiler warnings about mixing signed and unsigned integral types in comparisons. ........ r46359 | tim.peters | 2006-05-26 18:52:04 +0200 (Fri, 26 May 2006) | 2 lines Whitespace normalization. ........ r46360 | tim.peters | 2006-05-26 18:53:04 +0200 (Fri, 26 May 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r46362 | fredrik.lundh | 2006-05-26 19:04:58 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: stringlib refactoring (in progress) ........ r46363 | thomas.heller | 2006-05-26 19:18:33 +0200 (Fri, 26 May 2006) | 1 line Write some docs. ........ r46364 | fredrik.lundh | 2006-05-26 19:22:38 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: stringlib refactoring (in progress) ........ r46366 | fredrik.lundh | 2006-05-26 19:26:39 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: cleanup ........ r46367 | fredrik.lundh | 2006-05-26 19:31:41 +0200 (Fri, 26 May 2006) | 4 lines needforspeed: remove remaining USE_FAST macros; if fastsearch was broken, someone would have noticed by now ;-) ........ r46368 | steve.holden | 2006-05-26 19:41:32 +0200 (Fri, 26 May 2006) | 5 lines Use minimum calibration time rather than avergae to avoid the illusion of negative run times. Halt with an error if run times go below 10 ms, indicating that results will be unreliable. ........ r46370 | thomas.heller | 2006-05-26 19:47:40 +0200 (Fri, 26 May 2006) | 2 lines Reordered, and wrote more docs. ........ r46372 | georg.brandl | 2006-05-26 20:03:31 +0200 (Fri, 26 May 2006) | 9 lines Need for speed: Patch #921466 : sys.path_importer_cache is now used to cache valid and invalid file paths for the built-in import machinery which leads to fewer open calls on startup. Also fix issue with PEP 302 style import hooks which lead to more open() calls than necessary. ........ r46373 | fredrik.lundh | 2006-05-26 20:05:34 +0200 (Fri, 26 May 2006) | 3 lines removed unnecessary include ........ r46377 | fredrik.lundh | 2006-05-26 20:15:38 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: added rpartition implementation ........ r46380 | fredrik.lundh | 2006-05-26 20:24:15 +0200 (Fri, 26 May 2006) | 5 lines needspeed: rpartition documentation, tests, and a bug fixes. feel free to add more tests and improve the documentation. ........ r46381 | steve.holden | 2006-05-26 20:26:21 +0200 (Fri, 26 May 2006) | 4 lines Revert tests to MAL's original round sizes to retiain comparability from long ago and far away. Stop calling this pybench 1.4 because it isn't. Remove the empty test, which was a bad idea. ........ r46387 | andrew.kuchling | 2006-05-26 20:41:18 +0200 (Fri, 26 May 2006) | 1 line Add rpartition() and path caching ........ r46388 | andrew.dalke | 2006-05-26 21:02:09 +0200 (Fri, 26 May 2006) | 10 lines substring split now uses /F's fast string matching algorithm. (If compiled without FAST search support, changed the pre-memcmp test to check the last character as well as the first. This gave a 25% speedup for my test case.) Rewrote the split algorithms so they stop when maxsplit gets to 0. Previously they did a string match first then checked if the maxsplit was reached. The new way prevents a needless string search. ........ r46391 | brett.cannon | 2006-05-26 21:04:47 +0200 (Fri, 26 May 2006) | 2 lines Change C spacing to 4 spaces by default to match PEP 7 for new C files. ........ r46392 | georg.brandl | 2006-05-26 21:04:47 +0200 (Fri, 26 May 2006) | 3 lines Exception isn't the root of all exception classes anymore. ........ r46397 | fredrik.lundh | 2006-05-26 21:23:21 +0200 (Fri, 26 May 2006) | 3 lines added rpartition method to UserString class ........ r46398 | fredrik.lundh | 2006-05-26 21:24:53 +0200 (Fri, 26 May 2006) | 4 lines needforspeed: stringlib refactoring, continued. added count and find helpers; updated unicodeobject to use stringlib_count ........ r46400 | fredrik.lundh | 2006-05-26 21:29:05 +0200 (Fri, 26 May 2006) | 4 lines needforspeed: stringlib refactoring: use stringlib/find for unicode find ........ r46403 | fredrik.lundh | 2006-05-26 21:33:03 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: use a macro to fix slice indexes ........ r46404 | thomas.heller | 2006-05-26 21:43:45 +0200 (Fri, 26 May 2006) | 1 line Write more docs. ........ r46406 | fredrik.lundh | 2006-05-26 21:48:07 +0200 (Fri, 26 May 2006) | 3 lines needforspeed: stringlib refactoring: use stringlib/find for string find ........ r46407 | andrew.kuchling | 2006-05-26 21:51:10 +0200 (Fri, 26 May 2006) | 1 line Comment typo ........ r46409 | georg.brandl | 2006-05-26 22:04:44 +0200 (Fri, 26 May 2006) | 3 lines Replace Py_BuildValue("OO") by PyTuple_Pack. ........ r46411 | georg.brandl | 2006-05-26 22:14:47 +0200 (Fri, 26 May 2006) | 2 lines Patch #1492218: document None being a constant. ........ r46415 | georg.brandl | 2006-05-26 22:22:50 +0200 (Fri, 26 May 2006) | 3 lines Simplify calling. ........ r46416 | andrew.dalke | 2006-05-26 22:25:22 +0200 (Fri, 26 May 2006) | 4 lines Added limits to the replace code so it does not count all of the matching patterns in a string, only the number needed by the max limit. ........ r46417 | bob.ippolito | 2006-05-26 22:25:23 +0200 (Fri, 26 May 2006) | 1 line enable all of the struct tests, use ssize_t, fix some whitespace ........ r46418 | tim.peters | 2006-05-26 22:56:56 +0200 (Fri, 26 May 2006) | 2 lines Record Iceland sprint attendees. ........ r46421 | tim.peters | 2006-05-26 23:51:13 +0200 (Fri, 26 May 2006) | 2 lines Whitespace normalization. ........ r46422 | steve.holden | 2006-05-27 00:17:54 +0200 (Sat, 27 May 2006) | 2 lines Add Richard Tew to developers ........ r46423 | steve.holden | 2006-05-27 00:33:20 +0200 (Sat, 27 May 2006) | 2 lines Update help text and documentaition. ........ r46424 | steve.holden | 2006-05-27 00:39:27 +0200 (Sat, 27 May 2006) | 2 lines Blasted typos ... ........ r46425 | andrew.dalke | 2006-05-27 00:49:03 +0200 (Sat, 27 May 2006) | 2 lines Added description of why splitlines doesn't use the prealloc strategy ........ r46426 | tim.peters | 2006-05-27 01:14:37 +0200 (Sat, 27 May 2006) | 19 lines Patch 1145039. set_exc_info(), reset_exc_info(): By exploiting the likely (who knows?) invariant that when an exception's `type` is NULL, its `value` and `traceback` are also NULL, save some cycles in heavily-executed code. This is a "a kronar saved is a kronar earned" patch: the speedup isn't reliably measurable, but it obviously does reduce the operation count in the normal (no exception raised) path through PyEval_EvalFrameEx(). The tim-exc_sanity branch tries to push this harder, but is still blowing up (at least in part due to pre-existing subtle bugs that appear to have no other visible consequences!). Not a bugfix candidate. ........ r46429 | steve.holden | 2006-05-27 02:51:52 +0200 (Sat, 27 May 2006) | 2 lines Reinstate new-style object tests. ........ r46430 | neal.norwitz | 2006-05-27 07:18:57 +0200 (Sat, 27 May 2006) | 1 line Fix compiler warning (and whitespace) on Mac OS 10.4. (A lot of this code looked duplicated, I wonder if a utility function could help reduce the duplication here.) ........ r46431 | neal.norwitz | 2006-05-27 07:21:30 +0200 (Sat, 27 May 2006) | 4 lines Fix Coverity warnings. - Check the correct variable (str_obj, not str) for NULL - sep_len was already verified it wasn't 0 ........ r46432 | martin.v.loewis | 2006-05-27 10:36:52 +0200 (Sat, 27 May 2006) | 2 lines Patch 1494554: Update numeric properties to Unicode 4.1. ........ r46433 | martin.v.loewis | 2006-05-27 10:54:29 +0200 (Sat, 27 May 2006) | 2 lines Explain why 'consumed' is initialized. ........ r46436 | fredrik.lundh | 2006-05-27 12:05:10 +0200 (Sat, 27 May 2006) | 3 lines needforspeed: more stringlib refactoring ........ r46438 | fredrik.lundh | 2006-05-27 12:39:48 +0200 (Sat, 27 May 2006) | 5 lines needforspeed: backed out the Py_LOCAL-isation of ceval; the massive in- lining killed performance on certain Intel boxes, and the "aggressive" macro itself gives most of the benefits on others. ........ r46439 | andrew.dalke | 2006-05-27 13:04:36 +0200 (Sat, 27 May 2006) | 2 lines fixed typo ........ r46440 | martin.v.loewis | 2006-05-27 13:07:49 +0200 (Sat, 27 May 2006) | 2 lines Revert bogus change committed in 46432 to this file. ........ r46444 | andrew.kuchling | 2006-05-27 13:26:33 +0200 (Sat, 27 May 2006) | 1 line Add Py_LOCAL macros ........ r46450 | bob.ippolito | 2006-05-27 13:47:12 +0200 (Sat, 27 May 2006) | 1 line Remove the range checking and int usage #defines from _struct and strip out the now-dead code ........ r46454 | bob.ippolito | 2006-05-27 14:11:36 +0200 (Sat, 27 May 2006) | 1 line Fix up struct docstrings, add struct.pack_to function for symmetry ........ r46456 | richard.jones | 2006-05-27 14:29:24 +0200 (Sat, 27 May 2006) | 2 lines Conversion of exceptions over from faked-up classes to new-style C types. ........ r46457 | georg.brandl | 2006-05-27 14:30:25 +0200 (Sat, 27 May 2006) | 3 lines Add news item for new-style exception class branch merge. ........ r46458 | tim.peters | 2006-05-27 14:36:53 +0200 (Sat, 27 May 2006) | 3 lines More random thrashing trying to understand spurious Windows failures. Who's keeping a bz2 file open? ........ r46460 | andrew.kuchling | 2006-05-27 15:44:37 +0200 (Sat, 27 May 2006) | 1 line Mention new-style exceptions ........ r46461 | richard.jones | 2006-05-27 15:50:42 +0200 (Sat, 27 May 2006) | 1 line credit where credit is due ........ r46462 | georg.brandl | 2006-05-27 16:02:03 +0200 (Sat, 27 May 2006) | 3 lines Always close BZ2Proxy object. Remove unnecessary struct usage. ........ r46463 | tim.peters | 2006-05-27 16:13:13 +0200 (Sat, 27 May 2006) | 2 lines The cheery optimism of old age. ........ r46464 | andrew.dalke | 2006-05-27 16:16:40 +0200 (Sat, 27 May 2006) | 2 lines cleanup - removed trailing whitespace ........ r46465 | georg.brandl | 2006-05-27 16:41:55 +0200 (Sat, 27 May 2006) | 3 lines Remove spurious semicolons after macro invocations. ........ r46468 | fredrik.lundh | 2006-05-27 16:58:20 +0200 (Sat, 27 May 2006) | 4 lines needforspeed: replace improvements, changed to Py_LOCAL_INLINE where appropriate ........ r46469 | fredrik.lundh | 2006-05-27 17:20:22 +0200 (Sat, 27 May 2006) | 4 lines needforspeed: stringlib refactoring: changed find_obj to find_slice, to enable use from stringobject ........ r46470 | fredrik.lundh | 2006-05-27 17:26:19 +0200 (Sat, 27 May 2006) | 3 lines needforspeed: stringlib refactoring: use find_slice for stringobject ........ r46472 | kristjan.jonsson | 2006-05-27 17:41:31 +0200 (Sat, 27 May 2006) | 1 line Add a PCBuild8 build directory for building with Visual Studio .NET 2005. Contains a special project to perform profile guided optimizations on the pythoncore.dll, by instrumenting and running pybench.py ........ r46473 | jack.diederich | 2006-05-27 17:44:34 +0200 (Sat, 27 May 2006) | 3 lines needforspeed: use PyObject_MALLOC instead of system malloc for small allocations. Use PyMem_MALLOC for larger (1k+) chunks. 1%-2% speedup. ........ r46474 | bob.ippolito | 2006-05-27 17:53:49 +0200 (Sat, 27 May 2006) | 1 line fix struct regression on 64-bit platforms ........ r46475 | richard.jones | 2006-05-27 18:07:28 +0200 (Sat, 27 May 2006) | 1 line doc string additions and tweaks ........ r46477 | richard.jones | 2006-05-27 18:15:11 +0200 (Sat, 27 May 2006) | 1 line move semicolons ........ r46478 | george.yoshida | 2006-05-27 18:32:44 +0200 (Sat, 27 May 2006) | 2 lines minor markup nits ........ r46488 | george.yoshida | 2006-05-27 18:51:43 +0200 (Sat, 27 May 2006) | 3 lines End of Ch.3 is now about "with statement". Avoid obsolescence by directly referring to the section. ........ r46489 | george.yoshida | 2006-05-27 19:09:17 +0200 (Sat, 27 May 2006) | 2 lines fix typo ........ --- ccompiler.py | 6 ++--- command/bdist_msi.py | 16 ++++++------- command/build_ext.py | 5 ++++ command/upload.py | 2 +- msvccompiler.py | 2 +- sysconfig.py | 19 ++++++++++++++-- unixccompiler.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++-- util.py | 49 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 145 insertions(+), 18 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 6dad757a..1349abeb 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,7 +15,6 @@ from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath from distutils.dep_util import newer_pairwise, newer_group -from distutils.sysconfig import python_build from distutils.util import split_quoted, execute from distutils import log @@ -368,7 +367,7 @@ class CCompiler: # Get the list of expected output (object) files objects = self.object_filenames(sources, - strip_dir=python_build, + strip_dir=0, output_dir=outdir) assert len(objects) == len(sources) @@ -475,8 +474,7 @@ class CCompiler: which source files can be skipped. """ # Get the list of expected output (object) files - objects = self.object_filenames(sources, strip_dir=python_build, - output_dir=output_dir) + objects = self.object_filenames(sources, output_dir=output_dir) assert len(objects) == len(sources) if self.force: diff --git a/command/bdist_msi.py b/command/bdist_msi.py index f05d66cb..75db8773 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -1,5 +1,5 @@ # -*- coding: iso-8859-1 -*- -# Copyright (C) 2005 Martin v. Löwis +# Copyright (C) 2005, 2006 Martin v. Löwis # Licensed to PSF under a Contributor Agreement. # The bdist_wininst command proper # based on bdist_wininst @@ -16,7 +16,7 @@ from distutils.version import StrictVersion from distutils.errors import DistutilsOptionError from distutils import log import msilib -from msilib import schema, sequence, uisample +from msilib import schema, sequence, text from msilib import Directory, Feature, Dialog, add_data class PyDialog(Dialog): @@ -374,8 +374,8 @@ class bdist_msi (Command): ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), ("ProgressDlg", None, 1280)]) - add_data(db, 'ActionText', uisample.ActionText) - add_data(db, 'UIText', uisample.UIText) + add_data(db, 'ActionText', text.ActionText) + add_data(db, 'UIText', text.UIText) ##################################################################### # Standard dialogs: FatalError, UserExit, ExitDialog fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title, @@ -502,9 +502,9 @@ class bdist_msi (Command): seldlg.back("< Back", None, active=0) c = seldlg.next("Next >", "Cancel") - c.event("SetTargetPath", "TARGETDIR", order=1) - c.event("SpawnWaitDialog", "WaitForCostingDlg", order=2) - c.event("EndDialog", "Return", order=3) + c.event("SetTargetPath", "TARGETDIR", ordering=1) + c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=2) + c.event("EndDialog", "Return", ordering=3) c = seldlg.cancel("Cancel", "DirectoryCombo") c.event("SpawnDialog", "CancelDlg") @@ -561,7 +561,7 @@ class bdist_msi (Command): c = whichusers.next("Next >", "Cancel") c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) - c.event("EndDialog", "Return", order = 2) + c.event("EndDialog", "Return", ordering = 2) c = whichusers.cancel("Cancel", "AdminInstall") c.event("SpawnDialog", "CancelDlg") diff --git a/command/build_ext.py b/command/build_ext.py index 57712521..9626710b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -689,6 +689,11 @@ class build_ext (Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib, "m"] + extra + + elif sys.platform == 'darwin': + # Don't use the default code below + return ext.libraries + else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): diff --git a/command/upload.py b/command/upload.py index 6f4ce81f..4a9ed398 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ from distutils.errors import * from distutils.core import Command from distutils.spawn import spawn from distutils import log -from md5 import md5 +from hashlib import md5 import os import socket import platform diff --git a/msvccompiler.py b/msvccompiler.py index f88f3652..d24d0ac6 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -618,7 +618,7 @@ class MSVCCompiler (CCompiler) : "but the expected registry settings are not present.\n" "You must at least run the Visual Studio GUI once " "so that these entries are created.") - break + break return [] def set_path_env_var(self, name): diff --git a/sysconfig.py b/sysconfig.py index 49536f0d..e1397a19 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -366,8 +366,8 @@ def _init_posix(): # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so # it needs to be compatible. # If it isn't set we set it to the configure-time value - if sys.platform == 'darwin' and g.has_key('CONFIGURE_MACOSX_DEPLOYMENT_TARGET'): - cfg_target = g['CONFIGURE_MACOSX_DEPLOYMENT_TARGET'] + if sys.platform == 'darwin' and g.has_key('MACOSX_DEPLOYMENT_TARGET'): + cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') if cur_target == '': cur_target = cfg_target @@ -500,6 +500,21 @@ def get_config_vars(*args): _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + if sys.platform == 'darwin': + kernel_version = os.uname()[2] # Kernel version (8.4.3) + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # On Mac OS X before 10.4, check if -arch and -isysroot + # are in CFLAGS or LDFLAGS and remove them if they are. + # This is needed when building extensions on a 10.3 system + # using a universal build of python. + for key in ('LDFLAGS', 'BASECFLAGS'): + flags = _config_vars[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = re.sub('-isysroot [^ \t]* ', ' ', flags) + _config_vars[key] = flags + if args: vals = [] for name in args: diff --git a/unixccompiler.py b/unixccompiler.py index 56998c35..324819d4 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -42,6 +42,48 @@ from distutils import log # should just happily stuff them into the preprocessor/compiler/linker # options and carry on. +def _darwin_compiler_fixup(compiler_so, cc_args): + """ + This function will strip '-isysroot PATH' and '-arch ARCH' from the + compile flags if the user has specified one them in extra_compile_flags. + + This is needed because '-arch ARCH' adds another architecture to the + build, without a way to remove an architecture. Furthermore GCC will + barf if multiple '-isysroot' arguments are present. + """ + stripArch = stripSysroot = 0 + + compiler_so = list(compiler_so) + kernel_version = os.uname()[2] # 8.4.3 + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # OSX before 10.4.0, these don't support -arch and -isysroot at + # all. + stripArch = stripSysroot = True + else: + stripArch = '-arch' in cc_args + stripSysroot = '-isysroot' in cc_args + + if stripArch: + while 1: + try: + index = compiler_so.index('-arch') + # Strip this argument and the next one: + del compiler_so[index:index+2] + except ValueError: + break + + if stripSysroot: + try: + index = compiler_so.index('-isysroot') + # Strip this argument and the next one: + del compiler_so[index:index+1] + except ValueError: + pass + + return compiler_so + class UnixCCompiler(CCompiler): compiler_type = 'unix' @@ -108,8 +150,11 @@ class UnixCCompiler(CCompiler): raise CompileError, msg def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + compiler_so = self.compiler_so + if sys.platform == 'darwin': + compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs) try: - self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + + self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) except DistutilsExecError, msg: raise CompileError, msg @@ -172,7 +217,22 @@ class UnixCCompiler(CCompiler): else: linker = self.linker_so[:] if target_lang == "c++" and self.compiler_cxx: - linker[0] = self.compiler_cxx[0] + # skip over environment variable settings if /usr/bin/env + # is used to set up the linker's environment. + # This is needed on OSX. Note: this assumes that the + # normal and C++ compiler have the same environment + # settings. + i = 0 + if os.path.basename(linker[0]) == "env": + i = 1 + while '=' in linker[i]: + i = i + 1 + + linker[i] = self.compiler_cxx[i] + + if sys.platform == 'darwin': + linker = _darwin_compiler_fixup(linker, ld_args) + self.spawn(linker + ld_args) except DistutilsExecError, msg: raise LinkError, msg diff --git a/util.py b/util.py index 889bf136..1265f4c4 100644 --- a/util.py +++ b/util.py @@ -45,6 +45,7 @@ def get_platform (): osname = string.lower(osname) osname = string.replace(osname, '/', '') machine = string.replace(machine, ' ', '_') + machine = string.replace(machine, '/', '-') if osname[:5] == "linux": # At least on Linux/Intel, 'machine' is the processor -- @@ -66,6 +67,54 @@ def get_platform (): m = rel_re.match(release) if m: release = m.group() + elif osname[:6] == "darwin": + # + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # to. This makes the compatibility story a bit more sane because the + # machine is going to compile and link as if it were + # MACOSX_DEPLOYMENT_TARGET. + from distutils.sysconfig import get_config_vars + cfgvars = get_config_vars() + + macver = os.environ.get('MACOSX_DEPLOYMENT_TARGET') + if not macver: + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + + if not macver: + # Get the system version. Reading this plist is a documented + # way to get the system version (see the documentation for + # the Gestalt Manager) + try: + f = open('/System/Library/CoreServices/SystemVersion.plist') + except IOError: + # We're on a plain darwin box, fall back to the default + # behaviour. + pass + else: + m = re.search( + r'ProductUserVisibleVersion\s*' + + r'(.*?)', f.read()) + f.close() + if m is not None: + macver = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + + if macver: + from distutils.sysconfig import get_config_vars + release = macver + osname = "macosx" + + + if (release + '.') < '10.4.' and \ + get_config_vars().get('UNIVERSALSDK', '').strip(): + # The universal build will build fat binaries, but not on + # systems before 10.4 + machine = 'fat' + + elif machine in ('PowerPC', 'Power_Macintosh'): + # Pick a sane name for the PPC architecture. + machine = 'ppc' return "%s-%s-%s" % (osname, release, machine) -- cgit v1.2.1 From 5baff4ddf909616e95ebd3eb3febcc6ebfa471d9 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 27 Jun 2006 10:08:25 +0000 Subject: MacOSX: fix rather dumb buglet that made it impossible to create extensions on OSX 10.3 when using a binary distribution build on 10.4. --- sysconfig.py | 2 +- unixccompiler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 2ba3c4db..c3b1e4ec 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -512,7 +512,7 @@ def get_config_vars(*args): for key in ('LDFLAGS', 'BASECFLAGS'): flags = _config_vars[key] flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = re.sub('-isysroot [^ \t]* ', ' ', flags) + flags = re.sub('-isysroot [^ \t]*', ' ', flags) _config_vars[key] = flags if args: diff --git a/unixccompiler.py b/unixccompiler.py index 324819d4..6cd14f77 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -78,7 +78,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): try: index = compiler_so.index('-isysroot') # Strip this argument and the next one: - del compiler_so[index:index+1] + del compiler_so[index:index+2] except ValueError: pass -- cgit v1.2.1 From 07d3e934ec7648b1c34a414ecd2da1ecd82b4813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 3 Jul 2006 12:28:58 +0000 Subject: Bug #1267547: Put proper recursive setup.py call into the spec file generated by bdist_rpm. --- command/bdist_rpm.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 738e3f72..5b099658 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -467,7 +467,8 @@ class bdist_rpm (Command): # rpm scripts # figure out default build script - def_build = "%s setup.py build" % self.python + def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0])) + def_build = "%s build" % def_setup_call if self.use_rpm_opt_flags: def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build @@ -481,9 +482,9 @@ class bdist_rpm (Command): ('prep', 'prep_script', "%setup"), ('build', 'build_script', def_build), ('install', 'install_script', - ("%s setup.py install " + ("%s install " "--root=$RPM_BUILD_ROOT " - "--record=INSTALLED_FILES") % self.python), + "--record=INSTALLED_FILES") % def_setup_call), ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), ('verifyscript', 'verify_script', None), ('pre', 'pre_install', None), -- cgit v1.2.1 From 26275077cfdade84786eef6b0bdf0f082bccdb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 10 Jul 2006 07:23:48 +0000 Subject: Introduce DISTUTILS_USE_SDK as a flag to determine whether the SDK environment should be used. Fixes #1508010. --- msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index d24d0ac6..d725905b 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -237,7 +237,7 @@ class MSVCCompiler (CCompiler) : def initialize(self): self.__paths = [] - if os.environ.has_key("MSSdk") and self.find_exe("cl.exe"): + if os.environ.has_key("DISTUTILS_USE_SDK") and os.environ.has_key("MSSdk") and self.find_exe("cl.exe"): # Assume that the SDK set up everything alright; don't try to be # smarter self.cc = "cl.exe" -- cgit v1.2.1 From c536bc62ba9d9ab1ada6951d08f7638b8b13ac22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 10 Jul 2006 07:26:41 +0000 Subject: Change error message to indicate that VS2003 is necessary to build extension modules, not the .NET SDK. --- msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index d725905b..c96d5274 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -131,7 +131,7 @@ class MacroExpander: self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") except KeyError, exc: # raise DistutilsPlatformError, \ - ("The .NET Framework SDK needs to be installed before " + ("Visual Studio 2003 needs to be installed before " "building extensions for Python.") p = r"Software\Microsoft\NET Framework Setup\Product" -- cgit v1.2.1 From 50345b356d829f38ac236b497f5eca14852db45e Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Mon, 10 Jul 2006 19:18:35 +0000 Subject: Fix SF#1457312: bad socket error handling in distutils "upload" command. --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 4a9ed398..67ba0804 100644 --- a/command/upload.py +++ b/command/upload.py @@ -185,7 +185,7 @@ class upload(Command): http.endheaders() http.send(body) except socket.error, e: - self.announce(e.msg, log.ERROR) + self.announce(str(e), log.ERROR) return r = http.getresponse() -- cgit v1.2.1 From c26881ae32a53f1cfc3389764c10a16ff034bc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 27 Jul 2006 06:38:16 +0000 Subject: Bump distutils version to 2.5, as several new features have been introduced since 2.4. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a1dbb4b5..8a28531c 100644 --- a/__init__.py +++ b/__init__.py @@ -12,4 +12,4 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "2.4.0" +__version__ = "2.5.0" -- cgit v1.2.1 From 747bd24f77e15e41ecc476bc63cf71a56709a7df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 30 Jul 2006 13:14:05 +0000 Subject: Base __version__ on sys.version_info, as distutils is no longer maintained separatedly. --- __init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8a28531c..9c60e546 100644 --- a/__init__.py +++ b/__init__.py @@ -12,4 +12,6 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "2.5.0" +import sys +__version__ = "%d.%d.%d" % sys.version_info[:3] +del sys -- cgit v1.2.1 From 44994ba49455c4f3ba2c3c5f2e0cc9780168cb4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 30 Jul 2006 13:27:31 +0000 Subject: Mention Cygwin in distutils error message about a missing VS 2003. Fixes #1257728. --- msvccompiler.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index c96d5274..0d72837e 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -131,8 +131,10 @@ class MacroExpander: self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") except KeyError, exc: # raise DistutilsPlatformError, \ - ("Visual Studio 2003 needs to be installed before " - "building extensions for Python.") + ("""Python was built with Visual Studio 2003; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2003 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") p = r"Software\Microsoft\NET Framework Setup\Product" for base in HKEYS: -- cgit v1.2.1 From e11eb794d3d6a0845f8d742935a884c82f9eaaf0 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 11 Aug 2006 14:57:12 +0000 Subject: Merged revisions 46753-51188 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r46755 | brett.cannon | 2006-06-08 18:23:04 +0200 (Thu, 08 Jun 2006) | 4 lines Make binascii.hexlify() use s# for its arguments instead of t# to actually match its documentation stating it accepts any read-only buffer. ........ r46757 | brett.cannon | 2006-06-08 19:00:45 +0200 (Thu, 08 Jun 2006) | 8 lines Buffer objects would return the read or write buffer for a wrapped object when the char buffer was requested. Now it actually returns the char buffer if available or raises a TypeError if it isn't (as is raised for the other buffer types if they are not present but requested). Not a backport candidate since it does change semantics of the buffer object (although it could be argued this is enough of a bug to bother backporting). ........ r46760 | andrew.kuchling | 2006-06-09 03:10:17 +0200 (Fri, 09 Jun 2006) | 1 line Update functools section ........ r46762 | tim.peters | 2006-06-09 04:11:02 +0200 (Fri, 09 Jun 2006) | 6 lines Whitespace normalization. Since test_file is implicated in mysterious test failures when followed by test_optparse, if I had any brains I'd look at the checkin that last changed test_file ;-) ........ r46763 | tim.peters | 2006-06-09 05:09:42 +0200 (Fri, 09 Jun 2006) | 5 lines To boost morale :-), force test_optparse to run immediately after test_file until we can figure out how to fix it. (See python-dev; at the moment we don't even know which checkin caused the problem.) ........ r46764 | tim.peters | 2006-06-09 05:51:41 +0200 (Fri, 09 Jun 2006) | 6 lines AutoFileTests.tearDown(): Removed mysterious undocumented try/except. Remove TESTFN. Throughout: used open() instead of file(), and wrapped long lines. ........ r46765 | tim.peters | 2006-06-09 06:02:06 +0200 (Fri, 09 Jun 2006) | 8 lines testUnicodeOpen(): I have no idea why, but making this test clean up after itself appears to fix the test failures when test_optparse follows test_file. test_main(): Get rid of TESTFN no matter what. That's also enough to fix the mystery failures. Doesn't hurt to fix them twice :-) ........ r46766 | tim.peters | 2006-06-09 07:12:40 +0200 (Fri, 09 Jun 2006) | 6 lines Remove the temporary hack to force test_optparse to run immediately after test_file. At least 8 buildbot boxes passed since the underlying problem got fixed, and they all failed before the fix, so there's no point to this anymore. ........ r46767 | neal.norwitz | 2006-06-09 07:54:18 +0200 (Fri, 09 Jun 2006) | 1 line Fix grammar and reflow ........ r46769 | andrew.kuchling | 2006-06-09 12:22:35 +0200 (Fri, 09 Jun 2006) | 1 line Markup fix ........ r46773 | andrew.kuchling | 2006-06-09 15:15:57 +0200 (Fri, 09 Jun 2006) | 1 line [Bug #1472827] Make saxutils.XMLGenerator handle \r\n\t in attribute values by escaping them properly. 2.4 bugfix candidate. ........ r46778 | kristjan.jonsson | 2006-06-09 18:28:01 +0200 (Fri, 09 Jun 2006) | 2 lines Turn off warning about deprecated CRT functions on for VisualStudio .NET 2005. Make the definition #ARRAYSIZE conditional. VisualStudio .NET 2005 already has it defined using a better gimmick. ........ r46779 | phillip.eby | 2006-06-09 18:40:18 +0200 (Fri, 09 Jun 2006) | 2 lines Import wsgiref into the stdlib, as of the external version 0.1-r2181. ........ r46783 | andrew.kuchling | 2006-06-09 18:44:40 +0200 (Fri, 09 Jun 2006) | 1 line Add note about XMLGenerator bugfix ........ r46784 | andrew.kuchling | 2006-06-09 18:46:51 +0200 (Fri, 09 Jun 2006) | 1 line Add note about wsgiref ........ r46785 | brett.cannon | 2006-06-09 19:05:48 +0200 (Fri, 09 Jun 2006) | 2 lines Fix inconsistency in naming within an enum. ........ r46787 | tim.peters | 2006-06-09 19:47:00 +0200 (Fri, 09 Jun 2006) | 2 lines Whitespace normalization. ........ r46792 | georg.brandl | 2006-06-09 20:29:52 +0200 (Fri, 09 Jun 2006) | 3 lines Test file.__exit__. ........ r46794 | brett.cannon | 2006-06-09 20:40:46 +0200 (Fri, 09 Jun 2006) | 2 lines svn:ignore .pyc and .pyo files. ........ r46795 | georg.brandl | 2006-06-09 20:45:48 +0200 (Fri, 09 Jun 2006) | 3 lines RFE #1491485: str/unicode.endswith()/startswith() now accept a tuple as first argument. ........ r46798 | andrew.kuchling | 2006-06-09 21:03:16 +0200 (Fri, 09 Jun 2006) | 1 line Describe startswith()/endswiith() change; add reminder about wsgiref ........ r46799 | tim.peters | 2006-06-09 21:24:44 +0200 (Fri, 09 Jun 2006) | 11 lines Implementing a happy idea from Georg Brandl: make runtest() try to clean up files and directories the tests often leave behind by mistake. This is the first time in history I don't have a bogus "db_home" directory after running the tests ;-) Also worked on runtest's docstring, to say something about all the arguments, and to document the non-obvious return values. New functions runtest_inner() and cleanup_test_droppings() in support of the above. ........ r46800 | andrew.kuchling | 2006-06-09 21:43:25 +0200 (Fri, 09 Jun 2006) | 1 line Remove unused variable ........ r46801 | andrew.kuchling | 2006-06-09 21:56:05 +0200 (Fri, 09 Jun 2006) | 1 line Add some wsgiref text ........ r46803 | thomas.heller | 2006-06-09 21:59:11 +0200 (Fri, 09 Jun 2006) | 1 line set eol-style svn property ........ r46804 | thomas.heller | 2006-06-09 22:01:01 +0200 (Fri, 09 Jun 2006) | 1 line set eol-style svn property ........ r46805 | georg.brandl | 2006-06-09 22:43:48 +0200 (Fri, 09 Jun 2006) | 3 lines Make use of new str.startswith/endswith semantics. Occurences in email and compiler were ignored due to backwards compat requirements. ........ r46806 | brett.cannon | 2006-06-10 00:31:23 +0200 (Sat, 10 Jun 2006) | 4 lines An object with __call__ as an attribute, when called, will have that attribute checked for __call__ itself, and will continue to look until it finds an object without the attribute. This can lead to an infinite recursion. Closes bug #532646, again. Will be backported. ........ r46808 | brett.cannon | 2006-06-10 00:45:54 +0200 (Sat, 10 Jun 2006) | 2 lines Fix bug introduced in rev. 46806 by not having variable declaration at the top of a block. ........ r46812 | georg.brandl | 2006-06-10 08:40:50 +0200 (Sat, 10 Jun 2006) | 4 lines Apply perky's fix for #1503157: "/".join([u"", u""]) raising OverflowError. Also improve error message on overflow. ........ r46817 | martin.v.loewis | 2006-06-10 10:14:03 +0200 (Sat, 10 Jun 2006) | 2 lines Port cygwin kill_python changes from 2.4 branch. ........ r46818 | armin.rigo | 2006-06-10 12:57:40 +0200 (Sat, 10 Jun 2006) | 4 lines SF bug #1503294. PyThreadState_GET() complains if the tstate is NULL, but only in debug mode. ........ r46819 | martin.v.loewis | 2006-06-10 14:23:46 +0200 (Sat, 10 Jun 2006) | 4 lines Patch #1495999: Part two of Windows CE changes. - update header checks, using autoconf - provide dummies for getenv, environ, and GetVersion - adjust MSC_VER check in socketmodule.c ........ r46820 | skip.montanaro | 2006-06-10 16:09:11 +0200 (Sat, 10 Jun 2006) | 1 line document the class, not its initializer ........ r46821 | greg.ward | 2006-06-10 18:40:01 +0200 (Sat, 10 Jun 2006) | 4 lines Sync with Optik docs (rev 518): * restore "Extending optparse" section * document ALWAYS_TYPED_ACTIONS (SF #1449311) ........ r46824 | thomas.heller | 2006-06-10 21:51:46 +0200 (Sat, 10 Jun 2006) | 8 lines Upgrade to ctypes version 0.9.9.7. Summary of changes: - support for 'variable sized' data - support for anonymous structure/union fields - fix severe bug with certain arrays or structures containing more than 256 fields ........ r46825 | thomas.heller | 2006-06-10 21:55:36 +0200 (Sat, 10 Jun 2006) | 8 lines Upgrade to ctypes version 0.9.9.7. Summary of changes: - support for 'variable sized' data - support for anonymous structure/union fields - fix severe bug with certain arrays or structures containing more than 256 fields ........ r46826 | fred.drake | 2006-06-10 22:01:34 +0200 (Sat, 10 Jun 2006) | 4 lines SF patch #1303595: improve description of __builtins__, explaining how it varies between __main__ and other modules, and strongly suggest not touching it but using __builtin__ if absolutely necessary ........ r46827 | fred.drake | 2006-06-10 22:02:58 +0200 (Sat, 10 Jun 2006) | 1 line credit for SF patch #1303595 ........ r46831 | thomas.heller | 2006-06-10 22:29:34 +0200 (Sat, 10 Jun 2006) | 2 lines New docs for ctypes. ........ r46834 | thomas.heller | 2006-06-10 23:07:19 +0200 (Sat, 10 Jun 2006) | 1 line Fix a wrong printf format. ........ r46835 | thomas.heller | 2006-06-10 23:17:58 +0200 (Sat, 10 Jun 2006) | 1 line Fix the second occurrence of the problematic printf format. ........ r46837 | thomas.heller | 2006-06-10 23:56:03 +0200 (Sat, 10 Jun 2006) | 1 line Don't use C++ comment. ........ r46838 | thomas.heller | 2006-06-11 00:01:50 +0200 (Sun, 11 Jun 2006) | 1 line Handle failure of PyMem_Realloc. ........ r46839 | skip.montanaro | 2006-06-11 00:38:13 +0200 (Sun, 11 Jun 2006) | 2 lines Suppress warning on MacOSX about possible use before set of proc. ........ r46840 | tim.peters | 2006-06-11 00:51:45 +0200 (Sun, 11 Jun 2006) | 8 lines shuffle() doscstring: Removed warning about sequence length versus generator period. While this was a real weakness of the older WH generator for lists with just a few dozen elements, and so could potentially bite the naive ;-), the Twister should show excellent behavior up to at least 600 elements. Module docstring: reflowed some jarringly short lines. ........ r46844 | greg.ward | 2006-06-11 02:40:49 +0200 (Sun, 11 Jun 2006) | 4 lines Bug #1361643: fix textwrap.dedent() so it handles tabs appropriately, i.e. do *not* expand tabs, but treat them as whitespace that is not equivalent to spaces. Add a couple of test cases. Clarify docs. ........ r46850 | neal.norwitz | 2006-06-11 07:44:18 +0200 (Sun, 11 Jun 2006) | 5 lines Fix Coverity # 146. newDBSequenceObject would deref dbobj, so it can't be NULL. We know it's not NULL from the ParseTuple and DbObject_Check will verify it's not NULL. ........ r46851 | neal.norwitz | 2006-06-11 07:45:25 +0200 (Sun, 11 Jun 2006) | 4 lines Wrap some long lines Top/Bottom factor out some common expressions Add a XXX comment about widing offset. ........ r46852 | neal.norwitz | 2006-06-11 07:45:47 +0200 (Sun, 11 Jun 2006) | 1 line Add versionadded to doc ........ r46853 | neal.norwitz | 2006-06-11 07:47:14 +0200 (Sun, 11 Jun 2006) | 3 lines Update doc to make it agree with code. Bottom factor out some common code. ........ r46854 | neal.norwitz | 2006-06-11 07:48:14 +0200 (Sun, 11 Jun 2006) | 3 lines f_code can't be NULL based on Frame_New and other code that derefs it. So there doesn't seem to be much point to checking here. ........ r46855 | neal.norwitz | 2006-06-11 09:26:27 +0200 (Sun, 11 Jun 2006) | 1 line Fix errors found by pychecker ........ r46856 | neal.norwitz | 2006-06-11 09:26:50 +0200 (Sun, 11 Jun 2006) | 1 line warnings was imported at module scope, no need to import again ........ r46857 | neal.norwitz | 2006-06-11 09:27:56 +0200 (Sun, 11 Jun 2006) | 5 lines Fix errors found by pychecker. I think these changes are correct, but I'm not sure. Could someone who knows how this module works test it? It can at least start on the cmd line. ........ r46858 | neal.norwitz | 2006-06-11 10:35:14 +0200 (Sun, 11 Jun 2006) | 1 line Fix errors found by pychecker ........ r46859 | ronald.oussoren | 2006-06-11 16:33:36 +0200 (Sun, 11 Jun 2006) | 4 lines This patch improves the L&F of IDLE on OSX. The changes are conditionalized on being in an IDLE.app bundle on darwin. This does a slight reorganisation of the menus and adds support for file-open events. ........ r46860 | greg.ward | 2006-06-11 16:42:41 +0200 (Sun, 11 Jun 2006) | 1 line SF #1366250: optparse docs: fix inconsistency in variable name; minor tweaks. ........ r46861 | greg.ward | 2006-06-11 18:24:11 +0200 (Sun, 11 Jun 2006) | 3 lines Bug #1498146: fix optparse to handle Unicode strings in option help, description, and epilog. ........ r46862 | thomas.heller | 2006-06-11 19:04:22 +0200 (Sun, 11 Jun 2006) | 2 lines Release the GIL during COM method calls, to avoid deadlocks in Python coded COM objects. ........ r46863 | tim.peters | 2006-06-11 21:42:51 +0200 (Sun, 11 Jun 2006) | 2 lines Whitespace normalization. ........ r46864 | tim.peters | 2006-06-11 21:43:49 +0200 (Sun, 11 Jun 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r46865 | ronald.oussoren | 2006-06-11 21:45:57 +0200 (Sun, 11 Jun 2006) | 2 lines Remove message about using make frameworkinstall, that's no longer necesssary ........ r46866 | ronald.oussoren | 2006-06-11 22:23:29 +0200 (Sun, 11 Jun 2006) | 2 lines Use configure to substitute the correct prefix instead of hardcoding ........ r46867 | ronald.oussoren | 2006-06-11 22:24:45 +0200 (Sun, 11 Jun 2006) | 4 lines - Change fixapplepython23.py to ensure that it will run with /usr/bin/python on intel macs. - Fix some minor problems in the installer for OSX ........ r46868 | neal.norwitz | 2006-06-11 22:25:56 +0200 (Sun, 11 Jun 2006) | 5 lines Try to fix several networking tests. The problem is that if hosts have a search path setup, some of these hosts resolve to the wrong address. By appending a period to the hostname, the hostname should only resolve to what we want it to resolve to. Hopefully this doesn't break different bots. ........ r46869 | neal.norwitz | 2006-06-11 22:42:02 +0200 (Sun, 11 Jun 2006) | 7 lines Try to fix another networking test. The problem is that if hosts have a search path setup, some of these hosts resolve to the wrong address. By appending a period to the hostname, the hostname should only resolve to what we want it to resolve to. Hopefully this doesn't break different bots. Also add more info to failure message to aid debugging test failure. ........ r46870 | neal.norwitz | 2006-06-11 22:46:46 +0200 (Sun, 11 Jun 2006) | 4 lines Fix test on PPC64 buildbot. It raised an IOError (really an URLError which derives from an IOError). That seems valid. Env Error includes both OSError and IOError, so this seems like a reasonable fix. ........ r46871 | tim.peters | 2006-06-11 22:52:59 +0200 (Sun, 11 Jun 2006) | 10 lines compare_generic_iter(): Fixed the failure of test_wsgiref's testFileWrapper when running with -O. test_simple_validation_error still fails under -O. That appears to be because wsgiref's validate.py uses `assert` statements all over the place to check arguments for sanity. That should all be changed (it's not a logical error in the software if a user passes bogus arguments, so this isn't a reasonable use for `assert` -- checking external preconditions should generally raise ValueError or TypeError instead, as appropriate). ........ r46872 | neal.norwitz | 2006-06-11 23:38:38 +0200 (Sun, 11 Jun 2006) | 1 line Get test to pass on S/390. Shout if you think this change is incorrect. ........ r46873 | neal.norwitz | 2006-06-12 04:05:55 +0200 (Mon, 12 Jun 2006) | 1 line Cleanup Py_ssize_t a little (get rid of second #ifdef) ........ r46874 | neal.norwitz | 2006-06-12 04:06:17 +0200 (Mon, 12 Jun 2006) | 1 line Fix some Py_ssize_t issues ........ r46875 | neal.norwitz | 2006-06-12 04:06:42 +0200 (Mon, 12 Jun 2006) | 1 line Fix some Py_ssize_t issues ........ r46876 | neal.norwitz | 2006-06-12 04:07:24 +0200 (Mon, 12 Jun 2006) | 2 lines Cleanup: Remove import of types to get StringTypes, we can just use basestring. ........ r46877 | neal.norwitz | 2006-06-12 04:07:57 +0200 (Mon, 12 Jun 2006) | 1 line Don't truncate if size_t is bigger than uint ........ r46878 | neal.norwitz | 2006-06-12 04:08:41 +0200 (Mon, 12 Jun 2006) | 1 line Don't leak the list object if there's an error allocating the item storage. Backport candidate ........ r46879 | neal.norwitz | 2006-06-12 04:09:03 +0200 (Mon, 12 Jun 2006) | 1 line Fix typo. Backport if anyone cares. :-) ........ r46880 | neal.norwitz | 2006-06-12 04:09:34 +0200 (Mon, 12 Jun 2006) | 1 line Fix indentation of case and a Py_ssize_t issue. ........ r46881 | neal.norwitz | 2006-06-12 04:11:18 +0200 (Mon, 12 Jun 2006) | 3 lines Get rid of f_restricted too. Doc the other 4 ints that were already removed at the NeedForSpeed sprint. ........ r46882 | neal.norwitz | 2006-06-12 04:13:21 +0200 (Mon, 12 Jun 2006) | 1 line Fix the socket tests so they can be run concurrently. Backport candidate ........ r46883 | neal.norwitz | 2006-06-12 04:16:10 +0200 (Mon, 12 Jun 2006) | 1 line i and j are initialized below when used. No need to do it twice ........ r46884 | neal.norwitz | 2006-06-12 05:05:03 +0200 (Mon, 12 Jun 2006) | 1 line Remove unused import ........ r46885 | neal.norwitz | 2006-06-12 05:05:40 +0200 (Mon, 12 Jun 2006) | 1 line Impl ssize_t ........ r46886 | neal.norwitz | 2006-06-12 05:33:09 +0200 (Mon, 12 Jun 2006) | 6 lines Patch #1503046, Conditional compilation of zlib.(de)compressobj.copy copy is only in newer versions of zlib. This should allow zlibmodule to work with older versions like the Tru64 buildbot. ........ r46887 | phillip.eby | 2006-06-12 06:04:32 +0200 (Mon, 12 Jun 2006) | 2 lines Sync w/external release 0.1.2. Please see PEP 360 before making changes to external packages. ........ r46888 | martin.v.loewis | 2006-06-12 06:26:31 +0200 (Mon, 12 Jun 2006) | 2 lines Get rid of function pointer cast. ........ r46889 | thomas.heller | 2006-06-12 08:05:57 +0200 (Mon, 12 Jun 2006) | 3 lines I don't know how that happend, but the entire file contents was duplicated. Thanks to Simon Percivall for the heads up. ........ r46890 | nick.coghlan | 2006-06-12 10:19:37 +0200 (Mon, 12 Jun 2006) | 1 line Fix site module docstring to match the code ........ r46891 | nick.coghlan | 2006-06-12 10:23:02 +0200 (Mon, 12 Jun 2006) | 1 line Fix site module docstring to match the code for Mac OSX, too ........ r46892 | nick.coghlan | 2006-06-12 10:27:13 +0200 (Mon, 12 Jun 2006) | 1 line The site module documentation also described the Windows behaviour incorrectly. ........ r46893 | nick.coghlan | 2006-06-12 12:17:11 +0200 (Mon, 12 Jun 2006) | 1 line Make the -m switch conform to the documentation of sys.path by behaving like the -c switch ........ r46894 | kristjan.jonsson | 2006-06-12 17:45:12 +0200 (Mon, 12 Jun 2006) | 2 lines Fix the CRT argument error handling for VisualStudio .NET 2005. Install a CRT error handler and disable the assertion for debug builds. This causes CRT to set errno to EINVAL. This update fixes crash cases in the test suite where the default CRT error handler would cause process exit. ........ r46899 | thomas.heller | 2006-06-12 22:56:48 +0200 (Mon, 12 Jun 2006) | 1 line Add pep-291 compatibility markers. ........ r46901 | ka-ping.yee | 2006-06-13 01:47:52 +0200 (Tue, 13 Jun 2006) | 5 lines Add the uuid module. This module has been tested so far on Windows XP (Python 2.4 and 2.5a2), Mac OS X (Python 2.3, 2.4, and 2.5a2), and Linux (Python 2.4 and 2.5a2). ........ r46902 | tim.peters | 2006-06-13 02:30:01 +0200 (Tue, 13 Jun 2006) | 2 lines Whitespace normalization. ........ r46903 | tim.peters | 2006-06-13 02:30:50 +0200 (Tue, 13 Jun 2006) | 2 lines Added missing svn:eol-style property to text files. ........ r46905 | tim.peters | 2006-06-13 05:30:07 +0200 (Tue, 13 Jun 2006) | 5 lines get_matching_blocks(): rewrote code & comments so they match; added more comments about why it's this way at all; and removed what looked like needless expense (sorting (i, j, k) triples directly should give exactly the same order as sorting (i, (i, j, k)) pairs). ........ r46906 | neal.norwitz | 2006-06-13 06:08:53 +0200 (Tue, 13 Jun 2006) | 1 line Don't fail if another process is listening on our port. ........ r46908 | neal.norwitz | 2006-06-13 10:28:19 +0200 (Tue, 13 Jun 2006) | 2 lines Initialize the type object so pychecker can't crash the interpreter. ........ r46909 | neal.norwitz | 2006-06-13 10:41:06 +0200 (Tue, 13 Jun 2006) | 1 line Verify the crash due to EncodingMap not initialized does not return ........ r46910 | thomas.heller | 2006-06-13 10:56:14 +0200 (Tue, 13 Jun 2006) | 3 lines Add some windows datatypes that were missing from this file, and add the aliases defined in windows header files for the structures. ........ r46911 | thomas.heller | 2006-06-13 11:40:14 +0200 (Tue, 13 Jun 2006) | 3 lines Add back WCHAR, UINT, DOUBLE, _LARGE_INTEGER, _ULARGE_INTEGER. VARIANT_BOOL is a special _ctypes data type, not c_short. ........ r46912 | ronald.oussoren | 2006-06-13 13:19:56 +0200 (Tue, 13 Jun 2006) | 4 lines Linecache contains support for PEP302 loaders, but fails to deal with loaders that return None to indicate that the module is valid but no source is available. This patch fixes that. ........ r46913 | andrew.kuchling | 2006-06-13 13:57:04 +0200 (Tue, 13 Jun 2006) | 1 line Mention uuid module ........ r46915 | walter.doerwald | 2006-06-13 14:02:12 +0200 (Tue, 13 Jun 2006) | 2 lines Fix passing errors to the encoder and decoder functions. ........ r46917 | walter.doerwald | 2006-06-13 14:04:43 +0200 (Tue, 13 Jun 2006) | 3 lines errors is an attribute in the incremental decoder not an argument. ........ r46919 | andrew.macintyre | 2006-06-13 17:04:24 +0200 (Tue, 13 Jun 2006) | 11 lines Patch #1454481: Make thread stack size runtime tunable. Heavily revised, comprising revisions: 46640 - original trunk revision (backed out in r46655) 46647 - markup fix (backed out in r46655) 46692:46918 merged from branch aimacintyre-sf1454481 branch tested on buildbots (Windows buildbots had problems not related to these changes). ........ r46920 | brett.cannon | 2006-06-13 18:06:55 +0200 (Tue, 13 Jun 2006) | 2 lines Remove unused variable. ........ r46921 | andrew.kuchling | 2006-06-13 18:41:41 +0200 (Tue, 13 Jun 2006) | 1 line Add ability to set stack size ........ r46923 | marc-andre.lemburg | 2006-06-13 19:04:26 +0200 (Tue, 13 Jun 2006) | 2 lines Update pybench to version 2.0. ........ r46924 | marc-andre.lemburg | 2006-06-13 19:07:14 +0200 (Tue, 13 Jun 2006) | 2 lines Revert wrong svn copy. ........ r46925 | andrew.macintyre | 2006-06-13 19:14:36 +0200 (Tue, 13 Jun 2006) | 2 lines fix exception usage ........ r46927 | tim.peters | 2006-06-13 20:37:07 +0200 (Tue, 13 Jun 2006) | 2 lines Whitespace normalization. ........ r46928 | marc-andre.lemburg | 2006-06-13 20:56:56 +0200 (Tue, 13 Jun 2006) | 9 lines Updated to pybench 2.0. See svn.python.org/external/pybench-2.0 for the original import of that version. Note that platform.py was not copied over from pybench-2.0 since it is already part of Python 2.5. ........ r46929 | andrew.macintyre | 2006-06-13 21:02:35 +0200 (Tue, 13 Jun 2006) | 5 lines Increase the small thread stack size to get the test to pass reliably on the one buildbot that insists on more than 32kB of thread stack. ........ r46930 | marc-andre.lemburg | 2006-06-13 21:20:07 +0200 (Tue, 13 Jun 2006) | 2 lines Whitespace normalization. ........ r46931 | thomas.heller | 2006-06-13 22:18:43 +0200 (Tue, 13 Jun 2006) | 2 lines More docs for ctypes. ........ r46932 | brett.cannon | 2006-06-13 23:34:24 +0200 (Tue, 13 Jun 2006) | 2 lines Ignore .pyc and .pyo files in Pybench. ........ r46933 | brett.cannon | 2006-06-13 23:46:41 +0200 (Tue, 13 Jun 2006) | 7 lines If a classic class defined a __coerce__() method that just returned its two arguments in reverse, the interpreter would infinitely recourse trying to get a coercion that worked. So put in a recursion check after a coercion is made and the next call to attempt to use the coerced values. Fixes bug #992017 and closes crashers/coerce.py . ........ r46936 | gerhard.haering | 2006-06-14 00:24:47 +0200 (Wed, 14 Jun 2006) | 3 lines Merged changes from external pysqlite 2.3.0 release. Documentation updates will follow in a few hours at the latest. Then we should be ready for beta1. ........ r46937 | brett.cannon | 2006-06-14 00:26:13 +0200 (Wed, 14 Jun 2006) | 2 lines Missed test for rev. 46933; infinite recursion from __coerce__() returning its arguments reversed. ........ r46938 | gerhard.haering | 2006-06-14 00:53:48 +0200 (Wed, 14 Jun 2006) | 2 lines Updated documentation for pysqlite 2.3.0 API. ........ r46939 | tim.peters | 2006-06-14 06:09:25 +0200 (Wed, 14 Jun 2006) | 10 lines SequenceMatcher.get_matching_blocks(): This now guarantees that adjacent triples in the result list describe non-adjacent matching blocks. That's _nice_ to have, and Guido said he wanted it. Not a bugfix candidate: Guido or not ;-), this changes visible endcase semantics (note that some tests had to change), and nothing about this was documented before. Since it was working as designed, and behavior was consistent with the docs, it wasn't "a bug". ........ r46940 | tim.peters | 2006-06-14 06:13:00 +0200 (Wed, 14 Jun 2006) | 2 lines Repaired typo in new comment. ........ r46941 | tim.peters | 2006-06-14 06:15:27 +0200 (Wed, 14 Jun 2006) | 2 lines Whitespace normalization. ........ r46942 | fred.drake | 2006-06-14 06:25:02 +0200 (Wed, 14 Jun 2006) | 3 lines - make some disabled tests run what they intend when enabled - remove some over-zealous triple-quoting ........ r46943 | fred.drake | 2006-06-14 07:04:47 +0200 (Wed, 14 Jun 2006) | 3 lines add tests for two cases that are handled correctly in the current code, but that SF patch 1504676 as written mis-handles ........ r46944 | fred.drake | 2006-06-14 07:15:51 +0200 (Wed, 14 Jun 2006) | 1 line explain an XXX in more detail ........ r46945 | martin.v.loewis | 2006-06-14 07:21:04 +0200 (Wed, 14 Jun 2006) | 1 line Patch #1455898: Incremental mode for "mbcs" codec. ........ r46946 | georg.brandl | 2006-06-14 08:08:31 +0200 (Wed, 14 Jun 2006) | 3 lines Bug #1339007: Shelf objects now don't raise an exception in their __del__ method when initialization failed. ........ r46948 | thomas.heller | 2006-06-14 08:18:15 +0200 (Wed, 14 Jun 2006) | 1 line Fix docstring. ........ r46949 | georg.brandl | 2006-06-14 08:29:07 +0200 (Wed, 14 Jun 2006) | 2 lines Bug #1501122: mention __gt__ &co in description of comparison order. ........ r46951 | thomas.heller | 2006-06-14 09:08:38 +0200 (Wed, 14 Jun 2006) | 1 line Write more docs. ........ r46952 | georg.brandl | 2006-06-14 10:31:39 +0200 (Wed, 14 Jun 2006) | 3 lines Bug #1153163: describe __add__ vs __radd__ behavior when adding objects of same type/of subclasses of the other. ........ r46954 | georg.brandl | 2006-06-14 10:42:11 +0200 (Wed, 14 Jun 2006) | 3 lines Bug #1202018: add some common mime.types locations. ........ r46955 | georg.brandl | 2006-06-14 10:50:03 +0200 (Wed, 14 Jun 2006) | 3 lines Bug #1117556: SimpleHTTPServer now tries to find and use the system's mime.types file for determining MIME types. ........ r46957 | thomas.heller | 2006-06-14 11:09:08 +0200 (Wed, 14 Jun 2006) | 1 line Document paramflags. ........ r46958 | thomas.heller | 2006-06-14 11:20:11 +0200 (Wed, 14 Jun 2006) | 1 line Add an __all__ list, since this module does 'from ctypes import *'. ........ r46959 | andrew.kuchling | 2006-06-14 15:59:15 +0200 (Wed, 14 Jun 2006) | 1 line Add item ........ r46961 | georg.brandl | 2006-06-14 18:46:43 +0200 (Wed, 14 Jun 2006) | 3 lines Bug #805015: doc error in PyUnicode_FromEncodedObject. ........ r46962 | gerhard.haering | 2006-06-15 00:28:37 +0200 (Thu, 15 Jun 2006) | 10 lines - Added version checks in C code to make sure we don't trigger bugs in older SQLite versions. - Added version checks in test suite so that we don't execute tests that we know will fail with older (buggy) SQLite versions. Now, all tests should run against all SQLite versions from 3.0.8 until 3.3.6 (latest one now). The sqlite3 module can be built against all these SQLite versions and the sqlite3 module does its best to not trigger bugs in SQLite, but using SQLite 3.3.3 or later is recommended. ........ r46963 | tim.peters | 2006-06-15 00:38:13 +0200 (Thu, 15 Jun 2006) | 2 lines Whitespace normalization. ........ r46964 | neal.norwitz | 2006-06-15 06:54:29 +0200 (Thu, 15 Jun 2006) | 9 lines Speculative checkin (requires approval of Gerhard Haering) This backs out the test changes in 46962 which prevented crashes by not running the tests via a version check. All the version checks added in that rev were removed from the tests. Code was added to the error handler in connection.c that seems to work with older versions of sqlite including 3.1.3. ........ r46965 | neal.norwitz | 2006-06-15 07:55:49 +0200 (Thu, 15 Jun 2006) | 1 line Try to narrow window of failure on slow/busy boxes (ppc64 buildbot) ........ r46966 | martin.v.loewis | 2006-06-15 08:45:05 +0200 (Thu, 15 Jun 2006) | 2 lines Make import/lookup of mbcs fail on non-Windows systems. ........ r46967 | ronald.oussoren | 2006-06-15 10:14:18 +0200 (Thu, 15 Jun 2006) | 2 lines Patch #1446489 (zipfile: support for ZIP64) ........ r46968 | neal.norwitz | 2006-06-15 10:16:44 +0200 (Thu, 15 Jun 2006) | 6 lines Re-revert this change. Install the version check and don't run the test until Gerhard has time to fully debug the issue. This affects versions before 3.2.1 (possibly only versions earlier than 3.1.3). Based on discussion on python-checkins. ........ r46969 | gregory.p.smith | 2006-06-15 10:52:32 +0200 (Thu, 15 Jun 2006) | 6 lines - bsddb: multithreaded DB access using the simple bsddb module interface now works reliably. It has been updated to use automatic BerkeleyDB deadlock detection and the bsddb.dbutils.DeadlockWrap wrapper to retry database calls that would previously deadlock. [SF python bug #775414] ........ r46970 | gregory.p.smith | 2006-06-15 11:23:52 +0200 (Thu, 15 Jun 2006) | 2 lines minor documentation cleanup. mention the bsddb.db interface explicitly by name. ........ r46971 | neal.norwitz | 2006-06-15 11:57:03 +0200 (Thu, 15 Jun 2006) | 5 lines Steal the trick from test_compiler to print out a slow msg. This will hopefully get the buildbots to pass. Not sure this test will be feasible or even work. But everything is red now, so it can't get much worse. ........ r46972 | neal.norwitz | 2006-06-15 12:24:49 +0200 (Thu, 15 Jun 2006) | 1 line Print some more info to get an idea of how much longer the test will last ........ r46981 | tim.peters | 2006-06-15 20:04:40 +0200 (Thu, 15 Jun 2006) | 6 lines Try to reduce the extreme peak memory and disk-space use of this test. It probably still requires more disk space than most buildbots have, and in any case is still so intrusive that if we don't find another way to test this I'm taking my buildbot offline permanently ;-) ........ r46982 | tim.peters | 2006-06-15 20:06:29 +0200 (Thu, 15 Jun 2006) | 2 lines Whitespace normalization. ........ r46983 | tim.peters | 2006-06-15 20:07:28 +0200 (Thu, 15 Jun 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r46984 | tim.peters | 2006-06-15 20:38:19 +0200 (Thu, 15 Jun 2006) | 2 lines Oops -- I introduced an off-by-6436159488 error. ........ r46990 | neal.norwitz | 2006-06-16 06:30:34 +0200 (Fri, 16 Jun 2006) | 1 line Disable this test until we can determine what to do about it ........ r46991 | neal.norwitz | 2006-06-16 06:31:06 +0200 (Fri, 16 Jun 2006) | 1 line Param name is dir, not directory. Update docstring. Backport candidate ........ r46992 | neal.norwitz | 2006-06-16 06:31:28 +0200 (Fri, 16 Jun 2006) | 1 line Add missing period in comment. ........ r46993 | neal.norwitz | 2006-06-16 06:32:43 +0200 (Fri, 16 Jun 2006) | 1 line Fix whitespace, there are memory leaks in this module. ........ r46995 | fred.drake | 2006-06-17 01:45:06 +0200 (Sat, 17 Jun 2006) | 3 lines SF patch 1504676: Make sgmllib char and entity references pluggable (implementation/tests contributed by Sam Ruby) ........ r46996 | fred.drake | 2006-06-17 03:07:54 +0200 (Sat, 17 Jun 2006) | 1 line fix change that broke the htmllib tests ........ r46998 | martin.v.loewis | 2006-06-17 11:15:14 +0200 (Sat, 17 Jun 2006) | 3 lines Patch #763580: Add name and value arguments to Tkinter variable classes. ........ r46999 | martin.v.loewis | 2006-06-17 11:20:41 +0200 (Sat, 17 Jun 2006) | 2 lines Patch #1096231: Add default argument to wm_iconbitmap. ........ r47000 | martin.v.loewis | 2006-06-17 11:25:15 +0200 (Sat, 17 Jun 2006) | 2 lines Patch #1494750: Destroy master after deleting children. ........ r47003 | george.yoshida | 2006-06-17 18:31:52 +0200 (Sat, 17 Jun 2006) | 2 lines markup fix ........ r47005 | george.yoshida | 2006-06-17 18:39:13 +0200 (Sat, 17 Jun 2006) | 4 lines Update url. Old url returned status code:301 Moved permanently. ........ r47007 | martin.v.loewis | 2006-06-17 20:44:27 +0200 (Sat, 17 Jun 2006) | 2 lines Patch #812986: Update the canvas even if not tracing. ........ r47008 | martin.v.loewis | 2006-06-17 21:03:26 +0200 (Sat, 17 Jun 2006) | 2 lines Patch #815924: Restore ability to pass type= and icon= ........ r47009 | neal.norwitz | 2006-06-18 00:37:45 +0200 (Sun, 18 Jun 2006) | 1 line Fix typo in docstring ........ r47010 | neal.norwitz | 2006-06-18 00:38:15 +0200 (Sun, 18 Jun 2006) | 1 line Fix memory leak reported by valgrind while running test_subprocess ........ r47011 | fred.drake | 2006-06-18 04:57:35 +0200 (Sun, 18 Jun 2006) | 1 line remove unnecessary markup ........ r47013 | neal.norwitz | 2006-06-18 21:35:01 +0200 (Sun, 18 Jun 2006) | 7 lines Prevent spurious leaks when running regrtest.py -R. There may be more issues that crop up from time to time, but this change seems to have been pretty stable (no spurious warnings) for about a week. Other modules which use threads may require similar use of threading_setup/threading_cleanup from test_support. ........ r47014 | neal.norwitz | 2006-06-18 21:37:40 +0200 (Sun, 18 Jun 2006) | 9 lines The hppa ubuntu box sometimes hangs forever in these tests. My guess is that the wait is failing for some reason. Use WNOHANG, so we won't wait until the buildbot kills the test suite. I haven't been able to reproduce the failure, so I'm not sure if this will help or not. Hopefully, this change will cause the test to fail, rather than hang. That will be better since we will get the rest of the test results. It may also help us debug the real problem. ........ r47015 | neal.norwitz | 2006-06-18 22:10:24 +0200 (Sun, 18 Jun 2006) | 1 line Revert 47014 until it is more robust ........ r47016 | thomas.heller | 2006-06-18 23:27:04 +0200 (Sun, 18 Jun 2006) | 6 lines Fix typos. Fix doctest example. Mention in the tutorial that 'errcheck' is explained in the ref manual. Use better wording in some places. Remoce code examples that shouldn't be in the tutorial. Remove some XXX notices. ........ r47017 | georg.brandl | 2006-06-19 00:17:29 +0200 (Mon, 19 Jun 2006) | 3 lines Patch #1507676: improve exception messages in abstract.c, object.c and typeobject.c. ........ r47018 | neal.norwitz | 2006-06-19 07:40:44 +0200 (Mon, 19 Jun 2006) | 1 line Use Py_ssize_t ........ r47019 | georg.brandl | 2006-06-19 08:35:54 +0200 (Mon, 19 Jun 2006) | 3 lines Add news entry about error msg improvement. ........ r47020 | thomas.heller | 2006-06-19 09:07:49 +0200 (Mon, 19 Jun 2006) | 2 lines Try to repair the failing test on the OpenBSD buildbot. Trial and error... ........ r47021 | tim.peters | 2006-06-19 09:45:16 +0200 (Mon, 19 Jun 2006) | 2 lines Whitespace normalization. ........ r47022 | walter.doerwald | 2006-06-19 10:07:50 +0200 (Mon, 19 Jun 2006) | 4 lines Patch #1506645: add Python wrappers for the curses functions is_term_resized, resize_term and resizeterm. This uses three separate configure checks (one for each function). ........ r47023 | walter.doerwald | 2006-06-19 10:14:09 +0200 (Mon, 19 Jun 2006) | 2 lines Make check order match in configure and configure.in. ........ r47024 | tim.peters | 2006-06-19 10:14:28 +0200 (Mon, 19 Jun 2006) | 3 lines Repair KeyError when running test_threaded_import under -R, as reported by Neal on python-dev. ........ r47025 | thomas.heller | 2006-06-19 10:32:46 +0200 (Mon, 19 Jun 2006) | 3 lines Next try to fix the OpenBSD buildbot tests: Use ctypes.util.find_library to locate the C runtime library on platforms where is returns useful results. ........ r47026 | tim.peters | 2006-06-19 11:09:44 +0200 (Mon, 19 Jun 2006) | 13 lines TestHelp.make_parser(): This was making a permanent change to os.environ (setting envar COLUMNS), which at least caused test_float_default() to fail if the tests were run more than once. This repairs the test_optparse -R failures Neal reported on python-dev. It also explains some seemingly bizarre test_optparse failures we saw a couple weeks ago on the buildbots, when test_optparse failed due to test_file failing to clean up after itself, and then test_optparse failed in an entirely different way when regrtest's -w option ran test_optparse a second time. It's now obvious that make_parser() permanently changing os.environ was responsible for the second half of that. ........ r47027 | anthony.baxter | 2006-06-19 14:04:15 +0200 (Mon, 19 Jun 2006) | 2 lines Preparing for 2.5b1. ........ r47029 | fred.drake | 2006-06-19 19:31:16 +0200 (Mon, 19 Jun 2006) | 1 line remove non-working document formats from edist ........ r47030 | gerhard.haering | 2006-06-19 23:17:35 +0200 (Mon, 19 Jun 2006) | 5 lines Fixed a memory leak that was introduced with incorrect usage of the Python weak reference API in pysqlite 2.2.1. Bumbed pysqlite version number to upcoming pysqlite 2.3.1 release. ........ r47032 | ka-ping.yee | 2006-06-20 00:49:36 +0200 (Tue, 20 Jun 2006) | 2 lines Remove Python 2.3 compatibility comment. ........ r47033 | trent.mick | 2006-06-20 01:21:25 +0200 (Tue, 20 Jun 2006) | 2 lines Upgrade pyexpat to expat 2.0.0 (http://python.org/sf/1462338). ........ r47034 | trent.mick | 2006-06-20 01:57:41 +0200 (Tue, 20 Jun 2006) | 3 lines [ 1295808 ] expat symbols should be namespaced in pyexpat (http://python.org/sf/1295808) ........ r47039 | andrew.kuchling | 2006-06-20 13:52:16 +0200 (Tue, 20 Jun 2006) | 1 line Uncomment wsgiref section ........ r47040 | andrew.kuchling | 2006-06-20 14:15:09 +0200 (Tue, 20 Jun 2006) | 1 line Add four library items ........ r47041 | andrew.kuchling | 2006-06-20 14:19:54 +0200 (Tue, 20 Jun 2006) | 1 line Terminology and typography fixes ........ r47042 | andrew.kuchling | 2006-06-20 15:05:12 +0200 (Tue, 20 Jun 2006) | 1 line Add introductory paragraphs summarizing the release; minor edits ........ r47043 | andrew.kuchling | 2006-06-20 15:11:29 +0200 (Tue, 20 Jun 2006) | 1 line Minor edits and rearrangements; markup fix ........ r47044 | andrew.kuchling | 2006-06-20 15:20:30 +0200 (Tue, 20 Jun 2006) | 1 line [Bug #1504456] Mention xml -> xmlcore change ........ r47047 | brett.cannon | 2006-06-20 19:30:26 +0200 (Tue, 20 Jun 2006) | 2 lines Raise TestSkipped when the test socket connection is refused. ........ r47049 | brett.cannon | 2006-06-20 21:20:17 +0200 (Tue, 20 Jun 2006) | 2 lines Fix typo of exception name. ........ r47053 | brett.cannon | 2006-06-21 18:57:57 +0200 (Wed, 21 Jun 2006) | 5 lines At the C level, tuple arguments are passed in directly to the exception constructor, meaning it is treated as *args, not as a single argument. This means using the 'message' attribute won't work (until Py3K comes around), and so one must grab from 'arg' to get the error number. ........ r47054 | andrew.kuchling | 2006-06-21 19:10:18 +0200 (Wed, 21 Jun 2006) | 1 line Link to LibRef module documentation ........ r47055 | andrew.kuchling | 2006-06-21 19:17:10 +0200 (Wed, 21 Jun 2006) | 1 line Note some of Barry's work ........ r47056 | andrew.kuchling | 2006-06-21 19:17:28 +0200 (Wed, 21 Jun 2006) | 1 line Bump version ........ r47057 | georg.brandl | 2006-06-21 19:45:17 +0200 (Wed, 21 Jun 2006) | 3 lines fix [ 1509132 ] compiler module builds incorrect AST for TryExceptFinally ........ r47058 | georg.brandl | 2006-06-21 19:52:36 +0200 (Wed, 21 Jun 2006) | 3 lines Make test_fcntl aware of netbsd3. ........ r47059 | georg.brandl | 2006-06-21 19:53:17 +0200 (Wed, 21 Jun 2006) | 3 lines Patch #1509001: expected skips for netbsd3. ........ r47060 | gerhard.haering | 2006-06-21 22:55:04 +0200 (Wed, 21 Jun 2006) | 2 lines Removed call to enable_callback_tracebacks that slipped in by accident. ........ r47061 | armin.rigo | 2006-06-21 23:58:50 +0200 (Wed, 21 Jun 2006) | 13 lines Fix for an obscure bug introduced by revs 46806 and 46808, with a test. The problem of checking too eagerly for recursive calls is the following: if a RuntimeError is caused by recursion, and if code needs to normalize it immediately (as in the 2nd test), then PyErr_NormalizeException() needs a call to the RuntimeError class to instantiate it, and this hits the recursion limit again... causing PyErr_NormalizeException() to never finish. Moved this particular recursion check to slot_tp_call(), which is not involved in instantiating built-in exceptions. Backport candidate. ........ r47064 | neal.norwitz | 2006-06-22 08:30:50 +0200 (Thu, 22 Jun 2006) | 3 lines Copy the wsgiref package during make install. ........ r47065 | neal.norwitz | 2006-06-22 08:35:30 +0200 (Thu, 22 Jun 2006) | 1 line Reset the doc date to today for the automatic doc builds ........ r47067 | andrew.kuchling | 2006-06-22 15:10:23 +0200 (Thu, 22 Jun 2006) | 1 line Mention how to suppress warnings ........ r47069 | georg.brandl | 2006-06-22 16:46:17 +0200 (Thu, 22 Jun 2006) | 3 lines Set lineno correctly on list, tuple and dict literals. ........ r47070 | georg.brandl | 2006-06-22 16:46:46 +0200 (Thu, 22 Jun 2006) | 4 lines Test for correct compilation of try-except-finally stmt. Test for correct lineno on list, tuple, dict literals. ........ r47071 | fred.drake | 2006-06-22 17:50:08 +0200 (Thu, 22 Jun 2006) | 1 line fix markup nit ........ r47072 | brett.cannon | 2006-06-22 18:49:14 +0200 (Thu, 22 Jun 2006) | 6 lines 'warning's was improperly requiring that a command-line Warning category be both a subclass of Warning and a subclass of types.ClassType. The latter is no longer true thanks to new-style exceptions. Closes bug #1510580. Thanks to AMK for the test. ........ r47073 | ronald.oussoren | 2006-06-22 20:33:54 +0200 (Thu, 22 Jun 2006) | 3 lines MacOSX: Add a message to the first screen of the installer that tells users how to avoid updates to their shell profile. ........ r47074 | georg.brandl | 2006-06-22 21:02:18 +0200 (Thu, 22 Jun 2006) | 3 lines Fix my name ;) ........ r47075 | thomas.heller | 2006-06-22 21:07:36 +0200 (Thu, 22 Jun 2006) | 2 lines Small fixes, mostly in the markup. ........ r47076 | peter.astrand | 2006-06-22 22:06:46 +0200 (Thu, 22 Jun 2006) | 1 line Make it possible to run test_subprocess.py on Python 2.2, which lacks test_support.is_resource_enabled. ........ r47077 | peter.astrand | 2006-06-22 22:21:26 +0200 (Thu, 22 Jun 2006) | 1 line Applied patch #1506758: Prevent MemoryErrors with large MAXFD. ........ r47079 | neal.norwitz | 2006-06-23 05:32:44 +0200 (Fri, 23 Jun 2006) | 1 line Fix refleak ........ r47080 | fred.drake | 2006-06-23 08:03:45 +0200 (Fri, 23 Jun 2006) | 9 lines - SF bug #853506: IP6 address parsing in sgmllib ('[' and ']' were not accepted in unquoted attribute values) - cleaned up tests of character and entity reference decoding so the tests cover the documented relationships among handle_charref, handle_entityref, convert_charref, convert_codepoint, and convert_entityref, without bringing up Unicode issues that sgmllib cannot be involved in ........ r47085 | andrew.kuchling | 2006-06-23 21:23:40 +0200 (Fri, 23 Jun 2006) | 11 lines Fit Makefile for the Python doc environment better; this is a step toward including the howtos in the build process. * Put LaTeX output in ../paper-/. * Put HTML output in ../html/ * Explain some of the Makefile variables * Remove some cruft dating to my environment (e.g. the 'web' target) This makefile isn't currently invoked by the documentation build process, so these changes won't destabilize anything. ........ r47086 | hyeshik.chang | 2006-06-23 23:16:18 +0200 (Fri, 23 Jun 2006) | 5 lines Bug #1511381: codec_getstreamcodec() in codec.c is corrected to omit a default "error" argument for NULL pointer. This allows the parser to take a codec from cjkcodecs again. (Reported by Taewook Kang and reviewed by Walter Doerwald) ........ r47091 | ronald.oussoren | 2006-06-25 22:44:16 +0200 (Sun, 25 Jun 2006) | 6 lines Workaround for bug #1512124 Without this patch IDLE will get unresponsive when you open the debugger window on OSX. This is both using the system Tcl/Tk on Tiger as the latest universal download from tk-components.sf.net. ........ r47092 | ronald.oussoren | 2006-06-25 23:14:19 +0200 (Sun, 25 Jun 2006) | 3 lines Drop the calldll demo's for macos, calldll isn't present anymore, no need to keep the demo's around. ........ r47093 | ronald.oussoren | 2006-06-25 23:15:58 +0200 (Sun, 25 Jun 2006) | 3 lines Use a path without a double slash to compile the .py files after installation (macosx, binary installer). This fixes bug #1508369 for python 2.5. ........ r47094 | ronald.oussoren | 2006-06-25 23:19:06 +0200 (Sun, 25 Jun 2006) | 3 lines Also install the .egg-info files in Lib. This will cause wsgiref.egg-info to be installed. ........ r47097 | andrew.kuchling | 2006-06-26 14:40:02 +0200 (Mon, 26 Jun 2006) | 1 line [Bug #1511998] Various comments from Nick Coghlan; thanks! ........ r47098 | andrew.kuchling | 2006-06-26 14:43:43 +0200 (Mon, 26 Jun 2006) | 1 line Describe workaround for PyRange_New()'s removal ........ r47099 | andrew.kuchling | 2006-06-26 15:08:24 +0200 (Mon, 26 Jun 2006) | 5 lines [Bug #1512163] Fix typo. This change will probably break tests on FreeBSD buildbots, but I'll check in a fix for that next. ........ r47100 | andrew.kuchling | 2006-06-26 15:12:16 +0200 (Mon, 26 Jun 2006) | 9 lines [Bug #1512163] Use one set of locking methods, lockf(); remove the flock() calls. On FreeBSD, the two methods lockf() and flock() end up using the same mechanism and the second one fails. A Linux man page claims that the two methods are orthogonal (so locks acquired one way don't interact with locks acquired the other way) but that clearly must be false. ........ r47101 | andrew.kuchling | 2006-06-26 15:23:10 +0200 (Mon, 26 Jun 2006) | 5 lines Add a test for a conflicting lock. On slow machines, maybe the time intervals (2 sec, 0.5 sec) will be too tight. I'll see how the buildbots like it. ........ r47103 | andrew.kuchling | 2006-06-26 16:33:24 +0200 (Mon, 26 Jun 2006) | 1 line Windows doesn't have os.fork(). I'll just disable this test for now ........ r47106 | andrew.kuchling | 2006-06-26 19:00:35 +0200 (Mon, 26 Jun 2006) | 9 lines Attempt to fix build failure on OS X and Debian alpha; the symptom is consistent with os.wait() returning immediately because some other subprocess had previously exited; the test suite then immediately tries to lock the mailbox and gets an error saying it's already locked. To fix this, do a waitpid() so the test suite only continues once the intended child process has exited. ........ r47113 | neal.norwitz | 2006-06-27 06:06:46 +0200 (Tue, 27 Jun 2006) | 1 line Ignore some more warnings in the dynamic linker on an older gentoo ........ r47114 | neal.norwitz | 2006-06-27 06:09:13 +0200 (Tue, 27 Jun 2006) | 6 lines Instead of doing a make test, run the regression tests out of the installed copy. This will hopefully catch problems where directories are added under Lib/ but not to Makefile.pre.in. This breaks out the 2 runs of the test suite with and without -O which is also nicer. ........ r47115 | neal.norwitz | 2006-06-27 06:12:58 +0200 (Tue, 27 Jun 2006) | 5 lines Fix SF bug #1513032, 'make install' failure on FreeBSD 5.3. No need to install lib-old, it's empty in 2.5. ........ r47116 | neal.norwitz | 2006-06-27 06:23:06 +0200 (Tue, 27 Jun 2006) | 1 line Test unimportant change to verify buildbot does not try to build ........ r47117 | neal.norwitz | 2006-06-27 06:26:30 +0200 (Tue, 27 Jun 2006) | 1 line Try again: test unimportant change to verify buildbot does not try to build ........ r47118 | neal.norwitz | 2006-06-27 06:28:56 +0200 (Tue, 27 Jun 2006) | 1 line Verify buildbot picks up these changes (really needs testing after last change to Makefile.pre.in) ........ r47121 | vinay.sajip | 2006-06-27 09:34:37 +0200 (Tue, 27 Jun 2006) | 1 line Removed buggy exception handling in doRollover of rotating file handlers. Exceptions now propagate to caller. ........ r47123 | ronald.oussoren | 2006-06-27 12:08:25 +0200 (Tue, 27 Jun 2006) | 3 lines MacOSX: fix rather dumb buglet that made it impossible to create extensions on OSX 10.3 when using a binary distribution build on 10.4. ........ r47125 | tim.peters | 2006-06-27 13:52:49 +0200 (Tue, 27 Jun 2006) | 2 lines Whitespace normalization. ........ r47128 | ronald.oussoren | 2006-06-27 14:53:52 +0200 (Tue, 27 Jun 2006) | 8 lines Use staticly build copies of zlib and bzip2 to build the OSX installer, that way the resulting binaries have a better change of running on 10.3. This patch also updates the search logic for sleepycat db3/4, without this patch you cannot use a sleepycat build with a non-standard prefix; with this you can (at least on OSX) if you add the prefix to CPPFLAGS/LDFLAGS at configure-time. This change is needed to build the binary installer for OSX. ........ r47131 | ronald.oussoren | 2006-06-27 17:45:32 +0200 (Tue, 27 Jun 2006) | 5 lines macosx: Install a libpython2.5.a inside the framework as a symlink to the actual dylib at the root of the framework, that way tools that expect a unix-like install (python-config, but more importantly external products like mod_python) work correctly. ........ r47137 | neal.norwitz | 2006-06-28 07:03:22 +0200 (Wed, 28 Jun 2006) | 4 lines According to the man pages on Gentoo Linux and Tru64, EACCES or EAGAIN can be returned if fcntl (lockf) fails. This fixes the test failure on Tru64 by checking for either error rather than just EAGAIN. ........ r47139 | neal.norwitz | 2006-06-28 08:28:31 +0200 (Wed, 28 Jun 2006) | 5 lines Fix bug #1512695: cPickle.loads could crash if it was interrupted with a KeyboardInterrupt since PyTuple_Pack was passed a NULL. Will backport. ........ r47142 | nick.coghlan | 2006-06-28 12:41:47 +0200 (Wed, 28 Jun 2006) | 1 line Make full module name available as __module_name__ even when __name__ is set to something else (like '__main__') ........ r47143 | armin.rigo | 2006-06-28 12:49:51 +0200 (Wed, 28 Jun 2006) | 2 lines A couple of crashers of the "won't fix" kind. ........ r47147 | andrew.kuchling | 2006-06-28 16:25:20 +0200 (Wed, 28 Jun 2006) | 1 line [Bug #1508766] Add docs for uuid module; docs written by George Yoshida, with minor rearrangements by me. ........ r47148 | andrew.kuchling | 2006-06-28 16:27:21 +0200 (Wed, 28 Jun 2006) | 1 line [Bug #1508766] Add docs for uuid module; this puts the module in the 'Internet Protocols' section. Arguably this module could also have gone in the chapters on strings or encodings, maybe even the crypto chapter. Fred, please move if you see fit. ........ r47151 | georg.brandl | 2006-06-28 22:23:25 +0200 (Wed, 28 Jun 2006) | 3 lines Fix end_fill(). ........ r47153 | trent.mick | 2006-06-28 22:30:41 +0200 (Wed, 28 Jun 2006) | 2 lines Mention the expat upgrade and pyexpat fix I put in 2.5b1. ........ r47154 | fred.drake | 2006-06-29 02:51:53 +0200 (Thu, 29 Jun 2006) | 6 lines SF bug #1504333: sgmlib should allow angle brackets in quoted values (modified patch by Sam Ruby; changed to use separate REs for start and end tags to reduce matching cost for end tags; extended tests; updated to avoid breaking previous changes to support IPv6 addresses in unquoted attribute values) ........ r47156 | fred.drake | 2006-06-29 04:57:48 +0200 (Thu, 29 Jun 2006) | 1 line document recent bugfixes in sgmllib ........ r47158 | neal.norwitz | 2006-06-29 06:10:08 +0200 (Thu, 29 Jun 2006) | 10 lines Add new utility function, reap_children(), to test_support. This should be called at the end of each test that spawns children (perhaps it should be called from regrtest instead?). This will hopefully prevent some of the unexplained failures in the buildbots (hppa and alpha) during tests that spawn children. The problems were not reproducible. There were many zombies that remained at the end of several tests. In the worst case, this shouldn't cause any more problems, though it may not help either. Time will tell. ........ r47159 | neal.norwitz | 2006-06-29 07:48:14 +0200 (Thu, 29 Jun 2006) | 5 lines This should fix the buildbot failure on s/390 which can't connect to gmail.org. It makes the error message consistent and always sends to stderr. It would be much better for all the networking tests to hit only python.org. ........ r47161 | thomas.heller | 2006-06-29 20:34:15 +0200 (Thu, 29 Jun 2006) | 3 lines Protect the thread api calls in the _ctypes extension module within #ifdef WITH_THREADS/#endif blocks. Found by Sam Rushing. ........ r47162 | martin.v.loewis | 2006-06-29 20:58:44 +0200 (Thu, 29 Jun 2006) | 2 lines Patch #1509163: MS Toolkit Compiler no longer available ........ r47163 | skip.montanaro | 2006-06-29 21:20:09 +0200 (Thu, 29 Jun 2006) | 1 line add string methods to index ........ r47164 | vinay.sajip | 2006-06-30 02:13:08 +0200 (Fri, 30 Jun 2006) | 1 line Fixed bug in fileConfig() which failed to clear logging._handlerList ........ r47166 | tim.peters | 2006-06-30 08:18:39 +0200 (Fri, 30 Jun 2006) | 2 lines Whitespace normalization. ........ r47170 | neal.norwitz | 2006-06-30 09:32:16 +0200 (Fri, 30 Jun 2006) | 1 line Silence compiler warning ........ r47171 | neal.norwitz | 2006-06-30 09:32:46 +0200 (Fri, 30 Jun 2006) | 1 line Another problem reported by Coverity. Backport candidate. ........ r47175 | thomas.heller | 2006-06-30 19:44:54 +0200 (Fri, 30 Jun 2006) | 2 lines Revert the use of PY_FORMAT_SIZE_T in PyErr_Format. ........ r47176 | tim.peters | 2006-06-30 20:34:51 +0200 (Fri, 30 Jun 2006) | 2 lines Remove now-unused fidding with PY_FORMAT_SIZE_T. ........ r47177 | georg.brandl | 2006-06-30 20:47:56 +0200 (Fri, 30 Jun 2006) | 3 lines Document decorator usage of property. ........ r47181 | fred.drake | 2006-06-30 21:29:25 +0200 (Fri, 30 Jun 2006) | 4 lines - consistency nit: always include "()" in \function and \method (*should* be done by the presentation, but that requires changes all over) - avoid spreading the __name meme ........ r47188 | vinay.sajip | 2006-07-01 12:45:20 +0200 (Sat, 01 Jul 2006) | 1 line Added entry for fileConfig() bugfix. ........ r47189 | vinay.sajip | 2006-07-01 12:47:20 +0200 (Sat, 01 Jul 2006) | 1 line Added duplicate call to fileConfig() to ensure that it cleans up after itself correctly. ........ r47190 | martin.v.loewis | 2006-07-01 17:33:37 +0200 (Sat, 01 Jul 2006) | 2 lines Release all forwarded functions in .close. Fixes #1513223. ........ r47191 | fred.drake | 2006-07-01 18:28:20 +0200 (Sat, 01 Jul 2006) | 7 lines SF bug #1296433 (Expat bug #1515266): Unchecked calls to character data handler would cause a segfault. This merges in Expat's lib/xmlparse.c revisions 1.154 and 1.155, which fix this and a closely related problem (the later does not affect Python). Moved the crasher test to the tests for xml.parsers.expat. ........ r47197 | gerhard.haering | 2006-07-02 19:48:30 +0200 (Sun, 02 Jul 2006) | 4 lines The sqlite3 module did cut off data from the SQLite database at the first null character before sending it to a custom converter. This has been fixed now. ........ r47198 | martin.v.loewis | 2006-07-02 20:44:00 +0200 (Sun, 02 Jul 2006) | 1 line Correct arithmetic in access on Win32. Fixes #1513646. ........ r47203 | thomas.heller | 2006-07-03 09:58:09 +0200 (Mon, 03 Jul 2006) | 1 line Cleanup: Remove commented out code. ........ r47204 | thomas.heller | 2006-07-03 09:59:50 +0200 (Mon, 03 Jul 2006) | 1 line Don't run the doctests with Python 2.3 because it doesn't have the ELLIPSIS flag. ........ r47205 | thomas.heller | 2006-07-03 10:04:05 +0200 (Mon, 03 Jul 2006) | 7 lines Fixes so that _ctypes can be compiled with the MingW compiler. It seems that the definition of '__attribute__(x)' was responsible for the compiler ignoring the '__fastcall' attribute on the ffi_closure_SYSV function in libffi_msvc/ffi.c, took me quite some time to figure this out. ........ r47206 | thomas.heller | 2006-07-03 10:08:14 +0200 (Mon, 03 Jul 2006) | 11 lines Add a new function uses_seh() to the _ctypes extension module. This will return True if Windows Structured Exception handling (SEH) is used when calling functions, False otherwise. Currently, only MSVC supports SEH. Fix the test so that it doesn't crash when run with MingW compiled _ctypes. Note that two tests are still failing when mingw is used, I suspect structure layout differences and function calling conventions between MSVC and MingW. ........ r47207 | tim.peters | 2006-07-03 10:23:19 +0200 (Mon, 03 Jul 2006) | 2 lines Whitespace normalization. ........ r47208 | martin.v.loewis | 2006-07-03 11:44:00 +0200 (Mon, 03 Jul 2006) | 3 lines Only setup canvas when it is first created. Fixes #1514703 ........ r47209 | martin.v.loewis | 2006-07-03 12:05:30 +0200 (Mon, 03 Jul 2006) | 3 lines Reimplement turtle.circle using a polyline, to allow correct filling of arcs. Also fixes #1514693. ........ r47210 | martin.v.loewis | 2006-07-03 12:19:49 +0200 (Mon, 03 Jul 2006) | 3 lines Bug #1514693: Update turtle's heading when switching between degrees and radians. ........ r47211 | martin.v.loewis | 2006-07-03 13:12:06 +0200 (Mon, 03 Jul 2006) | 2 lines Document functions added in 2.3 and 2.5. ........ r47212 | martin.v.loewis | 2006-07-03 14:19:50 +0200 (Mon, 03 Jul 2006) | 3 lines Bug #1417699: Reject locale-specific decimal point in float() and atof(). ........ r47213 | martin.v.loewis | 2006-07-03 14:28:58 +0200 (Mon, 03 Jul 2006) | 3 lines Bug #1267547: Put proper recursive setup.py call into the spec file generated by bdist_rpm. ........ r47215 | martin.v.loewis | 2006-07-03 15:01:35 +0200 (Mon, 03 Jul 2006) | 3 lines Patch #825417: Fix timeout processing in expect, read_until. Will backport to 2.4. ........ r47218 | martin.v.loewis | 2006-07-03 15:47:40 +0200 (Mon, 03 Jul 2006) | 2 lines Put method-wrappers into trashcan. Fixes #927248. ........ r47219 | andrew.kuchling | 2006-07-03 16:07:30 +0200 (Mon, 03 Jul 2006) | 1 line [Bug #1515932] Clarify description of slice assignment ........ r47220 | andrew.kuchling | 2006-07-03 16:16:09 +0200 (Mon, 03 Jul 2006) | 4 lines [Bug #1511911] Clarify description of optional arguments to sorted() by improving the xref to the section on lists, and by copying the explanations of the arguments (with a slight modification). ........ r47223 | kristjan.jonsson | 2006-07-03 16:59:05 +0200 (Mon, 03 Jul 2006) | 1 line Fix build problems with the platform SDK on windows. It is not sufficient to test for the C compiler version when determining if we have the secure CRT from microsoft. Must test with an undocumented macro, __STDC_SECURE_LIB__ too. ........ r47224 | ronald.oussoren | 2006-07-04 14:30:22 +0200 (Tue, 04 Jul 2006) | 7 lines Sync the darwin/x86 port libffi with the copy in PyObjC. This fixes a number of bugs in that port. The most annoying ones were due to some subtle differences between the document ABI and the actual implementation :-( (there are no python unittests that fail without this patch, but without it some of libffi's unittests fail). ........ r47234 | georg.brandl | 2006-07-05 10:21:00 +0200 (Wed, 05 Jul 2006) | 3 lines Remove remaining references to OverflowWarning. ........ r47236 | thomas.heller | 2006-07-05 11:13:56 +0200 (Wed, 05 Jul 2006) | 3 lines Fix the bitfield test when _ctypes is compiled with MingW. Structures containing bitfields may have different layout on MSVC and MingW . ........ r47237 | thomas.wouters | 2006-07-05 13:03:49 +0200 (Wed, 05 Jul 2006) | 15 lines Fix bug in passing tuples to string.Template. All other values (with working str() or repr()) would work, just not multi-value tuples. Probably not a backport candidate, since it changes the behaviour of passing a single-element tuple: >>> string.Template("$foo").substitute(dict(foo=(1,))) '(1,)' versus '1' ........ r47241 | georg.brandl | 2006-07-05 16:18:45 +0200 (Wed, 05 Jul 2006) | 2 lines Patch #1517490: fix glitches in filter() docs. ........ r47244 | georg.brandl | 2006-07-05 17:50:05 +0200 (Wed, 05 Jul 2006) | 2 lines no need to elaborate "string". ........ r47251 | neal.norwitz | 2006-07-06 06:28:59 +0200 (Thu, 06 Jul 2006) | 3 lines Fix refleaks reported by Shane Hathaway in SF patch #1515361. This change contains only the changes related to leaking the copy variable. ........ r47253 | fred.drake | 2006-07-06 07:13:22 +0200 (Thu, 06 Jul 2006) | 4 lines - back out Expat change; the final fix to Expat will be different - change the pyexpat wrapper to not be so sensitive to this detail of the Expat implementation (the ex-crasher test still passes) ........ r47257 | neal.norwitz | 2006-07-06 08:45:08 +0200 (Thu, 06 Jul 2006) | 1 line Add a NEWS entry for a recent pyexpat fix ........ r47258 | martin.v.loewis | 2006-07-06 08:55:58 +0200 (Thu, 06 Jul 2006) | 2 lines Add sqlite3.dll to the DLLs component, not to the TkDLLs component. Fixes #1517388. ........ r47259 | martin.v.loewis | 2006-07-06 09:05:21 +0200 (Thu, 06 Jul 2006) | 1 line Properly quote compileall and Lib paths in case TARGETDIR has a space. ........ r47260 | thomas.heller | 2006-07-06 09:50:18 +0200 (Thu, 06 Jul 2006) | 5 lines Revert the change done in svn revision 47206: Add a new function uses_seh() to the _ctypes extension module. This will return True if Windows Structured Exception handling (SEH) is used when calling functions, False otherwise. ........ r47261 | armin.rigo | 2006-07-06 09:58:18 +0200 (Thu, 06 Jul 2006) | 3 lines A couple of examples about how to attack the fact that _PyType_Lookup() returns a borrowed ref. Many of the calls are open to attack. ........ r47262 | thomas.heller | 2006-07-06 10:28:14 +0200 (Thu, 06 Jul 2006) | 2 lines The test that calls a function with invalid arguments and catches the resulting Windows access violation will not be run by default. ........ r47263 | thomas.heller | 2006-07-06 10:48:35 +0200 (Thu, 06 Jul 2006) | 5 lines Patch #1517790: It is now possible to use custom objects in the ctypes foreign function argtypes sequence as long as they provide a from_param method, no longer is it required that the object is a ctypes type. ........ r47264 | thomas.heller | 2006-07-06 10:58:40 +0200 (Thu, 06 Jul 2006) | 2 lines Document the Struture and Union constructors. ........ r47265 | thomas.heller | 2006-07-06 11:11:22 +0200 (Thu, 06 Jul 2006) | 2 lines Document the changes in svn revision 47263, from patch #1517790. ........ r47267 | ronald.oussoren | 2006-07-06 12:13:35 +0200 (Thu, 06 Jul 2006) | 7 lines This patch solves the problem Skip was seeing with zlib, this patch ensures that configure uses similar compiler flags as setup.py when doing the zlib test. Without this patch configure would use the first shared library on the linker path, with this patch it uses the first shared or static library on that path just like setup.py. ........ r47268 | thomas.wouters | 2006-07-06 12:48:28 +0200 (Thu, 06 Jul 2006) | 4 lines NEWS entry for r47267: fixing configure's zlib probing. ........ r47269 | fredrik.lundh | 2006-07-06 14:29:24 +0200 (Thu, 06 Jul 2006) | 3 lines added XMLParser alias for cElementTree compatibility ........ r47271 | nick.coghlan | 2006-07-06 14:53:04 +0200 (Thu, 06 Jul 2006) | 1 line Revert the __module_name__ changes made in rev 47142. We'll revisit this in Python 2.6 ........ r47272 | nick.coghlan | 2006-07-06 15:04:56 +0200 (Thu, 06 Jul 2006) | 1 line Update the tutorial section on relative imports ........ r47273 | nick.coghlan | 2006-07-06 15:35:27 +0200 (Thu, 06 Jul 2006) | 1 line Ignore ImportWarning by default ........ r47274 | nick.coghlan | 2006-07-06 15:41:34 +0200 (Thu, 06 Jul 2006) | 1 line Cover ImportWarning, PendingDeprecationWarning and simplefilter() in the warnings module docs ........ r47275 | nick.coghlan | 2006-07-06 15:47:18 +0200 (Thu, 06 Jul 2006) | 1 line Add NEWS entries for the ImportWarning change and documentation update ........ r47276 | andrew.kuchling | 2006-07-06 15:57:28 +0200 (Thu, 06 Jul 2006) | 1 line ImportWarning is now silent by default ........ r47277 | thomas.heller | 2006-07-06 17:06:05 +0200 (Thu, 06 Jul 2006) | 2 lines Document the correct return type of PyLong_AsUnsignedLongLongMask. ........ r47278 | hyeshik.chang | 2006-07-06 17:21:52 +0200 (Thu, 06 Jul 2006) | 2 lines Add a testcase for r47086 which fixed a bug in codec_getstreamcodec(). ........ r47279 | hyeshik.chang | 2006-07-06 17:39:24 +0200 (Thu, 06 Jul 2006) | 3 lines Test using all CJK encodings for the testcases which don't require specific encodings. ........ r47280 | martin.v.loewis | 2006-07-06 21:28:03 +0200 (Thu, 06 Jul 2006) | 2 lines Properly generate logical file ids. Fixes #1515998. Also correct typo in Control.mapping. ........ r47287 | neal.norwitz | 2006-07-07 08:03:15 +0200 (Fri, 07 Jul 2006) | 17 lines Restore rev 47014: The hppa ubuntu box sometimes hangs forever in these tests. My guess is that the wait is failing for some reason. Use WNOHANG, so we won't wait until the buildbot kills the test suite. I haven't been able to reproduce the failure, so I'm not sure if this will help or not. Hopefully, this change will cause the test to fail, rather than hang. That will be better since we will get the rest of the test results. It may also help us debug the real problem. *** The reason this originally failed was because there were many zombie children outstanding before rev 47158 cleaned them up. There are still hangs in test_subprocess that need to be addressed, but that will take more work. This should close some holes. ........ r47289 | georg.brandl | 2006-07-07 10:15:12 +0200 (Fri, 07 Jul 2006) | 3 lines Fix RFC number. ........ r50489 | neal.norwitz | 2006-07-08 07:31:37 +0200 (Sat, 08 Jul 2006) | 1 line Fix SF bug #1519018: 'as' is now validated properly in import statements ........ r50490 | georg.brandl | 2006-07-08 14:15:27 +0200 (Sat, 08 Jul 2006) | 3 lines Add an additional test for bug #1519018. ........ r50491 | tim.peters | 2006-07-08 21:55:05 +0200 (Sat, 08 Jul 2006) | 2 lines Whitespace normalization. ........ r50493 | neil.schemenauer | 2006-07-09 18:16:34 +0200 (Sun, 09 Jul 2006) | 2 lines Fix AST compiler bug #1501934: incorrect LOAD/STORE_GLOBAL generation. ........ r50495 | neil.schemenauer | 2006-07-09 23:19:29 +0200 (Sun, 09 Jul 2006) | 2 lines Fix SF bug 1441486: bad unary minus folding in compiler. ........ r50497 | neal.norwitz | 2006-07-10 00:14:42 +0200 (Mon, 10 Jul 2006) | 4 lines On 64 bit systems, int literals that use less than 64 bits are now ints rather than longs. This also fixes the test for eval(-sys.maxint - 1). ........ r50500 | neal.norwitz | 2006-07-10 02:04:44 +0200 (Mon, 10 Jul 2006) | 4 lines Bug #1512814, Fix incorrect lineno's when code at module scope started after line 256. ........ r50501 | neal.norwitz | 2006-07-10 02:05:34 +0200 (Mon, 10 Jul 2006) | 1 line Fix doco. Backport candidate. ........ r50503 | neal.norwitz | 2006-07-10 02:23:17 +0200 (Mon, 10 Jul 2006) | 5 lines Part of SF patch #1484695. This removes dead code. The chksum was already verified in .frombuf() on the lines above. If there was a problem an exception is raised, so there was no way this condition could have been true. ........ r50504 | neal.norwitz | 2006-07-10 03:18:57 +0200 (Mon, 10 Jul 2006) | 3 lines Patch #1516912: improve Modules support for OpenVMS. ........ r50506 | neal.norwitz | 2006-07-10 04:36:41 +0200 (Mon, 10 Jul 2006) | 7 lines Patch #1504046: Add documentation for xml.etree. /F wrote the text docs, Englebert Gruber massaged it to latex and I did some more massaging to try and improve the consistency and fix some name mismatches between the declaration and text. ........ r50509 | martin.v.loewis | 2006-07-10 09:23:48 +0200 (Mon, 10 Jul 2006) | 2 lines Introduce DISTUTILS_USE_SDK as a flag to determine whether the SDK environment should be used. Fixes #1508010. ........ r50510 | martin.v.loewis | 2006-07-10 09:26:41 +0200 (Mon, 10 Jul 2006) | 1 line Change error message to indicate that VS2003 is necessary to build extension modules, not the .NET SDK. ........ r50511 | martin.v.loewis | 2006-07-10 09:29:41 +0200 (Mon, 10 Jul 2006) | 1 line Add svn:ignore. ........ r50512 | anthony.baxter | 2006-07-10 09:41:04 +0200 (Mon, 10 Jul 2006) | 1 line preparing for 2.5b2 ........ r50513 | thomas.heller | 2006-07-10 11:10:28 +0200 (Mon, 10 Jul 2006) | 2 lines Fix bug #1518190: accept any integer or long value in the ctypes.c_void_p constructor. ........ r50514 | thomas.heller | 2006-07-10 11:31:06 +0200 (Mon, 10 Jul 2006) | 3 lines Fixed a segfault when ctypes.wintypes were imported on non-Windows machines. ........ r50516 | thomas.heller | 2006-07-10 13:11:10 +0200 (Mon, 10 Jul 2006) | 3 lines Assigning None to pointer type structure fields possible overwrote wrong fields. ........ r50517 | thomas.heller | 2006-07-10 13:17:37 +0200 (Mon, 10 Jul 2006) | 5 lines Moved the ctypes news entries from the 'Library' section into the 'Extension Modules' section where they belong, probably. This destroyes the original order of the news entries, don't know if that is important or not. ........ r50526 | phillip.eby | 2006-07-10 21:03:29 +0200 (Mon, 10 Jul 2006) | 2 lines Fix SF#1516184 and add a test to prevent regression. ........ r50528 | phillip.eby | 2006-07-10 21:18:35 +0200 (Mon, 10 Jul 2006) | 2 lines Fix SF#1457312: bad socket error handling in distutils "upload" command. ........ r50537 | peter.astrand | 2006-07-10 22:39:49 +0200 (Mon, 10 Jul 2006) | 1 line Make it possible to run test_subprocess.py with Python 2.2, which lacks test_support.reap_children(). ........ r50541 | tim.peters | 2006-07-10 23:08:24 +0200 (Mon, 10 Jul 2006) | 5 lines After approval from Anthony, merge the tim-current_frames branch into the trunk. This adds a new sys._current_frames() function, which returns a dict mapping thread id to topmost thread stack frame. ........ r50542 | tim.peters | 2006-07-10 23:11:49 +0200 (Mon, 10 Jul 2006) | 2 lines Whitespace normalization. ........ r50553 | martin.v.loewis | 2006-07-11 00:11:28 +0200 (Tue, 11 Jul 2006) | 4 lines Patch #1519566: Remove unused _tofill member. Make begin_fill idempotent. Update demo2 to demonstrate filling of concave shapes. ........ r50567 | anthony.baxter | 2006-07-11 04:04:09 +0200 (Tue, 11 Jul 2006) | 4 lines #1494314: Fix a regression with high-numbered sockets in 2.4.3. This means that select() on sockets > FD_SETSIZE (typically 1024) work again. The patch makes sockets use poll() internally where available. ........ r50568 | tim.peters | 2006-07-11 04:17:48 +0200 (Tue, 11 Jul 2006) | 2 lines Whitespace normalization. ........ r50575 | thomas.heller | 2006-07-11 18:42:05 +0200 (Tue, 11 Jul 2006) | 1 line Add missing Py_DECREF. ........ r50576 | thomas.heller | 2006-07-11 18:44:25 +0200 (Tue, 11 Jul 2006) | 1 line Add missing Py_DECREFs. ........ r50579 | andrew.kuchling | 2006-07-11 19:20:16 +0200 (Tue, 11 Jul 2006) | 1 line Bump version number; add sys._current_frames ........ r50582 | thomas.heller | 2006-07-11 20:28:35 +0200 (Tue, 11 Jul 2006) | 3 lines When a foreign function is retrived by calling __getitem__ on a ctypes library instance, do not set it as attribute. ........ r50583 | thomas.heller | 2006-07-11 20:40:50 +0200 (Tue, 11 Jul 2006) | 2 lines Change the ctypes version number to 1.0.0. ........ r50597 | neal.norwitz | 2006-07-12 07:26:17 +0200 (Wed, 12 Jul 2006) | 3 lines Bug #1520864: unpacking singleton tuples in for loop (for x, in) work again. ........ r50598 | neal.norwitz | 2006-07-12 07:26:35 +0200 (Wed, 12 Jul 2006) | 1 line Fix function name in error msg ........ r50599 | neal.norwitz | 2006-07-12 07:27:46 +0200 (Wed, 12 Jul 2006) | 4 lines Fix uninitialized memory read reported by Valgrind when running doctest. This could happen if size == 0. ........ r50600 | neal.norwitz | 2006-07-12 09:28:29 +0200 (Wed, 12 Jul 2006) | 1 line Actually change the MAGIC #. Create a new section for 2.5c1 and mention the impact of changing the MAGIC #. ........ r50601 | thomas.heller | 2006-07-12 10:43:47 +0200 (Wed, 12 Jul 2006) | 3 lines Fix #1467450: ctypes now uses RTLD_GLOBAL by default on OSX 10.3 to load shared libraries. ........ r50604 | thomas.heller | 2006-07-12 16:25:18 +0200 (Wed, 12 Jul 2006) | 3 lines Fix the wrong description of LibraryLoader.LoadLibrary, and document the DEFAULT_MODE constant. ........ r50607 | georg.brandl | 2006-07-12 17:31:17 +0200 (Wed, 12 Jul 2006) | 3 lines Accept long options "--help" and "--version". ........ r50617 | thomas.heller | 2006-07-13 11:53:47 +0200 (Thu, 13 Jul 2006) | 3 lines A misspelled preprocessor symbol caused ctypes to be always compiled without thread support. Replaced WITH_THREADS with WITH_THREAD. ........ r50619 | thomas.heller | 2006-07-13 19:01:14 +0200 (Thu, 13 Jul 2006) | 3 lines Fix #1521375. When running with root priviledges, 'gcc -o /dev/null' did overwrite /dev/null. Use a temporary file instead of /dev/null. ........ r50620 | thomas.heller | 2006-07-13 19:05:13 +0200 (Thu, 13 Jul 2006) | 2 lines Fix misleading words. ........ r50622 | andrew.kuchling | 2006-07-13 19:37:26 +0200 (Thu, 13 Jul 2006) | 1 line Typo fix ........ r50629 | georg.brandl | 2006-07-14 09:12:54 +0200 (Fri, 14 Jul 2006) | 3 lines Patch #1521874: grammar errors in doanddont.tex. ........ r50630 | neal.norwitz | 2006-07-14 09:20:04 +0200 (Fri, 14 Jul 2006) | 1 line Try to improve grammar further. ........ r50631 | martin.v.loewis | 2006-07-14 11:58:55 +0200 (Fri, 14 Jul 2006) | 1 line Extend build_ssl to Win64, using VSExtComp. ........ r50632 | martin.v.loewis | 2006-07-14 14:10:09 +0200 (Fri, 14 Jul 2006) | 1 line Add debug output to analyse buildbot failure. ........ r50633 | martin.v.loewis | 2006-07-14 14:31:05 +0200 (Fri, 14 Jul 2006) | 1 line Fix Debug build of _ssl. ........ r50636 | andrew.kuchling | 2006-07-14 15:32:38 +0200 (Fri, 14 Jul 2006) | 1 line Mention new options ........ r50638 | peter.astrand | 2006-07-14 16:04:45 +0200 (Fri, 14 Jul 2006) | 1 line Bug #1223937: CalledProcessError.errno -> CalledProcessError.returncode. ........ r50640 | thomas.heller | 2006-07-14 17:01:05 +0200 (Fri, 14 Jul 2006) | 4 lines Make the prototypes of our private PyUnicode_FromWideChar and PyUnicode_AsWideChar replacement functions compatible to the official functions by using Py_ssize_t instead of int. ........ r50643 | thomas.heller | 2006-07-14 19:51:14 +0200 (Fri, 14 Jul 2006) | 3 lines Patch #1521817: The index range checking on ctypes arrays containing exactly one element is enabled again. ........ r50647 | thomas.heller | 2006-07-14 20:22:50 +0200 (Fri, 14 Jul 2006) | 2 lines Updates for the ctypes documentation. ........ r50655 | fredrik.lundh | 2006-07-14 23:45:48 +0200 (Fri, 14 Jul 2006) | 3 lines typo ........ r50664 | george.yoshida | 2006-07-15 18:03:49 +0200 (Sat, 15 Jul 2006) | 2 lines Bug #15187702 : ext/win-cookbook.html has a broken link to distutils ........ r50667 | bob.ippolito | 2006-07-15 18:53:15 +0200 (Sat, 15 Jul 2006) | 1 line Patch #1220874: Update the binhex module for Mach-O. ........ r50671 | fred.drake | 2006-07-16 03:21:20 +0200 (Sun, 16 Jul 2006) | 1 line clean up some link markup ........ r50673 | neal.norwitz | 2006-07-16 03:50:38 +0200 (Sun, 16 Jul 2006) | 4 lines Bug #1512814, Fix incorrect lineno's when code within a function had more than 255 blank lines. Byte codes need to go first, line #s second. ........ r50674 | neal.norwitz | 2006-07-16 04:00:32 +0200 (Sun, 16 Jul 2006) | 5 lines a & b were dereffed above, so they are known to be valid pointers. z is known to be NULL, nothing to DECREF. Reported by Klockwork, #107. ........ r50675 | neal.norwitz | 2006-07-16 04:02:57 +0200 (Sun, 16 Jul 2006) | 5 lines self is dereffed (and passed as first arg), so it's known to be good. func is returned from PyArg_ParseTuple and also dereffed. Reported by Klocwork, #30 (self one at least). ........ r50676 | neal.norwitz | 2006-07-16 04:05:35 +0200 (Sun, 16 Jul 2006) | 4 lines proto was dereffed above and is known to be good. No need for X. Reported by Klocwork, #39. ........ r50677 | neal.norwitz | 2006-07-16 04:15:27 +0200 (Sun, 16 Jul 2006) | 5 lines Fix memory leaks in some conditions. Reported by Klocwork #152. ........ r50678 | neal.norwitz | 2006-07-16 04:17:36 +0200 (Sun, 16 Jul 2006) | 4 lines Fix memory leak under some conditions. Reported by Klocwork, #98. ........ r50679 | neal.norwitz | 2006-07-16 04:22:30 +0200 (Sun, 16 Jul 2006) | 8 lines Use sizeof(buffer) instead of duplicating the constants to ensure they won't be wrong. The real change is to pass (bufsz - 1) to PyOS_ascii_formatd and 1 to strncat. strncat copies n+1 bytes from src (not dest). Reported by Klocwork #58. ........ r50680 | neal.norwitz | 2006-07-16 04:32:03 +0200 (Sun, 16 Jul 2006) | 5 lines Handle a NULL name properly. Reported by Klocwork #67 ........ r50681 | neal.norwitz | 2006-07-16 04:35:47 +0200 (Sun, 16 Jul 2006) | 6 lines PyFunction_SetDefaults() is documented as taking None or a tuple. A NULL would crash the PyTuple_Check(). Now make NULL return a SystemError. Reported by Klocwork #73. ........ r50683 | neal.norwitz | 2006-07-17 02:55:45 +0200 (Mon, 17 Jul 2006) | 5 lines Stop INCREFing name, then checking if it's NULL. name (f_name) should never be NULL so assert it. Fix one place where we could have passed NULL. Reported by Klocwork #66. ........ r50684 | neal.norwitz | 2006-07-17 02:57:15 +0200 (Mon, 17 Jul 2006) | 5 lines otherset is known to be non-NULL based on checks before and DECREF after. DECREF otherset rather than XDECREF in error conditions too. Reported by Klockwork #154. ........ r50685 | neal.norwitz | 2006-07-17 02:59:04 +0200 (Mon, 17 Jul 2006) | 7 lines Reported by Klocwork #151. v2 can be NULL if exception2 is NULL. I don't think that condition can happen, but I'm not sure it can't either. Now the code will protect against either being NULL. ........ r50686 | neal.norwitz | 2006-07-17 03:00:16 +0200 (Mon, 17 Jul 2006) | 1 line Add NEWS entry for a bunch of fixes due to warnings produced by Klocworks static analysis tool. ........ r50687 | fred.drake | 2006-07-17 07:47:52 +0200 (Mon, 17 Jul 2006) | 3 lines document xmlcore (still minimal; needs mention in each of the xml.* modules) SF bug #1504456 (partial) ........ r50688 | georg.brandl | 2006-07-17 15:23:46 +0200 (Mon, 17 Jul 2006) | 3 lines Remove usage of sets module (patch #1500609). ........ r50689 | georg.brandl | 2006-07-17 15:26:33 +0200 (Mon, 17 Jul 2006) | 3 lines Add missing NEWS item (#1522771) ........ r50690 | andrew.kuchling | 2006-07-17 18:47:54 +0200 (Mon, 17 Jul 2006) | 1 line Attribute more features ........ r50692 | kurt.kaiser | 2006-07-17 23:59:27 +0200 (Mon, 17 Jul 2006) | 8 lines Patch 1479219 - Tal Einat 1. 'as' highlighted as builtin in comment string on import line 2. Comments such as "#False identity" which start with a keyword immediately after the '#' character aren't colored as comments. 3. u or U beginning unicode string not correctly highlighted Closes bug 1325071 ........ r50693 | barry.warsaw | 2006-07-18 01:07:51 +0200 (Tue, 18 Jul 2006) | 16 lines decode_rfc2231(): Be more robust against buggy RFC 2231 encodings. Specifically, instead of raising a ValueError when there is a single tick in the parameter, simply return that the entire string unquoted, with None for both the charset and the language. Also, if there are more than 2 ticks in the parameter, interpret the first three parts as the standard RFC 2231 parts, then the rest of the parts as the encoded string. Test cases added. Original fewer-than-3-parts fix by Tokio Kikuchi. Resolves SF bug # 1218081. I will back port the fix and tests to Python 2.4 (email 3.0) and Python 2.3 (email 2.5). Also, bump the version number to email 4.0.1, removing the 'alpha' moniker. ........ r50695 | kurt.kaiser | 2006-07-18 06:03:16 +0200 (Tue, 18 Jul 2006) | 2 lines Rebinding Tab key was inserting 'tab' instead of 'Tab'. Bug 1179168. ........ r50696 | brett.cannon | 2006-07-18 06:41:36 +0200 (Tue, 18 Jul 2006) | 6 lines Fix bug #1520914. Starting in 2.4, time.strftime() began to check the bounds of values in the time tuple passed in. Unfortunately people came to rely on undocumented behaviour of setting unneeded values to 0, regardless of if it was within the valid range. Now those values force the value internally to the minimum value when 0 is passed in. ........ r50697 | facundo.batista | 2006-07-18 14:16:13 +0200 (Tue, 18 Jul 2006) | 1 line Comments and docs cleanups, and some little fixes, provided by Santiágo Peresón ........ r50704 | martin.v.loewis | 2006-07-18 19:46:31 +0200 (Tue, 18 Jul 2006) | 2 lines Patch #1524429: Use repr instead of backticks again. ........ r50706 | tim.peters | 2006-07-18 23:55:15 +0200 (Tue, 18 Jul 2006) | 2 lines Whitespace normalization. ........ r50708 | tim.peters | 2006-07-19 02:03:19 +0200 (Wed, 19 Jul 2006) | 18 lines SF bug 1524317: configure --without-threads fails to build Moved the code for _PyThread_CurrentFrames() up, so it's no longer in a huge "#ifdef WITH_THREAD" block (I didn't realize it /was/ in one). Changed test_sys's test_current_frames() so it passes with or without thread supported compiled in. Note that test_sys fails when Python is compiled without threads, but for an unrelated reason (the old test_exit() fails with an indirect ImportError on the `thread` module). There are also other unrelated compilation failures without threads, in extension modules (like ctypes); at least the core compiles again. Do we really support --without-threads? If so, there are several problems remaining. ........ r50713 | thomas.heller | 2006-07-19 11:09:32 +0200 (Wed, 19 Jul 2006) | 4 lines Make sure the _ctypes extension can be compiled when WITH_THREAD is not defined on Windows, even if that configuration is probably not supported at all. ........ r50715 | martin.v.loewis | 2006-07-19 19:18:32 +0200 (Wed, 19 Jul 2006) | 4 lines Revert r50706 (Whitespace normalization) and r50697: Comments and docs cleanups, and some little fixes per recommendation from Raymond Hettinger. ........ r50719 | phillip.eby | 2006-07-20 17:54:16 +0200 (Thu, 20 Jul 2006) | 4 lines Fix SF#1516184 (again) and add a test to prevent regression. (There was a problem with empty filenames still causing recursion) ........ r50720 | georg.brandl | 2006-07-20 18:28:39 +0200 (Thu, 20 Jul 2006) | 3 lines Guard for _active being None in __del__ method. ........ r50721 | vinay.sajip | 2006-07-20 18:28:39 +0200 (Thu, 20 Jul 2006) | 1 line Updated documentation for TimedRotatingFileHandler relating to how rollover files are named. The previous documentation was wrongly the same as for RotatingFileHandler. ........ r50731 | fred.drake | 2006-07-20 22:11:57 +0200 (Thu, 20 Jul 2006) | 1 line markup fix ........ r50739 | kurt.kaiser | 2006-07-21 00:22:52 +0200 (Fri, 21 Jul 2006) | 7 lines Avoid occasional failure to detect closing paren properly. Patch 1407280 Tal Einat M ParenMatch.py M NEWS.txt M CREDITS.txt ........ r50740 | vinay.sajip | 2006-07-21 01:20:12 +0200 (Fri, 21 Jul 2006) | 1 line Addressed SF#1524081 by using a dictionary to map level names to syslog priority names, rather than a string.lower(). ........ r50741 | neal.norwitz | 2006-07-21 07:29:58 +0200 (Fri, 21 Jul 2006) | 1 line Add some asserts that we got good params passed ........ r50742 | neal.norwitz | 2006-07-21 07:31:02 +0200 (Fri, 21 Jul 2006) | 5 lines Move the initialization of some pointers earlier. The problem is that if we call Py_DECREF(frame) like we do if allocating locals fails, frame_dealloc() will try to use these bogus values and crash. ........ r50743 | neal.norwitz | 2006-07-21 07:32:28 +0200 (Fri, 21 Jul 2006) | 4 lines Handle allocation failures gracefully. Found with failmalloc. Many (all?) of these could be backported. ........ r50745 | neal.norwitz | 2006-07-21 09:59:02 +0200 (Fri, 21 Jul 2006) | 1 line Speel initialise write. Tanks Anthony. ........ r50746 | neal.norwitz | 2006-07-21 09:59:47 +0200 (Fri, 21 Jul 2006) | 2 lines Handle more memory allocation failures without crashing. ........ r50754 | barry.warsaw | 2006-07-21 16:51:07 +0200 (Fri, 21 Jul 2006) | 23 lines More RFC 2231 improvements for the email 4.0 package. As Mark Sapiro rightly points out there are really two types of continued headers defined in this RFC (i.e. "encoded" parameters with the form "name*0*=" and unencoded parameters with the form "name*0="), but we were were handling them both the same way and that isn't correct. This patch should be much more RFC compliant in that only encoded params are %-decoded and the charset/language information is only extract if there are any encoded params in the segments. If there are no encoded params then the RFC says that there will be no charset/language parts. Note however that this will change the return value for Message.get_param() in some cases. For example, whereas before if you had all unencoded param continuations you would have still gotten a 3-tuple back from this method (with charset and language == None), you will now get just a string. I don't believe this is a backward incompatible change though because the documentation for this method already indicates that either return value is possible and that you must do an isinstance(val, tuple) check to discriminate between the two. (Yeah that API kind of sucks but we can't change /that/ without breaking code.) Test cases, some documentation updates, and a NEWS item accompany this patch. ........ r50759 | georg.brandl | 2006-07-21 19:36:31 +0200 (Fri, 21 Jul 2006) | 3 lines Fix check for empty list (vs. None). ........ r50771 | brett.cannon | 2006-07-22 00:44:07 +0200 (Sat, 22 Jul 2006) | 2 lines Remove an XXX marker in a comment. ........ r50773 | neal.norwitz | 2006-07-22 18:20:49 +0200 (Sat, 22 Jul 2006) | 1 line Fix more memory allocation issues found with failmalloc. ........ r50774 | neal.norwitz | 2006-07-22 19:00:57 +0200 (Sat, 22 Jul 2006) | 1 line Don't fail if the directory already exists ........ r50775 | greg.ward | 2006-07-23 04:25:53 +0200 (Sun, 23 Jul 2006) | 6 lines Be a lot smarter about whether this test passes: instead of assuming that a 2.93 sec audio file will always take 3.1 sec (as it did on the hardware I had when I first wrote the test), expect that it will take 2.93 sec +/- 10%, and only fail if it's outside of that range. Compute the expected ........ r50776 | kurt.kaiser | 2006-07-23 06:19:49 +0200 (Sun, 23 Jul 2006) | 2 lines Tooltips failed on new-syle class __init__ args. Bug 1027566 Loren Guthrie ........ r50777 | neal.norwitz | 2006-07-23 09:50:36 +0200 (Sun, 23 Jul 2006) | 1 line Handle more mem alloc issues found with failmalloc ........ r50778 | neal.norwitz | 2006-07-23 09:51:58 +0200 (Sun, 23 Jul 2006) | 5 lines If the for loop isn't entered, entryblock will be NULL. If passed to stackdepth_walk it will be dereffed. Not sure if I found with failmalloc or Klockwork #55. ........ r50779 | neal.norwitz | 2006-07-23 09:53:14 +0200 (Sun, 23 Jul 2006) | 4 lines Move the initialization of size_a down below the check for a being NULL. Reported by Klocwork #106 ........ r50780 | neal.norwitz | 2006-07-23 09:55:55 +0200 (Sun, 23 Jul 2006) | 9 lines Check the allocation of b_objects and return if there was a failure. Also fix a few memory leaks in other failure scenarios. It seems that if b_objects == Py_None, we will have an extra ref to b_objects. Add XXX comment so hopefully someone documents why the else isn't necessary or adds it in. Reported by Klocwork #20 ........ r50781 | neal.norwitz | 2006-07-23 09:57:11 +0200 (Sun, 23 Jul 2006) | 2 lines Fix memory leaks spotted by Klocwork #37. ........ r50782 | neal.norwitz | 2006-07-23 09:59:00 +0200 (Sun, 23 Jul 2006) | 5 lines nextlink can be NULL if teedataobject_new fails, so use XINCREF. Ensure that dataobj is never NULL. Reported by Klocwork #102 ........ r50783 | neal.norwitz | 2006-07-23 10:01:43 +0200 (Sun, 23 Jul 2006) | 8 lines Ensure we don't write beyond errText. I think I got this right, but it definitely could use some review to ensure I'm not off by one and there's no possible overflow/wrap-around of bytes_left. Reported by Klocwork #1. Fix a problem if there is a failure allocating self->db. Found with failmalloc. ........ r50784 | ronald.oussoren | 2006-07-23 11:41:09 +0200 (Sun, 23 Jul 2006) | 3 lines Without this patch CMD-W won't close EditorWindows on MacOS X. This solves part of bug #1517990. ........ r50785 | ronald.oussoren | 2006-07-23 11:46:11 +0200 (Sun, 23 Jul 2006) | 5 lines Fix for bug #1517996: Class and Path browsers show Tk menu This patch replaces the menubar that is used by AquaTk for windows without a menubar of their own by one that is more appropriate for IDLE. ........ r50786 | andrew.macintyre | 2006-07-23 14:57:02 +0200 (Sun, 23 Jul 2006) | 2 lines Build updates for OS/2 EMX port ........ r50787 | andrew.macintyre | 2006-07-23 15:00:04 +0200 (Sun, 23 Jul 2006) | 3 lines bugfix: PyThread_start_new_thread() returns the thread ID, not a flag; will backport. ........ r50789 | andrew.macintyre | 2006-07-23 15:04:00 +0200 (Sun, 23 Jul 2006) | 2 lines Get mailbox module working on OS/2 EMX port. ........ r50791 | greg.ward | 2006-07-23 18:05:51 +0200 (Sun, 23 Jul 2006) | 1 line Resync optparse with Optik 1.5.3: minor tweaks for/to tests. ........ r50794 | martin.v.loewis | 2006-07-24 07:05:22 +0200 (Mon, 24 Jul 2006) | 2 lines Update list of unsupported systems. Fixes #1510853. ........ r50795 | martin.v.loewis | 2006-07-24 12:26:33 +0200 (Mon, 24 Jul 2006) | 1 line Patch #1448199: Release GIL around ConnectRegistry. ........ r50796 | martin.v.loewis | 2006-07-24 13:54:53 +0200 (Mon, 24 Jul 2006) | 3 lines Patch #1232023: Don't include empty path component from registry, so that the current directory does not get added to sys.path. Also fixes #1526785. ........ r50797 | martin.v.loewis | 2006-07-24 14:54:17 +0200 (Mon, 24 Jul 2006) | 3 lines Bug #1524310: Properly report errors from FindNextFile in os.listdir. Will backport to 2.4. ........ r50800 | georg.brandl | 2006-07-24 15:28:57 +0200 (Mon, 24 Jul 2006) | 7 lines Patch #1523356: fix determining include dirs in python-config. Also don't install "python-config" when doing altinstall, but always install "python-config2.x" and make a link to it like with the main executable. ........ r50802 | georg.brandl | 2006-07-24 15:46:47 +0200 (Mon, 24 Jul 2006) | 3 lines Patch #1527744: right order of includes in order to have HAVE_CONIO_H defined properly. ........ r50803 | georg.brandl | 2006-07-24 16:09:56 +0200 (Mon, 24 Jul 2006) | 3 lines Patch #1515343: Fix printing of deprecated string exceptions with a value in the traceback module. ........ r50804 | kurt.kaiser | 2006-07-24 19:13:23 +0200 (Mon, 24 Jul 2006) | 7 lines EditorWindow failed when used stand-alone if sys.ps1 not set. Bug 1010370 Dave Florek M EditorWindow.py M PyShell.py M NEWS.txt ........ r50805 | kurt.kaiser | 2006-07-24 20:05:51 +0200 (Mon, 24 Jul 2006) | 6 lines - EditorWindow.test() was failing. Bug 1417598 M EditorWindow.py M ScriptBinding.py M NEWS.txt ........ r50808 | georg.brandl | 2006-07-24 22:11:35 +0200 (Mon, 24 Jul 2006) | 3 lines Repair accidental NameError. ........ r50809 | tim.peters | 2006-07-24 23:02:15 +0200 (Mon, 24 Jul 2006) | 2 lines Whitespace normalization. ........ r50810 | greg.ward | 2006-07-25 04:11:12 +0200 (Tue, 25 Jul 2006) | 3 lines Don't use standard assert: want tests to fail even when run with -O. Delete cruft. ........ r50811 | tim.peters | 2006-07-25 06:07:22 +0200 (Tue, 25 Jul 2006) | 10 lines current_frames_with_threads(): There's actually no way to guess /which/ line the spawned thread is in at the time sys._current_frames() is called: we know it finished enter_g.set(), but can't know whether the instruction counter has advanced to the following leave_g.wait(). The latter is overwhelming most likely, but not guaranteed, and I see that the "x86 Ubuntu dapper (icc) trunk" buildbot found it on the other line once. Changed the test so it passes in either case. ........ r50815 | martin.v.loewis | 2006-07-25 11:53:12 +0200 (Tue, 25 Jul 2006) | 2 lines Bug #1525817: Don't truncate short lines in IDLE's tool tips. ........ r50816 | martin.v.loewis | 2006-07-25 12:05:47 +0200 (Tue, 25 Jul 2006) | 3 lines Bug #978833: Really close underlying socket in _socketobject.close. Will backport to 2.4. ........ r50817 | martin.v.loewis | 2006-07-25 12:11:14 +0200 (Tue, 25 Jul 2006) | 1 line Revert incomplete checkin. ........ r50819 | georg.brandl | 2006-07-25 12:22:34 +0200 (Tue, 25 Jul 2006) | 4 lines Patch #1525766: correctly pass onerror arg to recursive calls of pkg.walk_packages. Also improve the docstrings. ........ r50825 | brett.cannon | 2006-07-25 19:32:20 +0200 (Tue, 25 Jul 2006) | 2 lines Add comment for changes to test_ossaudiodev. ........ r50826 | brett.cannon | 2006-07-25 19:34:36 +0200 (Tue, 25 Jul 2006) | 3 lines Fix a bug in the messages for an assert failure where not enough arguments to a string were being converted in the format. ........ r50828 | armin.rigo | 2006-07-25 20:09:57 +0200 (Tue, 25 Jul 2006) | 2 lines Document why is and is not a good way to fix the gc_inspection crasher. ........ r50829 | armin.rigo | 2006-07-25 20:11:07 +0200 (Tue, 25 Jul 2006) | 5 lines Added another crasher, which hit me today (I was not intentionally writing such code, of course, but it took some gdb time to figure out what my bug was). ........ r50830 | armin.rigo | 2006-07-25 20:38:39 +0200 (Tue, 25 Jul 2006) | 3 lines Document the crashers that will not go away soon as "won't fix", and explain why. ........ r50831 | ronald.oussoren | 2006-07-25 21:13:35 +0200 (Tue, 25 Jul 2006) | 3 lines Install the compatibility symlink to libpython.a on OSX using 'ln -sf' instead of 'ln -s', this avoid problems when reinstalling python. ........ r50832 | ronald.oussoren | 2006-07-25 21:20:54 +0200 (Tue, 25 Jul 2006) | 7 lines Fix for bug #1525447 (renaming to MacOSmodule.c would also work, but not without causing problems for anyone that is on a case-insensitive filesystem). Setup.py tries to compile the MacOS extension from MacOSmodule.c, while the actual file is named macosmodule.c. This is no problem on the (default) case-insensitive filesystem, but doesn't work on case-sensitive filesystems. ........ r50833 | ronald.oussoren | 2006-07-25 22:28:55 +0200 (Tue, 25 Jul 2006) | 7 lines Fix bug #1517990: IDLE keybindings on OSX This adds a new key definition for OSX, which is slightly different from the classic mac definition. Also add NEWS item for a couple of bugfixes I added recently. ........ r50834 | tim.peters | 2006-07-26 00:30:24 +0200 (Wed, 26 Jul 2006) | 2 lines Whitespace normalization. ........ r50839 | neal.norwitz | 2006-07-26 06:00:18 +0200 (Wed, 26 Jul 2006) | 1 line Hmm, only python2.x is installed, not plain python. Did that change recently? ........ r50840 | barry.warsaw | 2006-07-26 07:54:46 +0200 (Wed, 26 Jul 2006) | 6 lines Forward port some fixes that were in email 2.5 but for some reason didn't make it into email 4.0. Specifically, in Message.get_content_charset(), handle RFC 2231 headers that contain an encoding not known to Python, or a character in the data that isn't in the charset encoding. Also forward port the appropriate unit tests. ........ r50841 | georg.brandl | 2006-07-26 09:23:32 +0200 (Wed, 26 Jul 2006) | 3 lines NEWS entry for #1525766. ........ r50842 | georg.brandl | 2006-07-26 09:40:17 +0200 (Wed, 26 Jul 2006) | 3 lines Bug #1459963: properly capitalize HTTP header names. ........ r50843 | georg.brandl | 2006-07-26 10:03:10 +0200 (Wed, 26 Jul 2006) | 6 lines Part of bug #1523610: fix miscalculation of buffer length. Also add a guard against NULL in converttuple and add a test case (that previously would have crashed). ........ r50844 | martin.v.loewis | 2006-07-26 14:12:56 +0200 (Wed, 26 Jul 2006) | 3 lines Bug #978833: Really close underlying socket in _socketobject.close. Fix httplib.HTTPConnection.getresponse to not close the socket if it is still needed for the response. ........ r50845 | andrew.kuchling | 2006-07-26 19:16:52 +0200 (Wed, 26 Jul 2006) | 1 line [Bug #1471938] Fix build problem on Solaris 8 by conditionalizing the use of mvwgetnstr(); it was conditionalized a few lines below. Fix from Paul Eggert. I also tried out the STRICT_SYSV_CURSES case and am therefore removing the 'untested' comment. ........ r50846 | andrew.kuchling | 2006-07-26 19:18:01 +0200 (Wed, 26 Jul 2006) | 1 line Correct error message ........ r50847 | andrew.kuchling | 2006-07-26 19:19:39 +0200 (Wed, 26 Jul 2006) | 1 line Minor grammar fix ........ r50848 | andrew.kuchling | 2006-07-26 19:22:21 +0200 (Wed, 26 Jul 2006) | 1 line Put news item in right section ........ r50850 | andrew.kuchling | 2006-07-26 20:03:12 +0200 (Wed, 26 Jul 2006) | 1 line Use sys.exc_info() ........ r50851 | andrew.kuchling | 2006-07-26 20:15:45 +0200 (Wed, 26 Jul 2006) | 1 line Use sys.exc_info() ........ r50852 | phillip.eby | 2006-07-26 21:48:27 +0200 (Wed, 26 Jul 2006) | 4 lines Allow the 'onerror' argument to walk_packages() to catch any Exception, not just ImportError. This allows documentation tools to better skip unimportable packages. ........ r50854 | tim.peters | 2006-07-27 01:23:15 +0200 (Thu, 27 Jul 2006) | 2 lines Whitespace normalization. ........ r50855 | tim.peters | 2006-07-27 03:14:53 +0200 (Thu, 27 Jul 2006) | 21 lines Bug #1521947: possible bug in mystrtol.c with recent gcc. In general, C doesn't define anything about what happens when an operation on a signed integral type overflows, and PyOS_strtol() did several formally undefined things of that nature on signed longs. Some version of gcc apparently tries to exploit that now, and PyOS_strtol() could fail to detect overflow then. Tried to repair all that, although it seems at least as likely to me that we'll get screwed by bad platform definitions for LONG_MIN and/or LONG_MAX now. For that reason, I don't recommend backporting this. Note that I have no box on which this makes a lick of difference -- can't really test it, except to note that it didn't break anything on my boxes. Silent change: PyOS_strtol() used to return the hard-coded 0x7fffffff in case of overflow. Now it returns LONG_MAX. They're the same only on 32-bit boxes (although C doesn't guarantee that either ...). ........ r50856 | neal.norwitz | 2006-07-27 05:51:58 +0200 (Thu, 27 Jul 2006) | 6 lines Don't kill a normal instance of python running on windows when checking to kill a cygwin instance. build\\python.exe was matching a normal windows instance. Prefix that with a \\ to ensure build is a directory and not PCbuild. As discussed on python-dev. ........ r50857 | neal.norwitz | 2006-07-27 05:55:39 +0200 (Thu, 27 Jul 2006) | 5 lines Closure can't be NULL at this point since we know it's a tuple. Reported by Klocwork # 74. ........ r50858 | neal.norwitz | 2006-07-27 06:04:50 +0200 (Thu, 27 Jul 2006) | 1 line No functional change. Add comment and assert to describe why there cannot be overflow which was reported by Klocwork. Discussed on python-dev ........ r50859 | martin.v.loewis | 2006-07-27 08:38:16 +0200 (Thu, 27 Jul 2006) | 3 lines Bump distutils version to 2.5, as several new features have been introduced since 2.4. ........ r50860 | andrew.kuchling | 2006-07-27 14:18:20 +0200 (Thu, 27 Jul 2006) | 1 line Reformat docstring; fix typo ........ r50861 | georg.brandl | 2006-07-27 17:05:36 +0200 (Thu, 27 Jul 2006) | 6 lines Add test_main() methods. These three tests were never run by regrtest.py. We really need a simpler testing framework. ........ r50862 | tim.peters | 2006-07-27 17:09:20 +0200 (Thu, 27 Jul 2006) | 2 lines News for patch #1529686. ........ r50863 | tim.peters | 2006-07-27 17:11:00 +0200 (Thu, 27 Jul 2006) | 2 lines Whitespace normalization. ........ r50864 | georg.brandl | 2006-07-27 17:38:33 +0200 (Thu, 27 Jul 2006) | 3 lines Amend news entry. ........ r50865 | georg.brandl | 2006-07-27 18:08:15 +0200 (Thu, 27 Jul 2006) | 3 lines Make uuid test suite pass on this box by requesting output with LC_ALL=C. ........ r50866 | andrew.kuchling | 2006-07-27 20:37:33 +0200 (Thu, 27 Jul 2006) | 1 line Add example ........ r50867 | thomas.heller | 2006-07-27 20:39:55 +0200 (Thu, 27 Jul 2006) | 9 lines Remove code that is no longer used (ctypes.com). Fix the DllGetClassObject and DllCanUnloadNow so that they forward the call to the comtypes.server.inprocserver module. The latter was never documented, never used by published code, and didn't work anyway, so I think it does not deserve a NEWS entry (but I might be wrong). ........ r50868 | andrew.kuchling | 2006-07-27 20:41:21 +0200 (Thu, 27 Jul 2006) | 1 line Typo fix ('publically' is rare, poss. non-standard) ........ r50869 | andrew.kuchling | 2006-07-27 20:42:41 +0200 (Thu, 27 Jul 2006) | 1 line Add missing word ........ r50870 | andrew.kuchling | 2006-07-27 20:44:10 +0200 (Thu, 27 Jul 2006) | 1 line Repair typos ........ r50872 | andrew.kuchling | 2006-07-27 20:53:33 +0200 (Thu, 27 Jul 2006) | 1 line Update URL; add example ........ r50873 | andrew.kuchling | 2006-07-27 21:07:29 +0200 (Thu, 27 Jul 2006) | 1 line Add punctuation mark; add some examples ........ r50874 | andrew.kuchling | 2006-07-27 21:11:07 +0200 (Thu, 27 Jul 2006) | 1 line Mention base64 module; rewrite last sentence to be more positive ........ r50875 | andrew.kuchling | 2006-07-27 21:12:49 +0200 (Thu, 27 Jul 2006) | 1 line If binhex is higher-level than binascii, it should come first in the chapter ........ r50876 | tim.peters | 2006-07-27 22:47:24 +0200 (Thu, 27 Jul 2006) | 28 lines check_node(): stop spraying mystery output to stderr. When a node number disagrees, keep track of all sources & the node numbers they reported, and stick all that in the error message. Changed all callers to supply a non-empty "source" argument; made the "source" argument non-optional. On my box, test_uuid still fails, but with the less confusing output: AssertionError: different sources disagree on node: from source 'getnode1', node was 00038a000015 from source 'getnode2', node was 00038a000015 from source 'ipconfig', node was 001111b2b7bf Only the last one appears to be correct; e.g., C:\Code\python\PCbuild>getmac Physical Address Transport Name =================== ========================================================== 00-11-11-B2-B7-BF \Device\Tcpip_{190FB163-5AFD-4483-86A1-2FE16AC61FF1} 62-A1-AC-6C-FD-BE \Device\Tcpip_{8F77DF5A-EA3D-4F1D-975E-D472CEE6438A} E2-1F-01-C6-5D-88 \Device\Tcpip_{CD18F76B-2EF3-409F-9B8A-6481EE70A1E4} I can't find anything on my box with MAC 00-03-8a-00-00-15, and am not clear on where that comes from. ........ r50878 | andrew.kuchling | 2006-07-28 00:40:05 +0200 (Fri, 28 Jul 2006) | 1 line Reword paragraph ........ r50879 | andrew.kuchling | 2006-07-28 00:49:38 +0200 (Fri, 28 Jul 2006) | 1 line Add example ........ r50880 | andrew.kuchling | 2006-07-28 00:49:54 +0200 (Fri, 28 Jul 2006) | 1 line Add example ........ r50881 | barry.warsaw | 2006-07-28 01:43:15 +0200 (Fri, 28 Jul 2006) | 27 lines Patch #1520294: Support for getset and member descriptors in types.py, inspect.py, and pydoc.py. Specifically, this allows for querying the type of an object against these built-in C types and more importantly, for getting their docstrings printed in the interactive interpreter's help() function. This patch includes a new built-in module called _types which provides definitions of getset and member descriptors for use by the types.py module. These types are exposed as types.GetSetDescriptorType and types.MemberDescriptorType. Query functions are provided as inspect.isgetsetdescriptor() and inspect.ismemberdescriptor(). The implementations of these are robust enough to work with Python implementations other than CPython, which may not have these fundamental types. The patch also includes documentation and test suite updates. I commit these changes now under these guiding principles: 1. Silence is assent. The release manager has not said "no", and of the few people that cared enough to respond to the thread, the worst vote was "0". 2. It's easier to ask for forgiveness than permission. 3. It's so dang easy to revert stuff in svn, that you could view this as a forcing function. :) Windows build patches will follow. ........ r50882 | tim.peters | 2006-07-28 01:44:37 +0200 (Fri, 28 Jul 2006) | 4 lines Bug #1529297: The rewrite of doctest for Python 2.4 unintentionally lost that tests are sorted by name before being run. ``DocTestFinder`` has been changed to sort the list of tests it returns. ........ r50883 | tim.peters | 2006-07-28 01:45:48 +0200 (Fri, 28 Jul 2006) | 2 lines Whitespace normalization. ........ r50884 | tim.peters | 2006-07-28 01:46:36 +0200 (Fri, 28 Jul 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r50885 | barry.warsaw | 2006-07-28 01:50:40 +0200 (Fri, 28 Jul 2006) | 4 lines Enable the building of the _types module on Windows. Note that this has only been tested for VS 2003 since that's all I have. ........ r50887 | tim.peters | 2006-07-28 02:23:15 +0200 (Fri, 28 Jul 2006) | 7 lines defdict_reduce(): Plug leaks. We didn't notice these before because test_defaultdict didn't actually do anything before Georg fixed that earlier today. Neal's next refleak run then showed test_defaultdict leaking 9 references on each run. That's repaired by this checkin. ........ r50888 | tim.peters | 2006-07-28 02:30:00 +0200 (Fri, 28 Jul 2006) | 2 lines News about the repaired memory leak in defaultdict. ........ r50889 | gregory.p.smith | 2006-07-28 03:35:25 +0200 (Fri, 28 Jul 2006) | 7 lines - pybsddb Bug #1527939: bsddb module DBEnv dbremove and dbrename methods now allow their database parameter to be None as the sleepycat API allows. Also adds an appropriate test case for DBEnv.dbrename and dbremove. ........ r50895 | neal.norwitz | 2006-07-28 06:22:34 +0200 (Fri, 28 Jul 2006) | 1 line Ensure the actual number matches the expected count ........ r50896 | tim.peters | 2006-07-28 06:51:59 +0200 (Fri, 28 Jul 2006) | 6 lines Live with that "the hardware address" is an ill-defined concept, and that different ways of trying to find "the hardware address" may return different results. Certainly true on both of my Windows boxes, and in different ways (see whining on python-dev). ........ r50897 | neal.norwitz | 2006-07-28 09:21:27 +0200 (Fri, 28 Jul 2006) | 3 lines Try to find the MAC addr on various flavours of Unix. This seems hopeless. The reduces the test_uuid failures, but there's still another method failing. ........ r50898 | martin.v.loewis | 2006-07-28 09:45:49 +0200 (Fri, 28 Jul 2006) | 2 lines Add UUID for upcoming 2.5b3. ........ r50899 | matt.fleming | 2006-07-28 13:27:27 +0200 (Fri, 28 Jul 2006) | 3 lines Allow socketmodule to compile on NetBSD -current, whose bluetooth API differs from both Linux and FreeBSD. Accepted by Neal Norwitz. ........ r50900 | andrew.kuchling | 2006-07-28 14:07:12 +0200 (Fri, 28 Jul 2006) | 1 line [Patch #1529811] Correction to description of r|* mode ........ r50901 | andrew.kuchling | 2006-07-28 14:18:22 +0200 (Fri, 28 Jul 2006) | 1 line Typo fix ........ r50902 | andrew.kuchling | 2006-07-28 14:32:43 +0200 (Fri, 28 Jul 2006) | 1 line Add example ........ r50903 | andrew.kuchling | 2006-07-28 14:33:19 +0200 (Fri, 28 Jul 2006) | 1 line Add example ........ r50904 | andrew.kuchling | 2006-07-28 14:45:55 +0200 (Fri, 28 Jul 2006) | 1 line Don't overwrite built-in name; add some blank lines for readability ........ r50905 | andrew.kuchling | 2006-07-28 14:48:07 +0200 (Fri, 28 Jul 2006) | 1 line Add example. Should I propagate this example to all the other DBM-ish modules, too? ........ r50912 | georg.brandl | 2006-07-28 20:31:39 +0200 (Fri, 28 Jul 2006) | 3 lines Patch #1529686: also run test_email_codecs with regrtest.py. ........ r50913 | georg.brandl | 2006-07-28 20:36:01 +0200 (Fri, 28 Jul 2006) | 3 lines Fix spelling. ........ r50915 | thomas.heller | 2006-07-28 21:42:40 +0200 (Fri, 28 Jul 2006) | 3 lines Remove a useless XXX comment. Cosmetic changes to the code so that the #ifdef _UNICODE block doesn't mess emacs code formatting. ........ r50916 | phillip.eby | 2006-07-28 23:12:07 +0200 (Fri, 28 Jul 2006) | 5 lines Bug #1529871: The speed enhancement patch #921466 broke Python's compliance with PEP 302. This was fixed by adding an ``imp.NullImporter`` type that is used in ``sys.path_importer_cache`` to cache non-directory paths and avoid excessive filesystem operations during imports. ........ r50917 | phillip.eby | 2006-07-28 23:31:54 +0200 (Fri, 28 Jul 2006) | 2 lines Fix svn merge spew. ........ r50918 | thomas.heller | 2006-07-28 23:43:20 +0200 (Fri, 28 Jul 2006) | 4 lines Patch #1529514: More openbsd platforms for ctypes. Regenerated Modules/_ctypes/libffi/configure with autoconf 2.59. Approved by Neal. ........ r50922 | georg.brandl | 2006-07-29 10:51:21 +0200 (Sat, 29 Jul 2006) | 2 lines Bug #835255: The "closure" argument to new.function() is now documented. ........ r50924 | georg.brandl | 2006-07-29 11:33:26 +0200 (Sat, 29 Jul 2006) | 3 lines Bug #1441397: The compiler module now recognizes module and function docstrings correctly as it did in Python 2.4. ........ r50925 | georg.brandl | 2006-07-29 12:25:46 +0200 (Sat, 29 Jul 2006) | 4 lines Revert rev 42617, it was introduced to work around bug #1441397. test_compiler now passes again. ........ r50926 | fred.drake | 2006-07-29 15:22:49 +0200 (Sat, 29 Jul 2006) | 1 line update target version number ........ r50927 | andrew.kuchling | 2006-07-29 15:56:48 +0200 (Sat, 29 Jul 2006) | 1 line Add example ........ r50928 | andrew.kuchling | 2006-07-29 16:04:47 +0200 (Sat, 29 Jul 2006) | 1 line Update URL ........ r50930 | andrew.kuchling | 2006-07-29 16:08:15 +0200 (Sat, 29 Jul 2006) | 1 line Reword paragraph to match the order of the subsequent sections ........ r50931 | andrew.kuchling | 2006-07-29 16:21:15 +0200 (Sat, 29 Jul 2006) | 1 line [Bug #1529157] Mention raw_input() and input(); while I'm at it, reword the description a bit ........ r50932 | andrew.kuchling | 2006-07-29 16:42:48 +0200 (Sat, 29 Jul 2006) | 1 line [Bug #1519571] Document some missing functions: setup(), title(), done() ........ r50933 | andrew.kuchling | 2006-07-29 16:43:55 +0200 (Sat, 29 Jul 2006) | 1 line Fix docstring punctuation ........ r50934 | andrew.kuchling | 2006-07-29 17:10:32 +0200 (Sat, 29 Jul 2006) | 1 line [Bug #1414697] Change docstring of set/frozenset types to specify that the contents are unique. Raymond, please feel free to edit or revert. ........ r50935 | andrew.kuchling | 2006-07-29 17:35:21 +0200 (Sat, 29 Jul 2006) | 1 line [Bug #1530382] Document SSL.server(), .issuer() methods ........ r50936 | andrew.kuchling | 2006-07-29 17:42:46 +0200 (Sat, 29 Jul 2006) | 1 line Typo fix ........ r50937 | andrew.kuchling | 2006-07-29 17:43:13 +0200 (Sat, 29 Jul 2006) | 1 line Tweak wording ........ r50938 | matt.fleming | 2006-07-29 17:55:30 +0200 (Sat, 29 Jul 2006) | 2 lines Fix typo ........ r50939 | andrew.kuchling | 2006-07-29 17:57:08 +0200 (Sat, 29 Jul 2006) | 6 lines [Bug #1528258] Mention that the 'data' argument can be None. The constructor docs referred the reader to the add_data() method's docs, but they weren't very helpful. I've simply copied an earlier explanation of 'data' that's more useful. ........ r50940 | andrew.kuchling | 2006-07-29 18:08:40 +0200 (Sat, 29 Jul 2006) | 1 line Set bug/patch count. Take a bow, everyone! ........ r50941 | fred.drake | 2006-07-29 18:56:15 +0200 (Sat, 29 Jul 2006) | 18 lines expunge the xmlcore changes: 41667, 41668 - initial switch to xmlcore 47044 - mention of xmlcore in What's New 50687 - mention of xmlcore in the library reference re-apply xmlcore changes to xml: 41674 - line ending changes (re-applied manually), directory props 41677 - add cElementTree wrapper 41678 - PSF licensing for etree 41812 - whitespace normalization 42724 - fix svn:eol-style settings 43681, 43682 - remove Python version-compatibility cruft from minidom 46773 - fix encoding of \r\n\t in attr values in saxutils 47269 - added XMLParser alias for cElementTree compatibility additional tests were added in Lib/test/test_sax.py that failed with the xmlcore changes; these relate to SF bugs #1511497, #1513611 ........ r50942 | andrew.kuchling | 2006-07-29 20:14:07 +0200 (Sat, 29 Jul 2006) | 17 lines Reorganize the docs for 'file' and 'open()' after some discussion with Fred. We want to encourage users to write open() when opening a file, but open() was described with a single paragraph and 'file' had lots of explanation of the mode and bufsize arguments. I've shrunk the description of 'file' to cross-reference to the 'File objects' section, and to open() for an explanation of the arguments. open() now has all the paragraphs about the mode string. The bufsize argument was moved up so that it isn't buried at the end; now there's 1 paragraph on mode, 1 on bufsize, and then 3 more on mode. Various other edits and rearrangements were made in the process. It's probably best to read the final text and not to try to make sense of the diffs. ........ r50943 | fred.drake | 2006-07-29 20:19:19 +0200 (Sat, 29 Jul 2006) | 1 line restore test un-intentionally removed in the xmlcore purge (revision 50941) ........ r50944 | fred.drake | 2006-07-29 20:33:29 +0200 (Sat, 29 Jul 2006) | 3 lines make the reference to older versions of the documentation a link to the right page on python.org ........ r50945 | fred.drake | 2006-07-29 21:09:01 +0200 (Sat, 29 Jul 2006) | 1 line document the footnote usage pattern ........ r50947 | fred.drake | 2006-07-29 21:14:10 +0200 (Sat, 29 Jul 2006) | 1 line emphasize and oddball nuance of LaTeX comment syntax ........ r50948 | andrew.kuchling | 2006-07-29 21:24:04 +0200 (Sat, 29 Jul 2006) | 1 line [Patch #1490989 from Skip Montanaro] Mention debugging builds in the API documentation. I've changed Skip's patch to point to Misc/SpecialBuilds and fiddled with the markup a bit. ........ r50949 | neal.norwitz | 2006-07-29 21:29:35 +0200 (Sat, 29 Jul 2006) | 6 lines Disable these tests until they are reliable across platforms. These problems may mask more important, real problems. One or both methods are known to fail on: Solaris, OpenBSD, Debian, Ubuntu. They pass on Windows and some Linux boxes. ........ r50950 | andrew.kuchling | 2006-07-29 21:50:37 +0200 (Sat, 29 Jul 2006) | 1 line [Patch #1068277] Clarify that os.path.exists() can return False depending on permissions. Fred approved committing this patch in December 2004! ........ r50952 | fred.drake | 2006-07-29 22:04:42 +0200 (Sat, 29 Jul 2006) | 6 lines SF bug #1193966: Weakref types documentation misplaced The information about supporting weakrefs with types defined in C extensions is moved to the Extending & Embedding manual. Py_TPFLAGS_HAVE_WEAKREFS is no longer mentioned since it is part of Py_TPFLAGS_DEFAULT. ........ r50953 | skip.montanaro | 2006-07-29 22:06:05 +0200 (Sat, 29 Jul 2006) | 4 lines Add a comment to the csv reader documentation that explains why the treatment of newlines changed in 2.5. Pulled almost verbatim from a comment by Andrew McNamara in . ........ r50954 | neal.norwitz | 2006-07-29 22:20:52 +0200 (Sat, 29 Jul 2006) | 3 lines If the executable doesn't exist, there's no reason to try to start it. This prevents garbage about command not found being printed on Solaris. ........ r50955 | fred.drake | 2006-07-29 22:21:25 +0200 (Sat, 29 Jul 2006) | 1 line fix minor markup error that introduced extra punctuation ........ r50957 | neal.norwitz | 2006-07-29 22:37:08 +0200 (Sat, 29 Jul 2006) | 3 lines Disable test_getnode too, since this is also unreliable. ........ r50958 | andrew.kuchling | 2006-07-29 23:27:12 +0200 (Sat, 29 Jul 2006) | 1 line Follow TeX's conventions for hyphens ........ r50959 | andrew.kuchling | 2006-07-29 23:30:21 +0200 (Sat, 29 Jul 2006) | 1 line Fix case for 'Unix' ........ r50960 | fred.drake | 2006-07-30 01:34:57 +0200 (Sun, 30 Jul 2006) | 1 line markup cleanups ........ r50961 | andrew.kuchling | 2006-07-30 02:27:34 +0200 (Sun, 30 Jul 2006) | 1 line Minor typo fixes ........ r50962 | andrew.kuchling | 2006-07-30 02:37:56 +0200 (Sun, 30 Jul 2006) | 1 line [Bug #793553] Correct description of keyword arguments for SSL authentication ........ r50963 | tim.peters | 2006-07-30 02:58:15 +0200 (Sun, 30 Jul 2006) | 2 lines Whitespace normalization. ........ r50964 | fred.drake | 2006-07-30 05:03:43 +0200 (Sun, 30 Jul 2006) | 1 line lots of markup nits, most commonly Unix/unix --> \UNIX ........ r50965 | fred.drake | 2006-07-30 07:41:28 +0200 (Sun, 30 Jul 2006) | 1 line update information on wxPython, from Robin Dunn ........ r50966 | fred.drake | 2006-07-30 07:49:49 +0200 (Sun, 30 Jul 2006) | 4 lines remove possibly-outdated comment on what GUI toolkit is most commonly used; it is hard to know whether this is right, and it does not add valuable reference information at any rate ........ r50967 | fred.drake | 2006-07-30 07:55:39 +0200 (Sun, 30 Jul 2006) | 3 lines - remove yet another reference to how commonly Tkinter is (thought to be) used - fix an internal section reference ........ r50968 | neal.norwitz | 2006-07-30 08:53:31 +0200 (Sun, 30 Jul 2006) | 4 lines Patch #1531113: Fix augmented assignment with yield expressions. Also fix a SystemError when trying to assign to yield expressions. ........ r50969 | neal.norwitz | 2006-07-30 08:55:48 +0200 (Sun, 30 Jul 2006) | 5 lines Add PyErr_WarnEx() so C code can pass the stacklevel to warnings.warn(). This provides the proper warning for struct.pack(). PyErr_Warn() is now deprecated in favor of PyErr_WarnEx(). As mentioned by Tim Peters on python-dev. ........ r50970 | neal.norwitz | 2006-07-30 08:57:04 +0200 (Sun, 30 Jul 2006) | 3 lines Bug #1515471: string.replace() accepts character buffers again. Pass the char* and size around rather than PyObject's. ........ r50971 | neal.norwitz | 2006-07-30 08:59:13 +0200 (Sun, 30 Jul 2006) | 1 line Whitespace normalization ........ r50973 | georg.brandl | 2006-07-30 12:53:32 +0200 (Sun, 30 Jul 2006) | 3 lines Clarify that __op__ methods must return NotImplemented if they don't support the operation. ........ r50974 | georg.brandl | 2006-07-30 13:07:23 +0200 (Sun, 30 Jul 2006) | 3 lines Bug #1002398: The documentation for os.path.sameopenfile now correctly refers to file descriptors, not file objects. ........ r50977 | martin.v.loewis | 2006-07-30 15:00:31 +0200 (Sun, 30 Jul 2006) | 3 lines Don't copy directory stat times in shutil.copytree on Windows Fixes #1525866. ........ r50978 | martin.v.loewis | 2006-07-30 15:14:05 +0200 (Sun, 30 Jul 2006) | 3 lines Base __version__ on sys.version_info, as distutils is no longer maintained separatedly. ........ r50979 | martin.v.loewis | 2006-07-30 15:27:31 +0200 (Sun, 30 Jul 2006) | 3 lines Mention Cygwin in distutils error message about a missing VS 2003. Fixes #1257728. ........ r50982 | martin.v.loewis | 2006-07-30 16:09:47 +0200 (Sun, 30 Jul 2006) | 5 lines Drop usage of test -e in configure as it is not portable. Fixes #1439538 Will backport to 2.4 Also regenerate pyconfig.h.in. ........ r50984 | georg.brandl | 2006-07-30 18:20:10 +0200 (Sun, 30 Jul 2006) | 3 lines Fix makefile changes for python-config. ........ r50985 | george.yoshida | 2006-07-30 18:37:37 +0200 (Sun, 30 Jul 2006) | 2 lines Rename struct.pack_to to struct.pack_into as changed in revision 46642. ........ r50986 | george.yoshida | 2006-07-30 18:41:30 +0200 (Sun, 30 Jul 2006) | 2 lines Typo fix ........ r50987 | neal.norwitz | 2006-07-30 21:18:13 +0200 (Sun, 30 Jul 2006) | 1 line Add some asserts and update comments ........ r50988 | neal.norwitz | 2006-07-30 21:18:38 +0200 (Sun, 30 Jul 2006) | 1 line Verify that the signal handlers were really called ........ r50989 | neal.norwitz | 2006-07-30 21:20:42 +0200 (Sun, 30 Jul 2006) | 3 lines Try to prevent hangs on Tru64/Alpha buildbot. I'm not certain this will help and may need to be reverted if it causes problems. ........ r50990 | georg.brandl | 2006-07-30 22:18:51 +0200 (Sun, 30 Jul 2006) | 2 lines Bug #1531349: right <-> left glitch in __rop__ description. ........ r50992 | tim.peters | 2006-07-31 03:46:03 +0200 (Mon, 31 Jul 2006) | 2 lines Whitespace normalization. ........ r50993 | andrew.mcnamara | 2006-07-31 04:27:48 +0200 (Mon, 31 Jul 2006) | 2 lines Redo the comment about the 2.5 change in quoted-newline handling. ........ r50994 | tim.peters | 2006-07-31 04:40:23 +0200 (Mon, 31 Jul 2006) | 10 lines ZipFile.close(): Killed one of the struct.pack deprecation warnings on Win32. Also added an XXX about the line: pos3 = self.fp.tell() `pos3` is never referenced, and I have no idea what the code intended to do instead. ........ r50996 | tim.peters | 2006-07-31 04:53:03 +0200 (Mon, 31 Jul 2006) | 8 lines ZipFile.close(): Kill the other struct.pack deprecation warning on Windows. Afraid I can't detect a pattern to when the pack formats decide to use a signed or unsigned format code -- appears nearly arbitrary to my eyes. So I left all the pack formats alone and changed the special-case data values instead. ........ r50997 | skip.montanaro | 2006-07-31 05:09:45 +0200 (Mon, 31 Jul 2006) | 1 line minor tweaks ........ r50998 | skip.montanaro | 2006-07-31 05:11:11 +0200 (Mon, 31 Jul 2006) | 1 line minor tweaks ........ r50999 | andrew.kuchling | 2006-07-31 14:20:24 +0200 (Mon, 31 Jul 2006) | 1 line Add refcounts for PyErr_WarnEx ........ r51000 | andrew.kuchling | 2006-07-31 14:39:05 +0200 (Mon, 31 Jul 2006) | 9 lines Document PyErr_WarnEx. (Bad Neal! No biscuit!) Is the explanation of the 'stacklevel' parameter clear? Please feel free to edit it. I don't have LaTeX installed on this machine, so haven't verified that the markup is correct. Will check tonight, or maybe the automatic doc build will tell me. ........ r51001 | andrew.kuchling | 2006-07-31 14:52:26 +0200 (Mon, 31 Jul 2006) | 1 line Add PyErr_WarnEx() ........ r51002 | andrew.kuchling | 2006-07-31 15:18:27 +0200 (Mon, 31 Jul 2006) | 1 line Mention csv newline changes ........ r51003 | andrew.kuchling | 2006-07-31 17:22:58 +0200 (Mon, 31 Jul 2006) | 1 line Typo fix ........ r51004 | andrew.kuchling | 2006-07-31 17:23:43 +0200 (Mon, 31 Jul 2006) | 1 line Remove reference to notation ........ r51005 | georg.brandl | 2006-07-31 18:00:34 +0200 (Mon, 31 Jul 2006) | 3 lines Fix function name. ........ r51006 | andrew.kuchling | 2006-07-31 18:10:24 +0200 (Mon, 31 Jul 2006) | 1 line [Bug #1514540] Instead of putting the standard types in a section, put them in a chapter of their own. This means string methods will now show up in the ToC. (Should the types come before or after the functions+exceptions+constants chapter? I've put them after, for now.) ........ r51007 | andrew.kuchling | 2006-07-31 18:22:05 +0200 (Mon, 31 Jul 2006) | 1 line [Bug #848556] Remove \d* from second alternative to avoid exponential case when repeating match ........ r51008 | andrew.kuchling | 2006-07-31 18:27:57 +0200 (Mon, 31 Jul 2006) | 1 line Update list of files; fix a typo ........ r51013 | andrew.kuchling | 2006-08-01 18:24:30 +0200 (Tue, 01 Aug 2006) | 1 line typo fix ........ r51018 | thomas.heller | 2006-08-01 18:54:43 +0200 (Tue, 01 Aug 2006) | 2 lines Fix a potential segfault and various potentail refcount leaks in the cast() function. ........ r51020 | thomas.heller | 2006-08-01 19:46:10 +0200 (Tue, 01 Aug 2006) | 1 line Minimal useful docstring for CopyComPointer. ........ r51021 | andrew.kuchling | 2006-08-01 20:16:15 +0200 (Tue, 01 Aug 2006) | 8 lines [Patch #1520905] Attempt to suppress core file created by test_subprocess.py. Patch by Douglas Greiman. The test_run_abort() testcase produces a core file on Unix systems, even though the test is successful. This can be confusing or alarming to someone who runs 'make test' and then finds that the Python interpreter apparently crashed. ........ r51023 | georg.brandl | 2006-08-01 20:49:24 +0200 (Tue, 01 Aug 2006) | 3 lines os.urandom no longer masks unrelated exceptions like SystemExit or KeyboardInterrupt. ........ r51025 | thomas.heller | 2006-08-01 21:14:15 +0200 (Tue, 01 Aug 2006) | 2 lines Speed up PyType_stgdict and PyObject_stgdict. ........ r51027 | ronald.oussoren | 2006-08-01 22:30:31 +0200 (Tue, 01 Aug 2006) | 3 lines Make sure the postinstall action that optionally updates the user's profile on MacOS X actually works correctly in all cases. ........ r51028 | ronald.oussoren | 2006-08-01 23:00:57 +0200 (Tue, 01 Aug 2006) | 4 lines This fixes bug #1527397: PythonLauncher runs scripts with the wrong working directory. It also fixes a bug where PythonLauncher failed to launch scripts when the scriptname (or the path to the script) contains quotes. ........ r51031 | tim.peters | 2006-08-02 05:27:46 +0200 (Wed, 02 Aug 2006) | 2 lines Whitespace normalization. ........ r51032 | tim.peters | 2006-08-02 06:12:36 +0200 (Wed, 02 Aug 2006) | 19 lines Try to squash struct.pack warnings on the "amd64 gentoo trunk" buildbot (& possibly other 64-bit boxes) during test_gzip. The native zlib crc32 function returns an unsigned 32-bit integer, which the Python wrapper implicitly casts to C long. Therefore the same crc can "look negative" on a 32-bit box but "look positive" on a 64-bit box. This patch papers over that platform difference when writing the crc to file. It may be better to change the Python wrapper, either to make the result "look positive" on all platforms (which means it may have to return a Python long at times on a 32-bit box), or to keep the sign the same across boxes. But that would be a visible change in what users see, while the current hack changes no visible behavior (well, apart from stopping the struct deprecation warning). Note that the module-level write32() function is no longer used. ........ r51033 | neal.norwitz | 2006-08-02 06:27:11 +0200 (Wed, 02 Aug 2006) | 4 lines Prevent memory leak on error. Reported by Klocwork #36 ........ r51034 | tim.peters | 2006-08-02 07:20:08 +0200 (Wed, 02 Aug 2006) | 9 lines _Stream.close(): Try to kill struct.pack() warnings when writing the crc to file on the "PPC64 Debian trunk" buildbot when running test_tarfile. This is again a case where the native zlib crc is an unsigned 32-bit int, but the Python wrapper implicitly casts it to signed C long, so that "the sign bit looks different" on different platforms. ........ r51035 | ronald.oussoren | 2006-08-02 08:10:10 +0200 (Wed, 02 Aug 2006) | 2 lines Updated documentation for the script that builds the OSX installer. ........ r51036 | neal.norwitz | 2006-08-02 08:14:22 +0200 (Wed, 02 Aug 2006) | 2 lines _PyWeakref_GetWeakrefCount() now returns a Py_ssize_t instead of long. ........ r51037 | neal.norwitz | 2006-08-02 08:15:10 +0200 (Wed, 02 Aug 2006) | 1 line v is already checked for NULL, so just DECREF it ........ r51038 | neal.norwitz | 2006-08-02 08:19:19 +0200 (Wed, 02 Aug 2006) | 1 line Let us know when there was a problem and the child had to kill the parent ........ r51039 | neal.norwitz | 2006-08-02 08:46:21 +0200 (Wed, 02 Aug 2006) | 5 lines Patch #1519025 and bug #926423: If a KeyboardInterrupt occurs during a socket operation on a socket with a timeout, the exception will be caught correctly. Previously, the exception was not caught. ........ r51040 | neal.norwitz | 2006-08-02 09:09:32 +0200 (Wed, 02 Aug 2006) | 1 line Add some explanation about Klocwork and Coverity static analysis ........ r51041 | anthony.baxter | 2006-08-02 09:43:09 +0200 (Wed, 02 Aug 2006) | 1 line pre-release machinations ........ r51043 | thomas.heller | 2006-08-02 13:35:31 +0200 (Wed, 02 Aug 2006) | 4 lines A few nore words about what ctypes does. Document that using the wrong calling convention can also raise 'ValueError: Procedure called with the wrong number of arguments'. ........ r51045 | thomas.heller | 2006-08-02 14:00:13 +0200 (Wed, 02 Aug 2006) | 1 line Fix a mistake. ........ r51046 | martin.v.loewis | 2006-08-02 15:53:55 +0200 (Wed, 02 Aug 2006) | 3 lines Correction of patch #1455898: In the mbcs decoder, set final=False for stream decoder, but final=True for the decode function. ........ r51049 | tim.peters | 2006-08-02 20:19:35 +0200 (Wed, 02 Aug 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r51079 | neal.norwitz | 2006-08-04 06:50:21 +0200 (Fri, 04 Aug 2006) | 3 lines Bug #1531405, format_exception no longer raises an exception if str(exception) raised an exception. ........ r51080 | neal.norwitz | 2006-08-04 06:58:47 +0200 (Fri, 04 Aug 2006) | 11 lines Bug #1191458: tracing over for loops now produces a line event on each iteration. I'm not positive this is the best way to handle this. I'm also not sure that there aren't other cases where the lnotab is generated incorrectly. It would be great if people that use pdb or tracing could test heavily. Also: * Remove dead/duplicated code that wasn't used/necessary because we already handled the docstring prior to entering the loop. * add some debugging code into the compiler (#if 0'd out). ........ r51081 | neal.norwitz | 2006-08-04 07:09:28 +0200 (Fri, 04 Aug 2006) | 4 lines Bug #1333982: string/number constants were inappropriately stored in the byte code and co_consts even if they were not used, ie immediately popped off the stack. ........ r51082 | neal.norwitz | 2006-08-04 07:12:19 +0200 (Fri, 04 Aug 2006) | 1 line There were really two issues ........ r51084 | fred.drake | 2006-08-04 07:17:21 +0200 (Fri, 04 Aug 2006) | 1 line SF patch #1534048 (bug #1531003): fix typo in error message ........ r51085 | gregory.p.smith | 2006-08-04 07:17:47 +0200 (Fri, 04 Aug 2006) | 3 lines fix typos ........ r51087 | georg.brandl | 2006-08-04 08:03:53 +0200 (Fri, 04 Aug 2006) | 3 lines Fix bug caused by first decrefing, then increfing. ........ r51109 | neil.schemenauer | 2006-08-04 18:20:30 +0200 (Fri, 04 Aug 2006) | 5 lines Fix the 'compiler' package to generate correct code for MAKE_CLOSURE. In the 2.5 development cycle, MAKE_CLOSURE as changed to take free variables as a tuple rather than as individual items on the stack. Closes patch #1534084. ........ r51110 | georg.brandl | 2006-08-04 20:03:37 +0200 (Fri, 04 Aug 2006) | 3 lines Change fix for segfaulting property(), add a NEWS entry and a test. ........ r51111 | georg.brandl | 2006-08-04 20:07:34 +0200 (Fri, 04 Aug 2006) | 3 lines Better fix for bug #1531405, not executing str(value) twice. ........ r51112 | thomas.heller | 2006-08-04 20:17:40 +0200 (Fri, 04 Aug 2006) | 1 line On Windows, make PyErr_Warn an exported function again. ........ r51113 | thomas.heller | 2006-08-04 20:57:34 +0200 (Fri, 04 Aug 2006) | 4 lines Fix #1530448 - fix ctypes build failure on solaris 10. The '-mimpure-text' linker flag is required when linking _ctypes.so. ........ r51114 | thomas.heller | 2006-08-04 21:49:31 +0200 (Fri, 04 Aug 2006) | 3 lines Fix #1534738: win32 debug version of _msi must be _msi_d.pyd, not _msi.pyd. Fix the name of the pdb file as well. ........ r51115 | andrew.kuchling | 2006-08-04 22:37:43 +0200 (Fri, 04 Aug 2006) | 1 line Typo fixes ........ r51116 | andrew.kuchling | 2006-08-04 23:10:03 +0200 (Fri, 04 Aug 2006) | 1 line Fix mangled sentence ........ r51118 | tim.peters | 2006-08-05 00:00:35 +0200 (Sat, 05 Aug 2006) | 2 lines Whitespace normalization. ........ r51119 | bob.ippolito | 2006-08-05 01:59:21 +0200 (Sat, 05 Aug 2006) | 5 lines Fix #1530559, struct.pack raises TypeError where it used to convert. Passing float arguments to struct.pack when integers are expected now triggers a DeprecationWarning. ........ r51123 | georg.brandl | 2006-08-05 08:10:54 +0200 (Sat, 05 Aug 2006) | 3 lines Patch #1534922: correct and enhance unittest docs. ........ r51126 | georg.brandl | 2006-08-06 09:06:33 +0200 (Sun, 06 Aug 2006) | 2 lines Bug #1535182: really test the xreadlines() method of bz2 objects. ........ r51128 | georg.brandl | 2006-08-06 09:26:21 +0200 (Sun, 06 Aug 2006) | 4 lines Bug #1535081: A leading underscore has been added to the names of the md5 and sha modules, so add it in Modules/Setup.dist too. ........ r51129 | georg.brandl | 2006-08-06 10:23:54 +0200 (Sun, 06 Aug 2006) | 3 lines Bug #1535165: fixed a segfault in input() and raw_input() when sys.stdin is closed. ........ r51131 | georg.brandl | 2006-08-06 11:17:16 +0200 (Sun, 06 Aug 2006) | 2 lines Don't produce output in test_builtin. ........ r51133 | andrew.macintyre | 2006-08-06 14:37:03 +0200 (Sun, 06 Aug 2006) | 4 lines test_threading now skips testing alternate thread stack sizes on platforms that don't support changing thread stack size. ........ r51134 | andrew.kuchling | 2006-08-07 00:07:04 +0200 (Mon, 07 Aug 2006) | 2 lines [Patch #1464056] Ensure that we use the panelw library when linking with ncursesw. Once I see how the buildbots react, I'll backport this to 2.4. ........ r51137 | georg.brandl | 2006-08-08 13:52:34 +0200 (Tue, 08 Aug 2006) | 3 lines webbrowser: Silence stderr output if no gconftool or gnome browser found ........ r51138 | georg.brandl | 2006-08-08 13:56:21 +0200 (Tue, 08 Aug 2006) | 7 lines Remove "non-mapping" and "non-sequence" from TypeErrors raised by PyMapping_Size and PySequence_Size. Because len() tries first sequence, then mapping size, it will always raise a "non-mapping object has no len" error which is confusing. ........ r51139 | thomas.heller | 2006-08-08 19:37:00 +0200 (Tue, 08 Aug 2006) | 3 lines memcmp() can return values other than -1, 0, and +1 but tp_compare must not. ........ r51140 | thomas.heller | 2006-08-08 19:39:20 +0200 (Tue, 08 Aug 2006) | 1 line Remove accidently committed, duplicated test. ........ r51147 | andrew.kuchling | 2006-08-08 20:50:14 +0200 (Tue, 08 Aug 2006) | 1 line Reword paragraph to clarify ........ r51148 | andrew.kuchling | 2006-08-08 20:56:08 +0200 (Tue, 08 Aug 2006) | 1 line Move obmalloc item into C API section ........ r51149 | andrew.kuchling | 2006-08-08 21:00:14 +0200 (Tue, 08 Aug 2006) | 1 line 'Other changes' section now has only one item; move the item elsewhere and remove the section ........ r51150 | andrew.kuchling | 2006-08-08 21:00:34 +0200 (Tue, 08 Aug 2006) | 1 line Bump version number ........ r51151 | georg.brandl | 2006-08-08 22:11:22 +0200 (Tue, 08 Aug 2006) | 2 lines Bug #1536828: typo: TypeType should have been StringType. ........ r51153 | georg.brandl | 2006-08-08 22:13:13 +0200 (Tue, 08 Aug 2006) | 2 lines Bug #1536660: separate two words. ........ r51155 | georg.brandl | 2006-08-08 22:48:10 +0200 (Tue, 08 Aug 2006) | 3 lines ``str`` is now the same object as ``types.StringType``. ........ r51156 | tim.peters | 2006-08-09 02:52:26 +0200 (Wed, 09 Aug 2006) | 2 lines Whitespace normalization. ........ r51158 | georg.brandl | 2006-08-09 09:03:22 +0200 (Wed, 09 Aug 2006) | 4 lines Introduce an upper bound on tuple nesting depth in C argument format strings; fixes rest of #1523610. ........ r51160 | martin.v.loewis | 2006-08-09 09:57:39 +0200 (Wed, 09 Aug 2006) | 4 lines __hash__ may now return long int; the final hash value is obtained by invoking hash on the long int. Fixes #1536021. ........ r51168 | andrew.kuchling | 2006-08-09 15:03:41 +0200 (Wed, 09 Aug 2006) | 1 line [Bug #1536021] Mention __hash__ change ........ r51169 | andrew.kuchling | 2006-08-09 15:57:05 +0200 (Wed, 09 Aug 2006) | 1 line [Patch #1534027] Add notes on locale module changes ........ r51170 | andrew.kuchling | 2006-08-09 16:05:35 +0200 (Wed, 09 Aug 2006) | 1 line Add missing 'self' parameters ........ r51171 | andrew.kuchling | 2006-08-09 16:06:19 +0200 (Wed, 09 Aug 2006) | 1 line Reindent code ........ r51172 | armin.rigo | 2006-08-09 16:55:26 +0200 (Wed, 09 Aug 2006) | 2 lines Fix and test for an infinite C recursion. ........ r51173 | ronald.oussoren | 2006-08-09 16:56:33 +0200 (Wed, 09 Aug 2006) | 2 lines It's unlikely that future versions will require _POSIX_C_SOURCE ........ r51178 | armin.rigo | 2006-08-09 17:37:26 +0200 (Wed, 09 Aug 2006) | 2 lines Concatenation on a long string breaks (SF #1526585). ........ r51180 | kurt.kaiser | 2006-08-09 18:46:15 +0200 (Wed, 09 Aug 2006) | 8 lines 1. When used w/o subprocess, all exceptions were preceeded by an error message claiming they were IDLE internal errors (since 1.2a1). 2. Add Ronald Oussoren to CREDITS M NEWS.txt M PyShell.py M CREDITS.txt ........ r51181 | kurt.kaiser | 2006-08-09 19:47:15 +0200 (Wed, 09 Aug 2006) | 4 lines As a slight enhancement to the previous checkin, improve the internal error reporting by moving message to IDLE console. ........ r51182 | andrew.kuchling | 2006-08-09 20:23:14 +0200 (Wed, 09 Aug 2006) | 1 line Typo fix ........ r51183 | kurt.kaiser | 2006-08-09 22:34:46 +0200 (Wed, 09 Aug 2006) | 2 lines ToggleTab dialog was setting indent to 8 even if cancelled (since 1.2a1). ........ r51184 | martin.v.loewis | 2006-08-10 01:42:18 +0200 (Thu, 10 Aug 2006) | 2 lines Add some commentary on -mimpure-text. ........ r51185 | tim.peters | 2006-08-10 02:58:49 +0200 (Thu, 10 Aug 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r51186 | kurt.kaiser | 2006-08-10 03:41:17 +0200 (Thu, 10 Aug 2006) | 2 lines Changing tokenize (39046) to detect dedent broke tabnanny check (since 1.2a1) ........ r51187 | tim.peters | 2006-08-10 05:01:26 +0200 (Thu, 10 Aug 2006) | 13 lines test_copytree_simple(): This was leaving behind two new temp directories each time it ran, at least on Windows. Several changes: explicitly closed all files; wrapped long lines; stopped suppressing errors when removing a file or directory fails (removing /shouldn't/ fail!); and changed what appeared to be incorrect usage of os.removedirs() (that doesn't remove empty directories at and /under/ the given path, instead it must be given an empty leaf directory and then deletes empty directories moving /up/ the path -- could be that the conceptually simpler shutil.rmtree() was really actually intended here). ........ --- __init__.py | 4 +++- command/bdist_rpm.py | 7 ++++--- command/upload.py | 2 +- msvccompiler.py | 8 +++++--- sysconfig.py | 2 +- unixccompiler.py | 2 +- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/__init__.py b/__init__.py index a1dbb4b5..9c60e546 100644 --- a/__init__.py +++ b/__init__.py @@ -12,4 +12,6 @@ used from a setup script as __revision__ = "$Id$" -__version__ = "2.4.0" +import sys +__version__ = "%d.%d.%d" % sys.version_info[:3] +del sys diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 738e3f72..5b099658 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -467,7 +467,8 @@ class bdist_rpm (Command): # rpm scripts # figure out default build script - def_build = "%s setup.py build" % self.python + def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0])) + def_build = "%s build" % def_setup_call if self.use_rpm_opt_flags: def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build @@ -481,9 +482,9 @@ class bdist_rpm (Command): ('prep', 'prep_script', "%setup"), ('build', 'build_script', def_build), ('install', 'install_script', - ("%s setup.py install " + ("%s install " "--root=$RPM_BUILD_ROOT " - "--record=INSTALLED_FILES") % self.python), + "--record=INSTALLED_FILES") % def_setup_call), ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), ('verifyscript', 'verify_script', None), ('pre', 'pre_install', None), diff --git a/command/upload.py b/command/upload.py index 4a9ed398..67ba0804 100644 --- a/command/upload.py +++ b/command/upload.py @@ -185,7 +185,7 @@ class upload(Command): http.endheaders() http.send(body) except socket.error, e: - self.announce(e.msg, log.ERROR) + self.announce(str(e), log.ERROR) return r = http.getresponse() diff --git a/msvccompiler.py b/msvccompiler.py index d24d0ac6..0d72837e 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -131,8 +131,10 @@ class MacroExpander: self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") except KeyError, exc: # raise DistutilsPlatformError, \ - ("The .NET Framework SDK needs to be installed before " - "building extensions for Python.") + ("""Python was built with Visual Studio 2003; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2003 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") p = r"Software\Microsoft\NET Framework Setup\Product" for base in HKEYS: @@ -237,7 +239,7 @@ class MSVCCompiler (CCompiler) : def initialize(self): self.__paths = [] - if os.environ.has_key("MSSdk") and self.find_exe("cl.exe"): + if os.environ.has_key("DISTUTILS_USE_SDK") and os.environ.has_key("MSSdk") and self.find_exe("cl.exe"): # Assume that the SDK set up everything alright; don't try to be # smarter self.cc = "cl.exe" diff --git a/sysconfig.py b/sysconfig.py index e1397a19..76fe256c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -512,7 +512,7 @@ def get_config_vars(*args): for key in ('LDFLAGS', 'BASECFLAGS'): flags = _config_vars[key] flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = re.sub('-isysroot [^ \t]* ', ' ', flags) + flags = re.sub('-isysroot [^ \t]*', ' ', flags) _config_vars[key] = flags if args: diff --git a/unixccompiler.py b/unixccompiler.py index 324819d4..6cd14f77 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -78,7 +78,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): try: index = compiler_so.index('-isysroot') # Strip this argument and the next one: - del compiler_so[index:index+1] + del compiler_so[index:index+2] except ValueError: pass -- cgit v1.2.1 From 4c9e5df1bd52b0bad6fcfb2ac599999a00c8f413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Wed, 16 Aug 2006 08:13:26 +0000 Subject: Revert to having static version numbers again. --- __init__.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/__init__.py b/__init__.py index 9c60e546..21d34c76 100644 --- a/__init__.py +++ b/__init__.py @@ -12,6 +12,12 @@ used from a setup script as __revision__ = "$Id$" -import sys -__version__ = "%d.%d.%d" % sys.version_info[:3] -del sys +# Distutils version +# +# Please coordinate with Marc-Andre Lemburg when adding +# new features to distutils that would warrant bumping the version number. +# +# In general, major and minor version should loosely follow the Python +# version number the distutils code was shipped with. +# +__version__ = "2.5.0" -- cgit v1.2.1 From 61e2384194c8c759b122464be8f82c562301a108 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 18 Aug 2006 22:13:04 +0000 Subject: Get rid of dict.has_key(). Boy this has a lot of repercussions! Not all code has been fixed yet; this is just a checkpoint... The C API still has PyDict_HasKey() and _HasKeyString(); not sure if I want to change those just yet. --- archive_util.py | 2 +- ccompiler.py | 2 +- command/build_ext.py | 2 +- core.py | 6 +++--- dist.py | 8 ++++---- fancy_getopt.py | 8 ++++---- sysconfig.py | 22 +++++++++++----------- text_file.py | 4 ++-- util.py | 6 +++--- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/archive_util.py b/archive_util.py index b725a14b..059d5447 100644 --- a/archive_util.py +++ b/archive_util.py @@ -124,7 +124,7 @@ ARCHIVE_FORMATS = { def check_archive_formats (formats): for format in formats: - if not ARCHIVE_FORMATS.has_key(format): + if format not in ARCHIVE_FORMATS: return format else: return None diff --git a/ccompiler.py b/ccompiler.py index 1349abeb..0ed9a40a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -159,7 +159,7 @@ class CCompiler: # basically the same things with Unix C compilers. for key in args.keys(): - if not self.executables.has_key(key): + if key not in self.executables: raise ValueError, \ "unknown executable '%s' for class %s" % \ (key, self.__class__.__name__) diff --git a/command/build_ext.py b/command/build_ext.py index 9626710b..cd67544b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -341,7 +341,7 @@ class build_ext (Command): # Medium-easy stuff: same syntax/semantics, different names. ext.runtime_library_dirs = build_info.get('rpath') - if build_info.has_key('def_file'): + if 'def_file' in build_info: log.warn("'def_file' element of build info dict " "no longer supported") diff --git a/core.py b/core.py index c9c6f037..85a28fe7 100644 --- a/core.py +++ b/core.py @@ -101,9 +101,9 @@ def setup (**attrs): else: klass = Distribution - if not attrs.has_key('script_name'): + if 'script_name' not in attrs: attrs['script_name'] = os.path.basename(sys.argv[0]) - if not attrs.has_key('script_args'): + if 'script_args' not in attrs: attrs['script_args'] = sys.argv[1:] # Create the Distribution instance, using the remaining arguments @@ -111,7 +111,7 @@ def setup (**attrs): try: _setup_distribution = dist = klass(attrs) except DistutilsSetupError, msg: - if attrs.has_key('name'): + if 'name' not in attrs: raise SystemExit, "error in %s setup command: %s" % \ (attrs['name'], msg) else: diff --git a/dist.py b/dist.py index ff49886d..d098cb97 100644 --- a/dist.py +++ b/dist.py @@ -239,7 +239,7 @@ Common commands: (see '--help-commands' for more) for (opt, val) in cmd_options.items(): opt_dict[opt] = ("setup script", val) - if attrs.has_key('licence'): + if 'licence' in attrs: attrs['license'] = attrs['licence'] del attrs['licence'] msg = "'licence' distribution option is deprecated; use 'license'" @@ -343,7 +343,7 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - if os.environ.has_key('HOME'): + if 'HOME' in os.environ: user_file = os.path.join(os.environ.get('HOME'), user_filename) if os.path.isfile(user_file): files.append(user_file) @@ -388,7 +388,7 @@ Common commands: (see '--help-commands' for more) # If there was a "global" section in the config file, use it # to set Distribution options. - if self.command_options.has_key('global'): + if 'global' in self.command_options: for (opt, (src, val)) in self.command_options['global'].items(): alias = self.negative_opt.get(opt) try: @@ -907,7 +907,7 @@ Common commands: (see '--help-commands' for more) try: is_string = type(value) is StringType - if neg_opt.has_key(option) and is_string: + if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: setattr(command_obj, option, strtobool(value)) diff --git a/fancy_getopt.py b/fancy_getopt.py index 218ed73f..31cf0c5c 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -97,7 +97,7 @@ class FancyGetopt: self._build_index() def add_option (self, long_option, short_option=None, help_string=None): - if self.option_index.has_key(long_option): + if long_option in self.option_index: raise DistutilsGetoptError, \ "option conflict: already an option '%s'" % long_option else: @@ -109,7 +109,7 @@ class FancyGetopt: def has_option (self, long_option): """Return true if the option table for this parser has an option with long name 'long_option'.""" - return self.option_index.has_key(long_option) + return long_option in self.option_index def get_attr_name (self, long_option): """Translate long option name 'long_option' to the form it @@ -121,11 +121,11 @@ class FancyGetopt: def _check_alias_dict (self, aliases, what): assert type(aliases) is DictionaryType for (alias, opt) in aliases.items(): - if not self.option_index.has_key(alias): + if alias not in self.option_index: raise DistutilsGetoptError, \ ("invalid %s '%s': " "option '%s' not defined") % (what, alias, alias) - if not self.option_index.has_key(opt): + if opt not in self.option_index: raise DistutilsGetoptError, \ ("invalid %s '%s': " "aliased option '%s' not defined") % (what, alias, opt) diff --git a/sysconfig.py b/sysconfig.py index 76fe256c..0ea4bb73 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -150,22 +150,22 @@ def customize_compiler(compiler): get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SO') - if os.environ.has_key('CC'): + if 'CC' in os.environ: cc = os.environ['CC'] - if os.environ.has_key('CXX'): + if 'CXX' in os.environ: cxx = os.environ['CXX'] - if os.environ.has_key('LDSHARED'): + if 'LDSHARED' in os.environ: ldshared = os.environ['LDSHARED'] - if os.environ.has_key('CPP'): + if 'CPP' in os.environ: cpp = os.environ['CPP'] else: cpp = cc + " -E" # not always - if os.environ.has_key('LDFLAGS'): + if 'LDFLAGS' in os.environ: ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if os.environ.has_key('CFLAGS'): + if 'CFLAGS' in os.environ: cflags = opt + ' ' + os.environ['CFLAGS'] ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if os.environ.has_key('CPPFLAGS'): + if 'CPPFLAGS' in os.environ: cpp = cpp + ' ' + os.environ['CPPFLAGS'] cflags = cflags + ' ' + os.environ['CPPFLAGS'] ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] @@ -277,12 +277,12 @@ def parse_makefile(fn, g=None): if m: n = m.group(1) found = True - if done.has_key(n): + if n in done: item = str(done[n]) - elif notdone.has_key(n): + elif n in notdone: # get it on a subsequent round found = False - elif os.environ.has_key(n): + elif n in os.environ: # do it like make: fall back to environment item = os.environ[n] else: @@ -366,7 +366,7 @@ def _init_posix(): # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so # it needs to be compatible. # If it isn't set we set it to the configure-time value - if sys.platform == 'darwin' and g.has_key('MACOSX_DEPLOYMENT_TARGET'): + if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g: cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') if cur_target == '': diff --git a/text_file.py b/text_file.py index 67efd65e..ff2878de 100644 --- a/text_file.py +++ b/text_file.py @@ -89,7 +89,7 @@ class TextFile: # set values for all options -- either from client option hash # or fallback to default_options for opt in self.default_options.keys(): - if options.has_key (opt): + if opt in options: setattr (self, opt, options[opt]) else: @@ -97,7 +97,7 @@ class TextFile: # sanity check client option hash for opt in options.keys(): - if not self.default_options.has_key (opt): + if opt not in self.default_options: raise KeyError, "invalid TextFile option '%s'" % opt if file is None: diff --git a/util.py b/util.py index 1265f4c4..1bcda93f 100644 --- a/util.py +++ b/util.py @@ -200,11 +200,11 @@ def check_environ (): if _environ_checked: return - if os.name == 'posix' and not os.environ.has_key('HOME'): + if os.name == 'posix' and 'HOME' not in os.environ: import pwd os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] - if not os.environ.has_key('PLAT'): + if 'PLAT' not in os.environ: os.environ['PLAT'] = get_platform() _environ_checked = 1 @@ -222,7 +222,7 @@ def subst_vars (s, local_vars): check_environ() def _subst (match, local_vars=local_vars): var_name = match.group(1) - if local_vars.has_key(var_name): + if var_name in local_vars: return str(local_vars[var_name]) else: return os.environ[var_name] -- cgit v1.2.1 From cacb980ab0e5e233a16ebdcea087b89b2f97f79a Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sun, 20 Aug 2006 16:25:10 +0000 Subject: Get rid of a bunch more has_key() uses. We *really* need a tool for this. test_aepack now passes. IDLE still needs to be converted (among others). --- dir_util.py | 2 +- msvccompiler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dir_util.py b/dir_util.py index a4aff58e..92f49346 100644 --- a/dir_util.py +++ b/dir_util.py @@ -207,7 +207,7 @@ def remove_tree (directory, verbose=0, dry_run=0): cmd[0](cmd[1]) # remove dir from cache if it's already there abspath = os.path.abspath(cmd[1]) - if _path_created.has_key(abspath): + if abspath in _path_created: del _path_created[abspath] except (IOError, OSError), exc: log.warn(grok_environment_error( diff --git a/msvccompiler.py b/msvccompiler.py index 0d72837e..9ec3508c 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -239,7 +239,7 @@ class MSVCCompiler (CCompiler) : def initialize(self): self.__paths = [] - if os.environ.has_key("DISTUTILS_USE_SDK") and os.environ.has_key("MSSdk") and self.find_exe("cl.exe"): + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): # Assume that the SDK set up everything alright; don't try to be # smarter self.cc = "cl.exe" -- cgit v1.2.1 From 75682ad15d892d2159454279c881771f44e2500f Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Mon, 21 Aug 2006 19:07:27 +0000 Subject: Merge current trunk into p3yk. This includes the PyNumber_Index API change, which unfortunately means the errors from the bytes type change somewhat: bytes([300]) still raises a ValueError, but bytes([10**100]) now raises a TypeError (either that, or bytes(1.0) also raises a ValueError -- PyNumber_AsSsize_t() can only raise one type of exception.) Merged revisions 51188-51433 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r51189 | kurt.kaiser | 2006-08-10 19:11:09 +0200 (Thu, 10 Aug 2006) | 4 lines Retrieval of previous shell command was not always preserving indentation since 1.2a1) Patch 1528468 Tal Einat. ........ r51190 | guido.van.rossum | 2006-08-10 19:41:07 +0200 (Thu, 10 Aug 2006) | 3 lines Chris McDonough's patch to defend against certain DoS attacks on FieldStorage. SF bug #1112549. ........ r51191 | guido.van.rossum | 2006-08-10 19:42:50 +0200 (Thu, 10 Aug 2006) | 2 lines News item for SF bug 1112549. ........ r51192 | guido.van.rossum | 2006-08-10 20:09:25 +0200 (Thu, 10 Aug 2006) | 2 lines Fix title -- it's rc1, not beta3. ........ r51194 | martin.v.loewis | 2006-08-10 21:04:00 +0200 (Thu, 10 Aug 2006) | 3 lines Update dangling references to the 3.2 database to mention that this is UCD 4.1 now. ........ r51195 | tim.peters | 2006-08-11 00:45:34 +0200 (Fri, 11 Aug 2006) | 6 lines Followup to bug #1069160. PyThreadState_SetAsyncExc(): internal correctness changes wrt refcount safety and deadlock avoidance. Also added a basic test case (relying on ctypes) and repaired the docs. ........ r51196 | tim.peters | 2006-08-11 00:48:45 +0200 (Fri, 11 Aug 2006) | 2 lines Whitespace normalization. ........ r51197 | tim.peters | 2006-08-11 01:22:13 +0200 (Fri, 11 Aug 2006) | 5 lines Whitespace normalization broke test_cgi, because a line of quoted test data relied on preserving a single trailing blank. Changed the string from raw to regular, and forced in the trailing blank via an explicit \x20 escape. ........ r51198 | tim.peters | 2006-08-11 02:49:01 +0200 (Fri, 11 Aug 2006) | 10 lines test_PyThreadState_SetAsyncExc(): This is failing on some 64-bit boxes. I have no idea what the ctypes docs mean by "integers", and blind-guessing here that it intended to mean the signed C "int" type, in which case perhaps I can repair this by feeding the thread id argument to type ctypes.c_long(). Also made the worker thread daemonic, so it doesn't hang Python shutdown if the test continues to fail. ........ r51199 | tim.peters | 2006-08-11 05:49:10 +0200 (Fri, 11 Aug 2006) | 6 lines force_test_exit(): This has been completely ineffective at stopping test_signal from hanging forever on the Tru64 buildbot. That could be because there's no such thing as signal.SIGALARM. Changed to the idiotic (but standard) signal.SIGALRM instead, and added some more debug output. ........ r51202 | neal.norwitz | 2006-08-11 08:09:41 +0200 (Fri, 11 Aug 2006) | 6 lines Fix the failures on cygwin (2006-08-10 fixed the actual locking issue). The first hunk changes the colon to an ! like other Windows variants. We need to always wait on the child so the lock gets released and no other tests fail. This is the try/finally in the second hunk. ........ r51205 | georg.brandl | 2006-08-11 09:15:38 +0200 (Fri, 11 Aug 2006) | 3 lines Add Chris McDonough (latest cgi.py patch) ........ r51206 | georg.brandl | 2006-08-11 09:26:10 +0200 (Fri, 11 Aug 2006) | 3 lines logging's atexit hook now runs even if the rest of the module has already been cleaned up. ........ r51212 | thomas.wouters | 2006-08-11 17:02:39 +0200 (Fri, 11 Aug 2006) | 4 lines Add ignore of *.pyc and *.pyo to Lib/xml/etree/. ........ r51215 | thomas.heller | 2006-08-11 21:55:35 +0200 (Fri, 11 Aug 2006) | 7 lines When a ctypes C callback function is called, zero out the result storage before converting the result to C data. See the comment in the code for details. Provide a better context for errors when the conversion of a callback function's result cannot be converted. ........ r51218 | neal.norwitz | 2006-08-12 03:43:40 +0200 (Sat, 12 Aug 2006) | 6 lines Klocwork made another run and found a bunch more problems. This is the first batch of fixes that should be easy to verify based on context. This fixes problem numbers: 220 (ast), 323-324 (symtable), 321-322 (structseq), 215 (array), 210 (hotshot), 182 (codecs), 209 (etree). ........ r51219 | neal.norwitz | 2006-08-12 03:45:47 +0200 (Sat, 12 Aug 2006) | 9 lines Even though _Py_Mangle() isn't truly public anyone can call it and there was no verification that privateobj was a PyString. If it wasn't a string, this could have allowed a NULL pointer to creep in below and crash. I wonder if this should be PyString_CheckExact? Must identifiers be strings or can they be subclasses? Klocwork #275 ........ r51220 | neal.norwitz | 2006-08-12 03:46:42 +0200 (Sat, 12 Aug 2006) | 5 lines It's highly unlikely, though possible for PyEval_Get*() to return NULLs. So be safe and do an XINCREF. Klocwork # 221-222. ........ r51221 | neal.norwitz | 2006-08-12 03:47:59 +0200 (Sat, 12 Aug 2006) | 7 lines This code is actually not used unless WITHOUT_COMPLEX is defined. However, there was no error checking that PyFloat_FromDouble returned a valid pointer. I believe this change is correct as it seemed to follow other code in the area. Klocwork # 292. ........ r51222 | neal.norwitz | 2006-08-12 03:49:12 +0200 (Sat, 12 Aug 2006) | 5 lines Handle NULL nodes while parsing. I'm not entirely sure this is correct. There might be something else that needs to be done to setup the error. Klocwork #295. ........ r51223 | neal.norwitz | 2006-08-12 03:50:38 +0200 (Sat, 12 Aug 2006) | 6 lines If _stat_float_times is false, we will try to INCREF ival which could be NULL. Return early in that case. The caller checks for PyErr_Occurred so this should be ok. Klocwork #297 ........ r51224 | neal.norwitz | 2006-08-12 03:51:12 +0200 (Sat, 12 Aug 2006) | 3 lines Move the assert which checks for a NULL pointer first. Klocwork #274. ........ r51225 | neal.norwitz | 2006-08-12 03:53:28 +0200 (Sat, 12 Aug 2006) | 5 lines Try to handle a malloc failure. I'm not entirely sure this is correct. There might be something else we need to do to handle the exception. Klocwork # 212-213 ........ r51226 | neal.norwitz | 2006-08-12 03:57:47 +0200 (Sat, 12 Aug 2006) | 6 lines I'm not sure why this code allocates this string for the error message. I think it would be better to always use snprintf and have the format limit the size of the name appropriately (like %.200s). Klocwork #340 ........ r51227 | neal.norwitz | 2006-08-12 04:06:34 +0200 (Sat, 12 Aug 2006) | 3 lines Check returned pointer is valid. Klocwork #233 ........ r51228 | neal.norwitz | 2006-08-12 04:12:30 +0200 (Sat, 12 Aug 2006) | 1 line Whoops, how did that get in there. :-) Revert all the parts of 51227 that were not supposed to go it. Only Modules/_ctypes/cfields.c was supposed to be changed ........ r51229 | neal.norwitz | 2006-08-12 04:33:36 +0200 (Sat, 12 Aug 2006) | 4 lines Don't deref v if it's NULL. Klocwork #214 ........ r51230 | neal.norwitz | 2006-08-12 05:16:54 +0200 (Sat, 12 Aug 2006) | 5 lines Check return of PyMem_MALLOC (garbage) is non-NULL. Check seq in both portions of if/else. Klocwork #289-290. ........ r51231 | neal.norwitz | 2006-08-12 05:17:41 +0200 (Sat, 12 Aug 2006) | 4 lines PyModule_GetDict() can fail, produce fatal errors if this happens on startup. Klocwork #298-299. ........ r51232 | neal.norwitz | 2006-08-12 05:18:50 +0200 (Sat, 12 Aug 2006) | 5 lines Verify verdat which is returned from malloc is not NULL. Ensure we don't pass NULL to free. Klocwork #306 (at least the first part, checking malloc) ........ r51233 | tim.peters | 2006-08-12 06:42:47 +0200 (Sat, 12 Aug 2006) | 35 lines test_signal: Signal handling on the Tru64 buildbot appears to be utterly insane. Plug some theoretical insecurities in the test script: - Verify that the SIGALRM handler was actually installed. - Don't call alarm() before the handler is installed. - Move everything that can fail inside the try/finally, so the test cleans up after itself more often. - Try sending all the expected signals in force_test_exit(), not just SIGALRM. Since that was fixed to actually send SIGALRM (instead of invisibly dying with an AttributeError), we've seen that sending SIGALRM alone does not stop this from hanging. - Move the "kill the child" business into the finally clause, so the child doesn't survive test failure to send SIGALRM to other tests later (there are also baffling SIGALRM-related failures in test_socket). - Cancel the alarm in the finally clause -- if the test dies early, we again don't want SIGALRM showing up to confuse a later test. Alas, this still relies on timing luck wrt the spawned script that sends the test signals, but it's hard to see how waiting for seconds can so often be so unlucky. test_threadedsignals: curiously, this test never fails on Tru64, but doesn't normally signal SIGALRM. Anyway, fixed an obvious (but probably inconsequential) logic error. ........ r51234 | tim.peters | 2006-08-12 07:17:41 +0200 (Sat, 12 Aug 2006) | 8 lines Ah, fudge. One of the prints here actually "shouldn't be" protected by "if verbose:", which caused the test to fail on all non-Windows boxes. Note that I deliberately didn't convert this to unittest yet, because I expect it would be even harder to debug this on Tru64 after conversion. ........ r51235 | georg.brandl | 2006-08-12 10:32:02 +0200 (Sat, 12 Aug 2006) | 3 lines Repair logging test spew caused by rev. 51206. ........ r51236 | neal.norwitz | 2006-08-12 19:03:09 +0200 (Sat, 12 Aug 2006) | 8 lines Patch #1538606, Patch to fix __index__() clipping. I modified this patch some by fixing style, some error checking, and adding XXX comments. This patch requires review and some changes are to be expected. I'm checking in now to get the greatest possible review and establish a baseline for moving forward. I don't want this to hold up release if possible. ........ r51238 | neal.norwitz | 2006-08-12 20:44:06 +0200 (Sat, 12 Aug 2006) | 10 lines Fix a couple of bugs exposed by the new __index__ code. The 64-bit buildbots were failing due to inappropriate clipping of numbers larger than 2**31 with new-style classes. (typeobject.c) In reviewing the code for classic classes, there were 2 problems. Any negative value return could be returned. Always return -1 if there was an error. Also make the checks similar with the new-style classes. I believe this is correct for 32 and 64 bit boxes, including Windows64. Add a test of classic classes too. ........ r51240 | neal.norwitz | 2006-08-13 02:20:49 +0200 (Sun, 13 Aug 2006) | 1 line SF bug #1539336, distutils example code missing ........ r51245 | neal.norwitz | 2006-08-13 20:10:10 +0200 (Sun, 13 Aug 2006) | 6 lines Move/copy assert for tstate != NULL before first use. Verify that PyEval_Get{Globals,Locals} returned valid pointers. Klocwork 231-232 ........ r51246 | neal.norwitz | 2006-08-13 20:10:28 +0200 (Sun, 13 Aug 2006) | 5 lines Handle a whole lot of failures from PyString_FromInternedString(). Should fix most of Klocwork 234-272. ........ r51247 | neal.norwitz | 2006-08-13 20:10:47 +0200 (Sun, 13 Aug 2006) | 8 lines cpathname could be NULL if it was longer than MAXPATHLEN. Don't try to write the .pyc to NULL. Check results of PyList_GetItem() and PyModule_GetDict() are not NULL. Klocwork 282, 283, 285 ........ r51248 | neal.norwitz | 2006-08-13 20:11:08 +0200 (Sun, 13 Aug 2006) | 6 lines Fix segfault when doing string formatting on subclasses of long if __oct__, __hex__ don't return a string. Klocwork 308 ........ r51250 | neal.norwitz | 2006-08-13 20:11:27 +0200 (Sun, 13 Aug 2006) | 5 lines Check return result of PyModule_GetDict(). Fix a bunch of refleaks in the init of the module. This would only be found when running python -v. ........ r51251 | neal.norwitz | 2006-08-13 20:11:43 +0200 (Sun, 13 Aug 2006) | 5 lines Handle malloc and fopen failures more gracefully. Klocwork 180-181 ........ r51252 | neal.norwitz | 2006-08-13 20:12:03 +0200 (Sun, 13 Aug 2006) | 7 lines It's very unlikely, though possible that source is not a string. Verify that PyString_AsString() returns a valid pointer. (The problem can arise when zlib.decompress doesn't return a string.) Klocwork 346 ........ r51253 | neal.norwitz | 2006-08-13 20:12:26 +0200 (Sun, 13 Aug 2006) | 5 lines Handle failures from lookup. Klocwork 341-342 ........ r51254 | neal.norwitz | 2006-08-13 20:12:45 +0200 (Sun, 13 Aug 2006) | 6 lines Handle failure from PyModule_GetDict() (Klocwork 208). Fix a bunch of refleaks in the init of the module. This would only be found when running python -v. ........ r51255 | neal.norwitz | 2006-08-13 20:13:02 +0200 (Sun, 13 Aug 2006) | 4 lines Really address the issue of where to place the assert for leftblock. (Followup of Klocwork 274) ........ r51256 | neal.norwitz | 2006-08-13 20:13:36 +0200 (Sun, 13 Aug 2006) | 4 lines Handle malloc failure. Klocwork 281 ........ r51258 | neal.norwitz | 2006-08-13 20:40:39 +0200 (Sun, 13 Aug 2006) | 4 lines Handle alloca failures. Klocwork 225-228 ........ r51259 | neal.norwitz | 2006-08-13 20:41:15 +0200 (Sun, 13 Aug 2006) | 1 line Get rid of compiler warning ........ r51261 | neal.norwitz | 2006-08-14 02:51:15 +0200 (Mon, 14 Aug 2006) | 1 line Ignore pgen.exe and kill_python.exe for cygwin ........ r51262 | neal.norwitz | 2006-08-14 02:59:03 +0200 (Mon, 14 Aug 2006) | 4 lines Can't return NULL from a void function. If there is a memory error, about the best we can do is call PyErr_WriteUnraisable and go on. We won't be able to do the call below either, so verify delstr is valid. ........ r51263 | neal.norwitz | 2006-08-14 03:49:54 +0200 (Mon, 14 Aug 2006) | 1 line Update purify doc some. ........ r51264 | thomas.heller | 2006-08-14 09:13:05 +0200 (Mon, 14 Aug 2006) | 2 lines Remove unused, buggy test function. Fixes klockwork issue #207. ........ r51265 | thomas.heller | 2006-08-14 09:14:09 +0200 (Mon, 14 Aug 2006) | 2 lines Check for NULL return value from new_CArgObject(). Fixes klockwork issues #183, #184, #185. ........ r51266 | thomas.heller | 2006-08-14 09:50:14 +0200 (Mon, 14 Aug 2006) | 2 lines Check for NULL return value of GenericCData_new(). Fixes klockwork issues #188, #189. ........ r51274 | thomas.heller | 2006-08-14 12:02:24 +0200 (Mon, 14 Aug 2006) | 2 lines Revert the change that tries to zero out a closure's result storage area because the size if unknown in source/callproc.c. ........ r51276 | marc-andre.lemburg | 2006-08-14 12:55:19 +0200 (Mon, 14 Aug 2006) | 11 lines Slightly revised version of patch #1538956: Replace UnicodeDecodeErrors raised during == and != compares of Unicode and other objects with a new UnicodeWarning. All other comparisons continue to raise exceptions. Exceptions other than UnicodeDecodeErrors are also left untouched. ........ r51277 | thomas.heller | 2006-08-14 13:17:48 +0200 (Mon, 14 Aug 2006) | 13 lines Apply the patch #1532975 plus ideas from the patch #1533481. ctypes instances no longer have the internal and undocumented '_as_parameter_' attribute which was used to adapt them to foreign function calls; this mechanism is replaced by a function pointer in the type's stgdict. In the 'from_param' class methods, try the _as_parameter_ attribute if other conversions are not possible. This makes the documented _as_parameter_ mechanism work as intended. Change the ctypes version number to 1.0.1. ........ r51278 | marc-andre.lemburg | 2006-08-14 13:44:34 +0200 (Mon, 14 Aug 2006) | 3 lines Readd NEWS items that were accidentally removed by r51276. ........ r51279 | georg.brandl | 2006-08-14 14:36:06 +0200 (Mon, 14 Aug 2006) | 3 lines Improve markup in PyUnicode_RichCompare. ........ r51280 | marc-andre.lemburg | 2006-08-14 14:57:27 +0200 (Mon, 14 Aug 2006) | 3 lines Correct an accidentally removed previous patch. ........ r51281 | thomas.heller | 2006-08-14 18:17:41 +0200 (Mon, 14 Aug 2006) | 3 lines Patch #1536908: Add support for AMD64 / OpenBSD. Remove the -no-stack-protector compiler flag for OpenBSD as it has been reported to be unneeded. ........ r51282 | thomas.heller | 2006-08-14 18:20:04 +0200 (Mon, 14 Aug 2006) | 1 line News item for rev 51281. ........ r51283 | georg.brandl | 2006-08-14 22:25:39 +0200 (Mon, 14 Aug 2006) | 3 lines Fix refleak introduced in rev. 51248. ........ r51284 | georg.brandl | 2006-08-14 23:34:08 +0200 (Mon, 14 Aug 2006) | 5 lines Make tabnanny recognize IndentationErrors raised by tokenize. Add a test to test_inspect to make sure indented source is recognized correctly. (fixes #1224621) ........ r51285 | georg.brandl | 2006-08-14 23:42:55 +0200 (Mon, 14 Aug 2006) | 3 lines Patch #1535500: fix segfault in BZ2File.writelines and make sure it raises the correct exceptions. ........ r51287 | georg.brandl | 2006-08-14 23:45:32 +0200 (Mon, 14 Aug 2006) | 3 lines Add an additional test: BZ2File write methods should raise IOError when file is read-only. ........ r51289 | georg.brandl | 2006-08-14 23:55:28 +0200 (Mon, 14 Aug 2006) | 3 lines Patch #1536071: trace.py should now find the full module name of a file correctly even on Windows. ........ r51290 | georg.brandl | 2006-08-15 00:01:24 +0200 (Tue, 15 Aug 2006) | 3 lines Cookie.py shouldn't "bogusly" use string._idmap. ........ r51291 | georg.brandl | 2006-08-15 00:10:24 +0200 (Tue, 15 Aug 2006) | 3 lines Patch #1511317: don't crash on invalid hostname info ........ r51292 | tim.peters | 2006-08-15 02:25:04 +0200 (Tue, 15 Aug 2006) | 2 lines Whitespace normalization. ........ r51293 | neal.norwitz | 2006-08-15 06:14:57 +0200 (Tue, 15 Aug 2006) | 3 lines Georg fixed one of my bugs, so I'll repay him with 2 NEWS entries. Now we're even. :-) ........ r51295 | neal.norwitz | 2006-08-15 06:58:28 +0200 (Tue, 15 Aug 2006) | 8 lines Fix the test for SocketServer so it should pass on cygwin and not fail sporadically on other platforms. This is really a band-aid that doesn't fix the underlying issue in SocketServer. It's not clear if it's worth it to fix SocketServer, however, I opened a bug to track it: http://python.org/sf/1540386 ........ r51296 | neal.norwitz | 2006-08-15 06:59:30 +0200 (Tue, 15 Aug 2006) | 3 lines Update the docstring to use a version a little newer than 1999. This was taken from a Debian patch. Should we update the version for each release? ........ r51298 | neal.norwitz | 2006-08-15 08:29:03 +0200 (Tue, 15 Aug 2006) | 2 lines Subclasses of int/long are allowed to define an __index__. ........ r51300 | thomas.heller | 2006-08-15 15:07:21 +0200 (Tue, 15 Aug 2006) | 1 line Check for NULL return value from new_CArgObject calls. ........ r51303 | kurt.kaiser | 2006-08-16 05:15:26 +0200 (Wed, 16 Aug 2006) | 2 lines The 'with' statement is now a Code Context block opener ........ r51304 | anthony.baxter | 2006-08-16 05:42:26 +0200 (Wed, 16 Aug 2006) | 1 line preparing for 2.5c1 ........ r51305 | anthony.baxter | 2006-08-16 05:58:37 +0200 (Wed, 16 Aug 2006) | 1 line preparing for 2.5c1 - no, really this time ........ r51306 | kurt.kaiser | 2006-08-16 07:01:42 +0200 (Wed, 16 Aug 2006) | 9 lines Patch #1540892: site.py Quitter() class attempts to close sys.stdin before raising SystemExit, allowing IDLE to honor quit() and exit(). M Lib/site.py M Lib/idlelib/PyShell.py M Lib/idlelib/CREDITS.txt M Lib/idlelib/NEWS.txt M Misc/NEWS ........ r51307 | ka-ping.yee | 2006-08-16 09:02:50 +0200 (Wed, 16 Aug 2006) | 6 lines Update code and tests to support the 'bytes_le' attribute (for little-endian byte order on Windows), and to work around clocks with low resolution yielding duplicate UUIDs. Anthony Baxter has approved this change. ........ r51308 | kurt.kaiser | 2006-08-16 09:04:17 +0200 (Wed, 16 Aug 2006) | 2 lines Get quit() and exit() to work cleanly when not using subprocess. ........ r51309 | marc-andre.lemburg | 2006-08-16 10:13:26 +0200 (Wed, 16 Aug 2006) | 2 lines Revert to having static version numbers again. ........ r51310 | martin.v.loewis | 2006-08-16 14:55:10 +0200 (Wed, 16 Aug 2006) | 2 lines Build _hashlib on Windows. Build OpenSSL with masm assembler code. Fixes #1535502. ........ r51311 | thomas.heller | 2006-08-16 15:03:11 +0200 (Wed, 16 Aug 2006) | 6 lines Add commented assert statements to check that the result of PyObject_stgdict() and PyType_stgdict() calls are non-NULL before dereferencing the result. Hopefully this fixes what klocwork is complaining about. Fix a few other nits as well. ........ r51312 | anthony.baxter | 2006-08-16 15:08:25 +0200 (Wed, 16 Aug 2006) | 1 line news entry for 51307 ........ r51313 | andrew.kuchling | 2006-08-16 15:22:20 +0200 (Wed, 16 Aug 2006) | 1 line Add UnicodeWarning ........ r51314 | andrew.kuchling | 2006-08-16 15:41:52 +0200 (Wed, 16 Aug 2006) | 1 line Bump document version to 1.0; remove pystone paragraph ........ r51315 | andrew.kuchling | 2006-08-16 15:51:32 +0200 (Wed, 16 Aug 2006) | 1 line Link to docs; remove an XXX comment ........ r51316 | martin.v.loewis | 2006-08-16 15:58:51 +0200 (Wed, 16 Aug 2006) | 1 line Make cl build step compile-only (/c). Remove libs from source list. ........ r51317 | thomas.heller | 2006-08-16 16:07:44 +0200 (Wed, 16 Aug 2006) | 5 lines The __repr__ method of a NULL py_object does no longer raise an exception. Remove a stray '?' character from the exception text when the value is retrieved of such an object. Includes tests. ........ r51318 | andrew.kuchling | 2006-08-16 16:18:23 +0200 (Wed, 16 Aug 2006) | 1 line Update bug/patch counts ........ r51319 | andrew.kuchling | 2006-08-16 16:21:14 +0200 (Wed, 16 Aug 2006) | 1 line Wording/typo fixes ........ r51320 | thomas.heller | 2006-08-16 17:10:12 +0200 (Wed, 16 Aug 2006) | 9 lines Remove the special casing of Py_None when converting the return value of the Python part of a callback function to C. If it cannot be converted, call PyErr_WriteUnraisable with the exception we got. Before, arbitrary data has been passed to the calling C code in this case. (I'm not really sure the NEWS entry is understandable, but I cannot find better words) ........ r51321 | marc-andre.lemburg | 2006-08-16 18:11:01 +0200 (Wed, 16 Aug 2006) | 2 lines Add NEWS item mentioning the reverted distutils version number patch. ........ r51322 | fredrik.lundh | 2006-08-16 18:47:07 +0200 (Wed, 16 Aug 2006) | 5 lines SF#1534630 ignore data that arrives before the opening start tag ........ r51324 | andrew.kuchling | 2006-08-16 19:11:18 +0200 (Wed, 16 Aug 2006) | 1 line Grammar fix ........ r51328 | thomas.heller | 2006-08-16 20:02:11 +0200 (Wed, 16 Aug 2006) | 12 lines Tutorial: Clarify somewhat how parameters are passed to functions (especially explain what integer means). Correct the table - Python integers and longs can both be used. Further clarification to the table comparing ctypes types, Python types, and C types. Reference: Replace integer by C ``int`` where it makes sense. ........ r51329 | kurt.kaiser | 2006-08-16 23:45:59 +0200 (Wed, 16 Aug 2006) | 8 lines File menu hotkeys: there were three 'p' assignments. Reassign the 'Save Copy As' and 'Print' hotkeys to 'y' and 't'. Change the Shell menu hotkey from 's' to 'l'. M Bindings.py M PyShell.py M NEWS.txt ........ r51330 | neil.schemenauer | 2006-08-17 01:38:05 +0200 (Thu, 17 Aug 2006) | 3 lines Fix a bug in the ``compiler`` package that caused invalid code to be generated for generator expressions. ........ r51342 | martin.v.loewis | 2006-08-17 21:19:32 +0200 (Thu, 17 Aug 2006) | 3 lines Merge 51340 and 51341 from 2.5 branch: Leave tk build directory to restore original path. Invoke debug mk1mf.pl after running Configure. ........ r51354 | martin.v.loewis | 2006-08-18 05:47:18 +0200 (Fri, 18 Aug 2006) | 3 lines Bug #1541863: uuid.uuid1 failed to generate unique identifiers on systems with low clock resolution. ........ r51355 | neal.norwitz | 2006-08-18 05:57:54 +0200 (Fri, 18 Aug 2006) | 1 line Add template for 2.6 on HEAD ........ r51356 | neal.norwitz | 2006-08-18 06:01:38 +0200 (Fri, 18 Aug 2006) | 1 line More post-release wibble ........ r51357 | neal.norwitz | 2006-08-18 06:58:33 +0200 (Fri, 18 Aug 2006) | 1 line Try to get Windows bots working again ........ r51358 | neal.norwitz | 2006-08-18 07:10:00 +0200 (Fri, 18 Aug 2006) | 1 line Try to get Windows bots working again. Take 2 ........ r51359 | neal.norwitz | 2006-08-18 07:39:20 +0200 (Fri, 18 Aug 2006) | 1 line Try to get Unix bots install working again. ........ r51360 | neal.norwitz | 2006-08-18 07:41:46 +0200 (Fri, 18 Aug 2006) | 1 line Set version to 2.6a0, seems more consistent. ........ r51362 | neal.norwitz | 2006-08-18 08:14:52 +0200 (Fri, 18 Aug 2006) | 1 line More version wibble ........ r51364 | georg.brandl | 2006-08-18 09:27:59 +0200 (Fri, 18 Aug 2006) | 4 lines Bug #1541682: Fix example in the "Refcount details" API docs. Additionally, remove a faulty example showing PySequence_SetItem applied to a newly created list object and add notes that this isn't a good idea. ........ r51366 | anthony.baxter | 2006-08-18 09:29:02 +0200 (Fri, 18 Aug 2006) | 3 lines Updating IDLE's version number to match Python's (as per python-dev discussion). ........ r51367 | anthony.baxter | 2006-08-18 09:30:07 +0200 (Fri, 18 Aug 2006) | 1 line RPM specfile updates ........ r51368 | georg.brandl | 2006-08-18 09:35:47 +0200 (Fri, 18 Aug 2006) | 2 lines Typo in tp_clear docs. ........ r51378 | andrew.kuchling | 2006-08-18 15:57:13 +0200 (Fri, 18 Aug 2006) | 1 line Minor edits ........ r51379 | thomas.heller | 2006-08-18 16:38:46 +0200 (Fri, 18 Aug 2006) | 6 lines Add asserts to check for 'impossible' NULL values, with comments. In one place where I'n not 1000% sure about the non-NULL, raise a RuntimeError for safety. This should fix the klocwork issues that Neal sent me. If so, it should be applied to the release25-maint branch also. ........ r51400 | neal.norwitz | 2006-08-19 06:22:33 +0200 (Sat, 19 Aug 2006) | 5 lines Move initialization of interned strings to before allocating the object so we don't leak op. (Fixes an earlier patch to this code) Klockwork #350 ........ r51401 | neal.norwitz | 2006-08-19 06:23:04 +0200 (Sat, 19 Aug 2006) | 4 lines Move assert to after NULL check, otherwise we deref NULL in the assert. Klocwork #307 ........ r51402 | neal.norwitz | 2006-08-19 06:25:29 +0200 (Sat, 19 Aug 2006) | 2 lines SF #1542693: Remove semi-colon at end of PyImport_ImportModuleEx macro ........ r51403 | neal.norwitz | 2006-08-19 06:28:55 +0200 (Sat, 19 Aug 2006) | 6 lines Move initialization to after the asserts for non-NULL values. Klocwork 286-287. (I'm not backporting this, but if someone wants to, feel free.) ........ r51404 | neal.norwitz | 2006-08-19 06:52:03 +0200 (Sat, 19 Aug 2006) | 6 lines Handle PyString_FromInternedString() failing (unlikely, but possible). Klocwork #325 (I'm not backporting this, but if someone wants to, feel free.) ........ r51416 | georg.brandl | 2006-08-20 15:15:39 +0200 (Sun, 20 Aug 2006) | 2 lines Patch #1542948: fix urllib2 header casing issue. With new test. ........ r51428 | jeremy.hylton | 2006-08-21 18:19:37 +0200 (Mon, 21 Aug 2006) | 3 lines Move peephole optimizer to separate file. ........ r51429 | jeremy.hylton | 2006-08-21 18:20:29 +0200 (Mon, 21 Aug 2006) | 2 lines Move peephole optimizer to separate file. (Forgot .h in previous checkin.) ........ r51432 | neal.norwitz | 2006-08-21 19:59:46 +0200 (Mon, 21 Aug 2006) | 5 lines Fix bug #1543303, tarfile adds padding that breaks gunzip. Patch # 1543897. Will backport to 2.5 ........ r51433 | neal.norwitz | 2006-08-21 20:01:30 +0200 (Mon, 21 Aug 2006) | 2 lines Add assert to make Klocwork happy (#276) ........ --- __init__.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/__init__.py b/__init__.py index 9c60e546..21d34c76 100644 --- a/__init__.py +++ b/__init__.py @@ -12,6 +12,12 @@ used from a setup script as __revision__ = "$Id$" -import sys -__version__ = "%d.%d.%d" % sys.version_info[:3] -del sys +# Distutils version +# +# Please coordinate with Marc-Andre Lemburg when adding +# new features to distutils that would warrant bumping the version number. +# +# In general, major and minor version should loosely follow the Python +# version number the distutils code was shipped with. +# +__version__ = "2.5.0" -- cgit v1.2.1 From 6c4f3892adad5878f43e5246f8794c3de5b685e1 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 24 Aug 2006 00:41:19 +0000 Subject: Restructure comparison dramatically. There is no longer a default *ordering* between objects; there is only a default equality test (defined by an object being equal to itself only). Read the comment in object.c. The current implementation never uses a three-way comparison to compute a rich comparison, but it does use a rich comparison to compute a three-way comparison. I'm not quite done ripping out all the calls to PyObject_Compare/Cmp, or replacing tp_compare implementations with tp_richcompare implementations; but much of that has happened (to make most unit tests pass). The following tests still fail, because I need help deciding or understanding: test_codeop -- depends on comparing code objects test_datetime -- need Tim Peters' opinion test_marshal -- depends on comparing code objects test_mutants -- need help understanding it The problem with test_codeop and test_marshal is this: these tests compare two different code objects and expect them to be equal. Is that still a feature we'd like to support? I've temporarily removed the comparison and hash code from code objects, so they use the default (equality by pointer only) comparison. For the other two tests, run them to see for yourself. (There may be more failing test with "-u all".) A general problem with getting lots of these tests to pass is the reality that for object types that have a natural total ordering, implementing __cmp__ is much more convenient than implementing __eq__, __ne__, __lt__, and so on. Should we go back to allowing __cmp__ to provide a total ordering? Should we provide some other way to implement rich comparison with a single method override? Alex proposed a __key__() method; I've considered a __richcmp__() method. Or perhaps __cmp__() just shouldn't be killed off... --- version.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/version.py b/version.py index 71a56147..2cd36365 100644 --- a/version.py +++ b/version.py @@ -32,7 +32,8 @@ from types import StringType class Version: """Abstract base class for version numbering classes. Just provides constructor (__init__) and reproducer (__repr__), because those - seem to be the same for all version numbering classes. + seem to be the same for all version numbering classes; and route + rich comparisons to __cmp__. """ def __init__ (self, vstring=None): @@ -42,6 +43,42 @@ class Version: def __repr__ (self): return "%s ('%s')" % (self.__class__.__name__, str(self)) + def __eq__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c == 0 + + def __ne__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c != 0 + + def __lt__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c < 0 + + def __le__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c <= 0 + + def __gt__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c > 0 + + def __ge__(self, other): + c = self.__cmp__(other) + if c is NotImplemented: + return c + return c >= 0 + # Interface for version-number classes -- must be implemented # by the following classes (the concrete ones -- Version should -- cgit v1.2.1 From f4e988990abc4e2e3408af5bf07bb08203347252 Mon Sep 17 00:00:00 2001 From: Alex Martelli Date: Thu, 24 Aug 2006 02:58:11 +0000 Subject: Anna Ravenscroft identified many occurrences of "file" used to open a file in the stdlib and changed each of them to use "open" instead. At this time there are no other known occurrences that can be safely changed (in Lib and all subdirectories thereof). --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 0ea4bb73..96923bdc 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -354,7 +354,7 @@ def _init_posix(): # load the installed pyconfig.h: try: filename = get_config_h_filename() - parse_config_h(file(filename), g) + parse_config_h(open(filename), g) except IOError, msg: my_msg = "invalid Python installation: unable to open %s" % filename if hasattr(msg, "strerror"): -- cgit v1.2.1 From e9ab4dbfd784248dbcfc3332f76526872c8e114d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 4 Oct 2006 15:25:28 +0000 Subject: Add MSVC8 project files to create wininst-8.exe. --- command/wininst-8.exe | Bin 0 -> 61440 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 command/wininst-8.exe diff --git a/command/wininst-8.exe b/command/wininst-8.exe new file mode 100644 index 00000000..7403bfab Binary files /dev/null and b/command/wininst-8.exe differ -- cgit v1.2.1 From a731ab145f5bca79f452269c2dfc52ab0ad6f6de Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 6 Oct 2006 13:18:26 +0000 Subject: [Bug #1545341] Allow 'classifier' parameter to be a tuple as well as a list. Will backport. --- command/register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/register.py b/command/register.py index dec9aa2b..5fcc8d27 100644 --- a/command/register.py +++ b/command/register.py @@ -251,7 +251,7 @@ Your selection [default 1]: ''', body = StringIO.StringIO() for key, value in data.items(): # handle multiple entries for the same name - if type(value) != type([]): + if type(value) not in (type([]), type( () )): value = [value] for value in value: value = unicode(value).encode("utf-8") -- cgit v1.2.1 From 942bf846fffbdd0aa873115756281bd91d155dea Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 8 Oct 2006 17:49:52 +0000 Subject: MacOSX: distutils changes the values of BASECFLAGS and LDFLAGS when using a universal build of python on OSX 10.3 to ensure that those flags can be used to compile code (the universal build uses compiler flags that aren't supported on 10.3). This patches gives the same treatment to CFLAGS, PY_CFLAGS and BLDSHARED. --- sysconfig.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index c3b1e4ec..0cfafab9 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -509,7 +509,10 @@ def get_config_vars(*args): # are in CFLAGS or LDFLAGS and remove them if they are. # This is needed when building extensions on a 10.3 system # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS'): + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): flags = _config_vars[key] flags = re.sub('-arch\s+\w+\s', ' ', flags) flags = re.sub('-isysroot [^ \t]*', ' ', flags) -- cgit v1.2.1 From ab8b0e572d34555fd27810253833db03dd2efb0b Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 8 Oct 2006 17:50:26 +0000 Subject: Backport of r52233 --- sysconfig.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index c3b1e4ec..bf52a274 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -509,7 +509,11 @@ def get_config_vars(*args): # are in CFLAGS or LDFLAGS and remove them if they are. # This is needed when building extensions on a 10.3 system # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS'): + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + flags = _config_vars[key] flags = re.sub('-arch\s+\w+\s', ' ', flags) flags = re.sub('-isysroot [^ \t]*', ' ', flags) -- cgit v1.2.1 From e3d14a32164b47b216074647f6e3a05ede126198 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 8 Oct 2006 17:51:46 +0000 Subject: MacOSX: The universal build requires that users have the MacOSX10.4u SDK installed to build extensions. This patch makes distutils emit a warning when the compiler should use an SDK but that SDK is not installed, hopefully reducing some confusion. --- unixccompiler.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/unixccompiler.py b/unixccompiler.py index 6cd14f77..d1fd1d95 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -82,6 +82,22 @@ def _darwin_compiler_fixup(compiler_so, cc_args): except ValueError: pass + # Check if the SDK that is used during compilation actually exists, + # the universal build requires the usage of a universal SDK and not all + # users have that installed by default. + sysroot = None + if '-isysroot' in cc_args: + idx = cc_args.index('-isysroot') + sysroot = cc_args[idx+1] + elif '-isysroot' in compiler_so: + idx = compiler_so.index('-isysroot') + sysroot = compiler_so[idx+1] + + if sysroot and not os.path.isdir(sysroot): + log.warn("Compiling with an SDK that doesn't seem to exist: %s", + sysroot) + log.warn("Please check your Xcode installation") + return compiler_so class UnixCCompiler(CCompiler): -- cgit v1.2.1 From 364819cca89dfd6c2bfe1c7cdac42073baa01ba3 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 8 Oct 2006 17:52:37 +0000 Subject: Backport of r52236 --- unixccompiler.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/unixccompiler.py b/unixccompiler.py index 6cd14f77..d1fd1d95 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -82,6 +82,22 @@ def _darwin_compiler_fixup(compiler_so, cc_args): except ValueError: pass + # Check if the SDK that is used during compilation actually exists, + # the universal build requires the usage of a universal SDK and not all + # users have that installed by default. + sysroot = None + if '-isysroot' in cc_args: + idx = cc_args.index('-isysroot') + sysroot = cc_args[idx+1] + elif '-isysroot' in compiler_so: + idx = compiler_so.index('-isysroot') + sysroot = compiler_so[idx+1] + + if sysroot and not os.path.isdir(sysroot): + log.warn("Compiling with an SDK that doesn't seem to exist: %s", + sysroot) + log.warn("Please check your Xcode installation") + return compiler_so class UnixCCompiler(CCompiler): -- cgit v1.2.1 From 299c49adb264eb716796a042441fa013b094e2c2 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 9 Oct 2006 17:13:26 +0000 Subject: [Bug #1545341] Let the 'classifiers' parameter be a tuple as well as a list. --- command/register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/register.py b/command/register.py index dec9aa2b..5fcc8d27 100644 --- a/command/register.py +++ b/command/register.py @@ -251,7 +251,7 @@ Your selection [default 1]: ''', body = StringIO.StringIO() for key, value in data.items(): # handle multiple entries for the same name - if type(value) != type([]): + if type(value) not in (type([]), type( () )): value = [value] for value in value: value = unicode(value).encode("utf-8") -- cgit v1.2.1 From 3624acc8638f40e03e87655ec596501dece3042f Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Wed, 18 Oct 2006 05:09:12 +0000 Subject: Whitespace normalization. --- unixccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index d1fd1d95..75e8a531 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -82,7 +82,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): except ValueError: pass - # Check if the SDK that is used during compilation actually exists, + # Check if the SDK that is used during compilation actually exists, # the universal build requires the usage of a universal SDK and not all # users have that installed by default. sysroot = None -- cgit v1.2.1 From a31ffb0ff77ad8260c85cab5c713d20688e2dc78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 4 Nov 2006 18:14:06 +0000 Subject: - Patch #1060577: Extract list of RPM files from spec file in bdist_rpm Will backport to 2.5. --- command/bdist_rpm.py | 60 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 5b099658..03ef070c 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -337,37 +337,47 @@ class bdist_rpm (Command): if not self.keep_temp: rpm_cmd.append('--clean') rpm_cmd.append(spec_path) + # Determine the binary rpm names that should be built out of this spec + # file + # Note that some of these may not be really built (if the file + # list is empty) + nvr_string = "%{name}-%{version}-%{release}" + src_rpm = nvr_string + ".src.rpm" + non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" + q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % ( + src_rpm, non_src_rpm, spec_path) + + out = os.popen(q_cmd) + binary_rpms = [] + source_rpm = None + while 1: + line = out.readline() + if not line: + break + l = string.split(string.strip(line)) + assert(len(l) == 2) + binary_rpms.append(l[1]) + # The source rpm is named after the first entry in the spec file + if source_rpm is None: + source_rpm = l[0] + + status = out.close() + if status: + raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + self.spawn(rpm_cmd) - # XXX this is a nasty hack -- we really should have a proper way to - # find out the names of the RPM files created; also, this assumes - # that RPM creates exactly one source and one binary RPM. if not self.dry_run: if not self.binary_only: - srpms = glob.glob(os.path.join(rpm_dir['SRPMS'], "*.rpm")) - assert len(srpms) == 1, \ - "unexpected number of SRPM files found: %s" % srpms - dist_file = ('bdist_rpm', 'any', - self._dist_path(srpms[0])) - self.distribution.dist_files.append(dist_file) - self.move_file(srpms[0], self.dist_dir) + srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) + assert(os.path.exists(srpm)) + self.move_file(srpm, self.dist_dir) if not self.source_only: - rpms = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*.rpm")) - debuginfo = glob.glob(os.path.join(rpm_dir['RPMS'], - "*/*debuginfo*.rpm")) - if debuginfo: - rpms.remove(debuginfo[0]) - assert len(rpms) == 1, \ - "unexpected number of RPM files found: %s" % rpms - dist_file = ('bdist_rpm', get_python_version(), - self._dist_path(rpms[0])) - self.distribution.dist_files.append(dist_file) - self.move_file(rpms[0], self.dist_dir) - if debuginfo: - dist_file = ('bdist_rpm', get_python_version(), - self._dist_path(debuginfo[0])) - self.move_file(debuginfo[0], self.dist_dir) + for rpm in binary_rpms: + rpm = os.path.join(rpm_dir['RPMS'], rpm) + if os.path.exists(rpm): + self.move_file(rpm, self.dist_dir) # run() def _dist_path(self, path): -- cgit v1.2.1 From cf89211da249d28e2aa80ebb440380672878d59e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 4 Nov 2006 18:14:22 +0000 Subject: Patch #1060577: Extract list of RPM files from spec file in bdist_rpm --- command/bdist_rpm.py | 60 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 5b099658..03ef070c 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -337,37 +337,47 @@ class bdist_rpm (Command): if not self.keep_temp: rpm_cmd.append('--clean') rpm_cmd.append(spec_path) + # Determine the binary rpm names that should be built out of this spec + # file + # Note that some of these may not be really built (if the file + # list is empty) + nvr_string = "%{name}-%{version}-%{release}" + src_rpm = nvr_string + ".src.rpm" + non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" + q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % ( + src_rpm, non_src_rpm, spec_path) + + out = os.popen(q_cmd) + binary_rpms = [] + source_rpm = None + while 1: + line = out.readline() + if not line: + break + l = string.split(string.strip(line)) + assert(len(l) == 2) + binary_rpms.append(l[1]) + # The source rpm is named after the first entry in the spec file + if source_rpm is None: + source_rpm = l[0] + + status = out.close() + if status: + raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + self.spawn(rpm_cmd) - # XXX this is a nasty hack -- we really should have a proper way to - # find out the names of the RPM files created; also, this assumes - # that RPM creates exactly one source and one binary RPM. if not self.dry_run: if not self.binary_only: - srpms = glob.glob(os.path.join(rpm_dir['SRPMS'], "*.rpm")) - assert len(srpms) == 1, \ - "unexpected number of SRPM files found: %s" % srpms - dist_file = ('bdist_rpm', 'any', - self._dist_path(srpms[0])) - self.distribution.dist_files.append(dist_file) - self.move_file(srpms[0], self.dist_dir) + srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) + assert(os.path.exists(srpm)) + self.move_file(srpm, self.dist_dir) if not self.source_only: - rpms = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*.rpm")) - debuginfo = glob.glob(os.path.join(rpm_dir['RPMS'], - "*/*debuginfo*.rpm")) - if debuginfo: - rpms.remove(debuginfo[0]) - assert len(rpms) == 1, \ - "unexpected number of RPM files found: %s" % rpms - dist_file = ('bdist_rpm', get_python_version(), - self._dist_path(rpms[0])) - self.distribution.dist_files.append(dist_file) - self.move_file(rpms[0], self.dist_dir) - if debuginfo: - dist_file = ('bdist_rpm', get_python_version(), - self._dist_path(debuginfo[0])) - self.move_file(debuginfo[0], self.dist_dir) + for rpm in binary_rpms: + rpm = os.path.join(rpm_dir['RPMS'], rpm) + if os.path.exists(rpm): + self.move_file(rpm, self.dist_dir) # run() def _dist_path(self, path): -- cgit v1.2.1 From 5a83e9120fb4a858150b3ddfe7d7a9c7b7e9f7d6 Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Fri, 10 Nov 2006 00:33:36 +0000 Subject: Fix SF#1566719: not creating site-packages (or other target directory) when installing .egg-info for a project that contains no modules or packages, while using --root (as in bdist_rpm). --- command/install_egg_info.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/install_egg_info.py b/command/install_egg_info.py index c31ac296..c8880310 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -35,6 +35,9 @@ class install_egg_info(Command): dir_util.remove_tree(target, dry_run=self.dry_run) elif os.path.exists(target): self.execute(os.unlink,(self.target,),"Removing "+target) + elif not os.path.isdir(self.install_dir): + self.execute(os.makedirs, (self.install_dir,), + "Creating "+self.install_dir) log.info("Writing %s", target) if not self.dry_run: f = open(target, 'w') -- cgit v1.2.1 From cff70f8bd97d2c1072fcead0aab6a6ea16b8f473 Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Fri, 10 Nov 2006 17:13:29 +0000 Subject: Fix SF#1566719: not creating site-packages (or other target directory) when installing .egg-info for a project that contains no modules or packages, while using --root (as in bdist_rpm). (Backport from trunk) --- command/install_egg_info.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/install_egg_info.py b/command/install_egg_info.py index c31ac296..c8880310 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -35,6 +35,9 @@ class install_egg_info(Command): dir_util.remove_tree(target, dry_run=self.dry_run) elif os.path.exists(target): self.execute(os.unlink,(self.target,),"Removing "+target) + elif not os.path.isdir(self.install_dir): + self.execute(os.makedirs, (self.install_dir,), + "Creating "+self.install_dir) log.info("Writing %s", target) if not self.dry_run: f = open(target, 'w') -- cgit v1.2.1 From 1b618f36541a2feff0f6a9614b47605e3ca8add8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 12 Nov 2006 18:56:03 +0000 Subject: Patch #1360200: Use unmangled_version RPM spec field to deal with file name mangling. Will backport to 2.5. --- command/bdist_rpm.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 03ef070c..6f0e0d88 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -391,6 +391,7 @@ class bdist_rpm (Command): spec_file = [ '%define name ' + self.distribution.get_name(), '%define version ' + self.distribution.get_version().replace('-','_'), + '%define unmangled_version ' + self.distribution.get_version(), '%define release ' + self.release.replace('-','_'), '', 'Summary: ' + self.distribution.get_description(), @@ -412,9 +413,9 @@ class bdist_rpm (Command): # but only after it has run: and we create the spec file before # running "sdist", in case of --spec-only. if self.use_bzip2: - spec_file.append('Source0: %{name}-%{version}.tar.bz2') + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') else: - spec_file.append('Source0: %{name}-%{version}.tar.gz') + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') spec_file.extend([ 'License: ' + self.distribution.get_license(), @@ -489,7 +490,7 @@ class bdist_rpm (Command): # are just text that we drop in as-is. Hmmm. script_options = [ - ('prep', 'prep_script', "%setup"), + ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), ('build', 'build_script', def_build), ('install', 'install_script', ("%s install " -- cgit v1.2.1 From e63244103d3f57f2539f575704fe9f7fdad77aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 12 Nov 2006 18:56:18 +0000 Subject: Patch #1360200: Use unmangled_version RPM spec field to deal with file name mangling. --- command/bdist_rpm.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 03ef070c..6f0e0d88 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -391,6 +391,7 @@ class bdist_rpm (Command): spec_file = [ '%define name ' + self.distribution.get_name(), '%define version ' + self.distribution.get_version().replace('-','_'), + '%define unmangled_version ' + self.distribution.get_version(), '%define release ' + self.release.replace('-','_'), '', 'Summary: ' + self.distribution.get_description(), @@ -412,9 +413,9 @@ class bdist_rpm (Command): # but only after it has run: and we create the spec file before # running "sdist", in case of --spec-only. if self.use_bzip2: - spec_file.append('Source0: %{name}-%{version}.tar.bz2') + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') else: - spec_file.append('Source0: %{name}-%{version}.tar.gz') + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') spec_file.extend([ 'License: ' + self.distribution.get_license(), @@ -489,7 +490,7 @@ class bdist_rpm (Command): # are just text that we drop in as-is. Hmmm. script_options = [ - ('prep', 'prep_script', "%setup"), + ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), ('build', 'build_script', def_build), ('install', 'install_script', ("%s install " -- cgit v1.2.1 From 6188d82d9f938c426b361a467b459d7a0ff21db5 Mon Sep 17 00:00:00 2001 From: Matthias Klose Date: Sun, 3 Dec 2006 17:13:54 +0000 Subject: - Fix build failure on kfreebsd and on the hurd. --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 00f8a6b9..24138293 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos' or \ - (sys.platform.startswith('linux') and + ((sys.platform.startswith('linux') or sys.platform.startswith('gnu')) and sysconfig.get_config_var('Py_ENABLE_SHARED')): if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions -- cgit v1.2.1 From f2107aba5992adb79db1e79607b500541270f89b Mon Sep 17 00:00:00 2001 From: Matthias Klose Date: Sun, 3 Dec 2006 17:16:41 +0000 Subject: - Fix build failure on kfreebsd and on the hurd. --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 00f8a6b9..24138293 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos' or \ - (sys.platform.startswith('linux') and + ((sys.platform.startswith('linux') or sys.platform.startswith('gnu')) and sysconfig.get_config_var('Py_ENABLE_SHARED')): if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions -- cgit v1.2.1 From 6c3e3687e30a200343363312dc6ee61e0dce9d8b Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Wed, 13 Dec 2006 04:49:30 +0000 Subject: Four months of trunk changes (including a few releases...) Merged revisions 51434-53004 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r51434 | neal.norwitz | 2006-08-21 20:20:10 +0200 (Mon, 21 Aug 2006) | 1 line Fix a couple of ssize-t issues reported by Alexander Belopolsky on python-dev ........ r51439 | neal.norwitz | 2006-08-21 21:47:08 +0200 (Mon, 21 Aug 2006) | 6 lines Patch #1542451: disallow continue anywhere under a finally I'm undecided if this should be backported to 2.5 or 2.5.1. Armin suggested to wait (I'm of the same opinion). Thomas W thinks it's fine to go in 2.5. ........ r51443 | neal.norwitz | 2006-08-21 22:16:24 +0200 (Mon, 21 Aug 2006) | 4 lines Handle a few more error conditions. Klocwork 301 and 302. Will backport. ........ r51450 | neal.norwitz | 2006-08-22 00:21:19 +0200 (Tue, 22 Aug 2006) | 5 lines Patch #1541585: fix buffer overrun when performing repr() on a unicode string in a build with wide unicode (UCS-4) support. This code could be improved, so add an XXX comment. ........ r51456 | neal.norwitz | 2006-08-22 01:44:48 +0200 (Tue, 22 Aug 2006) | 1 line Try to get the windows bots working again with the new peephole.c ........ r51461 | anthony.baxter | 2006-08-22 09:36:59 +0200 (Tue, 22 Aug 2006) | 1 line patch for documentation for recent uuid changes (from ping) ........ r51473 | neal.norwitz | 2006-08-22 15:56:56 +0200 (Tue, 22 Aug 2006) | 1 line Alexander Belopolsky pointed out that pos is a size_t ........ r51489 | jeremy.hylton | 2006-08-22 22:46:00 +0200 (Tue, 22 Aug 2006) | 2 lines Expose column offset information in parse trees. ........ r51497 | andrew.kuchling | 2006-08-23 01:13:43 +0200 (Wed, 23 Aug 2006) | 1 line Move functional howto into trunk ........ r51515 | jeremy.hylton | 2006-08-23 20:37:43 +0200 (Wed, 23 Aug 2006) | 2 lines Baby steps towards better tests for tokenize ........ r51525 | alex.martelli | 2006-08-23 22:42:02 +0200 (Wed, 23 Aug 2006) | 6 lines x**2 should about equal x*x (including for a float x such that the result is inf) but didn't; added a test to test_float to verify that, and ignored the ERANGE value for errno in the pow operation to make the new test pass (with help from Marilyn Davis at the Google Python Sprint -- thanks!). ........ r51526 | jeremy.hylton | 2006-08-23 23:14:03 +0200 (Wed, 23 Aug 2006) | 20 lines Bug fixes large and small for tokenize. Small: Always generate a NL or NEWLINE token following a COMMENT token. The old code did not generate an NL token if the comment was on a line by itself. Large: The output of untokenize() will now match the input exactly if it is passed the full token sequence. The old, crufty output is still generated if a limited input sequence is provided, where limited means that it does not include position information for tokens. Remaining bug: There is no CONTINUATION token (\) so there is no way for untokenize() to handle such code. Also, expanded the number of doctests in hopes of eventually removing the old-style tests that compare against a golden file. Bug fix candidate for Python 2.5.1. (Sigh.) ........ r51527 | jeremy.hylton | 2006-08-23 23:26:46 +0200 (Wed, 23 Aug 2006) | 5 lines Replace dead code with an assert. Now that COMMENT tokens are reliably followed by NL or NEWLINE, there is never a need to add extra newlines in untokenize. ........ r51530 | alex.martelli | 2006-08-24 00:17:59 +0200 (Thu, 24 Aug 2006) | 7 lines Reverting the patch that tried to fix the issue whereby x**2 raises OverflowError while x*x succeeds and produces infinity; apparently these inconsistencies cannot be fixed across ``all'' platforms and there's a widespread feeling that therefore ``every'' platform should keep suffering forevermore. Ah well. ........ r51565 | thomas.wouters | 2006-08-24 20:40:20 +0200 (Thu, 24 Aug 2006) | 6 lines Fix SF bug #1545837: array.array borks on deepcopy. array.__deepcopy__() needs to take an argument, even if it doesn't actually use it. Will backport to 2.5 and 2.4 (if applicable.) ........ r51580 | martin.v.loewis | 2006-08-25 02:03:34 +0200 (Fri, 25 Aug 2006) | 3 lines Patch #1545507: Exclude ctypes package in Win64 MSI file. Will backport to 2.5. ........ r51589 | neal.norwitz | 2006-08-25 03:52:49 +0200 (Fri, 25 Aug 2006) | 1 line importing types is not necessary if we use isinstance ........ r51604 | thomas.heller | 2006-08-25 09:27:33 +0200 (Fri, 25 Aug 2006) | 3 lines Port _ctypes.pyd to win64 on AMD64. ........ r51605 | thomas.heller | 2006-08-25 09:34:51 +0200 (Fri, 25 Aug 2006) | 3 lines Add missing file for _ctypes.pyd port to win64 on AMD64. ........ r51606 | thomas.heller | 2006-08-25 11:26:33 +0200 (Fri, 25 Aug 2006) | 6 lines Build _ctypes.pyd for win AMD64 into the MSVC project file. Since MSVC doesn't know about .asm files, a helper batch file is needed to find ml64.exe in predefined locations. The helper script hardcodes the path to the MS Platform SDK. ........ r51608 | armin.rigo | 2006-08-25 14:44:28 +0200 (Fri, 25 Aug 2006) | 4 lines The regular expression engine in '_sre' can segfault when interpreting bogus bytecode. It is unclear whether this is a real bug or a "won't fix" case like bogus_code_obj.py. ........ r51617 | tim.peters | 2006-08-26 00:05:39 +0200 (Sat, 26 Aug 2006) | 2 lines Whitespace normalization. ........ r51618 | tim.peters | 2006-08-26 00:06:44 +0200 (Sat, 26 Aug 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r51619 | tim.peters | 2006-08-26 00:26:21 +0200 (Sat, 26 Aug 2006) | 3 lines A new test here relied on preserving invisible trailing whitespace in expected output. Stop that. ........ r51624 | jack.diederich | 2006-08-26 20:42:06 +0200 (Sat, 26 Aug 2006) | 4 lines - Move functions common to all path modules into genericpath.py and have the OS speicifc path modules import them. - Have os2emxpath import common functions fron ntpath instead of using copies ........ r51642 | neal.norwitz | 2006-08-29 07:40:58 +0200 (Tue, 29 Aug 2006) | 1 line Fix a couple of typos. ........ r51647 | marc-andre.lemburg | 2006-08-29 12:34:12 +0200 (Tue, 29 Aug 2006) | 5 lines Fix a buglet in the error reporting (SF bug report #1546372). This should probably go into Python 2.5 or 2.5.1 as well. ........ r51663 | armin.rigo | 2006-08-31 10:51:06 +0200 (Thu, 31 Aug 2006) | 3 lines Doc fix: hashlib objects don't always return a digest of 16 bytes. Backport candidate for 2.5. ........ r51664 | nick.coghlan | 2006-08-31 14:00:43 +0200 (Thu, 31 Aug 2006) | 1 line Fix the wrongheaded implementation of context management in the decimal module and add unit tests. (python-dev discussion is ongoing regarding what we do about Python 2.5) ........ r51665 | nick.coghlan | 2006-08-31 14:51:25 +0200 (Thu, 31 Aug 2006) | 1 line Remove the old decimal context management tests from test_contextlib (guess who didn't run the test suite before committing...) ........ r51669 | brett.cannon | 2006-08-31 20:54:26 +0200 (Thu, 31 Aug 2006) | 4 lines Make sure memory is properly cleaned up in file_init. Backport candidate. ........ r51671 | brett.cannon | 2006-08-31 23:47:52 +0200 (Thu, 31 Aug 2006) | 2 lines Fix comment about indentation level in C files. ........ r51674 | brett.cannon | 2006-09-01 00:42:37 +0200 (Fri, 01 Sep 2006) | 3 lines Have pre-existing C files use 8 spaces indents (to match old PEP 7 style), but have all new files use 4 spaces (to match current PEP 7 style). ........ r51676 | fred.drake | 2006-09-01 05:57:19 +0200 (Fri, 01 Sep 2006) | 3 lines - SF patch #1550263: Enhance and correct unittest docs - various minor cleanups for improved consistency ........ r51677 | georg.brandl | 2006-09-02 00:30:52 +0200 (Sat, 02 Sep 2006) | 2 lines evalfile() should be execfile(). ........ r51681 | neal.norwitz | 2006-09-02 04:43:17 +0200 (Sat, 02 Sep 2006) | 1 line SF #1547931, fix typo (missing and). Will backport to 2.5 ........ r51683 | neal.norwitz | 2006-09-02 04:50:35 +0200 (Sat, 02 Sep 2006) | 1 line Bug #1548092: fix curses.tparm seg fault on invalid input. Needs backport to 2.5.1 and earlier. ........ r51684 | neal.norwitz | 2006-09-02 04:58:13 +0200 (Sat, 02 Sep 2006) | 4 lines Bug #1550714: fix SystemError from itertools.tee on negative value for n. Needs backport to 2.5.1 and earlier. ........ r51685 | nick.coghlan | 2006-09-02 05:54:17 +0200 (Sat, 02 Sep 2006) | 1 line Make decimal.ContextManager a private implementation detail of decimal.localcontext() ........ r51686 | nick.coghlan | 2006-09-02 06:04:18 +0200 (Sat, 02 Sep 2006) | 1 line Further corrections to the decimal module context management documentation ........ r51688 | raymond.hettinger | 2006-09-02 19:07:23 +0200 (Sat, 02 Sep 2006) | 1 line Fix documentation nits for decimal context managers. ........ r51690 | neal.norwitz | 2006-09-02 20:51:34 +0200 (Sat, 02 Sep 2006) | 1 line Add missing word in comment ........ r51691 | neal.norwitz | 2006-09-02 21:40:19 +0200 (Sat, 02 Sep 2006) | 7 lines Hmm, this test has failed at least twice recently on the OpenBSD and Debian sparc buildbots. Since this goes through a lot of tests and hits the disk a lot it could be slow (especially if NFS is involved). I'm not sure if that's the problem, but printing periodic msgs shouldn't hurt. The code was stolen from test_compiler. ........ r51693 | nick.coghlan | 2006-09-03 03:02:00 +0200 (Sun, 03 Sep 2006) | 1 line Fix final documentation nits before backporting decimal module fixes to 2.5 ........ r51694 | nick.coghlan | 2006-09-03 03:06:07 +0200 (Sun, 03 Sep 2006) | 1 line Typo fix for decimal docs ........ r51697 | nick.coghlan | 2006-09-03 03:20:46 +0200 (Sun, 03 Sep 2006) | 1 line NEWS entry on trunk for decimal module changes ........ r51704 | raymond.hettinger | 2006-09-04 17:32:48 +0200 (Mon, 04 Sep 2006) | 1 line Fix endcase for str.rpartition() ........ r51716 | tim.peters | 2006-09-05 04:18:09 +0200 (Tue, 05 Sep 2006) | 12 lines "Conceptual" merge of rev 51711 from the 2.5 branch. i_divmod(): As discussed on Python-Dev, changed the overflow checking to live happily with recent gcc optimizations that assume signed integer arithmetic never overflows. This differs from the corresponding change on the 2.5 and 2.4 branches, using a less obscure approach, but one that /may/ tickle platform idiocies in their definitions of LONG_MIN. The 2.4 + 2.5 change avoided introducing a dependence on LONG_MIN, at the cost of substantially goofier code. ........ r51717 | tim.peters | 2006-09-05 04:21:19 +0200 (Tue, 05 Sep 2006) | 2 lines Whitespace normalization. ........ r51719 | tim.peters | 2006-09-05 04:22:17 +0200 (Tue, 05 Sep 2006) | 2 lines Add missing svn:eol-style property to text files. ........ r51720 | neal.norwitz | 2006-09-05 04:24:03 +0200 (Tue, 05 Sep 2006) | 2 lines Fix SF bug #1546288, crash in dict_equal. ........ r51721 | neal.norwitz | 2006-09-05 04:25:41 +0200 (Tue, 05 Sep 2006) | 1 line Fix SF #1552093, eval docstring typo (3 ps in mapping) ........ r51724 | neal.norwitz | 2006-09-05 04:35:08 +0200 (Tue, 05 Sep 2006) | 1 line This was found by Guido AFAIK on p3yk (sic) branch. ........ r51725 | neal.norwitz | 2006-09-05 04:36:20 +0200 (Tue, 05 Sep 2006) | 1 line Add a NEWS entry for str.rpartition() change ........ r51728 | neal.norwitz | 2006-09-05 04:57:01 +0200 (Tue, 05 Sep 2006) | 1 line Patch #1540470, for OpenBSD 4.0. Backport candidate for 2.[34]. ........ r51729 | neal.norwitz | 2006-09-05 05:53:08 +0200 (Tue, 05 Sep 2006) | 12 lines Bug #1520864 (again): unpacking singleton tuples in list comprehensions and generator expressions (x for x, in ... ) works again. Sigh, I only fixed for loops the first time, not list comps and genexprs too. I couldn't find any more unpacking cases where there is a similar bug lurking. This code should be refactored to eliminate the duplication. I'm sure the listcomp/genexpr code can be refactored. I'm not sure if the for loop can re-use any of the same code though. Will backport to 2.5 (the only place it matters). ........ r51731 | neal.norwitz | 2006-09-05 05:58:26 +0200 (Tue, 05 Sep 2006) | 1 line Add a comment about some refactoring. (There's probably more that should be done.) I will reformat this file in the next checkin due to the inconsistent tabs/spaces. ........ r51732 | neal.norwitz | 2006-09-05 06:00:12 +0200 (Tue, 05 Sep 2006) | 1 line M-x untabify ........ r51737 | hyeshik.chang | 2006-09-05 14:07:09 +0200 (Tue, 05 Sep 2006) | 7 lines Fix a few bugs on cjkcodecs found by Oren Tirosh: - gbk and gb18030 codec now handle U+30FB KATAKANA MIDDLE DOT correctly. - iso2022_jp_2 codec now encodes into G0 for KS X 1001, GB2312 codepoints to conform the standard. - iso2022_jp_3 and iso2022_jp_2004 codec can encode JIS X 2013:2 codepoints now. ........ r51738 | hyeshik.chang | 2006-09-05 14:14:57 +0200 (Tue, 05 Sep 2006) | 2 lines Fix a typo: 2013 -> 0213 ........ r51740 | georg.brandl | 2006-09-05 14:44:58 +0200 (Tue, 05 Sep 2006) | 3 lines Bug #1552618: change docs of dict.has_key() to reflect recommendation to use "in". ........ r51742 | andrew.kuchling | 2006-09-05 15:02:40 +0200 (Tue, 05 Sep 2006) | 1 line Rearrange example a bit, and show rpartition() when separator is not found ........ r51744 | andrew.kuchling | 2006-09-05 15:15:41 +0200 (Tue, 05 Sep 2006) | 1 line [Bug #1525469] SimpleXMLRPCServer still uses the sys.exc_{value,type} module-level globals instead of calling sys.exc_info(). Reported by Russell Warren ........ r51745 | andrew.kuchling | 2006-09-05 15:19:18 +0200 (Tue, 05 Sep 2006) | 3 lines [Bug #1526834] Fix crash in pdb when you do 'b f('; the function name was placed into a regex pattern and the unbalanced paren caused re.compile() to report an error ........ r51751 | kristjan.jonsson | 2006-09-05 19:58:12 +0200 (Tue, 05 Sep 2006) | 6 lines Update the PCBuild8 solution. Facilitate cross-compilation by having binaries in separate Win32 and x64 directories. Rationalized configs by making proper use of platforms/configurations. Remove pythoncore_pgo project. Add new PGIRelease and PGORelease configurations to perform Profile Guided Optimisation. Removed I64 support, but this can be easily added by copying the x64 platform settings. ........ r51758 | gustavo.niemeyer | 2006-09-06 03:58:52 +0200 (Wed, 06 Sep 2006) | 3 lines Fixing #1531862: Do not close standard file descriptors in the subprocess module. ........ r51760 | neal.norwitz | 2006-09-06 05:58:34 +0200 (Wed, 06 Sep 2006) | 1 line Revert 51758 because it broke all the buildbots ........ r51762 | georg.brandl | 2006-09-06 08:03:59 +0200 (Wed, 06 Sep 2006) | 3 lines Bug #1551427: fix a wrong NULL pointer check in the win32 version of os.urandom(). ........ r51765 | georg.brandl | 2006-09-06 08:09:31 +0200 (Wed, 06 Sep 2006) | 3 lines Bug #1550983: emit better error messages for erroneous relative imports (if not in package and if beyond toplevel package). ........ r51767 | neal.norwitz | 2006-09-06 08:28:06 +0200 (Wed, 06 Sep 2006) | 1 line with and as are now keywords. There are some generated files I can't recreate. ........ r51770 | georg.brandl | 2006-09-06 08:50:05 +0200 (Wed, 06 Sep 2006) | 5 lines Bug #1542051: Exceptions now correctly call PyObject_GC_UnTrack. Also make sure that every exception class has __module__ set to 'exceptions'. ........ r51785 | georg.brandl | 2006-09-06 22:05:58 +0200 (Wed, 06 Sep 2006) | 2 lines Fix missing import of the types module in logging.config. ........ r51789 | marc-andre.lemburg | 2006-09-06 22:40:22 +0200 (Wed, 06 Sep 2006) | 3 lines Add news item for bug fix of SF bug report #1546372. ........ r51797 | gustavo.niemeyer | 2006-09-07 02:48:33 +0200 (Thu, 07 Sep 2006) | 3 lines Fixed subprocess bug #1531862 again, after removing tests offending buildbot ........ r51798 | raymond.hettinger | 2006-09-07 04:42:48 +0200 (Thu, 07 Sep 2006) | 1 line Fix refcounts and add error checks. ........ r51803 | nick.coghlan | 2006-09-07 12:50:34 +0200 (Thu, 07 Sep 2006) | 1 line Fix the speed regression in inspect.py by adding another cache to speed up getmodule(). Patch #1553314 ........ r51805 | ronald.oussoren | 2006-09-07 14:03:10 +0200 (Thu, 07 Sep 2006) | 2 lines Fix a glaring error and update some version numbers. ........ r51814 | andrew.kuchling | 2006-09-07 15:56:23 +0200 (Thu, 07 Sep 2006) | 1 line Typo fix ........ r51815 | andrew.kuchling | 2006-09-07 15:59:38 +0200 (Thu, 07 Sep 2006) | 8 lines [Bug #1552726] Avoid repeatedly polling in interactive mode -- only put a timeout on the select() if an input hook has been defined. Patch by Richard Boulton. This select() code is only executed with readline 2.1, or if READLINE_CALLBACKS is defined. Backport candidate for 2.5, 2.4, probably earlier versions too. ........ r51816 | armin.rigo | 2006-09-07 17:06:00 +0200 (Thu, 07 Sep 2006) | 2 lines Add a warning notice on top of the generated grammar.txt. ........ r51819 | thomas.heller | 2006-09-07 20:56:28 +0200 (Thu, 07 Sep 2006) | 5 lines Anonymous structure fields that have a bit-width specified did not work, and they gave a strange error message from PyArg_ParseTuple: function takes exactly 2 arguments (3 given). With tests. ........ r51820 | thomas.heller | 2006-09-07 21:09:54 +0200 (Thu, 07 Sep 2006) | 4 lines The cast function did not accept c_char_p or c_wchar_p instances as first argument, and failed with a 'bad argument to internal function' error message. ........ r51827 | nick.coghlan | 2006-09-08 12:04:38 +0200 (Fri, 08 Sep 2006) | 1 line Add missing NEWS entry for rev 51803 ........ r51828 | andrew.kuchling | 2006-09-08 15:25:23 +0200 (Fri, 08 Sep 2006) | 1 line Add missing word ........ r51829 | andrew.kuchling | 2006-09-08 15:35:49 +0200 (Fri, 08 Sep 2006) | 1 line Explain SQLite a bit more clearly ........ r51830 | andrew.kuchling | 2006-09-08 15:36:36 +0200 (Fri, 08 Sep 2006) | 1 line Explain SQLite a bit more clearly ........ r51832 | andrew.kuchling | 2006-09-08 16:02:45 +0200 (Fri, 08 Sep 2006) | 1 line Use native SQLite types ........ r51833 | andrew.kuchling | 2006-09-08 16:03:01 +0200 (Fri, 08 Sep 2006) | 1 line Use native SQLite types ........ r51835 | andrew.kuchling | 2006-09-08 16:05:10 +0200 (Fri, 08 Sep 2006) | 1 line Fix typo in example ........ r51837 | brett.cannon | 2006-09-09 09:11:46 +0200 (Sat, 09 Sep 2006) | 6 lines Remove the __unicode__ method from exceptions. Allows unicode() to be called on exception classes. Would require introducing a tp_unicode slot to make it work otherwise. Fixes bug #1551432 and will be backported. ........ r51854 | neal.norwitz | 2006-09-11 06:24:09 +0200 (Mon, 11 Sep 2006) | 8 lines Forward port of 51850 from release25-maint branch. As mentioned on python-dev, reverting patch #1504333 because it introduced an infinite loop in rev 47154. This patch also adds a test to prevent the regression. ........ r51855 | neal.norwitz | 2006-09-11 06:28:16 +0200 (Mon, 11 Sep 2006) | 5 lines Properly handle a NULL returned from PyArena_New(). (Also fix some whitespace) Klocwork #364. ........ r51856 | neal.norwitz | 2006-09-11 06:32:57 +0200 (Mon, 11 Sep 2006) | 1 line Add a "crasher" taken from the sgml bug report referenced in the comment ........ r51858 | georg.brandl | 2006-09-11 11:38:35 +0200 (Mon, 11 Sep 2006) | 12 lines Forward-port of rev. 51857: Building with HP's cc on HP-UX turned up a couple of problems. _PyGILState_NoteThreadState was declared as static inconsistently. Make it static as it's not necessary outside of this module. Some tests failed because errno was reset to 0. (I think the tests that failed were at least: test_fcntl and test_mailbox). Ensure that errno doesn't change after a call to Py_END_ALLOW_THREADS. This only affected debug builds. ........ r51865 | martin.v.loewis | 2006-09-12 21:49:20 +0200 (Tue, 12 Sep 2006) | 2 lines Forward-port 51862: Add sgml_input.html. ........ r51866 | andrew.kuchling | 2006-09-12 22:50:23 +0200 (Tue, 12 Sep 2006) | 1 line Markup typo fix ........ r51867 | andrew.kuchling | 2006-09-12 23:09:02 +0200 (Tue, 12 Sep 2006) | 1 line Some editing, markup fixes ........ r51868 | andrew.kuchling | 2006-09-12 23:21:51 +0200 (Tue, 12 Sep 2006) | 1 line More wordsmithing ........ r51877 | andrew.kuchling | 2006-09-14 13:22:18 +0200 (Thu, 14 Sep 2006) | 1 line Make --help mention that -v can be supplied multiple times ........ r51878 | andrew.kuchling | 2006-09-14 13:28:50 +0200 (Thu, 14 Sep 2006) | 1 line Rewrite help message to remove some of the parentheticals. (There were a lot of them.) ........ r51883 | ka-ping.yee | 2006-09-15 02:34:19 +0200 (Fri, 15 Sep 2006) | 2 lines Fix grammar errors and improve clarity. ........ r51885 | georg.brandl | 2006-09-15 07:22:24 +0200 (Fri, 15 Sep 2006) | 3 lines Correct elementtree module index entry. ........ r51889 | fred.drake | 2006-09-15 17:18:04 +0200 (Fri, 15 Sep 2006) | 4 lines - fix module name in links in formatted documentation - minor markup cleanup (forward-ported from release25-maint revision 51888) ........ r51891 | fred.drake | 2006-09-15 18:11:27 +0200 (Fri, 15 Sep 2006) | 3 lines revise explanation of returns_unicode to reflect bool values and to include the default value (merged from release25-maint revision 51890) ........ r51897 | martin.v.loewis | 2006-09-16 19:36:37 +0200 (Sat, 16 Sep 2006) | 2 lines Patch #1557515: Add RLIMIT_SBSIZE. ........ r51903 | ronald.oussoren | 2006-09-17 20:42:53 +0200 (Sun, 17 Sep 2006) | 2 lines Port of revision 51902 in release25-maint to the trunk ........ r51904 | ronald.oussoren | 2006-09-17 21:23:27 +0200 (Sun, 17 Sep 2006) | 3 lines Tweak Mac/Makefile in to ensure that pythonw gets rebuild when the major version of python changes (2.5 -> 2.6). Bug #1552935. ........ r51913 | guido.van.rossum | 2006-09-18 23:36:16 +0200 (Mon, 18 Sep 2006) | 2 lines Make this thing executable. ........ r51920 | gregory.p.smith | 2006-09-19 19:35:04 +0200 (Tue, 19 Sep 2006) | 5 lines Fixes a bug with bsddb.DB.stat where the flags and txn keyword arguments are transposed. (reported by Louis Zechtzer) ..already committed to release24-maint ..needs committing to release25-maint ........ r51926 | brett.cannon | 2006-09-20 20:34:28 +0200 (Wed, 20 Sep 2006) | 3 lines Accidentally didn't commit Misc/NEWS entry on when __unicode__() was removed from exceptions. ........ r51927 | brett.cannon | 2006-09-20 20:43:13 +0200 (Wed, 20 Sep 2006) | 6 lines Allow exceptions to be directly sliced again (e.g., ``BaseException(1,2,3)[0:2]``). Discovered in Python 2.5.0 by Thomas Heller and reported to python-dev. This should be backported to 2.5 . ........ r51928 | brett.cannon | 2006-09-20 21:28:35 +0200 (Wed, 20 Sep 2006) | 2 lines Make python.vim output more deterministic. ........ r51949 | walter.doerwald | 2006-09-21 17:09:55 +0200 (Thu, 21 Sep 2006) | 2 lines Fix typo. ........ r51950 | jack.diederich | 2006-09-21 19:50:26 +0200 (Thu, 21 Sep 2006) | 5 lines * regression bug, count_next was coercing a Py_ssize_t to an unsigned Py_size_t which breaks negative counts * added test for negative numbers will backport to 2.5.1 ........ r51953 | jack.diederich | 2006-09-21 22:34:49 +0200 (Thu, 21 Sep 2006) | 1 line added itertools.count(-n) fix ........ r51971 | neal.norwitz | 2006-09-22 10:16:26 +0200 (Fri, 22 Sep 2006) | 10 lines Fix %zd string formatting on Mac OS X so it prints negative numbers. In addition to testing positive numbers, verify negative numbers work in configure. In order to avoid compiler warnings on OS X 10.4, also change the order of the check for the format character to use (PY_FORMAT_SIZE_T) in the sprintf format for Py_ssize_t. This patch changes PY_FORMAT_SIZE_T from "" to "l" if it wasn't defined at configure time. Need to verify the buildbot results. Backport candidate (if everyone thinks this patch can't be improved). ........ r51972 | neal.norwitz | 2006-09-22 10:18:10 +0200 (Fri, 22 Sep 2006) | 7 lines Bug #1557232: fix seg fault with def f((((x)))) and def f(((x),)). These tests should be improved. Hopefully this fixes variations when flipping back and forth between fpdef and fplist. Backport candidate. ........ r51975 | neal.norwitz | 2006-09-22 10:47:23 +0200 (Fri, 22 Sep 2006) | 4 lines Mostly revert this file to the same version as before. Only force setting of PY_FORMAT_SIZE_T to "l" for Mac OSX. I don't know a better define to use. This should get rid of the warnings on other platforms and Mac too. ........ r51986 | fred.drake | 2006-09-23 02:26:31 +0200 (Sat, 23 Sep 2006) | 1 line add boilerplate "What's New" document so the docs will build ........ r51987 | neal.norwitz | 2006-09-23 06:11:38 +0200 (Sat, 23 Sep 2006) | 1 line Remove extra semi-colons reported by Johnny Lee on python-dev. Backport if anyone cares. ........ r51989 | neal.norwitz | 2006-09-23 20:11:58 +0200 (Sat, 23 Sep 2006) | 1 line SF Bug #1563963, add missing word and cleanup first sentance ........ r51990 | brett.cannon | 2006-09-23 21:53:20 +0200 (Sat, 23 Sep 2006) | 3 lines Make output on test_strptime() be more verbose in face of failure. This is in hopes that more information will help debug the failing test on HPPA Ubuntu. ........ r51991 | georg.brandl | 2006-09-24 12:36:01 +0200 (Sun, 24 Sep 2006) | 2 lines Fix webbrowser.BackgroundBrowser on Windows. ........ r51993 | georg.brandl | 2006-09-24 14:35:36 +0200 (Sun, 24 Sep 2006) | 4 lines Fix a bug in the parser's future statement handling that led to "with" not being recognized as a keyword after, e.g., this statement: from __future__ import division, with_statement ........ r51995 | georg.brandl | 2006-09-24 14:50:24 +0200 (Sun, 24 Sep 2006) | 4 lines Fix a bug in traceback.format_exception_only() that led to an error being raised when print_exc() was called without an exception set. In version 2.4, this printed "None", restored that behavior. ........ r52000 | armin.rigo | 2006-09-25 17:16:26 +0200 (Mon, 25 Sep 2006) | 2 lines Another crasher. ........ r52011 | brett.cannon | 2006-09-27 01:38:24 +0200 (Wed, 27 Sep 2006) | 2 lines Make the error message for when the time data and format do not match clearer. ........ r52014 | andrew.kuchling | 2006-09-27 18:37:30 +0200 (Wed, 27 Sep 2006) | 1 line Add news item for rev. 51815 ........ r52018 | andrew.kuchling | 2006-09-27 21:23:05 +0200 (Wed, 27 Sep 2006) | 1 line Make examples do error checking on Py_InitModule ........ r52032 | brett.cannon | 2006-09-29 00:10:14 +0200 (Fri, 29 Sep 2006) | 2 lines Very minor grammatical fix in a comment. ........ r52048 | george.yoshida | 2006-09-30 07:14:02 +0200 (Sat, 30 Sep 2006) | 4 lines SF bug #1567976 : fix typo Will backport to 2.5. ........ r52051 | gregory.p.smith | 2006-09-30 08:08:20 +0200 (Sat, 30 Sep 2006) | 2 lines wording change ........ r52053 | georg.brandl | 2006-09-30 09:24:48 +0200 (Sat, 30 Sep 2006) | 2 lines Bug #1567375: a minor logical glitch in example description. ........ r52056 | georg.brandl | 2006-09-30 09:31:57 +0200 (Sat, 30 Sep 2006) | 3 lines Bug #1565661: in webbrowser, split() the command for the default GNOME browser in case it is a command with args. ........ r52058 | georg.brandl | 2006-09-30 10:43:30 +0200 (Sat, 30 Sep 2006) | 4 lines Patch #1567691: super() and new.instancemethod() now don't accept keyword arguments any more (previously they accepted them, but didn't use them). ........ r52061 | georg.brandl | 2006-09-30 11:03:42 +0200 (Sat, 30 Sep 2006) | 3 lines Bug #1566800: make sure that EnvironmentError can be called with any number of arguments, as was the case in Python 2.4. ........ r52063 | georg.brandl | 2006-09-30 11:06:45 +0200 (Sat, 30 Sep 2006) | 2 lines Bug #1566663: remove obsolete example from datetime docs. ........ r52065 | georg.brandl | 2006-09-30 11:13:21 +0200 (Sat, 30 Sep 2006) | 3 lines Bug #1566602: correct failure of posixpath unittest when $HOME ends with a slash. ........ r52068 | georg.brandl | 2006-09-30 12:58:01 +0200 (Sat, 30 Sep 2006) | 3 lines Bug #1457823: cgi.(Sv)FormContentDict's constructor now takes keep_blank_values and strict_parsing keyword arguments. ........ r52069 | georg.brandl | 2006-09-30 13:06:47 +0200 (Sat, 30 Sep 2006) | 3 lines Bug #1560617: in pyclbr, return full module name not only for classes, but also for functions. ........ r52072 | georg.brandl | 2006-09-30 13:17:34 +0200 (Sat, 30 Sep 2006) | 3 lines Bug #1556784: allow format strings longer than 127 characters in datetime's strftime function. ........ r52075 | georg.brandl | 2006-09-30 13:22:28 +0200 (Sat, 30 Sep 2006) | 3 lines Bug #1446043: correctly raise a LookupError if an encoding name given to encodings.search_function() contains a dot. ........ r52078 | georg.brandl | 2006-09-30 14:02:57 +0200 (Sat, 30 Sep 2006) | 3 lines Bug #1546052: clarify that PyString_FromString(AndSize) copies the string pointed to by its parameter. ........ r52080 | georg.brandl | 2006-09-30 14:16:03 +0200 (Sat, 30 Sep 2006) | 3 lines Convert test_import to unittest. ........ r52083 | kurt.kaiser | 2006-10-01 23:16:45 +0200 (Sun, 01 Oct 2006) | 5 lines Some syntax errors were being caught by tokenize during the tabnanny check, resulting in obscure error messages. Do the syntax check first. Bug 1562716, 1562719 ........ r52084 | kurt.kaiser | 2006-10-01 23:54:37 +0200 (Sun, 01 Oct 2006) | 3 lines Add comment explaining that error msgs may be due to user code when running w/o subprocess. ........ r52086 | martin.v.loewis | 2006-10-02 16:55:51 +0200 (Mon, 02 Oct 2006) | 3 lines Fix test for uintptr_t. Fixes #1568842. Will backport. ........ r52089 | martin.v.loewis | 2006-10-02 17:20:37 +0200 (Mon, 02 Oct 2006) | 3 lines Guard uintptr_t test with HAVE_STDINT_H, test for stdint.h. Will backport. ........ r52100 | vinay.sajip | 2006-10-03 20:02:37 +0200 (Tue, 03 Oct 2006) | 1 line Documentation omitted the additional parameter to LogRecord.__init__ which was added in 2.5. (See SF #1569622). ........ r52101 | vinay.sajip | 2006-10-03 20:20:26 +0200 (Tue, 03 Oct 2006) | 1 line Documentation clarified to mention optional parameters. ........ r52102 | vinay.sajip | 2006-10-03 20:21:56 +0200 (Tue, 03 Oct 2006) | 1 line Modified LogRecord.__init__ to make the func parameter optional. (See SF #1569622). ........ r52121 | brett.cannon | 2006-10-03 23:58:55 +0200 (Tue, 03 Oct 2006) | 2 lines Fix minor typo in a comment. ........ r52123 | brett.cannon | 2006-10-04 01:23:14 +0200 (Wed, 04 Oct 2006) | 2 lines Convert test_imp over to unittest. ........ r52128 | barry.warsaw | 2006-10-04 04:06:36 +0200 (Wed, 04 Oct 2006) | 3 lines decode_rfc2231(): As Christian Robottom Reis points out, it makes no sense to test for parts > 3 when we use .split(..., 2). ........ r52129 | jeremy.hylton | 2006-10-04 04:24:52 +0200 (Wed, 04 Oct 2006) | 9 lines Fix for SF bug 1569998: break permitted inside try. The compiler was checking that there was something on the fblock stack, but not that there was a loop on the stack. Fixed that and added a test for the specific syntax error. Bug fix candidate. ........ r52130 | martin.v.loewis | 2006-10-04 07:47:34 +0200 (Wed, 04 Oct 2006) | 4 lines Fix integer negation and absolute value to not rely on undefined behaviour of the C compiler anymore. Will backport to 2.5 and 2.4. ........ r52135 | martin.v.loewis | 2006-10-04 11:21:20 +0200 (Wed, 04 Oct 2006) | 1 line Forward port r52134: Add uuids for 2.4.4. ........ r52137 | armin.rigo | 2006-10-04 12:23:57 +0200 (Wed, 04 Oct 2006) | 3 lines Compilation problem caused by conflicting typedefs for uint32_t (unsigned long vs. unsigned int). ........ r52139 | armin.rigo | 2006-10-04 14:17:45 +0200 (Wed, 04 Oct 2006) | 23 lines Forward-port of r52136,52138: a review of overflow-detecting code. * unified the way intobject, longobject and mystrtoul handle values around -sys.maxint-1. * in general, trying to entierely avoid overflows in any computation involving signed ints or longs is extremely involved. Fixed a few simple cases where a compiler might be too clever (but that's all guesswork). * more overflow checks against bad data in marshal.c. * 2.5 specific: fixed a number of places that were still confusing int and Py_ssize_t. Some of them could potentially have caused "real-world" breakage. * list.pop(x): fixing overflow issues on x was messy. I just reverted to PyArg_ParseTuple("n"), which does the right thing. (An obscure test was trying to give a Decimal to list.pop()... doesn't make sense any more IMHO) * trying to write a few tests... ........ r52147 | andrew.kuchling | 2006-10-04 15:42:43 +0200 (Wed, 04 Oct 2006) | 6 lines Cause a PyObject_Malloc() failure to trigger a MemoryError, and then add 'if (PyErr_Occurred())' checks to various places so that NULL is returned properly. 2.4 backport candidate. ........ r52148 | martin.v.loewis | 2006-10-04 17:25:28 +0200 (Wed, 04 Oct 2006) | 1 line Add MSVC8 project files to create wininst-8.exe. ........ r52196 | brett.cannon | 2006-10-06 00:02:31 +0200 (Fri, 06 Oct 2006) | 7 lines Clarify what "re-initialization" means for init_builtin() and init_dynamic(). Also remove warning about re-initialization as possibly raising an execption as both call _PyImport_FindExtension() which pulls any module that was already imported from the Python process' extension cache and just copies the __dict__ into the module stored in sys.modules. ........ r52200 | fred.drake | 2006-10-06 02:03:45 +0200 (Fri, 06 Oct 2006) | 3 lines - update links - remove Sleepycat name now that they have been bought ........ r52204 | andrew.kuchling | 2006-10-06 12:41:01 +0200 (Fri, 06 Oct 2006) | 1 line Case fix ........ r52208 | georg.brandl | 2006-10-06 14:46:08 +0200 (Fri, 06 Oct 2006) | 3 lines Fix name. ........ r52211 | andrew.kuchling | 2006-10-06 15:18:26 +0200 (Fri, 06 Oct 2006) | 1 line [Bug #1545341] Allow 'classifier' parameter to be a tuple as well as a list. Will backport. ........ r52212 | armin.rigo | 2006-10-06 18:33:22 +0200 (Fri, 06 Oct 2006) | 4 lines A very minor bug fix: this code looks like it is designed to accept any hue value and do the modulo itself, except it doesn't quite do it in all cases. At least, the "cannot get here" comment was wrong. ........ r52213 | andrew.kuchling | 2006-10-06 20:51:55 +0200 (Fri, 06 Oct 2006) | 1 line Comment grammar ........ r52218 | skip.montanaro | 2006-10-07 13:05:02 +0200 (Sat, 07 Oct 2006) | 6 lines Note that the excel_tab class is registered as the "excel-tab" dialect. Fixes 1572471. Make a similar change for the excel class and clean up references to the Dialects and Formatting Parameters section in a few places. ........ r52221 | georg.brandl | 2006-10-08 09:11:54 +0200 (Sun, 08 Oct 2006) | 3 lines Add missing NEWS entry for rev. 52129. ........ r52223 | hyeshik.chang | 2006-10-08 15:48:34 +0200 (Sun, 08 Oct 2006) | 3 lines Bug #1572832: fix a bug in ISO-2022 codecs which may cause segfault when encoding non-BMP unicode characters. (Submitted by Ray Chason) ........ r52227 | ronald.oussoren | 2006-10-08 19:37:58 +0200 (Sun, 08 Oct 2006) | 4 lines Add version number to the link to the python documentation in /Developer/Documentation/Python, better for users that install multiple versions of python. ........ r52229 | ronald.oussoren | 2006-10-08 19:40:02 +0200 (Sun, 08 Oct 2006) | 2 lines Fix for bug #1570284 ........ r52233 | ronald.oussoren | 2006-10-08 19:49:52 +0200 (Sun, 08 Oct 2006) | 6 lines MacOSX: distutils changes the values of BASECFLAGS and LDFLAGS when using a universal build of python on OSX 10.3 to ensure that those flags can be used to compile code (the universal build uses compiler flags that aren't supported on 10.3). This patches gives the same treatment to CFLAGS, PY_CFLAGS and BLDSHARED. ........ r52236 | ronald.oussoren | 2006-10-08 19:51:46 +0200 (Sun, 08 Oct 2006) | 5 lines MacOSX: The universal build requires that users have the MacOSX10.4u SDK installed to build extensions. This patch makes distutils emit a warning when the compiler should use an SDK but that SDK is not installed, hopefully reducing some confusion. ........ r52238 | ronald.oussoren | 2006-10-08 20:18:26 +0200 (Sun, 08 Oct 2006) | 3 lines MacOSX: add more logic to recognize the correct startup file to patch to the shell profile patching post-install script. ........ r52242 | andrew.kuchling | 2006-10-09 19:10:12 +0200 (Mon, 09 Oct 2006) | 1 line Add news item for rev. 52211 change ........ r52245 | andrew.kuchling | 2006-10-09 20:05:19 +0200 (Mon, 09 Oct 2006) | 1 line Fix wording in comment ........ r52251 | georg.brandl | 2006-10-09 21:03:06 +0200 (Mon, 09 Oct 2006) | 2 lines Patch #1572724: fix typo ('=' instead of '==') in _msi.c. ........ r52255 | barry.warsaw | 2006-10-09 21:43:24 +0200 (Mon, 09 Oct 2006) | 2 lines List gc.get_count() in the module docstring. ........ r52257 | martin.v.loewis | 2006-10-09 22:44:25 +0200 (Mon, 09 Oct 2006) | 1 line Bug #1565150: Fix subsecond processing for os.utime on Windows. ........ r52268 | ronald.oussoren | 2006-10-10 09:55:06 +0200 (Tue, 10 Oct 2006) | 2 lines MacOSX: fix permission problem in the generated installer ........ r52293 | georg.brandl | 2006-10-12 09:38:04 +0200 (Thu, 12 Oct 2006) | 2 lines Bug #1575746: fix typo in property() docs. ........ r52295 | georg.brandl | 2006-10-12 09:57:21 +0200 (Thu, 12 Oct 2006) | 3 lines Bug #813342: Start the IDLE subprocess with -Qnew if the parent is started with that option. ........ r52297 | georg.brandl | 2006-10-12 10:22:53 +0200 (Thu, 12 Oct 2006) | 2 lines Bug #1565919: document set types in the Language Reference. ........ r52299 | georg.brandl | 2006-10-12 11:20:33 +0200 (Thu, 12 Oct 2006) | 3 lines Bug #1550524: better heuristics to find correct class definition in inspect.findsource(). ........ r52301 | georg.brandl | 2006-10-12 11:47:12 +0200 (Thu, 12 Oct 2006) | 4 lines Bug #1548891: The cStringIO.StringIO() constructor now encodes unicode arguments with the system default encoding just like the write() method does, instead of converting it to a raw buffer. ........ r52303 | georg.brandl | 2006-10-12 13:14:40 +0200 (Thu, 12 Oct 2006) | 2 lines Bug #1546628: add a note about urlparse.urljoin() and absolute paths. ........ r52305 | georg.brandl | 2006-10-12 13:27:59 +0200 (Thu, 12 Oct 2006) | 3 lines Bug #1545497: when given an explicit base, int() did ignore NULs embedded in the string to convert. ........ r52307 | georg.brandl | 2006-10-12 13:41:11 +0200 (Thu, 12 Oct 2006) | 3 lines Add a note to fpectl docs that it's not built by default (bug #1556261). ........ r52309 | georg.brandl | 2006-10-12 13:46:57 +0200 (Thu, 12 Oct 2006) | 3 lines Bug #1560114: the Mac filesystem does have accurate information about the case of filenames. ........ r52311 | georg.brandl | 2006-10-12 13:59:27 +0200 (Thu, 12 Oct 2006) | 2 lines Small grammar fix, thanks Sjoerd. ........ r52313 | georg.brandl | 2006-10-12 14:03:07 +0200 (Thu, 12 Oct 2006) | 2 lines Fix tarfile depending on buggy int('1\0', base) behavior. ........ r52315 | georg.brandl | 2006-10-12 14:33:07 +0200 (Thu, 12 Oct 2006) | 2 lines Bug #1283491: follow docstring convention wrt. keyword-able args in sum(). ........ r52316 | georg.brandl | 2006-10-12 15:08:16 +0200 (Thu, 12 Oct 2006) | 3 lines Bug #1560179: speed up posixpath.(dir|base)name ........ r52327 | brett.cannon | 2006-10-14 08:36:45 +0200 (Sat, 14 Oct 2006) | 3 lines Clean up the language of a sentence relating to the connect() function and user-defined datatypes. ........ r52332 | neal.norwitz | 2006-10-14 23:33:38 +0200 (Sat, 14 Oct 2006) | 3 lines Update the peephole optimizer to remove more dead code (jumps after returns) and inline jumps to returns. ........ r52333 | martin.v.loewis | 2006-10-15 09:54:40 +0200 (Sun, 15 Oct 2006) | 4 lines Patch #1576954: Update VC6 build directory; remove redundant files in VC7. Will backport to 2.5. ........ r52335 | martin.v.loewis | 2006-10-15 10:43:33 +0200 (Sun, 15 Oct 2006) | 1 line Patch #1576166: Support os.utime for directories on Windows NT+. ........ r52336 | martin.v.loewis | 2006-10-15 10:51:22 +0200 (Sun, 15 Oct 2006) | 2 lines Patch #1577551: Add ctypes and ET build support for VC6. Will backport to 2.5. ........ r52338 | martin.v.loewis | 2006-10-15 11:35:51 +0200 (Sun, 15 Oct 2006) | 1 line Loosen the test for equal time stamps. ........ r52339 | martin.v.loewis | 2006-10-15 11:43:39 +0200 (Sun, 15 Oct 2006) | 2 lines Bug #1567666: Emulate GetFileAttributesExA for Win95. Will backport to 2.5. ........ r52341 | martin.v.loewis | 2006-10-15 13:02:07 +0200 (Sun, 15 Oct 2006) | 2 lines Round to int, because some systems support sub-second time stamps in stat, but not in utime. Also be consistent with modifying only mtime, not atime. ........ r52342 | martin.v.loewis | 2006-10-15 13:57:40 +0200 (Sun, 15 Oct 2006) | 2 lines Set the eol-style for project files to "CRLF". ........ r52343 | martin.v.loewis | 2006-10-15 13:59:56 +0200 (Sun, 15 Oct 2006) | 3 lines Drop binary property on dsp files, set eol-style to CRLF instead. ........ r52344 | martin.v.loewis | 2006-10-15 14:01:43 +0200 (Sun, 15 Oct 2006) | 2 lines Remove binary property, set eol-style to CRLF instead. ........ r52346 | martin.v.loewis | 2006-10-15 16:30:38 +0200 (Sun, 15 Oct 2006) | 2 lines Mention the bdist_msi module. Will backport to 2.5. ........ r52354 | brett.cannon | 2006-10-16 05:09:52 +0200 (Mon, 16 Oct 2006) | 3 lines Fix turtle so that you can launch the demo2 function on its own instead of only when the module is launched as a script. ........ r52356 | martin.v.loewis | 2006-10-17 17:18:06 +0200 (Tue, 17 Oct 2006) | 2 lines Patch #1457736: Update VC6 to use current PCbuild settings. Will backport to 2.5. ........ r52360 | martin.v.loewis | 2006-10-17 20:09:55 +0200 (Tue, 17 Oct 2006) | 2 lines Remove obsolete file. Will backport. ........ r52363 | martin.v.loewis | 2006-10-17 20:59:23 +0200 (Tue, 17 Oct 2006) | 4 lines Forward-port r52358: - Bug #1578513: Cross compilation was broken by a change to configure. Repair so that it's back to how it was in 2.4.3. ........ r52365 | thomas.heller | 2006-10-17 21:30:48 +0200 (Tue, 17 Oct 2006) | 6 lines ctypes callback functions only support 'fundamental' result types. Check this and raise an error when something else is used - before this change ctypes would hang or crash when such a callback was called. This is a partial fix for #1574584. Will backport to release25-maint. ........ r52377 | tim.peters | 2006-10-18 07:06:06 +0200 (Wed, 18 Oct 2006) | 2 lines newIobject(): repaired incorrect cast to quiet MSVC warning. ........ r52378 | tim.peters | 2006-10-18 07:09:12 +0200 (Wed, 18 Oct 2006) | 2 lines Whitespace normalization. ........ r52379 | tim.peters | 2006-10-18 07:10:28 +0200 (Wed, 18 Oct 2006) | 2 lines Add missing svn:eol-style to text files. ........ r52387 | martin.v.loewis | 2006-10-19 12:58:46 +0200 (Thu, 19 Oct 2006) | 3 lines Add check for the PyArg_ParseTuple format, and declare it if it is supported. ........ r52388 | martin.v.loewis | 2006-10-19 13:00:37 +0200 (Thu, 19 Oct 2006) | 3 lines Fix various minor errors in passing arguments to PyArg_ParseTuple. ........ r52389 | martin.v.loewis | 2006-10-19 18:01:37 +0200 (Thu, 19 Oct 2006) | 2 lines Restore CFLAGS after checking for __attribute__ ........ r52390 | andrew.kuchling | 2006-10-19 23:55:55 +0200 (Thu, 19 Oct 2006) | 1 line [Bug #1576348] Fix typo in example ........ r52414 | walter.doerwald | 2006-10-22 10:59:41 +0200 (Sun, 22 Oct 2006) | 2 lines Port test___future__ to unittest. ........ r52415 | ronald.oussoren | 2006-10-22 12:45:18 +0200 (Sun, 22 Oct 2006) | 3 lines Patch #1580674: with this patch os.readlink uses the filesystem encoding to decode unicode objects and returns an unicode object when the argument is one. ........ r52416 | martin.v.loewis | 2006-10-22 12:46:18 +0200 (Sun, 22 Oct 2006) | 3 lines Patch #1580872: Remove duplicate declaration of PyCallable_Check. Will backport to 2.5. ........ r52418 | martin.v.loewis | 2006-10-22 12:55:15 +0200 (Sun, 22 Oct 2006) | 4 lines - Patch #1560695: Add .note.GNU-stack to ctypes' sysv.S so that ctypes isn't considered as requiring executable stacks. Will backport to 2.5. ........ r52420 | martin.v.loewis | 2006-10-22 15:45:13 +0200 (Sun, 22 Oct 2006) | 3 lines Remove passwd.adjunct.byname from list of maps for test_nis. Will backport to 2.5. ........ r52431 | georg.brandl | 2006-10-24 18:54:16 +0200 (Tue, 24 Oct 2006) | 2 lines Patch [ 1583506 ] tarfile.py: 100-char filenames are truncated ........ r52446 | andrew.kuchling | 2006-10-26 21:10:46 +0200 (Thu, 26 Oct 2006) | 1 line [Bug #1579796] Wrong syntax for PyDateTime_IMPORT in documentation. Reported by David Faure. ........ r52449 | andrew.kuchling | 2006-10-26 21:16:46 +0200 (Thu, 26 Oct 2006) | 1 line Typo fix ........ r52452 | martin.v.loewis | 2006-10-27 08:16:31 +0200 (Fri, 27 Oct 2006) | 3 lines Patch #1549049: Rewrite type conversion in structmember. Fixes #1545696 and #1566140. Will backport to 2.5. ........ r52454 | martin.v.loewis | 2006-10-27 08:42:27 +0200 (Fri, 27 Oct 2006) | 2 lines Check for values.h. Will backport. ........ r52456 | martin.v.loewis | 2006-10-27 09:06:52 +0200 (Fri, 27 Oct 2006) | 2 lines Get DBL_MAX from float.h not values.h. Will backport. ........ r52458 | martin.v.loewis | 2006-10-27 09:13:28 +0200 (Fri, 27 Oct 2006) | 2 lines Patch #1567274: Support SMTP over TLS. ........ r52459 | andrew.kuchling | 2006-10-27 13:33:29 +0200 (Fri, 27 Oct 2006) | 1 line Set svn:keywords property ........ r52460 | andrew.kuchling | 2006-10-27 13:36:41 +0200 (Fri, 27 Oct 2006) | 1 line Add item ........ r52461 | andrew.kuchling | 2006-10-27 13:37:01 +0200 (Fri, 27 Oct 2006) | 1 line Some wording changes and markup fixes ........ r52462 | andrew.kuchling | 2006-10-27 14:18:38 +0200 (Fri, 27 Oct 2006) | 1 line [Bug #1585690] Note that line_num was added in Python 2.5 ........ r52464 | andrew.kuchling | 2006-10-27 14:50:38 +0200 (Fri, 27 Oct 2006) | 1 line [Bug #1583946] Reword description of server and issuer ........ r52466 | andrew.kuchling | 2006-10-27 15:06:25 +0200 (Fri, 27 Oct 2006) | 1 line [Bug #1562583] Mention the set_reuse_addr() method ........ r52469 | andrew.kuchling | 2006-10-27 15:22:46 +0200 (Fri, 27 Oct 2006) | 4 lines [Bug #1542016] Report PCALL_POP value. This makes the return value of sys.callstats() match its docstring. Backport candidate. Though it's an API change, this is a pretty obscure portion of the API. ........ r52473 | andrew.kuchling | 2006-10-27 16:53:41 +0200 (Fri, 27 Oct 2006) | 1 line Point users to the subprocess module in the docs for os.system, os.spawn*, os.popen2, and the popen2 and commands modules ........ r52476 | andrew.kuchling | 2006-10-27 18:39:10 +0200 (Fri, 27 Oct 2006) | 1 line [Bug #1576241] Let functools.wraps work with built-in functions ........ r52478 | andrew.kuchling | 2006-10-27 18:55:34 +0200 (Fri, 27 Oct 2006) | 1 line [Bug #1575506] The _singlefileMailbox class was using the wrong file object in its flush() method, causing an error ........ r52480 | andrew.kuchling | 2006-10-27 19:06:16 +0200 (Fri, 27 Oct 2006) | 1 line Clarify docstring ........ r52481 | andrew.kuchling | 2006-10-27 19:11:23 +0200 (Fri, 27 Oct 2006) | 5 lines [Patch #1574068 by Scott Dial] urllib and urllib2 were using base64.encodestring() for encoding authentication data. encodestring() can include newlines for very long input, which produced broken HTTP headers. ........ r52483 | andrew.kuchling | 2006-10-27 20:13:46 +0200 (Fri, 27 Oct 2006) | 1 line Check db_setup_debug for a few print statements; change sqlite_setup_debug to False ........ r52484 | andrew.kuchling | 2006-10-27 20:15:02 +0200 (Fri, 27 Oct 2006) | 1 line [Patch #1503717] Tiny patch from Chris AtLee to stop a lengthy line from being printed ........ r52485 | thomas.heller | 2006-10-27 20:31:36 +0200 (Fri, 27 Oct 2006) | 5 lines WindowsError.str should display the windows error code, not the posix error code; with test. Fixes #1576174. Will backport to release25-maint. ........ r52487 | thomas.heller | 2006-10-27 21:05:53 +0200 (Fri, 27 Oct 2006) | 4 lines Modulefinder now handles absolute and relative imports, including tests. Will backport to release25-maint. ........ r52488 | georg.brandl | 2006-10-27 22:39:43 +0200 (Fri, 27 Oct 2006) | 2 lines Patch #1552024: add decorator support to unparse.py demo script. ........ r52492 | walter.doerwald | 2006-10-28 12:47:12 +0200 (Sat, 28 Oct 2006) | 2 lines Port test_bufio to unittest. ........ r52493 | georg.brandl | 2006-10-28 15:10:17 +0200 (Sat, 28 Oct 2006) | 6 lines Convert test_global, test_scope and test_grammar to unittest. I tried to enclose all tests which must be run at the toplevel (instead of inside a method) in exec statements. ........ r52494 | georg.brandl | 2006-10-28 15:11:41 +0200 (Sat, 28 Oct 2006) | 3 lines Update outstanding bugs test file. ........ r52495 | georg.brandl | 2006-10-28 15:51:49 +0200 (Sat, 28 Oct 2006) | 3 lines Convert test_math to unittest. ........ r52496 | georg.brandl | 2006-10-28 15:56:58 +0200 (Sat, 28 Oct 2006) | 3 lines Convert test_opcodes to unittest. ........ r52497 | georg.brandl | 2006-10-28 18:04:04 +0200 (Sat, 28 Oct 2006) | 2 lines Fix nth() itertool recipe. ........ r52500 | georg.brandl | 2006-10-28 22:25:09 +0200 (Sat, 28 Oct 2006) | 2 lines make test_grammar pass with python -O ........ r52501 | neal.norwitz | 2006-10-28 23:15:30 +0200 (Sat, 28 Oct 2006) | 6 lines Add some asserts. In sysmodule, I think these were to try to silence some warnings from Klokwork. They verify the assumptions of the format of svn version output. The assert in the thread module helped debug a problem on HP-UX. ........ r52502 | neal.norwitz | 2006-10-28 23:16:54 +0200 (Sat, 28 Oct 2006) | 5 lines Fix warnings with HP's C compiler. It doesn't recognize that infinite loops are, um, infinite. These conditions should not be able to happen. Will backport. ........ r52503 | neal.norwitz | 2006-10-28 23:17:51 +0200 (Sat, 28 Oct 2006) | 5 lines Fix crash in test on HP-UX. Apparently, it's not possible to delete a lock if it's held (even by the current thread). Will backport. ........ r52504 | neal.norwitz | 2006-10-28 23:19:07 +0200 (Sat, 28 Oct 2006) | 6 lines Fix bug #1565514, SystemError not raised on too many nested blocks. It seems like this should be a different error than SystemError, but I don't have any great ideas and SystemError was raised in 2.4 and earlier. Will backport. ........ r52505 | neal.norwitz | 2006-10-28 23:20:12 +0200 (Sat, 28 Oct 2006) | 4 lines Prevent crash if alloc of garbage fails. Found by Typo.pl. Will backport. ........ r52506 | neal.norwitz | 2006-10-28 23:21:00 +0200 (Sat, 28 Oct 2006) | 4 lines Don't inline Py_ADDRESS_IN_RANGE with gcc 4+ either. Will backport. ........ r52513 | neal.norwitz | 2006-10-28 23:56:49 +0200 (Sat, 28 Oct 2006) | 2 lines Fix test_modulefinder so it doesn't fail when run after test_distutils. ........ r52514 | neal.norwitz | 2006-10-29 00:12:26 +0200 (Sun, 29 Oct 2006) | 4 lines From SF 1557890, fix problem of using wrong type in example. Will backport. ........ r52517 | georg.brandl | 2006-10-29 09:39:22 +0100 (Sun, 29 Oct 2006) | 4 lines Fix codecs.EncodedFile which did not use file_encoding in 2.5.0, and fix all codecs file wrappers to work correctly with the "with" statement (bug #1586513). ........ r52519 | georg.brandl | 2006-10-29 09:47:08 +0100 (Sun, 29 Oct 2006) | 3 lines Clean up a leftover from old listcomp generation code. ........ r52520 | georg.brandl | 2006-10-29 09:53:06 +0100 (Sun, 29 Oct 2006) | 4 lines Bug #1586448: the compiler module now emits the same bytecode for list comprehensions as the builtin compiler, using the LIST_APPEND opcode. ........ r52521 | georg.brandl | 2006-10-29 10:01:01 +0100 (Sun, 29 Oct 2006) | 3 lines Remove trailing comma. ........ r52522 | georg.brandl | 2006-10-29 10:05:04 +0100 (Sun, 29 Oct 2006) | 3 lines Bug #1357915: allow all sequence types for shell arguments in subprocess. ........ r52524 | georg.brandl | 2006-10-29 10:16:12 +0100 (Sun, 29 Oct 2006) | 3 lines Patch #1583880: fix tarfile's problems with long names and posix/ GNU modes. ........ r52526 | georg.brandl | 2006-10-29 10:18:00 +0100 (Sun, 29 Oct 2006) | 3 lines Test assert if __debug__ is true. ........ r52527 | georg.brandl | 2006-10-29 10:32:16 +0100 (Sun, 29 Oct 2006) | 2 lines Fix the new EncodedFile test to work with big endian platforms. ........ r52529 | georg.brandl | 2006-10-29 15:39:09 +0100 (Sun, 29 Oct 2006) | 2 lines Bug #1586613: fix zlib and bz2 codecs' incremental en/decoders. ........ r52532 | georg.brandl | 2006-10-29 19:01:08 +0100 (Sun, 29 Oct 2006) | 2 lines Bug #1586773: extend hashlib docstring. ........ r52534 | neal.norwitz | 2006-10-29 19:30:10 +0100 (Sun, 29 Oct 2006) | 4 lines Update comments, remove commented out code. Move assembler structure next to assembler code to make it easier to move it to a separate file. ........ r52535 | georg.brandl | 2006-10-29 19:31:42 +0100 (Sun, 29 Oct 2006) | 3 lines Bug #1576657: when setting a KeyError for a tuple key, make sure that the tuple isn't used as the "exception arguments tuple". ........ r52537 | georg.brandl | 2006-10-29 20:13:40 +0100 (Sun, 29 Oct 2006) | 3 lines Convert test_mmap to unittest. ........ r52538 | georg.brandl | 2006-10-29 20:20:45 +0100 (Sun, 29 Oct 2006) | 3 lines Convert test_poll to unittest. ........ r52539 | georg.brandl | 2006-10-29 20:24:43 +0100 (Sun, 29 Oct 2006) | 3 lines Convert test_nis to unittest. ........ r52540 | georg.brandl | 2006-10-29 20:35:03 +0100 (Sun, 29 Oct 2006) | 3 lines Convert test_types to unittest. ........ r52541 | georg.brandl | 2006-10-29 20:51:16 +0100 (Sun, 29 Oct 2006) | 3 lines Convert test_cookie to unittest. ........ r52542 | georg.brandl | 2006-10-29 21:09:12 +0100 (Sun, 29 Oct 2006) | 3 lines Convert test_cgi to unittest. ........ r52543 | georg.brandl | 2006-10-29 21:24:01 +0100 (Sun, 29 Oct 2006) | 3 lines Completely convert test_httplib to unittest. ........ r52544 | georg.brandl | 2006-10-29 21:28:26 +0100 (Sun, 29 Oct 2006) | 2 lines Convert test_MimeWriter to unittest. ........ r52545 | georg.brandl | 2006-10-29 21:31:17 +0100 (Sun, 29 Oct 2006) | 3 lines Convert test_openpty to unittest. ........ r52546 | georg.brandl | 2006-10-29 21:35:12 +0100 (Sun, 29 Oct 2006) | 3 lines Remove leftover test output file. ........ r52547 | georg.brandl | 2006-10-29 22:54:18 +0100 (Sun, 29 Oct 2006) | 3 lines Move the check for openpty to the beginning. ........ r52548 | walter.doerwald | 2006-10-29 23:06:28 +0100 (Sun, 29 Oct 2006) | 2 lines Add tests for basic argument errors. ........ r52549 | walter.doerwald | 2006-10-30 00:02:27 +0100 (Mon, 30 Oct 2006) | 3 lines Add tests for incremental codecs with an errors argument. ........ r52550 | neal.norwitz | 2006-10-30 00:39:03 +0100 (Mon, 30 Oct 2006) | 1 line Fix refleak ........ r52552 | neal.norwitz | 2006-10-30 00:58:36 +0100 (Mon, 30 Oct 2006) | 1 line I'm assuming this is correct, it fixes the tests so they pass again ........ r52555 | vinay.sajip | 2006-10-31 18:32:37 +0100 (Tue, 31 Oct 2006) | 1 line Change to improve speed of _fixupChildren ........ r52556 | vinay.sajip | 2006-10-31 18:34:31 +0100 (Tue, 31 Oct 2006) | 1 line Added relativeCreated to Formatter doc (has been in the system for a long time - was unaccountably left out of the docs and not noticed until now). ........ r52588 | thomas.heller | 2006-11-02 20:48:24 +0100 (Thu, 02 Nov 2006) | 5 lines Replace the XXX marker in the 'Arrays and pointers' reference manual section with a link to the tutorial sections. Will backport to release25-maint. ........ r52592 | thomas.heller | 2006-11-02 21:22:29 +0100 (Thu, 02 Nov 2006) | 6 lines Fix a code example by adding a missing import. Fixes #1557890. Will backport to release25-maint. ........ r52598 | tim.peters | 2006-11-03 03:32:46 +0100 (Fri, 03 Nov 2006) | 2 lines Whitespace normalization. ........ r52619 | martin.v.loewis | 2006-11-04 19:14:06 +0100 (Sat, 04 Nov 2006) | 4 lines - Patch #1060577: Extract list of RPM files from spec file in bdist_rpm Will backport to 2.5. ........ r52621 | neal.norwitz | 2006-11-04 20:25:22 +0100 (Sat, 04 Nov 2006) | 4 lines Bug #1588287: fix invalid assertion for `1,2` in debug builds. Will backport ........ r52630 | andrew.kuchling | 2006-11-05 22:04:37 +0100 (Sun, 05 Nov 2006) | 1 line Update link ........ r52631 | skip.montanaro | 2006-11-06 15:34:52 +0100 (Mon, 06 Nov 2006) | 1 line note that user can control directory location even if default dir is used ........ r52644 | ronald.oussoren | 2006-11-07 16:53:38 +0100 (Tue, 07 Nov 2006) | 2 lines Fix a number of typos in strings and comments (sf#1589070) ........ r52647 | ronald.oussoren | 2006-11-07 17:00:34 +0100 (Tue, 07 Nov 2006) | 2 lines Whitespace changes to make the source more compliant with PEP8 (SF#1589070) ........ r52651 | thomas.heller | 2006-11-07 19:01:18 +0100 (Tue, 07 Nov 2006) | 3 lines Fix markup. Will backport to release25-maint. ........ r52653 | thomas.heller | 2006-11-07 19:20:47 +0100 (Tue, 07 Nov 2006) | 3 lines Fix grammatical error as well. Will backport to release25-maint. ........ r52657 | andrew.kuchling | 2006-11-07 21:39:16 +0100 (Tue, 07 Nov 2006) | 1 line Add missing word ........ r52662 | martin.v.loewis | 2006-11-08 07:46:37 +0100 (Wed, 08 Nov 2006) | 4 lines Correctly forward exception in instance_contains(). Fixes #1591996. Patch contributed by Neal Norwitz. Will backport. ........ r52664 | martin.v.loewis | 2006-11-08 07:48:36 +0100 (Wed, 08 Nov 2006) | 2 lines News entry for 52662. ........ r52665 | martin.v.loewis | 2006-11-08 08:35:55 +0100 (Wed, 08 Nov 2006) | 2 lines Patch #1351744: Add askyesnocancel helper for tkMessageBox. ........ r52666 | georg.brandl | 2006-11-08 08:45:59 +0100 (Wed, 08 Nov 2006) | 2 lines Patch #1592072: fix docs for return value of PyErr_CheckSignals. ........ r52668 | georg.brandl | 2006-11-08 11:04:29 +0100 (Wed, 08 Nov 2006) | 3 lines Bug #1592533: rename variable in heapq doc example, to avoid shadowing "sorted". ........ r52671 | andrew.kuchling | 2006-11-08 14:35:34 +0100 (Wed, 08 Nov 2006) | 1 line Add section on the functional module ........ r52672 | andrew.kuchling | 2006-11-08 15:14:30 +0100 (Wed, 08 Nov 2006) | 1 line Add section on operator module; make a few edits ........ r52673 | andrew.kuchling | 2006-11-08 15:24:03 +0100 (Wed, 08 Nov 2006) | 1 line Add table of contents; this required fixing a few headings. Some more smalle edits. ........ r52674 | andrew.kuchling | 2006-11-08 15:30:14 +0100 (Wed, 08 Nov 2006) | 1 line More edits ........ r52686 | martin.v.loewis | 2006-11-09 12:06:03 +0100 (Thu, 09 Nov 2006) | 3 lines Patch #838546: Make terminal become controlling in pty.fork(). Will backport to 2.5. ........ r52688 | martin.v.loewis | 2006-11-09 12:27:32 +0100 (Thu, 09 Nov 2006) | 2 lines Patch #1592250: Add elidge argument to Tkinter.Text.search. ........ r52690 | andrew.kuchling | 2006-11-09 14:27:07 +0100 (Thu, 09 Nov 2006) | 7 lines [Bug #1569790] mailbox.Maildir.get_folder() loses factory information Both the Maildir and MH classes had this bug; the patch fixes both classes and adds a test. Will backport to 25-maint. ........ r52692 | andrew.kuchling | 2006-11-09 14:51:14 +0100 (Thu, 09 Nov 2006) | 1 line [Patch #1514544 by David Watson] use fsync() to ensure data is really on disk ........ r52695 | walter.doerwald | 2006-11-09 17:23:26 +0100 (Thu, 09 Nov 2006) | 2 lines Replace C++ comment with C comment (fixes SF bug #1593525). ........ r52712 | andrew.kuchling | 2006-11-09 22:16:46 +0100 (Thu, 09 Nov 2006) | 11 lines [Patch #1514543] mailbox (Maildir): avoid losing messages on name clash Two changes: Where possible, use link()/remove() to move files into a directory; this makes it easier to avoid overwriting an existing file. Use _create_carefully() to create files in tmp/, which uses O_EXCL. Backport candidate. ........ r52716 | phillip.eby | 2006-11-10 01:33:36 +0100 (Fri, 10 Nov 2006) | 4 lines Fix SF#1566719: not creating site-packages (or other target directory) when installing .egg-info for a project that contains no modules or packages, while using --root (as in bdist_rpm). ........ r52719 | andrew.kuchling | 2006-11-10 14:14:01 +0100 (Fri, 10 Nov 2006) | 1 line Reword entry ........ r52725 | andrew.kuchling | 2006-11-10 15:39:01 +0100 (Fri, 10 Nov 2006) | 1 line [Feature request #1542920] Link to wsgi.org ........ r52731 | georg.brandl | 2006-11-11 19:29:11 +0100 (Sat, 11 Nov 2006) | 2 lines Bug #1594742: wrong word in stringobject doc. ........ r52733 | georg.brandl | 2006-11-11 19:32:47 +0100 (Sat, 11 Nov 2006) | 2 lines Bug #1594758: wording improvement for dict.update() docs. ........ r52736 | martin.v.loewis | 2006-11-12 11:32:47 +0100 (Sun, 12 Nov 2006) | 3 lines Patch #1065257: Support passing open files as body in HTTPConnection.request(). ........ r52737 | martin.v.loewis | 2006-11-12 11:41:39 +0100 (Sun, 12 Nov 2006) | 2 lines Patch #1355023: support whence argument for GzipFile.seek. ........ r52738 | martin.v.loewis | 2006-11-12 19:24:26 +0100 (Sun, 12 Nov 2006) | 2 lines Bug #1067760: Deprecate passing floats to file.seek. ........ r52739 | martin.v.loewis | 2006-11-12 19:48:13 +0100 (Sun, 12 Nov 2006) | 3 lines Patch #1359217: Ignore 2xx response before 150 response. Will backport to 2.5. ........ r52741 | martin.v.loewis | 2006-11-12 19:56:03 +0100 (Sun, 12 Nov 2006) | 4 lines Patch #1360200: Use unmangled_version RPM spec field to deal with file name mangling. Will backport to 2.5. ........ r52753 | walter.doerwald | 2006-11-15 17:23:46 +0100 (Wed, 15 Nov 2006) | 2 lines Fix typo. ........ r52754 | georg.brandl | 2006-11-15 18:42:03 +0100 (Wed, 15 Nov 2006) | 2 lines Bug #1594809: add a note to README regarding PYTHONPATH and make install. ........ r52762 | georg.brandl | 2006-11-16 16:05:14 +0100 (Thu, 16 Nov 2006) | 2 lines Bug #1597576: mention that the new base64 api has been introduced in py2.4. ........ r52764 | georg.brandl | 2006-11-16 17:50:59 +0100 (Thu, 16 Nov 2006) | 3 lines Bug #1597824: return the registered function from atexit.register() to facilitate usage as a decorator. ........ r52765 | georg.brandl | 2006-11-16 18:08:45 +0100 (Thu, 16 Nov 2006) | 4 lines Bug #1588217: don't parse "= " as a soft line break in binascii's a2b_qp() function, instead leave it in the string as quopri.decode() does. ........ r52776 | andrew.kuchling | 2006-11-17 14:30:25 +0100 (Fri, 17 Nov 2006) | 17 lines Remove file-locking in MH.pack() method. This change looks massive but it's mostly a re-indenting after removing some try...finally blocks. Also adds a test case that does a pack() while the mailbox is locked; this test would have turned up bugs in the original code on some platforms. In both nmh and GNU Mailutils' implementation of MH-format mailboxes, no locking is done of individual message files when renaming them. The original mailbox.py code did do locking, which meant that message files had to be opened. This code was buggy on certain platforms (found through reading the code); there were code paths that closed the file object and then called _unlock_file() on it. Will backport to 25-maint once I see how the buildbots react to this patch. ........ r52780 | martin.v.loewis | 2006-11-18 19:00:23 +0100 (Sat, 18 Nov 2006) | 5 lines Patch #1538878: Don't make tkSimpleDialog dialogs transient if the parent window is withdrawn. This mirrors what dialog.tcl does. Will backport to 2.5. ........ r52782 | martin.v.loewis | 2006-11-18 19:05:35 +0100 (Sat, 18 Nov 2006) | 4 lines Patch #1594554: Always close a tkSimpleDialog on ok(), even if an exception occurs. Will backport to 2.5. ........ r52784 | martin.v.loewis | 2006-11-18 19:42:11 +0100 (Sat, 18 Nov 2006) | 3 lines Patch #1472877: Fix Tix subwidget name resolution. Will backport to 2.5. ........ r52786 | andrew.kuchling | 2006-11-18 23:17:33 +0100 (Sat, 18 Nov 2006) | 1 line Expand checking in test_sha ........ r52787 | georg.brandl | 2006-11-19 09:48:30 +0100 (Sun, 19 Nov 2006) | 3 lines Patch [ 1586791 ] better error msgs for some TypeErrors ........ r52788 | martin.v.loewis | 2006-11-19 11:41:41 +0100 (Sun, 19 Nov 2006) | 4 lines Make cStringIO.truncate raise IOError for negative arguments (even for -1). Fixes the last bit of #1359365. ........ r52789 | andrew.kuchling | 2006-11-19 19:40:01 +0100 (Sun, 19 Nov 2006) | 1 line Add a test case of data w/ bytes > 127 ........ r52790 | martin.v.loewis | 2006-11-19 19:51:54 +0100 (Sun, 19 Nov 2006) | 3 lines Patch #1070046: Marshal new-style objects like InstanceType in xmlrpclib. ........ r52792 | neal.norwitz | 2006-11-19 22:26:53 +0100 (Sun, 19 Nov 2006) | 4 lines Speed up function calls into the math module by using METH_O. There should be no functional changes. However, the error msgs are slightly different. Also verified that the module dict is not NULL on init. ........ r52794 | george.yoshida | 2006-11-20 03:24:48 +0100 (Mon, 20 Nov 2006) | 2 lines markup fix ........ r52795 | georg.brandl | 2006-11-20 08:12:58 +0100 (Mon, 20 Nov 2006) | 3 lines Further markup fix. ........ r52800 | andrew.kuchling | 2006-11-20 14:39:37 +0100 (Mon, 20 Nov 2006) | 2 lines Jython compatibility fix: if uu.decode() opened its output file, be sure to close it. ........ r52811 | neal.norwitz | 2006-11-21 06:26:22 +0100 (Tue, 21 Nov 2006) | 9 lines Bug #1599782: Fix segfault on bsddb.db.DB().type(). The problem is that _DB_get_type() can't be called without the GIL because it calls a bunch of PyErr_* APIs when an error occurs. There were no other cases in this file that it was called without the GIL. Removing the BEGIN/END THREAD around _DB_get_type() made everything work. Will backport. ........ r52814 | neal.norwitz | 2006-11-21 06:51:51 +0100 (Tue, 21 Nov 2006) | 1 line Oops, convert tabs to spaces ........ r52815 | neal.norwitz | 2006-11-21 07:23:44 +0100 (Tue, 21 Nov 2006) | 1 line Fix SF #1599879, socket.gethostname should ref getfqdn directly. ........ r52817 | martin.v.loewis | 2006-11-21 19:20:25 +0100 (Tue, 21 Nov 2006) | 4 lines Conditionalize definition of _CRT_SECURE_NO_DEPRECATE and _CRT_NONSTDC_NO_DEPRECATE. Will backport. ........ r52821 | martin.v.loewis | 2006-11-22 09:50:02 +0100 (Wed, 22 Nov 2006) | 4 lines Patch #1362975: Rework CodeContext indentation algorithm to avoid hard-coding pixel widths. Also make the text's scrollbar a child of the text frame, not the top widget. ........ r52826 | walter.doerwald | 2006-11-23 06:03:56 +0100 (Thu, 23 Nov 2006) | 3 lines Change decode() so that it works with a buffer (i.e. unicode(..., 'utf-8-sig')) SF bug #1601501. ........ r52833 | georg.brandl | 2006-11-23 10:55:07 +0100 (Thu, 23 Nov 2006) | 2 lines Bug #1601630: little improvement to getopt docs ........ r52835 | michael.hudson | 2006-11-23 14:54:04 +0100 (Thu, 23 Nov 2006) | 3 lines a test for an error condition not covered by existing tests (noticed this when writing the equivalent code for pypy) ........ r52839 | raymond.hettinger | 2006-11-23 22:06:03 +0100 (Thu, 23 Nov 2006) | 1 line Fix and/add typo ........ r52840 | raymond.hettinger | 2006-11-23 22:35:19 +0100 (Thu, 23 Nov 2006) | 1 line ... and the number of the counting shall be three. ........ r52841 | thomas.heller | 2006-11-24 19:45:39 +0100 (Fri, 24 Nov 2006) | 1 line Fix bug #1598620: A ctypes structure cannot contain itself. ........ r52843 | martin.v.loewis | 2006-11-25 16:39:19 +0100 (Sat, 25 Nov 2006) | 3 lines Disable _XOPEN_SOURCE on NetBSD 1.x. Will backport to 2.5 ........ r52845 | georg.brandl | 2006-11-26 20:27:47 +0100 (Sun, 26 Nov 2006) | 2 lines Bug #1603321: make pstats.Stats accept Unicode file paths. ........ r52850 | georg.brandl | 2006-11-27 19:46:21 +0100 (Mon, 27 Nov 2006) | 2 lines Bug #1603789: grammatical error in Tkinter docs. ........ r52855 | thomas.heller | 2006-11-28 21:21:54 +0100 (Tue, 28 Nov 2006) | 7 lines Fix #1563807: _ctypes built on AIX fails with ld ffi error. The contents of ffi_darwin.c must be compiled unless __APPLE__ is defined and __ppc__ is not. Will backport. ........ r52862 | armin.rigo | 2006-11-29 22:59:22 +0100 (Wed, 29 Nov 2006) | 3 lines Forgot a case where the locals can now be a general mapping instead of just a dictionary. (backporting...) ........ r52872 | guido.van.rossum | 2006-11-30 20:23:13 +0100 (Thu, 30 Nov 2006) | 2 lines Update version. ........ r52890 | walter.doerwald | 2006-12-01 17:59:47 +0100 (Fri, 01 Dec 2006) | 3 lines Move xdrlib tests from the module into a separate test script, port the tests to unittest and add a few new tests. ........ r52900 | raymond.hettinger | 2006-12-02 03:00:39 +0100 (Sat, 02 Dec 2006) | 1 line Add name to credits (for untokenize). ........ r52905 | martin.v.loewis | 2006-12-03 10:54:46 +0100 (Sun, 03 Dec 2006) | 2 lines Move IDLE news into NEWS.txt. ........ r52906 | martin.v.loewis | 2006-12-03 12:23:45 +0100 (Sun, 03 Dec 2006) | 4 lines Patch #1544279: Improve thread-safety of the socket module by moving the sock_addr_t storage out of the socket object. Will backport to 2.5. ........ r52908 | martin.v.loewis | 2006-12-03 13:01:53 +0100 (Sun, 03 Dec 2006) | 3 lines Patch #1371075: Make ConfigParser accept optional dict type for ordering, sorting, etc. ........ r52910 | matthias.klose | 2006-12-03 18:16:41 +0100 (Sun, 03 Dec 2006) | 2 lines - Fix build failure on kfreebsd and on the hurd. ........ r52915 | george.yoshida | 2006-12-04 12:41:54 +0100 (Mon, 04 Dec 2006) | 2 lines fix a versionchanged tag ........ r52917 | george.yoshida | 2006-12-05 06:39:50 +0100 (Tue, 05 Dec 2006) | 3 lines Fix pickle doc typo Patch #1608758 ........ r52938 | georg.brandl | 2006-12-06 23:21:18 +0100 (Wed, 06 Dec 2006) | 2 lines Patch #1610437: fix a tarfile bug with long filename headers. ........ r52945 | brett.cannon | 2006-12-07 00:38:48 +0100 (Thu, 07 Dec 2006) | 3 lines Fix a bad assumption that all objects assigned to '__loader__' on a module will have a '_files' attribute. ........ r52951 | georg.brandl | 2006-12-07 10:30:06 +0100 (Thu, 07 Dec 2006) | 3 lines RFE #1592899: mention string.maketrans() in docs for str.translate, remove reference to the old regex module in the former's doc. ........ r52962 | raymond.hettinger | 2006-12-08 04:17:18 +0100 (Fri, 08 Dec 2006) | 1 line Eliminate two redundant calls to PyObject_Hash(). ........ r52963 | raymond.hettinger | 2006-12-08 05:24:33 +0100 (Fri, 08 Dec 2006) | 3 lines Port Armin's fix for a dict resize vulnerability (svn revision 46589, sf bug 1456209). ........ r52964 | raymond.hettinger | 2006-12-08 05:57:50 +0100 (Fri, 08 Dec 2006) | 4 lines Port Georg's dictobject.c fix keys that were tuples got unpacked on the way to setting a KeyError (svn revision 52535, sf bug 1576657). ........ r52966 | raymond.hettinger | 2006-12-08 18:35:25 +0100 (Fri, 08 Dec 2006) | 2 lines Add test for SF bug 1576657 ........ r52970 | georg.brandl | 2006-12-08 21:46:11 +0100 (Fri, 08 Dec 2006) | 3 lines #1577756: svnversion doesn't react to LANG=C, use LC_ALL=C to force English output. ........ r52972 | georg.brandl | 2006-12-09 10:08:29 +0100 (Sat, 09 Dec 2006) | 3 lines Patch #1608267: fix a race condition in os.makedirs() is the directory to be created is already there. ........ r52975 | matthias.klose | 2006-12-09 13:15:27 +0100 (Sat, 09 Dec 2006) | 2 lines - Fix the build of the library reference in info format. ........ r52994 | neal.norwitz | 2006-12-11 02:01:06 +0100 (Mon, 11 Dec 2006) | 1 line Fix a typo ........ r52996 | georg.brandl | 2006-12-11 08:56:33 +0100 (Mon, 11 Dec 2006) | 2 lines Move errno imports back to individual functions. ........ r52998 | vinay.sajip | 2006-12-11 15:07:16 +0100 (Mon, 11 Dec 2006) | 1 line Patch by Jeremy Katz (SF #1609407) ........ r53000 | vinay.sajip | 2006-12-11 15:26:23 +0100 (Mon, 11 Dec 2006) | 1 line Patch by "cuppatea" (SF #1503765) ........ --- command/bdist_rpm.py | 67 ++++++++++++++++++++++++++------------------ command/build_ext.py | 2 +- command/install_egg_info.py | 3 ++ command/register.py | 2 +- command/wininst-8.exe | Bin 0 -> 61440 bytes sysconfig.py | 5 +++- unixccompiler.py | 16 +++++++++++ 7 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 command/wininst-8.exe diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 5b099658..6f0e0d88 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -337,37 +337,47 @@ class bdist_rpm (Command): if not self.keep_temp: rpm_cmd.append('--clean') rpm_cmd.append(spec_path) + # Determine the binary rpm names that should be built out of this spec + # file + # Note that some of these may not be really built (if the file + # list is empty) + nvr_string = "%{name}-%{version}-%{release}" + src_rpm = nvr_string + ".src.rpm" + non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" + q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % ( + src_rpm, non_src_rpm, spec_path) + + out = os.popen(q_cmd) + binary_rpms = [] + source_rpm = None + while 1: + line = out.readline() + if not line: + break + l = string.split(string.strip(line)) + assert(len(l) == 2) + binary_rpms.append(l[1]) + # The source rpm is named after the first entry in the spec file + if source_rpm is None: + source_rpm = l[0] + + status = out.close() + if status: + raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + self.spawn(rpm_cmd) - # XXX this is a nasty hack -- we really should have a proper way to - # find out the names of the RPM files created; also, this assumes - # that RPM creates exactly one source and one binary RPM. if not self.dry_run: if not self.binary_only: - srpms = glob.glob(os.path.join(rpm_dir['SRPMS'], "*.rpm")) - assert len(srpms) == 1, \ - "unexpected number of SRPM files found: %s" % srpms - dist_file = ('bdist_rpm', 'any', - self._dist_path(srpms[0])) - self.distribution.dist_files.append(dist_file) - self.move_file(srpms[0], self.dist_dir) + srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) + assert(os.path.exists(srpm)) + self.move_file(srpm, self.dist_dir) if not self.source_only: - rpms = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*.rpm")) - debuginfo = glob.glob(os.path.join(rpm_dir['RPMS'], - "*/*debuginfo*.rpm")) - if debuginfo: - rpms.remove(debuginfo[0]) - assert len(rpms) == 1, \ - "unexpected number of RPM files found: %s" % rpms - dist_file = ('bdist_rpm', get_python_version(), - self._dist_path(rpms[0])) - self.distribution.dist_files.append(dist_file) - self.move_file(rpms[0], self.dist_dir) - if debuginfo: - dist_file = ('bdist_rpm', get_python_version(), - self._dist_path(debuginfo[0])) - self.move_file(debuginfo[0], self.dist_dir) + for rpm in binary_rpms: + rpm = os.path.join(rpm_dir['RPMS'], rpm) + if os.path.exists(rpm): + self.move_file(rpm, self.dist_dir) # run() def _dist_path(self, path): @@ -381,6 +391,7 @@ class bdist_rpm (Command): spec_file = [ '%define name ' + self.distribution.get_name(), '%define version ' + self.distribution.get_version().replace('-','_'), + '%define unmangled_version ' + self.distribution.get_version(), '%define release ' + self.release.replace('-','_'), '', 'Summary: ' + self.distribution.get_description(), @@ -402,9 +413,9 @@ class bdist_rpm (Command): # but only after it has run: and we create the spec file before # running "sdist", in case of --spec-only. if self.use_bzip2: - spec_file.append('Source0: %{name}-%{version}.tar.bz2') + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') else: - spec_file.append('Source0: %{name}-%{version}.tar.gz') + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') spec_file.extend([ 'License: ' + self.distribution.get_license(), @@ -479,7 +490,7 @@ class bdist_rpm (Command): # are just text that we drop in as-is. Hmmm. script_options = [ - ('prep', 'prep_script', "%setup"), + ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), ('build', 'build_script', def_build), ('install', 'install_script', ("%s install " diff --git a/command/build_ext.py b/command/build_ext.py index cd67544b..f79eee3b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos' or \ - (sys.platform.startswith('linux') and + ((sys.platform.startswith('linux') or sys.platform.startswith('gnu')) and sysconfig.get_config_var('Py_ENABLE_SHARED')): if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions diff --git a/command/install_egg_info.py b/command/install_egg_info.py index c31ac296..c8880310 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -35,6 +35,9 @@ class install_egg_info(Command): dir_util.remove_tree(target, dry_run=self.dry_run) elif os.path.exists(target): self.execute(os.unlink,(self.target,),"Removing "+target) + elif not os.path.isdir(self.install_dir): + self.execute(os.makedirs, (self.install_dir,), + "Creating "+self.install_dir) log.info("Writing %s", target) if not self.dry_run: f = open(target, 'w') diff --git a/command/register.py b/command/register.py index f8912621..3177476b 100644 --- a/command/register.py +++ b/command/register.py @@ -256,7 +256,7 @@ Your selection [default 1]: ''', body = StringIO.StringIO() for key, value in data.items(): # handle multiple entries for the same name - if type(value) != type([]): + if type(value) not in (type([]), type( () )): value = [value] for value in value: value = unicode(value).encode("utf-8") diff --git a/command/wininst-8.exe b/command/wininst-8.exe new file mode 100644 index 00000000..7403bfab Binary files /dev/null and b/command/wininst-8.exe differ diff --git a/sysconfig.py b/sysconfig.py index 96923bdc..8989d92b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -509,7 +509,10 @@ def get_config_vars(*args): # are in CFLAGS or LDFLAGS and remove them if they are. # This is needed when building extensions on a 10.3 system # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS'): + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): flags = _config_vars[key] flags = re.sub('-arch\s+\w+\s', ' ', flags) flags = re.sub('-isysroot [^ \t]*', ' ', flags) diff --git a/unixccompiler.py b/unixccompiler.py index 6cd14f77..75e8a531 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -82,6 +82,22 @@ def _darwin_compiler_fixup(compiler_so, cc_args): except ValueError: pass + # Check if the SDK that is used during compilation actually exists, + # the universal build requires the usage of a universal SDK and not all + # users have that installed by default. + sysroot = None + if '-isysroot' in cc_args: + idx = cc_args.index('-isysroot') + sysroot = cc_args[idx+1] + elif '-isysroot' in compiler_so: + idx = compiler_so.index('-isysroot') + sysroot = compiler_so[idx+1] + + if sysroot and not os.path.isdir(sysroot): + log.warn("Compiling with an SDK that doesn't seem to exist: %s", + sysroot) + log.warn("Please check your Xcode installation") + return compiler_so class UnixCCompiler(CCompiler): -- cgit v1.2.1 From ba04afa355a9b33a8dc4e52e11de3b11cae13afd Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 10 Jan 2007 16:19:56 +0000 Subject: SF patch 1631942 by Collin Winter: (a) "except E, V" -> "except E as V" (b) V is now limited to a simple name (local variable) (c) V is now deleted at the end of the except block --- bcppcompiler.py | 10 +++++----- command/register.py | 4 ++-- command/sdist.py | 2 +- command/upload.py | 2 +- core.py | 8 ++++---- cygwinccompiler.py | 6 +++--- dir_util.py | 7 ++++--- dist.py | 6 +++--- emxccompiler.py | 6 +++--- fancy_getopt.py | 2 +- file_util.py | 23 +++++++++++++++-------- msvccompiler.py | 12 ++++++------ spawn.py | 8 ++++---- sysconfig.py | 4 ++-- unixccompiler.py | 8 ++++---- util.py | 2 +- 16 files changed, 59 insertions(+), 51 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index ca524a5b..6968cb8f 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -115,7 +115,7 @@ class BCPPCompiler(CCompiler) : # This needs to be compiled to a .res file -- do it now. try: self.spawn (["brcc32", "-fo", obj, src]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg continue # the 'for' loop @@ -139,7 +139,7 @@ class BCPPCompiler(CCompiler) : self.spawn ([self.cc] + compile_opts + pp_opts + [input_opt, output_opt] + extra_postargs + [src]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg return objects @@ -164,7 +164,7 @@ class BCPPCompiler(CCompiler) : pass # XXX what goes here? try: self.spawn ([self.lib] + lib_args) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise LibError, msg else: log.debug("skipping %s (up-to-date)", output_filename) @@ -298,7 +298,7 @@ class BCPPCompiler(CCompiler) : self.mkpath (os.path.dirname (output_filename)) try: self.spawn ([self.linker] + ld_args) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise LinkError, msg else: @@ -391,7 +391,7 @@ class BCPPCompiler(CCompiler) : self.mkpath(os.path.dirname(output_file)) try: self.spawn(pp_args) - except DistutilsExecError, msg: + except DistutilsExecError as msg: print msg raise CompileError, msg diff --git a/command/register.py b/command/register.py index 3177476b..cb9525ab 100644 --- a/command/register.py +++ b/command/register.py @@ -284,11 +284,11 @@ Your selection [default 1]: ''', data = '' try: result = opener.open(req) - except urllib2.HTTPError, e: + except urllib2.HTTPError as e: if self.show_response: data = e.fp.read() result = e.code, e.msg - except urllib2.URLError, e: + except urllib2.URLError as e: result = 500, str(e) else: if self.show_response: diff --git a/command/sdist.py b/command/sdist.py index 3dfe6f21..eb2db505 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -333,7 +333,7 @@ class sdist (Command): try: self.filelist.process_template_line(line) - except DistutilsTemplateError, msg: + except DistutilsTemplateError as msg: self.warn("%s, line %d: %s" % (template.filename, template.current_line, msg)) diff --git a/command/upload.py b/command/upload.py index 67ba0804..7f96a475 100644 --- a/command/upload.py +++ b/command/upload.py @@ -184,7 +184,7 @@ class upload(Command): http.putheader('Authorization', auth) http.endheaders() http.send(body) - except socket.error, e: + except socket.error as e: self.announce(str(e), log.ERROR) return diff --git a/core.py b/core.py index 85a28fe7..4dc8eb01 100644 --- a/core.py +++ b/core.py @@ -110,7 +110,7 @@ def setup (**attrs): # (ie. everything except distclass) to initialize it try: _setup_distribution = dist = klass(attrs) - except DistutilsSetupError, msg: + except DistutilsSetupError as msg: if 'name' not in attrs: raise SystemExit, "error in %s setup command: %s" % \ (attrs['name'], msg) @@ -135,7 +135,7 @@ def setup (**attrs): # fault, so turn them into SystemExit to suppress tracebacks. try: ok = dist.parse_command_line() - except DistutilsArgError, msg: + except DistutilsArgError as msg: raise SystemExit, gen_usage(dist.script_name) + "\nerror: %s" % msg if DEBUG: @@ -151,7 +151,7 @@ def setup (**attrs): dist.run_commands() except KeyboardInterrupt: raise SystemExit, "interrupted" - except (IOError, os.error), exc: + except (IOError, os.error) as exc: error = grok_environment_error(exc) if DEBUG: @@ -161,7 +161,7 @@ def setup (**attrs): raise SystemExit, error except (DistutilsError, - CCompilerError), msg: + CCompilerError) as msg: if DEBUG: raise else: diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 4fd23e6d..44b58508 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -142,13 +142,13 @@ class CygwinCCompiler (UnixCCompiler): # gcc needs '.res' and '.rc' compiled to object files !!! try: self.spawn(["windres", "-i", src, "-o", obj]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg else: # for other files use the C-compiler try: self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg def link (self, @@ -379,7 +379,7 @@ def check_config_h(): s = f.read() f.close() - except IOError, exc: + except IOError as exc: # if we can't read this file, we cannot say it is wrong # the compiler will complain later about this file as missing return (CONFIG_H_UNCERTAIN, diff --git a/dir_util.py b/dir_util.py index 92f49346..398f2429 100644 --- a/dir_util.py +++ b/dir_util.py @@ -75,7 +75,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): try: os.mkdir(head) created_dirs.append(head) - except OSError, exc: + except OSError as exc: raise DistutilsFileError, \ "could not create '%s': %s" % (head, exc[-1]) @@ -142,7 +142,8 @@ def copy_tree (src, dst, "cannot copy tree '%s': not a directory" % src try: names = os.listdir(src) - except os.error, (errno, errstr): + except os.error as e: + (errno, errstr) = e if dry_run: names = [] else: @@ -209,7 +210,7 @@ def remove_tree (directory, verbose=0, dry_run=0): abspath = os.path.abspath(cmd[1]) if abspath in _path_created: del _path_created[abspath] - except (IOError, OSError), exc: + except (IOError, OSError) as exc: log.warn(grok_environment_error( exc, "error removing %s: " % directory)) diff --git a/dist.py b/dist.py index d098cb97..d21c5e2f 100644 --- a/dist.py +++ b/dist.py @@ -398,7 +398,7 @@ Common commands: (see '--help-commands' for more) setattr(self, opt, strtobool(val)) else: setattr(self, opt, val) - except ValueError, msg: + except ValueError as msg: raise DistutilsOptionError, msg # parse_config_files () @@ -515,7 +515,7 @@ Common commands: (see '--help-commands' for more) # it takes. try: cmd_class = self.get_command_class(command) - except DistutilsModuleError, msg: + except DistutilsModuleError as msg: raise DistutilsArgError, msg # Require that the command class be derived from Command -- want @@ -917,7 +917,7 @@ Common commands: (see '--help-commands' for more) raise DistutilsOptionError, \ ("error in %s: command '%s' has no such option '%s'" % (source, command_name, option)) - except ValueError, msg: + except ValueError as msg: raise DistutilsOptionError, msg def reinitialize_command (self, command, reinit_subcommands=0): diff --git a/emxccompiler.py b/emxccompiler.py index f52e6323..8aef2b7a 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -79,13 +79,13 @@ class EMXCCompiler (UnixCCompiler): # gcc requires '.rc' compiled to binary ('.res') files !!! try: self.spawn(["rc", "-r", src]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg else: # for other files use the C-compiler try: self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg def link (self, @@ -275,7 +275,7 @@ def check_config_h(): s = f.read() f.close() - except IOError, exc: + except IOError as exc: # if we can't read this file, we cannot say it is wrong # the compiler will complain later about this file as missing return (CONFIG_H_UNCERTAIN, diff --git a/fancy_getopt.py b/fancy_getopt.py index 31cf0c5c..62a24e85 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -256,7 +256,7 @@ class FancyGetopt: short_opts = string.join(self.short_opts) try: opts, args = getopt.getopt(args, short_opts, self.long_opts) - except getopt.error, msg: + except getopt.error as msg: raise DistutilsArgError, msg for opt, val in opts: diff --git a/file_util.py b/file_util.py index 37b152ed..c225ad31 100644 --- a/file_util.py +++ b/file_util.py @@ -32,27 +32,31 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): try: try: fsrc = open(src, 'rb') - except os.error, (errno, errstr): + except os.error as e: + (errno, errstr) = e raise DistutilsFileError, \ "could not open '%s': %s" % (src, errstr) if os.path.exists(dst): try: os.unlink(dst) - except os.error, (errno, errstr): + except os.error as e: + (errno, errstr) = e raise DistutilsFileError, \ "could not delete '%s': %s" % (dst, errstr) try: fdst = open(dst, 'wb') - except os.error, (errno, errstr): + except os.error as e: + (errno, errstr) = e raise DistutilsFileError, \ "could not create '%s': %s" % (dst, errstr) while 1: try: buf = fsrc.read(buffer_size) - except os.error, (errno, errstr): + except os.error as e: + (errno, errstr) = e raise DistutilsFileError, \ "could not read from '%s': %s" % (src, errstr) @@ -61,7 +65,8 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): try: fdst.write(buf) - except os.error, (errno, errstr): + except os.error as e: + (errno, errstr) = e raise DistutilsFileError, \ "could not write to '%s': %s" % (dst, errstr) @@ -146,7 +151,7 @@ def copy_file (src, dst, import macostools try: macostools.copy(src, dst, 0, preserve_times) - except os.error, exc: + except os.error as exc: raise DistutilsFileError, \ "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) @@ -217,7 +222,8 @@ def move_file (src, dst, copy_it = 0 try: os.rename(src, dst) - except os.error, (num, msg): + except os.error as e: + (num, msg) = e if num == errno.EXDEV: copy_it = 1 else: @@ -228,7 +234,8 @@ def move_file (src, dst, copy_file(src, dst) try: os.unlink(src) - except os.error, (num, msg): + except os.error as e: + (num, msg) = e try: os.unlink(dst) except os.error: diff --git a/msvccompiler.py b/msvccompiler.py index 9ec3508c..968a4ffd 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -129,7 +129,7 @@ class MacroExpander: self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") else: self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") - except KeyError, exc: # + except KeyError as exc: # raise DistutilsPlatformError, \ ("""Python was built with Visual Studio 2003; extensions must be built with a compiler than can generate compatible binaries. @@ -371,7 +371,7 @@ class MSVCCompiler (CCompiler) : try: self.spawn ([self.rc] + pp_opts + [output_opt] + [input_opt]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg continue elif ext in self._mc_extensions: @@ -400,7 +400,7 @@ class MSVCCompiler (CCompiler) : self.spawn ([self.rc] + ["/fo" + obj] + [rc_file]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg continue else: @@ -414,7 +414,7 @@ class MSVCCompiler (CCompiler) : self.spawn ([self.cc] + compile_opts + pp_opts + [input_opt, output_opt] + extra_postargs) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg return objects @@ -440,7 +440,7 @@ class MSVCCompiler (CCompiler) : pass # XXX what goes here? try: self.spawn ([self.lib] + lib_args) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise LibError, msg else: @@ -519,7 +519,7 @@ class MSVCCompiler (CCompiler) : self.mkpath (os.path.dirname (output_filename)) try: self.spawn ([self.linker] + ld_args) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise LinkError, msg else: diff --git a/spawn.py b/spawn.py index e5654ff0..6b07f52e 100644 --- a/spawn.py +++ b/spawn.py @@ -78,7 +78,7 @@ def _spawn_nt (cmd, # spawn for NT requires a full path to the .exe try: rc = os.spawnv(os.P_WAIT, executable, cmd) - except OSError, exc: + except OSError as exc: # this seems to happen when the command isn't found raise DistutilsExecError, \ "command '%s' failed: %s" % (cmd[0], exc[-1]) @@ -103,7 +103,7 @@ def _spawn_os2 (cmd, # spawnv for OS/2 EMX requires a full path to the .exe try: rc = os.spawnv(os.P_WAIT, executable, cmd) - except OSError, exc: + except OSError as exc: # this seems to happen when the command isn't found raise DistutilsExecError, \ "command '%s' failed: %s" % (cmd[0], exc[-1]) @@ -131,7 +131,7 @@ def _spawn_posix (cmd, #print "cmd[0] =", cmd[0] #print "cmd =", cmd exec_fn(cmd[0], cmd) - except OSError, e: + except OSError as e: sys.stderr.write("unable to execute %s: %s\n" % (cmd[0], e.strerror)) os._exit(1) @@ -146,7 +146,7 @@ def _spawn_posix (cmd, while 1: try: (pid, status) = os.waitpid(pid, 0) - except OSError, exc: + except OSError as exc: import errno if exc.errno == errno.EINTR: continue diff --git a/sysconfig.py b/sysconfig.py index 8989d92b..9de72346 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -344,7 +344,7 @@ def _init_posix(): try: filename = get_makefile_filename() parse_makefile(filename, g) - except IOError, msg: + except IOError as msg: my_msg = "invalid Python installation: unable to open %s" % filename if hasattr(msg, "strerror"): my_msg = my_msg + " (%s)" % msg.strerror @@ -355,7 +355,7 @@ def _init_posix(): try: filename = get_config_h_filename() parse_config_h(open(filename), g) - except IOError, msg: + except IOError as msg: my_msg = "invalid Python installation: unable to open %s" % filename if hasattr(msg, "strerror"): my_msg = my_msg + " (%s)" % msg.strerror diff --git a/unixccompiler.py b/unixccompiler.py index 75e8a531..0795f120 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -162,7 +162,7 @@ class UnixCCompiler(CCompiler): self.mkpath(os.path.dirname(output_file)) try: self.spawn(pp_args) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): @@ -172,7 +172,7 @@ class UnixCCompiler(CCompiler): try: self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg def create_static_lib(self, objects, output_libname, @@ -196,7 +196,7 @@ class UnixCCompiler(CCompiler): if self.ranlib: try: self.spawn(self.ranlib + [output_filename]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise LibError, msg else: log.debug("skipping %s (up-to-date)", output_filename) @@ -250,7 +250,7 @@ class UnixCCompiler(CCompiler): linker = _darwin_compiler_fixup(linker, ld_args) self.spawn(linker + ld_args) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise LinkError, msg else: log.debug("skipping %s (up-to-date)", output_filename) diff --git a/util.py b/util.py index 1bcda93f..16d8bcba 100644 --- a/util.py +++ b/util.py @@ -229,7 +229,7 @@ def subst_vars (s, local_vars): try: return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) - except KeyError, var: + except KeyError as var: raise ValueError, "invalid variable '$%s'" % var # subst_vars () -- cgit v1.2.1 From 4e0a58cc6113aa5ab73196de13b16d500ff46fe6 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 9 Feb 2007 05:37:30 +0000 Subject: Fix most trivially-findable print statements. There's one major and one minor category still unfixed: doctests are the major category (and I hope to be able to augment the refactoring tool to refactor bona fide doctests soon); other code generating print statements in strings is the minor category. (Oh, and I don't know if the compiler package works.) --- bcppcompiler.py | 2 +- ccompiler.py | 2 +- cmd.py | 8 ++++---- command/bdist_rpm.py | 10 +++++----- command/config.py | 4 ++-- command/install.py | 6 +++--- command/register.py | 30 +++++++++++++++--------------- command/upload.py | 2 +- core.py | 4 ++-- dist.py | 48 ++++++++++++++++++++++++------------------------ fancy_getopt.py | 6 +++--- filelist.py | 2 +- log.py | 4 ++-- mwerkscompiler.py | 4 ++-- spawn.py | 2 +- tests/test_dist.py | 4 ++-- text_file.py | 12 ++++++------ 17 files changed, 75 insertions(+), 75 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 6968cb8f..b6a3bf62 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -392,7 +392,7 @@ class BCPPCompiler(CCompiler) : try: self.spawn(pp_args) except DistutilsExecError as msg: - print msg + print(msg) raise CompileError, msg # preprocess() diff --git a/ccompiler.py b/ccompiler.py index 0ed9a40a..25d90c8a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1026,7 +1026,7 @@ main (int argc, char **argv) { def debug_print (self, msg): from distutils.debug import DEBUG if DEBUG: - print msg + print(msg) def warn (self, msg): sys.stderr.write ("warning: %s\n" % msg) diff --git a/cmd.py b/cmd.py index 3cd58589..be0a3e23 100644 --- a/cmd.py +++ b/cmd.py @@ -163,14 +163,14 @@ class Command: from distutils.fancy_getopt import longopt_xlate if header is None: header = "command options for '%s':" % self.get_command_name() - print indent + header + print(indent + header) indent = indent + " " for (option, _, _) in self.user_options: option = string.translate(option, longopt_xlate) if option[-1] == "=": option = option[:-1] value = getattr(self, option) - print indent + "%s = %s" % (option, value) + print(indent + "%s = %s" % (option, value)) def run (self): @@ -199,7 +199,7 @@ class Command: """ from distutils.debug import DEBUG if DEBUG: - print msg + print(msg) sys.stdout.flush() @@ -475,4 +475,4 @@ class install_misc (Command): if __name__ == "__main__": - print "ok" + print("ok") diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 6f0e0d88..bbcf2927 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -267,11 +267,11 @@ class bdist_rpm (Command): def run (self): if DEBUG: - print "before _get_package_data():" - print "vendor =", self.vendor - print "packager =", self.packager - print "doc_files =", self.doc_files - print "changelog =", self.changelog + print("before _get_package_data():") + print("vendor =", self.vendor) + print("packager =", self.packager) + print("doc_files =", self.doc_files) + print("changelog =", self.changelog) # make directories if self.spec_only: diff --git a/command/config.py b/command/config.py index 520c1b0c..42465693 100644 --- a/command/config.py +++ b/command/config.py @@ -359,9 +359,9 @@ class config (Command): def dump_file (filename, head=None): if head is None: - print filename + ":" + print(filename + ":") else: - print head + print(head) file = open(filename) sys.stdout.write(file.read()) diff --git a/command/install.py b/command/install.py index 453151d0..fd5d6d1f 100644 --- a/command/install.py +++ b/command/install.py @@ -292,7 +292,7 @@ class install (Command): if DEBUG: from pprint import pprint - print "config vars:" + print("config vars:") pprint(self.config_vars) # Expand "~" and configuration variables in the installation @@ -347,7 +347,7 @@ class install (Command): def dump_dirs (self, msg): if DEBUG: from distutils.fancy_getopt import longopt_xlate - print msg + ":" + print(msg + ":") for opt in self.user_options: opt_name = opt[0] if opt_name[-1] == "=": @@ -359,7 +359,7 @@ class install (Command): else: opt_name = string.translate(opt_name, longopt_xlate) val = getattr(self, opt_name) - print " %s: %s" % (opt_name, val) + print(" %s: %s" % (opt_name, val)) def finalize_unix (self): diff --git a/command/register.py b/command/register.py index cb9525ab..48070ee8 100644 --- a/command/register.py +++ b/command/register.py @@ -86,14 +86,14 @@ class register(Command): ''' Fetch the list of classifiers from the server. ''' response = urllib2.urlopen(self.repository+'?:action=list_classifiers') - print response.read() + print(response.read()) def verify_metadata(self): ''' Send the metadata to the package index server to be checked. ''' # send the info to the server and report the result (code, result) = self.post_to_server(self.build_post_data('verify')) - print 'Server response (%s): %s'%(code, result) + print('Server response (%s): %s'%(code, result)) def send_metadata(self): ''' Send the metadata to the package index server. @@ -128,7 +128,7 @@ class register(Command): if os.environ.has_key('HOME'): rc = os.path.join(os.environ['HOME'], '.pypirc') if os.path.exists(rc): - print 'Using PyPI login from %s'%rc + print('Using PyPI login from %s'%rc) config = ConfigParser.ConfigParser() config.read(rc) username = config.get('server-login', 'username') @@ -138,17 +138,17 @@ class register(Command): # get the user's login info choices = '1 2 3 4'.split() while choice not in choices: - print '''We need to know who you are, so please choose either: + print('''We need to know who you are, so please choose either: 1. use your existing login, 2. register as a new user, 3. have the server generate a new password for you (and email it to you), or 4. quit -Your selection [default 1]: ''', +Your selection [default 1]: ''', end=' ') choice = raw_input() if not choice: choice = '1' elif choice not in choices: - print 'Please choose one of the four options!' + print('Please choose one of the four options!') if choice == '1': # get the username and password @@ -165,13 +165,13 @@ Your selection [default 1]: ''', # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) - print 'Server response (%s): %s'%(code, result) + print('Server response (%s): %s'%(code, result)) # possibly save the login if os.environ.has_key('HOME') and config is None and code == 200: rc = os.path.join(os.environ['HOME'], '.pypirc') - print 'I can store your PyPI login so future submissions will be faster.' - print '(the login will be stored in %s)'%rc + print('I can store your PyPI login so future submissions will be faster.') + print('(the login will be stored in %s)'%rc) choice = 'X' while choice.lower() not in 'yn': choice = raw_input('Save your login (y/N)?') @@ -200,22 +200,22 @@ Your selection [default 1]: ''', if data['password'] != data['confirm']: data['password'] = '' data['confirm'] = None - print "Password and confirm don't match!" + print("Password and confirm don't match!") while not data['email']: data['email'] = raw_input(' EMail: ') code, result = self.post_to_server(data) if code != 200: - print 'Server response (%s): %s'%(code, result) + print('Server response (%s): %s'%(code, result)) else: - print 'You will receive an email shortly.' - print 'Follow the instructions in it to complete registration.' + print('You will receive an email shortly.') + print('Follow the instructions in it to complete registration.') elif choice == '3': data = {':action': 'password_reset'} data['email'] = '' while not data['email']: data['email'] = raw_input('Your email address: ') code, result = self.post_to_server(data) - print 'Server response (%s): %s'%(code, result) + print('Server response (%s): %s'%(code, result)) def build_post_data(self, action): # figure the data to send - the metadata plus some additional @@ -295,5 +295,5 @@ Your selection [default 1]: ''', data = result.read() result = 200, 'OK' if self.show_response: - print '-'*75, data, '-'*75 + print('-'*75, data, '-'*75) return result diff --git a/command/upload.py b/command/upload.py index 7f96a475..438ae99a 100644 --- a/command/upload.py +++ b/command/upload.py @@ -196,4 +196,4 @@ class upload(Command): self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - print '-'*75, r.read(), '-'*75 + print('-'*75, r.read(), '-'*75) diff --git a/core.py b/core.py index 4dc8eb01..6242775c 100644 --- a/core.py +++ b/core.py @@ -125,7 +125,7 @@ def setup (**attrs): dist.parse_config_files() if DEBUG: - print "options (after parsing config files):" + print("options (after parsing config files):") dist.dump_option_dicts() if _setup_stop_after == "config": @@ -139,7 +139,7 @@ def setup (**attrs): raise SystemExit, gen_usage(dist.script_name) + "\nerror: %s" % msg if DEBUG: - print "options (after parsing command line):" + print("options (after parsing command line):") dist.dump_option_dicts() if _setup_stop_after == "commandline": diff --git a/dist.py b/dist.py index d21c5e2f..ca7a2f99 100644 --- a/dist.py +++ b/dist.py @@ -290,22 +290,22 @@ Common commands: (see '--help-commands' for more) commands.sort() if header is not None: - print indent + header + print(indent + header) indent = indent + " " if not commands: - print indent + "no commands known yet" + print(indent + "no commands known yet") return for cmd_name in commands: opt_dict = self.command_options.get(cmd_name) if opt_dict is None: - print indent + "no option dict for '%s' command" % cmd_name + print(indent + "no option dict for '%s' command" % cmd_name) else: - print indent + "option dict for '%s' command:" % cmd_name + print(indent + "option dict for '%s' command:" % cmd_name) out = pformat(opt_dict) for line in string.split(out, "\n"): - print indent + " " + line + print(indent + " " + line) # dump_option_dicts () @@ -365,11 +365,11 @@ Common commands: (see '--help-commands' for more) if filenames is None: filenames = self.find_config_files() - if DEBUG: print "Distribution.parse_config_files():" + if DEBUG: print("Distribution.parse_config_files():") parser = ConfigParser() for filename in filenames: - if DEBUG: print " reading", filename + if DEBUG: print(" reading", filename) parser.read(filename) for section in parser.sections(): options = parser.options(section) @@ -636,14 +636,14 @@ Common commands: (see '--help-commands' for more) options = self.global_options parser.set_option_table(options) parser.print_help(self.common_usage + "\nGlobal options:") - print + print() if display_options: parser.set_option_table(self.display_options) parser.print_help( "Information display options (just display " + "information, ignore any commands)") - print + print() for command in self.commands: if type(command) is ClassType and issubclass(command, Command): @@ -657,9 +657,9 @@ Common commands: (see '--help-commands' for more) else: parser.set_option_table(klass.user_options) parser.print_help("Options for '%s' command:" % klass.__name__) - print + print() - print gen_usage(self.script_name) + print(gen_usage(self.script_name)) return # _show_help () @@ -678,8 +678,8 @@ Common commands: (see '--help-commands' for more) # we ignore "foo bar"). if self.help_commands: self.print_commands() - print - print gen_usage(self.script_name) + print() + print(gen_usage(self.script_name)) return 1 # If user supplied any of the "display metadata" options, then @@ -695,12 +695,12 @@ Common commands: (see '--help-commands' for more) opt = translate_longopt(opt) value = getattr(self.metadata, "get_"+opt)() if opt in ['keywords', 'platforms']: - print string.join(value, ',') + print(string.join(value, ',')) elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'): - print string.join(value, '\n') + print(string.join(value, '\n')) else: - print value + print(value) any_display_options = 1 return any_display_options @@ -712,7 +712,7 @@ Common commands: (see '--help-commands' for more) 'print_commands()'. """ - print header + ":" + print(header + ":") for cmd in commands: klass = self.cmdclass.get(cmd) @@ -723,7 +723,7 @@ Common commands: (see '--help-commands' for more) except AttributeError: description = "(no description available)" - print " %-*s %s" % (max_length, cmd, description) + print(" %-*s %s" % (max_length, cmd, description)) # print_command_list () @@ -757,7 +757,7 @@ Common commands: (see '--help-commands' for more) "Standard commands", max_length) if extra_commands: - print + print() self.print_command_list(extra_commands, "Extra commands", max_length) @@ -862,8 +862,8 @@ Common commands: (see '--help-commands' for more) cmd_obj = self.command_obj.get(command) if not cmd_obj and create: if DEBUG: - print "Distribution.get_command_obj(): " \ - "creating '%s' command object" % command + print("Distribution.get_command_obj(): " \ + "creating '%s' command object" % command) klass = self.get_command_class(command) cmd_obj = self.command_obj[command] = klass(self) @@ -893,9 +893,9 @@ Common commands: (see '--help-commands' for more) if option_dict is None: option_dict = self.get_option_dict(command_name) - if DEBUG: print " setting options for '%s' command:" % command_name + if DEBUG: print(" setting options for '%s' command:" % command_name) for (option, (source, value)) in option_dict.items(): - if DEBUG: print " %s = %s (from %s)" % (option, value, source) + if DEBUG: print(" %s = %s (from %s)" % (option, value, source)) try: bool_opts = map(translate_longopt, command_obj.boolean_options) except AttributeError: @@ -1219,4 +1219,4 @@ def fix_help_options (options): if __name__ == "__main__": dist = Distribution() - print "ok" + print("ok") diff --git a/fancy_getopt.py b/fancy_getopt.py index 62a24e85..646f8e1e 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -497,6 +497,6 @@ How *do* you spell that odd word, anyways? say, "How should I know?"].)""" for w in (10, 20, 30, 40): - print "width: %d" % w - print string.join(wrap_text(text, w), "\n") - print + print("width: %d" % w) + print(string.join(wrap_text(text, w), "\n")) + print() diff --git a/filelist.py b/filelist.py index 4bbdd1f0..e4a83d64 100644 --- a/filelist.py +++ b/filelist.py @@ -53,7 +53,7 @@ class FileList: """ from distutils.debug import DEBUG if DEBUG: - print msg + print(msg) # -- List-like methods --------------------------------------------- diff --git a/log.py b/log.py index 95d4c1c5..e4959d64 100644 --- a/log.py +++ b/log.py @@ -23,9 +23,9 @@ class Log: if not args: # msg may contain a '%'. If args is empty, # don't even try to string-format - print msg + print(msg) else: - print msg % args + print(msg % args) sys.stdout.flush() def log(self, level, msg, *args): diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 0de123d9..9db1a39b 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -160,9 +160,9 @@ class MWerksCompiler (CCompiler) : settings['libraries'] = libraries settings['extrasearchdirs'] = sourcefiledirs + include_dirs + library_dirs if self.dry_run: - print 'CALLING LINKER IN', os.getcwd() + print('CALLING LINKER IN', os.getcwd()) for key, value in settings.items(): - print '%20.20s %s'%(key, value) + print('%20.20s %s'%(key, value)) return # Build the export file exportfilename = os.path.join(build_temp, exportname) diff --git a/spawn.py b/spawn.py index 6b07f52e..c75931c3 100644 --- a/spawn.py +++ b/spawn.py @@ -109,7 +109,7 @@ def _spawn_os2 (cmd, "command '%s' failed: %s" % (cmd[0], exc[-1]) if rc != 0: # and this reflects the command running but failing - print "command '%s' failed with exit status %d" % (cmd[0], rc) + print("command '%s' failed with exit status %d" % (cmd[0], rc)) raise DistutilsExecError, \ "command '%s' failed with exit status %d" % (cmd[0], rc) diff --git a/tests/test_dist.py b/tests/test_dist.py index 4d2a7cdf..8d4b0703 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -74,8 +74,8 @@ class DistributionTestCase(unittest.TestCase): sys.argv.append("build") f = open(TESTFN, "w") try: - print >>f, "[global]" - print >>f, "command_packages = foo.bar, splat" + print("[global]", file=f) + print("command_packages = foo.bar, splat", file=f) f.close() d = self.create_distribution([TESTFN]) self.assertEqual(d.get_command_packages(), diff --git a/text_file.py b/text_file.py index ff2878de..10ee1be7 100644 --- a/text_file.py +++ b/text_file.py @@ -342,13 +342,13 @@ line 3 \\ result = file.readlines () # result = string.join (result, '') if result == expected_result: - print "ok %d (%s)" % (count, description) + print("ok %d (%s)" % (count, description)) else: - print "not ok %d (%s):" % (count, description) - print "** expected:" - print expected_result - print "** received:" - print result + print("not ok %d (%s):" % (count, description)) + print("** expected:") + print(expected_result) + print("** received:") + print(result) filename = "test.txt" -- cgit v1.2.1 From 16a4375780a8081b708a0236c31c22555e2969e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Fri, 9 Feb 2007 12:36:48 +0000 Subject: Bug #1600860: Search for shared python library in LIBDIR, not lib/python/config, on "linux" and "gnu" systems. Will backport. --- command/build_ext.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 24138293..542b77a7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -185,9 +185,7 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs - if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos' or \ - ((sys.platform.startswith('linux') or sys.platform.startswith('gnu')) and - sysconfig.get_config_var('Py_ENABLE_SHARED')): + if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", @@ -197,6 +195,17 @@ class build_ext (Command): # building python standard extensions self.library_dirs.append('.') + # for extensions under Linux with a shared Python library, + # Python's library directory must be appended to library_dirs + if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ + and sysconfig.get_config_var('Py_ENABLE_SHARED'): + if string.find(sys.executable, sys.exec_prefix) != -1: + # building third party extensions + self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) + else: + # building python standard extensions + self.library_dirs.append('.') + # The argument parsing will result in self.define being a string, but # it has to be a list of 2-tuples. All the preprocessor symbols # specified by the 'define' option will be set to '1'. Multiple -- cgit v1.2.1 From 0624f6d5917587a042fd6ba4c3642c7bafeddcb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Fri, 9 Feb 2007 12:37:12 +0000 Subject: Bug #1600860: Search for shared python library in LIBDIR, not lib/python/config, on "linux" and "gnu" systems. --- command/build_ext.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 24138293..542b77a7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -185,9 +185,7 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs - if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos' or \ - ((sys.platform.startswith('linux') or sys.platform.startswith('gnu')) and - sysconfig.get_config_var('Py_ENABLE_SHARED')): + if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", @@ -197,6 +195,17 @@ class build_ext (Command): # building python standard extensions self.library_dirs.append('.') + # for extensions under Linux with a shared Python library, + # Python's library directory must be appended to library_dirs + if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ + and sysconfig.get_config_var('Py_ENABLE_SHARED'): + if string.find(sys.executable, sys.exec_prefix) != -1: + # building third party extensions + self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) + else: + # building python standard extensions + self.library_dirs.append('.') + # The argument parsing will result in self.define being a string, but # it has to be a list of 2-tuples. All the preprocessor symbols # specified by the 'define' option will be set to '1'. Multiple -- cgit v1.2.1 From a8bf57767e4e8ba7c99e9a51f30861707124c642 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 9 Feb 2007 20:13:25 +0000 Subject: Fix a bunch of doctests with the -d option of refactor.py. We still have 27 failing tests (down from 39). --- versionpredicate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versionpredicate.py b/versionpredicate.py index ba8b6c02..434b34f1 100644 --- a/versionpredicate.py +++ b/versionpredicate.py @@ -40,7 +40,7 @@ class VersionPredicate: The str() of a `VersionPredicate` provides a normalized human-readable version of the expression:: - >>> print v + >>> print(v) pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) The `satisfied_by()` method can be used to determine with a given -- cgit v1.2.1 From 0eaf9a8276f4587601ba9514243c1f06e4511cb8 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 11 Feb 2007 06:12:03 +0000 Subject: - PEP 3106: dict.iterkeys(), .iteritems(), .itervalues() are now gone; and .keys(), .items(), .values() return dict views. The dict views aren't fully functional yet; in particular, they can't be compared to sets yet. but they are useful as "iterator wells". There are still 27 failing unit tests; I expect that many of these have fairly trivial fixes, but there are so many, I could use help. --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 9de72346..220b0331 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -271,7 +271,7 @@ def parse_makefile(fn, g=None): # do variable interpolation here while notdone: - for name in notdone.keys(): + for name in list(notdone): value = notdone[name] m = _findvar1_rx.search(value) or _findvar2_rx.search(value) if m: -- cgit v1.2.1 From a67f7d4700f890ac5822401b756dc0b20a6eb421 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 23 Feb 2007 15:07:44 +0000 Subject: Merged revisions 53623-53858 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r53624 | peter.astrand | 2007-02-02 20:06:36 +0100 (Fri, 02 Feb 2007) | 1 line We had several if statements checking the value of a fd. This is unsafe, since valid fds might be zero. We should check for not None instead. ........ r53635 | kurt.kaiser | 2007-02-05 07:03:18 +0100 (Mon, 05 Feb 2007) | 2 lines Add 'raw' support to configHandler. Patch 1650174 Tal Einat. ........ r53641 | kurt.kaiser | 2007-02-06 00:02:16 +0100 (Tue, 06 Feb 2007) | 5 lines 1. Calltips now 'handle' tuples in the argument list (display '' :) Suggested solution by Christos Georgiou, Bug 791968. 2. Clean up tests, were not failing when they should have been. 4. Remove some camelcase and an unneeded try/except block. ........ r53644 | kurt.kaiser | 2007-02-06 04:21:40 +0100 (Tue, 06 Feb 2007) | 2 lines Clean up ModifiedInterpreter.runcode() structure ........ r53646 | peter.astrand | 2007-02-06 16:37:50 +0100 (Tue, 06 Feb 2007) | 1 line Applied patch 1124861.3.patch to solve bug #1124861: Automatically create pipes on Windows, if GetStdHandle fails. Will backport. ........ r53648 | lars.gustaebel | 2007-02-06 19:38:13 +0100 (Tue, 06 Feb 2007) | 4 lines Patch #1652681: create nonexistent files in append mode and allow appending to empty files. ........ r53649 | kurt.kaiser | 2007-02-06 20:09:43 +0100 (Tue, 06 Feb 2007) | 4 lines Updated patch (CodeContext.061217.patch) to [ 1362975 ] CodeContext - Improved text indentation Tal Einat 16Dec06 ........ r53650 | kurt.kaiser | 2007-02-06 20:21:19 +0100 (Tue, 06 Feb 2007) | 2 lines narrow exception per [ 1540849 ] except too broad ........ r53653 | kurt.kaiser | 2007-02-07 04:39:41 +0100 (Wed, 07 Feb 2007) | 4 lines [ 1621265 ] Auto-completion list placement Move AC window below input line unless not enough space, then put it above. Patch: Tal Einat ........ r53654 | kurt.kaiser | 2007-02-07 09:07:13 +0100 (Wed, 07 Feb 2007) | 2 lines Handle AttributeError during calltip lookup ........ r53656 | raymond.hettinger | 2007-02-07 21:08:22 +0100 (Wed, 07 Feb 2007) | 3 lines SF #1615701: make d.update(m) honor __getitem__() and keys() in dict subclasses ........ r53658 | raymond.hettinger | 2007-02-07 22:04:20 +0100 (Wed, 07 Feb 2007) | 1 line SF: 1397711 Set docs conflated immutable and hashable ........ r53660 | raymond.hettinger | 2007-02-07 22:42:17 +0100 (Wed, 07 Feb 2007) | 1 line Check for a common user error with defaultdict(). ........ r53662 | raymond.hettinger | 2007-02-07 23:24:07 +0100 (Wed, 07 Feb 2007) | 1 line Bug #1575169: operator.isSequenceType() now returns False for subclasses of dict. ........ r53664 | raymond.hettinger | 2007-02-08 00:49:03 +0100 (Thu, 08 Feb 2007) | 1 line Silence compiler warning ........ r53666 | raymond.hettinger | 2007-02-08 01:07:32 +0100 (Thu, 08 Feb 2007) | 1 line Do not let overflows in enumerate() and count() pass silently. ........ r53668 | raymond.hettinger | 2007-02-08 01:50:39 +0100 (Thu, 08 Feb 2007) | 1 line Bypass set specific optimizations for set and frozenset subclasses. ........ r53670 | raymond.hettinger | 2007-02-08 02:42:35 +0100 (Thu, 08 Feb 2007) | 1 line Fix docstring bug ........ r53671 | martin.v.loewis | 2007-02-08 10:13:36 +0100 (Thu, 08 Feb 2007) | 3 lines Bug #1653736: Complain about keyword arguments to time.isoformat. Will backport to 2.5. ........ r53679 | kurt.kaiser | 2007-02-08 23:58:18 +0100 (Thu, 08 Feb 2007) | 6 lines Corrected some bugs in AutoComplete. Also, Page Up/Down in ACW implemented; mouse and cursor selection in ACWindow implemented; double Tab inserts current selection and closes ACW (similar to double-click and Return); scroll wheel now works in ACW. Added AutoComplete instructions to IDLE Help. ........ r53689 | martin.v.loewis | 2007-02-09 13:19:32 +0100 (Fri, 09 Feb 2007) | 3 lines Bug #1653736: Properly discard third argument to slot_nb_inplace_power. Will backport. ........ r53691 | martin.v.loewis | 2007-02-09 13:36:48 +0100 (Fri, 09 Feb 2007) | 4 lines Bug #1600860: Search for shared python library in LIBDIR, not lib/python/config, on "linux" and "gnu" systems. Will backport. ........ r53693 | martin.v.loewis | 2007-02-09 13:58:49 +0100 (Fri, 09 Feb 2007) | 2 lines Update broken link. Will backport to 2.5. ........ r53697 | georg.brandl | 2007-02-09 19:48:41 +0100 (Fri, 09 Feb 2007) | 2 lines Bug #1656078: typo in in profile docs. ........ r53731 | brett.cannon | 2007-02-11 06:36:00 +0100 (Sun, 11 Feb 2007) | 3 lines Change a very minor inconsistency (that is purely cosmetic) in the AST definition. ........ r53735 | skip.montanaro | 2007-02-11 19:24:37 +0100 (Sun, 11 Feb 2007) | 1 line fix trace.py --ignore-dir ........ r53741 | brett.cannon | 2007-02-11 20:44:41 +0100 (Sun, 11 Feb 2007) | 3 lines Check in changed Python-ast.c from a cosmetic change to Python.asdl (in r53731). ........ r53751 | brett.cannon | 2007-02-12 04:51:02 +0100 (Mon, 12 Feb 2007) | 5 lines Modify Parser/asdl_c.py so that the __version__ number for Python/Python-ast.c is specified at the top of the file. Also add a note that Python/Python-ast.c needs to be committed separately after a change to the AST grammar to capture the revision number of the change (which is what __version__ is set to). ........ r53752 | lars.gustaebel | 2007-02-12 10:25:53 +0100 (Mon, 12 Feb 2007) | 3 lines Bug #1656581: Point out that external file objects are supposed to be at position 0. ........ r53754 | martin.v.loewis | 2007-02-12 13:21:10 +0100 (Mon, 12 Feb 2007) | 3 lines Patch 1463026: Support default namespace in XMLGenerator. Fixes #847665. Will backport. ........ r53757 | armin.rigo | 2007-02-12 17:23:24 +0100 (Mon, 12 Feb 2007) | 4 lines Fix the line to what is my guess at the original author's meaning. (The line has no effect anyway, but is present because it's customary call the base class __init__). ........ r53763 | martin.v.loewis | 2007-02-13 09:34:45 +0100 (Tue, 13 Feb 2007) | 3 lines Patch #685268: Consider a package's __path__ in imputil. Will backport. ........ r53765 | martin.v.loewis | 2007-02-13 10:49:38 +0100 (Tue, 13 Feb 2007) | 2 lines Patch #698833: Support file decryption in zipfile. ........ r53766 | martin.v.loewis | 2007-02-13 11:10:39 +0100 (Tue, 13 Feb 2007) | 3 lines Patch #1517891: Make 'a' create the file if it doesn't exist. Fixes #1514451. ........ r53767 | martin.v.loewis | 2007-02-13 13:08:24 +0100 (Tue, 13 Feb 2007) | 3 lines Bug #1658794: Remove extraneous 'this'. Will backport to 2.5. ........ r53769 | martin.v.loewis | 2007-02-13 13:14:19 +0100 (Tue, 13 Feb 2007) | 3 lines Patch #1657276: Make NETLINK_DNRTMSG conditional. Will backport. ........ r53771 | lars.gustaebel | 2007-02-13 17:09:24 +0100 (Tue, 13 Feb 2007) | 4 lines Patch #1647484: Renamed GzipFile's filename attribute to name. The filename attribute is still accessible as a property that emits a DeprecationWarning. ........ r53772 | lars.gustaebel | 2007-02-13 17:24:00 +0100 (Tue, 13 Feb 2007) | 3 lines Strip the '.gz' extension from the filename that is written to the gzip header. ........ r53774 | martin.v.loewis | 2007-02-14 11:07:37 +0100 (Wed, 14 Feb 2007) | 2 lines Patch #1432399: Add HCI sockets. ........ r53775 | martin.v.loewis | 2007-02-14 12:30:07 +0100 (Wed, 14 Feb 2007) | 2 lines Update 1432399 to removal of _BT_SOCKADDR_MEMB. ........ r53776 | martin.v.loewis | 2007-02-14 12:30:56 +0100 (Wed, 14 Feb 2007) | 3 lines Ignore directory time stamps when considering whether to rerun libffi configure. ........ r53778 | lars.gustaebel | 2007-02-14 15:45:12 +0100 (Wed, 14 Feb 2007) | 4 lines A missing binary mode in AppendTest caused failures in Windows Buildbot. ........ r53782 | martin.v.loewis | 2007-02-15 10:51:35 +0100 (Thu, 15 Feb 2007) | 2 lines Patch #1397848: add the reasoning behind no-resize-on-shrinkage. ........ r53783 | georg.brandl | 2007-02-15 11:37:59 +0100 (Thu, 15 Feb 2007) | 2 lines Make functools.wraps() docs a bit clearer. ........ r53785 | georg.brandl | 2007-02-15 12:29:04 +0100 (Thu, 15 Feb 2007) | 2 lines Patch #1494140: Add documentation for the new struct.Struct object. ........ r53787 | georg.brandl | 2007-02-15 12:29:55 +0100 (Thu, 15 Feb 2007) | 2 lines Add missing \versionadded. ........ r53800 | brett.cannon | 2007-02-15 23:54:39 +0100 (Thu, 15 Feb 2007) | 11 lines Update the encoding package's search function to use absolute imports when calling __import__. This helps make the expected search locations for encoding modules be more explicit. One could use an explicit value for __path__ when making the call to __import__ to force the exact location searched for encodings. This would give the most strict search path possible if one is worried about malicious code being imported. The unfortunate side-effect of that is that if __path__ was modified on 'encodings' on purpose in a safe way it would not be picked up in future __import__ calls. ........ r53801 | brett.cannon | 2007-02-16 20:33:01 +0100 (Fri, 16 Feb 2007) | 2 lines Make the __import__ call in encodings.__init__ absolute with a level 0 call. ........ r53809 | vinay.sajip | 2007-02-16 23:36:24 +0100 (Fri, 16 Feb 2007) | 1 line Minor fix for currentframe (SF #1652788). ........ r53818 | raymond.hettinger | 2007-02-19 03:03:19 +0100 (Mon, 19 Feb 2007) | 3 lines Extend work on revision 52962: Eliminate redundant calls to PyObject_Hash(). ........ r53820 | raymond.hettinger | 2007-02-19 05:08:43 +0100 (Mon, 19 Feb 2007) | 1 line Add merge() function to heapq. ........ r53821 | raymond.hettinger | 2007-02-19 06:28:28 +0100 (Mon, 19 Feb 2007) | 1 line Add tie-breaker count to preserve sort stability. ........ r53822 | raymond.hettinger | 2007-02-19 07:59:32 +0100 (Mon, 19 Feb 2007) | 1 line Use C heapreplace() instead of slower _siftup() in pure python. ........ r53823 | raymond.hettinger | 2007-02-19 08:30:21 +0100 (Mon, 19 Feb 2007) | 1 line Add test for merge stability ........ r53824 | raymond.hettinger | 2007-02-19 10:14:10 +0100 (Mon, 19 Feb 2007) | 1 line Provide an example of defaultdict with non-zero constant factory function. ........ r53825 | lars.gustaebel | 2007-02-19 10:54:47 +0100 (Mon, 19 Feb 2007) | 2 lines Moved misplaced news item. ........ r53826 | martin.v.loewis | 2007-02-19 11:55:19 +0100 (Mon, 19 Feb 2007) | 3 lines Patch #1490190: posixmodule now includes os.chflags() and os.lchflags() functions on platforms where the underlying system calls are available. ........ r53827 | raymond.hettinger | 2007-02-19 19:15:04 +0100 (Mon, 19 Feb 2007) | 1 line Fixup docstrings for merge(). ........ r53829 | raymond.hettinger | 2007-02-19 21:44:04 +0100 (Mon, 19 Feb 2007) | 1 line Fixup set/dict interoperability. ........ r53837 | raymond.hettinger | 2007-02-21 06:20:38 +0100 (Wed, 21 Feb 2007) | 1 line Add itertools.izip_longest(). ........ r53838 | raymond.hettinger | 2007-02-21 18:22:05 +0100 (Wed, 21 Feb 2007) | 1 line Remove filler struct item and fix leak. ........ --- command/build_ext.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index f79eee3b..bbe51462 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -185,9 +185,7 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs - if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos' or \ - ((sys.platform.startswith('linux') or sys.platform.startswith('gnu')) and - sysconfig.get_config_var('Py_ENABLE_SHARED')): + if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': if string.find(sys.executable, sys.exec_prefix) != -1: # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", @@ -197,6 +195,17 @@ class build_ext (Command): # building python standard extensions self.library_dirs.append('.') + # for extensions under Linux with a shared Python library, + # Python's library directory must be appended to library_dirs + if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ + and sysconfig.get_config_var('Py_ENABLE_SHARED'): + if string.find(sys.executable, sys.exec_prefix) != -1: + # building third party extensions + self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) + else: + # building python standard extensions + self.library_dirs.append('.') + # The argument parsing will result in self.define being a string, but # it has to be a list of 2-tuples. All the preprocessor symbols # specified by the 'define' option will be set to '1'. Multiple -- cgit v1.2.1 From 4b5b20c7db13586c89f2de8110f9b3af5a8e69be Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 13 Mar 2007 10:19:22 +0000 Subject: Patch #1569798: fix a bug in distutils when building Python from a directory within sys.exec_prefix. --- command/build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 542b77a7..82474de8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': - if string.find(sys.executable, sys.exec_prefix) != -1: + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", "python" + get_python_version(), @@ -199,7 +199,7 @@ class build_ext (Command): # Python's library directory must be appended to library_dirs if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ and sysconfig.get_config_var('Py_ENABLE_SHARED'): - if string.find(sys.executable, sys.exec_prefix) != -1: + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: -- cgit v1.2.1 From e936d6d24f1cb257cb2a66a70c32c89f65306d0a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 13 Mar 2007 10:19:35 +0000 Subject: Patch #1569798: fix a bug in distutils when building Python from a directory within sys.exec_prefix. (backport from rev. 54331) --- command/build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 542b77a7..82474de8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': - if string.find(sys.executable, sys.exec_prefix) != -1: + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", "python" + get_python_version(), @@ -199,7 +199,7 @@ class build_ext (Command): # Python's library directory must be appended to library_dirs if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ and sysconfig.get_config_var('Py_ENABLE_SHARED'): - if string.find(sys.executable, sys.exec_prefix) != -1: + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: -- cgit v1.2.1 From b0ac75c972effad4c578498808683e20da236ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Fri, 30 Mar 2007 15:01:42 +0000 Subject: Bump the patch level version of distutils since there were a few bug fixes since the 2.5.0 release. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 21d34c76..86ad44fe 100644 --- a/__init__.py +++ b/__init__.py @@ -20,4 +20,4 @@ __revision__ = "$Id$" # In general, major and minor version should loosely follow the Python # version number the distutils code was shipped with. # -__version__ = "2.5.0" +__version__ = "2.5.1" -- cgit v1.2.1 From fcee36f5b542e0eee58aa86aaa14a447ae3c03c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Sat, 31 Mar 2007 21:02:43 +0000 Subject: Bump the patch level version of distutils since there were a few bug fixes since the 2.5.0 release. Backport of r54615. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 21d34c76..86ad44fe 100644 --- a/__init__.py +++ b/__init__.py @@ -20,4 +20,4 @@ __revision__ = "$Id$" # In general, major and minor version should loosely follow the Python # version number the distutils code was shipped with. # -__version__ = "2.5.0" +__version__ = "2.5.1" -- cgit v1.2.1 From e49345974375c27eddb6aa6a576e5b30688e3511 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sun, 1 Apr 2007 18:24:22 +0000 Subject: SF #1685563, MSVCCompiler creates redundant and long PATH strings If MSVCCompiler.initialize() was called multiple times, the path would get duplicated. On Windows, this is a problem because the path is limited to 4k. There's no benefit in adding a path multiple times, so prevent that from occuring. We also normalize the path before checking for duplicates so things like /a and /a/ won't both be stored. Will backport. --- msvccompiler.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/msvccompiler.py b/msvccompiler.py index 0d72837e..29a90209 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -187,6 +187,19 @@ def get_build_architecture(): j = string.find(sys.version, ")", i) return sys.version[i+len(prefix):j] +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths class MSVCCompiler (CCompiler) : @@ -270,6 +283,7 @@ class MSVCCompiler (CCompiler) : self.__paths.append(p) except KeyError: pass + self.__paths = normalize_and_reduce_paths(self.__paths) os.environ['path'] = string.join(self.__paths, ';') self.preprocess_options = None -- cgit v1.2.1 From 1085c6753feacd1c88d97e59031217679ef25e6a Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sun, 1 Apr 2007 18:29:47 +0000 Subject: Backport 54644: SF #1685563, MSVCCompiler creates redundant and long PATH strings If MSVCCompiler.initialize() was called multiple times, the path would get duplicated. On Windows, this is a problem because the path is limited to 4k. There's no benefit in adding a path multiple times, so prevent that from occuring. We also normalize the path before checking for duplicates so things like /a and /a/ won't both be stored. --- msvccompiler.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/msvccompiler.py b/msvccompiler.py index 0d72837e..29a90209 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -187,6 +187,19 @@ def get_build_architecture(): j = string.find(sys.version, ")", i) return sys.version[i+len(prefix):j] +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths class MSVCCompiler (CCompiler) : @@ -270,6 +283,7 @@ class MSVCCompiler (CCompiler) : self.__paths.append(p) except KeyError: pass + self.__paths = normalize_and_reduce_paths(self.__paths) os.environ['path'] = string.join(self.__paths, ';') self.preprocess_options = None -- cgit v1.2.1 From 57e9dc4a5d61c5572467c3fe69e816c357647a6f Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Tue, 17 Apr 2007 08:48:32 +0000 Subject: Remove functions in string module that are also string methods. Also remove: * all calls to functions in the string module (except maketrans) * everything from stropmodule except for maketrans() which is still used --- cmd.py | 6 +++--- command/bdist.py | 2 +- command/bdist_msi.py | 2 +- command/bdist_rpm.py | 18 +++++++++--------- command/bdist_wininst.py | 10 +++++----- command/build_clib.py | 5 ++--- command/build_ext.py | 24 ++++++++++++------------ command/build_py.py | 14 +++++++------- command/config.py | 10 +++++----- command/install.py | 12 ++++++------ command/install_lib.py | 2 +- command/register.py | 4 ++-- command/sdist.py | 6 +++--- cygwinccompiler.py | 5 ++--- dist.py | 24 ++++++++++++------------ emxccompiler.py | 5 ++--- extension.py | 6 +++--- fancy_getopt.py | 14 +++++++------- filelist.py | 16 ++++++++-------- msvccompiler.py | 22 +++++++++++----------- mwerkscompiler.py | 6 +++--- spawn.py | 12 ++++++------ sysconfig.py | 7 +++---- text_file.py | 22 +++++++++++----------- util.py | 26 ++++++++++++-------------- version.py | 17 ++++++++--------- 26 files changed, 145 insertions(+), 152 deletions(-) diff --git a/cmd.py b/cmd.py index be0a3e23..ebd99310 100644 --- a/cmd.py +++ b/cmd.py @@ -8,7 +8,7 @@ in the distutils.command package. __revision__ = "$Id$" -import sys, os, string, re +import sys, os, re from types import * from distutils.errors import * from distutils import util, dir_util, file_util, archive_util, dep_util @@ -166,7 +166,7 @@ class Command: print(indent + header) indent = indent + " " for (option, _, _) in self.user_options: - option = string.translate(option, longopt_xlate) + option = option.translate(longopt_xlate) if option[-1] == "=": option = option[:-1] value = getattr(self, option) @@ -415,7 +415,7 @@ class Command: """ if exec_msg is None: exec_msg = "generating %s from %s" % \ - (outfile, string.join(infiles, ', ')) + (outfile, ', '.join(infiles)) if skip_msg is None: skip_msg = "skipping %s (inputs unchanged)" % outfile diff --git a/command/bdist.py b/command/bdist.py index 23c25a55..d6897d2d 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -7,7 +7,7 @@ distribution).""" __revision__ = "$Id$" -import os, string +import os from types import * from distutils.core import Command from distutils.errors import * diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 75db8773..f31bdeda 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -7,7 +7,7 @@ Implements the bdist_msi command. """ -import sys, os, string +import sys, os from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import remove_tree diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index bbcf2927..ef2bdfd8 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -7,7 +7,7 @@ distributions).""" __revision__ = "$Id$" -import sys, os, string +import sys, os import glob from types import * from distutils.core import Command @@ -354,7 +354,7 @@ class bdist_rpm (Command): line = out.readline() if not line: break - l = string.split(string.strip(line)) + l = line.strip().split() assert(len(l) == 2) binary_rpms.append(l[1]) # The source rpm is named after the first entry in the spec file @@ -437,9 +437,9 @@ class bdist_rpm (Command): 'Conflicts', 'Obsoletes', ): - val = getattr(self, string.lower(field)) + val = getattr(self, field.lower()) if type(val) is ListType: - spec_file.append('%s: %s' % (field, string.join(val))) + spec_file.append('%s: %s' % (field, ' '.join(val))) elif val is not None: spec_file.append('%s: %s' % (field, val)) @@ -452,7 +452,7 @@ class bdist_rpm (Command): if self.build_requires: spec_file.append('BuildRequires: ' + - string.join(self.build_requires)) + ' '.join(self.build_requires)) if self.icon: spec_file.append('Icon: ' + os.path.basename(self.icon)) @@ -513,7 +513,7 @@ class bdist_rpm (Command): '', '%' + rpm_opt,]) if val: - spec_file.extend(string.split(open(val, 'r').read(), '\n')) + spec_file.extend(open(val, 'r').read().split('\n')) else: spec_file.append(default) @@ -526,7 +526,7 @@ class bdist_rpm (Command): ]) if self.doc_files: - spec_file.append('%doc ' + string.join(self.doc_files)) + spec_file.append('%doc ' + ' '.join(self.doc_files)) if self.changelog: spec_file.extend([ @@ -544,8 +544,8 @@ class bdist_rpm (Command): if not changelog: return changelog new_changelog = [] - for line in string.split(string.strip(changelog), '\n'): - line = string.strip(line) + for line in changelog.strip().split('\n'): + line = line.strip() if line[0] == '*': new_changelog.extend(['', line]) elif line[0] == '-': diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 49afca04..21465a82 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -7,7 +7,7 @@ exe-program.""" __revision__ = "$Id$" -import sys, os, string +import sys, os from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import create_tree, remove_tree @@ -135,7 +135,7 @@ class bdist_wininst (Command): # Use a custom scheme for the zip-file, because we have to decide # at installation time which scheme to use. for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): - value = string.upper(key) + value = key.upper() if key == 'headers': value = value + '/Include/$dist_name' setattr(install, @@ -192,14 +192,14 @@ class bdist_wininst (Command): # Escape newline characters def escape(s): - return string.replace(s, "\n", "\\n") + return s.replace("\n", "\\n") for name in ["author", "author_email", "description", "maintainer", "maintainer_email", "name", "url", "version"]: data = getattr(metadata, name, "") if data: info = info + ("\n %s: %s" % \ - (string.capitalize(name), escape(data))) + (name.capitalize(), escape(data))) lines.append("%s=%s" % (name, escape(data))) # The [setup] section contains entries controlling @@ -220,7 +220,7 @@ class bdist_wininst (Command): build_info = "Built %s with distutils-%s" % \ (time.ctime(time.time()), distutils.__version__) lines.append("build_info=%s" % build_info) - return string.join(lines, "\n") + return "\n".join(lines) # get_inidata() diff --git a/command/build_clib.py b/command/build_clib.py index 69d8c751..07f58179 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -18,7 +18,7 @@ __revision__ = "$Id$" # two modules, mainly because a number of subtle details changed in the # cut 'n paste. Sigh. -import os, string +import os from types import * from distutils.core import Command from distutils.errors import * @@ -93,8 +93,7 @@ class build_clib (Command): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if type(self.include_dirs) is StringType: - self.include_dirs = string.split(self.include_dirs, - os.pathsep) + self.include_dirs = self.include_dirs.split(os.pathsep) # XXX same as for build_ext -- what about 'self.define' and # 'self.undef' ? diff --git a/command/build_ext.py b/command/build_ext.py index bbe51462..d0cd1629 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -8,7 +8,7 @@ extensions ASAP).""" __revision__ = "$Id$" -import sys, os, string, re +import sys, os, re from types import * from distutils.core import Command from distutils.errors import * @@ -138,7 +138,7 @@ class build_ext (Command): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if type(self.include_dirs) is StringType: - self.include_dirs = string.split(self.include_dirs, os.pathsep) + self.include_dirs = self.include_dirs.split(os.pathsep) # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. @@ -156,12 +156,12 @@ class build_ext (Command): if self.library_dirs is None: self.library_dirs = [] elif type(self.library_dirs) is StringType: - self.library_dirs = string.split(self.library_dirs, os.pathsep) + self.library_dirs = self.library_dirs.split(os.pathsep) if self.rpath is None: self.rpath = [] elif type(self.rpath) is StringType: - self.rpath = string.split(self.rpath, os.pathsep) + self.rpath = self.rpath.split(os.pathsep) # for extensions under windows use different directories # for Release and Debug builds. @@ -186,7 +186,7 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': - if string.find(sys.executable, sys.exec_prefix) != -1: + if sys.executable.find(sys.exec_prefix) != -1: # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", "python" + get_python_version(), @@ -199,7 +199,7 @@ class build_ext (Command): # Python's library directory must be appended to library_dirs if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ and sysconfig.get_config_var('Py_ENABLE_SHARED'): - if string.find(sys.executable, sys.exec_prefix) != -1: + if sys.executable.find(sys.exec_prefix) != -1: # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: @@ -212,14 +212,14 @@ class build_ext (Command): # symbols can be separated with commas. if self.define: - defines = string.split(self.define, ',') + defines = self.define.split(',') self.define = map(lambda symbol: (symbol, '1'), defines) # The option for macros to undefine is also a string from the # option parsing, but has to be a list. Multiple symbols can also # be separated with commas here. if self.undef: - self.undef = string.split(self.undef, ',') + self.undef = self.undef.split(',') if self.swig_opts is None: self.swig_opts = [] @@ -429,8 +429,8 @@ class build_ext (Command): # ignore build-lib -- put the compiled extension into # the source tree along with pure Python modules - modpath = string.split(fullname, '.') - package = string.join(modpath[0:-1], '.') + modpath = fullname.split('.') + package = '.'.join(modpath[0:-1]) base = modpath[-1] build_py = self.get_finalized_command('build_py') @@ -617,7 +617,7 @@ class build_ext (Command): """ from distutils.sysconfig import get_config_var - ext_path = string.split(ext_name, '.') + ext_path = ext_name.split('.') # OS/2 has an 8 character module (extension) limit :-( if os.name == "os2": ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] @@ -634,7 +634,7 @@ class build_ext (Command): the .pyd file (DLL) must export the module "init" function. """ - initfunc_name = "init" + string.split(ext.name,'.')[-1] + initfunc_name = "init" + ext.name.split('.')[-1] if initfunc_name not in ext.export_symbols: ext.export_symbols.append(initfunc_name) return ext.export_symbols diff --git a/command/build_py.py b/command/build_py.py index 3b7ec62c..990824bd 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -6,7 +6,7 @@ Implements the Distutils 'build_py' command.""" __revision__ = "$Id$" -import sys, string, os +import sys, os from types import * from glob import glob @@ -150,7 +150,7 @@ class build_py (Command): distribution, where package 'package' should be found (at least according to the 'package_dir' option, if any).""" - path = string.split(package, '.') + path = package.split('.') if not self.package_dir: if path: @@ -161,7 +161,7 @@ class build_py (Command): tail = [] while path: try: - pdir = self.package_dir[string.join(path, '.')] + pdir = self.package_dir['.'.join(path)] except KeyError: tail.insert(0, path[-1]) del path[-1] @@ -272,8 +272,8 @@ class build_py (Command): # - don't check for __init__.py in directory for empty package for module in self.py_modules: - path = string.split(module, '.') - package = string.join(path[0:-1], '.') + path = module.split('.') + package = '.'.join(path[0:-1]) module_base = path[-1] try: @@ -342,7 +342,7 @@ class build_py (Command): modules = self.find_all_modules() outputs = [] for (package, module, module_file) in modules: - package = string.split(package, '.') + package = package.split('.') filename = self.get_module_outfile(self.build_lib, package, module) outputs.append(filename) if include_bytecode: @@ -362,7 +362,7 @@ class build_py (Command): def build_module (self, module, module_file, package): if type(package) is StringType: - package = string.split(package, '.') + package = package.split('.') elif type(package) not in (ListType, TupleType): raise TypeError, \ "'package' must be a string (dot-separated), list, or tuple" diff --git a/command/config.py b/command/config.py index 42465693..3374db9e 100644 --- a/command/config.py +++ b/command/config.py @@ -13,7 +13,7 @@ this header file lives". __revision__ = "$Id$" -import sys, os, string, re +import sys, os, re from types import * from distutils.core import Command from distutils.errors import DistutilsExecError @@ -74,7 +74,7 @@ class config (Command): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] elif type(self.include_dirs) is StringType: - self.include_dirs = string.split(self.include_dirs, os.pathsep) + self.include_dirs = self.include_dirs.split(os.pathsep) if self.libraries is None: self.libraries = [] @@ -84,7 +84,7 @@ class config (Command): if self.library_dirs is None: self.library_dirs = [] elif type(self.library_dirs) is StringType: - self.library_dirs = string.split(self.library_dirs, os.pathsep) + self.library_dirs = self.library_dirs.split(os.pathsep) def run (self): @@ -163,7 +163,7 @@ class config (Command): if not filenames: filenames = self.temp_files self.temp_files = [] - log.info("removing: %s", string.join(filenames)) + log.info("removing: %s", ' '.join(filenames)) for filename in filenames: try: os.remove(filename) @@ -322,7 +322,7 @@ class config (Command): else: body.append(" %s;" % func) body.append("}") - body = string.join(body, "\n") + "\n" + body = "\n".join(body) + "\n" return self.try_link(body, headers, include_dirs, libraries, library_dirs) diff --git a/command/install.py b/command/install.py index fd5d6d1f..c03a90e8 100644 --- a/command/install.py +++ b/command/install.py @@ -8,7 +8,7 @@ from distutils import log __revision__ = "$Id$" -import sys, os, string +import sys, os from types import * from distutils.core import Command from distutils.debug import DEBUG @@ -269,7 +269,7 @@ class install (Command): # $platbase in the other installation directories and not worry # about needing recursive variable expansion (shudder). - py_version = (string.split(sys.version))[0] + py_version = sys.version.split()[0] (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), @@ -353,11 +353,11 @@ class install (Command): if opt_name[-1] == "=": opt_name = opt_name[0:-1] if self.negative_opt.has_key(opt_name): - opt_name = string.translate(self.negative_opt[opt_name], - longopt_xlate) + opt_name = self.negative_opt[opt_name].translate( + longopt_xlate) val = not getattr(self, opt_name) else: - opt_name = string.translate(opt_name, longopt_xlate) + opt_name = opt_name.translate(longopt_xlate) val = getattr(self, opt_name) print(" %s: %s" % (opt_name, val)) @@ -464,7 +464,7 @@ class install (Command): if self.extra_path is not None: if type(self.extra_path) is StringType: - self.extra_path = string.split(self.extra_path, ',') + self.extra_path = self.extra_path.split(',') if len(self.extra_path) == 1: path_file = extra_dirs = self.extra_path[0] diff --git a/command/install_lib.py b/command/install_lib.py index 08ff5434..65d25f7a 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -2,7 +2,7 @@ __revision__ = "$Id$" -import sys, os, string +import sys, os from types import IntType from distutils.core import Command from distutils.errors import DistutilsOptionError diff --git a/command/register.py b/command/register.py index 48070ee8..33567981 100644 --- a/command/register.py +++ b/command/register.py @@ -7,7 +7,7 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" -import sys, os, string, urllib2, getpass, urlparse +import sys, os, urllib2, getpass, urlparse import StringIO, ConfigParser from distutils.core import Command @@ -67,7 +67,7 @@ class register(Command): if missing: self.warn("missing required meta-data: " + - string.join(missing, ", ")) + ", ".join(missing)) if metadata.author: if not metadata.author_email: diff --git a/command/sdist.py b/command/sdist.py index eb2db505..8e1c0660 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -6,7 +6,7 @@ Implements the Distutils 'sdist' command (create a source distribution).""" __revision__ = "$Id$" -import sys, os, string +import sys, os from types import * from glob import glob from distutils.core import Command @@ -166,7 +166,7 @@ class sdist (Command): if missing: self.warn("missing required meta-data: " + - string.join(missing, ", ")) + ", ".join(missing)) if metadata.author: if not metadata.author_email: @@ -279,7 +279,7 @@ class sdist (Command): if not got_it: self.warn("standard file not found: should have one of " + - string.join(alts, ', ')) + ', '.join(alts)) else: if os.path.exists(fn): self.filelist.append(fn) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 44b58508..fae68481 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -365,10 +365,9 @@ def check_config_h(): # "pyconfig.h" check -- should probably be renamed... from distutils import sysconfig - import string # if sys.version contains GCC then python was compiled with # GCC, and the pyconfig.h file should be OK - if string.find(sys.version,"GCC") >= 0: + if sys.version.find("GCC") >= 0: return (CONFIG_H_OK, "sys.version mentions 'GCC'") fn = sysconfig.get_config_h_filename() @@ -387,7 +386,7 @@ def check_config_h(): else: # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar - if string.find(s,"__GNUC__") >= 0: + if s.find("__GNUC__") >= 0: return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) else: return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) diff --git a/dist.py b/dist.py index ca7a2f99..7ed1b5cf 100644 --- a/dist.py +++ b/dist.py @@ -8,7 +8,7 @@ being built/installed/distributed. __revision__ = "$Id$" -import sys, os, string, re +import sys, os, re from types import * from copy import copy @@ -304,7 +304,7 @@ Common commands: (see '--help-commands' for more) else: print(indent + "option dict for '%s' command:" % cmd_name) out = pformat(opt_dict) - for line in string.split(out, "\n"): + for line in out.split("\n"): print(indent + " " + line) # dump_option_dicts () @@ -378,7 +378,7 @@ Common commands: (see '--help-commands' for more) for opt in options: if opt != '__name__': val = parser.get(section,opt) - opt = string.replace(opt, '-', '_') + opt = opt.replace('-', '_') opt_dict[opt] = (filename, val) # Make the ConfigParser forget everything (so we retain @@ -599,14 +599,14 @@ Common commands: (see '--help-commands' for more) keywords = self.metadata.keywords if keywords is not None: if type(keywords) is StringType: - keywordlist = string.split(keywords, ',') - self.metadata.keywords = map(string.strip, keywordlist) + keywordlist = keywords.split(',') + self.metadata.keywords = [x.strip() for x in keywordlist] platforms = self.metadata.platforms if platforms is not None: if type(platforms) is StringType: - platformlist = string.split(platforms, ',') - self.metadata.platforms = map(string.strip, platformlist) + platformlist = platforms.split(',') + self.metadata.platforms = [x.strip() for x in platformlist] def _show_help (self, parser, @@ -695,10 +695,10 @@ Common commands: (see '--help-commands' for more) opt = translate_longopt(opt) value = getattr(self.metadata, "get_"+opt)() if opt in ['keywords', 'platforms']: - print(string.join(value, ',')) + print(','.join(value)) elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'): - print(string.join(value, '\n')) + print('\n'.join(value)) else: print(value) any_display_options = 1 @@ -803,9 +803,9 @@ Common commands: (see '--help-commands' for more) """Return a list of packages from which commands are loaded.""" pkgs = self.command_packages if not isinstance(pkgs, type([])): - pkgs = string.split(pkgs or "", ",") + pkgs = (pkgs or "").split(",") for i in range(len(pkgs)): - pkgs[i] = string.strip(pkgs[i]) + pkgs[i] = pkgs[i].strip() pkgs = filter(None, pkgs) if "distutils.command" not in pkgs: pkgs.insert(0, "distutils.command") @@ -1100,7 +1100,7 @@ class DistributionMetadata: long_desc = rfc822_escape( self.get_long_description() ) file.write('Description: %s\n' % long_desc) - keywords = string.join( self.get_keywords(), ',') + keywords = ','.join(self.get_keywords()) if keywords: file.write('Keywords: %s\n' % keywords ) diff --git a/emxccompiler.py b/emxccompiler.py index 8aef2b7a..f4b90dcd 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -261,10 +261,9 @@ def check_config_h(): # "pyconfig.h" check -- should probably be renamed... from distutils import sysconfig - import string # if sys.version contains GCC then python was compiled with # GCC, and the pyconfig.h file should be OK - if string.find(sys.version,"GCC") >= 0: + if sys.version.find("GCC") >= 0: return (CONFIG_H_OK, "sys.version mentions 'GCC'") fn = sysconfig.get_config_h_filename() @@ -283,7 +282,7 @@ def check_config_h(): else: # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar - if string.find(s,"__GNUC__") >= 0: + if s.find("__GNUC__") >= 0: return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) else: return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) diff --git a/extension.py b/extension.py index 440d128c..0fcbcc14 100644 --- a/extension.py +++ b/extension.py @@ -5,7 +5,7 @@ modules in setup scripts.""" __revision__ = "$Id$" -import os, string, sys +import os, sys from types import * try: @@ -128,7 +128,7 @@ class Extension: if len(kw): L = kw.keys() ; L.sort() L = map(repr, L) - msg = "Unknown Extension options: " + string.join(L, ', ') + msg = "Unknown Extension options: " + ', '.join(L) if warnings is not None: warnings.warn(msg) else: @@ -195,7 +195,7 @@ def read_setup_file (filename): elif switch == "-I": ext.include_dirs.append(value) elif switch == "-D": - equals = string.find(value, "=") + equals = value.find("=") if equals == -1: # bare "-DFOO" -- no value ext.define_macros.append((value, None)) else: # "-DFOO=blah" diff --git a/fancy_getopt.py b/fancy_getopt.py index 646f8e1e..317a3a48 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -115,7 +115,7 @@ class FancyGetopt: """Translate long option name 'long_option' to the form it has as an attribute of some object: ie., translate hyphens to underscores.""" - return string.translate(long_option, longopt_xlate) + return long_option.translate(longopt_xlate) def _check_alias_dict (self, aliases, what): @@ -253,7 +253,7 @@ class FancyGetopt: self._grok_option_table() - short_opts = string.join(self.short_opts) + short_opts = ' '.join(self.short_opts) try: opts, args = getopt.getopt(args, short_opts, self.long_opts) except getopt.error as msg: @@ -420,8 +420,8 @@ def wrap_text (text, width): if len(text) <= width: return [text] - text = string.expandtabs(text) - text = string.translate(text, WS_TRANS) + text = text.expandtabs() + text = text.translate(WS_TRANS) chunks = re.split(r'( +|-+)', text) chunks = filter(None, chunks) # ' - ' results in empty strings lines = [] @@ -460,7 +460,7 @@ def wrap_text (text, width): # and store this line in the list-of-all-lines -- as a single # string, of course! - lines.append(string.join(cur_line, '')) + lines.append(''.join(cur_line)) # while chunks @@ -473,7 +473,7 @@ def translate_longopt (opt): """Convert a long option name to a valid Python identifier by changing "-" to "_". """ - return string.translate(opt, longopt_xlate) + return opt.translate(longopt_xlate) class OptionDummy: @@ -498,5 +498,5 @@ say, "How should I know?"].)""" for w in (10, 20, 30, 40): print("width: %d" % w) - print(string.join(wrap_text(text, w), "\n")) + print("\n".join(wrap_text(text, w))) print() diff --git a/filelist.py b/filelist.py index e4a83d64..bc668cfa 100644 --- a/filelist.py +++ b/filelist.py @@ -8,7 +8,7 @@ and building lists of files. __revision__ = "$Id$" -import os, string, re +import os, re import fnmatch from types import * from glob import glob @@ -84,7 +84,7 @@ class FileList: # -- "File template" methods --------------------------------------- def _parse_template_line (self, line): - words = string.split(line) + words = line.split() action = words[0] patterns = dir = dir_pattern = None @@ -133,28 +133,28 @@ class FileList: # right number of words on the line for that action -- so we # can proceed with minimal error-checking. if action == 'include': - self.debug_print("include " + string.join(patterns)) + self.debug_print("include " + ' '.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=1): log.warn("warning: no files found matching '%s'", pattern) elif action == 'exclude': - self.debug_print("exclude " + string.join(patterns)) + self.debug_print("exclude " + ' '.join(patterns)) for pattern in patterns: if not self.exclude_pattern(pattern, anchor=1): log.warn(("warning: no previously-included files " "found matching '%s'"), pattern) elif action == 'global-include': - self.debug_print("global-include " + string.join(patterns)) + self.debug_print("global-include " + ' '.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=0): log.warn(("warning: no files found matching '%s' " + "anywhere in distribution"), pattern) elif action == 'global-exclude': - self.debug_print("global-exclude " + string.join(patterns)) + self.debug_print("global-exclude " + ' '.join(patterns)) for pattern in patterns: if not self.exclude_pattern(pattern, anchor=0): log.warn(("warning: no previously-included files matching " @@ -163,7 +163,7 @@ class FileList: elif action == 'recursive-include': self.debug_print("recursive-include %s %s" % - (dir, string.join(patterns))) + (dir, ' '.join(patterns))) for pattern in patterns: if not self.include_pattern(pattern, prefix=dir): log.warn(("warning: no files found matching '%s' " + @@ -172,7 +172,7 @@ class FileList: elif action == 'recursive-exclude': self.debug_print("recursive-exclude %s %s" % - (dir, string.join(patterns))) + (dir, ' '.join(patterns))) for pattern in patterns: if not self.exclude_pattern(pattern, prefix=dir): log.warn(("warning: no previously-included files matching " diff --git a/msvccompiler.py b/msvccompiler.py index 968a4ffd..ca1feaa0 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -12,7 +12,7 @@ for the Microsoft Visual Studio. __revision__ = "$Id$" -import sys, os, string +import sys, os from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -148,7 +148,7 @@ you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") def sub(self, s): for k, v in self.macros.items(): - s = string.replace(s, k, v) + s = s.replace(k, v) return s def get_build_version(): @@ -159,7 +159,7 @@ def get_build_version(): """ prefix = "MSC v." - i = string.find(sys.version, prefix) + i = sys.version.find(prefix) if i == -1: return 6 i = i + len(prefix) @@ -181,10 +181,10 @@ def get_build_architecture(): """ prefix = " bit (" - i = string.find(sys.version, prefix) + i = sys.version.find(prefix) if i == -1: return "Intel" - j = string.find(sys.version, ")", i) + j = sys.version.find(")", i) return sys.version[i+len(prefix):j] @@ -266,11 +266,11 @@ class MSVCCompiler (CCompiler) : # extend the MSVC path with the current path try: - for p in string.split(os.environ['path'], ';'): + for p in os.environ['path'].split(';'): self.__paths.append(p) except KeyError: pass - os.environ['path'] = string.join(self.__paths, ';') + os.environ['path'] = ';'.join(self.__paths) self.preprocess_options = None if self.__arch == "Intel": @@ -579,7 +579,7 @@ class MSVCCompiler (CCompiler) : return fn # didn't find it; try existing path - for p in string.split(os.environ['Path'],';'): + for p in os.environ['Path'].split(';'): fn = os.path.join(os.path.abspath(p),exe) if os.path.isfile(fn): return fn @@ -608,9 +608,9 @@ class MSVCCompiler (CCompiler) : d = read_values(base, key) if d: if self.__version >= 7: - return string.split(self.__macros.sub(d[path]), ";") + return self.__macros.sub(d[path]).split(";") else: - return string.split(d[path], ";") + return d[path].split(";") # MSVC 6 seems to create the registry entries we need only when # the GUI is run. if self.__version == 6: @@ -635,4 +635,4 @@ class MSVCCompiler (CCompiler) : else: p = self.get_msvc_paths(name) if p: - os.environ[name] = string.join(p, ';') + os.environ[name] = ';'.join(p) diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 9db1a39b..028ea825 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -8,7 +8,7 @@ Windows.""" __revision__ = "$Id$" -import sys, os, string +import sys, os from types import * from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ @@ -213,11 +213,11 @@ class MWerksCompiler (CCompiler) : curdir = os.getcwd() filename = os.path.join(curdir, filename) # Finally remove .. components - components = string.split(filename, ':') + components = filename.split(':') for i in range(1, len(components)): if components[i] == '..': components[i] = '' - return string.join(components, ':') + return ':'.join(components) def library_dir_option (self, dir): """Return the compiler option to add 'dir' to the list of diff --git a/spawn.py b/spawn.py index c75931c3..c70c10bf 100644 --- a/spawn.py +++ b/spawn.py @@ -10,7 +10,7 @@ executable name. __revision__ = "$Id$" -import sys, os, string +import sys, os from distutils.errors import * from distutils import log @@ -59,7 +59,7 @@ def _nt_quote_args (args): # quoting?) for i in range(len(args)): - if string.find(args[i], ' ') != -1: + if args[i].find(' ') != -1: args[i] = '"%s"' % args[i] return args @@ -73,7 +73,7 @@ def _spawn_nt (cmd, if search_path: # either we find one or it stays the same executable = find_executable(executable) or executable - log.info(string.join([executable] + cmd[1:], ' ')) + log.info(' '.join([executable] + cmd[1:])) if not dry_run: # spawn for NT requires a full path to the .exe try: @@ -98,7 +98,7 @@ def _spawn_os2 (cmd, if search_path: # either we find one or it stays the same executable = find_executable(executable) or executable - log.info(string.join([executable] + cmd[1:], ' ')) + log.info(' '.join([executable] + cmd[1:])) if not dry_run: # spawnv for OS/2 EMX requires a full path to the .exe try: @@ -119,7 +119,7 @@ def _spawn_posix (cmd, verbose=0, dry_run=0): - log.info(string.join(cmd, ' ')) + log.info(' '.join(cmd)) if dry_run: return exec_fn = search_path and os.execvp or os.execv @@ -184,7 +184,7 @@ def find_executable(executable, path=None): """ if path is None: path = os.environ['PATH'] - paths = string.split(path, os.pathsep) + paths = path.split(os.pathsep) (base, ext) = os.path.splitext(executable) if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): executable = executable + '.exe' diff --git a/sysconfig.py b/sysconfig.py index 220b0331..ea2e0597 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -13,7 +13,6 @@ __revision__ = "$Id$" import os import re -import string import sys from .errors import DistutilsPlatformError @@ -261,7 +260,7 @@ def parse_makefile(fn, g=None): m = _variable_rx.match(line) if m: n, v = m.group(1, 2) - v = string.strip(v) + v = v.strip() if "$" in v: notdone[n] = v else: @@ -295,7 +294,7 @@ def parse_makefile(fn, g=None): else: try: value = int(value) except ValueError: - done[name] = string.strip(value) + done[name] = value.strip() else: done[name] = value del notdone[name] @@ -399,7 +398,7 @@ def _init_posix(): # relative to the srcdir, which after installation no longer makes # sense. python_lib = get_python_lib(standard_lib=1) - linkerscript_path = string.split(g['LDSHARED'])[0] + linkerscript_path = g['LDSHARED'].split()[0] linkerscript_name = os.path.basename(linkerscript_path) linkerscript = os.path.join(python_lib, 'config', linkerscript_name) diff --git a/text_file.py b/text_file.py index 10ee1be7..c23a31dd 100644 --- a/text_file.py +++ b/text_file.py @@ -7,7 +7,7 @@ lines, and joining lines with backslashes.""" __revision__ = "$Id$" from types import * -import sys, os, string +import sys, os class TextFile: @@ -142,7 +142,7 @@ class TextFile: else: outmsg.append("line %d: " % line) outmsg.append(str(msg)) - return string.join(outmsg, "") + return "".join(outmsg) def error (self, msg, line=None): @@ -196,7 +196,7 @@ class TextFile: # unescape it (and any other escaped "#"'s that might be # lurking in there) and otherwise leave the line alone. - pos = string.find (line, "#") + pos = line.find ("#") if pos == -1: # no "#" -- no comments pass @@ -219,11 +219,11 @@ class TextFile: # # comment that should be ignored # there # result in "hello there". - if string.strip(line) == "": + if line.strip () == "": continue else: # it's an escaped "#" - line = string.replace (line, "\\#", "#") + line = line.replace("\\#", "#") # did previous line end with a backslash? then accumulate @@ -235,7 +235,7 @@ class TextFile: return buildup_line if self.collapse_join: - line = string.lstrip (line) + line = line.lstrip () line = buildup_line + line # careful: pay attention to line number when incrementing it @@ -259,11 +259,11 @@ class TextFile: # strip whitespace however the client wants (leading and # trailing, or one or the other, or neither) if self.lstrip_ws and self.rstrip_ws: - line = string.strip (line) + line = line.strip () elif self.lstrip_ws: - line = string.lstrip (line) + line = line.lstrip () elif self.rstrip_ws: - line = string.rstrip (line) + line = line.rstrip () # blank line (whether we rstrip'ed or not)? skip to next line # if appropriate @@ -313,7 +313,7 @@ line 3 \\ continues on next line """ # result 1: no fancy options - result1 = map (lambda x: x + "\n", string.split (test_data, "\n")[0:-1]) + result1 = map (lambda x: x + "\n", test_data.split ("\n")[0:-1]) # result 2: just strip comments result2 = ["\n", @@ -340,7 +340,7 @@ line 3 \\ def test_input (count, description, file, expected_result): result = file.readlines () - # result = string.join (result, '') + # result = ''.join (result) if result == expected_result: print("ok %d (%s)" % (count, description)) else: diff --git a/util.py b/util.py index 16d8bcba..6f15ce8b 100644 --- a/util.py +++ b/util.py @@ -42,10 +42,9 @@ def get_platform (): # Convert the OS name to lowercase, remove '/' characters # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") - osname = string.lower(osname) - osname = string.replace(osname, '/', '') - machine = string.replace(machine, ' ', '_') - machine = string.replace(machine, '/', '-') + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') if osname[:5] == "linux": # At least on Linux/Intel, 'machine' is the processor -- @@ -139,7 +138,7 @@ def convert_path (pathname): if pathname[-1] == '/': raise ValueError, "path '%s' cannot end with '/'" % pathname - paths = string.split(pathname, '/') + paths = pathname.split('/') while '.' in paths: paths.remove('.') if not paths: @@ -178,7 +177,7 @@ def change_root (new_root, pathname): return os.path.join(new_root, pathname) else: # Chop off volume name from start of path - elements = string.split(pathname, ":", 1) + elements = pathname.split(":", 1) pathname = ":" + elements[1] return os.path.join(new_root, pathname) @@ -281,7 +280,7 @@ def split_quoted (s): # bit of a brain-bender to get it working right, though... if _wordchars_re is None: _init_regex() - s = string.strip(s) + s = s.strip() words = [] pos = 0 @@ -294,7 +293,7 @@ def split_quoted (s): if s[end] in string.whitespace: # unescaped, unquoted whitespace: now words.append(s[:end]) # we definitely have a word delimiter - s = string.lstrip(s[end:]) + s = s[end:].lstrip() pos = 0 elif s[end] == '\\': # preserve whatever is being escaped; @@ -354,7 +353,7 @@ def strtobool (val): are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 'val' is anything else. """ - val = string.lower(val) + val = val.lower() if val in ('y', 'yes', 't', 'true', 'on', '1'): return 1 elif val in ('n', 'no', 'f', 'false', 'off', '0'): @@ -445,7 +444,7 @@ files = [ #if prefix: # prefix = os.path.abspath(prefix) - script.write(string.join(map(repr, py_files), ",\n") + "]\n") + script.write(",\n".join(map(repr, py_files)) + "]\n") script.write(""" byte_compile(files, optimize=%r, force=%r, prefix=%r, base_dir=%r, @@ -507,7 +506,6 @@ def rfc822_escape (header): """Return a version of the string escaped for inclusion in an RFC-822 header, by ensuring there are 8 spaces space after each newline. """ - lines = string.split(header, '\n') - lines = map(string.strip, lines) - header = string.join(lines, '\n' + 8*' ') - return header + lines = [x.strip() for x in header.split('\n')] + sep = '\n' + 8*' ' + return sep.join(lines) diff --git a/version.py b/version.py index 2cd36365..2db6b18f 100644 --- a/version.py +++ b/version.py @@ -26,8 +26,7 @@ Every version number class implements the following interface: of the same class, thus must follow the same rules) """ -import string, re -from types import StringType +import re class Version: """Abstract base class for version numbering classes. Just provides @@ -147,12 +146,12 @@ class StrictVersion (Version): match.group(1, 2, 4, 5, 6) if patch: - self.version = tuple(map(string.atoi, [major, minor, patch])) + self.version = tuple(map(int, [major, minor, patch])) else: - self.version = tuple(map(string.atoi, [major, minor]) + [0]) + self.version = tuple(map(int, [major, minor]) + [0]) if prerelease: - self.prerelease = (prerelease[0], string.atoi(prerelease_num)) + self.prerelease = (prerelease[0], int(prerelease_num)) else: self.prerelease = None @@ -160,9 +159,9 @@ class StrictVersion (Version): def __str__ (self): if self.version[2] == 0: - vstring = string.join(map(str, self.version[0:2]), '.') + vstring = '.'.join(map(str, self.version[0:2])) else: - vstring = string.join(map(str, self.version), '.') + vstring = '.'.join(map(str, self.version)) if self.prerelease: vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) @@ -171,7 +170,7 @@ class StrictVersion (Version): def __cmp__ (self, other): - if isinstance(other, StringType): + if isinstance(other, str): other = StrictVersion(other) compare = cmp(self.version, other.version) @@ -327,7 +326,7 @@ class LooseVersion (Version): def __cmp__ (self, other): - if isinstance(other, StringType): + if isinstance(other, str): other = LooseVersion(other) return cmp(self.version, other.version) -- cgit v1.2.1 From 44168b43a0a197dc0295f0e80734880cecf658ea Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 24 Apr 2007 15:27:13 +0000 Subject: Bug #1706381: Specifying the SWIG option "-c++" in the setup.py file (as opposed to the command line) will now write file names ending in ".cpp" too. --- command/build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 82474de8..12d40837 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -533,7 +533,8 @@ class build_ext (Command): if self.swig_cpp: log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") - if self.swig_cpp or ('-c++' in self.swig_opts): + if self.swig_cpp or ('-c++' in self.swig_opts) or \ + ('-c++' in extension.swig_opts): target_ext = '.cpp' else: target_ext = '.c' -- cgit v1.2.1 From 092ed8384228a0ca24fd297dde6243b6751a46a0 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 24 Apr 2007 15:27:25 +0000 Subject: Bug #1706381: Specifying the SWIG option "-c++" in the setup.py file (as opposed to the command line) will now write file names ending in ".cpp" too. (backport from rev. 54941) --- command/build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 82474de8..12d40837 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -533,7 +533,8 @@ class build_ext (Command): if self.swig_cpp: log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") - if self.swig_cpp or ('-c++' in self.swig_opts): + if self.swig_cpp or ('-c++' in self.swig_opts) or \ + ('-c++' in extension.swig_opts): target_ext = '.cpp' else: target_ext = '.c' -- cgit v1.2.1 From 0086340118b6b5d4243cb84b3e4c6df726a064da Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Wed, 25 Apr 2007 06:42:41 +0000 Subject: Whitespace normalization --- unixccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index d1fd1d95..75e8a531 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -82,7 +82,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): except ValueError: pass - # Check if the SDK that is used during compilation actually exists, + # Check if the SDK that is used during compilation actually exists, # the universal build requires the usage of a universal SDK and not all # users have that installed by default. sysroot = None -- cgit v1.2.1 From 078fadd293abe8b159cb6185a3ded2e87210c5f8 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 27 Apr 2007 19:54:29 +0000 Subject: Merged revisions 53952-54987 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r53954 | jeremy.hylton | 2007-02-26 10:41:18 -0800 (Mon, 26 Feb 2007) | 10 lines Do not copy free variables to locals in class namespaces. Fixes bug 1569356, but at the cost of a minor incompatibility in locals(). Add test that verifies that the class namespace is not polluted. Also clarify the behavior in the library docs. Along the way, cleaned up the dict_to_map and map_to_dict implementations and added some comments that explain what they do. ........ r53955 | jeremy.hylton | 2007-02-26 11:00:20 -0800 (Mon, 26 Feb 2007) | 2 lines Fix assertion. ........ r53969 | neal.norwitz | 2007-02-26 14:41:45 -0800 (Mon, 26 Feb 2007) | 3 lines When printing an unraisable error, don't print exceptions. before the name. This duplicates the behavior whening normally printing exceptions. ........ r53970 | andrew.kuchling | 2007-02-26 15:02:47 -0800 (Mon, 26 Feb 2007) | 1 line Markup fix ........ r53975 | neal.norwitz | 2007-02-26 15:48:27 -0800 (Mon, 26 Feb 2007) | 3 lines SF #1669182, 2.5 was already fixed. Just assert in 2.6 since string exceptions are gone. ........ r53976 | andrew.kuchling | 2007-02-26 15:54:17 -0800 (Mon, 26 Feb 2007) | 1 line Add some items ........ r53981 | jeremy.hylton | 2007-02-26 17:01:59 -0800 (Mon, 26 Feb 2007) | 4 lines Fix long-standing bug in name mangling for package imports Reported by Mike Verdone. ........ r53993 | jeremy.hylton | 2007-02-27 08:00:06 -0800 (Tue, 27 Feb 2007) | 2 lines tabify ........ r53994 | jeremy.hylton | 2007-02-27 08:13:23 -0800 (Tue, 27 Feb 2007) | 5 lines tabify Note that ast.c still has a mix of tabs and spaces, because it attempts to use four-space indents for more of the new code. ........ r53996 | jeremy.hylton | 2007-02-27 09:24:48 -0800 (Tue, 27 Feb 2007) | 2 lines whitespace normalization ........ r53997 | jeremy.hylton | 2007-02-27 10:29:45 -0800 (Tue, 27 Feb 2007) | 24 lines Add checking for a number of metaclass error conditions. We add some new rules that are required for preserving internal invariants of types. 1. If type (or a subclass of type) appears in bases, it must appear before any non-type bases. If a non-type base (like a regular new-style class) occurred first, it could trick type into allocating the new class an __dict__ which must be impossible. 2. There are several checks that are made of bases when creating a type. Those checks are now repeated when assigning to __bases__. We also add the restriction that assignment to __bases__ may not change the metaclass of the type. Add new tests for these cases and for a few other oddball errors that were no previously tested. Remove a crasher test that was fixed. Also some internal refactoring: Extract the code to find the most derived metaclass of a type and its bases. It is now needed in two places. Rewrite the TypeError checks in test_descr to use doctest. The tests now clearly show what exception they expect to see. ........ r53998 | jeremy.hylton | 2007-02-27 10:33:31 -0800 (Tue, 27 Feb 2007) | 2 lines Add news about changes to metaclasses and __bases__ error checking. ........ r54016 | armin.rigo | 2007-02-28 01:25:29 -0800 (Wed, 28 Feb 2007) | 3 lines Modify the segfaulting example to show why r53997 is not a solution to it. ........ r54022 | brett.cannon | 2007-02-28 10:15:00 -0800 (Wed, 28 Feb 2007) | 2 lines Add a test for instantiating SyntaxError with no arguments. ........ r54026 | raymond.hettinger | 2007-02-28 10:27:41 -0800 (Wed, 28 Feb 2007) | 1 line Docstring nit. ........ r54033 | raymond.hettinger | 2007-02-28 10:37:52 -0800 (Wed, 28 Feb 2007) | 1 line Prepare collections module for pure python code entries. ........ r54053 | raymond.hettinger | 2007-02-28 22:16:43 -0800 (Wed, 28 Feb 2007) | 1 line Add collections.NamedTuple ........ r54054 | neal.norwitz | 2007-02-28 23:04:41 -0800 (Wed, 28 Feb 2007) | 1 line Add Pat and Eric for work on PEP 3101 in the sandbox ........ r54061 | andrew.kuchling | 2007-03-01 06:36:12 -0800 (Thu, 01 Mar 2007) | 1 line Add NamedTuple ........ r54080 | georg.brandl | 2007-03-02 06:37:12 -0800 (Fri, 02 Mar 2007) | 2 lines Bug #1628895: some better tries to find HTML documentation in pydoc. ........ r54086 | raymond.hettinger | 2007-03-02 11:20:46 -0800 (Fri, 02 Mar 2007) | 1 line Fix embarrassing typo and fix constantification of None ........ r54088 | georg.brandl | 2007-03-02 12:30:14 -0800 (Fri, 02 Mar 2007) | 2 lines Bugs #1668032, #1668036, #1669304: clarify behavior of PyMem_Realloc and _Resize. ........ r54114 | georg.brandl | 2007-03-04 09:18:54 -0800 (Sun, 04 Mar 2007) | 2 lines Fix a bug in test_dict and test_userdict, found at the PyPy sprint. ........ r54124 | skip.montanaro | 2007-03-04 12:52:28 -0800 (Sun, 04 Mar 2007) | 2 lines Teach setup.py how to find Berkeley DB on Macs using MacPorts. ........ r54125 | skip.montanaro | 2007-03-04 12:54:12 -0800 (Sun, 04 Mar 2007) | 1 line note MacPorts/BerkDB change in setup.py ........ r54136 | neal.norwitz | 2007-03-04 23:52:01 -0800 (Sun, 04 Mar 2007) | 1 line Added Pete for 3101 too ........ r54138 | facundo.batista | 2007-03-05 08:31:54 -0800 (Mon, 05 Mar 2007) | 1 line Minor corrections to docs, and an explanation comentary ........ r54139 | georg.brandl | 2007-03-05 14:28:08 -0800 (Mon, 05 Mar 2007) | 3 lines Patch #1674228: when assigning a slice (old-style), check for the sq_ass_slice instead of the sq_slice slot. ........ r54149 | georg.brandl | 2007-03-06 01:33:01 -0800 (Tue, 06 Mar 2007) | 3 lines Nit: a struct field is set to GenericAlloc, not GenericAlloc(). ........ r54150 | georg.brandl | 2007-03-06 02:02:47 -0800 (Tue, 06 Mar 2007) | 3 lines Patch #1671450: add a section about subclassing builtin types to the "extending and embedding" tutorial. ........ r54152 | martin.v.loewis | 2007-03-06 02:41:24 -0800 (Tue, 06 Mar 2007) | 2 lines Patch #1121142: Implement ZipFile.open. ........ r54154 | georg.brandl | 2007-03-06 03:51:14 -0800 (Tue, 06 Mar 2007) | 2 lines A test case for the fix in #1674228. ........ r54156 | georg.brandl | 2007-03-06 03:52:24 -0800 (Tue, 06 Mar 2007) | 2 lines Patch #1672481: fix bug in idlelib.MultiCall. ........ r54159 | georg.brandl | 2007-03-06 04:17:50 -0800 (Tue, 06 Mar 2007) | 1 line Bug #1674503: close the file opened by execfile() in an error condition. ........ r54160 | georg.brandl | 2007-03-06 05:32:52 -0800 (Tue, 06 Mar 2007) | 3 lines Fix another reincarnation of bug #1576657 in defaultdict. ........ r54162 | georg.brandl | 2007-03-06 05:35:00 -0800 (Tue, 06 Mar 2007) | 2 lines A test case for the defaultdict KeyError bug. ........ r54164 | georg.brandl | 2007-03-06 05:37:45 -0800 (Tue, 06 Mar 2007) | 3 lines Patch #1663234: you can now run doctest on test files and modules using "python -m doctest [-v] filename ...". ........ r54165 | martin.v.loewis | 2007-03-06 06:43:00 -0800 (Tue, 06 Mar 2007) | 3 lines Patch #912410: Replace HTML entity references for attribute values in HTMLParser. ........ r54166 | skip.montanaro | 2007-03-06 07:41:38 -0800 (Tue, 06 Mar 2007) | 1 line patch 1673619 - identify extension modules which cannot be built ........ r54167 | guido.van.rossum | 2007-03-06 07:50:01 -0800 (Tue, 06 Mar 2007) | 5 lines Patch #1646728: datetime.fromtimestamp fails with negative fractional times. With unittest. Somebody please backport to 2.5. ........ r54169 | georg.brandl | 2007-03-06 09:49:14 -0800 (Tue, 06 Mar 2007) | 2 lines Fix cmp vs. key argument for list.sort. ........ r54170 | georg.brandl | 2007-03-06 10:21:32 -0800 (Tue, 06 Mar 2007) | 2 lines Small nit, found by Neal. ........ r54171 | georg.brandl | 2007-03-06 10:29:58 -0800 (Tue, 06 Mar 2007) | 3 lines Patch #1602128: clarify that richcmp methods can return NotImplemented and should return True or False otherwise. ........ r54173 | georg.brandl | 2007-03-06 10:41:12 -0800 (Tue, 06 Mar 2007) | 2 lines Patch #1638879: don't accept strings with embedded NUL bytes in long(). ........ r54175 | georg.brandl | 2007-03-06 10:47:31 -0800 (Tue, 06 Mar 2007) | 2 lines Patch #1673121: update README wrt. OSX default shell. ........ r54177 | georg.brandl | 2007-03-06 10:59:11 -0800 (Tue, 06 Mar 2007) | 3 lines Patch #1654417: make operator.{get,set,del}slice use the full range of Py_ssize_t. ........ r54180 | walter.doerwald | 2007-03-06 12:38:57 -0800 (Tue, 06 Mar 2007) | 4 lines Patch for bug #1633621: if curses.resizeterm() or curses.resize_term() is called, update _curses.LINES, _curses.COLS, curses.LINES and curses.COLS. ........ r54182 | walter.doerwald | 2007-03-06 13:15:24 -0800 (Tue, 06 Mar 2007) | 2 lines Document change to curses. ........ r54188 | georg.brandl | 2007-03-06 16:34:46 -0800 (Tue, 06 Mar 2007) | 5 lines Variant of patch #697613: don't exit the interpreter on a SystemExit exception if the -i command line option or PYTHONINSPECT environment variable is given, but break into the interactive interpreter just like on other exceptions or normal program exit. (backport) ........ r54189 | georg.brandl | 2007-03-06 16:40:28 -0800 (Tue, 06 Mar 2007) | 4 lines Patch #703779: unset __file__ in __main__ after running a file. This makes the filenames the warning module prints much more sensible when a PYTHONSTARTUP file is used. ........ r54192 | george.yoshida | 2007-03-06 20:21:18 -0800 (Tue, 06 Mar 2007) | 2 lines add versionadded info ........ r54195 | georg.brandl | 2007-03-06 23:39:06 -0800 (Tue, 06 Mar 2007) | 2 lines Patch #812285: allow multiple auth schemes in AbstractBasicAuthHandler. ........ r54197 | georg.brandl | 2007-03-07 00:31:51 -0800 (Wed, 07 Mar 2007) | 3 lines Patch #1001604: glob.glob() now returns unicode filenames if it was given a unicode argument and os.listdir() returns unicode filenames. ........ r54199 | georg.brandl | 2007-03-07 01:09:40 -0800 (Wed, 07 Mar 2007) | 3 lines Patches #1550273, #1550272: fix a few bugs in unittest and add a comprehensive test suite for the module. ........ r54201 | georg.brandl | 2007-03-07 01:21:06 -0800 (Wed, 07 Mar 2007) | 3 lines Patch #787789: allow to pass custom TestRunner instances to unittest's main() function. ........ r54202 | georg.brandl | 2007-03-07 01:34:45 -0800 (Wed, 07 Mar 2007) | 2 lines Patch #1669331: clarify shutil.copyfileobj() behavior wrt. file position. ........ r54204 | martin.v.loewis | 2007-03-07 03:04:33 -0800 (Wed, 07 Mar 2007) | 2 lines Bug #1115886: os.path.splitext('.cshrc') gives now ('.cshrc', ''). ........ r54206 | georg.brandl | 2007-03-07 03:37:42 -0800 (Wed, 07 Mar 2007) | 2 lines Patch #1675471: convert test_pty to unittest. ........ r54207 | georg.brandl | 2007-03-07 03:54:49 -0800 (Wed, 07 Mar 2007) | 4 lines Add some sanity checks to unittest.TestSuite's addTest(s) methods. Fixes #878275. ........ r54209 | guido.van.rossum | 2007-03-07 07:16:29 -0800 (Wed, 07 Mar 2007) | 3 lines Windows doesn't support negative timestamps. Skip the tests involving them if os.name == "nt". ........ r54219 | martin.v.loewis | 2007-03-08 05:42:43 -0800 (Thu, 08 Mar 2007) | 2 lines Add missing ) in parenthical remark. ........ r54220 | georg.brandl | 2007-03-08 09:49:06 -0800 (Thu, 08 Mar 2007) | 2 lines Fix #1676656: \em is different from \emph... ........ r54222 | georg.brandl | 2007-03-08 10:37:31 -0800 (Thu, 08 Mar 2007) | 2 lines Add a NEWS entry for rev. 54207,8. ........ r54225 | raymond.hettinger | 2007-03-08 11:24:27 -0800 (Thu, 08 Mar 2007) | 1 line SF 1676321: empty() returned wrong result ........ r54227 | collin.winter | 2007-03-08 11:58:14 -0800 (Thu, 08 Mar 2007) | 1 line Backported r54226 from p3yk: Move test_unittest, test_doctest and test_doctest2 higher up in the testing order. ........ r54230 | raymond.hettinger | 2007-03-08 13:33:47 -0800 (Thu, 08 Mar 2007) | 1 line SF #1637850: make_table in difflib did not work with unicode ........ r54232 | collin.winter | 2007-03-08 14:16:25 -0800 (Thu, 08 Mar 2007) | 1 line Patch #1668482: don't use '-' in mkstemp ........ r54233 | brett.cannon | 2007-03-08 15:58:11 -0800 (Thu, 08 Mar 2007) | 10 lines Introduce test.test_support.TransientResource. It's a context manager to surround calls to resources that may or may not be available. Specifying the expected exception and attributes to be raised if the resource is not available prevents overly broad catching of exceptions. This is meant to help suppress spurious failures by raising test.test_support.ResourceDenied if the exception matches. It would probably be good to go through the various network tests and surround the calls to catch connection timeouts (as done with test_socket_ssl in this commit). ........ r54234 | collin.winter | 2007-03-08 19:15:56 -0800 (Thu, 08 Mar 2007) | 1 line Patch #1481079: Support of HTTP_REFERER in CGIHTTPServer.py ........ r54235 | collin.winter | 2007-03-08 19:26:32 -0800 (Thu, 08 Mar 2007) | 1 line Add NEWS item for patch #1481079 (r54234). ........ r54237 | neal.norwitz | 2007-03-08 21:59:01 -0800 (Thu, 08 Mar 2007) | 1 line Fix SF #1676971, Complex OverflowError has a typo ........ r54239 | georg.brandl | 2007-03-09 04:58:41 -0800 (Fri, 09 Mar 2007) | 2 lines Typo. ........ r54240 | martin.v.loewis | 2007-03-09 07:35:55 -0800 (Fri, 09 Mar 2007) | 2 lines Patch #957003: Implement smtplib.LMTP. ........ r54243 | collin.winter | 2007-03-09 10:09:10 -0800 (Fri, 09 Mar 2007) | 2 lines Bug #1629566: clarify the docs on the return values of parsedate() and parsedate_tz() in email.utils and rfc822. ........ r54244 | thomas.heller | 2007-03-09 11:21:28 -0800 (Fri, 09 Mar 2007) | 3 lines Fix bug #1646630: ctypes.string_at(buf, 0) and ctypes.wstring_at(buf, 0) returned string up to the first NUL character. ........ r54245 | martin.v.loewis | 2007-03-09 11:36:01 -0800 (Fri, 09 Mar 2007) | 2 lines Add Ziga Seilnacht. ........ r54247 | collin.winter | 2007-03-09 12:33:07 -0800 (Fri, 09 Mar 2007) | 2 lines Patch #1491866: change the complex() constructor to allow parthensized forms. This means complex(repr(x)) now works instead of raising a ValueError. ........ r54248 | thomas.heller | 2007-03-09 12:39:22 -0800 (Fri, 09 Mar 2007) | 7 lines Bug #1651235: When a tuple was passed to a ctypes function call, Python would crash instead of raising an error. The crash was caused by a section of code that should have been removed long ago, at that time ctypes had other ways to pass parameters to function calls. ........ r54250 | collin.winter | 2007-03-09 15:30:39 -0800 (Fri, 09 Mar 2007) | 1 line Hashing simplification pointed out by Thomas Wouters. ........ r54252 | collin.winter | 2007-03-09 18:23:40 -0800 (Fri, 09 Mar 2007) | 5 lines * Unlink test files before and after each test; hopefully this will cut down on recent buildbot failures in test_islink. * Drop safe_remove() in favor of test_support.unlink(). * Fix the indentation of test_samefile so that it runs. ........ r54253 | collin.winter | 2007-03-09 18:51:26 -0800 (Fri, 09 Mar 2007) | 3 lines Bug #1531963: Make SocketServer.TCPServer's server_address always be equal to calling getsockname() on the server's socket. Will backport. ........ r54254 | neal.norwitz | 2007-03-09 19:19:18 -0800 (Fri, 09 Mar 2007) | 4 lines Simplify a little by handling the TCP case first. Update to use predominant style of spaces around = in args list and print to stderr if debugging. ........ r54256 | collin.winter | 2007-03-09 19:35:34 -0800 (Fri, 09 Mar 2007) | 1 line Add proper attribution for a bug fix. ........ r54257 | georg.brandl | 2007-03-09 23:38:14 -0800 (Fri, 09 Mar 2007) | 2 lines Typos. ........ r54260 | collin.winter | 2007-03-10 06:33:32 -0800 (Sat, 10 Mar 2007) | 1 line Convert an assert to a raise so it works even in the presence of -O. ........ r54262 | collin.winter | 2007-03-10 06:41:48 -0800 (Sat, 10 Mar 2007) | 2 lines Patch #1599845: Add an option to disable the implicit calls to server_bind() and server_activate() in the constructors for TCPServer, SimpleXMLRPCServer and DocXMLRPCServer. ........ r54268 | georg.brandl | 2007-03-11 00:28:46 -0800 (Sun, 11 Mar 2007) | 2 lines Add missing "return" statements in exception handler. ........ r54270 | ziga.seilnacht | 2007-03-11 08:54:54 -0700 (Sun, 11 Mar 2007) | 3 lines Patch #1675981: remove unreachable code from type.__new__() method. __dict__ and __weakref__ are removed from the slots tuple earlier in the code, in the loop that mangles slot names. Will backport. ........ r54271 | collin.winter | 2007-03-11 09:00:20 -0700 (Sun, 11 Mar 2007) | 3 lines Patch #1192590: Fix pdb's "ignore" and "condition" commands so they trap the IndexError caused by passing in an invalid breakpoint number. Will backport. ........ r54274 | vinay.sajip | 2007-03-11 11:32:07 -0700 (Sun, 11 Mar 2007) | 1 line Fix resource leak reported in SF #1516995. ........ r54278 | collin.winter | 2007-03-11 18:55:54 -0700 (Sun, 11 Mar 2007) | 4 lines Patch #1678662: ftp.python.org does not exist. So the testcode in urllib.py must use a more stable FTP. Will backport. ........ r54280 | barry.warsaw | 2007-03-11 20:20:01 -0700 (Sun, 11 Mar 2007) | 8 lines Tokio Kikuchi's fix for SF bug #1629369; folding whitespace allowed in the display name of an email address, e.g. Foo \tBar Test case added by Barry. ........ r54282 | skip.montanaro | 2007-03-11 20:30:50 -0700 (Sun, 11 Mar 2007) | 4 lines Sane humans would call these invalid tests, but Andrew McNamara pointed out that given the inputs in these tests Excel does indeed produce the output these tests expect. Document that for future confused folks. ........ r54283 | martin.v.loewis | 2007-03-12 03:50:39 -0700 (Mon, 12 Mar 2007) | 2 lines Bug #1675511: Use -Kpic instead of -xcode=pic32 on Solaris/x86. ........ r54285 | martin.v.loewis | 2007-03-12 04:01:10 -0700 (Mon, 12 Mar 2007) | 2 lines Patch #1677862: Require a space or tab after import in .pth files. ........ r54287 | georg.brandl | 2007-03-12 06:17:36 -0700 (Mon, 12 Mar 2007) | 8 lines Backport from Py3k branch: Patch #1591665: implement the __dir__() special function lookup in PyObject_Dir. Had to change a few bits of the patch because classobjs and __methods__ are still in Py2.6. ........ r54288 | georg.brandl | 2007-03-12 07:30:05 -0700 (Mon, 12 Mar 2007) | 3 lines Bug #1678647: write a newline after printing an exception in any case, even when converting the value to a string failed. ........ r54290 | collin.winter | 2007-03-12 08:57:19 -0700 (Mon, 12 Mar 2007) | 1 line Patch #1678088: convert test_operations to use unittest, fold the result into test_dict. ........ r54291 | collin.winter | 2007-03-12 09:11:39 -0700 (Mon, 12 Mar 2007) | 3 lines Bug #742342: make Python stop segfaulting on infinitely-recursive reload()s. Fixed by patch #922167. Will backport. ........ r54292 | georg.brandl | 2007-03-12 09:15:09 -0700 (Mon, 12 Mar 2007) | 2 lines Typo fix. ........ r54295 | collin.winter | 2007-03-12 10:24:07 -0700 (Mon, 12 Mar 2007) | 1 line Patch #1670993: Refactor test_threadedtempfile.py to use unittest. ........ r54296 | tim.peters | 2007-03-12 11:07:52 -0700 (Mon, 12 Mar 2007) | 2 lines Whitespace normalization. ........ r54297 | tim.peters | 2007-03-12 11:09:22 -0700 (Mon, 12 Mar 2007) | 2 lines Set missing svn:eol-style property on text files. ........ r54315 | brett.cannon | 2007-03-12 19:34:09 -0700 (Mon, 12 Mar 2007) | 8 lines Add test.test_support.transient_internet . Returns a context manager that nests test.test_support.TransientResource context managers that capture exceptions raised when the Internet connection is flaky. Initially using in test_socket_ssl but should probably be expanded to cover any test that should not raise the captured exceptions if the Internet connection works. ........ r54316 | brett.cannon | 2007-03-12 20:05:40 -0700 (Mon, 12 Mar 2007) | 2 lines Fix a typo where the variable name was not updated. ........ r54318 | neal.norwitz | 2007-03-12 21:59:58 -0700 (Mon, 12 Mar 2007) | 1 line Add Jerry Seutter for a bunch of his recent patches refactoring tests ........ r54319 | neal.norwitz | 2007-03-12 22:07:14 -0700 (Mon, 12 Mar 2007) | 7 lines Add some other acks for recent checkins: Brian Leair - 922167 Tomer Filiba - 1591665 Jeremy Jones - 1192590 ........ r54321 | neal.norwitz | 2007-03-12 22:31:38 -0700 (Mon, 12 Mar 2007) | 9 lines Fix some style nits: * lines too long * wrong indentation * space after a function name * wrong function name in error string * simplifying some logic Also add an error check to PyDict_SetItemString. ........ r54322 | georg.brandl | 2007-03-13 00:23:16 -0700 (Tue, 13 Mar 2007) | 2 lines Typo and grammar fixes. ........ r54323 | georg.brandl | 2007-03-13 00:50:57 -0700 (Tue, 13 Mar 2007) | 2 lines Patch #1679379: add documentation for fnmatch.translate(). ........ r54325 | georg.brandl | 2007-03-13 00:57:51 -0700 (Tue, 13 Mar 2007) | 2 lines Patch #1642844: comments to clarify the complexobject constructor. ........ r54326 | georg.brandl | 2007-03-13 01:14:27 -0700 (Tue, 13 Mar 2007) | 3 lines Patch #1668100: urllib2 now correctly raises URLError instead of OSError if accessing a local file via the file:// protocol fails. ........ r54327 | georg.brandl | 2007-03-13 02:32:11 -0700 (Tue, 13 Mar 2007) | 4 lines Patch #1635454: the csv.DictWriter class now includes the offending field names in its exception message if you try to write a record with a dictionary containing fields not in the CSV field names list. ........ r54328 | georg.brandl | 2007-03-13 02:41:31 -0700 (Tue, 13 Mar 2007) | 3 lines Patch #1555098: use str.join() instead of repeated string concatenation in robotparser. ........ r54329 | georg.brandl | 2007-03-13 03:06:48 -0700 (Tue, 13 Mar 2007) | 3 lines Patch #1542681: add entries for "with", "as" and "CONTEXTMANAGERS" to pydoc's help keywords. ........ r54331 | georg.brandl | 2007-03-13 03:19:22 -0700 (Tue, 13 Mar 2007) | 3 lines Patch #1569798: fix a bug in distutils when building Python from a directory within sys.exec_prefix. ........ r54333 | martin.v.loewis | 2007-03-13 03:24:00 -0700 (Tue, 13 Mar 2007) | 4 lines Patch #1449244: Support Unicode strings in email.message.Message.{set_charset,get_content_charset}. Will backport. ........ r54335 | lars.gustaebel | 2007-03-13 03:47:19 -0700 (Tue, 13 Mar 2007) | 34 lines This is the implementation of POSIX.1-2001 (pax) format read/write support. The TarInfo class now contains all necessary logic to process and create tar header data which has been moved there from the TarFile class. The fromtarfile() method was added. The new path and linkpath properties are aliases for the name and linkname attributes in correspondence to the pax naming scheme. The TarFile constructor and classmethods now accept a number of keyword arguments which could only be set as attributes before (e.g. dereference, ignore_zeros). The encoding and pax_headers arguments were added for pax support. There is a new tarinfo keyword argument that allows using subclassed TarInfo objects in TarFile. The boolean TarFile.posix attribute is deprecated, because now three tar formats are supported. Instead, the desired format for writing is specified using the constants USTAR_FORMAT, GNU_FORMAT and PAX_FORMAT as the format keyword argument. This change affects TarInfo.tobuf() as well. The test suite has been heavily reorganized and partially rewritten. A new testtar.tar was added that contains sample data in many formats from 4 different tar programs. Some bugs and quirks that also have been fixed: Directory names do no longer have a trailing slash in TarInfo.name or TarFile.getnames(). Adding the same file twice does not create a hardlink file member. The TarFile constructor does no longer need a name argument. The TarFile._mode attribute was renamed to mode and contains either 'r', 'w' or 'a'. ........ r54336 | georg.brandl | 2007-03-13 05:34:25 -0700 (Tue, 13 Mar 2007) | 3 lines Bug #1622896: fix a rare corner case where the bz2 module raised an error in spite of a succesful compression. ........ r54338 | lars.gustaebel | 2007-03-13 08:47:07 -0700 (Tue, 13 Mar 2007) | 3 lines Quick fix for tests that fail on systems with an encoding other than 'iso8859-1'. ........ r54339 | georg.brandl | 2007-03-13 10:43:32 -0700 (Tue, 13 Mar 2007) | 4 lines Patch #1603688: ConfigParser.SafeConfigParser now checks values that are set for invalid interpolation sequences that would lead to errors on reading back those values. ........ r54341 | georg.brandl | 2007-03-13 11:15:41 -0700 (Tue, 13 Mar 2007) | 3 lines Patch #1581073: add a flag to textwrap that prevents the dropping of whitespace while wrapping. ........ r54343 | georg.brandl | 2007-03-13 11:24:40 -0700 (Tue, 13 Mar 2007) | 2 lines Patch #1605192: list allowed states in error messages for imaplib. ........ r54344 | georg.brandl | 2007-03-13 11:31:49 -0700 (Tue, 13 Mar 2007) | 4 lines Patch #1537850: tempfile.NamedTemporaryFile now has a "delete" parameter which can be set to False to prevent the default delete-on-close behavior. ........ r54345 | collin.winter | 2007-03-13 11:53:04 -0700 (Tue, 13 Mar 2007) | 9 lines Add acks for recent patch checkins: Arvin Schnell - 1668482 S?\195?\169bastien Martini - 1481079 Heiko Wundram - 1491866 Damon Kohler - 1545011 Peter Parente - 1599845 Bjorn Lindqvist - 1678662 ........ r54346 | georg.brandl | 2007-03-13 12:00:36 -0700 (Tue, 13 Mar 2007) | 2 lines Acks for recent patches. ........ r54347 | georg.brandl | 2007-03-13 12:18:18 -0700 (Tue, 13 Mar 2007) | 3 lines Fix a tab. ........ r54348 | georg.brandl | 2007-03-13 12:32:21 -0700 (Tue, 13 Mar 2007) | 4 lines Patch #1533909: the timeit module now accepts callables in addition to strings for the code to time and the setup code. Also added two convenience functions for instantiating a Timer and calling its methods. ........ r54352 | georg.brandl | 2007-03-13 13:02:57 -0700 (Tue, 13 Mar 2007) | 3 lines Patch #1530482: add pydoc.render_doc() which returns the documentation for a thing instead of paging it to stdout, which pydoc.doc() does. ........ r54357 | thomas.heller | 2007-03-13 13:42:52 -0700 (Tue, 13 Mar 2007) | 1 line Patch #1649190: Adding support for _Bool to ctypes as c_bool, by David Remahl. ........ r54358 | georg.brandl | 2007-03-13 13:46:32 -0700 (Tue, 13 Mar 2007) | 2 lines Patch #1444529: the builtin compile() now accepts keyword arguments. (backport) ........ r54359 | thomas.heller | 2007-03-13 14:01:39 -0700 (Tue, 13 Mar 2007) | 1 line Add versionadded marker for ctypes.c_bool. ........ r54360 | georg.brandl | 2007-03-13 14:08:15 -0700 (Tue, 13 Mar 2007) | 3 lines Patch #1393667: pdb now has a "run" command which restarts the debugged Python program, optionally with different arguments. ........ r54361 | georg.brandl | 2007-03-13 14:32:01 -0700 (Tue, 13 Mar 2007) | 3 lines Deprecate commands.getstatus(). ........ r54362 | georg.brandl | 2007-03-13 14:32:56 -0700 (Tue, 13 Mar 2007) | 2 lines NEWS entry for getstatus() deprecation. ........ r54363 | georg.brandl | 2007-03-13 14:58:44 -0700 (Tue, 13 Mar 2007) | 4 lines Patch #1429539: pdb now correctly initializes the __main__ module for the debugged script, which means that imports from __main__ work correctly now. ........ r54364 | georg.brandl | 2007-03-13 15:07:36 -0700 (Tue, 13 Mar 2007) | 4 lines Patch #957650: "%var%" environment variable references are now properly expanded in ntpath.expandvars(), also "~user" home directory references are recognized and handled on Windows. ........ r54365 | georg.brandl | 2007-03-13 15:16:30 -0700 (Tue, 13 Mar 2007) | 2 lines Patch #1194449: correctly detect unbound methods in pydoc. ........ r54367 | georg.brandl | 2007-03-13 15:49:43 -0700 (Tue, 13 Mar 2007) | 5 lines Patch #1185447: binascii.b2a_qp() now correctly quotes binary characters with ASCII value less than 32. Also, it correctly quotes dots only if they occur on a single line, as opposed to the previous behavior of quoting dots if they are the second character of any line. ........ r54368 | collin.winter | 2007-03-13 16:02:15 -0700 (Tue, 13 Mar 2007) | 1 line Inline PyImport_GetModulesReloading(). ........ r54371 | barry.warsaw | 2007-03-13 21:59:50 -0700 (Tue, 13 Mar 2007) | 6 lines SF bug #1582282; decode_header() incorrectly splits not-conformant RFC 2047-like headers where there is no whitespace between encoded words. This fix changes the matching regexp to include a trailing lookahead assertion that the closing ?= must be followed by whitespace, newline, or end-of-string. This also changes the regexp to add the MULTILINE flag. ........ r54372 | gregory.p.smith | 2007-03-14 00:17:40 -0700 (Wed, 14 Mar 2007) | 2 lines correct order and names of the less often used keyword parameters. ........ r54373 | gregory.p.smith | 2007-03-14 00:19:50 -0700 (Wed, 14 Mar 2007) | 5 lines Its time to stop listing (Unix, Windows) when we really mean "everything but Mac OS 9" now that nobody is likely to use Python on Mac OS 9 and most of the (Mac) platform items are all OS X special API specific since OS X is unixy enough for these modules to be available out of the box. ........ r54376 | georg.brandl | 2007-03-14 01:27:52 -0700 (Wed, 14 Mar 2007) | 4 lines Bug #767111: fix long-standing bug in urllib which caused an AttributeError instead of an IOError when the server's response didn't contain a valid HTTP status line. ........ r54378 | ziga.seilnacht | 2007-03-14 05:24:09 -0700 (Wed, 14 Mar 2007) | 4 lines Patch #1680015: Don't modify __slots__ tuple if it contains an unicode name. Remove a reference leak that happened if the name could not be converted to string. Will backport. ........ r54386 | martin.v.loewis | 2007-03-14 13:02:31 -0700 (Wed, 14 Mar 2007) | 3 lines Patch #1559413: Fix test_cmd_line if sys.executable contains a space. Will backport. ........ r54389 | brett.cannon | 2007-03-14 14:40:13 -0700 (Wed, 14 Mar 2007) | 3 lines Note how test_socket_ssl has various exceptions that deal with a flaky Net connection are silenced. ........ r54390 | brett.cannon | 2007-03-14 14:44:15 -0700 (Wed, 14 Mar 2007) | 2 lines Raise ResourceDenied in test_urllib2net when the Net connection goes bad. ........ r54391 | neal.norwitz | 2007-03-14 21:41:20 -0700 (Wed, 14 Mar 2007) | 1 line Wrap a long line and fix a typo (is -> if) ........ r54392 | georg.brandl | 2007-03-15 00:38:14 -0700 (Thu, 15 Mar 2007) | 3 lines Patch #1680978: consistently use "alive" instead of "active" in the thread lib doc. ........ r54394 | georg.brandl | 2007-03-15 00:41:30 -0700 (Thu, 15 Mar 2007) | 3 lines Patch #1681153: the wave module now closes a file object it opened if initialization failed. ........ r54397 | ziga.seilnacht | 2007-03-15 04:44:55 -0700 (Thu, 15 Mar 2007) | 3 lines Patch #1462488: prevent a segfault in object_reduce_ex() by splitting the implementation for __reduce__ and __reduce_ex__ into two separate functions. Fixes bug #931877. Will backport. ........ r54404 | collin.winter | 2007-03-15 21:11:30 -0700 (Thu, 15 Mar 2007) | 3 lines Patch #1642547: Fix an error/crash when encountering syntax errors in complex if statements. Will backport. ........ r54406 | georg.brandl | 2007-03-16 00:55:09 -0700 (Fri, 16 Mar 2007) | 5 lines Bug #1681228: the webbrowser module now correctly uses the default GNOME or KDE browser, depending on whether there is a session of one of those present. Also, it tries the Windows default browser before trying Mozilla variants. (backport) ........ r54407 | georg.brandl | 2007-03-16 01:22:40 -0700 (Fri, 16 Mar 2007) | 4 lines Patch #1273829: os.walk() now has a "followlinks" parameter. If set to True (which is not the default), it visits symlinks pointing to directories. ........ r54408 | georg.brandl | 2007-03-16 01:24:21 -0700 (Fri, 16 Mar 2007) | 2 lines Add \versionadded tag. ........ r54409 | georg.brandl | 2007-03-16 01:33:47 -0700 (Fri, 16 Mar 2007) | 2 lines RFE #1670167: fix in isinstance() docs. ........ r54412 | ziga.seilnacht | 2007-03-16 04:59:38 -0700 (Fri, 16 Mar 2007) | 3 lines Patch #1623563: allow __class__ assignment for classes with __slots__. The old and the new class are still required to have the same slot names, but the order in which they are specified is not relevant. ........ r54413 | ziga.seilnacht | 2007-03-16 05:11:11 -0700 (Fri, 16 Mar 2007) | 2 lines Whitespace cleanup. Also remove the empty lines from the previous check in. ........ r54414 | jeremy.hylton | 2007-03-16 07:49:11 -0700 (Fri, 16 Mar 2007) | 2 lines Remove warning: funcion declaration isn't a prototype ........ r54415 | jeremy.hylton | 2007-03-16 08:59:47 -0700 (Fri, 16 Mar 2007) | 11 lines Clean up formatting of this file. The file should now follow PEP 7, except that it uses 4 space indents (in the style of Py3k). This particular code would be really hard to read with the regular tab idents. Other changes: - reflow long lines - change multi-line conditionals to have test at end of line ........ r54417 | collin.winter | 2007-03-16 14:13:35 -0700 (Fri, 16 Mar 2007) | 1 line Patch #1676994: Refactor test_popen2 to use unittest. ........ r54418 | collin.winter | 2007-03-16 14:15:35 -0700 (Fri, 16 Mar 2007) | 1 line Remove test/output/test_popen2 (missed in r54417). ........ r54419 | collin.winter | 2007-03-16 15:16:08 -0700 (Fri, 16 Mar 2007) | 1 line Patch 1339796: add a relpath() function to os.path. ........ r54421 | georg.brandl | 2007-03-17 09:08:45 -0700 (Sat, 17 Mar 2007) | 5 lines Patch #1675423: PyComplex_AsCComplex() now tries to convert an object to complex using its __complex__() method before falling back to the __float__() method. Therefore, the functions in the cmath module now can operate on objects that define a __complex__() method. (backport) ........ r54423 | gregory.p.smith | 2007-03-17 15:33:35 -0700 (Sat, 17 Mar 2007) | 2 lines move note to the correct section ........ r54426 | georg.brandl | 2007-03-18 01:25:00 -0700 (Sun, 18 Mar 2007) | 2 lines Patch #1682878: the new socket methods are recv_into and recvfrom_into, not *_buf. ........ r54432 | georg.brandl | 2007-03-18 11:28:25 -0700 (Sun, 18 Mar 2007) | 2 lines Patch #1678339: test case for bug in difflib. ........ r54439 | collin.winter | 2007-03-19 11:52:08 -0700 (Mon, 19 Mar 2007) | 1 line Patch #1630118: add a SpooledTemporaryFile class to tempfile. ........ r54441 | georg.brandl | 2007-03-19 12:02:48 -0700 (Mon, 19 Mar 2007) | 2 lines Patch #1683328: fixes and enhancements for "unparse" demo. ........ r54456 | neal.norwitz | 2007-03-19 22:07:28 -0700 (Mon, 19 Mar 2007) | 1 line Add some doc that was left out from some change to platform.py ........ r54457 | neal.norwitz | 2007-03-19 22:08:23 -0700 (Mon, 19 Mar 2007) | 1 line Add a comment about 3k migration ........ r54458 | neal.norwitz | 2007-03-19 22:21:21 -0700 (Mon, 19 Mar 2007) | 1 line Get rid of deprecation warning when testing commands.getstatus() ........ r54459 | neal.norwitz | 2007-03-19 22:23:09 -0700 (Mon, 19 Mar 2007) | 4 lines Try backing out 54407 to see if it corrects the problems on the Windows buildbots. This rev was backported, so we will need to keep both branches in sync, pending the outcome of the test after this checkin. ........ r54460 | neal.norwitz | 2007-03-19 23:13:25 -0700 (Mon, 19 Mar 2007) | 1 line Try to make this test more resistant to dropping from previous runs (ie, files that may exist but cause the test to fail). Should be backported (assuming it works :-) ........ r54461 | neal.norwitz | 2007-03-19 23:16:26 -0700 (Mon, 19 Mar 2007) | 1 line Try to make this test more resistant to dropping from previous runs (ie, files that may exist but cause the test to fail). Should be backported (assuming it works :-) ........ r54462 | neal.norwitz | 2007-03-19 23:53:17 -0700 (Mon, 19 Mar 2007) | 5 lines Try to be a little more resilient to errors. This might help the test pass, but my guess is that it won't. I'm guessing that some other test is leaving this file open which means it can't be removed under Windows AFAIK. ........ r54463 | neal.norwitz | 2007-03-20 01:14:57 -0700 (Tue, 20 Mar 2007) | 8 lines Try to get test_urllib to pass on Windows by closing the file. I'm guessing that's the problem. h.getfile() must be called *after* h.getreply() and the fp can be None. I'm not entirely convinced this is the best fix (or even correct). The buildbots will tell us if things improve or not. I don't know if this needs to be backported (assuming it actually works). ........ r54465 | raymond.hettinger | 2007-03-20 14:27:24 -0700 (Tue, 20 Mar 2007) | 1 line Extend work on rev 52962 and 53829 eliminating redundant PyObject_Hash() calls and fixing set/dict interoperability. ........ r54468 | georg.brandl | 2007-03-20 16:05:14 -0700 (Tue, 20 Mar 2007) | 2 lines Fix for glob.py if filesystem encoding is None. ........ r54479 | neal.norwitz | 2007-03-20 23:39:48 -0700 (Tue, 20 Mar 2007) | 1 line Remove unused file spotted by Paul Hankin ........ r54480 | georg.brandl | 2007-03-21 02:00:39 -0700 (Wed, 21 Mar 2007) | 3 lines Patch #1682205: a TypeError while unpacking an iterable is no longer masked by a generic one with the message "unpack non-sequence". ........ r54482 | georg.brandl | 2007-03-21 02:10:29 -0700 (Wed, 21 Mar 2007) | 2 lines New test for rev. 54407 which only uses directories under TESTFN. ........ r54483 | georg.brandl | 2007-03-21 02:16:53 -0700 (Wed, 21 Mar 2007) | 2 lines Patch #1684834: document some utility C API functions. ........ r54485 | georg.brandl | 2007-03-21 04:51:25 -0700 (Wed, 21 Mar 2007) | 2 lines Fix #1684254: split BROWSER contents with shlex to avoid displaying 'URL'. ........ r54487 | andrew.kuchling | 2007-03-21 07:32:43 -0700 (Wed, 21 Mar 2007) | 1 line Add comments on maintenance of this file ........ r54489 | andrew.kuchling | 2007-03-21 09:57:32 -0700 (Wed, 21 Mar 2007) | 1 line Fix sentence, and fix typo in example ........ r54490 | andrew.kuchling | 2007-03-21 09:59:20 -0700 (Wed, 21 Mar 2007) | 1 line Put code examples at left margin instead of indenting them ........ r54491 | facundo.batista | 2007-03-21 12:41:24 -0700 (Wed, 21 Mar 2007) | 1 line Minor clarification, saying that blocking means no timeout (from bug #882297) ........ r54492 | ziga.seilnacht | 2007-03-21 13:07:56 -0700 (Wed, 21 Mar 2007) | 2 lines Bug #1675967: re patterns pickled with older Python versions can now be unpickled. Will backport. ........ r54495 | raymond.hettinger | 2007-03-21 13:33:57 -0700 (Wed, 21 Mar 2007) | 1 line Add test and fix for fromkeys() optional argument. ........ r54524 | georg.brandl | 2007-03-22 01:05:45 -0700 (Thu, 22 Mar 2007) | 2 lines Bug #1685704: use -m switch in timeit docs. ........ r54533 | thomas.heller | 2007-03-22 12:44:31 -0700 (Thu, 22 Mar 2007) | 5 lines Back out "Patch #1643874: memory leak in ctypes fixed." The code in this patch leaves no way to give up the ownership of a BSTR instance. ........ r54538 | thomas.heller | 2007-03-22 13:34:37 -0700 (Thu, 22 Mar 2007) | 2 lines Explain the purpose of the b_needsfree flag (forward ported from release25-maint). ........ r54539 | guido.van.rossum | 2007-03-22 21:58:42 -0700 (Thu, 22 Mar 2007) | 12 lines - Bug #1683368: The object.__init__() and object.__new__() methods are now stricter in rejecting excess arguments. The only time when either allows excess arguments is when it is not overridden and the other one is. For backwards compatibility, when both are overridden, it is a deprecation warning (for now; maybe a Py3k warning later). When merging this into 3.0, the warnings should become errors. Note: without the change to string.py, lots of spurious warnings happen. What's going on there? ........ r54540 | neal.norwitz | 2007-03-22 22:17:23 -0700 (Thu, 22 Mar 2007) | 1 line Add Mark Dickinson for SF # 1675423. ........ r54541 | martin.v.loewis | 2007-03-23 03:35:49 -0700 (Fri, 23 Mar 2007) | 3 lines Patch #1686451: Fix return type for PySequence_{Count,Index,Fast_GET_SIZE}. Will backport. ........ r54543 | martin.v.loewis | 2007-03-23 06:27:15 -0700 (Fri, 23 Mar 2007) | 3 lines Bug #978833: Revert r50844, as it broke _socketobject.dup. Will backport. ........ r54545 | guido.van.rossum | 2007-03-23 11:53:03 -0700 (Fri, 23 Mar 2007) | 8 lines Add a type.__init__() method that enforces the same signature as type.__new__(), and then calls object.__init__(cls), just to be anal. This allows us to restore the code in string.py's _TemplateMetaclass that called super(...).__init__(name, bases, dct), which I commented out yesterday since it broke due to the stricter argument checking added to object.__init__(). ........ r54546 | facundo.batista | 2007-03-23 11:54:07 -0700 (Fri, 23 Mar 2007) | 4 lines Added a 'create_connect()' function to socket.py, which creates a connection with an optional timeout, and modified httplib.py to use this function in HTTPConnection. Applies patch 1676823. ........ r54547 | guido.van.rossum | 2007-03-23 12:39:01 -0700 (Fri, 23 Mar 2007) | 2 lines Add note about type.__init__(). ........ r54553 | thomas.heller | 2007-03-23 12:55:27 -0700 (Fri, 23 Mar 2007) | 5 lines Prevent creation (followed by a segfault) of array types when the size overflows the valid Py_ssize_t range. Check return values of PyMem_Malloc. Will backport to release25-maint. ........ r54555 | facundo.batista | 2007-03-23 13:23:08 -0700 (Fri, 23 Mar 2007) | 6 lines Surrounded with try/finally to socket's default timeout setting changes in the tests, so failing one test won't produce strange results in others. Also relaxed the timeout settings in the test (where actually the value didn't mean anything). ........ r54556 | collin.winter | 2007-03-23 15:24:39 -0700 (Fri, 23 Mar 2007) | 1 line Make test_relpath() pass on Windows. ........ r54559 | ziga.seilnacht | 2007-03-24 07:24:26 -0700 (Sat, 24 Mar 2007) | 6 lines Patch #1489771: update syntax rules in Python Reference Manual. Python 2.5 added support for explicit relative import statements and yield expressions, which were missing in the manual. Also fix grammar productions that used the names from the Grammar file, markup that broke the generated grammar.txt, and wrap some lines that broke the pdf output. Will backport. ........ r54565 | georg.brandl | 2007-03-24 15:20:34 -0700 (Sat, 24 Mar 2007) | 2 lines Remove typo accent. ........ r54566 | georg.brandl | 2007-03-24 15:27:56 -0700 (Sat, 24 Mar 2007) | 2 lines Revert accidental change. ........ r54567 | brett.cannon | 2007-03-24 18:32:36 -0700 (Sat, 24 Mar 2007) | 3 lines Change the docs to no longer claim that unittest is preferred over doctest for regression tests. ........ r54568 | facundo.batista | 2007-03-24 18:53:21 -0700 (Sat, 24 Mar 2007) | 4 lines Redone the tests, using the infrastructure already present for threading and socket serving. ........ r54570 | facundo.batista | 2007-03-24 20:20:05 -0700 (Sat, 24 Mar 2007) | 3 lines Closing the HTTP connection after each test, and listening more. ........ r54572 | georg.brandl | 2007-03-25 11:44:35 -0700 (Sun, 25 Mar 2007) | 2 lines Markup fix. ........ r54573 | georg.brandl | 2007-03-25 12:04:55 -0700 (Sun, 25 Mar 2007) | 2 lines Markup fix. ........ r54580 | facundo.batista | 2007-03-26 13:18:31 -0700 (Mon, 26 Mar 2007) | 5 lines Added an optional timeout to FTP class. Also I started a test_ftplib.py file to test the ftp lib (right now I included a basic test, the timeout one, and nothing else). ........ r54581 | georg.brandl | 2007-03-26 13:28:28 -0700 (Mon, 26 Mar 2007) | 2 lines Some nits. ........ r54582 | facundo.batista | 2007-03-26 13:56:09 -0700 (Mon, 26 Mar 2007) | 4 lines Forgot to add the file before the previous commit, here go the ftplib tests. ........ r54585 | facundo.batista | 2007-03-27 11:23:21 -0700 (Tue, 27 Mar 2007) | 5 lines Added an optional timeout to poplib.POP3. Also created a test_poplib.py file with a basic test and the timeout ones. Docs are also updated. ........ r54586 | facundo.batista | 2007-03-27 11:50:29 -0700 (Tue, 27 Mar 2007) | 3 lines The basic test cases of poplib.py. ........ r54594 | facundo.batista | 2007-03-27 20:45:20 -0700 (Tue, 27 Mar 2007) | 4 lines Bug 1688393. Adds a control of negative values in socket.recvfrom, which caused an ugly crash. ........ r54599 | facundo.batista | 2007-03-28 11:25:54 -0700 (Wed, 28 Mar 2007) | 5 lines Added timeout to smtplib (to SMTP and SMTP_SSL). Also created the test_smtplib.py file, with a basic test and the timeout ones. Docs are updated too. ........ r54603 | collin.winter | 2007-03-28 16:34:06 -0700 (Wed, 28 Mar 2007) | 3 lines Consolidate patches #1690164, 1683397, and 1690169, all of which refactor XML-related test suites. The patches are applied together because they use a common output/xmltests file. Thanks to Jerry Seutter for all three patches. ........ r54604 | collin.winter | 2007-03-28 19:28:16 -0700 (Wed, 28 Mar 2007) | 1 line Make test_zipfile clean up its temporary files properly. ........ r54605 | georg.brandl | 2007-03-29 00:41:32 -0700 (Thu, 29 Mar 2007) | 2 lines These are actually methods. ........ r54606 | georg.brandl | 2007-03-29 05:42:07 -0700 (Thu, 29 Mar 2007) | 4 lines In Windows' time.clock(), when QueryPerformanceFrequency() fails, the C lib's clock() is used, but it must be divided by CLOCKS_PER_SEC as for the POSIX implementation (thanks to #pypy). ........ r54608 | facundo.batista | 2007-03-29 11:22:35 -0700 (Thu, 29 Mar 2007) | 5 lines Added timout parameter to telnetlib.Telnet. Also created test_telnetlib.py with a basic test and timeout ones. Docs are also updated. ........ r54613 | facundo.batista | 2007-03-30 06:00:35 -0700 (Fri, 30 Mar 2007) | 4 lines Added the posibility to pass the timeout to FTP.connect, not only when instantiating the class. Docs and tests are updated. ........ r54614 | collin.winter | 2007-03-30 07:01:25 -0700 (Fri, 30 Mar 2007) | 1 line Bug #1688274: add documentation for C-level class objects. ........ r54615 | marc-andre.lemburg | 2007-03-30 08:01:42 -0700 (Fri, 30 Mar 2007) | 4 lines Bump the patch level version of distutils since there were a few bug fixes since the 2.5.0 release. ........ r54617 | georg.brandl | 2007-03-30 08:49:05 -0700 (Fri, 30 Mar 2007) | 2 lines Markup fix. ........ r54618 | georg.brandl | 2007-03-30 10:39:39 -0700 (Fri, 30 Mar 2007) | 2 lines Label name fix. ........ r54619 | georg.brandl | 2007-03-30 10:47:21 -0700 (Fri, 30 Mar 2007) | 2 lines Duplicate label fix. ........ r54620 | georg.brandl | 2007-03-30 10:48:39 -0700 (Fri, 30 Mar 2007) | 2 lines Markup fix. ........ r54623 | andrew.kuchling | 2007-03-30 11:00:15 -0700 (Fri, 30 Mar 2007) | 1 line Add item. (Oops, accidentally checked this in on my branch) ........ r54624 | georg.brandl | 2007-03-30 12:01:38 -0700 (Fri, 30 Mar 2007) | 2 lines Duplicate label fix. ........ r54625 | georg.brandl | 2007-03-30 12:14:02 -0700 (Fri, 30 Mar 2007) | 2 lines Markup fix. ........ r54629 | georg.brandl | 2007-03-31 03:17:31 -0700 (Sat, 31 Mar 2007) | 2 lines repair string literal. ........ r54630 | georg.brandl | 2007-03-31 04:54:58 -0700 (Sat, 31 Mar 2007) | 2 lines Markup fix. ........ r54631 | georg.brandl | 2007-03-31 04:58:36 -0700 (Sat, 31 Mar 2007) | 2 lines Duplicate label fix. ........ r54632 | georg.brandl | 2007-03-31 04:59:54 -0700 (Sat, 31 Mar 2007) | 2 lines Typo fix. ........ r54633 | neal.norwitz | 2007-03-31 11:54:18 -0700 (Sat, 31 Mar 2007) | 1 line Fix method names. Will backport. ........ r54634 | georg.brandl | 2007-03-31 11:56:11 -0700 (Sat, 31 Mar 2007) | 4 lines Bug #1655392: don't add -L/usr/lib/pythonX.Y/config to the LDFLAGS returned by python-config if Python was built with --enable-shared because that prevented the shared library from being used. ........ r54637 | collin.winter | 2007-03-31 12:31:34 -0700 (Sat, 31 Mar 2007) | 1 line Shut up an occaisonal buildbot error due to test files being left around. ........ r54644 | neal.norwitz | 2007-04-01 11:24:22 -0700 (Sun, 01 Apr 2007) | 11 lines SF #1685563, MSVCCompiler creates redundant and long PATH strings If MSVCCompiler.initialize() was called multiple times, the path would get duplicated. On Windows, this is a problem because the path is limited to 4k. There's no benefit in adding a path multiple times, so prevent that from occuring. We also normalize the path before checking for duplicates so things like /a and /a/ won't both be stored. Will backport. ........ r54646 | brett.cannon | 2007-04-01 11:47:27 -0700 (Sun, 01 Apr 2007) | 8 lines time.strptime's caching of its locale object was being recreated when the locale changed but not used during the function call it was recreated during. The test in this checkin is untested (OS X does not have the proper locale support for me to test), although the fix for the bug this deals with was tested by the OP (#1290505). Once the buildbots verify the test at least doesn't fail it becomes a backport candidate. ........ r54647 | brett.cannon | 2007-04-01 12:46:19 -0700 (Sun, 01 Apr 2007) | 3 lines Fix the test for recreating the locale cache object by not worrying about if one of the test locales cannot be set. ........ r54649 | georg.brandl | 2007-04-01 14:29:15 -0700 (Sun, 01 Apr 2007) | 2 lines Fix a lot of markup and meta-information glitches. ........ r54650 | georg.brandl | 2007-04-01 14:39:52 -0700 (Sun, 01 Apr 2007) | 2 lines Another fix. ........ r54651 | georg.brandl | 2007-04-01 15:39:10 -0700 (Sun, 01 Apr 2007) | 2 lines Lots of explicit class names for method and member descs. ........ r54652 | georg.brandl | 2007-04-01 15:40:12 -0700 (Sun, 01 Apr 2007) | 2 lines Explicit class names. ........ r54653 | georg.brandl | 2007-04-01 15:47:31 -0700 (Sun, 01 Apr 2007) | 2 lines Some semantic fixes. ........ r54654 | georg.brandl | 2007-04-01 16:29:10 -0700 (Sun, 01 Apr 2007) | 2 lines Remove bogus entry. ........ r54655 | georg.brandl | 2007-04-01 16:31:30 -0700 (Sun, 01 Apr 2007) | 2 lines Fix the class name of strings. ........ r54658 | raymond.hettinger | 2007-04-02 10:29:30 -0700 (Mon, 02 Apr 2007) | 1 line SF #1693079: Cannot save empty array in shelve ........ r54663 | raymond.hettinger | 2007-04-02 15:54:21 -0700 (Mon, 02 Apr 2007) | 3 lines Array module's buffer interface can now handle empty arrays. ........ r54664 | guido.van.rossum | 2007-04-02 16:55:37 -0700 (Mon, 02 Apr 2007) | 5 lines Fix warnings about object.__init__() signature. Two (test_array and test_descr) were bug IMO; the third (copy_reg) is a work-around which recognizes that object.__init__() doesn't do anything. ........ r54666 | raymond.hettinger | 2007-04-02 17:02:11 -0700 (Mon, 02 Apr 2007) | 1 line SF 1602378 Clarify docstrings for bisect ........ r54668 | raymond.hettinger | 2007-04-02 18:39:43 -0700 (Mon, 02 Apr 2007) | 3 lines SF #1382213: Tutorial section 9.5.1 ignores MRO for new-style classes ........ r54669 | matthias.klose | 2007-04-02 21:35:59 -0700 (Mon, 02 Apr 2007) | 4 lines - Fix an off-by-one bug in locale.strxfrm(). patch taken from http://bugs.debian.org/416934. ........ r54671 | georg.brandl | 2007-04-03 00:04:27 -0700 (Tue, 03 Apr 2007) | 9 lines Fix the strange case of \begin{methoddesc}[NNTP]{...} where \ifx#1\@undefined ended up comparing N and N, therefore executing the true part of the conditional, blowing up at \@undefined. ........ r54672 | facundo.batista | 2007-04-03 07:05:08 -0700 (Tue, 03 Apr 2007) | 4 lines Now using unittest for the tests infraestructure. Also split the tests in those who need the network, and that who doesn't. ........ r54673 | walter.doerwald | 2007-04-03 09:08:10 -0700 (Tue, 03 Apr 2007) | 4 lines Move the functionality for catching warnings in test_warnings.py into a separate class to that reusing the functionality in test_structmembers.py doesn't rerun the tests from test_warnings.py. ........ r54674 | walter.doerwald | 2007-04-03 09:16:24 -0700 (Tue, 03 Apr 2007) | 2 lines Document that CatchWarningTests is reused by test_structmembers.py. ........ r54675 | walter.doerwald | 2007-04-03 09:53:43 -0700 (Tue, 03 Apr 2007) | 4 lines Add tests for the filename. Test that the stacklevel is handled correctly. ........ r54676 | facundo.batista | 2007-04-03 10:29:48 -0700 (Tue, 03 Apr 2007) | 6 lines Added a SSL server to test_socket_ssl.py to be able to test locally. Now, it checks if have openssl available and run those specific tests (it starts openssl at the beggining of all the tests and then kills it at the end). ........ r54677 | walter.doerwald | 2007-04-03 11:33:29 -0700 (Tue, 03 Apr 2007) | 6 lines Implement a contextmanager test.test_support.catch_warning that can be used to catch the last warning issued by the warning framework. Change test_warnings.py and test_structmembers.py to use this new contextmanager. ........ r54678 | facundo.batista | 2007-04-03 14:15:34 -0700 (Tue, 03 Apr 2007) | 4 lines Changed the whole structure of startup and checking if the server is available. Hope to not get more false alarms. ........ r54681 | facundo.batista | 2007-04-04 07:10:40 -0700 (Wed, 04 Apr 2007) | 4 lines Fixed the way that the .pem files are looked for, and changed how to kill the process in win32 to use the _handle attribute. ........ r54682 | guido.van.rossum | 2007-04-04 10:43:02 -0700 (Wed, 04 Apr 2007) | 4 lines Fix a race condition in this test -- instead of assuming that it will take the test server thread at most 0.5 seconds to get ready, use an event variable. ........ r54683 | collin.winter | 2007-04-04 11:14:17 -0700 (Wed, 04 Apr 2007) | 1 line Clean up imports. ........ r54684 | collin.winter | 2007-04-04 11:16:24 -0700 (Wed, 04 Apr 2007) | 1 line Stop using test_support.verify(). ........ r54685 | martin.v.loewis | 2007-04-04 11:30:36 -0700 (Wed, 04 Apr 2007) | 2 lines Bug #1686475: Support stat'ing open files on Windows again. Will backport to 2.5. ........ r54687 | collin.winter | 2007-04-04 11:33:40 -0700 (Wed, 04 Apr 2007) | 1 line Make test_getopt use unittest. ........ r54688 | collin.winter | 2007-04-04 11:36:30 -0700 (Wed, 04 Apr 2007) | 1 line Make test_softspace use unittest. ........ r54689 | ziga.seilnacht | 2007-04-04 11:38:47 -0700 (Wed, 04 Apr 2007) | 2 lines Fix WalkTests.test_traversal() on Windows. The cleanup in MakedirTests.setUp() can now be removed. ........ r54695 | raymond.hettinger | 2007-04-05 11:00:03 -0700 (Thu, 05 Apr 2007) | 3 lines Bug #1563759: struct.unpack doens't support buffer protocol objects ........ r54697 | collin.winter | 2007-04-05 13:05:07 -0700 (Thu, 05 Apr 2007) | 1 line Convert test_long_future to use unittest. ........ r54698 | collin.winter | 2007-04-05 13:08:56 -0700 (Thu, 05 Apr 2007) | 1 line Convert test_normalization to use unittest. ........ r54699 | andrew.kuchling | 2007-04-05 18:11:58 -0700 (Thu, 05 Apr 2007) | 1 line Some grammar fixes ........ r54704 | collin.winter | 2007-04-06 12:27:40 -0700 (Fri, 06 Apr 2007) | 1 line Convert test_stringprep to use unittest. ........ r54705 | collin.winter | 2007-04-06 12:32:32 -0700 (Fri, 06 Apr 2007) | 1 line Import cleanup in test_crypt. ........ r54706 | collin.winter | 2007-04-06 13:00:05 -0700 (Fri, 06 Apr 2007) | 1 line Convert test_gc to use unittest. ........ r54707 | collin.winter | 2007-04-06 13:03:11 -0700 (Fri, 06 Apr 2007) | 1 line Convert test_module to use unittest. ........ r54711 | collin.winter | 2007-04-06 21:40:43 -0700 (Fri, 06 Apr 2007) | 1 line Convert test_fileinput to use unittest. ........ r54712 | brett.cannon | 2007-04-07 21:29:32 -0700 (Sat, 07 Apr 2007) | 5 lines Doc that file.next() has undefined behaviour when called on a file opened with 'w'. Closes bug #1569057. To be backported once 2.5 branch is unfrozen. ........ r54726 | vinay.sajip | 2007-04-09 09:16:10 -0700 (Mon, 09 Apr 2007) | 1 line Added optional timeout to SocketHandler.makeSocket (SF #1695948) ........ r54727 | ziga.seilnacht | 2007-04-09 12:10:29 -0700 (Mon, 09 Apr 2007) | 3 lines Patch #1695862: remove old test directory that causes test_urllib failures on Windows buildbots. The change is a one time fix and will be removed after a successful buildbot run. ........ r54729 | facundo.batista | 2007-04-09 20:00:37 -0700 (Mon, 09 Apr 2007) | 3 lines Minor fix to the tests pass ok even with -O. ........ r54730 | collin.winter | 2007-04-09 21:44:49 -0700 (Mon, 09 Apr 2007) | 1 line Typo fix. ........ r54732 | facundo.batista | 2007-04-10 05:58:45 -0700 (Tue, 10 Apr 2007) | 5 lines General clean-up. Lot of margin corrections, comments, some typos. Exceptions now are raised in the new style. And a mockup class is now also new style. Thanks Santiago Pereson. ........ r54741 | georg.brandl | 2007-04-10 14:39:38 -0700 (Tue, 10 Apr 2007) | 2 lines Repair a duplicate label and some obsolete uses of \setindexsubitem. ........ r54746 | andrew.kuchling | 2007-04-11 06:39:00 -0700 (Wed, 11 Apr 2007) | 1 line Add window.chgat() method, submitted via e-mail by Fabian Kreutz ........ r54747 | andrew.kuchling | 2007-04-11 06:42:25 -0700 (Wed, 11 Apr 2007) | 1 line Point readers at the patch submission instructions ........ r54748 | andrew.kuchling | 2007-04-11 06:47:13 -0700 (Wed, 11 Apr 2007) | 1 line Describe undocumented third argument to touchline() ........ r54757 | georg.brandl | 2007-04-11 10:16:24 -0700 (Wed, 11 Apr 2007) | 3 lines Add some missing NULL checks which trigger crashes on low-memory conditions. Found by Victor Stinner. Will backport when 2.5 branch is unfrozen. ........ r54760 | raymond.hettinger | 2007-04-11 11:40:58 -0700 (Wed, 11 Apr 2007) | 1 line SF 1191699: Make slices picklable ........ r54762 | georg.brandl | 2007-04-11 12:25:11 -0700 (Wed, 11 Apr 2007) | 2 lines Exceptions are no longer old-style instances. Fix accordingly. ........ r54763 | georg.brandl | 2007-04-11 16:28:44 -0700 (Wed, 11 Apr 2007) | 2 lines Repair missing spaces after \UNIX. ........ r54772 | raymond.hettinger | 2007-04-11 21:10:00 -0700 (Wed, 11 Apr 2007) | 1 line SF 1193128: Let str.translate(None) be an identity transformation ........ r54784 | georg.brandl | 2007-04-12 00:01:19 -0700 (Thu, 12 Apr 2007) | 2 lines Patch #1698951: clarify deprecation message in rexec and Bastion ........ r54785 | ziga.seilnacht | 2007-04-12 01:46:51 -0700 (Thu, 12 Apr 2007) | 2 lines Patch #1695862: remove the cleanup code, now that Windows buildbots are green again. ........ r54786 | walter.doerwald | 2007-04-12 03:35:00 -0700 (Thu, 12 Apr 2007) | 3 lines Fix utf-8-sig incremental decoder, which didn't recognise a BOM when the first chunk fed to the decoder started with a BOM, but was longer than 3 bytes. ........ r54807 | barry.warsaw | 2007-04-13 11:47:14 -0700 (Fri, 13 Apr 2007) | 8 lines Port r54805 from python25-maint branch: Add code to read from master_fd in the parent, breaking when we get an OSError (EIO can occur on Linux) or there's no more data to read. Without this, test_pty.py can hang on the waitpid() because the child is blocking on the stdout write. This will definitely happen on Mac OS X and could potentially happen on other platforms. See the comment for details. ........ r54812 | kristjan.jonsson | 2007-04-13 15:07:33 -0700 (Fri, 13 Apr 2007) | 1 line Fix a bug when using the __lltrace__ opcode tracer, and a problem sith signed chars in frameobject.c which can occur with opcodes > 127 ........ r54814 | kristjan.jonsson | 2007-04-13 15:20:13 -0700 (Fri, 13 Apr 2007) | 1 line Fix potential crash in path manipulation on windows ........ r54816 | trent.mick | 2007-04-13 16:22:05 -0700 (Fri, 13 Apr 2007) | 4 lines Add the necessary dependency for the Windows VC6 build to ensure 'pythoncore' is built before '_ctypes' is attempted. Will backport to 2.5 once it is unfrozen for 2.5.1. ........ r54825 | neal.norwitz | 2007-04-13 22:25:50 -0700 (Fri, 13 Apr 2007) | 3 lines When __slots__ are set to a unicode string, make it work the same as setting a plain string, ie don't expand to single letter identifiers. ........ r54841 | neal.norwitz | 2007-04-16 00:37:55 -0700 (Mon, 16 Apr 2007) | 1 line SF #1701207, Fix bogus assertion (and test it!) ........ r54844 | collin.winter | 2007-04-16 15:10:32 -0700 (Mon, 16 Apr 2007) | 1 line Check the availability of the urlfetch resource earlier than before. ........ r54849 | martin.v.loewis | 2007-04-16 22:02:01 -0700 (Mon, 16 Apr 2007) | 2 lines Add Travis Oliphant. ........ r54873 | brett.cannon | 2007-04-18 20:44:17 -0700 (Wed, 18 Apr 2007) | 2 lines Silence a compiler warning about incompatible pointer types. ........ r54874 | neal.norwitz | 2007-04-18 22:52:37 -0700 (Wed, 18 Apr 2007) | 2 lines SF #1703270, add missing declaration in readline.c to avoid compiler warning. ........ r54875 | armin.rigo | 2007-04-19 07:44:48 -0700 (Thu, 19 Apr 2007) | 8 lines Revert r53997 as per http://mail.python.org/pipermail/python-dev/2007-March/071796.html . I've kept a couple of still-valid extra tests in test_descr, but didn't bother to sort through the new comments and refactorings added in r53997 to see if some of them could be kept. If so, they could go in a follow-up check-in. ........ r54876 | armin.rigo | 2007-04-19 07:56:48 -0700 (Thu, 19 Apr 2007) | 2 lines Fix a usage of the dangerous pattern decref - modify field - incref. ........ r54884 | neal.norwitz | 2007-04-19 22:20:38 -0700 (Thu, 19 Apr 2007) | 9 lines Add an optional address to copy the failure mails to. Detect a conflict in the only file that should have outstanding changes when this script is run. This doesn't matter on the trunk, but does when run on a branch. Trunk always has the date set to today in boilerplate.tex. Each time a release is cut with a different date, a conflict occurs. (We could copy a known good version, but then we would lose changes to this file.) ........ r54918 | georg.brandl | 2007-04-21 13:35:38 -0700 (Sat, 21 Apr 2007) | 3 lines Bug #1704790: bind name "sys" locally in __del__ method so that it is not cleared before __del__ is run. ........ r54920 | facundo.batista | 2007-04-21 18:18:56 -0700 (Sat, 21 Apr 2007) | 5 lines Added tests for other methods of SSL object. Now we cover all the object methods. This is the final step to close the #451607 bug. ........ r54927 | facundo.batista | 2007-04-23 10:08:31 -0700 (Mon, 23 Apr 2007) | 5 lines As specified in RFC 2616, 2xx code indicates that the client's request was successfully received, understood, and accepted. Now in these cases no error is raised. Also fixed tests. ........ r54929 | collin.winter | 2007-04-23 20:43:46 -0700 (Mon, 23 Apr 2007) | 1 line Convert PyUnit -> unittest. ........ r54931 | collin.winter | 2007-04-23 21:09:52 -0700 (Mon, 23 Apr 2007) | 1 line Remove code that hasn't been called in years. ........ r54932 | neal.norwitz | 2007-04-23 21:53:12 -0700 (Mon, 23 Apr 2007) | 1 line Fix SF #1703110, Incorrect example for add_password() (use uri, not host) ........ r54934 | georg.brandl | 2007-04-24 03:36:42 -0700 (Tue, 24 Apr 2007) | 2 lines Some new year updates. ........ r54938 | facundo.batista | 2007-04-24 06:54:38 -0700 (Tue, 24 Apr 2007) | 4 lines Added a comment about last change in urllib2.py (all 2xx responses are ok now). ........ r54939 | georg.brandl | 2007-04-24 08:10:09 -0700 (Tue, 24 Apr 2007) | 2 lines Bug #1705717: error in sys.argv docs. ........ r54941 | georg.brandl | 2007-04-24 08:27:13 -0700 (Tue, 24 Apr 2007) | 4 lines Bug #1706381: Specifying the SWIG option "-c++" in the setup.py file (as opposed to the command line) will now write file names ending in ".cpp" too. ........ r54944 | raymond.hettinger | 2007-04-24 15:13:43 -0700 (Tue, 24 Apr 2007) | 1 line Fix markup ........ r54945 | kristjan.jonsson | 2007-04-24 17:10:50 -0700 (Tue, 24 Apr 2007) | 1 line Merge change 54909 from release25-maint: Fix several minor issues discovered using code analysis in VisualStudio 2005 Team Edition ........ r54947 | kristjan.jonsson | 2007-04-24 17:17:39 -0700 (Tue, 24 Apr 2007) | 1 line Make pythoncore compile cleanly with VisualStudio 2005. Used an explicit typecast to get a 64 bit integer, and undefined the Yield macro that conflicts with winbase.h ........ r54948 | kristjan.jonsson | 2007-04-24 17:19:26 -0700 (Tue, 24 Apr 2007) | 1 line Remove obsolete comment. Importing of .dll files has been discontinued, only .pyd files supported on windows now. ........ r54949 | georg.brandl | 2007-04-24 23:24:59 -0700 (Tue, 24 Apr 2007) | 2 lines Patch #1698768: updated the "using Python on the Mac" intro. ........ r54951 | georg.brandl | 2007-04-24 23:25:55 -0700 (Tue, 24 Apr 2007) | 2 lines Markup fix. ........ r54953 | neal.norwitz | 2007-04-24 23:30:05 -0700 (Tue, 24 Apr 2007) | 3 lines Whitespace normalization. Ugh, we really need to do this more often. You might want to review this change as it's my first time. Be gentle. :-) ........ r54956 | collin.winter | 2007-04-25 10:29:52 -0700 (Wed, 25 Apr 2007) | 1 line Standardize on test.test_support.run_unittest() (as opposed to a mix of run_unittest() and run_suite()). Also, add functionality to run_unittest() that admits usage of unittest.TestLoader.loadTestsFromModule(). ........ r54957 | collin.winter | 2007-04-25 10:37:35 -0700 (Wed, 25 Apr 2007) | 1 line Remove functionality from test_datetime.test_main() that does reference count checking; 'regrtest.py -R' is the way to do this kind of testing. ........ r54958 | collin.winter | 2007-04-25 10:57:53 -0700 (Wed, 25 Apr 2007) | 1 line Change test_support.have_unicode to use True/False instead of 1/0. ........ r54959 | tim.peters | 2007-04-25 11:47:18 -0700 (Wed, 25 Apr 2007) | 2 lines Whitespace normalization. ........ r54960 | tim.peters | 2007-04-25 11:48:35 -0700 (Wed, 25 Apr 2007) | 2 lines Set missing svn:eol-style property on text files. ........ r54961 | collin.winter | 2007-04-25 11:54:36 -0700 (Wed, 25 Apr 2007) | 1 line Import and raise statement cleanup. ........ r54969 | collin.winter | 2007-04-25 13:41:34 -0700 (Wed, 25 Apr 2007) | 1 line Convert test_ossaudiodev to use unittest. ........ r54974 | collin.winter | 2007-04-25 14:50:25 -0700 (Wed, 25 Apr 2007) | 1 line Fix an issue related to the unittest conversion. ........ r54979 | fred.drake | 2007-04-25 21:42:19 -0700 (Wed, 25 Apr 2007) | 1 line fix some markup errors ........ r54982 | kristjan.jonsson | 2007-04-26 02:15:08 -0700 (Thu, 26 Apr 2007) | 1 line Export function sanitize_the_mode from fileobject.c as _PyFile_SanitizeMode(). Use this function in posixmodule.c when implementing fdopen(). This fixes test_subprocess.py for a VisualStudio 2005 compile. ........ r54983 | kristjan.jonsson | 2007-04-26 06:44:16 -0700 (Thu, 26 Apr 2007) | 1 line The locale "En" appears not to be valid on windows underi VisualStudio.2005. Added "English" to the test_locale.py to make the testsuite pass for that build ........ r54984 | steve.holden | 2007-04-26 07:23:12 -0700 (Thu, 26 Apr 2007) | 1 line Minor wording change on slicing aide-memoire. ........ r54985 | kristjan.jonsson | 2007-04-26 08:24:54 -0700 (Thu, 26 Apr 2007) | 1 line Accomodate 64 bit time_t in the _bsddb module. ........ --- __init__.py | 2 +- command/build_ext.py | 7 ++++--- msvccompiler.py | 16 +++++++++++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/__init__.py b/__init__.py index 21d34c76..86ad44fe 100644 --- a/__init__.py +++ b/__init__.py @@ -20,4 +20,4 @@ __revision__ = "$Id$" # In general, major and minor version should loosely follow the Python # version number the distutils code was shipped with. # -__version__ = "2.5.0" +__version__ = "2.5.1" diff --git a/command/build_ext.py b/command/build_ext.py index d0cd1629..2832d571 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ class build_ext (Command): # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': - if sys.executable.find(sys.exec_prefix) != -1: + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", "python" + get_python_version(), @@ -199,7 +199,7 @@ class build_ext (Command): # Python's library directory must be appended to library_dirs if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ and sysconfig.get_config_var('Py_ENABLE_SHARED'): - if sys.executable.find(sys.exec_prefix) != -1: + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: @@ -533,7 +533,8 @@ class build_ext (Command): if self.swig_cpp: log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") - if self.swig_cpp or ('-c++' in self.swig_opts): + if self.swig_cpp or ('-c++' in self.swig_opts) or \ + ('-c++' in extension.swig_opts): target_ext = '.cpp' else: target_ext = '.c' diff --git a/msvccompiler.py b/msvccompiler.py index ca1feaa0..07c76f18 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -187,6 +187,19 @@ def get_build_architecture(): j = sys.version.find(")", i) return sys.version[i+len(prefix):j] +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths class MSVCCompiler (CCompiler) : @@ -270,7 +283,8 @@ class MSVCCompiler (CCompiler) : self.__paths.append(p) except KeyError: pass - os.environ['path'] = ';'.join(self.__paths) + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) self.preprocess_options = None if self.__arch == "Intel": -- cgit v1.2.1 From 3bf5ce40cfaf14aa42593b4c4e5c28338c31633a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 27 Apr 2007 23:53:51 +0000 Subject: Checkpoint. Manipulated things so that string literals are always unicode, and a few other compensating changes, e.g. str <- unicode, chr <- unichr, and repr() of a unicode string no longer starts with 'u'. Lots of unit tests are broken, but some basic things work, in particular distutils works so the extensions can be built, and test_builtin.py works. --- ccompiler.py | 12 ++++++------ cmd.py | 9 ++++----- command/build_clib.py | 4 ++-- command/build_ext.py | 10 +++++----- command/build_py.py | 2 +- command/config.py | 8 ++++---- command/install.py | 2 +- command/install_data.py | 3 +-- dir_util.py | 2 +- dist.py | 6 +++--- extension.py | 4 ++-- fancy_getopt.py | 4 ++-- filelist.py | 2 +- unixccompiler.py | 4 ++-- 14 files changed, 35 insertions(+), 37 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 25d90c8a..50905c1d 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -168,7 +168,7 @@ class CCompiler: # set_executables () def set_executable(self, key, value): - if type(value) is StringType: + if isinstance(value, basestring): setattr(self, key, split_quoted(value)) else: setattr(self, key, value) @@ -193,8 +193,8 @@ class CCompiler: if not (type (defn) is TupleType and (len (defn) == 1 or (len (defn) == 2 and - (type (defn[1]) is StringType or defn[1] is None))) and - type (defn[0]) is StringType): + (isinstance (defn[1], basestring) or defn[1] is None))) and + isinstance (defn[0], basestring)): raise TypeError, \ ("invalid macro definition '%s': " % defn) + \ "must be tuple (string,), (string, string), or " + \ @@ -344,7 +344,7 @@ class CCompiler: """ if outdir is None: outdir = self.output_dir - elif type(outdir) is not StringType: + elif not isinstance(outdir, basestring): raise TypeError, "'output_dir' must be a string or None" if macros is None: @@ -442,7 +442,7 @@ class CCompiler: """ if output_dir is None: output_dir = self.output_dir - elif type (output_dir) is not StringType: + elif not isinstance(output_dir, basestring): raise TypeError, "'output_dir' must be a string or None" if macros is None: @@ -527,7 +527,7 @@ class CCompiler: if output_dir is None: output_dir = self.output_dir - elif type (output_dir) is not StringType: + elif not isinstance(output_dir, basestring): raise TypeError, "'output_dir' must be a string or None" return (objects, output_dir) diff --git a/cmd.py b/cmd.py index ebd99310..ea3799af 100644 --- a/cmd.py +++ b/cmd.py @@ -222,7 +222,7 @@ class Command: if val is None: setattr(self, option, default) return default - elif type(val) is not StringType: + elif not isinstance(val, basestring): raise DistutilsOptionError, \ "'%s' must be a %s (got `%s`)" % (option, what, val) return val @@ -242,12 +242,11 @@ class Command: val = getattr(self, option) if val is None: return - elif type(val) is StringType: + elif isinstance(val, basestring): setattr(self, option, re.split(r',\s*|\s+', val)) else: if type(val) is ListType: - types = map(type, val) - ok = (types == [StringType] * len(val)) + ok = all(isinstance(v, basestring) for v in val) else: ok = 0 @@ -421,7 +420,7 @@ class Command: # Allow 'infiles' to be a single string - if type(infiles) is StringType: + if isinstance(infiles, basestring): infiles = (infiles,) elif type(infiles) not in (ListType, TupleType): raise TypeError, \ diff --git a/command/build_clib.py b/command/build_clib.py index 07f58179..5434e86c 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -92,7 +92,7 @@ class build_clib (Command): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - if type(self.include_dirs) is StringType: + if isinstance(self.include_dirs, basestring): self.include_dirs = self.include_dirs.split(os.pathsep) # XXX same as for build_ext -- what about 'self.define' and @@ -147,7 +147,7 @@ class build_clib (Command): raise DistutilsSetupError, \ "each element of 'libraries' must a 2-tuple" - if type(lib[0]) is not StringType: + if isinstance(lib[0], basestring) StringType: raise DistutilsSetupError, \ "first element of each tuple in 'libraries' " + \ "must be a string (the library name)" diff --git a/command/build_ext.py b/command/build_ext.py index 2832d571..0236a268 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -137,7 +137,7 @@ class build_ext (Command): plat_py_include = sysconfig.get_python_inc(plat_specific=1) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - if type(self.include_dirs) is StringType: + if isinstance(self.include_dirs, basestring): self.include_dirs = self.include_dirs.split(os.pathsep) # Put the Python "system" include dir at the end, so that @@ -146,7 +146,7 @@ class build_ext (Command): if plat_py_include != py_include: self.include_dirs.append(plat_py_include) - if type(self.libraries) is StringType: + if isinstance(self.libraries, basestring): self.libraries = [self.libraries] # Life is easier if we're not forever checking for None, so @@ -155,12 +155,12 @@ class build_ext (Command): self.libraries = [] if self.library_dirs is None: self.library_dirs = [] - elif type(self.library_dirs) is StringType: + elif isinstance(self.library_dirs, basestring): self.library_dirs = self.library_dirs.split(os.pathsep) if self.rpath is None: self.rpath = [] - elif type(self.rpath) is StringType: + elif isinstance(self.rpath, basestring): self.rpath = self.rpath.split(os.pathsep) # for extensions under windows use different directories @@ -321,7 +321,7 @@ class build_ext (Command): ("each element of 'ext_modules' option must be an " "Extension instance or 2-tuple") - if not (type(ext_name) is StringType and + if not (isinstance(ext_name, basestring) and extension_name_re.match(ext_name)): raise DistutilsSetupError, \ ("first element of each tuple in 'ext_modules' " diff --git a/command/build_py.py b/command/build_py.py index 990824bd..52534bdb 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -361,7 +361,7 @@ class build_py (Command): def build_module (self, module, module_file, package): - if type(package) is StringType: + if isinstance(package, basestring): package = package.split('.') elif type(package) not in (ListType, TupleType): raise TypeError, \ diff --git a/command/config.py b/command/config.py index 3374db9e..04cfcde7 100644 --- a/command/config.py +++ b/command/config.py @@ -73,17 +73,17 @@ class config (Command): def finalize_options (self): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - elif type(self.include_dirs) is StringType: + elif isinstance(self.include_dirs, basestring): self.include_dirs = self.include_dirs.split(os.pathsep) if self.libraries is None: self.libraries = [] - elif type(self.libraries) is StringType: + elif isinstance(self.libraries, basestring): self.libraries = [self.libraries] if self.library_dirs is None: self.library_dirs = [] - elif type(self.library_dirs) is StringType: + elif isinstance(self.library_dirs, basestring): self.library_dirs = self.library_dirs.split(os.pathsep) @@ -212,7 +212,7 @@ class config (Command): self._check_compiler() (src, out) = self._preprocess(body, headers, include_dirs, lang) - if type(pattern) is StringType: + if isinstance(pattern, basestring): pattern = re.compile(pattern) file = open(out) diff --git a/command/install.py b/command/install.py index c03a90e8..cc92e963 100644 --- a/command/install.py +++ b/command/install.py @@ -463,7 +463,7 @@ class install (Command): self.extra_path = self.distribution.extra_path if self.extra_path is not None: - if type(self.extra_path) is StringType: + if isinstance(self.extra_path, basestring): self.extra_path = self.extra_path.split(',') if len(self.extra_path) == 1: diff --git a/command/install_data.py b/command/install_data.py index 1069830f..a9519442 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -10,7 +10,6 @@ platform-independent data files.""" __revision__ = "$Id$" import os -from types import StringType from distutils.core import Command from distutils.util import change_root, convert_path @@ -48,7 +47,7 @@ class install_data (Command): def run (self): self.mkpath(self.install_dir) for f in self.data_files: - if type(f) is StringType: + if isinstance(f, basestring): # it's a simple file, so copy it f = convert_path(f) if self.warn_dir: diff --git a/dir_util.py b/dir_util.py index 398f2429..c6f014b2 100644 --- a/dir_util.py +++ b/dir_util.py @@ -31,7 +31,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): global _path_created # Detect a common bug -- name is None - if not isinstance(name, StringTypes): + if not isinstance(name, basestring): raise DistutilsInternalError, \ "mkpath: 'name' must be a string (got %r)" % (name,) diff --git a/dist.py b/dist.py index 7ed1b5cf..89390fb2 100644 --- a/dist.py +++ b/dist.py @@ -598,13 +598,13 @@ Common commands: (see '--help-commands' for more) keywords = self.metadata.keywords if keywords is not None: - if type(keywords) is StringType: + if isinstance(keywords, basestring): keywordlist = keywords.split(',') self.metadata.keywords = [x.strip() for x in keywordlist] platforms = self.metadata.platforms if platforms is not None: - if type(platforms) is StringType: + if isinstance(platforms, basestring): platformlist = platforms.split(',') self.metadata.platforms = [x.strip() for x in platformlist] @@ -906,7 +906,7 @@ Common commands: (see '--help-commands' for more) neg_opt = {} try: - is_string = type(value) is StringType + is_string = isinstance(value, basestring) if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: diff --git a/extension.py b/extension.py index 0fcbcc14..7fe846b8 100644 --- a/extension.py +++ b/extension.py @@ -103,9 +103,9 @@ class Extension: language=None, **kw # To catch unknown keywords ): - assert type(name) is StringType, "'name' must be a string" + assert isinstance(name, basestring), "'name' must be a string" assert (type(sources) is ListType and - map(type, sources) == [StringType]*len(sources)), \ + all(isinstance(v, basestring) for v in sources)), \ "'sources' must be a list of strings" self.name = name diff --git a/fancy_getopt.py b/fancy_getopt.py index 317a3a48..82e1f4d1 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -166,13 +166,13 @@ class FancyGetopt: raise ValueError, "invalid option tuple: %r" % (option,) # Type- and value-check the option names - if type(long) is not StringType or len(long) < 2: + if not isinstance(long, basestring) or len(long) < 2: raise DistutilsGetoptError, \ ("invalid long option '%s': " "must be a string of length >= 2") % long if (not ((short is None) or - (type(short) is StringType and len(short) == 1))): + (isinstance(short, basestring) and len(short) == 1))): raise DistutilsGetoptError, \ ("invalid short option '%s': " "must a single character or None") % short diff --git a/filelist.py b/filelist.py index bc668cfa..a0523531 100644 --- a/filelist.py +++ b/filelist.py @@ -333,7 +333,7 @@ def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): or just returned as-is (assumes it's a regex object). """ if is_regex: - if type(pattern) is StringType: + if isinstance(pattern, basestring): return re.compile(pattern) else: return pattern diff --git a/unixccompiler.py b/unixccompiler.py index 0795f120..a42ab5ed 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -16,7 +16,7 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" import os, sys -from types import StringType, NoneType +from types import NoneType from copy import copy from distutils import sysconfig @@ -212,7 +212,7 @@ class UnixCCompiler(CCompiler): lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) - if type(output_dir) not in (StringType, NoneType): + if not isinstance(output_dir, (basestring, NoneType)): raise TypeError, "'output_dir' must be a string or None" if output_dir is not None: output_filename = os.path.join(output_dir, output_filename) -- cgit v1.2.1 From 74aa5c74dd3dda2d8f3fef3c83107f3e006d724c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 2 May 2007 19:09:54 +0000 Subject: Rip out all the u"..." literals and calls to unicode(). --- command/bdist_wininst.py | 4 ++-- command/build_clib.py | 2 +- command/register.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 21465a82..5074a1a6 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -247,11 +247,11 @@ class bdist_wininst (Command): # Convert cfgdata from unicode to ascii, mbcs encoded try: - unicode + str except NameError: pass else: - if isinstance(cfgdata, unicode): + if isinstance(cfgdata, str): cfgdata = cfgdata.encode("mbcs") # Append the pre-install script diff --git a/command/build_clib.py b/command/build_clib.py index 5434e86c..bdf98bf3 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -147,7 +147,7 @@ class build_clib (Command): raise DistutilsSetupError, \ "each element of 'libraries' must a 2-tuple" - if isinstance(lib[0], basestring) StringType: + if isinstance(lib[0], basestring): raise DistutilsSetupError, \ "first element of each tuple in 'libraries' " + \ "must be a string (the library name)" diff --git a/command/register.py b/command/register.py index 33567981..2ddabadf 100644 --- a/command/register.py +++ b/command/register.py @@ -259,7 +259,7 @@ Your selection [default 1]: ''', end=' ') if type(value) not in (type([]), type( () )): value = [value] for value in value: - value = unicode(value).encode("utf-8") + value = str(value).encode("utf-8") body.write(sep_boundary) body.write('\nContent-Disposition: form-data; name="%s"'%key) body.write("\n\n") -- cgit v1.2.1 From b6944798d4c6fe91c594c4e64ef13820f0c1b266 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 7 May 2007 22:24:25 +0000 Subject: Merged revisions 55007-55179 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/p3yk ........ r55077 | guido.van.rossum | 2007-05-02 11:54:37 -0700 (Wed, 02 May 2007) | 2 lines Use the new print syntax, at least. ........ r55142 | fred.drake | 2007-05-04 21:27:30 -0700 (Fri, 04 May 2007) | 1 line remove old cruftiness ........ r55143 | fred.drake | 2007-05-04 21:52:16 -0700 (Fri, 04 May 2007) | 1 line make this work with the new Python ........ r55162 | neal.norwitz | 2007-05-06 22:29:18 -0700 (Sun, 06 May 2007) | 1 line Get asdl code gen working with Python 2.3. Should continue to work with 3.0 ........ r55164 | neal.norwitz | 2007-05-07 00:00:38 -0700 (Mon, 07 May 2007) | 1 line Verify checkins to p3yk (sic) branch go to 3000 list. ........ r55166 | neal.norwitz | 2007-05-07 00:12:35 -0700 (Mon, 07 May 2007) | 1 line Fix this test so it runs again by importing warnings_test properly. ........ r55167 | neal.norwitz | 2007-05-07 01:03:22 -0700 (Mon, 07 May 2007) | 8 lines So long xrange. range() now supports values that are outside -sys.maxint to sys.maxint. floats raise a TypeError. This has been sitting for a long time. It probably has some problems and needs cleanup. Objects/rangeobject.c now uses 4-space indents since it is almost completely new. ........ r55171 | guido.van.rossum | 2007-05-07 10:21:26 -0700 (Mon, 07 May 2007) | 4 lines Fix two tests that were previously depending on significant spaces at the end of a line (and before that on Python 2.x print behavior that has no exact equivalent in 3.0). ........ --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index cc92e963..a6543ba6 100644 --- a/command/install.py +++ b/command/install.py @@ -517,7 +517,7 @@ class install (Command): outputs = self.get_outputs() if self.root: # strip any package prefix root_len = len(self.root) - for counter in xrange(len(outputs)): + for counter in range(len(outputs)): outputs[counter] = outputs[counter][root_len:] self.execute(write_file, (self.record, outputs), -- cgit v1.2.1 From 0a68cc8e653759fd3913f2bcbc85eeffbb02f194 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 22 May 2007 18:11:13 +0000 Subject: Merged revisions 55407-55513 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/p3yk ................ r55413 | fred.drake | 2007-05-17 12:30:10 -0700 (Thu, 17 May 2007) | 1 line fix argument name in documentation; match the implementation ................ r55430 | jack.diederich | 2007-05-18 06:39:59 -0700 (Fri, 18 May 2007) | 1 line Implements class decorators, PEP 3129. ................ r55432 | guido.van.rossum | 2007-05-18 08:09:41 -0700 (Fri, 18 May 2007) | 2 lines obsubmit. ................ r55434 | guido.van.rossum | 2007-05-18 09:39:10 -0700 (Fri, 18 May 2007) | 3 lines Fix bug in test_inspect. (I presume this is how it should be fixed; Jack Diedrich, please verify.) ................ r55460 | brett.cannon | 2007-05-20 00:31:57 -0700 (Sun, 20 May 2007) | 4 lines Remove the imageop module. With imgfile already removed in Python 3.0 and rgbimg gone in Python 2.6 the unit tests themselves were made worthless. Plus third-party libraries perform the same function much better. ................ r55469 | neal.norwitz | 2007-05-20 11:28:20 -0700 (Sun, 20 May 2007) | 118 lines Merged revisions 55324-55467 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r55348 | georg.brandl | 2007-05-15 13:19:34 -0700 (Tue, 15 May 2007) | 4 lines HTML-escape the plain traceback in cgitb's HTML output, to prevent the traceback inadvertently or maliciously closing the comment and injecting HTML into the error page. ........ r55372 | neal.norwitz | 2007-05-15 21:33:50 -0700 (Tue, 15 May 2007) | 6 lines Port rev 55353 from Guido: Add what looks like a necessary call to PyErr_NoMemory() when PyMem_MALLOC() fails. Will backport. ........ r55377 | neal.norwitz | 2007-05-15 22:06:33 -0700 (Tue, 15 May 2007) | 1 line Mention removal of some directories for obsolete platforms ........ r55380 | brett.cannon | 2007-05-15 22:50:03 -0700 (Tue, 15 May 2007) | 2 lines Change the maintainer of the BeOS port. ........ r55383 | georg.brandl | 2007-05-16 06:44:18 -0700 (Wed, 16 May 2007) | 2 lines Bug #1719995: don't use deprecated method in sets example. ........ r55386 | neal.norwitz | 2007-05-16 13:05:11 -0700 (Wed, 16 May 2007) | 5 lines Fix bug in marshal where bad data would cause a segfault due to lack of an infinite recursion check. Contributed by Damien Miller at Google. ........ r55389 | brett.cannon | 2007-05-16 15:42:29 -0700 (Wed, 16 May 2007) | 6 lines Remove the gopherlib module. It has been raising a DeprecationWarning since Python 2.5. Also remove gopher support from urllib/urllib2. As both imported gopherlib the usage of the support would have raised a DeprecationWarning. ........ r55394 | raymond.hettinger | 2007-05-16 18:08:04 -0700 (Wed, 16 May 2007) | 1 line calendar.py gets no benefit from xrange() instead of range() ........ r55395 | brett.cannon | 2007-05-16 19:02:56 -0700 (Wed, 16 May 2007) | 3 lines Complete deprecation of BaseException.message. Some subclasses were directly accessing the message attribute instead of using the descriptor. ........ r55396 | neal.norwitz | 2007-05-16 23:11:36 -0700 (Wed, 16 May 2007) | 4 lines Reduce the max stack depth to see if this fixes the segfaults on Windows and some other boxes. If this is successful, this rev should be backported. I'm not sure how close to the limit we should push this. ........ r55397 | neal.norwitz | 2007-05-16 23:23:50 -0700 (Wed, 16 May 2007) | 4 lines Set the depth to something very small to try to determine if the crashes on Windows are really due to the stack size or possibly some other problem. ........ r55398 | neal.norwitz | 2007-05-17 00:04:46 -0700 (Thu, 17 May 2007) | 4 lines Last try for tweaking the max stack depth. 5000 was the original value, 4000 didn't work either. 1000 does work on Windows. If 2000 works, that will hopefully be a reasonable balance. ........ r55412 | fred.drake | 2007-05-17 12:29:58 -0700 (Thu, 17 May 2007) | 1 line fix argument name in documentation; match the implementation ........ r55427 | neal.norwitz | 2007-05-17 22:47:16 -0700 (Thu, 17 May 2007) | 1 line Verify neither dumps or loads overflow the stack and segfault. ........ r55446 | collin.winter | 2007-05-18 16:11:24 -0700 (Fri, 18 May 2007) | 1 line Backport PEP 3110's new 'except' syntax to 2.6. ........ r55448 | raymond.hettinger | 2007-05-18 18:11:16 -0700 (Fri, 18 May 2007) | 1 line Improvements to NamedTuple's implementation, tests, and documentation ........ r55449 | raymond.hettinger | 2007-05-18 18:50:11 -0700 (Fri, 18 May 2007) | 1 line Fix beginner mistake -- don't mix spaces and tabs. ........ r55450 | neal.norwitz | 2007-05-18 20:48:47 -0700 (Fri, 18 May 2007) | 1 line Clear data so random memory does not get freed. Will backport. ........ r55452 | neal.norwitz | 2007-05-18 21:34:55 -0700 (Fri, 18 May 2007) | 3 lines Whoops, need to pay attention to those test failures. Move the clear to *before* the first use, not after. ........ r55453 | neal.norwitz | 2007-05-18 21:35:52 -0700 (Fri, 18 May 2007) | 1 line Give some clue as to what happened if the test fails. ........ r55455 | georg.brandl | 2007-05-19 11:09:26 -0700 (Sat, 19 May 2007) | 2 lines Fix docstring for add_package in site.py. ........ r55458 | brett.cannon | 2007-05-20 00:09:50 -0700 (Sun, 20 May 2007) | 2 lines Remove the rgbimg module. It has been deprecated since Python 2.5. ........ r55465 | nick.coghlan | 2007-05-20 04:12:49 -0700 (Sun, 20 May 2007) | 1 line Fix typo in example (should be backported, but my maintenance branch is woefully out of date) ........ ................ r55472 | brett.cannon | 2007-05-20 12:06:18 -0700 (Sun, 20 May 2007) | 2 lines Remove imageop from the Windows build process. ................ r55486 | neal.norwitz | 2007-05-20 23:59:52 -0700 (Sun, 20 May 2007) | 1 line Remove callable() builtin ................ r55506 | neal.norwitz | 2007-05-22 00:43:29 -0700 (Tue, 22 May 2007) | 78 lines Merged revisions 55468-55505 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r55468 | neal.norwitz | 2007-05-20 11:06:27 -0700 (Sun, 20 May 2007) | 1 line rotor is long gone. ........ r55470 | neal.norwitz | 2007-05-20 11:43:00 -0700 (Sun, 20 May 2007) | 1 line Update directories/files at the top-level. ........ r55471 | brett.cannon | 2007-05-20 12:05:06 -0700 (Sun, 20 May 2007) | 2 lines Try to remove rgbimg from Windows builds. ........ r55474 | brett.cannon | 2007-05-20 16:17:38 -0700 (Sun, 20 May 2007) | 4 lines Remove the macfs module. This led to the deprecation of macostools.touched(); it completely relied on macfs and is a no-op on OS X according to code comments. ........ r55476 | brett.cannon | 2007-05-20 16:56:18 -0700 (Sun, 20 May 2007) | 3 lines Move imgfile import to the global namespace to trigger an import error ASAP to prevent creation of a test file. ........ r55477 | brett.cannon | 2007-05-20 16:57:38 -0700 (Sun, 20 May 2007) | 3 lines Cause posixfile to raise a DeprecationWarning. Documented as deprecated since Ptyhon 1.5. ........ r55479 | andrew.kuchling | 2007-05-20 17:03:15 -0700 (Sun, 20 May 2007) | 1 line Note removed modules ........ r55481 | martin.v.loewis | 2007-05-20 21:35:47 -0700 (Sun, 20 May 2007) | 2 lines Add Alexandre Vassalotti. ........ r55482 | george.yoshida | 2007-05-20 21:41:21 -0700 (Sun, 20 May 2007) | 4 lines fix against r55474 [Remove the macfs module] Remove "libmacfs.tex" from Makefile.deps and mac/mac.tex. ........ r55487 | raymond.hettinger | 2007-05-21 01:13:35 -0700 (Mon, 21 May 2007) | 1 line Replace assertion with straight error-checking. ........ r55489 | raymond.hettinger | 2007-05-21 09:40:10 -0700 (Mon, 21 May 2007) | 1 line Allow all alphanumeric and underscores in type and field names. ........ r55490 | facundo.batista | 2007-05-21 10:32:32 -0700 (Mon, 21 May 2007) | 5 lines Added timeout support to HTTPSConnection, through the socket.create_connection function. Also added a small test for this, and updated NEWS file. ........ r55495 | georg.brandl | 2007-05-21 13:34:16 -0700 (Mon, 21 May 2007) | 2 lines Patch #1686487: you can now pass any mapping after '**' in function calls. ........ r55502 | neal.norwitz | 2007-05-21 23:03:36 -0700 (Mon, 21 May 2007) | 1 line Document new params to HTTPSConnection ........ r55504 | neal.norwitz | 2007-05-22 00:16:10 -0700 (Tue, 22 May 2007) | 1 line Stop using METH_OLDARGS ........ r55505 | neal.norwitz | 2007-05-22 00:16:44 -0700 (Tue, 22 May 2007) | 1 line Stop using METH_OLDARGS implicitly ........ ................ --- dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 89390fb2..e89d942f 100644 --- a/dist.py +++ b/dist.py @@ -569,7 +569,7 @@ Common commands: (see '--help-commands' for more) #print "showing help for option %s of command %s" % \ # (help_option[0],cmd_class) - if callable(func): + if hasattr(func, '__call__'): func() else: raise DistutilsClassError( -- cgit v1.2.1 From 846a6d17bc1356201efb984e23caafff578982f4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 25 May 2007 18:39:29 +0000 Subject: Minimal fixes to save the bootstrap on OSX. --- sysconfig.py | 3 ++- text_file.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index ea2e0597..51f23a24 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -11,6 +11,7 @@ Email: __revision__ = "$Id$" +import io import os import re import sys @@ -353,7 +354,7 @@ def _init_posix(): # load the installed pyconfig.h: try: filename = get_config_h_filename() - parse_config_h(open(filename), g) + parse_config_h(io.open(filename), g) except IOError as msg: my_msg = "invalid Python installation: unable to open %s" % filename if hasattr(msg, "strerror"): diff --git a/text_file.py b/text_file.py index c23a31dd..9d4d42b4 100644 --- a/text_file.py +++ b/text_file.py @@ -7,7 +7,7 @@ lines, and joining lines with backslashes.""" __revision__ = "$Id$" from types import * -import sys, os +import sys, os, io class TextFile: @@ -34,7 +34,7 @@ class TextFile: something that provides 'readline()' and 'close()' methods). It is recommended that you supply at least 'filename', so that TextFile can include it in warning messages. If 'file' is not supplied, - TextFile creates its own using the 'open()' builtin. + TextFile creates its own using 'io.open()'. The options are all boolean, and affect the value returned by 'readline()': @@ -118,7 +118,7 @@ class TextFile: 'filename' and 'file' arguments to the constructor.""" self.filename = filename - self.file = open (self.filename, 'r') + self.file = io.open (self.filename, 'r') self.current_line = 0 -- cgit v1.2.1 From 6d3419303b176f1986ec06a1421452f227a839c1 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 1 Jun 2007 07:29:12 +0000 Subject: SF 1668596/1720897: distutils now copies data files even if package_dir is empty. This needs to be backported. I'm too tired tonight. It would be great if someone backports this if the buildbots are ok with it. Otherwise, I will try to get to it tomorrow. --- command/build_py.py | 4 +++- tests/test_build_py.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/command/build_py.py b/command/build_py.py index 621bcb4a..b9f39bae 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -114,7 +114,9 @@ class build_py (Command): build_dir = os.path.join(*([self.build_lib] + package.split('.'))) # Length of path to strip from found files - plen = len(src_dir)+1 + plen = 0 + if src_dir: + plen = len(src_dir)+1 # Strip directory from globbed filenames filenames = [ diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 78e4c55e..54a4ed80 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -1,10 +1,13 @@ """Tests for distutils.command.build_py.""" import os +import sys +import StringIO import unittest from distutils.command.build_py import build_py from distutils.core import Distribution +from distutils.errors import DistutilsFileError from distutils.tests import support @@ -53,6 +56,38 @@ class BuildPyTestCase(support.TempdirManager, self.assert_("__init__.pyc" in files) self.assert_("README.txt" in files) + def test_empty_package_dir (self): + # See SF 1668596/1720897. + cwd = os.getcwd() + + # create the distribution files. + sources = self.mkdtemp() + open(os.path.join(sources, "__init__.py"), "w").close() + + testdir = os.path.join(sources, "doc") + os.mkdir(testdir) + open(os.path.join(testdir, "testfile"), "w").close() + + os.chdir(sources) + sys.stdout = StringIO.StringIO() + + try: + dist = Distribution({"packages": ["pkg"], + "package_dir": {"pkg": ""}, + "package_data": {"pkg": ["doc/*"]}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.script_args = ["build"] + dist.parse_command_line() + + try: + dist.run_commands() + except DistutilsFileError: + self.fail("failed package_data test when package_dir is ''") + finally: + # Restore state. + os.chdir(cwd) + sys.stdout = sys.__stdout__ def test_suite(): return unittest.makeSuite(BuildPyTestCase) -- cgit v1.2.1 From 4951f23221c37e1b9a5b595397529ec8c301f705 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sat, 2 Jun 2007 18:53:07 +0000 Subject: Backport 55731: SF 1668596/1720897: distutils now copies data files even if package_dir is empty. --- command/build_py.py | 4 +++- tests/test_build_py.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/command/build_py.py b/command/build_py.py index 621bcb4a..b9f39bae 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -114,7 +114,9 @@ class build_py (Command): build_dir = os.path.join(*([self.build_lib] + package.split('.'))) # Length of path to strip from found files - plen = len(src_dir)+1 + plen = 0 + if src_dir: + plen = len(src_dir)+1 # Strip directory from globbed filenames filenames = [ diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 78e4c55e..54a4ed80 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -1,10 +1,13 @@ """Tests for distutils.command.build_py.""" import os +import sys +import StringIO import unittest from distutils.command.build_py import build_py from distutils.core import Distribution +from distutils.errors import DistutilsFileError from distutils.tests import support @@ -53,6 +56,38 @@ class BuildPyTestCase(support.TempdirManager, self.assert_("__init__.pyc" in files) self.assert_("README.txt" in files) + def test_empty_package_dir (self): + # See SF 1668596/1720897. + cwd = os.getcwd() + + # create the distribution files. + sources = self.mkdtemp() + open(os.path.join(sources, "__init__.py"), "w").close() + + testdir = os.path.join(sources, "doc") + os.mkdir(testdir) + open(os.path.join(testdir, "testfile"), "w").close() + + os.chdir(sources) + sys.stdout = StringIO.StringIO() + + try: + dist = Distribution({"packages": ["pkg"], + "package_dir": {"pkg": ""}, + "package_data": {"pkg": ["doc/*"]}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.script_args = ["build"] + dist.parse_command_line() + + try: + dist.run_commands() + except DistutilsFileError: + self.fail("failed package_data test when package_dir is ''") + finally: + # Restore state. + os.chdir(cwd) + sys.stdout = sys.__stdout__ def test_suite(): return unittest.makeSuite(BuildPyTestCase) -- cgit v1.2.1 From b56a8f9ed0bfc43878362c0d08e7292b146f9b81 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 7 Jun 2007 23:15:56 +0000 Subject: Merged revisions 55795-55816 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/p3yk ........ r55797 | neal.norwitz | 2007-06-07 00:00:57 -0700 (Thu, 07 Jun 2007) | 3 lines Get rid of some remnants of classic classes. types.ClassType == type. Also get rid of almost all uses of the types module and use the builtin name. ........ r55798 | neal.norwitz | 2007-06-07 00:12:36 -0700 (Thu, 07 Jun 2007) | 1 line Remove a use of types, verify commit hook works ........ r55809 | guido.van.rossum | 2007-06-07 11:11:29 -0700 (Thu, 07 Jun 2007) | 2 lines Fix syntax error introduced by Neal in last checkin. ........ --- cmd.py | 5 ++--- dist.py | 11 +++++------ extension.py | 3 +-- text_file.py | 7 +++---- unixccompiler.py | 3 +-- 5 files changed, 12 insertions(+), 17 deletions(-) diff --git a/cmd.py b/cmd.py index ea3799af..8d77e7fb 100644 --- a/cmd.py +++ b/cmd.py @@ -9,7 +9,6 @@ in the distutils.command package. __revision__ = "$Id$" import sys, os, re -from types import * from distutils.errors import * from distutils import util, dir_util, file_util, archive_util, dep_util from distutils import log @@ -245,7 +244,7 @@ class Command: elif isinstance(val, basestring): setattr(self, option, re.split(r',\s*|\s+', val)) else: - if type(val) is ListType: + if isinstance(val, list): ok = all(isinstance(v, basestring) for v in val) else: ok = 0 @@ -422,7 +421,7 @@ class Command: # Allow 'infiles' to be a single string if isinstance(infiles, basestring): infiles = (infiles,) - elif type(infiles) not in (ListType, TupleType): + elif not isinstance(infiles, (list, tuple)): raise TypeError, \ "'infiles' must be a string, or a list or tuple of strings" diff --git a/dist.py b/dist.py index e89d942f..c01724d8 100644 --- a/dist.py +++ b/dist.py @@ -9,7 +9,6 @@ being built/installed/distributed. __revision__ = "$Id$" import sys, os, re -from types import * from copy import copy try: @@ -527,7 +526,7 @@ Common commands: (see '--help-commands' for more) # Also make sure that the command object provides a list of its # known options. if not (hasattr(cmd_class, 'user_options') and - type(cmd_class.user_options) is ListType): + isinstance(cmd_class.user_options, list)): raise DistutilsClassError, \ ("command class %s must provide " + "'user_options' attribute (a list of tuples)") % \ @@ -543,7 +542,7 @@ Common commands: (see '--help-commands' for more) # Check for help_options in command class. They have a different # format (tuple of four) so we need to preprocess them here. if (hasattr(cmd_class, 'help_options') and - type(cmd_class.help_options) is ListType): + isinstance(cmd_class.help_options, list)): help_options = fix_help_options(cmd_class.help_options) else: help_options = [] @@ -561,7 +560,7 @@ Common commands: (see '--help-commands' for more) return if (hasattr(cmd_class, 'help_options') and - type(cmd_class.help_options) is ListType): + isinstance(cmd_class.help_options, list)): help_option_found=0 for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): @@ -646,12 +645,12 @@ Common commands: (see '--help-commands' for more) print() for command in self.commands: - if type(command) is ClassType and issubclass(command, Command): + if isinstance(command, type) and issubclass(command, Command): klass = command else: klass = self.get_command_class(command) if (hasattr(klass, 'help_options') and - type(klass.help_options) is ListType): + isinstance(klass.help_options, list)): parser.set_option_table(klass.user_options + fix_help_options(klass.help_options)) else: diff --git a/extension.py b/extension.py index 7fe846b8..43b0d3fd 100644 --- a/extension.py +++ b/extension.py @@ -6,7 +6,6 @@ modules in setup scripts.""" __revision__ = "$Id$" import os, sys -from types import * try: import warnings @@ -104,7 +103,7 @@ class Extension: **kw # To catch unknown keywords ): assert isinstance(name, basestring), "'name' must be a string" - assert (type(sources) is ListType and + assert (isinstance(sources, list) and all(isinstance(v, basestring) for v in sources)), \ "'sources' must be a list of strings" diff --git a/text_file.py b/text_file.py index 9d4d42b4..3f6a220d 100644 --- a/text_file.py +++ b/text_file.py @@ -6,7 +6,6 @@ lines, and joining lines with backslashes.""" __revision__ = "$Id$" -from types import * import sys, os, io @@ -137,7 +136,7 @@ class TextFile: if line is None: line = self.current_line outmsg.append(self.filename + ", ") - if type (line) in (ListType, TupleType): + if isinstance (line, (list, tuple)): outmsg.append("lines %d-%d: " % tuple (line)) else: outmsg.append("line %d: " % line) @@ -239,7 +238,7 @@ class TextFile: line = buildup_line + line # careful: pay attention to line number when incrementing it - if type (self.current_line) is ListType: + if isinstance (self.current_line, list): self.current_line[1] = self.current_line[1] + 1 else: self.current_line = [self.current_line, @@ -250,7 +249,7 @@ class TextFile: return None # still have to be careful about incrementing the line number! - if type (self.current_line) is ListType: + if isinstance (self.current_line, list): self.current_line = self.current_line[1] + 1 else: self.current_line = self.current_line + 1 diff --git a/unixccompiler.py b/unixccompiler.py index a42ab5ed..d07ae1e3 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -16,7 +16,6 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" import os, sys -from types import NoneType from copy import copy from distutils import sysconfig @@ -212,7 +211,7 @@ class UnixCCompiler(CCompiler): lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) - if not isinstance(output_dir, (basestring, NoneType)): + if not isinstance(output_dir, (basestring, type(None))): raise TypeError, "'output_dir' must be a string or None" if output_dir is not None: output_filename = os.path.join(output_dir, output_filename) -- cgit v1.2.1 From d9a8d08e4670a883b259554331b4372e8ae18f12 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Mon, 11 Jun 2007 05:28:45 +0000 Subject: Add all of the distuils modules that don't seem to have explicit tests. :-( Move an import in mworkscompiler so that this module can be imported on any platform. Hopefully this works on all platforms. --- mwerkscompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 0de123d9..343c6cec 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -18,7 +18,6 @@ from distutils.ccompiler import \ import distutils.util import distutils.dir_util from distutils import log -import mkcwproject class MWerksCompiler (CCompiler) : """Concrete class that implements an interface to MetroWerks CodeWarrior, @@ -188,6 +187,7 @@ class MWerksCompiler (CCompiler) : # doesn't have a clue about our working directory. xmlfilename = os.path.join(os.getcwd(), os.path.join(build_temp, xmlname)) log.debug("\tCreate XML file %s", xmlfilename) + import mkcwproject xmlbuilder = mkcwproject.cwxmlgen.ProjectBuilder(settings) xmlbuilder.generate() xmldata = settings['tmp_projectxmldata'] -- cgit v1.2.1 From 8d56e63638d9152547d8b6f8a4cbdba90cf91a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Walter=20D=C3=B6rwald?= Date: Mon, 11 Jun 2007 21:38:39 +0000 Subject: Simplify various spots where: str() is called on something that already is a string or the existence of the str class is checked or a check is done for str twice. These all stem from the initial unicode->str replacement. --- command/bdist_wininst.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 5074a1a6..55d5d7e1 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -246,13 +246,8 @@ class bdist_wininst (Command): file.write(bitmapdata) # Convert cfgdata from unicode to ascii, mbcs encoded - try: - str - except NameError: - pass - else: - if isinstance(cfgdata, str): - cfgdata = cfgdata.encode("mbcs") + if isinstance(cfgdata, str): + cfgdata = cfgdata.encode("mbcs") # Append the pre-install script cfgdata = cfgdata + "\0" -- cgit v1.2.1 From 4beaf762f12da3cb21e32d646c5f01c8bdb7e9ca Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 13 Jun 2007 18:07:49 +0000 Subject: Merged revisions 55817-55961 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/p3yk ................ r55837 | guido.van.rossum | 2007-06-08 16:04:42 -0700 (Fri, 08 Jun 2007) | 2 lines PEP 3119 -- the abc module. ................ r55838 | guido.van.rossum | 2007-06-08 17:38:55 -0700 (Fri, 08 Jun 2007) | 2 lines Implement part of PEP 3119 -- One Trick Ponies. ................ r55847 | guido.van.rossum | 2007-06-09 08:28:06 -0700 (Sat, 09 Jun 2007) | 2 lines Different way to do one trick ponies, allowing registration (per PEP strawman). ................ r55849 | guido.van.rossum | 2007-06-09 18:06:38 -0700 (Sat, 09 Jun 2007) | 3 lines Make sure that the magic looking for __hash__ (etc.) doesn't apply to real subclasses of Hashable. ................ r55852 | guido.van.rossum | 2007-06-10 08:29:51 -0700 (Sun, 10 Jun 2007) | 2 lines Add some more examples, e.g. generators and dict views. ................ r55853 | guido.van.rossum | 2007-06-10 08:31:59 -0700 (Sun, 10 Jun 2007) | 2 lines keys() and items() *are* containers -- just values() isn't. ................ r55864 | georg.brandl | 2007-06-10 15:29:40 -0700 (Sun, 10 Jun 2007) | 2 lines PEP 3127: new octal literals, binary literals. ................ r55865 | georg.brandl | 2007-06-10 15:31:37 -0700 (Sun, 10 Jun 2007) | 2 lines Some octal literal fixes in Tools. ................ r55866 | georg.brandl | 2007-06-10 15:37:43 -0700 (Sun, 10 Jun 2007) | 2 lines Tokenizer changes for PEP 3127. ................ r55867 | georg.brandl | 2007-06-10 15:37:55 -0700 (Sun, 10 Jun 2007) | 2 lines Some docs for PEP 3127. ................ r55868 | georg.brandl | 2007-06-10 15:44:39 -0700 (Sun, 10 Jun 2007) | 2 lines Missed a place in intobject.c. Is that used anymore anyway? ................ r55871 | neal.norwitz | 2007-06-10 18:31:49 -0700 (Sun, 10 Jun 2007) | 182 lines Merged revisions 55729-55868 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r55731 | neal.norwitz | 2007-06-01 00:29:12 -0700 (Fri, 01 Jun 2007) | 7 lines SF 1668596/1720897: distutils now copies data files even if package_dir is empty. This needs to be backported. I'm too tired tonight. It would be great if someone backports this if the buildbots are ok with it. Otherwise, I will try to get to it tomorrow. ........ r55732 | georg.brandl | 2007-06-01 04:33:33 -0700 (Fri, 01 Jun 2007) | 2 lines Bug #1722484: remove docstrings again when running with -OO. ........ r55735 | georg.brandl | 2007-06-01 12:20:27 -0700 (Fri, 01 Jun 2007) | 2 lines Fix wrong issue number. ........ r55739 | brett.cannon | 2007-06-01 20:02:29 -0700 (Fri, 01 Jun 2007) | 3 lines Have configure raise an error when building on AtheOS. Code specific to AtheOS will be removed in Python 2.7. ........ r55746 | neal.norwitz | 2007-06-02 11:33:53 -0700 (Sat, 02 Jun 2007) | 1 line Update expected birthday of 2.6 ........ r55751 | neal.norwitz | 2007-06-03 13:32:50 -0700 (Sun, 03 Jun 2007) | 10 lines Backout the original 'fix' to 1721309 which had no effect. Different versions of Berkeley DB handle this differently. The comments and bug report should have the details. Memory is allocated in 4.4 (and presumably earlier), but not in 4.5. Thus 4.5 has the free error, but not earlier versions. Mostly update comments, plus make the free conditional. This fix was already applied to the 2.5 branch. ........ r55752 | brett.cannon | 2007-06-03 16:13:41 -0700 (Sun, 03 Jun 2007) | 6 lines Make _strptime.TimeRE().pattern() use ``\s+`` for matching whitespace instead of ``\s*``. This prevents patterns from "stealing" bits from other patterns in order to make a match work. Closes bug #1730389. Will be backported. ........ r55766 | hyeshik.chang | 2007-06-05 11:16:52 -0700 (Tue, 05 Jun 2007) | 4 lines Fix build on FreeBSD. Bluetooth HCI API in FreeBSD is quite different from Linux's. Just fix the build for now but the code doesn't support the complete capability of HCI on FreeBSD yet. ........ r55770 | hyeshik.chang | 2007-06-05 11:58:51 -0700 (Tue, 05 Jun 2007) | 4 lines Bug #1728403: Fix a bug that CJKCodecs StreamReader hangs when it reads a file that ends with incomplete sequence and sizehint argument for .read() is specified. ........ r55775 | hyeshik.chang | 2007-06-05 12:28:15 -0700 (Tue, 05 Jun 2007) | 2 lines Fix for Windows: close a temporary file before trying to delete it. ........ r55783 | guido.van.rossum | 2007-06-05 14:24:47 -0700 (Tue, 05 Jun 2007) | 2 lines Patch by Tim Delany (missing DECREF). SF #1731330. ........ r55785 | collin.winter | 2007-06-05 17:17:35 -0700 (Tue, 05 Jun 2007) | 3 lines Patch #1731049: make threading.py use a proper "raise" when checking internal state, rather than assert statements (which get stripped out by -O). ........ r55786 | facundo.batista | 2007-06-06 08:13:37 -0700 (Wed, 06 Jun 2007) | 4 lines FTP.ntransfercmd method now uses create_connection when passive, using the timeout received in connection time. ........ r55792 | facundo.batista | 2007-06-06 10:15:23 -0700 (Wed, 06 Jun 2007) | 7 lines Added an optional timeout parameter to function urllib2.urlopen, with tests in test_urllib2net.py (must have network resource enabled to execute them). Also modified test_urllib2.py because testing mock classes must take it into acount. Docs are also updated. ........ r55793 | thomas.heller | 2007-06-06 13:19:19 -0700 (Wed, 06 Jun 2007) | 1 line Build _ctypes and _ctypes_test in the ReleaseAMD64 configuration. ........ r55802 | georg.brandl | 2007-06-07 06:23:24 -0700 (Thu, 07 Jun 2007) | 3 lines Disallow function calls like foo(None=1). Backport from py3k rev. 55708 by Guido. ........ r55804 | georg.brandl | 2007-06-07 06:30:24 -0700 (Thu, 07 Jun 2007) | 2 lines Make reindent.py executable. ........ r55805 | georg.brandl | 2007-06-07 06:34:10 -0700 (Thu, 07 Jun 2007) | 2 lines Patch #1667860: Fix UnboundLocalError in urllib2. ........ r55821 | kristjan.jonsson | 2007-06-07 16:53:49 -0700 (Thu, 07 Jun 2007) | 1 line Fixing changes to getbuildinfo.c that broke linux builds ........ r55828 | thomas.heller | 2007-06-08 09:10:27 -0700 (Fri, 08 Jun 2007) | 1 line Make this test work with older Python releases where struct has no 't' format character. ........ r55829 | martin.v.loewis | 2007-06-08 10:29:20 -0700 (Fri, 08 Jun 2007) | 3 lines Bug #1733488: Fix compilation of bufferobject.c on AIX. Will backport to 2.5. ........ r55831 | thomas.heller | 2007-06-08 11:20:09 -0700 (Fri, 08 Jun 2007) | 2 lines [ 1715718 ] x64 clean compile patch for _ctypes, by Kristj?n Valur with small modifications. ........ r55832 | thomas.heller | 2007-06-08 12:01:06 -0700 (Fri, 08 Jun 2007) | 1 line Fix gcc warnings intruduced by passing Py_ssize_t to PyErr_Format calls. ........ r55833 | thomas.heller | 2007-06-08 12:08:31 -0700 (Fri, 08 Jun 2007) | 2 lines Fix wrong documentation, and correct the punktuation. Closes [1700455]. ........ r55834 | thomas.heller | 2007-06-08 12:14:23 -0700 (Fri, 08 Jun 2007) | 1 line Fix warnings by using proper function prototype. ........ r55839 | neal.norwitz | 2007-06-08 20:36:34 -0700 (Fri, 08 Jun 2007) | 7 lines Prevent expandtabs() on string and unicode objects from causing a segfault when a large width is passed on 32-bit platforms. Found by Google. It would be good for people to review this especially carefully and verify I don't have an off by one error and there is no other way to cause overflow. ........ r55841 | neal.norwitz | 2007-06-08 21:48:22 -0700 (Fri, 08 Jun 2007) | 1 line Use macro version of GET_SIZE to avoid Coverity warning (#150) about a possible error. ........ r55842 | martin.v.loewis | 2007-06-09 00:42:52 -0700 (Sat, 09 Jun 2007) | 3 lines Patch #1733960: Allow T_LONGLONG to accept ints. Will backport to 2.5. ........ r55843 | martin.v.loewis | 2007-06-09 00:58:05 -0700 (Sat, 09 Jun 2007) | 2 lines Fix Windows build. ........ r55845 | martin.v.loewis | 2007-06-09 03:10:26 -0700 (Sat, 09 Jun 2007) | 2 lines Provide LLONG_MAX for S390. ........ r55854 | thomas.heller | 2007-06-10 08:59:17 -0700 (Sun, 10 Jun 2007) | 4 lines First version of build scripts for Windows/AMD64 (no external components are built yet, and 'kill_python' is disabled). ........ r55855 | thomas.heller | 2007-06-10 10:55:51 -0700 (Sun, 10 Jun 2007) | 3 lines For now, disable the _bsddb, _sqlite3, _ssl, _testcapi, _tkinter modules in the ReleaseAMD64 configuration because they do not compile. ........ r55856 | thomas.heller | 2007-06-10 11:27:54 -0700 (Sun, 10 Jun 2007) | 1 line Need to set the environment variables, otherwise devenv.com is not found. ........ r55860 | thomas.heller | 2007-06-10 14:01:17 -0700 (Sun, 10 Jun 2007) | 1 line Revert commit 55855. ........ ................ r55880 | neal.norwitz | 2007-06-10 22:07:36 -0700 (Sun, 10 Jun 2007) | 5 lines Fix the refleak counter on test_collections. The ABC metaclass creates a registry which must be cleared on each run. Otherwise, there *seem* to be refleaks when there really aren't any. (The class is held within the registry even though it's no longer needed.) ................ r55884 | neal.norwitz | 2007-06-10 22:46:33 -0700 (Sun, 10 Jun 2007) | 1 line These tests have been removed, so they are no longer needed here ................ r55886 | georg.brandl | 2007-06-11 00:26:37 -0700 (Mon, 11 Jun 2007) | 3 lines Optimize access to True and False in the compiler (if True) and the peepholer (LOAD_NAME True). ................ r55905 | georg.brandl | 2007-06-11 10:02:26 -0700 (Mon, 11 Jun 2007) | 5 lines Remove __oct__ and __hex__ and use __index__ for converting non-ints before formatting in a base. Add a bin() builtin. ................ r55906 | georg.brandl | 2007-06-11 10:04:44 -0700 (Mon, 11 Jun 2007) | 2 lines int(x, 0) does not "guess". ................ r55907 | georg.brandl | 2007-06-11 10:05:47 -0700 (Mon, 11 Jun 2007) | 2 lines Add a comment to explain that nb_oct and nb_hex are nonfunctional. ................ r55908 | guido.van.rossum | 2007-06-11 10:49:18 -0700 (Mon, 11 Jun 2007) | 2 lines Get rid of unused imports and comment. ................ r55910 | guido.van.rossum | 2007-06-11 13:05:17 -0700 (Mon, 11 Jun 2007) | 2 lines _Abstract.__new__ now requires either no arguments or __init__ overridden. ................ r55911 | guido.van.rossum | 2007-06-11 13:07:49 -0700 (Mon, 11 Jun 2007) | 7 lines Move the collections ABCs to a separate file, _abcoll.py, in order to avoid needing to import _collections.so during the bootstrap (this will become apparent in the next submit of os.py). Add (plain and mutable) ABCs for Set, Mapping, Sequence. ................ r55912 | guido.van.rossum | 2007-06-11 13:09:31 -0700 (Mon, 11 Jun 2007) | 2 lines Rewrite the _Environ class to use the new collections ABCs. ................ r55913 | guido.van.rossum | 2007-06-11 13:59:45 -0700 (Mon, 11 Jun 2007) | 72 lines Merged revisions 55869-55912 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r55869 | neal.norwitz | 2007-06-10 17:42:11 -0700 (Sun, 10 Jun 2007) | 1 line Add Atul Varma for patch # 1667860 ........ r55870 | neal.norwitz | 2007-06-10 18:22:03 -0700 (Sun, 10 Jun 2007) | 1 line Ignore valgrind problems on Ubuntu from ld ........ r55872 | neal.norwitz | 2007-06-10 18:48:46 -0700 (Sun, 10 Jun 2007) | 2 lines Ignore config.status.lineno which seems new (new autoconf?) ........ r55873 | neal.norwitz | 2007-06-10 19:14:39 -0700 (Sun, 10 Jun 2007) | 1 line Prevent these tests from running on Win64 since they don\'t apply there either ........ r55874 | neal.norwitz | 2007-06-10 19:16:10 -0700 (Sun, 10 Jun 2007) | 5 lines Fix a bug when there was a newline in the string expandtabs was called on. This also catches another condition that can overflow. Will backport. ........ r55879 | neal.norwitz | 2007-06-10 21:52:37 -0700 (Sun, 10 Jun 2007) | 1 line Prevent hang if the port cannot be opened. ........ r55881 | neal.norwitz | 2007-06-10 22:28:45 -0700 (Sun, 10 Jun 2007) | 4 lines Add all of the distuils modules that don't seem to have explicit tests. :-( Move an import in mworkscompiler so that this module can be imported on any platform. Hopefully this works on all platforms. ........ r55882 | neal.norwitz | 2007-06-10 22:35:10 -0700 (Sun, 10 Jun 2007) | 4 lines SF #1734732, lower case the module names per PEP 8. Will backport. ........ r55885 | neal.norwitz | 2007-06-10 23:16:48 -0700 (Sun, 10 Jun 2007) | 4 lines Not sure why this only fails sometimes on Unix machines. Better to disable it and only import msvccompiler on Windows since that's the only place it can work anyways. ........ r55887 | neal.norwitz | 2007-06-11 00:29:43 -0700 (Mon, 11 Jun 2007) | 4 lines Bug #1734723: Fix repr.Repr() so it doesn't ignore the maxtuple attribute. Will backport ........ r55889 | neal.norwitz | 2007-06-11 00:36:24 -0700 (Mon, 11 Jun 2007) | 1 line Reflow long line ........ r55896 | thomas.heller | 2007-06-11 08:58:33 -0700 (Mon, 11 Jun 2007) | 3 lines Use "O&" in calls to PyArg_Parse when we need a 'void*' instead of "k" or "K" codes. ........ r55901 | facundo.batista | 2007-06-11 09:27:08 -0700 (Mon, 11 Jun 2007) | 5 lines Added versionchanged flag to all the methods which received a new optional timeout parameter, and a versionadded flag to the socket.create_connection function. ........ ................ r55914 | guido.van.rossum | 2007-06-11 14:19:50 -0700 (Mon, 11 Jun 2007) | 3 lines New super() implementation, for PEP 3135 (though the PEP is not yet updated to this design, and small tweaks may still be made later). ................ r55923 | guido.van.rossum | 2007-06-11 21:15:24 -0700 (Mon, 11 Jun 2007) | 4 lines I'm guessing this module broke when Neal ripped out the types module -- it used 'list' both as a local variable and as the built-in list type. Renamed the local variable since that was easier. ................ r55924 | guido.van.rossum | 2007-06-11 21:20:05 -0700 (Mon, 11 Jun 2007) | 5 lines Change all occurrences of super(, ) to super(). Seems to have worked, all the tests still pass. Exception: test_descr and test_descrtut, which have tons of these and are there to test the various usages. ................ r55939 | collin.winter | 2007-06-12 13:57:33 -0700 (Tue, 12 Jun 2007) | 1 line Patch #1735485: remove StandardError from the exception hierarchy. ................ r55954 | neal.norwitz | 2007-06-12 21:56:32 -0700 (Tue, 12 Jun 2007) | 51 lines Merged revisions 55913-55950 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r55926 | marc-andre.lemburg | 2007-06-12 02:09:58 -0700 (Tue, 12 Jun 2007) | 3 lines Apply patch #1734945 to support TurboLinux as distribution. ........ r55927 | marc-andre.lemburg | 2007-06-12 02:26:49 -0700 (Tue, 12 Jun 2007) | 3 lines Add patch #1726668: Windows Vista support. ........ r55929 | thomas.heller | 2007-06-12 08:36:22 -0700 (Tue, 12 Jun 2007) | 1 line Checkout, but do not yet try to build, exernal sources. ........ r55930 | thomas.heller | 2007-06-12 09:08:27 -0700 (Tue, 12 Jun 2007) | 6 lines Add bufferoverflowU.lib to the libraries needed by _ssl (is this the right thing to do?). Set the /XP64 /RETAIL build enviroment in the makefile when building ReleaseAMD64. ........ r55931 | thomas.heller | 2007-06-12 09:23:19 -0700 (Tue, 12 Jun 2007) | 5 lines Revert this change, since it breaks the win32 build: Add bufferoverflowU.lib to the libraries needed by _ssl (is this the right thing to do?). ........ r55934 | thomas.heller | 2007-06-12 10:28:31 -0700 (Tue, 12 Jun 2007) | 3 lines Specify the bufferoverflowU.lib to the makefile on the command line (for ReleaseAMD64 builds). ........ r55937 | thomas.heller | 2007-06-12 12:02:59 -0700 (Tue, 12 Jun 2007) | 3 lines Add bufferoverflowU.lib to PCBuild\_bsddb.vcproj. Build sqlite3.dll and bsddb. ........ r55938 | thomas.heller | 2007-06-12 12:56:12 -0700 (Tue, 12 Jun 2007) | 2 lines Don't rebuild Berkeley DB if not needed (this was committed by accident). ........ r55948 | martin.v.loewis | 2007-06-12 20:42:19 -0700 (Tue, 12 Jun 2007) | 3 lines Provide PY_LLONG_MAX on all systems having long long. Will backport to 2.5. ........ ................ r55959 | guido.van.rossum | 2007-06-13 09:22:41 -0700 (Wed, 13 Jun 2007) | 2 lines Fix a compilation warning. ................ --- ccompiler.py | 2 +- cmd.py | 2 +- command/build_py.py | 4 +++- command/build_scripts.py | 4 ++-- command/install_scripts.py | 2 +- command/register.py | 2 +- dir_util.py | 4 ++-- mwerkscompiler.py | 2 +- tests/support.py | 8 ++++---- tests/test_build_py.py | 35 +++++++++++++++++++++++++++++++++++ 10 files changed, 51 insertions(+), 14 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 50905c1d..d4f4adea 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1040,7 +1040,7 @@ main (int argc, char **argv) { def move_file (self, src, dst): return move_file (src, dst, dry_run=self.dry_run) - def mkpath (self, name, mode=0777): + def mkpath (self, name, mode=0o777): mkpath (name, mode, self.dry_run) diff --git a/cmd.py b/cmd.py index 8d77e7fb..b2c952c3 100644 --- a/cmd.py +++ b/cmd.py @@ -356,7 +356,7 @@ class Command: util.execute(func, args, msg, dry_run=self.dry_run) - def mkpath (self, name, mode=0777): + def mkpath (self, name, mode=0o777): dir_util.mkpath(name, mode, dry_run=self.dry_run) diff --git a/command/build_py.py b/command/build_py.py index 52534bdb..8f560908 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -114,7 +114,9 @@ class build_py (Command): build_dir = os.path.join(*([self.build_lib] + package.split('.'))) # Length of path to strip from found files - plen = len(src_dir)+1 + plen = 0 + if src_dir: + plen = len(src_dir)+1 # Strip directory from globbed filenames filenames = [ diff --git a/command/build_scripts.py b/command/build_scripts.py index bda4480c..511b82f9 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -119,8 +119,8 @@ class build_scripts (Command): if self.dry_run: log.info("changing mode of %s", file) else: - oldmode = os.stat(file)[ST_MODE] & 07777 - newmode = (oldmode | 0555) & 07777 + oldmode = os.stat(file)[ST_MODE] & 0o7777 + newmode = (oldmode | 0o555) & 0o7777 if newmode != oldmode: log.info("changing mode of %s from %o to %o", file, oldmode, newmode) diff --git a/command/install_scripts.py b/command/install_scripts.py index fe93ef5a..da2da358 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -53,7 +53,7 @@ class install_scripts (Command): if self.dry_run: log.info("changing mode of %s", file) else: - mode = ((os.stat(file)[ST_MODE]) | 0555) & 07777 + mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777 log.info("changing mode of %s to %o", file, mode) os.chmod(file, mode) diff --git a/command/register.py b/command/register.py index 2ddabadf..53f4293e 100644 --- a/command/register.py +++ b/command/register.py @@ -183,7 +183,7 @@ Your selection [default 1]: ''', end=' ') username, password)) f.close() try: - os.chmod(rc, 0600) + os.chmod(rc, 0o600) except: pass elif choice == '2': diff --git a/dir_util.py b/dir_util.py index c6f014b2..0cfca2e8 100644 --- a/dir_util.py +++ b/dir_util.py @@ -18,7 +18,7 @@ _path_created = {} # I don't use os.makedirs because a) it's new to Python 1.5.2, and # b) it blows up if the directory already exists (I want to silently # succeed in that case). -def mkpath (name, mode=0777, verbose=0, dry_run=0): +def mkpath (name, mode=0o777, verbose=0, dry_run=0): """Create a directory and any missing ancestor directories. If the directory already exists (or if 'name' is the empty string, which means the current directory, which of course exists), then do @@ -85,7 +85,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): # mkpath () -def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0): +def create_tree (base_dir, files, mode=0o777, verbose=0, dry_run=0): """Create all the empty directories under 'base_dir' needed to put 'files' there. 'base_dir' is just the a name of a directory diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 028ea825..662046ae 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -18,7 +18,6 @@ from distutils.ccompiler import \ import distutils.util import distutils.dir_util from distutils import log -import mkcwproject class MWerksCompiler (CCompiler) : """Concrete class that implements an interface to MetroWerks CodeWarrior, @@ -188,6 +187,7 @@ class MWerksCompiler (CCompiler) : # doesn't have a clue about our working directory. xmlfilename = os.path.join(os.getcwd(), os.path.join(build_temp, xmlname)) log.debug("\tCreate XML file %s", xmlfilename) + import mkcwproject xmlbuilder = mkcwproject.cwxmlgen.ProjectBuilder(settings) xmlbuilder.generate() xmldata = settings['tmp_projectxmldata'] diff --git a/tests/support.py b/tests/support.py index 475ceee5..91e704cf 100644 --- a/tests/support.py +++ b/tests/support.py @@ -9,12 +9,12 @@ from distutils import log class LoggingSilencer(object): def setUp(self): - super(LoggingSilencer, self).setUp() + super().setUp() self.threshold = log.set_threshold(log.FATAL) def tearDown(self): log.set_threshold(self.threshold) - super(LoggingSilencer, self).tearDown() + super().tearDown() class TempdirManager(object): @@ -24,11 +24,11 @@ class TempdirManager(object): """ def setUp(self): - super(TempdirManager, self).setUp() + super().setUp() self.tempdirs = [] def tearDown(self): - super(TempdirManager, self).tearDown() + super().tearDown() while self.tempdirs: d = self.tempdirs.pop() shutil.rmtree(d) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 78e4c55e..54a4ed80 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -1,10 +1,13 @@ """Tests for distutils.command.build_py.""" import os +import sys +import StringIO import unittest from distutils.command.build_py import build_py from distutils.core import Distribution +from distutils.errors import DistutilsFileError from distutils.tests import support @@ -53,6 +56,38 @@ class BuildPyTestCase(support.TempdirManager, self.assert_("__init__.pyc" in files) self.assert_("README.txt" in files) + def test_empty_package_dir (self): + # See SF 1668596/1720897. + cwd = os.getcwd() + + # create the distribution files. + sources = self.mkdtemp() + open(os.path.join(sources, "__init__.py"), "w").close() + + testdir = os.path.join(sources, "doc") + os.mkdir(testdir) + open(os.path.join(testdir, "testfile"), "w").close() + + os.chdir(sources) + sys.stdout = StringIO.StringIO() + + try: + dist = Distribution({"packages": ["pkg"], + "package_dir": {"pkg": ""}, + "package_data": {"pkg": ["doc/*"]}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.script_args = ["build"] + dist.parse_command_line() + + try: + dist.run_commands() + except DistutilsFileError: + self.fail("failed package_data test when package_dir is ''") + finally: + # Restore state. + os.chdir(cwd) + sys.stdout = sys.__stdout__ def test_suite(): return unittest.makeSuite(BuildPyTestCase) -- cgit v1.2.1 From a16a76a14958dc71e70971b0fc346e34491f734e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sat, 30 Jun 2007 05:01:58 +0000 Subject: Merged revisions 56020-56124 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/p3yk ................ r56037 | georg.brandl | 2007-06-19 05:33:20 -0700 (Tue, 19 Jun 2007) | 2 lines Patch #1739659: don't slice dict.keys() in pydoc. ................ r56060 | martin.v.loewis | 2007-06-21 13:00:02 -0700 (Thu, 21 Jun 2007) | 2 lines Regenerate to add True, False, None. ................ r56069 | neal.norwitz | 2007-06-21 22:31:56 -0700 (Thu, 21 Jun 2007) | 1 line Get the doctest working again after adding None, True, and False as kewyords. ................ r56070 | neal.norwitz | 2007-06-21 23:25:33 -0700 (Thu, 21 Jun 2007) | 1 line Add space to error message. ................ r56071 | neal.norwitz | 2007-06-21 23:40:04 -0700 (Thu, 21 Jun 2007) | 6 lines Get pybench working, primarily * Use print function * Stop using string module * Use sorted instead of assuming dict methods return lists * Convert range result to a list ................ r56089 | collin.winter | 2007-06-26 10:31:48 -0700 (Tue, 26 Jun 2007) | 1 line Fix AttributeError in distutils/dir_util.py. ................ r56124 | guido.van.rossum | 2007-06-29 18:04:31 -0700 (Fri, 29 Jun 2007) | 30 lines Merged revisions 56014-56123 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r56019 | lars.gustaebel | 2007-06-18 04:42:11 -0700 (Mon, 18 Jun 2007) | 2 lines Added exclude keyword argument to the TarFile.add() method. ........ r56023 | lars.gustaebel | 2007-06-18 13:05:55 -0700 (Mon, 18 Jun 2007) | 3 lines Added missing \versionchanged tag for the new exclude parameter. ........ r56038 | georg.brandl | 2007-06-19 05:36:00 -0700 (Tue, 19 Jun 2007) | 2 lines Bug #1737864: allow empty message in logging format routines. ........ r56040 | georg.brandl | 2007-06-19 05:38:20 -0700 (Tue, 19 Jun 2007) | 2 lines Bug #1739115: make shutil.rmtree docs clear wrt. file deletion. ........ r56084 | georg.brandl | 2007-06-25 08:21:23 -0700 (Mon, 25 Jun 2007) | 2 lines Bug #1742901: document None behavior of shlex.split. ........ r56091 | georg.brandl | 2007-06-27 07:09:56 -0700 (Wed, 27 Jun 2007) | 2 lines Fix a variable name in winreg docs. ........ ................ --- dir_util.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dir_util.py b/dir_util.py index 0cfca2e8..a6c4416a 100644 --- a/dir_util.py +++ b/dir_util.py @@ -96,14 +96,12 @@ def create_tree (base_dir, files, mode=0o777, verbose=0, dry_run=0): for 'mkpath()'.""" # First get the list of directories to create - need_dir = {} + need_dir = set() for file in files: - need_dir[os.path.join(base_dir, os.path.dirname(file))] = 1 - need_dirs = need_dir.keys() - need_dirs.sort() + need_dir.add(os.path.join(base_dir, os.path.dirname(file))) # Now create them - for dir in need_dirs: + for dir in sorted(need_dir): mkpath(dir, mode, dry_run=dry_run) # create_tree () -- cgit v1.2.1 From 3abb62b2c71e85682cb45612ce358f174a71b545 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 3 Jul 2007 08:25:58 +0000 Subject: Merged revisions 56125-56153 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/p3yk ........ r56127 | georg.brandl | 2007-06-30 09:32:49 +0200 (Sat, 30 Jun 2007) | 2 lines Fix a place where floor division would be in order. ........ r56135 | guido.van.rossum | 2007-07-01 06:13:54 +0200 (Sun, 01 Jul 2007) | 28 lines Make map() and filter() identical to itertools.imap() and .ifilter(), respectively. I fixed two bootstrap issues, due to the dynamic import of itertools: 1. Starting python requires that map() and filter() are not used until site.py has added build/lib. to sys.path. 2. Building python requires that setup.py and distutils and everything they use is free of map() and filter() calls. Beyond this, I only fixed the tests in test_builtin.py. Others, please help fixing the remaining tests that are now broken! The fixes are usually simple: a. map(None, X) -> list(X) b. map(F, X) -> list(map(F, X)) c. map(lambda x: F(x), X) -> [F(x) for x in X] d. filter(F, X) -> list(filter(F, X)) e. filter(lambda x: P(x), X) -> [x for x in X if P(x)] Someone, please also contribute a fixer for 2to3 to do this. It can leave map()/filter() calls alone that are already inside a list() or sorted() call or for-loop. Only in rare cases have I seen code that depends on map() of lists of different lengths going to the end of the longest, or on filter() of a string or tuple returning an object of the same type; these will need more thought to fix. ........ r56136 | guido.van.rossum | 2007-07-01 06:22:01 +0200 (Sun, 01 Jul 2007) | 3 lines Make it so that test_decimal fails instead of hangs, to help automated test runners. ........ r56139 | georg.brandl | 2007-07-01 18:20:58 +0200 (Sun, 01 Jul 2007) | 2 lines Fix a few test cases after the map->imap change. ........ r56142 | neal.norwitz | 2007-07-02 06:38:12 +0200 (Mon, 02 Jul 2007) | 1 line Get a bunch more tests passing after converting map/filter to return iterators. ........ r56147 | guido.van.rossum | 2007-07-02 15:32:02 +0200 (Mon, 02 Jul 2007) | 4 lines Fix the remaining failing unit tests (at least on OSX). Also tweaked urllib2 so it doesn't raise socket.gaierror when all network interfaces are turned off. ........ --- dist.py | 5 ++--- sysconfig.py | 2 +- version.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/dist.py b/dist.py index c01724d8..8f614765 100644 --- a/dist.py +++ b/dist.py @@ -112,8 +112,7 @@ Common commands: (see '--help-commands' for more) ('obsoletes', None, "print the list of packages/modules made obsolete") ] - display_option_names = map(lambda x: translate_longopt(x[0]), - display_options) + display_option_names = [translate_longopt(x[0]) for x in display_options] # negative options are options that exclude other options negative_opt = {'quiet': 'verbose'} @@ -805,7 +804,7 @@ Common commands: (see '--help-commands' for more) pkgs = (pkgs or "").split(",") for i in range(len(pkgs)): pkgs[i] = pkgs[i].strip() - pkgs = filter(None, pkgs) + pkgs = [p for p in pkgs if p] if "distutils.command" not in pkgs: pkgs.insert(0, "distutils.command") self.command_packages = pkgs diff --git a/sysconfig.py b/sysconfig.py index 51f23a24..346707fa 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -372,7 +372,7 @@ def _init_posix(): if cur_target == '': cur_target = cfg_target os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) - elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')): + elif [int(x) for x in cfg_target.split('.')] > [int(x) for x in cur_target.split('.')]: my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' % (cur_target, cfg_target)) raise DistutilsPlatformError(my_msg) diff --git a/version.py b/version.py index 2db6b18f..de20e21f 100644 --- a/version.py +++ b/version.py @@ -148,7 +148,7 @@ class StrictVersion (Version): if patch: self.version = tuple(map(int, [major, minor, patch])) else: - self.version = tuple(map(int, [major, minor]) + [0]) + self.version = tuple(map(int, [major, minor])) + (0,) if prerelease: self.prerelease = (prerelease[0], int(prerelease_num)) -- cgit v1.2.1 From 8f2fa921c711ac7d3ff775023a9e85061fddd202 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 16 Jul 2007 23:10:57 +0000 Subject: Change a bunch of file encodings from Latin-1 to UTF-8. Remove the encoding from Tix.py (it doesn't seem to need one). Note: we still have to keep the "coding: utf-8" declaration for files that aren't pure ASCII, as the default per PEP 3120 hasn't been implemented yet. --- command/bdist_msi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index f31bdeda..012d06ea 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -1,5 +1,5 @@ -# -*- coding: iso-8859-1 -*- -# Copyright (C) 2005, 2006 Martin v. Löwis +# -*- coding: utf-8 -*- +# Copyright (C) 2005, 2006 Martin v. Löwis # Licensed to PSF under a Contributor Agreement. # The bdist_wininst command proper # based on bdist_wininst -- cgit v1.2.1 From 804a12d456aa7253cd54ce0e183277f4459f7d7d Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Tue, 17 Jul 2007 00:38:21 +0000 Subject: Fix a bug in distutils.core's error handling. --- core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index 6242775c..bd9b8542 100644 --- a/core.py +++ b/core.py @@ -112,10 +112,10 @@ def setup (**attrs): _setup_distribution = dist = klass(attrs) except DistutilsSetupError as msg: if 'name' not in attrs: + raise SystemExit, "error in setup command: %s" % msg + else: raise SystemExit, "error in %s setup command: %s" % \ (attrs['name'], msg) - else: - raise SystemExit, "error in setup command: %s" % msg if _setup_stop_after == "init": return dist -- cgit v1.2.1 From 555f239ef6a7c03122bb5b73a3b229fd08634adc Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Tue, 17 Jul 2007 00:39:32 +0000 Subject: Fix two bugs from the map->itertools.imap switch. --- filelist.py | 3 +-- version.py | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/filelist.py b/filelist.py index a0523531..cc48e48e 100644 --- a/filelist.py +++ b/filelist.py @@ -65,8 +65,7 @@ class FileList: def sort (self): # Not a strict lexical sort! - sortable_files = map(os.path.split, self.files) - sortable_files.sort() + sortable_files = sorted(map(os.path.split, self.files)) self.files = [] for sort_tuple in sortable_files: self.files.append(os.path.join(*sort_tuple)) diff --git a/version.py b/version.py index de20e21f..96b65521 100644 --- a/version.py +++ b/version.py @@ -306,11 +306,11 @@ class LooseVersion (Version): # from the parsed tuple -- so I just store the string here for # use by __str__ self.vstring = vstring - components = filter(lambda x: x and x != '.', - self.component_re.split(vstring)) - for i in range(len(components)): + components = [x for x in self.component_re.split(vstring) + if x and x != '.'] + for i, obj in enumerate(components): try: - components[i] = int(components[i]) + components[i] = int(obj) except ValueError: pass -- cgit v1.2.1 From 3bffc1fa4b8b251e239b69f32086413d12932441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 25 Jul 2007 16:24:08 +0000 Subject: Change location of the package index to pypi.python.org/pypi. --- command/register.py | 2 +- command/upload.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/register.py b/command/register.py index 5fcc8d27..200e61e2 100644 --- a/command/register.py +++ b/command/register.py @@ -17,7 +17,7 @@ class register(Command): description = ("register the distribution with the Python package index") - DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' user_options = [ ('repository=', 'r', diff --git a/command/upload.py b/command/upload.py index 67ba0804..7461e5f4 100644 --- a/command/upload.py +++ b/command/upload.py @@ -20,7 +20,7 @@ class upload(Command): description = "upload binary package to PyPI" - DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' user_options = [ ('repository=', 'r', -- cgit v1.2.1 From 1b328f363d2ec8dc42735a5fae9e133e5e221d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 25 Jul 2007 16:24:23 +0000 Subject: Change location of the package index to pypi.python.org/pypi --- command/register.py | 2 +- command/upload.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/register.py b/command/register.py index 5fcc8d27..200e61e2 100644 --- a/command/register.py +++ b/command/register.py @@ -17,7 +17,7 @@ class register(Command): description = ("register the distribution with the Python package index") - DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' user_options = [ ('repository=', 'r', diff --git a/command/upload.py b/command/upload.py index 67ba0804..7461e5f4 100644 --- a/command/upload.py +++ b/command/upload.py @@ -20,7 +20,7 @@ class upload(Command): description = "upload binary package to PyPI" - DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' user_options = [ ('repository=', 'r', -- cgit v1.2.1 From 8c45e5f857293b59b834acf6fbaa8428ae6dca70 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 6 Aug 2007 23:33:07 +0000 Subject: Merged revisions 56753-56781 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/p3yk ................ r56760 | neal.norwitz | 2007-08-05 18:55:39 -0700 (Sun, 05 Aug 2007) | 178 lines Merged revisions 56477-56759 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r56485 | facundo.batista | 2007-07-21 17:13:00 -0700 (Sat, 21 Jul 2007) | 5 lines Selectively enable tests for asyncore.readwrite based on the presence of poll support in the select module (since this is the only case in which readwrite can be called). [GSoC - Alan McIntyre] ........ r56488 | nick.coghlan | 2007-07-22 03:18:07 -0700 (Sun, 22 Jul 2007) | 1 line Add explicit relative import tests for runpy.run_module ........ r56509 | nick.coghlan | 2007-07-23 06:41:45 -0700 (Mon, 23 Jul 2007) | 5 lines Correctly cleanup sys.modules after executing runpy relative import tests Restore Python 2.4 ImportError when attempting to execute a package (as imports cannot be guaranteed to work properly if you try it) ........ r56519 | nick.coghlan | 2007-07-24 06:07:38 -0700 (Tue, 24 Jul 2007) | 1 line Tweak runpy test to do a better job of confirming that sys has been manipulated correctly ........ r56520 | nick.coghlan | 2007-07-24 06:58:28 -0700 (Tue, 24 Jul 2007) | 1 line Fix an incompatibility between the -i and -m command line switches as reported on python-dev by PJE - runpy.run_module now leaves any changes it makes to the sys module intact after the function terminates ........ r56523 | nick.coghlan | 2007-07-24 07:39:23 -0700 (Tue, 24 Jul 2007) | 1 line Try to get rid of spurious failure in test_resource on the Debian buildbots by changing the file size limit before attempting to close the file ........ r56533 | facundo.batista | 2007-07-24 14:20:42 -0700 (Tue, 24 Jul 2007) | 7 lines New tests for basic behavior of smtplib.SMTP and smtpd.DebuggingServer. Change to use global host & port number variables. Modified the 'server' to take a string to send back in order to vary test server responses. Added a test for the reaction of smtplib.SMTP to a non-200 HELO response. [GSoC - Alan McIntyre] ........ r56538 | nick.coghlan | 2007-07-25 05:57:48 -0700 (Wed, 25 Jul 2007) | 1 line More buildbot cleanup - let the OS assign the port for test_urllib2_localnet ........ r56539 | nick.coghlan | 2007-07-25 06:18:58 -0700 (Wed, 25 Jul 2007) | 1 line Add a temporary diagnostic message before a strange failure on the alpha Debian buildbot ........ r56543 | martin.v.loewis | 2007-07-25 09:24:23 -0700 (Wed, 25 Jul 2007) | 2 lines Change location of the package index to pypi.python.org/pypi ........ r56551 | georg.brandl | 2007-07-26 02:36:25 -0700 (Thu, 26 Jul 2007) | 2 lines tabs, newlines and crs are valid XML characters. ........ r56553 | nick.coghlan | 2007-07-26 07:03:00 -0700 (Thu, 26 Jul 2007) | 1 line Add explicit test for a misbehaving math.floor ........ r56561 | mark.hammond | 2007-07-26 21:52:32 -0700 (Thu, 26 Jul 2007) | 3 lines In consultation with Kristjan Jonsson, only define WINVER and _WINNT_WIN32 if (a) we are building Python itself and (b) no one previously defined them ........ r56562 | mark.hammond | 2007-07-26 22:08:54 -0700 (Thu, 26 Jul 2007) | 2 lines Correctly detect AMD64 architecture on VC2003 ........ r56566 | nick.coghlan | 2007-07-27 03:36:30 -0700 (Fri, 27 Jul 2007) | 1 line Make test_math error messages more meaningful for small discrepancies in results ........ r56588 | martin.v.loewis | 2007-07-27 11:28:22 -0700 (Fri, 27 Jul 2007) | 2 lines Bug #978833: Close https sockets by releasing the _ssl object. ........ r56601 | martin.v.loewis | 2007-07-28 00:03:05 -0700 (Sat, 28 Jul 2007) | 3 lines Bug #1704793: Return UTF-16 pair if unicodedata.lookup cannot represent the result in a single character. ........ r56604 | facundo.batista | 2007-07-28 07:21:22 -0700 (Sat, 28 Jul 2007) | 9 lines Moved all of the capture_server socket setup code into the try block so that the event gets set if a failure occurs during server setup (otherwise the test will block forever). Changed to let the OS assign the server port number, and client side of test waits for port number assignment before proceeding. The test data in DispatcherWithSendTests is also sent in multiple send() calls instead of one to make sure this works properly. [GSoC - Alan McIntyre] ........ r56611 | georg.brandl | 2007-07-29 01:26:10 -0700 (Sun, 29 Jul 2007) | 2 lines Clarify PEP 343 description. ........ r56614 | georg.brandl | 2007-07-29 02:11:15 -0700 (Sun, 29 Jul 2007) | 2 lines try-except-finally is new in 2.5. ........ r56617 | facundo.batista | 2007-07-29 07:23:08 -0700 (Sun, 29 Jul 2007) | 9 lines Added tests for asynchat classes simple_producer & fifo, and the find_prefix_at_end function. Check behavior of a string given as a producer. Added tests for behavior of asynchat.async_chat when given int, long, and None terminator arguments. Added usepoll attribute to TestAsynchat to allow running the asynchat tests with poll support chosen whether it's available or not (improves coverage of asyncore code). [GSoC - Alan McIntyre] ........ r56620 | georg.brandl | 2007-07-29 10:38:35 -0700 (Sun, 29 Jul 2007) | 2 lines Bug #1763149: use proper slice syntax in docstring. (backport) ........ r56624 | mark.hammond | 2007-07-29 17:45:29 -0700 (Sun, 29 Jul 2007) | 4 lines Correct use of Py_BUILD_CORE - now make sure it is defined before it is referenced, and also fix definition of _WIN32_WINNT. Resolves patch 1761803. ........ r56632 | facundo.batista | 2007-07-30 20:03:34 -0700 (Mon, 30 Jul 2007) | 8 lines When running asynchat tests on OS X (darwin), the test client now overrides asyncore.dispatcher.handle_expt to do nothing, since select.poll gives a POLLHUP error at the completion of these tests. Added timeout & count arguments to several asyncore.loop calls to avoid the possibility of a test hanging up a build. [GSoC - Alan McIntyre] ........ r56633 | nick.coghlan | 2007-07-31 06:38:01 -0700 (Tue, 31 Jul 2007) | 1 line Eliminate RLock race condition reported in SF bug #1764059 ........ r56636 | martin.v.loewis | 2007-07-31 12:57:56 -0700 (Tue, 31 Jul 2007) | 2 lines Define _BSD_SOURCE, to get access to POSIX extensions on OpenBSD 4.1+. ........ r56653 | facundo.batista | 2007-08-01 16:18:36 -0700 (Wed, 01 Aug 2007) | 9 lines Allow the OS to select a free port for each test server. For DebuggingServerTests, construct SMTP objects with a localhost argument to avoid abysmally long FQDN lookups (not relevant to items under test) on some machines that would cause the test to fail. Moved server setup code in the server function inside the try block to avoid the possibility of setup failure hanging the test. Minor edits to conform to PEP 8. [GSoC - Alan McIntyre] ........ r56681 | matthias.klose | 2007-08-02 14:33:13 -0700 (Thu, 02 Aug 2007) | 2 lines - Allow Emacs 22 for building the documentation in info format. ........ r56689 | neal.norwitz | 2007-08-02 23:46:29 -0700 (Thu, 02 Aug 2007) | 1 line Py_ssize_t is defined regardless of HAVE_LONG_LONG. Will backport ........ r56727 | hyeshik.chang | 2007-08-03 21:10:18 -0700 (Fri, 03 Aug 2007) | 3 lines Fix gb18030 codec's bug that doesn't map two-byte characters on GB18030 extension in encoding. (bug reported by Bjorn Stabell) ........ r56751 | neal.norwitz | 2007-08-04 20:23:31 -0700 (Sat, 04 Aug 2007) | 7 lines Handle errors when generating a warning. The value is always written to the returned pointer if getting it was successful, even if a warning causes an error. (This probably doesn't matter as the caller will probably discard the value.) Will backport. ........ ................ --- command/register.py | 2 +- command/upload.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/register.py b/command/register.py index 53f4293e..10a903e3 100644 --- a/command/register.py +++ b/command/register.py @@ -22,7 +22,7 @@ class register(Command): description = ("register the distribution with the Python package index") - DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' user_options = [ ('repository=', 'r', diff --git a/command/upload.py b/command/upload.py index 438ae99a..d1cf87a8 100644 --- a/command/upload.py +++ b/command/upload.py @@ -20,7 +20,7 @@ class upload(Command): description = "upload binary package to PyPI" - DEFAULT_REPOSITORY = 'http://www.python.org/pypi' + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' user_options = [ ('repository=', 'r', -- cgit v1.2.1 From 913423ef2fd98c50b414f552f21604a699430a67 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 9 Aug 2007 01:03:29 +0000 Subject: SF patch# 1770008 by Christian Heimes (plus some extras). Completely get rid of StringIO.py and cStringIO.c. I had to fix a few tests and modules beyond what Christian did, and invent a few conventions. E.g. in elementtree, I chose to write/return Unicode strings whe no encoding is given, but bytes when an explicit encoding is given. Also mimetools was made to always assume binary files. --- command/register.py | 4 ++-- command/upload.py | 3 +-- tests/test_build_py.py | 4 ++-- tests/test_dist.py | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/command/register.py b/command/register.py index 10a903e3..91081ddb 100644 --- a/command/register.py +++ b/command/register.py @@ -8,7 +8,7 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" import sys, os, urllib2, getpass, urlparse -import StringIO, ConfigParser +import io, ConfigParser from distutils.core import Command from distutils.errors import * @@ -253,7 +253,7 @@ Your selection [default 1]: ''', end=' ') boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary end_boundary = sep_boundary + '--' - body = StringIO.StringIO() + body = io.StringIO() for key, value in data.items(): # handle multiple entries for the same name if type(value) not in (type([]), type( () )): diff --git a/command/upload.py b/command/upload.py index d1cf87a8..1ca2fb90 100644 --- a/command/upload.py +++ b/command/upload.py @@ -14,7 +14,6 @@ import ConfigParser import httplib import base64 import urlparse -import cStringIO as StringIO class upload(Command): @@ -135,7 +134,7 @@ class upload(Command): boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary end_boundary = sep_boundary + '--' - body = StringIO.StringIO() + body = io.StringIO() for key, value in data.items(): # handle multiple entries for the same name if type(value) != type([]): diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 54a4ed80..75b6624f 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -2,7 +2,7 @@ import os import sys -import StringIO +import io import unittest from distutils.command.build_py import build_py @@ -69,7 +69,7 @@ class BuildPyTestCase(support.TempdirManager, open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) - sys.stdout = StringIO.StringIO() + sys.stdout = io.StringIO() try: dist = Distribution({"packages": ["pkg"], diff --git a/tests/test_dist.py b/tests/test_dist.py index 8d4b0703..23506b5d 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -4,7 +4,7 @@ import distutils.cmd import distutils.dist import os import shutil -import StringIO +import io import sys import tempfile import unittest @@ -177,7 +177,7 @@ class MetadataTestCase(unittest.TestCase): "obsoletes": ["my.pkg (splat)"]}) def format_metadata(self, dist): - sio = StringIO.StringIO() + sio = io.StringIO() dist.metadata.write_pkg_file(sio) return sio.getvalue() -- cgit v1.2.1 From 3a44c01ba5f3f9c0b34705d05e934cbae24d7d60 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sun, 12 Aug 2007 00:43:29 +0000 Subject: Kill execfile(), use exec() instead --- core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index bd9b8542..d6099824 100644 --- a/core.py +++ b/core.py @@ -179,7 +179,7 @@ def run_setup (script_name, script_args=None, stop_after="run"): keyword args from 'script' to 'setup()', or the contents of the config files or command-line. - 'script_name' is a file that will be run with 'execfile()'; + 'script_name' is a file that will be read and run with 'exec()'; 'sys.argv[0]' will be replaced with 'script' for the duration of the call. 'script_args' is a list of strings; if supplied, 'sys.argv[1:]' will be replaced by 'script_args' for the duration of @@ -217,7 +217,7 @@ def run_setup (script_name, script_args=None, stop_after="run"): sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args - execfile(script_name, g, l) + exec(open(script_name).read(), g, l) finally: sys.argv = save_argv _setup_stop_after = None -- cgit v1.2.1 From c2b4b26b086f52d2e029ebcbd5170f5092b39526 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Thu, 16 Aug 2007 14:35:24 +0000 Subject: Remove RISCOS support --- command/install_lib.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/command/install_lib.py b/command/install_lib.py index 65d25f7a..4efaf9c6 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -9,10 +9,7 @@ from distutils.errors import DistutilsOptionError # Extension for Python source files. -if hasattr(os, 'extsep'): - PYTHON_SOURCE_EXTENSION = os.extsep + "py" -else: - PYTHON_SOURCE_EXTENSION = ".py" +PYTHON_SOURCE_EXTENSION = ".py" class install_lib (Command): -- cgit v1.2.1 From 59cd99d0b267019cd9811bc03ec379827b8f8410 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Fri, 17 Aug 2007 12:57:41 +0000 Subject: Remove support for BeOS --- sysconfig.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 346707fa..c40c2e74 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -394,24 +394,6 @@ def _init_posix(): g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) - elif sys.platform == 'beos': - # Linker script is in the config directory. In the Makefile it is - # relative to the srcdir, which after installation no longer makes - # sense. - python_lib = get_python_lib(standard_lib=1) - linkerscript_path = g['LDSHARED'].split()[0] - linkerscript_name = os.path.basename(linkerscript_path) - linkerscript = os.path.join(python_lib, 'config', - linkerscript_name) - - # XXX this isn't the right place to do this: adding the Python - # library to the link, if needed, should be in the "build_ext" - # command. (It's also needed for non-MS compilers on Windows, and - # it's taken care of for them by the 'build_ext.get_libraries()' - # method.) - g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % - (linkerscript, PREFIX, get_python_version())) - global _config_vars _config_vars = g -- cgit v1.2.1 From e9d9c100389e6d04f060f58eeb5e75921e82eb59 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Tue, 21 Aug 2007 01:04:47 +0000 Subject: [ 1761786 ] distutils.util.get_platform() return value on 64bit Windows As discussed on distutils-sig: Allows the generated installer name on 64bit Windows platforms to be different than the name generated for 32bit Windows platforms. --- util.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/util.py b/util.py index cfcc6a95..8979634e 100644 --- a/util.py +++ b/util.py @@ -29,8 +29,27 @@ def get_platform (): irix-5.3 irix64-6.2 - For non-POSIX platforms, currently just returns 'sys.platform'. + Windows will return one of: + win-x86_64 (64bit Windows on x86_64 (AMD64)) + win-ia64 (64bit Windows on Itanium) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. """ + if os.name == 'nt': + # sniff sys.version for architecture. + prefix = " bit (" + i = string.find(sys.version, prefix) + if i == -1: + return sys.platform + j = string.find(sys.version, ")", i) + look = sys.version[i+len(prefix):j].lower() + if look=='amd64': + return 'win-x86_64' + if look=='itanium': + return 'win-ia64' + return sys.platform + if os.name != "posix" or not hasattr(os, 'uname'): # XXX what about the architecture? NT is Intel or Alpha, # Mac OS is M68k or PPC, etc. -- cgit v1.2.1 From 8654a69ef372ec3dc466d9fe80f4991d43d1f6cc Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Tue, 21 Aug 2007 01:05:16 +0000 Subject: [ 1761786 ] distutils.util.get_platform() return value on 64bit Windows As discussed on distutils-sig: Allows the generated installer name on 64bit Windows platforms to be different than the name generated for 32bit Windows platforms. --- command/bdist_msi.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 75db8773..a1c0df4b 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -633,7 +633,8 @@ class bdist_msi (Command): def get_installer_filename(self, fullname): # Factored out to allow overriding in subclasses + plat = get_platform() installer_name = os.path.join(self.dist_dir, - "%s.win32-py%s.msi" % - (fullname, self.target_version)) + "%s.%s-py%s.msi" % + (fullname, plat, self.target_version)) return installer_name -- cgit v1.2.1 From 04cd9c5fbf2d2bbdd8621c82fa07ac5347a45bf9 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 24 Aug 2007 16:32:05 +0000 Subject: Merged revisions 57221-57391 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r57227 | facundo.batista | 2007-08-20 17:16:21 -0700 (Mon, 20 Aug 2007) | 5 lines Catch ProtocolError exceptions and include the header information in test output (to make it easier to debug test failures caused by problems in the server). [GSoC - Alan McIntyre] ........ r57229 | mark.hammond | 2007-08-20 18:04:47 -0700 (Mon, 20 Aug 2007) | 5 lines [ 1761786 ] distutils.util.get_platform() return value on 64bit Windows As discussed on distutils-sig: Allows the generated installer name on 64bit Windows platforms to be different than the name generated for 32bit Windows platforms. ........ r57230 | mark.hammond | 2007-08-20 18:05:16 -0700 (Mon, 20 Aug 2007) | 5 lines [ 1761786 ] distutils.util.get_platform() return value on 64bit Windows As discussed on distutils-sig: Allows the generated installer name on 64bit Windows platforms to be different than the name generated for 32bit Windows platforms. ........ r57253 | georg.brandl | 2007-08-20 23:01:18 -0700 (Mon, 20 Aug 2007) | 2 lines Demand version 2.5.1 since 2.5 has a bug with codecs.open context managers. ........ r57254 | georg.brandl | 2007-08-20 23:03:43 -0700 (Mon, 20 Aug 2007) | 2 lines Revert accidental checkins from last commit. ........ r57255 | georg.brandl | 2007-08-20 23:07:08 -0700 (Mon, 20 Aug 2007) | 2 lines Bug #1777160: mention explicitly that e.g. -1**2 is -1. ........ r57256 | georg.brandl | 2007-08-20 23:12:19 -0700 (Mon, 20 Aug 2007) | 3 lines Bug #1777168: replace operator names "opa"... with "op1"... and mark everything up as literal, to enhance readability. ........ r57259 | facundo.batista | 2007-08-21 09:57:18 -0700 (Tue, 21 Aug 2007) | 8 lines Added test for behavior of operations on an unconnected SMTP object, and tests for NOOP, RSET, and VRFY. Corrected typo in a comment for testNonnumericPort. Added a check for constructing SMTP objects when non-numeric ports are included in the host name. Derived a server from SMTPServer to test various ESMTP/SMTP capabilities. Check that a second HELO to DebuggingServer returns an error. [GSoC - Alan McIntyre] ........ r57279 | skip.montanaro | 2007-08-22 12:02:16 -0700 (Wed, 22 Aug 2007) | 2 lines Note that BeOS is unsupported as of Python 2.6. ........ r57280 | skip.montanaro | 2007-08-22 12:05:21 -0700 (Wed, 22 Aug 2007) | 1 line whoops - need to check in configure as well ........ r57284 | alex.martelli | 2007-08-22 14:14:17 -0700 (Wed, 22 Aug 2007) | 5 lines Fix compile.c so that it records 0.0 and -0.0 as separate constants in a code object's co_consts tuple; add a test to show that the previous behavior (where these two constants were "collapsed" into one) causes serious malfunctioning. ........ r57286 | gregory.p.smith | 2007-08-22 14:32:34 -0700 (Wed, 22 Aug 2007) | 3 lines stop leaving log.0000001 __db.00* and xxx.db turds in developer sandboxes when bsddb3 tests are run. ........ r57301 | jeffrey.yasskin | 2007-08-22 16:14:27 -0700 (Wed, 22 Aug 2007) | 3 lines When setup.py fails to find the necessary bits to build some modules, have it print a slightly more informative message. ........ r57320 | brett.cannon | 2007-08-23 07:53:17 -0700 (Thu, 23 Aug 2007) | 2 lines Make test_runpy re-entrant. ........ r57324 | georg.brandl | 2007-08-23 10:54:11 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1768121: fix wrong/missing opcode docs. ........ r57326 | georg.brandl | 2007-08-23 10:57:05 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1766421: "return code" vs. "status code". ........ r57328 | georg.brandl | 2007-08-23 11:08:06 -0700 (Thu, 23 Aug 2007) | 2 lines Second half of #1752175: #ifdef out references to PyImport_DynLoadFiletab if HAVE_DYNAMIC_LOADING is not defined. ........ r57331 | georg.brandl | 2007-08-23 11:11:33 -0700 (Thu, 23 Aug 2007) | 2 lines Use try-except-finally in contextlib. ........ r57343 | georg.brandl | 2007-08-23 13:35:00 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1697820: document that the old slice protocol is still used by builtin types. ........ r57345 | georg.brandl | 2007-08-23 13:40:01 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1573854: fix docs for sqlite3 cursor rowcount attr. ........ r57347 | georg.brandl | 2007-08-23 13:50:23 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1694833: fix imp.find_module() docs wrt. packages. ........ r57348 | georg.brandl | 2007-08-23 13:53:28 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1594966: fix misleading usage example ........ r57349 | georg.brandl | 2007-08-23 13:55:44 -0700 (Thu, 23 Aug 2007) | 2 lines Clarify wording a bit. ........ r57351 | georg.brandl | 2007-08-23 14:18:44 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1752332: httplib no longer uses socket.getaddrinfo(). ........ r57352 | georg.brandl | 2007-08-23 14:21:36 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1734111: document struct.Struct.size. ........ r57353 | georg.brandl | 2007-08-23 14:27:57 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1688564: document os.path.join's absolute path behavior in the docstring. ........ r57354 | georg.brandl | 2007-08-23 14:36:05 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1625381: clarify match vs search introduction. ........ r57355 | georg.brandl | 2007-08-23 14:42:54 -0700 (Thu, 23 Aug 2007) | 2 lines Bug #1758696: more info about descriptors. ........ r57357 | georg.brandl | 2007-08-23 14:55:57 -0700 (Thu, 23 Aug 2007) | 2 lines Patch #1779550: remove redundant code in logging. ........ r57378 | gregory.p.smith | 2007-08-23 22:11:38 -0700 (Thu, 23 Aug 2007) | 2 lines Fix bug 1725856. ........ r57382 | georg.brandl | 2007-08-23 23:10:01 -0700 (Thu, 23 Aug 2007) | 2 lines uuid creation is now threadsafe, backport from py3k rev. 57375. ........ r57389 | georg.brandl | 2007-08-24 04:47:37 -0700 (Fri, 24 Aug 2007) | 2 lines Bug #1765375: fix stripping of unwanted LDFLAGS. ........ r57391 | guido.van.rossum | 2007-08-24 07:53:14 -0700 (Fri, 24 Aug 2007) | 2 lines Fix silly typo in test name. ........ --- command/bdist_msi.py | 5 +++-- util.py | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 012d06ea..5225bed1 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -633,7 +633,8 @@ class bdist_msi (Command): def get_installer_filename(self, fullname): # Factored out to allow overriding in subclasses + plat = get_platform() installer_name = os.path.join(self.dist_dir, - "%s.win32-py%s.msi" % - (fullname, self.target_version)) + "%s.%s-py%s.msi" % + (fullname, plat, self.target_version)) return installer_name diff --git a/util.py b/util.py index 6f15ce8b..9aa85705 100644 --- a/util.py +++ b/util.py @@ -29,8 +29,27 @@ def get_platform (): irix-5.3 irix64-6.2 - For non-POSIX platforms, currently just returns 'sys.platform'. + Windows will return one of: + win-x86_64 (64bit Windows on x86_64 (AMD64)) + win-ia64 (64bit Windows on Itanium) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. """ + if os.name == 'nt': + # sniff sys.version for architecture. + prefix = " bit (" + i = string.find(sys.version, prefix) + if i == -1: + return sys.platform + j = string.find(sys.version, ")", i) + look = sys.version[i+len(prefix):j].lower() + if look=='amd64': + return 'win-x86_64' + if look=='itanium': + return 'win-ia64' + return sys.platform + if os.name != "posix" or not hasattr(os, 'uname'): # XXX what about the architecture? NT is Intel or Alpha, # Mac OS is M68k or PPC, etc. -- cgit v1.2.1 From 767edf21398f72156adcea6a418eba9150d06a20 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 29 Aug 2007 13:18:47 +0000 Subject: Fix failure in error handler -- exc[-1] -> exc.args[-1]. --- dir_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dir_util.py b/dir_util.py index a6c4416a..7dc1205c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -76,8 +76,8 @@ def mkpath (name, mode=0o777, verbose=0, dry_run=0): os.mkdir(head) created_dirs.append(head) except OSError as exc: - raise DistutilsFileError, \ - "could not create '%s': %s" % (head, exc[-1]) + raise DistutilsFileError( + "could not create '%s': %s" % (head, exc.args[-1])) _path_created[abs_head] = 1 return created_dirs -- cgit v1.2.1 From aabf8bf21c444cfae978e92e626ba7c0274a9f10 Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Thu, 30 Aug 2007 03:52:21 +0000 Subject: General cleanup, raise normalization in Lib/distutils. --- __init__.py | 2 - archive_util.py | 13 +- bcppcompiler.py | 17 +- ccompiler.py | 450 +++++++++++++++++++-------------------------- cmd.py | 166 +++++++---------- command/__init__.py | 2 - command/bdist.py | 34 +--- command/bdist_dumb.py | 33 +--- command/bdist_msi.py | 28 +-- command/bdist_rpm.py | 54 ++---- command/bdist_wininst.py | 39 ++-- command/build.py | 26 +-- command/build_clib.py | 97 ++++------ command/build_ext.py | 120 ++++-------- command/build_py.py | 116 ++++-------- command/build_scripts.py | 20 +- command/clean.py | 6 +- command/command_template | 20 +- command/config.py | 102 ++++------ command/install.py | 85 ++++----- command/install_data.py | 14 +- command/install_headers.py | 16 +- command/install_lib.py | 44 ++--- command/install_scripts.py | 16 +- command/sdist.py | 89 +++------ command/upload.py | 2 +- core.py | 24 +-- cygwinccompiler.py | 11 +- debug.py | 2 - dep_util.py | 6 +- dir_util.py | 17 +- dist.py | 65 ++----- emxccompiler.py | 9 +- errors.py | 2 - extension.py | 12 +- fancy_getopt.py | 137 +++++--------- file_util.py | 88 ++++----- filelist.py | 97 ++++------ log.py | 2 - msvccompiler.py | 202 +++++++++----------- mwerkscompiler.py | 11 +- spawn.py | 95 ++++------ sysconfig.py | 18 +- text_file.py | 147 +++++++-------- unixccompiler.py | 31 ++-- util.py | 20 +- version.py | 2 +- 47 files changed, 966 insertions(+), 1643 deletions(-) diff --git a/__init__.py b/__init__.py index 86ad44fe..f71c3adb 100644 --- a/__init__.py +++ b/__init__.py @@ -8,8 +8,6 @@ used from a setup script as setup (...) """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" # Distutils version diff --git a/archive_util.py b/archive_util.py index 059d5447..f3f65c6a 100644 --- a/archive_util.py +++ b/archive_util.py @@ -3,8 +3,6 @@ Utility functions for creating archive files (tarballs, zip files, that sort of thing).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os @@ -39,8 +37,8 @@ def make_tarball (base_name, base_dir, compress="gzip", 'bzip2': ['-f9']} if compress is not None and compress not in compress_ext.keys(): - raise ValueError, \ - "bad value for 'compress': must be None, 'gzip', or 'compress'" + raise ValueError( + "bad value for 'compress': must be None, 'gzip', or 'compress'") archive_name = base_name + ".tar" mkpath(os.path.dirname(archive_name), dry_run=dry_run) @@ -86,10 +84,9 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): except DistutilsExecError: # XXX really should distinguish between "couldn't find # external 'zip' command" and "zip failed". - raise DistutilsExecError, \ - ("unable to create zip file '%s': " + raise DistutilsExecError(("unable to create zip file '%s': " "could neither import the 'zipfile' module nor " - "find a standalone zip utility") % zip_filename + "find a standalone zip utility") % zip_filename) else: log.info("creating '%s' and adding '%s' to it", @@ -157,7 +154,7 @@ def make_archive (base_name, format, try: format_info = ARCHIVE_FORMATS[format] except KeyError: - raise ValueError, "unknown archive format '%s'" % format + raise ValueError("unknown archive format '%s'" % format) func = format_info[0] for (arg,val) in format_info[1]: diff --git a/bcppcompiler.py b/bcppcompiler.py index b6a3bf62..1ab76c59 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,8 +11,6 @@ for the Borland C++ compiler. # someone should sit down and factor out the common code as # WindowsCCompiler! --GPW -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" @@ -116,7 +114,7 @@ class BCPPCompiler(CCompiler) : try: self.spawn (["brcc32", "-fo", obj, src]) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) continue # the 'for' loop # The next two are both for the real compiler. @@ -140,7 +138,7 @@ class BCPPCompiler(CCompiler) : [input_opt, output_opt] + extra_postargs + [src]) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) return objects @@ -165,7 +163,7 @@ class BCPPCompiler(CCompiler) : try: self.spawn ([self.lib] + lib_args) except DistutilsExecError as msg: - raise LibError, msg + raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) @@ -299,7 +297,7 @@ class BCPPCompiler(CCompiler) : try: self.spawn ([self.linker] + ld_args) except DistutilsExecError as msg: - raise LinkError, msg + raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) @@ -345,9 +343,8 @@ class BCPPCompiler(CCompiler) : # use normcase to make sure '.rc' is really '.rc' and not '.RC' (base, ext) = os.path.splitext (os.path.normcase(src_name)) if ext not in (self.src_extensions + ['.rc','.res']): - raise UnknownFileError, \ - "unknown file type '%s' (from '%s')" % \ - (ext, src_name) + raise UnknownFileError("unknown file type '%s' (from '%s')" % \ + (ext, src_name)) if strip_dir: base = os.path.basename (base) if ext == '.res': @@ -393,6 +390,6 @@ class BCPPCompiler(CCompiler) : self.spawn(pp_args) except DistutilsExecError as msg: print(msg) - raise CompileError, msg + raise CompileError(msg) # preprocess() diff --git a/ccompiler.py b/ccompiler.py index d4f4adea..c33e5ab4 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -3,12 +3,9 @@ Contains CCompiler, an abstract base class that defines the interface for the Distutils compiler abstraction model.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, re -from types import * from copy import copy from distutils.errors import * from distutils.spawn import spawn @@ -88,11 +85,7 @@ class CCompiler: } language_order = ["c++", "objc", "c"] - def __init__ (self, - verbose=0, - dry_run=0, - force=0): - + def __init__(self, verbose=0, dry_run=0, force=0): self.dry_run = dry_run self.force = force self.verbose = verbose @@ -128,11 +121,7 @@ class CCompiler: for key in self.executables.keys(): self.set_executable(key, self.executables[key]) - # __init__ () - - - def set_executables (self, **args): - + def set_executables(self, **kwargs): """Define the executables (and options for them) that will be run to perform the various stages of compilation. The exact set of executables that may be specified here depends on the compiler @@ -158,14 +147,11 @@ class CCompiler: # discovered at run-time, since there are many different ways to do # basically the same things with Unix C compilers. - for key in args.keys(): + for key, value in kwargs.items(): if key not in self.executables: - raise ValueError, \ - "unknown executable '%s' for class %s" % \ - (key, self.__class__.__name__) - self.set_executable(key, args[key]) - - # set_executables () + raise ValueError("unknown executable '%s' for class %s" % \ + (key, self.__class__.__name__)) + self.set_executable(key, value) def set_executable(self, key, value): if isinstance(value, basestring): @@ -173,37 +159,32 @@ class CCompiler: else: setattr(self, key, value) - - def _find_macro (self, name): + def _find_macro(self, name): i = 0 for defn in self.macros: if defn[0] == name: return i - i = i + 1 - + i += 1 return None - - def _check_macro_definitions (self, definitions): + def _check_macro_definitions(self, definitions): """Ensures that every element of 'definitions' is a valid macro definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do nothing if all definitions are OK, raise TypeError otherwise. """ for defn in definitions: - if not (type (defn) is TupleType and - (len (defn) == 1 or - (len (defn) == 2 and - (isinstance (defn[1], basestring) or defn[1] is None))) and + if not (isinstance(defn, tuple) and + (len(defn) in (1, 2) and + (isinstance (defn[1], basestring) or defn[1] is None)) and isinstance (defn[0], basestring)): - raise TypeError, \ - ("invalid macro definition '%s': " % defn) + \ + raise TypeError(("invalid macro definition '%s': " % defn) + \ "must be tuple (string,), (string, string), or " + \ - "(string, None)" + "(string, None)") # -- Bookkeeping methods ------------------------------------------- - def define_macro (self, name, value=None): + def define_macro(self, name, value=None): """Define a preprocessor macro for all compilations driven by this compiler object. The optional parameter 'value' should be a string; if it is not supplied, then the macro will be defined @@ -216,11 +197,9 @@ class CCompiler: if i is not None: del self.macros[i] - defn = (name, value) - self.macros.append (defn) + self.macros.append((name, value)) - - def undefine_macro (self, name): + def undefine_macro(self, name): """Undefine a preprocessor macro for all compilations driven by this compiler object. If the same macro is defined by 'define_macro()' and undefined by 'undefine_macro()' the last call @@ -236,18 +215,17 @@ class CCompiler: del self.macros[i] undefn = (name,) - self.macros.append (undefn) - + self.macros.append(undefn) - def add_include_dir (self, dir): + def add_include_dir(self, dir): """Add 'dir' to the list of directories that will be searched for header files. The compiler is instructed to search directories in the order in which they are supplied by successive calls to 'add_include_dir()'. """ - self.include_dirs.append (dir) + self.include_dirs.append(dir) - def set_include_dirs (self, dirs): + def set_include_dirs(self, dirs): """Set the list of directories that will be searched to 'dirs' (a list of strings). Overrides any preceding calls to 'add_include_dir()'; subsequence calls to 'add_include_dir()' add @@ -255,10 +233,9 @@ class CCompiler: any list of standard include directories that the compiler may search by default. """ - self.include_dirs = copy (dirs) - + self.include_dirs = copy(dirs) - def add_library (self, libname): + def add_library(self, libname): """Add 'libname' to the list of libraries that will be included in all links driven by this compiler object. Note that 'libname' should *not* be the name of a file containing a library, but the @@ -272,63 +249,60 @@ class CCompiler: names; the linker will be instructed to link against libraries as many times as they are mentioned. """ - self.libraries.append (libname) + self.libraries.append(libname) - def set_libraries (self, libnames): + def set_libraries(self, libnames): """Set the list of libraries to be included in all links driven by this compiler object to 'libnames' (a list of strings). This does not affect any standard system libraries that the linker may include by default. """ - self.libraries = copy (libnames) + self.libraries = copy(libnames) - - def add_library_dir (self, dir): + def add_library_dir(self, dir): """Add 'dir' to the list of directories that will be searched for libraries specified to 'add_library()' and 'set_libraries()'. The linker will be instructed to search for libraries in the order they are supplied to 'add_library_dir()' and/or 'set_library_dirs()'. """ - self.library_dirs.append (dir) + self.library_dirs.append(dir) - def set_library_dirs (self, dirs): + def set_library_dirs(self, dirs): """Set the list of library search directories to 'dirs' (a list of strings). This does not affect any standard library search path that the linker may search by default. """ - self.library_dirs = copy (dirs) - + self.library_dirs = copy(dirs) - def add_runtime_library_dir (self, dir): + def add_runtime_library_dir(self, dir): """Add 'dir' to the list of directories that will be searched for shared libraries at runtime. """ - self.runtime_library_dirs.append (dir) + self.runtime_library_dirs.append(dir) - def set_runtime_library_dirs (self, dirs): + def set_runtime_library_dirs(self, dirs): """Set the list of directories to search for shared libraries at runtime to 'dirs' (a list of strings). This does not affect any standard search path that the runtime linker may search by default. """ - self.runtime_library_dirs = copy (dirs) - + self.runtime_library_dirs = copy(dirs) - def add_link_object (self, object): + def add_link_object(self, object): """Add 'object' to the list of object files (or analogues, such as explicitly named library files or the output of "resource compilers") to be included in every link driven by this compiler object. """ - self.objects.append (object) + self.objects.append(object) - def set_link_objects (self, objects): + def set_link_objects(self, objects): """Set the list of object files (or analogues) to be included in every link to 'objects'. This does not affect any standard object files that the linker may include by default (such as system libraries). """ - self.objects = copy (objects) + self.objects = copy(objects) # -- Private utility methods -------------------------------------- @@ -345,29 +319,28 @@ class CCompiler: if outdir is None: outdir = self.output_dir elif not isinstance(outdir, basestring): - raise TypeError, "'output_dir' must be a string or None" + raise TypeError("'output_dir' must be a string or None") if macros is None: macros = self.macros - elif type(macros) is ListType: + elif isinstance(macros, list): macros = macros + (self.macros or []) else: - raise TypeError, "'macros' (if supplied) must be a list of tuples" + raise TypeError("'macros' (if supplied) must be a list of tuples") if incdirs is None: incdirs = self.include_dirs - elif type(incdirs) in (ListType, TupleType): + elif isinstance(incdirs, (list, tuple)): incdirs = list(incdirs) + (self.include_dirs or []) else: - raise TypeError, \ - "'include_dirs' (if supplied) must be a list of strings" + raise TypeError( + "'include_dirs' (if supplied) must be a list of strings") if extra is None: extra = [] # Get the list of expected output (object) files - objects = self.object_filenames(sources, - strip_dir=0, + objects = self.object_filenames(sources, strip_dir=0, output_dir=outdir) assert len(objects) == len(sources) @@ -430,7 +403,7 @@ class CCompiler: cc_args[:0] = before return cc_args - def _fix_compile_args (self, output_dir, macros, include_dirs): + def _fix_compile_args(self, output_dir, macros, include_dirs): """Typecheck and fix-up some of the arguments to the 'compile()' method, and return fixed-up values. Specifically: if 'output_dir' is None, replaces it with 'self.output_dir'; ensures that 'macros' @@ -443,28 +416,25 @@ class CCompiler: if output_dir is None: output_dir = self.output_dir elif not isinstance(output_dir, basestring): - raise TypeError, "'output_dir' must be a string or None" + raise TypeError("'output_dir' must be a string or None") if macros is None: macros = self.macros - elif type (macros) is ListType: + elif isinstance(macros, list): macros = macros + (self.macros or []) else: - raise TypeError, "'macros' (if supplied) must be a list of tuples" + raise TypeError("'macros' (if supplied) must be a list of tuples") if include_dirs is None: include_dirs = self.include_dirs - elif type (include_dirs) in (ListType, TupleType): - include_dirs = list (include_dirs) + (self.include_dirs or []) + elif isinstance(include_dirs, (list, tuple)): + include_dirs = list(include_dirs) + (self.include_dirs or []) else: - raise TypeError, \ - "'include_dirs' (if supplied) must be a list of strings" + raise TypeError( + "'include_dirs' (if supplied) must be a list of strings") return output_dir, macros, include_dirs - # _fix_compile_args () - - def _prep_compile(self, sources, output_dir, depends=None): """Decide which souce files must be recompiled. @@ -511,29 +481,25 @@ class CCompiler: return objects, skip_source - # _prep_compile () - - def _fix_object_args (self, objects, output_dir): + def _fix_object_args(self, objects, output_dir): """Typecheck and fix up some arguments supplied to various methods. Specifically: ensure that 'objects' is a list; if output_dir is None, replace with self.output_dir. Return fixed versions of 'objects' and 'output_dir'. """ - if type (objects) not in (ListType, TupleType): - raise TypeError, \ - "'objects' must be a list or tuple of strings" - objects = list (objects) + if not isinstance(objects, (list, tuple)): + raise TypeError("'objects' must be a list or tuple of strings") + objects = list(objects) if output_dir is None: output_dir = self.output_dir elif not isinstance(output_dir, basestring): - raise TypeError, "'output_dir' must be a string or None" + raise TypeError("'output_dir' must be a string or None") return (objects, output_dir) - - def _fix_lib_args (self, libraries, library_dirs, runtime_library_dirs): + def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs): """Typecheck and fix up some of the arguments supplied to the 'link_*' methods. Specifically: ensure that all arguments are lists, and augment them with their permanent versions @@ -542,41 +508,37 @@ class CCompiler: """ if libraries is None: libraries = self.libraries - elif type (libraries) in (ListType, TupleType): + elif isinstance(libraries, (list, tuple)): libraries = list (libraries) + (self.libraries or []) else: - raise TypeError, \ - "'libraries' (if supplied) must be a list of strings" + raise TypeError( + "'libraries' (if supplied) must be a list of strings") if library_dirs is None: library_dirs = self.library_dirs - elif type (library_dirs) in (ListType, TupleType): + elif isinstance(library_dirs, (list, tuple)): library_dirs = list (library_dirs) + (self.library_dirs or []) else: - raise TypeError, \ - "'library_dirs' (if supplied) must be a list of strings" + raise TypeError( + "'library_dirs' (if supplied) must be a list of strings") if runtime_library_dirs is None: runtime_library_dirs = self.runtime_library_dirs - elif type (runtime_library_dirs) in (ListType, TupleType): - runtime_library_dirs = (list (runtime_library_dirs) + + elif isinstance(runtime_library_dirs, (list, tuple)): + runtime_library_dirs = (list(runtime_library_dirs) + (self.runtime_library_dirs or [])) else: - raise TypeError, \ - "'runtime_library_dirs' (if supplied) " + \ - "must be a list of strings" + raise TypeError("'runtime_library_dirs' (if supplied) " + "must be a list of strings") return (libraries, library_dirs, runtime_library_dirs) - # _fix_lib_args () - - - def _need_link (self, objects, output_file): + def _need_link(self, objects, output_file): """Return true if we need to relink the files listed in 'objects' to recreate 'output_file'. """ if self.force: - return 1 + return True else: if self.dry_run: newer = newer_group (objects, output_file, missing='newer') @@ -584,13 +546,11 @@ class CCompiler: newer = newer_group (objects, output_file) return newer - # _need_link () - - def detect_language (self, sources): + def detect_language(self, sources): """Detect the language of a given file, or list of files. Uses language_map, and language_order to do the job. """ - if type(sources) is not ListType: + if not isinstance(sources, list): sources = [sources] lang = None index = len(self.language_order) @@ -606,18 +566,12 @@ class CCompiler: pass return lang - # detect_language () # -- Worker methods ------------------------------------------------ # (must be implemented by subclasses) - def preprocess (self, - source, - output_file=None, - macros=None, - include_dirs=None, - extra_preargs=None, - extra_postargs=None): + def preprocess(self, source, output_file=None, macros=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): """Preprocess a single C/C++ source file, named in 'source'. Output will be written to file named 'output_file', or stdout if 'output_file' not supplied. 'macros' is a list of macro @@ -680,10 +634,8 @@ class CCompiler: Raises CompileError on failure. """ - # A concrete compiler class can either override this method # entirely or implement _compile(). - macros, objects, extra_postargs, pp_opts, build = \ self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) @@ -701,17 +653,12 @@ class CCompiler: def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): """Compile 'src' to product 'obj'.""" - # A concrete compiler class that does not override compile() # should implement _compile(). pass - def create_static_lib (self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): + def create_static_lib(self, objects, output_libname, output_dir=None, + debug=0, target_lang=None): """Link a bunch of stuff together to create a static library file. The "bunch of stuff" consists of the list of object files supplied as 'objects', the extra object files supplied to @@ -742,20 +689,20 @@ class CCompiler: SHARED_LIBRARY = "shared_library" EXECUTABLE = "executable" - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): """Link a bunch of stuff together to create an executable or shared library file. @@ -804,19 +751,19 @@ class CCompiler: # Old 'link_*()' methods, rewritten to use the new 'link()' method. - def link_shared_lib (self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): + def link_shared_lib(self, + objects, + output_libname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): self.link(CCompiler.SHARED_LIBRARY, objects, self.library_filename(output_libname, lib_type='shared'), output_dir, @@ -825,19 +772,19 @@ class CCompiler: extra_preargs, extra_postargs, build_temp, target_lang) - def link_shared_object (self, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): + def link_shared_object(self, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): self.link(CCompiler.SHARED_OBJECT, objects, output_filename, output_dir, libraries, library_dirs, runtime_library_dirs, @@ -845,17 +792,17 @@ class CCompiler: extra_preargs, extra_postargs, build_temp, target_lang) - def link_executable (self, - objects, - output_progname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - target_lang=None): + def link_executable(self, + objects, + output_progname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + target_lang=None): self.link(CCompiler.EXECUTABLE, objects, self.executable_filename(output_progname), output_dir, libraries, library_dirs, runtime_library_dirs, None, @@ -867,34 +814,30 @@ class CCompiler: # no appropriate default implementation so subclasses should # implement all of these. - def library_dir_option (self, dir): + def library_dir_option(self, dir): """Return the compiler option to add 'dir' to the list of directories searched for libraries. """ raise NotImplementedError - def runtime_library_dir_option (self, dir): + def runtime_library_dir_option(self, dir): """Return the compiler option to add 'dir' to the list of directories searched for runtime libraries. """ raise NotImplementedError - def library_option (self, lib): + def library_option(self, lib): """Return the compiler option to add 'dir' to the list of libraries linked into the shared library or executable. """ raise NotImplementedError - def has_function(self, funcname, - includes=None, - include_dirs=None, - libraries=None, - library_dirs=None): + def has_function(self, funcname, includes=None, include_dirs=None, + libraries=None, library_dirs=None): """Return a boolean indicating whether funcname is supported on the current platform. The optional arguments can be used to augment the compilation environment. """ - # this can't be included at module scope because it tries to # import math which might not be available at that point - maybe # the necessary logic should just be inlined? @@ -982,8 +925,8 @@ main (int argc, char **argv) { base = os.path.splitdrive(base)[1] # Chop off the drive base = base[os.path.isabs(base):] # If abs, chop off leading / if ext not in self.src_extensions: - raise UnknownFileError, \ - "unknown file type '%s' (from '%s')" % (ext, src_name) + raise UnknownFileError( + "unknown file type '%s' (from '%s')" % (ext, src_name)) if strip_dir: base = os.path.basename(base) obj_names.append(os.path.join(output_dir, @@ -993,24 +936,25 @@ main (int argc, char **argv) { def shared_object_filename(self, basename, strip_dir=0, output_dir=''): assert output_dir is not None if strip_dir: - basename = os.path.basename (basename) + basename = os.path.basename(basename) return os.path.join(output_dir, basename + self.shared_lib_extension) def executable_filename(self, basename, strip_dir=0, output_dir=''): assert output_dir is not None if strip_dir: - basename = os.path.basename (basename) + basename = os.path.basename(basename) return os.path.join(output_dir, basename + (self.exe_extension or '')) def library_filename(self, libname, lib_type='static', # or 'shared' strip_dir=0, output_dir=''): assert output_dir is not None if lib_type not in ("static", "shared", "dylib"): - raise ValueError, "'lib_type' must be \"static\", \"shared\" or \"dylib\"" + raise ValueError( + "'lib_type' must be \"static\", \"shared\" or \"dylib\"") fmt = getattr(self, lib_type + "_lib_format") ext = getattr(self, lib_type + "_lib_extension") - dir, base = os.path.split (libname) + dir, base = os.path.split(libname) filename = fmt % (base, ext) if strip_dir: dir = '' @@ -1020,31 +964,28 @@ main (int argc, char **argv) { # -- Utility methods ----------------------------------------------- - def announce (self, msg, level=1): + def announce(self, msg, level=1): log.debug(msg) - def debug_print (self, msg): + def debug_print(self, msg): from distutils.debug import DEBUG if DEBUG: print(msg) - def warn (self, msg): - sys.stderr.write ("warning: %s\n" % msg) + def warn(self, msg): + sys.stderr.write("warning: %s\n" % msg) - def execute (self, func, args, msg=None, level=1): + def execute(self, func, args, msg=None, level=1): execute(func, args, msg, self.dry_run) - def spawn (self, cmd): - spawn (cmd, dry_run=self.dry_run) + def spawn(self, cmd): + spawn(cmd, dry_run=self.dry_run) - def move_file (self, src, dst): - return move_file (src, dst, dry_run=self.dry_run) + def move_file(self, src, dst): + return move_file(src, dst, dry_run=self.dry_run) - def mkpath (self, name, mode=0o777): - mkpath (name, mode, self.dry_run) - - -# class CCompiler + def mkpath(self, name, mode=0o777): + mkpath(name, mode, self.dry_run) # Map a sys.platform/os.name ('posix', 'nt') to the default compiler @@ -1068,16 +1009,14 @@ _default_compilers = ( ) def get_default_compiler(osname=None, platform=None): + """Determine the default compiler to use for the given platform. - """ Determine the default compiler to use for the given platform. - - osname should be one of the standard Python OS names (i.e. the - ones returned by os.name) and platform the common value - returned by sys.platform for the platform in question. - - The default values are os.name and sys.platform in case the - parameters are not given. + osname should be one of the standard Python OS names (i.e. the + ones returned by os.name) and platform the common value + returned by sys.platform for the platform in question. + The default values are os.name and sys.platform in case the + parameters are not given. """ if osname is None: osname = os.name @@ -1126,11 +1065,7 @@ def show_compilers(): pretty_printer.print_help("List of available compilers:") -def new_compiler (plat=None, - compiler=None, - verbose=0, - dry_run=0, - force=0): +def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0): """Generate an instance of some CCompiler subclass for the supplied platform/compiler combination. 'plat' defaults to 'os.name' (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler @@ -1153,7 +1088,7 @@ def new_compiler (plat=None, msg = "don't know how to compile C/C++ code on platform '%s'" % plat if compiler is not None: msg = msg + " with '%s' compiler" % compiler - raise DistutilsPlatformError, msg + raise DistutilsPlatformError(msg) try: module_name = "distutils." + module_name @@ -1161,21 +1096,21 @@ def new_compiler (plat=None, module = sys.modules[module_name] klass = vars(module)[class_name] except ImportError: - raise DistutilsModuleError, \ + raise DistutilsModuleError( "can't compile C/C++ code: unable to load module '%s'" % \ - module_name + module_name) except KeyError: - raise DistutilsModuleError, \ - ("can't compile C/C++ code: unable to find class '%s' " + - "in module '%s'") % (class_name, module_name) + raise DistutilsModuleError( + "can't compile C/C++ code: unable to find class '%s' " + "in module '%s'" % (class_name, module_name)) # XXX The None is necessary to preserve backwards compatibility # with classes that expect verbose to be the first positional # argument. - return klass (None, dry_run, force) + return klass(None, dry_run, force) -def gen_preprocess_options (macros, include_dirs): +def gen_preprocess_options(macros, include_dirs): """Generate C pre-processor options (-D, -U, -I) as used by at least two types of compilers: the typical Unix compiler and Visual C++. 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,) @@ -1196,35 +1131,29 @@ def gen_preprocess_options (macros, include_dirs): # redundancies like this should probably be the province of # CCompiler, since the data structures used are inherited from it # and therefore common to all CCompiler classes. - pp_opts = [] for macro in macros: - - if not (type (macro) is TupleType and - 1 <= len (macro) <= 2): - raise TypeError, \ - ("bad macro definition '%s': " + - "each element of 'macros' list must be a 1- or 2-tuple") % \ - macro - - if len (macro) == 1: # undefine this macro - pp_opts.append ("-U%s" % macro[0]) - elif len (macro) == 2: + if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2): + raise TypeError( + "bad macro definition '%s': " + "each element of 'macros' list must be a 1- or 2-tuple" + % macro) + + if len(macro) == 1: # undefine this macro + pp_opts.append("-U%s" % macro[0]) + elif len(macro) == 2: if macro[1] is None: # define with no explicit value - pp_opts.append ("-D%s" % macro[0]) + pp_opts.append("-D%s" % macro[0]) else: # XXX *don't* need to be clever about quoting the # macro value here, because we're going to avoid the # shell at all costs when we spawn the command! - pp_opts.append ("-D%s=%s" % macro) + pp_opts.append("-D%s=%s" % macro) for dir in include_dirs: - pp_opts.append ("-I%s" % dir) - + pp_opts.append("-I%s" % dir) return pp_opts -# gen_preprocess_options () - def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): """Generate linker options for searching library directories and @@ -1236,14 +1165,14 @@ def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): lib_opts = [] for dir in library_dirs: - lib_opts.append (compiler.library_dir_option (dir)) + lib_opts.append(compiler.library_dir_option(dir)) for dir in runtime_library_dirs: - opt = compiler.runtime_library_dir_option (dir) - if type(opt) is ListType: + opt = compiler.runtime_library_dir_option(dir) + if isinstance(opt, list): lib_opts = lib_opts + opt else: - lib_opts.append (opt) + lib_opts.append(opt) # XXX it's important that we *not* remove redundant library mentions! # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to @@ -1252,17 +1181,14 @@ def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): # pretty nasty way to arrange your C code. for lib in libraries: - (lib_dir, lib_name) = os.path.split (lib) + (lib_dir, lib_name) = os.path.split(lib) if lib_dir: - lib_file = compiler.find_library_file ([lib_dir], lib_name) + lib_file = compiler.find_library_file([lib_dir], lib_name) if lib_file: - lib_opts.append (lib_file) + lib_opts.append(lib_file) else: - compiler.warn ("no library file corresponding to " - "'%s' found (skipping)" % lib) + compiler.warn("no library file corresponding to " + "'%s' found (skipping)" % lib) else: - lib_opts.append (compiler.library_option (lib)) - + lib_opts.append(compiler.library_option (lib)) return lib_opts - -# gen_lib_options () diff --git a/cmd.py b/cmd.py index b2c952c3..66940f77 100644 --- a/cmd.py +++ b/cmd.py @@ -4,8 +4,6 @@ Provides the Command class, the base class for the command classes in the distutils.command package. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, re @@ -48,7 +46,7 @@ class Command: # -- Creation/initialization methods ------------------------------- - def __init__ (self, dist): + def __init__(self, dist): """Create and initialize a new Command object. Most importantly, invokes the 'initialize_options()' method, which is the real initializer and depends on the actual command being @@ -58,9 +56,9 @@ class Command: from distutils.dist import Distribution if not isinstance(dist, Distribution): - raise TypeError, "dist must be a Distribution instance" + raise TypeError("dist must be a Distribution instance") if self.__class__ is Command: - raise RuntimeError, "Command is an abstract class" + raise RuntimeError("Command is an abstract class") self.distribution = dist self.initialize_options() @@ -95,11 +93,8 @@ class Command: # always calls 'finalize_options()', to respect/update it. self.finalized = 0 - # __init__ () - # XXX A more explicit way to customize dry_run would be better. - def __getattr__ (self, attr): if attr == 'dry_run': myval = getattr(self, "_" + attr) @@ -108,15 +103,13 @@ class Command: else: return myval else: - raise AttributeError, attr - + raise AttributeError(attr) def ensure_finalized (self): if not self.finalized: self.finalize_options() self.finalized = 1 - # Subclasses must define: # initialize_options() # provide default values for all options; may be customized by @@ -130,7 +123,7 @@ class Command: # run the command: do whatever it is we're here to do, # controlled by the command's various option values - def initialize_options (self): + def initialize_options(self): """Set default values for all the options that this command supports. Note that these defaults may be overridden by other commands, by the setup script, by config files, or by the @@ -140,10 +133,10 @@ class Command: This method must be implemented by all command classes. """ - raise RuntimeError, \ - "abstract method -- subclass %s must override" % self.__class__ + raise RuntimeError("abstract method -- subclass %s must override" + % self.__class__) - def finalize_options (self): + def finalize_options(self): """Set final values for all the options that this command supports. This is always called as late as possible, ie. after any option assignments from the command-line or from other commands have been @@ -154,11 +147,11 @@ class Command: This method must be implemented by all command classes. """ - raise RuntimeError, \ - "abstract method -- subclass %s must override" % self.__class__ + raise RuntimeError("abstract method -- subclass %s must override" + % self.__class__) - def dump_options (self, header=None, indent=""): + def dump_options(self, header=None, indent=""): from distutils.fancy_getopt import longopt_xlate if header is None: header = "command options for '%s':" % self.get_command_name() @@ -172,7 +165,7 @@ class Command: print(indent + "%s = %s" % (option, value)) - def run (self): + def run(self): """A command's raison d'etre: carry out the action it exists to perform, controlled by the options initialized in 'initialize_options()', customized by other commands, the setup @@ -183,16 +176,16 @@ class Command: This method must be implemented by all command classes. """ - raise RuntimeError, \ - "abstract method -- subclass %s must override" % self.__class__ + raise RuntimeError("abstract method -- subclass %s must override" + % self.__class__) - def announce (self, msg, level=1): + def announce(self, msg, level=1): """If the current verbosity level is of greater than or equal to 'level' print 'msg' to stdout. """ log.log(level, msg) - def debug_print (self, msg): + def debug_print(self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the DISTUTILS_DEBUG environment variable) flag is true. """ @@ -202,7 +195,6 @@ class Command: sys.stdout.flush() - # -- Option validation methods ------------------------------------- # (these are very handy in writing the 'finalize_options()' method) # @@ -216,23 +208,23 @@ class Command: # and they can be guaranteed that thereafter, self.foo will be # a list of strings. - def _ensure_stringlike (self, option, what, default=None): + def _ensure_stringlike(self, option, what, default=None): val = getattr(self, option) if val is None: setattr(self, option, default) return default elif not isinstance(val, basestring): - raise DistutilsOptionError, \ - "'%s' must be a %s (got `%s`)" % (option, what, val) + raise DistutilsOptionError("'%s' must be a %s (got `%s`)" + % (option, what, val)) return val - def ensure_string (self, option, default=None): + def ensure_string(self, option, default=None): """Ensure that 'option' is a string; if not defined, set it to 'default'. """ self._ensure_stringlike(option, "string", default) - def ensure_string_list (self, option): + def ensure_string_list(self, option): """Ensure that 'option' is a list of strings. If 'option' is currently a string, we split it either on /,\s*/ or /\s+/, so "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become @@ -247,27 +239,26 @@ class Command: if isinstance(val, list): ok = all(isinstance(v, basestring) for v in val) else: - ok = 0 - + ok = False if not ok: - raise DistutilsOptionError, \ - "'%s' must be a list of strings (got %r)" % \ - (option, val) + raise DistutilsOptionError( + "'%s' must be a list of strings (got %r)" + % (option, val)) - def _ensure_tested_string (self, option, tester, - what, error_fmt, default=None): + def _ensure_tested_string(self, option, tester, what, error_fmt, + default=None): val = self._ensure_stringlike(option, what, default) if val is not None and not tester(val): - raise DistutilsOptionError, \ - ("error in '%s' option: " + error_fmt) % (option, val) + raise DistutilsOptionError(("error in '%s' option: " + error_fmt) + % (option, val)) - def ensure_filename (self, option): + def ensure_filename(self, option): """Ensure that 'option' is the name of an existing file.""" self._ensure_tested_string(option, os.path.isfile, "filename", "'%s' does not exist or is not a file") - def ensure_dirname (self, option): + def ensure_dirname(self, option): self._ensure_tested_string(option, os.path.isdir, "directory name", "'%s' does not exist or is not a directory") @@ -275,14 +266,13 @@ class Command: # -- Convenience methods for commands ------------------------------ - def get_command_name (self): + def get_command_name(self): if hasattr(self, 'command_name'): return self.command_name else: return self.__class__.__name__ - - def set_undefined_options (self, src_cmd, *option_pairs): + def set_undefined_options(self, src_cmd, *option_pairs): """Set the values of any "undefined" options from corresponding option values in some other command object. "Undefined" here means "is None", which is the convention used to indicate that an option @@ -296,18 +286,14 @@ class Command: 'src_option' in the 'src_cmd' command object, and copy it to 'dst_option' in the current command object". """ - # Option_pairs: list of (src_option, dst_option) tuples - src_cmd_obj = self.distribution.get_command_obj(src_cmd) src_cmd_obj.ensure_finalized() for (src_option, dst_option) in option_pairs: if getattr(self, dst_option) is None: - setattr(self, dst_option, - getattr(src_cmd_obj, src_option)) + setattr(self, dst_option, getattr(src_cmd_obj, src_option)) - - def get_finalized_command (self, command, create=1): + def get_finalized_command(self, command, create=1): """Wrapper around Distribution's 'get_command_obj()' method: find (create if necessary and 'create' is true) the command object for 'command', call its 'ensure_finalized()' method, and return the @@ -319,19 +305,18 @@ class Command: # XXX rename to 'get_reinitialized_command()'? (should do the # same in dist.py, if so) - def reinitialize_command (self, command, reinit_subcommands=0): - return self.distribution.reinitialize_command( - command, reinit_subcommands) + def reinitialize_command(self, command, reinit_subcommands=0): + return self.distribution.reinitialize_command(command, + reinit_subcommands) - def run_command (self, command): + def run_command(self, command): """Run some other command: uses the 'run_command()' method of Distribution, which creates and finalizes the command object if necessary and then invokes its 'run()' method. """ self.distribution.run_command(command) - - def get_sub_commands (self): + def get_sub_commands(self): """Determine the sub-commands that are relevant in the current distribution (ie., that need to be run). This is based on the 'sub_commands' class attribute: each tuple in that list may include @@ -347,62 +332,49 @@ class Command: # -- External world manipulation ----------------------------------- - def warn (self, msg): - sys.stderr.write("warning: %s: %s\n" % - (self.get_command_name(), msg)) - + def warn(self, msg): + sys.stderr.write("warning: %s: %s\n" % (self.get_command_name(), msg)) - def execute (self, func, args, msg=None, level=1): + def execute(self, func, args, msg=None, level=1): util.execute(func, args, msg, dry_run=self.dry_run) - - def mkpath (self, name, mode=0o777): + def mkpath(self, name, mode=0o777): dir_util.mkpath(name, mode, dry_run=self.dry_run) - - def copy_file (self, infile, outfile, - preserve_mode=1, preserve_times=1, link=None, level=1): + def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1, + link=None, level=1): """Copy a file respecting verbose, dry-run and force flags. (The former two default to whatever is in the Distribution object, and the latter defaults to false for commands that don't define it.)""" + return file_util.copy_file(infile, outfile, preserve_mode, + preserve_times, not self.force, link, + dry_run=self.dry_run) - return file_util.copy_file( - infile, outfile, - preserve_mode, preserve_times, - not self.force, - link, - dry_run=self.dry_run) - - - def copy_tree (self, infile, outfile, - preserve_mode=1, preserve_times=1, preserve_symlinks=0, - level=1): + def copy_tree (self, infile, outfile, preserve_mode=1, preserve_times=1, + preserve_symlinks=0, level=1): """Copy an entire directory tree respecting verbose, dry-run, and force flags. """ - return dir_util.copy_tree( - infile, outfile, - preserve_mode,preserve_times,preserve_symlinks, - not self.force, - dry_run=self.dry_run) + return dir_util.copy_tree(infile, outfile, preserve_mode, + preserve_times, preserve_symlinks, + not self.force, dry_run=self.dry_run) def move_file (self, src, dst, level=1): """Move a file respectin dry-run flag.""" - return file_util.move_file(src, dst, dry_run = self.dry_run) + return file_util.move_file(src, dst, dry_run=self.dry_run) - def spawn (self, cmd, search_path=1, level=1): + def spawn(self, cmd, search_path=1, level=1): """Spawn an external command respecting dry-run flag.""" from distutils.spawn import spawn - spawn(cmd, search_path, dry_run= self.dry_run) + spawn(cmd, search_path, dry_run=self.dry_run) - def make_archive (self, base_name, format, - root_dir=None, base_dir=None): - return archive_util.make_archive( - base_name, format, root_dir, base_dir, dry_run=self.dry_run) + def make_archive(self, base_name, format, root_dir=None, base_dir=None): + return archive_util.make_archive(base_name, format, root_dir, base_dir, + dry_run=self.dry_run) - def make_file (self, infiles, outfile, func, args, - exec_msg=None, skip_msg=None, level=1): + def make_file(self, infiles, outfile, func, args, + exec_msg=None, skip_msg=None, level=1): """Special case of 'execute()' for operations that process one or more input files and generate one output file. Works just like 'execute()', except the operation is skipped and a different @@ -412,8 +384,7 @@ class Command: timestamp checks. """ if exec_msg is None: - exec_msg = "generating %s from %s" % \ - (outfile, ', '.join(infiles)) + exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles)) if skip_msg is None: skip_msg = "skipping %s (inputs unchanged)" % outfile @@ -422,30 +393,25 @@ class Command: if isinstance(infiles, basestring): infiles = (infiles,) elif not isinstance(infiles, (list, tuple)): - raise TypeError, \ - "'infiles' must be a string, or a list or tuple of strings" + raise TypeError( + "'infiles' must be a string, or a list or tuple of strings") # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it if self.force or dep_util.newer_group (infiles, outfile): self.execute(func, args, exec_msg, level) - # Otherwise, print the "skip" message else: log.debug(skip_msg) - # make_file () - -# class Command - # XXX 'install_misc' class not currently used -- it was the base class for # both 'install_scripts' and 'install_data', but they outgrew it. It might # still be useful for 'install_headers', though, so I'm keeping it around # for the time being. -class install_misc (Command): +class install_misc(Command): """Common base class for installing some files in a subdirectory. Currently used by install_data and install_scripts. """ diff --git a/command/__init__.py b/command/__init__.py index 0888c271..add83f87 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -3,8 +3,6 @@ Package containing implementation of all the standard Distutils commands.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" __all__ = ['build', diff --git a/command/bdist.py b/command/bdist.py index d6897d2d..69c1b28f 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -3,22 +3,19 @@ Implements the Distutils 'bdist' command (create a built [binary] distribution).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os -from types import * from distutils.core import Command from distutils.errors import * from distutils.util import get_platform -def show_formats (): +def show_formats(): """Print list of available formats (arguments to "--format" option). """ from distutils.fancy_getopt import FancyGetopt - formats=[] + formats = [] for format in bdist.format_commands: formats.append(("formats=" + format, None, bdist.format_command[format][1])) @@ -26,7 +23,7 @@ def show_formats (): pretty_printer.print_help("List of available distribution formats:") -class bdist (Command): +class bdist(Command): description = "create a built (binary) distribution" @@ -84,17 +81,14 @@ class bdist (Command): } - def initialize_options (self): + def initialize_options(self): self.bdist_base = None self.plat_name = None self.formats = None self.dist_dir = None self.skip_build = 0 - # initialize_options() - - - def finalize_options (self): + def finalize_options(self): # have to finalize 'plat_name' before 'bdist_base' if self.plat_name is None: self.plat_name = get_platform() @@ -112,25 +106,21 @@ class bdist (Command): try: self.formats = [self.default_format[os.name]] except KeyError: - raise DistutilsPlatformError, \ - "don't know how to create built distributions " + \ - "on platform %s" % os.name + raise DistutilsPlatformError( + "don't know how to create built distributions " + "on platform %s" % os.name) if self.dist_dir is None: self.dist_dir = "dist" - # finalize_options() - - - def run (self): - + def run(self): # Figure out which sub-commands we need to run. commands = [] for format in self.formats: try: commands.append(self.format_command[format][0]) except KeyError: - raise DistutilsOptionError, "invalid format '%s'" % format + raise DistutilsOptionError("invalid format '%s'" % format) # Reinitialize and run each command. for i in range(len(self.formats)): @@ -144,7 +134,3 @@ class bdist (Command): if cmd_name in commands[i+1:]: sub_cmd.keep_temp = 1 self.run_command(cmd_name) - - # run() - -# class bdist diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index ccba0095..f8996176 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,8 +4,6 @@ Implements the Distutils 'bdist_dumb' command (create a "dumb" built distribution -- i.e., just an archive to be unpacked under $prefix or $exec_prefix).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os @@ -16,7 +14,7 @@ from distutils.errors import * from distutils.sysconfig import get_python_version from distutils import log -class bdist_dumb (Command): +class bdist_dumb(Command): description = "create a \"dumb\" built distribution" @@ -45,8 +43,7 @@ class bdist_dumb (Command): 'nt': 'zip', 'os2': 'zip' } - - def initialize_options (self): + def initialize_options(self): self.bdist_dir = None self.plat_name = None self.format = None @@ -55,11 +52,7 @@ class bdist_dumb (Command): self.skip_build = 0 self.relative = 0 - # initialize_options() - - - def finalize_options (self): - + def finalize_options(self): if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'dumb') @@ -68,19 +61,15 @@ class bdist_dumb (Command): try: self.format = self.default_format[os.name] except KeyError: - raise DistutilsPlatformError, \ - ("don't know how to create dumb built distributions " + - "on platform %s") % os.name + raise DistutilsPlatformError( + "don't know how to create dumb built distributions " + "on platform %s" % os.name) self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'), ('plat_name', 'plat_name')) - # finalize_options() - - - def run (self): - + def run(self): if not self.skip_build: self.run_command('build') @@ -108,8 +97,8 @@ class bdist_dumb (Command): else: if (self.distribution.has_ext_modules() and (install.install_base != install.install_platbase)): - raise DistutilsPlatformError, \ - ("can't make a dumb built distribution where " + raise DistutilsPlatformError( + "can't make a dumb built distribution where " "base and platbase are different (%s, %s)" % (repr(install.install_base), repr(install.install_platbase))) @@ -129,7 +118,3 @@ class bdist_dumb (Command): if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) - - # run() - -# class bdist_dumb diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 5225bed1..d313a508 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -81,7 +81,7 @@ class PyDialog(Dialog): Return the button, so that events can be associated""" return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next) -class bdist_msi (Command): +class bdist_msi(Command): description = "create a Microsoft Installer (.msi) binary distribution" @@ -114,7 +114,7 @@ class bdist_msi (Command): boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', 'skip-build'] - def initialize_options (self): + def initialize_options(self): self.bdist_dir = None self.keep_temp = 0 self.no_target_compile = 0 @@ -125,7 +125,7 @@ class bdist_msi (Command): self.install_script = None self.pre_install_script = None - def finalize_options (self): + def finalize_options(self): if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'msi') @@ -133,30 +133,29 @@ class bdist_msi (Command): if self.target_version: if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: - raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ - " option must be specified" % (short_version,) + raise DistutilsOptionError( + "target version can only be %s, or the '--skip_build'" + " option must be specified" % (short_version,)) else: self.target_version = short_version self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) if self.pre_install_script: - raise DistutilsOptionError, "the pre-install-script feature is not yet implemented" + raise DistutilsOptionError( + "the pre-install-script feature is not yet implemented") if self.install_script: for script in self.distribution.scripts: if self.install_script == os.path.basename(script): break else: - raise DistutilsOptionError, \ - "install_script '%s' not found in scripts" % \ - self.install_script + raise DistutilsOptionError( + "install_script '%s' not found in scripts" + % self.install_script) self.install_script_key = None - # finalize_options() - - def run (self): + def run(self): if not self.skip_build: self.run_command('build') @@ -263,7 +262,8 @@ class bdist_msi (Command): key = dir.add_file(file) if file==self.install_script: if self.install_script_key: - raise DistutilsOptionError, "Multiple files with name %s" % file + raise DistutilsOptionError( + "Multiple files with name %s" % file) self.install_script_key = '[#%s]' % key cab.commit(db) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index ef2bdfd8..72f74f9c 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -3,13 +3,10 @@ Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os import glob -from types import * from distutils.core import Command from distutils.debug import DEBUG from distutils.util import get_platform @@ -18,7 +15,7 @@ from distutils.errors import * from distutils.sysconfig import get_python_version from distutils import log -class bdist_rpm (Command): +class bdist_rpm(Command): description = "create an RPM distribution" @@ -136,7 +133,7 @@ class bdist_rpm (Command): 'rpm2-mode': 'rpm3-mode'} - def initialize_options (self): + def initialize_options(self): self.bdist_base = None self.rpm_base = None self.dist_dir = None @@ -180,15 +177,12 @@ class bdist_rpm (Command): self.force_arch = None - # initialize_options() - - - def finalize_options (self): + def finalize_options(self): self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) if self.rpm_base is None: if not self.rpm3_mode: - raise DistutilsOptionError, \ - "you must specify --rpm-base in RPM 2 mode" + raise DistutilsOptionError( + "you must specify --rpm-base in RPM 2 mode") self.rpm_base = os.path.join(self.bdist_base, "rpm") if self.python is None: @@ -197,16 +191,15 @@ class bdist_rpm (Command): else: self.python = "python" elif self.fix_python: - raise DistutilsOptionError, \ - "--python and --fix-python are mutually exclusive options" + raise DistutilsOptionError( + "--python and --fix-python are mutually exclusive options") if os.name != 'posix': - raise DistutilsPlatformError, \ - ("don't know how to create RPM " + raise DistutilsPlatformError("don't know how to create RPM " "distributions on platform %s" % os.name) if self.binary_only and self.source_only: - raise DistutilsOptionError, \ - "cannot supply both '--source-only' and '--binary-only'" + raise DistutilsOptionError( + "cannot supply both '--source-only' and '--binary-only'") # don't pass CFLAGS to pure python distributions if not self.distribution.has_ext_modules(): @@ -215,16 +208,14 @@ class bdist_rpm (Command): self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) self.finalize_package_data() - # finalize_options() - - def finalize_package_data (self): + def finalize_package_data(self): self.ensure_string('group', "Development/Libraries") self.ensure_string('vendor', "%s <%s>" % (self.distribution.get_contact(), self.distribution.get_contact_email())) self.ensure_string('packager') self.ensure_string_list('doc_files') - if type(self.doc_files) is ListType: + if isinstance(self.doc_files, list): for readme in ('README', 'README.txt'): if os.path.exists(readme) and readme not in self.doc_files: self.doc_files.append(readme) @@ -261,11 +252,8 @@ class bdist_rpm (Command): self.ensure_string_list('obsoletes') self.ensure_string('force_arch') - # finalize_package_data () - - - def run (self): + def run(self): if DEBUG: print("before _get_package_data():") print("vendor =", self.vendor) @@ -315,9 +303,8 @@ class bdist_rpm (Command): if os.path.exists(self.icon): self.copy_file(self.icon, source_dir) else: - raise DistutilsFileError, \ - "icon file '%s' does not exist" % self.icon - + raise DistutilsFileError( + "icon file '%s' does not exist" % self.icon) # build package log.info("building RPMs") @@ -350,7 +337,7 @@ class bdist_rpm (Command): out = os.popen(q_cmd) binary_rpms = [] source_rpm = None - while 1: + while True: line = out.readline() if not line: break @@ -378,7 +365,6 @@ class bdist_rpm (Command): rpm = os.path.join(rpm_dir['RPMS'], rpm) if os.path.exists(rpm): self.move_file(rpm, self.dist_dir) - # run() def _dist_path(self, path): return os.path.join(self.dist_dir, os.path.basename(path)) @@ -438,7 +424,7 @@ class bdist_rpm (Command): 'Obsoletes', ): val = getattr(self, field.lower()) - if type(val) is ListType: + if isinstance(val, list): spec_file.append('%s: %s' % (field, ' '.join(val))) elif val is not None: spec_file.append('%s: %s' % (field, val)) @@ -536,8 +522,6 @@ class bdist_rpm (Command): return spec_file - # _make_spec_file () - def _format_changelog(self, changelog): """Format the changelog correctly and convert it to a list of strings """ @@ -558,7 +542,3 @@ class bdist_rpm (Command): del new_changelog[0] return new_changelog - - # _format_changelog() - -# class bdist_rpm diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 55d5d7e1..249b74c1 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -3,8 +3,6 @@ Implements the Distutils 'bdist_wininst' command: create a windows installer exe-program.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os @@ -15,7 +13,7 @@ from distutils.errors import * from distutils.sysconfig import get_python_version from distutils import log -class bdist_wininst (Command): +class bdist_wininst(Command): description = "create an executable installer for MS Windows" @@ -52,7 +50,7 @@ class bdist_wininst (Command): boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', 'skip-build'] - def initialize_options (self): + def initialize_options(self): self.bdist_dir = None self.keep_temp = 0 self.no_target_compile = 0 @@ -65,10 +63,8 @@ class bdist_wininst (Command): self.install_script = None self.pre_install_script = None - # initialize_options() - - def finalize_options (self): + def finalize_options(self): if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'wininst') @@ -77,9 +73,9 @@ class bdist_wininst (Command): if not self.skip_build and self.distribution.has_ext_modules(): short_version = get_python_version() if self.target_version and self.target_version != short_version: - raise DistutilsOptionError, \ + raise DistutilsOptionError( "target version can only be %s, or the '--skip_build'" \ - " option must be specified" % (short_version,) + " option must be specified" % (short_version,)) self.target_version = short_version self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) @@ -89,13 +85,11 @@ class bdist_wininst (Command): if self.install_script == os.path.basename(script): break else: - raise DistutilsOptionError, \ - "install_script '%s' not found in scripts" % \ - self.install_script - # finalize_options() + raise DistutilsOptionError( + "install_script '%s' not found in scripts" + % self.install_script) - - def run (self): + def run(self): if (sys.platform != "win32" and (self.distribution.has_ext_modules() or self.distribution.has_c_libraries())): @@ -175,11 +169,8 @@ class bdist_wininst (Command): if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) - # run() - - def get_inidata (self): + def get_inidata(self): # Return data describing the installation. - lines = [] metadata = self.distribution.metadata @@ -222,9 +213,7 @@ class bdist_wininst (Command): lines.append("build_info=%s" % build_info) return "\n".join(lines) - # get_inidata() - - def create_exe (self, arcname, fullname, bitmap=None): + def create_exe(self, arcname, fullname, bitmap=None): import struct self.mkpath(self.dist_dir) @@ -272,8 +261,6 @@ class bdist_wininst (Command): file.write(header) file.write(open(arcname, "rb").read()) - # create_exe() - def get_installer_filename(self, fullname): # Factored out to allow overriding in subclasses if self.target_version: @@ -286,9 +273,8 @@ class bdist_wininst (Command): installer_name = os.path.join(self.dist_dir, "%s.win32.exe" % fullname) return installer_name - # get_installer_filename() - def get_exe_bytes (self): + def get_exe_bytes(self): from distutils.msvccompiler import get_build_version # If a target-version other than the current version has been # specified, then using the MSVC version from *this* build is no good. @@ -320,4 +306,3 @@ class bdist_wininst (Command): # used for python. XXX What about mingw, borland, and so on? filename = os.path.join(directory, "wininst-%s.exe" % bv) return open(filename, "rb").read() -# class bdist_wininst diff --git a/command/build.py b/command/build.py index 9ae0a292..1f2ce062 100644 --- a/command/build.py +++ b/command/build.py @@ -2,8 +2,6 @@ Implements the Distutils 'build' command.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os @@ -11,12 +9,12 @@ from distutils.core import Command from distutils.util import get_platform -def show_compilers (): +def show_compilers(): from distutils.ccompiler import show_compilers show_compilers() -class build (Command): +class build(Command): description = "build everything needed to install" @@ -51,7 +49,7 @@ class build (Command): "list available compilers", show_compilers), ] - def initialize_options (self): + def initialize_options(self): self.build_base = 'build' # these are decided only after 'build_base' has its final value # (unless overridden by the user or client) @@ -65,8 +63,7 @@ class build (Command): self.force = 0 self.executable = None - def finalize_options (self): - + def finalize_options(self): plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3]) # 'build_purelib' and 'build_platlib' just default to 'lib' and @@ -98,11 +95,8 @@ class build (Command): if self.executable is None: self.executable = os.path.normpath(sys.executable) - # finalize_options () - - - def run (self): + def run(self): # Run all relevant sub-commands. This will be some subset of: # - build_py - pure Python modules # - build_clib - standalone C libraries @@ -114,16 +108,16 @@ class build (Command): # -- Predicates for the sub-command list --------------------------- - def has_pure_modules (self): + def has_pure_modules(self): return self.distribution.has_pure_modules() - def has_c_libraries (self): + def has_c_libraries(self): return self.distribution.has_c_libraries() - def has_ext_modules (self): + def has_ext_modules(self): return self.distribution.has_ext_modules() - def has_scripts (self): + def has_scripts(self): return self.distribution.has_scripts() @@ -132,5 +126,3 @@ class build (Command): ('build_ext', has_ext_modules), ('build_scripts', has_scripts), ] - -# class build diff --git a/command/build_clib.py b/command/build_clib.py index bdf98bf3..5f95207c 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,8 +4,6 @@ Implements the Distutils 'build_clib' command, to build a C/C++ library that is included in the module distribution and needed by an extension module.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" @@ -19,18 +17,17 @@ __revision__ = "$Id$" # cut 'n paste. Sigh. import os -from types import * from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler from distutils import log -def show_compilers (): +def show_compilers(): from distutils.ccompiler import show_compilers show_compilers() -class build_clib (Command): +class build_clib(Command): description = "build C/C++ libraries used by Python extensions" @@ -54,7 +51,7 @@ class build_clib (Command): "list available compilers", show_compilers), ] - def initialize_options (self): + def initialize_options(self): self.build_clib = None self.build_temp = None @@ -69,11 +66,8 @@ class build_clib (Command): self.force = 0 self.compiler = None - # initialize_options() - - - def finalize_options (self): + def finalize_options(self): # This might be confusing: both build-clib and build-temp default # to build-temp as defined by the "build" command. This is because # I think that C libraries are really just temporary build @@ -98,11 +92,8 @@ class build_clib (Command): # XXX same as for build_ext -- what about 'self.define' and # 'self.undef' ? - # finalize_options() - - - def run (self): + def run(self): if not self.libraries: return @@ -125,51 +116,41 @@ class build_clib (Command): self.build_libraries(self.libraries) - # run() - - def check_library_list (self, libraries): + def check_library_list(self, libraries): """Ensure that the list of libraries (presumably provided as a command option 'libraries') is valid, i.e. it is a list of 2-tuples, where the tuples are (library_name, build_info_dict). Raise DistutilsSetupError if the structure is invalid anywhere; just returns otherwise.""" - # Yechh, blecch, ackk: this is ripped straight out of build_ext.py, # with only names changed to protect the innocent! - - if type(libraries) is not ListType: - raise DistutilsSetupError, \ - "'libraries' option must be a list of tuples" + if not isinstance(libraries, list): + raise DistutilsSetupError( + "'libraries' option must be a list of tuples") for lib in libraries: - if type(lib) is not TupleType and len(lib) != 2: - raise DistutilsSetupError, \ - "each element of 'libraries' must a 2-tuple" + if not isinstance(lib, tuple) and len(lib) != 2: + raise DistutilsSetupError( + "each element of 'libraries' must a 2-tuple") if isinstance(lib[0], basestring): - raise DistutilsSetupError, \ - "first element of each tuple in 'libraries' " + \ - "must be a string (the library name)" + raise DistutilsSetupError( + "first element of each tuple in 'libraries' " + "must be a string (the library name)") if '/' in lib[0] or (os.sep != '/' and os.sep in lib[0]): - raise DistutilsSetupError, \ - ("bad library name '%s': " + - "may not contain directory separators") % \ - lib[0] - - if type(lib[1]) is not DictionaryType: - raise DistutilsSetupError, \ - "second element of each tuple in 'libraries' " + \ - "must be a dictionary (build info)" - # for lib + raise DistutilsSetupError("bad library name '%s': " + "may not contain directory separators" % lib[0]) - # check_library_list () + if not isinstance(lib[1], dict): + raise DistutilsSetupError( + "second element of each tuple in 'libraries' " + "must be a dictionary (build info)") - def get_library_names (self): + def get_library_names(self): # Assume the library list is valid -- 'check_library_list()' is # called from 'finalize_options()', so it should be! - if not self.libraries: return None @@ -178,36 +159,30 @@ class build_clib (Command): lib_names.append(lib_name) return lib_names - # get_library_names () - - def get_source_files (self): + def get_source_files(self): self.check_library_list(self.libraries) filenames = [] for (lib_name, build_info) in self.libraries: sources = build_info.get('sources') - if (sources is None or - type(sources) not in (ListType, TupleType) ): - raise DistutilsSetupError, \ - ("in 'libraries' option (library '%s'), " + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " "'sources' must be present and must be " - "a list of source filenames") % lib_name + "a list of source filenames" % lib_name) filenames.extend(sources) - return filenames - # get_source_files () - - def build_libraries (self, libraries): + def build_libraries(self, libraries): for (lib_name, build_info) in libraries: sources = build_info.get('sources') - if sources is None or type(sources) not in (ListType, TupleType): - raise DistutilsSetupError, \ - ("in 'libraries' option (library '%s'), " + - "'sources' must be present and must be " + - "a list of source filenames") % lib_name + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % lib_name) sources = list(sources) log.info("building '%s' library", lib_name) @@ -229,9 +204,3 @@ class build_clib (Command): self.compiler.create_static_lib(objects, lib_name, output_dir=self.build_clib, debug=self.debug) - - # for libraries - - # build_libraries () - -# class build_lib diff --git a/command/build_ext.py b/command/build_ext.py index 0236a268..a439c49a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,12 +4,9 @@ Implements the Distutils 'build_ext' command, for building extension modules (currently limited to C extensions, should accommodate C++ extensions ASAP).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, re -from types import * from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version @@ -28,7 +25,7 @@ def show_compilers (): show_compilers() -class build_ext (Command): +class build_ext(Command): description = "build C/C++ extensions (compile/link to build directory)" @@ -94,7 +91,7 @@ class build_ext (Command): "list available compilers", show_compilers), ] - def initialize_options (self): + def initialize_options(self): self.extensions = None self.build_lib = None self.build_temp = None @@ -115,7 +112,7 @@ class build_ext (Command): self.swig_cpp = None self.swig_opts = None - def finalize_options (self): + def finalize_options(self): from distutils import sysconfig self.set_undefined_options('build', @@ -130,7 +127,6 @@ class build_ext (Command): self.extensions = self.distribution.ext_modules - # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. py_include = sysconfig.get_python_inc() @@ -226,11 +222,7 @@ class build_ext (Command): else: self.swig_opts = self.swig_opts.split(' ') - # finalize_options () - - - def run (self): - + def run(self): from distutils.ccompiler import new_compiler # 'self.extensions', as supplied by setup.py, is a list of @@ -289,10 +281,7 @@ class build_ext (Command): # Now actually compile and link everything. self.build_extensions() - # run () - - - def check_extensions_list (self, extensions): + def check_extensions_list(self, extensions): """Ensure that the list of extensions (presumably provided as a command option 'extensions') is valid, i.e. it is a list of Extension objects. We also support the old-style list of 2-tuples, @@ -302,34 +291,33 @@ class build_ext (Command): Raise DistutilsSetupError if the structure is invalid anywhere; just returns otherwise. """ - if type(extensions) is not ListType: - raise DistutilsSetupError, \ - "'ext_modules' option must be a list of Extension instances" + if not isinstance(extensions, list): + raise DistutilsSetupError( + "'ext_modules' option must be a list of Extension instances") - for i in range(len(extensions)): - ext = extensions[i] + for i, ext in enumerate(extensions): if isinstance(ext, Extension): continue # OK! (assume type-checking done # by Extension constructor) (ext_name, build_info) = ext - log.warn(("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s'" - "-- please convert to Extension instance" % ext_name)) - if type(ext) is not TupleType and len(ext) != 2: - raise DistutilsSetupError, \ - ("each element of 'ext_modules' option must be an " + log.warn("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s'" + "-- please convert to Extension instance" % ext_name) + if not isinstance(ext, tuple) and len(ext) != 2: + raise DistutilsSetupError( + "each element of 'ext_modules' option must be an " "Extension instance or 2-tuple") if not (isinstance(ext_name, basestring) and extension_name_re.match(ext_name)): - raise DistutilsSetupError, \ - ("first element of each tuple in 'ext_modules' " + raise DistutilsSetupError( + "first element of each tuple in 'ext_modules' " "must be the extension name (a string)") - if type(build_info) is not DictionaryType: - raise DistutilsSetupError, \ - ("second element of each tuple in 'ext_modules' " + if not instance(build_info, DictionaryType): + raise DistutilsSetupError( + "second element of each tuple in 'ext_modules' " "must be a dictionary (build info)") # OK, the (ext_name, build_info) dict is type-safe: convert it @@ -361,11 +349,10 @@ class build_ext (Command): ext.define_macros = [] ext.undef_macros = [] for macro in macros: - if not (type(macro) is TupleType and - 1 <= len(macro) <= 2): - raise DistutilsSetupError, \ - ("'macros' element of build info dict " - "must be 1- or 2-tuple") + if not (isinstance(macro, tuple) and len(macro) in (1, 2)): + raise DistutilsSetupError( + "'macros' element of build info dict " + "must be 1- or 2-tuple") if len(macro) == 1: ext.undef_macros.append(macro[0]) elif len(macro) == 2: @@ -373,24 +360,16 @@ class build_ext (Command): extensions[i] = ext - # for extensions - - # check_extensions_list () - - - def get_source_files (self): + def get_source_files(self): self.check_extensions_list(self.extensions) filenames = [] # Wouldn't it be neat if we knew the names of header files too... for ext in self.extensions: filenames.extend(ext.sources) - return filenames - - def get_outputs (self): - + def get_outputs(self): # Sanity check the 'extensions' list -- can't assume this is being # done in the same run as a 'build_extensions()' call (in fact, we # can probably assume that it *isn't*!). @@ -406,8 +385,6 @@ class build_ext (Command): self.get_ext_filename(fullname))) return outputs - # get_outputs () - def build_extensions(self): # First, sanity-check the 'extensions' list self.check_extensions_list(self.extensions) @@ -417,11 +394,11 @@ class build_ext (Command): def build_extension(self, ext): sources = ext.sources - if sources is None or type(sources) not in (ListType, TupleType): - raise DistutilsSetupError, \ - ("in 'ext_modules' option (extension '%s'), " + - "'sources' must be present and must be " + - "a list of source filenames") % ext.name + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'ext_modules' option (extension '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % ext.name) sources = list(sources) fullname = self.get_ext_fullname(ext.name) @@ -512,15 +489,12 @@ class build_ext (Command): build_temp=self.build_temp, target_lang=language) - - def swig_sources (self, sources, extension): - + def swig_sources(self, sources, extension): """Walk the list of source files in 'sources', looking for SWIG interface (.i) files. Run SWIG on all that are found, and return a modified 'sources' list with SWIG source files replaced by the generated C (or C++) files. """ - new_sources = [] swig_sources = [] swig_targets = {} @@ -569,18 +543,14 @@ class build_ext (Command): return new_sources - # swig_sources () - - def find_swig (self): + def find_swig(self): """Return the name of the SWIG executable. On Unix, this is just "swig" -- it should be in the PATH. Tries a bit harder on Windows. """ - if os.name == "posix": return "swig" elif os.name == "nt": - # Look for SWIG in its standard installation directory on # Windows (or so I presume!). If we find it there, great; # if not, act like Unix and assume it's in the PATH. @@ -590,33 +560,28 @@ class build_ext (Command): return fn else: return "swig.exe" - elif os.name == "os2": # assume swig available in the PATH. return "swig.exe" - else: - raise DistutilsPlatformError, \ - ("I don't know how to find (much less run) SWIG " - "on platform '%s'") % os.name - - # find_swig () + raise DistutilsPlatformError( + "I don't know how to find (much less run) SWIG " + "on platform '%s'" % os.name) # -- Name generators ----------------------------------------------- # (extension names, filenames, whatever) - def get_ext_fullname (self, ext_name): + def get_ext_fullname(self, ext_name): if self.package is None: return ext_name else: return self.package + '.' + ext_name - def get_ext_filename (self, ext_name): + def get_ext_filename(self, ext_name): r"""Convert the name of an extension (eg. "foo.bar") into the name of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ - from distutils.sysconfig import get_config_var ext_path = ext_name.split('.') # OS/2 has an 8 character module (extension) limit :-( @@ -628,19 +593,18 @@ class build_ext (Command): return os.path.join(*ext_path) + '_d' + so_ext return os.path.join(*ext_path) + so_ext - def get_export_symbols (self, ext): + def get_export_symbols(self, ext): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not provided, "init" + module_name. Only relevant on Windows, where the .pyd file (DLL) must export the module "init" function. """ - initfunc_name = "init" + ext.name.split('.')[-1] if initfunc_name not in ext.export_symbols: ext.export_symbols.append(initfunc_name) return ext.export_symbols - def get_libraries (self, ext): + def get_libraries(self, ext): """Return the list of libraries to link against when building a shared extension. On most platforms, this is just 'ext.libraries'; on Windows and OS/2, we add the Python library (eg. python20.dll). @@ -699,11 +663,9 @@ class build_ext (Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib, "m"] + extra - elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries - else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): @@ -713,5 +675,3 @@ class build_ext (Command): return ext.libraries + [pythonlib] else: return ext.libraries - -# class build_ext diff --git a/command/build_py.py b/command/build_py.py index 8f560908..454424f3 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -2,12 +2,9 @@ Implements the Distutils 'build_py' command.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os -from types import * from glob import glob from distutils.core import Command @@ -32,8 +29,7 @@ class build_py (Command): boolean_options = ['compile', 'force'] negative_opt = {'no-compile' : 'compile'} - - def initialize_options (self): + def initialize_options(self): self.build_lib = None self.py_modules = None self.package = None @@ -43,7 +39,7 @@ class build_py (Command): self.optimize = 0 self.force = None - def finalize_options (self): + def finalize_options(self): self.set_undefined_options('build', ('build_lib', 'build_lib'), ('force', 'force')) @@ -61,15 +57,14 @@ class build_py (Command): # Ick, copied straight from install_lib.py (fancy_getopt needs a # type system! Hell, *everything* needs a type system!!!) - if type(self.optimize) is not IntType: + if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) assert 0 <= self.optimize <= 2 except (ValueError, AssertionError): - raise DistutilsOptionError, "optimize must be 0, 1, or 2" - - def run (self): + raise DistutilsOptionError("optimize must be 0, 1, or 2") + def run(self): # XXX copy_file by default preserves atime and mtime. IMHO this is # the right thing to do, but perhaps it should be an option -- in # particular, a site administrator might want installed files to @@ -99,9 +94,7 @@ class build_py (Command): self.byte_compile(self.get_outputs(include_bytecode=0)) - # run () - - def get_data_files (self): + def get_data_files(self): """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" data = [] if not self.packages: @@ -125,7 +118,7 @@ class build_py (Command): data.append((package, src_dir, build_dir, filenames)) return data - def find_data_files (self, package, src_dir): + def find_data_files(self, package, src_dir): """Return filenames for package's data files in 'src_dir'""" globs = (self.package_data.get('', []) + self.package_data.get(package, [])) @@ -137,7 +130,7 @@ class build_py (Command): files.extend([fn for fn in filelist if fn not in files]) return files - def build_package_data (self): + def build_package_data(self): """Copy data files into build directory""" lastdir = None for package, src_dir, build_dir, filenames in self.data_files: @@ -147,11 +140,10 @@ class build_py (Command): self.copy_file(os.path.join(src_dir, filename), target, preserve_mode=False) - def get_package_dir (self, package): + def get_package_dir(self, package): """Return the directory, relative to the top of the source distribution, where package 'package' should be found (at least according to the 'package_dir' option, if any).""" - path = package.split('.') if not self.package_dir: @@ -187,23 +179,19 @@ class build_py (Command): else: return '' - # get_package_dir () - - - def check_package (self, package, package_dir): - + def check_package(self, package, package_dir): # Empty dir name means current directory, which we can probably # assume exists. Also, os.path.exists and isdir don't know about # my "empty string means current dir" convention, so we have to # circumvent them. if package_dir != "": if not os.path.exists(package_dir): - raise DistutilsFileError, \ - "package directory '%s' does not exist" % package_dir + raise DistutilsFileError( + "package directory '%s' does not exist" % package_dir) if not os.path.isdir(package_dir): - raise DistutilsFileError, \ - ("supposed package directory '%s' exists, " + - "but is not a directory") % package_dir + raise DistutilsFileError( + "supposed package directory '%s' exists, " + "but is not a directory" % package_dir) # Require __init__.py for all but the "root package" if package: @@ -218,20 +206,14 @@ class build_py (Command): # __init__.py doesn't exist -- so don't return the filename. return None - # check_package () - - - def check_module (self, module, module_file): + def check_module(self, module, module_file): if not os.path.isfile(module_file): log.warn("file %s (for module %s) not found", module_file, module) - return 0 + return False else: - return 1 + return True - # check_module () - - - def find_package_modules (self, package, package_dir): + def find_package_modules(self, package, package_dir): self.check_package(package, package_dir) module_files = glob(os.path.join(package_dir, "*.py")) modules = [] @@ -246,8 +228,7 @@ class build_py (Command): self.debug_print("excluding %s" % setup_script) return modules - - def find_modules (self): + def find_modules(self): """Finds individually-specified Python modules, ie. those listed by module name in 'self.py_modules'. Returns a list of tuples (package, module_base, filename): 'package' is a tuple of the path through @@ -256,7 +237,6 @@ class build_py (Command): ".py" file (relative to the distribution root) that implements the module. """ - # Map package names to tuples of useful info about the package: # (package_dir, checked) # package_dir - the directory where we'll find source files for @@ -272,7 +252,6 @@ class build_py (Command): # just the "package" for a toplevel is empty (either an empty # string or empty list, depending on context). Differences: # - don't check for __init__.py in directory for empty package - for module in self.py_modules: path = module.split('.') package = '.'.join(path[0:-1]) @@ -301,16 +280,12 @@ class build_py (Command): return modules - # find_modules () - - - def find_all_modules (self): + def find_all_modules(self): """Compute the list of all modules that will be built, whether they are specified one-module-at-a-time ('self.py_modules') or by whole packages ('self.packages'). Return a list of tuples (package, module, module_file), just like 'find_modules()' and 'find_package_modules()' do.""" - modules = [] if self.py_modules: modules.extend(self.find_modules()) @@ -319,28 +294,16 @@ class build_py (Command): package_dir = self.get_package_dir(package) m = self.find_package_modules(package, package_dir) modules.extend(m) - return modules - # find_all_modules () + def get_source_files(self): + return [module[-1] for module in self.find_all_modules()] - - def get_source_files (self): - - modules = self.find_all_modules() - filenames = [] - for module in modules: - filenames.append(module[-1]) - - return filenames - - - def get_module_outfile (self, build_dir, package, module): + def get_module_outfile(self, build_dir, package, module): outfile_path = [build_dir] + list(package) + [module + ".py"] return os.path.join(*outfile_path) - - def get_outputs (self, include_bytecode=1): + def get_outputs(self, include_bytecode=1): modules = self.find_all_modules() outputs = [] for (package, module, module_file) in modules: @@ -361,13 +324,12 @@ class build_py (Command): return outputs - - def build_module (self, module, module_file, package): + def build_module(self, module, module_file, package): if isinstance(package, basestring): package = package.split('.') - elif type(package) not in (ListType, TupleType): - raise TypeError, \ - "'package' must be a string (dot-separated), list, or tuple" + elif not isinstance(package, (list, tuple)): + raise TypeError( + "'package' must be a string (dot-separated), list, or tuple") # Now put the module source file into the "build" area -- this is # easy, we just copy it somewhere under self.build_lib (the build @@ -377,25 +339,17 @@ class build_py (Command): self.mkpath(dir) return self.copy_file(module_file, outfile, preserve_mode=0) - - def build_modules (self): - + def build_modules(self): modules = self.find_modules() for (package, module, module_file) in modules: - # Now "build" the module -- ie. copy the source file to # self.build_lib (the build directory for Python source). # (Actually, it gets copied to the directory for this package # under self.build_lib.) self.build_module(module, module_file, package) - # build_modules () - - - def build_packages (self): - + def build_packages(self): for package in self.packages: - # Get list of (package, module, module_file) tuples based on # scanning the package directory. 'package' is only included # in the tuple so that 'find_modules()' and @@ -414,10 +368,7 @@ class build_py (Command): assert package == package_ self.build_module(module, module_file, package) - # build_packages () - - - def byte_compile (self, files): + def byte_compile(self, files): from distutils.util import byte_compile prefix = self.build_lib if prefix[-1] != os.sep: @@ -426,12 +377,9 @@ class build_py (Command): # XXX this code is essentially the same as the 'byte_compile() # method of the "install_lib" command, except for the determination # of the 'prefix' string. Hmmm. - if self.compile: byte_compile(files, optimize=0, force=self.force, prefix=prefix, dry_run=self.dry_run) if self.optimize > 0: byte_compile(files, optimize=self.optimize, force=self.force, prefix=prefix, dry_run=self.dry_run) - -# class build_py diff --git a/command/build_scripts.py b/command/build_scripts.py index 511b82f9..176e6e10 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -2,8 +2,6 @@ Implements the Distutils 'build_scripts' command.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, re @@ -17,7 +15,7 @@ from distutils import log # check if Python is called on the first line with this expression first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$') -class build_scripts (Command): +class build_scripts(Command): description = "\"build\" scripts (copy and fixup #! line)" @@ -30,14 +28,14 @@ class build_scripts (Command): boolean_options = ['force'] - def initialize_options (self): + def initialize_options(self): self.build_dir = None self.scripts = None self.force = None self.executable = None self.outfiles = None - def finalize_options (self): + def finalize_options(self): self.set_undefined_options('build', ('build_scripts', 'build_dir'), ('force', 'force'), @@ -47,13 +45,13 @@ class build_scripts (Command): def get_source_files(self): return self.scripts - def run (self): + def run(self): if not self.scripts: return self.copy_scripts() - def copy_scripts (self): + def copy_scripts(self): """Copy each script listed in 'self.scripts'; if it's marked as a Python script in the Unix way (first line matches 'first_line_re', ie. starts with "\#!" and contains "python"), then adjust the first @@ -62,7 +60,7 @@ class build_scripts (Command): self.mkpath(self.build_dir) outfiles = [] for script in self.scripts: - adjust = 0 + adjust = False script = convert_path(script) outfile = os.path.join(self.build_dir, os.path.basename(script)) outfiles.append(outfile) @@ -88,7 +86,7 @@ class build_scripts (Command): match = first_line_re.match(first_line) if match: - adjust = 1 + adjust = True post_interp = match.group(1) or '' if adjust: @@ -125,7 +123,3 @@ class build_scripts (Command): log.info("changing mode of %s from %o to %o", file, oldmode, newmode) os.chmod(file, newmode) - - # copy_scripts () - -# class build_scripts diff --git a/command/clean.py b/command/clean.py index 02189c53..ae1d22c3 100644 --- a/command/clean.py +++ b/command/clean.py @@ -4,8 +4,6 @@ Implements the Distutils 'clean' command.""" # contributed by Bastian Kleineidam , added 2000-03-18 -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os @@ -13,7 +11,7 @@ from distutils.core import Command from distutils.dir_util import remove_tree from distutils import log -class clean (Command): +class clean(Command): description = "clean up temporary files from 'build' command" user_options = [ @@ -78,5 +76,3 @@ class clean (Command): log.info("removing '%s'", self.build_base) except OSError: pass - -# class clean diff --git a/command/command_template b/command/command_template index 50bbab7b..6106819d 100644 --- a/command/command_template +++ b/command/command_template @@ -10,7 +10,7 @@ __revision__ = "$Id$" from distutils.core import Command -class x (Command): +class x(Command): # Brief (40-50 characters) description of the command description = "" @@ -21,25 +21,13 @@ class x (Command): ""), ] - - def initialize_options (self): + def initialize_options(self): self. = None self. = None self. = None - # initialize_options() - - - def finalize_options (self): + def finalize_options(self): if self.x is None: self.x = - # finalize_options() - - - def run (self): - - - # run() - -# class x + def run(self): diff --git a/command/config.py b/command/config.py index 04cfcde7..a6012342 100644 --- a/command/config.py +++ b/command/config.py @@ -9,21 +9,17 @@ configure-like tasks: "try to compile this C code", or "figure out where this header file lives". """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, re -from types import * from distutils.core import Command from distutils.errors import DistutilsExecError from distutils.sysconfig import customize_compiler from distutils import log -LANG_EXT = {'c': '.c', - 'c++': '.cxx'} +LANG_EXT = {"c": ".c", "c++": ".cxx"} -class config (Command): +class config(Command): description = "prepare to build" @@ -53,7 +49,7 @@ class config (Command): # The three standard command methods: since the "config" command # does nothing by default, these are empty. - def initialize_options (self): + def initialize_options(self): self.compiler = None self.cc = None self.include_dirs = None @@ -70,7 +66,7 @@ class config (Command): # to clean at some point self.temp_files = [] - def finalize_options (self): + def finalize_options(self): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] elif isinstance(self.include_dirs, basestring): @@ -86,16 +82,14 @@ class config (Command): elif isinstance(self.library_dirs, basestring): self.library_dirs = self.library_dirs.split(os.pathsep) - - def run (self): + def run(self): pass - # Utility methods for actual "config" commands. The interfaces are # loosely based on Autoconf macros of similar names. Sub-classes # may use these freely. - def _check_compiler (self): + def _check_compiler(self): """Check that 'self.compiler' really is a CCompiler object; if not, make it one. """ @@ -113,8 +107,7 @@ class config (Command): if self.library_dirs: self.compiler.set_library_dirs(self.library_dirs) - - def _gen_temp_sourcefile (self, body, headers, lang): + def _gen_temp_sourcefile(self, body, headers, lang): filename = "_configtest" + LANG_EXT[lang] file = open(filename, "w") if headers: @@ -127,14 +120,14 @@ class config (Command): file.close() return filename - def _preprocess (self, body, headers, include_dirs, lang): + def _preprocess(self, body, headers, include_dirs, lang): src = self._gen_temp_sourcefile(body, headers, lang) out = "_configtest.i" self.temp_files.extend([src, out]) self.compiler.preprocess(src, out, include_dirs=include_dirs) return (src, out) - def _compile (self, body, headers, include_dirs, lang): + def _compile(self, body, headers, include_dirs, lang): src = self._gen_temp_sourcefile(body, headers, lang) if self.dump_source: dump_file(src, "compiling '%s':" % src) @@ -143,9 +136,8 @@ class config (Command): self.compiler.compile([src], include_dirs=include_dirs) return (src, obj) - def _link (self, body, - headers, include_dirs, - libraries, library_dirs, lang): + def _link(self, body, headers, include_dirs, libraries, + library_dirs, lang): (src, obj) = self._compile(body, headers, include_dirs, lang) prog = os.path.splitext(os.path.basename(src))[0] self.compiler.link_executable([obj], prog, @@ -159,7 +151,7 @@ class config (Command): return (src, obj, prog) - def _clean (self, *filenames): + def _clean(self, *filenames): if not filenames: filenames = self.temp_files self.temp_files = [] @@ -181,7 +173,7 @@ class config (Command): # XXX need access to the header search path and maybe default macros. - def try_cpp (self, body=None, headers=None, include_dirs=None, lang="c"): + def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): """Construct a source file from 'body' (a string containing lines of C/C++ code) and 'headers' (a list of header files to include) and run it through the preprocessor. Return true if the @@ -190,17 +182,17 @@ class config (Command): """ from distutils.ccompiler import CompileError self._check_compiler() - ok = 1 + ok = True try: self._preprocess(body, headers, include_dirs, lang) except CompileError: - ok = 0 + ok = False self._clean() return ok - def search_cpp (self, pattern, body=None, - headers=None, include_dirs=None, lang="c"): + def search_cpp(self, pattern, body=None, headers=None, + include_dirs=None, lang="c"): """Construct a source file (just like 'try_cpp()'), run it through the preprocessor, and return true if any line of the output matches 'pattern'. 'pattern' should either be a compiled regex object or a @@ -216,20 +208,20 @@ class config (Command): pattern = re.compile(pattern) file = open(out) - match = 0 - while 1: + match = False + while True: line = file.readline() if line == '': break if pattern.search(line): - match = 1 + match = True break file.close() self._clean() return match - def try_compile (self, body, headers=None, include_dirs=None, lang="c"): + def try_compile(self, body, headers=None, include_dirs=None, lang="c"): """Try to compile a source file built from 'body' and 'headers'. Return true on success, false otherwise. """ @@ -237,18 +229,16 @@ class config (Command): self._check_compiler() try: self._compile(body, headers, include_dirs, lang) - ok = 1 + ok = True except CompileError: - ok = 0 + ok = False log.info(ok and "success!" or "failure.") self._clean() return ok - def try_link (self, body, - headers=None, include_dirs=None, - libraries=None, library_dirs=None, - lang="c"): + def try_link(self, body, headers=None, include_dirs=None, + libraries=None, library_dirs=None, lang="c"): """Try to compile and link a source file, built from 'body' and 'headers', to executable form. Return true on success, false otherwise. @@ -258,18 +248,16 @@ class config (Command): try: self._link(body, headers, include_dirs, libraries, library_dirs, lang) - ok = 1 + ok = True except (CompileError, LinkError): - ok = 0 + ok = False log.info(ok and "success!" or "failure.") self._clean() return ok - def try_run (self, body, - headers=None, include_dirs=None, - libraries=None, library_dirs=None, - lang="c"): + def try_run(self, body, headers=None, include_dirs=None, + libraries=None, library_dirs=None, lang="c"): """Try to compile, link to an executable, and run a program built from 'body' and 'headers'. Return true on success, false otherwise. @@ -280,9 +268,9 @@ class config (Command): src, obj, exe = self._link(body, headers, include_dirs, libraries, library_dirs, lang) self.spawn([exe]) - ok = 1 + ok = True except (CompileError, LinkError, DistutilsExecError): - ok = 0 + ok = False log.info(ok and "success!" or "failure.") self._clean() @@ -293,11 +281,8 @@ class config (Command): # (these are the ones that are actually likely to be useful # when implementing a real-world config command!) - def check_func (self, func, - headers=None, include_dirs=None, - libraries=None, library_dirs=None, - decl=0, call=0): - + def check_func(self, func, headers=None, include_dirs=None, + libraries=None, library_dirs=None, decl=0, call=0): """Determine if function 'func' is available by constructing a source file that refers to 'func', and compiles and links it. If everything succeeds, returns true; otherwise returns false. @@ -311,7 +296,6 @@ class config (Command): calls it. 'libraries' and 'library_dirs' are used when linking. """ - self._check_compiler() body = [] if decl: @@ -327,10 +311,8 @@ class config (Command): return self.try_link(body, headers, include_dirs, libraries, library_dirs) - # check_func () - - def check_lib (self, library, library_dirs=None, - headers=None, include_dirs=None, other_libraries=[]): + def check_lib(self, library, library_dirs=None, headers=None, + include_dirs=None, other_libraries=[]): """Determine if 'library' is available to be linked against, without actually checking that any particular symbols are provided by it. 'headers' will be used in constructing the source file to @@ -340,12 +322,11 @@ class config (Command): has symbols that depend on other libraries. """ self._check_compiler() - return self.try_link("int main (void) { }", - headers, include_dirs, - [library]+other_libraries, library_dirs) + return self.try_link("int main (void) { }", headers, include_dirs, + [library] + other_libraries, library_dirs) - def check_header (self, header, include_dirs=None, - library_dirs=None, lang="c"): + def check_header(self, header, include_dirs=None, library_dirs=None, + lang="c"): """Determine if the system header file named by 'header_file' exists and can be found by the preprocessor; return true if so, false otherwise. @@ -354,10 +335,7 @@ class config (Command): include_dirs=include_dirs) -# class config - - -def dump_file (filename, head=None): +def dump_file(filename, head=None): if head is None: print(filename + ":") else: diff --git a/command/install.py b/command/install.py index a6543ba6..29cda1eb 100644 --- a/command/install.py +++ b/command/install.py @@ -4,12 +4,9 @@ Implements the Distutils 'install' command.""" from distutils import log -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os -from types import * from distutils.core import Command from distutils.debug import DEBUG from distutils.sysconfig import get_config_vars @@ -141,7 +138,7 @@ class install (Command): negative_opt = {'no-compile' : 'compile'} - def initialize_options (self): + def initialize_options(self): # High-level options: these select both an installation base # and scheme. @@ -215,7 +212,7 @@ class install (Command): # party Python modules on various platforms given a wide # array of user input is decided. Yes, it's quite complex!) - def finalize_options (self): + def finalize_options(self): # This method (and its pliant slaves, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default @@ -233,13 +230,13 @@ class install (Command): if ((self.prefix or self.exec_prefix or self.home) and (self.install_base or self.install_platbase)): - raise DistutilsOptionError, \ - ("must supply either prefix/exec-prefix/home or " + + raise DistutilsOptionError( + "must supply either prefix/exec-prefix/home or " + "install-base/install-platbase -- not both") if self.home and (self.prefix or self.exec_prefix): - raise DistutilsOptionError, \ - "must supply either home or prefix/exec-prefix -- not both" + raise DistutilsOptionError( + "must supply either home or prefix/exec-prefix -- not both") # Next, stuff that's wrong (or dubious) only on certain platforms. if os.name != "posix": @@ -341,10 +338,8 @@ class install (Command): # Punt on doc directories for now -- after all, we're punting on # documentation completely! - # finalize_options () - - def dump_dirs (self, msg): + def dump_dirs(self, msg): if DEBUG: from distutils.fancy_getopt import longopt_xlate print(msg + ":") @@ -362,8 +357,7 @@ class install (Command): print(" %s: %s" % (opt_name, val)) - def finalize_unix (self): - + def finalize_unix(self): if self.install_base is not None or self.install_platbase is not None: if ((self.install_lib is None and self.install_purelib is None and @@ -371,8 +365,8 @@ class install (Command): self.install_headers is None or self.install_scripts is None or self.install_data is None): - raise DistutilsOptionError, \ - ("install-base or install-platbase supplied, but " + raise DistutilsOptionError( + "install-base or install-platbase supplied, but " "installation scheme is incomplete") return @@ -382,8 +376,8 @@ class install (Command): else: if self.prefix is None: if self.exec_prefix is not None: - raise DistutilsOptionError, \ - "must not supply exec-prefix without prefix" + raise DistutilsOptionError( + "must not supply exec-prefix without prefix") self.prefix = os.path.normpath(sys.prefix) self.exec_prefix = os.path.normpath(sys.exec_prefix) @@ -396,11 +390,8 @@ class install (Command): self.install_platbase = self.exec_prefix self.select_scheme("unix_prefix") - # finalize_unix () - - - def finalize_other (self): # Windows and Mac OS for now + def finalize_other(self): # Windows and Mac OS for now if self.home is not None: self.install_base = self.install_platbase = self.home self.select_scheme("unix_home") @@ -412,13 +403,11 @@ class install (Command): try: self.select_scheme(os.name) except KeyError: - raise DistutilsPlatformError, \ - "I don't know how to install stuff on '%s'" % os.name - - # finalize_other () + raise DistutilsPlatformError( + "I don't know how to install stuff on '%s'" % os.name) - def select_scheme (self, name): + def select_scheme(self, name): # it's the caller's problem if they supply a bad name! scheme = INSTALL_SCHEMES[name] for key in SCHEME_KEYS: @@ -427,7 +416,7 @@ class install (Command): setattr(self, attrname, scheme[key]) - def _expand_attrs (self, attrs): + def _expand_attrs(self, attrs): for attr in attrs: val = getattr(self, attr) if val is not None: @@ -437,12 +426,12 @@ class install (Command): setattr(self, attr, val) - def expand_basedirs (self): + def expand_basedirs(self): self._expand_attrs(['install_base', 'install_platbase', 'root']) - def expand_dirs (self): + def expand_dirs(self): self._expand_attrs(['install_purelib', 'install_platlib', 'install_lib', @@ -451,14 +440,12 @@ class install (Command): 'install_data',]) - def convert_paths (self, *names): + def convert_paths(self, *names): for name in names: attr = "install_" + name setattr(self, attr, convert_path(getattr(self, attr))) - - def handle_extra_path (self): - + def handle_extra_path(self): if self.extra_path is None: self.extra_path = self.distribution.extra_path @@ -471,8 +458,8 @@ class install (Command): elif len(self.extra_path) == 2: (path_file, extra_dirs) = self.extra_path else: - raise DistutilsOptionError, \ - ("'extra_path' option must be a list, tuple, or " + raise DistutilsOptionError( + "'extra_path' option must be a list, tuple, or " "comma-separated string with 1 or 2 elements") # convert to local form in case Unix notation used (as it @@ -488,10 +475,7 @@ class install (Command): self.path_file = path_file self.extra_dirs = extra_dirs - # handle_extra_path () - - - def change_roots (self, *names): + def change_roots(self, *names): for name in names: attr = "install_" + name setattr(self, attr, change_root(self.root, getattr(self, attr))) @@ -499,8 +483,7 @@ class install (Command): # -- Command execution methods ------------------------------------- - def run (self): - + def run(self): # Obviously have to build before we can install if not self.skip_build: self.run_command('build') @@ -535,9 +518,7 @@ class install (Command): "you'll have to change the search path yourself"), self.install_lib) - # run () - - def create_path_file (self): + def create_path_file(self): filename = os.path.join(self.install_libbase, self.path_file + ".pth") if self.install_path_file: @@ -550,7 +531,7 @@ class install (Command): # -- Reporting methods --------------------------------------------- - def get_outputs (self): + def get_outputs(self): # Assemble the outputs of all the sub-commands. outputs = [] for cmd_name in self.get_sub_commands(): @@ -567,7 +548,7 @@ class install (Command): return outputs - def get_inputs (self): + def get_inputs(self): # XXX gee, this looks familiar ;-( inputs = [] for cmd_name in self.get_sub_commands(): @@ -579,19 +560,19 @@ class install (Command): # -- Predicates for sub-command list ------------------------------- - def has_lib (self): + def has_lib(self): """Return true if the current distribution has any Python modules to install.""" return (self.distribution.has_pure_modules() or self.distribution.has_ext_modules()) - def has_headers (self): + def has_headers(self): return self.distribution.has_headers() - def has_scripts (self): + def has_scripts(self): return self.distribution.has_scripts() - def has_data (self): + def has_data(self): return self.distribution.has_data_files() @@ -603,5 +584,3 @@ class install (Command): ('install_data', has_data), ('install_egg_info', lambda self:True), ] - -# class install diff --git a/command/install_data.py b/command/install_data.py index a9519442..2fbac63d 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -5,15 +5,13 @@ platform-independent data files.""" # contributed by Bastian Kleineidam -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os from distutils.core import Command from distutils.util import change_root, convert_path -class install_data (Command): +class install_data(Command): description = "install data files" @@ -28,7 +26,7 @@ class install_data (Command): boolean_options = ['force'] - def initialize_options (self): + def initialize_options(self): self.install_dir = None self.outfiles = [] self.root = None @@ -37,14 +35,14 @@ class install_data (Command): self.data_files = self.distribution.data_files self.warn_dir = 1 - def finalize_options (self): + def finalize_options(self): self.set_undefined_options('install', ('install_data', 'install_dir'), ('root', 'root'), ('force', 'force'), ) - def run (self): + def run(self): self.mkpath(self.install_dir) for f in self.data_files: if isinstance(f, basestring): @@ -77,8 +75,8 @@ class install_data (Command): (out, _) = self.copy_file(data, dir) self.outfiles.append(out) - def get_inputs (self): + def get_inputs(self): return self.data_files or [] - def get_outputs (self): + def get_outputs(self): return self.outfiles diff --git a/command/install_headers.py b/command/install_headers.py index 2bd1b043..7114eaf0 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -3,15 +3,13 @@ Implements the Distutils 'install_headers' command, to install C/C++ header files to the Python include directory.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os from distutils.core import Command -class install_headers (Command): +class install_headers(Command): description = "install C/C++ header files" @@ -23,18 +21,18 @@ class install_headers (Command): boolean_options = ['force'] - def initialize_options (self): + def initialize_options(self): self.install_dir = None self.force = 0 self.outfiles = [] - def finalize_options (self): + def finalize_options(self): self.set_undefined_options('install', ('install_headers', 'install_dir'), ('force', 'force')) - def run (self): + def run(self): headers = self.distribution.headers if not headers: return @@ -44,10 +42,8 @@ class install_headers (Command): (out, _) = self.copy_file(header, self.install_dir) self.outfiles.append(out) - def get_inputs (self): + def get_inputs(self): return self.distribution.headers or [] - def get_outputs (self): + def get_outputs(self): return self.outfiles - -# class install_headers diff --git a/command/install_lib.py b/command/install_lib.py index 4efaf9c6..ac620fcc 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -1,9 +1,6 @@ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os -from types import IntType from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -11,7 +8,7 @@ from distutils.errors import DistutilsOptionError # Extension for Python source files. PYTHON_SOURCE_EXTENSION = ".py" -class install_lib (Command): +class install_lib(Command): description = "install all Python modules (extensions and pure Python)" @@ -45,8 +42,7 @@ class install_lib (Command): boolean_options = ['force', 'compile', 'skip-build'] negative_opt = {'no-compile' : 'compile'} - - def initialize_options (self): + def initialize_options(self): # let the 'install' command dictate our installation directory self.install_dir = None self.build_dir = None @@ -55,7 +51,7 @@ class install_lib (Command): self.optimize = None self.skip_build = None - def finalize_options (self): + def finalize_options(self): # Get all the information we need to install pure Python modules # from the umbrella 'install' command -- build (source) directory, @@ -70,19 +66,18 @@ class install_lib (Command): ) if self.compile is None: - self.compile = 1 + self.compile = True if self.optimize is None: - self.optimize = 0 + self.optimize = False - if type(self.optimize) is not IntType: + if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) assert 0 <= self.optimize <= 2 except (ValueError, AssertionError): - raise DistutilsOptionError, "optimize must be 0, 1, or 2" - - def run (self): + raise DistutilsOptionError("optimize must be 0, 1, or 2") + def run(self): # Make sure we have built everything we need first self.build() @@ -95,20 +90,18 @@ class install_lib (Command): if outfiles is not None and self.distribution.has_pure_modules(): self.byte_compile(outfiles) - # run () - # -- Top-level worker functions ------------------------------------ # (called from 'run()') - def build (self): + def build(self): if not self.skip_build: if self.distribution.has_pure_modules(): self.run_command('build_py') if self.distribution.has_ext_modules(): self.run_command('build_ext') - def install (self): + def install(self): if os.path.isdir(self.build_dir): outfiles = self.copy_tree(self.build_dir, self.install_dir) else: @@ -117,7 +110,7 @@ class install_lib (Command): return return outfiles - def byte_compile (self, files): + def byte_compile(self, files): from distutils.util import byte_compile # Get the "--root" directory supplied to the "install" command, @@ -138,8 +131,7 @@ class install_lib (Command): # -- Utility methods ----------------------------------------------- - def _mutate_outputs (self, has_any, build_cmd, cmd_option, output_dir): - + def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir): if not has_any: return [] @@ -154,9 +146,7 @@ class install_lib (Command): return outputs - # _mutate_outputs () - - def _bytecode_filenames (self, py_filenames): + def _bytecode_filenames(self, py_filenames): bytecode_files = [] for py_file in py_filenames: # Since build_py handles package data installation, the @@ -176,7 +166,7 @@ class install_lib (Command): # -- External interface -------------------------------------------- # (called by outsiders) - def get_outputs (self): + def get_outputs(self): """Return the list of files that would be installed if this command were actually run. Not affected by the "dry-run" flag or whether modules have actually been built yet. @@ -197,9 +187,7 @@ class install_lib (Command): return pure_outputs + bytecode_outputs + ext_outputs - # get_outputs () - - def get_inputs (self): + def get_inputs(self): """Get the list of files that are input to this command, ie. the files that get installed as they are named in the build tree. The files in this list correspond one-to-one to the output @@ -216,5 +204,3 @@ class install_lib (Command): inputs.extend(build_ext.get_outputs()) return inputs - -# class install_lib diff --git a/command/install_scripts.py b/command/install_scripts.py index da2da358..ea8d5aa6 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -5,8 +5,6 @@ Python scripts.""" # contributed by Bastian Kleineidam -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os @@ -14,7 +12,8 @@ from distutils.core import Command from distutils import log from stat import ST_MODE -class install_scripts (Command): + +class install_scripts(Command): description = "install scripts (Python or otherwise)" @@ -27,14 +26,13 @@ class install_scripts (Command): boolean_options = ['force', 'skip-build'] - - def initialize_options (self): + def initialize_options(self): self.install_dir = None self.force = 0 self.build_dir = None self.skip_build = None - def finalize_options (self): + def finalize_options(self): self.set_undefined_options('build', ('build_scripts', 'build_dir')) self.set_undefined_options('install', ('install_scripts', 'install_dir'), @@ -42,7 +40,7 @@ class install_scripts (Command): ('skip_build', 'skip_build'), ) - def run (self): + def run(self): if not self.skip_build: self.run_command('build_scripts') self.outfiles = self.copy_tree(self.build_dir, self.install_dir) @@ -57,10 +55,8 @@ class install_scripts (Command): log.info("changing mode of %s to %o", file, mode) os.chmod(file, mode) - def get_inputs (self): + def get_inputs(self): return self.distribution.scripts or [] def get_outputs(self): return self.outfiles or [] - -# class install_scripts diff --git a/command/sdist.py b/command/sdist.py index 8e1c0660..b1c76486 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -2,12 +2,9 @@ Implements the Distutils 'sdist' command (create a source distribution).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os -from types import * from glob import glob from distutils.core import Command from distutils import dir_util, dep_util, file_util, archive_util @@ -82,7 +79,7 @@ class sdist (Command): default_format = { 'posix': 'gztar', 'nt': 'zip' } - def initialize_options (self): + def initialize_options(self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. self.template = None @@ -103,7 +100,7 @@ class sdist (Command): self.archive_files = None - def finalize_options (self): + def finalize_options(self): if self.manifest is None: self.manifest = "MANIFEST" if self.template is None: @@ -114,21 +111,20 @@ class sdist (Command): try: self.formats = [self.default_format[os.name]] except KeyError: - raise DistutilsPlatformError, \ - "don't know how to create source distributions " + \ - "on platform %s" % os.name + raise DistutilsPlatformError( + "don't know how to create source distributions " + "on platform %s" % os.name) bad_format = archive_util.check_archive_formats(self.formats) if bad_format: - raise DistutilsOptionError, \ - "unknown archive format '%s'" % bad_format + raise DistutilsOptionError( + "unknown archive format '%s'" % bad_format) if self.dist_dir is None: self.dist_dir = "dist" - def run (self): - + def run(self): # 'filelist' contains the list of files that will make up the # manifest self.filelist = FileList() @@ -150,8 +146,7 @@ class sdist (Command): # or zipfile, or whatever. self.make_distribution() - - def check_metadata (self): + def check_metadata(self): """Ensure that all required elements of meta-data (name, version, URL, (author and author_email) or (maintainer and maintainer_email)) are supplied by the Distribution object; warn if @@ -181,17 +176,13 @@ class sdist (Command): "or (maintainer and maintainer_email) " + "must be supplied") - # check_metadata () - - - def get_file_list (self): + def get_file_list(self): """Figure out the list of files to include in the source distribution, and put it in 'self.filelist'. This might involve reading the manifest template (and writing the manifest), or just reading the manifest, or just using the default file set -- it all depends on the user's options and the state of the filesystem. """ - # If we have a manifest template, see if it's newer than the # manifest; if so, we'll regenerate the manifest. template_exists = os.path.isfile(self.template) @@ -231,9 +222,9 @@ class sdist (Command): # Regenerate the manifest if necessary (or if explicitly told to) if manifest_outofdate or neither_exists or force_regen: if not template_exists: - self.warn(("manifest template '%s' does not exist " + - "(using default file list)") % - self.template) + self.warn("manifest template '%s' does not exist " + "(using default file list)" + % self.template) self.filelist.findall() if self.use_defaults: @@ -251,10 +242,8 @@ class sdist (Command): else: self.read_manifest() - # get_file_list () - - def add_defaults (self): + def add_defaults(self): """Add all the default files to self.filelist: - README or README.txt - setup.py @@ -265,15 +254,14 @@ class sdist (Command): Warns if (README or README.txt) or setup.py are missing; everything else is optional. """ - standards = [('README', 'README.txt'), self.distribution.script_name] for fn in standards: - if type(fn) is TupleType: + if isinstance(fn, tuple): alts = fn - got_it = 0 + got_it = False for fn in alts: if os.path.exists(fn): - got_it = 1 + got_it = True self.filelist.append(fn) break @@ -308,25 +296,18 @@ class sdist (Command): build_scripts = self.get_finalized_command('build_scripts') self.filelist.extend(build_scripts.get_source_files()) - # add_defaults () - - - def read_template (self): + def read_template(self): """Read and parse manifest template file named by self.template. (usually "MANIFEST.in") The parsing and processing is done by 'self.filelist', which updates itself accordingly. """ log.info("reading manifest template '%s'", self.template) - template = TextFile(self.template, - strip_comments=1, - skip_blanks=1, - join_lines=1, - lstrip_ws=1, - rstrip_ws=1, + template = TextFile(self.template, strip_comments=1, skip_blanks=1, + join_lines=1, lstrip_ws=1, rstrip_ws=1, collapse_join=1) - while 1: + while True: line = template.readline() if line is None: # end of file break @@ -338,10 +319,7 @@ class sdist (Command): template.current_line, msg)) - # read_template () - - - def prune_file_list (self): + def prune_file_list(self): """Prune off branches that might slip into the file list as created by 'read_template()', but really don't belong there: * the build tree (typically "build") @@ -356,8 +334,7 @@ class sdist (Command): self.filelist.exclude_pattern(None, prefix=base_dir) self.filelist.exclude_pattern(r'/(RCS|CVS|\.svn)/.*', is_regex=1) - - def write_manifest (self): + def write_manifest(self): """Write the file list in 'self.filelist' (presumably as filled in by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. @@ -366,17 +343,14 @@ class sdist (Command): (self.manifest, self.filelist.files), "writing manifest file '%s'" % self.manifest) - # write_manifest () - - - def read_manifest (self): + def read_manifest(self): """Read the manifest file (named by 'self.manifest') and use it to fill in 'self.filelist', the list of files to include in the source distribution. """ log.info("reading manifest file '%s'", self.manifest) manifest = open(self.manifest) - while 1: + while True: line = manifest.readline() if line == '': # end of file break @@ -384,10 +358,7 @@ class sdist (Command): line = line[0:-1] self.filelist.append(line) - # read_manifest () - - - def make_release_tree (self, base_dir, files): + def make_release_tree(self, base_dir, files): """Create the directory tree that will become the source distribution archive. All directories implied by the filenames in 'files' are created under 'base_dir', and then we hard link or copy @@ -429,9 +400,7 @@ class sdist (Command): self.distribution.metadata.write_pkg_info(base_dir) - # make_release_tree () - - def make_distribution (self): + def make_distribution(self): """Create the source distribution(s). First, we create the release tree with 'make_release_tree()'; then, we create all required archive files (according to 'self.formats') from the release tree. @@ -456,10 +425,8 @@ class sdist (Command): if not self.keep_temp: dir_util.remove_tree(base_dir, dry_run=self.dry_run) - def get_archive_files (self): + def get_archive_files(self): """Return the list of archive files created when the command was run, or None if the command hasn't run yet. """ return self.archive_files - -# class sdist diff --git a/command/upload.py b/command/upload.py index 1ca2fb90..b49acd12 100644 --- a/command/upload.py +++ b/command/upload.py @@ -170,7 +170,7 @@ class upload(Command): elif schema == 'https': http = httplib.HTTPSConnection(netloc) else: - raise AssertionError, "unsupported schema "+schema + raise AssertionError("unsupported schema "+schema) data = '' loglevel = log.INFO diff --git a/core.py b/core.py index d6099824..0490e637 100644 --- a/core.py +++ b/core.py @@ -6,12 +6,9 @@ indirectly provides the Distribution and Command classes, although they are really defined in distutils.dist and distutils.cmd. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os -from types import * from distutils.debug import DEBUG from distutils.errors import * @@ -112,10 +109,10 @@ def setup (**attrs): _setup_distribution = dist = klass(attrs) except DistutilsSetupError as msg: if 'name' not in attrs: - raise SystemExit, "error in setup command: %s" % msg + raise SystemExit("error in setup command: %s" % msg) else: - raise SystemExit, "error in %s setup command: %s" % \ - (attrs['name'], msg) + raise SystemExit("error in %s setup command: %s" % \ + (attrs['name'], msg)) if _setup_stop_after == "init": return dist @@ -136,7 +133,7 @@ def setup (**attrs): try: ok = dist.parse_command_line() except DistutilsArgError as msg: - raise SystemExit, gen_usage(dist.script_name) + "\nerror: %s" % msg + raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg) if DEBUG: print("options (after parsing command line):") @@ -150,7 +147,7 @@ def setup (**attrs): try: dist.run_commands() except KeyboardInterrupt: - raise SystemExit, "interrupted" + raise SystemExit("interrupted") except (IOError, os.error) as exc: error = grok_environment_error(exc) @@ -158,14 +155,14 @@ def setup (**attrs): sys.stderr.write(error + "\n") raise else: - raise SystemExit, error + raise SystemExit(error) except (DistutilsError, CCompilerError) as msg: if DEBUG: raise else: - raise SystemExit, "error: " + str(msg) + raise SystemExit("error: " + str(msg)) return dist @@ -204,7 +201,7 @@ def run_setup (script_name, script_args=None, stop_after="run"): used to drive the Distutils. """ if stop_after not in ('init', 'config', 'commandline', 'run'): - raise ValueError, "invalid value for 'stop_after': %r" % (stop_after,) + raise ValueError("invalid value for 'stop_after': %r" % (stop_after,)) global _setup_stop_after, _setup_distribution _setup_stop_after = stop_after @@ -229,10 +226,9 @@ def run_setup (script_name, script_args=None, stop_after="run"): raise if _setup_distribution is None: - raise RuntimeError, \ - ("'distutils.core.setup()' was never called -- " + raise RuntimeError(("'distutils.core.setup()' was never called -- " "perhaps '%s' is not a Distutils setup script?") % \ - script_name + script_name) # I wonder if the setup script's namespace -- g and l -- would be of # any interest to callers? diff --git a/cygwinccompiler.py b/cygwinccompiler.py index fae68481..bec72ca3 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -45,8 +45,6 @@ cygwin in no-cygwin mode). # * mingw gcc 3.2/ld 2.13 works # (ld supports -shared) -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os,sys,copy @@ -143,13 +141,13 @@ class CygwinCCompiler (UnixCCompiler): try: self.spawn(["windres", "-i", src, "-o", obj]) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) else: # for other files use the C-compiler try: self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) def link (self, target_desc, @@ -260,9 +258,8 @@ class CygwinCCompiler (UnixCCompiler): # use normcase to make sure '.rc' is really '.rc' and not '.RC' (base, ext) = os.path.splitext (os.path.normcase(src_name)) if ext not in (self.src_extensions + ['.rc','.res']): - raise UnknownFileError, \ - "unknown file type '%s' (from '%s')" % \ - (ext, src_name) + raise UnknownFileError("unknown file type '%s' (from '%s')" % \ + (ext, src_name)) if strip_dir: base = os.path.basename (base) if ext == '.res' or ext == '.rc': diff --git a/debug.py b/debug.py index b67139c7..28867444 100644 --- a/debug.py +++ b/debug.py @@ -1,7 +1,5 @@ import os -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" # If DISTUTILS_DEBUG is anything other than the empty string, we run in diff --git a/dep_util.py b/dep_util.py index c139c852..a9d589a2 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,8 +4,6 @@ Utility functions for simple, timestamp-based dependency of files and groups of files; also, function based entirely on such timestamp dependency analysis.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os @@ -19,7 +17,7 @@ def newer (source, target): Raise DistutilsFileError if 'source' does not exist. """ if not os.path.exists(source): - raise DistutilsFileError, "file '%s' does not exist" % source + raise DistutilsFileError("file '%s' does not exist" % source) if not os.path.exists(target): return 1 @@ -39,7 +37,7 @@ def newer_pairwise (sources, targets): of 'newer()'. """ if len(sources) != len(targets): - raise ValueError, "'sources' and 'targets' must be same length" + raise ValueError("'sources' and 'targets' must be same length") # build a pair of lists (sources, targets) where source is newer n_sources = [] diff --git a/dir_util.py b/dir_util.py index 7dc1205c..30e352db 100644 --- a/dir_util.py +++ b/dir_util.py @@ -2,12 +2,9 @@ Utility functions for manipulating directories and directory trees.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os, sys -from types import * from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log @@ -32,8 +29,8 @@ def mkpath (name, mode=0o777, verbose=0, dry_run=0): # Detect a common bug -- name is None if not isinstance(name, basestring): - raise DistutilsInternalError, \ - "mkpath: 'name' must be a string (got %r)" % (name,) + raise DistutilsInternalError( + "mkpath: 'name' must be a string (got %r)" % (name,)) # XXX what's the better way to handle verbosity? print as we create # each directory in the path (the current behaviour), or only announce @@ -136,8 +133,8 @@ def copy_tree (src, dst, from distutils.file_util import copy_file if not dry_run and not os.path.isdir(src): - raise DistutilsFileError, \ - "cannot copy tree '%s': not a directory" % src + raise DistutilsFileError( + "cannot copy tree '%s': not a directory" % src) try: names = os.listdir(src) except os.error as e: @@ -145,8 +142,8 @@ def copy_tree (src, dst, if dry_run: names = [] else: - raise DistutilsFileError, \ - "error listing files in '%s': %s" % (src, errstr) + raise DistutilsFileError( + "error listing files in '%s': %s" % (src, errstr)) if not dry_run: mkpath(dst) @@ -176,8 +173,6 @@ def copy_tree (src, dst, return outputs -# copy_tree () - # Helper for remove_tree() def _build_cmdtuple(path, cmdtuples): for f in os.listdir(path): diff --git a/dist.py b/dist.py index 8f614765..974ee516 100644 --- a/dist.py +++ b/dist.py @@ -4,8 +4,6 @@ Provides the Distribution class, which represents the module distribution being built/installed/distributed. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, re @@ -264,8 +262,6 @@ Common commands: (see '--help-commands' for more) self.finalize_options() - # __init__ () - def get_option_dict (self, command): """Get the option dictionary for a given command. If that @@ -305,8 +301,6 @@ Common commands: (see '--help-commands' for more) for line in out.split("\n"): print(indent + " " + line) - # dump_option_dicts () - # -- Config file finding/parsing methods --------------------------- @@ -353,8 +347,6 @@ Common commands: (see '--help-commands' for more) return files - # find_config_files () - def parse_config_files (self, filenames=None): @@ -397,9 +389,7 @@ Common commands: (see '--help-commands' for more) else: setattr(self, opt, val) except ValueError as msg: - raise DistutilsOptionError, msg - - # parse_config_files () + raise DistutilsOptionError(msg) # -- Command-line parsing methods ---------------------------------- @@ -472,12 +462,10 @@ Common commands: (see '--help-commands' for more) # Oops, no commands found -- an end-user error if not self.commands: - raise DistutilsArgError, "no commands supplied" + raise DistutilsArgError("no commands supplied") # All is well: return true - return 1 - - # parse_command_line() + return True def _get_toplevel_options (self): """Return the non-display options recognized at the top level. @@ -505,7 +493,7 @@ Common commands: (see '--help-commands' for more) # Pull the current command from the head of the command line command = args[0] if not command_re.match(command): - raise SystemExit, "invalid command name '%s'" % command + raise SystemExit("invalid command name '%s'" % command) self.commands.append(command) # Dig up the command class that implements this command, so we @@ -514,22 +502,21 @@ Common commands: (see '--help-commands' for more) try: cmd_class = self.get_command_class(command) except DistutilsModuleError as msg: - raise DistutilsArgError, msg + raise DistutilsArgError(msg) # Require that the command class be derived from Command -- want # to be sure that the basic "command" interface is implemented. if not issubclass(cmd_class, Command): - raise DistutilsClassError, \ - "command class %s must subclass Command" % cmd_class + raise DistutilsClassError( + "command class %s must subclass Command" % cmd_class) # Also make sure that the command object provides a list of its # known options. if not (hasattr(cmd_class, 'user_options') and isinstance(cmd_class.user_options, list)): - raise DistutilsClassError, \ - ("command class %s must provide " + + raise DistutilsClassError(("command class %s must provide " + "'user_options' attribute (a list of tuples)") % \ - cmd_class + cmd_class) # If the command class has a list of negative alias options, # merge it in with the global negative aliases. @@ -586,8 +573,6 @@ Common commands: (see '--help-commands' for more) return args - # _parse_command_opts () - def finalize_options (self): """Set final values for all the options on the Distribution instance, analogous to the .finalize_options() method of Command @@ -660,8 +645,6 @@ Common commands: (see '--help-commands' for more) print(gen_usage(self.script_name)) return - # _show_help () - def handle_display_options (self, option_order): """If there were any non-global "display-only" options @@ -703,13 +686,10 @@ Common commands: (see '--help-commands' for more) return any_display_options - # handle_display_options() - def print_command_list (self, commands, header, max_length): """Print a subset of the list of all commands -- used by 'print_commands()'. """ - print(header + ":") for cmd in commands: @@ -723,8 +703,6 @@ Common commands: (see '--help-commands' for more) print(" %-*s %s" % (max_length, cmd, description)) - # print_command_list () - def print_commands (self): """Print out a help message listing all available commands with a @@ -734,7 +712,6 @@ Common commands: (see '--help-commands' for more) descriptions come from the command class attribute 'description'. """ - import distutils.command std_commands = distutils.command.__all__ is_std = {} @@ -760,8 +737,6 @@ Common commands: (see '--help-commands' for more) "Extra commands", max_length) - # print_commands () - def get_command_list (self): """Get a list of (command, description) tuples. The list is divided into "standard commands" (listed in @@ -771,7 +746,6 @@ Common commands: (see '--help-commands' for more) """ # Currently this is only used on Mac OS, for the Mac-only GUI # Distutils interface (by Jack Jansen) - import distutils.command std_commands = distutils.command.__all__ is_std = {} @@ -839,18 +813,15 @@ Common commands: (see '--help-commands' for more) try: klass = getattr(module, klass_name) except AttributeError: - raise DistutilsModuleError, \ - "invalid command '%s' (no class '%s' in module '%s')" \ - % (command, klass_name, module_name) + raise DistutilsModuleError( + "invalid command '%s' (no class '%s' in module '%s')" + % (command, klass_name, module_name)) self.cmdclass[command] = klass return klass raise DistutilsModuleError("invalid command '%s'" % command) - - # get_command_class () - def get_command_obj (self, command, create=1): """Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command @@ -912,11 +883,11 @@ Common commands: (see '--help-commands' for more) elif hasattr(command_obj, option): setattr(command_obj, option, value) else: - raise DistutilsOptionError, \ - ("error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) + raise DistutilsOptionError( + "error in %s: command '%s' has no such option '%s'" + % (source, command_name, option)) except ValueError as msg: - raise DistutilsOptionError, msg + raise DistutilsOptionError(msg) def reinitialize_command (self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first @@ -1075,8 +1046,6 @@ class DistributionMetadata: pkg_info.close() - # write_pkg_info () - def write_pkg_file (self, file): """Write the PKG-INFO format data to a file object. """ @@ -1202,8 +1171,6 @@ class DistributionMetadata: distutils.versionpredicate.VersionPredicate(v) self.obsoletes = value -# class DistributionMetadata - def fix_help_options (options): """Convert a 4-tuple 'help_options' list as found in various command diff --git a/emxccompiler.py b/emxccompiler.py index f4b90dcd..d9ee82d5 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -80,13 +80,13 @@ class EMXCCompiler (UnixCCompiler): try: self.spawn(["rc", "-r", src]) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) else: # for other files use the C-compiler try: self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) def link (self, target_desc, @@ -189,9 +189,8 @@ class EMXCCompiler (UnixCCompiler): # use normcase to make sure '.rc' is really '.rc' and not '.RC' (base, ext) = os.path.splitext (os.path.normcase(src_name)) if ext not in (self.src_extensions + ['.rc']): - raise UnknownFileError, \ - "unknown file type '%s' (from '%s')" % \ - (ext, src_name) + raise UnknownFileError("unknown file type '%s' (from '%s')" % \ + (ext, src_name)) if strip_dir: base = os.path.basename (base) if ext == '.rc': diff --git a/errors.py b/errors.py index e72221bd..963d8337 100644 --- a/errors.py +++ b/errors.py @@ -8,8 +8,6 @@ usually raised for errors that are obviously the end-user's fault This module is safe to use in "from ... import *" mode; it only exports symbols whose names start with "Distutils" and end with "Error".""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" class DistutilsError (Exception): diff --git a/extension.py b/extension.py index 43b0d3fd..7f5954e4 100644 --- a/extension.py +++ b/extension.py @@ -86,7 +86,7 @@ class Extension: # When adding arguments to this constructor, be sure to update # setup_keywords in core.py. - def __init__ (self, name, sources, + def __init__(self, name, sources, include_dirs=None, define_macros=None, undef_macros=None, @@ -125,17 +125,15 @@ class Extension: # If there are unknown keyword options, warn about them if len(kw): - L = kw.keys() ; L.sort() - L = map(repr, L) + L = map(repr, sorted(kw)) msg = "Unknown Extension options: " + ', '.join(L) if warnings is not None: warnings.warn(msg) else: sys.stderr.write(msg + '\n') -# class Extension -def read_setup_file (filename): +def read_setup_file(filename): from distutils.sysconfig import \ parse_makefile, expand_makefile_vars, _variable_rx from distutils.text_file import TextFile @@ -151,7 +149,7 @@ def read_setup_file (filename): lstrip_ws=1, rstrip_ws=1) extensions = [] - while 1: + while True: line = file.readline() if line is None: # eof break @@ -241,5 +239,3 @@ def read_setup_file (filename): # 'lib_args': library_args } return extensions - -# read_setup_file () diff --git a/fancy_getopt.py b/fancy_getopt.py index 82e1f4d1..5434334e 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,12 +8,9 @@ additional features: * options set attributes of a passed-in object """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, string, re -from types import * import getopt from distutils.errors import * @@ -43,8 +40,7 @@ class FancyGetopt: on the command line sets 'verbose' to false """ - def __init__ (self, option_table=None): - + def __init__(self, option_table=None): # The option table is (currently) a list of tuples. The # tuples may have 3 or four values: # (long_option, short_option, help_string [, repeatable]) @@ -84,58 +80,51 @@ class FancyGetopt: # but expands short options, converts aliases, etc. self.option_order = [] - # __init__ () - - - def _build_index (self): + def _build_index(self): self.option_index.clear() for option in self.option_table: self.option_index[option[0]] = option - def set_option_table (self, option_table): + def set_option_table(self, option_table): self.option_table = option_table self._build_index() - def add_option (self, long_option, short_option=None, help_string=None): + def add_option(self, long_option, short_option=None, help_string=None): if long_option in self.option_index: - raise DistutilsGetoptError, \ - "option conflict: already an option '%s'" % long_option + raise DistutilsGetoptError( + "option conflict: already an option '%s'" % long_option) else: option = (long_option, short_option, help_string) self.option_table.append(option) self.option_index[long_option] = option - - def has_option (self, long_option): + def has_option(self, long_option): """Return true if the option table for this parser has an option with long name 'long_option'.""" return long_option in self.option_index - def get_attr_name (self, long_option): + def get_attr_name(self, long_option): """Translate long option name 'long_option' to the form it has as an attribute of some object: ie., translate hyphens to underscores.""" return long_option.translate(longopt_xlate) - - def _check_alias_dict (self, aliases, what): - assert type(aliases) is DictionaryType + def _check_alias_dict(self, aliases, what): + assert isinstance(aliases, dict) for (alias, opt) in aliases.items(): if alias not in self.option_index: - raise DistutilsGetoptError, \ - ("invalid %s '%s': " - "option '%s' not defined") % (what, alias, alias) + raise DistutilsGetoptError(("invalid %s '%s': " + "option '%s' not defined") % (what, alias, alias)) if opt not in self.option_index: - raise DistutilsGetoptError, \ - ("invalid %s '%s': " - "aliased option '%s' not defined") % (what, alias, opt) + raise DistutilsGetoptError(("invalid %s '%s': " + "aliased option '%s' not defined") % (what, alias, opt)) - def set_aliases (self, alias): + def set_aliases(self, alias): """Set the aliases for this option parser.""" self._check_alias_dict(alias, "alias") self.alias = alias - def set_negative_aliases (self, negative_alias): + def set_negative_aliases(self, negative_alias): """Set the negative aliases for this option parser. 'negative_alias' should be a dictionary mapping option names to option names, both the key and value must already be defined @@ -143,8 +132,7 @@ class FancyGetopt: self._check_alias_dict(negative_alias, "negative alias") self.negative_alias = negative_alias - - def _grok_option_table (self): + def _grok_option_table(self): """Populate the various data structures that keep tabs on the option table. Called by 'getopt()' before it can do anything worthwhile. @@ -163,19 +151,17 @@ class FancyGetopt: else: # the option table is part of the code, so simply # assert that it is correct - raise ValueError, "invalid option tuple: %r" % (option,) + raise ValueError("invalid option tuple: %r" % (option,)) # Type- and value-check the option names if not isinstance(long, basestring) or len(long) < 2: - raise DistutilsGetoptError, \ - ("invalid long option '%s': " - "must be a string of length >= 2") % long + raise DistutilsGetoptError(("invalid long option '%s': " + "must be a string of length >= 2") % long) if (not ((short is None) or (isinstance(short, basestring) and len(short) == 1))): - raise DistutilsGetoptError, \ - ("invalid short option '%s': " - "must a single character or None") % short + raise DistutilsGetoptError("invalid short option '%s': " + "must a single character or None" % short) self.repeat[long] = repeat self.long_opts.append(long) @@ -185,54 +171,45 @@ class FancyGetopt: long = long[0:-1] self.takes_arg[long] = 1 else: - # Is option is a "negative alias" for some other option (eg. # "quiet" == "!verbose")? alias_to = self.negative_alias.get(long) if alias_to is not None: if self.takes_arg[alias_to]: - raise DistutilsGetoptError, \ - ("invalid negative alias '%s': " - "aliased option '%s' takes a value") % \ - (long, alias_to) + raise DistutilsGetoptError( + "invalid negative alias '%s': " + "aliased option '%s' takes a value" + % (long, alias_to)) self.long_opts[-1] = long # XXX redundant?! - self.takes_arg[long] = 0 - - else: - self.takes_arg[long] = 0 + self.takes_arg[long] = 0 # If this is an alias option, make sure its "takes arg" flag is # the same as the option it's aliased to. alias_to = self.alias.get(long) if alias_to is not None: if self.takes_arg[long] != self.takes_arg[alias_to]: - raise DistutilsGetoptError, \ - ("invalid alias '%s': inconsistent with " - "aliased option '%s' (one of them takes a value, " - "the other doesn't") % (long, alias_to) - + raise DistutilsGetoptError( + "invalid alias '%s': inconsistent with " + "aliased option '%s' (one of them takes a value, " + "the other doesn't" + % (long, alias_to)) # Now enforce some bondage on the long option name, so we can # later translate it to an attribute name on some object. Have # to do this a bit late to make sure we've removed any trailing # '='. if not longopt_re.match(long): - raise DistutilsGetoptError, \ - ("invalid long option name '%s' " + - "(must be letters, numbers, hyphens only") % long + raise DistutilsGetoptError( + "invalid long option name '%s' " + "(must be letters, numbers, hyphens only" % long) self.attr_name[long] = self.get_attr_name(long) if short: self.short_opts.append(short) self.short2long[short[0]] = long - # for option_table - - # _grok_option_table() - - - def getopt (self, args=None, object=None): + def getopt(self, args=None, object=None): """Parse command-line options in args. Store as attributes on object. If 'args' is None or not supplied, uses 'sys.argv[1:]'. If @@ -247,9 +224,9 @@ class FancyGetopt: args = sys.argv[1:] if object is None: object = OptionDummy() - created_object = 1 + created_object = True else: - created_object = 0 + created_object = False self._grok_option_table() @@ -257,7 +234,7 @@ class FancyGetopt: try: opts, args = getopt.getopt(args, short_opts, self.long_opts) except getopt.error as msg: - raise DistutilsArgError, msg + raise DistutilsArgError(msg) for opt, val in opts: if len(opt) == 2 and opt[0] == '-': # it's a short option @@ -293,21 +270,17 @@ class FancyGetopt: else: return args - # getopt() - - - def get_option_order (self): + def get_option_order(self): """Returns the list of (option, value) tuples processed by the previous run of 'getopt()'. Raises RuntimeError if 'getopt()' hasn't been called yet. """ if self.option_order is None: - raise RuntimeError, "'getopt()' hasn't been called yet" + raise RuntimeError("'getopt()' hasn't been called yet") else: return self.option_order - - def generate_help (self, header=None): + def generate_help(self, header=None): """Generate help text (a list of strings, one per suggested line of output) from the option table for this FancyGetopt object. """ @@ -384,23 +357,16 @@ class FancyGetopt: for l in text[1:]: lines.append(big_indent + l) - - # for self.option_table - return lines - # generate_help () - - def print_help (self, header=None, file=None): + def print_help(self, header=None, file=None): if file is None: file = sys.stdout for line in self.generate_help(header): file.write(line + "\n") -# class FancyGetopt - -def fancy_getopt (options, negative_opt, object, args): +def fancy_getopt(options, negative_opt, object, args): parser = FancyGetopt(options) parser.set_negative_aliases(negative_opt) return parser.getopt(args, object) @@ -408,13 +374,12 @@ def fancy_getopt (options, negative_opt, object, args): WS_TRANS = string.maketrans(string.whitespace, ' ' * len(string.whitespace)) -def wrap_text (text, width): +def wrap_text(text, width): """wrap_text(text : string, width : int) -> [string] Split 'text' into multiple lines of no more than 'width' characters each, and return the list of strings that results. """ - if text is None: return [] if len(text) <= width: @@ -427,7 +392,6 @@ def wrap_text (text, width): lines = [] while chunks: - cur_line = [] # list of chunks (to-be-joined) cur_len = 0 # length of current line @@ -444,7 +408,6 @@ def wrap_text (text, width): break if chunks: # any chunks left to process? - # if the current line is still empty, then we had a single # chunk that's too big too fit on a line -- so we break # down and break it up at the line width @@ -462,14 +425,10 @@ def wrap_text (text, width): # string, of course! lines.append(''.join(cur_line)) - # while chunks - return lines -# wrap_text () - -def translate_longopt (opt): +def translate_longopt(opt): """Convert a long option name to a valid Python identifier by changing "-" to "_". """ @@ -480,14 +439,12 @@ class OptionDummy: """Dummy class just used as a place to hold command-line option values as instance attributes.""" - def __init__ (self, options=[]): + def __init__(self, options=[]): """Create a new OptionDummy instance. The attributes listed in 'options' will be initialized to None.""" for opt in options: setattr(self, opt, None) -# class OptionDummy - if __name__ == "__main__": text = """\ diff --git a/file_util.py b/file_util.py index c225ad31..e29e90e6 100644 --- a/file_util.py +++ b/file_util.py @@ -3,8 +3,6 @@ Utility functions for operating on single files. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os @@ -17,7 +15,7 @@ _copy_action = { None: 'copying', 'sym': 'symbolically linking' } -def _copy_file_contents (src, dst, buffer_size=16*1024): +def _copy_file_contents(src, dst, buffer_size=16*1024): """Copy the file 'src' to 'dst'; both must be filenames. Any error opening either file, reading from 'src', or writing to 'dst', raises DistutilsFileError. Data is read/written in chunks of 'buffer_size' @@ -26,7 +24,6 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): """ # Stolen from shutil module in the standard library, but with # custom error-handling added. - fsrc = None fdst = None try: @@ -34,31 +31,30 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): fsrc = open(src, 'rb') except os.error as e: (errno, errstr) = e - raise DistutilsFileError, \ - "could not open '%s': %s" % (src, errstr) + raise DistutilsFileError("could not open '%s': %s" % (src, errstr)) if os.path.exists(dst): try: os.unlink(dst) except os.error as e: (errno, errstr) = e - raise DistutilsFileError, \ - "could not delete '%s': %s" % (dst, errstr) + raise DistutilsFileError( + "could not delete '%s': %s" % (dst, errstr)) try: fdst = open(dst, 'wb') except os.error as e: (errno, errstr) = e - raise DistutilsFileError, \ - "could not create '%s': %s" % (dst, errstr) + raise DistutilsFileError( + "could not create '%s': %s" % (dst, errstr)) - while 1: + while True: try: buf = fsrc.read(buffer_size) except os.error as e: (errno, errstr) = e - raise DistutilsFileError, \ - "could not read from '%s': %s" % (src, errstr) + raise DistutilsFileError( + "could not read from '%s': %s" % (src, errstr)) if not buf: break @@ -67,25 +63,16 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): fdst.write(buf) except os.error as e: (errno, errstr) = e - raise DistutilsFileError, \ - "could not write to '%s': %s" % (dst, errstr) - + raise DistutilsFileError( + "could not write to '%s': %s" % (dst, errstr)) finally: if fdst: fdst.close() if fsrc: fsrc.close() -# _copy_file_contents() - -def copy_file (src, dst, - preserve_mode=1, - preserve_times=1, - update=0, - link=None, - verbose=0, - dry_run=0): - +def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, + link=None, verbose=0, dry_run=0): """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is copied there with the same name; otherwise, it must be a filename. (If the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' @@ -120,8 +107,8 @@ def copy_file (src, dst, from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE if not os.path.isfile(src): - raise DistutilsFileError, \ - "can't copy '%s': doesn't exist or not a regular file" % src + raise DistutilsFileError( + "can't copy '%s': doesn't exist or not a regular file" % src) if os.path.isdir(dst): dir = dst @@ -131,13 +118,12 @@ def copy_file (src, dst, if update and not newer(src, dst): log.debug("not copying %s (output up-to-date)", src) - return dst, 0 + return (dst, 0) try: action = _copy_action[link] except KeyError: - raise ValueError, \ - "invalid value '%s' for 'link' argument" % link + raise ValueError("invalid value '%s' for 'link' argument" % link) if os.path.basename(dst) == os.path.basename(src): log.info("%s %s -> %s", action, src, dir) else: @@ -152,8 +138,8 @@ def copy_file (src, dst, try: macostools.copy(src, dst, 0, preserve_times) except os.error as exc: - raise DistutilsFileError, \ - "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) + raise DistutilsFileError( + "could not copy '%s' to '%s': %s" % (src, dst, exc[-1])) # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) @@ -180,8 +166,6 @@ def copy_file (src, dst, return (dst, 1) -# copy_file () - # XXX I suspect this is Unix-specific -- need porting help! def move_file (src, dst, @@ -204,31 +188,30 @@ def move_file (src, dst, return dst if not isfile(src): - raise DistutilsFileError, \ - "can't move '%s': not a regular file" % src + raise DistutilsFileError("can't move '%s': not a regular file" % src) if isdir(dst): dst = os.path.join(dst, basename(src)) elif exists(dst): - raise DistutilsFileError, \ - "can't move '%s': destination '%s' already exists" % \ - (src, dst) + raise DistutilsFileError( + "can't move '%s': destination '%s' already exists" % + (src, dst)) if not isdir(dirname(dst)): - raise DistutilsFileError, \ - "can't move '%s': destination '%s' not a valid path" % \ - (src, dst) + raise DistutilsFileError( + "can't move '%s': destination '%s' not a valid path" % + (src, dst)) - copy_it = 0 + copy_it = False try: os.rename(src, dst) except os.error as e: (num, msg) = e if num == errno.EXDEV: - copy_it = 1 + copy_it = True else: - raise DistutilsFileError, \ - "couldn't move '%s' to '%s': %s" % (src, dst, msg) + raise DistutilsFileError( + "couldn't move '%s' to '%s': %s" % (src, dst, msg)) if copy_it: copy_file(src, dst) @@ -240,15 +223,12 @@ def move_file (src, dst, os.unlink(dst) except os.error: pass - raise DistutilsFileError, \ - ("couldn't move '%s' to '%s' by copy/delete: " + - "delete '%s' failed: %s") % \ - (src, dst, src, msg) - + raise DistutilsFileError( + "couldn't move '%s' to '%s' by copy/delete: " + "delete '%s' failed: %s" + % (src, dst, src, msg)) return dst -# move_file () - def write_file (filename, contents): """Create a file with the specified name and write 'contents' (a diff --git a/filelist.py b/filelist.py index cc48e48e..8f801073 100644 --- a/filelist.py +++ b/filelist.py @@ -4,20 +4,16 @@ Provides the FileList class, used for poking about the filesystem and building lists of files. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os, re import fnmatch -from types import * from glob import glob from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsInternalError from distutils import log class FileList: - """A list of files built by on exploring the filesystem and filtered by applying various patterns to what we find there. @@ -32,22 +28,19 @@ class FileList: filtering applied) """ - def __init__(self, - warn=None, - debug_print=None): + def __init__(self, warn=None, debug_print=None): # ignore argument to FileList, but keep them for backwards # compatibility - self.allfiles = None self.files = [] - def set_allfiles (self, allfiles): + def set_allfiles(self, allfiles): self.allfiles = allfiles - def findall (self, dir=os.curdir): + def findall(self, dir=os.curdir): self.allfiles = findall(dir) - def debug_print (self, msg): + def debug_print(self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the DISTUTILS_DEBUG environment variable) flag is true. """ @@ -57,13 +50,13 @@ class FileList: # -- List-like methods --------------------------------------------- - def append (self, item): + def append(self, item): self.files.append(item) - def extend (self, items): + def extend(self, items): self.files.extend(items) - def sort (self): + def sort(self): # Not a strict lexical sort! sortable_files = sorted(map(os.path.split, self.files)) self.files = [] @@ -73,7 +66,7 @@ class FileList: # -- Other miscellaneous utility methods --------------------------- - def remove_duplicates (self): + def remove_duplicates(self): # Assumes list has been sorted! for i in range(len(self.files) - 1, 0, -1): if self.files[i] == self.files[i - 1]: @@ -82,7 +75,7 @@ class FileList: # -- "File template" methods --------------------------------------- - def _parse_template_line (self, line): + def _parse_template_line(self, line): words = line.split() action = words[0] @@ -91,36 +84,26 @@ class FileList: if action in ('include', 'exclude', 'global-include', 'global-exclude'): if len(words) < 2: - raise DistutilsTemplateError, \ - "'%s' expects ..." % action - + raise DistutilsTemplateError( + "'%s' expects ..." % action) patterns = map(convert_path, words[1:]) - elif action in ('recursive-include', 'recursive-exclude'): if len(words) < 3: - raise DistutilsTemplateError, \ - "'%s' expects ..." % action - + raise DistutilsTemplateError( + "'%s' expects ..." % action) dir = convert_path(words[1]) patterns = map(convert_path, words[2:]) - elif action in ('graft', 'prune'): if len(words) != 2: - raise DistutilsTemplateError, \ - "'%s' expects a single " % action - + raise DistutilsTemplateError( + "'%s' expects a single " % action) dir_pattern = convert_path(words[1]) - else: - raise DistutilsTemplateError, "unknown action '%s'" % action + raise DistutilsTemplateError("unknown action '%s'" % action) return (action, patterns, dir, dir_pattern) - # _parse_template_line () - - - def process_template_line (self, line): - + def process_template_line(self, line): # Parse the line: split it up, make sure the right number of words # is there, and return the relevant words. 'action' is always # defined: it's the first word of the line. Which of the other @@ -149,7 +132,7 @@ class FileList: self.debug_print("global-include " + ' '.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=0): - log.warn(("warning: no files found matching '%s' " + + log.warn(("warning: no files found matching '%s' " "anywhere in distribution"), pattern) elif action == 'global-exclude': @@ -165,7 +148,7 @@ class FileList: (dir, ' '.join(patterns))) for pattern in patterns: if not self.include_pattern(pattern, prefix=dir): - log.warn(("warning: no files found matching '%s' " + + log.warn(("warning: no files found matching '%s' " "under directory '%s'"), pattern, dir) @@ -187,19 +170,16 @@ class FileList: elif action == 'prune': self.debug_print("prune " + dir_pattern) if not self.exclude_pattern(None, prefix=dir_pattern): - log.warn(("no previously-included directories found " + + log.warn(("no previously-included directories found " "matching '%s'"), dir_pattern) else: - raise DistutilsInternalError, \ - "this cannot happen: invalid action '%s'" % action - - # process_template_line () + raise DistutilsInternalError( + "this cannot happen: invalid action '%s'" % action) # -- Filtering/selection methods ----------------------------------- - def include_pattern (self, pattern, - anchor=1, prefix=None, is_regex=0): + def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): """Select strings (presumably filenames) from 'self.files' that match 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not quite the same as implemented by the 'fnmatch' module: '*' @@ -222,9 +202,9 @@ class FileList: Selected strings will be added to self.files. - Return 1 if files are found. + Return True if files are found, False otherwise. """ - files_found = 0 + files_found = False pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) self.debug_print("include_pattern: applying regex r'%s'" % pattern_re.pattern) @@ -237,12 +217,9 @@ class FileList: if pattern_re.search(name): self.debug_print(" adding " + name) self.files.append(name) - files_found = 1 - + files_found = True return files_found - # include_pattern () - def exclude_pattern (self, pattern, anchor=1, prefix=None, is_regex=0): @@ -250,9 +227,9 @@ class FileList: 'pattern'. Other parameters are the same as for 'include_pattern()', above. The list 'self.files' is modified in place. - Return 1 if files are found. + Return True if files are found, False otherwise. """ - files_found = 0 + files_found = False pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) self.debug_print("exclude_pattern: applying regex r'%s'" % pattern_re.pattern) @@ -260,19 +237,14 @@ class FileList: if pattern_re.search(self.files[i]): self.debug_print(" removing " + self.files[i]) del self.files[i] - files_found = 1 - + files_found = True return files_found - # exclude_pattern () - -# class FileList - # ---------------------------------------------------------------------- # Utility functions -def findall (dir = os.curdir): +def findall(dir=os.curdir): """Find all files under 'dir' and return the list of full filenames (relative to 'dir'). """ @@ -300,11 +272,10 @@ def findall (dir = os.curdir): list.append(fullname) elif S_ISDIR(mode) and not S_ISLNK(mode): push(fullname) - return list -def glob_to_re (pattern): +def glob_to_re(pattern): """Translate a shell-like glob pattern to a regular expression; return a string containing the regex. Differs from 'fnmatch.translate()' in that '*' does not match "special characters" (which are @@ -322,10 +293,8 @@ def glob_to_re (pattern): pattern_re = re.sub(r'(^|[^\\])\.', r'\1[^/]', pattern_re) return pattern_re -# glob_to_re () - -def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): +def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): """Translate a shell-like wildcard pattern to a compiled regular expression. Return the compiled regex. If 'is_regex' true, then 'pattern' is directly compiled to a regex (if it's a string) @@ -350,5 +319,3 @@ def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): pattern_re = "^" + pattern_re return re.compile(pattern_re) - -# translate_pattern () diff --git a/log.py b/log.py index e4959d64..97319a07 100644 --- a/log.py +++ b/log.py @@ -1,7 +1,5 @@ """A simple log mechanism styled after PEP 282.""" -# This module should be kept compatible with Python 2.1. - # The class here is styled after PEP 282 so that it could later be # replaced with a standard Python logging implementation. diff --git a/msvccompiler.py b/msvccompiler.py index 07c76f18..f5b67049 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -8,8 +8,6 @@ for the Microsoft Visual Studio. # hacked by Robin Becker and Thomas Heller to do a better job of # finding DevStudio (through the registry) -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os @@ -20,11 +18,11 @@ from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options from distutils import log -_can_read_reg = 0 +_can_read_reg = False try: import _winreg - _can_read_reg = 1 + _can_read_reg = True hkey_mod = _winreg RegOpenKeyEx = _winreg.OpenKeyEx @@ -36,14 +34,13 @@ except ImportError: try: import win32api import win32con - _can_read_reg = 1 + _can_read_reg = True hkey_mod = win32con RegOpenKeyEx = win32api.RegOpenKeyEx RegEnumKey = win32api.RegEnumKey RegEnumValue = win32api.RegEnumValue RegError = win32api.error - except ImportError: log.info("Warning: Can't read registry to find the " "necessary compiler setting\n" @@ -59,20 +56,19 @@ if _can_read_reg: def read_keys(base, key): """Return list of registry keys.""" - try: handle = RegOpenKeyEx(base, key) except RegError: return None L = [] i = 0 - while 1: + while True: try: k = RegEnumKey(handle, i) except RegError: break L.append(k) - i = i + 1 + i += 1 return L def read_values(base, key): @@ -86,14 +82,14 @@ def read_values(base, key): return None d = {} i = 0 - while 1: + while True: try: name, value, type = RegEnumValue(handle, i) except RegError: break name = name.lower() d[convert_mbcs(name)] = convert_mbcs(value) - i = i + 1 + i += 1 return d def convert_mbcs(s): @@ -106,7 +102,6 @@ def convert_mbcs(s): return s class MacroExpander: - def __init__(self, version): self.macros = {} self.load_macros(version) @@ -130,8 +125,8 @@ class MacroExpander: else: self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") except KeyError as exc: # - raise DistutilsPlatformError, \ - ("""Python was built with Visual Studio 2003; + raise DistutilsPlatformError( + """Python was built with Visual Studio 2003; extensions must be built with a compiler than can generate compatible binaries. Visual Studio 2003 was not found on this system. If you have Cygwin installed, you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") @@ -157,7 +152,6 @@ def get_build_version(): For Python 2.3 and up, the version number is included in sys.version. For earlier versions, assume the compiler is MSVC 6. """ - prefix = "MSC v." i = sys.version.find(prefix) if i == -1: @@ -202,7 +196,7 @@ def normalize_and_reduce_paths(paths): return reduced_paths -class MSVCCompiler (CCompiler) : +class MSVCCompiler(CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, as defined by the CCompiler abstract class.""" @@ -232,7 +226,7 @@ class MSVCCompiler (CCompiler) : static_lib_format = shared_lib_format = '%s%s' exe_extension = '.exe' - def __init__ (self, verbose=0, dry_run=0, force=0): + def __init__(self, verbose=0, dry_run=0, force=0): CCompiler.__init__ (self, verbose, dry_run, force) self.__version = get_build_version() self.__arch = get_build_architecture() @@ -263,11 +257,11 @@ class MSVCCompiler (CCompiler) : else: self.__paths = self.get_msvc_paths("path") - if len (self.__paths) == 0: - raise DistutilsPlatformError, \ - ("Python was built with %s, " + if len(self.__paths) == 0: + raise DistutilsPlatformError("Python was built with %s, " "and extensions need to be built with the same " - "version of the compiler, but it isn't installed." % self.__product) + "version of the compiler, but it isn't installed." + % self.__product) self.cc = self.find_exe("cl.exe") self.linker = self.find_exe("link.exe") @@ -314,10 +308,10 @@ class MSVCCompiler (CCompiler) : # -- Worker methods ------------------------------------------------ - def object_filenames (self, - source_filenames, - strip_dir=0, - output_dir=''): + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): # Copied from ccompiler.py, extended to return .res as 'object'-file # for .rc input file if output_dir is None: output_dir = '' @@ -344,17 +338,16 @@ class MSVCCompiler (CCompiler) : base + self.obj_extension)) return obj_names - # object_filenames () - def compile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): - if not self.initialized: self.initialize() - macros, objects, extra_postargs, pp_opts, build = \ - self._setup_compile(output_dir, macros, include_dirs, sources, - depends, extra_postargs) + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info compile_opts = extra_preargs or [] compile_opts.append ('/c') @@ -383,13 +376,12 @@ class MSVCCompiler (CCompiler) : input_opt = src output_opt = "/fo" + obj try: - self.spawn ([self.rc] + pp_opts + - [output_opt] + [input_opt]) + self.spawn([self.rc] + pp_opts + + [output_opt] + [input_opt]) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) continue elif ext in self._mc_extensions: - # Compile .MC to .RC file to .RES file. # * '-h dir' specifies the directory for the # generated include file @@ -401,99 +393,95 @@ class MSVCCompiler (CCompiler) : # we use the source-directory for the include file and # the build directory for the RC file and message # resources. This works at least for win32all. - - h_dir = os.path.dirname (src) - rc_dir = os.path.dirname (obj) + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) try: # first compile .MC to .RC and .H file - self.spawn ([self.mc] + - ['-h', h_dir, '-r', rc_dir] + [src]) + self.spawn([self.mc] + + ['-h', h_dir, '-r', rc_dir] + [src]) base, _ = os.path.splitext (os.path.basename (src)) rc_file = os.path.join (rc_dir, base + '.rc') # then compile .RC to .RES file - self.spawn ([self.rc] + - ["/fo" + obj] + [rc_file]) + self.spawn([self.rc] + + ["/fo" + obj] + [rc_file]) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) continue else: # how to handle this file? - raise CompileError ( - "Don't know how to compile %s to %s" % \ - (src, obj)) + raise CompileError("Don't know how to compile %s to %s" + % (src, obj)) output_opt = "/Fo" + obj try: - self.spawn ([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs) + self.spawn([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) return objects - # compile () + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): - def create_static_lib (self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) - if not self.initialized: self.initialize() - (objects, output_dir) = self._fix_object_args (objects, output_dir) - output_filename = \ - self.library_filename (output_libname, output_dir=output_dir) - - if self._need_link (objects, output_filename): + if self._need_link(objects, output_filename): lib_args = objects + ['/OUT:' + output_filename] if debug: - pass # XXX what goes here? + pass # XXX what goes here? try: - self.spawn ([self.lib] + lib_args) + self.([self.lib] + lib_args) except DistutilsExecError as msg: - raise LibError, msg - + raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) - # create_static_lib () - - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - if not self.initialized: self.initialize() - (objects, output_dir) = self._fix_object_args (objects, output_dir) - (libraries, library_dirs, runtime_library_dirs) = \ - self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + (libraries, library_dirs, runtime_library_dirs) = fixed_args if runtime_library_dirs: self.warn ("I don't know what to do with 'runtime_library_dirs': " + str (runtime_library_dirs)) - lib_opts = gen_lib_options (self, - library_dirs, runtime_library_dirs, - libraries) + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) if output_dir is not None: - output_filename = os.path.join (output_dir, output_filename) - - if self._need_link (objects, output_filename): + output_filename = os.path.join(output_dir, output_filename) + if self._need_link(objects, output_filename): if target_desc == CCompiler.EXECUTABLE: if debug: ldflags = self.ldflags_shared_debug[1:] @@ -530,34 +518,32 @@ class MSVCCompiler (CCompiler) : if extra_postargs: ld_args.extend(extra_postargs) - self.mkpath (os.path.dirname (output_filename)) + self.mkpath(os.path.dirname(output_filename)) try: - self.spawn ([self.linker] + ld_args) + self.spawn([self.linker] + ld_args) except DistutilsExecError as msg: - raise LinkError, msg + raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) - # link () - # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in # ccompiler.py. - def library_dir_option (self, dir): + def library_dir_option(self, dir): return "/LIBPATH:" + dir - def runtime_library_dir_option (self, dir): - raise DistutilsPlatformError, \ - "don't know how to set runtime library search path for MSVC++" + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC++") - def library_option (self, lib): - return self.library_filename (lib) + def library_option(self, lib): + return self.library_filename(lib) - def find_library_file (self, dirs, lib, debug=0): + def find_library_file(self, dirs, lib, debug=0): # Prefer a debugging library if found (and requested), but deal # with it if we don't have one. if debug: @@ -573,8 +559,6 @@ class MSVCCompiler (CCompiler) : # Oops, didn't find it in *any* of 'dirs' return None - # find_library_file () - # Helper methods for using the MSVC registry settings def find_exe(self, exe): @@ -586,7 +570,6 @@ class MSVCCompiler (CCompiler) : absolute path that is known to exist. If none of them work, just return the original program name, 'exe'. """ - for p in self.__paths: fn = os.path.join(os.path.abspath(p), exe) if os.path.isfile(fn): @@ -606,7 +589,6 @@ class MSVCCompiler (CCompiler) : Return a list of strings. The list will be empty if unable to access the registry or appropriate registry keys not found. """ - if not _can_read_reg: return [] diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 662046ae..25d48ae8 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -4,12 +4,9 @@ Contains MWerksCompiler, an implementation of the abstract CCompiler class for MetroWerks CodeWarrior on the Macintosh. Needs work to support CW on Windows.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os -from types import * from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -96,13 +93,13 @@ class MWerksCompiler (CCompiler) : # First examine a couple of options for things that aren't implemented yet if not target_desc in (self.SHARED_LIBRARY, self.SHARED_OBJECT): - raise DistutilsPlatformError, 'Can only make SHARED_LIBRARY or SHARED_OBJECT targets on the Mac' + raise DistutilsPlatformError('Can only make SHARED_LIBRARY or SHARED_OBJECT targets on the Mac') if runtime_library_dirs: - raise DistutilsPlatformError, 'Runtime library dirs not implemented yet' + raise DistutilsPlatformError('Runtime library dirs not implemented yet') if extra_preargs or extra_postargs: - raise DistutilsPlatformError, 'Runtime library dirs not implemented yet' + raise DistutilsPlatformError('Runtime library dirs not implemented yet') if len(export_symbols) != 1: - raise DistutilsPlatformError, 'Need exactly one export symbol' + raise DistutilsPlatformError('Need exactly one export symbol') # Next there are various things for which we need absolute pathnames. # This is because we (usually) create the project in a subdirectory of # where we are now, and keeping the paths relative is too much work right diff --git a/spawn.py b/spawn.py index c70c10bf..0aee2bc3 100644 --- a/spawn.py +++ b/spawn.py @@ -6,19 +6,13 @@ Also provides the 'find_executable()' to search the path for a given executable name. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os from distutils.errors import * from distutils import log -def spawn (cmd, - search_path=1, - verbose=0, - dry_run=0): - +def spawn(cmd, search_path=1, verbose=0, dry_run=0): """Run another program, specified as a command list 'cmd', in a new process. 'cmd' is just the argument list for the new process, ie. cmd[0] is the program to run and cmd[1:] are the rest of its arguments. @@ -40,34 +34,26 @@ def spawn (cmd, elif os.name == 'os2': _spawn_os2(cmd, search_path, dry_run=dry_run) else: - raise DistutilsPlatformError, \ - "don't know how to spawn programs on platform '%s'" % os.name + raise DistutilsPlatformError( + "don't know how to spawn programs on platform '%s'" % os.name) -# spawn () - -def _nt_quote_args (args): +def _nt_quote_args(args): """Quote command-line arguments for DOS/Windows conventions: just wraps every argument which contains blanks in double quotes, and returns a new argument list. """ - # XXX this doesn't seem very robust to me -- but if the Windows guys # say it'll work, I guess I'll have to accept it. (What if an arg # contains quotes? What other magic characters, other than spaces, # have to be escaped? Is there an escaping mechanism other than # quoting?) - for i in range(len(args)): if args[i].find(' ') != -1: args[i] = '"%s"' % args[i] return args -def _spawn_nt (cmd, - search_path=1, - verbose=0, - dry_run=0): - +def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): executable = cmd[0] cmd = _nt_quote_args(cmd) if search_path: @@ -80,19 +66,15 @@ def _spawn_nt (cmd, rc = os.spawnv(os.P_WAIT, executable, cmd) except OSError as exc: # this seems to happen when the command isn't found - raise DistutilsExecError, \ - "command '%s' failed: %s" % (cmd[0], exc[-1]) + raise DistutilsExecError( + "command '%s' failed: %s" % (cmd[0], exc[-1])) if rc != 0: # and this reflects the command running but failing - raise DistutilsExecError, \ - "command '%s' failed with exit status %d" % (cmd[0], rc) + raise DistutilsExecError( + "command '%s' failed with exit status %d" % (cmd[0], rc)) -def _spawn_os2 (cmd, - search_path=1, - verbose=0, - dry_run=0): - +def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): executable = cmd[0] #cmd = _nt_quote_args(cmd) if search_path: @@ -105,75 +87,62 @@ def _spawn_os2 (cmd, rc = os.spawnv(os.P_WAIT, executable, cmd) except OSError as exc: # this seems to happen when the command isn't found - raise DistutilsExecError, \ - "command '%s' failed: %s" % (cmd[0], exc[-1]) + raise DistutilsExecError( + "command '%s' failed: %s" % (cmd[0], exc[-1])) if rc != 0: # and this reflects the command running but failing print("command '%s' failed with exit status %d" % (cmd[0], rc)) - raise DistutilsExecError, \ - "command '%s' failed with exit status %d" % (cmd[0], rc) - + raise DistutilsExecError( + "command '%s' failed with exit status %d" % (cmd[0], rc)) -def _spawn_posix (cmd, - search_path=1, - verbose=0, - dry_run=0): +def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): log.info(' '.join(cmd)) if dry_run: return exec_fn = search_path and os.execvp or os.execv pid = os.fork() - - if pid == 0: # in the child + if pid == 0: # in the child try: - #print "cmd[0] =", cmd[0] - #print "cmd =", cmd exec_fn(cmd[0], cmd) except OSError as e: - sys.stderr.write("unable to execute %s: %s\n" % - (cmd[0], e.strerror)) + sys.stderr.write("unable to execute %s: %s\n" + % (cmd[0], e.strerror)) os._exit(1) sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0]) os._exit(1) - - - else: # in the parent + else: # in the parent # Loop until the child either exits or is terminated by a signal # (ie. keep waiting if it's merely stopped) - while 1: + while True: try: (pid, status) = os.waitpid(pid, 0) except OSError as exc: import errno if exc.errno == errno.EINTR: continue - raise DistutilsExecError, \ - "command '%s' failed: %s" % (cmd[0], exc[-1]) + raise DistutilsExecError( + "command '%s' failed: %s" % (cmd[0], exc[-1])) if os.WIFSIGNALED(status): - raise DistutilsExecError, \ - "command '%s' terminated by signal %d" % \ - (cmd[0], os.WTERMSIG(status)) - + raise DistutilsExecError( + "command '%s' terminated by signal %d" + % (cmd[0], os.WTERMSIG(status))) elif os.WIFEXITED(status): exit_status = os.WEXITSTATUS(status) if exit_status == 0: return # hey, it succeeded! else: - raise DistutilsExecError, \ - "command '%s' failed with exit status %d" % \ - (cmd[0], exit_status) - + raise DistutilsExecError( + "command '%s' failed with exit status %d" + % (cmd[0], exit_status)) elif os.WIFSTOPPED(status): continue - else: - raise DistutilsExecError, \ - "unknown error executing '%s': termination status %d" % \ - (cmd[0], status) -# _spawn_posix () + raise DistutilsExecError( + "unknown error executing '%s': termination status %d" + % (cmd[0], status)) def find_executable(executable, path=None): @@ -197,5 +166,3 @@ def find_executable(executable, path=None): return None else: return executable - -# find_executable() diff --git a/sysconfig.py b/sysconfig.py index c40c2e74..70a27993 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -27,11 +27,7 @@ EXEC_PREFIX = os.path.normpath(sys.exec_prefix) # different (hard-wired) directories. argv0_path = os.path.dirname(os.path.abspath(sys.executable)) -landmark = os.path.join(argv0_path, "Modules", "Setup") - -python_build = os.path.isfile(landmark) - -del landmark +python_build = os.path.isfile(os.path.join(argv0_path, "Modules", "Setup")) def get_python_version(): @@ -105,7 +101,6 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): return libpython else: return os.path.join(libpython, "site-packages") - elif os.name == "nt": if standard_lib: return os.path.join(prefix, "Lib") @@ -114,7 +109,6 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): return prefix else: return os.path.join(PREFIX, "Lib", "site-packages") - elif os.name == "mac": if plat_specific: if standard_lib: @@ -126,13 +120,11 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): return os.path.join(prefix, "Lib") else: return os.path.join(prefix, "Lib", "site-packages") - elif os.name == "os2": if standard_lib: return os.path.join(PREFIX, "Lib") else: return os.path.join(PREFIX, "Lib", "site-packages") - else: raise DistutilsPlatformError( "I don't know where Python installs its library " @@ -216,7 +208,7 @@ def parse_config_h(fp, g=None): define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") # - while 1: + while True: line = fp.readline() if not line: break @@ -254,9 +246,9 @@ def parse_makefile(fn, g=None): done = {} notdone = {} - while 1: + while True: line = fp.readline() - if line is None: # eof + if line is None: # eof break m = _variable_rx.match(line) if m: @@ -325,7 +317,7 @@ def expand_makefile_vars(s, vars): # 'parse_makefile()', which takes care of such expansions eagerly, # according to make's variable expansion semantics. - while 1: + while True: m = _findvar1_rx.search(s) or _findvar2_rx.search(s) if m: (beg, end) = m.span() diff --git a/text_file.py b/text_file.py index 3f6a220d..db054fd2 100644 --- a/text_file.py +++ b/text_file.py @@ -10,7 +10,6 @@ import sys, os, io class TextFile: - """Provides a file-like object that takes care of all the things you commonly want to do when processing a text file that has some line-by-line syntax: strip comments (as long as "#" is your @@ -75,32 +74,29 @@ class TextFile: 'collapse_join': 0, } - def __init__ (self, filename=None, file=None, **options): + def __init__(self, filename=None, file=None, **options): """Construct a new TextFile object. At least one of 'filename' (a string) and 'file' (a file-like object) must be supplied. They keyword argument options are described above and affect the values returned by 'readline()'.""" - if filename is None and file is None: - raise RuntimeError, \ - "you must supply either or both of 'filename' and 'file'" + raise RuntimeError("you must supply either or both of 'filename' and 'file'") # set values for all options -- either from client option hash # or fallback to default_options for opt in self.default_options.keys(): if opt in options: - setattr (self, opt, options[opt]) - + setattr(self, opt, options[opt]) else: - setattr (self, opt, self.default_options[opt]) + setattr(self, opt, self.default_options[opt]) # sanity check client option hash for opt in options.keys(): if opt not in self.default_options: - raise KeyError, "invalid TextFile option '%s'" % opt + raise KeyError("invalid TextFile option '%s'" % opt) if file is None: - self.open (filename) + self.open(filename) else: self.filename = filename self.file = file @@ -111,43 +107,37 @@ class TextFile: # 'unreadline()' operation self.linebuf = [] - - def open (self, filename): + def open(self, filename): """Open a new file named 'filename'. This overrides both the 'filename' and 'file' arguments to the constructor.""" - self.filename = filename - self.file = io.open (self.filename, 'r') + self.file = io.open(self.filename, 'r') self.current_line = 0 - - def close (self): + def close(self): """Close the current file and forget everything we know about it (filename, current line number).""" - - self.file.close () + self.file.close() self.file = None self.filename = None self.current_line = None - - def gen_error (self, msg, line=None): + def gen_error(self, msg, line=None): outmsg = [] if line is None: line = self.current_line outmsg.append(self.filename + ", ") - if isinstance (line, (list, tuple)): - outmsg.append("lines %d-%d: " % tuple (line)) + if isinstance(line, (list, tuple)): + outmsg.append("lines %d-%d: " % tuple(line)) else: outmsg.append("line %d: " % line) outmsg.append(str(msg)) return "".join(outmsg) + def error(self, msg, line=None): + raise ValueError("error: " + self.gen_error(msg, line)) - def error (self, msg, line=None): - raise ValueError, "error: " + self.gen_error(msg, line) - - def warn (self, msg, line=None): + def warn(self, msg, line=None): """Print (to stderr) a warning message tied to the current logical line in the current file. If the current logical line in the file spans multiple physical lines, the warning refers to the @@ -157,8 +147,7 @@ class TextFile: line.""" sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n") - - def readline (self): + def readline(self): """Read and return a single logical line from the current file (or from an internal buffer if lines have previously been "unread" with 'unreadline()'). If the 'join_lines' option is true, this @@ -168,7 +157,6 @@ class TextFile: line(s) just read. Returns None on end-of-file, since the empty string can occur if 'rstrip_ws' is true but 'strip_blanks' is not.""" - # If any "unread" lines waiting in 'linebuf', return the top # one. (We don't actually buffer read-ahead data -- lines only # get put in 'linebuf' if the client explicitly does an @@ -180,10 +168,11 @@ class TextFile: buildup_line = '' - while 1: + while True: # read the line, make it None if EOF line = self.file.readline() - if line == '': line = None + if line == '': + line = None if self.strip_comments and line: @@ -195,8 +184,8 @@ class TextFile: # unescape it (and any other escaped "#"'s that might be # lurking in there) and otherwise leave the line alone. - pos = line.find ("#") - if pos == -1: # no "#" -- no comments + pos = line.find("#") + if pos == -1: # no "#" -- no comments pass # It's definitely a comment -- either "#" is the first @@ -218,51 +207,48 @@ class TextFile: # # comment that should be ignored # there # result in "hello there". - if line.strip () == "": + if line.strip() == "": continue - - else: # it's an escaped "#" + else: # it's an escaped "#" line = line.replace("\\#", "#") - # did previous line end with a backslash? then accumulate if self.join_lines and buildup_line: # oops: end of file if line is None: - self.warn ("continuation line immediately precedes " - "end-of-file") + self.warn("continuation line immediately precedes " + "end-of-file") return buildup_line if self.collapse_join: - line = line.lstrip () + line = line.lstrip() line = buildup_line + line # careful: pay attention to line number when incrementing it - if isinstance (self.current_line, list): + if isinstance(self.current_line, list): self.current_line[1] = self.current_line[1] + 1 else: self.current_line = [self.current_line, - self.current_line+1] + self.current_line + 1] # just an ordinary line, read it as usual else: - if line is None: # eof + if line is None: # eof return None # still have to be careful about incrementing the line number! - if isinstance (self.current_line, list): + if isinstance(self.current_line, list): self.current_line = self.current_line[1] + 1 else: self.current_line = self.current_line + 1 - # strip whitespace however the client wants (leading and # trailing, or one or the other, or neither) if self.lstrip_ws and self.rstrip_ws: - line = line.strip () + line = line.strip() elif self.lstrip_ws: - line = line.lstrip () + line = line.lstrip() elif self.rstrip_ws: - line = line.rstrip () + line = line.rstrip() # blank line (whether we rstrip'ed or not)? skip to next line # if appropriate @@ -281,27 +267,21 @@ class TextFile: # well, I guess there's some actual content there: return it return line - # readline () - - - def readlines (self): + def readlines(self): """Read and return the list of all logical lines remaining in the current file.""" - lines = [] - while 1: + while True: line = self.readline() if line is None: return lines - lines.append (line) + lines.append(line) - - def unreadline (self, line): + def unreadline(self, line): """Push 'line' (a string) onto an internal buffer that will be checked by future 'readline()' calls. Handy for implementing a parser with line-at-a-time lookahead.""" - - self.linebuf.append (line) + self.linebuf.append(line) if __name__ == "__main__": @@ -312,7 +292,7 @@ line 3 \\ continues on next line """ # result 1: no fancy options - result1 = map (lambda x: x + "\n", test_data.split ("\n")[0:-1]) + result1 = map(lambda x: x + "\n", test_data.split("\n")[0:-1]) # result 2: just strip comments result2 = ["\n", @@ -337,9 +317,8 @@ line 3 \\ # "collapse" joined lines result6 = ["line 3 continues on next line"] - def test_input (count, description, file, expected_result): - result = file.readlines () - # result = ''.join (result) + def test_input(count, description, file, expected_result): + result = file.readlines() if result == expected_result: print("ok %d (%s)" % (count, description)) else: @@ -351,31 +330,31 @@ line 3 \\ filename = "test.txt" - out_file = open (filename, "w") - out_file.write (test_data) - out_file.close () + out_file = open(filename, "w") + out_file.write(test_data) + out_file.close() - in_file = TextFile (filename, strip_comments=0, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input (1, "no processing", in_file, result1) + in_file = TextFile(filename, strip_comments=0, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + test_input(1, "no processing", in_file, result1) - in_file = TextFile (filename, strip_comments=1, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input (2, "strip comments", in_file, result2) + in_file = TextFile(filename, strip_comments=1, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + test_input(2, "strip comments", in_file, result2) - in_file = TextFile (filename, strip_comments=0, skip_blanks=1, - lstrip_ws=0, rstrip_ws=0) - test_input (3, "strip blanks", in_file, result3) + in_file = TextFile(filename, strip_comments=0, skip_blanks=1, + lstrip_ws=0, rstrip_ws=0) + test_input(3, "strip blanks", in_file, result3) - in_file = TextFile (filename) - test_input (4, "default processing", in_file, result4) + in_file = TextFile(filename) + test_input(4, "default processing", in_file, result4) - in_file = TextFile (filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1) - test_input (5, "join lines without collapsing", in_file, result5) + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1) + test_input(5, "join lines without collapsing", in_file, result5) - in_file = TextFile (filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1, collapse_join=1) - test_input (6, "join lines with collapsing", in_file, result6) + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1, collapse_join=1) + test_input(6, "join lines with collapsing", in_file, result6) - os.remove (filename) + os.remove(filename) diff --git a/unixccompiler.py b/unixccompiler.py index d07ae1e3..91d0dffd 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -50,7 +50,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): build, without a way to remove an architecture. Furthermore GCC will barf if multiple '-isysroot' arguments are present. """ - stripArch = stripSysroot = 0 + stripArch = stripSysroot = False compiler_so = list(compiler_so) kernel_version = os.uname()[2] # 8.4.3 @@ -65,7 +65,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): stripSysroot = '-isysroot' in cc_args if stripArch: - while 1: + while True: try: index = compiler_so.index('-arch') # Strip this argument and the next one: @@ -137,11 +137,10 @@ class UnixCCompiler(CCompiler): if sys.platform == "cygwin": exe_extension = ".exe" - def preprocess(self, source, - output_file=None, macros=None, include_dirs=None, - extra_preargs=None, extra_postargs=None): - ignore, macros, include_dirs = \ - self._fix_compile_args(None, macros, include_dirs) + def preprocess(self, source, output_file=None, macros=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): + fixed_args = self._fix_compile_args(None, macros, include_dirs) + ignore, macros, include_dirs = fixed_args pp_opts = gen_preprocess_options(macros, include_dirs) pp_args = self.preprocessor + pp_opts if output_file: @@ -162,7 +161,7 @@ class UnixCCompiler(CCompiler): try: self.spawn(pp_args) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): compiler_so = self.compiler_so @@ -172,7 +171,7 @@ class UnixCCompiler(CCompiler): self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) def create_static_lib(self, objects, output_libname, output_dir=None, debug=0, target_lang=None): @@ -196,7 +195,7 @@ class UnixCCompiler(CCompiler): try: self.spawn(self.ranlib + [output_filename]) except DistutilsExecError as msg: - raise LibError, msg + raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) @@ -206,13 +205,14 @@ class UnixCCompiler(CCompiler): export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, target_lang=None): objects, output_dir = self._fix_object_args(objects, output_dir) - libraries, library_dirs, runtime_library_dirs = \ - self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + libraries, library_dirs, runtime_library_dirs = fixed_args lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) if not isinstance(output_dir, (basestring, type(None))): - raise TypeError, "'output_dir' must be a string or None" + raise TypeError("'output_dir' must be a string or None") if output_dir is not None: output_filename = os.path.join(output_dir, output_filename) @@ -241,8 +241,7 @@ class UnixCCompiler(CCompiler): if os.path.basename(linker[0]) == "env": i = 1 while '=' in linker[i]: - i = i + 1 - + i += 1 linker[i] = self.compiler_cxx[i] if sys.platform == 'darwin': @@ -250,7 +249,7 @@ class UnixCCompiler(CCompiler): self.spawn(linker + ld_args) except DistutilsExecError as msg: - raise LinkError, msg + raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) diff --git a/util.py b/util.py index 9aa85705..22a8ba23 100644 --- a/util.py +++ b/util.py @@ -153,9 +153,9 @@ def convert_path (pathname): if not pathname: return pathname if pathname[0] == '/': - raise ValueError, "path '%s' cannot be absolute" % pathname + raise ValueError("path '%s' cannot be absolute" % pathname) if pathname[-1] == '/': - raise ValueError, "path '%s' cannot end with '/'" % pathname + raise ValueError("path '%s' cannot end with '/'" % pathname) paths = pathname.split('/') while '.' in paths: @@ -201,8 +201,7 @@ def change_root (new_root, pathname): return os.path.join(new_root, pathname) else: - raise DistutilsPlatformError, \ - "nothing known about platform '%s'" % os.name + raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) _environ_checked = 0 @@ -248,7 +247,7 @@ def subst_vars (s, local_vars): try: return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) except KeyError as var: - raise ValueError, "invalid variable '$%s'" % var + raise ValueError("invalid variable '$%s'" % var) # subst_vars () @@ -326,12 +325,10 @@ def split_quoted (s): elif s[end] == '"': # slurp doubly-quoted string m = _dquote_re.match(s, end) else: - raise RuntimeError, \ - "this can't happen (bad char '%c')" % s[end] + raise RuntimeError("this can't happen (bad char '%c')" % s[end]) if m is None: - raise ValueError, \ - "bad string (mismatched %s quotes?)" % s[end] + raise ValueError("bad string (mismatched %s quotes?)" % s[end]) (beg, end) = m.span() s = s[:beg] + s[beg+1:end-1] + s[end:] @@ -378,7 +375,7 @@ def strtobool (val): elif val in ('n', 'no', 'f', 'false', 'off', '0'): return 0 else: - raise ValueError, "invalid truth value %r" % (val,) + raise ValueError("invalid truth value %r" % (val,)) def byte_compile (py_files, @@ -502,8 +499,7 @@ byte_compile(files, optimize=%r, force=%r, dfile = file if prefix: if file[:len(prefix)] != prefix: - raise ValueError, \ - ("invalid prefix: filename %r doesn't start with %r" + raise ValueError("invalid prefix: filename %r doesn't start with %r" % (file, prefix)) dfile = dfile[len(prefix):] if base_dir: diff --git a/version.py b/version.py index 96b65521..f71b2f6c 100644 --- a/version.py +++ b/version.py @@ -140,7 +140,7 @@ class StrictVersion (Version): def parse (self, vstring): match = self.version_re.match(vstring) if not match: - raise ValueError, "invalid version number '%s'" % vstring + raise ValueError("invalid version number '%s'" % vstring) (major, minor, patch, prerelease, prerelease_num) = \ match.group(1, 2, 4, 5, 6) -- cgit v1.2.1 From bf9ae3584f023df6f04f88e17cfe987e004de033 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Thu, 30 Aug 2007 05:35:41 +0000 Subject: Stop using the find function on the string module, use the string method. This will hopefully fix the problem on a Windows buildbot with test_sundry. --- util.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/util.py b/util.py index 22a8ba23..e0ae2e5e 100644 --- a/util.py +++ b/util.py @@ -39,14 +39,14 @@ def get_platform (): if os.name == 'nt': # sniff sys.version for architecture. prefix = " bit (" - i = string.find(sys.version, prefix) + i = sys.version.find(prefix) if i == -1: return sys.platform - j = string.find(sys.version, ")", i) + j = sys.version.find(")", i) look = sys.version[i+len(prefix):j].lower() - if look=='amd64': + if look == 'amd64': return 'win-x86_64' - if look=='itanium': + if look == 'itanium': return 'win-ia64' return sys.platform -- cgit v1.2.1 From b7375849d526bedd70683539901013baaf9edcea Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Thu, 30 Aug 2007 18:46:25 +0000 Subject: Fix a typo in the distutils cleanup. --- msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvccompiler.py b/msvccompiler.py index f5b67049..d239057b 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -442,7 +442,7 @@ class MSVCCompiler(CCompiler) : if debug: pass # XXX what goes here? try: - self.([self.lib] + lib_args) + self.spawn([self.lib] + lib_args) except DistutilsExecError as msg: raise LibError(msg) else: -- cgit v1.2.1 From 70ba60503c5d76dd223ae450b64f757ade0b9b2c Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 31 Aug 2007 10:37:15 +0000 Subject: string.maketrans() now produces translation tables for bytes.translate() -- wrong module? Fix all remaining instances that did bad things with the new str.translate(). --- command/install.py | 5 ++--- fancy_getopt.py | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/command/install.py b/command/install.py index 29cda1eb..ffd68db2 100644 --- a/command/install.py +++ b/command/install.py @@ -348,11 +348,10 @@ class install (Command): if opt_name[-1] == "=": opt_name = opt_name[0:-1] if self.negative_opt.has_key(opt_name): - opt_name = self.negative_opt[opt_name].translate( - longopt_xlate) + opt_name = longopt_xlate(self.negative_opt[opt_name]) val = not getattr(self, opt_name) else: - opt_name = opt_name.translate(longopt_xlate) + opt_name = longopt_xlate(opt_name) val = getattr(self, opt_name) print(" %s: %s" % (opt_name, val)) diff --git a/fancy_getopt.py b/fancy_getopt.py index 5434334e..15cbdd71 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -26,7 +26,7 @@ neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) # This is used to translate long options to legitimate Python identifiers # (for use as attributes of some object). -longopt_xlate = string.maketrans('-', '_') +longopt_xlate = lambda s: s.replace('-', '_') class FancyGetopt: """Wrapper around the standard 'getopt()' module that provides some @@ -107,7 +107,7 @@ class FancyGetopt: """Translate long option name 'long_option' to the form it has as an attribute of some object: ie., translate hyphens to underscores.""" - return long_option.translate(longopt_xlate) + return longopt_xlate(long_option) def _check_alias_dict(self, aliases, what): assert isinstance(aliases, dict) @@ -372,7 +372,7 @@ def fancy_getopt(options, negative_opt, object, args): return parser.getopt(args, object) -WS_TRANS = string.maketrans(string.whitespace, ' ' * len(string.whitespace)) +WS_TRANS = {ord(_wschar) : ' ' for _wschar in string.whitespace} def wrap_text(text, width): """wrap_text(text : string, width : int) -> [string] @@ -432,7 +432,7 @@ def translate_longopt(opt): """Convert a long option name to a valid Python identifier by changing "-" to "_". """ - return opt.translate(longopt_xlate) + return longopt_xlate(opt) class OptionDummy: -- cgit v1.2.1 From d597eb5b512b06572e8d5857ed0846c9f431cc6d Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Sat, 1 Sep 2007 20:37:22 +0000 Subject: Run 2to3's fix_has_key over distutils. --- command/install.py | 2 +- command/register.py | 4 ++-- command/upload.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/command/install.py b/command/install.py index ffd68db2..f1ebcbf0 100644 --- a/command/install.py +++ b/command/install.py @@ -347,7 +347,7 @@ class install (Command): opt_name = opt[0] if opt_name[-1] == "=": opt_name = opt_name[0:-1] - if self.negative_opt.has_key(opt_name): + if opt_name in self.negative_opt: opt_name = longopt_xlate(self.negative_opt[opt_name]) val = not getattr(self, opt_name) else: diff --git a/command/register.py b/command/register.py index 91081ddb..d123d327 100644 --- a/command/register.py +++ b/command/register.py @@ -125,7 +125,7 @@ class register(Command): # see if we can short-cut and get the username/password from the # config config = None - if os.environ.has_key('HOME'): + if 'HOME' in os.environ: rc = os.path.join(os.environ['HOME'], '.pypirc') if os.path.exists(rc): print('Using PyPI login from %s'%rc) @@ -168,7 +168,7 @@ Your selection [default 1]: ''', end=' ') print('Server response (%s): %s'%(code, result)) # possibly save the login - if os.environ.has_key('HOME') and config is None and code == 200: + if 'HOME' in os.environ and config is None and code == 200: rc = os.path.join(os.environ['HOME'], '.pypirc') print('I can store your PyPI login so future submissions will be faster.') print('(the login will be stored in %s)'%rc) diff --git a/command/upload.py b/command/upload.py index b49acd12..34b6692d 100644 --- a/command/upload.py +++ b/command/upload.py @@ -45,7 +45,7 @@ class upload(Command): raise DistutilsOptionError( "Must use --sign for --identity to have meaning" ) - if os.environ.has_key('HOME'): + if 'HOME' in os.environ: rc = os.path.join(os.environ['HOME'], '.pypirc') if os.path.exists(rc): self.announce('Using PyPI login from %s' % rc) -- cgit v1.2.1 From 69af9b586f1c9446c371b23a1c5d6e6a745f848e Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Sat, 8 Sep 2007 00:34:17 +0000 Subject: be explicit about the actual location of the missing file --- dep_util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dep_util.py b/dep_util.py index c139c852..2c6d7927 100644 --- a/dep_util.py +++ b/dep_util.py @@ -19,7 +19,8 @@ def newer (source, target): Raise DistutilsFileError if 'source' does not exist. """ if not os.path.exists(source): - raise DistutilsFileError, "file '%s' does not exist" % source + raise DistutilsFileError, ("file '%s' does not exist" % + os.path.abspath(source)) if not os.path.exists(target): return 1 -- cgit v1.2.1 From be6286d3fa93348073baeaa554fe14dea328e7ce Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 8 Sep 2007 17:39:28 +0000 Subject: Merged revisions 57778-58052 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r57820 | georg.brandl | 2007-08-31 08:59:27 +0200 (Fri, 31 Aug 2007) | 2 lines Document new shorthand notation for index entries. ........ r57827 | georg.brandl | 2007-08-31 10:47:51 +0200 (Fri, 31 Aug 2007) | 2 lines Fix subitem markup. ........ r57833 | martin.v.loewis | 2007-08-31 12:01:07 +0200 (Fri, 31 Aug 2007) | 1 line Mark registry components as 64-bit on Win64. ........ r57854 | bill.janssen | 2007-08-31 21:02:23 +0200 (Fri, 31 Aug 2007) | 1 line deprecate use of FakeSocket ........ r57855 | bill.janssen | 2007-08-31 21:02:46 +0200 (Fri, 31 Aug 2007) | 1 line remove mentions of socket.ssl in comments ........ r57856 | bill.janssen | 2007-08-31 21:03:31 +0200 (Fri, 31 Aug 2007) | 1 line remove use of non-existent SSLFakeSocket in apparently untested code ........ r57859 | martin.v.loewis | 2007-09-01 08:36:03 +0200 (Sat, 01 Sep 2007) | 3 lines Bug #1737210: Change Manufacturer of Windows installer to PSF. Will backport to 2.5. ........ r57865 | georg.brandl | 2007-09-01 09:51:24 +0200 (Sat, 01 Sep 2007) | 2 lines Fix RST link (backport from Py3k). ........ r57876 | georg.brandl | 2007-09-01 17:49:49 +0200 (Sat, 01 Sep 2007) | 2 lines Document sets' ">" and "<" operations (backport from py3k). ........ r57878 | skip.montanaro | 2007-09-01 19:40:03 +0200 (Sat, 01 Sep 2007) | 4 lines Added a note and examples to explain that re.split does not split on an empty pattern match. (issue 852532). ........ r57879 | walter.doerwald | 2007-09-01 20:18:09 +0200 (Sat, 01 Sep 2007) | 2 lines Fix wrong function names. ........ r57880 | walter.doerwald | 2007-09-01 20:34:05 +0200 (Sat, 01 Sep 2007) | 2 lines Fix typo. ........ r57889 | andrew.kuchling | 2007-09-01 22:31:59 +0200 (Sat, 01 Sep 2007) | 1 line Markup fix ........ r57892 | andrew.kuchling | 2007-09-01 22:43:36 +0200 (Sat, 01 Sep 2007) | 1 line Add various items ........ r57895 | andrew.kuchling | 2007-09-01 23:17:58 +0200 (Sat, 01 Sep 2007) | 1 line Wording change ........ r57896 | andrew.kuchling | 2007-09-01 23:18:31 +0200 (Sat, 01 Sep 2007) | 1 line Add more items ........ r57904 | ronald.oussoren | 2007-09-02 11:46:07 +0200 (Sun, 02 Sep 2007) | 3 lines Macosx: this patch ensures that the value of MACOSX_DEPLOYMENT_TARGET used by the Makefile is also used at configure-time. ........ r57925 | georg.brandl | 2007-09-03 09:16:46 +0200 (Mon, 03 Sep 2007) | 2 lines Fix #883466: don't allow Unicode as arguments to quopri and uu codecs. ........ r57936 | matthias.klose | 2007-09-04 01:33:04 +0200 (Tue, 04 Sep 2007) | 2 lines - Added support for linking the bsddb module against BerkeleyDB 4.6.x. ........ r57954 | mark.summerfield | 2007-09-04 10:16:15 +0200 (Tue, 04 Sep 2007) | 3 lines Added cross-references plus a note about dict & list shallow copying. ........ r57958 | martin.v.loewis | 2007-09-04 11:51:57 +0200 (Tue, 04 Sep 2007) | 3 lines Document that we rely on the OS to release the crypto context. Fixes #1626801. ........ r57960 | martin.v.loewis | 2007-09-04 15:13:14 +0200 (Tue, 04 Sep 2007) | 3 lines Patch #1388440: Add set_completion_display_matches_hook and get_completion_type to readline. ........ r57961 | martin.v.loewis | 2007-09-04 16:19:28 +0200 (Tue, 04 Sep 2007) | 3 lines Patch #1031213: Decode source line in SyntaxErrors back to its original source encoding. Will backport to 2.5. ........ r57972 | matthias.klose | 2007-09-04 20:17:36 +0200 (Tue, 04 Sep 2007) | 3 lines - Makefile.pre.in(buildbottest): Run an optional script pybuildbot.identify to include some information about the build environment. ........ r57973 | matthias.klose | 2007-09-04 21:05:38 +0200 (Tue, 04 Sep 2007) | 2 lines - Makefile.pre.in(buildbottest): Remove whitespace at eol. ........ r57975 | matthias.klose | 2007-09-04 22:46:02 +0200 (Tue, 04 Sep 2007) | 2 lines - Fix libffi configure for hppa*-*-linux* | parisc*-*-linux*. ........ r57980 | bill.janssen | 2007-09-05 02:46:27 +0200 (Wed, 05 Sep 2007) | 1 line SSL certificate distinguished names should be represented by tuples ........ r57985 | martin.v.loewis | 2007-09-05 08:39:17 +0200 (Wed, 05 Sep 2007) | 3 lines Patch #1105: Explain that one needs to build the solution to get dependencies right. ........ r57987 | armin.rigo | 2007-09-05 09:51:21 +0200 (Wed, 05 Sep 2007) | 4 lines PyDict_GetItem() returns a borrowed reference. There are probably a number of places that are open to attacks such as the following one, in bltinmodule.c:min_max(). ........ r57991 | martin.v.loewis | 2007-09-05 13:47:34 +0200 (Wed, 05 Sep 2007) | 3 lines Patch #786737: Allow building in a tree of symlinks pointing to a readonly source. ........ r57993 | georg.brandl | 2007-09-05 15:36:44 +0200 (Wed, 05 Sep 2007) | 2 lines Backport from Py3k: Bug #1684991: explain lookup semantics for __special__ methods (new-style classes only). ........ r58004 | armin.rigo | 2007-09-06 10:30:51 +0200 (Thu, 06 Sep 2007) | 4 lines Patch #1733973 by peaker: ptrace_enter_call() assumes no exception is currently set. This assumption is broken when throwing into a generator. ........ r58006 | armin.rigo | 2007-09-06 11:30:38 +0200 (Thu, 06 Sep 2007) | 4 lines PyDict_GetItem() returns a borrowed reference. This attack is against ceval.c:IMPORT_NAME, which calls an object (__builtin__.__import__) without holding a reference to it. ........ r58013 | georg.brandl | 2007-09-06 16:49:56 +0200 (Thu, 06 Sep 2007) | 2 lines Backport from 3k: #1116: fix reference to old filename. ........ r58021 | thomas.heller | 2007-09-06 22:26:20 +0200 (Thu, 06 Sep 2007) | 1 line Fix typo: c_float represents to C float type. ........ r58022 | skip.montanaro | 2007-09-07 00:29:06 +0200 (Fri, 07 Sep 2007) | 3 lines If this is correct for py3k branch and it's already in the release25-maint branch, seems like it ought to be on the trunk as well. ........ r58023 | gregory.p.smith | 2007-09-07 00:59:59 +0200 (Fri, 07 Sep 2007) | 4 lines Apply the fix from Issue1112 to make this test more robust and keep windows happy. ........ r58031 | brett.cannon | 2007-09-07 05:17:50 +0200 (Fri, 07 Sep 2007) | 4 lines Make uuid1 and uuid4 tests conditional on whether ctypes can be imported; implementation of either function depends on ctypes but uuid as a whole does not. ........ r58032 | brett.cannon | 2007-09-07 06:18:30 +0200 (Fri, 07 Sep 2007) | 6 lines Fix a crasher where Python code managed to infinitely recurse in C code without ever going back out to Python code in PyObject_Call(). Required introducing a static RuntimeError instance so that normalizing an exception there is no reliance on a recursive call that would put the exception system over the recursion check itself. ........ r58034 | thomas.heller | 2007-09-07 08:32:17 +0200 (Fri, 07 Sep 2007) | 1 line Add a 'c_longdouble' type to the ctypes module. ........ r58035 | thomas.heller | 2007-09-07 11:30:40 +0200 (Fri, 07 Sep 2007) | 1 line Remove unneeded #include. ........ r58036 | thomas.heller | 2007-09-07 11:33:24 +0200 (Fri, 07 Sep 2007) | 6 lines Backport from py3k branch: Add a workaround for a strange bug on win64, when _ctypes is compiled with the SDK compiler. This should fix the failing Lib\ctypes\test\test_as_parameter.py test. ........ r58037 | georg.brandl | 2007-09-07 16:14:40 +0200 (Fri, 07 Sep 2007) | 2 lines Fix a wrong indentation for sublists. ........ r58043 | georg.brandl | 2007-09-07 22:10:49 +0200 (Fri, 07 Sep 2007) | 2 lines #1095: ln -f doesn't work portably, fix in Makefile. ........ r58049 | skip.montanaro | 2007-09-08 02:34:17 +0200 (Sat, 08 Sep 2007) | 1 line be explicit about the actual location of the missing file ........ --- dep_util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dep_util.py b/dep_util.py index a9d589a2..07b3549c 100644 --- a/dep_util.py +++ b/dep_util.py @@ -17,7 +17,8 @@ def newer (source, target): Raise DistutilsFileError if 'source' does not exist. """ if not os.path.exists(source): - raise DistutilsFileError("file '%s' does not exist" % source) + raise DistutilsFileError("file '%s' does not exist" % + os.path.abspath(source)) if not os.path.exists(target): return 1 -- cgit v1.2.1 From a68bb215508d8be88a6391cc760e2a203e1b526b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 12 Sep 2007 19:29:28 +0000 Subject: #1120: put explicit version in the shebang lines of pydoc, idle and smtpd.py scripts that are installed by setup.py. That way, they work when only "make altinstall" is used. --- command/build_scripts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index bda4480c..b4810c3e 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -104,7 +104,8 @@ class build_scripts (Command): outf.write("#!%s%s\n" % (os.path.join( sysconfig.get_config_var("BINDIR"), - "python" + sysconfig.get_config_var("EXE")), + "python" + sysconfig.get_config_var("VERSION") + + sysconfig.get_config_var("EXE")), post_interp)) outf.writelines(f.readlines()) outf.close() -- cgit v1.2.1 From 02aa5e870e7910a62c992c30eaced5ef448d43fa Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Wed, 19 Sep 2007 03:06:30 +0000 Subject: Merged revisions 58095-58132,58136-58148,58151-58197 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r58096 | brett.cannon | 2007-09-10 23:38:27 +0200 (Mon, 10 Sep 2007) | 4 lines Fix a possible segfault from recursing too deep to get the repr of a list. Closes issue #1096. ........ r58097 | bill.janssen | 2007-09-10 23:51:02 +0200 (Mon, 10 Sep 2007) | 33 lines More work on SSL support. * Much expanded test suite: All protocols tested against all other protocols. All protocols tested with all certificate options. Tests for bad key and bad cert. Test of STARTTLS functionality. Test of RAND_* functions. * Fixes for threading/malloc bug. * Issue 1065 fixed: sslsocket class renamed to SSLSocket. sslerror class renamed to SSLError. Function "wrap_socket" now used to wrap an existing socket. * Issue 1583946 finally fixed: Support for subjectAltName added. Subject name now returned as proper DN list of RDNs. * SSLError exported from socket as "sslerror". * RAND_* functions properly exported from ssl.py. * Documentation improved: Example of how to create a self-signed certificate. Better indexing. ........ r58098 | guido.van.rossum | 2007-09-11 00:02:25 +0200 (Tue, 11 Sep 2007) | 9 lines Patch # 1140 (my code, approved by Effbot). Make sure the type of the return value of re.sub(x, y, z) is the type of y+x (i.e. unicode if either is unicode, str if they are both str) even if there are no substitutions or if x==z (which triggered various special cases in join_list()). Could be backported to 2.5; no need to port to 3.0. ........ r58099 | guido.van.rossum | 2007-09-11 00:36:02 +0200 (Tue, 11 Sep 2007) | 8 lines Patch # 1026 by Benjamin Aranguren (with Alex Martelli): Backport abc.py and isinstance/issubclass overloading to 2.6. I had to backport test_typechecks.py myself, and make one small change to abc.py to avoid duplicate work when x.__class__ and type(x) are the same. ........ r58100 | bill.janssen | 2007-09-11 01:41:24 +0200 (Tue, 11 Sep 2007) | 3 lines A better way of finding an open port to test with. ........ r58101 | bill.janssen | 2007-09-11 03:09:19 +0200 (Tue, 11 Sep 2007) | 4 lines Make sure test_ssl doesn't reference the ssl module in a context where it can't be imported. ........ r58102 | bill.janssen | 2007-09-11 04:42:07 +0200 (Tue, 11 Sep 2007) | 3 lines Fix some documentation bugs. ........ r58103 | nick.coghlan | 2007-09-11 16:01:18 +0200 (Tue, 11 Sep 2007) | 1 line Always use the -E flag when spawning subprocesses in test_cmd_line (Issue 1056) ........ r58106 | thomas.heller | 2007-09-11 21:17:48 +0200 (Tue, 11 Sep 2007) | 3 lines Disable some tests that fail on the 'ppc Debian unstable' buildbot to find out if they cause the segfault on the 'alpha Debian' machine. ........ r58108 | brett.cannon | 2007-09-11 23:02:28 +0200 (Tue, 11 Sep 2007) | 6 lines Generators had their throw() method allowing string exceptions. That's a no-no. Fixes issue #1147. Need to fix 2.5 to raise a proper warning if a string exception is passed in. ........ r58112 | georg.brandl | 2007-09-12 20:03:51 +0200 (Wed, 12 Sep 2007) | 3 lines New documentation page for the bdb module. (This doesn't need to be merged to Py3k.) ........ r58114 | georg.brandl | 2007-09-12 20:05:57 +0200 (Wed, 12 Sep 2007) | 2 lines Bug #1152: use non-deprecated name in example. ........ r58115 | georg.brandl | 2007-09-12 20:08:33 +0200 (Wed, 12 Sep 2007) | 2 lines Fix #1122: wrong return type documented for various _Size() functions. ........ r58117 | georg.brandl | 2007-09-12 20:10:56 +0200 (Wed, 12 Sep 2007) | 2 lines Fix #1139: PyFile_Encoding really is PyFile_SetEncoding. ........ r58119 | georg.brandl | 2007-09-12 20:29:18 +0200 (Wed, 12 Sep 2007) | 2 lines bug #1154: release memory allocated by "es" PyArg_ParseTuple format specifier. ........ r58121 | bill.janssen | 2007-09-12 20:52:05 +0200 (Wed, 12 Sep 2007) | 1 line root certificate for https://svn.python.org/, used in test_ssl ........ r58122 | georg.brandl | 2007-09-12 21:00:07 +0200 (Wed, 12 Sep 2007) | 3 lines Bug #1153: repr.repr() now doesn't require set and dictionary items to be orderable to properly represent them. ........ r58125 | georg.brandl | 2007-09-12 21:29:28 +0200 (Wed, 12 Sep 2007) | 4 lines #1120: put explicit version in the shebang lines of pydoc, idle and smtpd.py scripts that are installed by setup.py. That way, they work when only "make altinstall" is used. ........ r58139 | mark.summerfield | 2007-09-13 16:54:30 +0200 (Thu, 13 Sep 2007) | 9 lines Replaced variable o with obj in operator.rst because o is easy to confuse. Added a note about Python 3's collections.Mapping etc., above section that describes isMappingType() etc. Added xrefs between os, os.path, fileinput, and open(). ........ r58143 | facundo.batista | 2007-09-13 20:13:15 +0200 (Thu, 13 Sep 2007) | 7 lines Merged the decimal-branch (revisions 54886 to 58140). Decimal is now fully updated to the latests Decimal Specification (v1.66) and the latests test cases (v2.56). Thanks to Mark Dickinson for all his help during this process. ........ r58145 | facundo.batista | 2007-09-13 20:42:09 +0200 (Thu, 13 Sep 2007) | 7 lines Put the parameter watchexp back in (changed watchexp from an int to a bool). Also second argument to watchexp is now converted to Decimal, just as with all the other two-argument operations. Thanks Mark Dickinson. ........ r58147 | andrew.kuchling | 2007-09-14 00:49:34 +0200 (Fri, 14 Sep 2007) | 1 line Add various items ........ r58148 | andrew.kuchling | 2007-09-14 00:50:10 +0200 (Fri, 14 Sep 2007) | 1 line Make target unique ........ r58154 | facundo.batista | 2007-09-14 20:58:34 +0200 (Fri, 14 Sep 2007) | 3 lines Included the new functions, and new descriptions. ........ r58155 | thomas.heller | 2007-09-14 21:40:35 +0200 (Fri, 14 Sep 2007) | 2 lines ctypes.util.find_library uses dump(1) instead of objdump(1) on Solaris. Fixes issue #1777530; will backport to release25-maint. ........ r58159 | facundo.batista | 2007-09-14 23:29:52 +0200 (Fri, 14 Sep 2007) | 3 lines Some additions (examples and a bit on the tutorial). ........ r58160 | georg.brandl | 2007-09-15 18:53:36 +0200 (Sat, 15 Sep 2007) | 2 lines Remove bdb from the "undocumented modules" list. ........ r58164 | bill.janssen | 2007-09-17 00:06:00 +0200 (Mon, 17 Sep 2007) | 15 lines Add support for asyncore server-side SSL support. This requires adding the 'makefile' method to ssl.SSLSocket, and importing the requisite fakefile class from socket.py, and making the appropriate changes to it to make it use the SSL connection. Added sample HTTPS server to test_ssl.py, and test that uses it. Change SSL tests to use https://svn.python.org/, instead of www.sf.net and pop.gmail.com. Added utility function to ssl module, get_server_certificate, to wrap up the several things to be done to pull a certificate from a remote server. ........ r58173 | bill.janssen | 2007-09-17 01:16:46 +0200 (Mon, 17 Sep 2007) | 1 line use binary mode when reading files for testAsyncore to make Windows happy ........ r58175 | raymond.hettinger | 2007-09-17 02:55:00 +0200 (Mon, 17 Sep 2007) | 7 lines Sync-up named tuples with the latest version of the ASPN recipe. Allows optional commas in the field-name spec (help when named tuples are used in conjuction with sql queries). Adds the __fields__ attribute for introspection and to support conversion to dictionary form. Adds a __replace__() method similar to str.replace() but using a named field as a target. Clean-up spelling and presentation in doc-strings. ........ r58176 | brett.cannon | 2007-09-17 05:28:34 +0200 (Mon, 17 Sep 2007) | 5 lines Add a bunch of GIL release/acquire points in tp_print implementations and for PyObject_Print(). Closes issue #1164. ........ r58177 | sean.reifschneider | 2007-09-17 07:45:04 +0200 (Mon, 17 Sep 2007) | 2 lines issue1597011: Fix for bz2 module corner-case error due to error checking bug. ........ r58180 | facundo.batista | 2007-09-17 18:26:50 +0200 (Mon, 17 Sep 2007) | 3 lines Decimal is updated, :) ........ r58181 | facundo.batista | 2007-09-17 19:30:13 +0200 (Mon, 17 Sep 2007) | 5 lines The methods always return Decimal classes, even if they're executed through a subclass (thanks Mark Dickinson). Added a bit of testing for this. ........ r58183 | sean.reifschneider | 2007-09-17 22:53:21 +0200 (Mon, 17 Sep 2007) | 2 lines issue1082: Fixing platform and system for Vista. ........ r58185 | andrew.kuchling | 2007-09-18 03:36:16 +0200 (Tue, 18 Sep 2007) | 1 line Add item; sort properly ........ r58186 | raymond.hettinger | 2007-09-18 05:33:19 +0200 (Tue, 18 Sep 2007) | 1 line Handle corner cased on 0-tuples and 1-tuples. Add verbose option so people can see how it works. ........ r58192 | georg.brandl | 2007-09-18 09:24:40 +0200 (Tue, 18 Sep 2007) | 2 lines A bit of reordering, also show more subheadings in the lang ref index. ........ r58193 | facundo.batista | 2007-09-18 18:53:18 +0200 (Tue, 18 Sep 2007) | 4 lines Speed up of the various division operations (remainder, divide, divideint and divmod). Thanks Mark Dickinson. ........ r58197 | raymond.hettinger | 2007-09-19 00:18:02 +0200 (Wed, 19 Sep 2007) | 1 line Cleanup docs for NamedTuple. ........ --- command/build_scripts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 176e6e10..227bb9c7 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -102,7 +102,8 @@ class build_scripts(Command): outf.write("#!%s%s\n" % (os.path.join( sysconfig.get_config_var("BINDIR"), - "python" + sysconfig.get_config_var("EXE")), + "python" + sysconfig.get_config_var("VERSION") + + sysconfig.get_config_var("EXE")), post_interp)) outf.writelines(f.readlines()) outf.close() -- cgit v1.2.1 From 4cb4040d3e7687c6e5ec99a6a52217ca9091db9a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 25 Sep 2007 21:48:09 +0000 Subject: Fix a straggler filter() call. --- fancy_getopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fancy_getopt.py b/fancy_getopt.py index 15cbdd71..b3231c3d 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -388,7 +388,7 @@ def wrap_text(text, width): text = text.expandtabs() text = text.translate(WS_TRANS) chunks = re.split(r'( +|-+)', text) - chunks = filter(None, chunks) # ' - ' results in empty strings + chunks = [ch for ch in chunks if ch] # ' - ' results in empty strings lines = [] while chunks: -- cgit v1.2.1 From 27cd6610b468d4cc03dea2c22d74aa3c281d3a66 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 15 Oct 2007 01:27:53 +0000 Subject: Fix yet another stray 2.x-ism (maybe merged?). --- dist.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dist.py b/dist.py index 974ee516..631df48c 100644 --- a/dist.py +++ b/dist.py @@ -280,8 +280,7 @@ Common commands: (see '--help-commands' for more) from pprint import pformat if commands is None: # dump all command option dicts - commands = self.command_options.keys() - commands.sort() + commands = sorted(self.command_options.keys()) if header is not None: print(indent + header) -- cgit v1.2.1 From d593487053de01cd756914a5cb5cc6b058c99c03 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 16 Oct 2007 18:12:55 +0000 Subject: Patch# 1258 by Christian Heimes: kill basestring. I like this because it makes the code shorter! :-) --- ccompiler.py | 12 ++++++------ cmd.py | 8 ++++---- command/build_clib.py | 4 ++-- command/build_ext.py | 10 +++++----- command/build_py.py | 2 +- command/config.py | 8 ++++---- command/install.py | 2 +- command/install_data.py | 2 +- dir_util.py | 2 +- dist.py | 6 +++--- extension.py | 4 ++-- fancy_getopt.py | 4 ++-- filelist.py | 2 +- unixccompiler.py | 2 +- 14 files changed, 34 insertions(+), 34 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index c33e5ab4..f4edb7c6 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -154,7 +154,7 @@ class CCompiler: self.set_executable(key, value) def set_executable(self, key, value): - if isinstance(value, basestring): + if isinstance(value, str): setattr(self, key, split_quoted(value)) else: setattr(self, key, value) @@ -175,8 +175,8 @@ class CCompiler: for defn in definitions: if not (isinstance(defn, tuple) and (len(defn) in (1, 2) and - (isinstance (defn[1], basestring) or defn[1] is None)) and - isinstance (defn[0], basestring)): + (isinstance (defn[1], str) or defn[1] is None)) and + isinstance (defn[0], str)): raise TypeError(("invalid macro definition '%s': " % defn) + \ "must be tuple (string,), (string, string), or " + \ "(string, None)") @@ -318,7 +318,7 @@ class CCompiler: """ if outdir is None: outdir = self.output_dir - elif not isinstance(outdir, basestring): + elif not isinstance(outdir, str): raise TypeError("'output_dir' must be a string or None") if macros is None: @@ -415,7 +415,7 @@ class CCompiler: """ if output_dir is None: output_dir = self.output_dir - elif not isinstance(output_dir, basestring): + elif not isinstance(output_dir, str): raise TypeError("'output_dir' must be a string or None") if macros is None: @@ -494,7 +494,7 @@ class CCompiler: if output_dir is None: output_dir = self.output_dir - elif not isinstance(output_dir, basestring): + elif not isinstance(output_dir, str): raise TypeError("'output_dir' must be a string or None") return (objects, output_dir) diff --git a/cmd.py b/cmd.py index 66940f77..bd560a66 100644 --- a/cmd.py +++ b/cmd.py @@ -213,7 +213,7 @@ class Command: if val is None: setattr(self, option, default) return default - elif not isinstance(val, basestring): + elif not isinstance(val, str): raise DistutilsOptionError("'%s' must be a %s (got `%s`)" % (option, what, val)) return val @@ -233,11 +233,11 @@ class Command: val = getattr(self, option) if val is None: return - elif isinstance(val, basestring): + elif isinstance(val, str): setattr(self, option, re.split(r',\s*|\s+', val)) else: if isinstance(val, list): - ok = all(isinstance(v, basestring) for v in val) + ok = all(isinstance(v, str) for v in val) else: ok = False if not ok: @@ -390,7 +390,7 @@ class Command: # Allow 'infiles' to be a single string - if isinstance(infiles, basestring): + if isinstance(infiles, str): infiles = (infiles,) elif not isinstance(infiles, (list, tuple)): raise TypeError( diff --git a/command/build_clib.py b/command/build_clib.py index 5f95207c..34f49836 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -86,7 +86,7 @@ class build_clib(Command): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - if isinstance(self.include_dirs, basestring): + if isinstance(self.include_dirs, str): self.include_dirs = self.include_dirs.split(os.pathsep) # XXX same as for build_ext -- what about 'self.define' and @@ -134,7 +134,7 @@ class build_clib(Command): raise DistutilsSetupError( "each element of 'libraries' must a 2-tuple") - if isinstance(lib[0], basestring): + if isinstance(lib[0], str): raise DistutilsSetupError( "first element of each tuple in 'libraries' " "must be a string (the library name)") diff --git a/command/build_ext.py b/command/build_ext.py index a439c49a..ddf8f723 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -133,7 +133,7 @@ class build_ext(Command): plat_py_include = sysconfig.get_python_inc(plat_specific=1) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - if isinstance(self.include_dirs, basestring): + if isinstance(self.include_dirs, str): self.include_dirs = self.include_dirs.split(os.pathsep) # Put the Python "system" include dir at the end, so that @@ -142,7 +142,7 @@ class build_ext(Command): if plat_py_include != py_include: self.include_dirs.append(plat_py_include) - if isinstance(self.libraries, basestring): + if isinstance(self.libraries, str): self.libraries = [self.libraries] # Life is easier if we're not forever checking for None, so @@ -151,12 +151,12 @@ class build_ext(Command): self.libraries = [] if self.library_dirs is None: self.library_dirs = [] - elif isinstance(self.library_dirs, basestring): + elif isinstance(self.library_dirs, str): self.library_dirs = self.library_dirs.split(os.pathsep) if self.rpath is None: self.rpath = [] - elif isinstance(self.rpath, basestring): + elif isinstance(self.rpath, str): self.rpath = self.rpath.split(os.pathsep) # for extensions under windows use different directories @@ -309,7 +309,7 @@ class build_ext(Command): "each element of 'ext_modules' option must be an " "Extension instance or 2-tuple") - if not (isinstance(ext_name, basestring) and + if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)): raise DistutilsSetupError( "first element of each tuple in 'ext_modules' " diff --git a/command/build_py.py b/command/build_py.py index 454424f3..63ced4b4 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -325,7 +325,7 @@ class build_py (Command): return outputs def build_module(self, module, module_file, package): - if isinstance(package, basestring): + if isinstance(package, str): package = package.split('.') elif not isinstance(package, (list, tuple)): raise TypeError( diff --git a/command/config.py b/command/config.py index a6012342..34f91884 100644 --- a/command/config.py +++ b/command/config.py @@ -69,17 +69,17 @@ class config(Command): def finalize_options(self): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - elif isinstance(self.include_dirs, basestring): + elif isinstance(self.include_dirs, str): self.include_dirs = self.include_dirs.split(os.pathsep) if self.libraries is None: self.libraries = [] - elif isinstance(self.libraries, basestring): + elif isinstance(self.libraries, str): self.libraries = [self.libraries] if self.library_dirs is None: self.library_dirs = [] - elif isinstance(self.library_dirs, basestring): + elif isinstance(self.library_dirs, str): self.library_dirs = self.library_dirs.split(os.pathsep) def run(self): @@ -204,7 +204,7 @@ class config(Command): self._check_compiler() (src, out) = self._preprocess(body, headers, include_dirs, lang) - if isinstance(pattern, basestring): + if isinstance(pattern, str): pattern = re.compile(pattern) file = open(out) diff --git a/command/install.py b/command/install.py index f1ebcbf0..b768663c 100644 --- a/command/install.py +++ b/command/install.py @@ -449,7 +449,7 @@ class install (Command): self.extra_path = self.distribution.extra_path if self.extra_path is not None: - if isinstance(self.extra_path, basestring): + if isinstance(self.extra_path, str): self.extra_path = self.extra_path.split(',') if len(self.extra_path) == 1: diff --git a/command/install_data.py b/command/install_data.py index 2fbac63d..06a70b4a 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -45,7 +45,7 @@ class install_data(Command): def run(self): self.mkpath(self.install_dir) for f in self.data_files: - if isinstance(f, basestring): + if isinstance(f, str): # it's a simple file, so copy it f = convert_path(f) if self.warn_dir: diff --git a/dir_util.py b/dir_util.py index 30e352db..1f0d49c2 100644 --- a/dir_util.py +++ b/dir_util.py @@ -28,7 +28,7 @@ def mkpath (name, mode=0o777, verbose=0, dry_run=0): global _path_created # Detect a common bug -- name is None - if not isinstance(name, basestring): + if not isinstance(name, str): raise DistutilsInternalError( "mkpath: 'name' must be a string (got %r)" % (name,)) diff --git a/dist.py b/dist.py index 631df48c..ade2ab79 100644 --- a/dist.py +++ b/dist.py @@ -580,13 +580,13 @@ Common commands: (see '--help-commands' for more) keywords = self.metadata.keywords if keywords is not None: - if isinstance(keywords, basestring): + if isinstance(keywords, str): keywordlist = keywords.split(',') self.metadata.keywords = [x.strip() for x in keywordlist] platforms = self.metadata.platforms if platforms is not None: - if isinstance(platforms, basestring): + if isinstance(platforms, str): platformlist = platforms.split(',') self.metadata.platforms = [x.strip() for x in platformlist] @@ -874,7 +874,7 @@ Common commands: (see '--help-commands' for more) neg_opt = {} try: - is_string = isinstance(value, basestring) + is_string = isinstance(value, str) if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: diff --git a/extension.py b/extension.py index 7f5954e4..b2718168 100644 --- a/extension.py +++ b/extension.py @@ -102,9 +102,9 @@ class Extension: language=None, **kw # To catch unknown keywords ): - assert isinstance(name, basestring), "'name' must be a string" + assert isinstance(name, str), "'name' must be a string" assert (isinstance(sources, list) and - all(isinstance(v, basestring) for v in sources)), \ + all(isinstance(v, str) for v in sources)), \ "'sources' must be a list of strings" self.name = name diff --git a/fancy_getopt.py b/fancy_getopt.py index b3231c3d..72441fb4 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -154,12 +154,12 @@ class FancyGetopt: raise ValueError("invalid option tuple: %r" % (option,)) # Type- and value-check the option names - if not isinstance(long, basestring) or len(long) < 2: + if not isinstance(long, str) or len(long) < 2: raise DistutilsGetoptError(("invalid long option '%s': " "must be a string of length >= 2") % long) if (not ((short is None) or - (isinstance(short, basestring) and len(short) == 1))): + (isinstance(short, str) and len(short) == 1))): raise DistutilsGetoptError("invalid short option '%s': " "must a single character or None" % short) diff --git a/filelist.py b/filelist.py index 8f801073..6506c30e 100644 --- a/filelist.py +++ b/filelist.py @@ -301,7 +301,7 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): or just returned as-is (assumes it's a regex object). """ if is_regex: - if isinstance(pattern, basestring): + if isinstance(pattern, str): return re.compile(pattern) else: return pattern diff --git a/unixccompiler.py b/unixccompiler.py index 91d0dffd..ee975e15 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -211,7 +211,7 @@ class UnixCCompiler(CCompiler): lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) - if not isinstance(output_dir, (basestring, type(None))): + if not isinstance(output_dir, (str, type(None))): raise TypeError("'output_dir' must be a string or None") if output_dir is not None: output_filename = os.path.join(output_dir, output_filename) -- cgit v1.2.1 From 7fba7d60bbce7a9e20d887716e9b87b9496180f5 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 17 Nov 2007 11:46:54 +0000 Subject: The _winreg module returns bytes which must be decoded to unicode, not encoded. --- msvccompiler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index d239057b..6bf969f9 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -93,10 +93,10 @@ def read_values(base, key): return d def convert_mbcs(s): - enc = getattr(s, "encode", None) - if enc is not None: + dec = getattr(s, "decode", None) + if dec is not None: try: - s = enc("mbcs") + s = dec("mbcs") except UnicodeError: pass return s -- cgit v1.2.1 From 200d0eac23788226d0326f42258ced7ebf2431b2 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 22 Nov 2007 10:14:26 +0000 Subject: A test that should test for osx >= 10.4.0 actually tested for os versions <= 10.4. The end result is that a universal ("fat") build will claim to be a single-architecture on on OSX 10.5 (Leopard). This patch fixes this issue. --- util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.py b/util.py index cfcc6a95..731ee988 100644 --- a/util.py +++ b/util.py @@ -106,7 +106,7 @@ def get_platform (): osname = "macosx" - if (release + '.') < '10.4.' and \ + if (release + '.') >= '10.4.' and \ get_config_vars().get('UNIVERSALSDK', '').strip(): # The universal build will build fat binaries, but not on # systems before 10.4 -- cgit v1.2.1 From 83cd6eeec1a6e2141517e7944b38efb712536537 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 3 Dec 2007 13:47:29 +0000 Subject: Applied my patch #1455 with some extra fixes for VS 2005 The new msvc9compiler module supports VS 2005 and VS 2008. I've also fixed build_ext to support PCbuild8 and PCbuild9 and backported my fix for xxmodule.c from py3k. The old code msvccompiler is still in place in case somebody likes to build an extension with VS 2003 or earlier. I've also updated the cygwin compiler module for VS 2005 and VS 2008. It works with VS 2005 but I'm unable to test it with VS 2008. We have to wait for a new version of cygwin. --- command/build_ext.py | 14 +- cygwinccompiler.py | 48 ++-- msvc9compiler.py | 658 +++++++++++++++++++++++++++++++++++++++++++++++++++ msvccompiler.py | 8 + 4 files changed, 706 insertions(+), 22 deletions(-) create mode 100644 msvc9compiler.py diff --git a/command/build_ext.py b/command/build_ext.py index 12d40837..ecfa177d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -17,6 +17,10 @@ from distutils.dep_util import newer_group from distutils.extension import Extension from distutils import log +if os.name == 'nt': + from distutils.msvccompiler import get_build_version + MSVC_VERSION = int(get_build_version()) + # An extension name is just a dot-separated list of Python NAMEs (ie. # the same as a fully-qualified module name). extension_name_re = re.compile \ @@ -176,7 +180,15 @@ class build_ext (Command): # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) - self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) + if MSVC_VERSION == 9: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PCBuild9')) + elif MSVC_VERSION == 8: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PCBuild8', 'win32release')) + else: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PCBuild')) # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 4fd23e6d..4ac11eb4 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -56,6 +56,29 @@ from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError from distutils import log +def get_msvcr(): + """Include the appropriate MSVC runtime library if Python was built + with MSVC 7.0 or later. + """ + msc_pos = sys.version.find('MSC v.') + if msc_pos != -1: + msc_ver = sys.version[msc_pos+6:msc_pos+10] + if msc_ver == '1300': + # MSVC 7.0 + return ['msvcr70'] + elif msc_ver == '1310': + # MSVC 7.1 + return ['msvcr71'] + elif msc_ver == '1400': + # VS2005 / MSVC 8.0 + return ['msvcr80'] + elif msc_ver == '1500': + # VS2008 / MSVC 9.0 + return ['msvcr90'] + else: + raise ValueError("Unknown MS Compiler version %i " % msc_Ver) + + class CygwinCCompiler (UnixCCompiler): compiler_type = 'cygwin' @@ -121,18 +144,9 @@ class CygwinCCompiler (UnixCCompiler): self.warn( "Consider upgrading to a newer version of gcc") else: - self.dll_libraries=[] # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or 7.1. - msc_pos = sys.version.find('MSC v.') - if msc_pos != -1: - msc_ver = sys.version[msc_pos+6:msc_pos+10] - if msc_ver == '1300': - # MSVC 7.0 - self.dll_libraries = ['msvcr70'] - elif msc_ver == '1310': - # MSVC 7.1 - self.dll_libraries = ['msvcr71'] + # with MSVC 7.0 or later. + self.dll_libraries = get_msvcr() # __init__ () @@ -320,16 +334,8 @@ class Mingw32CCompiler (CygwinCCompiler): self.dll_libraries=[] # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or 7.1. - msc_pos = sys.version.find('MSC v.') - if msc_pos != -1: - msc_ver = sys.version[msc_pos+6:msc_pos+10] - if msc_ver == '1300': - # MSVC 7.0 - self.dll_libraries = ['msvcr70'] - elif msc_ver == '1310': - # MSVC 7.1 - self.dll_libraries = ['msvcr71'] + # with MSVC 7.0 or later. + self.dll_libraries = get_msvcr() # __init__ () diff --git a/msvc9compiler.py b/msvc9compiler.py new file mode 100644 index 00000000..a6cff2c0 --- /dev/null +++ b/msvc9compiler.py @@ -0,0 +1,658 @@ +"""distutils.msvc9compiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for the Microsoft Visual Studio 2008. + +The module is compatible with VS 2005 and VS 2008. You can find legacy support +for older versions of VS in distutils.msvccompiler. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) +# ported to VS2005 and VS 2008 by Christian Heimes + +__revision__ = "$Id$" + +import os +import subprocess +import sys +from distutils.errors import (DistutilsExecError, DistutilsPlatformError, + CompileError, LibError, LinkError) +from distutils.ccompiler import (CCompiler, gen_preprocess_options, + gen_lib_options) +from distutils import log + +import _winreg + +RegOpenKeyEx = _winreg.OpenKeyEx +RegEnumKey = _winreg.EnumKey +RegEnumValue = _winreg.EnumValue +RegError = _winreg.error + +HKEYS = (_winreg.HKEY_USERS, + _winreg.HKEY_CURRENT_USER, + _winreg.HKEY_LOCAL_MACHINE, + _winreg.HKEY_CLASSES_ROOT) + +VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" +WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" +NET_BASE = r"Software\Microsoft\.NETFramework" +ARCHS = {'DEFAULT' : 'x86', + 'intel' : 'x86', 'x86' : 'x86', + 'amd64' : 'x64', 'x64' : 'x64', + 'itanium' : 'ia64', 'ia64' : 'ia64', + } + +# The globals VERSION, ARCH, MACROS and VC_ENV are defined later + +class Reg: + """Helper class to read values from the registry + """ + + @classmethod + def get_value(cls, path, key): + for base in HKEYS: + d = cls.read_values(base, path) + if d and key in d: + return d[key] + raise KeyError(key) + + @classmethod + def read_keys(cls, base, key): + """Return list of registry keys.""" + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + L = [] + i = 0 + while True: + try: + k = RegEnumKey(handle, i) + except RegError: + break + L.append(k) + i += 1 + return L + + @classmethod + def read_values(cls, base, key): + """Return dict of registry keys and values. + + All names are converted to lowercase. + """ + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + d = {} + i = 0 + while True: + try: + name, value, type = RegEnumValue(handle, i) + except RegError: + break + name = name.lower() + d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) + i += 1 + return d + + @staticmethod + def convert_mbcs(s): + dec = getattr(s, "decode", None) + if dec is not None: + try: + s = dec("mbcs") + except UnicodeError: + pass + return s + +class MacroExpander: + + def __init__(self, version): + self.macros = {} + self.vsbase = VS_BASE % version + self.load_macros(version) + + def set_macro(self, macro, path, key): + self.macros["$(%s)" % macro] = Reg.get_value(path, key) + + def load_macros(self, version): + self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") + self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") + self.set_macro("FrameworkDir", NET_BASE, "installroot") + try: + if version >= 8.0: + self.set_macro("FrameworkSDKDir", NET_BASE, + "sdkinstallrootv2.0") + else: + raise KeyError("sdkinstallrootv2.0") + except KeyError as exc: # + raise DistutilsPlatformError( + """Python was built with Visual Studio 2008; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2008 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") + + if version >= 9.0: + self.set_macro("FrameworkVersion", self.vsbase, "clr version") + self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") + else: + p = r"Software\Microsoft\NET Framework Setup\Product" + for base in HKEYS: + try: + h = RegOpenKeyEx(base, p) + except RegError: + continue + key = RegEnumKey(h, 0) + d = Reg.get_value(base, r"%s\%s" % (p, key)) + self.macros["$(FrameworkVersion)"] = d["version"] + + def sub(self, s): + for k, v in self.macros.items(): + s = s.replace(k, v) + return s + +def get_build_version(): + """Return the version of MSVC that was used to build Python. + + For Python 2.3 and up, the version number is included in + sys.version. For earlier versions, assume the compiler is MSVC 6. + """ + prefix = "MSC v." + i = sys.version.find(prefix) + if i == -1: + return 6 + i = i + len(prefix) + s, rest = sys.version[i:].split(" ", 1) + majorVersion = int(s[:-2]) - 6 + minorVersion = int(s[2:3]) / 10.0 + # I don't think paths are affected by minor version in version 6 + if majorVersion == 6: + minorVersion = 0 + if majorVersion >= 6: + return majorVersion + minorVersion + # else we don't know what version of the compiler this is + return None + +def get_build_architecture(): + """Return the processor architecture. + + Possible results are "x86" or "amd64". + """ + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return "x86" + j = sys.version.find(")", i) + sysarch = sys.version[i+len(prefix):j].lower() + arch = ARCHS.get(sysarch, None) + if arch is None: + return ARCHS['DEFAULT'] + else: + return arch + +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths + +def find_vcvarsall(version): + """Find the vcvarsall.bat file + + At first it tries to find the productdir of VS 2008 in the registry. If + that fails it falls back to the VS90COMNTOOLS env var. + """ + vsbase = VS_BASE % version + try: + productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, + "productdir") + except KeyError: + log.debug("Unable to find productdir in registry") + productdir = None + + if not productdir or not os.path.isdir(productdir): + toolskey = "VS%0.f0COMNTOOLS" % version + toolsdir = os.environ.get(toolskey, None) + + if toolsdir and os.path.isdir(toolsdir): + productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") + productdir = os.path.abspath(productdir) + if not os.path.isdir(productdir): + log.debug("%s is not a valid directory" % productdir) + return None + else: + log.debug("Env var %s is not set or invalid" % toolskey) + if not productdir: + log.debug("No productdir found") + return None + vcvarsall = os.path.join(productdir, "vcvarsall.bat") + if os.path.isfile(vcvarsall): + return vcvarsall + log.debug("Unable to find vcvarsall.bat") + return None + +def query_vcvarsall(version, arch="x86"): + """Launch vcvarsall.bat and read the settings from its environment + """ + vcvarsall = find_vcvarsall(version) + interesting = set(("include", "lib", "libpath", "path")) + result = {} + + if vcvarsall is None: + raise IOError("Unable to find vcvarsall.bat") + popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + if popen.wait() != 0: + raise IOError(popen.stderr.read()) + + for line in popen.stdout: + line = Reg.convert_mbcs(line) + if '=' not in line: + continue + line = line.strip() + key, value = line.split('=') + key = key.lower() + if key in interesting: + if value.endswith(os.pathsep): + value = value[:-1] + result[key] = value + + if len(result) != len(interesting): + raise ValueError(str(list(result.keys()))) + + return result + +# More globals +VERSION = get_build_version() +if VERSION < 8.0: + raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) +ARCH = get_build_architecture() +# MACROS = MacroExpander(VERSION) +VC_ENV = query_vcvarsall(VERSION, ARCH) + +class MSVCCompiler(CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) + self.__version = VERSION + self.__arch = ARCH + self.__root = r"Software\Microsoft\VisualStudio" + # self.__macros = MACROS + self.__path = [] + self.initialized = False + + def initialize(self): + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): + # Assume that the SDK set up everything alright; don't try to be + # smarter + self.cc = "cl.exe" + self.linker = "link.exe" + self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" + else: + self.__paths = VC_ENV['path'].split(os.pathsep) + os.environ['lib'] = VC_ENV['lib'] + os.environ['include'] = VC_ENV['include'] + + if len(self.__paths) == 0: + raise DistutilsPlatformError("Python was built with %s, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." + % self.__product) + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + #self.set_path_env_var('lib') + #self.set_path_env_var('include') + + # extend the MSVC path with the current path + try: + for p in os.environ['path'].split(';'): + self.__paths.append(p) + except KeyError: + pass + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) + + self.preprocess_options = None + if self.__arch == "x86": + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', + '/Z7', '/D_DEBUG'] + else: + # Win64 + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', + '/Z7', '/D_DEBUG'] + + self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] + if self.__version >= 7: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None' + ] + self.ldflags_static = [ '/nologo'] + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): + # Copied from ccompiler.py, extended to return .res as 'object'-file + # for .rc input file + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext (src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / + if ext not in self.src_extensions: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError ("Don't know how to compile %s" % src_name) + if strip_dir: + base = os.path.basename (base) + if ext in self._rc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + elif ext in self._mc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append ('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + + [output_opt] + [input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc] + + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext (os.path.basename (src)) + rc_file = os.path.join (rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc] + + ["/fo" + obj] + [rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError("Don't know how to compile %s to %s" + % (src, obj)) + + output_opt = "/Fo" + obj + try: + self.spawn([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + (libraries, library_dirs, runtime_library_dirs) = fixed_args + + if runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) + + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + if target_desc == CCompiler.EXECUTABLE: + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + else: + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared + + export_opts = [] + for sym in (export_symbols or []): + export_opts.append("/EXPORT:" + sym) + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + os.path.dirname(objects[0]), + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath(os.path.dirname(output_filename)) + try: + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC++") + + def library_option(self, lib): + return self.library_filename(lib) + + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename (name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # Helper methods for using the MSVC registry settings + + def find_exe(self, exe): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + for p in self.__paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in os.environ['Path'].split(';'): + fn = os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): + return fn + + return exe diff --git a/msvccompiler.py b/msvccompiler.py index 29a90209..fa123462 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -650,3 +650,11 @@ class MSVCCompiler (CCompiler) : p = self.get_msvc_paths(name) if p: os.environ[name] = string.join(p, ';') + + +if get_build_version() >= 8.0: + log.debug("Importing new compiler from distutils.msvc9compiler") + OldMSVCCompiler = MSVCCompiler + from distutils.msvc9compiler import MSVCCompiler + from distutils.msvc9compiler import get_build_architecture + from distutils.msvc9compiler import MacroExpander -- cgit v1.2.1 From c9618bff1c953979a37bc8a27bd2b4cd435b07b9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 3 Dec 2007 19:47:54 +0000 Subject: Patch #1537 from Chad Austin Change GeneratorExit's base class from Exception to BaseException --- msvc9compiler.py | 658 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 658 insertions(+) create mode 100644 msvc9compiler.py diff --git a/msvc9compiler.py b/msvc9compiler.py new file mode 100644 index 00000000..a6cff2c0 --- /dev/null +++ b/msvc9compiler.py @@ -0,0 +1,658 @@ +"""distutils.msvc9compiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for the Microsoft Visual Studio 2008. + +The module is compatible with VS 2005 and VS 2008. You can find legacy support +for older versions of VS in distutils.msvccompiler. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) +# ported to VS2005 and VS 2008 by Christian Heimes + +__revision__ = "$Id$" + +import os +import subprocess +import sys +from distutils.errors import (DistutilsExecError, DistutilsPlatformError, + CompileError, LibError, LinkError) +from distutils.ccompiler import (CCompiler, gen_preprocess_options, + gen_lib_options) +from distutils import log + +import _winreg + +RegOpenKeyEx = _winreg.OpenKeyEx +RegEnumKey = _winreg.EnumKey +RegEnumValue = _winreg.EnumValue +RegError = _winreg.error + +HKEYS = (_winreg.HKEY_USERS, + _winreg.HKEY_CURRENT_USER, + _winreg.HKEY_LOCAL_MACHINE, + _winreg.HKEY_CLASSES_ROOT) + +VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" +WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" +NET_BASE = r"Software\Microsoft\.NETFramework" +ARCHS = {'DEFAULT' : 'x86', + 'intel' : 'x86', 'x86' : 'x86', + 'amd64' : 'x64', 'x64' : 'x64', + 'itanium' : 'ia64', 'ia64' : 'ia64', + } + +# The globals VERSION, ARCH, MACROS and VC_ENV are defined later + +class Reg: + """Helper class to read values from the registry + """ + + @classmethod + def get_value(cls, path, key): + for base in HKEYS: + d = cls.read_values(base, path) + if d and key in d: + return d[key] + raise KeyError(key) + + @classmethod + def read_keys(cls, base, key): + """Return list of registry keys.""" + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + L = [] + i = 0 + while True: + try: + k = RegEnumKey(handle, i) + except RegError: + break + L.append(k) + i += 1 + return L + + @classmethod + def read_values(cls, base, key): + """Return dict of registry keys and values. + + All names are converted to lowercase. + """ + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + d = {} + i = 0 + while True: + try: + name, value, type = RegEnumValue(handle, i) + except RegError: + break + name = name.lower() + d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) + i += 1 + return d + + @staticmethod + def convert_mbcs(s): + dec = getattr(s, "decode", None) + if dec is not None: + try: + s = dec("mbcs") + except UnicodeError: + pass + return s + +class MacroExpander: + + def __init__(self, version): + self.macros = {} + self.vsbase = VS_BASE % version + self.load_macros(version) + + def set_macro(self, macro, path, key): + self.macros["$(%s)" % macro] = Reg.get_value(path, key) + + def load_macros(self, version): + self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") + self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") + self.set_macro("FrameworkDir", NET_BASE, "installroot") + try: + if version >= 8.0: + self.set_macro("FrameworkSDKDir", NET_BASE, + "sdkinstallrootv2.0") + else: + raise KeyError("sdkinstallrootv2.0") + except KeyError as exc: # + raise DistutilsPlatformError( + """Python was built with Visual Studio 2008; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2008 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") + + if version >= 9.0: + self.set_macro("FrameworkVersion", self.vsbase, "clr version") + self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") + else: + p = r"Software\Microsoft\NET Framework Setup\Product" + for base in HKEYS: + try: + h = RegOpenKeyEx(base, p) + except RegError: + continue + key = RegEnumKey(h, 0) + d = Reg.get_value(base, r"%s\%s" % (p, key)) + self.macros["$(FrameworkVersion)"] = d["version"] + + def sub(self, s): + for k, v in self.macros.items(): + s = s.replace(k, v) + return s + +def get_build_version(): + """Return the version of MSVC that was used to build Python. + + For Python 2.3 and up, the version number is included in + sys.version. For earlier versions, assume the compiler is MSVC 6. + """ + prefix = "MSC v." + i = sys.version.find(prefix) + if i == -1: + return 6 + i = i + len(prefix) + s, rest = sys.version[i:].split(" ", 1) + majorVersion = int(s[:-2]) - 6 + minorVersion = int(s[2:3]) / 10.0 + # I don't think paths are affected by minor version in version 6 + if majorVersion == 6: + minorVersion = 0 + if majorVersion >= 6: + return majorVersion + minorVersion + # else we don't know what version of the compiler this is + return None + +def get_build_architecture(): + """Return the processor architecture. + + Possible results are "x86" or "amd64". + """ + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return "x86" + j = sys.version.find(")", i) + sysarch = sys.version[i+len(prefix):j].lower() + arch = ARCHS.get(sysarch, None) + if arch is None: + return ARCHS['DEFAULT'] + else: + return arch + +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths + +def find_vcvarsall(version): + """Find the vcvarsall.bat file + + At first it tries to find the productdir of VS 2008 in the registry. If + that fails it falls back to the VS90COMNTOOLS env var. + """ + vsbase = VS_BASE % version + try: + productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, + "productdir") + except KeyError: + log.debug("Unable to find productdir in registry") + productdir = None + + if not productdir or not os.path.isdir(productdir): + toolskey = "VS%0.f0COMNTOOLS" % version + toolsdir = os.environ.get(toolskey, None) + + if toolsdir and os.path.isdir(toolsdir): + productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") + productdir = os.path.abspath(productdir) + if not os.path.isdir(productdir): + log.debug("%s is not a valid directory" % productdir) + return None + else: + log.debug("Env var %s is not set or invalid" % toolskey) + if not productdir: + log.debug("No productdir found") + return None + vcvarsall = os.path.join(productdir, "vcvarsall.bat") + if os.path.isfile(vcvarsall): + return vcvarsall + log.debug("Unable to find vcvarsall.bat") + return None + +def query_vcvarsall(version, arch="x86"): + """Launch vcvarsall.bat and read the settings from its environment + """ + vcvarsall = find_vcvarsall(version) + interesting = set(("include", "lib", "libpath", "path")) + result = {} + + if vcvarsall is None: + raise IOError("Unable to find vcvarsall.bat") + popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + if popen.wait() != 0: + raise IOError(popen.stderr.read()) + + for line in popen.stdout: + line = Reg.convert_mbcs(line) + if '=' not in line: + continue + line = line.strip() + key, value = line.split('=') + key = key.lower() + if key in interesting: + if value.endswith(os.pathsep): + value = value[:-1] + result[key] = value + + if len(result) != len(interesting): + raise ValueError(str(list(result.keys()))) + + return result + +# More globals +VERSION = get_build_version() +if VERSION < 8.0: + raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) +ARCH = get_build_architecture() +# MACROS = MacroExpander(VERSION) +VC_ENV = query_vcvarsall(VERSION, ARCH) + +class MSVCCompiler(CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) + self.__version = VERSION + self.__arch = ARCH + self.__root = r"Software\Microsoft\VisualStudio" + # self.__macros = MACROS + self.__path = [] + self.initialized = False + + def initialize(self): + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): + # Assume that the SDK set up everything alright; don't try to be + # smarter + self.cc = "cl.exe" + self.linker = "link.exe" + self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" + else: + self.__paths = VC_ENV['path'].split(os.pathsep) + os.environ['lib'] = VC_ENV['lib'] + os.environ['include'] = VC_ENV['include'] + + if len(self.__paths) == 0: + raise DistutilsPlatformError("Python was built with %s, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." + % self.__product) + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + #self.set_path_env_var('lib') + #self.set_path_env_var('include') + + # extend the MSVC path with the current path + try: + for p in os.environ['path'].split(';'): + self.__paths.append(p) + except KeyError: + pass + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) + + self.preprocess_options = None + if self.__arch == "x86": + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', + '/Z7', '/D_DEBUG'] + else: + # Win64 + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', + '/Z7', '/D_DEBUG'] + + self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] + if self.__version >= 7: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None' + ] + self.ldflags_static = [ '/nologo'] + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): + # Copied from ccompiler.py, extended to return .res as 'object'-file + # for .rc input file + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext (src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / + if ext not in self.src_extensions: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError ("Don't know how to compile %s" % src_name) + if strip_dir: + base = os.path.basename (base) + if ext in self._rc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + elif ext in self._mc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append ('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + + [output_opt] + [input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc] + + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext (os.path.basename (src)) + rc_file = os.path.join (rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc] + + ["/fo" + obj] + [rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError("Don't know how to compile %s to %s" + % (src, obj)) + + output_opt = "/Fo" + obj + try: + self.spawn([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + (libraries, library_dirs, runtime_library_dirs) = fixed_args + + if runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) + + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + if target_desc == CCompiler.EXECUTABLE: + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + else: + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared + + export_opts = [] + for sym in (export_symbols or []): + export_opts.append("/EXPORT:" + sym) + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + os.path.dirname(objects[0]), + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath(os.path.dirname(output_filename)) + try: + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC++") + + def library_option(self, lib): + return self.library_filename(lib) + + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename (name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # Helper methods for using the MSVC registry settings + + def find_exe(self, exe): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + for p in self.__paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in os.environ['Path'].split(';'): + fn = os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): + return fn + + return exe -- cgit v1.2.1 From 57ce0a030801c3b95c04c514bad06f556d5a9933 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 3 Dec 2007 19:53:57 +0000 Subject: Reverting last commit. I had some staled data from an attempted svnmerge in my local sandbox --- msvc9compiler.py | 658 ------------------------------------------------------- 1 file changed, 658 deletions(-) delete mode 100644 msvc9compiler.py diff --git a/msvc9compiler.py b/msvc9compiler.py deleted file mode 100644 index a6cff2c0..00000000 --- a/msvc9compiler.py +++ /dev/null @@ -1,658 +0,0 @@ -"""distutils.msvc9compiler - -Contains MSVCCompiler, an implementation of the abstract CCompiler class -for the Microsoft Visual Studio 2008. - -The module is compatible with VS 2005 and VS 2008. You can find legacy support -for older versions of VS in distutils.msvccompiler. -""" - -# Written by Perry Stoll -# hacked by Robin Becker and Thomas Heller to do a better job of -# finding DevStudio (through the registry) -# ported to VS2005 and VS 2008 by Christian Heimes - -__revision__ = "$Id$" - -import os -import subprocess -import sys -from distutils.errors import (DistutilsExecError, DistutilsPlatformError, - CompileError, LibError, LinkError) -from distutils.ccompiler import (CCompiler, gen_preprocess_options, - gen_lib_options) -from distutils import log - -import _winreg - -RegOpenKeyEx = _winreg.OpenKeyEx -RegEnumKey = _winreg.EnumKey -RegEnumValue = _winreg.EnumValue -RegError = _winreg.error - -HKEYS = (_winreg.HKEY_USERS, - _winreg.HKEY_CURRENT_USER, - _winreg.HKEY_LOCAL_MACHINE, - _winreg.HKEY_CLASSES_ROOT) - -VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" -WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" -NET_BASE = r"Software\Microsoft\.NETFramework" -ARCHS = {'DEFAULT' : 'x86', - 'intel' : 'x86', 'x86' : 'x86', - 'amd64' : 'x64', 'x64' : 'x64', - 'itanium' : 'ia64', 'ia64' : 'ia64', - } - -# The globals VERSION, ARCH, MACROS and VC_ENV are defined later - -class Reg: - """Helper class to read values from the registry - """ - - @classmethod - def get_value(cls, path, key): - for base in HKEYS: - d = cls.read_values(base, path) - if d and key in d: - return d[key] - raise KeyError(key) - - @classmethod - def read_keys(cls, base, key): - """Return list of registry keys.""" - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - L = [] - i = 0 - while True: - try: - k = RegEnumKey(handle, i) - except RegError: - break - L.append(k) - i += 1 - return L - - @classmethod - def read_values(cls, base, key): - """Return dict of registry keys and values. - - All names are converted to lowercase. - """ - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - d = {} - i = 0 - while True: - try: - name, value, type = RegEnumValue(handle, i) - except RegError: - break - name = name.lower() - d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) - i += 1 - return d - - @staticmethod - def convert_mbcs(s): - dec = getattr(s, "decode", None) - if dec is not None: - try: - s = dec("mbcs") - except UnicodeError: - pass - return s - -class MacroExpander: - - def __init__(self, version): - self.macros = {} - self.vsbase = VS_BASE % version - self.load_macros(version) - - def set_macro(self, macro, path, key): - self.macros["$(%s)" % macro] = Reg.get_value(path, key) - - def load_macros(self, version): - self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") - self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") - self.set_macro("FrameworkDir", NET_BASE, "installroot") - try: - if version >= 8.0: - self.set_macro("FrameworkSDKDir", NET_BASE, - "sdkinstallrootv2.0") - else: - raise KeyError("sdkinstallrootv2.0") - except KeyError as exc: # - raise DistutilsPlatformError( - """Python was built with Visual Studio 2008; -extensions must be built with a compiler than can generate compatible binaries. -Visual Studio 2008 was not found on this system. If you have Cygwin installed, -you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") - - if version >= 9.0: - self.set_macro("FrameworkVersion", self.vsbase, "clr version") - self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") - else: - p = r"Software\Microsoft\NET Framework Setup\Product" - for base in HKEYS: - try: - h = RegOpenKeyEx(base, p) - except RegError: - continue - key = RegEnumKey(h, 0) - d = Reg.get_value(base, r"%s\%s" % (p, key)) - self.macros["$(FrameworkVersion)"] = d["version"] - - def sub(self, s): - for k, v in self.macros.items(): - s = s.replace(k, v) - return s - -def get_build_version(): - """Return the version of MSVC that was used to build Python. - - For Python 2.3 and up, the version number is included in - sys.version. For earlier versions, assume the compiler is MSVC 6. - """ - prefix = "MSC v." - i = sys.version.find(prefix) - if i == -1: - return 6 - i = i + len(prefix) - s, rest = sys.version[i:].split(" ", 1) - majorVersion = int(s[:-2]) - 6 - minorVersion = int(s[2:3]) / 10.0 - # I don't think paths are affected by minor version in version 6 - if majorVersion == 6: - minorVersion = 0 - if majorVersion >= 6: - return majorVersion + minorVersion - # else we don't know what version of the compiler this is - return None - -def get_build_architecture(): - """Return the processor architecture. - - Possible results are "x86" or "amd64". - """ - prefix = " bit (" - i = sys.version.find(prefix) - if i == -1: - return "x86" - j = sys.version.find(")", i) - sysarch = sys.version[i+len(prefix):j].lower() - arch = ARCHS.get(sysarch, None) - if arch is None: - return ARCHS['DEFAULT'] - else: - return arch - -def normalize_and_reduce_paths(paths): - """Return a list of normalized paths with duplicates removed. - - The current order of paths is maintained. - """ - # Paths are normalized so things like: /a and /a/ aren't both preserved. - reduced_paths = [] - for p in paths: - np = os.path.normpath(p) - # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. - if np not in reduced_paths: - reduced_paths.append(np) - return reduced_paths - -def find_vcvarsall(version): - """Find the vcvarsall.bat file - - At first it tries to find the productdir of VS 2008 in the registry. If - that fails it falls back to the VS90COMNTOOLS env var. - """ - vsbase = VS_BASE % version - try: - productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, - "productdir") - except KeyError: - log.debug("Unable to find productdir in registry") - productdir = None - - if not productdir or not os.path.isdir(productdir): - toolskey = "VS%0.f0COMNTOOLS" % version - toolsdir = os.environ.get(toolskey, None) - - if toolsdir and os.path.isdir(toolsdir): - productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") - productdir = os.path.abspath(productdir) - if not os.path.isdir(productdir): - log.debug("%s is not a valid directory" % productdir) - return None - else: - log.debug("Env var %s is not set or invalid" % toolskey) - if not productdir: - log.debug("No productdir found") - return None - vcvarsall = os.path.join(productdir, "vcvarsall.bat") - if os.path.isfile(vcvarsall): - return vcvarsall - log.debug("Unable to find vcvarsall.bat") - return None - -def query_vcvarsall(version, arch="x86"): - """Launch vcvarsall.bat and read the settings from its environment - """ - vcvarsall = find_vcvarsall(version) - interesting = set(("include", "lib", "libpath", "path")) - result = {} - - if vcvarsall is None: - raise IOError("Unable to find vcvarsall.bat") - popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - if popen.wait() != 0: - raise IOError(popen.stderr.read()) - - for line in popen.stdout: - line = Reg.convert_mbcs(line) - if '=' not in line: - continue - line = line.strip() - key, value = line.split('=') - key = key.lower() - if key in interesting: - if value.endswith(os.pathsep): - value = value[:-1] - result[key] = value - - if len(result) != len(interesting): - raise ValueError(str(list(result.keys()))) - - return result - -# More globals -VERSION = get_build_version() -if VERSION < 8.0: - raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) -ARCH = get_build_architecture() -# MACROS = MacroExpander(VERSION) -VC_ENV = query_vcvarsall(VERSION, ARCH) - -class MSVCCompiler(CCompiler) : - """Concrete class that implements an interface to Microsoft Visual C++, - as defined by the CCompiler abstract class.""" - - compiler_type = 'msvc' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.rc'] - _mc_extensions = ['.mc'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = (_c_extensions + _cpp_extensions + - _rc_extensions + _mc_extensions) - res_extension = '.res' - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - def __init__(self, verbose=0, dry_run=0, force=0): - CCompiler.__init__ (self, verbose, dry_run, force) - self.__version = VERSION - self.__arch = ARCH - self.__root = r"Software\Microsoft\VisualStudio" - # self.__macros = MACROS - self.__path = [] - self.initialized = False - - def initialize(self): - if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): - # Assume that the SDK set up everything alright; don't try to be - # smarter - self.cc = "cl.exe" - self.linker = "link.exe" - self.lib = "lib.exe" - self.rc = "rc.exe" - self.mc = "mc.exe" - else: - self.__paths = VC_ENV['path'].split(os.pathsep) - os.environ['lib'] = VC_ENV['lib'] - os.environ['include'] = VC_ENV['include'] - - if len(self.__paths) == 0: - raise DistutilsPlatformError("Python was built with %s, " - "and extensions need to be built with the same " - "version of the compiler, but it isn't installed." - % self.__product) - - self.cc = self.find_exe("cl.exe") - self.linker = self.find_exe("link.exe") - self.lib = self.find_exe("lib.exe") - self.rc = self.find_exe("rc.exe") # resource compiler - self.mc = self.find_exe("mc.exe") # message compiler - #self.set_path_env_var('lib') - #self.set_path_env_var('include') - - # extend the MSVC path with the current path - try: - for p in os.environ['path'].split(';'): - self.__paths.append(p) - except KeyError: - pass - self.__paths = normalize_and_reduce_paths(self.__paths) - os.environ['path'] = ";".join(self.__paths) - - self.preprocess_options = None - if self.__arch == "x86": - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', - '/Z7', '/D_DEBUG'] - else: - # Win64 - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', - '/Z7', '/D_DEBUG'] - - self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] - if self.__version >= 7: - self.ldflags_shared_debug = [ - '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None' - ] - self.ldflags_static = [ '/nologo'] - - self.initialized = True - - # -- Worker methods ------------------------------------------------ - - def object_filenames(self, - source_filenames, - strip_dir=0, - output_dir=''): - # Copied from ccompiler.py, extended to return .res as 'object'-file - # for .rc input file - if output_dir is None: output_dir = '' - obj_names = [] - for src_name in source_filenames: - (base, ext) = os.path.splitext (src_name) - base = os.path.splitdrive(base)[1] # Chop off the drive - base = base[os.path.isabs(base):] # If abs, chop off leading / - if ext not in self.src_extensions: - # Better to raise an exception instead of silently continuing - # and later complain about sources and targets having - # different lengths - raise CompileError ("Don't know how to compile %s" % src_name) - if strip_dir: - base = os.path.basename (base) - if ext in self._rc_extensions: - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - elif ext in self._mc_extensions: - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - else: - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) - return obj_names - - - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - if not self.initialized: - self.initialize() - compile_info = self._setup_compile(output_dir, macros, include_dirs, - sources, depends, extra_postargs) - macros, objects, extra_postargs, pp_opts, build = compile_info - - compile_opts = extra_preargs or [] - compile_opts.append ('/c') - if debug: - compile_opts.extend(self.compile_options_debug) - else: - compile_opts.extend(self.compile_options) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - if debug: - # pass the full pathname to MSVC in debug mode, - # this allows the debugger to find the source file - # without asking the user to browse for it - src = os.path.abspath(src) - - if ext in self._c_extensions: - input_opt = "/Tc" + src - elif ext in self._cpp_extensions: - input_opt = "/Tp" + src - elif ext in self._rc_extensions: - # compile .RC to .RES file - input_opt = src - output_opt = "/fo" + obj - try: - self.spawn([self.rc] + pp_opts + - [output_opt] + [input_opt]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue - elif ext in self._mc_extensions: - # Compile .MC to .RC file to .RES file. - # * '-h dir' specifies the directory for the - # generated include file - # * '-r dir' specifies the target directory of the - # generated RC file and the binary message resource - # it includes - # - # For now (since there are no options to change this), - # we use the source-directory for the include file and - # the build directory for the RC file and message - # resources. This works at least for win32all. - h_dir = os.path.dirname(src) - rc_dir = os.path.dirname(obj) - try: - # first compile .MC to .RC and .H file - self.spawn([self.mc] + - ['-h', h_dir, '-r', rc_dir] + [src]) - base, _ = os.path.splitext (os.path.basename (src)) - rc_file = os.path.join (rc_dir, base + '.rc') - # then compile .RC to .RES file - self.spawn([self.rc] + - ["/fo" + obj] + [rc_file]) - - except DistutilsExecError as msg: - raise CompileError(msg) - continue - else: - # how to handle this file? - raise CompileError("Don't know how to compile %s to %s" - % (src, obj)) - - output_opt = "/Fo" + obj - try: - self.spawn([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - - def create_static_lib(self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): - - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - output_filename = self.library_filename(output_libname, - output_dir=output_dir) - - if self._need_link(objects, output_filename): - lib_args = objects + ['/OUT:' + output_filename] - if debug: - pass # XXX what goes here? - try: - self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, - runtime_library_dirs) - (libraries, library_dirs, runtime_library_dirs) = fixed_args - - if runtime_library_dirs: - self.warn ("I don't know what to do with 'runtime_library_dirs': " - + str (runtime_library_dirs)) - - lib_opts = gen_lib_options(self, - library_dirs, runtime_library_dirs, - libraries) - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - if target_desc == CCompiler.EXECUTABLE: - if debug: - ldflags = self.ldflags_shared_debug[1:] - else: - ldflags = self.ldflags_shared[1:] - else: - if debug: - ldflags = self.ldflags_shared_debug - else: - ldflags = self.ldflags_shared - - export_opts = [] - for sym in (export_symbols or []): - export_opts.append("/EXPORT:" + sym) - - ld_args = (ldflags + lib_opts + export_opts + - objects + ['/OUT:' + output_filename]) - - # The MSVC linker generates .lib and .exp files, which cannot be - # suppressed by any linker switches. The .lib files may even be - # needed! Make sure they are generated in the temporary build - # directory. Since they have different names for debug and release - # builds, they can go into the same directory. - if export_symbols is not None: - (dll_name, dll_ext) = os.path.splitext( - os.path.basename(output_filename)) - implib_file = os.path.join( - os.path.dirname(objects[0]), - self.library_filename(dll_name)) - ld_args.append ('/IMPLIB:' + implib_file) - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - self.mkpath(os.path.dirname(output_filename)) - try: - self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - - else: - log.debug("skipping %s (up-to-date)", output_filename) - - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "/LIBPATH:" + dir - - def runtime_library_dir_option(self, dir): - raise DistutilsPlatformError( - "don't know how to set runtime library search path for MSVC++") - - def library_option(self, lib): - return self.library_filename(lib) - - - def find_library_file(self, dirs, lib, debug=0): - # Prefer a debugging library if found (and requested), but deal - # with it if we don't have one. - if debug: - try_names = [lib + "_d", lib] - else: - try_names = [lib] - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename (name)) - if os.path.exists(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None - - # Helper methods for using the MSVC registry settings - - def find_exe(self, exe): - """Return path to an MSVC executable program. - - Tries to find the program in several places: first, one of the - MSVC program search paths from the registry; next, the directories - in the PATH environment variable. If any of those work, return an - absolute path that is known to exist. If none of them work, just - return the original program name, 'exe'. - """ - for p in self.__paths: - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - - # didn't find it; try existing path - for p in os.environ['Path'].split(';'): - fn = os.path.join(os.path.abspath(p),exe) - if os.path.isfile(fn): - return fn - - return exe -- cgit v1.2.1 From 04bca8195e91cd3520b869da61781cb5649d4164 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 3 Dec 2007 21:02:03 +0000 Subject: Merged revisions 59275-59303 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE: The merge does NOT contain the modified file Python/import.c from r59288. I can't get it running. Nick, please check in the PEP 366 manually. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ........ r59279 | georg.brandl | 2007-12-02 19:17:50 +0100 (Sun, 02 Dec 2007) | 2 lines Fix a sentence I missed before. Do not merge to 3k. ........ r59281 | georg.brandl | 2007-12-02 22:58:54 +0100 (Sun, 02 Dec 2007) | 3 lines Add documentation for PySys_* functions. Written by Charlie Shepherd for GHOP. Also fixes #1245. ........ r59288 | nick.coghlan | 2007-12-03 13:55:17 +0100 (Mon, 03 Dec 2007) | 1 line Implement PEP 366 ........ r59290 | christian.heimes | 2007-12-03 14:47:29 +0100 (Mon, 03 Dec 2007) | 3 lines Applied my patch #1455 with some extra fixes for VS 2005 The new msvc9compiler module supports VS 2005 and VS 2008. I've also fixed build_ext to support PCbuild8 and PCbuild9 and backported my fix for xxmodule.c from py3k. The old code msvccompiler is still in place in case somebody likes to build an extension with VS 2003 or earlier. I've also updated the cygwin compiler module for VS 2005 and VS 2008. It works with VS 2005 but I'm unable to test it with VS 2008. We have to wait for a new version of cygwin. ........ r59291 | christian.heimes | 2007-12-03 14:55:16 +0100 (Mon, 03 Dec 2007) | 1 line Added comment to Misc/NEWS for r59290 ........ r59292 | christian.heimes | 2007-12-03 15:28:04 +0100 (Mon, 03 Dec 2007) | 1 line I followed MA Lemberg's suggestion and added comments to the late initialization of the type slots. ........ r59293 | facundo.batista | 2007-12-03 17:29:52 +0100 (Mon, 03 Dec 2007) | 3 lines Speedup and cleaning of __str__. Thanks Mark Dickinson. ........ r59294 | facundo.batista | 2007-12-03 18:55:00 +0100 (Mon, 03 Dec 2007) | 4 lines Faster _fix function, and some reordering for a more elegant coding. Thanks Mark Dickinson. ........ r59295 | martin.v.loewis | 2007-12-03 20:20:02 +0100 (Mon, 03 Dec 2007) | 5 lines Issue #1727780: Support loading pickles of random.Random objects created on 32-bit systems on 64-bit systems, and vice versa. As a consequence of the change, Random pickles created by Python 2.6 cannot be loaded in Python 2.5. ........ r59297 | facundo.batista | 2007-12-03 20:49:54 +0100 (Mon, 03 Dec 2007) | 3 lines Two small fixes. Issue 1547. ........ r59299 | georg.brandl | 2007-12-03 20:57:02 +0100 (Mon, 03 Dec 2007) | 2 lines #1548: fix apostroph placement. ........ r59300 | christian.heimes | 2007-12-03 21:01:02 +0100 (Mon, 03 Dec 2007) | 3 lines Patch #1537 from Chad Austin Change GeneratorExit's base class from Exception to BaseException (This time I'm applying the patch to the correct sandbox.) ........ r59302 | georg.brandl | 2007-12-03 21:03:46 +0100 (Mon, 03 Dec 2007) | 3 lines Add examples to the xmlrpclib docs. Written for GHOP by Josip Dzolonga. ........ --- command/build_ext.py | 14 +- cygwinccompiler.py | 48 ++-- msvc9compiler.py | 658 +++++++++++++++++++++++++++++++++++++++++++++++++++ msvccompiler.py | 8 + 4 files changed, 706 insertions(+), 22 deletions(-) create mode 100644 msvc9compiler.py diff --git a/command/build_ext.py b/command/build_ext.py index ddf8f723..6d42278e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -14,6 +14,10 @@ from distutils.dep_util import newer_group from distutils.extension import Extension from distutils import log +if os.name == 'nt': + from distutils.msvccompiler import get_build_version + MSVC_VERSION = int(get_build_version()) + # An extension name is just a dot-separated list of Python NAMEs (ie. # the same as a fully-qualified module name). extension_name_re = re.compile \ @@ -172,7 +176,15 @@ class build_ext(Command): # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) - self.library_dirs.append(os.path.join(sys.exec_prefix, 'PCBuild')) + if MSVC_VERSION == 9: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PCBuild9')) + elif MSVC_VERSION == 8: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PCBuild8', 'win32release')) + else: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PCBuild')) # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory diff --git a/cygwinccompiler.py b/cygwinccompiler.py index bec72ca3..48875230 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -54,6 +54,29 @@ from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError from distutils import log +def get_msvcr(): + """Include the appropriate MSVC runtime library if Python was built + with MSVC 7.0 or later. + """ + msc_pos = sys.version.find('MSC v.') + if msc_pos != -1: + msc_ver = sys.version[msc_pos+6:msc_pos+10] + if msc_ver == '1300': + # MSVC 7.0 + return ['msvcr70'] + elif msc_ver == '1310': + # MSVC 7.1 + return ['msvcr71'] + elif msc_ver == '1400': + # VS2005 / MSVC 8.0 + return ['msvcr80'] + elif msc_ver == '1500': + # VS2008 / MSVC 9.0 + return ['msvcr90'] + else: + raise ValueError("Unknown MS Compiler version %i " % msc_Ver) + + class CygwinCCompiler (UnixCCompiler): compiler_type = 'cygwin' @@ -119,18 +142,9 @@ class CygwinCCompiler (UnixCCompiler): self.warn( "Consider upgrading to a newer version of gcc") else: - self.dll_libraries=[] # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or 7.1. - msc_pos = sys.version.find('MSC v.') - if msc_pos != -1: - msc_ver = sys.version[msc_pos+6:msc_pos+10] - if msc_ver == '1300': - # MSVC 7.0 - self.dll_libraries = ['msvcr70'] - elif msc_ver == '1310': - # MSVC 7.1 - self.dll_libraries = ['msvcr71'] + # with MSVC 7.0 or later. + self.dll_libraries = get_msvcr() # __init__ () @@ -317,16 +331,8 @@ class Mingw32CCompiler (CygwinCCompiler): self.dll_libraries=[] # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or 7.1. - msc_pos = sys.version.find('MSC v.') - if msc_pos != -1: - msc_ver = sys.version[msc_pos+6:msc_pos+10] - if msc_ver == '1300': - # MSVC 7.0 - self.dll_libraries = ['msvcr70'] - elif msc_ver == '1310': - # MSVC 7.1 - self.dll_libraries = ['msvcr71'] + # with MSVC 7.0 or later. + self.dll_libraries = get_msvcr() # __init__ () diff --git a/msvc9compiler.py b/msvc9compiler.py new file mode 100644 index 00000000..a6cff2c0 --- /dev/null +++ b/msvc9compiler.py @@ -0,0 +1,658 @@ +"""distutils.msvc9compiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for the Microsoft Visual Studio 2008. + +The module is compatible with VS 2005 and VS 2008. You can find legacy support +for older versions of VS in distutils.msvccompiler. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) +# ported to VS2005 and VS 2008 by Christian Heimes + +__revision__ = "$Id$" + +import os +import subprocess +import sys +from distutils.errors import (DistutilsExecError, DistutilsPlatformError, + CompileError, LibError, LinkError) +from distutils.ccompiler import (CCompiler, gen_preprocess_options, + gen_lib_options) +from distutils import log + +import _winreg + +RegOpenKeyEx = _winreg.OpenKeyEx +RegEnumKey = _winreg.EnumKey +RegEnumValue = _winreg.EnumValue +RegError = _winreg.error + +HKEYS = (_winreg.HKEY_USERS, + _winreg.HKEY_CURRENT_USER, + _winreg.HKEY_LOCAL_MACHINE, + _winreg.HKEY_CLASSES_ROOT) + +VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" +WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" +NET_BASE = r"Software\Microsoft\.NETFramework" +ARCHS = {'DEFAULT' : 'x86', + 'intel' : 'x86', 'x86' : 'x86', + 'amd64' : 'x64', 'x64' : 'x64', + 'itanium' : 'ia64', 'ia64' : 'ia64', + } + +# The globals VERSION, ARCH, MACROS and VC_ENV are defined later + +class Reg: + """Helper class to read values from the registry + """ + + @classmethod + def get_value(cls, path, key): + for base in HKEYS: + d = cls.read_values(base, path) + if d and key in d: + return d[key] + raise KeyError(key) + + @classmethod + def read_keys(cls, base, key): + """Return list of registry keys.""" + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + L = [] + i = 0 + while True: + try: + k = RegEnumKey(handle, i) + except RegError: + break + L.append(k) + i += 1 + return L + + @classmethod + def read_values(cls, base, key): + """Return dict of registry keys and values. + + All names are converted to lowercase. + """ + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + d = {} + i = 0 + while True: + try: + name, value, type = RegEnumValue(handle, i) + except RegError: + break + name = name.lower() + d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) + i += 1 + return d + + @staticmethod + def convert_mbcs(s): + dec = getattr(s, "decode", None) + if dec is not None: + try: + s = dec("mbcs") + except UnicodeError: + pass + return s + +class MacroExpander: + + def __init__(self, version): + self.macros = {} + self.vsbase = VS_BASE % version + self.load_macros(version) + + def set_macro(self, macro, path, key): + self.macros["$(%s)" % macro] = Reg.get_value(path, key) + + def load_macros(self, version): + self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") + self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") + self.set_macro("FrameworkDir", NET_BASE, "installroot") + try: + if version >= 8.0: + self.set_macro("FrameworkSDKDir", NET_BASE, + "sdkinstallrootv2.0") + else: + raise KeyError("sdkinstallrootv2.0") + except KeyError as exc: # + raise DistutilsPlatformError( + """Python was built with Visual Studio 2008; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2008 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") + + if version >= 9.0: + self.set_macro("FrameworkVersion", self.vsbase, "clr version") + self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") + else: + p = r"Software\Microsoft\NET Framework Setup\Product" + for base in HKEYS: + try: + h = RegOpenKeyEx(base, p) + except RegError: + continue + key = RegEnumKey(h, 0) + d = Reg.get_value(base, r"%s\%s" % (p, key)) + self.macros["$(FrameworkVersion)"] = d["version"] + + def sub(self, s): + for k, v in self.macros.items(): + s = s.replace(k, v) + return s + +def get_build_version(): + """Return the version of MSVC that was used to build Python. + + For Python 2.3 and up, the version number is included in + sys.version. For earlier versions, assume the compiler is MSVC 6. + """ + prefix = "MSC v." + i = sys.version.find(prefix) + if i == -1: + return 6 + i = i + len(prefix) + s, rest = sys.version[i:].split(" ", 1) + majorVersion = int(s[:-2]) - 6 + minorVersion = int(s[2:3]) / 10.0 + # I don't think paths are affected by minor version in version 6 + if majorVersion == 6: + minorVersion = 0 + if majorVersion >= 6: + return majorVersion + minorVersion + # else we don't know what version of the compiler this is + return None + +def get_build_architecture(): + """Return the processor architecture. + + Possible results are "x86" or "amd64". + """ + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return "x86" + j = sys.version.find(")", i) + sysarch = sys.version[i+len(prefix):j].lower() + arch = ARCHS.get(sysarch, None) + if arch is None: + return ARCHS['DEFAULT'] + else: + return arch + +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths + +def find_vcvarsall(version): + """Find the vcvarsall.bat file + + At first it tries to find the productdir of VS 2008 in the registry. If + that fails it falls back to the VS90COMNTOOLS env var. + """ + vsbase = VS_BASE % version + try: + productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, + "productdir") + except KeyError: + log.debug("Unable to find productdir in registry") + productdir = None + + if not productdir or not os.path.isdir(productdir): + toolskey = "VS%0.f0COMNTOOLS" % version + toolsdir = os.environ.get(toolskey, None) + + if toolsdir and os.path.isdir(toolsdir): + productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") + productdir = os.path.abspath(productdir) + if not os.path.isdir(productdir): + log.debug("%s is not a valid directory" % productdir) + return None + else: + log.debug("Env var %s is not set or invalid" % toolskey) + if not productdir: + log.debug("No productdir found") + return None + vcvarsall = os.path.join(productdir, "vcvarsall.bat") + if os.path.isfile(vcvarsall): + return vcvarsall + log.debug("Unable to find vcvarsall.bat") + return None + +def query_vcvarsall(version, arch="x86"): + """Launch vcvarsall.bat and read the settings from its environment + """ + vcvarsall = find_vcvarsall(version) + interesting = set(("include", "lib", "libpath", "path")) + result = {} + + if vcvarsall is None: + raise IOError("Unable to find vcvarsall.bat") + popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + if popen.wait() != 0: + raise IOError(popen.stderr.read()) + + for line in popen.stdout: + line = Reg.convert_mbcs(line) + if '=' not in line: + continue + line = line.strip() + key, value = line.split('=') + key = key.lower() + if key in interesting: + if value.endswith(os.pathsep): + value = value[:-1] + result[key] = value + + if len(result) != len(interesting): + raise ValueError(str(list(result.keys()))) + + return result + +# More globals +VERSION = get_build_version() +if VERSION < 8.0: + raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) +ARCH = get_build_architecture() +# MACROS = MacroExpander(VERSION) +VC_ENV = query_vcvarsall(VERSION, ARCH) + +class MSVCCompiler(CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) + self.__version = VERSION + self.__arch = ARCH + self.__root = r"Software\Microsoft\VisualStudio" + # self.__macros = MACROS + self.__path = [] + self.initialized = False + + def initialize(self): + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): + # Assume that the SDK set up everything alright; don't try to be + # smarter + self.cc = "cl.exe" + self.linker = "link.exe" + self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" + else: + self.__paths = VC_ENV['path'].split(os.pathsep) + os.environ['lib'] = VC_ENV['lib'] + os.environ['include'] = VC_ENV['include'] + + if len(self.__paths) == 0: + raise DistutilsPlatformError("Python was built with %s, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." + % self.__product) + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + #self.set_path_env_var('lib') + #self.set_path_env_var('include') + + # extend the MSVC path with the current path + try: + for p in os.environ['path'].split(';'): + self.__paths.append(p) + except KeyError: + pass + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) + + self.preprocess_options = None + if self.__arch == "x86": + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', + '/Z7', '/D_DEBUG'] + else: + # Win64 + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', + '/Z7', '/D_DEBUG'] + + self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] + if self.__version >= 7: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None' + ] + self.ldflags_static = [ '/nologo'] + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): + # Copied from ccompiler.py, extended to return .res as 'object'-file + # for .rc input file + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext (src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / + if ext not in self.src_extensions: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError ("Don't know how to compile %s" % src_name) + if strip_dir: + base = os.path.basename (base) + if ext in self._rc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + elif ext in self._mc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append ('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + + [output_opt] + [input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc] + + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext (os.path.basename (src)) + rc_file = os.path.join (rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc] + + ["/fo" + obj] + [rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError("Don't know how to compile %s to %s" + % (src, obj)) + + output_opt = "/Fo" + obj + try: + self.spawn([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + (libraries, library_dirs, runtime_library_dirs) = fixed_args + + if runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) + + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + if target_desc == CCompiler.EXECUTABLE: + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + else: + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared + + export_opts = [] + for sym in (export_symbols or []): + export_opts.append("/EXPORT:" + sym) + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + os.path.dirname(objects[0]), + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath(os.path.dirname(output_filename)) + try: + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC++") + + def library_option(self, lib): + return self.library_filename(lib) + + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename (name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # Helper methods for using the MSVC registry settings + + def find_exe(self, exe): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + for p in self.__paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in os.environ['Path'].split(';'): + fn = os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): + return fn + + return exe diff --git a/msvccompiler.py b/msvccompiler.py index 6bf969f9..3b4e9c9d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -632,3 +632,11 @@ class MSVCCompiler(CCompiler) : p = self.get_msvc_paths(name) if p: os.environ[name] = ';'.join(p) + + +if get_build_version() >= 8.0: + log.debug("Importing new compiler from distutils.msvc9compiler") + OldMSVCCompiler = MSVCCompiler + from distutils.msvc9compiler import MSVCCompiler + from distutils.msvc9compiler import get_build_architecture + from distutils.msvc9compiler import MacroExpander -- cgit v1.2.1 From c03780a3a80420dab6fb7ffa8471e135a7dec9a2 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 5 Dec 2007 20:10:38 +0000 Subject: Fixed bug #1557 by using popen.communicate() before popen.wait() --- msvc9compiler.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index a6cff2c0..828d7fbf 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -254,10 +254,13 @@ def query_vcvarsall(version, arch="x86"): popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + stdout, stderr = popen.communicate() if popen.wait() != 0: - raise IOError(popen.stderr.read()) + raise IOError(stderr.decode("mbcs")) - for line in popen.stdout: + stdout = stdout.decode("mbcs") + for line in stdout.split("\n"): line = Reg.convert_mbcs(line) if '=' not in line: continue -- cgit v1.2.1 From 7ea9ff425c9efeff99694a5e4f8d1123f76133d1 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 5 Dec 2007 20:18:38 +0000 Subject: Merged revisions 59333-59370 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r59343 | georg.brandl | 2007-12-05 08:02:47 +0100 (Wed, 05 Dec 2007) | 2 lines Fix typo. ........ r59347 | christian.heimes | 2007-12-05 13:31:44 +0100 (Wed, 05 Dec 2007) | 1 line Fixed quoting and paths in the sqlite project file ........ r59348 | christian.heimes | 2007-12-05 13:45:11 +0100 (Wed, 05 Dec 2007) | 1 line Fixed error in regrtest. I must have missed the spot. ........ r59350 | christian.heimes | 2007-12-05 13:49:14 +0100 (Wed, 05 Dec 2007) | 1 line merge -r59315:59316 from py3k: Fix issue #1553: An errornous __length_hint__ can make list() raise a SystemError ........ r59352 | christian.heimes | 2007-12-05 13:52:34 +0100 (Wed, 05 Dec 2007) | 1 line Added msg to Misc/NEWS ........ r59354 | andrew.kuchling | 2007-12-05 14:27:20 +0100 (Wed, 05 Dec 2007) | 1 line Spelling fix ........ r59356 | georg.brandl | 2007-12-05 18:56:50 +0100 (Wed, 05 Dec 2007) | 3 lines Add examples to csv, pprint and traceback docs. Written by Ross for GHOP. ........ r59358 | raymond.hettinger | 2007-12-05 19:11:08 +0100 (Wed, 05 Dec 2007) | 1 line Error checking was too aggressive (reported by Chris Tismer) ........ r59359 | georg.brandl | 2007-12-05 19:30:48 +0100 (Wed, 05 Dec 2007) | 2 lines Add examples to re docs. Written for GHOP by Dan Finnie. ........ r59366 | georg.brandl | 2007-12-05 20:49:21 +0100 (Wed, 05 Dec 2007) | 2 lines Fix markup. ........ r59367 | christian.heimes | 2007-12-05 20:57:54 +0100 (Wed, 05 Dec 2007) | 1 line Updated documentation and build_tkinter.py script ........ r59368 | georg.brandl | 2007-12-05 21:03:57 +0100 (Wed, 05 Dec 2007) | 2 lines Another markup fix. ........ r59369 | ronald.oussoren | 2007-12-05 21:07:36 +0100 (Wed, 05 Dec 2007) | 7 lines This "fixes" compilation issues for the Carbon._OSA module on OSX Leopard by purging bindings to OSA's debug API's. Those APIs we're completely unsupported on OSX 10.4 and are no longer available on OSX 10.5. Note that this patches a generated file. This is somewhat acceptable because regenerating the file is non-trivial and wouldn't use system headers anyway. ........ r59370 | christian.heimes | 2007-12-05 21:10:38 +0100 (Wed, 05 Dec 2007) | 1 line Fixed bug #1557 by using popen.communicate() before popen.wait() ........ --- msvc9compiler.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index a6cff2c0..828d7fbf 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -254,10 +254,13 @@ def query_vcvarsall(version, arch="x86"): popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + stdout, stderr = popen.communicate() if popen.wait() != 0: - raise IOError(popen.stderr.read()) + raise IOError(stderr.decode("mbcs")) - for line in popen.stdout: + stdout = stdout.decode("mbcs") + for line in stdout.split("\n"): line = Reg.convert_mbcs(line) if '=' not in line: continue -- cgit v1.2.1 From ae50fdd2c427c6dbdb7990bfae890534240d5169 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 6 Dec 2007 13:15:13 +0000 Subject: Fixed get_config_h_filename for Windows. Without the patch it can't find the pyconfig.h file inside a build tree. Added several small unit tests for sysconfig. --- sysconfig.py | 22 ++++++++++++++-------- tests/test_sysconfig.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 tests/test_sysconfig.py diff --git a/sysconfig.py b/sysconfig.py index 0cfafab9..2ea7c78b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -22,16 +22,17 @@ from distutils.errors import DistutilsPlatformError PREFIX = os.path.normpath(sys.prefix) EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +# Path to the base directory of the project. On Windows the binary may +# live in project/PCBuild9 +project_base = os.path.dirname(os.path.abspath(sys.executable)) +if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) + # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use # different (hard-wired) directories. - -argv0_path = os.path.dirname(os.path.abspath(sys.executable)) -landmark = os.path.join(argv0_path, "Modules", "Setup") - -python_build = os.path.isfile(landmark) - -del landmark +python_build = os.path.isfile(os.path.join(project_base, "Modules", + "Setup.dist")) def get_python_version(): @@ -185,7 +186,10 @@ def customize_compiler(compiler): def get_config_h_filename(): """Return full pathname of installed pyconfig.h file.""" if python_build: - inc_dir = argv0_path + if os.name == "nt": + inc_dir = os.path.join(project_base, "PC") + else: + inc_dir = project_base else: inc_dir = get_python_inc(plat_specific=1) if get_python_version() < '2.2': @@ -428,6 +432,8 @@ def _init_nt(): g['SO'] = '.pyd' g['EXE'] = ".exe" + g['VERSION'] = get_python_version().replace(".", "") + g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) global _config_vars _config_vars = g diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py new file mode 100644 index 00000000..8337b0d2 --- /dev/null +++ b/tests/test_sysconfig.py @@ -0,0 +1,36 @@ +"""Tests for distutils.dist.""" + +from distutils import sysconfig +import os +import sys +import unittest + +from test.test_support import TESTFN + +class SysconfigTestCase(unittest.TestCase): + + def test_get_config_h_filename(self): + config_h = sysconfig.get_config_h_filename() + self.assert_(os.path.isfile(config_h), config_h) + + def test_get_python_lib(self): + lib_dir = sysconfig.get_python_lib() + self.assert_(os.path.isdir(lib_dir), lib_dir) + # test for pythonxx.lib? + + def test_get_python_inc(self): + inc_dir = sysconfig.get_python_inc() + self.assert_(os.path.isdir(inc_dir), inc_dir) + python_h = os.path.join(inc_dir, "Python.h") + self.assert_(os.path.isfile(python_h), python_h) + + def test_get_config_vars(self): + cvars = sysconfig.get_config_vars() + self.assert_(isinstance(cvars, dict)) + self.assert_(cvars) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(SysconfigTestCase)) + return suite -- cgit v1.2.1 From d1f35e69fb4d6a4e274369771d5d9574e45ecc59 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 6 Dec 2007 13:55:01 +0000 Subject: Disabled one test that is failing on Unix --- tests/test_sysconfig.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 8337b0d2..ef7c38bf 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -15,7 +15,8 @@ class SysconfigTestCase(unittest.TestCase): def test_get_python_lib(self): lib_dir = sysconfig.get_python_lib() - self.assert_(os.path.isdir(lib_dir), lib_dir) + # XXX doesn't work on Inux when Python was never installed before + #self.assert_(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? def test_get_python_inc(self): -- cgit v1.2.1 From 115bfed7f4bf7f00580c68cdc64e49e36434e7bb Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 8 Dec 2007 15:33:56 +0000 Subject: Merged revisions 59376-59406 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r59377 | georg.brandl | 2007-12-06 01:24:23 +0100 (Thu, 06 Dec 2007) | 2 lines Add another GHOP student to ACKS. ........ r59378 | raymond.hettinger | 2007-12-06 01:56:53 +0100 (Thu, 06 Dec 2007) | 5 lines Fix Issue 1045. Factor-out common calling code by simplifying the length_hint API. Speed-up the function by caching the PyObject_String for the attribute lookup. ........ r59380 | georg.brandl | 2007-12-06 02:52:24 +0100 (Thu, 06 Dec 2007) | 2 lines Diverse markup fixes. ........ r59383 | georg.brandl | 2007-12-06 10:45:39 +0100 (Thu, 06 Dec 2007) | 2 lines Better re.split examples. ........ r59386 | christian.heimes | 2007-12-06 14:15:13 +0100 (Thu, 06 Dec 2007) | 2 lines Fixed get_config_h_filename for Windows. Without the patch it can't find the pyconfig.h file inside a build tree. Added several small unit tests for sysconfig. ........ r59387 | christian.heimes | 2007-12-06 14:30:11 +0100 (Thu, 06 Dec 2007) | 1 line Silence more warnings, _CRT_NONSTDC_NO_DEPRECATE is already defined in pyconfig.h but several projects don't include it. ........ r59389 | christian.heimes | 2007-12-06 14:55:01 +0100 (Thu, 06 Dec 2007) | 1 line Disabled one test that is failing on Unix ........ r59399 | christian.heimes | 2007-12-06 22:13:06 +0100 (Thu, 06 Dec 2007) | 8 lines Several Windows related cleanups: * Removed a #define from pyconfig.h. The macro was already defined a few lines higher. * Fixed path to tix in the build_tkinter.py script * Changed make_buildinfo.c to use versions of unlink and strcat which are considered safe by Windows (as suggested by MvL). * Removed two defines from pyproject.vsprops that are no longer required. Both are defined in pyconfig.h and make_buildinfo.c doesn't use the unsafe versions any more (as suggested by MvL). * Added some more information about PGO and the property files to PCbuild9/readme.txt. Are you fine with the changes, Martin? ........ r59400 | raymond.hettinger | 2007-12-07 02:53:01 +0100 (Fri, 07 Dec 2007) | 4 lines Don't have the docs berate themselves. Keep a professional tone. If a todo is needed, put it in the tracker. ........ r59402 | georg.brandl | 2007-12-07 10:07:10 +0100 (Fri, 07 Dec 2007) | 3 lines Increase unit test coverage of SimpleXMLRPCServer. Written for GHOP by Turkay Eren. ........ r59406 | georg.brandl | 2007-12-07 16:16:57 +0100 (Fri, 07 Dec 2007) | 2 lines Update to windows doc from Robert. ........ --- sysconfig.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 70a27993..191f3d1d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -22,13 +22,17 @@ from .errors import DistutilsPlatformError PREFIX = os.path.normpath(sys.prefix) EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +# Path to the base directory of the project. On Windows the binary may +# live in project/PCBuild9 +project_base = os.path.dirname(os.path.abspath(sys.executable)) +if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) + # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use # different (hard-wired) directories. - -argv0_path = os.path.dirname(os.path.abspath(sys.executable)) -python_build = os.path.isfile(os.path.join(argv0_path, "Modules", "Setup")) - +python_build = os.path.isfile(os.path.join(project_base, "Modules", + "Setup.dist")) def get_python_version(): """Return a string containing the major and minor Python version, @@ -177,7 +181,10 @@ def customize_compiler(compiler): def get_config_h_filename(): """Return full pathname of installed pyconfig.h file.""" if python_build: - inc_dir = argv0_path + if os.name == "nt": + inc_dir = os.path.join(project_base, "PC") + else: + inc_dir = project_base else: inc_dir = get_python_inc(plat_specific=1) if get_python_version() < '2.2': @@ -402,6 +409,8 @@ def _init_nt(): g['SO'] = '.pyd' g['EXE'] = ".exe" + g['VERSION'] = get_python_version().replace(".", "") + g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) global _config_vars _config_vars = g -- cgit v1.2.1 From 329322869d65d079b3bdb40ecba5ecee0bb98d27 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 8 Dec 2007 15:34:59 +0000 Subject: Readded Lib/distutils/tests/test_sysconfig.py Somehow it went missing during the merge --- tests/test_sysconfig.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/test_sysconfig.py diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py new file mode 100644 index 00000000..ef7c38bf --- /dev/null +++ b/tests/test_sysconfig.py @@ -0,0 +1,37 @@ +"""Tests for distutils.dist.""" + +from distutils import sysconfig +import os +import sys +import unittest + +from test.test_support import TESTFN + +class SysconfigTestCase(unittest.TestCase): + + def test_get_config_h_filename(self): + config_h = sysconfig.get_config_h_filename() + self.assert_(os.path.isfile(config_h), config_h) + + def test_get_python_lib(self): + lib_dir = sysconfig.get_python_lib() + # XXX doesn't work on Inux when Python was never installed before + #self.assert_(os.path.isdir(lib_dir), lib_dir) + # test for pythonxx.lib? + + def test_get_python_inc(self): + inc_dir = sysconfig.get_python_inc() + self.assert_(os.path.isdir(inc_dir), inc_dir) + python_h = os.path.join(inc_dir, "Python.h") + self.assert_(os.path.isfile(python_h), python_h) + + def test_get_config_vars(self): + cvars = sysconfig.get_config_vars() + self.assert_(isinstance(cvars, dict)) + self.assert_(cvars) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(SysconfigTestCase)) + return suite -- cgit v1.2.1 From ed6b063cc4b7549d790cc4f83dcb1bf5fd4f93f6 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 13 Dec 2007 19:23:16 +0000 Subject: Fixed bug #1613: Makefile's VPATH feature is broken --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 2ea7c78b..aead1a19 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -32,7 +32,7 @@ if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): # building an extension with an un-installed Python, so we use # different (hard-wired) directories. python_build = os.path.isfile(os.path.join(project_base, "Modules", - "Setup.dist")) + "Setup.local")) def get_python_version(): -- cgit v1.2.1 From ebbd3f714cc3fb8da8fbc8d2f4daf42fffbef1b9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 14 Dec 2007 01:24:44 +0000 Subject: Merged revisions 59465-59487 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r59467 | georg.brandl | 2007-12-11 17:32:49 +0100 (Tue, 11 Dec 2007) | 2 lines Add another GHOP contributor. ........ r59468 | kurt.kaiser | 2007-12-11 20:35:12 +0100 (Tue, 11 Dec 2007) | 3 lines IDLE_tabbedpages.071101.patch Tal Einat Cosmetic changes, one bug. Remove tabpage.py, replaced by tabbedpages.py ........ r59471 | gerhard.haering | 2007-12-11 22:07:40 +0100 (Tue, 11 Dec 2007) | 9 lines Forward-port of commit 59184. - Backported a workaround for a bug in SQLite 3.2.x/3.3.x versions where a statement recompilation with no bound parameters lead to a segfault - Backported a fix necessary because of an SQLite API change in version 3.5. This prevents segfaults when executing empty queries, like our test suite does ........ r59475 | christian.heimes | 2007-12-12 19:09:06 +0100 (Wed, 12 Dec 2007) | 1 line Fixed a nasty problem in the xxmodule.c ........ r59478 | raymond.hettinger | 2007-12-13 01:08:37 +0100 (Thu, 13 Dec 2007) | 1 line Fix bug 1604. deque.__init__() did not clear existing contents like list.__init__. Not a backport candidate. ........ r59480 | alexandre.vassalotti | 2007-12-13 18:58:23 +0100 (Thu, 13 Dec 2007) | 2 lines Fix issue #1313119: urlparse "caches" parses regardless of encoding ........ r59482 | christian.heimes | 2007-12-13 20:23:16 +0100 (Thu, 13 Dec 2007) | 1 line Fixed bug #1613: Makefile's VPATH feature is broken ........ r59484 | guido.van.rossum | 2007-12-13 21:50:10 +0100 (Thu, 13 Dec 2007) | 3 lines Patch #1608. Someone with access to autoconf 2.61 or higher needs to run it and check in the resulting configure file. ........ r59485 | thomas.heller | 2007-12-13 22:20:29 +0100 (Thu, 13 Dec 2007) | 1 line Ran autoconf. ........ r59486 | raymond.hettinger | 2007-12-13 23:55:52 +0100 (Thu, 13 Dec 2007) | 1 line Simplify implementation of __replace__() ........ r59487 | raymond.hettinger | 2007-12-14 00:52:59 +0100 (Fri, 14 Dec 2007) | 1 line Small speedup ........ --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 191f3d1d..c450cd51 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -32,7 +32,7 @@ if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): # building an extension with an un-installed Python, so we use # different (hard-wired) directories. python_build = os.path.isfile(os.path.join(project_base, "Modules", - "Setup.dist")) + "Setup.local")) def get_python_version(): """Return a string containing the major and minor Python version, -- cgit v1.2.1 From 4e9153cd047c24fc7c7c595885444a4a1e599b8b Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 14 Dec 2007 23:42:36 +0000 Subject: Fixed bug #1628 The detection now works on Unix with Makefile, Makefile with VPATH and on Windows. --- sysconfig.py | 6 ++++-- tests/test_sysconfig.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index aead1a19..32b165ff 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -31,8 +31,10 @@ if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use # different (hard-wired) directories. -python_build = os.path.isfile(os.path.join(project_base, "Modules", - "Setup.local")) +# Setup.local is available for Makefile builds including VPATH builds, +# Setup.dist is available on Windows +python_build = any(os.path.isfile(os.path.join(project_base, "Modules", fn)) + for fn in ("Setup.dist", "Setup.local")) def get_python_version(): diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index ef7c38bf..770b7c37 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -15,7 +15,7 @@ class SysconfigTestCase(unittest.TestCase): def test_get_python_lib(self): lib_dir = sysconfig.get_python_lib() - # XXX doesn't work on Inux when Python was never installed before + # XXX doesn't work on Linux when Python was never installed before #self.assert_(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? -- cgit v1.2.1 From 0f89abbb94e9b4c48d3a5356c564b31862602227 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 15 Dec 2007 01:27:15 +0000 Subject: Merged revisions 59488-59511 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r59489 | christian.heimes | 2007-12-14 03:33:57 +0100 (Fri, 14 Dec 2007) | 1 line Silence a warning about an unsed variable in debug builds ........ r59490 | christian.heimes | 2007-12-14 03:35:23 +0100 (Fri, 14 Dec 2007) | 2 lines Fixed bug #1620: New @spam.getter property syntax modifies the property in place. I added also the feature that a @prop.getter decorator does not overwrite the doc string of the property if it was given as an argument to property(). ........ r59491 | raymond.hettinger | 2007-12-14 03:49:47 +0100 (Fri, 14 Dec 2007) | 1 line Cleaner method naming convention ........ r59492 | christian.heimes | 2007-12-14 04:02:34 +0100 (Fri, 14 Dec 2007) | 1 line Fixed a warning in _codecs_iso2022.c and some non C89 conform // comments. ........ r59493 | christian.heimes | 2007-12-14 05:38:13 +0100 (Fri, 14 Dec 2007) | 1 line Fixed warning in ssl module ........ r59500 | raymond.hettinger | 2007-12-14 19:08:20 +0100 (Fri, 14 Dec 2007) | 1 line Add line spacing for readability ........ r59501 | raymond.hettinger | 2007-12-14 19:12:21 +0100 (Fri, 14 Dec 2007) | 3 lines Update method names for named tuples. ........ r59503 | georg.brandl | 2007-12-14 20:03:36 +0100 (Fri, 14 Dec 2007) | 3 lines Add a section about nested listcomps to the tutorial. Thanks to Ian Bruntlett and Robert Lehmann. ........ r59504 | raymond.hettinger | 2007-12-14 20:19:59 +0100 (Fri, 14 Dec 2007) | 1 line Faster and simpler _replace() method ........ r59505 | raymond.hettinger | 2007-12-14 22:51:50 +0100 (Fri, 14 Dec 2007) | 1 line Add usage note ........ r59507 | andrew.kuchling | 2007-12-14 23:41:18 +0100 (Fri, 14 Dec 2007) | 1 line Remove warning about URL ........ r59510 | andrew.kuchling | 2007-12-14 23:52:36 +0100 (Fri, 14 Dec 2007) | 1 line Bump the version number, and make a few small edits ........ r59511 | christian.heimes | 2007-12-15 00:42:36 +0100 (Sat, 15 Dec 2007) | 2 lines Fixed bug #1628 The detection now works on Unix with Makefile, Makefile with VPATH and on Windows. ........ --- sysconfig.py | 6 ++++-- tests/test_sysconfig.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index c450cd51..ba89c3b8 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -31,8 +31,10 @@ if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use # different (hard-wired) directories. -python_build = os.path.isfile(os.path.join(project_base, "Modules", - "Setup.local")) +# Setup.local is available for Makefile builds including VPATH builds, +# Setup.dist is available on Windows +python_build = any(os.path.isfile(os.path.join(project_base, "Modules", fn)) + for fn in ("Setup.dist", "Setup.local")) def get_python_version(): """Return a string containing the major and minor Python version, diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index ef7c38bf..770b7c37 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -15,7 +15,7 @@ class SysconfigTestCase(unittest.TestCase): def test_get_python_lib(self): lib_dir = sysconfig.get_python_lib() - # XXX doesn't work on Inux when Python was never installed before + # XXX doesn't work on Linux when Python was never installed before #self.assert_(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? -- cgit v1.2.1 From c8533b41bab447f51d5e375f239163fcaafdd633 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 31 Dec 2007 14:47:07 +0000 Subject: Added wininst-9.0.exe executable for VS 2008 Integrated bdist_wininst into PCBuild9 directory --- command/bdist_wininst.py | 2 +- command/wininst-6.0.exe | Bin 0 -> 61440 bytes command/wininst-6.exe | Bin 61440 -> 0 bytes command/wininst-8.0.exe | Bin 0 -> 61440 bytes command/wininst-8.exe | Bin 61440 -> 0 bytes command/wininst-9.0.exe | Bin 0 -> 65536 bytes 6 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 command/wininst-6.0.exe delete mode 100644 command/wininst-6.exe create mode 100644 command/wininst-8.0.exe delete mode 100644 command/wininst-8.exe create mode 100644 command/wininst-9.0.exe diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 49afca04..b0691fb8 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -323,6 +323,6 @@ class bdist_wininst (Command): directory = os.path.dirname(__file__) # we must use a wininst-x.y.exe built with the same C compiler # used for python. XXX What about mingw, borland, and so on? - filename = os.path.join(directory, "wininst-%s.exe" % bv) + filename = os.path.join(directory, "wininst-%.1f.exe" % bv) return open(filename, "rb").read() # class bdist_wininst diff --git a/command/wininst-6.0.exe b/command/wininst-6.0.exe new file mode 100644 index 00000000..bd715250 Binary files /dev/null and b/command/wininst-6.0.exe differ diff --git a/command/wininst-6.exe b/command/wininst-6.exe deleted file mode 100644 index bd715250..00000000 Binary files a/command/wininst-6.exe and /dev/null differ diff --git a/command/wininst-8.0.exe b/command/wininst-8.0.exe new file mode 100644 index 00000000..7403bfab Binary files /dev/null and b/command/wininst-8.0.exe differ diff --git a/command/wininst-8.exe b/command/wininst-8.exe deleted file mode 100644 index 7403bfab..00000000 Binary files a/command/wininst-8.exe and /dev/null differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe new file mode 100644 index 00000000..5e0144c9 Binary files /dev/null and b/command/wininst-9.0.exe differ -- cgit v1.2.1 From 20578b2770e9e8bde5d669aa0764d003797da988 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 31 Dec 2007 16:14:33 +0000 Subject: Merged revisions 59605-59624 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r59606 | georg.brandl | 2007-12-29 11:57:00 +0100 (Sat, 29 Dec 2007) | 2 lines Some cleanup in the docs. ........ r59611 | martin.v.loewis | 2007-12-29 19:49:21 +0100 (Sat, 29 Dec 2007) | 2 lines Bug #1699: Define _BSD_SOURCE only on OpenBSD. ........ r59612 | raymond.hettinger | 2007-12-29 23:09:34 +0100 (Sat, 29 Dec 2007) | 1 line Simpler documentation for itertools.tee(). Should be backported. ........ r59613 | raymond.hettinger | 2007-12-29 23:16:24 +0100 (Sat, 29 Dec 2007) | 1 line Improve docs for itertools.groupby(). The use of xrange(0) to create a unique object is less obvious than object(). ........ r59620 | christian.heimes | 2007-12-31 15:47:07 +0100 (Mon, 31 Dec 2007) | 3 lines Added wininst-9.0.exe executable for VS 2008 Integrated bdist_wininst into PCBuild9 directory ........ r59621 | christian.heimes | 2007-12-31 15:51:18 +0100 (Mon, 31 Dec 2007) | 1 line Moved PCbuild directory to PC/VS7.1 ........ r59622 | christian.heimes | 2007-12-31 15:59:26 +0100 (Mon, 31 Dec 2007) | 1 line Fix paths for build bot ........ r59623 | christian.heimes | 2007-12-31 16:02:41 +0100 (Mon, 31 Dec 2007) | 1 line Fix paths for build bot, part 2 ........ r59624 | christian.heimes | 2007-12-31 16:18:55 +0100 (Mon, 31 Dec 2007) | 1 line Renamed PCBuild9 directory to PCBuild ........ --- command/bdist_wininst.py | 2 +- command/wininst-6.0.exe | Bin 0 -> 61440 bytes command/wininst-6.exe | Bin 61440 -> 0 bytes command/wininst-8.0.exe | Bin 0 -> 61440 bytes command/wininst-8.exe | Bin 61440 -> 0 bytes command/wininst-9.0.exe | Bin 0 -> 65536 bytes 6 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 command/wininst-6.0.exe delete mode 100644 command/wininst-6.exe create mode 100644 command/wininst-8.0.exe delete mode 100644 command/wininst-8.exe create mode 100644 command/wininst-9.0.exe diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 249b74c1..2d75a38f 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -304,5 +304,5 @@ class bdist_wininst(Command): directory = os.path.dirname(__file__) # we must use a wininst-x.y.exe built with the same C compiler # used for python. XXX What about mingw, borland, and so on? - filename = os.path.join(directory, "wininst-%s.exe" % bv) + filename = os.path.join(directory, "wininst-%.1f.exe" % bv) return open(filename, "rb").read() diff --git a/command/wininst-6.0.exe b/command/wininst-6.0.exe new file mode 100644 index 00000000..bd715250 Binary files /dev/null and b/command/wininst-6.0.exe differ diff --git a/command/wininst-6.exe b/command/wininst-6.exe deleted file mode 100644 index bd715250..00000000 Binary files a/command/wininst-6.exe and /dev/null differ diff --git a/command/wininst-8.0.exe b/command/wininst-8.0.exe new file mode 100644 index 00000000..7403bfab Binary files /dev/null and b/command/wininst-8.0.exe differ diff --git a/command/wininst-8.exe b/command/wininst-8.exe deleted file mode 100644 index 7403bfab..00000000 Binary files a/command/wininst-8.exe and /dev/null differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe new file mode 100644 index 00000000..5e0144c9 Binary files /dev/null and b/command/wininst-9.0.exe differ -- cgit v1.2.1 From 96dcf00b8e1a2ee3bdf5b0edbde4f28088a40f0a Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 1 Jan 2008 14:37:32 +0000 Subject: Added support for new Windows build dirs in PC/ to distutils.sysconfig --- sysconfig.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index 32b165ff..3cd647be 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -27,6 +27,10 @@ EXEC_PREFIX = os.path.normpath(sys.exec_prefix) project_base = os.path.dirname(os.path.abspath(sys.executable)) if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) +# PC/VS7.1 +if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, + os.path.pardir)) # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use -- cgit v1.2.1 From fdbce281befb4d8d7bf3c90e9a37f33164da71f7 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 1 Jan 2008 14:42:15 +0000 Subject: Merged revisions 59628-59641 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r59631 | christian.heimes | 2007-12-31 20:16:56 +0100 (Mon, 31 Dec 2007) | 1 line Fixed path ........ r59632 | christian.heimes | 2007-12-31 20:20:57 +0100 (Mon, 31 Dec 2007) | 1 line Fixed path to _ssl.c in Windows make file ........ r59633 | christian.heimes | 2007-12-31 20:23:22 +0100 (Mon, 31 Dec 2007) | 1 line Fixed path to _ssl.c in Windows make file, take two ........ r59634 | christian.heimes | 2007-12-31 20:25:22 +0100 (Mon, 31 Dec 2007) | 1 line Fixed path to _ssl.c in Windows make file, take three ... ........ r59635 | neal.norwitz | 2008-01-01 00:48:47 +0100 (Tue, 01 Jan 2008) | 1 line Fix refleak ........ r59637 | guido.van.rossum | 2008-01-01 05:15:29 +0100 (Tue, 01 Jan 2008) | 5 lines Fix an odd error which would only occur close to new year's eve, due to use of datetime.datetime.now() instead of utcnow() for comparison. (I think the test can still fail if it's executed pretty much *at* new year's eve, but that's not worth fixing.) ........ r59638 | christian.heimes | 2008-01-01 14:40:26 +0100 (Tue, 01 Jan 2008) | 1 line MSI uses back slashes as path separators ........ r59639 | christian.heimes | 2008-01-01 14:52:57 +0100 (Tue, 01 Jan 2008) | 1 line Added new wininst files to msi.py and adjusted some paths ........ r59640 | christian.heimes | 2008-01-01 14:58:16 +0100 (Tue, 01 Jan 2008) | 1 line The root of the project is two levels up from PC/VS7.1 ........ r59641 | christian.heimes | 2008-01-01 15:37:32 +0100 (Tue, 01 Jan 2008) | 1 line Added support for new Windows build dirs in PC/ to distutils.sysconfig ........ --- sysconfig.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index ba89c3b8..4d790ccb 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -27,6 +27,10 @@ EXEC_PREFIX = os.path.normpath(sys.exec_prefix) project_base = os.path.dirname(os.path.abspath(sys.executable)) if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) +# PC/VS7.1 +if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, + os.path.pardir)) # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use -- cgit v1.2.1 From 30e73c78342c8397efc90d0c9dfbd9f1a7aebf8c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 2 Jan 2008 18:59:36 +0000 Subject: Patch #1696. Don't attempt to call None.close() in dry-run mode. --- command/build_scripts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index bda4480c..d26fe94b 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -111,7 +111,8 @@ class build_scripts (Command): if f: f.close() else: - f.close() + if f: + f.close() self.copy_file(script, outfile) if os.name == 'posix': -- cgit v1.2.1 From 667a1ea083db9823c816fd690d6d61ca0d931710 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 2 Jan 2008 19:00:46 +0000 Subject: Patch #1696. Don't attempt to close None in dry-run mode. --- command/build_scripts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index b4810c3e..6af363b3 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -112,7 +112,8 @@ class build_scripts (Command): if f: f.close() else: - f.close() + if f: + f.close() self.copy_file(script, outfile) if os.name == 'posix': -- cgit v1.2.1 From 2f672dbe9907466088c0283b1ad4bc23b23107b9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 3 Jan 2008 23:01:04 +0000 Subject: Merged revisions 59666-59679 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r59666 | christian.heimes | 2008-01-02 19:28:32 +0100 (Wed, 02 Jan 2008) | 1 line Made vs9to8 Unix compatible ........ r59669 | guido.van.rossum | 2008-01-02 20:00:46 +0100 (Wed, 02 Jan 2008) | 2 lines Patch #1696. Don't attempt to close None in dry-run mode. ........ r59671 | jeffrey.yasskin | 2008-01-03 03:21:52 +0100 (Thu, 03 Jan 2008) | 6 lines Backport PEP 3141 from the py3k branch to the trunk. This includes r50877 (just the complex_pow part), r56649, r56652, r56715, r57296, r57302, r57359, r57361, r57372, r57738, r57739, r58017, r58039, r58040, and r59390, and new documentation. The only significant difference is that round(x) returns a float to preserve backward-compatibility. See http://bugs.python.org/issue1689. ........ r59672 | christian.heimes | 2008-01-03 16:41:30 +0100 (Thu, 03 Jan 2008) | 1 line Issue #1726: Remove Python/atof.c from PCBuild/pythoncore.vcproj ........ r59675 | guido.van.rossum | 2008-01-03 20:12:44 +0100 (Thu, 03 Jan 2008) | 4 lines Issue #1700, reported by Nguyen Quan Son, fix by Fredruk Lundh: Regular Expression inline flags not handled correctly for some unicode characters. (Forward port from 2.5.2.) ........ r59676 | christian.heimes | 2008-01-03 21:23:15 +0100 (Thu, 03 Jan 2008) | 1 line Added math.isinf() and math.isnan() ........ r59677 | christian.heimes | 2008-01-03 22:14:48 +0100 (Thu, 03 Jan 2008) | 1 line Some build bots don't compile mathmodule. There is an issue with the long definition of pi and euler ........ r59678 | christian.heimes | 2008-01-03 23:16:32 +0100 (Thu, 03 Jan 2008) | 2 lines Modified PyImport_Import and PyImport_ImportModule to always use absolute imports by calling __import__ with an explicit level of 0 Added a new API function PyImport_ImportModuleNoBlock. It solves the problem with dead locks when mixing threads and imports ........ r59679 | christian.heimes | 2008-01-03 23:32:26 +0100 (Thu, 03 Jan 2008) | 1 line Added copysign(x, y) function to the math module ........ --- command/build_scripts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 227bb9c7..7b44264c 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -110,7 +110,8 @@ class build_scripts(Command): if f: f.close() else: - f.close() + if f: + f.close() self.copy_file(script, outfile) if os.name == 'posix': -- cgit v1.2.1 From 8b891e185114ca8306b56c80aef8866034f179c8 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 6 Jan 2008 21:13:42 +0000 Subject: Fix more exception slicing. --- file_util.py | 2 +- spawn.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/file_util.py b/file_util.py index e29e90e6..69190601 100644 --- a/file_util.py +++ b/file_util.py @@ -139,7 +139,7 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, macostools.copy(src, dst, 0, preserve_times) except os.error as exc: raise DistutilsFileError( - "could not copy '%s' to '%s': %s" % (src, dst, exc[-1])) + "could not copy '%s' to '%s': %s" % (src, dst, exc.args[-1])) # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) diff --git a/spawn.py b/spawn.py index 0aee2bc3..4c536d28 100644 --- a/spawn.py +++ b/spawn.py @@ -67,7 +67,7 @@ def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): except OSError as exc: # this seems to happen when the command isn't found raise DistutilsExecError( - "command '%s' failed: %s" % (cmd[0], exc[-1])) + "command '%s' failed: %s" % (cmd[0], exc.args[-1])) if rc != 0: # and this reflects the command running but failing raise DistutilsExecError( @@ -88,7 +88,7 @@ def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): except OSError as exc: # this seems to happen when the command isn't found raise DistutilsExecError( - "command '%s' failed: %s" % (cmd[0], exc[-1])) + "command '%s' failed: %s" % (cmd[0], exc.args[-1])) if rc != 0: # and this reflects the command running but failing print("command '%s' failed with exit status %d" % (cmd[0], rc)) @@ -124,7 +124,7 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): if exc.errno == errno.EINTR: continue raise DistutilsExecError( - "command '%s' failed: %s" % (cmd[0], exc[-1])) + "command '%s' failed: %s" % (cmd[0], exc.args[-1])) if os.WIFSIGNALED(status): raise DistutilsExecError( "command '%s' terminated by signal %d" -- cgit v1.2.1 From 60d10b8328e3c77e3e722bb4015477c9ac7854dd Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 6 Jan 2008 21:41:49 +0000 Subject: Missed one because of indirection. --- util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.py b/util.py index e0ae2e5e..917f1d0b 100644 --- a/util.py +++ b/util.py @@ -269,7 +269,7 @@ def grok_environment_error (exc, prefix="error: "): # include the filename in the exception object! error = prefix + "%s" % exc.strerror else: - error = prefix + str(exc[-1]) + error = prefix + str(exc.args[-1]) return error -- cgit v1.2.1 From c39e9fa5952a01de7527da5fc1ec0d04451e300e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 21 Jan 2008 17:42:40 +0000 Subject: #1530959: change distutils build dir for --with-pydebug python builds. --- command/build.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/command/build.py b/command/build.py index 9ae0a292..bca031f7 100644 --- a/command/build.py +++ b/command/build.py @@ -69,6 +69,12 @@ class build (Command): plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3]) + # Make it so Python 2.x and Python 2.x with --with-pydebug don't + # share the same build directories. Doing so confuses the build + # process for C modules + if hasattr(sys, 'gettotalrefcount'): + plat_specifier += '-pydebug' + # 'build_purelib' and 'build_platlib' just default to 'lib' and # 'lib.' under the base build directory. We only use one of # them for a given distribution, though -- -- cgit v1.2.1 From 32ff6ab5969de182ade4a74e0159e28e301d79d7 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 21 Jan 2008 20:36:10 +0000 Subject: Merged revisions 60151-60159,60161-60168,60170,60172-60173,60175 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r60151 | christian.heimes | 2008-01-21 14:11:15 +0100 (Mon, 21 Jan 2008) | 1 line A bunch of header files were not listed as dependencies for object files. Changes to files like Parser/parser.h weren't picked up by make. ........ r60152 | georg.brandl | 2008-01-21 15:16:46 +0100 (Mon, 21 Jan 2008) | 3 lines #1087741: make mmap.mmap the type of mmap objects, not a factory function. Allow it to be subclassed. ........ r60153 | georg.brandl | 2008-01-21 15:18:14 +0100 (Mon, 21 Jan 2008) | 2 lines mmap is an extension module. ........ r60154 | georg.brandl | 2008-01-21 17:28:13 +0100 (Mon, 21 Jan 2008) | 2 lines Fix example. ........ r60155 | georg.brandl | 2008-01-21 17:34:07 +0100 (Mon, 21 Jan 2008) | 2 lines #1555501: document plistlib and move it to the general library. ........ r60156 | georg.brandl | 2008-01-21 17:36:00 +0100 (Mon, 21 Jan 2008) | 2 lines Add a stub for bundlebuilder documentation. ........ r60157 | georg.brandl | 2008-01-21 17:46:58 +0100 (Mon, 21 Jan 2008) | 2 lines Removing bundlebuilder docs again -- it's not to be used anymore (see #779825). ........ r60158 | georg.brandl | 2008-01-21 17:51:51 +0100 (Mon, 21 Jan 2008) | 2 lines #997912: acknowledge nested scopes in tutorial. ........ r60159 | vinay.sajip | 2008-01-21 18:02:26 +0100 (Mon, 21 Jan 2008) | 1 line Fix: #1836: Off-by-one bug in TimedRotatingFileHandler rollover calculation. Patch thanks to Kathryn M. Kowalski. ........ r60161 | georg.brandl | 2008-01-21 18:13:03 +0100 (Mon, 21 Jan 2008) | 2 lines Adapt pydoc to new doc URLs. ........ r60162 | georg.brandl | 2008-01-21 18:17:00 +0100 (Mon, 21 Jan 2008) | 2 lines Fix old link. ........ r60163 | georg.brandl | 2008-01-21 18:22:06 +0100 (Mon, 21 Jan 2008) | 2 lines #1726198: replace while 1: fp.readline() with file iteration. ........ r60164 | georg.brandl | 2008-01-21 18:29:23 +0100 (Mon, 21 Jan 2008) | 2 lines Clarify $ behavior in re docstring. #1631394. ........ r60165 | vinay.sajip | 2008-01-21 18:39:22 +0100 (Mon, 21 Jan 2008) | 1 line Minor documentation change - hyperlink tidied up. ........ r60166 | georg.brandl | 2008-01-21 18:42:40 +0100 (Mon, 21 Jan 2008) | 2 lines #1530959: change distutils build dir for --with-pydebug python builds. ........ r60167 | vinay.sajip | 2008-01-21 19:16:05 +0100 (Mon, 21 Jan 2008) | 1 line Updated to include news on recent logging fixes and documentation changes. ........ r60168 | georg.brandl | 2008-01-21 19:35:49 +0100 (Mon, 21 Jan 2008) | 3 lines Issue #1882: when compiling code from a string, encoding cookies in the second line of code were not always recognized correctly. ........ r60170 | georg.brandl | 2008-01-21 19:36:51 +0100 (Mon, 21 Jan 2008) | 2 lines Add NEWS entry for #1882. ........ r60172 | georg.brandl | 2008-01-21 19:41:24 +0100 (Mon, 21 Jan 2008) | 2 lines Use original location of document, which has translations. ........ r60173 | walter.doerwald | 2008-01-21 21:18:04 +0100 (Mon, 21 Jan 2008) | 2 lines Follow PEP 8 in module docstring. ........ r60175 | georg.brandl | 2008-01-21 21:20:53 +0100 (Mon, 21 Jan 2008) | 2 lines Adapt to latest doctools refactoring. ........ --- command/build.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/command/build.py b/command/build.py index 1f2ce062..4fe95b0c 100644 --- a/command/build.py +++ b/command/build.py @@ -66,6 +66,12 @@ class build(Command): def finalize_options(self): plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3]) + # Make it so Python 2.x and Python 2.x with --with-pydebug don't + # share the same build directories. Doing so confuses the build + # process for C modules + if hasattr(sys, 'gettotalrefcount'): + plat_specifier += '-pydebug' + # 'build_purelib' and 'build_platlib' just default to 'lib' and # 'lib.' under the base build directory. We only use one of # them for a given distribution, though -- -- cgit v1.2.1 From ef21fafddb93c89ba8bed432a50727c45d2132e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Fri, 1 Feb 2008 22:58:17 +0000 Subject: Bump distutils version number to match Python version. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 86ad44fe..8c954c22 100644 --- a/__init__.py +++ b/__init__.py @@ -20,4 +20,4 @@ __revision__ = "$Id$" # In general, major and minor version should loosely follow the Python # version number the distutils code was shipped with. # -__version__ = "2.5.1" +__version__ = "2.6.0" -- cgit v1.2.1 From 2f0340a9ac492d58a9220fe9eef0daccdf997b56 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 3 Feb 2008 14:34:18 +0000 Subject: Fixed paths to Windows build directories in build_ext.py Use vsbuild instead of devenv in build.bat and _bsddb.vcproj --- command/build_ext.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index ecfa177d..29d5668c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -182,13 +182,13 @@ class build_ext (Command): self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) if MSVC_VERSION == 9: self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PCBuild9')) + 'PCbuild')) elif MSVC_VERSION == 8: self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PCBuild8', 'win32release')) + 'PC', 'VS8.0', 'win32release')) else: self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PCBuild')) + 'PC', 'VS7.1')) # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory -- cgit v1.2.1 From e5260241bc5e4e91531a72a9efd5b0f46ad8704e Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 3 Feb 2008 16:51:08 +0000 Subject: Merged revisions 60481,60485,60489-60520,60523-60527,60530-60533,60535-60538,60540-60551 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk NOTE: I blocked the following revisions: svnmerge.py block -r 60521,60522,60528,60529,60534,60539 The new tests must be merged with lots of manual work. ........ r60493 | georg.brandl | 2008-02-01 12:59:08 +0100 (Fri, 01 Feb 2008) | 2 lines Update IPv6 RFC number. ........ r60497 | georg.brandl | 2008-02-01 16:50:15 +0100 (Fri, 01 Feb 2008) | 2 lines Add link checker builder, written for GHOP by Thomas Lamb. ........ r60500 | georg.brandl | 2008-02-01 19:08:09 +0100 (Fri, 01 Feb 2008) | 2 lines Rename batch file. ........ r60504 | christian.heimes | 2008-02-01 19:49:26 +0100 (Fri, 01 Feb 2008) | 1 line More int -> pid_t. ........ r60507 | georg.brandl | 2008-02-01 20:24:01 +0100 (Fri, 01 Feb 2008) | 2 lines Wording nit. ........ r60510 | georg.brandl | 2008-02-01 21:45:33 +0100 (Fri, 01 Feb 2008) | 2 lines Update for latest sphinx latex writer. ........ r60511 | raymond.hettinger | 2008-02-01 22:30:23 +0100 (Fri, 01 Feb 2008) | 1 line Issue #1996: float.as_integer_ratio() should return fraction in lowest terms. ........ r60512 | raymond.hettinger | 2008-02-01 23:15:52 +0100 (Fri, 01 Feb 2008) | 1 line Integer ratio should return ints instead of longs whereever possible. ........ r60513 | raymond.hettinger | 2008-02-01 23:22:50 +0100 (Fri, 01 Feb 2008) | 1 line labs() takes a long for an input. ........ r60514 | raymond.hettinger | 2008-02-01 23:42:59 +0100 (Fri, 01 Feb 2008) | 1 line Test round-trip on float.as_integer_ratio() and float.__truediv__(). ........ r60515 | marc-andre.lemburg | 2008-02-01 23:58:17 +0100 (Fri, 01 Feb 2008) | 3 lines Bump distutils version number to match Python version. ........ r60516 | raymond.hettinger | 2008-02-02 00:12:19 +0100 (Sat, 02 Feb 2008) | 1 line Fix int/long typecase. Add check for non-binary floating point. ........ r60517 | raymond.hettinger | 2008-02-02 00:45:44 +0100 (Sat, 02 Feb 2008) | 1 line Add protection from weirdness while scaling the mantissa to an integer. ........ r60518 | raymond.hettinger | 2008-02-02 06:11:40 +0100 (Sat, 02 Feb 2008) | 1 line Simpler solution to handling non-IEEE 754 environments. ........ r60519 | raymond.hettinger | 2008-02-02 06:24:44 +0100 (Sat, 02 Feb 2008) | 1 line Neaten-up a bit. ........ r60520 | georg.brandl | 2008-02-02 10:56:20 +0100 (Sat, 02 Feb 2008) | 2 lines Amendments to the urllib2 docs, written for GHOP by Thomas Lamb. ........ r60525 | georg.brandl | 2008-02-02 11:49:58 +0100 (Sat, 02 Feb 2008) | 3 lines Add email example how to send a multipart message. Written for GHOP by Martin Matejek. ........ r60526 | georg.brandl | 2008-02-02 12:05:00 +0100 (Sat, 02 Feb 2008) | 2 lines Rewrite test_socketserver as unittest, written for GHOP by Benjamin Petersen. ........ r60527 | georg.brandl | 2008-02-02 12:05:34 +0100 (Sat, 02 Feb 2008) | 2 lines Add GHOP contributor. ........ r60530 | mark.dickinson | 2008-02-02 18:16:13 +0100 (Sat, 02 Feb 2008) | 2 lines Make the Rational constructor accept '3.' and '.2' as well as '3.2'. ........ r60531 | neal.norwitz | 2008-02-02 19:52:51 +0100 (Sat, 02 Feb 2008) | 1 line Update the leaky tests (ie, ignore these tests if they report leaks). This version has been running for a while. ........ r60533 | skip.montanaro | 2008-02-02 20:11:57 +0100 (Sat, 02 Feb 2008) | 7 lines Split the refleak mail body into two parts, the first being those failing tests which are deemed more important issues, the second those which are known to have difficult to solve problems and are generally expected to leak. Hopefully this doesn't break the script... ........ r60535 | georg.brandl | 2008-02-03 01:04:50 +0100 (Sun, 03 Feb 2008) | 3 lines Wait for a delay before reaping children -- this should fix the test_socketserver failures on several platforms. ........ r60536 | brett.cannon | 2008-02-03 03:07:55 +0100 (Sun, 03 Feb 2008) | 2 lines Fix a minor typo. ........ r60537 | brett.cannon | 2008-02-03 03:08:45 +0100 (Sun, 03 Feb 2008) | 3 lines Directories from CPPFLAGS and LDFLAGS were being added in the reverse order for searches as to how they were listed in the environment variable. ........ r60538 | brett.cannon | 2008-02-03 03:34:14 +0100 (Sun, 03 Feb 2008) | 2 lines Remove extra tick marks and add a missing closing parenthesis. ........ r60540 | andrew.macintyre | 2008-02-03 07:58:06 +0100 (Sun, 03 Feb 2008) | 2 lines Update OS/2 EMX build bits for 2.6. ........ r60541 | andrew.macintyre | 2008-02-03 08:01:11 +0100 (Sun, 03 Feb 2008) | 2 lines Rename module definition file to reflect v2.6. ........ r60542 | andrew.macintyre | 2008-02-03 08:07:31 +0100 (Sun, 03 Feb 2008) | 6 lines The wrapper function is supposed to be for spawnvpe() so that's what we should call [this wrapper only available on OS/2]. Backport candidate to 2.5. ........ r60544 | gregory.p.smith | 2008-02-03 08:20:53 +0100 (Sun, 03 Feb 2008) | 6 lines Merge this fix from the pybsddb tree: r293 | jcea | 2008-01-31 01:08:19 -0800 (Thu, 31 Jan 2008) | 4 lines Solved memory leak when using cursors with databases without environment. ........ r60546 | gregory.p.smith | 2008-02-03 09:01:46 +0100 (Sun, 03 Feb 2008) | 2 lines remove a repeated occurance of a hardcoded berkeleydb library version number ........ r60549 | brett.cannon | 2008-02-03 10:59:21 +0100 (Sun, 03 Feb 2008) | 2 lines Add an entry for r60537. ........ r60550 | georg.brandl | 2008-02-03 13:29:00 +0100 (Sun, 03 Feb 2008) | 2 lines #2003: fix sentence. ........ r60551 | christian.heimes | 2008-02-03 15:34:18 +0100 (Sun, 03 Feb 2008) | 2 lines Fixed paths to Windows build directories in build_ext.py Use vsbuild instead of devenv in build.bat and _bsddb.vcproj ........ --- __init__.py | 2 +- command/build_ext.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/__init__.py b/__init__.py index f71c3adb..0c3d939d 100644 --- a/__init__.py +++ b/__init__.py @@ -18,4 +18,4 @@ __revision__ = "$Id$" # In general, major and minor version should loosely follow the Python # version number the distutils code was shipped with. # -__version__ = "2.5.1" +__version__ = "2.6.0" diff --git a/command/build_ext.py b/command/build_ext.py index 6d42278e..ff84bca7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -178,13 +178,13 @@ class build_ext(Command): self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) if MSVC_VERSION == 9: self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PCBuild9')) + 'PCbuild')) elif MSVC_VERSION == 8: self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PCBuild8', 'win32release')) + 'PC', 'VS8.0', 'win32release')) else: self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PCBuild')) + 'PC', 'VS7.1')) # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory -- cgit v1.2.1 From 21a51933d80f59b70313d7fb9689ed54093944fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Tue, 5 Feb 2008 14:50:40 +0000 Subject: Keep distutils Python 2.1 compatible (or even Python 2.4 in this case). --- sysconfig.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 3cd647be..1aaaa28f 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -37,8 +37,12 @@ if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): # different (hard-wired) directories. # Setup.local is available for Makefile builds including VPATH builds, # Setup.dist is available on Windows -python_build = any(os.path.isfile(os.path.join(project_base, "Modules", fn)) - for fn in ("Setup.dist", "Setup.local")) +def _python_build(): + for fn in ("Setup.dist", "Setup.local"): + if os.path.isfile(os.path.join(project_base, "Modules", fn)): + return True + return False +python_build = _python_build() def get_python_version(): -- cgit v1.2.1 From 71947cc1efed4a4f2ce1980ac465d70ea2542f29 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 6 Feb 2008 14:31:34 +0000 Subject: Merged revisions 60481,60485,60489-60492,60494-60496,60498-60499,60501-60503,60505-60506,60508-60509,60523-60524,60532,60543,60545,60547-60548,60552,60554,60556-60559,60561-60562,60568-60598,60600-60616 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r60568 | christian.heimes | 2008-02-04 19:48:38 +0100 (Mon, 04 Feb 2008) | 1 line Increase debugging to investige failing tests on some build bots ........ r60570 | christian.heimes | 2008-02-04 20:30:05 +0100 (Mon, 04 Feb 2008) | 1 line Small adjustments for test compact freelist test. It's no passing on Windows as well. ........ r60573 | amaury.forgeotdarc | 2008-02-04 21:53:14 +0100 (Mon, 04 Feb 2008) | 2 lines Correct quotes in NEWS file ........ r60575 | amaury.forgeotdarc | 2008-02-04 22:45:05 +0100 (Mon, 04 Feb 2008) | 13 lines #1750076: Debugger did not step on every iteration of a while statement. The mapping between bytecode offsets and source lines (lnotab) did not contain an entry for the beginning of the loop. Now it does, and the lnotab can be a bit larger: in particular, several statements on the same line generate several entries. However, this does not bother the settrace function, which will trigger only one 'line' event. The lnotab seems to be exactly the same as with python2.4. ........ r60584 | amaury.forgeotdarc | 2008-02-05 01:26:21 +0100 (Tue, 05 Feb 2008) | 3 lines Change r60575 broke test_compile: there is no need to emit co_lnotab item when both offsets are zeros. ........ r60587 | skip.montanaro | 2008-02-05 03:32:16 +0100 (Tue, 05 Feb 2008) | 1 line sync with most recent version from python-mode sf project ........ r60588 | lars.gustaebel | 2008-02-05 12:51:40 +0100 (Tue, 05 Feb 2008) | 5 lines Issue #2004: Use mode 0700 for temporary directories and default permissions for missing directories. (will backport to 2.5) ........ r60590 | georg.brandl | 2008-02-05 13:01:24 +0100 (Tue, 05 Feb 2008) | 2 lines Convert external links to internal links. Fixes #2010. ........ r60592 | marc-andre.lemburg | 2008-02-05 15:50:40 +0100 (Tue, 05 Feb 2008) | 3 lines Keep distutils Python 2.1 compatible (or even Python 2.4 in this case). ........ r60593 | andrew.kuchling | 2008-02-05 17:06:57 +0100 (Tue, 05 Feb 2008) | 5 lines Update PEP URL. (This code is duplicated between pydoc and DocXMLRPCServer; maybe it should be refactored as a GHOP project.) 2.5.2 backport candidate. ........ r60596 | guido.van.rossum | 2008-02-05 18:32:15 +0100 (Tue, 05 Feb 2008) | 2 lines In the experimental 'Scanner' feature, the group count was set wrong. ........ r60602 | facundo.batista | 2008-02-05 20:03:32 +0100 (Tue, 05 Feb 2008) | 3 lines Issue 1951. Converts wave test cases to unittest. ........ r60603 | georg.brandl | 2008-02-05 20:07:10 +0100 (Tue, 05 Feb 2008) | 2 lines Actually run the test. ........ r60604 | skip.montanaro | 2008-02-05 20:24:30 +0100 (Tue, 05 Feb 2008) | 2 lines correct object name ........ r60605 | georg.brandl | 2008-02-05 20:58:17 +0100 (Tue, 05 Feb 2008) | 7 lines * Use the same code to profile for test_profile and test_cprofile. * Convert both to unittest. * Use the same unit testing code. * Include the expected output in both test files. * Make it possible to regenerate the expected output by running the file as a script with an '-r' argument. ........ r60613 | raymond.hettinger | 2008-02-06 02:49:00 +0100 (Wed, 06 Feb 2008) | 1 line Sync-up with Py3k work. ........ r60614 | christian.heimes | 2008-02-06 13:44:34 +0100 (Wed, 06 Feb 2008) | 1 line Limit free list of method and builtin function objects to 256 entries each. ........ r60616 | christian.heimes | 2008-02-06 14:33:44 +0100 (Wed, 06 Feb 2008) | 7 lines Unified naming convention for free lists and their limits. All free lists in Object/ are named ``free_list``, the counter ``numfree`` and the upper limit is a macro ``PyName_MAXFREELIST`` inside an #ifndef block. The chances should make it easier to adjust Python for platforms with less memory, e.g. mobile phones. ........ --- sysconfig.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 4d790ccb..39216a53 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -37,8 +37,12 @@ if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): # different (hard-wired) directories. # Setup.local is available for Makefile builds including VPATH builds, # Setup.dist is available on Windows -python_build = any(os.path.isfile(os.path.join(project_base, "Modules", fn)) - for fn in ("Setup.dist", "Setup.local")) +def _python_build(): + for fn in ("Setup.dist", "Setup.local"): + if os.path.isfile(os.path.join(project_base, "Modules", fn)): + return True + return False +python_build = _python_build() def get_python_version(): """Return a string containing the major and minor Python version, -- cgit v1.2.1 From b1165a0f3caf19626c96f1b397bd52a9d1c080f4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Feb 2008 14:23:38 +0000 Subject: Close manifest file. This change doesn't make any difference to CPython, but is a necessary fix for Jython. --- command/sdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/sdist.py b/command/sdist.py index 3dfe6f21..a06b1953 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -383,6 +383,7 @@ class sdist (Command): if line[-1] == '\n': line = line[0:-1] self.filelist.append(line) + manifest.close() # read_manifest () -- cgit v1.2.1 From 36bf11e144f03aaf5cce1d8cd8efcdc1e3a53b35 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 21 Feb 2008 18:18:37 +0000 Subject: Removed uses of dict.has_key() from distutils, and uses of callable() from copy_reg.py, so the interpreter now starts up without warnings when '-3' is given. More work like this needs to be done in the rest of the stdlib. --- archive_util.py | 2 +- ccompiler.py | 2 +- command/build_ext.py | 2 +- command/install.py | 2 +- command/register.py | 4 ++-- command/upload.py | 2 +- core.py | 6 +++--- dir_util.py | 2 +- dist.py | 8 ++++---- fancy_getopt.py | 8 ++++---- msvccompiler.py | 2 +- sysconfig.py | 22 +++++++++++----------- text_file.py | 4 ++-- util.py | 6 +++--- 14 files changed, 36 insertions(+), 36 deletions(-) diff --git a/archive_util.py b/archive_util.py index 6aa5e635..a9c6a5b4 100644 --- a/archive_util.py +++ b/archive_util.py @@ -124,7 +124,7 @@ ARCHIVE_FORMATS = { def check_archive_formats (formats): for format in formats: - if not ARCHIVE_FORMATS.has_key(format): + if format not in ARCHIVE_FORMATS: return format else: return None diff --git a/ccompiler.py b/ccompiler.py index 1349abeb..0ed9a40a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -159,7 +159,7 @@ class CCompiler: # basically the same things with Unix C compilers. for key in args.keys(): - if not self.executables.has_key(key): + if key not in self.executables: raise ValueError, \ "unknown executable '%s' for class %s" % \ (key, self.__class__.__name__) diff --git a/command/build_ext.py b/command/build_ext.py index 29d5668c..3042fe02 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -362,7 +362,7 @@ class build_ext (Command): # Medium-easy stuff: same syntax/semantics, different names. ext.runtime_library_dirs = build_info.get('rpath') - if build_info.has_key('def_file'): + if 'def_file' in build_info: log.warn("'def_file' element of build info dict " "no longer supported") diff --git a/command/install.py b/command/install.py index 453151d0..c6212102 100644 --- a/command/install.py +++ b/command/install.py @@ -352,7 +352,7 @@ class install (Command): opt_name = opt[0] if opt_name[-1] == "=": opt_name = opt_name[0:-1] - if self.negative_opt.has_key(opt_name): + if opt_name in self.negative_opt: opt_name = string.translate(self.negative_opt[opt_name], longopt_xlate) val = not getattr(self, opt_name) diff --git a/command/register.py b/command/register.py index 200e61e2..d1a9100d 100644 --- a/command/register.py +++ b/command/register.py @@ -120,7 +120,7 @@ class register(Command): # see if we can short-cut and get the username/password from the # config config = None - if os.environ.has_key('HOME'): + if 'HOME' in os.environ: rc = os.path.join(os.environ['HOME'], '.pypirc') if os.path.exists(rc): print 'Using PyPI login from %s'%rc @@ -163,7 +163,7 @@ Your selection [default 1]: ''', print 'Server response (%s): %s'%(code, result) # possibly save the login - if os.environ.has_key('HOME') and config is None and code == 200: + if 'HOME' in os.environ and config is None and code == 200: rc = os.path.join(os.environ['HOME'], '.pypirc') print 'I can store your PyPI login so future submissions will be faster.' print '(the login will be stored in %s)'%rc diff --git a/command/upload.py b/command/upload.py index 7461e5f4..301a1595 100644 --- a/command/upload.py +++ b/command/upload.py @@ -46,7 +46,7 @@ class upload(Command): raise DistutilsOptionError( "Must use --sign for --identity to have meaning" ) - if os.environ.has_key('HOME'): + if 'HOME' in os.environ: rc = os.path.join(os.environ['HOME'], '.pypirc') if os.path.exists(rc): self.announce('Using PyPI login from %s' % rc) diff --git a/core.py b/core.py index c9c6f037..c40dd0a7 100644 --- a/core.py +++ b/core.py @@ -101,9 +101,9 @@ def setup (**attrs): else: klass = Distribution - if not attrs.has_key('script_name'): + if 'script_name' not in attrs: attrs['script_name'] = os.path.basename(sys.argv[0]) - if not attrs.has_key('script_args'): + if 'script_args' not in attrs: attrs['script_args'] = sys.argv[1:] # Create the Distribution instance, using the remaining arguments @@ -111,7 +111,7 @@ def setup (**attrs): try: _setup_distribution = dist = klass(attrs) except DistutilsSetupError, msg: - if attrs.has_key('name'): + if 'name' in attrs: raise SystemExit, "error in %s setup command: %s" % \ (attrs['name'], msg) else: diff --git a/dir_util.py b/dir_util.py index 43994db3..77f25325 100644 --- a/dir_util.py +++ b/dir_util.py @@ -207,7 +207,7 @@ def remove_tree (directory, verbose=0, dry_run=0): apply(cmd[0], (cmd[1],)) # remove dir from cache if it's already there abspath = os.path.abspath(cmd[1]) - if _path_created.has_key(abspath): + if abspath in _path_created: del _path_created[abspath] except (IOError, OSError), exc: log.warn(grok_environment_error( diff --git a/dist.py b/dist.py index ff49886d..d098cb97 100644 --- a/dist.py +++ b/dist.py @@ -239,7 +239,7 @@ Common commands: (see '--help-commands' for more) for (opt, val) in cmd_options.items(): opt_dict[opt] = ("setup script", val) - if attrs.has_key('licence'): + if 'licence' in attrs: attrs['license'] = attrs['licence'] del attrs['licence'] msg = "'licence' distribution option is deprecated; use 'license'" @@ -343,7 +343,7 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - if os.environ.has_key('HOME'): + if 'HOME' in os.environ: user_file = os.path.join(os.environ.get('HOME'), user_filename) if os.path.isfile(user_file): files.append(user_file) @@ -388,7 +388,7 @@ Common commands: (see '--help-commands' for more) # If there was a "global" section in the config file, use it # to set Distribution options. - if self.command_options.has_key('global'): + if 'global' in self.command_options: for (opt, (src, val)) in self.command_options['global'].items(): alias = self.negative_opt.get(opt) try: @@ -907,7 +907,7 @@ Common commands: (see '--help-commands' for more) try: is_string = type(value) is StringType - if neg_opt.has_key(option) and is_string: + if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: setattr(command_obj, option, strtobool(value)) diff --git a/fancy_getopt.py b/fancy_getopt.py index 218ed73f..31cf0c5c 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -97,7 +97,7 @@ class FancyGetopt: self._build_index() def add_option (self, long_option, short_option=None, help_string=None): - if self.option_index.has_key(long_option): + if long_option in self.option_index: raise DistutilsGetoptError, \ "option conflict: already an option '%s'" % long_option else: @@ -109,7 +109,7 @@ class FancyGetopt: def has_option (self, long_option): """Return true if the option table for this parser has an option with long name 'long_option'.""" - return self.option_index.has_key(long_option) + return long_option in self.option_index def get_attr_name (self, long_option): """Translate long option name 'long_option' to the form it @@ -121,11 +121,11 @@ class FancyGetopt: def _check_alias_dict (self, aliases, what): assert type(aliases) is DictionaryType for (alias, opt) in aliases.items(): - if not self.option_index.has_key(alias): + if alias not in self.option_index: raise DistutilsGetoptError, \ ("invalid %s '%s': " "option '%s' not defined") % (what, alias, alias) - if not self.option_index.has_key(opt): + if opt not in self.option_index: raise DistutilsGetoptError, \ ("invalid %s '%s': " "aliased option '%s' not defined") % (what, alias, opt) diff --git a/msvccompiler.py b/msvccompiler.py index fa123462..f3acb534 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -252,7 +252,7 @@ class MSVCCompiler (CCompiler) : def initialize(self): self.__paths = [] - if os.environ.has_key("DISTUTILS_USE_SDK") and os.environ.has_key("MSSdk") and self.find_exe("cl.exe"): + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): # Assume that the SDK set up everything alright; don't try to be # smarter self.cc = "cl.exe" diff --git a/sysconfig.py b/sysconfig.py index 1aaaa28f..506cf385 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -161,22 +161,22 @@ def customize_compiler(compiler): get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SO') - if os.environ.has_key('CC'): + if 'CC' in os.environ: cc = os.environ['CC'] - if os.environ.has_key('CXX'): + if 'CXX' in os.environ: cxx = os.environ['CXX'] - if os.environ.has_key('LDSHARED'): + if 'LDSHARED' in os.environ: ldshared = os.environ['LDSHARED'] - if os.environ.has_key('CPP'): + if 'CPP' in os.environ: cpp = os.environ['CPP'] else: cpp = cc + " -E" # not always - if os.environ.has_key('LDFLAGS'): + if 'LDFLAGS' in os.environ: ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if os.environ.has_key('CFLAGS'): + if 'CFLAGS' in os.environ: cflags = opt + ' ' + os.environ['CFLAGS'] ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if os.environ.has_key('CPPFLAGS'): + if 'CPPFLAGS' in os.environ: cpp = cpp + ' ' + os.environ['CPPFLAGS'] cflags = cflags + ' ' + os.environ['CPPFLAGS'] ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] @@ -291,12 +291,12 @@ def parse_makefile(fn, g=None): if m: n = m.group(1) found = True - if done.has_key(n): + if n in done: item = str(done[n]) - elif notdone.has_key(n): + elif n in notdone: # get it on a subsequent round found = False - elif os.environ.has_key(n): + elif n in os.environ: # do it like make: fall back to environment item = os.environ[n] else: @@ -380,7 +380,7 @@ def _init_posix(): # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so # it needs to be compatible. # If it isn't set we set it to the configure-time value - if sys.platform == 'darwin' and g.has_key('MACOSX_DEPLOYMENT_TARGET'): + if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g: cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') if cur_target == '': diff --git a/text_file.py b/text_file.py index 67efd65e..ff2878de 100644 --- a/text_file.py +++ b/text_file.py @@ -89,7 +89,7 @@ class TextFile: # set values for all options -- either from client option hash # or fallback to default_options for opt in self.default_options.keys(): - if options.has_key (opt): + if opt in options: setattr (self, opt, options[opt]) else: @@ -97,7 +97,7 @@ class TextFile: # sanity check client option hash for opt in options.keys(): - if not self.default_options.has_key (opt): + if opt not in self.default_options: raise KeyError, "invalid TextFile option '%s'" % opt if file is None: diff --git a/util.py b/util.py index 8979634e..deb9a0a0 100644 --- a/util.py +++ b/util.py @@ -219,11 +219,11 @@ def check_environ (): if _environ_checked: return - if os.name == 'posix' and not os.environ.has_key('HOME'): + if os.name == 'posix' and 'HOME' not in os.environ: import pwd os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] - if not os.environ.has_key('PLAT'): + if 'PLAT' not in os.environ: os.environ['PLAT'] = get_platform() _environ_checked = 1 @@ -241,7 +241,7 @@ def subst_vars (s, local_vars): check_environ() def _subst (match, local_vars=local_vars): var_name = match.group(1) - if local_vars.has_key(var_name): + if var_name in local_vars: return str(local_vars[var_name]) else: return os.environ[var_name] -- cgit v1.2.1 From a5c15c1aba945408f59b87959d31be1b74a2cb2a Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 22 Feb 2008 16:37:40 +0000 Subject: Merged revisions 60481,60485,60489-60492,60494-60496,60498-60499,60501-60503,60505-60506,60508-60509,60523-60524,60532,60543,60545,60547-60548,60552,60554,60556-60559,60561-60562,60569,60571-60572,60574,60576-60583,60585-60586,60589,60591,60594-60595,60597-60598,60600-60601,60606-60612,60615,60617,60619-60621,60623-60625,60627-60629,60631,60633,60635,60647,60650,60652,60654,60656,60658-60659,60664-60666,60668-60670,60672,60676,60678,60680-60683,60685-60686,60688,60690,60692-60694,60697-60700,60705-60706,60708,60711,60714,60720,60724-60730,60732,60736,60742,60744,60746,60748,60750-60751,60753,60756-60757,60759-60761,60763-60764,60766,60769-60770,60774-60784,60787-60789,60793,60796,60799-60809,60812-60813,60815-60821,60823-60826,60828-60829,60831-60834,60836,60838-60839,60846-60849,60852-60854,60856-60859,60861-60870,60874-60875,60880-60881,60886,60888-60890,60892,60894-60898,60900-60931,60933-60958 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r60901 | eric.smith | 2008-02-19 14:21:56 +0100 (Tue, 19 Feb 2008) | 1 line Added PEP 3101. ........ r60907 | georg.brandl | 2008-02-20 20:12:36 +0100 (Wed, 20 Feb 2008) | 2 lines Fixes contributed by Ori Avtalion. ........ r60909 | eric.smith | 2008-02-21 00:34:22 +0100 (Thu, 21 Feb 2008) | 1 line Trim leading zeros from a floating point exponent, per C99. See issue 1600. As far as I know, this only affects Windows. Add float type 'n' to PyOS_ascii_formatd (see PEP 3101 for 'n' description). ........ r60910 | eric.smith | 2008-02-21 00:39:28 +0100 (Thu, 21 Feb 2008) | 1 line Now that PyOS_ascii_formatd supports the 'n' format, simplify the float formatting code to just call it. ........ r60918 | andrew.kuchling | 2008-02-21 15:23:38 +0100 (Thu, 21 Feb 2008) | 2 lines Close manifest file. This change doesn't make any difference to CPython, but is a necessary fix for Jython. ........ r60921 | guido.van.rossum | 2008-02-21 18:46:16 +0100 (Thu, 21 Feb 2008) | 2 lines Remove news about float repr() -- issue 1580 is still in limbo. ........ r60923 | guido.van.rossum | 2008-02-21 19:18:37 +0100 (Thu, 21 Feb 2008) | 5 lines Removed uses of dict.has_key() from distutils, and uses of callable() from copy_reg.py, so the interpreter now starts up without warnings when '-3' is given. More work like this needs to be done in the rest of the stdlib. ........ r60924 | thomas.heller | 2008-02-21 19:28:48 +0100 (Thu, 21 Feb 2008) | 4 lines configure.ac: Remove the configure check for _Bool, it is already done in the top-level Python configure script. configure, fficonfig.h.in: regenerated. ........ r60925 | thomas.heller | 2008-02-21 19:52:20 +0100 (Thu, 21 Feb 2008) | 3 lines Replace 'has_key()' with 'in'. Replace 'raise Error, stuff' with 'raise Error(stuff)'. ........ r60927 | raymond.hettinger | 2008-02-21 20:24:53 +0100 (Thu, 21 Feb 2008) | 1 line Update more instances of has_key(). ........ r60928 | guido.van.rossum | 2008-02-21 20:46:35 +0100 (Thu, 21 Feb 2008) | 3 lines Fix a few typos and layout glitches (more work is needed). Move 2.5 news to Misc/HISTORY. ........ r60936 | georg.brandl | 2008-02-21 21:33:38 +0100 (Thu, 21 Feb 2008) | 2 lines #2079: typo in userdict docs. ........ r60938 | georg.brandl | 2008-02-21 21:38:13 +0100 (Thu, 21 Feb 2008) | 2 lines Part of #2154: minimal syntax fixes in doc example snippets. ........ r60942 | raymond.hettinger | 2008-02-22 04:16:42 +0100 (Fri, 22 Feb 2008) | 1 line First draft for itertools.product(). Docs and other updates forthcoming. ........ r60955 | nick.coghlan | 2008-02-22 11:54:06 +0100 (Fri, 22 Feb 2008) | 1 line Try to make command line error messages from runpy easier to understand (and suppress traceback cruft from the implicitly invoked runpy machinery) ........ r60956 | georg.brandl | 2008-02-22 13:31:45 +0100 (Fri, 22 Feb 2008) | 2 lines A lot more typo fixes by Ori Avtalion. ........ r60957 | georg.brandl | 2008-02-22 13:56:34 +0100 (Fri, 22 Feb 2008) | 2 lines Don't reference pyshell. ........ r60958 | georg.brandl | 2008-02-22 13:57:05 +0100 (Fri, 22 Feb 2008) | 2 lines Another fix. ........ --- ccompiler.py | 6 +++--- command/sdist.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index f4edb7c6..1e09123a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -147,11 +147,11 @@ class CCompiler: # discovered at run-time, since there are many different ways to do # basically the same things with Unix C compilers. - for key, value in kwargs.items(): + for key in kwargs: if key not in self.executables: - raise ValueError("unknown executable '%s' for class %s" % \ + raise ValueError("unknown executable '%s' for class %s" % (key, self.__class__.__name__)) - self.set_executable(key, value) + self.set_executable(key, kwargs[key]) def set_executable(self, key, value): if isinstance(value, str): diff --git a/command/sdist.py b/command/sdist.py index b1c76486..82899257 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -357,6 +357,7 @@ class sdist (Command): if line[-1] == '\n': line = line[0:-1] self.filelist.append(line) + manifest.close() def make_release_tree(self, base_dir, files): """Create the directory tree that will become the source -- cgit v1.2.1 From c3981d56adcbfdd1e06f1b4fabc554e470ce4646 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 23 Feb 2008 17:40:11 +0000 Subject: Patch #2167 from calvin: Remove unused imports --- bcppcompiler.py | 2 +- command/bdist.py | 2 +- command/bdist_dumb.py | 2 +- command/bdist_msi.py | 2 +- command/bdist_rpm.py | 1 - command/build_py.py | 2 +- command/build_scripts.py | 2 +- command/install.py | 1 - command/install_headers.py | 1 - command/install_lib.py | 2 +- command/register.py | 2 +- command/sdist.py | 2 +- filelist.py | 1 - tests/test_dist.py | 2 -- tests/test_sysconfig.py | 1 - unixccompiler.py | 1 - 16 files changed, 9 insertions(+), 17 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index ca524a5b..d4fc5336 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -16,7 +16,7 @@ for the Borland C++ compiler. __revision__ = "$Id$" -import sys, os +import os from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError, UnknownFileError diff --git a/command/bdist.py b/command/bdist.py index 23c25a55..d6897d2d 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -7,7 +7,7 @@ distribution).""" __revision__ = "$Id$" -import os, string +import os from types import * from distutils.core import Command from distutils.errors import * diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index ccba0095..f740c468 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -11,7 +11,7 @@ __revision__ = "$Id$" import os from distutils.core import Command from distutils.util import get_platform -from distutils.dir_util import create_tree, remove_tree, ensure_relative +from distutils.dir_util import remove_tree, ensure_relative from distutils.errors import * from distutils.sysconfig import get_python_version from distutils import log diff --git a/command/bdist_msi.py b/command/bdist_msi.py index a1c0df4b..a4014525 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -7,7 +7,7 @@ Implements the bdist_msi command. """ -import sys, os, string +import sys, os from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import remove_tree diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 6f0e0d88..9aa70300 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -8,7 +8,6 @@ distributions).""" __revision__ = "$Id$" import sys, os, string -import glob from types import * from distutils.core import Command from distutils.debug import DEBUG diff --git a/command/build_py.py b/command/build_py.py index b9f39bae..be6d2c5b 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -6,7 +6,7 @@ Implements the Distutils 'build_py' command.""" __revision__ = "$Id$" -import sys, string, os +import string, os from types import * from glob import glob diff --git a/command/build_scripts.py b/command/build_scripts.py index 6af363b3..104be0b3 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -6,7 +6,7 @@ Implements the Distutils 'build_scripts' command.""" __revision__ = "$Id$" -import sys, os, re +import os, re from stat import ST_MODE from distutils import sysconfig from distutils.core import Command diff --git a/command/install.py b/command/install.py index c6212102..0d39b91c 100644 --- a/command/install.py +++ b/command/install.py @@ -17,7 +17,6 @@ from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root from distutils.errors import DistutilsOptionError -from glob import glob if sys.version < "2.2": WINDOWS_SCHEME = { diff --git a/command/install_headers.py b/command/install_headers.py index 2bd1b043..4895240a 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -7,7 +7,6 @@ files to the Python include directory.""" __revision__ = "$Id$" -import os from distutils.core import Command diff --git a/command/install_lib.py b/command/install_lib.py index 08ff5434..81107a85 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -2,7 +2,7 @@ __revision__ = "$Id$" -import sys, os, string +import os from types import IntType from distutils.core import Command from distutils.errors import DistutilsOptionError diff --git a/command/register.py b/command/register.py index d1a9100d..5c588eff 100644 --- a/command/register.py +++ b/command/register.py @@ -7,7 +7,7 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" -import sys, os, string, urllib2, getpass, urlparse +import os, string, urllib2, getpass, urlparse import StringIO, ConfigParser from distutils.core import Command diff --git a/command/sdist.py b/command/sdist.py index a06b1953..9b37f789 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -6,7 +6,7 @@ Implements the Distutils 'sdist' command (create a source distribution).""" __revision__ = "$Id$" -import sys, os, string +import os, string from types import * from glob import glob from distutils.core import Command diff --git a/filelist.py b/filelist.py index 43f9aaaf..6d27cce6 100644 --- a/filelist.py +++ b/filelist.py @@ -11,7 +11,6 @@ __revision__ = "$Id$" import os, string, re import fnmatch from types import * -from glob import glob from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsInternalError from distutils import log diff --git a/tests/test_dist.py b/tests/test_dist.py index 4d2a7cdf..5ae79332 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -3,10 +3,8 @@ import distutils.cmd import distutils.dist import os -import shutil import StringIO import sys -import tempfile import unittest from test.test_support import TESTFN diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 770b7c37..d56f7e9b 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -2,7 +2,6 @@ from distutils import sysconfig import os -import sys import unittest from test.test_support import TESTFN diff --git a/unixccompiler.py b/unixccompiler.py index 75e8a531..dc759ee8 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,7 +17,6 @@ __revision__ = "$Id$" import os, sys from types import StringType, NoneType -from copy import copy from distutils import sysconfig from distutils.dep_util import newer -- cgit v1.2.1 From 6b8ac4a8066f127f3e2009e760eceda2354e3db1 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 23 Feb 2008 18:30:17 +0000 Subject: Merged revisions 60990-61002 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r60990 | eric.smith | 2008-02-23 17:05:26 +0100 (Sat, 23 Feb 2008) | 1 line Removed duplicate Py_CHARMASK define. It's already defined in Python.h. ........ r60991 | andrew.kuchling | 2008-02-23 17:23:05 +0100 (Sat, 23 Feb 2008) | 4 lines #1330538: Improve comparison of xmlrpclib.DateTime and datetime instances. Remove automatic handling of datetime.date and datetime.time. This breaks backward compatibility, but python-dev discussion was strongly against this automatic conversion; see the bug for a link. ........ r60994 | andrew.kuchling | 2008-02-23 17:39:43 +0100 (Sat, 23 Feb 2008) | 1 line #835521: Add index entries for various pickle-protocol methods and attributes ........ r60995 | andrew.kuchling | 2008-02-23 18:10:46 +0100 (Sat, 23 Feb 2008) | 2 lines #1433694: minidom's .normalize() failed to set .nextSibling for last element. Fix by Malte Helmert ........ r61000 | christian.heimes | 2008-02-23 18:40:11 +0100 (Sat, 23 Feb 2008) | 1 line Patch #2167 from calvin: Remove unused imports ........ r61001 | christian.heimes | 2008-02-23 18:42:31 +0100 (Sat, 23 Feb 2008) | 1 line Patch #1957: syslogmodule: Release GIL when calling syslog(3) ........ r61002 | christian.heimes | 2008-02-23 18:52:07 +0100 (Sat, 23 Feb 2008) | 2 lines Issue #2051 and patch from Alexander Belopolsky: Permission for pyc and pyo files are inherited from the py file. ........ --- bcppcompiler.py | 2 +- command/bdist_dumb.py | 2 +- command/bdist_rpm.py | 1 - command/build_scripts.py | 2 +- command/install.py | 1 - command/install_headers.py | 1 - command/register.py | 2 +- filelist.py | 1 - tests/test_dist.py | 2 -- tests/test_sysconfig.py | 1 - unixccompiler.py | 1 - 11 files changed, 4 insertions(+), 12 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 1ab76c59..c5e5cd25 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -14,7 +14,7 @@ for the Borland C++ compiler. __revision__ = "$Id$" -import sys, os +import os from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError, UnknownFileError diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index f8996176..2d399226 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -9,7 +9,7 @@ __revision__ = "$Id$" import os from distutils.core import Command from distutils.util import get_platform -from distutils.dir_util import create_tree, remove_tree, ensure_relative +from distutils.dir_util import remove_tree, ensure_relative from distutils.errors import * from distutils.sysconfig import get_python_version from distutils import log diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 72f74f9c..83efb8e0 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -6,7 +6,6 @@ distributions).""" __revision__ = "$Id$" import sys, os -import glob from distutils.core import Command from distutils.debug import DEBUG from distutils.util import get_platform diff --git a/command/build_scripts.py b/command/build_scripts.py index 7b44264c..3ac5b0c0 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -4,7 +4,7 @@ Implements the Distutils 'build_scripts' command.""" __revision__ = "$Id$" -import sys, os, re +import os, re from stat import ST_MODE from distutils import sysconfig from distutils.core import Command diff --git a/command/install.py b/command/install.py index b768663c..e4ee6802 100644 --- a/command/install.py +++ b/command/install.py @@ -14,7 +14,6 @@ from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root from distutils.errors import DistutilsOptionError -from glob import glob if sys.version < "2.2": WINDOWS_SCHEME = { diff --git a/command/install_headers.py b/command/install_headers.py index 7114eaf0..346daaad 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -5,7 +5,6 @@ files to the Python include directory.""" __revision__ = "$Id$" -import os from distutils.core import Command diff --git a/command/register.py b/command/register.py index d123d327..40d9f20d 100644 --- a/command/register.py +++ b/command/register.py @@ -7,7 +7,7 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" -import sys, os, urllib2, getpass, urlparse +import os, string, urllib2, getpass, urlparse import io, ConfigParser from distutils.core import Command diff --git a/filelist.py b/filelist.py index 6506c30e..8eab0a95 100644 --- a/filelist.py +++ b/filelist.py @@ -8,7 +8,6 @@ __revision__ = "$Id$" import os, re import fnmatch -from glob import glob from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsInternalError from distutils import log diff --git a/tests/test_dist.py b/tests/test_dist.py index 23506b5d..91acf458 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -3,10 +3,8 @@ import distutils.cmd import distutils.dist import os -import shutil import io import sys -import tempfile import unittest from test.test_support import TESTFN diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 770b7c37..d56f7e9b 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -2,7 +2,6 @@ from distutils import sysconfig import os -import sys import unittest from test.test_support import TESTFN diff --git a/unixccompiler.py b/unixccompiler.py index ee975e15..25a042d0 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -16,7 +16,6 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" import os, sys -from copy import copy from distutils import sysconfig from distutils.dep_util import newer -- cgit v1.2.1 From 7eaf2c9799ea499603b40dbf7324c8adb60d8fad Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 6 Mar 2008 06:47:18 +0000 Subject: #1725737: ignore other VC directories other than CVS and SVN's too. --- command/sdist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 9b37f789..961256c3 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -347,14 +347,14 @@ class sdist (Command): * the build tree (typically "build") * the release tree itself (only an issue if we ran "sdist" previously with --keep-temp, or it aborted) - * any RCS, CVS and .svn directories + * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories """ build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() self.filelist.exclude_pattern(None, prefix=build.build_base) self.filelist.exclude_pattern(None, prefix=base_dir) - self.filelist.exclude_pattern(r'/(RCS|CVS|\.svn)/.*', is_regex=1) + self.filelist.exclude_pattern(r'(^|/)(RCS|CVS|\.svn|\.hg|\.git|\.bzr|_darcs)/.*', is_regex=1) def write_manifest (self): -- cgit v1.2.1 From 5533c2dfe6f54d028b6164c6d6586b98c0f82d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 6 Mar 2008 07:14:26 +0000 Subject: Backport of r61263: #1725737: ignore other VC directories other than CVS and SVN's too. --- command/sdist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 3dfe6f21..b724a3b7 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -347,14 +347,14 @@ class sdist (Command): * the build tree (typically "build") * the release tree itself (only an issue if we ran "sdist" previously with --keep-temp, or it aborted) - * any RCS, CVS and .svn directories + * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories """ build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() self.filelist.exclude_pattern(None, prefix=build.build_base) self.filelist.exclude_pattern(None, prefix=base_dir) - self.filelist.exclude_pattern(r'/(RCS|CVS|\.svn)/.*', is_regex=1) + self.filelist.exclude_pattern(r'(^|/)(RCS|CVS|\.svn|\.hg|\.git|\.bzr|_darcs)/.*', is_regex=1) def write_manifest (self): -- cgit v1.2.1 From b41f4b8c277041837cda7062923b4cde662d5ebb Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 16 Mar 2008 00:07:10 +0000 Subject: Merged revisions 61239-61249,61252-61257,61260-61264,61269-61275,61278-61279,61285-61286,61288-61290,61298,61303-61305,61312-61314,61317,61329,61332,61344,61350-61351,61363-61376,61378-61379,61382-61383,61387-61388,61392,61395-61396,61402-61403 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r61239 | andrew.kuchling | 2008-03-05 01:44:41 +0100 (Wed, 05 Mar 2008) | 1 line Add more items; add fragmentary notes ........ r61240 | amaury.forgeotdarc | 2008-03-05 02:50:33 +0100 (Wed, 05 Mar 2008) | 13 lines Issue#2238: some syntax errors from *args or **kwargs expressions would give bogus error messages, because of untested exceptions:: >>> f(**g(1=2)) XXX undetected error Traceback (most recent call last): File "", line 1, in TypeError: 'int' object is not iterable instead of the expected SyntaxError: keyword can't be an expression Will backport. ........ r61241 | neal.norwitz | 2008-03-05 06:10:48 +0100 (Wed, 05 Mar 2008) | 3 lines Remove the files/dirs after closing the DB so the tests work on Windows. Patch from Trent Nelson. Also simplified removing a file by using test_support. ........ r61242 | neal.norwitz | 2008-03-05 06:14:18 +0100 (Wed, 05 Mar 2008) | 3 lines Get this test to pass even when there is no sound card in the system. Patch from Trent Nelson. (I can't test this.) ........ r61243 | neal.norwitz | 2008-03-05 06:20:44 +0100 (Wed, 05 Mar 2008) | 3 lines Catch OSError when trying to remove a file in case removal fails. This should prevent a failure in tearDown masking any real test failure. ........ r61244 | neal.norwitz | 2008-03-05 06:38:06 +0100 (Wed, 05 Mar 2008) | 5 lines Make the timeout longer to give slow machines a chance to pass the test before timing out. This doesn't change the duration of the test under normal circumstances. This is targetted at fixing the spurious failures on the FreeBSD buildbot primarily. ........ r61245 | neal.norwitz | 2008-03-05 06:49:03 +0100 (Wed, 05 Mar 2008) | 1 line Tabs -> spaces ........ r61246 | neal.norwitz | 2008-03-05 06:50:20 +0100 (Wed, 05 Mar 2008) | 1 line Use -u urlfetch to run more tests ........ r61247 | neal.norwitz | 2008-03-05 06:51:20 +0100 (Wed, 05 Mar 2008) | 1 line test_smtplib sometimes reports leaks too, suppress it ........ r61248 | jeffrey.yasskin | 2008-03-05 07:19:56 +0100 (Wed, 05 Mar 2008) | 5 lines Fix test_socketserver on Windows after r61099 added several signal.alarm() calls (which don't exist on non-Unix platforms). Thanks to Trent Nelson for the report and patch. ........ r61249 | georg.brandl | 2008-03-05 08:10:35 +0100 (Wed, 05 Mar 2008) | 2 lines Fix some rst. ........ r61252 | thomas.heller | 2008-03-05 15:53:39 +0100 (Wed, 05 Mar 2008) | 2 lines News entry for yesterdays commit. ........ r61253 | thomas.heller | 2008-03-05 16:34:29 +0100 (Wed, 05 Mar 2008) | 3 lines Issue 1872: Changed the struct module typecode from 't' to '?', for compatibility with PEP3118. ........ r61254 | skip.montanaro | 2008-03-05 17:41:09 +0100 (Wed, 05 Mar 2008) | 4 lines Elaborate on the role of the altinstall target when installing multiple versions. ........ r61255 | georg.brandl | 2008-03-05 20:31:44 +0100 (Wed, 05 Mar 2008) | 2 lines #2239: PYTHONPATH delimiter is os.pathsep. ........ r61256 | raymond.hettinger | 2008-03-05 21:59:58 +0100 (Wed, 05 Mar 2008) | 1 line C implementation of itertools.permutations(). ........ r61257 | raymond.hettinger | 2008-03-05 22:04:32 +0100 (Wed, 05 Mar 2008) | 1 line Small code cleanup. ........ r61260 | martin.v.loewis | 2008-03-05 23:24:31 +0100 (Wed, 05 Mar 2008) | 2 lines cd PCbuild only after deleting all pyc files. ........ r61261 | raymond.hettinger | 2008-03-06 02:15:52 +0100 (Thu, 06 Mar 2008) | 1 line Add examples. ........ r61262 | andrew.kuchling | 2008-03-06 02:36:27 +0100 (Thu, 06 Mar 2008) | 1 line Add two items ........ r61263 | georg.brandl | 2008-03-06 07:47:18 +0100 (Thu, 06 Mar 2008) | 2 lines #1725737: ignore other VC directories other than CVS and SVN's too. ........ r61264 | martin.v.loewis | 2008-03-06 07:55:22 +0100 (Thu, 06 Mar 2008) | 4 lines Patch #2232: os.tmpfile might fail on Windows if the user has no permission to create files in the root directory. Will backport to 2.5. ........ r61269 | georg.brandl | 2008-03-06 08:19:15 +0100 (Thu, 06 Mar 2008) | 2 lines Expand on re.split behavior with captured expressions. ........ r61270 | georg.brandl | 2008-03-06 08:22:09 +0100 (Thu, 06 Mar 2008) | 2 lines Little clarification of assignments. ........ r61271 | georg.brandl | 2008-03-06 08:31:34 +0100 (Thu, 06 Mar 2008) | 2 lines Add isinstance/issubclass to tutorial. ........ r61272 | georg.brandl | 2008-03-06 08:34:52 +0100 (Thu, 06 Mar 2008) | 2 lines Add missing NEWS entry for r61263. ........ r61273 | georg.brandl | 2008-03-06 08:41:16 +0100 (Thu, 06 Mar 2008) | 2 lines #2225: return nonzero status code from py_compile if not all files could be compiled. ........ r61274 | georg.brandl | 2008-03-06 08:43:02 +0100 (Thu, 06 Mar 2008) | 2 lines #2220: handle matching failure more gracefully. ........ r61275 | georg.brandl | 2008-03-06 08:45:52 +0100 (Thu, 06 Mar 2008) | 2 lines Bug #2220: handle rlcompleter attribute match failure more gracefully. ........ r61278 | martin.v.loewis | 2008-03-06 14:49:47 +0100 (Thu, 06 Mar 2008) | 1 line Rely on x64 platform configuration when building _bsddb on AMD64. ........ r61279 | martin.v.loewis | 2008-03-06 14:50:28 +0100 (Thu, 06 Mar 2008) | 1 line Update db-4.4.20 build procedure. ........ r61285 | raymond.hettinger | 2008-03-06 21:52:01 +0100 (Thu, 06 Mar 2008) | 1 line More tests. ........ r61286 | raymond.hettinger | 2008-03-06 23:51:36 +0100 (Thu, 06 Mar 2008) | 1 line Issue 2246: itertools grouper object did not participate in GC (should be backported). ........ r61288 | raymond.hettinger | 2008-03-07 02:33:20 +0100 (Fri, 07 Mar 2008) | 1 line Tweak recipes and tests ........ r61289 | jeffrey.yasskin | 2008-03-07 07:22:15 +0100 (Fri, 07 Mar 2008) | 5 lines Progress on issue #1193577 by adding a polling .shutdown() method to SocketServers. The core of the patch was written by Pedro Werneck, but any bugs are mine. I've also rearranged the code for timeouts in order to avoid interfering with the shutdown poll. ........ r61290 | nick.coghlan | 2008-03-07 15:13:28 +0100 (Fri, 07 Mar 2008) | 1 line Speed up with statements by storing the __exit__ method on the stack instead of in a temp variable (bumps the magic number for pyc files) ........ r61298 | andrew.kuchling | 2008-03-07 22:09:23 +0100 (Fri, 07 Mar 2008) | 1 line Grammar fix ........ r61303 | georg.brandl | 2008-03-08 10:54:06 +0100 (Sat, 08 Mar 2008) | 2 lines #2253: fix continue vs. finally docs. ........ r61304 | marc-andre.lemburg | 2008-03-08 11:01:43 +0100 (Sat, 08 Mar 2008) | 3 lines Add new name for Mandrake: Mandriva. ........ r61305 | georg.brandl | 2008-03-08 11:05:24 +0100 (Sat, 08 Mar 2008) | 2 lines #1533486: fix types in refcount intro. ........ r61312 | facundo.batista | 2008-03-08 17:50:27 +0100 (Sat, 08 Mar 2008) | 5 lines Issue 1106316. post_mortem()'s parameter, traceback, is now optional: it defaults to the traceback of the exception that is currently being handled. ........ r61313 | jeffrey.yasskin | 2008-03-08 19:26:54 +0100 (Sat, 08 Mar 2008) | 2 lines Add tests for with and finally performance to pybench. ........ r61314 | jeffrey.yasskin | 2008-03-08 21:08:21 +0100 (Sat, 08 Mar 2008) | 2 lines Fix pybench for pythons < 2.6, tested back to 2.3. ........ r61317 | jeffrey.yasskin | 2008-03-08 22:35:15 +0100 (Sat, 08 Mar 2008) | 3 lines Well that was dumb. platform.python_implementation returns a function, not a string. ........ r61329 | georg.brandl | 2008-03-09 16:11:39 +0100 (Sun, 09 Mar 2008) | 2 lines #2249: document assertTrue and assertFalse. ........ r61332 | neal.norwitz | 2008-03-09 20:03:42 +0100 (Sun, 09 Mar 2008) | 4 lines Introduce a lock to fix a race condition which caused an exception in the test. Some buildbots were consistently failing (e.g., amd64). Also remove a couple of semi-colons. ........ r61344 | raymond.hettinger | 2008-03-11 01:19:07 +0100 (Tue, 11 Mar 2008) | 1 line Add recipe to docs. ........ r61350 | guido.van.rossum | 2008-03-11 22:18:06 +0100 (Tue, 11 Mar 2008) | 3 lines Fix the overflows in expandtabs(). "This time for sure!" (Exploit at request.) ........ r61351 | raymond.hettinger | 2008-03-11 22:37:46 +0100 (Tue, 11 Mar 2008) | 1 line Improve docs for itemgetter(). Show that it works with slices. ........ r61363 | georg.brandl | 2008-03-13 08:15:56 +0100 (Thu, 13 Mar 2008) | 2 lines #2265: fix example. ........ r61364 | georg.brandl | 2008-03-13 08:17:14 +0100 (Thu, 13 Mar 2008) | 2 lines #2270: fix typo. ........ r61365 | georg.brandl | 2008-03-13 08:21:41 +0100 (Thu, 13 Mar 2008) | 2 lines #1720705: add docs about import/threading interaction, wording by Nick. ........ r61366 | andrew.kuchling | 2008-03-13 12:07:35 +0100 (Thu, 13 Mar 2008) | 1 line Add class decorators ........ r61367 | raymond.hettinger | 2008-03-13 17:43:17 +0100 (Thu, 13 Mar 2008) | 1 line Add 2-to-3 support for the itertools moved to builtins or renamed. ........ r61368 | raymond.hettinger | 2008-03-13 17:43:59 +0100 (Thu, 13 Mar 2008) | 1 line Consistent tense. ........ r61369 | raymond.hettinger | 2008-03-13 20:03:51 +0100 (Thu, 13 Mar 2008) | 1 line Issue 2274: Add heapq.heappushpop(). ........ r61370 | raymond.hettinger | 2008-03-13 20:33:34 +0100 (Thu, 13 Mar 2008) | 1 line Simplify the nlargest() code using heappushpop(). ........ r61371 | brett.cannon | 2008-03-13 21:27:00 +0100 (Thu, 13 Mar 2008) | 4 lines Move test_thread over to unittest. Commits GHOP 237. Thanks Benjamin Peterson for the patch. ........ r61372 | brett.cannon | 2008-03-13 21:33:10 +0100 (Thu, 13 Mar 2008) | 4 lines Move test_tokenize to doctest. Done as GHOP 238 by Josip Dzolonga. ........ r61373 | brett.cannon | 2008-03-13 21:47:41 +0100 (Thu, 13 Mar 2008) | 4 lines Convert test_contains, test_crypt, and test_select to unittest. Patch from GHOP 294 by David Marek. ........ r61374 | brett.cannon | 2008-03-13 22:02:16 +0100 (Thu, 13 Mar 2008) | 4 lines Move test_gdbm to use unittest. Closes issue #1960. Thanks Giampaolo Rodola. ........ r61375 | brett.cannon | 2008-03-13 22:09:28 +0100 (Thu, 13 Mar 2008) | 4 lines Convert test_fcntl to unittest. Closes issue #2055. Thanks Giampaolo Rodola. ........ r61376 | raymond.hettinger | 2008-03-14 06:03:44 +0100 (Fri, 14 Mar 2008) | 1 line Leave heapreplace() unchanged. ........ r61378 | martin.v.loewis | 2008-03-14 14:56:09 +0100 (Fri, 14 Mar 2008) | 2 lines Patch #2284: add -x64 option to rt.bat. ........ r61379 | martin.v.loewis | 2008-03-14 14:57:59 +0100 (Fri, 14 Mar 2008) | 2 lines Use -x64 flag. ........ r61382 | brett.cannon | 2008-03-14 15:03:10 +0100 (Fri, 14 Mar 2008) | 2 lines Remove a bad test. ........ r61383 | mark.dickinson | 2008-03-14 15:23:37 +0100 (Fri, 14 Mar 2008) | 9 lines Issue 705836: Fix struct.pack(">f", 1e40) to behave consistently across platforms: it should now raise OverflowError on all platforms. (Previously it raised OverflowError only on non IEEE 754 platforms.) Also fix the (already existing) test for this behaviour so that it actually raises TestFailed instead of just referencing it. ........ r61387 | thomas.heller | 2008-03-14 22:06:21 +0100 (Fri, 14 Mar 2008) | 1 line Remove unneeded initializer. ........ r61388 | martin.v.loewis | 2008-03-14 22:19:28 +0100 (Fri, 14 Mar 2008) | 2 lines Run debug version, cd to PCbuild. ........ r61392 | georg.brandl | 2008-03-15 00:10:34 +0100 (Sat, 15 Mar 2008) | 2 lines Remove obsolete paragraph. #2288. ........ r61395 | georg.brandl | 2008-03-15 01:20:19 +0100 (Sat, 15 Mar 2008) | 2 lines Fix lots of broken links in the docs, found by Sphinx' external link checker. ........ r61396 | skip.montanaro | 2008-03-15 03:32:49 +0100 (Sat, 15 Mar 2008) | 1 line note that fork and forkpty raise OSError on failure ........ r61402 | skip.montanaro | 2008-03-15 17:04:45 +0100 (Sat, 15 Mar 2008) | 1 line add %f format to datetime - issue 1158 ........ r61403 | skip.montanaro | 2008-03-15 17:07:11 +0100 (Sat, 15 Mar 2008) | 2 lines . ........ --- command/sdist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 82899257..b29fc64e 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -325,14 +325,14 @@ class sdist (Command): * the build tree (typically "build") * the release tree itself (only an issue if we ran "sdist" previously with --keep-temp, or it aborted) - * any RCS, CVS and .svn directories + * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories """ build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() self.filelist.exclude_pattern(None, prefix=build.build_base) self.filelist.exclude_pattern(None, prefix=base_dir) - self.filelist.exclude_pattern(r'/(RCS|CVS|\.svn)/.*', is_regex=1) + self.filelist.exclude_pattern(r'(^|/)(RCS|CVS|\.svn|\.hg|\.git|\.bzr|_darcs)/.*', is_regex=1) def write_manifest(self): """Write the file list in 'self.filelist' (presumably as filled in -- cgit v1.2.1 From 7382bd51a39f785fee596182768da1b8deecf6c7 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Wed, 19 Mar 2008 06:28:24 +0000 Subject: Issue2290: Support x64 Windows builds that live in pcbuild/amd64. Without it, sysutils._python_build() returns the wrong directory, which causes the test_get_config_h_filename method in Lib/distutils/tests/test_sysconfig.py to fail. --- sysconfig.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 506cf385..27a770b1 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -23,7 +23,8 @@ PREFIX = os.path.normpath(sys.prefix) EXEC_PREFIX = os.path.normpath(sys.exec_prefix) # Path to the base directory of the project. On Windows the binary may -# live in project/PCBuild9 +# live in project/PCBuild9. If we're dealing with an x64 Windows build, +# it'll live in project/PCbuild/amd64. project_base = os.path.dirname(os.path.abspath(sys.executable)) if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) @@ -31,6 +32,10 @@ if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, os.path.pardir)) +# PC/AMD64 +if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, + os.path.pardir)) # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use -- cgit v1.2.1 From 227cb3d385365b0716558d21804dbcf8d36b86ba Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 19 Mar 2008 21:50:51 +0000 Subject: Merged revisions 61538-61540,61556,61559-61560,61563,61565,61571,61575-61576,61580-61582,61586,61591,61593,61595,61605-61606,61613-61616,61618,61621-61623,61625,61627,61631-61634 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r61538 | steven.bethard | 2008-03-18 20:03:50 +0100 (Di, 18 Mär 2008) | 1 line cell_compare needs to return -2 instead of NULL. ........ r61539 | steven.bethard | 2008-03-18 20:04:32 +0100 (Di, 18 Mär 2008) | 1 line _have_soundcard() is a bad check for winsound.Beep, since you can have a soundcard but have the beep driver disabled. This revision basically disables the beep tests by wrapping them in a try/except. The Right Way To Do It is to come up with a _have_enabled_beep_driver() and use that. ........ r61540 | gregory.p.smith | 2008-03-18 20:05:32 +0100 (Di, 18 Mär 2008) | 8 lines Fix chown on 64-bit linux. It needed to take a long (64-bit on 64bit linux) as uid and gid input to accept values >=2**31 as valid while still accepting negative numbers to pass -1 to chown for "no change". Fixes issue1747858. This should be backported to release25-maint. ........ r61556 | steven.bethard | 2008-03-18 20:59:14 +0100 (Di, 18 Mär 2008) | 1 line Fix test_atexit so that it still passes when -3 is supplied. (It was catching the warning messages on stdio from using the reload() function.) ........ r61559 | neal.norwitz | 2008-03-18 21:30:38 +0100 (Di, 18 Mär 2008) | 1 line Import the test properly. This is especially important for py3k. ........ r61560 | gregory.p.smith | 2008-03-18 21:40:01 +0100 (Di, 18 Mär 2008) | 2 lines news entry for the chown fix ........ r61563 | brett.cannon | 2008-03-18 22:12:42 +0100 (Di, 18 Mär 2008) | 2 lines Ignore BIG5HKSCS-2004.TXT which is downloaded as part of a test. ........ r61565 | steven.bethard | 2008-03-18 22:30:13 +0100 (Di, 18 Mär 2008) | 1 line Have regrtest skip test_py3kwarn when the -3 flag is missing. ........ r61571 | gregory.p.smith | 2008-03-18 23:27:41 +0100 (Di, 18 Mär 2008) | 4 lines Add a test to make sure zlib.crc32 and binascii.crc32 return the same thing. Fix a buglet in binascii.crc32, the second optional argument could previously have a signedness mismatch with the C variable its going into. ........ r61575 | raymond.hettinger | 2008-03-19 00:22:29 +0100 (Mi, 19 Mär 2008) | 1 line Speed-up isinstance() for one easy case. ........ r61576 | raymond.hettinger | 2008-03-19 00:33:08 +0100 (Mi, 19 Mär 2008) | 1 line Issue: 2354: Add 3K warning for the cmp argument to list.sort() and sorted(). ........ r61580 | andrew.kuchling | 2008-03-19 02:05:35 +0100 (Mi, 19 Mär 2008) | 1 line Add Jeff Rush ........ r61581 | gregory.p.smith | 2008-03-19 02:38:35 +0100 (Mi, 19 Mär 2008) | 3 lines Mention that crc32 and adler32 are available in a different module (zlib). Some people look for them in hashlib. ........ r61582 | gregory.p.smith | 2008-03-19 02:46:10 +0100 (Mi, 19 Mär 2008) | 3 lines Use zlib's crc32 routine instead of binascii when available. zlib's is faster when compiled properly optimized and about the same speed otherwise. ........ r61586 | david.wolever | 2008-03-19 03:26:57 +0100 (Mi, 19 Mär 2008) | 1 line Added my name to ACKS ........ r61591 | gregory.p.smith | 2008-03-19 04:14:41 +0100 (Mi, 19 Mär 2008) | 5 lines Fix the struct module DeprecationWarnings that zipfile was triggering by removing all use of signed struct values. test_zipfile and test_zipfile64 pass. no more warnings. ........ r61593 | raymond.hettinger | 2008-03-19 04:56:59 +0100 (Mi, 19 Mär 2008) | 1 line Fix compiler warning. ........ r61595 | martin.v.loewis | 2008-03-19 05:39:13 +0100 (Mi, 19 Mär 2008) | 2 lines Issue #2400: Allow relative imports to "import *". ........ r61605 | martin.v.loewis | 2008-03-19 07:00:28 +0100 (Mi, 19 Mär 2008) | 2 lines Import relimport using a relative import. ........ r61606 | trent.nelson | 2008-03-19 07:28:24 +0100 (Mi, 19 Mär 2008) | 1 line Issue2290: Support x64 Windows builds that live in pcbuild/amd64. Without it, sysutils._python_build() returns the wrong directory, which causes the test_get_config_h_filename method in Lib/distutils/tests/test_sysconfig.py to fail. ........ r61613 | trent.nelson | 2008-03-19 08:45:19 +0100 (Mi, 19 Mär 2008) | 3 lines Refine the Visual Studio 2008 build solution in order to improve how we deal with external components, as well as fixing outstanding issues with Windows x64 build support. Introduce two new .vcproj files, _bsddb44.vcproj and sqlite3.vcproj, which replace the previous pre-link event scripts for _bsddb and _sqlite3 respectively. The new project files inherit from our property files as if they were any other Python module. This has numerous benefits. First, the components get built with exactly the same compiler flags and settings as the rest of Python. Second, it makes it much easier to debug problems in the external components when they're part of the build system. Third, they'll benefit from profile guided optimisation in the release builds, just like the rest of Python core. I've also introduced a slightly new pattern for managing externals in subversion. New components get checked in as -.x, where matches the exact vendor version string. After the initial import of the external component, the .x is tagged as .0 (i.e. tcl-8.4.18.x -> tcl-8.4.18.0). Some components may not need any tweaking, whereas there are others that might (tcl/tk fall into this bucket). In that case, the relevant modifications are made to the .x branch, which will be subsequently tagged as .1 (and then n+1 going forward) when they build successfully and all tests pass. Buildbots will be converted to rely on these explicit tags only, which makes it easy for us to switch them over to a new version as and when required. (Simple change to external(-amd64).bat: if we've bumped tcl to 8.4.18.1, change the .bat to rmdir 8.4.18.0 if it exists and check out a new .1 copy.) ........ r61614 | trent.nelson | 2008-03-19 08:56:39 +0100 (Mi, 19 Mär 2008) | 1 line Remove extraneous apostrophe and semi-colon from AdditionalIncludeDirectories. ........ r61615 | georg.brandl | 2008-03-19 08:56:40 +0100 (Mi, 19 Mär 2008) | 2 lines Remove footnote from versionchanged as it upsets LaTeX. ........ r61616 | georg.brandl | 2008-03-19 08:57:57 +0100 (Mi, 19 Mär 2008) | 2 lines Another one. ........ r61618 | trent.nelson | 2008-03-19 09:06:03 +0100 (Mi, 19 Mär 2008) | 1 line Fix the tcl-8.4.18.1 path and make sure we cd into the right directory when building tcl/tk. ........ r61621 | trent.nelson | 2008-03-19 10:23:08 +0100 (Mi, 19 Mär 2008) | 1 line Lets have another try at getting the Windows buildbots in a consistent state before rebuilding using the new process. ........ r61622 | eric.smith | 2008-03-19 13:09:55 +0100 (Mi, 19 Mär 2008) | 2 lines Use test.test_support.captured_stdout instead of a custom contextmanager. Thanks Nick Coghlan. ........ r61623 | eric.smith | 2008-03-19 13:15:10 +0100 (Mi, 19 Mär 2008) | 1 line Trivial typo. ........ r61625 | thomas.heller | 2008-03-19 17:10:57 +0100 (Mi, 19 Mär 2008) | 2 lines Checkout sqlite-source when it is not there. ........ r61627 | brett.cannon | 2008-03-19 17:50:13 +0100 (Mi, 19 Mär 2008) | 5 lines test_nis would fail if test.test_support.verbose was true but NIS was not set up on the machine. Closes issue2411. Thanks Michael Bishop. ........ r61631 | brett.cannon | 2008-03-19 18:37:43 +0100 (Mi, 19 Mär 2008) | 2 lines Use sys.py3kwarning instead of trying to trigger a Py3k-related warning. ........ r61632 | raymond.hettinger | 2008-03-19 18:45:19 +0100 (Mi, 19 Mär 2008) | 1 line Issue 2354: Fix-up compare warning. Patch contributed by Jeff Balogh. ........ r61633 | raymond.hettinger | 2008-03-19 18:58:59 +0100 (Mi, 19 Mär 2008) | 1 line The filter() function does support a None argument in Py3.0. ........ r61634 | raymond.hettinger | 2008-03-19 19:01:58 +0100 (Mi, 19 Mär 2008) | 1 line Remove itertools warnings I had added before the 2-to-3 handled the migration. ........ --- sysconfig.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 39216a53..d2b2c9a3 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -23,7 +23,8 @@ PREFIX = os.path.normpath(sys.prefix) EXEC_PREFIX = os.path.normpath(sys.exec_prefix) # Path to the base directory of the project. On Windows the binary may -# live in project/PCBuild9 +# live in project/PCBuild9. If we're dealing with an x64 Windows build, +# it'll live in project/PCbuild/amd64. project_base = os.path.dirname(os.path.abspath(sys.executable)) if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) @@ -31,6 +32,10 @@ if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, os.path.pardir)) +# PC/AMD64 +if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, + os.path.pardir)) # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use -- cgit v1.2.1 From dcf379739cccd5c8dd0d9a606ab52239e659d154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 22 Mar 2008 00:35:10 +0000 Subject: Add build_py_2to3. --- command/build_py.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/command/build_py.py b/command/build_py.py index 63ced4b4..f39ffb87 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -383,3 +383,27 @@ class build_py (Command): if self.optimize > 0: byte_compile(files, optimize=self.optimize, force=self.force, prefix=prefix, dry_run=self.dry_run) + +class build_py_2to3(build_py): + def run(self): + from lib2to3.refactor import RefactoringTool + self.updated_files = [] + build_py.run(self) + class Options: + pass + o = Options() + o.doctests_only = False + o.fix = [] + o.list_fixes = [] + o.print_function = False + o.verbose = False + o.write = True + r = RefactoringTool(o) + r.refactor_args(self.updated_files) + + def build_module(self, module, module_file, package): + res = build_py.build_module(self, module, module_file, package) + if res[1]: + # file was copied + self.updated_files.append(res[0]) + return res -- cgit v1.2.1 From 143e8f4fe669de9fb9e1ed23be226c7a4b6b410a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 22 Mar 2008 22:07:43 +0000 Subject: Invoke byte-compilation after running 2to3. --- command/build_py.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index f39ffb87..09f6d233 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -386,9 +386,17 @@ class build_py (Command): class build_py_2to3(build_py): def run(self): - from lib2to3.refactor import RefactoringTool self.updated_files = [] - build_py.run(self) + + # Base class code + if self.py_modules: + self.build_modules() + if self.packages: + self.build_packages() + self.build_package_data() + + # 2to3 + from lib2to3.refactor import RefactoringTool class Options: pass o = Options() @@ -401,6 +409,9 @@ class build_py_2to3(build_py): r = RefactoringTool(o) r.refactor_args(self.updated_files) + # Remaining base class code + self.byte_compile(self.get_outputs(include_bytecode=0)) + def build_module(self, module, module_file, package): res = build_py.build_module(self, module, module_file, package) if res[1]: -- cgit v1.2.1 From 3405aa3e09e63da9af1a4ca90c4ae1c5bf9dcc7d Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 2 Apr 2008 23:33:27 +0000 Subject: release.py induced and manual editing steps for 3.0a4. --- __init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 0c3d939d..b583e397 100644 --- a/__init__.py +++ b/__init__.py @@ -18,4 +18,7 @@ __revision__ = "$Id$" # In general, major and minor version should loosely follow the Python # version number the distutils code was shipped with. # -__version__ = "2.6.0" + +#--start constants-- +__version__ = "3.0a4" +#--end constants-- -- cgit v1.2.1 From b2c84945a0906325790271d2d8108fbe81483b70 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 3 Apr 2008 04:10:02 +0000 Subject: Updating for 2.6a2 --- __init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8c954c22..d0dd03a3 100644 --- a/__init__.py +++ b/__init__.py @@ -20,4 +20,7 @@ __revision__ = "$Id$" # In general, major and minor version should loosely follow the Python # version number the distutils code was shipped with. # -__version__ = "2.6.0" + +#--start constants-- +__version__ = "2.6a2" +#--end constants-- -- cgit v1.2.1 From fb68e5c8dd08660e12a945ace7487a7b2b4daff1 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 4 Apr 2008 05:41:30 +0000 Subject: - Issue #2385: distutils.core.run_script() makes __file__ available, so the controlled environment will more closely mirror the typical script environment. This supports setup.py scripts that refer to data files. --- core.py | 3 ++- tests/test_core.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/test_core.py diff --git a/core.py b/core.py index c40dd0a7..640d6b59 100644 --- a/core.py +++ b/core.py @@ -210,8 +210,9 @@ def run_setup (script_name, script_args=None, stop_after="run"): _setup_stop_after = stop_after save_argv = sys.argv - g = {} + g = {'__file__': script_name} l = {} + os.chdir(os.path.dirname(script_name) or os.curdir) try: try: sys.argv[0] = script_name diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100644 index 00000000..6de58d92 --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,40 @@ +"""Tests for distutils.core.""" + +import StringIO +import distutils.core +import os +import test.test_support +import unittest + + +# setup script that uses __file__ +setup_using___file__ = """\ + +__file__ + +from distutils.core import setup +setup() +""" + + +class CoreTestCase(unittest.TestCase): + + def tearDown(self): + os.remove(test.test_support.TESTFN) + + def write_setup(self, text): + return fn + + def test_run_setup_provides_file(self): + # Make sure the script can use __file__; if that's missing, the test + # setup.py script will raise NameError. + fn = test.test_support.TESTFN + open(fn, "w").write(setup_using___file__) + distutils.core.run_setup(fn) + + +def test_suite(): + return unittest.makeSuite(CoreTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 2e06d2e918a1f2d6e7c8f87853dee6f04e86d6d9 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 4 Apr 2008 11:31:14 +0000 Subject: my previous change did what I said it should not: it changed the current directory to the directory in which the setup.py script lived (which made __file__ wrong) fixed, with test that the script is run in the current directory of the caller --- core.py | 1 - tests/test_core.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/core.py b/core.py index 640d6b59..32b90269 100644 --- a/core.py +++ b/core.py @@ -212,7 +212,6 @@ def run_setup (script_name, script_args=None, stop_after="run"): save_argv = sys.argv g = {'__file__': script_name} l = {} - os.chdir(os.path.dirname(script_name) or os.curdir) try: try: sys.argv[0] = script_name diff --git a/tests/test_core.py b/tests/test_core.py index 6de58d92..4356c212 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3,6 +3,8 @@ import StringIO import distutils.core import os +import shutil +import sys import test.test_support import unittest @@ -16,21 +18,61 @@ from distutils.core import setup setup() """ +setup_prints_cwd = """\ + +import os +print os.getcwd() + +from distutils.core import setup +setup() +""" + class CoreTestCase(unittest.TestCase): + def setUp(self): + self.old_stdout = sys.stdout + self.cleanup_testfn() + def tearDown(self): - os.remove(test.test_support.TESTFN) + sys.stdout = self.old_stdout + self.cleanup_testfn() + + def cleanup_testfn(self): + path = test.test_support.TESTFN + if os.path.isfile(path): + os.remove(path) + elif os.path.isdir(path): + shutil.rmtree(path) - def write_setup(self, text): - return fn + def write_setup(self, text, path=test.test_support.TESTFN): + open(path, "w").write(text) + return path def test_run_setup_provides_file(self): # Make sure the script can use __file__; if that's missing, the test # setup.py script will raise NameError. - fn = test.test_support.TESTFN - open(fn, "w").write(setup_using___file__) - distutils.core.run_setup(fn) + distutils.core.run_setup( + self.write_setup(setup_using___file__)) + + def test_run_setup_uses_current_dir(self): + # This tests that the setup script is run with the current directory + # as it's own current directory; this was temporarily broken by a + # previous patch when TESTFN did not use the current directory. + sys.stdout = StringIO.StringIO() + cwd = os.getcwd() + + # Create a directory and write the setup.py file there: + os.mkdir(test.test_support.TESTFN) + setup_py = os.path.join(test.test_support.TESTFN, "setup.py") + distutils.core.run_setup( + self.write_setup(setup_prints_cwd, path=setup_py)) + + output = sys.stdout.getvalue() + open("/dev/tty", "w").write("\n\n%r\n\n\n" % (output,)) + if output.endswith("\n"): + output = output[:-1] + self.assertEqual(cwd, output) def test_suite(): -- cgit v1.2.1 From 5ad3f56a92652c3996fb97ab7d923ca7e3647fe5 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 4 Apr 2008 11:38:51 +0000 Subject: stupid, stupid, stupid! --- tests/test_core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 4356c212..55d2d5df 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -69,7 +69,6 @@ class CoreTestCase(unittest.TestCase): self.write_setup(setup_prints_cwd, path=setup_py)) output = sys.stdout.getvalue() - open("/dev/tty", "w").write("\n\n%r\n\n\n" % (output,)) if output.endswith("\n"): output = output[:-1] self.assertEqual(cwd, output) -- cgit v1.2.1 From dc94cc0477c3ef464ffc9cd41fac155898e72402 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sat, 5 Apr 2008 04:47:45 +0000 Subject: Merged revisions 61440-61441,61443,61445-61448,61451-61452,61455-61457,61459-61464,61466-61467,61469-61470,61476-61477,61479,61481-61482,61485,61487,61490,61493-61494,61497,61499-61502,61505-61506,61508,61511-61514,61519,61521-61522,61530-61531,61533-61537,61541-61555,61557-61558,61561-61562,61566-61569,61572-61574,61578-61579,61583-61584,61588-61589,61592,61594,61598-61601,61603-61604,61607-61612,61617,61619-61620,61624,61626,61628-61630,61635-61638,61640-61643,61645,61648,61653-61655,61659-61662,61664,61666,61668-61671,61673,61675,61679-61680,61682,61685-61686,61689-61695,61697-61699,61701-61703,61706,61710,61713,61717,61723,61726-61730,61736,61738,61740,61742,61745-61752,61754-61760,61762-61764,61768,61770-61772,61774-61775,61784-61787,61789-61792,61794-61795,61797-61806,61808-61809,61811-61812,61814-61819,61824,61826-61833,61835-61840,61843-61845,61848,61850,61854-61862,61865-61866,61868,61872-61873,61876-61877,61883-61888,61890-61891,61893-61899,61901-61903,61905-61912,61914,61917,61920-61921,61927,61930,61932-61934,61939,61941-61942,61944-61951,61955,61960-61963,61980,61982-61983,61991,61994-61996,62001-62003,62008-62010,62016-62017,62022,62024,62027,62031-62034,62041,62045-62046,62055-62058,62060-62066,62068-62074,62076-62079,62081-62083,62086-62089,62092-62094,62098,62101,62104,62106-62109,62115-62122,62124-62125,62127-62128,62130,62132,62134-62137,62139-62142,62144,62146-62148,62150-62152,62155-62161 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r62127 | trent.nelson | 2008-04-03 08:39:17 -0700 (Thu, 03 Apr 2008) | 1 line Remove the building of Berkeley DB step; _bsddb44.vcproj takes care of this for us now. ........ r62136 | amaury.forgeotdarc | 2008-04-03 16:07:55 -0700 (Thu, 03 Apr 2008) | 9 lines #1733757: the interpreter would hang on shutdown, if the function set by sys.settrace calls threading.currentThread. The correction somewhat improves the code, but it was close. Many thanks to the "with" construct, which turns python code into C calls. I wonder if it is not better to sys.settrace(None) just after running the __main__ module and before finalization. ........ r62141 | jeffrey.yasskin | 2008-04-03 21:51:19 -0700 (Thu, 03 Apr 2008) | 5 lines Doh! os.read() raises an OSError, not an IOError when it's interrupted. And fix some flakiness in test_itimer_prof, which could detect that the timer had reached 0 before the signal arrived announcing that fact. ........ r62142 | fred.drake | 2008-04-03 22:41:30 -0700 (Thu, 03 Apr 2008) | 4 lines - Issue #2385: distutils.core.run_script() makes __file__ available, so the controlled environment will more closely mirror the typical script environment. This supports setup.py scripts that refer to data files. ........ r62147 | fred.drake | 2008-04-04 04:31:14 -0700 (Fri, 04 Apr 2008) | 6 lines my previous change did what I said it should not: it changed the current directory to the directory in which the setup.py script lived (which made __file__ wrong) fixed, with test that the script is run in the current directory of the caller ........ r62148 | fred.drake | 2008-04-04 04:38:51 -0700 (Fri, 04 Apr 2008) | 2 lines stupid, stupid, stupid! ........ r62150 | jeffrey.yasskin | 2008-04-04 09:48:19 -0700 (Fri, 04 Apr 2008) | 2 lines Oops again. EINTR is in errno, not signal. ........ r62158 | andrew.kuchling | 2008-04-04 19:42:20 -0700 (Fri, 04 Apr 2008) | 1 line Minor edits ........ r62159 | andrew.kuchling | 2008-04-04 19:47:07 -0700 (Fri, 04 Apr 2008) | 1 line Markup fix; explain what interval timers do; typo fix ........ r62160 | andrew.kuchling | 2008-04-04 20:38:39 -0700 (Fri, 04 Apr 2008) | 1 line Various edits ........ r62161 | neal.norwitz | 2008-04-04 21:26:31 -0700 (Fri, 04 Apr 2008) | 9 lines Prevent test_sqlite from hanging on older versions of sqlite. The problem is that when trying to do the second insert, sqlite seems to sleep for a very long time. Here is the output from strace: read(6, "SQLite format 3\0\4\0\1\1\0@ \0\0\0\1\0\0\0\0"..., 1024) = 1024 nanosleep({4294, 966296000}, I don't know which version this was fixed in, but 3.2.1 definitely fails. ........ --- core.py | 2 +- tests/test_core.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/test_core.py diff --git a/core.py b/core.py index 0490e637..a4c5e180 100644 --- a/core.py +++ b/core.py @@ -207,7 +207,7 @@ def run_setup (script_name, script_args=None, stop_after="run"): _setup_stop_after = stop_after save_argv = sys.argv - g = {} + g = {'__file__': script_name} l = {} try: try: diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100644 index 00000000..8e274dd4 --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,81 @@ +"""Tests for distutils.core.""" + +import io +import distutils.core +import os +import shutil +import sys +import test.test_support +import unittest + + +# setup script that uses __file__ +setup_using___file__ = """\ + +__file__ + +from distutils.core import setup +setup() +""" + +setup_prints_cwd = """\ + +import os +print(os.getcwd()) + +from distutils.core import setup +setup() +""" + + +class CoreTestCase(unittest.TestCase): + + def setUp(self): + self.old_stdout = sys.stdout + self.cleanup_testfn() + + def tearDown(self): + sys.stdout = self.old_stdout + self.cleanup_testfn() + + def cleanup_testfn(self): + path = test.test_support.TESTFN + if os.path.isfile(path): + os.remove(path) + elif os.path.isdir(path): + shutil.rmtree(path) + + def write_setup(self, text, path=test.test_support.TESTFN): + open(path, "w").write(text) + return path + + def test_run_setup_provides_file(self): + # Make sure the script can use __file__; if that's missing, the test + # setup.py script will raise NameError. + distutils.core.run_setup( + self.write_setup(setup_using___file__)) + + def test_run_setup_uses_current_dir(self): + # This tests that the setup script is run with the current directory + # as it's own current directory; this was temporarily broken by a + # previous patch when TESTFN did not use the current directory. + sys.stdout = io.StringIO() + cwd = os.getcwd() + + # Create a directory and write the setup.py file there: + os.mkdir(test.test_support.TESTFN) + setup_py = os.path.join(test.test_support.TESTFN, "setup.py") + distutils.core.run_setup( + self.write_setup(setup_prints_cwd, path=setup_py)) + + output = sys.stdout.getvalue() + if output.endswith("\n"): + output = output[:-1] + self.assertEqual(cwd, output) + + +def test_suite(): + return unittest.makeSuite(CoreTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From bbe02c7a83e5792122e71937f50787a18245f27b Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 5 Apr 2008 23:39:15 +0000 Subject: Fix test_distutils to not fail when running 'make test' from a Python build directory that is not the source directory (ie, one created using '/path/to/source/configure'.) Leaves this test very slightly degraded in that particular case, compared to the build-in-sourcedir case, but that case isn't a particularly strong test either: neither test the actual path that will be used after installing. There isn't a particularly good way to test this, and a poor test beats a failing test. --- tests/test_sysconfig.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index d56f7e9b..aa1187e7 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -19,7 +19,27 @@ class SysconfigTestCase(unittest.TestCase): # test for pythonxx.lib? def test_get_python_inc(self): - inc_dir = sysconfig.get_python_inc() + # The check for srcdir is copied from Python's setup.py, + # and is necessary to make this test pass when building + # Python in a directory other than the source directory. + (srcdir,) = sysconfig.get_config_vars('srcdir') + if not srcdir: + inc_dir = sysconfig.get_python_inc() + else: + # This test is not really a proper test: when building + # Python from source, even in the same directory, + # we won't be testing the same thing as when running + # distutils' tests on an installed Python. Nevertheless, + # let's try to do our best: if we are running Python's + # unittests from a build directory that is not the source + # directory, the normal inc_dir will exist, it will just not + # contain anything of interest. + inc_dir = sysconfig.get_python_inc() + self.assert_(os.path.isdir(inc_dir)) + # Now test the source location, to make sure Python.h does + # exist. + inc_dir = os.path.join(os.getcwd(), srcdir, 'Include') + inc_dir = os.path.normpath(inc_dir) self.assert_(os.path.isdir(inc_dir), inc_dir) python_h = os.path.join(inc_dir, "Python.h") self.assert_(os.path.isfile(python_h), python_h) -- cgit v1.2.1 From 6b17a395e92863f9bb6e93e067d487ed42c0de9b Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Mon, 7 Apr 2008 00:25:59 +0000 Subject: Merged revisions 62179 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r62179 | thomas.wouters | 2008-04-06 01:39:15 +0200 (Sun, 06 Apr 2008) | 10 lines Fix test_distutils to not fail when running 'make test' from a Python build directory that is not the source directory (ie, one created using '/path/to/source/configure'.) Leaves this test very slightly degraded in that particular case, compared to the build-in-sourcedir case, but that case isn't a particularly strong test either: neither test the actual path that will be used after installing. There isn't a particularly good way to test this, and a poor test beats a failing test. ........ --- tests/test_sysconfig.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index d56f7e9b..aa1187e7 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -19,7 +19,27 @@ class SysconfigTestCase(unittest.TestCase): # test for pythonxx.lib? def test_get_python_inc(self): - inc_dir = sysconfig.get_python_inc() + # The check for srcdir is copied from Python's setup.py, + # and is necessary to make this test pass when building + # Python in a directory other than the source directory. + (srcdir,) = sysconfig.get_config_vars('srcdir') + if not srcdir: + inc_dir = sysconfig.get_python_inc() + else: + # This test is not really a proper test: when building + # Python from source, even in the same directory, + # we won't be testing the same thing as when running + # distutils' tests on an installed Python. Nevertheless, + # let's try to do our best: if we are running Python's + # unittests from a build directory that is not the source + # directory, the normal inc_dir will exist, it will just not + # contain anything of interest. + inc_dir = sysconfig.get_python_inc() + self.assert_(os.path.isdir(inc_dir)) + # Now test the source location, to make sure Python.h does + # exist. + inc_dir = os.path.join(os.getcwd(), srcdir, 'Include') + inc_dir = os.path.normpath(inc_dir) self.assert_(os.path.isdir(inc_dir), inc_dir) python_h = os.path.join(inc_dir, "Python.h") self.assert_(os.path.isfile(python_h), python_h) -- cgit v1.2.1 From 9351875b842427a4cf795d32ecec02dbd992cff7 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 7 Apr 2008 01:53:39 +0000 Subject: Issue #2513: enable 64bit cross compilation on windows. --- command/bdist.py | 6 ++-- command/bdist_msi.py | 20 ++++++++---- command/bdist_wininst.py | 28 +++++++++++----- command/build.py | 18 ++++++++++- command/build_ext.py | 30 ++++++++++++++++-- command/install.py | 9 ++++++ command/wininst-9.0-amd64.exe | Bin 0 -> 76288 bytes msvc9compiler.py | 72 ++++++++++++++++++++++++------------------ msvccompiler.py | 2 +- util.py | 4 +-- 10 files changed, 135 insertions(+), 54 deletions(-) create mode 100644 command/wininst-9.0-amd64.exe diff --git a/command/bdist.py b/command/bdist.py index d6897d2d..ca3da74c 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -97,7 +97,10 @@ class bdist (Command): def finalize_options (self): # have to finalize 'plat_name' before 'bdist_base' if self.plat_name is None: - self.plat_name = get_platform() + if self.skip_build: + self.plat_name = get_platform() + else: + self.plat_name = self.get_finalized_command('build').plat_name # 'bdist_base' -- parent of per-built-distribution-format # temporary directories (eg. we'll probably have @@ -121,7 +124,6 @@ class bdist (Command): # finalize_options() - def run (self): # Figure out which sub-commands we need to run. diff --git a/command/bdist_msi.py b/command/bdist_msi.py index a4014525..f94d9579 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -9,11 +9,11 @@ Implements the bdist_msi command. import sys, os from distutils.core import Command -from distutils.util import get_platform from distutils.dir_util import remove_tree from distutils.sysconfig import get_python_version from distutils.version import StrictVersion from distutils.errors import DistutilsOptionError +from distutils.util import get_platform from distutils import log import msilib from msilib import schema, sequence, text @@ -87,6 +87,9 @@ class bdist_msi (Command): user_options = [('bdist-dir=', None, "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), ('keep-temp', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), @@ -116,6 +119,7 @@ class bdist_msi (Command): def initialize_options (self): self.bdist_dir = None + self.plat_name = None self.keep_temp = 0 self.no_target_compile = 0 self.no_target_optimize = 0 @@ -139,7 +143,10 @@ class bdist_msi (Command): else: self.target_version = short_version - self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ) if self.pre_install_script: raise DistutilsOptionError, "the pre-install-script feature is not yet implemented" @@ -181,7 +188,7 @@ class bdist_msi (Command): if not target_version: assert self.skip_build, "Should have already checked this" target_version = sys.version[0:3] - plat_specifier = ".%s-%s" % (get_platform(), target_version) + plat_specifier = ".%s-%s" % (self.plat_name, target_version) build = self.get_finalized_command('build') build.build_lib = os.path.join(build.build_base, 'lib' + plat_specifier) @@ -633,8 +640,7 @@ class bdist_msi (Command): def get_installer_filename(self, fullname): # Factored out to allow overriding in subclasses - plat = get_platform() - installer_name = os.path.join(self.dist_dir, - "%s.%s-py%s.msi" % - (fullname, plat, self.target_version)) + base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, + self.target_version) + installer_name = os.path.join(self.dist_dir, base_name) return installer_name diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index b0691fb8..02542afb 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -21,6 +21,9 @@ class bdist_wininst (Command): user_options = [('bdist-dir=', None, "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), ('keep-temp', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), @@ -54,6 +57,7 @@ class bdist_wininst (Command): def initialize_options (self): self.bdist_dir = None + self.plat_name = None self.keep_temp = 0 self.no_target_compile = 0 self.no_target_optimize = 0 @@ -82,7 +86,10 @@ class bdist_wininst (Command): " option must be specified" % (short_version,) self.target_version = short_version - self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ) if self.install_script: for script in self.distribution.scripts: @@ -110,6 +117,7 @@ class bdist_wininst (Command): install.root = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 + install.plat_name = self.plat_name install_lib = self.reinitialize_command('install_lib') # we do not want to include pyc or pyo files @@ -127,7 +135,7 @@ class bdist_wininst (Command): if not target_version: assert self.skip_build, "Should have already checked this" target_version = sys.version[0:3] - plat_specifier = ".%s-%s" % (get_platform(), target_version) + plat_specifier = ".%s-%s" % (self.plat_name, target_version) build = self.get_finalized_command('build') build.build_lib = os.path.join(build.build_base, 'lib' + plat_specifier) @@ -285,11 +293,11 @@ class bdist_wininst (Command): # if we create an installer for a specific python version, # it's better to include this in the name installer_name = os.path.join(self.dist_dir, - "%s.win32-py%s.exe" % - (fullname, self.target_version)) + "%s.%s-py%s.exe" % + (fullname, self.plat_name, self.target_version)) else: installer_name = os.path.join(self.dist_dir, - "%s.win32.exe" % fullname) + "%s.%s.exe" % (fullname, self.plat_name)) return installer_name # get_installer_filename() @@ -312,9 +320,9 @@ class bdist_wininst (Command): bv = get_build_version() else: if self.target_version < "2.4": - bv = "6" + bv = 6.0 else: - bv = "7.1" + bv = 7.1 else: # for current version - use authoritative check. bv = get_build_version() @@ -323,6 +331,10 @@ class bdist_wininst (Command): directory = os.path.dirname(__file__) # we must use a wininst-x.y.exe built with the same C compiler # used for python. XXX What about mingw, borland, and so on? - filename = os.path.join(directory, "wininst-%.1f.exe" % bv) + if self.plat_name == 'win32': + sfix = '' + else: + sfix = self.plat_name[3:] # strip 'win' - leaves eg '-amd64' + filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) return open(filename, "rb").read() # class bdist_wininst diff --git a/command/build.py b/command/build.py index bca031f7..7462e934 100644 --- a/command/build.py +++ b/command/build.py @@ -8,6 +8,7 @@ __revision__ = "$Id$" import sys, os from distutils.core import Command +from distutils.errors import DistutilsOptionError from distutils.util import get_platform @@ -34,6 +35,9 @@ class build (Command): "build directory for scripts"), ('build-temp=', 't', "temporary build directory"), + ('plat-name=', 'p', + "platform name to build for, if supported " + "(default: %s)" % get_platform()), ('compiler=', 'c', "specify the compiler type"), ('debug', 'g', @@ -61,13 +65,25 @@ class build (Command): self.build_temp = None self.build_scripts = None self.compiler = None + self.plat_name = None self.debug = None self.force = 0 self.executable = None def finalize_options (self): - plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3]) + if self.plat_name is None: + self.plat_name = get_platform() + else: + # plat-name only supported for windows (other platforms are + # supported via ./configure flags, if at all). Avoid misleading + # other platforms. + if os.name != 'nt': + raise DistutilsOptionError( + "--plat-name only supported on Windows (try " + "using './configure --help' on your platform)") + + plat_specifier = ".%s-%s" % (self.plat_name, sys.version[0:3]) # Make it so Python 2.x and Python 2.x with --with-pydebug don't # share the same build directories. Doing so confuses the build diff --git a/command/build_ext.py b/command/build_ext.py index 3042fe02..bf5ad7e0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -15,6 +15,7 @@ from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version from distutils.dep_util import newer_group from distutils.extension import Extension +from distutils.util import get_platform from distutils import log if os.name == 'nt': @@ -60,6 +61,9 @@ class build_ext (Command): "directory for compiled extension modules"), ('build-temp=', 't', "directory for temporary files (build by-products)"), + ('plat-name=', 'p', + "platform name to cross-compile for, if supported " + "(default: %s)" % get_platform()), ('inplace', 'i', "ignore build-lib and put compiled extensions into the source " + "directory alongside your pure Python modules"), @@ -101,6 +105,7 @@ class build_ext (Command): def initialize_options (self): self.extensions = None self.build_lib = None + self.plat_name = None self.build_temp = None self.inplace = 0 self.package = None @@ -127,7 +132,9 @@ class build_ext (Command): ('build_temp', 'build_temp'), ('compiler', 'compiler'), ('debug', 'debug'), - ('force', 'force')) + ('force', 'force'), + ('plat_name', 'plat_name'), + ) if self.package is None: self.package = self.distribution.ext_package @@ -171,6 +178,9 @@ class build_ext (Command): # for Release and Debug builds. # also Python's library directory must be appended to library_dirs if os.name == 'nt': + # the 'libs' directory is for binary installs - we assume that + # must be the *native* platform. But we don't really support + # cross-compiling via a binary install anyway, so we let it go. self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) if self.debug: self.build_temp = os.path.join(self.build_temp, "Debug") @@ -181,8 +191,17 @@ class build_ext (Command): # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) if MSVC_VERSION == 9: - self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PCbuild')) + # Use the .lib files for the correct architecture + if self.plat_name == 'win32': + suffix = '' + else: + # win-amd64 or win-ia64 + suffix = self.plat_name[4:] + new_lib = os.path.join(sys.exec_prefix, 'PCbuild') + if suffix: + new_lib = os.path.join(new_lib, suffix) + self.library_dirs.append(new_lib) + elif MSVC_VERSION == 8: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS8.0', 'win32release')) @@ -275,6 +294,11 @@ class build_ext (Command): dry_run=self.dry_run, force=self.force) customize_compiler(self.compiler) + # If we are cross-compiling, init the compiler now (if we are not + # cross-compiling, init would not hurt, but people may rely on + # late initialization of compiler even if they shouldn't...) + if os.name == 'nt' and self.plat_name != get_platform(): + self.compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in diff --git a/command/install.py b/command/install.py index 0d39b91c..33a1212b 100644 --- a/command/install.py +++ b/command/install.py @@ -16,6 +16,7 @@ from distutils.sysconfig import get_config_vars from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root +from distutils.util import get_platform from distutils.errors import DistutilsOptionError if sys.version < "2.2": @@ -503,6 +504,14 @@ class install (Command): # Obviously have to build before we can install if not self.skip_build: self.run_command('build') + # If we built for any other platform, we can't install. + build_plat = self.distribution.get_command_obj('build').plat_name + # check warn_dir - it is a clue that the 'install' is happening + # internally, and not to sys.path, so we don't check the platform + # matches what we are running. + if self.warn_dir and build_plat != get_platform(): + raise DistutilsPlatformError("Can't install when " + "cross-compiling") # Run all sub-commands (at least those that need to be run) for cmd_name in self.get_sub_commands(): diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe new file mode 100644 index 00000000..c99ede4b Binary files /dev/null and b/command/wininst-9.0-amd64.exe differ diff --git a/msvc9compiler.py b/msvc9compiler.py index 828d7fbf..8b1cf9a9 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -22,6 +22,7 @@ from distutils.errors import (DistutilsExecError, DistutilsPlatformError, from distutils.ccompiler import (CCompiler, gen_preprocess_options, gen_lib_options) from distutils import log +from distutils.util import get_platform import _winreg @@ -38,13 +39,15 @@ HKEYS = (_winreg.HKEY_USERS, VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" NET_BASE = r"Software\Microsoft\.NETFramework" -ARCHS = {'DEFAULT' : 'x86', - 'intel' : 'x86', 'x86' : 'x86', - 'amd64' : 'x64', 'x64' : 'x64', - 'itanium' : 'ia64', 'ia64' : 'ia64', - } -# The globals VERSION, ARCH, MACROS and VC_ENV are defined later +# A map keyed by get_platform() return values to values accepted by +# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is +# the param to cross-compile on x86 targetting amd64.) +PLAT_TO_VCVARS = { + 'win32' : 'x86', + 'win-amd64' : 'amd64', + 'win-ia64' : 'ia64', +} class Reg: """Helper class to read values from the registry @@ -176,23 +179,6 @@ def get_build_version(): # else we don't know what version of the compiler this is return None -def get_build_architecture(): - """Return the processor architecture. - - Possible results are "x86" or "amd64". - """ - prefix = " bit (" - i = sys.version.find(prefix) - if i == -1: - return "x86" - j = sys.version.find(")", i) - sysarch = sys.version[i+len(prefix):j].lower() - arch = ARCHS.get(sysarch, None) - if arch is None: - return ARCHS['DEFAULT'] - else: - return arch - def normalize_and_reduce_paths(paths): """Return a list of normalized paths with duplicates removed. @@ -251,6 +237,7 @@ def query_vcvarsall(version, arch="x86"): if vcvarsall is None: raise IOError("Unable to find vcvarsall.bat") + log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -281,9 +268,7 @@ def query_vcvarsall(version, arch="x86"): VERSION = get_build_version() if VERSION < 8.0: raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) -ARCH = get_build_architecture() # MACROS = MacroExpander(VERSION) -VC_ENV = query_vcvarsall(VERSION, ARCH) class MSVCCompiler(CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, @@ -318,13 +303,25 @@ class MSVCCompiler(CCompiler) : def __init__(self, verbose=0, dry_run=0, force=0): CCompiler.__init__ (self, verbose, dry_run, force) self.__version = VERSION - self.__arch = ARCH self.__root = r"Software\Microsoft\VisualStudio" # self.__macros = MACROS self.__path = [] + # target platform (.plat_name is consistent with 'bdist') + self.plat_name = None + self.__arch = None # deprecated name self.initialized = False - def initialize(self): + def initialize(self, plat_name=None): + # multi-init means we would need to check platform same each time... + assert not self.initialized, "don't init multiple times" + if plat_name is None: + plat_name = get_platform() + # sanity check for platforms to prevent obscure errors later. + ok_plats = 'win32', 'win-amd64', 'win-ia64' + if plat_name not in ok_plats: + raise DistutilsPlatformError("--plat-name must be one of %s" % + (ok_plats,)) + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): # Assume that the SDK set up everything alright; don't try to be # smarter @@ -334,9 +331,24 @@ class MSVCCompiler(CCompiler) : self.rc = "rc.exe" self.mc = "mc.exe" else: - self.__paths = VC_ENV['path'].split(os.pathsep) - os.environ['lib'] = VC_ENV['lib'] - os.environ['include'] = VC_ENV['include'] + # On x86, 'vcvars32.bat amd64' creates an env that doesn't work; + # to cross compile, you use 'x86_amd64'. + # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross + # compile use 'x86' (ie, it runs the x86 compiler directly) + # No idea how itanium handles this, if at all. + if plat_name == get_platform() or plat_name == 'win32': + # native build or cross-compile to win32 + plat_spec = PLAT_TO_VCVARS[plat_name] + else: + # cross compile from win32 -> some 64bit + plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ + PLAT_TO_VCVARS[plat_name] + + vc_env = query_vcvarsall(VERSION, plat_spec) + + self.__paths = vc_env['path'].split(os.pathsep) + os.environ['lib'] = vc_env['lib'] + os.environ['include'] = vc_env['include'] if len(self.__paths) == 0: raise DistutilsPlatformError("Python was built with %s, " diff --git a/msvccompiler.py b/msvccompiler.py index f3acb534..30a9fc6f 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -656,5 +656,5 @@ if get_build_version() >= 8.0: log.debug("Importing new compiler from distutils.msvc9compiler") OldMSVCCompiler = MSVCCompiler from distutils.msvc9compiler import MSVCCompiler - from distutils.msvc9compiler import get_build_architecture + # get_build_architecture not really relevant now we support cross-compile from distutils.msvc9compiler import MacroExpander diff --git a/util.py b/util.py index deb9a0a0..69d90cff 100644 --- a/util.py +++ b/util.py @@ -30,7 +30,7 @@ def get_platform (): irix64-6.2 Windows will return one of: - win-x86_64 (64bit Windows on x86_64 (AMD64)) + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) win-ia64 (64bit Windows on Itanium) win32 (all others - specifically, sys.platform is returned) @@ -45,7 +45,7 @@ def get_platform (): j = string.find(sys.version, ")", i) look = sys.version[i+len(prefix):j].lower() if look=='amd64': - return 'win-x86_64' + return 'win-amd64' if look=='itanium': return 'win-ia64' return sys.platform -- cgit v1.2.1 From f39783d33eeae201072044971535a43415e3e78f Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 9 Apr 2008 08:37:03 +0000 Subject: Merged revisions 62194,62197-62198,62204-62205,62214,62219-62221,62227,62229-62231,62233-62235,62237-62239 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r62194 | jeffrey.yasskin | 2008-04-07 01:04:28 +0200 (Mon, 07 Apr 2008) | 7 lines Add enough debugging information to diagnose failures where the HandlerBException is ignored, and fix one such problem, where it was thrown during the __del__ method of the previous Popen object. We may want to find a better way of printing verbose information so it's not spammy when the test passes. ........ r62197 | mark.hammond | 2008-04-07 03:53:39 +0200 (Mon, 07 Apr 2008) | 2 lines Issue #2513: enable 64bit cross compilation on windows. ........ r62198 | mark.hammond | 2008-04-07 03:59:40 +0200 (Mon, 07 Apr 2008) | 2 lines correct heading underline for new "Cross-compiling on Windows" section ........ r62204 | gregory.p.smith | 2008-04-07 08:33:21 +0200 (Mon, 07 Apr 2008) | 4 lines Use the new PyFile_IncUseCount & PyFile_DecUseCount calls appropriatly within the standard library. These modules use PyFile_AsFile and later release the GIL while operating on the previously returned FILE*. ........ r62205 | mark.summerfield | 2008-04-07 09:39:23 +0200 (Mon, 07 Apr 2008) | 4 lines changed "2500 components" to "several thousand" since the number keeps growning:-) ........ r62214 | georg.brandl | 2008-04-07 20:51:59 +0200 (Mon, 07 Apr 2008) | 2 lines #2525: update timezone info examples in the docs. ........ r62219 | andrew.kuchling | 2008-04-08 01:57:07 +0200 (Tue, 08 Apr 2008) | 1 line Write PEP 3127 section; add items ........ r62220 | andrew.kuchling | 2008-04-08 01:57:21 +0200 (Tue, 08 Apr 2008) | 1 line Typo fix ........ r62221 | andrew.kuchling | 2008-04-08 03:33:10 +0200 (Tue, 08 Apr 2008) | 1 line Typographical fix: 32bit -> 32-bit, 64bit -> 64-bit ........ r62227 | andrew.kuchling | 2008-04-08 23:22:53 +0200 (Tue, 08 Apr 2008) | 1 line Add items ........ r62229 | amaury.forgeotdarc | 2008-04-08 23:27:42 +0200 (Tue, 08 Apr 2008) | 7 lines Issue2564: Prevent a hang in "import test.autotest", which runs the entire test suite as a side-effect of importing the module. - in test_capi, a thread tried to import other modules - re.compile() imported sre_parse again on every call. ........ r62230 | amaury.forgeotdarc | 2008-04-08 23:51:57 +0200 (Tue, 08 Apr 2008) | 2 lines Prevent an error when inspect.isabstract() is called with something else than a new-style class. ........ r62231 | amaury.forgeotdarc | 2008-04-09 00:07:05 +0200 (Wed, 09 Apr 2008) | 8 lines Issue 2408: remove the _types module It was only used as a helper in types.py to access types (GetSetDescriptorType and MemberDescriptorType), when they can easily be obtained with python code. These expressions even work with Jython. I don't know what the future of the types module is; (cf. discussion in http://bugs.python.org/issue1605 ) at least this change makes it simpler. ........ r62233 | amaury.forgeotdarc | 2008-04-09 01:10:07 +0200 (Wed, 09 Apr 2008) | 2 lines Add a NEWS entry for previous checkin ........ r62234 | trent.nelson | 2008-04-09 01:47:30 +0200 (Wed, 09 Apr 2008) | 37 lines - Issue #2550: The approach used by client/server code for obtaining ports to listen on in network-oriented tests has been refined in an effort to facilitate running multiple instances of the entire regression test suite in parallel without issue. test_support.bind_port() has been fixed such that it will always return a unique port -- which wasn't always the case with the previous implementation, especially if socket options had been set that affected address reuse (i.e. SO_REUSEADDR, SO_REUSEPORT). The new implementation of bind_port() will actually raise an exception if it is passed an AF_INET/SOCK_STREAM socket with either the SO_REUSEADDR or SO_REUSEPORT socket option set. Furthermore, if available, bind_port() will set the SO_EXCLUSIVEADDRUSE option on the socket it's been passed. This currently only applies to Windows. This option prevents any other sockets from binding to the host/port we've bound to, thus removing the possibility of the 'non-deterministic' behaviour, as Microsoft puts it, that occurs when a second SOCK_STREAM socket binds and accepts to a host/port that's already been bound by another socket. The optional preferred port parameter to bind_port() has been removed. Under no circumstances should tests be hard coding ports! test_support.find_unused_port() has also been introduced, which will pass a temporary socket object to bind_port() in order to obtain an unused port. The temporary socket object is then closed and deleted, and the port is returned. This method should only be used for obtaining an unused port in order to pass to an external program (i.e. the -accept [port] argument to openssl's s_server mode) or as a parameter to a server-oriented class that doesn't give you direct access to the underlying socket used. Finally, test_support.HOST has been introduced, which should be used for the host argument of any relevant socket calls (i.e. bind and connect). The following tests were updated to following the new conventions: test_socket, test_smtplib, test_asyncore, test_ssl, test_httplib, test_poplib, test_ftplib, test_telnetlib, test_socketserver, test_asynchat and test_socket_ssl. It is now possible for multiple instances of the regression test suite to run in parallel without issue. ........ r62235 | gregory.p.smith | 2008-04-09 02:25:17 +0200 (Wed, 09 Apr 2008) | 3 lines Fix zlib crash from zlib.decompressobj().flush(val) when val was not positive. It tried to allocate negative or zero memory. That fails. ........ r62237 | trent.nelson | 2008-04-09 02:34:53 +0200 (Wed, 09 Apr 2008) | 1 line Fix typo with regards to self.PORT shadowing class variables with the same name. ........ r62238 | andrew.kuchling | 2008-04-09 03:08:32 +0200 (Wed, 09 Apr 2008) | 1 line Add items ........ r62239 | jerry.seutter | 2008-04-09 07:07:58 +0200 (Wed, 09 Apr 2008) | 1 line Changed test so it no longer runs as a side effect of importing. ........ --- command/bdist.py | 5 ++- command/bdist_msi.py | 20 ++++++++---- command/bdist_wininst.py | 28 +++++++++++----- command/build.py | 18 ++++++++++- command/build_ext.py | 30 ++++++++++++++++-- command/install.py | 9 ++++++ command/wininst-9.0-amd64.exe | Bin 0 -> 76288 bytes msvc9compiler.py | 72 ++++++++++++++++++++++++------------------ msvccompiler.py | 2 +- util.py | 4 +-- 10 files changed, 135 insertions(+), 53 deletions(-) create mode 100644 command/wininst-9.0-amd64.exe diff --git a/command/bdist.py b/command/bdist.py index 69c1b28f..e3b047c6 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -91,7 +91,10 @@ class bdist(Command): def finalize_options(self): # have to finalize 'plat_name' before 'bdist_base' if self.plat_name is None: - self.plat_name = get_platform() + if self.skip_build: + self.plat_name = get_platform() + else: + self.plat_name = self.get_finalized_command('build').plat_name # 'bdist_base' -- parent of per-built-distribution-format # temporary directories (eg. we'll probably have diff --git a/command/bdist_msi.py b/command/bdist_msi.py index d313a508..aab89cd6 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -9,11 +9,11 @@ Implements the bdist_msi command. import sys, os from distutils.core import Command -from distutils.util import get_platform from distutils.dir_util import remove_tree from distutils.sysconfig import get_python_version from distutils.version import StrictVersion from distutils.errors import DistutilsOptionError +from distutils.util import get_platform from distutils import log import msilib from msilib import schema, sequence, text @@ -87,6 +87,9 @@ class bdist_msi(Command): user_options = [('bdist-dir=', None, "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), ('keep-temp', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), @@ -116,6 +119,7 @@ class bdist_msi(Command): def initialize_options(self): self.bdist_dir = None + self.plat_name = None self.keep_temp = 0 self.no_target_compile = 0 self.no_target_optimize = 0 @@ -139,7 +143,10 @@ class bdist_msi(Command): else: self.target_version = short_version - self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ) if self.pre_install_script: raise DistutilsOptionError( @@ -180,7 +187,7 @@ class bdist_msi(Command): if not target_version: assert self.skip_build, "Should have already checked this" target_version = sys.version[0:3] - plat_specifier = ".%s-%s" % (get_platform(), target_version) + plat_specifier = ".%s-%s" % (self.plat_name, target_version) build = self.get_finalized_command('build') build.build_lib = os.path.join(build.build_base, 'lib' + plat_specifier) @@ -633,8 +640,7 @@ class bdist_msi(Command): def get_installer_filename(self, fullname): # Factored out to allow overriding in subclasses - plat = get_platform() - installer_name = os.path.join(self.dist_dir, - "%s.%s-py%s.msi" % - (fullname, plat, self.target_version)) + base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, + self.target_version) + installer_name = os.path.join(self.dist_dir, base_name) return installer_name diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 2d75a38f..bf8d022a 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -19,6 +19,9 @@ class bdist_wininst(Command): user_options = [('bdist-dir=', None, "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), ('keep-temp', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), @@ -52,6 +55,7 @@ class bdist_wininst(Command): def initialize_options(self): self.bdist_dir = None + self.plat_name = None self.keep_temp = 0 self.no_target_compile = 0 self.no_target_optimize = 0 @@ -78,7 +82,10 @@ class bdist_wininst(Command): " option must be specified" % (short_version,)) self.target_version = short_version - self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ) if self.install_script: for script in self.distribution.scripts: @@ -104,6 +111,7 @@ class bdist_wininst(Command): install.root = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 + install.plat_name = self.plat_name install_lib = self.reinitialize_command('install_lib') # we do not want to include pyc or pyo files @@ -121,7 +129,7 @@ class bdist_wininst(Command): if not target_version: assert self.skip_build, "Should have already checked this" target_version = sys.version[0:3] - plat_specifier = ".%s-%s" % (get_platform(), target_version) + plat_specifier = ".%s-%s" % (self.plat_name, target_version) build = self.get_finalized_command('build') build.build_lib = os.path.join(build.build_base, 'lib' + plat_specifier) @@ -267,11 +275,11 @@ class bdist_wininst(Command): # if we create an installer for a specific python version, # it's better to include this in the name installer_name = os.path.join(self.dist_dir, - "%s.win32-py%s.exe" % - (fullname, self.target_version)) + "%s.%s-py%s.exe" % + (fullname, self.plat_name, self.target_version)) else: installer_name = os.path.join(self.dist_dir, - "%s.win32.exe" % fullname) + "%s.%s.exe" % (fullname, self.plat_name)) return installer_name def get_exe_bytes(self): @@ -293,9 +301,9 @@ class bdist_wininst(Command): bv = get_build_version() else: if self.target_version < "2.4": - bv = "6" + bv = 6.0 else: - bv = "7.1" + bv = 7.1 else: # for current version - use authoritative check. bv = get_build_version() @@ -304,5 +312,9 @@ class bdist_wininst(Command): directory = os.path.dirname(__file__) # we must use a wininst-x.y.exe built with the same C compiler # used for python. XXX What about mingw, borland, and so on? - filename = os.path.join(directory, "wininst-%.1f.exe" % bv) + if self.plat_name == 'win32': + sfix = '' + else: + sfix = self.plat_name[3:] # strip 'win' - leaves eg '-amd64' + filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) return open(filename, "rb").read() diff --git a/command/build.py b/command/build.py index 4fe95b0c..9c2667cf 100644 --- a/command/build.py +++ b/command/build.py @@ -6,6 +6,7 @@ __revision__ = "$Id$" import sys, os from distutils.core import Command +from distutils.errors import DistutilsOptionError from distutils.util import get_platform @@ -32,6 +33,9 @@ class build(Command): "build directory for scripts"), ('build-temp=', 't', "temporary build directory"), + ('plat-name=', 'p', + "platform name to build for, if supported " + "(default: %s)" % get_platform()), ('compiler=', 'c', "specify the compiler type"), ('debug', 'g', @@ -59,12 +63,24 @@ class build(Command): self.build_temp = None self.build_scripts = None self.compiler = None + self.plat_name = None self.debug = None self.force = 0 self.executable = None def finalize_options(self): - plat_specifier = ".%s-%s" % (get_platform(), sys.version[0:3]) + if self.plat_name is None: + self.plat_name = get_platform() + else: + # plat-name only supported for windows (other platforms are + # supported via ./configure flags, if at all). Avoid misleading + # other platforms. + if os.name != 'nt': + raise DistutilsOptionError( + "--plat-name only supported on Windows (try " + "using './configure --help' on your platform)") + + plat_specifier = ".%s-%s" % (self.plat_name, sys.version[0:3]) # Make it so Python 2.x and Python 2.x with --with-pydebug don't # share the same build directories. Doing so confuses the build diff --git a/command/build_ext.py b/command/build_ext.py index ff84bca7..e0112198 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -12,6 +12,7 @@ from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version from distutils.dep_util import newer_group from distutils.extension import Extension +from distutils.util import get_platform from distutils import log if os.name == 'nt': @@ -57,6 +58,9 @@ class build_ext(Command): "directory for compiled extension modules"), ('build-temp=', 't', "directory for temporary files (build by-products)"), + ('plat-name=', 'p', + "platform name to cross-compile for, if supported " + "(default: %s)" % get_platform()), ('inplace', 'i', "ignore build-lib and put compiled extensions into the source " + "directory alongside your pure Python modules"), @@ -98,6 +102,7 @@ class build_ext(Command): def initialize_options(self): self.extensions = None self.build_lib = None + self.plat_name = None self.build_temp = None self.inplace = 0 self.package = None @@ -124,7 +129,9 @@ class build_ext(Command): ('build_temp', 'build_temp'), ('compiler', 'compiler'), ('debug', 'debug'), - ('force', 'force')) + ('force', 'force'), + ('plat_name', 'plat_name'), + ) if self.package is None: self.package = self.distribution.ext_package @@ -167,6 +174,9 @@ class build_ext(Command): # for Release and Debug builds. # also Python's library directory must be appended to library_dirs if os.name == 'nt': + # the 'libs' directory is for binary installs - we assume that + # must be the *native* platform. But we don't really support + # cross-compiling via a binary install anyway, so we let it go. self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) if self.debug: self.build_temp = os.path.join(self.build_temp, "Debug") @@ -177,8 +187,17 @@ class build_ext(Command): # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) if MSVC_VERSION == 9: - self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PCbuild')) + # Use the .lib files for the correct architecture + if self.plat_name == 'win32': + suffix = '' + else: + # win-amd64 or win-ia64 + suffix = self.plat_name[4:] + new_lib = os.path.join(sys.exec_prefix, 'PCbuild') + if suffix: + new_lib = os.path.join(new_lib, suffix) + self.library_dirs.append(new_lib) + elif MSVC_VERSION == 8: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS8.0', 'win32release')) @@ -267,6 +286,11 @@ class build_ext(Command): dry_run=self.dry_run, force=self.force) customize_compiler(self.compiler) + # If we are cross-compiling, init the compiler now (if we are not + # cross-compiling, init would not hurt, but people may rely on + # late initialization of compiler even if they shouldn't...) + if os.name == 'nt' and self.plat_name != get_platform(): + self.compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in diff --git a/command/install.py b/command/install.py index e4ee6802..50884c33 100644 --- a/command/install.py +++ b/command/install.py @@ -13,6 +13,7 @@ from distutils.sysconfig import get_config_vars from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root +from distutils.util import get_platform from distutils.errors import DistutilsOptionError if sys.version < "2.2": @@ -485,6 +486,14 @@ class install (Command): # Obviously have to build before we can install if not self.skip_build: self.run_command('build') + # If we built for any other platform, we can't install. + build_plat = self.distribution.get_command_obj('build').plat_name + # check warn_dir - it is a clue that the 'install' is happening + # internally, and not to sys.path, so we don't check the platform + # matches what we are running. + if self.warn_dir and build_plat != get_platform(): + raise DistutilsPlatformError("Can't install when " + "cross-compiling") # Run all sub-commands (at least those that need to be run) for cmd_name in self.get_sub_commands(): diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe new file mode 100644 index 00000000..c99ede4b Binary files /dev/null and b/command/wininst-9.0-amd64.exe differ diff --git a/msvc9compiler.py b/msvc9compiler.py index 828d7fbf..8b1cf9a9 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -22,6 +22,7 @@ from distutils.errors import (DistutilsExecError, DistutilsPlatformError, from distutils.ccompiler import (CCompiler, gen_preprocess_options, gen_lib_options) from distutils import log +from distutils.util import get_platform import _winreg @@ -38,13 +39,15 @@ HKEYS = (_winreg.HKEY_USERS, VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" NET_BASE = r"Software\Microsoft\.NETFramework" -ARCHS = {'DEFAULT' : 'x86', - 'intel' : 'x86', 'x86' : 'x86', - 'amd64' : 'x64', 'x64' : 'x64', - 'itanium' : 'ia64', 'ia64' : 'ia64', - } -# The globals VERSION, ARCH, MACROS and VC_ENV are defined later +# A map keyed by get_platform() return values to values accepted by +# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is +# the param to cross-compile on x86 targetting amd64.) +PLAT_TO_VCVARS = { + 'win32' : 'x86', + 'win-amd64' : 'amd64', + 'win-ia64' : 'ia64', +} class Reg: """Helper class to read values from the registry @@ -176,23 +179,6 @@ def get_build_version(): # else we don't know what version of the compiler this is return None -def get_build_architecture(): - """Return the processor architecture. - - Possible results are "x86" or "amd64". - """ - prefix = " bit (" - i = sys.version.find(prefix) - if i == -1: - return "x86" - j = sys.version.find(")", i) - sysarch = sys.version[i+len(prefix):j].lower() - arch = ARCHS.get(sysarch, None) - if arch is None: - return ARCHS['DEFAULT'] - else: - return arch - def normalize_and_reduce_paths(paths): """Return a list of normalized paths with duplicates removed. @@ -251,6 +237,7 @@ def query_vcvarsall(version, arch="x86"): if vcvarsall is None: raise IOError("Unable to find vcvarsall.bat") + log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -281,9 +268,7 @@ def query_vcvarsall(version, arch="x86"): VERSION = get_build_version() if VERSION < 8.0: raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) -ARCH = get_build_architecture() # MACROS = MacroExpander(VERSION) -VC_ENV = query_vcvarsall(VERSION, ARCH) class MSVCCompiler(CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, @@ -318,13 +303,25 @@ class MSVCCompiler(CCompiler) : def __init__(self, verbose=0, dry_run=0, force=0): CCompiler.__init__ (self, verbose, dry_run, force) self.__version = VERSION - self.__arch = ARCH self.__root = r"Software\Microsoft\VisualStudio" # self.__macros = MACROS self.__path = [] + # target platform (.plat_name is consistent with 'bdist') + self.plat_name = None + self.__arch = None # deprecated name self.initialized = False - def initialize(self): + def initialize(self, plat_name=None): + # multi-init means we would need to check platform same each time... + assert not self.initialized, "don't init multiple times" + if plat_name is None: + plat_name = get_platform() + # sanity check for platforms to prevent obscure errors later. + ok_plats = 'win32', 'win-amd64', 'win-ia64' + if plat_name not in ok_plats: + raise DistutilsPlatformError("--plat-name must be one of %s" % + (ok_plats,)) + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): # Assume that the SDK set up everything alright; don't try to be # smarter @@ -334,9 +331,24 @@ class MSVCCompiler(CCompiler) : self.rc = "rc.exe" self.mc = "mc.exe" else: - self.__paths = VC_ENV['path'].split(os.pathsep) - os.environ['lib'] = VC_ENV['lib'] - os.environ['include'] = VC_ENV['include'] + # On x86, 'vcvars32.bat amd64' creates an env that doesn't work; + # to cross compile, you use 'x86_amd64'. + # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross + # compile use 'x86' (ie, it runs the x86 compiler directly) + # No idea how itanium handles this, if at all. + if plat_name == get_platform() or plat_name == 'win32': + # native build or cross-compile to win32 + plat_spec = PLAT_TO_VCVARS[plat_name] + else: + # cross compile from win32 -> some 64bit + plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ + PLAT_TO_VCVARS[plat_name] + + vc_env = query_vcvarsall(VERSION, plat_spec) + + self.__paths = vc_env['path'].split(os.pathsep) + os.environ['lib'] = vc_env['lib'] + os.environ['include'] = vc_env['include'] if len(self.__paths) == 0: raise DistutilsPlatformError("Python was built with %s, " diff --git a/msvccompiler.py b/msvccompiler.py index 3b4e9c9d..71146dcf 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -638,5 +638,5 @@ if get_build_version() >= 8.0: log.debug("Importing new compiler from distutils.msvc9compiler") OldMSVCCompiler = MSVCCompiler from distutils.msvc9compiler import MSVCCompiler - from distutils.msvc9compiler import get_build_architecture + # get_build_architecture not really relevant now we support cross-compile from distutils.msvc9compiler import MacroExpander diff --git a/util.py b/util.py index 917f1d0b..72039a7e 100644 --- a/util.py +++ b/util.py @@ -30,7 +30,7 @@ def get_platform (): irix64-6.2 Windows will return one of: - win-x86_64 (64bit Windows on x86_64 (AMD64)) + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) win-ia64 (64bit Windows on Itanium) win32 (all others - specifically, sys.platform is returned) @@ -45,7 +45,7 @@ def get_platform (): j = sys.version.find(")", i) look = sys.version[i+len(prefix):j].lower() if look == 'amd64': - return 'win-x86_64' + return 'win-amd64' if look == 'itanium': return 'win-ia64' return sys.platform -- cgit v1.2.1 From 6d3e6470c91a0dd98c587edd9ef353906a981a17 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Fri, 2 May 2008 12:48:15 +0000 Subject: #2581: Vista UAC/elevation support for bdist_wininst --- command/bdist_wininst.py | 7 +++++++ command/wininst-6.0.exe | Bin 61440 -> 61440 bytes command/wininst-7.1.exe | Bin 61440 -> 61440 bytes command/wininst-9.0-amd64.exe | Bin 76288 -> 77312 bytes command/wininst-9.0.exe | Bin 65536 -> 66048 bytes msvc9compiler.py | 25 ++++++++++++++++++++++++- 6 files changed, 31 insertions(+), 1 deletion(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 02542afb..7c43e745 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -50,6 +50,10 @@ class bdist_wininst (Command): "Fully qualified filename of a script to be run before " "any files are installed. This script need not be in the " "distribution"), + ('user-access-control=', None, + "specify Vista's UAC handling - 'none'/default=no " + "handling, 'auto'=use UAC if target Python installed for " + "all users, 'force'=always use UAC"), ] boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', @@ -68,6 +72,7 @@ class bdist_wininst (Command): self.skip_build = 0 self.install_script = None self.pre_install_script = None + self.user_access_control = None # initialize_options() @@ -220,6 +225,8 @@ class bdist_wininst (Command): lines.append("target_optimize=%d" % (not self.no_target_optimize)) if self.target_version: lines.append("target_version=%s" % self.target_version) + if self.user_access_control: + lines.append("user_access_control=%s" % self.user_access_control) title = self.title or self.distribution.get_fullname() lines.append("title=%s" % escape(title)) diff --git a/command/wininst-6.0.exe b/command/wininst-6.0.exe index bd715250..10c98199 100644 Binary files a/command/wininst-6.0.exe and b/command/wininst-6.0.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index ee357139..6779aa8d 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index c99ede4b..b4cb062c 100644 Binary files a/command/wininst-9.0-amd64.exe and b/command/wininst-9.0-amd64.exe differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 5e0144c9..0d04a667 100644 Binary files a/command/wininst-9.0.exe and b/command/wininst-9.0.exe differ diff --git a/msvc9compiler.py b/msvc9compiler.py index 8b1cf9a9..c8d52c42 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -594,14 +594,25 @@ class MSVCCompiler(CCompiler) : # needed! Make sure they are generated in the temporary build # directory. Since they have different names for debug and release # builds, they can go into the same directory. + build_temp = os.path.dirname(objects[0]) if export_symbols is not None: (dll_name, dll_ext) = os.path.splitext( os.path.basename(output_filename)) implib_file = os.path.join( - os.path.dirname(objects[0]), + build_temp, self.library_filename(dll_name)) ld_args.append ('/IMPLIB:' + implib_file) + # Embedded manifests are recommended - see MSDN article titled + # "How to: Embed a Manifest Inside a C/C++ Application" + # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) + # Ask the linker to generate the manifest in the temp dir, so + # we can embed it later. + temp_manifest = os.path.join( + build_temp, + os.path.basename(output_filename) + ".manifest") + ld_args.append('/MANIFESTFILE:' + temp_manifest) + if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: @@ -613,6 +624,18 @@ class MSVCCompiler(CCompiler) : except DistutilsExecError as msg: raise LinkError(msg) + # embed the manifest + # XXX - this is somewhat fragile - if mt.exe fails, distutils + # will still consider the DLL up-to-date, but it will not have a + # manifest. Maybe we should link to a temp file? OTOH, that + # implies a build environment error that shouldn't go undetected. + mfid = 1 if target_desc == CCompiler.EXECUTABLE else 2 + out_arg = '-outputresource:%s;%s' % (output_filename, mfid) + try: + self.spawn(['mt.exe', '-nologo', '-manifest', + temp_manifest, out_arg]) + except DistutilsExecError as msg: + raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) -- cgit v1.2.1 From 2da7eca35531226358da395c9cd1aacaa81620e9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 4 May 2008 22:42:01 +0000 Subject: Merged revisions 62425-62429,62434-62436,62441,62444,62446-62448,62450-62455,62463,62465-62466,62469,62474,62476-62478,62480,62485,62492,62497-62498,62500,62507,62513-62514,62516,62521,62531,62535,62545-62546,62548-62551,62553-62559,62569,62574,62577,62593,62595,62604-62606,62608,62616,62626-62627,62636,62638,62644-62645,62647-62648,62651-62653,62656,62661,62663,62680,62686-62687,62696,62699-62703,62711 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ................ r62425 | andrew.kuchling | 2008-04-21 03:45:57 +0200 (Mon, 21 Apr 2008) | 1 line Comment typo ................ r62426 | mark.dickinson | 2008-04-21 03:55:50 +0200 (Mon, 21 Apr 2008) | 2 lines Silence 'r may be used uninitialized' compiler warning. ................ r62427 | andrew.kuchling | 2008-04-21 04:08:00 +0200 (Mon, 21 Apr 2008) | 1 line Markup fix ................ r62428 | andrew.kuchling | 2008-04-21 04:08:13 +0200 (Mon, 21 Apr 2008) | 1 line Wording changes ................ r62429 | andrew.kuchling | 2008-04-21 04:14:24 +0200 (Mon, 21 Apr 2008) | 1 line Add various items ................ r62434 | thomas.heller | 2008-04-21 15:46:55 +0200 (Mon, 21 Apr 2008) | 1 line Fix typo. ................ r62435 | david.goodger | 2008-04-21 16:40:22 +0200 (Mon, 21 Apr 2008) | 1 line corrections ("reStructuredText" is one word) ................ r62436 | david.goodger | 2008-04-21 16:43:33 +0200 (Mon, 21 Apr 2008) | 1 line capitalization ................ r62441 | gregory.p.smith | 2008-04-21 19:46:40 +0200 (Mon, 21 Apr 2008) | 2 lines explicitly flush after the ... since there wasn't a newline ................ r62444 | jeroen.ruigrok | 2008-04-21 22:15:39 +0200 (Mon, 21 Apr 2008) | 2 lines Windows x64 also falls under VER_PLATFORM_WIN32_NT. ................ r62446 | gregory.p.smith | 2008-04-21 23:31:08 +0200 (Mon, 21 Apr 2008) | 3 lines If sys.stdin is not a tty, fall back to default_getpass after printing a warning instead of failing with a termios.error. ................ r62447 | mark.dickinson | 2008-04-22 00:32:24 +0200 (Tue, 22 Apr 2008) | 8 lines test_math and test_cmath are failing on the FreeBSD 6.2 trunk buildbot, apparently because tanh(-0.) loses the sign of zero on that platform. If true, this is a bug in FreeBSD. Added a configure test to verify this. I still need to figure out how best to deal with this failure. ................ r62448 | amaury.forgeotdarc | 2008-04-22 00:35:30 +0200 (Tue, 22 Apr 2008) | 7 lines Issue 2665: On Windows, sys.stderr does not contain a valid file when running without a console. It seems to work, but will fail at the first flush. This causes IDLE to crash when too many warnings are printed. Will backport. ................ r62450 | benjamin.peterson | 2008-04-22 00:57:00 +0200 (Tue, 22 Apr 2008) | 2 lines Fix Sphinx warnings ................ r62451 | mark.dickinson | 2008-04-22 02:54:27 +0200 (Tue, 22 Apr 2008) | 3 lines Make configure test for tanh(-0.) == -0. committed in r62447 actually work. (The test wasn't properly linked with libm. Sigh.) ................ r62452 | benjamin.peterson | 2008-04-22 04:16:03 +0200 (Tue, 22 Apr 2008) | 2 lines Various io doc updates ................ r62453 | neal.norwitz | 2008-04-22 07:07:47 +0200 (Tue, 22 Apr 2008) | 1 line Add Thomas Lee ................ r62454 | gregory.p.smith | 2008-04-22 10:08:41 +0200 (Tue, 22 Apr 2008) | 8 lines Major improvements: * Default to using /dev/tty for the password prompt and input before falling back to sys.stdin and sys.stderr. * Use sys.stderr instead of sys.stdout. * print the 'password may be echoed' warning to stream used to display the prompt rather than always sys.stderr. * warn() with GetPassWarning when input may be echoed. ................ r62455 | gregory.p.smith | 2008-04-22 10:11:33 +0200 (Tue, 22 Apr 2008) | 2 lines update the getpass entry ................ r62463 | amaury.forgeotdarc | 2008-04-22 23:14:41 +0200 (Tue, 22 Apr 2008) | 5 lines Issue #2670: urllib2.build_opener() failed when two handlers derive the same default base class. Will backport. ................ r62465 | skip.montanaro | 2008-04-23 00:45:09 +0200 (Wed, 23 Apr 2008) | 3 lines Factor in documentation changes from issue 1753732. ................ r62466 | gregory.p.smith | 2008-04-23 03:06:42 +0200 (Wed, 23 Apr 2008) | 2 lines syntax fixup ................ r62469 | benjamin.peterson | 2008-04-23 22:38:06 +0200 (Wed, 23 Apr 2008) | 2 lines #2673 Fix example typo in optparse docs ................ r62474 | martin.v.loewis | 2008-04-24 11:50:50 +0200 (Thu, 24 Apr 2008) | 2 lines Add Guilherme Polo. ................ r62476 | martin.v.loewis | 2008-04-24 15:16:36 +0200 (Thu, 24 Apr 2008) | 3 lines Remove Py_Refcnt, Py_Type, Py_Size, as they were added only for backwards compatibility, yet 2.5 did not have them at all. ................ r62477 | martin.v.loewis | 2008-04-24 15:17:24 +0200 (Thu, 24 Apr 2008) | 2 lines Fix typo. ................ r62478 | martin.v.loewis | 2008-04-24 15:18:03 +0200 (Thu, 24 Apr 2008) | 2 lines Add Jesus Cea. ................ r62480 | amaury.forgeotdarc | 2008-04-24 20:07:05 +0200 (Thu, 24 Apr 2008) | 4 lines Issue2681: the literal 0o8 was wrongly accepted, and evaluated as float(0.0). This happened only when 8 is the first digit. Credits go to Lukas Meuser. ................ r62485 | amaury.forgeotdarc | 2008-04-24 22:10:26 +0200 (Thu, 24 Apr 2008) | 5 lines Disable gc when running test_trace, or we may record the __del__ of collected objects. See http://mail.python.org/pipermail/python-checkins/2008-April/068633.html the extra events perfectly match several calls to socket._fileobject.__del__() ................ r62492 | neal.norwitz | 2008-04-25 05:40:17 +0200 (Fri, 25 Apr 2008) | 1 line Fix typo (now -> no) ................ r62497 | armin.rigo | 2008-04-25 11:35:18 +0200 (Fri, 25 Apr 2008) | 2 lines A new crasher. ................ r62498 | thomas.heller | 2008-04-25 17:44:16 +0200 (Fri, 25 Apr 2008) | 1 line Add from_buffer and from_buffer_copy class methods to ctypes types. ................ r62500 | mark.dickinson | 2008-04-25 18:59:09 +0200 (Fri, 25 Apr 2008) | 3 lines Issue 2635: fix bug in the fix_sentence_endings option to textwrap.fill. ................ r62507 | benjamin.peterson | 2008-04-25 23:43:56 +0200 (Fri, 25 Apr 2008) | 2 lines Allow test_import to work when it is invoked directly ................ r62513 | georg.brandl | 2008-04-26 20:31:07 +0200 (Sat, 26 Apr 2008) | 2 lines #2691: document PyLong (s)size_t APIs, patch by Alexander Belopolsky. ................ r62514 | georg.brandl | 2008-04-26 20:32:17 +0200 (Sat, 26 Apr 2008) | 2 lines Add missing return type to dealloc. ................ r62516 | alexandre.vassalotti | 2008-04-27 02:52:24 +0200 (Sun, 27 Apr 2008) | 2 lines Fixed URL of PEP 205 in weakref's module docstring. ................ r62521 | georg.brandl | 2008-04-27 11:39:59 +0200 (Sun, 27 Apr 2008) | 2 lines #2677: add note that not all functions may accept keyword args. ................ r62531 | georg.brandl | 2008-04-27 19:38:55 +0200 (Sun, 27 Apr 2008) | 2 lines Use correct XHTML tags. ................ r62535 | benjamin.peterson | 2008-04-27 20:14:39 +0200 (Sun, 27 Apr 2008) | 2 lines #2700 Document PyNumber_ToBase ................ r62545 | skip.montanaro | 2008-04-27 22:53:57 +0200 (Sun, 27 Apr 2008) | 1 line minor wording changes, rewrap a few lines ................ r62546 | kurt.kaiser | 2008-04-27 23:07:41 +0200 (Sun, 27 Apr 2008) | 7 lines Home / Control-A toggles between left margin and end of leading white space. Patch 1196903 Jeff Shute. M idlelib/PyShell.py M idlelib/EditorWindow.py M idlelib/NEWS.txt ................ r62548 | kurt.kaiser | 2008-04-27 23:38:05 +0200 (Sun, 27 Apr 2008) | 2 lines Improved AutoCompleteWindow logic. Patch 2062 Tal Einat. ................ r62549 | kurt.kaiser | 2008-04-27 23:52:19 +0200 (Sun, 27 Apr 2008) | 4 lines Autocompletion of filenames now support alternate separators, e.g. the '/' char on Windows. Patch 2061 Tal Einat. ................ r62550 | skip.montanaro | 2008-04-28 00:49:56 +0200 (Mon, 28 Apr 2008) | 6 lines A few small changes: * The only exception we should catch when trying to import cStringIO is an ImportError. * Delete the function signatures embedded in the mk*temp docstrings. * The tempdir global variable was initialized twice. ................ r62551 | skip.montanaro | 2008-04-28 00:52:02 +0200 (Mon, 28 Apr 2008) | 4 lines Wrap some long paragraphs and include the default values for optional function parameters. ................ r62553 | skip.montanaro | 2008-04-28 04:57:23 +0200 (Mon, 28 Apr 2008) | 7 lines Minor cleanups: * Avoid creating unused local variables where we can. Where we can't prefix the unused variables with '_'. * Avoid shadowing builtins where it won't change the external interface of a function. * Use None as default path arg to readmodule and readmodule_ex. ................ r62554 | skip.montanaro | 2008-04-28 04:59:45 +0200 (Mon, 28 Apr 2008) | 6 lines Correct documentation to match implementation: "Class" instead of "class_descriptor", "Function" instead of "function_descriptor". Note default path value for readmodule*. Wrap some long paragraphs. Don't mention 'inpackage' which isn't part of the public API. ................ r62555 | brett.cannon | 2008-04-28 05:23:50 +0200 (Mon, 28 Apr 2008) | 5 lines Fix a bug introduced by the warnings rewrite where tracebacks were being improperly indented. Closes issue #2699. ................ r62556 | skip.montanaro | 2008-04-28 05:25:37 +0200 (Mon, 28 Apr 2008) | 2 lines Wrap some long lines. ................ r62557 | skip.montanaro | 2008-04-28 05:27:53 +0200 (Mon, 28 Apr 2008) | 6 lines Get rid of _test(), _main(), _debug() and _check(). Tests are no longer needed (better set available in Lib/test/test_robotparser.py). Clean up a few PEP 8 nits (compound statements on a single line, whitespace around operators). ................ r62558 | brett.cannon | 2008-04-28 06:50:06 +0200 (Mon, 28 Apr 2008) | 3 lines Rename the test_traceback_print() function to traceback_print() to prevent test_capi from automatically calling the function. ................ r62559 | georg.brandl | 2008-04-28 07:16:30 +0200 (Mon, 28 Apr 2008) | 2 lines Fix markup. ................ r62569 | amaury.forgeotdarc | 2008-04-28 23:07:06 +0200 (Mon, 28 Apr 2008) | 5 lines test_sundry performs minimal tests (a simple import...) on modules that are not tested otherwise. Some of them now have tests and can be removed. Only 70 to go... ................ r62574 | andrew.kuchling | 2008-04-29 04:03:54 +0200 (Tue, 29 Apr 2008) | 1 line Strip down SSL docs; I'm not managing to get test programs working, so I'll just give a minimal description ................ r62577 | martin.v.loewis | 2008-04-29 08:10:53 +0200 (Tue, 29 Apr 2008) | 2 lines Add Rodrigo and Heiko. ................ r62593 | nick.coghlan | 2008-04-30 16:23:36 +0200 (Wed, 30 Apr 2008) | 1 line Update command line usage documentation to reflect 2.6 changes (also includes some minor cleanups). Addresses TODO list issue 2258 ................ r62595 | andrew.kuchling | 2008-04-30 18:19:55 +0200 (Wed, 30 Apr 2008) | 1 line Typo fix ................ r62604 | benjamin.peterson | 2008-04-30 23:03:58 +0200 (Wed, 30 Apr 2008) | 2 lines make test_support's captured_output a bit more robust when exceptions happen ................ r62605 | georg.brandl | 2008-04-30 23:08:42 +0200 (Wed, 30 Apr 2008) | 2 lines #1748: use functools.wraps instead of rolling own metadata update. ................ r62606 | benjamin.peterson | 2008-04-30 23:25:55 +0200 (Wed, 30 Apr 2008) | 2 lines Remove some from __future__ import with_statements ................ r62608 | benjamin.peterson | 2008-05-01 00:03:36 +0200 (Thu, 01 May 2008) | 2 lines Fix typo in whatsnew ................ r62616 | georg.brandl | 2008-05-01 20:24:32 +0200 (Thu, 01 May 2008) | 2 lines Fix synopsis. ................ r62626 | brett.cannon | 2008-05-02 04:25:09 +0200 (Fri, 02 May 2008) | 6 lines Fix a backwards-compatibility mistake where a new optional argument for warnings.showwarning() was being used. This broke pre-existing replacements for the function since they didn't support the extra argument. Closes issue 2705. ................ r62627 | gregory.p.smith | 2008-05-02 09:26:52 +0200 (Fri, 02 May 2008) | 20 lines This should fix issue2632. A long description of the two competing problems is in the bug report (one old, one recently introduced trying to fix the old one). In short: buffer data during socket._fileobject.read() and readlines() within a cStringIO object instead of a [] of str()s returned from the recv() call. This prevents excessive memory use due to the size parameter being passed to recv() being grossly larger than the actual size of the data returned *and* prevents excessive cpu usage due to looping in python calling recv() with a very tiny size value if min() is used as the previous memory-use bug "fix" did. It also documents what the socket._fileobject._rbufsize member is actually used for. This is a candidate for back porting to 2.5. ................ r62636 | mark.hammond | 2008-05-02 14:48:15 +0200 (Fri, 02 May 2008) | 2 lines #2581: Vista UAC/elevation support for bdist_wininst ................ r62638 | facundo.batista | 2008-05-02 19:39:00 +0200 (Fri, 02 May 2008) | 3 lines Fixed some test structures. Thanks Mark Dickinson. ................ r62644 | ronald.oussoren | 2008-05-02 21:45:11 +0200 (Fri, 02 May 2008) | 7 lines Fix for issue #2573: Can't change the framework name on OS X builds This introduces a new configure option: --with-framework-name=NAME (defaulting to 'Python'). This allows you to install several copies of the Python framework with different names (such as a normal build and a debug build). ................ r62645 | ronald.oussoren | 2008-05-02 21:58:56 +0200 (Fri, 02 May 2008) | 2 lines Finish fix for issue2573, previous patch was incomplete. ................ r62647 | martin.v.loewis | 2008-05-02 23:30:20 +0200 (Fri, 02 May 2008) | 13 lines Merged revisions 62263-62646 via svnmerge from svn+ssh://pythondev@svn.python.org/sandbox/trunk/2to3/lib2to3 ........ r62470 | david.wolever | 2008-04-24 02:11:07 +0200 (Do, 24 Apr 2008) | 3 lines Fixed up and applied the patch for #2431 -- speeding up 2to3 with a lookup table. ........ r62646 | martin.v.loewis | 2008-05-02 23:29:27 +0200 (Fr, 02 Mai 2008) | 2 lines Fix whitespace. ........ ................ r62648 | ronald.oussoren | 2008-05-02 23:42:35 +0200 (Fri, 02 May 2008) | 4 lines Fix for #1905: PythonLauncher not working correctly on OSX 10.5/Leopard This fixes both Python Launchar and the terminalcommand module. ................ r62651 | ronald.oussoren | 2008-05-02 23:54:56 +0200 (Fri, 02 May 2008) | 2 lines Fix for issue #2520 (cannot import macerrors) ................ r62652 | benjamin.peterson | 2008-05-03 00:12:58 +0200 (Sat, 03 May 2008) | 2 lines capitalization nit for reStructuredText ................ r62653 | brett.cannon | 2008-05-03 03:02:41 +0200 (Sat, 03 May 2008) | 2 lines Fix some indentation errors. ................ r62656 | brett.cannon | 2008-05-03 05:19:39 +0200 (Sat, 03 May 2008) | 6 lines Fix the C implementation of 'warnings' to infer the filename of the module that raised an exception properly when __file__ is not set, __name__ == '__main__', and sys.argv[0] is a false value. Closes issue2743. ................ r62661 | amaury.forgeotdarc | 2008-05-03 14:21:13 +0200 (Sat, 03 May 2008) | 8 lines In test_io, StatefulIncrementalDecoderTest was not part of the test suite. And of course, the test failed: a bytearray was used without reason in io.TextIOWrapper.tell(). The difference is that iterating over bytes (i.e. str in python2.6) returns 1-char bytes, whereas bytearrays yield integers. This code should still work with python3.0 ................ r62663 | benjamin.peterson | 2008-05-03 17:56:42 +0200 (Sat, 03 May 2008) | 2 lines The compiling struct is now passed around to all AST helpers (see issue 2720) ................ r62680 | benjamin.peterson | 2008-05-03 23:35:18 +0200 (Sat, 03 May 2008) | 2 lines Moved testing of builtin types out of test_builtin and into type specific modules ................ r62686 | mark.dickinson | 2008-05-04 04:25:46 +0200 (Sun, 04 May 2008) | 4 lines Make sure that Context traps and flags dictionaries have values 0 and 1 (as documented) rather than True and False. ................ r62687 | benjamin.peterson | 2008-05-04 05:05:49 +0200 (Sun, 04 May 2008) | 2 lines Fix typo in whatsnew ................ r62696 | georg.brandl | 2008-05-04 11:15:04 +0200 (Sun, 04 May 2008) | 2 lines #2752: wrong meaning of '' for socket host. ................ r62699 | christian.heimes | 2008-05-04 13:50:53 +0200 (Sun, 04 May 2008) | 1 line Added note that Python requires at least Win2k SP4 ................ r62700 | gerhard.haering | 2008-05-04 14:59:57 +0200 (Sun, 04 May 2008) | 3 lines SQLite requires 64-bit integers in order to build. So the whole HAVE_LONG_LONG #ifdefing was useless. ................ r62701 | gerhard.haering | 2008-05-04 15:15:12 +0200 (Sun, 04 May 2008) | 3 lines Applied sqliterow-richcmp.diff patch from Thomas Heller in Issue2152. The sqlite3.Row type is now correctly hashable. ................ r62702 | gerhard.haering | 2008-05-04 15:42:44 +0200 (Sun, 04 May 2008) | 5 lines Implemented feature request 2157: Converter names are cut off at '(' characters. This avoids the common case of something like 'NUMBER(10)' not being parsed as 'NUMBER', like expected. Also corrected the docs about converter names being case-sensitive. They aren't any longer. ................ r62703 | georg.brandl | 2008-05-04 17:45:05 +0200 (Sun, 04 May 2008) | 2 lines #2757: Remove spare newline. ................ r62711 | benjamin.peterson | 2008-05-04 21:10:02 +0200 (Sun, 04 May 2008) | 2 lines Fix typo in bugs.rst ................ --- command/bdist_wininst.py | 7 +++++++ command/wininst-6.0.exe | Bin 61440 -> 61440 bytes command/wininst-7.1.exe | Bin 61440 -> 61440 bytes command/wininst-9.0-amd64.exe | Bin 76288 -> 77312 bytes command/wininst-9.0.exe | Bin 65536 -> 66048 bytes msvc9compiler.py | 25 ++++++++++++++++++++++++- 6 files changed, 31 insertions(+), 1 deletion(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index bf8d022a..3f0e09bc 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -48,6 +48,10 @@ class bdist_wininst(Command): "Fully qualified filename of a script to be run before " "any files are installed. This script need not be in the " "distribution"), + ('user-access-control=', None, + "specify Vista's UAC handling - 'none'/default=no " + "handling, 'auto'=use UAC if target Python installed for " + "all users, 'force'=always use UAC"), ] boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', @@ -66,6 +70,7 @@ class bdist_wininst(Command): self.skip_build = 0 self.install_script = None self.pre_install_script = None + self.user_access_control = None def finalize_options(self): @@ -211,6 +216,8 @@ class bdist_wininst(Command): lines.append("target_optimize=%d" % (not self.no_target_optimize)) if self.target_version: lines.append("target_version=%s" % self.target_version) + if self.user_access_control: + lines.append("user_access_control=%s" % self.user_access_control) title = self.title or self.distribution.get_fullname() lines.append("title=%s" % escape(title)) diff --git a/command/wininst-6.0.exe b/command/wininst-6.0.exe index bd715250..10c98199 100644 Binary files a/command/wininst-6.0.exe and b/command/wininst-6.0.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index ee357139..6779aa8d 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index c99ede4b..b4cb062c 100644 Binary files a/command/wininst-9.0-amd64.exe and b/command/wininst-9.0-amd64.exe differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 5e0144c9..0d04a667 100644 Binary files a/command/wininst-9.0.exe and b/command/wininst-9.0.exe differ diff --git a/msvc9compiler.py b/msvc9compiler.py index 8b1cf9a9..c8d52c42 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -594,14 +594,25 @@ class MSVCCompiler(CCompiler) : # needed! Make sure they are generated in the temporary build # directory. Since they have different names for debug and release # builds, they can go into the same directory. + build_temp = os.path.dirname(objects[0]) if export_symbols is not None: (dll_name, dll_ext) = os.path.splitext( os.path.basename(output_filename)) implib_file = os.path.join( - os.path.dirname(objects[0]), + build_temp, self.library_filename(dll_name)) ld_args.append ('/IMPLIB:' + implib_file) + # Embedded manifests are recommended - see MSDN article titled + # "How to: Embed a Manifest Inside a C/C++ Application" + # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) + # Ask the linker to generate the manifest in the temp dir, so + # we can embed it later. + temp_manifest = os.path.join( + build_temp, + os.path.basename(output_filename) + ".manifest") + ld_args.append('/MANIFESTFILE:' + temp_manifest) + if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: @@ -613,6 +624,18 @@ class MSVCCompiler(CCompiler) : except DistutilsExecError as msg: raise LinkError(msg) + # embed the manifest + # XXX - this is somewhat fragile - if mt.exe fails, distutils + # will still consider the DLL up-to-date, but it will not have a + # manifest. Maybe we should link to a temp file? OTOH, that + # implies a build environment error that shouldn't go undetected. + mfid = 1 if target_desc == CCompiler.EXECUTABLE else 2 + out_arg = '-outputresource:%s;%s' % (output_filename, mfid) + try: + self.spawn(['mt.exe', '-nologo', '-manifest', + temp_manifest, out_arg]) + except DistutilsExecError as msg: + raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) -- cgit v1.2.1 From e2aef69588355bb9ecd378a778b3d50f00ca14c0 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 May 2008 22:41:46 +0000 Subject: Implemented PEP 370 --- command/install.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/command/install.py b/command/install.py index 33a1212b..44c76926 100644 --- a/command/install.py +++ b/command/install.py @@ -18,6 +18,9 @@ from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root from distutils.util import get_platform from distutils.errors import DistutilsOptionError +from site import USER_BASE +from site import USER_SITE + if sys.version < "2.2": WINDOWS_SCHEME = { @@ -51,7 +54,21 @@ INSTALL_SCHEMES = { 'scripts': '$base/bin', 'data' : '$base', }, + 'unix_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, 'nt': WINDOWS_SCHEME, + 'nt_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', + 'scripts': '$userbase/Scripts', + 'data' : '$userbase', + }, 'mac': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', @@ -59,13 +76,27 @@ INSTALL_SCHEMES = { 'scripts': '$base/Scripts', 'data' : '$base', }, + 'mac_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/$py_version_short/include/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, 'os2': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', 'headers': '$base/Include/$dist_name', 'scripts': '$base/Scripts', 'data' : '$base', - } + }, + 'os2_home': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, } # The keys to an installation scheme; if any new types of files are to be @@ -86,6 +117,8 @@ class install (Command): "(Unix only) prefix for platform-specific files"), ('home=', None, "(Unix only) home directory to install under"), + ('user', None, + "install in user site-package '%s'" % USER_SITE), # Or, just set the base director(y|ies) ('install-base=', None, @@ -137,7 +170,7 @@ class install (Command): "filename in which to record list of installed files"), ] - boolean_options = ['compile', 'force', 'skip-build'] + boolean_options = ['compile', 'force', 'skip-build', 'user'] negative_opt = {'no-compile' : 'compile'} @@ -148,6 +181,7 @@ class install (Command): self.prefix = None self.exec_prefix = None self.home = None + self.user = 0 # These select only the installation base; it's up to the user to # specify the installation scheme (currently, that means supplying @@ -166,6 +200,8 @@ class install (Command): self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None + self.install_userbase = USER_BASE + self.install_usersite = USER_SITE self.compile = None self.optimize = None @@ -241,6 +277,11 @@ class install (Command): raise DistutilsOptionError, \ "must supply either home or prefix/exec-prefix -- not both" + if self.user and (self.prefix or self.exec_prefix or self.home or + self.install_base or self.install_platbase): + raise DistutilsOptionError("can't combine user with with prefix/" + "exec_prefix/home or install_(plat)base") + # Next, stuff that's wrong (or dubious) only on certain platforms. if os.name != "posix": if self.exec_prefix: @@ -276,10 +317,13 @@ class install (Command): 'dist_fullname': self.distribution.get_fullname(), 'py_version': py_version, 'py_version_short': py_version[0:3], + 'py_version_nodot': py_version[0] + py_version[2], 'sys_prefix': prefix, 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, + 'userbase': self.install_userbase, + 'usersite': self.install_usersite, } self.expand_basedirs() @@ -301,6 +345,10 @@ class install (Command): self.dump_dirs("post-expand_dirs()") + # Create directories in the home dir: + if self.user: + self.create_home_path() + # Pick the actual directory to install all modules to: either # install_purelib or install_platlib, depending on whether this # module distribution is pure or not. Of course, if the user @@ -315,7 +363,8 @@ class install (Command): # Convert directories from Unix /-separated syntax to the local # convention. self.convert_paths('lib', 'purelib', 'platlib', - 'scripts', 'data', 'headers') + 'scripts', 'data', 'headers', + 'userbase', 'usersite') # Well, we're not actually fully completely finalized yet: we still # have to deal with 'extra_path', which is the hack for allowing @@ -376,7 +425,13 @@ class install (Command): "installation scheme is incomplete") return - if self.home is not None: + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError( + "User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme("unix_user") + elif self.home is not None: self.install_base = self.install_platbase = self.home self.select_scheme("unix_home") else: @@ -401,7 +456,13 @@ class install (Command): def finalize_other (self): # Windows and Mac OS for now - if self.home is not None: + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError( + "User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme(os.name + "_user") + elif self.home is not None: self.install_base = self.install_platbase = self.home self.select_scheme("unix_home") else: @@ -431,7 +492,7 @@ class install (Command): for attr in attrs: val = getattr(self, attr) if val is not None: - if os.name == 'posix': + if os.name == 'posix' or os.name == 'nt': val = os.path.expanduser(val) val = subst_vars(val, self.config_vars) setattr(self, attr, val) @@ -496,6 +557,16 @@ class install (Command): attr = "install_" + name setattr(self, attr, change_root(self.root, getattr(self, attr))) + def create_home_path(self): + """Create directories under ~ + """ + if not self.user: + return + home = convert_path(os.path.expanduser("~")) + for name, path in self.config_vars.iteritems(): + if path.startswith(home) and not os.path.isdir(path): + self.debug_print("os.makedirs('%s', 0700)" % path) + os.makedirs(path, 0700) # -- Command execution methods ------------------------------------- -- cgit v1.2.1 From dd58a30c84365eaa82aa6175dbc52a26ee63bb7f Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 May 2008 23:45:46 +0000 Subject: Merged revisions 62774-62775,62785,62787-62788 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r62774 | georg.brandl | 2008-05-06 19:11:42 +0200 (Tue, 06 May 2008) | 2 lines #2773: fix description of 'g' and 'G' formatting spec. ........ r62775 | georg.brandl | 2008-05-06 19:20:54 +0200 (Tue, 06 May 2008) | 2 lines > != (!<). ........ r62785 | benjamin.peterson | 2008-05-07 00:18:11 +0200 (Wed, 07 May 2008) | 2 lines Fix logic error in Python/_warnings.c and add a test to verify ........ r62787 | benjamin.peterson | 2008-05-07 00:31:52 +0200 (Wed, 07 May 2008) | 2 lines Make the Python implementation of warnings compatible with the C implementation regarding non-callable showwarning ........ r62788 | christian.heimes | 2008-05-07 00:41:46 +0200 (Wed, 07 May 2008) | 1 line Implemented PEP 370 ........ --- command/install.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/command/install.py b/command/install.py index 50884c33..0a902cee 100644 --- a/command/install.py +++ b/command/install.py @@ -15,6 +15,9 @@ from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root from distutils.util import get_platform from distutils.errors import DistutilsOptionError +from site import USER_BASE +from site import USER_SITE + if sys.version < "2.2": WINDOWS_SCHEME = { @@ -48,7 +51,21 @@ INSTALL_SCHEMES = { 'scripts': '$base/bin', 'data' : '$base', }, + 'unix_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, 'nt': WINDOWS_SCHEME, + 'nt_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', + 'scripts': '$userbase/Scripts', + 'data' : '$userbase', + }, 'mac': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', @@ -56,13 +73,27 @@ INSTALL_SCHEMES = { 'scripts': '$base/Scripts', 'data' : '$base', }, + 'mac_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/$py_version_short/include/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, 'os2': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', 'headers': '$base/Include/$dist_name', 'scripts': '$base/Scripts', 'data' : '$base', - } + }, + 'os2_home': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, } # The keys to an installation scheme; if any new types of files are to be @@ -83,6 +114,8 @@ class install (Command): "(Unix only) prefix for platform-specific files"), ('home=', None, "(Unix only) home directory to install under"), + ('user', None, + "install in user site-package '%s'" % USER_SITE), # Or, just set the base director(y|ies) ('install-base=', None, @@ -134,7 +167,7 @@ class install (Command): "filename in which to record list of installed files"), ] - boolean_options = ['compile', 'force', 'skip-build'] + boolean_options = ['compile', 'force', 'skip-build', 'user'] negative_opt = {'no-compile' : 'compile'} @@ -145,6 +178,7 @@ class install (Command): self.prefix = None self.exec_prefix = None self.home = None + self.user = 0 # These select only the installation base; it's up to the user to # specify the installation scheme (currently, that means supplying @@ -163,6 +197,8 @@ class install (Command): self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None + self.install_userbase = USER_BASE + self.install_usersite = USER_SITE self.compile = None self.optimize = None @@ -238,6 +274,11 @@ class install (Command): raise DistutilsOptionError( "must supply either home or prefix/exec-prefix -- not both") + if self.user and (self.prefix or self.exec_prefix or self.home or + self.install_base or self.install_platbase): + raise DistutilsOptionError("can't combine user with with prefix/" + "exec_prefix/home or install_(plat)base") + # Next, stuff that's wrong (or dubious) only on certain platforms. if os.name != "posix": if self.exec_prefix: @@ -273,10 +314,13 @@ class install (Command): 'dist_fullname': self.distribution.get_fullname(), 'py_version': py_version, 'py_version_short': py_version[0:3], + 'py_version_nodot': py_version[0] + py_version[2], 'sys_prefix': prefix, 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, + 'userbase': self.install_userbase, + 'usersite': self.install_usersite, } self.expand_basedirs() @@ -298,6 +342,10 @@ class install (Command): self.dump_dirs("post-expand_dirs()") + # Create directories in the home dir: + if self.user: + self.create_home_path() + # Pick the actual directory to install all modules to: either # install_purelib or install_platlib, depending on whether this # module distribution is pure or not. Of course, if the user @@ -312,7 +360,8 @@ class install (Command): # Convert directories from Unix /-separated syntax to the local # convention. self.convert_paths('lib', 'purelib', 'platlib', - 'scripts', 'data', 'headers') + 'scripts', 'data', 'headers', + 'userbase', 'usersite') # Well, we're not actually fully completely finalized yet: we still # have to deal with 'extra_path', which is the hack for allowing @@ -369,7 +418,13 @@ class install (Command): "installation scheme is incomplete") return - if self.home is not None: + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError( + "User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme("unix_user") + elif self.home is not None: self.install_base = self.install_platbase = self.home self.select_scheme("unix_home") else: @@ -391,7 +446,13 @@ class install (Command): def finalize_other(self): # Windows and Mac OS for now - if self.home is not None: + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError( + "User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme(os.name + "_user") + elif self.home is not None: self.install_base = self.install_platbase = self.home self.select_scheme("unix_home") else: @@ -419,7 +480,7 @@ class install (Command): for attr in attrs: val = getattr(self, attr) if val is not None: - if os.name == 'posix': + if os.name == 'posix' or os.name == 'nt': val = os.path.expanduser(val) val = subst_vars(val, self.config_vars) setattr(self, attr, val) @@ -479,6 +540,16 @@ class install (Command): attr = "install_" + name setattr(self, attr, change_root(self.root, getattr(self, attr))) + def create_home_path(self): + """Create directories under ~ + """ + if not self.user: + return + home = convert_path(os.path.expanduser("~")) + for name, path in self.config_vars.iteritems(): + if path.startswith(home) and not os.path.isdir(path): + self.debug_print("os.makedirs('%s', 0o700)" % path) + os.makedirs(path, 0o700) # -- Command execution methods ------------------------------------- -- cgit v1.2.1 From af488a5062e1af5b85b7c25c2a58087cb9905a86 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 8 May 2008 11:54:13 +0000 Subject: Bumping versions for 3.0a5 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b583e397..3618c4f7 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "3.0a4" +__version__ = "3.0a5" #--end constants-- -- cgit v1.2.1 From a59a6118c2edd586a14b636043fc27f6509a62f2 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 8 May 2008 13:16:19 +0000 Subject: Bump to 2.6a3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index d0dd03a3..e3e32f51 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6a2" +__version__ = "2.6a3" #--end constants-- -- cgit v1.2.1 From 8a2d33caa7898053d9c911a313660fc39a412bbe Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 8 May 2008 22:09:54 +0000 Subject: Replace instances of os.path.walk with os.walk --- archive_util.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/archive_util.py b/archive_util.py index a9c6a5b4..264e66fa 100644 --- a/archive_util.py +++ b/archive_util.py @@ -95,18 +95,16 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) - def visit (z, dirname, names): - for name in names: - path = os.path.normpath(os.path.join(dirname, name)) - if os.path.isfile(path): - z.write(path, path) - log.info("adding '%s'" % path) - if not dry_run: z = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED) - os.path.walk(base_dir, visit, z) + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + z.write(path, path) + log.info("adding '%s'" % path) z.close() return zip_filename -- cgit v1.2.1 From 2e52da55e1a498e632f64468c4babe7f2b542850 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 8 May 2008 22:27:58 +0000 Subject: Merged revisions 62873,62887,62892-62896,62904 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r62873 | raymond.hettinger | 2008-05-08 12:18:13 -0500 (Thu, 08 May 2008) | 1 line Issue 2778. Document the temporary frozenset swap in __contains__(), remove(), and discard(). ........ r62887 | brett.cannon | 2008-05-08 14:50:51 -0500 (Thu, 08 May 2008) | 5 lines Make test.test_support.catch_warning() take an argument specifying if any triggered warnings should be captured. This allows the context manager to be used to just prevent the internal state of the 'warnings' framework and thus allow triggered warnings to be displayed. ........ r62892 | brett.cannon | 2008-05-08 15:20:24 -0500 (Thu, 08 May 2008) | 4 lines Fix a bug introduced by the addition of the 'record' argument to test.test_support.catch_warning() where showwarning() was not being set properly. ........ r62893 | brett.cannon | 2008-05-08 15:20:54 -0500 (Thu, 08 May 2008) | 2 lines Document the 'record' argument for test.test_support.catch_warning(). ........ r62894 | brett.cannon | 2008-05-08 15:23:06 -0500 (Thu, 08 May 2008) | 4 lines Fix sys.flags to properly expose bytes_warning. Closes issue #2790. ........ r62895 | brett.cannon | 2008-05-08 15:23:54 -0500 (Thu, 08 May 2008) | 2 lines Add a missing entry on the fix for issue #2790. ........ r62896 | brett.cannon | 2008-05-08 15:24:43 -0500 (Thu, 08 May 2008) | 2 lines Add test.test_support.catch_warning()'s new argument. ........ r62904 | benjamin.peterson | 2008-05-08 17:09:54 -0500 (Thu, 08 May 2008) | 2 lines Replace instances of os.path.walk with os.walk ........ --- archive_util.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/archive_util.py b/archive_util.py index f3f65c6a..9444ff01 100644 --- a/archive_util.py +++ b/archive_util.py @@ -92,18 +92,16 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) - def visit (z, dirname, names): - for name in names: - path = os.path.normpath(os.path.join(dirname, name)) - if os.path.isfile(path): - z.write(path, path) - log.info("adding '%s'" % path) - if not dry_run: z = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED) - os.path.walk(base_dir, visit, z) + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + z.write(path, path) + log.info("adding '%s'" % path) z.close() return zip_filename -- cgit v1.2.1 From 78cb86ae70da077547532ef552ebdaee33ac0a72 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 9 May 2008 12:19:09 +0000 Subject: Add --user option to build_ext --- command/build_ext.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index bf5ad7e0..beb33194 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -10,6 +10,7 @@ __revision__ = "$Id$" import sys, os, string, re from types import * +from site import USER_BASE, USER_SITE from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version @@ -93,9 +94,11 @@ class build_ext (Command): "list of SWIG command line options"), ('swig=', None, "path to the SWIG executable"), + ('user', None, + "add user include, library and rpath"), ] - boolean_options = ['inplace', 'debug', 'force', 'swig-cpp'] + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] help_options = [ ('help-compiler', None, @@ -123,6 +126,7 @@ class build_ext (Command): self.swig = None self.swig_cpp = None self.swig_opts = None + self.user = None def finalize_options (self): from distutils import sysconfig @@ -257,6 +261,16 @@ class build_ext (Command): else: self.swig_opts = self.swig_opts.split(' ') + # Finally add the user include and library directories if requested + if self.user: + user_include = os.path.join(USER_BASE, "include") + user_lib = os.path.join(USER_BASE, "lib") + if os.path.isdir(user_include): + self.include_dirs.append(user_include) + if os.path.isdir(user_lib): + self.library_dirs.append(user_lib) + self.rpath.append(user_lib) + # finalize_options () -- cgit v1.2.1 From 22b54825ad9cfd0d15ebecb4b31b7886f6391833 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 10 May 2008 19:51:55 +0000 Subject: #1858 from Tarek Ziade: Allow multiple repositories in .pypirc; see http://wiki.python.org/moin/EnhancedPyPI for discussion. The patch is slightly revised from Tarek's last patch: I've simplified the PyPIRCCommand.finalize_options() method to not look at sys.argv. Tests still pass. --- command/register.py | 97 +++++++++++++++++++++++++++-------------------------- command/upload.py | 41 +++++++--------------- core.py | 1 + dist.py | 7 ++-- tests/test_dist.py | 47 ++++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 80 deletions(-) diff --git a/command/register.py b/command/register.py index 5c588eff..a9ab361a 100644 --- a/command/register.py +++ b/command/register.py @@ -8,37 +8,29 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" import os, string, urllib2, getpass, urlparse -import StringIO, ConfigParser +import StringIO -from distutils.core import Command +from distutils.core import PyPIRCCommand from distutils.errors import * +from distutils import log -class register(Command): +class register(PyPIRCCommand): description = ("register the distribution with the Python package index") - - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]"%DEFAULT_REPOSITORY), + user_options = PyPIRCCommand.user_options + [ ('list-classifiers', None, 'list the valid Trove classifiers'), - ('show-response', None, - 'display full response text from server'), ] - boolean_options = ['verify', 'show-response', 'list-classifiers'] + boolean_options = PyPIRCCommand.boolean_options + [ + 'verify', 'list-classifiers'] def initialize_options(self): - self.repository = None - self.show_response = 0 + PyPIRCCommand.initialize_options(self) self.list_classifiers = 0 - def finalize_options(self): - if self.repository is None: - self.repository = self.DEFAULT_REPOSITORY - def run(self): + self.finalize_options() + self._set_config() self.check_metadata() if self.dry_run: self.verify_metadata() @@ -77,6 +69,23 @@ class register(Command): "or (maintainer and maintainer_email) " + "must be supplied") + def _set_config(self): + ''' Reads the configuration file and set attributes. + ''' + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + self.has_config = True + else: + if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): + raise ValueError('%s not found in .pypirc' % self.repository) + if self.repository == 'pypi': + self.repository = self.DEFAULT_REPOSITORY + self.has_config = False + def classifiers(self): ''' Fetch the list of classifiers from the server. ''' @@ -90,6 +99,7 @@ class register(Command): (code, result) = self.post_to_server(self.build_post_data('verify')) print 'Server response (%s): %s'%(code, result) + def send_metadata(self): ''' Send the metadata to the package index server. @@ -99,10 +109,14 @@ class register(Command): First we try to read the username/password from $HOME/.pypirc, which is a ConfigParser-formatted file with a section - [server-login] containing username and password entries (both + [distutils] containing username and password entries (both in clear text). Eg: - [server-login] + [distutils] + index-servers = + pypi + + [pypi] username: fred password: sekrit @@ -114,21 +128,15 @@ class register(Command): 3. set the password to a random string and email the user. ''' - choice = 'x' - username = password = '' - # see if we can short-cut and get the username/password from the # config - config = None - if 'HOME' in os.environ: - rc = os.path.join(os.environ['HOME'], '.pypirc') - if os.path.exists(rc): - print 'Using PyPI login from %s'%rc - config = ConfigParser.ConfigParser() - config.read(rc) - username = config.get('server-login', 'username') - password = config.get('server-login', 'password') - choice = '1' + if self.has_config: + choice = '1' + username = self.username + password = self.password + else: + choice = 'x' + username = password = '' # get the user's login info choices = '1 2 3 4'.split() @@ -155,32 +163,24 @@ Your selection [default 1]: ''', # set up the authentication auth = urllib2.HTTPPasswordMgr() host = urlparse.urlparse(self.repository)[1] - auth.add_password('pypi', host, username, password) - + auth.add_password(self.realm, host, username, password) # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) - print 'Server response (%s): %s'%(code, result) + print 'Server response (%s): %s' % (code, result) # possibly save the login - if 'HOME' in os.environ and config is None and code == 200: - rc = os.path.join(os.environ['HOME'], '.pypirc') + if not self.has_config and code == 200: print 'I can store your PyPI login so future submissions will be faster.' - print '(the login will be stored in %s)'%rc + print '(the login will be stored in %s)' % self._get_rc_file() choice = 'X' while choice.lower() not in 'yn': choice = raw_input('Save your login (y/N)?') if not choice: choice = 'n' if choice.lower() == 'y': - f = open(rc, 'w') - f.write('[server-login]\nusername:%s\npassword:%s\n'%( - username, password)) - f.close() - try: - os.chmod(rc, 0600) - except: - pass + self._store_pypirc(username, password) + elif choice == '2': data = {':action': 'user'} data['name'] = data['password'] = data['email'] = '' @@ -243,7 +243,8 @@ Your selection [default 1]: ''', def post_to_server(self, data, auth=None): ''' Post a query to the server, and return a string response. ''' - + self.announce('Registering %s to %s' % (data['name'], + self.repository), log.INFO) # Build up the MIME payload for the urllib2 POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary diff --git a/command/upload.py b/command/upload.py index 301a1595..daf68112 100644 --- a/command/upload.py +++ b/command/upload.py @@ -3,7 +3,7 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" from distutils.errors import * -from distutils.core import Command +from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log from hashlib import md5 @@ -16,53 +16,38 @@ import base64 import urlparse import cStringIO as StringIO -class upload(Command): +class upload(PyPIRCCommand): description = "upload binary package to PyPI" - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]" % DEFAULT_REPOSITORY), - ('show-response', None, - 'display full response text from server'), + user_options = PyPIRCCommand.user_options + [ ('sign', 's', 'sign files to upload using gpg'), ('identity=', 'i', 'GPG identity used to sign files'), ] - boolean_options = ['show-response', 'sign'] + + boolean_options = PyPIRCCommand.boolean_options + ['sign'] def initialize_options(self): + PyPIRCCommand.initialize_options(self) self.username = '' self.password = '' - self.repository = '' self.show_response = 0 self.sign = False self.identity = None def finalize_options(self): + PyPIRCCommand.finalize_options(self) if self.identity and not self.sign: raise DistutilsOptionError( "Must use --sign for --identity to have meaning" ) - if 'HOME' in os.environ: - rc = os.path.join(os.environ['HOME'], '.pypirc') - if os.path.exists(rc): - self.announce('Using PyPI login from %s' % rc) - config = ConfigParser.ConfigParser({ - 'username':'', - 'password':'', - 'repository':''}) - config.read(rc) - if not self.repository: - self.repository = config.get('server-login', 'repository') - if not self.username: - self.username = config.get('server-login', 'username') - if not self.password: - self.password = config.get('server-login', 'password') - if not self.repository: - self.repository = self.DEFAULT_REPOSITORY + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] def run(self): if not self.distribution.dist_files: diff --git a/core.py b/core.py index 32b90269..de9ce7d7 100644 --- a/core.py +++ b/core.py @@ -20,6 +20,7 @@ from distutils.util import grok_environment_error # Mainly import these so setup scripts can "from distutils.core import" them. from distutils.dist import Distribution from distutils.cmd import Command +from distutils.config import PyPIRCCommand from distutils.extension import Extension # This is a barebones help message generated displayed when the user diff --git a/dist.py b/dist.py index d098cb97..0b13c1e6 100644 --- a/dist.py +++ b/dist.py @@ -343,10 +343,9 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - if 'HOME' in os.environ: - user_file = os.path.join(os.environ.get('HOME'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) + user_file = os.path.join(os.path.expanduser('~'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" diff --git a/tests/test_dist.py b/tests/test_dist.py index 5ae79332..28572637 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -55,6 +55,7 @@ class DistributionTestCase(unittest.TestCase): self.assertEqual(d.get_command_packages(), ["distutils.command"]) def test_command_packages_cmdline(self): + from distutils.tests.test_dist import test_dist sys.argv.extend(["--command-packages", "foo.bar,distutils.tests", "test_dist", @@ -65,6 +66,7 @@ class DistributionTestCase(unittest.TestCase): self.assertEqual(d.get_command_packages(), ["distutils.command", "foo.bar", "distutils.tests"]) cmd = d.get_command_obj("test_dist") + print cmd.__class__, test_dist self.assert_(isinstance(cmd, test_dist)) self.assertEqual(cmd.sample_option, "sometext") @@ -179,9 +181,54 @@ class MetadataTestCase(unittest.TestCase): dist.metadata.write_pkg_file(sio) return sio.getvalue() + def test_custom_pydistutils(self): + # fixes #2166 + # make sure pydistutils.cfg is found + old = {} + for env in ('HOME', 'HOMEPATH', 'HOMEDRIVE'): + value = os.environ.get(env) + old[env] = value + if value is not None: + del os.environ[env] + + if os.name == 'posix': + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + + curdir = os.path.dirname(__file__) + user_filename = os.path.join(curdir, user_filename) + f = open(user_filename, 'w') + f.write('.') + f.close() + + try: + dist = distutils.dist.Distribution() + + # linux-style + if sys.platform in ('linux', 'darwin'): + os.environ['HOME'] = curdir + files = dist.find_config_files() + self.assert_(user_filename in files) + + # win32-style + if sys.platform == 'win32': + # home drive should be found + os.environ['HOMEPATH'] = curdir + files = dist.find_config_files() + self.assert_(user_filename in files) + finally: + for key, value in old.items(): + if value is None: + continue + os.environ[key] = value + os.remove(user_filename) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) suite.addTest(unittest.makeSuite(MetadataTestCase)) return suite + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From a189b6be0df13ba6b77292b59ea501774804b422 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 10 May 2008 20:52:01 +0000 Subject: Revert r62998 as it broke the build (seems distutils.config is missing). --- command/register.py | 97 ++++++++++++++++++++++++++--------------------------- command/upload.py | 41 +++++++++++++++------- core.py | 1 - dist.py | 7 ++-- tests/test_dist.py | 47 -------------------------- 5 files changed, 80 insertions(+), 113 deletions(-) diff --git a/command/register.py b/command/register.py index a9ab361a..5c588eff 100644 --- a/command/register.py +++ b/command/register.py @@ -8,29 +8,37 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" import os, string, urllib2, getpass, urlparse -import StringIO +import StringIO, ConfigParser -from distutils.core import PyPIRCCommand +from distutils.core import Command from distutils.errors import * -from distutils import log -class register(PyPIRCCommand): +class register(Command): description = ("register the distribution with the Python package index") - user_options = PyPIRCCommand.user_options + [ + + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]"%DEFAULT_REPOSITORY), ('list-classifiers', None, 'list the valid Trove classifiers'), + ('show-response', None, + 'display full response text from server'), ] - boolean_options = PyPIRCCommand.boolean_options + [ - 'verify', 'list-classifiers'] + boolean_options = ['verify', 'show-response', 'list-classifiers'] def initialize_options(self): - PyPIRCCommand.initialize_options(self) + self.repository = None + self.show_response = 0 self.list_classifiers = 0 + def finalize_options(self): + if self.repository is None: + self.repository = self.DEFAULT_REPOSITORY + def run(self): - self.finalize_options() - self._set_config() self.check_metadata() if self.dry_run: self.verify_metadata() @@ -69,23 +77,6 @@ class register(PyPIRCCommand): "or (maintainer and maintainer_email) " + "must be supplied") - def _set_config(self): - ''' Reads the configuration file and set attributes. - ''' - config = self._read_pypirc() - if config != {}: - self.username = config['username'] - self.password = config['password'] - self.repository = config['repository'] - self.realm = config['realm'] - self.has_config = True - else: - if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): - raise ValueError('%s not found in .pypirc' % self.repository) - if self.repository == 'pypi': - self.repository = self.DEFAULT_REPOSITORY - self.has_config = False - def classifiers(self): ''' Fetch the list of classifiers from the server. ''' @@ -99,7 +90,6 @@ class register(PyPIRCCommand): (code, result) = self.post_to_server(self.build_post_data('verify')) print 'Server response (%s): %s'%(code, result) - def send_metadata(self): ''' Send the metadata to the package index server. @@ -109,14 +99,10 @@ class register(PyPIRCCommand): First we try to read the username/password from $HOME/.pypirc, which is a ConfigParser-formatted file with a section - [distutils] containing username and password entries (both + [server-login] containing username and password entries (both in clear text). Eg: - [distutils] - index-servers = - pypi - - [pypi] + [server-login] username: fred password: sekrit @@ -128,15 +114,21 @@ class register(PyPIRCCommand): 3. set the password to a random string and email the user. ''' + choice = 'x' + username = password = '' + # see if we can short-cut and get the username/password from the # config - if self.has_config: - choice = '1' - username = self.username - password = self.password - else: - choice = 'x' - username = password = '' + config = None + if 'HOME' in os.environ: + rc = os.path.join(os.environ['HOME'], '.pypirc') + if os.path.exists(rc): + print 'Using PyPI login from %s'%rc + config = ConfigParser.ConfigParser() + config.read(rc) + username = config.get('server-login', 'username') + password = config.get('server-login', 'password') + choice = '1' # get the user's login info choices = '1 2 3 4'.split() @@ -163,24 +155,32 @@ Your selection [default 1]: ''', # set up the authentication auth = urllib2.HTTPPasswordMgr() host = urlparse.urlparse(self.repository)[1] - auth.add_password(self.realm, host, username, password) + auth.add_password('pypi', host, username, password) + # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) - print 'Server response (%s): %s' % (code, result) + print 'Server response (%s): %s'%(code, result) # possibly save the login - if not self.has_config and code == 200: + if 'HOME' in os.environ and config is None and code == 200: + rc = os.path.join(os.environ['HOME'], '.pypirc') print 'I can store your PyPI login so future submissions will be faster.' - print '(the login will be stored in %s)' % self._get_rc_file() + print '(the login will be stored in %s)'%rc choice = 'X' while choice.lower() not in 'yn': choice = raw_input('Save your login (y/N)?') if not choice: choice = 'n' if choice.lower() == 'y': - self._store_pypirc(username, password) - + f = open(rc, 'w') + f.write('[server-login]\nusername:%s\npassword:%s\n'%( + username, password)) + f.close() + try: + os.chmod(rc, 0600) + except: + pass elif choice == '2': data = {':action': 'user'} data['name'] = data['password'] = data['email'] = '' @@ -243,8 +243,7 @@ Your selection [default 1]: ''', def post_to_server(self, data, auth=None): ''' Post a query to the server, and return a string response. ''' - self.announce('Registering %s to %s' % (data['name'], - self.repository), log.INFO) + # Build up the MIME payload for the urllib2 POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary diff --git a/command/upload.py b/command/upload.py index daf68112..301a1595 100644 --- a/command/upload.py +++ b/command/upload.py @@ -3,7 +3,7 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" from distutils.errors import * -from distutils.core import PyPIRCCommand +from distutils.core import Command from distutils.spawn import spawn from distutils import log from hashlib import md5 @@ -16,38 +16,53 @@ import base64 import urlparse import cStringIO as StringIO -class upload(PyPIRCCommand): +class upload(Command): description = "upload binary package to PyPI" - user_options = PyPIRCCommand.user_options + [ + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server'), ('sign', 's', 'sign files to upload using gpg'), ('identity=', 'i', 'GPG identity used to sign files'), ] - - boolean_options = PyPIRCCommand.boolean_options + ['sign'] + boolean_options = ['show-response', 'sign'] def initialize_options(self): - PyPIRCCommand.initialize_options(self) self.username = '' self.password = '' + self.repository = '' self.show_response = 0 self.sign = False self.identity = None def finalize_options(self): - PyPIRCCommand.finalize_options(self) if self.identity and not self.sign: raise DistutilsOptionError( "Must use --sign for --identity to have meaning" ) - config = self._read_pypirc() - if config != {}: - self.username = config['username'] - self.password = config['password'] - self.repository = config['repository'] - self.realm = config['realm'] + if 'HOME' in os.environ: + rc = os.path.join(os.environ['HOME'], '.pypirc') + if os.path.exists(rc): + self.announce('Using PyPI login from %s' % rc) + config = ConfigParser.ConfigParser({ + 'username':'', + 'password':'', + 'repository':''}) + config.read(rc) + if not self.repository: + self.repository = config.get('server-login', 'repository') + if not self.username: + self.username = config.get('server-login', 'username') + if not self.password: + self.password = config.get('server-login', 'password') + if not self.repository: + self.repository = self.DEFAULT_REPOSITORY def run(self): if not self.distribution.dist_files: diff --git a/core.py b/core.py index de9ce7d7..32b90269 100644 --- a/core.py +++ b/core.py @@ -20,7 +20,6 @@ from distutils.util import grok_environment_error # Mainly import these so setup scripts can "from distutils.core import" them. from distutils.dist import Distribution from distutils.cmd import Command -from distutils.config import PyPIRCCommand from distutils.extension import Extension # This is a barebones help message generated displayed when the user diff --git a/dist.py b/dist.py index 0b13c1e6..d098cb97 100644 --- a/dist.py +++ b/dist.py @@ -343,9 +343,10 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - user_file = os.path.join(os.path.expanduser('~'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) + if 'HOME' in os.environ: + user_file = os.path.join(os.environ.get('HOME'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" diff --git a/tests/test_dist.py b/tests/test_dist.py index 28572637..5ae79332 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -55,7 +55,6 @@ class DistributionTestCase(unittest.TestCase): self.assertEqual(d.get_command_packages(), ["distutils.command"]) def test_command_packages_cmdline(self): - from distutils.tests.test_dist import test_dist sys.argv.extend(["--command-packages", "foo.bar,distutils.tests", "test_dist", @@ -66,7 +65,6 @@ class DistributionTestCase(unittest.TestCase): self.assertEqual(d.get_command_packages(), ["distutils.command", "foo.bar", "distutils.tests"]) cmd = d.get_command_obj("test_dist") - print cmd.__class__, test_dist self.assert_(isinstance(cmd, test_dist)) self.assertEqual(cmd.sample_option, "sometext") @@ -181,54 +179,9 @@ class MetadataTestCase(unittest.TestCase): dist.metadata.write_pkg_file(sio) return sio.getvalue() - def test_custom_pydistutils(self): - # fixes #2166 - # make sure pydistutils.cfg is found - old = {} - for env in ('HOME', 'HOMEPATH', 'HOMEDRIVE'): - value = os.environ.get(env) - old[env] = value - if value is not None: - del os.environ[env] - - if os.name == 'posix': - user_filename = ".pydistutils.cfg" - else: - user_filename = "pydistutils.cfg" - - curdir = os.path.dirname(__file__) - user_filename = os.path.join(curdir, user_filename) - f = open(user_filename, 'w') - f.write('.') - f.close() - - try: - dist = distutils.dist.Distribution() - - # linux-style - if sys.platform in ('linux', 'darwin'): - os.environ['HOME'] = curdir - files = dist.find_config_files() - self.assert_(user_filename in files) - - # win32-style - if sys.platform == 'win32': - # home drive should be found - os.environ['HOMEPATH'] = curdir - files = dist.find_config_files() - self.assert_(user_filename in files) - finally: - for key, value in old.items(): - if value is None: - continue - os.environ[key] = value - os.remove(user_filename) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) suite.addTest(unittest.makeSuite(MetadataTestCase)) return suite - -if __name__ == "__main__": - unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From a76f1b7559518a73f906030946db7d12ea21fddc Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 10 May 2008 22:12:38 +0000 Subject: #1858: add distutils.config module --- config.py | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 config.py diff --git a/config.py b/config.py new file mode 100644 index 00000000..cf547698 --- /dev/null +++ b/config.py @@ -0,0 +1,124 @@ +"""distutils.pypirc + +Provides the PyPIRCCommand class, the base class for the command classes +that uses .pypirc in the distutils.command package. +""" +import os +import sys +from ConfigParser import ConfigParser + +from distutils.core import Command + +DEFAULT_PYPIRC = """\ +[pypirc] +servers = + pypi + +[pypi] +username:%s +password:%s +""" + +class PyPIRCCommand(Command): + """Base command that knows how to handle the .pypirc file + """ + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' + DEFAULT_REALM = 'pypi' + repository = None + realm = None + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % \ + DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server')] + + boolean_options = ['show-response'] + + def _get_rc_file(self): + """Returns rc file path.""" + return os.path.join(os.path.expanduser('~'), '.pypirc') + + def _store_pypirc(self, username, password): + """Creates a default .pypirc file.""" + rc = self._get_rc_file() + f = open(rc, 'w') + try: + f.write(DEFAULT_PYPIRC % (username, password)) + finally: + f.close() + try: + os.chmod(rc, 0600) + except OSError: + # should do something better here + pass + + def _read_pypirc(self): + """Reads the .pypirc file.""" + rc = self._get_rc_file() + if os.path.exists(rc): + print 'Using PyPI login from %s' % rc + repository = self.repository or self.DEFAULT_REPOSITORY + realm = self.realm or self.DEFAULT_REALM + + config = ConfigParser() + config.read(rc) + sections = config.sections() + if 'distutils' in sections: + # let's get the list of servers + index_servers = config.get('distutils', 'index-servers') + _servers = [server.strip() for server in + index_servers.split('\n') + if server.strip() != ''] + if _servers == []: + # nothing set, let's try to get the default pypi + if 'pypi' in sections: + _servers = ['pypi'] + else: + # the file is not properly defined, returning + # an empty dict + return {} + for server in _servers: + current = {'server': server} + current['username'] = config.get(server, 'username') + current['password'] = config.get(server, 'password') + + # optional params + for key, default in (('repository', + self.DEFAULT_REPOSITORY), + ('realm', self.DEFAULT_REALM)): + if config.has_option(server, key): + current[key] = config.get(server, key) + else: + current[key] = default + if (current['server'] == repository or + current['repository'] == repository): + return current + elif 'server-login' in sections: + # old format + server = 'server-login' + if config.has_option(server, 'repository'): + repository = config.get(server, 'repository') + else: + repository = self.DEFAULT_REPOSITORY + return {'username': config.get(server, 'username'), + 'password': config.get(server, 'password'), + 'repository': repository, + 'server': server, + 'realm': self.DEFAULT_REALM} + + return {} + + def initialize_options(self): + """Initialize options.""" + self.repository = None + self.realm = None + self.show_response = 0 + + def finalize_options(self): + """Finalizes options.""" + if self.repository is None: + self.repository = self.DEFAULT_REPOSITORY + if self.realm is None: + self.realm = self.DEFAULT_REALM -- cgit v1.2.1 From 650146240e3815b6cee42887e1919baf4077de40 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sun, 11 May 2008 14:00:00 +0000 Subject: #1858: re-apply patch for this, adding the missing files --- command/register.py | 97 ++++++++++++++++++++++++----------------------- command/upload.py | 41 +++++++------------- core.py | 1 + dist.py | 7 ++-- tests/test_config.py | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_dist.py | 46 ++++++++++++++++++++++ tests/test_upload.py | 34 +++++++++++++++++ 7 files changed, 251 insertions(+), 80 deletions(-) create mode 100644 tests/test_config.py create mode 100644 tests/test_upload.py diff --git a/command/register.py b/command/register.py index 5c588eff..a9ab361a 100644 --- a/command/register.py +++ b/command/register.py @@ -8,37 +8,29 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" import os, string, urllib2, getpass, urlparse -import StringIO, ConfigParser +import StringIO -from distutils.core import Command +from distutils.core import PyPIRCCommand from distutils.errors import * +from distutils import log -class register(Command): +class register(PyPIRCCommand): description = ("register the distribution with the Python package index") - - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]"%DEFAULT_REPOSITORY), + user_options = PyPIRCCommand.user_options + [ ('list-classifiers', None, 'list the valid Trove classifiers'), - ('show-response', None, - 'display full response text from server'), ] - boolean_options = ['verify', 'show-response', 'list-classifiers'] + boolean_options = PyPIRCCommand.boolean_options + [ + 'verify', 'list-classifiers'] def initialize_options(self): - self.repository = None - self.show_response = 0 + PyPIRCCommand.initialize_options(self) self.list_classifiers = 0 - def finalize_options(self): - if self.repository is None: - self.repository = self.DEFAULT_REPOSITORY - def run(self): + self.finalize_options() + self._set_config() self.check_metadata() if self.dry_run: self.verify_metadata() @@ -77,6 +69,23 @@ class register(Command): "or (maintainer and maintainer_email) " + "must be supplied") + def _set_config(self): + ''' Reads the configuration file and set attributes. + ''' + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + self.has_config = True + else: + if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): + raise ValueError('%s not found in .pypirc' % self.repository) + if self.repository == 'pypi': + self.repository = self.DEFAULT_REPOSITORY + self.has_config = False + def classifiers(self): ''' Fetch the list of classifiers from the server. ''' @@ -90,6 +99,7 @@ class register(Command): (code, result) = self.post_to_server(self.build_post_data('verify')) print 'Server response (%s): %s'%(code, result) + def send_metadata(self): ''' Send the metadata to the package index server. @@ -99,10 +109,14 @@ class register(Command): First we try to read the username/password from $HOME/.pypirc, which is a ConfigParser-formatted file with a section - [server-login] containing username and password entries (both + [distutils] containing username and password entries (both in clear text). Eg: - [server-login] + [distutils] + index-servers = + pypi + + [pypi] username: fred password: sekrit @@ -114,21 +128,15 @@ class register(Command): 3. set the password to a random string and email the user. ''' - choice = 'x' - username = password = '' - # see if we can short-cut and get the username/password from the # config - config = None - if 'HOME' in os.environ: - rc = os.path.join(os.environ['HOME'], '.pypirc') - if os.path.exists(rc): - print 'Using PyPI login from %s'%rc - config = ConfigParser.ConfigParser() - config.read(rc) - username = config.get('server-login', 'username') - password = config.get('server-login', 'password') - choice = '1' + if self.has_config: + choice = '1' + username = self.username + password = self.password + else: + choice = 'x' + username = password = '' # get the user's login info choices = '1 2 3 4'.split() @@ -155,32 +163,24 @@ Your selection [default 1]: ''', # set up the authentication auth = urllib2.HTTPPasswordMgr() host = urlparse.urlparse(self.repository)[1] - auth.add_password('pypi', host, username, password) - + auth.add_password(self.realm, host, username, password) # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) - print 'Server response (%s): %s'%(code, result) + print 'Server response (%s): %s' % (code, result) # possibly save the login - if 'HOME' in os.environ and config is None and code == 200: - rc = os.path.join(os.environ['HOME'], '.pypirc') + if not self.has_config and code == 200: print 'I can store your PyPI login so future submissions will be faster.' - print '(the login will be stored in %s)'%rc + print '(the login will be stored in %s)' % self._get_rc_file() choice = 'X' while choice.lower() not in 'yn': choice = raw_input('Save your login (y/N)?') if not choice: choice = 'n' if choice.lower() == 'y': - f = open(rc, 'w') - f.write('[server-login]\nusername:%s\npassword:%s\n'%( - username, password)) - f.close() - try: - os.chmod(rc, 0600) - except: - pass + self._store_pypirc(username, password) + elif choice == '2': data = {':action': 'user'} data['name'] = data['password'] = data['email'] = '' @@ -243,7 +243,8 @@ Your selection [default 1]: ''', def post_to_server(self, data, auth=None): ''' Post a query to the server, and return a string response. ''' - + self.announce('Registering %s to %s' % (data['name'], + self.repository), log.INFO) # Build up the MIME payload for the urllib2 POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary diff --git a/command/upload.py b/command/upload.py index 301a1595..daf68112 100644 --- a/command/upload.py +++ b/command/upload.py @@ -3,7 +3,7 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" from distutils.errors import * -from distutils.core import Command +from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log from hashlib import md5 @@ -16,53 +16,38 @@ import base64 import urlparse import cStringIO as StringIO -class upload(Command): +class upload(PyPIRCCommand): description = "upload binary package to PyPI" - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]" % DEFAULT_REPOSITORY), - ('show-response', None, - 'display full response text from server'), + user_options = PyPIRCCommand.user_options + [ ('sign', 's', 'sign files to upload using gpg'), ('identity=', 'i', 'GPG identity used to sign files'), ] - boolean_options = ['show-response', 'sign'] + + boolean_options = PyPIRCCommand.boolean_options + ['sign'] def initialize_options(self): + PyPIRCCommand.initialize_options(self) self.username = '' self.password = '' - self.repository = '' self.show_response = 0 self.sign = False self.identity = None def finalize_options(self): + PyPIRCCommand.finalize_options(self) if self.identity and not self.sign: raise DistutilsOptionError( "Must use --sign for --identity to have meaning" ) - if 'HOME' in os.environ: - rc = os.path.join(os.environ['HOME'], '.pypirc') - if os.path.exists(rc): - self.announce('Using PyPI login from %s' % rc) - config = ConfigParser.ConfigParser({ - 'username':'', - 'password':'', - 'repository':''}) - config.read(rc) - if not self.repository: - self.repository = config.get('server-login', 'repository') - if not self.username: - self.username = config.get('server-login', 'username') - if not self.password: - self.password = config.get('server-login', 'password') - if not self.repository: - self.repository = self.DEFAULT_REPOSITORY + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] def run(self): if not self.distribution.dist_files: diff --git a/core.py b/core.py index 32b90269..de9ce7d7 100644 --- a/core.py +++ b/core.py @@ -20,6 +20,7 @@ from distutils.util import grok_environment_error # Mainly import these so setup scripts can "from distutils.core import" them. from distutils.dist import Distribution from distutils.cmd import Command +from distutils.config import PyPIRCCommand from distutils.extension import Extension # This is a barebones help message generated displayed when the user diff --git a/dist.py b/dist.py index d098cb97..0b13c1e6 100644 --- a/dist.py +++ b/dist.py @@ -343,10 +343,9 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - if 'HOME' in os.environ: - user_file = os.path.join(os.environ.get('HOME'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) + user_file = os.path.join(os.path.expanduser('~'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 00000000..bfce3e3e --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,105 @@ +"""Tests for distutils.pypirc.pypirc.""" +import sys +import os +import unittest + +from distutils.core import PyPIRCCommand +from distutils.core import Distribution + +from distutils.tests import support + +PYPIRC = """\ +[distutils] + +index-servers = + server1 + server2 + +[server1] +username:me +password:secret + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ +""" + +PYPIRC_OLD = """\ +[server-login] +username:tarek +password:secret +""" + +class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): + + def setUp(self): + """Patches the environment.""" + if os.environ.has_key('HOME'): + self._old_home = os.environ['HOME'] + else: + self._old_home = None + curdir = os.path.dirname(__file__) + os.environ['HOME'] = curdir + self.rc = os.path.join(curdir, '.pypirc') + self.dist = Distribution() + + class command(PyPIRCCommand): + def __init__(self, dist): + PyPIRCCommand.__init__(self, dist) + def initialize_options(self): + pass + finalize_options = initialize_options + + self._cmd = command + + def tearDown(self): + """Removes the patch.""" + if self._old_home is None: + del os.environ['HOME'] + else: + os.environ['HOME'] = self._old_home + if os.path.exists(self.rc): + os.remove(self.rc) + + def test_server_registration(self): + # This test makes sure PyPIRCCommand knows how to: + # 1. handle several sections in .pypirc + # 2. handle the old format + + # new format + f = open(self.rc, 'w') + try: + f.write(PYPIRC) + finally: + f.close() + + cmd = self._cmd(self.dist) + config = cmd._read_pypirc() + + config = config.items() + config.sort() + waited = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server1'), ('username', 'me')] + self.assertEquals(config, waited) + + # old format + f = open(self.rc, 'w') + f.write(PYPIRC_OLD) + f.close() + + config = cmd._read_pypirc() + config = config.items() + config.sort() + waited = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server-login'), ('username', 'tarek')] + self.assertEquals(config, waited) + +def test_suite(): + return unittest.makeSuite(PyPIRCCommandTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_dist.py b/tests/test_dist.py index 5ae79332..804c8a46 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -55,6 +55,7 @@ class DistributionTestCase(unittest.TestCase): self.assertEqual(d.get_command_packages(), ["distutils.command"]) def test_command_packages_cmdline(self): + from distutils.tests.test_dist import test_dist sys.argv.extend(["--command-packages", "foo.bar,distutils.tests", "test_dist", @@ -179,9 +180,54 @@ class MetadataTestCase(unittest.TestCase): dist.metadata.write_pkg_file(sio) return sio.getvalue() + def test_custom_pydistutils(self): + # fixes #2166 + # make sure pydistutils.cfg is found + old = {} + for env in ('HOME', 'HOMEPATH', 'HOMEDRIVE'): + value = os.environ.get(env) + old[env] = value + if value is not None: + del os.environ[env] + + if os.name == 'posix': + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + + curdir = os.path.dirname(__file__) + user_filename = os.path.join(curdir, user_filename) + f = open(user_filename, 'w') + f.write('.') + f.close() + + try: + dist = distutils.dist.Distribution() + + # linux-style + if sys.platform in ('linux', 'darwin'): + os.environ['HOME'] = curdir + files = dist.find_config_files() + self.assert_(user_filename in files) + + # win32-style + if sys.platform == 'win32': + # home drive should be found + os.environ['HOMEPATH'] = curdir + files = dist.find_config_files() + self.assert_(user_filename in files) + finally: + for key, value in old.items(): + if value is None: + continue + os.environ[key] = value + os.remove(user_filename) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) suite.addTest(unittest.makeSuite(MetadataTestCase)) return suite + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_upload.py b/tests/test_upload.py new file mode 100644 index 00000000..b05ab1f7 --- /dev/null +++ b/tests/test_upload.py @@ -0,0 +1,34 @@ +"""Tests for distutils.command.upload.""" +import sys +import os +import unittest + +from distutils.command.upload import upload +from distutils.core import Distribution + +from distutils.tests import support +from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase + +class uploadTestCase(PyPIRCCommandTestCase): + + def test_finalize_options(self): + + # new format + f = open(self.rc, 'w') + f.write(PYPIRC) + f.close() + + dist = Distribution() + cmd = upload(dist) + cmd.finalize_options() + for attr, waited in (('username', 'me'), ('password', 'secret'), + ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi')): + self.assertEquals(getattr(cmd, attr), waited) + + +def test_suite(): + return unittest.makeSuite(uploadTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 9dd1c5afa5e07e7da296484f3508464e9622a5bf Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sun, 11 May 2008 19:15:52 +0000 Subject: Add message to test assertion --- tests/test_dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index 804c8a46..05342af8 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -215,7 +215,8 @@ class MetadataTestCase(unittest.TestCase): # home drive should be found os.environ['HOMEPATH'] = curdir files = dist.find_config_files() - self.assert_(user_filename in files) + self.assert_(user_filename in files, + '%r not found in %r' % (user_filename, files)) finally: for key, value in old.items(): if value is None: -- cgit v1.2.1 From 34cbcc09ce17524bcef6b344b93a8968f2c34582 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sun, 11 May 2008 20:08:33 +0000 Subject: Try setting HOME env.var to fix test on Win32 --- tests/test_dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index 05342af8..8f1288e9 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -213,7 +213,7 @@ class MetadataTestCase(unittest.TestCase): # win32-style if sys.platform == 'win32': # home drive should be found - os.environ['HOMEPATH'] = curdir + os.environ['HOME'] = curdir files = dist.find_config_files() self.assert_(user_filename in files, '%r not found in %r' % (user_filename, files)) -- cgit v1.2.1 From 2a4ac571e1458d6871960e7fc687eaba5b6b778d Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Wed, 14 May 2008 22:44:22 +0000 Subject: Updated import statements to use the new `configparser` module name. Updated the documentation to use the new name. Revert addition of the stub entry for the old name. Georg, I am reverting your changes since this commit should propagate to py3k. --- command/upload.py | 2 +- config.py | 2 +- dist.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/command/upload.py b/command/upload.py index daf68112..ecc06f05 100644 --- a/command/upload.py +++ b/command/upload.py @@ -10,7 +10,7 @@ from hashlib import md5 import os import socket import platform -import ConfigParser +import configparser import httplib import base64 import urlparse diff --git a/config.py b/config.py index cf547698..e9ba4026 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,7 @@ that uses .pypirc in the distutils.command package. """ import os import sys -from ConfigParser import ConfigParser +from configparser import ConfigParser from distutils.core import Command diff --git a/dist.py b/dist.py index 0b13c1e6..e4ab3363 100644 --- a/dist.py +++ b/dist.py @@ -359,7 +359,7 @@ Common commands: (see '--help-commands' for more) def parse_config_files (self, filenames=None): - from ConfigParser import ConfigParser + from configparser import ConfigParser if filenames is None: filenames = self.find_config_files() -- cgit v1.2.1 From 9285f7c07f1c115cfbf61b15b00925f1ea4be940 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Wed, 14 May 2008 22:59:42 +0000 Subject: Renamed ConfigParser to configparser. Merged revisions 63247-63248 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r63247 | georg.brandl | 2008-05-14 18:30:31 -0400 (Wed, 14 May 2008) | 2 lines Update configparser docs for lowercasing rename. ........ r63248 | alexandre.vassalotti | 2008-05-14 18:44:22 -0400 (Wed, 14 May 2008) | 8 lines Updated import statements to use the new `configparser` module name. Updated the documentation to use the new name. Revert addition of the stub entry for the old name. Georg, I am reverting your changes since this commit should propagate to py3k. ........ --- command/upload.py | 2 +- dist.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/upload.py b/command/upload.py index 34b6692d..23999ae9 100644 --- a/command/upload.py +++ b/command/upload.py @@ -10,7 +10,7 @@ from hashlib import md5 import os import socket import platform -import ConfigParser +import configparser import httplib import base64 import urlparse diff --git a/dist.py b/dist.py index ade2ab79..847eb902 100644 --- a/dist.py +++ b/dist.py @@ -349,7 +349,7 @@ Common commands: (see '--help-commands' for more) def parse_config_files (self, filenames=None): - from ConfigParser import ConfigParser + from configparser import ConfigParser if filenames is None: filenames = self.find_config_files() -- cgit v1.2.1 From 04c09e98e06c0b9467e8f22c52d2fe052fd505e1 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Thu, 15 May 2008 00:33:57 +0000 Subject: Revert distutils changes done in r63248. As explained by Marc-Andre Lemburg, distutils needs to stay backward-compatible. Therefore, it should use the old ConfigParser module name. --- command/upload.py | 2 +- config.py | 2 +- dist.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/command/upload.py b/command/upload.py index ecc06f05..daf68112 100644 --- a/command/upload.py +++ b/command/upload.py @@ -10,7 +10,7 @@ from hashlib import md5 import os import socket import platform -import configparser +import ConfigParser import httplib import base64 import urlparse diff --git a/config.py b/config.py index e9ba4026..cf547698 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,7 @@ that uses .pypirc in the distutils.command package. """ import os import sys -from configparser import ConfigParser +from ConfigParser import ConfigParser from distutils.core import Command diff --git a/dist.py b/dist.py index e4ab3363..0b13c1e6 100644 --- a/dist.py +++ b/dist.py @@ -359,7 +359,7 @@ Common commands: (see '--help-commands' for more) def parse_config_files (self, filenames=None): - from configparser import ConfigParser + from ConfigParser import ConfigParser if filenames is None: filenames = self.find_config_files() -- cgit v1.2.1 From 7af87d550e429290a39a66350c57fd30141080f1 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Thu, 15 May 2008 02:14:05 +0000 Subject: Fixed configparser import in distutils.config. --- config.py | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 config.py diff --git a/config.py b/config.py new file mode 100644 index 00000000..e9ba4026 --- /dev/null +++ b/config.py @@ -0,0 +1,124 @@ +"""distutils.pypirc + +Provides the PyPIRCCommand class, the base class for the command classes +that uses .pypirc in the distutils.command package. +""" +import os +import sys +from configparser import ConfigParser + +from distutils.core import Command + +DEFAULT_PYPIRC = """\ +[pypirc] +servers = + pypi + +[pypi] +username:%s +password:%s +""" + +class PyPIRCCommand(Command): + """Base command that knows how to handle the .pypirc file + """ + DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' + DEFAULT_REALM = 'pypi' + repository = None + realm = None + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % \ + DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server')] + + boolean_options = ['show-response'] + + def _get_rc_file(self): + """Returns rc file path.""" + return os.path.join(os.path.expanduser('~'), '.pypirc') + + def _store_pypirc(self, username, password): + """Creates a default .pypirc file.""" + rc = self._get_rc_file() + f = open(rc, 'w') + try: + f.write(DEFAULT_PYPIRC % (username, password)) + finally: + f.close() + try: + os.chmod(rc, 0600) + except OSError: + # should do something better here + pass + + def _read_pypirc(self): + """Reads the .pypirc file.""" + rc = self._get_rc_file() + if os.path.exists(rc): + print 'Using PyPI login from %s' % rc + repository = self.repository or self.DEFAULT_REPOSITORY + realm = self.realm or self.DEFAULT_REALM + + config = ConfigParser() + config.read(rc) + sections = config.sections() + if 'distutils' in sections: + # let's get the list of servers + index_servers = config.get('distutils', 'index-servers') + _servers = [server.strip() for server in + index_servers.split('\n') + if server.strip() != ''] + if _servers == []: + # nothing set, let's try to get the default pypi + if 'pypi' in sections: + _servers = ['pypi'] + else: + # the file is not properly defined, returning + # an empty dict + return {} + for server in _servers: + current = {'server': server} + current['username'] = config.get(server, 'username') + current['password'] = config.get(server, 'password') + + # optional params + for key, default in (('repository', + self.DEFAULT_REPOSITORY), + ('realm', self.DEFAULT_REALM)): + if config.has_option(server, key): + current[key] = config.get(server, key) + else: + current[key] = default + if (current['server'] == repository or + current['repository'] == repository): + return current + elif 'server-login' in sections: + # old format + server = 'server-login' + if config.has_option(server, 'repository'): + repository = config.get(server, 'repository') + else: + repository = self.DEFAULT_REPOSITORY + return {'username': config.get(server, 'username'), + 'password': config.get(server, 'password'), + 'repository': repository, + 'server': server, + 'realm': self.DEFAULT_REALM} + + return {} + + def initialize_options(self): + """Initialize options.""" + self.repository = None + self.realm = None + self.show_response = 0 + + def finalize_options(self): + """Finalizes options.""" + if self.repository is None: + self.repository = self.DEFAULT_REPOSITORY + if self.realm is None: + self.realm = self.DEFAULT_REALM -- cgit v1.2.1 From 1ea453fe09439490fd90dff0675db494b5b82b86 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 15 May 2008 20:06:51 +0000 Subject: Use lowercase version of module name --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index cf547698..e9ba4026 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,7 @@ that uses .pypirc in the distutils.command package. """ import os import sys -from ConfigParser import ConfigParser +from configparser import ConfigParser from distutils.core import Command -- cgit v1.2.1 From f543720ad42bf00329d322a6ae72f69a9e3d90b4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 15 May 2008 20:07:39 +0000 Subject: Import class from distutils.cmd, not .core, to avoid circular import --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index e9ba4026..edba47f5 100644 --- a/config.py +++ b/config.py @@ -7,7 +7,7 @@ import os import sys from configparser import ConfigParser -from distutils.core import Command +from distutils.cmd import Command DEFAULT_PYPIRC = """\ [pypirc] -- cgit v1.2.1 From c926aa0be33963fb9fe9c776acf77df7a25770c1 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Thu, 15 May 2008 20:13:54 +0000 Subject: Fixed another missed ConfigParser import rename. --- command/register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/register.py b/command/register.py index 40d9f20d..b6a36f5b 100644 --- a/command/register.py +++ b/command/register.py @@ -8,7 +8,7 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" import os, string, urllib2, getpass, urlparse -import io, ConfigParser +import io, configparser from distutils.core import Command from distutils.errors import * -- cgit v1.2.1 From a2b4da6548ce557e87cbc6a40bf312fb308d4d4b Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Thu, 15 May 2008 20:30:56 +0000 Subject: Revert r63323: Use lowercase version of module name. The distutils module should continue to use the old ConfigParser name, for backward-compatibility. --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index edba47f5..f1117bee 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,7 @@ that uses .pypirc in the distutils.command package. """ import os import sys -from configparser import ConfigParser +from ConfigParser import ConfigParser from distutils.cmd import Command -- cgit v1.2.1 From 67495c271aba3864b15d2e9f2fab5cb7c775edc0 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Thu, 15 May 2008 22:09:29 +0000 Subject: Merged revisions 62914-62916,62918-62919,62921-62922,62924-62942,62944-62945,62947-62949 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r62914 | skip.montanaro | 2008-05-08 20:45:00 -0400 (Thu, 08 May 2008) | 4 lines Add an example about using NamedTemporaryFile() to replace mktemp(). I'm unclear whether the verbatim text should have been indented or by how much. ........ r62915 | benjamin.peterson | 2008-05-08 20:50:40 -0400 (Thu, 08 May 2008) | 2 lines reindent example ........ r62927 | georg.brandl | 2008-05-09 02:09:25 -0400 (Fri, 09 May 2008) | 2 lines #2788: add .hgignore file. ........ r62928 | georg.brandl | 2008-05-09 02:10:43 -0400 (Fri, 09 May 2008) | 2 lines #2781: fix function name. ........ r62929 | georg.brandl | 2008-05-09 02:18:27 -0400 (Fri, 09 May 2008) | 2 lines Add a sentence to basicConfig() that is in the docstring. ........ r62930 | georg.brandl | 2008-05-09 02:26:54 -0400 (Fri, 09 May 2008) | 2 lines Add another link to colorsys docs. ........ r62931 | georg.brandl | 2008-05-09 02:36:07 -0400 (Fri, 09 May 2008) | 2 lines Add Kodos as a re reference. ........ r62932 | georg.brandl | 2008-05-09 02:39:58 -0400 (Fri, 09 May 2008) | 2 lines Add a note about using reload(). ........ r62933 | andrew.kuchling | 2008-05-09 07:46:05 -0400 (Fri, 09 May 2008) | 3 lines Update planned release date. Uncomment PEP 370 section. Add some module items ........ r62934 | christian.heimes | 2008-05-09 08:19:09 -0400 (Fri, 09 May 2008) | 1 line Add --user option to build_ext ........ r62948 | mark.dickinson | 2008-05-09 13:54:23 -0400 (Fri, 09 May 2008) | 3 lines Issue #2487. math.ldexp(x, n) raised OverflowError when n was large and negative; fix to return an (appropriately signed) zero instead. ........ r62949 | martin.v.loewis | 2008-05-09 14:21:55 -0400 (Fri, 09 May 2008) | 1 line Use the CHM file name that Sphinx assigns. ........ --- command/build_ext.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index e0112198..73cc00ba 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -7,6 +7,7 @@ extensions ASAP).""" __revision__ = "$Id$" import sys, os, re +from site import USER_BASE, USER_SITE from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version @@ -90,9 +91,11 @@ class build_ext(Command): "list of SWIG command line options"), ('swig=', None, "path to the SWIG executable"), + ('user', None, + "add user include, library and rpath"), ] - boolean_options = ['inplace', 'debug', 'force', 'swig-cpp'] + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] help_options = [ ('help-compiler', None, @@ -120,6 +123,7 @@ class build_ext(Command): self.swig = None self.swig_cpp = None self.swig_opts = None + self.user = None def finalize_options(self): from distutils import sysconfig @@ -253,6 +257,16 @@ class build_ext(Command): else: self.swig_opts = self.swig_opts.split(' ') + # Finally add the user include and library directories if requested + if self.user: + user_include = os.path.join(USER_BASE, "include") + user_lib = os.path.join(USER_BASE, "lib") + if os.path.isdir(user_include): + self.include_dirs.append(user_include) + if os.path.isdir(user_lib): + self.library_dirs.append(user_lib) + self.rpath.append(user_lib) + def run(self): from distutils.ccompiler import new_compiler -- cgit v1.2.1 From f71e81f1f9cf18ae21fe3e17c4ac17e83b3ef403 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Fri, 16 May 2008 00:03:33 +0000 Subject: Merged revisions 62998-63003,63005-63006,63009-63012,63014-63017,63019-63020,63022-63024,63026-63029,63031-63041,63043-63045,63047-63054,63056-63062 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r62998 | andrew.kuchling | 2008-05-10 15:51:55 -0400 (Sat, 10 May 2008) | 7 lines #1858 from Tarek Ziade: Allow multiple repositories in .pypirc; see http://wiki.python.org/moin/EnhancedPyPI for discussion. The patch is slightly revised from Tarek's last patch: I've simplified the PyPIRCCommand.finalize_options() method to not look at sys.argv. Tests still pass. ........ r63000 | alexandre.vassalotti | 2008-05-10 15:59:16 -0400 (Sat, 10 May 2008) | 5 lines Cleaned up io._BytesIO.write(). I am amazed that the old code, for inserting null-bytes, actually worked. Who wrote that thing? Oh, it is me... doh. ........ r63002 | brett.cannon | 2008-05-10 16:52:01 -0400 (Sat, 10 May 2008) | 2 lines Revert r62998 as it broke the build (seems distutils.config is missing). ........ r63014 | andrew.kuchling | 2008-05-10 18:12:38 -0400 (Sat, 10 May 2008) | 1 line #1858: add distutils.config module ........ r63027 | brett.cannon | 2008-05-10 21:09:32 -0400 (Sat, 10 May 2008) | 2 lines Flesh out the 3.0 deprecation to suggest using the ctypes module. ........ r63028 | skip.montanaro | 2008-05-10 22:59:30 -0400 (Sat, 10 May 2008) | 4 lines Copied two versions of the example from the interactive session. Delete one. ........ r63037 | georg.brandl | 2008-05-11 03:02:17 -0400 (Sun, 11 May 2008) | 2 lines reload() takes the module itself. ........ r63038 | alexandre.vassalotti | 2008-05-11 03:06:04 -0400 (Sun, 11 May 2008) | 4 lines Added test framework for handling module renames. Factored the import guard in test_py3kwarn.TestStdlibRemovals into a context manager, namely test_support.CleanImport. ........ r63039 | georg.brandl | 2008-05-11 03:06:05 -0400 (Sun, 11 May 2008) | 2 lines #2742: ``''`` is not converted to NULL in getaddrinfo. ........ r63040 | alexandre.vassalotti | 2008-05-11 03:08:12 -0400 (Sun, 11 May 2008) | 2 lines Fixed typo in a comment of test_support.CleanImport. ........ r63041 | alexandre.vassalotti | 2008-05-11 03:10:25 -0400 (Sun, 11 May 2008) | 2 lines Removed a dead line of code. ........ r63043 | georg.brandl | 2008-05-11 04:47:53 -0400 (Sun, 11 May 2008) | 2 lines #2812: document property.getter/setter/deleter. ........ r63049 | georg.brandl | 2008-05-11 05:06:30 -0400 (Sun, 11 May 2008) | 2 lines #1153769: document PEP 237 changes to string formatting. ........ r63050 | georg.brandl | 2008-05-11 05:11:40 -0400 (Sun, 11 May 2008) | 2 lines #2809: elaborate str.split docstring a bit. ........ r63051 | georg.brandl | 2008-05-11 06:13:59 -0400 (Sun, 11 May 2008) | 2 lines Fix typo. ........ r63052 | georg.brandl | 2008-05-11 06:33:27 -0400 (Sun, 11 May 2008) | 2 lines #2709: clarification. ........ r63053 | georg.brandl | 2008-05-11 06:42:28 -0400 (Sun, 11 May 2008) | 2 lines #2659: add ``break_on_hyphens`` to TextWrapper. ........ r63057 | georg.brandl | 2008-05-11 06:59:39 -0400 (Sun, 11 May 2008) | 2 lines #2741: clarification of value range for address_family. ........ r63058 | georg.brandl | 2008-05-11 07:09:35 -0400 (Sun, 11 May 2008) | 2 lines #2452: timeout is used for all blocking operations. ........ r63059 | andrew.kuchling | 2008-05-11 09:33:56 -0400 (Sun, 11 May 2008) | 2 lines #1792: Improve performance of marshal.dumps() on large objects by increasing the size of the buffer more quickly. ........ r63060 | andrew.kuchling | 2008-05-11 10:00:00 -0400 (Sun, 11 May 2008) | 1 line #1858: re-apply patch for this, adding the missing files ........ r63061 | benjamin.peterson | 2008-05-11 10:13:25 -0400 (Sun, 11 May 2008) | 2 lines Add the "until" command to pdb ........ r63062 | georg.brandl | 2008-05-11 10:17:13 -0400 (Sun, 11 May 2008) | 2 lines Add some sentence endings. ........ --- command/register.py | 95 ++++++++++++++++++++++++----------------------- command/upload.py | 41 +++++++------------- config.py | 4 +- core.py | 1 + dist.py | 7 ++-- tests/test_config.py | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_dist.py | 46 +++++++++++++++++++++++ tests/test_upload.py | 34 +++++++++++++++++ 8 files changed, 250 insertions(+), 81 deletions(-) create mode 100644 tests/test_config.py create mode 100644 tests/test_upload.py diff --git a/command/register.py b/command/register.py index b6a36f5b..89cb2d41 100644 --- a/command/register.py +++ b/command/register.py @@ -8,42 +8,34 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" import os, string, urllib2, getpass, urlparse -import io, configparser +import io -from distutils.core import Command +from distutils.core import PyPIRCCommand from distutils.errors import * +from distutils import log def raw_input(prompt): sys.stdout.write(prompt) sys.stdout.flush() return sys.stdin.readline() -class register(Command): +class register(PyPIRCCommand): description = ("register the distribution with the Python package index") - - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]"%DEFAULT_REPOSITORY), + user_options = PyPIRCCommand.user_options + [ ('list-classifiers', None, 'list the valid Trove classifiers'), - ('show-response', None, - 'display full response text from server'), ] - boolean_options = ['verify', 'show-response', 'list-classifiers'] + boolean_options = PyPIRCCommand.boolean_options + [ + 'verify', 'list-classifiers'] def initialize_options(self): - self.repository = None - self.show_response = 0 + PyPIRCCommand.initialize_options(self) self.list_classifiers = 0 - def finalize_options(self): - if self.repository is None: - self.repository = self.DEFAULT_REPOSITORY - def run(self): + self.finalize_options() + self._set_config() self.check_metadata() if self.dry_run: self.verify_metadata() @@ -82,6 +74,23 @@ class register(Command): "or (maintainer and maintainer_email) " + "must be supplied") + def _set_config(self): + ''' Reads the configuration file and set attributes. + ''' + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + self.has_config = True + else: + if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): + raise ValueError('%s not found in .pypirc' % self.repository) + if self.repository == 'pypi': + self.repository = self.DEFAULT_REPOSITORY + self.has_config = False + def classifiers(self): ''' Fetch the list of classifiers from the server. ''' @@ -95,6 +104,7 @@ class register(Command): (code, result) = self.post_to_server(self.build_post_data('verify')) print('Server response (%s): %s'%(code, result)) + def send_metadata(self): ''' Send the metadata to the package index server. @@ -104,10 +114,14 @@ class register(Command): First we try to read the username/password from $HOME/.pypirc, which is a ConfigParser-formatted file with a section - [server-login] containing username and password entries (both + [distutils] containing username and password entries (both in clear text). Eg: - [server-login] + [distutils] + index-servers = + pypi + + [pypi] username: fred password: sekrit @@ -119,21 +133,15 @@ class register(Command): 3. set the password to a random string and email the user. ''' - choice = 'x' - username = password = '' - # see if we can short-cut and get the username/password from the # config - config = None - if 'HOME' in os.environ: - rc = os.path.join(os.environ['HOME'], '.pypirc') - if os.path.exists(rc): - print('Using PyPI login from %s'%rc) - config = ConfigParser.ConfigParser() - config.read(rc) - username = config.get('server-login', 'username') - password = config.get('server-login', 'password') - choice = '1' + if self.has_config: + choice = '1' + username = self.username + password = self.password + else: + choice = 'x' + username = password = '' # get the user's login info choices = '1 2 3 4'.split() @@ -160,32 +168,24 @@ Your selection [default 1]: ''', end=' ') # set up the authentication auth = urllib2.HTTPPasswordMgr() host = urlparse.urlparse(self.repository)[1] - auth.add_password('pypi', host, username, password) - + auth.add_password(self.realm, host, username, password) # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) print('Server response (%s): %s'%(code, result)) # possibly save the login - if 'HOME' in os.environ and config is None and code == 200: - rc = os.path.join(os.environ['HOME'], '.pypirc') + if not self.has_config and code == 200: print('I can store your PyPI login so future submissions will be faster.') - print('(the login will be stored in %s)'%rc) + print('(the login will be stored in %s)' % self._get_rc_file()) choice = 'X' while choice.lower() not in 'yn': choice = raw_input('Save your login (y/N)?') if not choice: choice = 'n' if choice.lower() == 'y': - f = open(rc, 'w') - f.write('[server-login]\nusername:%s\npassword:%s\n'%( - username, password)) - f.close() - try: - os.chmod(rc, 0o600) - except: - pass + self._store_pypirc(username, password) + elif choice == '2': data = {':action': 'user'} data['name'] = data['password'] = data['email'] = '' @@ -248,7 +248,8 @@ Your selection [default 1]: ''', end=' ') def post_to_server(self, data, auth=None): ''' Post a query to the server, and return a string response. ''' - + self.announce('Registering %s to %s' % (data['name'], + self.repository), log.INFO) # Build up the MIME payload for the urllib2 POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary diff --git a/command/upload.py b/command/upload.py index 23999ae9..603336c9 100644 --- a/command/upload.py +++ b/command/upload.py @@ -3,7 +3,7 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" from distutils.errors import * -from distutils.core import Command +from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log from hashlib import md5 @@ -15,53 +15,38 @@ import httplib import base64 import urlparse -class upload(Command): +class upload(PyPIRCCommand): description = "upload binary package to PyPI" - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]" % DEFAULT_REPOSITORY), - ('show-response', None, - 'display full response text from server'), + user_options = PyPIRCCommand.user_options + [ ('sign', 's', 'sign files to upload using gpg'), ('identity=', 'i', 'GPG identity used to sign files'), ] - boolean_options = ['show-response', 'sign'] + + boolean_options = PyPIRCCommand.boolean_options + ['sign'] def initialize_options(self): + PyPIRCCommand.initialize_options(self) self.username = '' self.password = '' - self.repository = '' self.show_response = 0 self.sign = False self.identity = None def finalize_options(self): + PyPIRCCommand.finalize_options(self) if self.identity and not self.sign: raise DistutilsOptionError( "Must use --sign for --identity to have meaning" ) - if 'HOME' in os.environ: - rc = os.path.join(os.environ['HOME'], '.pypirc') - if os.path.exists(rc): - self.announce('Using PyPI login from %s' % rc) - config = ConfigParser.ConfigParser({ - 'username':'', - 'password':'', - 'repository':''}) - config.read(rc) - if not self.repository: - self.repository = config.get('server-login', 'repository') - if not self.username: - self.username = config.get('server-login', 'username') - if not self.password: - self.password = config.get('server-login', 'password') - if not self.repository: - self.repository = self.DEFAULT_REPOSITORY + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] def run(self): if not self.distribution.dist_files: diff --git a/config.py b/config.py index e9ba4026..a625aec5 100644 --- a/config.py +++ b/config.py @@ -49,7 +49,7 @@ class PyPIRCCommand(Command): finally: f.close() try: - os.chmod(rc, 0600) + os.chmod(rc, 0o600) except OSError: # should do something better here pass @@ -58,7 +58,7 @@ class PyPIRCCommand(Command): """Reads the .pypirc file.""" rc = self._get_rc_file() if os.path.exists(rc): - print 'Using PyPI login from %s' % rc + print('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY realm = self.realm or self.DEFAULT_REALM diff --git a/core.py b/core.py index a4c5e180..6e489203 100644 --- a/core.py +++ b/core.py @@ -17,6 +17,7 @@ from distutils.util import grok_environment_error # Mainly import these so setup scripts can "from distutils.core import" them. from distutils.dist import Distribution from distutils.cmd import Command +from distutils.config import PyPIRCCommand from distutils.extension import Extension # This is a barebones help message generated displayed when the user diff --git a/dist.py b/dist.py index 847eb902..ddde909f 100644 --- a/dist.py +++ b/dist.py @@ -334,10 +334,9 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - if 'HOME' in os.environ: - user_file = os.path.join(os.environ.get('HOME'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) + user_file = os.path.join(os.path.expanduser('~'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 00000000..016ba4cc --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,103 @@ +"""Tests for distutils.pypirc.pypirc.""" +import sys +import os +import unittest + +from distutils.core import PyPIRCCommand +from distutils.core import Distribution + +from distutils.tests import support + +PYPIRC = """\ +[distutils] + +index-servers = + server1 + server2 + +[server1] +username:me +password:secret + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ +""" + +PYPIRC_OLD = """\ +[server-login] +username:tarek +password:secret +""" + +class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): + + def setUp(self): + """Patches the environment.""" + if 'HOME' in os.environ: + self._old_home = os.environ['HOME'] + else: + self._old_home = None + curdir = os.path.dirname(__file__) + os.environ['HOME'] = curdir + self.rc = os.path.join(curdir, '.pypirc') + self.dist = Distribution() + + class command(PyPIRCCommand): + def __init__(self, dist): + PyPIRCCommand.__init__(self, dist) + def initialize_options(self): + pass + finalize_options = initialize_options + + self._cmd = command + + def tearDown(self): + """Removes the patch.""" + if self._old_home is None: + del os.environ['HOME'] + else: + os.environ['HOME'] = self._old_home + if os.path.exists(self.rc): + os.remove(self.rc) + + def test_server_registration(self): + # This test makes sure PyPIRCCommand knows how to: + # 1. handle several sections in .pypirc + # 2. handle the old format + + # new format + f = open(self.rc, 'w') + try: + f.write(PYPIRC) + finally: + f.close() + + cmd = self._cmd(self.dist) + config = cmd._read_pypirc() + + config = list(sorted(config.items())) + waited = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server1'), ('username', 'me')] + self.assertEquals(config, waited) + + # old format + f = open(self.rc, 'w') + f.write(PYPIRC_OLD) + f.close() + + config = cmd._read_pypirc() + config = list(sorted(config.items())) + waited = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server-login'), ('username', 'tarek')] + self.assertEquals(config, waited) + +def test_suite(): + return unittest.makeSuite(PyPIRCCommandTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_dist.py b/tests/test_dist.py index 91acf458..81459ace 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -55,6 +55,7 @@ class DistributionTestCase(unittest.TestCase): self.assertEqual(d.get_command_packages(), ["distutils.command"]) def test_command_packages_cmdline(self): + from distutils.tests.test_dist import test_dist sys.argv.extend(["--command-packages", "foo.bar,distutils.tests", "test_dist", @@ -179,9 +180,54 @@ class MetadataTestCase(unittest.TestCase): dist.metadata.write_pkg_file(sio) return sio.getvalue() + def test_custom_pydistutils(self): + # fixes #2166 + # make sure pydistutils.cfg is found + old = {} + for env in ('HOME', 'HOMEPATH', 'HOMEDRIVE'): + value = os.environ.get(env) + old[env] = value + if value is not None: + del os.environ[env] + + if os.name == 'posix': + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + + curdir = os.path.dirname(__file__) + user_filename = os.path.join(curdir, user_filename) + f = open(user_filename, 'w') + f.write('.') + f.close() + + try: + dist = distutils.dist.Distribution() + + # linux-style + if sys.platform in ('linux', 'darwin'): + os.environ['HOME'] = curdir + files = dist.find_config_files() + self.assert_(user_filename in files) + + # win32-style + if sys.platform == 'win32': + # home drive should be found + os.environ['HOMEPATH'] = curdir + files = dist.find_config_files() + self.assert_(user_filename in files) + finally: + for key, value in old.items(): + if value is None: + continue + os.environ[key] = value + os.remove(user_filename) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) suite.addTest(unittest.makeSuite(MetadataTestCase)) return suite + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_upload.py b/tests/test_upload.py new file mode 100644 index 00000000..b05ab1f7 --- /dev/null +++ b/tests/test_upload.py @@ -0,0 +1,34 @@ +"""Tests for distutils.command.upload.""" +import sys +import os +import unittest + +from distutils.command.upload import upload +from distutils.core import Distribution + +from distutils.tests import support +from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase + +class uploadTestCase(PyPIRCCommandTestCase): + + def test_finalize_options(self): + + # new format + f = open(self.rc, 'w') + f.write(PYPIRC) + f.close() + + dist = Distribution() + cmd = upload(dist) + cmd.finalize_options() + for attr, waited in (('username', 'me'), ('password', 'secret'), + ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi')): + self.assertEquals(getattr(cmd, attr), waited) + + +def test_suite(): + return unittest.makeSuite(uploadTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 17a42fcbc36865bdf006384e396f968312103464 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Fri, 16 May 2008 00:41:41 +0000 Subject: Merged revisions 63066-63076,63079,63081-63085,63087-63097,63099,63101-63104 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r63066 | georg.brandl | 2008-05-11 10:56:04 -0400 (Sun, 11 May 2008) | 2 lines #2709 followup: better description of Tk's pros and cons. ........ r63067 | georg.brandl | 2008-05-11 11:05:13 -0400 (Sun, 11 May 2008) | 2 lines #1326: document and test zipimporter.archive and zipimporter.prefix. ........ r63068 | georg.brandl | 2008-05-11 11:07:39 -0400 (Sun, 11 May 2008) | 2 lines #2816: clarify error messages for EOF while scanning strings. ........ r63069 | georg.brandl | 2008-05-11 11:17:41 -0400 (Sun, 11 May 2008) | 3 lines #2787: Flush stdout after writing test name, helpful when running hanging or long-running tests. Patch by Adam Olsen. ........ r63070 | georg.brandl | 2008-05-11 11:20:16 -0400 (Sun, 11 May 2008) | 3 lines #2803: fix wrong invocation of heappush in seldom-reached code. Thanks to Matt Harden. ........ r63073 | benjamin.peterson | 2008-05-11 12:38:07 -0400 (Sun, 11 May 2008) | 2 lines broaden .bzrignore ........ r63076 | andrew.kuchling | 2008-05-11 15:15:52 -0400 (Sun, 11 May 2008) | 1 line Add message to test assertion ........ r63083 | andrew.kuchling | 2008-05-11 16:08:33 -0400 (Sun, 11 May 2008) | 1 line Try setting HOME env.var to fix test on Win32 ........ r63092 | georg.brandl | 2008-05-11 16:53:55 -0400 (Sun, 11 May 2008) | 2 lines #2809 followup: even better split docstring. ........ r63094 | georg.brandl | 2008-05-11 17:03:42 -0400 (Sun, 11 May 2008) | 4 lines - #2250: Exceptions raised during evaluation of names in rlcompleter's ``Completer.complete()`` method are now caught and ignored. ........ r63095 | georg.brandl | 2008-05-11 17:16:37 -0400 (Sun, 11 May 2008) | 2 lines Clarify os.strerror()s exception behavior. ........ r63097 | georg.brandl | 2008-05-11 17:34:10 -0400 (Sun, 11 May 2008) | 2 lines #2535: remove duplicated method. ........ r63104 | alexandre.vassalotti | 2008-05-11 19:04:27 -0400 (Sun, 11 May 2008) | 2 lines Moved the Queue module stub in lib-old. ........ --- tests/test_dist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index 81459ace..dd077350 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -213,9 +213,10 @@ class MetadataTestCase(unittest.TestCase): # win32-style if sys.platform == 'win32': # home drive should be found - os.environ['HOMEPATH'] = curdir + os.environ['HOME'] = curdir files = dist.find_config_files() - self.assert_(user_filename in files) + self.assert_(user_filename in files, + '%r not found in %r' % (user_filename, files)) finally: for key, value in old.items(): if value is None: -- cgit v1.2.1 From 372161eb00ab6befb04dd3a2a85d38238efd5b0e Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Fri, 16 May 2008 02:06:59 +0000 Subject: Fixed import of configparser in the distutils module. If configparser is unavailable, try to import configparser using its old name. This is required for backward-compatibility with older Python versions. --- command/upload.py | 7 ++++++- config.py | 6 +++++- dist.py | 7 +++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/command/upload.py b/command/upload.py index daf68112..92c4bf20 100644 --- a/command/upload.py +++ b/command/upload.py @@ -10,11 +10,16 @@ from hashlib import md5 import os import socket import platform -import ConfigParser import httplib import base64 import urlparse import cStringIO as StringIO +try: + from configparser import ConfigParser +except ImportError: + # For backward-compatibility with Python versions < 2.6. + from ConfigParser import ConfigParser + class upload(PyPIRCCommand): diff --git a/config.py b/config.py index f1117bee..35a21ec2 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,11 @@ that uses .pypirc in the distutils.command package. """ import os import sys -from ConfigParser import ConfigParser +try: + from configparser import ConfigParser +except ImportError: + # For backward-compatibility with Python versions < 2.6. + from ConfigParser import ConfigParser from distutils.cmd import Command diff --git a/dist.py b/dist.py index 0b13c1e6..6299919f 100644 --- a/dist.py +++ b/dist.py @@ -358,8 +358,11 @@ Common commands: (see '--help-commands' for more) def parse_config_files (self, filenames=None): - - from ConfigParser import ConfigParser + try: + from configparser import ConfigParser + except ImportError: + # For backward-compatibility with Python versions < 2.6. + from ConfigParser import ConfigParser if filenames is None: filenames = self.find_config_files() -- cgit v1.2.1 From fbd52989678335fca9cbf0007209bd04513036eb Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Fri, 16 May 2008 04:39:54 +0000 Subject: Merged revisions 63208-63209,63211-63212,63214-63217,63219-63224,63226-63227,63229-63232,63234-63235,63237-63239,63241,63243-63246,63250-63254,63256-63259,63261,63263-63264,63266-63267,63269-63270,63272-63273,63275-63276,63278,63280-63281,63283-63284,63286-63287,63289-63290,63292-63293,63295-63296,63298-63299,63301-63302,63304-63305,63307,63309-63314,63316-63322,63324-63325,63327-63335,63337-63338,63340-63342,63344-63346,63348 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r63208 | georg.brandl | 2008-05-13 15:04:54 -0400 (Tue, 13 May 2008) | 2 lines #2831: add start argument to enumerate(). Patch by Scott Dial and me. ........ r63209 | marc-andre.lemburg | 2008-05-13 15:10:45 -0400 (Tue, 13 May 2008) | 3 lines Remove leftovers from reverted setuptools checkin (they were added in r45525). ........ r63211 | georg.brandl | 2008-05-13 17:32:03 -0400 (Tue, 13 May 2008) | 2 lines Fix a refleak in the _warnings module. ........ r63212 | andrew.kuchling | 2008-05-13 20:46:41 -0400 (Tue, 13 May 2008) | 1 line List all the removes and renamed modules ........ r63214 | brett.cannon | 2008-05-13 21:09:40 -0400 (Tue, 13 May 2008) | 2 lines Rewrap some lines in test_py3kwarn. ........ r63219 | georg.brandl | 2008-05-14 02:34:15 -0400 (Wed, 14 May 2008) | 2 lines Add NEWS entry for #2831. ........ r63220 | neal.norwitz | 2008-05-14 02:47:56 -0400 (Wed, 14 May 2008) | 3 lines Fix "refleak" by restoring the tearDown method removed by accident (AFAICT) in r62788. ........ r63221 | georg.brandl | 2008-05-14 03:18:22 -0400 (Wed, 14 May 2008) | 2 lines Fix another "refleak" by clearing the filters after test. ........ r63222 | neal.norwitz | 2008-05-14 03:21:42 -0400 (Wed, 14 May 2008) | 5 lines Install the json package and tests as well as the lib2to3 tests so the tests work when run from an install directory. They are currently skipped on the daily runs (not from the buildbots) for checking refleaks, etc. ........ r63256 | andrew.kuchling | 2008-05-14 21:10:24 -0400 (Wed, 14 May 2008) | 1 line Note some removals and a rename ........ r63311 | brett.cannon | 2008-05-15 00:36:53 -0400 (Thu, 15 May 2008) | 2 lines Add a snippet for the deprecation directive for docs. ........ r63313 | gregory.p.smith | 2008-05-15 00:56:18 -0400 (Thu, 15 May 2008) | 5 lines disable the crashing test. I will also file a bug. This crash does not appear to be a new bug, its just that the test coverage went up recently exposing it. (I verified that by testing this test code on an older Modules/_bsddb.c) ........ r63320 | georg.brandl | 2008-05-15 11:08:32 -0400 (Thu, 15 May 2008) | 2 lines #2863: add gen.__name__ and add this name to generator repr(). ........ r63324 | andrew.kuchling | 2008-05-15 16:07:39 -0400 (Thu, 15 May 2008) | 1 line Import class from distutils.cmd, not .core, to avoid circular import ........ r63327 | alexandre.vassalotti | 2008-05-15 16:31:42 -0400 (Thu, 15 May 2008) | 2 lines Fixed typo in a doctest of test_genexps. ........ r63332 | benjamin.peterson | 2008-05-15 18:34:33 -0400 (Thu, 15 May 2008) | 2 lines add Mac modules to the list of deprecated ones ........ r63333 | benjamin.peterson | 2008-05-15 18:41:16 -0400 (Thu, 15 May 2008) | 2 lines fix typos in whatsnew ........ r63348 | benjamin.peterson | 2008-05-15 22:24:49 -0400 (Thu, 15 May 2008) | 2 lines make test_platform a bit more assertive (We'll see what the buildbots say.) ........ --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index a625aec5..6ffccaa0 100644 --- a/config.py +++ b/config.py @@ -7,7 +7,7 @@ import os import sys from configparser import ConfigParser -from distutils.core import Command +from distutils.cmd import Command DEFAULT_PYPIRC = """\ [pypirc] -- cgit v1.2.1 From 9c132229971073925b69493eb88596556c7a658c Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 18 May 2008 11:52:36 +0000 Subject: GHOP #257: test distutils' build_ext command, written by Josip Dzolonga. --- tests/test_build_ext.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/test_build_ext.py diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py new file mode 100644 index 00000000..b5c2aa23 --- /dev/null +++ b/tests/test_build_ext.py @@ -0,0 +1,72 @@ +import sys +import os +import tempfile +import shutil +from StringIO import StringIO + +from distutils.core import Extension, Distribution +from distutils.command.build_ext import build_ext +from distutils import sysconfig + +import unittest +from test import test_support + +class BuildExtTestCase(unittest.TestCase): + def setUp(self): + # Create a simple test environment + # Note that we're making changes to sys.path + self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") + self.sys_path = sys.path[:] + sys.path.append(self.tmp_dir) + + xx_c = os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') + shutil.copy(xx_c, self.tmp_dir) + + def test_build_ext(self): + xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') + xx_ext = Extension('xx', [xx_c]) + dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) + dist.package_dir = self.tmp_dir + cmd = build_ext(dist) + cmd.build_lib = self.tmp_dir + cmd.build_temp = self.tmp_dir + + old_stdout = sys.stdout + if not test_support.verbose: + # silence compiler output + sys.stdout = StringIO() + try: + cmd.ensure_finalized() + cmd.run() + finally: + sys.stdout = old_stdout + + import xx + + for attr in ('error', 'foo', 'new', 'roj'): + self.assert_(hasattr(xx, attr)) + + self.assertEquals(xx.foo(2, 5), 7) + self.assertEquals(xx.foo(13,15), 28) + self.assertEquals(xx.new().demo(), None) + doc = 'This is a template module just for instruction.' + self.assertEquals(xx.__doc__, doc) + self.assert_(isinstance(xx.Null(), xx.Null)) + self.assert_(isinstance(xx.Str(), xx.Str)) + + def tearDown(self): + # Get everything back to normal + test_support.unload('xx') + sys.path = self.sys_path + # XXX on Windows the test leaves a directory with xx.pyd in TEMP + shutil.rmtree(self.tmp_dir, False if os.name != "nt" else True) + +def test_suite(): + if not sysconfig.python_build: + if test_support.verbose: + print 'test_build_ext: The test must be run in a python build dir' + return unittest.TestSuite() + else: return unittest.makeSuite(BuildExtTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 0c96212a691e860e15ae057f59b5dab114d90c90 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 20 May 2008 21:35:26 +0000 Subject: #2621 rename test.test_support to test.support --- tests/test_core.py | 10 +++++----- tests/test_dist.py | 2 +- tests/test_sysconfig.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 8e274dd4..170d7675 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5,7 +5,7 @@ import distutils.core import os import shutil import sys -import test.test_support +import test.support import unittest @@ -39,13 +39,13 @@ class CoreTestCase(unittest.TestCase): self.cleanup_testfn() def cleanup_testfn(self): - path = test.test_support.TESTFN + path = test.support.TESTFN if os.path.isfile(path): os.remove(path) elif os.path.isdir(path): shutil.rmtree(path) - def write_setup(self, text, path=test.test_support.TESTFN): + def write_setup(self, text, path=test.support.TESTFN): open(path, "w").write(text) return path @@ -63,8 +63,8 @@ class CoreTestCase(unittest.TestCase): cwd = os.getcwd() # Create a directory and write the setup.py file there: - os.mkdir(test.test_support.TESTFN) - setup_py = os.path.join(test.test_support.TESTFN, "setup.py") + os.mkdir(test.support.TESTFN) + setup_py = os.path.join(test.support.TESTFN, "setup.py") distutils.core.run_setup( self.write_setup(setup_prints_cwd, path=setup_py)) diff --git a/tests/test_dist.py b/tests/test_dist.py index dd077350..f1b11c17 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -7,7 +7,7 @@ import io import sys import unittest -from test.test_support import TESTFN +from test.support import TESTFN class test_dist(distutils.cmd.Command): diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index aa1187e7..c6ab9aa5 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -4,7 +4,7 @@ from distutils import sysconfig import os import unittest -from test.test_support import TESTFN +from test.support import TESTFN class SysconfigTestCase(unittest.TestCase): -- cgit v1.2.1 From ca303a98aa60ae1678585125caeb983e27a2b096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 24 May 2008 09:00:04 +0000 Subject: Use announce instead of print, to suppress output in the testsuite. --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index 35a21ec2..e07f8ac9 100644 --- a/config.py +++ b/config.py @@ -62,7 +62,7 @@ class PyPIRCCommand(Command): """Reads the .pypirc file.""" rc = self._get_rc_file() if os.path.exists(rc): - print 'Using PyPI login from %s' % rc + self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY realm = self.realm or self.DEFAULT_REALM -- cgit v1.2.1 From f21c3f4abd01a3a0e8d94b71aa43e817ccebca98 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 25 May 2008 07:25:25 +0000 Subject: ConfigParser renaming reversal part 3: move module into place and adapt imports. --- command/upload.py | 6 +----- config.py | 6 +----- dist.py | 6 +----- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/command/upload.py b/command/upload.py index 92c4bf20..8805d41d 100644 --- a/command/upload.py +++ b/command/upload.py @@ -14,11 +14,7 @@ import httplib import base64 import urlparse import cStringIO as StringIO -try: - from configparser import ConfigParser -except ImportError: - # For backward-compatibility with Python versions < 2.6. - from ConfigParser import ConfigParser +from ConfigParser import ConfigParser class upload(PyPIRCCommand): diff --git a/config.py b/config.py index e07f8ac9..e3a4c57e 100644 --- a/config.py +++ b/config.py @@ -5,11 +5,7 @@ that uses .pypirc in the distutils.command package. """ import os import sys -try: - from configparser import ConfigParser -except ImportError: - # For backward-compatibility with Python versions < 2.6. - from ConfigParser import ConfigParser +from ConfigParser import ConfigParser from distutils.cmd import Command diff --git a/dist.py b/dist.py index 6299919f..0a213809 100644 --- a/dist.py +++ b/dist.py @@ -358,11 +358,7 @@ Common commands: (see '--help-commands' for more) def parse_config_files (self, filenames=None): - try: - from configparser import ConfigParser - except ImportError: - # For backward-compatibility with Python versions < 2.6. - from ConfigParser import ConfigParser + from ConfigParser import ConfigParser if filenames is None: filenames = self.find_config_files() -- cgit v1.2.1 From 281cb13d4994a8d61b79b5d0fbc26eb3e2b29d23 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 25 May 2008 07:45:51 +0000 Subject: #2879: rename _winreg to winreg. --- msvc9compiler.py | 18 +++++++++--------- msvccompiler.py | 14 +++++++------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index c8d52c42..fdb74aea 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -24,17 +24,17 @@ from distutils.ccompiler import (CCompiler, gen_preprocess_options, from distutils import log from distutils.util import get_platform -import _winreg +import winreg -RegOpenKeyEx = _winreg.OpenKeyEx -RegEnumKey = _winreg.EnumKey -RegEnumValue = _winreg.EnumValue -RegError = _winreg.error +RegOpenKeyEx = winreg.OpenKeyEx +RegEnumKey = winreg.EnumKey +RegEnumValue = winreg.EnumValue +RegError = winreg.error -HKEYS = (_winreg.HKEY_USERS, - _winreg.HKEY_CURRENT_USER, - _winreg.HKEY_LOCAL_MACHINE, - _winreg.HKEY_CLASSES_ROOT) +HKEYS = (winreg.HKEY_USERS, + winreg.HKEY_CURRENT_USER, + winreg.HKEY_LOCAL_MACHINE, + winreg.HKEY_CLASSES_ROOT) VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" diff --git a/msvccompiler.py b/msvccompiler.py index 71146dcf..1cd0f91d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -20,15 +20,15 @@ from distutils import log _can_read_reg = False try: - import _winreg + import winreg _can_read_reg = True - hkey_mod = _winreg + hkey_mod = winreg - RegOpenKeyEx = _winreg.OpenKeyEx - RegEnumKey = _winreg.EnumKey - RegEnumValue = _winreg.EnumValue - RegError = _winreg.error + RegOpenKeyEx = winreg.OpenKeyEx + RegEnumKey = winreg.EnumKey + RegEnumValue = winreg.EnumValue + RegError = winreg.error except ImportError: try: @@ -44,7 +44,7 @@ except ImportError: except ImportError: log.info("Warning: Can't read registry to find the " "necessary compiler setting\n" - "Make sure that Python modules _winreg, " + "Make sure that Python modules winreg, " "win32api or win32con are installed.") pass -- cgit v1.2.1 From b923b203a06f4768e4793c212c10ab2594784536 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 25 May 2008 18:19:30 +0000 Subject: Merged revisions 63412,63445-63447,63449-63450,63452,63454,63459,63463,63465,63470,63483-63484,63496-63497,63499-63501,63530-63531,63540,63614 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r63412 | georg.brandl | 2008-05-17 19:57:01 +0200 (Sat, 17 May 2008) | 2 lines #961805: fix Edit.text_modified(). ........ r63445 | georg.brandl | 2008-05-18 10:52:59 +0200 (Sun, 18 May 2008) | 2 lines GHOP #180 by Michael Schneider: add examples to the socketserver documentation. ........ r63446 | georg.brandl | 2008-05-18 11:12:20 +0200 (Sun, 18 May 2008) | 2 lines GHOP #134, #171, #137: unit tests for the three HTTPServer modules. ........ r63447 | georg.brandl | 2008-05-18 12:39:26 +0200 (Sun, 18 May 2008) | 3 lines Take namedtuple item names only from ascii_letters (this blew up on OSX), and make sure there are no duplicate names. ........ r63449 | georg.brandl | 2008-05-18 13:46:51 +0200 (Sun, 18 May 2008) | 2 lines GHOP #217: add support for compiling Python with coverage checking enabled. ........ r63450 | georg.brandl | 2008-05-18 13:52:36 +0200 (Sun, 18 May 2008) | 2 lines GHOP #257: test distutils' build_ext command, written by Josip Dzolonga. ........ r63452 | georg.brandl | 2008-05-18 15:34:06 +0200 (Sun, 18 May 2008) | 2 lines Add GHOP students. ........ r63454 | georg.brandl | 2008-05-18 18:32:48 +0200 (Sun, 18 May 2008) | 2 lines GHOP #121: improve test_pydoc, by Benjamin Peterson. ........ r63459 | benjamin.peterson | 2008-05-18 22:48:07 +0200 (Sun, 18 May 2008) | 2 lines bring test_pydoc up to my high standards (now that I have them) ........ r63463 | georg.brandl | 2008-05-18 23:10:19 +0200 (Sun, 18 May 2008) | 2 lines Fix test_pyclbr after another platform-dependent function was added to urllib. ........ r63465 | benjamin.peterson | 2008-05-19 01:07:07 +0200 (Mon, 19 May 2008) | 2 lines change some imports in tests so they will not be skipped in 3.0 ........ r63470 | georg.brandl | 2008-05-19 18:47:25 +0200 (Mon, 19 May 2008) | 2 lines test_httpservers has unpredictable refcount behavior. ........ r63483 | georg.brandl | 2008-05-20 08:15:36 +0200 (Tue, 20 May 2008) | 2 lines Activate two more test cases in test_httpservers. ........ r63484 | georg.brandl | 2008-05-20 08:47:31 +0200 (Tue, 20 May 2008) | 2 lines Argh, this is the *actual* test that works under Windows. ........ r63496 | georg.brandl | 2008-05-20 10:07:36 +0200 (Tue, 20 May 2008) | 2 lines Improve diffing logic and output for test_pydoc. ........ r63497 | georg.brandl | 2008-05-20 10:10:03 +0200 (Tue, 20 May 2008) | 2 lines Use inspect.getabsfile() to get the documented module's filename. ........ r63499 | georg.brandl | 2008-05-20 10:25:48 +0200 (Tue, 20 May 2008) | 3 lines Patch #1775025: allow opening zipfile members via ZipInfo instances. Patch by Graham Horler. ........ r63500 | georg.brandl | 2008-05-20 10:40:43 +0200 (Tue, 20 May 2008) | 2 lines #2592: delegate nb_index and the floor/truediv slots in weakref.proxy. ........ r63501 | georg.brandl | 2008-05-20 10:48:34 +0200 (Tue, 20 May 2008) | 2 lines #615772: raise a more explicit error from Tkinter.Misc.__contains__. ........ r63530 | benjamin.peterson | 2008-05-22 02:57:02 +0200 (Thu, 22 May 2008) | 2 lines use more specific asserts in test_opcode ........ r63531 | benjamin.peterson | 2008-05-22 03:02:23 +0200 (Thu, 22 May 2008) | 2 lines remove redundant invocation of json doctests ........ r63540 | benjamin.peterson | 2008-05-23 01:09:26 +0200 (Fri, 23 May 2008) | 3 lines fix test_pydoc so it works on make installed Python installations Also let it pass when invoked directly ........ r63614 | georg.brandl | 2008-05-25 10:07:37 +0200 (Sun, 25 May 2008) | 2 lines #2959: allow multiple close() calls for GzipFile. ........ --- tests/test_build_ext.py | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/test_build_ext.py diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py new file mode 100644 index 00000000..96c0a239 --- /dev/null +++ b/tests/test_build_ext.py @@ -0,0 +1,72 @@ +import sys +import os +import tempfile +import shutil +from io import StringIO + +from distutils.core import Extension, Distribution +from distutils.command.build_ext import build_ext +from distutils import sysconfig + +import unittest +from test import support + +class BuildExtTestCase(unittest.TestCase): + def setUp(self): + # Create a simple test environment + # Note that we're making changes to sys.path + self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") + self.sys_path = sys.path[:] + sys.path.append(self.tmp_dir) + + xx_c = os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') + shutil.copy(xx_c, self.tmp_dir) + + def test_build_ext(self): + xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') + xx_ext = Extension('xx', [xx_c]) + dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) + dist.package_dir = self.tmp_dir + cmd = build_ext(dist) + cmd.build_lib = self.tmp_dir + cmd.build_temp = self.tmp_dir + + old_stdout = sys.stdout + if not support.verbose: + # silence compiler output + sys.stdout = StringIO() + try: + cmd.ensure_finalized() + cmd.run() + finally: + sys.stdout = old_stdout + + import xx + + for attr in ('error', 'foo', 'new', 'roj'): + self.assert_(hasattr(xx, attr)) + + self.assertEquals(xx.foo(2, 5), 7) + self.assertEquals(xx.foo(13,15), 28) + self.assertEquals(xx.new().demo(), None) + doc = 'This is a template module just for instruction.' + self.assertEquals(xx.__doc__, doc) + self.assert_(isinstance(xx.Null(), xx.Null)) + self.assert_(isinstance(xx.Str(), xx.Str)) + + def tearDown(self): + # Get everything back to normal + support.unload('xx') + sys.path = self.sys_path + # XXX on Windows the test leaves a directory with xx.pyd in TEMP + shutil.rmtree(self.tmp_dir, False if os.name != "nt" else True) + +def test_suite(): + if not sysconfig.python_build: + if support.verbose: + print('test_build_ext: The test must be run in a python build dir') + return unittest.TestSuite() + else: return unittest.makeSuite(BuildExtTestCase) + +if __name__ == '__main__': + support.run_unittest(test_suite()) -- cgit v1.2.1 From 46e1390e226138292126a2ffb6a8e960ff570e85 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 26 May 2008 11:42:40 +0000 Subject: On Windows, we must build a debug version iff running a debug build of Python --- tests/test_build_ext.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index b5c2aa23..a658f1aa 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -28,6 +28,10 @@ class BuildExtTestCase(unittest.TestCase): dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir cmd = build_ext(dist) + if os.name == "nt": + # On Windows, we must build a debug version iff running + # a debug build of Python + cmd.debug = sys.executable.endswith("_d.exe") cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir -- cgit v1.2.1 From 882263289e030cef4305d310473f9d80401caa56 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 26 May 2008 11:51:44 +0000 Subject: Merged revisions 63670 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r63670 | thomas.heller | 2008-05-26 13:42:40 +0200 (Mo, 26 Mai 2008) | 4 lines On Windows, we must build a debug version iff running a debug build of Python ........ --- tests/test_build_ext.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 96c0a239..20c10745 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -28,6 +28,10 @@ class BuildExtTestCase(unittest.TestCase): dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir cmd = build_ext(dist) + if os.name == "nt": + # On Windows, we must build a debug version iff running + # a debug build of Python + cmd.debug = sys.executable.endswith("_d.exe") cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir -- cgit v1.2.1 From 208bfafeaaa2900a5bacd8534cc7b8f3bab0b947 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 26 May 2008 16:32:26 +0000 Subject: Create http package. #2883. --- command/upload.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/upload.py b/command/upload.py index 603336c9..2cad2c7b 100644 --- a/command/upload.py +++ b/command/upload.py @@ -11,7 +11,7 @@ import os import socket import platform import configparser -import httplib +import http.client import base64 import urlparse @@ -151,9 +151,9 @@ class upload(PyPIRCCommand): urlparse.urlparse(self.repository) assert not params and not query and not fragments if schema == 'http': - http = httplib.HTTPConnection(netloc) + http = http.client.HTTPConnection(netloc) elif schema == 'https': - http = httplib.HTTPSConnection(netloc) + http = http.client.HTTPSConnection(netloc) else: raise AssertionError("unsupported schema "+schema) -- cgit v1.2.1 From 52ba3bb9b2d0a33e082e139a2a2e48154dbf60e2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 26 May 2008 17:01:57 +0000 Subject: Merged revisions 63575 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r63575 | martin.v.loewis | 2008-05-24 11:00:04 +0200 (Sat, 24 May 2008) | 3 lines Use announce instead of print, to suppress output in the testsuite. ........ --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index 6ffccaa0..0ecfe0cc 100644 --- a/config.py +++ b/config.py @@ -58,7 +58,7 @@ class PyPIRCCommand(Command): """Reads the .pypirc file.""" rc = self._get_rc_file() if os.path.exists(rc): - print('Using PyPI login from %s' % rc) + self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY realm = self.realm or self.DEFAULT_REALM -- cgit v1.2.1 From 92a963ecf18661f11b3e8eea815b6a1bcb69cba3 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Wed, 28 May 2008 01:54:55 +0000 Subject: bdist_wininst now works correctly when both --skip-build and --plat-name are specified. --- command/bdist_wininst.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 7c43e745..f18e318c 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -79,6 +79,12 @@ class bdist_wininst (Command): def finalize_options (self): if self.bdist_dir is None: + if self.skip_build and self.plat_name: + # If build is skipped and plat_name is overridden, bdist will + # not see the correct 'plat_name' - so set that up manually. + bdist = self.distribution.get_command_obj('bdist') + bdist.plat_name = self.plat_name + # next the command will be initialized using that name bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'wininst') if not self.target_version: -- cgit v1.2.1 From 4416152bdd4d21c96ac792ecefe9db9239460e73 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Sat, 31 May 2008 05:11:07 +0000 Subject: Fix bdist_wininst --user-access-control for win2k --- command/wininst-6.0.exe | Bin 61440 -> 61440 bytes command/wininst-7.1.exe | Bin 61440 -> 65536 bytes command/wininst-9.0-amd64.exe | Bin 77312 -> 77824 bytes command/wininst-9.0.exe | Bin 66048 -> 66048 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-6.0.exe b/command/wininst-6.0.exe index 10c98199..f57c855a 100644 Binary files a/command/wininst-6.0.exe and b/command/wininst-6.0.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 6779aa8d..1433bc1a 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index b4cb062c..9dedfcdc 100644 Binary files a/command/wininst-9.0-amd64.exe and b/command/wininst-9.0-amd64.exe differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 0d04a667..9102ecdb 100644 Binary files a/command/wininst-9.0.exe and b/command/wininst-9.0.exe differ -- cgit v1.2.1 From dcd2eb88e377b96a037acf6da4729aafe95607d5 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 5 Jun 2008 12:58:24 +0000 Subject: MacOS X: Enable 4-way universal builds This patch adds a new configure argument on OSX: --with-universal-archs=[32-bit|64-bit|all] When used with the --enable-universalsdk option this controls which CPU architectures are includes in the framework. The default is 32-bit, meaning i386 and ppc. The most useful alternative is 'all', which includes all 4 CPU architectures supported by MacOS X (i386, ppc, x86_64 and ppc64). This includes limited support for the Carbon bindings in 64-bit mode as well, limited because (a) I haven't done extensive testing and (b) a large portion of the Carbon API's aren't available in 64-bit mode anyway. I've also duplicated a feature of Apple's build of python: setting the environment variable 'ARCHFLAGS' controls the '-arch' flags used for building extensions using distutils. --- sysconfig.py | 20 ++++++++++++++++++++ unixccompiler.py | 8 +++++++- util.py | 11 +++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 27a770b1..9993fba1 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -539,6 +539,26 @@ def get_config_vars(*args): flags = re.sub('-isysroot [^ \t]*', ' ', flags) _config_vars[key] = flags + else: + + # Allow the user to override the architecture flags using + # an environment variable. + # NOTE: This name was introduced by Apple in OSX 10.5 and + # is used by several scripting languages distributed with + # that OS release. + + if 'ARCHFLAGS' in os.environ: + arch = os.environ['ARCHFLAGS'] + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _config_vars[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = flags + ' ' + arch + _config_vars[key] = flags + if args: vals = [] for name in args: diff --git a/unixccompiler.py b/unixccompiler.py index dc759ee8..6144efc0 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -64,7 +64,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): stripArch = '-arch' in cc_args stripSysroot = '-isysroot' in cc_args - if stripArch: + if stripArch or 'ARCHFLAGS' in os.environ: while 1: try: index = compiler_so.index('-arch') @@ -73,6 +73,12 @@ def _darwin_compiler_fixup(compiler_so, cc_args): except ValueError: break + if 'ARCHFLAGS' in os.environ and not stripArch: + # User specified different -arch flags in the environ, + # see also distutils.sysconfig + compiler_so = compiler_so + ' ' + os.environ['ARCHFLAGS'] + + if stripSysroot: try: index = compiler_so.index('-isysroot') diff --git a/util.py b/util.py index 69d90cff..262a9b8b 100644 --- a/util.py +++ b/util.py @@ -125,12 +125,19 @@ def get_platform (): osname = "macosx" - if (release + '.') < '10.4.' and \ - get_config_vars().get('UNIVERSALSDK', '').strip(): + if (release + '.') >= '10.4.' and \ + '-arch' in get_config_vars().get('CFLAGS', '').strip(): # The universal build will build fat binaries, but not on # systems before 10.4 + # + # Try to detect 4-way universal builds, those have machine-type + # 'universal' instead of 'fat'. + machine = 'fat' + if '-arch x86_64' in get_config_vars().get('CFLAGS'): + machine = 'universal' + elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. machine = 'ppc' -- cgit v1.2.1 From ff2fd5ca8a081c349f525318bbe8e07c8f1fcc3f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 10 Jun 2008 16:57:31 +0000 Subject: Merged revisions 63724,63726,63732,63744,63754-63755,63757-63758,63760,63775,63781-63782,63787,63805-63808,63818-63819,63823-63824 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r63724 | gregory.p.smith | 2008-05-26 22:22:14 +0200 (Mon, 26 May 2008) | 6 lines Fixes issue2791: subprocess.Popen.communicate leaked a file descripton until the last reference to the Popen instance was dropped. Adding explicit close() calls fixes it. Candidate for backport to release25-maint. ........ r63726 | benjamin.peterson | 2008-05-26 22:43:24 +0200 (Mon, 26 May 2008) | 2 lines fix minor grammar typo ........ r63732 | benjamin.peterson | 2008-05-26 23:44:26 +0200 (Mon, 26 May 2008) | 2 lines remove duplication in test module ........ r63744 | lars.gustaebel | 2008-05-27 14:39:23 +0200 (Tue, 27 May 2008) | 3 lines Do not close external file objects passed to tarfile.open(mode='w:bz2') when the TarFile is closed. ........ r63754 | benjamin.peterson | 2008-05-28 03:12:35 +0200 (Wed, 28 May 2008) | 2 lines update tutorial function with more appropiate one from Eric Smith ........ r63755 | mark.hammond | 2008-05-28 03:54:55 +0200 (Wed, 28 May 2008) | 2 lines bdist_wininst now works correctly when both --skip-build and --plat-name are specified. ........ r63757 | georg.brandl | 2008-05-28 13:21:39 +0200 (Wed, 28 May 2008) | 2 lines #2989: add PyType_Modified(). ........ r63758 | benjamin.peterson | 2008-05-28 13:51:41 +0200 (Wed, 28 May 2008) | 2 lines fix spelling ........ r63760 | georg.brandl | 2008-05-28 17:41:36 +0200 (Wed, 28 May 2008) | 2 lines #2990: prevent inconsistent state while updating method cache. ........ r63775 | georg.brandl | 2008-05-29 09:18:17 +0200 (Thu, 29 May 2008) | 2 lines Two fixes in bytearray docs. ........ r63781 | georg.brandl | 2008-05-29 09:38:37 +0200 (Thu, 29 May 2008) | 2 lines #2988: add note about catching CookieError when parsing untrusted cookie data. ........ r63782 | georg.brandl | 2008-05-29 09:45:26 +0200 (Thu, 29 May 2008) | 2 lines #2985: allow i8 in XMLRPC responses. ........ r63787 | georg.brandl | 2008-05-29 16:35:39 +0200 (Thu, 29 May 2008) | 2 lines Revert #2990 patch; it's not necessary as Armin showed. ........ r63805 | raymond.hettinger | 2008-05-30 08:37:27 +0200 (Fri, 30 May 2008) | 1 line Issue 2784: fix leaks in exception exit. ........ r63806 | raymond.hettinger | 2008-05-30 08:49:47 +0200 (Fri, 30 May 2008) | 1 line Issue 2855: Fix obscure crasher by slowing down the entire module. Mimics what was done to dictionaries in r59223. ........ r63807 | raymond.hettinger | 2008-05-30 09:16:53 +0200 (Fri, 30 May 2008) | 1 line Issue 2903: Add __name__ in globals for namedtuple namespace. ........ r63808 | georg.brandl | 2008-05-30 09:54:16 +0200 (Fri, 30 May 2008) | 2 lines #2999: fix name of third parameter in unicode.replace()'s docstring. ........ r63818 | georg.brandl | 2008-05-30 21:12:13 +0200 (Fri, 30 May 2008) | 2 lines getloadavg() is not available on Windows. ........ r63819 | georg.brandl | 2008-05-30 21:17:29 +0200 (Fri, 30 May 2008) | 2 lines Better quote with single quotes. ........ r63823 | benjamin.peterson | 2008-05-30 22:44:39 +0200 (Fri, 30 May 2008) | 2 lines fix grammar ........ r63824 | marc-andre.lemburg | 2008-05-30 22:52:18 +0200 (Fri, 30 May 2008) | 5 lines Update the locale module alias table. Closes #3011. ........ --- command/bdist_wininst.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 3f0e09bc..ae7d4fde 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -75,6 +75,12 @@ class bdist_wininst(Command): def finalize_options(self): if self.bdist_dir is None: + if self.skip_build and self.plat_name: + # If build is skipped and plat_name is overridden, bdist will + # not see the correct 'plat_name' - so set that up manually. + bdist = self.distribution.get_command_obj('bdist') + bdist.plat_name = self.plat_name + # next the command will be initialized using that name bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'wininst') if not self.target_version: -- cgit v1.2.1 From a7d9be62dc156189214df813236f2ff56a66016a Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Wed, 11 Jun 2008 17:46:10 +0000 Subject: Follow-up of PEP 3121: Correct the exported symbol for extension modules built by distutils --- command/build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 73cc00ba..c0eef628 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -646,10 +646,10 @@ class build_ext(Command): def get_export_symbols(self, ext): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not - provided, "init" + module_name. Only relevant on Windows, where + provided, "PyInit_" + module_name. Only relevant on Windows, where the .pyd file (DLL) must export the module "init" function. """ - initfunc_name = "init" + ext.name.split('.')[-1] + initfunc_name = "PyInit_" + ext.name.split('.')[-1] if initfunc_name not in ext.export_symbols: ext.export_symbols.append(initfunc_name) return ext.export_symbols -- cgit v1.2.1 From b06cbddbcbe9c30374cca3e0ecc4d96b61f39d7e Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 18 Jun 2008 20:49:58 +0000 Subject: Make a new urllib package . It consists of code from urllib, urllib2, urlparse, and robotparser. The old modules have all been removed. The new package has five submodules: urllib.parse, urllib.request, urllib.response, urllib.error, and urllib.robotparser. The urllib.request.urlopen() function uses the url opener from urllib2. Note that the unittests have not been renamed for the beta, but they will be renamed in the future. Joint work with Senthil Kumaran. --- command/register.py | 20 +++++++++++--------- command/upload.py | 7 ++++--- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/command/register.py b/command/register.py index 89cb2d41..6d5c4599 100644 --- a/command/register.py +++ b/command/register.py @@ -7,8 +7,9 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" -import os, string, urllib2, getpass, urlparse +import os, string, getpass import io +import urllib.parse, urllib.request from distutils.core import PyPIRCCommand from distutils.errors import * @@ -94,7 +95,8 @@ class register(PyPIRCCommand): def classifiers(self): ''' Fetch the list of classifiers from the server. ''' - response = urllib2.urlopen(self.repository+'?:action=list_classifiers') + url = self.repository+'?:action=list_classifiers' + response = urllib.request.urlopen(url) print(response.read()) def verify_metadata(self): @@ -166,8 +168,8 @@ Your selection [default 1]: ''', end=' ') password = getpass.getpass('Password: ') # set up the authentication - auth = urllib2.HTTPPasswordMgr() - host = urlparse.urlparse(self.repository)[1] + auth = urllib.request.HTTPPasswordMgr() + host = urllib.parse.urlparse(self.repository)[1] auth.add_password(self.realm, host, username, password) # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), @@ -276,20 +278,20 @@ Your selection [default 1]: ''', end=' ') 'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary, 'Content-length': str(len(body)) } - req = urllib2.Request(self.repository, body, headers) + req = urllib.request.Request(self.repository, body, headers) # handle HTTP and include the Basic Auth handler - opener = urllib2.build_opener( - urllib2.HTTPBasicAuthHandler(password_mgr=auth) + opener = urllib.request.build_opener( + urllib.request.HTTPBasicAuthHandler(password_mgr=auth) ) data = '' try: result = opener.open(req) - except urllib2.HTTPError as e: + except urllib.error.HTTPError as e: if self.show_response: data = e.fp.read() result = e.code, e.msg - except urllib2.URLError as e: + except urllib.error.URLError as e: result = 500, str(e) else: if self.show_response: diff --git a/command/upload.py b/command/upload.py index 2cad2c7b..5049f03e 100644 --- a/command/upload.py +++ b/command/upload.py @@ -13,7 +13,7 @@ import platform import configparser import http.client import base64 -import urlparse +import urllib.parse class upload(PyPIRCCommand): @@ -145,10 +145,11 @@ class upload(PyPIRCCommand): self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) # build the Request - # We can't use urllib2 since we need to send the Basic + # We can't use urllib since we need to send the Basic # auth right with the first request + # TODO(jhylton): Can we fix urllib? schema, netloc, url, params, query, fragments = \ - urlparse.urlparse(self.repository) + urllib.parse.urlparse(self.repository) assert not params and not query and not fragments if schema == 'http': http = http.client.HTTPConnection(netloc) -- cgit v1.2.1 From 6f6285e128b5c81398538d25ece8a9af988bfb4a Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 19 Jun 2008 00:35:43 +0000 Subject: Bump to 3.0b1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 3618c4f7..54fc2369 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "3.0a5" +__version__ = "3.0b1" #--end constants-- -- cgit v1.2.1 From 07491092fe0ca819ef1c40ec6fb32710d4f2b872 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 19 Jun 2008 01:48:07 +0000 Subject: Bumping to 2.6b1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e3e32f51..a1923578 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6a3" +__version__ = "2.6b1" #--end constants-- -- cgit v1.2.1 From e518d13e52d41fc198d3c3f5fc42275b2ca2c614 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 16 Jul 2008 02:02:25 +0000 Subject: Merged revisions 63828 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r63828 | mark.hammond | 2008-05-31 07:11:07 +0200 (Sat, 31 May 2008) | 2 lines Fix bdist_wininst --user-access-control for win2k ........ --- command/wininst-6.0.exe | Bin 61440 -> 61440 bytes command/wininst-7.1.exe | Bin 61440 -> 65536 bytes command/wininst-9.0-amd64.exe | Bin 77312 -> 77824 bytes command/wininst-9.0.exe | Bin 66048 -> 66048 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-6.0.exe b/command/wininst-6.0.exe index 10c98199..f57c855a 100644 Binary files a/command/wininst-6.0.exe and b/command/wininst-6.0.exe differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 6779aa8d..1433bc1a 100644 Binary files a/command/wininst-7.1.exe and b/command/wininst-7.1.exe differ diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index b4cb062c..9dedfcdc 100644 Binary files a/command/wininst-9.0-amd64.exe and b/command/wininst-9.0-amd64.exe differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 0d04a667..9102ecdb 100644 Binary files a/command/wininst-9.0.exe and b/command/wininst-9.0.exe differ -- cgit v1.2.1 From bd559affbcf751d932274da766e8e762c23e1f87 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 16 Jul 2008 02:17:56 +0000 Subject: Merged revisions 63955 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r63955 | ronald.oussoren | 2008-06-05 14:58:24 +0200 (Thu, 05 Jun 2008) | 20 lines MacOS X: Enable 4-way universal builds This patch adds a new configure argument on OSX: --with-universal-archs=[32-bit|64-bit|all] When used with the --enable-universalsdk option this controls which CPU architectures are includes in the framework. The default is 32-bit, meaning i386 and ppc. The most useful alternative is 'all', which includes all 4 CPU architectures supported by MacOS X (i386, ppc, x86_64 and ppc64). This includes limited support for the Carbon bindings in 64-bit mode as well, limited because (a) I haven't done extensive testing and (b) a large portion of the Carbon API's aren't available in 64-bit mode anyway. I've also duplicated a feature of Apple's build of python: setting the environment variable 'ARCHFLAGS' controls the '-arch' flags used for building extensions using distutils. ........ --- sysconfig.py | 20 ++++++++++++++++++++ unixccompiler.py | 8 +++++++- util.py | 11 +++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index d2b2c9a3..3a120dd5 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -516,6 +516,26 @@ def get_config_vars(*args): flags = re.sub('-isysroot [^ \t]*', ' ', flags) _config_vars[key] = flags + else: + + # Allow the user to override the architecture flags using + # an environment variable. + # NOTE: This name was introduced by Apple in OSX 10.5 and + # is used by several scripting languages distributed with + # that OS release. + + if 'ARCHFLAGS' in os.environ: + arch = os.environ['ARCHFLAGS'] + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _config_vars[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = flags + ' ' + arch + _config_vars[key] = flags + if args: vals = [] for name in args: diff --git a/unixccompiler.py b/unixccompiler.py index 25a042d0..87ce9217 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -63,7 +63,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): stripArch = '-arch' in cc_args stripSysroot = '-isysroot' in cc_args - if stripArch: + if stripArch or 'ARCHFLAGS' in os.environ: while True: try: index = compiler_so.index('-arch') @@ -72,6 +72,12 @@ def _darwin_compiler_fixup(compiler_so, cc_args): except ValueError: break + if 'ARCHFLAGS' in os.environ and not stripArch: + # User specified different -arch flags in the environ, + # see also distutils.sysconfig + compiler_so = compiler_so + ' ' + os.environ['ARCHFLAGS'] + + if stripSysroot: try: index = compiler_so.index('-isysroot') diff --git a/util.py b/util.py index 72039a7e..76798b95 100644 --- a/util.py +++ b/util.py @@ -124,12 +124,19 @@ def get_platform (): osname = "macosx" - if (release + '.') < '10.4.' and \ - get_config_vars().get('UNIVERSALSDK', '').strip(): + if (release + '.') >= '10.4.' and \ + '-arch' in get_config_vars().get('CFLAGS', '').strip(): # The universal build will build fat binaries, but not on # systems before 10.4 + # + # Try to detect 4-way universal builds, those have machine-type + # 'universal' instead of 'fat'. + machine = 'fat' + if '-arch x86_64' in get_config_vars().get('CFLAGS'): + machine = 'universal' + elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. machine = 'ppc' -- cgit v1.2.1 From cc23416383cd1931421722ea974a8b8fcc273d10 Mon Sep 17 00:00:00 2001 From: Jesse Noller Date: Wed, 16 Jul 2008 13:24:06 +0000 Subject: Apply patch for issue 3090: ARCHFLAGS parsing incorrect --- unixccompiler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 6144efc0..045368a9 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -76,8 +76,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): if 'ARCHFLAGS' in os.environ and not stripArch: # User specified different -arch flags in the environ, # see also distutils.sysconfig - compiler_so = compiler_so + ' ' + os.environ['ARCHFLAGS'] - + compiler_so = compiler_so + os.environ['ARCHFLAGS'].split() if stripSysroot: try: -- cgit v1.2.1 From 859dfb6ac16f97bbbd9632c03ac655ad5e3e5d59 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 18 Jul 2008 02:28:44 +0000 Subject: Bumping to 3.0b2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 54fc2369..48bb0056 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "3.0b1" +__version__ = "3.0b2" #--end constants-- -- cgit v1.2.1 From 5656e21cbe602ccf223815c23bad6f8411d039d0 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 18 Jul 2008 03:20:07 +0000 Subject: Bumping to 2.6b2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a1923578..06ec0d4b 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6b1" +__version__ = "2.6b2" #--end constants-- -- cgit v1.2.1 From e6c78afba556f44c4d87f6e5392a7ffba98a45c9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 23 Jul 2008 16:10:53 +0000 Subject: Merged revisions 65012,65035,65037-65040,65048,65057,65077,65091-65095,65097-65099,65127-65128,65131,65133-65136,65139,65149-65151,65155,65158-65159,65176-65178,65183-65184,65187-65190,65192,65194 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r65012 | jesse.noller | 2008-07-16 15:24:06 +0200 (Wed, 16 Jul 2008) | 2 lines Apply patch for issue 3090: ARCHFLAGS parsing incorrect ........ r65035 | georg.brandl | 2008-07-16 23:19:28 +0200 (Wed, 16 Jul 2008) | 2 lines #3045: fix pydoc behavior for TEMP path with spaces. ........ r65037 | georg.brandl | 2008-07-16 23:31:41 +0200 (Wed, 16 Jul 2008) | 2 lines #1608818: errno can get set by every call to readdir(). ........ r65038 | georg.brandl | 2008-07-17 00:04:20 +0200 (Thu, 17 Jul 2008) | 2 lines #3305: self->stream can be NULL. ........ r65039 | georg.brandl | 2008-07-17 00:09:17 +0200 (Thu, 17 Jul 2008) | 2 lines #3345: fix docstring. ........ r65040 | georg.brandl | 2008-07-17 00:33:18 +0200 (Thu, 17 Jul 2008) | 2 lines #3312: fix two sqlite3 crashes. ........ r65048 | georg.brandl | 2008-07-17 01:35:54 +0200 (Thu, 17 Jul 2008) | 2 lines #3388: add a paragraph about using "with" for file objects. ........ r65057 | gregory.p.smith | 2008-07-17 05:13:05 +0200 (Thu, 17 Jul 2008) | 2 lines news note for r63052 ........ r65077 | jesse.noller | 2008-07-17 23:01:05 +0200 (Thu, 17 Jul 2008) | 3 lines Fix issue 3395, update _debugInfo to be _debug_info ........ r65091 | ronald.oussoren | 2008-07-18 07:48:03 +0200 (Fri, 18 Jul 2008) | 2 lines Last bit of a fix for issue3381 (addon for my patch in r65061) ........ r65092 | vinay.sajip | 2008-07-18 10:59:06 +0200 (Fri, 18 Jul 2008) | 1 line Issue #3389: Allow resolving dotted names for handlers in logging configuration files. Thanks to Philip Jenvey for the patch. ........ r65093 | vinay.sajip | 2008-07-18 11:00:00 +0200 (Fri, 18 Jul 2008) | 1 line Issue #3389: Allow resolving dotted names for handlers in logging configuration files. Thanks to Philip Jenvey for the patch. ........ r65094 | vinay.sajip | 2008-07-18 11:00:35 +0200 (Fri, 18 Jul 2008) | 1 line Issue #3389: Allow resolving dotted names for handlers in logging configuration files. Thanks to Philip Jenvey for the patch. ........ r65095 | vinay.sajip | 2008-07-18 11:01:10 +0200 (Fri, 18 Jul 2008) | 1 line Issue #3389: Allow resolving dotted names for handlers in logging configuration files. Thanks to Philip Jenvey for the patch. ........ r65097 | georg.brandl | 2008-07-18 12:20:59 +0200 (Fri, 18 Jul 2008) | 2 lines Remove duplicate entry in __all__. ........ r65098 | georg.brandl | 2008-07-18 12:29:30 +0200 (Fri, 18 Jul 2008) | 2 lines Correct attribute name. ........ r65099 | georg.brandl | 2008-07-18 13:15:06 +0200 (Fri, 18 Jul 2008) | 3 lines Document the different meaning of precision for {:f} and {:g}. Also document how inf and nan are formatted. #3404. ........ r65127 | raymond.hettinger | 2008-07-19 02:42:03 +0200 (Sat, 19 Jul 2008) | 1 line Improve accuracy of gamma test function ........ r65128 | raymond.hettinger | 2008-07-19 02:43:00 +0200 (Sat, 19 Jul 2008) | 1 line Add recipe to the itertools docs. ........ r65131 | georg.brandl | 2008-07-19 12:08:55 +0200 (Sat, 19 Jul 2008) | 2 lines #3378: in case of no memory, don't leak even more memory. :) ........ r65133 | georg.brandl | 2008-07-19 14:39:10 +0200 (Sat, 19 Jul 2008) | 3 lines #3302: fix segfaults when passing None for arguments that can't be NULL for the C functions. ........ r65134 | georg.brandl | 2008-07-19 14:46:12 +0200 (Sat, 19 Jul 2008) | 2 lines #3303: fix crash with invalid Py_DECREF in strcoll(). ........ r65135 | georg.brandl | 2008-07-19 15:00:22 +0200 (Sat, 19 Jul 2008) | 3 lines #3319: don't raise ZeroDivisionError if number of rounds is so low that benchtime is zero. ........ r65136 | georg.brandl | 2008-07-19 15:09:42 +0200 (Sat, 19 Jul 2008) | 3 lines #3323: mention that if inheriting from a class without __slots__, the subclass will have a __dict__ available too. ........ r65139 | georg.brandl | 2008-07-19 15:48:44 +0200 (Sat, 19 Jul 2008) | 2 lines Add ordering info for findall and finditer. ........ r65149 | raymond.hettinger | 2008-07-20 01:21:57 +0200 (Sun, 20 Jul 2008) | 1 line Fix compress() recipe in docs to use itertools. ........ r65150 | raymond.hettinger | 2008-07-20 01:58:47 +0200 (Sun, 20 Jul 2008) | 1 line Clean-up itertools docs and recipes. ........ r65151 | gregory.p.smith | 2008-07-20 02:22:08 +0200 (Sun, 20 Jul 2008) | 9 lines fix issue3120 - don't truncate handles on 64-bit Windows. This is still messy, realistically PC/_subprocess.c should never cast pointers to python numbers and back at all. I don't have a 64-bit windows build environment because microsoft apparently thinks that should cost money. Time to watch the buildbots. It builds and passes tests on 32-bit windows. ........ r65155 | georg.brandl | 2008-07-20 13:50:29 +0200 (Sun, 20 Jul 2008) | 2 lines #926501: add info where to put the docstring. ........ r65158 | neal.norwitz | 2008-07-20 21:35:23 +0200 (Sun, 20 Jul 2008) | 1 line Fix a couple of names in error messages that were wrong ........ r65159 | neal.norwitz | 2008-07-20 22:39:36 +0200 (Sun, 20 Jul 2008) | 1 line Fix misspeeld method name (negative) ........ r65176 | amaury.forgeotdarc | 2008-07-21 23:36:24 +0200 (Mon, 21 Jul 2008) | 4 lines Increment version number in NEWS file, and move items that were added after 2.6b2. (I thought there was a script to automate this kind of updates) ........ r65177 | amaury.forgeotdarc | 2008-07-22 00:00:38 +0200 (Tue, 22 Jul 2008) | 5 lines Issue2378: pdb would delete free variables when stepping into a class statement. The problem was introduced by r53954, the correction is to restore the symmetry between PyFrame_FastToLocals and PyFrame_LocalsToFast ........ r65178 | benjamin.peterson | 2008-07-22 00:05:34 +0200 (Tue, 22 Jul 2008) | 1 line don't use assert statement ........ r65183 | ronald.oussoren | 2008-07-22 09:06:00 +0200 (Tue, 22 Jul 2008) | 2 lines Fix buglet in fix for issue3381 ........ r65184 | ronald.oussoren | 2008-07-22 09:06:33 +0200 (Tue, 22 Jul 2008) | 2 lines Fix build issue on OSX 10.4, somehow this wasn't committed before. ........ r65187 | raymond.hettinger | 2008-07-22 20:54:02 +0200 (Tue, 22 Jul 2008) | 1 line Remove out-of-date section on Exact/Inexact. ........ r65188 | raymond.hettinger | 2008-07-22 21:00:47 +0200 (Tue, 22 Jul 2008) | 1 line Tuples now have both count() and index(). ........ r65189 | raymond.hettinger | 2008-07-22 21:03:05 +0200 (Tue, 22 Jul 2008) | 1 line Fix credits for math.sum() ........ r65190 | raymond.hettinger | 2008-07-22 21:18:50 +0200 (Tue, 22 Jul 2008) | 1 line One more attribution. ........ r65192 | benjamin.peterson | 2008-07-23 01:44:37 +0200 (Wed, 23 Jul 2008) | 1 line remove unneeded import ........ r65194 | benjamin.peterson | 2008-07-23 15:25:06 +0200 (Wed, 23 Jul 2008) | 1 line use isinstance ........ --- unixccompiler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index 87ce9217..d65ab321 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -75,8 +75,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): if 'ARCHFLAGS' in os.environ and not stripArch: # User specified different -arch flags in the environ, # see also distutils.sysconfig - compiler_so = compiler_so + ' ' + os.environ['ARCHFLAGS'] - + compiler_so = compiler_so + os.environ['ARCHFLAGS'].split() if stripSysroot: try: -- cgit v1.2.1 From a52ff5544a4ddb9aa9b5e94543f45cf7ad7f348e Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Sat, 26 Jul 2008 20:09:45 +0000 Subject: Remove incorrect usages of map() in distutils. Reported by Lisandro Dalcin. --- cmd.py | 2 +- command/build_ext.py | 2 +- dist.py | 3 ++- filelist.py | 4 ++-- mwerkscompiler.py | 6 +++--- text_file.py | 3 ++- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cmd.py b/cmd.py index bd560a66..c6572caa 100644 --- a/cmd.py +++ b/cmd.py @@ -158,7 +158,7 @@ class Command: print(indent + header) indent = indent + " " for (option, _, _) in self.user_options: - option = option.translate(longopt_xlate) + option = longopt_xlate(option) if option[-1] == "=": option = option[:-1] value = getattr(self, option) diff --git a/command/build_ext.py b/command/build_ext.py index c0eef628..2db53f13 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -244,7 +244,7 @@ class build_ext(Command): if self.define: defines = self.define.split(',') - self.define = map(lambda symbol: (symbol, '1'), defines) + self.define = [(symbol, '1') for symbol in defines] # The option for macros to undefine is also a string from the # option parsing, but has to be a list. Multiple symbols can also diff --git a/dist.py b/dist.py index ddde909f..8bcb88c3 100644 --- a/dist.py +++ b/dist.py @@ -864,7 +864,8 @@ Common commands: (see '--help-commands' for more) for (option, (source, value)) in option_dict.items(): if DEBUG: print(" %s = %s (from %s)" % (option, value, source)) try: - bool_opts = map(translate_longopt, command_obj.boolean_options) + bool_opts = [translate_longopt(o) + for o in command_obj.boolean_options] except AttributeError: bool_opts = [] try: diff --git a/filelist.py b/filelist.py index 8eab0a95..a80c71e8 100644 --- a/filelist.py +++ b/filelist.py @@ -85,13 +85,13 @@ class FileList: if len(words) < 2: raise DistutilsTemplateError( "'%s' expects ..." % action) - patterns = map(convert_path, words[1:]) + patterns = [convert_path(w) for w in words[1:]] elif action in ('recursive-include', 'recursive-exclude'): if len(words) < 3: raise DistutilsTemplateError( "'%s' expects ..." % action) dir = convert_path(words[1]) - patterns = map(convert_path, words[2:]) + patterns = [convert_path(w) for w in words[2:]] elif action in ('graft', 'prune'): if len(words) != 2: raise DistutilsTemplateError( diff --git a/mwerkscompiler.py b/mwerkscompiler.py index 25d48ae8..130cd614 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -104,10 +104,10 @@ class MWerksCompiler (CCompiler) : # This is because we (usually) create the project in a subdirectory of # where we are now, and keeping the paths relative is too much work right # now. - sources = map(self._filename_to_abs, self.__sources) - include_dirs = map(self._filename_to_abs, self.__include_dirs) + sources = [self._filename_to_abs(s) for s in self.__sources] + include_dirs = [self._filename_to_abs(d) for d in self.__include_dirs] if objects: - objects = map(self._filename_to_abs, objects) + objects = [self._filename_to_abs(o) for o in objects] else: objects = [] if build_temp: diff --git a/text_file.py b/text_file.py index db054fd2..266466c1 100644 --- a/text_file.py +++ b/text_file.py @@ -292,7 +292,7 @@ line 3 \\ continues on next line """ # result 1: no fancy options - result1 = map(lambda x: x + "\n", test_data.split("\n")[0:-1]) + result1 = [x + "\n" for x in test_data.split("\n")[:-1]] # result 2: just strip comments result2 = ["\n", @@ -357,4 +357,5 @@ line 3 \\ join_lines=1, rstrip_ws=1, collapse_join=1) test_input(6, "join lines with collapsing", in_file, result6) + del in_file os.remove(filename) -- cgit v1.2.1 From 4c8a00dd7b5c30ef7f88deaf6e76be5815cee1fb Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Thu, 14 Aug 2008 05:50:43 +0000 Subject: Fixed test_distutils error (test_build_ext) on VC6. --- command/build_ext.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index beb33194..8cf78881 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -209,9 +209,12 @@ class build_ext (Command): elif MSVC_VERSION == 8: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS8.0', 'win32release')) - else: + elif MSVC_VERSION == 7: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS7.1')) + else: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PC', 'VC6')) # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory -- cgit v1.2.1 From dd8e87105079fbf52d866825dc56482a40732b99 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Thu, 14 Aug 2008 07:35:13 +0000 Subject: Merged revisions 65667 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r65667 | hirokazu.yamamoto | 2008-08-14 14:50:43 +0900 | 1 line Fixed test_distutils error (test_build_ext) on VC6. ........ --- command/build_ext.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 2db53f13..7ef5becc 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -205,9 +205,12 @@ class build_ext(Command): elif MSVC_VERSION == 8: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS8.0', 'win32release')) - else: + elif MSVC_VERSION == 7: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS7.1')) + else: + self.library_dirs.append(os.path.join(sys.exec_prefix, + 'PC', 'VC6')) # OS/2 (EMX) doesn't support Debug vs Release builds, but has the # import libraries in its "Config" subdirectory -- cgit v1.2.1 From bc2dcae88fe834e1fe0de12f0b01df8763fa7d38 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sun, 17 Aug 2008 04:16:04 +0000 Subject: Update distutils so that it triggers no warnings when run under -3. --- command/build_ext.py | 2 +- command/build_py.py | 4 ++-- core.py | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 8cf78881..1461409f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -679,7 +679,7 @@ class build_ext (Command): so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: return apply(os.path.join, ext_path) + '_d' + so_ext - return apply(os.path.join, ext_path) + so_ext + return os.path.join(*ext_path) + so_ext def get_export_symbols (self, ext): """Return the list of symbols that a shared extension has to diff --git a/command/build_py.py b/command/build_py.py index be6d2c5b..3bf12673 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -169,7 +169,7 @@ class build_py (Command): del path[-1] else: tail.insert(0, pdir) - return apply(os.path.join, tail) + return os.path.join(*tail) else: # Oops, got all the way through 'path' without finding a # match in package_dir. If package_dir defines a directory @@ -337,7 +337,7 @@ class build_py (Command): def get_module_outfile (self, build_dir, package, module): outfile_path = [build_dir] + list(package) + [module + ".py"] - return apply(os.path.join, outfile_path) + return os.path.join(*outfile_path) def get_outputs (self, include_bytecode=1): diff --git a/core.py b/core.py index de9ce7d7..a0e44ea6 100644 --- a/core.py +++ b/core.py @@ -218,7 +218,8 @@ def run_setup (script_name, script_args=None, stop_after="run"): sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args - execfile(script_name, g, l) + with open(script_name, 'r') as file: + exec file.read() in g, l finally: sys.argv = save_argv _setup_stop_after = None -- cgit v1.2.1 From 93cbbc9e4f93377f401d264b08561fe580ea2bff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Mon, 18 Aug 2008 11:13:45 +0000 Subject: Restore Python 2.3 compatibility and remove "with" usage. --- core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core.py b/core.py index a0e44ea6..62a3389b 100644 --- a/core.py +++ b/core.py @@ -218,8 +218,7 @@ def run_setup (script_name, script_args=None, stop_after="run"): sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args - with open(script_name, 'r') as file: - exec file.read() in g, l + exec open(script_name, 'r').read() in g, l finally: sys.argv = save_argv _setup_stop_after = None -- cgit v1.2.1 From 07f55be128098cc6915ebd7671a1a13078291e9e Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Mon, 18 Aug 2008 19:23:47 +0000 Subject: #2234 distutils failed with mingw binutils 2.18.50.20080109. Be less strict when parsing these version numbers, they don't necessarily follow the python numbering scheme. --- cygwinccompiler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 4ac11eb4..94a7bd96 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -404,7 +404,7 @@ def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. If not possible it returns None for it. """ - from distutils.version import StrictVersion + from distutils.version import LooseVersion from distutils.spawn import find_executable import re @@ -415,7 +415,7 @@ def get_versions(): out.close() result = re.search('(\d+\.\d+(\.\d+)*)',out_string) if result: - gcc_version = StrictVersion(result.group(1)) + gcc_version = LooseVersion(result.group(1)) else: gcc_version = None else: @@ -427,7 +427,7 @@ def get_versions(): out.close() result = re.search('(\d+\.\d+(\.\d+)*)',out_string) if result: - ld_version = StrictVersion(result.group(1)) + ld_version = LooseVersion(result.group(1)) else: ld_version = None else: @@ -439,7 +439,7 @@ def get_versions(): out.close() result = re.search(' (\d+\.\d+(\.\d+)*)',out_string) if result: - dllwrap_version = StrictVersion(result.group(1)) + dllwrap_version = LooseVersion(result.group(1)) else: dllwrap_version = None else: -- cgit v1.2.1 From 478e78efb66d39ece39152d4d41f3842f147a48b Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Mon, 18 Aug 2008 19:33:42 +0000 Subject: Issue #2234: distutils failed with mingw binutils 2.18.50.20080109. Be less strict when parsing these version numbers, they don't necessarily follow the python numbering scheme. Backport of r65834 --- cygwinccompiler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 4fd23e6d..6d1ba16b 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -398,7 +398,7 @@ def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. If not possible it returns None for it. """ - from distutils.version import StrictVersion + from distutils.version import LooseVersion from distutils.spawn import find_executable import re @@ -409,7 +409,7 @@ def get_versions(): out.close() result = re.search('(\d+\.\d+(\.\d+)*)',out_string) if result: - gcc_version = StrictVersion(result.group(1)) + gcc_version = LooseVersion(result.group(1)) else: gcc_version = None else: @@ -421,7 +421,7 @@ def get_versions(): out.close() result = re.search('(\d+\.\d+(\.\d+)*)',out_string) if result: - ld_version = StrictVersion(result.group(1)) + ld_version = LooseVersion(result.group(1)) else: ld_version = None else: @@ -433,7 +433,7 @@ def get_versions(): out.close() result = re.search(' (\d+\.\d+(\.\d+)*)',out_string) if result: - dllwrap_version = StrictVersion(result.group(1)) + dllwrap_version = LooseVersion(result.group(1)) else: dllwrap_version = None else: -- cgit v1.2.1 From 9b46d3fc54dc19f29a4d7503e8c06dcfbebd7b5a Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Tue, 19 Aug 2008 17:56:33 +0000 Subject: #2834: Change re module semantics, so that str and bytes mixing is forbidden, and str (unicode) patterns get full unicode matching by default. The re.ASCII flag is also introduced to ask for ASCII matching instead. --- cygwinccompiler.py | 6 +++--- emxccompiler.py | 2 +- sysconfig.py | 2 +- util.py | 2 +- version.py | 2 +- versionpredicate.py | 6 ++++-- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 48875230..da2c74a2 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -409,7 +409,7 @@ def get_versions(): out = os.popen(gcc_exe + ' -dumpversion','r') out_string = out.read() out.close() - result = re.search('(\d+\.\d+(\.\d+)*)',out_string) + result = re.search('(\d+\.\d+(\.\d+)*)', out_string, re.ASCII) if result: gcc_version = StrictVersion(result.group(1)) else: @@ -421,7 +421,7 @@ def get_versions(): out = os.popen(ld_exe + ' -v','r') out_string = out.read() out.close() - result = re.search('(\d+\.\d+(\.\d+)*)',out_string) + result = re.search('(\d+\.\d+(\.\d+)*)', out_string, re.ASCII) if result: ld_version = StrictVersion(result.group(1)) else: @@ -433,7 +433,7 @@ def get_versions(): out = os.popen(dllwrap_exe + ' --version','r') out_string = out.read() out.close() - result = re.search(' (\d+\.\d+(\.\d+)*)',out_string) + result = re.search(' (\d+\.\d+(\.\d+)*)', out_string, re.ASCII) if result: dllwrap_version = StrictVersion(result.group(1)) else: diff --git a/emxccompiler.py b/emxccompiler.py index d9ee82d5..62a4c5b4 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -300,7 +300,7 @@ def get_versions(): out = os.popen(gcc_exe + ' -dumpversion','r') out_string = out.read() out.close() - result = re.search('(\d+\.\d+\.\d+)',out_string) + result = re.search('(\d+\.\d+\.\d+)', out_string, re.ASCII) if result: gcc_version = StrictVersion(result.group(1)) else: diff --git a/sysconfig.py b/sysconfig.py index 3a120dd5..b17743a8 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -512,7 +512,7 @@ def get_config_vars(*args): # patched up as well. 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = re.sub('-arch\s+\w+\s', ' ', flags, re.ASCII) flags = re.sub('-isysroot [^ \t]*', ' ', flags) _config_vars[key] = flags diff --git a/util.py b/util.py index 76798b95..b87dfbe0 100644 --- a/util.py +++ b/util.py @@ -81,7 +81,7 @@ def get_platform (): return "%s-%s.%s" % (osname, version, release) elif osname[:6] == "cygwin": osname = "cygwin" - rel_re = re.compile (r'[\d.]+') + rel_re = re.compile (r'[\d.]+', re.ASCII) m = rel_re.match(release) if m: release = m.group() diff --git a/version.py b/version.py index f71b2f6c..907f71c3 100644 --- a/version.py +++ b/version.py @@ -134,7 +134,7 @@ class StrictVersion (Version): """ version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', - re.VERBOSE) + re.VERBOSE | re.ASCII) def parse (self, vstring): diff --git a/versionpredicate.py b/versionpredicate.py index 434b34f1..b0dd9f45 100644 --- a/versionpredicate.py +++ b/versionpredicate.py @@ -5,7 +5,8 @@ import distutils.version import operator -re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)") +re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)", + re.ASCII) # (package) (rest) re_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses @@ -153,7 +154,8 @@ def split_provision(value): global _provision_rx if _provision_rx is None: _provision_rx = re.compile( - "([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$") + "([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", + re.ASCII) value = value.strip() m = _provision_rx.match(value) if not m: -- cgit v1.2.1 From 677cbea5d0294da0039c6645d0c5a8abce8efcd0 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 19 Aug 2008 18:57:56 +0000 Subject: Merged revisions 65780,65782,65785,65809,65812,65834,65846,65859,65861 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r65780 | antoine.pitrou | 2008-08-17 15:15:07 -0500 (Sun, 17 Aug 2008) | 3 lines #3580: fix a failure in test_os ........ r65782 | benjamin.peterson | 2008-08-17 15:33:45 -0500 (Sun, 17 Aug 2008) | 1 line set svn:executable on a script ........ r65785 | amaury.forgeotdarc | 2008-08-17 16:05:18 -0500 (Sun, 17 Aug 2008) | 3 lines Fix a refleak in bytearray.split and bytearray.rsplit, detected by regrtest.py -R:: test_bytes ........ r65809 | nick.coghlan | 2008-08-18 07:42:46 -0500 (Mon, 18 Aug 2008) | 1 line Belated NEWS entry for r65642 ........ r65812 | nick.coghlan | 2008-08-18 08:32:19 -0500 (Mon, 18 Aug 2008) | 1 line Fix typo ........ r65834 | amaury.forgeotdarc | 2008-08-18 14:23:47 -0500 (Mon, 18 Aug 2008) | 4 lines #2234 distutils failed with mingw binutils 2.18.50.20080109. Be less strict when parsing these version numbers, they don't necessarily follow the python numbering scheme. ........ r65846 | georg.brandl | 2008-08-18 18:09:49 -0500 (Mon, 18 Aug 2008) | 2 lines Fix grammar. ........ r65859 | thomas.heller | 2008-08-19 12:47:13 -0500 (Tue, 19 Aug 2008) | 2 lines Fix strange character in the docstring. ........ r65861 | benjamin.peterson | 2008-08-19 12:59:23 -0500 (Tue, 19 Aug 2008) | 1 line get unparse to at least unparse its self ........ --- cygwinccompiler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index da2c74a2..ea4c7971 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -400,7 +400,7 @@ def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. If not possible it returns None for it. """ - from distutils.version import StrictVersion + from distutils.version import LooseVersion from distutils.spawn import find_executable import re @@ -411,7 +411,7 @@ def get_versions(): out.close() result = re.search('(\d+\.\d+(\.\d+)*)', out_string, re.ASCII) if result: - gcc_version = StrictVersion(result.group(1)) + gcc_version = LooseVersion(result.group(1)) else: gcc_version = None else: @@ -423,7 +423,7 @@ def get_versions(): out.close() result = re.search('(\d+\.\d+(\.\d+)*)', out_string, re.ASCII) if result: - ld_version = StrictVersion(result.group(1)) + ld_version = LooseVersion(result.group(1)) else: ld_version = None else: @@ -435,7 +435,7 @@ def get_versions(): out.close() result = re.search(' (\d+\.\d+(\.\d+)*)', out_string, re.ASCII) if result: - dllwrap_version = StrictVersion(result.group(1)) + dllwrap_version = LooseVersion(result.group(1)) else: dllwrap_version = None else: -- cgit v1.2.1 From ce2fde27c45c1041550de710a9dce751742ed47d Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 21 Aug 2008 01:15:08 +0000 Subject: Bump to 2.6b3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 06ec0d4b..74d0a47d 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6b2" +__version__ = "2.6b3" #--end constants-- -- cgit v1.2.1 From 82e68f4b495194b137f661c4ace674a857ab9a24 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 21 Aug 2008 02:39:51 +0000 Subject: Bumping to 3.0b3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 48bb0056..9af2d920 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "3.0b2" +__version__ = "3.0b3" #--end constants-- -- cgit v1.2.1 From 031df9f3aba5b6fac4773f05107ad5e7c6d830c4 Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Tue, 2 Sep 2008 23:19:56 +0000 Subject: Issue 2975: when compiling multiple extension modules with visual studio 2008 from the same python instance, some environment variables (LIB, INCLUDE) would grow without limit. Tested with these statements: distutils.ccompiler.new_compiler().initialize() print os.environ['LIB'] But I don't know how to turn them into reliable unit tests. --- msvc9compiler.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index c8d52c42..0b27428d 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -193,6 +193,17 @@ def normalize_and_reduce_paths(paths): reduced_paths.append(np) return reduced_paths +def removeDuplicates(variable): + """Remove duplicate values of an environment variable. + """ + oldList = variable.split(os.pathsep) + newList = [] + for i in oldList: + if i not in newList: + newList.append(i) + newVariable = os.pathsep.join(newList) + return newVariable + def find_vcvarsall(version): """Find the vcvarsall.bat file @@ -252,12 +263,12 @@ def query_vcvarsall(version, arch="x86"): if '=' not in line: continue line = line.strip() - key, value = line.split('=') + key, value = line.split('=', 1) key = key.lower() if key in interesting: if value.endswith(os.pathsep): value = value[:-1] - result[key] = value + result[key] = removeDuplicates(value) if len(result) != len(interesting): raise ValueError(str(list(result.keys()))) -- cgit v1.2.1 From acd8349dbdd9b886e8e78d43238c32f7858c0c20 Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Tue, 2 Sep 2008 23:22:56 +0000 Subject: Merged revisions 66171 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r66171 | amaury.forgeotdarc | 2008-09-03 01:19:56 +0200 (mer., 03 sept. 2008) | 9 lines Issue 2975: when compiling multiple extension modules with visual studio 2008 from the same python instance, some environment variables (LIB, INCLUDE) would grow without limit. Tested with these statements: distutils.ccompiler.new_compiler().initialize() print os.environ['LIB'] But I don't know how to turn them into reliable unit tests. ........ --- msvc9compiler.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index fdb74aea..465013dc 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -193,6 +193,17 @@ def normalize_and_reduce_paths(paths): reduced_paths.append(np) return reduced_paths +def removeDuplicates(variable): + """Remove duplicate values of an environment variable. + """ + oldList = variable.split(os.pathsep) + newList = [] + for i in oldList: + if i not in newList: + newList.append(i) + newVariable = os.pathsep.join(newList) + return newVariable + def find_vcvarsall(version): """Find the vcvarsall.bat file @@ -252,12 +263,12 @@ def query_vcvarsall(version, arch="x86"): if '=' not in line: continue line = line.strip() - key, value = line.split('=') + key, value = line.split('=', 1) key = key.lower() if key in interesting: if value.endswith(os.pathsep): value = value[:-1] - result[key] = value + result[key] = removeDuplicates(value) if len(result) != len(interesting): raise ValueError(str(list(result.keys()))) -- cgit v1.2.1 From 299925be446045f3df379da3ca13797087c43efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Wed, 3 Sep 2008 11:13:56 +0000 Subject: Issue #2562: Fix distutils PKG-INFO writing logic to allow having non-ascii characters and Unicode in setup.py meta-data. --- dist.py | 38 +++++++++++++++++++++++++------------- tests/test_dist.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/dist.py b/dist.py index 0a213809..9ad94fbe 100644 --- a/dist.py +++ b/dist.py @@ -23,6 +23,9 @@ from distutils.util import check_environ, strtobool, rfc822_escape from distutils import log from distutils.debug import DEBUG +# Encoding used for the PKG-INFO files +PKG_INFO_ENCODING = 'utf-8' + # Regex to define acceptable Distutils command names. This is not *quite* # the same as a Python NAME -- I don't allow leading underscores. The fact # that they're very similar is no coincidence; the default naming scheme is @@ -1084,23 +1087,23 @@ class DistributionMetadata: if self.provides or self.requires or self.obsoletes: version = '1.1' - file.write('Metadata-Version: %s\n' % version) - file.write('Name: %s\n' % self.get_name() ) - file.write('Version: %s\n' % self.get_version() ) - file.write('Summary: %s\n' % self.get_description() ) - file.write('Home-page: %s\n' % self.get_url() ) - file.write('Author: %s\n' % self.get_contact() ) - file.write('Author-email: %s\n' % self.get_contact_email() ) - file.write('License: %s\n' % self.get_license() ) + self._write_field(file, 'Metadata-Version', version) + self._write_field(file, 'Name', self.get_name()) + self._write_field(file, 'Version', self.get_version()) + self._write_field(file, 'Summary', self.get_description()) + self._write_field(file, 'Home-page', self.get_url()) + self._write_field(file, 'Author', self.get_contact()) + self._write_field(file, 'Author-email', self.get_contact_email()) + self._write_field(file, 'License', self.get_license()) if self.download_url: - file.write('Download-URL: %s\n' % self.download_url) + self._write_field(file, 'Download-URL', self.download_url) - long_desc = rfc822_escape( self.get_long_description() ) - file.write('Description: %s\n' % long_desc) + long_desc = rfc822_escape( self.get_long_description()) + self._write_field(file, 'Description', long_desc) keywords = string.join( self.get_keywords(), ',') if keywords: - file.write('Keywords: %s\n' % keywords ) + self._write_field(file, 'Keywords', keywords) self._write_list(file, 'Platform', self.get_platforms()) self._write_list(file, 'Classifier', self.get_classifiers()) @@ -1110,9 +1113,18 @@ class DistributionMetadata: self._write_list(file, 'Provides', self.get_provides()) self._write_list(file, 'Obsoletes', self.get_obsoletes()) + def _write_field(self, file, name, value): + + if isinstance(value, unicode): + value = value.encode(PKG_INFO_ENCODING) + else: + value = str(value) + file.write('%s: %s\n' % (name, value)) + def _write_list (self, file, name, values): + for value in values: - file.write('%s: %s\n' % (name, value)) + self._write_field(file, name, value) # -- Metadata query methods ---------------------------------------- diff --git a/tests/test_dist.py b/tests/test_dist.py index 8f1288e9..6f5fe9c7 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -1,3 +1,5 @@ +# -*- coding: latin-1 -*- + """Tests for distutils.dist.""" import distutils.cmd @@ -95,6 +97,39 @@ class DistributionTestCase(unittest.TestCase): finally: os.unlink(TESTFN) + def test_write_pkg_file(self): + # Check DistributionMetadata handling of Unicode fields + my_file = os.path.join(os.path.dirname(__file__), 'f') + klass = distutils.dist.Distribution + + dist = klass(attrs={'author': u'Mister Café', + 'name': 'my.package', + 'maintainer': u'Café Junior', + 'description': u'Café torréfié', + 'long_description': u'Héhéhé'}) + + + # let's make sure the file can be written + # with Unicode fields. they are encoded with + # PKG_INFO_ENCODING + try: + dist.metadata.write_pkg_file(open(my_file, 'w')) + finally: + if os.path.exists(my_file): + os.remove(my_file) + + # regular ascii is of course always usable + dist = klass(attrs={'author': 'Mister Cafe', + 'name': 'my.package', + 'maintainer': 'Cafe Junior', + 'description': 'Cafe torrefie', + 'long_description': 'Hehehe'}) + + try: + dist.metadata.write_pkg_file(open(my_file, 'w')) + finally: + if os.path.exists(my_file): + os.remove(my_file) class MetadataTestCase(unittest.TestCase): -- cgit v1.2.1 From a69032e129711e6b10e786a753ba6bde427272fc Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 4 Sep 2008 21:32:09 +0000 Subject: Issue #3160: the "bdist_wininst" distutils command didn't work. Reviewed by Trent Nelson. --- command/bdist_wininst.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index ae7d4fde..e997e8f0 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -260,13 +260,18 @@ class bdist_wininst(Command): cfgdata = cfgdata.encode("mbcs") # Append the pre-install script - cfgdata = cfgdata + "\0" + cfgdata = cfgdata + b"\0" if self.pre_install_script: - script_data = open(self.pre_install_script, "r").read() - cfgdata = cfgdata + script_data + "\n\0" + # We need to normalize newlines, so we open in text mode and + # convert back to bytes. "latin1" simply avoids any possible + # failures. + with open(self.pre_install_script, "r", + encoding="latin1") as script: + script_data = script.read().encode("latin1") + cfgdata = cfgdata + script_data + b"\n\0" else: # empty pre-install script - cfgdata = cfgdata + "\0" + cfgdata = cfgdata + b"\0" file.write(cfgdata) # The 'magic number' 0x1234567B is used to make sure that the -- cgit v1.2.1 From a97c6df7180f7bd58a5fe5415023ed396a1e7d58 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 12 Sep 2008 23:25:57 +0000 Subject: Bumping to 2.6rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 74d0a47d..b622f7d2 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6b3" +__version__ = "2.6rc1" #--end constants-- -- cgit v1.2.1 From d85f8e8c6760feba91c06d78299f7f3ccd06cf63 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 18 Sep 2008 03:00:28 +0000 Subject: bumping to 3.0rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 9af2d920..2bc0ab04 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "3.0b3" +__version__ = "3.0rc1" #--end constants-- -- cgit v1.2.1 From 3c15d77130e62f3ce3799cd5e27aa1efea293169 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 18 Sep 2008 03:51:46 +0000 Subject: avoid putting unicode objects in the environment causing later test failures. As discussed on #python-dev --- msvc9compiler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 0b27428d..804f1751 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -357,9 +357,10 @@ class MSVCCompiler(CCompiler) : vc_env = query_vcvarsall(VERSION, plat_spec) - self.__paths = vc_env['path'].split(os.pathsep) - os.environ['lib'] = vc_env['lib'] - os.environ['include'] = vc_env['include'] + # take care to only use strings in the environment. + self.__paths = vc_env['path'].encode('mbcs').split(os.pathsep) + os.environ['lib'] = vc_env['lib'].encode('mbcs') + os.environ['include'] = vc_env['include'].encode('mbcs') if len(self.__paths) == 0: raise DistutilsPlatformError("Python was built with %s, " -- cgit v1.2.1 From b4b479dd9b72baf9837af27ee2d31adf0dda6f7e Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 18 Sep 2008 04:33:43 +0000 Subject: Bumping to 2.6rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b622f7d2..fb8043c3 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6rc1" +__version__ = "2.6rc2" #--end constants-- -- cgit v1.2.1 From 2bf49fbe89baac64f6d5487e888385e9797e6164 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 21 Sep 2008 20:48:41 +0000 Subject: Issue #3925: Ignores shutil.rmtree error on cygwin too. Reviewed by Benjamin Peterson. --- tests/test_build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index a658f1aa..31b8b481 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -62,8 +62,8 @@ class BuildExtTestCase(unittest.TestCase): # Get everything back to normal test_support.unload('xx') sys.path = self.sys_path - # XXX on Windows the test leaves a directory with xx.pyd in TEMP - shutil.rmtree(self.tmp_dir, False if os.name != "nt" else True) + # XXX on Windows the test leaves a directory with xx module in TEMP + shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') def test_suite(): if not sysconfig.python_build: -- cgit v1.2.1 From 2e2fdd07fefbe699f2978e48c0ea70e51a85eb30 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 21 Sep 2008 20:52:42 +0000 Subject: Merged revisions 66542 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r66542 | hirokazu.yamamoto | 2008-09-22 05:48:41 +0900 | 2 lines Issue #3925: Ignores shutil.rmtree error on cygwin too. Reviewed by Benjamin Peterson. ........ --- tests/test_build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 20c10745..552a3d14 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -62,8 +62,8 @@ class BuildExtTestCase(unittest.TestCase): # Get everything back to normal support.unload('xx') sys.path = self.sys_path - # XXX on Windows the test leaves a directory with xx.pyd in TEMP - shutil.rmtree(self.tmp_dir, False if os.name != "nt" else True) + # XXX on Windows the test leaves a directory with xx module in TEMP + shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') def test_suite(): if not sysconfig.python_build: -- cgit v1.2.1 From 2ffea9b40a52bc8b1f8ee1595969d86d9d87ced7 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 1 Oct 2008 21:46:40 +0000 Subject: Bumping to 2.6 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index fb8043c3..7315a372 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6rc2" +__version__ = "2.6" #--end constants-- -- cgit v1.2.1 From 9dda0b585406f106ef19ec45b300f89677d0197e Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Sun, 5 Oct 2008 09:00:28 +0000 Subject: Fix [issue4038] py3k error in distutils file_copy exception handlers. r=martin. --- file_util.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/file_util.py b/file_util.py index 69190601..b46b0da6 100644 --- a/file_util.py +++ b/file_util.py @@ -30,31 +30,27 @@ def _copy_file_contents(src, dst, buffer_size=16*1024): try: fsrc = open(src, 'rb') except os.error as e: - (errno, errstr) = e - raise DistutilsFileError("could not open '%s': %s" % (src, errstr)) + raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror)) if os.path.exists(dst): try: os.unlink(dst) except os.error as e: - (errno, errstr) = e raise DistutilsFileError( - "could not delete '%s': %s" % (dst, errstr)) + "could not delete '%s': %s" % (dst, e.strerror)) try: fdst = open(dst, 'wb') except os.error as e: - (errno, errstr) = e raise DistutilsFileError( - "could not create '%s': %s" % (dst, errstr)) + "could not create '%s': %s" % (dst, e.strerror)) while True: try: buf = fsrc.read(buffer_size) except os.error as e: - (errno, errstr) = e raise DistutilsFileError( - "could not read from '%s': %s" % (src, errstr)) + "could not read from '%s': %s" % (src, e.strerror)) if not buf: break @@ -62,9 +58,8 @@ def _copy_file_contents(src, dst, buffer_size=16*1024): try: fdst.write(buf) except os.error as e: - (errno, errstr) = e raise DistutilsFileError( - "could not write to '%s': %s" % (dst, errstr)) + "could not write to '%s': %s" % (dst, e.strerror)) finally: if fdst: fdst.close() -- cgit v1.2.1 From f6a06d79d04028e85bc6db7402d2cfbfd8bc0b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Wed, 15 Oct 2008 05:58:17 +0000 Subject: Issue #4072: Restore build_py_2to3. Add a distutils demo for build_py_2to3. --- command/build_py.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 09f6d233..77284894 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -384,6 +384,18 @@ class build_py (Command): byte_compile(files, optimize=self.optimize, force=self.force, prefix=prefix, dry_run=self.dry_run) +from lib2to3.refactor import RefactoringTool, get_fixers_from_package +class DistutilsRefactoringTool(RefactoringTool): + def log_error(self, msg, *args, **kw): + # XXX ignores kw + log.error(msg, *args) + + def log_message(self, msg, *args): + log.info(msg, *args) + + def log_debug(self, msg, *args): + log.debug(msg, *args) + class build_py_2to3(build_py): def run(self): self.updated_files = [] @@ -396,18 +408,12 @@ class build_py_2to3(build_py): self.build_package_data() # 2to3 - from lib2to3.refactor import RefactoringTool - class Options: - pass - o = Options() - o.doctests_only = False - o.fix = [] - o.list_fixes = [] - o.print_function = False - o.verbose = False - o.write = True - r = RefactoringTool(o) - r.refactor_args(self.updated_files) + fixers = get_fixers_from_package('lib2to3.fixes') + options = dict(fix=[], list_fixes=[], + print_function=False, verbose=False, + write=True) + r = DistutilsRefactoringTool(fixers, options) + r.refactor(self.updated_files, write=True) # Remaining base class code self.byte_compile(self.get_outputs(include_bytecode=0)) -- cgit v1.2.1 From f4beb1d3745aee6654e115b5da14424ff4933a9c Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 6 Nov 2008 03:29:32 +0000 Subject: Bumping to 3.0rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2bc0ab04..5e4972fd 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "3.0rc1" +__version__ = "3.0rc2" #--end constants-- -- cgit v1.2.1 From f5f5810be1fccf5d38fe34126b0422079ffd3934 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Nov 2008 15:15:57 +0000 Subject: #4283: fix left-over iteritems() in distutils. --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 0a902cee..b5c9554d 100644 --- a/command/install.py +++ b/command/install.py @@ -546,7 +546,7 @@ class install (Command): if not self.user: return home = convert_path(os.path.expanduser("~")) - for name, path in self.config_vars.iteritems(): + for name, path in self.config_vars.items(): if path.startswith(home) and not os.path.isdir(path): self.debug_print("os.makedirs('%s', 0o700)" % path) os.makedirs(path, 0o700) -- cgit v1.2.1 From 94f19788a970b7cd603e843207f06ade6f0dbd5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 20 Nov 2008 16:21:55 +0000 Subject: Issue #4354: Fix distutils register command. --- command/register.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/command/register.py b/command/register.py index 6d5c4599..045cbb63 100644 --- a/command/register.py +++ b/command/register.py @@ -15,11 +15,6 @@ from distutils.core import PyPIRCCommand from distutils.errors import * from distutils import log -def raw_input(prompt): - sys.stdout.write(prompt) - sys.stdout.flush() - return sys.stdin.readline() - class register(PyPIRCCommand): description = ("register the distribution with the Python package index") @@ -154,7 +149,7 @@ class register(PyPIRCCommand): 3. have the server generate a new password for you (and email it to you), or 4. quit Your selection [default 1]: ''', end=' ') - choice = raw_input() + choice = input() if not choice: choice = '1' elif choice not in choices: @@ -163,7 +158,7 @@ Your selection [default 1]: ''', end=' ') if choice == '1': # get the username and password while not username: - username = raw_input('Username: ') + username = input('Username: ') while not password: password = getpass.getpass('Password: ') @@ -182,7 +177,7 @@ Your selection [default 1]: ''', end=' ') print('(the login will be stored in %s)' % self._get_rc_file()) choice = 'X' while choice.lower() not in 'yn': - choice = raw_input('Save your login (y/N)?') + choice = input('Save your login (y/N)?') if not choice: choice = 'n' if choice.lower() == 'y': @@ -193,7 +188,7 @@ Your selection [default 1]: ''', end=' ') data['name'] = data['password'] = data['email'] = '' data['confirm'] = None while not data['name']: - data['name'] = raw_input('Username: ') + data['name'] = input('Username: ') while data['password'] != data['confirm']: while not data['password']: data['password'] = getpass.getpass('Password: ') @@ -204,7 +199,7 @@ Your selection [default 1]: ''', end=' ') data['confirm'] = None print("Password and confirm don't match!") while not data['email']: - data['email'] = raw_input(' EMail: ') + data['email'] = input(' EMail: ') code, result = self.post_to_server(data) if code != 200: print('Server response (%s): %s'%(code, result)) @@ -215,7 +210,7 @@ Your selection [default 1]: ''', end=' ') data = {':action': 'password_reset'} data['email'] = '' while not data['email']: - data['email'] = raw_input('Your email address: ') + data['email'] = input('Your email address: ') code, result = self.post_to_server(data) print('Server response (%s): %s'%(code, result)) @@ -262,7 +257,7 @@ Your selection [default 1]: ''', end=' ') if type(value) not in (type([]), type( () )): value = [value] for value in value: - value = str(value).encode("utf-8") + value = str(value) body.write(sep_boundary) body.write('\nContent-Disposition: form-data; name="%s"'%key) body.write("\n\n") @@ -271,7 +266,7 @@ Your selection [default 1]: ''', end=' ') body.write('\n') # write an extra newline (lurve Macs) body.write(end_boundary) body.write("\n") - body = body.getvalue() + body = body.getvalue().encode("utf-8") # build the Request headers = { -- cgit v1.2.1 From 2dd637745910d84b0c7342622a823ed0c3a2cd62 Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Thu, 20 Nov 2008 23:53:46 +0000 Subject: #4338: Fix the distutils "setup.py upload" command. The code still mixed bytes and strings. Reviewed by Martin von Loewis. --- command/upload.py | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/command/upload.py b/command/upload.py index 5049f03e..7ba7f588 100644 --- a/command/upload.py +++ b/command/upload.py @@ -7,11 +7,11 @@ from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log from hashlib import md5 -import os +import os, io import socket import platform import configparser -import http.client +import http.client as httpclient import base64 import urllib.parse @@ -113,33 +113,35 @@ class upload(PyPIRCCommand): open(filename+".asc").read()) # set up the authentication - auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() + user_pass = (self.username + ":" + self.password).encode('ascii') + # The exact encoding of the authentication string is debated. + # Anyway PyPI only accepts ascii for both username or password. + auth = "Basic " + base64.encodestring(user_pass).strip().decode('ascii') # Build up the MIME payload for the POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = '\n--' + boundary - end_boundary = sep_boundary + '--' - body = io.StringIO() + sep_boundary = b'\n--' + boundary.encode('ascii') + end_boundary = sep_boundary + b'--' + body = io.BytesIO() for key, value in data.items(): + title = '\nContent-Disposition: form-data; name="%s"' % key # handle multiple entries for the same name if type(value) != type([]): value = [value] for value in value: if type(value) is tuple: - fn = ';filename="%s"' % value[0] + title += '; filename="%s"' % value[0] value = value[1] else: - fn = "" - value = str(value) + value = str(value).encode('utf-8') body.write(sep_boundary) - body.write('\nContent-Disposition: form-data; name="%s"'%key) - body.write(fn) - body.write("\n\n") + body.write(title.encode('utf-8')) + body.write(b"\n\n") body.write(value) - if value and value[-1] == '\r': - body.write('\n') # write an extra newline (lurve Macs) + if value and value[-1:] == b'\r': + body.write(b'\n') # write an extra newline (lurve Macs) body.write(end_boundary) - body.write("\n") + body.write(b"\n") body = body.getvalue() self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) @@ -152,9 +154,9 @@ class upload(PyPIRCCommand): urllib.parse.urlparse(self.repository) assert not params and not query and not fragments if schema == 'http': - http = http.client.HTTPConnection(netloc) + http = httpclient.HTTPConnection(netloc) elif schema == 'https': - http = http.client.HTTPSConnection(netloc) + http = httpclient.HTTPSConnection(netloc) else: raise AssertionError("unsupported schema "+schema) -- cgit v1.2.1 From a122c2880d7b97fac4345692b4d9fedd58bec7b5 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 21 Nov 2008 01:18:21 +0000 Subject: Bump to 3.0rc3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 5e4972fd..2cf9d4d3 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "3.0rc2" +__version__ = "3.0rc3" #--end constants-- -- cgit v1.2.1 From c6338dae14e511f967554aaceb5ade0c698327a3 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 25 Nov 2008 21:21:32 +0000 Subject: Second fix for issue #4373 --- tests/test_build_ext.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 552a3d14..52752977 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -11,6 +11,10 @@ from distutils import sysconfig import unittest from test import support +# http://bugs.python.org/issue4373 +# Don't load the xx module more than once. +ALREADY_TESTED = False + class BuildExtTestCase(unittest.TestCase): def setUp(self): # Create a simple test environment @@ -23,6 +27,7 @@ class BuildExtTestCase(unittest.TestCase): shutil.copy(xx_c, self.tmp_dir) def test_build_ext(self): + global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) @@ -45,6 +50,11 @@ class BuildExtTestCase(unittest.TestCase): finally: sys.stdout = old_stdout + if ALREADY_TESTED: + return + else: + ALREADY_TESTED = True + import xx for attr in ('error', 'foo', 'new', 'roj'): -- cgit v1.2.1 From 3b615290cad279b0375edf204b8c673c0353f27a Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 28 Nov 2008 11:02:32 +0000 Subject: Fixed issue ##3741: DISTUTILS_USE_SDK set causes msvc9compiler.py to raise an exception --- msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 804f1751..4f72e780 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -316,7 +316,7 @@ class MSVCCompiler(CCompiler) : self.__version = VERSION self.__root = r"Software\Microsoft\VisualStudio" # self.__macros = MACROS - self.__path = [] + self.__paths = [] # target platform (.plat_name is consistent with 'bdist') self.plat_name = None self.__arch = None # deprecated name -- cgit v1.2.1 From 6850072391c86bfcfd01c6b0d65762f9d9c45d40 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 28 Nov 2008 11:03:48 +0000 Subject: Merged revisions 67414 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r67414 | christian.heimes | 2008-11-28 12:02:32 +0100 (Fri, 28 Nov 2008) | 1 line Fixed issue ##3741: DISTUTILS_USE_SDK set causes msvc9compiler.py to raise an exception ........ --- msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 804f1751..4f72e780 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -316,7 +316,7 @@ class MSVCCompiler(CCompiler) : self.__version = VERSION self.__root = r"Software\Microsoft\VisualStudio" # self.__macros = MACROS - self.__path = [] + self.__paths = [] # target platform (.plat_name is consistent with 'bdist') self.plat_name = None self.__arch = None # deprecated name -- cgit v1.2.1 From e32c222776d9dff73e9ba246c7cb654764157bb9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 28 Nov 2008 11:05:17 +0000 Subject: Merged revisions 67414 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r67414 | christian.heimes | 2008-11-28 12:02:32 +0100 (Fri, 28 Nov 2008) | 1 line Fixed issue ##3741: DISTUTILS_USE_SDK set causes msvc9compiler.py to raise an exception ........ --- msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 465013dc..eced0524 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -316,7 +316,7 @@ class MSVCCompiler(CCompiler) : self.__version = VERSION self.__root = r"Software\Microsoft\VisualStudio" # self.__macros = MACROS - self.__path = [] + self.__paths = [] # target platform (.plat_name is consistent with 'bdist') self.plat_name = None self.__arch = None # deprecated name -- cgit v1.2.1 From c2ce44619f5637169f3a57efbd9fc6e97f476ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 1 Dec 2008 04:38:52 +0000 Subject: Issue #4073: Add 2to3 support to build_scripts, refactor that support in build_py. --- command/build_py.py | 23 +++-------------------- command/build_scripts.py | 15 ++++++++++++++- util.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 77284894..99d85be9 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -9,7 +9,7 @@ from glob import glob from distutils.core import Command from distutils.errors import * -from distutils.util import convert_path +from distutils.util import convert_path, Mixin2to3 from distutils import log class build_py (Command): @@ -384,19 +384,7 @@ class build_py (Command): byte_compile(files, optimize=self.optimize, force=self.force, prefix=prefix, dry_run=self.dry_run) -from lib2to3.refactor import RefactoringTool, get_fixers_from_package -class DistutilsRefactoringTool(RefactoringTool): - def log_error(self, msg, *args, **kw): - # XXX ignores kw - log.error(msg, *args) - - def log_message(self, msg, *args): - log.info(msg, *args) - - def log_debug(self, msg, *args): - log.debug(msg, *args) - -class build_py_2to3(build_py): +class build_py_2to3(build_py, Mixin2to3): def run(self): self.updated_files = [] @@ -408,12 +396,7 @@ class build_py_2to3(build_py): self.build_package_data() # 2to3 - fixers = get_fixers_from_package('lib2to3.fixes') - options = dict(fix=[], list_fixes=[], - print_function=False, verbose=False, - write=True) - r = DistutilsRefactoringTool(fixers, options) - r.refactor(self.updated_files, write=True) + self.run_2to3(self.updated_files) # Remaining base class code self.byte_compile(self.get_outputs(include_bytecode=0)) diff --git a/command/build_scripts.py b/command/build_scripts.py index 3ac5b0c0..dc04a9fc 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -9,7 +9,7 @@ from stat import ST_MODE from distutils import sysconfig from distutils.core import Command from distutils.dep_util import newer -from distutils.util import convert_path +from distutils.util import convert_path, Mixin2to3 from distutils import log # check if Python is called on the first line with this expression @@ -59,6 +59,7 @@ class build_scripts(Command): """ self.mkpath(self.build_dir) outfiles = [] + updated_files = [] for script in self.scripts: adjust = False script = convert_path(script) @@ -92,6 +93,7 @@ class build_scripts(Command): if adjust: log.info("copying and adjusting %s -> %s", script, self.build_dir) + updated_files.append(outfile) if not self.dry_run: outf = open(outfile, "w") if not sysconfig.python_build: @@ -112,6 +114,7 @@ class build_scripts(Command): else: if f: f.close() + updated_files.append(outfile) self.copy_file(script, outfile) if os.name == 'posix': @@ -125,3 +128,13 @@ class build_scripts(Command): log.info("changing mode of %s from %o to %o", file, oldmode, newmode) os.chmod(file, newmode) + # XXX should we modify self.outfiles? + return outfiles, updated_files + +class build_scripts_2to3(build_scripts, Mixin2to3): + + def copy_scripts(self): + outfiles, updated_files = build_scripts.copy_scripts(self) + if not self.dry_run: + self.run_2to3(updated_files) + return outfiles, updated_files diff --git a/util.py b/util.py index b87dfbe0..28337fa9 100644 --- a/util.py +++ b/util.py @@ -531,3 +531,51 @@ def rfc822_escape (header): lines = [x.strip() for x in header.split('\n')] sep = '\n' + 8*' ' return sep.join(lines) + +# 2to3 support + +def run_2to3(files, fixer_names=None, options=None, explicit=None): + """Invoke 2to3 on a list of Python files. + The files should all come from the build area, as the + modification is done in-place. To reduce the build time, + only files modified since the last invocation of this + function should be passed in the files argument.""" + + if not files: + return + + # Make this class local, to delay import of 2to3 + from lib2to3.refactor import RefactoringTool, get_fixers_from_package + class DistutilsRefactoringTool(RefactoringTool): + def log_error(self, msg, *args, **kw): + log.error(msg, *args) + + def log_message(self, msg, *args): + log.info(msg, *args) + + def log_debug(self, msg, *args): + log.debug(msg, *args) + + if fixer_names is None: + fixer_names = get_fixers_from_package('lib2to3.fixes') + r = DistutilsRefactoringTool(fixer_names, options=options) + r.refactor(files, write=True) + +class Mixin2to3: + '''Mixin class for commands that run 2to3. + To configure 2to3, setup scripts may either change + the class variables, or inherit from individual commands + to override how 2to3 is invoked.''' + + # provide list of fixers to run; + # defaults to all from lib2to3.fixers + fixer_names = None + + # options dictionary + options = None + + # list of fixers to invoke even though they are marked as explicit + explicit = None + + def run_2to3(self, files): + return run_2to3(files, self.fixer_names, self.options, self.explicit) -- cgit v1.2.1 From e55ca00608e5c34e302a4ad0fc367fa55fd5cadb Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 3 Dec 2008 16:46:14 +0000 Subject: Prep for Python 3.1! --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2cf9d4d3..34fc008a 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "3.0rc3" +__version__ = "3.1a0" #--end constants-- -- cgit v1.2.1 From 78ad69e50bbf4f0948621c9805a65936b5bcee2e Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 4 Dec 2008 02:59:51 +0000 Subject: Prep for 2.6.1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7315a372..8f7eb842 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6" +__version__ = "2.6.1" #--end constants-- -- cgit v1.2.1 From 30a3850d75b9050b79a65a4da14c58401e495443 Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Thu, 11 Dec 2008 00:03:42 +0000 Subject: #1030250: correctly pass the dry_run option to the mkpath() function. --- ccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 0ed9a40a..87d6e273 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1041,7 +1041,7 @@ main (int argc, char **argv) { return move_file (src, dst, dry_run=self.dry_run) def mkpath (self, name, mode=0777): - mkpath (name, mode, self.dry_run) + mkpath (name, mode, dry_run=self.dry_run) # class CCompiler -- cgit v1.2.1 From 73b7309d54071194b72b68c783eded336f96d9fb Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 21 Dec 2008 00:06:59 +0000 Subject: Merged revisions 67654,67676-67677,67681,67692,67725,67761,67784-67785,67787-67788,67802,67848-67850,67862-67864,67880,67882 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r67654 | georg.brandl | 2008-12-07 16:42:09 -0600 (Sun, 07 Dec 2008) | 2 lines #4457: rewrite __import__() documentation. ........ r67676 | benjamin.peterson | 2008-12-08 20:03:03 -0600 (Mon, 08 Dec 2008) | 1 line specify how things are copied ........ r67677 | benjamin.peterson | 2008-12-08 20:05:11 -0600 (Mon, 08 Dec 2008) | 1 line revert unrelated change to installer script ........ r67681 | jeremy.hylton | 2008-12-09 15:03:10 -0600 (Tue, 09 Dec 2008) | 2 lines Add simple unittests for Request ........ r67692 | amaury.forgeotdarc | 2008-12-10 18:03:42 -0600 (Wed, 10 Dec 2008) | 2 lines #1030250: correctly pass the dry_run option to the mkpath() function. ........ r67725 | benjamin.peterson | 2008-12-12 22:02:20 -0600 (Fri, 12 Dec 2008) | 1 line fix incorrect example ........ r67761 | benjamin.peterson | 2008-12-14 11:26:04 -0600 (Sun, 14 Dec 2008) | 1 line fix missing bracket ........ r67784 | georg.brandl | 2008-12-15 02:33:58 -0600 (Mon, 15 Dec 2008) | 2 lines #4446: document "platforms" argument for setup(). ........ r67785 | georg.brandl | 2008-12-15 02:36:11 -0600 (Mon, 15 Dec 2008) | 2 lines #4611: fix typo. ........ r67787 | georg.brandl | 2008-12-15 02:58:59 -0600 (Mon, 15 Dec 2008) | 2 lines #4578: fix has_key() usage in compiler package. ........ r67788 | georg.brandl | 2008-12-15 03:07:39 -0600 (Mon, 15 Dec 2008) | 2 lines #4568: remove limitation in varargs callback example. ........ r67802 | amaury.forgeotdarc | 2008-12-15 16:29:14 -0600 (Mon, 15 Dec 2008) | 4 lines #3632: the "pyo" macro from gdbinit can now run when the GIL is released. Patch by haypo. ........ r67848 | benjamin.peterson | 2008-12-18 20:28:56 -0600 (Thu, 18 Dec 2008) | 1 line fix typo ........ r67849 | benjamin.peterson | 2008-12-18 20:31:35 -0600 (Thu, 18 Dec 2008) | 1 line _call_method -> _callmethod and _get_value to _getvalue ........ r67850 | raymond.hettinger | 2008-12-19 03:06:07 -0600 (Fri, 19 Dec 2008) | 9 lines Fix-up and clean-up docs for int.bit_length(). * Replace dramatic footnote with in-line comment about possible round-off errors in logarithms of large numbers. * Add comments to the pure python code equivalent. * replace floor() with int() in the mathematical equivalent so the type is correct (should be an int, not a float). * add abs() to the mathematical equivalent so that it matches the previous line that it is supposed to be equivalent to. * make one combined example with a negative input. ........ r67862 | benjamin.peterson | 2008-12-19 20:48:02 -0600 (Fri, 19 Dec 2008) | 1 line copy sentence from docstring ........ r67863 | benjamin.peterson | 2008-12-19 20:51:26 -0600 (Fri, 19 Dec 2008) | 1 line add headings ........ r67864 | benjamin.peterson | 2008-12-19 20:57:19 -0600 (Fri, 19 Dec 2008) | 1 line beef up docstring ........ r67880 | benjamin.peterson | 2008-12-20 16:49:24 -0600 (Sat, 20 Dec 2008) | 1 line remove redundant sentence ........ r67882 | benjamin.peterson | 2008-12-20 16:59:49 -0600 (Sat, 20 Dec 2008) | 1 line add some recent releases to the list ........ --- ccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 1e09123a..f5d1587d 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -984,8 +984,8 @@ main (int argc, char **argv) { def move_file(self, src, dst): return move_file(src, dst, dry_run=self.dry_run) - def mkpath(self, name, mode=0o777): - mkpath(name, mode, self.dry_run) + def mkpath (self, name, mode=0o777): + mkpath(name, mode, dry_run=self.dry_run) # Map a sys.platform/os.name ('posix', 'nt') to the default compiler -- cgit v1.2.1 From 058256f5d77a12162cd5ffd6d5249a53b7f44add Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 21 Dec 2008 17:01:26 +0000 Subject: Merged revisions 67654,67676-67677,67681,67692,67725,67746,67748,67761,67784-67785,67787-67788,67802,67832,67848-67849,67859,67862-67864,67880,67882,67885,67889-67892,67895 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ................ r67654 | georg.brandl | 2008-12-07 16:42:09 -0600 (Sun, 07 Dec 2008) | 2 lines #4457: rewrite __import__() documentation. ................ r67676 | benjamin.peterson | 2008-12-08 20:03:03 -0600 (Mon, 08 Dec 2008) | 1 line specify how things are copied ................ r67677 | benjamin.peterson | 2008-12-08 20:05:11 -0600 (Mon, 08 Dec 2008) | 1 line revert unrelated change to installer script ................ r67681 | jeremy.hylton | 2008-12-09 15:03:10 -0600 (Tue, 09 Dec 2008) | 2 lines Add simple unittests for Request ................ r67692 | amaury.forgeotdarc | 2008-12-10 18:03:42 -0600 (Wed, 10 Dec 2008) | 2 lines #1030250: correctly pass the dry_run option to the mkpath() function. ................ r67725 | benjamin.peterson | 2008-12-12 22:02:20 -0600 (Fri, 12 Dec 2008) | 1 line fix incorrect example ................ r67746 | antoine.pitrou | 2008-12-13 17:12:30 -0600 (Sat, 13 Dec 2008) | 3 lines Issue #4163: Use unicode-friendly word splitting in the textwrap functions when given an unicode string. ................ r67748 | benjamin.peterson | 2008-12-13 19:46:11 -0600 (Sat, 13 Dec 2008) | 1 line remove has_key usage ................ r67761 | benjamin.peterson | 2008-12-14 11:26:04 -0600 (Sun, 14 Dec 2008) | 1 line fix missing bracket ................ r67784 | georg.brandl | 2008-12-15 02:33:58 -0600 (Mon, 15 Dec 2008) | 2 lines #4446: document "platforms" argument for setup(). ................ r67785 | georg.brandl | 2008-12-15 02:36:11 -0600 (Mon, 15 Dec 2008) | 2 lines #4611: fix typo. ................ r67787 | georg.brandl | 2008-12-15 02:58:59 -0600 (Mon, 15 Dec 2008) | 2 lines #4578: fix has_key() usage in compiler package. ................ r67788 | georg.brandl | 2008-12-15 03:07:39 -0600 (Mon, 15 Dec 2008) | 2 lines #4568: remove limitation in varargs callback example. ................ r67802 | amaury.forgeotdarc | 2008-12-15 16:29:14 -0600 (Mon, 15 Dec 2008) | 4 lines #3632: the "pyo" macro from gdbinit can now run when the GIL is released. Patch by haypo. ................ r67832 | antoine.pitrou | 2008-12-17 16:46:54 -0600 (Wed, 17 Dec 2008) | 4 lines Issue #2467: gc.DEBUG_STATS reports invalid elapsed times. Patch by Neil Schemenauer, very slightly modified. ................ r67848 | benjamin.peterson | 2008-12-18 20:28:56 -0600 (Thu, 18 Dec 2008) | 1 line fix typo ................ r67849 | benjamin.peterson | 2008-12-18 20:31:35 -0600 (Thu, 18 Dec 2008) | 1 line _call_method -> _callmethod and _get_value to _getvalue ................ r67859 | amaury.forgeotdarc | 2008-12-19 16:56:48 -0600 (Fri, 19 Dec 2008) | 4 lines #4700: crtlicense.txt is displayed by the license() command and should be kept ascii-only. Will port to 3.0 ................ r67862 | benjamin.peterson | 2008-12-19 20:48:02 -0600 (Fri, 19 Dec 2008) | 1 line copy sentence from docstring ................ r67863 | benjamin.peterson | 2008-12-19 20:51:26 -0600 (Fri, 19 Dec 2008) | 1 line add headings ................ r67864 | benjamin.peterson | 2008-12-19 20:57:19 -0600 (Fri, 19 Dec 2008) | 1 line beef up docstring ................ r67880 | benjamin.peterson | 2008-12-20 16:49:24 -0600 (Sat, 20 Dec 2008) | 1 line remove redundant sentence ................ r67882 | benjamin.peterson | 2008-12-20 16:59:49 -0600 (Sat, 20 Dec 2008) | 1 line add some recent releases to the list ................ r67885 | benjamin.peterson | 2008-12-20 17:48:54 -0600 (Sat, 20 Dec 2008) | 1 line silence annoying DeprecationWarning ................ r67889 | benjamin.peterson | 2008-12-20 19:04:32 -0600 (Sat, 20 Dec 2008) | 1 line sphinx.web is long gone ................ r67890 | benjamin.peterson | 2008-12-20 19:12:26 -0600 (Sat, 20 Dec 2008) | 1 line update readme ................ r67891 | benjamin.peterson | 2008-12-20 19:14:47 -0600 (Sat, 20 Dec 2008) | 1 line there are way too many places which need to have the current version added ................ r67892 | benjamin.peterson | 2008-12-20 19:29:32 -0600 (Sat, 20 Dec 2008) | 9 lines Merged revisions 67809 via svnmerge from svn+ssh://pythondev@svn.python.org/sandbox/trunk/2to3/lib2to3 ........ r67809 | benjamin.peterson | 2008-12-15 21:54:45 -0600 (Mon, 15 Dec 2008) | 1 line fix logic error ........ ................ r67895 | neal.norwitz | 2008-12-21 08:28:32 -0600 (Sun, 21 Dec 2008) | 2 lines Add Tarek for work on distutils. ................ --- ccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 0ed9a40a..87d6e273 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1041,7 +1041,7 @@ main (int argc, char **argv) { return move_file (src, dst, dry_run=self.dry_run) def mkpath (self, name, mode=0777): - mkpath (name, mode, self.dry_run) + mkpath (name, mode, dry_run=self.dry_run) # class CCompiler -- cgit v1.2.1 From 0bcdf06224e998e958d9fc01aa27eb2655cbe6b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 24 Dec 2008 19:10:05 +0000 Subject: fixed #4400 : distutils .pypirc default generated file was broken. --- command/register.py | 16 +++++--- config.py | 4 +- tests/test_config.py | 29 +++++++++++++ tests/test_register.py | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 tests/test_register.py diff --git a/command/register.py b/command/register.py index a9ab361a..bf7be960 100644 --- a/command/register.py +++ b/command/register.py @@ -141,12 +141,14 @@ class register(PyPIRCCommand): # get the user's login info choices = '1 2 3 4'.split() while choice not in choices: - print '''We need to know who you are, so please choose either: + self.announce('''\ +We need to know who you are, so please choose either: 1. use your existing login, 2. register as a new user, 3. have the server generate a new password for you (and email it to you), or 4. quit -Your selection [default 1]: ''', +Your selection [default 1]: ''', log.INFO) + choice = raw_input() if not choice: choice = '1' @@ -167,12 +169,16 @@ Your selection [default 1]: ''', # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) - print 'Server response (%s): %s' % (code, result) + self.announce('Server response (%s): %s' % (code, result), + log.INFO) # possibly save the login if not self.has_config and code == 200: - print 'I can store your PyPI login so future submissions will be faster.' - print '(the login will be stored in %s)' % self._get_rc_file() + self.announce(('I can store your PyPI login so future ' + 'submissions will be faster.'), log.INFO) + self.announce('(the login will be stored in %s)' % \ + self._get_rc_file(), log.INFO) + choice = 'X' while choice.lower() not in 'yn': choice = raw_input('Save your login (y/N)?') diff --git a/config.py b/config.py index e3a4c57e..4186c9b1 100644 --- a/config.py +++ b/config.py @@ -10,8 +10,8 @@ from ConfigParser import ConfigParser from distutils.cmd import Command DEFAULT_PYPIRC = """\ -[pypirc] -servers = +[distutils] +index-servers = pypi [pypi] diff --git a/tests/test_config.py b/tests/test_config.py index bfce3e3e..cae76898 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -5,6 +5,8 @@ import unittest from distutils.core import PyPIRCCommand from distutils.core import Distribution +from distutils.log import set_threshold +from distutils.log import WARN from distutils.tests import support @@ -32,6 +34,17 @@ username:tarek password:secret """ +WANTED = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:xxx +""" + + class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): @@ -53,6 +66,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): finalize_options = initialize_options self._cmd = command + self.old_threshold = set_threshold(WARN) def tearDown(self): """Removes the patch.""" @@ -62,6 +76,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): os.environ['HOME'] = self._old_home if os.path.exists(self.rc): os.remove(self.rc) + set_threshold(self.old_threshold) def test_server_registration(self): # This test makes sure PyPIRCCommand knows how to: @@ -98,6 +113,20 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): ('server', 'server-login'), ('username', 'tarek')] self.assertEquals(config, waited) + def test_server_empty_registration(self): + + cmd = self._cmd(self.dist) + rc = cmd._get_rc_file() + self.assert_(not os.path.exists(rc)) + + cmd._store_pypirc('tarek', 'xxx') + + self.assert_(os.path.exists(rc)) + content = open(rc).read() + + self.assertEquals(content, WANTED) + + def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) diff --git a/tests/test_register.py b/tests/test_register.py new file mode 100644 index 00000000..3a3a3b73 --- /dev/null +++ b/tests/test_register.py @@ -0,0 +1,109 @@ +"""Tests for distutils.command.register.""" +import sys +import os +import unittest + +from distutils.command.register import register +from distutils.core import Distribution + +from distutils.tests import support +from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase + +class RawInputs(object): + """Fakes user inputs.""" + def __init__(self, *answers): + self.answers = answers + self.index = 0 + + def __call__(self, prompt=''): + try: + return self.answers[self.index] + finally: + self.index += 1 + +WANTED_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:xxx +""" + +class registerTestCase(PyPIRCCommandTestCase): + + def test_create_pypirc(self): + # this test makes sure a .pypirc file + # is created when requested. + + # let's create a fake distribution + # and a register instance + dist = Distribution() + dist.metadata.url = 'xxx' + dist.metadata.author = 'xxx' + dist.metadata.author_email = 'xxx' + dist.metadata.name = 'xxx' + dist.metadata.version = 'xxx' + cmd = register(dist) + + # we shouldn't have a .pypirc file yet + self.assert_(not os.path.exists(self.rc)) + + # patching raw_input and getpass.getpass + # so register gets happy + # + # Here's what we are faking : + # use your existing login (choice 1.) + # Username : 'tarek' + # Password : 'xxx' + # Save your login (y/N)? : 'y' + inputs = RawInputs('1', 'tarek', 'y') + from distutils.command import register as register_module + register_module.raw_input = inputs.__call__ + def _getpass(prompt): + return 'xxx' + register_module.getpass.getpass = _getpass + class FakeServer(object): + def __init__(self): + self.calls = [] + + def __call__(self, *args): + # we want to compare them, so let's store + # something comparable + els = args[0].items() + els.sort() + self.calls.append(tuple(els)) + return 200, 'OK' + + cmd.post_to_server = pypi_server = FakeServer() + + # let's run the command + cmd.run() + + # we should have a brand new .pypirc file + self.assert_(os.path.exists(self.rc)) + + # with the content similar to WANTED_PYPIRC + content = open(self.rc).read() + self.assertEquals(content, WANTED_PYPIRC) + + # now let's make sure the .pypirc file generated + # really works : we shouldn't be asked anything + # if we run the command again + def _no_way(prompt=''): + raise AssertionError(prompt) + register_module.raw_input = _no_way + + cmd.run() + + # let's see what the server received : we should + # have 2 similar requests + self.assert_(len(pypi_server.calls), 2) + self.assert_(pypi_server.calls[0], pypi_server.calls[1]) + +def test_suite(): + return unittest.makeSuite(registerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 2f485bf7a6d9627d79b9ba753a6a68d8d142eeeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 27 Dec 2008 13:28:42 +0000 Subject: Merged revisions 67926 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r67926 | tarek.ziade | 2008-12-24 20:10:05 +0100 (Wed, 24 Dec 2008) | 1 line fixed #4400 : distutils .pypirc default generated file was broken. ........ --- command/register.py | 16 +++++--- config.py | 4 +- tests/test_config.py | 29 +++++++++++++ tests/test_register.py | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 tests/test_register.py diff --git a/command/register.py b/command/register.py index a9ab361a..bf7be960 100644 --- a/command/register.py +++ b/command/register.py @@ -141,12 +141,14 @@ class register(PyPIRCCommand): # get the user's login info choices = '1 2 3 4'.split() while choice not in choices: - print '''We need to know who you are, so please choose either: + self.announce('''\ +We need to know who you are, so please choose either: 1. use your existing login, 2. register as a new user, 3. have the server generate a new password for you (and email it to you), or 4. quit -Your selection [default 1]: ''', +Your selection [default 1]: ''', log.INFO) + choice = raw_input() if not choice: choice = '1' @@ -167,12 +169,16 @@ Your selection [default 1]: ''', # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) - print 'Server response (%s): %s' % (code, result) + self.announce('Server response (%s): %s' % (code, result), + log.INFO) # possibly save the login if not self.has_config and code == 200: - print 'I can store your PyPI login so future submissions will be faster.' - print '(the login will be stored in %s)' % self._get_rc_file() + self.announce(('I can store your PyPI login so future ' + 'submissions will be faster.'), log.INFO) + self.announce('(the login will be stored in %s)' % \ + self._get_rc_file(), log.INFO) + choice = 'X' while choice.lower() not in 'yn': choice = raw_input('Save your login (y/N)?') diff --git a/config.py b/config.py index e3a4c57e..4186c9b1 100644 --- a/config.py +++ b/config.py @@ -10,8 +10,8 @@ from ConfigParser import ConfigParser from distutils.cmd import Command DEFAULT_PYPIRC = """\ -[pypirc] -servers = +[distutils] +index-servers = pypi [pypi] diff --git a/tests/test_config.py b/tests/test_config.py index bfce3e3e..cae76898 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -5,6 +5,8 @@ import unittest from distutils.core import PyPIRCCommand from distutils.core import Distribution +from distutils.log import set_threshold +from distutils.log import WARN from distutils.tests import support @@ -32,6 +34,17 @@ username:tarek password:secret """ +WANTED = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:xxx +""" + + class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): @@ -53,6 +66,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): finalize_options = initialize_options self._cmd = command + self.old_threshold = set_threshold(WARN) def tearDown(self): """Removes the patch.""" @@ -62,6 +76,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): os.environ['HOME'] = self._old_home if os.path.exists(self.rc): os.remove(self.rc) + set_threshold(self.old_threshold) def test_server_registration(self): # This test makes sure PyPIRCCommand knows how to: @@ -98,6 +113,20 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): ('server', 'server-login'), ('username', 'tarek')] self.assertEquals(config, waited) + def test_server_empty_registration(self): + + cmd = self._cmd(self.dist) + rc = cmd._get_rc_file() + self.assert_(not os.path.exists(rc)) + + cmd._store_pypirc('tarek', 'xxx') + + self.assert_(os.path.exists(rc)) + content = open(rc).read() + + self.assertEquals(content, WANTED) + + def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) diff --git a/tests/test_register.py b/tests/test_register.py new file mode 100644 index 00000000..3a3a3b73 --- /dev/null +++ b/tests/test_register.py @@ -0,0 +1,109 @@ +"""Tests for distutils.command.register.""" +import sys +import os +import unittest + +from distutils.command.register import register +from distutils.core import Distribution + +from distutils.tests import support +from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase + +class RawInputs(object): + """Fakes user inputs.""" + def __init__(self, *answers): + self.answers = answers + self.index = 0 + + def __call__(self, prompt=''): + try: + return self.answers[self.index] + finally: + self.index += 1 + +WANTED_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:xxx +""" + +class registerTestCase(PyPIRCCommandTestCase): + + def test_create_pypirc(self): + # this test makes sure a .pypirc file + # is created when requested. + + # let's create a fake distribution + # and a register instance + dist = Distribution() + dist.metadata.url = 'xxx' + dist.metadata.author = 'xxx' + dist.metadata.author_email = 'xxx' + dist.metadata.name = 'xxx' + dist.metadata.version = 'xxx' + cmd = register(dist) + + # we shouldn't have a .pypirc file yet + self.assert_(not os.path.exists(self.rc)) + + # patching raw_input and getpass.getpass + # so register gets happy + # + # Here's what we are faking : + # use your existing login (choice 1.) + # Username : 'tarek' + # Password : 'xxx' + # Save your login (y/N)? : 'y' + inputs = RawInputs('1', 'tarek', 'y') + from distutils.command import register as register_module + register_module.raw_input = inputs.__call__ + def _getpass(prompt): + return 'xxx' + register_module.getpass.getpass = _getpass + class FakeServer(object): + def __init__(self): + self.calls = [] + + def __call__(self, *args): + # we want to compare them, so let's store + # something comparable + els = args[0].items() + els.sort() + self.calls.append(tuple(els)) + return 200, 'OK' + + cmd.post_to_server = pypi_server = FakeServer() + + # let's run the command + cmd.run() + + # we should have a brand new .pypirc file + self.assert_(os.path.exists(self.rc)) + + # with the content similar to WANTED_PYPIRC + content = open(self.rc).read() + self.assertEquals(content, WANTED_PYPIRC) + + # now let's make sure the .pypirc file generated + # really works : we shouldn't be asked anything + # if we run the command again + def _no_way(prompt=''): + raise AssertionError(prompt) + register_module.raw_input = _no_way + + cmd.run() + + # let's see what the server received : we should + # have 2 similar requests + self.assert_(len(pypi_server.calls), 2) + self.assert_(pypi_server.calls[0], pypi_server.calls[1]) + +def test_suite(): + return unittest.makeSuite(registerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From a89c58deedd83e10acf51ce48c0a8ee9b753cc98 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Dec 2008 16:00:54 +0000 Subject: Merged revisions 67889-67892,67895,67898,67904-67907,67912,67918,67920-67921,67923-67924,67926-67927,67930,67943 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ................ r67889 | benjamin.peterson | 2008-12-20 19:04:32 -0600 (Sat, 20 Dec 2008) | 1 line sphinx.web is long gone ................ r67890 | benjamin.peterson | 2008-12-20 19:12:26 -0600 (Sat, 20 Dec 2008) | 1 line update readme ................ r67891 | benjamin.peterson | 2008-12-20 19:14:47 -0600 (Sat, 20 Dec 2008) | 1 line there are way too many places which need to have the current version added ................ r67892 | benjamin.peterson | 2008-12-20 19:29:32 -0600 (Sat, 20 Dec 2008) | 9 lines Merged revisions 67809 via svnmerge from svn+ssh://pythondev@svn.python.org/sandbox/trunk/2to3/lib2to3 ........ r67809 | benjamin.peterson | 2008-12-15 21:54:45 -0600 (Mon, 15 Dec 2008) | 1 line fix logic error ........ ................ r67895 | neal.norwitz | 2008-12-21 08:28:32 -0600 (Sun, 21 Dec 2008) | 2 lines Add Tarek for work on distutils. ................ r67898 | benjamin.peterson | 2008-12-21 15:00:53 -0600 (Sun, 21 Dec 2008) | 1 line compute DISTVERSION with patchlevel.py ................ r67904 | benjamin.peterson | 2008-12-22 14:44:58 -0600 (Mon, 22 Dec 2008) | 1 line less attitude ................ r67905 | benjamin.peterson | 2008-12-22 14:51:15 -0600 (Mon, 22 Dec 2008) | 1 line fix #4720: the format to PyArg_ParseTupleAndKeywords can now start with '|' ................ r67906 | benjamin.peterson | 2008-12-22 14:52:53 -0600 (Mon, 22 Dec 2008) | 1 line add NEWS note ................ r67907 | benjamin.peterson | 2008-12-22 16:12:19 -0600 (Mon, 22 Dec 2008) | 1 line silence compiler warning ................ r67912 | georg.brandl | 2008-12-23 06:37:21 -0600 (Tue, 23 Dec 2008) | 2 lines Fix missing "svn" command. ................ r67918 | georg.brandl | 2008-12-23 09:44:25 -0600 (Tue, 23 Dec 2008) | 2 lines Markup fix. ................ r67920 | benjamin.peterson | 2008-12-23 14:09:28 -0600 (Tue, 23 Dec 2008) | 1 line use a global variable, so the compiler doesn't optimize the assignment out ................ r67921 | benjamin.peterson | 2008-12-23 14:12:33 -0600 (Tue, 23 Dec 2008) | 1 line make global static ................ r67923 | benjamin.peterson | 2008-12-24 09:10:27 -0600 (Wed, 24 Dec 2008) | 1 line #4736 BufferRWPair.closed shouldn't try to call another property as a function ................ r67924 | benjamin.peterson | 2008-12-24 10:10:05 -0600 (Wed, 24 Dec 2008) | 1 line pretend exceptions don't exist a while longer ................ r67926 | tarek.ziade | 2008-12-24 13:10:05 -0600 (Wed, 24 Dec 2008) | 1 line fixed #4400 : distutils .pypirc default generated file was broken. ................ r67927 | benjamin.peterson | 2008-12-26 17:26:30 -0600 (Fri, 26 Dec 2008) | 1 line python version is included in file name now ................ r67930 | hirokazu.yamamoto | 2008-12-26 22:19:48 -0600 (Fri, 26 Dec 2008) | 2 lines Issue #4740: Use HIGHEST_PROTOCOL in pickle test. (There is no behavior difference in 2.x because HIGHEST_PROTOCOL == 2) ................ r67943 | alexandre.vassalotti | 2008-12-27 04:02:59 -0600 (Sat, 27 Dec 2008) | 2 lines Fix bogus unicode tests in pickletester. ................ --- command/register.py | 17 +++++--- config.py | 4 +- tests/test_config.py | 29 +++++++++++++ tests/test_register.py | 109 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 tests/test_register.py diff --git a/command/register.py b/command/register.py index 045cbb63..bd9b8c01 100644 --- a/command/register.py +++ b/command/register.py @@ -143,13 +143,14 @@ class register(PyPIRCCommand): # get the user's login info choices = '1 2 3 4'.split() while choice not in choices: - print('''We need to know who you are, so please choose either: + self.announce('''\ +We need to know who you are, so please choose either: 1. use your existing login, 2. register as a new user, 3. have the server generate a new password for you (and email it to you), or 4. quit -Your selection [default 1]: ''', end=' ') - choice = input() +Your selection [default 1]: ''', log.INFO) + choice = raw_input() if not choice: choice = '1' elif choice not in choices: @@ -169,12 +170,16 @@ Your selection [default 1]: ''', end=' ') # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) - print('Server response (%s): %s'%(code, result)) + self.announce('Server response (%s): %s' % (code, result), + log.INFO) # possibly save the login if not self.has_config and code == 200: - print('I can store your PyPI login so future submissions will be faster.') - print('(the login will be stored in %s)' % self._get_rc_file()) + self.announce(('I can store your PyPI login so future ' + 'submissions will be faster.'), log.INFO) + self.announce('(the login will be stored in %s)' % \ + self._get_rc_file(), log.INFO) + choice = 'X' while choice.lower() not in 'yn': choice = input('Save your login (y/N)?') diff --git a/config.py b/config.py index 0ecfe0cc..73f32604 100644 --- a/config.py +++ b/config.py @@ -10,8 +10,8 @@ from configparser import ConfigParser from distutils.cmd import Command DEFAULT_PYPIRC = """\ -[pypirc] -servers = +[distutils] +index-servers = pypi [pypi] diff --git a/tests/test_config.py b/tests/test_config.py index 016ba4cc..bdc9b2b5 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -5,6 +5,8 @@ import unittest from distutils.core import PyPIRCCommand from distutils.core import Distribution +from distutils.log import set_threshold +from distutils.log import WARN from distutils.tests import support @@ -32,6 +34,17 @@ username:tarek password:secret """ +WANTED = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:xxx +""" + + class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): @@ -53,6 +66,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): finalize_options = initialize_options self._cmd = command + self.old_threshold = set_threshold(WARN) def tearDown(self): """Removes the patch.""" @@ -62,6 +76,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): os.environ['HOME'] = self._old_home if os.path.exists(self.rc): os.remove(self.rc) + set_threshold(self.old_threshold) def test_server_registration(self): # This test makes sure PyPIRCCommand knows how to: @@ -96,6 +111,20 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): ('server', 'server-login'), ('username', 'tarek')] self.assertEquals(config, waited) + def test_server_empty_registration(self): + + cmd = self._cmd(self.dist) + rc = cmd._get_rc_file() + self.assert_(not os.path.exists(rc)) + + cmd._store_pypirc('tarek', 'xxx') + + self.assert_(os.path.exists(rc)) + content = open(rc).read() + + self.assertEquals(content, WANTED) + + def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) diff --git a/tests/test_register.py b/tests/test_register.py new file mode 100644 index 00000000..3a3a3b73 --- /dev/null +++ b/tests/test_register.py @@ -0,0 +1,109 @@ +"""Tests for distutils.command.register.""" +import sys +import os +import unittest + +from distutils.command.register import register +from distutils.core import Distribution + +from distutils.tests import support +from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase + +class RawInputs(object): + """Fakes user inputs.""" + def __init__(self, *answers): + self.answers = answers + self.index = 0 + + def __call__(self, prompt=''): + try: + return self.answers[self.index] + finally: + self.index += 1 + +WANTED_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:xxx +""" + +class registerTestCase(PyPIRCCommandTestCase): + + def test_create_pypirc(self): + # this test makes sure a .pypirc file + # is created when requested. + + # let's create a fake distribution + # and a register instance + dist = Distribution() + dist.metadata.url = 'xxx' + dist.metadata.author = 'xxx' + dist.metadata.author_email = 'xxx' + dist.metadata.name = 'xxx' + dist.metadata.version = 'xxx' + cmd = register(dist) + + # we shouldn't have a .pypirc file yet + self.assert_(not os.path.exists(self.rc)) + + # patching raw_input and getpass.getpass + # so register gets happy + # + # Here's what we are faking : + # use your existing login (choice 1.) + # Username : 'tarek' + # Password : 'xxx' + # Save your login (y/N)? : 'y' + inputs = RawInputs('1', 'tarek', 'y') + from distutils.command import register as register_module + register_module.raw_input = inputs.__call__ + def _getpass(prompt): + return 'xxx' + register_module.getpass.getpass = _getpass + class FakeServer(object): + def __init__(self): + self.calls = [] + + def __call__(self, *args): + # we want to compare them, so let's store + # something comparable + els = args[0].items() + els.sort() + self.calls.append(tuple(els)) + return 200, 'OK' + + cmd.post_to_server = pypi_server = FakeServer() + + # let's run the command + cmd.run() + + # we should have a brand new .pypirc file + self.assert_(os.path.exists(self.rc)) + + # with the content similar to WANTED_PYPIRC + content = open(self.rc).read() + self.assertEquals(content, WANTED_PYPIRC) + + # now let's make sure the .pypirc file generated + # really works : we shouldn't be asked anything + # if we run the command again + def _no_way(prompt=''): + raise AssertionError(prompt) + register_module.raw_input = _no_way + + cmd.run() + + # let's see what the server received : we should + # have 2 similar requests + self.assert_(len(pypi_server.calls), 2) + self.assert_(pypi_server.calls[0], pypi_server.calls[1]) + +def test_suite(): + return unittest.makeSuite(registerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 326cd6e433ffd81a314773b3b24db40a0a1cd09e Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Dec 2008 17:00:44 +0000 Subject: fix 2.x isms in distutils test --- command/register.py | 2 +- tests/test_register.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/command/register.py b/command/register.py index bd9b8c01..30e9a37e 100644 --- a/command/register.py +++ b/command/register.py @@ -150,7 +150,7 @@ We need to know who you are, so please choose either: 3. have the server generate a new password for you (and email it to you), or 4. quit Your selection [default 1]: ''', log.INFO) - choice = raw_input() + choice = input() if not choice: choice = '1' elif choice not in choices: diff --git a/tests/test_register.py b/tests/test_register.py index 3a3a3b73..021b3ea8 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -60,7 +60,7 @@ class registerTestCase(PyPIRCCommandTestCase): # Save your login (y/N)? : 'y' inputs = RawInputs('1', 'tarek', 'y') from distutils.command import register as register_module - register_module.raw_input = inputs.__call__ + register_module.input = inputs.__call__ def _getpass(prompt): return 'xxx' register_module.getpass.getpass = _getpass @@ -71,8 +71,7 @@ class registerTestCase(PyPIRCCommandTestCase): def __call__(self, *args): # we want to compare them, so let's store # something comparable - els = args[0].items() - els.sort() + els = sorted(args[0].items()) self.calls.append(tuple(els)) return 200, 'OK' -- cgit v1.2.1 From d2c6eebccda5f6a0d0b252729142548561441e0f Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 28 Dec 2008 19:40:56 +0000 Subject: Issue4064: architecture string for universal builds on OSX --- util.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/util.py b/util.py index 262a9b8b..48cc17f6 100644 --- a/util.py +++ b/util.py @@ -100,7 +100,11 @@ def get_platform (): if not macver: macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') - if not macver: + if 1: + # Always calculate the release of the running machine, + # needed to determine if we can build fat binaries or not. + + macrelease = macver # Get the system version. Reading this plist is a documented # way to get the system version (see the documentation for # the Gestalt Manager) @@ -116,16 +120,18 @@ def get_platform (): r'(.*?)', f.read()) f.close() if m is not None: - macver = '.'.join(m.group(1).split('.')[:2]) + macrelease = '.'.join(m.group(1).split('.')[:2]) # else: fall back to the default behaviour + if not macver: + macver = macrelease + if macver: from distutils.sysconfig import get_config_vars release = macver osname = "macosx" - - if (release + '.') >= '10.4.' and \ + if (macrelease + '.') >= '10.4.' and \ '-arch' in get_config_vars().get('CFLAGS', '').strip(): # The universal build will build fat binaries, but not on # systems before 10.4 -- cgit v1.2.1 From be477751d0c5f8a5fb565a0e871a2ad8fb915d45 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 28 Dec 2008 19:42:55 +0000 Subject: Backport of r67988 --- util.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/util.py b/util.py index 262a9b8b..48cc17f6 100644 --- a/util.py +++ b/util.py @@ -100,7 +100,11 @@ def get_platform (): if not macver: macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') - if not macver: + if 1: + # Always calculate the release of the running machine, + # needed to determine if we can build fat binaries or not. + + macrelease = macver # Get the system version. Reading this plist is a documented # way to get the system version (see the documentation for # the Gestalt Manager) @@ -116,16 +120,18 @@ def get_platform (): r'(.*?)', f.read()) f.close() if m is not None: - macver = '.'.join(m.group(1).split('.')[:2]) + macrelease = '.'.join(m.group(1).split('.')[:2]) # else: fall back to the default behaviour + if not macver: + macver = macrelease + if macver: from distutils.sysconfig import get_config_vars release = macver osname = "macosx" - - if (release + '.') >= '10.4.' and \ + if (macrelease + '.') >= '10.4.' and \ '-arch' in get_config_vars().get('CFLAGS', '').strip(): # The universal build will build fat binaries, but not on # systems before 10.4 -- cgit v1.2.1 From 99059691e42d8d443fbb3746eb7b6695226dc166 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 28 Dec 2008 19:50:40 +0000 Subject: Update the fix for issue4064 to deal correctly with all three variants of universal builds that are presented by the configure script. --- util.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 48cc17f6..e9d29ff4 100644 --- a/util.py +++ b/util.py @@ -140,9 +140,13 @@ def get_platform (): # 'universal' instead of 'fat'. machine = 'fat' + cflags = get_config_vars().get('CFLAGS') - if '-arch x86_64' in get_config_vars().get('CFLAGS'): - machine = 'universal' + if '-arch x86_64' in cflags: + if '-arch i386' in cflags: + machine = 'universal' + else: + machine = 'fat64' elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. -- cgit v1.2.1 From 48eeeee501e34167c0245d9a9121da0af6cda5c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 29 Dec 2008 22:23:53 +0000 Subject: fixed #4646 : distutils was choking on empty options arg in the setup function. --- dist.py | 2 +- tests/test_dist.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 9ad94fbe..c15ca977 100644 --- a/dist.py +++ b/dist.py @@ -235,7 +235,7 @@ Common commands: (see '--help-commands' for more) # command options will override any supplied redundantly # through the general options dictionary. options = attrs.get('options') - if options: + if options is not None: del attrs['options'] for (command, cmd_options) in options.items(): opt_dict = self.get_option_dict(command) diff --git a/tests/test_dist.py b/tests/test_dist.py index 6f5fe9c7..bf59c418 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,6 +8,7 @@ import os import StringIO import sys import unittest +import warnings from test.test_support import TESTFN @@ -131,6 +132,29 @@ class DistributionTestCase(unittest.TestCase): if os.path.exists(my_file): os.remove(my_file) + def test_empty_options(self): + # an empty options dictionary should not stay in the + # list of attributes + klass = distutils.dist.Distribution + + # catching warnings + warns = [] + def _warn(msg): + warns.append(msg) + + old_warn = warnings.warn + warnings.warn = _warn + try: + dist = klass(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'options': {}}) + finally: + warnings.warn = old_warn + + self.assertEquals(len(warns), 0) + class MetadataTestCase(unittest.TestCase): def test_simple_metadata(self): -- cgit v1.2.1 From 4ebe0bf0afa6a11df205dd6091326658eb4993b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 29 Dec 2008 22:36:22 +0000 Subject: Merged revisions 68033 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68033 | tarek.ziade | 2008-12-29 23:23:53 +0100 (Mon, 29 Dec 2008) | 1 line fixed #4646 : distutils was choking on empty options arg in the setup function. ........ --- dist.py | 2 +- tests/test_dist.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 9ad94fbe..c15ca977 100644 --- a/dist.py +++ b/dist.py @@ -235,7 +235,7 @@ Common commands: (see '--help-commands' for more) # command options will override any supplied redundantly # through the general options dictionary. options = attrs.get('options') - if options: + if options is not None: del attrs['options'] for (command, cmd_options) in options.items(): opt_dict = self.get_option_dict(command) diff --git a/tests/test_dist.py b/tests/test_dist.py index 6f5fe9c7..bf59c418 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,6 +8,7 @@ import os import StringIO import sys import unittest +import warnings from test.test_support import TESTFN @@ -131,6 +132,29 @@ class DistributionTestCase(unittest.TestCase): if os.path.exists(my_file): os.remove(my_file) + def test_empty_options(self): + # an empty options dictionary should not stay in the + # list of attributes + klass = distutils.dist.Distribution + + # catching warnings + warns = [] + def _warn(msg): + warns.append(msg) + + old_warn = warnings.warn + warnings.warn = _warn + try: + dist = klass(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'options': {}}) + finally: + warnings.warn = old_warn + + self.assertEquals(len(warns), 0) + class MetadataTestCase(unittest.TestCase): def test_simple_metadata(self): -- cgit v1.2.1 From bf2af83b71d70127ece0c4ef573216c7570fca95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 29 Dec 2008 22:38:38 +0000 Subject: Merged revisions 68033 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68033 | tarek.ziade | 2008-12-29 23:23:53 +0100 (Mon, 29 Dec 2008) | 1 line fixed #4646 : distutils was choking on empty options arg in the setup function. ........ --- dist.py | 2 +- tests/test_dist.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 8bcb88c3..7903c2a7 100644 --- a/dist.py +++ b/dist.py @@ -228,7 +228,7 @@ Common commands: (see '--help-commands' for more) # command options will override any supplied redundantly # through the general options dictionary. options = attrs.get('options') - if options: + if options is not None: del attrs['options'] for (command, cmd_options) in options.items(): opt_dict = self.get_option_dict(command) diff --git a/tests/test_dist.py b/tests/test_dist.py index f1b11c17..8e7ef5d3 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -6,6 +6,7 @@ import os import io import sys import unittest +import warnings from test.support import TESTFN @@ -96,6 +97,29 @@ class DistributionTestCase(unittest.TestCase): os.unlink(TESTFN) + def test_empty_options(self): + # an empty options dictionary should not stay in the + # list of attributes + klass = distutils.dist.Distribution + + # catching warnings + warns = [] + def _warn(msg): + warns.append(msg) + + old_warn = warnings.warn + warnings.warn = _warn + try: + dist = klass(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'options': {}}) + finally: + warnings.warn = old_warn + + self.assertEquals(len(warns), 0) + class MetadataTestCase(unittest.TestCase): def test_simple_metadata(self): -- cgit v1.2.1 From de38e65a4ea05f1b0924676bc9fb2ecccd182f40 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 30 Dec 2008 17:56:45 +0000 Subject: Merged revisions 67982,67988,67990 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r67982 | benjamin.peterson | 2008-12-28 09:37:31 -0600 (Sun, 28 Dec 2008) | 1 line fix WORD_BIGEDIAN declaration in Universal builds; fixes #4060 and #4728 ........ r67988 | ronald.oussoren | 2008-12-28 13:40:56 -0600 (Sun, 28 Dec 2008) | 1 line Issue4064: architecture string for universal builds on OSX ........ r67990 | ronald.oussoren | 2008-12-28 13:50:40 -0600 (Sun, 28 Dec 2008) | 3 lines Update the fix for issue4064 to deal correctly with all three variants of universal builds that are presented by the configure script. ........ --- util.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/util.py b/util.py index 28337fa9..042306e9 100644 --- a/util.py +++ b/util.py @@ -99,7 +99,11 @@ def get_platform (): if not macver: macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') - if not macver: + if 1: + # Always calculate the release of the running machine, + # needed to determine if we can build fat binaries or not. + + macrelease = macver # Get the system version. Reading this plist is a documented # way to get the system version (see the documentation for # the Gestalt Manager) @@ -115,16 +119,18 @@ def get_platform (): r'(.*?)', f.read()) f.close() if m is not None: - macver = '.'.join(m.group(1).split('.')[:2]) + macrelease = '.'.join(m.group(1).split('.')[:2]) # else: fall back to the default behaviour + if not macver: + macver = macrelease + if macver: from distutils.sysconfig import get_config_vars release = macver osname = "macosx" - - if (release + '.') >= '10.4.' and \ + if (macrelease + '.') >= '10.4.' and \ '-arch' in get_config_vars().get('CFLAGS', '').strip(): # The universal build will build fat binaries, but not on # systems before 10.4 @@ -133,9 +139,13 @@ def get_platform (): # 'universal' instead of 'fat'. machine = 'fat' + cflags = get_config_vars().get('CFLAGS') - if '-arch x86_64' in get_config_vars().get('CFLAGS'): - machine = 'universal' + if '-arch x86_64' in cflags: + if '-arch i386' in cflags: + machine = 'universal' + else: + machine = 'fat64' elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. -- cgit v1.2.1 From 5a3897bfe4addcf975b3d99bdb1c9b2ecec604bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 30 Dec 2008 23:03:41 +0000 Subject: Fixed #4702: Throwing DistutilsPlatformError instead of IOError under win32 if MSVC is not found --- msvc9compiler.py | 4 ++-- tests/test_msvc9compiler.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/test_msvc9compiler.py diff --git a/msvc9compiler.py b/msvc9compiler.py index 4f72e780..68b77758 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -247,7 +247,7 @@ def query_vcvarsall(version, arch="x86"): result = {} if vcvarsall is None: - raise IOError("Unable to find vcvarsall.bat") + raise DistutilsPlatformError("Unable to find vcvarsall.bat") log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, @@ -255,7 +255,7 @@ def query_vcvarsall(version, arch="x86"): stdout, stderr = popen.communicate() if popen.wait() != 0: - raise IOError(stderr.decode("mbcs")) + raise DistutilsPlatformError(stderr.decode("mbcs")) stdout = stdout.decode("mbcs") for line in stdout.split("\n"): diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py new file mode 100644 index 00000000..1659cea5 --- /dev/null +++ b/tests/test_msvc9compiler.py @@ -0,0 +1,33 @@ +"""Tests for distutils.msvc9compiler.""" +import sys +import unittest + +from distutils.errors import DistutilsPlatformError + +class msvc9compilerTestCase(unittest.TestCase): + + def test_no_compiler(self): + # makes sure query_vcvarsall throws + # a DistutilsPlatformError if the compiler + # is not found + if sys.platform != 'win32': + # this test is only for win32 + return + from distutils.msvc9compiler import query_vcvarsall + def _find_vcvarsall(version): + return None + + from distutils import msvc9compiler + old_find_vcvarsall = msvc9compiler.find_vcvarsall + msvc9compiler.find_vcvarsall = _find_vcvarsall + try: + self.assertRaises(DistutilsPlatformError, query_vcvarsall, + 'wont find this version') + finally: + msvc9compiler.find_vcvarsall = old_find_vcvarsall + +def test_suite(): + return unittest.makeSuite(msvc9compilerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From a2dde29acd7c619ea5af0182c429e4bdf86cfb9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 30 Dec 2008 23:06:46 +0000 Subject: Merged revisions 68081 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68081 | tarek.ziade | 2008-12-31 00:03:41 +0100 (Wed, 31 Dec 2008) | 1 line Fixed #4702: Throwing DistutilsPlatformError instead of IOError under win32 if MSVC is not found ........ --- msvc9compiler.py | 4 ++-- tests/test_msvc9compiler.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/test_msvc9compiler.py diff --git a/msvc9compiler.py b/msvc9compiler.py index 4f72e780..68b77758 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -247,7 +247,7 @@ def query_vcvarsall(version, arch="x86"): result = {} if vcvarsall is None: - raise IOError("Unable to find vcvarsall.bat") + raise DistutilsPlatformError("Unable to find vcvarsall.bat") log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, @@ -255,7 +255,7 @@ def query_vcvarsall(version, arch="x86"): stdout, stderr = popen.communicate() if popen.wait() != 0: - raise IOError(stderr.decode("mbcs")) + raise DistutilsPlatformError(stderr.decode("mbcs")) stdout = stdout.decode("mbcs") for line in stdout.split("\n"): diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py new file mode 100644 index 00000000..1659cea5 --- /dev/null +++ b/tests/test_msvc9compiler.py @@ -0,0 +1,33 @@ +"""Tests for distutils.msvc9compiler.""" +import sys +import unittest + +from distutils.errors import DistutilsPlatformError + +class msvc9compilerTestCase(unittest.TestCase): + + def test_no_compiler(self): + # makes sure query_vcvarsall throws + # a DistutilsPlatformError if the compiler + # is not found + if sys.platform != 'win32': + # this test is only for win32 + return + from distutils.msvc9compiler import query_vcvarsall + def _find_vcvarsall(version): + return None + + from distutils import msvc9compiler + old_find_vcvarsall = msvc9compiler.find_vcvarsall + msvc9compiler.find_vcvarsall = _find_vcvarsall + try: + self.assertRaises(DistutilsPlatformError, query_vcvarsall, + 'wont find this version') + finally: + msvc9compiler.find_vcvarsall = old_find_vcvarsall + +def test_suite(): + return unittest.makeSuite(msvc9compilerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From fcd16e351d5e231cbf218bf33371a1bcbf7a225e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 30 Dec 2008 23:09:20 +0000 Subject: Merged revisions 68081 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68081 | tarek.ziade | 2008-12-31 00:03:41 +0100 (Wed, 31 Dec 2008) | 1 line Fixed #4702: Throwing DistutilsPlatformError instead of IOError under win32 if MSVC is not found ........ --- msvc9compiler.py | 4 ++-- tests/test_msvc9compiler.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/test_msvc9compiler.py diff --git a/msvc9compiler.py b/msvc9compiler.py index eced0524..aae06373 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -247,7 +247,7 @@ def query_vcvarsall(version, arch="x86"): result = {} if vcvarsall is None: - raise IOError("Unable to find vcvarsall.bat") + raise DistutilsPlatformError("Unable to find vcvarsall.bat") log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, @@ -255,7 +255,7 @@ def query_vcvarsall(version, arch="x86"): stdout, stderr = popen.communicate() if popen.wait() != 0: - raise IOError(stderr.decode("mbcs")) + raise DistutilsPlatformError(stderr.decode("mbcs")) stdout = stdout.decode("mbcs") for line in stdout.split("\n"): diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py new file mode 100644 index 00000000..1659cea5 --- /dev/null +++ b/tests/test_msvc9compiler.py @@ -0,0 +1,33 @@ +"""Tests for distutils.msvc9compiler.""" +import sys +import unittest + +from distutils.errors import DistutilsPlatformError + +class msvc9compilerTestCase(unittest.TestCase): + + def test_no_compiler(self): + # makes sure query_vcvarsall throws + # a DistutilsPlatformError if the compiler + # is not found + if sys.platform != 'win32': + # this test is only for win32 + return + from distutils.msvc9compiler import query_vcvarsall + def _find_vcvarsall(version): + return None + + from distutils import msvc9compiler + old_find_vcvarsall = msvc9compiler.find_vcvarsall + msvc9compiler.find_vcvarsall = _find_vcvarsall + try: + self.assertRaises(DistutilsPlatformError, query_vcvarsall, + 'wont find this version') + finally: + msvc9compiler.find_vcvarsall = old_find_vcvarsall + +def test_suite(): + return unittest.makeSuite(msvc9compilerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From b7024702a738bd1e31391b391cc073b97d94c008 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 1 Jan 2009 15:46:10 +0000 Subject: Merged revisions 67952-67953,67955,67957-67958,67960-67961,67963,67965,67967,67970-67971,67973,67982,67988,67990,67995,68014,68016,68030,68057,68061,68112,68115-68118,68120-68121,68123-68128 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r67952 | georg.brandl | 2008-12-27 18:42:40 +0100 (Sat, 27 Dec 2008) | 2 lines #4752: actually use custom handler in example. ........ r67953 | georg.brandl | 2008-12-27 19:20:04 +0100 (Sat, 27 Dec 2008) | 3 lines Patch #4739 by David Laban: add symbols to pydoc help topics, so that ``help('@')`` works as expected. ........ r67955 | georg.brandl | 2008-12-27 19:27:53 +0100 (Sat, 27 Dec 2008) | 3 lines Follow-up to r67746 in order to restore backwards-compatibility for those who (monkey-)patch TextWrapper.wordsep_re with a custom RE. ........ r67957 | georg.brandl | 2008-12-27 19:49:19 +0100 (Sat, 27 Dec 2008) | 2 lines #4754: improve winsound documentation. ........ r67958 | georg.brandl | 2008-12-27 20:02:59 +0100 (Sat, 27 Dec 2008) | 2 lines #4682: 'b' is actually unsigned char. ........ r67960 | georg.brandl | 2008-12-27 20:04:44 +0100 (Sat, 27 Dec 2008) | 2 lines #4695: fix backslashery. ........ r67961 | georg.brandl | 2008-12-27 20:06:04 +0100 (Sat, 27 Dec 2008) | 2 lines Use :samp: role. ........ r67963 | georg.brandl | 2008-12-27 20:11:15 +0100 (Sat, 27 Dec 2008) | 2 lines #4671: document that pydoc imports modules. ........ r67965 | antoine.pitrou | 2008-12-27 21:34:52 +0100 (Sat, 27 Dec 2008) | 3 lines Issue #4677: add two list comprehension tests to pybench. ........ r67967 | benjamin.peterson | 2008-12-27 23:18:58 +0100 (Sat, 27 Dec 2008) | 1 line fix markup ........ r67970 | alexandre.vassalotti | 2008-12-28 02:52:58 +0100 (Sun, 28 Dec 2008) | 2 lines Fix name mangling of PyUnicode_ClearFreeList. ........ r67971 | alexandre.vassalotti | 2008-12-28 03:10:35 +0100 (Sun, 28 Dec 2008) | 2 lines Sort UCS-2/UCS-4 name mangling list. ........ r67973 | alexandre.vassalotti | 2008-12-28 03:58:22 +0100 (Sun, 28 Dec 2008) | 2 lines Document Py_VaBuildValue. ........ r67982 | benjamin.peterson | 2008-12-28 16:37:31 +0100 (Sun, 28 Dec 2008) | 1 line fix WORD_BIGEDIAN declaration in Universal builds; fixes #4060 and #4728 ........ r67988 | ronald.oussoren | 2008-12-28 20:40:56 +0100 (Sun, 28 Dec 2008) | 1 line Issue4064: architecture string for universal builds on OSX ........ r67990 | ronald.oussoren | 2008-12-28 20:50:40 +0100 (Sun, 28 Dec 2008) | 3 lines Update the fix for issue4064 to deal correctly with all three variants of universal builds that are presented by the configure script. ........ r67995 | benjamin.peterson | 2008-12-28 22:16:07 +0100 (Sun, 28 Dec 2008) | 1 line #4763 PyErr_ExceptionMatches won't blow up with NULL arguments ........ r68014 | benjamin.peterson | 2008-12-29 18:47:42 +0100 (Mon, 29 Dec 2008) | 1 line #4764 set IOError.filename when trying to open a directory on POSIX platforms ........ r68016 | benjamin.peterson | 2008-12-29 18:56:58 +0100 (Mon, 29 Dec 2008) | 1 line #4764 in io.open, set IOError.filename when trying to open a directory on POSIX platforms ........ r68030 | benjamin.peterson | 2008-12-29 22:38:14 +0100 (Mon, 29 Dec 2008) | 1 line fix French ........ r68057 | vinay.sajip | 2008-12-30 08:01:25 +0100 (Tue, 30 Dec 2008) | 1 line Minor documentation change relating to NullHandler. ........ r68061 | georg.brandl | 2008-12-30 11:15:49 +0100 (Tue, 30 Dec 2008) | 2 lines #4778: attributes can't be called. ........ r68112 | benjamin.peterson | 2009-01-01 00:48:39 +0100 (Thu, 01 Jan 2009) | 1 line #4795 inspect.isgeneratorfunction() should return False instead of None ........ r68115 | benjamin.peterson | 2009-01-01 05:04:41 +0100 (Thu, 01 Jan 2009) | 1 line simplfy code ........ r68116 | georg.brandl | 2009-01-01 12:46:51 +0100 (Thu, 01 Jan 2009) | 2 lines #4100: note that element children are not necessarily present on "start" events. ........ r68117 | georg.brandl | 2009-01-01 12:53:55 +0100 (Thu, 01 Jan 2009) | 2 lines #4156: make clear that "protocol" is to be replaced with the protocol name. ........ r68118 | georg.brandl | 2009-01-01 13:00:19 +0100 (Thu, 01 Jan 2009) | 2 lines #4185: clarify escape behavior of replacement strings. ........ r68120 | georg.brandl | 2009-01-01 13:15:31 +0100 (Thu, 01 Jan 2009) | 4 lines #4228: Pack negative values the same way as 2.4 in struct's L format. ........ r68121 | georg.brandl | 2009-01-01 13:43:33 +0100 (Thu, 01 Jan 2009) | 2 lines Point to types module in new module deprecation notice. ........ r68123 | georg.brandl | 2009-01-01 13:52:29 +0100 (Thu, 01 Jan 2009) | 2 lines #4784: ... on three counts ... ........ r68124 | georg.brandl | 2009-01-01 13:53:19 +0100 (Thu, 01 Jan 2009) | 2 lines #4782: Fix markup error that hid load() and loads(). ........ r68125 | georg.brandl | 2009-01-01 14:02:09 +0100 (Thu, 01 Jan 2009) | 2 lines #4776: add data_files and package_dir arguments. ........ r68126 | georg.brandl | 2009-01-01 14:05:13 +0100 (Thu, 01 Jan 2009) | 2 lines Handlers are in the `logging.handlers` module. ........ r68127 | georg.brandl | 2009-01-01 14:14:49 +0100 (Thu, 01 Jan 2009) | 2 lines #4767: Use correct submodules for all MIME classes. ........ r68128 | antoine.pitrou | 2009-01-01 15:11:22 +0100 (Thu, 01 Jan 2009) | 3 lines Issue #3680: Reference cycles created through a dict, set or deque iterator did not get collected. ........ --- util.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 48cc17f6..e9d29ff4 100644 --- a/util.py +++ b/util.py @@ -140,9 +140,13 @@ def get_platform (): # 'universal' instead of 'fat'. machine = 'fat' + cflags = get_config_vars().get('CFLAGS') - if '-arch x86_64' in get_config_vars().get('CFLAGS'): - machine = 'universal' + if '-arch x86_64' in cflags: + if '-arch i386' in cflags: + machine = 'universal' + else: + machine = 'fat64' elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. -- cgit v1.2.1 From 093c9524d2419914fb3d24a8d7f231c77432a3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 4 Jan 2009 00:04:49 +0000 Subject: fixed #1702551: distutils sdist was not pruning VCS directories under win32 --- command/sdist.py | 8 +++- tests/test_sdist.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/test_sdist.py diff --git a/command/sdist.py b/command/sdist.py index 961256c3..e10e25b3 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -7,6 +7,7 @@ Implements the Distutils 'sdist' command (create a source distribution).""" __revision__ = "$Id$" import os, string +import sys from types import * from glob import glob from distutils.core import Command @@ -354,8 +355,13 @@ class sdist (Command): self.filelist.exclude_pattern(None, prefix=build.build_base) self.filelist.exclude_pattern(None, prefix=base_dir) - self.filelist.exclude_pattern(r'(^|/)(RCS|CVS|\.svn|\.hg|\.git|\.bzr|_darcs)/.*', is_regex=1) + # pruning out vcs directories + # both separators are used under win32 + seps = sys.platform == 'win32' and r'/|\\' or '/' + vcs_dirs = ['RCS', 'CVS', '\.svn', '\.hg', '\.git', '\.bzr', '_darcs'] + vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) + self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) def write_manifest (self): """Write the file list in 'self.filelist' (presumably as filled in diff --git a/tests/test_sdist.py b/tests/test_sdist.py new file mode 100644 index 00000000..7cc04cfc --- /dev/null +++ b/tests/test_sdist.py @@ -0,0 +1,110 @@ +"""Tests for distutils.command.sdist.""" +import os +import unittest +import shutil +import zipfile +from os.path import join + +from distutils.command.sdist import sdist +from distutils.core import Distribution +from distutils.tests.test_config import PyPIRCCommandTestCase + +CURDIR = os.path.dirname(__file__) +TEMP_PKG = join(CURDIR, 'temppkg') + +SETUP_PY = """ +from distutils.core import setup +import somecode + +setup(name='fake') +""" + +MANIFEST_IN = """ +recursive-include somecode * +""" + +class sdistTestCase(PyPIRCCommandTestCase): + + def setUp(self): + PyPIRCCommandTestCase.setUp(self) + self.old_path = os.getcwd() + + def tearDown(self): + os.chdir(self.old_path) + if os.path.exists(TEMP_PKG): + shutil.rmtree(TEMP_PKG) + PyPIRCCommandTestCase.tearDown(self) + + def _write(self, path, content): + f = open(path, 'w') + try: + f.write(content) + finally: + f.close() + + def test_prune_file_list(self): + # this test creates a package with some vcs dirs in it + # and launch sdist to make sure they get pruned + # on all systems + if not os.path.exists(TEMP_PKG): + os.mkdir(TEMP_PKG) + os.mkdir(join(TEMP_PKG, 'somecode')) + + # creating a MANIFEST, a package, and a README + self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) + self._write(join(TEMP_PKG, 'README'), 'xxx') + self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') + self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) + + # creating VCS directories with some files in them + os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) + self._write(join(TEMP_PKG, 'somecode', '.svn', 'ok.py'), 'xxx') + + os.mkdir(join(TEMP_PKG, 'somecode', '.hg')) + self._write(join(TEMP_PKG, 'somecode', '.hg', + 'ok'), 'xxx') + + os.mkdir(join(TEMP_PKG, 'somecode', '.git')) + self._write(join(TEMP_PKG, 'somecode', '.git', + 'ok'), 'xxx') + + os.chdir(TEMP_PKG) + + # now building a sdist + dist = Distribution() + dist.script_name = 'setup.py' + dist.metadata.name = 'fake' + dist.metadata.version = '1.0' + dist.metadata.url = 'http://xxx' + dist.metadata.author = dist.metadata.author_email = 'xxx' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.manifest = 'MANIFEST' + cmd.template = 'MANIFEST.in' + cmd.dist_dir = 'dist' + + # zip is available universally + # (tar might not be installed under win32) + cmd.formats = ['zip'] + cmd.run() + + # now let's check what we have + dist_folder = join(TEMP_PKG, 'dist') + files = os.listdir(dist_folder) + self.assertEquals(files, ['fake-1.0.zip']) + + zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) + try: + content = zip_file.namelist() + finally: + zip_file.close() + + # making sure everything has been pruned correctly + self.assertEquals(len(content), 4) + +def test_suite(): + return unittest.makeSuite(sdistTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From dea3bab1f658e3f4e696dc1f74e5c41cb4ade04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 4 Jan 2009 00:07:14 +0000 Subject: Merged revisions 68276 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68276 | tarek.ziade | 2009-01-04 01:04:49 +0100 (Sun, 04 Jan 2009) | 1 line fixed #1702551: distutils sdist was not pruning VCS directories under win32 ........ --- command/sdist.py | 8 +++- tests/test_sdist.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 tests/test_sdist.py diff --git a/command/sdist.py b/command/sdist.py index 961256c3..e10e25b3 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -7,6 +7,7 @@ Implements the Distutils 'sdist' command (create a source distribution).""" __revision__ = "$Id$" import os, string +import sys from types import * from glob import glob from distutils.core import Command @@ -354,8 +355,13 @@ class sdist (Command): self.filelist.exclude_pattern(None, prefix=build.build_base) self.filelist.exclude_pattern(None, prefix=base_dir) - self.filelist.exclude_pattern(r'(^|/)(RCS|CVS|\.svn|\.hg|\.git|\.bzr|_darcs)/.*', is_regex=1) + # pruning out vcs directories + # both separators are used under win32 + seps = sys.platform == 'win32' and r'/|\\' or '/' + vcs_dirs = ['RCS', 'CVS', '\.svn', '\.hg', '\.git', '\.bzr', '_darcs'] + vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) + self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) def write_manifest (self): """Write the file list in 'self.filelist' (presumably as filled in diff --git a/tests/test_sdist.py b/tests/test_sdist.py new file mode 100644 index 00000000..7cc04cfc --- /dev/null +++ b/tests/test_sdist.py @@ -0,0 +1,110 @@ +"""Tests for distutils.command.sdist.""" +import os +import unittest +import shutil +import zipfile +from os.path import join + +from distutils.command.sdist import sdist +from distutils.core import Distribution +from distutils.tests.test_config import PyPIRCCommandTestCase + +CURDIR = os.path.dirname(__file__) +TEMP_PKG = join(CURDIR, 'temppkg') + +SETUP_PY = """ +from distutils.core import setup +import somecode + +setup(name='fake') +""" + +MANIFEST_IN = """ +recursive-include somecode * +""" + +class sdistTestCase(PyPIRCCommandTestCase): + + def setUp(self): + PyPIRCCommandTestCase.setUp(self) + self.old_path = os.getcwd() + + def tearDown(self): + os.chdir(self.old_path) + if os.path.exists(TEMP_PKG): + shutil.rmtree(TEMP_PKG) + PyPIRCCommandTestCase.tearDown(self) + + def _write(self, path, content): + f = open(path, 'w') + try: + f.write(content) + finally: + f.close() + + def test_prune_file_list(self): + # this test creates a package with some vcs dirs in it + # and launch sdist to make sure they get pruned + # on all systems + if not os.path.exists(TEMP_PKG): + os.mkdir(TEMP_PKG) + os.mkdir(join(TEMP_PKG, 'somecode')) + + # creating a MANIFEST, a package, and a README + self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) + self._write(join(TEMP_PKG, 'README'), 'xxx') + self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') + self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) + + # creating VCS directories with some files in them + os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) + self._write(join(TEMP_PKG, 'somecode', '.svn', 'ok.py'), 'xxx') + + os.mkdir(join(TEMP_PKG, 'somecode', '.hg')) + self._write(join(TEMP_PKG, 'somecode', '.hg', + 'ok'), 'xxx') + + os.mkdir(join(TEMP_PKG, 'somecode', '.git')) + self._write(join(TEMP_PKG, 'somecode', '.git', + 'ok'), 'xxx') + + os.chdir(TEMP_PKG) + + # now building a sdist + dist = Distribution() + dist.script_name = 'setup.py' + dist.metadata.name = 'fake' + dist.metadata.version = '1.0' + dist.metadata.url = 'http://xxx' + dist.metadata.author = dist.metadata.author_email = 'xxx' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.manifest = 'MANIFEST' + cmd.template = 'MANIFEST.in' + cmd.dist_dir = 'dist' + + # zip is available universally + # (tar might not be installed under win32) + cmd.formats = ['zip'] + cmd.run() + + # now let's check what we have + dist_folder = join(TEMP_PKG, 'dist') + files = os.listdir(dist_folder) + self.assertEquals(files, ['fake-1.0.zip']) + + zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) + try: + content = zip_file.namelist() + finally: + zip_file.close() + + # making sure everything has been pruned correctly + self.assertEquals(len(content), 4) + +def test_suite(): + return unittest.makeSuite(sdistTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 3fa05608d6cbcdf5d59824a1edde412e5440ae8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 4 Jan 2009 10:37:52 +0000 Subject: using clearer syntax --- command/sdist.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index e10e25b3..27883fd6 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -358,8 +358,13 @@ class sdist (Command): # pruning out vcs directories # both separators are used under win32 - seps = sys.platform == 'win32' and r'/|\\' or '/' - vcs_dirs = ['RCS', 'CVS', '\.svn', '\.hg', '\.git', '\.bzr', '_darcs'] + if sys.platform == 'win32': + seps = r'/|\\' + else: + seps = '/' + + vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', + '_darcs'] vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) -- cgit v1.2.1 From 6d66c0011b6f80f3e04ed13872bddc2e48b12056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 4 Jan 2009 10:43:32 +0000 Subject: Merged revisions 68293 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68293 | tarek.ziade | 2009-01-04 11:37:52 +0100 (Sun, 04 Jan 2009) | 1 line using clearer syntax ........ --- command/sdist.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index e10e25b3..27883fd6 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -358,8 +358,13 @@ class sdist (Command): # pruning out vcs directories # both separators are used under win32 - seps = sys.platform == 'win32' and r'/|\\' or '/' - vcs_dirs = ['RCS', 'CVS', '\.svn', '\.hg', '\.git', '\.bzr', '_darcs'] + if sys.platform == 'win32': + seps = r'/|\\' + else: + seps = '/' + + vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', + '_darcs'] vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) -- cgit v1.2.1 From ecf83b1f901da44a7408bda6aba7d408b4f8155a Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Wed, 7 Jan 2009 09:42:28 +0000 Subject: Issue #4864: test_msvc9compiler failed on VC6/7. Reviewed by Amaury Forgeot d'Arc. --- tests/test_msvc9compiler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 1659cea5..0c8bd6e0 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -13,6 +13,10 @@ class msvc9compilerTestCase(unittest.TestCase): if sys.platform != 'win32': # this test is only for win32 return + from distutils.msvccompiler import get_build_version + if get_build_version() < 8.0: + # this test is only for MSVC8.0 or above + return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None -- cgit v1.2.1 From 18a095cf8cedcd8e615a6dd6d71634b2fe35bb70 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Wed, 7 Jan 2009 09:53:35 +0000 Subject: Merged revisions 68373 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68373 | hirokazu.yamamoto | 2009-01-07 18:42:28 +0900 | 2 lines Issue #4864: test_msvc9compiler failed on VC6/7. Reviewed by Amaury Forgeot d'Arc. ........ --- tests/test_msvc9compiler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 1659cea5..0c8bd6e0 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -13,6 +13,10 @@ class msvc9compilerTestCase(unittest.TestCase): if sys.platform != 'win32': # this test is only for win32 return + from distutils.msvccompiler import get_build_version + if get_build_version() < 8.0: + # this test is only for MSVC8.0 or above + return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None -- cgit v1.2.1 From 7da96ed6b364ce84ab7b0f2c563fda2779f641ec Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Wed, 7 Jan 2009 10:11:17 +0000 Subject: Merged revisions 68373 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68373 | hirokazu.yamamoto | 2009-01-07 18:42:28 +0900 | 2 lines Issue #4864: test_msvc9compiler failed on VC6/7. Reviewed by Amaury Forgeot d'Arc. ........ --- tests/test_msvc9compiler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 1659cea5..0c8bd6e0 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -13,6 +13,10 @@ class msvc9compilerTestCase(unittest.TestCase): if sys.platform != 'win32': # this test is only for win32 return + from distutils.msvccompiler import get_build_version + if get_build_version() < 8.0: + # this test is only for MSVC8.0 or above + return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None -- cgit v1.2.1 From 31ca6309c1297c8539b0e6c54f6743c58f841922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 8 Jan 2009 23:56:31 +0000 Subject: fixed #4394 make the storage of the password optional in .pypirc --- command/register.py | 30 ++++++++++-------- command/upload.py | 5 +++ config.py | 4 +-- dist.py | 1 + tests/test_register.py | 86 ++++++++++++++++++++++++++++++++++++-------------- tests/test_upload.py | 29 +++++++++++++++++ 6 files changed, 117 insertions(+), 38 deletions(-) diff --git a/command/register.py b/command/register.py index bf7be960..40661d87 100644 --- a/command/register.py +++ b/command/register.py @@ -173,19 +173,23 @@ Your selection [default 1]: ''', log.INFO) log.INFO) # possibly save the login - if not self.has_config and code == 200: - self.announce(('I can store your PyPI login so future ' - 'submissions will be faster.'), log.INFO) - self.announce('(the login will be stored in %s)' % \ - self._get_rc_file(), log.INFO) - - choice = 'X' - while choice.lower() not in 'yn': - choice = raw_input('Save your login (y/N)?') - if not choice: - choice = 'n' - if choice.lower() == 'y': - self._store_pypirc(username, password) + if code == 200: + if self.has_config: + # sharing the password in the distribution instance + # so the upload command can reuse it + self.distribution.password = password + else: + self.announce(('I can store your PyPI login so future ' + 'submissions will be faster.'), log.INFO) + self.announce('(the login will be stored in %s)' % \ + self._get_rc_file(), log.INFO) + choice = 'X' + while choice.lower() not in 'yn': + choice = raw_input('Save your login (y/N)?') + if not choice: + choice = 'n' + if choice.lower() == 'y': + self._store_pypirc(username, password) elif choice == '2': data = {':action': 'user'} diff --git a/command/upload.py b/command/upload.py index 8805d41d..e30347e5 100644 --- a/command/upload.py +++ b/command/upload.py @@ -50,6 +50,11 @@ class upload(PyPIRCCommand): self.repository = config['repository'] self.realm = config['realm'] + # getting the password from the distribution + # if previously set by the register command + if not self.password and self.distribution.password: + self.password = self.distribution.password + def run(self): if not self.distribution.dist_files: raise DistutilsOptionError("No dist file created in earlier command") diff --git a/config.py b/config.py index 4186c9b1..9166199e 100644 --- a/config.py +++ b/config.py @@ -82,12 +82,12 @@ class PyPIRCCommand(Command): for server in _servers: current = {'server': server} current['username'] = config.get(server, 'username') - current['password'] = config.get(server, 'password') # optional params for key, default in (('repository', self.DEFAULT_REPOSITORY), - ('realm', self.DEFAULT_REALM)): + ('realm', self.DEFAULT_REALM), + ('password', None)): if config.has_option(server, key): current[key] = config.get(server, key) else: diff --git a/dist.py b/dist.py index c15ca977..18cc9109 100644 --- a/dist.py +++ b/dist.py @@ -206,6 +206,7 @@ Common commands: (see '--help-commands' for more) self.extra_path = None self.scripts = None self.data_files = None + self.password = '' # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to diff --git a/tests/test_register.py b/tests/test_register.py index 3a3a3b73..b3543f50 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -2,6 +2,7 @@ import sys import os import unittest +import getpass from distutils.command.register import register from distutils.core import Distribution @@ -9,6 +10,26 @@ from distutils.core import Distribution from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase +PYPIRC_NOPASSWORD = """\ +[distutils] + +index-servers = + server1 + +[server1] +username:me +""" + +WANTED_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:password +""" + class RawInputs(object): """Fakes user inputs.""" def __init__(self, *answers): @@ -21,18 +42,33 @@ class RawInputs(object): finally: self.index += 1 -WANTED_PYPIRC = """\ -[distutils] -index-servers = - pypi +class FakeServer(object): + """Fakes a PyPI server""" + def __init__(self): + self.calls = [] -[pypi] -username:tarek -password:xxx -""" + def __call__(self, *args): + # we want to compare them, so let's store + # something comparable + els = args[0].items() + els.sort() + self.calls.append(tuple(els)) + return 200, 'OK' class registerTestCase(PyPIRCCommandTestCase): + def setUp(self): + PyPIRCCommandTestCase.setUp(self) + # patching the password prompt + self._old_getpass = getpass.getpass + def _getpass(prompt): + return 'password' + getpass.getpass = _getpass + + def tearDown(self): + getpass.getpass = self._old_getpass + PyPIRCCommandTestCase.tearDown(self) + def test_create_pypirc(self): # this test makes sure a .pypirc file # is created when requested. @@ -56,25 +92,11 @@ class registerTestCase(PyPIRCCommandTestCase): # Here's what we are faking : # use your existing login (choice 1.) # Username : 'tarek' - # Password : 'xxx' + # Password : 'password' # Save your login (y/N)? : 'y' inputs = RawInputs('1', 'tarek', 'y') from distutils.command import register as register_module register_module.raw_input = inputs.__call__ - def _getpass(prompt): - return 'xxx' - register_module.getpass.getpass = _getpass - class FakeServer(object): - def __init__(self): - self.calls = [] - - def __call__(self, *args): - # we want to compare them, so let's store - # something comparable - els = args[0].items() - els.sort() - self.calls.append(tuple(els)) - return 200, 'OK' cmd.post_to_server = pypi_server = FakeServer() @@ -102,6 +124,24 @@ class registerTestCase(PyPIRCCommandTestCase): self.assert_(len(pypi_server.calls), 2) self.assert_(pypi_server.calls[0], pypi_server.calls[1]) + def test_password_not_in_file(self): + + f = open(self.rc, 'w') + f.write(PYPIRC_NOPASSWORD) + f.close() + + dist = Distribution() + cmd = register(dist) + cmd.post_to_server = FakeServer() + + cmd._set_config() + cmd.finalize_options() + cmd.send_metadata() + + # dist.password should be set + # therefore used afterwards by other commands + self.assertEquals(dist.password, 'password') + def test_suite(): return unittest.makeSuite(registerTestCase) diff --git a/tests/test_upload.py b/tests/test_upload.py index b05ab1f7..3f8ca6d6 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -9,6 +9,17 @@ from distutils.core import Distribution from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase +PYPIRC_NOPASSWORD = """\ +[distutils] + +index-servers = + server1 + +[server1] +username:me +""" + + class uploadTestCase(PyPIRCCommandTestCase): def test_finalize_options(self): @@ -26,6 +37,24 @@ class uploadTestCase(PyPIRCCommandTestCase): ('repository', 'http://pypi.python.org/pypi')): self.assertEquals(getattr(cmd, attr), waited) + def test_saved_password(self): + # file with no password + f = open(self.rc, 'w') + f.write(PYPIRC_NOPASSWORD) + f.close() + + # make sure it passes + dist = Distribution() + cmd = upload(dist) + cmd.finalize_options() + self.assertEquals(cmd.password, None) + + # make sure we get it as well, if another command + # initialized it at the dist level + dist.password = 'xxx' + cmd = upload(dist) + cmd.finalize_options() + self.assertEquals(cmd.password, 'xxx') def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From 5b32d845dc21e255e9b1d40c5aa7b12aa9149271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 9 Jan 2009 00:15:45 +0000 Subject: Merged revisions 68415 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68415 | tarek.ziade | 2009-01-09 00:56:31 +0100 (Fri, 09 Jan 2009) | 1 line fixed #4394 make the storage of the password optional in .pypirc ........ --- command/register.py | 30 +++++++++-------- command/upload.py | 5 +++ config.py | 4 +-- dist.py | 1 + tests/test_register.py | 91 ++++++++++++++++++++++++++++++++++++-------------- tests/test_upload.py | 29 ++++++++++++++++ 6 files changed, 120 insertions(+), 40 deletions(-) diff --git a/command/register.py b/command/register.py index 30e9a37e..c271b18c 100644 --- a/command/register.py +++ b/command/register.py @@ -174,19 +174,23 @@ Your selection [default 1]: ''', log.INFO) log.INFO) # possibly save the login - if not self.has_config and code == 200: - self.announce(('I can store your PyPI login so future ' - 'submissions will be faster.'), log.INFO) - self.announce('(the login will be stored in %s)' % \ - self._get_rc_file(), log.INFO) - - choice = 'X' - while choice.lower() not in 'yn': - choice = input('Save your login (y/N)?') - if not choice: - choice = 'n' - if choice.lower() == 'y': - self._store_pypirc(username, password) + if code == 200: + if self.has_config: + # sharing the password in the distribution instance + # so the upload command can reuse it + self.distribution.password = password + else: + self.announce(('I can store your PyPI login so future ' + 'submissions will be faster.'), log.INFO) + self.announce('(the login will be stored in %s)' % \ + self._get_rc_file(), log.INFO) + choice = 'X' + while choice.lower() not in 'yn': + choice = input('Save your login (y/N)?') + if not choice: + choice = 'n' + if choice.lower() == 'y': + self._store_pypirc(username, password) elif choice == '2': data = {':action': 'user'} diff --git a/command/upload.py b/command/upload.py index 7ba7f588..020e860a 100644 --- a/command/upload.py +++ b/command/upload.py @@ -48,6 +48,11 @@ class upload(PyPIRCCommand): self.repository = config['repository'] self.realm = config['realm'] + # getting the password from the distribution + # if previously set by the register command + if not self.password and self.distribution.password: + self.password = self.distribution.password + def run(self): if not self.distribution.dist_files: raise DistutilsOptionError("No dist file created in earlier command") diff --git a/config.py b/config.py index 73f32604..5b625f3f 100644 --- a/config.py +++ b/config.py @@ -82,12 +82,12 @@ class PyPIRCCommand(Command): for server in _servers: current = {'server': server} current['username'] = config.get(server, 'username') - current['password'] = config.get(server, 'password') # optional params for key, default in (('repository', self.DEFAULT_REPOSITORY), - ('realm', self.DEFAULT_REALM)): + ('realm', self.DEFAULT_REALM), + ('password', None)): if config.has_option(server, key): current[key] = config.get(server, key) else: diff --git a/dist.py b/dist.py index 7903c2a7..6c4b4afb 100644 --- a/dist.py +++ b/dist.py @@ -199,6 +199,7 @@ Common commands: (see '--help-commands' for more) self.extra_path = None self.scripts = None self.data_files = None + self.password = '' # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to diff --git a/tests/test_register.py b/tests/test_register.py index 021b3ea8..8826e90f 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -2,6 +2,7 @@ import sys import os import unittest +import getpass from distutils.command.register import register from distutils.core import Distribution @@ -9,7 +10,27 @@ from distutils.core import Distribution from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase -class RawInputs(object): +PYPIRC_NOPASSWORD = """\ +[distutils] + +index-servers = + server1 + +[server1] +username:me +""" + +WANTED_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:password +""" + +class Inputs(object): """Fakes user inputs.""" def __init__(self, *answers): self.answers = answers @@ -21,18 +42,33 @@ class RawInputs(object): finally: self.index += 1 -WANTED_PYPIRC = """\ -[distutils] -index-servers = - pypi +class FakeServer(object): + """Fakes a PyPI server""" + def __init__(self): + self.calls = [] -[pypi] -username:tarek -password:xxx -""" + def __call__(self, *args): + # we want to compare them, so let's store + # something comparable + els = list(args[0].items()) + els.sort() + self.calls.append(tuple(els)) + return 200, 'OK' class registerTestCase(PyPIRCCommandTestCase): + def setUp(self): + PyPIRCCommandTestCase.setUp(self) + # patching the password prompt + self._old_getpass = getpass.getpass + def _getpass(prompt): + return 'password' + getpass.getpass = _getpass + + def tearDown(self): + getpass.getpass = self._old_getpass + PyPIRCCommandTestCase.tearDown(self) + def test_create_pypirc(self): # this test makes sure a .pypirc file # is created when requested. @@ -50,30 +86,17 @@ class registerTestCase(PyPIRCCommandTestCase): # we shouldn't have a .pypirc file yet self.assert_(not os.path.exists(self.rc)) - # patching raw_input and getpass.getpass + # patching input and getpass.getpass # so register gets happy # # Here's what we are faking : # use your existing login (choice 1.) # Username : 'tarek' - # Password : 'xxx' + # Password : 'password' # Save your login (y/N)? : 'y' - inputs = RawInputs('1', 'tarek', 'y') + inputs = Inputs('1', 'tarek', 'y') from distutils.command import register as register_module register_module.input = inputs.__call__ - def _getpass(prompt): - return 'xxx' - register_module.getpass.getpass = _getpass - class FakeServer(object): - def __init__(self): - self.calls = [] - - def __call__(self, *args): - # we want to compare them, so let's store - # something comparable - els = sorted(args[0].items()) - self.calls.append(tuple(els)) - return 200, 'OK' cmd.post_to_server = pypi_server = FakeServer() @@ -101,6 +124,24 @@ class registerTestCase(PyPIRCCommandTestCase): self.assert_(len(pypi_server.calls), 2) self.assert_(pypi_server.calls[0], pypi_server.calls[1]) + def test_password_not_in_file(self): + + f = open(self.rc, 'w') + f.write(PYPIRC_NOPASSWORD) + f.close() + + dist = Distribution() + cmd = register(dist) + cmd.post_to_server = FakeServer() + + cmd._set_config() + cmd.finalize_options() + cmd.send_metadata() + + # dist.password should be set + # therefore used afterwards by other commands + self.assertEquals(dist.password, 'password') + def test_suite(): return unittest.makeSuite(registerTestCase) diff --git a/tests/test_upload.py b/tests/test_upload.py index b05ab1f7..3f8ca6d6 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -9,6 +9,17 @@ from distutils.core import Distribution from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase +PYPIRC_NOPASSWORD = """\ +[distutils] + +index-servers = + server1 + +[server1] +username:me +""" + + class uploadTestCase(PyPIRCCommandTestCase): def test_finalize_options(self): @@ -26,6 +37,24 @@ class uploadTestCase(PyPIRCCommandTestCase): ('repository', 'http://pypi.python.org/pypi')): self.assertEquals(getattr(cmd, attr), waited) + def test_saved_password(self): + # file with no password + f = open(self.rc, 'w') + f.write(PYPIRC_NOPASSWORD) + f.close() + + # make sure it passes + dist = Distribution() + cmd = upload(dist) + cmd.finalize_options() + self.assertEquals(cmd.password, None) + + # make sure we get it as well, if another command + # initialized it at the dist level + dist.password = 'xxx' + cmd = upload(dist) + cmd.finalize_options() + self.assertEquals(cmd.password, 'xxx') def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From d5b7603836bc49a68327aaa343590cb4e051cdf4 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 9 Jan 2009 04:11:44 +0000 Subject: Merged revisions 68167,68276,68292-68293,68344 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68167 | vinay.sajip | 2009-01-02 12:53:04 -0600 (Fri, 02 Jan 2009) | 1 line Minor documentation changes relating to NullHandler, the module used for handlers and references to ConfigParser. ........ r68276 | tarek.ziade | 2009-01-03 18:04:49 -0600 (Sat, 03 Jan 2009) | 1 line fixed #1702551: distutils sdist was not pruning VCS directories under win32 ........ r68292 | skip.montanaro | 2009-01-04 04:36:58 -0600 (Sun, 04 Jan 2009) | 3 lines If user configures --without-gcc give preference to $CC instead of blindly assuming the compiler will be "cc". ........ r68293 | tarek.ziade | 2009-01-04 04:37:52 -0600 (Sun, 04 Jan 2009) | 1 line using clearer syntax ........ r68344 | marc-andre.lemburg | 2009-01-05 13:43:35 -0600 (Mon, 05 Jan 2009) | 7 lines Fix #4846 (Py_UNICODE_ISSPACE causes linker error) by moving the declaration into the extern "C" section. Add a few more comments and apply some minor edits to make the file contents fit the original structure again. ........ --- command/sdist.py | 18 +++++++-- tests/test_sdist.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 tests/test_sdist.py diff --git a/command/sdist.py b/command/sdist.py index b29fc64e..96fb7fa1 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -4,7 +4,10 @@ Implements the Distutils 'sdist' command (create a source distribution).""" __revision__ = "$Id$" -import sys, os +import os +import string +import sys +from types import * from glob import glob from distutils.core import Command from distutils import dir_util, dep_util, file_util, archive_util @@ -332,9 +335,18 @@ class sdist (Command): self.filelist.exclude_pattern(None, prefix=build.build_base) self.filelist.exclude_pattern(None, prefix=base_dir) - self.filelist.exclude_pattern(r'(^|/)(RCS|CVS|\.svn|\.hg|\.git|\.bzr|_darcs)/.*', is_regex=1) - def write_manifest(self): + if sys.platform == 'win32': + seps = r'/|\\' + else: + seps = '/' + + vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', + '_darcs'] + vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) + self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) + + def write_manifest (self): """Write the file list in 'self.filelist' (presumably as filled in by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. diff --git a/tests/test_sdist.py b/tests/test_sdist.py new file mode 100644 index 00000000..7cc04cfc --- /dev/null +++ b/tests/test_sdist.py @@ -0,0 +1,110 @@ +"""Tests for distutils.command.sdist.""" +import os +import unittest +import shutil +import zipfile +from os.path import join + +from distutils.command.sdist import sdist +from distutils.core import Distribution +from distutils.tests.test_config import PyPIRCCommandTestCase + +CURDIR = os.path.dirname(__file__) +TEMP_PKG = join(CURDIR, 'temppkg') + +SETUP_PY = """ +from distutils.core import setup +import somecode + +setup(name='fake') +""" + +MANIFEST_IN = """ +recursive-include somecode * +""" + +class sdistTestCase(PyPIRCCommandTestCase): + + def setUp(self): + PyPIRCCommandTestCase.setUp(self) + self.old_path = os.getcwd() + + def tearDown(self): + os.chdir(self.old_path) + if os.path.exists(TEMP_PKG): + shutil.rmtree(TEMP_PKG) + PyPIRCCommandTestCase.tearDown(self) + + def _write(self, path, content): + f = open(path, 'w') + try: + f.write(content) + finally: + f.close() + + def test_prune_file_list(self): + # this test creates a package with some vcs dirs in it + # and launch sdist to make sure they get pruned + # on all systems + if not os.path.exists(TEMP_PKG): + os.mkdir(TEMP_PKG) + os.mkdir(join(TEMP_PKG, 'somecode')) + + # creating a MANIFEST, a package, and a README + self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) + self._write(join(TEMP_PKG, 'README'), 'xxx') + self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') + self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) + + # creating VCS directories with some files in them + os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) + self._write(join(TEMP_PKG, 'somecode', '.svn', 'ok.py'), 'xxx') + + os.mkdir(join(TEMP_PKG, 'somecode', '.hg')) + self._write(join(TEMP_PKG, 'somecode', '.hg', + 'ok'), 'xxx') + + os.mkdir(join(TEMP_PKG, 'somecode', '.git')) + self._write(join(TEMP_PKG, 'somecode', '.git', + 'ok'), 'xxx') + + os.chdir(TEMP_PKG) + + # now building a sdist + dist = Distribution() + dist.script_name = 'setup.py' + dist.metadata.name = 'fake' + dist.metadata.version = '1.0' + dist.metadata.url = 'http://xxx' + dist.metadata.author = dist.metadata.author_email = 'xxx' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.manifest = 'MANIFEST' + cmd.template = 'MANIFEST.in' + cmd.dist_dir = 'dist' + + # zip is available universally + # (tar might not be installed under win32) + cmd.formats = ['zip'] + cmd.run() + + # now let's check what we have + dist_folder = join(TEMP_PKG, 'dist') + files = os.listdir(dist_folder) + self.assertEquals(files, ['fake-1.0.zip']) + + zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) + try: + content = zip_file.namelist() + finally: + zip_file.close() + + # making sure everything has been pruned correctly + self.assertEquals(len(content), 4) + +def test_suite(): + return unittest.makeSuite(sdistTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 5a672e9f309ce3c1bf808e839c00369b7e75a25d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 20 Jan 2009 07:24:44 +0000 Subject: Issue 4998: Decimal should not subclass or register with numbers.Real. --- command/wininst-8.0.exe | Bin 61440 -> 73728 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-8.0.exe b/command/wininst-8.0.exe index 7403bfab..7407032d 100644 Binary files a/command/wininst-8.0.exe and b/command/wininst-8.0.exe differ -- cgit v1.2.1 From 135be1455bb46863549fadf72cb99b0c30c9bece Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 20 Jan 2009 21:25:32 +0000 Subject: Revert part of r68799 which unintentionally updated this file. --- command/wininst-8.0.exe | Bin 73728 -> 61440 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-8.0.exe b/command/wininst-8.0.exe index 7407032d..7403bfab 100644 Binary files a/command/wininst-8.0.exe and b/command/wininst-8.0.exe differ -- cgit v1.2.1 From 31b6b08aad43d5139d126dee38b1de471914423f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 25 Jan 2009 18:19:25 +0000 Subject: Fixed #4863: removed distutils.mwerkscompiler --- ccompiler.py | 3 - mwerkscompiler.py | 248 ------------------------------------------------------ 2 files changed, 251 deletions(-) delete mode 100644 mwerkscompiler.py diff --git a/ccompiler.py b/ccompiler.py index 87d6e273..aa92a9f1 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1063,7 +1063,6 @@ _default_compilers = ( # OS name mappings ('posix', 'unix'), ('nt', 'msvc'), - ('mac', 'mwerks'), ) @@ -1103,8 +1102,6 @@ compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', "Mingw32 port of GNU C Compiler for Win32"), 'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"), - 'mwerks': ('mwerkscompiler', 'MWerksCompiler', - "MetroWerks CodeWarrior"), 'emx': ('emxccompiler', 'EMXCCompiler', "EMX port of GNU C Compiler for OS/2"), } diff --git a/mwerkscompiler.py b/mwerkscompiler.py deleted file mode 100644 index 343c6cec..00000000 --- a/mwerkscompiler.py +++ /dev/null @@ -1,248 +0,0 @@ -"""distutils.mwerkscompiler - -Contains MWerksCompiler, an implementation of the abstract CCompiler class -for MetroWerks CodeWarrior on the Macintosh. Needs work to support CW on -Windows.""" - -# This module should be kept compatible with Python 2.1. - -__revision__ = "$Id$" - -import sys, os, string -from types import * -from distutils.errors import \ - DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options -import distutils.util -import distutils.dir_util -from distutils import log - -class MWerksCompiler (CCompiler) : - """Concrete class that implements an interface to MetroWerks CodeWarrior, - as defined by the CCompiler abstract class.""" - - compiler_type = 'mwerks' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.r'] - _exp_extension = '.exp' - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = (_c_extensions + _cpp_extensions + - _rc_extensions) - res_extension = '.rsrc' - obj_extension = '.obj' # Not used, really - static_lib_extension = '.lib' - shared_lib_extension = '.slb' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '' - - - def __init__ (self, - verbose=0, - dry_run=0, - force=0): - - CCompiler.__init__ (self, verbose, dry_run, force) - - - def compile (self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - depends=None): - (output_dir, macros, include_dirs) = \ - self._fix_compile_args (output_dir, macros, include_dirs) - self.__sources = sources - self.__macros = macros - self.__include_dirs = include_dirs - # Don't need extra_preargs and extra_postargs for CW - return [] - - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - # First fixup. - (objects, output_dir) = self._fix_object_args (objects, output_dir) - (libraries, library_dirs, runtime_library_dirs) = \ - self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) - - # First examine a couple of options for things that aren't implemented yet - if not target_desc in (self.SHARED_LIBRARY, self.SHARED_OBJECT): - raise DistutilsPlatformError, 'Can only make SHARED_LIBRARY or SHARED_OBJECT targets on the Mac' - if runtime_library_dirs: - raise DistutilsPlatformError, 'Runtime library dirs not implemented yet' - if extra_preargs or extra_postargs: - raise DistutilsPlatformError, 'Runtime library dirs not implemented yet' - if len(export_symbols) != 1: - raise DistutilsPlatformError, 'Need exactly one export symbol' - # Next there are various things for which we need absolute pathnames. - # This is because we (usually) create the project in a subdirectory of - # where we are now, and keeping the paths relative is too much work right - # now. - sources = map(self._filename_to_abs, self.__sources) - include_dirs = map(self._filename_to_abs, self.__include_dirs) - if objects: - objects = map(self._filename_to_abs, objects) - else: - objects = [] - if build_temp: - build_temp = self._filename_to_abs(build_temp) - else: - build_temp = os.curdir() - if output_dir: - output_filename = os.path.join(output_dir, output_filename) - # The output filename needs special handling: splitting it into dir and - # filename part. Actually I'm not sure this is really needed, but it - # can't hurt. - output_filename = self._filename_to_abs(output_filename) - output_dir, output_filename = os.path.split(output_filename) - # Now we need the short names of a couple of things for putting them - # into the project. - if output_filename[-8:] == '.ppc.slb': - basename = output_filename[:-8] - elif output_filename[-11:] == '.carbon.slb': - basename = output_filename[:-11] - else: - basename = os.path.strip(output_filename)[0] - projectname = basename + '.mcp' - targetname = basename - xmlname = basename + '.xml' - exportname = basename + '.mcp.exp' - prefixname = 'mwerks_%s_config.h'%basename - # Create the directories we need - distutils.dir_util.mkpath(build_temp, dry_run=self.dry_run) - distutils.dir_util.mkpath(output_dir, dry_run=self.dry_run) - # And on to filling in the parameters for the project builder - settings = {} - settings['mac_exportname'] = exportname - settings['mac_outputdir'] = output_dir - settings['mac_dllname'] = output_filename - settings['mac_targetname'] = targetname - settings['sysprefix'] = sys.prefix - settings['mac_sysprefixtype'] = 'Absolute' - sourcefilenames = [] - sourcefiledirs = [] - for filename in sources + objects: - dirname, filename = os.path.split(filename) - sourcefilenames.append(filename) - if not dirname in sourcefiledirs: - sourcefiledirs.append(dirname) - settings['sources'] = sourcefilenames - settings['libraries'] = libraries - settings['extrasearchdirs'] = sourcefiledirs + include_dirs + library_dirs - if self.dry_run: - print 'CALLING LINKER IN', os.getcwd() - for key, value in settings.items(): - print '%20.20s %s'%(key, value) - return - # Build the export file - exportfilename = os.path.join(build_temp, exportname) - log.debug("\tCreate export file %s", exportfilename) - fp = open(exportfilename, 'w') - fp.write('%s\n'%export_symbols[0]) - fp.close() - # Generate the prefix file, if needed, and put it in the settings - if self.__macros: - prefixfilename = os.path.join(os.getcwd(), os.path.join(build_temp, prefixname)) - fp = open(prefixfilename, 'w') - fp.write('#include "mwerks_shcarbon_config.h"\n') - for name, value in self.__macros: - if value is None: - fp.write('#define %s\n'%name) - else: - fp.write('#define %s %s\n'%(name, value)) - fp.close() - settings['prefixname'] = prefixname - - # Build the XML file. We need the full pathname (only lateron, really) - # because we pass this pathname to CodeWarrior in an AppleEvent, and CW - # doesn't have a clue about our working directory. - xmlfilename = os.path.join(os.getcwd(), os.path.join(build_temp, xmlname)) - log.debug("\tCreate XML file %s", xmlfilename) - import mkcwproject - xmlbuilder = mkcwproject.cwxmlgen.ProjectBuilder(settings) - xmlbuilder.generate() - xmldata = settings['tmp_projectxmldata'] - fp = open(xmlfilename, 'w') - fp.write(xmldata) - fp.close() - # Generate the project. Again a full pathname. - projectfilename = os.path.join(os.getcwd(), os.path.join(build_temp, projectname)) - log.debug('\tCreate project file %s', projectfilename) - mkcwproject.makeproject(xmlfilename, projectfilename) - # And build it - log.debug('\tBuild project') - mkcwproject.buildproject(projectfilename) - - def _filename_to_abs(self, filename): - # Some filenames seem to be unix-like. Convert to Mac names. -## if '/' in filename and ':' in filename: -## raise DistutilsPlatformError, 'Filename may be Unix or Mac style: %s'%filename -## if '/' in filename: -## filename = macurl2path(filename) - filename = distutils.util.convert_path(filename) - if not os.path.isabs(filename): - curdir = os.getcwd() - filename = os.path.join(curdir, filename) - # Finally remove .. components - components = string.split(filename, ':') - for i in range(1, len(components)): - if components[i] == '..': - components[i] = '' - return string.join(components, ':') - - def library_dir_option (self, dir): - """Return the compiler option to add 'dir' to the list of - directories searched for libraries. - """ - return # XXXX Not correct... - - def runtime_library_dir_option (self, dir): - """Return the compiler option to add 'dir' to the list of - directories searched for runtime libraries. - """ - # Nothing needed or Mwerks/Mac. - return - - def library_option (self, lib): - """Return the compiler option to add 'dir' to the list of libraries - linked into the shared library or executable. - """ - return - - def find_library_file (self, dirs, lib, debug=0): - """Search the specified list of directories for a static or shared - library file 'lib' and return the full path to that file. If - 'debug' true, look for a debugging version (if that makes sense on - the current platform). Return None if 'lib' wasn't found in any of - the specified directories. - """ - return 0 -- cgit v1.2.1 From 0f5cedfabbc30367ff2a5a566e959254855aabc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 25 Jan 2009 18:27:45 +0000 Subject: Merged revisions 68929 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68929 | tarek.ziade | 2009-01-25 19:19:25 +0100 (Sun, 25 Jan 2009) | 1 line Fixed #4863: removed distutils.mwerkscompiler ........ --- ccompiler.py | 3 - mwerkscompiler.py | 245 ------------------------------------------------------ 2 files changed, 248 deletions(-) delete mode 100644 mwerkscompiler.py diff --git a/ccompiler.py b/ccompiler.py index f5d1587d..cad663a3 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1004,7 +1004,6 @@ _default_compilers = ( # OS name mappings ('posix', 'unix'), ('nt', 'msvc'), - ('mac', 'mwerks'), ) @@ -1042,8 +1041,6 @@ compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', "Mingw32 port of GNU C Compiler for Win32"), 'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"), - 'mwerks': ('mwerkscompiler', 'MWerksCompiler', - "MetroWerks CodeWarrior"), 'emx': ('emxccompiler', 'EMXCCompiler', "EMX port of GNU C Compiler for OS/2"), } diff --git a/mwerkscompiler.py b/mwerkscompiler.py deleted file mode 100644 index 130cd614..00000000 --- a/mwerkscompiler.py +++ /dev/null @@ -1,245 +0,0 @@ -"""distutils.mwerkscompiler - -Contains MWerksCompiler, an implementation of the abstract CCompiler class -for MetroWerks CodeWarrior on the Macintosh. Needs work to support CW on -Windows.""" - -__revision__ = "$Id$" - -import sys, os -from distutils.errors import \ - DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options -import distutils.util -import distutils.dir_util -from distutils import log - -class MWerksCompiler (CCompiler) : - """Concrete class that implements an interface to MetroWerks CodeWarrior, - as defined by the CCompiler abstract class.""" - - compiler_type = 'mwerks' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.r'] - _exp_extension = '.exp' - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = (_c_extensions + _cpp_extensions + - _rc_extensions) - res_extension = '.rsrc' - obj_extension = '.obj' # Not used, really - static_lib_extension = '.lib' - shared_lib_extension = '.slb' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '' - - - def __init__ (self, - verbose=0, - dry_run=0, - force=0): - - CCompiler.__init__ (self, verbose, dry_run, force) - - - def compile (self, - sources, - output_dir=None, - macros=None, - include_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - depends=None): - (output_dir, macros, include_dirs) = \ - self._fix_compile_args (output_dir, macros, include_dirs) - self.__sources = sources - self.__macros = macros - self.__include_dirs = include_dirs - # Don't need extra_preargs and extra_postargs for CW - return [] - - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - # First fixup. - (objects, output_dir) = self._fix_object_args (objects, output_dir) - (libraries, library_dirs, runtime_library_dirs) = \ - self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) - - # First examine a couple of options for things that aren't implemented yet - if not target_desc in (self.SHARED_LIBRARY, self.SHARED_OBJECT): - raise DistutilsPlatformError('Can only make SHARED_LIBRARY or SHARED_OBJECT targets on the Mac') - if runtime_library_dirs: - raise DistutilsPlatformError('Runtime library dirs not implemented yet') - if extra_preargs or extra_postargs: - raise DistutilsPlatformError('Runtime library dirs not implemented yet') - if len(export_symbols) != 1: - raise DistutilsPlatformError('Need exactly one export symbol') - # Next there are various things for which we need absolute pathnames. - # This is because we (usually) create the project in a subdirectory of - # where we are now, and keeping the paths relative is too much work right - # now. - sources = [self._filename_to_abs(s) for s in self.__sources] - include_dirs = [self._filename_to_abs(d) for d in self.__include_dirs] - if objects: - objects = [self._filename_to_abs(o) for o in objects] - else: - objects = [] - if build_temp: - build_temp = self._filename_to_abs(build_temp) - else: - build_temp = os.curdir() - if output_dir: - output_filename = os.path.join(output_dir, output_filename) - # The output filename needs special handling: splitting it into dir and - # filename part. Actually I'm not sure this is really needed, but it - # can't hurt. - output_filename = self._filename_to_abs(output_filename) - output_dir, output_filename = os.path.split(output_filename) - # Now we need the short names of a couple of things for putting them - # into the project. - if output_filename[-8:] == '.ppc.slb': - basename = output_filename[:-8] - elif output_filename[-11:] == '.carbon.slb': - basename = output_filename[:-11] - else: - basename = os.path.strip(output_filename)[0] - projectname = basename + '.mcp' - targetname = basename - xmlname = basename + '.xml' - exportname = basename + '.mcp.exp' - prefixname = 'mwerks_%s_config.h'%basename - # Create the directories we need - distutils.dir_util.mkpath(build_temp, dry_run=self.dry_run) - distutils.dir_util.mkpath(output_dir, dry_run=self.dry_run) - # And on to filling in the parameters for the project builder - settings = {} - settings['mac_exportname'] = exportname - settings['mac_outputdir'] = output_dir - settings['mac_dllname'] = output_filename - settings['mac_targetname'] = targetname - settings['sysprefix'] = sys.prefix - settings['mac_sysprefixtype'] = 'Absolute' - sourcefilenames = [] - sourcefiledirs = [] - for filename in sources + objects: - dirname, filename = os.path.split(filename) - sourcefilenames.append(filename) - if not dirname in sourcefiledirs: - sourcefiledirs.append(dirname) - settings['sources'] = sourcefilenames - settings['libraries'] = libraries - settings['extrasearchdirs'] = sourcefiledirs + include_dirs + library_dirs - if self.dry_run: - print('CALLING LINKER IN', os.getcwd()) - for key, value in settings.items(): - print('%20.20s %s'%(key, value)) - return - # Build the export file - exportfilename = os.path.join(build_temp, exportname) - log.debug("\tCreate export file %s", exportfilename) - fp = open(exportfilename, 'w') - fp.write('%s\n'%export_symbols[0]) - fp.close() - # Generate the prefix file, if needed, and put it in the settings - if self.__macros: - prefixfilename = os.path.join(os.getcwd(), os.path.join(build_temp, prefixname)) - fp = open(prefixfilename, 'w') - fp.write('#include "mwerks_shcarbon_config.h"\n') - for name, value in self.__macros: - if value is None: - fp.write('#define %s\n'%name) - else: - fp.write('#define %s %s\n'%(name, value)) - fp.close() - settings['prefixname'] = prefixname - - # Build the XML file. We need the full pathname (only lateron, really) - # because we pass this pathname to CodeWarrior in an AppleEvent, and CW - # doesn't have a clue about our working directory. - xmlfilename = os.path.join(os.getcwd(), os.path.join(build_temp, xmlname)) - log.debug("\tCreate XML file %s", xmlfilename) - import mkcwproject - xmlbuilder = mkcwproject.cwxmlgen.ProjectBuilder(settings) - xmlbuilder.generate() - xmldata = settings['tmp_projectxmldata'] - fp = open(xmlfilename, 'w') - fp.write(xmldata) - fp.close() - # Generate the project. Again a full pathname. - projectfilename = os.path.join(os.getcwd(), os.path.join(build_temp, projectname)) - log.debug('\tCreate project file %s', projectfilename) - mkcwproject.makeproject(xmlfilename, projectfilename) - # And build it - log.debug('\tBuild project') - mkcwproject.buildproject(projectfilename) - - def _filename_to_abs(self, filename): - # Some filenames seem to be unix-like. Convert to Mac names. -## if '/' in filename and ':' in filename: -## raise DistutilsPlatformError, 'Filename may be Unix or Mac style: %s'%filename -## if '/' in filename: -## filename = macurl2path(filename) - filename = distutils.util.convert_path(filename) - if not os.path.isabs(filename): - curdir = os.getcwd() - filename = os.path.join(curdir, filename) - # Finally remove .. components - components = filename.split(':') - for i in range(1, len(components)): - if components[i] == '..': - components[i] = '' - return ':'.join(components) - - def library_dir_option (self, dir): - """Return the compiler option to add 'dir' to the list of - directories searched for libraries. - """ - return # XXXX Not correct... - - def runtime_library_dir_option (self, dir): - """Return the compiler option to add 'dir' to the list of - directories searched for runtime libraries. - """ - # Nothing needed or Mwerks/Mac. - return - - def library_option (self, lib): - """Return the compiler option to add 'dir' to the list of libraries - linked into the shared library or executable. - """ - return - - def find_library_file (self, dirs, lib, debug=0): - """Search the specified list of directories for a static or shared - library file 'lib' and return the full path to that file. If - 'debug' true, look for a debugging version (if that makes sense on - the current platform). Return None if 'lib' wasn't found in any of - the specified directories. - """ - return 0 -- cgit v1.2.1 From d404801cfcaaf114b9e8ac94f40cce68c293f671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 25 Jan 2009 22:09:10 +0000 Subject: Issue #5052: removed backward compatibility information (out of date) --- __init__.py | 2 -- archive_util.py | 2 -- bcppcompiler.py | 2 -- ccompiler.py | 2 -- cmd.py | 2 -- command/__init__.py | 2 -- command/bdist.py | 2 -- command/bdist_dumb.py | 2 -- command/bdist_rpm.py | 2 -- command/bdist_wininst.py | 2 -- command/build.py | 2 -- command/build_clib.py | 2 -- command/build_ext.py | 2 -- command/build_py.py | 2 -- command/build_scripts.py | 2 -- command/clean.py | 2 -- command/config.py | 2 -- command/install.py | 2 -- command/install_data.py | 2 -- command/install_headers.py | 2 -- command/install_lib.py | 2 -- command/install_scripts.py | 2 -- command/sdist.py | 2 -- core.py | 2 -- cygwinccompiler.py | 2 -- debug.py | 2 -- dep_util.py | 2 -- dir_util.py | 2 -- dist.py | 2 -- errors.py | 2 -- fancy_getopt.py | 2 -- file_util.py | 2 -- filelist.py | 2 -- log.py | 2 -- msvccompiler.py | 2 -- spawn.py | 2 -- 36 files changed, 72 deletions(-) diff --git a/__init__.py b/__init__.py index 7315a372..57fcf976 100644 --- a/__init__.py +++ b/__init__.py @@ -8,8 +8,6 @@ used from a setup script as setup (...) """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" # Distutils version diff --git a/archive_util.py b/archive_util.py index 264e66fa..b50563e3 100644 --- a/archive_util.py +++ b/archive_util.py @@ -3,8 +3,6 @@ Utility functions for creating archive files (tarballs, zip files, that sort of thing).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os diff --git a/bcppcompiler.py b/bcppcompiler.py index d4fc5336..5a0fa727 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,8 +11,6 @@ for the Borland C++ compiler. # someone should sit down and factor out the common code as # WindowsCCompiler! --GPW -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" diff --git a/ccompiler.py b/ccompiler.py index aa92a9f1..a56a0388 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -3,8 +3,6 @@ Contains CCompiler, an abstract base class that defines the interface for the Distutils compiler abstraction model.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, re diff --git a/cmd.py b/cmd.py index 3cd58589..267cf180 100644 --- a/cmd.py +++ b/cmd.py @@ -4,8 +4,6 @@ Provides the Command class, the base class for the command classes in the distutils.command package. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, string, re diff --git a/command/__init__.py b/command/__init__.py index 0888c271..add83f87 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -3,8 +3,6 @@ Package containing implementation of all the standard Distutils commands.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" __all__ = ['build', diff --git a/command/bdist.py b/command/bdist.py index ca3da74c..3df5ef4c 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -3,8 +3,6 @@ Implements the Distutils 'bdist' command (create a built [binary] distribution).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index f740c468..7324c6c2 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,8 +4,6 @@ Implements the Distutils 'bdist_dumb' command (create a "dumb" built distribution -- i.e., just an archive to be unpacked under $prefix or $exec_prefix).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 9aa70300..a48e0f18 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -3,8 +3,6 @@ Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, string diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index f18e318c..ad6eee8e 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -3,8 +3,6 @@ Implements the Distutils 'bdist_wininst' command: create a windows installer exe-program.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, string diff --git a/command/build.py b/command/build.py index 7462e934..84e05020 100644 --- a/command/build.py +++ b/command/build.py @@ -2,8 +2,6 @@ Implements the Distutils 'build' command.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os diff --git a/command/build_clib.py b/command/build_clib.py index 69d8c751..fe921fb8 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,8 +4,6 @@ Implements the Distutils 'build_clib' command, to build a C/C++ library that is included in the module distribution and needed by an extension module.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" diff --git a/command/build_ext.py b/command/build_ext.py index 1461409f..fbf2a0b2 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,8 +4,6 @@ Implements the Distutils 'build_ext' command, for building extension modules (currently limited to C extensions, should accommodate C++ extensions ASAP).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, string, re diff --git a/command/build_py.py b/command/build_py.py index 3bf12673..d9c15749 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -2,8 +2,6 @@ Implements the Distutils 'build_py' command.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import string, os diff --git a/command/build_scripts.py b/command/build_scripts.py index 104be0b3..48e06aa5 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -2,8 +2,6 @@ Implements the Distutils 'build_scripts' command.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os, re diff --git a/command/clean.py b/command/clean.py index 02189c53..ba03d1ff 100644 --- a/command/clean.py +++ b/command/clean.py @@ -4,8 +4,6 @@ Implements the Distutils 'clean' command.""" # contributed by Bastian Kleineidam , added 2000-03-18 -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os diff --git a/command/config.py b/command/config.py index 520c1b0c..6b358cf2 100644 --- a/command/config.py +++ b/command/config.py @@ -9,8 +9,6 @@ configure-like tasks: "try to compile this C code", or "figure out where this header file lives". """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, string, re diff --git a/command/install.py b/command/install.py index 44c76926..adc9daf1 100644 --- a/command/install.py +++ b/command/install.py @@ -4,8 +4,6 @@ Implements the Distutils 'install' command.""" from distutils import log -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, string diff --git a/command/install_data.py b/command/install_data.py index 1069830f..cb113710 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -5,8 +5,6 @@ platform-independent data files.""" # contributed by Bastian Kleineidam -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os diff --git a/command/install_headers.py b/command/install_headers.py index 4895240a..c25b7712 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -3,8 +3,6 @@ Implements the Distutils 'install_headers' command, to install C/C++ header files to the Python include directory.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" from distutils.core import Command diff --git a/command/install_lib.py b/command/install_lib.py index 81107a85..fed7e536 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -1,5 +1,3 @@ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os diff --git a/command/install_scripts.py b/command/install_scripts.py index fe93ef5a..29cd9e7a 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -5,8 +5,6 @@ Python scripts.""" # contributed by Bastian Kleineidam -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os diff --git a/command/sdist.py b/command/sdist.py index 27883fd6..6ce8c43d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -2,8 +2,6 @@ Implements the Distutils 'sdist' command (create a source distribution).""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os, string diff --git a/core.py b/core.py index 62a3389b..0e85ca6f 100644 --- a/core.py +++ b/core.py @@ -6,8 +6,6 @@ indirectly provides the Distribution and Command classes, although they are really defined in distutils.dist and distutils.cmd. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 94a7bd96..3ac1dceb 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -45,8 +45,6 @@ cygwin in no-cygwin mode). # * mingw gcc 3.2/ld 2.13 works # (ld supports -shared) -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os,sys,copy diff --git a/debug.py b/debug.py index b67139c7..28867444 100644 --- a/debug.py +++ b/debug.py @@ -1,7 +1,5 @@ import os -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" # If DISTUTILS_DEBUG is anything other than the empty string, we run in diff --git a/dep_util.py b/dep_util.py index 2c6d7927..39eecfb2 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,8 +4,6 @@ Utility functions for simple, timestamp-based dependency of files and groups of files; also, function based entirely on such timestamp dependency analysis.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os diff --git a/dir_util.py b/dir_util.py index 77f25325..54f5c68e 100644 --- a/dir_util.py +++ b/dir_util.py @@ -2,8 +2,6 @@ Utility functions for manipulating directories and directory trees.""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os, sys diff --git a/dist.py b/dist.py index 18cc9109..3dd776cc 100644 --- a/dist.py +++ b/dist.py @@ -4,8 +4,6 @@ Provides the Distribution class, which represents the module distribution being built/installed/distributed. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, string, re diff --git a/errors.py b/errors.py index e72221bd..963d8337 100644 --- a/errors.py +++ b/errors.py @@ -8,8 +8,6 @@ usually raised for errors that are obviously the end-user's fault This module is safe to use in "from ... import *" mode; it only exports symbols whose names start with "Distutils" and end with "Error".""" -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" class DistutilsError (Exception): diff --git a/fancy_getopt.py b/fancy_getopt.py index 31cf0c5c..e19319ae 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,8 +8,6 @@ additional features: * options set attributes of a passed-in object """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, string, re diff --git a/file_util.py b/file_util.py index 37b152ed..3af6344f 100644 --- a/file_util.py +++ b/file_util.py @@ -3,8 +3,6 @@ Utility functions for operating on single files. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os diff --git a/filelist.py b/filelist.py index 6d27cce6..51a1d57e 100644 --- a/filelist.py +++ b/filelist.py @@ -4,8 +4,6 @@ Provides the FileList class, used for poking about the filesystem and building lists of files. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os, string, re diff --git a/log.py b/log.py index 95d4c1c5..fcaa545a 100644 --- a/log.py +++ b/log.py @@ -1,7 +1,5 @@ """A simple log mechanism styled after PEP 282.""" -# This module should be kept compatible with Python 2.1. - # The class here is styled after PEP 282 so that it could later be # replaced with a standard Python logging implementation. diff --git a/msvccompiler.py b/msvccompiler.py index 30a9fc6f..d38afb72 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -8,8 +8,6 @@ for the Microsoft Visual Studio. # hacked by Robin Becker and Thomas Heller to do a better job of # finding DevStudio (through the registry) -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, string diff --git a/spawn.py b/spawn.py index e5654ff0..a6d34263 100644 --- a/spawn.py +++ b/spawn.py @@ -6,8 +6,6 @@ Also provides the 'find_executable()' to search the path for a given executable name. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, string -- cgit v1.2.1 From f95cd9c07c8f71e37cbe442b9e1a6df70029df63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 25 Jan 2009 22:11:04 +0000 Subject: added missing module docstring --- command/install_lib.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/command/install_lib.py b/command/install_lib.py index fed7e536..4c62c713 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -1,3 +1,8 @@ +"""distutils.command.install_lib + +Implements the Distutils 'install_lib' command +(install all Python modules).""" + __revision__ = "$Id$" import os -- cgit v1.2.1 From e59b73327e824b92fd5ab54894b6ba1fd91e16d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 25 Jan 2009 23:34:00 +0000 Subject: Fixed #1885: --formats=tar,gztar was not working properly in the sdist command --- command/sdist.py | 4 +++ tests/test_sdist.py | 77 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 6ce8c43d..e8b6bce9 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -456,6 +456,10 @@ class sdist (Command): self.make_release_tree(base_dir, self.filelist.files) archive_files = [] # remember names of files we create + # tar archive must be created last to avoid overwrite and remove + if 'tar' in self.formats: + self.formats.append(self.formats.pop(self.formats.index('tar'))) + for fmt in self.formats: file = self.make_archive(base_name, fmt, base_dir=base_dir) archive_files.append(file) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 7cc04cfc..96ec381e 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -4,10 +4,13 @@ import unittest import shutil import zipfile from os.path import join +import sys from distutils.command.sdist import sdist from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase +from distutils.errors import DistutilsExecError +from distutils.spawn import spawn CURDIR = os.path.dirname(__file__) TEMP_PKG = join(CURDIR, 'temppkg') @@ -35,6 +38,19 @@ class sdistTestCase(PyPIRCCommandTestCase): shutil.rmtree(TEMP_PKG) PyPIRCCommandTestCase.tearDown(self) + def _init_tmp_pkg(self): + if os.path.exists(TEMP_PKG): + shutil.rmtree(TEMP_PKG) + os.mkdir(TEMP_PKG) + os.mkdir(join(TEMP_PKG, 'somecode')) + os.mkdir(join(TEMP_PKG, 'dist')) + # creating a MANIFEST, a package, and a README + self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) + self._write(join(TEMP_PKG, 'README'), 'xxx') + self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') + self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) + os.chdir(TEMP_PKG) + def _write(self, path, content): f = open(path, 'w') try: @@ -46,15 +62,7 @@ class sdistTestCase(PyPIRCCommandTestCase): # this test creates a package with some vcs dirs in it # and launch sdist to make sure they get pruned # on all systems - if not os.path.exists(TEMP_PKG): - os.mkdir(TEMP_PKG) - os.mkdir(join(TEMP_PKG, 'somecode')) - - # creating a MANIFEST, a package, and a README - self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) - self._write(join(TEMP_PKG, 'README'), 'xxx') - self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') - self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) + self._init_tmp_pkg() # creating VCS directories with some files in them os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) @@ -68,8 +76,6 @@ class sdistTestCase(PyPIRCCommandTestCase): self._write(join(TEMP_PKG, 'somecode', '.git', 'ok'), 'xxx') - os.chdir(TEMP_PKG) - # now building a sdist dist = Distribution() dist.script_name = 'setup.py' @@ -103,6 +109,55 @@ class sdistTestCase(PyPIRCCommandTestCase): # making sure everything has been pruned correctly self.assertEquals(len(content), 4) + def test_make_distribution(self): + + self._init_tmp_pkg() + + # check if tar is installed under win32 + if sys.platform == 'win32': + try: + spawn('tar --help') + except DistutilsExecError: + # let's return, no need to go further + return + + # now building a sdist + dist = Distribution() + dist.script_name = 'setup.py' + dist.metadata.name = 'fake' + dist.metadata.version = '1.0' + dist.metadata.url = 'http://xxx' + dist.metadata.author = dist.metadata.author_email = 'xxx' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.manifest = 'MANIFEST' + cmd.template = 'MANIFEST.in' + cmd.dist_dir = 'dist' + + # creating a gztar then a tar + cmd.formats = ['gztar', 'tar'] + cmd.run() + + # making sure we have two files + dist_folder = join(TEMP_PKG, 'dist') + result = os.listdir(dist_folder) + result.sort() + self.assertEquals(result, + ['fake-1.0.tar', 'fake-1.0.tar.gz'] ) + + os.remove(join(dist_folder, 'fake-1.0.tar')) + os.remove(join(dist_folder, 'fake-1.0.tar.gz')) + + # now trying a tar then a gztar + cmd.formats = ['tar', 'gztar'] + cmd.run() + + result = os.listdir(dist_folder) + result.sort() + self.assertEquals(result, + ['fake-1.0.tar', 'fake-1.0.tar.gz']) + def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From adc20e818b17882e3ef80dcd2c4a81decb9e9888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 26 Jan 2009 17:20:15 +0000 Subject: Merged revisions 68951 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68951 | tarek.ziade | 2009-01-26 00:34:00 +0100 (Mon, 26 Jan 2009) | 1 line Fixed #1885: --formats=tar,gztar was not working properly in the sdist command ........ --- command/sdist.py | 4 +++ tests/test_sdist.py | 77 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 27883fd6..a366d1ea 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -458,6 +458,10 @@ class sdist (Command): self.make_release_tree(base_dir, self.filelist.files) archive_files = [] # remember names of files we create + # tar archive must be created last to avoid overwrite and remove + if 'tar' in self.formats: + self.formats.append(self.formats.pop(self.formats.index('tar'))) + for fmt in self.formats: file = self.make_archive(base_name, fmt, base_dir=base_dir) archive_files.append(file) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 7cc04cfc..96ec381e 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -4,10 +4,13 @@ import unittest import shutil import zipfile from os.path import join +import sys from distutils.command.sdist import sdist from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase +from distutils.errors import DistutilsExecError +from distutils.spawn import spawn CURDIR = os.path.dirname(__file__) TEMP_PKG = join(CURDIR, 'temppkg') @@ -35,6 +38,19 @@ class sdistTestCase(PyPIRCCommandTestCase): shutil.rmtree(TEMP_PKG) PyPIRCCommandTestCase.tearDown(self) + def _init_tmp_pkg(self): + if os.path.exists(TEMP_PKG): + shutil.rmtree(TEMP_PKG) + os.mkdir(TEMP_PKG) + os.mkdir(join(TEMP_PKG, 'somecode')) + os.mkdir(join(TEMP_PKG, 'dist')) + # creating a MANIFEST, a package, and a README + self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) + self._write(join(TEMP_PKG, 'README'), 'xxx') + self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') + self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) + os.chdir(TEMP_PKG) + def _write(self, path, content): f = open(path, 'w') try: @@ -46,15 +62,7 @@ class sdistTestCase(PyPIRCCommandTestCase): # this test creates a package with some vcs dirs in it # and launch sdist to make sure they get pruned # on all systems - if not os.path.exists(TEMP_PKG): - os.mkdir(TEMP_PKG) - os.mkdir(join(TEMP_PKG, 'somecode')) - - # creating a MANIFEST, a package, and a README - self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) - self._write(join(TEMP_PKG, 'README'), 'xxx') - self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') - self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) + self._init_tmp_pkg() # creating VCS directories with some files in them os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) @@ -68,8 +76,6 @@ class sdistTestCase(PyPIRCCommandTestCase): self._write(join(TEMP_PKG, 'somecode', '.git', 'ok'), 'xxx') - os.chdir(TEMP_PKG) - # now building a sdist dist = Distribution() dist.script_name = 'setup.py' @@ -103,6 +109,55 @@ class sdistTestCase(PyPIRCCommandTestCase): # making sure everything has been pruned correctly self.assertEquals(len(content), 4) + def test_make_distribution(self): + + self._init_tmp_pkg() + + # check if tar is installed under win32 + if sys.platform == 'win32': + try: + spawn('tar --help') + except DistutilsExecError: + # let's return, no need to go further + return + + # now building a sdist + dist = Distribution() + dist.script_name = 'setup.py' + dist.metadata.name = 'fake' + dist.metadata.version = '1.0' + dist.metadata.url = 'http://xxx' + dist.metadata.author = dist.metadata.author_email = 'xxx' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.manifest = 'MANIFEST' + cmd.template = 'MANIFEST.in' + cmd.dist_dir = 'dist' + + # creating a gztar then a tar + cmd.formats = ['gztar', 'tar'] + cmd.run() + + # making sure we have two files + dist_folder = join(TEMP_PKG, 'dist') + result = os.listdir(dist_folder) + result.sort() + self.assertEquals(result, + ['fake-1.0.tar', 'fake-1.0.tar.gz'] ) + + os.remove(join(dist_folder, 'fake-1.0.tar')) + os.remove(join(dist_folder, 'fake-1.0.tar.gz')) + + # now trying a tar then a gztar + cmd.formats = ['tar', 'gztar'] + cmd.run() + + result = os.listdir(dist_folder) + result.sort() + self.assertEquals(result, + ['fake-1.0.tar', 'fake-1.0.tar.gz']) + def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From da4be9fd4435d184850ca6f2f1e2f00429ac7f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 26 Jan 2009 17:23:20 +0000 Subject: Merged revisions 68951 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68951 | tarek.ziade | 2009-01-26 00:34:00 +0100 (Mon, 26 Jan 2009) | 1 line Fixed #1885: --formats=tar,gztar was not working properly in the sdist command ........ --- command/sdist.py | 4 +++ tests/test_sdist.py | 77 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 96fb7fa1..054fe191 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -428,6 +428,10 @@ class sdist (Command): self.make_release_tree(base_dir, self.filelist.files) archive_files = [] # remember names of files we create + # tar archive must be created last to avoid overwrite and remove + if 'tar' in self.formats: + self.formats.append(self.formats.pop(self.formats.index('tar'))) + for fmt in self.formats: file = self.make_archive(base_name, fmt, base_dir=base_dir) archive_files.append(file) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 7cc04cfc..96ec381e 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -4,10 +4,13 @@ import unittest import shutil import zipfile from os.path import join +import sys from distutils.command.sdist import sdist from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase +from distutils.errors import DistutilsExecError +from distutils.spawn import spawn CURDIR = os.path.dirname(__file__) TEMP_PKG = join(CURDIR, 'temppkg') @@ -35,6 +38,19 @@ class sdistTestCase(PyPIRCCommandTestCase): shutil.rmtree(TEMP_PKG) PyPIRCCommandTestCase.tearDown(self) + def _init_tmp_pkg(self): + if os.path.exists(TEMP_PKG): + shutil.rmtree(TEMP_PKG) + os.mkdir(TEMP_PKG) + os.mkdir(join(TEMP_PKG, 'somecode')) + os.mkdir(join(TEMP_PKG, 'dist')) + # creating a MANIFEST, a package, and a README + self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) + self._write(join(TEMP_PKG, 'README'), 'xxx') + self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') + self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) + os.chdir(TEMP_PKG) + def _write(self, path, content): f = open(path, 'w') try: @@ -46,15 +62,7 @@ class sdistTestCase(PyPIRCCommandTestCase): # this test creates a package with some vcs dirs in it # and launch sdist to make sure they get pruned # on all systems - if not os.path.exists(TEMP_PKG): - os.mkdir(TEMP_PKG) - os.mkdir(join(TEMP_PKG, 'somecode')) - - # creating a MANIFEST, a package, and a README - self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) - self._write(join(TEMP_PKG, 'README'), 'xxx') - self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') - self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) + self._init_tmp_pkg() # creating VCS directories with some files in them os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) @@ -68,8 +76,6 @@ class sdistTestCase(PyPIRCCommandTestCase): self._write(join(TEMP_PKG, 'somecode', '.git', 'ok'), 'xxx') - os.chdir(TEMP_PKG) - # now building a sdist dist = Distribution() dist.script_name = 'setup.py' @@ -103,6 +109,55 @@ class sdistTestCase(PyPIRCCommandTestCase): # making sure everything has been pruned correctly self.assertEquals(len(content), 4) + def test_make_distribution(self): + + self._init_tmp_pkg() + + # check if tar is installed under win32 + if sys.platform == 'win32': + try: + spawn('tar --help') + except DistutilsExecError: + # let's return, no need to go further + return + + # now building a sdist + dist = Distribution() + dist.script_name = 'setup.py' + dist.metadata.name = 'fake' + dist.metadata.version = '1.0' + dist.metadata.url = 'http://xxx' + dist.metadata.author = dist.metadata.author_email = 'xxx' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.manifest = 'MANIFEST' + cmd.template = 'MANIFEST.in' + cmd.dist_dir = 'dist' + + # creating a gztar then a tar + cmd.formats = ['gztar', 'tar'] + cmd.run() + + # making sure we have two files + dist_folder = join(TEMP_PKG, 'dist') + result = os.listdir(dist_folder) + result.sort() + self.assertEquals(result, + ['fake-1.0.tar', 'fake-1.0.tar.gz'] ) + + os.remove(join(dist_folder, 'fake-1.0.tar')) + os.remove(join(dist_folder, 'fake-1.0.tar.gz')) + + # now trying a tar then a gztar + cmd.formats = ['tar', 'gztar'] + cmd.run() + + result = os.listdir(dist_folder) + result.sort() + self.assertEquals(result, + ['fake-1.0.tar', 'fake-1.0.tar.gz']) + def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From 52141aae7f073fe917c841ddbb4531a9beb04ddc Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Tue, 27 Jan 2009 18:17:45 +0000 Subject: Issue #1717: Remove cmp. Stage 1: remove all uses of cmp and __cmp__ from the standard library and tests. --- version.py | 63 +++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/version.py b/version.py index 907f71c3..77814377 100644 --- a/version.py +++ b/version.py @@ -21,7 +21,7 @@ Every version number class implements the following interface: an equivalent string -- ie. one that will generate an equivalent version number instance) * __repr__ generates Python code to recreate the version number instance - * __cmp__ compares the current instance with either another instance + * _cmp compares the current instance with either another instance of the same class or a string (which will be parsed to an instance of the same class, thus must follow the same rules) """ @@ -32,7 +32,7 @@ class Version: """Abstract base class for version numbering classes. Just provides constructor (__init__) and reproducer (__repr__), because those seem to be the same for all version numbering classes; and route - rich comparisons to __cmp__. + rich comparisons to _cmp. """ def __init__ (self, vstring=None): @@ -43,37 +43,37 @@ class Version: return "%s ('%s')" % (self.__class__.__name__, str(self)) def __eq__(self, other): - c = self.__cmp__(other) + c = self._cmp(other) if c is NotImplemented: return c return c == 0 def __ne__(self, other): - c = self.__cmp__(other) + c = self._cmp(other) if c is NotImplemented: return c return c != 0 def __lt__(self, other): - c = self.__cmp__(other) + c = self._cmp(other) if c is NotImplemented: return c return c < 0 def __le__(self, other): - c = self.__cmp__(other) + c = self._cmp(other) if c is NotImplemented: return c return c <= 0 def __gt__(self, other): - c = self.__cmp__(other) + c = self._cmp(other) if c is NotImplemented: return c return c > 0 def __ge__(self, other): - c = self.__cmp__(other) + c = self._cmp(other) if c is NotImplemented: return c return c >= 0 @@ -91,7 +91,7 @@ class Version: # (if not identical to) the string supplied to parse # __repr__ (self) - generate Python code to recreate # the instance -# __cmp__ (self, other) - compare two version numbers ('other' may +# _cmp (self, other) - compare two version numbers ('other' may # be an unparsed version string, or another # instance of your version class) @@ -169,30 +169,39 @@ class StrictVersion (Version): return vstring - def __cmp__ (self, other): + def _cmp (self, other): if isinstance(other, str): other = StrictVersion(other) - compare = cmp(self.version, other.version) - if (compare == 0): # have to compare prerelease - - # case 1: neither has prerelease; they're equal - # case 2: self has prerelease, other doesn't; other is greater - # case 3: self doesn't have prerelease, other does: self is greater - # case 4: both have prerelease: must compare them! + if self.version != other.version: + # numeric versions don't match + # prerelease stuff doesn't matter + if self.version < other.version: + return -1 + else: + return 1 - if (not self.prerelease and not other.prerelease): + # have to compare prerelease + # case 1: neither has prerelease; they're equal + # case 2: self has prerelease, other doesn't; other is greater + # case 3: self doesn't have prerelease, other does: self is greater + # case 4: both have prerelease: must compare them! + + if (not self.prerelease and not other.prerelease): + return 0 + elif (self.prerelease and not other.prerelease): + return -1 + elif (not self.prerelease and other.prerelease): + return 1 + elif (self.prerelease and other.prerelease): + if self.prerelease == other.prerelease: return 0 - elif (self.prerelease and not other.prerelease): + elif self.prerelease < other.prerelease: return -1 - elif (not self.prerelease and other.prerelease): + else: return 1 - elif (self.prerelease and other.prerelease): - return cmp(self.prerelease, other.prerelease) - - else: # numeric versions don't match -- - return compare # prerelease stuff doesn't matter - + else: + assert False, "never get here" # end class StrictVersion @@ -325,7 +334,7 @@ class LooseVersion (Version): return "LooseVersion ('%s')" % str(self) - def __cmp__ (self, other): + def _cmp (self, other): if isinstance(other, str): other = LooseVersion(other) -- cgit v1.2.1 From 8e32f7ad504aad7f9ed2603c282f0e9831c716ba Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 29 Jan 2009 12:13:31 +0000 Subject: Fix issue5075: bdist_wininst should not depend on the vc runtime? --- command/wininst-9.0-amd64.exe | Bin 77824 -> 223744 bytes command/wininst-9.0.exe | Bin 66048 -> 196096 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index 9dedfcdc..11d8011c 100644 Binary files a/command/wininst-9.0-amd64.exe and b/command/wininst-9.0-amd64.exe differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 9102ecdb..dadb31d8 100644 Binary files a/command/wininst-9.0.exe and b/command/wininst-9.0.exe differ -- cgit v1.2.1 From 4b0e22f7614b2454d2a1b0c1abc13e4cdad49707 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 29 Jan 2009 12:31:51 +0000 Subject: Merged revisions 69094 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69094 | mark.hammond | 2009-01-29 23:13:31 +1100 (Thu, 29 Jan 2009) | 2 lines Fix issue5075: bdist_wininst should not depend on the vc runtime? ........ --- command/wininst-9.0-amd64.exe | Bin 77824 -> 223744 bytes command/wininst-9.0.exe | Bin 66048 -> 196096 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index 9dedfcdc..11d8011c 100644 Binary files a/command/wininst-9.0-amd64.exe and b/command/wininst-9.0-amd64.exe differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 9102ecdb..dadb31d8 100644 Binary files a/command/wininst-9.0.exe and b/command/wininst-9.0.exe differ -- cgit v1.2.1 From 7036aee66775cdb74a5c8342e6d08c8df75d4048 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 29 Jan 2009 12:36:50 +0000 Subject: Merged revisions 69094 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69094 | mark.hammond | 2009-01-29 23:13:31 +1100 (Thu, 29 Jan 2009) | 2 lines Fix issue5075: bdist_wininst should not depend on the vc runtime? ........ --- command/wininst-9.0-amd64.exe | Bin 77824 -> 223744 bytes command/wininst-9.0.exe | Bin 66048 -> 196096 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index 9dedfcdc..11d8011c 100644 Binary files a/command/wininst-9.0-amd64.exe and b/command/wininst-9.0-amd64.exe differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 9102ecdb..dadb31d8 100644 Binary files a/command/wininst-9.0.exe and b/command/wininst-9.0.exe differ -- cgit v1.2.1 From f7bf90fda5ccf81484f5bb114cd0115c62eb1c18 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 29 Jan 2009 13:08:01 +0000 Subject: Fix issue5076: bdist_wininst fails on py3k --- command/wininst-9.0-amd64.exe | Bin 223744 -> 224256 bytes command/wininst-9.0.exe | Bin 196096 -> 196096 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index 11d8011c..94fbd434 100644 Binary files a/command/wininst-9.0-amd64.exe and b/command/wininst-9.0-amd64.exe differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index dadb31d8..2ec261f9 100644 Binary files a/command/wininst-9.0.exe and b/command/wininst-9.0.exe differ -- cgit v1.2.1 From 5f79419c30c7b3eb2ae476e03c05509265926f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 29 Jan 2009 23:49:17 +0000 Subject: fixed test_make_distribution so it runs on any platform, as long as tar an gzip are available --- tests/test_sdist.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 96ec381e..0d839b5c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -10,7 +10,7 @@ from distutils.command.sdist import sdist from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError -from distutils.spawn import spawn +from distutils.spawn import find_executable CURDIR = os.path.dirname(__file__) TEMP_PKG = join(CURDIR, 'temppkg') @@ -111,15 +111,12 @@ class sdistTestCase(PyPIRCCommandTestCase): def test_make_distribution(self): - self._init_tmp_pkg() + # check if tar and gzip are installed + if (find_executable('tar') is None or + find_executable('gzip') is None): + return - # check if tar is installed under win32 - if sys.platform == 'win32': - try: - spawn('tar --help') - except DistutilsExecError: - # let's return, no need to go further - return + self._init_tmp_pkg() # now building a sdist dist = Distribution() -- cgit v1.2.1 From 2185915ff974a216f44b4ddaedab793f7c01c611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 29 Jan 2009 23:51:53 +0000 Subject: Merged revisions 69106 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69106 | tarek.ziade | 2009-01-30 00:49:17 +0100 (Fri, 30 Jan 2009) | 1 line fixed test_make_distribution so it runs on any platform, as long as tar an gzip are available ........ --- tests/test_sdist.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 96ec381e..0d839b5c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -10,7 +10,7 @@ from distutils.command.sdist import sdist from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError -from distutils.spawn import spawn +from distutils.spawn import find_executable CURDIR = os.path.dirname(__file__) TEMP_PKG = join(CURDIR, 'temppkg') @@ -111,15 +111,12 @@ class sdistTestCase(PyPIRCCommandTestCase): def test_make_distribution(self): - self._init_tmp_pkg() + # check if tar and gzip are installed + if (find_executable('tar') is None or + find_executable('gzip') is None): + return - # check if tar is installed under win32 - if sys.platform == 'win32': - try: - spawn('tar --help') - except DistutilsExecError: - # let's return, no need to go further - return + self._init_tmp_pkg() # now building a sdist dist = Distribution() -- cgit v1.2.1 From f11ca53664d739a8c8e8194de7afdc0437ac7994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 29 Jan 2009 23:54:06 +0000 Subject: Merged revisions 69106 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69106 | tarek.ziade | 2009-01-30 00:49:17 +0100 (Fri, 30 Jan 2009) | 1 line fixed test_make_distribution so it runs on any platform, as long as tar an gzip are available ........ --- tests/test_sdist.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 96ec381e..0d839b5c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -10,7 +10,7 @@ from distutils.command.sdist import sdist from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError -from distutils.spawn import spawn +from distutils.spawn import find_executable CURDIR = os.path.dirname(__file__) TEMP_PKG = join(CURDIR, 'temppkg') @@ -111,15 +111,12 @@ class sdistTestCase(PyPIRCCommandTestCase): def test_make_distribution(self): - self._init_tmp_pkg() + # check if tar and gzip are installed + if (find_executable('tar') is None or + find_executable('gzip') is None): + return - # check if tar is installed under win32 - if sys.platform == 'win32': - try: - spawn('tar --help') - except DistutilsExecError: - # let's return, no need to go further - return + self._init_tmp_pkg() # now building a sdist dist = Distribution() -- cgit v1.2.1 From 77931446b65e84f947756a3a32eecf0cf3a05fde Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 30 Jan 2009 04:00:29 +0000 Subject: Merged revisions 68840,68881,68943,68945 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68840 | andrew.kuchling | 2009-01-20 20:15:43 -0600 (Tue, 20 Jan 2009) | 1 line Add some items ........ r68881 | andrew.kuchling | 2009-01-23 21:28:18 -0600 (Fri, 23 Jan 2009) | 1 line Add various items ........ r68943 | tarek.ziade | 2009-01-25 16:09:10 -0600 (Sun, 25 Jan 2009) | 1 line Issue #5052: removed backward compatibility information (out of date) ........ r68945 | tarek.ziade | 2009-01-25 16:11:04 -0600 (Sun, 25 Jan 2009) | 1 line added missing module docstring ........ --- command/install_lib.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/command/install_lib.py b/command/install_lib.py index ac620fcc..94895f42 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -1,3 +1,8 @@ +"""distutils.command.install_lib + +Implements the Distutils 'install_lib' command +(install all Python modules).""" + __revision__ = "$Id$" import sys, os -- cgit v1.2.1 From ec8ccd9e3fd60d7ffac44b54fef21c18efbd14ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 5 Feb 2009 09:06:23 +0000 Subject: Fix comment for #1835 --- __init__.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/__init__.py b/__init__.py index 57fcf976..e418214c 100644 --- a/__init__.py +++ b/__init__.py @@ -12,13 +12,8 @@ __revision__ = "$Id$" # Distutils version # -# Please coordinate with Marc-Andre Lemburg when adding -# new features to distutils that would warrant bumping the version number. +# Updated automatically by the Python release process. # -# In general, major and minor version should loosely follow the Python -# version number the distutils code was shipped with. -# - #--start constants-- __version__ = "2.6" #--end constants-- -- cgit v1.2.1 From 7126baf0a432aac54faee9bffcb78e93e67bced8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 5 Feb 2009 09:08:59 +0000 Subject: Merged revisions 69285 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69285 | tarek.ziade | 2009-02-05 10:06:23 +0100 (Thu, 05 Feb 2009) | 1 line Fix comment for #1835 ........ --- __init__.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/__init__.py b/__init__.py index 34fc008a..1cf8a6ea 100644 --- a/__init__.py +++ b/__init__.py @@ -12,13 +12,8 @@ __revision__ = "$Id$" # Distutils version # -# Please coordinate with Marc-Andre Lemburg when adding -# new features to distutils that would warrant bumping the version number. +# Updated automatically by the Python release process. # -# In general, major and minor version should loosely follow the Python -# version number the distutils code was shipped with. -# - #--start constants-- __version__ = "3.1a0" #--end constants-- -- cgit v1.2.1 From a1f7b98d85ddc39f6473eec042ddb6e85e39433f Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 5 Feb 2009 16:14:39 +0000 Subject: Fix get_python_inc() to work when building in a directory separate from the source. Also, define 'srcdir' on non-posix platforms. --- sysconfig.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 9993fba1..ec2f8a9c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -73,14 +73,17 @@ def get_python_inc(plat_specific=0, prefix=None): prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": if python_build: + # Assume the executable is in the build directory. The + # pyconfig.h file should be in the same directory. Since + # the build directory may not be the source directory, we + # must use "srcdir" from the makefile to find the "Include" + # directory. base = os.path.dirname(os.path.abspath(sys.executable)) if plat_specific: - inc_dir = base + return base else: - inc_dir = os.path.join(base, "Include") - if not os.path.exists(inc_dir): - inc_dir = os.path.join(os.path.dirname(base), "Include") - return inc_dir + incdir = os.path.join(get_config_var('srcdir'), 'Include') + return os.path.normpath(incdir) return os.path.join(prefix, "include", "python" + get_python_version()) elif os.name == "nt": return os.path.join(prefix, "include") @@ -521,6 +524,9 @@ def get_config_vars(*args): _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + if 'srcdir' not in _config_vars: + _config_vars['srcdir'] = project_base + if sys.platform == 'darwin': kernel_version = os.uname()[2] # Kernel version (8.4.3) major_version = int(kernel_version.split('.')[0]) -- cgit v1.2.1 From d1ea619952d81916af8fcfdb82e1961ae114e4e1 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 5 Feb 2009 16:19:05 +0000 Subject: Since sysconfig.get_python_inc() now works when building in a directory other than the source directory, simplify the test code in test_sysconfig.py. --- tests/test_sysconfig.py | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index aa1187e7..397bb12c 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -19,27 +19,10 @@ class SysconfigTestCase(unittest.TestCase): # test for pythonxx.lib? def test_get_python_inc(self): - # The check for srcdir is copied from Python's setup.py, - # and is necessary to make this test pass when building - # Python in a directory other than the source directory. - (srcdir,) = sysconfig.get_config_vars('srcdir') - if not srcdir: - inc_dir = sysconfig.get_python_inc() - else: - # This test is not really a proper test: when building - # Python from source, even in the same directory, - # we won't be testing the same thing as when running - # distutils' tests on an installed Python. Nevertheless, - # let's try to do our best: if we are running Python's - # unittests from a build directory that is not the source - # directory, the normal inc_dir will exist, it will just not - # contain anything of interest. - inc_dir = sysconfig.get_python_inc() - self.assert_(os.path.isdir(inc_dir)) - # Now test the source location, to make sure Python.h does - # exist. - inc_dir = os.path.join(os.getcwd(), srcdir, 'Include') - inc_dir = os.path.normpath(inc_dir) + inc_dir = sysconfig.get_python_inc() + # This is not much of a test. We make sure Python.h exists + # in the directory returned by get_python_inc() but we don't know + # it is the correct file. self.assert_(os.path.isdir(inc_dir), inc_dir) python_h = os.path.join(inc_dir, "Python.h") self.assert_(os.path.isfile(python_h), python_h) -- cgit v1.2.1 From bf94d914167a979fe2bc878bb594335fe3aaeb9b Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 5 Feb 2009 16:25:16 +0000 Subject: Fix test_build_ext.py to work when building in a separate directory. Since "srcdir" should now be defined on all platforms, use it to find the module source. --- tests/test_build_ext.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 31b8b481..b268820f 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -11,6 +11,10 @@ from distutils import sysconfig import unittest from test import test_support +def _get_source_filename(): + srcdir = sysconfig.get_config_var('srcdir') + return os.path.join(srcdir, 'Modules', 'xxmodule.c') + class BuildExtTestCase(unittest.TestCase): def setUp(self): # Create a simple test environment @@ -18,9 +22,7 @@ class BuildExtTestCase(unittest.TestCase): self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) - - xx_c = os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') - shutil.copy(xx_c, self.tmp_dir) + shutil.copy(_get_source_filename(), self.tmp_dir) def test_build_ext(self): xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') @@ -66,9 +68,11 @@ class BuildExtTestCase(unittest.TestCase): shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') def test_suite(): - if not sysconfig.python_build: + src = _get_source_filename() + if not os.path.exists(src): if test_support.verbose: - print 'test_build_ext: The test must be run in a python build dir' + print ('test_build_ext: Cannot find source code (test' + ' must run in python build dir)') return unittest.TestSuite() else: return unittest.makeSuite(BuildExtTestCase) -- cgit v1.2.1 From 092c2c911cf324eb35fe10416fe35d2fdb8d9e1c Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 5 Feb 2009 16:33:41 +0000 Subject: Fix get_python_inc() to work when building in a directory separate from the source. Also, define 'srcdir' on non-posix platforms. --- sysconfig.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index b17743a8..de8c5fcc 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -72,14 +72,17 @@ def get_python_inc(plat_specific=0, prefix=None): prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": if python_build: + # Assume the executable is in the build directory. The + # pyconfig.h file should be in the same directory. Since + # the build directory may not be the source directory, we + # must use "srcdir" from the makefile to find the "Include" + # directory. base = os.path.dirname(os.path.abspath(sys.executable)) if plat_specific: - inc_dir = base + return base else: - inc_dir = os.path.join(base, "Include") - if not os.path.exists(inc_dir): - inc_dir = os.path.join(os.path.dirname(base), "Include") - return inc_dir + incdir = os.path.join(get_config_var('srcdir'), 'Include') + return os.path.normpath(incdir) return os.path.join(prefix, "include", "python" + get_python_version()) elif os.name == "nt": return os.path.join(prefix, "include") -- cgit v1.2.1 From 79a5c6b29982615cff8c78b6ee045cdd3a26ac55 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 5 Feb 2009 16:35:04 +0000 Subject: Since sysconfig.get_python_inc() now works when building in a directory other than the source directory, simplify the test code in test_sysconfig.py. --- tests/test_sysconfig.py | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index c6ab9aa5..490410e1 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -19,27 +19,10 @@ class SysconfigTestCase(unittest.TestCase): # test for pythonxx.lib? def test_get_python_inc(self): - # The check for srcdir is copied from Python's setup.py, - # and is necessary to make this test pass when building - # Python in a directory other than the source directory. - (srcdir,) = sysconfig.get_config_vars('srcdir') - if not srcdir: - inc_dir = sysconfig.get_python_inc() - else: - # This test is not really a proper test: when building - # Python from source, even in the same directory, - # we won't be testing the same thing as when running - # distutils' tests on an installed Python. Nevertheless, - # let's try to do our best: if we are running Python's - # unittests from a build directory that is not the source - # directory, the normal inc_dir will exist, it will just not - # contain anything of interest. - inc_dir = sysconfig.get_python_inc() - self.assert_(os.path.isdir(inc_dir)) - # Now test the source location, to make sure Python.h does - # exist. - inc_dir = os.path.join(os.getcwd(), srcdir, 'Include') - inc_dir = os.path.normpath(inc_dir) + inc_dir = sysconfig.get_python_inc() + # This is not much of a test. We make sure Python.h exists + # in the directory returned by get_python_inc() but we don't know + # it is the correct file. self.assert_(os.path.isdir(inc_dir), inc_dir) python_h = os.path.join(inc_dir, "Python.h") self.assert_(os.path.isfile(python_h), python_h) -- cgit v1.2.1 From efc1ec81a7cc219b13f025b3bc75a8e1a1cdb4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 5 Feb 2009 22:52:52 +0000 Subject: Fixed #5132: enable extensions to link on Solaris --- command/build_ext.py | 8 +++++--- tests/test_build_ext.py | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index fbf2a0b2..2ed3ef65 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -231,10 +231,12 @@ class build_ext (Command): # building python standard extensions self.library_dirs.append('.') - # for extensions under Linux with a shared Python library, + # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs - if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ - and sysconfig.get_config_var('Py_ENABLE_SHARED'): + sysconfig.get_config_var('Py_ENABLE_SHARED') + if ((sys.platform.startswith('linux') or sys.platform.startswith('gnu') + or sys.platform.startswith('sunos')) + and sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index b268820f..780660da 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -67,6 +67,27 @@ class BuildExtTestCase(unittest.TestCase): # XXX on Windows the test leaves a directory with xx module in TEMP shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') + def test_solaris_enable_shared(self): + dist = Distribution({'name': 'xx'}) + cmd = build_ext(dist) + old = sys.platform + + sys.platform = 'sunos' # fooling finalize_options + from distutils.sysconfig import _config_vars + old_var = _config_vars.get('Py_ENABLE_SHARED') + _config_vars['Py_ENABLE_SHARED'] = 1 + try: + cmd.ensure_finalized() + finally: + sys.platform = old + if old_var is None: + del _config_vars['Py_ENABLE_SHARED'] + else: + _config_vars['Py_ENABLE_SHARED'] = old_var + + # make sur we get some lobrary dirs under solaris + self.assert_(len(cmd.library_dirs) > 0) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From f148277fd0221d779fe64e6d0715f7c713d24c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 5 Feb 2009 22:55:00 +0000 Subject: Merged revisions 69316 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69316 | tarek.ziade | 2009-02-05 23:52:52 +0100 (Thu, 05 Feb 2009) | 1 line Fixed #5132: enable extensions to link on Solaris ........ --- command/build_ext.py | 8 +++++--- tests/test_build_ext.py | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 1461409f..936ea8d0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -233,10 +233,12 @@ class build_ext (Command): # building python standard extensions self.library_dirs.append('.') - # for extensions under Linux with a shared Python library, + # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs - if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ - and sysconfig.get_config_var('Py_ENABLE_SHARED'): + sysconfig.get_config_var('Py_ENABLE_SHARED') + if ((sys.platform.startswith('linux') or sys.platform.startswith('gnu') + or sys.platform.startswith('sunos')) + and sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 31b8b481..d0cce9ba 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -65,6 +65,27 @@ class BuildExtTestCase(unittest.TestCase): # XXX on Windows the test leaves a directory with xx module in TEMP shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') + def test_solaris_enable_shared(self): + dist = Distribution({'name': 'xx'}) + cmd = build_ext(dist) + old = sys.platform + + sys.platform = 'sunos' # fooling finalize_options + from distutils.sysconfig import _config_vars + old_var = _config_vars.get('Py_ENABLE_SHARED') + _config_vars['Py_ENABLE_SHARED'] = 1 + try: + cmd.ensure_finalized() + finally: + sys.platform = old + if old_var is None: + del _config_vars['Py_ENABLE_SHARED'] + else: + _config_vars['Py_ENABLE_SHARED'] = old_var + + # make sur we get some lobrary dirs under solaris + self.assert_(len(cmd.library_dirs) > 0) + def test_suite(): if not sysconfig.python_build: if test_support.verbose: -- cgit v1.2.1 From 1ccb3b08c1c773ad4eaff4d253d7cddecb8a4b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 5 Feb 2009 22:56:14 +0000 Subject: Merged revisions 69316 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69316 | tarek.ziade | 2009-02-05 23:52:52 +0100 (Thu, 05 Feb 2009) | 1 line Fixed #5132: enable extensions to link on Solaris ........ --- command/build_ext.py | 8 +++++--- tests/test_build_ext.py | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 7ef5becc..b12da63f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -229,10 +229,12 @@ class build_ext(Command): # building python standard extensions self.library_dirs.append('.') - # for extensions under Linux with a shared Python library, + # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs - if (sys.platform.startswith('linux') or sys.platform.startswith('gnu')) \ - and sysconfig.get_config_var('Py_ENABLE_SHARED'): + sysconfig.get_config_var('Py_ENABLE_SHARED') + if ((sys.platform.startswith('linux') or sys.platform.startswith('gnu') + or sys.platform.startswith('sunos')) + and sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 52752977..4c572325 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -75,6 +75,27 @@ class BuildExtTestCase(unittest.TestCase): # XXX on Windows the test leaves a directory with xx module in TEMP shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') + def test_solaris_enable_shared(self): + dist = Distribution({'name': 'xx'}) + cmd = build_ext(dist) + old = sys.platform + + sys.platform = 'sunos' # fooling finalize_options + from distutils.sysconfig import _config_vars + old_var = _config_vars.get('Py_ENABLE_SHARED') + _config_vars['Py_ENABLE_SHARED'] = 1 + try: + cmd.ensure_finalized() + finally: + sys.platform = old + if old_var is None: + del _config_vars['Py_ENABLE_SHARED'] + else: + _config_vars['Py_ENABLE_SHARED'] = old_var + + # make sur we get some lobrary dirs under solaris + self.assert_(len(cmd.library_dirs) > 0) + def test_suite(): if not sysconfig.python_build: if support.verbose: -- cgit v1.2.1 From fed87c12d428957f2341abafa44669549b649c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 00:31:59 +0000 Subject: Fixed #1276768: verbose option was not used in the code. --- dir_util.py | 30 ++++++++-------- file_util.py | 22 +++++++----- tests/test_dir_util.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_file_util.py | 66 +++++++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 24 deletions(-) create mode 100644 tests/test_dir_util.py create mode 100644 tests/test_file_util.py diff --git a/dir_util.py b/dir_util.py index 54f5c68e..6d896ee4 100644 --- a/dir_util.py +++ b/dir_util.py @@ -16,7 +16,7 @@ _path_created = {} # I don't use os.makedirs because a) it's new to Python 1.5.2, and # b) it blows up if the directory already exists (I want to silently # succeed in that case). -def mkpath (name, mode=0777, verbose=0, dry_run=0): +def mkpath (name, mode=0777, verbose=1, dry_run=0): """Create a directory and any missing ancestor directories. If the directory already exists (or if 'name' is the empty string, which means the current directory, which of course exists), then do @@ -49,13 +49,9 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): tails = [tail] # stack of lone dirs to create while head and tail and not os.path.isdir(head): - #print "splitting '%s': " % head, (head, tail) = os.path.split(head) - #print "to ('%s','%s')" % (head, tail) tails.insert(0, tail) # push next higher dir onto stack - #print "stack of tails:", tails - # now 'head' contains the deepest directory that already exists # (that is, the child of 'head' in 'name' is the highest directory # that does *not* exist) @@ -67,7 +63,8 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): if _path_created.get(abs_head): continue - log.info("creating %s", head) + if verbose == 1: + log.info("creating %s", head) if not dry_run: try: @@ -83,7 +80,7 @@ def mkpath (name, mode=0777, verbose=0, dry_run=0): # mkpath () -def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0): +def create_tree (base_dir, files, mode=0777, verbose=1, dry_run=0): """Create all the empty directories under 'base_dir' needed to put 'files' there. 'base_dir' is just the a name of a directory @@ -102,7 +99,7 @@ def create_tree (base_dir, files, mode=0777, verbose=0, dry_run=0): # Now create them for dir in need_dirs: - mkpath(dir, mode, dry_run=dry_run) + mkpath(dir, mode, verbose=verbose, dry_run=dry_run) # create_tree () @@ -112,7 +109,7 @@ def copy_tree (src, dst, preserve_times=1, preserve_symlinks=0, update=0, - verbose=0, + verbose=1, dry_run=0): """Copy an entire directory tree 'src' to a new location 'dst'. Both @@ -148,7 +145,7 @@ def copy_tree (src, dst, "error listing files in '%s': %s" % (src, errstr) if not dry_run: - mkpath(dst) + mkpath(dst, verbose=verbose) outputs = [] @@ -158,7 +155,8 @@ def copy_tree (src, dst, if preserve_symlinks and os.path.islink(src_name): link_dest = os.readlink(src_name) - log.info("linking %s -> %s", dst_name, link_dest) + if verbose == 1: + log.info("linking %s -> %s", dst_name, link_dest) if not dry_run: os.symlink(link_dest, dst_name) outputs.append(dst_name) @@ -167,10 +165,11 @@ def copy_tree (src, dst, outputs.extend( copy_tree(src_name, dst_name, preserve_mode, preserve_times, preserve_symlinks, update, - dry_run=dry_run)) + verbose=verbose, dry_run=dry_run)) else: copy_file(src_name, dst_name, preserve_mode, - preserve_times, update, dry_run=dry_run) + preserve_times, update, verbose=verbose, + dry_run=dry_run) outputs.append(dst_name) return outputs @@ -188,14 +187,15 @@ def _build_cmdtuple(path, cmdtuples): cmdtuples.append((os.rmdir, path)) -def remove_tree (directory, verbose=0, dry_run=0): +def remove_tree (directory, verbose=1, dry_run=0): """Recursively remove an entire directory tree. Any errors are ignored (apart from being reported to stdout if 'verbose' is true). """ from distutils.util import grok_environment_error global _path_created - log.info("removing '%s' (and everything under it)", directory) + if verbose == 1: + log.info("removing '%s' (and everything under it)", directory) if dry_run: return cmdtuples = [] diff --git a/file_util.py b/file_util.py index 3af6344f..82a8b0ad 100644 --- a/file_util.py +++ b/file_util.py @@ -76,7 +76,7 @@ def copy_file (src, dst, preserve_times=1, update=0, link=None, - verbose=0, + verbose=1, dry_run=0): """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is @@ -123,7 +123,8 @@ def copy_file (src, dst, dir = os.path.dirname(dst) if update and not newer(src, dst): - log.debug("not copying %s (output up-to-date)", src) + if verbose == 1: + log.debug("not copying %s (output up-to-date)", src) return dst, 0 try: @@ -131,10 +132,12 @@ def copy_file (src, dst, except KeyError: raise ValueError, \ "invalid value '%s' for 'link' argument" % link - if os.path.basename(dst) == os.path.basename(src): - log.info("%s %s -> %s", action, src, dir) - else: - log.info("%s %s -> %s", action, src, dst) + + if verbose == 1: + if os.path.basename(dst) == os.path.basename(src): + log.info("%s %s -> %s", action, src, dir) + else: + log.info("%s %s -> %s", action, src, dst) if dry_run: return (dst, 1) @@ -178,7 +181,7 @@ def copy_file (src, dst, # XXX I suspect this is Unix-specific -- need porting help! def move_file (src, dst, - verbose=0, + verbose=1, dry_run=0): """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will @@ -191,7 +194,8 @@ def move_file (src, dst, from os.path import exists, isfile, isdir, basename, dirname import errno - log.info("moving %s -> %s", src, dst) + if verbose == 1: + log.info("moving %s -> %s", src, dst) if dry_run: return dst @@ -223,7 +227,7 @@ def move_file (src, dst, "couldn't move '%s' to '%s': %s" % (src, dst, msg) if copy_it: - copy_file(src, dst) + copy_file(src, dst, verbose=verbose) try: os.unlink(src) except os.error, (num, msg): diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py new file mode 100644 index 00000000..edf93c40 --- /dev/null +++ b/tests/test_dir_util.py @@ -0,0 +1,91 @@ +"""Tests for distutils.dir_util.""" +import unittest +import os +import shutil + +from distutils.dir_util import mkpath +from distutils.dir_util import remove_tree +from distutils.dir_util import create_tree +from distutils.dir_util import copy_tree + +from distutils import log + +class DirUtilTestCase(unittest.TestCase): + + def _log(self, msg, *args): + if len(args) > 0: + self._logs.append(msg % args) + else: + self._logs.append(msg) + + def setUp(self): + self._logs = [] + self.root_target = os.path.join(os.path.dirname(__file__), 'deep') + self.target = os.path.join(self.root_target, 'here') + self.target2 = os.path.join(os.path.dirname(__file__), 'deep2') + self.old_log = log.info + log.info = self._log + + def tearDown(self): + for target in (self.target, self.target2): + if os.path.exists(target): + shutil.rmtree(target) + log.info = self.old_log + + def test_mkpath_remove_tree_verbosity(self): + + mkpath(self.target, verbose=0) + wanted = [] + self.assertEquals(self._logs, wanted) + remove_tree(self.root_target, verbose=0) + + mkpath(self.target, verbose=1) + wanted = ['creating %s' % self.root_target, + 'creating %s' % self.target] + self.assertEquals(self._logs, wanted) + self._logs = [] + + remove_tree(self.root_target, verbose=1) + wanted = ["removing '%s' (and everything under it)" % self.root_target] + self.assertEquals(self._logs, wanted) + + def test_create_tree_verbosity(self): + + create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) + self.assertEquals(self._logs, []) + remove_tree(self.root_target, verbose=0) + + wanted = ['creating %s' % self.root_target] + create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) + self.assertEquals(self._logs, wanted) + + remove_tree(self.root_target, verbose=0) + + + def test_copy_tree_verbosity(self): + + mkpath(self.target, verbose=0) + + copy_tree(self.target, self.target2, verbose=0) + self.assertEquals(self._logs, []) + + remove_tree(self.root_target, verbose=0) + + mkpath(self.target, verbose=0) + a_file = os.path.join(self.target, 'ok.txt') + f = open(a_file, 'w') + f.write('some content') + f.close() + + wanted = ['copying %s -> %s' % (a_file, self.target2)] + copy_tree(self.target, self.target2, verbose=1) + self.assertEquals(self._logs, wanted) + + remove_tree(self.root_target, verbose=0) + remove_tree(self.target2, verbose=0) + +def test_suite(): + return unittest.makeSuite(DirUtilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_file_util.py b/tests/test_file_util.py new file mode 100644 index 00000000..523f1aef --- /dev/null +++ b/tests/test_file_util.py @@ -0,0 +1,66 @@ +"""Tests for distutils.file_util.""" +import unittest +import os +import shutil + +from distutils.file_util import move_file +from distutils import log + +class FileUtilTestCase(unittest.TestCase): + + def _log(self, msg, *args): + if len(args) > 0: + self._logs.append(msg % args) + else: + self._logs.append(msg) + + def setUp(self): + self._logs = [] + self.old_log = log.info + log.info = self._log + self.source = os.path.join(os.path.dirname(__file__), 'f1') + self.target = os.path.join(os.path.dirname(__file__), 'f2') + self.target_dir = os.path.join(os.path.dirname(__file__), 'd1') + + def tearDown(self): + log.info = self.old_log + for f in (self.source, self.target, self.target_dir): + if os.path.exists(f): + if os.path.isfile(f): + os.remove(f) + else: + shutil.rmtree(f) + + def test_move_file_verbosity(self): + + f = open(self.source, 'w') + f.write('some content') + f.close() + + move_file(self.source, self.target, verbose=0) + wanted = [] + self.assertEquals(self._logs, wanted) + + # back to original state + move_file(self.target, self.source, verbose=0) + + move_file(self.source, self.target, verbose=1) + wanted = ['moving %s -> %s' % (self.source, self.target)] + self.assertEquals(self._logs, wanted) + + # back to original state + move_file(self.target, self.source, verbose=0) + + self._logs = [] + # now the target is a dir + os.mkdir(self.target_dir) + move_file(self.source, self.target_dir, verbose=1) + wanted = ['moving %s -> %s' % (self.source, self.target_dir)] + self.assertEquals(self._logs, wanted) + + +def test_suite(): + return unittest.makeSuite(FileUtilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 96983b36bdea4053cc97656f1a6c10194a7f4fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 00:38:35 +0000 Subject: Merged revisions 69324 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69324 | tarek.ziade | 2009-02-06 01:31:59 +0100 (Fri, 06 Feb 2009) | 1 line Fixed #1276768: verbose option was not used in the code. ........ --- dir_util.py | 30 ++++++++-------- file_util.py | 22 +++++++----- tests/test_dir_util.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_file_util.py | 66 +++++++++++++++++++++++++++++++++++ 4 files changed, 185 insertions(+), 24 deletions(-) create mode 100644 tests/test_dir_util.py create mode 100644 tests/test_file_util.py diff --git a/dir_util.py b/dir_util.py index 1f0d49c2..db754e5c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -15,7 +15,7 @@ _path_created = {} # I don't use os.makedirs because a) it's new to Python 1.5.2, and # b) it blows up if the directory already exists (I want to silently # succeed in that case). -def mkpath (name, mode=0o777, verbose=0, dry_run=0): +def mkpath (name, mode=0o777, verbose=1, dry_run=0): """Create a directory and any missing ancestor directories. If the directory already exists (or if 'name' is the empty string, which means the current directory, which of course exists), then do @@ -48,13 +48,9 @@ def mkpath (name, mode=0o777, verbose=0, dry_run=0): tails = [tail] # stack of lone dirs to create while head and tail and not os.path.isdir(head): - #print "splitting '%s': " % head, (head, tail) = os.path.split(head) - #print "to ('%s','%s')" % (head, tail) tails.insert(0, tail) # push next higher dir onto stack - #print "stack of tails:", tails - # now 'head' contains the deepest directory that already exists # (that is, the child of 'head' in 'name' is the highest directory # that does *not* exist) @@ -66,7 +62,8 @@ def mkpath (name, mode=0o777, verbose=0, dry_run=0): if _path_created.get(abs_head): continue - log.info("creating %s", head) + if verbose == 1: + log.info("creating %s", head) if not dry_run: try: @@ -82,7 +79,7 @@ def mkpath (name, mode=0o777, verbose=0, dry_run=0): # mkpath () -def create_tree (base_dir, files, mode=0o777, verbose=0, dry_run=0): +def create_tree (base_dir, files, mode=0o777, verbose=1, dry_run=0): """Create all the empty directories under 'base_dir' needed to put 'files' there. 'base_dir' is just the a name of a directory @@ -99,7 +96,7 @@ def create_tree (base_dir, files, mode=0o777, verbose=0, dry_run=0): # Now create them for dir in sorted(need_dir): - mkpath(dir, mode, dry_run=dry_run) + mkpath(dir, mode, verbose=verbose, dry_run=dry_run) # create_tree () @@ -109,7 +106,7 @@ def copy_tree (src, dst, preserve_times=1, preserve_symlinks=0, update=0, - verbose=0, + verbose=1, dry_run=0): """Copy an entire directory tree 'src' to a new location 'dst'. Both @@ -146,7 +143,7 @@ def copy_tree (src, dst, "error listing files in '%s': %s" % (src, errstr)) if not dry_run: - mkpath(dst) + mkpath(dst, verbose=verbose) outputs = [] @@ -156,7 +153,8 @@ def copy_tree (src, dst, if preserve_symlinks and os.path.islink(src_name): link_dest = os.readlink(src_name) - log.info("linking %s -> %s", dst_name, link_dest) + if verbose == 1: + log.info("linking %s -> %s", dst_name, link_dest) if not dry_run: os.symlink(link_dest, dst_name) outputs.append(dst_name) @@ -165,10 +163,11 @@ def copy_tree (src, dst, outputs.extend( copy_tree(src_name, dst_name, preserve_mode, preserve_times, preserve_symlinks, update, - dry_run=dry_run)) + verbose=verbose, dry_run=dry_run)) else: copy_file(src_name, dst_name, preserve_mode, - preserve_times, update, dry_run=dry_run) + preserve_times, update, verbose=verbose, + dry_run=dry_run) outputs.append(dst_name) return outputs @@ -184,14 +183,15 @@ def _build_cmdtuple(path, cmdtuples): cmdtuples.append((os.rmdir, path)) -def remove_tree (directory, verbose=0, dry_run=0): +def remove_tree (directory, verbose=1, dry_run=0): """Recursively remove an entire directory tree. Any errors are ignored (apart from being reported to stdout if 'verbose' is true). """ from distutils.util import grok_environment_error global _path_created - log.info("removing '%s' (and everything under it)", directory) + if verbose == 1: + log.info("removing '%s' (and everything under it)", directory) if dry_run: return cmdtuples = [] diff --git a/file_util.py b/file_util.py index b46b0da6..00c5bc5b 100644 --- a/file_util.py +++ b/file_util.py @@ -67,7 +67,7 @@ def _copy_file_contents(src, dst, buffer_size=16*1024): fsrc.close() def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, - link=None, verbose=0, dry_run=0): + link=None, verbose=1, dry_run=0): """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is copied there with the same name; otherwise, it must be a filename. (If the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' @@ -112,17 +112,20 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, dir = os.path.dirname(dst) if update and not newer(src, dst): - log.debug("not copying %s (output up-to-date)", src) + if verbose == 1: + log.debug("not copying %s (output up-to-date)", src) return (dst, 0) try: action = _copy_action[link] except KeyError: raise ValueError("invalid value '%s' for 'link' argument" % link) - if os.path.basename(dst) == os.path.basename(src): - log.info("%s %s -> %s", action, src, dir) - else: - log.info("%s %s -> %s", action, src, dst) + + if verbose == 1: + if os.path.basename(dst) == os.path.basename(src): + log.info("%s %s -> %s", action, src, dir) + else: + log.info("%s %s -> %s", action, src, dst) if dry_run: return (dst, 1) @@ -164,7 +167,7 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, # XXX I suspect this is Unix-specific -- need porting help! def move_file (src, dst, - verbose=0, + verbose=1, dry_run=0): """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will @@ -177,7 +180,8 @@ def move_file (src, dst, from os.path import exists, isfile, isdir, basename, dirname import errno - log.info("moving %s -> %s", src, dst) + if verbose == 1: + log.info("moving %s -> %s", src, dst) if dry_run: return dst @@ -209,7 +213,7 @@ def move_file (src, dst, "couldn't move '%s' to '%s': %s" % (src, dst, msg)) if copy_it: - copy_file(src, dst) + copy_file(src, dst, verbose=verbose) try: os.unlink(src) except os.error as e: diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py new file mode 100644 index 00000000..edf93c40 --- /dev/null +++ b/tests/test_dir_util.py @@ -0,0 +1,91 @@ +"""Tests for distutils.dir_util.""" +import unittest +import os +import shutil + +from distutils.dir_util import mkpath +from distutils.dir_util import remove_tree +from distutils.dir_util import create_tree +from distutils.dir_util import copy_tree + +from distutils import log + +class DirUtilTestCase(unittest.TestCase): + + def _log(self, msg, *args): + if len(args) > 0: + self._logs.append(msg % args) + else: + self._logs.append(msg) + + def setUp(self): + self._logs = [] + self.root_target = os.path.join(os.path.dirname(__file__), 'deep') + self.target = os.path.join(self.root_target, 'here') + self.target2 = os.path.join(os.path.dirname(__file__), 'deep2') + self.old_log = log.info + log.info = self._log + + def tearDown(self): + for target in (self.target, self.target2): + if os.path.exists(target): + shutil.rmtree(target) + log.info = self.old_log + + def test_mkpath_remove_tree_verbosity(self): + + mkpath(self.target, verbose=0) + wanted = [] + self.assertEquals(self._logs, wanted) + remove_tree(self.root_target, verbose=0) + + mkpath(self.target, verbose=1) + wanted = ['creating %s' % self.root_target, + 'creating %s' % self.target] + self.assertEquals(self._logs, wanted) + self._logs = [] + + remove_tree(self.root_target, verbose=1) + wanted = ["removing '%s' (and everything under it)" % self.root_target] + self.assertEquals(self._logs, wanted) + + def test_create_tree_verbosity(self): + + create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) + self.assertEquals(self._logs, []) + remove_tree(self.root_target, verbose=0) + + wanted = ['creating %s' % self.root_target] + create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) + self.assertEquals(self._logs, wanted) + + remove_tree(self.root_target, verbose=0) + + + def test_copy_tree_verbosity(self): + + mkpath(self.target, verbose=0) + + copy_tree(self.target, self.target2, verbose=0) + self.assertEquals(self._logs, []) + + remove_tree(self.root_target, verbose=0) + + mkpath(self.target, verbose=0) + a_file = os.path.join(self.target, 'ok.txt') + f = open(a_file, 'w') + f.write('some content') + f.close() + + wanted = ['copying %s -> %s' % (a_file, self.target2)] + copy_tree(self.target, self.target2, verbose=1) + self.assertEquals(self._logs, wanted) + + remove_tree(self.root_target, verbose=0) + remove_tree(self.target2, verbose=0) + +def test_suite(): + return unittest.makeSuite(DirUtilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_file_util.py b/tests/test_file_util.py new file mode 100644 index 00000000..523f1aef --- /dev/null +++ b/tests/test_file_util.py @@ -0,0 +1,66 @@ +"""Tests for distutils.file_util.""" +import unittest +import os +import shutil + +from distutils.file_util import move_file +from distutils import log + +class FileUtilTestCase(unittest.TestCase): + + def _log(self, msg, *args): + if len(args) > 0: + self._logs.append(msg % args) + else: + self._logs.append(msg) + + def setUp(self): + self._logs = [] + self.old_log = log.info + log.info = self._log + self.source = os.path.join(os.path.dirname(__file__), 'f1') + self.target = os.path.join(os.path.dirname(__file__), 'f2') + self.target_dir = os.path.join(os.path.dirname(__file__), 'd1') + + def tearDown(self): + log.info = self.old_log + for f in (self.source, self.target, self.target_dir): + if os.path.exists(f): + if os.path.isfile(f): + os.remove(f) + else: + shutil.rmtree(f) + + def test_move_file_verbosity(self): + + f = open(self.source, 'w') + f.write('some content') + f.close() + + move_file(self.source, self.target, verbose=0) + wanted = [] + self.assertEquals(self._logs, wanted) + + # back to original state + move_file(self.target, self.source, verbose=0) + + move_file(self.source, self.target, verbose=1) + wanted = ['moving %s -> %s' % (self.source, self.target)] + self.assertEquals(self._logs, wanted) + + # back to original state + move_file(self.target, self.source, verbose=0) + + self._logs = [] + # now the target is a dir + os.mkdir(self.target_dir) + move_file(self.source, self.target_dir, verbose=1) + wanted = ['moving %s -> %s' % (self.source, self.target_dir)] + self.assertEquals(self._logs, wanted) + + +def test_suite(): + return unittest.makeSuite(FileUtilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From f5de94cd2bf6253c15fdea5a9963847476c7233d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 00:46:57 +0000 Subject: README now reflects the current state --- README | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/README b/README index 45c7ca8c..23f48850 100644 --- a/README +++ b/README @@ -1,22 +1,11 @@ -This directory contains only a subset of the Distutils, specifically -the Python modules in the 'distutils' and 'distutils.command' -packages. This is all you need to distribute and install Python -modules using the Distutils. There is also a separately packaged -standalone version of the Distutils available for people who want to -upgrade the Distutils without upgrading Python, available from the -Distutils web page: +This directory contains the Distutils package. - http://www.python.org/sigs/distutils-sig/ +There's a full documentation available at: -The standalone version includes all of the code in this directory, -plus documentation, test scripts, examples, etc. + http://docs.python.org/distutils/ -The Distutils documentation is divided into two documents, "Installing -Python Modules", which explains how to install Python packages, and -"Distributing Python Modules", which explains how to write setup.py -files. Both documents are part of the standard Python documentation -set, and are available from http://www.python.org/doc/current/ . +The Distutils-SIG web page is also a good starting point: - Greg Ward (gward@python.net) + http://www.python.org/sigs/distutils-sig/ $Id$ -- cgit v1.2.1 From cbed4405dbe59d12aa1dbaea1437beff6709235a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 00:49:45 +0000 Subject: using >= so setting verbose to 2 will work as well --- dir_util.py | 6 +++--- file_util.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dir_util.py b/dir_util.py index 6d896ee4..5a134408 100644 --- a/dir_util.py +++ b/dir_util.py @@ -63,7 +63,7 @@ def mkpath (name, mode=0777, verbose=1, dry_run=0): if _path_created.get(abs_head): continue - if verbose == 1: + if verbose >= 1: log.info("creating %s", head) if not dry_run: @@ -155,7 +155,7 @@ def copy_tree (src, dst, if preserve_symlinks and os.path.islink(src_name): link_dest = os.readlink(src_name) - if verbose == 1: + if verbose >= 1: log.info("linking %s -> %s", dst_name, link_dest) if not dry_run: os.symlink(link_dest, dst_name) @@ -194,7 +194,7 @@ def remove_tree (directory, verbose=1, dry_run=0): from distutils.util import grok_environment_error global _path_created - if verbose == 1: + if verbose >= 1: log.info("removing '%s' (and everything under it)", directory) if dry_run: return diff --git a/file_util.py b/file_util.py index 82a8b0ad..0c890559 100644 --- a/file_util.py +++ b/file_util.py @@ -123,7 +123,7 @@ def copy_file (src, dst, dir = os.path.dirname(dst) if update and not newer(src, dst): - if verbose == 1: + if verbose >= 1: log.debug("not copying %s (output up-to-date)", src) return dst, 0 @@ -133,7 +133,7 @@ def copy_file (src, dst, raise ValueError, \ "invalid value '%s' for 'link' argument" % link - if verbose == 1: + if verbose >= 1: if os.path.basename(dst) == os.path.basename(src): log.info("%s %s -> %s", action, src, dir) else: @@ -194,7 +194,7 @@ def move_file (src, dst, from os.path import exists, isfile, isdir, basename, dirname import errno - if verbose == 1: + if verbose >= 1: log.info("moving %s -> %s", src, dst) if dry_run: -- cgit v1.2.1 From 24ce784984a61381cdc39cb0ed3f3d9cfe38439b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 00:52:52 +0000 Subject: Merged revisions 69330 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69330 | tarek.ziade | 2009-02-06 01:46:57 +0100 (Fri, 06 Feb 2009) | 1 line README now reflects the current state ........ --- README | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/README b/README index 45c7ca8c..23f48850 100644 --- a/README +++ b/README @@ -1,22 +1,11 @@ -This directory contains only a subset of the Distutils, specifically -the Python modules in the 'distutils' and 'distutils.command' -packages. This is all you need to distribute and install Python -modules using the Distutils. There is also a separately packaged -standalone version of the Distutils available for people who want to -upgrade the Distutils without upgrading Python, available from the -Distutils web page: +This directory contains the Distutils package. - http://www.python.org/sigs/distutils-sig/ +There's a full documentation available at: -The standalone version includes all of the code in this directory, -plus documentation, test scripts, examples, etc. + http://docs.python.org/distutils/ -The Distutils documentation is divided into two documents, "Installing -Python Modules", which explains how to install Python packages, and -"Distributing Python Modules", which explains how to write setup.py -files. Both documents are part of the standard Python documentation -set, and are available from http://www.python.org/doc/current/ . +The Distutils-SIG web page is also a good starting point: - Greg Ward (gward@python.net) + http://www.python.org/sigs/distutils-sig/ $Id$ -- cgit v1.2.1 From b01bd4697af5320818653fc06a17fde9a7770a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 00:53:43 +0000 Subject: Merged revisions 69332 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69332 | tarek.ziade | 2009-02-06 01:49:45 +0100 (Fri, 06 Feb 2009) | 1 line using >= so setting verbose to 2 will work as well ........ --- dir_util.py | 6 +++--- file_util.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dir_util.py b/dir_util.py index db754e5c..ed54ccef 100644 --- a/dir_util.py +++ b/dir_util.py @@ -62,7 +62,7 @@ def mkpath (name, mode=0o777, verbose=1, dry_run=0): if _path_created.get(abs_head): continue - if verbose == 1: + if verbose >= 1: log.info("creating %s", head) if not dry_run: @@ -153,7 +153,7 @@ def copy_tree (src, dst, if preserve_symlinks and os.path.islink(src_name): link_dest = os.readlink(src_name) - if verbose == 1: + if verbose >= 1: log.info("linking %s -> %s", dst_name, link_dest) if not dry_run: os.symlink(link_dest, dst_name) @@ -190,7 +190,7 @@ def remove_tree (directory, verbose=1, dry_run=0): from distutils.util import grok_environment_error global _path_created - if verbose == 1: + if verbose >= 1: log.info("removing '%s' (and everything under it)", directory) if dry_run: return diff --git a/file_util.py b/file_util.py index 00c5bc5b..65aa7e0f 100644 --- a/file_util.py +++ b/file_util.py @@ -112,7 +112,7 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, dir = os.path.dirname(dst) if update and not newer(src, dst): - if verbose == 1: + if verbose >= 1: log.debug("not copying %s (output up-to-date)", src) return (dst, 0) @@ -121,7 +121,7 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, except KeyError: raise ValueError("invalid value '%s' for 'link' argument" % link) - if verbose == 1: + if verbose >= 1: if os.path.basename(dst) == os.path.basename(src): log.info("%s %s -> %s", action, src, dir) else: @@ -180,7 +180,7 @@ def move_file (src, dst, from os.path import exists, isfile, isdir, basename, dirname import errno - if verbose == 1: + if verbose >= 1: log.info("moving %s -> %s", src, dst) if dry_run: -- cgit v1.2.1 From 4ca827a930005d5a11aa21c4ee89374398c1baa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 01:15:51 +0000 Subject: fixed #1520877: now distutils reads Read from the environment/Makefile --- sysconfig.py | 9 ++++++--- tests/test_sysconfig.py | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index ec2f8a9c..deb51a1c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -165,9 +165,9 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext) = \ + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar) = \ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO') + 'CCSHARED', 'LDSHARED', 'SO', 'AR') if 'CC' in os.environ: cc = os.environ['CC'] @@ -188,6 +188,8 @@ def customize_compiler(compiler): cpp = cpp + ' ' + os.environ['CPPFLAGS'] cflags = cflags + ' ' + os.environ['CPPFLAGS'] ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] cc_cmd = cc + ' ' + cflags compiler.set_executables( @@ -196,7 +198,8 @@ def customize_compiler(compiler): compiler_so=cc_cmd + ' ' + ccshared, compiler_cxx=cxx, linker_so=ldshared, - linker_exe=cc) + linker_exe=cc, + archiver=ar) compiler.shared_lib_extension = so_ext diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 397bb12c..7f0bce63 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -8,6 +8,13 @@ from test.test_support import TESTFN class SysconfigTestCase(unittest.TestCase): + def setUp(self): + self.old_AR = os.environ.get('AR') + + def tearDown(self): + if self.old_AR is not None: + os.environ['AR'] = self.old_AR + def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() self.assert_(os.path.isfile(config_h), config_h) @@ -32,6 +39,21 @@ class SysconfigTestCase(unittest.TestCase): self.assert_(isinstance(cvars, dict)) self.assert_(cvars) + def test_customize_compiler(self): + + os.environ['AR'] = 'xxx' + + # make sure AR gets caught + class compiler: + compiler_type = 'unix' + + def set_executables(self, **kw): + self.exes = kw + + comp = compiler() + sysconfig.customize_compiler(comp) + self.assertEquals(comp.exes['archiver'], 'xxx') + def test_suite(): suite = unittest.TestSuite() -- cgit v1.2.1 From 6512d9fd3c7bf335a66ba8338956f3f545a9999c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 01:18:36 +0000 Subject: Merged revisions 69342 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69342 | tarek.ziade | 2009-02-06 02:15:51 +0100 (Fri, 06 Feb 2009) | 1 line fixed #1520877: now distutils reads Read from the environment/Makefile ........ --- sysconfig.py | 9 ++++++--- tests/test_sysconfig.py | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index de8c5fcc..386ae89b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -160,9 +160,9 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext) = \ + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar) = \ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO') + 'CCSHARED', 'LDSHARED', 'SO', 'AR') if 'CC' in os.environ: cc = os.environ['CC'] @@ -183,6 +183,8 @@ def customize_compiler(compiler): cpp = cpp + ' ' + os.environ['CPPFLAGS'] cflags = cflags + ' ' + os.environ['CPPFLAGS'] ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] cc_cmd = cc + ' ' + cflags compiler.set_executables( @@ -191,7 +193,8 @@ def customize_compiler(compiler): compiler_so=cc_cmd + ' ' + ccshared, compiler_cxx=cxx, linker_so=ldshared, - linker_exe=cc) + linker_exe=cc, + archiver=ar) compiler.shared_lib_extension = so_ext diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 490410e1..cb802c62 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -8,6 +8,13 @@ from test.support import TESTFN class SysconfigTestCase(unittest.TestCase): + def setUp(self): + self.old_AR = os.environ.get('AR') + + def tearDown(self): + if self.old_AR is not None: + os.environ['AR'] = self.old_AR + def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() self.assert_(os.path.isfile(config_h), config_h) @@ -32,6 +39,21 @@ class SysconfigTestCase(unittest.TestCase): self.assert_(isinstance(cvars, dict)) self.assert_(cvars) + def test_customize_compiler(self): + + os.environ['AR'] = 'xxx' + + # make sure AR gets caught + class compiler: + compiler_type = 'unix' + + def set_executables(self, **kw): + self.exes = kw + + comp = compiler() + sysconfig.customize_compiler(comp) + self.assertEquals(comp.exes['archiver'], 'xxx') + def test_suite(): suite = unittest.TestSuite() -- cgit v1.2.1 From bd73f62c8e0bd56c41e540072a6ed1a0fff7cbd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 08:20:15 +0000 Subject: Fixed #3987 : removed unused import --- core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/core.py b/core.py index 0e85ca6f..0fb6f81e 100644 --- a/core.py +++ b/core.py @@ -9,7 +9,6 @@ really defined in distutils.dist and distutils.cmd. __revision__ = "$Id$" import sys, os -from types import * from distutils.debug import DEBUG from distutils.errors import * -- cgit v1.2.1 From 7f0b8ac04611f305e08185605d5be2825a7ee897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 08:55:23 +0000 Subject: removed types usage and added test coverage (work for #3986) --- cmd.py | 22 +++++++++++++--------- tests/test_cmd.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 tests/test_cmd.py diff --git a/cmd.py b/cmd.py index 267cf180..2d6cfb18 100644 --- a/cmd.py +++ b/cmd.py @@ -7,8 +7,7 @@ in the distutils.command package. __revision__ = "$Id$" import sys, os, string, re -from types import * -from distutils.errors import * +from distutils.errors import DistutilsOptionError from distutils import util, dir_util, file_util, archive_util, dep_util from distutils import log @@ -220,7 +219,7 @@ class Command: if val is None: setattr(self, option, default) return default - elif type(val) is not StringType: + elif not isinstance(val, str): raise DistutilsOptionError, \ "'%s' must be a %s (got `%s`)" % (option, what, val) return val @@ -240,19 +239,24 @@ class Command: val = getattr(self, option) if val is None: return - elif type(val) is StringType: + elif isinstance(val, str): setattr(self, option, re.split(r',\s*|\s+', val)) else: - if type(val) is ListType: - types = map(type, val) - ok = (types == [StringType] * len(val)) + if isinstance(val, list): + # checks if all elements are str + ok = 1 + for element in val: + if not isinstance(element, str): + ok = 0 + break else: ok = 0 if not ok: raise DistutilsOptionError, \ - "'%s' must be a list of strings (got %r)" % \ - (option, val) + "'%s' must be a list of strings (got %r)" % \ + (option, val) + def _ensure_tested_string (self, option, tester, what, error_fmt, default=None): diff --git a/tests/test_cmd.py b/tests/test_cmd.py new file mode 100644 index 00000000..19079c03 --- /dev/null +++ b/tests/test_cmd.py @@ -0,0 +1,38 @@ +"""Tests for distutils.cmd.""" +import unittest + +from distutils.cmd import Command +from distutils.dist import Distribution +from distutils.errors import DistutilsOptionError + +class CommandTestCase(unittest.TestCase): + + def test_ensure_string_list(self): + + class MyCmd(Command): + + def initialize_options(self): + pass + + dist = Distribution() + cmd = MyCmd(dist) + + cmd.not_string_list = ['one', 2, 'three'] + cmd.yes_string_list = ['one', 'two', 'three'] + cmd.not_string_list2 = object() + cmd.yes_string_list2 = 'ok' + + cmd.ensure_string_list('yes_string_list') + cmd.ensure_string_list('yes_string_list2') + + self.assertRaises(DistutilsOptionError, + cmd.ensure_string_list, 'not_string_list') + + self.assertRaises(DistutilsOptionError, + cmd.ensure_string_list, 'not_string_list2') + +def test_suite(): + return unittest.makeSuite(CommandTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 296683d31be7741dbd12ab7ffcc2b8575564b540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 09:03:10 +0000 Subject: Merged revisions 69360 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69360 | tarek.ziade | 2009-02-06 09:55:23 +0100 (Fri, 06 Feb 2009) | 1 line removed types usage and added test coverage (work for #3986) ........ --- cmd.py | 2 +- tests/test_cmd.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/test_cmd.py diff --git a/cmd.py b/cmd.py index c6572caa..4669dc2d 100644 --- a/cmd.py +++ b/cmd.py @@ -7,7 +7,7 @@ in the distutils.command package. __revision__ = "$Id$" import sys, os, re -from distutils.errors import * +from distutils.errors import DistutilsOptionError from distutils import util, dir_util, file_util, archive_util, dep_util from distutils import log diff --git a/tests/test_cmd.py b/tests/test_cmd.py new file mode 100644 index 00000000..19079c03 --- /dev/null +++ b/tests/test_cmd.py @@ -0,0 +1,38 @@ +"""Tests for distutils.cmd.""" +import unittest + +from distutils.cmd import Command +from distutils.dist import Distribution +from distutils.errors import DistutilsOptionError + +class CommandTestCase(unittest.TestCase): + + def test_ensure_string_list(self): + + class MyCmd(Command): + + def initialize_options(self): + pass + + dist = Distribution() + cmd = MyCmd(dist) + + cmd.not_string_list = ['one', 2, 'three'] + cmd.yes_string_list = ['one', 'two', 'three'] + cmd.not_string_list2 = object() + cmd.yes_string_list2 = 'ok' + + cmd.ensure_string_list('yes_string_list') + cmd.ensure_string_list('yes_string_list2') + + self.assertRaises(DistutilsOptionError, + cmd.ensure_string_list, 'not_string_list') + + self.assertRaises(DistutilsOptionError, + cmd.ensure_string_list, 'not_string_list2') + +def test_suite(): + return unittest.makeSuite(CommandTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 1db73ee62c96d75e13a8740fe35642ea7a8ac9a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 13:27:38 +0000 Subject: Fixed #5167: test_customize_compiler does not apply under non unix compilers --- tests/test_sysconfig.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 7f0bce63..4636c0c1 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,6 +1,8 @@ """Tests for distutils.dist.""" from distutils import sysconfig +from distutils.ccompiler import get_default_compiler + import os import unittest @@ -41,6 +43,10 @@ class SysconfigTestCase(unittest.TestCase): def test_customize_compiler(self): + # not testing if default compiler is not unix + if get_default_compiler() != 'unix': + return + os.environ['AR'] = 'xxx' # make sure AR gets caught -- cgit v1.2.1 From 04e222bc312426aa74df95a63623691e07324ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 6 Feb 2009 13:33:47 +0000 Subject: Merged revisions 69366 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69366 | tarek.ziade | 2009-02-06 14:27:38 +0100 (Fri, 06 Feb 2009) | 1 line Fixed #5167: test_customize_compiler does not apply under non unix compilers ........ --- tests/test_sysconfig.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index cb802c62..af6f1830 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,6 +1,8 @@ """Tests for distutils.dist.""" from distutils import sysconfig +from distutils.ccompiler import get_default_compiler + import os import unittest @@ -41,6 +43,10 @@ class SysconfigTestCase(unittest.TestCase): def test_customize_compiler(self): + # not testing if default compiler is not unix + if get_default_compiler() != 'unix': + return + os.environ['AR'] = 'xxx' # make sure AR gets caught -- cgit v1.2.1 From ecaef925f14658bfbfc9d7ad6df97ec35a7b0591 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Fri, 6 Feb 2009 21:33:45 +0000 Subject: Convert "srcdir" into an absolute path if that seems prudent. Currrently the only user of this is Lib/distutils/tests/test_build_ext.py (in order to find the source for xxmodule.c). I'm not sure if other platforms need similar tweaks, I'm not brave enough to attempt it without being able to test. --- sysconfig.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index deb51a1c..4e06546a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -530,6 +530,20 @@ def get_config_vars(*args): if 'srcdir' not in _config_vars: _config_vars['srcdir'] = project_base + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if python_build and os.name == "posix": + base = os.path.dirname(os.path.abspath(sys.executable)) + if (not os.path.isabs(_config_vars['srcdir']) and + base != os.getcwd()): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _config_vars['srcdir']) + _config_vars['srcdir'] = os.path.normpath(srcdir) + if sys.platform == 'darwin': kernel_version = os.uname()[2] # Kernel version (8.4.3) major_version = int(kernel_version.split('.')[0]) -- cgit v1.2.1 From a719c0dc5cddc1cab0881c42889ad3df0b0dfcbd Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Fri, 6 Feb 2009 21:42:05 +0000 Subject: Make test_build_ext.py use sysconfig "srcdir" to find the source for xxmodule.c. Have sysconfig make the srcdir path absolute if that seems necessary (running non-installed Python outside the build directory). --- sysconfig.py | 14 ++++++++++++++ tests/test_build_ext.py | 14 +++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 386ae89b..a3ef3f6d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -504,6 +504,20 @@ def get_config_vars(*args): _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if python_build and os.name == "posix": + base = os.path.dirname(os.path.abspath(sys.executable)) + if (not os.path.isabs(_config_vars['srcdir']) and + base != os.getcwd()): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _config_vars['srcdir']) + _config_vars['srcdir'] = os.path.normpath(srcdir) + if sys.platform == 'darwin': kernel_version = os.uname()[2] # Kernel version (8.4.3) major_version = int(kernel_version.split('.')[0]) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 4c572325..5e42943c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -15,6 +15,10 @@ from test import support # Don't load the xx module more than once. ALREADY_TESTED = False +def _get_source_filename(): + srcdir = sysconfig.get_config_var('srcdir') + return os.path.join(srcdir, 'Modules', 'xxmodule.c') + class BuildExtTestCase(unittest.TestCase): def setUp(self): # Create a simple test environment @@ -22,9 +26,7 @@ class BuildExtTestCase(unittest.TestCase): self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) - - xx_c = os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') - shutil.copy(xx_c, self.tmp_dir) + shutil.copy(_get_source_filename(), self.tmp_dir) def test_build_ext(self): global ALREADY_TESTED @@ -97,9 +99,11 @@ class BuildExtTestCase(unittest.TestCase): self.assert_(len(cmd.library_dirs) > 0) def test_suite(): - if not sysconfig.python_build: + src = _get_source_filename() + if not os.path.exists(src): if support.verbose: - print('test_build_ext: The test must be run in a python build dir') + print('test_build_ext: Cannot find source code (test' + ' must run in python build dir)') return unittest.TestSuite() else: return unittest.makeSuite(BuildExtTestCase) -- cgit v1.2.1 From d3802bd0332d6e460e0db6a66a230c261529bf09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 7 Feb 2009 00:05:39 +0000 Subject: #3986 replacing string and types call (like in the Py3k branch), and put exec_msg call at the right place --- cmd.py | 30 +++++++++++++++--------------- tests/test_cmd.py | 49 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/cmd.py b/cmd.py index 2d6cfb18..1351f445 100644 --- a/cmd.py +++ b/cmd.py @@ -6,7 +6,7 @@ in the distutils.command package. __revision__ = "$Id$" -import sys, os, string, re +import sys, os, re from distutils.errors import DistutilsOptionError from distutils import util, dir_util, file_util, archive_util, dep_util from distutils import log @@ -156,19 +156,19 @@ class Command: "abstract method -- subclass %s must override" % self.__class__ - def dump_options (self, header=None, indent=""): + def dump_options(self, header=None, indent=""): from distutils.fancy_getopt import longopt_xlate if header is None: header = "command options for '%s':" % self.get_command_name() - print indent + header + self.announce(indent + header, level=log.INFO) indent = indent + " " for (option, _, _) in self.user_options: - option = string.translate(option, longopt_xlate) + option = option.translate(longopt_xlate) if option[-1] == "=": option = option[:-1] value = getattr(self, option) - print indent + "%s = %s" % (option, value) - + self.announce(indent + "%s = %s" % (option, value), + level=log.INFO) def run (self): """A command's raison d'etre: carry out the action it exists to @@ -405,8 +405,8 @@ class Command: base_name, format, root_dir, base_dir, dry_run=self.dry_run) - def make_file (self, infiles, outfile, func, args, - exec_msg=None, skip_msg=None, level=1): + def make_file(self, infiles, outfile, func, args, + exec_msg=None, skip_msg=None, level=1): """Special case of 'execute()' for operations that process one or more input files and generate one output file. Works just like 'execute()', except the operation is skipped and a different @@ -415,24 +415,24 @@ class Command: and it is true, then the command is unconditionally run -- does no timestamp checks. """ - if exec_msg is None: - exec_msg = "generating %s from %s" % \ - (outfile, string.join(infiles, ', ')) if skip_msg is None: skip_msg = "skipping %s (inputs unchanged)" % outfile - # Allow 'infiles' to be a single string - if type(infiles) is StringType: + if isinstance(infiles, str): infiles = (infiles,) - elif type(infiles) not in (ListType, TupleType): + elif not isinstance(infiles, (list, tuple)): raise TypeError, \ "'infiles' must be a string, or a list or tuple of strings" + if exec_msg is None: + exec_msg = "generating %s from %s" % \ + (outfile, ', '.join(infiles)) + # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if self.force or dep_util.newer_group (infiles, outfile): + if self.force or dep_util.newer_group(infiles, outfile): self.execute(func, args, exec_msg, level) # Otherwise, print the "skip" message diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 19079c03..a252c355 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -5,23 +5,23 @@ from distutils.cmd import Command from distutils.dist import Distribution from distutils.errors import DistutilsOptionError -class CommandTestCase(unittest.TestCase): - - def test_ensure_string_list(self): - - class MyCmd(Command): +class MyCmd(Command): + def initialize_options(self): + pass - def initialize_options(self): - pass +class CommandTestCase(unittest.TestCase): + def setUp(self): dist = Distribution() - cmd = MyCmd(dist) + self.cmd = MyCmd(dist) + + def test_ensure_string_list(self): + cmd = self.cmd cmd.not_string_list = ['one', 2, 'three'] cmd.yes_string_list = ['one', 'two', 'three'] cmd.not_string_list2 = object() cmd.yes_string_list2 = 'ok' - cmd.ensure_string_list('yes_string_list') cmd.ensure_string_list('yes_string_list2') @@ -31,6 +31,37 @@ class CommandTestCase(unittest.TestCase): self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, 'not_string_list2') + def test_make_file(self): + + cmd = self.cmd + + # making sure it raises when infiles is not a string or a list/tuple + self.assertRaises(TypeError, cmd.make_file, + infiles=1, outfile='', func='func', args=()) + + # making sure execute gets called properly + def _execute(func, args, exec_msg, level): + self.assertEquals(exec_msg, 'generating out from in') + cmd.force = True + cmd.execute = _execute + cmd.make_file(infiles='in', outfile='out', func='func', args=()) + + def test_dump_options(self): + + msgs = [] + def _announce(msg, level): + msgs.append(msg) + cmd = self.cmd + cmd.announce = _announce + cmd.option1 = 1 + cmd.option2 = 1 + cmd.user_options = [('option1', '', ''), ('option2', '', '')] + cmd.dump_options() + + wanted = ["command options for 'MyCmd':", ' option1 = 1', + ' option2 = 1'] + self.assertEquals(msgs, wanted) + def test_suite(): return unittest.makeSuite(CommandTestCase) -- cgit v1.2.1 From 27713a8ca68d6cd642e8fbf239d763fbf9571a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 7 Feb 2009 00:10:48 +0000 Subject: Merged revisions 69385 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69385 | tarek.ziade | 2009-02-07 01:05:39 +0100 (Sat, 07 Feb 2009) | 1 line #3986 replacing string and types call (like in the Py3k branch), and put exec_msg call at the right place ........ --- cmd.py | 14 +++++++------- tests/test_cmd.py | 49 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/cmd.py b/cmd.py index 4669dc2d..295c9140 100644 --- a/cmd.py +++ b/cmd.py @@ -155,15 +155,15 @@ class Command: from distutils.fancy_getopt import longopt_xlate if header is None: header = "command options for '%s':" % self.get_command_name() - print(indent + header) + self.announce(indent + header, level=log.INFO) indent = indent + " " for (option, _, _) in self.user_options: option = longopt_xlate(option) if option[-1] == "=": option = option[:-1] value = getattr(self, option) - print(indent + "%s = %s" % (option, value)) - + self.announce(indent + "%s = %s" % (option, value), + level=log.INFO) def run(self): """A command's raison d'etre: carry out the action it exists to @@ -383,12 +383,9 @@ class Command: and it is true, then the command is unconditionally run -- does no timestamp checks. """ - if exec_msg is None: - exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles)) if skip_msg is None: skip_msg = "skipping %s (inputs unchanged)" % outfile - # Allow 'infiles' to be a single string if isinstance(infiles, str): infiles = (infiles,) @@ -396,10 +393,13 @@ class Command: raise TypeError( "'infiles' must be a string, or a list or tuple of strings") + if exec_msg is None: + exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles)) + # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if self.force or dep_util.newer_group (infiles, outfile): + if self.force or dep_util.newer_group(infiles, outfile): self.execute(func, args, exec_msg, level) # Otherwise, print the "skip" message else: diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 19079c03..a252c355 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -5,23 +5,23 @@ from distutils.cmd import Command from distutils.dist import Distribution from distutils.errors import DistutilsOptionError -class CommandTestCase(unittest.TestCase): - - def test_ensure_string_list(self): - - class MyCmd(Command): +class MyCmd(Command): + def initialize_options(self): + pass - def initialize_options(self): - pass +class CommandTestCase(unittest.TestCase): + def setUp(self): dist = Distribution() - cmd = MyCmd(dist) + self.cmd = MyCmd(dist) + + def test_ensure_string_list(self): + cmd = self.cmd cmd.not_string_list = ['one', 2, 'three'] cmd.yes_string_list = ['one', 'two', 'three'] cmd.not_string_list2 = object() cmd.yes_string_list2 = 'ok' - cmd.ensure_string_list('yes_string_list') cmd.ensure_string_list('yes_string_list2') @@ -31,6 +31,37 @@ class CommandTestCase(unittest.TestCase): self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, 'not_string_list2') + def test_make_file(self): + + cmd = self.cmd + + # making sure it raises when infiles is not a string or a list/tuple + self.assertRaises(TypeError, cmd.make_file, + infiles=1, outfile='', func='func', args=()) + + # making sure execute gets called properly + def _execute(func, args, exec_msg, level): + self.assertEquals(exec_msg, 'generating out from in') + cmd.force = True + cmd.execute = _execute + cmd.make_file(infiles='in', outfile='out', func='func', args=()) + + def test_dump_options(self): + + msgs = [] + def _announce(msg, level): + msgs.append(msg) + cmd = self.cmd + cmd.announce = _announce + cmd.option1 = 1 + cmd.option2 = 1 + cmd.user_options = [('option1', '', ''), ('option2', '', '')] + cmd.dump_options() + + wanted = ["command options for 'MyCmd':", ' option1 = 1', + ' option2 = 1'] + self.assertEquals(msgs, wanted) + def test_suite(): return unittest.makeSuite(CommandTestCase) -- cgit v1.2.1 From 9ab19ddedbd4daab9af83c485e616857950ab225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 10 Feb 2009 12:31:09 +0000 Subject: Fixed #3386: the optional prefix argument was ignored under OS2 and NT in distutils.sysconfig.get_python_lib --- sysconfig.py | 6 +++--- tests/test_sysconfig.py | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 4e06546a..56cb861a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -132,7 +132,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): if get_python_version() < "2.2": return prefix else: - return os.path.join(PREFIX, "Lib", "site-packages") + return os.path.join(prefix, "Lib", "site-packages") elif os.name == "mac": if plat_specific: @@ -148,9 +148,9 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): elif os.name == "os2": if standard_lib: - return os.path.join(PREFIX, "Lib") + return os.path.join(prefix, "Lib") else: - return os.path.join(PREFIX, "Lib", "site-packages") + return os.path.join(prefix, "Lib", "site-packages") else: raise DistutilsPlatformError( diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 4636c0c1..dd17eb34 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -26,6 +26,8 @@ class SysconfigTestCase(unittest.TestCase): # XXX doesn't work on Linux when Python was never installed before #self.assert_(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? + self.assertNotEqual(sysconfig.get_python_lib(), + sysconfig.get_python_lib(prefix=TESTFN)) def test_get_python_inc(self): inc_dir = sysconfig.get_python_inc() -- cgit v1.2.1 From e21451406ffc2771198edca9bce2e5d5f570847d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 10 Feb 2009 12:33:42 +0000 Subject: Merged revisions 69485 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69485 | tarek.ziade | 2009-02-10 13:31:09 +0100 (Tue, 10 Feb 2009) | 1 line Fixed #3386: the optional prefix argument was ignored under OS2 and NT in distutils.sysconfig.get_python_lib ........ --- sysconfig.py | 6 +++--- tests/test_sysconfig.py | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 9993fba1..615da07e 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -129,7 +129,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): if get_python_version() < "2.2": return prefix else: - return os.path.join(PREFIX, "Lib", "site-packages") + return os.path.join(prefix, "Lib", "site-packages") elif os.name == "mac": if plat_specific: @@ -145,9 +145,9 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): elif os.name == "os2": if standard_lib: - return os.path.join(PREFIX, "Lib") + return os.path.join(prefix, "Lib") else: - return os.path.join(PREFIX, "Lib", "site-packages") + return os.path.join(prefix, "Lib", "site-packages") else: raise DistutilsPlatformError( diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index aa1187e7..9f820575 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -17,6 +17,8 @@ class SysconfigTestCase(unittest.TestCase): # XXX doesn't work on Linux when Python was never installed before #self.assert_(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? + self.assertNotEqual(sysconfig.get_python_lib(), + sysconfig.get_python_lib(prefix=TESTFN)) def test_get_python_inc(self): # The check for srcdir is copied from Python's setup.py, -- cgit v1.2.1 From a80b2645dfdc8e78856c0328942a46828e0d7de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 10 Feb 2009 12:36:33 +0000 Subject: Merged revisions 69485 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69485 | tarek.ziade | 2009-02-10 13:31:09 +0100 (Tue, 10 Feb 2009) | 1 line Fixed #3386: the optional prefix argument was ignored under OS2 and NT in distutils.sysconfig.get_python_lib ........ --- sysconfig.py | 6 +++--- tests/test_sysconfig.py | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index a3ef3f6d..fbeb45f8 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -130,7 +130,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): if get_python_version() < "2.2": return prefix else: - return os.path.join(PREFIX, "Lib", "site-packages") + return os.path.join(prefix, "Lib", "site-packages") elif os.name == "mac": if plat_specific: if standard_lib: @@ -144,9 +144,9 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): return os.path.join(prefix, "Lib", "site-packages") elif os.name == "os2": if standard_lib: - return os.path.join(PREFIX, "Lib") + return os.path.join(prefix, "Lib") else: - return os.path.join(PREFIX, "Lib", "site-packages") + return os.path.join(prefix, "Lib", "site-packages") else: raise DistutilsPlatformError( "I don't know where Python installs its library " diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index af6f1830..1e9dbd54 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -26,6 +26,8 @@ class SysconfigTestCase(unittest.TestCase): # XXX doesn't work on Linux when Python was never installed before #self.assert_(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? + self.assertNotEqual(sysconfig.get_python_lib(), + sysconfig.get_python_lib(prefix=TESTFN)) def test_get_python_inc(self): inc_dir = sysconfig.get_python_inc() -- cgit v1.2.1 From 99ce57fa1abba2aa6aa7dc3f49d08ff11338e624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 12 Feb 2009 20:56:21 +0000 Subject: fixing the leak introduced in r69304 --- tests/test_build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 780660da..1891e682 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -1,6 +1,5 @@ import sys import os -import tempfile import shutil from StringIO import StringIO @@ -19,7 +18,8 @@ class BuildExtTestCase(unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path - self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") + self.tmp_dir = os.path.join(os.path.dirname(__file__), 'xx') + os.mkdir(self.tmp_dir) self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) -- cgit v1.2.1 From 87c31b7e90c7799d4215ba2e59c503419e4052ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 12 Feb 2009 21:00:18 +0000 Subject: Merged revisions 69551 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69551 | tarek.ziade | 2009-02-12 21:56:21 +0100 (Thu, 12 Feb 2009) | 1 line fixing the leak introduced in r69304 ........ --- tests/test_build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index d0cce9ba..00630ab9 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -1,6 +1,5 @@ import sys import os -import tempfile import shutil from StringIO import StringIO @@ -15,7 +14,8 @@ class BuildExtTestCase(unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path - self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") + self.tmp_dir = os.path.join(os.path.dirname(__file__), 'xx') + os.mkdir(self.tmp_dir) self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) -- cgit v1.2.1 From f2c97b50ebcf21d37019bf8c1121d2e5e05597b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 12 Feb 2009 21:02:07 +0000 Subject: Merged revisions 69551 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69551 | tarek.ziade | 2009-02-12 21:56:21 +0100 (Thu, 12 Feb 2009) | 1 line fixing the leak introduced in r69304 ........ --- tests/test_build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 5e42943c..aa5378c4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -1,6 +1,5 @@ import sys import os -import tempfile import shutil from io import StringIO @@ -23,7 +22,8 @@ class BuildExtTestCase(unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path - self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") + self.tmp_dir = os.path.join(os.path.dirname(__file__), 'xx') + os.mkdir(self.tmp_dir) self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) -- cgit v1.2.1 From 456ea0886e7494aec1cf4f9ef765c2be6f8a5151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 13 Feb 2009 16:13:16 +0000 Subject: reverted leak fix, to use the one done in py3k branch (r67382) --- tests/test_build_ext.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 1891e682..3c7d228d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -1,5 +1,6 @@ import sys import os +import tempfile import shutil from StringIO import StringIO @@ -10,6 +11,10 @@ from distutils import sysconfig import unittest from test import test_support +# http://bugs.python.org/issue4373 +# Don't load the xx module more than once. +ALREADY_TESTED = False + def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') return os.path.join(srcdir, 'Modules', 'xxmodule.c') @@ -18,13 +23,13 @@ class BuildExtTestCase(unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path - self.tmp_dir = os.path.join(os.path.dirname(__file__), 'xx') - os.mkdir(self.tmp_dir) + self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) def test_build_ext(self): + global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) @@ -47,6 +52,11 @@ class BuildExtTestCase(unittest.TestCase): finally: sys.stdout = old_stdout + if ALREADY_TESTED: + return + else: + ALREADY_TESTED = True + import xx for attr in ('error', 'foo', 'new', 'roj'): -- cgit v1.2.1 From afc614eb0d2c7cb40779d00c168eb420b43e088a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 13 Feb 2009 16:20:24 +0000 Subject: Merged revisions 69585 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69585 | tarek.ziade | 2009-02-13 17:13:16 +0100 (Fri, 13 Feb 2009) | 1 line reverted leak fix, to use the one done in py3k branch (r67382) ........ --- tests/test_build_ext.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 00630ab9..dbec65e8 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -1,5 +1,6 @@ import sys import os +import tempfile import shutil from StringIO import StringIO @@ -10,12 +11,15 @@ from distutils import sysconfig import unittest from test import test_support +# http://bugs.python.org/issue4373 +# Don't load the xx module more than once. +ALREADY_TESTED = False + class BuildExtTestCase(unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path - self.tmp_dir = os.path.join(os.path.dirname(__file__), 'xx') - os.mkdir(self.tmp_dir) + self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) @@ -23,6 +27,7 @@ class BuildExtTestCase(unittest.TestCase): shutil.copy(xx_c, self.tmp_dir) def test_build_ext(self): + global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) @@ -45,6 +50,11 @@ class BuildExtTestCase(unittest.TestCase): finally: sys.stdout = old_stdout + if ALREADY_TESTED: + return + else: + ALREADY_TESTED = True + import xx for attr in ('error', 'foo', 'new', 'roj'): -- cgit v1.2.1 From 0f3bc82cb894b6fb67b3006b6b749a9171710062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 13 Feb 2009 16:23:57 +0000 Subject: Merged revisions 69585 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69585 | tarek.ziade | 2009-02-13 17:13:16 +0100 (Fri, 13 Feb 2009) | 1 line reverted leak fix, to use the one done in py3k branch (r67382) ........ --- tests/test_build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index aa5378c4..5e42943c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -1,5 +1,6 @@ import sys import os +import tempfile import shutil from io import StringIO @@ -22,8 +23,7 @@ class BuildExtTestCase(unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path - self.tmp_dir = os.path.join(os.path.dirname(__file__), 'xx') - os.mkdir(self.tmp_dir) + self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) -- cgit v1.2.1 From fcbbe9c25a4ae1020ea705adbbb2b13269b8c6b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 13 Feb 2009 22:22:03 +0000 Subject: Issue #2461: added tests for distutils.util --- tests/test_util.py | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 tests/test_util.py diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 00000000..3505fe26 --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,213 @@ +"""Tests for distutils.util.""" +# not covered yet: +# - byte_compile +# +import os +import sys +import unittest + +from distutils.errors import DistutilsPlatformError + +from distutils.util import get_platform +from distutils.util import convert_path +from distutils.util import change_root +from distutils.util import check_environ +from distutils.util import split_quoted +from distutils.util import strtobool +from distutils.util import rfc822_escape + +from distutils import util # used to patch _environ_checked + +class utilTestCase(unittest.TestCase): + + def setUp(self): + # saving the environment + self.name = os.name + self.platform = sys.platform + self.version = sys.version + self.sep = os.sep + self.environ = os.environ + self.join = os.path.join + self.isabs = os.path.isabs + self.splitdrive = os.path.splitdrive + + # patching os.uname + if hasattr(os, 'uname'): + self.uname = os.uname + self._uname = os.uname() + os.uname = self._get_uname + else: + self.uname = None + self._uname = None + + def tearDown(self): + # getting back tne environment + os.name = self.name + sys.platform = self.platform + sys.version = self.version + os.sep = self.sep + os.environ = self.environ + os.path.join = self.join + os.path.isabs = self.isabs + os.path.splitdrive = self.splitdrive + if self.uname is not None: + os.uname = self.uname + + def _set_uname(self, uname): + self._uname = uname + + def _get_uname(self): + return self._uname + + def test_get_platform(self): + + # windows XP, 32bits + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Intel)]') + sys.platform = 'win32' + self.assertEquals(get_platform(), 'win32') + + # windows XP, amd64 + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Amd64)]') + sys.platform = 'win32' + self.assertEquals(get_platform(), 'win-amd64') + + # windows XP, itanium + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Itanium)]') + sys.platform = 'win32' + self.assertEquals(get_platform(), 'win-ia64') + + # macbook + os.name = 'posix' + sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' + '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') + sys.platform = 'darwin' + self._set_uname(('Darwin', 'macziade', '8.11.1', + ('Darwin Kernel Version 8.11.1: ' + 'Wed Oct 10 18:23:28 PDT 2007; ' + 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) + + self.assertEquals(get_platform(), 'macosx-10.3-i386') + + # linux debian sarge + os.name = 'posix' + sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' + '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]') + sys.platform = 'linux2' + self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', + '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) + + self.assertEquals(get_platform(), 'linux-i686') + + # XXX more platforms to tests here + + def test_convert_path(self): + # linux/mac + os.sep = '/' + def _join(path): + return '/'.join(path) + os.path.join = _join + + self.assertEquals(convert_path('/home/to/my/stuff'), + '/home/to/my/stuff') + + # win + os.sep = '\\' + def _join(*path): + return '\\'.join(path) + os.path.join = _join + + self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') + self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') + + self.assertEquals(convert_path('home/to/my/stuff'), + 'home\\to\\my\\stuff') + self.assertEquals(convert_path('.'), + os.curdir) + + def test_change_root(self): + # linux/mac + os.name = 'posix' + def _isabs(path): + return path[0] == '/' + os.path.isabs = _isabs + + self.assertEquals(change_root('/root', '/old/its/here'), + '/root/old/its/here') + self.assertEquals(change_root('/root', 'its/here'), + '/root/its/here') + + # windows + os.name = 'nt' + def _isabs(path): + return path.startswith('c:\\') + os.path.isabs = _isabs + def _splitdrive(path): + if path.startswith('c:'): + return ('', path.replace('c:', '')) + return ('', path) + os.path.splitdrive = _splitdrive + def _join(*path): + return '\\'.join(path) + os.path.join = _join + + self.assertEquals(change_root('c:\\root', 'c:\\old\\its\\here'), + 'c:\\root\\old\\its\\here') + self.assertEquals(change_root('c:\\root', 'its\\here'), + 'c:\\root\\its\\here') + + # BugsBunny os (it's a great os) + os.name = 'BugsBunny' + self.assertRaises(DistutilsPlatformError, + change_root, 'c:\\root', 'its\\here') + + # XXX platforms to be covered: os2, mac + + def test_check_environ(self): + util._environ_checked = 0 + + # posix without HOME + if os.name == 'posix': # this test won't run on windows + os.environ = {} + check_environ() + + import pwd + self.assertEquals(os.environ['HOME'], + pwd.getpwuid(os.getuid())[5]) + else: + check_environ() + + self.assertEquals(os.environ['PLAT'], get_platform()) + self.assertEquals(util._environ_checked, 1) + + def test_split_quoted(self): + self.assertEquals(split_quoted('""one"" "two" \'three\' \\four'), + ['one', 'two', 'three', 'four']) + + def test_strtobool(self): + yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') + no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') + + for y in yes: + self.assert_(strtobool(y)) + + for n in no: + self.assert_(not strtobool(n)) + + def test_rfc822_escape(self): + header = 'I am a\npoor\nlonesome\nheader\n' + res = rfc822_escape(header) + wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' + 'header%(8s)s') % {'8s': '\n'+8*' '} + self.assertEquals(res, wanted) + +def test_suite(): + return unittest.makeSuite(utilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 0bfae040eb480b3ac952d9ce384311f62af50825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 13 Feb 2009 22:26:15 +0000 Subject: Merged revisions 69594 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69594 | tarek.ziade | 2009-02-13 23:22:03 +0100 (Fri, 13 Feb 2009) | 1 line Issue #2461: added tests for distutils.util ........ --- tests/test_util.py | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 tests/test_util.py diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 00000000..3505fe26 --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,213 @@ +"""Tests for distutils.util.""" +# not covered yet: +# - byte_compile +# +import os +import sys +import unittest + +from distutils.errors import DistutilsPlatformError + +from distutils.util import get_platform +from distutils.util import convert_path +from distutils.util import change_root +from distutils.util import check_environ +from distutils.util import split_quoted +from distutils.util import strtobool +from distutils.util import rfc822_escape + +from distutils import util # used to patch _environ_checked + +class utilTestCase(unittest.TestCase): + + def setUp(self): + # saving the environment + self.name = os.name + self.platform = sys.platform + self.version = sys.version + self.sep = os.sep + self.environ = os.environ + self.join = os.path.join + self.isabs = os.path.isabs + self.splitdrive = os.path.splitdrive + + # patching os.uname + if hasattr(os, 'uname'): + self.uname = os.uname + self._uname = os.uname() + os.uname = self._get_uname + else: + self.uname = None + self._uname = None + + def tearDown(self): + # getting back tne environment + os.name = self.name + sys.platform = self.platform + sys.version = self.version + os.sep = self.sep + os.environ = self.environ + os.path.join = self.join + os.path.isabs = self.isabs + os.path.splitdrive = self.splitdrive + if self.uname is not None: + os.uname = self.uname + + def _set_uname(self, uname): + self._uname = uname + + def _get_uname(self): + return self._uname + + def test_get_platform(self): + + # windows XP, 32bits + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Intel)]') + sys.platform = 'win32' + self.assertEquals(get_platform(), 'win32') + + # windows XP, amd64 + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Amd64)]') + sys.platform = 'win32' + self.assertEquals(get_platform(), 'win-amd64') + + # windows XP, itanium + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Itanium)]') + sys.platform = 'win32' + self.assertEquals(get_platform(), 'win-ia64') + + # macbook + os.name = 'posix' + sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' + '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') + sys.platform = 'darwin' + self._set_uname(('Darwin', 'macziade', '8.11.1', + ('Darwin Kernel Version 8.11.1: ' + 'Wed Oct 10 18:23:28 PDT 2007; ' + 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) + + self.assertEquals(get_platform(), 'macosx-10.3-i386') + + # linux debian sarge + os.name = 'posix' + sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' + '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]') + sys.platform = 'linux2' + self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', + '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) + + self.assertEquals(get_platform(), 'linux-i686') + + # XXX more platforms to tests here + + def test_convert_path(self): + # linux/mac + os.sep = '/' + def _join(path): + return '/'.join(path) + os.path.join = _join + + self.assertEquals(convert_path('/home/to/my/stuff'), + '/home/to/my/stuff') + + # win + os.sep = '\\' + def _join(*path): + return '\\'.join(path) + os.path.join = _join + + self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') + self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') + + self.assertEquals(convert_path('home/to/my/stuff'), + 'home\\to\\my\\stuff') + self.assertEquals(convert_path('.'), + os.curdir) + + def test_change_root(self): + # linux/mac + os.name = 'posix' + def _isabs(path): + return path[0] == '/' + os.path.isabs = _isabs + + self.assertEquals(change_root('/root', '/old/its/here'), + '/root/old/its/here') + self.assertEquals(change_root('/root', 'its/here'), + '/root/its/here') + + # windows + os.name = 'nt' + def _isabs(path): + return path.startswith('c:\\') + os.path.isabs = _isabs + def _splitdrive(path): + if path.startswith('c:'): + return ('', path.replace('c:', '')) + return ('', path) + os.path.splitdrive = _splitdrive + def _join(*path): + return '\\'.join(path) + os.path.join = _join + + self.assertEquals(change_root('c:\\root', 'c:\\old\\its\\here'), + 'c:\\root\\old\\its\\here') + self.assertEquals(change_root('c:\\root', 'its\\here'), + 'c:\\root\\its\\here') + + # BugsBunny os (it's a great os) + os.name = 'BugsBunny' + self.assertRaises(DistutilsPlatformError, + change_root, 'c:\\root', 'its\\here') + + # XXX platforms to be covered: os2, mac + + def test_check_environ(self): + util._environ_checked = 0 + + # posix without HOME + if os.name == 'posix': # this test won't run on windows + os.environ = {} + check_environ() + + import pwd + self.assertEquals(os.environ['HOME'], + pwd.getpwuid(os.getuid())[5]) + else: + check_environ() + + self.assertEquals(os.environ['PLAT'], get_platform()) + self.assertEquals(util._environ_checked, 1) + + def test_split_quoted(self): + self.assertEquals(split_quoted('""one"" "two" \'three\' \\four'), + ['one', 'two', 'three', 'four']) + + def test_strtobool(self): + yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') + no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') + + for y in yes: + self.assert_(strtobool(y)) + + for n in no: + self.assert_(not strtobool(n)) + + def test_rfc822_escape(self): + header = 'I am a\npoor\nlonesome\nheader\n' + res = rfc822_escape(header) + wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' + 'header%(8s)s') % {'8s': '\n'+8*' '} + self.assertEquals(res, wanted) + +def test_suite(): + return unittest.makeSuite(utilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From c2f4d1be776acb70494ee8444cdf884c1a6cc32e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 13 Feb 2009 23:00:43 +0000 Subject: Fixed #4524: distutils build_script command failed with --with-suffix=3 --- command/build_scripts.py | 4 ++-- tests/test_build_scripts.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 48e06aa5..2ad4c7be 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -102,8 +102,8 @@ class build_scripts (Command): outf.write("#!%s%s\n" % (os.path.join( sysconfig.get_config_var("BINDIR"), - "python" + sysconfig.get_config_var("VERSION") - + sysconfig.get_config_var("EXE")), + "python%s%s" % (sysconfig.get_config_var("VERSION"), + sysconfig.get_config_var("EXE"))), post_interp)) outf.writelines(f.readlines()) outf.close() diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 666ca44c..2acfab82 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,6 +5,7 @@ import unittest from distutils.command.build_scripts import build_scripts from distutils.core import Distribution +from distutils import sysconfig from distutils.tests import support @@ -73,6 +74,33 @@ class BuildScriptsTestCase(support.TempdirManager, f.write(text) f.close() + def test_version_int(self): + source = self.mkdtemp() + target = self.mkdtemp() + expected = self.write_sample_scripts(source) + + + cmd = self.get_build_scripts_cmd(target, + [os.path.join(source, fn) + for fn in expected]) + cmd.finalize_options() + + # http://bugs.python.org/issue4524 + # + # On linux-g++-32 with command line `./configure --enable-ipv6 + # --with-suffix=3`, python is compiled okay but the build scripts + # failed when writing the name of the executable + old = sysconfig._config_vars.get('VERSION') + sysconfig._config_vars['VERSION'] = 4 + try: + cmd.run() + finally: + if old is not None: + sysconfig._config_vars['VERSION'] = old + + built = os.listdir(target) + for name in expected: + self.assert_(name in built) def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) -- cgit v1.2.1 From adfa78507b8da0b97fc02a533eef0d015b9cae0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 13 Feb 2009 23:02:44 +0000 Subject: Merged revisions 69598 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69598 | tarek.ziade | 2009-02-14 00:00:43 +0100 (Sat, 14 Feb 2009) | 1 line Fixed #4524: distutils build_script command failed with --with-suffix=3 ........ --- command/build_scripts.py | 4 ++-- tests/test_build_scripts.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 104be0b3..453330fe 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -104,8 +104,8 @@ class build_scripts (Command): outf.write("#!%s%s\n" % (os.path.join( sysconfig.get_config_var("BINDIR"), - "python" + sysconfig.get_config_var("VERSION") - + sysconfig.get_config_var("EXE")), + "python%s%s" % (sysconfig.get_config_var("VERSION"), + sysconfig.get_config_var("EXE"))), post_interp)) outf.writelines(f.readlines()) outf.close() diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 666ca44c..2acfab82 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,6 +5,7 @@ import unittest from distutils.command.build_scripts import build_scripts from distutils.core import Distribution +from distutils import sysconfig from distutils.tests import support @@ -73,6 +74,33 @@ class BuildScriptsTestCase(support.TempdirManager, f.write(text) f.close() + def test_version_int(self): + source = self.mkdtemp() + target = self.mkdtemp() + expected = self.write_sample_scripts(source) + + + cmd = self.get_build_scripts_cmd(target, + [os.path.join(source, fn) + for fn in expected]) + cmd.finalize_options() + + # http://bugs.python.org/issue4524 + # + # On linux-g++-32 with command line `./configure --enable-ipv6 + # --with-suffix=3`, python is compiled okay but the build scripts + # failed when writing the name of the executable + old = sysconfig._config_vars.get('VERSION') + sysconfig._config_vars['VERSION'] = 4 + try: + cmd.run() + finally: + if old is not None: + sysconfig._config_vars['VERSION'] = old + + built = os.listdir(target) + for name in expected: + self.assert_(name in built) def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) -- cgit v1.2.1 From e63773a4f880dbec08188c94df28cbf90bc70881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 13 Feb 2009 23:04:17 +0000 Subject: Merged revisions 69598 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69598 | tarek.ziade | 2009-02-14 00:00:43 +0100 (Sat, 14 Feb 2009) | 1 line Fixed #4524: distutils build_script command failed with --with-suffix=3 ........ --- command/build_scripts.py | 4 ++-- tests/test_build_scripts.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index dc04a9fc..8b08bfea 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -104,8 +104,8 @@ class build_scripts(Command): outf.write("#!%s%s\n" % (os.path.join( sysconfig.get_config_var("BINDIR"), - "python" + sysconfig.get_config_var("VERSION") - + sysconfig.get_config_var("EXE")), + "python%s%s" % (sysconfig.get_config_var("VERSION"), + sysconfig.get_config_var("EXE"))), post_interp)) outf.writelines(f.readlines()) outf.close() diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 666ca44c..2acfab82 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,6 +5,7 @@ import unittest from distutils.command.build_scripts import build_scripts from distutils.core import Distribution +from distutils import sysconfig from distutils.tests import support @@ -73,6 +74,33 @@ class BuildScriptsTestCase(support.TempdirManager, f.write(text) f.close() + def test_version_int(self): + source = self.mkdtemp() + target = self.mkdtemp() + expected = self.write_sample_scripts(source) + + + cmd = self.get_build_scripts_cmd(target, + [os.path.join(source, fn) + for fn in expected]) + cmd.finalize_options() + + # http://bugs.python.org/issue4524 + # + # On linux-g++-32 with command line `./configure --enable-ipv6 + # --with-suffix=3`, python is compiled okay but the build scripts + # failed when writing the name of the executable + old = sysconfig._config_vars.get('VERSION') + sysconfig._config_vars['VERSION'] = 4 + try: + cmd.run() + finally: + if old is not None: + sysconfig._config_vars['VERSION'] = old + + built = os.listdir(target) + for name in expected: + self.assert_(name in built) def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) -- cgit v1.2.1 From a587bd4900a222cd112f56d03a88d3c8990c401c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 13 Feb 2009 23:41:57 +0000 Subject: fix the environ for distutils test_util --- tests/test_util.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index 3505fe26..ca586268 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -35,11 +35,12 @@ class utilTestCase(unittest.TestCase): if hasattr(os, 'uname'): self.uname = os.uname self._uname = os.uname() - os.uname = self._get_uname else: self.uname = None self._uname = None + os.uname = self._get_uname + def tearDown(self): # getting back tne environment os.name = self.name @@ -91,6 +92,7 @@ class utilTestCase(unittest.TestCase): ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' self.assertEquals(get_platform(), 'macosx-10.3-i386') @@ -136,6 +138,9 @@ class utilTestCase(unittest.TestCase): def _isabs(path): return path[0] == '/' os.path.isabs = _isabs + def _join(*path): + return '/'.join(path) + os.path.join = _join self.assertEquals(change_root('/root', '/old/its/here'), '/root/old/its/here') -- cgit v1.2.1 From e1e318222b20681c360d5743872aedc2b5dc1c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 13 Feb 2009 23:48:11 +0000 Subject: Merged revisions 69602 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69602 | tarek.ziade | 2009-02-14 00:41:57 +0100 (Sat, 14 Feb 2009) | 1 line fix the environ for distutils test_util ........ --- tests/test_util.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index 3505fe26..ca586268 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -35,11 +35,12 @@ class utilTestCase(unittest.TestCase): if hasattr(os, 'uname'): self.uname = os.uname self._uname = os.uname() - os.uname = self._get_uname else: self.uname = None self._uname = None + os.uname = self._get_uname + def tearDown(self): # getting back tne environment os.name = self.name @@ -91,6 +92,7 @@ class utilTestCase(unittest.TestCase): ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' self.assertEquals(get_platform(), 'macosx-10.3-i386') @@ -136,6 +138,9 @@ class utilTestCase(unittest.TestCase): def _isabs(path): return path[0] == '/' os.path.isabs = _isabs + def _join(*path): + return '/'.join(path) + os.path.join = _join self.assertEquals(change_root('/root', '/old/its/here'), '/root/old/its/here') -- cgit v1.2.1 From 7c81709c02b41f3d673fbf9e12c9ee4f6f853e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 14 Feb 2009 14:10:23 +0000 Subject: Fix for #5257: refactored all tests in distutils, so they use a temporary directory. --- tests/support.py | 4 ++-- tests/test_build_ext.py | 9 +++++---- tests/test_config.py | 13 ++++++++----- tests/test_dir_util.py | 13 +++++++------ tests/test_dist.py | 30 +++++++++++++---------------- tests/test_file_util.py | 19 ++++++++----------- tests/test_sdist.py | 50 +++++++++++++++++++++---------------------------- 7 files changed, 64 insertions(+), 74 deletions(-) diff --git a/tests/support.py b/tests/support.py index 475ceee5..6dfc82cd 100644 --- a/tests/support.py +++ b/tests/support.py @@ -1,5 +1,5 @@ """Support code for distutils test cases.""" - +import os import shutil import tempfile @@ -31,7 +31,7 @@ class TempdirManager(object): super(TempdirManager, self).tearDown() while self.tempdirs: d = self.tempdirs.pop() - shutil.rmtree(d) + shutil.rmtree(d, os.name in ('nt', 'cygwin')) def mkdtemp(self): """Create a temporary directory that will be cleaned up. diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 3c7d228d..a38558b0 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -7,6 +7,7 @@ from StringIO import StringIO from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig +from distutils.tests import support import unittest from test import test_support @@ -19,11 +20,12 @@ def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') return os.path.join(srcdir, 'Modules', 'xxmodule.c') -class BuildExtTestCase(unittest.TestCase): +class BuildExtTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path - self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") + support.TempdirManager.setUp(self) + self.tmp_dir = self.mkdtemp() self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) @@ -74,8 +76,7 @@ class BuildExtTestCase(unittest.TestCase): # Get everything back to normal test_support.unload('xx') sys.path = self.sys_path - # XXX on Windows the test leaves a directory with xx module in TEMP - shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') + support.TempdirManager.tearDown(self) def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) diff --git a/tests/test_config.py b/tests/test_config.py index cae76898..d8127692 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -2,6 +2,8 @@ import sys import os import unittest +import tempfile +import shutil from distutils.core import PyPIRCCommand from distutils.core import Distribution @@ -49,13 +51,15 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): """Patches the environment.""" + support.TempdirManager.setUp(self) + if os.environ.has_key('HOME'): self._old_home = os.environ['HOME'] else: self._old_home = None - curdir = os.path.dirname(__file__) - os.environ['HOME'] = curdir - self.rc = os.path.join(curdir, '.pypirc') + self.tmp_dir = self.mkdtemp() + os.environ['HOME'] = self.tmp_dir + self.rc = os.path.join(self.tmp_dir, '.pypirc') self.dist = Distribution() class command(PyPIRCCommand): @@ -74,9 +78,8 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): del os.environ['HOME'] else: os.environ['HOME'] = self._old_home - if os.path.exists(self.rc): - os.remove(self.rc) set_threshold(self.old_threshold) + support.TempdirManager.tearDown(self) def test_server_registration(self): # This test makes sure PyPIRCCommand knows how to: diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index edf93c40..bf416b6d 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -9,8 +9,9 @@ from distutils.dir_util import create_tree from distutils.dir_util import copy_tree from distutils import log +from distutils.tests import support -class DirUtilTestCase(unittest.TestCase): +class DirUtilTestCase(support.TempdirManager, unittest.TestCase): def _log(self, msg, *args): if len(args) > 0: @@ -19,18 +20,18 @@ class DirUtilTestCase(unittest.TestCase): self._logs.append(msg) def setUp(self): + support.TempdirManager.setUp(self) self._logs = [] - self.root_target = os.path.join(os.path.dirname(__file__), 'deep') + tmp_dir = self.mkdtemp() + self.root_target = os.path.join(tmp_dir, 'deep') self.target = os.path.join(self.root_target, 'here') - self.target2 = os.path.join(os.path.dirname(__file__), 'deep2') + self.target2 = os.path.join(tmp_dir, 'deep2') self.old_log = log.info log.info = self._log def tearDown(self): - for target in (self.target, self.target2): - if os.path.exists(target): - shutil.rmtree(target) log.info = self.old_log + support.TempdirManager.tearDown(self) def test_mkpath_remove_tree_verbosity(self): diff --git a/tests/test_dist.py b/tests/test_dist.py index bf59c418..0e9868ae 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -11,7 +11,7 @@ import unittest import warnings from test.test_support import TESTFN - +from distutils.tests import support class test_dist(distutils.cmd.Command): """Sample distutils extension command.""" @@ -36,14 +36,16 @@ class TestDistribution(distutils.dist.Distribution): return self._config_files -class DistributionTestCase(unittest.TestCase): +class DistributionTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): + support.TempdirManager.setUp(self) self.argv = sys.argv[:] del sys.argv[1:] def tearDown(self): sys.argv[:] = self.argv + support.TempdirManager.tearDown(self) def create_distribution(self, configfiles=()): d = TestDistribution() @@ -100,7 +102,8 @@ class DistributionTestCase(unittest.TestCase): def test_write_pkg_file(self): # Check DistributionMetadata handling of Unicode fields - my_file = os.path.join(os.path.dirname(__file__), 'f') + tmp_dir = self.mkdtemp() + my_file = os.path.join(tmp_dir, 'f') klass = distutils.dist.Distribution dist = klass(attrs={'author': u'Mister Café', @@ -113,11 +116,7 @@ class DistributionTestCase(unittest.TestCase): # let's make sure the file can be written # with Unicode fields. they are encoded with # PKG_INFO_ENCODING - try: - dist.metadata.write_pkg_file(open(my_file, 'w')) - finally: - if os.path.exists(my_file): - os.remove(my_file) + dist.metadata.write_pkg_file(open(my_file, 'w')) # regular ascii is of course always usable dist = klass(attrs={'author': 'Mister Cafe', @@ -126,11 +125,8 @@ class DistributionTestCase(unittest.TestCase): 'description': 'Cafe torrefie', 'long_description': 'Hehehe'}) - try: - dist.metadata.write_pkg_file(open(my_file, 'w')) - finally: - if os.path.exists(my_file): - os.remove(my_file) + my_file2 = os.path.join(tmp_dir, 'f2') + dist.metadata.write_pkg_file(open(my_file, 'w')) def test_empty_options(self): # an empty options dictionary should not stay in the @@ -155,7 +151,7 @@ class DistributionTestCase(unittest.TestCase): self.assertEquals(len(warns), 0) -class MetadataTestCase(unittest.TestCase): +class MetadataTestCase(support.TempdirManager, unittest.TestCase): def test_simple_metadata(self): attrs = {"name": "package", @@ -254,8 +250,8 @@ class MetadataTestCase(unittest.TestCase): else: user_filename = "pydistutils.cfg" - curdir = os.path.dirname(__file__) - user_filename = os.path.join(curdir, user_filename) + temp_dir = self.mkdtemp() + user_filename = os.path.join(temp_dir, user_filename) f = open(user_filename, 'w') f.write('.') f.close() @@ -265,7 +261,7 @@ class MetadataTestCase(unittest.TestCase): # linux-style if sys.platform in ('linux', 'darwin'): - os.environ['HOME'] = curdir + os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assert_(user_filename in files) diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 523f1aef..9373af85 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -5,8 +5,9 @@ import shutil from distutils.file_util import move_file from distutils import log +from distutils.tests import support -class FileUtilTestCase(unittest.TestCase): +class FileUtilTestCase(support.TempdirManager, unittest.TestCase): def _log(self, msg, *args): if len(args) > 0: @@ -15,24 +16,20 @@ class FileUtilTestCase(unittest.TestCase): self._logs.append(msg) def setUp(self): + support.TempdirManager.setUp(self) self._logs = [] self.old_log = log.info log.info = self._log - self.source = os.path.join(os.path.dirname(__file__), 'f1') - self.target = os.path.join(os.path.dirname(__file__), 'f2') - self.target_dir = os.path.join(os.path.dirname(__file__), 'd1') + tmp_dir = self.mkdtemp() + self.source = os.path.join(tmp_dir, 'f1') + self.target = os.path.join(tmp_dir, 'f2') + self.target_dir = os.path.join(tmp_dir, 'd1') def tearDown(self): log.info = self.old_log - for f in (self.source, self.target, self.target_dir): - if os.path.exists(f): - if os.path.isfile(f): - os.remove(f) - else: - shutil.rmtree(f) + support.TempdirManager.tearDown(self) def test_move_file_verbosity(self): - f = open(self.source, 'w') f.write('some content') f.close() diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 0d839b5c..b2e98008 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -5,6 +5,7 @@ import shutil import zipfile from os.path import join import sys +import tempfile from distutils.command.sdist import sdist from distutils.core import Distribution @@ -12,9 +13,6 @@ from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError from distutils.spawn import find_executable -CURDIR = os.path.dirname(__file__) -TEMP_PKG = join(CURDIR, 'temppkg') - SETUP_PY = """ from distutils.core import setup import somecode @@ -29,28 +27,25 @@ recursive-include somecode * class sdistTestCase(PyPIRCCommandTestCase): def setUp(self): + # PyPIRCCommandTestCase creates a temp dir already + # and put it in self.tmp_dir PyPIRCCommandTestCase.setUp(self) + # setting up an environment self.old_path = os.getcwd() + os.mkdir(join(self.tmp_dir, 'somecode')) + os.mkdir(join(self.tmp_dir, 'dist')) + # creating a MANIFEST, a package, and a README + self._write(join(self.tmp_dir, 'MANIFEST.in'), MANIFEST_IN) + self._write(join(self.tmp_dir, 'README'), 'xxx') + self._write(join(self.tmp_dir, 'somecode', '__init__.py'), '#') + self._write(join(self.tmp_dir, 'setup.py'), SETUP_PY) + os.chdir(self.tmp_dir) def tearDown(self): + # back to normal os.chdir(self.old_path) - if os.path.exists(TEMP_PKG): - shutil.rmtree(TEMP_PKG) PyPIRCCommandTestCase.tearDown(self) - def _init_tmp_pkg(self): - if os.path.exists(TEMP_PKG): - shutil.rmtree(TEMP_PKG) - os.mkdir(TEMP_PKG) - os.mkdir(join(TEMP_PKG, 'somecode')) - os.mkdir(join(TEMP_PKG, 'dist')) - # creating a MANIFEST, a package, and a README - self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) - self._write(join(TEMP_PKG, 'README'), 'xxx') - self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') - self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) - os.chdir(TEMP_PKG) - def _write(self, path, content): f = open(path, 'w') try: @@ -62,18 +57,17 @@ class sdistTestCase(PyPIRCCommandTestCase): # this test creates a package with some vcs dirs in it # and launch sdist to make sure they get pruned # on all systems - self._init_tmp_pkg() # creating VCS directories with some files in them - os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) - self._write(join(TEMP_PKG, 'somecode', '.svn', 'ok.py'), 'xxx') + os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) + self._write(join(self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') - os.mkdir(join(TEMP_PKG, 'somecode', '.hg')) - self._write(join(TEMP_PKG, 'somecode', '.hg', + os.mkdir(join(self.tmp_dir, 'somecode', '.hg')) + self._write(join(self.tmp_dir, 'somecode', '.hg', 'ok'), 'xxx') - os.mkdir(join(TEMP_PKG, 'somecode', '.git')) - self._write(join(TEMP_PKG, 'somecode', '.git', + os.mkdir(join(self.tmp_dir, 'somecode', '.git')) + self._write(join(self.tmp_dir, 'somecode', '.git', 'ok'), 'xxx') # now building a sdist @@ -96,7 +90,7 @@ class sdistTestCase(PyPIRCCommandTestCase): cmd.run() # now let's check what we have - dist_folder = join(TEMP_PKG, 'dist') + dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) self.assertEquals(files, ['fake-1.0.zip']) @@ -116,8 +110,6 @@ class sdistTestCase(PyPIRCCommandTestCase): find_executable('gzip') is None): return - self._init_tmp_pkg() - # now building a sdist dist = Distribution() dist.script_name = 'setup.py' @@ -137,7 +129,7 @@ class sdistTestCase(PyPIRCCommandTestCase): cmd.run() # making sure we have two files - dist_folder = join(TEMP_PKG, 'dist') + dist_folder = join(self.tmp_dir, 'dist') result = os.listdir(dist_folder) result.sort() self.assertEquals(result, -- cgit v1.2.1 From 9fa930f80d7a407b6a22441c9c2f04548f74eb87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 14 Feb 2009 14:12:30 +0000 Subject: Replace variable --- tests/test_dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index 0e9868ae..847df7bb 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -268,7 +268,7 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase): # win32-style if sys.platform == 'win32': # home drive should be found - os.environ['HOME'] = curdir + os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assert_(user_filename in files, '%r not found in %r' % (user_filename, files)) -- cgit v1.2.1 From 181cd140aa9a1b02ba4e592755cabe67fb034d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 14 Feb 2009 14:35:51 +0000 Subject: Merged revisions 69609 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69609 | tarek.ziade | 2009-02-14 15:10:23 +0100 (Sat, 14 Feb 2009) | 1 line Fix for #5257: refactored all tests in distutils, so they use a temporary directory. ........ --- tests/support.py | 4 ++-- tests/test_build_ext.py | 9 +++++---- tests/test_config.py | 12 +++++++----- tests/test_dir_util.py | 13 +++++++------ tests/test_dist.py | 11 ++++++----- tests/test_file_util.py | 19 ++++++++----------- tests/test_sdist.py | 50 +++++++++++++++++++++---------------------------- 7 files changed, 56 insertions(+), 62 deletions(-) diff --git a/tests/support.py b/tests/support.py index 91e704cf..48bcbccf 100644 --- a/tests/support.py +++ b/tests/support.py @@ -1,5 +1,5 @@ """Support code for distutils test cases.""" - +import os import shutil import tempfile @@ -31,7 +31,7 @@ class TempdirManager(object): super().tearDown() while self.tempdirs: d = self.tempdirs.pop() - shutil.rmtree(d) + shutil.rmtree(d, os.name in ('nt', 'cygwin')) def mkdtemp(self): """Create a temporary directory that will be cleaned up. diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 5e42943c..91b3de85 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -7,6 +7,7 @@ from io import StringIO from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig +from distutils.tests.support import TempdirManager import unittest from test import support @@ -19,11 +20,12 @@ def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') return os.path.join(srcdir, 'Modules', 'xxmodule.c') -class BuildExtTestCase(unittest.TestCase): +class BuildExtTestCase(TempdirManager, unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path - self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") + TempdirManager.setUp(self) + self.tmp_dir = self.mkdtemp() self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) @@ -74,8 +76,7 @@ class BuildExtTestCase(unittest.TestCase): # Get everything back to normal support.unload('xx') sys.path = self.sys_path - # XXX on Windows the test leaves a directory with xx module in TEMP - shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') + TempdirManager.tearDown(self) def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) diff --git a/tests/test_config.py b/tests/test_config.py index bdc9b2b5..0df6af84 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -2,6 +2,7 @@ import sys import os import unittest +import tempfile from distutils.core import PyPIRCCommand from distutils.core import Distribution @@ -49,13 +50,15 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): """Patches the environment.""" + support.TempdirManager.setUp(self) + if 'HOME' in os.environ: self._old_home = os.environ['HOME'] else: self._old_home = None - curdir = os.path.dirname(__file__) - os.environ['HOME'] = curdir - self.rc = os.path.join(curdir, '.pypirc') + self.tmp_dir = self.mkdtemp() + os.environ['HOME'] = self.tmp_dir + self.rc = os.path.join(self.tmp_dir, '.pypirc') self.dist = Distribution() class command(PyPIRCCommand): @@ -74,9 +77,8 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): del os.environ['HOME'] else: os.environ['HOME'] = self._old_home - if os.path.exists(self.rc): - os.remove(self.rc) set_threshold(self.old_threshold) + support.TempdirManager.tearDown(self) def test_server_registration(self): # This test makes sure PyPIRCCommand knows how to: diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index edf93c40..bf416b6d 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -9,8 +9,9 @@ from distutils.dir_util import create_tree from distutils.dir_util import copy_tree from distutils import log +from distutils.tests import support -class DirUtilTestCase(unittest.TestCase): +class DirUtilTestCase(support.TempdirManager, unittest.TestCase): def _log(self, msg, *args): if len(args) > 0: @@ -19,18 +20,18 @@ class DirUtilTestCase(unittest.TestCase): self._logs.append(msg) def setUp(self): + support.TempdirManager.setUp(self) self._logs = [] - self.root_target = os.path.join(os.path.dirname(__file__), 'deep') + tmp_dir = self.mkdtemp() + self.root_target = os.path.join(tmp_dir, 'deep') self.target = os.path.join(self.root_target, 'here') - self.target2 = os.path.join(os.path.dirname(__file__), 'deep2') + self.target2 = os.path.join(tmp_dir, 'deep2') self.old_log = log.info log.info = self._log def tearDown(self): - for target in (self.target, self.target2): - if os.path.exists(target): - shutil.rmtree(target) log.info = self.old_log + support.TempdirManager.tearDown(self) def test_mkpath_remove_tree_verbosity(self): diff --git a/tests/test_dist.py b/tests/test_dist.py index 8e7ef5d3..3ac0fdd1 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -9,6 +9,7 @@ import unittest import warnings from test.support import TESTFN +from distutils.tests import support class test_dist(distutils.cmd.Command): @@ -120,7 +121,7 @@ class DistributionTestCase(unittest.TestCase): self.assertEquals(len(warns), 0) -class MetadataTestCase(unittest.TestCase): +class MetadataTestCase(support.TempdirManager, unittest.TestCase): def test_simple_metadata(self): attrs = {"name": "package", @@ -219,8 +220,8 @@ class MetadataTestCase(unittest.TestCase): else: user_filename = "pydistutils.cfg" - curdir = os.path.dirname(__file__) - user_filename = os.path.join(curdir, user_filename) + temp_dir = self.mkdtemp() + user_filename = os.path.join(temp_dir, user_filename) f = open(user_filename, 'w') f.write('.') f.close() @@ -230,14 +231,14 @@ class MetadataTestCase(unittest.TestCase): # linux-style if sys.platform in ('linux', 'darwin'): - os.environ['HOME'] = curdir + os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assert_(user_filename in files) # win32-style if sys.platform == 'win32': # home drive should be found - os.environ['HOME'] = curdir + os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assert_(user_filename in files, '%r not found in %r' % (user_filename, files)) diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 523f1aef..9373af85 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -5,8 +5,9 @@ import shutil from distutils.file_util import move_file from distutils import log +from distutils.tests import support -class FileUtilTestCase(unittest.TestCase): +class FileUtilTestCase(support.TempdirManager, unittest.TestCase): def _log(self, msg, *args): if len(args) > 0: @@ -15,24 +16,20 @@ class FileUtilTestCase(unittest.TestCase): self._logs.append(msg) def setUp(self): + support.TempdirManager.setUp(self) self._logs = [] self.old_log = log.info log.info = self._log - self.source = os.path.join(os.path.dirname(__file__), 'f1') - self.target = os.path.join(os.path.dirname(__file__), 'f2') - self.target_dir = os.path.join(os.path.dirname(__file__), 'd1') + tmp_dir = self.mkdtemp() + self.source = os.path.join(tmp_dir, 'f1') + self.target = os.path.join(tmp_dir, 'f2') + self.target_dir = os.path.join(tmp_dir, 'd1') def tearDown(self): log.info = self.old_log - for f in (self.source, self.target, self.target_dir): - if os.path.exists(f): - if os.path.isfile(f): - os.remove(f) - else: - shutil.rmtree(f) + support.TempdirManager.tearDown(self) def test_move_file_verbosity(self): - f = open(self.source, 'w') f.write('some content') f.close() diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 0d839b5c..b2e98008 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -5,6 +5,7 @@ import shutil import zipfile from os.path import join import sys +import tempfile from distutils.command.sdist import sdist from distutils.core import Distribution @@ -12,9 +13,6 @@ from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError from distutils.spawn import find_executable -CURDIR = os.path.dirname(__file__) -TEMP_PKG = join(CURDIR, 'temppkg') - SETUP_PY = """ from distutils.core import setup import somecode @@ -29,28 +27,25 @@ recursive-include somecode * class sdistTestCase(PyPIRCCommandTestCase): def setUp(self): + # PyPIRCCommandTestCase creates a temp dir already + # and put it in self.tmp_dir PyPIRCCommandTestCase.setUp(self) + # setting up an environment self.old_path = os.getcwd() + os.mkdir(join(self.tmp_dir, 'somecode')) + os.mkdir(join(self.tmp_dir, 'dist')) + # creating a MANIFEST, a package, and a README + self._write(join(self.tmp_dir, 'MANIFEST.in'), MANIFEST_IN) + self._write(join(self.tmp_dir, 'README'), 'xxx') + self._write(join(self.tmp_dir, 'somecode', '__init__.py'), '#') + self._write(join(self.tmp_dir, 'setup.py'), SETUP_PY) + os.chdir(self.tmp_dir) def tearDown(self): + # back to normal os.chdir(self.old_path) - if os.path.exists(TEMP_PKG): - shutil.rmtree(TEMP_PKG) PyPIRCCommandTestCase.tearDown(self) - def _init_tmp_pkg(self): - if os.path.exists(TEMP_PKG): - shutil.rmtree(TEMP_PKG) - os.mkdir(TEMP_PKG) - os.mkdir(join(TEMP_PKG, 'somecode')) - os.mkdir(join(TEMP_PKG, 'dist')) - # creating a MANIFEST, a package, and a README - self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) - self._write(join(TEMP_PKG, 'README'), 'xxx') - self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') - self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) - os.chdir(TEMP_PKG) - def _write(self, path, content): f = open(path, 'w') try: @@ -62,18 +57,17 @@ class sdistTestCase(PyPIRCCommandTestCase): # this test creates a package with some vcs dirs in it # and launch sdist to make sure they get pruned # on all systems - self._init_tmp_pkg() # creating VCS directories with some files in them - os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) - self._write(join(TEMP_PKG, 'somecode', '.svn', 'ok.py'), 'xxx') + os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) + self._write(join(self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') - os.mkdir(join(TEMP_PKG, 'somecode', '.hg')) - self._write(join(TEMP_PKG, 'somecode', '.hg', + os.mkdir(join(self.tmp_dir, 'somecode', '.hg')) + self._write(join(self.tmp_dir, 'somecode', '.hg', 'ok'), 'xxx') - os.mkdir(join(TEMP_PKG, 'somecode', '.git')) - self._write(join(TEMP_PKG, 'somecode', '.git', + os.mkdir(join(self.tmp_dir, 'somecode', '.git')) + self._write(join(self.tmp_dir, 'somecode', '.git', 'ok'), 'xxx') # now building a sdist @@ -96,7 +90,7 @@ class sdistTestCase(PyPIRCCommandTestCase): cmd.run() # now let's check what we have - dist_folder = join(TEMP_PKG, 'dist') + dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) self.assertEquals(files, ['fake-1.0.zip']) @@ -116,8 +110,6 @@ class sdistTestCase(PyPIRCCommandTestCase): find_executable('gzip') is None): return - self._init_tmp_pkg() - # now building a sdist dist = Distribution() dist.script_name = 'setup.py' @@ -137,7 +129,7 @@ class sdistTestCase(PyPIRCCommandTestCase): cmd.run() # making sure we have two files - dist_folder = join(TEMP_PKG, 'dist') + dist_folder = join(self.tmp_dir, 'dist') result = os.listdir(dist_folder) result.sort() self.assertEquals(result, -- cgit v1.2.1 From 348e5075053db429b26d72a22fa7e671dd4935d0 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 16 Feb 2009 18:22:15 +0000 Subject: remove another use of cmp() --- version.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/version.py b/version.py index 77814377..79d458d8 100644 --- a/version.py +++ b/version.py @@ -338,7 +338,12 @@ class LooseVersion (Version): if isinstance(other, str): other = LooseVersion(other) - return cmp(self.version, other.version) + if self.version == other.version: + return 0 + if self.version < other.version: + return -1 + if self.version > other.version: + return 1 # end class LooseVersion -- cgit v1.2.1 From 31babd18fd9e32d764982b2585265c50d90faf0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 16 Feb 2009 21:38:01 +0000 Subject: Fixed #2279: distutils.sdist.add_defaults now add files listed in package_data and data_files --- command/sdist.py | 22 ++++++++- tests/support.py | 13 +++++ tests/test_sdist.py | 137 ++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 129 insertions(+), 43 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index e8b6bce9..291e8123 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -259,6 +259,9 @@ class sdist (Command): - setup.py - test/test*.py - all pure Python modules mentioned in setup script + - all files pointed by package_data (build_py) + - all files defined in data_files. + - all files defined as scripts. - all C sources listed as part of extensions or C libraries in the setup script (doesn't catch C headers!) Warns if (README or README.txt) or setup.py are missing; everything @@ -291,10 +294,27 @@ class sdist (Command): if files: self.filelist.extend(files) + # build_py is used to get: + # - python modules + # - files defined in package_data + build_py = self.get_finalized_command('build_py') + + # getting python files if self.distribution.has_pure_modules(): - build_py = self.get_finalized_command('build_py') self.filelist.extend(build_py.get_source_files()) + # getting package_data files + # (computed in build_py.data_files by build_py.finalize_options) + for pkg, src_dir, build_dir, filenames in build_py.data_files: + for filename in filenames: + self.filelist.append(os.path.join(src_dir, filename)) + + # getting distribution.data_files + if self.distribution.has_data_files(): + for dirname, filenames in self.distribution.data_files: + for filename in filenames: + self.filelist.append(os.path.join(dirname, filename)) + if self.distribution.has_ext_modules(): build_ext = self.get_finalized_command('build_ext') self.filelist.extend(build_ext.get_source_files()) diff --git a/tests/support.py b/tests/support.py index 6dfc82cd..fbbf35d8 100644 --- a/tests/support.py +++ b/tests/support.py @@ -42,6 +42,19 @@ class TempdirManager(object): self.tempdirs.append(d) return d + def write_file(self, path, content): + """Writes a file in the given path. + + + path can be a string or a sequence. + """ + if isinstance(path, (list, tuple)): + path = os.path.join(*path) + f = open(path, 'w') + try: + f.write(content) + finally: + f.close() class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" diff --git a/tests/test_sdist.py b/tests/test_sdist.py index b2e98008..de588112 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -12,6 +12,7 @@ from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError from distutils.spawn import find_executable +from distutils.tests import support SETUP_PY = """ from distutils.core import setup @@ -20,13 +21,20 @@ import somecode setup(name='fake') """ -MANIFEST_IN = """ -recursive-include somecode * +MANIFEST = """\ +README +setup.py +data/data.dt +scripts/script.py +somecode/__init__.py +somecode/doc.dat +somecode/doc.txt """ -class sdistTestCase(PyPIRCCommandTestCase): +class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): def setUp(self): + support.LoggingSilencer.setUp(self) # PyPIRCCommandTestCase creates a temp dir already # and put it in self.tmp_dir PyPIRCCommandTestCase.setUp(self) @@ -34,24 +42,34 @@ class sdistTestCase(PyPIRCCommandTestCase): self.old_path = os.getcwd() os.mkdir(join(self.tmp_dir, 'somecode')) os.mkdir(join(self.tmp_dir, 'dist')) - # creating a MANIFEST, a package, and a README - self._write(join(self.tmp_dir, 'MANIFEST.in'), MANIFEST_IN) - self._write(join(self.tmp_dir, 'README'), 'xxx') - self._write(join(self.tmp_dir, 'somecode', '__init__.py'), '#') - self._write(join(self.tmp_dir, 'setup.py'), SETUP_PY) + # a package, and a README + self.write_file((self.tmp_dir, 'README'), 'xxx') + self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#') + self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY) os.chdir(self.tmp_dir) def tearDown(self): # back to normal os.chdir(self.old_path) PyPIRCCommandTestCase.tearDown(self) - - def _write(self, path, content): - f = open(path, 'w') - try: - f.write(content) - finally: - f.close() + support.LoggingSilencer.tearDown(self) + + def get_cmd(self, metadata=None): + """Returns a cmd""" + if metadata is None: + metadata = {'name': 'fake', 'version': '1.0', + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'} + dist = Distribution(metadata) + dist.script_name = 'setup.py' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.dist_dir = 'dist' + def _warn(*args): + pass + cmd.warn = _warn + return dist, cmd def test_prune_file_list(self): # this test creates a package with some vcs dirs in it @@ -60,33 +78,24 @@ class sdistTestCase(PyPIRCCommandTestCase): # creating VCS directories with some files in them os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) - self._write(join(self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') + self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') os.mkdir(join(self.tmp_dir, 'somecode', '.hg')) - self._write(join(self.tmp_dir, 'somecode', '.hg', + self.write_file((self.tmp_dir, 'somecode', '.hg', 'ok'), 'xxx') os.mkdir(join(self.tmp_dir, 'somecode', '.git')) - self._write(join(self.tmp_dir, 'somecode', '.git', + self.write_file((self.tmp_dir, 'somecode', '.git', 'ok'), 'xxx') # now building a sdist - dist = Distribution() - dist.script_name = 'setup.py' - dist.metadata.name = 'fake' - dist.metadata.version = '1.0' - dist.metadata.url = 'http://xxx' - dist.metadata.author = dist.metadata.author_email = 'xxx' - dist.packages = ['somecode'] - dist.include_package_data = True - cmd = sdist(dist) - cmd.manifest = 'MANIFEST' - cmd.template = 'MANIFEST.in' - cmd.dist_dir = 'dist' + dist, cmd = self.get_cmd() # zip is available universally # (tar might not be installed under win32) cmd.formats = ['zip'] + + cmd.ensure_finalized() cmd.run() # now let's check what we have @@ -111,21 +120,11 @@ class sdistTestCase(PyPIRCCommandTestCase): return # now building a sdist - dist = Distribution() - dist.script_name = 'setup.py' - dist.metadata.name = 'fake' - dist.metadata.version = '1.0' - dist.metadata.url = 'http://xxx' - dist.metadata.author = dist.metadata.author_email = 'xxx' - dist.packages = ['somecode'] - dist.include_package_data = True - cmd = sdist(dist) - cmd.manifest = 'MANIFEST' - cmd.template = 'MANIFEST.in' - cmd.dist_dir = 'dist' + dist, cmd = self.get_cmd() # creating a gztar then a tar cmd.formats = ['gztar', 'tar'] + cmd.ensure_finalized() cmd.run() # making sure we have two files @@ -140,6 +139,8 @@ class sdistTestCase(PyPIRCCommandTestCase): # now trying a tar then a gztar cmd.formats = ['tar', 'gztar'] + + cmd.ensure_finalized() cmd.run() result = os.listdir(dist_folder) @@ -147,6 +148,58 @@ class sdistTestCase(PyPIRCCommandTestCase): self.assertEquals(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + def test_add_defaults(self): + + # http://bugs.python.org/issue2279 + + # add_default should also include + # data_files and package_data + dist, cmd = self.get_cmd() + + # filling data_files by pointing files + # in package_data + dist.package_data = {'': ['*.cfg', '*.dat'], + 'somecode': ['*.txt']} + self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#') + + # adding some data in data_files + data_dir = join(self.tmp_dir, 'data') + os.mkdir(data_dir) + self.write_file((data_dir, 'data.dt'), '#') + dist.data_files = [('data', ['data.dt'])] + + # adding a script + script_dir = join(self.tmp_dir, 'scripts') + os.mkdir(script_dir) + self.write_file((script_dir, 'script.py'), '#') + dist.scripts = [join('scripts', 'script.py')] + + + cmd.formats = ['zip'] + cmd.use_defaults = True + + cmd.ensure_finalized() + cmd.run() + + # now let's check what we have + dist_folder = join(self.tmp_dir, 'dist') + files = os.listdir(dist_folder) + self.assertEquals(files, ['fake-1.0.zip']) + + zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) + try: + content = zip_file.namelist() + finally: + zip_file.close() + + # making sure everything was added + self.assertEquals(len(content), 8) + + # checking the MANIFEST + manifest = open(join(self.tmp_dir, 'MANIFEST')).read() + self.assertEquals(manifest, MANIFEST) + def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From 25e6f089801d3a41b308953ae1adcbd842968ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 16 Feb 2009 21:41:54 +0000 Subject: #2279: use os.sep so the MANIFEST file test work on win32 --- tests/test_sdist.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index de588112..b4e922f9 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -24,11 +24,11 @@ setup(name='fake') MANIFEST = """\ README setup.py -data/data.dt -scripts/script.py -somecode/__init__.py -somecode/doc.dat -somecode/doc.txt +data%(sep)sdata.dt +scripts%(sep)sscript.py +somecode%(sep)s__init__.py +somecode%(sep)sdoc.dat +somecode%(sep)sdoc.txt """ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): @@ -198,7 +198,7 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): # checking the MANIFEST manifest = open(join(self.tmp_dir, 'MANIFEST')).read() - self.assertEquals(manifest, MANIFEST) + self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From edd4bb62c85ac3b9164bcf80930e849a78ae17e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 16 Feb 2009 21:49:12 +0000 Subject: Merged revisions 69692 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69692 | tarek.ziade | 2009-02-16 22:38:01 +0100 (Mon, 16 Feb 2009) | 1 line Fixed #2279: distutils.sdist.add_defaults now add files listed in package_data and data_files ........ --- command/sdist.py | 22 ++++++++- tests/support.py | 13 +++++ tests/test_sdist.py | 137 ++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 129 insertions(+), 43 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 054fe191..c057b667 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -252,6 +252,9 @@ class sdist (Command): - setup.py - test/test*.py - all pure Python modules mentioned in setup script + - all files pointed by package_data (build_py) + - all files defined in data_files. + - all files defined as scripts. - all C sources listed as part of extensions or C libraries in the setup script (doesn't catch C headers!) Warns if (README or README.txt) or setup.py are missing; everything @@ -283,10 +286,27 @@ class sdist (Command): if files: self.filelist.extend(files) + # build_py is used to get: + # - python modules + # - files defined in package_data + build_py = self.get_finalized_command('build_py') + + # getting python files if self.distribution.has_pure_modules(): - build_py = self.get_finalized_command('build_py') self.filelist.extend(build_py.get_source_files()) + # getting package_data files + # (computed in build_py.data_files by build_py.finalize_options) + for pkg, src_dir, build_dir, filenames in build_py.data_files: + for filename in filenames: + self.filelist.append(os.path.join(src_dir, filename)) + + # getting distribution.data_files + if self.distribution.has_data_files(): + for dirname, filenames in self.distribution.data_files: + for filename in filenames: + self.filelist.append(os.path.join(dirname, filename)) + if self.distribution.has_ext_modules(): build_ext = self.get_finalized_command('build_ext') self.filelist.extend(build_ext.get_source_files()) diff --git a/tests/support.py b/tests/support.py index 48bcbccf..ecc8da17 100644 --- a/tests/support.py +++ b/tests/support.py @@ -42,6 +42,19 @@ class TempdirManager(object): self.tempdirs.append(d) return d + def write_file(self, path, content): + """Writes a file in the given path. + + + path can be a string or a sequence. + """ + if isinstance(path, (list, tuple)): + path = os.path.join(*path) + f = open(path, 'w') + try: + f.write(content) + finally: + f.close() class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" diff --git a/tests/test_sdist.py b/tests/test_sdist.py index b2e98008..de588112 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -12,6 +12,7 @@ from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError from distutils.spawn import find_executable +from distutils.tests import support SETUP_PY = """ from distutils.core import setup @@ -20,13 +21,20 @@ import somecode setup(name='fake') """ -MANIFEST_IN = """ -recursive-include somecode * +MANIFEST = """\ +README +setup.py +data/data.dt +scripts/script.py +somecode/__init__.py +somecode/doc.dat +somecode/doc.txt """ -class sdistTestCase(PyPIRCCommandTestCase): +class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): def setUp(self): + support.LoggingSilencer.setUp(self) # PyPIRCCommandTestCase creates a temp dir already # and put it in self.tmp_dir PyPIRCCommandTestCase.setUp(self) @@ -34,24 +42,34 @@ class sdistTestCase(PyPIRCCommandTestCase): self.old_path = os.getcwd() os.mkdir(join(self.tmp_dir, 'somecode')) os.mkdir(join(self.tmp_dir, 'dist')) - # creating a MANIFEST, a package, and a README - self._write(join(self.tmp_dir, 'MANIFEST.in'), MANIFEST_IN) - self._write(join(self.tmp_dir, 'README'), 'xxx') - self._write(join(self.tmp_dir, 'somecode', '__init__.py'), '#') - self._write(join(self.tmp_dir, 'setup.py'), SETUP_PY) + # a package, and a README + self.write_file((self.tmp_dir, 'README'), 'xxx') + self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#') + self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY) os.chdir(self.tmp_dir) def tearDown(self): # back to normal os.chdir(self.old_path) PyPIRCCommandTestCase.tearDown(self) - - def _write(self, path, content): - f = open(path, 'w') - try: - f.write(content) - finally: - f.close() + support.LoggingSilencer.tearDown(self) + + def get_cmd(self, metadata=None): + """Returns a cmd""" + if metadata is None: + metadata = {'name': 'fake', 'version': '1.0', + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'} + dist = Distribution(metadata) + dist.script_name = 'setup.py' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.dist_dir = 'dist' + def _warn(*args): + pass + cmd.warn = _warn + return dist, cmd def test_prune_file_list(self): # this test creates a package with some vcs dirs in it @@ -60,33 +78,24 @@ class sdistTestCase(PyPIRCCommandTestCase): # creating VCS directories with some files in them os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) - self._write(join(self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') + self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') os.mkdir(join(self.tmp_dir, 'somecode', '.hg')) - self._write(join(self.tmp_dir, 'somecode', '.hg', + self.write_file((self.tmp_dir, 'somecode', '.hg', 'ok'), 'xxx') os.mkdir(join(self.tmp_dir, 'somecode', '.git')) - self._write(join(self.tmp_dir, 'somecode', '.git', + self.write_file((self.tmp_dir, 'somecode', '.git', 'ok'), 'xxx') # now building a sdist - dist = Distribution() - dist.script_name = 'setup.py' - dist.metadata.name = 'fake' - dist.metadata.version = '1.0' - dist.metadata.url = 'http://xxx' - dist.metadata.author = dist.metadata.author_email = 'xxx' - dist.packages = ['somecode'] - dist.include_package_data = True - cmd = sdist(dist) - cmd.manifest = 'MANIFEST' - cmd.template = 'MANIFEST.in' - cmd.dist_dir = 'dist' + dist, cmd = self.get_cmd() # zip is available universally # (tar might not be installed under win32) cmd.formats = ['zip'] + + cmd.ensure_finalized() cmd.run() # now let's check what we have @@ -111,21 +120,11 @@ class sdistTestCase(PyPIRCCommandTestCase): return # now building a sdist - dist = Distribution() - dist.script_name = 'setup.py' - dist.metadata.name = 'fake' - dist.metadata.version = '1.0' - dist.metadata.url = 'http://xxx' - dist.metadata.author = dist.metadata.author_email = 'xxx' - dist.packages = ['somecode'] - dist.include_package_data = True - cmd = sdist(dist) - cmd.manifest = 'MANIFEST' - cmd.template = 'MANIFEST.in' - cmd.dist_dir = 'dist' + dist, cmd = self.get_cmd() # creating a gztar then a tar cmd.formats = ['gztar', 'tar'] + cmd.ensure_finalized() cmd.run() # making sure we have two files @@ -140,6 +139,8 @@ class sdistTestCase(PyPIRCCommandTestCase): # now trying a tar then a gztar cmd.formats = ['tar', 'gztar'] + + cmd.ensure_finalized() cmd.run() result = os.listdir(dist_folder) @@ -147,6 +148,58 @@ class sdistTestCase(PyPIRCCommandTestCase): self.assertEquals(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + def test_add_defaults(self): + + # http://bugs.python.org/issue2279 + + # add_default should also include + # data_files and package_data + dist, cmd = self.get_cmd() + + # filling data_files by pointing files + # in package_data + dist.package_data = {'': ['*.cfg', '*.dat'], + 'somecode': ['*.txt']} + self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#') + + # adding some data in data_files + data_dir = join(self.tmp_dir, 'data') + os.mkdir(data_dir) + self.write_file((data_dir, 'data.dt'), '#') + dist.data_files = [('data', ['data.dt'])] + + # adding a script + script_dir = join(self.tmp_dir, 'scripts') + os.mkdir(script_dir) + self.write_file((script_dir, 'script.py'), '#') + dist.scripts = [join('scripts', 'script.py')] + + + cmd.formats = ['zip'] + cmd.use_defaults = True + + cmd.ensure_finalized() + cmd.run() + + # now let's check what we have + dist_folder = join(self.tmp_dir, 'dist') + files = os.listdir(dist_folder) + self.assertEquals(files, ['fake-1.0.zip']) + + zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) + try: + content = zip_file.namelist() + finally: + zip_file.close() + + # making sure everything was added + self.assertEquals(len(content), 8) + + # checking the MANIFEST + manifest = open(join(self.tmp_dir, 'MANIFEST')).read() + self.assertEquals(manifest, MANIFEST) + def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From 431e410a6959dcad8b91db6b35d04ab9b956e787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 16 Feb 2009 21:51:13 +0000 Subject: Merged revisions 69693 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69693 | tarek.ziade | 2009-02-16 22:41:54 +0100 (Mon, 16 Feb 2009) | 1 line #2279: use os.sep so the MANIFEST file test work on win32 ........ --- tests/test_sdist.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index de588112..b4e922f9 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -24,11 +24,11 @@ setup(name='fake') MANIFEST = """\ README setup.py -data/data.dt -scripts/script.py -somecode/__init__.py -somecode/doc.dat -somecode/doc.txt +data%(sep)sdata.dt +scripts%(sep)sscript.py +somecode%(sep)s__init__.py +somecode%(sep)sdoc.dat +somecode%(sep)sdoc.txt """ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): @@ -198,7 +198,7 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): # checking the MANIFEST manifest = open(join(self.tmp_dir, 'MANIFEST')).read() - self.assertEquals(manifest, MANIFEST) + self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From 681c04f6333588b00ded56b7725cddc3ab2629d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 17 Feb 2009 09:42:44 +0000 Subject: #2279 added the plain path case for data_files --- command/sdist.py | 16 ++++++++++++---- tests/test_sdist.py | 14 +++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 291e8123..a1a0fb79 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -14,7 +14,7 @@ from distutils.text_file import TextFile from distutils.errors import * from distutils.filelist import FileList from distutils import log - +from distutils.util import convert_path def show_formats (): """Print all possible values for the 'formats' option (used by @@ -311,9 +311,17 @@ class sdist (Command): # getting distribution.data_files if self.distribution.has_data_files(): - for dirname, filenames in self.distribution.data_files: - for filename in filenames: - self.filelist.append(os.path.join(dirname, filename)) + for item in self.distribution.data_files: + if isinstance(item, str): # plain file + item = convert_path(item) + if os.path.isfile(item): + self.filelist.append(item) + else: # a (dirname, filenames) tuple + dirname, filenames = item + for f in filenames: + f = convert_path(os.path.join(dirname, f)) + if os.path.isfile(f): + self.filelist.append(f) if self.distribution.has_ext_modules(): build_ext = self.get_finalized_command('build_ext') diff --git a/tests/test_sdist.py b/tests/test_sdist.py index b4e922f9..82e5dc6d 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -26,6 +26,8 @@ README setup.py data%(sep)sdata.dt scripts%(sep)sscript.py +some%(sep)sfile.txt +some%(sep)sother_file.txt somecode%(sep)s__init__.py somecode%(sep)sdoc.dat somecode%(sep)sdoc.txt @@ -167,7 +169,14 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): data_dir = join(self.tmp_dir, 'data') os.mkdir(data_dir) self.write_file((data_dir, 'data.dt'), '#') - dist.data_files = [('data', ['data.dt'])] + some_dir = join(self.tmp_dir, 'some') + os.mkdir(some_dir) + self.write_file((some_dir, 'file.txt'), '#') + self.write_file((some_dir, 'other_file.txt'), '#') + + dist.data_files = [('data', ['data.dt', 'notexisting']), + 'some/file.txt', + 'some/other_file.txt'] # adding a script script_dir = join(self.tmp_dir, 'scripts') @@ -175,7 +184,6 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): self.write_file((script_dir, 'script.py'), '#') dist.scripts = [join('scripts', 'script.py')] - cmd.formats = ['zip'] cmd.use_defaults = True @@ -194,7 +202,7 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): zip_file.close() # making sure everything was added - self.assertEquals(len(content), 8) + self.assertEquals(len(content), 10) # checking the MANIFEST manifest = open(join(self.tmp_dir, 'MANIFEST')).read() -- cgit v1.2.1 From 0040fcb0a57bed6b39e790e05fd542f37a43c2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 17 Feb 2009 09:47:25 +0000 Subject: Merged revisions 69710 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69710 | tarek.ziade | 2009-02-17 10:42:44 +0100 (Tue, 17 Feb 2009) | 1 line #2279 added the plain path case for data_files ........ --- command/sdist.py | 16 ++++++++++++---- tests/test_sdist.py | 14 +++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index c057b667..9bb2ae04 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -15,7 +15,7 @@ from distutils.text_file import TextFile from distutils.errors import * from distutils.filelist import FileList from distutils import log - +from distutils.util import convert_path def show_formats (): """Print all possible values for the 'formats' option (used by @@ -303,9 +303,17 @@ class sdist (Command): # getting distribution.data_files if self.distribution.has_data_files(): - for dirname, filenames in self.distribution.data_files: - for filename in filenames: - self.filelist.append(os.path.join(dirname, filename)) + for item in self.distribution.data_files: + if isinstance(item, str): # plain file + item = convert_path(item) + if os.path.isfile(item): + self.filelist.append(item) + else: # a (dirname, filenames) tuple + dirname, filenames = item + for f in filenames: + f = convert_path(os.path.join(dirname, f)) + if os.path.isfile(f): + self.filelist.append(f) if self.distribution.has_ext_modules(): build_ext = self.get_finalized_command('build_ext') diff --git a/tests/test_sdist.py b/tests/test_sdist.py index b4e922f9..82e5dc6d 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -26,6 +26,8 @@ README setup.py data%(sep)sdata.dt scripts%(sep)sscript.py +some%(sep)sfile.txt +some%(sep)sother_file.txt somecode%(sep)s__init__.py somecode%(sep)sdoc.dat somecode%(sep)sdoc.txt @@ -167,7 +169,14 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): data_dir = join(self.tmp_dir, 'data') os.mkdir(data_dir) self.write_file((data_dir, 'data.dt'), '#') - dist.data_files = [('data', ['data.dt'])] + some_dir = join(self.tmp_dir, 'some') + os.mkdir(some_dir) + self.write_file((some_dir, 'file.txt'), '#') + self.write_file((some_dir, 'other_file.txt'), '#') + + dist.data_files = [('data', ['data.dt', 'notexisting']), + 'some/file.txt', + 'some/other_file.txt'] # adding a script script_dir = join(self.tmp_dir, 'scripts') @@ -175,7 +184,6 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): self.write_file((script_dir, 'script.py'), '#') dist.scripts = [join('scripts', 'script.py')] - cmd.formats = ['zip'] cmd.use_defaults = True @@ -194,7 +202,7 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): zip_file.close() # making sure everything was added - self.assertEquals(len(content), 8) + self.assertEquals(len(content), 10) # checking the MANIFEST manifest = open(join(self.tmp_dir, 'MANIFEST')).read() -- cgit v1.2.1 From 2a11a83c076445fd2992eba94358bb0a3add5e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 17 Feb 2009 23:06:51 +0000 Subject: fixed the data_files inclusion behavior --- command/sdist.py | 2 +- tests/test_sdist.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index a1a0fb79..a9ce28a7 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -319,7 +319,7 @@ class sdist (Command): else: # a (dirname, filenames) tuple dirname, filenames = item for f in filenames: - f = convert_path(os.path.join(dirname, f)) + f = convert_path(f) if os.path.isfile(f): self.filelist.append(f) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 82e5dc6d..9c579b40 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -23,6 +23,7 @@ setup(name='fake') MANIFEST = """\ README +inroot.txt setup.py data%(sep)sdata.dt scripts%(sep)sscript.py @@ -171,10 +172,13 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): self.write_file((data_dir, 'data.dt'), '#') some_dir = join(self.tmp_dir, 'some') os.mkdir(some_dir) + self.write_file((self.tmp_dir, 'inroot.txt'), '#') self.write_file((some_dir, 'file.txt'), '#') self.write_file((some_dir, 'other_file.txt'), '#') - dist.data_files = [('data', ['data.dt', 'notexisting']), + dist.data_files = [('data', ['data/data.dt', + 'inroot.txt', + 'notexisting']), 'some/file.txt', 'some/other_file.txt'] @@ -202,7 +206,7 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): zip_file.close() # making sure everything was added - self.assertEquals(len(content), 10) + self.assertEquals(len(content), 11) # checking the MANIFEST manifest = open(join(self.tmp_dir, 'MANIFEST')).read() -- cgit v1.2.1 From b235b6fad51c8f78e05ccc88e5eddf43f7e2aaba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 17 Feb 2009 23:10:18 +0000 Subject: Merged revisions 69724 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69724 | tarek.ziade | 2009-02-18 00:06:51 +0100 (Wed, 18 Feb 2009) | 1 line fixed the data_files inclusion behavior ........ --- command/sdist.py | 2 +- tests/test_sdist.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 9bb2ae04..1a64d0e3 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -311,7 +311,7 @@ class sdist (Command): else: # a (dirname, filenames) tuple dirname, filenames = item for f in filenames: - f = convert_path(os.path.join(dirname, f)) + f = convert_path(f) if os.path.isfile(f): self.filelist.append(f) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 82e5dc6d..9c579b40 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -23,6 +23,7 @@ setup(name='fake') MANIFEST = """\ README +inroot.txt setup.py data%(sep)sdata.dt scripts%(sep)sscript.py @@ -171,10 +172,13 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): self.write_file((data_dir, 'data.dt'), '#') some_dir = join(self.tmp_dir, 'some') os.mkdir(some_dir) + self.write_file((self.tmp_dir, 'inroot.txt'), '#') self.write_file((some_dir, 'file.txt'), '#') self.write_file((some_dir, 'other_file.txt'), '#') - dist.data_files = [('data', ['data.dt', 'notexisting']), + dist.data_files = [('data', ['data/data.dt', + 'inroot.txt', + 'notexisting']), 'some/file.txt', 'some/other_file.txt'] @@ -202,7 +206,7 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): zip_file.close() # making sure everything was added - self.assertEquals(len(content), 10) + self.assertEquals(len(content), 11) # checking the MANIFEST manifest = open(join(self.tmp_dir, 'MANIFEST')).read() -- cgit v1.2.1 From 9f19d45baca8e8371e5706a41dfe3d6352f7a2dc Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 21 Feb 2009 20:27:01 +0000 Subject: Issue #5341: Fix a variety of spelling errors. --- tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 55d2d5df..e481e8de 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -57,7 +57,7 @@ class CoreTestCase(unittest.TestCase): def test_run_setup_uses_current_dir(self): # This tests that the setup script is run with the current directory - # as it's own current directory; this was temporarily broken by a + # as its own current directory; this was temporarily broken by a # previous patch when TESTFN did not use the current directory. sys.stdout = StringIO.StringIO() cwd = os.getcwd() -- cgit v1.2.1 From 763718d5ed62b5076d9c022846e703eafa932a2f Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 21 Feb 2009 20:59:32 +0000 Subject: Merged revisions 69846 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69846 | mark.dickinson | 2009-02-21 20:27:01 +0000 (Sat, 21 Feb 2009) | 2 lines Issue #5341: Fix a variety of spelling errors. ........ --- tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 170d7675..7f021dcb 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -57,7 +57,7 @@ class CoreTestCase(unittest.TestCase): def test_run_setup_uses_current_dir(self): # This tests that the setup script is run with the current directory - # as it's own current directory; this was temporarily broken by a + # as its own current directory; this was temporarily broken by a # previous patch when TESTFN did not use the current directory. sys.stdout = io.StringIO() cwd = os.getcwd() -- cgit v1.2.1 From c0250c3b53f9dc239e467e52403e1dd464b59130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 22 Feb 2009 19:58:12 +0000 Subject: moved distutils.text_file tests into a real unittest class --- tests/test_text_file.py | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ text_file.py | 77 ------------------------------------------- 2 files changed, 87 insertions(+), 77 deletions(-) create mode 100644 tests/test_text_file.py diff --git a/tests/test_text_file.py b/tests/test_text_file.py new file mode 100644 index 00000000..8f4f7e83 --- /dev/null +++ b/tests/test_text_file.py @@ -0,0 +1,87 @@ +"""Tests for distutils.text_file.""" +import os +import unittest +from distutils.text_file import TextFile +from distutils.tests import support + +TEST_DATA = """# test file + +line 3 \\ +# intervening comment + continues on next line +""" + +class TextFileTestCase(support.TempdirManager, unittest.TestCase): + + def test_class(self): + # old tests moved from text_file.__main__ + # so they are really called by the buildbots + + # result 1: no fancy options + result1 = map(lambda x: x + "\n", + TEST_DATA.split("\n")[0:-1]) + + # result 2: just strip comments + result2 = ["\n", + "line 3 \\\n", + " continues on next line\n"] + + # result 3: just strip blank lines + result3 = ["# test file\n", + "line 3 \\\n", + "# intervening comment\n", + " continues on next line\n"] + + # result 4: default, strip comments, blank lines, + # and trailing whitespace + result4 = ["line 3 \\", + " continues on next line"] + + # result 5: strip comments and blanks, plus join lines (but don't + # "collapse" joined lines + result5 = ["line 3 continues on next line"] + + # result 6: strip comments and blanks, plus join lines (and + # "collapse" joined lines + result6 = ["line 3 continues on next line"] + + def test_input(count, description, file, expected_result): + result = file.readlines() + self.assertEquals(result, expected_result) + + tmpdir = self.mkdtemp() + filename = os.path.join(tmpdir, "test.txt") + out_file = open(filename, "w") + try: + out_file.write(TEST_DATA) + finally: + out_file.close() + + in_file = TextFile (filename, strip_comments=0, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + test_input (1, "no processing", in_file, result1) + + in_file = TextFile (filename, strip_comments=1, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + test_input (2, "strip comments", in_file, result2) + + in_file = TextFile (filename, strip_comments=0, skip_blanks=1, + lstrip_ws=0, rstrip_ws=0) + test_input (3, "strip blanks", in_file, result3) + + in_file = TextFile (filename) + test_input (4, "default processing", in_file, result4) + + in_file = TextFile (filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1) + test_input (5, "join lines without collapsing", in_file, result5) + + in_file = TextFile (filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1, collapse_join=1) + test_input (6, "join lines with collapsing", in_file, result6) + +def test_suite(): + return unittest.makeSuite(TextFileTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/text_file.py b/text_file.py index ff2878de..931f0bac 100644 --- a/text_file.py +++ b/text_file.py @@ -303,80 +303,3 @@ class TextFile: a parser with line-at-a-time lookahead.""" self.linebuf.append (line) - - -if __name__ == "__main__": - test_data = """# test file - -line 3 \\ -# intervening comment - continues on next line -""" - # result 1: no fancy options - result1 = map (lambda x: x + "\n", string.split (test_data, "\n")[0:-1]) - - # result 2: just strip comments - result2 = ["\n", - "line 3 \\\n", - " continues on next line\n"] - - # result 3: just strip blank lines - result3 = ["# test file\n", - "line 3 \\\n", - "# intervening comment\n", - " continues on next line\n"] - - # result 4: default, strip comments, blank lines, and trailing whitespace - result4 = ["line 3 \\", - " continues on next line"] - - # result 5: strip comments and blanks, plus join lines (but don't - # "collapse" joined lines - result5 = ["line 3 continues on next line"] - - # result 6: strip comments and blanks, plus join lines (and - # "collapse" joined lines - result6 = ["line 3 continues on next line"] - - def test_input (count, description, file, expected_result): - result = file.readlines () - # result = string.join (result, '') - if result == expected_result: - print "ok %d (%s)" % (count, description) - else: - print "not ok %d (%s):" % (count, description) - print "** expected:" - print expected_result - print "** received:" - print result - - - filename = "test.txt" - out_file = open (filename, "w") - out_file.write (test_data) - out_file.close () - - in_file = TextFile (filename, strip_comments=0, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input (1, "no processing", in_file, result1) - - in_file = TextFile (filename, strip_comments=1, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input (2, "strip comments", in_file, result2) - - in_file = TextFile (filename, strip_comments=0, skip_blanks=1, - lstrip_ws=0, rstrip_ws=0) - test_input (3, "strip blanks", in_file, result3) - - in_file = TextFile (filename) - test_input (4, "default processing", in_file, result4) - - in_file = TextFile (filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1) - test_input (5, "join lines without collapsing", in_file, result5) - - in_file = TextFile (filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1, collapse_join=1) - test_input (6, "join lines with collapsing", in_file, result6) - - os.remove (filename) -- cgit v1.2.1 From 91c68e1501720ba1fb7c6533c469a0972be2ebed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 22 Feb 2009 20:05:16 +0000 Subject: Merged revisions 69874 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69874 | tarek.ziade | 2009-02-22 20:58:12 +0100 (Sun, 22 Feb 2009) | 1 line moved distutils.text_file tests into a real unittest class ........ --- tests/test_text_file.py | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ text_file.py | 77 ------------------------------------------- 2 files changed, 88 insertions(+), 77 deletions(-) create mode 100644 tests/test_text_file.py diff --git a/tests/test_text_file.py b/tests/test_text_file.py new file mode 100644 index 00000000..00f083a1 --- /dev/null +++ b/tests/test_text_file.py @@ -0,0 +1,88 @@ +"""Tests for distutils.text_file.""" +import os +import unittest +from distutils.text_file import TextFile +from distutils.tests import support + +TEST_DATA = """# test file + +line 3 \\ +# intervening comment + continues on next line +""" + +class TextFileTestCase(support.TempdirManager, unittest.TestCase): + + def test_class(self): + # old tests moved from text_file.__main__ + # so they are really called by the buildbots + + # result 1: no fancy options + result1 = ['# test file\n', '\n', 'line 3 \\\n', + '# intervening comment\n', + ' continues on next line\n'] + + # result 2: just strip comments + result2 = ["\n", + "line 3 \\\n", + " continues on next line\n"] + + # result 3: just strip blank lines + result3 = ["# test file\n", + "line 3 \\\n", + "# intervening comment\n", + " continues on next line\n"] + + # result 4: default, strip comments, blank lines, + # and trailing whitespace + result4 = ["line 3 \\", + " continues on next line"] + + # result 5: strip comments and blanks, plus join lines (but don't + # "collapse" joined lines + result5 = ["line 3 continues on next line"] + + # result 6: strip comments and blanks, plus join lines (and + # "collapse" joined lines + result6 = ["line 3 continues on next line"] + + def test_input(count, description, file, expected_result): + result = file.readlines() + self.assertEquals(result, expected_result) + + tmpdir = self.mkdtemp() + filename = os.path.join(tmpdir, "test.txt") + out_file = open(filename, "w") + try: + out_file.write(TEST_DATA) + finally: + out_file.close() + + in_file = TextFile (filename, strip_comments=0, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + test_input (1, "no processing", in_file, result1) + + in_file = TextFile (filename, strip_comments=1, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + test_input (2, "strip comments", in_file, result2) + + in_file = TextFile (filename, strip_comments=0, skip_blanks=1, + lstrip_ws=0, rstrip_ws=0) + test_input (3, "strip blanks", in_file, result3) + + in_file = TextFile (filename) + test_input (4, "default processing", in_file, result4) + + in_file = TextFile (filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1) + test_input (5, "join lines without collapsing", in_file, result5) + + in_file = TextFile (filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1, collapse_join=1) + test_input (6, "join lines with collapsing", in_file, result6) + +def test_suite(): + return unittest.makeSuite(TextFileTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/text_file.py b/text_file.py index 266466c1..97459fbf 100644 --- a/text_file.py +++ b/text_file.py @@ -282,80 +282,3 @@ class TextFile: checked by future 'readline()' calls. Handy for implementing a parser with line-at-a-time lookahead.""" self.linebuf.append(line) - - -if __name__ == "__main__": - test_data = """# test file - -line 3 \\ -# intervening comment - continues on next line -""" - # result 1: no fancy options - result1 = [x + "\n" for x in test_data.split("\n")[:-1]] - - # result 2: just strip comments - result2 = ["\n", - "line 3 \\\n", - " continues on next line\n"] - - # result 3: just strip blank lines - result3 = ["# test file\n", - "line 3 \\\n", - "# intervening comment\n", - " continues on next line\n"] - - # result 4: default, strip comments, blank lines, and trailing whitespace - result4 = ["line 3 \\", - " continues on next line"] - - # result 5: strip comments and blanks, plus join lines (but don't - # "collapse" joined lines - result5 = ["line 3 continues on next line"] - - # result 6: strip comments and blanks, plus join lines (and - # "collapse" joined lines - result6 = ["line 3 continues on next line"] - - def test_input(count, description, file, expected_result): - result = file.readlines() - if result == expected_result: - print("ok %d (%s)" % (count, description)) - else: - print("not ok %d (%s):" % (count, description)) - print("** expected:") - print(expected_result) - print("** received:") - print(result) - - - filename = "test.txt" - out_file = open(filename, "w") - out_file.write(test_data) - out_file.close() - - in_file = TextFile(filename, strip_comments=0, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input(1, "no processing", in_file, result1) - - in_file = TextFile(filename, strip_comments=1, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input(2, "strip comments", in_file, result2) - - in_file = TextFile(filename, strip_comments=0, skip_blanks=1, - lstrip_ws=0, rstrip_ws=0) - test_input(3, "strip blanks", in_file, result3) - - in_file = TextFile(filename) - test_input(4, "default processing", in_file, result4) - - in_file = TextFile(filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1) - test_input(5, "join lines without collapsing", in_file, result5) - - in_file = TextFile(filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1, collapse_join=1) - test_input(6, "join lines with collapsing", in_file, result6) - - del in_file - os.remove(filename) -- cgit v1.2.1 From df1fb3abf4e8b2bcaaedd4057dd3a4ff2d4231fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 22 Feb 2009 20:11:46 +0000 Subject: removing map and lambda usage, so the test is similar to py3k's branch one --- tests/test_text_file.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_text_file.py b/tests/test_text_file.py index 8f4f7e83..00f083a1 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -18,8 +18,9 @@ class TextFileTestCase(support.TempdirManager, unittest.TestCase): # so they are really called by the buildbots # result 1: no fancy options - result1 = map(lambda x: x + "\n", - TEST_DATA.split("\n")[0:-1]) + result1 = ['# test file\n', '\n', 'line 3 \\\n', + '# intervening comment\n', + ' continues on next line\n'] # result 2: just strip comments result2 = ["\n", -- cgit v1.2.1 From 305462a33f5cb3a50f50fb3c913b59e8e34eb4e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 22 Feb 2009 20:15:41 +0000 Subject: Removing unused __main__ sections --- cmd.py | 4 ---- dist.py | 5 ----- 2 files changed, 9 deletions(-) diff --git a/cmd.py b/cmd.py index 1351f445..012fca15 100644 --- a/cmd.py +++ b/cmd.py @@ -474,7 +474,3 @@ class install_misc (Command): def get_outputs (self): return self.outfiles - - -if __name__ == "__main__": - print "ok" diff --git a/dist.py b/dist.py index 3dd776cc..2d57ad09 100644 --- a/dist.py +++ b/dist.py @@ -1224,8 +1224,3 @@ def fix_help_options (options): for help_tuple in options: new_options.append(help_tuple[0:3]) return new_options - - -if __name__ == "__main__": - dist = Distribution() - print "ok" -- cgit v1.2.1 From ead790166c33119b238f8d4b9356390dd8c2a9bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 22 Feb 2009 20:20:59 +0000 Subject: Merged revisions 69881 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69881 | tarek.ziade | 2009-02-22 21:15:41 +0100 (Sun, 22 Feb 2009) | 1 line Removing unused __main__ sections ........ --- cmd.py | 4 ---- dist.py | 5 ----- 2 files changed, 9 deletions(-) diff --git a/cmd.py b/cmd.py index 295c9140..800425d4 100644 --- a/cmd.py +++ b/cmd.py @@ -436,7 +436,3 @@ class install_misc(Command): def get_outputs (self): return self.outfiles - - -if __name__ == "__main__": - print("ok") diff --git a/dist.py b/dist.py index 6c4b4afb..7c30e885 100644 --- a/dist.py +++ b/dist.py @@ -1180,8 +1180,3 @@ def fix_help_options (options): for help_tuple in options: new_options.append(help_tuple[0:3]) return new_options - - -if __name__ == "__main__": - dist = Distribution() - print("ok") -- cgit v1.2.1 From e092af3741d37b82a06bcefab24e76dd23baab8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 23 Feb 2009 12:41:29 +0000 Subject: more test coverage --- tests/test_bdist_dumb.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_version.py | 70 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 tests/test_bdist_dumb.py create mode 100644 tests/test_version.py diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py new file mode 100644 index 00000000..2863b620 --- /dev/null +++ b/tests/test_bdist_dumb.py @@ -0,0 +1,80 @@ +"""Tests for distutils.command.bdist_dumb.""" + +import unittest +import sys +import os + +from distutils.core import Distribution +from distutils.command.bdist_dumb import bdist_dumb +from distutils.tests import support + +SETUP_PY = """\ +from distutils.core import setup +import foo + +setup(name='foo', version='0.1', py_modules=['foo'], + url='xxx', author='xxx', author_email='xxx') + +""" + +class BuildDumbTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def setUp(self): + support.TempdirManager.setUp(self) + support.LoggingSilencer.setUp(self) + self.old_location = os.getcwd() + self.old_sys_argv = sys.argv[:] + + def tearDown(self): + os.chdir(self.old_location) + sys.argv = self.old_sys_argv[:] + support.LoggingSilencer.tearDown(self) + support.TempdirManager.tearDown(self) + + def test_simple_built(self): + + # let's create a simple package + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_dumb(dist) + + # so the output is the same no matter + # what is the platform + cmd.format = 'zip' + + cmd.ensure_finalized() + cmd.run() + + # see what we have + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + base = "%s.%s" % (dist.get_fullname(), cmd.plat_name) + if os.name == 'os2': + base = base.replace(':', '-') + + wanted = ['%s.zip' % base] + self.assertEquals(dist_created, wanted) + + # now let's check what we have in the zip file + # XXX to be done + +def test_suite(): + return unittest.makeSuite(BuildDumbTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 00000000..747db948 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,70 @@ +"""Tests for distutils.version.""" +import unittest +from distutils.version import LooseVersion +from distutils.version import StrictVersion + +class VersionTestCase(unittest.TestCase): + + def test_prerelease(self): + version = StrictVersion('1.2.3a1') + self.assertEquals(version.version, (1, 2, 3)) + self.assertEquals(version.prerelease, ('a', 1)) + self.assertEquals(str(version), '1.2.3a1') + + version = StrictVersion('1.2.0') + self.assertEquals(str(version), '1.2') + + def test_cmp_strict(self): + versions = (('1.5.1', '1.5.2b2', -1), + ('161', '3.10a', ValueError), + ('8.02', '8.02', 0), + ('3.4j', '1996.07.12', ValueError), + ('3.2.pl0', '3.1.1.6', ValueError), + ('2g6', '11g', ValueError), + ('0.9', '2.2', -1), + ('1.2.1', '1.2', 1), + ('1.1', '1.2.2', -1), + ('1.2', '1.1', 1), + ('1.2.1', '1.2.2', -1), + ('1.2.2', '1.2', 1), + ('1.2', '1.2.2', -1), + ('0.4.0', '0.4', 0), + ('1.13++', '5.5.kw', ValueError)) + + for v1, v2, wanted in versions: + try: + res = StrictVersion(v1).__cmp__(StrictVersion(v2)) + except ValueError: + if wanted is ValueError: + continue + else: + raise AssertionError(("cmp(%s, %s) " + "shouldn't raise ValueError") + % (v1, v2)) + self.assertEquals(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + + + def test_cmp(self): + versions = (('1.5.1', '1.5.2b2', -1), + ('161', '3.10a', 1), + ('8.02', '8.02', 0), + ('3.4j', '1996.07.12', -1), + ('3.2.pl0', '3.1.1.6', 1), + ('2g6', '11g', -1), + ('0.960923', '2.2beta29', -1), + ('1.13++', '5.5.kw', -1)) + + + for v1, v2, wanted in versions: + res = LooseVersion(v1).__cmp__(LooseVersion(v2)) + self.assertEquals(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + +def test_suite(): + return unittest.makeSuite(VersionTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 3fa4cb44a2c238a7fe8569e149332937dc3e39ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 23 Feb 2009 12:47:55 +0000 Subject: Merged revisions 69902 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69902 | tarek.ziade | 2009-02-23 13:41:29 +0100 (Mon, 23 Feb 2009) | 1 line more test coverage ........ --- tests/test_bdist_dumb.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_version.py | 70 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 tests/test_bdist_dumb.py create mode 100644 tests/test_version.py diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py new file mode 100644 index 00000000..2863b620 --- /dev/null +++ b/tests/test_bdist_dumb.py @@ -0,0 +1,80 @@ +"""Tests for distutils.command.bdist_dumb.""" + +import unittest +import sys +import os + +from distutils.core import Distribution +from distutils.command.bdist_dumb import bdist_dumb +from distutils.tests import support + +SETUP_PY = """\ +from distutils.core import setup +import foo + +setup(name='foo', version='0.1', py_modules=['foo'], + url='xxx', author='xxx', author_email='xxx') + +""" + +class BuildDumbTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def setUp(self): + support.TempdirManager.setUp(self) + support.LoggingSilencer.setUp(self) + self.old_location = os.getcwd() + self.old_sys_argv = sys.argv[:] + + def tearDown(self): + os.chdir(self.old_location) + sys.argv = self.old_sys_argv[:] + support.LoggingSilencer.tearDown(self) + support.TempdirManager.tearDown(self) + + def test_simple_built(self): + + # let's create a simple package + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_dumb(dist) + + # so the output is the same no matter + # what is the platform + cmd.format = 'zip' + + cmd.ensure_finalized() + cmd.run() + + # see what we have + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + base = "%s.%s" % (dist.get_fullname(), cmd.plat_name) + if os.name == 'os2': + base = base.replace(':', '-') + + wanted = ['%s.zip' % base] + self.assertEquals(dist_created, wanted) + + # now let's check what we have in the zip file + # XXX to be done + +def test_suite(): + return unittest.makeSuite(BuildDumbTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 00000000..fa214330 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,70 @@ +"""Tests for distutils.version.""" +import unittest +from distutils.version import LooseVersion +from distutils.version import StrictVersion + +class VersionTestCase(unittest.TestCase): + + def test_prerelease(self): + version = StrictVersion('1.2.3a1') + self.assertEquals(version.version, (1, 2, 3)) + self.assertEquals(version.prerelease, ('a', 1)) + self.assertEquals(str(version), '1.2.3a1') + + version = StrictVersion('1.2.0') + self.assertEquals(str(version), '1.2') + + def test_cmp_strict(self): + versions = (('1.5.1', '1.5.2b2', -1), + ('161', '3.10a', ValueError), + ('8.02', '8.02', 0), + ('3.4j', '1996.07.12', ValueError), + ('3.2.pl0', '3.1.1.6', ValueError), + ('2g6', '11g', ValueError), + ('0.9', '2.2', -1), + ('1.2.1', '1.2', 1), + ('1.1', '1.2.2', -1), + ('1.2', '1.1', 1), + ('1.2.1', '1.2.2', -1), + ('1.2.2', '1.2', 1), + ('1.2', '1.2.2', -1), + ('0.4.0', '0.4', 0), + ('1.13++', '5.5.kw', ValueError)) + + for v1, v2, wanted in versions: + try: + res = StrictVersion(v1)._cmp(StrictVersion(v2)) + except ValueError: + if wanted is ValueError: + continue + else: + raise AssertionError(("cmp(%s, %s) " + "shouldn't raise ValueError") + % (v1, v2)) + self.assertEquals(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + + + def test_cmp(self): + versions = (('1.5.1', '1.5.2b2', -1), + ('161', '3.10a', 1), + ('8.02', '8.02', 0), + ('3.4j', '1996.07.12', -1), + ('3.2.pl0', '3.1.1.6', 1), + ('2g6', '11g', -1), + ('0.960923', '2.2beta29', -1), + ('1.13++', '5.5.kw', -1)) + + + for v1, v2, wanted in versions: + res = LooseVersion(v1)._cmp(LooseVersion(v2)) + self.assertEquals(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + +def test_suite(): + return unittest.makeSuite(VersionTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 9841cdd9d6e09f214265adaefc1b5b43c25ade1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 25 Feb 2009 22:29:27 +0000 Subject: Fixed #5316 : test failure in test_site --- tests/test_config.py | 4 ++-- tests/test_sdist.py | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index d8127692..a18f4535 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -51,7 +51,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): """Patches the environment.""" - support.TempdirManager.setUp(self) + super(PyPIRCCommandTestCase, self).setUp() if os.environ.has_key('HOME'): self._old_home = os.environ['HOME'] @@ -79,7 +79,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): else: os.environ['HOME'] = self._old_home set_threshold(self.old_threshold) - support.TempdirManager.tearDown(self) + super(PyPIRCCommandTestCase, self).tearDown() def test_server_registration(self): # This test makes sure PyPIRCCommand knows how to: diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 9c579b40..15a8c808 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -37,10 +37,9 @@ somecode%(sep)sdoc.txt class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): def setUp(self): - support.LoggingSilencer.setUp(self) # PyPIRCCommandTestCase creates a temp dir already # and put it in self.tmp_dir - PyPIRCCommandTestCase.setUp(self) + super(sdistTestCase, self).setUp() # setting up an environment self.old_path = os.getcwd() os.mkdir(join(self.tmp_dir, 'somecode')) @@ -54,8 +53,7 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): def tearDown(self): # back to normal os.chdir(self.old_path) - PyPIRCCommandTestCase.tearDown(self) - support.LoggingSilencer.tearDown(self) + super(sdistTestCase, self).tearDown() def get_cmd(self, metadata=None): """Returns a cmd""" -- cgit v1.2.1 From 706b92c2ebd63afe86a4d0e31d86011dcaf017f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 25 Feb 2009 22:31:38 +0000 Subject: Merged revisions 69976 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69976 | tarek.ziade | 2009-02-25 23:29:27 +0100 (Wed, 25 Feb 2009) | 1 line Fixed #5316 : test failure in test_site ........ --- tests/test_config.py | 4 ++-- tests/test_sdist.py | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 0df6af84..09abfcd8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -50,7 +50,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): """Patches the environment.""" - support.TempdirManager.setUp(self) + super(PyPIRCCommandTestCase, self).setUp() if 'HOME' in os.environ: self._old_home = os.environ['HOME'] @@ -78,7 +78,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): else: os.environ['HOME'] = self._old_home set_threshold(self.old_threshold) - support.TempdirManager.tearDown(self) + super(PyPIRCCommandTestCase, self).tearDown() def test_server_registration(self): # This test makes sure PyPIRCCommand knows how to: diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 9c579b40..15a8c808 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -37,10 +37,9 @@ somecode%(sep)sdoc.txt class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): def setUp(self): - support.LoggingSilencer.setUp(self) # PyPIRCCommandTestCase creates a temp dir already # and put it in self.tmp_dir - PyPIRCCommandTestCase.setUp(self) + super(sdistTestCase, self).setUp() # setting up an environment self.old_path = os.getcwd() os.mkdir(join(self.tmp_dir, 'somecode')) @@ -54,8 +53,7 @@ class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): def tearDown(self): # back to normal os.chdir(self.old_path) - PyPIRCCommandTestCase.tearDown(self) - support.LoggingSilencer.tearDown(self) + super(sdistTestCase, self).tearDown() def get_cmd(self, metadata=None): """Returns a cmd""" -- cgit v1.2.1 From 9daeb929f037438cec1b32dd526a5de62581b769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 26 Feb 2009 23:44:00 +0000 Subject: removed unused import --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 2ed3ef65..ac0a0670 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -8,7 +8,7 @@ __revision__ = "$Id$" import sys, os, string, re from types import * -from site import USER_BASE, USER_SITE +from site import USER_BASE from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version -- cgit v1.2.1 From 73ac2e14e27f78bd054a1192099437777bc87c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 26 Feb 2009 23:47:00 +0000 Subject: Merged revisions 70003 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70003 | tarek.ziade | 2009-02-27 00:44:00 +0100 (Fri, 27 Feb 2009) | 1 line removed unused import ........ --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index b12da63f..1d5a75d2 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -7,7 +7,7 @@ extensions ASAP).""" __revision__ = "$Id$" import sys, os, re -from site import USER_BASE, USER_SITE +from site import USER_BASE from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version -- cgit v1.2.1 From 8ac6d0fd24f52432843766fc24fbe20fb618ebb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 27 Feb 2009 12:53:34 +0000 Subject: Issue #5052: make Distutils compatible with 2.3 again. --- README | 2 ++ command/build_ext.py | 18 ++++++++--- command/install.py | 83 +++++++++++++++++++++++++++++++------------------ command/upload.py | 7 ++++- tests/test_build_ext.py | 47 ++++++++++++++++++++++++++-- tests/test_install.py | 64 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 183 insertions(+), 38 deletions(-) diff --git a/README b/README index 23f48850..408a203b 100644 --- a/README +++ b/README @@ -8,4 +8,6 @@ The Distutils-SIG web page is also a good starting point: http://www.python.org/sigs/distutils-sig/ +WARNING : Distutils must remain compatible with 2.3 + $Id$ diff --git a/command/build_ext.py b/command/build_ext.py index ac0a0670..125fa7f5 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -8,7 +8,6 @@ __revision__ = "$Id$" import sys, os, string, re from types import * -from site import USER_BASE from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version @@ -17,6 +16,14 @@ from distutils.extension import Extension from distutils.util import get_platform from distutils import log +# this keeps compatibility from 2.3 to 2.5 +if sys.version < "2.6": + USER_BASE = None + HAS_USER_SITE = False +else: + from site import USER_BASE + HAS_USER_SITE = True + if os.name == 'nt': from distutils.msvccompiler import get_build_version MSVC_VERSION = int(get_build_version()) @@ -92,11 +99,14 @@ class build_ext (Command): "list of SWIG command line options"), ('swig=', None, "path to the SWIG executable"), - ('user', None, - "add user include, library and rpath"), ] - boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp'] + + if HAS_USER_SITE: + user_options.append(('user', None, + "add user include, library and rpath")) + boolean_options.append('user') help_options = [ ('help-compiler', None, diff --git a/command/install.py b/command/install.py index adc9daf1..4fd66cbf 100644 --- a/command/install.py +++ b/command/install.py @@ -16,9 +16,16 @@ from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root from distutils.util import get_platform from distutils.errors import DistutilsOptionError -from site import USER_BASE -from site import USER_SITE +# this keeps compatibility from 2.3 to 2.5 +if sys.version < "2.6": + USER_BASE = None + USER_SITE = None + HAS_USER_SITE = False +else: + from site import USER_BASE + from site import USER_SITE + HAS_USER_SITE = True if sys.version < "2.2": WINDOWS_SCHEME = { @@ -52,21 +59,7 @@ INSTALL_SCHEMES = { 'scripts': '$base/bin', 'data' : '$base', }, - 'unix_user': { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/include/python$py_version_short/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - }, 'nt': WINDOWS_SCHEME, - 'nt_user': { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', - 'scripts': '$userbase/Scripts', - 'data' : '$userbase', - }, 'mac': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', @@ -74,13 +67,7 @@ INSTALL_SCHEMES = { 'scripts': '$base/Scripts', 'data' : '$base', }, - 'mac_user': { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/$py_version_short/include/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - }, + 'os2': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', @@ -88,14 +75,41 @@ INSTALL_SCHEMES = { 'scripts': '$base/Scripts', 'data' : '$base', }, - 'os2_home': { + } + +# user site schemes +if HAS_USER_SITE: + INSTALL_SCHEMES['nt_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', + 'scripts': '$userbase/Scripts', + 'data' : '$userbase', + } + + INSTALL_SCHEMES['unix_user'] = { 'purelib': '$usersite', 'platlib': '$usersite', 'headers': '$userbase/include/python$py_version_short/$dist_name', 'scripts': '$userbase/bin', 'data' : '$userbase', - }, - } + } + + INSTALL_SCHEMES['mac_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/$py_version_short/include/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + } + + INSTALL_SCHEMES['os2_home'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + } # The keys to an installation scheme; if any new types of files are to be # installed, be sure to add an entry to every installation scheme above, @@ -115,8 +129,6 @@ class install (Command): "(Unix only) prefix for platform-specific files"), ('home=', None, "(Unix only) home directory to install under"), - ('user', None, - "install in user site-package '%s'" % USER_SITE), # Or, just set the base director(y|ies) ('install-base=', None, @@ -168,7 +180,13 @@ class install (Command): "filename in which to record list of installed files"), ] - boolean_options = ['compile', 'force', 'skip-build', 'user'] + boolean_options = ['compile', 'force', 'skip-build'] + + if HAS_USER_SITE: + user_options.append(('user', None, + "install in user site-package '%s'" % USER_SITE)) + boolean_options.append('user') + negative_opt = {'no-compile' : 'compile'} @@ -320,9 +338,12 @@ class install (Command): 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, - 'userbase': self.install_userbase, - 'usersite': self.install_usersite, } + + if HAS_USER_SITE: + self.config_vars['userbase'] = self.install_userbase + self.config_vars['usersite'] = self.install_usersite + self.expand_basedirs() self.dump_dirs("post-expand_basedirs()") diff --git a/command/upload.py b/command/upload.py index e30347e5..df82e4ca 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ from distutils.errors import * from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log -from hashlib import md5 +import sys import os import socket import platform @@ -16,6 +16,11 @@ import urlparse import cStringIO as StringIO from ConfigParser import ConfigParser +# this keeps compatibility for 2.3 and 2.4 +if sys.version < "2.5": + from md5 import md5 +else: + from hashlib import md5 class upload(PyPIRCCommand): diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index a38558b0..ff60c12b 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -24,11 +24,17 @@ class BuildExtTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path - support.TempdirManager.setUp(self) + super(BuildExtTestCase, self).setUp() self.tmp_dir = self.mkdtemp() self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) + if sys.version > "2.6": + import site + self.old_user_base = site.USER_BASE + site.USER_BASE = self.mkdtemp() + from distutils.command import build_ext + build_ext.USER_BASE = site.USER_BASE def test_build_ext(self): global ALREADY_TESTED @@ -76,7 +82,13 @@ class BuildExtTestCase(support.TempdirManager, unittest.TestCase): # Get everything back to normal test_support.unload('xx') sys.path = self.sys_path - support.TempdirManager.tearDown(self) + if sys.version > "2.6": + import site + site.USER_BASE = self.old_user_base + from distutils.command import build_ext + build_ext.USER_BASE = self.old_user_base + + super(BuildExtTestCase, self).tearDown() def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) @@ -99,6 +111,37 @@ class BuildExtTestCase(support.TempdirManager, unittest.TestCase): # make sur we get some lobrary dirs under solaris self.assert_(len(cmd.library_dirs) > 0) + def test_user_site(self): + # site.USER_SITE was introduced in 2.6 + if sys.version < '2.6': + return + + import site + dist = Distribution({'name': 'xx'}) + cmd = build_ext(dist) + + # making sure the suer option is there + options = [name for name, short, lable in + cmd.user_options] + self.assert_('user' in options) + + # setting a value + cmd.user = 1 + + # setting user based lib and include + lib = os.path.join(site.USER_BASE, 'lib') + incl = os.path.join(site.USER_BASE, 'include') + os.mkdir(lib) + os.mkdir(incl) + + # let's run finalize + cmd.ensure_finalized() + + # see if include_dirs and library_dirs + # were set + self.assert_(lib in cmd.library_dirs) + self.assert_(incl in cmd.include_dirs) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): diff --git a/tests/test_install.py b/tests/test_install.py index c834b91b..9ceccf68 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -1,9 +1,14 @@ """Tests for distutils.command.install.""" import os +import os.path +import sys import unittest +import site from distutils.command.install import install +from distutils.command import install as install_module +from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution from distutils.tests import support @@ -47,6 +52,65 @@ class InstallTestCase(support.TempdirManager, unittest.TestCase): check_path(cmd.install_scripts, os.path.join(destination, "bin")) check_path(cmd.install_data, destination) + def test_user_site(self): + # site.USER_SITE was introduced in 2.6 + if sys.version < '2.6': + return + + # preparing the environement for the test + self.old_user_base = site.USER_BASE + self.old_user_site = site.USER_SITE + self.tmpdir = self.mkdtemp() + self.user_base = os.path.join(self.tmpdir, 'B') + self.user_site = os.path.join(self.tmpdir, 'S') + site.USER_BASE = self.user_base + site.USER_SITE = self.user_site + install_module.USER_BASE = self.user_base + install_module.USER_SITE = self.user_site + + def _expanduser(path): + return self.tmpdir + self.old_expand = os.path.expanduser + os.path.expanduser = _expanduser + + try: + # this is the actual test + self._test_user_site() + finally: + site.USER_BASE = self.old_user_base + site.USER_SITE = self.old_user_site + install_module.USER_BASE = self.old_user_base + install_module.USER_SITE = self.old_user_site + os.path.expanduser = self.old_expand + + def _test_user_site(self): + for key in ('nt_user', 'unix_user', 'os2_home'): + self.assert_(key in INSTALL_SCHEMES) + + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # making sure the user option is there + options = [name for name, short, lable in + cmd.user_options] + self.assert_('user' in options) + + # setting a value + cmd.user = 1 + + # user base and site shouldn't be created yet + self.assert_(not os.path.exists(self.user_base)) + self.assert_(not os.path.exists(self.user_site)) + + # let's run finalize + cmd.ensure_finalized() + + # now they should + self.assert_(os.path.exists(self.user_base)) + self.assert_(os.path.exists(self.user_site)) + + self.assert_('userbase' in cmd.config_vars) + self.assert_('usersite' in cmd.config_vars) def test_suite(): return unittest.makeSuite(InstallTestCase) -- cgit v1.2.1 From 42e1c973f8cb4baba16a761b1aa6263ee5f47278 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 27 Feb 2009 12:58:56 +0000 Subject: Merged revisions 70017 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70017 | tarek.ziade | 2009-02-27 13:53:34 +0100 (Fri, 27 Feb 2009) | 1 line Issue #5052: make Distutils compatible with 2.3 again. ........ --- README | 2 ++ command/build_ext.py | 18 ++++++++--- command/install.py | 83 +++++++++++++++++++++++++++++++------------------ command/upload.py | 8 ++++- tests/test_build_ext.py | 46 +++++++++++++++++++++++++-- tests/test_install.py | 64 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 183 insertions(+), 38 deletions(-) diff --git a/README b/README index 23f48850..408a203b 100644 --- a/README +++ b/README @@ -8,4 +8,6 @@ The Distutils-SIG web page is also a good starting point: http://www.python.org/sigs/distutils-sig/ +WARNING : Distutils must remain compatible with 2.3 + $Id$ diff --git a/command/build_ext.py b/command/build_ext.py index 1d5a75d2..1ed69f33 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -7,7 +7,6 @@ extensions ASAP).""" __revision__ = "$Id$" import sys, os, re -from site import USER_BASE from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version @@ -16,6 +15,14 @@ from distutils.extension import Extension from distutils.util import get_platform from distutils import log +# this keeps compatibility from 2.3 to 2.5 +if sys.version < "2.6": + USER_BASE = None + HAS_USER_SITE = False +else: + from site import USER_BASE + HAS_USER_SITE = True + if os.name == 'nt': from distutils.msvccompiler import get_build_version MSVC_VERSION = int(get_build_version()) @@ -91,11 +98,14 @@ class build_ext(Command): "list of SWIG command line options"), ('swig=', None, "path to the SWIG executable"), - ('user', None, - "add user include, library and rpath"), ] - boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp'] + + if HAS_USER_SITE: + user_options.append(('user', None, + "add user include, library and rpath")) + boolean_options.append('user') help_options = [ ('help-compiler', None, diff --git a/command/install.py b/command/install.py index b5c9554d..de811733 100644 --- a/command/install.py +++ b/command/install.py @@ -15,9 +15,16 @@ from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root from distutils.util import get_platform from distutils.errors import DistutilsOptionError -from site import USER_BASE -from site import USER_SITE +# this keeps compatibility from 2.3 to 2.5 +if sys.version < "2.6": + USER_BASE = None + USER_SITE = None + HAS_USER_SITE = False +else: + from site import USER_BASE + from site import USER_SITE + HAS_USER_SITE = True if sys.version < "2.2": WINDOWS_SCHEME = { @@ -51,21 +58,7 @@ INSTALL_SCHEMES = { 'scripts': '$base/bin', 'data' : '$base', }, - 'unix_user': { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/include/python$py_version_short/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - }, 'nt': WINDOWS_SCHEME, - 'nt_user': { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', - 'scripts': '$userbase/Scripts', - 'data' : '$userbase', - }, 'mac': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', @@ -73,13 +66,7 @@ INSTALL_SCHEMES = { 'scripts': '$base/Scripts', 'data' : '$base', }, - 'mac_user': { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/$py_version_short/include/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - }, + 'os2': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', @@ -87,14 +74,41 @@ INSTALL_SCHEMES = { 'scripts': '$base/Scripts', 'data' : '$base', }, - 'os2_home': { + } + +# user site schemes +if HAS_USER_SITE: + INSTALL_SCHEMES['nt_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', + 'scripts': '$userbase/Scripts', + 'data' : '$userbase', + } + + INSTALL_SCHEMES['unix_user'] = { 'purelib': '$usersite', 'platlib': '$usersite', 'headers': '$userbase/include/python$py_version_short/$dist_name', 'scripts': '$userbase/bin', 'data' : '$userbase', - }, - } + } + + INSTALL_SCHEMES['mac_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/$py_version_short/include/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + } + + INSTALL_SCHEMES['os2_home'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + } # The keys to an installation scheme; if any new types of files are to be # installed, be sure to add an entry to every installation scheme above, @@ -114,8 +128,6 @@ class install (Command): "(Unix only) prefix for platform-specific files"), ('home=', None, "(Unix only) home directory to install under"), - ('user', None, - "install in user site-package '%s'" % USER_SITE), # Or, just set the base director(y|ies) ('install-base=', None, @@ -167,7 +179,13 @@ class install (Command): "filename in which to record list of installed files"), ] - boolean_options = ['compile', 'force', 'skip-build', 'user'] + boolean_options = ['compile', 'force', 'skip-build'] + + if HAS_USER_SITE: + user_options.append(('user', None, + "install in user site-package '%s'" % USER_SITE)) + boolean_options.append('user') + negative_opt = {'no-compile' : 'compile'} @@ -319,9 +337,12 @@ class install (Command): 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, - 'userbase': self.install_userbase, - 'usersite': self.install_usersite, } + + if HAS_USER_SITE: + self.config_vars['userbase'] = self.install_userbase + self.config_vars['usersite'] = self.install_usersite + self.expand_basedirs() self.dump_dirs("post-expand_basedirs()") diff --git a/command/upload.py b/command/upload.py index 020e860a..eb2f5167 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ from distutils.errors import * from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log -from hashlib import md5 +import sys import os, io import socket import platform @@ -15,6 +15,12 @@ import http.client as httpclient import base64 import urllib.parse +# this keeps compatibility for 2.3 and 2.4 +if sys.version < "2.5": + from md5 import md5 +else: + from hashlib import md5 + class upload(PyPIRCCommand): description = "upload binary package to PyPI" diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 91b3de85..2e47953e 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -24,11 +24,17 @@ class BuildExtTestCase(TempdirManager, unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path - TempdirManager.setUp(self) + super(BuildExtTestCase, self).setUp() self.tmp_dir = self.mkdtemp() self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) + if sys.version > "2.6": + import site + self.old_user_base = site.USER_BASE + site.USER_BASE = self.mkdtemp() + from distutils.command import build_ext + build_ext.USER_BASE = site.USER_BASE def test_build_ext(self): global ALREADY_TESTED @@ -76,7 +82,12 @@ class BuildExtTestCase(TempdirManager, unittest.TestCase): # Get everything back to normal support.unload('xx') sys.path = self.sys_path - TempdirManager.tearDown(self) + if sys.version > "2.6": + import site + site.USER_BASE = self.old_user_base + from distutils.command import build_ext + build_ext.USER_BASE = self.old_user_base + super(BuildExtTestCase, self).tearDown() def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) @@ -99,6 +110,37 @@ class BuildExtTestCase(TempdirManager, unittest.TestCase): # make sur we get some lobrary dirs under solaris self.assert_(len(cmd.library_dirs) > 0) + def test_user_site(self): + # site.USER_SITE was introduced in 2.6 + if sys.version < '2.6': + return + + import site + dist = Distribution({'name': 'xx'}) + cmd = build_ext(dist) + + # making sure the suer option is there + options = [name for name, short, lable in + cmd.user_options] + self.assert_('user' in options) + + # setting a value + cmd.user = 1 + + # setting user based lib and include + lib = os.path.join(site.USER_BASE, 'lib') + incl = os.path.join(site.USER_BASE, 'include') + os.mkdir(lib) + os.mkdir(incl) + + # let's run finalize + cmd.ensure_finalized() + + # see if include_dirs and library_dirs + # were set + self.assert_(lib in cmd.library_dirs) + self.assert_(incl in cmd.include_dirs) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): diff --git a/tests/test_install.py b/tests/test_install.py index c834b91b..9ceccf68 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -1,9 +1,14 @@ """Tests for distutils.command.install.""" import os +import os.path +import sys import unittest +import site from distutils.command.install import install +from distutils.command import install as install_module +from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution from distutils.tests import support @@ -47,6 +52,65 @@ class InstallTestCase(support.TempdirManager, unittest.TestCase): check_path(cmd.install_scripts, os.path.join(destination, "bin")) check_path(cmd.install_data, destination) + def test_user_site(self): + # site.USER_SITE was introduced in 2.6 + if sys.version < '2.6': + return + + # preparing the environement for the test + self.old_user_base = site.USER_BASE + self.old_user_site = site.USER_SITE + self.tmpdir = self.mkdtemp() + self.user_base = os.path.join(self.tmpdir, 'B') + self.user_site = os.path.join(self.tmpdir, 'S') + site.USER_BASE = self.user_base + site.USER_SITE = self.user_site + install_module.USER_BASE = self.user_base + install_module.USER_SITE = self.user_site + + def _expanduser(path): + return self.tmpdir + self.old_expand = os.path.expanduser + os.path.expanduser = _expanduser + + try: + # this is the actual test + self._test_user_site() + finally: + site.USER_BASE = self.old_user_base + site.USER_SITE = self.old_user_site + install_module.USER_BASE = self.old_user_base + install_module.USER_SITE = self.old_user_site + os.path.expanduser = self.old_expand + + def _test_user_site(self): + for key in ('nt_user', 'unix_user', 'os2_home'): + self.assert_(key in INSTALL_SCHEMES) + + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # making sure the user option is there + options = [name for name, short, lable in + cmd.user_options] + self.assert_('user' in options) + + # setting a value + cmd.user = 1 + + # user base and site shouldn't be created yet + self.assert_(not os.path.exists(self.user_base)) + self.assert_(not os.path.exists(self.user_site)) + + # let's run finalize + cmd.ensure_finalized() + + # now they should + self.assert_(os.path.exists(self.user_base)) + self.assert_(os.path.exists(self.user_site)) + + self.assert_('userbase' in cmd.config_vars) + self.assert_('usersite' in cmd.config_vars) def test_suite(): return unittest.makeSuite(InstallTestCase) -- cgit v1.2.1 From 314bb85f3930ff28db2d3ba0e2802486c0738780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 28 Feb 2009 10:08:02 +0000 Subject: Issues #1533164 and #5378: Added quiet and force-optimize options to Distutils bdist_rpm command --- command/bdist_rpm.py | 36 ++++++++++-- tests/test_bdist_rpm.py | 149 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 tests/test_bdist_rpm.py diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index a48e0f18..34822ec7 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -123,10 +123,21 @@ class bdist_rpm (Command): # Allow a packager to explicitly force an architecture ('force-arch=', None, "Force an architecture onto the RPM build process"), - ] + + ('quiet', 'q', + "Run the INSTALL phase of RPM building in quiet mode"), + + # Forces the -O1 option when calling the install command, + # so the rpm contains all files needed for proper operation under + # SELinux. Some systems checks for this on build-time and will + # fail without this. + ('force-optimize', None, + "Forces the -O1 option when calling the install command"), + + ] boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', - 'no-autoreq'] + 'no-autoreq', 'quiet', 'force-optimize'] negative_opt = {'no-keep-temp': 'keep-temp', 'no-rpm-opt-flags': 'use-rpm-opt-flags', @@ -176,6 +187,8 @@ class bdist_rpm (Command): self.no_autoreq = 0 self.force_arch = None + self.quiet = 0 + self.force_optimize = 1 # initialize_options() @@ -322,6 +335,7 @@ class bdist_rpm (Command): if os.path.exists('/usr/bin/rpmbuild') or \ os.path.exists('/bin/rpmbuild'): rpm_cmd = ['rpmbuild'] + if self.source_only: # what kind of RPMs? rpm_cmd.append('-bs') elif self.binary_only: @@ -333,6 +347,10 @@ class bdist_rpm (Command): '_topdir %s' % os.path.abspath(self.rpm_base)]) if not self.keep_temp: rpm_cmd.append('--clean') + + if self.quiet: + rpm_cmd.append('--quiet') + rpm_cmd.append(spec_path) # Determine the binary rpm names that should be built out of this spec # file @@ -486,13 +504,19 @@ class bdist_rpm (Command): # that we open and interpolate into the spec file, but the defaults # are just text that we drop in as-is. Hmmm. + # forcing -O1 if force-optimize + if self.force_optimize: + optimize = ' -O1' + else: + optimize = '' + + install_cmd = ('%s install%s --root=$RPM_BUILD_ROOT ' + '--record=INSTALLED_FILES') % (def_setup_call, optimize) + script_options = [ ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), ('build', 'build_script', def_build), - ('install', 'install_script', - ("%s install " - "--root=$RPM_BUILD_ROOT " - "--record=INSTALLED_FILES") % def_setup_call), + ('install', 'install_script', install_cmd), ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), ('verifyscript', 'verify_script', None), ('pre', 'pre_install', None), diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py new file mode 100644 index 00000000..ff40628f --- /dev/null +++ b/tests/test_bdist_rpm.py @@ -0,0 +1,149 @@ +"""Tests for distutils.command.bdist_rpm.""" + +import unittest +import sys +import os +import tempfile +import shutil + +from distutils.core import Distribution +from distutils.command.bdist_rpm import bdist_rpm +from distutils.tests import support +from distutils.spawn import find_executable +from distutils import spawn +from distutils.errors import DistutilsExecError + +SETUP_PY = """\ +from distutils.core import setup +import foo + +setup(name='foo', version='0.1', py_modules=['foo'], + url='xxx', author='xxx', author_email='xxx') + +""" + +class BuildRpmTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def setUp(self): + super(BuildRpmTestCase, self).setUp() + self.old_location = os.getcwd() + self.old_sys_argv = sys.argv[:] + + def tearDown(self): + os.chdir(self.old_location) + sys.argv = self.old_sys_argv[:] + super(BuildRpmTestCase, self).tearDown() + + def test_quiet(self): + + # XXX I am unable yet to make this test work without + # spurious sdtout/stderr output under Mac OS X + if sys.platform != 'linux2': + return + + # this test will run only if the rpm commands are found + if (find_executable('rpm') is None or + find_executable('rpmbuild') is None): + return + + # let's create a package + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_rpm(dist) + cmd.fix_python = True + + # running in quiet mode + cmd.quiet = 1 + cmd.ensure_finalized() + cmd.run() + + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + + def test_no_optimize_flag(self): + + # XXX I am unable yet to make this test work without + # spurious sdtout/stderr output under Mac OS X + if sys.platform != 'linux2': + return + + # http://bugs.python.org/issue1533164 + # this test will run only if the rpm command is found + if (find_executable('rpm') is None or + find_executable('rpmbuild') is None): + return + + # let's create a package that brakes bdist_rpm + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_rpm(dist) + cmd.fix_python = True + + # running with force-optimize = 1 + # and quiet = 1 + cmd.quiet = 1 + cmd.ensure_finalized() + cmd.run() + + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) + + # XXX I am unable yet to make this test work without + # spurious stderr output + # so returning until distutils.spawn acts better + return + + # running with force-optimize = 0 + cmd.force_optimize = 0 + try: + # XXX How to prevent the spawned + # rpmbuild command to display errors ? + # this can be a problem for buildbots + cmd.ensure_finalized() + cmd.run() + except DistutilsExecError: + # happens only under Fedora/RedHat + # and some flavors of Linux + # otherwise it's a bug + if sys.platform == 'linux2': + return + + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + +def test_suite(): + return unittest.makeSuite(BuildRpmTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 476e03885b439b139121edd039dc77bcb1c00305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 28 Feb 2009 10:16:43 +0000 Subject: Merged revisions 70049 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70049 | tarek.ziade | 2009-02-28 11:08:02 +0100 (Sat, 28 Feb 2009) | 1 line Issues #1533164 and #5378: Added quiet and force-optimize options to Distutils bdist_rpm command ........ --- command/bdist_rpm.py | 36 ++++++++++-- tests/test_bdist_rpm.py | 149 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 tests/test_bdist_rpm.py diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 83efb8e0..3afdafef 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -122,10 +122,21 @@ class bdist_rpm(Command): # Allow a packager to explicitly force an architecture ('force-arch=', None, "Force an architecture onto the RPM build process"), - ] + + ('quiet', 'q', + "Run the INSTALL phase of RPM building in quiet mode"), + + # Forces the -O1 option when calling the install command, + # so the rpm contains all files needed for proper operation under + # SELinux. Some systems checks for this on build-time and will + # fail without this. + ('force-optimize', None, + "Forces the -O1 option when calling the install command"), + + ] boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', - 'no-autoreq'] + 'no-autoreq', 'quiet', 'force-optimize'] negative_opt = {'no-keep-temp': 'keep-temp', 'no-rpm-opt-flags': 'use-rpm-opt-flags', @@ -175,6 +186,8 @@ class bdist_rpm(Command): self.no_autoreq = 0 self.force_arch = None + self.quiet = 0 + self.force_optimize = 1 def finalize_options(self): self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) @@ -311,6 +324,7 @@ class bdist_rpm(Command): if os.path.exists('/usr/bin/rpmbuild') or \ os.path.exists('/bin/rpmbuild'): rpm_cmd = ['rpmbuild'] + if self.source_only: # what kind of RPMs? rpm_cmd.append('-bs') elif self.binary_only: @@ -322,6 +336,10 @@ class bdist_rpm(Command): '_topdir %s' % os.path.abspath(self.rpm_base)]) if not self.keep_temp: rpm_cmd.append('--clean') + + if self.quiet: + rpm_cmd.append('--quiet') + rpm_cmd.append(spec_path) # Determine the binary rpm names that should be built out of this spec # file @@ -474,13 +492,19 @@ class bdist_rpm(Command): # that we open and interpolate into the spec file, but the defaults # are just text that we drop in as-is. Hmmm. + # forcing -O1 if force-optimize + if self.force_optimize: + optimize = ' -O1' + else: + optimize = '' + + install_cmd = ('%s install%s --root=$RPM_BUILD_ROOT ' + '--record=INSTALLED_FILES') % (def_setup_call, optimize) + script_options = [ ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), ('build', 'build_script', def_build), - ('install', 'install_script', - ("%s install " - "--root=$RPM_BUILD_ROOT " - "--record=INSTALLED_FILES") % def_setup_call), + ('install', 'install_script', install_cmd), ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), ('verifyscript', 'verify_script', None), ('pre', 'pre_install', None), diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py new file mode 100644 index 00000000..ff40628f --- /dev/null +++ b/tests/test_bdist_rpm.py @@ -0,0 +1,149 @@ +"""Tests for distutils.command.bdist_rpm.""" + +import unittest +import sys +import os +import tempfile +import shutil + +from distutils.core import Distribution +from distutils.command.bdist_rpm import bdist_rpm +from distutils.tests import support +from distutils.spawn import find_executable +from distutils import spawn +from distutils.errors import DistutilsExecError + +SETUP_PY = """\ +from distutils.core import setup +import foo + +setup(name='foo', version='0.1', py_modules=['foo'], + url='xxx', author='xxx', author_email='xxx') + +""" + +class BuildRpmTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def setUp(self): + super(BuildRpmTestCase, self).setUp() + self.old_location = os.getcwd() + self.old_sys_argv = sys.argv[:] + + def tearDown(self): + os.chdir(self.old_location) + sys.argv = self.old_sys_argv[:] + super(BuildRpmTestCase, self).tearDown() + + def test_quiet(self): + + # XXX I am unable yet to make this test work without + # spurious sdtout/stderr output under Mac OS X + if sys.platform != 'linux2': + return + + # this test will run only if the rpm commands are found + if (find_executable('rpm') is None or + find_executable('rpmbuild') is None): + return + + # let's create a package + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_rpm(dist) + cmd.fix_python = True + + # running in quiet mode + cmd.quiet = 1 + cmd.ensure_finalized() + cmd.run() + + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + + def test_no_optimize_flag(self): + + # XXX I am unable yet to make this test work without + # spurious sdtout/stderr output under Mac OS X + if sys.platform != 'linux2': + return + + # http://bugs.python.org/issue1533164 + # this test will run only if the rpm command is found + if (find_executable('rpm') is None or + find_executable('rpmbuild') is None): + return + + # let's create a package that brakes bdist_rpm + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_rpm(dist) + cmd.fix_python = True + + # running with force-optimize = 1 + # and quiet = 1 + cmd.quiet = 1 + cmd.ensure_finalized() + cmd.run() + + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) + + # XXX I am unable yet to make this test work without + # spurious stderr output + # so returning until distutils.spawn acts better + return + + # running with force-optimize = 0 + cmd.force_optimize = 0 + try: + # XXX How to prevent the spawned + # rpmbuild command to display errors ? + # this can be a problem for buildbots + cmd.ensure_finalized() + cmd.run() + except DistutilsExecError: + # happens only under Fedora/RedHat + # and some flavors of Linux + # otherwise it's a bug + if sys.platform == 'linux2': + return + + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + +def test_suite(): + return unittest.makeSuite(BuildRpmTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 23fb0d98b0e7696ae54a1c1562117a8035aa6607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 2 Mar 2009 05:38:44 +0000 Subject: removing the force-optimized option as discussed in #1533164 --- command/bdist_rpm.py | 21 +++------------------ tests/test_bdist_rpm.py | 25 ------------------------- 2 files changed, 3 insertions(+), 43 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 34822ec7..1eccc6a4 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -126,18 +126,10 @@ class bdist_rpm (Command): ('quiet', 'q', "Run the INSTALL phase of RPM building in quiet mode"), - - # Forces the -O1 option when calling the install command, - # so the rpm contains all files needed for proper operation under - # SELinux. Some systems checks for this on build-time and will - # fail without this. - ('force-optimize', None, - "Forces the -O1 option when calling the install command"), - ] boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', - 'no-autoreq', 'quiet', 'force-optimize'] + 'no-autoreq', 'quiet'] negative_opt = {'no-keep-temp': 'keep-temp', 'no-rpm-opt-flags': 'use-rpm-opt-flags', @@ -188,7 +180,6 @@ class bdist_rpm (Command): self.force_arch = None self.quiet = 0 - self.force_optimize = 1 # initialize_options() @@ -504,14 +495,8 @@ class bdist_rpm (Command): # that we open and interpolate into the spec file, but the defaults # are just text that we drop in as-is. Hmmm. - # forcing -O1 if force-optimize - if self.force_optimize: - optimize = ' -O1' - else: - optimize = '' - - install_cmd = ('%s install%s --root=$RPM_BUILD_ROOT ' - '--record=INSTALLED_FILES') % (def_setup_call, optimize) + install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT ' + '--record=INSTALLED_FILES') % def_setup_call script_options = [ ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index ff40628f..2d84007f 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -109,8 +109,6 @@ class BuildRpmTestCase(support.TempdirManager, cmd = bdist_rpm(dist) cmd.fix_python = True - # running with force-optimize = 1 - # and quiet = 1 cmd.quiet = 1 cmd.ensure_finalized() cmd.run() @@ -119,29 +117,6 @@ class BuildRpmTestCase(support.TempdirManager, self.assert_('foo-0.1-1.noarch.rpm' in dist_created) os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) - # XXX I am unable yet to make this test work without - # spurious stderr output - # so returning until distutils.spawn acts better - return - - # running with force-optimize = 0 - cmd.force_optimize = 0 - try: - # XXX How to prevent the spawned - # rpmbuild command to display errors ? - # this can be a problem for buildbots - cmd.ensure_finalized() - cmd.run() - except DistutilsExecError: - # happens only under Fedora/RedHat - # and some flavors of Linux - # otherwise it's a bug - if sys.platform == 'linux2': - return - - dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assert_('foo-0.1-1.noarch.rpm' in dist_created) - def test_suite(): return unittest.makeSuite(BuildRpmTestCase) -- cgit v1.2.1 From d3c376f3f1359f37e486775bb1b3009148e794c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 2 Mar 2009 05:41:25 +0000 Subject: Merged revisions 70094 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70094 | tarek.ziade | 2009-03-02 06:38:44 +0100 (Mon, 02 Mar 2009) | 1 line removing the force-optimized option as discussed in #1533164 ........ --- command/bdist_rpm.py | 21 +++------------------ tests/test_bdist_rpm.py | 25 ------------------------- 2 files changed, 3 insertions(+), 43 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 3afdafef..452f9502 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -125,18 +125,10 @@ class bdist_rpm(Command): ('quiet', 'q', "Run the INSTALL phase of RPM building in quiet mode"), - - # Forces the -O1 option when calling the install command, - # so the rpm contains all files needed for proper operation under - # SELinux. Some systems checks for this on build-time and will - # fail without this. - ('force-optimize', None, - "Forces the -O1 option when calling the install command"), - ] boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', - 'no-autoreq', 'quiet', 'force-optimize'] + 'no-autoreq', 'quiet'] negative_opt = {'no-keep-temp': 'keep-temp', 'no-rpm-opt-flags': 'use-rpm-opt-flags', @@ -187,7 +179,6 @@ class bdist_rpm(Command): self.force_arch = None self.quiet = 0 - self.force_optimize = 1 def finalize_options(self): self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) @@ -492,14 +483,8 @@ class bdist_rpm(Command): # that we open and interpolate into the spec file, but the defaults # are just text that we drop in as-is. Hmmm. - # forcing -O1 if force-optimize - if self.force_optimize: - optimize = ' -O1' - else: - optimize = '' - - install_cmd = ('%s install%s --root=$RPM_BUILD_ROOT ' - '--record=INSTALLED_FILES') % (def_setup_call, optimize) + install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT ' + '--record=INSTALLED_FILES') % def_setup_call script_options = [ ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index ff40628f..2d84007f 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -109,8 +109,6 @@ class BuildRpmTestCase(support.TempdirManager, cmd = bdist_rpm(dist) cmd.fix_python = True - # running with force-optimize = 1 - # and quiet = 1 cmd.quiet = 1 cmd.ensure_finalized() cmd.run() @@ -119,29 +117,6 @@ class BuildRpmTestCase(support.TempdirManager, self.assert_('foo-0.1-1.noarch.rpm' in dist_created) os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) - # XXX I am unable yet to make this test work without - # spurious stderr output - # so returning until distutils.spawn acts better - return - - # running with force-optimize = 0 - cmd.force_optimize = 0 - try: - # XXX How to prevent the spawned - # rpmbuild command to display errors ? - # this can be a problem for buildbots - cmd.ensure_finalized() - cmd.run() - except DistutilsExecError: - # happens only under Fedora/RedHat - # and some flavors of Linux - # otherwise it's a bug - if sys.platform == 'linux2': - return - - dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assert_('foo-0.1-1.noarch.rpm' in dist_created) - def test_suite(): return unittest.makeSuite(BuildRpmTestCase) -- cgit v1.2.1 From aee35e095b854dd8969b6e33a6a82a0b635fec39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 7 Mar 2009 00:32:45 +0000 Subject: Issue #5394: removed > 2.3 syntax from distutils.msvc9compiler --- msvc9compiler.py | 36 ++++++++++++++++++++---------------- tests/test_msvc9compiler.py | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 68b77758..00d76d4b 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,10 +17,11 @@ __revision__ = "$Id$" import os import subprocess import sys -from distutils.errors import (DistutilsExecError, DistutilsPlatformError, - CompileError, LibError, LinkError) -from distutils.ccompiler import (CCompiler, gen_preprocess_options, - gen_lib_options) + +from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import CCompiler, gen_preprocess_options, \ + gen_lib_options from distutils import log from distutils.util import get_platform @@ -53,15 +54,14 @@ class Reg: """Helper class to read values from the registry """ - @classmethod def get_value(cls, path, key): for base in HKEYS: d = cls.read_values(base, path) if d and key in d: return d[key] raise KeyError(key) + get_value = classmethod(get_value) - @classmethod def read_keys(cls, base, key): """Return list of registry keys.""" try: @@ -78,8 +78,8 @@ class Reg: L.append(k) i += 1 return L + read_keys = classmethod(read_keys) - @classmethod def read_values(cls, base, key): """Return dict of registry keys and values. @@ -100,8 +100,8 @@ class Reg: d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) i += 1 return d + read_values = classmethod(read_values) - @staticmethod def convert_mbcs(s): dec = getattr(s, "decode", None) if dec is not None: @@ -110,6 +110,7 @@ class Reg: except UnicodeError: pass return s + convert_mbcs = staticmethod(convert_mbcs) class MacroExpander: @@ -131,7 +132,7 @@ class MacroExpander: "sdkinstallrootv2.0") else: raise KeyError("sdkinstallrootv2.0") - except KeyError as exc: # + except KeyError: raise DistutilsPlatformError( """Python was built with Visual Studio 2008; extensions must be built with a compiler than can generate compatible binaries. @@ -479,7 +480,7 @@ class MSVCCompiler(CCompiler) : try: self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt]) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise CompileError(msg) continue elif ext in self._mc_extensions: @@ -506,7 +507,7 @@ class MSVCCompiler(CCompiler) : self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise CompileError(msg) continue else: @@ -519,7 +520,7 @@ class MSVCCompiler(CCompiler) : self.spawn([self.cc] + compile_opts + pp_opts + [input_opt, output_opt] + extra_postargs) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise CompileError(msg) return objects @@ -544,7 +545,7 @@ class MSVCCompiler(CCompiler) : pass # XXX what goes here? try: self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) @@ -633,7 +634,7 @@ class MSVCCompiler(CCompiler) : self.mkpath(os.path.dirname(output_filename)) try: self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise LinkError(msg) # embed the manifest @@ -641,12 +642,15 @@ class MSVCCompiler(CCompiler) : # will still consider the DLL up-to-date, but it will not have a # manifest. Maybe we should link to a temp file? OTOH, that # implies a build environment error that shouldn't go undetected. - mfid = 1 if target_desc == CCompiler.EXECUTABLE else 2 + if target_desc == CCompiler.EXECUTABLE: + mfid = 1 + else: + mfid = 2 out_arg = '-outputresource:%s;%s' % (output_filename, mfid) try: self.spawn(['mt.exe', '-nologo', '-manifest', temp_manifest, out_arg]) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 0c8bd6e0..bde614e9 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -30,6 +30,28 @@ class msvc9compilerTestCase(unittest.TestCase): finally: msvc9compiler.find_vcvarsall = old_find_vcvarsall + def test_reg_class(self): + if sys.platform != 'win32': + # this test is only for win32 + return + + from distutils.msvc9compiler import Reg + self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') + + # looking for values that should exist on all + # windows registeries versions. + path = r'Software\Microsoft\Notepad' + v = Reg.get_value(path, u"lfitalic") + self.assert_(v in (0, 1)) + + import _winreg + HKCU = _winreg.HKEY_CURRENT_USER + keys = Reg.read_keys(HKCU, 'xxxx') + self.assertEquals(keys, None) + + keys = Reg.read_keys(HKCU, r'Software\Microsoft') + self.assert_('Notepad' in keys) + def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) -- cgit v1.2.1 From efe5ba3afc4aa141d408bac712301a121b2f4ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 7 Mar 2009 00:51:53 +0000 Subject: Merged revisions 70212 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70212 | tarek.ziade | 2009-03-07 01:32:45 +0100 (Sat, 07 Mar 2009) | 1 line Issue #5394: removed > 2.3 syntax from distutils.msvc9compiler ........ --- msvc9compiler.py | 36 ++++++++++++++++++++---------------- tests/test_msvc9compiler.py | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index aae06373..48551ddf 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,10 +17,11 @@ __revision__ = "$Id$" import os import subprocess import sys -from distutils.errors import (DistutilsExecError, DistutilsPlatformError, - CompileError, LibError, LinkError) -from distutils.ccompiler import (CCompiler, gen_preprocess_options, - gen_lib_options) + +from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import CCompiler, gen_preprocess_options, \ + gen_lib_options from distutils import log from distutils.util import get_platform @@ -53,15 +54,14 @@ class Reg: """Helper class to read values from the registry """ - @classmethod def get_value(cls, path, key): for base in HKEYS: d = cls.read_values(base, path) if d and key in d: return d[key] raise KeyError(key) + get_value = classmethod(get_value) - @classmethod def read_keys(cls, base, key): """Return list of registry keys.""" try: @@ -78,8 +78,8 @@ class Reg: L.append(k) i += 1 return L + read_keys = classmethod(read_keys) - @classmethod def read_values(cls, base, key): """Return dict of registry keys and values. @@ -100,8 +100,8 @@ class Reg: d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) i += 1 return d + read_values = classmethod(read_values) - @staticmethod def convert_mbcs(s): dec = getattr(s, "decode", None) if dec is not None: @@ -110,6 +110,7 @@ class Reg: except UnicodeError: pass return s + convert_mbcs = staticmethod(convert_mbcs) class MacroExpander: @@ -131,7 +132,7 @@ class MacroExpander: "sdkinstallrootv2.0") else: raise KeyError("sdkinstallrootv2.0") - except KeyError as exc: # + except KeyError: raise DistutilsPlatformError( """Python was built with Visual Studio 2008; extensions must be built with a compiler than can generate compatible binaries. @@ -478,7 +479,7 @@ class MSVCCompiler(CCompiler) : try: self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt]) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise CompileError(msg) continue elif ext in self._mc_extensions: @@ -505,7 +506,7 @@ class MSVCCompiler(CCompiler) : self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise CompileError(msg) continue else: @@ -518,7 +519,7 @@ class MSVCCompiler(CCompiler) : self.spawn([self.cc] + compile_opts + pp_opts + [input_opt, output_opt] + extra_postargs) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise CompileError(msg) return objects @@ -543,7 +544,7 @@ class MSVCCompiler(CCompiler) : pass # XXX what goes here? try: self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) @@ -632,7 +633,7 @@ class MSVCCompiler(CCompiler) : self.mkpath(os.path.dirname(output_filename)) try: self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise LinkError(msg) # embed the manifest @@ -640,12 +641,15 @@ class MSVCCompiler(CCompiler) : # will still consider the DLL up-to-date, but it will not have a # manifest. Maybe we should link to a temp file? OTOH, that # implies a build environment error that shouldn't go undetected. - mfid = 1 if target_desc == CCompiler.EXECUTABLE else 2 + if target_desc == CCompiler.EXECUTABLE: + mfid = 1 + else: + mfid = 2 out_arg = '-outputresource:%s;%s' % (output_filename, mfid) try: self.spawn(['mt.exe', '-nologo', '-manifest', temp_manifest, out_arg]) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 0c8bd6e0..e7d1620f 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -30,6 +30,28 @@ class msvc9compilerTestCase(unittest.TestCase): finally: msvc9compiler.find_vcvarsall = old_find_vcvarsall + def test_reg_class(self): + if sys.platform != 'win32': + # this test is only for win32 + return + + from distutils.msvc9compiler import Reg + self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') + + # looking for values that should exist on all + # windows registeries versions. + path = r'Software\Microsoft\Notepad' + v = Reg.get_value(path, "lfitalic") + self.assert_(v in (0, 1)) + + import _winreg + HKCU = _winreg.HKEY_CURRENT_USER + keys = Reg.read_keys(HKCU, 'xxxx') + self.assertEquals(keys, None) + + keys = Reg.read_keys(HKCU, r'Software\Microsoft') + self.assert_('Notepad' in keys) + def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) -- cgit v1.2.1 From 57eb312a5abee5c02035c01b23853d7f8c9122cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 7 Mar 2009 01:12:09 +0000 Subject: fixed except syntax for py3 --- msvc9compiler.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 48551ddf..ef895422 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -479,7 +479,7 @@ class MSVCCompiler(CCompiler) : try: self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError(msg) continue elif ext in self._mc_extensions: @@ -506,7 +506,7 @@ class MSVCCompiler(CCompiler) : self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError(msg) continue else: @@ -519,7 +519,7 @@ class MSVCCompiler(CCompiler) : self.spawn([self.cc] + compile_opts + pp_opts + [input_opt, output_opt] + extra_postargs) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError(msg) return objects @@ -544,7 +544,7 @@ class MSVCCompiler(CCompiler) : pass # XXX what goes here? try: self.spawn([self.lib] + lib_args) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) @@ -633,7 +633,7 @@ class MSVCCompiler(CCompiler) : self.mkpath(os.path.dirname(output_filename)) try: self.spawn([self.linker] + ld_args) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise LinkError(msg) # embed the manifest @@ -649,7 +649,7 @@ class MSVCCompiler(CCompiler) : try: self.spawn(['mt.exe', '-nologo', '-manifest', temp_manifest, out_arg]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) -- cgit v1.2.1 From 2d5c28e86921686607c1f6d9e67a2eaddbefb447 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 7 Mar 2009 16:34:40 +0000 Subject: bump version to 3.1a1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 1cf8a6ea..12a430ec 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1a0" +__version__ = "3.1a1" #--end constants-- -- cgit v1.2.1 From 9a76eb65587528397be4a18d14f3feb69871eb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 11 Mar 2009 12:48:04 +0000 Subject: Issue #5472: Fixed distutils.test_util tear down --- tests/test_util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_util.py b/tests/test_util.py index ca586268..67474025 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -53,6 +53,8 @@ class utilTestCase(unittest.TestCase): os.path.splitdrive = self.splitdrive if self.uname is not None: os.uname = self.uname + else: + del os.uname def _set_uname(self, uname): self._uname = uname -- cgit v1.2.1 From a5f88c28d9f7fdda89cb5e7528e3c27eae9403d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 11 Mar 2009 12:52:00 +0000 Subject: Merged revisions 70308 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70308 | tarek.ziade | 2009-03-11 13:48:04 +0100 (Wed, 11 Mar 2009) | 1 line Issue #5472: Fixed distutils.test_util tear down ........ --- tests/test_util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_util.py b/tests/test_util.py index ca586268..67474025 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -53,6 +53,8 @@ class utilTestCase(unittest.TestCase): os.path.splitdrive = self.splitdrive if self.uname is not None: os.uname = self.uname + else: + del os.uname def _set_uname(self, uname): self._uname = uname -- cgit v1.2.1 From 08c27fd41a67693d49c78ecbb512d3db78445a51 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 15 Mar 2009 22:43:14 +0000 Subject: Added skip for old MSVC. --- tests/test_msvc9compiler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index bde614e9..78ce3b7a 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -34,6 +34,10 @@ class msvc9compilerTestCase(unittest.TestCase): if sys.platform != 'win32': # this test is only for win32 return + from distutils.msvccompiler import get_build_version + if get_build_version() < 8.0: + # this test is only for MSVC8.0 or above + return from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') -- cgit v1.2.1 From 1f49eec84ddf546ae1b2c4ec7d28e611cc4c9d80 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 21 Mar 2009 17:31:58 +0000 Subject: Merged revisions 70342,70385-70387,70389-70390,70392-70393,70395,70400,70405-70406,70418,70438,70464,70468 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70342 | georg.brandl | 2009-03-13 14:03:58 -0500 (Fri, 13 Mar 2009) | 1 line #5486: typos. ........ r70385 | benjamin.peterson | 2009-03-15 09:38:55 -0500 (Sun, 15 Mar 2009) | 1 line fix tuple.index() error message #5495 ........ r70386 | georg.brandl | 2009-03-15 16:32:06 -0500 (Sun, 15 Mar 2009) | 1 line #5496: fix docstring of lookup(). ........ r70387 | georg.brandl | 2009-03-15 16:37:16 -0500 (Sun, 15 Mar 2009) | 1 line #5493: clarify __nonzero__ docs. ........ r70389 | georg.brandl | 2009-03-15 16:43:38 -0500 (Sun, 15 Mar 2009) | 1 line Fix a small nit in the error message if bool() falls back on __len__ and it returns the wrong type: it would tell the user that __nonzero__ should return bool or int. ........ r70390 | georg.brandl | 2009-03-15 16:44:43 -0500 (Sun, 15 Mar 2009) | 1 line #5491: clarify nested() semantics. ........ r70392 | georg.brandl | 2009-03-15 16:46:00 -0500 (Sun, 15 Mar 2009) | 1 line #5488: add missing struct member. ........ r70393 | georg.brandl | 2009-03-15 16:47:42 -0500 (Sun, 15 Mar 2009) | 1 line #5478: fix copy-paste oversight in function signature. ........ r70395 | georg.brandl | 2009-03-15 16:51:48 -0500 (Sun, 15 Mar 2009) | 1 line #5276: document IDLESTARTUP and .Idle.py. ........ r70400 | georg.brandl | 2009-03-15 16:59:37 -0500 (Sun, 15 Mar 2009) | 3 lines Fix markup in re docs and give a mail address in regex howto, so that the recommendation to send suggestions to the author can be followed. ........ r70405 | georg.brandl | 2009-03-15 17:11:07 -0500 (Sun, 15 Mar 2009) | 7 lines Move the previously local import of threading to module level. This is cleaner and avoids lockups in obscure cases where a Queue is instantiated while the import lock is already held by another thread. OKed by Tim Peters. ........ r70406 | hirokazu.yamamoto | 2009-03-15 17:43:14 -0500 (Sun, 15 Mar 2009) | 1 line Added skip for old MSVC. ........ r70418 | georg.brandl | 2009-03-16 14:42:03 -0500 (Mon, 16 Mar 2009) | 1 line Add token markup. ........ r70438 | benjamin.peterson | 2009-03-17 15:29:51 -0500 (Tue, 17 Mar 2009) | 1 line I thought this was begging for an example ........ r70464 | benjamin.peterson | 2009-03-18 15:58:09 -0500 (Wed, 18 Mar 2009) | 1 line a much better example ........ r70468 | benjamin.peterson | 2009-03-18 22:04:31 -0500 (Wed, 18 Mar 2009) | 1 line close files after comparing them ........ --- tests/test_msvc9compiler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index e7d1620f..aefadd6d 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -34,6 +34,10 @@ class msvc9compilerTestCase(unittest.TestCase): if sys.platform != 'win32': # this test is only for win32 return + from distutils.msvccompiler import get_build_version + if get_build_version() < 8.0: + # this test is only for MSVC8.0 or above + return from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') -- cgit v1.2.1 From 48a187444772146188fbfb1ffb5483fc03a84e87 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 28 Mar 2009 00:48:48 +0000 Subject: Fix typo. --- version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.py b/version.py index 71a56147..0fb5b6e2 100644 --- a/version.py +++ b/version.py @@ -162,7 +162,7 @@ class StrictVersion (Version): # The rules according to Greg Stein: -# 1) a version number has 1 or more numbers separate by a period or by +# 1) a version number has 1 or more numbers separated by a period or by # sequences of letters. If only periods, then these are compared # left-to-right to determine an ordering. # 2) sequences of letters are part of the tuple for comparison and are -- cgit v1.2.1 From faad81c0beb9849650bd8673266556b2438bb73d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 30 Mar 2009 14:51:56 +0000 Subject: Merged revisions 70578,70599,70641-70642,70650,70660-70661,70674,70691,70697-70698,70700,70704 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70578 | benjamin.peterson | 2009-03-23 22:24:56 -0500 (Mon, 23 Mar 2009) | 1 line this is better written using assertRaises ........ r70599 | benjamin.peterson | 2009-03-25 16:42:51 -0500 (Wed, 25 Mar 2009) | 1 line this can be slightly less ugly ........ r70641 | guilherme.polo | 2009-03-27 16:43:08 -0500 (Fri, 27 Mar 2009) | 3 lines Adjusted _tkinter to compile without warnings when WITH_THREAD is not defined (part of issue #5035) ........ r70642 | georg.brandl | 2009-03-27 19:48:48 -0500 (Fri, 27 Mar 2009) | 1 line Fix typo. ........ r70650 | benjamin.peterson | 2009-03-28 14:16:10 -0500 (Sat, 28 Mar 2009) | 1 line give os.symlink and os.link() better parameter names #5564 ........ r70660 | georg.brandl | 2009-03-28 14:52:58 -0500 (Sat, 28 Mar 2009) | 1 line Switch to fixed Sphinx version. ........ r70661 | georg.brandl | 2009-03-28 14:57:36 -0500 (Sat, 28 Mar 2009) | 2 lines Add section numbering to some of the larger subdocuments. ........ r70674 | guilherme.polo | 2009-03-29 05:19:05 -0500 (Sun, 29 Mar 2009) | 1 line Typo fix. ........ r70691 | raymond.hettinger | 2009-03-29 13:51:11 -0500 (Sun, 29 Mar 2009) | 1 line Make life easier for non-CPython implementations. ........ r70697 | benjamin.peterson | 2009-03-29 16:22:35 -0500 (Sun, 29 Mar 2009) | 1 line this has been fixed since 2.6 (I love removing these) ........ r70698 | benjamin.peterson | 2009-03-29 16:31:05 -0500 (Sun, 29 Mar 2009) | 1 line thanks to guido's bytecode verifier, this is fixed ........ r70700 | benjamin.peterson | 2009-03-29 16:50:14 -0500 (Sun, 29 Mar 2009) | 1 line use the awesome new status iterator ........ r70704 | benjamin.peterson | 2009-03-29 21:49:32 -0500 (Sun, 29 Mar 2009) | 1 line there's actually three methods here #5600 ........ --- version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.py b/version.py index 79d458d8..ebcab84e 100644 --- a/version.py +++ b/version.py @@ -207,7 +207,7 @@ class StrictVersion (Version): # The rules according to Greg Stein: -# 1) a version number has 1 or more numbers separate by a period or by +# 1) a version number has 1 or more numbers separated by a period or by # sequences of letters. If only periods, then these are compared # left-to-right to determine an ordering. # 2) sequences of letters are part of the tuple for comparison and are -- cgit v1.2.1 From 31b701a80d234c9782272d4ce02f38b8c365e119 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 31 Mar 2009 00:34:54 +0000 Subject: Add new copydir_run_2to3() function, for use e.g. in test runners to transparently convert and run tests written for Python 2. --- util.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/util.py b/util.py index 042306e9..4c1956a2 100644 --- a/util.py +++ b/util.py @@ -571,6 +571,39 @@ def run_2to3(files, fixer_names=None, options=None, explicit=None): r = DistutilsRefactoringTool(fixer_names, options=options) r.refactor(files, write=True) +def copydir_run_2to3(src, dest, template=None, fixer_names=None, + options=None, explicit=None): + """Recursively copy a directory, only copying new and changed files, + running run_2to3 over all newly copied Python modules afterward. + + If you give a template string, it's parsed like a MANIFEST.in. + """ + from distutils.dir_util import mkpath + from distutils.file_util import copy_file + from distutils.filelist import FileList + filelist = FileList() + curdir = os.getcwd() + os.chdir(src) + try: + filelist.findall() + finally: + os.chdir(curdir) + filelist.files[:] = filelist.allfiles + if template: + for line in template.splitlines(): + line = line.strip() + if not line: continue + filelist.process_template_line(line) + copied = [] + for filename in filelist.files: + outname = os.path.join(dest, filename) + mkpath(os.path.dirname(outname)) + res = copy_file(os.path.join(src, filename), outname, update=1) + if res[1]: copied.append(outname) + run_2to3([fn for fn in copied if fn.lower().endswith('.py')], + fixer_names=fixer_names, options=options, explicit=explicit) + return copied + class Mixin2to3: '''Mixin class for commands that run 2to3. To configure 2to3, setup scripts may either change -- cgit v1.2.1 From 405478c9f8d1613cbc225ff13ea972b5039aa316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 20:48:31 +0000 Subject: using log.warn for sys.stderr --- cmd.py | 5 ++--- log.py | 13 +++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd.py b/cmd.py index 012fca15..dfcbe235 100644 --- a/cmd.py +++ b/cmd.py @@ -352,9 +352,8 @@ class Command: # -- External world manipulation ----------------------------------- def warn (self, msg): - sys.stderr.write("warning: %s: %s\n" % - (self.get_command_name(), msg)) - + log.warn("warning: %s: %s\n" % + (self.get_command_name(), msg)) def execute (self, func, args, msg=None, level=1): util.execute(func, args, msg, dry_run=self.dry_run) diff --git a/log.py b/log.py index fcaa545a..6f949d51 100644 --- a/log.py +++ b/log.py @@ -18,13 +18,14 @@ class Log: def _log(self, level, msg, args): if level >= self.threshold: - if not args: - # msg may contain a '%'. If args is empty, - # don't even try to string-format - print msg + if args: + msg = msg % args + if level in (WARN, ERROR, FATAL): + stream = sys.stderr else: - print msg % args - sys.stdout.flush() + stream = sys.stdout + stream.write('%s\n' % msg) + stream.flush() def log(self, level, msg, *args): self._log(level, msg, args) -- cgit v1.2.1 From aca2cc988727dfa93fab949125562b1714f5ab69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 20:50:59 +0000 Subject: added tests for the clean command --- command/clean.py | 2 +- tests/support.py | 21 +++++++++++++++++++-- tests/test_clean.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 3 deletions(-) create mode 100755 tests/test_clean.py diff --git a/command/clean.py b/command/clean.py index ba03d1ff..90ef35f1 100644 --- a/command/clean.py +++ b/command/clean.py @@ -11,7 +11,7 @@ from distutils.core import Command from distutils.dir_util import remove_tree from distutils import log -class clean (Command): +class clean(Command): description = "clean up temporary files from 'build' command" user_options = [ diff --git a/tests/support.py b/tests/support.py index fbbf35d8..578cf40c 100644 --- a/tests/support.py +++ b/tests/support.py @@ -4,7 +4,7 @@ import shutil import tempfile from distutils import log - +from distutils.core import Distribution class LoggingSilencer(object): @@ -42,7 +42,7 @@ class TempdirManager(object): self.tempdirs.append(d) return d - def write_file(self, path, content): + def write_file(self, path, content='xxx'): """Writes a file in the given path. @@ -56,6 +56,23 @@ class TempdirManager(object): finally: f.close() + def create_dist(self, pkg_name='foo', **kw): + """Will generate a test environment. + + This function creates: + - a Distribution instance using keywords + - a temporary directory with a package structure + + It returns the package directory and the distribution + instance. + """ + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, pkg_name) + os.mkdir(pkg_dir) + dist = Distribution(attrs=kw) + + return pkg_dir, dist + class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" diff --git a/tests/test_clean.py b/tests/test_clean.py new file mode 100755 index 00000000..a94a812b --- /dev/null +++ b/tests/test_clean.py @@ -0,0 +1,49 @@ +"""Tests for distutils.command.clean.""" +import sys +import os +import unittest +import getpass + +from distutils.command.clean import clean +from distutils.tests import support + +class cleanTestCase(support.TempdirManager, + unittest.TestCase): + + def test_simple_run(self): + pkg_dir, dist = self.create_dist() + cmd = clean(dist) + + # let's add some elements clean should remove + dirs = [(d, os.path.join(pkg_dir, d)) + for d in ('build_temp', 'build_lib', 'bdist_base', + 'build_scripts', 'build_base')] + + for name, path in dirs: + os.mkdir(path) + setattr(cmd, name, path) + if name == 'build_base': + continue + for f in ('one', 'two', 'three'): + self.write_file(os.path.join(path, f)) + + # let's run the command + cmd.all = 1 + cmd.ensure_finalized() + cmd.run() + + # make sure the files where removed + for name, path in dirs: + self.assert_(not os.path.exists(path), + '%s was not removed' % path) + + # let's run the command again (should spit warnings but suceed) + cmd.all = 1 + cmd.ensure_finalized() + cmd.run() + +def test_suite(): + return unittest.makeSuite(cleanTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From ec2b5a008569d749c58f1ac688a165c12af051cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 20:53:13 +0000 Subject: more tests for the register command --- command/register.py | 23 ++++++----- tests/test_register.py | 108 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 89 insertions(+), 42 deletions(-) diff --git a/command/register.py b/command/register.py index 40661d87..f3463dc2 100644 --- a/command/register.py +++ b/command/register.py @@ -90,14 +90,14 @@ class register(PyPIRCCommand): ''' Fetch the list of classifiers from the server. ''' response = urllib2.urlopen(self.repository+'?:action=list_classifiers') - print response.read() + log.info(response.read()) def verify_metadata(self): ''' Send the metadata to the package index server to be checked. ''' # send the info to the server and report the result (code, result) = self.post_to_server(self.build_post_data('verify')) - print 'Server response (%s): %s'%(code, result) + log.info('Server response (%s): %s' % (code, result)) def send_metadata(self): @@ -210,17 +210,18 @@ Your selection [default 1]: ''', log.INFO) data['email'] = raw_input(' EMail: ') code, result = self.post_to_server(data) if code != 200: - print 'Server response (%s): %s'%(code, result) + log.info('Server response (%s): %s' % (code, result)) else: - print 'You will receive an email shortly.' - print 'Follow the instructions in it to complete registration.' + log.info('You will receive an email shortly.') + log.info(('Follow the instructions in it to ' + 'complete registration.')) elif choice == '3': data = {':action': 'password_reset'} data['email'] = '' while not data['email']: data['email'] = raw_input('Your email address: ') code, result = self.post_to_server(data) - print 'Server response (%s): %s'%(code, result) + log.info('Server response (%s): %s' % (code, result)) def build_post_data(self, action): # figure the data to send - the metadata plus some additional @@ -253,8 +254,10 @@ Your selection [default 1]: ''', log.INFO) def post_to_server(self, data, auth=None): ''' Post a query to the server, and return a string response. ''' - self.announce('Registering %s to %s' % (data['name'], - self.repository), log.INFO) + if 'name' in data: + self.announce('Registering %s to %s' % (data['name'], + self.repository), + log.INFO) # Build up the MIME payload for the urllib2 POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary @@ -301,5 +304,7 @@ Your selection [default 1]: ''', log.INFO) data = result.read() result = 200, 'OK' if self.show_response: - print '-'*75, data, '-'*75 + dashes = '-' * 75 + self.announce('%s%s%s' % (dashes, data, dashes)) + return result diff --git a/tests/test_register.py b/tests/test_register.py index b3543f50..7da8edb5 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -3,7 +3,9 @@ import sys import os import unittest import getpass +import urllib2 +from distutils.command import register as register_module from distutils.command.register import register from distutils.core import Distribution @@ -42,18 +44,20 @@ class RawInputs(object): finally: self.index += 1 -class FakeServer(object): +class FakeOpener(object): """Fakes a PyPI server""" def __init__(self): - self.calls = [] + self.reqs = [] def __call__(self, *args): - # we want to compare them, so let's store - # something comparable - els = args[0].items() - els.sort() - self.calls.append(tuple(els)) - return 200, 'OK' + return self + + def open(self, req): + self.reqs.append(req) + return self + + def read(self): + return 'xxx' class registerTestCase(PyPIRCCommandTestCase): @@ -64,24 +68,27 @@ class registerTestCase(PyPIRCCommandTestCase): def _getpass(prompt): return 'password' getpass.getpass = _getpass + self.old_opener = urllib2.build_opener + self.conn = urllib2.build_opener = FakeOpener() def tearDown(self): getpass.getpass = self._old_getpass + urllib2.build_opener = self.old_opener PyPIRCCommandTestCase.tearDown(self) + def _get_cmd(self): + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx'} + pkg_info, dist = self.create_dist(**metadata) + return register(dist) + def test_create_pypirc(self): # this test makes sure a .pypirc file # is created when requested. - # let's create a fake distribution - # and a register instance - dist = Distribution() - dist.metadata.url = 'xxx' - dist.metadata.author = 'xxx' - dist.metadata.author_email = 'xxx' - dist.metadata.name = 'xxx' - dist.metadata.version = 'xxx' - cmd = register(dist) + # let's create a register instance + cmd = self._get_cmd() # we shouldn't have a .pypirc file yet self.assert_(not os.path.exists(self.rc)) @@ -95,13 +102,12 @@ class registerTestCase(PyPIRCCommandTestCase): # Password : 'password' # Save your login (y/N)? : 'y' inputs = RawInputs('1', 'tarek', 'y') - from distutils.command import register as register_module register_module.raw_input = inputs.__call__ - - cmd.post_to_server = pypi_server = FakeServer() - # let's run the command - cmd.run() + try: + cmd.run() + finally: + del register_module.raw_input # we should have a brand new .pypirc file self.assert_(os.path.exists(self.rc)) @@ -117,30 +123,66 @@ class registerTestCase(PyPIRCCommandTestCase): raise AssertionError(prompt) register_module.raw_input = _no_way + cmd.show_response = 1 cmd.run() # let's see what the server received : we should # have 2 similar requests - self.assert_(len(pypi_server.calls), 2) - self.assert_(pypi_server.calls[0], pypi_server.calls[1]) - - def test_password_not_in_file(self): + self.assert_(self.conn.reqs, 2) + req1 = dict(self.conn.reqs[0].headers) + req2 = dict(self.conn.reqs[1].headers) - f = open(self.rc, 'w') - f.write(PYPIRC_NOPASSWORD) - f.close() + self.assertEquals(req1['Content-length'], '1374') + self.assertEquals(req2['Content-length'], '1374') + self.assert_('xxx' in self.conn.reqs[1].data) - dist = Distribution() - cmd = register(dist) - cmd.post_to_server = FakeServer() + def test_password_not_in_file(self): + self.write_file(self.rc, PYPIRC_NOPASSWORD) + cmd = self._get_cmd() cmd._set_config() cmd.finalize_options() cmd.send_metadata() # dist.password should be set # therefore used afterwards by other commands - self.assertEquals(dist.password, 'password') + self.assertEquals(cmd.distribution.password, 'password') + + def test_registering(self): + # this test runs choice 2 + cmd = self._get_cmd() + inputs = RawInputs('2', 'tarek', 'tarek@ziade.org') + register_module.raw_input = inputs.__call__ + try: + # let's run the command + cmd.run() + finally: + del register_module.raw_input + + # we should have send a request + self.assert_(self.conn.reqs, 1) + req = self.conn.reqs[0] + headers = dict(req.headers) + self.assertEquals(headers['Content-length'], '608') + self.assert_('tarek' in req.data) + + def test_password_reset(self): + # this test runs choice 3 + cmd = self._get_cmd() + inputs = RawInputs('3', 'tarek@ziade.org') + register_module.raw_input = inputs.__call__ + try: + # let's run the command + cmd.run() + finally: + del register_module.raw_input + + # we should have send a request + self.assert_(self.conn.reqs, 1) + req = self.conn.reqs[0] + headers = dict(req.headers) + self.assertEquals(headers['Content-length'], '290') + self.assert_('tarek' in req.data) def test_suite(): return unittest.makeSuite(registerTestCase) -- cgit v1.2.1 From 34a3369b79dad443a930c06247b9195fcd024837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 20:53:55 +0000 Subject: more tests for the upload command --- command/upload.py | 2 +- tests/test_upload.py | 70 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/command/upload.py b/command/upload.py index df82e4ca..74b06283 100644 --- a/command/upload.py +++ b/command/upload.py @@ -192,4 +192,4 @@ class upload(PyPIRCCommand): self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - print '-'*75, r.read(), '-'*75 + self.announce('-'*75, r.read(), '-'*75) diff --git a/tests/test_upload.py b/tests/test_upload.py index 3f8ca6d6..f57a4a3f 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -2,6 +2,7 @@ import sys import os import unittest +import httplib from distutils.command.upload import upload from distutils.core import Distribution @@ -18,17 +19,52 @@ index-servers = [server1] username:me """ +class Response(object): + def __init__(self, status=200, reason='OK'): + self.status = status + self.reason = reason +class FakeConnection(object): + + def __init__(self): + self.requests = [] + self.headers = [] + self.body = '' + + def __call__(self, netloc): + return self + + def connect(self): + pass + endheaders = connect + + def putrequest(self, method, url): + self.requests.append((method, url)) + + def putheader(self, name, value): + self.headers.append((name, value)) + + def send(self, body): + self.body = body + + def getresponse(self): + return Response() class uploadTestCase(PyPIRCCommandTestCase): + def setUp(self): + super(uploadTestCase, self).setUp() + self.old_class = httplib.HTTPConnection + self.conn = httplib.HTTPConnection = FakeConnection() + + def tearDown(self): + httplib.HTTPConnection = self.old_class + super(uploadTestCase, self).tearDown() + def test_finalize_options(self): # new format - f = open(self.rc, 'w') - f.write(PYPIRC) - f.close() - + self.write_file(self.rc, PYPIRC) dist = Distribution() cmd = upload(dist) cmd.finalize_options() @@ -39,9 +75,7 @@ class uploadTestCase(PyPIRCCommandTestCase): def test_saved_password(self): # file with no password - f = open(self.rc, 'w') - f.write(PYPIRC_NOPASSWORD) - f.close() + self.write_file(self.rc, PYPIRC_NOPASSWORD) # make sure it passes dist = Distribution() @@ -56,6 +90,28 @@ class uploadTestCase(PyPIRCCommandTestCase): cmd.finalize_options() self.assertEquals(cmd.password, 'xxx') + def test_upload(self): + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path) + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + self.write_file(self.rc, PYPIRC) + + # lets run it + pkg_dir, dist = self.create_dist(dist_files=dist_files) + cmd = upload(dist) + cmd.ensure_finalized() + cmd.run() + + # what did we send ? + headers = dict(self.conn.headers) + self.assertEquals(headers['Content-length'], '2086') + self.assert_(headers['Content-type'].startswith('multipart/form-data')) + + self.assertEquals(self.conn.requests, [('POST', '/pypi')]) + self.assert_('xxx' in self.conn.body) + def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From 50dee054d28f60fc285f609e47e422dee1fcb161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 20:54:38 +0000 Subject: added test to the install_data command --- command/install_data.py | 11 ++++--- tests/test_install_data.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 tests/test_install_data.py diff --git a/command/install_data.py b/command/install_data.py index cb113710..ec78ce34 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -12,7 +12,7 @@ from types import StringType from distutils.core import Command from distutils.util import change_root, convert_path -class install_data (Command): +class install_data(Command): description = "install data files" @@ -27,12 +27,11 @@ class install_data (Command): boolean_options = ['force'] - def initialize_options (self): + def initialize_options(self): self.install_dir = None self.outfiles = [] self.root = None self.force = 0 - self.data_files = self.distribution.data_files self.warn_dir = 1 @@ -43,7 +42,7 @@ class install_data (Command): ('force', 'force'), ) - def run (self): + def run(self): self.mkpath(self.install_dir) for f in self.data_files: if type(f) is StringType: @@ -76,8 +75,8 @@ class install_data (Command): (out, _) = self.copy_file(data, dir) self.outfiles.append(out) - def get_inputs (self): + def get_inputs(self): return self.data_files or [] - def get_outputs (self): + def get_outputs(self): return self.outfiles diff --git a/tests/test_install_data.py b/tests/test_install_data.py new file mode 100644 index 00000000..73c40371 --- /dev/null +++ b/tests/test_install_data.py @@ -0,0 +1,75 @@ +"""Tests for distutils.command.install_data.""" +import sys +import os +import unittest +import getpass + +from distutils.command.install_data import install_data +from distutils.tests import support + +class InstallDataTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_simple_run(self): + pkg_dir, dist = self.create_dist() + cmd = install_data(dist) + cmd.install_dir = inst = os.path.join(pkg_dir, 'inst') + + # data_files can contain + # - simple files + # - a tuple with a path, and a list of file + one = os.path.join(pkg_dir, 'one') + self.write_file(one, 'xxx') + inst2 = os.path.join(pkg_dir, 'inst2') + two = os.path.join(pkg_dir, 'two') + self.write_file(two, 'xxx') + + cmd.data_files = [one, (inst2, [two])] + self.assertEquals(cmd.get_inputs(), [one, (inst2, [two])]) + + # let's run the command + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEquals(len(cmd.get_outputs()), 2) + rtwo = os.path.split(two)[-1] + self.assert_(os.path.exists(os.path.join(inst2, rtwo))) + rone = os.path.split(one)[-1] + self.assert_(os.path.exists(os.path.join(inst, rone))) + cmd.outfiles = [] + + # let's try with warn_dir one + cmd.warn_dir = 1 + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEquals(len(cmd.get_outputs()), 2) + self.assert_(os.path.exists(os.path.join(inst2, rtwo))) + self.assert_(os.path.exists(os.path.join(inst, rone))) + cmd.outfiles = [] + + # now using root and empty dir + cmd.root = os.path.join(pkg_dir, 'root') + inst3 = os.path.join(cmd.install_dir, 'inst3') + inst4 = os.path.join(pkg_dir, 'inst4') + three = os.path.join(cmd.install_dir, 'three') + self.write_file(three, 'xx') + cmd.data_files = [one, (inst2, [two]), + ('inst3', [three]), + (inst4, [])] + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEquals(len(cmd.get_outputs()), 4) + self.assert_(os.path.exists(os.path.join(inst2, rtwo))) + self.assert_(os.path.exists(os.path.join(inst, rone))) + +def test_suite(): + return unittest.makeSuite(InstallDataTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 8d75f8d30fef8621e6d3c585e7b2e93c15d1cf27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 20:55:21 +0000 Subject: added tests to the install_headers command --- command/install_headers.py | 13 +++++++------ tests/test_install_headers.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 tests/test_install_headers.py diff --git a/command/install_headers.py b/command/install_headers.py index c25b7712..d892416a 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -8,7 +8,8 @@ __revision__ = "$Id$" from distutils.core import Command -class install_headers (Command): +# XXX force is never used +class install_headers(Command): description = "install C/C++ header files" @@ -20,18 +21,18 @@ class install_headers (Command): boolean_options = ['force'] - def initialize_options (self): + def initialize_options(self): self.install_dir = None self.force = 0 self.outfiles = [] - def finalize_options (self): + def finalize_options(self): self.set_undefined_options('install', ('install_headers', 'install_dir'), ('force', 'force')) - def run (self): + def run(self): headers = self.distribution.headers if not headers: return @@ -41,10 +42,10 @@ class install_headers (Command): (out, _) = self.copy_file(header, self.install_dir) self.outfiles.append(out) - def get_inputs (self): + def get_inputs(self): return self.distribution.headers or [] - def get_outputs (self): + def get_outputs(self): return self.outfiles # class install_headers diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py new file mode 100644 index 00000000..2564563f --- /dev/null +++ b/tests/test_install_headers.py @@ -0,0 +1,39 @@ +"""Tests for distutils.command.install_headers.""" +import sys +import os +import unittest +import getpass + +from distutils.command.install_headers import install_headers +from distutils.tests import support + +class InstallHeadersTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_simple_run(self): + # we have two headers + header_list = self.mkdtemp() + header1 = os.path.join(header_list, 'header1') + header2 = os.path.join(header_list, 'header2') + self.write_file(header1) + self.write_file(header2) + headers = [header1, header2] + + pkg_dir, dist = self.create_dist(headers=headers) + cmd = install_headers(dist) + self.assertEquals(cmd.get_inputs(), headers) + + # let's run the command + cmd.install_dir = os.path.join(pkg_dir, 'inst') + cmd.ensure_finalized() + cmd.run() + + # let's check the results + self.assertEquals(len(cmd.get_outputs()), 2) + +def test_suite(): + return unittest.makeSuite(InstallHeadersTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From e96f0dd0d6fbf5f13d206aa20d10dc5e3047770c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 20:56:11 +0000 Subject: making sdist and config test silents --- tests/test_config.py | 4 +++- tests/test_sdist.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index a18f4535..7737538b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -47,7 +47,9 @@ password:xxx """ -class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): +class PyPIRCCommandTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def setUp(self): """Patches the environment.""" diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 15a8c808..8af080f9 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -34,7 +34,7 @@ somecode%(sep)sdoc.dat somecode%(sep)sdoc.txt """ -class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): +class sdistTestCase(PyPIRCCommandTestCase): def setUp(self): # PyPIRCCommandTestCase creates a temp dir already -- cgit v1.2.1 From f2378909df4f8e5552bae53353a305b4775e9699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 21:37:16 +0000 Subject: Merged revisions 70886,70888-70892 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70886 | tarek.ziade | 2009-03-31 15:50:59 -0500 (Tue, 31 Mar 2009) | 1 line added tests for the clean command ........ r70888 | tarek.ziade | 2009-03-31 15:53:13 -0500 (Tue, 31 Mar 2009) | 1 line more tests for the register command ........ r70889 | tarek.ziade | 2009-03-31 15:53:55 -0500 (Tue, 31 Mar 2009) | 1 line more tests for the upload command ........ r70890 | tarek.ziade | 2009-03-31 15:54:38 -0500 (Tue, 31 Mar 2009) | 1 line added test to the install_data command ........ r70891 | tarek.ziade | 2009-03-31 15:55:21 -0500 (Tue, 31 Mar 2009) | 1 line added tests to the install_headers command ........ r70892 | tarek.ziade | 2009-03-31 15:56:11 -0500 (Tue, 31 Mar 2009) | 1 line making sdist and config test silents ........ --- cmd.py | 2 +- command/install_data.py | 1 - command/install_headers.py | 1 + command/register.py | 23 +++++---- command/upload.py | 2 +- tests/support.py | 21 +++++++- tests/test_clean.py | 49 +++++++++++++++++++ tests/test_config.py | 4 +- tests/test_install_data.py | 75 ++++++++++++++++++++++++++++ tests/test_install_headers.py | 39 +++++++++++++++ tests/test_register.py | 110 +++++++++++++++++++++++++++++------------- tests/test_sdist.py | 2 +- tests/test_upload.py | 70 ++++++++++++++++++++++++--- 13 files changed, 341 insertions(+), 58 deletions(-) create mode 100755 tests/test_clean.py create mode 100644 tests/test_install_data.py create mode 100644 tests/test_install_headers.py diff --git a/cmd.py b/cmd.py index 800425d4..46055b4b 100644 --- a/cmd.py +++ b/cmd.py @@ -333,7 +333,7 @@ class Command: # -- External world manipulation ----------------------------------- def warn(self, msg): - sys.stderr.write("warning: %s: %s\n" % (self.get_command_name(), msg)) + log.warn("warning: %s: %s\n" % (self.get_command_name(), msg)) def execute(self, func, args, msg=None, level=1): util.execute(func, args, msg, dry_run=self.dry_run) diff --git a/command/install_data.py b/command/install_data.py index 06a70b4a..ab40797b 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -31,7 +31,6 @@ class install_data(Command): self.outfiles = [] self.root = None self.force = 0 - self.data_files = self.distribution.data_files self.warn_dir = 1 diff --git a/command/install_headers.py b/command/install_headers.py index 346daaad..38125b55 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -8,6 +8,7 @@ __revision__ = "$Id$" from distutils.core import Command +# XXX force is never used class install_headers(Command): description = "install C/C++ header files" diff --git a/command/register.py b/command/register.py index c271b18c..7dd30999 100644 --- a/command/register.py +++ b/command/register.py @@ -92,15 +92,14 @@ class register(PyPIRCCommand): ''' url = self.repository+'?:action=list_classifiers' response = urllib.request.urlopen(url) - print(response.read()) + log.info(response.read()) def verify_metadata(self): ''' Send the metadata to the package index server to be checked. ''' # send the info to the server and report the result (code, result) = self.post_to_server(self.build_post_data('verify')) - print('Server response (%s): %s'%(code, result)) - + log.info('Server response (%s): %s' % (code, result)) def send_metadata(self): ''' Send the metadata to the package index server. @@ -211,17 +210,18 @@ Your selection [default 1]: ''', log.INFO) data['email'] = input(' EMail: ') code, result = self.post_to_server(data) if code != 200: - print('Server response (%s): %s'%(code, result)) + log.info('Server response (%s): %s' % (code, result)) else: - print('You will receive an email shortly.') - print('Follow the instructions in it to complete registration.') + log.info('You will receive an email shortly.') + log.info(('Follow the instructions in it to ' + 'complete registration.')) elif choice == '3': data = {':action': 'password_reset'} data['email'] = '' while not data['email']: data['email'] = input('Your email address: ') code, result = self.post_to_server(data) - print('Server response (%s): %s'%(code, result)) + log.info('Server response (%s): %s' % (code, result)) def build_post_data(self, action): # figure the data to send - the metadata plus some additional @@ -254,8 +254,10 @@ Your selection [default 1]: ''', log.INFO) def post_to_server(self, data, auth=None): ''' Post a query to the server, and return a string response. ''' - self.announce('Registering %s to %s' % (data['name'], - self.repository), log.INFO) + if 'name' in data: + self.announce('Registering %s to %s' % (data['name'], + self.repository), + log.INFO) # Build up the MIME payload for the urllib2 POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary @@ -302,5 +304,6 @@ Your selection [default 1]: ''', log.INFO) data = result.read() result = 200, 'OK' if self.show_response: - print('-'*75, data, '-'*75) + dashes = '-' * 75 + self.announce('%s%s%s' % (dashes, data, dashes)) return result diff --git a/command/upload.py b/command/upload.py index eb2f5167..9249427a 100644 --- a/command/upload.py +++ b/command/upload.py @@ -194,4 +194,4 @@ class upload(PyPIRCCommand): self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - print('-'*75, r.read(), '-'*75) + self.announce('-'*75, r.read(), '-'*75) diff --git a/tests/support.py b/tests/support.py index ecc8da17..ab2af9a6 100644 --- a/tests/support.py +++ b/tests/support.py @@ -4,7 +4,7 @@ import shutil import tempfile from distutils import log - +from distutils.core import Distribution class LoggingSilencer(object): @@ -42,7 +42,7 @@ class TempdirManager(object): self.tempdirs.append(d) return d - def write_file(self, path, content): + def write_file(self, path, content='xxx'): """Writes a file in the given path. @@ -56,6 +56,23 @@ class TempdirManager(object): finally: f.close() + def create_dist(self, pkg_name='foo', **kw): + """Will generate a test environment. + + This function creates: + - a Distribution instance using keywords + - a temporary directory with a package structure + + It returns the package directory and the distribution + instance. + """ + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, pkg_name) + os.mkdir(pkg_dir) + dist = Distribution(attrs=kw) + + return pkg_dir, dist + class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" diff --git a/tests/test_clean.py b/tests/test_clean.py new file mode 100755 index 00000000..a94a812b --- /dev/null +++ b/tests/test_clean.py @@ -0,0 +1,49 @@ +"""Tests for distutils.command.clean.""" +import sys +import os +import unittest +import getpass + +from distutils.command.clean import clean +from distutils.tests import support + +class cleanTestCase(support.TempdirManager, + unittest.TestCase): + + def test_simple_run(self): + pkg_dir, dist = self.create_dist() + cmd = clean(dist) + + # let's add some elements clean should remove + dirs = [(d, os.path.join(pkg_dir, d)) + for d in ('build_temp', 'build_lib', 'bdist_base', + 'build_scripts', 'build_base')] + + for name, path in dirs: + os.mkdir(path) + setattr(cmd, name, path) + if name == 'build_base': + continue + for f in ('one', 'two', 'three'): + self.write_file(os.path.join(path, f)) + + # let's run the command + cmd.all = 1 + cmd.ensure_finalized() + cmd.run() + + # make sure the files where removed + for name, path in dirs: + self.assert_(not os.path.exists(path), + '%s was not removed' % path) + + # let's run the command again (should spit warnings but suceed) + cmd.all = 1 + cmd.ensure_finalized() + cmd.run() + +def test_suite(): + return unittest.makeSuite(cleanTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_config.py b/tests/test_config.py index 09abfcd8..7506f932 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -46,7 +46,9 @@ password:xxx """ -class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): +class PyPIRCCommandTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def setUp(self): """Patches the environment.""" diff --git a/tests/test_install_data.py b/tests/test_install_data.py new file mode 100644 index 00000000..73c40371 --- /dev/null +++ b/tests/test_install_data.py @@ -0,0 +1,75 @@ +"""Tests for distutils.command.install_data.""" +import sys +import os +import unittest +import getpass + +from distutils.command.install_data import install_data +from distutils.tests import support + +class InstallDataTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_simple_run(self): + pkg_dir, dist = self.create_dist() + cmd = install_data(dist) + cmd.install_dir = inst = os.path.join(pkg_dir, 'inst') + + # data_files can contain + # - simple files + # - a tuple with a path, and a list of file + one = os.path.join(pkg_dir, 'one') + self.write_file(one, 'xxx') + inst2 = os.path.join(pkg_dir, 'inst2') + two = os.path.join(pkg_dir, 'two') + self.write_file(two, 'xxx') + + cmd.data_files = [one, (inst2, [two])] + self.assertEquals(cmd.get_inputs(), [one, (inst2, [two])]) + + # let's run the command + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEquals(len(cmd.get_outputs()), 2) + rtwo = os.path.split(two)[-1] + self.assert_(os.path.exists(os.path.join(inst2, rtwo))) + rone = os.path.split(one)[-1] + self.assert_(os.path.exists(os.path.join(inst, rone))) + cmd.outfiles = [] + + # let's try with warn_dir one + cmd.warn_dir = 1 + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEquals(len(cmd.get_outputs()), 2) + self.assert_(os.path.exists(os.path.join(inst2, rtwo))) + self.assert_(os.path.exists(os.path.join(inst, rone))) + cmd.outfiles = [] + + # now using root and empty dir + cmd.root = os.path.join(pkg_dir, 'root') + inst3 = os.path.join(cmd.install_dir, 'inst3') + inst4 = os.path.join(pkg_dir, 'inst4') + three = os.path.join(cmd.install_dir, 'three') + self.write_file(three, 'xx') + cmd.data_files = [one, (inst2, [two]), + ('inst3', [three]), + (inst4, [])] + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEquals(len(cmd.get_outputs()), 4) + self.assert_(os.path.exists(os.path.join(inst2, rtwo))) + self.assert_(os.path.exists(os.path.join(inst, rone))) + +def test_suite(): + return unittest.makeSuite(InstallDataTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py new file mode 100644 index 00000000..2564563f --- /dev/null +++ b/tests/test_install_headers.py @@ -0,0 +1,39 @@ +"""Tests for distutils.command.install_headers.""" +import sys +import os +import unittest +import getpass + +from distutils.command.install_headers import install_headers +from distutils.tests import support + +class InstallHeadersTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_simple_run(self): + # we have two headers + header_list = self.mkdtemp() + header1 = os.path.join(header_list, 'header1') + header2 = os.path.join(header_list, 'header2') + self.write_file(header1) + self.write_file(header2) + headers = [header1, header2] + + pkg_dir, dist = self.create_dist(headers=headers) + cmd = install_headers(dist) + self.assertEquals(cmd.get_inputs(), headers) + + # let's run the command + cmd.install_dir = os.path.join(pkg_dir, 'inst') + cmd.ensure_finalized() + cmd.run() + + # let's check the results + self.assertEquals(len(cmd.get_outputs()), 2) + +def test_suite(): + return unittest.makeSuite(InstallHeadersTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_register.py b/tests/test_register.py index 8826e90f..46f7761d 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -3,7 +3,9 @@ import sys import os import unittest import getpass +import urllib +from distutils.command import register as register_module from distutils.command.register import register from distutils.core import Distribution @@ -42,18 +44,20 @@ class Inputs(object): finally: self.index += 1 -class FakeServer(object): +class FakeOpener(object): """Fakes a PyPI server""" def __init__(self): - self.calls = [] + self.reqs = [] def __call__(self, *args): - # we want to compare them, so let's store - # something comparable - els = list(args[0].items()) - els.sort() - self.calls.append(tuple(els)) - return 200, 'OK' + return self + + def open(self, req): + self.reqs.append(req) + return self + + def read(self): + return 'xxx' class registerTestCase(PyPIRCCommandTestCase): @@ -64,24 +68,27 @@ class registerTestCase(PyPIRCCommandTestCase): def _getpass(prompt): return 'password' getpass.getpass = _getpass + self.old_opener = urllib.request.build_opener + self.conn = urllib.request.build_opener = FakeOpener() def tearDown(self): getpass.getpass = self._old_getpass + urllib.request.build_opener = self.old_opener PyPIRCCommandTestCase.tearDown(self) + def _get_cmd(self): + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx'} + pkg_info, dist = self.create_dist(**metadata) + return register(dist) + def test_create_pypirc(self): # this test makes sure a .pypirc file # is created when requested. - # let's create a fake distribution - # and a register instance - dist = Distribution() - dist.metadata.url = 'xxx' - dist.metadata.author = 'xxx' - dist.metadata.author_email = 'xxx' - dist.metadata.name = 'xxx' - dist.metadata.version = 'xxx' - cmd = register(dist) + # let's create a register instance + cmd = self._get_cmd() # we shouldn't have a .pypirc file yet self.assert_(not os.path.exists(self.rc)) @@ -95,13 +102,12 @@ class registerTestCase(PyPIRCCommandTestCase): # Password : 'password' # Save your login (y/N)? : 'y' inputs = Inputs('1', 'tarek', 'y') - from distutils.command import register as register_module register_module.input = inputs.__call__ - - cmd.post_to_server = pypi_server = FakeServer() - # let's run the command - cmd.run() + try: + cmd.run() + finally: + del register_module.input # we should have a brand new .pypirc file self.assert_(os.path.exists(self.rc)) @@ -115,32 +121,68 @@ class registerTestCase(PyPIRCCommandTestCase): # if we run the command again def _no_way(prompt=''): raise AssertionError(prompt) - register_module.raw_input = _no_way + register_module.input = _no_way + cmd.show_response = 1 cmd.run() # let's see what the server received : we should # have 2 similar requests - self.assert_(len(pypi_server.calls), 2) - self.assert_(pypi_server.calls[0], pypi_server.calls[1]) - - def test_password_not_in_file(self): + self.assert_(self.conn.reqs, 2) + req1 = dict(self.conn.reqs[0].headers) + req2 = dict(self.conn.reqs[1].headers) - f = open(self.rc, 'w') - f.write(PYPIRC_NOPASSWORD) - f.close() + self.assertEquals(req1['Content-length'], '1374') + self.assertEquals(req2['Content-length'], '1374') + self.assert_((b'xxx') in self.conn.reqs[1].data) - dist = Distribution() - cmd = register(dist) - cmd.post_to_server = FakeServer() + def test_password_not_in_file(self): + self.write_file(self.rc, PYPIRC_NOPASSWORD) + cmd = self._get_cmd() cmd._set_config() cmd.finalize_options() cmd.send_metadata() # dist.password should be set # therefore used afterwards by other commands - self.assertEquals(dist.password, 'password') + self.assertEquals(cmd.distribution.password, 'password') + + def test_registering(self): + # this test runs choice 2 + cmd = self._get_cmd() + inputs = Inputs('2', 'tarek', 'tarek@ziade.org') + register_module.input = inputs.__call__ + try: + # let's run the command + cmd.run() + finally: + del register_module.input + + # we should have send a request + self.assert_(self.conn.reqs, 1) + req = self.conn.reqs[0] + headers = dict(req.headers) + self.assertEquals(headers['Content-length'], '608') + self.assert_((b'tarek') in req.data) + + def test_password_reset(self): + # this test runs choice 3 + cmd = self._get_cmd() + inputs = Inputs('3', 'tarek@ziade.org') + register_module.input = inputs.__call__ + try: + # let's run the command + cmd.run() + finally: + del register_module.input + + # we should have send a request + self.assert_(self.conn.reqs, 1) + req = self.conn.reqs[0] + headers = dict(req.headers) + self.assertEquals(headers['Content-length'], '290') + self.assert_((b'tarek') in req.data) def test_suite(): return unittest.makeSuite(registerTestCase) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 15a8c808..8af080f9 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -34,7 +34,7 @@ somecode%(sep)sdoc.dat somecode%(sep)sdoc.txt """ -class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): +class sdistTestCase(PyPIRCCommandTestCase): def setUp(self): # PyPIRCCommandTestCase creates a temp dir already diff --git a/tests/test_upload.py b/tests/test_upload.py index 3f8ca6d6..95e4ac33 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -2,6 +2,7 @@ import sys import os import unittest +import http.client as httpclient from distutils.command.upload import upload from distutils.core import Distribution @@ -18,17 +19,52 @@ index-servers = [server1] username:me """ +class Response(object): + def __init__(self, status=200, reason='OK'): + self.status = status + self.reason = reason +class FakeConnection(object): + + def __init__(self): + self.requests = [] + self.headers = [] + self.body = '' + + def __call__(self, netloc): + return self + + def connect(self): + pass + endheaders = connect + + def putrequest(self, method, url): + self.requests.append((method, url)) + + def putheader(self, name, value): + self.headers.append((name, value)) + + def send(self, body): + self.body = body + + def getresponse(self): + return Response() class uploadTestCase(PyPIRCCommandTestCase): + def setUp(self): + super(uploadTestCase, self).setUp() + self.old_class = httpclient.HTTPConnection + self.conn = httpclient.HTTPConnection = FakeConnection() + + def tearDown(self): + httpclient.HTTPConnection = self.old_class + super(uploadTestCase, self).tearDown() + def test_finalize_options(self): # new format - f = open(self.rc, 'w') - f.write(PYPIRC) - f.close() - + self.write_file(self.rc, PYPIRC) dist = Distribution() cmd = upload(dist) cmd.finalize_options() @@ -39,9 +75,7 @@ class uploadTestCase(PyPIRCCommandTestCase): def test_saved_password(self): # file with no password - f = open(self.rc, 'w') - f.write(PYPIRC_NOPASSWORD) - f.close() + self.write_file(self.rc, PYPIRC_NOPASSWORD) # make sure it passes dist = Distribution() @@ -56,6 +90,28 @@ class uploadTestCase(PyPIRCCommandTestCase): cmd.finalize_options() self.assertEquals(cmd.password, 'xxx') + def test_upload(self): + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path) + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + self.write_file(self.rc, PYPIRC) + + # lets run it + pkg_dir, dist = self.create_dist(dist_files=dist_files) + cmd = upload(dist) + cmd.ensure_finalized() + cmd.run() + + # what did we send ? + headers = dict(self.conn.headers) + self.assertEquals(headers['Content-length'], '2087') + self.assert_(headers['Content-type'].startswith('multipart/form-data')) + + self.assertEquals(self.conn.requests, [('POST', '/pypi')]) + self.assert_((b'xxx') in self.conn.body) + def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From d2f43a0e5bd8df91da76da9aa45b34f218914336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 22:27:23 +0000 Subject: #5583 Added optional Extensions in Distutils --- command/build_ext.py | 8 +++++++- extension.py | 5 +++++ tests/test_build_ext.py | 22 +++++++++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 125fa7f5..905fa1ff 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -476,7 +476,13 @@ class build_ext (Command): self.check_extensions_list(self.extensions) for ext in self.extensions: - self.build_extension(ext) + try: + self.build_extension(ext) + except (CCompilerError, DistutilsError), e: + if not ext.optional: + raise + self.warn('building extension "%s" failed: %s' % + (ext.name, e)) def build_extension(self, ext): sources = ext.sources diff --git a/extension.py b/extension.py index 440d128c..c80c61e3 100644 --- a/extension.py +++ b/extension.py @@ -83,6 +83,9 @@ class Extension: language : string extension language (i.e. "c", "c++", "objc"). Will be detected from the source extensions if not provided. + optional : boolean + specifies that a build failure in the extension should not abort the + build process, but simply not install the failing extension. """ # When adding arguments to this constructor, be sure to update @@ -101,6 +104,7 @@ class Extension: swig_opts = None, depends=None, language=None, + optional=None, **kw # To catch unknown keywords ): assert type(name) is StringType, "'name' must be a string" @@ -123,6 +127,7 @@ class Extension: self.swig_opts = swig_opts or [] self.depends = depends or [] self.language = language + self.optional = optional # If there are unknown keyword options, warn about them if len(kw): diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index ff60c12b..a27696d8 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -8,6 +8,8 @@ from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig from distutils.tests import support +from distutils.extension import Extension +from distutils.errors import UnknownFileError import unittest from test import test_support @@ -20,7 +22,9 @@ def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') return os.path.join(srcdir, 'Modules', 'xxmodule.c') -class BuildExtTestCase(support.TempdirManager, unittest.TestCase): +class BuildExtTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path @@ -142,6 +146,22 @@ class BuildExtTestCase(support.TempdirManager, unittest.TestCase): self.assert_(lib in cmd.library_dirs) self.assert_(incl in cmd.include_dirs) + def test_optional_extension(self): + + # this extension will fail, but let's ignore this failure + # with the optional argument. + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.ensure_finalized() + self.assertRaises(UnknownFileError, cmd.run) # should raise an error + + modules = [Extension('foo', ['xxx'], optional=True)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.ensure_finalized() + cmd.run() # should pass + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 0aee51d142b182b216825edf7272e0d913064b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 22:37:55 +0000 Subject: Merged revisions 70910 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70910 | tarek.ziade | 2009-03-31 17:27:23 -0500 (Tue, 31 Mar 2009) | 1 line #5583 Added optional Extensions in Distutils ........ --- command/build_ext.py | 8 +++++++- extension.py | 5 +++++ tests/test_build_ext.py | 23 ++++++++++++++++++++++- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 1ed69f33..22959066 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -455,7 +455,13 @@ class build_ext(Command): self.check_extensions_list(self.extensions) for ext in self.extensions: - self.build_extension(ext) + try: + self.build_extension(ext) + except (CCompilerError, DistutilsError) as e: + if not ext.optional: + raise + self.warn('building extension "%s" failed: %s' % + (ext.name, e)) def build_extension(self, ext): sources = ext.sources diff --git a/extension.py b/extension.py index b2718168..f7e7b4ed 100644 --- a/extension.py +++ b/extension.py @@ -82,6 +82,9 @@ class Extension: language : string extension language (i.e. "c", "c++", "objc"). Will be detected from the source extensions if not provided. + optional : boolean + specifies that a build failure in the extension should not abort the + build process, but simply not install the failing extension. """ # When adding arguments to this constructor, be sure to update @@ -100,6 +103,7 @@ class Extension: swig_opts = None, depends=None, language=None, + optional=None, **kw # To catch unknown keywords ): assert isinstance(name, str), "'name' must be a string" @@ -122,6 +126,7 @@ class Extension: self.swig_opts = swig_opts or [] self.depends = depends or [] self.language = language + self.optional = optional # If there are unknown keyword options, warn about them if len(kw): diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 2e47953e..094f4b66 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -8,6 +8,9 @@ from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig from distutils.tests.support import TempdirManager +from distutils.tests.support import LoggingSilencer +from distutils.extension import Extension +from distutils.errors import UnknownFileError import unittest from test import support @@ -20,7 +23,9 @@ def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') return os.path.join(srcdir, 'Modules', 'xxmodule.c') -class BuildExtTestCase(TempdirManager, unittest.TestCase): +class BuildExtTestCase(TempdirManager, + LoggingSilencer, + unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path @@ -141,6 +146,22 @@ class BuildExtTestCase(TempdirManager, unittest.TestCase): self.assert_(lib in cmd.library_dirs) self.assert_(incl in cmd.include_dirs) + def test_optional_extension(self): + + # this extension will fail, but let's ignore this failure + # with the optional argument. + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.ensure_finalized() + self.assertRaises(UnknownFileError, cmd.run) # should raise an error + + modules = [Extension('foo', ['xxx'], optional=True)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.ensure_finalized() + cmd.run() # should pass + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 0396d17ef15568fc9260ec230ee523cf8649014b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 22:44:10 +0000 Subject: catching msvc9compiler error as well --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 905fa1ff..2c6df1d2 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -478,7 +478,7 @@ class build_ext (Command): for ext in self.extensions: try: self.build_extension(ext) - except (CCompilerError, DistutilsError), e: + except (CCompilerError, DistutilsError, CompileError), e: if not ext.optional: raise self.warn('building extension "%s" failed: %s' % -- cgit v1.2.1 From 8d3a9ec909afa64b86fc44749e9e8e70960b5eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 22:47:01 +0000 Subject: fixed the test for win32 CompileError --- tests/test_build_ext.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index a27696d8..9097bee2 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -10,6 +10,7 @@ from distutils import sysconfig from distutils.tests import support from distutils.extension import Extension from distutils.errors import UnknownFileError +from distutils.errors import CompileError import unittest from test import test_support @@ -154,7 +155,8 @@ class BuildExtTestCase(support.TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = build_ext(dist) cmd.ensure_finalized() - self.assertRaises(UnknownFileError, cmd.run) # should raise an error + self.assertRaises((UnknownFileError, CompileError), + cmd.run) # should raise an error modules = [Extension('foo', ['xxx'], optional=True)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) -- cgit v1.2.1 From e7ed75cadeb3cc8d852182dbb771c3894921c88f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 31 Mar 2009 22:50:54 +0000 Subject: Merged revisions 70920,70922 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70920 | tarek.ziade | 2009-03-31 17:44:10 -0500 (Tue, 31 Mar 2009) | 1 line catching msvc9compiler error as well ........ r70922 | tarek.ziade | 2009-03-31 17:47:01 -0500 (Tue, 31 Mar 2009) | 1 line fixed the test for win32 CompileError ........ --- command/build_ext.py | 2 +- tests/test_build_ext.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 22959066..ade95be2 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -457,7 +457,7 @@ class build_ext(Command): for ext in self.extensions: try: self.build_extension(ext) - except (CCompilerError, DistutilsError) as e: + except (CCompilerError, DistutilsError, CompileError) as e: if not ext.optional: raise self.warn('building extension "%s" failed: %s' % diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 094f4b66..5ea67bed 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -11,6 +11,7 @@ from distutils.tests.support import TempdirManager from distutils.tests.support import LoggingSilencer from distutils.extension import Extension from distutils.errors import UnknownFileError +from distutils.errors import CompileError import unittest from test import support @@ -154,7 +155,8 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = build_ext(dist) cmd.ensure_finalized() - self.assertRaises(UnknownFileError, cmd.run) # should raise an error + self.assertRaises((UnknownFileError, CompileError), + cmd.run) # should raise an error modules = [Extension('foo', ['xxx'], optional=True)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) -- cgit v1.2.1 From 0c34a6afb92b1530da9c19f1b96b2371aa6ff606 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 1 Apr 2009 04:28:33 +0000 Subject: #5624: _winreg is winreg in Python 3. --- tests/test_msvc9compiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index aefadd6d..11e5a476 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -48,8 +48,8 @@ class msvc9compilerTestCase(unittest.TestCase): v = Reg.get_value(path, "lfitalic") self.assert_(v in (0, 1)) - import _winreg - HKCU = _winreg.HKEY_CURRENT_USER + import winreg + HKCU = winreg.HKEY_CURRENT_USER keys = Reg.read_keys(HKCU, 'xxxx') self.assertEquals(keys, None) -- cgit v1.2.1 From 865250689f39b67167b128293e9fb3ef3d0aad3e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 1 Apr 2009 04:32:39 +0000 Subject: #5631: add upload to list of possible commands, which is presented in --help-commands. --- command/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/__init__.py b/command/__init__.py index add83f87..274cb010 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -22,6 +22,8 @@ __all__ = ['build', 'bdist_dumb', 'bdist_rpm', 'bdist_wininst', + 'upload', + # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', -- cgit v1.2.1 From 1b3f5a086d20cde5ddb2e399336974aac4291c8d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 4 Apr 2009 21:06:52 +0000 Subject: bump version to 3.1a2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 12a430ec..c6680636 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1a1" +__version__ = "3.1a2" #--end constants-- -- cgit v1.2.1 From 528d035a1887b9cd0488a3e53b138b58a48842aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 5 Apr 2009 18:31:24 +0000 Subject: Fixed 5694: removed spurious test output in Distutils --- tests/test_clean.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_clean.py b/tests/test_clean.py index a94a812b..30260327 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -8,6 +8,7 @@ from distutils.command.clean import clean from distutils.tests import support class cleanTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_simple_run(self): -- cgit v1.2.1 From 9b5d00d842645f4226911f1f835a5bb90557e8d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 5 Apr 2009 18:33:34 +0000 Subject: Merged revisions 71253 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71253 | tarek.ziade | 2009-04-05 20:31:24 +0200 (Sun, 05 Apr 2009) | 1 line Fixed 5694: removed spurious test output in Distutils ........ --- tests/test_clean.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_clean.py b/tests/test_clean.py index a94a812b..30260327 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -8,6 +8,7 @@ from distutils.command.clean import clean from distutils.tests import support class cleanTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_simple_run(self): -- cgit v1.2.1 From 7a3178f5a9438241874c2cbee10b89c43831e2d8 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 5 Apr 2009 19:13:16 +0000 Subject: Merged revisions 70712,70714,70764-70765,70769-70771,70773,70776-70777,70788-70789,70824,70828,70832,70836,70842,70851,70855,70857,70866-70872,70883,70885,70893-70894,70896-70897,70903,70905-70907,70915,70927,70933,70951,70960,70962-70964,70998,71001,71006,71008,71010-71011,71019,71037,71056,71094,71101-71103,71106,71119,71123,71149-71150,71203,71212,71214-71217,71221,71240 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70712 | benjamin.peterson | 2009-03-30 10:15:38 -0500 (Mon, 30 Mar 2009) | 1 line don't rely on the order dict repr #5605 ........ r70714 | brett.cannon | 2009-03-30 10:20:53 -0500 (Mon, 30 Mar 2009) | 1 line Add an entry to developers.txt. ........ r70764 | martin.v.loewis | 2009-03-30 17:06:33 -0500 (Mon, 30 Mar 2009) | 2 lines Add several VM developers. ........ r70765 | georg.brandl | 2009-03-30 17:09:34 -0500 (Mon, 30 Mar 2009) | 1 line #5199: make warning about vars() assignment more visible. ........ r70769 | andrew.kuchling | 2009-03-30 17:29:53 -0500 (Mon, 30 Mar 2009) | 1 line Remove comment ........ r70770 | andrew.kuchling | 2009-03-30 17:30:20 -0500 (Mon, 30 Mar 2009) | 1 line Add several items and placeholders ........ r70771 | andrew.kuchling | 2009-03-30 17:31:11 -0500 (Mon, 30 Mar 2009) | 1 line Many edits ........ r70773 | georg.brandl | 2009-03-30 17:43:00 -0500 (Mon, 30 Mar 2009) | 1 line #5039: make it clear that the impl. note refers to CPython. ........ r70776 | andrew.kuchling | 2009-03-30 18:08:24 -0500 (Mon, 30 Mar 2009) | 1 line typo fix ........ r70777 | andrew.kuchling | 2009-03-30 18:09:46 -0500 (Mon, 30 Mar 2009) | 1 line Add more items ........ r70788 | andrew.kuchling | 2009-03-30 20:21:01 -0500 (Mon, 30 Mar 2009) | 1 line Add various items ........ r70789 | georg.brandl | 2009-03-30 20:25:15 -0500 (Mon, 30 Mar 2009) | 1 line Fix a wrong struct field assignment (docstring as closure). ........ r70824 | georg.brandl | 2009-03-31 10:43:20 -0500 (Tue, 31 Mar 2009) | 1 line #5519: remove reference to Kodos, which seems dead. ........ r70828 | georg.brandl | 2009-03-31 10:50:16 -0500 (Tue, 31 Mar 2009) | 1 line #5581: fget argument of abstractproperty is optional as well. ........ r70832 | georg.brandl | 2009-03-31 11:31:11 -0500 (Tue, 31 Mar 2009) | 1 line #1386675: specify WindowsError as the exception, because it has a winerror attribute that EnvironmentError doesnt have. ........ r70836 | georg.brandl | 2009-03-31 11:50:25 -0500 (Tue, 31 Mar 2009) | 1 line #5417: replace references to undocumented functions by ones to documented functions. ........ r70842 | georg.brandl | 2009-03-31 12:13:06 -0500 (Tue, 31 Mar 2009) | 1 line #970783: document PyObject_Generic[GS]etAttr. ........ r70851 | georg.brandl | 2009-03-31 13:26:55 -0500 (Tue, 31 Mar 2009) | 1 line #837577: note cryptic return value of spawn*e on invalid env dicts. ........ r70855 | georg.brandl | 2009-03-31 13:30:37 -0500 (Tue, 31 Mar 2009) | 1 line #5245: note that PyRun_SimpleString doesnt return on SystemExit. ........ r70857 | georg.brandl | 2009-03-31 13:33:10 -0500 (Tue, 31 Mar 2009) | 1 line #5227: note that Py_Main doesnt return on SystemExit. ........ r70866 | georg.brandl | 2009-03-31 14:06:57 -0500 (Tue, 31 Mar 2009) | 1 line #4882: document named group behavior a bit better. ........ r70867 | georg.brandl | 2009-03-31 14:10:35 -0500 (Tue, 31 Mar 2009) | 1 line #1096310: document usage of sys.__std*__ a bit better. ........ r70868 | georg.brandl | 2009-03-31 14:12:17 -0500 (Tue, 31 Mar 2009) | 1 line #5190: export make_option in __all__. ........ r70869 | georg.brandl | 2009-03-31 14:14:42 -0500 (Tue, 31 Mar 2009) | 1 line Fix-up unwanted change. ........ r70870 | georg.brandl | 2009-03-31 14:26:24 -0500 (Tue, 31 Mar 2009) | 1 line #4411: document mro() and __mro__. (I hope I got it right.) ........ r70871 | georg.brandl | 2009-03-31 14:30:56 -0500 (Tue, 31 Mar 2009) | 1 line #5618: fix typo. ........ r70872 | r.david.murray | 2009-03-31 14:31:17 -0500 (Tue, 31 Mar 2009) | 3 lines Delete out-of-date and little-known README from the test directory by consensus of devs at pycon sprint. ........ r70883 | georg.brandl | 2009-03-31 15:41:08 -0500 (Tue, 31 Mar 2009) | 1 line #1674032: return value of flag from Event.wait(). OKed by Guido. ........ r70885 | tarek.ziade | 2009-03-31 15:48:31 -0500 (Tue, 31 Mar 2009) | 1 line using log.warn for sys.stderr ........ r70893 | georg.brandl | 2009-03-31 15:56:32 -0500 (Tue, 31 Mar 2009) | 1 line #1530012: move TQS section before raw strings. ........ r70894 | benjamin.peterson | 2009-03-31 16:06:30 -0500 (Tue, 31 Mar 2009) | 1 line take the usual lock precautions around _active_limbo_lock ........ r70896 | georg.brandl | 2009-03-31 16:15:33 -0500 (Tue, 31 Mar 2009) | 1 line #5598: document DocFileSuite *args argument. ........ r70897 | benjamin.peterson | 2009-03-31 16:34:42 -0500 (Tue, 31 Mar 2009) | 1 line fix Thread.ident when it is the main thread or a dummy thread #5632 ........ r70903 | georg.brandl | 2009-03-31 16:45:18 -0500 (Tue, 31 Mar 2009) | 1 line #1676135: remove trailing slashes from --prefix argument. ........ r70905 | georg.brandl | 2009-03-31 17:03:40 -0500 (Tue, 31 Mar 2009) | 1 line #5563: more documentation for bdist_msi. ........ r70906 | georg.brandl | 2009-03-31 17:11:53 -0500 (Tue, 31 Mar 2009) | 1 line #1651995: fix _convert_ref for non-ASCII characters. ........ r70907 | georg.brandl | 2009-03-31 17:18:19 -0500 (Tue, 31 Mar 2009) | 1 line #3427: document correct return type for urlopen().info(). ........ r70915 | georg.brandl | 2009-03-31 17:40:16 -0500 (Tue, 31 Mar 2009) | 1 line #5018: remove confusing paragraph. ........ r70927 | georg.brandl | 2009-03-31 18:01:27 -0500 (Tue, 31 Mar 2009) | 1 line Dont shout to users. ........ r70933 | georg.brandl | 2009-03-31 19:04:33 -0500 (Tue, 31 Mar 2009) | 2 lines Issue #5635: Fix running test_sys with tracing enabled. ........ r70951 | georg.brandl | 2009-04-01 09:02:27 -0500 (Wed, 01 Apr 2009) | 1 line Add Maksim, who worked on several issues at the sprint. ........ r70960 | jesse.noller | 2009-04-01 11:42:19 -0500 (Wed, 01 Apr 2009) | 1 line Issue 3270: document Listener address restrictions on windows ........ r70962 | brett.cannon | 2009-04-01 12:07:16 -0500 (Wed, 01 Apr 2009) | 2 lines Ron DuPlain was given commit privileges at PyCon 2009 to work on 3to2. ........ r70963 | georg.brandl | 2009-04-01 12:46:01 -0500 (Wed, 01 Apr 2009) | 1 line #5655: fix docstring oversight. ........ r70964 | brett.cannon | 2009-04-01 12:52:13 -0500 (Wed, 01 Apr 2009) | 2 lines Paul Kippes was given commit privileges to work on 3to2. ........ r70998 | georg.brandl | 2009-04-01 16:54:21 -0500 (Wed, 01 Apr 2009) | 1 line In Pdb, stop assigning values to __builtin__._ which interferes with the one commonly installed by gettext. ........ r71001 | brett.cannon | 2009-04-01 18:01:12 -0500 (Wed, 01 Apr 2009) | 3 lines Add my initials to Misc/developers.txt. Names are now sorted by number of characters in the person's name. ........ r71006 | georg.brandl | 2009-04-01 18:32:17 -0500 (Wed, 01 Apr 2009) | 1 line Cache the f_locals dict of the current frame, since every access to frame.f_locals overrides its contents with the real locals which undoes modifications made by the debugging user. ........ r71008 | andrew.kuchling | 2009-04-01 19:02:14 -0500 (Wed, 01 Apr 2009) | 1 line Typo fix ........ r71010 | benjamin.peterson | 2009-04-01 19:11:52 -0500 (Wed, 01 Apr 2009) | 1 line fix markup ........ r71011 | benjamin.peterson | 2009-04-01 19:12:47 -0500 (Wed, 01 Apr 2009) | 1 line this should be :noindex: ........ r71019 | georg.brandl | 2009-04-01 21:00:01 -0500 (Wed, 01 Apr 2009) | 1 line Fix test_doctest, missed two assignments to curframe. ........ r71037 | r.david.murray | 2009-04-01 23:34:04 -0500 (Wed, 01 Apr 2009) | 6 lines Clarify that datetime strftime does not produce leap seconds and datetime strptime does not accept it in the strftime behavior section of the datetime docs. Closes issue 2568. ........ r71056 | georg.brandl | 2009-04-02 12:43:07 -0500 (Thu, 02 Apr 2009) | 2 lines Actually the displayhook should print the repr. ........ r71094 | vinay.sajip | 2009-04-03 05:23:18 -0500 (Fri, 03 Apr 2009) | 1 line Added warning about logging use from asynchronous signal handlers. ........ r71101 | andrew.kuchling | 2009-04-03 16:43:00 -0500 (Fri, 03 Apr 2009) | 1 line Add some items ........ r71102 | andrew.kuchling | 2009-04-03 16:44:49 -0500 (Fri, 03 Apr 2009) | 1 line Fix 'the the'; grammar fix ........ r71103 | andrew.kuchling | 2009-04-03 16:45:29 -0500 (Fri, 03 Apr 2009) | 1 line Fix 'the the' duplication ........ r71106 | vinay.sajip | 2009-04-03 16:58:16 -0500 (Fri, 03 Apr 2009) | 1 line Clarified warning about logging use from asynchronous signal handlers. ........ r71119 | raymond.hettinger | 2009-04-04 00:37:47 -0500 (Sat, 04 Apr 2009) | 1 line Add helpful link. ........ r71123 | r.david.murray | 2009-04-04 01:39:56 -0500 (Sat, 04 Apr 2009) | 2 lines Fix error in description of 'oct' (issue 5678). ........ r71149 | georg.brandl | 2009-04-04 08:42:39 -0500 (Sat, 04 Apr 2009) | 1 line #5642: clarify map() compatibility to the builtin. ........ r71150 | georg.brandl | 2009-04-04 08:45:49 -0500 (Sat, 04 Apr 2009) | 1 line #5601: clarify that webbrowser is not meant for file names. ........ r71203 | benjamin.peterson | 2009-04-04 18:46:34 -0500 (Sat, 04 Apr 2009) | 1 line note how using iter* are unsafe while mutating and document iter(dict) ........ r71212 | georg.brandl | 2009-04-05 05:24:20 -0500 (Sun, 05 Apr 2009) | 1 line #1742837: expand HTTP server docs, and fix SocketServer ones to document methods as methods, not functions. ........ r71214 | georg.brandl | 2009-04-05 05:29:57 -0500 (Sun, 05 Apr 2009) | 1 line Normalize spelling of Mac OS X. ........ r71215 | georg.brandl | 2009-04-05 05:32:26 -0500 (Sun, 05 Apr 2009) | 1 line Avoid sure signs of a diseased mind. ........ r71216 | georg.brandl | 2009-04-05 05:41:02 -0500 (Sun, 05 Apr 2009) | 1 line #1718017: document the relation of os.path and the posixpath, ntpath etc. modules better. ........ r71217 | georg.brandl | 2009-04-05 05:48:47 -0500 (Sun, 05 Apr 2009) | 1 line #1726172: dont raise an unexpected IndexError if a voidresp() call has an empty response. ........ r71221 | vinay.sajip | 2009-04-05 06:06:24 -0500 (Sun, 05 Apr 2009) | 1 line Issue #5695: Moved logging.captureWarnings() call inside with statement in WarningsTest.test_warnings. ........ r71240 | georg.brandl | 2009-04-05 09:40:06 -0500 (Sun, 05 Apr 2009) | 1 line #5370: doc update about unpickling objects with custom __getattr__ etc. methods. ........ --- cmd.py | 3 ++- log.py | 13 +++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cmd.py b/cmd.py index 46055b4b..5829a566 100644 --- a/cmd.py +++ b/cmd.py @@ -333,7 +333,8 @@ class Command: # -- External world manipulation ----------------------------------- def warn(self, msg): - log.warn("warning: %s: %s\n" % (self.get_command_name(), msg)) + log.warn("warning: %s: %s\n" % + (self.get_command_name(), msg)) def execute(self, func, args, msg=None, level=1): util.execute(func, args, msg, dry_run=self.dry_run) diff --git a/log.py b/log.py index 97319a07..6f949d51 100644 --- a/log.py +++ b/log.py @@ -18,13 +18,14 @@ class Log: def _log(self, level, msg, args): if level >= self.threshold: - if not args: - # msg may contain a '%'. If args is empty, - # don't even try to string-format - print(msg) + if args: + msg = msg % args + if level in (WARN, ERROR, FATAL): + stream = sys.stderr else: - print(msg % args) - sys.stdout.flush() + stream = sys.stdout + stream.write('%s\n' % msg) + stream.flush() def log(self, level, msg, *args): self._log(level, msg, args) -- cgit v1.2.1 From ba7be325ab789b28c9670887397c6924d42a8cf9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 5 Apr 2009 21:11:43 +0000 Subject: Merged revisions 70642,70648,70656,70661,70765,70773,70789,70824-70825,70828,70830,70832,70836,70838,70842,70851,70855,70857-70858 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r70642 | georg.brandl | 2009-03-28 01:48:48 +0100 (Sa, 28 Mär 2009) | 1 line Fix typo. ........ r70648 | georg.brandl | 2009-03-28 20:10:37 +0100 (Sa, 28 Mär 2009) | 1 line #5324: document __subclasses__(). ........ r70656 | georg.brandl | 2009-03-28 20:33:33 +0100 (Sa, 28 Mär 2009) | 2 lines Add a script to fixup rst files if the pre-commit hook rejects them. ........ r70661 | georg.brandl | 2009-03-28 20:57:36 +0100 (Sa, 28 Mär 2009) | 2 lines Add section numbering to some of the larger subdocuments. ........ r70765 | georg.brandl | 2009-03-31 00:09:34 +0200 (Di, 31 Mär 2009) | 1 line #5199: make warning about vars() assignment more visible. ........ r70773 | georg.brandl | 2009-03-31 00:43:00 +0200 (Di, 31 Mär 2009) | 1 line #5039: make it clear that the impl. note refers to CPython. ........ r70789 | georg.brandl | 2009-03-31 03:25:15 +0200 (Di, 31 Mär 2009) | 1 line Fix a wrong struct field assignment (docstring as closure). ........ r70824 | georg.brandl | 2009-03-31 17:43:20 +0200 (Di, 31 Mär 2009) | 1 line #5519: remove reference to Kodos, which seems dead. ........ r70825 | georg.brandl | 2009-03-31 17:46:30 +0200 (Di, 31 Mär 2009) | 1 line #5566: fix versionadded from PyLong ssize_t functions. ........ r70828 | georg.brandl | 2009-03-31 17:50:16 +0200 (Di, 31 Mär 2009) | 1 line #5581: fget argument of abstractproperty is optional as well. ........ r70830 | georg.brandl | 2009-03-31 18:11:45 +0200 (Di, 31 Mär 2009) | 1 line #5529: backport new docs of import semantics written by Brett to 2.x. ........ r70832 | georg.brandl | 2009-03-31 18:31:11 +0200 (Di, 31 Mär 2009) | 1 line #1386675: specify WindowsError as the exception, because it has a winerror attribute that EnvironmentError doesnt have. ........ r70836 | georg.brandl | 2009-03-31 18:50:25 +0200 (Di, 31 Mär 2009) | 1 line #5417: replace references to undocumented functions by ones to documented functions. ........ r70838 | georg.brandl | 2009-03-31 18:54:38 +0200 (Di, 31 Mär 2009) | 1 line #992207: document that the parser only accepts \\n newlines. ........ r70842 | georg.brandl | 2009-03-31 19:13:06 +0200 (Di, 31 Mär 2009) | 1 line #970783: document PyObject_Generic[GS]etAttr. ........ r70851 | georg.brandl | 2009-03-31 20:26:55 +0200 (Di, 31 Mär 2009) | 1 line #837577: note cryptic return value of spawn*e on invalid env dicts. ........ r70855 | georg.brandl | 2009-03-31 20:30:37 +0200 (Di, 31 Mär 2009) | 1 line #5245: note that PyRun_SimpleString doesnt return on SystemExit. ........ r70857 | georg.brandl | 2009-03-31 20:33:10 +0200 (Di, 31 Mär 2009) | 1 line #5227: note that Py_Main doesnt return on SystemExit. ........ r70858 | georg.brandl | 2009-03-31 20:38:56 +0200 (Di, 31 Mär 2009) | 1 line #5241: document missing U in regex howto. ........ --- version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.py b/version.py index 71a56147..0fb5b6e2 100644 --- a/version.py +++ b/version.py @@ -162,7 +162,7 @@ class StrictVersion (Version): # The rules according to Greg Stein: -# 1) a version number has 1 or more numbers separate by a period or by +# 1) a version number has 1 or more numbers separated by a period or by # sequences of letters. If only periods, then these are compared # left-to-right to determine an ordering. # 2) sequences of letters are part of the tuple for comparison and are -- cgit v1.2.1 From 443bc8fdb3ffe7aded9326275131e10a7bc6286b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 5 Apr 2009 21:21:05 +0000 Subject: Merged revisions 70866-70868,70870-70871,70893,70896,70902,70905,70907,70912,70915,70927,70933,70940,70944,70954,70963,70998,71056 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r70866 | georg.brandl | 2009-03-31 21:06:57 +0200 (Di, 31 Mär 2009) | 1 line #4882: document named group behavior a bit better. ........ r70867 | georg.brandl | 2009-03-31 21:10:35 +0200 (Di, 31 Mär 2009) | 1 line #1096310: document usage of sys.__std*__ a bit better. ........ r70868 | georg.brandl | 2009-03-31 21:12:17 +0200 (Di, 31 Mär 2009) | 1 line #5190: export make_option in __all__. ........ r70870 | georg.brandl | 2009-03-31 21:26:24 +0200 (Di, 31 Mär 2009) | 1 line #4411: document mro() and __mro__. (I hope I got it right.) ........ r70871 | georg.brandl | 2009-03-31 21:30:56 +0200 (Di, 31 Mär 2009) | 1 line #5618: fix typo. ........ r70893 | georg.brandl | 2009-03-31 22:56:32 +0200 (Di, 31 Mär 2009) | 1 line #1530012: move TQS section before raw strings. ........ r70896 | georg.brandl | 2009-03-31 23:15:33 +0200 (Di, 31 Mär 2009) | 1 line #5598: document DocFileSuite *args argument. ........ r70902 | georg.brandl | 2009-03-31 23:43:03 +0200 (Di, 31 Mär 2009) | 1 line #1675026: add a note about a strange Windows problem, and remove notes about AtheOS. ........ r70905 | georg.brandl | 2009-04-01 00:03:40 +0200 (Mi, 01 Apr 2009) | 1 line #5563: more documentation for bdist_msi. ........ r70907 | georg.brandl | 2009-04-01 00:18:19 +0200 (Mi, 01 Apr 2009) | 1 line #3427: document correct return type for urlopen().info(). ........ r70912 | georg.brandl | 2009-04-01 00:35:46 +0200 (Mi, 01 Apr 2009) | 1 line #5617: add a handy function to print a unicode string to gdbinit. ........ r70915 | georg.brandl | 2009-04-01 00:40:16 +0200 (Mi, 01 Apr 2009) | 1 line #5018: remove confusing paragraph. ........ r70927 | georg.brandl | 2009-04-01 01:01:27 +0200 (Mi, 01 Apr 2009) | 1 line Dont shout to users. ........ r70933 | georg.brandl | 2009-04-01 02:04:33 +0200 (Mi, 01 Apr 2009) | 2 lines Issue #5635: Fix running test_sys with tracing enabled. ........ r70940 | georg.brandl | 2009-04-01 06:21:14 +0200 (Mi, 01 Apr 2009) | 2 lines The SimpleXMLRPCServer's CGI handler now runs like a pony. ........ r70944 | georg.brandl | 2009-04-01 06:32:39 +0200 (Mi, 01 Apr 2009) | 1 line #5631: add upload to list of possible commands, which is presented in --help-commands. ........ r70954 | georg.brandl | 2009-04-01 17:23:43 +0200 (Mi, 01 Apr 2009) | 1 line Fix test_xmlrpc and make the CGI handler work with no CONTENT_LENGTH. ........ r70963 | georg.brandl | 2009-04-01 19:46:01 +0200 (Mi, 01 Apr 2009) | 1 line #5655: fix docstring oversight. ........ r70998 | georg.brandl | 2009-04-01 23:54:21 +0200 (Mi, 01 Apr 2009) | 1 line In Pdb, stop assigning values to __builtin__._ which interferes with the one commonly installed by gettext. ........ r71056 | georg.brandl | 2009-04-02 19:43:07 +0200 (Do, 02 Apr 2009) | 2 lines Actually the displayhook should print the repr. ........ --- command/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/__init__.py b/command/__init__.py index 0888c271..05c758a2 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -24,6 +24,8 @@ __all__ = ['build', 'bdist_dumb', 'bdist_rpm', 'bdist_wininst', + 'upload', + # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', -- cgit v1.2.1 From a8e0178ca10ed3610546d48949baef25dde94222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 5 Apr 2009 21:44:08 +0000 Subject: Fixed #1491431: distutils.filelist.glob_to_re was broken for some edge cases (detailed in the test --- filelist.py | 5 +++-- tests/test_filelist.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/test_filelist.py diff --git a/filelist.py b/filelist.py index 51a1d57e..3ba5720c 100644 --- a/filelist.py +++ b/filelist.py @@ -302,7 +302,7 @@ def findall (dir = os.curdir): return list -def glob_to_re (pattern): +def glob_to_re(pattern): """Translate a shell-like glob pattern to a regular expression; return a string containing the regex. Differs from 'fnmatch.translate()' in that '*' does not match "special characters" (which are @@ -317,7 +317,8 @@ def glob_to_re (pattern): # character except the special characters. # XXX currently the "special characters" are just slash -- i.e. this is # Unix-only. - pattern_re = re.sub(r'(^|[^\\])\.', r'\1[^/]', pattern_re) + pattern_re = re.sub(r'((? Date: Sun, 5 Apr 2009 21:47:02 +0000 Subject: Merged revisions 71280 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71280 | tarek.ziade | 2009-04-05 23:44:08 +0200 (Sun, 05 Apr 2009) | 1 line Fixed #1491431: distutils.filelist.glob_to_re was broken for some edge cases (detailed in the test ........ --- filelist.py | 5 +++-- tests/test_filelist.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/test_filelist.py diff --git a/filelist.py b/filelist.py index 6d27cce6..3ce56350 100644 --- a/filelist.py +++ b/filelist.py @@ -304,7 +304,7 @@ def findall (dir = os.curdir): return list -def glob_to_re (pattern): +def glob_to_re(pattern): """Translate a shell-like glob pattern to a regular expression; return a string containing the regex. Differs from 'fnmatch.translate()' in that '*' does not match "special characters" (which are @@ -319,7 +319,8 @@ def glob_to_re (pattern): # character except the special characters. # XXX currently the "special characters" are just slash -- i.e. this is # Unix-only. - pattern_re = re.sub(r'(^|[^\\])\.', r'\1[^/]', pattern_re) + pattern_re = re.sub(r'((? Date: Sun, 5 Apr 2009 21:49:36 +0000 Subject: Merged revisions 71280 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71280 | tarek.ziade | 2009-04-05 23:44:08 +0200 (Sun, 05 Apr 2009) | 1 line Fixed #1491431: distutils.filelist.glob_to_re was broken for some edge cases (detailed in the test ........ --- filelist.py | 3 ++- tests/test_filelist.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/test_filelist.py diff --git a/filelist.py b/filelist.py index a80c71e8..58a2bfb1 100644 --- a/filelist.py +++ b/filelist.py @@ -289,7 +289,8 @@ def glob_to_re(pattern): # character except the special characters. # XXX currently the "special characters" are just slash -- i.e. this is # Unix-only. - pattern_re = re.sub(r'(^|[^\\])\.', r'\1[^/]', pattern_re) + pattern_re = re.sub(r'((? Date: Sun, 5 Apr 2009 22:04:38 +0000 Subject: added a simplest test to distutils.spawn._nt_quote_args --- tests/test_spawn.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/test_spawn.py diff --git a/tests/test_spawn.py b/tests/test_spawn.py new file mode 100644 index 00000000..33e8bcf6 --- /dev/null +++ b/tests/test_spawn.py @@ -0,0 +1,20 @@ +"""Tests for distutils.spawn.""" +import unittest +from distutils.spawn import _nt_quote_args + +class SpawnTestCase(unittest.TestCase): + + def test_nt_quote_args(self): + + for (args, wanted) in ((['with space', 'nospace'], + ['"with space"', 'nospace']), + (['nochange', 'nospace'], + ['nochange', 'nospace'])): + res = _nt_quote_args(args) + self.assertEquals(res, wanted) + +def test_suite(): + return unittest.makeSuite(SpawnTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 9dd99efbbbffa381dd119b49bfe43dc5a64484be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 5 Apr 2009 22:51:09 +0000 Subject: Fixed #5095: msi missing from Distutils bdist formats --- command/bdist.py | 49 ++++++++++++++++--------------------------------- tests/test_bdist.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 33 deletions(-) create mode 100644 tests/test_bdist.py diff --git a/command/bdist.py b/command/bdist.py index 3df5ef4c..cb7598df 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -12,7 +12,7 @@ from distutils.errors import * from distutils.util import get_platform -def show_formats (): +def show_formats(): """Print list of available formats (arguments to "--format" option). """ from distutils.fancy_getopt import FancyGetopt @@ -24,7 +24,7 @@ def show_formats (): pretty_printer.print_help("List of available distribution formats:") -class bdist (Command): +class bdist(Command): description = "create a built (binary) distribution" @@ -50,35 +50,28 @@ class bdist (Command): ] # The following commands do not take a format option from bdist - no_format_option = ('bdist_rpm', - #'bdist_sdux', 'bdist_pkgtool' - ) + no_format_option = ('bdist_rpm',) # This won't do in reality: will need to distinguish RPM-ish Linux, # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. - default_format = { 'posix': 'gztar', - 'nt': 'zip', - 'os2': 'zip', } + default_format = {'posix': 'gztar', + 'nt': 'zip', + 'os2': 'zip'} # Establish the preferred order (for the --help-formats option). format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', - 'wininst', 'zip', - #'pkgtool', 'sdux' - ] + 'wininst', 'zip', 'msi'] # And the real information. - format_command = { 'rpm': ('bdist_rpm', "RPM distribution"), - 'zip': ('bdist_dumb', "ZIP file"), - 'gztar': ('bdist_dumb', "gzip'ed tar file"), - 'bztar': ('bdist_dumb', "bzip2'ed tar file"), - 'ztar': ('bdist_dumb', "compressed tar file"), - 'tar': ('bdist_dumb', "tar file"), - 'wininst': ('bdist_wininst', - "Windows executable installer"), - 'zip': ('bdist_dumb', "ZIP file"), - #'pkgtool': ('bdist_pkgtool', - # "Solaris pkgtool distribution"), - #'sdux': ('bdist_sdux', "HP-UX swinstall depot"), + format_command = {'rpm': ('bdist_rpm', "RPM distribution"), + 'gztar': ('bdist_dumb', "gzip'ed tar file"), + 'bztar': ('bdist_dumb', "bzip2'ed tar file"), + 'ztar': ('bdist_dumb', "compressed tar file"), + 'tar': ('bdist_dumb', "tar file"), + 'wininst': ('bdist_wininst', + "Windows executable installer"), + 'zip': ('bdist_dumb', "ZIP file"), + 'msi': ('bdist_msi', "Microsoft Installer") } @@ -89,9 +82,6 @@ class bdist (Command): self.dist_dir = None self.skip_build = 0 - # initialize_options() - - def finalize_options (self): # have to finalize 'plat_name' before 'bdist_base' if self.plat_name is None: @@ -120,10 +110,7 @@ class bdist (Command): if self.dist_dir is None: self.dist_dir = "dist" - # finalize_options() - def run (self): - # Figure out which sub-commands we need to run. commands = [] for format in self.formats: @@ -144,7 +131,3 @@ class bdist (Command): if cmd_name in commands[i+1:]: sub_cmd.keep_temp = 1 self.run_command(cmd_name) - - # run() - -# class bdist diff --git a/tests/test_bdist.py b/tests/test_bdist.py new file mode 100644 index 00000000..be3ec749 --- /dev/null +++ b/tests/test_bdist.py @@ -0,0 +1,43 @@ +"""Tests for distutils.command.bdist.""" +import unittest +import sys +import os +import tempfile +import shutil + +from distutils.core import Distribution +from distutils.command.bdist import bdist +from distutils.tests import support +from distutils.spawn import find_executable +from distutils import spawn +from distutils.errors import DistutilsExecError + +class BuildTestCase(support.TempdirManager, + unittest.TestCase): + + def test_formats(self): + + # let's create a command and make sure + # we can fix the format + pkg_pth, dist = self.create_dist() + cmd = bdist(dist) + cmd.formats = ['msi'] + cmd.ensure_finalized() + self.assertEquals(cmd.formats, ['msi']) + + # what format bdist offers ? + # XXX an explicit list in bdist is + # not the best way to bdist_* commands + # we should add a registry + formats = ['rpm', 'zip', 'gztar', 'bztar', 'ztar', + 'tar', 'wininst', 'msi'] + formats.sort() + founded = cmd.format_command.keys() + founded.sort() + self.assertEquals(founded, formats) + +def test_suite(): + return unittest.makeSuite(BuildTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From ae1a2a43aeb89b3afbe475cdd5715a9985cf7976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 5 Apr 2009 22:57:21 +0000 Subject: Merged revisions 71291 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71291 | tarek.ziade | 2009-04-06 00:51:09 +0200 (Mon, 06 Apr 2009) | 1 line Fixed #5095: msi missing from Distutils bdist formats ........ --- command/bdist.py | 35 ++++++++++++++--------------------- tests/test_bdist.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 21 deletions(-) create mode 100644 tests/test_bdist.py diff --git a/command/bdist.py b/command/bdist.py index e3b047c6..1a360b59 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -49,35 +49,28 @@ class bdist(Command): ] # The following commands do not take a format option from bdist - no_format_option = ('bdist_rpm', - #'bdist_sdux', 'bdist_pkgtool' - ) + no_format_option = ('bdist_rpm',) # This won't do in reality: will need to distinguish RPM-ish Linux, # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. - default_format = { 'posix': 'gztar', - 'nt': 'zip', - 'os2': 'zip', } + default_format = {'posix': 'gztar', + 'nt': 'zip', + 'os2': 'zip'} # Establish the preferred order (for the --help-formats option). format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', - 'wininst', 'zip', - #'pkgtool', 'sdux' - ] + 'wininst', 'zip', 'msi'] # And the real information. - format_command = { 'rpm': ('bdist_rpm', "RPM distribution"), - 'zip': ('bdist_dumb', "ZIP file"), - 'gztar': ('bdist_dumb', "gzip'ed tar file"), - 'bztar': ('bdist_dumb', "bzip2'ed tar file"), - 'ztar': ('bdist_dumb', "compressed tar file"), - 'tar': ('bdist_dumb', "tar file"), - 'wininst': ('bdist_wininst', - "Windows executable installer"), - 'zip': ('bdist_dumb', "ZIP file"), - #'pkgtool': ('bdist_pkgtool', - # "Solaris pkgtool distribution"), - #'sdux': ('bdist_sdux', "HP-UX swinstall depot"), + format_command = {'rpm': ('bdist_rpm', "RPM distribution"), + 'gztar': ('bdist_dumb', "gzip'ed tar file"), + 'bztar': ('bdist_dumb', "bzip2'ed tar file"), + 'ztar': ('bdist_dumb', "compressed tar file"), + 'tar': ('bdist_dumb', "tar file"), + 'wininst': ('bdist_wininst', + "Windows executable installer"), + 'zip': ('bdist_dumb', "ZIP file"), + 'msi': ('bdist_msi', "Microsoft Installer") } diff --git a/tests/test_bdist.py b/tests/test_bdist.py new file mode 100644 index 00000000..f2849a97 --- /dev/null +++ b/tests/test_bdist.py @@ -0,0 +1,43 @@ +"""Tests for distutils.command.bdist.""" +import unittest +import sys +import os +import tempfile +import shutil + +from distutils.core import Distribution +from distutils.command.bdist import bdist +from distutils.tests import support +from distutils.spawn import find_executable +from distutils import spawn +from distutils.errors import DistutilsExecError + +class BuildTestCase(support.TempdirManager, + unittest.TestCase): + + def test_formats(self): + + # let's create a command and make sure + # we can fix the format + pkg_pth, dist = self.create_dist() + cmd = bdist(dist) + cmd.formats = ['msi'] + cmd.ensure_finalized() + self.assertEquals(cmd.formats, ['msi']) + + # what format bdist offers ? + # XXX an explicit list in bdist is + # not the best way to bdist_* commands + # we should add a registry + formats = ['rpm', 'zip', 'gztar', 'bztar', 'ztar', + 'tar', 'wininst', 'msi'] + formats.sort() + founded = list(cmd.format_command.keys()) + founded.sort() + self.assertEquals(founded, formats) + +def test_suite(): + return unittest.makeSuite(BuildTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 511f865b473f07c3d77f1507fd628f6d0a662a51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 5 Apr 2009 23:03:10 +0000 Subject: pep8-fied method names --- command/bdist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index cb7598df..ec3375e5 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -75,14 +75,14 @@ class bdist(Command): } - def initialize_options (self): + def initialize_options(self): self.bdist_base = None self.plat_name = None self.formats = None self.dist_dir = None self.skip_build = 0 - def finalize_options (self): + def finalize_options(self): # have to finalize 'plat_name' before 'bdist_base' if self.plat_name is None: if self.skip_build: @@ -110,7 +110,7 @@ class bdist(Command): if self.dist_dir is None: self.dist_dir = "dist" - def run (self): + def run(self): # Figure out which sub-commands we need to run. commands = [] for format in self.formats: -- cgit v1.2.1 From 67406893c1b9b727f313a374affe9868ec986fa6 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 7 Apr 2009 01:54:02 +0000 Subject: Bump to 2.6.2c1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8f7eb842..e8f55cf0 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.1" +__version__ = "2.6.2c1" #--end constants-- -- cgit v1.2.1 From 24cf99bffe579c9be03c0d69ea1298e83825d202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 9 Apr 2009 21:36:44 +0000 Subject: Fixed #5731: Distutils bdist_wininst no longer worked on non-Windows platforms --- command/bdist_wininst.py | 11 ++++++++--- tests/test_bdist_wininst.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/test_bdist_wininst.py diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index ad6eee8e..8a6eca59 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -342,10 +342,15 @@ class bdist_wininst (Command): directory = os.path.dirname(__file__) # we must use a wininst-x.y.exe built with the same C compiler # used for python. XXX What about mingw, borland, and so on? - if self.plat_name == 'win32': - sfix = '' + + # if plat_name starts with "win" but is not "win32" + # we want to strip "win" and leave the rest (e.g. -amd64) + # for all other cases, we don't want any suffix + if self.plat_name != 'win32' and self.plat_name[:3] == 'win': + sfix = self.plat_name[3:] else: - sfix = self.plat_name[3:] # strip 'win' - leaves eg '-amd64' + sfix = '' + filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) return open(filename, "rb").read() # class bdist_wininst diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py new file mode 100644 index 00000000..d851d6c7 --- /dev/null +++ b/tests/test_bdist_wininst.py @@ -0,0 +1,29 @@ +"""Tests for distutils.command.bdist_wininst.""" +import unittest + +from distutils.command.bdist_wininst import bdist_wininst +from distutils.tests import support + +class BuildWinInstTestCase(support.TempdirManager, + unittest.TestCase): + + def test_get_exe_bytes(self): + + # issue5731: command was broken on non-windows platforms + # this test makes sure it works now for every platform + # let's create a command + pkg_pth, dist = self.create_dist() + cmd = bdist_wininst(dist) + cmd.ensure_finalized() + + # let's run the code that finds the right wininst*.exe file + # and make sure it finds it and returns its content + # no matter what platform we have + exe_file = cmd.get_exe_bytes() + self.assert_(len(exe_file) > 10) + +def test_suite(): + return unittest.makeSuite(BuildWinInstTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 8723e53f8dcb017b1c6e0d84ced060fdcb336cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 9 Apr 2009 22:02:39 +0000 Subject: Merged revisions 71413 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71413 | tarek.ziade | 2009-04-09 23:36:44 +0200 (Thu, 09 Apr 2009) | 1 line Fixed #5731: Distutils bdist_wininst no longer worked on non-Windows platforms ........ --- command/bdist_wininst.py | 11 ++++++++--- tests/test_bdist_wininst.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 tests/test_bdist_wininst.py diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index e997e8f0..d6d01c63 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -330,9 +330,14 @@ class bdist_wininst(Command): directory = os.path.dirname(__file__) # we must use a wininst-x.y.exe built with the same C compiler # used for python. XXX What about mingw, borland, and so on? - if self.plat_name == 'win32': - sfix = '' + + # if plat_name starts with "win" but is not "win32" + # we want to strip "win" and leave the rest (e.g. -amd64) + # for all other cases, we don't want any suffix + if self.plat_name != 'win32' and self.plat_name[:3] == 'win': + sfix = self.plat_name[3:] else: - sfix = self.plat_name[3:] # strip 'win' - leaves eg '-amd64' + sfix = '' + filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) return open(filename, "rb").read() diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py new file mode 100644 index 00000000..d851d6c7 --- /dev/null +++ b/tests/test_bdist_wininst.py @@ -0,0 +1,29 @@ +"""Tests for distutils.command.bdist_wininst.""" +import unittest + +from distutils.command.bdist_wininst import bdist_wininst +from distutils.tests import support + +class BuildWinInstTestCase(support.TempdirManager, + unittest.TestCase): + + def test_get_exe_bytes(self): + + # issue5731: command was broken on non-windows platforms + # this test makes sure it works now for every platform + # let's create a command + pkg_pth, dist = self.create_dist() + cmd = bdist_wininst(dist) + cmd.ensure_finalized() + + # let's run the code that finds the right wininst*.exe file + # and make sure it finds it and returns its content + # no matter what platform we have + exe_file = cmd.get_exe_bytes() + self.assert_(len(exe_file) > 10) + +def test_suite(): + return unittest.makeSuite(BuildWinInstTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 2c03b265b9bbd461c0275c3d9dac16a57714ec8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 9 Apr 2009 22:48:19 +0000 Subject: Merged revisions 71413 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71413 | tarek.ziade | 2009-04-09 23:36:44 +0200 (Thu, 09 Apr 2009) | 1 line Fixed #5731: Distutils bdist_wininst no longer worked on non-Windows platforms ........ --- command/bdist_wininst.py | 11 ++++++++--- tests/test_bdist_wininst.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 tests/test_bdist_wininst.py diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index f18e318c..d153e2bc 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -344,10 +344,15 @@ class bdist_wininst (Command): directory = os.path.dirname(__file__) # we must use a wininst-x.y.exe built with the same C compiler # used for python. XXX What about mingw, borland, and so on? - if self.plat_name == 'win32': - sfix = '' + + # if plat_name starts with "win" but is not "win32" + # we want to strip "win" and leave the rest (e.g. -amd64) + # for all other cases, we don't want any suffix + if self.plat_name != 'win32' and self.plat_name[:3] == 'win': + sfix = self.plat_name[3:] else: - sfix = self.plat_name[3:] # strip 'win' - leaves eg '-amd64' + sfix = '' + filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) return open(filename, "rb").read() # class bdist_wininst diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py new file mode 100644 index 00000000..de6601fb --- /dev/null +++ b/tests/test_bdist_wininst.py @@ -0,0 +1,34 @@ +"""Tests for distutils.command.bdist_wininst.""" +import unittest +import os + +from distutils.dist import Distribution +from distutils.command.bdist_wininst import bdist_wininst +from distutils.tests import support + +class BuildWinInstTestCase(support.TempdirManager, + unittest.TestCase): + + def test_get_exe_bytes(self): + + # issue5731: command was broken on non-windows platforms + # this test makes sure it works now for every platform + # let's create a command + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + dist = Distribution() + cmd = bdist_wininst(dist) + cmd.ensure_finalized() + + # let's run the code that finds the right wininst*.exe file + # and make sure it finds it and returns its content + # no matter what platform we have + exe_file = cmd.get_exe_bytes() + self.assert_(len(exe_file) > 10) + +def test_suite(): + return unittest.makeSuite(BuildWinInstTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 5ac71381e3864771cbe4d32dcad1630b98b660a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Apr 2009 14:55:07 +0000 Subject: #5732: added the check command into Distutils --- command/__init__.py | 2 +- command/check.py | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_check.py | 92 +++++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 command/check.py create mode 100644 tests/test_check.py diff --git a/command/__init__.py b/command/__init__.py index 274cb010..20b159f7 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -23,7 +23,7 @@ __all__ = ['build', 'bdist_rpm', 'bdist_wininst', 'upload', - + 'check', # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', diff --git a/command/check.py b/command/check.py new file mode 100644 index 00000000..c7291495 --- /dev/null +++ b/command/check.py @@ -0,0 +1,143 @@ +"""distutils.command.check + +Implements the Distutils 'check' command. +""" +__revision__ = "$Id$" + +from distutils.core import Command +from distutils.errors import DistutilsSetupError + +try: + # docutils is installed + from docutils.utils import Reporter + from docutils.parsers.rst import Parser + from docutils import frontend + from docutils import nodes + from StringIO import StringIO + + class SilentReporter(Reporter): + + def __init__(self, source, report_level, halt_level, stream=None, + debug=0, encoding='ascii', error_handler='replace'): + self.messages = [] + Reporter.__init__(self, source, report_level, halt_level, stream, + debug, encoding, error_handler) + + def system_message(self, level, message, *children, **kwargs): + self.messages.append((level, message, children, kwargs)) + + HAS_DOCUTILS = True +except ImportError: + # docutils is not installed + HAS_DOCUTILS = False + +class check(Command): + """This command checks the meta-data of the package. + """ + description = ("perform some checks on the package") + user_options = [('metadata', 'm', 'Verify meta-data'), + ('restructuredtext', 'r', + ('Checks if long string meta-data syntax ' + 'are reStructuredText-compliant')), + ('strict', 's', + 'Will exit with an error if a check fails')] + + boolean_options = ['metadata', 'restructuredtext', 'strict'] + + def initialize_options(self): + """Sets default values for options.""" + self.restructuredtext = 0 + self.metadata = 1 + self.strict = 0 + self._warnings = 0 + + def finalize_options(self): + pass + + def warn(self, msg): + """Counts the number of warnings that occurs.""" + self._warnings += 1 + return Command.warn(self, msg) + + def run(self): + """Runs the command.""" + # perform the various tests + if self.metadata: + self.check_metadata() + if self.restructuredtext: + if docutils: + self.check_restructuredtext() + elif self.strict: + raise DistutilsSetupError('The docutils package is needed.') + + # let's raise an error in strict mode, if we have at least + # one warning + if self.strict and self._warnings > 1: + raise DistutilsSetupError('Please correct your package.') + + def check_metadata(self): + """Ensures that all required elements of meta-data are supplied. + + name, version, URL, (author and author_email) or + (maintainer and maintainer_email)). + + Warns if any are missing. + """ + metadata = self.distribution.metadata + + missing = [] + for attr in ('name', 'version', 'url'): + if not (hasattr(metadata, attr) and getattr(metadata, attr)): + missing.append(attr) + + if missing: + self.warn("missing required meta-data: %s" % ' ,'.join(missing)) + if metadata.author: + if not metadata.author_email: + self.warn("missing meta-data: if 'author' supplied, " + + "'author_email' must be supplied too") + elif metadata.maintainer: + if not metadata.maintainer_email: + self.warn("missing meta-data: if 'maintainer' supplied, " + + "'maintainer_email' must be supplied too") + else: + self.warn("missing meta-data: either (author and author_email) " + + "or (maintainer and maintainer_email) " + + "must be supplied") + + def check_restructuredtext(self): + """Checks if the long string fields are reST-compliant.""" + data = self.distribution.get_long_description() + for warning in self._check_rst_data(data): + line = warning[-1].get('line') + if line is None: + warning = warning[1] + else: + warning = '%s (line %s)' % (warning[1], line) + self.warn(warning) + + def _check_rst_data(self, data): + """Returns warnings when the provided data doesn't compile.""" + source_path = StringIO() + parser = Parser() + settings = frontend.OptionParser().get_default_values() + settings.tab_width = 4 + settings.pep_references = None + settings.rfc_references = None + reporter = SilentReporter(source_path, + settings.report_level, + settings.halt_level, + stream=settings.warning_stream, + debug=settings.debug, + encoding=settings.error_encoding, + error_handler=settings.error_encoding_error_handler) + + document = nodes.document(settings, reporter, source=source_path) + document.note_source(source_path, -1) + try: + parser.parse(data, document) + except AttributeError: + reporter.messages.append((-1, 'Could not finish the parsing.', + '', {})) + + return reporter.messages diff --git a/tests/test_check.py b/tests/test_check.py new file mode 100644 index 00000000..443fa35b --- /dev/null +++ b/tests/test_check.py @@ -0,0 +1,92 @@ +"""Tests for distutils.command.check.""" +import unittest + +from distutils.command.check import check, HAS_DOCUTILS +from distutils.tests import support +from distutils.errors import DistutilsSetupError + +class CheckTestCase(support.LoggingSilencer, + support.TempdirManager, + unittest.TestCase): + + def _run(self, metadata=None, **options): + if metadata is None: + metadata = {} + pkg_info, dist = self.create_dist(**metadata) + cmd = check(dist) + cmd.initialize_options() + for name, value in options.items(): + setattr(cmd, name, value) + cmd.ensure_finalized() + cmd.run() + return cmd + + def test_check_metadata(self): + # let's run the command with no metadata at all + # by default, check is checking the metadata + # should have some warnings + cmd = self._run() + self.assertEquals(cmd._warnings, 2) + + # now let's add the required fields + # and run it again, to make sure we don't get + # any warning anymore + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx'} + cmd = self._run(metadata) + self.assertEquals(cmd._warnings, 0) + + # now with the strict mode, we should + # get an error if there are missing metadata + self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1}) + + # and of course, no error when all metadata are present + cmd = self._run(metadata, strict=1) + self.assertEquals(cmd._warnings, 0) + + def test_check_document(self): + if not HAS_DOCUTILS: # won't test without docutils + return + pkg_info, dist = self.create_dist() + cmd = check(dist) + + # let's see if it detects broken rest + broken_rest = 'title\n===\n\ntest' + msgs = cmd._check_rst_data(broken_rest) + self.assertEquals(len(msgs), 1) + + # and non-broken rest + rest = 'title\n=====\n\ntest' + msgs = cmd._check_rst_data(rest) + self.assertEquals(len(msgs), 0) + + def test_check_restructuredtext(self): + if not HAS_DOCUTILS: # won't test without docutils + return + # let's see if it detects broken rest in long_description + broken_rest = 'title\n===\n\ntest' + pkg_info, dist = self.create_dist(long_description=broken_rest) + cmd = check(dist) + cmd.check_restructuredtext() + self.assertEquals(cmd._warnings, 1) + + # let's see if we have an error with strict=1 + cmd = check(dist) + cmd.initialize_options() + cmd.strict = 1 + cmd.ensure_finalized() + self.assertRaises(DistutilsSetupError, cmd.run) + + # and non-broken rest + rest = 'title\n=====\n\ntest' + pkg_info, dist = self.create_dist(long_description=rest) + cmd = check(dist) + cmd.check_restructuredtext() + self.assertEquals(cmd._warnings, 0) + +def test_suite(): + return unittest.makeSuite(CheckTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 416e5b55de1ffbdb2ced47b9655e3f8facdc299d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Apr 2009 15:00:43 +0000 Subject: Merged revisions 71473 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71473 | tarek.ziade | 2009-04-11 16:55:07 +0200 (Sat, 11 Apr 2009) | 1 line #5732: added the check command into Distutils ........ --- command/__init__.py | 1 + command/check.py | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_check.py | 92 +++++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 command/check.py create mode 100644 tests/test_check.py diff --git a/command/__init__.py b/command/__init__.py index add83f87..f7dcde48 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -22,6 +22,7 @@ __all__ = ['build', 'bdist_dumb', 'bdist_rpm', 'bdist_wininst', + 'check', # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', diff --git a/command/check.py b/command/check.py new file mode 100644 index 00000000..c7291495 --- /dev/null +++ b/command/check.py @@ -0,0 +1,143 @@ +"""distutils.command.check + +Implements the Distutils 'check' command. +""" +__revision__ = "$Id$" + +from distutils.core import Command +from distutils.errors import DistutilsSetupError + +try: + # docutils is installed + from docutils.utils import Reporter + from docutils.parsers.rst import Parser + from docutils import frontend + from docutils import nodes + from StringIO import StringIO + + class SilentReporter(Reporter): + + def __init__(self, source, report_level, halt_level, stream=None, + debug=0, encoding='ascii', error_handler='replace'): + self.messages = [] + Reporter.__init__(self, source, report_level, halt_level, stream, + debug, encoding, error_handler) + + def system_message(self, level, message, *children, **kwargs): + self.messages.append((level, message, children, kwargs)) + + HAS_DOCUTILS = True +except ImportError: + # docutils is not installed + HAS_DOCUTILS = False + +class check(Command): + """This command checks the meta-data of the package. + """ + description = ("perform some checks on the package") + user_options = [('metadata', 'm', 'Verify meta-data'), + ('restructuredtext', 'r', + ('Checks if long string meta-data syntax ' + 'are reStructuredText-compliant')), + ('strict', 's', + 'Will exit with an error if a check fails')] + + boolean_options = ['metadata', 'restructuredtext', 'strict'] + + def initialize_options(self): + """Sets default values for options.""" + self.restructuredtext = 0 + self.metadata = 1 + self.strict = 0 + self._warnings = 0 + + def finalize_options(self): + pass + + def warn(self, msg): + """Counts the number of warnings that occurs.""" + self._warnings += 1 + return Command.warn(self, msg) + + def run(self): + """Runs the command.""" + # perform the various tests + if self.metadata: + self.check_metadata() + if self.restructuredtext: + if docutils: + self.check_restructuredtext() + elif self.strict: + raise DistutilsSetupError('The docutils package is needed.') + + # let's raise an error in strict mode, if we have at least + # one warning + if self.strict and self._warnings > 1: + raise DistutilsSetupError('Please correct your package.') + + def check_metadata(self): + """Ensures that all required elements of meta-data are supplied. + + name, version, URL, (author and author_email) or + (maintainer and maintainer_email)). + + Warns if any are missing. + """ + metadata = self.distribution.metadata + + missing = [] + for attr in ('name', 'version', 'url'): + if not (hasattr(metadata, attr) and getattr(metadata, attr)): + missing.append(attr) + + if missing: + self.warn("missing required meta-data: %s" % ' ,'.join(missing)) + if metadata.author: + if not metadata.author_email: + self.warn("missing meta-data: if 'author' supplied, " + + "'author_email' must be supplied too") + elif metadata.maintainer: + if not metadata.maintainer_email: + self.warn("missing meta-data: if 'maintainer' supplied, " + + "'maintainer_email' must be supplied too") + else: + self.warn("missing meta-data: either (author and author_email) " + + "or (maintainer and maintainer_email) " + + "must be supplied") + + def check_restructuredtext(self): + """Checks if the long string fields are reST-compliant.""" + data = self.distribution.get_long_description() + for warning in self._check_rst_data(data): + line = warning[-1].get('line') + if line is None: + warning = warning[1] + else: + warning = '%s (line %s)' % (warning[1], line) + self.warn(warning) + + def _check_rst_data(self, data): + """Returns warnings when the provided data doesn't compile.""" + source_path = StringIO() + parser = Parser() + settings = frontend.OptionParser().get_default_values() + settings.tab_width = 4 + settings.pep_references = None + settings.rfc_references = None + reporter = SilentReporter(source_path, + settings.report_level, + settings.halt_level, + stream=settings.warning_stream, + debug=settings.debug, + encoding=settings.error_encoding, + error_handler=settings.error_encoding_error_handler) + + document = nodes.document(settings, reporter, source=source_path) + document.note_source(source_path, -1) + try: + parser.parse(data, document) + except AttributeError: + reporter.messages.append((-1, 'Could not finish the parsing.', + '', {})) + + return reporter.messages diff --git a/tests/test_check.py b/tests/test_check.py new file mode 100644 index 00000000..443fa35b --- /dev/null +++ b/tests/test_check.py @@ -0,0 +1,92 @@ +"""Tests for distutils.command.check.""" +import unittest + +from distutils.command.check import check, HAS_DOCUTILS +from distutils.tests import support +from distutils.errors import DistutilsSetupError + +class CheckTestCase(support.LoggingSilencer, + support.TempdirManager, + unittest.TestCase): + + def _run(self, metadata=None, **options): + if metadata is None: + metadata = {} + pkg_info, dist = self.create_dist(**metadata) + cmd = check(dist) + cmd.initialize_options() + for name, value in options.items(): + setattr(cmd, name, value) + cmd.ensure_finalized() + cmd.run() + return cmd + + def test_check_metadata(self): + # let's run the command with no metadata at all + # by default, check is checking the metadata + # should have some warnings + cmd = self._run() + self.assertEquals(cmd._warnings, 2) + + # now let's add the required fields + # and run it again, to make sure we don't get + # any warning anymore + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx'} + cmd = self._run(metadata) + self.assertEquals(cmd._warnings, 0) + + # now with the strict mode, we should + # get an error if there are missing metadata + self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1}) + + # and of course, no error when all metadata are present + cmd = self._run(metadata, strict=1) + self.assertEquals(cmd._warnings, 0) + + def test_check_document(self): + if not HAS_DOCUTILS: # won't test without docutils + return + pkg_info, dist = self.create_dist() + cmd = check(dist) + + # let's see if it detects broken rest + broken_rest = 'title\n===\n\ntest' + msgs = cmd._check_rst_data(broken_rest) + self.assertEquals(len(msgs), 1) + + # and non-broken rest + rest = 'title\n=====\n\ntest' + msgs = cmd._check_rst_data(rest) + self.assertEquals(len(msgs), 0) + + def test_check_restructuredtext(self): + if not HAS_DOCUTILS: # won't test without docutils + return + # let's see if it detects broken rest in long_description + broken_rest = 'title\n===\n\ntest' + pkg_info, dist = self.create_dist(long_description=broken_rest) + cmd = check(dist) + cmd.check_restructuredtext() + self.assertEquals(cmd._warnings, 1) + + # let's see if we have an error with strict=1 + cmd = check(dist) + cmd.initialize_options() + cmd.strict = 1 + cmd.ensure_finalized() + self.assertRaises(DistutilsSetupError, cmd.run) + + # and non-broken rest + rest = 'title\n=====\n\ntest' + pkg_info, dist = self.create_dist(long_description=rest) + cmd = check(dist) + cmd.check_restructuredtext() + self.assertEquals(cmd._warnings, 0) + +def test_suite(): + return unittest.makeSuite(CheckTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 2311950320c0ddea8f2d8fa834d052eaedf144f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Apr 2009 15:14:17 +0000 Subject: testing a full check case --- command/check.py | 2 +- tests/test_check.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/command/check.py b/command/check.py index c7291495..d164f3f5 100644 --- a/command/check.py +++ b/command/check.py @@ -65,7 +65,7 @@ class check(Command): if self.metadata: self.check_metadata() if self.restructuredtext: - if docutils: + if HAS_DOCUTILS: self.check_restructuredtext() elif self.strict: raise DistutilsSetupError('The docutils package is needed.') diff --git a/tests/test_check.py b/tests/test_check.py index 443fa35b..5e0c4531 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -85,6 +85,13 @@ class CheckTestCase(support.LoggingSilencer, cmd.check_restructuredtext() self.assertEquals(cmd._warnings, 0) + def test_check_all(self): + + metadata = {'url': 'xxx', 'author': 'xxx'} + self.assertRaises(DistutilsSetupError, self._run, + {}, **{'strict': 1, + 'restructuredtext': 1}) + def test_suite(): return unittest.makeSuite(CheckTestCase) -- cgit v1.2.1 From ca1e9a291e3e1662fdfda0143c50aa654fa8b02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Apr 2009 15:17:04 +0000 Subject: Merged revisions 71478 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71478 | tarek.ziade | 2009-04-11 17:14:17 +0200 (Sat, 11 Apr 2009) | 1 line testing a full check case ........ --- command/check.py | 2 +- tests/test_check.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/command/check.py b/command/check.py index c7291495..d164f3f5 100644 --- a/command/check.py +++ b/command/check.py @@ -65,7 +65,7 @@ class check(Command): if self.metadata: self.check_metadata() if self.restructuredtext: - if docutils: + if HAS_DOCUTILS: self.check_restructuredtext() elif self.strict: raise DistutilsSetupError('The docutils package is needed.') diff --git a/tests/test_check.py b/tests/test_check.py index 443fa35b..5e0c4531 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -85,6 +85,13 @@ class CheckTestCase(support.LoggingSilencer, cmd.check_restructuredtext() self.assertEquals(cmd._warnings, 0) + def test_check_all(self): + + metadata = {'url': 'xxx', 'author': 'xxx'} + self.assertRaises(DistutilsSetupError, self._run, + {}, **{'strict': 1, + 'restructuredtext': 1}) + def test_suite(): return unittest.makeSuite(CheckTestCase) -- cgit v1.2.1 From c22d66c04f50a075bb19cae0a0ebcc3f8ade5a14 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 11 Apr 2009 20:45:40 +0000 Subject: Merged revisions 70912,70944,70968,71033,71041,71208,71263,71286,71395-71396,71405-71406,71485,71492,71494 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70912 | georg.brandl | 2009-03-31 17:35:46 -0500 (Tue, 31 Mar 2009) | 1 line #5617: add a handy function to print a unicode string to gdbinit. ........ r70944 | georg.brandl | 2009-03-31 23:32:39 -0500 (Tue, 31 Mar 2009) | 1 line #5631: add upload to list of possible commands, which is presented in --help-commands. ........ r70968 | michael.foord | 2009-04-01 13:25:38 -0500 (Wed, 01 Apr 2009) | 1 line Adding Wing project file ........ r71033 | brett.cannon | 2009-04-01 22:34:53 -0500 (Wed, 01 Apr 2009) | 3 lines Fix two issues introduced by issue #71031 by changing the signature of PyImport_AppendInittab() to take a const char *. ........ r71041 | jesse.noller | 2009-04-02 00:17:26 -0500 (Thu, 02 Apr 2009) | 1 line Add custom initializer argument to multiprocess.Manager*, courtesy of lekma ........ r71208 | michael.foord | 2009-04-04 20:15:01 -0500 (Sat, 04 Apr 2009) | 4 lines Change the way unittest.TestSuite use their tests to always access them through iteration. Non behavior changing, this allows you to create custom subclasses that override __iter__. Issue #5693 ........ r71263 | michael.foord | 2009-04-05 14:19:28 -0500 (Sun, 05 Apr 2009) | 4 lines Adding assertIs and assertIsNot methods to unittest.TestCase Issue #2578 ........ r71286 | tarek.ziade | 2009-04-05 17:04:38 -0500 (Sun, 05 Apr 2009) | 1 line added a simplest test to distutils.spawn._nt_quote_args ........ r71395 | benjamin.peterson | 2009-04-08 08:27:29 -0500 (Wed, 08 Apr 2009) | 1 line these must be installed to correctly run tests ........ r71396 | benjamin.peterson | 2009-04-08 08:29:41 -0500 (Wed, 08 Apr 2009) | 1 line fix syntax ........ r71405 | andrew.kuchling | 2009-04-09 06:22:47 -0500 (Thu, 09 Apr 2009) | 1 line Add items ........ r71406 | andrew.kuchling | 2009-04-09 06:23:36 -0500 (Thu, 09 Apr 2009) | 1 line Typo fixes ........ r71485 | andrew.kuchling | 2009-04-11 11:12:23 -0500 (Sat, 11 Apr 2009) | 1 line Add various items ........ r71492 | georg.brandl | 2009-04-11 13:19:27 -0500 (Sat, 11 Apr 2009) | 1 line Take credit for a patch of mine. ........ r71494 | benjamin.peterson | 2009-04-11 14:31:00 -0500 (Sat, 11 Apr 2009) | 1 line ignore py3_test_grammar when compiling the library ........ --- command/__init__.py | 1 + command/check.py | 5 +++-- tests/test_spawn.py | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/test_spawn.py diff --git a/command/__init__.py b/command/__init__.py index f7dcde48..c379edbe 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -23,6 +23,7 @@ __all__ = ['build', 'bdist_rpm', 'bdist_wininst', 'check', + 'upload', # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', diff --git a/command/check.py b/command/check.py index d164f3f5..5dd73b07 100644 --- a/command/check.py +++ b/command/check.py @@ -27,8 +27,9 @@ try: self.messages.append((level, message, children, kwargs)) HAS_DOCUTILS = True -except ImportError: - # docutils is not installed +except Exception: + # Catch all exceptions because exceptions besides ImportError probably + # indicate that docutils is not ported to Py3k. HAS_DOCUTILS = False class check(Command): diff --git a/tests/test_spawn.py b/tests/test_spawn.py new file mode 100644 index 00000000..33e8bcf6 --- /dev/null +++ b/tests/test_spawn.py @@ -0,0 +1,20 @@ +"""Tests for distutils.spawn.""" +import unittest +from distutils.spawn import _nt_quote_args + +class SpawnTestCase(unittest.TestCase): + + def test_nt_quote_args(self): + + for (args, wanted) in ((['with space', 'nospace'], + ['"with space"', 'nospace']), + (['nochange', 'nospace'], + ['nochange', 'nospace'])): + res = _nt_quote_args(args) + self.assertEquals(res, wanted) + +def test_suite(): + return unittest.makeSuite(SpawnTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 4589130e9c66f7d818ac17f854c48643b7bba95f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Apr 2009 14:53:51 +0000 Subject: removed the print statements and added a test --- command/config.py | 16 ++++++++++------ tests/test_config_cmd.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 tests/test_config_cmd.py diff --git a/command/config.py b/command/config.py index 6b358cf2..c36456ad 100644 --- a/command/config.py +++ b/command/config.py @@ -354,13 +354,17 @@ class config (Command): # class config +def dump_file(filename, head=None): + """Dumps a file content into log.info. -def dump_file (filename, head=None): + If head is not None, will be dumped before the file content. + """ if head is None: - print filename + ":" + log.info('%s' % filename) else: - print head - + log.info(head) file = open(filename) - sys.stdout.write(file.read()) - file.close() + try: + log.info(file.read()) + finally: + file.close() diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py new file mode 100644 index 00000000..6fd17766 --- /dev/null +++ b/tests/test_config_cmd.py @@ -0,0 +1,42 @@ +"""Tests for distutils.command.config.""" +import unittest +import os + +from distutils.command.config import dump_file +from distutils.tests import support +from distutils import log + +class ConfigTestCase(support.LoggingSilencer, + support.TempdirManager, + unittest.TestCase): + + def _info(self, msg): + for line in msg.splitlines(): + self._logs.append(line) + + def setUp(self): + super(ConfigTestCase, self).setUp() + self._logs = [] + self.old_log = log.info + log.info = self._info + + def tearDown(self): + log.info = self.old_log + super(ConfigTestCase, self).tearDown() + + def test_dump_file(self): + this_file = os.path.splitext(__file__)[0] + '.py' + f = open(this_file) + try: + numlines = len(f.readlines()) + finally: + f.close() + + dump_file(this_file, 'I am the header') + self.assertEquals(len(self._logs), numlines+1) + +def test_suite(): + return unittest.makeSuite(ConfigTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 46a2116c217f63b2a3059dafad7123f509fa46f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Apr 2009 14:57:46 +0000 Subject: Merged revisions 71509 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71509 | tarek.ziade | 2009-04-12 16:53:51 +0200 (Sun, 12 Apr 2009) | 1 line removed the print statements and added a test ........ --- command/config.py | 15 ++++++++++----- tests/test_config_cmd.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 tests/test_config_cmd.py diff --git a/command/config.py b/command/config.py index 34f91884..144c5132 100644 --- a/command/config.py +++ b/command/config.py @@ -336,11 +336,16 @@ class config(Command): def dump_file(filename, head=None): + """Dumps a file content into log.info. + + If head is not None, will be dumped before the file content. + """ if head is None: - print(filename + ":") + log.info('%s' % filename) else: - print(head) - + log.info(head) file = open(filename) - sys.stdout.write(file.read()) - file.close() + try: + log.info(file.read()) + finally: + file.close() diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py new file mode 100644 index 00000000..6fd17766 --- /dev/null +++ b/tests/test_config_cmd.py @@ -0,0 +1,42 @@ +"""Tests for distutils.command.config.""" +import unittest +import os + +from distutils.command.config import dump_file +from distutils.tests import support +from distutils import log + +class ConfigTestCase(support.LoggingSilencer, + support.TempdirManager, + unittest.TestCase): + + def _info(self, msg): + for line in msg.splitlines(): + self._logs.append(line) + + def setUp(self): + super(ConfigTestCase, self).setUp() + self._logs = [] + self.old_log = log.info + log.info = self._info + + def tearDown(self): + log.info = self.old_log + super(ConfigTestCase, self).tearDown() + + def test_dump_file(self): + this_file = os.path.splitext(__file__)[0] + '.py' + f = open(this_file) + try: + numlines = len(f.readlines()) + finally: + f.close() + + dump_file(this_file, 'I am the header') + self.assertEquals(len(self._logs), numlines+1) + +def test_suite(): + return unittest.makeSuite(ConfigTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From a3a55acac57195c9b2dab89f5086e30d6bebdf93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Apr 2009 15:03:50 +0000 Subject: pep8-fied the module before adding tests --- command/config.py | 62 ++++++++++++++++++++++--------------------------------- 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/command/config.py b/command/config.py index c36456ad..74eed783 100644 --- a/command/config.py +++ b/command/config.py @@ -18,10 +18,9 @@ from distutils.errors import DistutilsExecError from distutils.sysconfig import customize_compiler from distutils import log -LANG_EXT = {'c': '.c', - 'c++': '.cxx'} +LANG_EXT = {'c': '.c', 'c++': '.cxx'} -class config (Command): +class config(Command): description = "prepare to build" @@ -51,12 +50,10 @@ class config (Command): # The three standard command methods: since the "config" command # does nothing by default, these are empty. - def initialize_options (self): + def initialize_options(self): self.compiler = None self.cc = None self.include_dirs = None - #self.define = None - #self.undef = None self.libraries = None self.library_dirs = None @@ -68,7 +65,7 @@ class config (Command): # to clean at some point self.temp_files = [] - def finalize_options (self): + def finalize_options(self): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] elif type(self.include_dirs) is StringType: @@ -93,7 +90,7 @@ class config (Command): # loosely based on Autoconf macros of similar names. Sub-classes # may use these freely. - def _check_compiler (self): + def _check_compiler(self): """Check that 'self.compiler' really is a CCompiler object; if not, make it one. """ @@ -112,7 +109,7 @@ class config (Command): self.compiler.set_library_dirs(self.library_dirs) - def _gen_temp_sourcefile (self, body, headers, lang): + def _gen_temp_sourcefile(self, body, headers, lang): filename = "_configtest" + LANG_EXT[lang] file = open(filename, "w") if headers: @@ -125,14 +122,14 @@ class config (Command): file.close() return filename - def _preprocess (self, body, headers, include_dirs, lang): + def _preprocess(self, body, headers, include_dirs, lang): src = self._gen_temp_sourcefile(body, headers, lang) out = "_configtest.i" self.temp_files.extend([src, out]) self.compiler.preprocess(src, out, include_dirs=include_dirs) return (src, out) - def _compile (self, body, headers, include_dirs, lang): + def _compile(self, body, headers, include_dirs, lang): src = self._gen_temp_sourcefile(body, headers, lang) if self.dump_source: dump_file(src, "compiling '%s':" % src) @@ -141,9 +138,8 @@ class config (Command): self.compiler.compile([src], include_dirs=include_dirs) return (src, obj) - def _link (self, body, - headers, include_dirs, - libraries, library_dirs, lang): + def _link(self, body, headers, include_dirs, libraries, library_dirs, + lang): (src, obj) = self._compile(body, headers, include_dirs, lang) prog = os.path.splitext(os.path.basename(src))[0] self.compiler.link_executable([obj], prog, @@ -157,7 +153,7 @@ class config (Command): return (src, obj, prog) - def _clean (self, *filenames): + def _clean(self, *filenames): if not filenames: filenames = self.temp_files self.temp_files = [] @@ -179,7 +175,7 @@ class config (Command): # XXX need access to the header search path and maybe default macros. - def try_cpp (self, body=None, headers=None, include_dirs=None, lang="c"): + def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): """Construct a source file from 'body' (a string containing lines of C/C++ code) and 'headers' (a list of header files to include) and run it through the preprocessor. Return true if the @@ -197,8 +193,8 @@ class config (Command): self._clean() return ok - def search_cpp (self, pattern, body=None, - headers=None, include_dirs=None, lang="c"): + def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, + lang="c"): """Construct a source file (just like 'try_cpp()'), run it through the preprocessor, and return true if any line of the output matches 'pattern'. 'pattern' should either be a compiled regex object or a @@ -227,7 +223,7 @@ class config (Command): self._clean() return match - def try_compile (self, body, headers=None, include_dirs=None, lang="c"): + def try_compile(self, body, headers=None, include_dirs=None, lang="c"): """Try to compile a source file built from 'body' and 'headers'. Return true on success, false otherwise. """ @@ -243,10 +239,8 @@ class config (Command): self._clean() return ok - def try_link (self, body, - headers=None, include_dirs=None, - libraries=None, library_dirs=None, - lang="c"): + def try_link(self, body, headers=None, include_dirs=None, libraries=None, + library_dirs=None, lang="c"): """Try to compile and link a source file, built from 'body' and 'headers', to executable form. Return true on success, false otherwise. @@ -264,10 +258,8 @@ class config (Command): self._clean() return ok - def try_run (self, body, - headers=None, include_dirs=None, - libraries=None, library_dirs=None, - lang="c"): + def try_run(self, body, headers=None, include_dirs=None, libraries=None, + library_dirs=None, lang="c"): """Try to compile, link to an executable, and run a program built from 'body' and 'headers'. Return true on success, false otherwise. @@ -291,10 +283,8 @@ class config (Command): # (these are the ones that are actually likely to be useful # when implementing a real-world config command!) - def check_func (self, func, - headers=None, include_dirs=None, - libraries=None, library_dirs=None, - decl=0, call=0): + def check_func(self, func, headers=None, include_dirs=None, + libraries=None, library_dirs=None, decl=0, call=0): """Determine if function 'func' is available by constructing a source file that refers to 'func', and compiles and links it. @@ -327,8 +317,8 @@ class config (Command): # check_func () - def check_lib (self, library, library_dirs=None, - headers=None, include_dirs=None, other_libraries=[]): + def check_lib(self, library, library_dirs=None, headers=None, + include_dirs=None, other_libraries=[]): """Determine if 'library' is available to be linked against, without actually checking that any particular symbols are provided by it. 'headers' will be used in constructing the source file to @@ -342,8 +332,8 @@ class config (Command): headers, include_dirs, [library]+other_libraries, library_dirs) - def check_header (self, header, include_dirs=None, - library_dirs=None, lang="c"): + def check_header(self, header, include_dirs=None, library_dirs=None, + lang="c"): """Determine if the system header file named by 'header_file' exists and can be found by the preprocessor; return true if so, false otherwise. @@ -352,8 +342,6 @@ class config (Command): include_dirs=include_dirs) -# class config - def dump_file(filename, head=None): """Dumps a file content into log.info. -- cgit v1.2.1 From 6a0952de72b896a4a74338f867d14205a0fe6394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Apr 2009 15:07:31 +0000 Subject: Merged revisions 71513 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71513 | tarek.ziade | 2009-04-12 17:03:50 +0200 (Sun, 12 Apr 2009) | 1 line pep8-fied the module before adding tests ........ --- command/config.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/command/config.py b/command/config.py index 144c5132..385a47a7 100644 --- a/command/config.py +++ b/command/config.py @@ -53,8 +53,6 @@ class config(Command): self.compiler = None self.cc = None self.include_dirs = None - #self.define = None - #self.undef = None self.libraries = None self.library_dirs = None @@ -136,8 +134,8 @@ class config(Command): self.compiler.compile([src], include_dirs=include_dirs) return (src, obj) - def _link(self, body, headers, include_dirs, libraries, - library_dirs, lang): + def _link(self, body, headers, include_dirs, libraries, library_dirs, + lang): (src, obj) = self._compile(body, headers, include_dirs, lang) prog = os.path.splitext(os.path.basename(src))[0] self.compiler.link_executable([obj], prog, @@ -191,8 +189,8 @@ class config(Command): self._clean() return ok - def search_cpp(self, pattern, body=None, headers=None, - include_dirs=None, lang="c"): + def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, + lang="c"): """Construct a source file (just like 'try_cpp()'), run it through the preprocessor, and return true if any line of the output matches 'pattern'. 'pattern' should either be a compiled regex object or a @@ -237,8 +235,8 @@ class config(Command): self._clean() return ok - def try_link(self, body, headers=None, include_dirs=None, - libraries=None, library_dirs=None, lang="c"): + def try_link(self, body, headers=None, include_dirs=None, libraries=None, + library_dirs=None, lang="c"): """Try to compile and link a source file, built from 'body' and 'headers', to executable form. Return true on success, false otherwise. @@ -256,8 +254,8 @@ class config(Command): self._clean() return ok - def try_run(self, body, headers=None, include_dirs=None, - libraries=None, library_dirs=None, lang="c"): + def try_run(self, body, headers=None, include_dirs=None, libraries=None, + library_dirs=None, lang="c"): """Try to compile, link to an executable, and run a program built from 'body' and 'headers'. Return true on success, false otherwise. -- cgit v1.2.1 From 343e5f79fa09f634bc1b7273b1096ee370aa988f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Apr 2009 16:31:24 +0000 Subject: added a simple test for search_cpp --- command/config.py | 5 ++--- tests/test_config_cmd.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/command/config.py b/command/config.py index 74eed783..43a8dc84 100644 --- a/command/config.py +++ b/command/config.py @@ -202,11 +202,10 @@ class config(Command): preprocesses an empty file -- which can be useful to determine the symbols the preprocessor and compiler set by default. """ - self._check_compiler() - (src, out) = self._preprocess(body, headers, include_dirs, lang) + src, out = self._preprocess(body, headers, include_dirs, lang) - if type(pattern) is StringType: + if isinstance(pattern, str): pattern = re.compile(pattern) file = open(out) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 6fd17766..af16d4c8 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -2,7 +2,7 @@ import unittest import os -from distutils.command.config import dump_file +from distutils.command.config import dump_file, config from distutils.tests import support from distutils import log @@ -10,7 +10,7 @@ class ConfigTestCase(support.LoggingSilencer, support.TempdirManager, unittest.TestCase): - def _info(self, msg): + def _info(self, msg, *args): for line in msg.splitlines(): self._logs.append(line) @@ -35,6 +35,17 @@ class ConfigTestCase(support.LoggingSilencer, dump_file(this_file, 'I am the header') self.assertEquals(len(self._logs), numlines+1) + def test_search_cpp(self): + pkg_dir, dist = self.create_dist() + cmd = config(dist) + + # simple pattern searches + match = cmd.search_cpp(pattern='xxx', body='// xxx') + self.assertEquals(match, 0) + + match = cmd.search_cpp(pattern='command', body='// xxx') + self.assertEquals(match, 1) + def test_suite(): return unittest.makeSuite(ConfigTestCase) -- cgit v1.2.1 From ea43ebbadd370b5247b600b4b46053f358541259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Apr 2009 16:34:34 +0000 Subject: Merged revisions 71523 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71523 | tarek.ziade | 2009-04-12 18:31:24 +0200 (Sun, 12 Apr 2009) | 1 line added a simple test for search_cpp ........ --- command/config.py | 3 +-- tests/test_config_cmd.py | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/command/config.py b/command/config.py index 385a47a7..c3122055 100644 --- a/command/config.py +++ b/command/config.py @@ -198,9 +198,8 @@ class config(Command): preprocesses an empty file -- which can be useful to determine the symbols the preprocessor and compiler set by default. """ - self._check_compiler() - (src, out) = self._preprocess(body, headers, include_dirs, lang) + src, out = self._preprocess(body, headers, include_dirs, lang) if isinstance(pattern, str): pattern = re.compile(pattern) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 6fd17766..af16d4c8 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -2,7 +2,7 @@ import unittest import os -from distutils.command.config import dump_file +from distutils.command.config import dump_file, config from distutils.tests import support from distutils import log @@ -10,7 +10,7 @@ class ConfigTestCase(support.LoggingSilencer, support.TempdirManager, unittest.TestCase): - def _info(self, msg): + def _info(self, msg, *args): for line in msg.splitlines(): self._logs.append(line) @@ -35,6 +35,17 @@ class ConfigTestCase(support.LoggingSilencer, dump_file(this_file, 'I am the header') self.assertEquals(len(self._logs), numlines+1) + def test_search_cpp(self): + pkg_dir, dist = self.create_dist() + cmd = config(dist) + + # simple pattern searches + match = cmd.search_cpp(pattern='xxx', body='// xxx') + self.assertEquals(match, 0) + + match = cmd.search_cpp(pattern='command', body='// xxx') + self.assertEquals(match, 1) + def test_suite(): return unittest.makeSuite(ConfigTestCase) -- cgit v1.2.1 From b4b74e3688d8bebe6d1dd2a7121a2032510412a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Apr 2009 16:45:32 +0000 Subject: added a test for finalize_options --- command/config.py | 13 ++++++------- tests/test_config_cmd.py | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/command/config.py b/command/config.py index 43a8dc84..f2ebe37d 100644 --- a/command/config.py +++ b/command/config.py @@ -12,7 +12,7 @@ this header file lives". __revision__ = "$Id$" import sys, os, string, re -from types import * + from distutils.core import Command from distutils.errors import DistutilsExecError from distutils.sysconfig import customize_compiler @@ -68,19 +68,18 @@ class config(Command): def finalize_options(self): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - elif type(self.include_dirs) is StringType: - self.include_dirs = string.split(self.include_dirs, os.pathsep) + elif isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) if self.libraries is None: self.libraries = [] - elif type(self.libraries) is StringType: + elif isinstance(self.libraries, str): self.libraries = [self.libraries] if self.library_dirs is None: self.library_dirs = [] - elif type(self.library_dirs) is StringType: - self.library_dirs = string.split(self.library_dirs, os.pathsep) - + elif isinstance(self.library_dirs, str): + self.library_dirs = self.library_dirs.split(os.pathsep) def run (self): pass diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index af16d4c8..45d480ba 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -46,6 +46,21 @@ class ConfigTestCase(support.LoggingSilencer, match = cmd.search_cpp(pattern='command', body='// xxx') self.assertEquals(match, 1) + def test_finalize_options(self): + # finalize_options does a bit of transformation + # on options + pkg_dir, dist = self.create_dist() + cmd = config(dist) + cmd.include_dirs = 'one%stwo' % os.pathsep + cmd.libraries = 'one' + cmd.library_dirs = 'three%sfour' % os.pathsep + cmd.ensure_finalized() + + self.assertEquals(cmd.include_dirs, ['one', 'two']) + self.assertEquals(cmd.libraries, ['one']) + self.assertEquals(cmd.library_dirs, ['three', 'four']) + + def test_suite(): return unittest.makeSuite(ConfigTestCase) -- cgit v1.2.1 From fe17289ba8c6ef976d19c69ac49cadce27ae1611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Apr 2009 16:49:20 +0000 Subject: Merged revisions 71528 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71528 | tarek.ziade | 2009-04-12 18:45:32 +0200 (Sun, 12 Apr 2009) | 1 line added a test for finalize_options ........ --- command/config.py | 1 + tests/test_config_cmd.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/command/config.py b/command/config.py index c3122055..ac80a54e 100644 --- a/command/config.py +++ b/command/config.py @@ -12,6 +12,7 @@ this header file lives". __revision__ = "$Id$" import sys, os, re + from distutils.core import Command from distutils.errors import DistutilsExecError from distutils.sysconfig import customize_compiler diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index af16d4c8..45d480ba 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -46,6 +46,21 @@ class ConfigTestCase(support.LoggingSilencer, match = cmd.search_cpp(pattern='command', body='// xxx') self.assertEquals(match, 1) + def test_finalize_options(self): + # finalize_options does a bit of transformation + # on options + pkg_dir, dist = self.create_dist() + cmd = config(dist) + cmd.include_dirs = 'one%stwo' % os.pathsep + cmd.libraries = 'one' + cmd.library_dirs = 'three%sfour' % os.pathsep + cmd.ensure_finalized() + + self.assertEquals(cmd.include_dirs, ['one', 'two']) + self.assertEquals(cmd.libraries, ['one']) + self.assertEquals(cmd.library_dirs, ['three', 'four']) + + def test_suite(): return unittest.makeSuite(ConfigTestCase) -- cgit v1.2.1 From 44c22c6f2b8bbf6c929777a17a2fadfec2afbd26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Apr 2009 17:02:08 +0000 Subject: removed string usage and added a test for _clean --- command/config.py | 8 ++++---- tests/test_config_cmd.py | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/command/config.py b/command/config.py index f2ebe37d..134fa389 100644 --- a/command/config.py +++ b/command/config.py @@ -11,7 +11,7 @@ this header file lives". __revision__ = "$Id$" -import sys, os, string, re +import sys, os, re from distutils.core import Command from distutils.errors import DistutilsExecError @@ -81,7 +81,7 @@ class config(Command): elif isinstance(self.library_dirs, str): self.library_dirs = self.library_dirs.split(os.pathsep) - def run (self): + def run(self): pass @@ -156,7 +156,7 @@ class config(Command): if not filenames: filenames = self.temp_files self.temp_files = [] - log.info("removing: %s", string.join(filenames)) + log.info("removing: %s", ' '.join(filenames)) for filename in filenames: try: os.remove(filename) @@ -308,7 +308,7 @@ class config(Command): else: body.append(" %s;" % func) body.append("}") - body = string.join(body, "\n") + "\n" + body = "\n".join(body) + "\n" return self.try_link(body, headers, include_dirs, libraries, library_dirs) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 45d480ba..f5c09e4f 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -60,6 +60,24 @@ class ConfigTestCase(support.LoggingSilencer, self.assertEquals(cmd.libraries, ['one']) self.assertEquals(cmd.library_dirs, ['three', 'four']) + def test_clean(self): + # _clean removes files + tmp_dir = self.mkdtemp() + f1 = os.path.join(tmp_dir, 'one') + f2 = os.path.join(tmp_dir, 'two') + + self.write_file(f1, 'xxx') + self.write_file(f2, 'xxx') + + for f in (f1, f2): + self.assert_(os.path.exists(f)) + + pkg_dir, dist = self.create_dist() + cmd = config(dist) + cmd._clean(f1, f2) + + for f in (f1, f2): + self.assert_(not os.path.exists(f)) def test_suite(): return unittest.makeSuite(ConfigTestCase) -- cgit v1.2.1 From 3032240dde2e76b4bfe1c11c05eeaa7252e1b14e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Apr 2009 17:04:39 +0000 Subject: Merged revisions 71533 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71533 | tarek.ziade | 2009-04-12 19:02:08 +0200 (Sun, 12 Apr 2009) | 1 line removed string usage and added a test for _clean ........ --- tests/test_config_cmd.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 45d480ba..f5c09e4f 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -60,6 +60,24 @@ class ConfigTestCase(support.LoggingSilencer, self.assertEquals(cmd.libraries, ['one']) self.assertEquals(cmd.library_dirs, ['three', 'four']) + def test_clean(self): + # _clean removes files + tmp_dir = self.mkdtemp() + f1 = os.path.join(tmp_dir, 'one') + f2 = os.path.join(tmp_dir, 'two') + + self.write_file(f1, 'xxx') + self.write_file(f2, 'xxx') + + for f in (f1, f2): + self.assert_(os.path.exists(f)) + + pkg_dir, dist = self.create_dist() + cmd = config(dist) + cmd._clean(f1, f2) + + for f in (f1, f2): + self.assert_(not os.path.exists(f)) def test_suite(): return unittest.makeSuite(ConfigTestCase) -- cgit v1.2.1 From db733eec63b1e1cb294a2c83699c9ee095a22d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 13 Apr 2009 12:34:01 +0000 Subject: Fixed #5607: Distutils test_get_platform was failing fo Mac OS X fat binaries. --- tests/test_util.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index 67474025..f27c4524 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -5,6 +5,7 @@ import os import sys import unittest +from copy import copy from distutils.errors import DistutilsPlatformError @@ -17,6 +18,7 @@ from distutils.util import strtobool from distutils.util import rfc822_escape from distutils import util # used to patch _environ_checked +from distutils.sysconfig import get_config_vars, _config_vars class utilTestCase(unittest.TestCase): @@ -30,6 +32,7 @@ class utilTestCase(unittest.TestCase): self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive + self._config_vars = copy(_config_vars) # patching os.uname if hasattr(os, 'uname'): @@ -42,7 +45,7 @@ class utilTestCase(unittest.TestCase): os.uname = self._get_uname def tearDown(self): - # getting back tne environment + # getting back the environment os.name = self.name sys.platform = self.platform sys.version = self.version @@ -55,6 +58,7 @@ class utilTestCase(unittest.TestCase): os.uname = self.uname else: del os.uname + _config_vars = copy(self._config_vars) def _set_uname(self, uname): self._uname = uname @@ -96,8 +100,34 @@ class utilTestCase(unittest.TestCase): 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' + '-fwrapv -O3 -Wall -Wstrict-prototypes') + self.assertEquals(get_platform(), 'macosx-10.3-i386') + # macbook with fat binaries (fat, universal or fat64) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEquals(get_platform(), 'macosx-10.4-fat') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEquals(get_platform(), 'macosx-10.4-universal') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEquals(get_platform(), 'macosx-10.4-fat64') + # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' -- cgit v1.2.1 From b37e2b50392d2a4d5bff20f774f5cd10cbf094fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 13 Apr 2009 12:37:57 +0000 Subject: Merged revisions 71560 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71560 | tarek.ziade | 2009-04-13 14:34:01 +0200 (Mon, 13 Apr 2009) | 1 line Fixed #5607: Distutils test_get_platform was failing fo Mac OS X fat binaries. ........ --- tests/test_util.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index 67474025..f27c4524 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -5,6 +5,7 @@ import os import sys import unittest +from copy import copy from distutils.errors import DistutilsPlatformError @@ -17,6 +18,7 @@ from distutils.util import strtobool from distutils.util import rfc822_escape from distutils import util # used to patch _environ_checked +from distutils.sysconfig import get_config_vars, _config_vars class utilTestCase(unittest.TestCase): @@ -30,6 +32,7 @@ class utilTestCase(unittest.TestCase): self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive + self._config_vars = copy(_config_vars) # patching os.uname if hasattr(os, 'uname'): @@ -42,7 +45,7 @@ class utilTestCase(unittest.TestCase): os.uname = self._get_uname def tearDown(self): - # getting back tne environment + # getting back the environment os.name = self.name sys.platform = self.platform sys.version = self.version @@ -55,6 +58,7 @@ class utilTestCase(unittest.TestCase): os.uname = self.uname else: del os.uname + _config_vars = copy(self._config_vars) def _set_uname(self, uname): self._uname = uname @@ -96,8 +100,34 @@ class utilTestCase(unittest.TestCase): 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' + '-fwrapv -O3 -Wall -Wstrict-prototypes') + self.assertEquals(get_platform(), 'macosx-10.3-i386') + # macbook with fat binaries (fat, universal or fat64) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEquals(get_platform(), 'macosx-10.4-fat') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEquals(get_platform(), 'macosx-10.4-universal') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEquals(get_platform(), 'macosx-10.4-fat64') + # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' -- cgit v1.2.1 From f6f67fef03ad922f8e974cf41e0ed88a8a0931a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 13 Apr 2009 12:42:26 +0000 Subject: deactivate test_search_cpp under win32 --- tests/test_config_cmd.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index f5c09e4f..1e6ebfac 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -1,6 +1,7 @@ """Tests for distutils.command.config.""" import unittest import os +import sys from distutils.command.config import dump_file, config from distutils.tests import support @@ -36,6 +37,8 @@ class ConfigTestCase(support.LoggingSilencer, self.assertEquals(len(self._logs), numlines+1) def test_search_cpp(self): + if sys.platform == 'win32': + return pkg_dir, dist = self.create_dist() cmd = config(dist) -- cgit v1.2.1 From c5ab35d770e11483e3c05ade821a28dafaa5c5e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 13 Apr 2009 12:59:03 +0000 Subject: Merged revisions 71569 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71569 | tarek.ziade | 2009-04-13 14:42:26 +0200 (Mon, 13 Apr 2009) | 1 line deactivate test_search_cpp under win32 ........ --- tests/test_config_cmd.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index f5c09e4f..1e6ebfac 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -1,6 +1,7 @@ """Tests for distutils.command.config.""" import unittest import os +import sys from distutils.command.config import dump_file, config from distutils.tests import support @@ -36,6 +37,8 @@ class ConfigTestCase(support.LoggingSilencer, self.assertEquals(len(self._logs), numlines+1) def test_search_cpp(self): + if sys.platform == 'win32': + return pkg_dir, dist = self.create_dist() cmd = config(dist) -- cgit v1.2.1 From f27b9ed76fd3444675d28d9d9632503466882551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 13 Apr 2009 20:03:44 +0000 Subject: improved test coverage for distutils.cmd --- cmd.py | 14 +++++++------- tests/test_cmd.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/cmd.py b/cmd.py index dfcbe235..0e564600 100644 --- a/cmd.py +++ b/cmd.py @@ -214,7 +214,7 @@ class Command: # and they can be guaranteed that thereafter, self.foo will be # a list of strings. - def _ensure_stringlike (self, option, what, default=None): + def _ensure_stringlike(self, option, what, default=None): val = getattr(self, option) if val is None: setattr(self, option, default) @@ -224,13 +224,13 @@ class Command: "'%s' must be a %s (got `%s`)" % (option, what, val) return val - def ensure_string (self, option, default=None): + def ensure_string(self, option, default=None): """Ensure that 'option' is a string; if not defined, set it to 'default'. """ self._ensure_stringlike(option, "string", default) - def ensure_string_list (self, option): + def ensure_string_list(self, option): """Ensure that 'option' is a list of strings. If 'option' is currently a string, we split it either on /,\s*/ or /\s+/, so "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become @@ -258,20 +258,20 @@ class Command: (option, val) - def _ensure_tested_string (self, option, tester, - what, error_fmt, default=None): + def _ensure_tested_string(self, option, tester, + what, error_fmt, default=None): val = self._ensure_stringlike(option, what, default) if val is not None and not tester(val): raise DistutilsOptionError, \ ("error in '%s' option: " + error_fmt) % (option, val) - def ensure_filename (self, option): + def ensure_filename(self, option): """Ensure that 'option' is the name of an existing file.""" self._ensure_tested_string(option, os.path.isfile, "filename", "'%s' does not exist or is not a file") - def ensure_dirname (self, option): + def ensure_dirname(self, option): self._ensure_tested_string(option, os.path.isdir, "directory name", "'%s' does not exist or is not a directory") diff --git a/tests/test_cmd.py b/tests/test_cmd.py index a252c355..8f2b36fb 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,5 +1,6 @@ """Tests for distutils.cmd.""" import unittest +import os from distutils.cmd import Command from distutils.dist import Distribution @@ -62,6 +63,45 @@ class CommandTestCase(unittest.TestCase): ' option2 = 1'] self.assertEquals(msgs, wanted) + def test_ensure_string(self): + cmd = self.cmd + cmd.option1 = 'ok' + cmd.ensure_string('option1') + + cmd.option2 = None + cmd.ensure_string('option2', 'xxx') + self.assert_(hasattr(cmd, 'option2')) + + cmd.option3 = 1 + self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') + + def test_ensure_string_list(self): + cmd = self.cmd + cmd.option1 = 'ok,dok' + cmd.ensure_string_list('option1') + self.assertEquals(cmd.option1, ['ok', 'dok']) + + cmd.option2 = ['xxx', 'www'] + cmd.ensure_string_list('option2') + + cmd.option3 = ['ok', 2] + self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, + 'option3') + + def test_ensure_filename(self): + cmd = self.cmd + cmd.option1 = __file__ + cmd.ensure_filename('option1') + cmd.option2 = 'xxx' + self.assertRaises(DistutilsOptionError, cmd.ensure_filename, 'option2') + + def test_ensure_dirname(self): + cmd = self.cmd + cmd.option1 = os.path.dirname(__file__) + cmd.ensure_dirname('option1') + cmd.option2 = 'xxx' + self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') + def test_suite(): return unittest.makeSuite(CommandTestCase) -- cgit v1.2.1 From 21748d58c6bac04eb31504bf976b216ee5e8d3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 13 Apr 2009 20:07:23 +0000 Subject: Merged revisions 71585 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71585 | tarek.ziade | 2009-04-13 22:03:44 +0200 (Mon, 13 Apr 2009) | 1 line improved test coverage for distutils.cmd ........ --- tests/test_cmd.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index a252c355..8f2b36fb 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,5 +1,6 @@ """Tests for distutils.cmd.""" import unittest +import os from distutils.cmd import Command from distutils.dist import Distribution @@ -62,6 +63,45 @@ class CommandTestCase(unittest.TestCase): ' option2 = 1'] self.assertEquals(msgs, wanted) + def test_ensure_string(self): + cmd = self.cmd + cmd.option1 = 'ok' + cmd.ensure_string('option1') + + cmd.option2 = None + cmd.ensure_string('option2', 'xxx') + self.assert_(hasattr(cmd, 'option2')) + + cmd.option3 = 1 + self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') + + def test_ensure_string_list(self): + cmd = self.cmd + cmd.option1 = 'ok,dok' + cmd.ensure_string_list('option1') + self.assertEquals(cmd.option1, ['ok', 'dok']) + + cmd.option2 = ['xxx', 'www'] + cmd.ensure_string_list('option2') + + cmd.option3 = ['ok', 2] + self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, + 'option3') + + def test_ensure_filename(self): + cmd = self.cmd + cmd.option1 = __file__ + cmd.ensure_filename('option1') + cmd.option2 = 'xxx' + self.assertRaises(DistutilsOptionError, cmd.ensure_filename, 'option2') + + def test_ensure_dirname(self): + cmd = self.cmd + cmd.option1 = os.path.dirname(__file__) + cmd.ensure_dirname('option1') + cmd.option2 = 'xxx' + self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') + def test_suite(): return unittest.makeSuite(CommandTestCase) -- cgit v1.2.1 From 1b8d985f66013fe20bbfd4181afc29248d19f35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 13 Apr 2009 20:14:54 +0000 Subject: pep8-fied --- cmd.py | 65 +++++++++++++++++++++++------------------------------------------ 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/cmd.py b/cmd.py index 0e564600..869258ba 100644 --- a/cmd.py +++ b/cmd.py @@ -46,7 +46,7 @@ class Command: # -- Creation/initialization methods ------------------------------- - def __init__ (self, dist): + def __init__(self, dist): """Create and initialize a new Command object. Most importantly, invokes the 'initialize_options()' method, which is the real initializer and depends on the actual command being @@ -93,12 +93,8 @@ class Command: # always calls 'finalize_options()', to respect/update it. self.finalized = 0 - # __init__ () - - # XXX A more explicit way to customize dry_run would be better. - - def __getattr__ (self, attr): + def __getattr__(self, attr): if attr == 'dry_run': myval = getattr(self, "_" + attr) if myval is None: @@ -108,13 +104,11 @@ class Command: else: raise AttributeError, attr - - def ensure_finalized (self): + def ensure_finalized(self): if not self.finalized: self.finalize_options() self.finalized = 1 - # Subclasses must define: # initialize_options() # provide default values for all options; may be customized by @@ -128,7 +122,7 @@ class Command: # run the command: do whatever it is we're here to do, # controlled by the command's various option values - def initialize_options (self): + def initialize_options(self): """Set default values for all the options that this command supports. Note that these defaults may be overridden by other commands, by the setup script, by config files, or by the @@ -141,7 +135,7 @@ class Command: raise RuntimeError, \ "abstract method -- subclass %s must override" % self.__class__ - def finalize_options (self): + def finalize_options(self): """Set final values for all the options that this command supports. This is always called as late as possible, ie. after any option assignments from the command-line or from other commands have been @@ -170,7 +164,7 @@ class Command: self.announce(indent + "%s = %s" % (option, value), level=log.INFO) - def run (self): + def run(self): """A command's raison d'etre: carry out the action it exists to perform, controlled by the options initialized in 'initialize_options()', customized by other commands, the setup @@ -180,17 +174,16 @@ class Command: This method must be implemented by all command classes. """ - raise RuntimeError, \ "abstract method -- subclass %s must override" % self.__class__ - def announce (self, msg, level=1): + def announce(self, msg, level=1): """If the current verbosity level is of greater than or equal to 'level' print 'msg' to stdout. """ log.log(level, msg) - def debug_print (self, msg): + def debug_print(self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the DISTUTILS_DEBUG environment variable) flag is true. """ @@ -200,7 +193,6 @@ class Command: sys.stdout.flush() - # -- Option validation methods ------------------------------------- # (these are very handy in writing the 'finalize_options()' method) # @@ -279,14 +271,13 @@ class Command: # -- Convenience methods for commands ------------------------------ - def get_command_name (self): + def get_command_name(self): if hasattr(self, 'command_name'): return self.command_name else: return self.__class__.__name__ - - def set_undefined_options (self, src_cmd, *option_pairs): + def set_undefined_options(self, src_cmd, *option_pairs): """Set the values of any "undefined" options from corresponding option values in some other command object. "Undefined" here means "is None", which is the convention used to indicate that an option @@ -311,7 +302,7 @@ class Command: getattr(src_cmd_obj, src_option)) - def get_finalized_command (self, command, create=1): + def get_finalized_command(self, command, create=1): """Wrapper around Distribution's 'get_command_obj()' method: find (create if necessary and 'create' is true) the command object for 'command', call its 'ensure_finalized()' method, and return the @@ -323,19 +314,18 @@ class Command: # XXX rename to 'get_reinitialized_command()'? (should do the # same in dist.py, if so) - def reinitialize_command (self, command, reinit_subcommands=0): + def reinitialize_command(self, command, reinit_subcommands=0): return self.distribution.reinitialize_command( command, reinit_subcommands) - def run_command (self, command): + def run_command(self, command): """Run some other command: uses the 'run_command()' method of Distribution, which creates and finalizes the command object if necessary and then invokes its 'run()' method. """ self.distribution.run_command(command) - - def get_sub_commands (self): + def get_sub_commands(self): """Determine the sub-commands that are relevant in the current distribution (ie., that need to be run). This is based on the 'sub_commands' class attribute: each tuple in that list may include @@ -351,19 +341,17 @@ class Command: # -- External world manipulation ----------------------------------- - def warn (self, msg): + def warn(self, msg): log.warn("warning: %s: %s\n" % (self.get_command_name(), msg)) - def execute (self, func, args, msg=None, level=1): + def execute(self, func, args, msg=None, level=1): util.execute(func, args, msg, dry_run=self.dry_run) - - def mkpath (self, name, mode=0777): + def mkpath(self, name, mode=0777): dir_util.mkpath(name, mode, dry_run=self.dry_run) - - def copy_file (self, infile, outfile, + def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1, link=None, level=1): """Copy a file respecting verbose, dry-run and force flags. (The former two default to whatever is in the Distribution object, and @@ -376,8 +364,7 @@ class Command: link, dry_run=self.dry_run) - - def copy_tree (self, infile, outfile, + def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1): """Copy an entire directory tree respecting verbose, dry-run, @@ -403,7 +390,6 @@ class Command: return archive_util.make_archive( base_name, format, root_dir, base_dir, dry_run=self.dry_run) - def make_file(self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): """Special case of 'execute()' for operations that process one or @@ -438,17 +424,12 @@ class Command: else: log.debug(skip_msg) - # make_file () - -# class Command - - # XXX 'install_misc' class not currently used -- it was the base class for # both 'install_scripts' and 'install_data', but they outgrew it. It might # still be useful for 'install_headers', though, so I'm keeping it around # for the time being. -class install_misc (Command): +class install_misc(Command): """Common base class for installing some files in a subdirectory. Currently used by install_data and install_scripts. """ @@ -459,10 +440,10 @@ class install_misc (Command): self.install_dir = None self.outfiles = [] - def _install_dir_from (self, dirname): + def _install_dir_from(self, dirname): self.set_undefined_options('install', (dirname, 'install_dir')) - def _copy_files (self, filelist): + def _copy_files(self, filelist): self.outfiles = [] if not filelist: return @@ -471,5 +452,5 @@ class install_misc (Command): self.copy_file(f, self.install_dir) self.outfiles.append(os.path.join(self.install_dir, f)) - def get_outputs (self): + def get_outputs(self): return self.outfiles -- cgit v1.2.1 From 5ff3e0818920cd90da7e957d901ed9d42a9a21e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 13 Apr 2009 20:19:58 +0000 Subject: Merged revisions 71589 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71589 | tarek.ziade | 2009-04-13 22:14:54 +0200 (Mon, 13 Apr 2009) | 1 line pep8-fied ........ --- cmd.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/cmd.py b/cmd.py index 5829a566..aff7d68e 100644 --- a/cmd.py +++ b/cmd.py @@ -93,9 +93,8 @@ class Command: # always calls 'finalize_options()', to respect/update it. self.finalized = 0 - # XXX A more explicit way to customize dry_run would be better. - def __getattr__ (self, attr): + def __getattr__(self, attr): if attr == 'dry_run': myval = getattr(self, "_" + attr) if myval is None: @@ -105,7 +104,7 @@ class Command: else: raise AttributeError(attr) - def ensure_finalized (self): + def ensure_finalized(self): if not self.finalized: self.finalize_options() self.finalized = 1 @@ -175,7 +174,6 @@ class Command: This method must be implemented by all command classes. """ - raise RuntimeError("abstract method -- subclass %s must override" % self.__class__) @@ -351,7 +349,7 @@ class Command: preserve_times, not self.force, link, dry_run=self.dry_run) - def copy_tree (self, infile, outfile, preserve_mode=1, preserve_times=1, + def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1): """Copy an entire directory tree respecting verbose, dry-run, and force flags. @@ -373,7 +371,6 @@ class Command: return archive_util.make_archive(base_name, format, root_dir, base_dir, dry_run=self.dry_run) - def make_file(self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): """Special case of 'execute()' for operations that process one or @@ -406,7 +403,6 @@ class Command: else: log.debug(skip_msg) - # XXX 'install_misc' class not currently used -- it was the base class for # both 'install_scripts' and 'install_data', but they outgrew it. It might # still be useful for 'install_headers', though, so I'm keeping it around @@ -423,10 +419,10 @@ class install_misc(Command): self.install_dir = None self.outfiles = [] - def _install_dir_from (self, dirname): + def _install_dir_from(self, dirname): self.set_undefined_options('install', (dirname, 'install_dir')) - def _copy_files (self, filelist): + def _copy_files(self, filelist): self.outfiles = [] if not filelist: return @@ -435,5 +431,5 @@ class install_misc(Command): self.copy_file(f, self.install_dir) self.outfiles.append(os.path.join(self.install_dir, f)) - def get_outputs (self): + def get_outputs(self): return self.outfiles -- cgit v1.2.1 From 9c62b7799426d02dc52636b95a34f12988a19305 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 14 Apr 2009 13:16:19 +0000 Subject: Bumping to 2.6.2 (final). --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e8f55cf0..ba926063 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.2c1" +__version__ = "2.6.2" #--end constants-- -- cgit v1.2.1 From 452e5137d5612d5e68d72c5748b14a2fed3dbc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 17 Apr 2009 14:29:56 +0000 Subject: DistutilsSetupError was not raised when one single warning occured --- command/check.py | 2 +- tests/test_check.py | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/command/check.py b/command/check.py index d164f3f5..7d56dfcf 100644 --- a/command/check.py +++ b/command/check.py @@ -72,7 +72,7 @@ class check(Command): # let's raise an error in strict mode, if we have at least # one warning - if self.strict and self._warnings > 1: + if self.strict and self._warnings > 0: raise DistutilsSetupError('Please correct your package.') def check_metadata(self): diff --git a/tests/test_check.py b/tests/test_check.py index 5e0c4531..372bae36 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -72,17 +72,16 @@ class CheckTestCase(support.LoggingSilencer, self.assertEquals(cmd._warnings, 1) # let's see if we have an error with strict=1 - cmd = check(dist) - cmd.initialize_options() - cmd.strict = 1 - cmd.ensure_finalized() - self.assertRaises(DistutilsSetupError, cmd.run) + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx', + 'long_description': broken_rest} + self.assertRaises(DistutilsSetupError, self._run, metadata, + **{'strict': 1, 'restructuredtext': 1}) # and non-broken rest - rest = 'title\n=====\n\ntest' - pkg_info, dist = self.create_dist(long_description=rest) - cmd = check(dist) - cmd.check_restructuredtext() + metadata['long_description'] = 'title\n=====\n\ntest' + cmd = self._run(metadata, strict=1, restructuredtext=1) self.assertEquals(cmd._warnings, 0) def test_check_all(self): -- cgit v1.2.1 From a97cd0dee2cd722b7036319ce0aab8d03951f3f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 17 Apr 2009 14:34:49 +0000 Subject: Merged revisions 71674 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71674 | tarek.ziade | 2009-04-17 16:29:56 +0200 (Fri, 17 Apr 2009) | 1 line DistutilsSetupError was not raised when one single warning occured ........ --- command/check.py | 2 +- tests/test_check.py | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/command/check.py b/command/check.py index 5dd73b07..9a8fca1d 100644 --- a/command/check.py +++ b/command/check.py @@ -73,7 +73,7 @@ class check(Command): # let's raise an error in strict mode, if we have at least # one warning - if self.strict and self._warnings > 1: + if self.strict and self._warnings > 0: raise DistutilsSetupError('Please correct your package.') def check_metadata(self): diff --git a/tests/test_check.py b/tests/test_check.py index 5e0c4531..372bae36 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -72,17 +72,16 @@ class CheckTestCase(support.LoggingSilencer, self.assertEquals(cmd._warnings, 1) # let's see if we have an error with strict=1 - cmd = check(dist) - cmd.initialize_options() - cmd.strict = 1 - cmd.ensure_finalized() - self.assertRaises(DistutilsSetupError, cmd.run) + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx', + 'long_description': broken_rest} + self.assertRaises(DistutilsSetupError, self._run, metadata, + **{'strict': 1, 'restructuredtext': 1}) # and non-broken rest - rest = 'title\n=====\n\ntest' - pkg_info, dist = self.create_dist(long_description=rest) - cmd = check(dist) - cmd.check_restructuredtext() + metadata['long_description'] = 'title\n=====\n\ntest' + cmd = self._run(metadata, strict=1, restructuredtext=1) self.assertEquals(cmd._warnings, 0) def test_check_all(self): -- cgit v1.2.1 From 9e39129c58b8b48972a4b776ba7b10ed80107a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 20 Apr 2009 07:53:55 +0000 Subject: #5795 sysconfig._config_vars was shadowed in tearDown --- tests/test_util.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_util.py b/tests/test_util.py index f27c4524..29be0cfd 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -18,7 +18,8 @@ from distutils.util import strtobool from distutils.util import rfc822_escape from distutils import util # used to patch _environ_checked -from distutils.sysconfig import get_config_vars, _config_vars +from distutils.sysconfig import get_config_vars +from distutils import sysconfig class utilTestCase(unittest.TestCase): @@ -32,7 +33,7 @@ class utilTestCase(unittest.TestCase): self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive - self._config_vars = copy(_config_vars) + self._config_vars = copy(sysconfig._config_vars) # patching os.uname if hasattr(os, 'uname'): @@ -58,7 +59,7 @@ class utilTestCase(unittest.TestCase): os.uname = self.uname else: del os.uname - _config_vars = copy(self._config_vars) + sysconfig._config_vars = copy(self._config_vars) def _set_uname(self, uname): self._uname = uname -- cgit v1.2.1 From e5374224727a94148777aeea1ff7d35d540e7e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 20 Apr 2009 10:33:47 +0000 Subject: making BuildWinInstTestCase silent in case bdist_wininst is not run under win32 --- tests/test_bdist_wininst.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index d851d6c7..f2cb4fde 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -5,6 +5,7 @@ from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support class BuildWinInstTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_get_exe_bytes(self): -- cgit v1.2.1 From 39d9889757fcefa8fda44500a0ee02a72f9d5cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 20 Apr 2009 12:18:08 +0000 Subject: Merged revisions 71758 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71758 | tarek.ziade | 2009-04-20 09:53:55 +0200 (Mon, 20 Apr 2009) | 1 line #5795 sysconfig._config_vars was shadowed in tearDown ........ --- tests/test_util.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_util.py b/tests/test_util.py index f27c4524..29be0cfd 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -18,7 +18,8 @@ from distutils.util import strtobool from distutils.util import rfc822_escape from distutils import util # used to patch _environ_checked -from distutils.sysconfig import get_config_vars, _config_vars +from distutils.sysconfig import get_config_vars +from distutils import sysconfig class utilTestCase(unittest.TestCase): @@ -32,7 +33,7 @@ class utilTestCase(unittest.TestCase): self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive - self._config_vars = copy(_config_vars) + self._config_vars = copy(sysconfig._config_vars) # patching os.uname if hasattr(os, 'uname'): @@ -58,7 +59,7 @@ class utilTestCase(unittest.TestCase): os.uname = self.uname else: del os.uname - _config_vars = copy(self._config_vars) + sysconfig._config_vars = copy(self._config_vars) def _set_uname(self, uname): self._uname = uname -- cgit v1.2.1 From a7d4678492b113544483d4e93a6e6cdca002f56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 20 Apr 2009 12:37:58 +0000 Subject: Merged revisions 71759 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71759 | tarek.ziade | 2009-04-20 12:33:47 +0200 (Mon, 20 Apr 2009) | 1 line making BuildWinInstTestCase silent in case bdist_wininst is not run under win32 ........ --- tests/test_bdist_wininst.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index d851d6c7..f2cb4fde 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -5,6 +5,7 @@ from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support class BuildWinInstTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_get_exe_bytes(self): -- cgit v1.2.1 From 5717e2c0f80872e578d6292da94e0540063bc312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 20 Apr 2009 12:48:47 +0000 Subject: Merged revisions 71759 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71759 | tarek.ziade | 2009-04-20 12:33:47 +0200 (Mon, 20 Apr 2009) | 1 line making BuildWinInstTestCase silent in case bdist_wininst is not run under win32 ........ --- tests/test_bdist_wininst.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index de6601fb..85aa02a2 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -7,6 +7,7 @@ from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support class BuildWinInstTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_get_exe_bytes(self): -- cgit v1.2.1 From 23ac9a3f5d25ac86966a339921a3fd032b1eb02d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 25 Apr 2009 12:38:08 +0000 Subject: Issue #4951: Fixed failure in test_httpservers --- tests/test_util.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_util.py b/tests/test_util.py index 29be0cfd..348933e9 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -29,7 +29,7 @@ class utilTestCase(unittest.TestCase): self.platform = sys.platform self.version = sys.version self.sep = os.sep - self.environ = os.environ + self.environ = dict(os.environ) self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive @@ -51,7 +51,10 @@ class utilTestCase(unittest.TestCase): sys.platform = self.platform sys.version = self.version os.sep = self.sep - os.environ = self.environ + for k, v in self.environ.items(): + os.environ[k] = v + for k in set(os.environ) - set(self.environ): + del os.environ[k] os.path.join = self.join os.path.isabs = self.isabs os.path.splitdrive = self.splitdrive -- cgit v1.2.1 From 53d92cb18d49ee06a6c23a299d97c06c2442566d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 25 Apr 2009 12:39:56 +0000 Subject: Merged revisions 71878 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71878 | tarek.ziade | 2009-04-25 14:38:08 +0200 (Sat, 25 Apr 2009) | 1 line Issue #4951: Fixed failure in test_httpservers ........ --- tests/test_util.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_util.py b/tests/test_util.py index 29be0cfd..348933e9 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -29,7 +29,7 @@ class utilTestCase(unittest.TestCase): self.platform = sys.platform self.version = sys.version self.sep = os.sep - self.environ = os.environ + self.environ = dict(os.environ) self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive @@ -51,7 +51,10 @@ class utilTestCase(unittest.TestCase): sys.platform = self.platform sys.version = self.version os.sep = self.sep - os.environ = self.environ + for k, v in self.environ.items(): + os.environ[k] = v + for k in set(os.environ) - set(self.environ): + del os.environ[k] os.path.join = self.join os.path.isabs = self.isabs os.path.splitdrive = self.splitdrive -- cgit v1.2.1 From 5b4daec209610504336618f33489cec401a6850f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 25 Apr 2009 12:51:59 +0000 Subject: #5810: Fixed Distutils test_build_scripts --- tests/test_build_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 2acfab82..b55eb585 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -90,7 +90,7 @@ class BuildScriptsTestCase(support.TempdirManager, # On linux-g++-32 with command line `./configure --enable-ipv6 # --with-suffix=3`, python is compiled okay but the build scripts # failed when writing the name of the executable - old = sysconfig._config_vars.get('VERSION') + old = sysconfig.get_config_vars().get('VERSION') sysconfig._config_vars['VERSION'] = 4 try: cmd.run() -- cgit v1.2.1 From e557c16ec08bb6927410f65cf813ad20293859d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 25 Apr 2009 12:53:56 +0000 Subject: Merged revisions 71884 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71884 | tarek.ziade | 2009-04-25 14:51:59 +0200 (Sat, 25 Apr 2009) | 1 line #5810: Fixed Distutils test_build_scripts ........ --- tests/test_build_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 2acfab82..b55eb585 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -90,7 +90,7 @@ class BuildScriptsTestCase(support.TempdirManager, # On linux-g++-32 with command line `./configure --enable-ipv6 # --with-suffix=3`, python is compiled okay but the build scripts # failed when writing the name of the executable - old = sysconfig._config_vars.get('VERSION') + old = sysconfig.get_config_vars().get('VERSION') sysconfig._config_vars['VERSION'] = 4 try: cmd.run() -- cgit v1.2.1 From 3967ecc4455a2662822d000fc243a5bc56ca105a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 25 Apr 2009 12:55:56 +0000 Subject: Merged revisions 71884 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r71884 | tarek.ziade | 2009-04-25 14:51:59 +0200 (Sat, 25 Apr 2009) | 1 line #5810: Fixed Distutils test_build_scripts ........ --- tests/test_build_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 2acfab82..b55eb585 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -90,7 +90,7 @@ class BuildScriptsTestCase(support.TempdirManager, # On linux-g++-32 with command line `./configure --enable-ipv6 # --with-suffix=3`, python is compiled okay but the build scripts # failed when writing the name of the executable - old = sysconfig._config_vars.get('VERSION') + old = sysconfig.get_config_vars().get('VERSION') sysconfig._config_vars['VERSION'] = 4 try: cmd.run() -- cgit v1.2.1 From 9bb7e34ba2331d21bbad5abe79c3ebac63946e4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 29 Apr 2009 08:03:46 +0000 Subject: Fixed #5874 : distutils.tests.test_config_cmd is not locale-sensitive anymore --- tests/test_config_cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 1e6ebfac..bacf13a2 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -46,7 +46,7 @@ class ConfigTestCase(support.LoggingSilencer, match = cmd.search_cpp(pattern='xxx', body='// xxx') self.assertEquals(match, 0) - match = cmd.search_cpp(pattern='command', body='// xxx') + match = cmd.search_cpp(pattern='_configtest', body='// xxx') self.assertEquals(match, 1) def test_finalize_options(self): -- cgit v1.2.1 From 85120c6b47d6f8ccd1eb1e64a4308f08f1b4e517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 29 Apr 2009 08:07:44 +0000 Subject: Merged revisions 72094 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72094 | tarek.ziade | 2009-04-29 10:03:46 +0200 (Wed, 29 Apr 2009) | 1 line Fixed #5874 : distutils.tests.test_config_cmd is not locale-sensitive anymore ........ --- tests/test_config_cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 1e6ebfac..bacf13a2 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -46,7 +46,7 @@ class ConfigTestCase(support.LoggingSilencer, match = cmd.search_cpp(pattern='xxx', body='// xxx') self.assertEquals(match, 0) - match = cmd.search_cpp(pattern='command', body='// xxx') + match = cmd.search_cpp(pattern='_configtest', body='// xxx') self.assertEquals(match, 1) def test_finalize_options(self): -- cgit v1.2.1 From 534e308892506dae6bf5aedd724dcbe975ed1149 Mon Sep 17 00:00:00 2001 From: Steven Bethard Date: Tue, 5 May 2009 01:31:22 +0000 Subject: Update bdist_msi so that the generated MSIs for pure Python modules can install to any version of Python, like the generated EXEs from bdist_wininst. (Previously, you had to create a new MSI for each version of Python.) --- command/bdist_msi.py | 237 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 162 insertions(+), 75 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index f94d9579..18bddeb4 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -117,6 +117,12 @@ class bdist_msi (Command): boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', 'skip-build'] + all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4', + '2.5', '2.6', '2.7', '2.8', '2.9', + '3.0', '3.1', '3.2', '3.3', '3.4', + '3.5', '3.6', '3.7', '3.8', '3.9'] + other_version = 'X' + def initialize_options (self): self.bdist_dir = None self.plat_name = None @@ -128,6 +134,7 @@ class bdist_msi (Command): self.skip_build = 0 self.install_script = None self.pre_install_script = None + self.versions = None def finalize_options (self): if self.bdist_dir is None: @@ -135,13 +142,14 @@ class bdist_msi (Command): self.bdist_dir = os.path.join(bdist_base, 'msi') short_version = get_python_version() if self.target_version: + self.versions = [self.target_version] if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: raise DistutilsOptionError, \ "target version can only be %s, or the '--skip_build'" \ " option must be specified" % (short_version,) else: - self.target_version = short_version + self.versions = list(self.all_versions) self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'), @@ -223,8 +231,11 @@ class bdist_msi (Command): # Prefix ProductName with Python x.y, so that # it sorts together with the other Python packages # in Add-Remove-Programs (APR) - product_name = "Python %s %s" % (self.target_version, - self.distribution.get_fullname()) + fullname = self.distribution.get_fullname() + if self.target_version: + product_name = "Python %s %s" % (self.target_version, fullname) + else: + product_name = "Python %s" % (fullname) self.db = msilib.init_database(installer_name, schema, product_name, msilib.gen_uuid(), sversion, author) @@ -245,7 +256,8 @@ class bdist_msi (Command): self.db.Commit() if hasattr(self.distribution, 'dist_files'): - self.distribution.dist_files.append(('bdist_msi', self.target_version, fullname)) + tup = 'bdist_msi', self.target_version or 'any', fullname + self.distribution.dist_files.append(tup) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) @@ -253,65 +265,121 @@ class bdist_msi (Command): def add_files(self): db = self.db cab = msilib.CAB("distfiles") - f = Feature(db, "default", "Default Feature", "Everything", 1, directory="TARGETDIR") - f.set_current() rootdir = os.path.abspath(self.bdist_dir) + root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir") + f = Feature(db, "Python", "Python", "Everything", + 0, 1, directory="TARGETDIR") + + items = [(f, root, '')] + for version in self.versions + [self.other_version]: + target = "TARGETDIR" + version + name = default = "Python" + version + desc = "Everything" + if version is self.other_version: + title = "Python from another location" + level = 2 + else: + title = "Python %s from registry" % version + level = 1 + f = Feature(db, name, title, desc, 1, level, directory=target) + dir = Directory(db, cab, root, rootdir, target, default) + items.append((f, dir, version)) db.Commit() - todo = [root] - while todo: - dir = todo.pop() - for file in os.listdir(dir.absolute): - afile = os.path.join(dir.absolute, file) - if os.path.isdir(afile): - newdir = Directory(db, cab, dir, file, file, "%s|%s" % (dir.make_short(file), file)) - todo.append(newdir) - else: - key = dir.add_file(file) - if file==self.install_script: - if self.install_script_key: - raise DistutilsOptionError, "Multiple files with name %s" % file - self.install_script_key = '[#%s]' % key + + seen = {} + for feature, dir, version in items: + todo = [dir] + while todo: + dir = todo.pop() + for file in os.listdir(dir.absolute): + afile = os.path.join(dir.absolute, file) + if os.path.isdir(afile): + short = "%s|%s" % (dir.make_short(file), file) + default = file + version + newdir = Directory(db, cab, dir, file, default, short) + todo.append(newdir) + else: + if not dir.component: + dir.start_component(dir.logical, feature, 0) + if afile not in seen: + key = seen[afile] = dir.add_file(file) + if file==self.install_script: + if self.install_script_key: + raise DistutilsOptionError( + "Multiple files with name %s" % file) + self.install_script_key = '[#%s]' % key + else: + key = seen[afile] + add_data(self.db, "DuplicateFile", + [(key + version, dir.component, key, None, dir.logical)]) + cab.commit(db) def add_find_python(self): """Adds code to the installer to compute the location of Python. - Properties PYTHON.MACHINE, PYTHON.USER, PYTHONDIR and PYTHON will be set - in both the execute and UI sequences; PYTHONDIR will be set from - PYTHON.USER if defined, else from PYTHON.MACHINE. - PYTHON is PYTHONDIR\python.exe""" - install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % self.target_version - add_data(self.db, "RegLocator", - [("python.machine", 2, install_path, None, 2), - ("python.user", 1, install_path, None, 2)]) - add_data(self.db, "AppSearch", - [("PYTHON.MACHINE", "python.machine"), - ("PYTHON.USER", "python.user")]) - add_data(self.db, "CustomAction", - [("PythonFromMachine", 51+256, "PYTHONDIR", "[PYTHON.MACHINE]"), - ("PythonFromUser", 51+256, "PYTHONDIR", "[PYTHON.USER]"), - ("PythonExe", 51+256, "PYTHON", "[PYTHONDIR]\\python.exe"), - ("InitialTargetDir", 51+256, "TARGETDIR", "[PYTHONDIR]")]) - add_data(self.db, "InstallExecuteSequence", - [("PythonFromMachine", "PYTHON.MACHINE", 401), - ("PythonFromUser", "PYTHON.USER", 402), - ("PythonExe", None, 403), - ("InitialTargetDir", 'TARGETDIR=""', 404), - ]) - add_data(self.db, "InstallUISequence", - [("PythonFromMachine", "PYTHON.MACHINE", 401), - ("PythonFromUser", "PYTHON.USER", 402), - ("PythonExe", None, 403), - ("InitialTargetDir", 'TARGETDIR=""', 404), - ]) - def add_scripts(self): - if self.install_script: + Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the + registry for each version of Python. + + Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined, + else from PYTHON.MACHINE.X.Y. + + Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe""" + + start = 402 + for ver in self.versions: + install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver + machine_reg = "python.machine." + ver + user_reg = "python.user." + ver + machine_prop = "PYTHON.MACHINE." + ver + user_prop = "PYTHON.USER." + ver + machine_action = "PythonFromMachine" + ver + user_action = "PythonFromUser" + ver + exe_action = "PythonExe" + ver + target_dir_prop = "TARGETDIR" + ver + exe_prop = "PYTHON" + ver + add_data(self.db, "RegLocator", + [(machine_reg, 2, install_path, None, 2), + (user_reg, 1, install_path, None, 2)]) + add_data(self.db, "AppSearch", + [(machine_prop, machine_reg), + (user_prop, user_reg)]) add_data(self.db, "CustomAction", - [("install_script", 50, "PYTHON", self.install_script_key)]) + [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"), + (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"), + (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"), + ]) add_data(self.db, "InstallExecuteSequence", - [("install_script", "NOT Installed", 6800)]) + [(machine_action, machine_prop, start), + (user_action, user_prop, start + 1), + (exe_action, None, start + 2), + ]) + add_data(self.db, "InstallUISequence", + [(machine_action, machine_prop, start), + (user_action, user_prop, start + 1), + (exe_action, None, start + 2), + ]) + add_data(self.db, "Condition", + [("Python" + ver, 0, "NOT TARGETDIR" + ver)]) + start += 4 + assert start < 500 + + def add_scripts(self): + if self.install_script: + start = 6800 + for ver in self.versions + [self.other_version]: + install_action = "install_script." + ver + exe_prop = "PYTHON" + ver + add_data(self.db, "CustomAction", + [(install_action, 50, exe_prop, self.install_script_key)]) + add_data(self.db, "InstallExecuteSequence", + [(install_action, "&Python%s=3" % ver, start)]) + start += 1 + # XXX pre-install scripts are currently refused in finalize_options() + # but if this feature is completed, it will also need to add + # entries for each version as the above code does if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") f = open(scriptfn, "w") @@ -375,7 +443,7 @@ class bdist_msi (Command): [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140), ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141), # In the user interface, assume all-users installation if privileged. - ("SelectDirectoryDlg", "Not Installed", 1230), + ("SelectFeaturesDlg", "Not Installed", 1230), # XXX no support for resume installations yet #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240), ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), @@ -498,33 +566,49 @@ class bdist_msi (Command): c.event("SpawnDialog", "CancelDlg") ##################################################################### - # Target directory selection - seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title, + # Feature (Python directory) selection + seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title, "Next", "Next", "Cancel") - seldlg.title("Select Destination Directory") + seldlg.title("Select Python Installations") - version = sys.version[:3]+" " - seldlg.text("Hint", 15, 30, 300, 40, 3, - "The destination directory should contain a Python %sinstallation" % version) + seldlg.text("Hint", 15, 30, 300, 20, 3, + "Select the Python locations where %s should be installed." + % self.distribution.get_fullname()) seldlg.back("< Back", None, active=0) c = seldlg.next("Next >", "Cancel") - c.event("SetTargetPath", "TARGETDIR", ordering=1) - c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=2) - c.event("EndDialog", "Return", ordering=3) - - c = seldlg.cancel("Cancel", "DirectoryCombo") + order = 1 + c.event("[TARGETDIR]", "[SourceDir]", ordering=order) + for version in self.versions + [self.other_version]: + order += 1 + c.event("[TARGETDIR]", "[TARGETDIR%s]" % version, + "FEATURE_SELECTED AND &Python%s=3" % version, + ordering=order) + c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1) + c.event("EndDialog", "Return", ordering=order + 2) + c = seldlg.cancel("Cancel", "Features") c.event("SpawnDialog", "CancelDlg") - seldlg.control("DirectoryCombo", "DirectoryCombo", 15, 70, 272, 80, 393219, - "TARGETDIR", None, "DirectoryList", None) - seldlg.control("DirectoryList", "DirectoryList", 15, 90, 308, 136, 3, "TARGETDIR", - None, "PathEdit", None) - seldlg.control("PathEdit", "PathEdit", 15, 230, 306, 16, 3, "TARGETDIR", None, "Next", None) - c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None) - c.event("DirectoryListUp", "0") - c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None) - c.event("DirectoryListNew", "0") + c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3, + "FEATURE", None, "PathEdit", None) + c.event("[FEATURE_SELECTED]", "1") + ver = self.other_version + install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver + dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver + + c = seldlg.text("Other", 15, 200, 300, 15, 3, + "Provide an alternate Python location") + c.condition("Enable", install_other_cond) + c.condition("Show", install_other_cond) + c.condition("Disable", dont_install_other_cond) + c.condition("Hide", dont_install_other_cond) + + c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1, + "TARGETDIR" + ver, None, "Next", None) + c.condition("Enable", install_other_cond) + c.condition("Show", install_other_cond) + c.condition("Disable", dont_install_other_cond) + c.condition("Hide", dont_install_other_cond) ##################################################################### # Disk cost @@ -640,7 +724,10 @@ class bdist_msi (Command): def get_installer_filename(self, fullname): # Factored out to allow overriding in subclasses - base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, - self.target_version) + if self.target_version: + base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, + self.target_version) + else: + base_name = "%s.%s.msi" % (fullname, self.plat_name) installer_name = os.path.join(self.dist_dir, base_name) return installer_name -- cgit v1.2.1 From f70dabd395a2452cafc0e2a526070ed71b8ea7bd Mon Sep 17 00:00:00 2001 From: Steven Bethard Date: Tue, 5 May 2009 02:22:38 +0000 Subject: Merged revisions 72306 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72306 | steven.bethard | 2009-05-04 18:31:22 -0700 (Mon, 04 May 2009) | 1 line Update bdist_msi so that the generated MSIs for pure Python modules can install to any version of Python, like the generated EXEs from bdist_wininst. (Previously, you had to create a new MSI for each version of Python.) ........ --- command/bdist_msi.py | 240 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 163 insertions(+), 77 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index aab89cd6..ffd668eb 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2005, 2006 Martin v. Löwis +# Copyright (C) 2005, 2006 Martin v. Löwis # Licensed to PSF under a Contributor Agreement. # The bdist_wininst command proper # based on bdist_wininst @@ -117,6 +117,12 @@ class bdist_msi(Command): boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', 'skip-build'] + all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4', + '2.5', '2.6', '2.7', '2.8', '2.9', + '3.0', '3.1', '3.2', '3.3', '3.4', + '3.5', '3.6', '3.7', '3.8', '3.9'] + other_version = 'X' + def initialize_options(self): self.bdist_dir = None self.plat_name = None @@ -128,6 +134,7 @@ class bdist_msi(Command): self.skip_build = 0 self.install_script = None self.pre_install_script = None + self.versions = None def finalize_options(self): if self.bdist_dir is None: @@ -135,13 +142,14 @@ class bdist_msi(Command): self.bdist_dir = os.path.join(bdist_base, 'msi') short_version = get_python_version() if self.target_version: + self.versions = [self.target_version] if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: raise DistutilsOptionError( "target version can only be %s, or the '--skip_build'" " option must be specified" % (short_version,)) else: - self.target_version = short_version + self.versions = list(self.all_versions) self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'), @@ -222,8 +230,11 @@ class bdist_msi(Command): # Prefix ProductName with Python x.y, so that # it sorts together with the other Python packages # in Add-Remove-Programs (APR) - product_name = "Python %s %s" % (self.target_version, - self.distribution.get_fullname()) + fullname = self.distribution.get_fullname() + if self.target_version: + product_name = "Python %s %s" % (self.target_version, fullname) + else: + product_name = "Python %s" % (fullname) self.db = msilib.init_database(installer_name, schema, product_name, msilib.gen_uuid(), sversion, author) @@ -244,7 +255,8 @@ class bdist_msi(Command): self.db.Commit() if hasattr(self.distribution, 'dist_files'): - self.distribution.dist_files.append(('bdist_msi', self.target_version, fullname)) + tup = 'bdist_msi', self.target_version or 'any', fullname + self.distribution.dist_files.append(tup) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) @@ -252,66 +264,121 @@ class bdist_msi(Command): def add_files(self): db = self.db cab = msilib.CAB("distfiles") - f = Feature(db, "default", "Default Feature", "Everything", 1, directory="TARGETDIR") - f.set_current() rootdir = os.path.abspath(self.bdist_dir) + root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir") + f = Feature(db, "Python", "Python", "Everything", + 0, 1, directory="TARGETDIR") + + items = [(f, root, '')] + for version in self.versions + [self.other_version]: + target = "TARGETDIR" + version + name = default = "Python" + version + desc = "Everything" + if version is self.other_version: + title = "Python from another location" + level = 2 + else: + title = "Python %s from registry" % version + level = 1 + f = Feature(db, name, title, desc, 1, level, directory=target) + dir = Directory(db, cab, root, rootdir, target, default) + items.append((f, dir, version)) db.Commit() - todo = [root] - while todo: - dir = todo.pop() - for file in os.listdir(dir.absolute): - afile = os.path.join(dir.absolute, file) - if os.path.isdir(afile): - newdir = Directory(db, cab, dir, file, file, "%s|%s" % (dir.make_short(file), file)) - todo.append(newdir) - else: - key = dir.add_file(file) - if file==self.install_script: - if self.install_script_key: - raise DistutilsOptionError( - "Multiple files with name %s" % file) - self.install_script_key = '[#%s]' % key + + seen = {} + for feature, dir, version in items: + todo = [dir] + while todo: + dir = todo.pop() + for file in os.listdir(dir.absolute): + afile = os.path.join(dir.absolute, file) + if os.path.isdir(afile): + short = "%s|%s" % (dir.make_short(file), file) + default = file + version + newdir = Directory(db, cab, dir, file, default, short) + todo.append(newdir) + else: + if not dir.component: + dir.start_component(dir.logical, feature, 0) + if afile not in seen: + key = seen[afile] = dir.add_file(file) + if file==self.install_script: + if self.install_script_key: + raise DistutilsOptionError( + "Multiple files with name %s" % file) + self.install_script_key = '[#%s]' % key + else: + key = seen[afile] + add_data(self.db, "DuplicateFile", + [(key + version, dir.component, key, None, dir.logical)]) + cab.commit(db) def add_find_python(self): """Adds code to the installer to compute the location of Python. - Properties PYTHON.MACHINE, PYTHON.USER, PYTHONDIR and PYTHON will be set - in both the execute and UI sequences; PYTHONDIR will be set from - PYTHON.USER if defined, else from PYTHON.MACHINE. - PYTHON is PYTHONDIR\python.exe""" - install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % self.target_version - add_data(self.db, "RegLocator", - [("python.machine", 2, install_path, None, 2), - ("python.user", 1, install_path, None, 2)]) - add_data(self.db, "AppSearch", - [("PYTHON.MACHINE", "python.machine"), - ("PYTHON.USER", "python.user")]) - add_data(self.db, "CustomAction", - [("PythonFromMachine", 51+256, "PYTHONDIR", "[PYTHON.MACHINE]"), - ("PythonFromUser", 51+256, "PYTHONDIR", "[PYTHON.USER]"), - ("PythonExe", 51+256, "PYTHON", "[PYTHONDIR]\\python.exe"), - ("InitialTargetDir", 51+256, "TARGETDIR", "[PYTHONDIR]")]) - add_data(self.db, "InstallExecuteSequence", - [("PythonFromMachine", "PYTHON.MACHINE", 401), - ("PythonFromUser", "PYTHON.USER", 402), - ("PythonExe", None, 403), - ("InitialTargetDir", 'TARGETDIR=""', 404), - ]) - add_data(self.db, "InstallUISequence", - [("PythonFromMachine", "PYTHON.MACHINE", 401), - ("PythonFromUser", "PYTHON.USER", 402), - ("PythonExe", None, 403), - ("InitialTargetDir", 'TARGETDIR=""', 404), - ]) - def add_scripts(self): - if self.install_script: + Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the + registry for each version of Python. + + Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined, + else from PYTHON.MACHINE.X.Y. + + Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe""" + + start = 402 + for ver in self.versions: + install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver + machine_reg = "python.machine." + ver + user_reg = "python.user." + ver + machine_prop = "PYTHON.MACHINE." + ver + user_prop = "PYTHON.USER." + ver + machine_action = "PythonFromMachine" + ver + user_action = "PythonFromUser" + ver + exe_action = "PythonExe" + ver + target_dir_prop = "TARGETDIR" + ver + exe_prop = "PYTHON" + ver + add_data(self.db, "RegLocator", + [(machine_reg, 2, install_path, None, 2), + (user_reg, 1, install_path, None, 2)]) + add_data(self.db, "AppSearch", + [(machine_prop, machine_reg), + (user_prop, user_reg)]) add_data(self.db, "CustomAction", - [("install_script", 50, "PYTHON", self.install_script_key)]) + [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"), + (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"), + (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"), + ]) add_data(self.db, "InstallExecuteSequence", - [("install_script", "NOT Installed", 6800)]) + [(machine_action, machine_prop, start), + (user_action, user_prop, start + 1), + (exe_action, None, start + 2), + ]) + add_data(self.db, "InstallUISequence", + [(machine_action, machine_prop, start), + (user_action, user_prop, start + 1), + (exe_action, None, start + 2), + ]) + add_data(self.db, "Condition", + [("Python" + ver, 0, "NOT TARGETDIR" + ver)]) + start += 4 + assert start < 500 + + def add_scripts(self): + if self.install_script: + start = 6800 + for ver in self.versions + [self.other_version]: + install_action = "install_script." + ver + exe_prop = "PYTHON" + ver + add_data(self.db, "CustomAction", + [(install_action, 50, exe_prop, self.install_script_key)]) + add_data(self.db, "InstallExecuteSequence", + [(install_action, "&Python%s=3" % ver, start)]) + start += 1 + # XXX pre-install scripts are currently refused in finalize_options() + # but if this feature is completed, it will also need to add + # entries for each version as the above code does if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") f = open(scriptfn, "w") @@ -375,7 +442,7 @@ class bdist_msi(Command): [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140), ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141), # In the user interface, assume all-users installation if privileged. - ("SelectDirectoryDlg", "Not Installed", 1230), + ("SelectFeaturesDlg", "Not Installed", 1230), # XXX no support for resume installations yet #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240), ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), @@ -498,33 +565,49 @@ class bdist_msi(Command): c.event("SpawnDialog", "CancelDlg") ##################################################################### - # Target directory selection - seldlg = PyDialog(db, "SelectDirectoryDlg", x, y, w, h, modal, title, + # Feature (Python directory) selection + seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title, "Next", "Next", "Cancel") - seldlg.title("Select Destination Directory") + seldlg.title("Select Python Installations") - version = sys.version[:3]+" " - seldlg.text("Hint", 15, 30, 300, 40, 3, - "The destination directory should contain a Python %sinstallation" % version) + seldlg.text("Hint", 15, 30, 300, 20, 3, + "Select the Python locations where %s should be installed." + % self.distribution.get_fullname()) seldlg.back("< Back", None, active=0) c = seldlg.next("Next >", "Cancel") - c.event("SetTargetPath", "TARGETDIR", ordering=1) - c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=2) - c.event("EndDialog", "Return", ordering=3) - - c = seldlg.cancel("Cancel", "DirectoryCombo") + order = 1 + c.event("[TARGETDIR]", "[SourceDir]", ordering=order) + for version in self.versions + [self.other_version]: + order += 1 + c.event("[TARGETDIR]", "[TARGETDIR%s]" % version, + "FEATURE_SELECTED AND &Python%s=3" % version, + ordering=order) + c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1) + c.event("EndDialog", "Return", ordering=order + 2) + c = seldlg.cancel("Cancel", "Features") c.event("SpawnDialog", "CancelDlg") - seldlg.control("DirectoryCombo", "DirectoryCombo", 15, 70, 272, 80, 393219, - "TARGETDIR", None, "DirectoryList", None) - seldlg.control("DirectoryList", "DirectoryList", 15, 90, 308, 136, 3, "TARGETDIR", - None, "PathEdit", None) - seldlg.control("PathEdit", "PathEdit", 15, 230, 306, 16, 3, "TARGETDIR", None, "Next", None) - c = seldlg.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None) - c.event("DirectoryListUp", "0") - c = seldlg.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None) - c.event("DirectoryListNew", "0") + c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3, + "FEATURE", None, "PathEdit", None) + c.event("[FEATURE_SELECTED]", "1") + ver = self.other_version + install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver + dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver + + c = seldlg.text("Other", 15, 200, 300, 15, 3, + "Provide an alternate Python location") + c.condition("Enable", install_other_cond) + c.condition("Show", install_other_cond) + c.condition("Disable", dont_install_other_cond) + c.condition("Hide", dont_install_other_cond) + + c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1, + "TARGETDIR" + ver, None, "Next", None) + c.condition("Enable", install_other_cond) + c.condition("Show", install_other_cond) + c.condition("Disable", dont_install_other_cond) + c.condition("Hide", dont_install_other_cond) ##################################################################### # Disk cost @@ -640,7 +723,10 @@ class bdist_msi(Command): def get_installer_filename(self, fullname): # Factored out to allow overriding in subclasses - base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, - self.target_version) + if self.target_version: + base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, + self.target_version) + else: + base_name = "%s.%s.msi" % (fullname, self.plat_name) installer_name = os.path.join(self.dist_dir, base_name) return installer_name -- cgit v1.2.1 From 1cd9b19ad7991f6709ed553b2e752fd2d0b6d47c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 6 May 2009 07:17:52 +0000 Subject: Added a test and cleaned check_library_list to be ready to fix #5940 --- command/build_clib.py | 32 +++++++++++++++----------------- tests/test_build_clib.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 17 deletions(-) create mode 100644 tests/test_build_clib.py diff --git a/command/build_clib.py b/command/build_clib.py index fe921fb8..1d8cd8c3 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -127,43 +127,41 @@ class build_clib (Command): # run() - def check_library_list (self, libraries): - """Ensure that the list of libraries (presumably provided as a - command option 'libraries') is valid, i.e. it is a list of - 2-tuples, where the tuples are (library_name, build_info_dict). - Raise DistutilsSetupError if the structure is invalid anywhere; - just returns otherwise.""" + def check_library_list(self, libraries): + """Ensure that the list of libraries is valid. - # Yechh, blecch, ackk: this is ripped straight out of build_ext.py, - # with only names changed to protect the innocent! + `library` is presumably provided as a command option 'libraries'. + This method checks that it is a list of 2-tuples, where the tuples + are (library_name, build_info_dict). - if type(libraries) is not ListType: + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ + if not isinstance(libraries, list): raise DistutilsSetupError, \ "'libraries' option must be a list of tuples" for lib in libraries: - if type(lib) is not TupleType and len(lib) != 2: + if not isinstance(lib, tuple) and len(lib) != 2: raise DistutilsSetupError, \ "each element of 'libraries' must a 2-tuple" - if type(lib[0]) is not StringType: + name, build_info = lib + + if not isinstance(name, str): raise DistutilsSetupError, \ "first element of each tuple in 'libraries' " + \ "must be a string (the library name)" - if '/' in lib[0] or (os.sep != '/' and os.sep in lib[0]): + if '/' in name or (os.sep != '/' and os.sep in name): raise DistutilsSetupError, \ ("bad library name '%s': " + "may not contain directory separators") % \ lib[0] - if type(lib[1]) is not DictionaryType: + if not isinstance(build_info, dict): raise DistutilsSetupError, \ "second element of each tuple in 'libraries' " + \ "must be a dictionary (build info)" - # for lib - - # check_library_list () - def get_library_names (self): # Assume the library list is valid -- 'check_library_list()' is diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py new file mode 100644 index 00000000..36c07b79 --- /dev/null +++ b/tests/test_build_clib.py @@ -0,0 +1,47 @@ +"""Tests for distutils.command.build_clib.""" +import unittest + +from distutils.command.build_clib import build_clib +from distutils.errors import DistutilsSetupError +from distutils.tests import support + +class BuildCLibTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_check_library_dist(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + # 'libraries' option must be a list + self.assertRaises(DistutilsSetupError, cmd.check_library_list, 'foo') + + # each element of 'libraries' must a 2-tuple + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + ['foo1', 'foo2']) + + # first element of each tuple in 'libraries' + # must be a string (the library name) + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [(1, 'foo1'), ('name', 'foo2')]) + + # library name may not contain directory separators + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [('name', 'foo1'), + ('another/name', 'foo2')]) + + # second element of each tuple must be a dictionary (build info) + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [('name', {}), + ('another', 'foo2')]) + + # those work + libs = [('name', {}), ('name', {'ok': 'good'})] + cmd.check_library_list(libs) + + +def test_suite(): + return unittest.makeSuite(BuildCLibTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 0a4c48c5ff377d4d8e7e5ee1c151dace8b57ef83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 6 May 2009 07:26:24 +0000 Subject: Fixed #5940: distutils.command.build_clib.check_library_list is doing the right checkings again --- command/build_clib.py | 25 +++++++++++++++---------- tests/test_build_clib.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 tests/test_build_clib.py diff --git a/command/build_clib.py b/command/build_clib.py index 34f49836..258d7c10 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -118,13 +118,15 @@ class build_clib(Command): def check_library_list(self, libraries): - """Ensure that the list of libraries (presumably provided as a - command option 'libraries') is valid, i.e. it is a list of - 2-tuples, where the tuples are (library_name, build_info_dict). - Raise DistutilsSetupError if the structure is invalid anywhere; - just returns otherwise.""" - # Yechh, blecch, ackk: this is ripped straight out of build_ext.py, - # with only names changed to protect the innocent! + """Ensure that the list of libraries is valid. + + `library` is presumably provided as a command option 'libraries'. + This method checks that it is a list of 2-tuples, where the tuples + are (library_name, build_info_dict). + + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ if not isinstance(libraries, list): raise DistutilsSetupError( "'libraries' option must be a list of tuples") @@ -134,15 +136,18 @@ class build_clib(Command): raise DistutilsSetupError( "each element of 'libraries' must a 2-tuple") - if isinstance(lib[0], str): + name, build_info = lib + + if not isinstance(name, str): raise DistutilsSetupError( "first element of each tuple in 'libraries' " "must be a string (the library name)") - if '/' in lib[0] or (os.sep != '/' and os.sep in lib[0]): + + if '/' in name or (os.sep != '/' and os.sep in name): raise DistutilsSetupError("bad library name '%s': " "may not contain directory separators" % lib[0]) - if not isinstance(lib[1], dict): + if not isinstance(build_info, dict): raise DistutilsSetupError( "second element of each tuple in 'libraries' " "must be a dictionary (build info)") diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py new file mode 100644 index 00000000..36c07b79 --- /dev/null +++ b/tests/test_build_clib.py @@ -0,0 +1,47 @@ +"""Tests for distutils.command.build_clib.""" +import unittest + +from distutils.command.build_clib import build_clib +from distutils.errors import DistutilsSetupError +from distutils.tests import support + +class BuildCLibTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_check_library_dist(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + # 'libraries' option must be a list + self.assertRaises(DistutilsSetupError, cmd.check_library_list, 'foo') + + # each element of 'libraries' must a 2-tuple + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + ['foo1', 'foo2']) + + # first element of each tuple in 'libraries' + # must be a string (the library name) + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [(1, 'foo1'), ('name', 'foo2')]) + + # library name may not contain directory separators + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [('name', 'foo1'), + ('another/name', 'foo2')]) + + # second element of each tuple must be a dictionary (build info) + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [('name', {}), + ('another', 'foo2')]) + + # those work + libs = [('name', {}), ('name', {'ok': 'good'})] + cmd.check_library_list(libs) + + +def test_suite(): + return unittest.makeSuite(BuildCLibTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 5c0bfa1ffda99f8f88c4f31360812dd8aa9633ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 6 May 2009 07:41:53 +0000 Subject: pep8-fied build_clib module : it is now similar to the one in 3.x --- command/build_clib.py | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index 1d8cd8c3..18415cfc 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -17,7 +17,6 @@ __revision__ = "$Id$" # cut 'n paste. Sigh. import os, string -from types import * from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler @@ -52,7 +51,7 @@ class build_clib (Command): "list available compilers", show_compilers), ] - def initialize_options (self): + def initialize_options(self): self.build_clib = None self.build_temp = None @@ -67,11 +66,8 @@ class build_clib (Command): self.force = 0 self.compiler = None - # initialize_options() - - - def finalize_options (self): + def finalize_options(self): # This might be confusing: both build-clib and build-temp default # to build-temp as defined by the "build" command. This is because # I think that C libraries are really just temporary build @@ -97,11 +93,7 @@ class build_clib (Command): # XXX same as for build_ext -- what about 'self.define' and # 'self.undef' ? - # finalize_options() - - - def run (self): - + def run(self): if not self.libraries: return @@ -124,8 +116,6 @@ class build_clib (Command): self.build_libraries(self.libraries) - # run() - def check_library_list(self, libraries): """Ensure that the list of libraries is valid. @@ -163,10 +153,9 @@ class build_clib (Command): "second element of each tuple in 'libraries' " + \ "must be a dictionary (build info)" - def get_library_names (self): + def get_library_names(self): # Assume the library list is valid -- 'check_library_list()' is # called from 'finalize_options()', so it should be! - if not self.libraries: return None @@ -175,10 +164,8 @@ class build_clib (Command): lib_names.append(lib_name) return lib_names - # get_library_names () - - def get_source_files (self): + def get_source_files(self): self.check_library_list(self.libraries) filenames = [] for (lib_name, build_info) in self.libraries: @@ -191,13 +178,9 @@ class build_clib (Command): "a list of source filenames") % lib_name filenames.extend(sources) - return filenames - # get_source_files () - def build_libraries (self, libraries): - for (lib_name, build_info) in libraries: sources = build_info.get('sources') if sources is None or type(sources) not in (ListType, TupleType): @@ -226,9 +209,3 @@ class build_clib (Command): self.compiler.create_static_lib(objects, lib_name, output_dir=self.build_clib, debug=self.debug) - - # for libraries - - # build_libraries () - -# class build_lib -- cgit v1.2.1 From d337edee8fe4fa9c4d9dbefa65c1829c97b231c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 6 May 2009 08:05:47 +0000 Subject: more build_clib cleanup + test coverage --- command/build_clib.py | 7 +++--- tests/test_build_clib.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index 18415cfc..447ea949 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -86,7 +86,7 @@ class build_clib (Command): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - if type(self.include_dirs) is StringType: + if isinstance(self.include_dirs, str): self.include_dirs = string.split(self.include_dirs, os.pathsep) @@ -170,8 +170,7 @@ class build_clib (Command): filenames = [] for (lib_name, build_info) in self.libraries: sources = build_info.get('sources') - if (sources is None or - type(sources) not in (ListType, TupleType) ): + if sources is None or not isinstance(sources, (list, tuple)): raise DistutilsSetupError, \ ("in 'libraries' option (library '%s'), " "'sources' must be present and must be " @@ -183,7 +182,7 @@ class build_clib (Command): def build_libraries (self, libraries): for (lib_name, build_info) in libraries: sources = build_info.get('sources') - if sources is None or type(sources) not in (ListType, TupleType): + if sources is None or not isinstance(sources, (list, tuple)): raise DistutilsSetupError, \ ("in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 36c07b79..7374c495 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -39,6 +39,63 @@ class BuildCLibTestCase(support.TempdirManager, libs = [('name', {}), ('name', {'ok': 'good'})] cmd.check_library_list(libs) + def test_get_source_files(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + # "in 'libraries' option 'sources' must be present and must be + # a list of source filenames + cmd.libraries = [('name', {})] + self.assertRaises(DistutilsSetupError, cmd.get_source_files) + + cmd.libraries = [('name', {'sources': 1})] + self.assertRaises(DistutilsSetupError, cmd.get_source_files) + + cmd.libraries = [('name', {'sources': ['a', 'b']})] + self.assertEquals(cmd.get_source_files(), ['a', 'b']) + + cmd.libraries = [('name', {'sources': ('a', 'b')})] + self.assertEquals(cmd.get_source_files(), ['a', 'b']) + + cmd.libraries = [('name', {'sources': ('a', 'b')}), + ('name2', {'sources': ['c', 'd']})] + self.assertEquals(cmd.get_source_files(), ['a', 'b', 'c', 'd']) + + def test_build_libraries(self): + + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + class FakeCompiler: + def compile(*args, **kw): + pass + create_static_lib = compile + + cmd.compiler = FakeCompiler() + + # build_libraries is also doing a bit of typoe checking + lib = [('name', {'sources': 'notvalid'})] + self.assertRaises(DistutilsSetupError, cmd.build_libraries, lib) + + lib = [('name', {'sources': list()})] + cmd.build_libraries(lib) + + lib = [('name', {'sources': tuple()})] + cmd.build_libraries(lib) + + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + cmd.include_dirs = 'one-dir' + cmd.finalize_options() + self.assertEquals(cmd.include_dirs, ['one-dir']) + + cmd.include_dirs = None + cmd.finalize_options() + self.assertEquals(cmd.include_dirs, []) + + cmd.distribution.libraries = 'WONTWORK' + self.assertRaises(DistutilsSetupError, cmd.finalize_options) def test_suite(): return unittest.makeSuite(BuildCLibTestCase) -- cgit v1.2.1 From 822b9626452ce994033db5ae7af1139eaeb5ff68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 6 May 2009 08:08:26 +0000 Subject: Merged revisions 72388 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72388 | tarek.ziade | 2009-05-06 10:05:47 +0200 (Wed, 06 May 2009) | 1 line more build_clib cleanup + test coverage ........ --- tests/test_build_clib.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 36c07b79..7374c495 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -39,6 +39,63 @@ class BuildCLibTestCase(support.TempdirManager, libs = [('name', {}), ('name', {'ok': 'good'})] cmd.check_library_list(libs) + def test_get_source_files(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + # "in 'libraries' option 'sources' must be present and must be + # a list of source filenames + cmd.libraries = [('name', {})] + self.assertRaises(DistutilsSetupError, cmd.get_source_files) + + cmd.libraries = [('name', {'sources': 1})] + self.assertRaises(DistutilsSetupError, cmd.get_source_files) + + cmd.libraries = [('name', {'sources': ['a', 'b']})] + self.assertEquals(cmd.get_source_files(), ['a', 'b']) + + cmd.libraries = [('name', {'sources': ('a', 'b')})] + self.assertEquals(cmd.get_source_files(), ['a', 'b']) + + cmd.libraries = [('name', {'sources': ('a', 'b')}), + ('name2', {'sources': ['c', 'd']})] + self.assertEquals(cmd.get_source_files(), ['a', 'b', 'c', 'd']) + + def test_build_libraries(self): + + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + class FakeCompiler: + def compile(*args, **kw): + pass + create_static_lib = compile + + cmd.compiler = FakeCompiler() + + # build_libraries is also doing a bit of typoe checking + lib = [('name', {'sources': 'notvalid'})] + self.assertRaises(DistutilsSetupError, cmd.build_libraries, lib) + + lib = [('name', {'sources': list()})] + cmd.build_libraries(lib) + + lib = [('name', {'sources': tuple()})] + cmd.build_libraries(lib) + + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + cmd.include_dirs = 'one-dir' + cmd.finalize_options() + self.assertEquals(cmd.include_dirs, ['one-dir']) + + cmd.include_dirs = None + cmd.finalize_options() + self.assertEquals(cmd.include_dirs, []) + + cmd.distribution.libraries = 'WONTWORK' + self.assertRaises(DistutilsSetupError, cmd.finalize_options) def test_suite(): return unittest.makeSuite(BuildCLibTestCase) -- cgit v1.2.1 From a990c6a1f3084b141a9be5772685e1a8b7498515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 6 May 2009 08:11:00 +0000 Subject: removed string.split usage --- command/build_clib.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index 447ea949..d475232b 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -16,7 +16,7 @@ __revision__ = "$Id$" # two modules, mainly because a number of subtle details changed in the # cut 'n paste. Sigh. -import os, string +import os from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler @@ -87,8 +87,7 @@ class build_clib (Command): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if isinstance(self.include_dirs, str): - self.include_dirs = string.split(self.include_dirs, - os.pathsep) + self.include_dirs = self.include_dirs.split(os.pathsep) # XXX same as for build_ext -- what about 'self.define' and # 'self.undef' ? -- cgit v1.2.1 From 14c20a579282de3d0fc983b150630d1f2f5d7f99 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 6 May 2009 20:43:28 +0000 Subject: bump version to 3.1b1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c6680636..80ccf611 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1a2" +__version__ = "3.1b1" #--end constants-- -- cgit v1.2.1 From 8bbedf87070ecfa92b99ef0a704eab780c15e3a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 7 May 2009 21:13:02 +0000 Subject: removed remaining spaces --- command/build_clib.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index d475232b..9545b27a 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -22,12 +22,12 @@ from distutils.errors import * from distutils.sysconfig import customize_compiler from distutils import log -def show_compilers (): +def show_compilers(): from distutils.ccompiler import show_compilers show_compilers() -class build_clib (Command): +class build_clib(Command): description = "build C/C++ libraries used by Python extensions" @@ -178,7 +178,7 @@ class build_clib (Command): filenames.extend(sources) return filenames - def build_libraries (self, libraries): + def build_libraries(self, libraries): for (lib_name, build_info) in libraries: sources = build_info.get('sources') if sources is None or not isinstance(sources, (list, tuple)): -- cgit v1.2.1 From afc36e0fa764d77b641b477a92f769fb225a4c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 7 May 2009 21:20:34 +0000 Subject: Fixed #5941: added ARFLAGS for the archiver command. --- sysconfig.py | 10 +++++++--- tests/test_build_clib.py | 40 ++++++++++++++++++++++++++++++++++++++++ tests/test_sysconfig.py | 5 +++-- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 56cb861a..099e0586 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -165,9 +165,9 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar) = \ + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO', 'AR') + 'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS') if 'CC' in os.environ: cc = os.environ['CC'] @@ -190,6 +190,10 @@ def customize_compiler(compiler): ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] if 'AR' in os.environ: ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] + else: + archiver = ar + ' ' + ar_flags cc_cmd = cc + ' ' + cflags compiler.set_executables( @@ -199,7 +203,7 @@ def customize_compiler(compiler): compiler_cxx=cxx, linker_so=ldshared, linker_exe=cc, - archiver=ar) + archiver=archiver) compiler.shared_lib_extension = so_ext diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 7374c495..3c6643f3 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -1,9 +1,12 @@ """Tests for distutils.command.build_clib.""" import unittest +import os +import sys from distutils.command.build_clib import build_clib from distutils.errors import DistutilsSetupError from distutils.tests import support +from distutils.spawn import find_executable class BuildCLibTestCase(support.TempdirManager, support.LoggingSilencer, @@ -97,6 +100,43 @@ class BuildCLibTestCase(support.TempdirManager, cmd.distribution.libraries = 'WONTWORK' self.assertRaises(DistutilsSetupError, cmd.finalize_options) + def test_run(self): + # can't test on windows + if sys.platform == 'win32': + return + + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + foo_c = os.path.join(pkg_dir, 'foo.c') + self.write_file(foo_c, 'int main(void) { return 1;}') + cmd.libraries = [('foo', {'sources': [foo_c]})] + + build_temp = os.path.join(pkg_dir, 'build') + os.mkdir(build_temp) + cmd.build_temp = build_temp + cmd.build_clib = build_temp + + # before we run the command, we want to make sure + # all commands are present on the system + # by creating a compiler and checking its executables + from distutils.ccompiler import new_compiler + from distutils.sysconfig import customize_compiler + + compiler = new_compiler() + customize_compiler(compiler) + for ccmd in compiler.executables.values(): + if ccmd is None: + continue + if find_executable(ccmd[0]) is None: + return # can't test + + # this should work + cmd.run() + + # let's check the result + self.assert_('libfoo.a' in os.listdir(build_temp)) + def test_suite(): return unittest.makeSuite(BuildCLibTestCase) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index dd17eb34..040dfcb6 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -49,7 +49,8 @@ class SysconfigTestCase(unittest.TestCase): if get_default_compiler() != 'unix': return - os.environ['AR'] = 'xxx' + os.environ['AR'] = 'my_ar' + os.environ['ARFLAGS'] = '-arflags' # make sure AR gets caught class compiler: @@ -60,7 +61,7 @@ class SysconfigTestCase(unittest.TestCase): comp = compiler() sysconfig.customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'xxx') + self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') def test_suite(): -- cgit v1.2.1 From 40f8502c0bede251d25993361779f82b1ffe44db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 7 May 2009 21:24:43 +0000 Subject: Merged revisions 72445 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72445 | tarek.ziade | 2009-05-07 23:20:34 +0200 (Thu, 07 May 2009) | 1 line Fixed #5941: added ARFLAGS for the archiver command. ........ --- sysconfig.py | 10 +++++++--- tests/test_build_clib.py | 40 ++++++++++++++++++++++++++++++++++++++++ tests/test_sysconfig.py | 5 +++-- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index fbeb45f8..223ff672 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -160,9 +160,9 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar) = \ + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO', 'AR') + 'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS') if 'CC' in os.environ: cc = os.environ['CC'] @@ -185,6 +185,10 @@ def customize_compiler(compiler): ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] if 'AR' in os.environ: ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] + else: + archiver = ar + ' ' + ar_flags cc_cmd = cc + ' ' + cflags compiler.set_executables( @@ -194,7 +198,7 @@ def customize_compiler(compiler): compiler_cxx=cxx, linker_so=ldshared, linker_exe=cc, - archiver=ar) + archiver=archiver) compiler.shared_lib_extension = so_ext diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 7374c495..3c6643f3 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -1,9 +1,12 @@ """Tests for distutils.command.build_clib.""" import unittest +import os +import sys from distutils.command.build_clib import build_clib from distutils.errors import DistutilsSetupError from distutils.tests import support +from distutils.spawn import find_executable class BuildCLibTestCase(support.TempdirManager, support.LoggingSilencer, @@ -97,6 +100,43 @@ class BuildCLibTestCase(support.TempdirManager, cmd.distribution.libraries = 'WONTWORK' self.assertRaises(DistutilsSetupError, cmd.finalize_options) + def test_run(self): + # can't test on windows + if sys.platform == 'win32': + return + + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + foo_c = os.path.join(pkg_dir, 'foo.c') + self.write_file(foo_c, 'int main(void) { return 1;}') + cmd.libraries = [('foo', {'sources': [foo_c]})] + + build_temp = os.path.join(pkg_dir, 'build') + os.mkdir(build_temp) + cmd.build_temp = build_temp + cmd.build_clib = build_temp + + # before we run the command, we want to make sure + # all commands are present on the system + # by creating a compiler and checking its executables + from distutils.ccompiler import new_compiler + from distutils.sysconfig import customize_compiler + + compiler = new_compiler() + customize_compiler(compiler) + for ccmd in compiler.executables.values(): + if ccmd is None: + continue + if find_executable(ccmd[0]) is None: + return # can't test + + # this should work + cmd.run() + + # let's check the result + self.assert_('libfoo.a' in os.listdir(build_temp)) + def test_suite(): return unittest.makeSuite(BuildCLibTestCase) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 1e9dbd54..971aac6d 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -49,7 +49,8 @@ class SysconfigTestCase(unittest.TestCase): if get_default_compiler() != 'unix': return - os.environ['AR'] = 'xxx' + os.environ['AR'] = 'my_ar' + os.environ['ARFLAGS'] = '-arflags' # make sure AR gets caught class compiler: @@ -60,7 +61,7 @@ class SysconfigTestCase(unittest.TestCase): comp = compiler() sysconfig.customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'xxx') + self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') def test_suite(): -- cgit v1.2.1 From 217fe3228dd9a092a05ddf8d777ef5eca22bf600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 7 May 2009 23:01:56 +0000 Subject: fixed AR/ARFLAGS values in test_sysconfig --- tests/test_sysconfig.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 040dfcb6..bf0043aa 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -11,11 +11,15 @@ from test.test_support import TESTFN class SysconfigTestCase(unittest.TestCase): def setUp(self): - self.old_AR = os.environ.get('AR') + self.old_flags = [('AR', os.environ.get('AR')), + ('ARFLAGS', os.environ.get('ARFLAGS'))] def tearDown(self): - if self.old_AR is not None: - os.environ['AR'] = self.old_AR + for name, value in self.old_flags: + if value is not None: + os.environ[name] = value + elif name in os.environ: + del os.environ[name] def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() -- cgit v1.2.1 From 6218b5aeb11b942cdb54f15043086f55ac1927d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 7 May 2009 23:11:34 +0000 Subject: Merged revisions 72454 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72454 | tarek.ziade | 2009-05-08 01:01:56 +0200 (Fri, 08 May 2009) | 1 line fixed AR/ARFLAGS values in test_sysconfig ........ --- tests/test_sysconfig.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 971aac6d..f65bc725 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -11,11 +11,15 @@ from test.support import TESTFN class SysconfigTestCase(unittest.TestCase): def setUp(self): - self.old_AR = os.environ.get('AR') + self.old_flags = [('AR', os.environ.get('AR')), + ('ARFLAGS', os.environ.get('ARFLAGS'))] def tearDown(self): - if self.old_AR is not None: - os.environ['AR'] = self.old_AR + for name, value in self.old_flags: + if value is not None: + os.environ[name] = value + elif name in os.environ: + del os.environ[name] def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() -- cgit v1.2.1 From 9e299d5f4ef5f92ed722d5176e07d7d98c4292bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 9 May 2009 07:12:44 +0000 Subject: Revert encoding error from r72309. --- command/bdist_msi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index ffd668eb..96451586 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2005, 2006 Martin v. Löwis +# Copyright (C) 2005, 2006 Martin von Löwis # Licensed to PSF under a Contributor Agreement. # The bdist_wininst command proper # based on bdist_wininst -- cgit v1.2.1 From f2cc5e5fffe3aeb72a2d20bc2feffe5faa587c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 9 May 2009 08:28:53 +0000 Subject: Fixed Issue 5900: distutils.command.build_ext - Ensure RUNPATH is added to extension modules with RPATH if GNU ld is used --- tests/test_build_ext.py | 3 +- tests/test_unixccompiler.py | 93 +++++++++++++++++++++++++++++++++++++++++++++ unixccompiler.py | 24 +++++++++--- 3 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 tests/test_unixccompiler.py diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 9097bee2..141c2869 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -125,7 +125,7 @@ class BuildExtTestCase(support.TempdirManager, dist = Distribution({'name': 'xx'}) cmd = build_ext(dist) - # making sure the suer option is there + # making sure the user option is there options = [name for name, short, lable in cmd.user_options] self.assert_('user' in options) @@ -145,6 +145,7 @@ class BuildExtTestCase(support.TempdirManager, # see if include_dirs and library_dirs # were set self.assert_(lib in cmd.library_dirs) + self.assert_(lib in cmd.rpath) self.assert_(incl in cmd.include_dirs) def test_optional_extension(self): diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py new file mode 100644 index 00000000..94e9edfc --- /dev/null +++ b/tests/test_unixccompiler.py @@ -0,0 +1,93 @@ +"""Tests for distutils.unixccompiler.""" +import sys +import unittest + +from distutils import sysconfig +from distutils.unixccompiler import UnixCCompiler + +class UnixCCompilerTestCase(unittest.TestCase): + + def setUp(self): + self._backup_platform = sys.platform + self._backup_get_config_var = sysconfig.get_config_var + class CompilerWrapper(UnixCCompiler): + def rpath_foo(self): + return self.runtime_library_dir_option('/foo') + self.cc = CompilerWrapper() + + def tearDown(self): + sys.platform = self._backup_platform + sysconfig.get_config_var = self._backup_get_config_var + + def test_runtime_libdir_option(self): + + # not tested under windows + if sys.platform == 'win32': + return + + # Issue#5900 + # + # Ensure RUNPATH is added to extension modules with RPATH if + # GNU ld is used + + # darwin + sys.platform = 'darwin' + self.assertEqual(self.cc.rpath_foo(), '-L/foo') + + # hp-ux + sys.platform = 'hp-ux' + self.assertEqual(self.cc.rpath_foo(), '+s -L/foo') + + # irix646 + sys.platform = 'irix646' + self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo']) + + # osf1V5 + sys.platform = 'osf1V5' + self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo']) + + # GCC GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'gcc' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + + # GCC non-GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'gcc' + elif v == 'GNULD': + return 'no' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') + + # non-GCC GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'cc' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + + # non-GCC non-GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'cc' + elif v == 'GNULD': + return 'no' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + +def test_suite(): + return unittest.makeSuite(UnixCCompilerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/unixccompiler.py b/unixccompiler.py index 045368a9..0b1dc4af 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -273,8 +273,9 @@ class UnixCCompiler(CCompiler): # Linkers on different platforms need different options to # specify that directories need to be added to the list of # directories searched for dependencies when a dynamic library - # is sought. GCC has to be told to pass the -R option through - # to the linker, whereas other compilers just know this. + # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to + # be told to pass the -R option through to the linker, whereas + # other compilers and gcc on other systems just know this. # Other compilers may need something slightly different. At # this time, there's no way to determine this information from # the configuration data stored in the Python installation, so @@ -287,10 +288,23 @@ class UnixCCompiler(CCompiler): return "+s -L" + dir elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] - elif compiler[:3] == "gcc" or compiler[:3] == "g++": - return "-Wl,-R" + dir else: - return "-R" + dir + if compiler[:3] == "gcc" or compiler[:3] == "g++": + # gcc on non-GNU systems does not need -Wl, but can + # use it anyway. Since distutils has always passed in + # -Wl whenever gcc was used in the past it is probably + # safest to keep doing so. + if sysconfig.get_config_var("GNULD") == "yes": + # GNU ld needs an extra option to get a RUNPATH + # instead of just an RPATH. + return "-Wl,--enable-new-dtags,-R" + dir + else: + return "-Wl,-R" + dir + else: + # No idea how --enable-new-dtags would be passed on to + # ld if this system was using GNU ld. Don't know if a + # system like this even exists. + return "-R" + dir def library_option(self, lib): return "-l" + lib -- cgit v1.2.1 From fbc4ab332c9be99d822e2cbd8728cb34560f5ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 9 May 2009 10:06:00 +0000 Subject: #5976: fixed distutils test_check_environ --- tests/test_util.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/test_util.py b/tests/test_util.py index 348933e9..ea7c5925 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -214,12 +214,17 @@ class utilTestCase(unittest.TestCase): # posix without HOME if os.name == 'posix': # this test won't run on windows - os.environ = {} - check_environ() - - import pwd - self.assertEquals(os.environ['HOME'], - pwd.getpwuid(os.getuid())[5]) + old_home = os.environ.get('HOME') + try: + check_environ() + import pwd + self.assertEquals(os.environ['HOME'], + pwd.getpwuid(os.getuid())[5]) + finally: + if old_home is not None: + os.environ['HOME'] = old_home + else: + del os.environ['HOME'] else: check_environ() -- cgit v1.2.1 From 8faf60dd1c9f552ad552a6267ec8bcb894bd8792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 9 May 2009 10:09:11 +0000 Subject: Merged revisions 72500 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72500 | tarek.ziade | 2009-05-09 12:06:00 +0200 (Sat, 09 May 2009) | 1 line #5976: fixed distutils test_check_environ ........ --- tests/test_util.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/test_util.py b/tests/test_util.py index 348933e9..ea7c5925 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -214,12 +214,17 @@ class utilTestCase(unittest.TestCase): # posix without HOME if os.name == 'posix': # this test won't run on windows - os.environ = {} - check_environ() - - import pwd - self.assertEquals(os.environ['HOME'], - pwd.getpwuid(os.getuid())[5]) + old_home = os.environ.get('HOME') + try: + check_environ() + import pwd + self.assertEquals(os.environ['HOME'], + pwd.getpwuid(os.getuid())[5]) + finally: + if old_home is not None: + os.environ['HOME'] = old_home + else: + del os.environ['HOME'] else: check_environ() -- cgit v1.2.1 From 6625a07e76fff84d1f3ed50ded7d1861744ffce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 9 May 2009 11:55:12 +0000 Subject: Merged revisions 72497 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72497 | tarek.ziade | 2009-05-09 10:28:53 +0200 (Sat, 09 May 2009) | 1 line Fixed Issue 5900: distutils.command.build_ext - Ensure RUNPATH is added to extension modules with RPATH if GNU ld is used ........ --- tests/test_build_ext.py | 3 +- tests/test_unixccompiler.py | 93 +++++++++++++++++++++++++++++++++++++++++++++ unixccompiler.py | 24 +++++++++--- 3 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 tests/test_unixccompiler.py diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 5ea67bed..e4a02391 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -125,7 +125,7 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx'}) cmd = build_ext(dist) - # making sure the suer option is there + # making sure the user option is there options = [name for name, short, lable in cmd.user_options] self.assert_('user' in options) @@ -145,6 +145,7 @@ class BuildExtTestCase(TempdirManager, # see if include_dirs and library_dirs # were set self.assert_(lib in cmd.library_dirs) + self.assert_(lib in cmd.rpath) self.assert_(incl in cmd.include_dirs) def test_optional_extension(self): diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py new file mode 100644 index 00000000..94e9edfc --- /dev/null +++ b/tests/test_unixccompiler.py @@ -0,0 +1,93 @@ +"""Tests for distutils.unixccompiler.""" +import sys +import unittest + +from distutils import sysconfig +from distutils.unixccompiler import UnixCCompiler + +class UnixCCompilerTestCase(unittest.TestCase): + + def setUp(self): + self._backup_platform = sys.platform + self._backup_get_config_var = sysconfig.get_config_var + class CompilerWrapper(UnixCCompiler): + def rpath_foo(self): + return self.runtime_library_dir_option('/foo') + self.cc = CompilerWrapper() + + def tearDown(self): + sys.platform = self._backup_platform + sysconfig.get_config_var = self._backup_get_config_var + + def test_runtime_libdir_option(self): + + # not tested under windows + if sys.platform == 'win32': + return + + # Issue#5900 + # + # Ensure RUNPATH is added to extension modules with RPATH if + # GNU ld is used + + # darwin + sys.platform = 'darwin' + self.assertEqual(self.cc.rpath_foo(), '-L/foo') + + # hp-ux + sys.platform = 'hp-ux' + self.assertEqual(self.cc.rpath_foo(), '+s -L/foo') + + # irix646 + sys.platform = 'irix646' + self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo']) + + # osf1V5 + sys.platform = 'osf1V5' + self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo']) + + # GCC GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'gcc' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + + # GCC non-GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'gcc' + elif v == 'GNULD': + return 'no' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') + + # non-GCC GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'cc' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + + # non-GCC non-GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'cc' + elif v == 'GNULD': + return 'no' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + +def test_suite(): + return unittest.makeSuite(UnixCCompilerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/unixccompiler.py b/unixccompiler.py index d65ab321..c11544d8 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -271,8 +271,9 @@ class UnixCCompiler(CCompiler): # Linkers on different platforms need different options to # specify that directories need to be added to the list of # directories searched for dependencies when a dynamic library - # is sought. GCC has to be told to pass the -R option through - # to the linker, whereas other compilers just know this. + # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to + # be told to pass the -R option through to the linker, whereas + # other compilers and gcc on other systems just know this. # Other compilers may need something slightly different. At # this time, there's no way to determine this information from # the configuration data stored in the Python installation, so @@ -285,10 +286,23 @@ class UnixCCompiler(CCompiler): return "+s -L" + dir elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] - elif compiler[:3] == "gcc" or compiler[:3] == "g++": - return "-Wl,-R" + dir else: - return "-R" + dir + if compiler[:3] == "gcc" or compiler[:3] == "g++": + # gcc on non-GNU systems does not need -Wl, but can + # use it anyway. Since distutils has always passed in + # -Wl whenever gcc was used in the past it is probably + # safest to keep doing so. + if sysconfig.get_config_var("GNULD") == "yes": + # GNU ld needs an extra option to get a RUNPATH + # instead of just an RPATH. + return "-Wl,--enable-new-dtags,-R" + dir + else: + return "-Wl,-R" + dir + else: + # No idea how --enable-new-dtags would be passed on to + # ld if this system was using GNU ld. Don't know if a + # system like this even exists. + return "-R" + dir def library_option(self, lib): return "-l" + lib -- cgit v1.2.1 From c2b1e652ad5c124b958955a646efa51a17810609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 10:12:08 +0000 Subject: fixed #5984 and improved test coverage --- command/build_ext.py | 70 ++++++++++---------------- tests/test_build_ext.py | 128 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 47 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 2c6df1d2..dfabf910 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -136,7 +136,7 @@ class build_ext (Command): self.swig_opts = None self.user = None - def finalize_options (self): + def finalize_options(self): from distutils import sysconfig self.set_undefined_options('build', @@ -153,15 +153,14 @@ class build_ext (Command): self.extensions = self.distribution.ext_modules - # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. py_include = sysconfig.get_python_inc() plat_py_include = sysconfig.get_python_inc(plat_specific=1) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - if type(self.include_dirs) is StringType: - self.include_dirs = string.split(self.include_dirs, os.pathsep) + if isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. @@ -169,7 +168,7 @@ class build_ext (Command): if plat_py_include != py_include: self.include_dirs.append(plat_py_include) - if type(self.libraries) is StringType: + if isinstance(self.libraries, str): self.libraries = [self.libraries] # Life is easier if we're not forever checking for None, so @@ -260,14 +259,14 @@ class build_ext (Command): # symbols can be separated with commas. if self.define: - defines = string.split(self.define, ',') + defines = self.define.split(',') self.define = map(lambda symbol: (symbol, '1'), defines) # The option for macros to undefine is also a string from the # option parsing, but has to be a list. Multiple symbols can also # be separated with commas here. if self.undef: - self.undef = string.split(self.undef, ',') + self.undef = self.undef.split(',') if self.swig_opts is None: self.swig_opts = [] @@ -284,11 +283,7 @@ class build_ext (Command): self.library_dirs.append(user_lib) self.rpath.append(user_lib) - # finalize_options () - - - def run (self): - + def run(self): from distutils.ccompiler import new_compiler # 'self.extensions', as supplied by setup.py, is a list of @@ -335,7 +330,7 @@ class build_ext (Command): self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples - for (name,value) in self.define: + for (name, value) in self.define: self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: @@ -352,10 +347,7 @@ class build_ext (Command): # Now actually compile and link everything. self.build_extensions() - # run () - - - def check_extensions_list (self, extensions): + def check_extensions_list(self, extensions): """Ensure that the list of extensions (presumably provided as a command option 'extensions') is valid, i.e. it is a list of Extension objects. We also support the old-style list of 2-tuples, @@ -365,32 +357,33 @@ class build_ext (Command): Raise DistutilsSetupError if the structure is invalid anywhere; just returns otherwise. """ - if type(extensions) is not ListType: + if not isinstance(extensions, list): raise DistutilsSetupError, \ "'ext_modules' option must be a list of Extension instances" - for i in range(len(extensions)): - ext = extensions[i] + for i, ext in enumerate(extensions): if isinstance(ext, Extension): continue # OK! (assume type-checking done # by Extension constructor) - (ext_name, build_info) = ext - log.warn(("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s'" - "-- please convert to Extension instance" % ext_name)) - if type(ext) is not TupleType and len(ext) != 2: + if not isinstance(ext, tuple) or len(ext) != 2: raise DistutilsSetupError, \ ("each element of 'ext_modules' option must be an " "Extension instance or 2-tuple") - if not (type(ext_name) is StringType and + ext_name, build_info = ext + + log.warn(("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s'" + "-- please convert to Extension instance" % ext_name)) + + if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)): raise DistutilsSetupError, \ ("first element of each tuple in 'ext_modules' " "must be the extension name (a string)") - if type(build_info) is not DictionaryType: + if not isinstance(build_info, dict): raise DistutilsSetupError, \ ("second element of each tuple in 'ext_modules' " "must be a dictionary (build info)") @@ -401,11 +394,8 @@ class build_ext (Command): # Easy stuff: one-to-one mapping from dict elements to # instance attributes. - for key in ('include_dirs', - 'library_dirs', - 'libraries', - 'extra_objects', - 'extra_compile_args', + for key in ('include_dirs', 'library_dirs', 'libraries', + 'extra_objects', 'extra_compile_args', 'extra_link_args'): val = build_info.get(key) if val is not None: @@ -424,8 +414,7 @@ class build_ext (Command): ext.define_macros = [] ext.undef_macros = [] for macro in macros: - if not (type(macro) is TupleType and - 1 <= len(macro) <= 2): + if not (isinstance(macro, tuple) and len(macro) in (1, 2)): raise DistutilsSetupError, \ ("'macros' element of build info dict " "must be 1- or 2-tuple") @@ -436,12 +425,7 @@ class build_ext (Command): extensions[i] = ext - # for extensions - - # check_extensions_list () - - - def get_source_files (self): + def get_source_files(self): self.check_extensions_list(self.extensions) filenames = [] @@ -451,9 +435,7 @@ class build_ext (Command): return filenames - - def get_outputs (self): - + def get_outputs(self): # Sanity check the 'extensions' list -- can't assume this is being # done in the same run as a 'build_extensions()' call (in fact, we # can probably assume that it *isn't*!). @@ -469,8 +451,6 @@ class build_ext (Command): self.get_ext_filename(fullname))) return outputs - # get_outputs () - def build_extensions(self): # First, sanity-check the 'extensions' list self.check_extensions_list(self.extensions) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 141c2869..dab9712a 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -9,8 +9,8 @@ from distutils.command.build_ext import build_ext from distutils import sysconfig from distutils.tests import support from distutils.extension import Extension -from distutils.errors import UnknownFileError -from distutils.errors import CompileError +from distutils.errors import (UnknownFileError, DistutilsSetupError, + CompileError) import unittest from test import test_support @@ -165,6 +165,130 @@ class BuildExtTestCase(support.TempdirManager, cmd.ensure_finalized() cmd.run() # should pass + def test_finalize_options(self): + # Make sure Python's include directories (for Python.h, pyconfig.h, + # etc.) are in the include search path. + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.finalize_options() + + from distutils import sysconfig + py_include = sysconfig.get_python_inc() + self.assert_(py_include in cmd.include_dirs) + + plat_py_include = sysconfig.get_python_inc(plat_specific=1) + self.assert_(plat_py_include in cmd.include_dirs) + + # make sure cmd.libraries is turned into a list + # if it's a string + cmd = build_ext(dist) + cmd.libraries = 'my_lib' + cmd.finalize_options() + self.assertEquals(cmd.libraries, ['my_lib']) + + # make sure cmd.library_dirs is turned into a list + # if it's a string + cmd = build_ext(dist) + cmd.library_dirs = 'my_lib_dir' + cmd.finalize_options() + self.assertEquals(cmd.library_dirs, ['my_lib_dir']) + + # make sure rpath is turned into a list + # if it's a list of os.pathsep's paths + cmd = build_ext(dist) + cmd.rpath = os.pathsep.join(['one', 'two']) + cmd.finalize_options() + self.assertEquals(cmd.rpath, ['one', 'two']) + + # XXX more tests to perform for win32 + + # make sure define is turned into 2-tuples + # strings if they are ','-separated strings + cmd = build_ext(dist) + cmd.define = 'one,two' + cmd.finalize_options() + self.assertEquals(cmd.define, [('one', '1'), ('two', '1')]) + + # make sure undef is turned into a list of + # strings if they are ','-separated strings + cmd = build_ext(dist) + cmd.undef = 'one,two' + cmd.finalize_options() + self.assertEquals(cmd.undef, ['one', 'two']) + + # make sure swig_opts is turned into a list + cmd = build_ext(dist) + cmd.swig_opts = None + cmd.finalize_options() + self.assertEquals(cmd.swig_opts, []) + + cmd = build_ext(dist) + cmd.swig_opts = '1 2' + cmd.finalize_options() + self.assertEquals(cmd.swig_opts, ['1', '2']) + + def test_check_extensions_list(self): + dist = Distribution() + cmd = build_ext(dist) + cmd.finalize_options() + + #'extensions' option must be a list of Extension instances + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo') + + # each element of 'ext_modules' option must be an + # Extension instance or 2-tuple + exts = [('bar', 'foo', 'bar'), 'foo'] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # first element of each tuple in 'ext_modules' + # must be the extension name (a string) and match + # a python dotted-separated name + exts = [('foo-bar', '')] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # second element of each tuple in 'ext_modules' + # must be a ary (build info) + exts = [('foo.bar', '')] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # ok this one should pass + exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', + 'some': 'bar'})] + cmd.check_extensions_list(exts) + ext = exts[0] + self.assert_(isinstance(ext, Extension)) + + # check_extensions_list adds in ext the values passed + # when they are in ('include_dirs', 'library_dirs', 'libraries' + # 'extra_objects', 'extra_compile_args', 'extra_link_args') + self.assertEquals(ext.libraries, 'foo') + self.assert_(not hasattr(ext, 'some')) + + # 'macros' element of build info dict must be 1- or 2-tuple + exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', + 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + exts[0][1]['macros'] = [('1', '2'), ('3',)] + cmd.check_extensions_list(exts) + self.assertEquals(exts[0].undef_macros, ['3']) + self.assertEquals(exts[0].define_macros, [('1', '2')]) + + def test_get_source_files(self): + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.ensure_finalized() + self.assertEquals(cmd.get_source_files(), ['xxx']) + + def test_get_outputs(self): + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.ensure_finalized() + self.assertEquals(len(cmd.get_outputs()), 1) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From dc71622b4ce6db30451eb3393ad04c13623076dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 10:24:16 +0000 Subject: Merged revisions 72531 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72531 | tarek.ziade | 2009-05-10 12:12:08 +0200 (Sun, 10 May 2009) | 1 line fixed #5984 and improved test coverage ........ --- command/build_ext.py | 70 +++++++++---------------- tests/test_build_ext.py | 132 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 46 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 936ea8d0..c03951cb 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -128,7 +128,7 @@ class build_ext (Command): self.swig_opts = None self.user = None - def finalize_options (self): + def finalize_options(self): from distutils import sysconfig self.set_undefined_options('build', @@ -145,15 +145,14 @@ class build_ext (Command): self.extensions = self.distribution.ext_modules - # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. py_include = sysconfig.get_python_inc() plat_py_include = sysconfig.get_python_inc(plat_specific=1) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] - if type(self.include_dirs) is StringType: - self.include_dirs = string.split(self.include_dirs, os.pathsep) + if isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. @@ -161,7 +160,7 @@ class build_ext (Command): if plat_py_include != py_include: self.include_dirs.append(plat_py_include) - if type(self.libraries) is StringType: + if isinstance(self.libraries, str): self.libraries = [self.libraries] # Life is easier if we're not forever checking for None, so @@ -252,14 +251,14 @@ class build_ext (Command): # symbols can be separated with commas. if self.define: - defines = string.split(self.define, ',') + defines = self.define.split(',') self.define = map(lambda symbol: (symbol, '1'), defines) # The option for macros to undefine is also a string from the # option parsing, but has to be a list. Multiple symbols can also # be separated with commas here. if self.undef: - self.undef = string.split(self.undef, ',') + self.undef = self.undef.split(',') if self.swig_opts is None: self.swig_opts = [] @@ -276,11 +275,7 @@ class build_ext (Command): self.library_dirs.append(user_lib) self.rpath.append(user_lib) - # finalize_options () - - - def run (self): - + def run(self): from distutils.ccompiler import new_compiler # 'self.extensions', as supplied by setup.py, is a list of @@ -327,7 +322,7 @@ class build_ext (Command): self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples - for (name,value) in self.define: + for (name, value) in self.define: self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: @@ -344,10 +339,7 @@ class build_ext (Command): # Now actually compile and link everything. self.build_extensions() - # run () - - - def check_extensions_list (self, extensions): + def check_extensions_list(self, extensions): """Ensure that the list of extensions (presumably provided as a command option 'extensions') is valid, i.e. it is a list of Extension objects. We also support the old-style list of 2-tuples, @@ -357,32 +349,33 @@ class build_ext (Command): Raise DistutilsSetupError if the structure is invalid anywhere; just returns otherwise. """ - if type(extensions) is not ListType: + if not isinstance(extensions, list): raise DistutilsSetupError, \ "'ext_modules' option must be a list of Extension instances" - for i in range(len(extensions)): - ext = extensions[i] + for i, ext in enumerate(extensions): if isinstance(ext, Extension): continue # OK! (assume type-checking done # by Extension constructor) - (ext_name, build_info) = ext - log.warn(("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s'" - "-- please convert to Extension instance" % ext_name)) - if type(ext) is not TupleType and len(ext) != 2: + if not isinstance(ext, tuple) or len(ext) != 2: raise DistutilsSetupError, \ ("each element of 'ext_modules' option must be an " "Extension instance or 2-tuple") - if not (type(ext_name) is StringType and + ext_name, build_info = ext + + log.warn(("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s'" + "-- please convert to Extension instance" % ext_name)) + + if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)): raise DistutilsSetupError, \ ("first element of each tuple in 'ext_modules' " "must be the extension name (a string)") - if type(build_info) is not DictionaryType: + if not isinstance(build_info, dict): raise DistutilsSetupError, \ ("second element of each tuple in 'ext_modules' " "must be a dictionary (build info)") @@ -393,11 +386,8 @@ class build_ext (Command): # Easy stuff: one-to-one mapping from dict elements to # instance attributes. - for key in ('include_dirs', - 'library_dirs', - 'libraries', - 'extra_objects', - 'extra_compile_args', + for key in ('include_dirs', 'library_dirs', 'libraries', + 'extra_objects', 'extra_compile_args', 'extra_link_args'): val = build_info.get(key) if val is not None: @@ -416,8 +406,7 @@ class build_ext (Command): ext.define_macros = [] ext.undef_macros = [] for macro in macros: - if not (type(macro) is TupleType and - 1 <= len(macro) <= 2): + if not (isinstance(macro, tuple) and len(macro) in (1, 2)): raise DistutilsSetupError, \ ("'macros' element of build info dict " "must be 1- or 2-tuple") @@ -428,12 +417,7 @@ class build_ext (Command): extensions[i] = ext - # for extensions - - # check_extensions_list () - - - def get_source_files (self): + def get_source_files(self): self.check_extensions_list(self.extensions) filenames = [] @@ -443,9 +427,7 @@ class build_ext (Command): return filenames - - def get_outputs (self): - + def get_outputs(self): # Sanity check the 'extensions' list -- can't assume this is being # done in the same run as a 'build_extensions()' call (in fact, we # can probably assume that it *isn't*!). @@ -461,8 +443,6 @@ class build_ext (Command): self.get_ext_filename(fullname))) return outputs - # get_outputs () - def build_extensions(self): # First, sanity-check the 'extensions' list self.check_extensions_list(self.extensions) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index dbec65e8..98ae8cf3 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -7,6 +7,8 @@ from StringIO import StringIO from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig +from distutils.tests import support +from distutils.errors import DistutilsSetupError import unittest from test import test_support @@ -15,10 +17,13 @@ from test import test_support # Don't load the xx module more than once. ALREADY_TESTED = False -class BuildExtTestCase(unittest.TestCase): +class BuildExtTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path + super(BuildExtTestCase, self).setUp() self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) @@ -74,6 +79,7 @@ class BuildExtTestCase(unittest.TestCase): sys.path = self.sys_path # XXX on Windows the test leaves a directory with xx module in TEMP shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') + super(BuildExtTestCase, self).tearDown() def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) @@ -96,6 +102,130 @@ class BuildExtTestCase(unittest.TestCase): # make sur we get some lobrary dirs under solaris self.assert_(len(cmd.library_dirs) > 0) + def test_finalize_options(self): + # Make sure Python's include directories (for Python.h, pyconfig.h, + # etc.) are in the include search path. + modules = [Extension('foo', ['xxx'])] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.finalize_options() + + from distutils import sysconfig + py_include = sysconfig.get_python_inc() + self.assert_(py_include in cmd.include_dirs) + + plat_py_include = sysconfig.get_python_inc(plat_specific=1) + self.assert_(plat_py_include in cmd.include_dirs) + + # make sure cmd.libraries is turned into a list + # if it's a string + cmd = build_ext(dist) + cmd.libraries = 'my_lib' + cmd.finalize_options() + self.assertEquals(cmd.libraries, ['my_lib']) + + # make sure cmd.library_dirs is turned into a list + # if it's a string + cmd = build_ext(dist) + cmd.library_dirs = 'my_lib_dir' + cmd.finalize_options() + self.assertEquals(cmd.library_dirs, ['my_lib_dir']) + + # make sure rpath is turned into a list + # if it's a list of os.pathsep's paths + cmd = build_ext(dist) + cmd.rpath = os.pathsep.join(['one', 'two']) + cmd.finalize_options() + self.assertEquals(cmd.rpath, ['one', 'two']) + + # XXX more tests to perform for win32 + + # make sure define is turned into 2-tuples + # strings if they are ','-separated strings + cmd = build_ext(dist) + cmd.define = 'one,two' + cmd.finalize_options() + self.assertEquals(cmd.define, [('one', '1'), ('two', '1')]) + + # make sure undef is turned into a list of + # strings if they are ','-separated strings + cmd = build_ext(dist) + cmd.undef = 'one,two' + cmd.finalize_options() + self.assertEquals(cmd.undef, ['one', 'two']) + + # make sure swig_opts is turned into a list + cmd = build_ext(dist) + cmd.swig_opts = None + cmd.finalize_options() + self.assertEquals(cmd.swig_opts, []) + + cmd = build_ext(dist) + cmd.swig_opts = '1 2' + cmd.finalize_options() + self.assertEquals(cmd.swig_opts, ['1', '2']) + + def test_check_extensions_list(self): + dist = Distribution() + cmd = build_ext(dist) + cmd.finalize_options() + + #'extensions' option must be a list of Extension instances + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo') + + # each element of 'ext_modules' option must be an + # Extension instance or 2-tuple + exts = [('bar', 'foo', 'bar'), 'foo'] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # first element of each tuple in 'ext_modules' + # must be the extension name (a string) and match + # a python dotted-separated name + exts = [('foo-bar', '')] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # second element of each tuple in 'ext_modules' + # must be a ary (build info) + exts = [('foo.bar', '')] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # ok this one should pass + exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', + 'some': 'bar'})] + cmd.check_extensions_list(exts) + ext = exts[0] + self.assert_(isinstance(ext, Extension)) + + # check_extensions_list adds in ext the values passed + # when they are in ('include_dirs', 'library_dirs', 'libraries' + # 'extra_objects', 'extra_compile_args', 'extra_link_args') + self.assertEquals(ext.libraries, 'foo') + self.assert_(not hasattr(ext, 'some')) + + # 'macros' element of build info dict must be 1- or 2-tuple + exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', + 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + exts[0][1]['macros'] = [('1', '2'), ('3',)] + cmd.check_extensions_list(exts) + self.assertEquals(exts[0].undef_macros, ['3']) + self.assertEquals(exts[0].define_macros, [('1', '2')]) + + def test_get_source_files(self): + modules = [Extension('foo', ['xxx'])] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.ensure_finalized() + self.assertEquals(cmd.get_source_files(), ['xxx']) + + def test_get_outputs(self): + modules = [Extension('foo', ['xxx'])] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.ensure_finalized() + self.assertEquals(len(cmd.get_outputs()), 1) + def test_suite(): if not sysconfig.python_build: if test_support.verbose: -- cgit v1.2.1 From 7edd653b77f93b3d5fabdf7f419d90b86d1db93b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 10:34:01 +0000 Subject: Merged revisions 72531 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72531 | tarek.ziade | 2009-05-10 12:12:08 +0200 (Sun, 10 May 2009) | 1 line fixed #5984 and improved test coverage ........ --- command/build_ext.py | 23 +++++---- tests/test_build_ext.py | 128 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 137 insertions(+), 14 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index ade95be2..d3668e9f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -329,7 +329,7 @@ class build_ext(Command): self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples - for (name,value) in self.define: + for (name, value) in self.define: self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: @@ -365,22 +365,24 @@ class build_ext(Command): continue # OK! (assume type-checking done # by Extension constructor) - (ext_name, build_info) = ext - log.warn("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s'" - "-- please convert to Extension instance" % ext_name) - if not isinstance(ext, tuple) and len(ext) != 2: + if not isinstance(ext, tuple) or len(ext) != 2: raise DistutilsSetupError( "each element of 'ext_modules' option must be an " "Extension instance or 2-tuple") + ext_name, build_info = ext + + log.warn(("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s'" + "-- please convert to Extension instance" % ext_name)) + if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)): raise DistutilsSetupError( "first element of each tuple in 'ext_modules' " "must be the extension name (a string)") - if not instance(build_info, DictionaryType): + if not isinstance(build_info, dict): raise DistutilsSetupError( "second element of each tuple in 'ext_modules' " "must be a dictionary (build info)") @@ -391,11 +393,8 @@ class build_ext(Command): # Easy stuff: one-to-one mapping from dict elements to # instance attributes. - for key in ('include_dirs', - 'library_dirs', - 'libraries', - 'extra_objects', - 'extra_compile_args', + for key in ('include_dirs', 'library_dirs', 'libraries', + 'extra_objects', 'extra_compile_args', 'extra_link_args'): val = build_info.get(key) if val is not None: diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index e4a02391..c22be2ac 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -10,8 +10,8 @@ from distutils import sysconfig from distutils.tests.support import TempdirManager from distutils.tests.support import LoggingSilencer from distutils.extension import Extension -from distutils.errors import UnknownFileError -from distutils.errors import CompileError +from distutils.errors import (UnknownFileError, DistutilsSetupError, + CompileError) import unittest from test import support @@ -165,6 +165,130 @@ class BuildExtTestCase(TempdirManager, cmd.ensure_finalized() cmd.run() # should pass + def test_finalize_options(self): + # Make sure Python's include directories (for Python.h, pyconfig.h, + # etc.) are in the include search path. + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.finalize_options() + + from distutils import sysconfig + py_include = sysconfig.get_python_inc() + self.assert_(py_include in cmd.include_dirs) + + plat_py_include = sysconfig.get_python_inc(plat_specific=1) + self.assert_(plat_py_include in cmd.include_dirs) + + # make sure cmd.libraries is turned into a list + # if it's a string + cmd = build_ext(dist) + cmd.libraries = 'my_lib' + cmd.finalize_options() + self.assertEquals(cmd.libraries, ['my_lib']) + + # make sure cmd.library_dirs is turned into a list + # if it's a string + cmd = build_ext(dist) + cmd.library_dirs = 'my_lib_dir' + cmd.finalize_options() + self.assertEquals(cmd.library_dirs, ['my_lib_dir']) + + # make sure rpath is turned into a list + # if it's a list of os.pathsep's paths + cmd = build_ext(dist) + cmd.rpath = os.pathsep.join(['one', 'two']) + cmd.finalize_options() + self.assertEquals(cmd.rpath, ['one', 'two']) + + # XXX more tests to perform for win32 + + # make sure define is turned into 2-tuples + # strings if they are ','-separated strings + cmd = build_ext(dist) + cmd.define = 'one,two' + cmd.finalize_options() + self.assertEquals(cmd.define, [('one', '1'), ('two', '1')]) + + # make sure undef is turned into a list of + # strings if they are ','-separated strings + cmd = build_ext(dist) + cmd.undef = 'one,two' + cmd.finalize_options() + self.assertEquals(cmd.undef, ['one', 'two']) + + # make sure swig_opts is turned into a list + cmd = build_ext(dist) + cmd.swig_opts = None + cmd.finalize_options() + self.assertEquals(cmd.swig_opts, []) + + cmd = build_ext(dist) + cmd.swig_opts = '1 2' + cmd.finalize_options() + self.assertEquals(cmd.swig_opts, ['1', '2']) + + def test_check_extensions_list(self): + dist = Distribution() + cmd = build_ext(dist) + cmd.finalize_options() + + #'extensions' option must be a list of Extension instances + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo') + + # each element of 'ext_modules' option must be an + # Extension instance or 2-tuple + exts = [('bar', 'foo', 'bar'), 'foo'] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # first element of each tuple in 'ext_modules' + # must be the extension name (a string) and match + # a python dotted-separated name + exts = [('foo-bar', '')] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # second element of each tuple in 'ext_modules' + # must be a ary (build info) + exts = [('foo.bar', '')] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # ok this one should pass + exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', + 'some': 'bar'})] + cmd.check_extensions_list(exts) + ext = exts[0] + self.assert_(isinstance(ext, Extension)) + + # check_extensions_list adds in ext the values passed + # when they are in ('include_dirs', 'library_dirs', 'libraries' + # 'extra_objects', 'extra_compile_args', 'extra_link_args') + self.assertEquals(ext.libraries, 'foo') + self.assert_(not hasattr(ext, 'some')) + + # 'macros' element of build info dict must be 1- or 2-tuple + exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', + 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + exts[0][1]['macros'] = [('1', '2'), ('3',)] + cmd.check_extensions_list(exts) + self.assertEquals(exts[0].undef_macros, ['3']) + self.assertEquals(exts[0].define_macros, [('1', '2')]) + + def test_get_source_files(self): + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.ensure_finalized() + self.assertEquals(cmd.get_source_files(), ['xxx']) + + def test_get_outputs(self): + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = build_ext(dist) + cmd.ensure_finalized() + self.assertEquals(len(cmd.get_outputs()), 1) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From a7de6ba0a764ace7b5430be8a3d709204850df39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 11:42:46 +0000 Subject: Added tests form install_lib and pep8-fied the module --- command/install_lib.py | 40 ++++++++-------------- tests/test_install_lib.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 tests/test_install_lib.py diff --git a/command/install_lib.py b/command/install_lib.py index 4c62c713..87e3c7aa 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,7 +6,6 @@ Implements the Distutils 'install_lib' command __revision__ = "$Id$" import os -from types import IntType from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -17,7 +16,7 @@ if hasattr(os, 'extsep'): else: PYTHON_SOURCE_EXTENSION = ".py" -class install_lib (Command): +class install_lib(Command): description = "install all Python modules (extensions and pure Python)" @@ -51,8 +50,7 @@ class install_lib (Command): boolean_options = ['force', 'compile', 'skip-build'] negative_opt = {'no-compile' : 'compile'} - - def initialize_options (self): + def initialize_options(self): # let the 'install' command dictate our installation directory self.install_dir = None self.build_dir = None @@ -61,8 +59,7 @@ class install_lib (Command): self.optimize = None self.skip_build = None - def finalize_options (self): - + def finalize_options(self): # Get all the information we need to install pure Python modules # from the umbrella 'install' command -- build (source) directory, # install (target) directory, and whether to compile .py files. @@ -80,15 +77,14 @@ class install_lib (Command): if self.optimize is None: self.optimize = 0 - if type(self.optimize) is not IntType: + if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) - assert 0 <= self.optimize <= 2 + assert self.optimize in (0, 1, 2) except (ValueError, AssertionError): raise DistutilsOptionError, "optimize must be 0, 1, or 2" - def run (self): - + def run(self): # Make sure we have built everything we need first self.build() @@ -101,20 +97,17 @@ class install_lib (Command): if outfiles is not None and self.distribution.has_pure_modules(): self.byte_compile(outfiles) - # run () - - # -- Top-level worker functions ------------------------------------ # (called from 'run()') - def build (self): + def build(self): if not self.skip_build: if self.distribution.has_pure_modules(): self.run_command('build_py') if self.distribution.has_ext_modules(): self.run_command('build_ext') - def install (self): + def install(self): if os.path.isdir(self.build_dir): outfiles = self.copy_tree(self.build_dir, self.install_dir) else: @@ -123,7 +116,7 @@ class install_lib (Command): return return outfiles - def byte_compile (self, files): + def byte_compile(self, files): from distutils.util import byte_compile # Get the "--root" directory supplied to the "install" command, @@ -144,8 +137,7 @@ class install_lib (Command): # -- Utility methods ----------------------------------------------- - def _mutate_outputs (self, has_any, build_cmd, cmd_option, output_dir): - + def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir): if not has_any: return [] @@ -160,9 +152,7 @@ class install_lib (Command): return outputs - # _mutate_outputs () - - def _bytecode_filenames (self, py_filenames): + def _bytecode_filenames(self, py_filenames): bytecode_files = [] for py_file in py_filenames: # Since build_py handles package data installation, the @@ -182,7 +172,7 @@ class install_lib (Command): # -- External interface -------------------------------------------- # (called by outsiders) - def get_outputs (self): + def get_outputs(self): """Return the list of files that would be installed if this command were actually run. Not affected by the "dry-run" flag or whether modules have actually been built yet. @@ -203,9 +193,7 @@ class install_lib (Command): return pure_outputs + bytecode_outputs + ext_outputs - # get_outputs () - - def get_inputs (self): + def get_inputs(self): """Get the list of files that are input to this command, ie. the files that get installed as they are named in the build tree. The files in this list correspond one-to-one to the output @@ -222,5 +210,3 @@ class install_lib (Command): inputs.extend(build_ext.get_outputs()) return inputs - -# class install_lib diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py new file mode 100644 index 00000000..69a24faf --- /dev/null +++ b/tests/test_install_lib.py @@ -0,0 +1,84 @@ +"""Tests for distutils.command.install_data.""" +import sys +import os +import unittest + +from distutils.command.install_lib import install_lib +from distutils.extension import Extension +from distutils.tests import support +from distutils.errors import DistutilsOptionError + +class InstallLibTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + + cmd.finalize_options() + self.assertEquals(cmd.compile, 1) + self.assertEquals(cmd.optimize, 0) + + # optimize must be 0, 1, or 2 + cmd.optimize = 'foo' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + cmd.optimize = '4' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + cmd.optimize = '2' + cmd.finalize_options() + self.assertEquals(cmd.optimize, 2) + + def test_byte_compile(self): + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + cmd.compile = cmd.optimize = 1 + + f = os.path.join(pkg_dir, 'foo.py') + self.write_file(f, '# python file') + cmd.byte_compile([f]) + self.assert_(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) + self.assert_(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) + + def test_get_outputs(self): + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + + # setting up a dist environment + cmd.compile = cmd.optimize = 1 + cmd.install_dir = pkg_dir + f = os.path.join(pkg_dir, 'foo.py') + self.write_file(f, '# python file') + cmd.distribution.py_modules = [pkg_dir] + cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] + cmd.distribution.packages = [pkg_dir] + cmd.distribution.script_name = 'setup.py' + + # get_output should return 4 elements + self.assertEquals(len(cmd.get_outputs()), 4) + + def test_get_inputs(self): + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + + # setting up a dist environment + cmd.compile = cmd.optimize = 1 + cmd.install_dir = pkg_dir + f = os.path.join(pkg_dir, 'foo.py') + self.write_file(f, '# python file') + cmd.distribution.py_modules = [pkg_dir] + cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] + cmd.distribution.packages = [pkg_dir] + cmd.distribution.script_name = 'setup.py' + + # get_input should return 2 elements + self.assertEquals(len(cmd.get_inputs()), 2) + + +def test_suite(): + return unittest.makeSuite(InstallLibTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From df1988a14be1c71891e64a2705f71691d1a8dfe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 11:45:41 +0000 Subject: Merged revisions 72535 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72535 | tarek.ziade | 2009-05-10 13:42:46 +0200 (Sun, 10 May 2009) | 1 line Added tests form install_lib and pep8-fied the module ........ --- command/install_lib.py | 6 ++-- tests/test_install_lib.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 tests/test_install_lib.py diff --git a/command/install_lib.py b/command/install_lib.py index 94895f42..022efa48 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -5,7 +5,7 @@ Implements the Distutils 'install_lib' command __revision__ = "$Id$" -import sys, os +import os from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -57,7 +57,6 @@ class install_lib(Command): self.skip_build = None def finalize_options(self): - # Get all the information we need to install pure Python modules # from the umbrella 'install' command -- build (source) directory, # install (target) directory, and whether to compile .py files. @@ -78,7 +77,7 @@ class install_lib(Command): if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) - assert 0 <= self.optimize <= 2 + assert self.optimize in (0, 1, 2) except (ValueError, AssertionError): raise DistutilsOptionError("optimize must be 0, 1, or 2") @@ -95,7 +94,6 @@ class install_lib(Command): if outfiles is not None and self.distribution.has_pure_modules(): self.byte_compile(outfiles) - # -- Top-level worker functions ------------------------------------ # (called from 'run()') diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py new file mode 100644 index 00000000..69a24faf --- /dev/null +++ b/tests/test_install_lib.py @@ -0,0 +1,84 @@ +"""Tests for distutils.command.install_data.""" +import sys +import os +import unittest + +from distutils.command.install_lib import install_lib +from distutils.extension import Extension +from distutils.tests import support +from distutils.errors import DistutilsOptionError + +class InstallLibTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + + cmd.finalize_options() + self.assertEquals(cmd.compile, 1) + self.assertEquals(cmd.optimize, 0) + + # optimize must be 0, 1, or 2 + cmd.optimize = 'foo' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + cmd.optimize = '4' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + cmd.optimize = '2' + cmd.finalize_options() + self.assertEquals(cmd.optimize, 2) + + def test_byte_compile(self): + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + cmd.compile = cmd.optimize = 1 + + f = os.path.join(pkg_dir, 'foo.py') + self.write_file(f, '# python file') + cmd.byte_compile([f]) + self.assert_(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) + self.assert_(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) + + def test_get_outputs(self): + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + + # setting up a dist environment + cmd.compile = cmd.optimize = 1 + cmd.install_dir = pkg_dir + f = os.path.join(pkg_dir, 'foo.py') + self.write_file(f, '# python file') + cmd.distribution.py_modules = [pkg_dir] + cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] + cmd.distribution.packages = [pkg_dir] + cmd.distribution.script_name = 'setup.py' + + # get_output should return 4 elements + self.assertEquals(len(cmd.get_outputs()), 4) + + def test_get_inputs(self): + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + + # setting up a dist environment + cmd.compile = cmd.optimize = 1 + cmd.install_dir = pkg_dir + f = os.path.join(pkg_dir, 'foo.py') + self.write_file(f, '# python file') + cmd.distribution.py_modules = [pkg_dir] + cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] + cmd.distribution.packages = [pkg_dir] + cmd.distribution.script_name = 'setup.py' + + # get_input should return 2 elements + self.assertEquals(len(cmd.get_inputs()), 2) + + +def test_suite(): + return unittest.makeSuite(InstallLibTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From a440b1ce0ae7a9e9ca14015c4d69b8e08b35c9c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 11:59:30 +0000 Subject: refactored test_sysconfig so it uses test.test_support.EnvironmentVarGuard --- tests/support.py | 11 +++++++++++ tests/test_sysconfig.py | 27 ++++++++------------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/support.py b/tests/support.py index 578cf40c..668edf9f 100644 --- a/tests/support.py +++ b/tests/support.py @@ -5,6 +5,7 @@ import tempfile from distutils import log from distutils.core import Distribution +from test.test_support import EnvironmentVarGuard class LoggingSilencer(object): @@ -82,3 +83,13 @@ class DummyCommand: def ensure_finalized(self): pass + +class EnvironGuard(object): + + def setUp(self): + super(EnvironGuard, self).setUp() + self.environ = EnvironmentVarGuard() + + def tearDown(self): + self.environ.__exit__() + super(EnvironGuard, self).tearDown() diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index bf0043aa..0a5ac294 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,25 +1,14 @@ -"""Tests for distutils.dist.""" - -from distutils import sysconfig -from distutils.ccompiler import get_default_compiler - +"""Tests for distutils.sysconfig.""" import os import unittest +from distutils import sysconfig +from distutils.ccompiler import get_default_compiler +from distutils.tests import support from test.test_support import TESTFN -class SysconfigTestCase(unittest.TestCase): - - def setUp(self): - self.old_flags = [('AR', os.environ.get('AR')), - ('ARFLAGS', os.environ.get('ARFLAGS'))] - - def tearDown(self): - for name, value in self.old_flags: - if value is not None: - os.environ[name] = value - elif name in os.environ: - del os.environ[name] +class SysconfigTestCase(support.EnvironGuard, + unittest.TestCase): def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() @@ -53,8 +42,8 @@ class SysconfigTestCase(unittest.TestCase): if get_default_compiler() != 'unix': return - os.environ['AR'] = 'my_ar' - os.environ['ARFLAGS'] = '-arflags' + self.environ['AR'] = 'my_ar' + self.environ['ARFLAGS'] = '-arflags' # make sure AR gets caught class compiler: -- cgit v1.2.1 From a1969cc045a19e41674d2c929e6f802c02b2e8ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 12:02:35 +0000 Subject: Merged revisions 72539 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72539 | tarek.ziade | 2009-05-10 13:59:30 +0200 (Sun, 10 May 2009) | 1 line refactored test_sysconfig so it uses test.test_support.EnvironmentVarGuard ........ --- tests/support.py | 11 +++++++++++ tests/test_sysconfig.py | 27 ++++++++------------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/support.py b/tests/support.py index ab2af9a6..cdcbc378 100644 --- a/tests/support.py +++ b/tests/support.py @@ -5,6 +5,7 @@ import tempfile from distutils import log from distutils.core import Distribution +from test.support import EnvironmentVarGuard class LoggingSilencer(object): @@ -82,3 +83,13 @@ class DummyCommand: def ensure_finalized(self): pass + +class EnvironGuard(object): + + def setUp(self): + super(EnvironGuard, self).setUp() + self.environ = EnvironmentVarGuard() + + def tearDown(self): + self.environ.__exit__() + super(EnvironGuard, self).tearDown() diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index f65bc725..322df39c 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,25 +1,14 @@ -"""Tests for distutils.dist.""" - -from distutils import sysconfig -from distutils.ccompiler import get_default_compiler - +"""Tests for distutils.sysconfig.""" import os import unittest +from distutils import sysconfig +from distutils.ccompiler import get_default_compiler +from distutils.tests import support from test.support import TESTFN -class SysconfigTestCase(unittest.TestCase): - - def setUp(self): - self.old_flags = [('AR', os.environ.get('AR')), - ('ARFLAGS', os.environ.get('ARFLAGS'))] - - def tearDown(self): - for name, value in self.old_flags: - if value is not None: - os.environ[name] = value - elif name in os.environ: - del os.environ[name] +class SysconfigTestCase(support.EnvironGuard, + unittest.TestCase): def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() @@ -53,8 +42,8 @@ class SysconfigTestCase(unittest.TestCase): if get_default_compiler() != 'unix': return - os.environ['AR'] = 'my_ar' - os.environ['ARFLAGS'] = '-arflags' + self.environ['AR'] = 'my_ar' + self.environ['ARFLAGS'] = '-arflags' # make sure AR gets caught class compiler: -- cgit v1.2.1 From 5bc0f864c12dd6129366d7cc44ed23d942a2818f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 12:17:30 +0000 Subject: now using EnvironGuard everywhere --- tests/test_config.py | 29 ++++------------------------- tests/test_dist.py | 22 ++++++---------------- tests/test_util.py | 45 +++++++++++++++------------------------------ 3 files changed, 25 insertions(+), 71 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 7737538b..fd778d1b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -49,18 +49,14 @@ password:xxx class PyPIRCCommandTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def setUp(self): """Patches the environment.""" super(PyPIRCCommandTestCase, self).setUp() - - if os.environ.has_key('HOME'): - self._old_home = os.environ['HOME'] - else: - self._old_home = None self.tmp_dir = self.mkdtemp() - os.environ['HOME'] = self.tmp_dir + self.environ['HOME'] = self.tmp_dir self.rc = os.path.join(self.tmp_dir, '.pypirc') self.dist = Distribution() @@ -76,10 +72,6 @@ class PyPIRCCommandTestCase(support.TempdirManager, def tearDown(self): """Removes the patch.""" - if self._old_home is None: - del os.environ['HOME'] - else: - os.environ['HOME'] = self._old_home set_threshold(self.old_threshold) super(PyPIRCCommandTestCase, self).tearDown() @@ -89,12 +81,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, # 2. handle the old format # new format - f = open(self.rc, 'w') - try: - f.write(PYPIRC) - finally: - f.close() - + self.write_file(self.rc, PYPIRC) cmd = self._cmd(self.dist) config = cmd._read_pypirc() @@ -106,10 +93,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, self.assertEquals(config, waited) # old format - f = open(self.rc, 'w') - f.write(PYPIRC_OLD) - f.close() - + self.write_file(self.rc, PYPIRC_OLD) config = cmd._read_pypirc() config = config.items() config.sort() @@ -119,19 +103,14 @@ class PyPIRCCommandTestCase(support.TempdirManager, self.assertEquals(config, waited) def test_server_empty_registration(self): - cmd = self._cmd(self.dist) rc = cmd._get_rc_file() self.assert_(not os.path.exists(rc)) - cmd._store_pypirc('tarek', 'xxx') - self.assert_(os.path.exists(rc)) content = open(rc).read() - self.assertEquals(content, WANTED) - def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) diff --git a/tests/test_dist.py b/tests/test_dist.py index 847df7bb..3304790b 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -39,13 +39,13 @@ class TestDistribution(distutils.dist.Distribution): class DistributionTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): - support.TempdirManager.setUp(self) + super(DistributionTestCase, self).setUp() self.argv = sys.argv[:] del sys.argv[1:] def tearDown(self): sys.argv[:] = self.argv - support.TempdirManager.tearDown(self) + super(DistributionTestCase, self).tearDown() def create_distribution(self, configfiles=()): d = TestDistribution() @@ -151,7 +151,8 @@ class DistributionTestCase(support.TempdirManager, unittest.TestCase): self.assertEquals(len(warns), 0) -class MetadataTestCase(support.TempdirManager, unittest.TestCase): +class MetadataTestCase(support.TempdirManager, support.EnvironGuard, + unittest.TestCase): def test_simple_metadata(self): attrs = {"name": "package", @@ -238,13 +239,6 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase): def test_custom_pydistutils(self): # fixes #2166 # make sure pydistutils.cfg is found - old = {} - for env in ('HOME', 'HOMEPATH', 'HOMEDRIVE'): - value = os.environ.get(env) - old[env] = value - if value is not None: - del os.environ[env] - if os.name == 'posix': user_filename = ".pydistutils.cfg" else: @@ -261,22 +255,18 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase): # linux-style if sys.platform in ('linux', 'darwin'): - os.environ['HOME'] = temp_dir + self.environ['HOME'] = temp_dir files = dist.find_config_files() self.assert_(user_filename in files) # win32-style if sys.platform == 'win32': # home drive should be found - os.environ['HOME'] = temp_dir + self.environ['HOME'] = temp_dir files = dist.find_config_files() self.assert_(user_filename in files, '%r not found in %r' % (user_filename, files)) finally: - for key, value in old.items(): - if value is None: - continue - os.environ[key] = value os.remove(user_filename) def test_suite(): diff --git a/tests/test_util.py b/tests/test_util.py index ea7c5925..cee7d526 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -8,28 +8,23 @@ import unittest from copy import copy from distutils.errors import DistutilsPlatformError - -from distutils.util import get_platform -from distutils.util import convert_path -from distutils.util import change_root -from distutils.util import check_environ -from distutils.util import split_quoted -from distutils.util import strtobool -from distutils.util import rfc822_escape - +from distutils.util import (get_platform, convert_path, change_root, + check_environ, split_quoted, strtobool, + rfc822_escape) from distutils import util # used to patch _environ_checked from distutils.sysconfig import get_config_vars from distutils import sysconfig +from distutils.tests import support -class utilTestCase(unittest.TestCase): +class UtilTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): + super(UtilTestCase, self).setUp() # saving the environment self.name = os.name self.platform = sys.platform self.version = sys.version self.sep = os.sep - self.environ = dict(os.environ) self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive @@ -51,10 +46,6 @@ class utilTestCase(unittest.TestCase): sys.platform = self.platform sys.version = self.version os.sep = self.sep - for k, v in self.environ.items(): - os.environ[k] = v - for k in set(os.environ) - set(self.environ): - del os.environ[k] os.path.join = self.join os.path.isabs = self.isabs os.path.splitdrive = self.splitdrive @@ -63,6 +54,7 @@ class utilTestCase(unittest.TestCase): else: del os.uname sysconfig._config_vars = copy(self._config_vars) + super(UtilTestCase, self).tearDown() def _set_uname(self, uname): self._uname = uname @@ -102,7 +94,7 @@ class utilTestCase(unittest.TestCase): ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + self.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') @@ -110,7 +102,7 @@ class utilTestCase(unittest.TestCase): self.assertEquals(get_platform(), 'macosx-10.3-i386') # macbook with fat binaries (fat, universal or fat64) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + self.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -214,21 +206,14 @@ class utilTestCase(unittest.TestCase): # posix without HOME if os.name == 'posix': # this test won't run on windows - old_home = os.environ.get('HOME') - try: - check_environ() - import pwd - self.assertEquals(os.environ['HOME'], - pwd.getpwuid(os.getuid())[5]) - finally: - if old_home is not None: - os.environ['HOME'] = old_home - else: - del os.environ['HOME'] + check_environ() + import pwd + self.assertEquals(self.environ['HOME'], + pwd.getpwuid(os.getuid())[5]) else: check_environ() - self.assertEquals(os.environ['PLAT'], get_platform()) + self.assertEquals(self.environ['PLAT'], get_platform()) self.assertEquals(util._environ_checked, 1) def test_split_quoted(self): @@ -253,7 +238,7 @@ class utilTestCase(unittest.TestCase): self.assertEquals(res, wanted) def test_suite(): - return unittest.makeSuite(utilTestCase) + return unittest.makeSuite(UtilTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 08fd7deaae5c8a866f5f5efe67efd104e632ec96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 12:20:44 +0000 Subject: Merged revisions 72543 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72543 | tarek.ziade | 2009-05-10 14:17:30 +0200 (Sun, 10 May 2009) | 1 line now using EnvironGuard everywhere ........ --- tests/test_config.py | 29 ++++------------------------- tests/test_dist.py | 20 ++++++-------------- tests/test_util.py | 45 +++++++++++++++------------------------------ 3 files changed, 25 insertions(+), 69 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 7506f932..0f97cf7a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -48,18 +48,14 @@ password:xxx class PyPIRCCommandTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def setUp(self): """Patches the environment.""" super(PyPIRCCommandTestCase, self).setUp() - - if 'HOME' in os.environ: - self._old_home = os.environ['HOME'] - else: - self._old_home = None self.tmp_dir = self.mkdtemp() - os.environ['HOME'] = self.tmp_dir + self.environ['HOME'] = self.tmp_dir self.rc = os.path.join(self.tmp_dir, '.pypirc') self.dist = Distribution() @@ -75,10 +71,6 @@ class PyPIRCCommandTestCase(support.TempdirManager, def tearDown(self): """Removes the patch.""" - if self._old_home is None: - del os.environ['HOME'] - else: - os.environ['HOME'] = self._old_home set_threshold(self.old_threshold) super(PyPIRCCommandTestCase, self).tearDown() @@ -88,12 +80,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, # 2. handle the old format # new format - f = open(self.rc, 'w') - try: - f.write(PYPIRC) - finally: - f.close() - + self.write_file(self.rc, PYPIRC) cmd = self._cmd(self.dist) config = cmd._read_pypirc() @@ -104,10 +91,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, self.assertEquals(config, waited) # old format - f = open(self.rc, 'w') - f.write(PYPIRC_OLD) - f.close() - + self.write_file(self.rc, PYPIRC_OLD) config = cmd._read_pypirc() config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), @@ -116,19 +100,14 @@ class PyPIRCCommandTestCase(support.TempdirManager, self.assertEquals(config, waited) def test_server_empty_registration(self): - cmd = self._cmd(self.dist) rc = cmd._get_rc_file() self.assert_(not os.path.exists(rc)) - cmd._store_pypirc('tarek', 'xxx') - self.assert_(os.path.exists(rc)) content = open(rc).read() - self.assertEquals(content, WANTED) - def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) diff --git a/tests/test_dist.py b/tests/test_dist.py index 3ac0fdd1..092bd14f 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -38,11 +38,13 @@ class TestDistribution(distutils.dist.Distribution): class DistributionTestCase(unittest.TestCase): def setUp(self): + super(DistributionTestCase, self).setUp() self.argv = sys.argv[:] del sys.argv[1:] def tearDown(self): sys.argv[:] = self.argv + super(DistributionTestCase, self).tearDown() def create_distribution(self, configfiles=()): d = TestDistribution() @@ -121,7 +123,8 @@ class DistributionTestCase(unittest.TestCase): self.assertEquals(len(warns), 0) -class MetadataTestCase(support.TempdirManager, unittest.TestCase): +class MetadataTestCase(support.TempdirManager, support.EnvironGuard, + unittest.TestCase): def test_simple_metadata(self): attrs = {"name": "package", @@ -208,13 +211,6 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase): def test_custom_pydistutils(self): # fixes #2166 # make sure pydistutils.cfg is found - old = {} - for env in ('HOME', 'HOMEPATH', 'HOMEDRIVE'): - value = os.environ.get(env) - old[env] = value - if value is not None: - del os.environ[env] - if os.name == 'posix': user_filename = ".pydistutils.cfg" else: @@ -231,22 +227,18 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase): # linux-style if sys.platform in ('linux', 'darwin'): - os.environ['HOME'] = temp_dir + self.environ['HOME'] = temp_dir files = dist.find_config_files() self.assert_(user_filename in files) # win32-style if sys.platform == 'win32': # home drive should be found - os.environ['HOME'] = temp_dir + self.environ['HOME'] = temp_dir files = dist.find_config_files() self.assert_(user_filename in files, '%r not found in %r' % (user_filename, files)) finally: - for key, value in old.items(): - if value is None: - continue - os.environ[key] = value os.remove(user_filename) def test_suite(): diff --git a/tests/test_util.py b/tests/test_util.py index ea7c5925..cee7d526 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -8,28 +8,23 @@ import unittest from copy import copy from distutils.errors import DistutilsPlatformError - -from distutils.util import get_platform -from distutils.util import convert_path -from distutils.util import change_root -from distutils.util import check_environ -from distutils.util import split_quoted -from distutils.util import strtobool -from distutils.util import rfc822_escape - +from distutils.util import (get_platform, convert_path, change_root, + check_environ, split_quoted, strtobool, + rfc822_escape) from distutils import util # used to patch _environ_checked from distutils.sysconfig import get_config_vars from distutils import sysconfig +from distutils.tests import support -class utilTestCase(unittest.TestCase): +class UtilTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): + super(UtilTestCase, self).setUp() # saving the environment self.name = os.name self.platform = sys.platform self.version = sys.version self.sep = os.sep - self.environ = dict(os.environ) self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive @@ -51,10 +46,6 @@ class utilTestCase(unittest.TestCase): sys.platform = self.platform sys.version = self.version os.sep = self.sep - for k, v in self.environ.items(): - os.environ[k] = v - for k in set(os.environ) - set(self.environ): - del os.environ[k] os.path.join = self.join os.path.isabs = self.isabs os.path.splitdrive = self.splitdrive @@ -63,6 +54,7 @@ class utilTestCase(unittest.TestCase): else: del os.uname sysconfig._config_vars = copy(self._config_vars) + super(UtilTestCase, self).tearDown() def _set_uname(self, uname): self._uname = uname @@ -102,7 +94,7 @@ class utilTestCase(unittest.TestCase): ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + self.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') @@ -110,7 +102,7 @@ class utilTestCase(unittest.TestCase): self.assertEquals(get_platform(), 'macosx-10.3-i386') # macbook with fat binaries (fat, universal or fat64) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + self.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -214,21 +206,14 @@ class utilTestCase(unittest.TestCase): # posix without HOME if os.name == 'posix': # this test won't run on windows - old_home = os.environ.get('HOME') - try: - check_environ() - import pwd - self.assertEquals(os.environ['HOME'], - pwd.getpwuid(os.getuid())[5]) - finally: - if old_home is not None: - os.environ['HOME'] = old_home - else: - del os.environ['HOME'] + check_environ() + import pwd + self.assertEquals(self.environ['HOME'], + pwd.getpwuid(os.getuid())[5]) else: check_environ() - self.assertEquals(os.environ['PLAT'], get_platform()) + self.assertEquals(self.environ['PLAT'], get_platform()) self.assertEquals(util._environ_checked, 1) def test_split_quoted(self): @@ -253,7 +238,7 @@ class utilTestCase(unittest.TestCase): self.assertEquals(res, wanted) def test_suite(): - return unittest.makeSuite(utilTestCase) + return unittest.makeSuite(UtilTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From f07c0de2be03f3de0a085d813069b42b71650cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 12:36:48 +0000 Subject: fixed test for all platforms --- tests/test_install_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 69a24faf..d7681668 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -57,7 +57,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.distribution.script_name = 'setup.py' # get_output should return 4 elements - self.assertEquals(len(cmd.get_outputs()), 4) + self.assert_(len(cmd.get_outputs()) >= 2) def test_get_inputs(self): pkg_dir, dist = self.create_dist() -- cgit v1.2.1 From 61608597d3a2eb12e119267100a479ba2657bd51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 12:38:16 +0000 Subject: Merged revisions 72547 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72547 | tarek.ziade | 2009-05-10 14:36:48 +0200 (Sun, 10 May 2009) | 1 line fixed test for all platforms ........ --- tests/test_install_lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 69a24faf..d7681668 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -57,7 +57,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.distribution.script_name = 'setup.py' # get_output should return 4 elements - self.assertEquals(len(cmd.get_outputs()), 4) + self.assert_(len(cmd.get_outputs()) >= 2) def test_get_inputs(self): pkg_dir, dist = self.create_dist() -- cgit v1.2.1 From faf53a7e22608dcd7399ef71fb5b6bcc3e126e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 21:27:55 +0000 Subject: fixed test_build_ext for win32 --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index dab9712a..54d6b96d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -192,7 +192,7 @@ class BuildExtTestCase(support.TempdirManager, cmd = build_ext(dist) cmd.library_dirs = 'my_lib_dir' cmd.finalize_options() - self.assertEquals(cmd.library_dirs, ['my_lib_dir']) + self.assert_('my_lib_dir' in cmd.library_dirs) # make sure rpath is turned into a list # if it's a list of os.pathsep's paths -- cgit v1.2.1 From f2e737ac9bcefdd21eaf377af03fb370b23c1997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 21:31:23 +0000 Subject: Merged revisions 72552 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72552 | tarek.ziade | 2009-05-10 23:27:55 +0200 (Sun, 10 May 2009) | 1 line fixed test_build_ext for win32 ........ --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index c22be2ac..6f71a4ab 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -192,7 +192,7 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.library_dirs = 'my_lib_dir' cmd.finalize_options() - self.assertEquals(cmd.library_dirs, ['my_lib_dir']) + self.assert_('my_lib_dir' in cmd.library_dirs) # make sure rpath is turned into a list # if it's a list of os.pathsep's paths -- cgit v1.2.1 From fb904cc2f4a3dc797b02f2d0fb8ad5684d35cd73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 10 May 2009 21:33:09 +0000 Subject: Merged revisions 72552 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72552 | tarek.ziade | 2009-05-10 23:27:55 +0200 (Sun, 10 May 2009) | 1 line fixed test_build_ext for win32 ........ --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 98ae8cf3..dc40d13f 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -129,7 +129,7 @@ class BuildExtTestCase(support.TempdirManager, cmd = build_ext(dist) cmd.library_dirs = 'my_lib_dir' cmd.finalize_options() - self.assertEquals(cmd.library_dirs, ['my_lib_dir']) + self.assert_('my_lib_dir' in cmd.library_dirs) # make sure rpath is turned into a list # if it's a list of os.pathsep's paths -- cgit v1.2.1 From 364368f687f823446e11af8b83a322ebba528f45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 11 May 2009 08:45:17 +0000 Subject: distutils.test_build_clib added a new line at the end of the file, to avoid a warning with some compilers --- tests/test_build_clib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 3c6643f3..47d85cd8 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -109,7 +109,7 @@ class BuildCLibTestCase(support.TempdirManager, cmd = build_clib(dist) foo_c = os.path.join(pkg_dir, 'foo.c') - self.write_file(foo_c, 'int main(void) { return 1;}') + self.write_file(foo_c, 'int main(void) { return 1;}\n') cmd.libraries = [('foo', {'sources': [foo_c]})] build_temp = os.path.join(pkg_dir, 'build') -- cgit v1.2.1 From 34a3ad72ee75c89f2510ecf11c65a62a759944f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 11 May 2009 08:49:17 +0000 Subject: Merged revisions 72560 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72560 | tarek.ziade | 2009-05-11 10:45:17 +0200 (Mon, 11 May 2009) | 1 line distutils.test_build_clib added a new line at the end of the file, to avoid a warning with some compilers ........ --- tests/test_build_clib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 3c6643f3..47d85cd8 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -109,7 +109,7 @@ class BuildCLibTestCase(support.TempdirManager, cmd = build_clib(dist) foo_c = os.path.join(pkg_dir, 'foo.c') - self.write_file(foo_c, 'int main(void) { return 1;}') + self.write_file(foo_c, 'int main(void) { return 1;}\n') cmd.libraries = [('foo', {'sources': [foo_c]})] build_temp = os.path.join(pkg_dir, 'build') -- cgit v1.2.1 From cb0eb9b3590a42eae1cd86e70345fd86d5b923dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 12 May 2009 07:01:29 +0000 Subject: removing the assert statement so the code works when Python is run with -O --- command/install_lib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index 87e3c7aa..411db79c 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -80,7 +80,8 @@ class install_lib(Command): if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) - assert self.optimize in (0, 1, 2) + if self.optimize not in (0, 1, 2): + raise AssertionError except (ValueError, AssertionError): raise DistutilsOptionError, "optimize must be 0, 1, or 2" -- cgit v1.2.1 From 57df14c3ab3980b1245a959940e5a6d20e61dfde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 12 May 2009 07:04:51 +0000 Subject: Merged revisions 72577 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72577 | tarek.ziade | 2009-05-12 09:01:29 +0200 (Tue, 12 May 2009) | 1 line removing the assert statement so the code works when Python is run with -O ........ --- command/install_lib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index 81107a85..4ea61d78 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -80,7 +80,8 @@ class install_lib (Command): if type(self.optimize) is not IntType: try: self.optimize = int(self.optimize) - assert 0 <= self.optimize <= 2 + if self.optimize not in (0, 1, 2): + raise AssertionError except (ValueError, AssertionError): raise DistutilsOptionError, "optimize must be 0, 1, or 2" -- cgit v1.2.1 From 5c2bbef86612775eb376d2dd50d50242c87793ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 12 May 2009 07:06:42 +0000 Subject: Merged revisions 72577 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72577 | tarek.ziade | 2009-05-12 09:01:29 +0200 (Tue, 12 May 2009) | 1 line removing the assert statement so the code works when Python is run with -O ........ --- command/install_lib.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/install_lib.py b/command/install_lib.py index 022efa48..85fb3acd 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -77,7 +77,8 @@ class install_lib(Command): if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) - assert self.optimize in (0, 1, 2) + if self.optimize not in (0, 1, 2): + raise AssertionError except (ValueError, AssertionError): raise DistutilsOptionError("optimize must be 0, 1, or 2") -- cgit v1.2.1 From 870adffdf536f9ce4762af9f1501dc4733a529f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 12 May 2009 17:07:14 +0000 Subject: fixed #5977: distutils build_ext.get_outputs was not using the inplace option --- command/build_ext.py | 87 +++++++++++++++++++++++++------------------------ tests/test_build_ext.py | 43 ++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 45 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index dfabf910..73fd7680 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -311,38 +311,38 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler(compiler=self.compiler, + self._compiler = new_compiler(compiler=self.compiler, verbose=self.verbose, dry_run=self.dry_run, force=self.force) - customize_compiler(self.compiler) + customize_compiler(self._compiler) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): - self.compiler.initialize(self.plat_name) + self._compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self.compiler.set_include_dirs(self.include_dirs) + self._compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: - self.compiler.define_macro(name, value) + self._compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self.compiler.undefine_macro(macro) + self._compiler.undefine_macro(macro) if self.libraries is not None: - self.compiler.set_libraries(self.libraries) + self._compiler.set_libraries(self.libraries) if self.library_dirs is not None: - self.compiler.set_library_dirs(self.library_dirs) + self._compiler.set_library_dirs(self.library_dirs) if self.rpath is not None: - self.compiler.set_runtime_library_dirs(self.rpath) + self._compiler.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self.compiler.set_link_objects(self.link_objects) + self._compiler.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() @@ -446,9 +446,7 @@ class build_ext (Command): # "build" tree. outputs = [] for ext in self.extensions: - fullname = self.get_ext_fullname(ext.name) - outputs.append(os.path.join(self.build_lib, - self.get_ext_filename(fullname))) + outputs.append(self.get_ext_fullpath(ext.name)) return outputs def build_extensions(self): @@ -473,24 +471,9 @@ class build_ext (Command): "a list of source filenames") % ext.name sources = list(sources) - fullname = self.get_ext_fullname(ext.name) - if self.inplace: - # ignore build-lib -- put the compiled extension into - # the source tree along with pure Python modules - - modpath = string.split(fullname, '.') - package = string.join(modpath[0:-1], '.') - base = modpath[-1] - - build_py = self.get_finalized_command('build_py') - package_dir = build_py.get_package_dir(package) - ext_filename = os.path.join(package_dir, - self.get_ext_filename(base)) - else: - ext_filename = os.path.join(self.build_lib, - self.get_ext_filename(fullname)) + ext_path = self.get_ext_fullpath(ext.name) depends = sources + ext.depends - if not (self.force or newer_group(depends, ext_filename, 'newer')): + if not (self.force or newer_group(depends, ext_path, 'newer')): log.debug("skipping '%s' extension (up-to-date)", ext.name) return else: @@ -521,13 +504,13 @@ class build_ext (Command): for undef in ext.undef_macros: macros.append((undef,)) - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args, - depends=ext.depends) + objects = self._compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends) # XXX -- this is a Vile HACK! # @@ -548,10 +531,10 @@ class build_ext (Command): extra_args = ext.extra_link_args or [] # Detect target language, if not provided - language = ext.language or self.compiler.detect_language(sources) + language = ext.language or self._compiler.detect_language(sources) - self.compiler.link_shared_object( - objects, ext_filename, + self._compiler.link_shared_object( + objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, runtime_library_dirs=ext.runtime_library_dirs, @@ -653,8 +636,28 @@ class build_ext (Command): # -- Name generators ----------------------------------------------- # (extension names, filenames, whatever) + def get_ext_fullpath(self, ext_name): + """Returns the path of the filename for a given extension. + + The file is located in `build_lib` or directly in the package + (inplace option). + """ + if self.inplace: + fullname = self.get_ext_fullname(ext_name) + modpath = fullname.split('.') + package = '.'.join(modpath[0:-1]) + base = modpath[-1] + build_py = self.get_finalized_command('build_py') + package_dir = os.path.abspath(build_py.get_package_dir(package)) + return os.path.join(package_dir, base) + else: + filename = self.get_ext_filename(ext_name) + return os.path.join(self.build_lib, filename) + + def get_ext_fullname(self, ext_name): + """Returns the fullname of a given extension name. - def get_ext_fullname (self, ext_name): + Adds the `package.` prefix""" if self.package is None: return ext_name else: @@ -701,7 +704,7 @@ class build_ext (Command): # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils.msvccompiler import MSVCCompiler - if not isinstance(self.compiler, MSVCCompiler): + if not isinstance(self._compiler, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 54d6b96d..5cdfc2a9 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -113,7 +113,7 @@ class BuildExtTestCase(support.TempdirManager, else: _config_vars['Py_ENABLE_SHARED'] = old_var - # make sur we get some lobrary dirs under solaris + # make sure we get some library dirs under solaris self.assert_(len(cmd.library_dirs) > 0) def test_user_site(self): @@ -282,13 +282,50 @@ class BuildExtTestCase(support.TempdirManager, cmd.ensure_finalized() self.assertEquals(cmd.get_source_files(), ['xxx']) + def test_compiler_option(self): + # cmd.compiler is an option and + # should not be overriden by a compiler instance + # when the command is run + dist = Distribution() + cmd = build_ext(dist) + cmd.compiler = 'unix' + cmd.ensure_finalized() + cmd.run() + self.assertEquals(cmd.compiler, 'unix') + def test_get_outputs(self): - modules = [Extension('foo', ['xxx'], optional=False)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) + tmp_dir = self.mkdtemp() + c_file = os.path.join(tmp_dir, 'foo.c') + self.write_file(c_file, '') + ext = Extension('foo', [c_file], optional=False) + dist = Distribution({'name': 'xx', + 'ext_modules': [ext]}) cmd = build_ext(dist) cmd.ensure_finalized() self.assertEquals(len(cmd.get_outputs()), 1) + if os.name == "nt": + cmd.debug = sys.executable.endswith("_d.exe") + + cmd.build_lib = os.path.join(self.tmp_dir, 'build') + cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') + + # issue #5977 : distutils build_ext.get_outputs + # returns wrong result with --inplace + cmd.inplace = 1 + cmd.run() + so_file = cmd.get_outputs()[0] + self.assert_(os.path.exists(so_file)) + so_dir = os.path.dirname(so_file) + self.assertEquals(so_dir, os.getcwd()) + + cmd.inplace = 0 + cmd.run() + so_file = cmd.get_outputs()[0] + self.assert_(os.path.exists(so_file)) + so_dir = os.path.dirname(so_file) + self.assertEquals(so_dir, cmd.build_lib) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 22a803701f441d9cc088ceb95c6c9559180158dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 12 May 2009 17:11:54 +0000 Subject: Merged revisions 72585 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72585 | tarek.ziade | 2009-05-12 19:07:14 +0200 (Tue, 12 May 2009) | 1 line fixed #5977: distutils build_ext.get_outputs was not using the inplace option ........ --- command/build_ext.py | 87 +++++++++++++++++++++++++------------------------ tests/support.py | 12 +++++++ tests/test_build_ext.py | 43 ++++++++++++++++++++++-- 3 files changed, 97 insertions(+), 45 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index c03951cb..c611ad48 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -303,38 +303,38 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler(compiler=self.compiler, + self._compiler = new_compiler(compiler=self.compiler, verbose=self.verbose, dry_run=self.dry_run, force=self.force) - customize_compiler(self.compiler) + customize_compiler(self._compiler) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): - self.compiler.initialize(self.plat_name) + self._compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self.compiler.set_include_dirs(self.include_dirs) + self._compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: - self.compiler.define_macro(name, value) + self._compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self.compiler.undefine_macro(macro) + self._compiler.undefine_macro(macro) if self.libraries is not None: - self.compiler.set_libraries(self.libraries) + self._compiler.set_libraries(self.libraries) if self.library_dirs is not None: - self.compiler.set_library_dirs(self.library_dirs) + self._compiler.set_library_dirs(self.library_dirs) if self.rpath is not None: - self.compiler.set_runtime_library_dirs(self.rpath) + self._compiler.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self.compiler.set_link_objects(self.link_objects) + self._compiler.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() @@ -438,9 +438,7 @@ class build_ext (Command): # "build" tree. outputs = [] for ext in self.extensions: - fullname = self.get_ext_fullname(ext.name) - outputs.append(os.path.join(self.build_lib, - self.get_ext_filename(fullname))) + outputs.append(self.get_ext_fullpath(ext.name)) return outputs def build_extensions(self): @@ -459,24 +457,9 @@ class build_ext (Command): "a list of source filenames") % ext.name sources = list(sources) - fullname = self.get_ext_fullname(ext.name) - if self.inplace: - # ignore build-lib -- put the compiled extension into - # the source tree along with pure Python modules - - modpath = string.split(fullname, '.') - package = string.join(modpath[0:-1], '.') - base = modpath[-1] - - build_py = self.get_finalized_command('build_py') - package_dir = build_py.get_package_dir(package) - ext_filename = os.path.join(package_dir, - self.get_ext_filename(base)) - else: - ext_filename = os.path.join(self.build_lib, - self.get_ext_filename(fullname)) + ext_path = self.get_ext_fullpath(ext.name) depends = sources + ext.depends - if not (self.force or newer_group(depends, ext_filename, 'newer')): + if not (self.force or newer_group(depends, ext_path, 'newer')): log.debug("skipping '%s' extension (up-to-date)", ext.name) return else: @@ -507,13 +490,13 @@ class build_ext (Command): for undef in ext.undef_macros: macros.append((undef,)) - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args, - depends=ext.depends) + objects = self._compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends) # XXX -- this is a Vile HACK! # @@ -534,10 +517,10 @@ class build_ext (Command): extra_args = ext.extra_link_args or [] # Detect target language, if not provided - language = ext.language or self.compiler.detect_language(sources) + language = ext.language or self._compiler.detect_language(sources) - self.compiler.link_shared_object( - objects, ext_filename, + self._compiler.link_shared_object( + objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, runtime_library_dirs=ext.runtime_library_dirs, @@ -639,8 +622,28 @@ class build_ext (Command): # -- Name generators ----------------------------------------------- # (extension names, filenames, whatever) + def get_ext_fullpath(self, ext_name): + """Returns the path of the filename for a given extension. + + The file is located in `build_lib` or directly in the package + (inplace option). + """ + if self.inplace: + fullname = self.get_ext_fullname(ext_name) + modpath = fullname.split('.') + package = '.'.join(modpath[0:-1]) + base = modpath[-1] + build_py = self.get_finalized_command('build_py') + package_dir = os.path.abspath(build_py.get_package_dir(package)) + return os.path.join(package_dir, base) + else: + filename = self.get_ext_filename(ext_name) + return os.path.join(self.build_lib, filename) + + def get_ext_fullname(self, ext_name): + """Returns the fullname of a given extension name. - def get_ext_fullname (self, ext_name): + Adds the `package.` prefix""" if self.package is None: return ext_name else: @@ -687,7 +690,7 @@ class build_ext (Command): # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils.msvccompiler import MSVCCompiler - if not isinstance(self.compiler, MSVCCompiler): + if not isinstance(self._compiler, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' diff --git a/tests/support.py b/tests/support.py index 475ceee5..d24a18ea 100644 --- a/tests/support.py +++ b/tests/support.py @@ -42,6 +42,18 @@ class TempdirManager(object): self.tempdirs.append(d) return d + def write_file(self, path, content='xxx'): + """Writes a file in the given path. + + path can be a string or a sequence. + """ + if isinstance(path, (list, tuple)): + path = os.path.join(*path) + f = open(path, 'w') + try: + f.write(content) + finally: + f.close() class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index dc40d13f..8889798c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -99,7 +99,7 @@ class BuildExtTestCase(support.TempdirManager, else: _config_vars['Py_ENABLE_SHARED'] = old_var - # make sur we get some lobrary dirs under solaris + # make sure we get some library dirs under solaris self.assert_(len(cmd.library_dirs) > 0) def test_finalize_options(self): @@ -219,13 +219,50 @@ class BuildExtTestCase(support.TempdirManager, cmd.ensure_finalized() self.assertEquals(cmd.get_source_files(), ['xxx']) + def test_compiler_option(self): + # cmd.compiler is an option and + # should not be overriden by a compiler instance + # when the command is run + dist = Distribution() + cmd = build_ext(dist) + cmd.compiler = 'unix' + cmd.ensure_finalized() + cmd.run() + self.assertEquals(cmd.compiler, 'unix') + def test_get_outputs(self): - modules = [Extension('foo', ['xxx'])] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) + tmp_dir = self.mkdtemp() + c_file = os.path.join(tmp_dir, 'foo.c') + self.write_file(c_file, '') + ext = Extension('foo', [c_file]) + dist = Distribution({'name': 'xx', + 'ext_modules': [ext]}) cmd = build_ext(dist) cmd.ensure_finalized() self.assertEquals(len(cmd.get_outputs()), 1) + if os.name == "nt": + cmd.debug = sys.executable.endswith("_d.exe") + + cmd.build_lib = os.path.join(self.tmp_dir, 'build') + cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') + + # issue #5977 : distutils build_ext.get_outputs + # returns wrong result with --inplace + cmd.inplace = 1 + cmd.run() + so_file = cmd.get_outputs()[0] + self.assert_(os.path.exists(so_file)) + so_dir = os.path.dirname(so_file) + self.assertEquals(so_dir, os.getcwd()) + + cmd.inplace = 0 + cmd.run() + so_file = cmd.get_outputs()[0] + self.assert_(os.path.exists(so_file)) + so_dir = os.path.dirname(so_file) + self.assertEquals(so_dir, cmd.build_lib) + def test_suite(): if not sysconfig.python_build: if test_support.verbose: -- cgit v1.2.1 From 8120d37484b292b246b7d0673a9faa0262a7696d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 12 May 2009 17:14:01 +0000 Subject: Merged revisions 72585 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72585 | tarek.ziade | 2009-05-12 19:07:14 +0200 (Tue, 12 May 2009) | 1 line fixed #5977: distutils build_ext.get_outputs was not using the inplace option ........ --- command/build_ext.py | 85 +++++++++++++++++++++++++------------------------ tests/test_build_ext.py | 43 +++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 44 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index d3668e9f..e305bde9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -310,38 +310,38 @@ class build_ext(Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler(compiler=self.compiler, + self._compiler = new_compiler(compiler=self.compiler, verbose=self.verbose, dry_run=self.dry_run, force=self.force) - customize_compiler(self.compiler) + customize_compiler(self._compiler) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): - self.compiler.initialize(self.plat_name) + self._compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self.compiler.set_include_dirs(self.include_dirs) + self._compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: - self.compiler.define_macro(name, value) + self._compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self.compiler.undefine_macro(macro) + self._compiler.undefine_macro(macro) if self.libraries is not None: - self.compiler.set_libraries(self.libraries) + self._compiler.set_libraries(self.libraries) if self.library_dirs is not None: - self.compiler.set_library_dirs(self.library_dirs) + self._compiler.set_library_dirs(self.library_dirs) if self.rpath is not None: - self.compiler.set_runtime_library_dirs(self.rpath) + self._compiler.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self.compiler.set_link_objects(self.link_objects) + self._compiler.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() @@ -444,9 +444,7 @@ class build_ext(Command): # "build" tree. outputs = [] for ext in self.extensions: - fullname = self.get_ext_fullname(ext.name) - outputs.append(os.path.join(self.build_lib, - self.get_ext_filename(fullname))) + outputs.append(self.get_ext_fullpath(ext.name)) return outputs def build_extensions(self): @@ -471,24 +469,9 @@ class build_ext(Command): "a list of source filenames" % ext.name) sources = list(sources) - fullname = self.get_ext_fullname(ext.name) - if self.inplace: - # ignore build-lib -- put the compiled extension into - # the source tree along with pure Python modules - - modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] - - build_py = self.get_finalized_command('build_py') - package_dir = build_py.get_package_dir(package) - ext_filename = os.path.join(package_dir, - self.get_ext_filename(base)) - else: - ext_filename = os.path.join(self.build_lib, - self.get_ext_filename(fullname)) + ext_path = self.get_ext_fullpath(ext.name) depends = sources + ext.depends - if not (self.force or newer_group(depends, ext_filename, 'newer')): + if not (self.force or newer_group(depends, ext_path, 'newer')): log.debug("skipping '%s' extension (up-to-date)", ext.name) return else: @@ -519,13 +502,13 @@ class build_ext(Command): for undef in ext.undef_macros: macros.append((undef,)) - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args, - depends=ext.depends) + objects = self._compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends) # XXX -- this is a Vile HACK! # @@ -546,10 +529,10 @@ class build_ext(Command): extra_args = ext.extra_link_args or [] # Detect target language, if not provided - language = ext.language or self.compiler.detect_language(sources) + language = ext.language or self._compiler.detect_language(sources) - self.compiler.link_shared_object( - objects, ext_filename, + self._compiler.link_shared_object( + objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, runtime_library_dirs=ext.runtime_library_dirs, @@ -640,8 +623,28 @@ class build_ext(Command): # -- Name generators ----------------------------------------------- # (extension names, filenames, whatever) + def get_ext_fullpath(self, ext_name): + """Returns the path of the filename for a given extension. + + The file is located in `build_lib` or directly in the package + (inplace option). + """ + if self.inplace: + fullname = self.get_ext_fullname(ext_name) + modpath = fullname.split('.') + package = '.'.join(modpath[0:-1]) + base = modpath[-1] + build_py = self.get_finalized_command('build_py') + package_dir = os.path.abspath(build_py.get_package_dir(package)) + return os.path.join(package_dir, base) + else: + filename = self.get_ext_filename(ext_name) + return os.path.join(self.build_lib, filename) def get_ext_fullname(self, ext_name): + """Returns the fullname of a given extension name. + + Adds the `package.` prefix""" if self.package is None: return ext_name else: @@ -686,7 +689,7 @@ class build_ext(Command): # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils.msvccompiler import MSVCCompiler - if not isinstance(self.compiler, MSVCCompiler): + if not isinstance(self._compiler, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 6f71a4ab..86064fcc 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -113,7 +113,7 @@ class BuildExtTestCase(TempdirManager, else: _config_vars['Py_ENABLE_SHARED'] = old_var - # make sur we get some lobrary dirs under solaris + # make sure we get some library dirs under solaris self.assert_(len(cmd.library_dirs) > 0) def test_user_site(self): @@ -282,13 +282,50 @@ class BuildExtTestCase(TempdirManager, cmd.ensure_finalized() self.assertEquals(cmd.get_source_files(), ['xxx']) + def test_compiler_option(self): + # cmd.compiler is an option and + # should not be overriden by a compiler instance + # when the command is run + dist = Distribution() + cmd = build_ext(dist) + cmd.compiler = 'unix' + cmd.ensure_finalized() + cmd.run() + self.assertEquals(cmd.compiler, 'unix') + def test_get_outputs(self): - modules = [Extension('foo', ['xxx'], optional=False)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) + tmp_dir = self.mkdtemp() + c_file = os.path.join(tmp_dir, 'foo.c') + self.write_file(c_file, '') + ext = Extension('foo', [c_file], optional=False) + dist = Distribution({'name': 'xx', + 'ext_modules': [ext]}) cmd = build_ext(dist) cmd.ensure_finalized() self.assertEquals(len(cmd.get_outputs()), 1) + if os.name == "nt": + cmd.debug = sys.executable.endswith("_d.exe") + + cmd.build_lib = os.path.join(self.tmp_dir, 'build') + cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') + + # issue #5977 : distutils build_ext.get_outputs + # returns wrong result with --inplace + cmd.inplace = 1 + cmd.run() + so_file = cmd.get_outputs()[0] + self.assert_(os.path.exists(so_file)) + so_dir = os.path.dirname(so_file) + self.assertEquals(so_dir, os.getcwd()) + + cmd.inplace = 0 + cmd.run() + so_file = cmd.get_outputs()[0] + self.assert_(os.path.exists(so_file)) + so_dir = os.path.dirname(so_file) + self.assertEquals(so_dir, cmd.build_lib) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 5042c7cc1026361bd2ce98f951a19ad2f3ece578 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 12 May 2009 21:06:05 +0000 Subject: the compiler attribute is used in setup.py; can't rename --- command/build_ext.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 73fd7680..10d50fad 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -311,38 +311,38 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self._compiler = new_compiler(compiler=self.compiler, + self.compiler = new_compiler(compiler=None, verbose=self.verbose, dry_run=self.dry_run, force=self.force) - customize_compiler(self._compiler) + customize_compiler(self.compiler) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): - self._compiler.initialize(self.plat_name) + self.compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self._compiler.set_include_dirs(self.include_dirs) + self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: - self._compiler.define_macro(name, value) + self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self._compiler.undefine_macro(macro) + self.compiler.undefine_macro(macro) if self.libraries is not None: - self._compiler.set_libraries(self.libraries) + self.compiler.set_libraries(self.libraries) if self.library_dirs is not None: - self._compiler.set_library_dirs(self.library_dirs) + self.compiler.set_library_dirs(self.library_dirs) if self.rpath is not None: - self._compiler.set_runtime_library_dirs(self.rpath) + self.compiler.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self._compiler.set_link_objects(self.link_objects) + self.compiler.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() @@ -504,7 +504,7 @@ class build_ext (Command): for undef in ext.undef_macros: macros.append((undef,)) - objects = self._compiler.compile(sources, + objects = self.compiler.compile(sources, output_dir=self.build_temp, macros=macros, include_dirs=ext.include_dirs, @@ -531,9 +531,9 @@ class build_ext (Command): extra_args = ext.extra_link_args or [] # Detect target language, if not provided - language = ext.language or self._compiler.detect_language(sources) + language = ext.language or self.compiler.detect_language(sources) - self._compiler.link_shared_object( + self.compiler.link_shared_object( objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, @@ -704,7 +704,7 @@ class build_ext (Command): # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils.msvccompiler import MSVCCompiler - if not isinstance(self._compiler, MSVCCompiler): + if not isinstance(self.compiler, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' -- cgit v1.2.1 From 431ef0a9555d0ccdd974e61a4cad5159dbdbc22a Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 12 May 2009 21:20:41 +0000 Subject: Merged revisions 72593 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72593 | benjamin.peterson | 2009-05-12 16:06:05 -0500 (Tue, 12 May 2009) | 1 line the compiler attribute is used in setup.py; can't rename ........ --- command/build_ext.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index c611ad48..01bab366 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -303,38 +303,38 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self._compiler = new_compiler(compiler=self.compiler, + self.compiler = new_compiler(compiler=None, verbose=self.verbose, dry_run=self.dry_run, force=self.force) - customize_compiler(self._compiler) + customize_compiler(self.compiler) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): - self._compiler.initialize(self.plat_name) + self.compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self._compiler.set_include_dirs(self.include_dirs) + self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: - self._compiler.define_macro(name, value) + self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self._compiler.undefine_macro(macro) + self.compiler.undefine_macro(macro) if self.libraries is not None: - self._compiler.set_libraries(self.libraries) + self.compiler.set_libraries(self.libraries) if self.library_dirs is not None: - self._compiler.set_library_dirs(self.library_dirs) + self.compiler.set_library_dirs(self.library_dirs) if self.rpath is not None: - self._compiler.set_runtime_library_dirs(self.rpath) + self.compiler.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self._compiler.set_link_objects(self.link_objects) + self.compiler.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() @@ -490,7 +490,7 @@ class build_ext (Command): for undef in ext.undef_macros: macros.append((undef,)) - objects = self._compiler.compile(sources, + objects = self.compiler.compile(sources, output_dir=self.build_temp, macros=macros, include_dirs=ext.include_dirs, @@ -517,9 +517,9 @@ class build_ext (Command): extra_args = ext.extra_link_args or [] # Detect target language, if not provided - language = ext.language or self._compiler.detect_language(sources) + language = ext.language or self.compiler.detect_language(sources) - self._compiler.link_shared_object( + self.compiler.link_shared_object( objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, @@ -690,7 +690,7 @@ class build_ext (Command): # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils.msvccompiler import MSVCCompiler - if not isinstance(self._compiler, MSVCCompiler): + if not isinstance(self.compiler, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' -- cgit v1.2.1 From d39a44c5da79381e4ae7dd7502e8a398814072d1 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 12 May 2009 21:21:26 +0000 Subject: Merged revisions 72593 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72593 | benjamin.peterson | 2009-05-12 16:06:05 -0500 (Tue, 12 May 2009) | 1 line the compiler attribute is used in setup.py; can't rename ........ --- command/build_ext.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index e305bde9..c39bd72d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -310,38 +310,38 @@ class build_ext(Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self._compiler = new_compiler(compiler=self.compiler, + self.compiler = new_compiler(compiler=None, verbose=self.verbose, dry_run=self.dry_run, force=self.force) - customize_compiler(self._compiler) + customize_compiler(self.compiler) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): - self._compiler.initialize(self.plat_name) + self.compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self._compiler.set_include_dirs(self.include_dirs) + self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: - self._compiler.define_macro(name, value) + self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self._compiler.undefine_macro(macro) + self.compiler.undefine_macro(macro) if self.libraries is not None: - self._compiler.set_libraries(self.libraries) + self.compiler.set_libraries(self.libraries) if self.library_dirs is not None: - self._compiler.set_library_dirs(self.library_dirs) + self.compiler.set_library_dirs(self.library_dirs) if self.rpath is not None: - self._compiler.set_runtime_library_dirs(self.rpath) + self.compiler.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self._compiler.set_link_objects(self.link_objects) + self.compiler.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() @@ -502,7 +502,7 @@ class build_ext(Command): for undef in ext.undef_macros: macros.append((undef,)) - objects = self._compiler.compile(sources, + objects = self.compiler.compile(sources, output_dir=self.build_temp, macros=macros, include_dirs=ext.include_dirs, @@ -529,9 +529,9 @@ class build_ext(Command): extra_args = ext.extra_link_args or [] # Detect target language, if not provided - language = ext.language or self._compiler.detect_language(sources) + language = ext.language or self.compiler.detect_language(sources) - self._compiler.link_shared_object( + self.compiler.link_shared_object( objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, @@ -689,7 +689,7 @@ class build_ext(Command): # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils.msvccompiler import MSVCCompiler - if not isinstance(self._compiler, MSVCCompiler): + if not isinstance(self.compiler, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' -- cgit v1.2.1 From f3af1e26187c79c6ff1e0a3ea442d354015654f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 13 May 2009 21:30:06 +0000 Subject: added an inifoo in the C file, to avoid a warning by the MSVC9 linker --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 5cdfc2a9..6d3852cc 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -296,7 +296,7 @@ class BuildExtTestCase(support.TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, '') + self.write_file(c_file, 'void initfoo() {};\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From 300d6e9a8f330cae08065bf5612f4c03eec5207b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 13 May 2009 22:08:54 +0000 Subject: Merged revisions 72610 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72610 | tarek.ziade | 2009-05-13 23:30:06 +0200 (Wed, 13 May 2009) | 1 line added an inifoo in the C file, to avoid a warning by the MSVC9 linker ........ --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 8889798c..20f05539 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -233,7 +233,7 @@ class BuildExtTestCase(support.TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, '') + self.write_file(c_file, 'void initfoo() {};\n') ext = Extension('foo', [c_file]) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From 6378315ebc64b957142fb142058415a4210784e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 13 May 2009 22:16:03 +0000 Subject: adding void to the c function --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 6d3852cc..45c5b39e 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -296,7 +296,7 @@ class BuildExtTestCase(support.TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void initfoo() {};\n') + self.write_file(c_file, 'void initfoo(void) {};\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From fcca9f694a425f7e4c647977e32a48eda9033c1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 13 May 2009 22:18:01 +0000 Subject: Merged revisions 72612 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72612 | tarek.ziade | 2009-05-14 00:16:03 +0200 (Thu, 14 May 2009) | 1 line adding void to the c function ........ --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 20f05539..cf254ac1 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -233,7 +233,7 @@ class BuildExtTestCase(support.TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void initfoo() {};\n') + self.write_file(c_file, 'void initfoo(void) {};\n') ext = Extension('foo', [c_file]) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From 9cc675e40154417eefcd9ccc24d5284d37969387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 13 May 2009 22:20:49 +0000 Subject: Merged revisions 72610,72612 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72610 | tarek.ziade | 2009-05-13 23:30:06 +0200 (Wed, 13 May 2009) | 1 line added an inifoo in the C file, to avoid a warning by the MSVC9 linker ........ r72612 | tarek.ziade | 2009-05-14 00:16:03 +0200 (Thu, 14 May 2009) | 1 line adding void to the c function ........ --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 86064fcc..64ca6505 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -296,7 +296,7 @@ class BuildExtTestCase(TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, '') + self.write_file(c_file, 'void initfoo(void) {};\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From 1a7f413db150145be8fe3964d491c9ae9946be8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 14 May 2009 12:40:59 +0000 Subject: more test coverage for distutils sdist command --- command/sdist.py | 7 +++---- tests/test_sdist.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index a9ce28a7..f77f52c1 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -16,19 +16,18 @@ from distutils.filelist import FileList from distutils import log from distutils.util import convert_path -def show_formats (): +def show_formats(): """Print all possible values for the 'formats' option (used by the "--help-formats" command-line option). """ from distutils.fancy_getopt import FancyGetopt from distutils.archive_util import ARCHIVE_FORMATS - formats=[] + formats = [] for format in ARCHIVE_FORMATS.keys(): formats.append(("formats=" + format, None, ARCHIVE_FORMATS[format][2])) formats.sort() - pretty_printer = FancyGetopt(formats) - pretty_printer.print_help( + FancyGetopt(formats).print_help( "List of available source distribution formats:") class sdist (Command): diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 8af080f9..551357ed 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -7,12 +7,16 @@ from os.path import join import sys import tempfile +from test.test_support import captured_stdout + from distutils.command.sdist import sdist +from distutils.command.sdist import show_formats from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError from distutils.spawn import find_executable from distutils.tests import support +from distutils.archive_util import ARCHIVE_FORMATS SETUP_PY = """ from distutils.core import setup @@ -210,6 +214,16 @@ class sdistTestCase(PyPIRCCommandTestCase): manifest = open(join(self.tmp_dir, 'MANIFEST')).read() self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + def test_show_formats(self): + with captured_stdout() as stdout: + show_formats() + + # the output should be a header line + one line per format + num_formats = len(ARCHIVE_FORMATS.keys()) + output = [line for line in stdout.getvalue().split('\n') + if line.strip().startswith('--formats=')] + self.assertEquals(len(output), num_formats) + def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From 8b08ff04c6c2e891754498db8908825b6c79444c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 14 May 2009 12:45:48 +0000 Subject: Merged revisions 72618 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72618 | tarek.ziade | 2009-05-14 14:40:59 +0200 (Thu, 14 May 2009) | 1 line more test coverage for distutils sdist command ........ --- command/sdist.py | 7 +++---- tests/test_sdist.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 1a64d0e3..836b9620 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -17,19 +17,18 @@ from distutils.filelist import FileList from distutils import log from distutils.util import convert_path -def show_formats (): +def show_formats(): """Print all possible values for the 'formats' option (used by the "--help-formats" command-line option). """ from distutils.fancy_getopt import FancyGetopt from distutils.archive_util import ARCHIVE_FORMATS - formats=[] + formats = [] for format in ARCHIVE_FORMATS.keys(): formats.append(("formats=" + format, None, ARCHIVE_FORMATS[format][2])) formats.sort() - pretty_printer = FancyGetopt(formats) - pretty_printer.print_help( + FancyGetopt(formats).print_help( "List of available source distribution formats:") class sdist (Command): diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 8af080f9..ec95ffb3 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -7,12 +7,16 @@ from os.path import join import sys import tempfile +from test.support import captured_stdout + from distutils.command.sdist import sdist +from distutils.command.sdist import show_formats from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError from distutils.spawn import find_executable from distutils.tests import support +from distutils.archive_util import ARCHIVE_FORMATS SETUP_PY = """ from distutils.core import setup @@ -210,6 +214,16 @@ class sdistTestCase(PyPIRCCommandTestCase): manifest = open(join(self.tmp_dir, 'MANIFEST')).read() self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + def test_show_formats(self): + with captured_stdout() as stdout: + show_formats() + + # the output should be a header line + one line per format + num_formats = len(ARCHIVE_FORMATS.keys()) + output = [line for line in stdout.getvalue().split('\n') + if line.strip().startswith('--formats=')] + self.assertEquals(len(output), num_formats) + def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From 6d9fe7ee5fca5acd065e820fb6ccb5ce3f665852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 14 May 2009 14:56:14 +0000 Subject: pep8-fied distutils.command.sdist + more tests --- command/sdist.py | 59 +++++++++++++++-------------------------------------- tests/test_sdist.py | 24 +++++++++++++++++++++- 2 files changed, 39 insertions(+), 44 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index f77f52c1..ab0ada1f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -30,7 +30,7 @@ def show_formats(): FancyGetopt(formats).print_help( "List of available source distribution formats:") -class sdist (Command): +class sdist(Command): description = "create a source distribution (tarball, zip file, etc.)" @@ -77,10 +77,10 @@ class sdist (Command): negative_opt = {'no-defaults': 'use-defaults', 'no-prune': 'prune' } - default_format = { 'posix': 'gztar', - 'nt': 'zip' } + default_format = {'posix': 'gztar', + 'nt': 'zip' } - def initialize_options (self): + def initialize_options(self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. self.template = None @@ -100,8 +100,7 @@ class sdist (Command): self.archive_files = None - - def finalize_options (self): + def finalize_options(self): if self.manifest is None: self.manifest = "MANIFEST" if self.template is None: @@ -124,9 +123,7 @@ class sdist (Command): if self.dist_dir is None: self.dist_dir = "dist" - - def run (self): - + def run(self): # 'filelist' contains the list of files that will make up the # manifest self.filelist = FileList() @@ -148,8 +145,7 @@ class sdist (Command): # or zipfile, or whatever. self.make_distribution() - - def check_metadata (self): + def check_metadata(self): """Ensure that all required elements of meta-data (name, version, URL, (author and author_email) or (maintainer and maintainer_email)) are supplied by the Distribution object; warn if @@ -179,17 +175,13 @@ class sdist (Command): "or (maintainer and maintainer_email) " + "must be supplied") - # check_metadata () - - - def get_file_list (self): + def get_file_list(self): """Figure out the list of files to include in the source distribution, and put it in 'self.filelist'. This might involve reading the manifest template (and writing the manifest), or just reading the manifest, or just using the default file set -- it all depends on the user's options and the state of the filesystem. """ - # If we have a manifest template, see if it's newer than the # manifest; if so, we'll regenerate the manifest. template_exists = os.path.isfile(self.template) @@ -249,10 +241,7 @@ class sdist (Command): else: self.read_manifest() - # get_file_list () - - - def add_defaults (self): + def add_defaults(self): """Add all the default files to self.filelist: - README or README.txt - setup.py @@ -334,10 +323,7 @@ class sdist (Command): build_scripts = self.get_finalized_command('build_scripts') self.filelist.extend(build_scripts.get_source_files()) - # add_defaults () - - - def read_template (self): + def read_template(self): """Read and parse manifest template file named by self.template. (usually "MANIFEST.in") The parsing and processing is done by @@ -364,10 +350,7 @@ class sdist (Command): template.current_line, msg)) - # read_template () - - - def prune_file_list (self): + def prune_file_list(self): """Prune off branches that might slip into the file list as created by 'read_template()', but really don't belong there: * the build tree (typically "build") @@ -393,7 +376,7 @@ class sdist (Command): vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) - def write_manifest (self): + def write_manifest(self): """Write the file list in 'self.filelist' (presumably as filled in by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. @@ -402,10 +385,7 @@ class sdist (Command): (self.manifest, self.filelist.files), "writing manifest file '%s'" % self.manifest) - # write_manifest () - - - def read_manifest (self): + def read_manifest(self): """Read the manifest file (named by 'self.manifest') and use it to fill in 'self.filelist', the list of files to include in the source distribution. @@ -421,10 +401,7 @@ class sdist (Command): self.filelist.append(line) manifest.close() - # read_manifest () - - - def make_release_tree (self, base_dir, files): + def make_release_tree(self, base_dir, files): """Create the directory tree that will become the source distribution archive. All directories implied by the filenames in 'files' are created under 'base_dir', and then we hard link or copy @@ -466,9 +443,7 @@ class sdist (Command): self.distribution.metadata.write_pkg_info(base_dir) - # make_release_tree () - - def make_distribution (self): + def make_distribution(self): """Create the source distribution(s). First, we create the release tree with 'make_release_tree()'; then, we create all required archive files (according to 'self.formats') from the release tree. @@ -497,10 +472,8 @@ class sdist (Command): if not self.keep_temp: dir_util.remove_tree(base_dir, dry_run=self.dry_run) - def get_archive_files (self): + def get_archive_files(self): """Return the list of archive files created when the command was run, or None if the command hasn't run yet. """ return self.archive_files - -# class sdist diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 551357ed..4f3d4bb9 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -13,7 +13,7 @@ from distutils.command.sdist import sdist from distutils.command.sdist import show_formats from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase -from distutils.errors import DistutilsExecError +from distutils.errors import DistutilsExecError, DistutilsOptionError from distutils.spawn import find_executable from distutils.tests import support from distutils.archive_util import ARCHIVE_FORMATS @@ -224,6 +224,28 @@ class sdistTestCase(PyPIRCCommandTestCase): if line.strip().startswith('--formats=')] self.assertEquals(len(output), num_formats) + def test_finalize_options(self): + + dist, cmd = self.get_cmd() + cmd.finalize_options() + + # default options set by finalize + self.assertEquals(cmd.manifest, 'MANIFEST') + self.assertEquals(cmd.template, 'MANIFEST.in') + self.assertEquals(cmd.dist_dir, 'dist') + + # formats has to be a string splitable on (' ', ',') or + # a stringlist + cmd.formats = 1 + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + cmd.formats = ['zip'] + cmd.finalize_options() + + # formats has to be known + cmd.formats = 'supazipa' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From 31ada7d3a3d7f87bec35033113c5a8fda4c93fb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 14 May 2009 15:21:26 +0000 Subject: Merged revisions 72624 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72624 | tarek.ziade | 2009-05-14 16:56:14 +0200 (Thu, 14 May 2009) | 1 line pep8-fied distutils.command.sdist + more tests ........ --- command/sdist.py | 11 ++++------- tests/test_sdist.py | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 836b9620..cbb77c21 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -31,7 +31,7 @@ def show_formats(): FancyGetopt(formats).print_help( "List of available source distribution formats:") -class sdist (Command): +class sdist(Command): description = "create a source distribution (tarball, zip file, etc.)" @@ -78,8 +78,8 @@ class sdist (Command): negative_opt = {'no-defaults': 'use-defaults', 'no-prune': 'prune' } - default_format = { 'posix': 'gztar', - 'nt': 'zip' } + default_format = {'posix': 'gztar', + 'nt': 'zip' } def initialize_options(self): # 'template' and 'manifest' are, respectively, the names of @@ -101,7 +101,6 @@ class sdist (Command): self.archive_files = None - def finalize_options(self): if self.manifest is None: self.manifest = "MANIFEST" @@ -125,7 +124,6 @@ class sdist (Command): if self.dist_dir is None: self.dist_dir = "dist" - def run(self): # 'filelist' contains the list of files that will make up the # manifest @@ -244,7 +242,6 @@ class sdist (Command): else: self.read_manifest() - def add_defaults(self): """Add all the default files to self.filelist: - README or README.txt @@ -373,7 +370,7 @@ class sdist (Command): vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) - def write_manifest (self): + def write_manifest(self): """Write the file list in 'self.filelist' (presumably as filled in by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. diff --git a/tests/test_sdist.py b/tests/test_sdist.py index ec95ffb3..9e279337 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -13,7 +13,7 @@ from distutils.command.sdist import sdist from distutils.command.sdist import show_formats from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase -from distutils.errors import DistutilsExecError +from distutils.errors import DistutilsExecError, DistutilsOptionError from distutils.spawn import find_executable from distutils.tests import support from distutils.archive_util import ARCHIVE_FORMATS @@ -224,6 +224,28 @@ class sdistTestCase(PyPIRCCommandTestCase): if line.strip().startswith('--formats=')] self.assertEquals(len(output), num_formats) + def test_finalize_options(self): + + dist, cmd = self.get_cmd() + cmd.finalize_options() + + # default options set by finalize + self.assertEquals(cmd.manifest, 'MANIFEST') + self.assertEquals(cmd.template, 'MANIFEST.in') + self.assertEquals(cmd.dist_dir, 'dist') + + # formats has to be a string splitable on (' ', ',') or + # a stringlist + cmd.formats = 1 + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + cmd.formats = ['zip'] + cmd.finalize_options() + + # formats has to be known + cmd.formats = 'supazipa' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From 904218f8a1f5be2a86fefee8fa68d19a640ba166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 14 May 2009 20:14:13 +0000 Subject: #6022 fixed test_get_outputs so it doesn't leaves a test file in the cwd --- tests/test_build_ext.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 45c5b39e..c6533ca9 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -312,12 +312,18 @@ class BuildExtTestCase(support.TempdirManager, # issue #5977 : distutils build_ext.get_outputs # returns wrong result with --inplace - cmd.inplace = 1 - cmd.run() - so_file = cmd.get_outputs()[0] + other_tmp_dir = os.path.realpath(self.mkdtemp()) + old_wd = os.getcwd() + os.chdir(other_tmp_dir) + try: + cmd.inplace = 1 + cmd.run() + so_file = cmd.get_outputs()[0] + finally: + os.chdir(old_wd) self.assert_(os.path.exists(so_file)) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, os.getcwd()) + self.assertEquals(so_dir, other_tmp_dir) cmd.inplace = 0 cmd.run() -- cgit v1.2.1 From edb14dc6af62abfb53b444fab52b38a1e5366acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 14 May 2009 20:17:32 +0000 Subject: Merged revisions 72636 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72636 | tarek.ziade | 2009-05-14 22:14:13 +0200 (Thu, 14 May 2009) | 1 line #6022 fixed test_get_outputs so it doesn't leaves a test file in the cwd ........ --- tests/test_build_ext.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index cf254ac1..2bd2292e 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -249,12 +249,18 @@ class BuildExtTestCase(support.TempdirManager, # issue #5977 : distutils build_ext.get_outputs # returns wrong result with --inplace - cmd.inplace = 1 - cmd.run() - so_file = cmd.get_outputs()[0] + other_tmp_dir = os.path.realpath(self.mkdtemp()) + old_wd = os.getcwd() + os.chdir(other_tmp_dir) + try: + cmd.inplace = 1 + cmd.run() + so_file = cmd.get_outputs()[0] + finally: + os.chdir(old_wd) self.assert_(os.path.exists(so_file)) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, os.getcwd()) + self.assertEquals(so_dir, other_tmp_dir) cmd.inplace = 0 cmd.run() -- cgit v1.2.1 From 15604e839e578d0b409d98f280e763621d33edb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 14 May 2009 20:20:47 +0000 Subject: Merged revisions 72636 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72636 | tarek.ziade | 2009-05-14 22:14:13 +0200 (Thu, 14 May 2009) | 1 line #6022 fixed test_get_outputs so it doesn't leaves a test file in the cwd ........ --- tests/test_build_ext.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 64ca6505..6a463c01 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -312,12 +312,18 @@ class BuildExtTestCase(TempdirManager, # issue #5977 : distutils build_ext.get_outputs # returns wrong result with --inplace - cmd.inplace = 1 - cmd.run() - so_file = cmd.get_outputs()[0] + other_tmp_dir = os.path.realpath(self.mkdtemp()) + old_wd = os.getcwd() + os.chdir(other_tmp_dir) + try: + cmd.inplace = 1 + cmd.run() + so_file = cmd.get_outputs()[0] + finally: + os.chdir(old_wd) self.assert_(os.path.exists(so_file)) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, os.getcwd()) + self.assertEquals(so_dir, other_tmp_dir) cmd.inplace = 0 cmd.run() -- cgit v1.2.1 From 6f1556e53cd1edc7db121e1c0bafa91e061ca989 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 15 May 2009 17:27:30 +0000 Subject: Fix bootstrapping by removing uses of the copy module in distutils --- ccompiler.py | 11 +++++------ dist.py | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index a56a0388..5a9ff76d 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -7,7 +7,6 @@ __revision__ = "$Id$" import sys, os, re from types import * -from copy import copy from distutils.errors import * from distutils.spawn import spawn from distutils.file_util import move_file @@ -253,7 +252,7 @@ class CCompiler: any list of standard include directories that the compiler may search by default. """ - self.include_dirs = copy (dirs) + self.include_dirs = dirs[:] def add_library (self, libname): @@ -278,7 +277,7 @@ class CCompiler: not affect any standard system libraries that the linker may include by default. """ - self.libraries = copy (libnames) + self.libraries = libnames[:] def add_library_dir (self, dir): @@ -294,7 +293,7 @@ class CCompiler: strings). This does not affect any standard library search path that the linker may search by default. """ - self.library_dirs = copy (dirs) + self.library_dirs = dirs[:] def add_runtime_library_dir (self, dir): @@ -309,7 +308,7 @@ class CCompiler: standard search path that the runtime linker may search by default. """ - self.runtime_library_dirs = copy (dirs) + self.runtime_library_dirs = dirs[:] def add_link_object (self, object): @@ -326,7 +325,7 @@ class CCompiler: files that the linker may include by default (such as system libraries). """ - self.objects = copy (objects) + self.objects = objects[:] # -- Private utility methods -------------------------------------- diff --git a/dist.py b/dist.py index 2d57ad09..5fe262b1 100644 --- a/dist.py +++ b/dist.py @@ -8,7 +8,6 @@ __revision__ = "$Id$" import sys, os, string, re from types import * -from copy import copy try: import warnings @@ -537,7 +536,7 @@ Common commands: (see '--help-commands' for more) # merge it in with the global negative aliases. negative_opt = self.negative_opt if hasattr(cmd_class, 'negative_opt'): - negative_opt = copy(negative_opt) + negative_opt = negative_opt.copy() negative_opt.update(cmd_class.negative_opt) # Check for help_options in command class. They have a different -- cgit v1.2.1 From fb1d44bd30704af391fa97fa93b7350a28b16826 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 15 May 2009 17:34:21 +0000 Subject: Merged revisions 72671 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72671 | antoine.pitrou | 2009-05-15 19:27:30 +0200 (ven., 15 mai 2009) | 3 lines Fix bootstrapping by removing uses of the copy module in distutils ........ --- ccompiler.py | 11 +++++------ dist.py | 3 +-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index cad663a3..875f96fa 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -6,7 +6,6 @@ for the Distutils compiler abstraction model.""" __revision__ = "$Id$" import sys, os, re -from copy import copy from distutils.errors import * from distutils.spawn import spawn from distutils.file_util import move_file @@ -233,7 +232,7 @@ class CCompiler: any list of standard include directories that the compiler may search by default. """ - self.include_dirs = copy(dirs) + self.include_dirs = dirs[:] def add_library(self, libname): """Add 'libname' to the list of libraries that will be included in @@ -257,7 +256,7 @@ class CCompiler: not affect any standard system libraries that the linker may include by default. """ - self.libraries = copy(libnames) + self.libraries = libnames[:] def add_library_dir(self, dir): """Add 'dir' to the list of directories that will be searched for @@ -272,7 +271,7 @@ class CCompiler: strings). This does not affect any standard library search path that the linker may search by default. """ - self.library_dirs = copy(dirs) + self.library_dirs = dirs[:] def add_runtime_library_dir(self, dir): """Add 'dir' to the list of directories that will be searched for @@ -286,7 +285,7 @@ class CCompiler: standard search path that the runtime linker may search by default. """ - self.runtime_library_dirs = copy(dirs) + self.runtime_library_dirs = dirs[:] def add_link_object(self, object): """Add 'object' to the list of object files (or analogues, such as @@ -302,7 +301,7 @@ class CCompiler: files that the linker may include by default (such as system libraries). """ - self.objects = copy(objects) + self.objects = objects[:] # -- Private utility methods -------------------------------------- diff --git a/dist.py b/dist.py index 7c30e885..91adc4d3 100644 --- a/dist.py +++ b/dist.py @@ -7,7 +7,6 @@ being built/installed/distributed. __revision__ = "$Id$" import sys, os, re -from copy import copy try: import warnings @@ -521,7 +520,7 @@ Common commands: (see '--help-commands' for more) # merge it in with the global negative aliases. negative_opt = self.negative_opt if hasattr(cmd_class, 'negative_opt'): - negative_opt = copy(negative_opt) + negative_opt = negative_opt.copy() negative_opt.update(cmd_class.negative_opt) # Check for help_options in command class. They have a different -- cgit v1.2.1 From ae85790799ba86885d7aa8ae623b2584c218dddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 16 May 2009 16:37:06 +0000 Subject: #6041: sdist and register now use the check command. No more duplicate code for metadata checking --- command/register.py | 57 +++++++++++++++++------------------- command/sdist.py | 56 +++++++++++++++-------------------- tests/support.py | 20 +++++++++++++ tests/test_register.py | 79 ++++++++++++++++++++++++++++++++++++++++++++++---- tests/test_sdist.py | 39 ++++++++++++++++++++++--- 5 files changed, 179 insertions(+), 72 deletions(-) diff --git a/command/register.py b/command/register.py index f3463dc2..3b5b2080 100644 --- a/command/register.py +++ b/command/register.py @@ -9,6 +9,7 @@ __revision__ = "$Id$" import os, string, urllib2, getpass, urlparse import StringIO +from warnings import warn from distutils.core import PyPIRCCommand from distutils.errors import * @@ -20,18 +21,34 @@ class register(PyPIRCCommand): user_options = PyPIRCCommand.user_options + [ ('list-classifiers', None, 'list the valid Trove classifiers'), + ('strict', None , + 'Will stop the registering if the meta-data are not fully compliant') ] boolean_options = PyPIRCCommand.boolean_options + [ - 'verify', 'list-classifiers'] + 'verify', 'list-classifiers', 'strict'] + + sub_commands = [('check', lambda self: True)] def initialize_options(self): PyPIRCCommand.initialize_options(self) self.list_classifiers = 0 + self.strict = 0 + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + # setting options for the `check` subcommand + check_options = {'strict': ('register', self.strict), + 'restructuredtext': ('register', 1)} + self.distribution.command_options['check'] = check_options def run(self): self.finalize_options() self._set_config() - self.check_metadata() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + if self.dry_run: self.verify_metadata() elif self.list_classifiers: @@ -40,34 +57,14 @@ class register(PyPIRCCommand): self.send_metadata() def check_metadata(self): - """Ensure that all required elements of meta-data (name, version, - URL, (author and author_email) or (maintainer and - maintainer_email)) are supplied by the Distribution object; warn if - any are missing. - """ - metadata = self.distribution.metadata - - missing = [] - for attr in ('name', 'version', 'url'): - if not (hasattr(metadata, attr) and getattr(metadata, attr)): - missing.append(attr) - - if missing: - self.warn("missing required meta-data: " + - string.join(missing, ", ")) - - if metadata.author: - if not metadata.author_email: - self.warn("missing meta-data: if 'author' supplied, " + - "'author_email' must be supplied too") - elif metadata.maintainer: - if not metadata.maintainer_email: - self.warn("missing meta-data: if 'maintainer' supplied, " + - "'maintainer_email' must be supplied too") - else: - self.warn("missing meta-data: either (author and author_email) " + - "or (maintainer and maintainer_email) " + - "must be supplied") + """Deprecated API.""" + warn("distutils.command.register.check_metadata is deprecated, \ + use the check command instead", PendingDeprecationWarning) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.strict = self.strict + check.restructuredtext = 1 + check.run() def _set_config(self): ''' Reads the configuration file and set attributes. diff --git a/command/sdist.py b/command/sdist.py index ab0ada1f..c21f3917 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -8,6 +8,8 @@ import os, string import sys from types import * from glob import glob +from warnings import warn + from distutils.core import Command from distutils import dir_util, dep_util, file_util, archive_util from distutils.text_file import TextFile @@ -34,6 +36,12 @@ class sdist(Command): description = "create a source distribution (tarball, zip file, etc.)" + def checking_metadata(self): + """Callable used for the check sub-command. + + Placed here so user_options can view it""" + return self.metadata_check + user_options = [ ('template=', 't', "name of manifest template file [default: MANIFEST.in]"), @@ -63,11 +71,14 @@ class sdist(Command): ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), + ('medata-check', None, + "Ensure that all required elements of meta-data " + "are supplied. Warn if any missing. [default]"), ] boolean_options = ['use-defaults', 'prune', 'manifest-only', 'force-manifest', - 'keep-temp'] + 'keep-temp', 'metadata-check'] help_options = [ ('help-formats', None, @@ -80,6 +91,8 @@ class sdist(Command): default_format = {'posix': 'gztar', 'nt': 'zip' } + sub_commands = [('check', checking_metadata)] + def initialize_options(self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. @@ -99,6 +112,7 @@ class sdist(Command): self.dist_dir = None self.archive_files = None + self.metadata_check = 1 def finalize_options(self): if self.manifest is None: @@ -128,9 +142,9 @@ class sdist(Command): # manifest self.filelist = FileList() - # Ensure that all required meta-data is given; warn if not (but - # don't die, it's not *that* serious!) - self.check_metadata() + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) # Do whatever it takes to get the list of files to process # (process the manifest template, read an existing manifest, @@ -146,34 +160,12 @@ class sdist(Command): self.make_distribution() def check_metadata(self): - """Ensure that all required elements of meta-data (name, version, - URL, (author and author_email) or (maintainer and - maintainer_email)) are supplied by the Distribution object; warn if - any are missing. - """ - metadata = self.distribution.metadata - - missing = [] - for attr in ('name', 'version', 'url'): - if not (hasattr(metadata, attr) and getattr(metadata, attr)): - missing.append(attr) - - if missing: - self.warn("missing required meta-data: " + - string.join(missing, ", ")) - - if metadata.author: - if not metadata.author_email: - self.warn("missing meta-data: if 'author' supplied, " + - "'author_email' must be supplied too") - elif metadata.maintainer: - if not metadata.maintainer_email: - self.warn("missing meta-data: if 'maintainer' supplied, " + - "'maintainer_email' must be supplied too") - else: - self.warn("missing meta-data: either (author and author_email) " + - "or (maintainer and maintainer_email) " + - "must be supplied") + """Deprecated API.""" + warn("distutils.command.sdist.check_metadata is deprecated, \ + use the check command instead", PendingDeprecationWarning) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.run() def get_file_list(self): """Figure out the list of files to include in the source diff --git a/tests/support.py b/tests/support.py index 668edf9f..bce03494 100644 --- a/tests/support.py +++ b/tests/support.py @@ -12,11 +12,31 @@ class LoggingSilencer(object): def setUp(self): super(LoggingSilencer, self).setUp() self.threshold = log.set_threshold(log.FATAL) + # catching warnings + # when log will be replaced by logging + # we won't need such monkey-patch anymore + self._old_log = log.Log._log + log.Log._log = self._log + self.logs = [] def tearDown(self): log.set_threshold(self.threshold) + log.Log._log = self._old_log super(LoggingSilencer, self).tearDown() + def _log(self, level, msg, args): + self.logs.append((level, msg, args)) + + def get_logs(self, *levels): + def _format(msg, args): + if len(args) == 0: + return msg + return msg % args + return [_format(msg, args) for level, msg, args + in self.logs if level in levels] + + def clear_logs(self): + self.logs = [] class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. diff --git a/tests/test_register.py b/tests/test_register.py index 7da8edb5..91d35813 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -4,10 +4,14 @@ import os import unittest import getpass import urllib2 +import warnings + +from test.test_support import check_warnings from distutils.command import register as register_module from distutils.command.register import register from distutils.core import Distribution +from distutils.errors import DistutilsSetupError from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase @@ -59,7 +63,7 @@ class FakeOpener(object): def read(self): return 'xxx' -class registerTestCase(PyPIRCCommandTestCase): +class RegisterTestCase(PyPIRCCommandTestCase): def setUp(self): PyPIRCCommandTestCase.setUp(self) @@ -76,10 +80,11 @@ class registerTestCase(PyPIRCCommandTestCase): urllib2.build_opener = self.old_opener PyPIRCCommandTestCase.tearDown(self) - def _get_cmd(self): - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', - 'name': 'xxx', 'version': 'xxx'} + def _get_cmd(self, metadata=None): + if metadata is None: + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx'} pkg_info, dist = self.create_dist(**metadata) return register(dist) @@ -184,8 +189,70 @@ class registerTestCase(PyPIRCCommandTestCase): self.assertEquals(headers['Content-length'], '290') self.assert_('tarek' in req.data) + def test_strict(self): + # testing the script option + # when on, the register command stops if + # the metadata is incomplete or if + # long_description is not reSt compliant + + # empty metadata + cmd = self._get_cmd({}) + cmd.ensure_finalized() + cmd.strict = 1 + self.assertRaises(DistutilsSetupError, cmd.run) + + # we don't test the reSt feature if docutils + # is not installed + try: + import docutils + except ImportError: + return + + # metadata are OK but long_description is broken + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx', + 'long_description': 'title\n==\n\ntext'} + + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + self.assertRaises(DistutilsSetupError, cmd.run) + + # now something that works + metadata['long_description'] = 'title\n=====\n\ntext' + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + inputs = RawInputs('1', 'tarek', 'y') + register_module.raw_input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.raw_input + + # strict is not by default + cmd = self._get_cmd() + cmd.ensure_finalized() + inputs = RawInputs('1', 'tarek', 'y') + register_module.raw_input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.raw_input + + def test_check_metadata_deprecated(self): + # makes sure make_metadata is deprecated + cmd = self._get_cmd() + with check_warnings() as w: + warnings.simplefilter("always") + cmd.check_metadata() + self.assertEquals(len(w.warnings), 1) + def test_suite(): - return unittest.makeSuite(registerTestCase) + return unittest.makeSuite(RegisterTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 4f3d4bb9..5808ca16 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -6,7 +6,9 @@ import zipfile from os.path import join import sys import tempfile +import warnings +from test.test_support import check_warnings from test.test_support import captured_stdout from distutils.command.sdist import sdist @@ -16,6 +18,7 @@ from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError, DistutilsOptionError from distutils.spawn import find_executable from distutils.tests import support +from distutils.log import WARN from distutils.archive_util import ARCHIVE_FORMATS SETUP_PY = """ @@ -38,12 +41,12 @@ somecode%(sep)sdoc.dat somecode%(sep)sdoc.txt """ -class sdistTestCase(PyPIRCCommandTestCase): +class SDistTestCase(PyPIRCCommandTestCase): def setUp(self): # PyPIRCCommandTestCase creates a temp dir already # and put it in self.tmp_dir - super(sdistTestCase, self).setUp() + super(SDistTestCase, self).setUp() # setting up an environment self.old_path = os.getcwd() os.mkdir(join(self.tmp_dir, 'somecode')) @@ -57,7 +60,7 @@ class sdistTestCase(PyPIRCCommandTestCase): def tearDown(self): # back to normal os.chdir(self.old_path) - super(sdistTestCase, self).tearDown() + super(SDistTestCase, self).tearDown() def get_cmd(self, metadata=None): """Returns a cmd""" @@ -214,6 +217,34 @@ class sdistTestCase(PyPIRCCommandTestCase): manifest = open(join(self.tmp_dir, 'MANIFEST')).read() self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + def test_metadata_check_option(self): + # testing the `medata-check` option + dist, cmd = self.get_cmd(metadata={}) + + # this should raise some warnings ! + # with the `check` subcommand + cmd.ensure_finalized() + cmd.run() + warnings = self.get_logs(WARN) + self.assertEquals(len(warnings), 2) + + # trying with a complete set of metadata + self.clear_logs() + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + cmd.metadata_check = 0 + cmd.run() + warnings = self.get_logs(WARN) + self.assertEquals(len(warnings), 0) + + def test_check_metadata_deprecated(self): + # makes sure make_metadata is deprecated + dist, cmd = self.get_cmd() + with check_warnings() as w: + warnings.simplefilter("always") + cmd.check_metadata() + self.assertEquals(len(w.warnings), 1) + def test_show_formats(self): with captured_stdout() as stdout: show_formats() @@ -247,7 +278,7 @@ class sdistTestCase(PyPIRCCommandTestCase): def test_suite(): - return unittest.makeSuite(sdistTestCase) + return unittest.makeSuite(SDistTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 86a2297c6222d8f368ac05f7693aa32b526aae5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 16 May 2009 16:52:13 +0000 Subject: Merged revisions 72681 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72681 | tarek.ziade | 2009-05-16 18:37:06 +0200 (Sat, 16 May 2009) | 1 line #6041: sdist and register now use the check command. No more duplicate code for metadata checking ........ --- command/register.py | 57 +++++++++++++++++------------------- command/sdist.py | 56 +++++++++++++++-------------------- tests/support.py | 20 +++++++++++++ tests/test_register.py | 79 ++++++++++++++++++++++++++++++++++++++++++++++---- tests/test_sdist.py | 39 ++++++++++++++++++++++--- 5 files changed, 179 insertions(+), 72 deletions(-) diff --git a/command/register.py b/command/register.py index 7dd30999..bdf5f8f0 100644 --- a/command/register.py +++ b/command/register.py @@ -10,6 +10,7 @@ __revision__ = "$Id$" import os, string, getpass import io import urllib.parse, urllib.request +from warnings import warn from distutils.core import PyPIRCCommand from distutils.errors import * @@ -21,18 +22,34 @@ class register(PyPIRCCommand): user_options = PyPIRCCommand.user_options + [ ('list-classifiers', None, 'list the valid Trove classifiers'), + ('strict', None , + 'Will stop the registering if the meta-data are not fully compliant') ] boolean_options = PyPIRCCommand.boolean_options + [ - 'verify', 'list-classifiers'] + 'verify', 'list-classifiers', 'strict'] + + sub_commands = [('check', lambda self: True)] def initialize_options(self): PyPIRCCommand.initialize_options(self) self.list_classifiers = 0 + self.strict = 0 + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + # setting options for the `check` subcommand + check_options = {'strict': ('register', self.strict), + 'restructuredtext': ('register', 1)} + self.distribution.command_options['check'] = check_options def run(self): self.finalize_options() self._set_config() - self.check_metadata() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + if self.dry_run: self.verify_metadata() elif self.list_classifiers: @@ -41,34 +58,14 @@ class register(PyPIRCCommand): self.send_metadata() def check_metadata(self): - """Ensure that all required elements of meta-data (name, version, - URL, (author and author_email) or (maintainer and - maintainer_email)) are supplied by the Distribution object; warn if - any are missing. - """ - metadata = self.distribution.metadata - - missing = [] - for attr in ('name', 'version', 'url'): - if not (hasattr(metadata, attr) and getattr(metadata, attr)): - missing.append(attr) - - if missing: - self.warn("missing required meta-data: " + - ", ".join(missing)) - - if metadata.author: - if not metadata.author_email: - self.warn("missing meta-data: if 'author' supplied, " + - "'author_email' must be supplied too") - elif metadata.maintainer: - if not metadata.maintainer_email: - self.warn("missing meta-data: if 'maintainer' supplied, " + - "'maintainer_email' must be supplied too") - else: - self.warn("missing meta-data: either (author and author_email) " + - "or (maintainer and maintainer_email) " + - "must be supplied") + """Deprecated API.""" + warn("distutils.command.register.check_metadata is deprecated, \ + use the check command instead", PendingDeprecationWarning) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.strict = self.strict + check.restructuredtext = 1 + check.run() def _set_config(self): ''' Reads the configuration file and set attributes. diff --git a/command/sdist.py b/command/sdist.py index cbb77c21..ace9eeeb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -9,6 +9,8 @@ import string import sys from types import * from glob import glob +from warnings import warn + from distutils.core import Command from distutils import dir_util, dep_util, file_util, archive_util from distutils.text_file import TextFile @@ -35,6 +37,12 @@ class sdist(Command): description = "create a source distribution (tarball, zip file, etc.)" + def checking_metadata(self): + """Callable used for the check sub-command. + + Placed here so user_options can view it""" + return self.metadata_check + user_options = [ ('template=', 't', "name of manifest template file [default: MANIFEST.in]"), @@ -64,11 +72,14 @@ class sdist(Command): ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), + ('medata-check', None, + "Ensure that all required elements of meta-data " + "are supplied. Warn if any missing. [default]"), ] boolean_options = ['use-defaults', 'prune', 'manifest-only', 'force-manifest', - 'keep-temp'] + 'keep-temp', 'metadata-check'] help_options = [ ('help-formats', None, @@ -81,6 +92,8 @@ class sdist(Command): default_format = {'posix': 'gztar', 'nt': 'zip' } + sub_commands = [('check', checking_metadata)] + def initialize_options(self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. @@ -100,6 +113,7 @@ class sdist(Command): self.dist_dir = None self.archive_files = None + self.metadata_check = 1 def finalize_options(self): if self.manifest is None: @@ -129,9 +143,9 @@ class sdist(Command): # manifest self.filelist = FileList() - # Ensure that all required meta-data is given; warn if not (but - # don't die, it's not *that* serious!) - self.check_metadata() + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) # Do whatever it takes to get the list of files to process # (process the manifest template, read an existing manifest, @@ -147,34 +161,12 @@ class sdist(Command): self.make_distribution() def check_metadata(self): - """Ensure that all required elements of meta-data (name, version, - URL, (author and author_email) or (maintainer and - maintainer_email)) are supplied by the Distribution object; warn if - any are missing. - """ - metadata = self.distribution.metadata - - missing = [] - for attr in ('name', 'version', 'url'): - if not (hasattr(metadata, attr) and getattr(metadata, attr)): - missing.append(attr) - - if missing: - self.warn("missing required meta-data: " + - ", ".join(missing)) - - if metadata.author: - if not metadata.author_email: - self.warn("missing meta-data: if 'author' supplied, " + - "'author_email' must be supplied too") - elif metadata.maintainer: - if not metadata.maintainer_email: - self.warn("missing meta-data: if 'maintainer' supplied, " + - "'maintainer_email' must be supplied too") - else: - self.warn("missing meta-data: either (author and author_email) " + - "or (maintainer and maintainer_email) " + - "must be supplied") + """Deprecated API.""" + warn("distutils.command.sdist.check_metadata is deprecated, \ + use the check command instead", PendingDeprecationWarning) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.run() def get_file_list(self): """Figure out the list of files to include in the source diff --git a/tests/support.py b/tests/support.py index cdcbc378..12554139 100644 --- a/tests/support.py +++ b/tests/support.py @@ -12,11 +12,31 @@ class LoggingSilencer(object): def setUp(self): super().setUp() self.threshold = log.set_threshold(log.FATAL) + # catching warnings + # when log will be replaced by logging + # we won't need such monkey-patch anymore + self._old_log = log.Log._log + log.Log._log = self._log + self.logs = [] def tearDown(self): log.set_threshold(self.threshold) + log.Log._log = self._old_log super().tearDown() + def _log(self, level, msg, args): + self.logs.append((level, msg, args)) + + def get_logs(self, *levels): + def _format(msg, args): + if len(args) == 0: + return msg + return msg % args + return [_format(msg, args) for level, msg, args + in self.logs if level in levels] + + def clear_logs(self): + self.logs = [] class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. diff --git a/tests/test_register.py b/tests/test_register.py index 46f7761d..f486cf79 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -4,10 +4,14 @@ import os import unittest import getpass import urllib +import warnings + +from test.support import check_warnings from distutils.command import register as register_module from distutils.command.register import register from distutils.core import Distribution +from distutils.errors import DistutilsSetupError from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase @@ -59,7 +63,7 @@ class FakeOpener(object): def read(self): return 'xxx' -class registerTestCase(PyPIRCCommandTestCase): +class RegisterTestCase(PyPIRCCommandTestCase): def setUp(self): PyPIRCCommandTestCase.setUp(self) @@ -76,10 +80,11 @@ class registerTestCase(PyPIRCCommandTestCase): urllib.request.build_opener = self.old_opener PyPIRCCommandTestCase.tearDown(self) - def _get_cmd(self): - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', - 'name': 'xxx', 'version': 'xxx'} + def _get_cmd(self, metadata=None): + if metadata is None: + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx'} pkg_info, dist = self.create_dist(**metadata) return register(dist) @@ -184,8 +189,70 @@ class registerTestCase(PyPIRCCommandTestCase): self.assertEquals(headers['Content-length'], '290') self.assert_((b'tarek') in req.data) + def test_strict(self): + # testing the script option + # when on, the register command stops if + # the metadata is incomplete or if + # long_description is not reSt compliant + + # empty metadata + cmd = self._get_cmd({}) + cmd.ensure_finalized() + cmd.strict = 1 + self.assertRaises(DistutilsSetupError, cmd.run) + + # we don't test the reSt feature if docutils + # is not installed + try: + import docutils + except ImportError: + return + + # metadata are OK but long_description is broken + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx', + 'long_description': 'title\n==\n\ntext'} + + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + self.assertRaises(DistutilsSetupError, cmd.run) + + # now something that works + metadata['long_description'] = 'title\n=====\n\ntext' + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + inputs = RawInputs('1', 'tarek', 'y') + register_module.raw_input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.raw_input + + # strict is not by default + cmd = self._get_cmd() + cmd.ensure_finalized() + inputs = RawInputs('1', 'tarek', 'y') + register_module.raw_input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.raw_input + + def test_check_metadata_deprecated(self): + # makes sure make_metadata is deprecated + cmd = self._get_cmd() + with check_warnings() as w: + warnings.simplefilter("always") + cmd.check_metadata() + self.assertEquals(len(w.warnings), 1) + def test_suite(): - return unittest.makeSuite(registerTestCase) + return unittest.makeSuite(RegisterTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 9e279337..b7e5859c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -6,7 +6,9 @@ import zipfile from os.path import join import sys import tempfile +import warnings +from test.support import check_warnings from test.support import captured_stdout from distutils.command.sdist import sdist @@ -16,6 +18,7 @@ from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError, DistutilsOptionError from distutils.spawn import find_executable from distutils.tests import support +from distutils.log import WARN from distutils.archive_util import ARCHIVE_FORMATS SETUP_PY = """ @@ -38,12 +41,12 @@ somecode%(sep)sdoc.dat somecode%(sep)sdoc.txt """ -class sdistTestCase(PyPIRCCommandTestCase): +class SDistTestCase(PyPIRCCommandTestCase): def setUp(self): # PyPIRCCommandTestCase creates a temp dir already # and put it in self.tmp_dir - super(sdistTestCase, self).setUp() + super(SDistTestCase, self).setUp() # setting up an environment self.old_path = os.getcwd() os.mkdir(join(self.tmp_dir, 'somecode')) @@ -57,7 +60,7 @@ class sdistTestCase(PyPIRCCommandTestCase): def tearDown(self): # back to normal os.chdir(self.old_path) - super(sdistTestCase, self).tearDown() + super(SDistTestCase, self).tearDown() def get_cmd(self, metadata=None): """Returns a cmd""" @@ -214,6 +217,34 @@ class sdistTestCase(PyPIRCCommandTestCase): manifest = open(join(self.tmp_dir, 'MANIFEST')).read() self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + def test_metadata_check_option(self): + # testing the `medata-check` option + dist, cmd = self.get_cmd(metadata={}) + + # this should raise some warnings ! + # with the `check` subcommand + cmd.ensure_finalized() + cmd.run() + warnings = self.get_logs(WARN) + self.assertEquals(len(warnings), 2) + + # trying with a complete set of metadata + self.clear_logs() + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + cmd.metadata_check = 0 + cmd.run() + warnings = self.get_logs(WARN) + self.assertEquals(len(warnings), 0) + + def test_check_metadata_deprecated(self): + # makes sure make_metadata is deprecated + dist, cmd = self.get_cmd() + with check_warnings() as w: + warnings.simplefilter("always") + cmd.check_metadata() + self.assertEquals(len(w.warnings), 1) + def test_show_formats(self): with captured_stdout() as stdout: show_formats() @@ -247,7 +278,7 @@ class sdistTestCase(PyPIRCCommandTestCase): def test_suite(): - return unittest.makeSuite(sdistTestCase) + return unittest.makeSuite(SDistTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 2bd69bbfb8b44521f1fda9b56567b1a3400b8cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 16 May 2009 18:29:40 +0000 Subject: pep8-fied distutils.dist module --- dist.py | 130 ++++++++++++++++++---------------------------------------------- 1 file changed, 37 insertions(+), 93 deletions(-) diff --git a/dist.py b/dist.py index 5fe262b1..0c77f85e 100644 --- a/dist.py +++ b/dist.py @@ -267,23 +267,18 @@ Common commands: (see '--help-commands' for more) self.finalize_options() - # __init__ () - - - def get_option_dict (self, command): + def get_option_dict(self, command): """Get the option dictionary for a given command. If that command's option dictionary hasn't been created yet, then create it and return the new dictionary; otherwise, return the existing option dictionary. """ - dict = self.command_options.get(command) if dict is None: dict = self.command_options[command] = {} return dict - - def dump_option_dicts (self, header=None, commands=None, indent=""): + def dump_option_dicts(self, header=None, commands=None, indent=""): from pprint import pformat if commands is None: # dump all command option dicts @@ -308,13 +303,9 @@ Common commands: (see '--help-commands' for more) for line in string.split(out, "\n"): print indent + " " + line - # dump_option_dicts () - - - # -- Config file finding/parsing methods --------------------------- - def find_config_files (self): + def find_config_files(self): """Find as many configuration files as should be processed for this platform, and return a list of filenames in the order in which they should be parsed. The filenames returned are guaranteed to exist @@ -355,10 +346,7 @@ Common commands: (see '--help-commands' for more) return files - # find_config_files () - - - def parse_config_files (self, filenames=None): + def parse_config_files(self, filenames=None): from ConfigParser import ConfigParser if filenames is None: @@ -400,12 +388,9 @@ Common commands: (see '--help-commands' for more) except ValueError, msg: raise DistutilsOptionError, msg - # parse_config_files () - - # -- Command-line parsing methods ---------------------------------- - def parse_command_line (self): + def parse_command_line(self): """Parse the setup script's command line, taken from the 'script_args' instance attribute (which defaults to 'sys.argv[1:]' -- see 'setup()' in core.py). This list is first processed for @@ -478,9 +463,7 @@ Common commands: (see '--help-commands' for more) # All is well: return true return 1 - # parse_command_line() - - def _get_toplevel_options (self): + def _get_toplevel_options(self): """Return the non-display options recognized at the top level. This includes options that are recognized *only* at the top @@ -491,7 +474,7 @@ Common commands: (see '--help-commands' for more) "list of packages that provide distutils commands"), ] - def _parse_command_opts (self, parser, args): + def _parse_command_opts(self, parser, args): """Parse the command-line options for a single command. 'parser' must be a FancyGetopt instance; 'args' must be the list of arguments, starting with the current command (whose options @@ -587,14 +570,11 @@ Common commands: (see '--help-commands' for more) return args - # _parse_command_opts () - - def finalize_options (self): + def finalize_options(self): """Set final values for all the options on the Distribution instance, analogous to the .finalize_options() method of Command objects. """ - keywords = self.metadata.keywords if keywords is not None: if type(keywords) is StringType: @@ -607,11 +587,8 @@ Common commands: (see '--help-commands' for more) platformlist = string.split(platforms, ',') self.metadata.platforms = map(string.strip, platformlist) - def _show_help (self, - parser, - global_options=1, - display_options=1, - commands=[]): + def _show_help(self, parser, global_options=1, display_options=1, + commands=[]): """Show help for the setup script command-line in the form of several lists of command-line options. 'parser' should be a FancyGetopt instance; do not expect it to be returned in the @@ -661,10 +638,7 @@ Common commands: (see '--help-commands' for more) print gen_usage(self.script_name) return - # _show_help () - - - def handle_display_options (self, option_order): + def handle_display_options(self, option_order): """If there were any non-global "display-only" options (--help-commands or the metadata display options) on the command line, display the requested info and return true; else return @@ -704,13 +678,10 @@ Common commands: (see '--help-commands' for more) return any_display_options - # handle_display_options() - - def print_command_list (self, commands, header, max_length): + def print_command_list(self, commands, header, max_length): """Print a subset of the list of all commands -- used by 'print_commands()'. """ - print header + ":" for cmd in commands: @@ -724,10 +695,7 @@ Common commands: (see '--help-commands' for more) print " %-*s %s" % (max_length, cmd, description) - # print_command_list () - - - def print_commands (self): + def print_commands(self): """Print out a help message listing all available commands with a description of each. The list is divided into "standard commands" (listed in distutils.command.__all__) and "extra commands" @@ -735,7 +703,6 @@ Common commands: (see '--help-commands' for more) descriptions come from the command class attribute 'description'. """ - import distutils.command std_commands = distutils.command.__all__ is_std = {} @@ -761,9 +728,7 @@ Common commands: (see '--help-commands' for more) "Extra commands", max_length) - # print_commands () - - def get_command_list (self): + def get_command_list(self): """Get a list of (command, description) tuples. The list is divided into "standard commands" (listed in distutils.command.__all__) and "extra commands" (mentioned in @@ -798,7 +763,7 @@ Common commands: (see '--help-commands' for more) # -- Command class/object methods ---------------------------------- - def get_command_packages (self): + def get_command_packages(self): """Return a list of packages from which commands are loaded.""" pkgs = self.command_packages if not isinstance(pkgs, type([])): @@ -811,7 +776,7 @@ Common commands: (see '--help-commands' for more) self.command_packages = pkgs return pkgs - def get_command_class (self, command): + def get_command_class(self, command): """Return the class that implements the Distutils command named by 'command'. First we check the 'cmdclass' dictionary; if the command is mentioned there, we fetch the class object from the @@ -850,9 +815,7 @@ Common commands: (see '--help-commands' for more) raise DistutilsModuleError("invalid command '%s'" % command) - # get_command_class () - - def get_command_obj (self, command, create=1): + def get_command_obj(self, command, create=1): """Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command object for 'command' is in the cache, then we either create and @@ -879,7 +842,7 @@ Common commands: (see '--help-commands' for more) return cmd_obj - def _set_command_options (self, command_obj, option_dict=None): + def _set_command_options(self, command_obj, option_dict=None): """Set the options for 'command_obj' from 'option_dict'. Basically this means copying elements of a dictionary ('option_dict') to attributes of an instance ('command'). @@ -919,7 +882,7 @@ Common commands: (see '--help-commands' for more) except ValueError, msg: raise DistutilsOptionError, msg - def reinitialize_command (self, command, reinit_subcommands=0): + def reinitialize_command(self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first returned by 'get_command_obj()': ie., initialized but not yet finalized. This provides the opportunity to sneak option @@ -958,13 +921,12 @@ Common commands: (see '--help-commands' for more) return command - # -- Methods that operate on the Distribution ---------------------- - def announce (self, msg, level=1): + def announce(self, msg, level=1): log.debug(msg) - def run_commands (self): + def run_commands(self): """Run each command that was seen on the setup script command line. Uses the list of commands found and cache of command objects created by 'get_command_obj()'. @@ -972,10 +934,9 @@ Common commands: (see '--help-commands' for more) for cmd in self.commands: self.run_command(cmd) - # -- Methods that operate on its Commands -------------------------- - def run_command (self, command): + def run_command(self, command): """Do whatever it takes to run a command (including nothing at all, if the command has already been run). Specifically: if we have already created and run the command named by 'command', return @@ -996,28 +957,28 @@ Common commands: (see '--help-commands' for more) # -- Distribution query methods ------------------------------------ - def has_pure_modules (self): + def has_pure_modules(self): return len(self.packages or self.py_modules or []) > 0 - def has_ext_modules (self): + def has_ext_modules(self): return self.ext_modules and len(self.ext_modules) > 0 - def has_c_libraries (self): + def has_c_libraries(self): return self.libraries and len(self.libraries) > 0 - def has_modules (self): + def has_modules(self): return self.has_pure_modules() or self.has_ext_modules() - def has_headers (self): + def has_headers(self): return self.headers and len(self.headers) > 0 - def has_scripts (self): + def has_scripts(self): return self.scripts and len(self.scripts) > 0 - def has_data_files (self): + def has_data_files(self): return self.data_files and len(self.data_files) > 0 - def is_pure (self): + def is_pure(self): return (self.has_pure_modules() and not self.has_ext_modules() and not self.has_c_libraries()) @@ -1029,9 +990,6 @@ Common commands: (see '--help-commands' for more) # to self.metadata.get_XXX. The actual code is in the # DistributionMetadata class, below. -# class Distribution - - class DistributionMetadata: """Dummy class to hold the distribution meta-data: name, version, author, and so forth. @@ -1067,18 +1025,14 @@ class DistributionMetadata: self.requires = None self.obsoletes = None - def write_pkg_info (self, base_dir): + def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. """ pkg_info = open( os.path.join(base_dir, 'PKG-INFO'), 'w') - self.write_pkg_file(pkg_info) - pkg_info.close() - # write_pkg_info () - - def write_pkg_file (self, file): + def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. """ version = '1.0' @@ -1112,7 +1066,6 @@ class DistributionMetadata: self._write_list(file, 'Obsoletes', self.get_obsoletes()) def _write_field(self, file, name, value): - if isinstance(value, unicode): value = value.encode(PKG_INFO_ENCODING) else: @@ -1120,19 +1073,18 @@ class DistributionMetadata: file.write('%s: %s\n' % (name, value)) def _write_list (self, file, name, values): - for value in values: self._write_field(file, name, value) # -- Metadata query methods ---------------------------------------- - def get_name (self): + def get_name(self): return self.name or "UNKNOWN" def get_version(self): return self.version or "0.0.0" - def get_fullname (self): + def get_fullname(self): return "%s-%s" % (self.get_name(), self.get_version()) def get_author(self): @@ -1148,14 +1100,10 @@ class DistributionMetadata: return self.maintainer_email or "UNKNOWN" def get_contact(self): - return (self.maintainer or - self.author or - "UNKNOWN") + return self.maintainer or self.author or "UNKNOWN" def get_contact_email(self): - return (self.maintainer_email or - self.author_email or - "UNKNOWN") + return self.maintainer_email or self.author_email or "UNKNOWN" def get_url(self): return self.url or "UNKNOWN" @@ -1183,7 +1131,6 @@ class DistributionMetadata: return self.download_url or "UNKNOWN" # PEP 314 - def get_requires(self): return self.requires or [] @@ -1212,10 +1159,7 @@ class DistributionMetadata: distutils.versionpredicate.VersionPredicate(v) self.obsoletes = value -# class DistributionMetadata - - -def fix_help_options (options): +def fix_help_options(options): """Convert a 4-tuple 'help_options' list as found in various command classes to the 3-tuple form required by FancyGetopt. """ -- cgit v1.2.1 From a65846f388968a08b4187534689bc9cf4f8ecd63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 16 May 2009 18:37:32 +0000 Subject: Merged revisions 72686 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72686 | tarek.ziade | 2009-05-16 20:29:40 +0200 (Sat, 16 May 2009) | 1 line pep8-fied distutils.dist module ........ --- dist.py | 103 ++++++++++++++++++++++++---------------------------------------- 1 file changed, 38 insertions(+), 65 deletions(-) diff --git a/dist.py b/dist.py index 91adc4d3..79695035 100644 --- a/dist.py +++ b/dist.py @@ -262,21 +262,18 @@ Common commands: (see '--help-commands' for more) self.finalize_options() - - def get_option_dict (self, command): + def get_option_dict(self, command): """Get the option dictionary for a given command. If that command's option dictionary hasn't been created yet, then create it and return the new dictionary; otherwise, return the existing option dictionary. """ - dict = self.command_options.get(command) if dict is None: dict = self.command_options[command] = {} return dict - - def dump_option_dicts (self, header=None, commands=None, indent=""): + def dump_option_dicts(self, header=None, commands=None, indent=""): from pprint import pformat if commands is None: # dump all command option dicts @@ -300,11 +297,9 @@ Common commands: (see '--help-commands' for more) for line in out.split("\n"): print(indent + " " + line) - - # -- Config file finding/parsing methods --------------------------- - def find_config_files (self): + def find_config_files(self): """Find as many configuration files as should be processed for this platform, and return a list of filenames in the order in which they should be parsed. The filenames returned are guaranteed to exist @@ -345,9 +340,7 @@ Common commands: (see '--help-commands' for more) return files - - def parse_config_files (self, filenames=None): - + def parse_config_files(self, filenames=None): from configparser import ConfigParser if filenames is None: @@ -389,10 +382,9 @@ Common commands: (see '--help-commands' for more) except ValueError as msg: raise DistutilsOptionError(msg) - # -- Command-line parsing methods ---------------------------------- - def parse_command_line (self): + def parse_command_line(self): """Parse the setup script's command line, taken from the 'script_args' instance attribute (which defaults to 'sys.argv[1:]' -- see 'setup()' in core.py). This list is first processed for @@ -465,7 +457,7 @@ Common commands: (see '--help-commands' for more) # All is well: return true return True - def _get_toplevel_options (self): + def _get_toplevel_options(self): """Return the non-display options recognized at the top level. This includes options that are recognized *only* at the top @@ -476,7 +468,7 @@ Common commands: (see '--help-commands' for more) "list of packages that provide distutils commands"), ] - def _parse_command_opts (self, parser, args): + def _parse_command_opts(self, parser, args): """Parse the command-line options for a single command. 'parser' must be a FancyGetopt instance; 'args' must be the list of arguments, starting with the current command (whose options @@ -571,12 +563,11 @@ Common commands: (see '--help-commands' for more) return args - def finalize_options (self): + def finalize_options(self): """Set final values for all the options on the Distribution instance, analogous to the .finalize_options() method of Command objects. """ - keywords = self.metadata.keywords if keywords is not None: if isinstance(keywords, str): @@ -589,11 +580,8 @@ Common commands: (see '--help-commands' for more) platformlist = platforms.split(',') self.metadata.platforms = [x.strip() for x in platformlist] - def _show_help (self, - parser, - global_options=1, - display_options=1, - commands=[]): + def _show_help(self, parser, global_options=1, display_options=1, + commands=[]): """Show help for the setup script command-line in the form of several lists of command-line options. 'parser' should be a FancyGetopt instance; do not expect it to be returned in the @@ -643,8 +631,7 @@ Common commands: (see '--help-commands' for more) print(gen_usage(self.script_name)) return - - def handle_display_options (self, option_order): + def handle_display_options(self, option_order): """If there were any non-global "display-only" options (--help-commands or the metadata display options) on the command line, display the requested info and return true; else return @@ -684,7 +671,7 @@ Common commands: (see '--help-commands' for more) return any_display_options - def print_command_list (self, commands, header, max_length): + def print_command_list(self, commands, header, max_length): """Print a subset of the list of all commands -- used by 'print_commands()'. """ @@ -701,8 +688,7 @@ Common commands: (see '--help-commands' for more) print(" %-*s %s" % (max_length, cmd, description)) - - def print_commands (self): + def print_commands(self): """Print out a help message listing all available commands with a description of each. The list is divided into "standard commands" (listed in distutils.command.__all__) and "extra commands" @@ -735,7 +721,7 @@ Common commands: (see '--help-commands' for more) "Extra commands", max_length) - def get_command_list (self): + def get_command_list(self): """Get a list of (command, description) tuples. The list is divided into "standard commands" (listed in distutils.command.__all__) and "extra commands" (mentioned in @@ -769,7 +755,7 @@ Common commands: (see '--help-commands' for more) # -- Command class/object methods ---------------------------------- - def get_command_packages (self): + def get_command_packages(self): """Return a list of packages from which commands are loaded.""" pkgs = self.command_packages if not isinstance(pkgs, type([])): @@ -782,7 +768,7 @@ Common commands: (see '--help-commands' for more) self.command_packages = pkgs return pkgs - def get_command_class (self, command): + def get_command_class(self, command): """Return the class that implements the Distutils command named by 'command'. First we check the 'cmdclass' dictionary; if the command is mentioned there, we fetch the class object from the @@ -820,7 +806,7 @@ Common commands: (see '--help-commands' for more) raise DistutilsModuleError("invalid command '%s'" % command) - def get_command_obj (self, command, create=1): + def get_command_obj(self, command, create=1): """Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command object for 'command' is in the cache, then we either create and @@ -847,7 +833,7 @@ Common commands: (see '--help-commands' for more) return cmd_obj - def _set_command_options (self, command_obj, option_dict=None): + def _set_command_options(self, command_obj, option_dict=None): """Set the options for 'command_obj' from 'option_dict'. Basically this means copying elements of a dictionary ('option_dict') to attributes of an instance ('command'). @@ -888,7 +874,7 @@ Common commands: (see '--help-commands' for more) except ValueError as msg: raise DistutilsOptionError(msg) - def reinitialize_command (self, command, reinit_subcommands=0): + def reinitialize_command(self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first returned by 'get_command_obj()': ie., initialized but not yet finalized. This provides the opportunity to sneak option @@ -927,13 +913,12 @@ Common commands: (see '--help-commands' for more) return command - # -- Methods that operate on the Distribution ---------------------- - def announce (self, msg, level=1): + def announce(self, msg, level=1): log.debug(msg) - def run_commands (self): + def run_commands(self): """Run each command that was seen on the setup script command line. Uses the list of commands found and cache of command objects created by 'get_command_obj()'. @@ -941,10 +926,9 @@ Common commands: (see '--help-commands' for more) for cmd in self.commands: self.run_command(cmd) - # -- Methods that operate on its Commands -------------------------- - def run_command (self, command): + def run_command(self, command): """Do whatever it takes to run a command (including nothing at all, if the command has already been run). Specifically: if we have already created and run the command named by 'command', return @@ -965,28 +949,28 @@ Common commands: (see '--help-commands' for more) # -- Distribution query methods ------------------------------------ - def has_pure_modules (self): + def has_pure_modules(self): return len(self.packages or self.py_modules or []) > 0 - def has_ext_modules (self): + def has_ext_modules(self): return self.ext_modules and len(self.ext_modules) > 0 - def has_c_libraries (self): + def has_c_libraries(self): return self.libraries and len(self.libraries) > 0 - def has_modules (self): + def has_modules(self): return self.has_pure_modules() or self.has_ext_modules() - def has_headers (self): + def has_headers(self): return self.headers and len(self.headers) > 0 - def has_scripts (self): + def has_scripts(self): return self.scripts and len(self.scripts) > 0 - def has_data_files (self): + def has_data_files(self): return self.data_files and len(self.data_files) > 0 - def is_pure (self): + def is_pure(self): return (self.has_pure_modules() and not self.has_ext_modules() and not self.has_c_libraries()) @@ -998,9 +982,6 @@ Common commands: (see '--help-commands' for more) # to self.metadata.get_XXX. The actual code is in the # DistributionMetadata class, below. -# class Distribution - - class DistributionMetadata: """Dummy class to hold the distribution meta-data: name, version, author, and so forth. @@ -1036,16 +1017,14 @@ class DistributionMetadata: self.requires = None self.obsoletes = None - def write_pkg_info (self, base_dir): + def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. """ pkg_info = open( os.path.join(base_dir, 'PKG-INFO'), 'w') - self.write_pkg_file(pkg_info) - pkg_info.close() - def write_pkg_file (self, file): + def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. """ version = '1.0' @@ -1078,19 +1057,19 @@ class DistributionMetadata: self._write_list(file, 'Provides', self.get_provides()) self._write_list(file, 'Obsoletes', self.get_obsoletes()) - def _write_list (self, file, name, values): + def _write_list(self, file, name, values): for value in values: file.write('%s: %s\n' % (name, value)) # -- Metadata query methods ---------------------------------------- - def get_name (self): + def get_name(self): return self.name or "UNKNOWN" def get_version(self): return self.version or "0.0.0" - def get_fullname (self): + def get_fullname(self): return "%s-%s" % (self.get_name(), self.get_version()) def get_author(self): @@ -1106,14 +1085,10 @@ class DistributionMetadata: return self.maintainer_email or "UNKNOWN" def get_contact(self): - return (self.maintainer or - self.author or - "UNKNOWN") + return self.maintainer or self.author or "UNKNOWN" def get_contact_email(self): - return (self.maintainer_email or - self.author_email or - "UNKNOWN") + return self.maintainer_email or self.author_email or "UNKNOWN" def get_url(self): return self.url or "UNKNOWN" @@ -1141,7 +1116,6 @@ class DistributionMetadata: return self.download_url or "UNKNOWN" # PEP 314 - def get_requires(self): return self.requires or [] @@ -1170,8 +1144,7 @@ class DistributionMetadata: distutils.versionpredicate.VersionPredicate(v) self.obsoletes = value - -def fix_help_options (options): +def fix_help_options(options): """Convert a 4-tuple 'help_options' list as found in various command classes to the 3-tuple form required by FancyGetopt. """ -- cgit v1.2.1 From 4b18d769629e294345acd8d714ec854d20bbc5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 10:07:48 +0000 Subject: not running this test with MSVC6 --- tests/test_build_ext.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index c6533ca9..7e3f7165 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,6 +19,12 @@ from test import test_support # Don't load the xx module more than once. ALREADY_TESTED = False +if sys.platform != 'win32': + UNDER_MSVC8 = False +else: + from distutils.msvccompiler import get_build_version + UNDER_MSVC8 = get_build_version() < 8.0 + def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') return os.path.join(srcdir, 'Modules', 'xxmodule.c') @@ -293,6 +299,7 @@ class BuildExtTestCase(support.TempdirManager, cmd.run() self.assertEquals(cmd.compiler, 'unix') + @unittest.skipIf(UNDER_MSVC8, 'not running this test for MSVC < 8') def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') -- cgit v1.2.1 From c822bf11bbd7e21e4cccd81bbcaf4a7fce16edde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 10:10:51 +0000 Subject: Merged revisions 72713 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72713 | tarek.ziade | 2009-05-17 12:07:48 +0200 (Sun, 17 May 2009) | 1 line not running this test with MSVC6 ........ --- tests/test_build_ext.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 2bd2292e..21020d39 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -17,6 +17,12 @@ from test import test_support # Don't load the xx module more than once. ALREADY_TESTED = False +if sys.platform != 'win32': + UNDER_MSVC8 = False +else: + from distutils.msvccompiler import get_build_version + UNDER_MSVC8 = get_build_version() < 8.0 + class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -231,6 +237,8 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(cmd.compiler, 'unix') def test_get_outputs(self): + if UNDER_MSVC8: + return # not running this test for MSVC < 8 tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') self.write_file(c_file, 'void initfoo(void) {};\n') -- cgit v1.2.1 From dc4754eb50a3b88a4e302dc27b474be6ba18eaab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 10:12:02 +0000 Subject: Merged revisions 72713 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72713 | tarek.ziade | 2009-05-17 12:07:48 +0200 (Sun, 17 May 2009) | 1 line not running this test with MSVC6 ........ --- tests/test_build_ext.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 6a463c01..764aa727 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -20,6 +20,12 @@ from test import support # Don't load the xx module more than once. ALREADY_TESTED = False +if sys.platform != 'win32': + UNDER_MSVC8 = False +else: + from distutils.msvccompiler import get_build_version + UNDER_MSVC8 = get_build_version() < 8.0 + def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') return os.path.join(srcdir, 'Modules', 'xxmodule.c') @@ -293,6 +299,7 @@ class BuildExtTestCase(TempdirManager, cmd.run() self.assertEquals(cmd.compiler, 'unix') + @unittest.skipIf(UNDER_MSVC8, 'not running this test for MSVC < 8') def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') -- cgit v1.2.1 From f05e22fcc970ca8879603dee6842e310027ee256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 10:44:12 +0000 Subject: removed sys.platform == 'mac' support in distutils.dist.parse_command_line and improved test coverage --- dist.py | 6 ------ tests/test_dist.py | 58 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/dist.py b/dist.py index 0c77f85e..93325b97 100644 --- a/dist.py +++ b/dist.py @@ -414,11 +414,6 @@ Common commands: (see '--help-commands' for more) # that allows the user to interactively specify the "command line". # toplevel_options = self._get_toplevel_options() - if sys.platform == 'mac': - import EasyDialogs - cmdlist = self.get_command_list() - self.script_args = EasyDialogs.GetArgv( - toplevel_options + self.display_options, cmdlist) # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- @@ -438,7 +433,6 @@ Common commands: (see '--help-commands' for more) # for display options we return immediately if self.handle_display_options(option_order): return - while args: args = self._parse_command_opts(parser, args) if args is None: # user asked for help (and got it) diff --git a/tests/test_dist.py b/tests/test_dist.py index 3304790b..49230c54 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -1,19 +1,19 @@ # -*- coding: latin-1 -*- """Tests for distutils.dist.""" - -import distutils.cmd -import distutils.dist import os import StringIO import sys import unittest import warnings -from test.test_support import TESTFN +from distutils.dist import Distribution, fix_help_options +from distutils.cmd import Command + +from test.test_support import TESTFN, captured_stdout from distutils.tests import support -class test_dist(distutils.cmd.Command): +class test_dist(Command): """Sample distutils extension command.""" user_options = [ @@ -24,7 +24,7 @@ class test_dist(distutils.cmd.Command): self.sample_option = None -class TestDistribution(distutils.dist.Distribution): +class TestDistribution(Distribution): """Distribution subclasses that avoids the default search for configuration files. @@ -104,7 +104,7 @@ class DistributionTestCase(support.TempdirManager, unittest.TestCase): # Check DistributionMetadata handling of Unicode fields tmp_dir = self.mkdtemp() my_file = os.path.join(tmp_dir, 'f') - klass = distutils.dist.Distribution + klass = Distribution dist = klass(attrs={'author': u'Mister Café', 'name': 'my.package', @@ -131,7 +131,7 @@ class DistributionTestCase(support.TempdirManager, unittest.TestCase): def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes - klass = distutils.dist.Distribution + klass = Distribution # catching warnings warns = [] @@ -157,7 +157,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_simple_metadata(self): attrs = {"name": "package", "version": "1.0"} - dist = distutils.dist.Distribution(attrs) + dist = Distribution(attrs) meta = self.format_metadata(dist) self.assert_("Metadata-Version: 1.0" in meta) self.assert_("provides:" not in meta.lower()) @@ -168,7 +168,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, attrs = {"name": "package", "version": "1.0", "provides": ["package", "package.sub"]} - dist = distutils.dist.Distribution(attrs) + dist = Distribution(attrs) self.assertEqual(dist.metadata.get_provides(), ["package", "package.sub"]) self.assertEqual(dist.get_provides(), @@ -179,8 +179,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assert_("obsoletes:" not in meta.lower()) def test_provides_illegal(self): - self.assertRaises(ValueError, - distutils.dist.Distribution, + self.assertRaises(ValueError, Distribution, {"name": "package", "version": "1.0", "provides": ["my.pkg (splat)"]}) @@ -189,7 +188,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, attrs = {"name": "package", "version": "1.0", "requires": ["other", "another (==1.0)"]} - dist = distutils.dist.Distribution(attrs) + dist = Distribution(attrs) self.assertEqual(dist.metadata.get_requires(), ["other", "another (==1.0)"]) self.assertEqual(dist.get_requires(), @@ -202,8 +201,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assert_("obsoletes:" not in meta.lower()) def test_requires_illegal(self): - self.assertRaises(ValueError, - distutils.dist.Distribution, + self.assertRaises(ValueError, Distribution, {"name": "package", "version": "1.0", "requires": ["my.pkg (splat)"]}) @@ -212,7 +210,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, attrs = {"name": "package", "version": "1.0", "obsoletes": ["other", "another (<1.0)"]} - dist = distutils.dist.Distribution(attrs) + dist = Distribution(attrs) self.assertEqual(dist.metadata.get_obsoletes(), ["other", "another (<1.0)"]) self.assertEqual(dist.get_obsoletes(), @@ -225,8 +223,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assert_("Obsoletes: another (<1.0)" in meta) def test_obsoletes_illegal(self): - self.assertRaises(ValueError, - distutils.dist.Distribution, + self.assertRaises(ValueError, Distribution, {"name": "package", "version": "1.0", "obsoletes": ["my.pkg (splat)"]}) @@ -251,7 +248,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, f.close() try: - dist = distutils.dist.Distribution() + dist = Distribution() # linux-style if sys.platform in ('linux', 'darwin'): @@ -269,6 +266,29 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, finally: os.remove(user_filename) + def test_fix_help_options(self): + help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] + fancy_options = fix_help_options(help_tuples) + self.assertEquals(fancy_options[0], ('a', 'b', 'c')) + self.assertEquals(fancy_options[1], (1, 2, 3)) + + def test_show_help(self): + # smoke test, just makes sure some help is displayed + dist = Distribution() + old_argv = sys.argv + sys.argv = [] + try: + dist.help = 1 + dist.script_name = 'setup.py' + with captured_stdout() as s: + dist.parse_command_line() + finally: + sys.argv = old_argv + + output = [line for line in s.getvalue().split('\n') + if line.strip() != ''] + self.assert_(len(output) > 0) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) -- cgit v1.2.1 From d3f1bdd3baedab6204777740c026f481305cbea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 10:50:24 +0000 Subject: Merged revisions 72721 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72721 | tarek.ziade | 2009-05-17 12:44:12 +0200 (Sun, 17 May 2009) | 1 line removed sys.platform == 'mac' support in distutils.dist.parse_command_line and improved test coverage ........ --- dist.py | 6 ------ tests/test_dist.py | 57 ++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/dist.py b/dist.py index 79695035..65c1ce91 100644 --- a/dist.py +++ b/dist.py @@ -408,11 +408,6 @@ Common commands: (see '--help-commands' for more) # that allows the user to interactively specify the "command line". # toplevel_options = self._get_toplevel_options() - if sys.platform == 'mac': - import EasyDialogs - cmdlist = self.get_command_list() - self.script_args = EasyDialogs.GetArgv( - toplevel_options + self.display_options, cmdlist) # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- @@ -432,7 +427,6 @@ Common commands: (see '--help-commands' for more) # for display options we return immediately if self.handle_display_options(option_order): return - while args: args = self._parse_command_opts(parser, args) if args is None: # user asked for help (and got it) diff --git a/tests/test_dist.py b/tests/test_dist.py index 092bd14f..54b63f7d 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -1,18 +1,18 @@ """Tests for distutils.dist.""" - -import distutils.cmd -import distutils.dist import os import io import sys import unittest import warnings -from test.support import TESTFN +from distutils.dist import Distribution, fix_help_options +from distutils.cmd import Command + +from test.support import TESTFN, captured_stdout from distutils.tests import support -class test_dist(distutils.cmd.Command): +class test_dist(Command): """Sample distutils extension command.""" user_options = [ @@ -23,7 +23,7 @@ class test_dist(distutils.cmd.Command): self.sample_option = None -class TestDistribution(distutils.dist.Distribution): +class TestDistribution(Distribution): """Distribution subclasses that avoids the default search for configuration files. @@ -99,11 +99,10 @@ class DistributionTestCase(unittest.TestCase): finally: os.unlink(TESTFN) - def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes - klass = distutils.dist.Distribution + klass = Distribution # catching warnings warns = [] @@ -129,7 +128,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_simple_metadata(self): attrs = {"name": "package", "version": "1.0"} - dist = distutils.dist.Distribution(attrs) + dist = Distribution(attrs) meta = self.format_metadata(dist) self.assert_("Metadata-Version: 1.0" in meta) self.assert_("provides:" not in meta.lower()) @@ -140,7 +139,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, attrs = {"name": "package", "version": "1.0", "provides": ["package", "package.sub"]} - dist = distutils.dist.Distribution(attrs) + dist = Distribution(attrs) self.assertEqual(dist.metadata.get_provides(), ["package", "package.sub"]) self.assertEqual(dist.get_provides(), @@ -151,8 +150,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assert_("obsoletes:" not in meta.lower()) def test_provides_illegal(self): - self.assertRaises(ValueError, - distutils.dist.Distribution, + self.assertRaises(ValueError, Distribution, {"name": "package", "version": "1.0", "provides": ["my.pkg (splat)"]}) @@ -161,7 +159,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, attrs = {"name": "package", "version": "1.0", "requires": ["other", "another (==1.0)"]} - dist = distutils.dist.Distribution(attrs) + dist = Distribution(attrs) self.assertEqual(dist.metadata.get_requires(), ["other", "another (==1.0)"]) self.assertEqual(dist.get_requires(), @@ -174,8 +172,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assert_("obsoletes:" not in meta.lower()) def test_requires_illegal(self): - self.assertRaises(ValueError, - distutils.dist.Distribution, + self.assertRaises(ValueError, Distribution, {"name": "package", "version": "1.0", "requires": ["my.pkg (splat)"]}) @@ -184,7 +181,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, attrs = {"name": "package", "version": "1.0", "obsoletes": ["other", "another (<1.0)"]} - dist = distutils.dist.Distribution(attrs) + dist = Distribution(attrs) self.assertEqual(dist.metadata.get_obsoletes(), ["other", "another (<1.0)"]) self.assertEqual(dist.get_obsoletes(), @@ -197,8 +194,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assert_("Obsoletes: another (<1.0)" in meta) def test_obsoletes_illegal(self): - self.assertRaises(ValueError, - distutils.dist.Distribution, + self.assertRaises(ValueError, Distribution, {"name": "package", "version": "1.0", "obsoletes": ["my.pkg (splat)"]}) @@ -223,7 +219,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, f.close() try: - dist = distutils.dist.Distribution() + dist = Distribution() # linux-style if sys.platform in ('linux', 'darwin'): @@ -241,6 +237,29 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, finally: os.remove(user_filename) + def test_fix_help_options(self): + help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] + fancy_options = fix_help_options(help_tuples) + self.assertEquals(fancy_options[0], ('a', 'b', 'c')) + self.assertEquals(fancy_options[1], (1, 2, 3)) + + def test_show_help(self): + # smoke test, just makes sure some help is displayed + dist = Distribution() + old_argv = sys.argv + sys.argv = [] + try: + dist.help = 1 + dist.script_name = 'setup.py' + with captured_stdout() as s: + dist.parse_command_line() + finally: + sys.argv = old_argv + + output = [line for line in s.getvalue().split('\n') + if line.strip() != ''] + self.assert_(len(output) > 0) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) -- cgit v1.2.1 From 9342187befd026978a753e2e41127ad0abddc35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 11:11:57 +0000 Subject: removed sys.platform == 'mac' usage in distutils.dir_util --- dir_util.py | 11 ++++------- tests/test_dir_util.py | 14 ++++++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/dir_util.py b/dir_util.py index 5a134408..9604c6d2 100644 --- a/dir_util.py +++ b/dir_util.py @@ -212,14 +212,11 @@ def remove_tree (directory, verbose=1, dry_run=0): exc, "error removing %s: " % directory)) -def ensure_relative (path): +def ensure_relative(path): """Take the full path 'path', and make it a relative path so it can be the second argument to os.path.join(). """ drive, path = os.path.splitdrive(path) - if sys.platform == 'mac': - return os.sep + path - else: - if path[0:1] == os.sep: - path = drive + path[1:] - return path + if path[0:1] == os.sep: + path = drive + path[1:] + return path diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index bf416b6d..9bd6530c 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,10 +3,8 @@ import unittest import os import shutil -from distutils.dir_util import mkpath -from distutils.dir_util import remove_tree -from distutils.dir_util import create_tree -from distutils.dir_util import copy_tree +from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, + ensure_relative) from distutils import log from distutils.tests import support @@ -85,6 +83,14 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): remove_tree(self.root_target, verbose=0) remove_tree(self.target2, verbose=0) + def test_ensure_relative(self): + if os.sep == '/': + self.assertEquals(ensure_relative('/home/foo'), 'home/foo') + self.assertEquals(ensure_relative('some/path'), 'some/path') + else: # \\ + self.assertEquals(ensure_relative('c:\\home\\foo'), 'home\\foo') + self.assertEquals(ensure_relative('home\\foo'), 'home\\foo') + def test_suite(): return unittest.makeSuite(DirUtilTestCase) -- cgit v1.2.1 From 67db2917c102fce286384cbbe3bd90c4d2d2642f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 11:14:15 +0000 Subject: Merged revisions 72727 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72727 | tarek.ziade | 2009-05-17 13:11:57 +0200 (Sun, 17 May 2009) | 1 line removed sys.platform == 'mac' usage in distutils.dir_util ........ --- dir_util.py | 11 ++++------- tests/test_dir_util.py | 14 ++++++++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/dir_util.py b/dir_util.py index ed54ccef..82608d2c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -208,14 +208,11 @@ def remove_tree (directory, verbose=1, dry_run=0): exc, "error removing %s: " % directory)) -def ensure_relative (path): +def ensure_relative(path): """Take the full path 'path', and make it a relative path so it can be the second argument to os.path.join(). """ drive, path = os.path.splitdrive(path) - if sys.platform == 'mac': - return os.sep + path - else: - if path[0:1] == os.sep: - path = drive + path[1:] - return path + if path[0:1] == os.sep: + path = drive + path[1:] + return path diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index bf416b6d..9bd6530c 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,10 +3,8 @@ import unittest import os import shutil -from distutils.dir_util import mkpath -from distutils.dir_util import remove_tree -from distutils.dir_util import create_tree -from distutils.dir_util import copy_tree +from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, + ensure_relative) from distutils import log from distutils.tests import support @@ -85,6 +83,14 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): remove_tree(self.root_target, verbose=0) remove_tree(self.target2, verbose=0) + def test_ensure_relative(self): + if os.sep == '/': + self.assertEquals(ensure_relative('/home/foo'), 'home/foo') + self.assertEquals(ensure_relative('some/path'), 'some/path') + else: # \\ + self.assertEquals(ensure_relative('c:\\home\\foo'), 'home\\foo') + self.assertEquals(ensure_relative('home\\foo'), 'home\\foo') + def test_suite(): return unittest.makeSuite(DirUtilTestCase) -- cgit v1.2.1 From 7d5ed40f17e1b7f49b5af117c149730860c5560f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 11:22:36 +0000 Subject: pep8-fied distutils.dir_util --- dir_util.py | 110 +++++++++++++++++++++++++++--------------------------------- 1 file changed, 49 insertions(+), 61 deletions(-) diff --git a/dir_util.py b/dir_util.py index 9604c6d2..55607e5b 100644 --- a/dir_util.py +++ b/dir_util.py @@ -5,7 +5,6 @@ Utility functions for manipulating directories and directory trees.""" __revision__ = "$Id$" import os, sys -from types import * from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log @@ -16,20 +15,21 @@ _path_created = {} # I don't use os.makedirs because a) it's new to Python 1.5.2, and # b) it blows up if the directory already exists (I want to silently # succeed in that case). -def mkpath (name, mode=0777, verbose=1, dry_run=0): - """Create a directory and any missing ancestor directories. If the - directory already exists (or if 'name' is the empty string, which - means the current directory, which of course exists), then do - nothing. Raise DistutilsFileError if unable to create some - directory along the way (eg. some sub-path exists, but is a file - rather than a directory). If 'verbose' is true, print a one-line - summary of each mkdir to stdout. Return the list of directories - actually created.""" +def mkpath(name, mode=0777, verbose=1, dry_run=0): + """Create a directory and any missing ancestor directories. + + If the directory already exists (or if 'name' is the empty string, which + means the current directory, which of course exists), then do nothing. + Raise DistutilsFileError if unable to create some directory along the way + (eg. some sub-path exists, but is a file rather than a directory). + If 'verbose' is true, print a one-line summary of each mkdir to stdout. + Return the list of directories actually created. + """ global _path_created # Detect a common bug -- name is None - if not isinstance(name, StringTypes): + if not isinstance(name, basestring): raise DistutilsInternalError, \ "mkpath: 'name' must be a string (got %r)" % (name,) @@ -77,19 +77,16 @@ def mkpath (name, mode=0777, verbose=1, dry_run=0): _path_created[abs_head] = 1 return created_dirs -# mkpath () - - -def create_tree (base_dir, files, mode=0777, verbose=1, dry_run=0): - - """Create all the empty directories under 'base_dir' needed to - put 'files' there. 'base_dir' is just the a name of a directory - which doesn't necessarily exist yet; 'files' is a list of filenames - to be interpreted relative to 'base_dir'. 'base_dir' + the - directory portion of every file in 'files' will be created if it - doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as - for 'mkpath()'.""" +def create_tree(base_dir, files, mode=0777, verbose=1, dry_run=0): + """Create all the empty directories under 'base_dir' needed to put 'files' + there. + 'base_dir' is just the a name of a directory which doesn't necessarily + exist yet; 'files' is a list of filenames to be interpreted relative to + 'base_dir'. 'base_dir' + the directory portion of every file in 'files' + will be created if it doesn't already exist. 'mode', 'verbose' and + 'dry_run' flags are as for 'mkpath()'. + """ # First get the list of directories to create need_dir = {} for file in files: @@ -101,35 +98,27 @@ def create_tree (base_dir, files, mode=0777, verbose=1, dry_run=0): for dir in need_dirs: mkpath(dir, mode, verbose=verbose, dry_run=dry_run) -# create_tree () - - -def copy_tree (src, dst, - preserve_mode=1, - preserve_times=1, - preserve_symlinks=0, - update=0, - verbose=1, - dry_run=0): - - """Copy an entire directory tree 'src' to a new location 'dst'. Both - 'src' and 'dst' must be directory names. If 'src' is not a - directory, raise DistutilsFileError. If 'dst' does not exist, it is - created with 'mkpath()'. The end result of the copy is that every - file in 'src' is copied to 'dst', and directories under 'src' are - recursively copied to 'dst'. Return the list of files that were - copied or might have been copied, using their output name. The - return value is unaffected by 'update' or 'dry_run': it is simply - the list of all files under 'src', with the names changed to be - under 'dst'. - - 'preserve_mode' and 'preserve_times' are the same as for - 'copy_file'; note that they only apply to regular files, not to - directories. If 'preserve_symlinks' is true, symlinks will be - copied as symlinks (on platforms that support them!); otherwise - (the default), the destination of the symlink will be copied. - 'update' and 'verbose' are the same as for 'copy_file'.""" - +def copy_tree(src, dst, preserve_mode=1, preserve_times=1, + preserve_symlinks=0, update=0, verbose=1, dry_run=0): + """Copy an entire directory tree 'src' to a new location 'dst'. + + Both 'src' and 'dst' must be directory names. If 'src' is not a + directory, raise DistutilsFileError. If 'dst' does not exist, it is + created with 'mkpath()'. The end result of the copy is that every + file in 'src' is copied to 'dst', and directories under 'src' are + recursively copied to 'dst'. Return the list of files that were + copied or might have been copied, using their output name. The + return value is unaffected by 'update' or 'dry_run': it is simply + the list of all files under 'src', with the names changed to be + under 'dst'. + + 'preserve_mode' and 'preserve_times' are the same as for + 'copy_file'; note that they only apply to regular files, not to + directories. If 'preserve_symlinks' is true, symlinks will be + copied as symlinks (on platforms that support them!); otherwise + (the default), the destination of the symlink will be copied. + 'update' and 'verbose' are the same as for 'copy_file'. + """ from distutils.file_util import copy_file if not dry_run and not os.path.isdir(src): @@ -174,10 +163,8 @@ def copy_tree (src, dst, return outputs -# copy_tree () - -# Helper for remove_tree() def _build_cmdtuple(path, cmdtuples): + """Helper for remove_tree().""" for f in os.listdir(path): real_f = os.path.join(path,f) if os.path.isdir(real_f) and not os.path.islink(real_f): @@ -186,10 +173,11 @@ def _build_cmdtuple(path, cmdtuples): cmdtuples.append((os.remove, real_f)) cmdtuples.append((os.rmdir, path)) +def remove_tree(directory, verbose=1, dry_run=0): + """Recursively remove an entire directory tree. -def remove_tree (directory, verbose=1, dry_run=0): - """Recursively remove an entire directory tree. Any errors are ignored - (apart from being reported to stdout if 'verbose' is true). + Any errors are ignored (apart from being reported to stdout if 'verbose' + is true). """ from distutils.util import grok_environment_error global _path_created @@ -211,10 +199,10 @@ def remove_tree (directory, verbose=1, dry_run=0): log.warn(grok_environment_error( exc, "error removing %s: " % directory)) - def ensure_relative(path): - """Take the full path 'path', and make it a relative path so - it can be the second argument to os.path.join(). + """Take the full path 'path', and make it a relative path. + + This is useful to make 'path' the second argument to os.path.join(). """ drive, path = os.path.splitdrive(path) if path[0:1] == os.sep: -- cgit v1.2.1 From 26aaaedf5db9750530cb20f8b71f1826a8ec8fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 11:25:57 +0000 Subject: Merged revisions 72730 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72730 | tarek.ziade | 2009-05-17 13:22:36 +0200 (Sun, 17 May 2009) | 1 line pep8-fied distutils.dir_util ........ --- dir_util.py | 105 +++++++++++++++++++++++++++--------------------------------- 1 file changed, 48 insertions(+), 57 deletions(-) diff --git a/dir_util.py b/dir_util.py index 82608d2c..98e6252c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -15,15 +15,16 @@ _path_created = {} # I don't use os.makedirs because a) it's new to Python 1.5.2, and # b) it blows up if the directory already exists (I want to silently # succeed in that case). -def mkpath (name, mode=0o777, verbose=1, dry_run=0): - """Create a directory and any missing ancestor directories. If the - directory already exists (or if 'name' is the empty string, which - means the current directory, which of course exists), then do - nothing. Raise DistutilsFileError if unable to create some - directory along the way (eg. some sub-path exists, but is a file - rather than a directory). If 'verbose' is true, print a one-line - summary of each mkdir to stdout. Return the list of directories - actually created.""" +def mkpath(name, mode=0o777, verbose=1, dry_run=0): + """Create a directory and any missing ancestor directories. + + If the directory already exists (or if 'name' is the empty string, which + means the current directory, which of course exists), then do nothing. + Raise DistutilsFileError if unable to create some directory along the way + (eg. some sub-path exists, but is a file rather than a directory). + If 'verbose' is true, print a one-line summary of each mkdir to stdout. + Return the list of directories actually created. + """ global _path_created @@ -76,19 +77,16 @@ def mkpath (name, mode=0o777, verbose=1, dry_run=0): _path_created[abs_head] = 1 return created_dirs -# mkpath () - - -def create_tree (base_dir, files, mode=0o777, verbose=1, dry_run=0): - - """Create all the empty directories under 'base_dir' needed to - put 'files' there. 'base_dir' is just the a name of a directory - which doesn't necessarily exist yet; 'files' is a list of filenames - to be interpreted relative to 'base_dir'. 'base_dir' + the - directory portion of every file in 'files' will be created if it - doesn't already exist. 'mode', 'verbose' and 'dry_run' flags are as - for 'mkpath()'.""" +def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0): + """Create all the empty directories under 'base_dir' needed to put 'files' + there. + 'base_dir' is just the a name of a directory which doesn't necessarily + exist yet; 'files' is a list of filenames to be interpreted relative to + 'base_dir'. 'base_dir' + the directory portion of every file in 'files' + will be created if it doesn't already exist. 'mode', 'verbose' and + 'dry_run' flags are as for 'mkpath()'. + """ # First get the list of directories to create need_dir = set() for file in files: @@ -98,35 +96,27 @@ def create_tree (base_dir, files, mode=0o777, verbose=1, dry_run=0): for dir in sorted(need_dir): mkpath(dir, mode, verbose=verbose, dry_run=dry_run) -# create_tree () - - -def copy_tree (src, dst, - preserve_mode=1, - preserve_times=1, - preserve_symlinks=0, - update=0, - verbose=1, - dry_run=0): - - """Copy an entire directory tree 'src' to a new location 'dst'. Both - 'src' and 'dst' must be directory names. If 'src' is not a - directory, raise DistutilsFileError. If 'dst' does not exist, it is - created with 'mkpath()'. The end result of the copy is that every - file in 'src' is copied to 'dst', and directories under 'src' are - recursively copied to 'dst'. Return the list of files that were - copied or might have been copied, using their output name. The - return value is unaffected by 'update' or 'dry_run': it is simply - the list of all files under 'src', with the names changed to be - under 'dst'. - - 'preserve_mode' and 'preserve_times' are the same as for - 'copy_file'; note that they only apply to regular files, not to - directories. If 'preserve_symlinks' is true, symlinks will be - copied as symlinks (on platforms that support them!); otherwise - (the default), the destination of the symlink will be copied. - 'update' and 'verbose' are the same as for 'copy_file'.""" - +def copy_tree(src, dst, preserve_mode=1, preserve_times=1, + preserve_symlinks=0, update=0, verbose=1, dry_run=0): + """Copy an entire directory tree 'src' to a new location 'dst'. + + Both 'src' and 'dst' must be directory names. If 'src' is not a + directory, raise DistutilsFileError. If 'dst' does not exist, it is + created with 'mkpath()'. The end result of the copy is that every + file in 'src' is copied to 'dst', and directories under 'src' are + recursively copied to 'dst'. Return the list of files that were + copied or might have been copied, using their output name. The + return value is unaffected by 'update' or 'dry_run': it is simply + the list of all files under 'src', with the names changed to be + under 'dst'. + + 'preserve_mode' and 'preserve_times' are the same as for + 'copy_file'; note that they only apply to regular files, not to + directories. If 'preserve_symlinks' is true, symlinks will be + copied as symlinks (on platforms that support them!); otherwise + (the default), the destination of the symlink will be copied. + 'update' and 'verbose' are the same as for 'copy_file'. + """ from distutils.file_util import copy_file if not dry_run and not os.path.isdir(src): @@ -172,8 +162,8 @@ def copy_tree (src, dst, return outputs -# Helper for remove_tree() def _build_cmdtuple(path, cmdtuples): + """Helper for remove_tree().""" for f in os.listdir(path): real_f = os.path.join(path,f) if os.path.isdir(real_f) and not os.path.islink(real_f): @@ -182,10 +172,11 @@ def _build_cmdtuple(path, cmdtuples): cmdtuples.append((os.remove, real_f)) cmdtuples.append((os.rmdir, path)) +def remove_tree(directory, verbose=1, dry_run=0): + """Recursively remove an entire directory tree. -def remove_tree (directory, verbose=1, dry_run=0): - """Recursively remove an entire directory tree. Any errors are ignored - (apart from being reported to stdout if 'verbose' is true). + Any errors are ignored (apart from being reported to stdout if 'verbose' + is true). """ from distutils.util import grok_environment_error global _path_created @@ -207,10 +198,10 @@ def remove_tree (directory, verbose=1, dry_run=0): log.warn(grok_environment_error( exc, "error removing %s: " % directory)) - def ensure_relative(path): - """Take the full path 'path', and make it a relative path so - it can be the second argument to os.path.join(). + """Take the full path 'path', and make it a relative path. + + This is useful to make 'path' the second argument to os.path.join(). """ drive, path = os.path.splitdrive(path) if path[0:1] == os.sep: -- cgit v1.2.1 From 0f9f597d4519708a7866861403174320a81f2baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 12:04:57 +0000 Subject: pep8-fied distutils.archive_util + added minimum test coverage --- archive_util.py | 80 +++++++++++++++++++++++----------------------- tests/test_archive_util.py | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 40 deletions(-) create mode 100644 tests/test_archive_util.py diff --git a/archive_util.py b/archive_util.py index b50563e3..f5959f5e 100644 --- a/archive_util.py +++ b/archive_util.py @@ -11,15 +11,16 @@ from distutils.spawn import spawn from distutils.dir_util import mkpath from distutils import log -def make_tarball (base_name, base_dir, compress="gzip", - verbose=0, dry_run=0): +def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): """Create a (possibly compressed) tar file from all the files under - 'base_dir'. 'compress' must be "gzip" (the default), "compress", - "bzip2", or None. Both "tar" and the compression utility named by - 'compress' must be on the default program search path, so this is - probably Unix-specific. The output tar file will be named 'base_dir' + - ".tar", possibly plus the appropriate compression extension (".gz", - ".bz2" or ".Z"). Return the output filename. + 'base_dir'. + + 'compress' must be "gzip" (the default), "compress", "bzip2", or None. + Both "tar" and the compression utility named by 'compress' must be on + the default program search path, so this is probably Unix-specific. + The output tar file will be named 'base_dir' + ".tar", possibly plus + the appropriate compression extension (".gz", ".bz2" or ".Z"). + Returns the output filename. """ # XXX GNU tar 1.13 has a nifty option to add a prefix directory. # It's pretty new, though, so we certainly can't require it -- @@ -27,9 +28,9 @@ def make_tarball (base_name, base_dir, compress="gzip", # "create a tree of hardlinks" step! (Would also be nice to # detect GNU tar to use its 'z' option and save a step.) - compress_ext = { 'gzip': ".gz", - 'bzip2': '.bz2', - 'compress': ".Z" } + compress_ext = {'gzip': ".gz", + 'bzip2': '.bz2', + 'compress': ".Z" } # flags for compression program, each element of list will be an argument compress_flags = {'gzip': ["-f9"], @@ -52,15 +53,14 @@ def make_tarball (base_name, base_dir, compress="gzip", else: return archive_name -# make_tarball () +def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): + """Create a zip file from all the files under 'base_dir'. - -def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): - """Create a zip file from all the files under 'base_dir'. The output - zip file will be named 'base_dir' + ".zip". Uses either the "zipfile" - Python module (if available) or the InfoZIP "zip" utility (if installed - and found on the default search path). If neither tool is available, - raises DistutilsExecError. Returns the name of the output zip file. + The output zip file will be named 'base_dir' + ".zip". Uses either the + "zipfile" Python module (if available) or the InfoZIP "zip" utility + (if installed and found on the default search path). If neither tool is + available, raises DistutilsExecError. Returns the name of the output zip + file. """ try: import zipfile @@ -94,22 +94,19 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): zip_filename, base_dir) if not dry_run: - z = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) for dirpath, dirnames, filenames in os.walk(base_dir): for name in filenames: path = os.path.normpath(os.path.join(dirpath, name)) if os.path.isfile(path): - z.write(path, path) + zip.write(path, path) log.info("adding '%s'" % path) - z.close() + zip.close() return zip_filename -# make_zipfile () - - ARCHIVE_FORMATS = { 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), @@ -118,19 +115,24 @@ ARCHIVE_FORMATS = { 'zip': (make_zipfile, [],"ZIP file") } -def check_archive_formats (formats): +def check_archive_formats(formats): + """Returns the first format from the 'format' list that is unknown. + + If all formats are known, returns None + """ for format in formats: if format not in ARCHIVE_FORMATS: return format - else: - return None - -def make_archive (base_name, format, - root_dir=None, base_dir=None, - verbose=0, dry_run=0): - """Create an archive file (eg. zip or tar). 'base_name' is the name - of the file to create, minus any format-specific extension; 'format' - is the archive format: one of "zip", "tar", "ztar", or "gztar". + return None + +def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, + dry_run=0): + """Create an archive file (eg. zip or tar). + + 'base_name' is the name of the file to create, minus any format-specific + extension; 'format' is the archive format: one of "zip", "tar", "ztar", + or "gztar". + 'root_dir' is a directory that will be the root directory of the archive; ie. we typically chdir into 'root_dir' before creating the archive. 'base_dir' is the directory where we start archiving from; @@ -148,7 +150,7 @@ def make_archive (base_name, format, if base_dir is None: base_dir = os.curdir - kwargs = { 'dry_run': dry_run } + kwargs = {'dry_run': dry_run} try: format_info = ARCHIVE_FORMATS[format] @@ -156,7 +158,7 @@ def make_archive (base_name, format, raise ValueError, "unknown archive format '%s'" % format func = format_info[0] - for (arg,val) in format_info[1]: + for arg, val in format_info[1]: kwargs[arg] = val filename = apply(func, (base_name, base_dir), kwargs) @@ -165,5 +167,3 @@ def make_archive (base_name, format, os.chdir(save_cwd) return filename - -# make_archive () diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py new file mode 100644 index 00000000..f5fd9eab --- /dev/null +++ b/tests/test_archive_util.py @@ -0,0 +1,70 @@ +"""Tests for distutils.archive_util.""" +__revision__ = "$Id:$" + +import unittest +import os + +from distutils.archive_util import (check_archive_formats, make_tarball, + make_zipfile, make_archive) +from distutils.spawn import find_executable +from distutils.tests import support + +try: + import zipfile + ZIP_SUPPORT = True +except ImportError: + ZIP_SUPPORT = find_executable('zip') + +class ArchiveUtilTestCase(support.TempdirManager, + unittest.TestCase): + + @unittest.skipUnless(find_executable('tar'), 'Need the tar command to run') + def test_make_tarball(self): + # creating something to tar + tmpdir = self.mkdtemp() + self.write_file([tmpdir, 'file1'], 'xxx') + self.write_file([tmpdir, 'file2'], 'xxx') + + tmpdir2 = self.mkdtemp() + base_name = os.path.join(tmpdir2, 'archive') + make_tarball(base_name, tmpdir) + + # check if the compressed tarball was created + tarball = base_name + '.tar.gz' + self.assert_(os.path.exists(tarball)) + + # trying an uncompressed one + base_name = os.path.join(tmpdir2, 'archive') + make_tarball(base_name, tmpdir, compress=None) + tarball = base_name + '.tar' + self.assert_(os.path.exists(tarball)) + + @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') + def test_make_tarball(self): + # creating something to tar + tmpdir = self.mkdtemp() + self.write_file([tmpdir, 'file1'], 'xxx') + self.write_file([tmpdir, 'file2'], 'xxx') + + tmpdir2 = self.mkdtemp() + base_name = os.path.join(tmpdir2, 'archive') + make_zipfile(base_name, tmpdir) + + # check if the compressed tarball was created + tarball = base_name + '.zip' + + def test_check_archive_formats(self): + self.assertEquals(check_archive_formats(['gztar', 'xxx', 'zip']), + 'xxx') + self.assertEquals(check_archive_formats(['gztar', 'zip']), None) + + def test_make_archive(self): + tmpdir = self.mkdtemp() + base_name = os.path.join(tmpdir, 'archive') + self.assertRaises(ValueError, make_archive, base_name, 'xxx') + +def test_suite(): + return unittest.makeSuite(ArchiveUtilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 4a9bfc806815434110760ba83b05c58bc2fe3014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 12:12:02 +0000 Subject: Merged revisions 72736 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72736 | tarek.ziade | 2009-05-17 14:04:57 +0200 (Sun, 17 May 2009) | 1 line pep8-fied distutils.archive_util + added minimum test coverage ........ --- archive_util.py | 80 +++++++++++++++++++++++----------------------- tests/test_archive_util.py | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 40 deletions(-) create mode 100644 tests/test_archive_util.py diff --git a/archive_util.py b/archive_util.py index 9444ff01..08a9e569 100644 --- a/archive_util.py +++ b/archive_util.py @@ -11,15 +11,16 @@ from distutils.spawn import spawn from distutils.dir_util import mkpath from distutils import log -def make_tarball (base_name, base_dir, compress="gzip", - verbose=0, dry_run=0): +def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): """Create a (possibly compressed) tar file from all the files under - 'base_dir'. 'compress' must be "gzip" (the default), "compress", - "bzip2", or None. Both "tar" and the compression utility named by - 'compress' must be on the default program search path, so this is - probably Unix-specific. The output tar file will be named 'base_dir' + - ".tar", possibly plus the appropriate compression extension (".gz", - ".bz2" or ".Z"). Return the output filename. + 'base_dir'. + + 'compress' must be "gzip" (the default), "compress", "bzip2", or None. + Both "tar" and the compression utility named by 'compress' must be on + the default program search path, so this is probably Unix-specific. + The output tar file will be named 'base_dir' + ".tar", possibly plus + the appropriate compression extension (".gz", ".bz2" or ".Z"). + Returns the output filename. """ # XXX GNU tar 1.13 has a nifty option to add a prefix directory. # It's pretty new, though, so we certainly can't require it -- @@ -27,9 +28,9 @@ def make_tarball (base_name, base_dir, compress="gzip", # "create a tree of hardlinks" step! (Would also be nice to # detect GNU tar to use its 'z' option and save a step.) - compress_ext = { 'gzip': ".gz", - 'bzip2': '.bz2', - 'compress': ".Z" } + compress_ext = {'gzip': ".gz", + 'bzip2': '.bz2', + 'compress': ".Z" } # flags for compression program, each element of list will be an argument compress_flags = {'gzip': ["-f9"], @@ -52,15 +53,14 @@ def make_tarball (base_name, base_dir, compress="gzip", else: return archive_name -# make_tarball () +def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): + """Create a zip file from all the files under 'base_dir'. - -def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): - """Create a zip file from all the files under 'base_dir'. The output - zip file will be named 'base_dir' + ".zip". Uses either the "zipfile" - Python module (if available) or the InfoZIP "zip" utility (if installed - and found on the default search path). If neither tool is available, - raises DistutilsExecError. Returns the name of the output zip file. + The output zip file will be named 'base_dir' + ".zip". Uses either the + "zipfile" Python module (if available) or the InfoZIP "zip" utility + (if installed and found on the default search path). If neither tool is + available, raises DistutilsExecError. Returns the name of the output zip + file. """ try: import zipfile @@ -93,22 +93,19 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): zip_filename, base_dir) if not dry_run: - z = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) for dirpath, dirnames, filenames in os.walk(base_dir): for name in filenames: path = os.path.normpath(os.path.join(dirpath, name)) if os.path.isfile(path): - z.write(path, path) + zip.write(path, path) log.info("adding '%s'" % path) - z.close() + zip.close() return zip_filename -# make_zipfile () - - ARCHIVE_FORMATS = { 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), @@ -117,19 +114,24 @@ ARCHIVE_FORMATS = { 'zip': (make_zipfile, [],"ZIP file") } -def check_archive_formats (formats): +def check_archive_formats(formats): + """Returns the first format from the 'format' list that is unknown. + + If all formats are known, returns None + """ for format in formats: if format not in ARCHIVE_FORMATS: return format - else: - return None - -def make_archive (base_name, format, - root_dir=None, base_dir=None, - verbose=0, dry_run=0): - """Create an archive file (eg. zip or tar). 'base_name' is the name - of the file to create, minus any format-specific extension; 'format' - is the archive format: one of "zip", "tar", "ztar", or "gztar". + return None + +def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, + dry_run=0): + """Create an archive file (eg. zip or tar). + + 'base_name' is the name of the file to create, minus any format-specific + extension; 'format' is the archive format: one of "zip", "tar", "ztar", + or "gztar". + 'root_dir' is a directory that will be the root directory of the archive; ie. we typically chdir into 'root_dir' before creating the archive. 'base_dir' is the directory where we start archiving from; @@ -147,7 +149,7 @@ def make_archive (base_name, format, if base_dir is None: base_dir = os.curdir - kwargs = { 'dry_run': dry_run } + kwargs = {'dry_run': dry_run} try: format_info = ARCHIVE_FORMATS[format] @@ -155,7 +157,7 @@ def make_archive (base_name, format, raise ValueError("unknown archive format '%s'" % format) func = format_info[0] - for (arg,val) in format_info[1]: + for arg, val in format_info[1]: kwargs[arg] = val filename = func(base_name, base_dir, **kwargs) @@ -164,5 +166,3 @@ def make_archive (base_name, format, os.chdir(save_cwd) return filename - -# make_archive () diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py new file mode 100644 index 00000000..f5fd9eab --- /dev/null +++ b/tests/test_archive_util.py @@ -0,0 +1,70 @@ +"""Tests for distutils.archive_util.""" +__revision__ = "$Id:$" + +import unittest +import os + +from distutils.archive_util import (check_archive_formats, make_tarball, + make_zipfile, make_archive) +from distutils.spawn import find_executable +from distutils.tests import support + +try: + import zipfile + ZIP_SUPPORT = True +except ImportError: + ZIP_SUPPORT = find_executable('zip') + +class ArchiveUtilTestCase(support.TempdirManager, + unittest.TestCase): + + @unittest.skipUnless(find_executable('tar'), 'Need the tar command to run') + def test_make_tarball(self): + # creating something to tar + tmpdir = self.mkdtemp() + self.write_file([tmpdir, 'file1'], 'xxx') + self.write_file([tmpdir, 'file2'], 'xxx') + + tmpdir2 = self.mkdtemp() + base_name = os.path.join(tmpdir2, 'archive') + make_tarball(base_name, tmpdir) + + # check if the compressed tarball was created + tarball = base_name + '.tar.gz' + self.assert_(os.path.exists(tarball)) + + # trying an uncompressed one + base_name = os.path.join(tmpdir2, 'archive') + make_tarball(base_name, tmpdir, compress=None) + tarball = base_name + '.tar' + self.assert_(os.path.exists(tarball)) + + @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') + def test_make_tarball(self): + # creating something to tar + tmpdir = self.mkdtemp() + self.write_file([tmpdir, 'file1'], 'xxx') + self.write_file([tmpdir, 'file2'], 'xxx') + + tmpdir2 = self.mkdtemp() + base_name = os.path.join(tmpdir2, 'archive') + make_zipfile(base_name, tmpdir) + + # check if the compressed tarball was created + tarball = base_name + '.zip' + + def test_check_archive_formats(self): + self.assertEquals(check_archive_formats(['gztar', 'xxx', 'zip']), + 'xxx') + self.assertEquals(check_archive_formats(['gztar', 'zip']), None) + + def test_make_archive(self): + tmpdir = self.mkdtemp() + base_name = os.path.join(tmpdir, 'archive') + self.assertRaises(ValueError, make_archive, base_name, 'xxx') + +def test_suite(): + return unittest.makeSuite(ArchiveUtilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 535f0f46a86e0b1625dc4bb7ebd6e1c9d89bb2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 14:59:05 +0000 Subject: fixed the test name --- tests/test_archive_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index f5fd9eab..3e1e04a7 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -1,5 +1,5 @@ """Tests for distutils.archive_util.""" -__revision__ = "$Id:$" +__revision__ = "$Id$" import unittest import os @@ -40,7 +40,7 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assert_(os.path.exists(tarball)) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') - def test_make_tarball(self): + def test_make_zipfile(self): # creating something to tar tmpdir = self.mkdtemp() self.write_file([tmpdir, 'file1'], 'xxx') -- cgit v1.2.1 From 445d69ec9e89b0008380697c8337e0b8850d8bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 17 May 2009 15:03:23 +0000 Subject: Merged revisions 72746 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72746 | tarek.ziade | 2009-05-17 16:59:05 +0200 (Sun, 17 May 2009) | 1 line fixed the test name ........ --- tests/test_archive_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index f5fd9eab..3e1e04a7 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -1,5 +1,5 @@ """Tests for distutils.archive_util.""" -__revision__ = "$Id:$" +__revision__ = "$Id$" import unittest import os @@ -40,7 +40,7 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assert_(os.path.exists(tarball)) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') - def test_make_tarball(self): + def test_make_zipfile(self): # creating something to tar tmpdir = self.mkdtemp() self.write_file([tmpdir, 'file1'], 'xxx') -- cgit v1.2.1 From fba15990838934c09b76999ff14350ac8105feef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 18 May 2009 08:03:37 +0000 Subject: Fixed the library extension when distutils build_ext is used inplace --- command/build_ext.py | 6 +++--- tests/test_build_ext.py | 11 ++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 10d50fad..0c77aaa6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -649,7 +649,8 @@ class build_ext (Command): base = modpath[-1] build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) - return os.path.join(package_dir, base) + filename = self.get_ext_filename(ext_name) + return os.path.join(package_dir, filename) else: filename = self.get_ext_filename(ext_name) return os.path.join(self.build_lib, filename) @@ -663,12 +664,11 @@ class build_ext (Command): else: return self.package + '.' + ext_name - def get_ext_filename (self, ext_name): + def get_ext_filename(self, ext_name): r"""Convert the name of an extension (eg. "foo.bar") into the name of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ - from distutils.sysconfig import get_config_var ext_path = string.split(ext_name, '.') # OS/2 has an 8 character module (extension) limit :-( diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 7e3f7165..f1f80bea 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,12 +19,6 @@ from test import test_support # Don't load the xx module more than once. ALREADY_TESTED = False -if sys.platform != 'win32': - UNDER_MSVC8 = False -else: - from distutils.msvccompiler import get_build_version - UNDER_MSVC8 = get_build_version() < 8.0 - def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') return os.path.join(srcdir, 'Modules', 'xxmodule.c') @@ -299,7 +293,6 @@ class BuildExtTestCase(support.TempdirManager, cmd.run() self.assertEquals(cmd.compiler, 'unix') - @unittest.skipIf(UNDER_MSVC8, 'not running this test for MSVC < 8') def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') @@ -329,6 +322,8 @@ class BuildExtTestCase(support.TempdirManager, finally: os.chdir(old_wd) self.assert_(os.path.exists(so_file)) + self.assertEquals(os.path.splitext(so_file)[-1], + sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, other_tmp_dir) @@ -336,6 +331,8 @@ class BuildExtTestCase(support.TempdirManager, cmd.run() so_file = cmd.get_outputs()[0] self.assert_(os.path.exists(so_file)) + self.assertEquals(os.path.splitext(so_file)[-1], + sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, cmd.build_lib) -- cgit v1.2.1 From 86915f4a6cb7244864725cc0c07daceee33fe8f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 18 May 2009 08:05:17 +0000 Subject: Merged revisions 72758 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72758 | tarek.ziade | 2009-05-18 10:03:37 +0200 (Mon, 18 May 2009) | 1 line Fixed the library extension when distutils build_ext is used inplace ........ --- command/build_ext.py | 6 +++--- tests/test_build_ext.py | 10 ++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 01bab366..85935d37 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -635,7 +635,8 @@ class build_ext (Command): base = modpath[-1] build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) - return os.path.join(package_dir, base) + filename = self.get_ext_filename(ext_name) + return os.path.join(package_dir, filename) else: filename = self.get_ext_filename(ext_name) return os.path.join(self.build_lib, filename) @@ -649,12 +650,11 @@ class build_ext (Command): else: return self.package + '.' + ext_name - def get_ext_filename (self, ext_name): + def get_ext_filename(self, ext_name): r"""Convert the name of an extension (eg. "foo.bar") into the name of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ - from distutils.sysconfig import get_config_var ext_path = string.split(ext_name, '.') # OS/2 has an 8 character module (extension) limit :-( diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 21020d39..bd906e35 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -17,12 +17,6 @@ from test import test_support # Don't load the xx module more than once. ALREADY_TESTED = False -if sys.platform != 'win32': - UNDER_MSVC8 = False -else: - from distutils.msvccompiler import get_build_version - UNDER_MSVC8 = get_build_version() < 8.0 - class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -267,6 +261,8 @@ class BuildExtTestCase(support.TempdirManager, finally: os.chdir(old_wd) self.assert_(os.path.exists(so_file)) + self.assertEquals(os.path.splitext(so_file)[-1], + sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, other_tmp_dir) @@ -274,6 +270,8 @@ class BuildExtTestCase(support.TempdirManager, cmd.run() so_file = cmd.get_outputs()[0] self.assert_(os.path.exists(so_file)) + self.assertEquals(os.path.splitext(so_file)[-1], + sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, cmd.build_lib) -- cgit v1.2.1 From 2a6e4aeb17aaaa1618b9fbf66241e91fcd38d755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 18 May 2009 08:07:46 +0000 Subject: Merged revisions 72758 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72758 | tarek.ziade | 2009-05-18 10:03:37 +0200 (Mon, 18 May 2009) | 1 line Fixed the library extension when distutils build_ext is used inplace ........ --- command/build_ext.py | 3 ++- tests/test_build_ext.py | 11 ++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index c39bd72d..eb4cb051 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -636,7 +636,8 @@ class build_ext(Command): base = modpath[-1] build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) - return os.path.join(package_dir, base) + filename = self.get_ext_filename(ext_name) + return os.path.join(package_dir, filename) else: filename = self.get_ext_filename(ext_name) return os.path.join(self.build_lib, filename) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 764aa727..add2923b 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -20,12 +20,6 @@ from test import support # Don't load the xx module more than once. ALREADY_TESTED = False -if sys.platform != 'win32': - UNDER_MSVC8 = False -else: - from distutils.msvccompiler import get_build_version - UNDER_MSVC8 = get_build_version() < 8.0 - def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') return os.path.join(srcdir, 'Modules', 'xxmodule.c') @@ -299,7 +293,6 @@ class BuildExtTestCase(TempdirManager, cmd.run() self.assertEquals(cmd.compiler, 'unix') - @unittest.skipIf(UNDER_MSVC8, 'not running this test for MSVC < 8') def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') @@ -329,6 +322,8 @@ class BuildExtTestCase(TempdirManager, finally: os.chdir(old_wd) self.assert_(os.path.exists(so_file)) + self.assertEquals(os.path.splitext(so_file)[-1], + sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, other_tmp_dir) @@ -336,6 +331,8 @@ class BuildExtTestCase(TempdirManager, cmd.run() so_file = cmd.get_outputs()[0] self.assert_(os.path.exists(so_file)) + self.assertEquals(os.path.splitext(so_file)[-1], + sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, cmd.build_lib) -- cgit v1.2.1 From 081b45d279495030ace791b068945064d38565e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 18 May 2009 08:08:31 +0000 Subject: removed badly merged section --- tests/test_build_ext.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index bd906e35..fcca2f0d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -231,8 +231,6 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(cmd.compiler, 'unix') def test_get_outputs(self): - if UNDER_MSVC8: - return # not running this test for MSVC < 8 tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') self.write_file(c_file, 'void initfoo(void) {};\n') -- cgit v1.2.1 From 339ad8b282de40378695f6858187fe1d78792e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 18 May 2009 08:20:55 +0000 Subject: working with relative paths to avoid tar warnings on absolute paths --- tests/test_archive_util.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 3e1e04a7..1c88457d 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -27,7 +27,14 @@ class ArchiveUtilTestCase(support.TempdirManager, tmpdir2 = self.mkdtemp() base_name = os.path.join(tmpdir2, 'archive') - make_tarball(base_name, tmpdir) + + # working with relative paths to avoid tar warnings + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, '.') + finally: + os.chdir(old_dir) # check if the compressed tarball was created tarball = base_name + '.tar.gz' @@ -35,7 +42,12 @@ class ArchiveUtilTestCase(support.TempdirManager, # trying an uncompressed one base_name = os.path.join(tmpdir2, 'archive') - make_tarball(base_name, tmpdir, compress=None) + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, '.', compress=None) + finally: + os.chdir(old_dir) tarball = base_name + '.tar' self.assert_(os.path.exists(tarball)) -- cgit v1.2.1 From 20cee3dea3f35f7c7b293828339f1b32f138a439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 18 May 2009 08:22:38 +0000 Subject: Merged revisions 72764 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72764 | tarek.ziade | 2009-05-18 10:20:55 +0200 (Mon, 18 May 2009) | 1 line working with relative paths to avoid tar warnings on absolute paths ........ --- tests/test_archive_util.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 3e1e04a7..1c88457d 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -27,7 +27,14 @@ class ArchiveUtilTestCase(support.TempdirManager, tmpdir2 = self.mkdtemp() base_name = os.path.join(tmpdir2, 'archive') - make_tarball(base_name, tmpdir) + + # working with relative paths to avoid tar warnings + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, '.') + finally: + os.chdir(old_dir) # check if the compressed tarball was created tarball = base_name + '.tar.gz' @@ -35,7 +42,12 @@ class ArchiveUtilTestCase(support.TempdirManager, # trying an uncompressed one base_name = os.path.join(tmpdir2, 'archive') - make_tarball(base_name, tmpdir, compress=None) + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, '.', compress=None) + finally: + os.chdir(old_dir) tarball = base_name + '.tar' self.assert_(os.path.exists(tarball)) -- cgit v1.2.1 From 74ddca391d68fdf88f9c8cd920c90b0b00e29952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 18 May 2009 12:21:26 +0000 Subject: Fixed #6053 - win32 fixes for distutils tests --- tests/test_archive_util.py | 8 ++++++-- tests/test_dir_util.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 1c88457d..cabb55bc 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -3,6 +3,7 @@ __revision__ = "$Id$" import unittest import os +from os.path import splitdrive from distutils.archive_util import (check_archive_formats, make_tarball, make_zipfile, make_archive) @@ -26,13 +27,16 @@ class ArchiveUtilTestCase(support.TempdirManager, self.write_file([tmpdir, 'file2'], 'xxx') tmpdir2 = self.mkdtemp() + unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], + "Source and target should be on same drive") + base_name = os.path.join(tmpdir2, 'archive') # working with relative paths to avoid tar warnings old_dir = os.getcwd() os.chdir(tmpdir) try: - make_tarball(base_name, '.') + make_tarball(splitdrive(base_name)[1], '.') finally: os.chdir(old_dir) @@ -45,7 +49,7 @@ class ArchiveUtilTestCase(support.TempdirManager, old_dir = os.getcwd() os.chdir(tmpdir) try: - make_tarball(base_name, '.', compress=None) + make_tarball(splitdrive(base_name)[1], '.', compress=None) finally: os.chdir(old_dir) tarball = base_name + '.tar' diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 9bd6530c..6b22f05f 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -88,7 +88,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): self.assertEquals(ensure_relative('/home/foo'), 'home/foo') self.assertEquals(ensure_relative('some/path'), 'some/path') else: # \\ - self.assertEquals(ensure_relative('c:\\home\\foo'), 'home\\foo') + self.assertEquals(ensure_relative('c:\\home\\foo'), 'c:home\\foo') self.assertEquals(ensure_relative('home\\foo'), 'home\\foo') def test_suite(): -- cgit v1.2.1 From a1f34fda85afa9ae18b3171d03dd258169d3f52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 18 May 2009 12:29:06 +0000 Subject: Merged revisions 72768 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72768 | tarek.ziade | 2009-05-18 14:21:26 +0200 (Mon, 18 May 2009) | 1 line Fixed #6053 - win32 fixes for distutils tests ........ --- tests/test_archive_util.py | 8 ++++++-- tests/test_dir_util.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 1c88457d..cabb55bc 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -3,6 +3,7 @@ __revision__ = "$Id$" import unittest import os +from os.path import splitdrive from distutils.archive_util import (check_archive_formats, make_tarball, make_zipfile, make_archive) @@ -26,13 +27,16 @@ class ArchiveUtilTestCase(support.TempdirManager, self.write_file([tmpdir, 'file2'], 'xxx') tmpdir2 = self.mkdtemp() + unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], + "Source and target should be on same drive") + base_name = os.path.join(tmpdir2, 'archive') # working with relative paths to avoid tar warnings old_dir = os.getcwd() os.chdir(tmpdir) try: - make_tarball(base_name, '.') + make_tarball(splitdrive(base_name)[1], '.') finally: os.chdir(old_dir) @@ -45,7 +49,7 @@ class ArchiveUtilTestCase(support.TempdirManager, old_dir = os.getcwd() os.chdir(tmpdir) try: - make_tarball(base_name, '.', compress=None) + make_tarball(splitdrive(base_name)[1], '.', compress=None) finally: os.chdir(old_dir) tarball = base_name + '.tar' diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 9bd6530c..6b22f05f 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -88,7 +88,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): self.assertEquals(ensure_relative('/home/foo'), 'home/foo') self.assertEquals(ensure_relative('some/path'), 'some/path') else: # \\ - self.assertEquals(ensure_relative('c:\\home\\foo'), 'home\\foo') + self.assertEquals(ensure_relative('c:\\home\\foo'), 'c:home\\foo') self.assertEquals(ensure_relative('home\\foo'), 'home\\foo') def test_suite(): -- cgit v1.2.1 From f8d02d79647a15807b50a43f1a4614f0fdf288ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 19 May 2009 16:17:21 +0000 Subject: fixed the 'package' option of build_ext --- command/build_ext.py | 24 +++++++++++++----------- tests/test_build_ext.py | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 0c77aaa6..293c214d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -642,19 +642,21 @@ class build_ext (Command): The file is located in `build_lib` or directly in the package (inplace option). """ - if self.inplace: - fullname = self.get_ext_fullname(ext_name) - modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] - build_py = self.get_finalized_command('build_py') - package_dir = os.path.abspath(build_py.get_package_dir(package)) - filename = self.get_ext_filename(ext_name) - return os.path.join(package_dir, filename) - else: - filename = self.get_ext_filename(ext_name) + fullname = self.get_ext_fullname(ext_name) + filename = self.get_ext_filename(fullname) + if not self.inplace: + # no further work needed return os.path.join(self.build_lib, filename) + # the inplace option requires to find the package directory + # using the build_py command + modpath = fullname.split('.') + package = '.'.join(modpath[0:-1]) + base = modpath[-1] + build_py = self.get_finalized_command('build_py') + package_dir = os.path.abspath(build_py.get_package_dir(package)) + return os.path.join(package_dir, filename) + def get_ext_fullname(self, ext_name): """Returns the fullname of a given extension name. diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index f1f80bea..12b8581c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -336,6 +336,28 @@ class BuildExtTestCase(support.TempdirManager, so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, cmd.build_lib) + # inplace = 0, cmd.package = 'bar' + cmd.package = 'bar' + path = cmd.get_ext_fullpath('foo') + # checking that the last directory is bar + path = os.path.split(path)[0] + lastdir = os.path.split(path)[-1] + self.assertEquals(lastdir, cmd.package) + + # inplace = 1, cmd.package = 'bar' + cmd.inplace = 1 + other_tmp_dir = os.path.realpath(self.mkdtemp()) + old_wd = os.getcwd() + os.chdir(other_tmp_dir) + try: + path = cmd.get_ext_fullpath('foo') + finally: + os.chdir(old_wd) + # checking that the last directory is bar + path = os.path.split(path)[0] + lastdir = os.path.split(path)[-1] + self.assertEquals(lastdir, cmd.package) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 7a61abeaf1519c93e89032e34b44b0a311015ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 19 May 2009 16:19:15 +0000 Subject: Merged revisions 72781 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72781 | tarek.ziade | 2009-05-19 18:17:21 +0200 (Tue, 19 May 2009) | 1 line fixed the 'package' option of build_ext ........ --- command/build_ext.py | 24 +++++++++++++----------- tests/test_build_ext.py | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 85935d37..e6e33ab9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -628,19 +628,21 @@ class build_ext (Command): The file is located in `build_lib` or directly in the package (inplace option). """ - if self.inplace: - fullname = self.get_ext_fullname(ext_name) - modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] - build_py = self.get_finalized_command('build_py') - package_dir = os.path.abspath(build_py.get_package_dir(package)) - filename = self.get_ext_filename(ext_name) - return os.path.join(package_dir, filename) - else: - filename = self.get_ext_filename(ext_name) + fullname = self.get_ext_fullname(ext_name) + filename = self.get_ext_filename(fullname) + if not self.inplace: + # no further work needed return os.path.join(self.build_lib, filename) + # the inplace option requires to find the package directory + # using the build_py command + modpath = fullname.split('.') + package = '.'.join(modpath[0:-1]) + base = modpath[-1] + build_py = self.get_finalized_command('build_py') + package_dir = os.path.abspath(build_py.get_package_dir(package)) + return os.path.join(package_dir, filename) + def get_ext_fullname(self, ext_name): """Returns the fullname of a given extension name. diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index fcca2f0d..dc8df98f 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -273,6 +273,28 @@ class BuildExtTestCase(support.TempdirManager, so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, cmd.build_lib) + # inplace = 0, cmd.package = 'bar' + cmd.package = 'bar' + path = cmd.get_ext_fullpath('foo') + # checking that the last directory is bar + path = os.path.split(path)[0] + lastdir = os.path.split(path)[-1] + self.assertEquals(lastdir, cmd.package) + + # inplace = 1, cmd.package = 'bar' + cmd.inplace = 1 + other_tmp_dir = os.path.realpath(self.mkdtemp()) + old_wd = os.getcwd() + os.chdir(other_tmp_dir) + try: + path = cmd.get_ext_fullpath('foo') + finally: + os.chdir(old_wd) + # checking that the last directory is bar + path = os.path.split(path)[0] + lastdir = os.path.split(path)[-1] + self.assertEquals(lastdir, cmd.package) + def test_suite(): if not sysconfig.python_build: if test_support.verbose: -- cgit v1.2.1 From 92ad4a19caeac451803ca1737b0d5514497e5be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 19 May 2009 16:22:57 +0000 Subject: Merged revisions 72781 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72781 | tarek.ziade | 2009-05-19 18:17:21 +0200 (Tue, 19 May 2009) | 1 line fixed the 'package' option of build_ext ........ --- command/build_ext.py | 24 +++++++++++++----------- tests/test_build_ext.py | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index eb4cb051..31e036bc 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -629,19 +629,21 @@ class build_ext(Command): The file is located in `build_lib` or directly in the package (inplace option). """ - if self.inplace: - fullname = self.get_ext_fullname(ext_name) - modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] - build_py = self.get_finalized_command('build_py') - package_dir = os.path.abspath(build_py.get_package_dir(package)) - filename = self.get_ext_filename(ext_name) - return os.path.join(package_dir, filename) - else: - filename = self.get_ext_filename(ext_name) + fullname = self.get_ext_fullname(ext_name) + filename = self.get_ext_filename(fullname) + if not self.inplace: + # no further work needed return os.path.join(self.build_lib, filename) + # the inplace option requires to find the package directory + # using the build_py command + modpath = fullname.split('.') + package = '.'.join(modpath[0:-1]) + base = modpath[-1] + build_py = self.get_finalized_command('build_py') + package_dir = os.path.abspath(build_py.get_package_dir(package)) + return os.path.join(package_dir, filename) + def get_ext_fullname(self, ext_name): """Returns the fullname of a given extension name. diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index add2923b..4ea11a15 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -336,6 +336,28 @@ class BuildExtTestCase(TempdirManager, so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, cmd.build_lib) + # inplace = 0, cmd.package = 'bar' + cmd.package = 'bar' + path = cmd.get_ext_fullpath('foo') + # checking that the last directory is bar + path = os.path.split(path)[0] + lastdir = os.path.split(path)[-1] + self.assertEquals(lastdir, cmd.package) + + # inplace = 1, cmd.package = 'bar' + cmd.inplace = 1 + other_tmp_dir = os.path.realpath(self.mkdtemp()) + old_wd = os.getcwd() + os.chdir(other_tmp_dir) + try: + path = cmd.get_ext_fullpath('foo') + finally: + os.chdir(old_wd) + # checking that the last directory is bar + path = os.path.split(path)[0] + lastdir = os.path.split(path)[-1] + self.assertEquals(lastdir, cmd.package) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 0b15216368f28f9178947b1f2df283b73f4a5633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 22 May 2009 09:42:43 +0000 Subject: fixed encoding --- command/bdist_msi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 18bddeb4..4272b818 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -1,5 +1,5 @@ -# -*- coding: iso-8859-1 -*- -# Copyright (C) 2005, 2006 Martin v. Löwis +# -*- coding: utf-8 -*- +# Copyright (C) 2005, 2006 Martin von Löwis # Licensed to PSF under a Contributor Agreement. # The bdist_wininst command proper # based on bdist_wininst -- cgit v1.2.1 From a81606b89db3da71c18ace79793438e7abb4aa42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 24 May 2009 19:10:52 +0000 Subject: Issue #6065: Do not try to build a version-independent installer if the package has extension modules. Also add NEWS entry for #5311. --- command/bdist_msi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 4272b818..b42e41b3 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -141,6 +141,8 @@ class bdist_msi (Command): bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'msi') short_version = get_python_version() + if (not self.target_version) and self.distribution.has_ext_modules(): + self.target_version = short_version if self.target_version: self.versions = [self.target_version] if not self.skip_build and self.distribution.has_ext_modules()\ -- cgit v1.2.1 From 0c3b40e164295dbd4282d6ad7675934758d6e36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 24 May 2009 19:19:17 +0000 Subject: Merged revisions 72891 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72891 | martin.v.loewis | 2009-05-24 21:10:52 +0200 (So, 24 Mai 2009) | 5 lines Issue #6065: Do not try to build a version-independent installer if the package has extension modules. Also add NEWS entry for #5311. ........ --- command/bdist_msi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 96451586..bca98cc2 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -141,6 +141,8 @@ class bdist_msi(Command): bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'msi') short_version = get_python_version() + if (not self.target_version) and self.distribution.has_ext_modules(): + self.target_version = short_version if self.target_version: self.versions = [self.target_version] if not self.skip_build and self.distribution.has_ext_modules()\ -- cgit v1.2.1 From 8488006f14d1c8008e2019f673a002e18062ff1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 28 May 2009 12:53:54 +0000 Subject: Fixed #6048: Distutils uses the tarfile module instead of the tar command now --- archive_util.py | 58 ++++++++++++++--------- tests/test_archive_util.py | 113 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 146 insertions(+), 25 deletions(-) diff --git a/archive_util.py b/archive_util.py index f5959f5e..62150b0d 100644 --- a/archive_util.py +++ b/archive_util.py @@ -6,6 +6,9 @@ that sort of thing).""" __revision__ = "$Id$" import os +from warnings import warn +import sys + from distutils.errors import DistutilsExecError from distutils.spawn import spawn from distutils.dir_util import mkpath @@ -22,36 +25,45 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): the appropriate compression extension (".gz", ".bz2" or ".Z"). Returns the output filename. """ - # XXX GNU tar 1.13 has a nifty option to add a prefix directory. - # It's pretty new, though, so we certainly can't require it -- - # but it would be nice to take advantage of it to skip the - # "create a tree of hardlinks" step! (Would also be nice to - # detect GNU tar to use its 'z' option and save a step.) - - compress_ext = {'gzip': ".gz", - 'bzip2': '.bz2', - 'compress': ".Z" } + tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''} + compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'compress': '.Z'} # flags for compression program, each element of list will be an argument - compress_flags = {'gzip': ["-f9"], - 'compress': ["-f"], - 'bzip2': ['-f9']} - if compress is not None and compress not in compress_ext.keys(): raise ValueError, \ - "bad value for 'compress': must be None, 'gzip', or 'compress'" + ("bad value for 'compress': must be None, 'gzip', 'bzip2' " + "or 'compress'") + + archive_name = base_name + '.tar' + if compress != 'compress': + archive_name += compress_ext.get(compress, '') - archive_name = base_name + ".tar" mkpath(os.path.dirname(archive_name), dry_run=dry_run) - cmd = ["tar", "-cf", archive_name, base_dir] - spawn(cmd, dry_run=dry_run) - if compress: - spawn([compress] + compress_flags[compress] + [archive_name], - dry_run=dry_run) - return archive_name + compress_ext[compress] - else: - return archive_name + # creating the tarball + import tarfile # late import so Python build itself doesn't break + + log.info('Creating tar archive') + if not dry_run: + tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) + try: + tar.add(base_dir) + finally: + tar.close() + + # compression using `compress` + if compress == 'compress': + warn("'compress' will be deprecated.", PendingDeprecationWarning) + # the option varies depending on the platform + compressed_name = archive_name + compress_ext[compress] + if sys.platform == 'win32': + cmd = [compress, archive_name, compressed_name] + else: + cmd = [compress, '-f', archive_name] + spawn(cmd, dry_run=dry_run) + return compressed_name + + return archive_name def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): """Create a zip file from all the files under 'base_dir'. diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index cabb55bc..2b241526 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -3,12 +3,15 @@ __revision__ = "$Id$" import unittest import os +import tarfile from os.path import splitdrive +import warnings from distutils.archive_util import (check_archive_formats, make_tarball, make_zipfile, make_archive) -from distutils.spawn import find_executable +from distutils.spawn import find_executable, spawn from distutils.tests import support +from test.test_support import check_warnings try: import zipfile @@ -19,12 +22,13 @@ except ImportError: class ArchiveUtilTestCase(support.TempdirManager, unittest.TestCase): - @unittest.skipUnless(find_executable('tar'), 'Need the tar command to run') def test_make_tarball(self): # creating something to tar tmpdir = self.mkdtemp() self.write_file([tmpdir, 'file1'], 'xxx') self.write_file([tmpdir, 'file2'], 'xxx') + os.mkdir(os.path.join(tmpdir, 'sub')) + self.write_file([tmpdir, 'sub', 'file3'], 'xxx') tmpdir2 = self.mkdtemp() unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], @@ -55,6 +59,111 @@ class ArchiveUtilTestCase(support.TempdirManager, tarball = base_name + '.tar' self.assert_(os.path.exists(tarball)) + def _tarinfo(self, path): + tar = tarfile.open(path) + try: + names = tar.getnames() + names.sort() + return tuple(names) + finally: + tar.close() + + def _create_files(self): + # creating something to tar + tmpdir = self.mkdtemp() + dist = os.path.join(tmpdir, 'dist') + os.mkdir(dist) + self.write_file([dist, 'file1'], 'xxx') + self.write_file([dist, 'file2'], 'xxx') + os.mkdir(os.path.join(dist, 'sub')) + self.write_file([dist, 'sub', 'file3'], 'xxx') + os.mkdir(os.path.join(dist, 'sub2')) + tmpdir2 = self.mkdtemp() + base_name = os.path.join(tmpdir2, 'archive') + return tmpdir, tmpdir2, base_name + + @unittest.skipUnless(find_executable('tar'), 'Need the tar command to run') + def test_tarfile_vs_tar(self): + tmpdir, tmpdir2, base_name = self._create_files() + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist') + finally: + os.chdir(old_dir) + + # check if the compressed tarball was created + tarball = base_name + '.tar.gz' + self.assert_(os.path.exists(tarball)) + + # now create another tarball using `tar` + tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') + cmd = ['tar', '-czf', 'archive2.tar.gz', 'dist'] + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + spawn(cmd) + finally: + os.chdir(old_dir) + + self.assert_(os.path.exists(tarball2)) + # let's compare both tarballs + self.assertEquals(self._tarinfo(tarball), self._tarinfo(tarball2)) + + # trying an uncompressed one + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist', compress=None) + finally: + os.chdir(old_dir) + tarball = base_name + '.tar' + self.assert_(os.path.exists(tarball)) + + # now for a dry_run + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist', compress=None, dry_run=True) + finally: + os.chdir(old_dir) + tarball = base_name + '.tar' + self.assert_(os.path.exists(tarball)) + + @unittest.skipUnless(find_executable('compress'), + 'The compress program is required') + def test_compress_deprecated(self): + tmpdir, tmpdir2, base_name = self._create_files() + + # using compress and testing the PendingDeprecationWarning + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + with check_warnings() as w: + warnings.simplefilter("always") + make_tarball(base_name, 'dist', compress='compress') + finally: + os.chdir(old_dir) + tarball = base_name + '.tar.Z' + self.assert_(os.path.exists(tarball)) + self.assertEquals(len(w.warnings), 1) + + # same test with dry_run + os.remove(tarball) + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + with check_warnings() as w: + warnings.simplefilter("always") + make_tarball(base_name, 'dist', compress='compress', + dry_run=True) + finally: + os.chdir(old_dir) + self.assert_(not os.path.exists(tarball)) + self.assertEquals(len(w.warnings), 1) + @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile(self): # creating something to tar -- cgit v1.2.1 From f8af27c626419e60e9f6df824d2fb96f9ee8d2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 28 May 2009 13:01:13 +0000 Subject: Merged revisions 72981 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72981 | tarek.ziade | 2009-05-28 14:53:54 +0200 (Thu, 28 May 2009) | 1 line Fixed #6048: Distutils uses the tarfile module instead of the tar command now ........ --- archive_util.py | 58 ++++++++++++++--------- tests/test_archive_util.py | 113 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 146 insertions(+), 25 deletions(-) diff --git a/archive_util.py b/archive_util.py index 08a9e569..a568854e 100644 --- a/archive_util.py +++ b/archive_util.py @@ -6,6 +6,9 @@ that sort of thing).""" __revision__ = "$Id$" import os +from warnings import warn +import sys + from distutils.errors import DistutilsExecError from distutils.spawn import spawn from distutils.dir_util import mkpath @@ -22,36 +25,45 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): the appropriate compression extension (".gz", ".bz2" or ".Z"). Returns the output filename. """ - # XXX GNU tar 1.13 has a nifty option to add a prefix directory. - # It's pretty new, though, so we certainly can't require it -- - # but it would be nice to take advantage of it to skip the - # "create a tree of hardlinks" step! (Would also be nice to - # detect GNU tar to use its 'z' option and save a step.) - - compress_ext = {'gzip': ".gz", - 'bzip2': '.bz2', - 'compress': ".Z" } + tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''} + compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'compress': '.Z'} # flags for compression program, each element of list will be an argument - compress_flags = {'gzip': ["-f9"], - 'compress': ["-f"], - 'bzip2': ['-f9']} - if compress is not None and compress not in compress_ext.keys(): raise ValueError( - "bad value for 'compress': must be None, 'gzip', or 'compress'") + "bad value for 'compress': must be None, 'gzip', 'bzip2' " + "or 'compress'") + + archive_name = base_name + '.tar' + if compress != 'compress': + archive_name += compress_ext.get(compress, '') - archive_name = base_name + ".tar" mkpath(os.path.dirname(archive_name), dry_run=dry_run) - cmd = ["tar", "-cf", archive_name, base_dir] - spawn(cmd, dry_run=dry_run) - if compress: - spawn([compress] + compress_flags[compress] + [archive_name], - dry_run=dry_run) - return archive_name + compress_ext[compress] - else: - return archive_name + # creating the tarball + import tarfile # late import so Python build itself doesn't break + + log.info('Creating tar archive') + if not dry_run: + tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) + try: + tar.add(base_dir) + finally: + tar.close() + + # compression using `compress` + if compress == 'compress': + warn("'compress' will be deprecated.", PendingDeprecationWarning) + # the option varies depending on the platform + compressed_name = archive_name + compress_ext[compress] + if sys.platform == 'win32': + cmd = [compress, archive_name, compressed_name] + else: + cmd = [compress, '-f', archive_name] + spawn(cmd, dry_run=dry_run) + return compressed_name + + return archive_name def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): """Create a zip file from all the files under 'base_dir'. diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index cabb55bc..5db9a5d0 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -3,12 +3,15 @@ __revision__ = "$Id$" import unittest import os +import tarfile from os.path import splitdrive +import warnings from distutils.archive_util import (check_archive_formats, make_tarball, make_zipfile, make_archive) -from distutils.spawn import find_executable +from distutils.spawn import find_executable, spawn from distutils.tests import support +from test.support import check_warnings try: import zipfile @@ -19,12 +22,13 @@ except ImportError: class ArchiveUtilTestCase(support.TempdirManager, unittest.TestCase): - @unittest.skipUnless(find_executable('tar'), 'Need the tar command to run') def test_make_tarball(self): # creating something to tar tmpdir = self.mkdtemp() self.write_file([tmpdir, 'file1'], 'xxx') self.write_file([tmpdir, 'file2'], 'xxx') + os.mkdir(os.path.join(tmpdir, 'sub')) + self.write_file([tmpdir, 'sub', 'file3'], 'xxx') tmpdir2 = self.mkdtemp() unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], @@ -55,6 +59,111 @@ class ArchiveUtilTestCase(support.TempdirManager, tarball = base_name + '.tar' self.assert_(os.path.exists(tarball)) + def _tarinfo(self, path): + tar = tarfile.open(path) + try: + names = tar.getnames() + names.sort() + return tuple(names) + finally: + tar.close() + + def _create_files(self): + # creating something to tar + tmpdir = self.mkdtemp() + dist = os.path.join(tmpdir, 'dist') + os.mkdir(dist) + self.write_file([dist, 'file1'], 'xxx') + self.write_file([dist, 'file2'], 'xxx') + os.mkdir(os.path.join(dist, 'sub')) + self.write_file([dist, 'sub', 'file3'], 'xxx') + os.mkdir(os.path.join(dist, 'sub2')) + tmpdir2 = self.mkdtemp() + base_name = os.path.join(tmpdir2, 'archive') + return tmpdir, tmpdir2, base_name + + @unittest.skipUnless(find_executable('tar'), 'Need the tar command to run') + def test_tarfile_vs_tar(self): + tmpdir, tmpdir2, base_name = self._create_files() + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist') + finally: + os.chdir(old_dir) + + # check if the compressed tarball was created + tarball = base_name + '.tar.gz' + self.assert_(os.path.exists(tarball)) + + # now create another tarball using `tar` + tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') + cmd = ['tar', '-czf', 'archive2.tar.gz', 'dist'] + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + spawn(cmd) + finally: + os.chdir(old_dir) + + self.assert_(os.path.exists(tarball2)) + # let's compare both tarballs + self.assertEquals(self._tarinfo(tarball), self._tarinfo(tarball2)) + + # trying an uncompressed one + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist', compress=None) + finally: + os.chdir(old_dir) + tarball = base_name + '.tar' + self.assert_(os.path.exists(tarball)) + + # now for a dry_run + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist', compress=None, dry_run=True) + finally: + os.chdir(old_dir) + tarball = base_name + '.tar' + self.assert_(os.path.exists(tarball)) + + @unittest.skipUnless(find_executable('compress'), + 'The compress program is required') + def test_compress_deprecated(self): + tmpdir, tmpdir2, base_name = self._create_files() + + # using compress and testing the PendingDeprecationWarning + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + with check_warnings() as w: + warnings.simplefilter("always") + make_tarball(base_name, 'dist', compress='compress') + finally: + os.chdir(old_dir) + tarball = base_name + '.tar.Z' + self.assert_(os.path.exists(tarball)) + self.assertEquals(len(w.warnings), 1) + + # same test with dry_run + os.remove(tarball) + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + with check_warnings() as w: + warnings.simplefilter("always") + make_tarball(base_name, 'dist', compress='compress', + dry_run=True) + finally: + os.chdir(old_dir) + self.assert_(not os.path.exists(tarball)) + self.assertEquals(len(w.warnings), 1) + @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile(self): # creating something to tar -- cgit v1.2.1 From 3917b17a0342869c54a033eb234c298b4e19a688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 28 May 2009 13:55:51 +0000 Subject: using 'tar' then 'gzip' in the test, because 'tar -czf' is not supported under some platforms --- tests/test_archive_util.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 2b241526..29cd271f 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -20,6 +20,7 @@ except ImportError: ZIP_SUPPORT = find_executable('zip') class ArchiveUtilTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_make_tarball(self): @@ -82,7 +83,8 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir2, 'archive') return tmpdir, tmpdir2, base_name - @unittest.skipUnless(find_executable('tar'), 'Need the tar command to run') + @unittest.skipUnless(find_executable('tar') and find_executable('gzip'), + 'Need the tar command to run') def test_tarfile_vs_tar(self): tmpdir, tmpdir2, base_name = self._create_files() old_dir = os.getcwd() @@ -98,11 +100,13 @@ class ArchiveUtilTestCase(support.TempdirManager, # now create another tarball using `tar` tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') - cmd = ['tar', '-czf', 'archive2.tar.gz', 'dist'] + tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist'] + gzip_cmd = ['gzip', '-f9', 'archive2.tar'] old_dir = os.getcwd() os.chdir(tmpdir) try: - spawn(cmd) + spawn(tar_cmd) + spawn(gzip_cmd) finally: os.chdir(old_dir) -- cgit v1.2.1 From 3418332f7bce309b6d404bd8e1a9775ea180e93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 28 May 2009 14:02:58 +0000 Subject: Merged revisions 72986 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r72986 | tarek.ziade | 2009-05-28 15:55:51 +0200 (Thu, 28 May 2009) | 1 line using 'tar' then 'gzip' in the test, because 'tar -czf' is not supported under some platforms ........ --- tests/test_archive_util.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 5db9a5d0..663b3353 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -20,6 +20,7 @@ except ImportError: ZIP_SUPPORT = find_executable('zip') class ArchiveUtilTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_make_tarball(self): @@ -82,7 +83,8 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir2, 'archive') return tmpdir, tmpdir2, base_name - @unittest.skipUnless(find_executable('tar'), 'Need the tar command to run') + @unittest.skipUnless(find_executable('tar') and find_executable('gzip'), + 'Need the tar command to run') def test_tarfile_vs_tar(self): tmpdir, tmpdir2, base_name = self._create_files() old_dir = os.getcwd() @@ -98,11 +100,13 @@ class ArchiveUtilTestCase(support.TempdirManager, # now create another tarball using `tar` tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') - cmd = ['tar', '-czf', 'archive2.tar.gz', 'dist'] + tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist'] + gzip_cmd = ['gzip', '-f9', 'archive2.tar'] old_dir = os.getcwd() os.chdir(tmpdir) try: - spawn(cmd) + spawn(tar_cmd) + spawn(gzip_cmd) finally: os.chdir(old_dir) -- cgit v1.2.1 From c9712ebc8749a8217c58091b11cf21f707a1a42c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 29 May 2009 08:08:07 +0000 Subject: Fixed #6131: test_modulefinder leaked when run after test_distutils --- tests/test_bdist_dumb.py | 6 ++---- tests/test_dir_util.py | 4 ++-- tests/test_file_util.py | 4 ++-- tests/test_register.py | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 2863b620..d2ea201b 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -22,16 +22,14 @@ class BuildDumbTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): - support.TempdirManager.setUp(self) - support.LoggingSilencer.setUp(self) + super(BuildDumbTestCase, self).setUp() self.old_location = os.getcwd() self.old_sys_argv = sys.argv[:] def tearDown(self): os.chdir(self.old_location) sys.argv = self.old_sys_argv[:] - support.LoggingSilencer.tearDown(self) - support.TempdirManager.tearDown(self) + super(BuildDumbTestCase, self).tearDown() def test_simple_built(self): diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 6b22f05f..0f694aa0 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -18,7 +18,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): self._logs.append(msg) def setUp(self): - support.TempdirManager.setUp(self) + super(DirUtilTestCase, self).setUp() self._logs = [] tmp_dir = self.mkdtemp() self.root_target = os.path.join(tmp_dir, 'deep') @@ -29,7 +29,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): def tearDown(self): log.info = self.old_log - support.TempdirManager.tearDown(self) + super(DirUtilTestCase, self).tearDown() def test_mkpath_remove_tree_verbosity(self): diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 9373af85..fac4a5d1 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -16,7 +16,7 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): self._logs.append(msg) def setUp(self): - support.TempdirManager.setUp(self) + super(FileUtilTestCase, self).setUp() self._logs = [] self.old_log = log.info log.info = self._log @@ -27,7 +27,7 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): def tearDown(self): log.info = self.old_log - support.TempdirManager.tearDown(self) + super(FileUtilTestCase, self).tearDown() def test_move_file_verbosity(self): f = open(self.source, 'w') diff --git a/tests/test_register.py b/tests/test_register.py index 91d35813..0c6bf669 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -66,7 +66,7 @@ class FakeOpener(object): class RegisterTestCase(PyPIRCCommandTestCase): def setUp(self): - PyPIRCCommandTestCase.setUp(self) + super(RegisterTestCase, self).setUp() # patching the password prompt self._old_getpass = getpass.getpass def _getpass(prompt): @@ -78,7 +78,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): def tearDown(self): getpass.getpass = self._old_getpass urllib2.build_opener = self.old_opener - PyPIRCCommandTestCase.tearDown(self) + super(RegisterTestCase, self).tearDown() def _get_cmd(self, metadata=None): if metadata is None: -- cgit v1.2.1 From f9b04c5e43bf858c654b1557447ed19051bd85a4 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Fri, 29 May 2009 09:14:04 +0000 Subject: Merged revisions 73008 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73008 | tarek.ziade | 2009-05-29 17:08:07 +0900 | 1 line Fixed #6131: test_modulefinder leaked when run after test_distutils ........ --- tests/test_bdist_dumb.py | 6 ++---- tests/test_dir_util.py | 4 ++-- tests/test_file_util.py | 4 ++-- tests/test_register.py | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 2863b620..d2ea201b 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -22,16 +22,14 @@ class BuildDumbTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): - support.TempdirManager.setUp(self) - support.LoggingSilencer.setUp(self) + super(BuildDumbTestCase, self).setUp() self.old_location = os.getcwd() self.old_sys_argv = sys.argv[:] def tearDown(self): os.chdir(self.old_location) sys.argv = self.old_sys_argv[:] - support.LoggingSilencer.tearDown(self) - support.TempdirManager.tearDown(self) + super(BuildDumbTestCase, self).tearDown() def test_simple_built(self): diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 6b22f05f..0f694aa0 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -18,7 +18,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): self._logs.append(msg) def setUp(self): - support.TempdirManager.setUp(self) + super(DirUtilTestCase, self).setUp() self._logs = [] tmp_dir = self.mkdtemp() self.root_target = os.path.join(tmp_dir, 'deep') @@ -29,7 +29,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): def tearDown(self): log.info = self.old_log - support.TempdirManager.tearDown(self) + super(DirUtilTestCase, self).tearDown() def test_mkpath_remove_tree_verbosity(self): diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 9373af85..fac4a5d1 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -16,7 +16,7 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): self._logs.append(msg) def setUp(self): - support.TempdirManager.setUp(self) + super(FileUtilTestCase, self).setUp() self._logs = [] self.old_log = log.info log.info = self._log @@ -27,7 +27,7 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): def tearDown(self): log.info = self.old_log - support.TempdirManager.tearDown(self) + super(FileUtilTestCase, self).tearDown() def test_move_file_verbosity(self): f = open(self.source, 'w') diff --git a/tests/test_register.py b/tests/test_register.py index f486cf79..d9ff3ec4 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -66,7 +66,7 @@ class FakeOpener(object): class RegisterTestCase(PyPIRCCommandTestCase): def setUp(self): - PyPIRCCommandTestCase.setUp(self) + super(RegisterTestCase, self).setUp() # patching the password prompt self._old_getpass = getpass.getpass def _getpass(prompt): @@ -78,7 +78,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): def tearDown(self): getpass.getpass = self._old_getpass urllib.request.build_opener = self.old_opener - PyPIRCCommandTestCase.tearDown(self) + super(RegisterTestCase, self).tearDown() def _get_cmd(self, metadata=None): if metadata is None: -- cgit v1.2.1 From 8e668444d71672740d52dfac4d6d87a89f43ab7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 29 May 2009 10:04:06 +0000 Subject: using super in distutils' sdistTestCase --- tests/test_sdist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 0d839b5c..59c24fd4 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,14 +29,14 @@ recursive-include somecode * class sdistTestCase(PyPIRCCommandTestCase): def setUp(self): - PyPIRCCommandTestCase.setUp(self) + super(sdistTestCase, self).setUp() self.old_path = os.getcwd() def tearDown(self): os.chdir(self.old_path) if os.path.exists(TEMP_PKG): shutil.rmtree(TEMP_PKG) - PyPIRCCommandTestCase.tearDown(self) + super(sdistTestCase, self).tearDown() def _init_tmp_pkg(self): if os.path.exists(TEMP_PKG): -- cgit v1.2.1 From 13e74436fe53eb88f4f20555c16df795b16987fc Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 30 May 2009 15:30:16 +0000 Subject: bump to 3.1rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 80ccf611..5a5db532 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1b1" +__version__ = "3.1rc1" #--end constants-- -- cgit v1.2.1 From 49443195f5ba2fb76c120d5218b962cb32b2fc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 1 Jun 2009 22:22:13 +0000 Subject: improved distutils.dist test coverage, pep-8 compliancy --- dist.py | 107 ++++++++++++++++++++++++++--------------------------- tests/test_dist.py | 57 +++++++++++++++++++++++++--- 2 files changed, 103 insertions(+), 61 deletions(-) diff --git a/dist.py b/dist.py index 93325b97..f4aecb41 100644 --- a/dist.py +++ b/dist.py @@ -6,8 +6,7 @@ being built/installed/distributed. __revision__ = "$Id$" -import sys, os, string, re -from types import * +import sys, os, re try: import warnings @@ -251,7 +250,7 @@ Common commands: (see '--help-commands' for more) # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! - for (key,val) in attrs.items(): + for (key, val) in attrs.items(): if hasattr(self.metadata, "set_" + key): getattr(self.metadata, "set_" + key)(val) elif hasattr(self.metadata, key): @@ -286,22 +285,24 @@ Common commands: (see '--help-commands' for more) commands.sort() if header is not None: - print indent + header + self.announce(indent + header) indent = indent + " " if not commands: - print indent + "no commands known yet" + self.announce(indent + "no commands known yet") return for cmd_name in commands: opt_dict = self.command_options.get(cmd_name) if opt_dict is None: - print indent + "no option dict for '%s' command" % cmd_name + self.announce(indent + + "no option dict for '%s' command" % cmd_name) else: - print indent + "option dict for '%s' command:" % cmd_name + self.announce(indent + + "option dict for '%s' command:" % cmd_name) out = pformat(opt_dict) - for line in string.split(out, "\n"): - print indent + " " + line + for line in out.split('\n'): + self.announce(indent + " " + line) # -- Config file finding/parsing methods --------------------------- @@ -352,11 +353,13 @@ Common commands: (see '--help-commands' for more) if filenames is None: filenames = self.find_config_files() - if DEBUG: print "Distribution.parse_config_files():" + if DEBUG: + self.announce("Distribution.parse_config_files():") parser = ConfigParser() for filename in filenames: - if DEBUG: print " reading", filename + if DEBUG: + self.announce(" reading", filename) parser.read(filename) for section in parser.sections(): options = parser.options(section) @@ -365,7 +368,7 @@ Common commands: (see '--help-commands' for more) for opt in options: if opt != '__name__': val = parser.get(section,opt) - opt = string.replace(opt, '-', '_') + opt = opt.replace('-', '_') opt_dict[opt] = (filename, val) # Make the ConfigParser forget everything (so we retain @@ -503,7 +506,7 @@ Common commands: (see '--help-commands' for more) # Also make sure that the command object provides a list of its # known options. if not (hasattr(cmd_class, 'user_options') and - type(cmd_class.user_options) is ListType): + isinstance(cmd_class.user_options, list)): raise DistutilsClassError, \ ("command class %s must provide " + "'user_options' attribute (a list of tuples)") % \ @@ -519,7 +522,7 @@ Common commands: (see '--help-commands' for more) # Check for help_options in command class. They have a different # format (tuple of four) so we need to preprocess them here. if (hasattr(cmd_class, 'help_options') and - type(cmd_class.help_options) is ListType): + isinstance(cmd_class.help_options, list)): help_options = fix_help_options(cmd_class.help_options) else: help_options = [] @@ -537,14 +540,11 @@ Common commands: (see '--help-commands' for more) return if (hasattr(cmd_class, 'help_options') and - type(cmd_class.help_options) is ListType): + isinstance(cmd_class.help_options, list)): help_option_found=0 for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): help_option_found=1 - #print "showing help for option %s of command %s" % \ - # (help_option[0],cmd_class) - if callable(func): func() else: @@ -569,17 +569,13 @@ Common commands: (see '--help-commands' for more) instance, analogous to the .finalize_options() method of Command objects. """ - keywords = self.metadata.keywords - if keywords is not None: - if type(keywords) is StringType: - keywordlist = string.split(keywords, ',') - self.metadata.keywords = map(string.strip, keywordlist) - - platforms = self.metadata.platforms - if platforms is not None: - if type(platforms) is StringType: - platformlist = string.split(platforms, ',') - self.metadata.platforms = map(string.strip, platformlist) + for attr in ('keywords', 'platforms'): + value = getattr(self.metadata, attr) + if value is None: + continue + if isinstance(value, str): + value = [elm.strip() for elm in value.split(',')] + setattr(self.metadata, attr, value) def _show_help(self, parser, global_options=1, display_options=1, commands=[]): @@ -606,31 +602,30 @@ Common commands: (see '--help-commands' for more) options = self.global_options parser.set_option_table(options) parser.print_help(self.common_usage + "\nGlobal options:") - print + self.announce('') if display_options: parser.set_option_table(self.display_options) parser.print_help( "Information display options (just display " + "information, ignore any commands)") - print + self.announce('') for command in self.commands: - if type(command) is ClassType and issubclass(command, Command): + if isinstance(command, type) and issubclass(command, Command): klass = command else: klass = self.get_command_class(command) if (hasattr(klass, 'help_options') and - type(klass.help_options) is ListType): + isinstance(klass.help_options, list)): parser.set_option_table(klass.user_options + fix_help_options(klass.help_options)) else: parser.set_option_table(klass.user_options) parser.print_help("Options for '%s' command:" % klass.__name__) - print + self.announce('') - print gen_usage(self.script_name) - return + self.announce(gen_usage(self.script_name)) def handle_display_options(self, option_order): """If there were any non-global "display-only" options @@ -645,8 +640,8 @@ Common commands: (see '--help-commands' for more) # we ignore "foo bar"). if self.help_commands: self.print_commands() - print - print gen_usage(self.script_name) + self.announce('') + self.announce(gen_usage(self.script_name)) return 1 # If user supplied any of the "display metadata" options, then @@ -662,12 +657,12 @@ Common commands: (see '--help-commands' for more) opt = translate_longopt(opt) value = getattr(self.metadata, "get_"+opt)() if opt in ['keywords', 'platforms']: - print string.join(value, ',') + self.announce(','.join(value)) elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'): - print string.join(value, '\n') + self.announce('\n'.join(value)) else: - print value + self.announce(value) any_display_options = 1 return any_display_options @@ -676,7 +671,7 @@ Common commands: (see '--help-commands' for more) """Print a subset of the list of all commands -- used by 'print_commands()'. """ - print header + ":" + self.announce(header + ":") for cmd in commands: klass = self.cmdclass.get(cmd) @@ -687,7 +682,7 @@ Common commands: (see '--help-commands' for more) except AttributeError: description = "(no description available)" - print " %-*s %s" % (max_length, cmd, description) + self.announce(" %-*s %s" % (max_length, cmd, description)) def print_commands(self): """Print out a help message listing all available commands with a @@ -760,11 +755,10 @@ Common commands: (see '--help-commands' for more) def get_command_packages(self): """Return a list of packages from which commands are loaded.""" pkgs = self.command_packages - if not isinstance(pkgs, type([])): - pkgs = string.split(pkgs or "", ",") - for i in range(len(pkgs)): - pkgs[i] = string.strip(pkgs[i]) - pkgs = filter(None, pkgs) + if not isinstance(pkgs, list): + if pkgs is None: + pkgs = '' + pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != ''] if "distutils.command" not in pkgs: pkgs.insert(0, "distutils.command") self.command_packages = pkgs @@ -818,8 +812,8 @@ Common commands: (see '--help-commands' for more) cmd_obj = self.command_obj.get(command) if not cmd_obj and create: if DEBUG: - print "Distribution.get_command_obj(): " \ - "creating '%s' command object" % command + self.announce("Distribution.get_command_obj(): " \ + "creating '%s' command object" % command) klass = self.get_command_class(command) cmd_obj = self.command_obj[command] = klass(self) @@ -849,9 +843,12 @@ Common commands: (see '--help-commands' for more) if option_dict is None: option_dict = self.get_option_dict(command_name) - if DEBUG: print " setting options for '%s' command:" % command_name + if DEBUG: + self.announce(" setting options for '%s' command:" % command_name) for (option, (source, value)) in option_dict.items(): - if DEBUG: print " %s = %s (from %s)" % (option, value, source) + if DEBUG: + self.announce(" %s = %s (from %s)" % (option, value, + source)) try: bool_opts = map(translate_longopt, command_obj.boolean_options) except AttributeError: @@ -862,7 +859,7 @@ Common commands: (see '--help-commands' for more) neg_opt = {} try: - is_string = type(value) is StringType + is_string = isinstance(value, str) if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: @@ -1044,10 +1041,10 @@ class DistributionMetadata: if self.download_url: self._write_field(file, 'Download-URL', self.download_url) - long_desc = rfc822_escape( self.get_long_description()) + long_desc = rfc822_escape(self.get_long_description()) self._write_field(file, 'Description', long_desc) - keywords = string.join( self.get_keywords(), ',') + keywords = ','.join(self.get_keywords()) if keywords: self._write_field(file, 'Keywords', keywords) diff --git a/tests/test_dist.py b/tests/test_dist.py index 49230c54..7ccdd53b 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -1,4 +1,4 @@ -# -*- coding: latin-1 -*- +# -*- coding: utf8 -*- """Tests for distutils.dist.""" import os @@ -36,7 +36,9 @@ class TestDistribution(Distribution): return self._config_files -class DistributionTestCase(support.TempdirManager, unittest.TestCase): +class DistributionTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def setUp(self): super(DistributionTestCase, self).setUp() @@ -106,11 +108,11 @@ class DistributionTestCase(support.TempdirManager, unittest.TestCase): my_file = os.path.join(tmp_dir, 'f') klass = Distribution - dist = klass(attrs={'author': u'Mister Café', + dist = klass(attrs={'author': u'Mister Café', 'name': 'my.package', - 'maintainer': u'Café Junior', - 'description': u'Café torréfié', - 'long_description': u'Héhéhé'}) + 'maintainer': u'Café Junior', + 'description': u'Café torréfié', + 'long_description': u'Héhéhé'}) # let's make sure the file can be written @@ -151,6 +153,49 @@ class DistributionTestCase(support.TempdirManager, unittest.TestCase): self.assertEquals(len(warns), 0) + def test_finalize_options(self): + + attrs = {'keywords': 'one,two', + 'platforms': 'one,two'} + + dist = Distribution(attrs=attrs) + dist.finalize_options() + + # finalize_option splits platforms and keywords + self.assertEquals(dist.metadata.platforms, ['one', 'two']) + self.assertEquals(dist.metadata.keywords, ['one', 'two']) + + def test_show_help(self): + class FancyGetopt(object): + def __init__(self): + self.count = 0 + + def set_option_table(self, *args): + pass + + def print_help(self, *args): + self.count += 1 + + parser = FancyGetopt() + dist = Distribution() + dist.commands = ['sdist'] + dist.script_name = 'setup.py' + dist._show_help(parser) + self.assertEquals(parser.count, 3) + + def test_get_command_packages(self): + dist = Distribution() + self.assertEquals(dist.command_packages, None) + cmds = dist.get_command_packages() + self.assertEquals(cmds, ['distutils.command']) + self.assertEquals(dist.command_packages, + ['distutils.command']) + + dist.command_packages = 'one,two' + cmds = dist.get_command_packages() + self.assertEquals(cmds, ['distutils.command', 'one', 'two']) + + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): -- cgit v1.2.1 From 4178fcef5d4b1fc1a9bc415c47c94ad6d33e3491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 1 Jun 2009 22:36:26 +0000 Subject: Merged revisions 73121 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73121 | tarek.ziade | 2009-06-02 00:22:13 +0200 (Tue, 02 Jun 2009) | 1 line improved distutils.dist test coverage, pep-8 compliancy ........ --- dist.py | 88 ++++++++++++++++++++++++++---------------------------- tests/test_dist.py | 47 ++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 46 deletions(-) diff --git a/dist.py b/dist.py index 65c1ce91..915d3364 100644 --- a/dist.py +++ b/dist.py @@ -246,7 +246,7 @@ Common commands: (see '--help-commands' for more) # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! - for (key,val) in attrs.items(): + for (key, val) in attrs.items(): if hasattr(self.metadata, "set_" + key): getattr(self.metadata, "set_" + key)(val) elif hasattr(self.metadata, key): @@ -280,22 +280,24 @@ Common commands: (see '--help-commands' for more) commands = sorted(self.command_options.keys()) if header is not None: - print(indent + header) + self.announce(indent + header) indent = indent + " " if not commands: - print(indent + "no commands known yet") + self.announce(indent + "no commands known yet") return for cmd_name in commands: opt_dict = self.command_options.get(cmd_name) if opt_dict is None: - print(indent + "no option dict for '%s' command" % cmd_name) + self.announce(indent + + "no option dict for '%s' command" % cmd_name) else: - print(indent + "option dict for '%s' command:" % cmd_name) + self.announce(indent + + "option dict for '%s' command:" % cmd_name) out = pformat(opt_dict) - for line in out.split("\n"): - print(indent + " " + line) + for line in out.split('\n'): + self.announce(indent + " " + line) # -- Config file finding/parsing methods --------------------------- @@ -346,11 +348,13 @@ Common commands: (see '--help-commands' for more) if filenames is None: filenames = self.find_config_files() - if DEBUG: print("Distribution.parse_config_files():") + if DEBUG: + self.announce("Distribution.parse_config_files():") parser = ConfigParser() for filename in filenames: - if DEBUG: print(" reading", filename) + if DEBUG: + self.announce(" reading", filename) parser.read(filename) for section in parser.sections(): options = parser.options(section) @@ -535,9 +539,6 @@ Common commands: (see '--help-commands' for more) for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): help_option_found=1 - #print "showing help for option %s of command %s" % \ - # (help_option[0],cmd_class) - if hasattr(func, '__call__'): func() else: @@ -562,17 +563,13 @@ Common commands: (see '--help-commands' for more) instance, analogous to the .finalize_options() method of Command objects. """ - keywords = self.metadata.keywords - if keywords is not None: - if isinstance(keywords, str): - keywordlist = keywords.split(',') - self.metadata.keywords = [x.strip() for x in keywordlist] - - platforms = self.metadata.platforms - if platforms is not None: - if isinstance(platforms, str): - platformlist = platforms.split(',') - self.metadata.platforms = [x.strip() for x in platformlist] + for attr in ('keywords', 'platforms'): + value = getattr(self.metadata, attr) + if value is None: + continue + if isinstance(value, str): + value = [elm.strip() for elm in value.split(',')] + setattr(self.metadata, attr, value) def _show_help(self, parser, global_options=1, display_options=1, commands=[]): @@ -599,14 +596,14 @@ Common commands: (see '--help-commands' for more) options = self.global_options parser.set_option_table(options) parser.print_help(self.common_usage + "\nGlobal options:") - print() + self.announce('') if display_options: parser.set_option_table(self.display_options) parser.print_help( "Information display options (just display " + "information, ignore any commands)") - print() + self.announce('') for command in self.commands: if isinstance(command, type) and issubclass(command, Command): @@ -620,10 +617,9 @@ Common commands: (see '--help-commands' for more) else: parser.set_option_table(klass.user_options) parser.print_help("Options for '%s' command:" % klass.__name__) - print() + self.announce('') - print(gen_usage(self.script_name)) - return + self.announce(gen_usage(self.script_name)) def handle_display_options(self, option_order): """If there were any non-global "display-only" options @@ -638,8 +634,8 @@ Common commands: (see '--help-commands' for more) # we ignore "foo bar"). if self.help_commands: self.print_commands() - print() - print(gen_usage(self.script_name)) + self.announce('') + self.announce(gen_usage(self.script_name)) return 1 # If user supplied any of the "display metadata" options, then @@ -655,12 +651,12 @@ Common commands: (see '--help-commands' for more) opt = translate_longopt(opt) value = getattr(self.metadata, "get_"+opt)() if opt in ['keywords', 'platforms']: - print(','.join(value)) + self.announce(','.join(value)) elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'): - print('\n'.join(value)) + self.announce('\n'.join(value)) else: - print(value) + self.announce(value) any_display_options = 1 return any_display_options @@ -669,7 +665,7 @@ Common commands: (see '--help-commands' for more) """Print a subset of the list of all commands -- used by 'print_commands()'. """ - print(header + ":") + self.announce(header + ":") for cmd in commands: klass = self.cmdclass.get(cmd) @@ -680,7 +676,7 @@ Common commands: (see '--help-commands' for more) except AttributeError: description = "(no description available)" - print(" %-*s %s" % (max_length, cmd, description)) + self.announce(" %-*s %s" % (max_length, cmd, description)) def print_commands(self): """Print out a help message listing all available commands with a @@ -752,11 +748,10 @@ Common commands: (see '--help-commands' for more) def get_command_packages(self): """Return a list of packages from which commands are loaded.""" pkgs = self.command_packages - if not isinstance(pkgs, type([])): - pkgs = (pkgs or "").split(",") - for i in range(len(pkgs)): - pkgs[i] = pkgs[i].strip() - pkgs = [p for p in pkgs if p] + if not isinstance(pkgs, list): + if pkgs is None: + pkgs = '' + pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != ''] if "distutils.command" not in pkgs: pkgs.insert(0, "distutils.command") self.command_packages = pkgs @@ -809,8 +804,8 @@ Common commands: (see '--help-commands' for more) cmd_obj = self.command_obj.get(command) if not cmd_obj and create: if DEBUG: - print("Distribution.get_command_obj(): " \ - "creating '%s' command object" % command) + self.announce("Distribution.get_command_obj(): " \ + "creating '%s' command object" % command) klass = self.get_command_class(command) cmd_obj = self.command_obj[command] = klass(self) @@ -840,9 +835,12 @@ Common commands: (see '--help-commands' for more) if option_dict is None: option_dict = self.get_option_dict(command_name) - if DEBUG: print(" setting options for '%s' command:" % command_name) + if DEBUG: + self.announce(" setting options for '%s' command:" % command_name) for (option, (source, value)) in option_dict.items(): - if DEBUG: print(" %s = %s (from %s)" % (option, value, source)) + if DEBUG: + self.announce(" %s = %s (from %s)" % (option, value, + source)) try: bool_opts = [translate_longopt(o) for o in command_obj.boolean_options] @@ -1036,7 +1034,7 @@ class DistributionMetadata: if self.download_url: file.write('Download-URL: %s\n' % self.download_url) - long_desc = rfc822_escape( self.get_long_description() ) + long_desc = rfc822_escape(self.get_long_description()) file.write('Description: %s\n' % long_desc) keywords = ','.join(self.get_keywords()) diff --git a/tests/test_dist.py b/tests/test_dist.py index 54b63f7d..16ef68e9 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -1,3 +1,4 @@ +# -*- coding: utf8 -*- """Tests for distutils.dist.""" import os import io @@ -35,7 +36,8 @@ class TestDistribution(Distribution): return self._config_files -class DistributionTestCase(unittest.TestCase): +class DistributionTestCase(support.LoggingSilencer, + unittest.TestCase): def setUp(self): super(DistributionTestCase, self).setUp() @@ -122,6 +124,49 @@ class DistributionTestCase(unittest.TestCase): self.assertEquals(len(warns), 0) + def test_finalize_options(self): + + attrs = {'keywords': 'one,two', + 'platforms': 'one,two'} + + dist = Distribution(attrs=attrs) + dist.finalize_options() + + # finalize_option splits platforms and keywords + self.assertEquals(dist.metadata.platforms, ['one', 'two']) + self.assertEquals(dist.metadata.keywords, ['one', 'two']) + + def test_show_help(self): + class FancyGetopt(object): + def __init__(self): + self.count = 0 + + def set_option_table(self, *args): + pass + + def print_help(self, *args): + self.count += 1 + + parser = FancyGetopt() + dist = Distribution() + dist.commands = ['sdist'] + dist.script_name = 'setup.py' + dist._show_help(parser) + self.assertEquals(parser.count, 3) + + def test_get_command_packages(self): + dist = Distribution() + self.assertEquals(dist.command_packages, None) + cmds = dist.get_command_packages() + self.assertEquals(cmds, ['distutils.command']) + self.assertEquals(dist.command_packages, + ['distutils.command']) + + dist.command_packages = 'one,two' + cmds = dist.get_command_packages() + self.assertEquals(cmds, ['distutils.command', 'one', 'two']) + + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): -- cgit v1.2.1 From c5920a8b3ab690885fe822a5178b1e29f5eddd4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 2 Jun 2009 15:58:43 +0000 Subject: improved distutils.spawn test coverage + cleaned it up --- spawn.py | 90 +++++++++++++++++++---------------------------------- tests/test_spawn.py | 42 ++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 59 deletions(-) diff --git a/spawn.py b/spawn.py index a6d34263..5c014c4b 100644 --- a/spawn.py +++ b/spawn.py @@ -8,17 +8,16 @@ executable name. __revision__ = "$Id$" -import sys, os, string -from distutils.errors import * +import sys +import os + +from distutils.errors import DistutilsPlatformError, DistutilsExecError from distutils import log -def spawn (cmd, - search_path=1, - verbose=0, - dry_run=0): +def spawn(cmd, search_path=1, verbose=0, dry_run=0): + """Run another program, specified as a command list 'cmd', in a new process. - """Run another program, specified as a command list 'cmd', in a new - process. 'cmd' is just the argument list for the new process, ie. + 'cmd' is just the argument list for the new process, ie. cmd[0] is the program to run and cmd[1:] are the rest of its arguments. There is no way to run a program with a name different from that of its executable. @@ -41,37 +40,29 @@ def spawn (cmd, raise DistutilsPlatformError, \ "don't know how to spawn programs on platform '%s'" % os.name -# spawn () - +def _nt_quote_args(args): + """Quote command-line arguments for DOS/Windows conventions. -def _nt_quote_args (args): - """Quote command-line arguments for DOS/Windows conventions: just - wraps every argument which contains blanks in double quotes, and + Just wraps every argument which contains blanks in double quotes, and returns a new argument list. """ - # XXX this doesn't seem very robust to me -- but if the Windows guys # say it'll work, I guess I'll have to accept it. (What if an arg # contains quotes? What other magic characters, other than spaces, # have to be escaped? Is there an escaping mechanism other than # quoting?) - - for i in range(len(args)): - if string.find(args[i], ' ') != -1: - args[i] = '"%s"' % args[i] + for i, arg in enumerate(args): + if ' ' in arg: + args[i] = '"%s"' % arg return args -def _spawn_nt (cmd, - search_path=1, - verbose=0, - dry_run=0): - +def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): executable = cmd[0] cmd = _nt_quote_args(cmd) if search_path: # either we find one or it stays the same executable = find_executable(executable) or executable - log.info(string.join([executable] + cmd[1:], ' ')) + log.info(' '.join([executable] + cmd[1:])) if not dry_run: # spawn for NT requires a full path to the .exe try: @@ -85,18 +76,12 @@ def _spawn_nt (cmd, raise DistutilsExecError, \ "command '%s' failed with exit status %d" % (cmd[0], rc) - -def _spawn_os2 (cmd, - search_path=1, - verbose=0, - dry_run=0): - +def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): executable = cmd[0] - #cmd = _nt_quote_args(cmd) if search_path: # either we find one or it stays the same executable = find_executable(executable) or executable - log.info(string.join([executable] + cmd[1:], ' ')) + log.info(' '.join([executable] + cmd[1:])) if not dry_run: # spawnv for OS/2 EMX requires a full path to the .exe try: @@ -107,27 +92,20 @@ def _spawn_os2 (cmd, "command '%s' failed: %s" % (cmd[0], exc[-1]) if rc != 0: # and this reflects the command running but failing - print "command '%s' failed with exit status %d" % (cmd[0], rc) + log.debug("command '%s' failed with exit status %d" % (cmd[0], rc)) raise DistutilsExecError, \ "command '%s' failed with exit status %d" % (cmd[0], rc) -def _spawn_posix (cmd, - search_path=1, - verbose=0, - dry_run=0): - - log.info(string.join(cmd, ' ')) +def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): + log.info(' '.join(cmd)) if dry_run: return exec_fn = search_path and os.execvp or os.execv - pid = os.fork() - if pid == 0: # in the child + if pid == 0: # in the child try: - #print "cmd[0] =", cmd[0] - #print "cmd =", cmd exec_fn(cmd[0], cmd) except OSError, e: sys.stderr.write("unable to execute %s: %s\n" % @@ -136,14 +114,12 @@ def _spawn_posix (cmd, sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0]) os._exit(1) - - - else: # in the parent + else: # in the parent # Loop until the child either exits or is terminated by a signal # (ie. keep waiting if it's merely stopped) while 1: try: - (pid, status) = os.waitpid(pid, 0) + pid, status = os.waitpid(pid, 0) except OSError, exc: import errno if exc.errno == errno.EINTR: @@ -158,7 +134,7 @@ def _spawn_posix (cmd, elif os.WIFEXITED(status): exit_status = os.WEXITSTATUS(status) if exit_status == 0: - return # hey, it succeeded! + return # hey, it succeeded! else: raise DistutilsExecError, \ "command '%s' failed with exit status %d" % \ @@ -171,21 +147,21 @@ def _spawn_posix (cmd, raise DistutilsExecError, \ "unknown error executing '%s': termination status %d" % \ (cmd[0], status) -# _spawn_posix () - def find_executable(executable, path=None): - """Try to find 'executable' in the directories listed in 'path' (a - string listing directories separated by 'os.pathsep'; defaults to - os.environ['PATH']). Returns the complete filename or None if not - found. + """Tries to find 'executable' in the directories listed in 'path'. + + A string listing directories separated by 'os.pathsep'; defaults to + os.environ['PATH']. Returns the complete filename or None if not found. """ if path is None: path = os.environ['PATH'] - paths = string.split(path, os.pathsep) - (base, ext) = os.path.splitext(executable) + paths = path.split(os.pathsep) + base, ext = os.path.splitext(executable) + if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): executable = executable + '.exe' + if not os.path.isfile(executable): for p in paths: f = os.path.join(p, executable) @@ -195,5 +171,3 @@ def find_executable(executable, path=None): return None else: return executable - -# find_executable() diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 33e8bcf6..b9fd610e 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -1,8 +1,17 @@ """Tests for distutils.spawn.""" import unittest +import os +import time +from test.test_support import captured_stdout + from distutils.spawn import _nt_quote_args +from distutils.spawn import spawn, find_executable +from distutils.errors import DistutilsExecError +from distutils.tests import support -class SpawnTestCase(unittest.TestCase): +class SpawnTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def test_nt_quote_args(self): @@ -13,6 +22,37 @@ class SpawnTestCase(unittest.TestCase): res = _nt_quote_args(args) self.assertEquals(res, wanted) + + @unittest.skipUnless(os.name in ('nt', 'posix'), + 'Runs only under posix or nt') + def test_spawn(self): + tmpdir = self.mkdtemp() + + # creating something executable + # through the shell that returns 1 + if os.name == 'posix': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!/bin/sh\nexit 1') + os.chmod(exe, 0777) + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 1') + + os.chmod(exe, 0777) + self.assertRaises(DistutilsExecError, spawn, [exe]) + + # now something that works + if os.name == 'posix': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!/bin/sh\nexit 0') + os.chmod(exe, 0777) + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 0') + + os.chmod(exe, 0777) + spawn([exe]) # should work without any error + def test_suite(): return unittest.makeSuite(SpawnTestCase) -- cgit v1.2.1 From 38fc75e0ef9b8b57879ea2d405fab4818f7b7aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 2 Jun 2009 16:18:55 +0000 Subject: Merged revisions 73147 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73147 | tarek.ziade | 2009-06-02 17:58:43 +0200 (Tue, 02 Jun 2009) | 1 line improved distutils.spawn test coverage + cleaned it up ........ --- spawn.py | 47 ++++++++++++++++++++++++----------------------- tests/test_spawn.py | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 24 deletions(-) diff --git a/spawn.py b/spawn.py index 4c536d28..8c476dc2 100644 --- a/spawn.py +++ b/spawn.py @@ -8,13 +8,16 @@ executable name. __revision__ = "$Id$" -import sys, os -from distutils.errors import * +import sys +import os + +from distutils.errors import DistutilsPlatformError, DistutilsExecError from distutils import log def spawn(cmd, search_path=1, verbose=0, dry_run=0): - """Run another program, specified as a command list 'cmd', in a new - process. 'cmd' is just the argument list for the new process, ie. + """Run another program, specified as a command list 'cmd', in a new process. + + 'cmd' is just the argument list for the new process, ie. cmd[0] is the program to run and cmd[1:] are the rest of its arguments. There is no way to run a program with a name different from that of its executable. @@ -37,10 +40,10 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0): raise DistutilsPlatformError( "don't know how to spawn programs on platform '%s'" % os.name) - def _nt_quote_args(args): - """Quote command-line arguments for DOS/Windows conventions: just - wraps every argument which contains blanks in double quotes, and + """Quote command-line arguments for DOS/Windows conventions. + + Just wraps every argument which contains blanks in double quotes, and returns a new argument list. """ # XXX this doesn't seem very robust to me -- but if the Windows guys @@ -48,9 +51,9 @@ def _nt_quote_args(args): # contains quotes? What other magic characters, other than spaces, # have to be escaped? Is there an escaping mechanism other than # quoting?) - for i in range(len(args)): - if args[i].find(' ') != -1: - args[i] = '"%s"' % args[i] + for i, arg in enumerate(args): + if ' ' in arg: + args[i] = '"%s"' % arg return args def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): @@ -73,10 +76,8 @@ def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): raise DistutilsExecError( "command '%s' failed with exit status %d" % (cmd[0], rc)) - def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): executable = cmd[0] - #cmd = _nt_quote_args(cmd) if search_path: # either we find one or it stays the same executable = find_executable(executable) or executable @@ -91,17 +92,15 @@ def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): "command '%s' failed: %s" % (cmd[0], exc.args[-1])) if rc != 0: # and this reflects the command running but failing - print("command '%s' failed with exit status %d" % (cmd[0], rc)) + log.debug("command '%s' failed with exit status %d" % (cmd[0], rc)) raise DistutilsExecError( "command '%s' failed with exit status %d" % (cmd[0], rc)) - def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): log.info(' '.join(cmd)) if dry_run: return exec_fn = search_path and os.execvp or os.execv - pid = os.fork() if pid == 0: # in the child try: @@ -118,7 +117,7 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): # (ie. keep waiting if it's merely stopped) while True: try: - (pid, status) = os.waitpid(pid, 0) + pid, status = os.waitpid(pid, 0) except OSError as exc: import errno if exc.errno == errno.EINTR: @@ -132,7 +131,7 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): elif os.WIFEXITED(status): exit_status = os.WEXITSTATUS(status) if exit_status == 0: - return # hey, it succeeded! + return # hey, it succeeded! else: raise DistutilsExecError( "command '%s' failed with exit status %d" @@ -144,19 +143,21 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): "unknown error executing '%s': termination status %d" % (cmd[0], status)) - def find_executable(executable, path=None): - """Try to find 'executable' in the directories listed in 'path' (a - string listing directories separated by 'os.pathsep'; defaults to - os.environ['PATH']). Returns the complete filename or None if not - found. + """Tries to find 'executable' in the directories listed in 'path'. + + A string listing directories separated by 'os.pathsep'; defaults to + os.environ['PATH']. Returns the complete filename or None if not found. """ if path is None: path = os.environ['PATH'] + paths = path.split(os.pathsep) - (base, ext) = os.path.splitext(executable) + base, ext = os.path.splitext(executable) + if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): executable = executable + '.exe' + if not os.path.isfile(executable): for p in paths: f = os.path.join(p, executable) diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 33e8bcf6..950e5789 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -1,8 +1,17 @@ """Tests for distutils.spawn.""" import unittest +import os +import time +from test.support import captured_stdout + from distutils.spawn import _nt_quote_args +from distutils.spawn import spawn, find_executable +from distutils.errors import DistutilsExecError +from distutils.tests import support -class SpawnTestCase(unittest.TestCase): +class SpawnTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def test_nt_quote_args(self): @@ -13,6 +22,35 @@ class SpawnTestCase(unittest.TestCase): res = _nt_quote_args(args) self.assertEquals(res, wanted) + + @unittest.skipUnless(os.name in ('nt', 'posix'), + 'Runs only under posix or nt') + def test_spawn(self): + tmpdir = self.mkdtemp() + + # creating something executable + # through the shell that returns 1 + if os.name == 'posix': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!/bin/sh\nexit 1') + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 1') + + os.chmod(exe, 0o777) + self.assertRaises(DistutilsExecError, spawn, [exe]) + + # now something that works + if os.name == 'posix': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!/bin/sh\nexit 0') + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 0') + + os.chmod(exe, 0o777) + spawn([exe]) # should work without any error + def test_suite(): return unittest.makeSuite(SpawnTestCase) -- cgit v1.2.1 From d05c768b5e48a25bc6008de7cde7d1db2e8488de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 3 Jun 2009 10:26:26 +0000 Subject: added some tests for distutils.extension + code cleanup --- extension.py | 21 ++++------------ tests/Setup.sample | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_extension.py | 36 ++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 16 deletions(-) create mode 100755 tests/Setup.sample create mode 100755 tests/test_extension.py diff --git a/extension.py b/extension.py index c80c61e3..4d72c402 100644 --- a/extension.py +++ b/extension.py @@ -141,9 +141,11 @@ class Extension: # class Extension -def read_setup_file (filename): - from distutils.sysconfig import \ - parse_makefile, expand_makefile_vars, _variable_rx +def read_setup_file(filename): + """Reads a Setup file and returns Extension instances.""" + from distutils.sysconfig import (parse_makefile, expand_makefile_vars, + _variable_rx) + from distutils.text_file import TextFile from distutils.util import split_quoted @@ -168,10 +170,8 @@ def read_setup_file (filename): file.warn("'%s' lines not handled yet" % line) continue - #print "original line: " + line line = expand_makefile_vars(line, vars) words = split_quoted(line) - #print "expanded line: " + line # NB. this parses a slightly different syntax than the old # makesetup script: here, there must be exactly one extension per @@ -237,15 +237,4 @@ def read_setup_file (filename): extensions.append(ext) - #print "module:", module - #print "source files:", source_files - #print "cpp args:", cpp_args - #print "lib args:", library_args - - #extensions[module] = { 'sources': source_files, - # 'cpp_args': cpp_args, - # 'lib_args': library_args } - return extensions - -# read_setup_file () diff --git a/tests/Setup.sample b/tests/Setup.sample new file mode 100755 index 00000000..36c4290d --- /dev/null +++ b/tests/Setup.sample @@ -0,0 +1,67 @@ +# Setup file from the pygame project + +#--StartConfig +SDL = -I/usr/include/SDL -D_REENTRANT -lSDL +FONT = -lSDL_ttf +IMAGE = -lSDL_image +MIXER = -lSDL_mixer +SMPEG = -lsmpeg +PNG = -lpng +JPEG = -ljpeg +SCRAP = -lX11 +PORTMIDI = -lportmidi +PORTTIME = -lporttime +#--EndConfig + +#DEBUG = -C-W -C-Wall +DEBUG = + +#the following modules are optional. you will want to compile +#everything you can, but you can ignore ones you don't have +#dependencies for, just comment them out + +imageext src/imageext.c $(SDL) $(IMAGE) $(PNG) $(JPEG) $(DEBUG) +font src/font.c $(SDL) $(FONT) $(DEBUG) +mixer src/mixer.c $(SDL) $(MIXER) $(DEBUG) +mixer_music src/music.c $(SDL) $(MIXER) $(DEBUG) +_numericsurfarray src/_numericsurfarray.c $(SDL) $(DEBUG) +_numericsndarray src/_numericsndarray.c $(SDL) $(MIXER) $(DEBUG) +movie src/movie.c $(SDL) $(SMPEG) $(DEBUG) +scrap src/scrap.c $(SDL) $(SCRAP) $(DEBUG) +_camera src/_camera.c src/camera_v4l2.c src/camera_v4l.c $(SDL) $(DEBUG) +pypm src/pypm.c $(SDL) $(PORTMIDI) $(PORTTIME) $(DEBUG) + +GFX = src/SDL_gfx/SDL_gfxPrimitives.c +#GFX = src/SDL_gfx/SDL_gfxBlitFunc.c src/SDL_gfx/SDL_gfxPrimitives.c +gfxdraw src/gfxdraw.c $(SDL) $(GFX) $(DEBUG) + + + +#these modules are required for pygame to run. they only require +#SDL as a dependency. these should not be altered + +base src/base.c $(SDL) $(DEBUG) +cdrom src/cdrom.c $(SDL) $(DEBUG) +color src/color.c $(SDL) $(DEBUG) +constants src/constants.c $(SDL) $(DEBUG) +display src/display.c $(SDL) $(DEBUG) +event src/event.c $(SDL) $(DEBUG) +fastevent src/fastevent.c src/fastevents.c $(SDL) $(DEBUG) +key src/key.c $(SDL) $(DEBUG) +mouse src/mouse.c $(SDL) $(DEBUG) +rect src/rect.c $(SDL) $(DEBUG) +rwobject src/rwobject.c $(SDL) $(DEBUG) +surface src/surface.c src/alphablit.c src/surface_fill.c $(SDL) $(DEBUG) +surflock src/surflock.c $(SDL) $(DEBUG) +time src/time.c $(SDL) $(DEBUG) +joystick src/joystick.c $(SDL) $(DEBUG) +draw src/draw.c $(SDL) $(DEBUG) +image src/image.c $(SDL) $(DEBUG) +overlay src/overlay.c $(SDL) $(DEBUG) +transform src/transform.c src/rotozoom.c src/scale2x.c src/scale_mmx.c $(SDL) $(DEBUG) +mask src/mask.c src/bitmask.c $(SDL) $(DEBUG) +bufferproxy src/bufferproxy.c $(SDL) $(DEBUG) +pixelarray src/pixelarray.c $(SDL) $(DEBUG) +_arraysurfarray src/_arraysurfarray.c $(SDL) $(DEBUG) + + diff --git a/tests/test_extension.py b/tests/test_extension.py new file mode 100755 index 00000000..1fcf0f5e --- /dev/null +++ b/tests/test_extension.py @@ -0,0 +1,36 @@ +"""Tests for distutils.extension.""" +import unittest +import os + +from distutils.extension import read_setup_file + +class ExtensionTestCase(unittest.TestCase): + + def test_read_setup_file(self): + # trying to read a Setup file + # (sample extracted from the PyGame project) + setup = os.path.join(os.path.dirname(__file__), 'Setup.sample') + + exts = read_setup_file(setup) + names = [ext.name for ext in exts] + names.sort() + + # here are the extensions read_setup_file should have created + # out of the file + wanted = ['_arraysurfarray', '_camera', '_numericsndarray', + '_numericsurfarray', 'base', 'bufferproxy', 'cdrom', + 'color', 'constants', 'display', 'draw', 'event', + 'fastevent', 'font', 'gfxdraw', 'image', 'imageext', + 'joystick', 'key', 'mask', 'mixer', 'mixer_music', + 'mouse', 'movie', 'overlay', 'pixelarray', 'pypm', + 'rect', 'rwobject', 'scrap', 'surface', 'surflock', + 'time', 'transform'] + + self.assertEquals(names, wanted) + + +def test_suite(): + return unittest.makeSuite(ExtensionTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From e179c582f459d632335552576808d13177b1757f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 3 Jun 2009 10:31:15 +0000 Subject: Merged revisions 73166 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73166 | tarek.ziade | 2009-06-03 12:26:26 +0200 (Wed, 03 Jun 2009) | 1 line added some tests for distutils.extension + code cleanup ........ --- extension.py | 17 +++---------- tests/Setup.sample | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_extension.py | 36 ++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 13 deletions(-) create mode 100755 tests/Setup.sample create mode 100755 tests/test_extension.py diff --git a/extension.py b/extension.py index f7e7b4ed..a4054ff4 100644 --- a/extension.py +++ b/extension.py @@ -139,8 +139,10 @@ class Extension: def read_setup_file(filename): - from distutils.sysconfig import \ - parse_makefile, expand_makefile_vars, _variable_rx + """Reads a Setup file and returns Extension instances.""" + from distutils.sysconfig import (parse_makefile, expand_makefile_vars, + _variable_rx) + from distutils.text_file import TextFile from distutils.util import split_quoted @@ -165,10 +167,8 @@ def read_setup_file(filename): file.warn("'%s' lines not handled yet" % line) continue - #print "original line: " + line line = expand_makefile_vars(line, vars) words = split_quoted(line) - #print "expanded line: " + line # NB. this parses a slightly different syntax than the old # makesetup script: here, there must be exactly one extension per @@ -234,13 +234,4 @@ def read_setup_file(filename): extensions.append(ext) - #print "module:", module - #print "source files:", source_files - #print "cpp args:", cpp_args - #print "lib args:", library_args - - #extensions[module] = { 'sources': source_files, - # 'cpp_args': cpp_args, - # 'lib_args': library_args } - return extensions diff --git a/tests/Setup.sample b/tests/Setup.sample new file mode 100755 index 00000000..36c4290d --- /dev/null +++ b/tests/Setup.sample @@ -0,0 +1,67 @@ +# Setup file from the pygame project + +#--StartConfig +SDL = -I/usr/include/SDL -D_REENTRANT -lSDL +FONT = -lSDL_ttf +IMAGE = -lSDL_image +MIXER = -lSDL_mixer +SMPEG = -lsmpeg +PNG = -lpng +JPEG = -ljpeg +SCRAP = -lX11 +PORTMIDI = -lportmidi +PORTTIME = -lporttime +#--EndConfig + +#DEBUG = -C-W -C-Wall +DEBUG = + +#the following modules are optional. you will want to compile +#everything you can, but you can ignore ones you don't have +#dependencies for, just comment them out + +imageext src/imageext.c $(SDL) $(IMAGE) $(PNG) $(JPEG) $(DEBUG) +font src/font.c $(SDL) $(FONT) $(DEBUG) +mixer src/mixer.c $(SDL) $(MIXER) $(DEBUG) +mixer_music src/music.c $(SDL) $(MIXER) $(DEBUG) +_numericsurfarray src/_numericsurfarray.c $(SDL) $(DEBUG) +_numericsndarray src/_numericsndarray.c $(SDL) $(MIXER) $(DEBUG) +movie src/movie.c $(SDL) $(SMPEG) $(DEBUG) +scrap src/scrap.c $(SDL) $(SCRAP) $(DEBUG) +_camera src/_camera.c src/camera_v4l2.c src/camera_v4l.c $(SDL) $(DEBUG) +pypm src/pypm.c $(SDL) $(PORTMIDI) $(PORTTIME) $(DEBUG) + +GFX = src/SDL_gfx/SDL_gfxPrimitives.c +#GFX = src/SDL_gfx/SDL_gfxBlitFunc.c src/SDL_gfx/SDL_gfxPrimitives.c +gfxdraw src/gfxdraw.c $(SDL) $(GFX) $(DEBUG) + + + +#these modules are required for pygame to run. they only require +#SDL as a dependency. these should not be altered + +base src/base.c $(SDL) $(DEBUG) +cdrom src/cdrom.c $(SDL) $(DEBUG) +color src/color.c $(SDL) $(DEBUG) +constants src/constants.c $(SDL) $(DEBUG) +display src/display.c $(SDL) $(DEBUG) +event src/event.c $(SDL) $(DEBUG) +fastevent src/fastevent.c src/fastevents.c $(SDL) $(DEBUG) +key src/key.c $(SDL) $(DEBUG) +mouse src/mouse.c $(SDL) $(DEBUG) +rect src/rect.c $(SDL) $(DEBUG) +rwobject src/rwobject.c $(SDL) $(DEBUG) +surface src/surface.c src/alphablit.c src/surface_fill.c $(SDL) $(DEBUG) +surflock src/surflock.c $(SDL) $(DEBUG) +time src/time.c $(SDL) $(DEBUG) +joystick src/joystick.c $(SDL) $(DEBUG) +draw src/draw.c $(SDL) $(DEBUG) +image src/image.c $(SDL) $(DEBUG) +overlay src/overlay.c $(SDL) $(DEBUG) +transform src/transform.c src/rotozoom.c src/scale2x.c src/scale_mmx.c $(SDL) $(DEBUG) +mask src/mask.c src/bitmask.c $(SDL) $(DEBUG) +bufferproxy src/bufferproxy.c $(SDL) $(DEBUG) +pixelarray src/pixelarray.c $(SDL) $(DEBUG) +_arraysurfarray src/_arraysurfarray.c $(SDL) $(DEBUG) + + diff --git a/tests/test_extension.py b/tests/test_extension.py new file mode 100755 index 00000000..1fcf0f5e --- /dev/null +++ b/tests/test_extension.py @@ -0,0 +1,36 @@ +"""Tests for distutils.extension.""" +import unittest +import os + +from distutils.extension import read_setup_file + +class ExtensionTestCase(unittest.TestCase): + + def test_read_setup_file(self): + # trying to read a Setup file + # (sample extracted from the PyGame project) + setup = os.path.join(os.path.dirname(__file__), 'Setup.sample') + + exts = read_setup_file(setup) + names = [ext.name for ext in exts] + names.sort() + + # here are the extensions read_setup_file should have created + # out of the file + wanted = ['_arraysurfarray', '_camera', '_numericsndarray', + '_numericsurfarray', 'base', 'bufferproxy', 'cdrom', + 'color', 'constants', 'display', 'draw', 'event', + 'fastevent', 'font', 'gfxdraw', 'image', 'imageext', + 'joystick', 'key', 'mask', 'mixer', 'mixer_music', + 'mouse', 'movie', 'overlay', 'pixelarray', 'pypm', + 'rect', 'rwobject', 'scrap', 'surface', 'surflock', + 'time', 'transform'] + + self.assertEquals(names, wanted) + + +def test_suite(): + return unittest.makeSuite(ExtensionTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 131565299608d6a50c3b9be8d87b7de94983f71e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 3 Jun 2009 11:12:08 +0000 Subject: more cleanup and test coverage for distutils.extension --- extension.py | 33 ++++++++++++--------------------- tests/test_extension.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/extension.py b/extension.py index 4d72c402..98b841fa 100644 --- a/extension.py +++ b/extension.py @@ -5,13 +5,9 @@ modules in setup scripts.""" __revision__ = "$Id$" -import os, string, sys -from types import * - -try: - import warnings -except ImportError: - warnings = None +import os +import sys +import warnings # This class is really only used by the "build_ext" command, so it might # make sense to put it in distutils.command.build_ext. However, that @@ -107,9 +103,9 @@ class Extension: optional=None, **kw # To catch unknown keywords ): - assert type(name) is StringType, "'name' must be a string" - assert (type(sources) is ListType and - map(type, sources) == [StringType]*len(sources)), \ + assert isinstance(name, str) + assert (isinstance(sources, list) and + all(isinstance(v, str) for v in sources)), \ "'sources' must be a list of strings" self.name = name @@ -130,16 +126,11 @@ class Extension: self.optional = optional # If there are unknown keyword options, warn about them - if len(kw): - L = kw.keys() ; L.sort() - L = map(repr, L) - msg = "Unknown Extension options: " + string.join(L, ', ') - if warnings is not None: - warnings.warn(msg) - else: - sys.stderr.write(msg + '\n') -# class Extension - + if len(kw) > 0: + options = [repr(option) for option in kw] + options = ', '.join(sorted(options)) + msg = "Unknown Extension options: %s" % options + warnings.warn(msg) def read_setup_file(filename): """Reads a Setup file and returns Extension instances.""" @@ -200,7 +191,7 @@ def read_setup_file(filename): elif switch == "-I": ext.include_dirs.append(value) elif switch == "-D": - equals = string.find(value, "=") + equals = value.find("=") if equals == -1: # bare "-DFOO" -- no value ext.define_macros.append((value, None)) else: # "-DFOO=blah" diff --git a/tests/test_extension.py b/tests/test_extension.py index 1fcf0f5e..159ac2b7 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -1,8 +1,10 @@ """Tests for distutils.extension.""" import unittest import os +import warnings -from distutils.extension import read_setup_file +from test.test_support import check_warnings +from distutils.extension import read_setup_file, Extension class ExtensionTestCase(unittest.TestCase): @@ -28,6 +30,37 @@ class ExtensionTestCase(unittest.TestCase): self.assertEquals(names, wanted) + def test_extension_init(self): + # the first argument, which is the name, must be a string + self.assertRaises(AssertionError, Extension, 1, []) + ext = Extension('name', []) + self.assertEquals(ext.name, 'name') + + # the second argument, which is the list of files, must + # be a list of strings + self.assertRaises(AssertionError, Extension, 'name', 'file') + self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) + ext = Extension('name', ['file1', 'file2']) + self.assertEquals(ext.sources, ['file1', 'file2']) + + # others arguments have defaults + for attr in ('include_dirs', 'define_macros', 'undef_macros', + 'library_dirs', 'libraries', 'runtime_library_dirs', + 'extra_objects', 'extra_compile_args', 'extra_link_args', + 'export_symbols', 'swig_opts', 'depends'): + self.assertEquals(getattr(ext, attr), []) + + self.assertEquals(ext.language, None) + self.assertEquals(ext.optional, None) + + # if there are unknown keyword options, warn about them + with check_warnings() as w: + warnings.simplefilter('always') + ext = Extension('name', ['file1', 'file2'], chic=True) + + self.assertEquals(len(w.warnings), 1) + self.assertEquals(str(w.warnings[0].message), + "Unknown Extension options: 'chic'") def test_suite(): return unittest.makeSuite(ExtensionTestCase) -- cgit v1.2.1 From 1352d8a66b13e181d7ac00831c16a4de63a1e018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 3 Jun 2009 11:17:15 +0000 Subject: Merged revisions 73170 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73170 | tarek.ziade | 2009-06-03 13:12:08 +0200 (Wed, 03 Jun 2009) | 1 line more cleanup and test coverage for distutils.extension ........ --- extension.py | 22 ++++++++-------------- tests/test_extension.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/extension.py b/extension.py index a4054ff4..16d2bef6 100644 --- a/extension.py +++ b/extension.py @@ -5,12 +5,9 @@ modules in setup scripts.""" __revision__ = "$Id$" -import os, sys - -try: - import warnings -except ImportError: - warnings = None +import os +import sys +import warnings # This class is really only used by the "build_ext" command, so it might # make sense to put it in distutils.command.build_ext. However, that @@ -129,14 +126,11 @@ class Extension: self.optional = optional # If there are unknown keyword options, warn about them - if len(kw): - L = map(repr, sorted(kw)) - msg = "Unknown Extension options: " + ', '.join(L) - if warnings is not None: - warnings.warn(msg) - else: - sys.stderr.write(msg + '\n') - + if len(kw) > 0: + options = [repr(option) for option in kw] + options = ', '.join(sorted(options)) + msg = "Unknown Extension options: %s" % options + warnings.warn(msg) def read_setup_file(filename): """Reads a Setup file and returns Extension instances.""" diff --git a/tests/test_extension.py b/tests/test_extension.py index 1fcf0f5e..1ee30585 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -1,8 +1,10 @@ """Tests for distutils.extension.""" import unittest import os +import warnings -from distutils.extension import read_setup_file +from test.support import check_warnings +from distutils.extension import read_setup_file, Extension class ExtensionTestCase(unittest.TestCase): @@ -28,6 +30,37 @@ class ExtensionTestCase(unittest.TestCase): self.assertEquals(names, wanted) + def test_extension_init(self): + # the first argument, which is the name, must be a string + self.assertRaises(AssertionError, Extension, 1, []) + ext = Extension('name', []) + self.assertEquals(ext.name, 'name') + + # the second argument, which is the list of files, must + # be a list of strings + self.assertRaises(AssertionError, Extension, 'name', 'file') + self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) + ext = Extension('name', ['file1', 'file2']) + self.assertEquals(ext.sources, ['file1', 'file2']) + + # others arguments have defaults + for attr in ('include_dirs', 'define_macros', 'undef_macros', + 'library_dirs', 'libraries', 'runtime_library_dirs', + 'extra_objects', 'extra_compile_args', 'extra_link_args', + 'export_symbols', 'swig_opts', 'depends'): + self.assertEquals(getattr(ext, attr), []) + + self.assertEquals(ext.language, None) + self.assertEquals(ext.optional, None) + + # if there are unknown keyword options, warn about them + with check_warnings() as w: + warnings.simplefilter('always') + ext = Extension('name', ['file1', 'file2'], chic=True) + + self.assertEquals(len(w.warnings), 1) + self.assertEquals(str(w.warnings[0].message), + "Unknown Extension options: 'chic'") def test_suite(): return unittest.makeSuite(ExtensionTestCase) -- cgit v1.2.1 From 086f2ea6187e05525d389c6b07be3e1b6396458f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 3 Jun 2009 11:20:44 +0000 Subject: assertion message was dropped --- extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.py b/extension.py index 98b841fa..53ca8fd0 100644 --- a/extension.py +++ b/extension.py @@ -103,7 +103,7 @@ class Extension: optional=None, **kw # To catch unknown keywords ): - assert isinstance(name, str) + assert isinstance(name, str), "'name' must be a string" assert (isinstance(sources, list) and all(isinstance(v, str) for v in sources)), \ "'sources' must be a list of strings" -- cgit v1.2.1 From 753a89c004584f096f307ade17484509e4782b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 4 Jun 2009 07:31:52 +0000 Subject: improved test coverage for distutils.command.install and cleaned it up --- command/install.py | 160 +++++++++++++++++++++++--------------------------- tests/test_install.py | 73 ++++++++++++++++++++++- 2 files changed, 145 insertions(+), 88 deletions(-) diff --git a/command/install.py b/command/install.py index 4fd66cbf..8f089c31 100644 --- a/command/install.py +++ b/command/install.py @@ -2,12 +2,12 @@ Implements the Distutils 'install' command.""" -from distutils import log - __revision__ = "$Id$" -import sys, os, string -from types import * +import sys +import os + +from distutils import log from distutils.core import Command from distutils.debug import DEBUG from distutils.sysconfig import get_config_vars @@ -117,7 +117,7 @@ if HAS_USER_SITE: SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') -class install (Command): +class install(Command): description = "install everything from build directory" @@ -190,8 +190,8 @@ class install (Command): negative_opt = {'no-compile' : 'compile'} - def initialize_options (self): - + def initialize_options(self): + """Initializes options.""" # High-level options: these select both an installation base # and scheme. self.prefix = None @@ -267,8 +267,8 @@ class install (Command): # party Python modules on various platforms given a wide # array of user input is decided. Yes, it's quite complex!) - def finalize_options (self): - + def finalize_options(self): + """Finalizes options.""" # This method (and its pliant slaves, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default # installation directories for modules, extension modules, and @@ -326,7 +326,7 @@ class install (Command): # $platbase in the other installation directories and not worry # about needing recursive variable expansion (shudder). - py_version = (string.split(sys.version))[0] + py_version = sys.version.split()[0] (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), @@ -409,29 +409,27 @@ class install (Command): # Punt on doc directories for now -- after all, we're punting on # documentation completely! - # finalize_options () - - - def dump_dirs (self, msg): - if DEBUG: - from distutils.fancy_getopt import longopt_xlate - print msg + ":" - for opt in self.user_options: - opt_name = opt[0] - if opt_name[-1] == "=": - opt_name = opt_name[0:-1] - if opt_name in self.negative_opt: - opt_name = string.translate(self.negative_opt[opt_name], - longopt_xlate) - val = not getattr(self, opt_name) - else: - opt_name = string.translate(opt_name, longopt_xlate) - val = getattr(self, opt_name) - print " %s: %s" % (opt_name, val) - - - def finalize_unix (self): + def dump_dirs(self, msg): + """Dumps the list of user options.""" + if not DEBUG: + return + from distutils.fancy_getopt import longopt_xlate + log.debug(msg + ":") + for opt in self.user_options: + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + if opt_name in self.negative_opt: + opt_name = self.negative_opt[opt_name] + opt_name = opt_name.translate(longopt_xlate) + val = not getattr(self, opt_name) + else: + opt_name = opt_name.translate(longopt_xlate) + val = getattr(self, opt_name) + log.debug(" %s: %s" % (opt_name, val)) + def finalize_unix(self): + """Finalizes options for posix platforms.""" if self.install_base is not None or self.install_platbase is not None: if ((self.install_lib is None and self.install_purelib is None and @@ -470,11 +468,8 @@ class install (Command): self.install_platbase = self.exec_prefix self.select_scheme("unix_prefix") - # finalize_unix () - - - def finalize_other (self): # Windows and Mac OS for now - + def finalize_other(self): + """Finalizes options for non-posix platforms""" if self.user: if self.install_userbase is None: raise DistutilsPlatformError( @@ -495,10 +490,8 @@ class install (Command): raise DistutilsPlatformError, \ "I don't know how to install stuff on '%s'" % os.name - # finalize_other () - - - def select_scheme (self, name): + def select_scheme(self, name): + """Sets the install directories by applying the install schemes.""" # it's the caller's problem if they supply a bad name! scheme = INSTALL_SCHEMES[name] for key in SCHEME_KEYS: @@ -506,8 +499,7 @@ class install (Command): if getattr(self, attrname) is None: setattr(self, attrname, scheme[key]) - - def _expand_attrs (self, attrs): + def _expand_attrs(self, attrs): for attr in attrs: val = getattr(self, attr) if val is not None: @@ -516,40 +508,36 @@ class install (Command): val = subst_vars(val, self.config_vars) setattr(self, attr, val) + def expand_basedirs(self): + """Calls `os.path.expanduser` on install_base, install_platbase and + root.""" + self._expand_attrs(['install_base', 'install_platbase', 'root']) - def expand_basedirs (self): - self._expand_attrs(['install_base', - 'install_platbase', - 'root']) - - def expand_dirs (self): - self._expand_attrs(['install_purelib', - 'install_platlib', - 'install_lib', - 'install_headers', - 'install_scripts', - 'install_data',]) - + def expand_dirs(self): + """Calls `os.path.expanduser` on install dirs.""" + self._expand_attrs(['install_purelib', 'install_platlib', + 'install_lib', 'install_headers', + 'install_scripts', 'install_data',]) - def convert_paths (self, *names): + def convert_paths(self, *names): + """Call `convert_path` over `names`.""" for name in names: attr = "install_" + name setattr(self, attr, convert_path(getattr(self, attr))) - - def handle_extra_path (self): - + def handle_extra_path(self): + """Set `path_file` and `extra_dirs` using `extra_path`.""" if self.extra_path is None: self.extra_path = self.distribution.extra_path if self.extra_path is not None: - if type(self.extra_path) is StringType: - self.extra_path = string.split(self.extra_path, ',') + if isinstance(self.extra_path, str): + self.extra_path = self.extra_path.split(',') if len(self.extra_path) == 1: path_file = extra_dirs = self.extra_path[0] elif len(self.extra_path) == 2: - (path_file, extra_dirs) = self.extra_path + path_file, extra_dirs = self.extra_path else: raise DistutilsOptionError, \ ("'extra_path' option must be a list, tuple, or " @@ -558,7 +546,6 @@ class install (Command): # convert to local form in case Unix notation used (as it # should be in setup scripts) extra_dirs = convert_path(extra_dirs) - else: path_file = None extra_dirs = '' @@ -568,17 +555,14 @@ class install (Command): self.path_file = path_file self.extra_dirs = extra_dirs - # handle_extra_path () - - - def change_roots (self, *names): + def change_roots(self, *names): + """Change the install direcories pointed by name using root.""" for name in names: attr = "install_" + name setattr(self, attr, change_root(self.root, getattr(self, attr))) def create_home_path(self): - """Create directories under ~ - """ + """Create directories under ~.""" if not self.user: return home = convert_path(os.path.expanduser("~")) @@ -589,8 +573,8 @@ class install (Command): # -- Command execution methods ------------------------------------- - def run (self): - + def run(self): + """Runs the command.""" # Obviously have to build before we can install if not self.skip_build: self.run_command('build') @@ -633,9 +617,8 @@ class install (Command): "you'll have to change the search path yourself"), self.install_lib) - # run () - - def create_path_file (self): + def create_path_file(self): + """Creates the .pth file""" filename = os.path.join(self.install_libbase, self.path_file + ".pth") if self.install_path_file: @@ -648,8 +631,8 @@ class install (Command): # -- Reporting methods --------------------------------------------- - def get_outputs (self): - # Assemble the outputs of all the sub-commands. + def get_outputs(self): + """Assembles the outputs of all the sub-commands.""" outputs = [] for cmd_name in self.get_sub_commands(): cmd = self.get_finalized_command(cmd_name) @@ -665,7 +648,8 @@ class install (Command): return outputs - def get_inputs (self): + def get_inputs(self): + """Returns the inputs of all the sub-commands""" # XXX gee, this looks familiar ;-( inputs = [] for cmd_name in self.get_sub_commands(): @@ -674,25 +658,29 @@ class install (Command): return inputs - # -- Predicates for sub-command list ------------------------------- - def has_lib (self): - """Return true if the current distribution has any Python + def has_lib(self): + """Returns true if the current distribution has any Python modules to install.""" return (self.distribution.has_pure_modules() or self.distribution.has_ext_modules()) - def has_headers (self): + def has_headers(self): + """Returns true if the current distribution has any headers to + install.""" return self.distribution.has_headers() - def has_scripts (self): + def has_scripts(self): + """Returns true if the current distribution has any scripts to. + install.""" return self.distribution.has_scripts() - def has_data (self): + def has_data(self): + """Returns true if the current distribution has any data to. + install.""" return self.distribution.has_data_files() - # 'sub_commands': a list of commands this command might have to run to # get its work done. See cmd.py for more info. sub_commands = [('install_lib', has_lib), @@ -701,5 +689,3 @@ class install (Command): ('install_data', has_data), ('install_egg_info', lambda self:True), ] - -# class install diff --git a/tests/test_install.py b/tests/test_install.py index 9ceccf68..8d7e9722 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -10,11 +10,14 @@ from distutils.command.install import install from distutils.command import install as install_module from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution +from distutils.errors import DistutilsOptionError from distutils.tests import support -class InstallTestCase(support.TempdirManager, unittest.TestCase): +class InstallTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def test_home_installation_scheme(self): # This ensure two things: @@ -112,6 +115,74 @@ class InstallTestCase(support.TempdirManager, unittest.TestCase): self.assert_('userbase' in cmd.config_vars) self.assert_('usersite' in cmd.config_vars) + def test_handle_extra_path(self): + dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) + cmd = install(dist) + + # two elements + cmd.handle_extra_path() + self.assertEquals(cmd.extra_path, ['path', 'dirs']) + self.assertEquals(cmd.extra_dirs, 'dirs') + self.assertEquals(cmd.path_file, 'path') + + # one element + cmd.extra_path = ['path'] + cmd.handle_extra_path() + self.assertEquals(cmd.extra_path, ['path']) + self.assertEquals(cmd.extra_dirs, 'path') + self.assertEquals(cmd.path_file, 'path') + + # none + dist.extra_path = cmd.extra_path = None + cmd.handle_extra_path() + self.assertEquals(cmd.extra_path, None) + self.assertEquals(cmd.extra_dirs, '') + self.assertEquals(cmd.path_file, None) + + # three elements (no way !) + cmd.extra_path = 'path,dirs,again' + self.assertRaises(DistutilsOptionError, cmd.handle_extra_path) + + def test_finalize_options(self): + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # must supply either prefix/exec-prefix/home or + # install-base/install-platbase -- not both + cmd.prefix = 'prefix' + cmd.install_base = 'base' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # must supply either home or prefix/exec-prefix -- not both + cmd.install_base = None + cmd.home = 'home' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # can't combine user with with prefix/exec_prefix/home or + # install_(plat)base + cmd.prefix = None + cmd.user = 'user' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + def test_record(self): + + install_dir = self.mkdtemp() + pkgdir, dist = self.create_dist() + + dist = Distribution() + cmd = install(dist) + dist.command_obj['install'] = cmd + cmd.root = install_dir + cmd.record = os.path.join(pkgdir, 'RECORD') + cmd.ensure_finalized() + + cmd.run() + + # let's check the RECORD file was created with one + # line (the egg info file) + with open(cmd.record) as f: + self.assertEquals(len(f.readlines()), 1) + def test_suite(): return unittest.makeSuite(InstallTestCase) -- cgit v1.2.1 From 74ad50e5f25005d305d4ff74c7cd2de62a42b76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 4 Jun 2009 07:39:50 +0000 Subject: Merged revisions 73197 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73197 | tarek.ziade | 2009-06-04 09:31:52 +0200 (Thu, 04 Jun 2009) | 1 line improved test coverage for distutils.command.install and cleaned it up ........ --- command/install.py | 96 +++++++++++++++++++++++++++------------------------ tests/test_install.py | 73 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 46 deletions(-) diff --git a/command/install.py b/command/install.py index de811733..2a905d92 100644 --- a/command/install.py +++ b/command/install.py @@ -2,11 +2,12 @@ Implements the Distutils 'install' command.""" -from distutils import log - __revision__ = "$Id$" -import sys, os +import sys +import os + +from distutils import log from distutils.core import Command from distutils.debug import DEBUG from distutils.sysconfig import get_config_vars @@ -116,7 +117,7 @@ if HAS_USER_SITE: SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') -class install (Command): +class install(Command): description = "install everything from build directory" @@ -190,7 +191,7 @@ class install (Command): def initialize_options(self): - + """Initializes options.""" # High-level options: these select both an installation base # and scheme. self.prefix = None @@ -267,7 +268,7 @@ class install (Command): # array of user input is decided. Yes, it's quite complex!) def finalize_options(self): - + """Finalizes options.""" # This method (and its pliant slaves, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default # installation directories for modules, extension modules, and @@ -408,25 +409,27 @@ class install (Command): # Punt on doc directories for now -- after all, we're punting on # documentation completely! - def dump_dirs(self, msg): - if DEBUG: - from distutils.fancy_getopt import longopt_xlate - print(msg + ":") - for opt in self.user_options: - opt_name = opt[0] - if opt_name[-1] == "=": - opt_name = opt_name[0:-1] - if opt_name in self.negative_opt: - opt_name = longopt_xlate(self.negative_opt[opt_name]) - val = not getattr(self, opt_name) - else: - opt_name = longopt_xlate(opt_name) - val = getattr(self, opt_name) - print(" %s: %s" % (opt_name, val)) - + """Dumps the list of user options.""" + if not DEBUG: + return + from distutils.fancy_getopt import longopt_xlate + log.debug(msg + ":") + for opt in self.user_options: + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + if opt_name in self.negative_opt: + opt_name = self.negative_opt[opt_name] + opt_name = opt_name.translate(longopt_xlate) + val = not getattr(self, opt_name) + else: + opt_name = opt_name.translate(longopt_xlate) + val = getattr(self, opt_name) + log.debug(" %s: %s" % (opt_name, val)) def finalize_unix(self): + """Finalizes options for posix platforms.""" if self.install_base is not None or self.install_platbase is not None: if ((self.install_lib is None and self.install_purelib is None and @@ -465,8 +468,8 @@ class install (Command): self.install_platbase = self.exec_prefix self.select_scheme("unix_prefix") - - def finalize_other(self): # Windows and Mac OS for now + def finalize_other(self): + """Finalizes options for non-posix platforms""" if self.user: if self.install_userbase is None: raise DistutilsPlatformError( @@ -487,8 +490,8 @@ class install (Command): raise DistutilsPlatformError( "I don't know how to install stuff on '%s'" % os.name) - def select_scheme(self, name): + """Sets the install directories by applying the install schemes.""" # it's the caller's problem if they supply a bad name! scheme = INSTALL_SCHEMES[name] for key in SCHEME_KEYS: @@ -496,7 +499,6 @@ class install (Command): if getattr(self, attrname) is None: setattr(self, attrname, scheme[key]) - def _expand_attrs(self, attrs): for attr in attrs: val = getattr(self, attr) @@ -506,27 +508,25 @@ class install (Command): val = subst_vars(val, self.config_vars) setattr(self, attr, val) - def expand_basedirs(self): - self._expand_attrs(['install_base', - 'install_platbase', - 'root']) + """Calls `os.path.expanduser` on install_base, install_platbase and + root.""" + self._expand_attrs(['install_base', 'install_platbase', 'root']) def expand_dirs(self): - self._expand_attrs(['install_purelib', - 'install_platlib', - 'install_lib', - 'install_headers', - 'install_scripts', - 'install_data',]) - + """Calls `os.path.expanduser` on install dirs.""" + self._expand_attrs(['install_purelib', 'install_platlib', + 'install_lib', 'install_headers', + 'install_scripts', 'install_data',]) def convert_paths(self, *names): + """Call `convert_path` over `names`.""" for name in names: attr = "install_" + name setattr(self, attr, convert_path(getattr(self, attr))) def handle_extra_path(self): + """Set `path_file` and `extra_dirs` using `extra_path`.""" if self.extra_path is None: self.extra_path = self.distribution.extra_path @@ -537,7 +537,7 @@ class install (Command): if len(self.extra_path) == 1: path_file = extra_dirs = self.extra_path[0] elif len(self.extra_path) == 2: - (path_file, extra_dirs) = self.extra_path + path_file, extra_dirs = self.extra_path else: raise DistutilsOptionError( "'extra_path' option must be a list, tuple, or " @@ -546,7 +546,6 @@ class install (Command): # convert to local form in case Unix notation used (as it # should be in setup scripts) extra_dirs = convert_path(extra_dirs) - else: path_file = None extra_dirs = '' @@ -557,13 +556,13 @@ class install (Command): self.extra_dirs = extra_dirs def change_roots(self, *names): + """Change the install direcories pointed by name using root.""" for name in names: attr = "install_" + name setattr(self, attr, change_root(self.root, getattr(self, attr))) def create_home_path(self): - """Create directories under ~ - """ + """Create directories under ~.""" if not self.user: return home = convert_path(os.path.expanduser("~")) @@ -575,6 +574,7 @@ class install (Command): # -- Command execution methods ------------------------------------- def run(self): + """Runs the command.""" # Obviously have to build before we can install if not self.skip_build: self.run_command('build') @@ -618,6 +618,7 @@ class install (Command): self.install_lib) def create_path_file(self): + """Creates the .pth file""" filename = os.path.join(self.install_libbase, self.path_file + ".pth") if self.install_path_file: @@ -631,7 +632,7 @@ class install (Command): # -- Reporting methods --------------------------------------------- def get_outputs(self): - # Assemble the outputs of all the sub-commands. + """Assembles the outputs of all the sub-commands.""" outputs = [] for cmd_name in self.get_sub_commands(): cmd = self.get_finalized_command(cmd_name) @@ -648,6 +649,7 @@ class install (Command): return outputs def get_inputs(self): + """Returns the inputs of all the sub-commands""" # XXX gee, this looks familiar ;-( inputs = [] for cmd_name in self.get_sub_commands(): @@ -656,25 +658,29 @@ class install (Command): return inputs - # -- Predicates for sub-command list ------------------------------- def has_lib(self): - """Return true if the current distribution has any Python + """Returns true if the current distribution has any Python modules to install.""" return (self.distribution.has_pure_modules() or self.distribution.has_ext_modules()) def has_headers(self): + """Returns true if the current distribution has any headers to + install.""" return self.distribution.has_headers() def has_scripts(self): + """Returns true if the current distribution has any scripts to. + install.""" return self.distribution.has_scripts() def has_data(self): + """Returns true if the current distribution has any data to. + install.""" return self.distribution.has_data_files() - # 'sub_commands': a list of commands this command might have to run to # get its work done. See cmd.py for more info. sub_commands = [('install_lib', has_lib), diff --git a/tests/test_install.py b/tests/test_install.py index 9ceccf68..8d7e9722 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -10,11 +10,14 @@ from distutils.command.install import install from distutils.command import install as install_module from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution +from distutils.errors import DistutilsOptionError from distutils.tests import support -class InstallTestCase(support.TempdirManager, unittest.TestCase): +class InstallTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def test_home_installation_scheme(self): # This ensure two things: @@ -112,6 +115,74 @@ class InstallTestCase(support.TempdirManager, unittest.TestCase): self.assert_('userbase' in cmd.config_vars) self.assert_('usersite' in cmd.config_vars) + def test_handle_extra_path(self): + dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) + cmd = install(dist) + + # two elements + cmd.handle_extra_path() + self.assertEquals(cmd.extra_path, ['path', 'dirs']) + self.assertEquals(cmd.extra_dirs, 'dirs') + self.assertEquals(cmd.path_file, 'path') + + # one element + cmd.extra_path = ['path'] + cmd.handle_extra_path() + self.assertEquals(cmd.extra_path, ['path']) + self.assertEquals(cmd.extra_dirs, 'path') + self.assertEquals(cmd.path_file, 'path') + + # none + dist.extra_path = cmd.extra_path = None + cmd.handle_extra_path() + self.assertEquals(cmd.extra_path, None) + self.assertEquals(cmd.extra_dirs, '') + self.assertEquals(cmd.path_file, None) + + # three elements (no way !) + cmd.extra_path = 'path,dirs,again' + self.assertRaises(DistutilsOptionError, cmd.handle_extra_path) + + def test_finalize_options(self): + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # must supply either prefix/exec-prefix/home or + # install-base/install-platbase -- not both + cmd.prefix = 'prefix' + cmd.install_base = 'base' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # must supply either home or prefix/exec-prefix -- not both + cmd.install_base = None + cmd.home = 'home' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # can't combine user with with prefix/exec_prefix/home or + # install_(plat)base + cmd.prefix = None + cmd.user = 'user' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + def test_record(self): + + install_dir = self.mkdtemp() + pkgdir, dist = self.create_dist() + + dist = Distribution() + cmd = install(dist) + dist.command_obj['install'] = cmd + cmd.root = install_dir + cmd.record = os.path.join(pkgdir, 'RECORD') + cmd.ensure_finalized() + + cmd.run() + + # let's check the RECORD file was created with one + # line (the egg info file) + with open(cmd.record) as f: + self.assertEquals(len(f.readlines()), 1) + def test_suite(): return unittest.makeSuite(InstallTestCase) -- cgit v1.2.1 From 22e57debf56bf09cdf3e1bd12f6b6da4663037b7 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 4 Jun 2009 09:42:55 +0000 Subject: More codestring -> codebytes. --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 9249427a..3b4a0367 100644 --- a/command/upload.py +++ b/command/upload.py @@ -127,7 +127,7 @@ class upload(PyPIRCCommand): user_pass = (self.username + ":" + self.password).encode('ascii') # The exact encoding of the authentication string is debated. # Anyway PyPI only accepts ascii for both username or password. - auth = "Basic " + base64.encodestring(user_pass).strip().decode('ascii') + auth = "Basic " + base64.encodebytes(user_pass).strip().decode('ascii') # Build up the MIME payload for the POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' -- cgit v1.2.1 From 0368c370af04800297fc18864d39885cb90f4cd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 5 Jun 2009 13:37:29 +0000 Subject: reverting r72823 : Python trunk has to use latin-1 encoding --- command/bdist_msi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index b42e41b3..52e193eb 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2005, 2006 Martin von Löwis +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2005, 2006 Martin von Löwis # Licensed to PSF under a Contributor Agreement. # The bdist_wininst command proper # based on bdist_wininst -- cgit v1.2.1 From 831813f0d1e42703be6794b49e4ea144995fb9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 10 Jun 2009 18:49:50 +0000 Subject: Distutils: started code cleanup and test coverage for cygwinccompiler --- cygwinccompiler.py | 121 +++++++++++++++++------------------------- tests/test_cygwinccompiler.py | 120 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 71 deletions(-) create mode 100644 tests/test_cygwinccompiler.py diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 3ac1dceb..df5ebaf9 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -47,12 +47,19 @@ cygwin in no-cygwin mode). __revision__ = "$Id$" -import os,sys,copy +import os +import sys +import copy +from subprocess import Popen, PIPE +import re + from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError from distutils import log +from distutils.version import LooseVersion +from distutils.spawn import find_executable def get_msvcr(): """Include the appropriate MSVC runtime library if Python was built @@ -348,16 +355,16 @@ CONFIG_H_NOTOK = "not ok" CONFIG_H_UNCERTAIN = "uncertain" def check_config_h(): + """Check if the current Python installation appears amenable to building + extensions with GCC. + + Returns a tuple (status, details), where 'status' is one of the following + constants: + + - CONFIG_H_OK: all is well, go ahead and compile + - CONFIG_H_NOTOK: doesn't look good + - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h - """Check if the current Python installation (specifically, pyconfig.h) - appears amenable to building extensions with GCC. Returns a tuple - (status, details), where 'status' is one of the following constants: - CONFIG_H_OK - all is well, go ahead and compile - CONFIG_H_NOTOK - doesn't look good - CONFIG_H_UNCERTAIN - not sure -- unable to read pyconfig.h 'details' is a human-readable string explaining the situation. Note there are two ways to conclude "OK": either 'sys.version' contains @@ -369,77 +376,49 @@ def check_config_h(): # "pyconfig.h" check -- should probably be renamed... from distutils import sysconfig - import string - # if sys.version contains GCC then python was compiled with - # GCC, and the pyconfig.h file should be OK - if string.find(sys.version,"GCC") >= 0: - return (CONFIG_H_OK, "sys.version mentions 'GCC'") + # if sys.version contains GCC then python was compiled with GCC, and the + # pyconfig.h file should be OK + if "GCC" in sys.version: + return CONFIG_H_OK, "sys.version mentions 'GCC'" + + # let's see if __GNUC__ is mentioned in python.h fn = sysconfig.get_config_h_filename() try: - # It would probably better to read single lines to search. - # But we do this only once, and it is fast enough - f = open(fn) - s = f.read() - f.close() - + with open(fn) as config_h: + if "__GNUC__" in config_h.read(): + return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn + else: + return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn except IOError, exc: - # if we can't read this file, we cannot say it is wrong - # the compiler will complain later about this file as missing return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) - else: - # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar - if string.find(s,"__GNUC__") >= 0: - return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) - else: - return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) +RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') +def _find_exe_version(cmd): + """Find the version of an executable by running `cmd` in the shell. + If the command is not found, or the output does not match + `RE_VERSION`, returns None. + """ + executable = cmd.split()[0] + if find_executable(executable) is None: + return None + out = Popen(cmd, shell=True, stdout=PIPE).stdout + try: + out_string = out.read() + finally: + out.close() + result = RE_VERSION.search(out_string) + if result is None: + return None + return LooseVersion(result.group(1)) def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. - If not possible it returns None for it. - """ - from distutils.version import LooseVersion - from distutils.spawn import find_executable - import re - gcc_exe = find_executable('gcc') - if gcc_exe: - out = os.popen(gcc_exe + ' -dumpversion','r') - out_string = out.read() - out.close() - result = re.search('(\d+\.\d+(\.\d+)*)',out_string) - if result: - gcc_version = LooseVersion(result.group(1)) - else: - gcc_version = None - else: - gcc_version = None - ld_exe = find_executable('ld') - if ld_exe: - out = os.popen(ld_exe + ' -v','r') - out_string = out.read() - out.close() - result = re.search('(\d+\.\d+(\.\d+)*)',out_string) - if result: - ld_version = LooseVersion(result.group(1)) - else: - ld_version = None - else: - ld_version = None - dllwrap_exe = find_executable('dllwrap') - if dllwrap_exe: - out = os.popen(dllwrap_exe + ' --version','r') - out_string = out.read() - out.close() - result = re.search(' (\d+\.\d+(\.\d+)*)',out_string) - if result: - dllwrap_version = LooseVersion(result.group(1)) - else: - dllwrap_version = None - else: - dllwrap_version = None - return (gcc_version, ld_version, dllwrap_version) + If not possible it returns None for it. + """ + commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] + return tuple([_find_exe_version(cmd) for cmd in commands]) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py new file mode 100644 index 00000000..1d465806 --- /dev/null +++ b/tests/test_cygwinccompiler.py @@ -0,0 +1,120 @@ +"""Tests for distutils.cygwinccompiler.""" +import unittest +import sys +import os +from StringIO import StringIO +import subprocess + +from distutils import cygwinccompiler +from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, + CONFIG_H_OK, CONFIG_H_NOTOK, + CONFIG_H_UNCERTAIN, get_versions) +from distutils.tests import support + +class FakePopen(object): + test_class = None + + def __init__(self, cmd, shell, stdout): + self.cmd = cmd.split()[0] + exes = self.test_class._exes + if self.cmd in exes: + self.stdout = StringIO(exes[self.cmd]) + else: + self.stdout = os.popen(cmd, 'r') + + +class CygwinCCompilerTestCase(support.TempdirManager, + unittest.TestCase): + + def setUp(self): + super(CygwinCCompilerTestCase, self).setUp() + self.version = sys.version + self.python_h = os.path.join(self.mkdtemp(), 'python.h') + from distutils import sysconfig + self.old_get_config_h_filename = sysconfig.get_config_h_filename + sysconfig.get_config_h_filename = self._get_config_h_filename + self.old_find_executable = cygwinccompiler.find_executable + cygwinccompiler.find_executable = self._find_executable + self._exes = {} + self.old_popen = cygwinccompiler.Popen + FakePopen.test_class = self + cygwinccompiler.Popen = FakePopen + + def tearDown(self): + sys.version = self.version + from distutils import sysconfig + sysconfig.get_config_h_filename = self.old_get_config_h_filename + cygwinccompiler.find_executable = self.old_find_executable + cygwinccompiler.Popen = self.old_popen + super(CygwinCCompilerTestCase, self).tearDown() + + def _get_config_h_filename(self): + return self.python_h + + def _find_executable(self, name): + if name in self._exes: + return name + return None + + def test_check_config_h(self): + + # check_config_h looks for "GCC" in sys.version first + # returns CONFIG_H_OK if found + sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' + '4.0.1 (Apple Computer, Inc. build 5370)]') + + self.assertEquals(check_config_h()[0], CONFIG_H_OK) + + # then it tries to see if it can find "__GNUC__" in pyconfig.h + sys.version = 'something without the *CC word' + + # if the file doesn't exist it returns CONFIG_H_UNCERTAIN + self.assertEquals(check_config_h()[0], CONFIG_H_UNCERTAIN) + + # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK + self.write_file(self.python_h, 'xxx') + self.assertEquals(check_config_h()[0], CONFIG_H_NOTOK) + + # and CONFIG_H_OK if __GNUC__ is found + self.write_file(self.python_h, 'xxx __GNUC__ xxx') + self.assertEquals(check_config_h()[0], CONFIG_H_OK) + + def test_get_versions(self): + + # get_versions calls distutils.spawn.find_executable on + # 'gcc', 'ld' and 'dllwrap' + self.assertEquals(get_versions(), (None, None, None)) + + # Let's fake we have 'gcc' and it returns '3.4.5' + self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF' + res = get_versions() + self.assertEquals(str(res[0]), '3.4.5') + + # and let's see what happens when the version + # doesn't match the regular expression + # (\d+\.\d+(\.\d+)*) + self._exes['gcc'] = 'very strange output' + res = get_versions() + self.assertEquals(res[0], None) + + # same thing for ld + self._exes['ld'] = 'GNU ld version 2.17.50 20060824' + res = get_versions() + self.assertEquals(str(res[1]), '2.17.50') + self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_versions() + self.assertEquals(res[1], None) + + # and dllwrap + self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF' + res = get_versions() + self.assertEquals(str(res[2]), '2.17.50') + self._exes['dllwrap'] = 'Cheese Wrap' + res = get_versions() + self.assertEquals(res[2], None) + +def test_suite(): + return unittest.makeSuite(CygwinCCompilerTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 41510f823530aa882a2c08e1c807b3e8d3d5ebf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 10 Jun 2009 18:56:35 +0000 Subject: Merged revisions 73336 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73336 | tarek.ziade | 2009-06-10 20:49:50 +0200 (Wed, 10 Jun 2009) | 1 line Distutils: started code cleanup and test coverage for cygwinccompiler ........ --- cygwinccompiler.py | 120 ++++++++++++++++++------------------------ tests/test_cygwinccompiler.py | 120 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 70 deletions(-) create mode 100644 tests/test_cygwinccompiler.py diff --git a/cygwinccompiler.py b/cygwinccompiler.py index ea4c7971..f5414891 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -47,12 +47,19 @@ cygwin in no-cygwin mode). __revision__ = "$Id$" -import os,sys,copy +import os +import sys +import copy +from subprocess import Popen, PIPE +import re + from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError from distutils import log +from distutils.version import LooseVersion +from distutils.spawn import find_executable def get_msvcr(): """Include the appropriate MSVC runtime library if Python was built @@ -347,16 +354,16 @@ CONFIG_H_NOTOK = "not ok" CONFIG_H_UNCERTAIN = "uncertain" def check_config_h(): + """Check if the current Python installation appears amenable to building + extensions with GCC. + + Returns a tuple (status, details), where 'status' is one of the following + constants: + + - CONFIG_H_OK: all is well, go ahead and compile + - CONFIG_H_NOTOK: doesn't look good + - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h - """Check if the current Python installation (specifically, pyconfig.h) - appears amenable to building extensions with GCC. Returns a tuple - (status, details), where 'status' is one of the following constants: - CONFIG_H_OK - all is well, go ahead and compile - CONFIG_H_NOTOK - doesn't look good - CONFIG_H_UNCERTAIN - not sure -- unable to read pyconfig.h 'details' is a human-readable string explaining the situation. Note there are two ways to conclude "OK": either 'sys.version' contains @@ -368,76 +375,49 @@ def check_config_h(): # "pyconfig.h" check -- should probably be renamed... from distutils import sysconfig - # if sys.version contains GCC then python was compiled with - # GCC, and the pyconfig.h file should be OK - if sys.version.find("GCC") >= 0: - return (CONFIG_H_OK, "sys.version mentions 'GCC'") + # if sys.version contains GCC then python was compiled with GCC, and the + # pyconfig.h file should be OK + if "GCC" in sys.version: + return CONFIG_H_OK, "sys.version mentions 'GCC'" + + # let's see if __GNUC__ is mentioned in python.h fn = sysconfig.get_config_h_filename() try: - # It would probably better to read single lines to search. - # But we do this only once, and it is fast enough - f = open(fn) - s = f.read() - f.close() - + with open(fn) as config_h: + if "__GNUC__" in config_h.read(): + return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn + else: + return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn except IOError as exc: - # if we can't read this file, we cannot say it is wrong - # the compiler will complain later about this file as missing return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) - else: - # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar - if s.find("__GNUC__") >= 0: - return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) - else: - return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) +RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') +def _find_exe_version(cmd): + """Find the version of an executable by running `cmd` in the shell. + If the command is not found, or the output does not match + `RE_VERSION`, returns None. + """ + executable = cmd.split()[0] + if find_executable(executable) is None: + return None + out = Popen(cmd, shell=True, stdout=PIPE).stdout + try: + out_string = out.read() + finally: + out.close() + result = RE_VERSION.search(out_string) + if result is None: + return None + return LooseVersion(result.group(1)) def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. - If not possible it returns None for it. - """ - from distutils.version import LooseVersion - from distutils.spawn import find_executable - import re - gcc_exe = find_executable('gcc') - if gcc_exe: - out = os.popen(gcc_exe + ' -dumpversion','r') - out_string = out.read() - out.close() - result = re.search('(\d+\.\d+(\.\d+)*)', out_string, re.ASCII) - if result: - gcc_version = LooseVersion(result.group(1)) - else: - gcc_version = None - else: - gcc_version = None - ld_exe = find_executable('ld') - if ld_exe: - out = os.popen(ld_exe + ' -v','r') - out_string = out.read() - out.close() - result = re.search('(\d+\.\d+(\.\d+)*)', out_string, re.ASCII) - if result: - ld_version = LooseVersion(result.group(1)) - else: - ld_version = None - else: - ld_version = None - dllwrap_exe = find_executable('dllwrap') - if dllwrap_exe: - out = os.popen(dllwrap_exe + ' --version','r') - out_string = out.read() - out.close() - result = re.search(' (\d+\.\d+(\.\d+)*)', out_string, re.ASCII) - if result: - dllwrap_version = LooseVersion(result.group(1)) - else: - dllwrap_version = None - else: - dllwrap_version = None - return (gcc_version, ld_version, dllwrap_version) + If not possible it returns None for it. + """ + commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] + return tuple([_find_exe_version(cmd) for cmd in commands]) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py new file mode 100644 index 00000000..42c5ea40 --- /dev/null +++ b/tests/test_cygwinccompiler.py @@ -0,0 +1,120 @@ +"""Tests for distutils.cygwinccompiler.""" +import unittest +import sys +import os +from io import StringIO +import subprocess + +from distutils import cygwinccompiler +from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, + CONFIG_H_OK, CONFIG_H_NOTOK, + CONFIG_H_UNCERTAIN, get_versions) +from distutils.tests import support + +class FakePopen(object): + test_class = None + + def __init__(self, cmd, shell, stdout): + self.cmd = cmd.split()[0] + exes = self.test_class._exes + if self.cmd in exes: + self.stdout = StringIO(exes[self.cmd]) + else: + self.stdout = os.popen(cmd, 'r') + + +class CygwinCCompilerTestCase(support.TempdirManager, + unittest.TestCase): + + def setUp(self): + super(CygwinCCompilerTestCase, self).setUp() + self.version = sys.version + self.python_h = os.path.join(self.mkdtemp(), 'python.h') + from distutils import sysconfig + self.old_get_config_h_filename = sysconfig.get_config_h_filename + sysconfig.get_config_h_filename = self._get_config_h_filename + self.old_find_executable = cygwinccompiler.find_executable + cygwinccompiler.find_executable = self._find_executable + self._exes = {} + self.old_popen = cygwinccompiler.Popen + FakePopen.test_class = self + cygwinccompiler.Popen = FakePopen + + def tearDown(self): + sys.version = self.version + from distutils import sysconfig + sysconfig.get_config_h_filename = self.old_get_config_h_filename + cygwinccompiler.find_executable = self.old_find_executable + cygwinccompiler.Popen = self.old_popen + super(CygwinCCompilerTestCase, self).tearDown() + + def _get_config_h_filename(self): + return self.python_h + + def _find_executable(self, name): + if name in self._exes: + return name + return None + + def test_check_config_h(self): + + # check_config_h looks for "GCC" in sys.version first + # returns CONFIG_H_OK if found + sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' + '4.0.1 (Apple Computer, Inc. build 5370)]') + + self.assertEquals(check_config_h()[0], CONFIG_H_OK) + + # then it tries to see if it can find "__GNUC__" in pyconfig.h + sys.version = 'something without the *CC word' + + # if the file doesn't exist it returns CONFIG_H_UNCERTAIN + self.assertEquals(check_config_h()[0], CONFIG_H_UNCERTAIN) + + # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK + self.write_file(self.python_h, 'xxx') + self.assertEquals(check_config_h()[0], CONFIG_H_NOTOK) + + # and CONFIG_H_OK if __GNUC__ is found + self.write_file(self.python_h, 'xxx __GNUC__ xxx') + self.assertEquals(check_config_h()[0], CONFIG_H_OK) + + def test_get_versions(self): + + # get_versions calls distutils.spawn.find_executable on + # 'gcc', 'ld' and 'dllwrap' + self.assertEquals(get_versions(), (None, None, None)) + + # Let's fake we have 'gcc' and it returns '3.4.5' + self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF' + res = get_versions() + self.assertEquals(str(res[0]), '3.4.5') + + # and let's see what happens when the version + # doesn't match the regular expression + # (\d+\.\d+(\.\d+)*) + self._exes['gcc'] = 'very strange output' + res = get_versions() + self.assertEquals(res[0], None) + + # same thing for ld + self._exes['ld'] = 'GNU ld version 2.17.50 20060824' + res = get_versions() + self.assertEquals(str(res[1]), '2.17.50') + self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_versions() + self.assertEquals(res[1], None) + + # and dllwrap + self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF' + res = get_versions() + self.assertEquals(str(res[2]), '2.17.50') + self._exes['dllwrap'] = 'Cheese Wrap' + res = get_versions() + self.assertEquals(res[2], None) + +def test_suite(): + return unittest.makeSuite(CygwinCCompilerTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 0ba4f63285bbba4b9fedf8a46c16fe362fd8201a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 11 Jun 2009 08:12:20 +0000 Subject: Fixed #5201: now distutils.sysconfig.parse_makefile() understands '53264' in Makefiles --- sysconfig.py | 21 ++++++++++++++------- tests/test_sysconfig.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 099e0586..4a4fadde 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -285,18 +285,25 @@ def parse_makefile(fn, g=None): while 1: line = fp.readline() - if line is None: # eof + if line is None: # eof break m = _variable_rx.match(line) if m: n, v = m.group(1, 2) - v = string.strip(v) - if "$" in v: + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: notdone[n] = v else: - try: v = int(v) - except ValueError: pass - done[n] = v + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v # do variable interpolation here while notdone: @@ -324,7 +331,7 @@ def parse_makefile(fn, g=None): else: try: value = int(value) except ValueError: - done[name] = string.strip(value) + done[name] = value.strip() else: done[name] = value del notdone[name] diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 0a5ac294..8534881c 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,5 +1,6 @@ """Tests for distutils.sysconfig.""" import os +import test import unittest from distutils import sysconfig @@ -9,6 +10,14 @@ from test.test_support import TESTFN class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): + def setUp(self): + super(SysconfigTestCase, self).setUp() + self.makefile = None + + def tearDown(self): + if self.makefile is not None: + os.unlink(self.makefile) + super(SysconfigTestCase, self).tearDown() def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() @@ -56,8 +65,32 @@ class SysconfigTestCase(support.EnvironGuard, sysconfig.customize_compiler(comp) self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') + def test_parse_makefile_base(self): + self.makefile = test.test_support.TESTFN + fd = open(self.makefile, 'w') + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + fd.close() + d = sysconfig.parse_makefile(self.makefile) + self.assertEquals(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", + 'OTHER': 'foo'}) + + def test_parse_makefile_literal_dollar(self): + self.makefile = test.test_support.TESTFN + fd = open(self.makefile, 'w') + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + fd.close() + d = sysconfig.parse_makefile(self.makefile) + self.assertEquals(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", + 'OTHER': 'foo'}) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(SysconfigTestCase)) return suite + + +if __name__ == '__main__': + test.test_support.run_unittest(test_suite()) -- cgit v1.2.1 From c242531d00417d756b1f0a83b70d29d818ececb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 11 Jun 2009 08:26:40 +0000 Subject: Merged revisions 73341 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73341 | tarek.ziade | 2009-06-11 10:12:20 +0200 (Thu, 11 Jun 2009) | 1 line Fixed #5201: now distutils.sysconfig.parse_makefile() understands '53264' in Makefiles ........ --- sysconfig.py | 21 ++++++++++++++------- tests/test_sysconfig.py | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 615da07e..6f99de28 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -275,18 +275,25 @@ def parse_makefile(fn, g=None): while 1: line = fp.readline() - if line is None: # eof + if line is None: # eof break m = _variable_rx.match(line) if m: n, v = m.group(1, 2) - v = string.strip(v) - if "$" in v: + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: notdone[n] = v else: - try: v = int(v) - except ValueError: pass - done[n] = v + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v # do variable interpolation here while notdone: @@ -314,7 +321,7 @@ def parse_makefile(fn, g=None): else: try: value = int(value) except ValueError: - done[name] = string.strip(value) + done[name] = value.strip() else: done[name] = value del notdone[name] diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 9f820575..9f139521 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -2,11 +2,20 @@ from distutils import sysconfig import os +import test import unittest from test.test_support import TESTFN class SysconfigTestCase(unittest.TestCase): + def setUp(self): + super(SysconfigTestCase, self).setUp() + self.makefile = None + + def tearDown(self): + if self.makefile is not None: + os.unlink(self.makefile) + super(SysconfigTestCase, self).tearDown() def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() @@ -51,8 +60,22 @@ class SysconfigTestCase(unittest.TestCase): self.assert_(isinstance(cvars, dict)) self.assert_(cvars) + def test_parse_makefile_literal_dollar(self): + self.makefile = test.test_support.TESTFN + fd = open(self.makefile, 'w') + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + fd.close() + d = sysconfig.parse_makefile(self.makefile) + self.assertEquals(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", + 'OTHER': 'foo'}) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(SysconfigTestCase)) return suite + + +if __name__ == '__main__': + test.test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 0f2d78ce11da94cffa073fd8090b7c27933cc7b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 11 Jun 2009 08:31:17 +0000 Subject: Merged revisions 73341 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73341 | tarek.ziade | 2009-06-11 10:12:20 +0200 (Thu, 11 Jun 2009) | 1 line Fixed #5201: now distutils.sysconfig.parse_makefile() understands '53264' in Makefiles ........ --- sysconfig.py | 15 +++++++++++---- tests/test_sysconfig.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 223ff672..0fbd5412 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -286,12 +286,19 @@ def parse_makefile(fn, g=None): if m: n, v = m.group(1, 2) v = v.strip() - if "$" in v: + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: notdone[n] = v else: - try: v = int(v) - except ValueError: pass - done[n] = v + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v # do variable interpolation here while notdone: diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 322df39c..2d8fa271 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,14 +1,23 @@ """Tests for distutils.sysconfig.""" import os +import test import unittest from distutils import sysconfig from distutils.ccompiler import get_default_compiler from distutils.tests import support -from test.support import TESTFN +from test.support import TESTFN, run_unittest class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): + def setUp(self): + super(SysconfigTestCase, self).setUp() + self.makefile = None + + def tearDown(self): + if self.makefile is not None: + os.unlink(self.makefile) + super(SysconfigTestCase, self).tearDown() def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() @@ -56,8 +65,32 @@ class SysconfigTestCase(support.EnvironGuard, sysconfig.customize_compiler(comp) self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') + def test_parse_makefile_base(self): + self.makefile = TESTFN + fd = open(self.makefile, 'w') + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + fd.close() + d = sysconfig.parse_makefile(self.makefile) + self.assertEquals(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", + 'OTHER': 'foo'}) + + def test_parse_makefile_literal_dollar(self): + self.makefile = TESTFN + fd = open(self.makefile, 'w') + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + fd.close() + d = sysconfig.parse_makefile(self.makefile) + self.assertEquals(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", + 'OTHER': 'foo'}) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(SysconfigTestCase)) return suite + + +if __name__ == '__main__': + run_unittest(test_suite()) -- cgit v1.2.1 From d2dbee56d652758ae690ddd7272237c414d7d9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 11 Jun 2009 08:43:26 +0000 Subject: removed the last string.split() call --- sysconfig.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 4a4fadde..dcc7231a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -13,7 +13,6 @@ __revision__ = "$Id$" import os import re -import string import sys from distutils.errors import DistutilsPlatformError @@ -435,7 +434,7 @@ def _init_posix(): # relative to the srcdir, which after installation no longer makes # sense. python_lib = get_python_lib(standard_lib=1) - linkerscript_path = string.split(g['LDSHARED'])[0] + linkerscript_path = g['LDSHARED'].split()[0] linkerscript_name = os.path.basename(linkerscript_path) linkerscript = os.path.join(python_lib, 'config', linkerscript_name) -- cgit v1.2.1 From ea7ef1006672c0a59e837376234e4b70e44936a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 11 Jun 2009 09:13:36 +0000 Subject: #6263 fixed syntax error in distutils.cygwinccompiler --- cygwinccompiler.py | 2 +- tests/test_cygwinccompiler.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index df5ebaf9..fd5296c3 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -81,7 +81,7 @@ def get_msvcr(): # VS2008 / MSVC 9.0 return ['msvcr90'] else: - raise ValueError("Unknown MS Compiler version %i " % msc_Ver) + raise ValueError("Unknown MS Compiler version %s " % msc_ver) class CygwinCCompiler (UnixCCompiler): diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index 1d465806..fb823e4d 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -8,7 +8,8 @@ import subprocess from distutils import cygwinccompiler from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, CONFIG_H_OK, CONFIG_H_NOTOK, - CONFIG_H_UNCERTAIN, get_versions) + CONFIG_H_UNCERTAIN, get_versions, + get_msvcr) from distutils.tests import support class FakePopen(object): @@ -113,6 +114,38 @@ class CygwinCCompilerTestCase(support.TempdirManager, res = get_versions() self.assertEquals(res[2], None) + def test_get_msvcr(self): + + # none + sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' + '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') + self.assertEquals(get_msvcr(), None) + + # MSVC 7.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1300 32 bits (Intel)]') + self.assertEquals(get_msvcr(), ['msvcr70']) + + # MSVC 7.1 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1310 32 bits (Intel)]') + self.assertEquals(get_msvcr(), ['msvcr71']) + + # VS2005 / MSVC 8.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1400 32 bits (Intel)]') + self.assertEquals(get_msvcr(), ['msvcr80']) + + # VS2008 / MSVC 9.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1500 32 bits (Intel)]') + self.assertEquals(get_msvcr(), ['msvcr90']) + + # unknown + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1999 32 bits (Intel)]') + self.assertRaises(ValueError, get_msvcr) + def test_suite(): return unittest.makeSuite(CygwinCCompilerTestCase) -- cgit v1.2.1 From 27e7780a17e1afcfb81b3bded04ff2f673ca2e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 11 Jun 2009 09:17:19 +0000 Subject: Merged revisions 73348 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73348 | tarek.ziade | 2009-06-11 11:13:36 +0200 (Thu, 11 Jun 2009) | 1 line #6263 fixed syntax error in distutils.cygwinccompiler ........ --- cygwinccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 94a7bd96..2dabc0f0 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -76,7 +76,7 @@ def get_msvcr(): # VS2008 / MSVC 9.0 return ['msvcr90'] else: - raise ValueError("Unknown MS Compiler version %i " % msc_Ver) + raise ValueError("Unknown MS Compiler version %s " % msc_ver) class CygwinCCompiler (UnixCCompiler): -- cgit v1.2.1 From e5ed970b227477740df0c7fea99a4326ad43bea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 11 Jun 2009 09:25:41 +0000 Subject: Merged revisions 73348 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73348 | tarek.ziade | 2009-06-11 11:13:36 +0200 (Thu, 11 Jun 2009) | 1 line #6263 fixed syntax error in distutils.cygwinccompiler ........ --- cygwinccompiler.py | 2 +- tests/test_cygwinccompiler.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index f5414891..a552ffd9 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -81,7 +81,7 @@ def get_msvcr(): # VS2008 / MSVC 9.0 return ['msvcr90'] else: - raise ValueError("Unknown MS Compiler version %i " % msc_Ver) + raise ValueError("Unknown MS Compiler version %s " % msc_ver) class CygwinCCompiler (UnixCCompiler): diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index 42c5ea40..c5a6495d 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -8,7 +8,8 @@ import subprocess from distutils import cygwinccompiler from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, CONFIG_H_OK, CONFIG_H_NOTOK, - CONFIG_H_UNCERTAIN, get_versions) + CONFIG_H_UNCERTAIN, get_versions, + get_msvcr) from distutils.tests import support class FakePopen(object): @@ -113,6 +114,38 @@ class CygwinCCompilerTestCase(support.TempdirManager, res = get_versions() self.assertEquals(res[2], None) + def test_get_msvcr(self): + + # none + sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' + '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') + self.assertEquals(get_msvcr(), None) + + # MSVC 7.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1300 32 bits (Intel)]') + self.assertEquals(get_msvcr(), ['msvcr70']) + + # MSVC 7.1 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1310 32 bits (Intel)]') + self.assertEquals(get_msvcr(), ['msvcr71']) + + # VS2005 / MSVC 8.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1400 32 bits (Intel)]') + self.assertEquals(get_msvcr(), ['msvcr80']) + + # VS2008 / MSVC 9.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1500 32 bits (Intel)]') + self.assertEquals(get_msvcr(), ['msvcr90']) + + # unknown + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1999 32 bits (Intel)]') + self.assertRaises(ValueError, get_msvcr) + def test_suite(): return unittest.makeSuite(CygwinCCompilerTestCase) -- cgit v1.2.1 From 36287e0beaf534918cae7634a9e9a2b7a89540f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 11 Jun 2009 09:55:09 +0000 Subject: pep8-fied cygwinccompiler module --- cygwinccompiler.py | 95 +++++++++++++++++------------------------------------- 1 file changed, 30 insertions(+), 65 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index fd5296c3..c35e49d7 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -84,8 +84,9 @@ def get_msvcr(): raise ValueError("Unknown MS Compiler version %s " % msc_ver) -class CygwinCCompiler (UnixCCompiler): - +class CygwinCCompiler(UnixCCompiler): + """ Handles the Cygwin port of the GNU C compiler to Windows. + """ compiler_type = 'cygwin' obj_extension = ".o" static_lib_extension = ".a" @@ -94,11 +95,11 @@ class CygwinCCompiler (UnixCCompiler): shared_lib_format = "%s%s" exe_extension = ".exe" - def __init__ (self, verbose=0, dry_run=0, force=0): + def __init__(self, verbose=0, dry_run=0, force=0): - UnixCCompiler.__init__ (self, verbose, dry_run, force) + UnixCCompiler.__init__(self, verbose, dry_run, force) - (status, details) = check_config_h() + status, details = check_config_h() self.debug_print("Python's GCC status: %s (details: %s)" % (status, details)) if status is not CONFIG_H_OK: @@ -153,10 +154,8 @@ class CygwinCCompiler (UnixCCompiler): # with MSVC 7.0 or later. self.dll_libraries = get_msvcr() - # __init__ () - - def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + """Compiles the source by spawing GCC and windres if needed.""" if ext == '.rc' or ext == '.res': # gcc needs '.res' and '.rc' compiled to object files !!! try: @@ -170,21 +169,11 @@ class CygwinCCompiler (UnixCCompiler): except DistutilsExecError, msg: raise CompileError, msg - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - + def link(self, target_desc, objects, output_filename, output_dir=None, + libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None): + """Link the objects.""" # use separate copies, so we can modify the lists extra_preargs = copy.copy(extra_preargs or []) libraries = copy.copy(libraries or []) @@ -249,64 +238,44 @@ class CygwinCCompiler (UnixCCompiler): if not debug: extra_preargs.append("-s") - UnixCCompiler.link(self, - target_desc, - objects, - output_filename, - output_dir, - libraries, - library_dirs, + UnixCCompiler.link(self, target_desc, objects, output_filename, + output_dir, libraries, library_dirs, runtime_library_dirs, None, # export_symbols, we do this in our def-file - debug, - extra_preargs, - extra_postargs, - build_temp, + debug, extra_preargs, extra_postargs, build_temp, target_lang) - # link () - # -- Miscellaneous methods ----------------------------------------- - # overwrite the one from CCompiler to support rc and res-files - def object_filenames (self, - source_filenames, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' + def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + """Adds supports for rc and res files.""" + if output_dir is None: + output_dir = '' obj_names = [] for src_name in source_filenames: # use normcase to make sure '.rc' is really '.rc' and not '.RC' - (base, ext) = os.path.splitext (os.path.normcase(src_name)) + base, ext = os.path.splitext(os.path.normcase(src_name)) if ext not in (self.src_extensions + ['.rc','.res']): raise UnknownFileError, \ - "unknown file type '%s' (from '%s')" % \ - (ext, src_name) + "unknown file type '%s' (from '%s')" % (ext, src_name) if strip_dir: base = os.path.basename (base) - if ext == '.res' or ext == '.rc': + if ext in ('.res', '.rc'): # these need to be compiled to object files - obj_names.append (os.path.join (output_dir, - base + ext + self.obj_extension)) + obj_names.append (os.path.join(output_dir, + base + ext + self.obj_extension)) else: - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) + obj_names.append (os.path.join(output_dir, + base + self.obj_extension)) return obj_names - # object_filenames () - -# class CygwinCCompiler - - # the same as cygwin plus some additional parameters -class Mingw32CCompiler (CygwinCCompiler): - +class Mingw32CCompiler(CygwinCCompiler): + """ Handles the Mingw32 port of the GNU C compiler to Windows. + """ compiler_type = 'mingw32' - def __init__ (self, - verbose=0, - dry_run=0, - force=0): + def __init__(self, verbose=0, dry_run=0, force=0): CygwinCCompiler.__init__ (self, verbose, dry_run, force) @@ -342,10 +311,6 @@ class Mingw32CCompiler (CygwinCCompiler): # with MSVC 7.0 or later. self.dll_libraries = get_msvcr() - # __init__ () - -# class Mingw32CCompiler - # Because these compilers aren't configured in Python's pyconfig.h file by # default, we should at least warn the user if he is using a unmodified # version. -- cgit v1.2.1 From 781da5cf0f6777bd12a55daef6ecceddb42b302f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 11 Jun 2009 10:00:56 +0000 Subject: Merged revisions 73354 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73354 | tarek.ziade | 2009-06-11 11:55:09 +0200 (Thu, 11 Jun 2009) | 1 line pep8-fied cygwinccompiler module ........ --- cygwinccompiler.py | 92 +++++++++++++++++------------------------------------- 1 file changed, 29 insertions(+), 63 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index a552ffd9..5f3a389e 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -84,8 +84,9 @@ def get_msvcr(): raise ValueError("Unknown MS Compiler version %s " % msc_ver) -class CygwinCCompiler (UnixCCompiler): - +class CygwinCCompiler(UnixCCompiler): + """ Handles the Cygwin port of the GNU C compiler to Windows. + """ compiler_type = 'cygwin' obj_extension = ".o" static_lib_extension = ".a" @@ -94,11 +95,11 @@ class CygwinCCompiler (UnixCCompiler): shared_lib_format = "%s%s" exe_extension = ".exe" - def __init__ (self, verbose=0, dry_run=0, force=0): + def __init__(self, verbose=0, dry_run=0, force=0): - UnixCCompiler.__init__ (self, verbose, dry_run, force) + UnixCCompiler.__init__(self, verbose, dry_run, force) - (status, details) = check_config_h() + status, details = check_config_h() self.debug_print("Python's GCC status: %s (details: %s)" % (status, details)) if status is not CONFIG_H_OK: @@ -153,10 +154,8 @@ class CygwinCCompiler (UnixCCompiler): # with MSVC 7.0 or later. self.dll_libraries = get_msvcr() - # __init__ () - - def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + """Compiles the source by spawing GCC and windres if needed.""" if ext == '.rc' or ext == '.res': # gcc needs '.res' and '.rc' compiled to object files !!! try: @@ -170,21 +169,11 @@ class CygwinCCompiler (UnixCCompiler): except DistutilsExecError as msg: raise CompileError(msg) - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - + def link(self, target_desc, objects, output_filename, output_dir=None, + libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None): + """Link the objects.""" # use separate copies, so we can modify the lists extra_preargs = copy.copy(extra_preargs or []) libraries = copy.copy(libraries or []) @@ -249,63 +238,44 @@ class CygwinCCompiler (UnixCCompiler): if not debug: extra_preargs.append("-s") - UnixCCompiler.link(self, - target_desc, - objects, - output_filename, - output_dir, - libraries, - library_dirs, + UnixCCompiler.link(self, target_desc, objects, output_filename, + output_dir, libraries, library_dirs, runtime_library_dirs, None, # export_symbols, we do this in our def-file - debug, - extra_preargs, - extra_postargs, - build_temp, + debug, extra_preargs, extra_postargs, build_temp, target_lang) - # link () - # -- Miscellaneous methods ----------------------------------------- - # overwrite the one from CCompiler to support rc and res-files - def object_filenames (self, - source_filenames, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' + def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + """Adds supports for rc and res files.""" + if output_dir is None: + output_dir = '' obj_names = [] for src_name in source_filenames: # use normcase to make sure '.rc' is really '.rc' and not '.RC' - (base, ext) = os.path.splitext (os.path.normcase(src_name)) + base, ext = os.path.splitext(os.path.normcase(src_name)) if ext not in (self.src_extensions + ['.rc','.res']): raise UnknownFileError("unknown file type '%s' (from '%s')" % \ (ext, src_name)) if strip_dir: base = os.path.basename (base) - if ext == '.res' or ext == '.rc': + if ext in ('.res', '.rc'): # these need to be compiled to object files - obj_names.append (os.path.join (output_dir, - base + ext + self.obj_extension)) + obj_names.append (os.path.join(output_dir, + base + ext + self.obj_extension)) else: - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) + obj_names.append (os.path.join(output_dir, + base + self.obj_extension)) return obj_names - # object_filenames () - -# class CygwinCCompiler - - # the same as cygwin plus some additional parameters -class Mingw32CCompiler (CygwinCCompiler): - +class Mingw32CCompiler(CygwinCCompiler): + """ Handles the Mingw32 port of the GNU C compiler to Windows. + """ compiler_type = 'mingw32' - def __init__ (self, - verbose=0, - dry_run=0, - force=0): + def __init__(self, verbose=0, dry_run=0, force=0): CygwinCCompiler.__init__ (self, verbose, dry_run, force) @@ -341,10 +311,6 @@ class Mingw32CCompiler (CygwinCCompiler): # with MSVC 7.0 or later. self.dll_libraries = get_msvcr() - # __init__ () - -# class Mingw32CCompiler - # Because these compilers aren't configured in Python's pyconfig.h file by # default, we should at least warn the user if he is using a unmodified # version. -- cgit v1.2.1 From f7d0cca6103fa1de330212ce723836a63d95597f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Fri, 12 Jun 2009 17:28:31 +0000 Subject: Support AMD64 in msilib. Set Win64 on reglocator. Fixes #6258. --- command/bdist_msi.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 52e193eb..d69c4b69 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -342,9 +342,14 @@ class bdist_msi (Command): exe_action = "PythonExe" + ver target_dir_prop = "TARGETDIR" + ver exe_prop = "PYTHON" + ver + if msilib.Win64: + # type: msidbLocatorTypeRawValue + msidbLocatorType64bit + Type = 2+16 + else: + Type = 2 add_data(self.db, "RegLocator", - [(machine_reg, 2, install_path, None, 2), - (user_reg, 1, install_path, None, 2)]) + [(machine_reg, 2, install_path, None, Type), + (user_reg, 1, install_path, None, Type)]) add_data(self.db, "AppSearch", [(machine_prop, machine_reg), (user_prop, user_reg)]) -- cgit v1.2.1 From 7d083e9637d1f453b3a9226250e0bc44f93ed349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Fri, 12 Jun 2009 17:31:41 +0000 Subject: Merged revisions 73390 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73390 | martin.v.loewis | 2009-06-12 19:28:31 +0200 (Fr, 12 Jun 2009) | 3 lines Support AMD64 in msilib. Set Win64 on reglocator. Fixes #6258. ........ --- command/bdist_msi.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index bca98cc2..2e5685f5 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -341,9 +341,14 @@ class bdist_msi(Command): exe_action = "PythonExe" + ver target_dir_prop = "TARGETDIR" + ver exe_prop = "PYTHON" + ver + if msilib.Win64: + # type: msidbLocatorTypeRawValue + msidbLocatorType64bit + Type = 2+16 + else: + Type = 2 add_data(self.db, "RegLocator", - [(machine_reg, 2, install_path, None, 2), - (user_reg, 1, install_path, None, 2)]) + [(machine_reg, 2, install_path, None, Type), + (user_reg, 1, install_path, None, Type)]) add_data(self.db, "AppSearch", [(machine_prop, machine_reg), (user_prop, user_reg)]) -- cgit v1.2.1 From 8da8bbb79ee7dea376212ba2b74319045dd9c929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 13 Jun 2009 09:07:01 +0000 Subject: Merged revisions 73390 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73390 | martin.v.loewis | 2009-06-12 19:28:31 +0200 (Fr, 12 Jun 2009) | 3 lines Support AMD64 in msilib. Set Win64 on reglocator. Fixes #6258. ........ --- command/bdist_msi.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index f94d9579..da0b30d8 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -282,9 +282,14 @@ class bdist_msi (Command): PYTHON.USER if defined, else from PYTHON.MACHINE. PYTHON is PYTHONDIR\python.exe""" install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % self.target_version + if msilib.Win64: + # type: msidbLocatorTypeRawValue + msidbLocatorType64bit + Type = 2+16 + else: + Type = 2 add_data(self.db, "RegLocator", - [("python.machine", 2, install_path, None, 2), - ("python.user", 1, install_path, None, 2)]) + [("python.machine", 2, install_path, None, Type), + ("python.user", 1, install_path, None, Type)]) add_data(self.db, "AppSearch", [("PYTHON.MACHINE", "python.machine"), ("PYTHON.USER", "python.user")]) -- cgit v1.2.1 From 3dae7e94d2ac0d05e9cb68e114ee0babb6cc015e Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 13 Jun 2009 13:15:04 +0000 Subject: bump version to 3.1rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 5a5db532..374ccf50 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1rc1" +__version__ = "3.1rc2" #--end constants-- -- cgit v1.2.1 From 130d9fa567ab96fcbe027b3fed83b757da8bca7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 15 Jun 2009 23:04:29 +0000 Subject: code cleanup --- command/upload.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/command/upload.py b/command/upload.py index 74b06283..d3066816 100644 --- a/command/upload.py +++ b/command/upload.py @@ -1,11 +1,6 @@ """distutils.command.upload Implements the Distutils 'upload' subcommand (upload package to PyPI).""" - -from distutils.errors import * -from distutils.core import PyPIRCCommand -from distutils.spawn import spawn -from distutils import log import sys import os import socket @@ -15,12 +10,12 @@ import base64 import urlparse import cStringIO as StringIO from ConfigParser import ConfigParser +from hashlib import md5 -# this keeps compatibility for 2.3 and 2.4 -if sys.version < "2.5": - from md5 import md5 -else: - from hashlib import md5 +from distutils.errors import * +from distutils.core import PyPIRCCommand +from distutils.spawn import spawn +from distutils import log class upload(PyPIRCCommand): @@ -125,7 +120,8 @@ class upload(PyPIRCCommand): open(filename+".asc").read()) # set up the authentication - auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() + auth = "Basic " + base64.encodestring(self.username + ":" + + self.password).strip() # Build up the MIME payload for the POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' @@ -134,10 +130,10 @@ class upload(PyPIRCCommand): body = StringIO.StringIO() for key, value in data.items(): # handle multiple entries for the same name - if type(value) != type([]): + if not isinstance(value, list): value = [value] for value in value: - if type(value) is tuple: + if isinstance(value, tuple): fn = ';filename="%s"' % value[0] value = value[1] else: -- cgit v1.2.1 From 8fdd3d8d2ce340eee2ceda407bb9ca8f22e5c58a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 15 Jun 2009 23:30:13 +0000 Subject: Issue #6286: distutils upload command now uses urllib2 --- command/upload.py | 56 +++++++++++++++++++++++++------------------------- tests/test_upload.py | 58 ++++++++++++++++++++++------------------------------ 2 files changed, 52 insertions(+), 62 deletions(-) diff --git a/command/upload.py b/command/upload.py index d3066816..8114feb8 100644 --- a/command/upload.py +++ b/command/upload.py @@ -5,7 +5,7 @@ import sys import os import socket import platform -import httplib +from urllib2 import urlopen, Request, HTTPError import base64 import urlparse import cStringIO as StringIO @@ -62,6 +62,15 @@ class upload(PyPIRCCommand): self.upload_file(command, pyversion, filename) def upload_file(self, command, pyversion, filename): + # Makes sure the repository URL is compliant + schema, netloc, url, params, query, fragments = \ + urlparse.urlparse(self.repository) + if params or query or fragments: + raise AssertionError("Incompatible url %s" % self.repository) + + if schema not in ('http', 'https'): + raise AssertionError("unsupported schema " + schema) + # Sign if requested if self.sign: gpg_args = ["gpg", "--detach-sign", "-a", filename] @@ -153,39 +162,30 @@ class upload(PyPIRCCommand): self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) # build the Request - # We can't use urllib2 since we need to send the Basic - # auth right with the first request - schema, netloc, url, params, query, fragments = \ - urlparse.urlparse(self.repository) - assert not params and not query and not fragments - if schema == 'http': - http = httplib.HTTPConnection(netloc) - elif schema == 'https': - http = httplib.HTTPSConnection(netloc) - else: - raise AssertionError, "unsupported schema "+schema - - data = '' - loglevel = log.INFO + headers = {'Content-type': + 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth} + + request = Request(self.repository, data=body, + headers=headers) + # send the data try: - http.connect() - http.putrequest("POST", url) - http.putheader('Content-type', - 'multipart/form-data; boundary=%s'%boundary) - http.putheader('Content-length', str(len(body))) - http.putheader('Authorization', auth) - http.endheaders() - http.send(body) + result = urlopen(request) + status = result.getcode() + reason = result.msg except socket.error, e: self.announce(str(e), log.ERROR) return + except HTTPError, e: + status = e.code + reason = e.msg - r = http.getresponse() - if r.status == 200: - self.announce('Server response (%s): %s' % (r.status, r.reason), + if status == 200: + self.announce('Server response (%s): %s' % (status, reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (r.status, r.reason), + self.announce('Upload failed (%s): %s' % (status, reason), log.ERROR) if self.show_response: - self.announce('-'*75, r.read(), '-'*75) + self.announce('-'*75, result.read(), '-'*75) diff --git a/tests/test_upload.py b/tests/test_upload.py index f57a4a3f..bbcd80db 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -2,8 +2,8 @@ import sys import os import unittest -import httplib +from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution @@ -19,48 +19,37 @@ index-servers = [server1] username:me """ -class Response(object): - def __init__(self, status=200, reason='OK'): - self.status = status - self.reason = reason -class FakeConnection(object): +class FakeOpen(object): - def __init__(self): - self.requests = [] - self.headers = [] - self.body = '' + def __init__(self, url): + self.url = url + if not isinstance(url, str): + self.req = url + else: + self.req = None + self.msg = 'OK' - def __call__(self, netloc): - return self + def getcode(self): + return 200 - def connect(self): - pass - endheaders = connect - - def putrequest(self, method, url): - self.requests.append((method, url)) - - def putheader(self, name, value): - self.headers.append((name, value)) - - def send(self, body): - self.body = body - - def getresponse(self): - return Response() class uploadTestCase(PyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() - self.old_class = httplib.HTTPConnection - self.conn = httplib.HTTPConnection = FakeConnection() + self.old_open = upload_mod.urlopen + upload_mod.urlopen = self._urlopen + self.last_open = None def tearDown(self): - httplib.HTTPConnection = self.old_class + upload_mod.urlopen = self.old_open super(uploadTestCase, self).tearDown() + def _urlopen(self, url): + self.last_open = FakeOpen(url) + return self.last_open + def test_finalize_options(self): # new format @@ -105,12 +94,13 @@ class uploadTestCase(PyPIRCCommandTestCase): cmd.run() # what did we send ? - headers = dict(self.conn.headers) + headers = dict(self.last_open.req.headers) self.assertEquals(headers['Content-length'], '2086') self.assert_(headers['Content-type'].startswith('multipart/form-data')) - - self.assertEquals(self.conn.requests, [('POST', '/pypi')]) - self.assert_('xxx' in self.conn.body) + self.assertEquals(self.last_open.req.get_method(), 'POST') + self.assertEquals(self.last_open.req.get_full_url(), + 'http://pypi.python.org/pypi') + self.assert_('xxx' in self.last_open.req.data) def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From 5cb388ee6a901dba9802d428eafe4f0a9b5e612a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 16 Jun 2009 08:31:01 +0000 Subject: starting distutils.ccompiler test coverage and cleanup --- ccompiler.py | 42 ++++++++++++++++++++---------------------- tests/test_ccompiler.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 tests/test_ccompiler.py diff --git a/ccompiler.py b/ccompiler.py index 5a9ff76d..e093eef2 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1217,27 +1217,27 @@ def gen_preprocess_options (macros, include_dirs): return pp_opts -# gen_preprocess_options () - -def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): +def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries): """Generate linker options for searching library directories and - linking with specific libraries. 'libraries' and 'library_dirs' are, - respectively, lists of library names (not filenames!) and search - directories. Returns a list of command-line options suitable for use - with some compiler (depending on the two format strings passed in). + linking with specific libraries. + + 'libraries' and 'library_dirs' are, respectively, lists of library names + (not filenames!) and search directories. Returns a list of command-line + options suitable for use with some compiler (depending on the two format + strings passed in). """ lib_opts = [] for dir in library_dirs: - lib_opts.append (compiler.library_dir_option (dir)) + lib_opts.append(compiler.library_dir_option(dir)) for dir in runtime_library_dirs: - opt = compiler.runtime_library_dir_option (dir) - if type(opt) is ListType: - lib_opts = lib_opts + opt + opt = compiler.runtime_library_dir_option(dir) + if isinstance(opt, list): + lib_opts.extend(opt) else: - lib_opts.append (opt) + lib_opts.append(opt) # XXX it's important that we *not* remove redundant library mentions! # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to @@ -1246,17 +1246,15 @@ def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): # pretty nasty way to arrange your C code. for lib in libraries: - (lib_dir, lib_name) = os.path.split (lib) - if lib_dir: - lib_file = compiler.find_library_file ([lib_dir], lib_name) - if lib_file: - lib_opts.append (lib_file) + lib_dir, lib_name = os.path.split(lib) + if lib_dir != '': + lib_file = compiler.find_library_file([lib_dir], lib_name) + if lib_file is not None: + lib_opts.append(lib_file) else: - compiler.warn ("no library file corresponding to " - "'%s' found (skipping)" % lib) + compiler.warn("no library file corresponding to " + "'%s' found (skipping)" % lib) else: - lib_opts.append (compiler.library_option (lib)) + lib_opts.append(compiler.library_option(lib)) return lib_opts - -# gen_lib_options () diff --git a/tests/test_ccompiler.py b/tests/test_ccompiler.py new file mode 100644 index 00000000..58c8c5da --- /dev/null +++ b/tests/test_ccompiler.py @@ -0,0 +1,37 @@ +"""Tests for distutils.ccompiler.""" +import os +import unittest + +from distutils.ccompiler import gen_lib_options + +class FakeCompiler(object): + def library_dir_option(self, dir): + return "-L" + dir + + def runtime_library_dir_option(self, dir): + return ["-cool", "-R" + dir] + + def find_library_file(self, dirs, lib, debug=0): + return 'found' + + def library_option(self, lib): + return "-l" + lib + +class CCompilerTestCase(unittest.TestCase): + + def test_gen_lib_options(self): + compiler = FakeCompiler() + libdirs = ['lib1', 'lib2'] + runlibdirs = ['runlib1'] + libs = [os.path.join('dir', 'name'), 'name2'] + + opts = gen_lib_options(compiler, libdirs, runlibdirs, libs) + wanted = ['-Llib1', '-Llib2', '-cool', '-Rrunlib1', 'found', + '-lname2'] + self.assertEquals(opts, wanted) + +def test_suite(): + return unittest.makeSuite(CCompilerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 1b8ce2c6b9877396b49639ffed0feca4f18989a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 20 Jun 2009 13:57:20 +0000 Subject: Fixed #6164 AIX specific linker argument in Distutils unixcompiler --- tests/test_unixccompiler.py | 8 ++++++++ unixccompiler.py | 33 +++++++++++++++++---------------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 94e9edfc..96f5454e 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -86,6 +86,14 @@ class UnixCCompilerTestCase(unittest.TestCase): sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-R/foo') + # AIX C/C++ linker + sys.platform = 'aix' + def gcv(v): + return 'xxx' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-blibpath:/foo') + + def test_suite(): return unittest.makeSuite(UnixCCompilerTestCase) diff --git a/unixccompiler.py b/unixccompiler.py index 0b1dc4af..129ac8cf 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -288,23 +288,24 @@ class UnixCCompiler(CCompiler): return "+s -L" + dir elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] - else: - if compiler[:3] == "gcc" or compiler[:3] == "g++": - # gcc on non-GNU systems does not need -Wl, but can - # use it anyway. Since distutils has always passed in - # -Wl whenever gcc was used in the past it is probably - # safest to keep doing so. - if sysconfig.get_config_var("GNULD") == "yes": - # GNU ld needs an extra option to get a RUNPATH - # instead of just an RPATH. - return "-Wl,--enable-new-dtags,-R" + dir - else: - return "-Wl,-R" + dir + elif compiler[:3] == "gcc" or compiler[:3] == "g++": + # gcc on non-GNU systems does not need -Wl, but can + # use it anyway. Since distutils has always passed in + # -Wl whenever gcc was used in the past it is probably + # safest to keep doing so. + if sysconfig.get_config_var("GNULD") == "yes": + # GNU ld needs an extra option to get a RUNPATH + # instead of just an RPATH. + return "-Wl,--enable-new-dtags,-R" + dir else: - # No idea how --enable-new-dtags would be passed on to - # ld if this system was using GNU ld. Don't know if a - # system like this even exists. - return "-R" + dir + return "-Wl,-R" + dir + elif sys.platform[:3] == "aix": + return "-blibpath:" + dir + else: + # No idea how --enable-new-dtags would be passed on to + # ld if this system was using GNU ld. Don't know if a + # system like this even exists. + return "-R" + dir def library_option(self, lib): return "-l" + lib -- cgit v1.2.1 From b7c54cd287ae36a0e91930dd390fbae077b91d85 Mon Sep 17 00:00:00 2001 From: Steven Bethard Date: Sun, 21 Jun 2009 21:03:41 +0000 Subject: Fix memory bug in bdist_msi. (Commit okayed in issue6319.) --- command/bdist_msi.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index d69c4b69..7a5ca807 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -315,8 +315,7 @@ class bdist_msi (Command): key = seen[afile] add_data(self.db, "DuplicateFile", [(key + version, dir.component, key, None, dir.logical)]) - - + db.Commit() cab.commit(db) def add_find_python(self): -- cgit v1.2.1 From 358676725f152706531025f7951588a1026f35b6 Mon Sep 17 00:00:00 2001 From: Steven Bethard Date: Sun, 21 Jun 2009 21:07:41 +0000 Subject: Merged revisions 73499 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73499 | steven.bethard | 2009-06-21 17:03:41 -0400 (Sun, 21 Jun 2009) | 1 line Fix memory bug in bdist_msi. (Commit okayed in issue6319.) ........ --- command/bdist_msi.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 2e5685f5..c4be47b5 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -314,8 +314,7 @@ class bdist_msi(Command): key = seen[afile] add_data(self.db, "DuplicateFile", [(key + version, dir.component, key, None, dir.logical)]) - - + db.Commit() cab.commit(db) def add_find_python(self): -- cgit v1.2.1 From 32cbe82d88579474ca58b8e7d099a9b13b876e8c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 26 Jun 2009 13:11:28 +0000 Subject: bump version to 3.1 final :) --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 374ccf50..73c77f1f 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1rc2" +__version__ = "3.1" #--end constants-- -- cgit v1.2.1 From 66e12f773def859e16ea917cd0c03f90f00a43f5 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Jun 2009 21:40:27 +0000 Subject: bump to 3.2a0 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 73c77f1f..18b02fc6 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1" +__version__ = "3.2a0" #--end constants-- -- cgit v1.2.1 From 94800550afc7760fb7c596a29c7f5f7bc8fa56cb Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Jun 2009 22:03:18 +0000 Subject: version to 3.1.1a0 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 73c77f1f..994e91fa 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1" +__version__ = "3.1.1a0" #--end constants-- -- cgit v1.2.1 From a538be9a7a15f703914e4ca8365d8756f07aa155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 28 Jun 2009 21:10:49 +0000 Subject: Merged revisions 73435 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73435 | tarek.ziade | 2009-06-16 01:04:29 +0200 (Tue, 16 Jun 2009) | 1 line code cleanup ........ --- command/upload.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/command/upload.py b/command/upload.py index 3b4a0367..26923775 100644 --- a/command/upload.py +++ b/command/upload.py @@ -1,11 +1,6 @@ """distutils.command.upload Implements the Distutils 'upload' subcommand (upload package to PyPI).""" - -from distutils.errors import * -from distutils.core import PyPIRCCommand -from distutils.spawn import spawn -from distutils import log import sys import os, io import socket @@ -14,12 +9,12 @@ import configparser import http.client as httpclient import base64 import urllib.parse +from hashlib import md5 -# this keeps compatibility for 2.3 and 2.4 -if sys.version < "2.5": - from md5 import md5 -else: - from hashlib import md5 +from distutils.errors import * +from distutils.core import PyPIRCCommand +from distutils.spawn import spawn +from distutils import log class upload(PyPIRCCommand): @@ -137,10 +132,10 @@ class upload(PyPIRCCommand): for key, value in data.items(): title = '\nContent-Disposition: form-data; name="%s"' % key # handle multiple entries for the same name - if type(value) != type([]): + if not isinstance(value, list): value = [value] for value in value: - if type(value) is tuple: + if isinstance(value, tuple): title += '; filename="%s"' % value[0] value = value[1] else: -- cgit v1.2.1 From c25f31bc19447133aaa2eb575b15e3b428eea569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 28 Jun 2009 21:26:27 +0000 Subject: Merged revisions 73436 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73436 | tarek.ziade | 2009-06-16 01:30:13 +0200 (Tue, 16 Jun 2009) | 1 line Issue #6286: distutils upload command now uses urllib2 ........ --- command/upload.py | 60 +++++++++++++++++++++++++--------------------------- tests/test_upload.py | 58 +++++++++++++++++++++----------------------------- 2 files changed, 53 insertions(+), 65 deletions(-) diff --git a/command/upload.py b/command/upload.py index 26923775..defdda64 100644 --- a/command/upload.py +++ b/command/upload.py @@ -5,10 +5,9 @@ import sys import os, io import socket import platform -import configparser -import http.client as httpclient +from urllib.request import urlopen, Request, HTTPError import base64 -import urllib.parse +from urllib.parse import urlparse from hashlib import md5 from distutils.errors import * @@ -61,6 +60,15 @@ class upload(PyPIRCCommand): self.upload_file(command, pyversion, filename) def upload_file(self, command, pyversion, filename): + # Makes sure the repository URL is compliant + schema, netloc, url, params, query, fragments = \ + urlparse(self.repository) + if params or query or fragments: + raise AssertionError("Incompatible url %s" % self.repository) + + if schema not in ('http', 'https'): + raise AssertionError("unsupported schema " + schema) + # Sign if requested if self.sign: gpg_args = ["gpg", "--detach-sign", "-a", filename] @@ -153,40 +161,30 @@ class upload(PyPIRCCommand): self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) # build the Request - # We can't use urllib since we need to send the Basic - # auth right with the first request - # TODO(jhylton): Can we fix urllib? - schema, netloc, url, params, query, fragments = \ - urllib.parse.urlparse(self.repository) - assert not params and not query and not fragments - if schema == 'http': - http = httpclient.HTTPConnection(netloc) - elif schema == 'https': - http = httpclient.HTTPSConnection(netloc) - else: - raise AssertionError("unsupported schema "+schema) - - data = '' - loglevel = log.INFO + headers = {'Content-type': + 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth} + + request = Request(self.repository, data=body, + headers=headers) + # send the data try: - http.connect() - http.putrequest("POST", url) - http.putheader('Content-type', - 'multipart/form-data; boundary=%s'%boundary) - http.putheader('Content-length', str(len(body))) - http.putheader('Authorization', auth) - http.endheaders() - http.send(body) + result = urlopen(request) + status = result.getcode() + reason = result.msg except socket.error as e: self.announce(str(e), log.ERROR) return + except HTTPError as e: + status = e.code + reason = e.msg - r = http.getresponse() - if r.status == 200: - self.announce('Server response (%s): %s' % (r.status, r.reason), + if status == 200: + self.announce('Server response (%s): %s' % (status, reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (r.status, r.reason), + self.announce('Upload failed (%s): %s' % (status, reason), log.ERROR) if self.show_response: - self.announce('-'*75, r.read(), '-'*75) + self.announce('-'*75, result.read(), '-'*75) diff --git a/tests/test_upload.py b/tests/test_upload.py index 95e4ac33..aaeaffde 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -2,8 +2,8 @@ import sys import os import unittest -import http.client as httpclient +from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution @@ -19,48 +19,37 @@ index-servers = [server1] username:me """ -class Response(object): - def __init__(self, status=200, reason='OK'): - self.status = status - self.reason = reason -class FakeConnection(object): +class FakeOpen(object): - def __init__(self): - self.requests = [] - self.headers = [] - self.body = '' + def __init__(self, url): + self.url = url + if not isinstance(url, str): + self.req = url + else: + self.req = None + self.msg = 'OK' - def __call__(self, netloc): - return self + def getcode(self): + return 200 - def connect(self): - pass - endheaders = connect - - def putrequest(self, method, url): - self.requests.append((method, url)) - - def putheader(self, name, value): - self.headers.append((name, value)) - - def send(self, body): - self.body = body - - def getresponse(self): - return Response() class uploadTestCase(PyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() - self.old_class = httpclient.HTTPConnection - self.conn = httpclient.HTTPConnection = FakeConnection() + self.old_open = upload_mod.urlopen + upload_mod.urlopen = self._urlopen + self.last_open = None def tearDown(self): - httpclient.HTTPConnection = self.old_class + upload_mod.urlopen = self.old_open super(uploadTestCase, self).tearDown() + def _urlopen(self, url): + self.last_open = FakeOpen(url) + return self.last_open + def test_finalize_options(self): # new format @@ -105,12 +94,13 @@ class uploadTestCase(PyPIRCCommandTestCase): cmd.run() # what did we send ? - headers = dict(self.conn.headers) + headers = dict(self.last_open.req.headers) self.assertEquals(headers['Content-length'], '2087') self.assert_(headers['Content-type'].startswith('multipart/form-data')) - - self.assertEquals(self.conn.requests, [('POST', '/pypi')]) - self.assert_((b'xxx') in self.conn.body) + self.assertEquals(self.last_open.req.get_method(), 'POST') + self.assertEquals(self.last_open.req.get_full_url(), + 'http://pypi.python.org/pypi') + self.assert_(b'xxx' in self.last_open.req.data) def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From 05df8a008c0f8fdf36a253a815390886eed089e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 28 Jun 2009 21:29:24 +0000 Subject: Merged revisions 73445 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73445 | tarek.ziade | 2009-06-16 10:31:01 +0200 (Tue, 16 Jun 2009) | 1 line starting distutils.ccompiler test coverage and cleanup ........ --- ccompiler.py | 22 ++++++++++++---------- tests/test_ccompiler.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 tests/test_ccompiler.py diff --git a/ccompiler.py b/ccompiler.py index 875f96fa..ff7f9df1 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1151,12 +1151,14 @@ def gen_preprocess_options(macros, include_dirs): return pp_opts -def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): +def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries): """Generate linker options for searching library directories and - linking with specific libraries. 'libraries' and 'library_dirs' are, - respectively, lists of library names (not filenames!) and search - directories. Returns a list of command-line options suitable for use - with some compiler (depending on the two format strings passed in). + linking with specific libraries. + + 'libraries' and 'library_dirs' are, respectively, lists of library names + (not filenames!) and search directories. Returns a list of command-line + options suitable for use with some compiler (depending on the two format + strings passed in). """ lib_opts = [] @@ -1166,7 +1168,7 @@ def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): for dir in runtime_library_dirs: opt = compiler.runtime_library_dir_option(dir) if isinstance(opt, list): - lib_opts = lib_opts + opt + lib_opts.extend(opt) else: lib_opts.append(opt) @@ -1177,14 +1179,14 @@ def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): # pretty nasty way to arrange your C code. for lib in libraries: - (lib_dir, lib_name) = os.path.split(lib) - if lib_dir: + lib_dir, lib_name = os.path.split(lib) + if lib_dir != '': lib_file = compiler.find_library_file([lib_dir], lib_name) - if lib_file: + if lib_file is not None: lib_opts.append(lib_file) else: compiler.warn("no library file corresponding to " "'%s' found (skipping)" % lib) else: - lib_opts.append(compiler.library_option (lib)) + lib_opts.append(compiler.library_option(lib)) return lib_opts diff --git a/tests/test_ccompiler.py b/tests/test_ccompiler.py new file mode 100644 index 00000000..58c8c5da --- /dev/null +++ b/tests/test_ccompiler.py @@ -0,0 +1,37 @@ +"""Tests for distutils.ccompiler.""" +import os +import unittest + +from distutils.ccompiler import gen_lib_options + +class FakeCompiler(object): + def library_dir_option(self, dir): + return "-L" + dir + + def runtime_library_dir_option(self, dir): + return ["-cool", "-R" + dir] + + def find_library_file(self, dirs, lib, debug=0): + return 'found' + + def library_option(self, lib): + return "-l" + lib + +class CCompilerTestCase(unittest.TestCase): + + def test_gen_lib_options(self): + compiler = FakeCompiler() + libdirs = ['lib1', 'lib2'] + runlibdirs = ['runlib1'] + libs = [os.path.join('dir', 'name'), 'name2'] + + opts = gen_lib_options(compiler, libdirs, runlibdirs, libs) + wanted = ['-Llib1', '-Llib2', '-cool', '-Rrunlib1', 'found', + '-lname2'] + self.assertEquals(opts, wanted) + +def test_suite(): + return unittest.makeSuite(CCompilerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From d500c02486a30fad1e49244afd7cca46009d0c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 28 Jun 2009 21:30:52 +0000 Subject: Merged revisions 73490 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73490 | tarek.ziade | 2009-06-20 15:57:20 +0200 (Sat, 20 Jun 2009) | 1 line Fixed #6164 AIX specific linker argument in Distutils unixcompiler ........ --- tests/test_unixccompiler.py | 8 ++++++++ unixccompiler.py | 33 +++++++++++++++++---------------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 94e9edfc..96f5454e 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -86,6 +86,14 @@ class UnixCCompilerTestCase(unittest.TestCase): sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-R/foo') + # AIX C/C++ linker + sys.platform = 'aix' + def gcv(v): + return 'xxx' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-blibpath:/foo') + + def test_suite(): return unittest.makeSuite(UnixCCompilerTestCase) diff --git a/unixccompiler.py b/unixccompiler.py index c11544d8..26d2856a 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -286,23 +286,24 @@ class UnixCCompiler(CCompiler): return "+s -L" + dir elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] - else: - if compiler[:3] == "gcc" or compiler[:3] == "g++": - # gcc on non-GNU systems does not need -Wl, but can - # use it anyway. Since distutils has always passed in - # -Wl whenever gcc was used in the past it is probably - # safest to keep doing so. - if sysconfig.get_config_var("GNULD") == "yes": - # GNU ld needs an extra option to get a RUNPATH - # instead of just an RPATH. - return "-Wl,--enable-new-dtags,-R" + dir - else: - return "-Wl,-R" + dir + elif compiler[:3] == "gcc" or compiler[:3] == "g++": + # gcc on non-GNU systems does not need -Wl, but can + # use it anyway. Since distutils has always passed in + # -Wl whenever gcc was used in the past it is probably + # safest to keep doing so. + if sysconfig.get_config_var("GNULD") == "yes": + # GNU ld needs an extra option to get a RUNPATH + # instead of just an RPATH. + return "-Wl,--enable-new-dtags,-R" + dir else: - # No idea how --enable-new-dtags would be passed on to - # ld if this system was using GNU ld. Don't know if a - # system like this even exists. - return "-R" + dir + return "-Wl,-R" + dir + elif sys.platform[:3] == "aix": + return "-blibpath:" + dir + else: + # No idea how --enable-new-dtags would be passed on to + # ld if this system was using GNU ld. Don't know if a + # system like this even exists. + return "-R" + dir def library_option(self, lib): return "-l" + lib -- cgit v1.2.1 From ae2382ced6b44c8d41fdb6eb3763aa64ac708dea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 29 Jun 2009 16:13:39 +0000 Subject: Fixed 6365: wrong inplace location for build_ext if the extension had dots --- command/build_ext.py | 8 ++++---- tests/test_build_ext.py | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 293c214d..c2c1bf13 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -643,16 +643,16 @@ class build_ext (Command): (inplace option). """ fullname = self.get_ext_fullname(ext_name) - filename = self.get_ext_filename(fullname) + modpath = fullname.split('.') + package = '.'.join(modpath[0:-1]) + base = modpath[-1] + filename = self.get_ext_filename(base) if not self.inplace: # no further work needed return os.path.join(self.build_lib, filename) # the inplace option requires to find the package directory # using the build_py command - modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) return os.path.join(package_dir, filename) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 12b8581c..5f28b799 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -339,10 +339,9 @@ class BuildExtTestCase(support.TempdirManager, # inplace = 0, cmd.package = 'bar' cmd.package = 'bar' path = cmd.get_ext_fullpath('foo') - # checking that the last directory is bar + # checking that the last directory is the build_dir path = os.path.split(path)[0] - lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, cmd.package) + self.assertEquals(path, cmd.build_lib) # inplace = 1, cmd.package = 'bar' cmd.inplace = 1 @@ -358,6 +357,19 @@ class BuildExtTestCase(support.TempdirManager, lastdir = os.path.split(path)[-1] self.assertEquals(lastdir, cmd.package) + def test_build_ext_inplace(self): + etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + etree_ext = Extension('lxml.etree', [etree_c]) + dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + cmd = build_ext(dist) + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + wanted = os.path.join(curdir, 'src', 'lxml', 'etree.so') + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 836436ecd7283a03864110e8aa71505605eb346b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 29 Jun 2009 16:19:22 +0000 Subject: Merged revisions 73688 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73688 | tarek.ziade | 2009-06-29 18:13:39 +0200 (Mon, 29 Jun 2009) | 1 line Fixed 6365: wrong inplace location for build_ext if the extension had dots ........ --- command/build_ext.py | 8 ++++---- tests/test_build_ext.py | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 31e036bc..57a110b0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -630,16 +630,16 @@ class build_ext(Command): (inplace option). """ fullname = self.get_ext_fullname(ext_name) - filename = self.get_ext_filename(fullname) + modpath = fullname.split('.') + package = '.'.join(modpath[0:-1]) + base = modpath[-1] + filename = self.get_ext_filename(base) if not self.inplace: # no further work needed return os.path.join(self.build_lib, filename) # the inplace option requires to find the package directory # using the build_py command - modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) return os.path.join(package_dir, filename) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 4ea11a15..fe6009b6 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -339,10 +339,9 @@ class BuildExtTestCase(TempdirManager, # inplace = 0, cmd.package = 'bar' cmd.package = 'bar' path = cmd.get_ext_fullpath('foo') - # checking that the last directory is bar + # checking that the last directory is the build_dir path = os.path.split(path)[0] - lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, cmd.package) + self.assertEquals(path, cmd.build_lib) # inplace = 1, cmd.package = 'bar' cmd.inplace = 1 @@ -358,6 +357,19 @@ class BuildExtTestCase(TempdirManager, lastdir = os.path.split(path)[-1] self.assertEquals(lastdir, cmd.package) + def test_build_ext_inplace(self): + etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + etree_ext = Extension('lxml.etree', [etree_c]) + dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + cmd = build_ext(dist) + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + wanted = os.path.join(curdir, 'src', 'lxml', 'etree.so') + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From fde1516d324741118bd201f845b1b1affc13d757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 29 Jun 2009 16:46:14 +0000 Subject: Merged revisions 73689 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r73689 | tarek.ziade | 2009-06-29 18:19:22 +0200 (Mon, 29 Jun 2009) | 9 lines Merged revisions 73688 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73688 | tarek.ziade | 2009-06-29 18:13:39 +0200 (Mon, 29 Jun 2009) | 1 line Fixed 6365: wrong inplace location for build_ext if the extension had dots ........ ................ --- command/build_ext.py | 8 ++++---- tests/test_build_ext.py | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 31e036bc..57a110b0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -630,16 +630,16 @@ class build_ext(Command): (inplace option). """ fullname = self.get_ext_fullname(ext_name) - filename = self.get_ext_filename(fullname) + modpath = fullname.split('.') + package = '.'.join(modpath[0:-1]) + base = modpath[-1] + filename = self.get_ext_filename(base) if not self.inplace: # no further work needed return os.path.join(self.build_lib, filename) # the inplace option requires to find the package directory # using the build_py command - modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) return os.path.join(package_dir, filename) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 4ea11a15..fe6009b6 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -339,10 +339,9 @@ class BuildExtTestCase(TempdirManager, # inplace = 0, cmd.package = 'bar' cmd.package = 'bar' path = cmd.get_ext_fullpath('foo') - # checking that the last directory is bar + # checking that the last directory is the build_dir path = os.path.split(path)[0] - lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, cmd.package) + self.assertEquals(path, cmd.build_lib) # inplace = 1, cmd.package = 'bar' cmd.inplace = 1 @@ -358,6 +357,19 @@ class BuildExtTestCase(TempdirManager, lastdir = os.path.split(path)[-1] self.assertEquals(lastdir, cmd.package) + def test_build_ext_inplace(self): + etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + etree_ext = Extension('lxml.etree', [etree_c]) + dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + cmd = build_ext(dist) + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + wanted = os.path.join(curdir, 'src', 'lxml', 'etree.so') + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 0b41ce09e9dbf51c0c44d2211982691097de4a0b Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 30 Jun 2009 22:57:08 +0000 Subject: convert usage of fail* to assert* --- tests/test_archive_util.py | 16 ++++++++-------- tests/test_bdist_rpm.py | 4 ++-- tests/test_bdist_wininst.py | 2 +- tests/test_build_clib.py | 2 +- tests/test_build_ext.py | 30 +++++++++++++++--------------- tests/test_build_py.py | 6 +++--- tests/test_build_scripts.py | 10 +++++----- tests/test_clean.py | 2 +- tests/test_cmd.py | 2 +- tests/test_config.py | 4 ++-- tests/test_config_cmd.py | 4 ++-- tests/test_dist.py | 42 +++++++++++++++++++++--------------------- tests/test_install.py | 16 ++++++++-------- tests/test_install_data.py | 12 ++++++------ tests/test_install_lib.py | 6 +++--- tests/test_install_scripts.py | 14 +++++++------- tests/test_msvc9compiler.py | 4 ++-- tests/test_register.py | 16 ++++++++-------- tests/test_sysconfig.py | 12 ++++++------ tests/test_upload.py | 4 ++-- tests/test_util.py | 4 ++-- 21 files changed, 106 insertions(+), 106 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 29cd271f..91bc4e3d 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -47,7 +47,7 @@ class ArchiveUtilTestCase(support.TempdirManager, # check if the compressed tarball was created tarball = base_name + '.tar.gz' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) # trying an uncompressed one base_name = os.path.join(tmpdir2, 'archive') @@ -58,7 +58,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) def _tarinfo(self, path): tar = tarfile.open(path) @@ -96,7 +96,7 @@ class ArchiveUtilTestCase(support.TempdirManager, # check if the compressed tarball was created tarball = base_name + '.tar.gz' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) # now create another tarball using `tar` tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') @@ -110,7 +110,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) - self.assert_(os.path.exists(tarball2)) + self.assertTrue(os.path.exists(tarball2)) # let's compare both tarballs self.assertEquals(self._tarinfo(tarball), self._tarinfo(tarball2)) @@ -123,7 +123,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) # now for a dry_run base_name = os.path.join(tmpdir2, 'archive') @@ -134,7 +134,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) @unittest.skipUnless(find_executable('compress'), 'The compress program is required') @@ -151,7 +151,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar.Z' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) self.assertEquals(len(w.warnings), 1) # same test with dry_run @@ -165,7 +165,7 @@ class ArchiveUtilTestCase(support.TempdirManager, dry_run=True) finally: os.chdir(old_dir) - self.assert_(not os.path.exists(tarball)) + self.assertTrue(not os.path.exists(tarball)) self.assertEquals(len(w.warnings), 1) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 2d84007f..c2715675 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -74,7 +74,7 @@ class BuildRpmTestCase(support.TempdirManager, cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) def test_no_optimize_flag(self): @@ -114,7 +114,7 @@ class BuildRpmTestCase(support.TempdirManager, cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) def test_suite(): diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index f2cb4fde..9b1ba6d1 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -21,7 +21,7 @@ class BuildWinInstTestCase(support.TempdirManager, # and make sure it finds it and returns its content # no matter what platform we have exe_file = cmd.get_exe_bytes() - self.assert_(len(exe_file) > 10) + self.assertTrue(len(exe_file) > 10) def test_suite(): return unittest.makeSuite(BuildWinInstTestCase) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 47d85cd8..536cd673 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -135,7 +135,7 @@ class BuildCLibTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assert_('libfoo.a' in os.listdir(build_temp)) + self.assertTrue('libfoo.a' in os.listdir(build_temp)) def test_suite(): return unittest.makeSuite(BuildCLibTestCase) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 5f28b799..467e90d9 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -73,15 +73,15 @@ class BuildExtTestCase(support.TempdirManager, import xx for attr in ('error', 'foo', 'new', 'roj'): - self.assert_(hasattr(xx, attr)) + self.assertTrue(hasattr(xx, attr)) self.assertEquals(xx.foo(2, 5), 7) self.assertEquals(xx.foo(13,15), 28) self.assertEquals(xx.new().demo(), None) doc = 'This is a template module just for instruction.' self.assertEquals(xx.__doc__, doc) - self.assert_(isinstance(xx.Null(), xx.Null)) - self.assert_(isinstance(xx.Str(), xx.Str)) + self.assertTrue(isinstance(xx.Null(), xx.Null)) + self.assertTrue(isinstance(xx.Str(), xx.Str)) def tearDown(self): # Get everything back to normal @@ -114,7 +114,7 @@ class BuildExtTestCase(support.TempdirManager, _config_vars['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris - self.assert_(len(cmd.library_dirs) > 0) + self.assertTrue(len(cmd.library_dirs) > 0) def test_user_site(self): # site.USER_SITE was introduced in 2.6 @@ -128,7 +128,7 @@ class BuildExtTestCase(support.TempdirManager, # making sure the user option is there options = [name for name, short, lable in cmd.user_options] - self.assert_('user' in options) + self.assertTrue('user' in options) # setting a value cmd.user = 1 @@ -144,9 +144,9 @@ class BuildExtTestCase(support.TempdirManager, # see if include_dirs and library_dirs # were set - self.assert_(lib in cmd.library_dirs) - self.assert_(lib in cmd.rpath) - self.assert_(incl in cmd.include_dirs) + self.assertTrue(lib in cmd.library_dirs) + self.assertTrue(lib in cmd.rpath) + self.assertTrue(incl in cmd.include_dirs) def test_optional_extension(self): @@ -175,10 +175,10 @@ class BuildExtTestCase(support.TempdirManager, from distutils import sysconfig py_include = sysconfig.get_python_inc() - self.assert_(py_include in cmd.include_dirs) + self.assertTrue(py_include in cmd.include_dirs) plat_py_include = sysconfig.get_python_inc(plat_specific=1) - self.assert_(plat_py_include in cmd.include_dirs) + self.assertTrue(plat_py_include in cmd.include_dirs) # make sure cmd.libraries is turned into a list # if it's a string @@ -192,7 +192,7 @@ class BuildExtTestCase(support.TempdirManager, cmd = build_ext(dist) cmd.library_dirs = 'my_lib_dir' cmd.finalize_options() - self.assert_('my_lib_dir' in cmd.library_dirs) + self.assertTrue('my_lib_dir' in cmd.library_dirs) # make sure rpath is turned into a list # if it's a list of os.pathsep's paths @@ -257,13 +257,13 @@ class BuildExtTestCase(support.TempdirManager, 'some': 'bar'})] cmd.check_extensions_list(exts) ext = exts[0] - self.assert_(isinstance(ext, Extension)) + self.assertTrue(isinstance(ext, Extension)) # check_extensions_list adds in ext the values passed # when they are in ('include_dirs', 'library_dirs', 'libraries' # 'extra_objects', 'extra_compile_args', 'extra_link_args') self.assertEquals(ext.libraries, 'foo') - self.assert_(not hasattr(ext, 'some')) + self.assertTrue(not hasattr(ext, 'some')) # 'macros' element of build info dict must be 1- or 2-tuple exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', @@ -321,7 +321,7 @@ class BuildExtTestCase(support.TempdirManager, so_file = cmd.get_outputs()[0] finally: os.chdir(old_wd) - self.assert_(os.path.exists(so_file)) + self.assertTrue(os.path.exists(so_file)) self.assertEquals(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) @@ -330,7 +330,7 @@ class BuildExtTestCase(support.TempdirManager, cmd.inplace = 0 cmd.run() so_file = cmd.get_outputs()[0] - self.assert_(os.path.exists(so_file)) + self.assertTrue(os.path.exists(so_file)) self.assertEquals(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 54a4ed80..c815d818 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -52,9 +52,9 @@ class BuildPyTestCase(support.TempdirManager, self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) - self.assert_("__init__.py" in files) - self.assert_("__init__.pyc" in files) - self.assert_("README.txt" in files) + self.assertTrue("__init__.py" in files) + self.assertTrue("__init__.pyc" in files) + self.assertTrue("README.txt" in files) def test_empty_package_dir (self): # See SF 1668596/1720897. diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index b55eb585..b1d2d079 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -16,12 +16,12 @@ class BuildScriptsTestCase(support.TempdirManager, def test_default_settings(self): cmd = self.get_build_scripts_cmd("/foo/bar", []) - self.assert_(not cmd.force) - self.assert_(cmd.build_dir is None) + self.assertTrue(not cmd.force) + self.assertTrue(cmd.build_dir is None) cmd.finalize_options() - self.assert_(cmd.force) + self.assertTrue(cmd.force) self.assertEqual(cmd.build_dir, "/foo/bar") def test_build(self): @@ -37,7 +37,7 @@ class BuildScriptsTestCase(support.TempdirManager, built = os.listdir(target) for name in expected: - self.assert_(name in built) + self.assertTrue(name in built) def get_build_scripts_cmd(self, target, scripts): import sys @@ -100,7 +100,7 @@ class BuildScriptsTestCase(support.TempdirManager, built = os.listdir(target) for name in expected: - self.assert_(name in built) + self.assertTrue(name in built) def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) diff --git a/tests/test_clean.py b/tests/test_clean.py index 30260327..dbc4ee2a 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -35,7 +35,7 @@ class cleanTestCase(support.TempdirManager, # make sure the files where removed for name, path in dirs: - self.assert_(not os.path.exists(path), + self.assertTrue(not os.path.exists(path), '%s was not removed' % path) # let's run the command again (should spit warnings but suceed) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 8f2b36fb..d6438b5e 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -70,7 +70,7 @@ class CommandTestCase(unittest.TestCase): cmd.option2 = None cmd.ensure_string('option2', 'xxx') - self.assert_(hasattr(cmd, 'option2')) + self.assertTrue(hasattr(cmd, 'option2')) cmd.option3 = 1 self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') diff --git a/tests/test_config.py b/tests/test_config.py index fd778d1b..69472755 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -105,9 +105,9 @@ class PyPIRCCommandTestCase(support.TempdirManager, def test_server_empty_registration(self): cmd = self._cmd(self.dist) rc = cmd._get_rc_file() - self.assert_(not os.path.exists(rc)) + self.assertTrue(not os.path.exists(rc)) cmd._store_pypirc('tarek', 'xxx') - self.assert_(os.path.exists(rc)) + self.assertTrue(os.path.exists(rc)) content = open(rc).read() self.assertEquals(content, WANTED) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index bacf13a2..ef2e7bc3 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -73,14 +73,14 @@ class ConfigTestCase(support.LoggingSilencer, self.write_file(f2, 'xxx') for f in (f1, f2): - self.assert_(os.path.exists(f)) + self.assertTrue(os.path.exists(f)) pkg_dir, dist = self.create_dist() cmd = config(dist) cmd._clean(f1, f2) for f in (f1, f2): - self.assert_(not os.path.exists(f)) + self.assertTrue(not os.path.exists(f)) def test_suite(): return unittest.makeSuite(ConfigTestCase) diff --git a/tests/test_dist.py b/tests/test_dist.py index 7ccdd53b..07f392c4 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -73,7 +73,7 @@ class DistributionTestCase(support.TempdirManager, self.assertEqual(d.get_command_packages(), ["distutils.command", "foo.bar", "distutils.tests"]) cmd = d.get_command_obj("test_dist") - self.assert_(isinstance(cmd, test_dist)) + self.assertTrue(isinstance(cmd, test_dist)) self.assertEqual(cmd.sample_option, "sometext") def test_command_packages_configfile(self): @@ -204,10 +204,10 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, "version": "1.0"} dist = Distribution(attrs) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.0" in meta) - self.assert_("provides:" not in meta.lower()) - self.assert_("requires:" not in meta.lower()) - self.assert_("obsoletes:" not in meta.lower()) + self.assertTrue("Metadata-Version: 1.0" in meta) + self.assertTrue("provides:" not in meta.lower()) + self.assertTrue("requires:" not in meta.lower()) + self.assertTrue("obsoletes:" not in meta.lower()) def test_provides(self): attrs = {"name": "package", @@ -219,9 +219,9 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_provides(), ["package", "package.sub"]) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.1" in meta) - self.assert_("requires:" not in meta.lower()) - self.assert_("obsoletes:" not in meta.lower()) + self.assertTrue("Metadata-Version: 1.1" in meta) + self.assertTrue("requires:" not in meta.lower()) + self.assertTrue("obsoletes:" not in meta.lower()) def test_provides_illegal(self): self.assertRaises(ValueError, Distribution, @@ -239,11 +239,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_requires(), ["other", "another (==1.0)"]) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.1" in meta) - self.assert_("provides:" not in meta.lower()) - self.assert_("Requires: other" in meta) - self.assert_("Requires: another (==1.0)" in meta) - self.assert_("obsoletes:" not in meta.lower()) + self.assertTrue("Metadata-Version: 1.1" in meta) + self.assertTrue("provides:" not in meta.lower()) + self.assertTrue("Requires: other" in meta) + self.assertTrue("Requires: another (==1.0)" in meta) + self.assertTrue("obsoletes:" not in meta.lower()) def test_requires_illegal(self): self.assertRaises(ValueError, Distribution, @@ -261,11 +261,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_obsoletes(), ["other", "another (<1.0)"]) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.1" in meta) - self.assert_("provides:" not in meta.lower()) - self.assert_("requires:" not in meta.lower()) - self.assert_("Obsoletes: other" in meta) - self.assert_("Obsoletes: another (<1.0)" in meta) + self.assertTrue("Metadata-Version: 1.1" in meta) + self.assertTrue("provides:" not in meta.lower()) + self.assertTrue("requires:" not in meta.lower()) + self.assertTrue("Obsoletes: other" in meta) + self.assertTrue("Obsoletes: another (<1.0)" in meta) def test_obsoletes_illegal(self): self.assertRaises(ValueError, Distribution, @@ -299,14 +299,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, if sys.platform in ('linux', 'darwin'): self.environ['HOME'] = temp_dir files = dist.find_config_files() - self.assert_(user_filename in files) + self.assertTrue(user_filename in files) # win32-style if sys.platform == 'win32': # home drive should be found self.environ['HOME'] = temp_dir files = dist.find_config_files() - self.assert_(user_filename in files, + self.assertTrue(user_filename in files, '%r not found in %r' % (user_filename, files)) finally: os.remove(user_filename) @@ -332,7 +332,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, output = [line for line in s.getvalue().split('\n') if line.strip() != ''] - self.assert_(len(output) > 0) + self.assertTrue(len(output) > 0) def test_suite(): suite = unittest.TestSuite() diff --git a/tests/test_install.py b/tests/test_install.py index 8d7e9722..d0ad5ce1 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -88,7 +88,7 @@ class InstallTestCase(support.TempdirManager, def _test_user_site(self): for key in ('nt_user', 'unix_user', 'os2_home'): - self.assert_(key in INSTALL_SCHEMES) + self.assertTrue(key in INSTALL_SCHEMES) dist = Distribution({'name': 'xx'}) cmd = install(dist) @@ -96,24 +96,24 @@ class InstallTestCase(support.TempdirManager, # making sure the user option is there options = [name for name, short, lable in cmd.user_options] - self.assert_('user' in options) + self.assertTrue('user' in options) # setting a value cmd.user = 1 # user base and site shouldn't be created yet - self.assert_(not os.path.exists(self.user_base)) - self.assert_(not os.path.exists(self.user_site)) + self.assertTrue(not os.path.exists(self.user_base)) + self.assertTrue(not os.path.exists(self.user_site)) # let's run finalize cmd.ensure_finalized() # now they should - self.assert_(os.path.exists(self.user_base)) - self.assert_(os.path.exists(self.user_site)) + self.assertTrue(os.path.exists(self.user_base)) + self.assertTrue(os.path.exists(self.user_site)) - self.assert_('userbase' in cmd.config_vars) - self.assert_('usersite' in cmd.config_vars) + self.assertTrue('userbase' in cmd.config_vars) + self.assertTrue('usersite' in cmd.config_vars) def test_handle_extra_path(self): dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 73c40371..70721367 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -35,9 +35,9 @@ class InstallDataTestCase(support.TempdirManager, # let's check the result self.assertEquals(len(cmd.get_outputs()), 2) rtwo = os.path.split(two)[-1] - self.assert_(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) rone = os.path.split(one)[-1] - self.assert_(os.path.exists(os.path.join(inst, rone))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] # let's try with warn_dir one @@ -47,8 +47,8 @@ class InstallDataTestCase(support.TempdirManager, # let's check the result self.assertEquals(len(cmd.get_outputs()), 2) - self.assert_(os.path.exists(os.path.join(inst2, rtwo))) - self.assert_(os.path.exists(os.path.join(inst, rone))) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] # now using root and empty dir @@ -65,8 +65,8 @@ class InstallDataTestCase(support.TempdirManager, # let's check the result self.assertEquals(len(cmd.get_outputs()), 4) - self.assert_(os.path.exists(os.path.join(inst2, rtwo))) - self.assert_(os.path.exists(os.path.join(inst, rone))) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) def test_suite(): return unittest.makeSuite(InstallDataTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index d7681668..793b95ca 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -39,8 +39,8 @@ class InstallLibTestCase(support.TempdirManager, f = os.path.join(pkg_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - self.assert_(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) - self.assert_(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) def test_get_outputs(self): pkg_dir, dist = self.create_dist() @@ -57,7 +57,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.distribution.script_name = 'setup.py' # get_output should return 4 elements - self.assert_(len(cmd.get_outputs()) >= 2) + self.assertTrue(len(cmd.get_outputs()) >= 2) def test_get_inputs(self): pkg_dir, dist = self.create_dist() diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index fffa6ef2..b7eb625a 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -23,15 +23,15 @@ class InstallScriptsTestCase(support.TempdirManager, skip_build=1, ) cmd = install_scripts(dist) - self.assert_(not cmd.force) - self.assert_(not cmd.skip_build) - self.assert_(cmd.build_dir is None) - self.assert_(cmd.install_dir is None) + self.assertTrue(not cmd.force) + self.assertTrue(not cmd.skip_build) + self.assertTrue(cmd.build_dir is None) + self.assertTrue(cmd.install_dir is None) cmd.finalize_options() - self.assert_(cmd.force) - self.assert_(cmd.skip_build) + self.assertTrue(cmd.force) + self.assertTrue(cmd.skip_build) self.assertEqual(cmd.build_dir, "/foo/bar") self.assertEqual(cmd.install_dir, "/splat/funk") @@ -69,7 +69,7 @@ class InstallScriptsTestCase(support.TempdirManager, installed = os.listdir(target) for name in expected: - self.assert_(name in installed) + self.assertTrue(name in installed) def test_suite(): diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 78ce3b7a..21242a8e 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ class msvc9compilerTestCase(unittest.TestCase): # windows registeries versions. path = r'Software\Microsoft\Notepad' v = Reg.get_value(path, u"lfitalic") - self.assert_(v in (0, 1)) + self.assertTrue(v in (0, 1)) import _winreg HKCU = _winreg.HKEY_CURRENT_USER @@ -54,7 +54,7 @@ class msvc9compilerTestCase(unittest.TestCase): self.assertEquals(keys, None) keys = Reg.read_keys(HKCU, r'Software\Microsoft') - self.assert_('Notepad' in keys) + self.assertTrue('Notepad' in keys) def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) diff --git a/tests/test_register.py b/tests/test_register.py index 0c6bf669..ada77a01 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -96,7 +96,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): cmd = self._get_cmd() # we shouldn't have a .pypirc file yet - self.assert_(not os.path.exists(self.rc)) + self.assertTrue(not os.path.exists(self.rc)) # patching raw_input and getpass.getpass # so register gets happy @@ -115,7 +115,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.raw_input # we should have a brand new .pypirc file - self.assert_(os.path.exists(self.rc)) + self.assertTrue(os.path.exists(self.rc)) # with the content similar to WANTED_PYPIRC content = open(self.rc).read() @@ -133,13 +133,13 @@ class RegisterTestCase(PyPIRCCommandTestCase): # let's see what the server received : we should # have 2 similar requests - self.assert_(self.conn.reqs, 2) + self.assertTrue(self.conn.reqs, 2) req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) self.assertEquals(req1['Content-length'], '1374') self.assertEquals(req2['Content-length'], '1374') - self.assert_('xxx' in self.conn.reqs[1].data) + self.assertTrue('xxx' in self.conn.reqs[1].data) def test_password_not_in_file(self): @@ -165,11 +165,11 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.raw_input # we should have send a request - self.assert_(self.conn.reqs, 1) + self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEquals(headers['Content-length'], '608') - self.assert_('tarek' in req.data) + self.assertTrue('tarek' in req.data) def test_password_reset(self): # this test runs choice 3 @@ -183,11 +183,11 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.raw_input # we should have send a request - self.assert_(self.conn.reqs, 1) + self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEquals(headers['Content-length'], '290') - self.assert_('tarek' in req.data) + self.assertTrue('tarek' in req.data) def test_strict(self): # testing the script option diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 8534881c..edc8fd8d 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -21,12 +21,12 @@ class SysconfigTestCase(support.EnvironGuard, def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() - self.assert_(os.path.isfile(config_h), config_h) + self.assertTrue(os.path.isfile(config_h), config_h) def test_get_python_lib(self): lib_dir = sysconfig.get_python_lib() # XXX doesn't work on Linux when Python was never installed before - #self.assert_(os.path.isdir(lib_dir), lib_dir) + #self.assertTrue(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? self.assertNotEqual(sysconfig.get_python_lib(), sysconfig.get_python_lib(prefix=TESTFN)) @@ -36,14 +36,14 @@ class SysconfigTestCase(support.EnvironGuard, # This is not much of a test. We make sure Python.h exists # in the directory returned by get_python_inc() but we don't know # it is the correct file. - self.assert_(os.path.isdir(inc_dir), inc_dir) + self.assertTrue(os.path.isdir(inc_dir), inc_dir) python_h = os.path.join(inc_dir, "Python.h") - self.assert_(os.path.isfile(python_h), python_h) + self.assertTrue(os.path.isfile(python_h), python_h) def test_get_config_vars(self): cvars = sysconfig.get_config_vars() - self.assert_(isinstance(cvars, dict)) - self.assert_(cvars) + self.assertTrue(isinstance(cvars, dict)) + self.assertTrue(cvars) def test_customize_compiler(self): diff --git a/tests/test_upload.py b/tests/test_upload.py index bbcd80db..0d95a091 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -96,11 +96,11 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.last_open.req.headers) self.assertEquals(headers['Content-length'], '2086') - self.assert_(headers['Content-type'].startswith('multipart/form-data')) + self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) self.assertEquals(self.last_open.req.get_method(), 'POST') self.assertEquals(self.last_open.req.get_full_url(), 'http://pypi.python.org/pypi') - self.assert_('xxx' in self.last_open.req.data) + self.assertTrue('xxx' in self.last_open.req.data) def test_suite(): return unittest.makeSuite(uploadTestCase) diff --git a/tests/test_util.py b/tests/test_util.py index cee7d526..c0acf5f4 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -225,10 +225,10 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') for y in yes: - self.assert_(strtobool(y)) + self.assertTrue(strtobool(y)) for n in no: - self.assert_(not strtobool(n)) + self.assertTrue(not strtobool(n)) def test_rfc822_escape(self): header = 'I am a\npoor\nlonesome\nheader\n' -- cgit v1.2.1 From d42774082d0532c78405d5b46f7d9896efcfb7a6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 30 Jun 2009 23:06:06 +0000 Subject: convert old fail* assertions to assert* --- tests/test_archive_util.py | 16 ++++++++-------- tests/test_bdist_rpm.py | 4 ++-- tests/test_bdist_wininst.py | 2 +- tests/test_build_clib.py | 2 +- tests/test_build_ext.py | 30 +++++++++++++++--------------- tests/test_build_py.py | 6 +++--- tests/test_build_scripts.py | 10 +++++----- tests/test_clean.py | 2 +- tests/test_cmd.py | 2 +- tests/test_config.py | 4 ++-- tests/test_config_cmd.py | 4 ++-- tests/test_dist.py | 42 +++++++++++++++++++++--------------------- tests/test_install.py | 16 ++++++++-------- tests/test_install_data.py | 12 ++++++------ tests/test_install_lib.py | 6 +++--- tests/test_install_scripts.py | 14 +++++++------- tests/test_msvc9compiler.py | 4 ++-- tests/test_register.py | 16 ++++++++-------- tests/test_sysconfig.py | 12 ++++++------ tests/test_upload.py | 4 ++-- tests/test_util.py | 4 ++-- 21 files changed, 106 insertions(+), 106 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 663b3353..d88e0b35 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -47,7 +47,7 @@ class ArchiveUtilTestCase(support.TempdirManager, # check if the compressed tarball was created tarball = base_name + '.tar.gz' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) # trying an uncompressed one base_name = os.path.join(tmpdir2, 'archive') @@ -58,7 +58,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) def _tarinfo(self, path): tar = tarfile.open(path) @@ -96,7 +96,7 @@ class ArchiveUtilTestCase(support.TempdirManager, # check if the compressed tarball was created tarball = base_name + '.tar.gz' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) # now create another tarball using `tar` tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') @@ -110,7 +110,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) - self.assert_(os.path.exists(tarball2)) + self.assertTrue(os.path.exists(tarball2)) # let's compare both tarballs self.assertEquals(self._tarinfo(tarball), self._tarinfo(tarball2)) @@ -123,7 +123,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) # now for a dry_run base_name = os.path.join(tmpdir2, 'archive') @@ -134,7 +134,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) @unittest.skipUnless(find_executable('compress'), 'The compress program is required') @@ -151,7 +151,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar.Z' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) self.assertEquals(len(w.warnings), 1) # same test with dry_run @@ -165,7 +165,7 @@ class ArchiveUtilTestCase(support.TempdirManager, dry_run=True) finally: os.chdir(old_dir) - self.assert_(not os.path.exists(tarball)) + self.assertTrue(not os.path.exists(tarball)) self.assertEquals(len(w.warnings), 1) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 2d84007f..c2715675 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -74,7 +74,7 @@ class BuildRpmTestCase(support.TempdirManager, cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) def test_no_optimize_flag(self): @@ -114,7 +114,7 @@ class BuildRpmTestCase(support.TempdirManager, cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) def test_suite(): diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index f2cb4fde..9b1ba6d1 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -21,7 +21,7 @@ class BuildWinInstTestCase(support.TempdirManager, # and make sure it finds it and returns its content # no matter what platform we have exe_file = cmd.get_exe_bytes() - self.assert_(len(exe_file) > 10) + self.assertTrue(len(exe_file) > 10) def test_suite(): return unittest.makeSuite(BuildWinInstTestCase) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 47d85cd8..536cd673 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -135,7 +135,7 @@ class BuildCLibTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assert_('libfoo.a' in os.listdir(build_temp)) + self.assertTrue('libfoo.a' in os.listdir(build_temp)) def test_suite(): return unittest.makeSuite(BuildCLibTestCase) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index fe6009b6..08e3e1ff 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -74,15 +74,15 @@ class BuildExtTestCase(TempdirManager, import xx for attr in ('error', 'foo', 'new', 'roj'): - self.assert_(hasattr(xx, attr)) + self.assertTrue(hasattr(xx, attr)) self.assertEquals(xx.foo(2, 5), 7) self.assertEquals(xx.foo(13,15), 28) self.assertEquals(xx.new().demo(), None) doc = 'This is a template module just for instruction.' self.assertEquals(xx.__doc__, doc) - self.assert_(isinstance(xx.Null(), xx.Null)) - self.assert_(isinstance(xx.Str(), xx.Str)) + self.assertTrue(isinstance(xx.Null(), xx.Null)) + self.assertTrue(isinstance(xx.Str(), xx.Str)) def tearDown(self): # Get everything back to normal @@ -114,7 +114,7 @@ class BuildExtTestCase(TempdirManager, _config_vars['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris - self.assert_(len(cmd.library_dirs) > 0) + self.assertTrue(len(cmd.library_dirs) > 0) def test_user_site(self): # site.USER_SITE was introduced in 2.6 @@ -128,7 +128,7 @@ class BuildExtTestCase(TempdirManager, # making sure the user option is there options = [name for name, short, lable in cmd.user_options] - self.assert_('user' in options) + self.assertTrue('user' in options) # setting a value cmd.user = 1 @@ -144,9 +144,9 @@ class BuildExtTestCase(TempdirManager, # see if include_dirs and library_dirs # were set - self.assert_(lib in cmd.library_dirs) - self.assert_(lib in cmd.rpath) - self.assert_(incl in cmd.include_dirs) + self.assertTrue(lib in cmd.library_dirs) + self.assertTrue(lib in cmd.rpath) + self.assertTrue(incl in cmd.include_dirs) def test_optional_extension(self): @@ -175,10 +175,10 @@ class BuildExtTestCase(TempdirManager, from distutils import sysconfig py_include = sysconfig.get_python_inc() - self.assert_(py_include in cmd.include_dirs) + self.assertTrue(py_include in cmd.include_dirs) plat_py_include = sysconfig.get_python_inc(plat_specific=1) - self.assert_(plat_py_include in cmd.include_dirs) + self.assertTrue(plat_py_include in cmd.include_dirs) # make sure cmd.libraries is turned into a list # if it's a string @@ -192,7 +192,7 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.library_dirs = 'my_lib_dir' cmd.finalize_options() - self.assert_('my_lib_dir' in cmd.library_dirs) + self.assertTrue('my_lib_dir' in cmd.library_dirs) # make sure rpath is turned into a list # if it's a list of os.pathsep's paths @@ -257,13 +257,13 @@ class BuildExtTestCase(TempdirManager, 'some': 'bar'})] cmd.check_extensions_list(exts) ext = exts[0] - self.assert_(isinstance(ext, Extension)) + self.assertTrue(isinstance(ext, Extension)) # check_extensions_list adds in ext the values passed # when they are in ('include_dirs', 'library_dirs', 'libraries' # 'extra_objects', 'extra_compile_args', 'extra_link_args') self.assertEquals(ext.libraries, 'foo') - self.assert_(not hasattr(ext, 'some')) + self.assertTrue(not hasattr(ext, 'some')) # 'macros' element of build info dict must be 1- or 2-tuple exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', @@ -321,7 +321,7 @@ class BuildExtTestCase(TempdirManager, so_file = cmd.get_outputs()[0] finally: os.chdir(old_wd) - self.assert_(os.path.exists(so_file)) + self.assertTrue(os.path.exists(so_file)) self.assertEquals(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) @@ -330,7 +330,7 @@ class BuildExtTestCase(TempdirManager, cmd.inplace = 0 cmd.run() so_file = cmd.get_outputs()[0] - self.assert_(os.path.exists(so_file)) + self.assertTrue(os.path.exists(so_file)) self.assertEquals(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 75b6624f..8ad3bbc4 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -52,9 +52,9 @@ class BuildPyTestCase(support.TempdirManager, self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) - self.assert_("__init__.py" in files) - self.assert_("__init__.pyc" in files) - self.assert_("README.txt" in files) + self.assertTrue("__init__.py" in files) + self.assertTrue("__init__.pyc" in files) + self.assertTrue("README.txt" in files) def test_empty_package_dir (self): # See SF 1668596/1720897. diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index b55eb585..b1d2d079 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -16,12 +16,12 @@ class BuildScriptsTestCase(support.TempdirManager, def test_default_settings(self): cmd = self.get_build_scripts_cmd("/foo/bar", []) - self.assert_(not cmd.force) - self.assert_(cmd.build_dir is None) + self.assertTrue(not cmd.force) + self.assertTrue(cmd.build_dir is None) cmd.finalize_options() - self.assert_(cmd.force) + self.assertTrue(cmd.force) self.assertEqual(cmd.build_dir, "/foo/bar") def test_build(self): @@ -37,7 +37,7 @@ class BuildScriptsTestCase(support.TempdirManager, built = os.listdir(target) for name in expected: - self.assert_(name in built) + self.assertTrue(name in built) def get_build_scripts_cmd(self, target, scripts): import sys @@ -100,7 +100,7 @@ class BuildScriptsTestCase(support.TempdirManager, built = os.listdir(target) for name in expected: - self.assert_(name in built) + self.assertTrue(name in built) def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) diff --git a/tests/test_clean.py b/tests/test_clean.py index 30260327..dbc4ee2a 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -35,7 +35,7 @@ class cleanTestCase(support.TempdirManager, # make sure the files where removed for name, path in dirs: - self.assert_(not os.path.exists(path), + self.assertTrue(not os.path.exists(path), '%s was not removed' % path) # let's run the command again (should spit warnings but suceed) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 8f2b36fb..d6438b5e 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -70,7 +70,7 @@ class CommandTestCase(unittest.TestCase): cmd.option2 = None cmd.ensure_string('option2', 'xxx') - self.assert_(hasattr(cmd, 'option2')) + self.assertTrue(hasattr(cmd, 'option2')) cmd.option3 = 1 self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') diff --git a/tests/test_config.py b/tests/test_config.py index 0f97cf7a..879689d2 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -102,9 +102,9 @@ class PyPIRCCommandTestCase(support.TempdirManager, def test_server_empty_registration(self): cmd = self._cmd(self.dist) rc = cmd._get_rc_file() - self.assert_(not os.path.exists(rc)) + self.assertTrue(not os.path.exists(rc)) cmd._store_pypirc('tarek', 'xxx') - self.assert_(os.path.exists(rc)) + self.assertTrue(os.path.exists(rc)) content = open(rc).read() self.assertEquals(content, WANTED) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index bacf13a2..ef2e7bc3 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -73,14 +73,14 @@ class ConfigTestCase(support.LoggingSilencer, self.write_file(f2, 'xxx') for f in (f1, f2): - self.assert_(os.path.exists(f)) + self.assertTrue(os.path.exists(f)) pkg_dir, dist = self.create_dist() cmd = config(dist) cmd._clean(f1, f2) for f in (f1, f2): - self.assert_(not os.path.exists(f)) + self.assertTrue(not os.path.exists(f)) def test_suite(): return unittest.makeSuite(ConfigTestCase) diff --git a/tests/test_dist.py b/tests/test_dist.py index 16ef68e9..c031cb85 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -72,7 +72,7 @@ class DistributionTestCase(support.LoggingSilencer, self.assertEqual(d.get_command_packages(), ["distutils.command", "foo.bar", "distutils.tests"]) cmd = d.get_command_obj("test_dist") - self.assert_(isinstance(cmd, test_dist)) + self.assertTrue(isinstance(cmd, test_dist)) self.assertEqual(cmd.sample_option, "sometext") def test_command_packages_configfile(self): @@ -175,10 +175,10 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, "version": "1.0"} dist = Distribution(attrs) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.0" in meta) - self.assert_("provides:" not in meta.lower()) - self.assert_("requires:" not in meta.lower()) - self.assert_("obsoletes:" not in meta.lower()) + self.assertTrue("Metadata-Version: 1.0" in meta) + self.assertTrue("provides:" not in meta.lower()) + self.assertTrue("requires:" not in meta.lower()) + self.assertTrue("obsoletes:" not in meta.lower()) def test_provides(self): attrs = {"name": "package", @@ -190,9 +190,9 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_provides(), ["package", "package.sub"]) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.1" in meta) - self.assert_("requires:" not in meta.lower()) - self.assert_("obsoletes:" not in meta.lower()) + self.assertTrue("Metadata-Version: 1.1" in meta) + self.assertTrue("requires:" not in meta.lower()) + self.assertTrue("obsoletes:" not in meta.lower()) def test_provides_illegal(self): self.assertRaises(ValueError, Distribution, @@ -210,11 +210,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_requires(), ["other", "another (==1.0)"]) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.1" in meta) - self.assert_("provides:" not in meta.lower()) - self.assert_("Requires: other" in meta) - self.assert_("Requires: another (==1.0)" in meta) - self.assert_("obsoletes:" not in meta.lower()) + self.assertTrue("Metadata-Version: 1.1" in meta) + self.assertTrue("provides:" not in meta.lower()) + self.assertTrue("Requires: other" in meta) + self.assertTrue("Requires: another (==1.0)" in meta) + self.assertTrue("obsoletes:" not in meta.lower()) def test_requires_illegal(self): self.assertRaises(ValueError, Distribution, @@ -232,11 +232,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_obsoletes(), ["other", "another (<1.0)"]) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.1" in meta) - self.assert_("provides:" not in meta.lower()) - self.assert_("requires:" not in meta.lower()) - self.assert_("Obsoletes: other" in meta) - self.assert_("Obsoletes: another (<1.0)" in meta) + self.assertTrue("Metadata-Version: 1.1" in meta) + self.assertTrue("provides:" not in meta.lower()) + self.assertTrue("requires:" not in meta.lower()) + self.assertTrue("Obsoletes: other" in meta) + self.assertTrue("Obsoletes: another (<1.0)" in meta) def test_obsoletes_illegal(self): self.assertRaises(ValueError, Distribution, @@ -270,14 +270,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, if sys.platform in ('linux', 'darwin'): self.environ['HOME'] = temp_dir files = dist.find_config_files() - self.assert_(user_filename in files) + self.assertTrue(user_filename in files) # win32-style if sys.platform == 'win32': # home drive should be found self.environ['HOME'] = temp_dir files = dist.find_config_files() - self.assert_(user_filename in files, + self.assertTrue(user_filename in files, '%r not found in %r' % (user_filename, files)) finally: os.remove(user_filename) @@ -303,7 +303,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, output = [line for line in s.getvalue().split('\n') if line.strip() != ''] - self.assert_(len(output) > 0) + self.assertTrue(len(output) > 0) def test_suite(): suite = unittest.TestSuite() diff --git a/tests/test_install.py b/tests/test_install.py index 8d7e9722..d0ad5ce1 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -88,7 +88,7 @@ class InstallTestCase(support.TempdirManager, def _test_user_site(self): for key in ('nt_user', 'unix_user', 'os2_home'): - self.assert_(key in INSTALL_SCHEMES) + self.assertTrue(key in INSTALL_SCHEMES) dist = Distribution({'name': 'xx'}) cmd = install(dist) @@ -96,24 +96,24 @@ class InstallTestCase(support.TempdirManager, # making sure the user option is there options = [name for name, short, lable in cmd.user_options] - self.assert_('user' in options) + self.assertTrue('user' in options) # setting a value cmd.user = 1 # user base and site shouldn't be created yet - self.assert_(not os.path.exists(self.user_base)) - self.assert_(not os.path.exists(self.user_site)) + self.assertTrue(not os.path.exists(self.user_base)) + self.assertTrue(not os.path.exists(self.user_site)) # let's run finalize cmd.ensure_finalized() # now they should - self.assert_(os.path.exists(self.user_base)) - self.assert_(os.path.exists(self.user_site)) + self.assertTrue(os.path.exists(self.user_base)) + self.assertTrue(os.path.exists(self.user_site)) - self.assert_('userbase' in cmd.config_vars) - self.assert_('usersite' in cmd.config_vars) + self.assertTrue('userbase' in cmd.config_vars) + self.assertTrue('usersite' in cmd.config_vars) def test_handle_extra_path(self): dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 73c40371..70721367 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -35,9 +35,9 @@ class InstallDataTestCase(support.TempdirManager, # let's check the result self.assertEquals(len(cmd.get_outputs()), 2) rtwo = os.path.split(two)[-1] - self.assert_(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) rone = os.path.split(one)[-1] - self.assert_(os.path.exists(os.path.join(inst, rone))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] # let's try with warn_dir one @@ -47,8 +47,8 @@ class InstallDataTestCase(support.TempdirManager, # let's check the result self.assertEquals(len(cmd.get_outputs()), 2) - self.assert_(os.path.exists(os.path.join(inst2, rtwo))) - self.assert_(os.path.exists(os.path.join(inst, rone))) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] # now using root and empty dir @@ -65,8 +65,8 @@ class InstallDataTestCase(support.TempdirManager, # let's check the result self.assertEquals(len(cmd.get_outputs()), 4) - self.assert_(os.path.exists(os.path.join(inst2, rtwo))) - self.assert_(os.path.exists(os.path.join(inst, rone))) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) def test_suite(): return unittest.makeSuite(InstallDataTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index d7681668..793b95ca 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -39,8 +39,8 @@ class InstallLibTestCase(support.TempdirManager, f = os.path.join(pkg_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - self.assert_(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) - self.assert_(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) def test_get_outputs(self): pkg_dir, dist = self.create_dist() @@ -57,7 +57,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.distribution.script_name = 'setup.py' # get_output should return 4 elements - self.assert_(len(cmd.get_outputs()) >= 2) + self.assertTrue(len(cmd.get_outputs()) >= 2) def test_get_inputs(self): pkg_dir, dist = self.create_dist() diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index fffa6ef2..b7eb625a 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -23,15 +23,15 @@ class InstallScriptsTestCase(support.TempdirManager, skip_build=1, ) cmd = install_scripts(dist) - self.assert_(not cmd.force) - self.assert_(not cmd.skip_build) - self.assert_(cmd.build_dir is None) - self.assert_(cmd.install_dir is None) + self.assertTrue(not cmd.force) + self.assertTrue(not cmd.skip_build) + self.assertTrue(cmd.build_dir is None) + self.assertTrue(cmd.install_dir is None) cmd.finalize_options() - self.assert_(cmd.force) - self.assert_(cmd.skip_build) + self.assertTrue(cmd.force) + self.assertTrue(cmd.skip_build) self.assertEqual(cmd.build_dir, "/foo/bar") self.assertEqual(cmd.install_dir, "/splat/funk") @@ -69,7 +69,7 @@ class InstallScriptsTestCase(support.TempdirManager, installed = os.listdir(target) for name in expected: - self.assert_(name in installed) + self.assertTrue(name in installed) def test_suite(): diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 11e5a476..73827988 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ class msvc9compilerTestCase(unittest.TestCase): # windows registeries versions. path = r'Software\Microsoft\Notepad' v = Reg.get_value(path, "lfitalic") - self.assert_(v in (0, 1)) + self.assertTrue(v in (0, 1)) import winreg HKCU = winreg.HKEY_CURRENT_USER @@ -54,7 +54,7 @@ class msvc9compilerTestCase(unittest.TestCase): self.assertEquals(keys, None) keys = Reg.read_keys(HKCU, r'Software\Microsoft') - self.assert_('Notepad' in keys) + self.assertTrue('Notepad' in keys) def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) diff --git a/tests/test_register.py b/tests/test_register.py index d9ff3ec4..c03ad101 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -96,7 +96,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): cmd = self._get_cmd() # we shouldn't have a .pypirc file yet - self.assert_(not os.path.exists(self.rc)) + self.assertTrue(not os.path.exists(self.rc)) # patching input and getpass.getpass # so register gets happy @@ -115,7 +115,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.input # we should have a brand new .pypirc file - self.assert_(os.path.exists(self.rc)) + self.assertTrue(os.path.exists(self.rc)) # with the content similar to WANTED_PYPIRC content = open(self.rc).read() @@ -133,13 +133,13 @@ class RegisterTestCase(PyPIRCCommandTestCase): # let's see what the server received : we should # have 2 similar requests - self.assert_(self.conn.reqs, 2) + self.assertTrue(self.conn.reqs, 2) req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) self.assertEquals(req1['Content-length'], '1374') self.assertEquals(req2['Content-length'], '1374') - self.assert_((b'xxx') in self.conn.reqs[1].data) + self.assertTrue((b'xxx') in self.conn.reqs[1].data) def test_password_not_in_file(self): @@ -165,11 +165,11 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.input # we should have send a request - self.assert_(self.conn.reqs, 1) + self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEquals(headers['Content-length'], '608') - self.assert_((b'tarek') in req.data) + self.assertTrue((b'tarek') in req.data) def test_password_reset(self): # this test runs choice 3 @@ -183,11 +183,11 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.input # we should have send a request - self.assert_(self.conn.reqs, 1) + self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEquals(headers['Content-length'], '290') - self.assert_((b'tarek') in req.data) + self.assertTrue((b'tarek') in req.data) def test_strict(self): # testing the script option diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 2d8fa271..eb362044 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -21,12 +21,12 @@ class SysconfigTestCase(support.EnvironGuard, def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() - self.assert_(os.path.isfile(config_h), config_h) + self.assertTrue(os.path.isfile(config_h), config_h) def test_get_python_lib(self): lib_dir = sysconfig.get_python_lib() # XXX doesn't work on Linux when Python was never installed before - #self.assert_(os.path.isdir(lib_dir), lib_dir) + #self.assertTrue(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? self.assertNotEqual(sysconfig.get_python_lib(), sysconfig.get_python_lib(prefix=TESTFN)) @@ -36,14 +36,14 @@ class SysconfigTestCase(support.EnvironGuard, # This is not much of a test. We make sure Python.h exists # in the directory returned by get_python_inc() but we don't know # it is the correct file. - self.assert_(os.path.isdir(inc_dir), inc_dir) + self.assertTrue(os.path.isdir(inc_dir), inc_dir) python_h = os.path.join(inc_dir, "Python.h") - self.assert_(os.path.isfile(python_h), python_h) + self.assertTrue(os.path.isfile(python_h), python_h) def test_get_config_vars(self): cvars = sysconfig.get_config_vars() - self.assert_(isinstance(cvars, dict)) - self.assert_(cvars) + self.assertTrue(isinstance(cvars, dict)) + self.assertTrue(cvars) def test_customize_compiler(self): diff --git a/tests/test_upload.py b/tests/test_upload.py index aaeaffde..92818858 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -96,11 +96,11 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.last_open.req.headers) self.assertEquals(headers['Content-length'], '2087') - self.assert_(headers['Content-type'].startswith('multipart/form-data')) + self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) self.assertEquals(self.last_open.req.get_method(), 'POST') self.assertEquals(self.last_open.req.get_full_url(), 'http://pypi.python.org/pypi') - self.assert_(b'xxx' in self.last_open.req.data) + self.assertTrue(b'xxx' in self.last_open.req.data) def test_suite(): return unittest.makeSuite(uploadTestCase) diff --git a/tests/test_util.py b/tests/test_util.py index cee7d526..c0acf5f4 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -225,10 +225,10 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') for y in yes: - self.assert_(strtobool(y)) + self.assertTrue(strtobool(y)) for n in no: - self.assert_(not strtobool(n)) + self.assertTrue(not strtobool(n)) def test_rfc822_escape(self): header = 'I am a\npoor\nlonesome\nheader\n' -- cgit v1.2.1 From b9706d78715b6195bb26fdf1e7f24715b9bcd56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 2 Jul 2009 12:47:54 +0000 Subject: raising bdist_dumb test coverage --- tests/test_bdist_dumb.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index d2ea201b..b28f89f5 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -71,6 +71,21 @@ class BuildDumbTestCase(support.TempdirManager, # now let's check what we have in the zip file # XXX to be done + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + os.chdir(pkg_dir) + cmd = bdist_dumb(dist) + self.assertEquals(cmd.bdist_dir, None) + cmd.finalize_options() + + # bdist_dir is initialized to bdist_base/dumb if not set + base = cmd.get_finalized_command('bdist').bdist_base + self.assertEquals(cmd.bdist_dir, os.path.join(base, 'dumb')) + + # the format is set to a default value depending on the os.name + default = cmd.default_format[os.name] + self.assertEquals(cmd.format, default) + def test_suite(): return unittest.makeSuite(BuildDumbTestCase) -- cgit v1.2.1 From 3eb63e2496e541c94db37e4d73a75cbd372be657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 2 Jul 2009 12:51:56 +0000 Subject: cleaned up the bdist_dumb module --- command/bdist_dumb.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 7324c6c2..3ab0330f 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -7,16 +7,17 @@ $exec_prefix).""" __revision__ = "$Id$" import os + from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import remove_tree, ensure_relative -from distutils.errors import * +from distutils.errors import DistutilsPlatformError from distutils.sysconfig import get_python_version from distutils import log class bdist_dumb (Command): - description = "create a \"dumb\" built distribution" + description = 'create a "dumb" built distribution' user_options = [('bdist-dir=', 'd', "temporary directory for creating the distribution"), @@ -53,11 +54,7 @@ class bdist_dumb (Command): self.skip_build = 0 self.relative = 0 - # initialize_options() - - - def finalize_options (self): - + def finalize_options(self): if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'dumb') @@ -74,11 +71,7 @@ class bdist_dumb (Command): ('dist_dir', 'dist_dir'), ('plat_name', 'plat_name')) - # finalize_options() - - def run (self): - if not self.skip_build: self.run_command('build') @@ -127,7 +120,3 @@ class bdist_dumb (Command): if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) - - # run() - -# class bdist_dumb -- cgit v1.2.1 From edea35d83e5b65b14a035dd01253f502a29c62d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 2 Jul 2009 13:02:21 +0000 Subject: Merged revisions 73756-73757 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73756 | tarek.ziade | 2009-07-02 14:47:54 +0200 (Thu, 02 Jul 2009) | 1 line raising bdist_dumb test coverage ........ r73757 | tarek.ziade | 2009-07-02 14:51:56 +0200 (Thu, 02 Jul 2009) | 1 line cleaned up the bdist_dumb module ........ --- command/bdist_dumb.py | 5 +++-- tests/test_bdist_dumb.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 2d399226..63c0a475 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -7,16 +7,17 @@ $exec_prefix).""" __revision__ = "$Id$" import os + from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import remove_tree, ensure_relative -from distutils.errors import * +from distutils.errors import DistutilsPlatformError from distutils.sysconfig import get_python_version from distutils import log class bdist_dumb(Command): - description = "create a \"dumb\" built distribution" + description = 'create a "dumb" built distribution' user_options = [('bdist-dir=', 'd', "temporary directory for creating the distribution"), diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index d2ea201b..b28f89f5 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -71,6 +71,21 @@ class BuildDumbTestCase(support.TempdirManager, # now let's check what we have in the zip file # XXX to be done + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + os.chdir(pkg_dir) + cmd = bdist_dumb(dist) + self.assertEquals(cmd.bdist_dir, None) + cmd.finalize_options() + + # bdist_dir is initialized to bdist_base/dumb if not set + base = cmd.get_finalized_command('bdist').bdist_base + self.assertEquals(cmd.bdist_dir, os.path.join(base, 'dumb')) + + # the format is set to a default value depending on the os.name + default = cmd.default_format[os.name] + self.assertEquals(cmd.format, default) + def test_suite(): return unittest.makeSuite(BuildDumbTestCase) -- cgit v1.2.1 From d25aae0db2d96401cf8e3471d594dccf1a3b025c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 2 Jul 2009 14:20:47 +0000 Subject: pep8-fied and cleaned up distutils.util --- util.py | 142 ++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 67 insertions(+), 75 deletions(-) diff --git a/util.py b/util.py index e9d29ff4..5bbbf225 100644 --- a/util.py +++ b/util.py @@ -12,9 +12,10 @@ from distutils.dep_util import newer from distutils.spawn import spawn from distutils import log -def get_platform (): - """Return a string that identifies the current platform. This is used - mainly to distinguish platform-specific build directories and +def get_platform(): + """Return a string that identifies the current platform. + + This is used mainly to distinguish platform-specific build directories and platform-specific built distributions. Typically includes the OS name and version and the architecture (as supplied by 'os.uname()'), although the exact information included depends on the OS; eg. for IRIX @@ -39,14 +40,14 @@ def get_platform (): if os.name == 'nt': # sniff sys.version for architecture. prefix = " bit (" - i = string.find(sys.version, prefix) + i = sys.version.find(prefix) if i == -1: return sys.platform - j = string.find(sys.version, ")", i) + j = sys.version.find(")", i) look = sys.version[i+len(prefix):j].lower() - if look=='amd64': + if look == 'amd64': return 'win-amd64' - if look=='itanium': + if look == 'itanium': return 'win-ia64' return sys.platform @@ -61,10 +62,9 @@ def get_platform (): # Convert the OS name to lowercase, remove '/' characters # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") - osname = string.lower(osname) - osname = string.replace(osname, '/', '') - machine = string.replace(machine, ' ', '_') - machine = string.replace(machine, '/', '-') + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') if osname[:5] == "linux": # At least on Linux/Intel, 'machine' is the processor -- @@ -154,11 +154,10 @@ def get_platform (): return "%s-%s-%s" % (osname, release, machine) -# get_platform () +def convert_path(pathname): + """Return 'pathname' as a name that will work on the native filesystem. -def convert_path (pathname): - """Return 'pathname' as a name that will work on the native filesystem, i.e. split it on '/' and put it back together again using the current directory separator. Needed because filenames in the setup script are always supplied in Unix style, and have to be converted to the local @@ -171,23 +170,23 @@ def convert_path (pathname): if not pathname: return pathname if pathname[0] == '/': - raise ValueError, "path '%s' cannot be absolute" % pathname + raise ValueError("path '%s' cannot be absolute" % pathname) if pathname[-1] == '/': - raise ValueError, "path '%s' cannot end with '/'" % pathname + raise ValueError("path '%s' cannot end with '/'" % pathname) - paths = string.split(pathname, '/') + paths = pathname.split('/') while '.' in paths: paths.remove('.') if not paths: return os.curdir - return apply(os.path.join, paths) + return os.path.join(*paths) -# convert_path () +def change_root(new_root, pathname): + """Return 'pathname' with 'new_root' prepended. -def change_root (new_root, pathname): - """Return 'pathname' with 'new_root' prepended. If 'pathname' is - relative, this is equivalent to "os.path.join(new_root,pathname)". + If 'pathname' is relative, this is equivalent to + "os.path.join(new_root,pathname)". Otherwise, it requires making 'pathname' relative and then joining the two, which is tricky on DOS/Windows and Mac OS. """ @@ -214,19 +213,20 @@ def change_root (new_root, pathname): return os.path.join(new_root, pathname) else: # Chop off volume name from start of path - elements = string.split(pathname, ":", 1) + elements = pathname.split(":", 1) pathname = ":" + elements[1] return os.path.join(new_root, pathname) else: - raise DistutilsPlatformError, \ - "nothing known about platform '%s'" % os.name - + raise DistutilsPlatformError("nothing known about " + "platform '%s'" % os.name) _environ_checked = 0 -def check_environ (): - """Ensure that 'os.environ' has all the environment variables we - guarantee that users can use in config files, command-line options, + +def check_environ(): + """Ensure that 'os.environ' has all the environment variables needed. + + We guarantee that users can use in config files, command-line options, etc. Currently this includes: HOME - user's home directory (Unix only) PLAT - description of the current platform, including hardware @@ -245,10 +245,10 @@ def check_environ (): _environ_checked = 1 +def subst_vars(s, local_vars): + """Perform shell/Perl-style variable substitution on 'string'. -def subst_vars (s, local_vars): - """Perform shell/Perl-style variable substitution on 'string'. Every - occurrence of '$' followed by a name is considered a variable, and + Every occurrence of '$' followed by a name is considered a variable, and variable is substituted by the value found in the 'local_vars' dictionary, or in 'os.environ' if it's not in 'local_vars'. 'os.environ' is first checked/augmented to guarantee that it contains @@ -266,14 +266,13 @@ def subst_vars (s, local_vars): try: return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) except KeyError, var: - raise ValueError, "invalid variable '$%s'" % var - -# subst_vars () + raise ValueError("invalid variable '$%s'" % var) +def grok_environment_error(exc, prefix="error: "): + """Generate a useful error message from an EnvironmentError. -def grok_environment_error (exc, prefix="error: "): - """Generate a useful error message from an EnvironmentError (IOError or - OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and + This will generate an IOError or an OSError exception object. + Handles Python 1.5.1 and 1.5.2 styles, and does what it can to deal with exception objects that don't have a filename (which happens when the error is due to a two-file operation, such as 'rename()' or 'link()'. Returns the error message as a string @@ -292,18 +291,20 @@ def grok_environment_error (exc, prefix="error: "): return error - # Needed by 'split_quoted()' _wordchars_re = _squote_re = _dquote_re = None + def _init_regex(): global _wordchars_re, _squote_re, _dquote_re _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') -def split_quoted (s): +def split_quoted(s): """Split a string up according to Unix shell-like rules for quotes and - backslashes. In short: words are delimited by spaces, as long as those + backslashes. + + In short: words are delimited by spaces, as long as those spaces are not escaped by a backslash, or inside a quoted string. Single and double quotes are equivalent, and the quote characters can be backslash-escaped. The backslash is stripped from any two-character @@ -311,13 +312,12 @@ def split_quoted (s): characters are stripped from any quoted string. Returns a list of words. """ - # This is a nice algorithm for splitting up a single string, since it # doesn't require character-by-character examination. It was a little # bit of a brain-bender to get it working right, though... if _wordchars_re is None: _init_regex() - s = string.strip(s) + s = s.strip() words = [] pos = 0 @@ -330,7 +330,7 @@ def split_quoted (s): if s[end] in string.whitespace: # unescaped, unquoted whitespace: now words.append(s[:end]) # we definitely have a word delimiter - s = string.lstrip(s[end:]) + s = s[end:].lstrip() pos = 0 elif s[end] == '\\': # preserve whatever is being escaped; @@ -344,12 +344,11 @@ def split_quoted (s): elif s[end] == '"': # slurp doubly-quoted string m = _dquote_re.match(s, end) else: - raise RuntimeError, \ - "this can't happen (bad char '%c')" % s[end] + raise RuntimeError("this can't happen " + "(bad char '%c')" % s[end]) if m is None: - raise ValueError, \ - "bad string (mismatched %s quotes?)" % s[end] + raise ValueError("bad string (mismatched %s quotes?)" % s[end]) (beg, end) = m.span() s = s[:beg] + s[beg+1:end-1] + s[end:] @@ -361,13 +360,12 @@ def split_quoted (s): return words -# split_quoted () +def execute(func, args, msg=None, verbose=0, dry_run=0): + """Perform some action that affects the outside world. -def execute (func, args, msg=None, verbose=0, dry_run=0): - """Perform some action that affects the outside world (eg. by - writing to the filesystem). Such actions are special because they - are disabled by the 'dry_run' flag. This method takes care of all + eg. by writing to the filesystem). Such actions are special because + they are disabled by the 'dry_run' flag. This method takes care of all that bureaucracy for you; all you have to do is supply the function to call and an argument tuple for it (to embody the "external action" being performed), and an optional message to @@ -380,17 +378,17 @@ def execute (func, args, msg=None, verbose=0, dry_run=0): log.info(msg) if not dry_run: - apply(func, args) + func(*args) -def strtobool (val): +def strtobool(val): """Convert a string representation of truth to true (1) or false (0). True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 'val' is anything else. """ - val = string.lower(val) + val = val.lower() if val in ('y', 'yes', 't', 'true', 'on', '1'): return 1 elif val in ('n', 'no', 'f', 'false', 'off', '0'): @@ -399,15 +397,13 @@ def strtobool (val): raise ValueError, "invalid truth value %r" % (val,) -def byte_compile (py_files, - optimize=0, force=0, - prefix=None, base_dir=None, - verbose=1, dry_run=0, - direct=None): +def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, + verbose=1, dry_run=0, direct=None): """Byte-compile a collection of Python source files to either .pyc - or .pyo files in the same directory. 'py_files' is a list of files - to compile; any files that don't end in ".py" are silently skipped. - 'optimize' must be one of the following: + or .pyo files in the same directory. + + 'py_files' is a list of files to compile; any files that don't end in + ".py" are silently skipped. 'optimize' must be one of the following: 0 - don't optimize (generate .pyc) 1 - normal optimization (like "python -O") 2 - extra optimization (like "python -OO") @@ -432,7 +428,6 @@ def byte_compile (py_files, generated in indirect mode; unless you know what you're doing, leave it set to None. """ - # First, if the caller didn't force us into direct or indirect mode, # figure out which mode we should be in. We take a conservative # approach: choose direct mode *only* if the current interpreter is @@ -481,7 +476,7 @@ files = [ #if prefix: # prefix = os.path.abspath(prefix) - script.write(string.join(map(repr, py_files), ",\n") + "]\n") + script.write(",\n".join(map(repr, py_files)) + "]\n") script.write(""" byte_compile(files, optimize=%r, force=%r, prefix=%r, base_dir=%r, @@ -520,9 +515,8 @@ byte_compile(files, optimize=%r, force=%r, dfile = file if prefix: if file[:len(prefix)] != prefix: - raise ValueError, \ - ("invalid prefix: filename %r doesn't start with %r" - % (file, prefix)) + raise ValueError("invalid prefix: filename %r doesn't " + "start with %r" % (file, prefix)) dfile = dfile[len(prefix):] if base_dir: dfile = os.path.join(base_dir, dfile) @@ -537,13 +531,11 @@ byte_compile(files, optimize=%r, force=%r, log.debug("skipping byte-compilation of %s to %s", file, cfile_base) -# byte_compile () -def rfc822_escape (header): +def rfc822_escape(header): """Return a version of the string escaped for inclusion in an RFC-822 header, by ensuring there are 8 spaces space after each newline. """ - lines = string.split(header, '\n') - lines = map(string.strip, lines) - header = string.join(lines, '\n' + 8*' ') - return header + lines = [x.strip() for x in header.split('\n')] + sep = '\n' + 8*' ' + return sep.join(lines) -- cgit v1.2.1 From 21d1447442999782252d6c7942348cea9ca4240e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 2 Jul 2009 14:25:23 +0000 Subject: Merged revisions 73762 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73762 | tarek.ziade | 2009-07-02 16:20:47 +0200 (Thu, 02 Jul 2009) | 1 line pep8-fied and cleaned up distutils.util ........ --- util.py | 89 +++++++++++++++++++++++++++++++---------------------------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/util.py b/util.py index 4c1956a2..ad7ae08d 100644 --- a/util.py +++ b/util.py @@ -12,9 +12,10 @@ from distutils.dep_util import newer from distutils.spawn import spawn from distutils import log -def get_platform (): - """Return a string that identifies the current platform. This is used - mainly to distinguish platform-specific build directories and +def get_platform(): + """Return a string that identifies the current platform. + + This is used mainly to distinguish platform-specific build directories and platform-specific built distributions. Typically includes the OS name and version and the architecture (as supplied by 'os.uname()'), although the exact information included depends on the OS; eg. for IRIX @@ -153,11 +154,10 @@ def get_platform (): return "%s-%s-%s" % (osname, release, machine) -# get_platform () +def convert_path(pathname): + """Return 'pathname' as a name that will work on the native filesystem. -def convert_path (pathname): - """Return 'pathname' as a name that will work on the native filesystem, i.e. split it on '/' and put it back together again using the current directory separator. Needed because filenames in the setup script are always supplied in Unix style, and have to be converted to the local @@ -181,12 +181,12 @@ def convert_path (pathname): return os.curdir return os.path.join(*paths) -# convert_path () +def change_root(new_root, pathname): + """Return 'pathname' with 'new_root' prepended. -def change_root (new_root, pathname): - """Return 'pathname' with 'new_root' prepended. If 'pathname' is - relative, this is equivalent to "os.path.join(new_root,pathname)". + If 'pathname' is relative, this is equivalent to + "os.path.join(new_root,pathname)". Otherwise, it requires making 'pathname' relative and then joining the two, which is tricky on DOS/Windows and Mac OS. """ @@ -218,13 +218,15 @@ def change_root (new_root, pathname): return os.path.join(new_root, pathname) else: - raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) - + raise DistutilsPlatformError("nothing known about " + "platform '%s'" % os.name) _environ_checked = 0 -def check_environ (): - """Ensure that 'os.environ' has all the environment variables we - guarantee that users can use in config files, command-line options, + +def check_environ(): + """Ensure that 'os.environ' has all the environment variables needed. + + We guarantee that users can use in config files, command-line options, etc. Currently this includes: HOME - user's home directory (Unix only) PLAT - description of the current platform, including hardware @@ -243,10 +245,10 @@ def check_environ (): _environ_checked = 1 +def subst_vars(s, local_vars): + """Perform shell/Perl-style variable substitution on 'string'. -def subst_vars (s, local_vars): - """Perform shell/Perl-style variable substitution on 'string'. Every - occurrence of '$' followed by a name is considered a variable, and + Every occurrence of '$' followed by a name is considered a variable, and variable is substituted by the value found in the 'local_vars' dictionary, or in 'os.environ' if it's not in 'local_vars'. 'os.environ' is first checked/augmented to guarantee that it contains @@ -266,12 +268,11 @@ def subst_vars (s, local_vars): except KeyError as var: raise ValueError("invalid variable '$%s'" % var) -# subst_vars () - +def grok_environment_error(exc, prefix="error: "): + """Generate a useful error message from an EnvironmentError. -def grok_environment_error (exc, prefix="error: "): - """Generate a useful error message from an EnvironmentError (IOError or - OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and + This will generate an IOError or an OSError exception object. + Handles Python 1.5.1 and 1.5.2 styles, and does what it can to deal with exception objects that don't have a filename (which happens when the error is due to a two-file operation, such as 'rename()' or 'link()'. Returns the error message as a string @@ -290,18 +291,20 @@ def grok_environment_error (exc, prefix="error: "): return error - # Needed by 'split_quoted()' _wordchars_re = _squote_re = _dquote_re = None + def _init_regex(): global _wordchars_re, _squote_re, _dquote_re _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') -def split_quoted (s): +def split_quoted(s): """Split a string up according to Unix shell-like rules for quotes and - backslashes. In short: words are delimited by spaces, as long as those + backslashes. + + In short: words are delimited by spaces, as long as those spaces are not escaped by a backslash, or inside a quoted string. Single and double quotes are equivalent, and the quote characters can be backslash-escaped. The backslash is stripped from any two-character @@ -309,7 +312,6 @@ def split_quoted (s): characters are stripped from any quoted string. Returns a list of words. """ - # This is a nice algorithm for splitting up a single string, since it # doesn't require character-by-character examination. It was a little # bit of a brain-bender to get it working right, though... @@ -357,13 +359,12 @@ def split_quoted (s): return words -# split_quoted () +def execute(func, args, msg=None, verbose=0, dry_run=0): + """Perform some action that affects the outside world. -def execute (func, args, msg=None, verbose=0, dry_run=0): - """Perform some action that affects the outside world (eg. by - writing to the filesystem). Such actions are special because they - are disabled by the 'dry_run' flag. This method takes care of all + eg. by writing to the filesystem). Such actions are special because + they are disabled by the 'dry_run' flag. This method takes care of all that bureaucracy for you; all you have to do is supply the function to call and an argument tuple for it (to embody the "external action" being performed), and an optional message to @@ -379,7 +380,7 @@ def execute (func, args, msg=None, verbose=0, dry_run=0): func(*args) -def strtobool (val): +def strtobool(val): """Convert a string representation of truth to true (1) or false (0). True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values @@ -395,15 +396,13 @@ def strtobool (val): raise ValueError("invalid truth value %r" % (val,)) -def byte_compile (py_files, - optimize=0, force=0, - prefix=None, base_dir=None, - verbose=1, dry_run=0, - direct=None): +def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, + verbose=1, dry_run=0, direct=None): """Byte-compile a collection of Python source files to either .pyc - or .pyo files in the same directory. 'py_files' is a list of files - to compile; any files that don't end in ".py" are silently skipped. - 'optimize' must be one of the following: + or .pyo files in the same directory. + + 'py_files' is a list of files to compile; any files that don't end in + ".py" are silently skipped. 'optimize' must be one of the following: 0 - don't optimize (generate .pyc) 1 - normal optimization (like "python -O") 2 - extra optimization (like "python -OO") @@ -428,7 +427,6 @@ def byte_compile (py_files, generated in indirect mode; unless you know what you're doing, leave it set to None. """ - # First, if the caller didn't force us into direct or indirect mode, # figure out which mode we should be in. We take a conservative # approach: choose direct mode *only* if the current interpreter is @@ -516,8 +514,8 @@ byte_compile(files, optimize=%r, force=%r, dfile = file if prefix: if file[:len(prefix)] != prefix: - raise ValueError("invalid prefix: filename %r doesn't start with %r" - % (file, prefix)) + raise ValueError("invalid prefix: filename %r doesn't " + "start with %r" % (file, prefix)) dfile = dfile[len(prefix):] if base_dir: dfile = os.path.join(base_dir, dfile) @@ -532,9 +530,8 @@ byte_compile(files, optimize=%r, force=%r, log.debug("skipping byte-compilation of %s to %s", file, cfile_base) -# byte_compile () -def rfc822_escape (header): +def rfc822_escape(header): """Return a version of the string escaped for inclusion in an RFC-822 header, by ensuring there are 8 spaces space after each newline. """ -- cgit v1.2.1 From 414e03b9816532270f3f29a4f1777c9e8e67804c Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Thu, 2 Jul 2009 15:37:21 +0000 Subject: Merged revisions 69846 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69846 | mark.dickinson | 2009-02-21 21:27:01 +0100 (Sat, 21 Feb 2009) | 2 lines Issue #5341: Fix a variety of spelling errors. ........ --- tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 55d2d5df..e481e8de 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -57,7 +57,7 @@ class CoreTestCase(unittest.TestCase): def test_run_setup_uses_current_dir(self): # This tests that the setup script is run with the current directory - # as it's own current directory; this was temporarily broken by a + # as its own current directory; this was temporarily broken by a # previous patch when TESTFN did not use the current directory. sys.stdout = StringIO.StringIO() cwd = os.getcwd() -- cgit v1.2.1 From 0884202338406bd0e3978c9ec4de6e9cd872ad2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 3 Jul 2009 08:22:56 +0000 Subject: Fixed #6403 : package path usage for build_ext --- command/build_ext.py | 14 ++++++++++---- tests/test_build_ext.py | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index c2c1bf13..96bd68be 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -644,17 +644,23 @@ class build_ext (Command): """ fullname = self.get_ext_fullname(ext_name) modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] - filename = self.get_ext_filename(base) + filename = self.get_ext_filename(modpath[-1]) + if not self.inplace: # no further work needed + # returning : + # build_dir/package/path/filename + filename = os.path.join(*modpath[:-1]+[filename]) return os.path.join(self.build_lib, filename) # the inplace option requires to find the package directory - # using the build_py command + # using the build_py command for that + package = '.'.join(modpath[0:-1]) build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) + + # returning + # package_dir/filename return os.path.join(package_dir, filename) def get_ext_fullname(self, ext_name): diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 467e90d9..2ecead3c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -337,7 +337,8 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(so_dir, cmd.build_lib) # inplace = 0, cmd.package = 'bar' - cmd.package = 'bar' + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {'': 'bar'} path = cmd.get_ext_fullpath('foo') # checking that the last directory is the build_dir path = os.path.split(path)[0] @@ -355,12 +356,14 @@ class BuildExtTestCase(support.TempdirManager, # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, cmd.package) + self.assertEquals(lastdir, 'bar') - def test_build_ext_inplace(self): - etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') - etree_ext = Extension('lxml.etree', [etree_c]) - dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + def test_ext_fullpath(self): + # building lxml.etree inplace + #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + #etree_ext = Extension('lxml.etree', [etree_c]) + #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + dist = Distribution() cmd = build_ext(dist) cmd.inplace = 1 cmd.distribution.package_dir = {'': 'src'} @@ -370,6 +373,28 @@ class BuildExtTestCase(support.TempdirManager, path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) + # building lxml.etree not inplace + cmd.inplace = 0 + cmd.build_lib = os.path.join(curdir, 'tmpdir') + wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree.so') + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + + # building twisted.runner.portmap not inplace + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {} + cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', + 'portmap.so') + self.assertEquals(wanted, path) + + # building twisted.runner.portmap inplace + cmd.inplace = 1 + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap.so') + self.assertEquals(wanted, path) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 8fde737bb947b2fe9b75f686d606b5cee236b317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 3 Jul 2009 08:31:31 +0000 Subject: Merged revisions 73790 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73790 | tarek.ziade | 2009-07-03 10:22:56 +0200 (Fri, 03 Jul 2009) | 1 line Fixed #6403 : package path usage for build_ext ........ --- command/build_ext.py | 13 +++++++++++-- tests/test_build_ext.py | 43 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index e6e33ab9..8cb1efe6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -629,18 +629,27 @@ class build_ext (Command): (inplace option). """ fullname = self.get_ext_fullname(ext_name) - filename = self.get_ext_filename(fullname) + modpath = fullname.split('.') + filename = self.get_ext_filename(modpath[-1]) + if not self.inplace: # no further work needed + # returning : + # build_dir/package/path/filename + filename = os.path.join(*modpath[:-1]+[filename]) return os.path.join(self.build_lib, filename) # the inplace option requires to find the package directory - # using the build_py command + # using the build_py command for that + package = '.'.join(modpath[0:-1]) modpath = fullname.split('.') package = '.'.join(modpath[0:-1]) base = modpath[-1] build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) + + # returning + # package_dir/filename return os.path.join(package_dir, filename) def get_ext_fullname(self, ext_name): diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index dc8df98f..e01c8820 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -274,12 +274,12 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(so_dir, cmd.build_lib) # inplace = 0, cmd.package = 'bar' - cmd.package = 'bar' + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {'': 'bar'} path = cmd.get_ext_fullpath('foo') - # checking that the last directory is bar + # checking that the last directory is the build_dir path = os.path.split(path)[0] - lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, cmd.package) + self.assertEquals(path, cmd.build_lib) # inplace = 1, cmd.package = 'bar' cmd.inplace = 1 @@ -293,7 +293,40 @@ class BuildExtTestCase(support.TempdirManager, # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, cmd.package) + self.assertEquals(lastdir, 'bar') + + def test_ext_fullpath(self): + dist = Distribution() + cmd = build_ext(dist) + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + wanted = os.path.join(curdir, 'src', 'lxml', 'etree.so') + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + + # building lxml.etree not inplace + cmd.inplace = 0 + cmd.build_lib = os.path.join(curdir, 'tmpdir') + wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree.so') + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + + # building twisted.runner.portmap not inplace + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {} + cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', + 'portmap.so') + self.assertEquals(wanted, path) + + # building twisted.runner.portmap inplace + cmd.inplace = 1 + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap.so') + self.assertEquals(wanted, path) def test_suite(): if not sysconfig.python_build: -- cgit v1.2.1 From 354b6e7100a54b04521cd57453c702386e4eae30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 3 Jul 2009 08:33:28 +0000 Subject: Merged revisions 73790 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73790 | tarek.ziade | 2009-07-03 10:22:56 +0200 (Fri, 03 Jul 2009) | 1 line Fixed #6403 : package path usage for build_ext ........ --- command/build_ext.py | 14 ++++++++++---- tests/test_build_ext.py | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 57a110b0..30457026 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -631,17 +631,23 @@ class build_ext(Command): """ fullname = self.get_ext_fullname(ext_name) modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] - filename = self.get_ext_filename(base) + filename = self.get_ext_filename(modpath[-1]) + if not self.inplace: # no further work needed + # returning : + # build_dir/package/path/filename + filename = os.path.join(*modpath[:-1]+[filename]) return os.path.join(self.build_lib, filename) # the inplace option requires to find the package directory - # using the build_py command + # using the build_py command for that + package = '.'.join(modpath[0:-1]) build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) + + # returning + # package_dir/filename return os.path.join(package_dir, filename) def get_ext_fullname(self, ext_name): diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 08e3e1ff..6f0e2309 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -337,7 +337,8 @@ class BuildExtTestCase(TempdirManager, self.assertEquals(so_dir, cmd.build_lib) # inplace = 0, cmd.package = 'bar' - cmd.package = 'bar' + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {'': 'bar'} path = cmd.get_ext_fullpath('foo') # checking that the last directory is the build_dir path = os.path.split(path)[0] @@ -355,12 +356,14 @@ class BuildExtTestCase(TempdirManager, # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, cmd.package) + self.assertEquals(lastdir, 'bar') - def test_build_ext_inplace(self): - etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') - etree_ext = Extension('lxml.etree', [etree_c]) - dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + def test_ext_fullpath(self): + # building lxml.etree inplace + #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + #etree_ext = Extension('lxml.etree', [etree_c]) + #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + dist = Distribution() cmd = build_ext(dist) cmd.inplace = 1 cmd.distribution.package_dir = {'': 'src'} @@ -370,6 +373,28 @@ class BuildExtTestCase(TempdirManager, path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) + # building lxml.etree not inplace + cmd.inplace = 0 + cmd.build_lib = os.path.join(curdir, 'tmpdir') + wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree.so') + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + + # building twisted.runner.portmap not inplace + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {} + cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', + 'portmap.so') + self.assertEquals(wanted, path) + + # building twisted.runner.portmap inplace + cmd.inplace = 1 + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap.so') + self.assertEquals(wanted, path) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From f781d05ce0ced0e9e34201c464c5a1de6eee334e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 3 Jul 2009 08:37:27 +0000 Subject: Merged revisions 73792 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r73792 | tarek.ziade | 2009-07-03 10:33:28 +0200 (Fri, 03 Jul 2009) | 9 lines Merged revisions 73790 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73790 | tarek.ziade | 2009-07-03 10:22:56 +0200 (Fri, 03 Jul 2009) | 1 line Fixed #6403 : package path usage for build_ext ........ ................ --- command/build_ext.py | 14 ++++++++++---- tests/test_build_ext.py | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 57a110b0..30457026 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -631,17 +631,23 @@ class build_ext(Command): """ fullname = self.get_ext_fullname(ext_name) modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] - filename = self.get_ext_filename(base) + filename = self.get_ext_filename(modpath[-1]) + if not self.inplace: # no further work needed + # returning : + # build_dir/package/path/filename + filename = os.path.join(*modpath[:-1]+[filename]) return os.path.join(self.build_lib, filename) # the inplace option requires to find the package directory - # using the build_py command + # using the build_py command for that + package = '.'.join(modpath[0:-1]) build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) + + # returning + # package_dir/filename return os.path.join(package_dir, filename) def get_ext_fullname(self, ext_name): diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index fe6009b6..f0580c1f 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -337,7 +337,8 @@ class BuildExtTestCase(TempdirManager, self.assertEquals(so_dir, cmd.build_lib) # inplace = 0, cmd.package = 'bar' - cmd.package = 'bar' + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {'': 'bar'} path = cmd.get_ext_fullpath('foo') # checking that the last directory is the build_dir path = os.path.split(path)[0] @@ -355,12 +356,14 @@ class BuildExtTestCase(TempdirManager, # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, cmd.package) + self.assertEquals(lastdir, 'bar') - def test_build_ext_inplace(self): - etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') - etree_ext = Extension('lxml.etree', [etree_c]) - dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + def test_ext_fullpath(self): + # building lxml.etree inplace + #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + #etree_ext = Extension('lxml.etree', [etree_c]) + #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + dist = Distribution() cmd = build_ext(dist) cmd.inplace = 1 cmd.distribution.package_dir = {'': 'src'} @@ -370,6 +373,28 @@ class BuildExtTestCase(TempdirManager, path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) + # building lxml.etree not inplace + cmd.inplace = 0 + cmd.build_lib = os.path.join(curdir, 'tmpdir') + wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree.so') + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + + # building twisted.runner.portmap not inplace + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {} + cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', + 'portmap.so') + self.assertEquals(wanted, path) + + # building twisted.runner.portmap inplace + cmd.inplace = 1 + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap.so') + self.assertEquals(wanted, path) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From a09e1355e93bba0d5872beb36c2afe34760e17d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 3 Jul 2009 09:01:07 +0000 Subject: cleaned up distutils.command.build_py --- command/build_py.py | 134 +++++++++++++++++----------------------------------- 1 file changed, 44 insertions(+), 90 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index d9c15749..5d249f3d 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -4,16 +4,15 @@ Implements the Distutils 'build_py' command.""" __revision__ = "$Id$" -import string, os -from types import * +import os from glob import glob from distutils.core import Command -from distutils.errors import * +from distutils.errors import DistutilsOptionError, DistutilsFileError from distutils.util import convert_path from distutils import log -class build_py (Command): +class build_py(Command): description = "\"build\" pure Python modules (copy to build directory)" @@ -30,8 +29,7 @@ class build_py (Command): boolean_options = ['compile', 'force'] negative_opt = {'no-compile' : 'compile'} - - def initialize_options (self): + def initialize_options(self): self.build_lib = None self.py_modules = None self.package = None @@ -41,7 +39,7 @@ class build_py (Command): self.optimize = 0 self.force = None - def finalize_options (self): + def finalize_options(self): self.set_undefined_options('build', ('build_lib', 'build_lib'), ('force', 'force')) @@ -59,15 +57,14 @@ class build_py (Command): # Ick, copied straight from install_lib.py (fancy_getopt needs a # type system! Hell, *everything* needs a type system!!!) - if type(self.optimize) is not IntType: + if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) assert 0 <= self.optimize <= 2 except (ValueError, AssertionError): - raise DistutilsOptionError, "optimize must be 0, 1, or 2" - - def run (self): + raise DistutilsOptionError("optimize must be 0, 1, or 2") + def run(self): # XXX copy_file by default preserves atime and mtime. IMHO this is # the right thing to do, but perhaps it should be an option -- in # particular, a site administrator might want installed files to @@ -97,9 +94,7 @@ class build_py (Command): self.byte_compile(self.get_outputs(include_bytecode=0)) - # run () - - def get_data_files (self): + def get_data_files(self): """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" data = [] if not self.packages: @@ -123,7 +118,7 @@ class build_py (Command): data.append((package, src_dir, build_dir, filenames)) return data - def find_data_files (self, package, src_dir): + def find_data_files(self, package, src_dir): """Return filenames for package's data files in 'src_dir'""" globs = (self.package_data.get('', []) + self.package_data.get(package, [])) @@ -135,7 +130,7 @@ class build_py (Command): files.extend([fn for fn in filelist if fn not in files]) return files - def build_package_data (self): + def build_package_data(self): """Copy data files into build directory""" lastdir = None for package, src_dir, build_dir, filenames in self.data_files: @@ -145,23 +140,23 @@ class build_py (Command): self.copy_file(os.path.join(src_dir, filename), target, preserve_mode=False) - def get_package_dir (self, package): + def get_package_dir(self, package): """Return the directory, relative to the top of the source distribution, where package 'package' should be found (at least according to the 'package_dir' option, if any).""" - path = string.split(package, '.') + path = package.split('.') if not self.package_dir: if path: - return apply(os.path.join, path) + return os.path.join(*path) else: return '' else: tail = [] while path: try: - pdir = self.package_dir[string.join(path, '.')] + pdir = self.package_dir['.'.join(path)] except KeyError: tail.insert(0, path[-1]) del path[-1] @@ -181,27 +176,23 @@ class build_py (Command): tail.insert(0, pdir) if tail: - return apply(os.path.join, tail) + return os.path.join(*tail) else: return '' - # get_package_dir () - - - def check_package (self, package, package_dir): - + def check_package(self, package, package_dir): # Empty dir name means current directory, which we can probably # assume exists. Also, os.path.exists and isdir don't know about # my "empty string means current dir" convention, so we have to # circumvent them. if package_dir != "": if not os.path.exists(package_dir): - raise DistutilsFileError, \ - "package directory '%s' does not exist" % package_dir + raise DistutilsFileError( + "package directory '%s' does not exist" % package_dir) if not os.path.isdir(package_dir): - raise DistutilsFileError, \ - ("supposed package directory '%s' exists, " + - "but is not a directory") % package_dir + raise DistutilsFileError( + "supposed package directory '%s' exists, " + "but is not a directory" % package_dir) # Require __init__.py for all but the "root package" if package: @@ -216,20 +207,14 @@ class build_py (Command): # __init__.py doesn't exist -- so don't return the filename. return None - # check_package () - - - def check_module (self, module, module_file): + def check_module(self, module, module_file): if not os.path.isfile(module_file): log.warn("file %s (for module %s) not found", module_file, module) - return 0 + return False else: - return 1 + return True - # check_module () - - - def find_package_modules (self, package, package_dir): + def find_package_modules(self, package, package_dir): self.check_package(package, package_dir) module_files = glob(os.path.join(package_dir, "*.py")) modules = [] @@ -244,8 +229,7 @@ class build_py (Command): self.debug_print("excluding %s" % setup_script) return modules - - def find_modules (self): + def find_modules(self): """Finds individually-specified Python modules, ie. those listed by module name in 'self.py_modules'. Returns a list of tuples (package, module_base, filename): 'package' is a tuple of the path through @@ -254,7 +238,6 @@ class build_py (Command): ".py" file (relative to the distribution root) that implements the module. """ - # Map package names to tuples of useful info about the package: # (package_dir, checked) # package_dir - the directory where we'll find source files for @@ -270,10 +253,9 @@ class build_py (Command): # just the "package" for a toplevel is empty (either an empty # string or empty list, depending on context). Differences: # - don't check for __init__.py in directory for empty package - for module in self.py_modules: - path = string.split(module, '.') - package = string.join(path[0:-1], '.') + path = module.split('.') + package = '.'.join(path[0:-1]) module_base = path[-1] try: @@ -299,16 +281,12 @@ class build_py (Command): return modules - # find_modules () - - - def find_all_modules (self): + def find_all_modules(self): """Compute the list of all modules that will be built, whether they are specified one-module-at-a-time ('self.py_modules') or by whole packages ('self.packages'). Return a list of tuples (package, module, module_file), just like 'find_modules()' and 'find_package_modules()' do.""" - modules = [] if self.py_modules: modules.extend(self.find_modules()) @@ -317,32 +295,20 @@ class build_py (Command): package_dir = self.get_package_dir(package) m = self.find_package_modules(package, package_dir) modules.extend(m) - return modules - # find_all_modules () - - - def get_source_files (self): - - modules = self.find_all_modules() - filenames = [] - for module in modules: - filenames.append(module[-1]) + def get_source_files(self): + return [module[-1] for module in self.find_all_modules()] - return filenames - - - def get_module_outfile (self, build_dir, package, module): + def get_module_outfile(self, build_dir, package, module): outfile_path = [build_dir] + list(package) + [module + ".py"] return os.path.join(*outfile_path) - - def get_outputs (self, include_bytecode=1): + def get_outputs(self, include_bytecode=1): modules = self.find_all_modules() outputs = [] for (package, module, module_file) in modules: - package = string.split(package, '.') + package = package.split('.') filename = self.get_module_outfile(self.build_lib, package, module) outputs.append(filename) if include_bytecode: @@ -359,13 +325,12 @@ class build_py (Command): return outputs - - def build_module (self, module, module_file, package): - if type(package) is StringType: - package = string.split(package, '.') - elif type(package) not in (ListType, TupleType): - raise TypeError, \ - "'package' must be a string (dot-separated), list, or tuple" + def build_module(self, module, module_file, package): + if isinstance(package, str): + package = package.split('.') + elif not isinstance(package, (list, tuple)): + raise TypeError( + "'package' must be a string (dot-separated), list, or tuple") # Now put the module source file into the "build" area -- this is # easy, we just copy it somewhere under self.build_lib (the build @@ -375,9 +340,7 @@ class build_py (Command): self.mkpath(dir) return self.copy_file(module_file, outfile, preserve_mode=0) - - def build_modules (self): - + def build_modules(self): modules = self.find_modules() for (package, module, module_file) in modules: @@ -387,11 +350,7 @@ class build_py (Command): # under self.build_lib.) self.build_module(module, module_file, package) - # build_modules () - - - def build_packages (self): - + def build_packages(self): for package in self.packages: # Get list of (package, module, module_file) tuples based on @@ -412,10 +371,7 @@ class build_py (Command): assert package == package_ self.build_module(module, module_file, package) - # build_packages () - - - def byte_compile (self, files): + def byte_compile(self, files): from distutils.util import byte_compile prefix = self.build_lib if prefix[-1] != os.sep: @@ -431,5 +387,3 @@ class build_py (Command): if self.optimize > 0: byte_compile(files, optimize=self.optimize, force=self.force, prefix=prefix, dry_run=self.dry_run) - -# class build_py -- cgit v1.2.1 From 9357a7c74f276c20dd408a3394241c226d841b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 3 Jul 2009 09:05:30 +0000 Subject: Merged revisions 73801 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73801 | tarek.ziade | 2009-07-03 11:01:07 +0200 (Fri, 03 Jul 2009) | 1 line cleaned up distutils.command.build_py ........ --- command/build_py.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 99d85be9..4cc80f7b 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -4,15 +4,15 @@ Implements the Distutils 'build_py' command.""" __revision__ = "$Id$" -import sys, os +import os from glob import glob from distutils.core import Command -from distutils.errors import * +from distutils.errors import DistutilsOptionError, DistutilsFileError from distutils.util import convert_path, Mixin2to3 from distutils import log -class build_py (Command): +class build_py(Command): description = "\"build\" pure Python modules (copy to build directory)" -- cgit v1.2.1 From 7c0126426802cc8186b46e808179d7c7a870514c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 3 Jul 2009 19:01:12 +0000 Subject: basic tests to raise distutils.file_util coverage --- tests/test_file_util.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_file_util.py b/tests/test_file_util.py index fac4a5d1..99b421f7 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -3,7 +3,7 @@ import unittest import os import shutil -from distutils.file_util import move_file +from distutils.file_util import move_file, write_file, copy_file from distutils import log from distutils.tests import support @@ -55,6 +55,21 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ['moving %s -> %s' % (self.source, self.target_dir)] self.assertEquals(self._logs, wanted) + def test_write_file(self): + lines = ['a', 'b', 'c'] + dir = self.mkdtemp() + foo = os.path.join(dir, 'foo') + write_file(foo, lines) + content = [line.strip() for line in open(foo).readlines()] + self.assertEquals(content, lines) + + def test_copy_file(self): + src_dir = self.mkdtemp() + foo = os.path.join(src_dir, 'foo') + write_file(foo, 'content') + dst_dir = self.mkdtemp() + copy_file(foo, dst_dir) + self.assertTrue(os.path.exists(os.path.join(dst_dir, 'foo'))) def test_suite(): return unittest.makeSuite(FileUtilTestCase) -- cgit v1.2.1 From 570969b419d41a25ce8ed52e36fa756a01c751ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 3 Jul 2009 19:14:49 +0000 Subject: cleaned distutils.file_util --- file_util.py | 121 +++++++++++++++++++++++++---------------------------------- 1 file changed, 52 insertions(+), 69 deletions(-) diff --git a/file_util.py b/file_util.py index 0c890559..d8e8fd5f 100644 --- a/file_util.py +++ b/file_util.py @@ -10,49 +10,48 @@ from distutils.errors import DistutilsFileError from distutils import log # for generating verbose output in 'copy_file()' -_copy_action = { None: 'copying', - 'hard': 'hard linking', - 'sym': 'symbolically linking' } +_copy_action = {None: 'copying', + 'hard': 'hard linking', + 'sym': 'symbolically linking'} -def _copy_file_contents (src, dst, buffer_size=16*1024): - """Copy the file 'src' to 'dst'; both must be filenames. Any error - opening either file, reading from 'src', or writing to 'dst', raises - DistutilsFileError. Data is read/written in chunks of 'buffer_size' - bytes (default 16k). No attempt is made to handle anything apart from - regular files. +def _copy_file_contents(src, dst, buffer_size=16*1024): + """Copy the file 'src' to 'dst'. + + Both must be filenames. Any error opening either file, reading from + 'src', or writing to 'dst', raises DistutilsFileError. Data is + read/written in chunks of 'buffer_size' bytes (default 16k). No attempt + is made to handle anything apart from regular files. """ # Stolen from shutil module in the standard library, but with # custom error-handling added. - fsrc = None fdst = None try: try: fsrc = open(src, 'rb') except os.error, (errno, errstr): - raise DistutilsFileError, \ - "could not open '%s': %s" % (src, errstr) + raise DistutilsFileError("could not open '%s': %s" % (src, errstr)) if os.path.exists(dst): try: os.unlink(dst) except os.error, (errno, errstr): - raise DistutilsFileError, \ - "could not delete '%s': %s" % (dst, errstr) + raise DistutilsFileError( + "could not delete '%s': %s" % (dst, errstr)) try: fdst = open(dst, 'wb') except os.error, (errno, errstr): - raise DistutilsFileError, \ - "could not create '%s': %s" % (dst, errstr) + raise DistutilsFileError( + "could not create '%s': %s" % (dst, errstr)) while 1: try: buf = fsrc.read(buffer_size) except os.error, (errno, errstr): - raise DistutilsFileError, \ - "could not read from '%s': %s" % (src, errstr) + raise DistutilsFileError( + "could not read from '%s': %s" % (src, errstr)) if not buf: break @@ -60,8 +59,8 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): try: fdst.write(buf) except os.error, (errno, errstr): - raise DistutilsFileError, \ - "could not write to '%s': %s" % (dst, errstr) + raise DistutilsFileError( + "could not write to '%s': %s" % (dst, errstr)) finally: if fdst: @@ -69,25 +68,18 @@ def _copy_file_contents (src, dst, buffer_size=16*1024): if fsrc: fsrc.close() -# _copy_file_contents() - -def copy_file (src, dst, - preserve_mode=1, - preserve_times=1, - update=0, - link=None, - verbose=1, - dry_run=0): - - """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is - copied there with the same name; otherwise, it must be a filename. (If - the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' - is true (the default), the file's mode (type and permission bits, or - whatever is analogous on the current platform) is copied. If - 'preserve_times' is true (the default), the last-modified and - last-access times are copied as well. If 'update' is true, 'src' will - only be copied if 'dst' does not exist, or if 'dst' does exist but is - older than 'src'. +def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, + link=None, verbose=1, dry_run=0): + """Copy a file 'src' to 'dst'. + + If 'dst' is a directory, then 'src' is copied there with the same name; + otherwise, it must be a filename. (If the file exists, it will be + ruthlessly clobbered.) If 'preserve_mode' is true (the default), + the file's mode (type and permission bits, or whatever is analogous on + the current platform) is copied. If 'preserve_times' is true (the + default), the last-modified and last-access times are copied as well. + If 'update' is true, 'src' will only be copied if 'dst' does not exist, + or if 'dst' does exist but is older than 'src'. 'link' allows you to make hard links (os.link) or symbolic links (os.symlink) instead of copying: set it to "hard" or "sym"; if it is @@ -113,8 +105,8 @@ def copy_file (src, dst, from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE if not os.path.isfile(src): - raise DistutilsFileError, \ - "can't copy '%s': doesn't exist or not a regular file" % src + raise DistutilsFileError( + "can't copy '%s': doesn't exist or not a regular file" % src) if os.path.isdir(dst): dir = dst @@ -130,8 +122,7 @@ def copy_file (src, dst, try: action = _copy_action[link] except KeyError: - raise ValueError, \ - "invalid value '%s' for 'link' argument" % link + raise ValueError("invalid value '%s' for 'link' argument" % link) if verbose >= 1: if os.path.basename(dst) == os.path.basename(src): @@ -148,8 +139,8 @@ def copy_file (src, dst, try: macostools.copy(src, dst, 0, preserve_times) except os.error, exc: - raise DistutilsFileError, \ - "could not copy '%s' to '%s': %s" % (src, dst, exc[-1]) + raise DistutilsFileError( + "could not copy '%s' to '%s': %s" % (src, dst, exc[-1])) # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) @@ -176,17 +167,13 @@ def copy_file (src, dst, return (dst, 1) -# copy_file () - - # XXX I suspect this is Unix-specific -- need porting help! -def move_file (src, dst, - verbose=1, - dry_run=0): +def move_file (src, dst, verbose=1, dry_run=0): + """Move a file 'src' to 'dst'. - """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will - be moved into it with the same name; otherwise, 'src' is just renamed - to 'dst'. Return the new full name of the file. + If 'dst' is a directory, the file will be moved into it with the same + name; otherwise, 'src' is just renamed to 'dst'. Return the new + full name of the file. Handles cross-device moves on Unix using 'copy_file()'. What about other systems??? @@ -201,20 +188,19 @@ def move_file (src, dst, return dst if not isfile(src): - raise DistutilsFileError, \ - "can't move '%s': not a regular file" % src + raise DistutilsFileError("can't move '%s': not a regular file" % src) if isdir(dst): dst = os.path.join(dst, basename(src)) elif exists(dst): - raise DistutilsFileError, \ - "can't move '%s': destination '%s' already exists" % \ - (src, dst) + raise DistutilsFileError( + "can't move '%s': destination '%s' already exists" % + (src, dst)) if not isdir(dirname(dst)): - raise DistutilsFileError, \ + raise DistutilsFileError( "can't move '%s': destination '%s' not a valid path" % \ - (src, dst) + (src, dst)) copy_it = 0 try: @@ -223,8 +209,8 @@ def move_file (src, dst, if num == errno.EXDEV: copy_it = 1 else: - raise DistutilsFileError, \ - "couldn't move '%s' to '%s': %s" % (src, dst, msg) + raise DistutilsFileError( + "couldn't move '%s' to '%s': %s" % (src, dst, msg)) if copy_it: copy_file(src, dst, verbose=verbose) @@ -235,15 +221,12 @@ def move_file (src, dst, os.unlink(dst) except os.error: pass - raise DistutilsFileError, \ + raise DistutilsFileError( ("couldn't move '%s' to '%s' by copy/delete: " + - "delete '%s' failed: %s") % \ - (src, dst, src, msg) - + "delete '%s' failed: %s") % + (src, dst, src, msg)) return dst -# move_file () - def write_file (filename, contents): """Create a file with the specified name and write 'contents' (a -- cgit v1.2.1 From 3f8ca0456b7e12be4eabd74bde600ec3b1fac405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 3 Jul 2009 19:22:23 +0000 Subject: Merged revisions 73814-73815 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73814 | tarek.ziade | 2009-07-03 21:01:12 +0200 (Fri, 03 Jul 2009) | 1 line basic tests to raise distutils.file_util coverage ........ r73815 | tarek.ziade | 2009-07-03 21:14:49 +0200 (Fri, 03 Jul 2009) | 1 line cleaned distutils.file_util ........ --- file_util.py | 49 +++++++++++++++++++++++++------------------------ tests/test_file_util.py | 17 ++++++++++++++++- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/file_util.py b/file_util.py index 65aa7e0f..758bde38 100644 --- a/file_util.py +++ b/file_util.py @@ -10,17 +10,18 @@ from distutils.errors import DistutilsFileError from distutils import log # for generating verbose output in 'copy_file()' -_copy_action = { None: 'copying', - 'hard': 'hard linking', - 'sym': 'symbolically linking' } +_copy_action = {None: 'copying', + 'hard': 'hard linking', + 'sym': 'symbolically linking'} def _copy_file_contents(src, dst, buffer_size=16*1024): - """Copy the file 'src' to 'dst'; both must be filenames. Any error - opening either file, reading from 'src', or writing to 'dst', raises - DistutilsFileError. Data is read/written in chunks of 'buffer_size' - bytes (default 16k). No attempt is made to handle anything apart from - regular files. + """Copy the file 'src' to 'dst'. + + Both must be filenames. Any error opening either file, reading from + 'src', or writing to 'dst', raises DistutilsFileError. Data is + read/written in chunks of 'buffer_size' bytes (default 16k). No attempt + is made to handle anything apart from regular files. """ # Stolen from shutil module in the standard library, but with # custom error-handling added. @@ -68,15 +69,16 @@ def _copy_file_contents(src, dst, buffer_size=16*1024): def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, link=None, verbose=1, dry_run=0): - """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is - copied there with the same name; otherwise, it must be a filename. (If - the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' - is true (the default), the file's mode (type and permission bits, or - whatever is analogous on the current platform) is copied. If - 'preserve_times' is true (the default), the last-modified and - last-access times are copied as well. If 'update' is true, 'src' will - only be copied if 'dst' does not exist, or if 'dst' does exist but is - older than 'src'. + """Copy a file 'src' to 'dst'. + + If 'dst' is a directory, then 'src' is copied there with the same name; + otherwise, it must be a filename. (If the file exists, it will be + ruthlessly clobbered.) If 'preserve_mode' is true (the default), + the file's mode (type and permission bits, or whatever is analogous on + the current platform) is copied. If 'preserve_times' is true (the + default), the last-modified and last-access times are copied as well. + If 'update' is true, 'src' will only be copied if 'dst' does not exist, + or if 'dst' does exist but is older than 'src'. 'link' allows you to make hard links (os.link) or symbolic links (os.symlink) instead of copying: set it to "hard" or "sym"; if it is @@ -166,13 +168,12 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, # XXX I suspect this is Unix-specific -- need porting help! -def move_file (src, dst, - verbose=1, - dry_run=0): +def move_file(src, dst, verbose=1, dry_run=0): + """Move a file 'src' to 'dst'. - """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will - be moved into it with the same name; otherwise, 'src' is just renamed - to 'dst'. Return the new full name of the file. + If 'dst' is a directory, the file will be moved into it with the same + name; otherwise, 'src' is just renamed to 'dst'. Return the new + full name of the file. Handles cross-device moves on Unix using 'copy_file()'. What about other systems??? @@ -229,7 +230,7 @@ def move_file (src, dst, return dst -def write_file (filename, contents): +def write_file(filename, contents): """Create a file with the specified name and write 'contents' (a sequence of strings without line terminators) to it. """ diff --git a/tests/test_file_util.py b/tests/test_file_util.py index fac4a5d1..99b421f7 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -3,7 +3,7 @@ import unittest import os import shutil -from distutils.file_util import move_file +from distutils.file_util import move_file, write_file, copy_file from distutils import log from distutils.tests import support @@ -55,6 +55,21 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ['moving %s -> %s' % (self.source, self.target_dir)] self.assertEquals(self._logs, wanted) + def test_write_file(self): + lines = ['a', 'b', 'c'] + dir = self.mkdtemp() + foo = os.path.join(dir, 'foo') + write_file(foo, lines) + content = [line.strip() for line in open(foo).readlines()] + self.assertEquals(content, lines) + + def test_copy_file(self): + src_dir = self.mkdtemp() + foo = os.path.join(src_dir, 'foo') + write_file(foo, 'content') + dst_dir = self.mkdtemp() + copy_file(foo, dst_dir) + self.assertTrue(os.path.exists(os.path.join(dst_dir, 'foo'))) def test_suite(): return unittest.makeSuite(FileUtilTestCase) -- cgit v1.2.1 From a45e8e791dab0f958ec7df15b417023dcb3a4c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 4 Jul 2009 02:02:41 +0000 Subject: Fixed #6413: fixed log level in distutils.dist.announce --- dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist.py b/dist.py index f4aecb41..7c29bf6c 100644 --- a/dist.py +++ b/dist.py @@ -914,8 +914,8 @@ Common commands: (see '--help-commands' for more) # -- Methods that operate on the Distribution ---------------------- - def announce(self, msg, level=1): - log.debug(msg) + def announce(self, msg, level=log.INFO): + log.log(level, msg) def run_commands(self): """Run each command that was seen on the setup script command line. -- cgit v1.2.1 From 59dac0c53f0d28ea9cff2ea6bc497a8107ce5f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 4 Jul 2009 02:04:21 +0000 Subject: Merged revisions 73827 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73827 | tarek.ziade | 2009-07-04 04:02:41 +0200 (Sat, 04 Jul 2009) | 1 line Fixed #6413: fixed log level in distutils.dist.announce ........ --- dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist.py b/dist.py index 915d3364..092ec0d2 100644 --- a/dist.py +++ b/dist.py @@ -907,8 +907,8 @@ Common commands: (see '--help-commands' for more) # -- Methods that operate on the Distribution ---------------------- - def announce(self, msg, level=1): - log.debug(msg) + def announce(self, msg, level=log.INFO): + log.log(level, msg) def run_commands(self): """Run each command that was seen on the setup script command line. -- cgit v1.2.1 From cc8b5fb33d9dabdd7256e5da5582f8e6380927ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 4 Jul 2009 02:05:51 +0000 Subject: Merged revisions 73828 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r73828 | tarek.ziade | 2009-07-04 04:04:21 +0200 (Sat, 04 Jul 2009) | 9 lines Merged revisions 73827 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73827 | tarek.ziade | 2009-07-04 04:02:41 +0200 (Sat, 04 Jul 2009) | 1 line Fixed #6413: fixed log level in distutils.dist.announce ........ ................ --- dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist.py b/dist.py index 915d3364..092ec0d2 100644 --- a/dist.py +++ b/dist.py @@ -907,8 +907,8 @@ Common commands: (see '--help-commands' for more) # -- Methods that operate on the Distribution ---------------------- - def announce(self, msg, level=1): - log.debug(msg) + def announce(self, msg, level=log.INFO): + log.log(level, msg) def run_commands(self): """Run each command that was seen on the setup script command line. -- cgit v1.2.1 From 6770846ebeb6751e80a877222f12b83dfe03fc5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 4 Jul 2009 02:59:19 +0000 Subject: using print statements when used for user interaction --- dist.py | 22 +++++++++++----------- tests/test_dist.py | 18 ------------------ 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/dist.py b/dist.py index 7c29bf6c..afed545d 100644 --- a/dist.py +++ b/dist.py @@ -602,14 +602,14 @@ Common commands: (see '--help-commands' for more) options = self.global_options parser.set_option_table(options) parser.print_help(self.common_usage + "\nGlobal options:") - self.announce('') + print('') if display_options: parser.set_option_table(self.display_options) parser.print_help( "Information display options (just display " + "information, ignore any commands)") - self.announce('') + print('') for command in self.commands: if isinstance(command, type) and issubclass(command, Command): @@ -623,9 +623,9 @@ Common commands: (see '--help-commands' for more) else: parser.set_option_table(klass.user_options) parser.print_help("Options for '%s' command:" % klass.__name__) - self.announce('') + print('') - self.announce(gen_usage(self.script_name)) + print(gen_usage(self.script_name)) def handle_display_options(self, option_order): """If there were any non-global "display-only" options @@ -640,8 +640,8 @@ Common commands: (see '--help-commands' for more) # we ignore "foo bar"). if self.help_commands: self.print_commands() - self.announce('') - self.announce(gen_usage(self.script_name)) + print('') + print(gen_usage(self.script_name)) return 1 # If user supplied any of the "display metadata" options, then @@ -657,12 +657,12 @@ Common commands: (see '--help-commands' for more) opt = translate_longopt(opt) value = getattr(self.metadata, "get_"+opt)() if opt in ['keywords', 'platforms']: - self.announce(','.join(value)) + print(','.join(value)) elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'): - self.announce('\n'.join(value)) + print('\n'.join(value)) else: - self.announce(value) + print(value) any_display_options = 1 return any_display_options @@ -671,7 +671,7 @@ Common commands: (see '--help-commands' for more) """Print a subset of the list of all commands -- used by 'print_commands()'. """ - self.announce(header + ":") + print(header + ":") for cmd in commands: klass = self.cmdclass.get(cmd) @@ -682,7 +682,7 @@ Common commands: (see '--help-commands' for more) except AttributeError: description = "(no description available)" - self.announce(" %-*s %s" % (max_length, cmd, description)) + print(" %-*s %s" % (max_length, cmd, description)) def print_commands(self): """Print out a help message listing all available commands with a diff --git a/tests/test_dist.py b/tests/test_dist.py index 07f392c4..75b74a2c 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -165,24 +165,6 @@ class DistributionTestCase(support.TempdirManager, self.assertEquals(dist.metadata.platforms, ['one', 'two']) self.assertEquals(dist.metadata.keywords, ['one', 'two']) - def test_show_help(self): - class FancyGetopt(object): - def __init__(self): - self.count = 0 - - def set_option_table(self, *args): - pass - - def print_help(self, *args): - self.count += 1 - - parser = FancyGetopt() - dist = Distribution() - dist.commands = ['sdist'] - dist.script_name = 'setup.py' - dist._show_help(parser) - self.assertEquals(parser.count, 3) - def test_get_command_packages(self): dist = Distribution() self.assertEquals(dist.command_packages, None) -- cgit v1.2.1 From 78e341ad759a608301a0e5a1f330bed09dc975b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 4 Jul 2009 03:00:50 +0000 Subject: Merged revisions 73834 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73834 | tarek.ziade | 2009-07-04 04:59:19 +0200 (Sat, 04 Jul 2009) | 1 line using print statements when used for user interaction ........ --- dist.py | 22 +++++++++++----------- tests/test_dist.py | 18 ------------------ 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/dist.py b/dist.py index 092ec0d2..ac5a0ca0 100644 --- a/dist.py +++ b/dist.py @@ -596,14 +596,14 @@ Common commands: (see '--help-commands' for more) options = self.global_options parser.set_option_table(options) parser.print_help(self.common_usage + "\nGlobal options:") - self.announce('') + print('') if display_options: parser.set_option_table(self.display_options) parser.print_help( "Information display options (just display " + "information, ignore any commands)") - self.announce('') + print('') for command in self.commands: if isinstance(command, type) and issubclass(command, Command): @@ -617,9 +617,9 @@ Common commands: (see '--help-commands' for more) else: parser.set_option_table(klass.user_options) parser.print_help("Options for '%s' command:" % klass.__name__) - self.announce('') + print('') - self.announce(gen_usage(self.script_name)) + print(gen_usage(self.script_name)) def handle_display_options(self, option_order): """If there were any non-global "display-only" options @@ -634,8 +634,8 @@ Common commands: (see '--help-commands' for more) # we ignore "foo bar"). if self.help_commands: self.print_commands() - self.announce('') - self.announce(gen_usage(self.script_name)) + print('') + print(gen_usage(self.script_name)) return 1 # If user supplied any of the "display metadata" options, then @@ -651,12 +651,12 @@ Common commands: (see '--help-commands' for more) opt = translate_longopt(opt) value = getattr(self.metadata, "get_"+opt)() if opt in ['keywords', 'platforms']: - self.announce(','.join(value)) + print(','.join(value)) elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'): - self.announce('\n'.join(value)) + print('\n'.join(value)) else: - self.announce(value) + print(value) any_display_options = 1 return any_display_options @@ -665,7 +665,7 @@ Common commands: (see '--help-commands' for more) """Print a subset of the list of all commands -- used by 'print_commands()'. """ - self.announce(header + ":") + print(header + ":") for cmd in commands: klass = self.cmdclass.get(cmd) @@ -676,7 +676,7 @@ Common commands: (see '--help-commands' for more) except AttributeError: description = "(no description available)" - self.announce(" %-*s %s" % (max_length, cmd, description)) + print(" %-*s %s" % (max_length, cmd, description)) def print_commands(self): """Print out a help message listing all available commands with a diff --git a/tests/test_dist.py b/tests/test_dist.py index c031cb85..9f795f43 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -136,24 +136,6 @@ class DistributionTestCase(support.LoggingSilencer, self.assertEquals(dist.metadata.platforms, ['one', 'two']) self.assertEquals(dist.metadata.keywords, ['one', 'two']) - def test_show_help(self): - class FancyGetopt(object): - def __init__(self): - self.count = 0 - - def set_option_table(self, *args): - pass - - def print_help(self, *args): - self.count += 1 - - parser = FancyGetopt() - dist = Distribution() - dist.commands = ['sdist'] - dist.script_name = 'setup.py' - dist._show_help(parser) - self.assertEquals(parser.count, 3) - def test_get_command_packages(self): dist = Distribution() self.assertEquals(dist.command_packages, None) -- cgit v1.2.1 From 876d6d4fad84a5abfd40bb09cc9f9164121f8594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 4 Jul 2009 03:01:33 +0000 Subject: Merged revisions 73835 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r73835 | tarek.ziade | 2009-07-04 05:00:50 +0200 (Sat, 04 Jul 2009) | 9 lines Merged revisions 73834 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73834 | tarek.ziade | 2009-07-04 04:59:19 +0200 (Sat, 04 Jul 2009) | 1 line using print statements when used for user interaction ........ ................ --- dist.py | 22 +++++++++++----------- tests/test_dist.py | 18 ------------------ 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/dist.py b/dist.py index 092ec0d2..ac5a0ca0 100644 --- a/dist.py +++ b/dist.py @@ -596,14 +596,14 @@ Common commands: (see '--help-commands' for more) options = self.global_options parser.set_option_table(options) parser.print_help(self.common_usage + "\nGlobal options:") - self.announce('') + print('') if display_options: parser.set_option_table(self.display_options) parser.print_help( "Information display options (just display " + "information, ignore any commands)") - self.announce('') + print('') for command in self.commands: if isinstance(command, type) and issubclass(command, Command): @@ -617,9 +617,9 @@ Common commands: (see '--help-commands' for more) else: parser.set_option_table(klass.user_options) parser.print_help("Options for '%s' command:" % klass.__name__) - self.announce('') + print('') - self.announce(gen_usage(self.script_name)) + print(gen_usage(self.script_name)) def handle_display_options(self, option_order): """If there were any non-global "display-only" options @@ -634,8 +634,8 @@ Common commands: (see '--help-commands' for more) # we ignore "foo bar"). if self.help_commands: self.print_commands() - self.announce('') - self.announce(gen_usage(self.script_name)) + print('') + print(gen_usage(self.script_name)) return 1 # If user supplied any of the "display metadata" options, then @@ -651,12 +651,12 @@ Common commands: (see '--help-commands' for more) opt = translate_longopt(opt) value = getattr(self.metadata, "get_"+opt)() if opt in ['keywords', 'platforms']: - self.announce(','.join(value)) + print(','.join(value)) elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'): - self.announce('\n'.join(value)) + print('\n'.join(value)) else: - self.announce(value) + print(value) any_display_options = 1 return any_display_options @@ -665,7 +665,7 @@ Common commands: (see '--help-commands' for more) """Print a subset of the list of all commands -- used by 'print_commands()'. """ - self.announce(header + ":") + print(header + ":") for cmd in commands: klass = self.cmdclass.get(cmd) @@ -676,7 +676,7 @@ Common commands: (see '--help-commands' for more) except AttributeError: description = "(no description available)" - self.announce(" %-*s %s" % (max_length, cmd, description)) + print(" %-*s %s" % (max_length, cmd, description)) def print_commands(self): """Print out a help message listing all available commands with a diff --git a/tests/test_dist.py b/tests/test_dist.py index 16ef68e9..40760390 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -136,24 +136,6 @@ class DistributionTestCase(support.LoggingSilencer, self.assertEquals(dist.metadata.platforms, ['one', 'two']) self.assertEquals(dist.metadata.keywords, ['one', 'two']) - def test_show_help(self): - class FancyGetopt(object): - def __init__(self): - self.count = 0 - - def set_option_table(self, *args): - pass - - def print_help(self, *args): - self.count += 1 - - parser = FancyGetopt() - dist = Distribution() - dist.commands = ['sdist'] - dist.script_name = 'setup.py' - dist._show_help(parser) - self.assertEquals(parser.count, 3) - def test_get_command_packages(self): dist = Distribution() self.assertEquals(dist.command_packages, None) -- cgit v1.2.1 From 54b37808bf90eb50889ee8761aa22e07969d6a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 6 Jul 2009 12:50:46 +0000 Subject: Fixed #6377: distutils compiler switch ignored (and added a deprecation warning if compiler is not used as supposed = a string option) --- command/build_ext.py | 78 ++++++++++++++++++++++++++++++++++--------------- tests/test_build_ext.py | 14 +++++++++ 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 96bd68be..be37313a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -8,6 +8,8 @@ __revision__ = "$Id$" import sys, os, string, re from types import * +from warnings import warn + from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version @@ -113,6 +115,36 @@ class build_ext (Command): "list available compilers", show_compilers), ] + + # making 'compiler' a property to deprecate + # its usage as something else than a compiler type + # e.g. like a compiler instance + def __init__(self, dist): + self._compiler = None + Command.__init__(self, dist) + + def __setattr__(self, name, value): + # need this to make sure setattr() (used in distutils) + # doesn't kill our property + if name == 'compiler': + self._set_compiler(value) + else: + self.__dict__[name] = value + + def _set_compiler(self, compiler): + if not isinstance(compiler, str) and compiler is not None: + # we don't want to allow that anymore in the future + warn("'compiler' specify the compiler type in build_ext. " + "If you want to get the compiler object itself, " + "use 'compiler_obj'", PendingDeprecationWarning) + + self._compiler = compiler + + def _get_compiler(self): + return self._compiler + + compiler = property(_get_compiler, _set_compiler) + def initialize_options (self): self.extensions = None self.build_lib = None @@ -311,38 +343,38 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler(compiler=None, - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force) - customize_compiler(self.compiler) + self.compiler_obj = new_compiler(compiler=self.compiler, + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) + customize_compiler(self.compiler_obj) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): - self.compiler.initialize(self.plat_name) + self.compiler_obj.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self.compiler.set_include_dirs(self.include_dirs) + self.compiler_obj.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: - self.compiler.define_macro(name, value) + self.compiler_obj.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self.compiler.undefine_macro(macro) + self.compiler_obj.undefine_macro(macro) if self.libraries is not None: - self.compiler.set_libraries(self.libraries) + self.compiler_obj.set_libraries(self.libraries) if self.library_dirs is not None: - self.compiler.set_library_dirs(self.library_dirs) + self.compiler_obj.set_library_dirs(self.library_dirs) if self.rpath is not None: - self.compiler.set_runtime_library_dirs(self.rpath) + self.compiler_obj.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self.compiler.set_link_objects(self.link_objects) + self.compiler_obj.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() @@ -504,13 +536,13 @@ class build_ext (Command): for undef in ext.undef_macros: macros.append((undef,)) - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args, - depends=ext.depends) + objects = self.compiler_obj.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends) # XXX -- this is a Vile HACK! # @@ -531,9 +563,9 @@ class build_ext (Command): extra_args = ext.extra_link_args or [] # Detect target language, if not provided - language = ext.language or self.compiler.detect_language(sources) + language = ext.language or self.compiler_obj.detect_language(sources) - self.compiler.link_shared_object( + self.compiler_obj.link_shared_object( objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, @@ -712,7 +744,7 @@ class build_ext (Command): # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils.msvccompiler import MSVCCompiler - if not isinstance(self.compiler, MSVCCompiler): + if not isinstance(self.compiler_obj, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 2ecead3c..afd0df32 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -3,6 +3,9 @@ import os import tempfile import shutil from StringIO import StringIO +import warnings +from test.test_support import check_warnings +from test.test_support import captured_stdout from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext @@ -395,6 +398,17 @@ class BuildExtTestCase(support.TempdirManager, wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap.so') self.assertEquals(wanted, path) + def test_compiler_deprecation_warning(self): + dist = Distribution() + cmd = build_ext(dist) + + with check_warnings() as w: + warnings.simplefilter("always") + cmd.compiler = object() + self.assertEquals(len(w.warnings), 1) + cmd.compile = 'unix' + self.assertEquals(len(w.warnings), 1) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 40f8dab3e0797b8cf84031702a7ba3a6802bf2eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 6 Jul 2009 13:52:17 +0000 Subject: Merged revisions 73864 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73864 | tarek.ziade | 2009-07-06 14:50:46 +0200 (Mon, 06 Jul 2009) | 1 line Fixed #6377: distutils compiler switch ignored (and added a deprecation warning if compiler is not used as supposed = a string option) ........ --- command/build_ext.py | 77 ++++++++++++++++++++++++++++++++++--------------- tests/test_build_ext.py | 14 +++++++++ 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 30457026..4a3aec2a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -7,6 +7,8 @@ extensions ASAP).""" __revision__ = "$Id$" import sys, os, re +from warnings import warn + from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version @@ -112,6 +114,35 @@ class build_ext(Command): "list available compilers", show_compilers), ] + # making 'compiler' a property to deprecate + # its usage as something else than a compiler type + # e.g. like a compiler instance + def __init__(self, dist): + self._compiler = None + Command.__init__(self, dist) + + def __setattr__(self, name, value): + # need this to make sure setattr() (used in distutils) + # doesn't kill our property + if name == 'compiler': + self._set_compiler(value) + else: + self.__dict__[name] = value + + def _set_compiler(self, compiler): + if not isinstance(compiler, str) and compiler is not None: + # we don't want to allow that anymore in the future + warn("'compiler' specify the compiler type in build_ext. " + "If you want to get the compiler object itself, " + "use 'compiler_obj'", PendingDeprecationWarning) + + self._compiler = compiler + + def _get_compiler(self): + return self._compiler + + compiler = property(_get_compiler, _set_compiler) + def initialize_options(self): self.extensions = None self.build_lib = None @@ -310,38 +341,38 @@ class build_ext(Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler(compiler=None, - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force) - customize_compiler(self.compiler) + self.compiler_obj = new_compiler(compiler=self.compiler, + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) + customize_compiler(self.compiler_obj) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): - self.compiler.initialize(self.plat_name) + self.compiler_obj.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self.compiler.set_include_dirs(self.include_dirs) + self.compiler_obj.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: - self.compiler.define_macro(name, value) + self.compiler_obj.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self.compiler.undefine_macro(macro) + self.compiler_obj.undefine_macro(macro) if self.libraries is not None: - self.compiler.set_libraries(self.libraries) + self.compiler_obj.set_libraries(self.libraries) if self.library_dirs is not None: - self.compiler.set_library_dirs(self.library_dirs) + self.compiler_obj.set_library_dirs(self.library_dirs) if self.rpath is not None: - self.compiler.set_runtime_library_dirs(self.rpath) + self.compiler_obj.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self.compiler.set_link_objects(self.link_objects) + self.compiler_obj.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() @@ -502,13 +533,13 @@ class build_ext(Command): for undef in ext.undef_macros: macros.append((undef,)) - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args, - depends=ext.depends) + objects = self.compiler_obj.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends) # XXX -- this is a Vile HACK! # @@ -529,9 +560,9 @@ class build_ext(Command): extra_args = ext.extra_link_args or [] # Detect target language, if not provided - language = ext.language or self.compiler.detect_language(sources) + language = ext.language or self.compiler_obj.detect_language(sources) - self.compiler.link_shared_object( + self.compiler_obj.link_shared_object( objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, @@ -698,7 +729,7 @@ class build_ext(Command): # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils.msvccompiler import MSVCCompiler - if not isinstance(self.compiler, MSVCCompiler): + if not isinstance(self.compiler_obj, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 6f0e2309..d5312930 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -3,6 +3,9 @@ import os import tempfile import shutil from io import StringIO +import warnings +from test.support import check_warnings +from test.support import captured_stdout from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext @@ -395,6 +398,17 @@ class BuildExtTestCase(TempdirManager, wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap.so') self.assertEquals(wanted, path) + def test_compiler_deprecation_warning(self): + dist = Distribution() + cmd = build_ext(dist) + + with check_warnings() as w: + warnings.simplefilter("always") + cmd.compiler = object() + self.assertEquals(len(w.warnings), 1) + cmd.compile = 'unix' + self.assertEquals(len(w.warnings), 1) + def test_suite(): src = _get_source_filename() if not os.path.exists(src): -- cgit v1.2.1 From 3d1571717a0bc82b7d050bc447e0714135aa0dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 8 Jul 2009 22:40:51 +0000 Subject: Sets the compiler attribute to keep the old behavior for third-party packages. --- command/build_ext.py | 22 +++++++++++++++++++--- tests/test_build_ext.py | 11 ++++++++++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index be37313a..fadd6337 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -134,13 +134,17 @@ class build_ext (Command): def _set_compiler(self, compiler): if not isinstance(compiler, str) and compiler is not None: # we don't want to allow that anymore in the future - warn("'compiler' specify the compiler type in build_ext. " + warn("'compiler' specifies the compiler type in build_ext. " "If you want to get the compiler object itself, " "use 'compiler_obj'", PendingDeprecationWarning) - self._compiler = compiler def _get_compiler(self): + if not isinstance(self._compiler, str) and self._compiler is not None: + # we don't want to allow that anymore in the future + warn("'compiler' specifies the compiler type in build_ext. " + "If you want to get the compiler object itself, " + "use 'compiler_obj'", PendingDeprecationWarning) return self._compiler compiler = property(_get_compiler, _set_compiler) @@ -343,10 +347,22 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler_obj = new_compiler(compiler=self.compiler, + + # used to prevent the usage of an existing compiler for the + # compiler option when calling new_compiler() + # this will be removed in 3.3 and 2.8 + if not isinstance(self._compiler, str): + self._compiler = None + + self.compiler_obj = new_compiler(compiler=self._compiler, verbose=self.verbose, dry_run=self.dry_run, force=self.force) + + # used to keep the compiler object reachable with + # "self.compiler". this will be removed in 3.3 and 2.8 + self._compiler = self.compiler_obj + customize_compiler(self.compiler_obj) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index afd0df32..9356ff80 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -402,12 +402,21 @@ class BuildExtTestCase(support.TempdirManager, dist = Distribution() cmd = build_ext(dist) + class MyCompiler(object): + def do_something(self): + pass + with check_warnings() as w: warnings.simplefilter("always") - cmd.compiler = object() + cmd.compiler = MyCompiler() self.assertEquals(len(w.warnings), 1) cmd.compile = 'unix' self.assertEquals(len(w.warnings), 1) + cmd.compiler = MyCompiler() + cmd.compiler.do_something() + # two more warnings genereated by the get + # and the set + self.assertEquals(len(w.warnings), 3) def test_suite(): src = _get_source_filename() -- cgit v1.2.1 From ea0bff08c9afaa3f0996dade7f847f8e397efa60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 8 Jul 2009 22:42:43 +0000 Subject: Merged revisions 73895 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73895 | tarek.ziade | 2009-07-09 00:40:51 +0200 (Thu, 09 Jul 2009) | 1 line Sets the compiler attribute to keep the old behavior for third-party packages. ........ --- command/build_ext.py | 22 +++++++++++++++++++--- tests/test_build_ext.py | 11 ++++++++++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 4a3aec2a..d8bb2512 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -132,13 +132,17 @@ class build_ext(Command): def _set_compiler(self, compiler): if not isinstance(compiler, str) and compiler is not None: # we don't want to allow that anymore in the future - warn("'compiler' specify the compiler type in build_ext. " + warn("'compiler' specifies the compiler type in build_ext. " "If you want to get the compiler object itself, " "use 'compiler_obj'", PendingDeprecationWarning) - self._compiler = compiler def _get_compiler(self): + if not isinstance(self._compiler, str) and self._compiler is not None: + # we don't want to allow that anymore in the future + warn("'compiler' specifies the compiler type in build_ext. " + "If you want to get the compiler object itself, " + "use 'compiler_obj'", PendingDeprecationWarning) return self._compiler compiler = property(_get_compiler, _set_compiler) @@ -341,10 +345,22 @@ class build_ext(Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler_obj = new_compiler(compiler=self.compiler, + + # used to prevent the usage of an existing compiler for the + # compiler option when calling new_compiler() + # this will be removed in 3.3 and 2.8 + if not isinstance(self._compiler, str): + self._compiler = None + + self.compiler_obj = new_compiler(compiler=self._compiler, verbose=self.verbose, dry_run=self.dry_run, force=self.force) + + # used to keep the compiler object reachable with + # "self.compiler". this will be removed in 3.3 and 2.8 + self._compiler = self.compiler_obj + customize_compiler(self.compiler_obj) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index d5312930..68295837 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -402,12 +402,21 @@ class BuildExtTestCase(TempdirManager, dist = Distribution() cmd = build_ext(dist) + class MyCompiler(object): + def do_something(self): + pass + with check_warnings() as w: warnings.simplefilter("always") - cmd.compiler = object() + cmd.compiler = MyCompiler() self.assertEquals(len(w.warnings), 1) cmd.compile = 'unix' self.assertEquals(len(w.warnings), 1) + cmd.compiler = MyCompiler() + cmd.compiler.do_something() + # two more warnings genereated by the get + # and the set + self.assertEquals(len(w.warnings), 3) def test_suite(): src = _get_source_filename() -- cgit v1.2.1 From 29efe3a5fa50723fc6ca62dd6670889e69732f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 9 Jul 2009 07:42:42 +0000 Subject: PendingDeprecationWarning -> DeprecationWarning in build_ext --- command/build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index fadd6337..86513ea9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -136,7 +136,7 @@ class build_ext (Command): # we don't want to allow that anymore in the future warn("'compiler' specifies the compiler type in build_ext. " "If you want to get the compiler object itself, " - "use 'compiler_obj'", PendingDeprecationWarning) + "use 'compiler_obj'", DeprecationWarning) self._compiler = compiler def _get_compiler(self): @@ -144,7 +144,7 @@ class build_ext (Command): # we don't want to allow that anymore in the future warn("'compiler' specifies the compiler type in build_ext. " "If you want to get the compiler object itself, " - "use 'compiler_obj'", PendingDeprecationWarning) + "use 'compiler_obj'", DeprecationWarning) return self._compiler compiler = property(_get_compiler, _set_compiler) -- cgit v1.2.1 From 83f285d2a77f1d46292e759dbb90b1e025555fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 9 Jul 2009 07:46:10 +0000 Subject: Merged revisions 73901 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73901 | tarek.ziade | 2009-07-09 09:42:42 +0200 (Thu, 09 Jul 2009) | 1 line PendingDeprecationWarning -> DeprecationWarning in build_ext ........ --- command/build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index d8bb2512..6f904992 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -134,7 +134,7 @@ class build_ext(Command): # we don't want to allow that anymore in the future warn("'compiler' specifies the compiler type in build_ext. " "If you want to get the compiler object itself, " - "use 'compiler_obj'", PendingDeprecationWarning) + "use 'compiler_obj'", DeprecationWarning) self._compiler = compiler def _get_compiler(self): @@ -142,7 +142,7 @@ class build_ext(Command): # we don't want to allow that anymore in the future warn("'compiler' specifies the compiler type in build_ext. " "If you want to get the compiler object itself, " - "use 'compiler_obj'", PendingDeprecationWarning) + "use 'compiler_obj'", DeprecationWarning) return self._compiler compiler = property(_get_compiler, _set_compiler) -- cgit v1.2.1 From 2e8960b7459d92bcb1eac2bb9e3eeeac97aea91a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 10 Jul 2009 09:10:33 +0000 Subject: Fixed #6455 (the test shall use pyd files under win32, rather than so files) --- tests/test_build_ext.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 9356ff80..4c09dd80 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -362,6 +362,7 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(lastdir, 'bar') def test_ext_fullpath(self): + ext = sysconfig.get_config_vars()['SO'] # building lxml.etree inplace #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') #etree_ext = Extension('lxml.etree', [etree_c]) @@ -372,14 +373,14 @@ class BuildExtTestCase(support.TempdirManager, cmd.distribution.package_dir = {'': 'src'} cmd.distribution.packages = ['lxml', 'lxml.html'] curdir = os.getcwd() - wanted = os.path.join(curdir, 'src', 'lxml', 'etree.so') + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) # building lxml.etree not inplace cmd.inplace = 0 cmd.build_lib = os.path.join(curdir, 'tmpdir') - wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree.so') + wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) @@ -389,13 +390,13 @@ class BuildExtTestCase(support.TempdirManager, cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', - 'portmap.so') + 'portmap' + ext) self.assertEquals(wanted, path) # building twisted.runner.portmap inplace cmd.inplace = 1 path = cmd.get_ext_fullpath('twisted.runner.portmap') - wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap.so') + wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) self.assertEquals(wanted, path) def test_compiler_deprecation_warning(self): -- cgit v1.2.1 From de551ca3893c39b20819e3da991b39f5892a4bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 10 Jul 2009 09:13:17 +0000 Subject: Merged revisions 73921 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73921 | tarek.ziade | 2009-07-10 11:10:33 +0200 (Fri, 10 Jul 2009) | 1 line Fixed #6455 (the test shall use pyd files under win32, rather than so files) ........ --- tests/test_build_ext.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index e01c8820..153c8756 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -296,20 +296,21 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(lastdir, 'bar') def test_ext_fullpath(self): + ext = sysconfig.get_config_vars()['SO'] dist = Distribution() cmd = build_ext(dist) cmd.inplace = 1 cmd.distribution.package_dir = {'': 'src'} cmd.distribution.packages = ['lxml', 'lxml.html'] curdir = os.getcwd() - wanted = os.path.join(curdir, 'src', 'lxml', 'etree.so') + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) # building lxml.etree not inplace cmd.inplace = 0 cmd.build_lib = os.path.join(curdir, 'tmpdir') - wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree.so') + wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) @@ -319,13 +320,13 @@ class BuildExtTestCase(support.TempdirManager, cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', - 'portmap.so') + 'portmap' + ext) self.assertEquals(wanted, path) # building twisted.runner.portmap inplace cmd.inplace = 1 path = cmd.get_ext_fullpath('twisted.runner.portmap') - wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap.so') + wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) self.assertEquals(wanted, path) def test_suite(): -- cgit v1.2.1 From 6aecd94fef0f521cf3839365e53e7272d23327f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 10 Jul 2009 09:14:31 +0000 Subject: Merged revisions 73921 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73921 | tarek.ziade | 2009-07-10 11:10:33 +0200 (Fri, 10 Jul 2009) | 1 line Fixed #6455 (the test shall use pyd files under win32, rather than so files) ........ --- tests/test_build_ext.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 68295837..d97a97eb 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -362,6 +362,7 @@ class BuildExtTestCase(TempdirManager, self.assertEquals(lastdir, 'bar') def test_ext_fullpath(self): + ext = sysconfig.get_config_vars()['SO'] # building lxml.etree inplace #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') #etree_ext = Extension('lxml.etree', [etree_c]) @@ -372,14 +373,14 @@ class BuildExtTestCase(TempdirManager, cmd.distribution.package_dir = {'': 'src'} cmd.distribution.packages = ['lxml', 'lxml.html'] curdir = os.getcwd() - wanted = os.path.join(curdir, 'src', 'lxml', 'etree.so') + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) # building lxml.etree not inplace cmd.inplace = 0 cmd.build_lib = os.path.join(curdir, 'tmpdir') - wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree.so') + wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) @@ -389,13 +390,13 @@ class BuildExtTestCase(TempdirManager, cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', - 'portmap.so') + 'portmap' + ext) self.assertEquals(wanted, path) # building twisted.runner.portmap inplace cmd.inplace = 1 path = cmd.get_ext_fullpath('twisted.runner.portmap') - wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap.so') + wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) self.assertEquals(wanted, path) def test_compiler_deprecation_warning(self): -- cgit v1.2.1 From 0c03a8176da33be31b41552fb2f36ec985e35277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 10 Jul 2009 09:16:07 +0000 Subject: Merged revisions 73923 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r73923 | tarek.ziade | 2009-07-10 11:14:31 +0200 (Fri, 10 Jul 2009) | 9 lines Merged revisions 73921 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73921 | tarek.ziade | 2009-07-10 11:10:33 +0200 (Fri, 10 Jul 2009) | 1 line Fixed #6455 (the test shall use pyd files under win32, rather than so files) ........ ................ --- tests/test_build_ext.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index f0580c1f..c6714836 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -359,6 +359,7 @@ class BuildExtTestCase(TempdirManager, self.assertEquals(lastdir, 'bar') def test_ext_fullpath(self): + ext = sysconfig.get_config_vars()['SO'] # building lxml.etree inplace #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') #etree_ext = Extension('lxml.etree', [etree_c]) @@ -369,14 +370,14 @@ class BuildExtTestCase(TempdirManager, cmd.distribution.package_dir = {'': 'src'} cmd.distribution.packages = ['lxml', 'lxml.html'] curdir = os.getcwd() - wanted = os.path.join(curdir, 'src', 'lxml', 'etree.so') + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) # building lxml.etree not inplace cmd.inplace = 0 cmd.build_lib = os.path.join(curdir, 'tmpdir') - wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree.so') + wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) @@ -386,13 +387,13 @@ class BuildExtTestCase(TempdirManager, cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', - 'portmap.so') + 'portmap' + ext) self.assertEquals(wanted, path) # building twisted.runner.portmap inplace cmd.inplace = 1 path = cmd.get_ext_fullpath('twisted.runner.portmap') - wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap.so') + wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) self.assertEquals(wanted, path) def test_suite(): -- cgit v1.2.1 From 015479841be6a07ee482f02cbd2aa6954a36729d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 10 Jul 2009 09:57:15 +0000 Subject: Added test coverage for distutils.command.build --- tests/test_build.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 tests/test_build.py diff --git a/tests/test_build.py b/tests/test_build.py new file mode 100644 index 00000000..6bca27ee --- /dev/null +++ b/tests/test_build.py @@ -0,0 +1,54 @@ +"""Tests for distutils.command.build.""" +import unittest +import os +import sys + +from distutils.command.build import build +from distutils.tests import support +from distutils.util import get_platform + +class BuildTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + cmd = build(dist) + cmd.finalize_options() + + # if not specified, plat_name gets the current platform + self.assertEquals(cmd.plat_name, get_platform()) + + # build_purelib is build + lib + wanted = os.path.join(cmd.build_base, 'lib') + self.assertEquals(cmd.build_purelib, wanted) + + # build_platlib is 'build/lib.platform-x.x[-pydebug]' + # examples: + # build/lib.macosx-10.3-i386-2.7 + plat_spec = '.%s-%s' % (cmd.plat_name, sys.version[0:3]) + if hasattr(sys, 'gettotalrefcount'): + self.assertTrue(cmd.build_platlib.endswith('-pydebug')) + plat_spec += '-pydebug' + wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) + self.assertEquals(cmd.build_platlib, wanted) + + # by default, build_lib = build_purelib + self.assertEquals(cmd.build_lib, cmd.build_purelib) + + # build_temp is build/temp. + wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) + self.assertEquals(cmd.build_temp, wanted) + + # build_scripts is build/scripts-x.x + wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3]) + self.assertEquals(cmd.build_scripts, wanted) + + # executable is os.path.normpath(sys.executable) + self.assertEquals(cmd.executable, os.path.normpath(sys.executable)) + +def test_suite(): + return unittest.makeSuite(BuildTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 56d544027577bfa402454b464e128f149c2929f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 10 Jul 2009 10:00:21 +0000 Subject: cleaned up distutils.command.build --- command/build.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/command/build.py b/command/build.py index 84e05020..d394e4b1 100644 --- a/command/build.py +++ b/command/build.py @@ -9,13 +9,11 @@ from distutils.core import Command from distutils.errors import DistutilsOptionError from distutils.util import get_platform - -def show_compilers (): +def show_compilers(): from distutils.ccompiler import show_compilers show_compilers() - -class build (Command): +class build(Command): description = "build everything needed to install" @@ -53,7 +51,7 @@ class build (Command): "list available compilers", show_compilers), ] - def initialize_options (self): + def initialize_options(self): self.build_base = 'build' # these are decided only after 'build_base' has its final value # (unless overridden by the user or client) @@ -68,8 +66,7 @@ class build (Command): self.force = 0 self.executable = None - def finalize_options (self): - + def finalize_options(self): if self.plat_name is None: self.plat_name = get_platform() else: @@ -118,11 +115,8 @@ class build (Command): if self.executable is None: self.executable = os.path.normpath(sys.executable) - # finalize_options () - - - def run (self): + def run(self): # Run all relevant sub-commands. This will be some subset of: # - build_py - pure Python modules # - build_clib - standalone C libraries @@ -131,7 +125,6 @@ class build (Command): for cmd_name in self.get_sub_commands(): self.run_command(cmd_name) - # -- Predicates for the sub-command list --------------------------- def has_pure_modules (self): @@ -146,11 +139,8 @@ class build (Command): def has_scripts (self): return self.distribution.has_scripts() - sub_commands = [('build_py', has_pure_modules), ('build_clib', has_c_libraries), ('build_ext', has_ext_modules), ('build_scripts', has_scripts), ] - -# class build -- cgit v1.2.1 From 6375e0821736229bf071350a185e6a984f16d368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 10 Jul 2009 10:03:20 +0000 Subject: Merged revisions 73925-73926 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73925 | tarek.ziade | 2009-07-10 11:57:15 +0200 (Fri, 10 Jul 2009) | 1 line Added test coverage for distutils.command.build ........ r73926 | tarek.ziade | 2009-07-10 12:00:21 +0200 (Fri, 10 Jul 2009) | 1 line cleaned up distutils.command.build ........ --- command/build.py | 4 ---- tests/test_build.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 tests/test_build.py diff --git a/command/build.py b/command/build.py index 9c2667cf..715621e1 100644 --- a/command/build.py +++ b/command/build.py @@ -9,12 +9,10 @@ from distutils.core import Command from distutils.errors import DistutilsOptionError from distutils.util import get_platform - def show_compilers(): from distutils.ccompiler import show_compilers show_compilers() - class build(Command): description = "build everything needed to install" @@ -127,7 +125,6 @@ class build(Command): for cmd_name in self.get_sub_commands(): self.run_command(cmd_name) - # -- Predicates for the sub-command list --------------------------- def has_pure_modules(self): @@ -142,7 +139,6 @@ class build(Command): def has_scripts(self): return self.distribution.has_scripts() - sub_commands = [('build_py', has_pure_modules), ('build_clib', has_c_libraries), ('build_ext', has_ext_modules), diff --git a/tests/test_build.py b/tests/test_build.py new file mode 100644 index 00000000..6bca27ee --- /dev/null +++ b/tests/test_build.py @@ -0,0 +1,54 @@ +"""Tests for distutils.command.build.""" +import unittest +import os +import sys + +from distutils.command.build import build +from distutils.tests import support +from distutils.util import get_platform + +class BuildTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + cmd = build(dist) + cmd.finalize_options() + + # if not specified, plat_name gets the current platform + self.assertEquals(cmd.plat_name, get_platform()) + + # build_purelib is build + lib + wanted = os.path.join(cmd.build_base, 'lib') + self.assertEquals(cmd.build_purelib, wanted) + + # build_platlib is 'build/lib.platform-x.x[-pydebug]' + # examples: + # build/lib.macosx-10.3-i386-2.7 + plat_spec = '.%s-%s' % (cmd.plat_name, sys.version[0:3]) + if hasattr(sys, 'gettotalrefcount'): + self.assertTrue(cmd.build_platlib.endswith('-pydebug')) + plat_spec += '-pydebug' + wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) + self.assertEquals(cmd.build_platlib, wanted) + + # by default, build_lib = build_purelib + self.assertEquals(cmd.build_lib, cmd.build_purelib) + + # build_temp is build/temp. + wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) + self.assertEquals(cmd.build_temp, wanted) + + # build_scripts is build/scripts-x.x + wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3]) + self.assertEquals(cmd.build_scripts, wanted) + + # executable is os.path.normpath(sys.executable) + self.assertEquals(cmd.executable, os.path.normpath(sys.executable)) + +def test_suite(): + return unittest.makeSuite(BuildTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From c7cb2d865ed19c68df148b0cd0018914c2a68b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Jul 2009 10:48:31 +0000 Subject: cleaned up distutils.build_ext module --- command/build_ext.py | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 86513ea9..0c79476b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -6,8 +6,7 @@ extensions ASAP).""" __revision__ = "$Id$" -import sys, os, string, re -from types import * +import sys, os, re from warnings import warn from distutils.core import Command @@ -41,7 +40,7 @@ def show_compilers (): show_compilers() -class build_ext (Command): +class build_ext(Command): description = "build C/C++ extensions (compile/link to build directory)" @@ -149,7 +148,7 @@ class build_ext (Command): compiler = property(_get_compiler, _set_compiler) - def initialize_options (self): + def initialize_options(self): self.extensions = None self.build_lib = None self.plat_name = None @@ -213,13 +212,13 @@ class build_ext (Command): self.libraries = [] if self.library_dirs is None: self.library_dirs = [] - elif type(self.library_dirs) is StringType: - self.library_dirs = string.split(self.library_dirs, os.pathsep) + elif isinstance(self.library_dirs, str): + self.library_dirs = self.library_dirs.split(os.pathsep) if self.rpath is None: self.rpath = [] - elif type(self.rpath) is StringType: - self.rpath = string.split(self.rpath, os.pathsep) + elif isinstance(self.rpath, str): + self.rpath = self.rpath.split(os.pathsep) # for extensions under windows use different directories # for Release and Debug builds. @@ -296,7 +295,7 @@ class build_ext (Command): if self.define: defines = self.define.split(',') - self.define = map(lambda symbol: (symbol, '1'), defines) + self.define = [(symbol, '1') for symbol in defines] # The option for macros to undefine is also a string from the # option parsing, but has to be a list. Multiple symbols can also @@ -512,7 +511,7 @@ class build_ext (Command): def build_extension(self, ext): sources = ext.sources - if sources is None or type(sources) not in (ListType, TupleType): + if sources is None or not isinstance(sources, (list, tuple)): raise DistutilsSetupError, \ ("in 'ext_modules' option (extension '%s'), " + "'sources' must be present and must be " + @@ -593,14 +592,12 @@ class build_ext (Command): target_lang=language) - def swig_sources (self, sources, extension): - + def swig_sources(self, sources, extension): """Walk the list of source files in 'sources', looking for SWIG interface (.i) files. Run SWIG on all that are found, and return a modified 'sources' list with SWIG source files replaced by the generated C (or C++) files. """ - new_sources = [] swig_sources = [] swig_targets = {} @@ -649,9 +646,7 @@ class build_ext (Command): return new_sources - # swig_sources () - - def find_swig (self): + def find_swig(self): """Return the name of the SWIG executable. On Unix, this is just "swig" -- it should be in the PATH. Tries a bit harder on Windows. @@ -680,8 +675,6 @@ class build_ext (Command): ("I don't know how to find (much less run) SWIG " "on platform '%s'") % os.name - # find_swig () - # -- Name generators ----------------------------------------------- # (extension names, filenames, whatever) def get_ext_fullpath(self, ext_name): @@ -726,29 +719,28 @@ class build_ext (Command): "foo\bar.pyd"). """ from distutils.sysconfig import get_config_var - ext_path = string.split(ext_name, '.') + ext_path = ext_name.split('.') # OS/2 has an 8 character module (extension) limit :-( if os.name == "os2": ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] # extensions in debug_mode are named 'module_d.pyd' under windows so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: - return apply(os.path.join, ext_path) + '_d' + so_ext + return os.path.join(*ext_path) + '_d' + so_ext return os.path.join(*ext_path) + so_ext - def get_export_symbols (self, ext): + def get_export_symbols(self, ext): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not provided, "init" + module_name. Only relevant on Windows, where the .pyd file (DLL) must export the module "init" function. """ - - initfunc_name = "init" + string.split(ext.name,'.')[-1] + initfunc_name = "init" + ext.name.split('.')[-1] if initfunc_name not in ext.export_symbols: ext.export_symbols.append(initfunc_name) return ext.export_symbols - def get_libraries (self, ext): + def get_libraries(self, ext): """Return the list of libraries to link against when building a shared extension. On most platforms, this is just 'ext.libraries'; on Windows and OS/2, we add the Python library (eg. python20.dll). @@ -821,5 +813,3 @@ class build_ext (Command): return ext.libraries + [pythonlib] else: return ext.libraries - -# class build_ext -- cgit v1.2.1 From 2bd17a600378485e8740d04c6715f2ce381254a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Jul 2009 10:55:27 +0000 Subject: fixed #6459: distutils.command.build_ext.get_export_symbols now uses 'PyInit' --- command/build_ext.py | 6 +++--- tests/test_build_ext.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 0c79476b..17cb26b5 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -732,10 +732,10 @@ class build_ext(Command): def get_export_symbols(self, ext): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not - provided, "init" + module_name. Only relevant on Windows, where - the .pyd file (DLL) must export the module "init" function. + provided, "PyInit_" + module_name. Only relevant on Windows, where + the .pyd file (DLL) must export the module "PyInit_" function. """ - initfunc_name = "init" + ext.name.split('.')[-1] + initfunc_name = "PyInit_" + ext.name.split('.')[-1] if initfunc_name not in ext.export_symbols: ext.export_symbols.append(initfunc_name) return ext.export_symbols diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 4c09dd80..7886c792 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -299,7 +299,7 @@ class BuildExtTestCase(support.TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void initfoo(void) {};\n') + self.write_file(c_file, 'void PyInit_foo(void) {};\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From d7048753d100a97e2251f20a7756c15499492d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Jul 2009 10:57:49 +0000 Subject: Merged revisions 73946 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73946 | tarek.ziade | 2009-07-11 12:55:27 +0200 (Sat, 11 Jul 2009) | 1 line fixed #6459: distutils.command.build_ext.get_export_symbols now uses 'PyInit' ........ --- command/build_ext.py | 7 +++---- tests/test_build_ext.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 8cb1efe6..ccc3fe56 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -680,11 +680,10 @@ class build_ext (Command): def get_export_symbols (self, ext): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not - provided, "init" + module_name. Only relevant on Windows, where - the .pyd file (DLL) must export the module "init" function. + provided, "PyInit_" + module_name. Only relevant on Windows, where + the .pyd file (DLL) must export the module "PyInit_" function. """ - - initfunc_name = "init" + string.split(ext.name,'.')[-1] + initfunc_name = "PyInit_" + ext.name.split('.')[-1] if initfunc_name not in ext.export_symbols: ext.export_symbols.append(initfunc_name) return ext.export_symbols diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 153c8756..d9925480 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -233,7 +233,7 @@ class BuildExtTestCase(support.TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void initfoo(void) {};\n') + self.write_file(c_file, 'void PyInit_foo(void) {};\n') ext = Extension('foo', [c_file]) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From 72a548f960200f1057295b791828ec1698a5c639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Jul 2009 10:59:56 +0000 Subject: Merged revisions 73946 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73946 | tarek.ziade | 2009-07-11 12:55:27 +0200 (Sat, 11 Jul 2009) | 1 line fixed #6459: distutils.command.build_ext.get_export_symbols now uses 'PyInit' ........ --- command/build_ext.py | 2 +- tests/test_build_ext.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 6f904992..14c529ac 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -726,7 +726,7 @@ class build_ext(Command): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not provided, "PyInit_" + module_name. Only relevant on Windows, where - the .pyd file (DLL) must export the module "init" function. + the .pyd file (DLL) must export the module "PyInit_" function. """ initfunc_name = "PyInit_" + ext.name.split('.')[-1] if initfunc_name not in ext.export_symbols: diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index d97a97eb..7a27f343 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -299,7 +299,7 @@ class BuildExtTestCase(TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void initfoo(void) {};\n') + self.write_file(c_file, 'void PyInit_foo(void) {};\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From 3b8fc3f7befd6da629a7875863c6d1eadda34b8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Jul 2009 11:01:14 +0000 Subject: Merged revisions 73948 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r73948 | tarek.ziade | 2009-07-11 12:59:56 +0200 (Sat, 11 Jul 2009) | 9 lines Merged revisions 73946 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73946 | tarek.ziade | 2009-07-11 12:55:27 +0200 (Sat, 11 Jul 2009) | 1 line fixed #6459: distutils.command.build_ext.get_export_symbols now uses 'PyInit' ........ ................ --- command/build_ext.py | 2 +- tests/test_build_ext.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 30457026..15965864 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -679,7 +679,7 @@ class build_ext(Command): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not provided, "PyInit_" + module_name. Only relevant on Windows, where - the .pyd file (DLL) must export the module "init" function. + the .pyd file (DLL) must export the module "PyInit_" function. """ initfunc_name = "PyInit_" + ext.name.split('.')[-1] if initfunc_name not in ext.export_symbols: diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index c6714836..9861ee29 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -296,7 +296,7 @@ class BuildExtTestCase(TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void initfoo(void) {};\n') + self.write_file(c_file, 'void PyInit_foo(void) {};\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From d901b9150542ff215c1a621991c10f3e5ec342c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Jul 2009 17:21:00 +0000 Subject: reverted changes for #6459 (doesn't apply on 2.x) --- command/build_ext.py | 6 +++--- tests/test_build_ext.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 17cb26b5..0c79476b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -732,10 +732,10 @@ class build_ext(Command): def get_export_symbols(self, ext): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not - provided, "PyInit_" + module_name. Only relevant on Windows, where - the .pyd file (DLL) must export the module "PyInit_" function. + provided, "init" + module_name. Only relevant on Windows, where + the .pyd file (DLL) must export the module "init" function. """ - initfunc_name = "PyInit_" + ext.name.split('.')[-1] + initfunc_name = "init" + ext.name.split('.')[-1] if initfunc_name not in ext.export_symbols: ext.export_symbols.append(initfunc_name) return ext.export_symbols diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 7886c792..51a21a68 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -299,7 +299,7 @@ class BuildExtTestCase(support.TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void PyInit_foo(void) {};\n') + self.write_file(c_file, 'void init_foo(void) {};\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From 2ee6163d4df9600975467bbc57a6f6e9a3008ba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 11 Jul 2009 17:22:14 +0000 Subject: Merged revisions 73954 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73954 | tarek.ziade | 2009-07-11 19:21:00 +0200 (Sat, 11 Jul 2009) | 1 line reverted changes for #6459 (doesn't apply on 2.x) ........ --- command/build_ext.py | 6 +++--- tests/test_build_ext.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index ccc3fe56..db82f1d0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -680,10 +680,10 @@ class build_ext (Command): def get_export_symbols (self, ext): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not - provided, "PyInit_" + module_name. Only relevant on Windows, where - the .pyd file (DLL) must export the module "PyInit_" function. + provided, "init" + module_name. Only relevant on Windows, where + the .pyd file (DLL) must export the module "init" function. """ - initfunc_name = "PyInit_" + ext.name.split('.')[-1] + initfunc_name = "init" + ext.name.split('.')[-1] if initfunc_name not in ext.export_symbols: ext.export_symbols.append(initfunc_name) return ext.export_symbols diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index d9925480..d0716485 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -233,7 +233,7 @@ class BuildExtTestCase(support.TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void PyInit_foo(void) {};\n') + self.write_file(c_file, 'void init_foo(void) {};\n') ext = Extension('foo', [c_file]) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From 0e0874b3e717156832ea24bb06f46147af1103dd Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 12 Jul 2009 02:04:47 +0000 Subject: Fixed distutils test. --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 51a21a68..4c09dd80 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -299,7 +299,7 @@ class BuildExtTestCase(support.TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void init_foo(void) {};\n') + self.write_file(c_file, 'void initfoo(void) {};\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From da220af37e1ec7552ff8f8724d03136f6aa96b4d Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 12 Jul 2009 02:09:25 +0000 Subject: Merged revisions 73970 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r73970 | hirokazu.yamamoto | 2009-07-12 11:04:47 +0900 | 1 line Fixed distutils test. ........ --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index d0716485..153c8756 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -233,7 +233,7 @@ class BuildExtTestCase(support.TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void init_foo(void) {};\n') + self.write_file(c_file, 'void initfoo(void) {};\n') ext = Extension('foo', [c_file]) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From d28cb5fd8236e1d5949e62723a7688dcaa12f447 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Jul 2009 08:27:26 +0000 Subject: Fixed #6438: distutils.cygwinccompiler.get_versions was trying to use a re string pattern on a bytes --- cygwinccompiler.py | 6 ++++-- tests/test_cygwinccompiler.py | 17 +++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 5f3a389e..85043718 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -359,7 +359,7 @@ def check_config_h(): return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) -RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') +RE_VERSION = re.compile(b'(\d+\.\d+(\.\d+)*)') def _find_exe_version(cmd): """Find the version of an executable by running `cmd` in the shell. @@ -378,7 +378,9 @@ def _find_exe_version(cmd): result = RE_VERSION.search(out_string) if result is None: return None - return LooseVersion(result.group(1)) + # LooseVersion works with strings + # so we need to decode our bytes + return LooseVersion(result.group(1).decode()) def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index c5a6495d..a57694d4 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -2,7 +2,7 @@ import unittest import sys import os -from io import StringIO +from io import BytesIO import subprocess from distutils import cygwinccompiler @@ -19,7 +19,8 @@ class FakePopen(object): self.cmd = cmd.split()[0] exes = self.test_class._exes if self.cmd in exes: - self.stdout = StringIO(exes[self.cmd]) + # issue #6438 in Python 3.x, Popen returns bytes + self.stdout = BytesIO(exes[self.cmd]) else: self.stdout = os.popen(cmd, 'r') @@ -87,30 +88,30 @@ class CygwinCCompilerTestCase(support.TempdirManager, self.assertEquals(get_versions(), (None, None, None)) # Let's fake we have 'gcc' and it returns '3.4.5' - self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF' + self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' res = get_versions() self.assertEquals(str(res[0]), '3.4.5') # and let's see what happens when the version # doesn't match the regular expression # (\d+\.\d+(\.\d+)*) - self._exes['gcc'] = 'very strange output' + self._exes['gcc'] = b'very strange output' res = get_versions() self.assertEquals(res[0], None) # same thing for ld - self._exes['ld'] = 'GNU ld version 2.17.50 20060824' + self._exes['ld'] = b'GNU ld version 2.17.50 20060824' res = get_versions() self.assertEquals(str(res[1]), '2.17.50') - self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' + self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' res = get_versions() self.assertEquals(res[1], None) # and dllwrap - self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF' + self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' res = get_versions() self.assertEquals(str(res[2]), '2.17.50') - self._exes['dllwrap'] = 'Cheese Wrap' + self._exes['dllwrap'] = b'Cheese Wrap' res = get_versions() self.assertEquals(res[2], None) -- cgit v1.2.1 From 716024cb88a39f8550ed10b312b3f8cec75a1e7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 12 Jul 2009 08:39:08 +0000 Subject: Merged revisions 73975 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r73975 | tarek.ziade | 2009-07-12 10:27:26 +0200 (Sun, 12 Jul 2009) | 1 line Fixed #6438: distutils.cygwinccompiler.get_versions was trying to use a re string pattern on a bytes ........ --- cygwinccompiler.py | 6 ++++-- tests/test_cygwinccompiler.py | 17 +++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 5f3a389e..85043718 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -359,7 +359,7 @@ def check_config_h(): return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) -RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') +RE_VERSION = re.compile(b'(\d+\.\d+(\.\d+)*)') def _find_exe_version(cmd): """Find the version of an executable by running `cmd` in the shell. @@ -378,7 +378,9 @@ def _find_exe_version(cmd): result = RE_VERSION.search(out_string) if result is None: return None - return LooseVersion(result.group(1)) + # LooseVersion works with strings + # so we need to decode our bytes + return LooseVersion(result.group(1).decode()) def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index c5a6495d..a57694d4 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -2,7 +2,7 @@ import unittest import sys import os -from io import StringIO +from io import BytesIO import subprocess from distutils import cygwinccompiler @@ -19,7 +19,8 @@ class FakePopen(object): self.cmd = cmd.split()[0] exes = self.test_class._exes if self.cmd in exes: - self.stdout = StringIO(exes[self.cmd]) + # issue #6438 in Python 3.x, Popen returns bytes + self.stdout = BytesIO(exes[self.cmd]) else: self.stdout = os.popen(cmd, 'r') @@ -87,30 +88,30 @@ class CygwinCCompilerTestCase(support.TempdirManager, self.assertEquals(get_versions(), (None, None, None)) # Let's fake we have 'gcc' and it returns '3.4.5' - self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF' + self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' res = get_versions() self.assertEquals(str(res[0]), '3.4.5') # and let's see what happens when the version # doesn't match the regular expression # (\d+\.\d+(\.\d+)*) - self._exes['gcc'] = 'very strange output' + self._exes['gcc'] = b'very strange output' res = get_versions() self.assertEquals(res[0], None) # same thing for ld - self._exes['ld'] = 'GNU ld version 2.17.50 20060824' + self._exes['ld'] = b'GNU ld version 2.17.50 20060824' res = get_versions() self.assertEquals(str(res[1]), '2.17.50') - self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' + self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' res = get_versions() self.assertEquals(res[1], None) # and dllwrap - self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF' + self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' res = get_versions() self.assertEquals(str(res[2]), '2.17.50') - self._exes['dllwrap'] = 'Cheese Wrap' + self._exes['dllwrap'] = b'Cheese Wrap' res = get_versions() self.assertEquals(res[2], None) -- cgit v1.2.1 From 01a07bef9a2a900b2ae961c1cd8b98f4c43d5b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 16 Jul 2009 15:35:45 +0000 Subject: #6466 refactored distutils duplicate get_versions() functions (used to get gcc/ld/dllwrap versions) --- cygwinccompiler.py | 42 ++++++++----------- emxccompiler.py | 33 ++++++--------- tests/test_cygwinccompiler.py | 83 ++++++++++-------------------------- tests/test_emxccompiler.py | 33 +++++++++++++++ tests/test_util.py | 97 +++++++++++++++++++++++++++++++++++++++++-- util.py | 54 +++++++++++++++++++++++- 6 files changed, 231 insertions(+), 111 deletions(-) create mode 100644 tests/test_emxccompiler.py diff --git a/cygwinccompiler.py b/cygwinccompiler.py index c35e49d7..a8476e65 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -50,16 +50,15 @@ __revision__ = "$Id$" import os import sys import copy -from subprocess import Popen, PIPE import re +from warnings import warn from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError from distutils import log -from distutils.version import LooseVersion -from distutils.spawn import find_executable +from distutils.util import get_compiler_versions def get_msvcr(): """Include the appropriate MSVC runtime library if Python was built @@ -110,7 +109,7 @@ class CygwinCCompiler(UnixCCompiler): % details) self.gcc_version, self.ld_version, self.dllwrap_version = \ - get_versions() + get_compiler_versions() self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % (self.gcc_version, self.ld_version, @@ -359,31 +358,26 @@ def check_config_h(): return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) -RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') +class _Deprecated_SRE_Pattern(object): + def __init__(self, pattern): + self.pattern = pattern -def _find_exe_version(cmd): - """Find the version of an executable by running `cmd` in the shell. + def __getattr__(self, name): + if name in ('findall', 'finditer', 'match', 'scanner', 'search', + 'split', 'sub', 'subn'): + warn("'distutils.cygwinccompiler.RE_VERSION' is deprecated " + "and will be removed in the next version", DeprecationWarning) + return getattr(self.pattern, name) - If the command is not found, or the output does not match - `RE_VERSION`, returns None. - """ - executable = cmd.split()[0] - if find_executable(executable) is None: - return None - out = Popen(cmd, shell=True, stdout=PIPE).stdout - try: - out_string = out.read() - finally: - out.close() - result = RE_VERSION.search(out_string) - if result is None: - return None - return LooseVersion(result.group(1)) +RE_VERSION = _Deprecated_SRE_Pattern(re.compile('(\d+\.\d+(\.\d+)*)')) def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. If not possible it returns None for it. """ - commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] - return tuple([_find_exe_version(cmd) for cmd in commands]) + warn("'distutils.cygwinccompiler.get_versions' is deprecated " + "use 'distutils.util.get_compiler_versions' instead", + DeprecationWarning) + + return get_compiler_versions() diff --git a/emxccompiler.py b/emxccompiler.py index f52e6323..a5a2ef88 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -21,12 +21,15 @@ handles the EMX port of the GNU C compiler to OS/2. __revision__ = "$Id$" -import os,sys,copy +import os, sys, copy +from warnings import warn + from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError from distutils import log +from distutils.util import get_compiler_versions class EMXCCompiler (UnixCCompiler): @@ -55,8 +58,8 @@ class EMXCCompiler (UnixCCompiler): ("Reason: %s." % details) + "Compiling may fail because of undefined preprocessor macros.") - (self.gcc_version, self.ld_version) = \ - get_versions() + gcc_version, ld_version, dllwrap_version = get_compiler_versions() + self.gcc_version, self.ld_version = gcc_version, ld_version self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" % (self.gcc_version, self.ld_version) ) @@ -293,23 +296,11 @@ def get_versions(): """ Try to find out the versions of gcc and ld. If not possible it returns None for it. """ - from distutils.version import StrictVersion - from distutils.spawn import find_executable - import re - - gcc_exe = find_executable('gcc') - if gcc_exe: - out = os.popen(gcc_exe + ' -dumpversion','r') - out_string = out.read() - out.close() - result = re.search('(\d+\.\d+\.\d+)',out_string) - if result: - gcc_version = StrictVersion(result.group(1)) - else: - gcc_version = None - else: - gcc_version = None + warn("'distutils.emxccompiler.get_versions' is deprecated " + "use 'distutils.util.get_compiler_versions' instead", + DeprecationWarning) + # EMX ld has no way of reporting version number, and we use GCC # anyway - so we can link OMF DLLs - ld_version = None - return (gcc_version, ld_version) + gcc_version, ld_version, dllwrap_version = get_compiler_versions() + return gcc_version, None diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index fb823e4d..b54ffff3 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -2,28 +2,19 @@ import unittest import sys import os -from StringIO import StringIO -import subprocess +import warnings + +from test.test_support import check_warnings +from test.test_support import captured_stdout from distutils import cygwinccompiler from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_UNCERTAIN, get_versions, - get_msvcr) + get_msvcr, RE_VERSION) +from distutils.util import get_compiler_versions from distutils.tests import support -class FakePopen(object): - test_class = None - - def __init__(self, cmd, shell, stdout): - self.cmd = cmd.split()[0] - exes = self.test_class._exes - if self.cmd in exes: - self.stdout = StringIO(exes[self.cmd]) - else: - self.stdout = os.popen(cmd, 'r') - - class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase): @@ -34,29 +25,16 @@ class CygwinCCompilerTestCase(support.TempdirManager, from distutils import sysconfig self.old_get_config_h_filename = sysconfig.get_config_h_filename sysconfig.get_config_h_filename = self._get_config_h_filename - self.old_find_executable = cygwinccompiler.find_executable - cygwinccompiler.find_executable = self._find_executable - self._exes = {} - self.old_popen = cygwinccompiler.Popen - FakePopen.test_class = self - cygwinccompiler.Popen = FakePopen def tearDown(self): sys.version = self.version from distutils import sysconfig sysconfig.get_config_h_filename = self.old_get_config_h_filename - cygwinccompiler.find_executable = self.old_find_executable - cygwinccompiler.Popen = self.old_popen super(CygwinCCompilerTestCase, self).tearDown() def _get_config_h_filename(self): return self.python_h - def _find_executable(self, name): - if name in self._exes: - return name - return None - def test_check_config_h(self): # check_config_h looks for "GCC" in sys.version first @@ -80,40 +58,6 @@ class CygwinCCompilerTestCase(support.TempdirManager, self.write_file(self.python_h, 'xxx __GNUC__ xxx') self.assertEquals(check_config_h()[0], CONFIG_H_OK) - def test_get_versions(self): - - # get_versions calls distutils.spawn.find_executable on - # 'gcc', 'ld' and 'dllwrap' - self.assertEquals(get_versions(), (None, None, None)) - - # Let's fake we have 'gcc' and it returns '3.4.5' - self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF' - res = get_versions() - self.assertEquals(str(res[0]), '3.4.5') - - # and let's see what happens when the version - # doesn't match the regular expression - # (\d+\.\d+(\.\d+)*) - self._exes['gcc'] = 'very strange output' - res = get_versions() - self.assertEquals(res[0], None) - - # same thing for ld - self._exes['ld'] = 'GNU ld version 2.17.50 20060824' - res = get_versions() - self.assertEquals(str(res[1]), '2.17.50') - self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' - res = get_versions() - self.assertEquals(res[1], None) - - # and dllwrap - self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF' - res = get_versions() - self.assertEquals(str(res[2]), '2.17.50') - self._exes['dllwrap'] = 'Cheese Wrap' - res = get_versions() - self.assertEquals(res[2], None) - def test_get_msvcr(self): # none @@ -146,6 +90,21 @@ class CygwinCCompilerTestCase(support.TempdirManager, '[MSC v.1999 32 bits (Intel)]') self.assertRaises(ValueError, get_msvcr) + + def test_get_version_deprecated(self): + with check_warnings() as w: + warnings.simplefilter("always") + # make sure get_compiler_versions and get_versions + # returns the same thing + self.assertEquals(get_compiler_versions(), get_versions()) + # make sure using get_version() generated a warning + self.assertEquals(len(w.warnings), 1) + # make sure any usage of RE_VERSION will also + # generate a warning, but till works + version = RE_VERSION.search('1.2').group(1) + self.assertEquals(version, '1.2') + self.assertEquals(len(w.warnings), 2) + def test_suite(): return unittest.makeSuite(CygwinCCompilerTestCase) diff --git a/tests/test_emxccompiler.py b/tests/test_emxccompiler.py new file mode 100644 index 00000000..6e1deced --- /dev/null +++ b/tests/test_emxccompiler.py @@ -0,0 +1,33 @@ +"""Tests for distutils.emxccompiler.""" +import unittest +import sys +import os +import warnings + +from test.test_support import check_warnings +from test.test_support import captured_stdout + +from distutils.emxccompiler import get_versions +from distutils.util import get_compiler_versions +from distutils.tests import support + +class EmxCCompilerTestCase(support.TempdirManager, + unittest.TestCase): + + def test_get_version_deprecated(self): + with check_warnings() as w: + warnings.simplefilter("always") + # make sure get_compiler_versions and get_versions + # returns the same gcc + gcc, ld, dllwrap = get_compiler_versions() + emx_gcc, emx_ld = get_versions() + self.assertEquals(gcc, emx_gcc) + + # make sure using get_version() generated a warning + self.assertEquals(len(w.warnings), 1) + +def test_suite(): + return unittest.makeSuite(EmxCCompilerTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) diff --git a/tests/test_util.py b/tests/test_util.py index c0acf5f4..ffa92bd0 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -6,15 +6,33 @@ import os import sys import unittest from copy import copy +from StringIO import StringIO +import subprocess from distutils.errors import DistutilsPlatformError from distutils.util import (get_platform, convert_path, change_root, check_environ, split_quoted, strtobool, - rfc822_escape) -from distutils import util # used to patch _environ_checked + rfc822_escape, get_compiler_versions, + _find_exe_version, _MAC_OS_X_LD_VERSION) +from distutils import util from distutils.sysconfig import get_config_vars from distutils import sysconfig from distutils.tests import support +from distutils.version import LooseVersion + +class FakePopen(object): + test_class = None + def __init__(self, cmd, shell, stdout, stderr): + self.cmd = cmd.split()[0] + exes = self.test_class._exes + if self.cmd not in exes: + # we don't want to call the system, returning an empty + # output so it doesn't match + self.stdout = StringIO() + self.stderr = StringIO() + else: + self.stdout = StringIO(exes[self.cmd]) + self.stderr = StringIO() class UtilTestCase(support.EnvironGuard, unittest.TestCase): @@ -37,9 +55,16 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): else: self.uname = None self._uname = None - os.uname = self._get_uname + # patching POpen + self.old_find_executable = util.find_executable + util.find_executable = self._find_executable + self._exes = {} + self.old_popen = subprocess.Popen + FakePopen.test_class = self + subprocess.Popen = FakePopen + def tearDown(self): # getting back the environment os.name = self.name @@ -54,6 +79,8 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): else: del os.uname sysconfig._config_vars = copy(self._config_vars) + util.find_executable = self.old_find_executable + subprocess.Popen = self.old_popen super(UtilTestCase, self).tearDown() def _set_uname(self, uname): @@ -237,6 +264,70 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): 'header%(8s)s') % {'8s': '\n'+8*' '} self.assertEquals(res, wanted) + def test_find_exe_version(self): + # the ld version scheme under MAC OS is: + # ^@(#)PROGRAM:ld PROJECT:ld64-VERSION + # + # where VERSION is a 2-digit number for major + # revisions. For instance under Leopard, it's + # currently 77 + # + # Dots are used when branching is done. + # + # The SnowLeopard ld64 is currently 95.2.12 + + for output, version in (('@(#)PROGRAM:ld PROJECT:ld64-77', '77'), + ('@(#)PROGRAM:ld PROJECT:ld64-95.2.12', + '95.2.12')): + result = _MAC_OS_X_LD_VERSION.search(output) + self.assertEquals(result.group(1), version) + + def _find_executable(self, name): + if name in self._exes: + return name + return None + + def test_get_compiler_versions(self): + # get_versions calls distutils.spawn.find_executable on + # 'gcc', 'ld' and 'dllwrap' + self.assertEquals(get_compiler_versions(), (None, None, None)) + + # Let's fake we have 'gcc' and it returns '3.4.5' + self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF' + res = get_compiler_versions() + self.assertEquals(str(res[0]), '3.4.5') + + # and let's see what happens when the version + # doesn't match the regular expression + # (\d+\.\d+(\.\d+)*) + self._exes['gcc'] = 'very strange output' + res = get_compiler_versions() + self.assertEquals(res[0], None) + + # same thing for ld + if sys.platform != 'darwin': + self._exes['ld'] = 'GNU ld version 2.17.50 20060824' + res = get_compiler_versions() + self.assertEquals(str(res[1]), '2.17.50') + self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_compiler_versions() + self.assertEquals(res[1], None) + else: + self._exes['ld'] = 'GNU ld version 2.17.50 20060824' + res = get_compiler_versions() + self.assertEquals(res[1], None) + self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_compiler_versions() + self.assertEquals(str(res[1]), '77') + + # and dllwrap + self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF' + res = get_compiler_versions() + self.assertEquals(str(res[2]), '2.17.50') + self._exes['dllwrap'] = 'Cheese Wrap' + res = get_compiler_versions() + self.assertEquals(res[2], None) + def test_suite(): return unittest.makeSuite(UtilTestCase) diff --git a/util.py b/util.py index 5bbbf225..459c3646 100644 --- a/util.py +++ b/util.py @@ -7,10 +7,12 @@ one of the other *util.py modules. __revision__ = "$Id$" import sys, os, string, re + from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer -from distutils.spawn import spawn +from distutils.spawn import spawn, find_executable from distutils import log +from distutils.version import LooseVersion def get_platform(): """Return a string that identifies the current platform. @@ -539,3 +541,53 @@ def rfc822_escape(header): lines = [x.strip() for x in header.split('\n')] sep = '\n' + 8*' ' return sep.join(lines) + +_RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') +_MAC_OS_X_LD_VERSION = re.compile('^@\(#\)PROGRAM:ld PROJECT:ld64-((\d+)(\.\d+)*)') + +def _find_ld_version(): + """Finds the ld version. The version scheme differs under Mac OSX.""" + if sys.platform == 'darwin': + return _find_exe_version('ld -v', _MAC_OS_X_LD_VERSION) + else: + return _find_exe_version('ld -v') + +def _find_exe_version(cmd, pattern=_RE_VERSION): + """Find the version of an executable by running `cmd` in the shell. + + `pattern` is a compiled regular expression. If not provided, default + to _RE_VERSION. If the command is not found, or the output does not + match the mattern, returns None. + """ + from subprocess import Popen, PIPE + executable = cmd.split()[0] + if find_executable(executable) is None: + return None + pipe = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) + try: + stdout, stderr = pipe.stdout.read(), pipe.stderr.read() + finally: + pipe.stdout.close() + pipe.stderr.close() + # some commands like ld under MacOS X, will give the + # output in the stderr, rather than stdout. + if stdout != '': + out_string = stdout + else: + out_string = stderr + + result = pattern.search(out_string) + if result is None: + return None + return LooseVersion(result.group(1)) + +def get_compiler_versions(): + """Returns a tuple providing the versions of gcc, ld and dllwrap + + For each command, if a command is not found, None is returned. + Otherwise a LooseVersion instance is returned. + """ + gcc = _find_exe_version('gcc -dumpversion') + ld = _find_ld_version() + dllwrap = _find_exe_version('dllwrap --version') + return gcc, ld, dllwrap -- cgit v1.2.1 From 36b50be6bf1f352ecd22b76b1d8f976cb2a8d1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 16 Jul 2009 16:18:19 +0000 Subject: Merged revisions 74024 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74024 | tarek.ziade | 2009-07-16 17:35:45 +0200 (Thu, 16 Jul 2009) | 1 line #6466 refactored distutils duplicate get_versions() functions (used to get gcc/ld/dllwrap versions) ........ --- cygwinccompiler.py | 45 +++++++++----------- emxccompiler.py | 33 ++++++--------- tests/test_cygwinccompiler.py | 83 ++++++++++-------------------------- tests/test_emxccompiler.py | 33 +++++++++++++++ tests/test_util.py | 97 +++++++++++++++++++++++++++++++++++++++++-- util.py | 54 +++++++++++++++++++++++- 6 files changed, 232 insertions(+), 113 deletions(-) create mode 100644 tests/test_emxccompiler.py diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 85043718..d9f4a43d 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -50,16 +50,15 @@ __revision__ = "$Id$" import os import sys import copy -from subprocess import Popen, PIPE import re +from warnings import warn from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError from distutils import log -from distutils.version import LooseVersion -from distutils.spawn import find_executable +from distutils.util import get_compiler_versions def get_msvcr(): """Include the appropriate MSVC runtime library if Python was built @@ -110,7 +109,7 @@ class CygwinCCompiler(UnixCCompiler): % details) self.gcc_version, self.ld_version, self.dllwrap_version = \ - get_versions() + get_compiler_versions() self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % (self.gcc_version, self.ld_version, @@ -359,33 +358,27 @@ def check_config_h(): return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) -RE_VERSION = re.compile(b'(\d+\.\d+(\.\d+)*)') +class _Deprecated_SRE_Pattern(object): + def __init__(self, pattern): + self.pattern = pattern -def _find_exe_version(cmd): - """Find the version of an executable by running `cmd` in the shell. + def __getattr__(self, name): + if name in ('findall', 'finditer', 'match', 'scanner', 'search', + 'split', 'sub', 'subn'): + warn("'distutils.cygwinccompiler.RE_VERSION' is deprecated " + "and will be removed in the next version", DeprecationWarning) + return getattr(self.pattern, name) - If the command is not found, or the output does not match - `RE_VERSION`, returns None. - """ - executable = cmd.split()[0] - if find_executable(executable) is None: - return None - out = Popen(cmd, shell=True, stdout=PIPE).stdout - try: - out_string = out.read() - finally: - out.close() - result = RE_VERSION.search(out_string) - if result is None: - return None - # LooseVersion works with strings - # so we need to decode our bytes - return LooseVersion(result.group(1).decode()) + +RE_VERSION = _Deprecated_SRE_Pattern(re.compile('(\d+\.\d+(\.\d+)*)')) def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. If not possible it returns None for it. """ - commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] - return tuple([_find_exe_version(cmd) for cmd in commands]) + warn("'distutils.cygwinccompiler.get_versions' is deprecated " + "use 'distutils.util.get_compiler_versions' instead", + DeprecationWarning) + + return get_compiler_versions() diff --git a/emxccompiler.py b/emxccompiler.py index 62a4c5b4..50634d6c 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -21,12 +21,15 @@ handles the EMX port of the GNU C compiler to OS/2. __revision__ = "$Id$" -import os,sys,copy +import os, sys, copy +from warnings import warn + from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError from distutils import log +from distutils.util import get_compiler_versions class EMXCCompiler (UnixCCompiler): @@ -55,8 +58,8 @@ class EMXCCompiler (UnixCCompiler): ("Reason: %s." % details) + "Compiling may fail because of undefined preprocessor macros.") - (self.gcc_version, self.ld_version) = \ - get_versions() + gcc_version, ld_version, dllwrap_version = get_compiler_versions() + self.gcc_version, self.ld_version = gcc_version, ld_version self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" % (self.gcc_version, self.ld_version) ) @@ -291,23 +294,11 @@ def get_versions(): """ Try to find out the versions of gcc and ld. If not possible it returns None for it. """ - from distutils.version import StrictVersion - from distutils.spawn import find_executable - import re - - gcc_exe = find_executable('gcc') - if gcc_exe: - out = os.popen(gcc_exe + ' -dumpversion','r') - out_string = out.read() - out.close() - result = re.search('(\d+\.\d+\.\d+)', out_string, re.ASCII) - if result: - gcc_version = StrictVersion(result.group(1)) - else: - gcc_version = None - else: - gcc_version = None + warn("'distutils.emxccompiler.get_versions' is deprecated " + "use 'distutils.util.get_compiler_versions' instead", + DeprecationWarning) + # EMX ld has no way of reporting version number, and we use GCC # anyway - so we can link OMF DLLs - ld_version = None - return (gcc_version, ld_version) + gcc_version, ld_version, dllwrap_version = get_compiler_versions() + return gcc_version, None diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index a57694d4..98f0f08e 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -2,29 +2,20 @@ import unittest import sys import os -from io import BytesIO import subprocess +import warnings + +from test.support import check_warnings +from test.support import captured_stdout from distutils import cygwinccompiler from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_UNCERTAIN, get_versions, - get_msvcr) + get_msvcr, RE_VERSION) +from distutils.util import get_compiler_versions from distutils.tests import support -class FakePopen(object): - test_class = None - - def __init__(self, cmd, shell, stdout): - self.cmd = cmd.split()[0] - exes = self.test_class._exes - if self.cmd in exes: - # issue #6438 in Python 3.x, Popen returns bytes - self.stdout = BytesIO(exes[self.cmd]) - else: - self.stdout = os.popen(cmd, 'r') - - class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase): @@ -35,29 +26,16 @@ class CygwinCCompilerTestCase(support.TempdirManager, from distutils import sysconfig self.old_get_config_h_filename = sysconfig.get_config_h_filename sysconfig.get_config_h_filename = self._get_config_h_filename - self.old_find_executable = cygwinccompiler.find_executable - cygwinccompiler.find_executable = self._find_executable - self._exes = {} - self.old_popen = cygwinccompiler.Popen - FakePopen.test_class = self - cygwinccompiler.Popen = FakePopen def tearDown(self): sys.version = self.version from distutils import sysconfig sysconfig.get_config_h_filename = self.old_get_config_h_filename - cygwinccompiler.find_executable = self.old_find_executable - cygwinccompiler.Popen = self.old_popen super(CygwinCCompilerTestCase, self).tearDown() def _get_config_h_filename(self): return self.python_h - def _find_executable(self, name): - if name in self._exes: - return name - return None - def test_check_config_h(self): # check_config_h looks for "GCC" in sys.version first @@ -81,40 +59,6 @@ class CygwinCCompilerTestCase(support.TempdirManager, self.write_file(self.python_h, 'xxx __GNUC__ xxx') self.assertEquals(check_config_h()[0], CONFIG_H_OK) - def test_get_versions(self): - - # get_versions calls distutils.spawn.find_executable on - # 'gcc', 'ld' and 'dllwrap' - self.assertEquals(get_versions(), (None, None, None)) - - # Let's fake we have 'gcc' and it returns '3.4.5' - self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' - res = get_versions() - self.assertEquals(str(res[0]), '3.4.5') - - # and let's see what happens when the version - # doesn't match the regular expression - # (\d+\.\d+(\.\d+)*) - self._exes['gcc'] = b'very strange output' - res = get_versions() - self.assertEquals(res[0], None) - - # same thing for ld - self._exes['ld'] = b'GNU ld version 2.17.50 20060824' - res = get_versions() - self.assertEquals(str(res[1]), '2.17.50') - self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' - res = get_versions() - self.assertEquals(res[1], None) - - # and dllwrap - self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' - res = get_versions() - self.assertEquals(str(res[2]), '2.17.50') - self._exes['dllwrap'] = b'Cheese Wrap' - res = get_versions() - self.assertEquals(res[2], None) - def test_get_msvcr(self): # none @@ -147,6 +91,21 @@ class CygwinCCompilerTestCase(support.TempdirManager, '[MSC v.1999 32 bits (Intel)]') self.assertRaises(ValueError, get_msvcr) + + def test_get_version_deprecated(self): + with check_warnings() as w: + warnings.simplefilter("always") + # make sure get_compiler_versions and get_versions + # returns the same thing + self.assertEquals(get_compiler_versions(), get_versions()) + # make sure using get_version() generated a warning + self.assertEquals(len(w.warnings), 1) + # make sure any usage of RE_VERSION will also + # generate a warning, but till works + version = RE_VERSION.search('1.2').group(1) + self.assertEquals(version, '1.2') + self.assertEquals(len(w.warnings), 2) + def test_suite(): return unittest.makeSuite(CygwinCCompilerTestCase) diff --git a/tests/test_emxccompiler.py b/tests/test_emxccompiler.py new file mode 100644 index 00000000..2176d641 --- /dev/null +++ b/tests/test_emxccompiler.py @@ -0,0 +1,33 @@ +"""Tests for distutils.emxccompiler.""" +import unittest +import sys +import os +import warnings + +from test.support import check_warnings +from test.support import captured_stdout + +from distutils.emxccompiler import get_versions +from distutils.util import get_compiler_versions +from distutils.tests import support + +class EmxCCompilerTestCase(support.TempdirManager, + unittest.TestCase): + + def test_get_version_deprecated(self): + with check_warnings() as w: + warnings.simplefilter("always") + # make sure get_compiler_versions and get_versions + # returns the same gcc + gcc, ld, dllwrap = get_compiler_versions() + emx_gcc, emx_ld = get_versions() + self.assertEquals(gcc, emx_gcc) + + # make sure using get_version() generated a warning + self.assertEquals(len(w.warnings), 1) + +def test_suite(): + return unittest.makeSuite(EmxCCompilerTestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) diff --git a/tests/test_util.py b/tests/test_util.py index c0acf5f4..e9065ff5 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -6,15 +6,33 @@ import os import sys import unittest from copy import copy +from io import BytesIO +import subprocess from distutils.errors import DistutilsPlatformError from distutils.util import (get_platform, convert_path, change_root, check_environ, split_quoted, strtobool, - rfc822_escape) -from distutils import util # used to patch _environ_checked + rfc822_escape, get_compiler_versions, + _find_exe_version, _MAC_OS_X_LD_VERSION) +from distutils import util from distutils.sysconfig import get_config_vars from distutils import sysconfig from distutils.tests import support +from distutils.version import LooseVersion + +class FakePopen(object): + test_class = None + def __init__(self, cmd, shell, stdout, stderr): + self.cmd = cmd.split()[0] + exes = self.test_class._exes + if self.cmd not in exes: + # we don't want to call the system, returning an empty + # output so it doesn't match + self.stdout = BytesIO() + self.stderr = BytesIO() + else: + self.stdout = BytesIO(exes[self.cmd]) + self.stderr = BytesIO() class UtilTestCase(support.EnvironGuard, unittest.TestCase): @@ -37,9 +55,16 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): else: self.uname = None self._uname = None - os.uname = self._get_uname + # patching POpen + self.old_find_executable = util.find_executable + util.find_executable = self._find_executable + self._exes = {} + self.old_popen = subprocess.Popen + FakePopen.test_class = self + subprocess.Popen = FakePopen + def tearDown(self): # getting back the environment os.name = self.name @@ -54,6 +79,8 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): else: del os.uname sysconfig._config_vars = copy(self._config_vars) + util.find_executable = self.old_find_executable + subprocess.Popen = self.old_popen super(UtilTestCase, self).tearDown() def _set_uname(self, uname): @@ -237,6 +264,70 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): 'header%(8s)s') % {'8s': '\n'+8*' '} self.assertEquals(res, wanted) + def test_find_exe_version(self): + # the ld version scheme under MAC OS is: + # ^@(#)PROGRAM:ld PROJECT:ld64-VERSION + # + # where VERSION is a 2-digit number for major + # revisions. For instance under Leopard, it's + # currently 77 + # + # Dots are used when branching is done. + # + # The SnowLeopard ld64 is currently 95.2.12 + + for output, version in ((b'@(#)PROGRAM:ld PROJECT:ld64-77', '77'), + (b'@(#)PROGRAM:ld PROJECT:ld64-95.2.12', + '95.2.12')): + result = _MAC_OS_X_LD_VERSION.search(output) + self.assertEquals(result.group(1).decode(), version) + + def _find_executable(self, name): + if name in self._exes: + return name + return None + + def test_get_compiler_versions(self): + # get_versions calls distutils.spawn.find_executable on + # 'gcc', 'ld' and 'dllwrap' + self.assertEquals(get_compiler_versions(), (None, None, None)) + + # Let's fake we have 'gcc' and it returns '3.4.5' + self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' + res = get_compiler_versions() + self.assertEquals(str(res[0]), '3.4.5') + + # and let's see what happens when the version + # doesn't match the regular expression + # (\d+\.\d+(\.\d+)*) + self._exes['gcc'] = b'very strange output' + res = get_compiler_versions() + self.assertEquals(res[0], None) + + # same thing for ld + if sys.platform != 'darwin': + self._exes['ld'] = b'GNU ld version 2.17.50 20060824' + res = get_compiler_versions() + self.assertEquals(str(res[1]), '2.17.50') + self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_compiler_versions() + self.assertEquals(res[1], None) + else: + self._exes['ld'] = b'GNU ld version 2.17.50 20060824' + res = get_compiler_versions() + self.assertEquals(res[1], None) + self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_compiler_versions() + self.assertEquals(str(res[1]), '77') + + # and dllwrap + self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' + res = get_compiler_versions() + self.assertEquals(str(res[2]), '2.17.50') + self._exes['dllwrap'] = b'Cheese Wrap' + res = get_compiler_versions() + self.assertEquals(res[2], None) + def test_suite(): return unittest.makeSuite(UtilTestCase) diff --git a/util.py b/util.py index ad7ae08d..0c88b819 100644 --- a/util.py +++ b/util.py @@ -7,10 +7,12 @@ one of the other *util.py modules. __revision__ = "$Id$" import sys, os, string, re + from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer -from distutils.spawn import spawn +from distutils.spawn import spawn, find_executable from distutils import log +from distutils.version import LooseVersion def get_platform(): """Return a string that identifies the current platform. @@ -539,6 +541,56 @@ def rfc822_escape(header): sep = '\n' + 8*' ' return sep.join(lines) +_RE_VERSION = re.compile(b'(\d+\.\d+(\.\d+)*)') +_MAC_OS_X_LD_VERSION = re.compile(b'^@\(#\)PROGRAM:ld PROJECT:ld64-((\d+)(\.\d+)*)') + +def _find_ld_version(): + """Finds the ld version. The version scheme differs under Mac OSX.""" + if sys.platform == 'darwin': + return _find_exe_version('ld -v', _MAC_OS_X_LD_VERSION) + else: + return _find_exe_version('ld -v') + +def _find_exe_version(cmd, pattern=_RE_VERSION): + """Find the version of an executable by running `cmd` in the shell. + + `pattern` is a compiled regular expression. If not provided, default + to _RE_VERSION. If the command is not found, or the output does not + match the mattern, returns None. + """ + from subprocess import Popen, PIPE + executable = cmd.split()[0] + if find_executable(executable) is None: + return None + pipe = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) + try: + stdout, stderr = pipe.stdout.read(), pipe.stderr.read() + finally: + pipe.stdout.close() + pipe.stderr.close() + # some commands like ld under MacOS X, will give the + # output in the stderr, rather than stdout. + if stdout != b'': + out_string = stdout + else: + out_string = stderr + + result = pattern.search(out_string) + if result is None: + return None + return LooseVersion(result.group(1).decode()) + +def get_compiler_versions(): + """Returns a tuple providing the versions of gcc, ld and dllwrap + + For each command, if a command is not found, None is returned. + Otherwise a LooseVersion instance is returned. + """ + gcc = _find_exe_version('gcc -dumpversion') + ld = _find_ld_version() + dllwrap = _find_exe_version('dllwrap --version') + return gcc, ld, dllwrap + # 2to3 support def run_2to3(files, fixer_names=None, options=None, explicit=None): -- cgit v1.2.1 From fcfd0cb5014a3b4f0bda28a8210a1286e8e63f71 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 19 Jul 2009 21:52:02 +0000 Subject: skip test when distutils is not made for py3k --- tests/test_register.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_register.py b/tests/test_register.py index c03ad101..acda1b1a 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -202,10 +202,10 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertRaises(DistutilsSetupError, cmd.run) # we don't test the reSt feature if docutils - # is not installed + # is not installed or we our on py3k try: import docutils - except ImportError: + except Exception: return # metadata are OK but long_description is broken -- cgit v1.2.1 From 1d3e8e21722d7354f77ebd0c8b19855e7a8045c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 22 Jul 2009 08:55:19 +0000 Subject: Issue #6545: Removed assert statements in distutils.Extension, so the behavior is similar when used with -O --- extension.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/extension.py b/extension.py index 53ca8fd0..6af18108 100644 --- a/extension.py +++ b/extension.py @@ -103,10 +103,11 @@ class Extension: optional=None, **kw # To catch unknown keywords ): - assert isinstance(name, str), "'name' must be a string" - assert (isinstance(sources, list) and - all(isinstance(v, str) for v in sources)), \ - "'sources' must be a list of strings" + if not isinstance(name, str): + raise AssertionError("'name' must be a string") + if not (isinstance(sources, list) and + all(isinstance(v, str) for v in sources)): + raise AssertionError("'sources' must be a list of strings") self.name = name self.sources = sources -- cgit v1.2.1 From 7d70ca7551824bdf4950932bd5d68e8a982cf1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 22 Jul 2009 08:57:28 +0000 Subject: Merged revisions 74163 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74163 | tarek.ziade | 2009-07-22 10:55:19 +0200 (Wed, 22 Jul 2009) | 1 line Issue #6545: Removed assert statements in distutils.Extension, so the behavior is similar when used with -O ........ --- extension.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/extension.py b/extension.py index 16d2bef6..5c07bdae 100644 --- a/extension.py +++ b/extension.py @@ -103,10 +103,11 @@ class Extension: optional=None, **kw # To catch unknown keywords ): - assert isinstance(name, str), "'name' must be a string" - assert (isinstance(sources, list) and - all(isinstance(v, str) for v in sources)), \ - "'sources' must be a list of strings" + if not isinstance(name, str): + raise AssertionError("'name' must be a string") + if not (isinstance(sources, list) and + all(isinstance(v, str) for v in sources)): + raise AssertionError("'sources' must be a list of strings") self.name = name self.sources = sources -- cgit v1.2.1 From 576fc43ebe6bd3b6f7707d8c8e9ba92cc9ed112b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 22 Jul 2009 09:03:01 +0000 Subject: Merged revisions 74164 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r74164 | tarek.ziade | 2009-07-22 10:57:28 +0200 (Wed, 22 Jul 2009) | 9 lines Merged revisions 74163 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74163 | tarek.ziade | 2009-07-22 10:55:19 +0200 (Wed, 22 Jul 2009) | 1 line Issue #6545: Removed assert statements in distutils.Extension, so the behavior is similar when used with -O ........ ................ --- extension.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/extension.py b/extension.py index 16d2bef6..5c07bdae 100644 --- a/extension.py +++ b/extension.py @@ -103,10 +103,11 @@ class Extension: optional=None, **kw # To catch unknown keywords ): - assert isinstance(name, str), "'name' must be a string" - assert (isinstance(sources, list) and - all(isinstance(v, str) for v in sources)), \ - "'sources' must be a list of strings" + if not isinstance(name, str): + raise AssertionError("'name' must be a string") + if not (isinstance(sources, list) and + all(isinstance(v, str) for v in sources)): + raise AssertionError("'sources' must be a list of strings") self.name = name self.sources = sources -- cgit v1.2.1 From 8c7f12ea567dc59e1031dbffdda12247dfce0b9e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 13 Aug 2009 08:51:18 +0000 Subject: Merged revisions 73715 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r73715 | benjamin.peterson | 2009-07-01 01:06:06 +0200 (Mi, 01 Jul 2009) | 1 line convert old fail* assertions to assert* ........ --- tests/test_archive_util.py | 16 ++++++++-------- tests/test_bdist_rpm.py | 4 ++-- tests/test_bdist_wininst.py | 2 +- tests/test_build_clib.py | 2 +- tests/test_build_ext.py | 30 +++++++++++++++--------------- tests/test_build_py.py | 6 +++--- tests/test_build_scripts.py | 10 +++++----- tests/test_clean.py | 2 +- tests/test_cmd.py | 2 +- tests/test_config.py | 4 ++-- tests/test_config_cmd.py | 4 ++-- tests/test_dist.py | 42 +++++++++++++++++++++--------------------- tests/test_install.py | 16 ++++++++-------- tests/test_install_data.py | 12 ++++++------ tests/test_install_lib.py | 6 +++--- tests/test_install_scripts.py | 14 +++++++------- tests/test_msvc9compiler.py | 4 ++-- tests/test_register.py | 16 ++++++++-------- tests/test_sysconfig.py | 12 ++++++------ tests/test_upload.py | 2 +- tests/test_util.py | 4 ++-- 21 files changed, 105 insertions(+), 105 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 663b3353..d88e0b35 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -47,7 +47,7 @@ class ArchiveUtilTestCase(support.TempdirManager, # check if the compressed tarball was created tarball = base_name + '.tar.gz' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) # trying an uncompressed one base_name = os.path.join(tmpdir2, 'archive') @@ -58,7 +58,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) def _tarinfo(self, path): tar = tarfile.open(path) @@ -96,7 +96,7 @@ class ArchiveUtilTestCase(support.TempdirManager, # check if the compressed tarball was created tarball = base_name + '.tar.gz' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) # now create another tarball using `tar` tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') @@ -110,7 +110,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) - self.assert_(os.path.exists(tarball2)) + self.assertTrue(os.path.exists(tarball2)) # let's compare both tarballs self.assertEquals(self._tarinfo(tarball), self._tarinfo(tarball2)) @@ -123,7 +123,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) # now for a dry_run base_name = os.path.join(tmpdir2, 'archive') @@ -134,7 +134,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) @unittest.skipUnless(find_executable('compress'), 'The compress program is required') @@ -151,7 +151,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) tarball = base_name + '.tar.Z' - self.assert_(os.path.exists(tarball)) + self.assertTrue(os.path.exists(tarball)) self.assertEquals(len(w.warnings), 1) # same test with dry_run @@ -165,7 +165,7 @@ class ArchiveUtilTestCase(support.TempdirManager, dry_run=True) finally: os.chdir(old_dir) - self.assert_(not os.path.exists(tarball)) + self.assertTrue(not os.path.exists(tarball)) self.assertEquals(len(w.warnings), 1) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 2d84007f..c2715675 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -74,7 +74,7 @@ class BuildRpmTestCase(support.TempdirManager, cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) def test_no_optimize_flag(self): @@ -114,7 +114,7 @@ class BuildRpmTestCase(support.TempdirManager, cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assert_('foo-0.1-1.noarch.rpm' in dist_created) + self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) def test_suite(): diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index f2cb4fde..9b1ba6d1 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -21,7 +21,7 @@ class BuildWinInstTestCase(support.TempdirManager, # and make sure it finds it and returns its content # no matter what platform we have exe_file = cmd.get_exe_bytes() - self.assert_(len(exe_file) > 10) + self.assertTrue(len(exe_file) > 10) def test_suite(): return unittest.makeSuite(BuildWinInstTestCase) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 47d85cd8..536cd673 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -135,7 +135,7 @@ class BuildCLibTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assert_('libfoo.a' in os.listdir(build_temp)) + self.assertTrue('libfoo.a' in os.listdir(build_temp)) def test_suite(): return unittest.makeSuite(BuildCLibTestCase) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 9861ee29..1221fd43 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -74,15 +74,15 @@ class BuildExtTestCase(TempdirManager, import xx for attr in ('error', 'foo', 'new', 'roj'): - self.assert_(hasattr(xx, attr)) + self.assertTrue(hasattr(xx, attr)) self.assertEquals(xx.foo(2, 5), 7) self.assertEquals(xx.foo(13,15), 28) self.assertEquals(xx.new().demo(), None) doc = 'This is a template module just for instruction.' self.assertEquals(xx.__doc__, doc) - self.assert_(isinstance(xx.Null(), xx.Null)) - self.assert_(isinstance(xx.Str(), xx.Str)) + self.assertTrue(isinstance(xx.Null(), xx.Null)) + self.assertTrue(isinstance(xx.Str(), xx.Str)) def tearDown(self): # Get everything back to normal @@ -114,7 +114,7 @@ class BuildExtTestCase(TempdirManager, _config_vars['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris - self.assert_(len(cmd.library_dirs) > 0) + self.assertTrue(len(cmd.library_dirs) > 0) def test_user_site(self): # site.USER_SITE was introduced in 2.6 @@ -128,7 +128,7 @@ class BuildExtTestCase(TempdirManager, # making sure the user option is there options = [name for name, short, lable in cmd.user_options] - self.assert_('user' in options) + self.assertTrue('user' in options) # setting a value cmd.user = 1 @@ -144,9 +144,9 @@ class BuildExtTestCase(TempdirManager, # see if include_dirs and library_dirs # were set - self.assert_(lib in cmd.library_dirs) - self.assert_(lib in cmd.rpath) - self.assert_(incl in cmd.include_dirs) + self.assertTrue(lib in cmd.library_dirs) + self.assertTrue(lib in cmd.rpath) + self.assertTrue(incl in cmd.include_dirs) def test_optional_extension(self): @@ -175,10 +175,10 @@ class BuildExtTestCase(TempdirManager, from distutils import sysconfig py_include = sysconfig.get_python_inc() - self.assert_(py_include in cmd.include_dirs) + self.assertTrue(py_include in cmd.include_dirs) plat_py_include = sysconfig.get_python_inc(plat_specific=1) - self.assert_(plat_py_include in cmd.include_dirs) + self.assertTrue(plat_py_include in cmd.include_dirs) # make sure cmd.libraries is turned into a list # if it's a string @@ -192,7 +192,7 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.library_dirs = 'my_lib_dir' cmd.finalize_options() - self.assert_('my_lib_dir' in cmd.library_dirs) + self.assertTrue('my_lib_dir' in cmd.library_dirs) # make sure rpath is turned into a list # if it's a list of os.pathsep's paths @@ -257,13 +257,13 @@ class BuildExtTestCase(TempdirManager, 'some': 'bar'})] cmd.check_extensions_list(exts) ext = exts[0] - self.assert_(isinstance(ext, Extension)) + self.assertTrue(isinstance(ext, Extension)) # check_extensions_list adds in ext the values passed # when they are in ('include_dirs', 'library_dirs', 'libraries' # 'extra_objects', 'extra_compile_args', 'extra_link_args') self.assertEquals(ext.libraries, 'foo') - self.assert_(not hasattr(ext, 'some')) + self.assertTrue(not hasattr(ext, 'some')) # 'macros' element of build info dict must be 1- or 2-tuple exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', @@ -321,7 +321,7 @@ class BuildExtTestCase(TempdirManager, so_file = cmd.get_outputs()[0] finally: os.chdir(old_wd) - self.assert_(os.path.exists(so_file)) + self.assertTrue(os.path.exists(so_file)) self.assertEquals(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) @@ -330,7 +330,7 @@ class BuildExtTestCase(TempdirManager, cmd.inplace = 0 cmd.run() so_file = cmd.get_outputs()[0] - self.assert_(os.path.exists(so_file)) + self.assertTrue(os.path.exists(so_file)) self.assertEquals(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 75b6624f..8ad3bbc4 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -52,9 +52,9 @@ class BuildPyTestCase(support.TempdirManager, self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) - self.assert_("__init__.py" in files) - self.assert_("__init__.pyc" in files) - self.assert_("README.txt" in files) + self.assertTrue("__init__.py" in files) + self.assertTrue("__init__.pyc" in files) + self.assertTrue("README.txt" in files) def test_empty_package_dir (self): # See SF 1668596/1720897. diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index b55eb585..b1d2d079 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -16,12 +16,12 @@ class BuildScriptsTestCase(support.TempdirManager, def test_default_settings(self): cmd = self.get_build_scripts_cmd("/foo/bar", []) - self.assert_(not cmd.force) - self.assert_(cmd.build_dir is None) + self.assertTrue(not cmd.force) + self.assertTrue(cmd.build_dir is None) cmd.finalize_options() - self.assert_(cmd.force) + self.assertTrue(cmd.force) self.assertEqual(cmd.build_dir, "/foo/bar") def test_build(self): @@ -37,7 +37,7 @@ class BuildScriptsTestCase(support.TempdirManager, built = os.listdir(target) for name in expected: - self.assert_(name in built) + self.assertTrue(name in built) def get_build_scripts_cmd(self, target, scripts): import sys @@ -100,7 +100,7 @@ class BuildScriptsTestCase(support.TempdirManager, built = os.listdir(target) for name in expected: - self.assert_(name in built) + self.assertTrue(name in built) def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) diff --git a/tests/test_clean.py b/tests/test_clean.py index 30260327..dbc4ee2a 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -35,7 +35,7 @@ class cleanTestCase(support.TempdirManager, # make sure the files where removed for name, path in dirs: - self.assert_(not os.path.exists(path), + self.assertTrue(not os.path.exists(path), '%s was not removed' % path) # let's run the command again (should spit warnings but suceed) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 8f2b36fb..d6438b5e 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -70,7 +70,7 @@ class CommandTestCase(unittest.TestCase): cmd.option2 = None cmd.ensure_string('option2', 'xxx') - self.assert_(hasattr(cmd, 'option2')) + self.assertTrue(hasattr(cmd, 'option2')) cmd.option3 = 1 self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') diff --git a/tests/test_config.py b/tests/test_config.py index 0f97cf7a..879689d2 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -102,9 +102,9 @@ class PyPIRCCommandTestCase(support.TempdirManager, def test_server_empty_registration(self): cmd = self._cmd(self.dist) rc = cmd._get_rc_file() - self.assert_(not os.path.exists(rc)) + self.assertTrue(not os.path.exists(rc)) cmd._store_pypirc('tarek', 'xxx') - self.assert_(os.path.exists(rc)) + self.assertTrue(os.path.exists(rc)) content = open(rc).read() self.assertEquals(content, WANTED) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index bacf13a2..ef2e7bc3 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -73,14 +73,14 @@ class ConfigTestCase(support.LoggingSilencer, self.write_file(f2, 'xxx') for f in (f1, f2): - self.assert_(os.path.exists(f)) + self.assertTrue(os.path.exists(f)) pkg_dir, dist = self.create_dist() cmd = config(dist) cmd._clean(f1, f2) for f in (f1, f2): - self.assert_(not os.path.exists(f)) + self.assertTrue(not os.path.exists(f)) def test_suite(): return unittest.makeSuite(ConfigTestCase) diff --git a/tests/test_dist.py b/tests/test_dist.py index 40760390..9f795f43 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -72,7 +72,7 @@ class DistributionTestCase(support.LoggingSilencer, self.assertEqual(d.get_command_packages(), ["distutils.command", "foo.bar", "distutils.tests"]) cmd = d.get_command_obj("test_dist") - self.assert_(isinstance(cmd, test_dist)) + self.assertTrue(isinstance(cmd, test_dist)) self.assertEqual(cmd.sample_option, "sometext") def test_command_packages_configfile(self): @@ -157,10 +157,10 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, "version": "1.0"} dist = Distribution(attrs) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.0" in meta) - self.assert_("provides:" not in meta.lower()) - self.assert_("requires:" not in meta.lower()) - self.assert_("obsoletes:" not in meta.lower()) + self.assertTrue("Metadata-Version: 1.0" in meta) + self.assertTrue("provides:" not in meta.lower()) + self.assertTrue("requires:" not in meta.lower()) + self.assertTrue("obsoletes:" not in meta.lower()) def test_provides(self): attrs = {"name": "package", @@ -172,9 +172,9 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_provides(), ["package", "package.sub"]) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.1" in meta) - self.assert_("requires:" not in meta.lower()) - self.assert_("obsoletes:" not in meta.lower()) + self.assertTrue("Metadata-Version: 1.1" in meta) + self.assertTrue("requires:" not in meta.lower()) + self.assertTrue("obsoletes:" not in meta.lower()) def test_provides_illegal(self): self.assertRaises(ValueError, Distribution, @@ -192,11 +192,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_requires(), ["other", "another (==1.0)"]) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.1" in meta) - self.assert_("provides:" not in meta.lower()) - self.assert_("Requires: other" in meta) - self.assert_("Requires: another (==1.0)" in meta) - self.assert_("obsoletes:" not in meta.lower()) + self.assertTrue("Metadata-Version: 1.1" in meta) + self.assertTrue("provides:" not in meta.lower()) + self.assertTrue("Requires: other" in meta) + self.assertTrue("Requires: another (==1.0)" in meta) + self.assertTrue("obsoletes:" not in meta.lower()) def test_requires_illegal(self): self.assertRaises(ValueError, Distribution, @@ -214,11 +214,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_obsoletes(), ["other", "another (<1.0)"]) meta = self.format_metadata(dist) - self.assert_("Metadata-Version: 1.1" in meta) - self.assert_("provides:" not in meta.lower()) - self.assert_("requires:" not in meta.lower()) - self.assert_("Obsoletes: other" in meta) - self.assert_("Obsoletes: another (<1.0)" in meta) + self.assertTrue("Metadata-Version: 1.1" in meta) + self.assertTrue("provides:" not in meta.lower()) + self.assertTrue("requires:" not in meta.lower()) + self.assertTrue("Obsoletes: other" in meta) + self.assertTrue("Obsoletes: another (<1.0)" in meta) def test_obsoletes_illegal(self): self.assertRaises(ValueError, Distribution, @@ -252,14 +252,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, if sys.platform in ('linux', 'darwin'): self.environ['HOME'] = temp_dir files = dist.find_config_files() - self.assert_(user_filename in files) + self.assertTrue(user_filename in files) # win32-style if sys.platform == 'win32': # home drive should be found self.environ['HOME'] = temp_dir files = dist.find_config_files() - self.assert_(user_filename in files, + self.assertTrue(user_filename in files, '%r not found in %r' % (user_filename, files)) finally: os.remove(user_filename) @@ -285,7 +285,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, output = [line for line in s.getvalue().split('\n') if line.strip() != ''] - self.assert_(len(output) > 0) + self.assertTrue(len(output) > 0) def test_suite(): suite = unittest.TestSuite() diff --git a/tests/test_install.py b/tests/test_install.py index 8d7e9722..d0ad5ce1 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -88,7 +88,7 @@ class InstallTestCase(support.TempdirManager, def _test_user_site(self): for key in ('nt_user', 'unix_user', 'os2_home'): - self.assert_(key in INSTALL_SCHEMES) + self.assertTrue(key in INSTALL_SCHEMES) dist = Distribution({'name': 'xx'}) cmd = install(dist) @@ -96,24 +96,24 @@ class InstallTestCase(support.TempdirManager, # making sure the user option is there options = [name for name, short, lable in cmd.user_options] - self.assert_('user' in options) + self.assertTrue('user' in options) # setting a value cmd.user = 1 # user base and site shouldn't be created yet - self.assert_(not os.path.exists(self.user_base)) - self.assert_(not os.path.exists(self.user_site)) + self.assertTrue(not os.path.exists(self.user_base)) + self.assertTrue(not os.path.exists(self.user_site)) # let's run finalize cmd.ensure_finalized() # now they should - self.assert_(os.path.exists(self.user_base)) - self.assert_(os.path.exists(self.user_site)) + self.assertTrue(os.path.exists(self.user_base)) + self.assertTrue(os.path.exists(self.user_site)) - self.assert_('userbase' in cmd.config_vars) - self.assert_('usersite' in cmd.config_vars) + self.assertTrue('userbase' in cmd.config_vars) + self.assertTrue('usersite' in cmd.config_vars) def test_handle_extra_path(self): dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 73c40371..70721367 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -35,9 +35,9 @@ class InstallDataTestCase(support.TempdirManager, # let's check the result self.assertEquals(len(cmd.get_outputs()), 2) rtwo = os.path.split(two)[-1] - self.assert_(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) rone = os.path.split(one)[-1] - self.assert_(os.path.exists(os.path.join(inst, rone))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] # let's try with warn_dir one @@ -47,8 +47,8 @@ class InstallDataTestCase(support.TempdirManager, # let's check the result self.assertEquals(len(cmd.get_outputs()), 2) - self.assert_(os.path.exists(os.path.join(inst2, rtwo))) - self.assert_(os.path.exists(os.path.join(inst, rone))) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] # now using root and empty dir @@ -65,8 +65,8 @@ class InstallDataTestCase(support.TempdirManager, # let's check the result self.assertEquals(len(cmd.get_outputs()), 4) - self.assert_(os.path.exists(os.path.join(inst2, rtwo))) - self.assert_(os.path.exists(os.path.join(inst, rone))) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) def test_suite(): return unittest.makeSuite(InstallDataTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index d7681668..793b95ca 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -39,8 +39,8 @@ class InstallLibTestCase(support.TempdirManager, f = os.path.join(pkg_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - self.assert_(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) - self.assert_(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) def test_get_outputs(self): pkg_dir, dist = self.create_dist() @@ -57,7 +57,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.distribution.script_name = 'setup.py' # get_output should return 4 elements - self.assert_(len(cmd.get_outputs()) >= 2) + self.assertTrue(len(cmd.get_outputs()) >= 2) def test_get_inputs(self): pkg_dir, dist = self.create_dist() diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index fffa6ef2..b7eb625a 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -23,15 +23,15 @@ class InstallScriptsTestCase(support.TempdirManager, skip_build=1, ) cmd = install_scripts(dist) - self.assert_(not cmd.force) - self.assert_(not cmd.skip_build) - self.assert_(cmd.build_dir is None) - self.assert_(cmd.install_dir is None) + self.assertTrue(not cmd.force) + self.assertTrue(not cmd.skip_build) + self.assertTrue(cmd.build_dir is None) + self.assertTrue(cmd.install_dir is None) cmd.finalize_options() - self.assert_(cmd.force) - self.assert_(cmd.skip_build) + self.assertTrue(cmd.force) + self.assertTrue(cmd.skip_build) self.assertEqual(cmd.build_dir, "/foo/bar") self.assertEqual(cmd.install_dir, "/splat/funk") @@ -69,7 +69,7 @@ class InstallScriptsTestCase(support.TempdirManager, installed = os.listdir(target) for name in expected: - self.assert_(name in installed) + self.assertTrue(name in installed) def test_suite(): diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 11e5a476..73827988 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ class msvc9compilerTestCase(unittest.TestCase): # windows registeries versions. path = r'Software\Microsoft\Notepad' v = Reg.get_value(path, "lfitalic") - self.assert_(v in (0, 1)) + self.assertTrue(v in (0, 1)) import winreg HKCU = winreg.HKEY_CURRENT_USER @@ -54,7 +54,7 @@ class msvc9compilerTestCase(unittest.TestCase): self.assertEquals(keys, None) keys = Reg.read_keys(HKCU, r'Software\Microsoft') - self.assert_('Notepad' in keys) + self.assertTrue('Notepad' in keys) def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) diff --git a/tests/test_register.py b/tests/test_register.py index d9ff3ec4..c03ad101 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -96,7 +96,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): cmd = self._get_cmd() # we shouldn't have a .pypirc file yet - self.assert_(not os.path.exists(self.rc)) + self.assertTrue(not os.path.exists(self.rc)) # patching input and getpass.getpass # so register gets happy @@ -115,7 +115,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.input # we should have a brand new .pypirc file - self.assert_(os.path.exists(self.rc)) + self.assertTrue(os.path.exists(self.rc)) # with the content similar to WANTED_PYPIRC content = open(self.rc).read() @@ -133,13 +133,13 @@ class RegisterTestCase(PyPIRCCommandTestCase): # let's see what the server received : we should # have 2 similar requests - self.assert_(self.conn.reqs, 2) + self.assertTrue(self.conn.reqs, 2) req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) self.assertEquals(req1['Content-length'], '1374') self.assertEquals(req2['Content-length'], '1374') - self.assert_((b'xxx') in self.conn.reqs[1].data) + self.assertTrue((b'xxx') in self.conn.reqs[1].data) def test_password_not_in_file(self): @@ -165,11 +165,11 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.input # we should have send a request - self.assert_(self.conn.reqs, 1) + self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEquals(headers['Content-length'], '608') - self.assert_((b'tarek') in req.data) + self.assertTrue((b'tarek') in req.data) def test_password_reset(self): # this test runs choice 3 @@ -183,11 +183,11 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.input # we should have send a request - self.assert_(self.conn.reqs, 1) + self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEquals(headers['Content-length'], '290') - self.assert_((b'tarek') in req.data) + self.assertTrue((b'tarek') in req.data) def test_strict(self): # testing the script option diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 2d8fa271..eb362044 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -21,12 +21,12 @@ class SysconfigTestCase(support.EnvironGuard, def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() - self.assert_(os.path.isfile(config_h), config_h) + self.assertTrue(os.path.isfile(config_h), config_h) def test_get_python_lib(self): lib_dir = sysconfig.get_python_lib() # XXX doesn't work on Linux when Python was never installed before - #self.assert_(os.path.isdir(lib_dir), lib_dir) + #self.assertTrue(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? self.assertNotEqual(sysconfig.get_python_lib(), sysconfig.get_python_lib(prefix=TESTFN)) @@ -36,14 +36,14 @@ class SysconfigTestCase(support.EnvironGuard, # This is not much of a test. We make sure Python.h exists # in the directory returned by get_python_inc() but we don't know # it is the correct file. - self.assert_(os.path.isdir(inc_dir), inc_dir) + self.assertTrue(os.path.isdir(inc_dir), inc_dir) python_h = os.path.join(inc_dir, "Python.h") - self.assert_(os.path.isfile(python_h), python_h) + self.assertTrue(os.path.isfile(python_h), python_h) def test_get_config_vars(self): cvars = sysconfig.get_config_vars() - self.assert_(isinstance(cvars, dict)) - self.assert_(cvars) + self.assertTrue(isinstance(cvars, dict)) + self.assertTrue(cvars) def test_customize_compiler(self): diff --git a/tests/test_upload.py b/tests/test_upload.py index 95e4ac33..828f20c5 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -107,7 +107,7 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.conn.headers) self.assertEquals(headers['Content-length'], '2087') - self.assert_(headers['Content-type'].startswith('multipart/form-data')) + self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) self.assertEquals(self.conn.requests, [('POST', '/pypi')]) self.assert_((b'xxx') in self.conn.body) diff --git a/tests/test_util.py b/tests/test_util.py index cee7d526..c0acf5f4 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -225,10 +225,10 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') for y in yes: - self.assert_(strtobool(y)) + self.assertTrue(strtobool(y)) for n in no: - self.assert_(not strtobool(n)) + self.assertTrue(not strtobool(n)) def test_rfc822_escape(self): header = 'I am a\npoor\nlonesome\nheader\n' -- cgit v1.2.1 From 7329a6d2504982f7ad4261bf7a1f24e4063ee2d2 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 13 Aug 2009 15:47:36 +0000 Subject: bump version to 3.1.1rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 994e91fa..e7f21232 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.1a0" +__version__ = "3.1.1rc1" #--end constants-- -- cgit v1.2.1 From e5d9d64f150067264eedc7738da02f46d06e7827 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 16 Aug 2009 21:59:05 +0000 Subject: bump to 3.1.1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e7f21232..8c0e5a3c 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.1rc1" +__version__ = "3.1.1" #--end constants-- -- cgit v1.2.1 From e5e2bce7407fa39872996bbadb033ff1def2eec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 Aug 2009 21:28:34 +0000 Subject: fixed how fnmatch.translate is used (since it has changed in r74475 for #6665). Now the code is not harcoding the usage of $ anymore --- filelist.py | 5 +++-- tests/test_filelist.py | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/filelist.py b/filelist.py index 3ba5720c..5a88fd4e 100644 --- a/filelist.py +++ b/filelist.py @@ -342,12 +342,13 @@ def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): pattern_re = '' if prefix is not None: - prefix_re = (glob_to_re(prefix))[0:-1] # ditch trailing $ + # ditch end of pattern character + empty_pattern = glob_to_re('') + prefix_re = (glob_to_re(prefix))[:-len(empty_pattern)] pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) else: # no prefix -- respect anchor flag if anchor: pattern_re = "^" + pattern_re return re.compile(pattern_re) - # translate_pattern () diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 86db5574..1faccfae 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -6,15 +6,15 @@ class FileListTestCase(unittest.TestCase): def test_glob_to_re(self): # simple cases - self.assertEquals(glob_to_re('foo*'), 'foo[^/]*$') - self.assertEquals(glob_to_re('foo?'), 'foo[^/]$') - self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]$') + self.assertEquals(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)') + self.assertEquals(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)') + self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)') # special cases - self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*$') - self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*$') - self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]$') - self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]$') + self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)') + self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)') + self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') + self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') def test_suite(): return unittest.makeSuite(FileListTestCase) -- cgit v1.2.1 From cc6230244cd7ff8d13b3cc9c0c493682b5560ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 Aug 2009 21:35:46 +0000 Subject: Merged revisions 74493 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74493 | tarek.ziade | 2009-08-17 23:28:34 +0200 (Mon, 17 Aug 2009) | 1 line fixed how fnmatch.translate is used (since it has changed in r74475 for #6665). Now the code is not harcoding the usage of $ anymore ........ --- filelist.py | 4 +++- tests/test_filelist.py | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/filelist.py b/filelist.py index 58a2bfb1..06a8da9a 100644 --- a/filelist.py +++ b/filelist.py @@ -312,7 +312,9 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): pattern_re = '' if prefix is not None: - prefix_re = (glob_to_re(prefix))[0:-1] # ditch trailing $ + # ditch end of pattern character + empty_pattern = glob_to_re('') + prefix_re = (glob_to_re(prefix))[:-len(empty_pattern)] pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) else: # no prefix -- respect anchor flag if anchor: diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 86db5574..1faccfae 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -6,15 +6,15 @@ class FileListTestCase(unittest.TestCase): def test_glob_to_re(self): # simple cases - self.assertEquals(glob_to_re('foo*'), 'foo[^/]*$') - self.assertEquals(glob_to_re('foo?'), 'foo[^/]$') - self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]$') + self.assertEquals(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)') + self.assertEquals(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)') + self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)') # special cases - self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*$') - self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*$') - self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]$') - self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]$') + self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)') + self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)') + self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') + self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') def test_suite(): return unittest.makeSuite(FileListTestCase) -- cgit v1.2.1 From 2adb920d5dce513c2743a1b1559559709c38caf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 Aug 2009 21:48:22 +0000 Subject: module cleanup --- filelist.py | 99 ++++++++++++++++++++++++++----------------------------------- 1 file changed, 42 insertions(+), 57 deletions(-) diff --git a/filelist.py b/filelist.py index 5a88fd4e..de665e7a 100644 --- a/filelist.py +++ b/filelist.py @@ -6,15 +6,13 @@ and building lists of files. __revision__ = "$Id$" -import os, string, re +import os, re import fnmatch -from types import * from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsInternalError from distutils import log class FileList: - """A list of files built by on exploring the filesystem and filtered by applying various patterns to what we find there. @@ -29,22 +27,19 @@ class FileList: filtering applied) """ - def __init__(self, - warn=None, - debug_print=None): + def __init__(self, warn=None, debug_print=None): # ignore argument to FileList, but keep them for backwards # compatibility - self.allfiles = None self.files = [] - def set_allfiles (self, allfiles): + def set_allfiles(self, allfiles): self.allfiles = allfiles - def findall (self, dir=os.curdir): + def findall(self, dir=os.curdir): self.allfiles = findall(dir) - def debug_print (self, msg): + def debug_print(self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the DISTUTILS_DEBUG environment variable) flag is true. """ @@ -54,13 +49,13 @@ class FileList: # -- List-like methods --------------------------------------------- - def append (self, item): + def append(self, item): self.files.append(item) - def extend (self, items): + def extend(self, items): self.files.extend(items) - def sort (self): + def sort(self): # Not a strict lexical sort! sortable_files = map(os.path.split, self.files) sortable_files.sort() @@ -71,7 +66,7 @@ class FileList: # -- Other miscellaneous utility methods --------------------------- - def remove_duplicates (self): + def remove_duplicates(self): # Assumes list has been sorted! for i in range(len(self.files) - 1, 0, -1): if self.files[i] == self.files[i - 1]: @@ -80,8 +75,8 @@ class FileList: # -- "File template" methods --------------------------------------- - def _parse_template_line (self, line): - words = string.split(line) + def _parse_template_line(self, line): + words = line.split() action = words[0] patterns = dir = dir_pattern = None @@ -114,11 +109,7 @@ class FileList: return (action, patterns, dir, dir_pattern) - # _parse_template_line () - - - def process_template_line (self, line): - + def process_template_line(self, line): # Parse the line: split it up, make sure the right number of words # is there, and return the relevant words. 'action' is always # defined: it's the first word of the line. Which of the other @@ -130,28 +121,28 @@ class FileList: # right number of words on the line for that action -- so we # can proceed with minimal error-checking. if action == 'include': - self.debug_print("include " + string.join(patterns)) + self.debug_print("include " + ' '.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=1): log.warn("warning: no files found matching '%s'", pattern) elif action == 'exclude': - self.debug_print("exclude " + string.join(patterns)) + self.debug_print("exclude " + ' '.join(patterns)) for pattern in patterns: if not self.exclude_pattern(pattern, anchor=1): log.warn(("warning: no previously-included files " "found matching '%s'"), pattern) elif action == 'global-include': - self.debug_print("global-include " + string.join(patterns)) + self.debug_print("global-include " + ' '.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=0): log.warn(("warning: no files found matching '%s' " + "anywhere in distribution"), pattern) elif action == 'global-exclude': - self.debug_print("global-exclude " + string.join(patterns)) + self.debug_print("global-exclude " + ' '.join(patterns)) for pattern in patterns: if not self.exclude_pattern(pattern, anchor=0): log.warn(("warning: no previously-included files matching " @@ -160,7 +151,7 @@ class FileList: elif action == 'recursive-include': self.debug_print("recursive-include %s %s" % - (dir, string.join(patterns))) + (dir, ' '.join(patterns))) for pattern in patterns: if not self.include_pattern(pattern, prefix=dir): log.warn(("warning: no files found matching '%s' " + @@ -169,7 +160,7 @@ class FileList: elif action == 'recursive-exclude': self.debug_print("recursive-exclude %s %s" % - (dir, string.join(patterns))) + (dir, ' '.join(patterns))) for pattern in patterns: if not self.exclude_pattern(pattern, prefix=dir): log.warn(("warning: no previously-included files matching " @@ -196,13 +187,13 @@ class FileList: # -- Filtering/selection methods ----------------------------------- - def include_pattern (self, pattern, - anchor=1, prefix=None, is_regex=0): + def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): """Select strings (presumably filenames) from 'self.files' that - match 'pattern', a Unix-style wildcard (glob) pattern. Patterns - are not quite the same as implemented by the 'fnmatch' module: '*' - and '?' match non-special characters, where "special" is platform- - dependent: slash on Unix; colon, slash, and backslash on + match 'pattern', a Unix-style wildcard (glob) pattern. + + Patterns are not quite the same as implemented by the 'fnmatch' + module: '*' and '?' match non-special characters, where "special" + is platform-dependent: slash on Unix; colon, slash, and backslash on DOS/Windows; and colon on Mac OS. If 'anchor' is true (the default), then the pattern match is more @@ -239,16 +230,14 @@ class FileList: return files_found - # include_pattern () - - def exclude_pattern (self, pattern, - anchor=1, prefix=None, is_regex=0): + def exclude_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): """Remove strings (presumably filenames) from 'files' that match - 'pattern'. Other parameters are the same as for - 'include_pattern()', above. - The list 'self.files' is modified in place. - Return 1 if files are found. + 'pattern'. + + Other parameters are the same as for 'include_pattern()', above. + The list 'self.files' is modified in place. Return 1 if files are + found. """ files_found = 0 pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) @@ -262,15 +251,11 @@ class FileList: return files_found - # exclude_pattern () - -# class FileList - # ---------------------------------------------------------------------- # Utility functions -def findall (dir = os.curdir): +def findall(dir = os.curdir): """Find all files under 'dir' and return the list of full filenames (relative to 'dir'). """ @@ -303,10 +288,11 @@ def findall (dir = os.curdir): def glob_to_re(pattern): - """Translate a shell-like glob pattern to a regular expression; return - a string containing the regex. Differs from 'fnmatch.translate()' in - that '*' does not match "special characters" (which are - platform-specific). + """Translate a shell-like glob pattern to a regular expression. + + Return a string containing the regex. Differs from + 'fnmatch.translate()' in that '*' does not match "special characters" + (which are platform-specific). """ pattern_re = fnmatch.translate(pattern) @@ -321,17 +307,17 @@ def glob_to_re(pattern): return pattern_re -# glob_to_re () - -def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): +def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): """Translate a shell-like wildcard pattern to a compiled regular - expression. Return the compiled regex. If 'is_regex' true, + expression. + + Return the compiled regex. If 'is_regex' true, then 'pattern' is directly compiled to a regex (if it's a string) or just returned as-is (assumes it's a regex object). """ if is_regex: - if type(pattern) is StringType: + if isinstance(pattern, str): return re.compile(pattern) else: return pattern @@ -344,11 +330,10 @@ def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): if prefix is not None: # ditch end of pattern character empty_pattern = glob_to_re('') - prefix_re = (glob_to_re(prefix))[:-len(empty_pattern)] + prefix_re = glob_to_re(prefix)[:-len(empty_pattern)] pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) else: # no prefix -- respect anchor flag if anchor: pattern_re = "^" + pattern_re return re.compile(pattern_re) -# translate_pattern () -- cgit v1.2.1 From 15409e57d2abff9f61fa6cd3b7e676da610a4514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 Aug 2009 21:50:37 +0000 Subject: Merged revisions 74495 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74495 | tarek.ziade | 2009-08-17 23:48:22 +0200 (Mon, 17 Aug 2009) | 1 line module cleanup ........ --- filelist.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/filelist.py b/filelist.py index 06a8da9a..b6a1dfee 100644 --- a/filelist.py +++ b/filelist.py @@ -180,10 +180,11 @@ class FileList: def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): """Select strings (presumably filenames) from 'self.files' that - match 'pattern', a Unix-style wildcard (glob) pattern. Patterns - are not quite the same as implemented by the 'fnmatch' module: '*' - and '?' match non-special characters, where "special" is platform- - dependent: slash on Unix; colon, slash, and backslash on + match 'pattern', a Unix-style wildcard (glob) pattern. + + Patterns are not quite the same as implemented by the 'fnmatch' + module: '*' and '?' match non-special characters, where "special" + is platform-dependent: slash on Unix; colon, slash, and backslash on DOS/Windows; and colon on Mac OS. If 'anchor' is true (the default), then the pattern match is more @@ -220,13 +221,13 @@ class FileList: return files_found - def exclude_pattern (self, pattern, - anchor=1, prefix=None, is_regex=0): + def exclude_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): """Remove strings (presumably filenames) from 'files' that match - 'pattern'. Other parameters are the same as for - 'include_pattern()', above. - The list 'self.files' is modified in place. - Return True if files are found, False otherwise. + 'pattern'. + + Other parameters are the same as for 'include_pattern()', above. + The list 'self.files' is modified in place. Return 1 if files are + found. """ files_found = False pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) @@ -275,10 +276,11 @@ def findall(dir=os.curdir): def glob_to_re(pattern): - """Translate a shell-like glob pattern to a regular expression; return - a string containing the regex. Differs from 'fnmatch.translate()' in - that '*' does not match "special characters" (which are - platform-specific). + """Translate a shell-like glob pattern to a regular expression. + + Return a string containing the regex. Differs from + 'fnmatch.translate()' in that '*' does not match "special characters" + (which are platform-specific). """ pattern_re = fnmatch.translate(pattern) @@ -296,7 +298,9 @@ def glob_to_re(pattern): def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): """Translate a shell-like wildcard pattern to a compiled regular - expression. Return the compiled regex. If 'is_regex' true, + expression. + + Return the compiled regex. If 'is_regex' true, then 'pattern' is directly compiled to a regex (if it's a string) or just returned as-is (assumes it's a regex object). """ @@ -314,7 +318,7 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): if prefix is not None: # ditch end of pattern character empty_pattern = glob_to_re('') - prefix_re = (glob_to_re(prefix))[:-len(empty_pattern)] + prefix_re = glob_to_re(prefix)[:-len(empty_pattern)] pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) else: # no prefix -- respect anchor flag if anchor: -- cgit v1.2.1 From b3720fd2d9d4207a120738aacfdc3e99d7317dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 18 Aug 2009 08:16:33 +0000 Subject: added more test coverage for distutils.filelist to prevent regressions when fnmatch or re are changed --- filelist.py | 5 +---- tests/test_filelist.py | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/filelist.py b/filelist.py index de665e7a..7cf05098 100644 --- a/filelist.py +++ b/filelist.py @@ -115,7 +115,7 @@ class FileList: # defined: it's the first word of the line. Which of the other # three are defined depends on the action; it'll be either # patterns, (dir and patterns), or (dir_pattern). - (action, patterns, dir, dir_pattern) = self._parse_template_line(line) + action, patterns, dir, dir_pattern = self._parse_template_line(line) # OK, now we know that the action is valid and we have the # right number of words on the line for that action -- so we @@ -182,9 +182,6 @@ class FileList: raise DistutilsInternalError, \ "this cannot happen: invalid action '%s'" % action - # process_template_line () - - # -- Filtering/selection methods ----------------------------------- def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 1faccfae..181a58d8 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -1,6 +1,21 @@ """Tests for distutils.filelist.""" +from os.path import join import unittest -from distutils.filelist import glob_to_re +from distutils.filelist import glob_to_re, FileList + +MANIFEST_IN = """\ +include ok +include xo +exclude xo +include foo.tmp +global-include *.x +global-include *.txt +global-exclude *.tmp +recursive-include f *.oo +recursive-exclude global *.x +graft dir +prune dir3 +""" class FileListTestCase(unittest.TestCase): @@ -16,6 +31,34 @@ class FileListTestCase(unittest.TestCase): self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') + def test_process_template_line(self): + # testing all MANIFEST.in template patterns + file_list = FileList() + + # simulated file list + file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt' + join('global', 'one.txt'), + join('global', 'two.txt'), + join('global', 'files.x'), + join('global', 'here.tmp'), + join('f', 'o', 'f.oo'), + join('dir', 'graft-one'), + join('dir', 'dir2', 'graft2'), + join('dir3', 'ok'), + join('dir3', 'sub', 'ok.txt') + ] + + for line in MANIFEST_IN.split('\n'): + if line.strip() == '': + continue + file_list.process_template_line(line) + + wanted = ['ok', join('global', 'one.txt'), join('global', 'two.txt'), + 'four.txt', join('f', 'o', 'f.oo'), join('dir', 'graft-one'), + join('dir', 'dir2', 'graft2')] + + self.assertEquals(file_list.files, wanted) + def test_suite(): return unittest.makeSuite(FileListTestCase) -- cgit v1.2.1 From fa9880b2ce1357d28b14880d2d7398e32dccdf31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 18 Aug 2009 08:21:49 +0000 Subject: fixed typo --- tests/test_filelist.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 181a58d8..cf64c744 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -36,7 +36,7 @@ class FileListTestCase(unittest.TestCase): file_list = FileList() # simulated file list - file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt' + file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt', join('global', 'one.txt'), join('global', 'two.txt'), join('global', 'files.x'), @@ -53,9 +53,9 @@ class FileListTestCase(unittest.TestCase): continue file_list.process_template_line(line) - wanted = ['ok', join('global', 'one.txt'), join('global', 'two.txt'), - 'four.txt', join('f', 'o', 'f.oo'), join('dir', 'graft-one'), - join('dir', 'dir2', 'graft2')] + wanted = ['ok', 'four.txt', join('global', 'one.txt'), + join('global', 'two.txt'), join('f', 'o', 'f.oo'), + join('dir', 'graft-one'), join('dir', 'dir2', 'graft2')] self.assertEquals(file_list.files, wanted) -- cgit v1.2.1 From 8f6778cb615909aff25a6b77a78d07e195be85af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 18 Aug 2009 08:23:10 +0000 Subject: Merged revisions 74501 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74501 | tarek.ziade | 2009-08-18 10:16:33 +0200 (Tue, 18 Aug 2009) | 1 line added more test coverage for distutils.filelist to prevent regressions when fnmatch or re are changed ........ --- filelist.py | 3 +-- tests/test_filelist.py | 45 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/filelist.py b/filelist.py index b6a1dfee..bfc6df69 100644 --- a/filelist.py +++ b/filelist.py @@ -108,7 +108,7 @@ class FileList: # defined: it's the first word of the line. Which of the other # three are defined depends on the action; it'll be either # patterns, (dir and patterns), or (dir_pattern). - (action, patterns, dir, dir_pattern) = self._parse_template_line(line) + action, patterns, dir, dir_pattern = self._parse_template_line(line) # OK, now we know that the action is valid and we have the # right number of words on the line for that action -- so we @@ -175,7 +175,6 @@ class FileList: raise DistutilsInternalError( "this cannot happen: invalid action '%s'" % action) - # -- Filtering/selection methods ----------------------------------- def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 1faccfae..cf64c744 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -1,6 +1,21 @@ """Tests for distutils.filelist.""" +from os.path import join import unittest -from distutils.filelist import glob_to_re +from distutils.filelist import glob_to_re, FileList + +MANIFEST_IN = """\ +include ok +include xo +exclude xo +include foo.tmp +global-include *.x +global-include *.txt +global-exclude *.tmp +recursive-include f *.oo +recursive-exclude global *.x +graft dir +prune dir3 +""" class FileListTestCase(unittest.TestCase): @@ -16,6 +31,34 @@ class FileListTestCase(unittest.TestCase): self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') + def test_process_template_line(self): + # testing all MANIFEST.in template patterns + file_list = FileList() + + # simulated file list + file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt', + join('global', 'one.txt'), + join('global', 'two.txt'), + join('global', 'files.x'), + join('global', 'here.tmp'), + join('f', 'o', 'f.oo'), + join('dir', 'graft-one'), + join('dir', 'dir2', 'graft2'), + join('dir3', 'ok'), + join('dir3', 'sub', 'ok.txt') + ] + + for line in MANIFEST_IN.split('\n'): + if line.strip() == '': + continue + file_list.process_template_line(line) + + wanted = ['ok', 'four.txt', join('global', 'one.txt'), + join('global', 'two.txt'), join('f', 'o', 'f.oo'), + join('dir', 'graft-one'), join('dir', 'dir2', 'graft2')] + + self.assertEquals(file_list.files, wanted) + def test_suite(): return unittest.makeSuite(FileListTestCase) -- cgit v1.2.1 From 0310beaafd54123056a2af0ae52c1aad81e9cf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 9 Sep 2009 08:14:20 +0000 Subject: Issue #6163: Fixed HP-UX runtime library dir options in distutils.unixcompiler --- tests/test_unixccompiler.py | 18 +++++++++++++++++- unixccompiler.py | 4 +++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 96f5454e..1b7dd4cd 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -36,7 +36,23 @@ class UnixCCompilerTestCase(unittest.TestCase): # hp-ux sys.platform = 'hp-ux' - self.assertEqual(self.cc.rpath_foo(), '+s -L/foo') + old_gcv = sysconfig.get_config_var + def gcv(v): + return 'xxx' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo']) + + def gcv(v): + return 'gcc' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + def gcv(v): + return 'g++' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + sysconfig.get_config_var = old_gcv # irix646 sys.platform = 'irix646' diff --git a/unixccompiler.py b/unixccompiler.py index 129ac8cf..2083f829 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -285,7 +285,9 @@ class UnixCCompiler(CCompiler): # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir elif sys.platform[:5] == "hp-ux": - return "+s -L" + dir + if "gcc" in compiler or "g++" in compiler: + return ["-Wl,+s", "-L" + dir] + return ["+s", "-L" + dir] elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] elif compiler[:3] == "gcc" or compiler[:3] == "g++": -- cgit v1.2.1 From 8137c6cfbb6449e5d89705b91f44f6d31e99b39b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 9 Sep 2009 08:34:06 +0000 Subject: Merged revisions 74728 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74728 | tarek.ziade | 2009-09-09 10:14:20 +0200 (Wed, 09 Sep 2009) | 1 line Issue #6163: Fixed HP-UX runtime library dir options in distutils.unixcompiler ........ --- unixccompiler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 045368a9..7556cbdb 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -284,7 +284,9 @@ class UnixCCompiler(CCompiler): # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir elif sys.platform[:5] == "hp-ux": - return "+s -L" + dir + if "gcc" in compiler or "g++" in compiler: + return ["-Wl,+s", "-L" + dir] + return ["+s", "-L" + dir] elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] elif compiler[:3] == "gcc" or compiler[:3] == "g++": -- cgit v1.2.1 From 031ebb0fc2345b84cefb440c6cc83264cc9bd79e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 9 Sep 2009 08:48:07 +0000 Subject: Merged revisions 74728 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74728 | tarek.ziade | 2009-09-09 10:14:20 +0200 (Wed, 09 Sep 2009) | 1 line Issue #6163: Fixed HP-UX runtime library dir options in distutils.unixcompiler ........ --- tests/test_unixccompiler.py | 18 +++++++++++++++++- unixccompiler.py | 4 +++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 96f5454e..1b7dd4cd 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -36,7 +36,23 @@ class UnixCCompilerTestCase(unittest.TestCase): # hp-ux sys.platform = 'hp-ux' - self.assertEqual(self.cc.rpath_foo(), '+s -L/foo') + old_gcv = sysconfig.get_config_var + def gcv(v): + return 'xxx' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo']) + + def gcv(v): + return 'gcc' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + def gcv(v): + return 'g++' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + sysconfig.get_config_var = old_gcv # irix646 sys.platform = 'irix646' diff --git a/unixccompiler.py b/unixccompiler.py index 26d2856a..da85c896 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -283,7 +283,9 @@ class UnixCCompiler(CCompiler): # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir elif sys.platform[:5] == "hp-ux": - return "+s -L" + dir + if "gcc" in compiler or "g++" in compiler: + return ["-Wl,+s", "-L" + dir] + return ["+s", "-L" + dir] elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] elif compiler[:3] == "gcc" or compiler[:3] == "g++": -- cgit v1.2.1 From 2bf23a8289d1d71e4ab874a28812cecab3dd8604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 9 Sep 2009 09:07:13 +0000 Subject: Merged revisions 74730 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r74730 | tarek.ziade | 2009-09-09 10:48:07 +0200 (Wed, 09 Sep 2009) | 9 lines Merged revisions 74728 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74728 | tarek.ziade | 2009-09-09 10:14:20 +0200 (Wed, 09 Sep 2009) | 1 line Issue #6163: Fixed HP-UX runtime library dir options in distutils.unixcompiler ........ ................ --- tests/test_unixccompiler.py | 18 +++++++++++++++++- unixccompiler.py | 4 +++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 94e9edfc..be2df5c6 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -36,7 +36,23 @@ class UnixCCompilerTestCase(unittest.TestCase): # hp-ux sys.platform = 'hp-ux' - self.assertEqual(self.cc.rpath_foo(), '+s -L/foo') + old_gcv = sysconfig.get_config_var + def gcv(v): + return 'xxx' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo']) + + def gcv(v): + return 'gcc' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + def gcv(v): + return 'g++' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + sysconfig.get_config_var = old_gcv # irix646 sys.platform = 'irix646' diff --git a/unixccompiler.py b/unixccompiler.py index c11544d8..8bbdb4b3 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -283,7 +283,9 @@ class UnixCCompiler(CCompiler): # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir elif sys.platform[:5] == "hp-ux": - return "+s -L" + dir + if "gcc" in compiler or "g++" in compiler: + return ["-Wl,+s", "-L" + dir] + return ["+s", "-L" + dir] elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] else: -- cgit v1.2.1 From bf6e05c74f29186165399ec4cd7939345c01154c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 9 Sep 2009 11:39:41 +0000 Subject: removed unecessary lines for clarity and added a the same test than in trunk for the inplace --- command/build_ext.py | 3 --- tests/test_build_ext.py | 13 +++++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index db82f1d0..a3e3982f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -642,9 +642,6 @@ class build_ext (Command): # the inplace option requires to find the package directory # using the build_py command for that package = '.'.join(modpath[0:-1]) - modpath = fullname.split('.') - package = '.'.join(modpath[0:-1]) - base = modpath[-1] build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 153c8756..d8d36677 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -329,6 +329,19 @@ class BuildExtTestCase(support.TempdirManager, wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) self.assertEquals(wanted, path) + def test_build_ext_inplace(self): + etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + etree_ext = Extension('lxml.etree', [etree_c]) + dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + cmd = build_ext(dist) + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + wanted = os.path.join(curdir, 'src', 'lxml', 'etree.so') + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + def test_suite(): if not sysconfig.python_build: if test_support.verbose: -- cgit v1.2.1 From 7bb74b86b24f8f300907200e02d5319db044736f Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 12 Sep 2009 14:43:43 +0000 Subject: #6026 - fix tests that failed without zlib --- tests/test_archive_util.py | 10 ++++++++++ tests/test_bdist_dumb.py | 8 ++++++++ tests/test_sdist.py | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 91bc4e3d..d6fb6769 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -19,10 +19,18 @@ try: except ImportError: ZIP_SUPPORT = find_executable('zip') +# some tests will fail if zlib is not available +try: + import zlib +except ImportError: + zlib = None + + class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): + @unittest.skipUnless(zlib, "Requires zlib") def test_make_tarball(self): # creating something to tar tmpdir = self.mkdtemp() @@ -83,6 +91,7 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir2, 'archive') return tmpdir, tmpdir2, base_name + @unittest.skipUnless(zlib, "Requires zlib") @unittest.skipUnless(find_executable('tar') and find_executable('gzip'), 'Need the tar command to run') def test_tarfile_vs_tar(self): @@ -168,6 +177,7 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertTrue(not os.path.exists(tarball)) self.assertEquals(len(w.warnings), 1) + @unittest.skipUnless(zlib, "Requires zlib") @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile(self): # creating something to tar diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index b28f89f5..a838f734 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -4,6 +4,13 @@ import unittest import sys import os +# zlib is not used here, but if it's not available +# test_simple_built will fail +try: + import zlib +except ImportError: + zlib = None + from distutils.core import Distribution from distutils.command.bdist_dumb import bdist_dumb from distutils.tests import support @@ -31,6 +38,7 @@ class BuildDumbTestCase(support.TempdirManager, sys.argv = self.old_sys_argv[:] super(BuildDumbTestCase, self).tearDown() + @unittest.skipUnless(zlib, "requires zlib") def test_simple_built(self): # let's create a simple package diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 5808ca16..c2feccb3 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -3,6 +3,14 @@ import os import unittest import shutil import zipfile + +# zlib is not used here, but if it's not available +# the tests that use zipfile may fail +try: + import zlib +except ImportError: + zlib = None + from os.path import join import sys import tempfile @@ -79,6 +87,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.warn = _warn return dist, cmd + @unittest.skipUnless(zlib, "requires zlib") def test_prune_file_list(self): # this test creates a package with some vcs dirs in it # and launch sdist to make sure they get pruned @@ -120,6 +129,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # making sure everything has been pruned correctly self.assertEquals(len(content), 4) + @unittest.skipUnless(zlib, "requires zlib") def test_make_distribution(self): # check if tar and gzip are installed @@ -156,6 +166,7 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEquals(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + @unittest.skipUnless(zlib, "requires zlib") def test_add_defaults(self): # http://bugs.python.org/issue2279 @@ -217,6 +228,7 @@ class SDistTestCase(PyPIRCCommandTestCase): manifest = open(join(self.tmp_dir, 'MANIFEST')).read() self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + @unittest.skipUnless(zlib, "requires zlib") def test_metadata_check_option(self): # testing the `medata-check` option dist, cmd = self.get_cmd(metadata={}) -- cgit v1.2.1 From a706937e76582c6005306a391d08feecc68e0966 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 12 Sep 2009 18:41:20 +0000 Subject: Merged revisions 74754 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74754 | ezio.melotti | 2009-09-12 17:43:43 +0300 (Sat, 12 Sep 2009) | 1 line #6026 - fix tests that failed without zlib ........ --- tests/test_archive_util.py | 10 ++++++++++ tests/test_bdist_dumb.py | 8 ++++++++ tests/test_sdist.py | 12 ++++++++++++ 3 files changed, 30 insertions(+) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index d88e0b35..d33b7e15 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -19,10 +19,18 @@ try: except ImportError: ZIP_SUPPORT = find_executable('zip') +# some tests will fail if zlib is not available +try: + import zlib +except ImportError: + zlib = None + + class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): + @unittest.skipUnless(zlib, "Requires zlib") def test_make_tarball(self): # creating something to tar tmpdir = self.mkdtemp() @@ -83,6 +91,7 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir2, 'archive') return tmpdir, tmpdir2, base_name + @unittest.skipUnless(zlib, "Requires zlib") @unittest.skipUnless(find_executable('tar') and find_executable('gzip'), 'Need the tar command to run') def test_tarfile_vs_tar(self): @@ -168,6 +177,7 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertTrue(not os.path.exists(tarball)) self.assertEquals(len(w.warnings), 1) + @unittest.skipUnless(zlib, "Requires zlib") @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile(self): # creating something to tar diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index b28f89f5..a838f734 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -4,6 +4,13 @@ import unittest import sys import os +# zlib is not used here, but if it's not available +# test_simple_built will fail +try: + import zlib +except ImportError: + zlib = None + from distutils.core import Distribution from distutils.command.bdist_dumb import bdist_dumb from distutils.tests import support @@ -31,6 +38,7 @@ class BuildDumbTestCase(support.TempdirManager, sys.argv = self.old_sys_argv[:] super(BuildDumbTestCase, self).tearDown() + @unittest.skipUnless(zlib, "requires zlib") def test_simple_built(self): # let's create a simple package diff --git a/tests/test_sdist.py b/tests/test_sdist.py index b7e5859c..986288e5 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -3,6 +3,14 @@ import os import unittest import shutil import zipfile + +# zlib is not used here, but if it's not available +# the tests that use zipfile may fail +try: + import zlib +except ImportError: + zlib = None + from os.path import join import sys import tempfile @@ -79,6 +87,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.warn = _warn return dist, cmd + @unittest.skipUnless(zlib, "requires zlib") def test_prune_file_list(self): # this test creates a package with some vcs dirs in it # and launch sdist to make sure they get pruned @@ -120,6 +129,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # making sure everything has been pruned correctly self.assertEquals(len(content), 4) + @unittest.skipUnless(zlib, "requires zlib") def test_make_distribution(self): # check if tar and gzip are installed @@ -156,6 +166,7 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEquals(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + @unittest.skipUnless(zlib, "requires zlib") def test_add_defaults(self): # http://bugs.python.org/issue2279 @@ -217,6 +228,7 @@ class SDistTestCase(PyPIRCCommandTestCase): manifest = open(join(self.tmp_dir, 'MANIFEST')).read() self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + @unittest.skipUnless(zlib, "requires zlib") def test_metadata_check_option(self): # testing the `medata-check` option dist, cmd = self.get_cmd(metadata={}) -- cgit v1.2.1 From 5132893c6fa2799cfb9d23b35ff1828f8c621421 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Sep 2009 19:13:15 +0000 Subject: Finish support for --with-universal-archs=intel and --with-universal-archs=3-way (issue6245) --- util.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/util.py b/util.py index 459c3646..fe6851cb 100644 --- a/util.py +++ b/util.py @@ -144,11 +144,26 @@ def get_platform(): machine = 'fat' cflags = get_config_vars().get('CFLAGS') - if '-arch x86_64' in cflags: - if '-arch i386' in cflags: - machine = 'universal' - else: - machine = 'fat64' + archs = re.findall('-arch\s+(\S+)', cflags) + archs.sort() + archs = tuple(archs) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r"%(archs,)) + elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. -- cgit v1.2.1 From d057f0f1b7dbf7446e4b979ccf8da90ce89995de Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Sep 2009 19:14:37 +0000 Subject: Merged revisions 74806 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74806 | ronald.oussoren | 2009-09-15 21:13:15 +0200 (Tue, 15 Sep 2009) | 3 lines Finish support for --with-universal-archs=intel and --with-universal-archs=3-way (issue6245) ........ --- util.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/util.py b/util.py index e9d29ff4..ee5829b0 100644 --- a/util.py +++ b/util.py @@ -142,11 +142,26 @@ def get_platform (): machine = 'fat' cflags = get_config_vars().get('CFLAGS') - if '-arch x86_64' in cflags: - if '-arch i386' in cflags: - machine = 'universal' - else: - machine = 'fat64' + archs = re.findall('-arch\s+(\S+)', cflags) + archs.sort() + archs = tuple(archs) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r"%(archs,)) + elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. -- cgit v1.2.1 From 589e2a746f2e3812fe1e7840391a61b20b3fdaf4 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Sep 2009 19:16:02 +0000 Subject: Merged revisions 74806 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74806 | ronald.oussoren | 2009-09-15 21:13:15 +0200 (Tue, 15 Sep 2009) | 3 lines Finish support for --with-universal-archs=intel and --with-universal-archs=3-way (issue6245) ........ --- util.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/util.py b/util.py index 0c88b819..6709bbfe 100644 --- a/util.py +++ b/util.py @@ -144,11 +144,26 @@ def get_platform(): machine = 'fat' cflags = get_config_vars().get('CFLAGS') - if '-arch x86_64' in cflags: - if '-arch i386' in cflags: - machine = 'universal' - else: - machine = 'fat64' + archs = re.findall('-arch\s+(\S+)', cflags) + archs.sort() + archs = tuple(archs) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r"%(archs,)) + elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. -- cgit v1.2.1 From b2dfd2fe8b6c80780cfa28e5ca013124fb1f45b6 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Sep 2009 19:16:40 +0000 Subject: Merged revisions 74808 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r74808 | ronald.oussoren | 2009-09-15 21:16:02 +0200 (Tue, 15 Sep 2009) | 10 lines Merged revisions 74806 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74806 | ronald.oussoren | 2009-09-15 21:13:15 +0200 (Tue, 15 Sep 2009) | 3 lines Finish support for --with-universal-archs=intel and --with-universal-archs=3-way (issue6245) ........ ................ --- util.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/util.py b/util.py index 4c1956a2..5054d0c8 100644 --- a/util.py +++ b/util.py @@ -141,11 +141,26 @@ def get_platform (): machine = 'fat' cflags = get_config_vars().get('CFLAGS') - if '-arch x86_64' in cflags: - if '-arch i386' in cflags: - machine = 'universal' - else: - machine = 'fat64' + archs = re.findall('-arch\s+(\S+)', cflags) + archs.sort() + archs = tuple(archs) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r"%(archs,)) + elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. -- cgit v1.2.1 From 56e40eafce0c788acacca65aef4f0fc93640aedd Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Sep 2009 21:24:07 +0000 Subject: Update distutils.util tests after my changes to --with-universal-archs --- tests/test_util.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index ffa92bd0..7190b2c9 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -142,15 +142,35 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') + self.assertEquals(get_platform(), 'macosx-10.4-intel') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + self.assertEquals(get_platform(), 'macosx-10.4-fat3') + + get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') self.assertEquals(get_platform(), 'macosx-10.4-universal') - get_config_vars()['CFLAGS'] = ('-arch x86_64 -isysroot ' + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEquals(get_platform(), 'macosx-10.4-fat64') + for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): + get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3'%(arch,)) + + self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) + # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' -- cgit v1.2.1 From d2f24d4c853603500381f1a4b34ae1d04d920ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 19 Sep 2009 09:58:51 +0000 Subject: Fixed #6947 - SO extension varies under windows --- tests/test_build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index d8d36677..94000a57 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -338,7 +338,8 @@ class BuildExtTestCase(support.TempdirManager, cmd.distribution.package_dir = {'': 'src'} cmd.distribution.packages = ['lxml', 'lxml.html'] curdir = os.getcwd() - wanted = os.path.join(curdir, 'src', 'lxml', 'etree.so') + ext = sysconfig.get_config_var("SO") + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) -- cgit v1.2.1 From e16bbeaf5a2c38ea024beb32eaee3f413acd7e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Sep 2009 12:19:07 +0000 Subject: improved distutils test coverage: now the DEBUG mode is covered too (will help fix the issue #6954 in py3k branch) --- tests/test_ccompiler.py | 24 +++++++++++++++++++++++- tests/test_cmd.py | 18 ++++++++++++++++++ tests/test_core.py | 20 ++++++++++++++++++++ tests/test_filelist.py | 19 +++++++++++++++++++ tests/test_install.py | 14 +++++++++++++- 5 files changed, 93 insertions(+), 2 deletions(-) diff --git a/tests/test_ccompiler.py b/tests/test_ccompiler.py index 58c8c5da..2c219b7b 100644 --- a/tests/test_ccompiler.py +++ b/tests/test_ccompiler.py @@ -1,8 +1,10 @@ """Tests for distutils.ccompiler.""" import os import unittest +from test.test_support import captured_stdout -from distutils.ccompiler import gen_lib_options +from distutils.ccompiler import gen_lib_options, CCompiler +from distutils import debug class FakeCompiler(object): def library_dir_option(self, dir): @@ -30,6 +32,26 @@ class CCompilerTestCase(unittest.TestCase): '-lname2'] self.assertEquals(opts, wanted) + def test_debug_print(self): + + class MyCCompiler(CCompiler): + executables = {} + + compiler = MyCCompiler() + with captured_stdout() as stdout: + compiler.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + compiler.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), 'xxx\n') + finally: + debug.DEBUG = False + def test_suite(): return unittest.makeSuite(CCompilerTestCase) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index d6438b5e..2174efbf 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,10 +1,12 @@ """Tests for distutils.cmd.""" import unittest import os +from test.test_support import captured_stdout from distutils.cmd import Command from distutils.dist import Distribution from distutils.errors import DistutilsOptionError +from distutils import debug class MyCmd(Command): def initialize_options(self): @@ -102,6 +104,22 @@ class CommandTestCase(unittest.TestCase): cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') + def test_debug_print(self): + cmd = self.cmd + with captured_stdout() as stdout: + cmd.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + cmd.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), 'xxx\n') + finally: + debug.DEBUG = False + def test_suite(): return unittest.makeSuite(CommandTestCase) diff --git a/tests/test_core.py b/tests/test_core.py index e481e8de..3c26fddb 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,6 +6,7 @@ import os import shutil import sys import test.test_support +from test.test_support import captured_stdout import unittest @@ -33,10 +34,12 @@ class CoreTestCase(unittest.TestCase): def setUp(self): self.old_stdout = sys.stdout self.cleanup_testfn() + self.old_argv = sys.argv[:] def tearDown(self): sys.stdout = self.old_stdout self.cleanup_testfn() + sys.argv = self.old_argv[:] def cleanup_testfn(self): path = test.test_support.TESTFN @@ -73,6 +76,23 @@ class CoreTestCase(unittest.TestCase): output = output[:-1] self.assertEqual(cwd, output) + def test_debug_mode(self): + # this covers the code called when DEBUG is set + sys.argv = ['setup.py', '--name'] + with captured_stdout() as stdout: + distutils.core.setup(name='bar') + stdout.seek(0) + self.assertEquals(stdout.read(), 'bar\n') + + distutils.core.DEBUG = True + try: + with captured_stdout() as stdout: + distutils.core.setup(name='bar') + finally: + distutils.core.DEBUG = False + stdout.seek(0) + wanted = "options (after parsing config files):\n" + self.assertEquals(stdout.readlines()[0], wanted) def test_suite(): return unittest.makeSuite(CoreTestCase) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index cf64c744..0cbb48bc 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -1,7 +1,10 @@ """Tests for distutils.filelist.""" from os.path import join import unittest +from test.test_support import captured_stdout + from distutils.filelist import glob_to_re, FileList +from distutils import debug MANIFEST_IN = """\ include ok @@ -59,6 +62,22 @@ class FileListTestCase(unittest.TestCase): self.assertEquals(file_list.files, wanted) + def test_debug_print(self): + file_list = FileList() + with captured_stdout() as stdout: + file_list.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + file_list.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), 'xxx\n') + finally: + debug.DEBUG = False + def test_suite(): return unittest.makeSuite(FileListTestCase) diff --git a/tests/test_install.py b/tests/test_install.py index d0ad5ce1..d2b8c8e9 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -6,6 +6,8 @@ import sys import unittest import site +from test.test_support import captured_stdout + from distutils.command.install import install from distutils.command import install as install_module from distutils.command.install import INSTALL_SCHEMES @@ -14,7 +16,6 @@ from distutils.errors import DistutilsOptionError from distutils.tests import support - class InstallTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -183,6 +184,17 @@ class InstallTestCase(support.TempdirManager, with open(cmd.record) as f: self.assertEquals(len(f.readlines()), 1) + def test_debug_mode(self): + # this covers the code called when DEBUG is set + old_logs_len = len(self.logs) + install_module.DEBUG = True + try: + with captured_stdout() as stdout: + self.test_record() + finally: + install_module.DEBUG = False + self.assertTrue(len(self.logs) > old_logs_len) + def test_suite(): return unittest.makeSuite(InstallTestCase) -- cgit v1.2.1 From f0f1b0325d3f06a06ab03c6912ea9743e9c30ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Sep 2009 13:01:54 +0000 Subject: Merged revisions 74988 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74988 | tarek.ziade | 2009-09-21 14:19:07 +0200 (Mon, 21 Sep 2009) | 1 line improved distutils test coverage: now the DEBUG mode is covered too (will help fix the issue #6954 in py3k branch) ........ --- cmd.py | 2 +- fancy_getopt.py | 6 +++--- tests/test_ccompiler.py | 24 +++++++++++++++++++++++- tests/test_cmd.py | 18 ++++++++++++++++++ tests/test_core.py | 20 ++++++++++++++++++++ tests/test_filelist.py | 19 +++++++++++++++++++ tests/test_install.py | 14 +++++++++++++- 7 files changed, 97 insertions(+), 6 deletions(-) diff --git a/cmd.py b/cmd.py index aff7d68e..ae4efc7e 100644 --- a/cmd.py +++ b/cmd.py @@ -157,7 +157,7 @@ class Command: self.announce(indent + header, level=log.INFO) indent = indent + " " for (option, _, _) in self.user_options: - option = longopt_xlate(option) + option = option.translate(longopt_xlate) if option[-1] == "=": option = option[:-1] value = getattr(self, option) diff --git a/fancy_getopt.py b/fancy_getopt.py index 72441fb4..879d4d25 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -26,7 +26,7 @@ neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) # This is used to translate long options to legitimate Python identifiers # (for use as attributes of some object). -longopt_xlate = lambda s: s.replace('-', '_') +longopt_xlate = str.maketrans('-', '_') class FancyGetopt: """Wrapper around the standard 'getopt()' module that provides some @@ -107,7 +107,7 @@ class FancyGetopt: """Translate long option name 'long_option' to the form it has as an attribute of some object: ie., translate hyphens to underscores.""" - return longopt_xlate(long_option) + return long_option.translate(longopt_xlate) def _check_alias_dict(self, aliases, what): assert isinstance(aliases, dict) @@ -432,7 +432,7 @@ def translate_longopt(opt): """Convert a long option name to a valid Python identifier by changing "-" to "_". """ - return longopt_xlate(opt) + return opt.translate(longopt_xlate) class OptionDummy: diff --git a/tests/test_ccompiler.py b/tests/test_ccompiler.py index 58c8c5da..9db8d246 100644 --- a/tests/test_ccompiler.py +++ b/tests/test_ccompiler.py @@ -1,8 +1,10 @@ """Tests for distutils.ccompiler.""" import os import unittest +from test.support import captured_stdout -from distutils.ccompiler import gen_lib_options +from distutils.ccompiler import gen_lib_options, CCompiler +from distutils import debug class FakeCompiler(object): def library_dir_option(self, dir): @@ -30,6 +32,26 @@ class CCompilerTestCase(unittest.TestCase): '-lname2'] self.assertEquals(opts, wanted) + def test_debug_print(self): + + class MyCCompiler(CCompiler): + executables = {} + + compiler = MyCCompiler() + with captured_stdout() as stdout: + compiler.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + compiler.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), 'xxx\n') + finally: + debug.DEBUG = False + def test_suite(): return unittest.makeSuite(CCompilerTestCase) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index d6438b5e..55ae421d 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,10 +1,12 @@ """Tests for distutils.cmd.""" import unittest import os +from test.support import captured_stdout from distutils.cmd import Command from distutils.dist import Distribution from distutils.errors import DistutilsOptionError +from distutils import debug class MyCmd(Command): def initialize_options(self): @@ -102,6 +104,22 @@ class CommandTestCase(unittest.TestCase): cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') + def test_debug_print(self): + cmd = self.cmd + with captured_stdout() as stdout: + cmd.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + cmd.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), 'xxx\n') + finally: + debug.DEBUG = False + def test_suite(): return unittest.makeSuite(CommandTestCase) diff --git a/tests/test_core.py b/tests/test_core.py index 7f021dcb..b5f391f5 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,6 +6,7 @@ import os import shutil import sys import test.support +from test.support import captured_stdout import unittest @@ -33,10 +34,12 @@ class CoreTestCase(unittest.TestCase): def setUp(self): self.old_stdout = sys.stdout self.cleanup_testfn() + self.old_argv = sys.argv[:] def tearDown(self): sys.stdout = self.old_stdout self.cleanup_testfn() + sys.argv = self.old_argv[:] def cleanup_testfn(self): path = test.support.TESTFN @@ -73,6 +76,23 @@ class CoreTestCase(unittest.TestCase): output = output[:-1] self.assertEqual(cwd, output) + def test_debug_mode(self): + # this covers the code called when DEBUG is set + sys.argv = ['setup.py', '--name'] + with captured_stdout() as stdout: + distutils.core.setup(name='bar') + stdout.seek(0) + self.assertEquals(stdout.read(), 'bar\n') + + distutils.core.DEBUG = True + try: + with captured_stdout() as stdout: + distutils.core.setup(name='bar') + finally: + distutils.core.DEBUG = False + stdout.seek(0) + wanted = "options (after parsing config files):\n" + self.assertEquals(stdout.readlines()[0], wanted) def test_suite(): return unittest.makeSuite(CoreTestCase) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index cf64c744..d98325ae 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -1,7 +1,10 @@ """Tests for distutils.filelist.""" from os.path import join import unittest +from test.support import captured_stdout + from distutils.filelist import glob_to_re, FileList +from distutils import debug MANIFEST_IN = """\ include ok @@ -59,6 +62,22 @@ class FileListTestCase(unittest.TestCase): self.assertEquals(file_list.files, wanted) + def test_debug_print(self): + file_list = FileList() + with captured_stdout() as stdout: + file_list.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + file_list.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), 'xxx\n') + finally: + debug.DEBUG = False + def test_suite(): return unittest.makeSuite(FileListTestCase) diff --git a/tests/test_install.py b/tests/test_install.py index d0ad5ce1..2087a0eb 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -6,6 +6,8 @@ import sys import unittest import site +from test.support import captured_stdout + from distutils.command.install import install from distutils.command import install as install_module from distutils.command.install import INSTALL_SCHEMES @@ -14,7 +16,6 @@ from distutils.errors import DistutilsOptionError from distutils.tests import support - class InstallTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -183,6 +184,17 @@ class InstallTestCase(support.TempdirManager, with open(cmd.record) as f: self.assertEquals(len(f.readlines()), 1) + def test_debug_mode(self): + # this covers the code called when DEBUG is set + old_logs_len = len(self.logs) + install_module.DEBUG = True + try: + with captured_stdout() as stdout: + self.test_record() + finally: + install_module.DEBUG = False + self.assertTrue(len(self.logs) > old_logs_len) + def test_suite(): return unittest.makeSuite(InstallTestCase) -- cgit v1.2.1 From ab2e256fe307738d2c0b4e9d4fa411b23f9be8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Sep 2009 13:10:05 +0000 Subject: Merged revisions 74990 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r74990 | tarek.ziade | 2009-09-21 15:01:54 +0200 (Mon, 21 Sep 2009) | 9 lines Merged revisions 74988 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74988 | tarek.ziade | 2009-09-21 14:19:07 +0200 (Mon, 21 Sep 2009) | 1 line improved distutils test coverage: now the DEBUG mode is covered too (will help fix the issue #6954 in py3k branch) ........ ................ --- cmd.py | 2 +- fancy_getopt.py | 6 +++--- tests/test_cmd.py | 18 ++++++++++++++++++ tests/test_core.py | 20 ++++++++++++++++++++ tests/test_filelist.py | 21 ++++++++++++++++++++- tests/test_install.py | 14 +++++++++++++- 6 files changed, 75 insertions(+), 6 deletions(-) diff --git a/cmd.py b/cmd.py index aff7d68e..ae4efc7e 100644 --- a/cmd.py +++ b/cmd.py @@ -157,7 +157,7 @@ class Command: self.announce(indent + header, level=log.INFO) indent = indent + " " for (option, _, _) in self.user_options: - option = longopt_xlate(option) + option = option.translate(longopt_xlate) if option[-1] == "=": option = option[:-1] value = getattr(self, option) diff --git a/fancy_getopt.py b/fancy_getopt.py index 72441fb4..879d4d25 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -26,7 +26,7 @@ neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) # This is used to translate long options to legitimate Python identifiers # (for use as attributes of some object). -longopt_xlate = lambda s: s.replace('-', '_') +longopt_xlate = str.maketrans('-', '_') class FancyGetopt: """Wrapper around the standard 'getopt()' module that provides some @@ -107,7 +107,7 @@ class FancyGetopt: """Translate long option name 'long_option' to the form it has as an attribute of some object: ie., translate hyphens to underscores.""" - return longopt_xlate(long_option) + return long_option.translate(longopt_xlate) def _check_alias_dict(self, aliases, what): assert isinstance(aliases, dict) @@ -432,7 +432,7 @@ def translate_longopt(opt): """Convert a long option name to a valid Python identifier by changing "-" to "_". """ - return longopt_xlate(opt) + return opt.translate(longopt_xlate) class OptionDummy: diff --git a/tests/test_cmd.py b/tests/test_cmd.py index d6438b5e..55ae421d 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,10 +1,12 @@ """Tests for distutils.cmd.""" import unittest import os +from test.support import captured_stdout from distutils.cmd import Command from distutils.dist import Distribution from distutils.errors import DistutilsOptionError +from distutils import debug class MyCmd(Command): def initialize_options(self): @@ -102,6 +104,22 @@ class CommandTestCase(unittest.TestCase): cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') + def test_debug_print(self): + cmd = self.cmd + with captured_stdout() as stdout: + cmd.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + cmd.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), 'xxx\n') + finally: + debug.DEBUG = False + def test_suite(): return unittest.makeSuite(CommandTestCase) diff --git a/tests/test_core.py b/tests/test_core.py index 7f021dcb..b5f391f5 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,6 +6,7 @@ import os import shutil import sys import test.support +from test.support import captured_stdout import unittest @@ -33,10 +34,12 @@ class CoreTestCase(unittest.TestCase): def setUp(self): self.old_stdout = sys.stdout self.cleanup_testfn() + self.old_argv = sys.argv[:] def tearDown(self): sys.stdout = self.old_stdout self.cleanup_testfn() + sys.argv = self.old_argv[:] def cleanup_testfn(self): path = test.support.TESTFN @@ -73,6 +76,23 @@ class CoreTestCase(unittest.TestCase): output = output[:-1] self.assertEqual(cwd, output) + def test_debug_mode(self): + # this covers the code called when DEBUG is set + sys.argv = ['setup.py', '--name'] + with captured_stdout() as stdout: + distutils.core.setup(name='bar') + stdout.seek(0) + self.assertEquals(stdout.read(), 'bar\n') + + distutils.core.DEBUG = True + try: + with captured_stdout() as stdout: + distutils.core.setup(name='bar') + finally: + distutils.core.DEBUG = False + stdout.seek(0) + wanted = "options (after parsing config files):\n" + self.assertEquals(stdout.readlines()[0], wanted) def test_suite(): return unittest.makeSuite(CoreTestCase) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 86db5574..b9db8f61 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -1,6 +1,9 @@ """Tests for distutils.filelist.""" import unittest -from distutils.filelist import glob_to_re + +from distutils.filelist import glob_to_re, FileList +from test.support import captured_stdout +from distutils import debug class FileListTestCase(unittest.TestCase): @@ -16,6 +19,22 @@ class FileListTestCase(unittest.TestCase): self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]$') self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]$') + def test_debug_print(self): + file_list = FileList() + with captured_stdout() as stdout: + file_list.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + file_list.debug_print('xxx') + stdout.seek(0) + self.assertEquals(stdout.read(), 'xxx\n') + finally: + debug.DEBUG = False + def test_suite(): return unittest.makeSuite(FileListTestCase) diff --git a/tests/test_install.py b/tests/test_install.py index d0ad5ce1..2087a0eb 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -6,6 +6,8 @@ import sys import unittest import site +from test.support import captured_stdout + from distutils.command.install import install from distutils.command import install as install_module from distutils.command.install import INSTALL_SCHEMES @@ -14,7 +16,6 @@ from distutils.errors import DistutilsOptionError from distutils.tests import support - class InstallTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -183,6 +184,17 @@ class InstallTestCase(support.TempdirManager, with open(cmd.record) as f: self.assertEquals(len(f.readlines()), 1) + def test_debug_mode(self): + # this covers the code called when DEBUG is set + old_logs_len = len(self.logs) + install_module.DEBUG = True + try: + with captured_stdout() as stdout: + self.test_record() + finally: + install_module.DEBUG = False + self.assertTrue(len(self.logs) > old_logs_len) + def test_suite(): return unittest.makeSuite(InstallTestCase) -- cgit v1.2.1 From 0610698781fd21cdc3dd80eb45640e6da374a1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Sep 2009 13:23:35 +0000 Subject: improving distutils coverage --- tests/test_dist.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_dist.py b/tests/test_dist.py index 75b74a2c..553f30c7 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -9,6 +9,7 @@ import warnings from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command +import distutils.dist from test.test_support import TESTFN, captured_stdout from distutils.tests import support @@ -56,6 +57,27 @@ class DistributionTestCase(support.TempdirManager, d.parse_command_line() return d + def test_debug_mode(self): + with open(TESTFN, "w") as f: + f.write("[global]") + f.write("command_packages = foo.bar, splat") + + files = [TESTFN] + sys.argv.append("build") + + with captured_stdout() as stdout: + self.create_distribution(files) + stdout.seek(0) + self.assertEquals(stdout.read(), '') + distutils.dist.DEBUG = True + try: + with captured_stdout() as stdout: + self.create_distribution(files) + stdout.seek(0) + self.assertEquals(stdout.read(), '') + finally: + distutils.dist.DEBUG = False + def test_command_packages_unspecified(self): sys.argv.append("build") d = self.create_distribution() -- cgit v1.2.1 From 142d88152055c80f02ee268f73aca04424c8e676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Sep 2009 13:41:08 +0000 Subject: #6954: Fixed crash when using DISTUTILS_DEBUG flag in Distutils. --- dist.py | 2 +- log.py | 3 +++ tests/test_dist.py | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index afed545d..f49afcce 100644 --- a/dist.py +++ b/dist.py @@ -359,7 +359,7 @@ Common commands: (see '--help-commands' for more) parser = ConfigParser() for filename in filenames: if DEBUG: - self.announce(" reading", filename) + self.announce(" reading %s" % filename) parser.read(filename) for section in parser.sections(): options = parser.options(section) diff --git a/log.py b/log.py index 6f949d51..75885708 100644 --- a/log.py +++ b/log.py @@ -17,6 +17,9 @@ class Log: self.threshold = threshold def _log(self, level, msg, args): + if level not in (DEBUG, INFO, WARN, ERROR, FATAL): + raise ValueError('%s wrong log level' % str(level)) + if level >= self.threshold: if args: msg = msg % args diff --git a/tests/test_dist.py b/tests/test_dist.py index 553f30c7..3d4b25f0 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -200,6 +200,13 @@ class DistributionTestCase(support.TempdirManager, self.assertEquals(cmds, ['distutils.command', 'one', 'two']) + def test_announce(self): + # make sure the level is known + dist = Distribution() + args = ('ok',) + kwargs = {'level': 'ok2'} + self.assertRaises(ValueError, dist.announce, args, kwargs) + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): -- cgit v1.2.1 From 5b7317782101927604eb838784b8aab23c611b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Sep 2009 13:43:09 +0000 Subject: Merged revisions 74992 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74992 | tarek.ziade | 2009-09-21 15:23:35 +0200 (Mon, 21 Sep 2009) | 1 line improving distutils coverage ........ --- tests/test_dist.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_dist.py b/tests/test_dist.py index 9f795f43..91297f0e 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,6 +8,7 @@ import warnings from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command +import distutils.dist from test.support import TESTFN, captured_stdout from distutils.tests import support @@ -55,6 +56,27 @@ class DistributionTestCase(support.LoggingSilencer, d.parse_command_line() return d + def test_debug_mode(self): + with open(TESTFN, "w") as f: + f.write("[global]") + f.write("command_packages = foo.bar, splat") + + files = [TESTFN] + sys.argv.append("build") + + with captured_stdout() as stdout: + self.create_distribution(files) + stdout.seek(0) + self.assertEquals(stdout.read(), '') + distutils.dist.DEBUG = True + try: + with captured_stdout() as stdout: + self.create_distribution(files) + stdout.seek(0) + self.assertEquals(stdout.read(), '') + finally: + distutils.dist.DEBUG = False + def test_command_packages_unspecified(self): sys.argv.append("build") d = self.create_distribution() -- cgit v1.2.1 From b620521f2e5b41994c054ca0ab7a361a41dab8bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Sep 2009 13:49:57 +0000 Subject: forgot to commit a file in previous commit (r74994, issue #6954) --- tests/support.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/support.py b/tests/support.py index bce03494..3c328cc9 100644 --- a/tests/support.py +++ b/tests/support.py @@ -4,6 +4,7 @@ import shutil import tempfile from distutils import log +from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution from test.test_support import EnvironmentVarGuard @@ -25,6 +26,8 @@ class LoggingSilencer(object): super(LoggingSilencer, self).tearDown() def _log(self, level, msg, args): + if level not in (DEBUG, INFO, WARN, ERROR, FATAL): + raise ValueError('%s wrong log level' % str(level)) self.logs.append((level, msg, args)) def get_logs(self, *levels): -- cgit v1.2.1 From 6d4d4281a733ee9b1c6d622be50b14879947a3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Sep 2009 13:55:19 +0000 Subject: Merged revisions 74994,74997 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74994 | tarek.ziade | 2009-09-21 15:41:08 +0200 (Mon, 21 Sep 2009) | 1 line #6954: Fixed crash when using DISTUTILS_DEBUG flag in Distutils. ........ r74997 | tarek.ziade | 2009-09-21 15:49:57 +0200 (Mon, 21 Sep 2009) | 1 line forgot to commit a file in previous commit (r74994, issue #6954) ........ --- dist.py | 2 +- log.py | 3 +++ tests/support.py | 3 +++ tests/test_dist.py | 7 +++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index ac5a0ca0..1c1ea477 100644 --- a/dist.py +++ b/dist.py @@ -354,7 +354,7 @@ Common commands: (see '--help-commands' for more) parser = ConfigParser() for filename in filenames: if DEBUG: - self.announce(" reading", filename) + self.announce(" reading %s" % filename) parser.read(filename) for section in parser.sections(): options = parser.options(section) diff --git a/log.py b/log.py index 6f949d51..75885708 100644 --- a/log.py +++ b/log.py @@ -17,6 +17,9 @@ class Log: self.threshold = threshold def _log(self, level, msg, args): + if level not in (DEBUG, INFO, WARN, ERROR, FATAL): + raise ValueError('%s wrong log level' % str(level)) + if level >= self.threshold: if args: msg = msg % args diff --git a/tests/support.py b/tests/support.py index 12554139..ea122111 100644 --- a/tests/support.py +++ b/tests/support.py @@ -4,6 +4,7 @@ import shutil import tempfile from distutils import log +from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution from test.support import EnvironmentVarGuard @@ -25,6 +26,8 @@ class LoggingSilencer(object): super().tearDown() def _log(self, level, msg, args): + if level not in (DEBUG, INFO, WARN, ERROR, FATAL): + raise ValueError('%s wrong log level' % str(level)) self.logs.append((level, msg, args)) def get_logs(self, *levels): diff --git a/tests/test_dist.py b/tests/test_dist.py index 91297f0e..799b0c06 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -171,6 +171,13 @@ class DistributionTestCase(support.LoggingSilencer, self.assertEquals(cmds, ['distutils.command', 'one', 'two']) + def test_announce(self): + # make sure the level is known + dist = Distribution() + args = ('ok',) + kwargs = {'level': 'ok2'} + self.assertRaises(ValueError, dist.announce, args, kwargs) + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): -- cgit v1.2.1 From b55f637193312bf8e9c13b1b5a2c421147db1281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Sep 2009 13:59:07 +0000 Subject: Merged revisions 74999 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r74999 | tarek.ziade | 2009-09-21 15:55:19 +0200 (Mon, 21 Sep 2009) | 13 lines Merged revisions 74994,74997 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74994 | tarek.ziade | 2009-09-21 15:41:08 +0200 (Mon, 21 Sep 2009) | 1 line #6954: Fixed crash when using DISTUTILS_DEBUG flag in Distutils. ........ r74997 | tarek.ziade | 2009-09-21 15:49:57 +0200 (Mon, 21 Sep 2009) | 1 line forgot to commit a file in previous commit (r74994, issue #6954) ........ ................ --- dist.py | 2 +- log.py | 3 +++ tests/support.py | 3 +++ tests/test_dist.py | 7 +++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index ac5a0ca0..1c1ea477 100644 --- a/dist.py +++ b/dist.py @@ -354,7 +354,7 @@ Common commands: (see '--help-commands' for more) parser = ConfigParser() for filename in filenames: if DEBUG: - self.announce(" reading", filename) + self.announce(" reading %s" % filename) parser.read(filename) for section in parser.sections(): options = parser.options(section) diff --git a/log.py b/log.py index 6f949d51..75885708 100644 --- a/log.py +++ b/log.py @@ -17,6 +17,9 @@ class Log: self.threshold = threshold def _log(self, level, msg, args): + if level not in (DEBUG, INFO, WARN, ERROR, FATAL): + raise ValueError('%s wrong log level' % str(level)) + if level >= self.threshold: if args: msg = msg % args diff --git a/tests/support.py b/tests/support.py index 12554139..ea122111 100644 --- a/tests/support.py +++ b/tests/support.py @@ -4,6 +4,7 @@ import shutil import tempfile from distutils import log +from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution from test.support import EnvironmentVarGuard @@ -25,6 +26,8 @@ class LoggingSilencer(object): super().tearDown() def _log(self, level, msg, args): + if level not in (DEBUG, INFO, WARN, ERROR, FATAL): + raise ValueError('%s wrong log level' % str(level)) self.logs.append((level, msg, args)) def get_logs(self, *levels): diff --git a/tests/test_dist.py b/tests/test_dist.py index 9f795f43..70c9ec50 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -149,6 +149,13 @@ class DistributionTestCase(support.LoggingSilencer, self.assertEquals(cmds, ['distutils.command', 'one', 'two']) + def test_announce(self): + # make sure the level is known + dist = Distribution() + args = ('ok',) + kwargs = {'level': 'ok2'} + self.assertRaises(ValueError, dist.announce, args, kwargs) + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): -- cgit v1.2.1 From b9c31d02ce197add682c4bb6ab7614f6bc9abf7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 22 Sep 2009 10:08:13 +0000 Subject: Merged revisions 74812 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74812 | ronald.oussoren | 2009-09-15 23:24:07 +0200 (Tue, 15 Sep 2009) | 3 lines Update distutils.util tests after my changes to --with-universal-archs ........ --- tests/test_util.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index e9065ff5..84caea5c 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -142,15 +142,35 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') + self.assertEquals(get_platform(), 'macosx-10.4-intel') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + self.assertEquals(get_platform(), 'macosx-10.4-fat3') + + get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') self.assertEquals(get_platform(), 'macosx-10.4-universal') - get_config_vars()['CFLAGS'] = ('-arch x86_64 -isysroot ' + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEquals(get_platform(), 'macosx-10.4-fat64') + for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): + get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3'%(arch,)) + + self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) + # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' -- cgit v1.2.1 From 99db2f54d353469fd4b48398994461efbbf61dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 22 Sep 2009 10:14:51 +0000 Subject: Merged revisions 75013 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r75013 | tarek.ziade | 2009-09-22 12:08:13 +0200 (Tue, 22 Sep 2009) | 10 lines Merged revisions 74812 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r74812 | ronald.oussoren | 2009-09-15 23:24:07 +0200 (Tue, 15 Sep 2009) | 3 lines Update distutils.util tests after my changes to --with-universal-archs ........ ................ --- tests/test_util.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index c0acf5f4..795edb8c 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -115,15 +115,35 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') + self.assertEquals(get_platform(), 'macosx-10.4-intel') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + self.assertEquals(get_platform(), 'macosx-10.4-fat3') + + get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') self.assertEquals(get_platform(), 'macosx-10.4-universal') - get_config_vars()['CFLAGS'] = ('-arch x86_64 -isysroot ' + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEquals(get_platform(), 'macosx-10.4-fat64') + for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): + get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3'%(arch,)) + + self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) + # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' -- cgit v1.2.1 From 4c66627839fa1384e17f427eebf17adbc807376d Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 22 Sep 2009 19:27:44 +0000 Subject: Half of the fix for issue 6957: ensure that distutils ignores the '-isysroot' option on OSX when the corresponding SDK is not installed. This ensures that the user can compile extensions on OSX 10.6 using the Python.org installer and a default installation of Xcode. --- sysconfig.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index dcc7231a..6ca6720a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -592,6 +592,29 @@ def get_config_vars(*args): flags = flags + ' ' + arch _config_vars[key] = flags + # If we're on OSX 10.5 or later and the user tries to + # compiles an extension using an SDK that is not present + # on the current machine it is better to not use an SDK + # than to fail. + # + # The major usecase for this is users using a Python.org + # binary installer on OSX 10.6: that installer uses + # the 10.4u SDK, but that SDK is not installed by default + # when you install Xcode. + # + m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS']) + if m is not None: + sdk = m.group(1) + if not os.path.exists(sdk): + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _config_vars[key] + flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) + _config_vars[key] = flags + if args: vals = [] for name in args: -- cgit v1.2.1 From f7a196298b22da695467cb9494743ce15683fca2 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 22 Sep 2009 19:31:34 +0000 Subject: Merged revisions 75022 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75022 | ronald.oussoren | 2009-09-22 21:27:44 +0200 (Tue, 22 Sep 2009) | 8 lines Half of the fix for issue 6957: ensure that distutils ignores the '-isysroot' option on OSX when the corresponding SDK is not installed. This ensures that the user can compile extensions on OSX 10.6 using the Python.org installer and a default installation of Xcode. ........ --- sysconfig.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index 6f99de28..8306202f 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -566,6 +566,29 @@ def get_config_vars(*args): flags = flags + ' ' + arch _config_vars[key] = flags + # If we're on OSX 10.5 or later and the user tries to + # compiles an extension using an SDK that is not present + # on the current machine it is better to not use an SDK + # than to fail. + # + # The major usecase for this is users using a Python.org + # binary installer on OSX 10.6: that installer uses + # the 10.4u SDK, but that SDK is not installed by default + # when you install Xcode. + # + m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS']) + if m is not None: + sdk = m.group(1) + if not os.path.exists(sdk): + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _config_vars[key] + flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) + _config_vars[key] = flags + if args: vals = [] for name in args: -- cgit v1.2.1 From 9c216a69630f8d8dfdf3365e5d26fe47b313b7ee Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 29 Sep 2009 22:41:09 +0000 Subject: Bumping for 2.6.3rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index ba926063..c8787040 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.2" +__version__ = "2.6.3rc1" #--end constants-- -- cgit v1.2.1 From 4d101962a4b4069c967df29bdea6e0be5e8071fd Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 1 Oct 2009 23:39:49 +0000 Subject: Bump to 2.6.3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c8787040..fd52b8d7 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.3rc1" +__version__ = "2.6.3" #--end constants-- -- cgit v1.2.1 From e63325c644493b2d2a9cb352ea8a254d96e9dacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 2 Oct 2009 22:37:51 +0000 Subject: fixed the distutils tests that were not writing in temp --- tests/test_config.py | 10 +++++----- tests/test_sdist.py | 42 +++++++++++++++++++----------------------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index cae76898..23291da8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -49,13 +49,14 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): """Patches the environment.""" + super(PyPIRCCommandTestCase, self).setUp() if os.environ.has_key('HOME'): self._old_home = os.environ['HOME'] else: self._old_home = None - curdir = os.path.dirname(__file__) - os.environ['HOME'] = curdir - self.rc = os.path.join(curdir, '.pypirc') + tempdir = self.mkdtemp() + os.environ['HOME'] = tempdir + self.rc = os.path.join(tempdir, '.pypirc') self.dist = Distribution() class command(PyPIRCCommand): @@ -74,9 +75,8 @@ class PyPIRCCommandTestCase(support.TempdirManager, unittest.TestCase): del os.environ['HOME'] else: os.environ['HOME'] = self._old_home - if os.path.exists(self.rc): - os.remove(self.rc) set_threshold(self.old_threshold) + super(PyPIRCCommandTestCase, self).tearDown() def test_server_registration(self): # This test makes sure PyPIRCCommand knows how to: diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 59c24fd4..e322c138 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -12,9 +12,6 @@ from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError from distutils.spawn import find_executable -CURDIR = os.path.dirname(__file__) -TEMP_PKG = join(CURDIR, 'temppkg') - SETUP_PY = """ from distutils.core import setup import somecode @@ -31,25 +28,24 @@ class sdistTestCase(PyPIRCCommandTestCase): def setUp(self): super(sdistTestCase, self).setUp() self.old_path = os.getcwd() + self.temp_pkg = os.path.join(self.mkdtemp(), 'temppkg') def tearDown(self): os.chdir(self.old_path) - if os.path.exists(TEMP_PKG): - shutil.rmtree(TEMP_PKG) super(sdistTestCase, self).tearDown() def _init_tmp_pkg(self): - if os.path.exists(TEMP_PKG): - shutil.rmtree(TEMP_PKG) - os.mkdir(TEMP_PKG) - os.mkdir(join(TEMP_PKG, 'somecode')) - os.mkdir(join(TEMP_PKG, 'dist')) + if os.path.exists(self.temp_pkg): + shutil.rmtree(self.temp_pkg) + os.mkdir(self.temp_pkg) + os.mkdir(join(self.temp_pkg, 'somecode')) + os.mkdir(join(self.temp_pkg, 'dist')) # creating a MANIFEST, a package, and a README - self._write(join(TEMP_PKG, 'MANIFEST.in'), MANIFEST_IN) - self._write(join(TEMP_PKG, 'README'), 'xxx') - self._write(join(TEMP_PKG, 'somecode', '__init__.py'), '#') - self._write(join(TEMP_PKG, 'setup.py'), SETUP_PY) - os.chdir(TEMP_PKG) + self._write(join(self.temp_pkg, 'MANIFEST.in'), MANIFEST_IN) + self._write(join(self.temp_pkg, 'README'), 'xxx') + self._write(join(self.temp_pkg, 'somecode', '__init__.py'), '#') + self._write(join(self.temp_pkg, 'setup.py'), SETUP_PY) + os.chdir(self.temp_pkg) def _write(self, path, content): f = open(path, 'w') @@ -65,15 +61,15 @@ class sdistTestCase(PyPIRCCommandTestCase): self._init_tmp_pkg() # creating VCS directories with some files in them - os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) - self._write(join(TEMP_PKG, 'somecode', '.svn', 'ok.py'), 'xxx') + os.mkdir(join(self.temp_pkg, 'somecode', '.svn')) + self._write(join(self.temp_pkg, 'somecode', '.svn', 'ok.py'), 'xxx') - os.mkdir(join(TEMP_PKG, 'somecode', '.hg')) - self._write(join(TEMP_PKG, 'somecode', '.hg', + os.mkdir(join(self.temp_pkg, 'somecode', '.hg')) + self._write(join(self.temp_pkg, 'somecode', '.hg', 'ok'), 'xxx') - os.mkdir(join(TEMP_PKG, 'somecode', '.git')) - self._write(join(TEMP_PKG, 'somecode', '.git', + os.mkdir(join(self.temp_pkg, 'somecode', '.git')) + self._write(join(self.temp_pkg, 'somecode', '.git', 'ok'), 'xxx') # now building a sdist @@ -96,7 +92,7 @@ class sdistTestCase(PyPIRCCommandTestCase): cmd.run() # now let's check what we have - dist_folder = join(TEMP_PKG, 'dist') + dist_folder = join(self.temp_pkg, 'dist') files = os.listdir(dist_folder) self.assertEquals(files, ['fake-1.0.zip']) @@ -137,7 +133,7 @@ class sdistTestCase(PyPIRCCommandTestCase): cmd.run() # making sure we have two files - dist_folder = join(TEMP_PKG, 'dist') + dist_folder = join(self.temp_pkg, 'dist') result = os.listdir(dist_folder) result.sort() self.assertEquals(result, -- cgit v1.2.1 From 55b356b15ea7b45da30f7147e7825e6c3bdd7e4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 2 Oct 2009 23:49:48 +0000 Subject: #6516 added owner/group support for tarfiles in Distutils --- archive_util.py | 72 ++++++++++++++++++++++++++++++++++++++++++---- cmd.py | 9 +++--- command/bdist.py | 13 +++++++++ command/bdist_dumb.py | 13 +++++++-- command/sdist.py | 9 +++++- tests/test_archive_util.py | 63 ++++++++++++++++++++++++++++++++++++++-- tests/test_sdist.py | 54 ++++++++++++++++++++++++++++++++++ 7 files changed, 218 insertions(+), 15 deletions(-) diff --git a/archive_util.py b/archive_util.py index 62150b0d..75318eba 100644 --- a/archive_util.py +++ b/archive_util.py @@ -14,15 +14,55 @@ from distutils.spawn import spawn from distutils.dir_util import mkpath from distutils import log -def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): +try: + from pwd import getpwnam +except AttributeError: + getpwnam = None + +try: + from grp import getgrnam +except AttributeError: + getgrnam = None + +def _get_gid(name): + """Returns a gid, given a group name.""" + if getgrnam is None or name is None: + return None + try: + result = getgrnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _get_uid(name): + """Returns an uid, given a user name.""" + if getpwnam is None or name is None: + return None + try: + result = getpwnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, + owner=None, group=None): """Create a (possibly compressed) tar file from all the files under 'base_dir'. 'compress' must be "gzip" (the default), "compress", "bzip2", or None. - Both "tar" and the compression utility named by 'compress' must be on - the default program search path, so this is probably Unix-specific. + (compress will be deprecated in Python 3.2) + + 'owner' and 'group' can be used to define an owner and a group for the + archive that is being built. If not provided, the current owner and group + will be used. + The output tar file will be named 'base_dir' + ".tar", possibly plus the appropriate compression extension (".gz", ".bz2" or ".Z"). + Returns the output filename. """ tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''} @@ -44,10 +84,23 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): import tarfile # late import so Python build itself doesn't break log.info('Creating tar archive') + + uid = _get_uid(owner) + gid = _get_gid(group) + + def _set_uid_gid(tarinfo): + if gid is not None: + tarinfo.gid = gid + tarinfo.gname = group + if uid is not None: + tarinfo.uid = uid + tarinfo.uname = owner + return tarinfo + if not dry_run: tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) try: - tar.add(base_dir) + tar.add(base_dir, filter=_set_uid_gid) finally: tar.close() @@ -138,7 +191,7 @@ def check_archive_formats(formats): return None def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, - dry_run=0): + dry_run=0, owner=None, group=None): """Create an archive file (eg. zip or tar). 'base_name' is the name of the file to create, minus any format-specific @@ -151,6 +204,9 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, ie. 'base_dir' will be the common prefix of all files and directories in the archive. 'root_dir' and 'base_dir' both default to the current directory. Returns the name of the archive file. + + 'owner' and 'group' are used when creating a tar archive. By default, + uses the current owner and group. """ save_cwd = os.getcwd() if root_dir is not None: @@ -172,8 +228,12 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, func = format_info[0] for arg, val in format_info[1]: kwargs[arg] = val - filename = apply(func, (base_name, base_dir), kwargs) + if format != 'zip': + kwargs['owner'] = owner + kwargs['group'] = group + + filename = apply(func, (base_name, base_dir), kwargs) if root_dir is not None: log.debug("changing back to '%s'", save_cwd) os.chdir(save_cwd) diff --git a/cmd.py b/cmd.py index 869258ba..dc40c14d 100644 --- a/cmd.py +++ b/cmd.py @@ -385,10 +385,11 @@ class Command: from distutils.spawn import spawn spawn(cmd, search_path, dry_run= self.dry_run) - def make_archive (self, base_name, format, - root_dir=None, base_dir=None): - return archive_util.make_archive( - base_name, format, root_dir, base_dir, dry_run=self.dry_run) + def make_archive(self, base_name, format, root_dir=None, base_dir=None, + owner=None, group=None): + return archive_util.make_archive(base_name, format, root_dir, + base_dir, dry_run=self.dry_run, + owner=owner, group=group) def make_file(self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): diff --git a/command/bdist.py b/command/bdist.py index ec3375e5..8e0ad080 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -40,6 +40,12 @@ class bdist(Command): "[default: dist]"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), ] boolean_options = ['skip-build'] @@ -81,6 +87,8 @@ class bdist(Command): self.formats = None self.dist_dir = None self.skip_build = 0 + self.group = None + self.owner = None def finalize_options(self): # have to finalize 'plat_name' before 'bdist_base' @@ -126,6 +134,11 @@ class bdist(Command): if cmd_name not in self.no_format_option: sub_cmd.format = self.formats[i] + # passing the owner and group names for tar archiving + if cmd_name == 'bdist_dumb': + sub_cmd.owner = self.owner + sub_cmd.group = self.group + # If we're going to need to run this command again, tell it to # keep its temporary files around so subsequent runs go faster. if cmd_name in commands[i+1:]: diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 3ab0330f..358a70ea 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -36,6 +36,12 @@ class bdist_dumb (Command): ('relative', None, "build the archive using relative paths" "(default: false)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), ] boolean_options = ['keep-temp', 'skip-build', 'relative'] @@ -53,6 +59,8 @@ class bdist_dumb (Command): self.dist_dir = None self.skip_build = 0 self.relative = 0 + self.owner = None + self.group = None def finalize_options(self): if self.bdist_dir is None: @@ -71,7 +79,7 @@ class bdist_dumb (Command): ('dist_dir', 'dist_dir'), ('plat_name', 'plat_name')) - def run (self): + def run(self): if not self.skip_build: self.run_command('build') @@ -110,7 +118,8 @@ class bdist_dumb (Command): # Make the archive filename = self.make_archive(pseudoinstall_root, - self.format, root_dir=archive_root) + self.format, root_dir=archive_root, + owner=self.owner, group=self.group) if self.distribution.has_ext_modules(): pyversion = get_python_version() else: diff --git a/command/sdist.py b/command/sdist.py index c21f3917..5c74e532 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -74,6 +74,10 @@ class sdist(Command): ('medata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), + ('owner=', 'u', + "Owner name used when creating a tar file [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file [default: current group]"), ] boolean_options = ['use-defaults', 'prune', @@ -113,6 +117,8 @@ class sdist(Command): self.archive_files = None self.metadata_check = 1 + self.owner = None + self.group = None def finalize_options(self): if self.manifest is None: @@ -455,7 +461,8 @@ class sdist(Command): self.formats.append(self.formats.pop(self.formats.index('tar'))) for fmt in self.formats: - file = self.make_archive(base_name, fmt, base_dir=base_dir) + file = self.make_archive(base_name, fmt, base_dir=base_dir, + owner=self.owner, group=self.group) archive_files.append(file) self.distribution.dist_files.append(('sdist', '', file)) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index d6fb6769..3faf5e0d 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -13,6 +13,13 @@ from distutils.spawn import find_executable, spawn from distutils.tests import support from test.test_support import check_warnings +try: + import grp + import pwd + UID_GID_SUPPORT = True +except ImportError: + UID_GID_SUPPORT = False + try: import zipfile ZIP_SUPPORT = True @@ -30,7 +37,7 @@ class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): - @unittest.skipUnless(zlib, "Requires zlib") + @unittest.skipUnless(zlib, "requires zlib") def test_make_tarball(self): # creating something to tar tmpdir = self.mkdtemp() @@ -41,7 +48,7 @@ class ArchiveUtilTestCase(support.TempdirManager, tmpdir2 = self.mkdtemp() unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], - "Source and target should be on same drive") + "source and target should be on same drive") base_name = os.path.join(tmpdir2, 'archive') @@ -202,6 +209,58 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir, 'archive') self.assertRaises(ValueError, make_archive, base_name, 'xxx') + def test_make_archive_owner_group(self): + # testing make_archive with owner and group, with various combinations + # this works even if there's not gid/uid support + if UID_GID_SUPPORT: + group = grp.getgrgid(0)[0] + owner = pwd.getpwuid(0)[0] + else: + group = owner = 'root' + + base_dir, root_dir, base_name = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, + group=group) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'zip', root_dir, base_dir) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'tar', root_dir, base_dir, + owner=owner, group=group) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'tar', root_dir, base_dir, + owner='kjhkjhkjg', group='oihohoh') + self.assertTrue(os.path.exists(res)) + + @unittest.skipUnless(zlib, "Requires zlib") + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + def test_tarfile_root_owner(self): + tmpdir, tmpdir2, base_name = self._create_files() + old_dir = os.getcwd() + os.chdir(tmpdir) + group = grp.getgrgid(0)[0] + owner = pwd.getpwuid(0)[0] + try: + archive_name = make_tarball(base_name, 'dist', compress=None, + owner=owner, group=group) + finally: + os.chdir(old_dir) + + # check if the compressed tarball was created + self.assertTrue(os.path.exists(archive_name)) + + # now checks the rights + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEquals(member.uid, 0) + self.assertEquals(member.gid, 0) + finally: + archive.close() + def test_suite(): return unittest.makeSuite(ArchiveUtilTestCase) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index c2feccb3..b8e3dca5 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -3,6 +3,7 @@ import os import unittest import shutil import zipfile +import tarfile # zlib is not used here, but if it's not available # the tests that use zipfile may fail @@ -11,6 +12,13 @@ try: except ImportError: zlib = None +try: + import grp + import pwd + UID_GID_SUPPORT = True +except ImportError: + UID_GID_SUPPORT = False + from os.path import join import sys import tempfile @@ -288,6 +296,52 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.formats = 'supazipa' self.assertRaises(DistutilsOptionError, cmd.finalize_options) + @unittest.skipUnless(zlib, "requires zlib") + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + def test_make_distribution_owner_group(self): + + # check if tar and gzip are installed + if (find_executable('tar') is None or + find_executable('gzip') is None): + return + + # now building a sdist + dist, cmd = self.get_cmd() + + # creating a gztar and specifying the owner+group + cmd.formats = ['gztar'] + cmd.owner = pwd.getpwuid(0)[0] + cmd.group = grp.getgrgid(0)[0] + cmd.ensure_finalized() + cmd.run() + + # making sure we have the good rights + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEquals(member.uid, 0) + self.assertEquals(member.gid, 0) + finally: + archive.close() + + # building a sdist again + dist, cmd = self.get_cmd() + + # creating a gztar + cmd.formats = ['gztar'] + cmd.ensure_finalized() + cmd.run() + + # making sure we have the good rights + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEquals(member.uid, os.getuid()) + self.assertEquals(member.gid, os.getgid()) + finally: + archive.close() def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From 68a24d4967c877b3127b41f5f18b49f1b135429a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 2 Oct 2009 23:56:02 +0000 Subject: Merged revisions 75192 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75192 | tarek.ziade | 2009-10-03 01:49:48 +0200 (Sat, 03 Oct 2009) | 1 line #6516 added owner/group support for tarfiles in Distutils ........ --- archive_util.py | 71 ++++++++++++++++++++++++++++++++++++++++++---- cmd.py | 8 ++++-- command/bdist.py | 13 +++++++++ command/bdist_dumb.py | 11 ++++++- command/sdist.py | 9 +++++- tests/test_archive_util.py | 63 ++++++++++++++++++++++++++++++++++++++-- tests/test_sdist.py | 54 +++++++++++++++++++++++++++++++++++ 7 files changed, 217 insertions(+), 12 deletions(-) diff --git a/archive_util.py b/archive_util.py index a568854e..d4525998 100644 --- a/archive_util.py +++ b/archive_util.py @@ -14,15 +14,55 @@ from distutils.spawn import spawn from distutils.dir_util import mkpath from distutils import log -def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): +try: + from pwd import getpwnam +except AttributeError: + getpwnam = None + +try: + from grp import getgrnam +except AttributeError: + getgrnam = None + +def _get_gid(name): + """Returns a gid, given a group name.""" + if getgrnam is None or name is None: + return None + try: + result = getgrnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _get_uid(name): + """Returns an uid, given a user name.""" + if getpwnam is None or name is None: + return None + try: + result = getpwnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, + owner=None, group=None): """Create a (possibly compressed) tar file from all the files under 'base_dir'. 'compress' must be "gzip" (the default), "compress", "bzip2", or None. - Both "tar" and the compression utility named by 'compress' must be on - the default program search path, so this is probably Unix-specific. + (compress will be deprecated in Python 3.2) + + 'owner' and 'group' can be used to define an owner and a group for the + archive that is being built. If not provided, the current owner and group + will be used. + The output tar file will be named 'base_dir' + ".tar", possibly plus the appropriate compression extension (".gz", ".bz2" or ".Z"). + Returns the output filename. """ tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''} @@ -44,10 +84,23 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): import tarfile # late import so Python build itself doesn't break log.info('Creating tar archive') + + uid = _get_uid(owner) + gid = _get_gid(group) + + def _set_uid_gid(tarinfo): + if gid is not None: + tarinfo.gid = gid + tarinfo.gname = group + if uid is not None: + tarinfo.uid = uid + tarinfo.uname = owner + return tarinfo + if not dry_run: tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) try: - tar.add(base_dir) + tar.add(base_dir, filter=_set_uid_gid) finally: tar.close() @@ -137,7 +190,7 @@ def check_archive_formats(formats): return None def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, - dry_run=0): + dry_run=0, owner=None, group=None): """Create an archive file (eg. zip or tar). 'base_name' is the name of the file to create, minus any format-specific @@ -150,6 +203,9 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, ie. 'base_dir' will be the common prefix of all files and directories in the archive. 'root_dir' and 'base_dir' both default to the current directory. Returns the name of the archive file. + + 'owner' and 'group' are used when creating a tar archive. By default, + uses the current owner and group. """ save_cwd = os.getcwd() if root_dir is not None: @@ -171,6 +227,11 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, func = format_info[0] for arg, val in format_info[1]: kwargs[arg] = val + + if format != 'zip': + kwargs['owner'] = owner + kwargs['group'] = group + filename = func(base_name, base_dir, **kwargs) if root_dir is not None: diff --git a/cmd.py b/cmd.py index ae4efc7e..08632099 100644 --- a/cmd.py +++ b/cmd.py @@ -367,9 +367,11 @@ class Command: from distutils.spawn import spawn spawn(cmd, search_path, dry_run=self.dry_run) - def make_archive(self, base_name, format, root_dir=None, base_dir=None): - return archive_util.make_archive(base_name, format, root_dir, base_dir, - dry_run=self.dry_run) + def make_archive(self, base_name, format, root_dir=None, base_dir=None, + owner=None, group=None): + return archive_util.make_archive(base_name, format, root_dir, + base_dir, dry_run=self.dry_run, + owner=owner, group=group) def make_file(self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): diff --git a/command/bdist.py b/command/bdist.py index 1a360b59..2c81f3a9 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -39,6 +39,12 @@ class bdist(Command): "[default: dist]"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), ] boolean_options = ['skip-build'] @@ -80,6 +86,8 @@ class bdist(Command): self.formats = None self.dist_dir = None self.skip_build = 0 + self.group = None + self.owner = None def finalize_options(self): # have to finalize 'plat_name' before 'bdist_base' @@ -125,6 +133,11 @@ class bdist(Command): if cmd_name not in self.no_format_option: sub_cmd.format = self.formats[i] + # passing the owner and group names for tar archiving + if cmd_name == 'bdist_dumb': + sub_cmd.owner = self.owner + sub_cmd.group = self.group + # If we're going to need to run this command again, tell it to # keep its temporary files around so subsequent runs go faster. if cmd_name in commands[i+1:]: diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 63c0a475..49fd653a 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -36,6 +36,12 @@ class bdist_dumb(Command): ('relative', None, "build the archive using relative paths" "(default: false)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), ] boolean_options = ['keep-temp', 'skip-build', 'relative'] @@ -52,6 +58,8 @@ class bdist_dumb(Command): self.dist_dir = None self.skip_build = 0 self.relative = 0 + self.owner = None + self.group = None def finalize_options(self): if self.bdist_dir is None: @@ -109,7 +117,8 @@ class bdist_dumb(Command): # Make the archive filename = self.make_archive(pseudoinstall_root, - self.format, root_dir=archive_root) + self.format, root_dir=archive_root, + owner=self.owner, group=self.group) if self.distribution.has_ext_modules(): pyversion = get_python_version() else: diff --git a/command/sdist.py b/command/sdist.py index ace9eeeb..76e1de81 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -75,6 +75,10 @@ class sdist(Command): ('medata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), + ('owner=', 'u', + "Owner name used when creating a tar file [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file [default: current group]"), ] boolean_options = ['use-defaults', 'prune', @@ -114,6 +118,8 @@ class sdist(Command): self.archive_files = None self.metadata_check = 1 + self.owner = None + self.group = None def finalize_options(self): if self.manifest is None: @@ -449,7 +455,8 @@ class sdist(Command): self.formats.append(self.formats.pop(self.formats.index('tar'))) for fmt in self.formats: - file = self.make_archive(base_name, fmt, base_dir=base_dir) + file = self.make_archive(base_name, fmt, base_dir=base_dir, + owner=self.owner, group=self.group) archive_files.append(file) self.distribution.dist_files.append(('sdist', '', file)) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index d33b7e15..9990df36 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -13,6 +13,13 @@ from distutils.spawn import find_executable, spawn from distutils.tests import support from test.support import check_warnings +try: + import grp + import pwd + UID_GID_SUPPORT = True +except ImportError: + UID_GID_SUPPORT = False + try: import zipfile ZIP_SUPPORT = True @@ -30,7 +37,7 @@ class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): - @unittest.skipUnless(zlib, "Requires zlib") + @unittest.skipUnless(zlib, "requires zlib") def test_make_tarball(self): # creating something to tar tmpdir = self.mkdtemp() @@ -41,7 +48,7 @@ class ArchiveUtilTestCase(support.TempdirManager, tmpdir2 = self.mkdtemp() unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], - "Source and target should be on same drive") + "source and target should be on same drive") base_name = os.path.join(tmpdir2, 'archive') @@ -202,6 +209,58 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir, 'archive') self.assertRaises(ValueError, make_archive, base_name, 'xxx') + def test_make_archive_owner_group(self): + # testing make_archive with owner and group, with various combinations + # this works even if there's not gid/uid support + if UID_GID_SUPPORT: + group = grp.getgrgid(0)[0] + owner = pwd.getpwuid(0)[0] + else: + group = owner = 'root' + + base_dir, root_dir, base_name = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, + group=group) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'zip', root_dir, base_dir) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'tar', root_dir, base_dir, + owner=owner, group=group) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'tar', root_dir, base_dir, + owner='kjhkjhkjg', group='oihohoh') + self.assertTrue(os.path.exists(res)) + + @unittest.skipUnless(zlib, "Requires zlib") + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + def test_tarfile_root_owner(self): + tmpdir, tmpdir2, base_name = self._create_files() + old_dir = os.getcwd() + os.chdir(tmpdir) + group = grp.getgrgid(0)[0] + owner = pwd.getpwuid(0)[0] + try: + archive_name = make_tarball(base_name, 'dist', compress=None, + owner=owner, group=group) + finally: + os.chdir(old_dir) + + # check if the compressed tarball was created + self.assertTrue(os.path.exists(archive_name)) + + # now checks the rights + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEquals(member.uid, 0) + self.assertEquals(member.gid, 0) + finally: + archive.close() + def test_suite(): return unittest.makeSuite(ArchiveUtilTestCase) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 986288e5..c10e973e 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -3,6 +3,7 @@ import os import unittest import shutil import zipfile +import tarfile # zlib is not used here, but if it's not available # the tests that use zipfile may fail @@ -11,6 +12,13 @@ try: except ImportError: zlib = None +try: + import grp + import pwd + UID_GID_SUPPORT = True +except ImportError: + UID_GID_SUPPORT = False + from os.path import join import sys import tempfile @@ -288,6 +296,52 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.formats = 'supazipa' self.assertRaises(DistutilsOptionError, cmd.finalize_options) + @unittest.skipUnless(zlib, "requires zlib") + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + def test_make_distribution_owner_group(self): + + # check if tar and gzip are installed + if (find_executable('tar') is None or + find_executable('gzip') is None): + return + + # now building a sdist + dist, cmd = self.get_cmd() + + # creating a gztar and specifying the owner+group + cmd.formats = ['gztar'] + cmd.owner = pwd.getpwuid(0)[0] + cmd.group = grp.getgrgid(0)[0] + cmd.ensure_finalized() + cmd.run() + + # making sure we have the good rights + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEquals(member.uid, 0) + self.assertEquals(member.gid, 0) + finally: + archive.close() + + # building a sdist again + dist, cmd = self.get_cmd() + + # creating a gztar + cmd.formats = ['gztar'] + cmd.ensure_finalized() + cmd.run() + + # making sure we have the good rights + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEquals(member.uid, os.getuid()) + self.assertEquals(member.gid, os.getgid()) + finally: + archive.close() def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From f6e2f956fb81947c730c6417d1d492210f2f2c32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 3 Oct 2009 00:07:35 +0000 Subject: removing the last remaning apply() calls --- archive_util.py | 2 +- dir_util.py | 2 +- filelist.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/archive_util.py b/archive_util.py index 75318eba..4ace7bdb 100644 --- a/archive_util.py +++ b/archive_util.py @@ -233,7 +233,7 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, kwargs['owner'] = owner kwargs['group'] = group - filename = apply(func, (base_name, base_dir), kwargs) + filename = func(base_name, base_dir, **kwargs) if root_dir is not None: log.debug("changing back to '%s'", save_cwd) os.chdir(save_cwd) diff --git a/dir_util.py b/dir_util.py index 55607e5b..3e2cd353 100644 --- a/dir_util.py +++ b/dir_util.py @@ -190,7 +190,7 @@ def remove_tree(directory, verbose=1, dry_run=0): _build_cmdtuple(directory, cmdtuples) for cmd in cmdtuples: try: - apply(cmd[0], (cmd[1],)) + cmd[0](cmd[1]) # remove dir from cache if it's already there abspath = os.path.abspath(cmd[1]) if abspath in _path_created: diff --git a/filelist.py b/filelist.py index 7cf05098..4aac6d39 100644 --- a/filelist.py +++ b/filelist.py @@ -61,7 +61,7 @@ class FileList: sortable_files.sort() self.files = [] for sort_tuple in sortable_files: - self.files.append(apply(os.path.join, sort_tuple)) + self.files.append(os.path.join(*sort_tuple)) # -- Other miscellaneous utility methods --------------------------- -- cgit v1.2.1 From 8a0d79621f41405e1315f39118fdb713809dbb4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 3 Oct 2009 01:21:38 +0000 Subject: Issue #7039: Fixed test_distutils when running tests on an installation with no build --- tests/test_sysconfig.py | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 9f139521..88f9ed86 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -30,30 +30,13 @@ class SysconfigTestCase(unittest.TestCase): sysconfig.get_python_lib(prefix=TESTFN)) def test_get_python_inc(self): - # The check for srcdir is copied from Python's setup.py, - # and is necessary to make this test pass when building - # Python in a directory other than the source directory. - (srcdir,) = sysconfig.get_config_vars('srcdir') - if not srcdir: - inc_dir = sysconfig.get_python_inc() - else: - # This test is not really a proper test: when building - # Python from source, even in the same directory, - # we won't be testing the same thing as when running - # distutils' tests on an installed Python. Nevertheless, - # let's try to do our best: if we are running Python's - # unittests from a build directory that is not the source - # directory, the normal inc_dir will exist, it will just not - # contain anything of interest. - inc_dir = sysconfig.get_python_inc() - self.assert_(os.path.isdir(inc_dir)) - # Now test the source location, to make sure Python.h does - # exist. - inc_dir = os.path.join(os.getcwd(), srcdir, 'Include') - inc_dir = os.path.normpath(inc_dir) - self.assert_(os.path.isdir(inc_dir), inc_dir) + inc_dir = sysconfig.get_python_inc() + # This is not much of a test. We make sure Python.h exists + # in the directory returned by get_python_inc() but we don't know + # it is the correct file. + self.assertTrue(os.path.isdir(inc_dir), inc_dir) python_h = os.path.join(inc_dir, "Python.h") - self.assert_(os.path.isfile(python_h), python_h) + self.assertTrue(os.path.isfile(python_h), python_h) def test_get_config_vars(self): cvars = sysconfig.get_config_vars() -- cgit v1.2.1 From c4b6ae4685dc181db793d5fff29b7cea8b7f3d79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 3 Oct 2009 14:52:33 +0000 Subject: now uses the right exception type --- archive_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archive_util.py b/archive_util.py index 4ace7bdb..bc5edfd8 100644 --- a/archive_util.py +++ b/archive_util.py @@ -16,12 +16,12 @@ from distutils import log try: from pwd import getpwnam -except AttributeError: +except ImportError: getpwnam = None try: from grp import getgrnam -except AttributeError: +except ImportError: getgrnam = None def _get_gid(name): -- cgit v1.2.1 From 50c7d916840891606674311b0643cd4a188b63c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 3 Oct 2009 14:54:15 +0000 Subject: Merged revisions 75209 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75209 | tarek.ziade | 2009-10-03 16:52:33 +0200 (Sat, 03 Oct 2009) | 1 line now uses the right exception type ........ --- archive_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archive_util.py b/archive_util.py index d4525998..d051f917 100644 --- a/archive_util.py +++ b/archive_util.py @@ -16,12 +16,12 @@ from distutils import log try: from pwd import getpwnam -except AttributeError: +except ImportError: getpwnam = None try: from grp import getgrnam -except AttributeError: +except ImportError: getgrnam = None def _get_gid(name): -- cgit v1.2.1 From 77aaa5d7d3d3183687745120cc24c88bd4eb8f0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 5 Oct 2009 17:35:51 +0000 Subject: Fixed #7064: making sure get_ext_filename is called as Setuptools is assuming so it doesn't break it --- command/build_ext.py | 3 +- tests/setuptools_build_ext.py | 287 ++++++++++++++++++++++++++++++++++++++++++ tests/setuptools_extension.py | 51 ++++++++ tests/test_build_ext.py | 20 +++ 4 files changed, 360 insertions(+), 1 deletion(-) create mode 100644 tests/setuptools_build_ext.py create mode 100644 tests/setuptools_extension.py diff --git a/command/build_ext.py b/command/build_ext.py index a3e3982f..1b7d3101 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -630,7 +630,8 @@ class build_ext (Command): """ fullname = self.get_ext_fullname(ext_name) modpath = fullname.split('.') - filename = self.get_ext_filename(modpath[-1]) + filename = self.get_ext_filename(ext_name) + filename = os.path.split(filename)[-1] if not self.inplace: # no further work needed diff --git a/tests/setuptools_build_ext.py b/tests/setuptools_build_ext.py new file mode 100644 index 00000000..21fa9e8f --- /dev/null +++ b/tests/setuptools_build_ext.py @@ -0,0 +1,287 @@ +from distutils.command.build_ext import build_ext as _du_build_ext +try: + # Attempt to use Pyrex for building extensions, if available + from Pyrex.Distutils.build_ext import build_ext as _build_ext +except ImportError: + _build_ext = _du_build_ext + +import os, sys +from distutils.file_util import copy_file + +from distutils.tests.setuptools_extension import Library + +from distutils.ccompiler import new_compiler +from distutils.sysconfig import customize_compiler, get_config_var +get_config_var("LDSHARED") # make sure _config_vars is initialized +from distutils.sysconfig import _config_vars +from distutils import log +from distutils.errors import * + +have_rtld = False +use_stubs = False +libtype = 'shared' + +if sys.platform == "darwin": + use_stubs = True +elif os.name != 'nt': + try: + from dl import RTLD_NOW + have_rtld = True + use_stubs = True + except ImportError: + pass + +def if_dl(s): + if have_rtld: + return s + return '' + + + + + + +class build_ext(_build_ext): + def run(self): + """Build extensions in build directory, then copy if --inplace""" + old_inplace, self.inplace = self.inplace, 0 + _build_ext.run(self) + self.inplace = old_inplace + if old_inplace: + self.copy_extensions_to_source() + + def copy_extensions_to_source(self): + build_py = self.get_finalized_command('build_py') + for ext in self.extensions: + fullname = self.get_ext_fullname(ext.name) + filename = self.get_ext_filename(fullname) + modpath = fullname.split('.') + package = '.'.join(modpath[:-1]) + package_dir = build_py.get_package_dir(package) + dest_filename = os.path.join(package_dir,os.path.basename(filename)) + src_filename = os.path.join(self.build_lib,filename) + + # Always copy, even if source is older than destination, to ensure + # that the right extensions for the current Python/platform are + # used. + copy_file( + src_filename, dest_filename, verbose=self.verbose, + dry_run=self.dry_run + ) + if ext._needs_stub: + self.write_stub(package_dir or os.curdir, ext, True) + + + if _build_ext is not _du_build_ext and not hasattr(_build_ext,'pyrex_sources'): + # Workaround for problems using some Pyrex versions w/SWIG and/or 2.4 + def swig_sources(self, sources, *otherargs): + # first do any Pyrex processing + sources = _build_ext.swig_sources(self, sources) or sources + # Then do any actual SWIG stuff on the remainder + return _du_build_ext.swig_sources(self, sources, *otherargs) + + + + def get_ext_filename(self, fullname): + filename = _build_ext.get_ext_filename(self,fullname) + ext = self.ext_map[fullname] + if isinstance(ext,Library): + fn, ext = os.path.splitext(filename) + return self.shlib_compiler.library_filename(fn,libtype) + elif use_stubs and ext._links_to_dynamic: + d,fn = os.path.split(filename) + return os.path.join(d,'dl-'+fn) + else: + return filename + + def initialize_options(self): + _build_ext.initialize_options(self) + self.shlib_compiler = None + self.shlibs = [] + self.ext_map = {} + + def finalize_options(self): + _build_ext.finalize_options(self) + self.extensions = self.extensions or [] + self.check_extensions_list(self.extensions) + self.shlibs = [ext for ext in self.extensions + if isinstance(ext,Library)] + if self.shlibs: + self.setup_shlib_compiler() + for ext in self.extensions: + ext._full_name = self.get_ext_fullname(ext.name) + for ext in self.extensions: + fullname = ext._full_name + self.ext_map[fullname] = ext + ltd = ext._links_to_dynamic = \ + self.shlibs and self.links_to_dynamic(ext) or False + ext._needs_stub = ltd and use_stubs and not isinstance(ext,Library) + filename = ext._file_name = self.get_ext_filename(fullname) + libdir = os.path.dirname(os.path.join(self.build_lib,filename)) + if ltd and libdir not in ext.library_dirs: + ext.library_dirs.append(libdir) + if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs: + ext.runtime_library_dirs.append(os.curdir) + + def setup_shlib_compiler(self): + compiler = self.shlib_compiler = new_compiler( + compiler=self.compiler, dry_run=self.dry_run, force=self.force + ) + if sys.platform == "darwin": + tmp = _config_vars.copy() + try: + # XXX Help! I don't have any idea whether these are right... + _config_vars['LDSHARED'] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup" + _config_vars['CCSHARED'] = " -dynamiclib" + _config_vars['SO'] = ".dylib" + customize_compiler(compiler) + finally: + _config_vars.clear() + _config_vars.update(tmp) + else: + customize_compiler(compiler) + + if self.include_dirs is not None: + compiler.set_include_dirs(self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name,value) in self.define: + compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + compiler.undefine_macro(macro) + if self.libraries is not None: + compiler.set_libraries(self.libraries) + if self.library_dirs is not None: + compiler.set_library_dirs(self.library_dirs) + if self.rpath is not None: + compiler.set_runtime_library_dirs(self.rpath) + if self.link_objects is not None: + compiler.set_link_objects(self.link_objects) + + # hack so distutils' build_extension() builds a library instead + compiler.link_shared_object = link_shared_object.__get__(compiler) + + + + def get_export_symbols(self, ext): + if isinstance(ext,Library): + return ext.export_symbols + return _build_ext.get_export_symbols(self,ext) + + def build_extension(self, ext): + _compiler = self.compiler + try: + if isinstance(ext,Library): + self.compiler = self.shlib_compiler + _build_ext.build_extension(self,ext) + if ext._needs_stub: + self.write_stub( + self.get_finalized_command('build_py').build_lib, ext + ) + finally: + self.compiler = _compiler + + def links_to_dynamic(self, ext): + """Return true if 'ext' links to a dynamic lib in the same package""" + # XXX this should check to ensure the lib is actually being built + # XXX as dynamic, and not just using a locally-found version or a + # XXX static-compiled version + libnames = dict.fromkeys([lib._full_name for lib in self.shlibs]) + pkg = '.'.join(ext._full_name.split('.')[:-1]+['']) + for libname in ext.libraries: + if pkg+libname in libnames: return True + return False + + def get_outputs(self): + outputs = _build_ext.get_outputs(self) + optimize = self.get_finalized_command('build_py').optimize + for ext in self.extensions: + if ext._needs_stub: + base = os.path.join(self.build_lib, *ext._full_name.split('.')) + outputs.append(base+'.py') + outputs.append(base+'.pyc') + if optimize: + outputs.append(base+'.pyo') + return outputs + + def write_stub(self, output_dir, ext, compile=False): + log.info("writing stub loader for %s to %s",ext._full_name, output_dir) + stub_file = os.path.join(output_dir, *ext._full_name.split('.'))+'.py' + if compile and os.path.exists(stub_file): + raise DistutilsError(stub_file+" already exists! Please delete.") + if not self.dry_run: + f = open(stub_file,'w') + f.write('\n'.join([ + "def __bootstrap__():", + " global __bootstrap__, __file__, __loader__", + " import sys, os, pkg_resources, imp"+if_dl(", dl"), + " __file__ = pkg_resources.resource_filename(__name__,%r)" + % os.path.basename(ext._file_name), + " del __bootstrap__", + " if '__loader__' in globals():", + " del __loader__", + if_dl(" old_flags = sys.getdlopenflags()"), + " old_dir = os.getcwd()", + " try:", + " os.chdir(os.path.dirname(__file__))", + if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), + " imp.load_dynamic(__name__,__file__)", + " finally:", + if_dl(" sys.setdlopenflags(old_flags)"), + " os.chdir(old_dir)", + "__bootstrap__()", + "" # terminal \n + ])) + f.close() + if compile: + from distutils.util import byte_compile + byte_compile([stub_file], optimize=0, + force=True, dry_run=self.dry_run) + optimize = self.get_finalized_command('install_lib').optimize + if optimize > 0: + byte_compile([stub_file], optimize=optimize, + force=True, dry_run=self.dry_run) + if os.path.exists(stub_file) and not self.dry_run: + os.unlink(stub_file) + + +if use_stubs or os.name=='nt': + # Build shared libraries + # + def link_shared_object(self, objects, output_libname, output_dir=None, + libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None + ): self.link( + self.SHARED_LIBRARY, objects, output_libname, + output_dir, libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, extra_preargs, extra_postargs, + build_temp, target_lang + ) +else: + # Build static libraries everywhere else + libtype = 'static' + + def link_shared_object(self, objects, output_libname, output_dir=None, + libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None + ): + # XXX we need to either disallow these attrs on Library instances, + # or warn/abort here if set, or something... + #libraries=None, library_dirs=None, runtime_library_dirs=None, + #export_symbols=None, extra_preargs=None, extra_postargs=None, + #build_temp=None + + assert output_dir is None # distutils build_ext doesn't pass this + output_dir,filename = os.path.split(output_libname) + basename, ext = os.path.splitext(filename) + if self.library_filename("x").startswith('lib'): + # strip 'lib' prefix; this is kludgy if some platform uses + # a different prefix + basename = basename[3:] + + self.create_static_lib( + objects, basename, output_dir, debug, target_lang + ) diff --git a/tests/setuptools_extension.py b/tests/setuptools_extension.py new file mode 100644 index 00000000..ec6b690c --- /dev/null +++ b/tests/setuptools_extension.py @@ -0,0 +1,51 @@ +from distutils.core import Extension as _Extension +from distutils.core import Distribution as _Distribution + +def _get_unpatched(cls): + """Protect against re-patching the distutils if reloaded + + Also ensures that no other distutils extension monkeypatched the distutils + first. + """ + while cls.__module__.startswith('setuptools'): + cls, = cls.__bases__ + if not cls.__module__.startswith('distutils'): + raise AssertionError( + "distutils has already been patched by %r" % cls + ) + return cls + +_Distribution = _get_unpatched(_Distribution) +_Extension = _get_unpatched(_Extension) + +try: + from Pyrex.Distutils.build_ext import build_ext +except ImportError: + have_pyrex = False +else: + have_pyrex = True + + +class Extension(_Extension): + """Extension that uses '.c' files in place of '.pyx' files""" + + if not have_pyrex: + # convert .pyx extensions to .c + def __init__(self,*args,**kw): + _Extension.__init__(self,*args,**kw) + sources = [] + for s in self.sources: + if s.endswith('.pyx'): + sources.append(s[:-3]+'c') + else: + sources.append(s) + self.sources = sources + +class Library(Extension): + """Just like a regular Extension, but built as a library instead""" + +import sys, distutils.core, distutils.extension +distutils.core.Extension = Extension +distutils.extension.Extension = Extension +if 'distutils.command.build_ext' in sys.modules: + sys.modules['distutils.command.build_ext'].Extension = Extension diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 94000a57..36e0bb84 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -13,6 +13,7 @@ from distutils.errors import DistutilsSetupError import unittest from test import test_support + # http://bugs.python.org/issue4373 # Don't load the xx module more than once. ALREADY_TESTED = False @@ -334,6 +335,25 @@ class BuildExtTestCase(support.TempdirManager, etree_ext = Extension('lxml.etree', [etree_c]) dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) cmd = build_ext(dist) + cmd.ensure_finalized() + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + ext = sysconfig.get_config_var("SO") + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + + def test_setuptools_compat(self): + from setuptools_build_ext import build_ext as setuptools_build_ext + from setuptools_extension import Extension + + etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + etree_ext = Extension('lxml.etree', [etree_c]) + dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + cmd = setuptools_build_ext(dist) + cmd.ensure_finalized() cmd.inplace = 1 cmd.distribution.package_dir = {'': 'src'} cmd.distribution.packages = ['lxml', 'lxml.html'] -- cgit v1.2.1 From f08d60462aba52ea189fe6b20741ee1c6fe58684 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 5 Oct 2009 22:32:48 +0000 Subject: Use standard comma punctuation; reword some sentences in the docs --- command/check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/check.py b/command/check.py index 7d56dfcf..bc29baab 100644 --- a/command/check.py +++ b/command/check.py @@ -91,7 +91,7 @@ class check(Command): missing.append(attr) if missing: - self.warn("missing required meta-data: %s" % ' ,'.join(missing)) + self.warn("missing required meta-data: %s" % ', '.join(missing)) if metadata.author: if not metadata.author_email: self.warn("missing meta-data: if 'author' supplied, " + -- cgit v1.2.1 From 82c7e63d170ca25fb083b729a1ebec9835010691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 6 Oct 2009 12:35:46 +0000 Subject: #7068: Fixed the partial renaming that occured in r72594 --- command/build_ext.py | 2 +- tests/test_build_ext.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 1b7d3101..da9cbfd5 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -303,7 +303,7 @@ class build_ext (Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler(compiler=None, + self.compiler = new_compiler(compiler=self.compiler, verbose=self.verbose, dry_run=self.dry_run, force=self.force) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 36e0bb84..00733a46 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -264,7 +264,7 @@ class BuildExtTestCase(support.TempdirManager, sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, other_tmp_dir) - + cmd.compiler = None cmd.inplace = 0 cmd.run() so_file = cmd.get_outputs()[0] -- cgit v1.2.1 From 7bbdd446ed91b60cfb54abb7e2a38b5657253759 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 6 Oct 2009 13:21:07 +0000 Subject: Bumping to 2.6.4rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index fd52b8d7..83c576f8 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.3" +__version__ = "2.6.4rc1" #--end constants-- -- cgit v1.2.1 From c048eb0e670dc6081c319ff83357d7a676dde723 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 9 Oct 2009 22:05:45 +0000 Subject: replace callable() --- dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.py b/dist.py index f49afcce..eb7d01c0 100644 --- a/dist.py +++ b/dist.py @@ -545,7 +545,7 @@ Common commands: (see '--help-commands' for more) for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): help_option_found=1 - if callable(func): + if hasattr(func, '__call__'): func() else: raise DistutilsClassError( -- cgit v1.2.1 From c22811e5e0f706060027a9fb4bc66a11926c4b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 12 Oct 2009 22:38:34 +0000 Subject: Fixed #7115: using paths instead of dotted name for extensions works too in distutils.command.build_ext --- command/build_ext.py | 2 ++ tests/test_build_ext.py | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index da9cbfd5..abc15841 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -628,6 +628,8 @@ class build_ext (Command): The file is located in `build_lib` or directly in the package (inplace option). """ + if os.sep in ext_name: + ext_name = ext_name.replace(os.sep, '.') fullname = self.get_ext_fullname(ext_name) modpath = fullname.split('.') filename = self.get_ext_filename(ext_name) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 00733a46..5dffe2ca 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -363,6 +363,16 @@ class BuildExtTestCase(support.TempdirManager, path = cmd.get_ext_fullpath('lxml.etree') self.assertEquals(wanted, path) + def test_build_ext_path_with_os_sep(self): + dist = Distribution({'name': 'UpdateManager'}) + cmd = build_ext(dist) + cmd.ensure_finalized() + ext = sysconfig.get_config_var("SO") + ext_name = os.path.join('UpdateManager', 'fdsend') + ext_path = cmd.get_ext_fullpath(ext_name) + wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext) + self.assertEquals(ext_path, wanted) + def test_suite(): if not sysconfig.python_build: if test_support.verbose: -- cgit v1.2.1 From 6d9f15b9f35719b016c34d96f2c23e05e1a0e1f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 13 Oct 2009 21:17:34 +0000 Subject: complementary fix for #7115 --- command/build_ext.py | 6 ++++-- tests/test_build_ext.py | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index abc15841..8248089f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -628,8 +628,10 @@ class build_ext (Command): The file is located in `build_lib` or directly in the package (inplace option). """ - if os.sep in ext_name: - ext_name = ext_name.replace(os.sep, '.') + # makes sure the extension name is only using dots + all_dots = string.maketrans('/'+os.sep, '..') + ext_name = ext_name.translate(all_dots) + fullname = self.get_ext_fullname(ext_name) modpath = fullname.split('.') filename = self.get_ext_filename(ext_name) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 5dffe2ca..93d18814 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -373,6 +373,19 @@ class BuildExtTestCase(support.TempdirManager, wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext) self.assertEquals(ext_path, wanted) + def test_build_ext_path_cross_platform(self): + if sys.platform != 'win32': + return + dist = Distribution({'name': 'UpdateManager'}) + cmd = build_ext(dist) + cmd.ensure_finalized() + ext = sysconfig.get_config_var("SO") + # this needs to work even under win32 + ext_name = 'UpdateManager/fdsend' + ext_path = cmd.get_ext_fullpath(ext_name) + wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext) + self.assertEquals(ext_path, wanted) + def test_suite(): if not sysconfig.python_build: if test_support.verbose: -- cgit v1.2.1 From e9ef9f712767530e054cdea670a744ee96f25695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 16 Oct 2009 23:04:16 +0000 Subject: this test requires zlib support --- tests/test_archive_util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 3faf5e0d..b91986ba 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -209,6 +209,7 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir, 'archive') self.assertRaises(ValueError, make_archive, base_name, 'xxx') + @unittest.skipUnless(zlib, "Requires zlib") def test_make_archive_owner_group(self): # testing make_archive with owner and group, with various combinations # this works even if there's not gid/uid support -- cgit v1.2.1 From f11e9a88c284209f037ae002fa00133833d48921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 16 Oct 2009 23:07:19 +0000 Subject: Merged revisions 75450 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75450 | tarek.ziade | 2009-10-17 01:04:16 +0200 (Sat, 17 Oct 2009) | 1 line this test requires zlib support ........ --- tests/test_archive_util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 9990df36..71d32dce 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -209,6 +209,7 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir, 'archive') self.assertRaises(ValueError, make_archive, base_name, 'xxx') + @unittest.skipUnless(zlib, "Requires zlib") def test_make_archive_owner_group(self): # testing make_archive with owner and group, with various combinations # this works even if there's not gid/uid support -- cgit v1.2.1 From 8a3f574e110a28ee9b3feab1bec433bdcaf5368f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 18 Oct 2009 09:28:26 +0000 Subject: Changed distutils tests to avoid environment alteration --- tests/support.py | 13 ++++++++++--- tests/test_bdist_dumb.py | 6 ++++-- tests/test_bdist_rpm.py | 5 +++-- tests/test_build_ext.py | 5 +++-- tests/test_config.py | 2 +- tests/test_core.py | 11 +++++++---- tests/test_dist.py | 31 +++++++++++++++++++------------ tests/test_install.py | 1 + tests/test_install_data.py | 1 + tests/test_install_headers.py | 1 + tests/test_install_lib.py | 2 +- tests/test_sysconfig.py | 12 ++++++++++-- tests/test_util.py | 11 ++++++----- 13 files changed, 67 insertions(+), 34 deletions(-) diff --git a/tests/support.py b/tests/support.py index 3c328cc9..41d3cd3c 100644 --- a/tests/support.py +++ b/tests/support.py @@ -2,11 +2,11 @@ import os import shutil import tempfile +from copy import deepcopy from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution -from test.test_support import EnvironmentVarGuard class LoggingSilencer(object): @@ -111,8 +111,15 @@ class EnvironGuard(object): def setUp(self): super(EnvironGuard, self).setUp() - self.environ = EnvironmentVarGuard() + self.old_environ = deepcopy(os.environ) def tearDown(self): - self.environ.__exit__() + for key, value in self.old_environ.items(): + if os.environ.get(key) != value: + os.environ[key] = value + + for key in os.environ.keys(): + if key not in self.old_environ: + del os.environ[key] + super(EnvironGuard, self).tearDown() diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index a838f734..5eaef2a9 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -26,16 +26,18 @@ setup(name='foo', version='0.1', py_modules=['foo'], class BuildDumbTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def setUp(self): super(BuildDumbTestCase, self).setUp() self.old_location = os.getcwd() - self.old_sys_argv = sys.argv[:] + self.old_sys_argv = sys.argv, sys.argv[:] def tearDown(self): os.chdir(self.old_location) - sys.argv = self.old_sys_argv[:] + sys.argv = self.old_sys_argv[0] + sys.argv[:] = self.old_sys_argv[1] super(BuildDumbTestCase, self).tearDown() @unittest.skipUnless(zlib, "requires zlib") diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index c2715675..2aa257f7 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -29,11 +29,12 @@ class BuildRpmTestCase(support.TempdirManager, def setUp(self): super(BuildRpmTestCase, self).setUp() self.old_location = os.getcwd() - self.old_sys_argv = sys.argv[:] + self.old_sys_argv = sys.argv, sys.argv[:] def tearDown(self): os.chdir(self.old_location) - sys.argv = self.old_sys_argv[:] + sys.argv = self.old_sys_argv[0] + sys.argv[:] = self.old_sys_argv[1] super(BuildRpmTestCase, self).tearDown() def test_quiet(self): diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 4c09dd80..317ce2bc 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -34,7 +34,7 @@ class BuildExtTestCase(support.TempdirManager, # Note that we're making changes to sys.path super(BuildExtTestCase, self).setUp() self.tmp_dir = self.mkdtemp() - self.sys_path = sys.path[:] + self.sys_path = sys.path, sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) if sys.version > "2.6": @@ -89,7 +89,8 @@ class BuildExtTestCase(support.TempdirManager, def tearDown(self): # Get everything back to normal test_support.unload('xx') - sys.path = self.sys_path + sys.path = self.sys_path[0] + sys.path[:] = self.sys_path[1] if sys.version > "2.6": import site site.USER_BASE = self.old_user_base diff --git a/tests/test_config.py b/tests/test_config.py index 69472755..0a1bb961 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -56,7 +56,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, """Patches the environment.""" super(PyPIRCCommandTestCase, self).setUp() self.tmp_dir = self.mkdtemp() - self.environ['HOME'] = self.tmp_dir + os.environ['HOME'] = self.tmp_dir self.rc = os.path.join(self.tmp_dir, '.pypirc') self.dist = Distribution() diff --git a/tests/test_core.py b/tests/test_core.py index 3c26fddb..48ec1b43 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8,7 +8,7 @@ import sys import test.test_support from test.test_support import captured_stdout import unittest - +from distutils.tests import support # setup script that uses __file__ setup_using___file__ = """\ @@ -29,17 +29,20 @@ setup() """ -class CoreTestCase(unittest.TestCase): +class CoreTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): + super(CoreTestCase, self).setUp() self.old_stdout = sys.stdout self.cleanup_testfn() - self.old_argv = sys.argv[:] + self.old_argv = sys.argv, sys.argv[:] def tearDown(self): sys.stdout = self.old_stdout self.cleanup_testfn() - sys.argv = self.old_argv[:] + sys.argv = self.old_argv[0] + sys.argv[:] = self.old_argv[1] + super(CoreTestCase, self).tearDown() def cleanup_testfn(self): path = test.test_support.TESTFN diff --git a/tests/test_dist.py b/tests/test_dist.py index 3d4b25f0..5f032ca6 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -39,15 +39,17 @@ class TestDistribution(Distribution): class DistributionTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def setUp(self): super(DistributionTestCase, self).setUp() - self.argv = sys.argv[:] + self.argv = sys.argv, sys.argv[:] del sys.argv[1:] def tearDown(self): - sys.argv[:] = self.argv + sys.argv = self.argv[0] + sys.argv[:] = self.argv[1] super(DistributionTestCase, self).tearDown() def create_distribution(self, configfiles=()): @@ -210,6 +212,15 @@ class DistributionTestCase(support.TempdirManager, class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): + def setUp(self): + super(MetadataTestCase, self).setUp() + self.argv = sys.argv, sys.argv[:] + + def tearDown(self): + sys.argv = self.argv[0] + sys.argv[:] = self.argv[1] + super(MetadataTestCase, self).tearDown() + def test_simple_metadata(self): attrs = {"name": "package", "version": "1.0"} @@ -308,14 +319,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, # linux-style if sys.platform in ('linux', 'darwin'): - self.environ['HOME'] = temp_dir + os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assertTrue(user_filename in files) # win32-style if sys.platform == 'win32': # home drive should be found - self.environ['HOME'] = temp_dir + os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assertTrue(user_filename in files, '%r not found in %r' % (user_filename, files)) @@ -331,15 +342,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_show_help(self): # smoke test, just makes sure some help is displayed dist = Distribution() - old_argv = sys.argv sys.argv = [] - try: - dist.help = 1 - dist.script_name = 'setup.py' - with captured_stdout() as s: - dist.parse_command_line() - finally: - sys.argv = old_argv + dist.help = 1 + dist.script_name = 'setup.py' + with captured_stdout() as s: + dist.parse_command_line() output = [line for line in s.getvalue().split('\n') if line.strip() != ''] diff --git a/tests/test_install.py b/tests/test_install.py index d2b8c8e9..d44156dd 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -17,6 +17,7 @@ from distutils.errors import DistutilsOptionError from distutils.tests import support class InstallTestCase(support.TempdirManager, + support.EnvironGuard, support.LoggingSilencer, unittest.TestCase): diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 70721367..377ae86e 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -9,6 +9,7 @@ from distutils.tests import support class InstallDataTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def test_simple_run(self): diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index 2564563f..5b32b13e 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -9,6 +9,7 @@ from distutils.tests import support class InstallHeadersTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def test_simple_run(self): diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 793b95ca..b2185b84 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -10,9 +10,9 @@ from distutils.errors import DistutilsOptionError class InstallLibTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): - def test_finalize_options(self): pkg_dir, dist = self.create_dist() cmd = install_lib(dist) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index edc8fd8d..498714de 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -17,8 +17,16 @@ class SysconfigTestCase(support.EnvironGuard, def tearDown(self): if self.makefile is not None: os.unlink(self.makefile) + self.cleanup_testfn() super(SysconfigTestCase, self).tearDown() + def cleanup_testfn(self): + path = test.test_support.TESTFN + if os.path.isfile(path): + os.remove(path) + elif os.path.isdir(path): + shutil.rmtree(path) + def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() self.assertTrue(os.path.isfile(config_h), config_h) @@ -51,8 +59,8 @@ class SysconfigTestCase(support.EnvironGuard, if get_default_compiler() != 'unix': return - self.environ['AR'] = 'my_ar' - self.environ['ARFLAGS'] = '-arflags' + os.environ['AR'] = 'my_ar' + os.environ['ARFLAGS'] = '-arflags' # make sure AR gets caught class compiler: diff --git a/tests/test_util.py b/tests/test_util.py index 7190b2c9..4099c950 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -121,7 +121,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) - self.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') @@ -129,7 +129,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEquals(get_platform(), 'macosx-10.3-i386') # macbook with fat binaries (fat, universal or fat64) - self.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -250,17 +250,18 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): def test_check_environ(self): util._environ_checked = 0 + if 'HOME' in os.environ: + del os.environ['HOME'] # posix without HOME if os.name == 'posix': # this test won't run on windows check_environ() import pwd - self.assertEquals(self.environ['HOME'], - pwd.getpwuid(os.getuid())[5]) + self.assertEquals(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) else: check_environ() - self.assertEquals(self.environ['PLAT'], get_platform()) + self.assertEquals(os.environ['PLAT'], get_platform()) self.assertEquals(util._environ_checked, 1) def test_split_quoted(self): -- cgit v1.2.1 From b4af667a099c424589142dea0a2ed4e7c2cd71ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 18 Oct 2009 11:34:51 +0000 Subject: Merged revisions 75485 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75485 | tarek.ziade | 2009-10-18 11:28:26 +0200 (Sun, 18 Oct 2009) | 1 line Changed distutils tests to avoid environment alteration ........ --- tests/support.py | 13 ++++++++++--- tests/test_bdist_dumb.py | 6 ++++-- tests/test_bdist_rpm.py | 5 +++-- tests/test_build_ext.py | 5 +++-- tests/test_config.py | 2 +- tests/test_core.py | 11 +++++++---- tests/test_dist.py | 31 +++++++++++++++++++------------ tests/test_install.py | 1 + tests/test_install_data.py | 1 + tests/test_install_headers.py | 1 + tests/test_install_lib.py | 2 +- tests/test_sysconfig.py | 11 +++++++++-- tests/test_util.py | 11 ++++++----- 13 files changed, 66 insertions(+), 34 deletions(-) diff --git a/tests/support.py b/tests/support.py index ea122111..e258d2e5 100644 --- a/tests/support.py +++ b/tests/support.py @@ -2,11 +2,11 @@ import os import shutil import tempfile +from copy import deepcopy from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution -from test.support import EnvironmentVarGuard class LoggingSilencer(object): @@ -111,8 +111,15 @@ class EnvironGuard(object): def setUp(self): super(EnvironGuard, self).setUp() - self.environ = EnvironmentVarGuard() + self.old_environ = deepcopy(os.environ) def tearDown(self): - self.environ.__exit__() + for key, value in self.old_environ.items(): + if os.environ.get(key) != value: + os.environ[key] = value + + for key in tuple(os.environ.keys()): + if key not in self.old_environ: + del os.environ[key] + super(EnvironGuard, self).tearDown() diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index a838f734..5eaef2a9 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -26,16 +26,18 @@ setup(name='foo', version='0.1', py_modules=['foo'], class BuildDumbTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def setUp(self): super(BuildDumbTestCase, self).setUp() self.old_location = os.getcwd() - self.old_sys_argv = sys.argv[:] + self.old_sys_argv = sys.argv, sys.argv[:] def tearDown(self): os.chdir(self.old_location) - sys.argv = self.old_sys_argv[:] + sys.argv = self.old_sys_argv[0] + sys.argv[:] = self.old_sys_argv[1] super(BuildDumbTestCase, self).tearDown() @unittest.skipUnless(zlib, "requires zlib") diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index c2715675..2aa257f7 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -29,11 +29,12 @@ class BuildRpmTestCase(support.TempdirManager, def setUp(self): super(BuildRpmTestCase, self).setUp() self.old_location = os.getcwd() - self.old_sys_argv = sys.argv[:] + self.old_sys_argv = sys.argv, sys.argv[:] def tearDown(self): os.chdir(self.old_location) - sys.argv = self.old_sys_argv[:] + sys.argv = self.old_sys_argv[0] + sys.argv[:] = self.old_sys_argv[1] super(BuildRpmTestCase, self).tearDown() def test_quiet(self): diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 7a27f343..ebbb3997 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -35,7 +35,7 @@ class BuildExtTestCase(TempdirManager, # Note that we're making changes to sys.path super(BuildExtTestCase, self).setUp() self.tmp_dir = self.mkdtemp() - self.sys_path = sys.path[:] + self.sys_path = sys.path, sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) if sys.version > "2.6": @@ -90,7 +90,8 @@ class BuildExtTestCase(TempdirManager, def tearDown(self): # Get everything back to normal support.unload('xx') - sys.path = self.sys_path + sys.path = self.sys_path[0] + sys.path[:] = self.sys_path[1] if sys.version > "2.6": import site site.USER_BASE = self.old_user_base diff --git a/tests/test_config.py b/tests/test_config.py index 879689d2..71c63678 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -55,7 +55,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, """Patches the environment.""" super(PyPIRCCommandTestCase, self).setUp() self.tmp_dir = self.mkdtemp() - self.environ['HOME'] = self.tmp_dir + os.environ['HOME'] = self.tmp_dir self.rc = os.path.join(self.tmp_dir, '.pypirc') self.dist = Distribution() diff --git a/tests/test_core.py b/tests/test_core.py index b5f391f5..b478fa62 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8,7 +8,7 @@ import sys import test.support from test.support import captured_stdout import unittest - +from distutils.tests import support # setup script that uses __file__ setup_using___file__ = """\ @@ -29,17 +29,20 @@ setup() """ -class CoreTestCase(unittest.TestCase): +class CoreTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): + super(CoreTestCase, self).setUp() self.old_stdout = sys.stdout self.cleanup_testfn() - self.old_argv = sys.argv[:] + self.old_argv = sys.argv, sys.argv[:] def tearDown(self): sys.stdout = self.old_stdout self.cleanup_testfn() - sys.argv = self.old_argv[:] + sys.argv = self.old_argv[0] + sys.argv[:] = self.old_argv[1] + super(CoreTestCase, self).tearDown() def cleanup_testfn(self): path = test.support.TESTFN diff --git a/tests/test_dist.py b/tests/test_dist.py index 799b0c06..be1010c6 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -38,15 +38,17 @@ class TestDistribution(Distribution): class DistributionTestCase(support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def setUp(self): super(DistributionTestCase, self).setUp() - self.argv = sys.argv[:] + self.argv = sys.argv, sys.argv[:] del sys.argv[1:] def tearDown(self): - sys.argv[:] = self.argv + sys.argv = self.argv[0] + sys.argv[:] = self.argv[1] super(DistributionTestCase, self).tearDown() def create_distribution(self, configfiles=()): @@ -181,6 +183,15 @@ class DistributionTestCase(support.LoggingSilencer, class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): + def setUp(self): + super(MetadataTestCase, self).setUp() + self.argv = sys.argv, sys.argv[:] + + def tearDown(self): + sys.argv = self.argv[0] + sys.argv[:] = self.argv[1] + super(MetadataTestCase, self).tearDown() + def test_simple_metadata(self): attrs = {"name": "package", "version": "1.0"} @@ -279,14 +290,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, # linux-style if sys.platform in ('linux', 'darwin'): - self.environ['HOME'] = temp_dir + os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assertTrue(user_filename in files) # win32-style if sys.platform == 'win32': # home drive should be found - self.environ['HOME'] = temp_dir + os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assertTrue(user_filename in files, '%r not found in %r' % (user_filename, files)) @@ -302,15 +313,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_show_help(self): # smoke test, just makes sure some help is displayed dist = Distribution() - old_argv = sys.argv sys.argv = [] - try: - dist.help = 1 - dist.script_name = 'setup.py' - with captured_stdout() as s: - dist.parse_command_line() - finally: - sys.argv = old_argv + dist.help = 1 + dist.script_name = 'setup.py' + with captured_stdout() as s: + dist.parse_command_line() output = [line for line in s.getvalue().split('\n') if line.strip() != ''] diff --git a/tests/test_install.py b/tests/test_install.py index 2087a0eb..76fa02ac 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -17,6 +17,7 @@ from distutils.errors import DistutilsOptionError from distutils.tests import support class InstallTestCase(support.TempdirManager, + support.EnvironGuard, support.LoggingSilencer, unittest.TestCase): diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 70721367..377ae86e 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -9,6 +9,7 @@ from distutils.tests import support class InstallDataTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def test_simple_run(self): diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index 2564563f..5b32b13e 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -9,6 +9,7 @@ from distutils.tests import support class InstallHeadersTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def test_simple_run(self): diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 793b95ca..b2185b84 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -10,9 +10,9 @@ from distutils.errors import DistutilsOptionError class InstallLibTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): - def test_finalize_options(self): pkg_dir, dist = self.create_dist() cmd = install_lib(dist) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index eb362044..edc755ed 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -17,8 +17,15 @@ class SysconfigTestCase(support.EnvironGuard, def tearDown(self): if self.makefile is not None: os.unlink(self.makefile) + self.cleanup_testfn() super(SysconfigTestCase, self).tearDown() + def cleanup_testfn(self): + if os.path.isfile(TESTFN): + os.remove(TESTFN) + elif os.path.isdir(TESTFN): + shutil.rmtree(TESTFN) + def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() self.assertTrue(os.path.isfile(config_h), config_h) @@ -51,8 +58,8 @@ class SysconfigTestCase(support.EnvironGuard, if get_default_compiler() != 'unix': return - self.environ['AR'] = 'my_ar' - self.environ['ARFLAGS'] = '-arflags' + os.environ['AR'] = 'my_ar' + os.environ['ARFLAGS'] = '-arflags' # make sure AR gets caught class compiler: diff --git a/tests/test_util.py b/tests/test_util.py index 84caea5c..8068726d 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -121,7 +121,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) - self.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') @@ -129,7 +129,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEquals(get_platform(), 'macosx-10.3-i386') # macbook with fat binaries (fat, universal or fat64) - self.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -250,17 +250,18 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): def test_check_environ(self): util._environ_checked = 0 + if 'HOME' in os.environ: + del os.environ['HOME'] # posix without HOME if os.name == 'posix': # this test won't run on windows check_environ() import pwd - self.assertEquals(self.environ['HOME'], - pwd.getpwuid(os.getuid())[5]) + self.assertEquals(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) else: check_environ() - self.assertEquals(self.environ['PLAT'], get_platform()) + self.assertEquals(os.environ['PLAT'], get_platform()) self.assertEquals(util._environ_checked, 1) def test_split_quoted(self): -- cgit v1.2.1 From 32a6241687d12965291bd1f62fb9c5eb85b9eab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 18 Oct 2009 12:41:30 +0000 Subject: Merged revisions 75491 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r75491 | tarek.ziade | 2009-10-18 13:34:51 +0200 (Sun, 18 Oct 2009) | 9 lines Merged revisions 75485 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75485 | tarek.ziade | 2009-10-18 11:28:26 +0200 (Sun, 18 Oct 2009) | 1 line Changed distutils tests to avoid environment alteration ........ ................ --- tests/support.py | 13 ++++++++++--- tests/test_bdist_dumb.py | 6 ++++-- tests/test_bdist_rpm.py | 5 +++-- tests/test_build_ext.py | 5 +++-- tests/test_config.py | 2 +- tests/test_core.py | 11 +++++++---- tests/test_dist.py | 31 +++++++++++++++++++------------ tests/test_install.py | 1 + tests/test_install_data.py | 1 + tests/test_install_headers.py | 1 + tests/test_install_lib.py | 2 +- tests/test_sysconfig.py | 11 +++++++++-- tests/test_util.py | 11 ++++++----- 13 files changed, 66 insertions(+), 34 deletions(-) diff --git a/tests/support.py b/tests/support.py index ea122111..e258d2e5 100644 --- a/tests/support.py +++ b/tests/support.py @@ -2,11 +2,11 @@ import os import shutil import tempfile +from copy import deepcopy from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution -from test.support import EnvironmentVarGuard class LoggingSilencer(object): @@ -111,8 +111,15 @@ class EnvironGuard(object): def setUp(self): super(EnvironGuard, self).setUp() - self.environ = EnvironmentVarGuard() + self.old_environ = deepcopy(os.environ) def tearDown(self): - self.environ.__exit__() + for key, value in self.old_environ.items(): + if os.environ.get(key) != value: + os.environ[key] = value + + for key in tuple(os.environ.keys()): + if key not in self.old_environ: + del os.environ[key] + super(EnvironGuard, self).tearDown() diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index d2ea201b..5e76809f 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -19,16 +19,18 @@ setup(name='foo', version='0.1', py_modules=['foo'], class BuildDumbTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def setUp(self): super(BuildDumbTestCase, self).setUp() self.old_location = os.getcwd() - self.old_sys_argv = sys.argv[:] + self.old_sys_argv = sys.argv, sys.argv[:] def tearDown(self): os.chdir(self.old_location) - sys.argv = self.old_sys_argv[:] + sys.argv = self.old_sys_argv[0] + sys.argv[:] = self.old_sys_argv[1] super(BuildDumbTestCase, self).tearDown() def test_simple_built(self): diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index c2715675..2aa257f7 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -29,11 +29,12 @@ class BuildRpmTestCase(support.TempdirManager, def setUp(self): super(BuildRpmTestCase, self).setUp() self.old_location = os.getcwd() - self.old_sys_argv = sys.argv[:] + self.old_sys_argv = sys.argv, sys.argv[:] def tearDown(self): os.chdir(self.old_location) - sys.argv = self.old_sys_argv[:] + sys.argv = self.old_sys_argv[0] + sys.argv[:] = self.old_sys_argv[1] super(BuildRpmTestCase, self).tearDown() def test_quiet(self): diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 1221fd43..f992928d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -32,7 +32,7 @@ class BuildExtTestCase(TempdirManager, # Note that we're making changes to sys.path super(BuildExtTestCase, self).setUp() self.tmp_dir = self.mkdtemp() - self.sys_path = sys.path[:] + self.sys_path = sys.path, sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) if sys.version > "2.6": @@ -87,7 +87,8 @@ class BuildExtTestCase(TempdirManager, def tearDown(self): # Get everything back to normal support.unload('xx') - sys.path = self.sys_path + sys.path = self.sys_path[0] + sys.path[:] = self.sys_path[1] if sys.version > "2.6": import site site.USER_BASE = self.old_user_base diff --git a/tests/test_config.py b/tests/test_config.py index 879689d2..71c63678 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -55,7 +55,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, """Patches the environment.""" super(PyPIRCCommandTestCase, self).setUp() self.tmp_dir = self.mkdtemp() - self.environ['HOME'] = self.tmp_dir + os.environ['HOME'] = self.tmp_dir self.rc = os.path.join(self.tmp_dir, '.pypirc') self.dist = Distribution() diff --git a/tests/test_core.py b/tests/test_core.py index b5f391f5..b478fa62 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8,7 +8,7 @@ import sys import test.support from test.support import captured_stdout import unittest - +from distutils.tests import support # setup script that uses __file__ setup_using___file__ = """\ @@ -29,17 +29,20 @@ setup() """ -class CoreTestCase(unittest.TestCase): +class CoreTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): + super(CoreTestCase, self).setUp() self.old_stdout = sys.stdout self.cleanup_testfn() - self.old_argv = sys.argv[:] + self.old_argv = sys.argv, sys.argv[:] def tearDown(self): sys.stdout = self.old_stdout self.cleanup_testfn() - sys.argv = self.old_argv[:] + sys.argv = self.old_argv[0] + sys.argv[:] = self.old_argv[1] + super(CoreTestCase, self).tearDown() def cleanup_testfn(self): path = test.support.TESTFN diff --git a/tests/test_dist.py b/tests/test_dist.py index 70c9ec50..5f96b97f 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -37,15 +37,17 @@ class TestDistribution(Distribution): class DistributionTestCase(support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def setUp(self): super(DistributionTestCase, self).setUp() - self.argv = sys.argv[:] + self.argv = sys.argv, sys.argv[:] del sys.argv[1:] def tearDown(self): - sys.argv[:] = self.argv + sys.argv = self.argv[0] + sys.argv[:] = self.argv[1] super(DistributionTestCase, self).tearDown() def create_distribution(self, configfiles=()): @@ -159,6 +161,15 @@ class DistributionTestCase(support.LoggingSilencer, class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): + def setUp(self): + super(MetadataTestCase, self).setUp() + self.argv = sys.argv, sys.argv[:] + + def tearDown(self): + sys.argv = self.argv[0] + sys.argv[:] = self.argv[1] + super(MetadataTestCase, self).tearDown() + def test_simple_metadata(self): attrs = {"name": "package", "version": "1.0"} @@ -257,14 +268,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, # linux-style if sys.platform in ('linux', 'darwin'): - self.environ['HOME'] = temp_dir + os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assertTrue(user_filename in files) # win32-style if sys.platform == 'win32': # home drive should be found - self.environ['HOME'] = temp_dir + os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assertTrue(user_filename in files, '%r not found in %r' % (user_filename, files)) @@ -280,15 +291,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_show_help(self): # smoke test, just makes sure some help is displayed dist = Distribution() - old_argv = sys.argv sys.argv = [] - try: - dist.help = 1 - dist.script_name = 'setup.py' - with captured_stdout() as s: - dist.parse_command_line() - finally: - sys.argv = old_argv + dist.help = 1 + dist.script_name = 'setup.py' + with captured_stdout() as s: + dist.parse_command_line() output = [line for line in s.getvalue().split('\n') if line.strip() != ''] diff --git a/tests/test_install.py b/tests/test_install.py index 2087a0eb..76fa02ac 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -17,6 +17,7 @@ from distutils.errors import DistutilsOptionError from distutils.tests import support class InstallTestCase(support.TempdirManager, + support.EnvironGuard, support.LoggingSilencer, unittest.TestCase): diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 70721367..377ae86e 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -9,6 +9,7 @@ from distutils.tests import support class InstallDataTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def test_simple_run(self): diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index 2564563f..5b32b13e 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -9,6 +9,7 @@ from distutils.tests import support class InstallHeadersTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def test_simple_run(self): diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 793b95ca..b2185b84 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -10,9 +10,9 @@ from distutils.errors import DistutilsOptionError class InstallLibTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): - def test_finalize_options(self): pkg_dir, dist = self.create_dist() cmd = install_lib(dist) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index eb362044..edc755ed 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -17,8 +17,15 @@ class SysconfigTestCase(support.EnvironGuard, def tearDown(self): if self.makefile is not None: os.unlink(self.makefile) + self.cleanup_testfn() super(SysconfigTestCase, self).tearDown() + def cleanup_testfn(self): + if os.path.isfile(TESTFN): + os.remove(TESTFN) + elif os.path.isdir(TESTFN): + shutil.rmtree(TESTFN) + def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() self.assertTrue(os.path.isfile(config_h), config_h) @@ -51,8 +58,8 @@ class SysconfigTestCase(support.EnvironGuard, if get_default_compiler() != 'unix': return - self.environ['AR'] = 'my_ar' - self.environ['ARFLAGS'] = '-arflags' + os.environ['AR'] = 'my_ar' + os.environ['ARFLAGS'] = '-arflags' # make sure AR gets caught class compiler: diff --git a/tests/test_util.py b/tests/test_util.py index 795edb8c..35f81a6b 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -94,7 +94,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) - self.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') @@ -102,7 +102,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEquals(get_platform(), 'macosx-10.3-i386') # macbook with fat binaries (fat, universal or fat64) - self.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -223,17 +223,18 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): def test_check_environ(self): util._environ_checked = 0 + if 'HOME' in os.environ: + del os.environ['HOME'] # posix without HOME if os.name == 'posix': # this test won't run on windows check_environ() import pwd - self.assertEquals(self.environ['HOME'], - pwd.getpwuid(os.getuid())[5]) + self.assertEquals(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) else: check_environ() - self.assertEquals(self.environ['PLAT'], get_platform()) + self.assertEquals(os.environ['PLAT'], get_platform()) self.assertEquals(util._environ_checked, 1) def test_split_quoted(self): -- cgit v1.2.1 From 0b33d63b2cf8fae5959e157a8798007bbc7049c4 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Sun, 18 Oct 2009 16:50:06 +0000 Subject: Bump to 2.6.4rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 83c576f8..4c8cf042 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.4rc1" +__version__ = "2.6.4rc2" #--end constants-- -- cgit v1.2.1 From cef81109d0ce7c62311b88479f37f02a73f3e917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 24 Oct 2009 13:29:44 +0000 Subject: #7066 - Fixed distutils.archive_util.make_archive behavior so it restores the cwd --- archive_util.py | 10 ++++++---- tests/test_archive_util.py | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/archive_util.py b/archive_util.py index bc5edfd8..c741cc01 100644 --- a/archive_util.py +++ b/archive_util.py @@ -233,9 +233,11 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, kwargs['owner'] = owner kwargs['group'] = group - filename = func(base_name, base_dir, **kwargs) - if root_dir is not None: - log.debug("changing back to '%s'", save_cwd) - os.chdir(save_cwd) + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + log.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) return filename diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index b91986ba..a9b46d8e 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -8,7 +8,8 @@ from os.path import splitdrive import warnings from distutils.archive_util import (check_archive_formats, make_tarball, - make_zipfile, make_archive) + make_zipfile, make_archive, + ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support from test.test_support import check_warnings @@ -262,6 +263,20 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: archive.close() + def test_make_archive_cwd(self): + current_dir = os.getcwd() + def _breaks(*args, **kw): + raise RuntimeError() + ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file') + try: + try: + make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) + except: + pass + self.assertEquals(os.getcwd(), current_dir) + finally: + del ARCHIVE_FORMATS['xxx'] + def test_suite(): return unittest.makeSuite(ArchiveUtilTestCase) -- cgit v1.2.1 From 0d4e4028bd9f2f05037cda612970b0ed16870e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 24 Oct 2009 13:38:27 +0000 Subject: Merged revisions 75659 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75659 | tarek.ziade | 2009-10-24 15:29:44 +0200 (Sat, 24 Oct 2009) | 1 line #7066 - Fixed distutils.archive_util.make_archive behavior so it restores the cwd ........ --- archive_util.py | 11 ++++++----- tests/test_archive_util.py | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/archive_util.py b/archive_util.py index d051f917..28e93fed 100644 --- a/archive_util.py +++ b/archive_util.py @@ -232,10 +232,11 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, kwargs['owner'] = owner kwargs['group'] = group - filename = func(base_name, base_dir, **kwargs) - - if root_dir is not None: - log.debug("changing back to '%s'", save_cwd) - os.chdir(save_cwd) + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + log.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) return filename diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 71d32dce..682f19a2 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -8,7 +8,8 @@ from os.path import splitdrive import warnings from distutils.archive_util import (check_archive_formats, make_tarball, - make_zipfile, make_archive) + make_zipfile, make_archive, + ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support from test.support import check_warnings @@ -262,6 +263,20 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: archive.close() + def test_make_archive_cwd(self): + current_dir = os.getcwd() + def _breaks(*args, **kw): + raise RuntimeError() + ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file') + try: + try: + make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) + except: + pass + self.assertEquals(os.getcwd(), current_dir) + finally: + del ARCHIVE_FORMATS['xxx'] + def test_suite(): return unittest.makeSuite(ArchiveUtilTestCase) -- cgit v1.2.1 From 57a2a657c29720facb1cd04f28751608323f9c6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 24 Oct 2009 13:42:10 +0000 Subject: Merged revisions 75662 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r75662 | tarek.ziade | 2009-10-24 15:38:27 +0200 (Sat, 24 Oct 2009) | 9 lines Merged revisions 75659 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75659 | tarek.ziade | 2009-10-24 15:29:44 +0200 (Sat, 24 Oct 2009) | 1 line #7066 - Fixed distutils.archive_util.make_archive behavior so it restores the cwd ........ ................ --- archive_util.py | 11 ++++++----- tests/test_archive_util.py | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/archive_util.py b/archive_util.py index a568854e..16164c7f 100644 --- a/archive_util.py +++ b/archive_util.py @@ -171,10 +171,11 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, func = format_info[0] for arg, val in format_info[1]: kwargs[arg] = val - filename = func(base_name, base_dir, **kwargs) - - if root_dir is not None: - log.debug("changing back to '%s'", save_cwd) - os.chdir(save_cwd) + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + log.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) return filename diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index d88e0b35..c6e08cbc 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -8,7 +8,8 @@ from os.path import splitdrive import warnings from distutils.archive_util import (check_archive_formats, make_tarball, - make_zipfile, make_archive) + make_zipfile, make_archive, + ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support from test.support import check_warnings @@ -192,6 +193,20 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir, 'archive') self.assertRaises(ValueError, make_archive, base_name, 'xxx') + def test_make_archive_cwd(self): + current_dir = os.getcwd() + def _breaks(*args, **kw): + raise RuntimeError() + ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file') + try: + try: + make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) + except: + pass + self.assertEquals(os.getcwd(), current_dir) + finally: + del ARCHIVE_FORMATS['xxx'] + def test_suite(): return unittest.makeSuite(ArchiveUtilTestCase) -- cgit v1.2.1 From 9e0e2af955949e3f06652856f6812ab291e47b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 24 Oct 2009 15:10:37 +0000 Subject: Issue #7071: byte-compilation in Distutils now looks at sys.dont_write_bytecode --- command/build_py.py | 5 +++++ command/install_lib.py | 6 ++++++ errors.py | 2 ++ tests/test_build_py.py | 16 ++++++++++++++++ tests/test_install_lib.py | 17 +++++++++++++++++ tests/test_util.py | 18 +++++++++++++----- util.py | 5 +++++ 7 files changed, 64 insertions(+), 5 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 5d249f3d..9b9f5518 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -5,6 +5,7 @@ Implements the Distutils 'build_py' command.""" __revision__ = "$Id$" import os +import sys from glob import glob from distutils.core import Command @@ -372,6 +373,10 @@ class build_py(Command): self.build_module(module, module_file, package) def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compile not supported on this platform, skipping.') + return + from distutils.util import byte_compile prefix = self.build_lib if prefix[-1] != os.sep: diff --git a/command/install_lib.py b/command/install_lib.py index 411db79c..7b4b45be 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,6 +6,8 @@ Implements the Distutils 'install_lib' command __revision__ = "$Id$" import os +import sys + from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -118,6 +120,10 @@ class install_lib(Command): return outfiles def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compile not supported on this platform, skipping.') + return + from distutils.util import byte_compile # Get the "--root" directory supplied to the "install" command, diff --git a/errors.py b/errors.py index 963d8337..acecaccc 100644 --- a/errors.py +++ b/errors.py @@ -74,6 +74,8 @@ class DistutilsInternalError (DistutilsError): class DistutilsTemplateError (DistutilsError): """Syntax error in a file list template.""" +class DistutilsByteCompileError(DistutilsError): + """Byte compile error.""" # Exception classes used by the CCompiler implementation classes class CCompilerError (Exception): diff --git a/tests/test_build_py.py b/tests/test_build_py.py index c815d818..e8c7ca92 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -89,6 +89,22 @@ class BuildPyTestCase(support.TempdirManager, os.chdir(cwd) sys.stdout = sys.__stdout__ + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + pkg_dir, dist = self.create_dist() + cmd = build_py(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertTrue('byte-compile not supported ' in self.logs[0][1]) + def test_suite(): return unittest.makeSuite(BuildPyTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index b2185b84..fab66d15 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -31,6 +31,8 @@ class InstallLibTestCase(support.TempdirManager, cmd.finalize_options() self.assertEquals(cmd.optimize, 2) + @unittest.skipUnless(not sys.dont_write_bytecode, + 'byte-compile not supported') def test_byte_compile(self): pkg_dir, dist = self.create_dist() cmd = install_lib(dist) @@ -76,6 +78,21 @@ class InstallLibTestCase(support.TempdirManager, # get_input should return 2 elements self.assertEquals(len(cmd.get_inputs()), 2) + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertTrue('byte-compile not supported ' in self.logs[0][1]) def test_suite(): return unittest.makeSuite(InstallLibTestCase) diff --git a/tests/test_util.py b/tests/test_util.py index 4099c950..4da1e651 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,7 +1,4 @@ """Tests for distutils.util.""" -# not covered yet: -# - byte_compile -# import os import sys import unittest @@ -9,11 +6,12 @@ from copy import copy from StringIO import StringIO import subprocess -from distutils.errors import DistutilsPlatformError +from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import (get_platform, convert_path, change_root, check_environ, split_quoted, strtobool, rfc822_escape, get_compiler_versions, - _find_exe_version, _MAC_OS_X_LD_VERSION) + _find_exe_version, _MAC_OS_X_LD_VERSION, + byte_compile) from distutils import util from distutils.sysconfig import get_config_vars from distutils import sysconfig @@ -349,6 +347,16 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): res = get_compiler_versions() self.assertEquals(res[2], None) + def test_dont_write_bytecode(self): + # makes sure byte_compile raise a DistutilsError + # if sys.dont_write_bytecode is True + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + self.assertRaises(DistutilsByteCompileError, byte_compile, []) + finally: + sys.dont_write_bytecode = False + def test_suite(): return unittest.makeSuite(UtilTestCase) diff --git a/util.py b/util.py index fe6851cb..a87ef44a 100644 --- a/util.py +++ b/util.py @@ -13,6 +13,7 @@ from distutils.dep_util import newer from distutils.spawn import spawn, find_executable from distutils import log from distutils.version import LooseVersion +from distutils.errors import DistutilsByteCompileError def get_platform(): """Return a string that identifies the current platform. @@ -445,6 +446,10 @@ def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, generated in indirect mode; unless you know what you're doing, leave it set to None. """ + # nothing is done if sys.dont_write_bytecode is True + if sys.dont_write_bytecode: + raise DistutilsByteCompileError('byte-compiling not supported.') + # First, if the caller didn't force us into direct or indirect mode, # figure out which mode we should be in. We take a conservative # approach: choose direct mode *only* if the current interpreter is -- cgit v1.2.1 From 32bc943918b21381298fc43df3f6d40b252a6937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 24 Oct 2009 15:19:03 +0000 Subject: fixed finally state in distutils.test_util --- tests/test_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index 4da1e651..55ef160f 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -355,7 +355,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): try: self.assertRaises(DistutilsByteCompileError, byte_compile, []) finally: - sys.dont_write_bytecode = False + sys.dont_write_bytecode = old_dont_write_bytecode def test_suite(): return unittest.makeSuite(UtilTestCase) -- cgit v1.2.1 From ecff8b73e4ab2da659b208b66c3d72a104ba7d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 24 Oct 2009 15:51:30 +0000 Subject: fixed warning and error message --- command/build_py.py | 2 +- command/install_lib.py | 2 +- tests/test_build_py.py | 2 +- tests/test_install_lib.py | 2 +- util.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 9b9f5518..5c7b473b 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -374,7 +374,7 @@ class build_py(Command): def byte_compile(self, files): if sys.dont_write_bytecode: - self.warn('byte-compile not supported on this platform, skipping.') + self.warn('byte-compiling is disabled, skipping.') return from distutils.util import byte_compile diff --git a/command/install_lib.py b/command/install_lib.py index 7b4b45be..043e8b6e 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -121,7 +121,7 @@ class install_lib(Command): def byte_compile(self, files): if sys.dont_write_bytecode: - self.warn('byte-compile not supported on this platform, skipping.') + self.warn('byte-compiling is disabled, skipping.') return from distutils.util import byte_compile diff --git a/tests/test_build_py.py b/tests/test_build_py.py index e8c7ca92..bfe61542 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -103,7 +103,7 @@ class BuildPyTestCase(support.TempdirManager, finally: sys.dont_write_bytecode = old_dont_write_bytecode - self.assertTrue('byte-compile not supported ' in self.logs[0][1]) + self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) def test_suite(): return unittest.makeSuite(BuildPyTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index fab66d15..99a6d906 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -92,7 +92,7 @@ class InstallLibTestCase(support.TempdirManager, finally: sys.dont_write_bytecode = old_dont_write_bytecode - self.assertTrue('byte-compile not supported ' in self.logs[0][1]) + self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) def test_suite(): return unittest.makeSuite(InstallLibTestCase) diff --git a/util.py b/util.py index a87ef44a..6bff44f7 100644 --- a/util.py +++ b/util.py @@ -448,7 +448,7 @@ def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, """ # nothing is done if sys.dont_write_bytecode is True if sys.dont_write_bytecode: - raise DistutilsByteCompileError('byte-compiling not supported.') + raise DistutilsByteCompileError('byte-compiling is disabled.') # First, if the caller didn't force us into direct or indirect mode, # figure out which mode we should be in. We take a conservative -- cgit v1.2.1 From 283c3eac2d9e113f876c649e5ddb1dcbf9a6e096 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 24 Oct 2009 20:11:21 +0000 Subject: Remove AtheOS support, as per PEP 11 (which claims that all code was removed in Python 3.0). --- command/build_ext.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 14c529ac..70dd81f1 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -262,9 +262,9 @@ class build_ext(Command): if os.name == 'os2': self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) - # for extensions under Cygwin and AtheOS Python's library directory must be + # for extensions under Cygwin Python's library directory must be # appended to library_dirs - if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': + if sys.platform[:6] == 'cygwin': if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", @@ -776,22 +776,6 @@ class build_ext(Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] - elif sys.platform[:6] == "atheos": - from distutils import sysconfig - - template = "python%d.%d" - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - # Get SHLIBS from Makefile - extra = [] - for lib in sysconfig.get_config_var('SHLIBS').split(): - if lib.startswith('-l'): - extra.append(lib[2:]) - else: - extra.append(lib) - # don't extend ext.libraries, it may be shared with other - # extensions, it is a reference to the original list - return ext.libraries + [pythonlib, "m"] + extra elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries -- cgit v1.2.1 From 389e4ff03d3ae338adf08fc8dc35693b07b332e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 25 Oct 2009 23:08:47 +0000 Subject: Merged revisions 75669-75671 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75669 | tarek.ziade | 2009-10-24 17:10:37 +0200 (Sat, 24 Oct 2009) | 1 line Issue #7071: byte-compilation in Distutils now looks at sys.dont_write_bytecode ........ r75670 | tarek.ziade | 2009-10-24 17:19:03 +0200 (Sat, 24 Oct 2009) | 1 line fixed finally state in distutils.test_util ........ r75671 | tarek.ziade | 2009-10-24 17:51:30 +0200 (Sat, 24 Oct 2009) | 1 line fixed warning and error message ........ --- command/build_py.py | 5 +++++ command/install_lib.py | 6 ++++++ errors.py | 2 ++ tests/test_build_py.py | 16 ++++++++++++++++ tests/test_install_lib.py | 17 +++++++++++++++++ tests/test_util.py | 18 +++++++++++++----- util.py | 5 +++++ 7 files changed, 64 insertions(+), 5 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 4cc80f7b..fa085796 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -5,6 +5,7 @@ Implements the Distutils 'build_py' command.""" __revision__ = "$Id$" import os +import sys from glob import glob from distutils.core import Command @@ -369,6 +370,10 @@ class build_py(Command): self.build_module(module, module_file, package) def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + from distutils.util import byte_compile prefix = self.build_lib if prefix[-1] != os.sep: diff --git a/command/install_lib.py b/command/install_lib.py index 85fb3acd..6022d30f 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,6 +6,8 @@ Implements the Distutils 'install_lib' command __revision__ = "$Id$" import os +import sys + from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -115,6 +117,10 @@ class install_lib(Command): return outfiles def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + from distutils.util import byte_compile # Get the "--root" directory supplied to the "install" command, diff --git a/errors.py b/errors.py index 963d8337..acecaccc 100644 --- a/errors.py +++ b/errors.py @@ -74,6 +74,8 @@ class DistutilsInternalError (DistutilsError): class DistutilsTemplateError (DistutilsError): """Syntax error in a file list template.""" +class DistutilsByteCompileError(DistutilsError): + """Byte compile error.""" # Exception classes used by the CCompiler implementation classes class CCompilerError (Exception): diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 8ad3bbc4..582f2463 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -89,6 +89,22 @@ class BuildPyTestCase(support.TempdirManager, os.chdir(cwd) sys.stdout = sys.__stdout__ + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + pkg_dir, dist = self.create_dist() + cmd = build_py(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) + def test_suite(): return unittest.makeSuite(BuildPyTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index b2185b84..99a6d906 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -31,6 +31,8 @@ class InstallLibTestCase(support.TempdirManager, cmd.finalize_options() self.assertEquals(cmd.optimize, 2) + @unittest.skipUnless(not sys.dont_write_bytecode, + 'byte-compile not supported') def test_byte_compile(self): pkg_dir, dist = self.create_dist() cmd = install_lib(dist) @@ -76,6 +78,21 @@ class InstallLibTestCase(support.TempdirManager, # get_input should return 2 elements self.assertEquals(len(cmd.get_inputs()), 2) + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) def test_suite(): return unittest.makeSuite(InstallLibTestCase) diff --git a/tests/test_util.py b/tests/test_util.py index 8068726d..a2f8ed2b 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,7 +1,4 @@ """Tests for distutils.util.""" -# not covered yet: -# - byte_compile -# import os import sys import unittest @@ -9,11 +6,12 @@ from copy import copy from io import BytesIO import subprocess -from distutils.errors import DistutilsPlatformError +from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import (get_platform, convert_path, change_root, check_environ, split_quoted, strtobool, rfc822_escape, get_compiler_versions, - _find_exe_version, _MAC_OS_X_LD_VERSION) + _find_exe_version, _MAC_OS_X_LD_VERSION, + byte_compile) from distutils import util from distutils.sysconfig import get_config_vars from distutils import sysconfig @@ -349,6 +347,16 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): res = get_compiler_versions() self.assertEquals(res[2], None) + def test_dont_write_bytecode(self): + # makes sure byte_compile raise a DistutilsError + # if sys.dont_write_bytecode is True + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + self.assertRaises(DistutilsByteCompileError, byte_compile, []) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + def test_suite(): return unittest.makeSuite(UtilTestCase) diff --git a/util.py b/util.py index 6709bbfe..a50621e3 100644 --- a/util.py +++ b/util.py @@ -13,6 +13,7 @@ from distutils.dep_util import newer from distutils.spawn import spawn, find_executable from distutils import log from distutils.version import LooseVersion +from distutils.errors import DistutilsByteCompileError def get_platform(): """Return a string that identifies the current platform. @@ -444,6 +445,10 @@ def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, generated in indirect mode; unless you know what you're doing, leave it set to None. """ + # nothing is done if sys.dont_write_bytecode is True + if sys.dont_write_bytecode: + raise DistutilsByteCompileError('byte-compiling is disabled.') + # First, if the caller didn't force us into direct or indirect mode, # figure out which mode we should be in. We take a conservative # approach: choose direct mode *only* if the current interpreter is -- cgit v1.2.1 From 4d70c7aa04be70bf8fdf486d70c3c383271d7a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 25 Oct 2009 23:16:51 +0000 Subject: Merged revisions 75704 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r75704 | tarek.ziade | 2009-10-26 00:08:47 +0100 (Mon, 26 Oct 2009) | 17 lines Merged revisions 75669-75671 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75669 | tarek.ziade | 2009-10-24 17:10:37 +0200 (Sat, 24 Oct 2009) | 1 line Issue #7071: byte-compilation in Distutils now looks at sys.dont_write_bytecode ........ r75670 | tarek.ziade | 2009-10-24 17:19:03 +0200 (Sat, 24 Oct 2009) | 1 line fixed finally state in distutils.test_util ........ r75671 | tarek.ziade | 2009-10-24 17:51:30 +0200 (Sat, 24 Oct 2009) | 1 line fixed warning and error message ........ ................ --- command/build_py.py | 5 +++++ command/install_lib.py | 6 ++++++ errors.py | 2 ++ tests/test_build_py.py | 16 ++++++++++++++++ tests/test_install_lib.py | 17 +++++++++++++++++ tests/test_util.py | 17 ++++++++++++----- util.py | 4 ++++ 7 files changed, 62 insertions(+), 5 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 99d85be9..26002e4b 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -5,6 +5,7 @@ Implements the Distutils 'build_py' command.""" __revision__ = "$Id$" import sys, os +import sys from glob import glob from distutils.core import Command @@ -369,6 +370,10 @@ class build_py (Command): self.build_module(module, module_file, package) def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + from distutils.util import byte_compile prefix = self.build_lib if prefix[-1] != os.sep: diff --git a/command/install_lib.py b/command/install_lib.py index 85fb3acd..6022d30f 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,6 +6,8 @@ Implements the Distutils 'install_lib' command __revision__ = "$Id$" import os +import sys + from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -115,6 +117,10 @@ class install_lib(Command): return outfiles def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + from distutils.util import byte_compile # Get the "--root" directory supplied to the "install" command, diff --git a/errors.py b/errors.py index 963d8337..acecaccc 100644 --- a/errors.py +++ b/errors.py @@ -74,6 +74,8 @@ class DistutilsInternalError (DistutilsError): class DistutilsTemplateError (DistutilsError): """Syntax error in a file list template.""" +class DistutilsByteCompileError(DistutilsError): + """Byte compile error.""" # Exception classes used by the CCompiler implementation classes class CCompilerError (Exception): diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 8ad3bbc4..582f2463 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -89,6 +89,22 @@ class BuildPyTestCase(support.TempdirManager, os.chdir(cwd) sys.stdout = sys.__stdout__ + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + pkg_dir, dist = self.create_dist() + cmd = build_py(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) + def test_suite(): return unittest.makeSuite(BuildPyTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index b2185b84..99a6d906 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -31,6 +31,8 @@ class InstallLibTestCase(support.TempdirManager, cmd.finalize_options() self.assertEquals(cmd.optimize, 2) + @unittest.skipUnless(not sys.dont_write_bytecode, + 'byte-compile not supported') def test_byte_compile(self): pkg_dir, dist = self.create_dist() cmd = install_lib(dist) @@ -76,6 +78,21 @@ class InstallLibTestCase(support.TempdirManager, # get_input should return 2 elements self.assertEquals(len(cmd.get_inputs()), 2) + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) def test_suite(): return unittest.makeSuite(InstallLibTestCase) diff --git a/tests/test_util.py b/tests/test_util.py index 35f81a6b..dcc1a206 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,16 +1,13 @@ """Tests for distutils.util.""" -# not covered yet: -# - byte_compile -# import os import sys import unittest from copy import copy -from distutils.errors import DistutilsPlatformError +from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import (get_platform, convert_path, change_root, check_environ, split_quoted, strtobool, - rfc822_escape) + rfc822_escape, byte_compile) from distutils import util # used to patch _environ_checked from distutils.sysconfig import get_config_vars from distutils import sysconfig @@ -258,6 +255,16 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): 'header%(8s)s') % {'8s': '\n'+8*' '} self.assertEquals(res, wanted) + def test_dont_write_bytecode(self): + # makes sure byte_compile raise a DistutilsError + # if sys.dont_write_bytecode is True + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + self.assertRaises(DistutilsByteCompileError, byte_compile, []) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + def test_suite(): return unittest.makeSuite(UtilTestCase) diff --git a/util.py b/util.py index 5054d0c8..4bc4c984 100644 --- a/util.py +++ b/util.py @@ -11,6 +11,7 @@ from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer from distutils.spawn import spawn from distutils import log +from distutils.errors import DistutilsByteCompileError def get_platform (): """Return a string that identifies the current platform. This is used @@ -443,6 +444,9 @@ def byte_compile (py_files, generated in indirect mode; unless you know what you're doing, leave it set to None. """ + # nothing is done if sys.dont_write_bytecode is True + if sys.dont_write_bytecode: + raise DistutilsByteCompileError('byte-compiling is disabled.') # First, if the caller didn't force us into direct or indirect mode, # figure out which mode we should be in. We take a conservative -- cgit v1.2.1 From 34ac4d6b68ea3f6955282afdbc7f113a084d3f31 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 26 Oct 2009 01:48:07 +0000 Subject: bumping to 2.6.4 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 4c8cf042..b37308c5 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.4rc2" +__version__ = "2.6.4" #--end constants-- -- cgit v1.2.1 From 145e091da579f3d1e2cd591ca3d0eaac70b5aeb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 27 Oct 2009 23:06:10 +0000 Subject: Fixed #1180: Option to ignore ~/.pydistutils.cfg in Distutils --- core.py | 5 +++-- dist.py | 35 ++++++++++++++++++++++++++++++----- tests/test_dist.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/core.py b/core.py index 0fb6f81e..8073a6bc 100644 --- a/core.py +++ b/core.py @@ -129,8 +129,9 @@ def setup (**attrs): if _setup_stop_after == "config": return dist - # Parse the command line; any command-line errors are the end user's - # fault, so turn them into SystemExit to suppress tracebacks. + # Parse the command line and override config files; any + # command-line errors are the end user's fault, so turn them into + # SystemExit to suppress tracebacks. try: ok = dist.parse_command_line() except DistutilsArgError, msg: diff --git a/dist.py b/dist.py index eb7d01c0..8dc64d43 100644 --- a/dist.py +++ b/dist.py @@ -56,7 +56,9 @@ class Distribution: ('quiet', 'q', "run quietly (turns verbosity off)"), ('dry-run', 'n', "don't actually do anything"), ('help', 'h', "show detailed help message"), - ] + ('no-user-cfg', None, + 'ignore pydistutils.cfg in your home directory'), + ] # 'common_usage' is a short (2-3 line) string describing the common # usage of the setup script. @@ -264,6 +266,22 @@ Common commands: (see '--help-commands' for more) else: sys.stderr.write(msg + "\n") + # no-user-cfg is handled before other command line args + # because other args override the config files, and this + # one is needed before we can load the config files. + # If attrs['script_args'] wasn't passed, assume false. + # + # This also make sure we just look at the global options + self.want_user_cfg = True + + if self.script_args is not None: + for arg in self.script_args: + if not arg.startswith('-'): + break + if arg == '--no-user-cfg': + self.want_user_cfg = False + break + self.finalize_options() def get_option_dict(self, command): @@ -316,7 +334,10 @@ Common commands: (see '--help-commands' for more) Distutils installation directory (ie. where the top-level Distutils __inst__.py file lives), a file in the user's home directory named .pydistutils.cfg on Unix and pydistutils.cfg - on Windows/Mac, and setup.cfg in the current directory. + on Windows/Mac; and setup.cfg in the current directory. + + The file in the user's home directory can be disabled with the + --no-user-cfg option. """ files = [] check_environ() @@ -336,15 +357,19 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - user_file = os.path.join(os.path.expanduser('~'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) + if self.want_user_cfg: + user_file = os.path.join(os.path.expanduser('~'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" if os.path.isfile(local_file): files.append(local_file) + if DEBUG: + self.announce("using config files: %s" % ', '.join(files)) + return files def parse_config_files(self, filenames=None): diff --git a/tests/test_dist.py b/tests/test_dist.py index 5f032ca6..573d0b9b 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -209,6 +209,35 @@ class DistributionTestCase(support.TempdirManager, kwargs = {'level': 'ok2'} self.assertRaises(ValueError, dist.announce, args, kwargs) + def test_find_config_files_disable(self): + # Ticket #1180: Allow user to disable their home config file. + temp_home = self.mkdtemp() + if os.name == 'posix': + user_filename = os.path.join(temp_home, ".pydistutils.cfg") + else: + user_filename = os.path.join(temp_home, "pydistutils.cfg") + + with open(user_filename, 'w') as f: + f.write('[distutils]\n') + + def _expander(path): + return temp_home + + old_expander = os.path.expanduser + os.path.expanduser = _expander + try: + d = distutils.dist.Distribution() + all_files = d.find_config_files() + + d = distutils.dist.Distribution(attrs={'script_args': + ['--no-user-cfg']}) + files = d.find_config_files() + finally: + os.path.expanduser = old_expander + + # make sure --no-user-cfg disables the user cfg file + self.assertEquals(len(all_files)-1, len(files)) + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): -- cgit v1.2.1 From 76c1f4ad5d4d7ae75abcc44d2e1345648a137a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 27 Oct 2009 23:12:01 +0000 Subject: Merged revisions 75893 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75893 | tarek.ziade | 2009-10-28 00:06:10 +0100 (Wed, 28 Oct 2009) | 1 line Fixed #1180: Option to ignore ~/.pydistutils.cfg in Distutils ........ --- core.py | 5 +++-- dist.py | 35 ++++++++++++++++++++++++++++++----- tests/test_dist.py | 32 +++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/core.py b/core.py index 6e489203..95c03574 100644 --- a/core.py +++ b/core.py @@ -129,8 +129,9 @@ def setup (**attrs): if _setup_stop_after == "config": return dist - # Parse the command line; any command-line errors are the end user's - # fault, so turn them into SystemExit to suppress tracebacks. + # Parse the command line and override config files; any + # command-line errors are the end user's fault, so turn them into + # SystemExit to suppress tracebacks. try: ok = dist.parse_command_line() except DistutilsArgError as msg: diff --git a/dist.py b/dist.py index 1c1ea477..90af6e2d 100644 --- a/dist.py +++ b/dist.py @@ -53,7 +53,9 @@ class Distribution: ('quiet', 'q', "run quietly (turns verbosity off)"), ('dry-run', 'n', "don't actually do anything"), ('help', 'h', "show detailed help message"), - ] + ('no-user-cfg', None, + 'ignore pydistutils.cfg in your home directory'), + ] # 'common_usage' is a short (2-3 line) string describing the common # usage of the setup script. @@ -260,6 +262,22 @@ Common commands: (see '--help-commands' for more) else: sys.stderr.write(msg + "\n") + # no-user-cfg is handled before other command line args + # because other args override the config files, and this + # one is needed before we can load the config files. + # If attrs['script_args'] wasn't passed, assume false. + # + # This also make sure we just look at the global options + self.want_user_cfg = True + + if self.script_args is not None: + for arg in self.script_args: + if not arg.startswith('-'): + break + if arg == '--no-user-cfg': + self.want_user_cfg = False + break + self.finalize_options() def get_option_dict(self, command): @@ -311,7 +329,10 @@ Common commands: (see '--help-commands' for more) Distutils installation directory (ie. where the top-level Distutils __inst__.py file lives), a file in the user's home directory named .pydistutils.cfg on Unix and pydistutils.cfg - on Windows/Mac, and setup.cfg in the current directory. + on Windows/Mac; and setup.cfg in the current directory. + + The file in the user's home directory can be disabled with the + --no-user-cfg option. """ files = [] check_environ() @@ -331,15 +352,19 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - user_file = os.path.join(os.path.expanduser('~'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) + if self.want_user_cfg: + user_file = os.path.join(os.path.expanduser('~'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" if os.path.isfile(local_file): files.append(local_file) + if DEBUG: + self.announce("using config files: %s" % ', '.join(files)) + return files def parse_config_files(self, filenames=None): diff --git a/tests/test_dist.py b/tests/test_dist.py index be1010c6..b1b184ea 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -37,7 +37,8 @@ class TestDistribution(Distribution): return self._config_files -class DistributionTestCase(support.LoggingSilencer, +class DistributionTestCase(support.TempdirManager, + support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): @@ -180,6 +181,35 @@ class DistributionTestCase(support.LoggingSilencer, kwargs = {'level': 'ok2'} self.assertRaises(ValueError, dist.announce, args, kwargs) + def test_find_config_files_disable(self): + # Ticket #1180: Allow user to disable their home config file. + temp_home = self.mkdtemp() + if os.name == 'posix': + user_filename = os.path.join(temp_home, ".pydistutils.cfg") + else: + user_filename = os.path.join(temp_home, "pydistutils.cfg") + + with open(user_filename, 'w') as f: + f.write('[distutils]\n') + + def _expander(path): + return temp_home + + old_expander = os.path.expanduser + os.path.expanduser = _expander + try: + d = distutils.dist.Distribution() + all_files = d.find_config_files() + + d = distutils.dist.Distribution(attrs={'script_args': + ['--no-user-cfg']}) + files = d.find_config_files() + finally: + os.path.expanduser = old_expander + + # make sure --no-user-cfg disables the user cfg file + self.assertEquals(len(all_files)-1, len(files)) + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): -- cgit v1.2.1 From cd4dc3e1269f9c6da842c6565ece1408979b42f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 28 Oct 2009 06:45:18 +0000 Subject: removed spurious spaces --- errors.py | 47 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/errors.py b/errors.py index acecaccc..d9c47c76 100644 --- a/errors.py +++ b/errors.py @@ -10,90 +10,79 @@ symbols whose names start with "Distutils" and end with "Error".""" __revision__ = "$Id$" -class DistutilsError (Exception): +class DistutilsError(Exception): """The root of all Distutils evil.""" - pass -class DistutilsModuleError (DistutilsError): +class DistutilsModuleError(DistutilsError): """Unable to load an expected module, or to find an expected class within some module (in particular, command modules and classes).""" - pass -class DistutilsClassError (DistutilsError): +class DistutilsClassError(DistutilsError): """Some command class (or possibly distribution class, if anyone feels a need to subclass Distribution) is found not to be holding up its end of the bargain, ie. implementing some part of the "command "interface.""" - pass -class DistutilsGetoptError (DistutilsError): +class DistutilsGetoptError(DistutilsError): """The option table provided to 'fancy_getopt()' is bogus.""" - pass -class DistutilsArgError (DistutilsError): +class DistutilsArgError(DistutilsError): """Raised by fancy_getopt in response to getopt.error -- ie. an error in the command line usage.""" - pass -class DistutilsFileError (DistutilsError): +class DistutilsFileError(DistutilsError): """Any problems in the filesystem: expected file not found, etc. Typically this is for problems that we detect before IOError or OSError could be raised.""" - pass -class DistutilsOptionError (DistutilsError): +class DistutilsOptionError(DistutilsError): """Syntactic/semantic errors in command options, such as use of mutually conflicting options, or inconsistent options, badly-spelled values, etc. No distinction is made between option values originating in the setup script, the command line, config files, or what-have-you -- but if we *know* something originated in the setup script, we'll raise DistutilsSetupError instead.""" - pass -class DistutilsSetupError (DistutilsError): +class DistutilsSetupError(DistutilsError): """For errors that can be definitely blamed on the setup script, such as invalid keyword arguments to 'setup()'.""" - pass -class DistutilsPlatformError (DistutilsError): +class DistutilsPlatformError(DistutilsError): """We don't know how to do something on the current platform (but we do know how to do it on some platform) -- eg. trying to compile C files on a platform not supported by a CCompiler subclass.""" - pass -class DistutilsExecError (DistutilsError): +class DistutilsExecError(DistutilsError): """Any problems executing an external program (such as the C compiler, when compiling C files).""" - pass -class DistutilsInternalError (DistutilsError): +class DistutilsInternalError(DistutilsError): """Internal inconsistencies or impossibilities (obviously, this should never be seen if the code is working!).""" - pass -class DistutilsTemplateError (DistutilsError): +class DistutilsTemplateError(DistutilsError): """Syntax error in a file list template.""" class DistutilsByteCompileError(DistutilsError): """Byte compile error.""" # Exception classes used by the CCompiler implementation classes -class CCompilerError (Exception): +class CCompilerError(Exception): """Some compile/link operation failed.""" -class PreprocessError (CCompilerError): +class PreprocessError(CCompilerError): """Failure to preprocess one or more C/C++ files.""" -class CompileError (CCompilerError): +class CompileError(CCompilerError): """Failure to compile one or more C/C++ source files.""" -class LibError (CCompilerError): +class LibError(CCompilerError): """Failure to create a static library from one or more C/C++ object files.""" -class LinkError (CCompilerError): +class LinkError(CCompilerError): """Failure to link one or more C/C++ object files into an executable or shared library file.""" -class UnknownFileError (CCompilerError): +class UnknownFileError(CCompilerError): """Attempt to process an unknown file type.""" -- cgit v1.2.1 From 3700b7dba0832dd3123775e0c74b8d6fb4ff25ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 28 Oct 2009 06:48:27 +0000 Subject: Merged revisions 75901 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75901 | tarek.ziade | 2009-10-28 07:45:18 +0100 (Wed, 28 Oct 2009) | 1 line removed spurious spaces ........ --- errors.py | 47 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/errors.py b/errors.py index acecaccc..d9c47c76 100644 --- a/errors.py +++ b/errors.py @@ -10,90 +10,79 @@ symbols whose names start with "Distutils" and end with "Error".""" __revision__ = "$Id$" -class DistutilsError (Exception): +class DistutilsError(Exception): """The root of all Distutils evil.""" - pass -class DistutilsModuleError (DistutilsError): +class DistutilsModuleError(DistutilsError): """Unable to load an expected module, or to find an expected class within some module (in particular, command modules and classes).""" - pass -class DistutilsClassError (DistutilsError): +class DistutilsClassError(DistutilsError): """Some command class (or possibly distribution class, if anyone feels a need to subclass Distribution) is found not to be holding up its end of the bargain, ie. implementing some part of the "command "interface.""" - pass -class DistutilsGetoptError (DistutilsError): +class DistutilsGetoptError(DistutilsError): """The option table provided to 'fancy_getopt()' is bogus.""" - pass -class DistutilsArgError (DistutilsError): +class DistutilsArgError(DistutilsError): """Raised by fancy_getopt in response to getopt.error -- ie. an error in the command line usage.""" - pass -class DistutilsFileError (DistutilsError): +class DistutilsFileError(DistutilsError): """Any problems in the filesystem: expected file not found, etc. Typically this is for problems that we detect before IOError or OSError could be raised.""" - pass -class DistutilsOptionError (DistutilsError): +class DistutilsOptionError(DistutilsError): """Syntactic/semantic errors in command options, such as use of mutually conflicting options, or inconsistent options, badly-spelled values, etc. No distinction is made between option values originating in the setup script, the command line, config files, or what-have-you -- but if we *know* something originated in the setup script, we'll raise DistutilsSetupError instead.""" - pass -class DistutilsSetupError (DistutilsError): +class DistutilsSetupError(DistutilsError): """For errors that can be definitely blamed on the setup script, such as invalid keyword arguments to 'setup()'.""" - pass -class DistutilsPlatformError (DistutilsError): +class DistutilsPlatformError(DistutilsError): """We don't know how to do something on the current platform (but we do know how to do it on some platform) -- eg. trying to compile C files on a platform not supported by a CCompiler subclass.""" - pass -class DistutilsExecError (DistutilsError): +class DistutilsExecError(DistutilsError): """Any problems executing an external program (such as the C compiler, when compiling C files).""" - pass -class DistutilsInternalError (DistutilsError): +class DistutilsInternalError(DistutilsError): """Internal inconsistencies or impossibilities (obviously, this should never be seen if the code is working!).""" - pass -class DistutilsTemplateError (DistutilsError): +class DistutilsTemplateError(DistutilsError): """Syntax error in a file list template.""" class DistutilsByteCompileError(DistutilsError): """Byte compile error.""" # Exception classes used by the CCompiler implementation classes -class CCompilerError (Exception): +class CCompilerError(Exception): """Some compile/link operation failed.""" -class PreprocessError (CCompilerError): +class PreprocessError(CCompilerError): """Failure to preprocess one or more C/C++ files.""" -class CompileError (CCompilerError): +class CompileError(CCompilerError): """Failure to compile one or more C/C++ source files.""" -class LibError (CCompilerError): +class LibError(CCompilerError): """Failure to create a static library from one or more C/C++ object files.""" -class LinkError (CCompilerError): +class LinkError(CCompilerError): """Failure to link one or more C/C++ object files into an executable or shared library file.""" -class UnknownFileError (CCompilerError): +class UnknownFileError(CCompilerError): """Attempt to process an unknown file type.""" -- cgit v1.2.1 From 1c2453667055bf68b5ca93f65db3efac9fc4c1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 1 Nov 2009 22:33:45 +0000 Subject: fixed stdout alteration in test_distutils --- tests/test_build_py.py | 3 ++- tests/test_util.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index bfe61542..472591dc 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -69,6 +69,7 @@ class BuildPyTestCase(support.TempdirManager, open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) + old_stdout = sys.stdout sys.stdout = StringIO.StringIO() try: @@ -87,7 +88,7 @@ class BuildPyTestCase(support.TempdirManager, finally: # Restore state. os.chdir(cwd) - sys.stdout = sys.__stdout__ + sys.stdout = old_stdout def test_dont_write_bytecode(self): # makes sure byte_compile is not used diff --git a/tests/test_util.py b/tests/test_util.py index 55ef160f..6722997f 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -60,6 +60,8 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): util.find_executable = self._find_executable self._exes = {} self.old_popen = subprocess.Popen + self.old_stdout = sys.stdout + self.old_stderr = sys.stderr FakePopen.test_class = self subprocess.Popen = FakePopen @@ -79,6 +81,8 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sysconfig._config_vars = copy(self._config_vars) util.find_executable = self.old_find_executable subprocess.Popen = self.old_popen + sys.old_stdout = self.old_stdout + sys.old_stderr = self.old_stderr super(UtilTestCase, self).tearDown() def _set_uname(self, uname): -- cgit v1.2.1 From 77f13813e91f896c9e368bf4b979f85530e05f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 1 Nov 2009 22:38:44 +0000 Subject: Merged revisions 76042 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76042 | tarek.ziade | 2009-11-01 23:33:45 +0100 (Sun, 01 Nov 2009) | 1 line fixed stdout alteration in test_distutils ........ --- tests/test_build_py.py | 3 ++- tests/test_util.py | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 582f2463..3e45f6e8 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -69,6 +69,7 @@ class BuildPyTestCase(support.TempdirManager, open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) + old_stdout = sys.stdout sys.stdout = io.StringIO() try: @@ -87,7 +88,7 @@ class BuildPyTestCase(support.TempdirManager, finally: # Restore state. os.chdir(cwd) - sys.stdout = sys.__stdout__ + sys.stdout = old_stdout def test_dont_write_bytecode(self): # makes sure byte_compile is not used diff --git a/tests/test_util.py b/tests/test_util.py index a2f8ed2b..41724726 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -60,6 +60,8 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): util.find_executable = self._find_executable self._exes = {} self.old_popen = subprocess.Popen + self.old_stdout = sys.stdout + self.old_stderr = sys.stderr FakePopen.test_class = self subprocess.Popen = FakePopen @@ -79,6 +81,8 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sysconfig._config_vars = copy(self._config_vars) util.find_executable = self.old_find_executable subprocess.Popen = self.old_popen + sys.old_stdout = self.old_stdout + sys.old_stderr = self.old_stderr super(UtilTestCase, self).tearDown() def _set_uname(self, uname): -- cgit v1.2.1 From 5f34bac6360a547a0797683883da85233e10a070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 1 Nov 2009 23:04:26 +0000 Subject: reapplied r74493 (after #6665 fix has been backported) --- filelist.py | 4 +++- tests/test_filelist.py | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/filelist.py b/filelist.py index 3ce56350..88b33c7c 100644 --- a/filelist.py +++ b/filelist.py @@ -344,7 +344,9 @@ def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): pattern_re = '' if prefix is not None: - prefix_re = (glob_to_re(prefix))[0:-1] # ditch trailing $ + # ditch end of pattern character + empty_pattern = glob_to_re('') + prefix_re = (glob_to_re(prefix))[:-len(empty_pattern)] pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) else: # no prefix -- respect anchor flag if anchor: diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 86db5574..1faccfae 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -6,15 +6,15 @@ class FileListTestCase(unittest.TestCase): def test_glob_to_re(self): # simple cases - self.assertEquals(glob_to_re('foo*'), 'foo[^/]*$') - self.assertEquals(glob_to_re('foo?'), 'foo[^/]$') - self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]$') + self.assertEquals(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)') + self.assertEquals(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)') + self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)') # special cases - self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*$') - self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*$') - self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]$') - self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]$') + self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)') + self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)') + self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') + self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') def test_suite(): return unittest.makeSuite(FileListTestCase) -- cgit v1.2.1 From 5ff2e55da5d92f0b33021ba205f787d6cb80358c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 1 Nov 2009 23:11:55 +0000 Subject: reapplied r74493 (after #6665 fix has been backported) --- filelist.py | 4 +++- tests/test_build_py.py | 3 ++- tests/test_filelist.py | 14 +++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/filelist.py b/filelist.py index 58a2bfb1..06a8da9a 100644 --- a/filelist.py +++ b/filelist.py @@ -312,7 +312,9 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): pattern_re = '' if prefix is not None: - prefix_re = (glob_to_re(prefix))[0:-1] # ditch trailing $ + # ditch end of pattern character + empty_pattern = glob_to_re('') + prefix_re = (glob_to_re(prefix))[:-len(empty_pattern)] pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) else: # no prefix -- respect anchor flag if anchor: diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 582f2463..3e45f6e8 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -69,6 +69,7 @@ class BuildPyTestCase(support.TempdirManager, open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) + old_stdout = sys.stdout sys.stdout = io.StringIO() try: @@ -87,7 +88,7 @@ class BuildPyTestCase(support.TempdirManager, finally: # Restore state. os.chdir(cwd) - sys.stdout = sys.__stdout__ + sys.stdout = old_stdout def test_dont_write_bytecode(self): # makes sure byte_compile is not used diff --git a/tests/test_filelist.py b/tests/test_filelist.py index b9db8f61..331180d2 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -9,15 +9,15 @@ class FileListTestCase(unittest.TestCase): def test_glob_to_re(self): # simple cases - self.assertEquals(glob_to_re('foo*'), 'foo[^/]*$') - self.assertEquals(glob_to_re('foo?'), 'foo[^/]$') - self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]$') + self.assertEquals(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)') + self.assertEquals(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)') + self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)') # special cases - self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*$') - self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*$') - self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]$') - self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]$') + self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)') + self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)') + self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') + self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') def test_debug_print(self): file_list = FileList() -- cgit v1.2.1 From bf2f5e7e511577261c9d9d41af73c090c9293a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 1 Nov 2009 23:17:51 +0000 Subject: Merged revisions 76042 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76042 | tarek.ziade | 2009-11-01 23:33:45 +0100 (Sun, 01 Nov 2009) | 1 line fixed stdout alteration in test_distutils ........ --- tests/test_build_py.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 54a4ed80..4b045474 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -69,6 +69,7 @@ class BuildPyTestCase(support.TempdirManager, open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) + old_stdout = sys.stdout sys.stdout = StringIO.StringIO() try: @@ -87,7 +88,7 @@ class BuildPyTestCase(support.TempdirManager, finally: # Restore state. os.chdir(cwd) - sys.stdout = sys.__stdout__ + sys.stdout = old_stdout def test_suite(): return unittest.makeSuite(BuildPyTestCase) -- cgit v1.2.1 From 868b46b50b94efc30bfdde021844d1020aafa4b6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 13 Nov 2009 02:25:08 +0000 Subject: Merged revisions 75149,75260-75263,75265-75267,75292,75300,75376,75405,75429-75433,75437,75445,75501,75551,75572,75589-75591,75657,75742,75868,75952-75957,76057,76105,76139,76143,76162,76223 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75149 | gregory.p.smith | 2009-09-29 16:56:31 -0500 (Tue, 29 Sep 2009) | 3 lines Mention issue6972 in extractall docs about overwriting things outside of the supplied path. ........ r75260 | andrew.kuchling | 2009-10-05 16:24:20 -0500 (Mon, 05 Oct 2009) | 1 line Wording fix ........ r75261 | andrew.kuchling | 2009-10-05 16:24:35 -0500 (Mon, 05 Oct 2009) | 1 line Fix narkup ........ r75262 | andrew.kuchling | 2009-10-05 16:25:03 -0500 (Mon, 05 Oct 2009) | 1 line Document 'skip' parameter to constructor ........ r75263 | andrew.kuchling | 2009-10-05 16:25:35 -0500 (Mon, 05 Oct 2009) | 1 line Note side benefit of socket.create_connection() ........ r75265 | andrew.kuchling | 2009-10-05 17:31:11 -0500 (Mon, 05 Oct 2009) | 1 line Reword sentence ........ r75266 | andrew.kuchling | 2009-10-05 17:32:48 -0500 (Mon, 05 Oct 2009) | 1 line Use standard comma punctuation; reword some sentences in the docs ........ r75267 | andrew.kuchling | 2009-10-05 17:42:56 -0500 (Mon, 05 Oct 2009) | 1 line Backport r73983: Document the thousands separator. ........ r75292 | benjamin.peterson | 2009-10-08 22:11:36 -0500 (Thu, 08 Oct 2009) | 1 line death to old CVS keyword ........ r75300 | benjamin.peterson | 2009-10-09 16:48:14 -0500 (Fri, 09 Oct 2009) | 1 line fix some coding style ........ r75376 | benjamin.peterson | 2009-10-11 20:26:07 -0500 (Sun, 11 Oct 2009) | 1 line platform we don't care about ........ r75405 | neil.schemenauer | 2009-10-14 12:17:14 -0500 (Wed, 14 Oct 2009) | 4 lines Issue #1754094: Improve the stack depth calculation in the compiler. There should be no other effect than a small decrease in memory use. Patch by Christopher Tur Lesniewski-Laas. ........ r75429 | benjamin.peterson | 2009-10-14 20:47:28 -0500 (Wed, 14 Oct 2009) | 1 line pep8ify if blocks ........ r75430 | benjamin.peterson | 2009-10-14 20:49:37 -0500 (Wed, 14 Oct 2009) | 1 line use floor division and add a test that exercises the tabsize codepath ........ r75431 | benjamin.peterson | 2009-10-14 20:56:25 -0500 (Wed, 14 Oct 2009) | 1 line change test to what I intended ........ r75432 | benjamin.peterson | 2009-10-14 22:05:39 -0500 (Wed, 14 Oct 2009) | 1 line some cleanups ........ r75433 | benjamin.peterson | 2009-10-14 22:06:55 -0500 (Wed, 14 Oct 2009) | 1 line make inspect.isabstract() always return a boolean; add a test for it, too #7069 ........ r75437 | benjamin.peterson | 2009-10-15 10:44:46 -0500 (Thu, 15 Oct 2009) | 1 line only clear a module's __dict__ if the module is the only one with a reference to it #7140 ........ r75445 | vinay.sajip | 2009-10-16 09:06:44 -0500 (Fri, 16 Oct 2009) | 1 line Issue #7120: logging: Removed import of multiprocessing which is causing crash in GAE. ........ r75501 | antoine.pitrou | 2009-10-18 13:37:11 -0500 (Sun, 18 Oct 2009) | 3 lines Add a comment about unreachable code, and fix a typo ........ r75551 | benjamin.peterson | 2009-10-19 22:14:10 -0500 (Mon, 19 Oct 2009) | 1 line use property api ........ r75572 | benjamin.peterson | 2009-10-20 16:55:17 -0500 (Tue, 20 Oct 2009) | 1 line clarify buffer arg #7178 ........ r75589 | benjamin.peterson | 2009-10-21 21:26:47 -0500 (Wed, 21 Oct 2009) | 1 line whitespace ........ r75590 | benjamin.peterson | 2009-10-21 21:36:47 -0500 (Wed, 21 Oct 2009) | 1 line rewrite to be nice to other implementations ........ r75591 | benjamin.peterson | 2009-10-21 21:50:38 -0500 (Wed, 21 Oct 2009) | 4 lines rewrite for style, clarify, and comments Also, use the hasattr() like scheme of allowing BaseException exceptions through. ........ r75657 | antoine.pitrou | 2009-10-24 07:41:27 -0500 (Sat, 24 Oct 2009) | 3 lines Fix compilation error in debug mode. ........ r75742 | benjamin.peterson | 2009-10-26 17:51:16 -0500 (Mon, 26 Oct 2009) | 1 line use 'is' instead of id() ........ r75868 | benjamin.peterson | 2009-10-27 15:59:18 -0500 (Tue, 27 Oct 2009) | 1 line test expect base classes ........ r75952 | georg.brandl | 2009-10-29 15:38:32 -0500 (Thu, 29 Oct 2009) | 1 line Use the correct function name in docstring. ........ r75953 | georg.brandl | 2009-10-29 15:39:50 -0500 (Thu, 29 Oct 2009) | 1 line Remove mention of the old -X command line switch. ........ r75954 | georg.brandl | 2009-10-29 15:53:00 -0500 (Thu, 29 Oct 2009) | 1 line Use constants instead of magic integers for test result. Do not re-run with --verbose3 for environment changing tests. ........ r75955 | georg.brandl | 2009-10-29 15:54:03 -0500 (Thu, 29 Oct 2009) | 1 line Use a single style for all the docstrings in the math module. ........ r75956 | georg.brandl | 2009-10-29 16:16:34 -0500 (Thu, 29 Oct 2009) | 1 line I do not think the "railroad" program mentioned is still available. ........ r75957 | georg.brandl | 2009-10-29 16:44:56 -0500 (Thu, 29 Oct 2009) | 1 line Fix constant name. ........ r76057 | benjamin.peterson | 2009-11-02 09:06:45 -0600 (Mon, 02 Nov 2009) | 1 line prevent a rather unlikely segfault ........ r76105 | georg.brandl | 2009-11-04 01:38:12 -0600 (Wed, 04 Nov 2009) | 1 line #7259: show correct equivalent for operator.i* operations in docstring; fix minor issues in operator docs. ........ r76139 | benjamin.peterson | 2009-11-06 19:04:38 -0600 (Fri, 06 Nov 2009) | 1 line spelling ........ r76143 | georg.brandl | 2009-11-07 02:26:07 -0600 (Sat, 07 Nov 2009) | 1 line #7271: fix typo. ........ r76162 | benjamin.peterson | 2009-11-08 22:10:53 -0600 (Sun, 08 Nov 2009) | 1 line discuss how to use -p ........ r76223 | georg.brandl | 2009-11-12 02:29:46 -0600 (Thu, 12 Nov 2009) | 1 line Give the profile module a module directive. ........ --- command/check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/check.py b/command/check.py index 9a8fca1d..12844cbf 100644 --- a/command/check.py +++ b/command/check.py @@ -92,7 +92,7 @@ class check(Command): missing.append(attr) if missing: - self.warn("missing required meta-data: %s" % ' ,'.join(missing)) + self.warn("missing required meta-data: %s" % ', '.join(missing)) if metadata.author: if not metadata.author_email: self.warn("missing meta-data: if 'author' supplied, " + -- cgit v1.2.1 From 83b75ba535abc50e9f0c9ac860903e0f97b28703 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 13 Nov 2009 02:29:35 +0000 Subject: Merged revisions 76235 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r76235 | benjamin.peterson | 2009-11-12 20:25:08 -0600 (Thu, 12 Nov 2009) | 170 lines Merged revisions 75149,75260-75263,75265-75267,75292,75300,75376,75405,75429-75433,75437,75445,75501,75551,75572,75589-75591,75657,75742,75868,75952-75957,76057,76105,76139,76143,76162,76223 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75149 | gregory.p.smith | 2009-09-29 16:56:31 -0500 (Tue, 29 Sep 2009) | 3 lines Mention issue6972 in extractall docs about overwriting things outside of the supplied path. ........ r75260 | andrew.kuchling | 2009-10-05 16:24:20 -0500 (Mon, 05 Oct 2009) | 1 line Wording fix ........ r75261 | andrew.kuchling | 2009-10-05 16:24:35 -0500 (Mon, 05 Oct 2009) | 1 line Fix narkup ........ r75262 | andrew.kuchling | 2009-10-05 16:25:03 -0500 (Mon, 05 Oct 2009) | 1 line Document 'skip' parameter to constructor ........ r75263 | andrew.kuchling | 2009-10-05 16:25:35 -0500 (Mon, 05 Oct 2009) | 1 line Note side benefit of socket.create_connection() ........ r75265 | andrew.kuchling | 2009-10-05 17:31:11 -0500 (Mon, 05 Oct 2009) | 1 line Reword sentence ........ r75266 | andrew.kuchling | 2009-10-05 17:32:48 -0500 (Mon, 05 Oct 2009) | 1 line Use standard comma punctuation; reword some sentences in the docs ........ r75267 | andrew.kuchling | 2009-10-05 17:42:56 -0500 (Mon, 05 Oct 2009) | 1 line Backport r73983: Document the thousands separator. ........ r75292 | benjamin.peterson | 2009-10-08 22:11:36 -0500 (Thu, 08 Oct 2009) | 1 line death to old CVS keyword ........ r75300 | benjamin.peterson | 2009-10-09 16:48:14 -0500 (Fri, 09 Oct 2009) | 1 line fix some coding style ........ r75376 | benjamin.peterson | 2009-10-11 20:26:07 -0500 (Sun, 11 Oct 2009) | 1 line platform we don't care about ........ r75405 | neil.schemenauer | 2009-10-14 12:17:14 -0500 (Wed, 14 Oct 2009) | 4 lines Issue #1754094: Improve the stack depth calculation in the compiler. There should be no other effect than a small decrease in memory use. Patch by Christopher Tur Lesniewski-Laas. ........ r75429 | benjamin.peterson | 2009-10-14 20:47:28 -0500 (Wed, 14 Oct 2009) | 1 line pep8ify if blocks ........ r75430 | benjamin.peterson | 2009-10-14 20:49:37 -0500 (Wed, 14 Oct 2009) | 1 line use floor division and add a test that exercises the tabsize codepath ........ r75431 | benjamin.peterson | 2009-10-14 20:56:25 -0500 (Wed, 14 Oct 2009) | 1 line change test to what I intended ........ r75432 | benjamin.peterson | 2009-10-14 22:05:39 -0500 (Wed, 14 Oct 2009) | 1 line some cleanups ........ r75433 | benjamin.peterson | 2009-10-14 22:06:55 -0500 (Wed, 14 Oct 2009) | 1 line make inspect.isabstract() always return a boolean; add a test for it, too #7069 ........ r75437 | benjamin.peterson | 2009-10-15 10:44:46 -0500 (Thu, 15 Oct 2009) | 1 line only clear a module's __dict__ if the module is the only one with a reference to it #7140 ........ r75445 | vinay.sajip | 2009-10-16 09:06:44 -0500 (Fri, 16 Oct 2009) | 1 line Issue #7120: logging: Removed import of multiprocessing which is causing crash in GAE. ........ r75501 | antoine.pitrou | 2009-10-18 13:37:11 -0500 (Sun, 18 Oct 2009) | 3 lines Add a comment about unreachable code, and fix a typo ........ r75551 | benjamin.peterson | 2009-10-19 22:14:10 -0500 (Mon, 19 Oct 2009) | 1 line use property api ........ r75572 | benjamin.peterson | 2009-10-20 16:55:17 -0500 (Tue, 20 Oct 2009) | 1 line clarify buffer arg #7178 ........ r75589 | benjamin.peterson | 2009-10-21 21:26:47 -0500 (Wed, 21 Oct 2009) | 1 line whitespace ........ r75590 | benjamin.peterson | 2009-10-21 21:36:47 -0500 (Wed, 21 Oct 2009) | 1 line rewrite to be nice to other implementations ........ r75591 | benjamin.peterson | 2009-10-21 21:50:38 -0500 (Wed, 21 Oct 2009) | 4 lines rewrite for style, clarify, and comments Also, use the hasattr() like scheme of allowing BaseException exceptions through. ........ r75657 | antoine.pitrou | 2009-10-24 07:41:27 -0500 (Sat, 24 Oct 2009) | 3 lines Fix compilation error in debug mode. ........ r75742 | benjamin.peterson | 2009-10-26 17:51:16 -0500 (Mon, 26 Oct 2009) | 1 line use 'is' instead of id() ........ r75868 | benjamin.peterson | 2009-10-27 15:59:18 -0500 (Tue, 27 Oct 2009) | 1 line test expect base classes ........ r75952 | georg.brandl | 2009-10-29 15:38:32 -0500 (Thu, 29 Oct 2009) | 1 line Use the correct function name in docstring. ........ r75953 | georg.brandl | 2009-10-29 15:39:50 -0500 (Thu, 29 Oct 2009) | 1 line Remove mention of the old -X command line switch. ........ r75954 | georg.brandl | 2009-10-29 15:53:00 -0500 (Thu, 29 Oct 2009) | 1 line Use constants instead of magic integers for test result. Do not re-run with --verbose3 for environment changing tests. ........ r75955 | georg.brandl | 2009-10-29 15:54:03 -0500 (Thu, 29 Oct 2009) | 1 line Use a single style for all the docstrings in the math module. ........ r75956 | georg.brandl | 2009-10-29 16:16:34 -0500 (Thu, 29 Oct 2009) | 1 line I do not think the "railroad" program mentioned is still available. ........ r75957 | georg.brandl | 2009-10-29 16:44:56 -0500 (Thu, 29 Oct 2009) | 1 line Fix constant name. ........ r76057 | benjamin.peterson | 2009-11-02 09:06:45 -0600 (Mon, 02 Nov 2009) | 1 line prevent a rather unlikely segfault ........ r76105 | georg.brandl | 2009-11-04 01:38:12 -0600 (Wed, 04 Nov 2009) | 1 line #7259: show correct equivalent for operator.i* operations in docstring; fix minor issues in operator docs. ........ r76139 | benjamin.peterson | 2009-11-06 19:04:38 -0600 (Fri, 06 Nov 2009) | 1 line spelling ........ r76143 | georg.brandl | 2009-11-07 02:26:07 -0600 (Sat, 07 Nov 2009) | 1 line #7271: fix typo. ........ r76162 | benjamin.peterson | 2009-11-08 22:10:53 -0600 (Sun, 08 Nov 2009) | 1 line discuss how to use -p ........ r76223 | georg.brandl | 2009-11-12 02:29:46 -0600 (Thu, 12 Nov 2009) | 1 line Give the profile module a module directive. ........ ................ --- command/check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/check.py b/command/check.py index 9a8fca1d..12844cbf 100644 --- a/command/check.py +++ b/command/check.py @@ -92,7 +92,7 @@ class check(Command): missing.append(attr) if missing: - self.warn("missing required meta-data: %s" % ' ,'.join(missing)) + self.warn("missing required meta-data: %s" % ', '.join(missing)) if metadata.author: if not metadata.author_email: self.warn("missing meta-data: if 'author' supplied, " + -- cgit v1.2.1 From bc0d08d5773c2fcfae1ded350a6ad378508a59cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 18 Nov 2009 08:46:56 +0000 Subject: #7293: distutils.test_msvc9compiler now uses a key that exists on any fresh windows install --- tests/test_msvc9compiler.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 21242a8e..69a393ca 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -44,17 +44,17 @@ class msvc9compilerTestCase(unittest.TestCase): # looking for values that should exist on all # windows registeries versions. - path = r'Software\Microsoft\Notepad' - v = Reg.get_value(path, u"lfitalic") - self.assertTrue(v in (0, 1)) + path = r'Control Panel\Desktop' + v = Reg.get_value(path, u'dragfullwindows') + self.assertTrue(v in (u'0', u'1')) import _winreg HKCU = _winreg.HKEY_CURRENT_USER keys = Reg.read_keys(HKCU, 'xxxx') self.assertEquals(keys, None) - keys = Reg.read_keys(HKCU, r'Software\Microsoft') - self.assertTrue('Notepad' in keys) + keys = Reg.read_keys(HKCU, r'Control Panel') + self.assertTrue('Desktop' in keys) def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) -- cgit v1.2.1 From 4927915841d4a7a3bcfd0b2f9d824e9e06fee118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 18 Nov 2009 09:32:34 +0000 Subject: Merged revisions 76358 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76358 | tarek.ziade | 2009-11-18 09:46:56 +0100 (Wed, 18 Nov 2009) | 1 line #7293: distutils.test_msvc9compiler now uses a key that exists on any fresh windows install ........ --- tests/test_msvc9compiler.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 73827988..7cf1a12e 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -44,17 +44,17 @@ class msvc9compilerTestCase(unittest.TestCase): # looking for values that should exist on all # windows registeries versions. - path = r'Software\Microsoft\Notepad' - v = Reg.get_value(path, "lfitalic") - self.assertTrue(v in (0, 1)) + path = r'Control Panel\Desktop' + v = Reg.get_value(path, 'dragfullwindows') + self.assertTrue(v in ('0', '1')) import winreg HKCU = winreg.HKEY_CURRENT_USER keys = Reg.read_keys(HKCU, 'xxxx') self.assertEquals(keys, None) - keys = Reg.read_keys(HKCU, r'Software\Microsoft') - self.assertTrue('Notepad' in keys) + keys = Reg.read_keys(HKCU, r'Control Panel') + self.assertTrue('Desktop' in keys) def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) -- cgit v1.2.1 From 6485ce9b0c624eb39ffb08c0bb264fedb8835be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 18 Nov 2009 10:19:38 +0000 Subject: Merged revisions 76360 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r76360 | tarek.ziade | 2009-11-18 10:32:34 +0100 (Wed, 18 Nov 2009) | 9 lines Merged revisions 76358 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76358 | tarek.ziade | 2009-11-18 09:46:56 +0100 (Wed, 18 Nov 2009) | 1 line #7293: distutils.test_msvc9compiler now uses a key that exists on any fresh windows install ........ ................ --- tests/test_msvc9compiler.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 73827988..7cf1a12e 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -44,17 +44,17 @@ class msvc9compilerTestCase(unittest.TestCase): # looking for values that should exist on all # windows registeries versions. - path = r'Software\Microsoft\Notepad' - v = Reg.get_value(path, "lfitalic") - self.assertTrue(v in (0, 1)) + path = r'Control Panel\Desktop' + v = Reg.get_value(path, 'dragfullwindows') + self.assertTrue(v in ('0', '1')) import winreg HKCU = winreg.HKEY_CURRENT_USER keys = Reg.read_keys(HKCU, 'xxxx') self.assertEquals(keys, None) - keys = Reg.read_keys(HKCU, r'Software\Microsoft') - self.assertTrue('Notepad' in keys) + keys = Reg.read_keys(HKCU, r'Control Panel') + self.assertTrue('Desktop' in keys) def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) -- cgit v1.2.1 From 3608b265680a9a04e19dc7e014ab8a3333dbd6ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 19 Nov 2009 05:33:16 +0000 Subject: dragfullwindows can have value 2 --- tests/test_msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 69a393ca..1264854d 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ class msvc9compilerTestCase(unittest.TestCase): # windows registeries versions. path = r'Control Panel\Desktop' v = Reg.get_value(path, u'dragfullwindows') - self.assertTrue(v in (u'0', u'1')) + self.assertTrue(v in (u'0', u'1', u'2')) import _winreg HKCU = _winreg.HKEY_CURRENT_USER -- cgit v1.2.1 From 83bf5a149567b658f71d0b78a622752019c03608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 19 Nov 2009 05:39:00 +0000 Subject: Merged revisions 76399 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76399 | tarek.ziade | 2009-11-19 06:33:16 +0100 (Thu, 19 Nov 2009) | 1 line dragfullwindows can have value 2 ........ --- tests/test_msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 7cf1a12e..05d34e60 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ class msvc9compilerTestCase(unittest.TestCase): # windows registeries versions. path = r'Control Panel\Desktop' v = Reg.get_value(path, 'dragfullwindows') - self.assertTrue(v in ('0', '1')) + self.assertTrue(v in ('0', '1', '2')) import winreg HKCU = winreg.HKEY_CURRENT_USER -- cgit v1.2.1 From 2ed389d59093022bf097e0ee48edd6c7ff5520b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 19 Nov 2009 05:41:34 +0000 Subject: Merged revisions 76401 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r76401 | tarek.ziade | 2009-11-19 06:39:00 +0100 (Thu, 19 Nov 2009) | 9 lines Merged revisions 76399 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76399 | tarek.ziade | 2009-11-19 06:33:16 +0100 (Thu, 19 Nov 2009) | 1 line dragfullwindows can have value 2 ........ ................ --- tests/test_msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 7cf1a12e..05d34e60 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ class msvc9compilerTestCase(unittest.TestCase): # windows registeries versions. path = r'Control Panel\Desktop' v = Reg.get_value(path, 'dragfullwindows') - self.assertTrue(v in ('0', '1')) + self.assertTrue(v in ('0', '1', '2')) import winreg HKCU = winreg.HKEY_CURRENT_USER -- cgit v1.2.1 From 6acee8f5f4c9fe09ebfc6a042b2684b8be769802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 29 Nov 2009 22:20:30 +0000 Subject: Fixed #7408: dropped group ownership checking because it relies on os-specific rules --- tests/test_sdist.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index b8e3dca5..20b20aae 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -336,10 +336,13 @@ class SDistTestCase(PyPIRCCommandTestCase): # making sure we have the good rights archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') archive = tarfile.open(archive_name) + + # note that we are not testing the group ownership here + # because, depending on the platforms and the container + # rights (see #7408) try: for member in archive.getmembers(): self.assertEquals(member.uid, os.getuid()) - self.assertEquals(member.gid, os.getgid()) finally: archive.close() -- cgit v1.2.1 From 8e0598f5a5773af651590ef4905b9eb624fe8ce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 29 Nov 2009 22:24:57 +0000 Subject: Merged revisions 76588 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76588 | tarek.ziade | 2009-11-29 23:20:30 +0100 (Sun, 29 Nov 2009) | 1 line Fixed #7408: dropped group ownership checking because it relies on os-specific rules ........ --- tests/test_sdist.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index c10e973e..e0f1e937 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -336,10 +336,13 @@ class SDistTestCase(PyPIRCCommandTestCase): # making sure we have the good rights archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') archive = tarfile.open(archive_name) + + # note that we are not testing the group ownership here + # because, depending on the platforms and the container + # rights (see #7408) try: for member in archive.getmembers(): self.assertEquals(member.uid, os.getuid()) - self.assertEquals(member.gid, os.getgid()) finally: archive.close() -- cgit v1.2.1 From 97ba6f9c79ad12e0fbefc6aa947e0351f9cca9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 3 Dec 2009 20:53:51 +0000 Subject: Issue #4120: Drop reference to CRT from manifest when building extensions with msvc9compiler. --- msvc9compiler.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/msvc9compiler.py b/msvc9compiler.py index 00d76d4b..c2287d92 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,6 +17,7 @@ __revision__ = "$Id$" import os import subprocess import sys +import re from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -646,6 +647,28 @@ class MSVCCompiler(CCompiler) : mfid = 1 else: mfid = 2 + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + manifest_f = open(temp_manifest, "rb") + manifest_buf = manifest_f.read() + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = "\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + manifest_f = open(temp_manifest, "wb") + manifest_f.write(manifest_buf) + manifest_f.close() + except IOError: + pass out_arg = '-outputresource:%s;%s' % (output_filename, mfid) try: self.spawn(['mt.exe', '-nologo', '-manifest', -- cgit v1.2.1 From 0222ecd7eaa7541d1515387342da5bb8ee3d4a4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 3 Dec 2009 20:56:15 +0000 Subject: Merged revisions 76651 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76651 | martin.v.loewis | 2009-12-03 21:53:51 +0100 (Do, 03 Dez 2009) | 3 lines Issue #4120: Drop reference to CRT from manifest when building extensions with msvc9compiler. ........ --- msvc9compiler.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 68b77758..9bf54c10 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,6 +17,7 @@ __revision__ = "$Id$" import os import subprocess import sys +import re from distutils.errors import (DistutilsExecError, DistutilsPlatformError, CompileError, LibError, LinkError) from distutils.ccompiler import (CCompiler, gen_preprocess_options, @@ -641,7 +642,32 @@ class MSVCCompiler(CCompiler) : # will still consider the DLL up-to-date, but it will not have a # manifest. Maybe we should link to a temp file? OTOH, that # implies a build environment error that shouldn't go undetected. - mfid = 1 if target_desc == CCompiler.EXECUTABLE else 2 + if target_desc == CCompiler.EXECUTABLE: + mfid = 1 + else: + mfid = 2 + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + manifest_f = open(temp_manifest, "rb") + manifest_buf = manifest_f.read() + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = "\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + manifest_f = open(temp_manifest, "wb") + manifest_f.write(manifest_buf) + manifest_f.close() + except IOError: + pass out_arg = '-outputresource:%s;%s' % (output_filename, mfid) try: self.spawn(['mt.exe', '-nologo', '-manifest', -- cgit v1.2.1 From 14820811451bddfe8c54b9bf234bb18953575046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 3 Dec 2009 20:57:49 +0000 Subject: Merged revisions 76651 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76651 | martin.v.loewis | 2009-12-03 21:53:51 +0100 (Do, 03 Dez 2009) | 3 lines Issue #4120: Drop reference to CRT from manifest when building extensions with msvc9compiler. ........ --- msvc9compiler.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/msvc9compiler.py b/msvc9compiler.py index ef895422..c84fb0b4 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,6 +17,7 @@ __revision__ = "$Id$" import os import subprocess import sys +import re from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -645,6 +646,28 @@ class MSVCCompiler(CCompiler) : mfid = 1 else: mfid = 2 + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + manifest_f = open(temp_manifest, "rb") + manifest_buf = manifest_f.read() + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = "\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + manifest_f = open(temp_manifest, "wb") + manifest_f.write(manifest_buf) + manifest_f.close() + except IOError: + pass out_arg = '-outputresource:%s;%s' % (output_filename, mfid) try: self.spawn(['mt.exe', '-nologo', '-manifest', -- cgit v1.2.1 From 625012af0113a6500c0a95a81ba0fdda5a2f0b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Thu, 3 Dec 2009 21:14:10 +0000 Subject: Merged revisions 76653 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r76653 | martin.v.loewis | 2009-12-03 21:57:49 +0100 (Do, 03 Dez 2009) | 10 lines Merged revisions 76651 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76651 | martin.v.loewis | 2009-12-03 21:53:51 +0100 (Do, 03 Dez 2009) | 3 lines Issue #4120: Drop reference to CRT from manifest when building extensions with msvc9compiler. ........ ................ --- msvc9compiler.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/msvc9compiler.py b/msvc9compiler.py index ef895422..c84fb0b4 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,6 +17,7 @@ __revision__ = "$Id$" import os import subprocess import sys +import re from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -645,6 +646,28 @@ class MSVCCompiler(CCompiler) : mfid = 1 else: mfid = 2 + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + manifest_f = open(temp_manifest, "rb") + manifest_buf = manifest_f.read() + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = "\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + manifest_f = open(temp_manifest, "wb") + manifest_f.write(manifest_buf) + manifest_f.close() + except IOError: + pass out_arg = '-outputresource:%s;%s' % (output_filename, mfid) try: self.spawn(['mt.exe', '-nologo', '-manifest', -- cgit v1.2.1 From 7ed6e18a1d4705bf067be40df44b831d9bab21e6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 5 Dec 2009 17:47:56 +0000 Subject: bump version to 2.7a1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e418214c..e8c213e4 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.6" +__version__ = "2.7a1" #--end constants-- -- cgit v1.2.1 From c9eb6ca234ce508e2232b761158c9439e59139f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 6 Dec 2009 09:22:40 +0000 Subject: Fixed #1923: make sure we don't strip meaningful whitespace in PKG-INFO Description field --- tests/test_dist.py | 16 ++++++++++++++++ util.py | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index 573d0b9b..8b141ca6 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -6,6 +6,7 @@ import StringIO import sys import unittest import warnings +import textwrap from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command @@ -381,6 +382,21 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, if line.strip() != ''] self.assertTrue(len(output) > 0) + def test_long_description(self): + long_desc = textwrap.dedent("""\ + example:: + We start here + and continue here + and end here.""") + attrs = {"name": "package", + "version": "1.0", + "long_description": long_desc} + + dist = distutils.dist.Distribution(attrs) + meta = self.format_metadata(dist) + meta = meta.replace('\n' + 8 * ' ', '\n') + self.assertTrue(long_desc in meta) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) diff --git a/util.py b/util.py index 6bff44f7..b8e4952f 100644 --- a/util.py +++ b/util.py @@ -558,8 +558,8 @@ def rfc822_escape(header): """Return a version of the string escaped for inclusion in an RFC-822 header, by ensuring there are 8 spaces space after each newline. """ - lines = [x.strip() for x in header.split('\n')] - sep = '\n' + 8*' ' + lines = header.split('\n') + sep = '\n' + 8 * ' ' return sep.join(lines) _RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') -- cgit v1.2.1 From 22b0f60962726ce1e614393de0af8061572adff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 6 Dec 2009 09:26:45 +0000 Subject: Merged revisions 76684 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76684 | tarek.ziade | 2009-12-06 10:22:40 +0100 (Sun, 06 Dec 2009) | 1 line Fixed #1923: make sure we don't strip meaningful whitespace in PKG-INFO Description field ........ --- tests/test_dist.py | 16 ++++++++++++++++ util.py | 1 - 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index bf59c418..af401863 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -9,6 +9,7 @@ import StringIO import sys import unittest import warnings +import textwrap from test.test_support import TESTFN @@ -283,6 +284,21 @@ class MetadataTestCase(unittest.TestCase): os.environ[key] = value os.remove(user_filename) + def test_long_description(self): + long_desc = textwrap.dedent("""\ + example:: + We start here + and continue here + and end here.""") + attrs = {"name": "package", + "version": "1.0", + "long_description": long_desc} + + dist = distutils.dist.Distribution(attrs) + meta = self.format_metadata(dist) + meta = meta.replace('\n' + 8 * ' ', '\n') + self.assertTrue(long_desc in meta) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) diff --git a/util.py b/util.py index ee5829b0..7bc52f19 100644 --- a/util.py +++ b/util.py @@ -559,6 +559,5 @@ def rfc822_escape (header): RFC-822 header, by ensuring there are 8 spaces space after each newline. """ lines = string.split(header, '\n') - lines = map(string.strip, lines) header = string.join(lines, '\n' + 8*' ') return header -- cgit v1.2.1 From 927dabee9d54b17bad408b8188ed533ffa38c694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 6 Dec 2009 09:28:17 +0000 Subject: Merged revisions 76684 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76684 | tarek.ziade | 2009-12-06 10:22:40 +0100 (Sun, 06 Dec 2009) | 1 line Fixed #1923: make sure we don't strip meaningful whitespace in PKG-INFO Description field ........ --- tests/test_dist.py | 16 ++++++++++++++++ util.py | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index b1b184ea..0e7d532d 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -5,6 +5,7 @@ import io import sys import unittest import warnings +import textwrap from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command @@ -353,6 +354,21 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, if line.strip() != ''] self.assertTrue(len(output) > 0) + def test_long_description(self): + long_desc = textwrap.dedent("""\ + example:: + We start here + and continue here + and end here.""") + attrs = {"name": "package", + "version": "1.0", + "long_description": long_desc} + + dist = distutils.dist.Distribution(attrs) + meta = self.format_metadata(dist) + meta = meta.replace('\n' + 8 * ' ', '\n') + self.assertTrue(long_desc in meta) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) diff --git a/util.py b/util.py index a50621e3..cca7b49f 100644 --- a/util.py +++ b/util.py @@ -557,8 +557,8 @@ def rfc822_escape(header): """Return a version of the string escaped for inclusion in an RFC-822 header, by ensuring there are 8 spaces space after each newline. """ - lines = [x.strip() for x in header.split('\n')] - sep = '\n' + 8*' ' + lines = header.split('\n') + sep = '\n' + 8 * ' ' return sep.join(lines) _RE_VERSION = re.compile(b'(\d+\.\d+(\.\d+)*)') -- cgit v1.2.1 From b42ca30f8ba473876220a2b63848c63bf22cbfd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 6 Dec 2009 09:30:47 +0000 Subject: Merged revisions 76686 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r76686 | tarek.ziade | 2009-12-06 10:28:17 +0100 (Sun, 06 Dec 2009) | 9 lines Merged revisions 76684 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76684 | tarek.ziade | 2009-12-06 10:22:40 +0100 (Sun, 06 Dec 2009) | 1 line Fixed #1923: make sure we don't strip meaningful whitespace in PKG-INFO Description field ........ ................ --- tests/test_dist.py | 16 ++++++++++++++++ util.py | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index 5f96b97f..3b7637f3 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -5,6 +5,7 @@ import io import sys import unittest import warnings +import textwrap from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command @@ -301,6 +302,21 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, if line.strip() != ''] self.assertTrue(len(output) > 0) + def test_long_description(self): + long_desc = textwrap.dedent("""\ + example:: + We start here + and continue here + and end here.""") + attrs = {"name": "package", + "version": "1.0", + "long_description": long_desc} + + dist = Distribution(attrs) + meta = self.format_metadata(dist) + meta = meta.replace('\n' + 8 * ' ', '\n') + self.assertTrue(long_desc in meta) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) diff --git a/util.py b/util.py index 4bc4c984..50ad8fef 100644 --- a/util.py +++ b/util.py @@ -557,8 +557,8 @@ def rfc822_escape (header): """Return a version of the string escaped for inclusion in an RFC-822 header, by ensuring there are 8 spaces space after each newline. """ - lines = [x.strip() for x in header.split('\n')] - sep = '\n' + 8*' ' + lines = header.split('\n') + sep = '\n' + 8 * ' ' return sep.join(lines) # 2to3 support -- cgit v1.2.1 From 3eb783e76d5bcac42d64a560aefceff662b5f8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 8 Dec 2009 08:56:49 +0000 Subject: Issue #7457: added a read_pkg_file method to distutils.dist.DistributionMetadata so we can read back PKG-INFO files --- dist.py | 91 ++++++++++++++++++++++++++++++++++++++++++++---------- tests/test_dist.py | 31 +++++++++++++++++-- 2 files changed, 103 insertions(+), 19 deletions(-) diff --git a/dist.py b/dist.py index 8dc64d43..a1fc7b36 100644 --- a/dist.py +++ b/dist.py @@ -7,6 +7,7 @@ being built/installed/distributed. __revision__ = "$Id$" import sys, os, re +import rfc822 try: import warnings @@ -1006,6 +1007,20 @@ Common commands: (see '--help-commands' for more) # to self.metadata.get_XXX. The actual code is in the # DistributionMetadata class, below. +class _MetadataMessage(rfc822.Message): + + def read_field(self, name): + value = self[name] + if value == 'UNKNOWN': + return None + return value + + def getheaders(self, name, default): + values = rfc822.Message.getheaders(self, name) + if values == []: + return None + return values + class DistributionMetadata: """Dummy class to hold the distribution meta-data: name, version, author, and so forth. @@ -1021,25 +1036,67 @@ class DistributionMetadata: "provides", "requires", "obsoletes", ) - def __init__ (self): - self.name = None - self.version = None - self.author = None - self.author_email = None + def __init__(self, path=None): + if path is not None: + self.read_pkg_file(open(path)) + else: + self.name = None + self.version = None + self.author = None + self.author_email = None + self.maintainer = None + self.maintainer_email = None + self.url = None + self.license = None + self.description = None + self.long_description = None + self.keywords = None + self.platforms = None + self.classifiers = None + self.download_url = None + # PEP 314 + self.provides = None + self.requires = None + self.obsoletes = None + + def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = _MetadataMessage(file) + metadata_version = msg['metadata-version'] + self.name = msg.read_field('name') + self.version = msg.read_field('version') + self.description = msg.read_field('summary') + # we are filling author only. + self.author = msg.read_field('author') self.maintainer = None + self.author_email = msg.read_field('author-email') self.maintainer_email = None - self.url = None - self.license = None - self.description = None - self.long_description = None - self.keywords = None - self.platforms = None - self.classifiers = None - self.download_url = None - # PEP 314 - self.provides = None - self.requires = None - self.obsoletes = None + self.url = msg.read_field('home-page') + self.license = msg.read_field('license') + + if 'download-url' in msg: + self.download_url = msg.read_field('download-url') + else: + self.download_url = None + + self.long_description = msg.read_field('description') + self.description = msg.read_field('summary') + + if 'keywords' in msg: + self.keywords = msg.read_field('keywords').split(',') + + self.platforms = msg.getheaders('platform', None) + self.classifiers = msg.getheaders('classifier', None) + + # PEP 314 - these fields only exist in 1.1 + if metadata_version == '1.1': + self.requires = msg.getheaders('requires', None) + self.provides = msg.getheaders('provides', None) + self.obsoletes = msg.getheaders('obsoletes', None) + else: + self.requires = None + self.provides = None + self.obsoletes = None def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. diff --git a/tests/test_dist.py b/tests/test_dist.py index 8b141ca6..1eddf6d2 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,10 +8,9 @@ import unittest import warnings import textwrap -from distutils.dist import Distribution, fix_help_options +from distutils.dist import Distribution, fix_help_options, DistributionMetadata from distutils.cmd import Command import distutils.dist - from test.test_support import TESTFN, captured_stdout from distutils.tests import support @@ -239,6 +238,7 @@ class DistributionTestCase(support.TempdirManager, # make sure --no-user-cfg disables the user cfg file self.assertEquals(len(all_files)-1, len(files)) + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): @@ -397,6 +397,33 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, meta = meta.replace('\n' + 8 * ' ', '\n') self.assertTrue(long_desc in meta) + def test_read_metadata(self): + attrs = {"name": "package", + "version": "1.0", + "long_description": "desc", + "description": "xxx", + "download_url": "http://example.com", + "keywords": ['one', 'two'], + "requires": ['foo']} + + dist = Distribution(attrs) + metadata = dist.metadata + + # write it then reloads it + PKG_INFO = StringIO.StringIO() + metadata.write_pkg_file(PKG_INFO) + PKG_INFO.seek(0) + metadata.read_pkg_file(PKG_INFO) + + self.assertEquals(metadata.name, "package") + self.assertEquals(metadata.version, "1.0") + self.assertEquals(metadata.description, "xxx") + self.assertEquals(metadata.download_url, 'http://example.com') + self.assertEquals(metadata.keywords, ['one', 'two']) + self.assertEquals(metadata.platforms, ['UNKNOWN']) + self.assertEquals(metadata.obsoletes, None) + self.assertEquals(metadata.requires, ['foo']) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) -- cgit v1.2.1 From b1a6b3c4c91509df552929a26e5bc1cc31ce6b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 8 Dec 2009 09:39:51 +0000 Subject: removed the usage of rfc822 in favor of email.message.Message --- dist.py | 63 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/dist.py b/dist.py index a1fc7b36..ee2cec90 100644 --- a/dist.py +++ b/dist.py @@ -7,7 +7,7 @@ being built/installed/distributed. __revision__ = "$Id$" import sys, os, re -import rfc822 +from email import message_from_file try: import warnings @@ -1007,20 +1007,6 @@ Common commands: (see '--help-commands' for more) # to self.metadata.get_XXX. The actual code is in the # DistributionMetadata class, below. -class _MetadataMessage(rfc822.Message): - - def read_field(self, name): - value = self[name] - if value == 'UNKNOWN': - return None - return value - - def getheaders(self, name, default): - values = rfc822.Message.getheaders(self, name) - if values == []: - return None - return values - class DistributionMetadata: """Dummy class to hold the distribution meta-data: name, version, author, and so forth. @@ -1061,38 +1047,51 @@ class DistributionMetadata: def read_pkg_file(self, file): """Reads the metadata values from a file object.""" - msg = _MetadataMessage(file) + msg = message_from_file(file) + + def _read_field(name): + value = msg[name] + if value == 'UNKNOWN': + return None + return value + + def _read_list(name): + values = msg.get_all(name, None) + if values == []: + return None + return values + metadata_version = msg['metadata-version'] - self.name = msg.read_field('name') - self.version = msg.read_field('version') - self.description = msg.read_field('summary') + self.name = _read_field('name') + self.version = _read_field('version') + self.description = _read_field('summary') # we are filling author only. - self.author = msg.read_field('author') + self.author = _read_field('author') self.maintainer = None - self.author_email = msg.read_field('author-email') + self.author_email = _read_field('author-email') self.maintainer_email = None - self.url = msg.read_field('home-page') - self.license = msg.read_field('license') + self.url = _read_field('home-page') + self.license = _read_field('license') if 'download-url' in msg: - self.download_url = msg.read_field('download-url') + self.download_url = _read_field('download-url') else: self.download_url = None - self.long_description = msg.read_field('description') - self.description = msg.read_field('summary') + self.long_description = _read_field('description') + self.description = _read_field('summary') if 'keywords' in msg: - self.keywords = msg.read_field('keywords').split(',') + self.keywords = _read_field('keywords').split(',') - self.platforms = msg.getheaders('platform', None) - self.classifiers = msg.getheaders('classifier', None) + self.platforms = _read_list('platform') + self.classifiers = _read_list('classifier') # PEP 314 - these fields only exist in 1.1 if metadata_version == '1.1': - self.requires = msg.getheaders('requires', None) - self.provides = msg.getheaders('provides', None) - self.obsoletes = msg.getheaders('obsoletes', None) + self.requires = _read_list('requires') + self.provides = _read_list('provides') + self.obsoletes = _read_list('obsoletes') else: self.requires = None self.provides = None -- cgit v1.2.1 From 6b16b3a1e6c1519906b509b8f906e1605557b5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 8 Dec 2009 09:45:25 +0000 Subject: Merged revisions 76702,76704 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76702 | tarek.ziade | 2009-12-08 09:56:49 +0100 (Tue, 08 Dec 2009) | 1 line Issue #7457: added a read_pkg_file method to distutils.dist.DistributionMetadata so we can read back PKG-INFO files ........ r76704 | tarek.ziade | 2009-12-08 10:39:51 +0100 (Tue, 08 Dec 2009) | 1 line removed the usage of rfc822 in favor of email.message.Message ........ --- dist.py | 90 +++++++++++++++++++++++++++++++++++++++++++----------- tests/test_dist.py | 29 +++++++++++++++++- 2 files changed, 101 insertions(+), 18 deletions(-) diff --git a/dist.py b/dist.py index 90af6e2d..353525e1 100644 --- a/dist.py +++ b/dist.py @@ -7,6 +7,7 @@ being built/installed/distributed. __revision__ = "$Id$" import sys, os, re +from email import message_from_file try: import warnings @@ -1014,25 +1015,80 @@ class DistributionMetadata: "provides", "requires", "obsoletes", ) - def __init__ (self): - self.name = None - self.version = None - self.author = None - self.author_email = None + def __init__(self, path=None): + if path is not None: + self.read_pkg_file(open(path)) + else: + self.name = None + self.version = None + self.author = None + self.author_email = None + self.maintainer = None + self.maintainer_email = None + self.url = None + self.license = None + self.description = None + self.long_description = None + self.keywords = None + self.platforms = None + self.classifiers = None + self.download_url = None + # PEP 314 + self.provides = None + self.requires = None + self.obsoletes = None + + def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = message_from_file(file) + + def _read_field(name): + value = msg[name] + if value == 'UNKNOWN': + return None + return value + + def _read_list(name): + values = msg.get_all(name, None) + if values == []: + return None + return values + + metadata_version = msg['metadata-version'] + self.name = _read_field('name') + self.version = _read_field('version') + self.description = _read_field('summary') + # we are filling author only. + self.author = _read_field('author') self.maintainer = None + self.author_email = _read_field('author-email') self.maintainer_email = None - self.url = None - self.license = None - self.description = None - self.long_description = None - self.keywords = None - self.platforms = None - self.classifiers = None - self.download_url = None - # PEP 314 - self.provides = None - self.requires = None - self.obsoletes = None + self.url = _read_field('home-page') + self.license = _read_field('license') + + if 'download-url' in msg: + self.download_url = _read_field('download-url') + else: + self.download_url = None + + self.long_description = _read_field('description') + self.description = _read_field('summary') + + if 'keywords' in msg: + self.keywords = _read_field('keywords').split(',') + + self.platforms = _read_list('platform') + self.classifiers = _read_list('classifier') + + # PEP 314 - these fields only exist in 1.1 + if metadata_version == '1.1': + self.requires = _read_list('requires') + self.provides = _read_list('provides') + self.obsoletes = _read_list('obsoletes') + else: + self.requires = None + self.provides = None + self.obsoletes = None def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. diff --git a/tests/test_dist.py b/tests/test_dist.py index 0e7d532d..4f51c16e 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -7,7 +7,7 @@ import unittest import warnings import textwrap -from distutils.dist import Distribution, fix_help_options +from distutils.dist import Distribution, fix_help_options, DistributionMetadata from distutils.cmd import Command import distutils.dist @@ -369,6 +369,33 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, meta = meta.replace('\n' + 8 * ' ', '\n') self.assertTrue(long_desc in meta) + def test_read_metadata(self): + attrs = {"name": "package", + "version": "1.0", + "long_description": "desc", + "description": "xxx", + "download_url": "http://example.com", + "keywords": ['one', 'two'], + "requires": ['foo']} + + dist = Distribution(attrs) + metadata = dist.metadata + + # write it then reloads it + PKG_INFO = io.StringIO() + metadata.write_pkg_file(PKG_INFO) + PKG_INFO.seek(0) + metadata.read_pkg_file(PKG_INFO) + + self.assertEquals(metadata.name, "package") + self.assertEquals(metadata.version, "1.0") + self.assertEquals(metadata.description, "xxx") + self.assertEquals(metadata.download_url, 'http://example.com') + self.assertEquals(metadata.keywords, ['one', 'two']) + self.assertEquals(metadata.platforms, ['UNKNOWN']) + self.assertEquals(metadata.obsoletes, None) + self.assertEquals(metadata.requires, ['foo']) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) -- cgit v1.2.1 From 769868e0309a3e8b454d1638cd50d5883ef5b2f2 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 10 Dec 2009 10:27:09 +0000 Subject: Fix an issue with the detection of a non-existing SDK on OSX. Without this patch it wasn't possible after all to compile extensions on OSX 10.6 with the binary installer unless the user had installed the (non-default) 10.4u SDK. --- sysconfig.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 6ca6720a..f3b2aca6 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -563,7 +563,7 @@ def get_config_vars(*args): # are in CFLAGS or LDFLAGS and remove them if they are. # This is needed when building extensions on a 10.3 system # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', # a number of derived variables. These need to be # patched up as well. 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): @@ -582,7 +582,7 @@ def get_config_vars(*args): if 'ARCHFLAGS' in os.environ: arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', # a number of derived variables. These need to be # patched up as well. 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): @@ -606,7 +606,7 @@ def get_config_vars(*args): if m is not None: sdk = m.group(1) if not os.path.exists(sdk): - for key in ('LDFLAGS', 'BASECFLAGS', + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', # a number of derived variables. These need to be # patched up as well. 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): -- cgit v1.2.1 From dde1c67ea48a115d96ccddc0f0e4ff44dfd0d286 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 10 Dec 2009 10:29:05 +0000 Subject: Merged revisions 76738 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76738 | ronald.oussoren | 2009-12-10 11:27:09 +0100 (Thu, 10 Dec 2009) | 6 lines Fix an issue with the detection of a non-existing SDK on OSX. Without this patch it wasn't possible after all to compile extensions on OSX 10.6 with the binary installer unless the user had installed the (non-default) 10.4u SDK. ........ --- sysconfig.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 8306202f..54ccec49 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -537,7 +537,7 @@ def get_config_vars(*args): # are in CFLAGS or LDFLAGS and remove them if they are. # This is needed when building extensions on a 10.3 system # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', # a number of derived variables. These need to be # patched up as well. 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): @@ -556,7 +556,7 @@ def get_config_vars(*args): if 'ARCHFLAGS' in os.environ: arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', # a number of derived variables. These need to be # patched up as well. 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): @@ -580,7 +580,7 @@ def get_config_vars(*args): if m is not None: sdk = m.group(1) if not os.path.exists(sdk): - for key in ('LDFLAGS', 'BASECFLAGS', + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', # a number of derived variables. These need to be # patched up as well. 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): -- cgit v1.2.1 From c96d565a9308ae6781dacdcc8d016445bbca75a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 10 Dec 2009 15:29:03 +0000 Subject: added test coverage for distutils.dep_util, and cleaned up the module --- dep_util.py | 68 +++++++++++++++------------------ tests/test_dep_util.py | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 37 deletions(-) create mode 100644 tests/test_dep_util.py diff --git a/dep_util.py b/dep_util.py index 39eecfb2..4e40df68 100644 --- a/dep_util.py +++ b/dep_util.py @@ -9,29 +9,27 @@ __revision__ = "$Id$" import os from distutils.errors import DistutilsFileError +def newer(source, target): + """Tells if the target is newer than the source. -def newer (source, target): - """Return true if 'source' exists and is more recently modified than - 'target', or if 'source' exists and 'target' doesn't. Return false if - both exist and 'target' is the same age or younger than 'source'. - Raise DistutilsFileError if 'source' does not exist. + Return true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. + + Return false if both exist and 'target' is the same age or younger + than 'source'. Raise DistutilsFileError if 'source' does not exist. + + Note that this test is not very accurate: files created in the same second + will have the same "age". """ if not os.path.exists(source): - raise DistutilsFileError, ("file '%s' does not exist" % - os.path.abspath(source)) + raise DistutilsFileError("file '%s' does not exist" % + os.path.abspath(source)) if not os.path.exists(target): - return 1 - - from stat import ST_MTIME - mtime1 = os.stat(source)[ST_MTIME] - mtime2 = os.stat(target)[ST_MTIME] - - return mtime1 > mtime2 - -# newer () + return True + return os.stat(source).st_mtime > os.stat(target).st_mtime -def newer_pairwise (sources, targets): +def newer_pairwise(sources, targets): """Walk two filename lists in parallel, testing if each source is newer than its corresponding target. Return a pair of lists (sources, targets) where source is newer than target, according to the semantics @@ -43,19 +41,18 @@ def newer_pairwise (sources, targets): # build a pair of lists (sources, targets) where source is newer n_sources = [] n_targets = [] - for i in range(len(sources)): - if newer(sources[i], targets[i]): - n_sources.append(sources[i]) - n_targets.append(targets[i]) + for source, target in zip(sources, targets): + if newer(source, target): + n_sources.append(source) + n_targets.append(target) - return (n_sources, n_targets) + return n_sources, n_targets -# newer_pairwise () - - -def newer_group (sources, target, missing='error'): +def newer_group(sources, target, missing='error'): """Return true if 'target' is out-of-date with respect to any file - listed in 'sources'. In other words, if 'target' exists and is newer + listed in 'sources'. + + In other words, if 'target' exists and is newer than every file in 'sources', return false; otherwise return true. 'missing' controls what we do when a source file is missing; the default ("error") is to blow up with an OSError from inside 'stat()'; @@ -68,14 +65,14 @@ def newer_group (sources, target, missing='error'): """ # If the target doesn't even exist, then it's definitely out-of-date. if not os.path.exists(target): - return 1 + return True # Otherwise we have to find out the hard way: if *any* source file # is more recent than 'target', then 'target' is out-of-date and # we can immediately return true. If we fall through to the end # of the loop, then 'target' is up-to-date and we return false. - from stat import ST_MTIME - target_mtime = os.stat(target)[ST_MTIME] + target_mtime = os.stat(target).st_mtime + for source in sources: if not os.path.exists(source): if missing == 'error': # blow up when we stat() the file @@ -83,12 +80,9 @@ def newer_group (sources, target, missing='error'): elif missing == 'ignore': # missing source dropped from continue # target's dependency list elif missing == 'newer': # missing source means target is - return 1 # out-of-date + return True # out-of-date - source_mtime = os.stat(source)[ST_MTIME] - if source_mtime > target_mtime: - return 1 - else: - return 0 + if os.stat(source).st_mtime > target_mtime: + return True -# newer_group () + return False diff --git a/tests/test_dep_util.py b/tests/test_dep_util.py new file mode 100644 index 00000000..21fc7bc0 --- /dev/null +++ b/tests/test_dep_util.py @@ -0,0 +1,101 @@ +"""Tests for distutils.dep_util.""" +import unittest +import os +import time + +from distutils.dep_util import newer, newer_pairwise, newer_group +from distutils.errors import DistutilsFileError +from distutils.tests import support + +# XXX needs to be tuned for the various platforms +_ST_MIME_TIMER = 1 + +class DepUtilTestCase(support.TempdirManager, unittest.TestCase): + + def test_newer(self): + tmpdir = self.mkdtemp() + target = os.path.join(tmpdir, 'target') + source = os.path.join(tmpdir, 'source') + + # Raise DistutilsFileError if 'source' does not exist. + self.assertRaises(DistutilsFileError, newer, target, source) + + # Return true if 'source' exists and is more recently modified than + # 'target', or if 'source' exists and 'target' doesn't. + self.write_file(target) + self.assertTrue(newer(target, source)) + self.write_file(source, 'xox') + time.sleep(_ST_MIME_TIMER) # ensures ST_MTIME differs + self.write_file(target, 'xhx') + self.assertTrue(newer(target, source)) + + # Return false if both exist and 'target' is the same age or younger + # than 'source'. + self.write_file(source, 'xox'); self.write_file(target, 'xhx') + self.assertFalse(newer(target, source)) + self.write_file(target, 'xox') + time.sleep(_ST_MIME_TIMER) + self.write_file(source, 'xhx') + self.assertFalse(newer(target, source)) + + def test_newer_pairwise(self): + tmpdir = self.mkdtemp() + sources = os.path.join(tmpdir, 'sources') + targets = os.path.join(tmpdir, 'targets') + os.mkdir(sources) + os.mkdir(targets) + one = os.path.join(sources, 'one') + two = os.path.join(sources, 'two') + three = os.path.join(targets, 'three') + four = os.path.join(targets, 'four') + + self.write_file(one) + self.write_file(three) + self.write_file(four) + time.sleep(_ST_MIME_TIMER) + self.write_file(two) + + self.assertEquals(newer_pairwise([one, two], [three, four]), + ([two],[four])) + + def test_newer_group(self): + tmpdir = self.mkdtemp() + sources = os.path.join(tmpdir, 'sources') + os.mkdir(sources) + one = os.path.join(sources, 'one') + two = os.path.join(sources, 'two') + three = os.path.join(sources, 'three') + target = os.path.join(tmpdir, 'target') + + # return true if 'target' is out-of-date with respect to any file + # listed in 'sources'. + self.write_file(target) + time.sleep(_ST_MIME_TIMER) + self.write_file(one) + self.write_file(two) + self.write_file(three) + self.assertTrue(newer_group([one, two, three], target)) + + self.write_file(one) + self.write_file(three) + self.write_file(two) + time.sleep(0.1) + self.write_file(target) + self.assertFalse(newer_group([one, two, three], target)) + + # missing handling + os.remove(one) + self.assertRaises(OSError, newer_group, [one, two, three], target) + + self.assertFalse(newer_group([one, two, three], target, + missing='ignore')) + + self.assertTrue(newer_group([one, two, three], target, + missing='newer')) + + +def test_suite(): + return unittest.makeSuite(DepUtilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From e7ff58b757d214c4565d89b58b3efd77620d7a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 10 Dec 2009 15:35:35 +0000 Subject: Merged revisions 76746 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76746 | tarek.ziade | 2009-12-10 16:29:03 +0100 (Thu, 10 Dec 2009) | 1 line added test coverage for distutils.dep_util, and cleaned up the module ........ --- dep_util.py | 64 ++++++++++++++----------------- tests/test_dep_util.py | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 35 deletions(-) create mode 100644 tests/test_dep_util.py diff --git a/dep_util.py b/dep_util.py index 07b3549c..b91a62eb 100644 --- a/dep_util.py +++ b/dep_util.py @@ -9,29 +9,27 @@ __revision__ = "$Id$" import os from distutils.errors import DistutilsFileError +def newer(source, target): + """Tells if the target is newer than the source. -def newer (source, target): - """Return true if 'source' exists and is more recently modified than - 'target', or if 'source' exists and 'target' doesn't. Return false if - both exist and 'target' is the same age or younger than 'source'. - Raise DistutilsFileError if 'source' does not exist. + Return true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. + + Return false if both exist and 'target' is the same age or younger + than 'source'. Raise DistutilsFileError if 'source' does not exist. + + Note that this test is not very accurate: files created in the same second + will have the same "age". """ if not os.path.exists(source): raise DistutilsFileError("file '%s' does not exist" % os.path.abspath(source)) if not os.path.exists(target): - return 1 - - from stat import ST_MTIME - mtime1 = os.stat(source)[ST_MTIME] - mtime2 = os.stat(target)[ST_MTIME] - - return mtime1 > mtime2 - -# newer () + return True + return os.stat(source).st_mtime > os.stat(target).st_mtime -def newer_pairwise (sources, targets): +def newer_pairwise(sources, targets): """Walk two filename lists in parallel, testing if each source is newer than its corresponding target. Return a pair of lists (sources, targets) where source is newer than target, according to the semantics @@ -43,19 +41,18 @@ def newer_pairwise (sources, targets): # build a pair of lists (sources, targets) where source is newer n_sources = [] n_targets = [] - for i in range(len(sources)): - if newer(sources[i], targets[i]): - n_sources.append(sources[i]) - n_targets.append(targets[i]) + for source, target in zip(sources, targets): + if newer(source, target): + n_sources.append(source) + n_targets.append(target) - return (n_sources, n_targets) + return n_sources, n_targets -# newer_pairwise () - - -def newer_group (sources, target, missing='error'): +def newer_group(sources, target, missing='error'): """Return true if 'target' is out-of-date with respect to any file - listed in 'sources'. In other words, if 'target' exists and is newer + listed in 'sources'. + + In other words, if 'target' exists and is newer than every file in 'sources', return false; otherwise return true. 'missing' controls what we do when a source file is missing; the default ("error") is to blow up with an OSError from inside 'stat()'; @@ -68,14 +65,14 @@ def newer_group (sources, target, missing='error'): """ # If the target doesn't even exist, then it's definitely out-of-date. if not os.path.exists(target): - return 1 + return True # Otherwise we have to find out the hard way: if *any* source file # is more recent than 'target', then 'target' is out-of-date and # we can immediately return true. If we fall through to the end # of the loop, then 'target' is up-to-date and we return false. - from stat import ST_MTIME - target_mtime = os.stat(target)[ST_MTIME] + target_mtime = os.stat(target).st_mtime + for source in sources: if not os.path.exists(source): if missing == 'error': # blow up when we stat() the file @@ -83,12 +80,9 @@ def newer_group (sources, target, missing='error'): elif missing == 'ignore': # missing source dropped from continue # target's dependency list elif missing == 'newer': # missing source means target is - return 1 # out-of-date + return True # out-of-date - source_mtime = os.stat(source)[ST_MTIME] - if source_mtime > target_mtime: - return 1 - else: - return 0 + if os.stat(source).st_mtime > target_mtime: + return True -# newer_group () + return False diff --git a/tests/test_dep_util.py b/tests/test_dep_util.py new file mode 100644 index 00000000..21fc7bc0 --- /dev/null +++ b/tests/test_dep_util.py @@ -0,0 +1,101 @@ +"""Tests for distutils.dep_util.""" +import unittest +import os +import time + +from distutils.dep_util import newer, newer_pairwise, newer_group +from distutils.errors import DistutilsFileError +from distutils.tests import support + +# XXX needs to be tuned for the various platforms +_ST_MIME_TIMER = 1 + +class DepUtilTestCase(support.TempdirManager, unittest.TestCase): + + def test_newer(self): + tmpdir = self.mkdtemp() + target = os.path.join(tmpdir, 'target') + source = os.path.join(tmpdir, 'source') + + # Raise DistutilsFileError if 'source' does not exist. + self.assertRaises(DistutilsFileError, newer, target, source) + + # Return true if 'source' exists and is more recently modified than + # 'target', or if 'source' exists and 'target' doesn't. + self.write_file(target) + self.assertTrue(newer(target, source)) + self.write_file(source, 'xox') + time.sleep(_ST_MIME_TIMER) # ensures ST_MTIME differs + self.write_file(target, 'xhx') + self.assertTrue(newer(target, source)) + + # Return false if both exist and 'target' is the same age or younger + # than 'source'. + self.write_file(source, 'xox'); self.write_file(target, 'xhx') + self.assertFalse(newer(target, source)) + self.write_file(target, 'xox') + time.sleep(_ST_MIME_TIMER) + self.write_file(source, 'xhx') + self.assertFalse(newer(target, source)) + + def test_newer_pairwise(self): + tmpdir = self.mkdtemp() + sources = os.path.join(tmpdir, 'sources') + targets = os.path.join(tmpdir, 'targets') + os.mkdir(sources) + os.mkdir(targets) + one = os.path.join(sources, 'one') + two = os.path.join(sources, 'two') + three = os.path.join(targets, 'three') + four = os.path.join(targets, 'four') + + self.write_file(one) + self.write_file(three) + self.write_file(four) + time.sleep(_ST_MIME_TIMER) + self.write_file(two) + + self.assertEquals(newer_pairwise([one, two], [three, four]), + ([two],[four])) + + def test_newer_group(self): + tmpdir = self.mkdtemp() + sources = os.path.join(tmpdir, 'sources') + os.mkdir(sources) + one = os.path.join(sources, 'one') + two = os.path.join(sources, 'two') + three = os.path.join(sources, 'three') + target = os.path.join(tmpdir, 'target') + + # return true if 'target' is out-of-date with respect to any file + # listed in 'sources'. + self.write_file(target) + time.sleep(_ST_MIME_TIMER) + self.write_file(one) + self.write_file(two) + self.write_file(three) + self.assertTrue(newer_group([one, two, three], target)) + + self.write_file(one) + self.write_file(three) + self.write_file(two) + time.sleep(0.1) + self.write_file(target) + self.assertFalse(newer_group([one, two, three], target)) + + # missing handling + os.remove(one) + self.assertRaises(OSError, newer_group, [one, two, three], target) + + self.assertFalse(newer_group([one, two, three], target, + missing='ignore')) + + self.assertTrue(newer_group([one, two, three], target, + missing='newer')) + + +def test_suite(): + return unittest.makeSuite(DepUtilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From fa1b455a97e292e8a338fb07b72c8db7ca52658a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 10 Dec 2009 19:29:53 +0000 Subject: using an existing file to avoid dealing with a sleep to test file ages --- tests/test_dep_util.py | 73 ++++++++++++++++++-------------------------------- 1 file changed, 26 insertions(+), 47 deletions(-) diff --git a/tests/test_dep_util.py b/tests/test_dep_util.py index 21fc7bc0..d81d9143 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -7,36 +7,26 @@ from distutils.dep_util import newer, newer_pairwise, newer_group from distutils.errors import DistutilsFileError from distutils.tests import support -# XXX needs to be tuned for the various platforms -_ST_MIME_TIMER = 1 - class DepUtilTestCase(support.TempdirManager, unittest.TestCase): def test_newer(self): + tmpdir = self.mkdtemp() - target = os.path.join(tmpdir, 'target') - source = os.path.join(tmpdir, 'source') - - # Raise DistutilsFileError if 'source' does not exist. - self.assertRaises(DistutilsFileError, newer, target, source) - - # Return true if 'source' exists and is more recently modified than - # 'target', or if 'source' exists and 'target' doesn't. - self.write_file(target) - self.assertTrue(newer(target, source)) - self.write_file(source, 'xox') - time.sleep(_ST_MIME_TIMER) # ensures ST_MTIME differs - self.write_file(target, 'xhx') - self.assertTrue(newer(target, source)) - - # Return false if both exist and 'target' is the same age or younger - # than 'source'. - self.write_file(source, 'xox'); self.write_file(target, 'xhx') - self.assertFalse(newer(target, source)) - self.write_file(target, 'xox') - time.sleep(_ST_MIME_TIMER) - self.write_file(source, 'xhx') - self.assertFalse(newer(target, source)) + new_file = os.path.join(tmpdir, 'new') + old_file = os.path.abspath(__file__) + + # Raise DistutilsFileError if 'new_file' does not exist. + self.assertRaises(DistutilsFileError, newer, new_file, old_file) + + # Return true if 'new_file' exists and is more recently modified than + # 'old_file', or if 'new_file' exists and 'old_file' doesn't. + self.write_file(new_file) + self.assertTrue(newer(new_file, 'I_dont_exist')) + self.assertTrue(newer(new_file, old_file)) + + # Return false if both exist and 'old_file' is the same age or younger + # than 'new_file'. + self.assertFalse(newer(old_file, new_file)) def test_newer_pairwise(self): tmpdir = self.mkdtemp() @@ -46,17 +36,14 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase): os.mkdir(targets) one = os.path.join(sources, 'one') two = os.path.join(sources, 'two') - three = os.path.join(targets, 'three') + three = os.path.abspath(__file__) # I am the old file four = os.path.join(targets, 'four') - self.write_file(one) - self.write_file(three) - self.write_file(four) - time.sleep(_ST_MIME_TIMER) self.write_file(two) + self.write_file(four) self.assertEquals(newer_pairwise([one, two], [three, four]), - ([two],[four])) + ([one],[three])) def test_newer_group(self): tmpdir = self.mkdtemp() @@ -65,32 +52,24 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase): one = os.path.join(sources, 'one') two = os.path.join(sources, 'two') three = os.path.join(sources, 'three') - target = os.path.join(tmpdir, 'target') + old_file = os.path.abspath(__file__) - # return true if 'target' is out-of-date with respect to any file + # return true if 'old_file' is out-of-date with respect to any file # listed in 'sources'. - self.write_file(target) - time.sleep(_ST_MIME_TIMER) self.write_file(one) self.write_file(two) self.write_file(three) - self.assertTrue(newer_group([one, two, three], target)) - - self.write_file(one) - self.write_file(three) - self.write_file(two) - time.sleep(0.1) - self.write_file(target) - self.assertFalse(newer_group([one, two, three], target)) + self.assertTrue(newer_group([one, two, three], old_file)) + self.assertFalse(newer_group([one, two, old_file], three)) # missing handling os.remove(one) - self.assertRaises(OSError, newer_group, [one, two, three], target) + self.assertRaises(OSError, newer_group, [one, two, old_file], three) - self.assertFalse(newer_group([one, two, three], target, + self.assertFalse(newer_group([one, two, old_file], three, missing='ignore')) - self.assertTrue(newer_group([one, two, three], target, + self.assertTrue(newer_group([one, two, old_file], three, missing='newer')) -- cgit v1.2.1 From 53e5b86d24ea8f12522a35ff9bf5767724d16938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 10 Dec 2009 19:37:05 +0000 Subject: Merged revisions 76750 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76750 | tarek.ziade | 2009-12-10 20:29:53 +0100 (Thu, 10 Dec 2009) | 1 line using an existing file to avoid dealing with a sleep to test file ages ........ --- tests/test_dep_util.py | 73 ++++++++++++++++++-------------------------------- 1 file changed, 26 insertions(+), 47 deletions(-) diff --git a/tests/test_dep_util.py b/tests/test_dep_util.py index 21fc7bc0..d81d9143 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -7,36 +7,26 @@ from distutils.dep_util import newer, newer_pairwise, newer_group from distutils.errors import DistutilsFileError from distutils.tests import support -# XXX needs to be tuned for the various platforms -_ST_MIME_TIMER = 1 - class DepUtilTestCase(support.TempdirManager, unittest.TestCase): def test_newer(self): + tmpdir = self.mkdtemp() - target = os.path.join(tmpdir, 'target') - source = os.path.join(tmpdir, 'source') - - # Raise DistutilsFileError if 'source' does not exist. - self.assertRaises(DistutilsFileError, newer, target, source) - - # Return true if 'source' exists and is more recently modified than - # 'target', or if 'source' exists and 'target' doesn't. - self.write_file(target) - self.assertTrue(newer(target, source)) - self.write_file(source, 'xox') - time.sleep(_ST_MIME_TIMER) # ensures ST_MTIME differs - self.write_file(target, 'xhx') - self.assertTrue(newer(target, source)) - - # Return false if both exist and 'target' is the same age or younger - # than 'source'. - self.write_file(source, 'xox'); self.write_file(target, 'xhx') - self.assertFalse(newer(target, source)) - self.write_file(target, 'xox') - time.sleep(_ST_MIME_TIMER) - self.write_file(source, 'xhx') - self.assertFalse(newer(target, source)) + new_file = os.path.join(tmpdir, 'new') + old_file = os.path.abspath(__file__) + + # Raise DistutilsFileError if 'new_file' does not exist. + self.assertRaises(DistutilsFileError, newer, new_file, old_file) + + # Return true if 'new_file' exists and is more recently modified than + # 'old_file', or if 'new_file' exists and 'old_file' doesn't. + self.write_file(new_file) + self.assertTrue(newer(new_file, 'I_dont_exist')) + self.assertTrue(newer(new_file, old_file)) + + # Return false if both exist and 'old_file' is the same age or younger + # than 'new_file'. + self.assertFalse(newer(old_file, new_file)) def test_newer_pairwise(self): tmpdir = self.mkdtemp() @@ -46,17 +36,14 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase): os.mkdir(targets) one = os.path.join(sources, 'one') two = os.path.join(sources, 'two') - three = os.path.join(targets, 'three') + three = os.path.abspath(__file__) # I am the old file four = os.path.join(targets, 'four') - self.write_file(one) - self.write_file(three) - self.write_file(four) - time.sleep(_ST_MIME_TIMER) self.write_file(two) + self.write_file(four) self.assertEquals(newer_pairwise([one, two], [three, four]), - ([two],[four])) + ([one],[three])) def test_newer_group(self): tmpdir = self.mkdtemp() @@ -65,32 +52,24 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase): one = os.path.join(sources, 'one') two = os.path.join(sources, 'two') three = os.path.join(sources, 'three') - target = os.path.join(tmpdir, 'target') + old_file = os.path.abspath(__file__) - # return true if 'target' is out-of-date with respect to any file + # return true if 'old_file' is out-of-date with respect to any file # listed in 'sources'. - self.write_file(target) - time.sleep(_ST_MIME_TIMER) self.write_file(one) self.write_file(two) self.write_file(three) - self.assertTrue(newer_group([one, two, three], target)) - - self.write_file(one) - self.write_file(three) - self.write_file(two) - time.sleep(0.1) - self.write_file(target) - self.assertFalse(newer_group([one, two, three], target)) + self.assertTrue(newer_group([one, two, three], old_file)) + self.assertFalse(newer_group([one, two, old_file], three)) # missing handling os.remove(one) - self.assertRaises(OSError, newer_group, [one, two, three], target) + self.assertRaises(OSError, newer_group, [one, two, old_file], three) - self.assertFalse(newer_group([one, two, three], target, + self.assertFalse(newer_group([one, two, old_file], three, missing='ignore')) - self.assertTrue(newer_group([one, two, three], target, + self.assertTrue(newer_group([one, two, old_file], three, missing='newer')) -- cgit v1.2.1 From 6f9d7dc8cb5b3bd62bfc305d43342b9f8c82b7b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 15 Dec 2009 06:29:19 +0000 Subject: cleaned up the module (PEP 8 + old fashion test removal) --- command/install_data.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/command/install_data.py b/command/install_data.py index ec78ce34..ab40797b 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -8,7 +8,6 @@ platform-independent data files.""" __revision__ = "$Id$" import os -from types import StringType from distutils.core import Command from distutils.util import change_root, convert_path @@ -35,7 +34,7 @@ class install_data(Command): self.data_files = self.distribution.data_files self.warn_dir = 1 - def finalize_options (self): + def finalize_options(self): self.set_undefined_options('install', ('install_data', 'install_dir'), ('root', 'root'), @@ -45,7 +44,7 @@ class install_data(Command): def run(self): self.mkpath(self.install_dir) for f in self.data_files: - if type(f) is StringType: + if isinstance(f, str): # it's a simple file, so copy it f = convert_path(f) if self.warn_dir: -- cgit v1.2.1 From 6d2d1caa21a5224fec7eee1678857c354f8f4e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 20 Dec 2009 23:23:34 +0000 Subject: Fixed #7552: fixed distutils.command.upload failure on very long passwords --- command/upload.py | 6 +++--- tests/test_upload.py | 23 ++++++++++++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/command/upload.py b/command/upload.py index 8114feb8..5d6ebcfa 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ import os import socket import platform from urllib2 import urlopen, Request, HTTPError -import base64 +from base64 import standard_b64encode import urlparse import cStringIO as StringIO from ConfigParser import ConfigParser @@ -129,8 +129,8 @@ class upload(PyPIRCCommand): open(filename+".asc").read()) # set up the authentication - auth = "Basic " + base64.encodestring(self.username + ":" + - self.password).strip() + auth = "Basic " + standard_b64encode(self.username + ":" + + self.password) # Build up the MIME payload for the POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' diff --git a/tests/test_upload.py b/tests/test_upload.py index 0d95a091..8f6701cc 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -10,6 +10,25 @@ from distutils.core import Distribution from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase +PYPIRC_LONG_PASSWORD = """\ +[distutils] + +index-servers = + server1 + server2 + +[server1] +username:me +password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ +""" + + PYPIRC_NOPASSWORD = """\ [distutils] @@ -85,7 +104,7 @@ class uploadTestCase(PyPIRCCommandTestCase): self.write_file(path) command, pyversion, filename = 'xxx', '2.6', path dist_files = [(command, pyversion, filename)] - self.write_file(self.rc, PYPIRC) + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) # lets run it pkg_dir, dist = self.create_dist(dist_files=dist_files) @@ -101,6 +120,8 @@ class uploadTestCase(PyPIRCCommandTestCase): self.assertEquals(self.last_open.req.get_full_url(), 'http://pypi.python.org/pypi') self.assertTrue('xxx' in self.last_open.req.data) + auth = self.last_open.req.headers['Authorization'] + self.assertFalse('\n' in auth) def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From 4de5e96120f3e583fc5d841220e6f9290c820014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 20 Dec 2009 23:54:52 +0000 Subject: Merged revisions 76952 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76952 | tarek.ziade | 2009-12-21 00:23:34 +0100 (Mon, 21 Dec 2009) | 1 line Fixed #7552: fixed distutils.command.upload failure on very long passwords ........ --- command/upload.py | 5 +-- tests/support.py | 21 +++++++++++-- tests/test_upload.py | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 4 deletions(-) diff --git a/command/upload.py b/command/upload.py index 8805d41d..9f1aae6a 100644 --- a/command/upload.py +++ b/command/upload.py @@ -11,7 +11,7 @@ import os import socket import platform import httplib -import base64 +from base64 import standard_b64encode import urlparse import cStringIO as StringIO from ConfigParser import ConfigParser @@ -115,7 +115,8 @@ class upload(PyPIRCCommand): open(filename+".asc").read()) # set up the authentication - auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() + auth = "Basic " + standard_b64encode(self.username + ":" + + self.password) # Build up the MIME payload for the POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' diff --git a/tests/support.py b/tests/support.py index d24a18ea..9d373e94 100644 --- a/tests/support.py +++ b/tests/support.py @@ -1,10 +1,10 @@ """Support code for distutils test cases.""" - +import os import shutil import tempfile from distutils import log - +from distutils.dist import Distribution class LoggingSilencer(object): @@ -55,6 +55,23 @@ class TempdirManager(object): finally: f.close() + def create_dist(self, pkg_name='foo', **kw): + """Will generate a test environment. + + This function creates: + - a Distribution instance using keywords + - a temporary directory with a package structure + + It returns the package directory and the distribution + instance. + """ + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, pkg_name) + os.mkdir(pkg_dir) + dist = Distribution(attrs=kw) + + return pkg_dir, dist + class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" diff --git a/tests/test_upload.py b/tests/test_upload.py index b05ab1f7..322beb77 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -2,6 +2,7 @@ import sys import os import unittest +import httplib from distutils.command.upload import upload from distutils.core import Distribution @@ -9,8 +10,66 @@ from distutils.core import Distribution from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase +PYPIRC_LONG_PASSWORD = """\ +[distutils] + +index-servers = + server1 + server2 + +[server1] +username:me +password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ +""" + +class _Resp(object): + def __init__(self, status): + self.status = status + self.reason = 'OK' + +_CONNECTIONS = [] + +class _FakeHTTPConnection(object): + def __init__(self, netloc): + self.requests = [] + self.headers = {} + self.body = None + self.netloc = netloc + _CONNECTIONS.append(self) + + def connect(self): + pass + endheaders = connect + + def send(self, body): + self.body = body + + def putrequest(self, type_, data): + self.requests.append((type_, data)) + + def putheader(self, name, value): + self.headers[name] = value + + def getresponse(self): + return _Resp(200) + class uploadTestCase(PyPIRCCommandTestCase): + def setUp(self): + super(uploadTestCase, self).setUp() + self.old_klass = httplib.HTTPConnection + httplib.HTTPConnection = _FakeHTTPConnection + + def tearDown(self): + httplib.HTTPConnection = self.old_klass + super(uploadTestCase, self).tearDown() + def test_finalize_options(self): # new format @@ -27,6 +86,33 @@ class uploadTestCase(PyPIRCCommandTestCase): self.assertEquals(getattr(cmd, attr), waited) + def test_upload(self): + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path) + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) + + # lets run it + pkg_dir, dist = self.create_dist(dist_files=dist_files) + cmd = upload(dist) + cmd.ensure_finalized() + cmd.run() + + # what did we send ? + res = _CONNECTIONS[-1] + + headers = res.headers + self.assertEquals(headers['Content-length'], '2086') + self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) + + method, request = res.requests[-1] + self.assertEquals(method, 'POST') + self.assertEquals(res.netloc, 'pypi.python.org') + self.assertTrue('xxx' in res.body) + self.assertFalse('\n' in headers['Authorization']) + def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From 7553531b1974c77b2c51f9e22aaaa36eab115b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Dec 2009 00:02:20 +0000 Subject: Merged revisions 76952 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76952 | tarek.ziade | 2009-12-21 00:23:34 +0100 (Mon, 21 Dec 2009) | 1 line Fixed #7552: fixed distutils.command.upload failure on very long passwords ........ --- command/upload.py | 4 ++-- tests/test_upload.py | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/command/upload.py b/command/upload.py index defdda64..674edd47 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ import os, io import socket import platform from urllib.request import urlopen, Request, HTTPError -import base64 +from base64 import standard_b64encode from urllib.parse import urlparse from hashlib import md5 @@ -130,7 +130,7 @@ class upload(PyPIRCCommand): user_pass = (self.username + ":" + self.password).encode('ascii') # The exact encoding of the authentication string is debated. # Anyway PyPI only accepts ascii for both username or password. - auth = "Basic " + base64.encodebytes(user_pass).strip().decode('ascii') + auth = "Basic " + standard_b64encode(user_pass).decode('ascii') # Build up the MIME payload for the POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' diff --git a/tests/test_upload.py b/tests/test_upload.py index 92818858..979ab228 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -10,6 +10,25 @@ from distutils.core import Distribution from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase +PYPIRC_LONG_PASSWORD = """\ +[distutils] + +index-servers = + server1 + server2 + +[server1] +username:me +password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ +""" + + PYPIRC_NOPASSWORD = """\ [distutils] @@ -85,7 +104,7 @@ class uploadTestCase(PyPIRCCommandTestCase): self.write_file(path) command, pyversion, filename = 'xxx', '2.6', path dist_files = [(command, pyversion, filename)] - self.write_file(self.rc, PYPIRC) + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) # lets run it pkg_dir, dist = self.create_dist(dist_files=dist_files) @@ -101,6 +120,8 @@ class uploadTestCase(PyPIRCCommandTestCase): self.assertEquals(self.last_open.req.get_full_url(), 'http://pypi.python.org/pypi') self.assertTrue(b'xxx' in self.last_open.req.data) + auth = self.last_open.req.headers['Authorization'] + self.assertFalse('\n' in auth) def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From 4a22e2d83c34290cda083fba276d73242d5a6983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Dec 2009 00:08:17 +0000 Subject: Merged revisions 76954 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r76954 | tarek.ziade | 2009-12-21 01:02:20 +0100 (Mon, 21 Dec 2009) | 9 lines Merged revisions 76952 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76952 | tarek.ziade | 2009-12-21 00:23:34 +0100 (Mon, 21 Dec 2009) | 1 line Fixed #7552: fixed distutils.command.upload failure on very long passwords ........ ................ --- command/upload.py | 4 ++-- tests/test_upload.py | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/command/upload.py b/command/upload.py index 3b4a0367..f602fbeb 100644 --- a/command/upload.py +++ b/command/upload.py @@ -12,7 +12,7 @@ import socket import platform import configparser import http.client as httpclient -import base64 +from base64 import standard_b64encode import urllib.parse # this keeps compatibility for 2.3 and 2.4 @@ -127,7 +127,7 @@ class upload(PyPIRCCommand): user_pass = (self.username + ":" + self.password).encode('ascii') # The exact encoding of the authentication string is debated. # Anyway PyPI only accepts ascii for both username or password. - auth = "Basic " + base64.encodebytes(user_pass).strip().decode('ascii') + auth = "Basic " + standard_b64encode(user_pass).decode('ascii') # Build up the MIME payload for the POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' diff --git a/tests/test_upload.py b/tests/test_upload.py index 828f20c5..35e97005 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -10,6 +10,25 @@ from distutils.core import Distribution from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase +PYPIRC_LONG_PASSWORD = """\ +[distutils] + +index-servers = + server1 + server2 + +[server1] +username:me +password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ +""" + + PYPIRC_NOPASSWORD = """\ [distutils] @@ -96,7 +115,7 @@ class uploadTestCase(PyPIRCCommandTestCase): self.write_file(path) command, pyversion, filename = 'xxx', '2.6', path dist_files = [(command, pyversion, filename)] - self.write_file(self.rc, PYPIRC) + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) # lets run it pkg_dir, dist = self.create_dist(dist_files=dist_files) @@ -108,6 +127,7 @@ class uploadTestCase(PyPIRCCommandTestCase): headers = dict(self.conn.headers) self.assertEquals(headers['Content-length'], '2087') self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) + self.assertFalse('\n' in headers['Authorization']) self.assertEquals(self.conn.requests, [('POST', '/pypi')]) self.assert_((b'xxx') in self.conn.body) -- cgit v1.2.1 From 3bd7aa023d339ea64ec5b381f2bf4f832dd50232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Dec 2009 01:22:46 +0000 Subject: massive import cleaning in Distutils --- bcppcompiler.py | 10 ++++------ ccompiler.py | 40 +++++++++++++++++++++------------------- command/bdist.py | 6 +++--- command/bdist_msi.py | 2 -- command/bdist_rpm.py | 15 ++++++++------- command/bdist_wininst.py | 9 ++++++--- command/build_clib.py | 2 +- command/build_ext.py | 3 ++- command/build_py.py | 1 - command/config.py | 3 ++- command/register.py | 5 +++-- command/sdist.py | 9 +++++---- command/upload.py | 4 +--- config.py | 3 --- core.py | 17 +++++++---------- cygwinccompiler.py | 2 -- dir_util.py | 2 +- dist.py | 3 ++- emxccompiler.py | 2 -- extension.py | 1 - fancy_getopt.py | 32 ++++++++------------------------ msvc9compiler.py | 7 +++---- msvccompiler.py | 15 ++++++++------- text_file.py | 25 ++++++++++++------------- 24 files changed, 97 insertions(+), 121 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 5a0fa727..f26e7ae4 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -13,13 +13,11 @@ for the Borland C++ compiler. __revision__ = "$Id$" - import os -from distutils.errors import \ - DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError, UnknownFileError -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options + +from distutils.errors import (DistutilsExecError, CompileError, LibError, + LinkError, UnknownFileError) +from distutils.ccompiler import CCompiler, gen_preprocess_options from distutils.file_util import write_file from distutils.dep_util import newer from distutils import log diff --git a/ccompiler.py b/ccompiler.py index e093eef2..c0469151 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -5,9 +5,11 @@ for the Distutils compiler abstraction model.""" __revision__ = "$Id$" -import sys, os, re -from types import * -from distutils.errors import * +import sys +import os +import re + +from distutils.errors import CompileError, LinkError, UnknownFileError from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath @@ -165,7 +167,7 @@ class CCompiler: # set_executables () def set_executable(self, key, value): - if type(value) is StringType: + if isinstance(value, str): setattr(self, key, split_quoted(value)) else: setattr(self, key, value) @@ -187,11 +189,11 @@ class CCompiler: nothing if all definitions are OK, raise TypeError otherwise. """ for defn in definitions: - if not (type (defn) is TupleType and + if not (isinstance(defn, tuple) and (len (defn) == 1 or (len (defn) == 2 and - (type (defn[1]) is StringType or defn[1] is None))) and - type (defn[0]) is StringType): + (isinstance(defn[1], str) or defn[1] is None))) and + isinstance(defn[0], str)): raise TypeError, \ ("invalid macro definition '%s': " % defn) + \ "must be tuple (string,), (string, string), or " + \ @@ -341,19 +343,19 @@ class CCompiler: """ if outdir is None: outdir = self.output_dir - elif type(outdir) is not StringType: + elif not isinstance(outdir, str): raise TypeError, "'output_dir' must be a string or None" if macros is None: macros = self.macros - elif type(macros) is ListType: + elif isinstance(macros, list): macros = macros + (self.macros or []) else: raise TypeError, "'macros' (if supplied) must be a list of tuples" if incdirs is None: incdirs = self.include_dirs - elif type(incdirs) in (ListType, TupleType): + elif isinstance(incdirs, (list, tuple)): incdirs = list(incdirs) + (self.include_dirs or []) else: raise TypeError, \ @@ -444,14 +446,14 @@ class CCompiler: if macros is None: macros = self.macros - elif type (macros) is ListType: + elif isinstance(macros, list): macros = macros + (self.macros or []) else: raise TypeError, "'macros' (if supplied) must be a list of tuples" if include_dirs is None: include_dirs = self.include_dirs - elif type (include_dirs) in (ListType, TupleType): + elif isinstance(include_dirs, (list, tuple)): include_dirs = list (include_dirs) + (self.include_dirs or []) else: raise TypeError, \ @@ -517,14 +519,14 @@ class CCompiler: None, replace with self.output_dir. Return fixed versions of 'objects' and 'output_dir'. """ - if type (objects) not in (ListType, TupleType): + if not isinstance(objects, (list, tuple)): raise TypeError, \ "'objects' must be a list or tuple of strings" objects = list (objects) if output_dir is None: output_dir = self.output_dir - elif type (output_dir) is not StringType: + elif not isinstance(output_dir, str): raise TypeError, "'output_dir' must be a string or None" return (objects, output_dir) @@ -539,7 +541,7 @@ class CCompiler: """ if libraries is None: libraries = self.libraries - elif type (libraries) in (ListType, TupleType): + elif isinstance(libraries, (list, tuple)): libraries = list (libraries) + (self.libraries or []) else: raise TypeError, \ @@ -547,7 +549,7 @@ class CCompiler: if library_dirs is None: library_dirs = self.library_dirs - elif type (library_dirs) in (ListType, TupleType): + elif isinstance(library_dirs, (list, tuple)): library_dirs = list (library_dirs) + (self.library_dirs or []) else: raise TypeError, \ @@ -555,7 +557,7 @@ class CCompiler: if runtime_library_dirs is None: runtime_library_dirs = self.runtime_library_dirs - elif type (runtime_library_dirs) in (ListType, TupleType): + elif isinstance(runtime_library_dirs, (list, tuple)): runtime_library_dirs = (list (runtime_library_dirs) + (self.runtime_library_dirs or [])) else: @@ -587,7 +589,7 @@ class CCompiler: """Detect the language of a given file, or list of files. Uses language_map, and language_order to do the job. """ - if type(sources) is not ListType: + if not isinstance(sources, list): sources = [sources] lang = None index = len(self.language_order) @@ -1194,7 +1196,7 @@ def gen_preprocess_options (macros, include_dirs): pp_opts = [] for macro in macros: - if not (type (macro) is TupleType and + if not (isinstance(macro, tuple) and 1 <= len (macro) <= 2): raise TypeError, \ ("bad macro definition '%s': " + diff --git a/command/bdist.py b/command/bdist.py index 8e0ad080..8cd69701 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,9 +6,9 @@ distribution).""" __revision__ = "$Id$" import os -from types import * + from distutils.core import Command -from distutils.errors import * +from distutils.errors import DistutilsPlatformError, DistutilsOptionError from distutils.util import get_platform @@ -16,7 +16,7 @@ def show_formats(): """Print list of available formats (arguments to "--format" option). """ from distutils.fancy_getopt import FancyGetopt - formats=[] + formats = [] for format in bdist.format_commands: formats.append(("formats=" + format, None, bdist.format_command[format][1])) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 7a5ca807..f3791bed 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -28,7 +28,6 @@ class PyDialog(Dialog): default, cancel, bitmap=true)""" Dialog.__init__(self, *args) ruler = self.h - 36 - bmwidth = 152*ruler/328 #if kw.get("bitmap", True): # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin") self.line("BottomLine", 0, ruler, self.w, 0) @@ -420,7 +419,6 @@ class bdist_msi (Command): # see "Dialog Style Bits" modal = 3 # visible | modal modeless = 1 # visible - track_disk_space = 32 # UI customization properties add_data(db, "Property", diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 1eccc6a4..6d9d47d2 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -5,14 +5,15 @@ distributions).""" __revision__ = "$Id$" -import sys, os, string -from types import * +import sys +import os +import string + from distutils.core import Command from distutils.debug import DEBUG -from distutils.util import get_platform from distutils.file_util import write_file -from distutils.errors import * -from distutils.sysconfig import get_python_version +from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, + DistutilsFileError, DistutilsExecError) from distutils import log class bdist_rpm (Command): @@ -225,7 +226,7 @@ class bdist_rpm (Command): self.distribution.get_contact_email())) self.ensure_string('packager') self.ensure_string_list('doc_files') - if type(self.doc_files) is ListType: + if isinstance(self.doc_files, list): for readme in ('README', 'README.txt'): if os.path.exists(readme) and readme not in self.doc_files: self.doc_files.append(readme) @@ -444,7 +445,7 @@ class bdist_rpm (Command): 'Obsoletes', ): val = getattr(self, string.lower(field)) - if type(val) is ListType: + if isinstance(val, list): spec_file.append('%s: %s' % (field, string.join(val))) elif val is not None: spec_file.append('%s: %s' % (field, val)) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 8a6eca59..cf4282d8 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -5,11 +5,14 @@ exe-program.""" __revision__ = "$Id$" -import sys, os, string +import sys +import os +import string + from distutils.core import Command from distutils.util import get_platform -from distutils.dir_util import create_tree, remove_tree -from distutils.errors import * +from distutils.dir_util import remove_tree +from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.sysconfig import get_python_version from distutils import log diff --git a/command/build_clib.py b/command/build_clib.py index 9545b27a..50b64dba 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -18,7 +18,7 @@ __revision__ = "$Id$" import os from distutils.core import Command -from distutils.errors import * +from distutils.errors import DistutilsSetupError from distutils.sysconfig import customize_compiler from distutils import log diff --git a/command/build_ext.py b/command/build_ext.py index 0c79476b..ff1a4be3 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -10,7 +10,8 @@ import sys, os, re from warnings import warn from distutils.core import Command -from distutils.errors import * +from distutils.errors import (CCompilerError, DistutilsError, CompileError, + DistutilsSetupError, DistutilsPlatformError) from distutils.sysconfig import customize_compiler, get_python_version from distutils.dep_util import newer_group from distutils.extension import Extension diff --git a/command/build_py.py b/command/build_py.py index 5c7b473b..04c455f0 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -133,7 +133,6 @@ class build_py(Command): def build_package_data(self): """Copy data files into build directory""" - lastdir = None for package, src_dir, build_dir, filenames in self.data_files: for filename in filenames: target = os.path.join(build_dir, filename) diff --git a/command/config.py b/command/config.py index 134fa389..b0849135 100644 --- a/command/config.py +++ b/command/config.py @@ -11,7 +11,8 @@ this header file lives". __revision__ = "$Id$" -import sys, os, re +import os +import re from distutils.core import Command from distutils.errors import DistutilsExecError diff --git a/command/register.py b/command/register.py index 3b5b2080..fb547c93 100644 --- a/command/register.py +++ b/command/register.py @@ -7,12 +7,13 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" -import os, string, urllib2, getpass, urlparse +import urllib2 +import getpass +import urlparse import StringIO from warnings import warn from distutils.core import PyPIRCCommand -from distutils.errors import * from distutils import log class register(PyPIRCCommand): diff --git a/command/sdist.py b/command/sdist.py index 5c74e532..f1335e6a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -4,16 +4,17 @@ Implements the Distutils 'sdist' command (create a source distribution).""" __revision__ = "$Id$" -import os, string +import os +import string import sys -from types import * from glob import glob from warnings import warn from distutils.core import Command from distutils import dir_util, dep_util, file_util, archive_util from distutils.text_file import TextFile -from distutils.errors import * +from distutils.errors import (DistutilsPlatformError, DistutilsOptionError, + DistutilsTemplateError) from distutils.filelist import FileList from distutils import log from distutils.util import convert_path @@ -256,7 +257,7 @@ class sdist(Command): standards = [('README', 'README.txt'), self.distribution.script_name] for fn in standards: - if type(fn) is TupleType: + if isinstance(fn, tuple): alts = fn got_it = 0 for fn in alts: diff --git a/command/upload.py b/command/upload.py index 5d6ebcfa..3e18aeaa 100644 --- a/command/upload.py +++ b/command/upload.py @@ -1,7 +1,6 @@ """distutils.command.upload Implements the Distutils 'upload' subcommand (upload package to PyPI).""" -import sys import os import socket import platform @@ -9,10 +8,9 @@ from urllib2 import urlopen, Request, HTTPError from base64 import standard_b64encode import urlparse import cStringIO as StringIO -from ConfigParser import ConfigParser from hashlib import md5 -from distutils.errors import * +from distutils.errors import DistutilsOptionError from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log diff --git a/config.py b/config.py index 9166199e..afa403f2 100644 --- a/config.py +++ b/config.py @@ -4,7 +4,6 @@ Provides the PyPIRCCommand class, the base class for the command classes that uses .pypirc in the distutils.command package. """ import os -import sys from ConfigParser import ConfigParser from distutils.cmd import Command @@ -60,8 +59,6 @@ class PyPIRCCommand(Command): if os.path.exists(rc): self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY - realm = self.realm or self.DEFAULT_REALM - config = ConfigParser() config.read(rc) sections = config.sections() diff --git a/core.py b/core.py index 8073a6bc..0b725ff0 100644 --- a/core.py +++ b/core.py @@ -8,10 +8,12 @@ really defined in distutils.dist and distutils.cmd. __revision__ = "$Id$" -import sys, os +import sys +import os from distutils.debug import DEBUG -from distutils.errors import * +from distutils.errors import (DistutilsSetupError, DistutilsArgError, + DistutilsError, CCompilerError) from distutils.util import grok_environment_error # Mainly import these so setup scripts can "from distutils.core import" them. @@ -31,7 +33,7 @@ usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: %(script)s cmd --help """ -def gen_usage (script_name): +def gen_usage(script_name): script = os.path.basename(script_name) return USAGE % vars() @@ -56,7 +58,7 @@ extension_keywords = ('name', 'sources', 'include_dirs', 'extra_objects', 'extra_compile_args', 'extra_link_args', 'swig_opts', 'export_symbols', 'depends', 'language') -def setup (**attrs): +def setup(**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a Distribution instance; find and parse config files; parse the command @@ -168,10 +170,8 @@ def setup (**attrs): return dist -# setup () - -def run_setup (script_name, script_args=None, stop_after="run"): +def run_setup(script_name, script_args=None, stop_after="run"): """Run a setup script in a somewhat controlled environment, and return the Distribution instance that drives things. This is useful if you need to find out the distribution meta-data (passed as @@ -235,7 +235,4 @@ def run_setup (script_name, script_args=None, stop_after="run"): # I wonder if the setup script's namespace -- g and l -- would be of # any interest to callers? - #print "_setup_distribution:", _setup_distribution return _setup_distribution - -# run_setup () diff --git a/cygwinccompiler.py b/cygwinccompiler.py index a8476e65..eed9c321 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -53,11 +53,9 @@ import copy import re from warnings import warn -from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils import log from distutils.util import get_compiler_versions def get_msvcr(): diff --git a/dir_util.py b/dir_util.py index 3e2cd353..9b4d2adb 100644 --- a/dir_util.py +++ b/dir_util.py @@ -4,7 +4,7 @@ Utility functions for manipulating directories and directory trees.""" __revision__ = "$Id$" -import os, sys +import os from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log diff --git a/dist.py b/dist.py index ee2cec90..f20a92a2 100644 --- a/dist.py +++ b/dist.py @@ -14,7 +14,8 @@ try: except ImportError: warnings = None -from distutils.errors import * +from distutils.errors import (DistutilsOptionError, DistutilsArgError, + DistutilsModuleError, DistutilsClassError) from distutils.fancy_getopt import FancyGetopt, translate_longopt from distutils.util import check_environ, strtobool, rfc822_escape from distutils import log diff --git a/emxccompiler.py b/emxccompiler.py index a5a2ef88..f2f77e52 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -24,11 +24,9 @@ __revision__ = "$Id$" import os, sys, copy from warnings import warn -from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils import log from distutils.util import get_compiler_versions class EMXCCompiler (UnixCCompiler): diff --git a/extension.py b/extension.py index 6af18108..9988ec0c 100644 --- a/extension.py +++ b/extension.py @@ -6,7 +6,6 @@ modules in setup scripts.""" __revision__ = "$Id$" import os -import sys import warnings # This class is really only used by the "build_ext" command, so it might diff --git a/fancy_getopt.py b/fancy_getopt.py index e19319ae..2dea9480 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -10,10 +10,11 @@ additional features: __revision__ = "$Id$" -import sys, string, re -from types import * +import sys +import string +import re import getopt -from distutils.errors import * +from distutils.errors import DistutilsGetoptError, DistutilsArgError # Much like command_re in distutils.core, this is close to but not quite # the same as a Python NAME -- except, in the spirit of most GNU @@ -117,7 +118,7 @@ class FancyGetopt: def _check_alias_dict (self, aliases, what): - assert type(aliases) is DictionaryType + assert isinstance(aliases, dict) for (alias, opt) in aliases.items(): if alias not in self.option_index: raise DistutilsGetoptError, \ @@ -164,13 +165,13 @@ class FancyGetopt: raise ValueError, "invalid option tuple: %r" % (option,) # Type- and value-check the option names - if type(long) is not StringType or len(long) < 2: + if not isinstance(long, str) or len(long) < 2: raise DistutilsGetoptError, \ ("invalid long option '%s': " "must be a string of length >= 2") % long if (not ((short is None) or - (type(short) is StringType and len(short) == 1))): + (isinstance(short, str) and len(short) == 1))): raise DistutilsGetoptError, \ ("invalid short option '%s': " "must a single character or None") % short @@ -464,10 +465,8 @@ def wrap_text (text, width): return lines -# wrap_text () - -def translate_longopt (opt): +def translate_longopt(opt): """Convert a long option name to a valid Python identifier by changing "-" to "_". """ @@ -483,18 +482,3 @@ class OptionDummy: 'options' will be initialized to None.""" for opt in options: setattr(self, opt, None) - -# class OptionDummy - - -if __name__ == "__main__": - text = """\ -Tra-la-la, supercalifragilisticexpialidocious. -How *do* you spell that odd word, anyways? -(Someone ask Mary -- she'll know [or she'll -say, "How should I know?"].)""" - - for w in (10, 20, 30, 40): - print "width: %d" % w - print string.join(wrap_text(text, w), "\n") - print diff --git a/msvc9compiler.py b/msvc9compiler.py index c2287d92..2309d894 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -19,10 +19,9 @@ import subprocess import sys import re -from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import CCompiler, gen_preprocess_options, \ - gen_lib_options +from distutils.errors import (DistutilsExecError, DistutilsPlatformError, + CompileError, LibError, LinkError) +from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log from distutils.util import get_platform diff --git a/msvccompiler.py b/msvccompiler.py index d38afb72..0e69fd36 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -10,12 +10,13 @@ for the Microsoft Visual Studio. __revision__ = "$Id$" -import sys, os, string -from distutils.errors import \ - DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options +import sys +import os +import string + +from distutils.errors import (DistutilsExecError, DistutilsPlatformError, + CompileError, LibError, LinkError) +from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log _can_read_reg = 0 @@ -127,7 +128,7 @@ class MacroExpander: self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") else: self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") - except KeyError, exc: # + except KeyError: raise DistutilsPlatformError, \ ("""Python was built with Visual Studio 2003; extensions must be built with a compiler than can generate compatible binaries. diff --git a/text_file.py b/text_file.py index 931f0bac..09a798b1 100644 --- a/text_file.py +++ b/text_file.py @@ -6,8 +6,7 @@ lines, and joining lines with backslashes.""" __revision__ = "$Id$" -from types import * -import sys, os, string +import sys class TextFile: @@ -137,12 +136,12 @@ class TextFile: if line is None: line = self.current_line outmsg.append(self.filename + ", ") - if type (line) in (ListType, TupleType): + if isinstance(line, (list, tuple)): outmsg.append("lines %d-%d: " % tuple (line)) else: outmsg.append("line %d: " % line) outmsg.append(str(msg)) - return string.join(outmsg, "") + return ''.join(outmsg) def error (self, msg, line=None): @@ -196,7 +195,7 @@ class TextFile: # unescape it (and any other escaped "#"'s that might be # lurking in there) and otherwise leave the line alone. - pos = string.find (line, "#") + pos = line.find("#") if pos == -1: # no "#" -- no comments pass @@ -219,11 +218,11 @@ class TextFile: # # comment that should be ignored # there # result in "hello there". - if string.strip(line) == "": + if line.strip() == "": continue else: # it's an escaped "#" - line = string.replace (line, "\\#", "#") + line = line.replace("\\#", "#") # did previous line end with a backslash? then accumulate @@ -235,11 +234,11 @@ class TextFile: return buildup_line if self.collapse_join: - line = string.lstrip (line) + line = line.lstrip() line = buildup_line + line # careful: pay attention to line number when incrementing it - if type (self.current_line) is ListType: + if isinstance(self.current_line, list): self.current_line[1] = self.current_line[1] + 1 else: self.current_line = [self.current_line, @@ -250,7 +249,7 @@ class TextFile: return None # still have to be careful about incrementing the line number! - if type (self.current_line) is ListType: + if isinstance(self.current_line, list): self.current_line = self.current_line[1] + 1 else: self.current_line = self.current_line + 1 @@ -259,11 +258,11 @@ class TextFile: # strip whitespace however the client wants (leading and # trailing, or one or the other, or neither) if self.lstrip_ws and self.rstrip_ws: - line = string.strip (line) + line = line.strip() elif self.lstrip_ws: - line = string.lstrip (line) + line = line.lstrip() elif self.rstrip_ws: - line = string.rstrip (line) + line = line.rstrip() # blank line (whether we rstrip'ed or not)? skip to next line # if appropriate -- cgit v1.2.1 From 9414e0ddcaa3c08782325904d0d1d890dc8fa332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Dec 2009 01:49:00 +0000 Subject: Merged revisions 76956 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r76956 | tarek.ziade | 2009-12-21 02:22:46 +0100 (Mon, 21 Dec 2009) | 1 line massive import cleaning in Distutils ........ --- bcppcompiler.py | 10 ++++------ ccompiler.py | 7 +++++-- command/bdist.py | 3 ++- command/bdist_msi.py | 2 -- command/bdist_rpm.py | 9 +++++---- command/bdist_wininst.py | 8 +++++--- command/build_clib.py | 2 +- command/build_ext.py | 3 ++- command/build_py.py | 1 - command/config.py | 3 ++- command/register.py | 8 +++++--- command/sdist.py | 4 ++-- command/upload.py | 6 +++--- config.py | 3 --- core.py | 17 +++++++---------- cygwinccompiler.py | 2 -- dir_util.py | 2 +- dist.py | 3 ++- emxccompiler.py | 2 -- extension.py | 1 - fancy_getopt.py | 19 ++++--------------- msvc9compiler.py | 7 +++---- msvccompiler.py | 14 +++++++------- text_file.py | 4 ++-- 24 files changed, 62 insertions(+), 78 deletions(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index c5e5cd25..77d94a59 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -13,13 +13,11 @@ for the Borland C++ compiler. __revision__ = "$Id$" - import os -from distutils.errors import \ - DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError, UnknownFileError -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options + +from distutils.errors import (DistutilsExecError, CompileError, LibError, + LinkError, UnknownFileError) +from distutils.ccompiler import CCompiler, gen_preprocess_options from distutils.file_util import write_file from distutils.dep_util import newer from distutils import log diff --git a/ccompiler.py b/ccompiler.py index ff7f9df1..407cabda 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -5,8 +5,11 @@ for the Distutils compiler abstraction model.""" __revision__ = "$Id$" -import sys, os, re -from distutils.errors import * +import sys +import os +import re + +from distutils.errors import CompileError, LinkError, UnknownFileError from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath diff --git a/command/bdist.py b/command/bdist.py index 2c81f3a9..dd202ff4 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,8 +6,9 @@ distribution).""" __revision__ = "$Id$" import os + from distutils.core import Command -from distutils.errors import * +from distutils.errors import DistutilsPlatformError, DistutilsOptionError from distutils.util import get_platform diff --git a/command/bdist_msi.py b/command/bdist_msi.py index c4be47b5..f9205483 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -28,7 +28,6 @@ class PyDialog(Dialog): default, cancel, bitmap=true)""" Dialog.__init__(self, *args) ruler = self.h - 36 - bmwidth = 152*ruler/328 #if kw.get("bitmap", True): # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin") self.line("BottomLine", 0, ruler, self.w, 0) @@ -419,7 +418,6 @@ class bdist_msi(Command): # see "Dialog Style Bits" modal = 3 # visible | modal modeless = 1 # visible - track_disk_space = 32 # UI customization properties add_data(db, "Property", diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 452f9502..0a579dbb 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -5,13 +5,14 @@ distributions).""" __revision__ = "$Id$" -import sys, os +import sys +import os + from distutils.core import Command from distutils.debug import DEBUG -from distutils.util import get_platform from distutils.file_util import write_file -from distutils.errors import * -from distutils.sysconfig import get_python_version +from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, + DistutilsFileError, DistutilsExecError) from distutils import log class bdist_rpm(Command): diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index d6d01c63..0b7044a0 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -5,11 +5,13 @@ exe-program.""" __revision__ = "$Id$" -import sys, os +import sys +import os + from distutils.core import Command from distutils.util import get_platform -from distutils.dir_util import create_tree, remove_tree -from distutils.errors import * +from distutils.dir_util import remove_tree +from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.sysconfig import get_python_version from distutils import log diff --git a/command/build_clib.py b/command/build_clib.py index 258d7c10..12bf1d2f 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -18,7 +18,7 @@ __revision__ = "$Id$" import os from distutils.core import Command -from distutils.errors import * +from distutils.errors import DistutilsSetupError from distutils.sysconfig import customize_compiler from distutils import log diff --git a/command/build_ext.py b/command/build_ext.py index 70dd81f1..de980bd8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -10,7 +10,8 @@ import sys, os, re from warnings import warn from distutils.core import Command -from distutils.errors import * +from distutils.errors import (CCompilerError, DistutilsError, CompileError, + DistutilsSetupError, DistutilsPlatformError) from distutils.sysconfig import customize_compiler, get_python_version from distutils.dep_util import newer_group from distutils.extension import Extension diff --git a/command/build_py.py b/command/build_py.py index fa085796..7cc93539 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -133,7 +133,6 @@ class build_py(Command): def build_package_data(self): """Copy data files into build directory""" - lastdir = None for package, src_dir, build_dir, filenames in self.data_files: for filename in filenames: target = os.path.join(build_dir, filename) diff --git a/command/config.py b/command/config.py index ac80a54e..830552cd 100644 --- a/command/config.py +++ b/command/config.py @@ -11,7 +11,8 @@ this header file lives". __revision__ = "$Id$" -import sys, os, re +import os +import re from distutils.core import Command from distutils.errors import DistutilsExecError diff --git a/command/register.py b/command/register.py index bdf5f8f0..7d3dc53a 100644 --- a/command/register.py +++ b/command/register.py @@ -7,13 +7,15 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" -import os, string, getpass +import os +import string +import getpass import io -import urllib.parse, urllib.request +import urllib.parse +import urllib.request from warnings import warn from distutils.core import PyPIRCCommand -from distutils.errors import * from distutils import log class register(PyPIRCCommand): diff --git a/command/sdist.py b/command/sdist.py index 76e1de81..f33406cd 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -7,14 +7,14 @@ __revision__ = "$Id$" import os import string import sys -from types import * from glob import glob from warnings import warn from distutils.core import Command from distutils import dir_util, dep_util, file_util, archive_util from distutils.text_file import TextFile -from distutils.errors import * +from distutils.errors import (DistutilsPlatformError, DistutilsOptionError, + DistutilsTemplateError) from distutils.filelist import FileList from distutils import log from distutils.util import convert_path diff --git a/command/upload.py b/command/upload.py index 674edd47..bb1b7fc4 100644 --- a/command/upload.py +++ b/command/upload.py @@ -1,8 +1,8 @@ """distutils.command.upload Implements the Distutils 'upload' subcommand (upload package to PyPI).""" -import sys -import os, io +import os +import io import socket import platform from urllib.request import urlopen, Request, HTTPError @@ -10,7 +10,7 @@ from base64 import standard_b64encode from urllib.parse import urlparse from hashlib import md5 -from distutils.errors import * +from distutils.errors import DistutilsOptionError from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log diff --git a/config.py b/config.py index 5b625f3f..fe41ce97 100644 --- a/config.py +++ b/config.py @@ -4,7 +4,6 @@ Provides the PyPIRCCommand class, the base class for the command classes that uses .pypirc in the distutils.command package. """ import os -import sys from configparser import ConfigParser from distutils.cmd import Command @@ -60,8 +59,6 @@ class PyPIRCCommand(Command): if os.path.exists(rc): self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY - realm = self.realm or self.DEFAULT_REALM - config = ConfigParser() config.read(rc) sections = config.sections() diff --git a/core.py b/core.py index 95c03574..c820a420 100644 --- a/core.py +++ b/core.py @@ -8,10 +8,12 @@ really defined in distutils.dist and distutils.cmd. __revision__ = "$Id$" -import sys, os +import sys +import os from distutils.debug import DEBUG -from distutils.errors import * +from distutils.errors import (DistutilsSetupError, DistutilsArgError, + DistutilsError, CCompilerError) from distutils.util import grok_environment_error # Mainly import these so setup scripts can "from distutils.core import" them. @@ -31,7 +33,7 @@ usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: %(script)s cmd --help """ -def gen_usage (script_name): +def gen_usage(script_name): script = os.path.basename(script_name) return USAGE % vars() @@ -56,7 +58,7 @@ extension_keywords = ('name', 'sources', 'include_dirs', 'extra_objects', 'extra_compile_args', 'extra_link_args', 'swig_opts', 'export_symbols', 'depends', 'language') -def setup (**attrs): +def setup(**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a Distribution instance; find and parse config files; parse the command @@ -168,10 +170,8 @@ def setup (**attrs): return dist -# setup () - -def run_setup (script_name, script_args=None, stop_after="run"): +def run_setup(script_name, script_args=None, stop_after="run"): """Run a setup script in a somewhat controlled environment, and return the Distribution instance that drives things. This is useful if you need to find out the distribution meta-data (passed as @@ -234,7 +234,4 @@ def run_setup (script_name, script_args=None, stop_after="run"): # I wonder if the setup script's namespace -- g and l -- would be of # any interest to callers? - #print "_setup_distribution:", _setup_distribution return _setup_distribution - -# run_setup () diff --git a/cygwinccompiler.py b/cygwinccompiler.py index d9f4a43d..3e2a634f 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -53,11 +53,9 @@ import copy import re from warnings import warn -from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils import log from distutils.util import get_compiler_versions def get_msvcr(): diff --git a/dir_util.py b/dir_util.py index 98e6252c..370025b7 100644 --- a/dir_util.py +++ b/dir_util.py @@ -4,7 +4,7 @@ Utility functions for manipulating directories and directory trees.""" __revision__ = "$Id$" -import os, sys +import os from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log diff --git a/dist.py b/dist.py index 353525e1..5a107e7d 100644 --- a/dist.py +++ b/dist.py @@ -14,7 +14,8 @@ try: except ImportError: warnings = None -from distutils.errors import * +from distutils.errors import (DistutilsOptionError, DistutilsArgError, + DistutilsModuleError, DistutilsClassError) from distutils.fancy_getopt import FancyGetopt, translate_longopt from distutils.util import check_environ, strtobool, rfc822_escape from distutils import log diff --git a/emxccompiler.py b/emxccompiler.py index 50634d6c..fd79aec2 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -24,11 +24,9 @@ __revision__ = "$Id$" import os, sys, copy from warnings import warn -from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils import log from distutils.util import get_compiler_versions class EMXCCompiler (UnixCCompiler): diff --git a/extension.py b/extension.py index 5c07bdae..10aaf7c5 100644 --- a/extension.py +++ b/extension.py @@ -6,7 +6,6 @@ modules in setup scripts.""" __revision__ = "$Id$" import os -import sys import warnings # This class is really only used by the "build_ext" command, so it might diff --git a/fancy_getopt.py b/fancy_getopt.py index 879d4d25..73343ad5 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -10,9 +10,11 @@ additional features: __revision__ = "$Id$" -import sys, string, re +import sys +import string +import re import getopt -from distutils.errors import * +from distutils.errors import DistutilsGetoptError, DistutilsArgError # Much like command_re in distutils.core, this is close to but not quite # the same as a Python NAME -- except, in the spirit of most GNU @@ -444,16 +446,3 @@ class OptionDummy: 'options' will be initialized to None.""" for opt in options: setattr(self, opt, None) - - -if __name__ == "__main__": - text = """\ -Tra-la-la, supercalifragilisticexpialidocious. -How *do* you spell that odd word, anyways? -(Someone ask Mary -- she'll know [or she'll -say, "How should I know?"].)""" - - for w in (10, 20, 30, 40): - print("width: %d" % w) - print("\n".join(wrap_text(text, w))) - print() diff --git a/msvc9compiler.py b/msvc9compiler.py index c84fb0b4..4d05a445 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -19,10 +19,9 @@ import subprocess import sys import re -from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import CCompiler, gen_preprocess_options, \ - gen_lib_options +from distutils.errors import (DistutilsExecError, DistutilsPlatformError, + CompileError, LibError, LinkError) +from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log from distutils.util import get_platform diff --git a/msvccompiler.py b/msvccompiler.py index 1cd0f91d..dc3bd8de 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -10,12 +10,12 @@ for the Microsoft Visual Studio. __revision__ = "$Id$" -import sys, os -from distutils.errors import \ - DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options +import sys +import os + +from distutils.errors import (DistutilsExecError, DistutilsPlatformError, + CompileError, LibError, LinkError) +from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log _can_read_reg = False @@ -124,7 +124,7 @@ class MacroExpander: self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") else: self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") - except KeyError as exc: # + except KeyError: raise DistutilsPlatformError( """Python was built with Visual Studio 2003; extensions must be built with a compiler than can generate compatible binaries. diff --git a/text_file.py b/text_file.py index 97459fbf..53c8561a 100644 --- a/text_file.py +++ b/text_file.py @@ -6,8 +6,8 @@ lines, and joining lines with backslashes.""" __revision__ = "$Id$" -import sys, os, io - +import sys +import io class TextFile: """Provides a file-like object that takes care of all the things you -- cgit v1.2.1 From 64110315b99e2f3a3d900cdaa4edd593a47373ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Dec 2009 23:12:41 +0000 Subject: Fixed #7556: editing the MSVC manifest file with a regexp was throwing an error --- msvc9compiler.py | 51 ++++++++++++++------------ tests/test_msvc9compiler.py | 87 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 109 insertions(+), 29 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 4d05a445..6455fffa 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -645,28 +645,8 @@ class MSVCCompiler(CCompiler) : mfid = 1 else: mfid = 2 - try: - # Remove references to the Visual C runtime, so they will - # fall through to the Visual C dependency of Python.exe. - # This way, when installed for a restricted user (e.g. - # runtimes are not in WinSxS folder, but in Python's own - # folder), the runtimes do not need to be in every folder - # with .pyd's. - manifest_f = open(temp_manifest, "rb") - manifest_buf = manifest_f.read() - manifest_f.close() - pattern = re.compile( - r"""|)""", - re.DOTALL) - manifest_buf = re.sub(pattern, "", manifest_buf) - pattern = "\s*" - manifest_buf = re.sub(pattern, "", manifest_buf) - manifest_f = open(temp_manifest, "wb") - manifest_f.write(manifest_buf) - manifest_f.close() - except IOError: - pass + # Remove references to the Visual C runtime + self._remove_visual_c_ref(temp_manifest) out_arg = '-outputresource:%s;%s' % (output_filename, mfid) try: self.spawn(['mt.exe', '-nologo', '-manifest', @@ -676,6 +656,33 @@ class MSVCCompiler(CCompiler) : else: log.debug("skipping %s (up-to-date)", output_filename) + def _remove_visual_c_ref(self, manifest_file): + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + manifest_f = open(manifest_file) + try: + manifest_buf = manifest_f.read() + finally: + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = "\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + manifest_f = open(manifest_file, 'w') + try: + manifest_f.write(manifest_buf) + finally: + manifest_f.close() + except IOError: + pass # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 05d34e60..e1f08d8a 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -1,18 +1,73 @@ """Tests for distutils.msvc9compiler.""" import sys import unittest +import os from distutils.errors import DistutilsPlatformError +from distutils.tests import support -class msvc9compilerTestCase(unittest.TestCase): +_MANIFEST = """\ + + + + + + + + + + + + + + + + + + + + + + +""" + +_CLEANED_MANIFEST = """\ + + + + + + + + + + + + + + + + + + +""" + +@unittest.skip("These tests are only for win32") +class msvc9compilerTestCase(support.TempdirManager, + unittest.TestCase): def test_no_compiler(self): # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler # is not found - if sys.platform != 'win32': - # this test is only for win32 - return from distutils.msvccompiler import get_build_version if get_build_version() < 8.0: # this test is only for MSVC8.0 or above @@ -31,9 +86,6 @@ class msvc9compilerTestCase(unittest.TestCase): msvc9compiler.find_vcvarsall = old_find_vcvarsall def test_reg_class(self): - if sys.platform != 'win32': - # this test is only for win32 - return from distutils.msvccompiler import get_build_version if get_build_version() < 8.0: # this test is only for MSVC8.0 or above @@ -56,6 +108,27 @@ class msvc9compilerTestCase(unittest.TestCase): keys = Reg.read_keys(HKCU, r'Control Panel') self.assertTrue('Desktop' in keys) + def test_remove_visual_c_ref(self): + from distutils.msvc9compiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + f.write(_MANIFEST) + f.close() + + compiler = MSVCCompiler() + compiler._remove_visual_c_ref(manifest) + + # see what we got + f = open(manifest) + # removing trailing spaces + content = '\n'.join([line.rstrip() for line in f.readlines()]) + f.close() + + # makes sure the manifest was properly cleaned + self.assertEquals(content, _CLEANED_MANIFEST) + + def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) -- cgit v1.2.1 From 8ea52f665aeeacfecc93f73974760b0c45a89c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Dec 2009 23:16:09 +0000 Subject: forgot to add the win32 test in the unittest skip call --- tests/test_msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index e1f08d8a..8a908d99 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -60,7 +60,7 @@ _CLEANED_MANIFEST = """\ """ -@unittest.skip("These tests are only for win32") +@unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): -- cgit v1.2.1 From 0c509da5a253d37dbb5a3890b5effe1948acd62c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Dec 2009 23:18:02 +0000 Subject: Merged revisions 76993-76994 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r76993 | tarek.ziade | 2009-12-22 00:12:41 +0100 (Tue, 22 Dec 2009) | 1 line Fixed #7556: editing the MSVC manifest file with a regexp was throwing an error ........ r76994 | tarek.ziade | 2009-12-22 00:16:09 +0100 (Tue, 22 Dec 2009) | 1 line forgot to add the win32 test in the unittest skip call ........ --- msvc9compiler.py | 51 ++++++++++++++------------ tests/test_msvc9compiler.py | 87 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 109 insertions(+), 29 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index c84fb0b4..ad021b57 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -646,28 +646,8 @@ class MSVCCompiler(CCompiler) : mfid = 1 else: mfid = 2 - try: - # Remove references to the Visual C runtime, so they will - # fall through to the Visual C dependency of Python.exe. - # This way, when installed for a restricted user (e.g. - # runtimes are not in WinSxS folder, but in Python's own - # folder), the runtimes do not need to be in every folder - # with .pyd's. - manifest_f = open(temp_manifest, "rb") - manifest_buf = manifest_f.read() - manifest_f.close() - pattern = re.compile( - r"""|)""", - re.DOTALL) - manifest_buf = re.sub(pattern, "", manifest_buf) - pattern = "\s*" - manifest_buf = re.sub(pattern, "", manifest_buf) - manifest_f = open(temp_manifest, "wb") - manifest_f.write(manifest_buf) - manifest_f.close() - except IOError: - pass + # Remove references to the Visual C runtime + self._remove_visual_c_ref(temp_manifest) out_arg = '-outputresource:%s;%s' % (output_filename, mfid) try: self.spawn(['mt.exe', '-nologo', '-manifest', @@ -677,6 +657,33 @@ class MSVCCompiler(CCompiler) : else: log.debug("skipping %s (up-to-date)", output_filename) + def _remove_visual_c_ref(self, manifest_file): + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + manifest_f = open(manifest_file) + try: + manifest_buf = manifest_f.read() + finally: + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = "\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + manifest_f = open(manifest_file, 'w') + try: + manifest_f.write(manifest_buf) + finally: + manifest_f.close() + except IOError: + pass # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 05d34e60..8a908d99 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -1,18 +1,73 @@ """Tests for distutils.msvc9compiler.""" import sys import unittest +import os from distutils.errors import DistutilsPlatformError +from distutils.tests import support -class msvc9compilerTestCase(unittest.TestCase): +_MANIFEST = """\ + + + + + + + + + + + + + + + + + + + + + + +""" + +_CLEANED_MANIFEST = """\ + + + + + + + + + + + + + + + + + + +""" + +@unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") +class msvc9compilerTestCase(support.TempdirManager, + unittest.TestCase): def test_no_compiler(self): # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler # is not found - if sys.platform != 'win32': - # this test is only for win32 - return from distutils.msvccompiler import get_build_version if get_build_version() < 8.0: # this test is only for MSVC8.0 or above @@ -31,9 +86,6 @@ class msvc9compilerTestCase(unittest.TestCase): msvc9compiler.find_vcvarsall = old_find_vcvarsall def test_reg_class(self): - if sys.platform != 'win32': - # this test is only for win32 - return from distutils.msvccompiler import get_build_version if get_build_version() < 8.0: # this test is only for MSVC8.0 or above @@ -56,6 +108,27 @@ class msvc9compilerTestCase(unittest.TestCase): keys = Reg.read_keys(HKCU, r'Control Panel') self.assertTrue('Desktop' in keys) + def test_remove_visual_c_ref(self): + from distutils.msvc9compiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + f.write(_MANIFEST) + f.close() + + compiler = MSVCCompiler() + compiler._remove_visual_c_ref(manifest) + + # see what we got + f = open(manifest) + # removing trailing spaces + content = '\n'.join([line.rstrip() for line in f.readlines()]) + f.close() + + # makes sure the manifest was properly cleaned + self.assertEquals(content, _CLEANED_MANIFEST) + + def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) -- cgit v1.2.1 From 9c871d1c7a99fb62f5682ec265fa354f3ff9af10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 21 Dec 2009 23:31:55 +0000 Subject: backported r76993 and r76994 so the trunk behaves the same way with MSVC Manifest files editing --- msvc9compiler.py | 50 ++++++++++++++------------ tests/test_msvc9compiler.py | 87 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 108 insertions(+), 29 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 2309d894..41d67faf 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -646,28 +646,7 @@ class MSVCCompiler(CCompiler) : mfid = 1 else: mfid = 2 - try: - # Remove references to the Visual C runtime, so they will - # fall through to the Visual C dependency of Python.exe. - # This way, when installed for a restricted user (e.g. - # runtimes are not in WinSxS folder, but in Python's own - # folder), the runtimes do not need to be in every folder - # with .pyd's. - manifest_f = open(temp_manifest, "rb") - manifest_buf = manifest_f.read() - manifest_f.close() - pattern = re.compile( - r"""|)""", - re.DOTALL) - manifest_buf = re.sub(pattern, "", manifest_buf) - pattern = "\s*" - manifest_buf = re.sub(pattern, "", manifest_buf) - manifest_f = open(temp_manifest, "wb") - manifest_f.write(manifest_buf) - manifest_f.close() - except IOError: - pass + self._remove_visual_c_ref(temp_manifest) out_arg = '-outputresource:%s;%s' % (output_filename, mfid) try: self.spawn(['mt.exe', '-nologo', '-manifest', @@ -677,6 +656,33 @@ class MSVCCompiler(CCompiler) : else: log.debug("skipping %s (up-to-date)", output_filename) + def _remove_visual_c_ref(self, manifest_file): + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + manifest_f = open(manifest_file) + try: + manifest_buf = manifest_f.read() + finally: + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = "\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + manifest_f = open(manifest_file, 'w') + try: + manifest_f.write(manifest_buf) + finally: + manifest_f.close() + except IOError: + pass # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 1264854d..503a5a80 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -1,18 +1,73 @@ """Tests for distutils.msvc9compiler.""" import sys import unittest +import os from distutils.errors import DistutilsPlatformError +from distutils.tests import support -class msvc9compilerTestCase(unittest.TestCase): +_MANIFEST = """\ + + + + + + + + + + + + + + + + + + + + + + +""" + +_CLEANED_MANIFEST = """\ + + + + + + + + + + + + + + + + + + +""" + +@unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") +class msvc9compilerTestCase(support.TempdirManager, + unittest.TestCase): def test_no_compiler(self): # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler # is not found - if sys.platform != 'win32': - # this test is only for win32 - return from distutils.msvccompiler import get_build_version if get_build_version() < 8.0: # this test is only for MSVC8.0 or above @@ -31,9 +86,6 @@ class msvc9compilerTestCase(unittest.TestCase): msvc9compiler.find_vcvarsall = old_find_vcvarsall def test_reg_class(self): - if sys.platform != 'win32': - # this test is only for win32 - return from distutils.msvccompiler import get_build_version if get_build_version() < 8.0: # this test is only for MSVC8.0 or above @@ -56,6 +108,27 @@ class msvc9compilerTestCase(unittest.TestCase): keys = Reg.read_keys(HKCU, r'Control Panel') self.assertTrue('Desktop' in keys) + def test_remove_visual_c_ref(self): + from distutils.msvc9compiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + f.write(_MANIFEST) + f.close() + + compiler = MSVCCompiler() + compiler._remove_visual_c_ref(manifest) + + # see what we got + f = open(manifest) + # removing trailing spaces + content = '\n'.join([line.rstrip() for line in f.readlines()]) + f.close() + + # makes sure the manifest was properly cleaned + self.assertEquals(content, _CLEANED_MANIFEST) + + def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) -- cgit v1.2.1 From 518952766c40646198629634cbf5fc5df9ba7ae6 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 24 Dec 2009 13:06:39 +0000 Subject: On OSX the output of "uname -m" always reflects the 32-bit architecture for the machine ("i386" or "ppc"), even if the executable is 64-bit. This patchs ensures that the distutils platform architecture represents the architecture for the executable when running a 64-bit only executable on OSX. --- util.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/util.py b/util.py index b8e4952f..f4bb0633 100644 --- a/util.py +++ b/util.py @@ -165,11 +165,21 @@ def get_platform(): raise ValueError( "Don't know machine value for archs=%r"%(archs,)) + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxint >= 2**32: + machine = 'x86_64' elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. machine = 'ppc' + # See 'i386' case + if sys.maxint >= 2**32: + machine = 'ppc64' + return "%s-%s-%s" % (osname, release, machine) -- cgit v1.2.1 From 40baf32676d4421839d651e3a3e561e4c045c639 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 24 Dec 2009 13:07:53 +0000 Subject: Merged revisions 77026 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77026 | ronald.oussoren | 2009-12-24 14:06:39 +0100 (Thu, 24 Dec 2009) | 8 lines On OSX the output of "uname -m" always reflects the 32-bit architecture for the machine ("i386" or "ppc"), even if the executable is 64-bit. This patchs ensures that the distutils platform architecture represents the architecture for the executable when running a 64-bit only executable on OSX. ........ --- util.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/util.py b/util.py index 7bc52f19..d314961b 100644 --- a/util.py +++ b/util.py @@ -162,11 +162,21 @@ def get_platform (): raise ValueError( "Don't know machine value for archs=%r"%(archs,)) + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxint >= 2**32: + machine = 'x86_64' elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. machine = 'ppc' + # See 'i386' case + if sys.maxint >= 2**32: + machine = 'ppc64' + return "%s-%s-%s" % (osname, release, machine) # get_platform () -- cgit v1.2.1 From 576fd8c16b2727a04bf59e08db0a6a166d5dd66e Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 24 Dec 2009 13:14:21 +0000 Subject: Merged revisions 77026 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77026 | ronald.oussoren | 2009-12-24 14:06:39 +0100 (Thu, 24 Dec 2009) | 8 lines On OSX the output of "uname -m" always reflects the 32-bit architecture for the machine ("i386" or "ppc"), even if the executable is 64-bit. This patchs ensures that the distutils platform architecture represents the architecture for the executable when running a 64-bit only executable on OSX. ........ --- util.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/util.py b/util.py index cca7b49f..8adf6e0d 100644 --- a/util.py +++ b/util.py @@ -165,11 +165,21 @@ def get_platform(): raise ValueError( "Don't know machine value for archs=%r"%(archs,)) + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxsize >= 2**32: + machine = 'x86_64' elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. machine = 'ppc' + # See 'i386' case + if sys.maxsize >= 2**32: + machine = 'ppc64' + return "%s-%s-%s" % (osname, release, machine) -- cgit v1.2.1 From 152d8eb8e580c3e346e8a30688a55cd28efd6a06 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 24 Dec 2009 13:16:53 +0000 Subject: Merged revisions 77028 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r77028 | ronald.oussoren | 2009-12-24 14:14:21 +0100 (Thu, 24 Dec 2009) | 15 lines Merged revisions 77026 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77026 | ronald.oussoren | 2009-12-24 14:06:39 +0100 (Thu, 24 Dec 2009) | 8 lines On OSX the output of "uname -m" always reflects the 32-bit architecture for the machine ("i386" or "ppc"), even if the executable is 64-bit. This patchs ensures that the distutils platform architecture represents the architecture for the executable when running a 64-bit only executable on OSX. ........ ................ --- util.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/util.py b/util.py index 50ad8fef..9a77561f 100644 --- a/util.py +++ b/util.py @@ -162,11 +162,21 @@ def get_platform (): raise ValueError( "Don't know machine value for archs=%r"%(archs,)) + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxsize >= 2**32: + machine = 'x86_64' elif machine in ('PowerPC', 'Power_Macintosh'): # Pick a sane name for the PPC architecture. machine = 'ppc' + # See 'i386' case + if sys.maxsize >= 2**32: + machine = 'ppc64' + return "%s-%s-%s" % (osname, release, machine) # get_platform () -- cgit v1.2.1 From 1be9d3f60881534ffd6251d3f76b098ef7a63f2f Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 24 Dec 2009 14:50:35 +0000 Subject: Unittests and news items for the patch in r77026. --- tests/test_util.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index 6722997f..80c58009 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -119,6 +119,26 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') sys.platform = 'darwin' + + self._set_uname(('Darwin', 'macziade', '8.11.1', + ('Darwin Kernel Version 8.11.1: ' + 'Wed Oct 10 18:23:28 PDT 2007; ' + 'root:xnu-792.25.20~1/RELEASE_I386'), 'PowerPC')) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + + get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' + '-fwrapv -O3 -Wall -Wstrict-prototypes') + + maxint = sys.maxint + try: + sys.maxint = 2147483647 + self.assertEquals(get_platform(), 'macosx-10.3-ppc') + sys.maxint = 9223372036854775807 + self.assertEquals(get_platform(), 'macosx-10.3-ppc64') + finally: + sys.maxint = maxint + + self._set_uname(('Darwin', 'macziade', '8.11.1', ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' @@ -128,7 +148,15 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') - self.assertEquals(get_platform(), 'macosx-10.3-i386') + maxint = sys.maxint + try: + sys.maxint = 2147483647 + self.assertEquals(get_platform(), 'macosx-10.3-i386') + sys.maxint = 9223372036854775807 + self.assertEquals(get_platform(), 'macosx-10.3-x86_64') + finally: + sys.maxint = maxint + # macbook with fat binaries (fat, universal or fat64) os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' @@ -173,6 +201,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) + # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' -- cgit v1.2.1 From 864559503a880c593e0333777b34f5e694d65fb5 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sat, 26 Dec 2009 13:16:15 +0000 Subject: Fix merge issue where I forgot to replace sys.maxint by sys.maxsize. --- tests/test_util.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index 41724726..8f8d4a10 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -119,6 +119,26 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') sys.platform = 'darwin' + + self._set_uname(('Darwin', 'macziade', '8.11.1', + ('Darwin Kernel Version 8.11.1: ' + 'Wed Oct 10 18:23:28 PDT 2007; ' + 'root:xnu-792.25.20~1/RELEASE_I386'), 'PowerPC')) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + + get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' + '-fwrapv -O3 -Wall -Wstrict-prototypes') + + maxsize = sys.maxsize + try: + sys.maxsize = 2147483647 + self.assertEquals(get_platform(), 'macosx-10.3-ppc') + sys.maxsize = 9223372036854775807 + self.assertEquals(get_platform(), 'macosx-10.3-ppc64') + finally: + sys.maxsize = maxsize + + self._set_uname(('Darwin', 'macziade', '8.11.1', ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' @@ -128,7 +148,15 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') - self.assertEquals(get_platform(), 'macosx-10.3-i386') + maxsize = sys.maxsize + try: + sys.maxsize = 2147483647 + self.assertEquals(get_platform(), 'macosx-10.3-i386') + sys.maxsize = 9223372036854775807 + self.assertEquals(get_platform(), 'macosx-10.3-x86_64') + finally: + sys.maxsize = maxsize + # macbook with fat binaries (fat, universal or fat64) os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' @@ -173,6 +201,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) + # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' -- cgit v1.2.1 From bbe3a4b6dfc6833ea2e3520691d0415c28f83979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 8 Jan 2010 23:27:23 +0000 Subject: Merged revisions 75669-75671 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r75669 | tarek.ziade | 2009-10-24 17:10:37 +0200 (Sat, 24 Oct 2009) | 1 line Issue #7071: byte-compilation in Distutils now looks at sys.dont_write_bytecode ........ r75670 | tarek.ziade | 2009-10-24 17:19:03 +0200 (Sat, 24 Oct 2009) | 1 line fixed finally state in distutils.test_util ........ r75671 | tarek.ziade | 2009-10-24 17:51:30 +0200 (Sat, 24 Oct 2009) | 1 line fixed warning and error message ........ --- command/build_py.py | 5 +++++ command/install_lib.py | 6 ++++++ errors.py | 2 ++ tests/support.py | 31 +++++++++++++++++++++++++++++++ tests/test_build_py.py | 16 ++++++++++++++++ util.py | 4 ++++ 6 files changed, 64 insertions(+) diff --git a/command/build_py.py b/command/build_py.py index 3bf12673..708ef0f3 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -8,6 +8,7 @@ __revision__ = "$Id$" import string, os from types import * +import sys from glob import glob from distutils.core import Command @@ -418,6 +419,10 @@ class build_py (Command): def byte_compile (self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + from distutils.util import byte_compile prefix = self.build_lib if prefix[-1] != os.sep: diff --git a/command/install_lib.py b/command/install_lib.py index 4ea61d78..7e0c7088 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -4,6 +4,8 @@ __revision__ = "$Id$" import os from types import IntType +import sys + from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -122,6 +124,10 @@ class install_lib (Command): return outfiles def byte_compile (self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + from distutils.util import byte_compile # Get the "--root" directory supplied to the "install" command, diff --git a/errors.py b/errors.py index e72221bd..9d1756b2 100644 --- a/errors.py +++ b/errors.py @@ -76,6 +76,8 @@ class DistutilsInternalError (DistutilsError): class DistutilsTemplateError (DistutilsError): """Syntax error in a file list template.""" +class DistutilsByteCompileError(DistutilsError): + """Byte compile error.""" # Exception classes used by the CCompiler implementation classes class CCompilerError (Exception): diff --git a/tests/support.py b/tests/support.py index 9d373e94..2a037652 100644 --- a/tests/support.py +++ b/tests/support.py @@ -3,19 +3,50 @@ import os import shutil import tempfile +from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils import log from distutils.dist import Distribution +from distutils.cmd import Command class LoggingSilencer(object): def setUp(self): super(LoggingSilencer, self).setUp() self.threshold = log.set_threshold(log.FATAL) + # catching warnings + # when log will be replaced by logging + # we won't need such monkey-patch anymore + self._old_log = log.Log._log + log.Log._log = self._log + self.logs = [] + self._old_warn = Command.warn + Command.warn = self._warn def tearDown(self): log.set_threshold(self.threshold) + log.Log._log = self._old_log + Command.warn = self._old_warn super(LoggingSilencer, self).tearDown() + def _warn(self, msg): + self.logs.append(('', msg, '')) + + def _log(self, level, msg, args): + if level not in (DEBUG, INFO, WARN, ERROR, FATAL): + raise ValueError('%s wrong log level' % str(level)) + self.logs.append((level, msg, args)) + + def get_logs(self, *levels): + def _format(msg, args): + if len(args) == 0: + return msg + return msg % args + return [_format(msg, args) for level, msg, args + in self.logs if level in levels] + + def clear_logs(self): + self.logs = [] + class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 4b045474..4a054f2a 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -90,6 +90,22 @@ class BuildPyTestCase(support.TempdirManager, os.chdir(cwd) sys.stdout = old_stdout + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + pkg_dir, dist = self.create_dist() + cmd = build_py(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) + def test_suite(): return unittest.makeSuite(BuildPyTestCase) diff --git a/util.py b/util.py index d314961b..36ac7213 100644 --- a/util.py +++ b/util.py @@ -11,6 +11,7 @@ from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer from distutils.spawn import spawn from distutils import log +from distutils.errors import DistutilsByteCompileError def get_platform (): """Return a string that identifies the current platform. This is used @@ -457,6 +458,9 @@ def byte_compile (py_files, generated in indirect mode; unless you know what you're doing, leave it set to None. """ + # nothing is done if sys.dont_write_bytecode is True + if sys.dont_write_bytecode: + raise DistutilsByteCompileError('byte-compiling is disabled.') # First, if the caller didn't force us into direct or indirect mode, # figure out which mode we should be in. We take a conservative -- cgit v1.2.1 From eb7e9954b37373447c9c923374af56c8a8ac81e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 8 Jan 2010 23:42:23 +0000 Subject: Fixed #7617: all flavors of gcc should be recognized now --- tests/test_unixccompiler.py | 12 ++++++++++++ unixccompiler.py | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 1b7dd4cd..008ae5d0 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -82,6 +82,18 @@ class UnixCCompilerTestCase(unittest.TestCase): sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') + # GCC GNULD with fully qualified configuration prefix + # see #7617 + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'x86_64-pc-linux-gnu-gcc-4.4.2' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + + # non-GCC GNULD sys.platform = 'bar' def gcv(v): diff --git a/unixccompiler.py b/unixccompiler.py index 2083f829..67adcfcf 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -266,6 +266,9 @@ class UnixCCompiler(CCompiler): def library_dir_option(self, dir): return "-L" + dir + def _is_gcc(self, compiler_name): + return "gcc" in compiler_name or "g++" in compiler_name + def runtime_library_dir_option(self, dir): # XXX Hackish, at the very least. See Python bug #445902: # http://sourceforge.net/tracker/index.php @@ -285,12 +288,12 @@ class UnixCCompiler(CCompiler): # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir elif sys.platform[:5] == "hp-ux": - if "gcc" in compiler or "g++" in compiler: + if self._is_gcc(compiler): return ["-Wl,+s", "-L" + dir] return ["+s", "-L" + dir] elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] - elif compiler[:3] == "gcc" or compiler[:3] == "g++": + elif self._is_gcc(compiler): # gcc on non-GNU systems does not need -Wl, but can # use it anyway. Since distutils has always passed in # -Wl whenever gcc was used in the past it is probably -- cgit v1.2.1 From 3028d15921718f24deeedb8a57960e6885e279b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 8 Jan 2010 23:48:37 +0000 Subject: Merged revisions 77377 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77377 | tarek.ziade | 2010-01-09 00:42:23 +0100 (Sat, 09 Jan 2010) | 1 line Fixed #7617: all flavors of gcc should be recognized now ........ --- tests/test_unixccompiler.py | 129 ++++++++++++++++++++++++++++++++++++++++++++ unixccompiler.py | 7 ++- 2 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 tests/test_unixccompiler.py diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py new file mode 100644 index 00000000..3f233e28 --- /dev/null +++ b/tests/test_unixccompiler.py @@ -0,0 +1,129 @@ +"""Tests for distutils.unixccompiler.""" +import sys +import unittest + +from distutils import sysconfig +from distutils.unixccompiler import UnixCCompiler + +class UnixCCompilerTestCase(unittest.TestCase): + + def setUp(self): + self._backup_platform = sys.platform + self._backup_get_config_var = sysconfig.get_config_var + class CompilerWrapper(UnixCCompiler): + def rpath_foo(self): + return self.runtime_library_dir_option('/foo') + self.cc = CompilerWrapper() + + def tearDown(self): + sys.platform = self._backup_platform + sysconfig.get_config_var = self._backup_get_config_var + + def test_runtime_libdir_option(self): + + # not tested under windows + if sys.platform == 'win32': + return + + # Issue#5900 + # + # Ensure RUNPATH is added to extension modules with RPATH if + # GNU ld is used + + # darwin + sys.platform = 'darwin' + self.assertEqual(self.cc.rpath_foo(), '-L/foo') + + # hp-ux + sys.platform = 'hp-ux' + old_gcv = sysconfig.get_config_var + def gcv(v): + return 'xxx' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo']) + + def gcv(v): + return 'gcc' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + def gcv(v): + return 'g++' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + sysconfig.get_config_var = old_gcv + + # irix646 + sys.platform = 'irix646' + self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo']) + + # osf1V5 + sys.platform = 'osf1V5' + self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo']) + + # GCC GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'gcc' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') + + # GCC non-GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'gcc' + elif v == 'GNULD': + return 'no' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') + + # GCC GNULD with fully qualified configuration prefix + # see #7617 + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'x86_64-pc-linux-gnu-gcc-4.4.2' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') + + + # non-GCC GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'cc' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + + # non-GCC non-GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'cc' + elif v == 'GNULD': + return 'no' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + + # AIX C/C++ linker + sys.platform = 'aix' + def gcv(v): + return 'xxx' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + + +def test_suite(): + return unittest.makeSuite(UnixCCompilerTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/unixccompiler.py b/unixccompiler.py index 7556cbdb..783d4dca 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -266,6 +266,9 @@ class UnixCCompiler(CCompiler): def library_dir_option(self, dir): return "-L" + dir + def _is_gcc(self, compiler_name): + return "gcc" in compiler_name or "g++" in compiler_name + def runtime_library_dir_option(self, dir): # XXX Hackish, at the very least. See Python bug #445902: # http://sourceforge.net/tracker/index.php @@ -284,12 +287,12 @@ class UnixCCompiler(CCompiler): # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir elif sys.platform[:5] == "hp-ux": - if "gcc" in compiler or "g++" in compiler: + if self._is_gcc(compiler): return ["-Wl,+s", "-L" + dir] return ["+s", "-L" + dir] elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] - elif compiler[:3] == "gcc" or compiler[:3] == "g++": + elif self._is_gcc(compiler): return "-Wl,-R" + dir else: return "-R" + dir -- cgit v1.2.1 From 3cdc8c99c134ff274895533fc30536215fd3dca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 8 Jan 2010 23:54:15 +0000 Subject: added more test coverage from trunk for #7617 --- tests/test_install_lib.py | 35 +++++++++++++++++++++++++++++++++++ tests/test_util.py | 24 ++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 tests/test_install_lib.py create mode 100644 tests/test_util.py diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py new file mode 100644 index 00000000..78922f47 --- /dev/null +++ b/tests/test_install_lib.py @@ -0,0 +1,35 @@ +"""Tests for distutils.command.install_data.""" +import sys +import os +import unittest + +from distutils.command.install_lib import install_lib +from distutils.extension import Extension +from distutils.tests import support +from distutils.errors import DistutilsOptionError + +class InstallLibTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + pkg_dir, dist = self.create_dist() + cmd = install_lib(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) + +def test_suite(): + return unittest.makeSuite(InstallLibTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/tests/test_util.py b/tests/test_util.py new file mode 100644 index 00000000..981ad000 --- /dev/null +++ b/tests/test_util.py @@ -0,0 +1,24 @@ +"""Tests for distutils.util.""" +import sys +import unittest + +from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError +from distutils.util import byte_compile + +class UtilTestCase(unittest.TestCase): + + def test_dont_write_bytecode(self): + # makes sure byte_compile raise a DistutilsError + # if sys.dont_write_bytecode is True + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + self.assertRaises(DistutilsByteCompileError, byte_compile, []) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + +def test_suite(): + return unittest.makeSuite(UtilTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 2c8ac6208548ee8f8306964077359e0057e16d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 8 Jan 2010 23:57:53 +0000 Subject: Merged revisions 77377 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77377 | tarek.ziade | 2010-01-09 00:42:23 +0100 (Sat, 09 Jan 2010) | 1 line Fixed #7617: all flavors of gcc should be recognized now ........ --- tests/test_unixccompiler.py | 12 ++++++++++++ unixccompiler.py | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 1b7dd4cd..008ae5d0 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -82,6 +82,18 @@ class UnixCCompilerTestCase(unittest.TestCase): sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') + # GCC GNULD with fully qualified configuration prefix + # see #7617 + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'x86_64-pc-linux-gnu-gcc-4.4.2' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + + # non-GCC GNULD sys.platform = 'bar' def gcv(v): diff --git a/unixccompiler.py b/unixccompiler.py index da85c896..51f6349a 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -264,6 +264,9 @@ class UnixCCompiler(CCompiler): def library_dir_option(self, dir): return "-L" + dir + def _is_gcc(self, compiler_name): + return "gcc" in compiler_name or "g++" in compiler_name + def runtime_library_dir_option(self, dir): # XXX Hackish, at the very least. See Python bug #445902: # http://sourceforge.net/tracker/index.php @@ -283,12 +286,12 @@ class UnixCCompiler(CCompiler): # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir elif sys.platform[:5] == "hp-ux": - if "gcc" in compiler or "g++" in compiler: + if self._is_gcc(compiler): return ["-Wl,+s", "-L" + dir] return ["+s", "-L" + dir] elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] - elif compiler[:3] == "gcc" or compiler[:3] == "g++": + elif self._is_gcc(compiler): # gcc on non-GNU systems does not need -Wl, but can # use it anyway. Since distutils has always passed in # -Wl whenever gcc was used in the past it is probably -- cgit v1.2.1 From ddece8fad41398b0ae313a9a554eaecb219ec36d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 9 Jan 2010 00:03:39 +0000 Subject: Merged revisions 77380 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r77380 | tarek.ziade | 2010-01-09 00:57:53 +0100 (Sat, 09 Jan 2010) | 9 lines Merged revisions 77377 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77377 | tarek.ziade | 2010-01-09 00:42:23 +0100 (Sat, 09 Jan 2010) | 1 line Fixed #7617: all flavors of gcc should be recognized now ........ ................ --- tests/test_unixccompiler.py | 12 ++++++++++++ unixccompiler.py | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index be2df5c6..3a41e6fc 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -82,6 +82,18 @@ class UnixCCompilerTestCase(unittest.TestCase): sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') + # GCC GNULD with fully qualified configuration prefix + # see #7617 + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'x86_64-pc-linux-gnu-gcc-4.4.2' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + + # non-GCC GNULD sys.platform = 'bar' def gcv(v): diff --git a/unixccompiler.py b/unixccompiler.py index 8bbdb4b3..a33fdf0f 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -264,6 +264,9 @@ class UnixCCompiler(CCompiler): def library_dir_option(self, dir): return "-L" + dir + def _is_gcc(self, compiler_name): + return "gcc" in compiler_name or "g++" in compiler_name + def runtime_library_dir_option(self, dir): # XXX Hackish, at the very least. See Python bug #445902: # http://sourceforge.net/tracker/index.php @@ -283,13 +286,13 @@ class UnixCCompiler(CCompiler): # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir elif sys.platform[:5] == "hp-ux": - if "gcc" in compiler or "g++" in compiler: + if self._is_gcc(compiler): return ["-Wl,+s", "-L" + dir] return ["+s", "-L" + dir] elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] else: - if compiler[:3] == "gcc" or compiler[:3] == "g++": + if self._is_gcc(compiler): # gcc on non-GNU systems does not need -Wl, but can # use it anyway. Since distutils has always passed in # -Wl whenever gcc was used in the past it is probably -- cgit v1.2.1 From 9692427a1f35e2911ad27ec342f939655d5fc337 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 9 Jan 2010 16:34:06 +0000 Subject: bump version to 2.7a2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e8c213e4..55aaf81a 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7a1" +__version__ = "2.7a2" #--end constants-- -- cgit v1.2.1 From 51c0c7aa736d410dae0179ad4f7304359240c397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 11 Jan 2010 22:50:29 +0000 Subject: Fixed #5372: .o files are now always rebuilt because file age test don't work in some case --- ccompiler.py | 96 ++---------------------------------------------------------- 1 file changed, 2 insertions(+), 94 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index c0469151..83ba83a0 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -337,10 +337,7 @@ class CCompiler: def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra): - """Process arguments and decide which source files to compile. - - Merges _fix_compile_args() and _prep_compile(). - """ + """Process arguments and decide which source files to compile.""" if outdir is None: outdir = self.output_dir elif not isinstance(outdir, str): @@ -370,41 +367,6 @@ class CCompiler: output_dir=outdir) assert len(objects) == len(sources) - # XXX should redo this code to eliminate skip_source entirely. - # XXX instead create build and issue skip messages inline - - if self.force: - skip_source = {} # rebuild everything - for source in sources: - skip_source[source] = 0 - elif depends is None: - # If depends is None, figure out which source files we - # have to recompile according to a simplistic check. We - # just compare the source and object file, no deep - # dependency checking involving header files. - skip_source = {} # rebuild everything - for source in sources: # no wait, rebuild nothing - skip_source[source] = 1 - - n_sources, n_objects = newer_pairwise(sources, objects) - for source in n_sources: # no really, only rebuild what's - skip_source[source] = 0 # out-of-date - else: - # If depends is a list of files, then do a different - # simplistic check. Assume that each object depends on - # its source and all files in the depends list. - skip_source = {} - # L contains all the depends plus a spot at the end for a - # particular source file - L = depends[:] + [None] - for i in range(len(objects)): - source = sources[i] - L[-1] = source - if newer_group(L, objects[i]): - skip_source[source] = 0 - else: - skip_source[source] = 1 - pp_opts = gen_preprocess_options(macros, incdirs) build = {} @@ -413,10 +375,7 @@ class CCompiler: obj = objects[i] ext = os.path.splitext(src)[1] self.mkpath(os.path.dirname(obj)) - if skip_source[src]: - log.debug("skipping %s (%s up-to-date)", src, obj) - else: - build[obj] = src, ext + build[obj] = (src, ext) return macros, objects, extra, pp_opts, build @@ -463,56 +422,6 @@ class CCompiler: # _fix_compile_args () - - def _prep_compile(self, sources, output_dir, depends=None): - """Decide which souce files must be recompiled. - - Determine the list of object files corresponding to 'sources', - and figure out which ones really need to be recompiled. - Return a list of all object files and a dictionary telling - which source files can be skipped. - """ - # Get the list of expected output (object) files - objects = self.object_filenames(sources, output_dir=output_dir) - assert len(objects) == len(sources) - - if self.force: - skip_source = {} # rebuild everything - for source in sources: - skip_source[source] = 0 - elif depends is None: - # If depends is None, figure out which source files we - # have to recompile according to a simplistic check. We - # just compare the source and object file, no deep - # dependency checking involving header files. - skip_source = {} # rebuild everything - for source in sources: # no wait, rebuild nothing - skip_source[source] = 1 - - n_sources, n_objects = newer_pairwise(sources, objects) - for source in n_sources: # no really, only rebuild what's - skip_source[source] = 0 # out-of-date - else: - # If depends is a list of files, then do a different - # simplistic check. Assume that each object depends on - # its source and all files in the depends list. - skip_source = {} - # L contains all the depends plus a spot at the end for a - # particular source file - L = depends[:] + [None] - for i in range(len(objects)): - source = sources[i] - L[-1] = source - if newer_group(L, objects[i]): - skip_source[source] = 0 - else: - skip_source[source] = 1 - - return objects, skip_source - - # _prep_compile () - - def _fix_object_args (self, objects, output_dir): """Typecheck and fix up some arguments supplied to various methods. Specifically: ensure that 'objects' is a list; if output_dir is @@ -679,7 +588,6 @@ class CCompiler: Raises CompileError on failure. """ - # A concrete compiler class can either override this method # entirely or implement _compile(). -- cgit v1.2.1 From 06cbacd4c16b16c8bc604418be4a796f9277d271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 11 Jan 2010 22:54:57 +0000 Subject: Merged revisions 77424 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77424 | tarek.ziade | 2010-01-11 23:50:29 +0100 (Mon, 11 Jan 2010) | 1 line Fixed #5372: .o files are now always rebuilt because file age test don't work in some case ........ --- ccompiler.py | 86 ++++-------------------------------------------------------- 1 file changed, 5 insertions(+), 81 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 87d6e273..bf92d780 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -338,10 +338,7 @@ class CCompiler: def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra): - """Process arguments and decide which source files to compile. - - Merges _fix_compile_args() and _prep_compile(). - """ + """Process arguments and decide which source files to compile.""" if outdir is None: outdir = self.output_dir elif type(outdir) is not StringType: @@ -371,41 +368,6 @@ class CCompiler: output_dir=outdir) assert len(objects) == len(sources) - # XXX should redo this code to eliminate skip_source entirely. - # XXX instead create build and issue skip messages inline - - if self.force: - skip_source = {} # rebuild everything - for source in sources: - skip_source[source] = 0 - elif depends is None: - # If depends is None, figure out which source files we - # have to recompile according to a simplistic check. We - # just compare the source and object file, no deep - # dependency checking involving header files. - skip_source = {} # rebuild everything - for source in sources: # no wait, rebuild nothing - skip_source[source] = 1 - - n_sources, n_objects = newer_pairwise(sources, objects) - for source in n_sources: # no really, only rebuild what's - skip_source[source] = 0 # out-of-date - else: - # If depends is a list of files, then do a different - # simplistic check. Assume that each object depends on - # its source and all files in the depends list. - skip_source = {} - # L contains all the depends plus a spot at the end for a - # particular source file - L = depends[:] + [None] - for i in range(len(objects)): - source = sources[i] - L[-1] = source - if newer_group(L, objects[i]): - skip_source[source] = 0 - else: - skip_source[source] = 1 - pp_opts = gen_preprocess_options(macros, incdirs) build = {} @@ -414,10 +376,7 @@ class CCompiler: obj = objects[i] ext = os.path.splitext(src)[1] self.mkpath(os.path.dirname(obj)) - if skip_source[src]: - log.debug("skipping %s (%s up-to-date)", src, obj) - else: - build[obj] = src, ext + build[obj] = (src, ext) return macros, objects, extra, pp_opts, build @@ -464,7 +423,6 @@ class CCompiler: # _fix_compile_args () - def _prep_compile(self, sources, output_dir, depends=None): """Decide which souce files must be recompiled. @@ -477,42 +435,9 @@ class CCompiler: objects = self.object_filenames(sources, output_dir=output_dir) assert len(objects) == len(sources) - if self.force: - skip_source = {} # rebuild everything - for source in sources: - skip_source[source] = 0 - elif depends is None: - # If depends is None, figure out which source files we - # have to recompile according to a simplistic check. We - # just compare the source and object file, no deep - # dependency checking involving header files. - skip_source = {} # rebuild everything - for source in sources: # no wait, rebuild nothing - skip_source[source] = 1 - - n_sources, n_objects = newer_pairwise(sources, objects) - for source in n_sources: # no really, only rebuild what's - skip_source[source] = 0 # out-of-date - else: - # If depends is a list of files, then do a different - # simplistic check. Assume that each object depends on - # its source and all files in the depends list. - skip_source = {} - # L contains all the depends plus a spot at the end for a - # particular source file - L = depends[:] + [None] - for i in range(len(objects)): - source = sources[i] - L[-1] = source - if newer_group(L, objects[i]): - skip_source[source] = 0 - else: - skip_source[source] = 1 - - return objects, skip_source - - # _prep_compile () - + # Return an empty dict for the "which source files can be skipped" + # return value to preserve API compatibility. + return objects, {} def _fix_object_args (self, objects, output_dir): """Typecheck and fix up some arguments supplied to various methods. @@ -680,7 +605,6 @@ class CCompiler: Raises CompileError on failure. """ - # A concrete compiler class can either override this method # entirely or implement _compile(). -- cgit v1.2.1 From bab7bc91d6a99514935bcc12923920ede33f01c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 11 Jan 2010 23:15:52 +0000 Subject: Merged revisions 77424 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77424 | tarek.ziade | 2010-01-11 23:50:29 +0100 (Mon, 11 Jan 2010) | 1 line Fixed #5372: .o files are now always rebuilt because file age test don't work in some case ........ --- ccompiler.py | 92 ++---------------------------------------------------------- 1 file changed, 2 insertions(+), 90 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 407cabda..38c4ae8e 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -314,10 +314,7 @@ class CCompiler: def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra): - """Process arguments and decide which source files to compile. - - Merges _fix_compile_args() and _prep_compile(). - """ + """Process arguments and decide which source files to compile.""" if outdir is None: outdir = self.output_dir elif not isinstance(outdir, str): @@ -346,41 +343,6 @@ class CCompiler: output_dir=outdir) assert len(objects) == len(sources) - # XXX should redo this code to eliminate skip_source entirely. - # XXX instead create build and issue skip messages inline - - if self.force: - skip_source = {} # rebuild everything - for source in sources: - skip_source[source] = 0 - elif depends is None: - # If depends is None, figure out which source files we - # have to recompile according to a simplistic check. We - # just compare the source and object file, no deep - # dependency checking involving header files. - skip_source = {} # rebuild everything - for source in sources: # no wait, rebuild nothing - skip_source[source] = 1 - - n_sources, n_objects = newer_pairwise(sources, objects) - for source in n_sources: # no really, only rebuild what's - skip_source[source] = 0 # out-of-date - else: - # If depends is a list of files, then do a different - # simplistic check. Assume that each object depends on - # its source and all files in the depends list. - skip_source = {} - # L contains all the depends plus a spot at the end for a - # particular source file - L = depends[:] + [None] - for i in range(len(objects)): - source = sources[i] - L[-1] = source - if newer_group(L, objects[i]): - skip_source[source] = 0 - else: - skip_source[source] = 1 - pp_opts = gen_preprocess_options(macros, incdirs) build = {} @@ -389,10 +351,7 @@ class CCompiler: obj = objects[i] ext = os.path.splitext(src)[1] self.mkpath(os.path.dirname(obj)) - if skip_source[src]: - log.debug("skipping %s (%s up-to-date)", src, obj) - else: - build[obj] = src, ext + build[obj] = (src, ext) return macros, objects, extra, pp_opts, build @@ -437,53 +396,6 @@ class CCompiler: return output_dir, macros, include_dirs - def _prep_compile(self, sources, output_dir, depends=None): - """Decide which souce files must be recompiled. - - Determine the list of object files corresponding to 'sources', - and figure out which ones really need to be recompiled. - Return a list of all object files and a dictionary telling - which source files can be skipped. - """ - # Get the list of expected output (object) files - objects = self.object_filenames(sources, output_dir=output_dir) - assert len(objects) == len(sources) - - if self.force: - skip_source = {} # rebuild everything - for source in sources: - skip_source[source] = 0 - elif depends is None: - # If depends is None, figure out which source files we - # have to recompile according to a simplistic check. We - # just compare the source and object file, no deep - # dependency checking involving header files. - skip_source = {} # rebuild everything - for source in sources: # no wait, rebuild nothing - skip_source[source] = 1 - - n_sources, n_objects = newer_pairwise(sources, objects) - for source in n_sources: # no really, only rebuild what's - skip_source[source] = 0 # out-of-date - else: - # If depends is a list of files, then do a different - # simplistic check. Assume that each object depends on - # its source and all files in the depends list. - skip_source = {} - # L contains all the depends plus a spot at the end for a - # particular source file - L = depends[:] + [None] - for i in range(len(objects)): - source = sources[i] - L[-1] = source - if newer_group(L, objects[i]): - skip_source[source] = 0 - else: - skip_source[source] = 1 - - return objects, skip_source - - def _fix_object_args(self, objects, output_dir): """Typecheck and fix up some arguments supplied to various methods. Specifically: ensure that 'objects' is a list; if output_dir is -- cgit v1.2.1 From 3b516635a07e4b381d313a4bf912c295849127b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 11 Jan 2010 23:23:44 +0000 Subject: Merged revisions 77427 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r77427 | tarek.ziade | 2010-01-12 00:15:52 +0100 (Tue, 12 Jan 2010) | 9 lines Merged revisions 77424 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77424 | tarek.ziade | 2010-01-11 23:50:29 +0100 (Mon, 11 Jan 2010) | 1 line Fixed #5372: .o files are now always rebuilt because file age test don't work in some case ........ ................ --- ccompiler.py | 82 ++++-------------------------------------------------------- 1 file changed, 5 insertions(+), 77 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 875f96fa..34c77a37 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -311,10 +311,7 @@ class CCompiler: def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra): - """Process arguments and decide which source files to compile. - - Merges _fix_compile_args() and _prep_compile(). - """ + """Process arguments and decide which source files to compile.""" if outdir is None: outdir = self.output_dir elif not isinstance(outdir, str): @@ -343,41 +340,6 @@ class CCompiler: output_dir=outdir) assert len(objects) == len(sources) - # XXX should redo this code to eliminate skip_source entirely. - # XXX instead create build and issue skip messages inline - - if self.force: - skip_source = {} # rebuild everything - for source in sources: - skip_source[source] = 0 - elif depends is None: - # If depends is None, figure out which source files we - # have to recompile according to a simplistic check. We - # just compare the source and object file, no deep - # dependency checking involving header files. - skip_source = {} # rebuild everything - for source in sources: # no wait, rebuild nothing - skip_source[source] = 1 - - n_sources, n_objects = newer_pairwise(sources, objects) - for source in n_sources: # no really, only rebuild what's - skip_source[source] = 0 # out-of-date - else: - # If depends is a list of files, then do a different - # simplistic check. Assume that each object depends on - # its source and all files in the depends list. - skip_source = {} - # L contains all the depends plus a spot at the end for a - # particular source file - L = depends[:] + [None] - for i in range(len(objects)): - source = sources[i] - L[-1] = source - if newer_group(L, objects[i]): - skip_source[source] = 0 - else: - skip_source[source] = 1 - pp_opts = gen_preprocess_options(macros, incdirs) build = {} @@ -386,10 +348,7 @@ class CCompiler: obj = objects[i] ext = os.path.splitext(src)[1] self.mkpath(os.path.dirname(obj)) - if skip_source[src]: - log.debug("skipping %s (%s up-to-date)", src, obj) - else: - build[obj] = src, ext + build[obj] = (src, ext) return macros, objects, extra, pp_opts, build @@ -446,40 +405,9 @@ class CCompiler: objects = self.object_filenames(sources, output_dir=output_dir) assert len(objects) == len(sources) - if self.force: - skip_source = {} # rebuild everything - for source in sources: - skip_source[source] = 0 - elif depends is None: - # If depends is None, figure out which source files we - # have to recompile according to a simplistic check. We - # just compare the source and object file, no deep - # dependency checking involving header files. - skip_source = {} # rebuild everything - for source in sources: # no wait, rebuild nothing - skip_source[source] = 1 - - n_sources, n_objects = newer_pairwise(sources, objects) - for source in n_sources: # no really, only rebuild what's - skip_source[source] = 0 # out-of-date - else: - # If depends is a list of files, then do a different - # simplistic check. Assume that each object depends on - # its source and all files in the depends list. - skip_source = {} - # L contains all the depends plus a spot at the end for a - # particular source file - L = depends[:] + [None] - for i in range(len(objects)): - source = sources[i] - L[-1] = source - if newer_group(L, objects[i]): - skip_source[source] = 0 - else: - skip_source[source] = 1 - - return objects, skip_source - + # Return an empty dict for the "which source files can be skipped" + # return value to preserve API compatibility. + return objects, {} def _fix_object_args(self, objects, output_dir): """Typecheck and fix up some arguments supplied to various methods. -- cgit v1.2.1 From aa70d631f51b21abea4f917d841e982f866f0f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 11 Jan 2010 23:41:32 +0000 Subject: module cleanup --- ccompiler.py | 217 +++++++++++++++++++---------------------------------------- 1 file changed, 69 insertions(+), 148 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 83ba83a0..97c61dd4 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -9,11 +9,12 @@ import sys import os import re -from distutils.errors import CompileError, LinkError, UnknownFileError +from distutils.errors import (CompileError, LinkError, UnknownFileError, + DistutilsPlatformError, DistutilsModuleError) from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath -from distutils.dep_util import newer_pairwise, newer_group +from distutils.dep_util import newer_group from distutils.util import split_quoted, execute from distutils import log @@ -87,11 +88,7 @@ class CCompiler: } language_order = ["c++", "objc", "c"] - def __init__ (self, - verbose=0, - dry_run=0, - force=0): - + def __init__ (self, verbose=0, dry_run=0, force=0): self.dry_run = dry_run self.force = force self.verbose = verbose @@ -127,11 +124,7 @@ class CCompiler: for key in self.executables.keys(): self.set_executable(key, self.executables[key]) - # __init__ () - - - def set_executables (self, **args): - + def set_executables(self, **args): """Define the executables (and options for them) that will be run to perform the various stages of compilation. The exact set of executables that may be specified here depends on the compiler @@ -164,26 +157,21 @@ class CCompiler: (key, self.__class__.__name__) self.set_executable(key, args[key]) - # set_executables () - def set_executable(self, key, value): if isinstance(value, str): setattr(self, key, split_quoted(value)) else: setattr(self, key, value) - - def _find_macro (self, name): + def _find_macro(self, name): i = 0 for defn in self.macros: if defn[0] == name: return i i = i + 1 - return None - - def _check_macro_definitions (self, definitions): + def _check_macro_definitions(self, definitions): """Ensures that every element of 'definitions' is a valid macro definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do nothing if all definitions are OK, raise TypeError otherwise. @@ -202,7 +190,7 @@ class CCompiler: # -- Bookkeeping methods ------------------------------------------- - def define_macro (self, name, value=None): + def define_macro(self, name, value=None): """Define a preprocessor macro for all compilations driven by this compiler object. The optional parameter 'value' should be a string; if it is not supplied, then the macro will be defined @@ -218,8 +206,7 @@ class CCompiler: defn = (name, value) self.macros.append (defn) - - def undefine_macro (self, name): + def undefine_macro(self, name): """Undefine a preprocessor macro for all compilations driven by this compiler object. If the same macro is defined by 'define_macro()' and undefined by 'undefine_macro()' the last call @@ -237,8 +224,7 @@ class CCompiler: undefn = (name,) self.macros.append (undefn) - - def add_include_dir (self, dir): + def add_include_dir(self, dir): """Add 'dir' to the list of directories that will be searched for header files. The compiler is instructed to search directories in the order in which they are supplied by successive calls to @@ -246,7 +232,7 @@ class CCompiler: """ self.include_dirs.append (dir) - def set_include_dirs (self, dirs): + def set_include_dirs(self, dirs): """Set the list of directories that will be searched to 'dirs' (a list of strings). Overrides any preceding calls to 'add_include_dir()'; subsequence calls to 'add_include_dir()' add @@ -256,8 +242,7 @@ class CCompiler: """ self.include_dirs = dirs[:] - - def add_library (self, libname): + def add_library(self, libname): """Add 'libname' to the list of libraries that will be included in all links driven by this compiler object. Note that 'libname' should *not* be the name of a file containing a library, but the @@ -273,7 +258,7 @@ class CCompiler: """ self.libraries.append (libname) - def set_libraries (self, libnames): + def set_libraries(self, libnames): """Set the list of libraries to be included in all links driven by this compiler object to 'libnames' (a list of strings). This does not affect any standard system libraries that the linker may @@ -282,29 +267,28 @@ class CCompiler: self.libraries = libnames[:] - def add_library_dir (self, dir): + def add_library_dir(self, dir): """Add 'dir' to the list of directories that will be searched for libraries specified to 'add_library()' and 'set_libraries()'. The linker will be instructed to search for libraries in the order they are supplied to 'add_library_dir()' and/or 'set_library_dirs()'. """ - self.library_dirs.append (dir) + self.library_dirs.append(dir) - def set_library_dirs (self, dirs): + def set_library_dirs(self, dirs): """Set the list of library search directories to 'dirs' (a list of strings). This does not affect any standard library search path that the linker may search by default. """ self.library_dirs = dirs[:] - - def add_runtime_library_dir (self, dir): + def add_runtime_library_dir(self, dir): """Add 'dir' to the list of directories that will be searched for shared libraries at runtime. """ - self.runtime_library_dirs.append (dir) + self.runtime_library_dirs.append(dir) - def set_runtime_library_dirs (self, dirs): + def set_runtime_library_dirs(self, dirs): """Set the list of directories to search for shared libraries at runtime to 'dirs' (a list of strings). This does not affect any standard search path that the runtime linker may search by @@ -312,16 +296,15 @@ class CCompiler: """ self.runtime_library_dirs = dirs[:] - - def add_link_object (self, object): + def add_link_object(self, object): """Add 'object' to the list of object files (or analogues, such as explicitly named library files or the output of "resource compilers") to be included in every link driven by this compiler object. """ - self.objects.append (object) + self.objects.append(object) - def set_link_objects (self, objects): + def set_link_objects(self, objects): """Set the list of object files (or analogues) to be included in every link to 'objects'. This does not affect any standard object files that the linker may include by default (such as system @@ -388,7 +371,7 @@ class CCompiler: cc_args[:0] = before return cc_args - def _fix_compile_args (self, output_dir, macros, include_dirs): + def _fix_compile_args(self, output_dir, macros, include_dirs): """Typecheck and fix-up some of the arguments to the 'compile()' method, and return fixed-up values. Specifically: if 'output_dir' is None, replaces it with 'self.output_dir'; ensures that 'macros' @@ -400,7 +383,7 @@ class CCompiler: """ if output_dir is None: output_dir = self.output_dir - elif type (output_dir) is not StringType: + elif not isinstance(output_dir, str): raise TypeError, "'output_dir' must be a string or None" if macros is None: @@ -420,9 +403,7 @@ class CCompiler: return output_dir, macros, include_dirs - # _fix_compile_args () - - def _fix_object_args (self, objects, output_dir): + def _fix_object_args(self, objects, output_dir): """Typecheck and fix up some arguments supplied to various methods. Specifically: ensure that 'objects' is a list; if output_dir is None, replace with self.output_dir. Return fixed versions of @@ -440,8 +421,7 @@ class CCompiler: return (objects, output_dir) - - def _fix_lib_args (self, libraries, library_dirs, runtime_library_dirs): + def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs): """Typecheck and fix up some of the arguments supplied to the 'link_*' methods. Specifically: ensure that all arguments are lists, and augment them with their permanent versions @@ -476,10 +456,7 @@ class CCompiler: return (libraries, library_dirs, runtime_library_dirs) - # _fix_lib_args () - - - def _need_link (self, objects, output_file): + def _need_link(self, objects, output_file): """Return true if we need to relink the files listed in 'objects' to recreate 'output_file'. """ @@ -492,9 +469,7 @@ class CCompiler: newer = newer_group (objects, output_file) return newer - # _need_link () - - def detect_language (self, sources): + def detect_language(self, sources): """Detect the language of a given file, or list of files. Uses language_map, and language_order to do the job. """ @@ -514,18 +489,11 @@ class CCompiler: pass return lang - # detect_language () - # -- Worker methods ------------------------------------------------ # (must be implemented by subclasses) - def preprocess (self, - source, - output_file=None, - macros=None, - include_dirs=None, - extra_preargs=None, - extra_postargs=None): + def preprocess(self, source, output_file=None, macros=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): """Preprocess a single C/C++ source file, named in 'source'. Output will be written to file named 'output_file', or stdout if 'output_file' not supplied. 'macros' is a list of macro @@ -613,12 +581,8 @@ class CCompiler: # should implement _compile(). pass - def create_static_lib (self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): + def create_static_lib(self, objects, output_libname, output_dir=None, + debug=0, target_lang=None): """Link a bunch of stuff together to create a static library file. The "bunch of stuff" consists of the list of object files supplied as 'objects', the extra object files supplied to @@ -643,26 +607,15 @@ class CCompiler: """ pass - # values for target_desc parameter in link() SHARED_OBJECT = "shared_object" SHARED_LIBRARY = "shared_library" EXECUTABLE = "executable" - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): + def link(self, target_desc, objects, output_filename, output_dir=None, + libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None): """Link a bunch of stuff together to create an executable or shared library file. @@ -711,19 +664,11 @@ class CCompiler: # Old 'link_*()' methods, rewritten to use the new 'link()' method. - def link_shared_lib (self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): + def link_shared_lib(self, objects, output_libname, output_dir=None, + libraries=None, library_dirs=None, + runtime_library_dirs=None, export_symbols=None, + debug=0, extra_preargs=None, extra_postargs=None, + build_temp=None, target_lang=None): self.link(CCompiler.SHARED_LIBRARY, objects, self.library_filename(output_libname, lib_type='shared'), output_dir, @@ -732,37 +677,21 @@ class CCompiler: extra_preargs, extra_postargs, build_temp, target_lang) - def link_shared_object (self, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): + def link_shared_object(self, objects, output_filename, output_dir=None, + libraries=None, library_dirs=None, + runtime_library_dirs=None, export_symbols=None, + debug=0, extra_preargs=None, extra_postargs=None, + build_temp=None, target_lang=None): self.link(CCompiler.SHARED_OBJECT, objects, output_filename, output_dir, libraries, library_dirs, runtime_library_dirs, export_symbols, debug, extra_preargs, extra_postargs, build_temp, target_lang) - - def link_executable (self, - objects, - output_progname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - target_lang=None): + def link_executable(self, objects, output_progname, output_dir=None, + libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, + extra_postargs=None, target_lang=None): self.link(CCompiler.EXECUTABLE, objects, self.executable_filename(output_progname), output_dir, libraries, library_dirs, runtime_library_dirs, None, @@ -774,29 +703,26 @@ class CCompiler: # no appropriate default implementation so subclasses should # implement all of these. - def library_dir_option (self, dir): + def library_dir_option(self, dir): """Return the compiler option to add 'dir' to the list of directories searched for libraries. """ raise NotImplementedError - def runtime_library_dir_option (self, dir): + def runtime_library_dir_option(self, dir): """Return the compiler option to add 'dir' to the list of directories searched for runtime libraries. """ raise NotImplementedError - def library_option (self, lib): + def library_option(self, lib): """Return the compiler option to add 'dir' to the list of libraries linked into the shared library or executable. """ raise NotImplementedError - def has_function(self, funcname, - includes=None, - include_dirs=None, - libraries=None, - library_dirs=None): + def has_function(self, funcname, includes=None, include_dirs=None, + libraries=None, library_dirs=None): """Return a boolean indicating whether funcname is supported on the current platform. The optional arguments can be used to augment the compilation environment. @@ -927,28 +853,28 @@ main (int argc, char **argv) { # -- Utility methods ----------------------------------------------- - def announce (self, msg, level=1): + def announce(self, msg, level=1): log.debug(msg) - def debug_print (self, msg): + def debug_print(self, msg): from distutils.debug import DEBUG if DEBUG: print msg - def warn (self, msg): - sys.stderr.write ("warning: %s\n" % msg) + def warn(self, msg): + sys.stderr.write("warning: %s\n" % msg) - def execute (self, func, args, msg=None, level=1): + def execute(self, func, args, msg=None, level=1): execute(func, args, msg, self.dry_run) - def spawn (self, cmd): - spawn (cmd, dry_run=self.dry_run) + def spawn(self, cmd): + spawn(cmd, dry_run=self.dry_run) - def move_file (self, src, dst): - return move_file (src, dst, dry_run=self.dry_run) + def move_file(self, src, dst): + return move_file(src, dst, dry_run=self.dry_run) - def mkpath (self, name, mode=0777): - mkpath (name, mode, dry_run=self.dry_run) + def mkpath(self, name, mode=0777): + mkpath(name, mode, dry_run=self.dry_run) # class CCompiler @@ -974,7 +900,6 @@ _default_compilers = ( ) def get_default_compiler(osname=None, platform=None): - """ Determine the default compiler to use for the given platform. osname should be one of the standard Python OS names (i.e. the @@ -1030,11 +955,7 @@ def show_compilers(): pretty_printer.print_help("List of available compilers:") -def new_compiler (plat=None, - compiler=None, - verbose=0, - dry_run=0, - force=0): +def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0): """Generate an instance of some CCompiler subclass for the supplied platform/compiler combination. 'plat' defaults to 'os.name' (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler @@ -1076,10 +997,10 @@ def new_compiler (plat=None, # XXX The None is necessary to preserve backwards compatibility # with classes that expect verbose to be the first positional # argument. - return klass (None, dry_run, force) + return klass(None, dry_run, force) -def gen_preprocess_options (macros, include_dirs): +def gen_preprocess_options(macros, include_dirs): """Generate C pre-processor options (-D, -U, -I) as used by at least two types of compilers: the typical Unix compiler and Visual C++. 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,) -- cgit v1.2.1 From 039104435cf43e73cc6d460637b95aac8ed4ef23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 11 Jan 2010 23:47:51 +0000 Subject: Merged revisions 77431 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77431 | tarek.ziade | 2010-01-12 00:41:32 +0100 (Tue, 12 Jan 2010) | 1 line module cleanup ........ --- ccompiler.py | 77 +++++++++++++++++------------------------------------------- 1 file changed, 22 insertions(+), 55 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 38c4ae8e..5a6bef44 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -9,11 +9,12 @@ import sys import os import re -from distutils.errors import CompileError, LinkError, UnknownFileError +from distutils.errors import (CompileError, LinkError, UnknownFileError, + DistutilsPlatformError, DistutilsModuleError) from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath -from distutils.dep_util import newer_pairwise, newer_group +from distutils.dep_util import newer_group from distutils.util import split_quoted, execute from distutils import log @@ -597,26 +598,15 @@ class CCompiler: """ pass - # values for target_desc parameter in link() SHARED_OBJECT = "shared_object" SHARED_LIBRARY = "shared_library" EXECUTABLE = "executable" - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): + def link(self, target_desc, objects, output_filename, output_dir=None, + libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None): """Link a bunch of stuff together to create an executable or shared library file. @@ -665,19 +655,11 @@ class CCompiler: # Old 'link_*()' methods, rewritten to use the new 'link()' method. - def link_shared_lib(self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): + def link_shared_lib(self, objects, output_libname, output_dir=None, + libraries=None, library_dirs=None, + runtime_library_dirs=None, export_symbols=None, + debug=0, extra_preargs=None, extra_postargs=None, + build_temp=None, target_lang=None): self.link(CCompiler.SHARED_LIBRARY, objects, self.library_filename(output_libname, lib_type='shared'), output_dir, @@ -686,19 +668,11 @@ class CCompiler: extra_preargs, extra_postargs, build_temp, target_lang) - def link_shared_object(self, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): + def link_shared_object(self, objects, output_filename, output_dir=None, + libraries=None, library_dirs=None, + runtime_library_dirs=None, export_symbols=None, + debug=0, extra_preargs=None, extra_postargs=None, + build_temp=None, target_lang=None): self.link(CCompiler.SHARED_OBJECT, objects, output_filename, output_dir, libraries, library_dirs, runtime_library_dirs, @@ -706,17 +680,10 @@ class CCompiler: extra_preargs, extra_postargs, build_temp, target_lang) - def link_executable(self, - objects, - output_progname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - target_lang=None): + def link_executable(self, objects, output_progname, output_dir=None, + libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, + extra_postargs=None, target_lang=None): self.link(CCompiler.EXECUTABLE, objects, self.executable_filename(output_progname), output_dir, libraries, library_dirs, runtime_library_dirs, None, @@ -898,7 +865,7 @@ main (int argc, char **argv) { def move_file(self, src, dst): return move_file(src, dst, dry_run=self.dry_run) - def mkpath (self, name, mode=0o777): + def mkpath(self, name, mode=0o777): mkpath(name, mode, dry_run=self.dry_run) -- cgit v1.2.1 From 2cb71ea99a87e5897c8d9fbfd4921f7470957c2f Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 17 Jan 2010 18:52:29 +0000 Subject: Ensure that distutils.tests.test_util will pass in 64-bit builds. Fixes #7591 --- tests/test_util.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test_util.py b/tests/test_util.py index dcc1a206..0c732f82 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -96,7 +96,12 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') - self.assertEquals(get_platform(), 'macosx-10.3-i386') + cursize = sys.maxsize + sys.maxsize = (2 ** 31)-1 + try: + self.assertEquals(get_platform(), 'macosx-10.3-i386') + finally: + sys.maxsize = cursize # macbook with fat binaries (fat, universal or fat64) os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' -- cgit v1.2.1 From efc2ae7c3f74eaf033d45215d6911fe030face88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 23 Jan 2010 09:23:15 +0000 Subject: taking sysconfig out of distutils --- ccompiler.py | 52 ++++ command/bdist.py | 2 +- command/bdist_dumb.py | 4 +- command/bdist_wininst.py | 4 +- command/build.py | 3 +- command/build_clib.py | 2 +- command/build_ext.py | 37 ++- command/build_scripts.py | 10 +- command/config.py | 2 +- command/install.py | 163 +++-------- core.py | 2 +- cygwinccompiler.py | 4 +- extension.py | 12 +- msvc9compiler.py | 10 +- sysconfig.py | 608 +++++------------------------------------- tests/support.py | 8 + tests/test_build.py | 2 +- tests/test_build_clib.py | 3 +- tests/test_build_ext.py | 17 +- tests/test_build_scripts.py | 6 +- tests/test_ccompiler.py | 26 +- tests/test_cygwinccompiler.py | 3 +- tests/test_extension.py | 2 + tests/test_install.py | 53 ++-- tests/test_sysconfig.py | 33 +-- tests/test_unixccompiler.py | 2 +- tests/test_util.py | 42 +-- unixccompiler.py | 10 +- util.py | 170 +----------- 29 files changed, 307 insertions(+), 985 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 97c61dd4..a34177e7 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -18,6 +18,58 @@ from distutils.dep_util import newer_group from distutils.util import split_quoted, execute from distutils import log +_sysconfig = __import__('sysconfig') + +def customize_compiler(compiler): + """Do any platform-specific customization of a CCompiler instance. + + Mainly needed on Unix, so we can plug in the information that + varies across Unices and is stored in Python's Makefile. + """ + if compiler.compiler_type == "unix": + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ + _sysconfig.get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', + 'CCSHARED', 'LDSHARED', 'SO', 'AR', + 'ARFLAGS') + + if 'CC' in os.environ: + cc = os.environ['CC'] + if 'CXX' in os.environ: + cxx = os.environ['CXX'] + if 'LDSHARED' in os.environ: + ldshared = os.environ['LDSHARED'] + if 'CPP' in os.environ: + cpp = os.environ['CPP'] + else: + cpp = cc + " -E" # not always + if 'LDFLAGS' in os.environ: + ldshared = ldshared + ' ' + os.environ['LDFLAGS'] + if 'CFLAGS' in os.environ: + cflags = opt + ' ' + os.environ['CFLAGS'] + ldshared = ldshared + ' ' + os.environ['CFLAGS'] + if 'CPPFLAGS' in os.environ: + cpp = cpp + ' ' + os.environ['CPPFLAGS'] + cflags = cflags + ' ' + os.environ['CPPFLAGS'] + ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] + else: + archiver = ar + ' ' + ar_flags + + cc_cmd = cc + ' ' + cflags + compiler.set_executables( + preprocessor=cpp, + compiler=cc_cmd, + compiler_so=cc_cmd + ' ' + ccshared, + compiler_cxx=cxx, + linker_so=ldshared, + linker_exe=cc, + archiver=archiver) + + compiler.shared_lib_extension = so_ext + class CCompiler: """Abstract base class to define the interface that must be implemented by real compiler classes. Also has some utility methods used by diff --git a/command/bdist.py b/command/bdist.py index 8cd69701..764024a0 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,10 +6,10 @@ distribution).""" __revision__ = "$Id$" import os +from sysconfig import get_platform from distutils.core import Command from distutils.errors import DistutilsPlatformError, DistutilsOptionError -from distutils.util import get_platform def show_formats(): diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 358a70ea..f7331d59 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -8,11 +8,11 @@ __revision__ = "$Id$" import os +from sysconfig import get_python_version, get_platform + from distutils.core import Command -from distutils.util import get_platform from distutils.dir_util import remove_tree, ensure_relative from distutils.errors import DistutilsPlatformError -from distutils.sysconfig import get_python_version from distutils import log class bdist_dumb (Command): diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index cf4282d8..0ece0306 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -9,11 +9,11 @@ import sys import os import string +from sysconfig import get_python_version, get_platform + from distutils.core import Command -from distutils.util import get_platform from distutils.dir_util import remove_tree from distutils.errors import DistutilsOptionError, DistutilsPlatformError -from distutils.sysconfig import get_python_version from distutils import log class bdist_wininst (Command): diff --git a/command/build.py b/command/build.py index d394e4b1..b84e40d6 100644 --- a/command/build.py +++ b/command/build.py @@ -5,9 +5,10 @@ Implements the Distutils 'build' command.""" __revision__ = "$Id$" import sys, os +from sysconfig import get_platform + from distutils.core import Command from distutils.errors import DistutilsOptionError -from distutils.util import get_platform def show_compilers(): from distutils.ccompiler import show_compilers diff --git a/command/build_clib.py b/command/build_clib.py index 50b64dba..8d49de75 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -19,7 +19,7 @@ __revision__ = "$Id$" import os from distutils.core import Command from distutils.errors import DistutilsSetupError -from distutils.sysconfig import customize_compiler +from distutils.ccompiler import customize_compiler from distutils import log def show_compilers(): diff --git a/command/build_ext.py b/command/build_ext.py index ff1a4be3..13bd030e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -9,13 +9,14 @@ __revision__ = "$Id$" import sys, os, re from warnings import warn +from sysconfig import get_platform + from distutils.core import Command -from distutils.errors import (CCompilerError, DistutilsError, CompileError, - DistutilsSetupError, DistutilsPlatformError) -from distutils.sysconfig import customize_compiler, get_python_version +from distutils.errors import * +from distutils.ccompiler import customize_compiler from distutils.dep_util import newer_group from distutils.extension import Extension -from distutils.util import get_platform + from distutils import log # this keeps compatibility from 2.3 to 2.5 @@ -173,8 +174,7 @@ class build_ext(Command): self.user = None def finalize_options(self): - from distutils import sysconfig - + _sysconfig = __import__('sysconfig') self.set_undefined_options('build', ('build_lib', 'build_lib'), ('build_temp', 'build_temp'), @@ -191,8 +191,8 @@ class build_ext(Command): # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. - py_include = sysconfig.get_python_inc() - plat_py_include = sysconfig.get_python_inc(plat_specific=1) + py_include = _sysconfig.get_path('include') + plat_py_include = _sysconfig.get_path('platinclude') if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if isinstance(self.include_dirs, str): @@ -270,7 +270,7 @@ class build_ext(Command): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", - "python" + get_python_version(), + "python" + _sysconfig.get_python_version(), "config")) else: # building python standard extensions @@ -278,13 +278,13 @@ class build_ext(Command): # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs - sysconfig.get_config_var('Py_ENABLE_SHARED') + _sysconfig.get_config_var('Py_ENABLE_SHARED') if ((sys.platform.startswith('linux') or sys.platform.startswith('gnu') or sys.platform.startswith('sunos')) - and sysconfig.get_config_var('Py_ENABLE_SHARED')): + and _sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions - self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) + self.library_dirs.append(_sysconfig.get_config_var('LIBDIR')) else: # building python standard extensions self.library_dirs.append('.') @@ -719,13 +719,13 @@ class build_ext(Command): of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ - from distutils.sysconfig import get_config_var + _sysconfig = __import__('sysconfig') ext_path = ext_name.split('.') # OS/2 has an 8 character module (extension) limit :-( if os.name == "os2": ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] # extensions in debug_mode are named 'module_d.pyd' under windows - so_ext = get_config_var('SO') + so_ext = _sysconfig.get_config_var('SO') if os.name == 'nt' and self.debug: return os.path.join(*ext_path) + '_d' + so_ext return os.path.join(*ext_path) + so_ext @@ -785,14 +785,13 @@ class build_ext(Command): # extensions, it is a reference to the original list return ext.libraries + [pythonlib] elif sys.platform[:6] == "atheos": - from distutils import sysconfig - + _sysconfig = __import__('sysconfig') template = "python%d.%d" pythonlib = (template % (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) # Get SHLIBS from Makefile extra = [] - for lib in sysconfig.get_config_var('SHLIBS').split(): + for lib in _sysconfig.get_config_var('SHLIBS').split(): if lib.startswith('-l'): extra.append(lib[2:]) else: @@ -806,8 +805,8 @@ class build_ext(Command): return ext.libraries else: - from distutils import sysconfig - if sysconfig.get_config_var('Py_ENABLE_SHARED'): + _sysconfig = __import__('sysconfig') + if _sysconfig.get_config_var('Py_ENABLE_SHARED'): template = "python%d.%d" pythonlib = (template % (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) diff --git a/command/build_scripts.py b/command/build_scripts.py index 2ad4c7be..567df658 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -6,7 +6,6 @@ __revision__ = "$Id$" import os, re from stat import ST_MODE -from distutils import sysconfig from distutils.core import Command from distutils.dep_util import newer from distutils.util import convert_path @@ -57,6 +56,7 @@ class build_scripts (Command): ie. starts with "\#!" and contains "python"), then adjust the first line to refer to the current Python interpreter as we copy. """ + _sysconfig = __import__('sysconfig') self.mkpath(self.build_dir) outfiles = [] for script in self.scripts: @@ -94,16 +94,16 @@ class build_scripts (Command): self.build_dir) if not self.dry_run: outf = open(outfile, "w") - if not sysconfig.python_build: + if not _sysconfig.is_python_build(): outf.write("#!%s%s\n" % (self.executable, post_interp)) else: outf.write("#!%s%s\n" % (os.path.join( - sysconfig.get_config_var("BINDIR"), - "python%s%s" % (sysconfig.get_config_var("VERSION"), - sysconfig.get_config_var("EXE"))), + _sysconfig.get_config_var("BINDIR"), + "python%s%s" % (_sysconfig.get_config_var("VERSION"), + _sysconfig.get_config_var("EXE"))), post_interp)) outf.writelines(f.readlines()) outf.close() diff --git a/command/config.py b/command/config.py index b0849135..da8da595 100644 --- a/command/config.py +++ b/command/config.py @@ -16,7 +16,7 @@ import re from distutils.core import Command from distutils.errors import DistutilsExecError -from distutils.sysconfig import customize_compiler +from distutils.ccompiler import customize_compiler from distutils import log LANG_EXT = {'c': '.c', 'c++': '.cxx'} diff --git a/command/install.py b/command/install.py index 8f089c31..68d6227a 100644 --- a/command/install.py +++ b/command/install.py @@ -7,115 +7,25 @@ __revision__ = "$Id$" import sys import os +from sysconfig import (get_config_vars, get_platform, get_paths, get_path, + get_config_var) + from distutils import log from distutils.core import Command from distutils.debug import DEBUG -from distutils.sysconfig import get_config_vars from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file -from distutils.util import convert_path, subst_vars, change_root -from distutils.util import get_platform +from distutils.util import convert_path, change_root from distutils.errors import DistutilsOptionError -# this keeps compatibility from 2.3 to 2.5 -if sys.version < "2.6": - USER_BASE = None - USER_SITE = None - HAS_USER_SITE = False -else: - from site import USER_BASE - from site import USER_SITE - HAS_USER_SITE = True - -if sys.version < "2.2": - WINDOWS_SCHEME = { - 'purelib': '$base', - 'platlib': '$base', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - } -else: - WINDOWS_SCHEME = { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - } - -INSTALL_SCHEMES = { - 'unix_prefix': { - 'purelib': '$base/lib/python$py_version_short/site-packages', - 'platlib': '$platbase/lib/python$py_version_short/site-packages', - 'headers': '$base/include/python$py_version_short/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', - }, - 'unix_home': { - 'purelib': '$base/lib/python', - 'platlib': '$base/lib/python', - 'headers': '$base/include/python/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', - }, - 'nt': WINDOWS_SCHEME, - 'mac': { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - }, - - 'os2': { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - }, - } - -# user site schemes -if HAS_USER_SITE: - INSTALL_SCHEMES['nt_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', - 'scripts': '$userbase/Scripts', - 'data' : '$userbase', - } - - INSTALL_SCHEMES['unix_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/include/python$py_version_short/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - } - - INSTALL_SCHEMES['mac_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/$py_version_short/include/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - } - - INSTALL_SCHEMES['os2_home'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/include/python$py_version_short/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - } - -# The keys to an installation scheme; if any new types of files are to be -# installed, be sure to add an entry to every installation scheme above, -# and to SCHEME_KEYS here. -SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') - +def _subst_vars(s, local_vars): + try: + return s.format(**local_vars) + except KeyError: + try: + return s.format(**os.environ) + except KeyError, var: + raise AttributeError('{%s}' % var) class install(Command): @@ -182,11 +92,10 @@ class install(Command): boolean_options = ['compile', 'force', 'skip-build'] - if HAS_USER_SITE: - user_options.append(('user', None, - "install in user site-package '%s'" % USER_SITE)) - boolean_options.append('user') - + user_options.append(('user', None, + "install in user site-package '%s'" % \ + get_path('purelib', '%s_user' % os.name))) + boolean_options.append('user') negative_opt = {'no-compile' : 'compile'} @@ -216,8 +125,8 @@ class install(Command): self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None - self.install_userbase = USER_BASE - self.install_usersite = USER_SITE + self.install_userbase = get_config_var('userbase') + self.install_usersite = get_path('purelib', '%s_user' % os.name) self.compile = None self.optimize = None @@ -327,7 +236,9 @@ class install(Command): # about needing recursive variable expansion (shudder). py_version = sys.version.split()[0] - (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') + prefix, exec_prefix, srcdir = get_config_vars('prefix', 'exec_prefix', + 'srcdir') + self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), 'dist_fullname': self.distribution.get_fullname(), @@ -338,12 +249,11 @@ class install(Command): 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, + 'srcdir': srcdir, } - if HAS_USER_SITE: - self.config_vars['userbase'] = self.install_userbase - self.config_vars['usersite'] = self.install_usersite - + self.config_vars['userbase'] = self.install_userbase + self.config_vars['usersite'] = self.install_usersite self.expand_basedirs() self.dump_dirs("post-expand_basedirs()") @@ -447,10 +357,10 @@ class install(Command): raise DistutilsPlatformError( "User base directory is not specified") self.install_base = self.install_platbase = self.install_userbase - self.select_scheme("unix_user") + self.select_scheme("posix_user") elif self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme("unix_home") + self.select_scheme("posix_home") else: if self.prefix is None: if self.exec_prefix is not None: @@ -466,7 +376,7 @@ class install(Command): self.install_base = self.prefix self.install_platbase = self.exec_prefix - self.select_scheme("unix_prefix") + self.select_scheme("posix_prefix") def finalize_other(self): """Finalizes options for non-posix platforms""" @@ -478,7 +388,7 @@ class install(Command): self.select_scheme(os.name + "_user") elif self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme("unix_home") + self.select_scheme("posix_home") else: if self.prefix is None: self.prefix = os.path.normpath(sys.prefix) @@ -493,11 +403,15 @@ class install(Command): def select_scheme(self, name): """Sets the install directories by applying the install schemes.""" # it's the caller's problem if they supply a bad name! - scheme = INSTALL_SCHEMES[name] - for key in SCHEME_KEYS: + scheme = get_paths(name, expand=False) + for key, value in scheme.items(): + if key == 'platinclude': + key = 'headers' + value = os.path.join(value, self.distribution.get_name()) attrname = 'install_' + key - if getattr(self, attrname) is None: - setattr(self, attrname, scheme[key]) + if hasattr(self, attrname): + if getattr(self, attrname) is None: + setattr(self, attrname, value) def _expand_attrs(self, attrs): for attr in attrs: @@ -505,7 +419,10 @@ class install(Command): if val is not None: if os.name == 'posix' or os.name == 'nt': val = os.path.expanduser(val) - val = subst_vars(val, self.config_vars) + try: + val = _subst_vars(val, self.config_vars) + except: + import pdb; pdb.set_trace() setattr(self, attr, val) def expand_basedirs(self): diff --git a/core.py b/core.py index 0b725ff0..99ccf44f 100644 --- a/core.py +++ b/core.py @@ -35,7 +35,7 @@ usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] def gen_usage(script_name): script = os.path.basename(script_name) - return USAGE % vars() + return USAGE % {'script': script} # Some mild magic to control the behaviour of 'setup()' from 'run_setup()'. diff --git a/cygwinccompiler.py b/cygwinccompiler.py index eed9c321..2a61bd5a 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -337,7 +337,7 @@ def check_config_h(): # XXX since this function also checks sys.version, it's not strictly a # "pyconfig.h" check -- should probably be renamed... - from distutils import sysconfig + _sysconfig = __import__('sysconfig') # if sys.version contains GCC then python was compiled with GCC, and the # pyconfig.h file should be OK @@ -345,7 +345,7 @@ def check_config_h(): return CONFIG_H_OK, "sys.version mentions 'GCC'" # let's see if __GNUC__ is mentioned in python.h - fn = sysconfig.get_config_h_filename() + fn = _sysconfig.get_config_h_filename() try: with open(fn) as config_h: if "__GNUC__" in config_h.read(): diff --git a/extension.py b/extension.py index 9988ec0c..244b1e78 100644 --- a/extension.py +++ b/extension.py @@ -134,14 +134,17 @@ class Extension: def read_setup_file(filename): """Reads a Setup file and returns Extension instances.""" - from distutils.sysconfig import (parse_makefile, expand_makefile_vars, + warnings.warn('distutils.extensions.read_setup_file is deprecated. ' + 'It will be removed in the next Python release.') + _sysconfig = __import__('sysconfig') + from distutils.sysconfig import (expand_makefile_vars, _variable_rx) from distutils.text_file import TextFile from distutils.util import split_quoted # First pass over the file to gather "VAR = VALUE" assignments. - vars = parse_makefile(filename) + vars = _sysconfig._parse_makefile(filename) # Second pass to gobble up the real content: lines of the form # ... [ ...] [ ...] [ ...] @@ -161,7 +164,10 @@ def read_setup_file(filename): file.warn("'%s' lines not handled yet" % line) continue - line = expand_makefile_vars(line, vars) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + line = expand_makefile_vars(line, vars) + words = split_quoted(line) # NB. this parses a slightly different syntax than the old diff --git a/msvc9compiler.py b/msvc9compiler.py index 41d67faf..6ac24f83 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -23,10 +23,10 @@ from distutils.errors import (DistutilsExecError, DistutilsPlatformError, CompileError, LibError, LinkError) from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log -from distutils.util import get_platform - import _winreg +_sysconfig = __import__('sysconfig') + RegOpenKeyEx = _winreg.OpenKeyEx RegEnumKey = _winreg.EnumKey RegEnumValue = _winreg.EnumValue @@ -327,7 +327,7 @@ class MSVCCompiler(CCompiler) : # multi-init means we would need to check platform same each time... assert not self.initialized, "don't init multiple times" if plat_name is None: - plat_name = get_platform() + plat_name = _sysconfig.get_platform() # sanity check for platforms to prevent obscure errors later. ok_plats = 'win32', 'win-amd64', 'win-ia64' if plat_name not in ok_plats: @@ -348,12 +348,12 @@ class MSVCCompiler(CCompiler) : # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross # compile use 'x86' (ie, it runs the x86 compiler directly) # No idea how itanium handles this, if at all. - if plat_name == get_platform() or plat_name == 'win32': + if plat_name == _sysconfig.get_platform() or plat_name == 'win32': # native build or cross-compile to win32 plat_spec = PLAT_TO_VCVARS[plat_name] else: # cross compile from win32 -> some 64bit - plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ + plat_spec = PLAT_TO_VCVARS[_sysconfig.get_platform()] + '_' + \ PLAT_TO_VCVARS[plat_name] vc_env = query_vcvarsall(VERSION, plat_spec) diff --git a/sysconfig.py b/sysconfig.py index f3b2aca6..bb8b5125 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -7,58 +7,42 @@ available. Written by: Fred L. Drake, Jr. Email: + +**This module has been moved out of Distutils and will be removed from +Python in the next version (3.2)** """ __revision__ = "$Id$" -import os import re -import sys - -from distutils.errors import DistutilsPlatformError - -# These are needed in a couple of spots, so just compute them once. -PREFIX = os.path.normpath(sys.prefix) -EXEC_PREFIX = os.path.normpath(sys.exec_prefix) - -# Path to the base directory of the project. On Windows the binary may -# live in project/PCBuild9. If we're dealing with an x64 Windows build, -# it'll live in project/PCbuild/amd64. -project_base = os.path.dirname(os.path.abspath(sys.executable)) -if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): - project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) -# PC/VS7.1 -if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): - project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, - os.path.pardir)) -# PC/AMD64 -if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): - project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, - os.path.pardir)) - -# python_build: (Boolean) if true, we're either building Python or -# building an extension with an un-installed Python, so we use -# different (hard-wired) directories. -# Setup.local is available for Makefile builds including VPATH builds, -# Setup.dist is available on Windows -def _python_build(): - for fn in ("Setup.dist", "Setup.local"): - if os.path.isfile(os.path.join(project_base, "Modules", fn)): - return True - return False -python_build = _python_build() +from warnings import warn +# importing sysconfig from Lib +# to avoid this module to shadow it +_sysconfig = __import__('sysconfig') -def get_python_version(): - """Return a string containing the major and minor Python version, - leaving off the patchlevel. Sample return values could be '1.5' - or '2.2'. - """ - return sys.version[:3] +_DEPRECATION_MSG = ("distutils.sysconfig.%s is deprecated. " + "Use the APIs provided by the sysconfig module instead") + +def _get_project_base(): + return _sysconfig._PROJECT_BASE +project_base = _get_project_base() + +class _DeprecatedBool(int): + def __nonzero__(self): + warn(_DEPRECATION_MSG % 'get_python_version', DeprecationWarning) + return super(_DeprecatedBool, self).__nonzero__() + +def _python_build(): + return _DeprecatedBool(_sysconfig.is_python_build()) + +python_build = _python_build() def get_python_inc(plat_specific=0, prefix=None): - """Return the directory containing installed Python header files. + """This function is deprecated. + + Return the directory containing installed Python header files. If 'plat_specific' is false (the default), this is the path to the non-platform-specific header files, i.e. Python.h and so on; @@ -68,39 +52,22 @@ def get_python_inc(plat_specific=0, prefix=None): If 'prefix' is supplied, use it instead of sys.prefix or sys.exec_prefix -- i.e., ignore 'plat_specific'. """ - if prefix is None: - prefix = plat_specific and EXEC_PREFIX or PREFIX - if os.name == "posix": - if python_build: - # Assume the executable is in the build directory. The - # pyconfig.h file should be in the same directory. Since - # the build directory may not be the source directory, we - # must use "srcdir" from the makefile to find the "Include" - # directory. - base = os.path.dirname(os.path.abspath(sys.executable)) - if plat_specific: - return base - else: - incdir = os.path.join(get_config_var('srcdir'), 'Include') - return os.path.normpath(incdir) - return os.path.join(prefix, "include", "python" + get_python_version()) - elif os.name == "nt": - return os.path.join(prefix, "include") - elif os.name == "mac": - if plat_specific: - return os.path.join(prefix, "Mac", "Include") - else: - return os.path.join(prefix, "Include") - elif os.name == "os2": - return os.path.join(prefix, "Include") + warn(_DEPRECATION_MSG % 'get_python_inc', DeprecationWarning) + get_path = _sysconfig.get_path + + if prefix is not None: + vars = {'base': prefix} + return get_path('include', vars=vars) + + if not plat_specific: + return get_path('include') else: - raise DistutilsPlatformError( - "I don't know where Python installs its C header files " - "on platform '%s'" % os.name) + return get_path('platinclude') +def get_python_lib(plat_specific=False, standard_lib=False, prefix=None): + """This function is deprecated. -def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): - """Return the directory containing the Python library (standard or + Return the directory containing the Python library (standard or site additions). If 'plat_specific' is true, return the directory containing @@ -113,153 +80,33 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): If 'prefix' is supplied, use it instead of sys.prefix or sys.exec_prefix -- i.e., ignore 'plat_specific'. """ - if prefix is None: - prefix = plat_specific and EXEC_PREFIX or PREFIX - - if os.name == "posix": - libpython = os.path.join(prefix, - "lib", "python" + get_python_version()) - if standard_lib: - return libpython - else: - return os.path.join(libpython, "site-packages") - - elif os.name == "nt": - if standard_lib: - return os.path.join(prefix, "Lib") + warn(_DEPRECATION_MSG % 'get_python_lib', DeprecationWarning) + vars = {} + get_path = _sysconfig.get_path + if prefix is not None: + if plat_specific: + vars['platbase'] = prefix else: - if get_python_version() < "2.2": - return prefix - else: - return os.path.join(prefix, "Lib", "site-packages") + vars['base'] = prefix - elif os.name == "mac": + if standard_lib: if plat_specific: - if standard_lib: - return os.path.join(prefix, "Lib", "lib-dynload") - else: - return os.path.join(prefix, "Lib", "site-packages") - else: - if standard_lib: - return os.path.join(prefix, "Lib") - else: - return os.path.join(prefix, "Lib", "site-packages") - - elif os.name == "os2": - if standard_lib: - return os.path.join(prefix, "Lib") + return get_path('platstdlib', vars=vars) else: - return os.path.join(prefix, "Lib", "site-packages") - + return get_path('stdlib', vars=vars) else: - raise DistutilsPlatformError( - "I don't know where Python installs its library " - "on platform '%s'" % os.name) - - -def customize_compiler(compiler): - """Do any platform-specific customization of a CCompiler instance. - - Mainly needed on Unix, so we can plug in the information that - varies across Unices and is stored in Python's Makefile. - """ - if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ - get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS') - - if 'CC' in os.environ: - cc = os.environ['CC'] - if 'CXX' in os.environ: - cxx = os.environ['CXX'] - if 'LDSHARED' in os.environ: - ldshared = os.environ['LDSHARED'] - if 'CPP' in os.environ: - cpp = os.environ['CPP'] - else: - cpp = cc + " -E" # not always - if 'LDFLAGS' in os.environ: - ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if 'CFLAGS' in os.environ: - cflags = opt + ' ' + os.environ['CFLAGS'] - ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if 'CPPFLAGS' in os.environ: - cpp = cpp + ' ' + os.environ['CPPFLAGS'] - cflags = cflags + ' ' + os.environ['CPPFLAGS'] - ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] - if 'AR' in os.environ: - ar = os.environ['AR'] - if 'ARFLAGS' in os.environ: - archiver = ar + ' ' + os.environ['ARFLAGS'] - else: - archiver = ar + ' ' + ar_flags - - cc_cmd = cc + ' ' + cflags - compiler.set_executables( - preprocessor=cpp, - compiler=cc_cmd, - compiler_so=cc_cmd + ' ' + ccshared, - compiler_cxx=cxx, - linker_so=ldshared, - linker_exe=cc, - archiver=archiver) - - compiler.shared_lib_extension = so_ext - - -def get_config_h_filename(): - """Return full pathname of installed pyconfig.h file.""" - if python_build: - if os.name == "nt": - inc_dir = os.path.join(project_base, "PC") + if plat_specific: + return get_path('platlib', vars=vars) else: - inc_dir = project_base - else: - inc_dir = get_python_inc(plat_specific=1) - if get_python_version() < '2.2': - config_h = 'config.h' - else: - # The name of the config.h file changed in 2.2 - config_h = 'pyconfig.h' - return os.path.join(inc_dir, config_h) - + return get_path('purelib', vars=vars) def get_makefile_filename(): - """Return full pathname of installed Makefile from the Python build.""" - if python_build: - return os.path.join(os.path.dirname(sys.executable), "Makefile") - lib_dir = get_python_lib(plat_specific=1, standard_lib=1) - return os.path.join(lib_dir, "config", "Makefile") - + """This function is deprecated. -def parse_config_h(fp, g=None): - """Parse a config.h-style file. - - A dictionary containing name/value pairs is returned. If an - optional dictionary is passed in as the second argument, it is - used instead of a new dictionary. + Return full pathname of installed Makefile from the Python build. """ - if g is None: - g = {} - define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") - undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") - # - while 1: - line = fp.readline() - if not line: - break - m = define_rx.match(line) - if m: - n, v = m.group(1, 2) - try: v = int(v) - except ValueError: pass - g[n] = v - else: - m = undef_rx.match(line) - if m: - g[m.group(1)] = 0 - return g - + warn(_DEPRECATION_MSG % 'get_makefile_filename', DeprecationWarning) + return _sysconfig._get_makefile_filename() # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). @@ -268,91 +115,29 @@ _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") def parse_makefile(fn, g=None): - """Parse a Makefile-style file. + """This function is deprecated. + + Parse a Makefile-style file. A dictionary containing name/value pairs is returned. If an optional dictionary is passed in as the second argument, it is used instead of a new dictionary. """ - from distutils.text_file import TextFile - fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1) - - if g is None: - g = {} - done = {} - notdone = {} - - while 1: - line = fp.readline() - if line is None: # eof - break - m = _variable_rx.match(line) - if m: - n, v = m.group(1, 2) - v = v.strip() - # `$$' is a literal `$' in make - tmpv = v.replace('$$', '') - - if "$" in tmpv: - notdone[n] = v - else: - try: - v = int(v) - except ValueError: - # insert literal `$' - done[n] = v.replace('$$', '$') - else: - done[n] = v - - # do variable interpolation here - while notdone: - for name in notdone.keys(): - value = notdone[name] - m = _findvar1_rx.search(value) or _findvar2_rx.search(value) - if m: - n = m.group(1) - found = True - if n in done: - item = str(done[n]) - elif n in notdone: - # get it on a subsequent round - found = False - elif n in os.environ: - # do it like make: fall back to environment - item = os.environ[n] - else: - done[n] = item = "" - if found: - after = value[m.end():] - value = value[:m.start()] + item + after - if "$" in after: - notdone[name] = value - else: - try: value = int(value) - except ValueError: - done[name] = value.strip() - else: - done[name] = value - del notdone[name] - else: - # bogus variable reference; just drop it since we can't deal - del notdone[name] - - fp.close() - - # save the results in the global dictionary - g.update(done) - return g - + warn(_DEPRECATION_MSG % 'parse_makefile', DeprecationWarning) + return _sysconfig._parse_makefile(fn, g) def expand_makefile_vars(s, vars): - """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in + """This function is deprecated. + + Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in 'string' according to 'vars' (a dictionary mapping variable names to values). Variables not present in 'vars' are silently expanded to the empty string. The variable values in 'vars' should not contain further variable expansions; if 'vars' is the output of 'parse_makefile()', you're fine. Returns a variable-expanded version of 's'. """ + warn('this function will be removed in then next version of Python', + DeprecationWarning) # This algorithm does multiple expansion, so if vars['foo'] contains # "${bar}", it will expand ${foo} to ${bar}, and then expand @@ -368,264 +153,3 @@ def expand_makefile_vars(s, vars): else: break return s - - -_config_vars = None - -def _init_posix(): - """Initialize the module as appropriate for POSIX systems.""" - g = {} - # load the installed Makefile: - try: - filename = get_makefile_filename() - parse_makefile(filename, g) - except IOError, msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # load the installed pyconfig.h: - try: - filename = get_config_h_filename() - parse_config_h(file(filename), g) - except IOError, msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # On MacOSX we need to check the setting of the environment variable - # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so - # it needs to be compatible. - # If it isn't set we set it to the configure-time value - if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g: - cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] - cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') - if cur_target == '': - cur_target = cfg_target - os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) - elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')): - my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' - % (cur_target, cfg_target)) - raise DistutilsPlatformError(my_msg) - - # On AIX, there are wrong paths to the linker scripts in the Makefile - # -- these paths are relative to the Python source, but when installed - # the scripts are in another directory. - if python_build: - g['LDSHARED'] = g['BLDSHARED'] - - elif get_python_version() < '2.1': - # The following two branches are for 1.5.2 compatibility. - if sys.platform == 'aix4': # what about AIX 3.x ? - # Linker script is in the config directory, not in Modules as the - # Makefile says. - python_lib = get_python_lib(standard_lib=1) - ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') - python_exp = os.path.join(python_lib, 'config', 'python.exp') - - g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) - - elif sys.platform == 'beos': - # Linker script is in the config directory. In the Makefile it is - # relative to the srcdir, which after installation no longer makes - # sense. - python_lib = get_python_lib(standard_lib=1) - linkerscript_path = g['LDSHARED'].split()[0] - linkerscript_name = os.path.basename(linkerscript_path) - linkerscript = os.path.join(python_lib, 'config', - linkerscript_name) - - # XXX this isn't the right place to do this: adding the Python - # library to the link, if needed, should be in the "build_ext" - # command. (It's also needed for non-MS compilers on Windows, and - # it's taken care of for them by the 'build_ext.get_libraries()' - # method.) - g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % - (linkerscript, PREFIX, get_python_version())) - - global _config_vars - _config_vars = g - - -def _init_nt(): - """Initialize the module as appropriate for NT""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - g['SO'] = '.pyd' - g['EXE'] = ".exe" - g['VERSION'] = get_python_version().replace(".", "") - g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) - - global _config_vars - _config_vars = g - - -def _init_mac(): - """Initialize the module as appropriate for Macintosh systems""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - import MacOS - if not hasattr(MacOS, 'runtimemodel'): - g['SO'] = '.ppc.slb' - else: - g['SO'] = '.%s.slb' % MacOS.runtimemodel - - # XXX are these used anywhere? - g['install_lib'] = os.path.join(EXEC_PREFIX, "Lib") - g['install_platlib'] = os.path.join(EXEC_PREFIX, "Mac", "Lib") - - # These are used by the extension module build - g['srcdir'] = ':' - global _config_vars - _config_vars = g - - -def _init_os2(): - """Initialize the module as appropriate for OS/2""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - g['SO'] = '.pyd' - g['EXE'] = ".exe" - - global _config_vars - _config_vars = g - - -def get_config_vars(*args): - """With no arguments, return a dictionary of all configuration - variables relevant for the current platform. Generally this includes - everything needed to build extensions and install both pure modules and - extensions. On Unix, this means every variable defined in Python's - installed Makefile; on Windows and Mac OS it's a much smaller set. - - With arguments, return a list of values that result from looking up - each argument in the configuration variable dictionary. - """ - global _config_vars - if _config_vars is None: - func = globals().get("_init_" + os.name) - if func: - func() - else: - _config_vars = {} - - # Normalized versions of prefix and exec_prefix are handy to have; - # in fact, these are the standard versions used most places in the - # Distutils. - _config_vars['prefix'] = PREFIX - _config_vars['exec_prefix'] = EXEC_PREFIX - - if 'srcdir' not in _config_vars: - _config_vars['srcdir'] = project_base - - # Convert srcdir into an absolute path if it appears necessary. - # Normally it is relative to the build directory. However, during - # testing, for example, we might be running a non-installed python - # from a different directory. - if python_build and os.name == "posix": - base = os.path.dirname(os.path.abspath(sys.executable)) - if (not os.path.isabs(_config_vars['srcdir']) and - base != os.getcwd()): - # srcdir is relative and we are not in the same directory - # as the executable. Assume executable is in the build - # directory and make srcdir absolute. - srcdir = os.path.join(base, _config_vars['srcdir']) - _config_vars['srcdir'] = os.path.normpath(srcdir) - - if sys.platform == 'darwin': - kernel_version = os.uname()[2] # Kernel version (8.4.3) - major_version = int(kernel_version.split('.')[0]) - - if major_version < 8: - # On Mac OS X before 10.4, check if -arch and -isysroot - # are in CFLAGS or LDFLAGS and remove them if they are. - # This is needed when building extensions on a 10.3 system - # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = re.sub('-isysroot [^ \t]*', ' ', flags) - _config_vars[key] = flags - - else: - - # Allow the user to override the architecture flags using - # an environment variable. - # NOTE: This name was introduced by Apple in OSX 10.5 and - # is used by several scripting languages distributed with - # that OS release. - - if 'ARCHFLAGS' in os.environ: - arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = flags + ' ' + arch - _config_vars[key] = flags - - # If we're on OSX 10.5 or later and the user tries to - # compiles an extension using an SDK that is not present - # on the current machine it is better to not use an SDK - # than to fail. - # - # The major usecase for this is users using a Python.org - # binary installer on OSX 10.6: that installer uses - # the 10.4u SDK, but that SDK is not installed by default - # when you install Xcode. - # - m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS']) - if m is not None: - sdk = m.group(1) - if not os.path.exists(sdk): - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) - _config_vars[key] = flags - - if args: - vals = [] - for name in args: - vals.append(_config_vars.get(name)) - return vals - else: - return _config_vars - -def get_config_var(name): - """Return the value of a single variable using the dictionary - returned by 'get_config_vars()'. Equivalent to - get_config_vars().get(name) - """ - return get_config_vars().get(name) diff --git a/tests/support.py b/tests/support.py index 41d3cd3c..783318e8 100644 --- a/tests/support.py +++ b/tests/support.py @@ -3,11 +3,19 @@ import os import shutil import tempfile from copy import deepcopy +import warnings from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution +def capture_warnings(func): + def _capture_warnings(*args, **kw): + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + return func(*args, **kw) + return _capture_warnings + class LoggingSilencer(object): def setUp(self): diff --git a/tests/test_build.py b/tests/test_build.py index 6bca27ee..2418e165 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -5,7 +5,7 @@ import sys from distutils.command.build import build from distutils.tests import support -from distutils.util import get_platform +from sysconfig import get_platform class BuildTestCase(support.TempdirManager, support.LoggingSilencer, diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 536cd673..145eff54 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -120,8 +120,7 @@ class BuildCLibTestCase(support.TempdirManager, # before we run the command, we want to make sure # all commands are present on the system # by creating a compiler and checking its executables - from distutils.ccompiler import new_compiler - from distutils.sysconfig import customize_compiler + from distutils.ccompiler import new_compiler, customize_compiler compiler = new_compiler() customize_compiler(compiler) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 317ce2bc..0e3f33e4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -9,7 +9,7 @@ from test.test_support import captured_stdout from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext -from distutils import sysconfig +import sysconfig from distutils.tests import support from distutils.extension import Extension from distutils.errors import (UnknownFileError, DistutilsSetupError, @@ -105,17 +105,17 @@ class BuildExtTestCase(support.TempdirManager, old = sys.platform sys.platform = 'sunos' # fooling finalize_options - from distutils.sysconfig import _config_vars - old_var = _config_vars.get('Py_ENABLE_SHARED') - _config_vars['Py_ENABLE_SHARED'] = 1 + from sysconfig import _CONFIG_VARS + old_var = _CONFIG_VARS.get('Py_ENABLE_SHARED') + _CONFIG_VARS['Py_ENABLE_SHARED'] = 1 try: cmd.ensure_finalized() finally: sys.platform = old if old_var is None: - del _config_vars['Py_ENABLE_SHARED'] + del _CONFIG_VARS['Py_ENABLE_SHARED'] else: - _config_vars['Py_ENABLE_SHARED'] = old_var + _CONFIG_VARS['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris self.assertTrue(len(cmd.library_dirs) > 0) @@ -177,11 +177,10 @@ class BuildExtTestCase(support.TempdirManager, cmd = build_ext(dist) cmd.finalize_options() - from distutils import sysconfig - py_include = sysconfig.get_python_inc() + py_include = sysconfig.get_path('include') self.assertTrue(py_include in cmd.include_dirs) - plat_py_include = sysconfig.get_python_inc(plat_specific=1) + plat_py_include = sysconfig.get_path('platinclude') self.assertTrue(plat_py_include in cmd.include_dirs) # make sure cmd.libraries is turned into a list diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index b1d2d079..72e8915c 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,7 +5,7 @@ import unittest from distutils.command.build_scripts import build_scripts from distutils.core import Distribution -from distutils import sysconfig +import sysconfig from distutils.tests import support @@ -91,12 +91,12 @@ class BuildScriptsTestCase(support.TempdirManager, # --with-suffix=3`, python is compiled okay but the build scripts # failed when writing the name of the executable old = sysconfig.get_config_vars().get('VERSION') - sysconfig._config_vars['VERSION'] = 4 + sysconfig._CONFIG_VARS['VERSION'] = 4 try: cmd.run() finally: if old is not None: - sysconfig._config_vars['VERSION'] = old + sysconfig._CONFIG_VARS['VERSION'] = old built = os.listdir(target) for name in expected: diff --git a/tests/test_ccompiler.py b/tests/test_ccompiler.py index 2c219b7b..317e77fd 100644 --- a/tests/test_ccompiler.py +++ b/tests/test_ccompiler.py @@ -3,8 +3,10 @@ import os import unittest from test.test_support import captured_stdout -from distutils.ccompiler import gen_lib_options, CCompiler +from distutils.ccompiler import (gen_lib_options, CCompiler, + get_default_compiler, customize_compiler) from distutils import debug +from distutils.tests import support class FakeCompiler(object): def library_dir_option(self, dir): @@ -19,7 +21,7 @@ class FakeCompiler(object): def library_option(self, lib): return "-l" + lib -class CCompilerTestCase(unittest.TestCase): +class CCompilerTestCase(support.EnvironGuard, unittest.TestCase): def test_gen_lib_options(self): compiler = FakeCompiler() @@ -52,6 +54,26 @@ class CCompilerTestCase(unittest.TestCase): finally: debug.DEBUG = False + def test_customize_compiler(self): + + # not testing if default compiler is not unix + if get_default_compiler() != 'unix': + return + + os.environ['AR'] = 'my_ar' + os.environ['ARFLAGS'] = '-arflags' + + # make sure AR gets caught + class compiler: + compiler_type = 'unix' + + def set_executables(self, **kw): + self.exes = kw + + comp = compiler() + customize_compiler(comp) + self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') + def test_suite(): return unittest.makeSuite(CCompilerTestCase) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index b54ffff3..b67f987e 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -3,6 +3,7 @@ import unittest import sys import os import warnings +import sysconfig from test.test_support import check_warnings from test.test_support import captured_stdout @@ -22,13 +23,11 @@ class CygwinCCompilerTestCase(support.TempdirManager, super(CygwinCCompilerTestCase, self).setUp() self.version = sys.version self.python_h = os.path.join(self.mkdtemp(), 'python.h') - from distutils import sysconfig self.old_get_config_h_filename = sysconfig.get_config_h_filename sysconfig.get_config_h_filename = self._get_config_h_filename def tearDown(self): sys.version = self.version - from distutils import sysconfig sysconfig.get_config_h_filename = self.old_get_config_h_filename super(CygwinCCompilerTestCase, self).tearDown() diff --git a/tests/test_extension.py b/tests/test_extension.py index 159ac2b7..cffa9a09 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -5,9 +5,11 @@ import warnings from test.test_support import check_warnings from distutils.extension import read_setup_file, Extension +from distutils.tests.support import capture_warnings class ExtensionTestCase(unittest.TestCase): + @capture_warnings def test_read_setup_file(self): # trying to read a Setup file # (sample extracted from the PyGame project) diff --git a/tests/test_install.py b/tests/test_install.py index d44156dd..8e3e942f 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -5,12 +5,14 @@ import os.path import sys import unittest import site +import sysconfig +from sysconfig import (get_scheme_names, _CONFIG_VARS, _INSTALL_SCHEMES, + get_config_var, get_path) from test.test_support import captured_stdout from distutils.command.install import install from distutils.command import install as install_module -from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution from distutils.errors import DistutilsOptionError @@ -36,9 +38,23 @@ class InstallTestCase(support.TempdirManager, build_lib=os.path.join(builddir, "lib"), ) - cmd = install(dist) - cmd.home = destination - cmd.ensure_finalized() + + + posix_prefix = _INSTALL_SCHEMES['posix_prefix'] + old_posix_prefix = posix_prefix['platinclude'] + posix_prefix['platinclude'] = \ + '{platbase}/include/python{py_version_short}' + + posix_home = _INSTALL_SCHEMES['posix_home'] + old_posix_home = posix_home['platinclude'] + posix_home['platinclude'] = '{base}/include/python' + try: + cmd = install(dist) + cmd.home = destination + cmd.ensure_finalized() + finally: + posix_home['platinclude'] = old_posix_home + posix_prefix['platinclude'] = old_posix_prefix self.assertEqual(cmd.install_base, destination) self.assertEqual(cmd.install_platbase, destination) @@ -63,18 +79,19 @@ class InstallTestCase(support.TempdirManager, return # preparing the environement for the test - self.old_user_base = site.USER_BASE - self.old_user_site = site.USER_SITE + self.old_user_base = get_config_var('userbase') + self.old_user_site = get_path('purelib', '%s_user' % os.name) self.tmpdir = self.mkdtemp() self.user_base = os.path.join(self.tmpdir, 'B') self.user_site = os.path.join(self.tmpdir, 'S') - site.USER_BASE = self.user_base - site.USER_SITE = self.user_site - install_module.USER_BASE = self.user_base - install_module.USER_SITE = self.user_site + _CONFIG_VARS['userbase'] = self.user_base + scheme = _INSTALL_SCHEMES['%s_user' % os.name] + scheme['purelib'] = self.user_site def _expanduser(path): - return self.tmpdir + if path[0] == '~': + path = os.path.normpath(self.tmpdir) + path[1:] + return path self.old_expand = os.path.expanduser os.path.expanduser = _expanduser @@ -82,19 +99,17 @@ class InstallTestCase(support.TempdirManager, # this is the actual test self._test_user_site() finally: - site.USER_BASE = self.old_user_base - site.USER_SITE = self.old_user_site - install_module.USER_BASE = self.old_user_base - install_module.USER_SITE = self.old_user_site + _CONFIG_VARS['userbase'] = self.old_user_base + scheme['purelib'] = self.old_user_site os.path.expanduser = self.old_expand def _test_user_site(self): - for key in ('nt_user', 'unix_user', 'os2_home'): - self.assertTrue(key in INSTALL_SCHEMES) + schemes = get_scheme_names() + for key in ('nt_user', 'posix_user', 'os2_home'): + self.assertTrue(key in schemes) dist = Distribution({'name': 'xx'}) cmd = install(dist) - # making sure the user option is there options = [name for name, short, lable in cmd.user_options] @@ -185,7 +200,7 @@ class InstallTestCase(support.TempdirManager, with open(cmd.record) as f: self.assertEquals(len(f.readlines()), 1) - def test_debug_mode(self): + def _test_debug_mode(self): # this covers the code called when DEBUG is set old_logs_len = len(self.logs) install_module.DEBUG = True diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 498714de..9013220f 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -4,7 +4,6 @@ import test import unittest from distutils import sysconfig -from distutils.ccompiler import get_default_compiler from distutils.tests import support from test.test_support import TESTFN @@ -27,10 +26,6 @@ class SysconfigTestCase(support.EnvironGuard, elif os.path.isdir(path): shutil.rmtree(path) - def test_get_config_h_filename(self): - config_h = sysconfig.get_config_h_filename() - self.assertTrue(os.path.isfile(config_h), config_h) - def test_get_python_lib(self): lib_dir = sysconfig.get_python_lib() # XXX doesn't work on Linux when Python was never installed before @@ -38,6 +33,9 @@ class SysconfigTestCase(support.EnvironGuard, # test for pythonxx.lib? self.assertNotEqual(sysconfig.get_python_lib(), sysconfig.get_python_lib(prefix=TESTFN)) + _sysconfig = __import__('sysconfig') + res = sysconfig.get_python_lib(True, True) + self.assertEquals(_sysconfig.get_path('platstdlib'), res) def test_get_python_inc(self): inc_dir = sysconfig.get_python_inc() @@ -48,31 +46,6 @@ class SysconfigTestCase(support.EnvironGuard, python_h = os.path.join(inc_dir, "Python.h") self.assertTrue(os.path.isfile(python_h), python_h) - def test_get_config_vars(self): - cvars = sysconfig.get_config_vars() - self.assertTrue(isinstance(cvars, dict)) - self.assertTrue(cvars) - - def test_customize_compiler(self): - - # not testing if default compiler is not unix - if get_default_compiler() != 'unix': - return - - os.environ['AR'] = 'my_ar' - os.environ['ARFLAGS'] = '-arflags' - - # make sure AR gets caught - class compiler: - compiler_type = 'unix' - - def set_executables(self, **kw): - self.exes = kw - - comp = compiler() - sysconfig.customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') - def test_parse_makefile_base(self): self.makefile = test.test_support.TESTFN fd = open(self.makefile, 'w') diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 008ae5d0..6976dd55 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -1,8 +1,8 @@ """Tests for distutils.unixccompiler.""" import sys import unittest +import sysconfig -from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler class UnixCCompilerTestCase(unittest.TestCase): diff --git a/tests/test_util.py b/tests/test_util.py index 80c58009..50f9ac17 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -6,15 +6,14 @@ from copy import copy from StringIO import StringIO import subprocess +from sysconfig import get_config_vars, get_platform from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError -from distutils.util import (get_platform, convert_path, change_root, +from distutils.util import (convert_path, change_root, check_environ, split_quoted, strtobool, rfc822_escape, get_compiler_versions, _find_exe_version, _MAC_OS_X_LD_VERSION, byte_compile) from distutils import util -from distutils.sysconfig import get_config_vars -from distutils import sysconfig from distutils.tests import support from distutils.version import LooseVersion @@ -44,7 +43,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive - self._config_vars = copy(sysconfig._config_vars) + #self._config_vars = copy(sysconfig._config_vars) # patching os.uname if hasattr(os, 'uname'): @@ -78,7 +77,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): os.uname = self.uname else: del os.uname - sysconfig._config_vars = copy(self._config_vars) + #sysconfig._config_vars = copy(self._config_vars) util.find_executable = self.old_find_executable subprocess.Popen = self.old_popen sys.old_stdout = self.old_stdout @@ -91,7 +90,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): def _get_uname(self): return self._uname - def test_get_platform(self): + def _test_get_platform(self): # windows XP, 32bits os.name = 'nt' @@ -119,26 +118,6 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') sys.platform = 'darwin' - - self._set_uname(('Darwin', 'macziade', '8.11.1', - ('Darwin Kernel Version 8.11.1: ' - 'Wed Oct 10 18:23:28 PDT 2007; ' - 'root:xnu-792.25.20~1/RELEASE_I386'), 'PowerPC')) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' - - get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' - '-fwrapv -O3 -Wall -Wstrict-prototypes') - - maxint = sys.maxint - try: - sys.maxint = 2147483647 - self.assertEquals(get_platform(), 'macosx-10.3-ppc') - sys.maxint = 9223372036854775807 - self.assertEquals(get_platform(), 'macosx-10.3-ppc64') - finally: - sys.maxint = maxint - - self._set_uname(('Darwin', 'macziade', '8.11.1', ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' @@ -148,15 +127,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') - maxint = sys.maxint - try: - sys.maxint = 2147483647 - self.assertEquals(get_platform(), 'macosx-10.3-i386') - sys.maxint = 9223372036854775807 - self.assertEquals(get_platform(), 'macosx-10.3-x86_64') - finally: - sys.maxint = maxint - + self.assertEquals(get_platform(), 'macosx-10.3-i386') # macbook with fat binaries (fat, universal or fat64) os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' @@ -201,7 +172,6 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) - # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' diff --git a/unixccompiler.py b/unixccompiler.py index 67adcfcf..8fe1a6a1 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -18,7 +18,6 @@ __revision__ = "$Id$" import os, sys from types import StringType, NoneType -from distutils import sysconfig from distutils.dep_util import newer from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options @@ -26,6 +25,7 @@ from distutils.errors import \ DistutilsExecError, CompileError, LibError, LinkError from distutils import log + # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's # Makefile and live with it. Is this adequate? If not, we might @@ -75,7 +75,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): if 'ARCHFLAGS' in os.environ and not stripArch: # User specified different -arch flags in the environ, - # see also distutils.sysconfig + # see also the sysconfig compiler_so = compiler_so + os.environ['ARCHFLAGS'].split() if stripSysroot: @@ -283,7 +283,9 @@ class UnixCCompiler(CCompiler): # this time, there's no way to determine this information from # the configuration data stored in the Python installation, so # we use this hack. - compiler = os.path.basename(sysconfig.get_config_var("CC")) + _sysconfig = __import__('sysconfig') + + compiler = os.path.basename(_sysconfig.get_config_var("CC")) if sys.platform[:6] == "darwin": # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir @@ -298,7 +300,7 @@ class UnixCCompiler(CCompiler): # use it anyway. Since distutils has always passed in # -Wl whenever gcc was used in the past it is probably # safest to keep doing so. - if sysconfig.get_config_var("GNULD") == "yes": + if _sysconfig.get_config_var("GNULD") == "yes": # GNU ld needs an extra option to get a RUNPATH # instead of just an RPATH. return "-Wl,--enable-new-dtags,-R" + dir diff --git a/util.py b/util.py index f4bb0633..18d0d2ef 100644 --- a/util.py +++ b/util.py @@ -15,173 +15,7 @@ from distutils import log from distutils.version import LooseVersion from distutils.errors import DistutilsByteCompileError -def get_platform(): - """Return a string that identifies the current platform. - - This is used mainly to distinguish platform-specific build directories and - platform-specific built distributions. Typically includes the OS name - and version and the architecture (as supplied by 'os.uname()'), - although the exact information included depends on the OS; eg. for IRIX - the architecture isn't particularly important (IRIX only runs on SGI - hardware), but for Linux the kernel version isn't particularly - important. - - Examples of returned values: - linux-i586 - linux-alpha (?) - solaris-2.6-sun4u - irix-5.3 - irix64-6.2 - - Windows will return one of: - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) - win-ia64 (64bit Windows on Itanium) - win32 (all others - specifically, sys.platform is returned) - - For other non-POSIX platforms, currently just returns 'sys.platform'. - """ - if os.name == 'nt': - # sniff sys.version for architecture. - prefix = " bit (" - i = sys.version.find(prefix) - if i == -1: - return sys.platform - j = sys.version.find(")", i) - look = sys.version[i+len(prefix):j].lower() - if look == 'amd64': - return 'win-amd64' - if look == 'itanium': - return 'win-ia64' - return sys.platform - - if os.name != "posix" or not hasattr(os, 'uname'): - # XXX what about the architecture? NT is Intel or Alpha, - # Mac OS is M68k or PPC, etc. - return sys.platform - - # Try to distinguish various flavours of Unix - - (osname, host, release, version, machine) = os.uname() - - # Convert the OS name to lowercase, remove '/' characters - # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") - osname = osname.lower().replace('/', '') - machine = machine.replace(' ', '_') - machine = machine.replace('/', '-') - - if osname[:5] == "linux": - # At least on Linux/Intel, 'machine' is the processor -- - # i386, etc. - # XXX what about Alpha, SPARC, etc? - return "%s-%s" % (osname, machine) - elif osname[:5] == "sunos": - if release[0] >= "5": # SunOS 5 == Solaris 2 - osname = "solaris" - release = "%d.%s" % (int(release[0]) - 3, release[2:]) - # fall through to standard osname-release-machine representation - elif osname[:4] == "irix": # could be "irix64"! - return "%s-%s" % (osname, release) - elif osname[:3] == "aix": - return "%s-%s.%s" % (osname, version, release) - elif osname[:6] == "cygwin": - osname = "cygwin" - rel_re = re.compile (r'[\d.]+') - m = rel_re.match(release) - if m: - release = m.group() - elif osname[:6] == "darwin": - # - # For our purposes, we'll assume that the system version from - # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set - # to. This makes the compatibility story a bit more sane because the - # machine is going to compile and link as if it were - # MACOSX_DEPLOYMENT_TARGET. - from distutils.sysconfig import get_config_vars - cfgvars = get_config_vars() - - macver = os.environ.get('MACOSX_DEPLOYMENT_TARGET') - if not macver: - macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') - - if 1: - # Always calculate the release of the running machine, - # needed to determine if we can build fat binaries or not. - - macrelease = macver - # Get the system version. Reading this plist is a documented - # way to get the system version (see the documentation for - # the Gestalt Manager) - try: - f = open('/System/Library/CoreServices/SystemVersion.plist') - except IOError: - # We're on a plain darwin box, fall back to the default - # behaviour. - pass - else: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - f.close() - if m is not None: - macrelease = '.'.join(m.group(1).split('.')[:2]) - # else: fall back to the default behaviour - - if not macver: - macver = macrelease - - if macver: - from distutils.sysconfig import get_config_vars - release = macver - osname = "macosx" - - if (macrelease + '.') >= '10.4.' and \ - '-arch' in get_config_vars().get('CFLAGS', '').strip(): - # The universal build will build fat binaries, but not on - # systems before 10.4 - # - # Try to detect 4-way universal builds, those have machine-type - # 'universal' instead of 'fat'. - - machine = 'fat' - cflags = get_config_vars().get('CFLAGS') - - archs = re.findall('-arch\s+(\S+)', cflags) - archs.sort() - archs = tuple(archs) - - if len(archs) == 1: - machine = archs[0] - elif archs == ('i386', 'ppc'): - machine = 'fat' - elif archs == ('i386', 'x86_64'): - machine = 'intel' - elif archs == ('i386', 'ppc', 'x86_64'): - machine = 'fat3' - elif archs == ('ppc64', 'x86_64'): - machine = 'fat64' - elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): - machine = 'universal' - else: - raise ValueError( - "Don't know machine value for archs=%r"%(archs,)) - - elif machine == 'i386': - # On OSX the machine type returned by uname is always the - # 32-bit variant, even if the executable architecture is - # the 64-bit variant - if sys.maxint >= 2**32: - machine = 'x86_64' - - elif machine in ('PowerPC', 'Power_Macintosh'): - # Pick a sane name for the PPC architecture. - machine = 'ppc' - - # See 'i386' case - if sys.maxint >= 2**32: - machine = 'ppc64' - - return "%s-%s-%s" % (osname, release, machine) - +_sysconfig = __import__('sysconfig') def convert_path(pathname): """Return 'pathname' as a name that will work on the native filesystem. @@ -269,7 +103,7 @@ def check_environ(): os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] if 'PLAT' not in os.environ: - os.environ['PLAT'] = get_platform() + os.environ['PLAT'] = _sysconfig.get_platform() _environ_checked = 1 -- cgit v1.2.1 From 36c50d5202caff909797ff127a7b830c90eac84a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 24 Jan 2010 00:33:32 +0000 Subject: Fixed #7748: now upload and register commands don't need to force the encoding anymore : DistributionMetada returns utf8 strings --- command/register.py | 1 - command/upload.py | 2 +- dist.py | 24 ++++++++++++++---------- tests/test_register.py | 7 +++---- tests/test_upload.py | 6 ++++-- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/command/register.py b/command/register.py index fb547c93..dc089902 100644 --- a/command/register.py +++ b/command/register.py @@ -266,7 +266,6 @@ Your selection [default 1]: ''', log.INFO) if type(value) not in (type([]), type( () )): value = [value] for value in value: - value = unicode(value).encode("utf-8") body.write(sep_boundary) body.write('\nContent-Disposition: form-data; name="%s"'%key) body.write("\n\n") diff --git a/command/upload.py b/command/upload.py index 3e18aeaa..18a10a0b 100644 --- a/command/upload.py +++ b/command/upload.py @@ -145,7 +145,7 @@ class upload(PyPIRCCommand): value = value[1] else: fn = "" - value = str(value) + body.write(sep_boundary) body.write('\nContent-Disposition: form-data; name="%s"'%key) body.write(fn) diff --git a/dist.py b/dist.py index f20a92a2..5dbdaef1 100644 --- a/dist.py +++ b/dist.py @@ -1139,16 +1139,19 @@ class DistributionMetadata: self._write_list(file, 'Obsoletes', self.get_obsoletes()) def _write_field(self, file, name, value): - if isinstance(value, unicode): - value = value.encode(PKG_INFO_ENCODING) - else: - value = str(value) - file.write('%s: %s\n' % (name, value)) + file.write('%s: %s\n' % (name, self._encode_field(value))) def _write_list (self, file, name, values): for value in values: self._write_field(file, name, value) + def _encode_field(self, value): + if value is None: + return None + if isinstance(value, unicode): + return value.encode(PKG_INFO_ENCODING) + return str(value) + # -- Metadata query methods ---------------------------------------- def get_name(self): @@ -1161,19 +1164,20 @@ class DistributionMetadata: return "%s-%s" % (self.get_name(), self.get_version()) def get_author(self): - return self.author or "UNKNOWN" + return self._encode_field(self.author) or "UNKNOWN" def get_author_email(self): return self.author_email or "UNKNOWN" def get_maintainer(self): - return self.maintainer or "UNKNOWN" + return self._encode_field(self.maintainer) or "UNKNOWN" def get_maintainer_email(self): return self.maintainer_email or "UNKNOWN" def get_contact(self): - return self.maintainer or self.author or "UNKNOWN" + return (self._encode_field(self.maintainer) or + self._encode_field(self.author) or "UNKNOWN") def get_contact_email(self): return self.maintainer_email or self.author_email or "UNKNOWN" @@ -1186,10 +1190,10 @@ class DistributionMetadata: get_licence = get_license def get_description(self): - return self.description or "UNKNOWN" + return self._encode_field(self.description) or "UNKNOWN" def get_long_description(self): - return self.long_description or "UNKNOWN" + return self._encode_field(self.long_description) or "UNKNOWN" def get_keywords(self): return self.keywords or [] diff --git a/tests/test_register.py b/tests/test_register.py index ada77a01..370d6598 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -1,4 +1,5 @@ """Tests for distutils.command.register.""" +# -*- encoding: utf8 -*- import sys import os import unittest @@ -136,9 +137,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(self.conn.reqs, 2) req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) - - self.assertEquals(req1['Content-length'], '1374') - self.assertEquals(req2['Content-length'], '1374') + self.assertEquals(req2['Content-length'], req1['Content-length']) self.assertTrue('xxx' in self.conn.reqs[1].data) def test_password_not_in_file(self): @@ -210,7 +209,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): # metadata are OK but long_description is broken metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', + 'author_email': u'éxéxé', 'name': 'xxx', 'version': 'xxx', 'long_description': 'title\n==\n\ntext'} diff --git a/tests/test_upload.py b/tests/test_upload.py index 8f6701cc..3ae89498 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -1,4 +1,5 @@ """Tests for distutils.command.upload.""" +# -*- encoding: utf8 -*- import sys import os import unittest @@ -107,14 +108,15 @@ class uploadTestCase(PyPIRCCommandTestCase): self.write_file(self.rc, PYPIRC_LONG_PASSWORD) # lets run it - pkg_dir, dist = self.create_dist(dist_files=dist_files) + pkg_dir, dist = self.create_dist(dist_files=dist_files, author=u'dédé') cmd = upload(dist) cmd.ensure_finalized() cmd.run() # what did we send ? + self.assertIn('dédé', self.last_open.req.data) headers = dict(self.last_open.req.headers) - self.assertEquals(headers['Content-length'], '2086') + self.assertEquals(headers['Content-length'], '2085') self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) self.assertEquals(self.last_open.req.get_method(), 'POST') self.assertEquals(self.last_open.req.get_full_url(), -- cgit v1.2.1 From a147462878fb6e26ffb40ff3d1b89d03803a7f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 24 Jan 2010 00:57:20 +0000 Subject: Merged revisions 77717 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77717 | tarek.ziade | 2010-01-24 01:33:32 +0100 (Sun, 24 Jan 2010) | 1 line Fixed #7748: now upload and register commands don't need to force the encoding anymore : DistributionMetada returns utf8 strings ........ --- command/register.py | 1 - command/upload.py | 2 +- dist.py | 27 ++++++++++++++------------- tests/test_upload.py | 6 ++++-- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/command/register.py b/command/register.py index bf7be960..f531d0fc 100644 --- a/command/register.py +++ b/command/register.py @@ -261,7 +261,6 @@ Your selection [default 1]: ''', log.INFO) if type(value) not in (type([]), type( () )): value = [value] for value in value: - value = unicode(value).encode("utf-8") body.write(sep_boundary) body.write('\nContent-Disposition: form-data; name="%s"'%key) body.write("\n\n") diff --git a/command/upload.py b/command/upload.py index 9f1aae6a..93727aed 100644 --- a/command/upload.py +++ b/command/upload.py @@ -133,7 +133,7 @@ class upload(PyPIRCCommand): value = value[1] else: fn = "" - value = str(value) + body.write(sep_boundary) body.write('\nContent-Disposition: form-data; name="%s"'%key) body.write(fn) diff --git a/dist.py b/dist.py index c15ca977..30260f39 100644 --- a/dist.py +++ b/dist.py @@ -1114,18 +1114,20 @@ class DistributionMetadata: self._write_list(file, 'Obsoletes', self.get_obsoletes()) def _write_field(self, file, name, value): - - if isinstance(value, unicode): - value = value.encode(PKG_INFO_ENCODING) - else: - value = str(value) - file.write('%s: %s\n' % (name, value)) + file.write('%s: %s\n' % (name, self._encode_field(value))) def _write_list (self, file, name, values): for value in values: self._write_field(file, name, value) + def _encode_field(self, value): + if value is None: + return None + if isinstance(value, unicode): + return value.encode(PKG_INFO_ENCODING) + return str(value) + # -- Metadata query methods ---------------------------------------- def get_name (self): @@ -1138,21 +1140,20 @@ class DistributionMetadata: return "%s-%s" % (self.get_name(), self.get_version()) def get_author(self): - return self.author or "UNKNOWN" + return self._encode_field(self.author) or "UNKNOWN" def get_author_email(self): return self.author_email or "UNKNOWN" def get_maintainer(self): - return self.maintainer or "UNKNOWN" + return self._encode_field(self.maintainer) or "UNKNOWN" def get_maintainer_email(self): return self.maintainer_email or "UNKNOWN" def get_contact(self): - return (self.maintainer or - self.author or - "UNKNOWN") + return (self._encode_field(self.maintainer) or + self._encode_field(self.author) or "UNKNOWN") def get_contact_email(self): return (self.maintainer_email or @@ -1167,10 +1168,10 @@ class DistributionMetadata: get_licence = get_license def get_description(self): - return self.description or "UNKNOWN" + return self._encode_field(self.description) or "UNKNOWN" def get_long_description(self): - return self.long_description or "UNKNOWN" + return self._encode_field(self.long_description) or "UNKNOWN" def get_keywords(self): return self.keywords or [] diff --git a/tests/test_upload.py b/tests/test_upload.py index 322beb77..382697fb 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -1,4 +1,5 @@ """Tests for distutils.command.upload.""" +# -*- encoding: utf8 -*- import sys import os import unittest @@ -95,7 +96,7 @@ class uploadTestCase(PyPIRCCommandTestCase): self.write_file(self.rc, PYPIRC_LONG_PASSWORD) # lets run it - pkg_dir, dist = self.create_dist(dist_files=dist_files) + pkg_dir, dist = self.create_dist(dist_files=dist_files, author=u'dédé') cmd = upload(dist) cmd.ensure_finalized() cmd.run() @@ -104,7 +105,8 @@ class uploadTestCase(PyPIRCCommandTestCase): res = _CONNECTIONS[-1] headers = res.headers - self.assertEquals(headers['Content-length'], '2086') + self.assert_('dédé' in res.body) + self.assertEquals(headers['Content-length'], '2085') self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) method, request = res.requests[-1] -- cgit v1.2.1 From b9018c0a933cdae3367543a28191514a8856d950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 26 Jan 2010 17:20:37 +0000 Subject: fixed bdist_msi imports and added a test module for distutils.command.bdist_msi --- command/bdist_msi.py | 6 +++--- tests/test_bdist_msi.py | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 tests/test_bdist_msi.py diff --git a/command/bdist_msi.py b/command/bdist_msi.py index f3791bed..5ecf73b9 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -6,15 +6,15 @@ """ Implements the bdist_msi command. """ - import sys, os +from sysconfig import get_python_version, get_platform + from distutils.core import Command from distutils.dir_util import remove_tree -from distutils.sysconfig import get_python_version from distutils.version import StrictVersion from distutils.errors import DistutilsOptionError -from distutils.util import get_platform from distutils import log + import msilib from msilib import schema, sequence, text from msilib import Directory, Feature, Dialog, add_data diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py new file mode 100644 index 00000000..ba2d3e19 --- /dev/null +++ b/tests/test_bdist_msi.py @@ -0,0 +1,23 @@ +"""Tests for distutils.command.bdist_msi.""" +import unittest +import sys + +from distutils.tests import support + +@unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") +class BDistMSITestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_minial(self): + # minimal test XXX need more tests + from distutils.command.bdist_msi import bdist_msi + pkg_pth, dist = self.create_dist() + cmd = bdist_msi(dist) + cmd.ensure_finalized() + +def test_suite(): + return unittest.makeSuite(BDistMSITestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From be0a428e7d61e0dc61e859e1819f207490b7d5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 26 Jan 2010 21:21:54 +0000 Subject: reintroduced the names in Distutils for APIs that were relocated --- sysconfig.py | 9 +++++++++ util.py | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index bb8b5125..2d92dabf 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -21,6 +21,15 @@ from warnings import warn # to avoid this module to shadow it _sysconfig = __import__('sysconfig') +# names defined here to keep backward compatibility +# for APIs that were relocated +get_python_version = _sysconfig.get_python_version +get_config_h_filename = _sysconfig.get_config_h_filename +parse_config_h = _sysconfig.parse_config_h +get_config_vars = _sysconfig.get_config_vars +get_config_var = _sysconfig.get_config_var +from distutils.ccompiler import customize_compiler + _DEPRECATION_MSG = ("distutils.sysconfig.%s is deprecated. " "Use the APIs provided by the sysconfig module instead") diff --git a/util.py b/util.py index 18d0d2ef..8650d450 100644 --- a/util.py +++ b/util.py @@ -17,6 +17,10 @@ from distutils.errors import DistutilsByteCompileError _sysconfig = __import__('sysconfig') +# kept for backward compatibility +# since this API was relocated +get_platform = _sysconfig.get_platform + def convert_path(pathname): """Return 'pathname' as a name that will work on the native filesystem. -- cgit v1.2.1 From 389afeb0a49561ac5df341c508571b65349e3035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 26 Jan 2010 22:46:15 +0000 Subject: added local get_platform/set_platform APIs in distutils.sysconfig --- command/bdist.py | 2 +- command/bdist_dumb.py | 3 +- command/bdist_msi.py | 3 +- command/bdist_wininst.py | 3 +- command/build.py | 2 +- command/build_ext.py | 4 +- command/install.py | 5 +-- msvc9compiler.py | 10 ++--- tests/test_util.py | 99 +++--------------------------------------------- util.py | 23 +++++++++-- 10 files changed, 42 insertions(+), 112 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 764024a0..d7910b14 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,8 +6,8 @@ distribution).""" __revision__ = "$Id$" import os -from sysconfig import get_platform +from distutils.util import get_platform from distutils.core import Command from distutils.errors import DistutilsPlatformError, DistutilsOptionError diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index f7331d59..7c60d39b 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -8,8 +8,9 @@ __revision__ = "$Id$" import os -from sysconfig import get_python_version, get_platform +from sysconfig import get_python_version +from distutils.util import get_platform from distutils.core import Command from distutils.dir_util import remove_tree, ensure_relative from distutils.errors import DistutilsPlatformError diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 5ecf73b9..72578498 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -7,13 +7,14 @@ Implements the bdist_msi command. """ import sys, os -from sysconfig import get_python_version, get_platform +from sysconfig import get_python_version from distutils.core import Command from distutils.dir_util import remove_tree from distutils.version import StrictVersion from distutils.errors import DistutilsOptionError from distutils import log +from distutils.util import get_platform import msilib from msilib import schema, sequence, text diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 0ece0306..88c0532f 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -9,12 +9,13 @@ import sys import os import string -from sysconfig import get_python_version, get_platform +from sysconfig import get_python_version from distutils.core import Command from distutils.dir_util import remove_tree from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils import log +from distutils.util import get_platform class bdist_wininst (Command): diff --git a/command/build.py b/command/build.py index b84e40d6..f84bf359 100644 --- a/command/build.py +++ b/command/build.py @@ -5,8 +5,8 @@ Implements the Distutils 'build' command.""" __revision__ = "$Id$" import sys, os -from sysconfig import get_platform +from distutils.util import get_platform from distutils.core import Command from distutils.errors import DistutilsOptionError diff --git a/command/build_ext.py b/command/build_ext.py index 13bd030e..420d7f17 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -9,14 +9,12 @@ __revision__ = "$Id$" import sys, os, re from warnings import warn -from sysconfig import get_platform - +from distutils.util import get_platform from distutils.core import Command from distutils.errors import * from distutils.ccompiler import customize_compiler from distutils.dep_util import newer_group from distutils.extension import Extension - from distutils import log # this keeps compatibility from 2.3 to 2.5 diff --git a/command/install.py b/command/install.py index 68d6227a..b5336fef 100644 --- a/command/install.py +++ b/command/install.py @@ -7,15 +7,14 @@ __revision__ = "$Id$" import sys import os -from sysconfig import (get_config_vars, get_platform, get_paths, get_path, - get_config_var) +from sysconfig import get_config_vars, get_paths, get_path, get_config_var from distutils import log from distutils.core import Command from distutils.debug import DEBUG from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file -from distutils.util import convert_path, change_root +from distutils.util import convert_path, change_root, get_platform from distutils.errors import DistutilsOptionError def _subst_vars(s, local_vars): diff --git a/msvc9compiler.py b/msvc9compiler.py index 6ac24f83..41d67faf 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -23,9 +23,9 @@ from distutils.errors import (DistutilsExecError, DistutilsPlatformError, CompileError, LibError, LinkError) from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log -import _winreg +from distutils.util import get_platform -_sysconfig = __import__('sysconfig') +import _winreg RegOpenKeyEx = _winreg.OpenKeyEx RegEnumKey = _winreg.EnumKey @@ -327,7 +327,7 @@ class MSVCCompiler(CCompiler) : # multi-init means we would need to check platform same each time... assert not self.initialized, "don't init multiple times" if plat_name is None: - plat_name = _sysconfig.get_platform() + plat_name = get_platform() # sanity check for platforms to prevent obscure errors later. ok_plats = 'win32', 'win-amd64', 'win-ia64' if plat_name not in ok_plats: @@ -348,12 +348,12 @@ class MSVCCompiler(CCompiler) : # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross # compile use 'x86' (ie, it runs the x86 compiler directly) # No idea how itanium handles this, if at all. - if plat_name == _sysconfig.get_platform() or plat_name == 'win32': + if plat_name == get_platform() or plat_name == 'win32': # native build or cross-compile to win32 plat_spec = PLAT_TO_VCVARS[plat_name] else: # cross compile from win32 -> some 64bit - plat_spec = PLAT_TO_VCVARS[_sysconfig.get_platform()] + '_' + \ + plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ PLAT_TO_VCVARS[plat_name] vc_env = query_vcvarsall(VERSION, plat_spec) diff --git a/tests/test_util.py b/tests/test_util.py index 50f9ac17..348b42be 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -90,99 +90,12 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): def _get_uname(self): return self._uname - def _test_get_platform(self): - - # windows XP, 32bits - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Intel)]') - sys.platform = 'win32' - self.assertEquals(get_platform(), 'win32') - - # windows XP, amd64 - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Amd64)]') - sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-amd64') - - # windows XP, itanium - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Itanium)]') - sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-ia64') - - # macbook - os.name = 'posix' - sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' - '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') - sys.platform = 'darwin' - self._set_uname(('Darwin', 'macziade', '8.11.1', - ('Darwin Kernel Version 8.11.1: ' - 'Wed Oct 10 18:23:28 PDT 2007; ' - 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' - - get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' - '-fwrapv -O3 -Wall -Wstrict-prototypes') - - self.assertEquals(get_platform(), 'macosx-10.3-i386') - - # macbook with fat binaries (fat, universal or fat64) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' - get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEquals(get_platform(), 'macosx-10.4-fat') - - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEquals(get_platform(), 'macosx-10.4-intel') - - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat3') - - get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-universal') - - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEquals(get_platform(), 'macosx-10.4-fat64') - - for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): - get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3'%(arch,)) - - self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) - - # linux debian sarge - os.name = 'posix' - sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' - '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]') - sys.platform = 'linux2' - self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', - '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) - - self.assertEquals(get_platform(), 'linux-i686') - - # XXX more platforms to tests here + def test_get_platform(self): + platform = util.get_platform() + self.assertEquals(platform, get_platform()) + util.set_platform('MyOwnPlatform') + self.assertEquals('MyOwnPlatform', util.get_platform()) + util.set_platform(platform) def test_convert_path(self): # linux/mac diff --git a/util.py b/util.py index 8650d450..fbd3a672 100644 --- a/util.py +++ b/util.py @@ -16,10 +16,27 @@ from distutils.version import LooseVersion from distutils.errors import DistutilsByteCompileError _sysconfig = __import__('sysconfig') +_PLATFORM = None -# kept for backward compatibility -# since this API was relocated -get_platform = _sysconfig.get_platform +def get_platform(): + """Return a string that identifies the current platform. + + By default, will return the value returned by sysconfig.get_platform(), + but it can be changed by calling set_platform(). + """ + global _PLATFORM + if _PLATFORM is None: + _PLATFORM = _sysconfig.get_platform() + return _PLATFORM + +def set_platform(identifier): + """Sets the platform string identifier returned by get_platform(). + + Note that this change doesn't impact the value returned by + sysconfig.get_platform() and is local to Distutils + """ + global _PLATFORM + _PLATFORM = identifier def convert_path(pathname): """Return 'pathname' as a name that will work on the native filesystem. -- cgit v1.2.1 From 43788cbefa5bf8f9c6fde80b8a25a4c5a35505ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 29 Jan 2010 11:41:03 +0000 Subject: Merged revisions 77704,77752 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77704 | tarek.ziade | 2010-01-23 10:23:15 +0100 (Sat, 23 Jan 2010) | 1 line taking sysconfig out of distutils ........ r77752 | tarek.ziade | 2010-01-26 00:19:56 +0100 (Tue, 26 Jan 2010) | 1 line switched the call order so this call works without suffering from issue #7774 ........ --- ccompiler.py | 52 ++++ command/bdist.py | 2 +- command/bdist_dumb.py | 4 +- command/bdist_wininst.py | 4 +- command/build.py | 3 +- command/build_clib.py | 2 +- command/build_ext.py | 32 +-- command/build_scripts.py | 10 +- command/config.py | 2 +- command/install.py | 163 +++--------- core.py | 2 +- cygwinccompiler.py | 4 +- extension.py | 12 +- msvc9compiler.py | 10 +- sysconfig.py | 562 ++++++------------------------------------ tests/support.py | 8 + tests/test_build.py | 2 +- tests/test_build_clib.py | 3 +- tests/test_build_ext.py | 17 +- tests/test_build_scripts.py | 6 +- tests/test_ccompiler.py | 26 +- tests/test_cygwinccompiler.py | 3 +- tests/test_extension.py | 2 + tests/test_install.py | 53 ++-- tests/test_sysconfig.py | 36 +-- tests/test_unixccompiler.py | 2 +- tests/test_util.py | 14 +- unixccompiler.py | 10 +- util.py | 170 +------------ 29 files changed, 310 insertions(+), 906 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 5a6bef44..1a4e8fb2 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -18,6 +18,58 @@ from distutils.dep_util import newer_group from distutils.util import split_quoted, execute from distutils import log +_sysconfig = __import__('sysconfig') + +def customize_compiler(compiler): + """Do any platform-specific customization of a CCompiler instance. + + Mainly needed on Unix, so we can plug in the information that + varies across Unices and is stored in Python's Makefile. + """ + if compiler.compiler_type == "unix": + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ + _sysconfig.get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', + 'CCSHARED', 'LDSHARED', 'SO', 'AR', + 'ARFLAGS') + + if 'CC' in os.environ: + cc = os.environ['CC'] + if 'CXX' in os.environ: + cxx = os.environ['CXX'] + if 'LDSHARED' in os.environ: + ldshared = os.environ['LDSHARED'] + if 'CPP' in os.environ: + cpp = os.environ['CPP'] + else: + cpp = cc + " -E" # not always + if 'LDFLAGS' in os.environ: + ldshared = ldshared + ' ' + os.environ['LDFLAGS'] + if 'CFLAGS' in os.environ: + cflags = opt + ' ' + os.environ['CFLAGS'] + ldshared = ldshared + ' ' + os.environ['CFLAGS'] + if 'CPPFLAGS' in os.environ: + cpp = cpp + ' ' + os.environ['CPPFLAGS'] + cflags = cflags + ' ' + os.environ['CPPFLAGS'] + ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] + else: + archiver = ar + ' ' + ar_flags + + cc_cmd = cc + ' ' + cflags + compiler.set_executables( + preprocessor=cpp, + compiler=cc_cmd, + compiler_so=cc_cmd + ' ' + ccshared, + compiler_cxx=cxx, + linker_so=ldshared, + linker_exe=cc, + archiver=archiver) + + compiler.shared_lib_extension = so_ext + class CCompiler: """Abstract base class to define the interface that must be implemented by real compiler classes. Also has some utility methods used by diff --git a/command/bdist.py b/command/bdist.py index dd202ff4..a79982b6 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,10 +6,10 @@ distribution).""" __revision__ = "$Id$" import os +from sysconfig import get_platform from distutils.core import Command from distutils.errors import DistutilsPlatformError, DistutilsOptionError -from distutils.util import get_platform def show_formats(): diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 49fd653a..c16125f2 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -8,11 +8,11 @@ __revision__ = "$Id$" import os +from sysconfig import get_python_version, get_platform + from distutils.core import Command -from distutils.util import get_platform from distutils.dir_util import remove_tree, ensure_relative from distutils.errors import DistutilsPlatformError -from distutils.sysconfig import get_python_version from distutils import log class bdist_dumb(Command): diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 0b7044a0..2cf28234 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -8,11 +8,11 @@ __revision__ = "$Id$" import sys import os +from sysconfig import get_python_version, get_platform + from distutils.core import Command -from distutils.util import get_platform from distutils.dir_util import remove_tree from distutils.errors import DistutilsOptionError, DistutilsPlatformError -from distutils.sysconfig import get_python_version from distutils import log class bdist_wininst(Command): diff --git a/command/build.py b/command/build.py index 715621e1..d7b0e3c5 100644 --- a/command/build.py +++ b/command/build.py @@ -5,9 +5,10 @@ Implements the Distutils 'build' command.""" __revision__ = "$Id$" import sys, os +from sysconfig import get_platform + from distutils.core import Command from distutils.errors import DistutilsOptionError -from distutils.util import get_platform def show_compilers(): from distutils.ccompiler import show_compilers diff --git a/command/build_clib.py b/command/build_clib.py index 12bf1d2f..4c6443ca 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -19,7 +19,7 @@ __revision__ = "$Id$" import os from distutils.core import Command from distutils.errors import DistutilsSetupError -from distutils.sysconfig import customize_compiler +from distutils.ccompiler import customize_compiler from distutils import log def show_compilers(): diff --git a/command/build_ext.py b/command/build_ext.py index de980bd8..39d37dab 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -9,13 +9,14 @@ __revision__ = "$Id$" import sys, os, re from warnings import warn +from sysconfig import get_platform + from distutils.core import Command -from distutils.errors import (CCompilerError, DistutilsError, CompileError, - DistutilsSetupError, DistutilsPlatformError) -from distutils.sysconfig import customize_compiler, get_python_version +from distutils.errors import * +from distutils.ccompiler import customize_compiler from distutils.dep_util import newer_group from distutils.extension import Extension -from distutils.util import get_platform + from distutils import log # this keeps compatibility from 2.3 to 2.5 @@ -172,8 +173,7 @@ class build_ext(Command): self.user = None def finalize_options(self): - from distutils import sysconfig - + _sysconfig = __import__('sysconfig') self.set_undefined_options('build', ('build_lib', 'build_lib'), ('build_temp', 'build_temp'), @@ -190,8 +190,8 @@ class build_ext(Command): # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. - py_include = sysconfig.get_python_inc() - plat_py_include = sysconfig.get_python_inc(plat_specific=1) + py_include = _sysconfig.get_path('include') + plat_py_include = _sysconfig.get_path('platinclude') if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if isinstance(self.include_dirs, str): @@ -269,7 +269,7 @@ class build_ext(Command): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", - "python" + get_python_version(), + "python" + _sysconfig.get_python_version(), "config")) else: # building python standard extensions @@ -277,13 +277,13 @@ class build_ext(Command): # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs - sysconfig.get_config_var('Py_ENABLE_SHARED') + _sysconfig.get_config_var('Py_ENABLE_SHARED') if ((sys.platform.startswith('linux') or sys.platform.startswith('gnu') or sys.platform.startswith('sunos')) - and sysconfig.get_config_var('Py_ENABLE_SHARED')): + and _sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions - self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) + self.library_dirs.append(_sysconfig.get_config_var('LIBDIR')) else: # building python standard extensions self.library_dirs.append('.') @@ -712,13 +712,13 @@ class build_ext(Command): of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ - from distutils.sysconfig import get_config_var + _sysconfig = __import__('sysconfig') ext_path = ext_name.split('.') # OS/2 has an 8 character module (extension) limit :-( if os.name == "os2": ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] # extensions in debug_mode are named 'module_d.pyd' under windows - so_ext = get_config_var('SO') + so_ext = _sysconfig.get_config_var('SO') if os.name == 'nt' and self.debug: return os.path.join(*ext_path) + '_d' + so_ext return os.path.join(*ext_path) + so_ext @@ -781,8 +781,8 @@ class build_ext(Command): # Don't use the default code below return ext.libraries else: - from distutils import sysconfig - if sysconfig.get_config_var('Py_ENABLE_SHARED'): + _sysconfig = __import__('sysconfig') + if _sysconfig.get_config_var('Py_ENABLE_SHARED'): template = "python%d.%d" pythonlib = (template % (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) diff --git a/command/build_scripts.py b/command/build_scripts.py index 8b08bfea..a54d6ed7 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -6,7 +6,6 @@ __revision__ = "$Id$" import os, re from stat import ST_MODE -from distutils import sysconfig from distutils.core import Command from distutils.dep_util import newer from distutils.util import convert_path, Mixin2to3 @@ -57,6 +56,7 @@ class build_scripts(Command): ie. starts with "\#!" and contains "python"), then adjust the first line to refer to the current Python interpreter as we copy. """ + _sysconfig = __import__('sysconfig') self.mkpath(self.build_dir) outfiles = [] updated_files = [] @@ -96,16 +96,16 @@ class build_scripts(Command): updated_files.append(outfile) if not self.dry_run: outf = open(outfile, "w") - if not sysconfig.python_build: + if not _sysconfig.is_python_build(): outf.write("#!%s%s\n" % (self.executable, post_interp)) else: outf.write("#!%s%s\n" % (os.path.join( - sysconfig.get_config_var("BINDIR"), - "python%s%s" % (sysconfig.get_config_var("VERSION"), - sysconfig.get_config_var("EXE"))), + _sysconfig.get_config_var("BINDIR"), + "python%s%s" % (_sysconfig.get_config_var("VERSION"), + _sysconfig.get_config_var("EXE"))), post_interp)) outf.writelines(f.readlines()) outf.close() diff --git a/command/config.py b/command/config.py index 830552cd..56f643c8 100644 --- a/command/config.py +++ b/command/config.py @@ -16,7 +16,7 @@ import re from distutils.core import Command from distutils.errors import DistutilsExecError -from distutils.sysconfig import customize_compiler +from distutils.ccompiler import customize_compiler from distutils import log LANG_EXT = {"c": ".c", "c++": ".cxx"} diff --git a/command/install.py b/command/install.py index 2a905d92..1f8d238a 100644 --- a/command/install.py +++ b/command/install.py @@ -7,115 +7,25 @@ __revision__ = "$Id$" import sys import os +from sysconfig import (get_config_vars, get_platform, get_paths, get_path, + get_config_var) + from distutils import log from distutils.core import Command from distutils.debug import DEBUG -from distutils.sysconfig import get_config_vars from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file -from distutils.util import convert_path, subst_vars, change_root -from distutils.util import get_platform +from distutils.util import convert_path, change_root from distutils.errors import DistutilsOptionError -# this keeps compatibility from 2.3 to 2.5 -if sys.version < "2.6": - USER_BASE = None - USER_SITE = None - HAS_USER_SITE = False -else: - from site import USER_BASE - from site import USER_SITE - HAS_USER_SITE = True - -if sys.version < "2.2": - WINDOWS_SCHEME = { - 'purelib': '$base', - 'platlib': '$base', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - } -else: - WINDOWS_SCHEME = { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - } - -INSTALL_SCHEMES = { - 'unix_prefix': { - 'purelib': '$base/lib/python$py_version_short/site-packages', - 'platlib': '$platbase/lib/python$py_version_short/site-packages', - 'headers': '$base/include/python$py_version_short/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', - }, - 'unix_home': { - 'purelib': '$base/lib/python', - 'platlib': '$base/lib/python', - 'headers': '$base/include/python/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', - }, - 'nt': WINDOWS_SCHEME, - 'mac': { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - }, - - 'os2': { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - }, - } - -# user site schemes -if HAS_USER_SITE: - INSTALL_SCHEMES['nt_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', - 'scripts': '$userbase/Scripts', - 'data' : '$userbase', - } - - INSTALL_SCHEMES['unix_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/include/python$py_version_short/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - } - - INSTALL_SCHEMES['mac_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/$py_version_short/include/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - } - - INSTALL_SCHEMES['os2_home'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/include/python$py_version_short/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - } - -# The keys to an installation scheme; if any new types of files are to be -# installed, be sure to add an entry to every installation scheme above, -# and to SCHEME_KEYS here. -SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') - +def _subst_vars(s, local_vars): + try: + return s.format(**local_vars) + except KeyError: + try: + return s.format(**os.environ) + except KeyError as var: + raise AttributeError('{%s}' % var) class install(Command): @@ -182,11 +92,10 @@ class install(Command): boolean_options = ['compile', 'force', 'skip-build'] - if HAS_USER_SITE: - user_options.append(('user', None, - "install in user site-package '%s'" % USER_SITE)) - boolean_options.append('user') - + user_options.append(('user', None, + "install in user site-package '%s'" % \ + get_path('purelib', '%s_user' % os.name))) + boolean_options.append('user') negative_opt = {'no-compile' : 'compile'} @@ -216,8 +125,8 @@ class install(Command): self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None - self.install_userbase = USER_BASE - self.install_usersite = USER_SITE + self.install_userbase = get_config_var('userbase') + self.install_usersite = get_path('purelib', '%s_user' % os.name) self.compile = None self.optimize = None @@ -327,7 +236,9 @@ class install(Command): # about needing recursive variable expansion (shudder). py_version = sys.version.split()[0] - (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') + prefix, exec_prefix, srcdir = get_config_vars('prefix', 'exec_prefix', + 'srcdir') + self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), 'dist_fullname': self.distribution.get_fullname(), @@ -338,12 +249,11 @@ class install(Command): 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, + 'srcdir': srcdir, } - if HAS_USER_SITE: - self.config_vars['userbase'] = self.install_userbase - self.config_vars['usersite'] = self.install_usersite - + self.config_vars['userbase'] = self.install_userbase + self.config_vars['usersite'] = self.install_usersite self.expand_basedirs() self.dump_dirs("post-expand_basedirs()") @@ -447,10 +357,10 @@ class install(Command): raise DistutilsPlatformError( "User base directory is not specified") self.install_base = self.install_platbase = self.install_userbase - self.select_scheme("unix_user") + self.select_scheme("posix_user") elif self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme("unix_home") + self.select_scheme("posix_home") else: if self.prefix is None: if self.exec_prefix is not None: @@ -466,7 +376,7 @@ class install(Command): self.install_base = self.prefix self.install_platbase = self.exec_prefix - self.select_scheme("unix_prefix") + self.select_scheme("posix_prefix") def finalize_other(self): """Finalizes options for non-posix platforms""" @@ -478,7 +388,7 @@ class install(Command): self.select_scheme(os.name + "_user") elif self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme("unix_home") + self.select_scheme("posix_home") else: if self.prefix is None: self.prefix = os.path.normpath(sys.prefix) @@ -493,11 +403,15 @@ class install(Command): def select_scheme(self, name): """Sets the install directories by applying the install schemes.""" # it's the caller's problem if they supply a bad name! - scheme = INSTALL_SCHEMES[name] - for key in SCHEME_KEYS: + scheme = get_paths(name, expand=False) + for key, value in scheme.items(): + if key == 'platinclude': + key = 'headers' + value = os.path.join(value, self.distribution.get_name()) attrname = 'install_' + key - if getattr(self, attrname) is None: - setattr(self, attrname, scheme[key]) + if hasattr(self, attrname): + if getattr(self, attrname) is None: + setattr(self, attrname, value) def _expand_attrs(self, attrs): for attr in attrs: @@ -505,7 +419,10 @@ class install(Command): if val is not None: if os.name == 'posix' or os.name == 'nt': val = os.path.expanduser(val) - val = subst_vars(val, self.config_vars) + try: + val = _subst_vars(val, self.config_vars) + except: + import pdb; pdb.set_trace() setattr(self, attr, val) def expand_basedirs(self): diff --git a/core.py b/core.py index c820a420..6ed3e8fa 100644 --- a/core.py +++ b/core.py @@ -35,7 +35,7 @@ usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] def gen_usage(script_name): script = os.path.basename(script_name) - return USAGE % vars() + return USAGE % {'script': script} # Some mild magic to control the behaviour of 'setup()' from 'run_setup()'. diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 3e2a634f..f381f608 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -337,7 +337,7 @@ def check_config_h(): # XXX since this function also checks sys.version, it's not strictly a # "pyconfig.h" check -- should probably be renamed... - from distutils import sysconfig + _sysconfig = __import__('sysconfig') # if sys.version contains GCC then python was compiled with GCC, and the # pyconfig.h file should be OK @@ -345,7 +345,7 @@ def check_config_h(): return CONFIG_H_OK, "sys.version mentions 'GCC'" # let's see if __GNUC__ is mentioned in python.h - fn = sysconfig.get_config_h_filename() + fn = _sysconfig.get_config_h_filename() try: with open(fn) as config_h: if "__GNUC__" in config_h.read(): diff --git a/extension.py b/extension.py index 10aaf7c5..ebe5437c 100644 --- a/extension.py +++ b/extension.py @@ -134,14 +134,17 @@ class Extension: def read_setup_file(filename): """Reads a Setup file and returns Extension instances.""" - from distutils.sysconfig import (parse_makefile, expand_makefile_vars, + warnings.warn('distutils.extensions.read_setup_file is deprecated. ' + 'It will be removed in the next Python release.') + _sysconfig = __import__('sysconfig') + from distutils.sysconfig import (expand_makefile_vars, _variable_rx) from distutils.text_file import TextFile from distutils.util import split_quoted # First pass over the file to gather "VAR = VALUE" assignments. - vars = parse_makefile(filename) + vars = _sysconfig._parse_makefile(filename) # Second pass to gobble up the real content: lines of the form # ... [ ...] [ ...] [ ...] @@ -161,7 +164,10 @@ def read_setup_file(filename): file.warn("'%s' lines not handled yet" % line) continue - line = expand_makefile_vars(line, vars) + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + line = expand_makefile_vars(line, vars) + words = split_quoted(line) # NB. this parses a slightly different syntax than the old diff --git a/msvc9compiler.py b/msvc9compiler.py index 6455fffa..38fc96f8 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -23,10 +23,10 @@ from distutils.errors import (DistutilsExecError, DistutilsPlatformError, CompileError, LibError, LinkError) from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log -from distutils.util import get_platform - import winreg +_sysconfig = __import__('sysconfig') + RegOpenKeyEx = winreg.OpenKeyEx RegEnumKey = winreg.EnumKey RegEnumValue = winreg.EnumValue @@ -327,7 +327,7 @@ class MSVCCompiler(CCompiler) : # multi-init means we would need to check platform same each time... assert not self.initialized, "don't init multiple times" if plat_name is None: - plat_name = get_platform() + plat_name = _sysconfig.get_platform() # sanity check for platforms to prevent obscure errors later. ok_plats = 'win32', 'win-amd64', 'win-ia64' if plat_name not in ok_plats: @@ -348,12 +348,12 @@ class MSVCCompiler(CCompiler) : # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross # compile use 'x86' (ie, it runs the x86 compiler directly) # No idea how itanium handles this, if at all. - if plat_name == get_platform() or plat_name == 'win32': + if plat_name == _sysconfig.get_platform() or plat_name == 'win32': # native build or cross-compile to win32 plat_spec = PLAT_TO_VCVARS[plat_name] else: # cross compile from win32 -> some 64bit - plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ + plat_spec = PLAT_TO_VCVARS[_sysconfig.get_platform()] + '_' + \ PLAT_TO_VCVARS[plat_name] vc_env = query_vcvarsall(VERSION, plat_spec) diff --git a/sysconfig.py b/sysconfig.py index 0fbd5412..48f22ad7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -7,6 +7,9 @@ available. Written by: Fred L. Drake, Jr. Email: + +**This module has been moved out of Distutils and will be removed from +Python in the next version (3.2)** """ __revision__ = "$Id$" @@ -14,51 +17,36 @@ __revision__ = "$Id$" import io import os import re -import sys - -from .errors import DistutilsPlatformError - -# These are needed in a couple of spots, so just compute them once. -PREFIX = os.path.normpath(sys.prefix) -EXEC_PREFIX = os.path.normpath(sys.exec_prefix) - -# Path to the base directory of the project. On Windows the binary may -# live in project/PCBuild9. If we're dealing with an x64 Windows build, -# it'll live in project/PCbuild/amd64. -project_base = os.path.dirname(os.path.abspath(sys.executable)) -if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): - project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) -# PC/VS7.1 -if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): - project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, - os.path.pardir)) -# PC/AMD64 -if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): - project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, - os.path.pardir)) - -# python_build: (Boolean) if true, we're either building Python or -# building an extension with an un-installed Python, so we use -# different (hard-wired) directories. -# Setup.local is available for Makefile builds including VPATH builds, -# Setup.dist is available on Windows -def _python_build(): - for fn in ("Setup.dist", "Setup.local"): - if os.path.isfile(os.path.join(project_base, "Modules", fn)): - return True - return False -python_build = _python_build() +from warnings import warn -def get_python_version(): - """Return a string containing the major and minor Python version, - leaving off the patchlevel. Sample return values could be '1.5' - or '2.2'. - """ - return sys.version[:3] +from distutils.errors import DistutilsPlatformError + +# importing sysconfig from Lib +# to avoid this module to shadow it +_sysconfig = __import__('sysconfig') +_DEPRECATION_MSG = ("distutils.sysconfig.%s is deprecated. " + "Use the APIs provided by the sysconfig module instead") + +def _get_project_base(): + return _sysconfig._PROJECT_BASE + +project_base = _get_project_base() + +class _DeprecatedBool(int): + def __nonzero__(self): + warn(_DEPRECATION_MSG % 'get_python_version', DeprecationWarning) + return super(_DeprecatedBool, self).__nonzero__() + +def _python_build(): + return _DeprecatedBool(_sysconfig.is_python_build()) + +python_build = _python_build() def get_python_inc(plat_specific=0, prefix=None): - """Return the directory containing installed Python header files. + """This function is deprecated. + + Return the directory containing installed Python header files. If 'plat_specific' is false (the default), this is the path to the non-platform-specific header files, i.e. Python.h and so on; @@ -68,39 +56,22 @@ def get_python_inc(plat_specific=0, prefix=None): If 'prefix' is supplied, use it instead of sys.prefix or sys.exec_prefix -- i.e., ignore 'plat_specific'. """ - if prefix is None: - prefix = plat_specific and EXEC_PREFIX or PREFIX - if os.name == "posix": - if python_build: - # Assume the executable is in the build directory. The - # pyconfig.h file should be in the same directory. Since - # the build directory may not be the source directory, we - # must use "srcdir" from the makefile to find the "Include" - # directory. - base = os.path.dirname(os.path.abspath(sys.executable)) - if plat_specific: - return base - else: - incdir = os.path.join(get_config_var('srcdir'), 'Include') - return os.path.normpath(incdir) - return os.path.join(prefix, "include", "python" + get_python_version()) - elif os.name == "nt": - return os.path.join(prefix, "include") - elif os.name == "mac": - if plat_specific: - return os.path.join(prefix, "Mac", "Include") - else: - return os.path.join(prefix, "Include") - elif os.name == "os2": - return os.path.join(prefix, "Include") + warn(_DEPRECATION_MSG % 'get_python_inc', DeprecationWarning) + get_path = _sysconfig.get_path + + if prefix is not None: + vars = {'base': prefix} + return get_path('include', vars=vars) + + if not plat_specific: + return get_path('include') else: - raise DistutilsPlatformError( - "I don't know where Python installs its C header files " - "on platform '%s'" % os.name) + return get_path('platinclude') +def get_python_lib(plat_specific=False, standard_lib=False, prefix=None): + """This function is deprecated. -def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): - """Return the directory containing the Python library (standard or + Return the directory containing the Python library (standard or site additions). If 'plat_specific' is true, return the directory containing @@ -113,149 +84,33 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): If 'prefix' is supplied, use it instead of sys.prefix or sys.exec_prefix -- i.e., ignore 'plat_specific'. """ - if prefix is None: - prefix = plat_specific and EXEC_PREFIX or PREFIX - - if os.name == "posix": - libpython = os.path.join(prefix, - "lib", "python" + get_python_version()) - if standard_lib: - return libpython - else: - return os.path.join(libpython, "site-packages") - elif os.name == "nt": - if standard_lib: - return os.path.join(prefix, "Lib") - else: - if get_python_version() < "2.2": - return prefix - else: - return os.path.join(prefix, "Lib", "site-packages") - elif os.name == "mac": + warn(_DEPRECATION_MSG % 'get_python_lib', DeprecationWarning) + vars = {} + get_path = _sysconfig.get_path + if prefix is not None: if plat_specific: - if standard_lib: - return os.path.join(prefix, "Lib", "lib-dynload") - else: - return os.path.join(prefix, "Lib", "site-packages") + vars['platbase'] = prefix else: - if standard_lib: - return os.path.join(prefix, "Lib") - else: - return os.path.join(prefix, "Lib", "site-packages") - elif os.name == "os2": - if standard_lib: - return os.path.join(prefix, "Lib") + vars['base'] = prefix + if standard_lib: + if plat_specific: + return get_path('platstdlib', vars=vars) else: - return os.path.join(prefix, "Lib", "site-packages") + return get_path('stdlib', vars=vars) else: - raise DistutilsPlatformError( - "I don't know where Python installs its library " - "on platform '%s'" % os.name) - - -def customize_compiler(compiler): - """Do any platform-specific customization of a CCompiler instance. - - Mainly needed on Unix, so we can plug in the information that - varies across Unices and is stored in Python's Makefile. - """ - if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ - get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS') - - if 'CC' in os.environ: - cc = os.environ['CC'] - if 'CXX' in os.environ: - cxx = os.environ['CXX'] - if 'LDSHARED' in os.environ: - ldshared = os.environ['LDSHARED'] - if 'CPP' in os.environ: - cpp = os.environ['CPP'] - else: - cpp = cc + " -E" # not always - if 'LDFLAGS' in os.environ: - ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if 'CFLAGS' in os.environ: - cflags = opt + ' ' + os.environ['CFLAGS'] - ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if 'CPPFLAGS' in os.environ: - cpp = cpp + ' ' + os.environ['CPPFLAGS'] - cflags = cflags + ' ' + os.environ['CPPFLAGS'] - ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] - if 'AR' in os.environ: - ar = os.environ['AR'] - if 'ARFLAGS' in os.environ: - archiver = ar + ' ' + os.environ['ARFLAGS'] - else: - archiver = ar + ' ' + ar_flags - - cc_cmd = cc + ' ' + cflags - compiler.set_executables( - preprocessor=cpp, - compiler=cc_cmd, - compiler_so=cc_cmd + ' ' + ccshared, - compiler_cxx=cxx, - linker_so=ldshared, - linker_exe=cc, - archiver=archiver) - - compiler.shared_lib_extension = so_ext - - -def get_config_h_filename(): - """Return full pathname of installed pyconfig.h file.""" - if python_build: - if os.name == "nt": - inc_dir = os.path.join(project_base, "PC") + if plat_specific: + return get_path('platlib', vars=vars) else: - inc_dir = project_base - else: - inc_dir = get_python_inc(plat_specific=1) - if get_python_version() < '2.2': - config_h = 'config.h' - else: - # The name of the config.h file changed in 2.2 - config_h = 'pyconfig.h' - return os.path.join(inc_dir, config_h) - + return get_path('purelib', vars=vars) def get_makefile_filename(): - """Return full pathname of installed Makefile from the Python build.""" - if python_build: - return os.path.join(os.path.dirname(sys.executable), "Makefile") - lib_dir = get_python_lib(plat_specific=1, standard_lib=1) - return os.path.join(lib_dir, "config", "Makefile") - + """This function is deprecated. -def parse_config_h(fp, g=None): - """Parse a config.h-style file. - - A dictionary containing name/value pairs is returned. If an - optional dictionary is passed in as the second argument, it is - used instead of a new dictionary. + Return full pathname of installed Makefile from the Python build. """ - if g is None: - g = {} - define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") - undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") - # - while True: - line = fp.readline() - if not line: - break - m = define_rx.match(line) - if m: - n, v = m.group(1, 2) - try: v = int(v) - except ValueError: pass - g[n] = v - else: - m = undef_rx.match(line) - if m: - g[m.group(1)] = 0 - return g + warn(_DEPRECATION_MSG % 'get_makefile_filename', DeprecationWarning) + return _sysconfig._get_makefile_filename() # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). @@ -264,91 +119,29 @@ _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") def parse_makefile(fn, g=None): - """Parse a Makefile-style file. + """This function is deprecated. + + Parse a Makefile-style file. A dictionary containing name/value pairs is returned. If an optional dictionary is passed in as the second argument, it is used instead of a new dictionary. """ - from distutils.text_file import TextFile - fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1) - - if g is None: - g = {} - done = {} - notdone = {} - - while True: - line = fp.readline() - if line is None: # eof - break - m = _variable_rx.match(line) - if m: - n, v = m.group(1, 2) - v = v.strip() - # `$$' is a literal `$' in make - tmpv = v.replace('$$', '') - - if "$" in tmpv: - notdone[n] = v - else: - try: - v = int(v) - except ValueError: - # insert literal `$' - done[n] = v.replace('$$', '$') - else: - done[n] = v - - # do variable interpolation here - while notdone: - for name in list(notdone): - value = notdone[name] - m = _findvar1_rx.search(value) or _findvar2_rx.search(value) - if m: - n = m.group(1) - found = True - if n in done: - item = str(done[n]) - elif n in notdone: - # get it on a subsequent round - found = False - elif n in os.environ: - # do it like make: fall back to environment - item = os.environ[n] - else: - done[n] = item = "" - if found: - after = value[m.end():] - value = value[:m.start()] + item + after - if "$" in after: - notdone[name] = value - else: - try: value = int(value) - except ValueError: - done[name] = value.strip() - else: - done[name] = value - del notdone[name] - else: - # bogus variable reference; just drop it since we can't deal - del notdone[name] - - fp.close() - - # save the results in the global dictionary - g.update(done) - return g - + warn(_DEPRECATION_MSG % 'parse_makefile', DeprecationWarning) + return _sysconfig._parse_makefile(fn, g) def expand_makefile_vars(s, vars): - """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in + """This function is deprecated. + + Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in 'string' according to 'vars' (a dictionary mapping variable names to values). Variables not present in 'vars' are silently expanded to the empty string. The variable values in 'vars' should not contain further variable expansions; if 'vars' is the output of 'parse_makefile()', you're fine. Returns a variable-expanded version of 's'. """ + warn('this function will be removed in then next version of Python', + DeprecationWarning) # This algorithm does multiple expansion, so if vars['foo'] contains # "${bar}", it will expand ${foo} to ${bar}, and then expand @@ -364,220 +157,3 @@ def expand_makefile_vars(s, vars): else: break return s - - -_config_vars = None - -def _init_posix(): - """Initialize the module as appropriate for POSIX systems.""" - g = {} - # load the installed Makefile: - try: - filename = get_makefile_filename() - parse_makefile(filename, g) - except IOError as msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # load the installed pyconfig.h: - try: - filename = get_config_h_filename() - parse_config_h(io.open(filename), g) - except IOError as msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # On MacOSX we need to check the setting of the environment variable - # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so - # it needs to be compatible. - # If it isn't set we set it to the configure-time value - if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g: - cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] - cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') - if cur_target == '': - cur_target = cfg_target - os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) - elif [int(x) for x in cfg_target.split('.')] > [int(x) for x in cur_target.split('.')]: - my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' - % (cur_target, cfg_target)) - raise DistutilsPlatformError(my_msg) - - # On AIX, there are wrong paths to the linker scripts in the Makefile - # -- these paths are relative to the Python source, but when installed - # the scripts are in another directory. - if python_build: - g['LDSHARED'] = g['BLDSHARED'] - - elif get_python_version() < '2.1': - # The following two branches are for 1.5.2 compatibility. - if sys.platform == 'aix4': # what about AIX 3.x ? - # Linker script is in the config directory, not in Modules as the - # Makefile says. - python_lib = get_python_lib(standard_lib=1) - ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') - python_exp = os.path.join(python_lib, 'config', 'python.exp') - - g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) - - global _config_vars - _config_vars = g - - -def _init_nt(): - """Initialize the module as appropriate for NT""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - g['SO'] = '.pyd' - g['EXE'] = ".exe" - g['VERSION'] = get_python_version().replace(".", "") - g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) - - global _config_vars - _config_vars = g - - -def _init_mac(): - """Initialize the module as appropriate for Macintosh systems""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - import MacOS - if not hasattr(MacOS, 'runtimemodel'): - g['SO'] = '.ppc.slb' - else: - g['SO'] = '.%s.slb' % MacOS.runtimemodel - - # XXX are these used anywhere? - g['install_lib'] = os.path.join(EXEC_PREFIX, "Lib") - g['install_platlib'] = os.path.join(EXEC_PREFIX, "Mac", "Lib") - - # These are used by the extension module build - g['srcdir'] = ':' - global _config_vars - _config_vars = g - - -def _init_os2(): - """Initialize the module as appropriate for OS/2""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - g['SO'] = '.pyd' - g['EXE'] = ".exe" - - global _config_vars - _config_vars = g - - -def get_config_vars(*args): - """With no arguments, return a dictionary of all configuration - variables relevant for the current platform. Generally this includes - everything needed to build extensions and install both pure modules and - extensions. On Unix, this means every variable defined in Python's - installed Makefile; on Windows and Mac OS it's a much smaller set. - - With arguments, return a list of values that result from looking up - each argument in the configuration variable dictionary. - """ - global _config_vars - if _config_vars is None: - func = globals().get("_init_" + os.name) - if func: - func() - else: - _config_vars = {} - - # Normalized versions of prefix and exec_prefix are handy to have; - # in fact, these are the standard versions used most places in the - # Distutils. - _config_vars['prefix'] = PREFIX - _config_vars['exec_prefix'] = EXEC_PREFIX - - # Convert srcdir into an absolute path if it appears necessary. - # Normally it is relative to the build directory. However, during - # testing, for example, we might be running a non-installed python - # from a different directory. - if python_build and os.name == "posix": - base = os.path.dirname(os.path.abspath(sys.executable)) - if (not os.path.isabs(_config_vars['srcdir']) and - base != os.getcwd()): - # srcdir is relative and we are not in the same directory - # as the executable. Assume executable is in the build - # directory and make srcdir absolute. - srcdir = os.path.join(base, _config_vars['srcdir']) - _config_vars['srcdir'] = os.path.normpath(srcdir) - - if sys.platform == 'darwin': - kernel_version = os.uname()[2] # Kernel version (8.4.3) - major_version = int(kernel_version.split('.')[0]) - - if major_version < 8: - # On Mac OS X before 10.4, check if -arch and -isysroot - # are in CFLAGS or LDFLAGS and remove them if they are. - # This is needed when building extensions on a 10.3 system - # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags, re.ASCII) - flags = re.sub('-isysroot [^ \t]*', ' ', flags) - _config_vars[key] = flags - - else: - - # Allow the user to override the architecture flags using - # an environment variable. - # NOTE: This name was introduced by Apple in OSX 10.5 and - # is used by several scripting languages distributed with - # that OS release. - - if 'ARCHFLAGS' in os.environ: - arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = flags + ' ' + arch - _config_vars[key] = flags - - if args: - vals = [] - for name in args: - vals.append(_config_vars.get(name)) - return vals - else: - return _config_vars - -def get_config_var(name): - """Return the value of a single variable using the dictionary - returned by 'get_config_vars()'. Equivalent to - get_config_vars().get(name) - """ - return get_config_vars().get(name) diff --git a/tests/support.py b/tests/support.py index e258d2e5..d60da854 100644 --- a/tests/support.py +++ b/tests/support.py @@ -3,11 +3,19 @@ import os import shutil import tempfile from copy import deepcopy +import warnings from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution +def capture_warnings(func): + def _capture_warnings(*args, **kw): + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + return func(*args, **kw) + return _capture_warnings + class LoggingSilencer(object): def setUp(self): diff --git a/tests/test_build.py b/tests/test_build.py index 6bca27ee..2418e165 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -5,7 +5,7 @@ import sys from distutils.command.build import build from distutils.tests import support -from distutils.util import get_platform +from sysconfig import get_platform class BuildTestCase(support.TempdirManager, support.LoggingSilencer, diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 536cd673..145eff54 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -120,8 +120,7 @@ class BuildCLibTestCase(support.TempdirManager, # before we run the command, we want to make sure # all commands are present on the system # by creating a compiler and checking its executables - from distutils.ccompiler import new_compiler - from distutils.sysconfig import customize_compiler + from distutils.ccompiler import new_compiler, customize_compiler compiler = new_compiler() customize_compiler(compiler) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index ebbb3997..d0971836 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -9,7 +9,7 @@ from test.support import captured_stdout from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext -from distutils import sysconfig +import sysconfig from distutils.tests.support import TempdirManager from distutils.tests.support import LoggingSilencer from distutils.extension import Extension @@ -105,17 +105,17 @@ class BuildExtTestCase(TempdirManager, old = sys.platform sys.platform = 'sunos' # fooling finalize_options - from distutils.sysconfig import _config_vars - old_var = _config_vars.get('Py_ENABLE_SHARED') - _config_vars['Py_ENABLE_SHARED'] = 1 + from sysconfig import _CONFIG_VARS + old_var = _CONFIG_VARS.get('Py_ENABLE_SHARED') + _CONFIG_VARS['Py_ENABLE_SHARED'] = 1 try: cmd.ensure_finalized() finally: sys.platform = old if old_var is None: - del _config_vars['Py_ENABLE_SHARED'] + del _CONFIG_VARS['Py_ENABLE_SHARED'] else: - _config_vars['Py_ENABLE_SHARED'] = old_var + _CONFIG_VARS['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris self.assertTrue(len(cmd.library_dirs) > 0) @@ -177,11 +177,10 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.finalize_options() - from distutils import sysconfig - py_include = sysconfig.get_python_inc() + py_include = sysconfig.get_path('include') self.assertTrue(py_include in cmd.include_dirs) - plat_py_include = sysconfig.get_python_inc(plat_specific=1) + plat_py_include = sysconfig.get_path('platinclude') self.assertTrue(plat_py_include in cmd.include_dirs) # make sure cmd.libraries is turned into a list diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index b1d2d079..72e8915c 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,7 +5,7 @@ import unittest from distutils.command.build_scripts import build_scripts from distutils.core import Distribution -from distutils import sysconfig +import sysconfig from distutils.tests import support @@ -91,12 +91,12 @@ class BuildScriptsTestCase(support.TempdirManager, # --with-suffix=3`, python is compiled okay but the build scripts # failed when writing the name of the executable old = sysconfig.get_config_vars().get('VERSION') - sysconfig._config_vars['VERSION'] = 4 + sysconfig._CONFIG_VARS['VERSION'] = 4 try: cmd.run() finally: if old is not None: - sysconfig._config_vars['VERSION'] = old + sysconfig._CONFIG_VARS['VERSION'] = old built = os.listdir(target) for name in expected: diff --git a/tests/test_ccompiler.py b/tests/test_ccompiler.py index 9db8d246..27b51a06 100644 --- a/tests/test_ccompiler.py +++ b/tests/test_ccompiler.py @@ -3,8 +3,10 @@ import os import unittest from test.support import captured_stdout -from distutils.ccompiler import gen_lib_options, CCompiler +from distutils.ccompiler import (gen_lib_options, CCompiler, + get_default_compiler, customize_compiler) from distutils import debug +from distutils.tests import support class FakeCompiler(object): def library_dir_option(self, dir): @@ -19,7 +21,7 @@ class FakeCompiler(object): def library_option(self, lib): return "-l" + lib -class CCompilerTestCase(unittest.TestCase): +class CCompilerTestCase(support.EnvironGuard, unittest.TestCase): def test_gen_lib_options(self): compiler = FakeCompiler() @@ -52,6 +54,26 @@ class CCompilerTestCase(unittest.TestCase): finally: debug.DEBUG = False + def test_customize_compiler(self): + + # not testing if default compiler is not unix + if get_default_compiler() != 'unix': + return + + os.environ['AR'] = 'my_ar' + os.environ['ARFLAGS'] = '-arflags' + + # make sure AR gets caught + class compiler: + compiler_type = 'unix' + + def set_executables(self, **kw): + self.exes = kw + + comp = compiler() + customize_compiler(comp) + self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') + def test_suite(): return unittest.makeSuite(CCompilerTestCase) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index 98f0f08e..4b95dfa3 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -4,6 +4,7 @@ import sys import os import subprocess import warnings +import sysconfig from test.support import check_warnings from test.support import captured_stdout @@ -23,13 +24,11 @@ class CygwinCCompilerTestCase(support.TempdirManager, super(CygwinCCompilerTestCase, self).setUp() self.version = sys.version self.python_h = os.path.join(self.mkdtemp(), 'python.h') - from distutils import sysconfig self.old_get_config_h_filename = sysconfig.get_config_h_filename sysconfig.get_config_h_filename = self._get_config_h_filename def tearDown(self): sys.version = self.version - from distutils import sysconfig sysconfig.get_config_h_filename = self.old_get_config_h_filename super(CygwinCCompilerTestCase, self).tearDown() diff --git a/tests/test_extension.py b/tests/test_extension.py index 1ee30585..9d3cfe6f 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -5,9 +5,11 @@ import warnings from test.support import check_warnings from distutils.extension import read_setup_file, Extension +from distutils.tests.support import capture_warnings class ExtensionTestCase(unittest.TestCase): + @capture_warnings def test_read_setup_file(self): # trying to read a Setup file # (sample extracted from the PyGame project) diff --git a/tests/test_install.py b/tests/test_install.py index 76fa02ac..59e90801 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -5,12 +5,14 @@ import os.path import sys import unittest import site +import sysconfig +from sysconfig import (get_scheme_names, _CONFIG_VARS, _INSTALL_SCHEMES, + get_config_var, get_path) from test.support import captured_stdout from distutils.command.install import install from distutils.command import install as install_module -from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution from distutils.errors import DistutilsOptionError @@ -36,9 +38,23 @@ class InstallTestCase(support.TempdirManager, build_lib=os.path.join(builddir, "lib"), ) - cmd = install(dist) - cmd.home = destination - cmd.ensure_finalized() + + + posix_prefix = _INSTALL_SCHEMES['posix_prefix'] + old_posix_prefix = posix_prefix['platinclude'] + posix_prefix['platinclude'] = \ + '{platbase}/include/python{py_version_short}' + + posix_home = _INSTALL_SCHEMES['posix_home'] + old_posix_home = posix_home['platinclude'] + posix_home['platinclude'] = '{base}/include/python' + try: + cmd = install(dist) + cmd.home = destination + cmd.ensure_finalized() + finally: + posix_home['platinclude'] = old_posix_home + posix_prefix['platinclude'] = old_posix_prefix self.assertEqual(cmd.install_base, destination) self.assertEqual(cmd.install_platbase, destination) @@ -63,18 +79,19 @@ class InstallTestCase(support.TempdirManager, return # preparing the environement for the test - self.old_user_base = site.USER_BASE - self.old_user_site = site.USER_SITE + self.old_user_base = get_config_var('userbase') + self.old_user_site = get_path('purelib', '%s_user' % os.name) self.tmpdir = self.mkdtemp() self.user_base = os.path.join(self.tmpdir, 'B') self.user_site = os.path.join(self.tmpdir, 'S') - site.USER_BASE = self.user_base - site.USER_SITE = self.user_site - install_module.USER_BASE = self.user_base - install_module.USER_SITE = self.user_site + _CONFIG_VARS['userbase'] = self.user_base + scheme = _INSTALL_SCHEMES['%s_user' % os.name] + scheme['purelib'] = self.user_site def _expanduser(path): - return self.tmpdir + if path[0] == '~': + path = os.path.normpath(self.tmpdir) + path[1:] + return path self.old_expand = os.path.expanduser os.path.expanduser = _expanduser @@ -82,19 +99,17 @@ class InstallTestCase(support.TempdirManager, # this is the actual test self._test_user_site() finally: - site.USER_BASE = self.old_user_base - site.USER_SITE = self.old_user_site - install_module.USER_BASE = self.old_user_base - install_module.USER_SITE = self.old_user_site + _CONFIG_VARS['userbase'] = self.old_user_base + scheme['purelib'] = self.old_user_site os.path.expanduser = self.old_expand def _test_user_site(self): - for key in ('nt_user', 'unix_user', 'os2_home'): - self.assertTrue(key in INSTALL_SCHEMES) + schemes = get_scheme_names() + for key in ('nt_user', 'posix_user', 'os2_home'): + self.assertTrue(key in schemes) dist = Distribution({'name': 'xx'}) cmd = install(dist) - # making sure the user option is there options = [name for name, short, lable in cmd.user_options] @@ -185,7 +200,7 @@ class InstallTestCase(support.TempdirManager, with open(cmd.record) as f: self.assertEquals(len(f.readlines()), 1) - def test_debug_mode(self): + def _test_debug_mode(self): # this covers the code called when DEBUG is set old_logs_len = len(self.logs) install_module.DEBUG = True diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index edc755ed..e7df8031 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -4,7 +4,6 @@ import test import unittest from distutils import sysconfig -from distutils.ccompiler import get_default_compiler from distutils.tests import support from test.support import TESTFN, run_unittest @@ -26,10 +25,7 @@ class SysconfigTestCase(support.EnvironGuard, elif os.path.isdir(TESTFN): shutil.rmtree(TESTFN) - def test_get_config_h_filename(self): - config_h = sysconfig.get_config_h_filename() - self.assertTrue(os.path.isfile(config_h), config_h) - + @support.capture_warnings def test_get_python_lib(self): lib_dir = sysconfig.get_python_lib() # XXX doesn't work on Linux when Python was never installed before @@ -37,7 +33,11 @@ class SysconfigTestCase(support.EnvironGuard, # test for pythonxx.lib? self.assertNotEqual(sysconfig.get_python_lib(), sysconfig.get_python_lib(prefix=TESTFN)) + _sysconfig = __import__('sysconfig') + res = sysconfig.get_python_lib(True, True) + self.assertEquals(_sysconfig.get_path('platstdlib'), res) + @support.capture_warnings def test_get_python_inc(self): inc_dir = sysconfig.get_python_inc() # This is not much of a test. We make sure Python.h exists @@ -47,31 +47,7 @@ class SysconfigTestCase(support.EnvironGuard, python_h = os.path.join(inc_dir, "Python.h") self.assertTrue(os.path.isfile(python_h), python_h) - def test_get_config_vars(self): - cvars = sysconfig.get_config_vars() - self.assertTrue(isinstance(cvars, dict)) - self.assertTrue(cvars) - - def test_customize_compiler(self): - - # not testing if default compiler is not unix - if get_default_compiler() != 'unix': - return - - os.environ['AR'] = 'my_ar' - os.environ['ARFLAGS'] = '-arflags' - - # make sure AR gets caught - class compiler: - compiler_type = 'unix' - - def set_executables(self, **kw): - self.exes = kw - - comp = compiler() - sysconfig.customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') - + @support.capture_warnings def test_parse_makefile_base(self): self.makefile = TESTFN fd = open(self.makefile, 'w') diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 008ae5d0..6976dd55 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -1,8 +1,8 @@ """Tests for distutils.unixccompiler.""" import sys import unittest +import sysconfig -from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler class UnixCCompilerTestCase(unittest.TestCase): diff --git a/tests/test_util.py b/tests/test_util.py index 8f8d4a10..ae6671de 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -6,15 +6,14 @@ from copy import copy from io import BytesIO import subprocess +from sysconfig import get_config_vars, get_platform from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError -from distutils.util import (get_platform, convert_path, change_root, +from distutils.util import (convert_path, change_root, check_environ, split_quoted, strtobool, rfc822_escape, get_compiler_versions, _find_exe_version, _MAC_OS_X_LD_VERSION, byte_compile) from distutils import util -from distutils.sysconfig import get_config_vars -from distutils import sysconfig from distutils.tests import support from distutils.version import LooseVersion @@ -44,7 +43,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive - self._config_vars = copy(sysconfig._config_vars) + #self._config_vars = copy(sysconfig._config_vars) # patching os.uname if hasattr(os, 'uname'): @@ -78,7 +77,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): os.uname = self.uname else: del os.uname - sysconfig._config_vars = copy(self._config_vars) + #sysconfig._config_vars = copy(self._config_vars) util.find_executable = self.old_find_executable subprocess.Popen = self.old_popen sys.old_stdout = self.old_stdout @@ -91,7 +90,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): def _get_uname(self): return self._uname - def test_get_platform(self): + def _test_get_platform(self): # windows XP, 32bits os.name = 'nt' @@ -119,7 +118,6 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') sys.platform = 'darwin' - self._set_uname(('Darwin', 'macziade', '8.11.1', ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' @@ -157,7 +155,6 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): finally: sys.maxsize = maxsize - # macbook with fat binaries (fat, universal or fat64) os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' @@ -201,7 +198,6 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) - # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' diff --git a/unixccompiler.py b/unixccompiler.py index 51f6349a..c14a5d3f 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,7 +17,6 @@ __revision__ = "$Id$" import os, sys -from distutils import sysconfig from distutils.dep_util import newer from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options @@ -25,6 +24,7 @@ from distutils.errors import \ DistutilsExecError, CompileError, LibError, LinkError from distutils import log + # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's # Makefile and live with it. Is this adequate? If not, we might @@ -74,7 +74,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): if 'ARCHFLAGS' in os.environ and not stripArch: # User specified different -arch flags in the environ, - # see also distutils.sysconfig + # see also the sysconfig compiler_so = compiler_so + os.environ['ARCHFLAGS'].split() if stripSysroot: @@ -281,7 +281,9 @@ class UnixCCompiler(CCompiler): # this time, there's no way to determine this information from # the configuration data stored in the Python installation, so # we use this hack. - compiler = os.path.basename(sysconfig.get_config_var("CC")) + _sysconfig = __import__('sysconfig') + + compiler = os.path.basename(_sysconfig.get_config_var("CC")) if sys.platform[:6] == "darwin": # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir @@ -296,7 +298,7 @@ class UnixCCompiler(CCompiler): # use it anyway. Since distutils has always passed in # -Wl whenever gcc was used in the past it is probably # safest to keep doing so. - if sysconfig.get_config_var("GNULD") == "yes": + if _sysconfig.get_config_var("GNULD") == "yes": # GNU ld needs an extra option to get a RUNPATH # instead of just an RPATH. return "-Wl,--enable-new-dtags,-R" + dir diff --git a/util.py b/util.py index 8adf6e0d..0515fefd 100644 --- a/util.py +++ b/util.py @@ -15,173 +15,7 @@ from distutils import log from distutils.version import LooseVersion from distutils.errors import DistutilsByteCompileError -def get_platform(): - """Return a string that identifies the current platform. - - This is used mainly to distinguish platform-specific build directories and - platform-specific built distributions. Typically includes the OS name - and version and the architecture (as supplied by 'os.uname()'), - although the exact information included depends on the OS; eg. for IRIX - the architecture isn't particularly important (IRIX only runs on SGI - hardware), but for Linux the kernel version isn't particularly - important. - - Examples of returned values: - linux-i586 - linux-alpha (?) - solaris-2.6-sun4u - irix-5.3 - irix64-6.2 - - Windows will return one of: - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) - win-ia64 (64bit Windows on Itanium) - win32 (all others - specifically, sys.platform is returned) - - For other non-POSIX platforms, currently just returns 'sys.platform'. - """ - if os.name == 'nt': - # sniff sys.version for architecture. - prefix = " bit (" - i = sys.version.find(prefix) - if i == -1: - return sys.platform - j = sys.version.find(")", i) - look = sys.version[i+len(prefix):j].lower() - if look == 'amd64': - return 'win-amd64' - if look == 'itanium': - return 'win-ia64' - return sys.platform - - if os.name != "posix" or not hasattr(os, 'uname'): - # XXX what about the architecture? NT is Intel or Alpha, - # Mac OS is M68k or PPC, etc. - return sys.platform - - # Try to distinguish various flavours of Unix - - (osname, host, release, version, machine) = os.uname() - - # Convert the OS name to lowercase, remove '/' characters - # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") - osname = osname.lower().replace('/', '') - machine = machine.replace(' ', '_') - machine = machine.replace('/', '-') - - if osname[:5] == "linux": - # At least on Linux/Intel, 'machine' is the processor -- - # i386, etc. - # XXX what about Alpha, SPARC, etc? - return "%s-%s" % (osname, machine) - elif osname[:5] == "sunos": - if release[0] >= "5": # SunOS 5 == Solaris 2 - osname = "solaris" - release = "%d.%s" % (int(release[0]) - 3, release[2:]) - # fall through to standard osname-release-machine representation - elif osname[:4] == "irix": # could be "irix64"! - return "%s-%s" % (osname, release) - elif osname[:3] == "aix": - return "%s-%s.%s" % (osname, version, release) - elif osname[:6] == "cygwin": - osname = "cygwin" - rel_re = re.compile (r'[\d.]+', re.ASCII) - m = rel_re.match(release) - if m: - release = m.group() - elif osname[:6] == "darwin": - # - # For our purposes, we'll assume that the system version from - # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set - # to. This makes the compatibility story a bit more sane because the - # machine is going to compile and link as if it were - # MACOSX_DEPLOYMENT_TARGET. - from distutils.sysconfig import get_config_vars - cfgvars = get_config_vars() - - macver = os.environ.get('MACOSX_DEPLOYMENT_TARGET') - if not macver: - macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') - - if 1: - # Always calculate the release of the running machine, - # needed to determine if we can build fat binaries or not. - - macrelease = macver - # Get the system version. Reading this plist is a documented - # way to get the system version (see the documentation for - # the Gestalt Manager) - try: - f = open('/System/Library/CoreServices/SystemVersion.plist') - except IOError: - # We're on a plain darwin box, fall back to the default - # behaviour. - pass - else: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - f.close() - if m is not None: - macrelease = '.'.join(m.group(1).split('.')[:2]) - # else: fall back to the default behaviour - - if not macver: - macver = macrelease - - if macver: - from distutils.sysconfig import get_config_vars - release = macver - osname = "macosx" - - if (macrelease + '.') >= '10.4.' and \ - '-arch' in get_config_vars().get('CFLAGS', '').strip(): - # The universal build will build fat binaries, but not on - # systems before 10.4 - # - # Try to detect 4-way universal builds, those have machine-type - # 'universal' instead of 'fat'. - - machine = 'fat' - cflags = get_config_vars().get('CFLAGS') - - archs = re.findall('-arch\s+(\S+)', cflags) - archs.sort() - archs = tuple(archs) - - if len(archs) == 1: - machine = archs[0] - elif archs == ('i386', 'ppc'): - machine = 'fat' - elif archs == ('i386', 'x86_64'): - machine = 'intel' - elif archs == ('i386', 'ppc', 'x86_64'): - machine = 'fat3' - elif archs == ('ppc64', 'x86_64'): - machine = 'fat64' - elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): - machine = 'universal' - else: - raise ValueError( - "Don't know machine value for archs=%r"%(archs,)) - - elif machine == 'i386': - # On OSX the machine type returned by uname is always the - # 32-bit variant, even if the executable architecture is - # the 64-bit variant - if sys.maxsize >= 2**32: - machine = 'x86_64' - - elif machine in ('PowerPC', 'Power_Macintosh'): - # Pick a sane name for the PPC architecture. - machine = 'ppc' - - # See 'i386' case - if sys.maxsize >= 2**32: - machine = 'ppc64' - - return "%s-%s-%s" % (osname, release, machine) - +_sysconfig = __import__('sysconfig') def convert_path(pathname): """Return 'pathname' as a name that will work on the native filesystem. @@ -269,7 +103,7 @@ def check_environ(): os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] if 'PLAT' not in os.environ: - os.environ['PLAT'] = get_platform() + os.environ['PLAT'] = _sysconfig.get_platform() _environ_checked = 1 -- cgit v1.2.1 From 70ef6e5c8e5729c1f14e57d4423be5d6c72e35e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 29 Jan 2010 11:46:31 +0000 Subject: Merged revisions 77759,77761 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77759 | tarek.ziade | 2010-01-26 22:21:54 +0100 (Tue, 26 Jan 2010) | 1 line reintroduced the names in Distutils for APIs that were relocated ........ r77761 | tarek.ziade | 2010-01-26 23:46:15 +0100 (Tue, 26 Jan 2010) | 1 line added local get_platform/set_platform APIs in distutils.sysconfig ........ --- command/bdist.py | 2 +- command/bdist_dumb.py | 3 +- command/bdist_msi.py | 3 ++ command/bdist_wininst.py | 3 +- command/build.py | 2 +- command/build_ext.py | 4 +- command/install.py | 5 +- msvc9compiler.py | 10 ++-- sysconfig.py | 9 ++++ tests/test_util.py | 125 +++-------------------------------------------- util.py | 21 ++++++++ 11 files changed, 53 insertions(+), 134 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index a79982b6..72b0cefe 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,8 +6,8 @@ distribution).""" __revision__ = "$Id$" import os -from sysconfig import get_platform +from distutils.util import get_platform from distutils.core import Command from distutils.errors import DistutilsPlatformError, DistutilsOptionError diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index c16125f2..c2af95f1 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -8,8 +8,9 @@ __revision__ = "$Id$" import os -from sysconfig import get_python_version, get_platform +from sysconfig import get_python_version +from distutils.util import get_platform from distutils.core import Command from distutils.dir_util import remove_tree, ensure_relative from distutils.errors import DistutilsPlatformError diff --git a/command/bdist_msi.py b/command/bdist_msi.py index f9205483..404f215f 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -6,6 +6,8 @@ """ Implements the bdist_msi command. """ +import sys, os +from sysconfig import get_python_version import sys, os from distutils.core import Command @@ -15,6 +17,7 @@ from distutils.version import StrictVersion from distutils.errors import DistutilsOptionError from distutils.util import get_platform from distutils import log + import msilib from msilib import schema, sequence, text from msilib import Directory, Feature, Dialog, add_data diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 2cf28234..71cc7988 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -8,12 +8,13 @@ __revision__ = "$Id$" import sys import os -from sysconfig import get_python_version, get_platform +from sysconfig import get_python_version from distutils.core import Command from distutils.dir_util import remove_tree from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils import log +from distutils.util import get_platform class bdist_wininst(Command): diff --git a/command/build.py b/command/build.py index d7b0e3c5..4d30f8ff 100644 --- a/command/build.py +++ b/command/build.py @@ -5,8 +5,8 @@ Implements the Distutils 'build' command.""" __revision__ = "$Id$" import sys, os -from sysconfig import get_platform +from distutils.util import get_platform from distutils.core import Command from distutils.errors import DistutilsOptionError diff --git a/command/build_ext.py b/command/build_ext.py index 39d37dab..8f41facd 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -9,14 +9,12 @@ __revision__ = "$Id$" import sys, os, re from warnings import warn -from sysconfig import get_platform - +from distutils.util import get_platform from distutils.core import Command from distutils.errors import * from distutils.ccompiler import customize_compiler from distutils.dep_util import newer_group from distutils.extension import Extension - from distutils import log # this keeps compatibility from 2.3 to 2.5 diff --git a/command/install.py b/command/install.py index 1f8d238a..2a6d4dd1 100644 --- a/command/install.py +++ b/command/install.py @@ -7,15 +7,14 @@ __revision__ = "$Id$" import sys import os -from sysconfig import (get_config_vars, get_platform, get_paths, get_path, - get_config_var) +from sysconfig import get_config_vars, get_paths, get_path, get_config_var from distutils import log from distutils.core import Command from distutils.debug import DEBUG from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file -from distutils.util import convert_path, change_root +from distutils.util import convert_path, change_root, get_platform from distutils.errors import DistutilsOptionError def _subst_vars(s, local_vars): diff --git a/msvc9compiler.py b/msvc9compiler.py index 38fc96f8..6455fffa 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -23,9 +23,9 @@ from distutils.errors import (DistutilsExecError, DistutilsPlatformError, CompileError, LibError, LinkError) from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log -import winreg +from distutils.util import get_platform -_sysconfig = __import__('sysconfig') +import winreg RegOpenKeyEx = winreg.OpenKeyEx RegEnumKey = winreg.EnumKey @@ -327,7 +327,7 @@ class MSVCCompiler(CCompiler) : # multi-init means we would need to check platform same each time... assert not self.initialized, "don't init multiple times" if plat_name is None: - plat_name = _sysconfig.get_platform() + plat_name = get_platform() # sanity check for platforms to prevent obscure errors later. ok_plats = 'win32', 'win-amd64', 'win-ia64' if plat_name not in ok_plats: @@ -348,12 +348,12 @@ class MSVCCompiler(CCompiler) : # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross # compile use 'x86' (ie, it runs the x86 compiler directly) # No idea how itanium handles this, if at all. - if plat_name == _sysconfig.get_platform() or plat_name == 'win32': + if plat_name == get_platform() or plat_name == 'win32': # native build or cross-compile to win32 plat_spec = PLAT_TO_VCVARS[plat_name] else: # cross compile from win32 -> some 64bit - plat_spec = PLAT_TO_VCVARS[_sysconfig.get_platform()] + '_' + \ + plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ PLAT_TO_VCVARS[plat_name] vc_env = query_vcvarsall(VERSION, plat_spec) diff --git a/sysconfig.py b/sysconfig.py index 48f22ad7..2561f573 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -25,6 +25,15 @@ from distutils.errors import DistutilsPlatformError # to avoid this module to shadow it _sysconfig = __import__('sysconfig') +# names defined here to keep backward compatibility +# for APIs that were relocated +get_python_version = _sysconfig.get_python_version +get_config_h_filename = _sysconfig.get_config_h_filename +parse_config_h = _sysconfig.parse_config_h +get_config_vars = _sysconfig.get_config_vars +get_config_var = _sysconfig.get_config_var +from distutils.ccompiler import customize_compiler + _DEPRECATION_MSG = ("distutils.sysconfig.%s is deprecated. " "Use the APIs provided by the sysconfig module instead") diff --git a/tests/test_util.py b/tests/test_util.py index ae6671de..896e1e07 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -90,125 +90,12 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): def _get_uname(self): return self._uname - def _test_get_platform(self): - - # windows XP, 32bits - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Intel)]') - sys.platform = 'win32' - self.assertEquals(get_platform(), 'win32') - - # windows XP, amd64 - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Amd64)]') - sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-amd64') - - # windows XP, itanium - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Itanium)]') - sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-ia64') - - # macbook - os.name = 'posix' - sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' - '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') - sys.platform = 'darwin' - self._set_uname(('Darwin', 'macziade', '8.11.1', - ('Darwin Kernel Version 8.11.1: ' - 'Wed Oct 10 18:23:28 PDT 2007; ' - 'root:xnu-792.25.20~1/RELEASE_I386'), 'PowerPC')) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' - - get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' - '-fwrapv -O3 -Wall -Wstrict-prototypes') - - maxsize = sys.maxsize - try: - sys.maxsize = 2147483647 - self.assertEquals(get_platform(), 'macosx-10.3-ppc') - sys.maxsize = 9223372036854775807 - self.assertEquals(get_platform(), 'macosx-10.3-ppc64') - finally: - sys.maxsize = maxsize - - - self._set_uname(('Darwin', 'macziade', '8.11.1', - ('Darwin Kernel Version 8.11.1: ' - 'Wed Oct 10 18:23:28 PDT 2007; ' - 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' - - get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' - '-fwrapv -O3 -Wall -Wstrict-prototypes') - - maxsize = sys.maxsize - try: - sys.maxsize = 2147483647 - self.assertEquals(get_platform(), 'macosx-10.3-i386') - sys.maxsize = 9223372036854775807 - self.assertEquals(get_platform(), 'macosx-10.3-x86_64') - finally: - sys.maxsize = maxsize - - # macbook with fat binaries (fat, universal or fat64) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' - get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEquals(get_platform(), 'macosx-10.4-fat') - - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEquals(get_platform(), 'macosx-10.4-intel') - - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat3') - - get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-universal') - - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEquals(get_platform(), 'macosx-10.4-fat64') - - for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): - get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3'%(arch,)) - - self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) - - # linux debian sarge - os.name = 'posix' - sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' - '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]') - sys.platform = 'linux2' - self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', - '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) - - self.assertEquals(get_platform(), 'linux-i686') - - # XXX more platforms to tests here + def test_get_platform(self): + platform = util.get_platform() + self.assertEquals(platform, get_platform()) + util.set_platform('MyOwnPlatform') + self.assertEquals('MyOwnPlatform', util.get_platform()) + util.set_platform(platform) def test_convert_path(self): # linux/mac diff --git a/util.py b/util.py index 0515fefd..8fd2ca07 100644 --- a/util.py +++ b/util.py @@ -16,6 +16,27 @@ from distutils.version import LooseVersion from distutils.errors import DistutilsByteCompileError _sysconfig = __import__('sysconfig') +_PLATFORM = None + +def get_platform(): + """Return a string that identifies the current platform. + + By default, will return the value returned by sysconfig.get_platform(), + but it can be changed by calling set_platform(). + """ + global _PLATFORM + if _PLATFORM is None: + _PLATFORM = _sysconfig.get_platform() + return _PLATFORM + +def set_platform(identifier): + """Sets the platform string identifier returned by get_platform(). + + Note that this change doesn't impact the value returned by + sysconfig.get_platform() and is local to Distutils + """ + global _PLATFORM + _PLATFORM = identifier def convert_path(pathname): """Return 'pathname' as a name that will work on the native filesystem. -- cgit v1.2.1 From f3b8f0e0dce15c006ebc11503282679119b7eb71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 2 Feb 2010 22:55:00 +0000 Subject: fixed a typo on distutils.sysconfig. thanks arfever --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 2d92dabf..a247bc2c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -9,7 +9,7 @@ Written by: Fred L. Drake, Jr. Email: **This module has been moved out of Distutils and will be removed from -Python in the next version (3.2)** +Python in the next version (3.3)** """ __revision__ = "$Id$" -- cgit v1.2.1 From 8f51dc176a1854fbf1f7c8612d67c63ca1c3d92f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 2 Feb 2010 23:16:13 +0000 Subject: Merged revisions 77919,77921-77922 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77919 | tarek.ziade | 2010-02-02 23:50:23 +0100 (Tue, 02 Feb 2010) | 1 line module reorganization + missing doctests ........ r77921 | tarek.ziade | 2010-02-02 23:54:28 +0100 (Tue, 02 Feb 2010) | 1 line sysconfig.get_scheme_names now returns a sorted tuple ........ r77922 | tarek.ziade | 2010-02-02 23:55:00 +0100 (Tue, 02 Feb 2010) | 1 line fixed a typo on distutils.sysconfig. thanks arfever ........ --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 2561f573..4a8629e6 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -9,7 +9,7 @@ Written by: Fred L. Drake, Jr. Email: **This module has been moved out of Distutils and will be removed from -Python in the next version (3.2)** +Python in the next version (3.3)** """ __revision__ = "$Id$" -- cgit v1.2.1 From efbb3664dba73095ae8d7ff40222b0dad5a13b75 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 3 Feb 2010 02:59:43 +0000 Subject: Merged revisions 77712,77740-77741,77756,77886,77902,77936 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77712 | tarek.ziade | 2010-01-23 11:52:57 -0600 (Sat, 23 Jan 2010) | 1 line fixed the 64bits tests for get_platform() - mac osx ........ r77740 | benjamin.peterson | 2010-01-24 21:58:21 -0600 (Sun, 24 Jan 2010) | 1 line compare types with is not == ........ r77741 | facundo.batista | 2010-01-25 00:15:01 -0600 (Mon, 25 Jan 2010) | 3 lines Added a note about Event.is_set() syntax being new to 2.6 ........ r77756 | tarek.ziade | 2010-01-26 11:20:37 -0600 (Tue, 26 Jan 2010) | 1 line fixed bdist_msi imports and added a test module for distutils.command.bdist_msi ........ r77886 | benjamin.peterson | 2010-01-31 12:09:34 -0600 (Sun, 31 Jan 2010) | 1 line move distutils.rst to different toc ........ r77902 | andrew.kuchling | 2010-01-31 20:04:26 -0600 (Sun, 31 Jan 2010) | 1 line Add various items ........ r77936 | andrew.kuchling | 2010-02-02 20:19:14 -0600 (Tue, 02 Feb 2010) | 1 line Add various items ........ --- command/bdist_msi.py | 5 +---- tests/test_bdist_msi.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 tests/test_bdist_msi.py diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 404f215f..f13c73b3 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -7,15 +7,12 @@ Implements the bdist_msi command. """ import sys, os -from sysconfig import get_python_version +from sysconfig import get_python_version, get_platform -import sys, os from distutils.core import Command from distutils.dir_util import remove_tree -from distutils.sysconfig import get_python_version from distutils.version import StrictVersion from distutils.errors import DistutilsOptionError -from distutils.util import get_platform from distutils import log import msilib diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py new file mode 100644 index 00000000..ba2d3e19 --- /dev/null +++ b/tests/test_bdist_msi.py @@ -0,0 +1,23 @@ +"""Tests for distutils.command.bdist_msi.""" +import unittest +import sys + +from distutils.tests import support + +@unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") +class BDistMSITestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_minial(self): + # minimal test XXX need more tests + from distutils.command.bdist_msi import bdist_msi + pkg_pth, dist = self.create_dist() + cmd = bdist_msi(dist) + cmd.ensure_finalized() + +def test_suite(): + return unittest.makeSuite(BDistMSITestCase) + +if __name__ == '__main__': + test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 375a738b125ed236d79369866fba47a360ddb6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 3 Feb 2010 15:38:12 +0000 Subject: leaving global attributes for backward compat --- command/install.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/command/install.py b/command/install.py index b5336fef..60e4df0f 100644 --- a/command/install.py +++ b/command/install.py @@ -17,6 +17,87 @@ from distutils.file_util import write_file from distutils.util import convert_path, change_root, get_platform from distutils.errors import DistutilsOptionError +# kept for backward compat, will be removed in 3.2 +if sys.version < "2.2": + WINDOWS_SCHEME = { + 'purelib': '$base', + 'platlib': '$base', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } +else: + WINDOWS_SCHEME = { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } + +INSTALL_SCHEMES = { + 'unix_prefix': { + 'purelib': '$base/lib/python$py_version_short/site-packages', + 'platlib': '$platbase/lib/python$py_version_short/site-packages', + 'headers': '$base/include/python$py_version_short/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'unix_home': { + 'purelib': '$base/lib/python', + 'platlib': '$base/lib/python', + 'headers': '$base/include/python/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'unix_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, + 'nt': WINDOWS_SCHEME, + 'nt_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', + 'scripts': '$userbase/Scripts', + 'data' : '$userbase', + }, + 'mac': { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + }, + 'mac_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/$py_version_short/include/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, + 'os2': { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + }, + 'os2_home': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, + } + +SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') +# end of backward compat + def _subst_vars(s, local_vars): try: return s.format(**local_vars) -- cgit v1.2.1 From a72113165de6217370d77b04b5abba938230953e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Wed, 3 Feb 2010 16:10:34 +0000 Subject: Merged revisions 77949 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r77949 | tarek.ziade | 2010-02-03 16:38:12 +0100 (Wed, 03 Feb 2010) | 1 line leaving global attributes for backward compat ........ --- command/install.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/command/install.py b/command/install.py index 2a6d4dd1..cfebeeab 100644 --- a/command/install.py +++ b/command/install.py @@ -17,6 +17,87 @@ from distutils.file_util import write_file from distutils.util import convert_path, change_root, get_platform from distutils.errors import DistutilsOptionError +# kept for backward compat, will be removed in 3.2 +if sys.version < "2.2": + WINDOWS_SCHEME = { + 'purelib': '$base', + 'platlib': '$base', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } +else: + WINDOWS_SCHEME = { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + } + +INSTALL_SCHEMES = { + 'unix_prefix': { + 'purelib': '$base/lib/python$py_version_short/site-packages', + 'platlib': '$platbase/lib/python$py_version_short/site-packages', + 'headers': '$base/include/python$py_version_short/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'unix_home': { + 'purelib': '$base/lib/python', + 'platlib': '$base/lib/python', + 'headers': '$base/include/python/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'unix_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, + 'nt': WINDOWS_SCHEME, + 'nt_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', + 'scripts': '$userbase/Scripts', + 'data' : '$userbase', + }, + 'mac': { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + }, + 'mac_user': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/$py_version_short/include/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, + 'os2': { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + }, + 'os2_home': { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + }, + } + +SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') +# end of backward compat + def _subst_vars(s, local_vars): try: return s.format(**local_vars) -- cgit v1.2.1 From e04f6e5ca792f5ac3df0dac51226396914f83789 Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Wed, 3 Feb 2010 22:06:03 +0000 Subject: Merged revisions 69304 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r69304 | neil.schemenauer | 2009-02-05 08:25:16 -0800 (Thu, 05 Feb 2009) | 4 lines Fix test_build_ext.py to work when building in a separate directory. Since "srcdir" should now be defined on all platforms, use it to find the module source. ........ --- tests/test_build_ext.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 93d18814..a1c236aa 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -13,11 +13,14 @@ from distutils.errors import DistutilsSetupError import unittest from test import test_support - # http://bugs.python.org/issue4373 # Don't load the xx module more than once. ALREADY_TESTED = False +def _get_source_filename(): + srcdir = sysconfig.get_config_var('srcdir') + return os.path.join(srcdir, 'Modules', 'xxmodule.c') + class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -28,9 +31,7 @@ class BuildExtTestCase(support.TempdirManager, self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) - - xx_c = os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') - shutil.copy(xx_c, self.tmp_dir) + shutil.copy(_get_source_filename(), self.tmp_dir) def test_build_ext(self): global ALREADY_TESTED @@ -387,9 +388,11 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(ext_path, wanted) def test_suite(): - if not sysconfig.python_build: + src = _get_source_filename() + if not os.path.exists(src): if test_support.verbose: - print 'test_build_ext: The test must be run in a python build dir' + print ('test_build_ext: Cannot find source code (test' + ' must run in python build dir)') return unittest.TestSuite() else: return unittest.makeSuite(BuildExtTestCase) -- cgit v1.2.1 From 6093446e3b99ec64d7b2207d36a9d9522114bd0e Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 6 Feb 2010 16:37:32 +0000 Subject: bump version to 2.7a3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 55aaf81a..977643bf 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7a2" +__version__ = "2.7a3" #--end constants-- -- cgit v1.2.1 From eea5ca2f97e7bc3ff5477d01b0add5c0800b98a3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 6 Feb 2010 23:53:52 +0000 Subject: Add missing import when running these tests standalone. --- tests/test_bdist.py | 4 +++- tests/test_bdist_dumb.py | 4 +++- tests/test_bdist_msi.py | 4 +++- tests/test_bdist_rpm.py | 4 +++- tests/test_bdist_wininst.py | 4 +++- tests/test_cmd.py | 4 ++-- tests/test_cygwinccompiler.py | 4 ++-- tests/test_emxccompiler.py | 4 ++-- tests/test_sysconfig.py | 1 + 9 files changed, 22 insertions(+), 11 deletions(-) diff --git a/tests/test_bdist.py b/tests/test_bdist.py index be3ec749..a37f4a9b 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -5,6 +5,8 @@ import os import tempfile import shutil +from test.test_support import run_unittest + from distutils.core import Distribution from distutils.command.bdist import bdist from distutils.tests import support @@ -40,4 +42,4 @@ def test_suite(): return unittest.makeSuite(BuildTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 5eaef2a9..f2220f47 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -11,6 +11,8 @@ try: except ImportError: zlib = None +from test.test_support import run_unittest + from distutils.core import Distribution from distutils.command.bdist_dumb import bdist_dumb from distutils.tests import support @@ -100,4 +102,4 @@ def test_suite(): return unittest.makeSuite(BuildDumbTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index ba2d3e19..7554e9f1 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -2,6 +2,8 @@ import unittest import sys +from test.test_support import run_unittest + from distutils.tests import support @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") @@ -20,4 +22,4 @@ def test_suite(): return unittest.makeSuite(BDistMSITestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 2aa257f7..25a5763a 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -6,6 +6,8 @@ import os import tempfile import shutil +from test.test_support import run_unittest + from distutils.core import Distribution from distutils.command.bdist_rpm import bdist_rpm from distutils.tests import support @@ -122,4 +124,4 @@ def test_suite(): return unittest.makeSuite(BuildRpmTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index 9b1ba6d1..c2b13b31 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -1,6 +1,8 @@ """Tests for distutils.command.bdist_wininst.""" import unittest +from test.test_support import run_unittest + from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support @@ -27,4 +29,4 @@ def test_suite(): return unittest.makeSuite(BuildWinInstTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 2174efbf..cfd64851 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,7 +1,7 @@ """Tests for distutils.cmd.""" import unittest import os -from test.test_support import captured_stdout +from test.test_support import captured_stdout, run_unittest from distutils.cmd import Command from distutils.dist import Distribution @@ -124,4 +124,4 @@ def test_suite(): return unittest.makeSuite(CommandTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index b67f987e..e97ac666 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -5,7 +5,7 @@ import os import warnings import sysconfig -from test.test_support import check_warnings +from test.test_support import check_warnings, run_unittest from test.test_support import captured_stdout from distutils import cygwinccompiler @@ -108,4 +108,4 @@ def test_suite(): return unittest.makeSuite(CygwinCCompilerTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_emxccompiler.py b/tests/test_emxccompiler.py index 6e1deced..d5e07dce 100644 --- a/tests/test_emxccompiler.py +++ b/tests/test_emxccompiler.py @@ -4,7 +4,7 @@ import sys import os import warnings -from test.test_support import check_warnings +from test.test_support import check_warnings, run_unittest from test.test_support import captured_stdout from distutils.emxccompiler import get_versions @@ -30,4 +30,4 @@ def test_suite(): return unittest.makeSuite(EmxCCompilerTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 9013220f..30664860 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -2,6 +2,7 @@ import os import test import unittest +import shutil from distutils import sysconfig from distutils.tests import support -- cgit v1.2.1 From 98075501e10ea81e40f9f0e5aff17083d0ff96aa Mon Sep 17 00:00:00 2001 From: "R. David Murray" Date: Tue, 23 Feb 2010 00:24:49 +0000 Subject: Issue 6292: for the moment at least, the test suite passes if run with -OO. Tests requiring docstrings are skipped. Patch by Brian Curtin, thanks to Matias Torchinsky for helping review and improve the patch. --- tests/test_build_py.py | 13 +++++++++++-- tests/test_extension.py | 17 ++++++++++++----- tests/test_install_lib.py | 17 +++++++++++------ 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 472591dc..3c8bc41b 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -16,7 +16,7 @@ class BuildPyTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): - def test_package_data(self): + def _setup_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") f.write("# Pretend this is a package.") @@ -52,10 +52,19 @@ class BuildPyTestCase(support.TempdirManager, self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) + return files + + def test_package_data(self): + files = self._setup_package_data() self.assertTrue("__init__.py" in files) - self.assertTrue("__init__.pyc" in files) self.assertTrue("README.txt" in files) + @unittest.skipIf(sys.flags.optimize >= 2, + "pyc files are not written with -O2 and above") + def test_package_data_pyc(self): + files = self._setup_package_data() + self.assertTrue("__init__.pyc" in files) + def test_empty_package_dir (self): # See SF 1668596/1720897. cwd = os.getcwd() diff --git a/tests/test_extension.py b/tests/test_extension.py index cffa9a09..0b5dfb82 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -1,6 +1,7 @@ """Tests for distutils.extension.""" -import unittest import os +import sys +import unittest import warnings from test.test_support import check_warnings @@ -32,16 +33,22 @@ class ExtensionTestCase(unittest.TestCase): self.assertEquals(names, wanted) - def test_extension_init(self): - # the first argument, which is the name, must be a string + @unittest.skipIf(sys.flags.optimize >= 2, + "Assertions are omitted with -O2 and above") + def test_extension_init_assertions(self): + # The first argument, which is the name, must be a string. self.assertRaises(AssertionError, Extension, 1, []) - ext = Extension('name', []) - self.assertEquals(ext.name, 'name') # the second argument, which is the list of files, must # be a list of strings self.assertRaises(AssertionError, Extension, 'name', 'file') self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) + + def test_extension_init(self): + ext = Extension('name', []) + self.assertEquals(ext.name, 'name') + + ext = Extension('name', ['file1', 'file2']) self.assertEquals(ext.sources, ['file1', 'file2']) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 99a6d906..13d27aba 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -1,6 +1,6 @@ """Tests for distutils.command.install_data.""" -import sys import os +import sys import unittest from distutils.command.install_lib import install_lib @@ -31,9 +31,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.finalize_options() self.assertEquals(cmd.optimize, 2) - @unittest.skipUnless(not sys.dont_write_bytecode, - 'byte-compile not supported') - def test_byte_compile(self): + def _setup_byte_compile(self): pkg_dir, dist = self.create_dist() cmd = install_lib(dist) cmd.compile = cmd.optimize = 1 @@ -41,8 +39,15 @@ class InstallLibTestCase(support.TempdirManager, f = os.path.join(pkg_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) - self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) + return pkg_dir + + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile not enabled') + def test_byte_compile(self): + pkg_dir = self._setup_byte_compile() + if sys.flags.optimize < 1: + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) + else: + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) def test_get_outputs(self): pkg_dir, dist = self.create_dist() -- cgit v1.2.1 From aac05903b0f3f790dbdd01e413a497ecf00cc1b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 23 Feb 2010 04:57:05 +0000 Subject: removed debugging code --- command/install.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/command/install.py b/command/install.py index 60e4df0f..fb17b4f6 100644 --- a/command/install.py +++ b/command/install.py @@ -499,10 +499,7 @@ class install(Command): if val is not None: if os.name == 'posix' or os.name == 'nt': val = os.path.expanduser(val) - try: - val = _subst_vars(val, self.config_vars) - except: - import pdb; pdb.set_trace() + val = _subst_vars(val, self.config_vars) setattr(self, attr, val) def expand_basedirs(self): -- cgit v1.2.1 From 8943dd0fe879512f88742a42893da24553cca38b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Tue, 23 Feb 2010 05:03:26 +0000 Subject: Merged revisions 78354 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r78354 | tarek.ziade | 2010-02-22 23:57:05 -0500 (Mon, 22 Feb 2010) | 1 line removed debugging code ........ --- command/install.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/command/install.py b/command/install.py index cfebeeab..31d03871 100644 --- a/command/install.py +++ b/command/install.py @@ -499,10 +499,7 @@ class install(Command): if val is not None: if os.name == 'posix' or os.name == 'nt': val = os.path.expanduser(val) - try: - val = _subst_vars(val, self.config_vars) - except: - import pdb; pdb.set_trace() + val = _subst_vars(val, self.config_vars) setattr(self, attr, val) def expand_basedirs(self): -- cgit v1.2.1 From 0d43be6127018ead86b1a3f2d20a9f97a85c92f3 Mon Sep 17 00:00:00 2001 From: "R. David Murray" Date: Wed, 24 Feb 2010 01:46:21 +0000 Subject: Merged revisions 78351 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r78351 | r.david.murray | 2010-02-22 19:24:49 -0500 (Mon, 22 Feb 2010) | 5 lines Issue 6292: for the moment at least, the test suite passes if run with -OO. Tests requiring docstrings are skipped. Patch by Brian Curtin, thanks to Matias Torchinsky for helping review and improve the patch. ........ --- tests/test_build_py.py | 13 +++++++++++-- tests/test_extension.py | 17 ++++++++++++----- tests/test_install_lib.py | 17 +++++++++++------ 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 3e45f6e8..61e213a5 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -16,7 +16,7 @@ class BuildPyTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): - def test_package_data(self): + def _setup_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") f.write("# Pretend this is a package.") @@ -52,10 +52,19 @@ class BuildPyTestCase(support.TempdirManager, self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) + return files + + def test_package_data(self): + files = self._setup_package_data() self.assertTrue("__init__.py" in files) - self.assertTrue("__init__.pyc" in files) self.assertTrue("README.txt" in files) + @unittest.skipIf(sys.flags.optimize >= 2, + "pyc files are not written with -O2 and above") + def test_package_data_pyc(self): + files = self._setup_package_data() + self.assertTrue("__init__.pyc" in files) + def test_empty_package_dir (self): # See SF 1668596/1720897. cwd = os.getcwd() diff --git a/tests/test_extension.py b/tests/test_extension.py index 9d3cfe6f..857284dd 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -1,6 +1,7 @@ """Tests for distutils.extension.""" -import unittest import os +import sys +import unittest import warnings from test.support import check_warnings @@ -32,16 +33,22 @@ class ExtensionTestCase(unittest.TestCase): self.assertEquals(names, wanted) - def test_extension_init(self): - # the first argument, which is the name, must be a string + @unittest.skipIf(sys.flags.optimize >= 2, + "Assertions are omitted with -O2 and above") + def test_extension_init_assertions(self): + # The first argument, which is the name, must be a string. self.assertRaises(AssertionError, Extension, 1, []) - ext = Extension('name', []) - self.assertEquals(ext.name, 'name') # the second argument, which is the list of files, must # be a list of strings self.assertRaises(AssertionError, Extension, 'name', 'file') self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) + + def test_extension_init(self): + ext = Extension('name', []) + self.assertEquals(ext.name, 'name') + + ext = Extension('name', ['file1', 'file2']) self.assertEquals(ext.sources, ['file1', 'file2']) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 99a6d906..13d27aba 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -1,6 +1,6 @@ """Tests for distutils.command.install_data.""" -import sys import os +import sys import unittest from distutils.command.install_lib import install_lib @@ -31,9 +31,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.finalize_options() self.assertEquals(cmd.optimize, 2) - @unittest.skipUnless(not sys.dont_write_bytecode, - 'byte-compile not supported') - def test_byte_compile(self): + def _setup_byte_compile(self): pkg_dir, dist = self.create_dist() cmd = install_lib(dist) cmd.compile = cmd.optimize = 1 @@ -41,8 +39,15 @@ class InstallLibTestCase(support.TempdirManager, f = os.path.join(pkg_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) - self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) + return pkg_dir + + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile not enabled') + def test_byte_compile(self): + pkg_dir = self._setup_byte_compile() + if sys.flags.optimize < 1: + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) + else: + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) def test_get_outputs(self): pkg_dir, dist = self.create_dist() -- cgit v1.2.1 From fa53c90a8e494240036abe332b3a0d4c1376ddd7 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 1 Mar 2010 22:10:45 +0000 Subject: Bump to 2.6.5 rc 1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b37308c5..8c2ab8f3 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.4" +__version__ = "2.6.5rc1" #--end constants-- -- cgit v1.2.1 From 687b1f10c00ef4970614a1450a74b8a9e3f5b9a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 5 Mar 2010 00:16:02 +0000 Subject: reverting partially distutils to its 2.6.x state so 2.7a4 looks more like the 2.7b1 in this. the whole revert will occur after a4 is tagged --- command/build_ext.py | 200 ++++++-------- command/install.py | 227 ++++++++-------- cygwinccompiler.py | 220 ++++++++++------ emxccompiler.py | 35 ++- extension.py | 71 ++--- sysconfig.py | 591 ++++++++++++++++++++++++++++++++++++------ tests/test_cygwinccompiler.py | 111 -------- tests/test_emxccompiler.py | 33 --- tests/test_extension.py | 78 ------ tests/test_install.py | 173 +------------ tests/test_unixccompiler.py | 8 +- tests/test_util.py | 260 +------------------ unixccompiler.py | 29 +-- util.py | 355 ++++++++++++++++--------- 14 files changed, 1157 insertions(+), 1234 deletions(-) delete mode 100644 tests/test_cygwinccompiler.py delete mode 100644 tests/test_emxccompiler.py delete mode 100755 tests/test_extension.py diff --git a/command/build_ext.py b/command/build_ext.py index 420d7f17..8248089f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,27 +4,21 @@ Implements the Distutils 'build_ext' command, for building extension modules (currently limited to C extensions, should accommodate C++ extensions ASAP).""" -__revision__ = "$Id$" +# This module should be kept compatible with Python 2.1. -import sys, os, re -from warnings import warn +__revision__ = "$Id$" -from distutils.util import get_platform +import sys, os, string, re +from types import * +from site import USER_BASE, USER_SITE from distutils.core import Command from distutils.errors import * -from distutils.ccompiler import customize_compiler +from distutils.sysconfig import customize_compiler, get_python_version from distutils.dep_util import newer_group from distutils.extension import Extension +from distutils.util import get_platform from distutils import log -# this keeps compatibility from 2.3 to 2.5 -if sys.version < "2.6": - USER_BASE = None - HAS_USER_SITE = False -else: - from site import USER_BASE - HAS_USER_SITE = True - if os.name == 'nt': from distutils.msvccompiler import get_build_version MSVC_VERSION = int(get_build_version()) @@ -40,7 +34,7 @@ def show_compilers (): show_compilers() -class build_ext(Command): +class build_ext (Command): description = "build C/C++ extensions (compile/link to build directory)" @@ -100,55 +94,18 @@ class build_ext(Command): "list of SWIG command line options"), ('swig=', None, "path to the SWIG executable"), + ('user', None, + "add user include, library and rpath"), ] - boolean_options = ['inplace', 'debug', 'force', 'swig-cpp'] - - if HAS_USER_SITE: - user_options.append(('user', None, - "add user include, library and rpath")) - boolean_options.append('user') + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] help_options = [ ('help-compiler', None, "list available compilers", show_compilers), ] - - # making 'compiler' a property to deprecate - # its usage as something else than a compiler type - # e.g. like a compiler instance - def __init__(self, dist): - self._compiler = None - Command.__init__(self, dist) - - def __setattr__(self, name, value): - # need this to make sure setattr() (used in distutils) - # doesn't kill our property - if name == 'compiler': - self._set_compiler(value) - else: - self.__dict__[name] = value - - def _set_compiler(self, compiler): - if not isinstance(compiler, str) and compiler is not None: - # we don't want to allow that anymore in the future - warn("'compiler' specifies the compiler type in build_ext. " - "If you want to get the compiler object itself, " - "use 'compiler_obj'", DeprecationWarning) - self._compiler = compiler - - def _get_compiler(self): - if not isinstance(self._compiler, str) and self._compiler is not None: - # we don't want to allow that anymore in the future - warn("'compiler' specifies the compiler type in build_ext. " - "If you want to get the compiler object itself, " - "use 'compiler_obj'", DeprecationWarning) - return self._compiler - - compiler = property(_get_compiler, _set_compiler) - - def initialize_options(self): + def initialize_options (self): self.extensions = None self.build_lib = None self.plat_name = None @@ -172,7 +129,8 @@ class build_ext(Command): self.user = None def finalize_options(self): - _sysconfig = __import__('sysconfig') + from distutils import sysconfig + self.set_undefined_options('build', ('build_lib', 'build_lib'), ('build_temp', 'build_temp'), @@ -189,8 +147,8 @@ class build_ext(Command): # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. - py_include = _sysconfig.get_path('include') - plat_py_include = _sysconfig.get_path('platinclude') + py_include = sysconfig.get_python_inc() + plat_py_include = sysconfig.get_python_inc(plat_specific=1) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if isinstance(self.include_dirs, str): @@ -211,13 +169,13 @@ class build_ext(Command): self.libraries = [] if self.library_dirs is None: self.library_dirs = [] - elif isinstance(self.library_dirs, str): - self.library_dirs = self.library_dirs.split(os.pathsep) + elif type(self.library_dirs) is StringType: + self.library_dirs = string.split(self.library_dirs, os.pathsep) if self.rpath is None: self.rpath = [] - elif isinstance(self.rpath, str): - self.rpath = self.rpath.split(os.pathsep) + elif type(self.rpath) is StringType: + self.rpath = string.split(self.rpath, os.pathsep) # for extensions under windows use different directories # for Release and Debug builds. @@ -268,7 +226,7 @@ class build_ext(Command): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", - "python" + _sysconfig.get_python_version(), + "python" + get_python_version(), "config")) else: # building python standard extensions @@ -276,13 +234,13 @@ class build_ext(Command): # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs - _sysconfig.get_config_var('Py_ENABLE_SHARED') + sysconfig.get_config_var('Py_ENABLE_SHARED') if ((sys.platform.startswith('linux') or sys.platform.startswith('gnu') or sys.platform.startswith('sunos')) - and _sysconfig.get_config_var('Py_ENABLE_SHARED')): + and sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions - self.library_dirs.append(_sysconfig.get_config_var('LIBDIR')) + self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: # building python standard extensions self.library_dirs.append('.') @@ -294,7 +252,7 @@ class build_ext(Command): if self.define: defines = self.define.split(',') - self.define = [(symbol, '1') for symbol in defines] + self.define = map(lambda symbol: (symbol, '1'), defines) # The option for macros to undefine is also a string from the # option parsing, but has to be a list. Multiple symbols can also @@ -345,50 +303,38 @@ class build_ext(Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - - # used to prevent the usage of an existing compiler for the - # compiler option when calling new_compiler() - # this will be removed in 3.3 and 2.8 - if not isinstance(self._compiler, str): - self._compiler = None - - self.compiler_obj = new_compiler(compiler=self._compiler, - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force) - - # used to keep the compiler object reachable with - # "self.compiler". this will be removed in 3.3 and 2.8 - self._compiler = self.compiler_obj - - customize_compiler(self.compiler_obj) + self.compiler = new_compiler(compiler=self.compiler, + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) + customize_compiler(self.compiler) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): - self.compiler_obj.initialize(self.plat_name) + self.compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self.compiler_obj.set_include_dirs(self.include_dirs) + self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: - self.compiler_obj.define_macro(name, value) + self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self.compiler_obj.undefine_macro(macro) + self.compiler.undefine_macro(macro) if self.libraries is not None: - self.compiler_obj.set_libraries(self.libraries) + self.compiler.set_libraries(self.libraries) if self.library_dirs is not None: - self.compiler_obj.set_library_dirs(self.library_dirs) + self.compiler.set_library_dirs(self.library_dirs) if self.rpath is not None: - self.compiler_obj.set_runtime_library_dirs(self.rpath) + self.compiler.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self.compiler_obj.set_link_objects(self.link_objects) + self.compiler.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() @@ -500,17 +446,11 @@ class build_ext(Command): self.check_extensions_list(self.extensions) for ext in self.extensions: - try: - self.build_extension(ext) - except (CCompilerError, DistutilsError, CompileError), e: - if not ext.optional: - raise - self.warn('building extension "%s" failed: %s' % - (ext.name, e)) + self.build_extension(ext) def build_extension(self, ext): sources = ext.sources - if sources is None or not isinstance(sources, (list, tuple)): + if sources is None or type(sources) not in (ListType, TupleType): raise DistutilsSetupError, \ ("in 'ext_modules' option (extension '%s'), " + "'sources' must be present and must be " + @@ -550,13 +490,13 @@ class build_ext(Command): for undef in ext.undef_macros: macros.append((undef,)) - objects = self.compiler_obj.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args, - depends=ext.depends) + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends) # XXX -- this is a Vile HACK! # @@ -577,9 +517,9 @@ class build_ext(Command): extra_args = ext.extra_link_args or [] # Detect target language, if not provided - language = ext.language or self.compiler_obj.detect_language(sources) + language = ext.language or self.compiler.detect_language(sources) - self.compiler_obj.link_shared_object( + self.compiler.link_shared_object( objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, @@ -591,12 +531,14 @@ class build_ext(Command): target_lang=language) - def swig_sources(self, sources, extension): + def swig_sources (self, sources, extension): + """Walk the list of source files in 'sources', looking for SWIG interface (.i) files. Run SWIG on all that are found, and return a modified 'sources' list with SWIG source files replaced by the generated C (or C++) files. """ + new_sources = [] swig_sources = [] swig_targets = {} @@ -645,7 +587,9 @@ class build_ext(Command): return new_sources - def find_swig(self): + # swig_sources () + + def find_swig (self): """Return the name of the SWIG executable. On Unix, this is just "swig" -- it should be in the PATH. Tries a bit harder on Windows. @@ -674,6 +618,8 @@ class build_ext(Command): ("I don't know how to find (much less run) SWIG " "on platform '%s'") % os.name + # find_swig () + # -- Name generators ----------------------------------------------- # (extension names, filenames, whatever) def get_ext_fullpath(self, ext_name): @@ -682,9 +628,14 @@ class build_ext(Command): The file is located in `build_lib` or directly in the package (inplace option). """ + # makes sure the extension name is only using dots + all_dots = string.maketrans('/'+os.sep, '..') + ext_name = ext_name.translate(all_dots) + fullname = self.get_ext_fullname(ext_name) modpath = fullname.split('.') - filename = self.get_ext_filename(modpath[-1]) + filename = self.get_ext_filename(ext_name) + filename = os.path.split(filename)[-1] if not self.inplace: # no further work needed @@ -717,18 +668,18 @@ class build_ext(Command): of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ - _sysconfig = __import__('sysconfig') - ext_path = ext_name.split('.') + from distutils.sysconfig import get_config_var + ext_path = string.split(ext_name, '.') # OS/2 has an 8 character module (extension) limit :-( if os.name == "os2": ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] # extensions in debug_mode are named 'module_d.pyd' under windows - so_ext = _sysconfig.get_config_var('SO') + so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: - return os.path.join(*ext_path) + '_d' + so_ext + return apply(os.path.join, ext_path) + '_d' + so_ext return os.path.join(*ext_path) + so_ext - def get_export_symbols(self, ext): + def get_export_symbols (self, ext): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not provided, "init" + module_name. Only relevant on Windows, where @@ -739,7 +690,7 @@ class build_ext(Command): ext.export_symbols.append(initfunc_name) return ext.export_symbols - def get_libraries(self, ext): + def get_libraries (self, ext): """Return the list of libraries to link against when building a shared extension. On most platforms, this is just 'ext.libraries'; on Windows and OS/2, we add the Python library (eg. python20.dll). @@ -751,7 +702,7 @@ class build_ext(Command): # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils.msvccompiler import MSVCCompiler - if not isinstance(self.compiler_obj, MSVCCompiler): + if not isinstance(self.compiler, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' @@ -783,13 +734,14 @@ class build_ext(Command): # extensions, it is a reference to the original list return ext.libraries + [pythonlib] elif sys.platform[:6] == "atheos": - _sysconfig = __import__('sysconfig') + from distutils import sysconfig + template = "python%d.%d" pythonlib = (template % (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) # Get SHLIBS from Makefile extra = [] - for lib in _sysconfig.get_config_var('SHLIBS').split(): + for lib in sysconfig.get_config_var('SHLIBS').split(): if lib.startswith('-l'): extra.append(lib[2:]) else: @@ -803,11 +755,13 @@ class build_ext(Command): return ext.libraries else: - _sysconfig = __import__('sysconfig') - if _sysconfig.get_config_var('Py_ENABLE_SHARED'): + from distutils import sysconfig + if sysconfig.get_config_var('Py_ENABLE_SHARED'): template = "python%d.%d" pythonlib = (template % (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) return ext.libraries + [pythonlib] else: return ext.libraries + +# class build_ext diff --git a/command/install.py b/command/install.py index fb17b4f6..44c76926 100644 --- a/command/install.py +++ b/command/install.py @@ -2,22 +2,26 @@ Implements the Distutils 'install' command.""" -__revision__ = "$Id$" +from distutils import log -import sys -import os +# This module should be kept compatible with Python 2.1. -from sysconfig import get_config_vars, get_paths, get_path, get_config_var +__revision__ = "$Id$" -from distutils import log +import sys, os, string +from types import * from distutils.core import Command from distutils.debug import DEBUG +from distutils.sysconfig import get_config_vars from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file -from distutils.util import convert_path, change_root, get_platform +from distutils.util import convert_path, subst_vars, change_root +from distutils.util import get_platform from distutils.errors import DistutilsOptionError +from site import USER_BASE +from site import USER_SITE + -# kept for backward compat, will be removed in 3.2 if sys.version < "2.2": WINDOWS_SCHEME = { 'purelib': '$base', @@ -95,19 +99,13 @@ INSTALL_SCHEMES = { }, } +# The keys to an installation scheme; if any new types of files are to be +# installed, be sure to add an entry to every installation scheme above, +# and to SCHEME_KEYS here. SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') -# end of backward compat -def _subst_vars(s, local_vars): - try: - return s.format(**local_vars) - except KeyError: - try: - return s.format(**os.environ) - except KeyError, var: - raise AttributeError('{%s}' % var) -class install(Command): +class install (Command): description = "install everything from build directory" @@ -119,6 +117,8 @@ class install(Command): "(Unix only) prefix for platform-specific files"), ('home=', None, "(Unix only) home directory to install under"), + ('user', None, + "install in user site-package '%s'" % USER_SITE), # Or, just set the base director(y|ies) ('install-base=', None, @@ -170,17 +170,12 @@ class install(Command): "filename in which to record list of installed files"), ] - boolean_options = ['compile', 'force', 'skip-build'] - - user_options.append(('user', None, - "install in user site-package '%s'" % \ - get_path('purelib', '%s_user' % os.name))) - boolean_options.append('user') + boolean_options = ['compile', 'force', 'skip-build', 'user'] negative_opt = {'no-compile' : 'compile'} - def initialize_options(self): - """Initializes options.""" + def initialize_options (self): + # High-level options: these select both an installation base # and scheme. self.prefix = None @@ -205,8 +200,8 @@ class install(Command): self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None - self.install_userbase = get_config_var('userbase') - self.install_usersite = get_path('purelib', '%s_user' % os.name) + self.install_userbase = USER_BASE + self.install_usersite = USER_SITE self.compile = None self.optimize = None @@ -256,8 +251,8 @@ class install(Command): # party Python modules on various platforms given a wide # array of user input is decided. Yes, it's quite complex!) - def finalize_options(self): - """Finalizes options.""" + def finalize_options (self): + # This method (and its pliant slaves, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default # installation directories for modules, extension modules, and @@ -315,10 +310,8 @@ class install(Command): # $platbase in the other installation directories and not worry # about needing recursive variable expansion (shudder). - py_version = sys.version.split()[0] - prefix, exec_prefix, srcdir = get_config_vars('prefix', 'exec_prefix', - 'srcdir') - + py_version = (string.split(sys.version))[0] + (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), 'dist_fullname': self.distribution.get_fullname(), @@ -329,11 +322,9 @@ class install(Command): 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, - 'srcdir': srcdir, + 'userbase': self.install_userbase, + 'usersite': self.install_usersite, } - - self.config_vars['userbase'] = self.install_userbase - self.config_vars['usersite'] = self.install_usersite self.expand_basedirs() self.dump_dirs("post-expand_basedirs()") @@ -399,27 +390,29 @@ class install(Command): # Punt on doc directories for now -- after all, we're punting on # documentation completely! - def dump_dirs(self, msg): - """Dumps the list of user options.""" - if not DEBUG: - return - from distutils.fancy_getopt import longopt_xlate - log.debug(msg + ":") - for opt in self.user_options: - opt_name = opt[0] - if opt_name[-1] == "=": - opt_name = opt_name[0:-1] - if opt_name in self.negative_opt: - opt_name = self.negative_opt[opt_name] - opt_name = opt_name.translate(longopt_xlate) - val = not getattr(self, opt_name) - else: - opt_name = opt_name.translate(longopt_xlate) - val = getattr(self, opt_name) - log.debug(" %s: %s" % (opt_name, val)) + # finalize_options () + + + def dump_dirs (self, msg): + if DEBUG: + from distutils.fancy_getopt import longopt_xlate + print msg + ":" + for opt in self.user_options: + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + if opt_name in self.negative_opt: + opt_name = string.translate(self.negative_opt[opt_name], + longopt_xlate) + val = not getattr(self, opt_name) + else: + opt_name = string.translate(opt_name, longopt_xlate) + val = getattr(self, opt_name) + print " %s: %s" % (opt_name, val) + + + def finalize_unix (self): - def finalize_unix(self): - """Finalizes options for posix platforms.""" if self.install_base is not None or self.install_platbase is not None: if ((self.install_lib is None and self.install_purelib is None and @@ -437,10 +430,10 @@ class install(Command): raise DistutilsPlatformError( "User base directory is not specified") self.install_base = self.install_platbase = self.install_userbase - self.select_scheme("posix_user") + self.select_scheme("unix_user") elif self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme("posix_home") + self.select_scheme("unix_home") else: if self.prefix is None: if self.exec_prefix is not None: @@ -456,10 +449,13 @@ class install(Command): self.install_base = self.prefix self.install_platbase = self.exec_prefix - self.select_scheme("posix_prefix") + self.select_scheme("unix_prefix") + + # finalize_unix () + + + def finalize_other (self): # Windows and Mac OS for now - def finalize_other(self): - """Finalizes options for non-posix platforms""" if self.user: if self.install_userbase is None: raise DistutilsPlatformError( @@ -468,7 +464,7 @@ class install(Command): self.select_scheme(os.name + "_user") elif self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme("posix_home") + self.select_scheme("unix_home") else: if self.prefix is None: self.prefix = os.path.normpath(sys.prefix) @@ -480,58 +476,61 @@ class install(Command): raise DistutilsPlatformError, \ "I don't know how to install stuff on '%s'" % os.name - def select_scheme(self, name): - """Sets the install directories by applying the install schemes.""" + # finalize_other () + + + def select_scheme (self, name): # it's the caller's problem if they supply a bad name! - scheme = get_paths(name, expand=False) - for key, value in scheme.items(): - if key == 'platinclude': - key = 'headers' - value = os.path.join(value, self.distribution.get_name()) + scheme = INSTALL_SCHEMES[name] + for key in SCHEME_KEYS: attrname = 'install_' + key - if hasattr(self, attrname): - if getattr(self, attrname) is None: - setattr(self, attrname, value) + if getattr(self, attrname) is None: + setattr(self, attrname, scheme[key]) + - def _expand_attrs(self, attrs): + def _expand_attrs (self, attrs): for attr in attrs: val = getattr(self, attr) if val is not None: if os.name == 'posix' or os.name == 'nt': val = os.path.expanduser(val) - val = _subst_vars(val, self.config_vars) + val = subst_vars(val, self.config_vars) setattr(self, attr, val) - def expand_basedirs(self): - """Calls `os.path.expanduser` on install_base, install_platbase and - root.""" - self._expand_attrs(['install_base', 'install_platbase', 'root']) - def expand_dirs(self): - """Calls `os.path.expanduser` on install dirs.""" - self._expand_attrs(['install_purelib', 'install_platlib', - 'install_lib', 'install_headers', - 'install_scripts', 'install_data',]) + def expand_basedirs (self): + self._expand_attrs(['install_base', + 'install_platbase', + 'root']) - def convert_paths(self, *names): - """Call `convert_path` over `names`.""" + def expand_dirs (self): + self._expand_attrs(['install_purelib', + 'install_platlib', + 'install_lib', + 'install_headers', + 'install_scripts', + 'install_data',]) + + + def convert_paths (self, *names): for name in names: attr = "install_" + name setattr(self, attr, convert_path(getattr(self, attr))) - def handle_extra_path(self): - """Set `path_file` and `extra_dirs` using `extra_path`.""" + + def handle_extra_path (self): + if self.extra_path is None: self.extra_path = self.distribution.extra_path if self.extra_path is not None: - if isinstance(self.extra_path, str): - self.extra_path = self.extra_path.split(',') + if type(self.extra_path) is StringType: + self.extra_path = string.split(self.extra_path, ',') if len(self.extra_path) == 1: path_file = extra_dirs = self.extra_path[0] elif len(self.extra_path) == 2: - path_file, extra_dirs = self.extra_path + (path_file, extra_dirs) = self.extra_path else: raise DistutilsOptionError, \ ("'extra_path' option must be a list, tuple, or " @@ -540,6 +539,7 @@ class install(Command): # convert to local form in case Unix notation used (as it # should be in setup scripts) extra_dirs = convert_path(extra_dirs) + else: path_file = None extra_dirs = '' @@ -549,14 +549,17 @@ class install(Command): self.path_file = path_file self.extra_dirs = extra_dirs - def change_roots(self, *names): - """Change the install direcories pointed by name using root.""" + # handle_extra_path () + + + def change_roots (self, *names): for name in names: attr = "install_" + name setattr(self, attr, change_root(self.root, getattr(self, attr))) def create_home_path(self): - """Create directories under ~.""" + """Create directories under ~ + """ if not self.user: return home = convert_path(os.path.expanduser("~")) @@ -567,8 +570,8 @@ class install(Command): # -- Command execution methods ------------------------------------- - def run(self): - """Runs the command.""" + def run (self): + # Obviously have to build before we can install if not self.skip_build: self.run_command('build') @@ -611,8 +614,9 @@ class install(Command): "you'll have to change the search path yourself"), self.install_lib) - def create_path_file(self): - """Creates the .pth file""" + # run () + + def create_path_file (self): filename = os.path.join(self.install_libbase, self.path_file + ".pth") if self.install_path_file: @@ -625,8 +629,8 @@ class install(Command): # -- Reporting methods --------------------------------------------- - def get_outputs(self): - """Assembles the outputs of all the sub-commands.""" + def get_outputs (self): + # Assemble the outputs of all the sub-commands. outputs = [] for cmd_name in self.get_sub_commands(): cmd = self.get_finalized_command(cmd_name) @@ -642,8 +646,7 @@ class install(Command): return outputs - def get_inputs(self): - """Returns the inputs of all the sub-commands""" + def get_inputs (self): # XXX gee, this looks familiar ;-( inputs = [] for cmd_name in self.get_sub_commands(): @@ -652,29 +655,25 @@ class install(Command): return inputs + # -- Predicates for sub-command list ------------------------------- - def has_lib(self): - """Returns true if the current distribution has any Python + def has_lib (self): + """Return true if the current distribution has any Python modules to install.""" return (self.distribution.has_pure_modules() or self.distribution.has_ext_modules()) - def has_headers(self): - """Returns true if the current distribution has any headers to - install.""" + def has_headers (self): return self.distribution.has_headers() - def has_scripts(self): - """Returns true if the current distribution has any scripts to. - install.""" + def has_scripts (self): return self.distribution.has_scripts() - def has_data(self): - """Returns true if the current distribution has any data to. - install.""" + def has_data (self): return self.distribution.has_data_files() + # 'sub_commands': a list of commands this command might have to run to # get its work done. See cmd.py for more info. sub_commands = [('install_lib', has_lib), @@ -683,3 +682,5 @@ class install(Command): ('install_data', has_data), ('install_egg_info', lambda self:True), ] + +# class install diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 2a61bd5a..2dabc0f0 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -45,18 +45,16 @@ cygwin in no-cygwin mode). # * mingw gcc 3.2/ld 2.13 works # (ld supports -shared) -__revision__ = "$Id$" +# This module should be kept compatible with Python 2.1. -import os -import sys -import copy -import re -from warnings import warn +__revision__ = "$Id$" +import os,sys,copy +from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils.util import get_compiler_versions +from distutils import log def get_msvcr(): """Include the appropriate MSVC runtime library if Python was built @@ -81,9 +79,8 @@ def get_msvcr(): raise ValueError("Unknown MS Compiler version %s " % msc_ver) -class CygwinCCompiler(UnixCCompiler): - """ Handles the Cygwin port of the GNU C compiler to Windows. - """ +class CygwinCCompiler (UnixCCompiler): + compiler_type = 'cygwin' obj_extension = ".o" static_lib_extension = ".a" @@ -92,11 +89,11 @@ class CygwinCCompiler(UnixCCompiler): shared_lib_format = "%s%s" exe_extension = ".exe" - def __init__(self, verbose=0, dry_run=0, force=0): + def __init__ (self, verbose=0, dry_run=0, force=0): - UnixCCompiler.__init__(self, verbose, dry_run, force) + UnixCCompiler.__init__ (self, verbose, dry_run, force) - status, details = check_config_h() + (status, details) = check_config_h() self.debug_print("Python's GCC status: %s (details: %s)" % (status, details)) if status is not CONFIG_H_OK: @@ -107,7 +104,7 @@ class CygwinCCompiler(UnixCCompiler): % details) self.gcc_version, self.ld_version, self.dllwrap_version = \ - get_compiler_versions() + get_versions() self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % (self.gcc_version, self.ld_version, @@ -151,8 +148,10 @@ class CygwinCCompiler(UnixCCompiler): # with MSVC 7.0 or later. self.dll_libraries = get_msvcr() + # __init__ () + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): - """Compiles the source by spawing GCC and windres if needed.""" if ext == '.rc' or ext == '.res': # gcc needs '.res' and '.rc' compiled to object files !!! try: @@ -166,11 +165,21 @@ class CygwinCCompiler(UnixCCompiler): except DistutilsExecError, msg: raise CompileError, msg - def link(self, target_desc, objects, output_filename, output_dir=None, - libraries=None, library_dirs=None, runtime_library_dirs=None, - export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None, build_temp=None, target_lang=None): - """Link the objects.""" + def link (self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + # use separate copies, so we can modify the lists extra_preargs = copy.copy(extra_preargs or []) libraries = copy.copy(libraries or []) @@ -235,44 +244,64 @@ class CygwinCCompiler(UnixCCompiler): if not debug: extra_preargs.append("-s") - UnixCCompiler.link(self, target_desc, objects, output_filename, - output_dir, libraries, library_dirs, + UnixCCompiler.link(self, + target_desc, + objects, + output_filename, + output_dir, + libraries, + library_dirs, runtime_library_dirs, None, # export_symbols, we do this in our def-file - debug, extra_preargs, extra_postargs, build_temp, + debug, + extra_preargs, + extra_postargs, + build_temp, target_lang) + # link () + # -- Miscellaneous methods ----------------------------------------- - def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): - """Adds supports for rc and res files.""" - if output_dir is None: - output_dir = '' + # overwrite the one from CCompiler to support rc and res-files + def object_filenames (self, + source_filenames, + strip_dir=0, + output_dir=''): + if output_dir is None: output_dir = '' obj_names = [] for src_name in source_filenames: # use normcase to make sure '.rc' is really '.rc' and not '.RC' - base, ext = os.path.splitext(os.path.normcase(src_name)) + (base, ext) = os.path.splitext (os.path.normcase(src_name)) if ext not in (self.src_extensions + ['.rc','.res']): raise UnknownFileError, \ - "unknown file type '%s' (from '%s')" % (ext, src_name) + "unknown file type '%s' (from '%s')" % \ + (ext, src_name) if strip_dir: base = os.path.basename (base) - if ext in ('.res', '.rc'): + if ext == '.res' or ext == '.rc': # these need to be compiled to object files - obj_names.append (os.path.join(output_dir, - base + ext + self.obj_extension)) + obj_names.append (os.path.join (output_dir, + base + ext + self.obj_extension)) else: - obj_names.append (os.path.join(output_dir, - base + self.obj_extension)) + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) return obj_names + # object_filenames () + +# class CygwinCCompiler + + # the same as cygwin plus some additional parameters -class Mingw32CCompiler(CygwinCCompiler): - """ Handles the Mingw32 port of the GNU C compiler to Windows. - """ +class Mingw32CCompiler (CygwinCCompiler): + compiler_type = 'mingw32' - def __init__(self, verbose=0, dry_run=0, force=0): + def __init__ (self, + verbose=0, + dry_run=0, + force=0): CygwinCCompiler.__init__ (self, verbose, dry_run, force) @@ -308,6 +337,10 @@ class Mingw32CCompiler(CygwinCCompiler): # with MSVC 7.0 or later. self.dll_libraries = get_msvcr() + # __init__ () + +# class Mingw32CCompiler + # Because these compilers aren't configured in Python's pyconfig.h file by # default, we should at least warn the user if he is using a unmodified # version. @@ -317,16 +350,16 @@ CONFIG_H_NOTOK = "not ok" CONFIG_H_UNCERTAIN = "uncertain" def check_config_h(): - """Check if the current Python installation appears amenable to building - extensions with GCC. - - Returns a tuple (status, details), where 'status' is one of the following - constants: - - - CONFIG_H_OK: all is well, go ahead and compile - - CONFIG_H_NOTOK: doesn't look good - - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h + """Check if the current Python installation (specifically, pyconfig.h) + appears amenable to building extensions with GCC. Returns a tuple + (status, details), where 'status' is one of the following constants: + CONFIG_H_OK + all is well, go ahead and compile + CONFIG_H_NOTOK + doesn't look good + CONFIG_H_UNCERTAIN + not sure -- unable to read pyconfig.h 'details' is a human-readable string explaining the situation. Note there are two ways to conclude "OK": either 'sys.version' contains @@ -337,45 +370,78 @@ def check_config_h(): # XXX since this function also checks sys.version, it's not strictly a # "pyconfig.h" check -- should probably be renamed... - _sysconfig = __import__('sysconfig') + from distutils import sysconfig + import string + # if sys.version contains GCC then python was compiled with + # GCC, and the pyconfig.h file should be OK + if string.find(sys.version,"GCC") >= 0: + return (CONFIG_H_OK, "sys.version mentions 'GCC'") - # if sys.version contains GCC then python was compiled with GCC, and the - # pyconfig.h file should be OK - if "GCC" in sys.version: - return CONFIG_H_OK, "sys.version mentions 'GCC'" - - # let's see if __GNUC__ is mentioned in python.h - fn = _sysconfig.get_config_h_filename() + fn = sysconfig.get_config_h_filename() try: - with open(fn) as config_h: - if "__GNUC__" in config_h.read(): - return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn - else: - return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn + # It would probably better to read single lines to search. + # But we do this only once, and it is fast enough + f = open(fn) + s = f.read() + f.close() + except IOError, exc: + # if we can't read this file, we cannot say it is wrong + # the compiler will complain later about this file as missing return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) -class _Deprecated_SRE_Pattern(object): - def __init__(self, pattern): - self.pattern = pattern + else: + # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar + if string.find(s,"__GNUC__") >= 0: + return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) + else: + return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) - def __getattr__(self, name): - if name in ('findall', 'finditer', 'match', 'scanner', 'search', - 'split', 'sub', 'subn'): - warn("'distutils.cygwinccompiler.RE_VERSION' is deprecated " - "and will be removed in the next version", DeprecationWarning) - return getattr(self.pattern, name) -RE_VERSION = _Deprecated_SRE_Pattern(re.compile('(\d+\.\d+(\.\d+)*)')) def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. - - If not possible it returns None for it. + If not possible it returns None for it. """ - warn("'distutils.cygwinccompiler.get_versions' is deprecated " - "use 'distutils.util.get_compiler_versions' instead", - DeprecationWarning) - - return get_compiler_versions() + from distutils.version import LooseVersion + from distutils.spawn import find_executable + import re + + gcc_exe = find_executable('gcc') + if gcc_exe: + out = os.popen(gcc_exe + ' -dumpversion','r') + out_string = out.read() + out.close() + result = re.search('(\d+\.\d+(\.\d+)*)',out_string) + if result: + gcc_version = LooseVersion(result.group(1)) + else: + gcc_version = None + else: + gcc_version = None + ld_exe = find_executable('ld') + if ld_exe: + out = os.popen(ld_exe + ' -v','r') + out_string = out.read() + out.close() + result = re.search('(\d+\.\d+(\.\d+)*)',out_string) + if result: + ld_version = LooseVersion(result.group(1)) + else: + ld_version = None + else: + ld_version = None + dllwrap_exe = find_executable('dllwrap') + if dllwrap_exe: + out = os.popen(dllwrap_exe + ' --version','r') + out_string = out.read() + out.close() + result = re.search(' (\d+\.\d+(\.\d+)*)',out_string) + if result: + dllwrap_version = LooseVersion(result.group(1)) + else: + dllwrap_version = None + else: + dllwrap_version = None + return (gcc_version, ld_version, dllwrap_version) diff --git a/emxccompiler.py b/emxccompiler.py index f2f77e52..f52e6323 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -21,13 +21,12 @@ handles the EMX port of the GNU C compiler to OS/2. __revision__ = "$Id$" -import os, sys, copy -from warnings import warn - +import os,sys,copy +from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils.util import get_compiler_versions +from distutils import log class EMXCCompiler (UnixCCompiler): @@ -56,8 +55,8 @@ class EMXCCompiler (UnixCCompiler): ("Reason: %s." % details) + "Compiling may fail because of undefined preprocessor macros.") - gcc_version, ld_version, dllwrap_version = get_compiler_versions() - self.gcc_version, self.ld_version = gcc_version, ld_version + (self.gcc_version, self.ld_version) = \ + get_versions() self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" % (self.gcc_version, self.ld_version) ) @@ -294,11 +293,23 @@ def get_versions(): """ Try to find out the versions of gcc and ld. If not possible it returns None for it. """ - warn("'distutils.emxccompiler.get_versions' is deprecated " - "use 'distutils.util.get_compiler_versions' instead", - DeprecationWarning) - + from distutils.version import StrictVersion + from distutils.spawn import find_executable + import re + + gcc_exe = find_executable('gcc') + if gcc_exe: + out = os.popen(gcc_exe + ' -dumpversion','r') + out_string = out.read() + out.close() + result = re.search('(\d+\.\d+\.\d+)',out_string) + if result: + gcc_version = StrictVersion(result.group(1)) + else: + gcc_version = None + else: + gcc_version = None # EMX ld has no way of reporting version number, and we use GCC # anyway - so we can link OMF DLLs - gcc_version, ld_version, dllwrap_version = get_compiler_versions() - return gcc_version, None + ld_version = None + return (gcc_version, ld_version) diff --git a/extension.py b/extension.py index 244b1e78..440d128c 100644 --- a/extension.py +++ b/extension.py @@ -5,8 +5,13 @@ modules in setup scripts.""" __revision__ = "$Id$" -import os -import warnings +import os, string, sys +from types import * + +try: + import warnings +except ImportError: + warnings = None # This class is really only used by the "build_ext" command, so it might # make sense to put it in distutils.command.build_ext. However, that @@ -78,9 +83,6 @@ class Extension: language : string extension language (i.e. "c", "c++", "objc"). Will be detected from the source extensions if not provided. - optional : boolean - specifies that a build failure in the extension should not abort the - build process, but simply not install the failing extension. """ # When adding arguments to this constructor, be sure to update @@ -99,14 +101,12 @@ class Extension: swig_opts = None, depends=None, language=None, - optional=None, **kw # To catch unknown keywords ): - if not isinstance(name, str): - raise AssertionError("'name' must be a string") - if not (isinstance(sources, list) and - all(isinstance(v, str) for v in sources)): - raise AssertionError("'sources' must be a list of strings") + assert type(name) is StringType, "'name' must be a string" + assert (type(sources) is ListType and + map(type, sources) == [StringType]*len(sources)), \ + "'sources' must be a list of strings" self.name = name self.sources = sources @@ -123,28 +123,27 @@ class Extension: self.swig_opts = swig_opts or [] self.depends = depends or [] self.language = language - self.optional = optional # If there are unknown keyword options, warn about them - if len(kw) > 0: - options = [repr(option) for option in kw] - options = ', '.join(sorted(options)) - msg = "Unknown Extension options: %s" % options - warnings.warn(msg) - -def read_setup_file(filename): - """Reads a Setup file and returns Extension instances.""" - warnings.warn('distutils.extensions.read_setup_file is deprecated. ' - 'It will be removed in the next Python release.') - _sysconfig = __import__('sysconfig') - from distutils.sysconfig import (expand_makefile_vars, - _variable_rx) + if len(kw): + L = kw.keys() ; L.sort() + L = map(repr, L) + msg = "Unknown Extension options: " + string.join(L, ', ') + if warnings is not None: + warnings.warn(msg) + else: + sys.stderr.write(msg + '\n') +# class Extension + +def read_setup_file (filename): + from distutils.sysconfig import \ + parse_makefile, expand_makefile_vars, _variable_rx from distutils.text_file import TextFile from distutils.util import split_quoted # First pass over the file to gather "VAR = VALUE" assignments. - vars = _sysconfig._parse_makefile(filename) + vars = parse_makefile(filename) # Second pass to gobble up the real content: lines of the form # ... [ ...] [ ...] [ ...] @@ -164,11 +163,10 @@ def read_setup_file(filename): file.warn("'%s' lines not handled yet" % line) continue - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - line = expand_makefile_vars(line, vars) - + #print "original line: " + line + line = expand_makefile_vars(line, vars) words = split_quoted(line) + #print "expanded line: " + line # NB. this parses a slightly different syntax than the old # makesetup script: here, there must be exactly one extension per @@ -197,7 +195,7 @@ def read_setup_file(filename): elif switch == "-I": ext.include_dirs.append(value) elif switch == "-D": - equals = value.find("=") + equals = string.find(value, "=") if equals == -1: # bare "-DFOO" -- no value ext.define_macros.append((value, None)) else: # "-DFOO=blah" @@ -234,4 +232,15 @@ def read_setup_file(filename): extensions.append(ext) + #print "module:", module + #print "source files:", source_files + #print "cpp args:", cpp_args + #print "lib args:", library_args + + #extensions[module] = { 'sources': source_files, + # 'cpp_args': cpp_args, + # 'lib_args': library_args } + return extensions + +# read_setup_file () diff --git a/sysconfig.py b/sysconfig.py index a247bc2c..54ccec49 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -7,51 +7,59 @@ available. Written by: Fred L. Drake, Jr. Email: - -**This module has been moved out of Distutils and will be removed from -Python in the next version (3.3)** """ __revision__ = "$Id$" +import os import re -from warnings import warn - -# importing sysconfig from Lib -# to avoid this module to shadow it -_sysconfig = __import__('sysconfig') - -# names defined here to keep backward compatibility -# for APIs that were relocated -get_python_version = _sysconfig.get_python_version -get_config_h_filename = _sysconfig.get_config_h_filename -parse_config_h = _sysconfig.parse_config_h -get_config_vars = _sysconfig.get_config_vars -get_config_var = _sysconfig.get_config_var -from distutils.ccompiler import customize_compiler - -_DEPRECATION_MSG = ("distutils.sysconfig.%s is deprecated. " - "Use the APIs provided by the sysconfig module instead") - -def _get_project_base(): - return _sysconfig._PROJECT_BASE - -project_base = _get_project_base() +import string +import sys + +from distutils.errors import DistutilsPlatformError + +# These are needed in a couple of spots, so just compute them once. +PREFIX = os.path.normpath(sys.prefix) +EXEC_PREFIX = os.path.normpath(sys.exec_prefix) + +# Path to the base directory of the project. On Windows the binary may +# live in project/PCBuild9. If we're dealing with an x64 Windows build, +# it'll live in project/PCbuild/amd64. +project_base = os.path.dirname(os.path.abspath(sys.executable)) +if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) +# PC/VS7.1 +if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, + os.path.pardir)) +# PC/AMD64 +if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, + os.path.pardir)) + +# python_build: (Boolean) if true, we're either building Python or +# building an extension with an un-installed Python, so we use +# different (hard-wired) directories. +# Setup.local is available for Makefile builds including VPATH builds, +# Setup.dist is available on Windows +def _python_build(): + for fn in ("Setup.dist", "Setup.local"): + if os.path.isfile(os.path.join(project_base, "Modules", fn)): + return True + return False +python_build = _python_build() -class _DeprecatedBool(int): - def __nonzero__(self): - warn(_DEPRECATION_MSG % 'get_python_version', DeprecationWarning) - return super(_DeprecatedBool, self).__nonzero__() -def _python_build(): - return _DeprecatedBool(_sysconfig.is_python_build()) +def get_python_version(): + """Return a string containing the major and minor Python version, + leaving off the patchlevel. Sample return values could be '1.5' + or '2.2'. + """ + return sys.version[:3] -python_build = _python_build() def get_python_inc(plat_specific=0, prefix=None): - """This function is deprecated. - - Return the directory containing installed Python header files. + """Return the directory containing installed Python header files. If 'plat_specific' is false (the default), this is the path to the non-platform-specific header files, i.e. Python.h and so on; @@ -61,22 +69,36 @@ def get_python_inc(plat_specific=0, prefix=None): If 'prefix' is supplied, use it instead of sys.prefix or sys.exec_prefix -- i.e., ignore 'plat_specific'. """ - warn(_DEPRECATION_MSG % 'get_python_inc', DeprecationWarning) - get_path = _sysconfig.get_path - - if prefix is not None: - vars = {'base': prefix} - return get_path('include', vars=vars) - - if not plat_specific: - return get_path('include') + if prefix is None: + prefix = plat_specific and EXEC_PREFIX or PREFIX + if os.name == "posix": + if python_build: + base = os.path.dirname(os.path.abspath(sys.executable)) + if plat_specific: + inc_dir = base + else: + inc_dir = os.path.join(base, "Include") + if not os.path.exists(inc_dir): + inc_dir = os.path.join(os.path.dirname(base), "Include") + return inc_dir + return os.path.join(prefix, "include", "python" + get_python_version()) + elif os.name == "nt": + return os.path.join(prefix, "include") + elif os.name == "mac": + if plat_specific: + return os.path.join(prefix, "Mac", "Include") + else: + return os.path.join(prefix, "Include") + elif os.name == "os2": + return os.path.join(prefix, "Include") else: - return get_path('platinclude') + raise DistutilsPlatformError( + "I don't know where Python installs its C header files " + "on platform '%s'" % os.name) -def get_python_lib(plat_specific=False, standard_lib=False, prefix=None): - """This function is deprecated. - Return the directory containing the Python library (standard or +def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): + """Return the directory containing the Python library (standard or site additions). If 'plat_specific' is true, return the directory containing @@ -89,33 +111,146 @@ def get_python_lib(plat_specific=False, standard_lib=False, prefix=None): If 'prefix' is supplied, use it instead of sys.prefix or sys.exec_prefix -- i.e., ignore 'plat_specific'. """ - warn(_DEPRECATION_MSG % 'get_python_lib', DeprecationWarning) - vars = {} - get_path = _sysconfig.get_path - if prefix is not None: - if plat_specific: - vars['platbase'] = prefix + if prefix is None: + prefix = plat_specific and EXEC_PREFIX or PREFIX + + if os.name == "posix": + libpython = os.path.join(prefix, + "lib", "python" + get_python_version()) + if standard_lib: + return libpython else: - vars['base'] = prefix + return os.path.join(libpython, "site-packages") - if standard_lib: + elif os.name == "nt": + if standard_lib: + return os.path.join(prefix, "Lib") + else: + if get_python_version() < "2.2": + return prefix + else: + return os.path.join(prefix, "Lib", "site-packages") + + elif os.name == "mac": if plat_specific: - return get_path('platstdlib', vars=vars) + if standard_lib: + return os.path.join(prefix, "Lib", "lib-dynload") + else: + return os.path.join(prefix, "Lib", "site-packages") else: - return get_path('stdlib', vars=vars) + if standard_lib: + return os.path.join(prefix, "Lib") + else: + return os.path.join(prefix, "Lib", "site-packages") + + elif os.name == "os2": + if standard_lib: + return os.path.join(prefix, "Lib") + else: + return os.path.join(prefix, "Lib", "site-packages") + else: - if plat_specific: - return get_path('platlib', vars=vars) + raise DistutilsPlatformError( + "I don't know where Python installs its library " + "on platform '%s'" % os.name) + + +def customize_compiler(compiler): + """Do any platform-specific customization of a CCompiler instance. + + Mainly needed on Unix, so we can plug in the information that + varies across Unices and is stored in Python's Makefile. + """ + if compiler.compiler_type == "unix": + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext) = \ + get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', + 'CCSHARED', 'LDSHARED', 'SO') + + if 'CC' in os.environ: + cc = os.environ['CC'] + if 'CXX' in os.environ: + cxx = os.environ['CXX'] + if 'LDSHARED' in os.environ: + ldshared = os.environ['LDSHARED'] + if 'CPP' in os.environ: + cpp = os.environ['CPP'] + else: + cpp = cc + " -E" # not always + if 'LDFLAGS' in os.environ: + ldshared = ldshared + ' ' + os.environ['LDFLAGS'] + if 'CFLAGS' in os.environ: + cflags = opt + ' ' + os.environ['CFLAGS'] + ldshared = ldshared + ' ' + os.environ['CFLAGS'] + if 'CPPFLAGS' in os.environ: + cpp = cpp + ' ' + os.environ['CPPFLAGS'] + cflags = cflags + ' ' + os.environ['CPPFLAGS'] + ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + + cc_cmd = cc + ' ' + cflags + compiler.set_executables( + preprocessor=cpp, + compiler=cc_cmd, + compiler_so=cc_cmd + ' ' + ccshared, + compiler_cxx=cxx, + linker_so=ldshared, + linker_exe=cc) + + compiler.shared_lib_extension = so_ext + + +def get_config_h_filename(): + """Return full pathname of installed pyconfig.h file.""" + if python_build: + if os.name == "nt": + inc_dir = os.path.join(project_base, "PC") else: - return get_path('purelib', vars=vars) + inc_dir = project_base + else: + inc_dir = get_python_inc(plat_specific=1) + if get_python_version() < '2.2': + config_h = 'config.h' + else: + # The name of the config.h file changed in 2.2 + config_h = 'pyconfig.h' + return os.path.join(inc_dir, config_h) + def get_makefile_filename(): - """This function is deprecated. + """Return full pathname of installed Makefile from the Python build.""" + if python_build: + return os.path.join(os.path.dirname(sys.executable), "Makefile") + lib_dir = get_python_lib(plat_specific=1, standard_lib=1) + return os.path.join(lib_dir, "config", "Makefile") + - Return full pathname of installed Makefile from the Python build. +def parse_config_h(fp, g=None): + """Parse a config.h-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. """ - warn(_DEPRECATION_MSG % 'get_makefile_filename', DeprecationWarning) - return _sysconfig._get_makefile_filename() + if g is None: + g = {} + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + # + while 1: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + try: v = int(v) + except ValueError: pass + g[n] = v + else: + m = undef_rx.match(line) + if m: + g[m.group(1)] = 0 + return g + # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). @@ -124,29 +259,91 @@ _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") def parse_makefile(fn, g=None): - """This function is deprecated. - - Parse a Makefile-style file. + """Parse a Makefile-style file. A dictionary containing name/value pairs is returned. If an optional dictionary is passed in as the second argument, it is used instead of a new dictionary. """ - warn(_DEPRECATION_MSG % 'parse_makefile', DeprecationWarning) - return _sysconfig._parse_makefile(fn, g) + from distutils.text_file import TextFile + fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1) -def expand_makefile_vars(s, vars): - """This function is deprecated. + if g is None: + g = {} + done = {} + notdone = {} - Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in + while 1: + line = fp.readline() + if line is None: # eof + break + m = _variable_rx.match(line) + if m: + n, v = m.group(1, 2) + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: + notdone[n] = v + else: + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v + + # do variable interpolation here + while notdone: + for name in notdone.keys(): + value = notdone[name] + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) + if m: + n = m.group(1) + found = True + if n in done: + item = str(done[n]) + elif n in notdone: + # get it on a subsequent round + found = False + elif n in os.environ: + # do it like make: fall back to environment + item = os.environ[n] + else: + done[n] = item = "" + if found: + after = value[m.end():] + value = value[:m.start()] + item + after + if "$" in after: + notdone[name] = value + else: + try: value = int(value) + except ValueError: + done[name] = value.strip() + else: + done[name] = value + del notdone[name] + else: + # bogus variable reference; just drop it since we can't deal + del notdone[name] + + fp.close() + + # save the results in the global dictionary + g.update(done) + return g + + +def expand_makefile_vars(s, vars): + """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in 'string' according to 'vars' (a dictionary mapping variable names to values). Variables not present in 'vars' are silently expanded to the empty string. The variable values in 'vars' should not contain further variable expansions; if 'vars' is the output of 'parse_makefile()', you're fine. Returns a variable-expanded version of 's'. """ - warn('this function will be removed in then next version of Python', - DeprecationWarning) # This algorithm does multiple expansion, so if vars['foo'] contains # "${bar}", it will expand ${foo} to ${bar}, and then expand @@ -162,3 +359,247 @@ def expand_makefile_vars(s, vars): else: break return s + + +_config_vars = None + +def _init_posix(): + """Initialize the module as appropriate for POSIX systems.""" + g = {} + # load the installed Makefile: + try: + filename = get_makefile_filename() + parse_makefile(filename, g) + except IOError, msg: + my_msg = "invalid Python installation: unable to open %s" % filename + if hasattr(msg, "strerror"): + my_msg = my_msg + " (%s)" % msg.strerror + + raise DistutilsPlatformError(my_msg) + + # load the installed pyconfig.h: + try: + filename = get_config_h_filename() + parse_config_h(file(filename), g) + except IOError, msg: + my_msg = "invalid Python installation: unable to open %s" % filename + if hasattr(msg, "strerror"): + my_msg = my_msg + " (%s)" % msg.strerror + + raise DistutilsPlatformError(my_msg) + + # On MacOSX we need to check the setting of the environment variable + # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so + # it needs to be compatible. + # If it isn't set we set it to the configure-time value + if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g: + cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] + cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') + if cur_target == '': + cur_target = cfg_target + os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) + elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')): + my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' + % (cur_target, cfg_target)) + raise DistutilsPlatformError(my_msg) + + # On AIX, there are wrong paths to the linker scripts in the Makefile + # -- these paths are relative to the Python source, but when installed + # the scripts are in another directory. + if python_build: + g['LDSHARED'] = g['BLDSHARED'] + + elif get_python_version() < '2.1': + # The following two branches are for 1.5.2 compatibility. + if sys.platform == 'aix4': # what about AIX 3.x ? + # Linker script is in the config directory, not in Modules as the + # Makefile says. + python_lib = get_python_lib(standard_lib=1) + ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') + python_exp = os.path.join(python_lib, 'config', 'python.exp') + + g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) + + elif sys.platform == 'beos': + # Linker script is in the config directory. In the Makefile it is + # relative to the srcdir, which after installation no longer makes + # sense. + python_lib = get_python_lib(standard_lib=1) + linkerscript_path = string.split(g['LDSHARED'])[0] + linkerscript_name = os.path.basename(linkerscript_path) + linkerscript = os.path.join(python_lib, 'config', + linkerscript_name) + + # XXX this isn't the right place to do this: adding the Python + # library to the link, if needed, should be in the "build_ext" + # command. (It's also needed for non-MS compilers on Windows, and + # it's taken care of for them by the 'build_ext.get_libraries()' + # method.) + g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % + (linkerscript, PREFIX, get_python_version())) + + global _config_vars + _config_vars = g + + +def _init_nt(): + """Initialize the module as appropriate for NT""" + g = {} + # set basic install directories + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) + + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = get_python_inc(plat_specific=0) + + g['SO'] = '.pyd' + g['EXE'] = ".exe" + g['VERSION'] = get_python_version().replace(".", "") + g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) + + global _config_vars + _config_vars = g + + +def _init_mac(): + """Initialize the module as appropriate for Macintosh systems""" + g = {} + # set basic install directories + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) + + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = get_python_inc(plat_specific=0) + + import MacOS + if not hasattr(MacOS, 'runtimemodel'): + g['SO'] = '.ppc.slb' + else: + g['SO'] = '.%s.slb' % MacOS.runtimemodel + + # XXX are these used anywhere? + g['install_lib'] = os.path.join(EXEC_PREFIX, "Lib") + g['install_platlib'] = os.path.join(EXEC_PREFIX, "Mac", "Lib") + + # These are used by the extension module build + g['srcdir'] = ':' + global _config_vars + _config_vars = g + + +def _init_os2(): + """Initialize the module as appropriate for OS/2""" + g = {} + # set basic install directories + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) + + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = get_python_inc(plat_specific=0) + + g['SO'] = '.pyd' + g['EXE'] = ".exe" + + global _config_vars + _config_vars = g + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. Generally this includes + everything needed to build extensions and install both pure modules and + extensions. On Unix, this means every variable defined in Python's + installed Makefile; on Windows and Mac OS it's a much smaller set. + + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _config_vars + if _config_vars is None: + func = globals().get("_init_" + os.name) + if func: + func() + else: + _config_vars = {} + + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # Distutils. + _config_vars['prefix'] = PREFIX + _config_vars['exec_prefix'] = EXEC_PREFIX + + if sys.platform == 'darwin': + kernel_version = os.uname()[2] # Kernel version (8.4.3) + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # On Mac OS X before 10.4, check if -arch and -isysroot + # are in CFLAGS or LDFLAGS and remove them if they are. + # This is needed when building extensions on a 10.3 system + # using a universal build of python. + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + flags = _config_vars[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = re.sub('-isysroot [^ \t]*', ' ', flags) + _config_vars[key] = flags + + else: + + # Allow the user to override the architecture flags using + # an environment variable. + # NOTE: This name was introduced by Apple in OSX 10.5 and + # is used by several scripting languages distributed with + # that OS release. + + if 'ARCHFLAGS' in os.environ: + arch = os.environ['ARCHFLAGS'] + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _config_vars[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = flags + ' ' + arch + _config_vars[key] = flags + + # If we're on OSX 10.5 or later and the user tries to + # compiles an extension using an SDK that is not present + # on the current machine it is better to not use an SDK + # than to fail. + # + # The major usecase for this is users using a Python.org + # binary installer on OSX 10.6: that installer uses + # the 10.4u SDK, but that SDK is not installed by default + # when you install Xcode. + # + m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS']) + if m is not None: + sdk = m.group(1) + if not os.path.exists(sdk): + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _config_vars[key] + flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) + _config_vars[key] = flags + + if args: + vals = [] + for name in args: + vals.append(_config_vars.get(name)) + return vals + else: + return _config_vars + +def get_config_var(name): + """Return the value of a single variable using the dictionary + returned by 'get_config_vars()'. Equivalent to + get_config_vars().get(name) + """ + return get_config_vars().get(name) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py deleted file mode 100644 index e97ac666..00000000 --- a/tests/test_cygwinccompiler.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Tests for distutils.cygwinccompiler.""" -import unittest -import sys -import os -import warnings -import sysconfig - -from test.test_support import check_warnings, run_unittest -from test.test_support import captured_stdout - -from distutils import cygwinccompiler -from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, - CONFIG_H_OK, CONFIG_H_NOTOK, - CONFIG_H_UNCERTAIN, get_versions, - get_msvcr, RE_VERSION) -from distutils.util import get_compiler_versions -from distutils.tests import support - -class CygwinCCompilerTestCase(support.TempdirManager, - unittest.TestCase): - - def setUp(self): - super(CygwinCCompilerTestCase, self).setUp() - self.version = sys.version - self.python_h = os.path.join(self.mkdtemp(), 'python.h') - self.old_get_config_h_filename = sysconfig.get_config_h_filename - sysconfig.get_config_h_filename = self._get_config_h_filename - - def tearDown(self): - sys.version = self.version - sysconfig.get_config_h_filename = self.old_get_config_h_filename - super(CygwinCCompilerTestCase, self).tearDown() - - def _get_config_h_filename(self): - return self.python_h - - def test_check_config_h(self): - - # check_config_h looks for "GCC" in sys.version first - # returns CONFIG_H_OK if found - sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' - '4.0.1 (Apple Computer, Inc. build 5370)]') - - self.assertEquals(check_config_h()[0], CONFIG_H_OK) - - # then it tries to see if it can find "__GNUC__" in pyconfig.h - sys.version = 'something without the *CC word' - - # if the file doesn't exist it returns CONFIG_H_UNCERTAIN - self.assertEquals(check_config_h()[0], CONFIG_H_UNCERTAIN) - - # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK - self.write_file(self.python_h, 'xxx') - self.assertEquals(check_config_h()[0], CONFIG_H_NOTOK) - - # and CONFIG_H_OK if __GNUC__ is found - self.write_file(self.python_h, 'xxx __GNUC__ xxx') - self.assertEquals(check_config_h()[0], CONFIG_H_OK) - - def test_get_msvcr(self): - - # none - sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' - '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEquals(get_msvcr(), None) - - # MSVC 7.0 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1300 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr70']) - - # MSVC 7.1 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1310 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr71']) - - # VS2005 / MSVC 8.0 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1400 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr80']) - - # VS2008 / MSVC 9.0 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1500 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr90']) - - # unknown - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1999 32 bits (Intel)]') - self.assertRaises(ValueError, get_msvcr) - - - def test_get_version_deprecated(self): - with check_warnings() as w: - warnings.simplefilter("always") - # make sure get_compiler_versions and get_versions - # returns the same thing - self.assertEquals(get_compiler_versions(), get_versions()) - # make sure using get_version() generated a warning - self.assertEquals(len(w.warnings), 1) - # make sure any usage of RE_VERSION will also - # generate a warning, but till works - version = RE_VERSION.search('1.2').group(1) - self.assertEquals(version, '1.2') - self.assertEquals(len(w.warnings), 2) - -def test_suite(): - return unittest.makeSuite(CygwinCCompilerTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_emxccompiler.py b/tests/test_emxccompiler.py deleted file mode 100644 index d5e07dce..00000000 --- a/tests/test_emxccompiler.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Tests for distutils.emxccompiler.""" -import unittest -import sys -import os -import warnings - -from test.test_support import check_warnings, run_unittest -from test.test_support import captured_stdout - -from distutils.emxccompiler import get_versions -from distutils.util import get_compiler_versions -from distutils.tests import support - -class EmxCCompilerTestCase(support.TempdirManager, - unittest.TestCase): - - def test_get_version_deprecated(self): - with check_warnings() as w: - warnings.simplefilter("always") - # make sure get_compiler_versions and get_versions - # returns the same gcc - gcc, ld, dllwrap = get_compiler_versions() - emx_gcc, emx_ld = get_versions() - self.assertEquals(gcc, emx_gcc) - - # make sure using get_version() generated a warning - self.assertEquals(len(w.warnings), 1) - -def test_suite(): - return unittest.makeSuite(EmxCCompilerTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_extension.py b/tests/test_extension.py deleted file mode 100755 index 0b5dfb82..00000000 --- a/tests/test_extension.py +++ /dev/null @@ -1,78 +0,0 @@ -"""Tests for distutils.extension.""" -import os -import sys -import unittest -import warnings - -from test.test_support import check_warnings -from distutils.extension import read_setup_file, Extension -from distutils.tests.support import capture_warnings - -class ExtensionTestCase(unittest.TestCase): - - @capture_warnings - def test_read_setup_file(self): - # trying to read a Setup file - # (sample extracted from the PyGame project) - setup = os.path.join(os.path.dirname(__file__), 'Setup.sample') - - exts = read_setup_file(setup) - names = [ext.name for ext in exts] - names.sort() - - # here are the extensions read_setup_file should have created - # out of the file - wanted = ['_arraysurfarray', '_camera', '_numericsndarray', - '_numericsurfarray', 'base', 'bufferproxy', 'cdrom', - 'color', 'constants', 'display', 'draw', 'event', - 'fastevent', 'font', 'gfxdraw', 'image', 'imageext', - 'joystick', 'key', 'mask', 'mixer', 'mixer_music', - 'mouse', 'movie', 'overlay', 'pixelarray', 'pypm', - 'rect', 'rwobject', 'scrap', 'surface', 'surflock', - 'time', 'transform'] - - self.assertEquals(names, wanted) - - @unittest.skipIf(sys.flags.optimize >= 2, - "Assertions are omitted with -O2 and above") - def test_extension_init_assertions(self): - # The first argument, which is the name, must be a string. - self.assertRaises(AssertionError, Extension, 1, []) - - # the second argument, which is the list of files, must - # be a list of strings - self.assertRaises(AssertionError, Extension, 'name', 'file') - self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) - - def test_extension_init(self): - ext = Extension('name', []) - self.assertEquals(ext.name, 'name') - - - ext = Extension('name', ['file1', 'file2']) - self.assertEquals(ext.sources, ['file1', 'file2']) - - # others arguments have defaults - for attr in ('include_dirs', 'define_macros', 'undef_macros', - 'library_dirs', 'libraries', 'runtime_library_dirs', - 'extra_objects', 'extra_compile_args', 'extra_link_args', - 'export_symbols', 'swig_opts', 'depends'): - self.assertEquals(getattr(ext, attr), []) - - self.assertEquals(ext.language, None) - self.assertEquals(ext.optional, None) - - # if there are unknown keyword options, warn about them - with check_warnings() as w: - warnings.simplefilter('always') - ext = Extension('name', ['file1', 'file2'], chic=True) - - self.assertEquals(len(w.warnings), 1) - self.assertEquals(str(w.warnings[0].message), - "Unknown Extension options: 'chic'") - -def test_suite(): - return unittest.makeSuite(ExtensionTestCase) - -if __name__ == "__main__": - unittest.main(defaultTest="test_suite") diff --git a/tests/test_install.py b/tests/test_install.py index 8e3e942f..c834b91b 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -1,27 +1,15 @@ """Tests for distutils.command.install.""" import os -import os.path -import sys import unittest -import site -import sysconfig -from sysconfig import (get_scheme_names, _CONFIG_VARS, _INSTALL_SCHEMES, - get_config_var, get_path) - -from test.test_support import captured_stdout from distutils.command.install import install -from distutils.command import install as install_module from distutils.core import Distribution -from distutils.errors import DistutilsOptionError from distutils.tests import support -class InstallTestCase(support.TempdirManager, - support.EnvironGuard, - support.LoggingSilencer, - unittest.TestCase): + +class InstallTestCase(support.TempdirManager, unittest.TestCase): def test_home_installation_scheme(self): # This ensure two things: @@ -38,23 +26,9 @@ class InstallTestCase(support.TempdirManager, build_lib=os.path.join(builddir, "lib"), ) - - - posix_prefix = _INSTALL_SCHEMES['posix_prefix'] - old_posix_prefix = posix_prefix['platinclude'] - posix_prefix['platinclude'] = \ - '{platbase}/include/python{py_version_short}' - - posix_home = _INSTALL_SCHEMES['posix_home'] - old_posix_home = posix_home['platinclude'] - posix_home['platinclude'] = '{base}/include/python' - try: - cmd = install(dist) - cmd.home = destination - cmd.ensure_finalized() - finally: - posix_home['platinclude'] = old_posix_home - posix_prefix['platinclude'] = old_posix_prefix + cmd = install(dist) + cmd.home = destination + cmd.ensure_finalized() self.assertEqual(cmd.install_base, destination) self.assertEqual(cmd.install_platbase, destination) @@ -73,143 +47,6 @@ class InstallTestCase(support.TempdirManager, check_path(cmd.install_scripts, os.path.join(destination, "bin")) check_path(cmd.install_data, destination) - def test_user_site(self): - # site.USER_SITE was introduced in 2.6 - if sys.version < '2.6': - return - - # preparing the environement for the test - self.old_user_base = get_config_var('userbase') - self.old_user_site = get_path('purelib', '%s_user' % os.name) - self.tmpdir = self.mkdtemp() - self.user_base = os.path.join(self.tmpdir, 'B') - self.user_site = os.path.join(self.tmpdir, 'S') - _CONFIG_VARS['userbase'] = self.user_base - scheme = _INSTALL_SCHEMES['%s_user' % os.name] - scheme['purelib'] = self.user_site - - def _expanduser(path): - if path[0] == '~': - path = os.path.normpath(self.tmpdir) + path[1:] - return path - self.old_expand = os.path.expanduser - os.path.expanduser = _expanduser - - try: - # this is the actual test - self._test_user_site() - finally: - _CONFIG_VARS['userbase'] = self.old_user_base - scheme['purelib'] = self.old_user_site - os.path.expanduser = self.old_expand - - def _test_user_site(self): - schemes = get_scheme_names() - for key in ('nt_user', 'posix_user', 'os2_home'): - self.assertTrue(key in schemes) - - dist = Distribution({'name': 'xx'}) - cmd = install(dist) - # making sure the user option is there - options = [name for name, short, lable in - cmd.user_options] - self.assertTrue('user' in options) - - # setting a value - cmd.user = 1 - - # user base and site shouldn't be created yet - self.assertTrue(not os.path.exists(self.user_base)) - self.assertTrue(not os.path.exists(self.user_site)) - - # let's run finalize - cmd.ensure_finalized() - - # now they should - self.assertTrue(os.path.exists(self.user_base)) - self.assertTrue(os.path.exists(self.user_site)) - - self.assertTrue('userbase' in cmd.config_vars) - self.assertTrue('usersite' in cmd.config_vars) - - def test_handle_extra_path(self): - dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) - cmd = install(dist) - - # two elements - cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, ['path', 'dirs']) - self.assertEquals(cmd.extra_dirs, 'dirs') - self.assertEquals(cmd.path_file, 'path') - - # one element - cmd.extra_path = ['path'] - cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, ['path']) - self.assertEquals(cmd.extra_dirs, 'path') - self.assertEquals(cmd.path_file, 'path') - - # none - dist.extra_path = cmd.extra_path = None - cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, None) - self.assertEquals(cmd.extra_dirs, '') - self.assertEquals(cmd.path_file, None) - - # three elements (no way !) - cmd.extra_path = 'path,dirs,again' - self.assertRaises(DistutilsOptionError, cmd.handle_extra_path) - - def test_finalize_options(self): - dist = Distribution({'name': 'xx'}) - cmd = install(dist) - - # must supply either prefix/exec-prefix/home or - # install-base/install-platbase -- not both - cmd.prefix = 'prefix' - cmd.install_base = 'base' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - # must supply either home or prefix/exec-prefix -- not both - cmd.install_base = None - cmd.home = 'home' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - # can't combine user with with prefix/exec_prefix/home or - # install_(plat)base - cmd.prefix = None - cmd.user = 'user' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - def test_record(self): - - install_dir = self.mkdtemp() - pkgdir, dist = self.create_dist() - - dist = Distribution() - cmd = install(dist) - dist.command_obj['install'] = cmd - cmd.root = install_dir - cmd.record = os.path.join(pkgdir, 'RECORD') - cmd.ensure_finalized() - - cmd.run() - - # let's check the RECORD file was created with one - # line (the egg info file) - with open(cmd.record) as f: - self.assertEquals(len(f.readlines()), 1) - - def _test_debug_mode(self): - # this covers the code called when DEBUG is set - old_logs_len = len(self.logs) - install_module.DEBUG = True - try: - with captured_stdout() as stdout: - self.test_record() - finally: - install_module.DEBUG = False - self.assertTrue(len(self.logs) > old_logs_len) def test_suite(): return unittest.makeSuite(InstallTestCase) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 6976dd55..3f233e28 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -1,8 +1,8 @@ """Tests for distutils.unixccompiler.""" import sys import unittest -import sysconfig +from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler class UnixCCompilerTestCase(unittest.TestCase): @@ -70,7 +70,7 @@ class UnixCCompilerTestCase(unittest.TestCase): elif v == 'GNULD': return 'yes' sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') # GCC non-GNULD sys.platform = 'bar' @@ -91,7 +91,7 @@ class UnixCCompilerTestCase(unittest.TestCase): elif v == 'GNULD': return 'yes' sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') # non-GCC GNULD @@ -119,7 +119,7 @@ class UnixCCompilerTestCase(unittest.TestCase): def gcv(v): return 'xxx' sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-blibpath:/foo') + self.assertEqual(self.cc.rpath_foo(), '-R/foo') def test_suite(): diff --git a/tests/test_util.py b/tests/test_util.py index 348b42be..981ad000 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,267 +1,11 @@ """Tests for distutils.util.""" -import os import sys import unittest -from copy import copy -from StringIO import StringIO -import subprocess -from sysconfig import get_config_vars, get_platform from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError -from distutils.util import (convert_path, change_root, - check_environ, split_quoted, strtobool, - rfc822_escape, get_compiler_versions, - _find_exe_version, _MAC_OS_X_LD_VERSION, - byte_compile) -from distutils import util -from distutils.tests import support -from distutils.version import LooseVersion +from distutils.util import byte_compile -class FakePopen(object): - test_class = None - def __init__(self, cmd, shell, stdout, stderr): - self.cmd = cmd.split()[0] - exes = self.test_class._exes - if self.cmd not in exes: - # we don't want to call the system, returning an empty - # output so it doesn't match - self.stdout = StringIO() - self.stderr = StringIO() - else: - self.stdout = StringIO(exes[self.cmd]) - self.stderr = StringIO() - -class UtilTestCase(support.EnvironGuard, unittest.TestCase): - - def setUp(self): - super(UtilTestCase, self).setUp() - # saving the environment - self.name = os.name - self.platform = sys.platform - self.version = sys.version - self.sep = os.sep - self.join = os.path.join - self.isabs = os.path.isabs - self.splitdrive = os.path.splitdrive - #self._config_vars = copy(sysconfig._config_vars) - - # patching os.uname - if hasattr(os, 'uname'): - self.uname = os.uname - self._uname = os.uname() - else: - self.uname = None - self._uname = None - os.uname = self._get_uname - - # patching POpen - self.old_find_executable = util.find_executable - util.find_executable = self._find_executable - self._exes = {} - self.old_popen = subprocess.Popen - self.old_stdout = sys.stdout - self.old_stderr = sys.stderr - FakePopen.test_class = self - subprocess.Popen = FakePopen - - def tearDown(self): - # getting back the environment - os.name = self.name - sys.platform = self.platform - sys.version = self.version - os.sep = self.sep - os.path.join = self.join - os.path.isabs = self.isabs - os.path.splitdrive = self.splitdrive - if self.uname is not None: - os.uname = self.uname - else: - del os.uname - #sysconfig._config_vars = copy(self._config_vars) - util.find_executable = self.old_find_executable - subprocess.Popen = self.old_popen - sys.old_stdout = self.old_stdout - sys.old_stderr = self.old_stderr - super(UtilTestCase, self).tearDown() - - def _set_uname(self, uname): - self._uname = uname - - def _get_uname(self): - return self._uname - - def test_get_platform(self): - platform = util.get_platform() - self.assertEquals(platform, get_platform()) - util.set_platform('MyOwnPlatform') - self.assertEquals('MyOwnPlatform', util.get_platform()) - util.set_platform(platform) - - def test_convert_path(self): - # linux/mac - os.sep = '/' - def _join(path): - return '/'.join(path) - os.path.join = _join - - self.assertEquals(convert_path('/home/to/my/stuff'), - '/home/to/my/stuff') - - # win - os.sep = '\\' - def _join(*path): - return '\\'.join(path) - os.path.join = _join - - self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') - self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') - - self.assertEquals(convert_path('home/to/my/stuff'), - 'home\\to\\my\\stuff') - self.assertEquals(convert_path('.'), - os.curdir) - - def test_change_root(self): - # linux/mac - os.name = 'posix' - def _isabs(path): - return path[0] == '/' - os.path.isabs = _isabs - def _join(*path): - return '/'.join(path) - os.path.join = _join - - self.assertEquals(change_root('/root', '/old/its/here'), - '/root/old/its/here') - self.assertEquals(change_root('/root', 'its/here'), - '/root/its/here') - - # windows - os.name = 'nt' - def _isabs(path): - return path.startswith('c:\\') - os.path.isabs = _isabs - def _splitdrive(path): - if path.startswith('c:'): - return ('', path.replace('c:', '')) - return ('', path) - os.path.splitdrive = _splitdrive - def _join(*path): - return '\\'.join(path) - os.path.join = _join - - self.assertEquals(change_root('c:\\root', 'c:\\old\\its\\here'), - 'c:\\root\\old\\its\\here') - self.assertEquals(change_root('c:\\root', 'its\\here'), - 'c:\\root\\its\\here') - - # BugsBunny os (it's a great os) - os.name = 'BugsBunny' - self.assertRaises(DistutilsPlatformError, - change_root, 'c:\\root', 'its\\here') - - # XXX platforms to be covered: os2, mac - - def test_check_environ(self): - util._environ_checked = 0 - if 'HOME' in os.environ: - del os.environ['HOME'] - - # posix without HOME - if os.name == 'posix': # this test won't run on windows - check_environ() - import pwd - self.assertEquals(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) - else: - check_environ() - - self.assertEquals(os.environ['PLAT'], get_platform()) - self.assertEquals(util._environ_checked, 1) - - def test_split_quoted(self): - self.assertEquals(split_quoted('""one"" "two" \'three\' \\four'), - ['one', 'two', 'three', 'four']) - - def test_strtobool(self): - yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') - no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') - - for y in yes: - self.assertTrue(strtobool(y)) - - for n in no: - self.assertTrue(not strtobool(n)) - - def test_rfc822_escape(self): - header = 'I am a\npoor\nlonesome\nheader\n' - res = rfc822_escape(header) - wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' - 'header%(8s)s') % {'8s': '\n'+8*' '} - self.assertEquals(res, wanted) - - def test_find_exe_version(self): - # the ld version scheme under MAC OS is: - # ^@(#)PROGRAM:ld PROJECT:ld64-VERSION - # - # where VERSION is a 2-digit number for major - # revisions. For instance under Leopard, it's - # currently 77 - # - # Dots are used when branching is done. - # - # The SnowLeopard ld64 is currently 95.2.12 - - for output, version in (('@(#)PROGRAM:ld PROJECT:ld64-77', '77'), - ('@(#)PROGRAM:ld PROJECT:ld64-95.2.12', - '95.2.12')): - result = _MAC_OS_X_LD_VERSION.search(output) - self.assertEquals(result.group(1), version) - - def _find_executable(self, name): - if name in self._exes: - return name - return None - - def test_get_compiler_versions(self): - # get_versions calls distutils.spawn.find_executable on - # 'gcc', 'ld' and 'dllwrap' - self.assertEquals(get_compiler_versions(), (None, None, None)) - - # Let's fake we have 'gcc' and it returns '3.4.5' - self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF' - res = get_compiler_versions() - self.assertEquals(str(res[0]), '3.4.5') - - # and let's see what happens when the version - # doesn't match the regular expression - # (\d+\.\d+(\.\d+)*) - self._exes['gcc'] = 'very strange output' - res = get_compiler_versions() - self.assertEquals(res[0], None) - - # same thing for ld - if sys.platform != 'darwin': - self._exes['ld'] = 'GNU ld version 2.17.50 20060824' - res = get_compiler_versions() - self.assertEquals(str(res[1]), '2.17.50') - self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' - res = get_compiler_versions() - self.assertEquals(res[1], None) - else: - self._exes['ld'] = 'GNU ld version 2.17.50 20060824' - res = get_compiler_versions() - self.assertEquals(res[1], None) - self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' - res = get_compiler_versions() - self.assertEquals(str(res[1]), '77') - - # and dllwrap - self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF' - res = get_compiler_versions() - self.assertEquals(str(res[2]), '2.17.50') - self._exes['dllwrap'] = 'Cheese Wrap' - res = get_compiler_versions() - self.assertEquals(res[2], None) +class UtilTestCase(unittest.TestCase): def test_dont_write_bytecode(self): # makes sure byte_compile raise a DistutilsError diff --git a/unixccompiler.py b/unixccompiler.py index 8fe1a6a1..783d4dca 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -18,6 +18,7 @@ __revision__ = "$Id$" import os, sys from types import StringType, NoneType +from distutils import sysconfig from distutils.dep_util import newer from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options @@ -25,7 +26,6 @@ from distutils.errors import \ DistutilsExecError, CompileError, LibError, LinkError from distutils import log - # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's # Makefile and live with it. Is this adequate? If not, we might @@ -75,7 +75,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): if 'ARCHFLAGS' in os.environ and not stripArch: # User specified different -arch flags in the environ, - # see also the sysconfig + # see also distutils.sysconfig compiler_so = compiler_so + os.environ['ARCHFLAGS'].split() if stripSysroot: @@ -276,16 +276,13 @@ class UnixCCompiler(CCompiler): # Linkers on different platforms need different options to # specify that directories need to be added to the list of # directories searched for dependencies when a dynamic library - # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to - # be told to pass the -R option through to the linker, whereas - # other compilers and gcc on other systems just know this. + # is sought. GCC has to be told to pass the -R option through + # to the linker, whereas other compilers just know this. # Other compilers may need something slightly different. At # this time, there's no way to determine this information from # the configuration data stored in the Python installation, so # we use this hack. - _sysconfig = __import__('sysconfig') - - compiler = os.path.basename(_sysconfig.get_config_var("CC")) + compiler = os.path.basename(sysconfig.get_config_var("CC")) if sys.platform[:6] == "darwin": # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir @@ -296,22 +293,8 @@ class UnixCCompiler(CCompiler): elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] elif self._is_gcc(compiler): - # gcc on non-GNU systems does not need -Wl, but can - # use it anyway. Since distutils has always passed in - # -Wl whenever gcc was used in the past it is probably - # safest to keep doing so. - if _sysconfig.get_config_var("GNULD") == "yes": - # GNU ld needs an extra option to get a RUNPATH - # instead of just an RPATH. - return "-Wl,--enable-new-dtags,-R" + dir - else: - return "-Wl,-R" + dir - elif sys.platform[:3] == "aix": - return "-blibpath:" + dir + return "-Wl,-R" + dir else: - # No idea how --enable-new-dtags would be passed on to - # ld if this system was using GNU ld. Don't know if a - # system like this even exists. return "-R" + dir def library_option(self, lib): diff --git a/util.py b/util.py index fbd3a672..36ac7213 100644 --- a/util.py +++ b/util.py @@ -7,40 +7,184 @@ one of the other *util.py modules. __revision__ = "$Id$" import sys, os, string, re - from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer -from distutils.spawn import spawn, find_executable +from distutils.spawn import spawn from distutils import log -from distutils.version import LooseVersion from distutils.errors import DistutilsByteCompileError -_sysconfig = __import__('sysconfig') -_PLATFORM = None +def get_platform (): + """Return a string that identifies the current platform. This is used + mainly to distinguish platform-specific build directories and + platform-specific built distributions. Typically includes the OS name + and version and the architecture (as supplied by 'os.uname()'), + although the exact information included depends on the OS; eg. for IRIX + the architecture isn't particularly important (IRIX only runs on SGI + hardware), but for Linux the kernel version isn't particularly + important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + irix-5.3 + irix64-6.2 + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win-ia64 (64bit Windows on Itanium) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + """ + if os.name == 'nt': + # sniff sys.version for architecture. + prefix = " bit (" + i = string.find(sys.version, prefix) + if i == -1: + return sys.platform + j = string.find(sys.version, ")", i) + look = sys.version[i+len(prefix):j].lower() + if look=='amd64': + return 'win-amd64' + if look=='itanium': + return 'win-ia64' + return sys.platform + + if os.name != "posix" or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + + (osname, host, release, version, machine) = os.uname() + + # Convert the OS name to lowercase, remove '/' characters + # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") + osname = string.lower(osname) + osname = string.replace(osname, '/', '') + machine = string.replace(machine, ' ', '_') + machine = string.replace(machine, '/', '-') + + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # fall through to standard osname-release-machine representation + elif osname[:4] == "irix": # could be "irix64"! + return "%s-%s" % (osname, release) + elif osname[:3] == "aix": + return "%s-%s.%s" % (osname, version, release) + elif osname[:6] == "cygwin": + osname = "cygwin" + rel_re = re.compile (r'[\d.]+') + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == "darwin": + # + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # to. This makes the compatibility story a bit more sane because the + # machine is going to compile and link as if it were + # MACOSX_DEPLOYMENT_TARGET. + from distutils.sysconfig import get_config_vars + cfgvars = get_config_vars() + + macver = os.environ.get('MACOSX_DEPLOYMENT_TARGET') + if not macver: + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + + if 1: + # Always calculate the release of the running machine, + # needed to determine if we can build fat binaries or not. + + macrelease = macver + # Get the system version. Reading this plist is a documented + # way to get the system version (see the documentation for + # the Gestalt Manager) + try: + f = open('/System/Library/CoreServices/SystemVersion.plist') + except IOError: + # We're on a plain darwin box, fall back to the default + # behaviour. + pass + else: + m = re.search( + r'ProductUserVisibleVersion\s*' + + r'(.*?)', f.read()) + f.close() + if m is not None: + macrelease = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + + if not macver: + macver = macrelease + + if macver: + from distutils.sysconfig import get_config_vars + release = macver + osname = "macosx" + + if (macrelease + '.') >= '10.4.' and \ + '-arch' in get_config_vars().get('CFLAGS', '').strip(): + # The universal build will build fat binaries, but not on + # systems before 10.4 + # + # Try to detect 4-way universal builds, those have machine-type + # 'universal' instead of 'fat'. + + machine = 'fat' + cflags = get_config_vars().get('CFLAGS') + + archs = re.findall('-arch\s+(\S+)', cflags) + archs.sort() + archs = tuple(archs) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r"%(archs,)) -def get_platform(): - """Return a string that identifies the current platform. + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxint >= 2**32: + machine = 'x86_64' - By default, will return the value returned by sysconfig.get_platform(), - but it can be changed by calling set_platform(). - """ - global _PLATFORM - if _PLATFORM is None: - _PLATFORM = _sysconfig.get_platform() - return _PLATFORM + elif machine in ('PowerPC', 'Power_Macintosh'): + # Pick a sane name for the PPC architecture. + machine = 'ppc' -def set_platform(identifier): - """Sets the platform string identifier returned by get_platform(). + # See 'i386' case + if sys.maxint >= 2**32: + machine = 'ppc64' - Note that this change doesn't impact the value returned by - sysconfig.get_platform() and is local to Distutils - """ - global _PLATFORM - _PLATFORM = identifier + return "%s-%s-%s" % (osname, release, machine) + +# get_platform () -def convert_path(pathname): - """Return 'pathname' as a name that will work on the native filesystem. +def convert_path (pathname): + """Return 'pathname' as a name that will work on the native filesystem, i.e. split it on '/' and put it back together again using the current directory separator. Needed because filenames in the setup script are always supplied in Unix style, and have to be converted to the local @@ -53,23 +197,23 @@ def convert_path(pathname): if not pathname: return pathname if pathname[0] == '/': - raise ValueError("path '%s' cannot be absolute" % pathname) + raise ValueError, "path '%s' cannot be absolute" % pathname if pathname[-1] == '/': - raise ValueError("path '%s' cannot end with '/'" % pathname) + raise ValueError, "path '%s' cannot end with '/'" % pathname - paths = pathname.split('/') + paths = string.split(pathname, '/') while '.' in paths: paths.remove('.') if not paths: return os.curdir - return os.path.join(*paths) + return apply(os.path.join, paths) +# convert_path () -def change_root(new_root, pathname): - """Return 'pathname' with 'new_root' prepended. - If 'pathname' is relative, this is equivalent to - "os.path.join(new_root,pathname)". +def change_root (new_root, pathname): + """Return 'pathname' with 'new_root' prepended. If 'pathname' is + relative, this is equivalent to "os.path.join(new_root,pathname)". Otherwise, it requires making 'pathname' relative and then joining the two, which is tricky on DOS/Windows and Mac OS. """ @@ -96,20 +240,19 @@ def change_root(new_root, pathname): return os.path.join(new_root, pathname) else: # Chop off volume name from start of path - elements = pathname.split(":", 1) + elements = string.split(pathname, ":", 1) pathname = ":" + elements[1] return os.path.join(new_root, pathname) else: - raise DistutilsPlatformError("nothing known about " - "platform '%s'" % os.name) + raise DistutilsPlatformError, \ + "nothing known about platform '%s'" % os.name -_environ_checked = 0 - -def check_environ(): - """Ensure that 'os.environ' has all the environment variables needed. - We guarantee that users can use in config files, command-line options, +_environ_checked = 0 +def check_environ (): + """Ensure that 'os.environ' has all the environment variables we + guarantee that users can use in config files, command-line options, etc. Currently this includes: HOME - user's home directory (Unix only) PLAT - description of the current platform, including hardware @@ -124,14 +267,14 @@ def check_environ(): os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] if 'PLAT' not in os.environ: - os.environ['PLAT'] = _sysconfig.get_platform() + os.environ['PLAT'] = get_platform() _environ_checked = 1 -def subst_vars(s, local_vars): - """Perform shell/Perl-style variable substitution on 'string'. - Every occurrence of '$' followed by a name is considered a variable, and +def subst_vars (s, local_vars): + """Perform shell/Perl-style variable substitution on 'string'. Every + occurrence of '$' followed by a name is considered a variable, and variable is substituted by the value found in the 'local_vars' dictionary, or in 'os.environ' if it's not in 'local_vars'. 'os.environ' is first checked/augmented to guarantee that it contains @@ -149,13 +292,14 @@ def subst_vars(s, local_vars): try: return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) except KeyError, var: - raise ValueError("invalid variable '$%s'" % var) + raise ValueError, "invalid variable '$%s'" % var + +# subst_vars () -def grok_environment_error(exc, prefix="error: "): - """Generate a useful error message from an EnvironmentError. - This will generate an IOError or an OSError exception object. - Handles Python 1.5.1 and 1.5.2 styles, and +def grok_environment_error (exc, prefix="error: "): + """Generate a useful error message from an EnvironmentError (IOError or + OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and does what it can to deal with exception objects that don't have a filename (which happens when the error is due to a two-file operation, such as 'rename()' or 'link()'. Returns the error message as a string @@ -174,20 +318,18 @@ def grok_environment_error(exc, prefix="error: "): return error + # Needed by 'split_quoted()' _wordchars_re = _squote_re = _dquote_re = None - def _init_regex(): global _wordchars_re, _squote_re, _dquote_re _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') -def split_quoted(s): +def split_quoted (s): """Split a string up according to Unix shell-like rules for quotes and - backslashes. - - In short: words are delimited by spaces, as long as those + backslashes. In short: words are delimited by spaces, as long as those spaces are not escaped by a backslash, or inside a quoted string. Single and double quotes are equivalent, and the quote characters can be backslash-escaped. The backslash is stripped from any two-character @@ -195,12 +337,13 @@ def split_quoted(s): characters are stripped from any quoted string. Returns a list of words. """ + # This is a nice algorithm for splitting up a single string, since it # doesn't require character-by-character examination. It was a little # bit of a brain-bender to get it working right, though... if _wordchars_re is None: _init_regex() - s = s.strip() + s = string.strip(s) words = [] pos = 0 @@ -213,7 +356,7 @@ def split_quoted(s): if s[end] in string.whitespace: # unescaped, unquoted whitespace: now words.append(s[:end]) # we definitely have a word delimiter - s = s[end:].lstrip() + s = string.lstrip(s[end:]) pos = 0 elif s[end] == '\\': # preserve whatever is being escaped; @@ -227,11 +370,12 @@ def split_quoted(s): elif s[end] == '"': # slurp doubly-quoted string m = _dquote_re.match(s, end) else: - raise RuntimeError("this can't happen " - "(bad char '%c')" % s[end]) + raise RuntimeError, \ + "this can't happen (bad char '%c')" % s[end] if m is None: - raise ValueError("bad string (mismatched %s quotes?)" % s[end]) + raise ValueError, \ + "bad string (mismatched %s quotes?)" % s[end] (beg, end) = m.span() s = s[:beg] + s[beg+1:end-1] + s[end:] @@ -243,12 +387,13 @@ def split_quoted(s): return words +# split_quoted () -def execute(func, args, msg=None, verbose=0, dry_run=0): - """Perform some action that affects the outside world. - eg. by writing to the filesystem). Such actions are special because - they are disabled by the 'dry_run' flag. This method takes care of all +def execute (func, args, msg=None, verbose=0, dry_run=0): + """Perform some action that affects the outside world (eg. by + writing to the filesystem). Such actions are special because they + are disabled by the 'dry_run' flag. This method takes care of all that bureaucracy for you; all you have to do is supply the function to call and an argument tuple for it (to embody the "external action" being performed), and an optional message to @@ -261,17 +406,17 @@ def execute(func, args, msg=None, verbose=0, dry_run=0): log.info(msg) if not dry_run: - func(*args) + apply(func, args) -def strtobool(val): +def strtobool (val): """Convert a string representation of truth to true (1) or false (0). True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 'val' is anything else. """ - val = val.lower() + val = string.lower(val) if val in ('y', 'yes', 't', 'true', 'on', '1'): return 1 elif val in ('n', 'no', 'f', 'false', 'off', '0'): @@ -280,13 +425,15 @@ def strtobool(val): raise ValueError, "invalid truth value %r" % (val,) -def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, - verbose=1, dry_run=0, direct=None): +def byte_compile (py_files, + optimize=0, force=0, + prefix=None, base_dir=None, + verbose=1, dry_run=0, + direct=None): """Byte-compile a collection of Python source files to either .pyc - or .pyo files in the same directory. - - 'py_files' is a list of files to compile; any files that don't end in - ".py" are silently skipped. 'optimize' must be one of the following: + or .pyo files in the same directory. 'py_files' is a list of files + to compile; any files that don't end in ".py" are silently skipped. + 'optimize' must be one of the following: 0 - don't optimize (generate .pyc) 1 - normal optimization (like "python -O") 2 - extra optimization (like "python -OO") @@ -363,7 +510,7 @@ files = [ #if prefix: # prefix = os.path.abspath(prefix) - script.write(",\n".join(map(repr, py_files)) + "]\n") + script.write(string.join(map(repr, py_files), ",\n") + "]\n") script.write(""" byte_compile(files, optimize=%r, force=%r, prefix=%r, base_dir=%r, @@ -402,8 +549,9 @@ byte_compile(files, optimize=%r, force=%r, dfile = file if prefix: if file[:len(prefix)] != prefix: - raise ValueError("invalid prefix: filename %r doesn't " - "start with %r" % (file, prefix)) + raise ValueError, \ + ("invalid prefix: filename %r doesn't start with %r" + % (file, prefix)) dfile = dfile[len(prefix):] if base_dir: dfile = os.path.join(base_dir, dfile) @@ -418,61 +566,12 @@ byte_compile(files, optimize=%r, force=%r, log.debug("skipping byte-compilation of %s to %s", file, cfile_base) +# byte_compile () -def rfc822_escape(header): +def rfc822_escape (header): """Return a version of the string escaped for inclusion in an RFC-822 header, by ensuring there are 8 spaces space after each newline. """ - lines = header.split('\n') - sep = '\n' + 8 * ' ' - return sep.join(lines) - -_RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') -_MAC_OS_X_LD_VERSION = re.compile('^@\(#\)PROGRAM:ld PROJECT:ld64-((\d+)(\.\d+)*)') - -def _find_ld_version(): - """Finds the ld version. The version scheme differs under Mac OSX.""" - if sys.platform == 'darwin': - return _find_exe_version('ld -v', _MAC_OS_X_LD_VERSION) - else: - return _find_exe_version('ld -v') - -def _find_exe_version(cmd, pattern=_RE_VERSION): - """Find the version of an executable by running `cmd` in the shell. - - `pattern` is a compiled regular expression. If not provided, default - to _RE_VERSION. If the command is not found, or the output does not - match the mattern, returns None. - """ - from subprocess import Popen, PIPE - executable = cmd.split()[0] - if find_executable(executable) is None: - return None - pipe = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) - try: - stdout, stderr = pipe.stdout.read(), pipe.stderr.read() - finally: - pipe.stdout.close() - pipe.stderr.close() - # some commands like ld under MacOS X, will give the - # output in the stderr, rather than stdout. - if stdout != '': - out_string = stdout - else: - out_string = stderr - - result = pattern.search(out_string) - if result is None: - return None - return LooseVersion(result.group(1)) - -def get_compiler_versions(): - """Returns a tuple providing the versions of gcc, ld and dllwrap - - For each command, if a command is not found, None is returned. - Otherwise a LooseVersion instance is returned. - """ - gcc = _find_exe_version('gcc -dumpversion') - ld = _find_ld_version() - dllwrap = _find_exe_version('dllwrap --version') - return gcc, ld, dllwrap + lines = string.split(header, '\n') + header = string.join(lines, '\n' + 8*' ') + return header -- cgit v1.2.1 From 17e4614fa2e3b907900dc2e4587a63c281db5425 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 5 Mar 2010 03:20:06 +0000 Subject: remove the svn:executable property from files that don't have shebang lines --- tests/test_clean.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tests/test_clean.py diff --git a/tests/test_clean.py b/tests/test_clean.py old mode 100755 new mode 100644 -- cgit v1.2.1 From a5dade0cc3debdff89c52fbbfc9d23c0a6d21062 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 5 Mar 2010 03:33:11 +0000 Subject: Merged revisions 78678,78680,78682 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r78678 | benjamin.peterson | 2010-03-04 21:07:59 -0600 (Thu, 04 Mar 2010) | 1 line set svn:eol-style ........ r78680 | benjamin.peterson | 2010-03-04 21:15:07 -0600 (Thu, 04 Mar 2010) | 1 line set svn:eol-style on Lib files ........ r78682 | benjamin.peterson | 2010-03-04 21:20:06 -0600 (Thu, 04 Mar 2010) | 1 line remove the svn:executable property from files that don't have shebang lines ........ --- tests/test_clean.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tests/test_clean.py diff --git a/tests/test_clean.py b/tests/test_clean.py old mode 100755 new mode 100644 -- cgit v1.2.1 From 95ca0fbb62d6de3643c66af34ebfb842fabd0bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 6 Mar 2010 01:04:14 +0000 Subject: copied back the build_ext tests from 2.6 --- tests/test_build_ext.py | 198 ++++++++++++++++++++---------------------------- 1 file changed, 83 insertions(+), 115 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 0e3f33e4..a1c236aa 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -3,17 +3,12 @@ import os import tempfile import shutil from StringIO import StringIO -import warnings -from test.test_support import check_warnings -from test.test_support import captured_stdout from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext -import sysconfig +from distutils import sysconfig from distutils.tests import support -from distutils.extension import Extension -from distutils.errors import (UnknownFileError, DistutilsSetupError, - CompileError) +from distutils.errors import DistutilsSetupError import unittest from test import test_support @@ -33,16 +28,10 @@ class BuildExtTestCase(support.TempdirManager, # Create a simple test environment # Note that we're making changes to sys.path super(BuildExtTestCase, self).setUp() - self.tmp_dir = self.mkdtemp() - self.sys_path = sys.path, sys.path[:] + self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") + self.sys_path = sys.path[:] sys.path.append(self.tmp_dir) shutil.copy(_get_source_filename(), self.tmp_dir) - if sys.version > "2.6": - import site - self.old_user_base = site.USER_BASE - site.USER_BASE = self.mkdtemp() - from distutils.command import build_ext - build_ext.USER_BASE = site.USER_BASE def test_build_ext(self): global ALREADY_TESTED @@ -76,27 +65,22 @@ class BuildExtTestCase(support.TempdirManager, import xx for attr in ('error', 'foo', 'new', 'roj'): - self.assertTrue(hasattr(xx, attr)) + self.assert_(hasattr(xx, attr)) self.assertEquals(xx.foo(2, 5), 7) self.assertEquals(xx.foo(13,15), 28) self.assertEquals(xx.new().demo(), None) doc = 'This is a template module just for instruction.' self.assertEquals(xx.__doc__, doc) - self.assertTrue(isinstance(xx.Null(), xx.Null)) - self.assertTrue(isinstance(xx.Str(), xx.Str)) + self.assert_(isinstance(xx.Null(), xx.Null)) + self.assert_(isinstance(xx.Str(), xx.Str)) def tearDown(self): # Get everything back to normal test_support.unload('xx') - sys.path = self.sys_path[0] - sys.path[:] = self.sys_path[1] - if sys.version > "2.6": - import site - site.USER_BASE = self.old_user_base - from distutils.command import build_ext - build_ext.USER_BASE = self.old_user_base - + sys.path = self.sys_path + # XXX on Windows the test leaves a directory with xx module in TEMP + shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') super(BuildExtTestCase, self).tearDown() def test_solaris_enable_shared(self): @@ -105,83 +89,35 @@ class BuildExtTestCase(support.TempdirManager, old = sys.platform sys.platform = 'sunos' # fooling finalize_options - from sysconfig import _CONFIG_VARS - old_var = _CONFIG_VARS.get('Py_ENABLE_SHARED') - _CONFIG_VARS['Py_ENABLE_SHARED'] = 1 + from distutils.sysconfig import _config_vars + old_var = _config_vars.get('Py_ENABLE_SHARED') + _config_vars['Py_ENABLE_SHARED'] = 1 try: cmd.ensure_finalized() finally: sys.platform = old if old_var is None: - del _CONFIG_VARS['Py_ENABLE_SHARED'] + del _config_vars['Py_ENABLE_SHARED'] else: - _CONFIG_VARS['Py_ENABLE_SHARED'] = old_var + _config_vars['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris - self.assertTrue(len(cmd.library_dirs) > 0) - - def test_user_site(self): - # site.USER_SITE was introduced in 2.6 - if sys.version < '2.6': - return - - import site - dist = Distribution({'name': 'xx'}) - cmd = build_ext(dist) - - # making sure the user option is there - options = [name for name, short, lable in - cmd.user_options] - self.assertTrue('user' in options) - - # setting a value - cmd.user = 1 - - # setting user based lib and include - lib = os.path.join(site.USER_BASE, 'lib') - incl = os.path.join(site.USER_BASE, 'include') - os.mkdir(lib) - os.mkdir(incl) - - # let's run finalize - cmd.ensure_finalized() - - # see if include_dirs and library_dirs - # were set - self.assertTrue(lib in cmd.library_dirs) - self.assertTrue(lib in cmd.rpath) - self.assertTrue(incl in cmd.include_dirs) - - def test_optional_extension(self): - - # this extension will fail, but let's ignore this failure - # with the optional argument. - modules = [Extension('foo', ['xxx'], optional=False)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = build_ext(dist) - cmd.ensure_finalized() - self.assertRaises((UnknownFileError, CompileError), - cmd.run) # should raise an error - - modules = [Extension('foo', ['xxx'], optional=True)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = build_ext(dist) - cmd.ensure_finalized() - cmd.run() # should pass + self.assert_(len(cmd.library_dirs) > 0) def test_finalize_options(self): # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. - modules = [Extension('foo', ['xxx'], optional=False)] + modules = [Extension('foo', ['xxx'])] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = build_ext(dist) cmd.finalize_options() - py_include = sysconfig.get_path('include') - self.assertTrue(py_include in cmd.include_dirs) + from distutils import sysconfig + py_include = sysconfig.get_python_inc() + self.assert_(py_include in cmd.include_dirs) - plat_py_include = sysconfig.get_path('platinclude') - self.assertTrue(plat_py_include in cmd.include_dirs) + plat_py_include = sysconfig.get_python_inc(plat_specific=1) + self.assert_(plat_py_include in cmd.include_dirs) # make sure cmd.libraries is turned into a list # if it's a string @@ -195,7 +131,7 @@ class BuildExtTestCase(support.TempdirManager, cmd = build_ext(dist) cmd.library_dirs = 'my_lib_dir' cmd.finalize_options() - self.assertTrue('my_lib_dir' in cmd.library_dirs) + self.assert_('my_lib_dir' in cmd.library_dirs) # make sure rpath is turned into a list # if it's a list of os.pathsep's paths @@ -260,13 +196,13 @@ class BuildExtTestCase(support.TempdirManager, 'some': 'bar'})] cmd.check_extensions_list(exts) ext = exts[0] - self.assertTrue(isinstance(ext, Extension)) + self.assert_(isinstance(ext, Extension)) # check_extensions_list adds in ext the values passed # when they are in ('include_dirs', 'library_dirs', 'libraries' # 'extra_objects', 'extra_compile_args', 'extra_link_args') self.assertEquals(ext.libraries, 'foo') - self.assertTrue(not hasattr(ext, 'some')) + self.assert_(not hasattr(ext, 'some')) # 'macros' element of build info dict must be 1- or 2-tuple exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', @@ -279,7 +215,7 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(exts[0].define_macros, [('1', '2')]) def test_get_source_files(self): - modules = [Extension('foo', ['xxx'], optional=False)] + modules = [Extension('foo', ['xxx'])] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = build_ext(dist) cmd.ensure_finalized() @@ -300,7 +236,7 @@ class BuildExtTestCase(support.TempdirManager, tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') self.write_file(c_file, 'void initfoo(void) {};\n') - ext = Extension('foo', [c_file], optional=False) + ext = Extension('foo', [c_file]) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) cmd = build_ext(dist) @@ -324,16 +260,16 @@ class BuildExtTestCase(support.TempdirManager, so_file = cmd.get_outputs()[0] finally: os.chdir(old_wd) - self.assertTrue(os.path.exists(so_file)) + self.assert_(os.path.exists(so_file)) self.assertEquals(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, other_tmp_dir) - + cmd.compiler = None cmd.inplace = 0 cmd.run() so_file = cmd.get_outputs()[0] - self.assertTrue(os.path.exists(so_file)) + self.assert_(os.path.exists(so_file)) self.assertEquals(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) @@ -363,10 +299,6 @@ class BuildExtTestCase(support.TempdirManager, def test_ext_fullpath(self): ext = sysconfig.get_config_vars()['SO'] - # building lxml.etree inplace - #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') - #etree_ext = Extension('lxml.etree', [etree_c]) - #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) dist = Distribution() cmd = build_ext(dist) cmd.inplace = 1 @@ -399,25 +331,61 @@ class BuildExtTestCase(support.TempdirManager, wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) self.assertEquals(wanted, path) - def test_compiler_deprecation_warning(self): - dist = Distribution() + def test_build_ext_inplace(self): + etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + etree_ext = Extension('lxml.etree', [etree_c]) + dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) cmd = build_ext(dist) + cmd.ensure_finalized() + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + ext = sysconfig.get_config_var("SO") + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + + def test_setuptools_compat(self): + from setuptools_build_ext import build_ext as setuptools_build_ext + from setuptools_extension import Extension - class MyCompiler(object): - def do_something(self): - pass - - with check_warnings() as w: - warnings.simplefilter("always") - cmd.compiler = MyCompiler() - self.assertEquals(len(w.warnings), 1) - cmd.compile = 'unix' - self.assertEquals(len(w.warnings), 1) - cmd.compiler = MyCompiler() - cmd.compiler.do_something() - # two more warnings genereated by the get - # and the set - self.assertEquals(len(w.warnings), 3) + etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + etree_ext = Extension('lxml.etree', [etree_c]) + dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + cmd = setuptools_build_ext(dist) + cmd.ensure_finalized() + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + ext = sysconfig.get_config_var("SO") + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + + def test_build_ext_path_with_os_sep(self): + dist = Distribution({'name': 'UpdateManager'}) + cmd = build_ext(dist) + cmd.ensure_finalized() + ext = sysconfig.get_config_var("SO") + ext_name = os.path.join('UpdateManager', 'fdsend') + ext_path = cmd.get_ext_fullpath(ext_name) + wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext) + self.assertEquals(ext_path, wanted) + + def test_build_ext_path_cross_platform(self): + if sys.platform != 'win32': + return + dist = Distribution({'name': 'UpdateManager'}) + cmd = build_ext(dist) + cmd.ensure_finalized() + ext = sysconfig.get_config_var("SO") + # this needs to work even under win32 + ext_name = 'UpdateManager/fdsend' + ext_path = cmd.get_ext_fullpath(ext_name) + wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext) + self.assertEquals(ext_path, wanted) def test_suite(): src = _get_source_filename() -- cgit v1.2.1 From 5ca1f667648aa5161dff9c0725fbd30edb336e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 6 Mar 2010 01:18:27 +0000 Subject: provide a fallback for xxmodule.c in case the buildir is not present --- tests/test_build_ext.py | 6 +- tests/xxmodule.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 tests/xxmodule.c diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index a1c236aa..5dea4dde 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,7 +19,11 @@ ALREADY_TESTED = False def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') - return os.path.join(srcdir, 'Modules', 'xxmodule.c') + xxmodule = os.path.join(srcdir, 'Modules', 'xxmodule.c') + if not os.path.exists(xxmodule): + # local fallback + xxmodule = os.path.join(os.path.dirname(__file__), 'xxmodule.c') + return xxmodule class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, diff --git a/tests/xxmodule.c b/tests/xxmodule.c new file mode 100644 index 00000000..6b498dd6 --- /dev/null +++ b/tests/xxmodule.c @@ -0,0 +1,379 @@ + +/* Use this file as a template to start implementing a module that + also declares object types. All occurrences of 'Xxo' should be changed + to something reasonable for your objects. After that, all other + occurrences of 'xx' should be changed to something reasonable for your + module. If your module is named foo your sourcefile should be named + foomodule.c. + + You will probably want to delete all references to 'x_attr' and add + your own types of attributes instead. Maybe you want to name your + local variables other than 'self'. If your object type is needed in + other files, you'll have to create a file "foobarobject.h"; see + intobject.h for an example. */ + +/* Xxo objects */ + +#include "Python.h" + +static PyObject *ErrorObject; + +typedef struct { + PyObject_HEAD + PyObject *x_attr; /* Attributes dictionary */ +} XxoObject; + +static PyTypeObject Xxo_Type; + +#define XxoObject_Check(v) (Py_TYPE(v) == &Xxo_Type) + +static XxoObject * +newXxoObject(PyObject *arg) +{ + XxoObject *self; + self = PyObject_New(XxoObject, &Xxo_Type); + if (self == NULL) + return NULL; + self->x_attr = NULL; + return self; +} + +/* Xxo methods */ + +static void +Xxo_dealloc(XxoObject *self) +{ + Py_XDECREF(self->x_attr); + PyObject_Del(self); +} + +static PyObject * +Xxo_demo(XxoObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":demo")) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef Xxo_methods[] = { + {"demo", (PyCFunction)Xxo_demo, METH_VARARGS, + PyDoc_STR("demo() -> None")}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +Xxo_getattr(XxoObject *self, char *name) +{ + if (self->x_attr != NULL) { + PyObject *v = PyDict_GetItemString(self->x_attr, name); + if (v != NULL) { + Py_INCREF(v); + return v; + } + } + return Py_FindMethod(Xxo_methods, (PyObject *)self, name); +} + +static int +Xxo_setattr(XxoObject *self, char *name, PyObject *v) +{ + if (self->x_attr == NULL) { + self->x_attr = PyDict_New(); + if (self->x_attr == NULL) + return -1; + } + if (v == NULL) { + int rv = PyDict_DelItemString(self->x_attr, name); + if (rv < 0) + PyErr_SetString(PyExc_AttributeError, + "delete non-existing Xxo attribute"); + return rv; + } + else + return PyDict_SetItemString(self->x_attr, name, v); +} + +static PyTypeObject Xxo_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyVarObject_HEAD_INIT(NULL, 0) + "xxmodule.Xxo", /*tp_name*/ + sizeof(XxoObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)Xxo_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)Xxo_getattr, /*tp_getattr*/ + (setattrfunc)Xxo_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; +/* --------------------------------------------------------------------- */ + +/* Function of two integers returning integer */ + +PyDoc_STRVAR(xx_foo_doc, +"foo(i,j)\n\ +\n\ +Return the sum of i and j."); + +static PyObject * +xx_foo(PyObject *self, PyObject *args) +{ + long i, j; + long res; + if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) + return NULL; + res = i+j; /* XXX Do something here */ + return PyInt_FromLong(res); +} + + +/* Function of no arguments returning new Xxo object */ + +static PyObject * +xx_new(PyObject *self, PyObject *args) +{ + XxoObject *rv; + + if (!PyArg_ParseTuple(args, ":new")) + return NULL; + rv = newXxoObject(args); + if (rv == NULL) + return NULL; + return (PyObject *)rv; +} + +/* Example with subtle bug from extensions manual ("Thin Ice"). */ + +static PyObject * +xx_bug(PyObject *self, PyObject *args) +{ + PyObject *list, *item; + + if (!PyArg_ParseTuple(args, "O:bug", &list)) + return NULL; + + item = PyList_GetItem(list, 0); + /* Py_INCREF(item); */ + PyList_SetItem(list, 1, PyInt_FromLong(0L)); + PyObject_Print(item, stdout, 0); + printf("\n"); + /* Py_DECREF(item); */ + + Py_INCREF(Py_None); + return Py_None; +} + +/* Test bad format character */ + +static PyObject * +xx_roj(PyObject *self, PyObject *args) +{ + PyObject *a; + long b; + if (!PyArg_ParseTuple(args, "O#:roj", &a, &b)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + + +/* ---------- */ + +static PyTypeObject Str_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyVarObject_HEAD_INIT(NULL, 0) + "xxmodule.Str", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /* see initxx */ /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +/* ---------- */ + +static PyObject * +null_richcompare(PyObject *self, PyObject *other, int op) +{ + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static PyTypeObject Null_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyVarObject_HEAD_INIT(NULL, 0) + "xxmodule.Null", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + null_richcompare, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /* see initxx */ /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /* see initxx */ /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + + +/* ---------- */ + + +/* List of functions defined in the module */ + +static PyMethodDef xx_methods[] = { + {"roj", xx_roj, METH_VARARGS, + PyDoc_STR("roj(a,b) -> None")}, + {"foo", xx_foo, METH_VARARGS, + xx_foo_doc}, + {"new", xx_new, METH_VARARGS, + PyDoc_STR("new() -> new Xx object")}, + {"bug", xx_bug, METH_VARARGS, + PyDoc_STR("bug(o) -> None")}, + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(module_doc, +"This is a template module just for instruction."); + +/* Initialization function for the module (*must* be called initxx) */ + +PyMODINIT_FUNC +initxx(void) +{ + PyObject *m; + + /* Due to cross platform compiler issues the slots must be filled + * here. It's required for portability to Windows without requiring + * C++. */ + Null_Type.tp_base = &PyBaseObject_Type; + Null_Type.tp_new = PyType_GenericNew; + Str_Type.tp_base = &PyUnicode_Type; + + /* Finalize the type object including setting type of the new type + * object; doing it here is required for portability, too. */ + if (PyType_Ready(&Xxo_Type) < 0) + return; + + /* Create the module and add the functions */ + m = Py_InitModule3("xx", xx_methods, module_doc); + if (m == NULL) + return; + + /* Add some symbolic constants to the module */ + if (ErrorObject == NULL) { + ErrorObject = PyErr_NewException("xx.error", NULL, NULL); + if (ErrorObject == NULL) + return; + } + Py_INCREF(ErrorObject); + PyModule_AddObject(m, "error", ErrorObject); + + /* Add Str */ + if (PyType_Ready(&Str_Type) < 0) + return; + PyModule_AddObject(m, "Str", (PyObject *)&Str_Type); + + /* Add Null */ + if (PyType_Ready(&Null_Type) < 0) + return; + PyModule_AddObject(m, "Null", (PyObject *)&Null_Type); +} -- cgit v1.2.1 From 205a0969d915976d9bd420ec5fa78a7e97e7a51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 6 Mar 2010 01:23:21 +0000 Subject: simplified the fallback case --- tests/test_build_ext.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 5dea4dde..867ba96e 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,11 +19,10 @@ ALREADY_TESTED = False def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') - xxmodule = os.path.join(srcdir, 'Modules', 'xxmodule.c') - if not os.path.exists(xxmodule): + if srcdir is None: # local fallback - xxmodule = os.path.join(os.path.dirname(__file__), 'xxmodule.c') - return xxmodule + return os.path.join(os.path.dirname(__file__), 'xxmodule.c') + return os.path.join(srcdir, 'Modules', 'xxmodule.c') class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, -- cgit v1.2.1 From 0d642959fe4e67e111f4306a187228ab2fcec179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 6 Mar 2010 01:27:09 +0000 Subject: files used by win32 tests --- tests/setuptools_build_ext.py | 287 ++++++++++++++++++++++++++++++++++++++++++ tests/setuptools_extension.py | 51 ++++++++ 2 files changed, 338 insertions(+) create mode 100644 tests/setuptools_build_ext.py create mode 100644 tests/setuptools_extension.py diff --git a/tests/setuptools_build_ext.py b/tests/setuptools_build_ext.py new file mode 100644 index 00000000..21fa9e8f --- /dev/null +++ b/tests/setuptools_build_ext.py @@ -0,0 +1,287 @@ +from distutils.command.build_ext import build_ext as _du_build_ext +try: + # Attempt to use Pyrex for building extensions, if available + from Pyrex.Distutils.build_ext import build_ext as _build_ext +except ImportError: + _build_ext = _du_build_ext + +import os, sys +from distutils.file_util import copy_file + +from distutils.tests.setuptools_extension import Library + +from distutils.ccompiler import new_compiler +from distutils.sysconfig import customize_compiler, get_config_var +get_config_var("LDSHARED") # make sure _config_vars is initialized +from distutils.sysconfig import _config_vars +from distutils import log +from distutils.errors import * + +have_rtld = False +use_stubs = False +libtype = 'shared' + +if sys.platform == "darwin": + use_stubs = True +elif os.name != 'nt': + try: + from dl import RTLD_NOW + have_rtld = True + use_stubs = True + except ImportError: + pass + +def if_dl(s): + if have_rtld: + return s + return '' + + + + + + +class build_ext(_build_ext): + def run(self): + """Build extensions in build directory, then copy if --inplace""" + old_inplace, self.inplace = self.inplace, 0 + _build_ext.run(self) + self.inplace = old_inplace + if old_inplace: + self.copy_extensions_to_source() + + def copy_extensions_to_source(self): + build_py = self.get_finalized_command('build_py') + for ext in self.extensions: + fullname = self.get_ext_fullname(ext.name) + filename = self.get_ext_filename(fullname) + modpath = fullname.split('.') + package = '.'.join(modpath[:-1]) + package_dir = build_py.get_package_dir(package) + dest_filename = os.path.join(package_dir,os.path.basename(filename)) + src_filename = os.path.join(self.build_lib,filename) + + # Always copy, even if source is older than destination, to ensure + # that the right extensions for the current Python/platform are + # used. + copy_file( + src_filename, dest_filename, verbose=self.verbose, + dry_run=self.dry_run + ) + if ext._needs_stub: + self.write_stub(package_dir or os.curdir, ext, True) + + + if _build_ext is not _du_build_ext and not hasattr(_build_ext,'pyrex_sources'): + # Workaround for problems using some Pyrex versions w/SWIG and/or 2.4 + def swig_sources(self, sources, *otherargs): + # first do any Pyrex processing + sources = _build_ext.swig_sources(self, sources) or sources + # Then do any actual SWIG stuff on the remainder + return _du_build_ext.swig_sources(self, sources, *otherargs) + + + + def get_ext_filename(self, fullname): + filename = _build_ext.get_ext_filename(self,fullname) + ext = self.ext_map[fullname] + if isinstance(ext,Library): + fn, ext = os.path.splitext(filename) + return self.shlib_compiler.library_filename(fn,libtype) + elif use_stubs and ext._links_to_dynamic: + d,fn = os.path.split(filename) + return os.path.join(d,'dl-'+fn) + else: + return filename + + def initialize_options(self): + _build_ext.initialize_options(self) + self.shlib_compiler = None + self.shlibs = [] + self.ext_map = {} + + def finalize_options(self): + _build_ext.finalize_options(self) + self.extensions = self.extensions or [] + self.check_extensions_list(self.extensions) + self.shlibs = [ext for ext in self.extensions + if isinstance(ext,Library)] + if self.shlibs: + self.setup_shlib_compiler() + for ext in self.extensions: + ext._full_name = self.get_ext_fullname(ext.name) + for ext in self.extensions: + fullname = ext._full_name + self.ext_map[fullname] = ext + ltd = ext._links_to_dynamic = \ + self.shlibs and self.links_to_dynamic(ext) or False + ext._needs_stub = ltd and use_stubs and not isinstance(ext,Library) + filename = ext._file_name = self.get_ext_filename(fullname) + libdir = os.path.dirname(os.path.join(self.build_lib,filename)) + if ltd and libdir not in ext.library_dirs: + ext.library_dirs.append(libdir) + if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs: + ext.runtime_library_dirs.append(os.curdir) + + def setup_shlib_compiler(self): + compiler = self.shlib_compiler = new_compiler( + compiler=self.compiler, dry_run=self.dry_run, force=self.force + ) + if sys.platform == "darwin": + tmp = _config_vars.copy() + try: + # XXX Help! I don't have any idea whether these are right... + _config_vars['LDSHARED'] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup" + _config_vars['CCSHARED'] = " -dynamiclib" + _config_vars['SO'] = ".dylib" + customize_compiler(compiler) + finally: + _config_vars.clear() + _config_vars.update(tmp) + else: + customize_compiler(compiler) + + if self.include_dirs is not None: + compiler.set_include_dirs(self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name,value) in self.define: + compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + compiler.undefine_macro(macro) + if self.libraries is not None: + compiler.set_libraries(self.libraries) + if self.library_dirs is not None: + compiler.set_library_dirs(self.library_dirs) + if self.rpath is not None: + compiler.set_runtime_library_dirs(self.rpath) + if self.link_objects is not None: + compiler.set_link_objects(self.link_objects) + + # hack so distutils' build_extension() builds a library instead + compiler.link_shared_object = link_shared_object.__get__(compiler) + + + + def get_export_symbols(self, ext): + if isinstance(ext,Library): + return ext.export_symbols + return _build_ext.get_export_symbols(self,ext) + + def build_extension(self, ext): + _compiler = self.compiler + try: + if isinstance(ext,Library): + self.compiler = self.shlib_compiler + _build_ext.build_extension(self,ext) + if ext._needs_stub: + self.write_stub( + self.get_finalized_command('build_py').build_lib, ext + ) + finally: + self.compiler = _compiler + + def links_to_dynamic(self, ext): + """Return true if 'ext' links to a dynamic lib in the same package""" + # XXX this should check to ensure the lib is actually being built + # XXX as dynamic, and not just using a locally-found version or a + # XXX static-compiled version + libnames = dict.fromkeys([lib._full_name for lib in self.shlibs]) + pkg = '.'.join(ext._full_name.split('.')[:-1]+['']) + for libname in ext.libraries: + if pkg+libname in libnames: return True + return False + + def get_outputs(self): + outputs = _build_ext.get_outputs(self) + optimize = self.get_finalized_command('build_py').optimize + for ext in self.extensions: + if ext._needs_stub: + base = os.path.join(self.build_lib, *ext._full_name.split('.')) + outputs.append(base+'.py') + outputs.append(base+'.pyc') + if optimize: + outputs.append(base+'.pyo') + return outputs + + def write_stub(self, output_dir, ext, compile=False): + log.info("writing stub loader for %s to %s",ext._full_name, output_dir) + stub_file = os.path.join(output_dir, *ext._full_name.split('.'))+'.py' + if compile and os.path.exists(stub_file): + raise DistutilsError(stub_file+" already exists! Please delete.") + if not self.dry_run: + f = open(stub_file,'w') + f.write('\n'.join([ + "def __bootstrap__():", + " global __bootstrap__, __file__, __loader__", + " import sys, os, pkg_resources, imp"+if_dl(", dl"), + " __file__ = pkg_resources.resource_filename(__name__,%r)" + % os.path.basename(ext._file_name), + " del __bootstrap__", + " if '__loader__' in globals():", + " del __loader__", + if_dl(" old_flags = sys.getdlopenflags()"), + " old_dir = os.getcwd()", + " try:", + " os.chdir(os.path.dirname(__file__))", + if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), + " imp.load_dynamic(__name__,__file__)", + " finally:", + if_dl(" sys.setdlopenflags(old_flags)"), + " os.chdir(old_dir)", + "__bootstrap__()", + "" # terminal \n + ])) + f.close() + if compile: + from distutils.util import byte_compile + byte_compile([stub_file], optimize=0, + force=True, dry_run=self.dry_run) + optimize = self.get_finalized_command('install_lib').optimize + if optimize > 0: + byte_compile([stub_file], optimize=optimize, + force=True, dry_run=self.dry_run) + if os.path.exists(stub_file) and not self.dry_run: + os.unlink(stub_file) + + +if use_stubs or os.name=='nt': + # Build shared libraries + # + def link_shared_object(self, objects, output_libname, output_dir=None, + libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None + ): self.link( + self.SHARED_LIBRARY, objects, output_libname, + output_dir, libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, extra_preargs, extra_postargs, + build_temp, target_lang + ) +else: + # Build static libraries everywhere else + libtype = 'static' + + def link_shared_object(self, objects, output_libname, output_dir=None, + libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None + ): + # XXX we need to either disallow these attrs on Library instances, + # or warn/abort here if set, or something... + #libraries=None, library_dirs=None, runtime_library_dirs=None, + #export_symbols=None, extra_preargs=None, extra_postargs=None, + #build_temp=None + + assert output_dir is None # distutils build_ext doesn't pass this + output_dir,filename = os.path.split(output_libname) + basename, ext = os.path.splitext(filename) + if self.library_filename("x").startswith('lib'): + # strip 'lib' prefix; this is kludgy if some platform uses + # a different prefix + basename = basename[3:] + + self.create_static_lib( + objects, basename, output_dir, debug, target_lang + ) diff --git a/tests/setuptools_extension.py b/tests/setuptools_extension.py new file mode 100644 index 00000000..ec6b690c --- /dev/null +++ b/tests/setuptools_extension.py @@ -0,0 +1,51 @@ +from distutils.core import Extension as _Extension +from distutils.core import Distribution as _Distribution + +def _get_unpatched(cls): + """Protect against re-patching the distutils if reloaded + + Also ensures that no other distutils extension monkeypatched the distutils + first. + """ + while cls.__module__.startswith('setuptools'): + cls, = cls.__bases__ + if not cls.__module__.startswith('distutils'): + raise AssertionError( + "distutils has already been patched by %r" % cls + ) + return cls + +_Distribution = _get_unpatched(_Distribution) +_Extension = _get_unpatched(_Extension) + +try: + from Pyrex.Distutils.build_ext import build_ext +except ImportError: + have_pyrex = False +else: + have_pyrex = True + + +class Extension(_Extension): + """Extension that uses '.c' files in place of '.pyx' files""" + + if not have_pyrex: + # convert .pyx extensions to .c + def __init__(self,*args,**kw): + _Extension.__init__(self,*args,**kw) + sources = [] + for s in self.sources: + if s.endswith('.pyx'): + sources.append(s[:-3]+'c') + else: + sources.append(s) + self.sources = sources + +class Library(Extension): + """Just like a regular Extension, but built as a library instead""" + +import sys, distutils.core, distutils.extension +distutils.core.Extension = Extension +distutils.extension.Extension = Extension +if 'distutils.command.build_ext' in sys.modules: + sys.modules['distutils.command.build_ext'].Extension = Extension -- cgit v1.2.1 From e8fce7b386235ac8943372a062330694090a7533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 6 Mar 2010 02:11:14 +0000 Subject: fixed various failures and environment alterations in distutils.test_build_ext --- tests/test_build_ext.py | 62 ++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 867ba96e..4860c9bf 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,10 +19,15 @@ ALREADY_TESTED = False def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') + fallback_path = os.path.join(os.path.dirname(__file__), 'xxmodule.c') if srcdir is None: - # local fallback - return os.path.join(os.path.dirname(__file__), 'xxmodule.c') - return os.path.join(srcdir, 'Modules', 'xxmodule.c') + return fallback_path + locations = (srcdir, os.path.dirname(sys.executable)) + for location in locations: + path = os.path.join(location, 'Modules', 'xxmodule.c') + if os.path.exists(path): + return path + return fallback_path class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, @@ -81,7 +86,7 @@ class BuildExtTestCase(support.TempdirManager, def tearDown(self): # Get everything back to normal test_support.unload('xx') - sys.path = self.sys_path + sys.path[:] = self.sys_path # XXX on Windows the test leaves a directory with xx module in TEMP shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') super(BuildExtTestCase, self).tearDown() @@ -350,22 +355,31 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(wanted, path) def test_setuptools_compat(self): - from setuptools_build_ext import build_ext as setuptools_build_ext - from setuptools_extension import Extension - - etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') - etree_ext = Extension('lxml.etree', [etree_c]) - dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) - cmd = setuptools_build_ext(dist) - cmd.ensure_finalized() - cmd.inplace = 1 - cmd.distribution.package_dir = {'': 'src'} - cmd.distribution.packages = ['lxml', 'lxml.html'] - curdir = os.getcwd() - ext = sysconfig.get_config_var("SO") - wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) - path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + import distutils.core, distutils.extension, distutils.command.build_ext + saved_ext = distutils.extension.Extension + try: + # theses import patch Distutils' Extension class + from setuptools_build_ext import build_ext as setuptools_build_ext + from setuptools_extension import Extension + + etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + etree_ext = Extension('lxml.etree', [etree_c]) + dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + cmd = setuptools_build_ext(dist) + cmd.ensure_finalized() + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + ext = sysconfig.get_config_var("SO") + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEquals(wanted, path) + finally: + # restoring Distutils' Extension class otherwise its broken + distutils.extension.Extension = saved_ext + distutils.core.Extension = saved_ext + distutils.command.build_ext.Extension = saved_ext def test_build_ext_path_with_os_sep(self): dist = Distribution({'name': 'UpdateManager'}) @@ -391,13 +405,7 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(ext_path, wanted) def test_suite(): - src = _get_source_filename() - if not os.path.exists(src): - if test_support.verbose: - print ('test_build_ext: Cannot find source code (test' - ' must run in python build dir)') - return unittest.TestSuite() - else: return unittest.makeSuite(BuildExtTestCase) + return unittest.makeSuite(BuildExtTestCase) if __name__ == '__main__': test_support.run_unittest(test_suite()) -- cgit v1.2.1 From 77d34a843c3d00ae2dc37cd7b071a47a2b460aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 6 Mar 2010 02:17:28 +0000 Subject: search in the alternative location for VCExpress --- msvc9compiler.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 41d67faf..932b6ea2 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -38,6 +38,7 @@ HKEYS = (_winreg.HKEY_USERS, _winreg.HKEY_CLASSES_ROOT) VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" +VSEXPRESS_BASE = r"Software\Microsoft\VCExpress\%0.1f" WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" NET_BASE = r"Software\Microsoft\.NETFramework" @@ -216,9 +217,18 @@ def find_vcvarsall(version): productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, "productdir") except KeyError: - log.debug("Unable to find productdir in registry") productdir = None + # trying Express edition + if productdir is None: + vsbase = VSEXPRESS_BASE % version + try: + productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, + "productdir") + except KeyError: + productdir = None + log.debug("Unable to find productdir in registry") + if not productdir or not os.path.isdir(productdir): toolskey = "VS%0.f0COMNTOOLS" % version toolsdir = os.environ.get(toolskey, None) -- cgit v1.2.1 From 1e6721ca45da49df064748cdd8161fc717140487 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 6 Mar 2010 20:28:33 +0000 Subject: bump to 3.1.2rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8c0e5a3c..2e6a6fb6 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.1" +__version__ = "3.1.2rc1" #--end constants-- -- cgit v1.2.1 From d9daa5ddcc3f32e2acd377eb210a3a061976c0ce Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 6 Mar 2010 20:34:14 +0000 Subject: bump version to 2.7a4 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 977643bf..01589964 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7a3" +__version__ = "2.7a4" #--end constants-- -- cgit v1.2.1 From 32c211ba3c57c6d0405a86f29291c56f105a86f4 Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Sun, 7 Mar 2010 12:14:25 +0000 Subject: Fix some py3k warnings in the standard library. --- util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.py b/util.py index 36ac7213..1a55f708 100644 --- a/util.py +++ b/util.py @@ -406,7 +406,7 @@ def execute (func, args, msg=None, verbose=0, dry_run=0): log.info(msg) if not dry_run: - apply(func, args) + func(*args) def strtobool (val): -- cgit v1.2.1 From c0dac00e8b014f7b9180438f8e056f411f10a2c0 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 9 Mar 2010 22:31:52 +0000 Subject: Bumping to 2.6.5rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8c2ab8f3..888dd54a 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.5rc1" +__version__ = "2.6.5rc2" #--end constants-- -- cgit v1.2.1 From e14cbce6b42677bd762a5b2d351451e14ee3e3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 12 Mar 2010 18:27:13 +0000 Subject: Merged revisions 78707,78709 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r78707 | tarek.ziade | 2010-03-05 20:18:27 -0500 (Fri, 05 Mar 2010) | 1 line provide a fallback for xxmodule.c in case the buildir is not present ........ r78709 | tarek.ziade | 2010-03-05 20:23:21 -0500 (Fri, 05 Mar 2010) | 1 line simplified the fallback case ........ --- tests/test_build_ext.py | 3 +- tests/xxmodule.c | 379 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 380 insertions(+), 2 deletions(-) create mode 100644 tests/xxmodule.c diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index a1c236aa..b6c521bf 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -18,8 +18,7 @@ from test import test_support ALREADY_TESTED = False def _get_source_filename(): - srcdir = sysconfig.get_config_var('srcdir') - return os.path.join(srcdir, 'Modules', 'xxmodule.c') + return os.path.join(os.path.dirname(__file__), 'xxmodule.c') class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, diff --git a/tests/xxmodule.c b/tests/xxmodule.c new file mode 100644 index 00000000..6b498dd6 --- /dev/null +++ b/tests/xxmodule.c @@ -0,0 +1,379 @@ + +/* Use this file as a template to start implementing a module that + also declares object types. All occurrences of 'Xxo' should be changed + to something reasonable for your objects. After that, all other + occurrences of 'xx' should be changed to something reasonable for your + module. If your module is named foo your sourcefile should be named + foomodule.c. + + You will probably want to delete all references to 'x_attr' and add + your own types of attributes instead. Maybe you want to name your + local variables other than 'self'. If your object type is needed in + other files, you'll have to create a file "foobarobject.h"; see + intobject.h for an example. */ + +/* Xxo objects */ + +#include "Python.h" + +static PyObject *ErrorObject; + +typedef struct { + PyObject_HEAD + PyObject *x_attr; /* Attributes dictionary */ +} XxoObject; + +static PyTypeObject Xxo_Type; + +#define XxoObject_Check(v) (Py_TYPE(v) == &Xxo_Type) + +static XxoObject * +newXxoObject(PyObject *arg) +{ + XxoObject *self; + self = PyObject_New(XxoObject, &Xxo_Type); + if (self == NULL) + return NULL; + self->x_attr = NULL; + return self; +} + +/* Xxo methods */ + +static void +Xxo_dealloc(XxoObject *self) +{ + Py_XDECREF(self->x_attr); + PyObject_Del(self); +} + +static PyObject * +Xxo_demo(XxoObject *self, PyObject *args) +{ + if (!PyArg_ParseTuple(args, ":demo")) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef Xxo_methods[] = { + {"demo", (PyCFunction)Xxo_demo, METH_VARARGS, + PyDoc_STR("demo() -> None")}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +Xxo_getattr(XxoObject *self, char *name) +{ + if (self->x_attr != NULL) { + PyObject *v = PyDict_GetItemString(self->x_attr, name); + if (v != NULL) { + Py_INCREF(v); + return v; + } + } + return Py_FindMethod(Xxo_methods, (PyObject *)self, name); +} + +static int +Xxo_setattr(XxoObject *self, char *name, PyObject *v) +{ + if (self->x_attr == NULL) { + self->x_attr = PyDict_New(); + if (self->x_attr == NULL) + return -1; + } + if (v == NULL) { + int rv = PyDict_DelItemString(self->x_attr, name); + if (rv < 0) + PyErr_SetString(PyExc_AttributeError, + "delete non-existing Xxo attribute"); + return rv; + } + else + return PyDict_SetItemString(self->x_attr, name, v); +} + +static PyTypeObject Xxo_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyVarObject_HEAD_INIT(NULL, 0) + "xxmodule.Xxo", /*tp_name*/ + sizeof(XxoObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)Xxo_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + (getattrfunc)Xxo_getattr, /*tp_getattr*/ + (setattrfunc)Xxo_setattr, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; +/* --------------------------------------------------------------------- */ + +/* Function of two integers returning integer */ + +PyDoc_STRVAR(xx_foo_doc, +"foo(i,j)\n\ +\n\ +Return the sum of i and j."); + +static PyObject * +xx_foo(PyObject *self, PyObject *args) +{ + long i, j; + long res; + if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) + return NULL; + res = i+j; /* XXX Do something here */ + return PyInt_FromLong(res); +} + + +/* Function of no arguments returning new Xxo object */ + +static PyObject * +xx_new(PyObject *self, PyObject *args) +{ + XxoObject *rv; + + if (!PyArg_ParseTuple(args, ":new")) + return NULL; + rv = newXxoObject(args); + if (rv == NULL) + return NULL; + return (PyObject *)rv; +} + +/* Example with subtle bug from extensions manual ("Thin Ice"). */ + +static PyObject * +xx_bug(PyObject *self, PyObject *args) +{ + PyObject *list, *item; + + if (!PyArg_ParseTuple(args, "O:bug", &list)) + return NULL; + + item = PyList_GetItem(list, 0); + /* Py_INCREF(item); */ + PyList_SetItem(list, 1, PyInt_FromLong(0L)); + PyObject_Print(item, stdout, 0); + printf("\n"); + /* Py_DECREF(item); */ + + Py_INCREF(Py_None); + return Py_None; +} + +/* Test bad format character */ + +static PyObject * +xx_roj(PyObject *self, PyObject *args) +{ + PyObject *a; + long b; + if (!PyArg_ParseTuple(args, "O#:roj", &a, &b)) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + + +/* ---------- */ + +static PyTypeObject Str_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyVarObject_HEAD_INIT(NULL, 0) + "xxmodule.Str", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /* see initxx */ /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + +/* ---------- */ + +static PyObject * +null_richcompare(PyObject *self, PyObject *other, int op) +{ + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static PyTypeObject Null_Type = { + /* The ob_type field must be initialized in the module init function + * to be portable to Windows without using C++. */ + PyVarObject_HEAD_INIT(NULL, 0) + "xxmodule.Null", /*tp_name*/ + 0, /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + null_richcompare, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + 0, /*tp_methods*/ + 0, /*tp_members*/ + 0, /*tp_getset*/ + 0, /* see initxx */ /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + 0, /* see initxx */ /*tp_new*/ + 0, /*tp_free*/ + 0, /*tp_is_gc*/ +}; + + +/* ---------- */ + + +/* List of functions defined in the module */ + +static PyMethodDef xx_methods[] = { + {"roj", xx_roj, METH_VARARGS, + PyDoc_STR("roj(a,b) -> None")}, + {"foo", xx_foo, METH_VARARGS, + xx_foo_doc}, + {"new", xx_new, METH_VARARGS, + PyDoc_STR("new() -> new Xx object")}, + {"bug", xx_bug, METH_VARARGS, + PyDoc_STR("bug(o) -> None")}, + {NULL, NULL} /* sentinel */ +}; + +PyDoc_STRVAR(module_doc, +"This is a template module just for instruction."); + +/* Initialization function for the module (*must* be called initxx) */ + +PyMODINIT_FUNC +initxx(void) +{ + PyObject *m; + + /* Due to cross platform compiler issues the slots must be filled + * here. It's required for portability to Windows without requiring + * C++. */ + Null_Type.tp_base = &PyBaseObject_Type; + Null_Type.tp_new = PyType_GenericNew; + Str_Type.tp_base = &PyUnicode_Type; + + /* Finalize the type object including setting type of the new type + * object; doing it here is required for portability, too. */ + if (PyType_Ready(&Xxo_Type) < 0) + return; + + /* Create the module and add the functions */ + m = Py_InitModule3("xx", xx_methods, module_doc); + if (m == NULL) + return; + + /* Add some symbolic constants to the module */ + if (ErrorObject == NULL) { + ErrorObject = PyErr_NewException("xx.error", NULL, NULL); + if (ErrorObject == NULL) + return; + } + Py_INCREF(ErrorObject); + PyModule_AddObject(m, "error", ErrorObject); + + /* Add Str */ + if (PyType_Ready(&Str_Type) < 0) + return; + PyModule_AddObject(m, "Str", (PyObject *)&Str_Type); + + /* Add Null */ + if (PyType_Ready(&Null_Type) < 0) + return; + PyModule_AddObject(m, "Null", (PyObject *)&Null_Type); +} -- cgit v1.2.1 From 90552b30f6c3938cb7a5953734a536fb0950ed9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sat, 13 Mar 2010 18:37:30 +0000 Subject: following Barry suggestion for test_build_ext (see #8107) --- tests/test_build_ext.py | 5 +- tests/xxmodule.c | 379 ------------------------------------------------ 2 files changed, 4 insertions(+), 380 deletions(-) delete mode 100644 tests/xxmodule.c diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index b6c521bf..5ecfe15b 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -18,7 +18,10 @@ from test import test_support ALREADY_TESTED = False def _get_source_filename(): - return os.path.join(os.path.dirname(__file__), 'xxmodule.c') + srcdir = sysconfig.get_config_var('srcdir') + if srcdir is None: + return os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') + return os.path.join(srcdir, 'Modules', 'xxmodule.c') class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, diff --git a/tests/xxmodule.c b/tests/xxmodule.c deleted file mode 100644 index 6b498dd6..00000000 --- a/tests/xxmodule.c +++ /dev/null @@ -1,379 +0,0 @@ - -/* Use this file as a template to start implementing a module that - also declares object types. All occurrences of 'Xxo' should be changed - to something reasonable for your objects. After that, all other - occurrences of 'xx' should be changed to something reasonable for your - module. If your module is named foo your sourcefile should be named - foomodule.c. - - You will probably want to delete all references to 'x_attr' and add - your own types of attributes instead. Maybe you want to name your - local variables other than 'self'. If your object type is needed in - other files, you'll have to create a file "foobarobject.h"; see - intobject.h for an example. */ - -/* Xxo objects */ - -#include "Python.h" - -static PyObject *ErrorObject; - -typedef struct { - PyObject_HEAD - PyObject *x_attr; /* Attributes dictionary */ -} XxoObject; - -static PyTypeObject Xxo_Type; - -#define XxoObject_Check(v) (Py_TYPE(v) == &Xxo_Type) - -static XxoObject * -newXxoObject(PyObject *arg) -{ - XxoObject *self; - self = PyObject_New(XxoObject, &Xxo_Type); - if (self == NULL) - return NULL; - self->x_attr = NULL; - return self; -} - -/* Xxo methods */ - -static void -Xxo_dealloc(XxoObject *self) -{ - Py_XDECREF(self->x_attr); - PyObject_Del(self); -} - -static PyObject * -Xxo_demo(XxoObject *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":demo")) - return NULL; - Py_INCREF(Py_None); - return Py_None; -} - -static PyMethodDef Xxo_methods[] = { - {"demo", (PyCFunction)Xxo_demo, METH_VARARGS, - PyDoc_STR("demo() -> None")}, - {NULL, NULL} /* sentinel */ -}; - -static PyObject * -Xxo_getattr(XxoObject *self, char *name) -{ - if (self->x_attr != NULL) { - PyObject *v = PyDict_GetItemString(self->x_attr, name); - if (v != NULL) { - Py_INCREF(v); - return v; - } - } - return Py_FindMethod(Xxo_methods, (PyObject *)self, name); -} - -static int -Xxo_setattr(XxoObject *self, char *name, PyObject *v) -{ - if (self->x_attr == NULL) { - self->x_attr = PyDict_New(); - if (self->x_attr == NULL) - return -1; - } - if (v == NULL) { - int rv = PyDict_DelItemString(self->x_attr, name); - if (rv < 0) - PyErr_SetString(PyExc_AttributeError, - "delete non-existing Xxo attribute"); - return rv; - } - else - return PyDict_SetItemString(self->x_attr, name, v); -} - -static PyTypeObject Xxo_Type = { - /* The ob_type field must be initialized in the module init function - * to be portable to Windows without using C++. */ - PyVarObject_HEAD_INIT(NULL, 0) - "xxmodule.Xxo", /*tp_name*/ - sizeof(XxoObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)Xxo_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)Xxo_getattr, /*tp_getattr*/ - (setattrfunc)Xxo_setattr, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - 0, /*tp_alloc*/ - 0, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ -}; -/* --------------------------------------------------------------------- */ - -/* Function of two integers returning integer */ - -PyDoc_STRVAR(xx_foo_doc, -"foo(i,j)\n\ -\n\ -Return the sum of i and j."); - -static PyObject * -xx_foo(PyObject *self, PyObject *args) -{ - long i, j; - long res; - if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) - return NULL; - res = i+j; /* XXX Do something here */ - return PyInt_FromLong(res); -} - - -/* Function of no arguments returning new Xxo object */ - -static PyObject * -xx_new(PyObject *self, PyObject *args) -{ - XxoObject *rv; - - if (!PyArg_ParseTuple(args, ":new")) - return NULL; - rv = newXxoObject(args); - if (rv == NULL) - return NULL; - return (PyObject *)rv; -} - -/* Example with subtle bug from extensions manual ("Thin Ice"). */ - -static PyObject * -xx_bug(PyObject *self, PyObject *args) -{ - PyObject *list, *item; - - if (!PyArg_ParseTuple(args, "O:bug", &list)) - return NULL; - - item = PyList_GetItem(list, 0); - /* Py_INCREF(item); */ - PyList_SetItem(list, 1, PyInt_FromLong(0L)); - PyObject_Print(item, stdout, 0); - printf("\n"); - /* Py_DECREF(item); */ - - Py_INCREF(Py_None); - return Py_None; -} - -/* Test bad format character */ - -static PyObject * -xx_roj(PyObject *self, PyObject *args) -{ - PyObject *a; - long b; - if (!PyArg_ParseTuple(args, "O#:roj", &a, &b)) - return NULL; - Py_INCREF(Py_None); - return Py_None; -} - - -/* ---------- */ - -static PyTypeObject Str_Type = { - /* The ob_type field must be initialized in the module init function - * to be portable to Windows without using C++. */ - PyVarObject_HEAD_INIT(NULL, 0) - "xxmodule.Str", /*tp_name*/ - 0, /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - 0, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /* see initxx */ /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - 0, /*tp_alloc*/ - 0, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ -}; - -/* ---------- */ - -static PyObject * -null_richcompare(PyObject *self, PyObject *other, int op) -{ - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; -} - -static PyTypeObject Null_Type = { - /* The ob_type field must be initialized in the module init function - * to be portable to Windows without using C++. */ - PyVarObject_HEAD_INIT(NULL, 0) - "xxmodule.Null", /*tp_name*/ - 0, /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - 0, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - null_richcompare, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /* see initxx */ /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - 0, /*tp_alloc*/ - 0, /* see initxx */ /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ -}; - - -/* ---------- */ - - -/* List of functions defined in the module */ - -static PyMethodDef xx_methods[] = { - {"roj", xx_roj, METH_VARARGS, - PyDoc_STR("roj(a,b) -> None")}, - {"foo", xx_foo, METH_VARARGS, - xx_foo_doc}, - {"new", xx_new, METH_VARARGS, - PyDoc_STR("new() -> new Xx object")}, - {"bug", xx_bug, METH_VARARGS, - PyDoc_STR("bug(o) -> None")}, - {NULL, NULL} /* sentinel */ -}; - -PyDoc_STRVAR(module_doc, -"This is a template module just for instruction."); - -/* Initialization function for the module (*must* be called initxx) */ - -PyMODINIT_FUNC -initxx(void) -{ - PyObject *m; - - /* Due to cross platform compiler issues the slots must be filled - * here. It's required for portability to Windows without requiring - * C++. */ - Null_Type.tp_base = &PyBaseObject_Type; - Null_Type.tp_new = PyType_GenericNew; - Str_Type.tp_base = &PyUnicode_Type; - - /* Finalize the type object including setting type of the new type - * object; doing it here is required for portability, too. */ - if (PyType_Ready(&Xxo_Type) < 0) - return; - - /* Create the module and add the functions */ - m = Py_InitModule3("xx", xx_methods, module_doc); - if (m == NULL) - return; - - /* Add some symbolic constants to the module */ - if (ErrorObject == NULL) { - ErrorObject = PyErr_NewException("xx.error", NULL, NULL); - if (ErrorObject == NULL) - return; - } - Py_INCREF(ErrorObject); - PyModule_AddObject(m, "error", ErrorObject); - - /* Add Str */ - if (PyType_Ready(&Str_Type) < 0) - return; - PyModule_AddObject(m, "Str", (PyObject *)&Str_Type); - - /* Add Null */ - if (PyType_Ready(&Null_Type) < 0) - return; - PyModule_AddObject(m, "Null", (PyObject *)&Null_Type); -} -- cgit v1.2.1 From 892006c7928c6f51cc81fa3238429b758f0cf719 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 14 Mar 2010 10:23:39 +0000 Subject: Merged revisions 78018,78035-78040,78042-78043,78046,78048-78052,78054,78059,78075-78080 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r78018 | georg.brandl | 2010-02-06 11:08:21 +0100 (Sa, 06 Feb 2010) | 1 line #7864: make deprecation notices a bit clearer. ........ r78035 | georg.brandl | 2010-02-06 23:44:17 +0100 (Sa, 06 Feb 2010) | 1 line Fix duplicate import. ........ r78036 | georg.brandl | 2010-02-06 23:49:47 +0100 (Sa, 06 Feb 2010) | 1 line Remove unused import. ........ r78037 | georg.brandl | 2010-02-06 23:59:15 +0100 (Sa, 06 Feb 2010) | 1 line No need to assign the results of expressions used only for side effects. ........ r78038 | georg.brandl | 2010-02-07 00:02:29 +0100 (So, 07 Feb 2010) | 1 line Add a missing import. ........ r78039 | georg.brandl | 2010-02-07 00:06:24 +0100 (So, 07 Feb 2010) | 1 line Add missing imports. ........ r78040 | georg.brandl | 2010-02-07 00:08:00 +0100 (So, 07 Feb 2010) | 1 line Fix a few UnboundLocalErrors in test_long. ........ r78042 | georg.brandl | 2010-02-07 00:12:12 +0100 (So, 07 Feb 2010) | 1 line Add missing import. ........ r78043 | georg.brandl | 2010-02-07 00:12:19 +0100 (So, 07 Feb 2010) | 1 line Remove duplicate test method. ........ r78046 | georg.brandl | 2010-02-07 00:18:00 +0100 (So, 07 Feb 2010) | 1 line Fix various missing import/unbound name errors. ........ r78048 | georg.brandl | 2010-02-07 00:23:45 +0100 (So, 07 Feb 2010) | 1 line We heard you like test failures so we put unbound locals in your test so that you can fail while you fail. ........ r78049 | georg.brandl | 2010-02-07 00:33:33 +0100 (So, 07 Feb 2010) | 1 line Fix import/access for some identifiers. _TestSharedCTypes does not seem to be executed? ........ r78050 | georg.brandl | 2010-02-07 00:34:10 +0100 (So, 07 Feb 2010) | 1 line Fix more unbound locals in code paths that do not seem to be used. ........ r78051 | georg.brandl | 2010-02-07 00:53:52 +0100 (So, 07 Feb 2010) | 1 line Add missing import when running these tests standalone. ........ r78052 | georg.brandl | 2010-02-07 00:54:04 +0100 (So, 07 Feb 2010) | 1 line Add missing import when running these tests standalone. ........ r78054 | georg.brandl | 2010-02-07 00:58:25 +0100 (So, 07 Feb 2010) | 1 line Add missing import. ........ r78059 | georg.brandl | 2010-02-07 12:34:15 +0100 (So, 07 Feb 2010) | 1 line Use "regexp" consistently. ........ r78075 | georg.brandl | 2010-02-07 13:16:12 +0100 (So, 07 Feb 2010) | 1 line Fix another duplicated test method. ........ r78076 | georg.brandl | 2010-02-07 13:19:43 +0100 (So, 07 Feb 2010) | 1 line Fix wrong usage of "except X, Y:". ........ r78077 | georg.brandl | 2010-02-07 13:25:50 +0100 (So, 07 Feb 2010) | 1 line Fix two redefined test methods. ........ r78078 | georg.brandl | 2010-02-07 13:27:06 +0100 (So, 07 Feb 2010) | 1 line Fix a redefined test method. ........ r78079 | georg.brandl | 2010-02-07 13:34:26 +0100 (So, 07 Feb 2010) | 1 line Add a minimal test for fnmatchcase(). ........ r78080 | georg.brandl | 2010-02-07 13:55:12 +0100 (So, 07 Feb 2010) | 1 line Remove duplicate test method. ........ --- tests/test_bdist.py | 4 +++- tests/test_bdist_dumb.py | 4 +++- tests/test_bdist_msi.py | 4 +++- tests/test_bdist_rpm.py | 4 +++- tests/test_bdist_wininst.py | 4 +++- tests/test_cmd.py | 4 ++-- tests/test_cygwinccompiler.py | 4 ++-- tests/test_emxccompiler.py | 4 ++-- tests/test_sysconfig.py | 1 + 9 files changed, 22 insertions(+), 11 deletions(-) diff --git a/tests/test_bdist.py b/tests/test_bdist.py index f2849a97..29dcc7c8 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -5,6 +5,8 @@ import os import tempfile import shutil +from test.support import run_unittest + from distutils.core import Distribution from distutils.command.bdist import bdist from distutils.tests import support @@ -40,4 +42,4 @@ def test_suite(): return unittest.makeSuite(BuildTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 5eaef2a9..746144bf 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -11,6 +11,8 @@ try: except ImportError: zlib = None +from test.support import run_unittest + from distutils.core import Distribution from distutils.command.bdist_dumb import bdist_dumb from distutils.tests import support @@ -100,4 +102,4 @@ def test_suite(): return unittest.makeSuite(BuildDumbTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index ba2d3e19..2b2d8542 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -2,6 +2,8 @@ import unittest import sys +from test.support import run_unittest + from distutils.tests import support @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") @@ -20,4 +22,4 @@ def test_suite(): return unittest.makeSuite(BDistMSITestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 2aa257f7..1014e549 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -6,6 +6,8 @@ import os import tempfile import shutil +from test.support import run_unittest + from distutils.core import Distribution from distutils.command.bdist_rpm import bdist_rpm from distutils.tests import support @@ -122,4 +124,4 @@ def test_suite(): return unittest.makeSuite(BuildRpmTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index 9b1ba6d1..ffe41353 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -1,6 +1,8 @@ """Tests for distutils.command.bdist_wininst.""" import unittest +from test.support import run_unittest + from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support @@ -27,4 +29,4 @@ def test_suite(): return unittest.makeSuite(BuildWinInstTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 55ae421d..728652e2 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,7 +1,7 @@ """Tests for distutils.cmd.""" import unittest import os -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils.cmd import Command from distutils.dist import Distribution @@ -124,4 +124,4 @@ def test_suite(): return unittest.makeSuite(CommandTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index 4b95dfa3..374f392d 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -6,7 +6,7 @@ import subprocess import warnings import sysconfig -from test.support import check_warnings +from test.support import check_warnings, run_unittest from test.support import captured_stdout from distutils import cygwinccompiler @@ -109,4 +109,4 @@ def test_suite(): return unittest.makeSuite(CygwinCCompilerTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_emxccompiler.py b/tests/test_emxccompiler.py index 2176d641..1360f829 100644 --- a/tests/test_emxccompiler.py +++ b/tests/test_emxccompiler.py @@ -4,7 +4,7 @@ import sys import os import warnings -from test.support import check_warnings +from test.support import check_warnings, run_unittest from test.support import captured_stdout from distutils.emxccompiler import get_versions @@ -30,4 +30,4 @@ def test_suite(): return unittest.makeSuite(EmxCCompilerTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index e7df8031..9496950f 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -2,6 +2,7 @@ import os import test import unittest +import shutil from distutils import sysconfig from distutils.tests import support -- cgit v1.2.1 From 1e0efd73526d8014b1eeb4e238f5a288fa0d75a7 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 18 Mar 2010 22:14:36 +0000 Subject: Bumping to 2.6.5 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 888dd54a..9e7ab903 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.5rc2" +__version__ = "2.6.5" #--end constants-- -- cgit v1.2.1 From ad8054f8a9cc39437ae2280e4b04e9a38eb8ed65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 19 Mar 2010 21:56:34 +0000 Subject: Fixed #2698 - now reads the compiler option when creating the compiler --- command/build_ext.py | 2 +- tests/test_build_ext.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 15965864..bd61bc56 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -310,7 +310,7 @@ class build_ext(Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - self.compiler = new_compiler(compiler=None, + self.compiler = new_compiler(compiler=self.compiler, verbose=self.verbose, dry_run=self.dry_run, force=self.force) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index f992928d..b7cdc20a 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -329,6 +329,7 @@ class BuildExtTestCase(TempdirManager, self.assertEquals(so_dir, other_tmp_dir) cmd.inplace = 0 + cmd.compiler = None cmd.run() so_file = cmd.get_outputs()[0] self.assertTrue(os.path.exists(so_file)) -- cgit v1.2.1 From 2fd59dc3b01d05c832ac667d16b3355315c35608 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 20 Mar 2010 20:47:27 +0000 Subject: version becomes 3.1.2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2e6a6fb6..6dca599f 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.2rc1" +__version__ = "3.1.2" #--end constants-- -- cgit v1.2.1 From 5db75ca65fe87f9aa02424394ac2c0dcc3ec1c7b Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Sun, 21 Mar 2010 11:50:17 +0000 Subject: No more deprecation warnings for distutils.sysconfig, following r78666. But when the "dl" module is available, it gives a py3k deprecation warning. --- tests/test_build_ext.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 4860c9bf..f97ae1a7 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -358,6 +358,9 @@ class BuildExtTestCase(support.TempdirManager, import distutils.core, distutils.extension, distutils.command.build_ext saved_ext = distutils.extension.Extension try: + # on some platforms, it loads the deprecated "dl" module + test_support.import_module('setuptools_build_ext', deprecated=True) + # theses import patch Distutils' Extension class from setuptools_build_ext import build_ext as setuptools_build_ext from setuptools_extension import Extension -- cgit v1.2.1 From 0ad92a85f3a3c1fc73154c6e95c2d39d6e189ae2 Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Thu, 1 Apr 2010 18:17:09 +0000 Subject: #7092: Fix some -3 warnings, and fix Lib/platform.py when the path contains a double-quote. --- command/build_ext.py | 2 +- util.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 8248089f..aeb6b744 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -676,7 +676,7 @@ class build_ext (Command): # extensions in debug_mode are named 'module_d.pyd' under windows so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: - return apply(os.path.join, ext_path) + '_d' + so_ext + return os.path.join(*ext_path) + '_d' + so_ext return os.path.join(*ext_path) + so_ext def get_export_symbols (self, ext): diff --git a/util.py b/util.py index 1a55f708..994dd211 100644 --- a/util.py +++ b/util.py @@ -206,7 +206,7 @@ def convert_path (pathname): paths.remove('.') if not paths: return os.curdir - return apply(os.path.join, paths) + return os.path.join(*paths) # convert_path () -- cgit v1.2.1 From fbd4a04bd57b59a25516e4d66f93b29219f3f51f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 2 Apr 2010 21:14:04 +0000 Subject: removed the local copy of xxmodule, and skip only test_build_ext when xxmodule is not found, not the whole unittest --- tests/test_build_ext.py | 40 ++--- tests/xxmodule.c | 379 ------------------------------------------------ 2 files changed, 21 insertions(+), 398 deletions(-) delete mode 100644 tests/xxmodule.c diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index f97ae1a7..beb6d96c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,15 +19,11 @@ ALREADY_TESTED = False def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') - fallback_path = os.path.join(os.path.dirname(__file__), 'xxmodule.c') if srcdir is None: - return fallback_path - locations = (srcdir, os.path.dirname(sys.executable)) - for location in locations: - path = os.path.join(location, 'Modules', 'xxmodule.c') - if os.path.exists(path): - return path - return fallback_path + return os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') + return os.path.join(srcdir, 'Modules', 'xxmodule.c') + +_XX_MODULE_PATH = _get_source_filename() class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, @@ -37,10 +33,24 @@ class BuildExtTestCase(support.TempdirManager, # Note that we're making changes to sys.path super(BuildExtTestCase, self).setUp() self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") - self.sys_path = sys.path[:] - sys.path.append(self.tmp_dir) - shutil.copy(_get_source_filename(), self.tmp_dir) + if os.path.exists(_XX_MODULE_PATH): + self.sys_path = sys.path[:] + sys.path.append(self.tmp_dir) + shutil.copy(_XX_MODULE_PATH, self.tmp_dir) + + def tearDown(self): + # Get everything back to normal + if os.path.exists(_XX_MODULE_PATH): + test_support.unload('xx') + sys.path[:] = self.sys_path + # XXX on Windows the test leaves a directory + # with xx module in TEMP + shutil.rmtree(self.tmp_dir, os.name == 'nt' or + sys.platform == 'cygwin') + super(BuildExtTestCase, self).tearDown() + @unittest.skipIf(not os.path.exists(_XX_MODULE_PATH), + 'xxmodule.c not found') def test_build_ext(self): global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') @@ -83,14 +93,6 @@ class BuildExtTestCase(support.TempdirManager, self.assert_(isinstance(xx.Null(), xx.Null)) self.assert_(isinstance(xx.Str(), xx.Str)) - def tearDown(self): - # Get everything back to normal - test_support.unload('xx') - sys.path[:] = self.sys_path - # XXX on Windows the test leaves a directory with xx module in TEMP - shutil.rmtree(self.tmp_dir, os.name == 'nt' or sys.platform == 'cygwin') - super(BuildExtTestCase, self).tearDown() - def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) cmd = build_ext(dist) diff --git a/tests/xxmodule.c b/tests/xxmodule.c deleted file mode 100644 index 6b498dd6..00000000 --- a/tests/xxmodule.c +++ /dev/null @@ -1,379 +0,0 @@ - -/* Use this file as a template to start implementing a module that - also declares object types. All occurrences of 'Xxo' should be changed - to something reasonable for your objects. After that, all other - occurrences of 'xx' should be changed to something reasonable for your - module. If your module is named foo your sourcefile should be named - foomodule.c. - - You will probably want to delete all references to 'x_attr' and add - your own types of attributes instead. Maybe you want to name your - local variables other than 'self'. If your object type is needed in - other files, you'll have to create a file "foobarobject.h"; see - intobject.h for an example. */ - -/* Xxo objects */ - -#include "Python.h" - -static PyObject *ErrorObject; - -typedef struct { - PyObject_HEAD - PyObject *x_attr; /* Attributes dictionary */ -} XxoObject; - -static PyTypeObject Xxo_Type; - -#define XxoObject_Check(v) (Py_TYPE(v) == &Xxo_Type) - -static XxoObject * -newXxoObject(PyObject *arg) -{ - XxoObject *self; - self = PyObject_New(XxoObject, &Xxo_Type); - if (self == NULL) - return NULL; - self->x_attr = NULL; - return self; -} - -/* Xxo methods */ - -static void -Xxo_dealloc(XxoObject *self) -{ - Py_XDECREF(self->x_attr); - PyObject_Del(self); -} - -static PyObject * -Xxo_demo(XxoObject *self, PyObject *args) -{ - if (!PyArg_ParseTuple(args, ":demo")) - return NULL; - Py_INCREF(Py_None); - return Py_None; -} - -static PyMethodDef Xxo_methods[] = { - {"demo", (PyCFunction)Xxo_demo, METH_VARARGS, - PyDoc_STR("demo() -> None")}, - {NULL, NULL} /* sentinel */ -}; - -static PyObject * -Xxo_getattr(XxoObject *self, char *name) -{ - if (self->x_attr != NULL) { - PyObject *v = PyDict_GetItemString(self->x_attr, name); - if (v != NULL) { - Py_INCREF(v); - return v; - } - } - return Py_FindMethod(Xxo_methods, (PyObject *)self, name); -} - -static int -Xxo_setattr(XxoObject *self, char *name, PyObject *v) -{ - if (self->x_attr == NULL) { - self->x_attr = PyDict_New(); - if (self->x_attr == NULL) - return -1; - } - if (v == NULL) { - int rv = PyDict_DelItemString(self->x_attr, name); - if (rv < 0) - PyErr_SetString(PyExc_AttributeError, - "delete non-existing Xxo attribute"); - return rv; - } - else - return PyDict_SetItemString(self->x_attr, name, v); -} - -static PyTypeObject Xxo_Type = { - /* The ob_type field must be initialized in the module init function - * to be portable to Windows without using C++. */ - PyVarObject_HEAD_INIT(NULL, 0) - "xxmodule.Xxo", /*tp_name*/ - sizeof(XxoObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - (destructor)Xxo_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ - (getattrfunc)Xxo_getattr, /*tp_getattr*/ - (setattrfunc)Xxo_setattr, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - 0, /*tp_alloc*/ - 0, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ -}; -/* --------------------------------------------------------------------- */ - -/* Function of two integers returning integer */ - -PyDoc_STRVAR(xx_foo_doc, -"foo(i,j)\n\ -\n\ -Return the sum of i and j."); - -static PyObject * -xx_foo(PyObject *self, PyObject *args) -{ - long i, j; - long res; - if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) - return NULL; - res = i+j; /* XXX Do something here */ - return PyInt_FromLong(res); -} - - -/* Function of no arguments returning new Xxo object */ - -static PyObject * -xx_new(PyObject *self, PyObject *args) -{ - XxoObject *rv; - - if (!PyArg_ParseTuple(args, ":new")) - return NULL; - rv = newXxoObject(args); - if (rv == NULL) - return NULL; - return (PyObject *)rv; -} - -/* Example with subtle bug from extensions manual ("Thin Ice"). */ - -static PyObject * -xx_bug(PyObject *self, PyObject *args) -{ - PyObject *list, *item; - - if (!PyArg_ParseTuple(args, "O:bug", &list)) - return NULL; - - item = PyList_GetItem(list, 0); - /* Py_INCREF(item); */ - PyList_SetItem(list, 1, PyInt_FromLong(0L)); - PyObject_Print(item, stdout, 0); - printf("\n"); - /* Py_DECREF(item); */ - - Py_INCREF(Py_None); - return Py_None; -} - -/* Test bad format character */ - -static PyObject * -xx_roj(PyObject *self, PyObject *args) -{ - PyObject *a; - long b; - if (!PyArg_ParseTuple(args, "O#:roj", &a, &b)) - return NULL; - Py_INCREF(Py_None); - return Py_None; -} - - -/* ---------- */ - -static PyTypeObject Str_Type = { - /* The ob_type field must be initialized in the module init function - * to be portable to Windows without using C++. */ - PyVarObject_HEAD_INIT(NULL, 0) - "xxmodule.Str", /*tp_name*/ - 0, /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - 0, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - 0, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /* see initxx */ /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - 0, /*tp_alloc*/ - 0, /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ -}; - -/* ---------- */ - -static PyObject * -null_richcompare(PyObject *self, PyObject *other, int op) -{ - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; -} - -static PyTypeObject Null_Type = { - /* The ob_type field must be initialized in the module init function - * to be portable to Windows without using C++. */ - PyVarObject_HEAD_INIT(NULL, 0) - "xxmodule.Null", /*tp_name*/ - 0, /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /* methods */ - 0, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash*/ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - 0, /*tp_doc*/ - 0, /*tp_traverse*/ - 0, /*tp_clear*/ - null_richcompare, /*tp_richcompare*/ - 0, /*tp_weaklistoffset*/ - 0, /*tp_iter*/ - 0, /*tp_iternext*/ - 0, /*tp_methods*/ - 0, /*tp_members*/ - 0, /*tp_getset*/ - 0, /* see initxx */ /*tp_base*/ - 0, /*tp_dict*/ - 0, /*tp_descr_get*/ - 0, /*tp_descr_set*/ - 0, /*tp_dictoffset*/ - 0, /*tp_init*/ - 0, /*tp_alloc*/ - 0, /* see initxx */ /*tp_new*/ - 0, /*tp_free*/ - 0, /*tp_is_gc*/ -}; - - -/* ---------- */ - - -/* List of functions defined in the module */ - -static PyMethodDef xx_methods[] = { - {"roj", xx_roj, METH_VARARGS, - PyDoc_STR("roj(a,b) -> None")}, - {"foo", xx_foo, METH_VARARGS, - xx_foo_doc}, - {"new", xx_new, METH_VARARGS, - PyDoc_STR("new() -> new Xx object")}, - {"bug", xx_bug, METH_VARARGS, - PyDoc_STR("bug(o) -> None")}, - {NULL, NULL} /* sentinel */ -}; - -PyDoc_STRVAR(module_doc, -"This is a template module just for instruction."); - -/* Initialization function for the module (*must* be called initxx) */ - -PyMODINIT_FUNC -initxx(void) -{ - PyObject *m; - - /* Due to cross platform compiler issues the slots must be filled - * here. It's required for portability to Windows without requiring - * C++. */ - Null_Type.tp_base = &PyBaseObject_Type; - Null_Type.tp_new = PyType_GenericNew; - Str_Type.tp_base = &PyUnicode_Type; - - /* Finalize the type object including setting type of the new type - * object; doing it here is required for portability, too. */ - if (PyType_Ready(&Xxo_Type) < 0) - return; - - /* Create the module and add the functions */ - m = Py_InitModule3("xx", xx_methods, module_doc); - if (m == NULL) - return; - - /* Add some symbolic constants to the module */ - if (ErrorObject == NULL) { - ErrorObject = PyErr_NewException("xx.error", NULL, NULL); - if (ErrorObject == NULL) - return; - } - Py_INCREF(ErrorObject); - PyModule_AddObject(m, "error", ErrorObject); - - /* Add Str */ - if (PyType_Ready(&Str_Type) < 0) - return; - PyModule_AddObject(m, "Str", (PyObject *)&Str_Type); - - /* Add Null */ - if (PyType_Ready(&Null_Type) < 0) - return; - PyModule_AddObject(m, "Null", (PyObject *)&Null_Type); -} -- cgit v1.2.1 From 147849f8190ccae1b36126562b12e04fe4d58a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 2 Apr 2010 21:24:55 +0000 Subject: Merged revisions 79618 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r79618 | tarek.ziade | 2010-04-02 23:14:04 +0200 (Fri, 02 Apr 2010) | 1 line removed the local copy of xxmodule, and skip only test_build_ext when xxmodule is not found, not the whole unittest ........ --- tests/test_build_ext.py | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index d0971836..aca2be21 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -25,19 +25,23 @@ ALREADY_TESTED = False def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') + if srcdir is None: + return os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') return os.path.join(srcdir, 'Modules', 'xxmodule.c') -class BuildExtTestCase(TempdirManager, - LoggingSilencer, - unittest.TestCase): +_XX_MODULE_PATH = _get_source_filename() + +class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase): + def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path super(BuildExtTestCase, self).setUp() self.tmp_dir = self.mkdtemp() - self.sys_path = sys.path, sys.path[:] - sys.path.append(self.tmp_dir) - shutil.copy(_get_source_filename(), self.tmp_dir) + if os.path.exists(_XX_MODULE_PATH): + self.sys_path = sys.path[:] + sys.path.append(self.tmp_dir) + shutil.copy(_XX_MODULE_PATH, self.tmp_dir) if sys.version > "2.6": import site self.old_user_base = site.USER_BASE @@ -45,6 +49,19 @@ class BuildExtTestCase(TempdirManager, from distutils.command import build_ext build_ext.USER_BASE = site.USER_BASE + def tearDown(self): + # Get everything back to normal + if os.path.exists(_XX_MODULE_PATH): + test_support.unload('xx') + sys.path[:] = self.sys_path + # XXX on Windows the test leaves a directory + # with xx module in TEMP + shutil.rmtree(self.tmp_dir, os.name == 'nt' or + sys.platform == 'cygwin') + super(BuildExtTestCase, self).tearDown() + + @unittest.skipIf(not os.path.exists(_XX_MODULE_PATH), + 'xxmodule.c not found') def test_build_ext(self): global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') @@ -87,18 +104,6 @@ class BuildExtTestCase(TempdirManager, self.assertTrue(isinstance(xx.Null(), xx.Null)) self.assertTrue(isinstance(xx.Str(), xx.Str)) - def tearDown(self): - # Get everything back to normal - support.unload('xx') - sys.path = self.sys_path[0] - sys.path[:] = self.sys_path[1] - if sys.version > "2.6": - import site - site.USER_BASE = self.old_user_base - from distutils.command import build_ext - build_ext.USER_BASE = self.old_user_base - super(BuildExtTestCase, self).tearDown() - def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) cmd = build_ext(dist) -- cgit v1.2.1 From 7b1cf0becdb877baafa3aa1ce8af17ac4c1ad0f7 Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Fri, 2 Apr 2010 22:38:52 +0000 Subject: Change test_support to support. Fixes a failing test on Windows. --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index aca2be21..e41a824f 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -52,7 +52,7 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase): def tearDown(self): # Get everything back to normal if os.path.exists(_XX_MODULE_PATH): - test_support.unload('xx') + support.unload('xx') sys.path[:] = self.sys_path # XXX on Windows the test leaves a directory # with xx module in TEMP -- cgit v1.2.1 From 4a4731eda22170a77bb24dd3c7fc8ff4cafecf9d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 10 Apr 2010 16:22:05 +0000 Subject: bump version to 2.7b1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 01589964..aa55cccb 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7a4" +__version__ = "2.7b1" #--end constants-- -- cgit v1.2.1 From ecfcbac59e57961b85a43bc7216ad4acd1797822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 12 Apr 2010 08:23:49 +0000 Subject: Fixed #8375 - test_distutils now checks what remains to be cleaned up during tearDown --- tests/support.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/support.py b/tests/support.py index d60da854..45c94411 100644 --- a/tests/support.py +++ b/tests/support.py @@ -63,6 +63,8 @@ class TempdirManager(object): super().tearDown() while self.tempdirs: d = self.tempdirs.pop() + if not os.path.exists(d): + continue shutil.rmtree(d, os.name in ('nt', 'cygwin')) def mkdtemp(self): -- cgit v1.2.1 From 48467da45e5b9e8a9c91eccfba55a9f0d483debd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 30 Apr 2010 12:15:12 +0000 Subject: Fixed #8577. distutils.sysconfig.get_python_inc() now differenciates buildir and srcdir --- sysconfig.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 54ccec49..bb53315b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -71,15 +71,19 @@ def get_python_inc(plat_specific=0, prefix=None): """ if prefix is None: prefix = plat_specific and EXEC_PREFIX or PREFIX + if os.name == "posix": if python_build: - base = os.path.dirname(os.path.abspath(sys.executable)) + buildir = os.path.dirname(sys.executable) if plat_specific: - inc_dir = base + # python.h is located in the buildir + inc_dir = buildir else: - inc_dir = os.path.join(base, "Include") - if not os.path.exists(inc_dir): - inc_dir = os.path.join(os.path.dirname(base), "Include") + # the source dir is relative to the buildir + srcdir = os.path.abspath(os.path.join(buildir, + get_config_var('srcdir'))) + # Include is located in the srcdir + inc_dir = os.path.join(srcdir, "Include") return inc_dir return os.path.join(prefix, "include", "python" + get_python_version()) elif os.name == "nt": -- cgit v1.2.1 From 8a6ec7a027bb2594b0e3c4f2499893a215cf2021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Fri, 30 Apr 2010 12:18:51 +0000 Subject: Merged revisions 80649 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r80649 | tarek.ziade | 2010-04-30 14:15:12 +0200 (Fri, 30 Apr 2010) | 1 line Fixed #8577. distutils.sysconfig.get_python_inc() now differenciates buildir and srcdir ........ --- sysconfig.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 54ccec49..bb53315b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -71,15 +71,19 @@ def get_python_inc(plat_specific=0, prefix=None): """ if prefix is None: prefix = plat_specific and EXEC_PREFIX or PREFIX + if os.name == "posix": if python_build: - base = os.path.dirname(os.path.abspath(sys.executable)) + buildir = os.path.dirname(sys.executable) if plat_specific: - inc_dir = base + # python.h is located in the buildir + inc_dir = buildir else: - inc_dir = os.path.join(base, "Include") - if not os.path.exists(inc_dir): - inc_dir = os.path.join(os.path.dirname(base), "Include") + # the source dir is relative to the buildir + srcdir = os.path.abspath(os.path.join(buildir, + get_config_var('srcdir'))) + # Include is located in the srcdir + inc_dir = os.path.join(srcdir, "Include") return inc_dir return os.path.join(prefix, "include", "python" + get_python_version()) elif os.name == "nt": -- cgit v1.2.1 From dd745deff5b10a162a986ed9e8c886b70a2f9d32 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Wed, 5 May 2010 19:09:31 +0000 Subject: In a number of places code still revers to "sys.platform == 'mac'" and that is dead code because it refers to a platform that is no longer supported (and hasn't been supported for several releases). Fixes issue #7908 for the trunk. --- command/install.py | 14 -------------- file_util.py | 11 +---------- sysconfig.py | 17 ----------------- util.py | 9 --------- 4 files changed, 1 insertion(+), 50 deletions(-) diff --git a/command/install.py b/command/install.py index 44c76926..f1f3bd5c 100644 --- a/command/install.py +++ b/command/install.py @@ -69,20 +69,6 @@ INSTALL_SCHEMES = { 'scripts': '$userbase/Scripts', 'data' : '$userbase', }, - 'mac': { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - }, - 'mac_user': { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/$py_version_short/include/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - }, 'os2': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', diff --git a/file_util.py b/file_util.py index d8e8fd5f..b3d9d54e 100644 --- a/file_util.py +++ b/file_util.py @@ -133,18 +133,9 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, if dry_run: return (dst, 1) - # On Mac OS, use the native file copy routine - if os.name == 'mac': - import macostools - try: - macostools.copy(src, dst, 0, preserve_times) - except os.error, exc: - raise DistutilsFileError( - "could not copy '%s' to '%s': %s" % (src, dst, exc[-1])) - # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) - elif link == 'hard': + if link == 'hard': if not (os.path.exists(dst) and os.path.samefile(src, dst)): os.link(src, dst) elif link == 'sym': diff --git a/sysconfig.py b/sysconfig.py index bb53315b..4d16b267 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -88,11 +88,6 @@ def get_python_inc(plat_specific=0, prefix=None): return os.path.join(prefix, "include", "python" + get_python_version()) elif os.name == "nt": return os.path.join(prefix, "include") - elif os.name == "mac": - if plat_specific: - return os.path.join(prefix, "Mac", "Include") - else: - return os.path.join(prefix, "Include") elif os.name == "os2": return os.path.join(prefix, "Include") else: @@ -135,18 +130,6 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): else: return os.path.join(prefix, "Lib", "site-packages") - elif os.name == "mac": - if plat_specific: - if standard_lib: - return os.path.join(prefix, "Lib", "lib-dynload") - else: - return os.path.join(prefix, "Lib", "site-packages") - else: - if standard_lib: - return os.path.join(prefix, "Lib") - else: - return os.path.join(prefix, "Lib", "site-packages") - elif os.name == "os2": if standard_lib: return os.path.join(prefix, "Lib") diff --git a/util.py b/util.py index 994dd211..b3ec6e96 100644 --- a/util.py +++ b/util.py @@ -235,15 +235,6 @@ def change_root (new_root, pathname): path = path[1:] return os.path.join(new_root, path) - elif os.name == 'mac': - if not os.path.isabs(pathname): - return os.path.join(new_root, pathname) - else: - # Chop off volume name from start of path - elements = string.split(pathname, ":", 1) - pathname = ":" + elements[1] - return os.path.join(new_root, pathname) - else: raise DistutilsPlatformError, \ "nothing known about platform '%s'" % os.name -- cgit v1.2.1 From fde15c22137823177d21001b7d4e302d4986791d Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Wed, 5 May 2010 19:11:21 +0000 Subject: Remove traces of MacOS9 support. Fix for issue #7908 --- command/install.py | 14 -------------- file_util.py | 9 --------- util.py | 9 --------- 3 files changed, 32 deletions(-) diff --git a/command/install.py b/command/install.py index 31d03871..3c28c660 100644 --- a/command/install.py +++ b/command/install.py @@ -65,20 +65,6 @@ INSTALL_SCHEMES = { 'scripts': '$userbase/Scripts', 'data' : '$userbase', }, - 'mac': { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - }, - 'mac_user': { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/$py_version_short/include/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - }, 'os2': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', diff --git a/file_util.py b/file_util.py index 758bde38..3a71bfd1 100644 --- a/file_util.py +++ b/file_util.py @@ -132,15 +132,6 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, if dry_run: return (dst, 1) - # On Mac OS, use the native file copy routine - if os.name == 'mac': - import macostools - try: - macostools.copy(src, dst, 0, preserve_times) - except os.error as exc: - raise DistutilsFileError( - "could not copy '%s' to '%s': %s" % (src, dst, exc.args[-1])) - # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) elif link == 'hard': diff --git a/util.py b/util.py index 8fd2ca07..c8bf0064 100644 --- a/util.py +++ b/util.py @@ -91,15 +91,6 @@ def change_root(new_root, pathname): path = path[1:] return os.path.join(new_root, path) - elif os.name == 'mac': - if not os.path.isabs(pathname): - return os.path.join(new_root, pathname) - else: - # Chop off volume name from start of path - elements = pathname.split(":", 1) - pathname = ":" + elements[1] - return os.path.join(new_root, pathname) - else: raise DistutilsPlatformError("nothing known about " "platform '%s'" % os.name) -- cgit v1.2.1 From 7337895492390e5fc7b8573ebfa9f3b43d5af94b Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sat, 8 May 2010 08:44:37 +0000 Subject: Fix for issue #7724: make it possible to build using the OSX 10.4u SDK on MacOSX 10.6 by honoring the specified SDK when looking for files. --- unixccompiler.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 783d4dca..ba831f7b 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" -import os, sys +import os, sys, re from types import StringType, NoneType from distutils import sysconfig @@ -305,10 +305,29 @@ class UnixCCompiler(CCompiler): dylib_f = self.library_filename(lib, lib_type='dylib') static_f = self.library_filename(lib, lib_type='static') + if sys.platform == 'darwin': + # On OSX users can specify an alternate SDK using + # '-isysroot', calculate the SDK root if it is specified + # (and use it further on) + cflags = sysconfig.get_config_var('CFLAGS') + m = re.search(r'-isysroot\s+(\S+)', cflags) + if m is None: + sysroot = '/' + else: + sysroot = m.group(1) + + + for dir in dirs: shared = os.path.join(dir, shared_f) dylib = os.path.join(dir, dylib_f) static = os.path.join(dir, static_f) + + if sys.platform == 'darwin' and (dir.startswith('/System/') or dir.startswith('/usr/')): + shared = os.path.join(sysroot, dir[1:], shared_f) + dylib = os.path.join(sysroot, dir[1:], dylib_f) + static = os.path.join(sysroot, dir[1:], static_f) + # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm # assuming that *all* Unix C compilers do. And of course I'm -- cgit v1.2.1 From edfa9c8bc18c53f3fd81697b251f5f84726182a3 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 8 May 2010 15:23:57 +0000 Subject: Revert r80963 - it broke compilation everywhere --- unixccompiler.py | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index ba831f7b..783d4dca 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" -import os, sys, re +import os, sys from types import StringType, NoneType from distutils import sysconfig @@ -305,29 +305,10 @@ class UnixCCompiler(CCompiler): dylib_f = self.library_filename(lib, lib_type='dylib') static_f = self.library_filename(lib, lib_type='static') - if sys.platform == 'darwin': - # On OSX users can specify an alternate SDK using - # '-isysroot', calculate the SDK root if it is specified - # (and use it further on) - cflags = sysconfig.get_config_var('CFLAGS') - m = re.search(r'-isysroot\s+(\S+)', cflags) - if m is None: - sysroot = '/' - else: - sysroot = m.group(1) - - - for dir in dirs: shared = os.path.join(dir, shared_f) dylib = os.path.join(dir, dylib_f) static = os.path.join(dir, static_f) - - if sys.platform == 'darwin' and (dir.startswith('/System/') or dir.startswith('/usr/')): - shared = os.path.join(sysroot, dir[1:], shared_f) - dylib = os.path.join(sysroot, dir[1:], dylib_f) - static = os.path.join(sysroot, dir[1:], static_f) - # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm # assuming that *all* Unix C compilers do. And of course I'm -- cgit v1.2.1 From 5b183062b39a966eeac553886793c3eef3c706e7 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 8 May 2010 17:08:17 +0000 Subject: bump version to 2.7 beta 2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index aa55cccb..5f401709 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7b1" +__version__ = "2.7b2" #--end constants-- -- cgit v1.2.1 From f23648b7d06b99e8eadfc45a75a703927dd038e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 May 2010 10:06:20 +0000 Subject: Fixed #8688: Distutils now recalculates MANIFEST everytime. --- command/sdist.py | 82 ++++++++++++++++------------------------------------- tests/test_sdist.py | 41 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index f1335e6a..4cede729 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -179,66 +179,34 @@ class sdist(Command): distribution, and put it in 'self.filelist'. This might involve reading the manifest template (and writing the manifest), or just reading the manifest, or just using the default file set -- it all - depends on the user's options and the state of the filesystem. + depends on the user's options. """ - # If we have a manifest template, see if it's newer than the - # manifest; if so, we'll regenerate the manifest. + # new behavior: + # the file list is recalculated everytime because + # even if MANIFEST.in or setup.py are not changed + # the user might have added some files in the tree that + # need to be included. + # + # This makes --force the default and only behavior. template_exists = os.path.isfile(self.template) + if not template_exists: + self.warn(("manifest template '%s' does not exist " + + "(using default file list)") % + self.template) + self.filelist.findall() + + if self.use_defaults: + self.add_defaults() + if template_exists: - template_newer = dep_util.newer(self.template, self.manifest) - - # The contents of the manifest file almost certainly depend on the - # setup script as well as the manifest template -- so if the setup - # script is newer than the manifest, we'll regenerate the manifest - # from the template. (Well, not quite: if we already have a - # manifest, but there's no template -- which will happen if the - # developer elects to generate a manifest some other way -- then we - # can't regenerate the manifest, so we don't.) - self.debug_print("checking if %s newer than %s" % - (self.distribution.script_name, self.manifest)) - setup_newer = dep_util.newer(self.distribution.script_name, - self.manifest) - - # cases: - # 1) no manifest, template exists: generate manifest - # (covered by 2a: no manifest == template newer) - # 2) manifest & template exist: - # 2a) template or setup script newer than manifest: - # regenerate manifest - # 2b) manifest newer than both: - # do nothing (unless --force or --manifest-only) - # 3) manifest exists, no template: - # do nothing (unless --force or --manifest-only) - # 4) no manifest, no template: generate w/ warning ("defaults only") - - manifest_outofdate = (template_exists and - (template_newer or setup_newer)) - force_regen = self.force_manifest or self.manifest_only - manifest_exists = os.path.isfile(self.manifest) - neither_exists = (not template_exists and not manifest_exists) - - # Regenerate the manifest if necessary (or if explicitly told to) - if manifest_outofdate or neither_exists or force_regen: - if not template_exists: - self.warn(("manifest template '%s' does not exist " + - "(using default file list)") % - self.template) - self.filelist.findall() - - if self.use_defaults: - self.add_defaults() - if template_exists: - self.read_template() - if self.prune: - self.prune_file_list() - - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() - - # Don't regenerate the manifest, just read it in. - else: - self.read_manifest() + self.read_template() + + if self.prune: + self.prune_file_list() + + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() def add_defaults(self): """Add all the default files to self.filelist: diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 20b20aae..76f5b774 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -346,6 +346,47 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: archive.close() + def test_get_file_list(self): + # make sure MANIFEST is recalculated + dist, cmd = self.get_cmd() + + # filling data_files by pointing files in package_data + dist.package_data = {'somecode': ['*.txt']} + self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEquals(len(manifest), 4) + + # adding a file + self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') + + # make sure build_py is reinitinialized, like a fresh run + build_py = dist.get_command_obj('build_py') + build_py.finalized = False + build_py.ensure_finalized() + + cmd.run() + + f = open(cmd.manifest) + try: + manifest2 = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + # do we have the new file in MANIFEST ? + self.assertEquals(len(manifest2), 5) + self.assertIn('doc2.txt', manifest2[-1]) + + def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From d27e915abdcc0ffa04c26e6eb7a9ea297fabffd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 May 2010 10:26:15 +0000 Subject: Merged revisions 81255 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r81255 | tarek.ziade | 2010-05-17 12:06:20 +0200 (Mon, 17 May 2010) | 1 line Fixed #8688: Distutils now recalculates MANIFEST everytime. ........ --- command/sdist.py | 83 ++++++++++++++++------------------------------------- tests/test_sdist.py | 62 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 58 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index a366d1ea..b93994d0 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -190,67 +190,34 @@ class sdist (Command): distribution, and put it in 'self.filelist'. This might involve reading the manifest template (and writing the manifest), or just reading the manifest, or just using the default file set -- it all - depends on the user's options and the state of the filesystem. + depends on the user's options. """ - - # If we have a manifest template, see if it's newer than the - # manifest; if so, we'll regenerate the manifest. + # new behavior: + # the file list is recalculated everytime because + # even if MANIFEST.in or setup.py are not changed + # the user might have added some files in the tree that + # need to be included. + # + # This makes --force the default and only behavior. template_exists = os.path.isfile(self.template) + if not template_exists: + self.warn(("manifest template '%s' does not exist " + + "(using default file list)") % + self.template) + self.filelist.findall() + + if self.use_defaults: + self.add_defaults() + if template_exists: - template_newer = dep_util.newer(self.template, self.manifest) - - # The contents of the manifest file almost certainly depend on the - # setup script as well as the manifest template -- so if the setup - # script is newer than the manifest, we'll regenerate the manifest - # from the template. (Well, not quite: if we already have a - # manifest, but there's no template -- which will happen if the - # developer elects to generate a manifest some other way -- then we - # can't regenerate the manifest, so we don't.) - self.debug_print("checking if %s newer than %s" % - (self.distribution.script_name, self.manifest)) - setup_newer = dep_util.newer(self.distribution.script_name, - self.manifest) - - # cases: - # 1) no manifest, template exists: generate manifest - # (covered by 2a: no manifest == template newer) - # 2) manifest & template exist: - # 2a) template or setup script newer than manifest: - # regenerate manifest - # 2b) manifest newer than both: - # do nothing (unless --force or --manifest-only) - # 3) manifest exists, no template: - # do nothing (unless --force or --manifest-only) - # 4) no manifest, no template: generate w/ warning ("defaults only") - - manifest_outofdate = (template_exists and - (template_newer or setup_newer)) - force_regen = self.force_manifest or self.manifest_only - manifest_exists = os.path.isfile(self.manifest) - neither_exists = (not template_exists and not manifest_exists) - - # Regenerate the manifest if necessary (or if explicitly told to) - if manifest_outofdate or neither_exists or force_regen: - if not template_exists: - self.warn(("manifest template '%s' does not exist " + - "(using default file list)") % - self.template) - self.filelist.findall() - - if self.use_defaults: - self.add_defaults() - if template_exists: - self.read_template() - if self.prune: - self.prune_file_list() - - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() - - # Don't regenerate the manifest, just read it in. - else: - self.read_manifest() + self.read_template() + + if self.prune: + self.prune_file_list() + + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() # get_file_list () diff --git a/tests/test_sdist.py b/tests/test_sdist.py index e322c138..6b8784ac 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,6 +29,7 @@ class sdistTestCase(PyPIRCCommandTestCase): super(sdistTestCase, self).setUp() self.old_path = os.getcwd() self.temp_pkg = os.path.join(self.mkdtemp(), 'temppkg') + self.tmp_dir = self.mkdtemp() def tearDown(self): os.chdir(self.old_path) @@ -151,6 +152,67 @@ class sdistTestCase(PyPIRCCommandTestCase): self.assertEquals(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + def get_cmd(self, metadata=None): + """Returns a cmd""" + if metadata is None: + metadata = {'name': 'fake', 'version': '1.0', + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'} + dist = Distribution(metadata) + dist.script_name = 'setup.py' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.dist_dir = 'dist' + def _warn(*args): + pass + cmd.warn = _warn + return dist, cmd + + def test_get_file_list(self): + # make sure MANIFEST is recalculated + dist, cmd = self.get_cmd() + + os.chdir(self.tmp_dir) + + # filling data_files by pointing files in package_data + os.mkdir(os.path.join(self.tmp_dir, 'somecode')) + self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#') + self.write_file((self.tmp_dir, 'somecode', 'one.py'), '#') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEquals(len(manifest), 2) + + # adding a file + self.write_file((self.tmp_dir, 'somecode', 'two.py'), '#') + + # make sure build_py is reinitinialized, like a fresh run + build_py = dist.get_command_obj('build_py') + build_py.finalized = False + build_py.ensure_finalized() + + cmd.run() + + f = open(cmd.manifest) + try: + manifest2 = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + # do we have the new file in MANIFEST ? + self.assertEquals(len(manifest2), 3) + self.assert_('two.py' in manifest2[-1]) + + def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From f842b86d7f34fade4059ab3e9fd224534f3fc664 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 May 2010 10:38:53 +0000 Subject: Merged revisions 81255 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r81255 | tarek.ziade | 2010-05-17 12:06:20 +0200 (Mon, 17 May 2010) | 1 line Fixed #8688: Distutils now recalculates MANIFEST everytime. ........ --- command/sdist.py | 82 ++++++++++++++++------------------------------------- tests/test_sdist.py | 41 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index f33406cd..b2088f98 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -179,66 +179,34 @@ class sdist(Command): distribution, and put it in 'self.filelist'. This might involve reading the manifest template (and writing the manifest), or just reading the manifest, or just using the default file set -- it all - depends on the user's options and the state of the filesystem. + depends on the user's options. """ - # If we have a manifest template, see if it's newer than the - # manifest; if so, we'll regenerate the manifest. + # new behavior: + # the file list is recalculated everytime because + # even if MANIFEST.in or setup.py are not changed + # the user might have added some files in the tree that + # need to be included. + # + # This makes --force the default and only behavior. template_exists = os.path.isfile(self.template) + if not template_exists: + self.warn(("manifest template '%s' does not exist " + + "(using default file list)") % + self.template) + self.filelist.findall() + + if self.use_defaults: + self.add_defaults() + if template_exists: - template_newer = dep_util.newer(self.template, self.manifest) - - # The contents of the manifest file almost certainly depend on the - # setup script as well as the manifest template -- so if the setup - # script is newer than the manifest, we'll regenerate the manifest - # from the template. (Well, not quite: if we already have a - # manifest, but there's no template -- which will happen if the - # developer elects to generate a manifest some other way -- then we - # can't regenerate the manifest, so we don't.) - self.debug_print("checking if %s newer than %s" % - (self.distribution.script_name, self.manifest)) - setup_newer = dep_util.newer(self.distribution.script_name, - self.manifest) - - # cases: - # 1) no manifest, template exists: generate manifest - # (covered by 2a: no manifest == template newer) - # 2) manifest & template exist: - # 2a) template or setup script newer than manifest: - # regenerate manifest - # 2b) manifest newer than both: - # do nothing (unless --force or --manifest-only) - # 3) manifest exists, no template: - # do nothing (unless --force or --manifest-only) - # 4) no manifest, no template: generate w/ warning ("defaults only") - - manifest_outofdate = (template_exists and - (template_newer or setup_newer)) - force_regen = self.force_manifest or self.manifest_only - manifest_exists = os.path.isfile(self.manifest) - neither_exists = (not template_exists and not manifest_exists) - - # Regenerate the manifest if necessary (or if explicitly told to) - if manifest_outofdate or neither_exists or force_regen: - if not template_exists: - self.warn("manifest template '%s' does not exist " - "(using default file list)" - % self.template) - self.filelist.findall() - - if self.use_defaults: - self.add_defaults() - if template_exists: - self.read_template() - if self.prune: - self.prune_file_list() - - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() - - # Don't regenerate the manifest, just read it in. - else: - self.read_manifest() + self.read_template() + + if self.prune: + self.prune_file_list() + + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() def add_defaults(self): """Add all the default files to self.filelist: diff --git a/tests/test_sdist.py b/tests/test_sdist.py index e0f1e937..1bc36c5c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -346,6 +346,47 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: archive.close() + def test_get_file_list(self): + # make sure MANIFEST is recalculated + dist, cmd = self.get_cmd() + + # filling data_files by pointing files in package_data + dist.package_data = {'somecode': ['*.txt']} + self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEquals(len(manifest), 4) + + # adding a file + self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') + + # make sure build_py is reinitinialized, like a fresh run + build_py = dist.get_command_obj('build_py') + build_py.finalized = False + build_py.ensure_finalized() + + cmd.run() + + f = open(cmd.manifest) + try: + manifest2 = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + # do we have the new file in MANIFEST ? + self.assertEquals(len(manifest2), 5) + self.assertIn('doc2.txt', manifest2[-1]) + + def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From 32b6f83c0cdc4ec43cda11decb3c4bade7a8880d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 May 2010 10:48:29 +0000 Subject: Merged revisions 81258 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r81258 | tarek.ziade | 2010-05-17 12:38:53 +0200 (Mon, 17 May 2010) | 9 lines Merged revisions 81255 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r81255 | tarek.ziade | 2010-05-17 12:06:20 +0200 (Mon, 17 May 2010) | 1 line Fixed #8688: Distutils now recalculates MANIFEST everytime. ........ ................ --- command/sdist.py | 82 ++++++++++++++++------------------------------------- tests/test_sdist.py | 41 +++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index ace9eeeb..88fde46c 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -173,66 +173,34 @@ class sdist(Command): distribution, and put it in 'self.filelist'. This might involve reading the manifest template (and writing the manifest), or just reading the manifest, or just using the default file set -- it all - depends on the user's options and the state of the filesystem. + depends on the user's options. """ - # If we have a manifest template, see if it's newer than the - # manifest; if so, we'll regenerate the manifest. + # new behavior: + # the file list is recalculated everytime because + # even if MANIFEST.in or setup.py are not changed + # the user might have added some files in the tree that + # need to be included. + # + # This makes --force the default and only behavior. template_exists = os.path.isfile(self.template) + if not template_exists: + self.warn(("manifest template '%s' does not exist " + + "(using default file list)") % + self.template) + self.filelist.findall() + + if self.use_defaults: + self.add_defaults() + if template_exists: - template_newer = dep_util.newer(self.template, self.manifest) - - # The contents of the manifest file almost certainly depend on the - # setup script as well as the manifest template -- so if the setup - # script is newer than the manifest, we'll regenerate the manifest - # from the template. (Well, not quite: if we already have a - # manifest, but there's no template -- which will happen if the - # developer elects to generate a manifest some other way -- then we - # can't regenerate the manifest, so we don't.) - self.debug_print("checking if %s newer than %s" % - (self.distribution.script_name, self.manifest)) - setup_newer = dep_util.newer(self.distribution.script_name, - self.manifest) - - # cases: - # 1) no manifest, template exists: generate manifest - # (covered by 2a: no manifest == template newer) - # 2) manifest & template exist: - # 2a) template or setup script newer than manifest: - # regenerate manifest - # 2b) manifest newer than both: - # do nothing (unless --force or --manifest-only) - # 3) manifest exists, no template: - # do nothing (unless --force or --manifest-only) - # 4) no manifest, no template: generate w/ warning ("defaults only") - - manifest_outofdate = (template_exists and - (template_newer or setup_newer)) - force_regen = self.force_manifest or self.manifest_only - manifest_exists = os.path.isfile(self.manifest) - neither_exists = (not template_exists and not manifest_exists) - - # Regenerate the manifest if necessary (or if explicitly told to) - if manifest_outofdate or neither_exists or force_regen: - if not template_exists: - self.warn("manifest template '%s' does not exist " - "(using default file list)" - % self.template) - self.filelist.findall() - - if self.use_defaults: - self.add_defaults() - if template_exists: - self.read_template() - if self.prune: - self.prune_file_list() - - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() - - # Don't regenerate the manifest, just read it in. - else: - self.read_manifest() + self.read_template() + + if self.prune: + self.prune_file_list() + + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() def add_defaults(self): """Add all the default files to self.filelist: diff --git a/tests/test_sdist.py b/tests/test_sdist.py index b7e5859c..f95035df 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -277,6 +277,47 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertRaises(DistutilsOptionError, cmd.finalize_options) + def test_get_file_list(self): + # make sure MANIFEST is recalculated + dist, cmd = self.get_cmd() + + # filling data_files by pointing files in package_data + dist.package_data = {'somecode': ['*.txt']} + self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEquals(len(manifest), 4) + + # adding a file + self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') + + # make sure build_py is reinitinialized, like a fresh run + build_py = dist.get_command_obj('build_py') + build_py.finalized = False + build_py.ensure_finalized() + + cmd.run() + + f = open(cmd.manifest) + try: + manifest2 = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + # do we have the new file in MANIFEST ? + self.assertEquals(len(manifest2), 5) + self.assertIn('doc2.txt', manifest2[-1]) + + def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From 170b952ecd0098e93237cbb1da234a8f2680035e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 May 2010 10:54:43 +0000 Subject: upgraded distutils docs w.r.t. the manifest regeneration --- command/sdist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index 4cede729..087ae9dc 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -63,7 +63,8 @@ class sdist(Command): "just regenerate the manifest and then stop " "(implies --force-manifest)"), ('force-manifest', 'f', - "forcibly regenerate the manifest and carry on as usual"), + "forcibly regenerate the manifest and carry on as usual. " + "Deprecated: now the manifest is always regenerated."), ('formats=', None, "formats for source distribution (comma-separated list)"), ('keep-temp', 'k', -- cgit v1.2.1 From 457ae770b4bc93dc866776384ab4dd9f6958ab5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 May 2010 11:00:17 +0000 Subject: Merged revisions 81261 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r81261 | tarek.ziade | 2010-05-17 12:54:43 +0200 (Mon, 17 May 2010) | 1 line upgraded distutils docs w.r.t. the manifest regeneration ........ --- command/sdist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index b93994d0..535b8d2a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -57,7 +57,8 @@ class sdist (Command): "just regenerate the manifest and then stop " "(implies --force-manifest)"), ('force-manifest', 'f', - "forcibly regenerate the manifest and carry on as usual"), + "forcibly regenerate the manifest and carry on as usual. " + "Deprecated: now the manifest is always regenerated."), ('formats=', None, "formats for source distribution (comma-separated list)"), ('keep-temp', 'k', -- cgit v1.2.1 From a99ff5b39025ea4dc8a72f9e7f1d51a17f1aacdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 May 2010 11:01:57 +0000 Subject: Merged revisions 81261 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r81261 | tarek.ziade | 2010-05-17 12:54:43 +0200 (Mon, 17 May 2010) | 1 line upgraded distutils docs w.r.t. the manifest regeneration ........ --- command/sdist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index b2088f98..f6e099b8 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -63,7 +63,8 @@ class sdist(Command): "just regenerate the manifest and then stop " "(implies --force-manifest)"), ('force-manifest', 'f', - "forcibly regenerate the manifest and carry on as usual"), + "forcibly regenerate the manifest and carry on as usual. " + "Deprecated: now the manifest is always regenerated."), ('formats=', None, "formats for source distribution (comma-separated list)"), ('keep-temp', 'k', -- cgit v1.2.1 From 86bffc2288f78b16f66e94e52c39c6e6fdf1c09a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Mon, 17 May 2010 11:04:41 +0000 Subject: Merged revisions 81263 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r81263 | tarek.ziade | 2010-05-17 13:01:57 +0200 (Mon, 17 May 2010) | 9 lines Merged revisions 81261 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r81261 | tarek.ziade | 2010-05-17 12:54:43 +0200 (Mon, 17 May 2010) | 1 line upgraded distutils docs w.r.t. the manifest regeneration ........ ................ --- command/sdist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index 88fde46c..bb210612 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -63,7 +63,8 @@ class sdist(Command): "just regenerate the manifest and then stop " "(implies --force-manifest)"), ('force-manifest', 'f', - "forcibly regenerate the manifest and carry on as usual"), + "forcibly regenerate the manifest and carry on as usual. " + "Deprecated: now the manifest is always regenerated."), ('formats=', None, "formats for source distribution (comma-separated list)"), ('keep-temp', 'k', -- cgit v1.2.1 From 8ea633f81832fc7cc959fa57b3ee97d886f1bd7d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 18 May 2010 23:37:50 +0000 Subject: Merged revisions 68750,68811,68945,69157 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r68750 | benjamin.peterson | 2009-01-18 22:47:04 +0000 (So, 18 Jan 2009) | 1 line fix encoding cookie case ........ r68811 | benjamin.peterson | 2009-01-20 18:58:27 +0000 (Di, 20 Jan 2009) | 1 line fix url ........ r68945 | tarek.ziade | 2009-01-25 22:11:04 +0000 (So, 25 Jan 2009) | 1 line added missing module docstring ........ r69157 | benjamin.peterson | 2009-01-31 23:43:25 +0000 (Sa, 31 Jan 2009) | 1 line add explanatory comment ........ --- command/install_lib.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/command/install_lib.py b/command/install_lib.py index 7e0c7088..fb15530e 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -1,4 +1,8 @@ # This module should be kept compatible with Python 2.1. +"""distutils.command.install_lib + +Implements the Distutils 'install_lib' command +(install all Python modules).""" __revision__ = "$Id$" -- cgit v1.2.1 From b78e3623ca34400e29297da0409211a7d83728c6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 19 May 2010 17:00:07 +0000 Subject: Issue #8663: distutils.log emulates backslashreplace error handler. Fix compilation in a non-ASCII directory if stdout encoding is ASCII (eg. if stdout is not a TTY). --- log.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/log.py b/log.py index 75885708..b301a833 100644 --- a/log.py +++ b/log.py @@ -27,6 +27,10 @@ class Log: stream = sys.stderr else: stream = sys.stdout + if stream.errors == 'strict': + # emulate backslashreplace error handler + encoding = stream.encoding + msg = msg.encode(encoding, "backslashreplace").decode(encoding) stream.write('%s\n' % msg) stream.flush() -- cgit v1.2.1 From 727851619e008c1f44e79a60ddff41bb4ebf02b7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 19 May 2010 17:15:50 +0000 Subject: Oops, add the new test_log.py for distutils test suite (missing part of r81359) --- tests/test_log.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/test_log.py diff --git a/tests/test_log.py b/tests/test_log.py new file mode 100644 index 00000000..d35de345 --- /dev/null +++ b/tests/test_log.py @@ -0,0 +1,36 @@ +"""Tests for distutils.log""" + +import sys +import unittest +from tempfile import NamedTemporaryFile + +from distutils import log + +class TestLog(unittest.TestCase): + def test_non_ascii(self): + # Issue #8663: test that non-ASCII text is escaped with + # backslashreplace error handler (stream use ASCII encoding and strict + # error handler) + old_stdout = sys.stdout + old_stderr = sys.stderr + try: + log.set_threshold(log.DEBUG) + with NamedTemporaryFile(mode="w+", encoding='ascii') as stdout, \ + NamedTemporaryFile(mode="w+", encoding='ascii') as stderr: + sys.stdout = stdout + sys.stderr = stderr + log.debug("debug:\xe9") + log.fatal("fatal:\xe9") + stdout.seek(0) + self.assertEquals(stdout.read().rstrip(), "debug:\\xe9") + stderr.seek(0) + self.assertEquals(stderr.read().rstrip(), "fatal:\\xe9") + finally: + sys.stdout = old_stdout + sys.stderr = old_stderr + +def test_suite(): + return unittest.makeSuite(TestLog) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From 6cf4f07323aac001c1c1a0e3aafbe55a84cec391 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 19 May 2010 20:30:19 +0000 Subject: Merged revisions 81359-81361 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r81359 | victor.stinner | 2010-05-19 19:00:07 +0200 (mer., 19 mai 2010) | 4 lines Issue #8663: distutils.log emulates backslashreplace error handler. Fix compilation in a non-ASCII directory if stdout encoding is ASCII (eg. if stdout is not a TTY). ........ r81360 | victor.stinner | 2010-05-19 19:11:19 +0200 (mer., 19 mai 2010) | 5 lines regrtest.py: call replace_stdout() before the first call to print() print("== ", os.getcwd()) fails if the current working directory is not ASCII whereas sys.stdout encoding is ASCII. ........ r81361 | victor.stinner | 2010-05-19 19:15:50 +0200 (mer., 19 mai 2010) | 2 lines Oops, add the new test_log.py for distutils test suite (missing part of r81359) ........ --- log.py | 4 ++++ tests/test_log.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/test_log.py diff --git a/log.py b/log.py index 75885708..b301a833 100644 --- a/log.py +++ b/log.py @@ -27,6 +27,10 @@ class Log: stream = sys.stderr else: stream = sys.stdout + if stream.errors == 'strict': + # emulate backslashreplace error handler + encoding = stream.encoding + msg = msg.encode(encoding, "backslashreplace").decode(encoding) stream.write('%s\n' % msg) stream.flush() diff --git a/tests/test_log.py b/tests/test_log.py new file mode 100644 index 00000000..d35de345 --- /dev/null +++ b/tests/test_log.py @@ -0,0 +1,36 @@ +"""Tests for distutils.log""" + +import sys +import unittest +from tempfile import NamedTemporaryFile + +from distutils import log + +class TestLog(unittest.TestCase): + def test_non_ascii(self): + # Issue #8663: test that non-ASCII text is escaped with + # backslashreplace error handler (stream use ASCII encoding and strict + # error handler) + old_stdout = sys.stdout + old_stderr = sys.stderr + try: + log.set_threshold(log.DEBUG) + with NamedTemporaryFile(mode="w+", encoding='ascii') as stdout, \ + NamedTemporaryFile(mode="w+", encoding='ascii') as stderr: + sys.stdout = stdout + sys.stderr = stderr + log.debug("debug:\xe9") + log.fatal("fatal:\xe9") + stdout.seek(0) + self.assertEquals(stdout.read().rstrip(), "debug:\\xe9") + stderr.seek(0) + self.assertEquals(stderr.read().rstrip(), "fatal:\\xe9") + finally: + sys.stdout = old_stdout + sys.stderr = old_stderr + +def test_suite(): + return unittest.makeSuite(TestLog) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- cgit v1.2.1 From cdb97f5f0ebcf2aa7abf44d4e08696e01310b7c3 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 3 Jun 2010 09:47:21 +0000 Subject: Fix for issue #7724: ensure that distutils and python's own setup.py honor the MacOSX SDK when one is specified. This is needed to be able to build using the 10.4u SDK while running on OSX 10.6. This is a fixed version of the patch in r80963, I've tested this patch on OSX and Linux. --- unixccompiler.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 783d4dca..b76f0d41 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" -import os, sys +import os, sys, re from types import StringType, NoneType from distutils import sysconfig @@ -305,10 +305,30 @@ class UnixCCompiler(CCompiler): dylib_f = self.library_filename(lib, lib_type='dylib') static_f = self.library_filename(lib, lib_type='static') + if sys.platform == 'darwin': + # On OSX users can specify an alternate SDK using + # '-isysroot', calculate the SDK root if it is specified + # (and use it further on) + cflags = sysconfig.get_config_var('CFLAGS') + m = re.search(r'-isysroot\s+(\S+)', cflags) + if m is None: + sysroot = '/' + else: + sysroot = m.group(1) + + + for dir in dirs: shared = os.path.join(dir, shared_f) dylib = os.path.join(dir, dylib_f) static = os.path.join(dir, static_f) + + if sys.platform == 'darwin' and ( + dir.startswith('/System/') or dir.startswith('/usr/')): + shared = os.path.join(sysroot, dir[1:], shared_f) + dylib = os.path.join(sysroot, dir[1:], dylib_f) + static = os.path.join(sysroot, dir[1:], static_f) + # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm # assuming that *all* Unix C compilers do. And of course I'm -- cgit v1.2.1 From 0ca04fb2c44992cbbfbee30960d10ffafeb3f286 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 3 Jun 2010 14:42:25 +0000 Subject: Merged revisions 81662 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r81662 | ronald.oussoren | 2010-06-03 11:47:21 +0200 (Thu, 03 Jun 2010) | 9 lines Fix for issue #7724: ensure that distutils and python's own setup.py honor the MacOSX SDK when one is specified. This is needed to be able to build using the 10.4u SDK while running on OSX 10.6. This is a fixed version of the patch in r80963, I've tested this patch on OSX and Linux. ........ --- unixccompiler.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index c14a5d3f..81a7de61 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" -import os, sys +import os, sys, re from distutils.dep_util import newer from distutils.ccompiler import \ @@ -320,10 +320,31 @@ class UnixCCompiler(CCompiler): dylib_f = self.library_filename(lib, lib_type='dylib') static_f = self.library_filename(lib, lib_type='static') + if sys.platform == 'darwin': + # On OSX users can specify an alternate SDK using + # '-isysroot', calculate the SDK root if it is specified + # (and use it further on) + _sysconfig = __import__('sysconfig') + cflags = _sysconfig.get_config_var('CFLAGS') + m = re.search(r'-isysroot\s+(\S+)', cflags) + if m is None: + sysroot = '/' + else: + sysroot = m.group(1) + + + for dir in dirs: shared = os.path.join(dir, shared_f) dylib = os.path.join(dir, dylib_f) static = os.path.join(dir, static_f) + + if sys.platform == 'darwin' and ( + dir.startswith('/System/') or dir.startswith('/usr/')): + shared = os.path.join(sysroot, dir[1:], shared_f) + dylib = os.path.join(sysroot, dir[1:], dylib_f) + static = os.path.join(sysroot, dir[1:], static_f) + # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm # assuming that *all* Unix C compilers do. And of course I'm -- cgit v1.2.1 From cd778292f149a5d71319f00c1bb22943d766683d Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 3 Jun 2010 14:59:56 +0000 Subject: Merged revisions 81662 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r81662 | ronald.oussoren | 2010-06-03 11:47:21 +0200 (Thu, 03 Jun 2010) | 9 lines Fix for issue #7724: ensure that distutils and python's own setup.py honor the MacOSX SDK when one is specified. This is needed to be able to build using the 10.4u SDK while running on OSX 10.6. This is a fixed version of the patch in r80963, I've tested this patch on OSX and Linux. ........ --- unixccompiler.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 783d4dca..b76f0d41 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" -import os, sys +import os, sys, re from types import StringType, NoneType from distutils import sysconfig @@ -305,10 +305,30 @@ class UnixCCompiler(CCompiler): dylib_f = self.library_filename(lib, lib_type='dylib') static_f = self.library_filename(lib, lib_type='static') + if sys.platform == 'darwin': + # On OSX users can specify an alternate SDK using + # '-isysroot', calculate the SDK root if it is specified + # (and use it further on) + cflags = sysconfig.get_config_var('CFLAGS') + m = re.search(r'-isysroot\s+(\S+)', cflags) + if m is None: + sysroot = '/' + else: + sysroot = m.group(1) + + + for dir in dirs: shared = os.path.join(dir, shared_f) dylib = os.path.join(dir, dylib_f) static = os.path.join(dir, static_f) + + if sys.platform == 'darwin' and ( + dir.startswith('/System/') or dir.startswith('/usr/')): + shared = os.path.join(sysroot, dir[1:], shared_f) + dylib = os.path.join(sysroot, dir[1:], dylib_f) + static = os.path.join(sysroot, dir[1:], static_f) + # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm # assuming that *all* Unix C compilers do. And of course I'm -- cgit v1.2.1 From 9c55a255899d88a40630c4ec8396638d17ebd89b Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 3 Jun 2010 16:21:03 +0000 Subject: Merged revisions 81673 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r81673 | ronald.oussoren | 2010-06-03 16:42:25 +0200 (Thu, 03 Jun 2010) | 16 lines Merged revisions 81662 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r81662 | ronald.oussoren | 2010-06-03 11:47:21 +0200 (Thu, 03 Jun 2010) | 9 lines Fix for issue #7724: ensure that distutils and python's own setup.py honor the MacOSX SDK when one is specified. This is needed to be able to build using the 10.4u SDK while running on OSX 10.6. This is a fixed version of the patch in r80963, I've tested this patch on OSX and Linux. ........ ................ --- unixccompiler.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index a33fdf0f..f4605f30 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ the "typical" Unix-style command-line C compiler: __revision__ = "$Id$" -import os, sys +import os, sys, re from distutils import sysconfig from distutils.dep_util import newer @@ -317,10 +317,30 @@ class UnixCCompiler(CCompiler): dylib_f = self.library_filename(lib, lib_type='dylib') static_f = self.library_filename(lib, lib_type='static') + if sys.platform == 'darwin': + # On OSX users can specify an alternate SDK using + # '-isysroot', calculate the SDK root if it is specified + # (and use it further on) + cflags = sysconfig.get_config_var('CFLAGS') + m = re.search(r'-isysroot\s+(\S+)', cflags) + if m is None: + sysroot = '/' + else: + sysroot = m.group(1) + + + for dir in dirs: shared = os.path.join(dir, shared_f) dylib = os.path.join(dir, dylib_f) static = os.path.join(dir, static_f) + + if sys.platform == 'darwin' and ( + dir.startswith('/System/') or dir.startswith('/usr/')): + shared = os.path.join(sysroot, dir[1:], shared_f) + dylib = os.path.join(sysroot, dir[1:], dylib_f) + static = os.path.join(sysroot, dir[1:], static_f) + # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm # assuming that *all* Unix C compilers do. And of course I'm -- cgit v1.2.1 From da66d6ba5d1d62159f678e9091f3946e9b096241 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 6 Jun 2010 00:22:09 +0000 Subject: bump version to 2.7 rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 5f401709..7f302101 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7b2" +__version__ = "2.7rc1" #--end constants-- -- cgit v1.2.1 From 59d7c14ff6725c5dba040f899f9c145cca169185 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Jun 2010 16:05:20 +0000 Subject: Fix for issue #8577: without this patch test_distutils will fail when builddir != srcdir (that is, when you run configure in a directory that is not the top of the source tree). --- command/install.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/command/install.py b/command/install.py index 3c28c660..e3e387ab 100644 --- a/command/install.py +++ b/command/install.py @@ -302,8 +302,8 @@ class install(Command): # about needing recursive variable expansion (shudder). py_version = sys.version.split()[0] - prefix, exec_prefix, srcdir = get_config_vars('prefix', 'exec_prefix', - 'srcdir') + prefix, exec_prefix, srcdir, projectbase = get_config_vars('prefix', 'exec_prefix', + 'srcdir', 'projectbase') self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), @@ -316,6 +316,7 @@ class install(Command): 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, 'srcdir': srcdir, + 'projectbase': projectbase, } self.config_vars['userbase'] = self.install_userbase -- cgit v1.2.1 From d42efce4a037530f77c8cc91e7f1735e3c14b96c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 21 Jun 2010 15:27:46 +0000 Subject: fix finding visual studio 2008 on 64 bit #8854 --- msvc9compiler.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 932b6ea2..d5d7f665 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -37,10 +37,20 @@ HKEYS = (_winreg.HKEY_USERS, _winreg.HKEY_LOCAL_MACHINE, _winreg.HKEY_CLASSES_ROOT) -VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" -VSEXPRESS_BASE = r"Software\Microsoft\VCExpress\%0.1f" -WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" -NET_BASE = r"Software\Microsoft\.NETFramework" +NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32) +if NATIVE_WIN64: + # Visual C++ is a 32-bit application, so we need to look in + # the corresponding registry branch, if we're running a + # 64-bit Python on Win64 + VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" + VSEXPRESS_BASE = r"Software\Wow6432Node\Microsoft\VCExpress\%0.1f" + WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" +else: + VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" + VSEXPRESS_BASE = r"Software\Microsoft\VCExpress\%0.1f" + WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Microsoft\.NETFramework" # A map keyed by get_platform() return values to values accepted by # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is -- cgit v1.2.1 From 898a004e6eee52a5ab4f92e2e40acbcaf0bdfd05 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 21 Jun 2010 15:37:16 +0000 Subject: Merged revisions 82130 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r82130 | benjamin.peterson | 2010-06-21 10:27:46 -0500 (Mon, 21 Jun 2010) | 1 line fix finding visual studio 2008 on 64 bit #8854 ........ --- msvc9compiler.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 6455fffa..15425d71 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -37,9 +37,18 @@ HKEYS = (winreg.HKEY_USERS, winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CLASSES_ROOT) -VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" -WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" -NET_BASE = r"Software\Microsoft\.NETFramework" +NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32) +if NATIVE_WIN64: + # Visual C++ is a 32-bit application, so we need to look in + # the corresponding registry branch, if we're running a + # 64-bit Python on Win64 + VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" +else: + VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Microsoft\.NETFramework" # A map keyed by get_platform() return values to values accepted by # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is -- cgit v1.2.1 From 39ba1bd353bca4a92a427e8e8ff3e9a870d47432 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 21 Jun 2010 15:39:28 +0000 Subject: Merged revisions 82130 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r82130 | benjamin.peterson | 2010-06-21 10:27:46 -0500 (Mon, 21 Jun 2010) | 1 line fix finding visual studio 2008 on 64 bit #8854 ........ --- msvc9compiler.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 9bf54c10..9fe8c744 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -37,9 +37,18 @@ HKEYS = (_winreg.HKEY_USERS, _winreg.HKEY_LOCAL_MACHINE, _winreg.HKEY_CLASSES_ROOT) -VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" -WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" -NET_BASE = r"Software\Microsoft\.NETFramework" +NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32) +if NATIVE_WIN64: + # Visual C++ is a 32-bit application, so we need to look in + # the corresponding registry branch, if we're running a + # 64-bit Python on Win64 + VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" +else: + VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Microsoft\.NETFramework" # A map keyed by get_platform() return values to values accepted by # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is -- cgit v1.2.1 From 7bed757e02f1ccf941a3b6210b46c0edda5ea9e8 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 21 Jun 2010 15:42:48 +0000 Subject: Merged revisions 82131 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r82131 | benjamin.peterson | 2010-06-21 10:37:16 -0500 (Mon, 21 Jun 2010) | 9 lines Merged revisions 82130 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r82130 | benjamin.peterson | 2010-06-21 10:27:46 -0500 (Mon, 21 Jun 2010) | 1 line fix finding visual studio 2008 on 64 bit #8854 ........ ................ --- msvc9compiler.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index ad021b57..761b9ca2 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -38,9 +38,18 @@ HKEYS = (winreg.HKEY_USERS, winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CLASSES_ROOT) -VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" -WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" -NET_BASE = r"Software\Microsoft\.NETFramework" +NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32) +if NATIVE_WIN64: + # Visual C++ is a 32-bit application, so we need to look in + # the corresponding registry branch, if we're running a + # 64-bit Python on Win64 + VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" +else: + VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Microsoft\.NETFramework" # A map keyed by get_platform() return values to values accepted by # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is -- cgit v1.2.1 From c54c55e5b4eeb636a400eff44da8f327ba383b93 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 21 Jun 2010 15:57:57 +0000 Subject: bump verson to 2.7rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7f302101..bdbf2b2d 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7rc1" +__version__ = "2.7rc2" #--end constants-- -- cgit v1.2.1 From 0f327d043dda9cbd1d528ff9e4e4051a803bd41a Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 27 Jun 2010 12:36:16 +0000 Subject: Two small fixes for the support for SDKs on MacOSX: 1) The code that checks if an path should be located in the SDK explicitly excludes /usr/local. This fixes issue9046 2) The SDK variant for filtering "db_dirs_to_check" in setup.py was not doing anything because of a missing assignment. --- unixccompiler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index b76f0d41..c49ac9ba 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -324,7 +324,9 @@ class UnixCCompiler(CCompiler): static = os.path.join(dir, static_f) if sys.platform == 'darwin' and ( - dir.startswith('/System/') or dir.startswith('/usr/')): + dir.startswith('/System/') or ( + dir.startswith('/usr/') and not dir.startswith('/usr/local/'))): + shared = os.path.join(sysroot, dir[1:], shared_f) dylib = os.path.join(sysroot, dir[1:], dylib_f) static = os.path.join(sysroot, dir[1:], static_f) -- cgit v1.2.1 From 177cb25982dd05cc40f8612a8ae14cf334509226 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 27 Jun 2010 12:37:46 +0000 Subject: Merged revisions 82272 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r82272 | ronald.oussoren | 2010-06-27 14:36:16 +0200 (Sun, 27 Jun 2010) | 8 lines Two small fixes for the support for SDKs on MacOSX: 1) The code that checks if an path should be located in the SDK explicitly excludes /usr/local. This fixes issue9046 2) The SDK variant for filtering "db_dirs_to_check" in setup.py was not doing anything because of a missing assignment. ........ --- unixccompiler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index 81a7de61..08179082 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -340,7 +340,9 @@ class UnixCCompiler(CCompiler): static = os.path.join(dir, static_f) if sys.platform == 'darwin' and ( - dir.startswith('/System/') or dir.startswith('/usr/')): + dir.startswith('/System/') or ( + dir.startswith('/usr/') and not dir.startswith('/usr/local/'))): + shared = os.path.join(sysroot, dir[1:], shared_f) dylib = os.path.join(sysroot, dir[1:], dylib_f) static = os.path.join(sysroot, dir[1:], static_f) -- cgit v1.2.1 From 5d756882f69f443b18cf373e4a61b1e803905f99 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 27 Jun 2010 12:39:22 +0000 Subject: Merged revisions 82272 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r82272 | ronald.oussoren | 2010-06-27 14:36:16 +0200 (Sun, 27 Jun 2010) | 8 lines Two small fixes for the support for SDKs on MacOSX: 1) The code that checks if an path should be located in the SDK explicitly excludes /usr/local. This fixes issue9046 2) The SDK variant for filtering "db_dirs_to_check" in setup.py was not doing anything because of a missing assignment. ........ --- unixccompiler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index b76f0d41..c49ac9ba 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -324,7 +324,9 @@ class UnixCCompiler(CCompiler): static = os.path.join(dir, static_f) if sys.platform == 'darwin' and ( - dir.startswith('/System/') or dir.startswith('/usr/')): + dir.startswith('/System/') or ( + dir.startswith('/usr/') and not dir.startswith('/usr/local/'))): + shared = os.path.join(sysroot, dir[1:], shared_f) dylib = os.path.join(sysroot, dir[1:], dylib_f) static = os.path.join(sysroot, dir[1:], static_f) -- cgit v1.2.1 From 1d311c0e512b4684b36e4a29a393a84d0452e8f5 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 27 Jun 2010 12:40:35 +0000 Subject: Merged revisions 82273 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ................ r82273 | ronald.oussoren | 2010-06-27 14:37:46 +0200 (Sun, 27 Jun 2010) | 15 lines Merged revisions 82272 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r82272 | ronald.oussoren | 2010-06-27 14:36:16 +0200 (Sun, 27 Jun 2010) | 8 lines Two small fixes for the support for SDKs on MacOSX: 1) The code that checks if an path should be located in the SDK explicitly excludes /usr/local. This fixes issue9046 2) The SDK variant for filtering "db_dirs_to_check" in setup.py was not doing anything because of a missing assignment. ........ ................ --- unixccompiler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index f4605f30..bf734161 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -336,7 +336,9 @@ class UnixCCompiler(CCompiler): static = os.path.join(dir, static_f) if sys.platform == 'darwin' and ( - dir.startswith('/System/') or dir.startswith('/usr/')): + dir.startswith('/System/') or ( + dir.startswith('/usr/') and not dir.startswith('/usr/local/'))): + shared = os.path.join(sysroot, dir[1:], shared_f) dylib = os.path.join(sysroot, dir[1:], dylib_f) static = os.path.join(sysroot, dir[1:], static_f) -- cgit v1.2.1 From 0342126c2eb6d6cecd585a296be6b111a63eaebd Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 3 Jul 2010 13:56:13 +0000 Subject: update to 2.7 final --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index bdbf2b2d..665fd768 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7rc2" +__version__ = "2.7" #--end constants-- -- cgit v1.2.1 From 6586ed42561965fc19b2c6c94f751b85b79b2f1c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 3 Jul 2010 14:51:25 +0000 Subject: prepare for 2.7.1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 665fd768..2c69a183 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7" +__version__ = "2.7.1a0" #--end constants-- -- cgit v1.2.1 From 9a1b14784687365ce119d0cc229765a4e50016a0 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 11 Jul 2010 08:52:52 +0000 Subject: Fix for issue #9164: with this patch sysconfig and distuls don't break when duplicate '-arch foo' flags end up in CFLAGS (which may happen when building a universal build using macports) --- util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/util.py b/util.py index b3ec6e96..4dcfeb55 100644 --- a/util.py +++ b/util.py @@ -144,8 +144,7 @@ def get_platform (): cflags = get_config_vars().get('CFLAGS') archs = re.findall('-arch\s+(\S+)', cflags) - archs.sort() - archs = tuple(archs) + archs = tuple(sorted(set(archs))) if len(archs) == 1: machine = archs[0] -- cgit v1.2.1 From 611bf70820557ab8c49303983449064d99360242 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 11 Jul 2010 09:08:11 +0000 Subject: Merged revisions 82791 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/release27-maint ........ r82791 | ronald.oussoren | 2010-07-11 10:52:52 +0200 (Sun, 11 Jul 2010) | 4 lines Fix for issue #9164: with this patch sysconfig and distuls don't break when duplicate '-arch foo' flags end up in CFLAGS (which may happen when building a universal build using macports) ........ --- util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/util.py b/util.py index 36ac7213..c4a8711d 100644 --- a/util.py +++ b/util.py @@ -144,8 +144,7 @@ def get_platform (): cflags = get_config_vars().get('CFLAGS') archs = re.findall('-arch\s+(\S+)', cflags) - archs.sort() - archs = tuple(archs) + archs = tuple(sorted(set(archs))) if len(archs) == 1: machine = archs[0] -- cgit v1.2.1 From a74d1cd994eb527a565d5686fa315dcdafac6d31 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 11 Jul 2010 09:29:09 +0000 Subject: Fix for issue 9164 --- util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/util.py b/util.py index 9a77561f..81754345 100644 --- a/util.py +++ b/util.py @@ -143,8 +143,7 @@ def get_platform (): cflags = get_config_vars().get('CFLAGS') archs = re.findall('-arch\s+(\S+)', cflags) - archs.sort() - archs = tuple(archs) + archs = tuple(sorted(set(archs))) if len(archs) == 1: machine = archs[0] -- cgit v1.2.1 From b97dbcf42d529ab4dfb2896cef8c0d53960595d7 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 12 Jul 2010 19:49:41 +0000 Subject: #6026: skip test_get_file_list when zlib is not available. --- tests/test_sdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 1bc36c5c..9a76eacb 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -346,6 +346,7 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: archive.close() + @unittest.skipUnless(zlib, "requires zlib") def test_get_file_list(self): # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() -- cgit v1.2.1 From 0c1ee9af184f3d93215a250b609f2607c2edfd62 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 12 Jul 2010 19:52:15 +0000 Subject: Merged revisions 82839 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r82839 | ezio.melotti | 2010-07-12 22:49:41 +0300 (Mon, 12 Jul 2010) | 1 line #6026: skip test_get_file_list when zlib is not available. ........ --- tests/test_sdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 76f5b774..f83b9f29 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -346,6 +346,7 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: archive.close() + @unittest.skipUnless(zlib, "requires zlib") def test_get_file_list(self): # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() -- cgit v1.2.1 From 9562a276832b4f27e2b7666e6157fa95937a22e1 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 12 Jul 2010 20:00:39 +0000 Subject: Merged revisions 82839 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r82839 | ezio.melotti | 2010-07-12 22:49:41 +0300 (Mon, 12 Jul 2010) | 1 line #6026: skip test_get_file_list when zlib is not available. ........ --- tests/test_sdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index f95035df..aa3a99fa 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -277,6 +277,7 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertRaises(DistutilsOptionError, cmd.finalize_options) + @unittest.skipUnless(zlib, "requires zlib") def test_get_file_list(self): # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() -- cgit v1.2.1 From 6cdb93646957b4c0ef41fd23bc05278c6f949d73 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 12 Jul 2010 21:23:20 +0000 Subject: Revert r82841. --- tests/test_sdist.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index aa3a99fa..f95035df 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -277,7 +277,6 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertRaises(DistutilsOptionError, cmd.finalize_options) - @unittest.skipUnless(zlib, "requires zlib") def test_get_file_list(self): # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() -- cgit v1.2.1 From 07be936471d1459a64a755dc90a53121a8ca41e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Thu, 22 Jul 2010 12:50:05 +0000 Subject: reverted distutils its 3.1 state. All new work is now happening in disutils2, and distutils is now feature-frozen. --- archive_util.py | 71 +----- bcppcompiler.py | 10 +- ccompiler.py | 172 ++++++------- cmd.py | 8 +- command/bdist.py | 18 +- command/bdist_dumb.py | 21 +- command/bdist_msi.py | 8 +- command/bdist_rpm.py | 9 +- command/bdist_wininst.py | 13 +- command/build.py | 7 +- command/build_clib.py | 4 +- command/build_ext.py | 140 ++++------- command/build_py.py | 7 +- command/build_scripts.py | 10 +- command/config.py | 5 +- command/install.py | 129 ++++++---- command/register.py | 8 +- command/sdist.py | 13 +- command/upload.py | 83 +++--- config.py | 3 + core.py | 24 +- cygwinccompiler.py | 51 ++-- dep_util.py | 64 ++--- dir_util.py | 2 +- dist.py | 128 ++-------- emxccompiler.py | 35 ++- errors.py | 47 ++-- extension.py | 13 +- fancy_getopt.py | 19 +- file_util.py | 58 +++-- filelist.py | 39 ++- msvc9compiler.py | 7 +- msvccompiler.py | 14 +- sysconfig.py | 571 ++++++++++++++++++++++++++++++++++++------ tests/support.py | 10 - tests/test_archive_util.py | 72 +----- tests/test_bdist.py | 4 +- tests/test_bdist_dumb.py | 27 +- tests/test_bdist_rpm.py | 4 +- tests/test_bdist_wininst.py | 4 +- tests/test_build_clib.py | 3 +- tests/test_build_ext.py | 82 +++--- tests/test_build_py.py | 13 +- tests/test_build_scripts.py | 6 +- tests/test_ccompiler.py | 81 ------ tests/test_cmd.py | 4 +- tests/test_cygwinccompiler.py | 88 +++++-- tests/test_dist.py | 85 +------ tests/test_emxccompiler.py | 33 --- tests/test_extension.py | 19 +- tests/test_file_util.py | 17 +- tests/test_filelist.py | 45 +--- tests/test_install.py | 53 ++-- tests/test_install_lib.py | 17 +- tests/test_register.py | 4 +- tests/test_sdist.py | 70 ------ tests/test_sysconfig.py | 37 ++- tests/test_unixccompiler.py | 10 +- tests/test_upload.py | 61 +++-- tests/test_util.py | 213 ++++++++-------- text_file.py | 4 +- unixccompiler.py | 44 ++-- util.py | 325 ++++++++++++++++-------- 63 files changed, 1597 insertions(+), 1649 deletions(-) delete mode 100644 tests/test_ccompiler.py delete mode 100644 tests/test_emxccompiler.py diff --git a/archive_util.py b/archive_util.py index 28e93fed..16164c7f 100644 --- a/archive_util.py +++ b/archive_util.py @@ -14,55 +14,15 @@ from distutils.spawn import spawn from distutils.dir_util import mkpath from distutils import log -try: - from pwd import getpwnam -except ImportError: - getpwnam = None - -try: - from grp import getgrnam -except ImportError: - getgrnam = None - -def _get_gid(name): - """Returns a gid, given a group name.""" - if getgrnam is None or name is None: - return None - try: - result = getgrnam(name) - except KeyError: - result = None - if result is not None: - return result[2] - return None - -def _get_uid(name): - """Returns an uid, given a user name.""" - if getpwnam is None or name is None: - return None - try: - result = getpwnam(name) - except KeyError: - result = None - if result is not None: - return result[2] - return None - -def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, - owner=None, group=None): +def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): """Create a (possibly compressed) tar file from all the files under 'base_dir'. 'compress' must be "gzip" (the default), "compress", "bzip2", or None. - (compress will be deprecated in Python 3.2) - - 'owner' and 'group' can be used to define an owner and a group for the - archive that is being built. If not provided, the current owner and group - will be used. - + Both "tar" and the compression utility named by 'compress' must be on + the default program search path, so this is probably Unix-specific. The output tar file will be named 'base_dir' + ".tar", possibly plus the appropriate compression extension (".gz", ".bz2" or ".Z"). - Returns the output filename. """ tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''} @@ -84,23 +44,10 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, import tarfile # late import so Python build itself doesn't break log.info('Creating tar archive') - - uid = _get_uid(owner) - gid = _get_gid(group) - - def _set_uid_gid(tarinfo): - if gid is not None: - tarinfo.gid = gid - tarinfo.gname = group - if uid is not None: - tarinfo.uid = uid - tarinfo.uname = owner - return tarinfo - if not dry_run: tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) try: - tar.add(base_dir, filter=_set_uid_gid) + tar.add(base_dir) finally: tar.close() @@ -190,7 +137,7 @@ def check_archive_formats(formats): return None def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, - dry_run=0, owner=None, group=None): + dry_run=0): """Create an archive file (eg. zip or tar). 'base_name' is the name of the file to create, minus any format-specific @@ -203,9 +150,6 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, ie. 'base_dir' will be the common prefix of all files and directories in the archive. 'root_dir' and 'base_dir' both default to the current directory. Returns the name of the archive file. - - 'owner' and 'group' are used when creating a tar archive. By default, - uses the current owner and group. """ save_cwd = os.getcwd() if root_dir is not None: @@ -227,11 +171,6 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, func = format_info[0] for arg, val in format_info[1]: kwargs[arg] = val - - if format != 'zip': - kwargs['owner'] = owner - kwargs['group'] = group - try: filename = func(base_name, base_dir, **kwargs) finally: diff --git a/bcppcompiler.py b/bcppcompiler.py index 77d94a59..c5e5cd25 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -13,11 +13,13 @@ for the Borland C++ compiler. __revision__ = "$Id$" -import os -from distutils.errors import (DistutilsExecError, CompileError, LibError, - LinkError, UnknownFileError) -from distutils.ccompiler import CCompiler, gen_preprocess_options +import os +from distutils.errors import \ + DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError, UnknownFileError +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options, gen_lib_options from distutils.file_util import write_file from distutils.dep_util import newer from distutils import log diff --git a/ccompiler.py b/ccompiler.py index 1a4e8fb2..34c77a37 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -5,71 +5,15 @@ for the Distutils compiler abstraction model.""" __revision__ = "$Id$" -import sys -import os -import re - -from distutils.errors import (CompileError, LinkError, UnknownFileError, - DistutilsPlatformError, DistutilsModuleError) +import sys, os, re +from distutils.errors import * from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath -from distutils.dep_util import newer_group +from distutils.dep_util import newer_pairwise, newer_group from distutils.util import split_quoted, execute from distutils import log -_sysconfig = __import__('sysconfig') - -def customize_compiler(compiler): - """Do any platform-specific customization of a CCompiler instance. - - Mainly needed on Unix, so we can plug in the information that - varies across Unices and is stored in Python's Makefile. - """ - if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ - _sysconfig.get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO', 'AR', - 'ARFLAGS') - - if 'CC' in os.environ: - cc = os.environ['CC'] - if 'CXX' in os.environ: - cxx = os.environ['CXX'] - if 'LDSHARED' in os.environ: - ldshared = os.environ['LDSHARED'] - if 'CPP' in os.environ: - cpp = os.environ['CPP'] - else: - cpp = cc + " -E" # not always - if 'LDFLAGS' in os.environ: - ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if 'CFLAGS' in os.environ: - cflags = opt + ' ' + os.environ['CFLAGS'] - ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if 'CPPFLAGS' in os.environ: - cpp = cpp + ' ' + os.environ['CPPFLAGS'] - cflags = cflags + ' ' + os.environ['CPPFLAGS'] - ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] - if 'AR' in os.environ: - ar = os.environ['AR'] - if 'ARFLAGS' in os.environ: - archiver = ar + ' ' + os.environ['ARFLAGS'] - else: - archiver = ar + ' ' + ar_flags - - cc_cmd = cc + ' ' + cflags - compiler.set_executables( - preprocessor=cpp, - compiler=cc_cmd, - compiler_so=cc_cmd + ' ' + ccshared, - compiler_cxx=cxx, - linker_so=ldshared, - linker_exe=cc, - archiver=archiver) - - compiler.shared_lib_extension = so_ext - class CCompiler: """Abstract base class to define the interface that must be implemented by real compiler classes. Also has some utility methods used by @@ -449,6 +393,22 @@ class CCompiler: return output_dir, macros, include_dirs + def _prep_compile(self, sources, output_dir, depends=None): + """Decide which souce files must be recompiled. + + Determine the list of object files corresponding to 'sources', + and figure out which ones really need to be recompiled. + Return a list of all object files and a dictionary telling + which source files can be skipped. + """ + # Get the list of expected output (object) files + objects = self.object_filenames(sources, output_dir=output_dir) + assert len(objects) == len(sources) + + # Return an empty dict for the "which source files can be skipped" + # return value to preserve API compatibility. + return objects, {} + def _fix_object_args(self, objects, output_dir): """Typecheck and fix up some arguments supplied to various methods. Specifically: ensure that 'objects' is a list; if output_dir is @@ -650,15 +610,26 @@ class CCompiler: """ pass + # values for target_desc parameter in link() SHARED_OBJECT = "shared_object" SHARED_LIBRARY = "shared_library" EXECUTABLE = "executable" - def link(self, target_desc, objects, output_filename, output_dir=None, - libraries=None, library_dirs=None, runtime_library_dirs=None, - export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None, build_temp=None, target_lang=None): + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): """Link a bunch of stuff together to create an executable or shared library file. @@ -707,11 +678,19 @@ class CCompiler: # Old 'link_*()' methods, rewritten to use the new 'link()' method. - def link_shared_lib(self, objects, output_libname, output_dir=None, - libraries=None, library_dirs=None, - runtime_library_dirs=None, export_symbols=None, - debug=0, extra_preargs=None, extra_postargs=None, - build_temp=None, target_lang=None): + def link_shared_lib(self, + objects, + output_libname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): self.link(CCompiler.SHARED_LIBRARY, objects, self.library_filename(output_libname, lib_type='shared'), output_dir, @@ -720,11 +699,19 @@ class CCompiler: extra_preargs, extra_postargs, build_temp, target_lang) - def link_shared_object(self, objects, output_filename, output_dir=None, - libraries=None, library_dirs=None, - runtime_library_dirs=None, export_symbols=None, - debug=0, extra_preargs=None, extra_postargs=None, - build_temp=None, target_lang=None): + def link_shared_object(self, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): self.link(CCompiler.SHARED_OBJECT, objects, output_filename, output_dir, libraries, library_dirs, runtime_library_dirs, @@ -732,10 +719,17 @@ class CCompiler: extra_preargs, extra_postargs, build_temp, target_lang) - def link_executable(self, objects, output_progname, output_dir=None, - libraries=None, library_dirs=None, - runtime_library_dirs=None, debug=0, extra_preargs=None, - extra_postargs=None, target_lang=None): + def link_executable(self, + objects, + output_progname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + target_lang=None): self.link(CCompiler.EXECUTABLE, objects, self.executable_filename(output_progname), output_dir, libraries, library_dirs, runtime_library_dirs, None, @@ -917,7 +911,7 @@ main (int argc, char **argv) { def move_file(self, src, dst): return move_file(src, dst, dry_run=self.dry_run) - def mkpath(self, name, mode=0o777): + def mkpath (self, name, mode=0o777): mkpath(name, mode, dry_run=self.dry_run) @@ -1085,14 +1079,12 @@ def gen_preprocess_options(macros, include_dirs): return pp_opts -def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries): +def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): """Generate linker options for searching library directories and - linking with specific libraries. - - 'libraries' and 'library_dirs' are, respectively, lists of library names - (not filenames!) and search directories. Returns a list of command-line - options suitable for use with some compiler (depending on the two format - strings passed in). + linking with specific libraries. 'libraries' and 'library_dirs' are, + respectively, lists of library names (not filenames!) and search + directories. Returns a list of command-line options suitable for use + with some compiler (depending on the two format strings passed in). """ lib_opts = [] @@ -1102,7 +1094,7 @@ def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries): for dir in runtime_library_dirs: opt = compiler.runtime_library_dir_option(dir) if isinstance(opt, list): - lib_opts.extend(opt) + lib_opts = lib_opts + opt else: lib_opts.append(opt) @@ -1113,14 +1105,14 @@ def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries): # pretty nasty way to arrange your C code. for lib in libraries: - lib_dir, lib_name = os.path.split(lib) - if lib_dir != '': + (lib_dir, lib_name) = os.path.split(lib) + if lib_dir: lib_file = compiler.find_library_file([lib_dir], lib_name) - if lib_file is not None: + if lib_file: lib_opts.append(lib_file) else: compiler.warn("no library file corresponding to " "'%s' found (skipping)" % lib) else: - lib_opts.append(compiler.library_option(lib)) + lib_opts.append(compiler.library_option (lib)) return lib_opts diff --git a/cmd.py b/cmd.py index 08632099..ae4efc7e 100644 --- a/cmd.py +++ b/cmd.py @@ -367,11 +367,9 @@ class Command: from distutils.spawn import spawn spawn(cmd, search_path, dry_run=self.dry_run) - def make_archive(self, base_name, format, root_dir=None, base_dir=None, - owner=None, group=None): - return archive_util.make_archive(base_name, format, root_dir, - base_dir, dry_run=self.dry_run, - owner=owner, group=group) + def make_archive(self, base_name, format, root_dir=None, base_dir=None): + return archive_util.make_archive(base_name, format, root_dir, base_dir, + dry_run=self.dry_run) def make_file(self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): diff --git a/command/bdist.py b/command/bdist.py index 72b0cefe..1a360b59 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,10 +6,9 @@ distribution).""" __revision__ = "$Id$" import os - -from distutils.util import get_platform from distutils.core import Command -from distutils.errors import DistutilsPlatformError, DistutilsOptionError +from distutils.errors import * +from distutils.util import get_platform def show_formats(): @@ -40,12 +39,6 @@ class bdist(Command): "[default: dist]"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), - ('owner=', 'u', - "Owner name used when creating a tar file" - " [default: current user]"), - ('group=', 'g', - "Group name used when creating a tar file" - " [default: current group]"), ] boolean_options = ['skip-build'] @@ -87,8 +80,6 @@ class bdist(Command): self.formats = None self.dist_dir = None self.skip_build = 0 - self.group = None - self.owner = None def finalize_options(self): # have to finalize 'plat_name' before 'bdist_base' @@ -134,11 +125,6 @@ class bdist(Command): if cmd_name not in self.no_format_option: sub_cmd.format = self.formats[i] - # passing the owner and group names for tar archiving - if cmd_name == 'bdist_dumb': - sub_cmd.owner = self.owner - sub_cmd.group = self.group - # If we're going to need to run this command again, tell it to # keep its temporary files around so subsequent runs go faster. if cmd_name in commands[i+1:]: diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index c2af95f1..2d399226 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -7,18 +7,16 @@ $exec_prefix).""" __revision__ = "$Id$" import os - -from sysconfig import get_python_version - -from distutils.util import get_platform from distutils.core import Command +from distutils.util import get_platform from distutils.dir_util import remove_tree, ensure_relative -from distutils.errors import DistutilsPlatformError +from distutils.errors import * +from distutils.sysconfig import get_python_version from distutils import log class bdist_dumb(Command): - description = 'create a "dumb" built distribution' + description = "create a \"dumb\" built distribution" user_options = [('bdist-dir=', 'd', "temporary directory for creating the distribution"), @@ -37,12 +35,6 @@ class bdist_dumb(Command): ('relative', None, "build the archive using relative paths" "(default: false)"), - ('owner=', 'u', - "Owner name used when creating a tar file" - " [default: current user]"), - ('group=', 'g', - "Group name used when creating a tar file" - " [default: current group]"), ] boolean_options = ['keep-temp', 'skip-build', 'relative'] @@ -59,8 +51,6 @@ class bdist_dumb(Command): self.dist_dir = None self.skip_build = 0 self.relative = 0 - self.owner = None - self.group = None def finalize_options(self): if self.bdist_dir is None: @@ -118,8 +108,7 @@ class bdist_dumb(Command): # Make the archive filename = self.make_archive(pseudoinstall_root, - self.format, root_dir=archive_root, - owner=self.owner, group=self.group) + self.format, root_dir=archive_root) if self.distribution.has_ext_modules(): pyversion = get_python_version() else: diff --git a/command/bdist_msi.py b/command/bdist_msi.py index f13c73b3..c4be47b5 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -6,15 +6,15 @@ """ Implements the bdist_msi command. """ -import sys, os -from sysconfig import get_python_version, get_platform +import sys, os from distutils.core import Command from distutils.dir_util import remove_tree +from distutils.sysconfig import get_python_version from distutils.version import StrictVersion from distutils.errors import DistutilsOptionError +from distutils.util import get_platform from distutils import log - import msilib from msilib import schema, sequence, text from msilib import Directory, Feature, Dialog, add_data @@ -28,6 +28,7 @@ class PyDialog(Dialog): default, cancel, bitmap=true)""" Dialog.__init__(self, *args) ruler = self.h - 36 + bmwidth = 152*ruler/328 #if kw.get("bitmap", True): # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin") self.line("BottomLine", 0, ruler, self.w, 0) @@ -418,6 +419,7 @@ class bdist_msi(Command): # see "Dialog Style Bits" modal = 3 # visible | modal modeless = 1 # visible + track_disk_space = 32 # UI customization properties add_data(db, "Property", diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 0a579dbb..452f9502 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -5,14 +5,13 @@ distributions).""" __revision__ = "$Id$" -import sys -import os - +import sys, os from distutils.core import Command from distutils.debug import DEBUG +from distutils.util import get_platform from distutils.file_util import write_file -from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, - DistutilsFileError, DistutilsExecError) +from distutils.errors import * +from distutils.sysconfig import get_python_version from distutils import log class bdist_rpm(Command): diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 71cc7988..d6d01c63 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -5,16 +5,13 @@ exe-program.""" __revision__ = "$Id$" -import sys -import os - -from sysconfig import get_python_version - +import sys, os from distutils.core import Command -from distutils.dir_util import remove_tree -from distutils.errors import DistutilsOptionError, DistutilsPlatformError -from distutils import log from distutils.util import get_platform +from distutils.dir_util import create_tree, remove_tree +from distutils.errors import * +from distutils.sysconfig import get_python_version +from distutils import log class bdist_wininst(Command): diff --git a/command/build.py b/command/build.py index 4d30f8ff..9c2667cf 100644 --- a/command/build.py +++ b/command/build.py @@ -5,15 +5,16 @@ Implements the Distutils 'build' command.""" __revision__ = "$Id$" import sys, os - -from distutils.util import get_platform from distutils.core import Command from distutils.errors import DistutilsOptionError +from distutils.util import get_platform + def show_compilers(): from distutils.ccompiler import show_compilers show_compilers() + class build(Command): description = "build everything needed to install" @@ -126,6 +127,7 @@ class build(Command): for cmd_name in self.get_sub_commands(): self.run_command(cmd_name) + # -- Predicates for the sub-command list --------------------------- def has_pure_modules(self): @@ -140,6 +142,7 @@ class build(Command): def has_scripts(self): return self.distribution.has_scripts() + sub_commands = [('build_py', has_pure_modules), ('build_clib', has_c_libraries), ('build_ext', has_ext_modules), diff --git a/command/build_clib.py b/command/build_clib.py index 4c6443ca..258d7c10 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -18,8 +18,8 @@ __revision__ = "$Id$" import os from distutils.core import Command -from distutils.errors import DistutilsSetupError -from distutils.ccompiler import customize_compiler +from distutils.errors import * +from distutils.sysconfig import customize_compiler from distutils import log def show_compilers(): diff --git a/command/build_ext.py b/command/build_ext.py index 8f41facd..bd61bc56 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -7,14 +7,12 @@ extensions ASAP).""" __revision__ = "$Id$" import sys, os, re -from warnings import warn - -from distutils.util import get_platform from distutils.core import Command from distutils.errors import * -from distutils.ccompiler import customize_compiler +from distutils.sysconfig import customize_compiler, get_python_version from distutils.dep_util import newer_group from distutils.extension import Extension +from distutils.util import get_platform from distutils import log # this keeps compatibility from 2.3 to 2.5 @@ -114,39 +112,6 @@ class build_ext(Command): "list available compilers", show_compilers), ] - # making 'compiler' a property to deprecate - # its usage as something else than a compiler type - # e.g. like a compiler instance - def __init__(self, dist): - self._compiler = None - Command.__init__(self, dist) - - def __setattr__(self, name, value): - # need this to make sure setattr() (used in distutils) - # doesn't kill our property - if name == 'compiler': - self._set_compiler(value) - else: - self.__dict__[name] = value - - def _set_compiler(self, compiler): - if not isinstance(compiler, str) and compiler is not None: - # we don't want to allow that anymore in the future - warn("'compiler' specifies the compiler type in build_ext. " - "If you want to get the compiler object itself, " - "use 'compiler_obj'", DeprecationWarning) - self._compiler = compiler - - def _get_compiler(self): - if not isinstance(self._compiler, str) and self._compiler is not None: - # we don't want to allow that anymore in the future - warn("'compiler' specifies the compiler type in build_ext. " - "If you want to get the compiler object itself, " - "use 'compiler_obj'", DeprecationWarning) - return self._compiler - - compiler = property(_get_compiler, _set_compiler) - def initialize_options(self): self.extensions = None self.build_lib = None @@ -171,7 +136,8 @@ class build_ext(Command): self.user = None def finalize_options(self): - _sysconfig = __import__('sysconfig') + from distutils import sysconfig + self.set_undefined_options('build', ('build_lib', 'build_lib'), ('build_temp', 'build_temp'), @@ -188,8 +154,8 @@ class build_ext(Command): # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. - py_include = _sysconfig.get_path('include') - plat_py_include = _sysconfig.get_path('platinclude') + py_include = sysconfig.get_python_inc() + plat_py_include = sysconfig.get_python_inc(plat_specific=1) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if isinstance(self.include_dirs, str): @@ -261,13 +227,13 @@ class build_ext(Command): if os.name == 'os2': self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) - # for extensions under Cygwin Python's library directory must be + # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs - if sys.platform[:6] == 'cygwin': + if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", - "python" + _sysconfig.get_python_version(), + "python" + get_python_version(), "config")) else: # building python standard extensions @@ -275,13 +241,13 @@ class build_ext(Command): # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs - _sysconfig.get_config_var('Py_ENABLE_SHARED') + sysconfig.get_config_var('Py_ENABLE_SHARED') if ((sys.platform.startswith('linux') or sys.platform.startswith('gnu') or sys.platform.startswith('sunos')) - and _sysconfig.get_config_var('Py_ENABLE_SHARED')): + and sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions - self.library_dirs.append(_sysconfig.get_config_var('LIBDIR')) + self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: # building python standard extensions self.library_dirs.append('.') @@ -344,50 +310,38 @@ class build_ext(Command): # Setup the CCompiler object that we'll use to do all the # compiling and linking - - # used to prevent the usage of an existing compiler for the - # compiler option when calling new_compiler() - # this will be removed in 3.3 and 2.8 - if not isinstance(self._compiler, str): - self._compiler = None - - self.compiler_obj = new_compiler(compiler=self._compiler, - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force) - - # used to keep the compiler object reachable with - # "self.compiler". this will be removed in 3.3 and 2.8 - self._compiler = self.compiler_obj - - customize_compiler(self.compiler_obj) + self.compiler = new_compiler(compiler=self.compiler, + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) + customize_compiler(self.compiler) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): - self.compiler_obj.initialize(self.plat_name) + self.compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: - self.compiler_obj.set_include_dirs(self.include_dirs) + self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: - self.compiler_obj.define_macro(name, value) + self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: - self.compiler_obj.undefine_macro(macro) + self.compiler.undefine_macro(macro) if self.libraries is not None: - self.compiler_obj.set_libraries(self.libraries) + self.compiler.set_libraries(self.libraries) if self.library_dirs is not None: - self.compiler_obj.set_library_dirs(self.library_dirs) + self.compiler.set_library_dirs(self.library_dirs) if self.rpath is not None: - self.compiler_obj.set_runtime_library_dirs(self.rpath) + self.compiler.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: - self.compiler_obj.set_link_objects(self.link_objects) + self.compiler.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() @@ -548,13 +502,13 @@ class build_ext(Command): for undef in ext.undef_macros: macros.append((undef,)) - objects = self.compiler_obj.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args, - depends=ext.depends) + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends) # XXX -- this is a Vile HACK! # @@ -575,9 +529,9 @@ class build_ext(Command): extra_args = ext.extra_link_args or [] # Detect target language, if not provided - language = ext.language or self.compiler_obj.detect_language(sources) + language = ext.language or self.compiler.detect_language(sources) - self.compiler_obj.link_shared_object( + self.compiler.link_shared_object( objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, @@ -710,13 +664,13 @@ class build_ext(Command): of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ - _sysconfig = __import__('sysconfig') + from distutils.sysconfig import get_config_var ext_path = ext_name.split('.') # OS/2 has an 8 character module (extension) limit :-( if os.name == "os2": ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] # extensions in debug_mode are named 'module_d.pyd' under windows - so_ext = _sysconfig.get_config_var('SO') + so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: return os.path.join(*ext_path) + '_d' + so_ext return os.path.join(*ext_path) + so_ext @@ -744,7 +698,7 @@ class build_ext(Command): # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils.msvccompiler import MSVCCompiler - if not isinstance(self.compiler_obj, MSVCCompiler): + if not isinstance(self.compiler, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' @@ -775,12 +729,28 @@ class build_ext(Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] + elif sys.platform[:6] == "atheos": + from distutils import sysconfig + + template = "python%d.%d" + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # Get SHLIBS from Makefile + extra = [] + for lib in sysconfig.get_config_var('SHLIBS').split(): + if lib.startswith('-l'): + extra.append(lib[2:]) + else: + extra.append(lib) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib, "m"] + extra elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries else: - _sysconfig = __import__('sysconfig') - if _sysconfig.get_config_var('Py_ENABLE_SHARED'): + from distutils import sysconfig + if sysconfig.get_config_var('Py_ENABLE_SHARED'): template = "python%d.%d" pythonlib = (template % (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) diff --git a/command/build_py.py b/command/build_py.py index 7cc93539..26002e4b 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -4,16 +4,16 @@ Implements the Distutils 'build_py' command.""" __revision__ = "$Id$" -import os +import sys, os import sys from glob import glob from distutils.core import Command -from distutils.errors import DistutilsOptionError, DistutilsFileError +from distutils.errors import * from distutils.util import convert_path, Mixin2to3 from distutils import log -class build_py(Command): +class build_py (Command): description = "\"build\" pure Python modules (copy to build directory)" @@ -133,6 +133,7 @@ class build_py(Command): def build_package_data(self): """Copy data files into build directory""" + lastdir = None for package, src_dir, build_dir, filenames in self.data_files: for filename in filenames: target = os.path.join(build_dir, filename) diff --git a/command/build_scripts.py b/command/build_scripts.py index a54d6ed7..8b08bfea 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -6,6 +6,7 @@ __revision__ = "$Id$" import os, re from stat import ST_MODE +from distutils import sysconfig from distutils.core import Command from distutils.dep_util import newer from distutils.util import convert_path, Mixin2to3 @@ -56,7 +57,6 @@ class build_scripts(Command): ie. starts with "\#!" and contains "python"), then adjust the first line to refer to the current Python interpreter as we copy. """ - _sysconfig = __import__('sysconfig') self.mkpath(self.build_dir) outfiles = [] updated_files = [] @@ -96,16 +96,16 @@ class build_scripts(Command): updated_files.append(outfile) if not self.dry_run: outf = open(outfile, "w") - if not _sysconfig.is_python_build(): + if not sysconfig.python_build: outf.write("#!%s%s\n" % (self.executable, post_interp)) else: outf.write("#!%s%s\n" % (os.path.join( - _sysconfig.get_config_var("BINDIR"), - "python%s%s" % (_sysconfig.get_config_var("VERSION"), - _sysconfig.get_config_var("EXE"))), + sysconfig.get_config_var("BINDIR"), + "python%s%s" % (sysconfig.get_config_var("VERSION"), + sysconfig.get_config_var("EXE"))), post_interp)) outf.writelines(f.readlines()) outf.close() diff --git a/command/config.py b/command/config.py index 56f643c8..ac80a54e 100644 --- a/command/config.py +++ b/command/config.py @@ -11,12 +11,11 @@ this header file lives". __revision__ = "$Id$" -import os -import re +import sys, os, re from distutils.core import Command from distutils.errors import DistutilsExecError -from distutils.ccompiler import customize_compiler +from distutils.sysconfig import customize_compiler from distutils import log LANG_EXT = {"c": ".c", "c++": ".cxx"} diff --git a/command/install.py b/command/install.py index e3e387ab..2a905d92 100644 --- a/command/install.py +++ b/command/install.py @@ -7,17 +7,26 @@ __revision__ = "$Id$" import sys import os -from sysconfig import get_config_vars, get_paths, get_path, get_config_var - from distutils import log from distutils.core import Command from distutils.debug import DEBUG +from distutils.sysconfig import get_config_vars from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file -from distutils.util import convert_path, change_root, get_platform +from distutils.util import convert_path, subst_vars, change_root +from distutils.util import get_platform from distutils.errors import DistutilsOptionError -# kept for backward compat, will be removed in 3.2 +# this keeps compatibility from 2.3 to 2.5 +if sys.version < "2.6": + USER_BASE = None + USER_SITE = None + HAS_USER_SITE = False +else: + from site import USER_BASE + from site import USER_SITE + HAS_USER_SITE = True + if sys.version < "2.2": WINDOWS_SCHEME = { 'purelib': '$base', @@ -50,21 +59,15 @@ INSTALL_SCHEMES = { 'scripts': '$base/bin', 'data' : '$base', }, - 'unix_user': { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/include/python$py_version_short/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - }, 'nt': WINDOWS_SCHEME, - 'nt_user': { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', - 'scripts': '$userbase/Scripts', - 'data' : '$userbase', + 'mac': { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', }, + 'os2': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', @@ -72,26 +75,47 @@ INSTALL_SCHEMES = { 'scripts': '$base/Scripts', 'data' : '$base', }, - 'os2_home': { + } + +# user site schemes +if HAS_USER_SITE: + INSTALL_SCHEMES['nt_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', + 'scripts': '$userbase/Scripts', + 'data' : '$userbase', + } + + INSTALL_SCHEMES['unix_user'] = { 'purelib': '$usersite', 'platlib': '$usersite', 'headers': '$userbase/include/python$py_version_short/$dist_name', 'scripts': '$userbase/bin', 'data' : '$userbase', - }, - } + } + + INSTALL_SCHEMES['mac_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/$py_version_short/include/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + } + INSTALL_SCHEMES['os2_home'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + } + +# The keys to an installation scheme; if any new types of files are to be +# installed, be sure to add an entry to every installation scheme above, +# and to SCHEME_KEYS here. SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') -# end of backward compat -def _subst_vars(s, local_vars): - try: - return s.format(**local_vars) - except KeyError: - try: - return s.format(**os.environ) - except KeyError as var: - raise AttributeError('{%s}' % var) class install(Command): @@ -158,10 +182,11 @@ class install(Command): boolean_options = ['compile', 'force', 'skip-build'] - user_options.append(('user', None, - "install in user site-package '%s'" % \ - get_path('purelib', '%s_user' % os.name))) - boolean_options.append('user') + if HAS_USER_SITE: + user_options.append(('user', None, + "install in user site-package '%s'" % USER_SITE)) + boolean_options.append('user') + negative_opt = {'no-compile' : 'compile'} @@ -191,8 +216,8 @@ class install(Command): self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None - self.install_userbase = get_config_var('userbase') - self.install_usersite = get_path('purelib', '%s_user' % os.name) + self.install_userbase = USER_BASE + self.install_usersite = USER_SITE self.compile = None self.optimize = None @@ -302,9 +327,7 @@ class install(Command): # about needing recursive variable expansion (shudder). py_version = sys.version.split()[0] - prefix, exec_prefix, srcdir, projectbase = get_config_vars('prefix', 'exec_prefix', - 'srcdir', 'projectbase') - + (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), 'dist_fullname': self.distribution.get_fullname(), @@ -315,12 +338,12 @@ class install(Command): 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, - 'srcdir': srcdir, - 'projectbase': projectbase, } - self.config_vars['userbase'] = self.install_userbase - self.config_vars['usersite'] = self.install_usersite + if HAS_USER_SITE: + self.config_vars['userbase'] = self.install_userbase + self.config_vars['usersite'] = self.install_usersite + self.expand_basedirs() self.dump_dirs("post-expand_basedirs()") @@ -424,10 +447,10 @@ class install(Command): raise DistutilsPlatformError( "User base directory is not specified") self.install_base = self.install_platbase = self.install_userbase - self.select_scheme("posix_user") + self.select_scheme("unix_user") elif self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme("posix_home") + self.select_scheme("unix_home") else: if self.prefix is None: if self.exec_prefix is not None: @@ -443,7 +466,7 @@ class install(Command): self.install_base = self.prefix self.install_platbase = self.exec_prefix - self.select_scheme("posix_prefix") + self.select_scheme("unix_prefix") def finalize_other(self): """Finalizes options for non-posix platforms""" @@ -455,7 +478,7 @@ class install(Command): self.select_scheme(os.name + "_user") elif self.home is not None: self.install_base = self.install_platbase = self.home - self.select_scheme("posix_home") + self.select_scheme("unix_home") else: if self.prefix is None: self.prefix = os.path.normpath(sys.prefix) @@ -470,15 +493,11 @@ class install(Command): def select_scheme(self, name): """Sets the install directories by applying the install schemes.""" # it's the caller's problem if they supply a bad name! - scheme = get_paths(name, expand=False) - for key, value in scheme.items(): - if key == 'platinclude': - key = 'headers' - value = os.path.join(value, self.distribution.get_name()) + scheme = INSTALL_SCHEMES[name] + for key in SCHEME_KEYS: attrname = 'install_' + key - if hasattr(self, attrname): - if getattr(self, attrname) is None: - setattr(self, attrname, value) + if getattr(self, attrname) is None: + setattr(self, attrname, scheme[key]) def _expand_attrs(self, attrs): for attr in attrs: @@ -486,7 +505,7 @@ class install(Command): if val is not None: if os.name == 'posix' or os.name == 'nt': val = os.path.expanduser(val) - val = _subst_vars(val, self.config_vars) + val = subst_vars(val, self.config_vars) setattr(self, attr, val) def expand_basedirs(self): diff --git a/command/register.py b/command/register.py index 7d3dc53a..bdf5f8f0 100644 --- a/command/register.py +++ b/command/register.py @@ -7,15 +7,13 @@ Implements the Distutils 'register' command (register with the repository). __revision__ = "$Id$" -import os -import string -import getpass +import os, string, getpass import io -import urllib.parse -import urllib.request +import urllib.parse, urllib.request from warnings import warn from distutils.core import PyPIRCCommand +from distutils.errors import * from distutils import log class register(PyPIRCCommand): diff --git a/command/sdist.py b/command/sdist.py index f6e099b8..bb210612 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -7,14 +7,14 @@ __revision__ = "$Id$" import os import string import sys +from types import * from glob import glob from warnings import warn from distutils.core import Command from distutils import dir_util, dep_util, file_util, archive_util from distutils.text_file import TextFile -from distutils.errors import (DistutilsPlatformError, DistutilsOptionError, - DistutilsTemplateError) +from distutils.errors import * from distutils.filelist import FileList from distutils import log from distutils.util import convert_path @@ -76,10 +76,6 @@ class sdist(Command): ('medata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), - ('owner=', 'u', - "Owner name used when creating a tar file [default: current user]"), - ('group=', 'g', - "Group name used when creating a tar file [default: current group]"), ] boolean_options = ['use-defaults', 'prune', @@ -119,8 +115,6 @@ class sdist(Command): self.archive_files = None self.metadata_check = 1 - self.owner = None - self.group = None def finalize_options(self): if self.manifest is None: @@ -424,8 +418,7 @@ class sdist(Command): self.formats.append(self.formats.pop(self.formats.index('tar'))) for fmt in self.formats: - file = self.make_archive(base_name, fmt, base_dir=base_dir, - owner=self.owner, group=self.group) + file = self.make_archive(base_name, fmt, base_dir=base_dir) archive_files.append(file) self.distribution.dist_files.append(('sdist', '', file)) diff --git a/command/upload.py b/command/upload.py index bb1b7fc4..f602fbeb 100644 --- a/command/upload.py +++ b/command/upload.py @@ -1,19 +1,25 @@ """distutils.command.upload Implements the Distutils 'upload' subcommand (upload package to PyPI).""" -import os -import io -import socket -import platform -from urllib.request import urlopen, Request, HTTPError -from base64 import standard_b64encode -from urllib.parse import urlparse -from hashlib import md5 -from distutils.errors import DistutilsOptionError +from distutils.errors import * from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log +import sys +import os, io +import socket +import platform +import configparser +import http.client as httpclient +from base64 import standard_b64encode +import urllib.parse + +# this keeps compatibility for 2.3 and 2.4 +if sys.version < "2.5": + from md5 import md5 +else: + from hashlib import md5 class upload(PyPIRCCommand): @@ -60,15 +66,6 @@ class upload(PyPIRCCommand): self.upload_file(command, pyversion, filename) def upload_file(self, command, pyversion, filename): - # Makes sure the repository URL is compliant - schema, netloc, url, params, query, fragments = \ - urlparse(self.repository) - if params or query or fragments: - raise AssertionError("Incompatible url %s" % self.repository) - - if schema not in ('http', 'https'): - raise AssertionError("unsupported schema " + schema) - # Sign if requested if self.sign: gpg_args = ["gpg", "--detach-sign", "-a", filename] @@ -140,10 +137,10 @@ class upload(PyPIRCCommand): for key, value in data.items(): title = '\nContent-Disposition: form-data; name="%s"' % key # handle multiple entries for the same name - if not isinstance(value, list): + if type(value) != type([]): value = [value] for value in value: - if isinstance(value, tuple): + if type(value) is tuple: title += '; filename="%s"' % value[0] value = value[1] else: @@ -161,30 +158,40 @@ class upload(PyPIRCCommand): self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) # build the Request - headers = {'Content-type': - 'multipart/form-data; boundary=%s' % boundary, - 'Content-length': str(len(body)), - 'Authorization': auth} - - request = Request(self.repository, data=body, - headers=headers) - # send the data + # We can't use urllib since we need to send the Basic + # auth right with the first request + # TODO(jhylton): Can we fix urllib? + schema, netloc, url, params, query, fragments = \ + urllib.parse.urlparse(self.repository) + assert not params and not query and not fragments + if schema == 'http': + http = httpclient.HTTPConnection(netloc) + elif schema == 'https': + http = httpclient.HTTPSConnection(netloc) + else: + raise AssertionError("unsupported schema "+schema) + + data = '' + loglevel = log.INFO try: - result = urlopen(request) - status = result.getcode() - reason = result.msg + http.connect() + http.putrequest("POST", url) + http.putheader('Content-type', + 'multipart/form-data; boundary=%s'%boundary) + http.putheader('Content-length', str(len(body))) + http.putheader('Authorization', auth) + http.endheaders() + http.send(body) except socket.error as e: self.announce(str(e), log.ERROR) return - except HTTPError as e: - status = e.code - reason = e.msg - if status == 200: - self.announce('Server response (%s): %s' % (status, reason), + r = http.getresponse() + if r.status == 200: + self.announce('Server response (%s): %s' % (r.status, r.reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (status, reason), + self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - self.announce('-'*75, result.read(), '-'*75) + self.announce('-'*75, r.read(), '-'*75) diff --git a/config.py b/config.py index fe41ce97..5b625f3f 100644 --- a/config.py +++ b/config.py @@ -4,6 +4,7 @@ Provides the PyPIRCCommand class, the base class for the command classes that uses .pypirc in the distutils.command package. """ import os +import sys from configparser import ConfigParser from distutils.cmd import Command @@ -59,6 +60,8 @@ class PyPIRCCommand(Command): if os.path.exists(rc): self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY + realm = self.realm or self.DEFAULT_REALM + config = ConfigParser() config.read(rc) sections = config.sections() diff --git a/core.py b/core.py index 6ed3e8fa..6e489203 100644 --- a/core.py +++ b/core.py @@ -8,12 +8,10 @@ really defined in distutils.dist and distutils.cmd. __revision__ = "$Id$" -import sys -import os +import sys, os from distutils.debug import DEBUG -from distutils.errors import (DistutilsSetupError, DistutilsArgError, - DistutilsError, CCompilerError) +from distutils.errors import * from distutils.util import grok_environment_error # Mainly import these so setup scripts can "from distutils.core import" them. @@ -33,9 +31,9 @@ usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: %(script)s cmd --help """ -def gen_usage(script_name): +def gen_usage (script_name): script = os.path.basename(script_name) - return USAGE % {'script': script} + return USAGE % vars() # Some mild magic to control the behaviour of 'setup()' from 'run_setup()'. @@ -58,7 +56,7 @@ extension_keywords = ('name', 'sources', 'include_dirs', 'extra_objects', 'extra_compile_args', 'extra_link_args', 'swig_opts', 'export_symbols', 'depends', 'language') -def setup(**attrs): +def setup (**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a Distribution instance; find and parse config files; parse the command @@ -131,9 +129,8 @@ def setup(**attrs): if _setup_stop_after == "config": return dist - # Parse the command line and override config files; any - # command-line errors are the end user's fault, so turn them into - # SystemExit to suppress tracebacks. + # Parse the command line; any command-line errors are the end user's + # fault, so turn them into SystemExit to suppress tracebacks. try: ok = dist.parse_command_line() except DistutilsArgError as msg: @@ -170,8 +167,10 @@ def setup(**attrs): return dist +# setup () -def run_setup(script_name, script_args=None, stop_after="run"): + +def run_setup (script_name, script_args=None, stop_after="run"): """Run a setup script in a somewhat controlled environment, and return the Distribution instance that drives things. This is useful if you need to find out the distribution meta-data (passed as @@ -234,4 +233,7 @@ def run_setup(script_name, script_args=None, stop_after="run"): # I wonder if the setup script's namespace -- g and l -- would be of # any interest to callers? + #print "_setup_distribution:", _setup_distribution return _setup_distribution + +# run_setup () diff --git a/cygwinccompiler.py b/cygwinccompiler.py index f381f608..85043718 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -50,13 +50,16 @@ __revision__ = "$Id$" import os import sys import copy +from subprocess import Popen, PIPE import re -from warnings import warn +from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils.util import get_compiler_versions +from distutils import log +from distutils.version import LooseVersion +from distutils.spawn import find_executable def get_msvcr(): """Include the appropriate MSVC runtime library if Python was built @@ -107,7 +110,7 @@ class CygwinCCompiler(UnixCCompiler): % details) self.gcc_version, self.ld_version, self.dllwrap_version = \ - get_compiler_versions() + get_versions() self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % (self.gcc_version, self.ld_version, @@ -337,7 +340,7 @@ def check_config_h(): # XXX since this function also checks sys.version, it's not strictly a # "pyconfig.h" check -- should probably be renamed... - _sysconfig = __import__('sysconfig') + from distutils import sysconfig # if sys.version contains GCC then python was compiled with GCC, and the # pyconfig.h file should be OK @@ -345,7 +348,7 @@ def check_config_h(): return CONFIG_H_OK, "sys.version mentions 'GCC'" # let's see if __GNUC__ is mentioned in python.h - fn = _sysconfig.get_config_h_filename() + fn = sysconfig.get_config_h_filename() try: with open(fn) as config_h: if "__GNUC__" in config_h.read(): @@ -356,27 +359,33 @@ def check_config_h(): return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) -class _Deprecated_SRE_Pattern(object): - def __init__(self, pattern): - self.pattern = pattern +RE_VERSION = re.compile(b'(\d+\.\d+(\.\d+)*)') - def __getattr__(self, name): - if name in ('findall', 'finditer', 'match', 'scanner', 'search', - 'split', 'sub', 'subn'): - warn("'distutils.cygwinccompiler.RE_VERSION' is deprecated " - "and will be removed in the next version", DeprecationWarning) - return getattr(self.pattern, name) +def _find_exe_version(cmd): + """Find the version of an executable by running `cmd` in the shell. - -RE_VERSION = _Deprecated_SRE_Pattern(re.compile('(\d+\.\d+(\.\d+)*)')) + If the command is not found, or the output does not match + `RE_VERSION`, returns None. + """ + executable = cmd.split()[0] + if find_executable(executable) is None: + return None + out = Popen(cmd, shell=True, stdout=PIPE).stdout + try: + out_string = out.read() + finally: + out.close() + result = RE_VERSION.search(out_string) + if result is None: + return None + # LooseVersion works with strings + # so we need to decode our bytes + return LooseVersion(result.group(1).decode()) def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. If not possible it returns None for it. """ - warn("'distutils.cygwinccompiler.get_versions' is deprecated " - "use 'distutils.util.get_compiler_versions' instead", - DeprecationWarning) - - return get_compiler_versions() + commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] + return tuple([_find_exe_version(cmd) for cmd in commands]) diff --git a/dep_util.py b/dep_util.py index b91a62eb..07b3549c 100644 --- a/dep_util.py +++ b/dep_util.py @@ -9,27 +9,29 @@ __revision__ = "$Id$" import os from distutils.errors import DistutilsFileError -def newer(source, target): - """Tells if the target is newer than the source. - Return true if 'source' exists and is more recently modified than - 'target', or if 'source' exists and 'target' doesn't. - - Return false if both exist and 'target' is the same age or younger - than 'source'. Raise DistutilsFileError if 'source' does not exist. - - Note that this test is not very accurate: files created in the same second - will have the same "age". +def newer (source, target): + """Return true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. Return false if + both exist and 'target' is the same age or younger than 'source'. + Raise DistutilsFileError if 'source' does not exist. """ if not os.path.exists(source): raise DistutilsFileError("file '%s' does not exist" % os.path.abspath(source)) if not os.path.exists(target): - return True + return 1 + + from stat import ST_MTIME + mtime1 = os.stat(source)[ST_MTIME] + mtime2 = os.stat(target)[ST_MTIME] + + return mtime1 > mtime2 + +# newer () - return os.stat(source).st_mtime > os.stat(target).st_mtime -def newer_pairwise(sources, targets): +def newer_pairwise (sources, targets): """Walk two filename lists in parallel, testing if each source is newer than its corresponding target. Return a pair of lists (sources, targets) where source is newer than target, according to the semantics @@ -41,18 +43,19 @@ def newer_pairwise(sources, targets): # build a pair of lists (sources, targets) where source is newer n_sources = [] n_targets = [] - for source, target in zip(sources, targets): - if newer(source, target): - n_sources.append(source) - n_targets.append(target) + for i in range(len(sources)): + if newer(sources[i], targets[i]): + n_sources.append(sources[i]) + n_targets.append(targets[i]) - return n_sources, n_targets + return (n_sources, n_targets) + +# newer_pairwise () -def newer_group(sources, target, missing='error'): - """Return true if 'target' is out-of-date with respect to any file - listed in 'sources'. - In other words, if 'target' exists and is newer +def newer_group (sources, target, missing='error'): + """Return true if 'target' is out-of-date with respect to any file + listed in 'sources'. In other words, if 'target' exists and is newer than every file in 'sources', return false; otherwise return true. 'missing' controls what we do when a source file is missing; the default ("error") is to blow up with an OSError from inside 'stat()'; @@ -65,14 +68,14 @@ def newer_group(sources, target, missing='error'): """ # If the target doesn't even exist, then it's definitely out-of-date. if not os.path.exists(target): - return True + return 1 # Otherwise we have to find out the hard way: if *any* source file # is more recent than 'target', then 'target' is out-of-date and # we can immediately return true. If we fall through to the end # of the loop, then 'target' is up-to-date and we return false. - target_mtime = os.stat(target).st_mtime - + from stat import ST_MTIME + target_mtime = os.stat(target)[ST_MTIME] for source in sources: if not os.path.exists(source): if missing == 'error': # blow up when we stat() the file @@ -80,9 +83,12 @@ def newer_group(sources, target, missing='error'): elif missing == 'ignore': # missing source dropped from continue # target's dependency list elif missing == 'newer': # missing source means target is - return True # out-of-date + return 1 # out-of-date - if os.stat(source).st_mtime > target_mtime: - return True + source_mtime = os.stat(source)[ST_MTIME] + if source_mtime > target_mtime: + return 1 + else: + return 0 - return False +# newer_group () diff --git a/dir_util.py b/dir_util.py index 370025b7..98e6252c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -4,7 +4,7 @@ Utility functions for manipulating directories and directory trees.""" __revision__ = "$Id$" -import os +import os, sys from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log diff --git a/dist.py b/dist.py index 5a107e7d..1c1ea477 100644 --- a/dist.py +++ b/dist.py @@ -7,15 +7,13 @@ being built/installed/distributed. __revision__ = "$Id$" import sys, os, re -from email import message_from_file try: import warnings except ImportError: warnings = None -from distutils.errors import (DistutilsOptionError, DistutilsArgError, - DistutilsModuleError, DistutilsClassError) +from distutils.errors import * from distutils.fancy_getopt import FancyGetopt, translate_longopt from distutils.util import check_environ, strtobool, rfc822_escape from distutils import log @@ -55,9 +53,7 @@ class Distribution: ('quiet', 'q', "run quietly (turns verbosity off)"), ('dry-run', 'n', "don't actually do anything"), ('help', 'h', "show detailed help message"), - ('no-user-cfg', None, - 'ignore pydistutils.cfg in your home directory'), - ] + ] # 'common_usage' is a short (2-3 line) string describing the common # usage of the setup script. @@ -264,22 +260,6 @@ Common commands: (see '--help-commands' for more) else: sys.stderr.write(msg + "\n") - # no-user-cfg is handled before other command line args - # because other args override the config files, and this - # one is needed before we can load the config files. - # If attrs['script_args'] wasn't passed, assume false. - # - # This also make sure we just look at the global options - self.want_user_cfg = True - - if self.script_args is not None: - for arg in self.script_args: - if not arg.startswith('-'): - break - if arg == '--no-user-cfg': - self.want_user_cfg = False - break - self.finalize_options() def get_option_dict(self, command): @@ -331,10 +311,7 @@ Common commands: (see '--help-commands' for more) Distutils installation directory (ie. where the top-level Distutils __inst__.py file lives), a file in the user's home directory named .pydistutils.cfg on Unix and pydistutils.cfg - on Windows/Mac; and setup.cfg in the current directory. - - The file in the user's home directory can be disabled with the - --no-user-cfg option. + on Windows/Mac, and setup.cfg in the current directory. """ files = [] check_environ() @@ -354,19 +331,15 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - if self.want_user_cfg: - user_file = os.path.join(os.path.expanduser('~'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) + user_file = os.path.join(os.path.expanduser('~'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" if os.path.isfile(local_file): files.append(local_file) - if DEBUG: - self.announce("using config files: %s" % ', '.join(files)) - return files def parse_config_files(self, filenames=None): @@ -1016,80 +989,25 @@ class DistributionMetadata: "provides", "requires", "obsoletes", ) - def __init__(self, path=None): - if path is not None: - self.read_pkg_file(open(path)) - else: - self.name = None - self.version = None - self.author = None - self.author_email = None - self.maintainer = None - self.maintainer_email = None - self.url = None - self.license = None - self.description = None - self.long_description = None - self.keywords = None - self.platforms = None - self.classifiers = None - self.download_url = None - # PEP 314 - self.provides = None - self.requires = None - self.obsoletes = None - - def read_pkg_file(self, file): - """Reads the metadata values from a file object.""" - msg = message_from_file(file) - - def _read_field(name): - value = msg[name] - if value == 'UNKNOWN': - return None - return value - - def _read_list(name): - values = msg.get_all(name, None) - if values == []: - return None - return values - - metadata_version = msg['metadata-version'] - self.name = _read_field('name') - self.version = _read_field('version') - self.description = _read_field('summary') - # we are filling author only. - self.author = _read_field('author') + def __init__ (self): + self.name = None + self.version = None + self.author = None + self.author_email = None self.maintainer = None - self.author_email = _read_field('author-email') self.maintainer_email = None - self.url = _read_field('home-page') - self.license = _read_field('license') - - if 'download-url' in msg: - self.download_url = _read_field('download-url') - else: - self.download_url = None - - self.long_description = _read_field('description') - self.description = _read_field('summary') - - if 'keywords' in msg: - self.keywords = _read_field('keywords').split(',') - - self.platforms = _read_list('platform') - self.classifiers = _read_list('classifier') - - # PEP 314 - these fields only exist in 1.1 - if metadata_version == '1.1': - self.requires = _read_list('requires') - self.provides = _read_list('provides') - self.obsoletes = _read_list('obsoletes') - else: - self.requires = None - self.provides = None - self.obsoletes = None + self.url = None + self.license = None + self.description = None + self.long_description = None + self.keywords = None + self.platforms = None + self.classifiers = None + self.download_url = None + # PEP 314 + self.provides = None + self.requires = None + self.obsoletes = None def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. diff --git a/emxccompiler.py b/emxccompiler.py index fd79aec2..62a4c5b4 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -21,13 +21,12 @@ handles the EMX port of the GNU C compiler to OS/2. __revision__ = "$Id$" -import os, sys, copy -from warnings import warn - +import os,sys,copy +from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils.util import get_compiler_versions +from distutils import log class EMXCCompiler (UnixCCompiler): @@ -56,8 +55,8 @@ class EMXCCompiler (UnixCCompiler): ("Reason: %s." % details) + "Compiling may fail because of undefined preprocessor macros.") - gcc_version, ld_version, dllwrap_version = get_compiler_versions() - self.gcc_version, self.ld_version = gcc_version, ld_version + (self.gcc_version, self.ld_version) = \ + get_versions() self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" % (self.gcc_version, self.ld_version) ) @@ -292,11 +291,23 @@ def get_versions(): """ Try to find out the versions of gcc and ld. If not possible it returns None for it. """ - warn("'distutils.emxccompiler.get_versions' is deprecated " - "use 'distutils.util.get_compiler_versions' instead", - DeprecationWarning) - + from distutils.version import StrictVersion + from distutils.spawn import find_executable + import re + + gcc_exe = find_executable('gcc') + if gcc_exe: + out = os.popen(gcc_exe + ' -dumpversion','r') + out_string = out.read() + out.close() + result = re.search('(\d+\.\d+\.\d+)', out_string, re.ASCII) + if result: + gcc_version = StrictVersion(result.group(1)) + else: + gcc_version = None + else: + gcc_version = None # EMX ld has no way of reporting version number, and we use GCC # anyway - so we can link OMF DLLs - gcc_version, ld_version, dllwrap_version = get_compiler_versions() - return gcc_version, None + ld_version = None + return (gcc_version, ld_version) diff --git a/errors.py b/errors.py index d9c47c76..acecaccc 100644 --- a/errors.py +++ b/errors.py @@ -10,79 +10,90 @@ symbols whose names start with "Distutils" and end with "Error".""" __revision__ = "$Id$" -class DistutilsError(Exception): +class DistutilsError (Exception): """The root of all Distutils evil.""" + pass -class DistutilsModuleError(DistutilsError): +class DistutilsModuleError (DistutilsError): """Unable to load an expected module, or to find an expected class within some module (in particular, command modules and classes).""" + pass -class DistutilsClassError(DistutilsError): +class DistutilsClassError (DistutilsError): """Some command class (or possibly distribution class, if anyone feels a need to subclass Distribution) is found not to be holding up its end of the bargain, ie. implementing some part of the "command "interface.""" + pass -class DistutilsGetoptError(DistutilsError): +class DistutilsGetoptError (DistutilsError): """The option table provided to 'fancy_getopt()' is bogus.""" + pass -class DistutilsArgError(DistutilsError): +class DistutilsArgError (DistutilsError): """Raised by fancy_getopt in response to getopt.error -- ie. an error in the command line usage.""" + pass -class DistutilsFileError(DistutilsError): +class DistutilsFileError (DistutilsError): """Any problems in the filesystem: expected file not found, etc. Typically this is for problems that we detect before IOError or OSError could be raised.""" + pass -class DistutilsOptionError(DistutilsError): +class DistutilsOptionError (DistutilsError): """Syntactic/semantic errors in command options, such as use of mutually conflicting options, or inconsistent options, badly-spelled values, etc. No distinction is made between option values originating in the setup script, the command line, config files, or what-have-you -- but if we *know* something originated in the setup script, we'll raise DistutilsSetupError instead.""" + pass -class DistutilsSetupError(DistutilsError): +class DistutilsSetupError (DistutilsError): """For errors that can be definitely blamed on the setup script, such as invalid keyword arguments to 'setup()'.""" + pass -class DistutilsPlatformError(DistutilsError): +class DistutilsPlatformError (DistutilsError): """We don't know how to do something on the current platform (but we do know how to do it on some platform) -- eg. trying to compile C files on a platform not supported by a CCompiler subclass.""" + pass -class DistutilsExecError(DistutilsError): +class DistutilsExecError (DistutilsError): """Any problems executing an external program (such as the C compiler, when compiling C files).""" + pass -class DistutilsInternalError(DistutilsError): +class DistutilsInternalError (DistutilsError): """Internal inconsistencies or impossibilities (obviously, this should never be seen if the code is working!).""" + pass -class DistutilsTemplateError(DistutilsError): +class DistutilsTemplateError (DistutilsError): """Syntax error in a file list template.""" class DistutilsByteCompileError(DistutilsError): """Byte compile error.""" # Exception classes used by the CCompiler implementation classes -class CCompilerError(Exception): +class CCompilerError (Exception): """Some compile/link operation failed.""" -class PreprocessError(CCompilerError): +class PreprocessError (CCompilerError): """Failure to preprocess one or more C/C++ files.""" -class CompileError(CCompilerError): +class CompileError (CCompilerError): """Failure to compile one or more C/C++ source files.""" -class LibError(CCompilerError): +class LibError (CCompilerError): """Failure to create a static library from one or more C/C++ object files.""" -class LinkError(CCompilerError): +class LinkError (CCompilerError): """Failure to link one or more C/C++ object files into an executable or shared library file.""" -class UnknownFileError(CCompilerError): +class UnknownFileError (CCompilerError): """Attempt to process an unknown file type.""" diff --git a/extension.py b/extension.py index ebe5437c..5c07bdae 100644 --- a/extension.py +++ b/extension.py @@ -6,6 +6,7 @@ modules in setup scripts.""" __revision__ = "$Id$" import os +import sys import warnings # This class is really only used by the "build_ext" command, so it might @@ -134,17 +135,14 @@ class Extension: def read_setup_file(filename): """Reads a Setup file and returns Extension instances.""" - warnings.warn('distutils.extensions.read_setup_file is deprecated. ' - 'It will be removed in the next Python release.') - _sysconfig = __import__('sysconfig') - from distutils.sysconfig import (expand_makefile_vars, + from distutils.sysconfig import (parse_makefile, expand_makefile_vars, _variable_rx) from distutils.text_file import TextFile from distutils.util import split_quoted # First pass over the file to gather "VAR = VALUE" assignments. - vars = _sysconfig._parse_makefile(filename) + vars = parse_makefile(filename) # Second pass to gobble up the real content: lines of the form # ... [ ...] [ ...] [ ...] @@ -164,10 +162,7 @@ def read_setup_file(filename): file.warn("'%s' lines not handled yet" % line) continue - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - line = expand_makefile_vars(line, vars) - + line = expand_makefile_vars(line, vars) words = split_quoted(line) # NB. this parses a slightly different syntax than the old diff --git a/fancy_getopt.py b/fancy_getopt.py index 73343ad5..879d4d25 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -10,11 +10,9 @@ additional features: __revision__ = "$Id$" -import sys -import string -import re +import sys, string, re import getopt -from distutils.errors import DistutilsGetoptError, DistutilsArgError +from distutils.errors import * # Much like command_re in distutils.core, this is close to but not quite # the same as a Python NAME -- except, in the spirit of most GNU @@ -446,3 +444,16 @@ class OptionDummy: 'options' will be initialized to None.""" for opt in options: setattr(self, opt, None) + + +if __name__ == "__main__": + text = """\ +Tra-la-la, supercalifragilisticexpialidocious. +How *do* you spell that odd word, anyways? +(Someone ask Mary -- she'll know [or she'll +say, "How should I know?"].)""" + + for w in (10, 20, 30, 40): + print("width: %d" % w) + print("\n".join(wrap_text(text, w))) + print() diff --git a/file_util.py b/file_util.py index 3a71bfd1..65aa7e0f 100644 --- a/file_util.py +++ b/file_util.py @@ -10,18 +10,17 @@ from distutils.errors import DistutilsFileError from distutils import log # for generating verbose output in 'copy_file()' -_copy_action = {None: 'copying', - 'hard': 'hard linking', - 'sym': 'symbolically linking'} +_copy_action = { None: 'copying', + 'hard': 'hard linking', + 'sym': 'symbolically linking' } def _copy_file_contents(src, dst, buffer_size=16*1024): - """Copy the file 'src' to 'dst'. - - Both must be filenames. Any error opening either file, reading from - 'src', or writing to 'dst', raises DistutilsFileError. Data is - read/written in chunks of 'buffer_size' bytes (default 16k). No attempt - is made to handle anything apart from regular files. + """Copy the file 'src' to 'dst'; both must be filenames. Any error + opening either file, reading from 'src', or writing to 'dst', raises + DistutilsFileError. Data is read/written in chunks of 'buffer_size' + bytes (default 16k). No attempt is made to handle anything apart from + regular files. """ # Stolen from shutil module in the standard library, but with # custom error-handling added. @@ -69,16 +68,15 @@ def _copy_file_contents(src, dst, buffer_size=16*1024): def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, link=None, verbose=1, dry_run=0): - """Copy a file 'src' to 'dst'. - - If 'dst' is a directory, then 'src' is copied there with the same name; - otherwise, it must be a filename. (If the file exists, it will be - ruthlessly clobbered.) If 'preserve_mode' is true (the default), - the file's mode (type and permission bits, or whatever is analogous on - the current platform) is copied. If 'preserve_times' is true (the - default), the last-modified and last-access times are copied as well. - If 'update' is true, 'src' will only be copied if 'dst' does not exist, - or if 'dst' does exist but is older than 'src'. + """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is + copied there with the same name; otherwise, it must be a filename. (If + the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' + is true (the default), the file's mode (type and permission bits, or + whatever is analogous on the current platform) is copied. If + 'preserve_times' is true (the default), the last-modified and + last-access times are copied as well. If 'update' is true, 'src' will + only be copied if 'dst' does not exist, or if 'dst' does exist but is + older than 'src'. 'link' allows you to make hard links (os.link) or symbolic links (os.symlink) instead of copying: set it to "hard" or "sym"; if it is @@ -132,6 +130,15 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, if dry_run: return (dst, 1) + # On Mac OS, use the native file copy routine + if os.name == 'mac': + import macostools + try: + macostools.copy(src, dst, 0, preserve_times) + except os.error as exc: + raise DistutilsFileError( + "could not copy '%s' to '%s': %s" % (src, dst, exc.args[-1])) + # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) elif link == 'hard': @@ -159,12 +166,13 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, # XXX I suspect this is Unix-specific -- need porting help! -def move_file(src, dst, verbose=1, dry_run=0): - """Move a file 'src' to 'dst'. +def move_file (src, dst, + verbose=1, + dry_run=0): - If 'dst' is a directory, the file will be moved into it with the same - name; otherwise, 'src' is just renamed to 'dst'. Return the new - full name of the file. + """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will + be moved into it with the same name; otherwise, 'src' is just renamed + to 'dst'. Return the new full name of the file. Handles cross-device moves on Unix using 'copy_file()'. What about other systems??? @@ -221,7 +229,7 @@ def move_file(src, dst, verbose=1, dry_run=0): return dst -def write_file(filename, contents): +def write_file (filename, contents): """Create a file with the specified name and write 'contents' (a sequence of strings without line terminators) to it. """ diff --git a/filelist.py b/filelist.py index bfc6df69..06a8da9a 100644 --- a/filelist.py +++ b/filelist.py @@ -108,7 +108,7 @@ class FileList: # defined: it's the first word of the line. Which of the other # three are defined depends on the action; it'll be either # patterns, (dir and patterns), or (dir_pattern). - action, patterns, dir, dir_pattern = self._parse_template_line(line) + (action, patterns, dir, dir_pattern) = self._parse_template_line(line) # OK, now we know that the action is valid and we have the # right number of words on the line for that action -- so we @@ -175,15 +175,15 @@ class FileList: raise DistutilsInternalError( "this cannot happen: invalid action '%s'" % action) + # -- Filtering/selection methods ----------------------------------- def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): """Select strings (presumably filenames) from 'self.files' that - match 'pattern', a Unix-style wildcard (glob) pattern. - - Patterns are not quite the same as implemented by the 'fnmatch' - module: '*' and '?' match non-special characters, where "special" - is platform-dependent: slash on Unix; colon, slash, and backslash on + match 'pattern', a Unix-style wildcard (glob) pattern. Patterns + are not quite the same as implemented by the 'fnmatch' module: '*' + and '?' match non-special characters, where "special" is platform- + dependent: slash on Unix; colon, slash, and backslash on DOS/Windows; and colon on Mac OS. If 'anchor' is true (the default), then the pattern match is more @@ -220,13 +220,13 @@ class FileList: return files_found - def exclude_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): + def exclude_pattern (self, pattern, + anchor=1, prefix=None, is_regex=0): """Remove strings (presumably filenames) from 'files' that match - 'pattern'. - - Other parameters are the same as for 'include_pattern()', above. - The list 'self.files' is modified in place. Return 1 if files are - found. + 'pattern'. Other parameters are the same as for + 'include_pattern()', above. + The list 'self.files' is modified in place. + Return True if files are found, False otherwise. """ files_found = False pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) @@ -275,11 +275,10 @@ def findall(dir=os.curdir): def glob_to_re(pattern): - """Translate a shell-like glob pattern to a regular expression. - - Return a string containing the regex. Differs from - 'fnmatch.translate()' in that '*' does not match "special characters" - (which are platform-specific). + """Translate a shell-like glob pattern to a regular expression; return + a string containing the regex. Differs from 'fnmatch.translate()' in + that '*' does not match "special characters" (which are + platform-specific). """ pattern_re = fnmatch.translate(pattern) @@ -297,9 +296,7 @@ def glob_to_re(pattern): def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): """Translate a shell-like wildcard pattern to a compiled regular - expression. - - Return the compiled regex. If 'is_regex' true, + expression. Return the compiled regex. If 'is_regex' true, then 'pattern' is directly compiled to a regex (if it's a string) or just returned as-is (assumes it's a regex object). """ @@ -317,7 +314,7 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): if prefix is not None: # ditch end of pattern character empty_pattern = glob_to_re('') - prefix_re = glob_to_re(prefix)[:-len(empty_pattern)] + prefix_re = (glob_to_re(prefix))[:-len(empty_pattern)] pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) else: # no prefix -- respect anchor flag if anchor: diff --git a/msvc9compiler.py b/msvc9compiler.py index 15425d71..761b9ca2 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -19,9 +19,10 @@ import subprocess import sys import re -from distutils.errors import (DistutilsExecError, DistutilsPlatformError, - CompileError, LibError, LinkError) -from distutils.ccompiler import CCompiler, gen_lib_options +from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import CCompiler, gen_preprocess_options, \ + gen_lib_options from distutils import log from distutils.util import get_platform diff --git a/msvccompiler.py b/msvccompiler.py index dc3bd8de..1cd0f91d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -10,12 +10,12 @@ for the Microsoft Visual Studio. __revision__ = "$Id$" -import sys -import os - -from distutils.errors import (DistutilsExecError, DistutilsPlatformError, - CompileError, LibError, LinkError) -from distutils.ccompiler import CCompiler, gen_lib_options +import sys, os +from distutils.errors import \ + DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options, gen_lib_options from distutils import log _can_read_reg = False @@ -124,7 +124,7 @@ class MacroExpander: self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") else: self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") - except KeyError: + except KeyError as exc: # raise DistutilsPlatformError( """Python was built with Visual Studio 2003; extensions must be built with a compiler than can generate compatible binaries. diff --git a/sysconfig.py b/sysconfig.py index 4a8629e6..0fbd5412 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -7,9 +7,6 @@ available. Written by: Fred L. Drake, Jr. Email: - -**This module has been moved out of Distutils and will be removed from -Python in the next version (3.3)** """ __revision__ = "$Id$" @@ -17,45 +14,51 @@ __revision__ = "$Id$" import io import os import re -from warnings import warn - -from distutils.errors import DistutilsPlatformError - -# importing sysconfig from Lib -# to avoid this module to shadow it -_sysconfig = __import__('sysconfig') - -# names defined here to keep backward compatibility -# for APIs that were relocated -get_python_version = _sysconfig.get_python_version -get_config_h_filename = _sysconfig.get_config_h_filename -parse_config_h = _sysconfig.parse_config_h -get_config_vars = _sysconfig.get_config_vars -get_config_var = _sysconfig.get_config_var -from distutils.ccompiler import customize_compiler - -_DEPRECATION_MSG = ("distutils.sysconfig.%s is deprecated. " - "Use the APIs provided by the sysconfig module instead") - -def _get_project_base(): - return _sysconfig._PROJECT_BASE - -project_base = _get_project_base() - -class _DeprecatedBool(int): - def __nonzero__(self): - warn(_DEPRECATION_MSG % 'get_python_version', DeprecationWarning) - return super(_DeprecatedBool, self).__nonzero__() - +import sys + +from .errors import DistutilsPlatformError + +# These are needed in a couple of spots, so just compute them once. +PREFIX = os.path.normpath(sys.prefix) +EXEC_PREFIX = os.path.normpath(sys.exec_prefix) + +# Path to the base directory of the project. On Windows the binary may +# live in project/PCBuild9. If we're dealing with an x64 Windows build, +# it'll live in project/PCbuild/amd64. +project_base = os.path.dirname(os.path.abspath(sys.executable)) +if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) +# PC/VS7.1 +if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, + os.path.pardir)) +# PC/AMD64 +if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): + project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, + os.path.pardir)) + +# python_build: (Boolean) if true, we're either building Python or +# building an extension with an un-installed Python, so we use +# different (hard-wired) directories. +# Setup.local is available for Makefile builds including VPATH builds, +# Setup.dist is available on Windows def _python_build(): - return _DeprecatedBool(_sysconfig.is_python_build()) - + for fn in ("Setup.dist", "Setup.local"): + if os.path.isfile(os.path.join(project_base, "Modules", fn)): + return True + return False python_build = _python_build() -def get_python_inc(plat_specific=0, prefix=None): - """This function is deprecated. +def get_python_version(): + """Return a string containing the major and minor Python version, + leaving off the patchlevel. Sample return values could be '1.5' + or '2.2'. + """ + return sys.version[:3] + - Return the directory containing installed Python header files. +def get_python_inc(plat_specific=0, prefix=None): + """Return the directory containing installed Python header files. If 'plat_specific' is false (the default), this is the path to the non-platform-specific header files, i.e. Python.h and so on; @@ -65,22 +68,39 @@ def get_python_inc(plat_specific=0, prefix=None): If 'prefix' is supplied, use it instead of sys.prefix or sys.exec_prefix -- i.e., ignore 'plat_specific'. """ - warn(_DEPRECATION_MSG % 'get_python_inc', DeprecationWarning) - get_path = _sysconfig.get_path - - if prefix is not None: - vars = {'base': prefix} - return get_path('include', vars=vars) - - if not plat_specific: - return get_path('include') + if prefix is None: + prefix = plat_specific and EXEC_PREFIX or PREFIX + if os.name == "posix": + if python_build: + # Assume the executable is in the build directory. The + # pyconfig.h file should be in the same directory. Since + # the build directory may not be the source directory, we + # must use "srcdir" from the makefile to find the "Include" + # directory. + base = os.path.dirname(os.path.abspath(sys.executable)) + if plat_specific: + return base + else: + incdir = os.path.join(get_config_var('srcdir'), 'Include') + return os.path.normpath(incdir) + return os.path.join(prefix, "include", "python" + get_python_version()) + elif os.name == "nt": + return os.path.join(prefix, "include") + elif os.name == "mac": + if plat_specific: + return os.path.join(prefix, "Mac", "Include") + else: + return os.path.join(prefix, "Include") + elif os.name == "os2": + return os.path.join(prefix, "Include") else: - return get_path('platinclude') + raise DistutilsPlatformError( + "I don't know where Python installs its C header files " + "on platform '%s'" % os.name) -def get_python_lib(plat_specific=False, standard_lib=False, prefix=None): - """This function is deprecated. - Return the directory containing the Python library (standard or +def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): + """Return the directory containing the Python library (standard or site additions). If 'plat_specific' is true, return the directory containing @@ -93,33 +113,149 @@ def get_python_lib(plat_specific=False, standard_lib=False, prefix=None): If 'prefix' is supplied, use it instead of sys.prefix or sys.exec_prefix -- i.e., ignore 'plat_specific'. """ - warn(_DEPRECATION_MSG % 'get_python_lib', DeprecationWarning) - vars = {} - get_path = _sysconfig.get_path - if prefix is not None: - if plat_specific: - vars['platbase'] = prefix + if prefix is None: + prefix = plat_specific and EXEC_PREFIX or PREFIX + + if os.name == "posix": + libpython = os.path.join(prefix, + "lib", "python" + get_python_version()) + if standard_lib: + return libpython else: - vars['base'] = prefix - if standard_lib: + return os.path.join(libpython, "site-packages") + elif os.name == "nt": + if standard_lib: + return os.path.join(prefix, "Lib") + else: + if get_python_version() < "2.2": + return prefix + else: + return os.path.join(prefix, "Lib", "site-packages") + elif os.name == "mac": if plat_specific: - return get_path('platstdlib', vars=vars) + if standard_lib: + return os.path.join(prefix, "Lib", "lib-dynload") + else: + return os.path.join(prefix, "Lib", "site-packages") + else: + if standard_lib: + return os.path.join(prefix, "Lib") + else: + return os.path.join(prefix, "Lib", "site-packages") + elif os.name == "os2": + if standard_lib: + return os.path.join(prefix, "Lib") else: - return get_path('stdlib', vars=vars) + return os.path.join(prefix, "Lib", "site-packages") else: - if plat_specific: - return get_path('platlib', vars=vars) + raise DistutilsPlatformError( + "I don't know where Python installs its library " + "on platform '%s'" % os.name) + + +def customize_compiler(compiler): + """Do any platform-specific customization of a CCompiler instance. + + Mainly needed on Unix, so we can plug in the information that + varies across Unices and is stored in Python's Makefile. + """ + if compiler.compiler_type == "unix": + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ + get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', + 'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS') + + if 'CC' in os.environ: + cc = os.environ['CC'] + if 'CXX' in os.environ: + cxx = os.environ['CXX'] + if 'LDSHARED' in os.environ: + ldshared = os.environ['LDSHARED'] + if 'CPP' in os.environ: + cpp = os.environ['CPP'] + else: + cpp = cc + " -E" # not always + if 'LDFLAGS' in os.environ: + ldshared = ldshared + ' ' + os.environ['LDFLAGS'] + if 'CFLAGS' in os.environ: + cflags = opt + ' ' + os.environ['CFLAGS'] + ldshared = ldshared + ' ' + os.environ['CFLAGS'] + if 'CPPFLAGS' in os.environ: + cpp = cpp + ' ' + os.environ['CPPFLAGS'] + cflags = cflags + ' ' + os.environ['CPPFLAGS'] + ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] else: - return get_path('purelib', vars=vars) + archiver = ar + ' ' + ar_flags + + cc_cmd = cc + ' ' + cflags + compiler.set_executables( + preprocessor=cpp, + compiler=cc_cmd, + compiler_so=cc_cmd + ' ' + ccshared, + compiler_cxx=cxx, + linker_so=ldshared, + linker_exe=cc, + archiver=archiver) + + compiler.shared_lib_extension = so_ext + + +def get_config_h_filename(): + """Return full pathname of installed pyconfig.h file.""" + if python_build: + if os.name == "nt": + inc_dir = os.path.join(project_base, "PC") + else: + inc_dir = project_base + else: + inc_dir = get_python_inc(plat_specific=1) + if get_python_version() < '2.2': + config_h = 'config.h' + else: + # The name of the config.h file changed in 2.2 + config_h = 'pyconfig.h' + return os.path.join(inc_dir, config_h) + def get_makefile_filename(): - """This function is deprecated. + """Return full pathname of installed Makefile from the Python build.""" + if python_build: + return os.path.join(os.path.dirname(sys.executable), "Makefile") + lib_dir = get_python_lib(plat_specific=1, standard_lib=1) + return os.path.join(lib_dir, "config", "Makefile") + + +def parse_config_h(fp, g=None): + """Parse a config.h-style file. - Return full pathname of installed Makefile from the Python build. + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. """ + if g is None: + g = {} + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + # + while True: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + try: v = int(v) + except ValueError: pass + g[n] = v + else: + m = undef_rx.match(line) + if m: + g[m.group(1)] = 0 + return g - warn(_DEPRECATION_MSG % 'get_makefile_filename', DeprecationWarning) - return _sysconfig._get_makefile_filename() # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). @@ -128,29 +264,91 @@ _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") def parse_makefile(fn, g=None): - """This function is deprecated. - - Parse a Makefile-style file. + """Parse a Makefile-style file. A dictionary containing name/value pairs is returned. If an optional dictionary is passed in as the second argument, it is used instead of a new dictionary. """ - warn(_DEPRECATION_MSG % 'parse_makefile', DeprecationWarning) - return _sysconfig._parse_makefile(fn, g) + from distutils.text_file import TextFile + fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1) -def expand_makefile_vars(s, vars): - """This function is deprecated. + if g is None: + g = {} + done = {} + notdone = {} - Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in + while True: + line = fp.readline() + if line is None: # eof + break + m = _variable_rx.match(line) + if m: + n, v = m.group(1, 2) + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: + notdone[n] = v + else: + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v + + # do variable interpolation here + while notdone: + for name in list(notdone): + value = notdone[name] + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) + if m: + n = m.group(1) + found = True + if n in done: + item = str(done[n]) + elif n in notdone: + # get it on a subsequent round + found = False + elif n in os.environ: + # do it like make: fall back to environment + item = os.environ[n] + else: + done[n] = item = "" + if found: + after = value[m.end():] + value = value[:m.start()] + item + after + if "$" in after: + notdone[name] = value + else: + try: value = int(value) + except ValueError: + done[name] = value.strip() + else: + done[name] = value + del notdone[name] + else: + # bogus variable reference; just drop it since we can't deal + del notdone[name] + + fp.close() + + # save the results in the global dictionary + g.update(done) + return g + + +def expand_makefile_vars(s, vars): + """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in 'string' according to 'vars' (a dictionary mapping variable names to values). Variables not present in 'vars' are silently expanded to the empty string. The variable values in 'vars' should not contain further variable expansions; if 'vars' is the output of 'parse_makefile()', you're fine. Returns a variable-expanded version of 's'. """ - warn('this function will be removed in then next version of Python', - DeprecationWarning) # This algorithm does multiple expansion, so if vars['foo'] contains # "${bar}", it will expand ${foo} to ${bar}, and then expand @@ -166,3 +364,220 @@ def expand_makefile_vars(s, vars): else: break return s + + +_config_vars = None + +def _init_posix(): + """Initialize the module as appropriate for POSIX systems.""" + g = {} + # load the installed Makefile: + try: + filename = get_makefile_filename() + parse_makefile(filename, g) + except IOError as msg: + my_msg = "invalid Python installation: unable to open %s" % filename + if hasattr(msg, "strerror"): + my_msg = my_msg + " (%s)" % msg.strerror + + raise DistutilsPlatformError(my_msg) + + # load the installed pyconfig.h: + try: + filename = get_config_h_filename() + parse_config_h(io.open(filename), g) + except IOError as msg: + my_msg = "invalid Python installation: unable to open %s" % filename + if hasattr(msg, "strerror"): + my_msg = my_msg + " (%s)" % msg.strerror + + raise DistutilsPlatformError(my_msg) + + # On MacOSX we need to check the setting of the environment variable + # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so + # it needs to be compatible. + # If it isn't set we set it to the configure-time value + if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g: + cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] + cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') + if cur_target == '': + cur_target = cfg_target + os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) + elif [int(x) for x in cfg_target.split('.')] > [int(x) for x in cur_target.split('.')]: + my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' + % (cur_target, cfg_target)) + raise DistutilsPlatformError(my_msg) + + # On AIX, there are wrong paths to the linker scripts in the Makefile + # -- these paths are relative to the Python source, but when installed + # the scripts are in another directory. + if python_build: + g['LDSHARED'] = g['BLDSHARED'] + + elif get_python_version() < '2.1': + # The following two branches are for 1.5.2 compatibility. + if sys.platform == 'aix4': # what about AIX 3.x ? + # Linker script is in the config directory, not in Modules as the + # Makefile says. + python_lib = get_python_lib(standard_lib=1) + ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') + python_exp = os.path.join(python_lib, 'config', 'python.exp') + + g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) + + global _config_vars + _config_vars = g + + +def _init_nt(): + """Initialize the module as appropriate for NT""" + g = {} + # set basic install directories + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) + + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = get_python_inc(plat_specific=0) + + g['SO'] = '.pyd' + g['EXE'] = ".exe" + g['VERSION'] = get_python_version().replace(".", "") + g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) + + global _config_vars + _config_vars = g + + +def _init_mac(): + """Initialize the module as appropriate for Macintosh systems""" + g = {} + # set basic install directories + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) + + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = get_python_inc(plat_specific=0) + + import MacOS + if not hasattr(MacOS, 'runtimemodel'): + g['SO'] = '.ppc.slb' + else: + g['SO'] = '.%s.slb' % MacOS.runtimemodel + + # XXX are these used anywhere? + g['install_lib'] = os.path.join(EXEC_PREFIX, "Lib") + g['install_platlib'] = os.path.join(EXEC_PREFIX, "Mac", "Lib") + + # These are used by the extension module build + g['srcdir'] = ':' + global _config_vars + _config_vars = g + + +def _init_os2(): + """Initialize the module as appropriate for OS/2""" + g = {} + # set basic install directories + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) + + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = get_python_inc(plat_specific=0) + + g['SO'] = '.pyd' + g['EXE'] = ".exe" + + global _config_vars + _config_vars = g + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. Generally this includes + everything needed to build extensions and install both pure modules and + extensions. On Unix, this means every variable defined in Python's + installed Makefile; on Windows and Mac OS it's a much smaller set. + + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _config_vars + if _config_vars is None: + func = globals().get("_init_" + os.name) + if func: + func() + else: + _config_vars = {} + + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # Distutils. + _config_vars['prefix'] = PREFIX + _config_vars['exec_prefix'] = EXEC_PREFIX + + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if python_build and os.name == "posix": + base = os.path.dirname(os.path.abspath(sys.executable)) + if (not os.path.isabs(_config_vars['srcdir']) and + base != os.getcwd()): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _config_vars['srcdir']) + _config_vars['srcdir'] = os.path.normpath(srcdir) + + if sys.platform == 'darwin': + kernel_version = os.uname()[2] # Kernel version (8.4.3) + major_version = int(kernel_version.split('.')[0]) + + if major_version < 8: + # On Mac OS X before 10.4, check if -arch and -isysroot + # are in CFLAGS or LDFLAGS and remove them if they are. + # This is needed when building extensions on a 10.3 system + # using a universal build of python. + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + flags = _config_vars[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags, re.ASCII) + flags = re.sub('-isysroot [^ \t]*', ' ', flags) + _config_vars[key] = flags + + else: + + # Allow the user to override the architecture flags using + # an environment variable. + # NOTE: This name was introduced by Apple in OSX 10.5 and + # is used by several scripting languages distributed with + # that OS release. + + if 'ARCHFLAGS' in os.environ: + arch = os.environ['ARCHFLAGS'] + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _config_vars[key] + flags = re.sub('-arch\s+\w+\s', ' ', flags) + flags = flags + ' ' + arch + _config_vars[key] = flags + + if args: + vals = [] + for name in args: + vals.append(_config_vars.get(name)) + return vals + else: + return _config_vars + +def get_config_var(name): + """Return the value of a single variable using the dictionary + returned by 'get_config_vars()'. Equivalent to + get_config_vars().get(name) + """ + return get_config_vars().get(name) diff --git a/tests/support.py b/tests/support.py index 45c94411..e258d2e5 100644 --- a/tests/support.py +++ b/tests/support.py @@ -3,19 +3,11 @@ import os import shutil import tempfile from copy import deepcopy -import warnings from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution -def capture_warnings(func): - def _capture_warnings(*args, **kw): - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - return func(*args, **kw) - return _capture_warnings - class LoggingSilencer(object): def setUp(self): @@ -63,8 +55,6 @@ class TempdirManager(object): super().tearDown() while self.tempdirs: d = self.tempdirs.pop() - if not os.path.exists(d): - continue shutil.rmtree(d, os.name in ('nt', 'cygwin')) def mkdtemp(self): diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 682f19a2..c6e08cbc 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -14,31 +14,16 @@ from distutils.spawn import find_executable, spawn from distutils.tests import support from test.support import check_warnings -try: - import grp - import pwd - UID_GID_SUPPORT = True -except ImportError: - UID_GID_SUPPORT = False - try: import zipfile ZIP_SUPPORT = True except ImportError: ZIP_SUPPORT = find_executable('zip') -# some tests will fail if zlib is not available -try: - import zlib -except ImportError: - zlib = None - - class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): - @unittest.skipUnless(zlib, "requires zlib") def test_make_tarball(self): # creating something to tar tmpdir = self.mkdtemp() @@ -49,7 +34,7 @@ class ArchiveUtilTestCase(support.TempdirManager, tmpdir2 = self.mkdtemp() unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], - "source and target should be on same drive") + "Source and target should be on same drive") base_name = os.path.join(tmpdir2, 'archive') @@ -99,7 +84,6 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir2, 'archive') return tmpdir, tmpdir2, base_name - @unittest.skipUnless(zlib, "Requires zlib") @unittest.skipUnless(find_executable('tar') and find_executable('gzip'), 'Need the tar command to run') def test_tarfile_vs_tar(self): @@ -185,7 +169,6 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertTrue(not os.path.exists(tarball)) self.assertEquals(len(w.warnings), 1) - @unittest.skipUnless(zlib, "Requires zlib") @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile(self): # creating something to tar @@ -210,59 +193,6 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir, 'archive') self.assertRaises(ValueError, make_archive, base_name, 'xxx') - @unittest.skipUnless(zlib, "Requires zlib") - def test_make_archive_owner_group(self): - # testing make_archive with owner and group, with various combinations - # this works even if there's not gid/uid support - if UID_GID_SUPPORT: - group = grp.getgrgid(0)[0] - owner = pwd.getpwuid(0)[0] - else: - group = owner = 'root' - - base_dir, root_dir, base_name = self._create_files() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, - group=group) - self.assertTrue(os.path.exists(res)) - - res = make_archive(base_name, 'zip', root_dir, base_dir) - self.assertTrue(os.path.exists(res)) - - res = make_archive(base_name, 'tar', root_dir, base_dir, - owner=owner, group=group) - self.assertTrue(os.path.exists(res)) - - res = make_archive(base_name, 'tar', root_dir, base_dir, - owner='kjhkjhkjg', group='oihohoh') - self.assertTrue(os.path.exists(res)) - - @unittest.skipUnless(zlib, "Requires zlib") - @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") - def test_tarfile_root_owner(self): - tmpdir, tmpdir2, base_name = self._create_files() - old_dir = os.getcwd() - os.chdir(tmpdir) - group = grp.getgrgid(0)[0] - owner = pwd.getpwuid(0)[0] - try: - archive_name = make_tarball(base_name, 'dist', compress=None, - owner=owner, group=group) - finally: - os.chdir(old_dir) - - # check if the compressed tarball was created - self.assertTrue(os.path.exists(archive_name)) - - # now checks the rights - archive = tarfile.open(archive_name) - try: - for member in archive.getmembers(): - self.assertEquals(member.uid, 0) - self.assertEquals(member.gid, 0) - finally: - archive.close() - def test_make_archive_cwd(self): current_dir = os.getcwd() def _breaks(*args, **kw): diff --git a/tests/test_bdist.py b/tests/test_bdist.py index 29dcc7c8..f2849a97 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -5,8 +5,6 @@ import os import tempfile import shutil -from test.support import run_unittest - from distutils.core import Distribution from distutils.command.bdist import bdist from distutils.tests import support @@ -42,4 +40,4 @@ def test_suite(): return unittest.makeSuite(BuildTestCase) if __name__ == '__main__': - run_unittest(test_suite()) + test_support.run_unittest(test_suite()) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 746144bf..5e76809f 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -4,15 +4,6 @@ import unittest import sys import os -# zlib is not used here, but if it's not available -# test_simple_built will fail -try: - import zlib -except ImportError: - zlib = None - -from test.support import run_unittest - from distutils.core import Distribution from distutils.command.bdist_dumb import bdist_dumb from distutils.tests import support @@ -42,7 +33,6 @@ class BuildDumbTestCase(support.TempdirManager, sys.argv[:] = self.old_sys_argv[1] super(BuildDumbTestCase, self).tearDown() - @unittest.skipUnless(zlib, "requires zlib") def test_simple_built(self): # let's create a simple package @@ -83,23 +73,8 @@ class BuildDumbTestCase(support.TempdirManager, # now let's check what we have in the zip file # XXX to be done - def test_finalize_options(self): - pkg_dir, dist = self.create_dist() - os.chdir(pkg_dir) - cmd = bdist_dumb(dist) - self.assertEquals(cmd.bdist_dir, None) - cmd.finalize_options() - - # bdist_dir is initialized to bdist_base/dumb if not set - base = cmd.get_finalized_command('bdist').bdist_base - self.assertEquals(cmd.bdist_dir, os.path.join(base, 'dumb')) - - # the format is set to a default value depending on the os.name - default = cmd.default_format[os.name] - self.assertEquals(cmd.format, default) - def test_suite(): return unittest.makeSuite(BuildDumbTestCase) if __name__ == '__main__': - run_unittest(test_suite()) + test_support.run_unittest(test_suite()) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 1014e549..2aa257f7 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -6,8 +6,6 @@ import os import tempfile import shutil -from test.support import run_unittest - from distutils.core import Distribution from distutils.command.bdist_rpm import bdist_rpm from distutils.tests import support @@ -124,4 +122,4 @@ def test_suite(): return unittest.makeSuite(BuildRpmTestCase) if __name__ == '__main__': - run_unittest(test_suite()) + test_support.run_unittest(test_suite()) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index ffe41353..9b1ba6d1 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -1,8 +1,6 @@ """Tests for distutils.command.bdist_wininst.""" import unittest -from test.support import run_unittest - from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support @@ -29,4 +27,4 @@ def test_suite(): return unittest.makeSuite(BuildWinInstTestCase) if __name__ == '__main__': - run_unittest(test_suite()) + test_support.run_unittest(test_suite()) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 145eff54..536cd673 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -120,7 +120,8 @@ class BuildCLibTestCase(support.TempdirManager, # before we run the command, we want to make sure # all commands are present on the system # by creating a compiler and checking its executables - from distutils.ccompiler import new_compiler, customize_compiler + from distutils.ccompiler import new_compiler + from distutils.sysconfig import customize_compiler compiler = new_compiler() customize_compiler(compiler) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index e41a824f..b7cdc20a 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -3,13 +3,10 @@ import os import tempfile import shutil from io import StringIO -import warnings -from test.support import check_warnings -from test.support import captured_stdout from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext -import sysconfig +from distutils import sysconfig from distutils.tests.support import TempdirManager from distutils.tests.support import LoggingSilencer from distutils.extension import Extension @@ -25,23 +22,19 @@ ALREADY_TESTED = False def _get_source_filename(): srcdir = sysconfig.get_config_var('srcdir') - if srcdir is None: - return os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') return os.path.join(srcdir, 'Modules', 'xxmodule.c') -_XX_MODULE_PATH = _get_source_filename() - -class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase): - +class BuildExtTestCase(TempdirManager, + LoggingSilencer, + unittest.TestCase): def setUp(self): # Create a simple test environment # Note that we're making changes to sys.path super(BuildExtTestCase, self).setUp() self.tmp_dir = self.mkdtemp() - if os.path.exists(_XX_MODULE_PATH): - self.sys_path = sys.path[:] - sys.path.append(self.tmp_dir) - shutil.copy(_XX_MODULE_PATH, self.tmp_dir) + self.sys_path = sys.path, sys.path[:] + sys.path.append(self.tmp_dir) + shutil.copy(_get_source_filename(), self.tmp_dir) if sys.version > "2.6": import site self.old_user_base = site.USER_BASE @@ -49,19 +42,6 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase): from distutils.command import build_ext build_ext.USER_BASE = site.USER_BASE - def tearDown(self): - # Get everything back to normal - if os.path.exists(_XX_MODULE_PATH): - support.unload('xx') - sys.path[:] = self.sys_path - # XXX on Windows the test leaves a directory - # with xx module in TEMP - shutil.rmtree(self.tmp_dir, os.name == 'nt' or - sys.platform == 'cygwin') - super(BuildExtTestCase, self).tearDown() - - @unittest.skipIf(not os.path.exists(_XX_MODULE_PATH), - 'xxmodule.c not found') def test_build_ext(self): global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') @@ -104,23 +84,35 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase): self.assertTrue(isinstance(xx.Null(), xx.Null)) self.assertTrue(isinstance(xx.Str(), xx.Str)) + def tearDown(self): + # Get everything back to normal + support.unload('xx') + sys.path = self.sys_path[0] + sys.path[:] = self.sys_path[1] + if sys.version > "2.6": + import site + site.USER_BASE = self.old_user_base + from distutils.command import build_ext + build_ext.USER_BASE = self.old_user_base + super(BuildExtTestCase, self).tearDown() + def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) cmd = build_ext(dist) old = sys.platform sys.platform = 'sunos' # fooling finalize_options - from sysconfig import _CONFIG_VARS - old_var = _CONFIG_VARS.get('Py_ENABLE_SHARED') - _CONFIG_VARS['Py_ENABLE_SHARED'] = 1 + from distutils.sysconfig import _config_vars + old_var = _config_vars.get('Py_ENABLE_SHARED') + _config_vars['Py_ENABLE_SHARED'] = 1 try: cmd.ensure_finalized() finally: sys.platform = old if old_var is None: - del _CONFIG_VARS['Py_ENABLE_SHARED'] + del _config_vars['Py_ENABLE_SHARED'] else: - _CONFIG_VARS['Py_ENABLE_SHARED'] = old_var + _config_vars['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris self.assertTrue(len(cmd.library_dirs) > 0) @@ -182,10 +174,11 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase): cmd = build_ext(dist) cmd.finalize_options() - py_include = sysconfig.get_path('include') + from distutils import sysconfig + py_include = sysconfig.get_python_inc() self.assertTrue(py_include in cmd.include_dirs) - plat_py_include = sysconfig.get_path('platinclude') + plat_py_include = sysconfig.get_python_inc(plat_specific=1) self.assertTrue(plat_py_include in cmd.include_dirs) # make sure cmd.libraries is turned into a list @@ -336,6 +329,7 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase): self.assertEquals(so_dir, other_tmp_dir) cmd.inplace = 0 + cmd.compiler = None cmd.run() so_file = cmd.get_outputs()[0] self.assertTrue(os.path.exists(so_file)) @@ -404,26 +398,6 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase): wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) self.assertEquals(wanted, path) - def test_compiler_deprecation_warning(self): - dist = Distribution() - cmd = build_ext(dist) - - class MyCompiler(object): - def do_something(self): - pass - - with check_warnings() as w: - warnings.simplefilter("always") - cmd.compiler = MyCompiler() - self.assertEquals(len(w.warnings), 1) - cmd.compile = 'unix' - self.assertEquals(len(w.warnings), 1) - cmd.compiler = MyCompiler() - cmd.compiler.do_something() - # two more warnings genereated by the get - # and the set - self.assertEquals(len(w.warnings), 3) - def test_suite(): src = _get_source_filename() if not os.path.exists(src): diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 61e213a5..3e45f6e8 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -16,7 +16,7 @@ class BuildPyTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): - def _setup_package_data(self): + def test_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") f.write("# Pretend this is a package.") @@ -52,18 +52,9 @@ class BuildPyTestCase(support.TempdirManager, self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) - return files - - def test_package_data(self): - files = self._setup_package_data() self.assertTrue("__init__.py" in files) - self.assertTrue("README.txt" in files) - - @unittest.skipIf(sys.flags.optimize >= 2, - "pyc files are not written with -O2 and above") - def test_package_data_pyc(self): - files = self._setup_package_data() self.assertTrue("__init__.pyc" in files) + self.assertTrue("README.txt" in files) def test_empty_package_dir (self): # See SF 1668596/1720897. diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 72e8915c..b1d2d079 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,7 +5,7 @@ import unittest from distutils.command.build_scripts import build_scripts from distutils.core import Distribution -import sysconfig +from distutils import sysconfig from distutils.tests import support @@ -91,12 +91,12 @@ class BuildScriptsTestCase(support.TempdirManager, # --with-suffix=3`, python is compiled okay but the build scripts # failed when writing the name of the executable old = sysconfig.get_config_vars().get('VERSION') - sysconfig._CONFIG_VARS['VERSION'] = 4 + sysconfig._config_vars['VERSION'] = 4 try: cmd.run() finally: if old is not None: - sysconfig._CONFIG_VARS['VERSION'] = old + sysconfig._config_vars['VERSION'] = old built = os.listdir(target) for name in expected: diff --git a/tests/test_ccompiler.py b/tests/test_ccompiler.py deleted file mode 100644 index 27b51a06..00000000 --- a/tests/test_ccompiler.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Tests for distutils.ccompiler.""" -import os -import unittest -from test.support import captured_stdout - -from distutils.ccompiler import (gen_lib_options, CCompiler, - get_default_compiler, customize_compiler) -from distutils import debug -from distutils.tests import support - -class FakeCompiler(object): - def library_dir_option(self, dir): - return "-L" + dir - - def runtime_library_dir_option(self, dir): - return ["-cool", "-R" + dir] - - def find_library_file(self, dirs, lib, debug=0): - return 'found' - - def library_option(self, lib): - return "-l" + lib - -class CCompilerTestCase(support.EnvironGuard, unittest.TestCase): - - def test_gen_lib_options(self): - compiler = FakeCompiler() - libdirs = ['lib1', 'lib2'] - runlibdirs = ['runlib1'] - libs = [os.path.join('dir', 'name'), 'name2'] - - opts = gen_lib_options(compiler, libdirs, runlibdirs, libs) - wanted = ['-Llib1', '-Llib2', '-cool', '-Rrunlib1', 'found', - '-lname2'] - self.assertEquals(opts, wanted) - - def test_debug_print(self): - - class MyCCompiler(CCompiler): - executables = {} - - compiler = MyCCompiler() - with captured_stdout() as stdout: - compiler.debug_print('xxx') - stdout.seek(0) - self.assertEquals(stdout.read(), '') - - debug.DEBUG = True - try: - with captured_stdout() as stdout: - compiler.debug_print('xxx') - stdout.seek(0) - self.assertEquals(stdout.read(), 'xxx\n') - finally: - debug.DEBUG = False - - def test_customize_compiler(self): - - # not testing if default compiler is not unix - if get_default_compiler() != 'unix': - return - - os.environ['AR'] = 'my_ar' - os.environ['ARFLAGS'] = '-arflags' - - # make sure AR gets caught - class compiler: - compiler_type = 'unix' - - def set_executables(self, **kw): - self.exes = kw - - comp = compiler() - customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') - -def test_suite(): - return unittest.makeSuite(CCompilerTestCase) - -if __name__ == "__main__": - unittest.main(defaultTest="test_suite") diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 728652e2..55ae421d 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,7 +1,7 @@ """Tests for distutils.cmd.""" import unittest import os -from test.support import captured_stdout, run_unittest +from test.support import captured_stdout from distutils.cmd import Command from distutils.dist import Distribution @@ -124,4 +124,4 @@ def test_suite(): return unittest.makeSuite(CommandTestCase) if __name__ == '__main__': - run_unittest(test_suite()) + test_support.run_unittest(test_suite()) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index 374f392d..a57694d4 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -2,21 +2,29 @@ import unittest import sys import os +from io import BytesIO import subprocess -import warnings -import sysconfig - -from test.support import check_warnings, run_unittest -from test.support import captured_stdout from distutils import cygwinccompiler from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_UNCERTAIN, get_versions, - get_msvcr, RE_VERSION) -from distutils.util import get_compiler_versions + get_msvcr) from distutils.tests import support +class FakePopen(object): + test_class = None + + def __init__(self, cmd, shell, stdout): + self.cmd = cmd.split()[0] + exes = self.test_class._exes + if self.cmd in exes: + # issue #6438 in Python 3.x, Popen returns bytes + self.stdout = BytesIO(exes[self.cmd]) + else: + self.stdout = os.popen(cmd, 'r') + + class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase): @@ -24,17 +32,32 @@ class CygwinCCompilerTestCase(support.TempdirManager, super(CygwinCCompilerTestCase, self).setUp() self.version = sys.version self.python_h = os.path.join(self.mkdtemp(), 'python.h') + from distutils import sysconfig self.old_get_config_h_filename = sysconfig.get_config_h_filename sysconfig.get_config_h_filename = self._get_config_h_filename + self.old_find_executable = cygwinccompiler.find_executable + cygwinccompiler.find_executable = self._find_executable + self._exes = {} + self.old_popen = cygwinccompiler.Popen + FakePopen.test_class = self + cygwinccompiler.Popen = FakePopen def tearDown(self): sys.version = self.version + from distutils import sysconfig sysconfig.get_config_h_filename = self.old_get_config_h_filename + cygwinccompiler.find_executable = self.old_find_executable + cygwinccompiler.Popen = self.old_popen super(CygwinCCompilerTestCase, self).tearDown() def _get_config_h_filename(self): return self.python_h + def _find_executable(self, name): + if name in self._exes: + return name + return None + def test_check_config_h(self): # check_config_h looks for "GCC" in sys.version first @@ -58,6 +81,40 @@ class CygwinCCompilerTestCase(support.TempdirManager, self.write_file(self.python_h, 'xxx __GNUC__ xxx') self.assertEquals(check_config_h()[0], CONFIG_H_OK) + def test_get_versions(self): + + # get_versions calls distutils.spawn.find_executable on + # 'gcc', 'ld' and 'dllwrap' + self.assertEquals(get_versions(), (None, None, None)) + + # Let's fake we have 'gcc' and it returns '3.4.5' + self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' + res = get_versions() + self.assertEquals(str(res[0]), '3.4.5') + + # and let's see what happens when the version + # doesn't match the regular expression + # (\d+\.\d+(\.\d+)*) + self._exes['gcc'] = b'very strange output' + res = get_versions() + self.assertEquals(res[0], None) + + # same thing for ld + self._exes['ld'] = b'GNU ld version 2.17.50 20060824' + res = get_versions() + self.assertEquals(str(res[1]), '2.17.50') + self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_versions() + self.assertEquals(res[1], None) + + # and dllwrap + self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' + res = get_versions() + self.assertEquals(str(res[2]), '2.17.50') + self._exes['dllwrap'] = b'Cheese Wrap' + res = get_versions() + self.assertEquals(res[2], None) + def test_get_msvcr(self): # none @@ -90,23 +147,8 @@ class CygwinCCompilerTestCase(support.TempdirManager, '[MSC v.1999 32 bits (Intel)]') self.assertRaises(ValueError, get_msvcr) - - def test_get_version_deprecated(self): - with check_warnings() as w: - warnings.simplefilter("always") - # make sure get_compiler_versions and get_versions - # returns the same thing - self.assertEquals(get_compiler_versions(), get_versions()) - # make sure using get_version() generated a warning - self.assertEquals(len(w.warnings), 1) - # make sure any usage of RE_VERSION will also - # generate a warning, but till works - version = RE_VERSION.search('1.2').group(1) - self.assertEquals(version, '1.2') - self.assertEquals(len(w.warnings), 2) - def test_suite(): return unittest.makeSuite(CygwinCCompilerTestCase) if __name__ == '__main__': - run_unittest(test_suite()) + test_support.run_unittest(test_suite()) diff --git a/tests/test_dist.py b/tests/test_dist.py index 4f51c16e..3b7637f3 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -7,9 +7,8 @@ import unittest import warnings import textwrap -from distutils.dist import Distribution, fix_help_options, DistributionMetadata +from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command -import distutils.dist from test.support import TESTFN, captured_stdout from distutils.tests import support @@ -38,8 +37,7 @@ class TestDistribution(Distribution): return self._config_files -class DistributionTestCase(support.TempdirManager, - support.LoggingSilencer, +class DistributionTestCase(support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): @@ -60,27 +58,6 @@ class DistributionTestCase(support.TempdirManager, d.parse_command_line() return d - def test_debug_mode(self): - with open(TESTFN, "w") as f: - f.write("[global]") - f.write("command_packages = foo.bar, splat") - - files = [TESTFN] - sys.argv.append("build") - - with captured_stdout() as stdout: - self.create_distribution(files) - stdout.seek(0) - self.assertEquals(stdout.read(), '') - distutils.dist.DEBUG = True - try: - with captured_stdout() as stdout: - self.create_distribution(files) - stdout.seek(0) - self.assertEquals(stdout.read(), '') - finally: - distutils.dist.DEBUG = False - def test_command_packages_unspecified(self): sys.argv.append("build") d = self.create_distribution() @@ -182,35 +159,6 @@ class DistributionTestCase(support.TempdirManager, kwargs = {'level': 'ok2'} self.assertRaises(ValueError, dist.announce, args, kwargs) - def test_find_config_files_disable(self): - # Ticket #1180: Allow user to disable their home config file. - temp_home = self.mkdtemp() - if os.name == 'posix': - user_filename = os.path.join(temp_home, ".pydistutils.cfg") - else: - user_filename = os.path.join(temp_home, "pydistutils.cfg") - - with open(user_filename, 'w') as f: - f.write('[distutils]\n') - - def _expander(path): - return temp_home - - old_expander = os.path.expanduser - os.path.expanduser = _expander - try: - d = distutils.dist.Distribution() - all_files = d.find_config_files() - - d = distutils.dist.Distribution(attrs={'script_args': - ['--no-user-cfg']}) - files = d.find_config_files() - finally: - os.path.expanduser = old_expander - - # make sure --no-user-cfg disables the user cfg file - self.assertEquals(len(all_files)-1, len(files)) - class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): @@ -364,38 +312,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, "version": "1.0", "long_description": long_desc} - dist = distutils.dist.Distribution(attrs) + dist = Distribution(attrs) meta = self.format_metadata(dist) meta = meta.replace('\n' + 8 * ' ', '\n') self.assertTrue(long_desc in meta) - def test_read_metadata(self): - attrs = {"name": "package", - "version": "1.0", - "long_description": "desc", - "description": "xxx", - "download_url": "http://example.com", - "keywords": ['one', 'two'], - "requires": ['foo']} - - dist = Distribution(attrs) - metadata = dist.metadata - - # write it then reloads it - PKG_INFO = io.StringIO() - metadata.write_pkg_file(PKG_INFO) - PKG_INFO.seek(0) - metadata.read_pkg_file(PKG_INFO) - - self.assertEquals(metadata.name, "package") - self.assertEquals(metadata.version, "1.0") - self.assertEquals(metadata.description, "xxx") - self.assertEquals(metadata.download_url, 'http://example.com') - self.assertEquals(metadata.keywords, ['one', 'two']) - self.assertEquals(metadata.platforms, ['UNKNOWN']) - self.assertEquals(metadata.obsoletes, None) - self.assertEquals(metadata.requires, ['foo']) - def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) diff --git a/tests/test_emxccompiler.py b/tests/test_emxccompiler.py deleted file mode 100644 index 1360f829..00000000 --- a/tests/test_emxccompiler.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Tests for distutils.emxccompiler.""" -import unittest -import sys -import os -import warnings - -from test.support import check_warnings, run_unittest -from test.support import captured_stdout - -from distutils.emxccompiler import get_versions -from distutils.util import get_compiler_versions -from distutils.tests import support - -class EmxCCompilerTestCase(support.TempdirManager, - unittest.TestCase): - - def test_get_version_deprecated(self): - with check_warnings() as w: - warnings.simplefilter("always") - # make sure get_compiler_versions and get_versions - # returns the same gcc - gcc, ld, dllwrap = get_compiler_versions() - emx_gcc, emx_ld = get_versions() - self.assertEquals(gcc, emx_gcc) - - # make sure using get_version() generated a warning - self.assertEquals(len(w.warnings), 1) - -def test_suite(): - return unittest.makeSuite(EmxCCompilerTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_extension.py b/tests/test_extension.py index 857284dd..1ee30585 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -1,16 +1,13 @@ """Tests for distutils.extension.""" -import os -import sys import unittest +import os import warnings from test.support import check_warnings from distutils.extension import read_setup_file, Extension -from distutils.tests.support import capture_warnings class ExtensionTestCase(unittest.TestCase): - @capture_warnings def test_read_setup_file(self): # trying to read a Setup file # (sample extracted from the PyGame project) @@ -33,22 +30,16 @@ class ExtensionTestCase(unittest.TestCase): self.assertEquals(names, wanted) - @unittest.skipIf(sys.flags.optimize >= 2, - "Assertions are omitted with -O2 and above") - def test_extension_init_assertions(self): - # The first argument, which is the name, must be a string. + def test_extension_init(self): + # the first argument, which is the name, must be a string self.assertRaises(AssertionError, Extension, 1, []) + ext = Extension('name', []) + self.assertEquals(ext.name, 'name') # the second argument, which is the list of files, must # be a list of strings self.assertRaises(AssertionError, Extension, 'name', 'file') self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) - - def test_extension_init(self): - ext = Extension('name', []) - self.assertEquals(ext.name, 'name') - - ext = Extension('name', ['file1', 'file2']) self.assertEquals(ext.sources, ['file1', 'file2']) diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 99b421f7..fac4a5d1 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -3,7 +3,7 @@ import unittest import os import shutil -from distutils.file_util import move_file, write_file, copy_file +from distutils.file_util import move_file from distutils import log from distutils.tests import support @@ -55,21 +55,6 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ['moving %s -> %s' % (self.source, self.target_dir)] self.assertEquals(self._logs, wanted) - def test_write_file(self): - lines = ['a', 'b', 'c'] - dir = self.mkdtemp() - foo = os.path.join(dir, 'foo') - write_file(foo, lines) - content = [line.strip() for line in open(foo).readlines()] - self.assertEquals(content, lines) - - def test_copy_file(self): - src_dir = self.mkdtemp() - foo = os.path.join(src_dir, 'foo') - write_file(foo, 'content') - dst_dir = self.mkdtemp() - copy_file(foo, dst_dir) - self.assertTrue(os.path.exists(os.path.join(dst_dir, 'foo'))) def test_suite(): return unittest.makeSuite(FileUtilTestCase) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index d98325ae..331180d2 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -1,25 +1,10 @@ """Tests for distutils.filelist.""" -from os.path import join import unittest -from test.support import captured_stdout from distutils.filelist import glob_to_re, FileList +from test.support import captured_stdout from distutils import debug -MANIFEST_IN = """\ -include ok -include xo -exclude xo -include foo.tmp -global-include *.x -global-include *.txt -global-exclude *.tmp -recursive-include f *.oo -recursive-exclude global *.x -graft dir -prune dir3 -""" - class FileListTestCase(unittest.TestCase): def test_glob_to_re(self): @@ -34,34 +19,6 @@ class FileListTestCase(unittest.TestCase): self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') - def test_process_template_line(self): - # testing all MANIFEST.in template patterns - file_list = FileList() - - # simulated file list - file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt', - join('global', 'one.txt'), - join('global', 'two.txt'), - join('global', 'files.x'), - join('global', 'here.tmp'), - join('f', 'o', 'f.oo'), - join('dir', 'graft-one'), - join('dir', 'dir2', 'graft2'), - join('dir3', 'ok'), - join('dir3', 'sub', 'ok.txt') - ] - - for line in MANIFEST_IN.split('\n'): - if line.strip() == '': - continue - file_list.process_template_line(line) - - wanted = ['ok', 'four.txt', join('global', 'one.txt'), - join('global', 'two.txt'), join('f', 'o', 'f.oo'), - join('dir', 'graft-one'), join('dir', 'dir2', 'graft2')] - - self.assertEquals(file_list.files, wanted) - def test_debug_print(self): file_list = FileList() with captured_stdout() as stdout: diff --git a/tests/test_install.py b/tests/test_install.py index 59e90801..76fa02ac 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -5,14 +5,12 @@ import os.path import sys import unittest import site -import sysconfig -from sysconfig import (get_scheme_names, _CONFIG_VARS, _INSTALL_SCHEMES, - get_config_var, get_path) from test.support import captured_stdout from distutils.command.install import install from distutils.command import install as install_module +from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution from distutils.errors import DistutilsOptionError @@ -38,23 +36,9 @@ class InstallTestCase(support.TempdirManager, build_lib=os.path.join(builddir, "lib"), ) - - - posix_prefix = _INSTALL_SCHEMES['posix_prefix'] - old_posix_prefix = posix_prefix['platinclude'] - posix_prefix['platinclude'] = \ - '{platbase}/include/python{py_version_short}' - - posix_home = _INSTALL_SCHEMES['posix_home'] - old_posix_home = posix_home['platinclude'] - posix_home['platinclude'] = '{base}/include/python' - try: - cmd = install(dist) - cmd.home = destination - cmd.ensure_finalized() - finally: - posix_home['platinclude'] = old_posix_home - posix_prefix['platinclude'] = old_posix_prefix + cmd = install(dist) + cmd.home = destination + cmd.ensure_finalized() self.assertEqual(cmd.install_base, destination) self.assertEqual(cmd.install_platbase, destination) @@ -79,19 +63,18 @@ class InstallTestCase(support.TempdirManager, return # preparing the environement for the test - self.old_user_base = get_config_var('userbase') - self.old_user_site = get_path('purelib', '%s_user' % os.name) + self.old_user_base = site.USER_BASE + self.old_user_site = site.USER_SITE self.tmpdir = self.mkdtemp() self.user_base = os.path.join(self.tmpdir, 'B') self.user_site = os.path.join(self.tmpdir, 'S') - _CONFIG_VARS['userbase'] = self.user_base - scheme = _INSTALL_SCHEMES['%s_user' % os.name] - scheme['purelib'] = self.user_site + site.USER_BASE = self.user_base + site.USER_SITE = self.user_site + install_module.USER_BASE = self.user_base + install_module.USER_SITE = self.user_site def _expanduser(path): - if path[0] == '~': - path = os.path.normpath(self.tmpdir) + path[1:] - return path + return self.tmpdir self.old_expand = os.path.expanduser os.path.expanduser = _expanduser @@ -99,17 +82,19 @@ class InstallTestCase(support.TempdirManager, # this is the actual test self._test_user_site() finally: - _CONFIG_VARS['userbase'] = self.old_user_base - scheme['purelib'] = self.old_user_site + site.USER_BASE = self.old_user_base + site.USER_SITE = self.old_user_site + install_module.USER_BASE = self.old_user_base + install_module.USER_SITE = self.old_user_site os.path.expanduser = self.old_expand def _test_user_site(self): - schemes = get_scheme_names() - for key in ('nt_user', 'posix_user', 'os2_home'): - self.assertTrue(key in schemes) + for key in ('nt_user', 'unix_user', 'os2_home'): + self.assertTrue(key in INSTALL_SCHEMES) dist = Distribution({'name': 'xx'}) cmd = install(dist) + # making sure the user option is there options = [name for name, short, lable in cmd.user_options] @@ -200,7 +185,7 @@ class InstallTestCase(support.TempdirManager, with open(cmd.record) as f: self.assertEquals(len(f.readlines()), 1) - def _test_debug_mode(self): + def test_debug_mode(self): # this covers the code called when DEBUG is set old_logs_len = len(self.logs) install_module.DEBUG = True diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 13d27aba..99a6d906 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -1,6 +1,6 @@ """Tests for distutils.command.install_data.""" -import os import sys +import os import unittest from distutils.command.install_lib import install_lib @@ -31,7 +31,9 @@ class InstallLibTestCase(support.TempdirManager, cmd.finalize_options() self.assertEquals(cmd.optimize, 2) - def _setup_byte_compile(self): + @unittest.skipUnless(not sys.dont_write_bytecode, + 'byte-compile not supported') + def test_byte_compile(self): pkg_dir, dist = self.create_dist() cmd = install_lib(dist) cmd.compile = cmd.optimize = 1 @@ -39,15 +41,8 @@ class InstallLibTestCase(support.TempdirManager, f = os.path.join(pkg_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - return pkg_dir - - @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile not enabled') - def test_byte_compile(self): - pkg_dir = self._setup_byte_compile() - if sys.flags.optimize < 1: - self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) - else: - self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) + self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) def test_get_outputs(self): pkg_dir, dist = self.create_dist() diff --git a/tests/test_register.py b/tests/test_register.py index acda1b1a..c03ad101 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -202,10 +202,10 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertRaises(DistutilsSetupError, cmd.run) # we don't test the reSt feature if docutils - # is not installed or we our on py3k + # is not installed try: import docutils - except Exception: + except ImportError: return # metadata are OK but long_description is broken diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 9a76eacb..f95035df 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -3,22 +3,6 @@ import os import unittest import shutil import zipfile -import tarfile - -# zlib is not used here, but if it's not available -# the tests that use zipfile may fail -try: - import zlib -except ImportError: - zlib = None - -try: - import grp - import pwd - UID_GID_SUPPORT = True -except ImportError: - UID_GID_SUPPORT = False - from os.path import join import sys import tempfile @@ -95,7 +79,6 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.warn = _warn return dist, cmd - @unittest.skipUnless(zlib, "requires zlib") def test_prune_file_list(self): # this test creates a package with some vcs dirs in it # and launch sdist to make sure they get pruned @@ -137,7 +120,6 @@ class SDistTestCase(PyPIRCCommandTestCase): # making sure everything has been pruned correctly self.assertEquals(len(content), 4) - @unittest.skipUnless(zlib, "requires zlib") def test_make_distribution(self): # check if tar and gzip are installed @@ -174,7 +156,6 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEquals(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) - @unittest.skipUnless(zlib, "requires zlib") def test_add_defaults(self): # http://bugs.python.org/issue2279 @@ -236,7 +217,6 @@ class SDistTestCase(PyPIRCCommandTestCase): manifest = open(join(self.tmp_dir, 'MANIFEST')).read() self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) - @unittest.skipUnless(zlib, "requires zlib") def test_metadata_check_option(self): # testing the `medata-check` option dist, cmd = self.get_cmd(metadata={}) @@ -296,57 +276,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.formats = 'supazipa' self.assertRaises(DistutilsOptionError, cmd.finalize_options) - @unittest.skipUnless(zlib, "requires zlib") - @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") - def test_make_distribution_owner_group(self): - - # check if tar and gzip are installed - if (find_executable('tar') is None or - find_executable('gzip') is None): - return - - # now building a sdist - dist, cmd = self.get_cmd() - - # creating a gztar and specifying the owner+group - cmd.formats = ['gztar'] - cmd.owner = pwd.getpwuid(0)[0] - cmd.group = grp.getgrgid(0)[0] - cmd.ensure_finalized() - cmd.run() - - # making sure we have the good rights - archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') - archive = tarfile.open(archive_name) - try: - for member in archive.getmembers(): - self.assertEquals(member.uid, 0) - self.assertEquals(member.gid, 0) - finally: - archive.close() - - # building a sdist again - dist, cmd = self.get_cmd() - - # creating a gztar - cmd.formats = ['gztar'] - cmd.ensure_finalized() - cmd.run() - - # making sure we have the good rights - archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') - archive = tarfile.open(archive_name) - - # note that we are not testing the group ownership here - # because, depending on the platforms and the container - # rights (see #7408) - try: - for member in archive.getmembers(): - self.assertEquals(member.uid, os.getuid()) - finally: - archive.close() - @unittest.skipUnless(zlib, "requires zlib") def test_get_file_list(self): # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 9496950f..edc755ed 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -2,9 +2,9 @@ import os import test import unittest -import shutil from distutils import sysconfig +from distutils.ccompiler import get_default_compiler from distutils.tests import support from test.support import TESTFN, run_unittest @@ -26,7 +26,10 @@ class SysconfigTestCase(support.EnvironGuard, elif os.path.isdir(TESTFN): shutil.rmtree(TESTFN) - @support.capture_warnings + def test_get_config_h_filename(self): + config_h = sysconfig.get_config_h_filename() + self.assertTrue(os.path.isfile(config_h), config_h) + def test_get_python_lib(self): lib_dir = sysconfig.get_python_lib() # XXX doesn't work on Linux when Python was never installed before @@ -34,11 +37,7 @@ class SysconfigTestCase(support.EnvironGuard, # test for pythonxx.lib? self.assertNotEqual(sysconfig.get_python_lib(), sysconfig.get_python_lib(prefix=TESTFN)) - _sysconfig = __import__('sysconfig') - res = sysconfig.get_python_lib(True, True) - self.assertEquals(_sysconfig.get_path('platstdlib'), res) - @support.capture_warnings def test_get_python_inc(self): inc_dir = sysconfig.get_python_inc() # This is not much of a test. We make sure Python.h exists @@ -48,7 +47,31 @@ class SysconfigTestCase(support.EnvironGuard, python_h = os.path.join(inc_dir, "Python.h") self.assertTrue(os.path.isfile(python_h), python_h) - @support.capture_warnings + def test_get_config_vars(self): + cvars = sysconfig.get_config_vars() + self.assertTrue(isinstance(cvars, dict)) + self.assertTrue(cvars) + + def test_customize_compiler(self): + + # not testing if default compiler is not unix + if get_default_compiler() != 'unix': + return + + os.environ['AR'] = 'my_ar' + os.environ['ARFLAGS'] = '-arflags' + + # make sure AR gets caught + class compiler: + compiler_type = 'unix' + + def set_executables(self, **kw): + self.exes = kw + + comp = compiler() + sysconfig.customize_compiler(comp) + self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') + def test_parse_makefile_base(self): self.makefile = TESTFN fd = open(self.makefile, 'w') diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 6976dd55..3a41e6fc 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -1,8 +1,8 @@ """Tests for distutils.unixccompiler.""" import sys import unittest -import sysconfig +from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler class UnixCCompilerTestCase(unittest.TestCase): @@ -114,14 +114,6 @@ class UnixCCompilerTestCase(unittest.TestCase): sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-R/foo') - # AIX C/C++ linker - sys.platform = 'aix' - def gcv(v): - return 'xxx' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-blibpath:/foo') - - def test_suite(): return unittest.makeSuite(UnixCCompilerTestCase) diff --git a/tests/test_upload.py b/tests/test_upload.py index 979ab228..35e97005 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -2,8 +2,8 @@ import sys import os import unittest +import http.client as httpclient -from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution @@ -38,37 +38,48 @@ index-servers = [server1] username:me """ +class Response(object): + def __init__(self, status=200, reason='OK'): + self.status = status + self.reason = reason -class FakeOpen(object): +class FakeConnection(object): - def __init__(self, url): - self.url = url - if not isinstance(url, str): - self.req = url - else: - self.req = None - self.msg = 'OK' + def __init__(self): + self.requests = [] + self.headers = [] + self.body = '' - def getcode(self): - return 200 + def __call__(self, netloc): + return self + def connect(self): + pass + endheaders = connect + + def putrequest(self, method, url): + self.requests.append((method, url)) + + def putheader(self, name, value): + self.headers.append((name, value)) + + def send(self, body): + self.body = body + + def getresponse(self): + return Response() class uploadTestCase(PyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() - self.old_open = upload_mod.urlopen - upload_mod.urlopen = self._urlopen - self.last_open = None + self.old_class = httpclient.HTTPConnection + self.conn = httpclient.HTTPConnection = FakeConnection() def tearDown(self): - upload_mod.urlopen = self.old_open + httpclient.HTTPConnection = self.old_class super(uploadTestCase, self).tearDown() - def _urlopen(self, url): - self.last_open = FakeOpen(url) - return self.last_open - def test_finalize_options(self): # new format @@ -113,15 +124,13 @@ class uploadTestCase(PyPIRCCommandTestCase): cmd.run() # what did we send ? - headers = dict(self.last_open.req.headers) + headers = dict(self.conn.headers) self.assertEquals(headers['Content-length'], '2087') self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) - self.assertEquals(self.last_open.req.get_method(), 'POST') - self.assertEquals(self.last_open.req.get_full_url(), - 'http://pypi.python.org/pypi') - self.assertTrue(b'xxx' in self.last_open.req.data) - auth = self.last_open.req.headers['Authorization'] - self.assertFalse('\n' in auth) + self.assertFalse('\n' in headers['Authorization']) + + self.assertEquals(self.conn.requests, [('POST', '/pypi')]) + self.assert_((b'xxx') in self.conn.body) def test_suite(): return unittest.makeSuite(uploadTestCase) diff --git a/tests/test_util.py b/tests/test_util.py index 896e1e07..0c732f82 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -3,33 +3,15 @@ import os import sys import unittest from copy import copy -from io import BytesIO -import subprocess -from sysconfig import get_config_vars, get_platform from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError -from distutils.util import (convert_path, change_root, +from distutils.util import (get_platform, convert_path, change_root, check_environ, split_quoted, strtobool, - rfc822_escape, get_compiler_versions, - _find_exe_version, _MAC_OS_X_LD_VERSION, - byte_compile) -from distutils import util + rfc822_escape, byte_compile) +from distutils import util # used to patch _environ_checked +from distutils.sysconfig import get_config_vars +from distutils import sysconfig from distutils.tests import support -from distutils.version import LooseVersion - -class FakePopen(object): - test_class = None - def __init__(self, cmd, shell, stdout, stderr): - self.cmd = cmd.split()[0] - exes = self.test_class._exes - if self.cmd not in exes: - # we don't want to call the system, returning an empty - # output so it doesn't match - self.stdout = BytesIO() - self.stderr = BytesIO() - else: - self.stdout = BytesIO(exes[self.cmd]) - self.stderr = BytesIO() class UtilTestCase(support.EnvironGuard, unittest.TestCase): @@ -43,7 +25,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive - #self._config_vars = copy(sysconfig._config_vars) + self._config_vars = copy(sysconfig._config_vars) # patching os.uname if hasattr(os, 'uname'): @@ -52,17 +34,8 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): else: self.uname = None self._uname = None - os.uname = self._get_uname - # patching POpen - self.old_find_executable = util.find_executable - util.find_executable = self._find_executable - self._exes = {} - self.old_popen = subprocess.Popen - self.old_stdout = sys.stdout - self.old_stderr = sys.stderr - FakePopen.test_class = self - subprocess.Popen = FakePopen + os.uname = self._get_uname def tearDown(self): # getting back the environment @@ -77,11 +50,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): os.uname = self.uname else: del os.uname - #sysconfig._config_vars = copy(self._config_vars) - util.find_executable = self.old_find_executable - subprocess.Popen = self.old_popen - sys.old_stdout = self.old_stdout - sys.old_stderr = self.old_stderr + sysconfig._config_vars = copy(self._config_vars) super(UtilTestCase, self).tearDown() def _set_uname(self, uname): @@ -91,11 +60,103 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): return self._uname def test_get_platform(self): - platform = util.get_platform() - self.assertEquals(platform, get_platform()) - util.set_platform('MyOwnPlatform') - self.assertEquals('MyOwnPlatform', util.get_platform()) - util.set_platform(platform) + + # windows XP, 32bits + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Intel)]') + sys.platform = 'win32' + self.assertEquals(get_platform(), 'win32') + + # windows XP, amd64 + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Amd64)]') + sys.platform = 'win32' + self.assertEquals(get_platform(), 'win-amd64') + + # windows XP, itanium + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Itanium)]') + sys.platform = 'win32' + self.assertEquals(get_platform(), 'win-ia64') + + # macbook + os.name = 'posix' + sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' + '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') + sys.platform = 'darwin' + self._set_uname(('Darwin', 'macziade', '8.11.1', + ('Darwin Kernel Version 8.11.1: ' + 'Wed Oct 10 18:23:28 PDT 2007; ' + 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + + get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' + '-fwrapv -O3 -Wall -Wstrict-prototypes') + + cursize = sys.maxsize + sys.maxsize = (2 ** 31)-1 + try: + self.assertEquals(get_platform(), 'macosx-10.3-i386') + finally: + sys.maxsize = cursize + + # macbook with fat binaries (fat, universal or fat64) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEquals(get_platform(), 'macosx-10.4-fat') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEquals(get_platform(), 'macosx-10.4-intel') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + self.assertEquals(get_platform(), 'macosx-10.4-fat3') + + get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + self.assertEquals(get_platform(), 'macosx-10.4-universal') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEquals(get_platform(), 'macosx-10.4-fat64') + + for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): + get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3'%(arch,)) + + self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) + + # linux debian sarge + os.name = 'posix' + sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' + '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]') + sys.platform = 'linux2' + self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', + '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) + + self.assertEquals(get_platform(), 'linux-i686') + + # XXX more platforms to tests here def test_convert_path(self): # linux/mac @@ -199,70 +260,6 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): 'header%(8s)s') % {'8s': '\n'+8*' '} self.assertEquals(res, wanted) - def test_find_exe_version(self): - # the ld version scheme under MAC OS is: - # ^@(#)PROGRAM:ld PROJECT:ld64-VERSION - # - # where VERSION is a 2-digit number for major - # revisions. For instance under Leopard, it's - # currently 77 - # - # Dots are used when branching is done. - # - # The SnowLeopard ld64 is currently 95.2.12 - - for output, version in ((b'@(#)PROGRAM:ld PROJECT:ld64-77', '77'), - (b'@(#)PROGRAM:ld PROJECT:ld64-95.2.12', - '95.2.12')): - result = _MAC_OS_X_LD_VERSION.search(output) - self.assertEquals(result.group(1).decode(), version) - - def _find_executable(self, name): - if name in self._exes: - return name - return None - - def test_get_compiler_versions(self): - # get_versions calls distutils.spawn.find_executable on - # 'gcc', 'ld' and 'dllwrap' - self.assertEquals(get_compiler_versions(), (None, None, None)) - - # Let's fake we have 'gcc' and it returns '3.4.5' - self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' - res = get_compiler_versions() - self.assertEquals(str(res[0]), '3.4.5') - - # and let's see what happens when the version - # doesn't match the regular expression - # (\d+\.\d+(\.\d+)*) - self._exes['gcc'] = b'very strange output' - res = get_compiler_versions() - self.assertEquals(res[0], None) - - # same thing for ld - if sys.platform != 'darwin': - self._exes['ld'] = b'GNU ld version 2.17.50 20060824' - res = get_compiler_versions() - self.assertEquals(str(res[1]), '2.17.50') - self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' - res = get_compiler_versions() - self.assertEquals(res[1], None) - else: - self._exes['ld'] = b'GNU ld version 2.17.50 20060824' - res = get_compiler_versions() - self.assertEquals(res[1], None) - self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' - res = get_compiler_versions() - self.assertEquals(str(res[1]), '77') - - # and dllwrap - self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' - res = get_compiler_versions() - self.assertEquals(str(res[2]), '2.17.50') - self._exes['dllwrap'] = b'Cheese Wrap' - res = get_compiler_versions() - self.assertEquals(res[2], None) - def test_dont_write_bytecode(self): # makes sure byte_compile raise a DistutilsError # if sys.dont_write_bytecode is True diff --git a/text_file.py b/text_file.py index 53c8561a..97459fbf 100644 --- a/text_file.py +++ b/text_file.py @@ -6,8 +6,8 @@ lines, and joining lines with backslashes.""" __revision__ = "$Id$" -import sys -import io +import sys, os, io + class TextFile: """Provides a file-like object that takes care of all the things you diff --git a/unixccompiler.py b/unixccompiler.py index 08179082..bf734161 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,6 +17,7 @@ __revision__ = "$Id$" import os, sys, re +from distutils import sysconfig from distutils.dep_util import newer from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options @@ -24,7 +25,6 @@ from distutils.errors import \ DistutilsExecError, CompileError, LibError, LinkError from distutils import log - # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's # Makefile and live with it. Is this adequate? If not, we might @@ -74,7 +74,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): if 'ARCHFLAGS' in os.environ and not stripArch: # User specified different -arch flags in the environ, - # see also the sysconfig + # see also distutils.sysconfig compiler_so = compiler_so + os.environ['ARCHFLAGS'].split() if stripSysroot: @@ -281,9 +281,7 @@ class UnixCCompiler(CCompiler): # this time, there's no way to determine this information from # the configuration data stored in the Python installation, so # we use this hack. - _sysconfig = __import__('sysconfig') - - compiler = os.path.basename(_sysconfig.get_config_var("CC")) + compiler = os.path.basename(sysconfig.get_config_var("CC")) if sys.platform[:6] == "darwin": # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir @@ -293,24 +291,23 @@ class UnixCCompiler(CCompiler): return ["+s", "-L" + dir] elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": return ["-rpath", dir] - elif self._is_gcc(compiler): - # gcc on non-GNU systems does not need -Wl, but can - # use it anyway. Since distutils has always passed in - # -Wl whenever gcc was used in the past it is probably - # safest to keep doing so. - if _sysconfig.get_config_var("GNULD") == "yes": - # GNU ld needs an extra option to get a RUNPATH - # instead of just an RPATH. - return "-Wl,--enable-new-dtags,-R" + dir - else: - return "-Wl,-R" + dir - elif sys.platform[:3] == "aix": - return "-blibpath:" + dir else: - # No idea how --enable-new-dtags would be passed on to - # ld if this system was using GNU ld. Don't know if a - # system like this even exists. - return "-R" + dir + if self._is_gcc(compiler): + # gcc on non-GNU systems does not need -Wl, but can + # use it anyway. Since distutils has always passed in + # -Wl whenever gcc was used in the past it is probably + # safest to keep doing so. + if sysconfig.get_config_var("GNULD") == "yes": + # GNU ld needs an extra option to get a RUNPATH + # instead of just an RPATH. + return "-Wl,--enable-new-dtags,-R" + dir + else: + return "-Wl,-R" + dir + else: + # No idea how --enable-new-dtags would be passed on to + # ld if this system was using GNU ld. Don't know if a + # system like this even exists. + return "-R" + dir def library_option(self, lib): return "-l" + lib @@ -324,8 +321,7 @@ class UnixCCompiler(CCompiler): # On OSX users can specify an alternate SDK using # '-isysroot', calculate the SDK root if it is specified # (and use it further on) - _sysconfig = __import__('sysconfig') - cflags = _sysconfig.get_config_var('CFLAGS') + cflags = sysconfig.get_config_var('CFLAGS') m = re.search(r'-isysroot\s+(\S+)', cflags) if m is None: sysroot = '/' diff --git a/util.py b/util.py index c8bf0064..81754345 100644 --- a/util.py +++ b/util.py @@ -7,40 +7,182 @@ one of the other *util.py modules. __revision__ = "$Id$" import sys, os, string, re - from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer -from distutils.spawn import spawn, find_executable +from distutils.spawn import spawn from distutils import log -from distutils.version import LooseVersion from distutils.errors import DistutilsByteCompileError -_sysconfig = __import__('sysconfig') -_PLATFORM = None +def get_platform (): + """Return a string that identifies the current platform. This is used + mainly to distinguish platform-specific build directories and + platform-specific built distributions. Typically includes the OS name + and version and the architecture (as supplied by 'os.uname()'), + although the exact information included depends on the OS; eg. for IRIX + the architecture isn't particularly important (IRIX only runs on SGI + hardware), but for Linux the kernel version isn't particularly + important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + irix-5.3 + irix64-6.2 + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win-ia64 (64bit Windows on Itanium) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + """ + if os.name == 'nt': + # sniff sys.version for architecture. + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return sys.platform + j = sys.version.find(")", i) + look = sys.version[i+len(prefix):j].lower() + if look == 'amd64': + return 'win-amd64' + if look == 'itanium': + return 'win-ia64' + return sys.platform + + if os.name != "posix" or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + + (osname, host, release, version, machine) = os.uname() + + # Convert the OS name to lowercase, remove '/' characters + # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') + + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # fall through to standard osname-release-machine representation + elif osname[:4] == "irix": # could be "irix64"! + return "%s-%s" % (osname, release) + elif osname[:3] == "aix": + return "%s-%s.%s" % (osname, version, release) + elif osname[:6] == "cygwin": + osname = "cygwin" + rel_re = re.compile (r'[\d.]+', re.ASCII) + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == "darwin": + # + # For our purposes, we'll assume that the system version from + # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set + # to. This makes the compatibility story a bit more sane because the + # machine is going to compile and link as if it were + # MACOSX_DEPLOYMENT_TARGET. + from distutils.sysconfig import get_config_vars + cfgvars = get_config_vars() + + macver = os.environ.get('MACOSX_DEPLOYMENT_TARGET') + if not macver: + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + + if 1: + # Always calculate the release of the running machine, + # needed to determine if we can build fat binaries or not. + + macrelease = macver + # Get the system version. Reading this plist is a documented + # way to get the system version (see the documentation for + # the Gestalt Manager) + try: + f = open('/System/Library/CoreServices/SystemVersion.plist') + except IOError: + # We're on a plain darwin box, fall back to the default + # behaviour. + pass + else: + m = re.search( + r'ProductUserVisibleVersion\s*' + + r'(.*?)', f.read()) + f.close() + if m is not None: + macrelease = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + + if not macver: + macver = macrelease + + if macver: + from distutils.sysconfig import get_config_vars + release = macver + osname = "macosx" + + if (macrelease + '.') >= '10.4.' and \ + '-arch' in get_config_vars().get('CFLAGS', '').strip(): + # The universal build will build fat binaries, but not on + # systems before 10.4 + # + # Try to detect 4-way universal builds, those have machine-type + # 'universal' instead of 'fat'. + + machine = 'fat' + cflags = get_config_vars().get('CFLAGS') + + archs = re.findall('-arch\s+(\S+)', cflags) + archs = tuple(sorted(set(archs))) + + if len(archs) == 1: + machine = archs[0] + elif archs == ('i386', 'ppc'): + machine = 'fat' + elif archs == ('i386', 'x86_64'): + machine = 'intel' + elif archs == ('i386', 'ppc', 'x86_64'): + machine = 'fat3' + elif archs == ('ppc64', 'x86_64'): + machine = 'fat64' + elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): + machine = 'universal' + else: + raise ValueError( + "Don't know machine value for archs=%r"%(archs,)) -def get_platform(): - """Return a string that identifies the current platform. + elif machine == 'i386': + # On OSX the machine type returned by uname is always the + # 32-bit variant, even if the executable architecture is + # the 64-bit variant + if sys.maxsize >= 2**32: + machine = 'x86_64' - By default, will return the value returned by sysconfig.get_platform(), - but it can be changed by calling set_platform(). - """ - global _PLATFORM - if _PLATFORM is None: - _PLATFORM = _sysconfig.get_platform() - return _PLATFORM + elif machine in ('PowerPC', 'Power_Macintosh'): + # Pick a sane name for the PPC architecture. + machine = 'ppc' -def set_platform(identifier): - """Sets the platform string identifier returned by get_platform(). + # See 'i386' case + if sys.maxsize >= 2**32: + machine = 'ppc64' - Note that this change doesn't impact the value returned by - sysconfig.get_platform() and is local to Distutils - """ - global _PLATFORM - _PLATFORM = identifier + return "%s-%s-%s" % (osname, release, machine) + +# get_platform () -def convert_path(pathname): - """Return 'pathname' as a name that will work on the native filesystem. +def convert_path (pathname): + """Return 'pathname' as a name that will work on the native filesystem, i.e. split it on '/' and put it back together again using the current directory separator. Needed because filenames in the setup script are always supplied in Unix style, and have to be converted to the local @@ -64,12 +206,12 @@ def convert_path(pathname): return os.curdir return os.path.join(*paths) +# convert_path () -def change_root(new_root, pathname): - """Return 'pathname' with 'new_root' prepended. - If 'pathname' is relative, this is equivalent to - "os.path.join(new_root,pathname)". +def change_root (new_root, pathname): + """Return 'pathname' with 'new_root' prepended. If 'pathname' is + relative, this is equivalent to "os.path.join(new_root,pathname)". Otherwise, it requires making 'pathname' relative and then joining the two, which is tricky on DOS/Windows and Mac OS. """ @@ -91,16 +233,23 @@ def change_root(new_root, pathname): path = path[1:] return os.path.join(new_root, path) - else: - raise DistutilsPlatformError("nothing known about " - "platform '%s'" % os.name) + elif os.name == 'mac': + if not os.path.isabs(pathname): + return os.path.join(new_root, pathname) + else: + # Chop off volume name from start of path + elements = pathname.split(":", 1) + pathname = ":" + elements[1] + return os.path.join(new_root, pathname) -_environ_checked = 0 + else: + raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) -def check_environ(): - """Ensure that 'os.environ' has all the environment variables needed. - We guarantee that users can use in config files, command-line options, +_environ_checked = 0 +def check_environ (): + """Ensure that 'os.environ' has all the environment variables we + guarantee that users can use in config files, command-line options, etc. Currently this includes: HOME - user's home directory (Unix only) PLAT - description of the current platform, including hardware @@ -115,14 +264,14 @@ def check_environ(): os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] if 'PLAT' not in os.environ: - os.environ['PLAT'] = _sysconfig.get_platform() + os.environ['PLAT'] = get_platform() _environ_checked = 1 -def subst_vars(s, local_vars): - """Perform shell/Perl-style variable substitution on 'string'. - Every occurrence of '$' followed by a name is considered a variable, and +def subst_vars (s, local_vars): + """Perform shell/Perl-style variable substitution on 'string'. Every + occurrence of '$' followed by a name is considered a variable, and variable is substituted by the value found in the 'local_vars' dictionary, or in 'os.environ' if it's not in 'local_vars'. 'os.environ' is first checked/augmented to guarantee that it contains @@ -142,11 +291,12 @@ def subst_vars(s, local_vars): except KeyError as var: raise ValueError("invalid variable '$%s'" % var) -def grok_environment_error(exc, prefix="error: "): - """Generate a useful error message from an EnvironmentError. +# subst_vars () - This will generate an IOError or an OSError exception object. - Handles Python 1.5.1 and 1.5.2 styles, and + +def grok_environment_error (exc, prefix="error: "): + """Generate a useful error message from an EnvironmentError (IOError or + OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and does what it can to deal with exception objects that don't have a filename (which happens when the error is due to a two-file operation, such as 'rename()' or 'link()'. Returns the error message as a string @@ -165,20 +315,18 @@ def grok_environment_error(exc, prefix="error: "): return error + # Needed by 'split_quoted()' _wordchars_re = _squote_re = _dquote_re = None - def _init_regex(): global _wordchars_re, _squote_re, _dquote_re _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') -def split_quoted(s): +def split_quoted (s): """Split a string up according to Unix shell-like rules for quotes and - backslashes. - - In short: words are delimited by spaces, as long as those + backslashes. In short: words are delimited by spaces, as long as those spaces are not escaped by a backslash, or inside a quoted string. Single and double quotes are equivalent, and the quote characters can be backslash-escaped. The backslash is stripped from any two-character @@ -186,6 +334,7 @@ def split_quoted(s): characters are stripped from any quoted string. Returns a list of words. """ + # This is a nice algorithm for splitting up a single string, since it # doesn't require character-by-character examination. It was a little # bit of a brain-bender to get it working right, though... @@ -233,12 +382,13 @@ def split_quoted(s): return words +# split_quoted () -def execute(func, args, msg=None, verbose=0, dry_run=0): - """Perform some action that affects the outside world. - eg. by writing to the filesystem). Such actions are special because - they are disabled by the 'dry_run' flag. This method takes care of all +def execute (func, args, msg=None, verbose=0, dry_run=0): + """Perform some action that affects the outside world (eg. by + writing to the filesystem). Such actions are special because they + are disabled by the 'dry_run' flag. This method takes care of all that bureaucracy for you; all you have to do is supply the function to call and an argument tuple for it (to embody the "external action" being performed), and an optional message to @@ -254,7 +404,7 @@ def execute(func, args, msg=None, verbose=0, dry_run=0): func(*args) -def strtobool(val): +def strtobool (val): """Convert a string representation of truth to true (1) or false (0). True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values @@ -270,13 +420,15 @@ def strtobool(val): raise ValueError("invalid truth value %r" % (val,)) -def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, - verbose=1, dry_run=0, direct=None): +def byte_compile (py_files, + optimize=0, force=0, + prefix=None, base_dir=None, + verbose=1, dry_run=0, + direct=None): """Byte-compile a collection of Python source files to either .pyc - or .pyo files in the same directory. - - 'py_files' is a list of files to compile; any files that don't end in - ".py" are silently skipped. 'optimize' must be one of the following: + or .pyo files in the same directory. 'py_files' is a list of files + to compile; any files that don't end in ".py" are silently skipped. + 'optimize' must be one of the following: 0 - don't optimize (generate .pyc) 1 - normal optimization (like "python -O") 2 - extra optimization (like "python -OO") @@ -392,8 +544,8 @@ byte_compile(files, optimize=%r, force=%r, dfile = file if prefix: if file[:len(prefix)] != prefix: - raise ValueError("invalid prefix: filename %r doesn't " - "start with %r" % (file, prefix)) + raise ValueError("invalid prefix: filename %r doesn't start with %r" + % (file, prefix)) dfile = dfile[len(prefix):] if base_dir: dfile = os.path.join(base_dir, dfile) @@ -408,8 +560,9 @@ byte_compile(files, optimize=%r, force=%r, log.debug("skipping byte-compilation of %s to %s", file, cfile_base) +# byte_compile () -def rfc822_escape(header): +def rfc822_escape (header): """Return a version of the string escaped for inclusion in an RFC-822 header, by ensuring there are 8 spaces space after each newline. """ @@ -417,56 +570,6 @@ def rfc822_escape(header): sep = '\n' + 8 * ' ' return sep.join(lines) -_RE_VERSION = re.compile(b'(\d+\.\d+(\.\d+)*)') -_MAC_OS_X_LD_VERSION = re.compile(b'^@\(#\)PROGRAM:ld PROJECT:ld64-((\d+)(\.\d+)*)') - -def _find_ld_version(): - """Finds the ld version. The version scheme differs under Mac OSX.""" - if sys.platform == 'darwin': - return _find_exe_version('ld -v', _MAC_OS_X_LD_VERSION) - else: - return _find_exe_version('ld -v') - -def _find_exe_version(cmd, pattern=_RE_VERSION): - """Find the version of an executable by running `cmd` in the shell. - - `pattern` is a compiled regular expression. If not provided, default - to _RE_VERSION. If the command is not found, or the output does not - match the mattern, returns None. - """ - from subprocess import Popen, PIPE - executable = cmd.split()[0] - if find_executable(executable) is None: - return None - pipe = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) - try: - stdout, stderr = pipe.stdout.read(), pipe.stderr.read() - finally: - pipe.stdout.close() - pipe.stderr.close() - # some commands like ld under MacOS X, will give the - # output in the stderr, rather than stdout. - if stdout != b'': - out_string = stdout - else: - out_string = stderr - - result = pattern.search(out_string) - if result is None: - return None - return LooseVersion(result.group(1).decode()) - -def get_compiler_versions(): - """Returns a tuple providing the versions of gcc, ld and dllwrap - - For each command, if a command is not found, None is returned. - Otherwise a LooseVersion instance is returned. - """ - gcc = _find_exe_version('gcc -dumpversion') - ld = _find_ld_version() - dllwrap = _find_exe_version('dllwrap --version') - return gcc, ld, dllwrap - # 2to3 support def run_2to3(files, fixer_names=None, options=None, explicit=None): -- cgit v1.2.1 From 575e9faa64b8e84e15de1124de71c3471b966327 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Fri, 23 Jul 2010 09:43:17 +0000 Subject: Ensure that the Makefile variable expansion in distutils.sysconfig matches that in the toplevel sysconfig module. Without this patch universal builds on OSX are broken. Als add a test that checks that the two version of get_config_vars agree on important values. --- sysconfig.py | 23 +++++++++++++++++++++++ tests/test_sysconfig.py | 9 +++++++++ 2 files changed, 32 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index 0fbd5412..48f3fe4d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -300,6 +300,12 @@ def parse_makefile(fn, g=None): else: done[n] = v + # Variables with a 'PY_' prefix in the makefile. These need to + # be made available without that prefix through sysconfig. + # Special care is needed to ensure that variable expansion works, even + # if the expansion uses the name without a prefix. + renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') + # do variable interpolation here while notdone: for name in list(notdone): @@ -316,6 +322,16 @@ def parse_makefile(fn, g=None): elif n in os.environ: # do it like make: fall back to environment item = os.environ[n] + + elif n in renamed_variables: + if name.startswith('PY_') and name[3:] in renamed_variables: + item = "" + + elif 'PY_' + n in notdone: + found = False + + else: + item = str(done['PY_' + n]) else: done[n] = item = "" if found: @@ -330,6 +346,13 @@ def parse_makefile(fn, g=None): else: done[name] = value del notdone[name] + + if name.startswith('PY_') \ + and name[3:] in renamed_variables: + + name = name[3:] + if name not in done: + done[name] = value else: # bogus variable reference; just drop it since we can't deal del notdone[name] diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index edc755ed..0167e0f1 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -93,6 +93,15 @@ class SysconfigTestCase(support.EnvironGuard, 'OTHER': 'foo'}) + def test_sysconfig_module(self): + import sysconfig as global_sysconfig + self.assertEquals(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS')) + self.assertEquals(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS')) + self.assertEquals(global_sysconfig.get_config_var('LDSHARED'),sysconfig.get_config_var('LDSHARED')) + self.assertEquals(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC')) + + + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(SysconfigTestCase)) -- cgit v1.2.1 From 022d6c31d81f8d4f42776503786dbfa486417322 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 31 Jul 2010 08:56:11 +0000 Subject: Bump versions and review NEWS file. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 18b02fc6..7b222791 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a0" +__version__ = "3.2a1" #--end constants-- -- cgit v1.2.1 From b9efa362d457b32845e11e0e03f377472112c0f1 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 31 Jul 2010 21:54:24 +0000 Subject: #8292: Fix three instances of truth tests on return values of filter() (which is always true in Python 3). --- command/sdist.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index bb210612..f51d72fa 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -240,8 +240,7 @@ class sdist(Command): optional = ['test/test*.py', 'setup.cfg'] for pattern in optional: files = filter(os.path.isfile, glob(pattern)) - if files: - self.filelist.extend(files) + self.filelist.extend(files) # build_py is used to get: # - python modules -- cgit v1.2.1 From dd1e83e827cd6658e31699039aae197149e4557e Mon Sep 17 00:00:00 2001 From: "R. David Murray" Date: Sun, 1 Aug 2010 01:53:52 +0000 Subject: Merged revisions 75659 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk Only the try/except was backported; owner and group were added in 2.7, as was the test file. ........ r75659 | tarek.ziade | 2009-10-24 09:29:44 -0400 (Sat, 24 Oct 2009) | 1 line #7066 - Fixed distutils.archive_util.make_archive behavior so it restores the cwd ........ --- archive_util.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/archive_util.py b/archive_util.py index 264e66fa..251c0df4 100644 --- a/archive_util.py +++ b/archive_util.py @@ -162,9 +162,12 @@ def make_archive (base_name, format, kwargs[arg] = val filename = apply(func, (base_name, base_dir), kwargs) - if root_dir is not None: - log.debug("changing back to '%s'", save_cwd) - os.chdir(save_cwd) + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + log.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) return filename -- cgit v1.2.1 From f5fa4f4f916ef28498df9c9cb54fb3f563d824bd Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 1 Aug 2010 19:07:28 +0000 Subject: Merged revisions 83371,83390 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83371 | georg.brandl | 2010-07-31 23:54:24 +0200 (Sa, 31 Jul 2010) | 1 line #8292: Fix three instances of truth tests on return values of filter() (which is always true in Python 3). ........ r83390 | georg.brandl | 2010-08-01 10:07:49 +0200 (So, 01 Aug 2010) | 1 line #8230: make Lib/test/sortperf.py run on Python 3. ........ --- command/sdist.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index bb210612..f51d72fa 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -240,8 +240,7 @@ class sdist(Command): optional = ['test/test*.py', 'setup.cfg'] for pattern in optional: files = filter(os.path.isfile, glob(pattern)) - if files: - self.filelist.extend(files) + self.filelist.extend(files) # build_py is used to get: # - python modules -- cgit v1.2.1 From 8e471803385c10cd1f4f98d669826eb388cc6476 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 2 Aug 2010 19:16:34 +0000 Subject: #7973: Fix distutils options spelling. --- command/bdist_msi.py | 2 +- command/bdist_wininst.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index c4be47b5..8a458d85 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -148,7 +148,7 @@ class bdist_msi(Command): if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: raise DistutilsOptionError( - "target version can only be %s, or the '--skip_build'" + "target version can only be %s, or the '--skip-build'" " option must be specified" % (short_version,)) else: self.versions = list(self.all_versions) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index d6d01c63..3aa1dac7 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -89,7 +89,7 @@ class bdist_wininst(Command): short_version = get_python_version() if self.target_version and self.target_version != short_version: raise DistutilsOptionError( - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,)) self.target_version = short_version -- cgit v1.2.1 From df831fea4bc33d771c8e6a1fb72533f71d7c2464 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 2 Aug 2010 20:26:41 +0000 Subject: Merged revisions 79558 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r79558 | florent.xicluna | 2010-04-01 21:17:09 +0300 (Thu, 01 Apr 2010) | 2 lines #7092: Fix some -3 warnings, and fix Lib/platform.py when the path contains a double-quote. ........ --- command/build_ext.py | 2 +- util.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 8248089f..aeb6b744 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -676,7 +676,7 @@ class build_ext (Command): # extensions in debug_mode are named 'module_d.pyd' under windows so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: - return apply(os.path.join, ext_path) + '_d' + so_ext + return os.path.join(*ext_path) + '_d' + so_ext return os.path.join(*ext_path) + so_ext def get_export_symbols (self, ext): diff --git a/util.py b/util.py index c4a8711d..90f68d82 100644 --- a/util.py +++ b/util.py @@ -205,7 +205,7 @@ def convert_path (pathname): paths.remove('.') if not paths: return os.curdir - return apply(os.path.join, paths) + return os.path.join(*paths) # convert_path () -- cgit v1.2.1 From 5a5a41af466e35970c94b0ab039d62f68581de05 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 2 Aug 2010 21:35:06 +0000 Subject: Merged revisions 78757 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r78757 | florent.xicluna | 2010-03-07 14:14:25 +0200 (Sun, 07 Mar 2010) | 2 lines Fix some py3k warnings in the standard library. ........ --- util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.py b/util.py index 90f68d82..e92bef1b 100644 --- a/util.py +++ b/util.py @@ -405,7 +405,7 @@ def execute (func, args, msg=None, verbose=0, dry_run=0): log.info(msg) if not dry_run: - apply(func, args) + func(*args) def strtobool (val): -- cgit v1.2.1 From 231e1cc9526df4a63a21c09b41015b05d98cd7f9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 2 Aug 2010 21:44:25 +0000 Subject: Merged revisions 83536,83546-83548,83550,83554-83555,83558,83563,83565,83571,83574-83575 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r83536 | georg.brandl | 2010-08-02 19:49:25 +0200 (Mo, 02 Aug 2010) | 1 line #8578: mention danger of not incref'ing weak referenced object. ........ r83546 | georg.brandl | 2010-08-02 21:16:34 +0200 (Mo, 02 Aug 2010) | 1 line #7973: Fix distutils options spelling. ........ r83547 | georg.brandl | 2010-08-02 21:19:26 +0200 (Mo, 02 Aug 2010) | 1 line #7386: add example that shows that trailing path separators are stripped. ........ r83548 | georg.brandl | 2010-08-02 21:23:34 +0200 (Mo, 02 Aug 2010) | 1 line #8172: how does one use a property? ........ r83550 | georg.brandl | 2010-08-02 21:32:43 +0200 (Mo, 02 Aug 2010) | 1 line #9451: strengthen warning about __*__ special name usage. ........ r83554 | georg.brandl | 2010-08-02 21:43:05 +0200 (Mo, 02 Aug 2010) | 1 line #7280: note about nasmw.exe. ........ r83555 | georg.brandl | 2010-08-02 21:44:48 +0200 (Mo, 02 Aug 2010) | 1 line #8861: remove unused variable. ........ r83558 | georg.brandl | 2010-08-02 22:05:19 +0200 (Mo, 02 Aug 2010) | 1 line #8648: document UTF-7 codec functions. ........ r83563 | georg.brandl | 2010-08-02 22:21:21 +0200 (Mo, 02 Aug 2010) | 1 line #9037: add example how to raise custom exceptions from C code. ........ r83565 | georg.brandl | 2010-08-02 22:27:20 +0200 (Mo, 02 Aug 2010) | 1 line #9111: document that do_help() looks at docstrings. ........ r83571 | georg.brandl | 2010-08-02 22:44:34 +0200 (Mo, 02 Aug 2010) | 1 line Clarify that abs() is not a namespace. ........ r83574 | georg.brandl | 2010-08-02 22:47:56 +0200 (Mo, 02 Aug 2010) | 1 line #6867: epoll.register() returns None. ........ r83575 | georg.brandl | 2010-08-02 22:52:10 +0200 (Mo, 02 Aug 2010) | 1 line #9238: zipfile does handle archive comments. ........ --- command/bdist_msi.py | 2 +- command/bdist_wininst.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 72578498..ded837d7 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -148,7 +148,7 @@ class bdist_msi (Command): if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,) else: self.versions = list(self.all_versions) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 88c0532f..a31a5f7b 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -95,7 +95,7 @@ class bdist_wininst (Command): short_version = get_python_version() if self.target_version and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,) self.target_version = short_version -- cgit v1.2.1 From 59c26754fc99abfc3cf85a7b435eb0ec14333812 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 2 Aug 2010 21:45:43 +0000 Subject: Merged revisions 83593 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/release27-maint ................ r83593 | georg.brandl | 2010-08-02 23:44:25 +0200 (Mo, 02 Aug 2010) | 57 lines Merged revisions 83536,83546-83548,83550,83554-83555,83558,83563,83565,83571,83574-83575 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r83536 | georg.brandl | 2010-08-02 19:49:25 +0200 (Mo, 02 Aug 2010) | 1 line #8578: mention danger of not incref'ing weak referenced object. ........ r83546 | georg.brandl | 2010-08-02 21:16:34 +0200 (Mo, 02 Aug 2010) | 1 line #7973: Fix distutils options spelling. ........ r83547 | georg.brandl | 2010-08-02 21:19:26 +0200 (Mo, 02 Aug 2010) | 1 line #7386: add example that shows that trailing path separators are stripped. ........ r83548 | georg.brandl | 2010-08-02 21:23:34 +0200 (Mo, 02 Aug 2010) | 1 line #8172: how does one use a property? ........ r83550 | georg.brandl | 2010-08-02 21:32:43 +0200 (Mo, 02 Aug 2010) | 1 line #9451: strengthen warning about __*__ special name usage. ........ r83554 | georg.brandl | 2010-08-02 21:43:05 +0200 (Mo, 02 Aug 2010) | 1 line #7280: note about nasmw.exe. ........ r83555 | georg.brandl | 2010-08-02 21:44:48 +0200 (Mo, 02 Aug 2010) | 1 line #8861: remove unused variable. ........ r83558 | georg.brandl | 2010-08-02 22:05:19 +0200 (Mo, 02 Aug 2010) | 1 line #8648: document UTF-7 codec functions. ........ r83563 | georg.brandl | 2010-08-02 22:21:21 +0200 (Mo, 02 Aug 2010) | 1 line #9037: add example how to raise custom exceptions from C code. ........ r83565 | georg.brandl | 2010-08-02 22:27:20 +0200 (Mo, 02 Aug 2010) | 1 line #9111: document that do_help() looks at docstrings. ........ r83571 | georg.brandl | 2010-08-02 22:44:34 +0200 (Mo, 02 Aug 2010) | 1 line Clarify that abs() is not a namespace. ........ r83574 | georg.brandl | 2010-08-02 22:47:56 +0200 (Mo, 02 Aug 2010) | 1 line #6867: epoll.register() returns None. ........ r83575 | georg.brandl | 2010-08-02 22:52:10 +0200 (Mo, 02 Aug 2010) | 1 line #9238: zipfile does handle archive comments. ........ ................ --- command/bdist_msi.py | 2 +- command/bdist_wininst.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index da0b30d8..42271c4d 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -138,7 +138,7 @@ class bdist_msi (Command): if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,) else: self.target_version = short_version diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index d153e2bc..128e1992 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -93,7 +93,7 @@ class bdist_wininst (Command): short_version = get_python_version() if self.target_version and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,) self.target_version = short_version -- cgit v1.2.1 From 95fbccaf635ace75fe1838449f512c46dd5b2e49 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Tue, 3 Aug 2010 07:51:50 +0000 Subject: Merged revisions 79191 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r79191 | florent.xicluna | 2010-03-21 13:50:17 +0200 (Sun, 21 Mar 2010) | 3 lines No more deprecation warnings for distutils.sysconfig, following r78666. But when the "dl" module is available, it gives a py3k deprecation warning. ........ --- archive_util.py | 2 +- command/build_py.py | 4 ++-- dir_util.py | 2 +- filelist.py | 2 +- tests/test_build_ext.py | 5 +++++ 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/archive_util.py b/archive_util.py index 251c0df4..782d4ef9 100644 --- a/archive_util.py +++ b/archive_util.py @@ -160,7 +160,7 @@ def make_archive (base_name, format, func = format_info[0] for (arg,val) in format_info[1]: kwargs[arg] = val - filename = apply(func, (base_name, base_dir), kwargs) + filename = func(base_name, base_dir, **kwargs) try: filename = func(base_name, base_dir, **kwargs) diff --git a/command/build_py.py b/command/build_py.py index 708ef0f3..9f8a759a 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -157,7 +157,7 @@ class build_py (Command): if not self.package_dir: if path: - return apply(os.path.join, path) + return os.path.join(*path) else: return '' else: @@ -184,7 +184,7 @@ class build_py (Command): tail.insert(0, pdir) if tail: - return apply(os.path.join, tail) + return os.path.join(*tail) else: return '' diff --git a/dir_util.py b/dir_util.py index 77f25325..92f49346 100644 --- a/dir_util.py +++ b/dir_util.py @@ -204,7 +204,7 @@ def remove_tree (directory, verbose=0, dry_run=0): _build_cmdtuple(directory, cmdtuples) for cmd in cmdtuples: try: - apply(cmd[0], (cmd[1],)) + cmd[0](cmd[1]) # remove dir from cache if it's already there abspath = os.path.abspath(cmd[1]) if abspath in _path_created: diff --git a/filelist.py b/filelist.py index 88b33c7c..4448d5c5 100644 --- a/filelist.py +++ b/filelist.py @@ -68,7 +68,7 @@ class FileList: sortable_files.sort() self.files = [] for sort_tuple in sortable_files: - self.files.append(apply(os.path.join, sort_tuple)) + self.files.append(os.path.join(*sort_tuple)) # -- Other miscellaneous utility methods --------------------------- diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 5ecfe15b..1ed9d04b 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -349,6 +349,11 @@ class BuildExtTestCase(support.TempdirManager, self.assertEquals(wanted, path) def test_setuptools_compat(self): + try: + # on some platforms, it loads the deprecated "dl" module + test_support.import_module('setuptools_build_ext', deprecated=True) + except test_support.TestSkipped: + return from setuptools_build_ext import build_ext as setuptools_build_ext from setuptools_extension import Extension -- cgit v1.2.1 From 60f0fc889114642ceeba5b3d812c705ea4b97de9 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Tue, 3 Aug 2010 21:18:06 +0000 Subject: - Issue #8447: Make distutils.sysconfig follow symlinks in the path to the interpreter executable. This fixes a failure of test_httpservers on OS X. --- sysconfig.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index bb53315b..46d23ecc 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -25,7 +25,7 @@ EXEC_PREFIX = os.path.normpath(sys.exec_prefix) # Path to the base directory of the project. On Windows the binary may # live in project/PCBuild9. If we're dealing with an x64 Windows build, # it'll live in project/PCbuild/amd64. -project_base = os.path.dirname(os.path.abspath(sys.executable)) +project_base = os.path.dirname(os.path.realpath(sys.executable)) if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) # PC/VS7.1 @@ -74,7 +74,7 @@ def get_python_inc(plat_specific=0, prefix=None): if os.name == "posix": if python_build: - buildir = os.path.dirname(sys.executable) + buildir = os.path.dirname(os.path.realpath(sys.executable)) if plat_specific: # python.h is located in the buildir inc_dir = buildir @@ -222,7 +222,8 @@ def get_config_h_filename(): def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: - return os.path.join(os.path.dirname(sys.executable), "Makefile") + return os.path.join(os.path.dirname(os.path.realpath(sys.executable)), + "Makefile") lib_dir = get_python_lib(plat_specific=1, standard_lib=1) return os.path.join(lib_dir, "config", "Makefile") @@ -459,7 +460,7 @@ def _init_nt(): g['SO'] = '.pyd' g['EXE'] = ".exe" g['VERSION'] = get_python_version().replace(".", "") - g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) + g['BINDIR'] = os.path.dirname(os.path.realpath(sys.executable)) global _config_vars _config_vars = g -- cgit v1.2.1 From 5673d48db60eb63f4da8a53f210656c22b95c7a4 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Tue, 3 Aug 2010 21:33:04 +0000 Subject: Issue #8447: Make distutils.sysconfig follow symlinks in the path to the interpreter executable. This fixes a failure of test_httpservers on OS X. --- sysconfig.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 0fbd5412..9842d26c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -25,7 +25,7 @@ EXEC_PREFIX = os.path.normpath(sys.exec_prefix) # Path to the base directory of the project. On Windows the binary may # live in project/PCBuild9. If we're dealing with an x64 Windows build, # it'll live in project/PCbuild/amd64. -project_base = os.path.dirname(os.path.abspath(sys.executable)) +project_base = os.path.dirname(os.path.realpath(sys.executable)) if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) # PC/VS7.1 @@ -77,7 +77,7 @@ def get_python_inc(plat_specific=0, prefix=None): # the build directory may not be the source directory, we # must use "srcdir" from the makefile to find the "Include" # directory. - base = os.path.dirname(os.path.abspath(sys.executable)) + base = os.path.dirname(os.path.realpath(sys.executable)) if plat_specific: return base else: @@ -223,7 +223,8 @@ def get_config_h_filename(): def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: - return os.path.join(os.path.dirname(sys.executable), "Makefile") + return os.path.join(os.path.dirname(os.path.realpath(sys.executable)), + "Makefile") lib_dir = get_python_lib(plat_specific=1, standard_lib=1) return os.path.join(lib_dir, "config", "Makefile") @@ -442,7 +443,7 @@ def _init_nt(): g['SO'] = '.pyd' g['EXE'] = ".exe" g['VERSION'] = get_python_version().replace(".", "") - g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) + g['BINDIR'] = os.path.dirname(os.path.realpath(sys.executable)) global _config_vars _config_vars = g -- cgit v1.2.1 From 499b7fdd90857e215a9abcc6a56971e04e1aa1a0 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 3 Aug 2010 22:39:42 +0000 Subject: Bumping to 2.6.6 rc 1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 9e7ab903..a861b3fb 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.5" +__version__ = "2.6.6rc1" #--end constants-- -- cgit v1.2.1 From a0e52b4f32d7383d92658e921017fe4b493af43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 14 Aug 2010 02:07:26 +0000 Subject: Revert regression from r81256 (with release manager approval, see #8688) --- command/sdist.py | 86 ++++++++++++++++++++++++++++++++++++----------------- tests/test_sdist.py | 62 -------------------------------------- 2 files changed, 59 insertions(+), 89 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 535b8d2a..a366d1ea 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -57,8 +57,7 @@ class sdist (Command): "just regenerate the manifest and then stop " "(implies --force-manifest)"), ('force-manifest', 'f', - "forcibly regenerate the manifest and carry on as usual. " - "Deprecated: now the manifest is always regenerated."), + "forcibly regenerate the manifest and carry on as usual"), ('formats=', None, "formats for source distribution (comma-separated list)"), ('keep-temp', 'k', @@ -191,34 +190,67 @@ class sdist (Command): distribution, and put it in 'self.filelist'. This might involve reading the manifest template (and writing the manifest), or just reading the manifest, or just using the default file set -- it all - depends on the user's options. + depends on the user's options and the state of the filesystem. """ - # new behavior: - # the file list is recalculated everytime because - # even if MANIFEST.in or setup.py are not changed - # the user might have added some files in the tree that - # need to be included. - # - # This makes --force the default and only behavior. - template_exists = os.path.isfile(self.template) - if not template_exists: - self.warn(("manifest template '%s' does not exist " + - "(using default file list)") % - self.template) - self.filelist.findall() - - if self.use_defaults: - self.add_defaults() + # If we have a manifest template, see if it's newer than the + # manifest; if so, we'll regenerate the manifest. + template_exists = os.path.isfile(self.template) if template_exists: - self.read_template() - - if self.prune: - self.prune_file_list() - - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() + template_newer = dep_util.newer(self.template, self.manifest) + + # The contents of the manifest file almost certainly depend on the + # setup script as well as the manifest template -- so if the setup + # script is newer than the manifest, we'll regenerate the manifest + # from the template. (Well, not quite: if we already have a + # manifest, but there's no template -- which will happen if the + # developer elects to generate a manifest some other way -- then we + # can't regenerate the manifest, so we don't.) + self.debug_print("checking if %s newer than %s" % + (self.distribution.script_name, self.manifest)) + setup_newer = dep_util.newer(self.distribution.script_name, + self.manifest) + + # cases: + # 1) no manifest, template exists: generate manifest + # (covered by 2a: no manifest == template newer) + # 2) manifest & template exist: + # 2a) template or setup script newer than manifest: + # regenerate manifest + # 2b) manifest newer than both: + # do nothing (unless --force or --manifest-only) + # 3) manifest exists, no template: + # do nothing (unless --force or --manifest-only) + # 4) no manifest, no template: generate w/ warning ("defaults only") + + manifest_outofdate = (template_exists and + (template_newer or setup_newer)) + force_regen = self.force_manifest or self.manifest_only + manifest_exists = os.path.isfile(self.manifest) + neither_exists = (not template_exists and not manifest_exists) + + # Regenerate the manifest if necessary (or if explicitly told to) + if manifest_outofdate or neither_exists or force_regen: + if not template_exists: + self.warn(("manifest template '%s' does not exist " + + "(using default file list)") % + self.template) + self.filelist.findall() + + if self.use_defaults: + self.add_defaults() + if template_exists: + self.read_template() + if self.prune: + self.prune_file_list() + + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() + + # Don't regenerate the manifest, just read it in. + else: + self.read_manifest() # get_file_list () diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 6b8784ac..e322c138 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,7 +29,6 @@ class sdistTestCase(PyPIRCCommandTestCase): super(sdistTestCase, self).setUp() self.old_path = os.getcwd() self.temp_pkg = os.path.join(self.mkdtemp(), 'temppkg') - self.tmp_dir = self.mkdtemp() def tearDown(self): os.chdir(self.old_path) @@ -152,67 +151,6 @@ class sdistTestCase(PyPIRCCommandTestCase): self.assertEquals(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) - def get_cmd(self, metadata=None): - """Returns a cmd""" - if metadata is None: - metadata = {'name': 'fake', 'version': '1.0', - 'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx'} - dist = Distribution(metadata) - dist.script_name = 'setup.py' - dist.packages = ['somecode'] - dist.include_package_data = True - cmd = sdist(dist) - cmd.dist_dir = 'dist' - def _warn(*args): - pass - cmd.warn = _warn - return dist, cmd - - def test_get_file_list(self): - # make sure MANIFEST is recalculated - dist, cmd = self.get_cmd() - - os.chdir(self.tmp_dir) - - # filling data_files by pointing files in package_data - os.mkdir(os.path.join(self.tmp_dir, 'somecode')) - self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#') - self.write_file((self.tmp_dir, 'somecode', 'one.py'), '#') - cmd.ensure_finalized() - cmd.run() - - f = open(cmd.manifest) - try: - manifest = [line.strip() for line in f.read().split('\n') - if line.strip() != ''] - finally: - f.close() - - self.assertEquals(len(manifest), 2) - - # adding a file - self.write_file((self.tmp_dir, 'somecode', 'two.py'), '#') - - # make sure build_py is reinitinialized, like a fresh run - build_py = dist.get_command_obj('build_py') - build_py.finalized = False - build_py.ensure_finalized() - - cmd.run() - - f = open(cmd.manifest) - try: - manifest2 = [line.strip() for line in f.read().split('\n') - if line.strip() != ''] - finally: - f.close() - - # do we have the new file in MANIFEST ? - self.assertEquals(len(manifest2), 3) - self.assert_('two.py' in manifest2[-1]) - - def test_suite(): return unittest.makeSuite(sdistTestCase) -- cgit v1.2.1 From aefeb97682f95d9ae4df1c41a5bd8f1fd047c016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 14 Aug 2010 02:30:34 +0000 Subject: Use a marker in generated MANIFEST files, don't touch files without it. Fixes #8688. --- command/sdist.py | 17 +++++++++++++++-- tests/test_sdist.py | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index f51d72fa..818a4526 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -335,8 +335,21 @@ class sdist(Command): by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. """ - self.execute(file_util.write_file, - (self.manifest, self.filelist.files), + if os.path.isfile(self.manifest): + fp = open(self.manifest) + try: + first_line = fp.readline() + finally: + fp.close() + + if first_line != '# file GENERATED by distutils, do NOT edit\n': + log.info("not writing to manually maintained " + "manifest file '%s'" % self.manifest) + return + + content = self.filelist.files[:] + content.insert(0, '# file GENERATED by distutils, do NOT edit') + self.execute(file_util.write_file, (self.manifest, content), "writing manifest file '%s'" % self.manifest) def read_manifest(self): diff --git a/tests/test_sdist.py b/tests/test_sdist.py index f95035df..209aa59b 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,6 +29,7 @@ setup(name='fake') """ MANIFEST = """\ +# file GENERATED by distutils, do NOT edit README inroot.txt setup.py @@ -294,7 +295,7 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: f.close() - self.assertEquals(len(manifest), 4) + self.assertEquals(len(manifest), 5) # adding a file self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') @@ -314,9 +315,40 @@ class SDistTestCase(PyPIRCCommandTestCase): f.close() # do we have the new file in MANIFEST ? - self.assertEquals(len(manifest2), 5) + self.assertEquals(len(manifest2), 6) self.assertIn('doc2.txt', manifest2[-1]) + def test_manifest_marker(self): + # check that autogenerated MANIFESTs have a marker + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest[0], + '# file GENERATED by distutils, do NOT edit') + + def test_manual_manifest(self): + # check that a MANIFEST without a marker is left alone + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest, ['README.manual']) def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From 6d8137e922dd736506976931c7acec8953025d2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 14 Aug 2010 02:36:26 +0000 Subject: Merged revisions 83993 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r83993 | eric.araujo | 2010-08-14 04:30:34 +0200 (sam., 14 août 2010) | 2 lines Use a marker in generated MANIFEST files, don't touch files without it. Fixes #8688. ........ --- command/sdist.py | 17 +++++++++++++++-- tests/test_sdist.py | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index f51d72fa..818a4526 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -335,8 +335,21 @@ class sdist(Command): by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. """ - self.execute(file_util.write_file, - (self.manifest, self.filelist.files), + if os.path.isfile(self.manifest): + fp = open(self.manifest) + try: + first_line = fp.readline() + finally: + fp.close() + + if first_line != '# file GENERATED by distutils, do NOT edit\n': + log.info("not writing to manually maintained " + "manifest file '%s'" % self.manifest) + return + + content = self.filelist.files[:] + content.insert(0, '# file GENERATED by distutils, do NOT edit') + self.execute(file_util.write_file, (self.manifest, content), "writing manifest file '%s'" % self.manifest) def read_manifest(self): diff --git a/tests/test_sdist.py b/tests/test_sdist.py index f95035df..209aa59b 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,6 +29,7 @@ setup(name='fake') """ MANIFEST = """\ +# file GENERATED by distutils, do NOT edit README inroot.txt setup.py @@ -294,7 +295,7 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: f.close() - self.assertEquals(len(manifest), 4) + self.assertEquals(len(manifest), 5) # adding a file self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') @@ -314,9 +315,40 @@ class SDistTestCase(PyPIRCCommandTestCase): f.close() # do we have the new file in MANIFEST ? - self.assertEquals(len(manifest2), 5) + self.assertEquals(len(manifest2), 6) self.assertIn('doc2.txt', manifest2[-1]) + def test_manifest_marker(self): + # check that autogenerated MANIFESTs have a marker + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest[0], + '# file GENERATED by distutils, do NOT edit') + + def test_manual_manifest(self): + # check that a MANIFEST without a marker is left alone + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest, ['README.manual']) def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From 517674a725c862dc2c5b0905b4f341d53e93f66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 14 Aug 2010 03:07:46 +0000 Subject: Merged revisions 83993 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r83993 | eric.araujo | 2010-08-14 04:30:34 +0200 (sam., 14 août 2010) | 2 lines Use a marker in generated MANIFEST files, don't touch files without it. Fixes #8688. ........ --- command/sdist.py | 17 +++++++++++++++-- tests/test_sdist.py | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 087ae9dc..f2d2f94b 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -349,8 +349,21 @@ class sdist(Command): by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. """ - self.execute(file_util.write_file, - (self.manifest, self.filelist.files), + if os.path.isfile(self.manifest): + fp = open(self.manifest) + try: + first_line = fp.readline() + finally: + fp.close() + + if first_line != '# file GENERATED by distutils, do NOT edit\n': + log.info("not writing to manually maintained " + "manifest file '%s'" % self.manifest) + return + + content = self.filelist.files[:] + content.insert(0, '# file GENERATED by distutils, do NOT edit') + self.execute(file_util.write_file, (self.manifest, content), "writing manifest file '%s'" % self.manifest) def read_manifest(self): diff --git a/tests/test_sdist.py b/tests/test_sdist.py index f83b9f29..87adb458 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -45,6 +45,7 @@ setup(name='fake') """ MANIFEST = """\ +# file GENERATED by distutils, do NOT edit README inroot.txt setup.py @@ -364,7 +365,7 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: f.close() - self.assertEquals(len(manifest), 4) + self.assertEquals(len(manifest), 5) # adding a file self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') @@ -384,9 +385,40 @@ class SDistTestCase(PyPIRCCommandTestCase): f.close() # do we have the new file in MANIFEST ? - self.assertEquals(len(manifest2), 5) + self.assertEquals(len(manifest2), 6) self.assertIn('doc2.txt', manifest2[-1]) + def test_manifest_marker(self): + # check that autogenerated MANIFESTs have a marker + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest[0], + '# file GENERATED by distutils, do NOT edit') + + def test_manual_manifest(self): + # check that a MANIFEST without a marker is left alone + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest, ['README.manual']) def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From 55096b84cc9e0882c64d0772880b9d08f2f9e41e Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 16 Aug 2010 22:19:57 +0000 Subject: Bumping to 2.6.6rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a861b3fb..c456c748 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.6rc1" +__version__ = "2.6.6rc2" #--end constants-- -- cgit v1.2.1 From e870a5993437de693e68c83e15ed02f3b7af1575 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 23 Aug 2010 23:37:56 +0000 Subject: 2.6.6 final. \o/ --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c456c748..90f96fea 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.6rc2" +__version__ = "2.6.6" #--end constants-- -- cgit v1.2.1 From 36cae28e19a68627c873ff982d2a2dd3279cea9c Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Mon, 30 Aug 2010 14:05:50 +0000 Subject: remove pointless coding cookies --- command/bdist_msi.py | 1 - tests/test_dist.py | 1 - 2 files changed, 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 8a458d85..b11957a7 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright (C) 2005, 2006 Martin von Löwis # Licensed to PSF under a Contributor Agreement. # The bdist_wininst command proper diff --git a/tests/test_dist.py b/tests/test_dist.py index 3b7637f3..007803e1 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -1,4 +1,3 @@ -# -*- coding: utf8 -*- """Tests for distutils.dist.""" import os import io -- cgit v1.2.1 From b202a1a25f6d7ab9707910d6e36df58e8f9a2508 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 3 Sep 2010 18:30:30 +0000 Subject: PEP 3149 is accepted. http://mail.python.org/pipermail/python-dev/2010-September/103408.html --- tests/test_build_ext.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index b7cdc20a..52479d7d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -323,8 +323,8 @@ class BuildExtTestCase(TempdirManager, finally: os.chdir(old_wd) self.assertTrue(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], - sysconfig.get_config_var('SO')) + so_ext = sysconfig.get_config_var('SO') + self.assertTrue(so_file.endswith(so_ext)) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, other_tmp_dir) @@ -333,8 +333,7 @@ class BuildExtTestCase(TempdirManager, cmd.run() so_file = cmd.get_outputs()[0] self.assertTrue(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], - sysconfig.get_config_var('SO')) + self.assertTrue(so_file.endswith(so_ext)) so_dir = os.path.dirname(so_file) self.assertEquals(so_dir, cmd.build_lib) -- cgit v1.2.1 From a51303cf88483095772fd60fc7184fceafc9073c Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 5 Sep 2010 08:30:40 +0000 Subject: Bump to 3.2a2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7b222791..92ed8f4d 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a1" +__version__ = "3.2a2" #--end constants-- -- cgit v1.2.1 From 0aeceb2acf76e1ecb122098bc9a797218568bece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 7 Sep 2010 22:11:52 +0000 Subject: Fix eon-old bug in build_clib options (#1718574) --- command/build_clib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index 258d7c10..428011a6 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -32,9 +32,9 @@ class build_clib(Command): description = "build C/C++ libraries used by Python extensions" user_options = [ - ('build-clib', 'b', + ('build-clib=', 'b', "directory to build C/C++ libraries to"), - ('build-temp', 't', + ('build-temp=', 't', "directory to put temporary build by-products"), ('debug', 'g', "compile with debugging information"), -- cgit v1.2.1 From 53eb48ea76c6fe4f6ee380f926ecac6136d2eee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 7 Sep 2010 22:17:04 +0000 Subject: Merged revisions 84608 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84608 | eric.araujo | 2010-09-08 00:11:52 +0200 (mer., 08 sept. 2010) | 2 lines Fix eon-old bug in build_clib options (#1718574) ........ --- command/build_clib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index 258d7c10..428011a6 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -32,9 +32,9 @@ class build_clib(Command): description = "build C/C++ libraries used by Python extensions" user_options = [ - ('build-clib', 'b', + ('build-clib=', 'b', "directory to build C/C++ libraries to"), - ('build-temp', 't', + ('build-temp=', 't', "directory to put temporary build by-products"), ('debug', 'g', "compile with debugging information"), -- cgit v1.2.1 From 878ab41a641d75ce127fda7857f7b271bd19efac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 7 Sep 2010 22:18:34 +0000 Subject: Merged revisions 84608 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84608 | eric.araujo | 2010-09-08 00:11:52 +0200 (mer., 08 sept. 2010) | 2 lines Fix eon-old bug in build_clib options (#1718574) ........ --- command/build_clib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/build_clib.py b/command/build_clib.py index 8d49de75..98a1726f 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -32,9 +32,9 @@ class build_clib(Command): description = "build C/C++ libraries used by Python extensions" user_options = [ - ('build-clib', 'b', + ('build-clib=', 'b', "directory to build C/C++ libraries to"), - ('build-temp', 't', + ('build-temp=', 't', "directory to put temporary build by-products"), ('debug', 'g', "compile with debugging information"), -- cgit v1.2.1 From fe763ddd66aa35cad53dd175d483ed37f26726d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 7 Sep 2010 23:08:57 +0000 Subject: Fix incorrect use of Command.announce (#9199) --- command/upload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index f602fbeb..41df1277 100644 --- a/command/upload.py +++ b/command/upload.py @@ -194,4 +194,5 @@ class upload(PyPIRCCommand): self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - self.announce('-'*75, r.read(), '-'*75) + msg = ''.join('-' * 75, r.read(), '-' * 75) + self.announce(msg, log.INFO) -- cgit v1.2.1 From 77ea1a964b3e95dddca07562f1c724a9ef71cdf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 7 Sep 2010 23:09:44 +0000 Subject: Merged revisions 84611 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84611 | eric.araujo | 2010-09-08 01:08:57 +0200 (mer., 08 sept. 2010) | 2 lines Fix incorrect use of Command.announce (#9199) ........ --- command/upload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index f602fbeb..41df1277 100644 --- a/command/upload.py +++ b/command/upload.py @@ -194,4 +194,5 @@ class upload(PyPIRCCommand): self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - self.announce('-'*75, r.read(), '-'*75) + msg = ''.join('-' * 75, r.read(), '-' * 75) + self.announce(msg, log.INFO) -- cgit v1.2.1 From 4123cdd0eebf95195be93eaa84c4b62d587a9028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 7 Sep 2010 23:12:59 +0000 Subject: Merged revisions 84611 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84611 | eric.araujo | 2010-09-08 01:08:57 +0200 (mer., 08 sept. 2010) | 2 lines Fix incorrect use of Command.announce (#9199) ........ --- command/upload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 18a10a0b..4b3ed3f9 100644 --- a/command/upload.py +++ b/command/upload.py @@ -186,4 +186,5 @@ class upload(PyPIRCCommand): self.announce('Upload failed (%s): %s' % (status, reason), log.ERROR) if self.show_response: - self.announce('-'*75, result.read(), '-'*75) + msg = ''.join('-' * 75, r.read(), '-' * 75) + self.announce(msg, log.INFO) -- cgit v1.2.1 From 3136e10a506a31f07107f7d08c96074d2709626b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 8 Sep 2010 00:00:45 +0000 Subject: Follow-up to #9199: Fix str.join use, add newlines. Thanks to Konrad Delong for writing a test for upload_docs --show-response in distutils2, letting me catch my mistake. --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 41df1277..99e03d74 100644 --- a/command/upload.py +++ b/command/upload.py @@ -194,5 +194,5 @@ class upload(PyPIRCCommand): self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - msg = ''.join('-' * 75, r.read(), '-' * 75) + msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) self.announce(msg, log.INFO) -- cgit v1.2.1 From 7a18c28655272f4da8f115d20b5b5f975699bf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 8 Sep 2010 00:02:00 +0000 Subject: Merged revisions 84614 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84614 | eric.araujo | 2010-09-08 02:00:45 +0200 (mer., 08 sept. 2010) | 5 lines Follow-up to #9199: Fix str.join use, add newlines. Thanks to Konrad Delong for writing a test for upload_docs --show-response in distutils2, letting me catch my mistake. ........ --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 41df1277..99e03d74 100644 --- a/command/upload.py +++ b/command/upload.py @@ -194,5 +194,5 @@ class upload(PyPIRCCommand): self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - msg = ''.join('-' * 75, r.read(), '-' * 75) + msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) self.announce(msg, log.INFO) -- cgit v1.2.1 From e0136d1da875b82d0cb33b89430ca52a4f5656fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 8 Sep 2010 00:02:29 +0000 Subject: Merged revisions 84614 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84614 | eric.araujo | 2010-09-08 02:00:45 +0200 (mer., 08 sept. 2010) | 5 lines Follow-up to #9199: Fix str.join use, add newlines. Thanks to Konrad Delong for writing a test for upload_docs --show-response in distutils2, letting me catch my mistake. ........ --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 4b3ed3f9..c3f19d20 100644 --- a/command/upload.py +++ b/command/upload.py @@ -186,5 +186,5 @@ class upload(PyPIRCCommand): self.announce('Upload failed (%s): %s' % (status, reason), log.ERROR) if self.show_response: - msg = ''.join('-' * 75, r.read(), '-' * 75) + msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) self.announce(msg, log.INFO) -- cgit v1.2.1 From 2f09caa926e12dbc91a0652c0e3d8ab494046579 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 10 Sep 2010 19:44:44 +0000 Subject: =?UTF-8?q?Issue=20#941346:=20Improve=20the=20build=20process=20un?= =?UTF-8?q?der=20AIX=20and=20allow=20Python=20to=20be=20built=20as=20a=20s?= =?UTF-8?q?hared=20library.=20=20Patch=20by=20S=C3=A9bastien=20Sabl=C3=A9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- command/build_ext.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index bd61bc56..4e664642 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -748,6 +748,9 @@ class build_ext(Command): elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries + elif sys.platform[:3] == 'aix': + # Don't use the default code below + return ext.libraries else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): -- cgit v1.2.1 From ba76a4b8033a52729a7ab55129b9113ccca9a2e4 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 10 Sep 2010 19:55:19 +0000 Subject: Merged revisions 84680 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r84680 | antoine.pitrou | 2010-09-10 21:44:44 +0200 (ven., 10 sept. 2010) | 4 lines Issue #941346: Improve the build process under AIX and allow Python to be built as a shared library. Patch by Sébastien Sablé. ........ --- command/build_ext.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index bd61bc56..4e664642 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -748,6 +748,9 @@ class build_ext(Command): elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries + elif sys.platform[:3] == 'aix': + # Don't use the default code below + return ext.libraries else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): -- cgit v1.2.1 From 8bede4b22db5241e8e141a6b4c2d718b02f4a57b Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 10 Sep 2010 20:03:17 +0000 Subject: Merged revisions 84680 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r84680 | antoine.pitrou | 2010-09-10 21:44:44 +0200 (ven., 10 sept. 2010) | 4 lines Issue #941346: Improve the build process under AIX and allow Python to be built as a shared library. Patch by Sébastien Sablé. ........ --- command/build_ext.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index aeb6b744..5b3be6fb 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -753,7 +753,9 @@ class build_ext (Command): elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries - + elif sys.platform[:3] == 'aix': + # Don't use the default code below + return ext.libraries else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): -- cgit v1.2.1 From 944d8d8d40616d46d4ebf34a58b2fd75bd317589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 11 Sep 2010 15:28:56 +0000 Subject: Fix typo in option name --- command/sdist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index 818a4526..1118060c 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -73,7 +73,7 @@ class sdist(Command): ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), - ('medata-check', None, + ('metadata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), ] -- cgit v1.2.1 From 5a1680dbd2d366418cd676f6b979a0589b645950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 11 Sep 2010 15:30:19 +0000 Subject: Merged revisions 84711 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84711 | eric.araujo | 2010-09-11 17:28:56 +0200 (sam., 11 sept. 2010) | 2 lines Fix typo in option name ........ --- command/sdist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index 818a4526..1118060c 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -73,7 +73,7 @@ class sdist(Command): ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), - ('medata-check', None, + ('metadata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), ] -- cgit v1.2.1 From 095b65f89a72607c6ec21acfdc655633a9aecb99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 11 Sep 2010 15:31:13 +0000 Subject: Merged revisions 84711 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84711 | eric.araujo | 2010-09-11 17:28:56 +0200 (sam., 11 sept. 2010) | 2 lines Fix typo in option name ........ --- command/sdist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index f2d2f94b..0c3b0b55 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -73,7 +73,7 @@ class sdist(Command): ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), - ('medata-check', None, + ('metadata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), ('owner=', 'u', -- cgit v1.2.1 From d4b44c7d2deb42b35cb3c54d8f367f16fa37e1ab Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 12 Sep 2010 22:55:40 +0000 Subject: Issue #9313: Skips test_remove_visual_c_ref on old MSVC. --- tests/test_msvc9compiler.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 8a908d99..39e2c11c 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -109,6 +109,11 @@ class msvc9compilerTestCase(support.TempdirManager, self.assertTrue('Desktop' in keys) def test_remove_visual_c_ref(self): + from distutils.msvccompiler import get_build_version + if get_build_version() < 8.0: + # this test is only for MSVC8.0 or above + return + from distutils.msvc9compiler import MSVCCompiler tempdir = self.mkdtemp() manifest = os.path.join(tempdir, 'manifest') -- cgit v1.2.1 From 50e74d1aefc924a81eb8a4ce26c7376b4c41c055 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 05:36:21 +0000 Subject: Issue #9313: Use unittest.skipUnless to skip old MSVC. --- tests/test_msvc9compiler.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 39e2c11c..ec2b2e36 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -60,7 +60,12 @@ _CLEANED_MANIFEST = """\ """ +if sys.platform=="win32": + from distutils.msvccompiler import get_build_version + @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") +@unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" + " MSVC8.0 or above") class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): @@ -68,10 +73,6 @@ class msvc9compilerTestCase(support.TempdirManager, # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler # is not found - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None @@ -86,11 +87,6 @@ class msvc9compilerTestCase(support.TempdirManager, msvc9compiler.find_vcvarsall = old_find_vcvarsall def test_reg_class(self): - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return - from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') @@ -109,11 +105,6 @@ class msvc9compilerTestCase(support.TempdirManager, self.assertTrue('Desktop' in keys) def test_remove_visual_c_ref(self): - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return - from distutils.msvc9compiler import MSVCCompiler tempdir = self.mkdtemp() manifest = os.path.join(tempdir, 'manifest') -- cgit v1.2.1 From 74e96af334b6541dc32dba42f6ba5c042111ddfd Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 05:48:30 +0000 Subject: Merged revisions 84753,84760 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84753 | hirokazu.yamamoto | 2010-09-13 07:55:40 +0900 | 1 line Issue #9313: Skips test_remove_visual_c_ref on old MSVC. ........ r84760 | hirokazu.yamamoto | 2010-09-13 14:36:21 +0900 | 1 line Issue #9313: Use unittest.skipUnless to skip old MSVC. ........ --- tests/test_msvc9compiler.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 8a908d99..ec2b2e36 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -60,7 +60,12 @@ _CLEANED_MANIFEST = """\ """ +if sys.platform=="win32": + from distutils.msvccompiler import get_build_version + @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") +@unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" + " MSVC8.0 or above") class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): @@ -68,10 +73,6 @@ class msvc9compilerTestCase(support.TempdirManager, # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler # is not found - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None @@ -86,11 +87,6 @@ class msvc9compilerTestCase(support.TempdirManager, msvc9compiler.find_vcvarsall = old_find_vcvarsall def test_reg_class(self): - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return - from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') -- cgit v1.2.1 From 9b43905136212f7c0463ec8e844c02c9830df14e Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 06:36:09 +0000 Subject: Merged revisions 84753,84760 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84753 | hirokazu.yamamoto | 2010-09-13 07:55:40 +0900 | 1 line Issue #9313: Skips test_remove_visual_c_ref on old MSVC. ........ r84760 | hirokazu.yamamoto | 2010-09-13 14:36:21 +0900 | 1 line Issue #9313: Use unittest.skipUnless to skip old MSVC. ........ --- tests/test_msvc9compiler.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 503a5a80..cd162516 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -60,7 +60,12 @@ _CLEANED_MANIFEST = """\ """ +if sys.platform=="win32": + from distutils.msvccompiler import get_build_version + @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") +@unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" + " MSVC8.0 or above") class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): @@ -68,10 +73,6 @@ class msvc9compilerTestCase(support.TempdirManager, # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler # is not found - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None @@ -86,11 +87,6 @@ class msvc9compilerTestCase(support.TempdirManager, msvc9compiler.find_vcvarsall = old_find_vcvarsall def test_reg_class(self): - from distutils.msvccompiler import get_build_version - if get_build_version() < 8.0: - # this test is only for MSVC8.0 or above - return - from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') -- cgit v1.2.1 From c9bc9d98d8766b49eea3d8fbc084e9313094b94e Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 07:18:30 +0000 Subject: get_build_version() is needed even where sys.platform != "win32". Try to fix buildbot error in other way. --- tests/test_msvc9compiler.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index ec2b2e36..f1da843f 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -62,10 +62,14 @@ _CLEANED_MANIFEST = """\ if sys.platform=="win32": from distutils.msvccompiler import get_build_version - -@unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") -@unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" - " MSVC8.0 or above") + if get_build_version()>=8.0: + SKIP_MESSAGE = None + else: + SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" +else: + SKIP_MESSAGE = "These tests are only for win32" + +@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): -- cgit v1.2.1 From 93127be78a63414c9ca002640c693192224221aa Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 07:48:22 +0000 Subject: Merged revisions 84765 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84765 | hirokazu.yamamoto | 2010-09-13 16:18:30 +0900 | 2 lines get_build_version() is needed even where sys.platform != "win32". Try to fix buildbot error in other way. ........ --- tests/test_msvc9compiler.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index ec2b2e36..f1da843f 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -62,10 +62,14 @@ _CLEANED_MANIFEST = """\ if sys.platform=="win32": from distutils.msvccompiler import get_build_version - -@unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") -@unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" - " MSVC8.0 or above") + if get_build_version()>=8.0: + SKIP_MESSAGE = None + else: + SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" +else: + SKIP_MESSAGE = "These tests are only for win32" + +@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): -- cgit v1.2.1 From 3033d62a264a06b0d60459c769af0d5d2ffb4db9 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 08:14:41 +0000 Subject: Merged revisions 84765 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84765 | hirokazu.yamamoto | 2010-09-13 16:18:30 +0900 | 2 lines get_build_version() is needed even where sys.platform != "win32". Try to fix buildbot error in other way. ........ --- tests/test_msvc9compiler.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index cd162516..c9579544 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -62,10 +62,14 @@ _CLEANED_MANIFEST = """\ if sys.platform=="win32": from distutils.msvccompiler import get_build_version - -@unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") -@unittest.skipUnless(get_build_version()>=8.0, "These tests are only for" - " MSVC8.0 or above") + if get_build_version()>=8.0: + SKIP_MESSAGE = None + else: + SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" +else: + SKIP_MESSAGE = "These tests are only for win32" + +@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): -- cgit v1.2.1 From fdc07fb43978baae1dbe06a43707d5c81a14d1db Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Fri, 17 Sep 2010 16:35:37 +0000 Subject: Fix Issue2236: Distutils' mkpath implementation ignoring the "mode" parameter --- dir_util.py | 2 +- tests/test_dir_util.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dir_util.py b/dir_util.py index 98e6252c..54376e5c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -68,7 +68,7 @@ def mkpath(name, mode=0o777, verbose=1, dry_run=0): if not dry_run: try: - os.mkdir(head) + os.mkdir(head, mode) created_dirs.append(head) except OSError as exc: raise DistutilsFileError( diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 0f694aa0..8986ca55 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -1,6 +1,7 @@ """Tests for distutils.dir_util.""" import unittest import os +import stat import shutil from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, @@ -48,6 +49,12 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + def test_mkpath_with_custom_mode(self): + mkpath(self.target, 0o700) + self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + mkpath(self.target2, 0o555) + self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + def test_create_tree_verbosity(self): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) -- cgit v1.2.1 From 1fbe22435a7a8e26625d33b3f1c5531fd8eb0090 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Fri, 17 Sep 2010 16:40:01 +0000 Subject: Merged revisions 84861 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84861 | senthil.kumaran | 2010-09-17 22:05:37 +0530 (Fri, 17 Sep 2010) | 3 lines Fix Issue2236: Distutils' mkpath implementation ignoring the "mode" parameter ........ --- dir_util.py | 2 +- tests/test_dir_util.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dir_util.py b/dir_util.py index 9b4d2adb..5a968063 100644 --- a/dir_util.py +++ b/dir_util.py @@ -68,7 +68,7 @@ def mkpath(name, mode=0777, verbose=1, dry_run=0): if not dry_run: try: - os.mkdir(head) + os.mkdir(head, mode) created_dirs.append(head) except OSError, exc: raise DistutilsFileError, \ diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 0f694aa0..8986ca55 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -1,6 +1,7 @@ """Tests for distutils.dir_util.""" import unittest import os +import stat import shutil from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, @@ -48,6 +49,12 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + def test_mkpath_with_custom_mode(self): + mkpath(self.target, 0o700) + self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + mkpath(self.target2, 0o555) + self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + def test_create_tree_verbosity(self): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) -- cgit v1.2.1 From 8ff8c31883ff47ba9489fa9e265dce2e3dfc2361 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Fri, 17 Sep 2010 16:42:05 +0000 Subject: Merged revisions 84861 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84861 | senthil.kumaran | 2010-09-17 22:05:37 +0530 (Fri, 17 Sep 2010) | 3 lines Fix Issue2236: Distutils' mkpath implementation ignoring the "mode" parameter ........ --- dir_util.py | 2 +- tests/test_dir_util.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dir_util.py b/dir_util.py index 98e6252c..54376e5c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -68,7 +68,7 @@ def mkpath(name, mode=0o777, verbose=1, dry_run=0): if not dry_run: try: - os.mkdir(head) + os.mkdir(head, mode) created_dirs.append(head) except OSError as exc: raise DistutilsFileError( diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 0f694aa0..8986ca55 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -1,6 +1,7 @@ """Tests for distutils.dir_util.""" import unittest import os +import stat import shutil from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, @@ -48,6 +49,12 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + def test_mkpath_with_custom_mode(self): + mkpath(self.target, 0o700) + self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + mkpath(self.target2, 0o555) + self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + def test_create_tree_verbosity(self): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) -- cgit v1.2.1 From e2489f924e1603823889e36f900b10e9211d903d Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sat, 18 Sep 2010 02:55:03 +0000 Subject: Skip the distutils mode test on Windows OS. --- tests/test_dir_util.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 8986ca55..892a66d2 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,6 +3,7 @@ import unittest import os import stat import shutil +import sys from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, ensure_relative) @@ -49,6 +50,8 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + @unittest.skipIf(sys.platform.startswith('win'), + "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): mkpath(self.target, 0o700) self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) -- cgit v1.2.1 From 2e99ac7174e503aa7a717d339eff6bcbd620e7d2 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sat, 18 Sep 2010 02:57:28 +0000 Subject: Merged revisions 84871 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84871 | senthil.kumaran | 2010-09-18 08:25:03 +0530 (Sat, 18 Sep 2010) | 3 lines Skip the distutils mode test on Windows OS. ........ --- tests/test_dir_util.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 8986ca55..892a66d2 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,6 +3,7 @@ import unittest import os import stat import shutil +import sys from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, ensure_relative) @@ -49,6 +50,8 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + @unittest.skipIf(sys.platform.startswith('win'), + "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): mkpath(self.target, 0o700) self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) -- cgit v1.2.1 From eb473877ee82b71b02cee7c530d5f279670f8a4d Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sat, 18 Sep 2010 02:58:49 +0000 Subject: Merged revisions 84871 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84871 | senthil.kumaran | 2010-09-18 08:25:03 +0530 (Sat, 18 Sep 2010) | 3 lines Skip the distutils mode test on Windows OS. ........ --- tests/test_dir_util.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 8986ca55..892a66d2 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,6 +3,7 @@ import unittest import os import stat import shutil +import sys from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, ensure_relative) @@ -49,6 +50,8 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEquals(self._logs, wanted) + @unittest.skipIf(sys.platform.startswith('win'), + "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): mkpath(self.target, 0o700) self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) -- cgit v1.2.1 From ebefff189367909ff68453fd010eb61213827644 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sun, 19 Sep 2010 03:09:54 +0000 Subject: Update the test_distutils mode test to test with umask value properly. --- tests/test_dir_util.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 892a66d2..a1647fbc 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -53,10 +53,15 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): @unittest.skipIf(sys.platform.startswith('win'), "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): + # Get and set the current umask value for testing mode bits. + umask = os.umask(0o002) + os.umask(umask) mkpath(self.target, 0o700) - self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + self.assertEqual( + stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) mkpath(self.target2, 0o555) - self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + self.assertEqual( + stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) def test_create_tree_verbosity(self): -- cgit v1.2.1 From d8b66d133304a36d1597de05eeaac5dfb60a8712 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sun, 19 Sep 2010 03:12:28 +0000 Subject: Merged revisions 84889 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84889 | senthil.kumaran | 2010-09-19 08:39:54 +0530 (Sun, 19 Sep 2010) | 3 lines Update the test_distutils mode test to test with umask value properly. ........ --- tests/test_dir_util.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 892a66d2..a1647fbc 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -53,10 +53,15 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): @unittest.skipIf(sys.platform.startswith('win'), "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): + # Get and set the current umask value for testing mode bits. + umask = os.umask(0o002) + os.umask(umask) mkpath(self.target, 0o700) - self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + self.assertEqual( + stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) mkpath(self.target2, 0o555) - self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + self.assertEqual( + stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) def test_create_tree_verbosity(self): -- cgit v1.2.1 From b5a3263df80e4b7b52e97e0f9ac4d92993c7b5dc Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sun, 19 Sep 2010 03:12:35 +0000 Subject: Merged revisions 84889 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r84889 | senthil.kumaran | 2010-09-19 08:39:54 +0530 (Sun, 19 Sep 2010) | 3 lines Update the test_distutils mode test to test with umask value properly. ........ --- tests/test_dir_util.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 892a66d2..a1647fbc 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -53,10 +53,15 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): @unittest.skipIf(sys.platform.startswith('win'), "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): + # Get and set the current umask value for testing mode bits. + umask = os.umask(0o002) + os.umask(umask) mkpath(self.target, 0o700) - self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700) + self.assertEqual( + stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) mkpath(self.target2, 0o555) - self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555) + self.assertEqual( + stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) def test_create_tree_verbosity(self): -- cgit v1.2.1 From 70b1270636c9a42b0f1f4458d37bbbbabac4bb32 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 20 Sep 2010 10:13:13 +0000 Subject: logging: added hasHandlers() to LoggerAdapter. --- sysconfig.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 48f3fe4d..3567db83 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -56,6 +56,18 @@ def get_python_version(): """ return sys.version[:3] +def _get_build_dir(name, plat_specific): + # Assume the executable is in the build directory. The + # pyconfig.h file should be in the same directory. Since + # the build directory may not be the source directory, we + # must use "srcdir" from the makefile to find the "Include" + # directory. + base = os.path.dirname(os.path.abspath(sys.executable)) + if plat_specific: + return base + else: + thedir = os.path.join(get_config_var('srcdir'), name) + return os.path.normpath(thedir) def get_python_inc(plat_specific=0, prefix=None): """Return the directory containing installed Python header files. @@ -72,17 +84,7 @@ def get_python_inc(plat_specific=0, prefix=None): prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": if python_build: - # Assume the executable is in the build directory. The - # pyconfig.h file should be in the same directory. Since - # the build directory may not be the source directory, we - # must use "srcdir" from the makefile to find the "Include" - # directory. - base = os.path.dirname(os.path.abspath(sys.executable)) - if plat_specific: - return base - else: - incdir = os.path.join(get_config_var('srcdir'), 'Include') - return os.path.normpath(incdir) + return _get_build_dir('Include', plat_specific) return os.path.join(prefix, "include", "python" + get_python_version()) elif os.name == "nt": return os.path.join(prefix, "include") @@ -117,6 +119,8 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": + if python_build: + return _get_build_dir('Lib', plat_specific) libpython = os.path.join(prefix, "lib", "python" + get_python_version()) if standard_lib: -- cgit v1.2.1 From 8eb84ccb87fb80d1e95382003af64a8c7d2f83de Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 20 Sep 2010 10:29:54 +0000 Subject: Reverted changes which were inadvertently committed. --- sysconfig.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 3567db83..48f3fe4d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -56,18 +56,6 @@ def get_python_version(): """ return sys.version[:3] -def _get_build_dir(name, plat_specific): - # Assume the executable is in the build directory. The - # pyconfig.h file should be in the same directory. Since - # the build directory may not be the source directory, we - # must use "srcdir" from the makefile to find the "Include" - # directory. - base = os.path.dirname(os.path.abspath(sys.executable)) - if plat_specific: - return base - else: - thedir = os.path.join(get_config_var('srcdir'), name) - return os.path.normpath(thedir) def get_python_inc(plat_specific=0, prefix=None): """Return the directory containing installed Python header files. @@ -84,7 +72,17 @@ def get_python_inc(plat_specific=0, prefix=None): prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": if python_build: - return _get_build_dir('Include', plat_specific) + # Assume the executable is in the build directory. The + # pyconfig.h file should be in the same directory. Since + # the build directory may not be the source directory, we + # must use "srcdir" from the makefile to find the "Include" + # directory. + base = os.path.dirname(os.path.abspath(sys.executable)) + if plat_specific: + return base + else: + incdir = os.path.join(get_config_var('srcdir'), 'Include') + return os.path.normpath(incdir) return os.path.join(prefix, "include", "python" + get_python_version()) elif os.name == "nt": return os.path.join(prefix, "include") @@ -119,8 +117,6 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": - if python_build: - return _get_build_dir('Lib', plat_specific) libpython = os.path.join(prefix, "lib", "python" + get_python_version()) if standard_lib: -- cgit v1.2.1 From f63c10f0b8f07a02e58c75b2da2454808529442c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 3 Oct 2010 14:18:09 +0000 Subject: Fixed #8980: distutils.command.check was failing w/ docutils installed --- command/check.py | 2 +- tests/test_register.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/command/check.py b/command/check.py index 12844cbf..2657c696 100644 --- a/command/check.py +++ b/command/check.py @@ -13,7 +13,7 @@ try: from docutils.parsers.rst import Parser from docutils import frontend from docutils import nodes - from StringIO import StringIO + from io import StringIO class SilentReporter(Reporter): diff --git a/tests/test_register.py b/tests/test_register.py index c03ad101..7d0ac9ee 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -224,24 +224,24 @@ class RegisterTestCase(PyPIRCCommandTestCase): cmd = self._get_cmd(metadata) cmd.ensure_finalized() cmd.strict = 1 - inputs = RawInputs('1', 'tarek', 'y') - register_module.raw_input = inputs.__call__ + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ # let's run the command try: cmd.run() finally: - del register_module.raw_input + del register_module.input # strict is not by default cmd = self._get_cmd() cmd.ensure_finalized() - inputs = RawInputs('1', 'tarek', 'y') - register_module.raw_input = inputs.__call__ + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ # let's run the command try: cmd.run() finally: - del register_module.raw_input + del register_module.input def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated -- cgit v1.2.1 From 3e5aeeda898da45a6d8f3f83397193500495183c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarek=20Ziad=C3=A9?= Date: Sun, 3 Oct 2010 14:30:11 +0000 Subject: Merged revisions 85197 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r85197 | tarek.ziade | 2010-10-03 16:18:09 +0200 (Sun, 03 Oct 2010) | 1 line Fixed #8980: distutils.command.check was failing w/ docutils installed ........ --- command/check.py | 2 +- tests/test_register.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/command/check.py b/command/check.py index 12844cbf..2657c696 100644 --- a/command/check.py +++ b/command/check.py @@ -13,7 +13,7 @@ try: from docutils.parsers.rst import Parser from docutils import frontend from docutils import nodes - from StringIO import StringIO + from io import StringIO class SilentReporter(Reporter): diff --git a/tests/test_register.py b/tests/test_register.py index c03ad101..7d0ac9ee 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -224,24 +224,24 @@ class RegisterTestCase(PyPIRCCommandTestCase): cmd = self._get_cmd(metadata) cmd.ensure_finalized() cmd.strict = 1 - inputs = RawInputs('1', 'tarek', 'y') - register_module.raw_input = inputs.__call__ + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ # let's run the command try: cmd.run() finally: - del register_module.raw_input + del register_module.input # strict is not by default cmd = self._get_cmd() cmd.ensure_finalized() - inputs = RawInputs('1', 'tarek', 'y') - register_module.raw_input = inputs.__call__ + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ # let's run the command try: cmd.run() finally: - del register_module.raw_input + del register_module.input def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated -- cgit v1.2.1 From aa258286d3d9a2651701148dca8781bd2a38adc1 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 6 Oct 2010 08:26:09 +0000 Subject: Merged revisions 82805-82806,83523-83527,83536,83538,83542,83546-83548,83550-83555,83558,83560 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r82805 | georg.brandl | 2010-07-11 11:42:10 +0200 (So, 11 Jul 2010) | 1 line #7935: cross-reference to ast.literal_eval() from eval() docs. ........ r82806 | georg.brandl | 2010-07-11 12:22:44 +0200 (So, 11 Jul 2010) | 1 line #9223: link to Command class reference, and move Command interface docs nearer to class docs. ........ r83523 | georg.brandl | 2010-08-02 14:06:18 +0200 (Mo, 02 Aug 2010) | 1 line #9209 and #7781: fix two crashes in pstats interactive browser. ........ r83524 | georg.brandl | 2010-08-02 14:20:23 +0200 (Mo, 02 Aug 2010) | 1 line #9428: fix running scripts from profile/cProfile with their own name and the right namespace. Same fix as for trace.py in #1690103. ........ r83525 | georg.brandl | 2010-08-02 14:36:24 +0200 (Mo, 02 Aug 2010) | 1 line Get rid of spurious "threading" entries in trace output. ........ r83526 | georg.brandl | 2010-08-02 14:40:22 +0200 (Mo, 02 Aug 2010) | 1 line Fix softspace relic. ........ r83527 | georg.brandl | 2010-08-02 14:48:46 +0200 (Mo, 02 Aug 2010) | 1 line #3821: beginnings of a trace.py unittest. ........ r83536 | georg.brandl | 2010-08-02 19:49:25 +0200 (Mo, 02 Aug 2010) | 1 line #8578: mention danger of not incref'ing weak referenced object. ........ r83538 | georg.brandl | 2010-08-02 20:10:13 +0200 (Mo, 02 Aug 2010) | 1 line #6928: fix class docs w.r.t. new metaclasses. ........ r83542 | georg.brandl | 2010-08-02 20:56:54 +0200 (Mo, 02 Aug 2010) | 1 line Move test_SimpleHTTPServer into test_httpservers. ........ r83546 | georg.brandl | 2010-08-02 21:16:34 +0200 (Mo, 02 Aug 2010) | 1 line #7973: Fix distutils options spelling. ........ r83547 | georg.brandl | 2010-08-02 21:19:26 +0200 (Mo, 02 Aug 2010) | 1 line #7386: add example that shows that trailing path separators are stripped. ........ r83548 | georg.brandl | 2010-08-02 21:23:34 +0200 (Mo, 02 Aug 2010) | 1 line #8172: how does one use a property? ........ r83550 | georg.brandl | 2010-08-02 21:32:43 +0200 (Mo, 02 Aug 2010) | 1 line #9451: strengthen warning about __*__ special name usage. ........ r83551 | georg.brandl | 2010-08-02 21:35:06 +0200 (Mo, 02 Aug 2010) | 1 line Remove XXX comment that was displayed. ........ r83552 | georg.brandl | 2010-08-02 21:36:36 +0200 (Mo, 02 Aug 2010) | 1 line #9438: clarify that constant names also cannot be assigned as attributes. ........ r83553 | georg.brandl | 2010-08-02 21:39:17 +0200 (Mo, 02 Aug 2010) | 1 line Remove redundant information. ........ r83554 | georg.brandl | 2010-08-02 21:43:05 +0200 (Mo, 02 Aug 2010) | 1 line #7280: note about nasmw.exe. ........ r83555 | georg.brandl | 2010-08-02 21:44:48 +0200 (Mo, 02 Aug 2010) | 1 line #8861: remove unused variable. ........ r83558 | georg.brandl | 2010-08-02 22:05:19 +0200 (Mo, 02 Aug 2010) | 1 line #8648: document UTF-7 codec functions. ........ r83560 | georg.brandl | 2010-08-02 22:16:18 +0200 (Mo, 02 Aug 2010) | 1 line #9087: update json docstrings -- unicode and long do not exist anymore. ........ --- command/bdist_msi.py | 2 +- command/bdist_wininst.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index c4be47b5..8a458d85 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -148,7 +148,7 @@ class bdist_msi(Command): if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: raise DistutilsOptionError( - "target version can only be %s, or the '--skip_build'" + "target version can only be %s, or the '--skip-build'" " option must be specified" % (short_version,)) else: self.versions = list(self.all_versions) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index d6d01c63..3aa1dac7 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -89,7 +89,7 @@ class bdist_wininst(Command): short_version = get_python_version() if self.target_version and self.target_version != short_version: raise DistutilsOptionError( - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,)) self.target_version = short_version -- cgit v1.2.1 From 107ea98e7050ff3c6ea4aaf1a3b7f73aef69bfbf Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 10 Oct 2010 09:37:12 +0000 Subject: Issue #9437: Fix building C extensions with non-default LDFLAGS. --- sysconfig.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index 48f3fe4d..8847e317 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -359,6 +359,11 @@ def parse_makefile(fn, g=None): fp.close() + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + # save the results in the global dictionary g.update(done) return g -- cgit v1.2.1 From 8a775b192d5d53db9bf259d3526348be1462df34 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 10 Oct 2010 09:40:34 +0000 Subject: Bump to 3.2a3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 92ed8f4d..9df61ab0 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a2" +__version__ = "3.2a3" #--end constants-- -- cgit v1.2.1 From 04895087400890db00af176be74899e4b51a5a84 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 10 Oct 2010 09:54:59 +0000 Subject: Merged revisions 85353 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r85353 | antoine.pitrou | 2010-10-10 11:37:12 +0200 (dim., 10 oct. 2010) | 3 lines Issue #9437: Fix building C extensions with non-default LDFLAGS. ........ --- sysconfig.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index 4d16b267..9888cd52 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -318,6 +318,11 @@ def parse_makefile(fn, g=None): fp.close() + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + # save the results in the global dictionary g.update(done) return g -- cgit v1.2.1 From f88b5e54e08c0a816c2a0154254959a0056f2184 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Sat, 16 Oct 2010 01:04:07 +0000 Subject: First (uncontroversial) part of issue 9807. * Expose the build flags to Python as sys.abiflags * Shared library libpythonX.Y.so * python-config --abiflags * Make two distutils tests that failed with --enable-shared (even before this patch) succeed. * Fix a few small style issues. --- command/build_ext.py | 6 +++--- tests/test_build_ext.py | 26 +++++++++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 4e664642..cc0d414c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -754,9 +754,9 @@ class build_ext(Command): else: from distutils import sysconfig if sysconfig.get_config_var('Py_ENABLE_SHARED'): - template = "python%d.%d" - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + pythonlib = 'python{}.{}{}'.format( + sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff, + sys.abiflags) return ext.libraries + [pythonlib] else: return ext.libraries diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 52479d7d..4351c0f8 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -1,17 +1,16 @@ import sys import os -import tempfile import shutil from io import StringIO -from distutils.core import Extension, Distribution +from distutils.core import Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig from distutils.tests.support import TempdirManager from distutils.tests.support import LoggingSilencer from distutils.extension import Extension -from distutils.errors import (UnknownFileError, DistutilsSetupError, - CompileError) +from distutils.errors import ( + CompileError, DistutilsSetupError, UnknownFileError) import unittest from test import support @@ -42,6 +41,20 @@ class BuildExtTestCase(TempdirManager, from distutils.command import build_ext build_ext.USER_BASE = site.USER_BASE + def _fixup_command(self, cmd): + # When Python was build with --enable-shared, -L. is not good enough + # to find the libpython.so. This is because regrtest runs it + # under a tempdir, not in the top level where the .so lives. By the + # time we've gotten here, Python's already been chdir'd to the + # tempdir. + # + # To further add to the fun, we can't just add library_dirs to the + # Extension() instance because that doesn't get plumbed through to the + # final compiler command. + if not sys.platform.startswith('win'): + library_dir = sysconfig.get_config_var('srcdir') + cmd.library_dirs = [('.' if library_dir is None else library_dir)] + def test_build_ext(self): global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') @@ -49,6 +62,7 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir cmd = build_ext(dist) + self._fixup_command(cmd) if os.name == "nt": # On Windows, we must build a debug version iff running # a debug build of Python @@ -235,7 +249,8 @@ class BuildExtTestCase(TempdirManager, cmd.finalize_options() #'extensions' option must be a list of Extension instances - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo') + self.assertRaises(DistutilsSetupError, + cmd.check_extensions_list, 'foo') # each element of 'ext_modules' option must be an # Extension instance or 2-tuple @@ -302,6 +317,7 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) cmd = build_ext(dist) + self._fixup_command(cmd) cmd.ensure_finalized() self.assertEquals(len(cmd.get_outputs()), 1) -- cgit v1.2.1 From b6d0fe5087a6648ec9541670386eccf38d53018e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Thu, 21 Oct 2010 18:46:36 +0000 Subject: Backport fix for #10126 --- tests/test_build_ext.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index b7cdc20a..04ebc5b1 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -42,6 +42,20 @@ class BuildExtTestCase(TempdirManager, from distutils.command import build_ext build_ext.USER_BASE = site.USER_BASE + def _fixup_command(self, cmd): + # When Python was build with --enable-shared, -L. is not good enough + # to find the libpython.so. This is because regrtest runs it + # under a tempdir, not in the top level where the .so lives. By the + # time we've gotten here, Python's already been chdir'd to the + # tempdir. + # + # To further add to the fun, we can't just add library_dirs to the + # Extension() instance because that doesn't get plumbed through to the + # final compiler command. + if not sys.platform.startswith('win'): + library_dir = sysconfig.get_config_var('srcdir') + cmd.library_dirs = [('.' if library_dir is None else library_dir)] + def test_build_ext(self): global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') @@ -49,6 +63,7 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir cmd = build_ext(dist) + self._fixup_command(cmd) if os.name == "nt": # On Windows, we must build a debug version iff running # a debug build of Python -- cgit v1.2.1 From d48f22926fe4e190cb1f490661eab6bf2cc61a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Thu, 21 Oct 2010 18:48:59 +0000 Subject: Backport fix for #10126 --- tests/test_build_ext.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index beb6d96c..154771f2 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -49,6 +49,20 @@ class BuildExtTestCase(support.TempdirManager, sys.platform == 'cygwin') super(BuildExtTestCase, self).tearDown() + def _fixup_command(self, cmd): + # When Python was build with --enable-shared, -L. is not good enough + # to find the libpython.so. This is because regrtest runs it + # under a tempdir, not in the top level where the .so lives. By the + # time we've gotten here, Python's already been chdir'd to the + # tempdir. + # + # To further add to the fun, we can't just add library_dirs to the + # Extension() instance because that doesn't get plumbed through to the + # final compiler command. + if not sys.platform.startswith('win'): + library_dir = sysconfig.get_config_var('srcdir') + cmd.library_dirs = [('.' if library_dir is None else library_dir)] + @unittest.skipIf(not os.path.exists(_XX_MODULE_PATH), 'xxmodule.c not found') def test_build_ext(self): @@ -58,6 +72,7 @@ class BuildExtTestCase(support.TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir cmd = build_ext(dist) + self._fixup_command(cmd) if os.name == "nt": # On Windows, we must build a debug version iff running # a debug build of Python -- cgit v1.2.1 From 7404daed7bf476fd57a47aac41c64ed48cc78d45 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 21 Oct 2010 22:13:29 +0000 Subject: Fix issue 10126 for Python 2.7 by using $RUNSHARED to find the directory to the shared library. test_distutils now passes when Python was built with --enable-shared. --- tests/test_build_ext.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 154771f2..e0459306 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -60,8 +60,12 @@ class BuildExtTestCase(support.TempdirManager, # Extension() instance because that doesn't get plumbed through to the # final compiler command. if not sys.platform.startswith('win'): - library_dir = sysconfig.get_config_var('srcdir') - cmd.library_dirs = [('.' if library_dir is None else library_dir)] + runshared = sysconfig.get_config_var('RUNSHARED') + if runshared is None: + cmd.library_dirs = ['.'] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = value.split(os.pathsep) @unittest.skipIf(not os.path.exists(_XX_MODULE_PATH), 'xxmodule.c not found') @@ -265,6 +269,7 @@ class BuildExtTestCase(support.TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) cmd = build_ext(dist) + self._fixup_command(cmd) cmd.ensure_finalized() self.assertEquals(len(cmd.get_outputs()), 1) -- cgit v1.2.1 From 1415d2f24237092366c384aae24fbb6d17e67abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Thu, 21 Oct 2010 23:02:07 +0000 Subject: Apply fix from r85784 on py3k too. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes bug #10126 for Python 3.2 by using $RUNSHARED to find the directory to the shared library. test_distutils now passes when Python was built with --enable-shared (Barry didn’t have the error but I did). --- tests/test_build_ext.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 4351c0f8..6858e5a4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -52,8 +52,12 @@ class BuildExtTestCase(TempdirManager, # Extension() instance because that doesn't get plumbed through to the # final compiler command. if not sys.platform.startswith('win'): - library_dir = sysconfig.get_config_var('srcdir') - cmd.library_dirs = [('.' if library_dir is None else library_dir)] + runshared = sysconfig.get_config_var('RUNSHARED') + if runshared is None: + cmd.library_dirs = ['.'] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = value.split(os.pathsep) def test_build_ext(self): global ALREADY_TESTED -- cgit v1.2.1 From d8c874020da7deef2a07f22929ad75ce5cd74021 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 22 Oct 2010 15:31:44 +0000 Subject: Only hack cmd.library_dirs when running under Py_ENABLE_SHARED. Tested both with and without --enable-shared on Ubuntu 10.10. Hopefully this finally solves bug 10126. Will check 3.1 and py3k next. --- tests/test_build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index e0459306..d14c5f6b 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -59,7 +59,8 @@ class BuildExtTestCase(support.TempdirManager, # To further add to the fun, we can't just add library_dirs to the # Extension() instance because that doesn't get plumbed through to the # final compiler command. - if not sys.platform.startswith('win'): + if (sysconfig.get_config_var('Py_ENABLE_SHARED') and + not sys.platform.startswith('win')): runshared = sysconfig.get_config_var('RUNSHARED') if runshared is None: cmd.library_dirs = ['.'] -- cgit v1.2.1 From d6acf0666e6011bcf96c64e07c5f95574c1e1fbd Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 22 Oct 2010 17:17:51 +0000 Subject: Only hack cmd.library_dirs when running under Py_ENABLE_SHARED. Tested both with and without --enable-shared on Ubuntu 10.10. Hopefully this finally solves bug 10126. Will check 3.1 next. --- tests/test_build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 6858e5a4..18e00117 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -51,7 +51,8 @@ class BuildExtTestCase(TempdirManager, # To further add to the fun, we can't just add library_dirs to the # Extension() instance because that doesn't get plumbed through to the # final compiler command. - if not sys.platform.startswith('win'): + if (sysconfig.get_config_var('Py_ENABLE_SHARED') and + not sys.platform.startswith('win')): runshared = sysconfig.get_config_var('RUNSHARED') if runshared is None: cmd.library_dirs = ['.'] -- cgit v1.2.1 From 1208ea17d6e97abffe76f3994d8575be5db3582f Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 22 Oct 2010 18:34:13 +0000 Subject: Should fix remaining 3.1 buildbot failure --- tests/test_build_ext.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 04ebc5b1..226f7bb1 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -52,9 +52,14 @@ class BuildExtTestCase(TempdirManager, # To further add to the fun, we can't just add library_dirs to the # Extension() instance because that doesn't get plumbed through to the # final compiler command. - if not sys.platform.startswith('win'): - library_dir = sysconfig.get_config_var('srcdir') - cmd.library_dirs = [('.' if library_dir is None else library_dir)] + if (sysconfig.get_config_var('Py_ENABLE_SHARED') and + not sys.platform.startswith('win')): + runshared = sysconfig.get_config_var('RUNSHARED') + if runshared is None: + cmd.library_dirs = ['.'] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = value.split(os.pathsep) def test_build_ext(self): global ALREADY_TESTED @@ -317,6 +322,7 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) cmd = build_ext(dist) + self._fixup_command(cmd) cmd.ensure_finalized() self.assertEquals(len(cmd.get_outputs()), 1) -- cgit v1.2.1 From c4405aad904aac1948e1798945118759ef677af6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 23 Oct 2010 17:02:31 +0000 Subject: Issue #6011: sysconfig and distutils.sysconfig use the surrogateescape error handler to parse the Makefile file. Avoid a UnicodeDecodeError if the source code directory name contains a non-ASCII character and the locale encoding is ASCII. --- sysconfig.py | 2 +- text_file.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 8847e317..6b5daa51 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -271,7 +271,7 @@ def parse_makefile(fn, g=None): used instead of a new dictionary. """ from distutils.text_file import TextFile - fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1) + fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape") if g is None: g = {} diff --git a/text_file.py b/text_file.py index 97459fbf..454725c6 100644 --- a/text_file.py +++ b/text_file.py @@ -58,6 +58,8 @@ class TextFile: collapse_join [default: false] strip leading whitespace from lines that are joined to their predecessor; only matters if (join_lines and not lstrip_ws) + errors [default: 'strict'] + error handler used to decode the file content Note that since 'rstrip_ws' can strip the trailing newline, the semantics of 'readline()' must differ from those of the builtin file @@ -72,6 +74,7 @@ class TextFile: 'rstrip_ws': 1, 'join_lines': 0, 'collapse_join': 0, + 'errors': 'strict', } def __init__(self, filename=None, file=None, **options): @@ -111,7 +114,7 @@ class TextFile: """Open a new file named 'filename'. This overrides both the 'filename' and 'file' arguments to the constructor.""" self.filename = filename - self.file = io.open(self.filename, 'r') + self.file = io.open(self.filename, 'r', errors=self.errors) self.current_line = 0 def close(self): -- cgit v1.2.1 From 91b89f544e774b04a09af9d1849334f4f7dc20b5 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 29 Oct 2010 22:36:08 +0000 Subject: Have distutils.sysconfig close a file to remove ResourceWarnings coming up during the build from setup.py. --- sysconfig.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 6b5daa51..8b55f217 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -413,7 +413,8 @@ def _init_posix(): # load the installed pyconfig.h: try: filename = get_config_h_filename() - parse_config_h(io.open(filename), g) + with open(filename) as file: + parse_config_h(file, g) except IOError as msg: my_msg = "invalid Python installation: unable to open %s" % filename if hasattr(msg, "strerror"): -- cgit v1.2.1 From 3b9b511708f987254fdcfdc5fc0f020d7a0ab923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 5 Nov 2010 23:51:56 +0000 Subject: Always close files in distutils code and tests (#10252). --- ccompiler.py | 10 +-- command/bdist_wininst.py | 6 +- command/upload.py | 6 +- core.py | 6 +- cygwinccompiler.py | 5 +- dist.py | 8 ++- emxccompiler.py | 12 ++-- extension.py | 155 ++++++++++++++++++++++---------------------- file_util.py | 8 ++- tests/test_build_py.py | 12 ++-- tests/test_build_scripts.py | 6 +- tests/test_config.py | 8 ++- tests/test_core.py | 6 +- tests/test_dir_util.py | 6 +- tests/test_dist.py | 40 ++++++------ tests/test_file_util.py | 6 +- tests/test_install.py | 5 +- tests/test_msvc9compiler.py | 14 ++-- tests/test_register.py | 8 ++- tests/test_sdist.py | 8 ++- tests/test_sysconfig.py | 16 +++-- tests/test_text_file.py | 52 ++++++++++----- util.py | 16 +++-- 23 files changed, 253 insertions(+), 166 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 34c77a37..291c008f 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -779,14 +779,16 @@ class CCompiler: library_dirs = [] fd, fname = tempfile.mkstemp(".c", funcname, text=True) f = os.fdopen(fd, "w") - for incl in includes: - f.write("""#include "%s"\n""" % incl) - f.write("""\ + try: + for incl in includes: + f.write("""#include "%s"\n""" % incl) + f.write("""\ main (int argc, char **argv) { %s(); } """ % funcname) - f.close() + finally: + f.close() try: objects = self.compile([fname], include_dirs=include_dirs) except CompileError: diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 3aa1dac7..b2e2fc6d 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -340,4 +340,8 @@ class bdist_wininst(Command): sfix = '' filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) - return open(filename, "rb").read() + f = open(filename, "rb") + try: + return f.read() + finally: + f.close() diff --git a/command/upload.py b/command/upload.py index 99e03d74..4926aa3e 100644 --- a/command/upload.py +++ b/command/upload.py @@ -76,7 +76,11 @@ class upload(PyPIRCCommand): # Fill in the data - send all the meta-data in case we need to # register a new release - content = open(filename,'rb').read() + f = open(filename,'rb') + try: + content = f.read() + finally: + f.close() meta = self.distribution.metadata data = { # action diff --git a/core.py b/core.py index 6e489203..fd2a43d7 100644 --- a/core.py +++ b/core.py @@ -215,7 +215,11 @@ def run_setup (script_name, script_args=None, stop_after="run"): sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args - exec(open(script_name).read(), g, l) + f = open(script_name) + try: + exec(f.read(), g, l) + finally: + f.close() finally: sys.argv = save_argv _setup_stop_after = None diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 85043718..95fa3ed3 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -350,11 +350,14 @@ def check_config_h(): # let's see if __GNUC__ is mentioned in python.h fn = sysconfig.get_config_h_filename() try: - with open(fn) as config_h: + config_h = open(fn) + try: if "__GNUC__" in config_h.read(): return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn else: return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn + finally: + config_h.close() except IOError as exc: return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) diff --git a/dist.py b/dist.py index 1c1ea477..01f1f1cf 100644 --- a/dist.py +++ b/dist.py @@ -1012,9 +1012,11 @@ class DistributionMetadata: def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. """ - pkg_info = open( os.path.join(base_dir, 'PKG-INFO'), 'w') - self.write_pkg_file(pkg_info) - pkg_info.close() + pkg_info = open(os.path.join(base_dir, 'PKG-INFO'), 'w') + try: + self.write_pkg_file(pkg_info) + finally: + pkg_info.close() def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. diff --git a/emxccompiler.py b/emxccompiler.py index 62a4c5b4..16dce535 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -270,8 +270,10 @@ def check_config_h(): # It would probably better to read single lines to search. # But we do this only once, and it is fast enough f = open(fn) - s = f.read() - f.close() + try: + s = f.read() + finally: + f.close() except IOError as exc: # if we can't read this file, we cannot say it is wrong @@ -298,8 +300,10 @@ def get_versions(): gcc_exe = find_executable('gcc') if gcc_exe: out = os.popen(gcc_exe + ' -dumpversion','r') - out_string = out.read() - out.close() + try: + out_string = out.read() + finally: + out.close() result = re.search('(\d+\.\d+\.\d+)', out_string, re.ASCII) if result: gcc_version = StrictVersion(result.group(1)) diff --git a/extension.py b/extension.py index 5c07bdae..2d1c36bd 100644 --- a/extension.py +++ b/extension.py @@ -149,84 +149,87 @@ def read_setup_file(filename): file = TextFile(filename, strip_comments=1, skip_blanks=1, join_lines=1, lstrip_ws=1, rstrip_ws=1) - extensions = [] - - while True: - line = file.readline() - if line is None: # eof - break - if _variable_rx.match(line): # VAR=VALUE, handled in first pass - continue - - if line[0] == line[-1] == "*": - file.warn("'%s' lines not handled yet" % line) - continue - - line = expand_makefile_vars(line, vars) - words = split_quoted(line) - - # NB. this parses a slightly different syntax than the old - # makesetup script: here, there must be exactly one extension per - # line, and it must be the first word of the line. I have no idea - # why the old syntax supported multiple extensions per line, as - # they all wind up being the same. - - module = words[0] - ext = Extension(module, []) - append_next_word = None - - for word in words[1:]: - if append_next_word is not None: - append_next_word.append(word) - append_next_word = None + try: + extensions = [] + + while True: + line = file.readline() + if line is None: # eof + break + if _variable_rx.match(line): # VAR=VALUE, handled in first pass continue - suffix = os.path.splitext(word)[1] - switch = word[0:2] ; value = word[2:] - - if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): - # hmm, should we do something about C vs. C++ sources? - # or leave it up to the CCompiler implementation to - # worry about? - ext.sources.append(word) - elif switch == "-I": - ext.include_dirs.append(value) - elif switch == "-D": - equals = value.find("=") - if equals == -1: # bare "-DFOO" -- no value - ext.define_macros.append((value, None)) - else: # "-DFOO=blah" - ext.define_macros.append((value[0:equals], - value[equals+2:])) - elif switch == "-U": - ext.undef_macros.append(value) - elif switch == "-C": # only here 'cause makesetup has it! - ext.extra_compile_args.append(word) - elif switch == "-l": - ext.libraries.append(value) - elif switch == "-L": - ext.library_dirs.append(value) - elif switch == "-R": - ext.runtime_library_dirs.append(value) - elif word == "-rpath": - append_next_word = ext.runtime_library_dirs - elif word == "-Xlinker": - append_next_word = ext.extra_link_args - elif word == "-Xcompiler": - append_next_word = ext.extra_compile_args - elif switch == "-u": - ext.extra_link_args.append(word) - if not value: + if line[0] == line[-1] == "*": + file.warn("'%s' lines not handled yet" % line) + continue + + line = expand_makefile_vars(line, vars) + words = split_quoted(line) + + # NB. this parses a slightly different syntax than the old + # makesetup script: here, there must be exactly one extension per + # line, and it must be the first word of the line. I have no idea + # why the old syntax supported multiple extensions per line, as + # they all wind up being the same. + + module = words[0] + ext = Extension(module, []) + append_next_word = None + + for word in words[1:]: + if append_next_word is not None: + append_next_word.append(word) + append_next_word = None + continue + + suffix = os.path.splitext(word)[1] + switch = word[0:2] ; value = word[2:] + + if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): + # hmm, should we do something about C vs. C++ sources? + # or leave it up to the CCompiler implementation to + # worry about? + ext.sources.append(word) + elif switch == "-I": + ext.include_dirs.append(value) + elif switch == "-D": + equals = value.find("=") + if equals == -1: # bare "-DFOO" -- no value + ext.define_macros.append((value, None)) + else: # "-DFOO=blah" + ext.define_macros.append((value[0:equals], + value[equals+2:])) + elif switch == "-U": + ext.undef_macros.append(value) + elif switch == "-C": # only here 'cause makesetup has it! + ext.extra_compile_args.append(word) + elif switch == "-l": + ext.libraries.append(value) + elif switch == "-L": + ext.library_dirs.append(value) + elif switch == "-R": + ext.runtime_library_dirs.append(value) + elif word == "-rpath": + append_next_word = ext.runtime_library_dirs + elif word == "-Xlinker": append_next_word = ext.extra_link_args - elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): - # NB. a really faithful emulation of makesetup would - # append a .o file to extra_objects only if it - # had a slash in it; otherwise, it would s/.o/.c/ - # and append it to sources. Hmmmm. - ext.extra_objects.append(word) - else: - file.warn("unrecognized argument '%s'" % word) - - extensions.append(ext) + elif word == "-Xcompiler": + append_next_word = ext.extra_compile_args + elif switch == "-u": + ext.extra_link_args.append(word) + if not value: + append_next_word = ext.extra_link_args + elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): + # NB. a really faithful emulation of makesetup would + # append a .o file to extra_objects only if it + # had a slash in it; otherwise, it would s/.o/.c/ + # and append it to sources. Hmmmm. + ext.extra_objects.append(word) + else: + file.warn("unrecognized argument '%s'" % word) + + extensions.append(ext) + finally: + file.close() return extensions diff --git a/file_util.py b/file_util.py index 65aa7e0f..c36e7128 100644 --- a/file_util.py +++ b/file_util.py @@ -234,6 +234,8 @@ def write_file (filename, contents): sequence of strings without line terminators) to it. """ f = open(filename, "w") - for line in contents: - f.write(line + "\n") - f.close() + try: + for line in contents: + f.write(line + "\n") + finally: + f.close() diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 3e45f6e8..8a69aaa0 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -19,11 +19,15 @@ class BuildPyTestCase(support.TempdirManager, def test_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") - f.write("# Pretend this is a package.") - f.close() + try: + f.write("# Pretend this is a package.") + finally: + f.close() f = open(os.path.join(sources, "README.txt"), "w") - f.write("Info about this package") - f.close() + try: + f.write("Info about this package") + finally: + f.close() destination = self.mkdtemp() diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index b1d2d079..85b04004 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -71,8 +71,10 @@ class BuildScriptsTestCase(support.TempdirManager, def write_script(self, dir, name, text): f = open(os.path.join(dir, name), "w") - f.write(text) - f.close() + try: + f.write(text) + finally: + f.close() def test_version_int(self): source = self.mkdtemp() diff --git a/tests/test_config.py b/tests/test_config.py index 71c63678..6a45a328 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -105,8 +105,12 @@ class PyPIRCCommandTestCase(support.TempdirManager, self.assertTrue(not os.path.exists(rc)) cmd._store_pypirc('tarek', 'xxx') self.assertTrue(os.path.exists(rc)) - content = open(rc).read() - self.assertEquals(content, WANTED) + f = open(rc) + try: + content = f.read() + self.assertEquals(content, WANTED) + finally: + f.close() def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) diff --git a/tests/test_core.py b/tests/test_core.py index b478fa62..e937b45a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -52,7 +52,11 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): shutil.rmtree(path) def write_setup(self, text, path=test.support.TESTFN): - open(path, "w").write(text) + f = open(path, "w") + try: + f.write(text) + finally: + f.close() return path def test_run_setup_provides_file(self): diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index a1647fbc..aa9f9ebb 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -88,8 +88,10 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): mkpath(self.target, verbose=0) a_file = os.path.join(self.target, 'ok.txt') f = open(a_file, 'w') - f.write('some content') - f.close() + try: + f.write('some content') + finally: + f.close() wanted = ['copying %s -> %s' % (a_file, self.target2)] copy_tree(self.target, self.target2, verbose=1) diff --git a/tests/test_dist.py b/tests/test_dist.py index 007803e1..ee8e8d4d 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -79,29 +79,29 @@ class DistributionTestCase(support.LoggingSilencer, def test_command_packages_configfile(self): sys.argv.append("build") + self.addCleanup(os.unlink, TESTFN) f = open(TESTFN, "w") try: print("[global]", file=f) print("command_packages = foo.bar, splat", file=f) + finally: f.close() - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), - ["distutils.command", "foo.bar", "splat"]) - - # ensure command line overrides config: - sys.argv[1:] = ["--command-packages", "spork", "build"] - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), - ["distutils.command", "spork"]) - - # Setting --command-packages to '' should cause the default to - # be used even if a config file specified something else: - sys.argv[1:] = ["--command-packages", "", "build"] - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), ["distutils.command"]) - finally: - os.unlink(TESTFN) + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "foo.bar", "splat"]) + + # ensure command line overrides config: + sys.argv[1:] = ["--command-packages", "spork", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "spork"]) + + # Setting --command-packages to '' should cause the default to + # be used even if a config file specified something else: + sys.argv[1:] = ["--command-packages", "", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), ["distutils.command"]) def test_empty_options(self): # an empty options dictionary should not stay in the @@ -260,8 +260,10 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, temp_dir = self.mkdtemp() user_filename = os.path.join(temp_dir, user_filename) f = open(user_filename, 'w') - f.write('.') - f.close() + try: + f.write('.') + finally: + f.close() try: dist = Distribution() diff --git a/tests/test_file_util.py b/tests/test_file_util.py index fac4a5d1..74618b52 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -31,8 +31,10 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): def test_move_file_verbosity(self): f = open(self.source, 'w') - f.write('some content') - f.close() + try: + f.write('some content') + finally: + f.close() move_file(self.source, self.target, verbose=0) wanted = [] diff --git a/tests/test_install.py b/tests/test_install.py index 76fa02ac..bc407cf9 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -182,8 +182,11 @@ class InstallTestCase(support.TempdirManager, # let's check the RECORD file was created with one # line (the egg info file) - with open(cmd.record) as f: + f = open(cmd.record) + try: self.assertEquals(len(f.readlines()), 1) + finally: + f.close() def test_debug_mode(self): # this covers the code called when DEBUG is set diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index f1da843f..40cb8be6 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -113,17 +113,21 @@ class msvc9compilerTestCase(support.TempdirManager, tempdir = self.mkdtemp() manifest = os.path.join(tempdir, 'manifest') f = open(manifest, 'w') - f.write(_MANIFEST) - f.close() + try: + f.write(_MANIFEST) + finally: + f.close() compiler = MSVCCompiler() compiler._remove_visual_c_ref(manifest) # see what we got f = open(manifest) - # removing trailing spaces - content = '\n'.join([line.rstrip() for line in f.readlines()]) - f.close() + try: + # removing trailing spaces + content = '\n'.join([line.rstrip() for line in f.readlines()]) + finally: + f.close() # makes sure the manifest was properly cleaned self.assertEquals(content, _CLEANED_MANIFEST) diff --git a/tests/test_register.py b/tests/test_register.py index 7d0ac9ee..3b80b6dc 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -118,8 +118,12 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(os.path.exists(self.rc)) # with the content similar to WANTED_PYPIRC - content = open(self.rc).read() - self.assertEquals(content, WANTED_PYPIRC) + f = open(self.rc) + try: + content = f.read() + self.assertEquals(content, WANTED_PYPIRC) + finally: + f.close() # now let's make sure the .pypirc file generated # really works : we shouldn't be asked anything diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 209aa59b..ad527c7d 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -215,8 +215,12 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEquals(len(content), 11) # checking the MANIFEST - manifest = open(join(self.tmp_dir, 'MANIFEST')).read() - self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + f = open(join(self.tmp_dir, 'MANIFEST')) + try: + manifest = f.read() + self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + finally: + f.close() def test_metadata_check_option(self): # testing the `medata-check` option diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 0167e0f1..215dc82d 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -75,9 +75,11 @@ class SysconfigTestCase(support.EnvironGuard, def test_parse_makefile_base(self): self.makefile = TESTFN fd = open(self.makefile, 'w') - fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') - fd.write('VAR=$OTHER\nOTHER=foo') - fd.close() + try: + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + finally: + fd.close() d = sysconfig.parse_makefile(self.makefile) self.assertEquals(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", 'OTHER': 'foo'}) @@ -85,9 +87,11 @@ class SysconfigTestCase(support.EnvironGuard, def test_parse_makefile_literal_dollar(self): self.makefile = TESTFN fd = open(self.makefile, 'w') - fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') - fd.write('VAR=$OTHER\nOTHER=foo') - fd.close() + try: + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + finally: + fd.close() d = sysconfig.parse_makefile(self.makefile) self.assertEquals(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", 'OTHER': 'foo'}) diff --git a/tests/test_text_file.py b/tests/test_text_file.py index 00f083a1..3093097d 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -58,28 +58,46 @@ class TextFileTestCase(support.TempdirManager, unittest.TestCase): finally: out_file.close() - in_file = TextFile (filename, strip_comments=0, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input (1, "no processing", in_file, result1) + in_file = TextFile(filename, strip_comments=0, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(1, "no processing", in_file, result1) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=1, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input (2, "strip comments", in_file, result2) + in_file = TextFile(filename, strip_comments=1, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(2, "strip comments", in_file, result2) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=0, skip_blanks=1, - lstrip_ws=0, rstrip_ws=0) - test_input (3, "strip blanks", in_file, result3) + in_file = TextFile(filename, strip_comments=0, skip_blanks=1, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(3, "strip blanks", in_file, result3) + finally: + in_file.close() - in_file = TextFile (filename) - test_input (4, "default processing", in_file, result4) + in_file = TextFile(filename) + try: + test_input(4, "default processing", in_file, result4) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1) - test_input (5, "join lines without collapsing", in_file, result5) + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1) + try: + test_input(5, "join lines without collapsing", in_file, result5) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1, collapse_join=1) - test_input (6, "join lines with collapsing", in_file, result6) + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1, collapse_join=1) + try: + test_input(6, "join lines with collapsing", in_file, result6) + finally: + in_file.close() def test_suite(): return unittest.makeSuite(TextFileTestCase) diff --git a/util.py b/util.py index 81754345..3081245b 100644 --- a/util.py +++ b/util.py @@ -115,13 +115,15 @@ def get_platform (): # behaviour. pass else: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - f.close() - if m is not None: - macrelease = '.'.join(m.group(1).split('.')[:2]) - # else: fall back to the default behaviour + try: + m = re.search( + r'ProductUserVisibleVersion\s*' + + r'(.*?)', f.read()) + if m is not None: + macrelease = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + finally: + f.close() if not macver: macver = macrelease -- cgit v1.2.1 From 41c052c438ce9e6f236c7015a228c3b9b1cdda2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 5 Nov 2010 23:59:32 +0000 Subject: Of course, I forgot one file in r86223. --- tests/test_install_scripts.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index b7eb625a..08360d29 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -42,8 +42,10 @@ class InstallScriptsTestCase(support.TempdirManager, def write_script(name, text): expected.append(name) f = open(os.path.join(source, name), "w") - f.write(text) - f.close() + try: + f.write(text) + finally: + f.close() write_script("script1.py", ("#! /usr/bin/env python2.3\n" "# bogus script w/ Python sh-bang\n" -- cgit v1.2.1 From 522ffa25d2d5d28511e8e104ff7497a36705365e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 02:10:32 +0000 Subject: Also close file descriptors from os.popen and subprocess.Popen --- command/bdist_rpm.py | 36 ++++++++++++++++++++---------------- msvc9compiler.py | 10 ++++++---- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 452f9502..e2ae877d 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -343,22 +343,26 @@ class bdist_rpm(Command): src_rpm, non_src_rpm, spec_path) out = os.popen(q_cmd) - binary_rpms = [] - source_rpm = None - while True: - line = out.readline() - if not line: - break - l = line.strip().split() - assert(len(l) == 2) - binary_rpms.append(l[1]) - # The source rpm is named after the first entry in the spec file - if source_rpm is None: - source_rpm = l[0] - - status = out.close() - if status: - raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + try: + binary_rpms = [] + source_rpm = None + while True: + line = out.readline() + if not line: + break + l = line.strip().split() + assert(len(l) == 2) + binary_rpms.append(l[1]) + # The source rpm is named after the first entry in the spec file + if source_rpm is None: + source_rpm = l[0] + + status = out.close() + if status: + raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + + finally: + out.close() self.spawn(rpm_cmd) diff --git a/msvc9compiler.py b/msvc9compiler.py index 761b9ca2..6d7825df 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -263,10 +263,12 @@ def query_vcvarsall(version, arch="x86"): popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - stdout, stderr = popen.communicate() - if popen.wait() != 0: - raise DistutilsPlatformError(stderr.decode("mbcs")) + try: + stdout, stderr = popen.communicate() + if popen.wait() != 0: + raise DistutilsPlatformError(stderr.decode("mbcs")) + finally: + popen.close() stdout = stdout.decode("mbcs") for line in stdout.split("\n"): -- cgit v1.2.1 From 74c3638728339edf3a8a99dbd61875e9ce624e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 02:44:43 +0000 Subject: Make sure each test can be run standalone (./python Lib/distutils/tests/x.py) --- tests/__init__.py | 5 +++-- tests/test_archive_util.py | 4 ++-- tests/test_bdist.py | 3 ++- tests/test_bdist_dumb.py | 3 ++- tests/test_bdist_msi.py | 2 +- tests/test_bdist_rpm.py | 3 ++- tests/test_bdist_wininst.py | 3 ++- tests/test_build.py | 3 ++- tests/test_build_clib.py | 4 +++- tests/test_build_ext.py | 1 + tests/test_build_py.py | 3 ++- tests/test_build_scripts.py | 3 ++- tests/test_check.py | 3 ++- tests/test_clean.py | 3 ++- tests/test_cmd.py | 6 +++--- tests/test_config.py | 3 ++- tests/test_config_cmd.py | 3 ++- tests/test_core.py | 4 ++-- tests/test_cygwinccompiler.py | 3 ++- tests/test_dep_util.py | 3 ++- tests/test_dir_util.py | 3 ++- tests/test_dist.py | 4 ++-- tests/test_extension.py | 4 ++-- tests/test_file_util.py | 3 ++- tests/test_filelist.py | 4 ++-- tests/test_install.py | 4 ++-- tests/test_install_data.py | 3 ++- tests/test_install_headers.py | 3 ++- tests/test_install_lib.py | 3 ++- tests/test_install_scripts.py | 3 ++- tests/test_log.py | 3 ++- tests/test_msvc9compiler.py | 3 ++- tests/test_register.py | 4 ++-- tests/test_sdist.py | 8 +++----- tests/test_spawn.py | 4 ++-- tests/test_text_file.py | 3 ++- tests/test_unixccompiler.py | 3 ++- tests/test_upload.py | 5 ++--- tests/test_util.py | 3 ++- tests/test_version.py | 3 ++- tests/test_versionpredicate.py | 4 ++++ 41 files changed, 86 insertions(+), 56 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 7bdb9124..1b939cbd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -15,9 +15,10 @@ by import rather than matching pre-defined names. import os import sys import unittest +from test.support import run_unittest -here = os.path.dirname(__file__) +here = os.path.dirname(__file__) or os.curdir def test_suite(): @@ -32,4 +33,4 @@ def test_suite(): if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index c6e08cbc..9e6264aa 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -12,7 +12,7 @@ from distutils.archive_util import (check_archive_formats, make_tarball, ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support -from test.support import check_warnings +from test.support import check_warnings, run_unittest try: import zipfile @@ -211,4 +211,4 @@ def test_suite(): return unittest.makeSuite(ArchiveUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_bdist.py b/tests/test_bdist.py index f2849a97..bf56e842 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -4,6 +4,7 @@ import sys import os import tempfile import shutil +from test.support import run_unittest from distutils.core import Distribution from distutils.command.bdist import bdist @@ -40,4 +41,4 @@ def test_suite(): return unittest.makeSuite(BuildTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 5e76809f..7d9d0aa8 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -3,6 +3,7 @@ import unittest import sys import os +from test.support import run_unittest from distutils.core import Distribution from distutils.command.bdist_dumb import bdist_dumb @@ -77,4 +78,4 @@ def test_suite(): return unittest.makeSuite(BuildDumbTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index 2b2d8542..9308c79d 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -11,7 +11,7 @@ class BDistMSITestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): - def test_minial(self): + def test_minimal(self): # minimal test XXX need more tests from distutils.command.bdist_msi import bdist_msi pkg_pth, dist = self.create_dist() diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 2aa257f7..804fb135 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -5,6 +5,7 @@ import sys import os import tempfile import shutil +from test.support import run_unittest from distutils.core import Distribution from distutils.command.bdist_rpm import bdist_rpm @@ -122,4 +123,4 @@ def test_suite(): return unittest.makeSuite(BuildRpmTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index 9b1ba6d1..f9e8f89e 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -1,5 +1,6 @@ """Tests for distutils.command.bdist_wininst.""" import unittest +from test.support import run_unittest from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support @@ -27,4 +28,4 @@ def test_suite(): return unittest.makeSuite(BuildWinInstTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_build.py b/tests/test_build.py index 2418e165..9f0e0ad2 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -2,6 +2,7 @@ import unittest import os import sys +from test.support import run_unittest from distutils.command.build import build from distutils.tests import support @@ -51,4 +52,4 @@ def test_suite(): return unittest.makeSuite(BuildTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 536cd673..e59b8f9a 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -3,6 +3,8 @@ import unittest import os import sys +from test.support import run_unittest + from distutils.command.build_clib import build_clib from distutils.errors import DistutilsSetupError from distutils.tests import support @@ -141,4 +143,4 @@ def test_suite(): return unittest.makeSuite(BuildCLibTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 18e00117..46ac9aaf 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -14,6 +14,7 @@ from distutils.errors import ( import unittest from test import support +from test.support import run_unittest # http://bugs.python.org/issue4373 # Don't load the xx module more than once. diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 8a69aaa0..da3232ce 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -10,6 +10,7 @@ from distutils.core import Distribution from distutils.errors import DistutilsFileError from distutils.tests import support +from test.support import run_unittest class BuildPyTestCase(support.TempdirManager, @@ -114,4 +115,4 @@ def test_suite(): return unittest.makeSuite(BuildPyTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 85b04004..e3326b85 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -8,6 +8,7 @@ from distutils.core import Distribution from distutils import sysconfig from distutils.tests import support +from test.support import run_unittest class BuildScriptsTestCase(support.TempdirManager, @@ -108,4 +109,4 @@ def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_check.py b/tests/test_check.py index 372bae36..6ad2fe39 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -1,5 +1,6 @@ """Tests for distutils.command.check.""" import unittest +from test.support import run_unittest from distutils.command.check import check, HAS_DOCUTILS from distutils.tests import support @@ -95,4 +96,4 @@ def test_suite(): return unittest.makeSuite(CheckTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_clean.py b/tests/test_clean.py index dbc4ee2a..649855f7 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -6,6 +6,7 @@ import getpass from distutils.command.clean import clean from distutils.tests import support +from test.support import run_unittest class cleanTestCase(support.TempdirManager, support.LoggingSilencer, @@ -47,4 +48,4 @@ def test_suite(): return unittest.makeSuite(cleanTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 55ae421d..969f82ba 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,7 +1,7 @@ """Tests for distutils.cmd.""" import unittest import os -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils.cmd import Command from distutils.dist import Distribution @@ -99,7 +99,7 @@ class CommandTestCase(unittest.TestCase): def test_ensure_dirname(self): cmd = self.cmd - cmd.option1 = os.path.dirname(__file__) + cmd.option1 = os.path.dirname(__file__) or os.curdir cmd.ensure_dirname('option1') cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') @@ -124,4 +124,4 @@ def test_suite(): return unittest.makeSuite(CommandTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_config.py b/tests/test_config.py index 6a45a328..05a35da9 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -10,6 +10,7 @@ from distutils.log import set_threshold from distutils.log import WARN from distutils.tests import support +from test.support import run_unittest PYPIRC = """\ [distutils] @@ -116,4 +117,4 @@ def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index ef2e7bc3..c224a5c3 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -2,6 +2,7 @@ import unittest import os import sys +from test.support import run_unittest from distutils.command.config import dump_file, config from distutils.tests import support @@ -86,4 +87,4 @@ def test_suite(): return unittest.makeSuite(ConfigTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_core.py b/tests/test_core.py index e937b45a..47fae245 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,7 +6,7 @@ import os import shutil import sys import test.support -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest import unittest from distutils.tests import support @@ -105,4 +105,4 @@ def test_suite(): return unittest.makeSuite(CoreTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index a57694d4..e57bab26 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -4,6 +4,7 @@ import sys import os from io import BytesIO import subprocess +from test.support import run_unittest from distutils import cygwinccompiler from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, @@ -151,4 +152,4 @@ def test_suite(): return unittest.makeSuite(CygwinCCompilerTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_dep_util.py b/tests/test_dep_util.py index d81d9143..390ed9b6 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -6,6 +6,7 @@ import time from distutils.dep_util import newer, newer_pairwise, newer_group from distutils.errors import DistutilsFileError from distutils.tests import support +from test.support import run_unittest class DepUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -77,4 +78,4 @@ def test_suite(): return unittest.makeSuite(DepUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index aa9f9ebb..7356c766 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -10,6 +10,7 @@ from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, from distutils import log from distutils.tests import support +from test.support import run_unittest class DirUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -112,4 +113,4 @@ def test_suite(): return unittest.makeSuite(DirUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_dist.py b/tests/test_dist.py index ee8e8d4d..a8eb8b1d 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -9,7 +9,7 @@ import textwrap from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command -from test.support import TESTFN, captured_stdout +from test.support import TESTFN, captured_stdout, run_unittest from distutils.tests import support @@ -325,4 +325,4 @@ def test_suite(): return suite if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_extension.py b/tests/test_extension.py index 1ee30585..c9c89657 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -3,7 +3,7 @@ import unittest import os import warnings -from test.support import check_warnings +from test.support import check_warnings, run_unittest from distutils.extension import read_setup_file, Extension class ExtensionTestCase(unittest.TestCase): @@ -66,4 +66,4 @@ def test_suite(): return unittest.makeSuite(ExtensionTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 74618b52..be743f34 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -6,6 +6,7 @@ import shutil from distutils.file_util import move_file from distutils import log from distutils.tests import support +from test.support import run_unittest class FileUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -62,4 +63,4 @@ def test_suite(): return unittest.makeSuite(FileUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 331180d2..6312a294 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -2,7 +2,7 @@ import unittest from distutils.filelist import glob_to_re, FileList -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils import debug class FileListTestCase(unittest.TestCase): @@ -39,4 +39,4 @@ def test_suite(): return unittest.makeSuite(FileListTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install.py b/tests/test_install.py index bc407cf9..22e79b89 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -6,7 +6,7 @@ import sys import unittest import site -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils.command.install import install from distutils.command import install as install_module @@ -203,4 +203,4 @@ def test_suite(): return unittest.makeSuite(InstallTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 377ae86e..6b3b4c82 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -6,6 +6,7 @@ import getpass from distutils.command.install_data import install_data from distutils.tests import support +from test.support import run_unittest class InstallDataTestCase(support.TempdirManager, support.LoggingSilencer, @@ -73,4 +74,4 @@ def test_suite(): return unittest.makeSuite(InstallDataTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index 5b32b13e..dc74c58d 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -6,6 +6,7 @@ import getpass from distutils.command.install_headers import install_headers from distutils.tests import support +from test.support import run_unittest class InstallHeadersTestCase(support.TempdirManager, support.LoggingSilencer, @@ -37,4 +38,4 @@ def test_suite(): return unittest.makeSuite(InstallHeadersTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 99a6d906..790d4ce3 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -7,6 +7,7 @@ from distutils.command.install_lib import install_lib from distutils.extension import Extension from distutils.tests import support from distutils.errors import DistutilsOptionError +from test.support import run_unittest class InstallLibTestCase(support.TempdirManager, support.LoggingSilencer, @@ -98,4 +99,4 @@ def test_suite(): return unittest.makeSuite(InstallLibTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index 08360d29..8952e744 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -7,6 +7,7 @@ from distutils.command.install_scripts import install_scripts from distutils.core import Distribution from distutils.tests import support +from test.support import run_unittest class InstallScriptsTestCase(support.TempdirManager, @@ -78,4 +79,4 @@ def test_suite(): return unittest.makeSuite(InstallScriptsTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_log.py b/tests/test_log.py index d35de345..5f87076b 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -3,6 +3,7 @@ import sys import unittest from tempfile import NamedTemporaryFile +from test.support import run_unittest from distutils import log @@ -33,4 +34,4 @@ def test_suite(): return unittest.makeSuite(TestLog) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 40cb8be6..45bae77a 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -5,6 +5,7 @@ import os from distutils.errors import DistutilsPlatformError from distutils.tests import support +from test.support import run_unittest _MANIFEST = """\ @@ -137,4 +138,4 @@ def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_register.py b/tests/test_register.py index 3b80b6dc..9336ac86 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -6,7 +6,7 @@ import getpass import urllib import warnings -from test.support import check_warnings +from test.support import check_warnings, run_unittest from distutils.command import register as register_module from distutils.command.register import register @@ -259,4 +259,4 @@ def test_suite(): return unittest.makeSuite(RegisterTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index ad527c7d..73962d34 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -8,11 +8,9 @@ import sys import tempfile import warnings -from test.support import check_warnings -from test.support import captured_stdout +from test.support import captured_stdout, check_warnings, run_unittest -from distutils.command.sdist import sdist -from distutils.command.sdist import show_formats +from distutils.command.sdist import sdist, show_formats from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError, DistutilsOptionError @@ -358,4 +356,4 @@ def test_suite(): return unittest.makeSuite(SDistTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 950e5789..5b91aa5a 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -2,7 +2,7 @@ import unittest import os import time -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils.spawn import _nt_quote_args from distutils.spawn import spawn, find_executable @@ -55,4 +55,4 @@ def test_suite(): return unittest.makeSuite(SpawnTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_text_file.py b/tests/test_text_file.py index 3093097d..953cb8ea 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -3,6 +3,7 @@ import os import unittest from distutils.text_file import TextFile from distutils.tests import support +from test.support import run_unittest TEST_DATA = """# test file @@ -103,4 +104,4 @@ def test_suite(): return unittest.makeSuite(TextFileTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 3a41e6fc..1bff38e9 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -1,6 +1,7 @@ """Tests for distutils.unixccompiler.""" import sys import unittest +from test.support import run_unittest from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler @@ -118,4 +119,4 @@ def test_suite(): return unittest.makeSuite(UnixCCompilerTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_upload.py b/tests/test_upload.py index 35e97005..4661ed32 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -1,13 +1,12 @@ """Tests for distutils.command.upload.""" -import sys import os import unittest import http.client as httpclient +from test.support import run_unittest from distutils.command.upload import upload from distutils.core import Distribution -from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase PYPIRC_LONG_PASSWORD = """\ @@ -136,4 +135,4 @@ def test_suite(): return unittest.makeSuite(uploadTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_util.py b/tests/test_util.py index 0c732f82..3b7159d7 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -3,6 +3,7 @@ import os import sys import unittest from copy import copy +from test.support import run_unittest from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import (get_platform, convert_path, change_root, @@ -274,4 +275,4 @@ def test_suite(): return unittest.makeSuite(UtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_version.py b/tests/test_version.py index fa214330..ff40f6b4 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -2,6 +2,7 @@ import unittest from distutils.version import LooseVersion from distutils.version import StrictVersion +from test.support import run_unittest class VersionTestCase(unittest.TestCase): @@ -67,4 +68,4 @@ def test_suite(): return unittest.makeSuite(VersionTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_versionpredicate.py b/tests/test_versionpredicate.py index 8a60dbe8..28ae09dc 100644 --- a/tests/test_versionpredicate.py +++ b/tests/test_versionpredicate.py @@ -4,6 +4,10 @@ import distutils.versionpredicate import doctest +from test.support import run_unittest def test_suite(): return doctest.DocTestSuite(distutils.versionpredicate) + +if __name__ == '__main__': + run_unittest(test_suite()) -- cgit v1.2.1 From c705a947d03d1505af6e1aeed5db58860fd5b717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 02:58:56 +0000 Subject: Merged revisions 86223-86224,86226,86234 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86223 | eric.araujo | 2010-11-06 00:51:56 +0100 (sam., 06 nov. 2010) | 2 lines Always close files in distutils code and tests (#10252). ........ r86224 | eric.araujo | 2010-11-06 00:58:34 +0100 (sam., 06 nov. 2010) | 2 lines Add missing entry for r86223. ........ r86226 | eric.araujo | 2010-11-06 00:59:32 +0100 (sam., 06 nov. 2010) | 2 lines Of course, I forgot one file in r86223. ........ r86234 | eric.araujo | 2010-11-06 03:10:32 +0100 (sam., 06 nov. 2010) | 2 lines Also close file descriptors from os.popen and subprocess.Popen ........ --- ccompiler.py | 10 +-- command/bdist_rpm.py | 36 +++++----- command/bdist_wininst.py | 6 +- command/upload.py | 6 +- core.py | 6 +- cygwinccompiler.py | 5 +- dist.py | 8 ++- emxccompiler.py | 12 ++-- extension.py | 155 +++++++++++++++++++++--------------------- file_util.py | 8 ++- msvc9compiler.py | 10 +-- tests/test_build_py.py | 12 ++-- tests/test_build_scripts.py | 6 +- tests/test_config.py | 8 ++- tests/test_core.py | 6 +- tests/test_dir_util.py | 6 +- tests/test_dist.py | 40 +++++------ tests/test_file_util.py | 6 +- tests/test_install.py | 5 +- tests/test_install_scripts.py | 6 +- tests/test_msvc9compiler.py | 14 ++-- tests/test_register.py | 8 ++- tests/test_sdist.py | 8 ++- tests/test_sysconfig.py | 16 +++-- tests/test_text_file.py | 52 +++++++++----- util.py | 16 +++-- 26 files changed, 283 insertions(+), 188 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 34c77a37..291c008f 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -779,14 +779,16 @@ class CCompiler: library_dirs = [] fd, fname = tempfile.mkstemp(".c", funcname, text=True) f = os.fdopen(fd, "w") - for incl in includes: - f.write("""#include "%s"\n""" % incl) - f.write("""\ + try: + for incl in includes: + f.write("""#include "%s"\n""" % incl) + f.write("""\ main (int argc, char **argv) { %s(); } """ % funcname) - f.close() + finally: + f.close() try: objects = self.compile([fname], include_dirs=include_dirs) except CompileError: diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 452f9502..e2ae877d 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -343,22 +343,26 @@ class bdist_rpm(Command): src_rpm, non_src_rpm, spec_path) out = os.popen(q_cmd) - binary_rpms = [] - source_rpm = None - while True: - line = out.readline() - if not line: - break - l = line.strip().split() - assert(len(l) == 2) - binary_rpms.append(l[1]) - # The source rpm is named after the first entry in the spec file - if source_rpm is None: - source_rpm = l[0] - - status = out.close() - if status: - raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + try: + binary_rpms = [] + source_rpm = None + while True: + line = out.readline() + if not line: + break + l = line.strip().split() + assert(len(l) == 2) + binary_rpms.append(l[1]) + # The source rpm is named after the first entry in the spec file + if source_rpm is None: + source_rpm = l[0] + + status = out.close() + if status: + raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + + finally: + out.close() self.spawn(rpm_cmd) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 3aa1dac7..b2e2fc6d 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -340,4 +340,8 @@ class bdist_wininst(Command): sfix = '' filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) - return open(filename, "rb").read() + f = open(filename, "rb") + try: + return f.read() + finally: + f.close() diff --git a/command/upload.py b/command/upload.py index 99e03d74..4926aa3e 100644 --- a/command/upload.py +++ b/command/upload.py @@ -76,7 +76,11 @@ class upload(PyPIRCCommand): # Fill in the data - send all the meta-data in case we need to # register a new release - content = open(filename,'rb').read() + f = open(filename,'rb') + try: + content = f.read() + finally: + f.close() meta = self.distribution.metadata data = { # action diff --git a/core.py b/core.py index 6e489203..fd2a43d7 100644 --- a/core.py +++ b/core.py @@ -215,7 +215,11 @@ def run_setup (script_name, script_args=None, stop_after="run"): sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args - exec(open(script_name).read(), g, l) + f = open(script_name) + try: + exec(f.read(), g, l) + finally: + f.close() finally: sys.argv = save_argv _setup_stop_after = None diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 85043718..95fa3ed3 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -350,11 +350,14 @@ def check_config_h(): # let's see if __GNUC__ is mentioned in python.h fn = sysconfig.get_config_h_filename() try: - with open(fn) as config_h: + config_h = open(fn) + try: if "__GNUC__" in config_h.read(): return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn else: return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn + finally: + config_h.close() except IOError as exc: return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) diff --git a/dist.py b/dist.py index 1c1ea477..01f1f1cf 100644 --- a/dist.py +++ b/dist.py @@ -1012,9 +1012,11 @@ class DistributionMetadata: def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. """ - pkg_info = open( os.path.join(base_dir, 'PKG-INFO'), 'w') - self.write_pkg_file(pkg_info) - pkg_info.close() + pkg_info = open(os.path.join(base_dir, 'PKG-INFO'), 'w') + try: + self.write_pkg_file(pkg_info) + finally: + pkg_info.close() def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. diff --git a/emxccompiler.py b/emxccompiler.py index 62a4c5b4..16dce535 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -270,8 +270,10 @@ def check_config_h(): # It would probably better to read single lines to search. # But we do this only once, and it is fast enough f = open(fn) - s = f.read() - f.close() + try: + s = f.read() + finally: + f.close() except IOError as exc: # if we can't read this file, we cannot say it is wrong @@ -298,8 +300,10 @@ def get_versions(): gcc_exe = find_executable('gcc') if gcc_exe: out = os.popen(gcc_exe + ' -dumpversion','r') - out_string = out.read() - out.close() + try: + out_string = out.read() + finally: + out.close() result = re.search('(\d+\.\d+\.\d+)', out_string, re.ASCII) if result: gcc_version = StrictVersion(result.group(1)) diff --git a/extension.py b/extension.py index 5c07bdae..2d1c36bd 100644 --- a/extension.py +++ b/extension.py @@ -149,84 +149,87 @@ def read_setup_file(filename): file = TextFile(filename, strip_comments=1, skip_blanks=1, join_lines=1, lstrip_ws=1, rstrip_ws=1) - extensions = [] - - while True: - line = file.readline() - if line is None: # eof - break - if _variable_rx.match(line): # VAR=VALUE, handled in first pass - continue - - if line[0] == line[-1] == "*": - file.warn("'%s' lines not handled yet" % line) - continue - - line = expand_makefile_vars(line, vars) - words = split_quoted(line) - - # NB. this parses a slightly different syntax than the old - # makesetup script: here, there must be exactly one extension per - # line, and it must be the first word of the line. I have no idea - # why the old syntax supported multiple extensions per line, as - # they all wind up being the same. - - module = words[0] - ext = Extension(module, []) - append_next_word = None - - for word in words[1:]: - if append_next_word is not None: - append_next_word.append(word) - append_next_word = None + try: + extensions = [] + + while True: + line = file.readline() + if line is None: # eof + break + if _variable_rx.match(line): # VAR=VALUE, handled in first pass continue - suffix = os.path.splitext(word)[1] - switch = word[0:2] ; value = word[2:] - - if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): - # hmm, should we do something about C vs. C++ sources? - # or leave it up to the CCompiler implementation to - # worry about? - ext.sources.append(word) - elif switch == "-I": - ext.include_dirs.append(value) - elif switch == "-D": - equals = value.find("=") - if equals == -1: # bare "-DFOO" -- no value - ext.define_macros.append((value, None)) - else: # "-DFOO=blah" - ext.define_macros.append((value[0:equals], - value[equals+2:])) - elif switch == "-U": - ext.undef_macros.append(value) - elif switch == "-C": # only here 'cause makesetup has it! - ext.extra_compile_args.append(word) - elif switch == "-l": - ext.libraries.append(value) - elif switch == "-L": - ext.library_dirs.append(value) - elif switch == "-R": - ext.runtime_library_dirs.append(value) - elif word == "-rpath": - append_next_word = ext.runtime_library_dirs - elif word == "-Xlinker": - append_next_word = ext.extra_link_args - elif word == "-Xcompiler": - append_next_word = ext.extra_compile_args - elif switch == "-u": - ext.extra_link_args.append(word) - if not value: + if line[0] == line[-1] == "*": + file.warn("'%s' lines not handled yet" % line) + continue + + line = expand_makefile_vars(line, vars) + words = split_quoted(line) + + # NB. this parses a slightly different syntax than the old + # makesetup script: here, there must be exactly one extension per + # line, and it must be the first word of the line. I have no idea + # why the old syntax supported multiple extensions per line, as + # they all wind up being the same. + + module = words[0] + ext = Extension(module, []) + append_next_word = None + + for word in words[1:]: + if append_next_word is not None: + append_next_word.append(word) + append_next_word = None + continue + + suffix = os.path.splitext(word)[1] + switch = word[0:2] ; value = word[2:] + + if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): + # hmm, should we do something about C vs. C++ sources? + # or leave it up to the CCompiler implementation to + # worry about? + ext.sources.append(word) + elif switch == "-I": + ext.include_dirs.append(value) + elif switch == "-D": + equals = value.find("=") + if equals == -1: # bare "-DFOO" -- no value + ext.define_macros.append((value, None)) + else: # "-DFOO=blah" + ext.define_macros.append((value[0:equals], + value[equals+2:])) + elif switch == "-U": + ext.undef_macros.append(value) + elif switch == "-C": # only here 'cause makesetup has it! + ext.extra_compile_args.append(word) + elif switch == "-l": + ext.libraries.append(value) + elif switch == "-L": + ext.library_dirs.append(value) + elif switch == "-R": + ext.runtime_library_dirs.append(value) + elif word == "-rpath": + append_next_word = ext.runtime_library_dirs + elif word == "-Xlinker": append_next_word = ext.extra_link_args - elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): - # NB. a really faithful emulation of makesetup would - # append a .o file to extra_objects only if it - # had a slash in it; otherwise, it would s/.o/.c/ - # and append it to sources. Hmmmm. - ext.extra_objects.append(word) - else: - file.warn("unrecognized argument '%s'" % word) - - extensions.append(ext) + elif word == "-Xcompiler": + append_next_word = ext.extra_compile_args + elif switch == "-u": + ext.extra_link_args.append(word) + if not value: + append_next_word = ext.extra_link_args + elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): + # NB. a really faithful emulation of makesetup would + # append a .o file to extra_objects only if it + # had a slash in it; otherwise, it would s/.o/.c/ + # and append it to sources. Hmmmm. + ext.extra_objects.append(word) + else: + file.warn("unrecognized argument '%s'" % word) + + extensions.append(ext) + finally: + file.close() return extensions diff --git a/file_util.py b/file_util.py index 65aa7e0f..c36e7128 100644 --- a/file_util.py +++ b/file_util.py @@ -234,6 +234,8 @@ def write_file (filename, contents): sequence of strings without line terminators) to it. """ f = open(filename, "w") - for line in contents: - f.write(line + "\n") - f.close() + try: + for line in contents: + f.write(line + "\n") + finally: + f.close() diff --git a/msvc9compiler.py b/msvc9compiler.py index 761b9ca2..6d7825df 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -263,10 +263,12 @@ def query_vcvarsall(version, arch="x86"): popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - stdout, stderr = popen.communicate() - if popen.wait() != 0: - raise DistutilsPlatformError(stderr.decode("mbcs")) + try: + stdout, stderr = popen.communicate() + if popen.wait() != 0: + raise DistutilsPlatformError(stderr.decode("mbcs")) + finally: + popen.close() stdout = stdout.decode("mbcs") for line in stdout.split("\n"): diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 3e45f6e8..8a69aaa0 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -19,11 +19,15 @@ class BuildPyTestCase(support.TempdirManager, def test_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") - f.write("# Pretend this is a package.") - f.close() + try: + f.write("# Pretend this is a package.") + finally: + f.close() f = open(os.path.join(sources, "README.txt"), "w") - f.write("Info about this package") - f.close() + try: + f.write("Info about this package") + finally: + f.close() destination = self.mkdtemp() diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index b1d2d079..85b04004 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -71,8 +71,10 @@ class BuildScriptsTestCase(support.TempdirManager, def write_script(self, dir, name, text): f = open(os.path.join(dir, name), "w") - f.write(text) - f.close() + try: + f.write(text) + finally: + f.close() def test_version_int(self): source = self.mkdtemp() diff --git a/tests/test_config.py b/tests/test_config.py index 71c63678..6a45a328 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -105,8 +105,12 @@ class PyPIRCCommandTestCase(support.TempdirManager, self.assertTrue(not os.path.exists(rc)) cmd._store_pypirc('tarek', 'xxx') self.assertTrue(os.path.exists(rc)) - content = open(rc).read() - self.assertEquals(content, WANTED) + f = open(rc) + try: + content = f.read() + self.assertEquals(content, WANTED) + finally: + f.close() def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) diff --git a/tests/test_core.py b/tests/test_core.py index b478fa62..e937b45a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -52,7 +52,11 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): shutil.rmtree(path) def write_setup(self, text, path=test.support.TESTFN): - open(path, "w").write(text) + f = open(path, "w") + try: + f.write(text) + finally: + f.close() return path def test_run_setup_provides_file(self): diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index a1647fbc..aa9f9ebb 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -88,8 +88,10 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): mkpath(self.target, verbose=0) a_file = os.path.join(self.target, 'ok.txt') f = open(a_file, 'w') - f.write('some content') - f.close() + try: + f.write('some content') + finally: + f.close() wanted = ['copying %s -> %s' % (a_file, self.target2)] copy_tree(self.target, self.target2, verbose=1) diff --git a/tests/test_dist.py b/tests/test_dist.py index 3b7637f3..f9c5a4fd 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -80,29 +80,29 @@ class DistributionTestCase(support.LoggingSilencer, def test_command_packages_configfile(self): sys.argv.append("build") + self.addCleanup(os.unlink, TESTFN) f = open(TESTFN, "w") try: print("[global]", file=f) print("command_packages = foo.bar, splat", file=f) + finally: f.close() - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), - ["distutils.command", "foo.bar", "splat"]) - - # ensure command line overrides config: - sys.argv[1:] = ["--command-packages", "spork", "build"] - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), - ["distutils.command", "spork"]) - - # Setting --command-packages to '' should cause the default to - # be used even if a config file specified something else: - sys.argv[1:] = ["--command-packages", "", "build"] - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), ["distutils.command"]) - finally: - os.unlink(TESTFN) + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "foo.bar", "splat"]) + + # ensure command line overrides config: + sys.argv[1:] = ["--command-packages", "spork", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "spork"]) + + # Setting --command-packages to '' should cause the default to + # be used even if a config file specified something else: + sys.argv[1:] = ["--command-packages", "", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), ["distutils.command"]) def test_empty_options(self): # an empty options dictionary should not stay in the @@ -261,8 +261,10 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, temp_dir = self.mkdtemp() user_filename = os.path.join(temp_dir, user_filename) f = open(user_filename, 'w') - f.write('.') - f.close() + try: + f.write('.') + finally: + f.close() try: dist = Distribution() diff --git a/tests/test_file_util.py b/tests/test_file_util.py index fac4a5d1..74618b52 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -31,8 +31,10 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): def test_move_file_verbosity(self): f = open(self.source, 'w') - f.write('some content') - f.close() + try: + f.write('some content') + finally: + f.close() move_file(self.source, self.target, verbose=0) wanted = [] diff --git a/tests/test_install.py b/tests/test_install.py index 76fa02ac..bc407cf9 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -182,8 +182,11 @@ class InstallTestCase(support.TempdirManager, # let's check the RECORD file was created with one # line (the egg info file) - with open(cmd.record) as f: + f = open(cmd.record) + try: self.assertEquals(len(f.readlines()), 1) + finally: + f.close() def test_debug_mode(self): # this covers the code called when DEBUG is set diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index b7eb625a..08360d29 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -42,8 +42,10 @@ class InstallScriptsTestCase(support.TempdirManager, def write_script(name, text): expected.append(name) f = open(os.path.join(source, name), "w") - f.write(text) - f.close() + try: + f.write(text) + finally: + f.close() write_script("script1.py", ("#! /usr/bin/env python2.3\n" "# bogus script w/ Python sh-bang\n" diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index f1da843f..40cb8be6 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -113,17 +113,21 @@ class msvc9compilerTestCase(support.TempdirManager, tempdir = self.mkdtemp() manifest = os.path.join(tempdir, 'manifest') f = open(manifest, 'w') - f.write(_MANIFEST) - f.close() + try: + f.write(_MANIFEST) + finally: + f.close() compiler = MSVCCompiler() compiler._remove_visual_c_ref(manifest) # see what we got f = open(manifest) - # removing trailing spaces - content = '\n'.join([line.rstrip() for line in f.readlines()]) - f.close() + try: + # removing trailing spaces + content = '\n'.join([line.rstrip() for line in f.readlines()]) + finally: + f.close() # makes sure the manifest was properly cleaned self.assertEquals(content, _CLEANED_MANIFEST) diff --git a/tests/test_register.py b/tests/test_register.py index 7d0ac9ee..3b80b6dc 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -118,8 +118,12 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(os.path.exists(self.rc)) # with the content similar to WANTED_PYPIRC - content = open(self.rc).read() - self.assertEquals(content, WANTED_PYPIRC) + f = open(self.rc) + try: + content = f.read() + self.assertEquals(content, WANTED_PYPIRC) + finally: + f.close() # now let's make sure the .pypirc file generated # really works : we shouldn't be asked anything diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 209aa59b..ad527c7d 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -215,8 +215,12 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEquals(len(content), 11) # checking the MANIFEST - manifest = open(join(self.tmp_dir, 'MANIFEST')).read() - self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + f = open(join(self.tmp_dir, 'MANIFEST')) + try: + manifest = f.read() + self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + finally: + f.close() def test_metadata_check_option(self): # testing the `medata-check` option diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index edc755ed..78bce946 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -75,9 +75,11 @@ class SysconfigTestCase(support.EnvironGuard, def test_parse_makefile_base(self): self.makefile = TESTFN fd = open(self.makefile, 'w') - fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') - fd.write('VAR=$OTHER\nOTHER=foo') - fd.close() + try: + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + finally: + fd.close() d = sysconfig.parse_makefile(self.makefile) self.assertEquals(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", 'OTHER': 'foo'}) @@ -85,9 +87,11 @@ class SysconfigTestCase(support.EnvironGuard, def test_parse_makefile_literal_dollar(self): self.makefile = TESTFN fd = open(self.makefile, 'w') - fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') - fd.write('VAR=$OTHER\nOTHER=foo') - fd.close() + try: + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + finally: + fd.close() d = sysconfig.parse_makefile(self.makefile) self.assertEquals(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", 'OTHER': 'foo'}) diff --git a/tests/test_text_file.py b/tests/test_text_file.py index 00f083a1..3093097d 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -58,28 +58,46 @@ class TextFileTestCase(support.TempdirManager, unittest.TestCase): finally: out_file.close() - in_file = TextFile (filename, strip_comments=0, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input (1, "no processing", in_file, result1) + in_file = TextFile(filename, strip_comments=0, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(1, "no processing", in_file, result1) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=1, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input (2, "strip comments", in_file, result2) + in_file = TextFile(filename, strip_comments=1, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(2, "strip comments", in_file, result2) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=0, skip_blanks=1, - lstrip_ws=0, rstrip_ws=0) - test_input (3, "strip blanks", in_file, result3) + in_file = TextFile(filename, strip_comments=0, skip_blanks=1, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(3, "strip blanks", in_file, result3) + finally: + in_file.close() - in_file = TextFile (filename) - test_input (4, "default processing", in_file, result4) + in_file = TextFile(filename) + try: + test_input(4, "default processing", in_file, result4) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1) - test_input (5, "join lines without collapsing", in_file, result5) + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1) + try: + test_input(5, "join lines without collapsing", in_file, result5) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1, collapse_join=1) - test_input (6, "join lines with collapsing", in_file, result6) + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1, collapse_join=1) + try: + test_input(6, "join lines with collapsing", in_file, result6) + finally: + in_file.close() def test_suite(): return unittest.makeSuite(TextFileTestCase) diff --git a/util.py b/util.py index 81754345..3081245b 100644 --- a/util.py +++ b/util.py @@ -115,13 +115,15 @@ def get_platform (): # behaviour. pass else: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - f.close() - if m is not None: - macrelease = '.'.join(m.group(1).split('.')[:2]) - # else: fall back to the default behaviour + try: + m = re.search( + r'ProductUserVisibleVersion\s*' + + r'(.*?)', f.read()) + if m is not None: + macrelease = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + finally: + f.close() if not macver: macver = macrelease -- cgit v1.2.1 From a5d239446824885f4c30d1924d3368874c9319ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 04:06:18 +0000 Subject: Merged revisions 86223-86224,86226,86234 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86223 | eric.araujo | 2010-11-06 00:51:56 +0100 (sam., 06 nov. 2010) | 2 lines Always close files in distutils code and tests (#10252). ........ r86224 | eric.araujo | 2010-11-06 00:58:34 +0100 (sam., 06 nov. 2010) | 2 lines Add missing entry for r86223. ........ r86226 | eric.araujo | 2010-11-06 00:59:32 +0100 (sam., 06 nov. 2010) | 2 lines Of course, I forgot one file in r86223. ........ r86234 | eric.araujo | 2010-11-06 03:10:32 +0100 (sam., 06 nov. 2010) | 2 lines Also close file descriptors from os.popen and subprocess.Popen ........ --- ccompiler.py | 10 ++- command/bdist_rpm.py | 36 +++++---- command/bdist_wininst.py | 6 +- command/upload.py | 6 +- core.py | 6 +- cygwinccompiler.py | 6 +- dist.py | 8 +- emxccompiler.py | 12 ++- extension.py | 165 ++++++++++++++++++++++-------------------- file_util.py | 8 +- msvc9compiler.py | 10 ++- tests/test_build_py.py | 12 ++- tests/test_build_scripts.py | 6 +- tests/test_config.py | 8 +- tests/test_core.py | 6 +- tests/test_dir_util.py | 6 +- tests/test_dist.py | 40 +++++----- tests/test_file_util.py | 6 +- tests/test_install_scripts.py | 6 +- tests/test_msvc9compiler.py | 14 ++-- tests/test_register.py | 8 +- tests/test_sdist.py | 8 +- tests/test_sysconfig.py | 16 ++-- tests/test_text_file.py | 52 ++++++++----- util.py | 16 ++-- 25 files changed, 287 insertions(+), 190 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index a34177e7..c2b1f6fb 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -794,14 +794,16 @@ class CCompiler: library_dirs = [] fd, fname = tempfile.mkstemp(".c", funcname, text=True) f = os.fdopen(fd, "w") - for incl in includes: - f.write("""#include "%s"\n""" % incl) - f.write("""\ + try: + for incl in includes: + f.write("""#include "%s"\n""" % incl) + f.write("""\ main (int argc, char **argv) { %s(); } """ % funcname) - f.close() + finally: + f.close() try: objects = self.compile([fname], include_dirs=include_dirs) except CompileError: diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 6d9d47d2..0bba3635 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -355,22 +355,26 @@ class bdist_rpm (Command): src_rpm, non_src_rpm, spec_path) out = os.popen(q_cmd) - binary_rpms = [] - source_rpm = None - while 1: - line = out.readline() - if not line: - break - l = string.split(string.strip(line)) - assert(len(l) == 2) - binary_rpms.append(l[1]) - # The source rpm is named after the first entry in the spec file - if source_rpm is None: - source_rpm = l[0] - - status = out.close() - if status: - raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + try: + binary_rpms = [] + source_rpm = None + while 1: + line = out.readline() + if not line: + break + l = string.split(string.strip(line)) + assert(len(l) == 2) + binary_rpms.append(l[1]) + # The source rpm is named after the first entry in the spec file + if source_rpm is None: + source_rpm = l[0] + + status = out.close() + if status: + raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + + finally: + out.close() self.spawn(rpm_cmd) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index a31a5f7b..36d46bd6 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -356,5 +356,9 @@ class bdist_wininst (Command): sfix = '' filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) - return open(filename, "rb").read() + f = open(filename, "rb") + try: + return f.read() + finally: + f.close() # class bdist_wininst diff --git a/command/upload.py b/command/upload.py index c3f19d20..980cf68d 100644 --- a/command/upload.py +++ b/command/upload.py @@ -79,7 +79,11 @@ class upload(PyPIRCCommand): # Fill in the data - send all the meta-data in case we need to # register a new release - content = open(filename,'rb').read() + f = open(filename,'rb') + try: + content = f.read() + finally: + f.close() meta = self.distribution.metadata data = { # action diff --git a/core.py b/core.py index 99ccf44f..b89557d7 100644 --- a/core.py +++ b/core.py @@ -216,7 +216,11 @@ def run_setup(script_name, script_args=None, stop_after="run"): sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args - exec open(script_name, 'r').read() in g, l + f = open(script_name) + try: + exec f.read() in g, l + finally: + f.close() finally: sys.argv = save_argv _setup_stop_after = None diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 2dabc0f0..a1ee815c 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -382,8 +382,10 @@ def check_config_h(): # It would probably better to read single lines to search. # But we do this only once, and it is fast enough f = open(fn) - s = f.read() - f.close() + try: + s = f.read() + finally: + f.close() except IOError, exc: # if we can't read this file, we cannot say it is wrong diff --git a/dist.py b/dist.py index 5dbdaef1..597909ea 100644 --- a/dist.py +++ b/dist.py @@ -1101,9 +1101,11 @@ class DistributionMetadata: def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. """ - pkg_info = open( os.path.join(base_dir, 'PKG-INFO'), 'w') - self.write_pkg_file(pkg_info) - pkg_info.close() + pkg_info = open(os.path.join(base_dir, 'PKG-INFO'), 'w') + try: + self.write_pkg_file(pkg_info) + finally: + pkg_info.close() def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. diff --git a/emxccompiler.py b/emxccompiler.py index f52e6323..a0172058 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -272,8 +272,10 @@ def check_config_h(): # It would probably better to read single lines to search. # But we do this only once, and it is fast enough f = open(fn) - s = f.read() - f.close() + try: + s = f.read() + finally: + f.close() except IOError, exc: # if we can't read this file, we cannot say it is wrong @@ -300,8 +302,10 @@ def get_versions(): gcc_exe = find_executable('gcc') if gcc_exe: out = os.popen(gcc_exe + ' -dumpversion','r') - out_string = out.read() - out.close() + try: + out_string = out.read() + finally: + out.close() result = re.search('(\d+\.\d+\.\d+)',out_string) if result: gcc_version = StrictVersion(result.group(1)) diff --git a/extension.py b/extension.py index 440d128c..9a67ca8b 100644 --- a/extension.py +++ b/extension.py @@ -150,87 +150,96 @@ def read_setup_file (filename): file = TextFile(filename, strip_comments=1, skip_blanks=1, join_lines=1, lstrip_ws=1, rstrip_ws=1) - extensions = [] - - while 1: - line = file.readline() - if line is None: # eof - break - if _variable_rx.match(line): # VAR=VALUE, handled in first pass - continue - - if line[0] == line[-1] == "*": - file.warn("'%s' lines not handled yet" % line) - continue - - #print "original line: " + line - line = expand_makefile_vars(line, vars) - words = split_quoted(line) - #print "expanded line: " + line - - # NB. this parses a slightly different syntax than the old - # makesetup script: here, there must be exactly one extension per - # line, and it must be the first word of the line. I have no idea - # why the old syntax supported multiple extensions per line, as - # they all wind up being the same. - - module = words[0] - ext = Extension(module, []) - append_next_word = None - - for word in words[1:]: - if append_next_word is not None: - append_next_word.append(word) - append_next_word = None + try: + extensions = [] + + while 1: + line = file.readline() + if line is None: # eof + break + if _variable_rx.match(line): # VAR=VALUE, handled in first pass continue - suffix = os.path.splitext(word)[1] - switch = word[0:2] ; value = word[2:] - - if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): - # hmm, should we do something about C vs. C++ sources? - # or leave it up to the CCompiler implementation to - # worry about? - ext.sources.append(word) - elif switch == "-I": - ext.include_dirs.append(value) - elif switch == "-D": - equals = string.find(value, "=") - if equals == -1: # bare "-DFOO" -- no value - ext.define_macros.append((value, None)) - else: # "-DFOO=blah" - ext.define_macros.append((value[0:equals], - value[equals+2:])) - elif switch == "-U": - ext.undef_macros.append(value) - elif switch == "-C": # only here 'cause makesetup has it! - ext.extra_compile_args.append(word) - elif switch == "-l": - ext.libraries.append(value) - elif switch == "-L": - ext.library_dirs.append(value) - elif switch == "-R": - ext.runtime_library_dirs.append(value) - elif word == "-rpath": - append_next_word = ext.runtime_library_dirs - elif word == "-Xlinker": - append_next_word = ext.extra_link_args - elif word == "-Xcompiler": - append_next_word = ext.extra_compile_args - elif switch == "-u": - ext.extra_link_args.append(word) - if not value: + if line[0] == line[-1] == "*": + file.warn("'%s' lines not handled yet" % line) + continue + + #print "original line: " + line + line = expand_makefile_vars(line, vars) + words = split_quoted(line) + #print "expanded line: " + line + + # NB. this parses a slightly different syntax than the old + # makesetup script: here, there must be exactly one extension per + # line, and it must be the first word of the line. I have no idea + # why the old syntax supported multiple extensions per line, as + # they all wind up being the same. + + module = words[0] + ext = Extension(module, []) + append_next_word = None + + for word in words[1:]: + if append_next_word is not None: + append_next_word.append(word) + append_next_word = None + continue + + suffix = os.path.splitext(word)[1] + switch = word[0:2] ; value = word[2:] + + if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): + # hmm, should we do something about C vs. C++ sources? + # or leave it up to the CCompiler implementation to + # worry about? + ext.sources.append(word) + elif switch == "-I": + ext.include_dirs.append(value) + elif switch == "-D": + equals = string.find(value, "=") + if equals == -1: # bare "-DFOO" -- no value + ext.define_macros.append((value, None)) + else: # "-DFOO=blah" + ext.define_macros.append((value[0:equals], + value[equals+2:])) + elif switch == "-U": + ext.undef_macros.append(value) + elif switch == "-C": # only here 'cause makesetup has it! + ext.extra_compile_args.append(word) + elif switch == "-l": + ext.libraries.append(value) + elif switch == "-L": + ext.library_dirs.append(value) + elif switch == "-R": + ext.runtime_library_dirs.append(value) + elif word == "-rpath": + append_next_word = ext.runtime_library_dirs + elif word == "-Xlinker": append_next_word = ext.extra_link_args - elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): - # NB. a really faithful emulation of makesetup would - # append a .o file to extra_objects only if it - # had a slash in it; otherwise, it would s/.o/.c/ - # and append it to sources. Hmmmm. - ext.extra_objects.append(word) - else: - file.warn("unrecognized argument '%s'" % word) - - extensions.append(ext) + elif word == "-Xcompiler": + append_next_word = ext.extra_compile_args + elif switch == "-u": + ext.extra_link_args.append(word) + if not value: + append_next_word = ext.extra_link_args + elif word == "-Xcompiler": + append_next_word = ext.extra_compile_args + elif switch == "-u": + ext.extra_link_args.append(word) + if not value: + append_next_word = ext.extra_link_args + elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): + # NB. a really faithful emulation of makesetup would + # append a .o file to extra_objects only if it + # had a slash in it; otherwise, it would s/.o/.c/ + # and append it to sources. Hmmmm. + ext.extra_objects.append(word) + else: + file.warn("unrecognized argument '%s'" % word) + + extensions.append(ext) + finally: + file.close() #print "module:", module #print "source files:", source_files diff --git a/file_util.py b/file_util.py index b3d9d54e..b9f07861 100644 --- a/file_util.py +++ b/file_util.py @@ -224,6 +224,8 @@ def write_file (filename, contents): sequence of strings without line terminators) to it. """ f = open(filename, "w") - for line in contents: - f.write(line + "\n") - f.close() + try: + for line in contents: + f.write(line + "\n") + finally: + f.close() diff --git a/msvc9compiler.py b/msvc9compiler.py index d5d7f665..55a4db18 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -273,10 +273,12 @@ def query_vcvarsall(version, arch="x86"): popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - stdout, stderr = popen.communicate() - if popen.wait() != 0: - raise DistutilsPlatformError(stderr.decode("mbcs")) + try: + stdout, stderr = popen.communicate() + if popen.wait() != 0: + raise DistutilsPlatformError(stderr.decode("mbcs")) + finally: + popen.close() stdout = stdout.decode("mbcs") for line in stdout.split("\n"): diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 3c8bc41b..03517392 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -19,11 +19,15 @@ class BuildPyTestCase(support.TempdirManager, def _setup_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") - f.write("# Pretend this is a package.") - f.close() + try: + f.write("# Pretend this is a package.") + finally: + f.close() f = open(os.path.join(sources, "README.txt"), "w") - f.write("Info about this package") - f.close() + try: + f.write("Info about this package") + finally: + f.close() destination = self.mkdtemp() diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 72e8915c..df89cdec 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -71,8 +71,10 @@ class BuildScriptsTestCase(support.TempdirManager, def write_script(self, dir, name, text): f = open(os.path.join(dir, name), "w") - f.write(text) - f.close() + try: + f.write(text) + finally: + f.close() def test_version_int(self): source = self.mkdtemp() diff --git a/tests/test_config.py b/tests/test_config.py index 0a1bb961..6c85efad 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -108,8 +108,12 @@ class PyPIRCCommandTestCase(support.TempdirManager, self.assertTrue(not os.path.exists(rc)) cmd._store_pypirc('tarek', 'xxx') self.assertTrue(os.path.exists(rc)) - content = open(rc).read() - self.assertEquals(content, WANTED) + f = open(rc) + try: + content = f.read() + self.assertEquals(content, WANTED) + finally: + f.close() def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) diff --git a/tests/test_core.py b/tests/test_core.py index 48ec1b43..63f6b31d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -52,7 +52,11 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): shutil.rmtree(path) def write_setup(self, text, path=test.test_support.TESTFN): - open(path, "w").write(text) + f = open(path, "w") + try: + f.write(text) + finally: + f.close() return path def test_run_setup_provides_file(self): diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index a1647fbc..aa9f9ebb 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -88,8 +88,10 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): mkpath(self.target, verbose=0) a_file = os.path.join(self.target, 'ok.txt') f = open(a_file, 'w') - f.write('some content') - f.close() + try: + f.write('some content') + finally: + f.close() wanted = ['copying %s -> %s' % (a_file, self.target2)] copy_tree(self.target, self.target2, verbose=1) diff --git a/tests/test_dist.py b/tests/test_dist.py index 1eddf6d2..d9b413f9 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -102,29 +102,29 @@ class DistributionTestCase(support.TempdirManager, def test_command_packages_configfile(self): sys.argv.append("build") + self.addCleanup(os.unlink, TESTFN) f = open(TESTFN, "w") try: print >>f, "[global]" print >>f, "command_packages = foo.bar, splat" + finally: f.close() - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), - ["distutils.command", "foo.bar", "splat"]) - - # ensure command line overrides config: - sys.argv[1:] = ["--command-packages", "spork", "build"] - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), - ["distutils.command", "spork"]) - - # Setting --command-packages to '' should cause the default to - # be used even if a config file specified something else: - sys.argv[1:] = ["--command-packages", "", "build"] - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), ["distutils.command"]) - finally: - os.unlink(TESTFN) + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "foo.bar", "splat"]) + + # ensure command line overrides config: + sys.argv[1:] = ["--command-packages", "spork", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "spork"]) + + # Setting --command-packages to '' should cause the default to + # be used even if a config file specified something else: + sys.argv[1:] = ["--command-packages", "", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), ["distutils.command"]) def test_write_pkg_file(self): # Check DistributionMetadata handling of Unicode fields @@ -341,8 +341,10 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, temp_dir = self.mkdtemp() user_filename = os.path.join(temp_dir, user_filename) f = open(user_filename, 'w') - f.write('.') - f.close() + try: + f.write('.') + finally: + f.close() try: dist = Distribution() diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 99b421f7..823f2110 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -31,8 +31,10 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): def test_move_file_verbosity(self): f = open(self.source, 'w') - f.write('some content') - f.close() + try: + f.write('some content') + finally: + f.close() move_file(self.source, self.target, verbose=0) wanted = [] diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index b7eb625a..08360d29 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -42,8 +42,10 @@ class InstallScriptsTestCase(support.TempdirManager, def write_script(name, text): expected.append(name) f = open(os.path.join(source, name), "w") - f.write(text) - f.close() + try: + f.write(text) + finally: + f.close() write_script("script1.py", ("#! /usr/bin/env python2.3\n" "# bogus script w/ Python sh-bang\n" diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index c9579544..567505dd 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -113,17 +113,21 @@ class msvc9compilerTestCase(support.TempdirManager, tempdir = self.mkdtemp() manifest = os.path.join(tempdir, 'manifest') f = open(manifest, 'w') - f.write(_MANIFEST) - f.close() + try: + f.write(_MANIFEST) + finally: + f.close() compiler = MSVCCompiler() compiler._remove_visual_c_ref(manifest) # see what we got f = open(manifest) - # removing trailing spaces - content = '\n'.join([line.rstrip() for line in f.readlines()]) - f.close() + try: + # removing trailing spaces + content = '\n'.join([line.rstrip() for line in f.readlines()]) + finally: + f.close() # makes sure the manifest was properly cleaned self.assertEquals(content, _CLEANED_MANIFEST) diff --git a/tests/test_register.py b/tests/test_register.py index 370d6598..6da479b1 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -119,8 +119,12 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(os.path.exists(self.rc)) # with the content similar to WANTED_PYPIRC - content = open(self.rc).read() - self.assertEquals(content, WANTED_PYPIRC) + f = open(self.rc) + try: + content = f.read() + self.assertEquals(content, WANTED_PYPIRC) + finally: + f.close() # now let's make sure the .pypirc file generated # really works : we shouldn't be asked anything diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 87adb458..7cebbf53 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -234,8 +234,12 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEquals(len(content), 11) # checking the MANIFEST - manifest = open(join(self.tmp_dir, 'MANIFEST')).read() - self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + f = open(join(self.tmp_dir, 'MANIFEST')) + try: + manifest = f.read() + self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + finally: + f.close() @unittest.skipUnless(zlib, "requires zlib") def test_metadata_check_option(self): diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 30664860..8449ddca 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -50,9 +50,11 @@ class SysconfigTestCase(support.EnvironGuard, def test_parse_makefile_base(self): self.makefile = test.test_support.TESTFN fd = open(self.makefile, 'w') - fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') - fd.write('VAR=$OTHER\nOTHER=foo') - fd.close() + try: + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + finally: + fd.close() d = sysconfig.parse_makefile(self.makefile) self.assertEquals(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", 'OTHER': 'foo'}) @@ -60,9 +62,11 @@ class SysconfigTestCase(support.EnvironGuard, def test_parse_makefile_literal_dollar(self): self.makefile = test.test_support.TESTFN fd = open(self.makefile, 'w') - fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') - fd.write('VAR=$OTHER\nOTHER=foo') - fd.close() + try: + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + finally: + fd.close() d = sysconfig.parse_makefile(self.makefile) self.assertEquals(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", 'OTHER': 'foo'}) diff --git a/tests/test_text_file.py b/tests/test_text_file.py index 00f083a1..3093097d 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -58,28 +58,46 @@ class TextFileTestCase(support.TempdirManager, unittest.TestCase): finally: out_file.close() - in_file = TextFile (filename, strip_comments=0, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input (1, "no processing", in_file, result1) + in_file = TextFile(filename, strip_comments=0, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(1, "no processing", in_file, result1) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=1, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - test_input (2, "strip comments", in_file, result2) + in_file = TextFile(filename, strip_comments=1, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(2, "strip comments", in_file, result2) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=0, skip_blanks=1, - lstrip_ws=0, rstrip_ws=0) - test_input (3, "strip blanks", in_file, result3) + in_file = TextFile(filename, strip_comments=0, skip_blanks=1, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(3, "strip blanks", in_file, result3) + finally: + in_file.close() - in_file = TextFile (filename) - test_input (4, "default processing", in_file, result4) + in_file = TextFile(filename) + try: + test_input(4, "default processing", in_file, result4) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1) - test_input (5, "join lines without collapsing", in_file, result5) + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1) + try: + test_input(5, "join lines without collapsing", in_file, result5) + finally: + in_file.close() - in_file = TextFile (filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1, collapse_join=1) - test_input (6, "join lines with collapsing", in_file, result6) + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1, collapse_join=1) + try: + test_input(6, "join lines with collapsing", in_file, result6) + finally: + in_file.close() def test_suite(): return unittest.makeSuite(TextFileTestCase) diff --git a/util.py b/util.py index 4dcfeb55..f06e4fdf 100644 --- a/util.py +++ b/util.py @@ -116,13 +116,15 @@ def get_platform (): # behaviour. pass else: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - f.close() - if m is not None: - macrelease = '.'.join(m.group(1).split('.')[:2]) - # else: fall back to the default behaviour + try: + m = re.search( + r'ProductUserVisibleVersion\s*' + + r'(.*?)', f.read()) + if m is not None: + macrelease = '.'.join(m.group(1).split('.')[:2]) + # else: fall back to the default behaviour + finally: + f.close() if not macver: macver = macrelease -- cgit v1.2.1 From 41f08f2173e692b35d9dc8d232eee3a44a6285d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 04:48:05 +0000 Subject: Prevent race condition with mkdir in distutils. Patch by Arfrever on #9281. --- dir_util.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dir_util.py b/dir_util.py index 54376e5c..c7c9fccf 100644 --- a/dir_util.py +++ b/dir_util.py @@ -69,10 +69,11 @@ def mkpath(name, mode=0o777, verbose=1, dry_run=0): if not dry_run: try: os.mkdir(head, mode) - created_dirs.append(head) except OSError as exc: - raise DistutilsFileError( - "could not create '%s': %s" % (head, exc.args[-1])) + if not (exc.errno == errno.EEXIST and os.path.isdir(head)): + raise DistutilsFileError( + "could not create '%s': %s" % (head, exc.args[-1])) + created_dirs.append(head) _path_created[abs_head] = 1 return created_dirs -- cgit v1.2.1 From d7f8760f536a13a32a3e93c28d527b9b48e0b849 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 04:51:10 +0000 Subject: Merged revisions 86244 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86244 | eric.araujo | 2010-11-06 05:48:05 +0100 (sam., 06 nov. 2010) | 3 lines Prevent race condition with mkdir in distutils. Patch by Arfrever on #9281. ........ --- dir_util.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dir_util.py b/dir_util.py index 54376e5c..c7c9fccf 100644 --- a/dir_util.py +++ b/dir_util.py @@ -69,10 +69,11 @@ def mkpath(name, mode=0o777, verbose=1, dry_run=0): if not dry_run: try: os.mkdir(head, mode) - created_dirs.append(head) except OSError as exc: - raise DistutilsFileError( - "could not create '%s': %s" % (head, exc.args[-1])) + if not (exc.errno == errno.EEXIST and os.path.isdir(head)): + raise DistutilsFileError( + "could not create '%s': %s" % (head, exc.args[-1])) + created_dirs.append(head) _path_created[abs_head] = 1 return created_dirs -- cgit v1.2.1 From 80531c1bf96caa7748c38597cb19161b804e8c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 04:53:42 +0000 Subject: Merged revisions 86244 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86244 | eric.araujo | 2010-11-06 05:48:05 +0100 (sam., 06 nov. 2010) | 3 lines Prevent race condition with mkdir in distutils. Patch by Arfrever on #9281. ........ --- dir_util.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dir_util.py b/dir_util.py index 5a968063..d6875936 100644 --- a/dir_util.py +++ b/dir_util.py @@ -69,10 +69,11 @@ def mkpath(name, mode=0777, verbose=1, dry_run=0): if not dry_run: try: os.mkdir(head, mode) - created_dirs.append(head) except OSError, exc: - raise DistutilsFileError, \ - "could not create '%s': %s" % (head, exc[-1]) + if not (exc.errno == errno.EEXIST and os.path.isdir(head)): + raise DistutilsFileError( + "could not create '%s': %s" % (head, exc.args[-1])) + created_dirs.append(head) _path_created[abs_head] = 1 return created_dirs -- cgit v1.2.1 From 228a5b4d720ae63f6f5a2b9acf535d9da00f934f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 06:00:54 +0000 Subject: Remove traces of Mac OS 9 support, again (#9508). This was done in r80805 (#7908) and erroneously brought back by the distutils revert. This commit removes more code than the original, which was uncomplete. There is no NEWS entry, like in r80805. --- command/install.py | 16 ---------------- file_util.py | 9 --------- sysconfig.py | 42 ------------------------------------------ util.py | 9 --------- 4 files changed, 76 deletions(-) diff --git a/command/install.py b/command/install.py index 2a905d92..58f6ec5b 100644 --- a/command/install.py +++ b/command/install.py @@ -60,14 +60,6 @@ INSTALL_SCHEMES = { 'data' : '$base', }, 'nt': WINDOWS_SCHEME, - 'mac': { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - }, - 'os2': { 'purelib': '$base/Lib/site-packages', 'platlib': '$base/Lib/site-packages', @@ -95,14 +87,6 @@ if HAS_USER_SITE: 'data' : '$userbase', } - INSTALL_SCHEMES['mac_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/$py_version_short/include/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - } - INSTALL_SCHEMES['os2_home'] = { 'purelib': '$usersite', 'platlib': '$usersite', diff --git a/file_util.py b/file_util.py index c36e7128..e1eb9329 100644 --- a/file_util.py +++ b/file_util.py @@ -130,15 +130,6 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, if dry_run: return (dst, 1) - # On Mac OS, use the native file copy routine - if os.name == 'mac': - import macostools - try: - macostools.copy(src, dst, 0, preserve_times) - except os.error as exc: - raise DistutilsFileError( - "could not copy '%s' to '%s': %s" % (src, dst, exc.args[-1])) - # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) elif link == 'hard': diff --git a/sysconfig.py b/sysconfig.py index 8b55f217..be91f92c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -86,11 +86,6 @@ def get_python_inc(plat_specific=0, prefix=None): return os.path.join(prefix, "include", "python" + get_python_version()) elif os.name == "nt": return os.path.join(prefix, "include") - elif os.name == "mac": - if plat_specific: - return os.path.join(prefix, "Mac", "Include") - else: - return os.path.join(prefix, "Include") elif os.name == "os2": return os.path.join(prefix, "Include") else: @@ -131,17 +126,6 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): return prefix else: return os.path.join(prefix, "Lib", "site-packages") - elif os.name == "mac": - if plat_specific: - if standard_lib: - return os.path.join(prefix, "Lib", "lib-dynload") - else: - return os.path.join(prefix, "Lib", "site-packages") - else: - if standard_lib: - return os.path.join(prefix, "Lib") - else: - return os.path.join(prefix, "Lib", "site-packages") elif os.name == "os2": if standard_lib: return os.path.join(prefix, "Lib") @@ -477,32 +461,6 @@ def _init_nt(): _config_vars = g -def _init_mac(): - """Initialize the module as appropriate for Macintosh systems""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - import MacOS - if not hasattr(MacOS, 'runtimemodel'): - g['SO'] = '.ppc.slb' - else: - g['SO'] = '.%s.slb' % MacOS.runtimemodel - - # XXX are these used anywhere? - g['install_lib'] = os.path.join(EXEC_PREFIX, "Lib") - g['install_platlib'] = os.path.join(EXEC_PREFIX, "Mac", "Lib") - - # These are used by the extension module build - g['srcdir'] = ':' - global _config_vars - _config_vars = g - - def _init_os2(): """Initialize the module as appropriate for OS/2""" g = {} diff --git a/util.py b/util.py index 3081245b..ce3cd6ca 100644 --- a/util.py +++ b/util.py @@ -235,15 +235,6 @@ def change_root (new_root, pathname): path = path[1:] return os.path.join(new_root, path) - elif os.name == 'mac': - if not os.path.isabs(pathname): - return os.path.join(new_root, pathname) - else: - # Chop off volume name from start of path - elements = pathname.split(":", 1) - pathname = ":" + elements[1] - return os.path.join(new_root, pathname) - else: raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) -- cgit v1.2.1 From f34397aa8e5ae017fcfabbbaad6fc41b53e41372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 14:16:30 +0000 Subject: Remove one trace of Mac OS 9 support (#7908 follow-up) This was overlooked in r80804. This change is not really a bug fix, but the release manager agreed to it. There is no NEWS entry, like in the original commit. --- sysconfig.py | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 9888cd52..33cc5a38 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -453,32 +453,6 @@ def _init_nt(): _config_vars = g -def _init_mac(): - """Initialize the module as appropriate for Macintosh systems""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - import MacOS - if not hasattr(MacOS, 'runtimemodel'): - g['SO'] = '.ppc.slb' - else: - g['SO'] = '.%s.slb' % MacOS.runtimemodel - - # XXX are these used anywhere? - g['install_lib'] = os.path.join(EXEC_PREFIX, "Lib") - g['install_platlib'] = os.path.join(EXEC_PREFIX, "Mac", "Lib") - - # These are used by the extension module build - g['srcdir'] = ':' - global _config_vars - _config_vars = g - - def _init_os2(): """Initialize the module as appropriate for OS/2""" g = {} -- cgit v1.2.1 From dd5fb967d4f2e247a7bc154a206a2218b613c4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 15:57:52 +0000 Subject: Correct the fix for #10252: Popen objects have no close method. --- cygwinccompiler.py | 4 +++- msvc9compiler.py | 31 +++++++++++++++++-------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 95fa3ed3..5676e91a 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -377,7 +377,9 @@ def _find_exe_version(cmd): try: out_string = out.read() finally: - out.close() + out.stdin.close() + out.stdout.close() + out.stderr.close() result = RE_VERSION.search(out_string) if result is None: return None diff --git a/msvc9compiler.py b/msvc9compiler.py index 6d7825df..488524db 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -267,21 +267,24 @@ def query_vcvarsall(version, arch="x86"): stdout, stderr = popen.communicate() if popen.wait() != 0: raise DistutilsPlatformError(stderr.decode("mbcs")) + + stdout = stdout.decode("mbcs") + for line in stdout.split("\n"): + line = Reg.convert_mbcs(line) + if '=' not in line: + continue + line = line.strip() + key, value = line.split('=', 1) + key = key.lower() + if key in interesting: + if value.endswith(os.pathsep): + value = value[:-1] + result[key] = removeDuplicates(value) + finally: - popen.close() - - stdout = stdout.decode("mbcs") - for line in stdout.split("\n"): - line = Reg.convert_mbcs(line) - if '=' not in line: - continue - line = line.strip() - key, value = line.split('=', 1) - key = key.lower() - if key in interesting: - if value.endswith(os.pathsep): - value = value[:-1] - result[key] = removeDuplicates(value) + popen.stdin.close() + popen.stdout.close() + popen.stderr.close() if len(result) != len(interesting): raise ValueError(str(list(result.keys()))) -- cgit v1.2.1 From 9c49a045a524afebada7c86980e2598de93f8615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 6 Nov 2010 18:03:52 +0000 Subject: Fix #10252 again (hopefully definitely). Patch by Brian Curtin. --- cygwinccompiler.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 5676e91a..95fa3ed3 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -377,9 +377,7 @@ def _find_exe_version(cmd): try: out_string = out.read() finally: - out.stdin.close() - out.stdout.close() - out.stderr.close() + out.close() result = RE_VERSION.search(out_string) if result is None: return None -- cgit v1.2.1 From 6f3d8cbb6fec170ba7c4e07c6c67d4a9bb57d625 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 9 Nov 2010 09:32:19 +0000 Subject: Issue #10359: Remove ";" after function definition, invalid in ISO C --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 46ac9aaf..203cbc98 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -318,7 +318,7 @@ class BuildExtTestCase(TempdirManager, def test_get_outputs(self): tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void PyInit_foo(void) {};\n') + self.write_file(c_file, 'void PyInit_foo(void) {}\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) -- cgit v1.2.1 From 436be32af39f9ac45717ae3c02a745252b0be238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 12 Nov 2010 20:27:45 +0000 Subject: Merged revisions 86274,86276 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86274 | eric.araujo | 2010-11-06 16:57:52 +0100 (sam., 06 nov. 2010) | 2 lines Correct the fix for #10252: Popen objects have no close method. ........ r86276 | eric.araujo | 2010-11-06 19:03:52 +0100 (sam., 06 nov. 2010) | 2 lines Fix #10252 again (hopefully definitely). Patch by Brian Curtin. ........ --- msvc9compiler.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 6d7825df..488524db 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -267,21 +267,24 @@ def query_vcvarsall(version, arch="x86"): stdout, stderr = popen.communicate() if popen.wait() != 0: raise DistutilsPlatformError(stderr.decode("mbcs")) + + stdout = stdout.decode("mbcs") + for line in stdout.split("\n"): + line = Reg.convert_mbcs(line) + if '=' not in line: + continue + line = line.strip() + key, value = line.split('=', 1) + key = key.lower() + if key in interesting: + if value.endswith(os.pathsep): + value = value[:-1] + result[key] = removeDuplicates(value) + finally: - popen.close() - - stdout = stdout.decode("mbcs") - for line in stdout.split("\n"): - line = Reg.convert_mbcs(line) - if '=' not in line: - continue - line = line.strip() - key, value = line.split('=', 1) - key = key.lower() - if key in interesting: - if value.endswith(os.pathsep): - value = value[:-1] - result[key] = removeDuplicates(value) + popen.stdin.close() + popen.stdout.close() + popen.stderr.close() if len(result) != len(interesting): raise ValueError(str(list(result.keys()))) -- cgit v1.2.1 From 021d150839a1a8b960ae2eb7ee6136f6e081e875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 12 Nov 2010 20:31:17 +0000 Subject: Merged revisions 86274,86276 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86274 | eric.araujo | 2010-11-06 16:57:52 +0100 (sam., 06 nov. 2010) | 2 lines Correct the fix for #10252: Popen objects have no close method. ........ r86276 | eric.araujo | 2010-11-06 19:03:52 +0100 (sam., 06 nov. 2010) | 2 lines Fix #10252 again (hopefully definitely). Patch by Brian Curtin. ........ --- msvc9compiler.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 55a4db18..615d8b83 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -277,21 +277,24 @@ def query_vcvarsall(version, arch="x86"): stdout, stderr = popen.communicate() if popen.wait() != 0: raise DistutilsPlatformError(stderr.decode("mbcs")) + + stdout = stdout.decode("mbcs") + for line in stdout.split("\n"): + line = Reg.convert_mbcs(line) + if '=' not in line: + continue + line = line.strip() + key, value = line.split('=', 1) + key = key.lower() + if key in interesting: + if value.endswith(os.pathsep): + value = value[:-1] + result[key] = removeDuplicates(value) + finally: - popen.close() - - stdout = stdout.decode("mbcs") - for line in stdout.split("\n"): - line = Reg.convert_mbcs(line) - if '=' not in line: - continue - line = line.strip() - key, value = line.split('=', 1) - key = key.lower() - if key in interesting: - if value.endswith(os.pathsep): - value = value[:-1] - result[key] = removeDuplicates(value) + popen.stdin.close() + popen.stdout.close() + popen.stderr.close() if len(result) != len(interesting): raise ValueError(str(list(result.keys()))) -- cgit v1.2.1 From 7c48608dcc43fbbded053bda97ba12fbb835003d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 12 Nov 2010 22:25:23 +0000 Subject: And now for something completely different: Finish fixing #10252 again. --- msvc9compiler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 488524db..e849e16d 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -282,7 +282,6 @@ def query_vcvarsall(version, arch="x86"): result[key] = removeDuplicates(value) finally: - popen.stdin.close() popen.stdout.close() popen.stderr.close() -- cgit v1.2.1 From c393ee270dfd8a7b7f25324d7bf1a361b4850678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 12 Nov 2010 22:26:37 +0000 Subject: Merged revisions 86438 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86438 | eric.araujo | 2010-11-12 23:25:23 +0100 (ven., 12 nov. 2010) | 2 lines And now for something completely different: Finish fixing #10252 again. ........ --- msvc9compiler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 488524db..e849e16d 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -282,7 +282,6 @@ def query_vcvarsall(version, arch="x86"): result[key] = removeDuplicates(value) finally: - popen.stdin.close() popen.stdout.close() popen.stderr.close() -- cgit v1.2.1 From ef556865368e00fb747ce7aae3d4f156688300c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 12 Nov 2010 22:27:28 +0000 Subject: Merged revisions 86438 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86438 | eric.araujo | 2010-11-12 23:25:23 +0100 (ven., 12 nov. 2010) | 2 lines And now for something completely different: Finish fixing #10252 again. ........ --- msvc9compiler.py | 1 - 1 file changed, 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 615d8b83..bf85ac75 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -292,7 +292,6 @@ def query_vcvarsall(version, arch="x86"): result[key] = removeDuplicates(value) finally: - popen.stdin.close() popen.stdout.close() popen.stderr.close() -- cgit v1.2.1 From bbcb611025969e9a4b8a987a7ce1528776740ae2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 13 Nov 2010 06:39:58 +0000 Subject: Bump to 3.2a4. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 9df61ab0..bae33b55 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a3" +__version__ = "3.2a4" #--end constants-- -- cgit v1.2.1 From 65e43d3b792289e3c4025cf203f1f3f1a2949ca4 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 13 Nov 2010 17:28:56 +0000 Subject: bump to 3.1.3rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6dca599f..9684a11c 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.2" +__version__ = "3.1.3rc1" #--end constants-- -- cgit v1.2.1 From 0f51e1c350bb195a7add16991c466fe9d59b9ced Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 13 Nov 2010 17:33:04 +0000 Subject: 2.7.1rc1 bump --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2c69a183..f9f97927 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.1a0" +__version__ = "2.7.1rc1" #--end constants-- -- cgit v1.2.1 From df79f96e47490dedbcb676f77c426fad14df1c6f Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 20 Nov 2010 19:04:17 +0000 Subject: #9424: Replace deprecated assert* methods in the Python test suite. --- tests/test_archive_util.py | 14 +++++----- tests/test_bdist.py | 4 +-- tests/test_bdist_dumb.py | 2 +- tests/test_build.py | 14 +++++----- tests/test_build_clib.py | 10 +++---- tests/test_build_ext.py | 48 ++++++++++++++++----------------- tests/test_check.py | 14 +++++----- tests/test_cmd.py | 10 +++---- tests/test_config.py | 6 ++--- tests/test_config_cmd.py | 12 ++++----- tests/test_core.py | 4 +-- tests/test_cygwinccompiler.py | 32 +++++++++++----------- tests/test_dep_util.py | 4 +-- tests/test_dir_util.py | 22 +++++++-------- tests/test_dist.py | 20 +++++++------- tests/test_extension.py | 16 +++++------ tests/test_file_util.py | 6 ++--- tests/test_filelist.py | 18 ++++++------- tests/test_install.py | 20 +++++++------- tests/test_install_data.py | 8 +++--- tests/test_install_headers.py | 4 +-- tests/test_install_lib.py | 8 +++--- tests/test_log.py | 4 +-- tests/test_msvc9compiler.py | 4 +-- tests/test_register.py | 14 +++++----- tests/test_sdist.py | 34 +++++++++++------------- tests/test_spawn.py | 2 +- tests/test_sysconfig.py | 18 ++++++------- tests/test_text_file.py | 2 +- tests/test_upload.py | 12 ++++----- tests/test_util.py | 62 +++++++++++++++++++++---------------------- tests/test_version.py | 20 +++++++------- 32 files changed, 233 insertions(+), 235 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 9e6264aa..aff42652 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -113,7 +113,7 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertTrue(os.path.exists(tarball2)) # let's compare both tarballs - self.assertEquals(self._tarinfo(tarball), self._tarinfo(tarball2)) + self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2)) # trying an uncompressed one base_name = os.path.join(tmpdir2, 'archive') @@ -153,7 +153,7 @@ class ArchiveUtilTestCase(support.TempdirManager, os.chdir(old_dir) tarball = base_name + '.tar.Z' self.assertTrue(os.path.exists(tarball)) - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) # same test with dry_run os.remove(tarball) @@ -167,7 +167,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) self.assertTrue(not os.path.exists(tarball)) - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile(self): @@ -184,9 +184,9 @@ class ArchiveUtilTestCase(support.TempdirManager, tarball = base_name + '.zip' def test_check_archive_formats(self): - self.assertEquals(check_archive_formats(['gztar', 'xxx', 'zip']), - 'xxx') - self.assertEquals(check_archive_formats(['gztar', 'zip']), None) + self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), + 'xxx') + self.assertEqual(check_archive_formats(['gztar', 'zip']), None) def test_make_archive(self): tmpdir = self.mkdtemp() @@ -203,7 +203,7 @@ class ArchiveUtilTestCase(support.TempdirManager, make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) except: pass - self.assertEquals(os.getcwd(), current_dir) + self.assertEqual(os.getcwd(), current_dir) finally: del ARCHIVE_FORMATS['xxx'] diff --git a/tests/test_bdist.py b/tests/test_bdist.py index bf56e842..94d40cc2 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -24,7 +24,7 @@ class BuildTestCase(support.TempdirManager, cmd = bdist(dist) cmd.formats = ['msi'] cmd.ensure_finalized() - self.assertEquals(cmd.formats, ['msi']) + self.assertEqual(cmd.formats, ['msi']) # what format bdist offers ? # XXX an explicit list in bdist is @@ -35,7 +35,7 @@ class BuildTestCase(support.TempdirManager, formats.sort() founded = list(cmd.format_command.keys()) founded.sort() - self.assertEquals(founded, formats) + self.assertEqual(founded, formats) def test_suite(): return unittest.makeSuite(BuildTestCase) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 7d9d0aa8..cc37fef8 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -69,7 +69,7 @@ class BuildDumbTestCase(support.TempdirManager, base = base.replace(':', '-') wanted = ['%s.zip' % base] - self.assertEquals(dist_created, wanted) + self.assertEqual(dist_created, wanted) # now let's check what we have in the zip file # XXX to be done diff --git a/tests/test_build.py b/tests/test_build.py index 9f0e0ad2..3391f36d 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -18,11 +18,11 @@ class BuildTestCase(support.TempdirManager, cmd.finalize_options() # if not specified, plat_name gets the current platform - self.assertEquals(cmd.plat_name, get_platform()) + self.assertEqual(cmd.plat_name, get_platform()) # build_purelib is build + lib wanted = os.path.join(cmd.build_base, 'lib') - self.assertEquals(cmd.build_purelib, wanted) + self.assertEqual(cmd.build_purelib, wanted) # build_platlib is 'build/lib.platform-x.x[-pydebug]' # examples: @@ -32,21 +32,21 @@ class BuildTestCase(support.TempdirManager, self.assertTrue(cmd.build_platlib.endswith('-pydebug')) plat_spec += '-pydebug' wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) - self.assertEquals(cmd.build_platlib, wanted) + self.assertEqual(cmd.build_platlib, wanted) # by default, build_lib = build_purelib - self.assertEquals(cmd.build_lib, cmd.build_purelib) + self.assertEqual(cmd.build_lib, cmd.build_purelib) # build_temp is build/temp. wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) - self.assertEquals(cmd.build_temp, wanted) + self.assertEqual(cmd.build_temp, wanted) # build_scripts is build/scripts-x.x wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3]) - self.assertEquals(cmd.build_scripts, wanted) + self.assertEqual(cmd.build_scripts, wanted) # executable is os.path.normpath(sys.executable) - self.assertEquals(cmd.executable, os.path.normpath(sys.executable)) + self.assertEqual(cmd.executable, os.path.normpath(sys.executable)) def test_suite(): return unittest.makeSuite(BuildTestCase) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index e59b8f9a..69bd2bf6 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -57,14 +57,14 @@ class BuildCLibTestCase(support.TempdirManager, self.assertRaises(DistutilsSetupError, cmd.get_source_files) cmd.libraries = [('name', {'sources': ['a', 'b']})] - self.assertEquals(cmd.get_source_files(), ['a', 'b']) + self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')})] - self.assertEquals(cmd.get_source_files(), ['a', 'b']) + self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')}), ('name2', {'sources': ['c', 'd']})] - self.assertEquals(cmd.get_source_files(), ['a', 'b', 'c', 'd']) + self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd']) def test_build_libraries(self): @@ -93,11 +93,11 @@ class BuildCLibTestCase(support.TempdirManager, cmd.include_dirs = 'one-dir' cmd.finalize_options() - self.assertEquals(cmd.include_dirs, ['one-dir']) + self.assertEqual(cmd.include_dirs, ['one-dir']) cmd.include_dirs = None cmd.finalize_options() - self.assertEquals(cmd.include_dirs, []) + self.assertEqual(cmd.include_dirs, []) cmd.distribution.libraries = 'WONTWORK' self.assertRaises(DistutilsSetupError, cmd.finalize_options) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 203cbc98..dcba75f7 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -96,11 +96,11 @@ class BuildExtTestCase(TempdirManager, for attr in ('error', 'foo', 'new', 'roj'): self.assertTrue(hasattr(xx, attr)) - self.assertEquals(xx.foo(2, 5), 7) - self.assertEquals(xx.foo(13,15), 28) - self.assertEquals(xx.new().demo(), None) + self.assertEqual(xx.foo(2, 5), 7) + self.assertEqual(xx.foo(13,15), 28) + self.assertEqual(xx.new().demo(), None) doc = 'This is a template module just for instruction.' - self.assertEquals(xx.__doc__, doc) + self.assertEqual(xx.__doc__, doc) self.assertTrue(isinstance(xx.Null(), xx.Null)) self.assertTrue(isinstance(xx.Str(), xx.Str)) @@ -206,7 +206,7 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.libraries = 'my_lib' cmd.finalize_options() - self.assertEquals(cmd.libraries, ['my_lib']) + self.assertEqual(cmd.libraries, ['my_lib']) # make sure cmd.library_dirs is turned into a list # if it's a string @@ -220,7 +220,7 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.rpath = os.pathsep.join(['one', 'two']) cmd.finalize_options() - self.assertEquals(cmd.rpath, ['one', 'two']) + self.assertEqual(cmd.rpath, ['one', 'two']) # XXX more tests to perform for win32 @@ -229,25 +229,25 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.define = 'one,two' cmd.finalize_options() - self.assertEquals(cmd.define, [('one', '1'), ('two', '1')]) + self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) # make sure undef is turned into a list of # strings if they are ','-separated strings cmd = build_ext(dist) cmd.undef = 'one,two' cmd.finalize_options() - self.assertEquals(cmd.undef, ['one', 'two']) + self.assertEqual(cmd.undef, ['one', 'two']) # make sure swig_opts is turned into a list cmd = build_ext(dist) cmd.swig_opts = None cmd.finalize_options() - self.assertEquals(cmd.swig_opts, []) + self.assertEqual(cmd.swig_opts, []) cmd = build_ext(dist) cmd.swig_opts = '1 2' cmd.finalize_options() - self.assertEquals(cmd.swig_opts, ['1', '2']) + self.assertEqual(cmd.swig_opts, ['1', '2']) def test_check_extensions_list(self): dist = Distribution() @@ -284,7 +284,7 @@ class BuildExtTestCase(TempdirManager, # check_extensions_list adds in ext the values passed # when they are in ('include_dirs', 'library_dirs', 'libraries' # 'extra_objects', 'extra_compile_args', 'extra_link_args') - self.assertEquals(ext.libraries, 'foo') + self.assertEqual(ext.libraries, 'foo') self.assertTrue(not hasattr(ext, 'some')) # 'macros' element of build info dict must be 1- or 2-tuple @@ -294,15 +294,15 @@ class BuildExtTestCase(TempdirManager, exts[0][1]['macros'] = [('1', '2'), ('3',)] cmd.check_extensions_list(exts) - self.assertEquals(exts[0].undef_macros, ['3']) - self.assertEquals(exts[0].define_macros, [('1', '2')]) + self.assertEqual(exts[0].undef_macros, ['3']) + self.assertEqual(exts[0].define_macros, [('1', '2')]) def test_get_source_files(self): modules = [Extension('foo', ['xxx'], optional=False)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = build_ext(dist) cmd.ensure_finalized() - self.assertEquals(cmd.get_source_files(), ['xxx']) + self.assertEqual(cmd.get_source_files(), ['xxx']) def test_compiler_option(self): # cmd.compiler is an option and @@ -313,7 +313,7 @@ class BuildExtTestCase(TempdirManager, cmd.compiler = 'unix' cmd.ensure_finalized() cmd.run() - self.assertEquals(cmd.compiler, 'unix') + self.assertEqual(cmd.compiler, 'unix') def test_get_outputs(self): tmp_dir = self.mkdtemp() @@ -325,7 +325,7 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) self._fixup_command(cmd) cmd.ensure_finalized() - self.assertEquals(len(cmd.get_outputs()), 1) + self.assertEqual(len(cmd.get_outputs()), 1) if os.name == "nt": cmd.debug = sys.executable.endswith("_d.exe") @@ -348,7 +348,7 @@ class BuildExtTestCase(TempdirManager, so_ext = sysconfig.get_config_var('SO') self.assertTrue(so_file.endswith(so_ext)) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, other_tmp_dir) + self.assertEqual(so_dir, other_tmp_dir) cmd.inplace = 0 cmd.compiler = None @@ -357,7 +357,7 @@ class BuildExtTestCase(TempdirManager, self.assertTrue(os.path.exists(so_file)) self.assertTrue(so_file.endswith(so_ext)) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, cmd.build_lib) + self.assertEqual(so_dir, cmd.build_lib) # inplace = 0, cmd.package = 'bar' build_py = cmd.get_finalized_command('build_py') @@ -365,7 +365,7 @@ class BuildExtTestCase(TempdirManager, path = cmd.get_ext_fullpath('foo') # checking that the last directory is the build_dir path = os.path.split(path)[0] - self.assertEquals(path, cmd.build_lib) + self.assertEqual(path, cmd.build_lib) # inplace = 1, cmd.package = 'bar' cmd.inplace = 1 @@ -379,7 +379,7 @@ class BuildExtTestCase(TempdirManager, # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, 'bar') + self.assertEqual(lastdir, 'bar') def test_ext_fullpath(self): ext = sysconfig.get_config_vars()['SO'] @@ -395,14 +395,14 @@ class BuildExtTestCase(TempdirManager, curdir = os.getcwd() wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building lxml.etree not inplace cmd.inplace = 0 cmd.build_lib = os.path.join(curdir, 'tmpdir') wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building twisted.runner.portmap not inplace build_py = cmd.get_finalized_command('build_py') @@ -411,13 +411,13 @@ class BuildExtTestCase(TempdirManager, path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', 'portmap' + ext) - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building twisted.runner.portmap inplace cmd.inplace = 1 path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) def test_suite(): src = _get_source_filename() diff --git a/tests/test_check.py b/tests/test_check.py index 6ad2fe39..229ae25c 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -27,7 +27,7 @@ class CheckTestCase(support.LoggingSilencer, # by default, check is checking the metadata # should have some warnings cmd = self._run() - self.assertEquals(cmd._warnings, 2) + self.assertEqual(cmd._warnings, 2) # now let's add the required fields # and run it again, to make sure we don't get @@ -36,7 +36,7 @@ class CheckTestCase(support.LoggingSilencer, 'author_email': 'xxx', 'name': 'xxx', 'version': 'xxx'} cmd = self._run(metadata) - self.assertEquals(cmd._warnings, 0) + self.assertEqual(cmd._warnings, 0) # now with the strict mode, we should # get an error if there are missing metadata @@ -44,7 +44,7 @@ class CheckTestCase(support.LoggingSilencer, # and of course, no error when all metadata are present cmd = self._run(metadata, strict=1) - self.assertEquals(cmd._warnings, 0) + self.assertEqual(cmd._warnings, 0) def test_check_document(self): if not HAS_DOCUTILS: # won't test without docutils @@ -55,12 +55,12 @@ class CheckTestCase(support.LoggingSilencer, # let's see if it detects broken rest broken_rest = 'title\n===\n\ntest' msgs = cmd._check_rst_data(broken_rest) - self.assertEquals(len(msgs), 1) + self.assertEqual(len(msgs), 1) # and non-broken rest rest = 'title\n=====\n\ntest' msgs = cmd._check_rst_data(rest) - self.assertEquals(len(msgs), 0) + self.assertEqual(len(msgs), 0) def test_check_restructuredtext(self): if not HAS_DOCUTILS: # won't test without docutils @@ -70,7 +70,7 @@ class CheckTestCase(support.LoggingSilencer, pkg_info, dist = self.create_dist(long_description=broken_rest) cmd = check(dist) cmd.check_restructuredtext() - self.assertEquals(cmd._warnings, 1) + self.assertEqual(cmd._warnings, 1) # let's see if we have an error with strict=1 metadata = {'url': 'xxx', 'author': 'xxx', @@ -83,7 +83,7 @@ class CheckTestCase(support.LoggingSilencer, # and non-broken rest metadata['long_description'] = 'title\n=====\n\ntest' cmd = self._run(metadata, strict=1, restructuredtext=1) - self.assertEquals(cmd._warnings, 0) + self.assertEqual(cmd._warnings, 0) def test_check_all(self): diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 969f82ba..195045cc 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -44,7 +44,7 @@ class CommandTestCase(unittest.TestCase): # making sure execute gets called properly def _execute(func, args, exec_msg, level): - self.assertEquals(exec_msg, 'generating out from in') + self.assertEqual(exec_msg, 'generating out from in') cmd.force = True cmd.execute = _execute cmd.make_file(infiles='in', outfile='out', func='func', args=()) @@ -63,7 +63,7 @@ class CommandTestCase(unittest.TestCase): wanted = ["command options for 'MyCmd':", ' option1 = 1', ' option2 = 1'] - self.assertEquals(msgs, wanted) + self.assertEqual(msgs, wanted) def test_ensure_string(self): cmd = self.cmd @@ -81,7 +81,7 @@ class CommandTestCase(unittest.TestCase): cmd = self.cmd cmd.option1 = 'ok,dok' cmd.ensure_string_list('option1') - self.assertEquals(cmd.option1, ['ok', 'dok']) + self.assertEqual(cmd.option1, ['ok', 'dok']) cmd.option2 = ['xxx', 'www'] cmd.ensure_string_list('option2') @@ -109,14 +109,14 @@ class CommandTestCase(unittest.TestCase): with captured_stdout() as stdout: cmd.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), '') + self.assertEqual(stdout.read(), '') debug.DEBUG = True try: with captured_stdout() as stdout: cmd.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), 'xxx\n') + self.assertEqual(stdout.read(), 'xxx\n') finally: debug.DEBUG = False diff --git a/tests/test_config.py b/tests/test_config.py index 05a35da9..525bee94 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -89,7 +89,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, waited = [('password', 'secret'), ('realm', 'pypi'), ('repository', 'http://pypi.python.org/pypi'), ('server', 'server1'), ('username', 'me')] - self.assertEquals(config, waited) + self.assertEqual(config, waited) # old format self.write_file(self.rc, PYPIRC_OLD) @@ -98,7 +98,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, waited = [('password', 'secret'), ('realm', 'pypi'), ('repository', 'http://pypi.python.org/pypi'), ('server', 'server-login'), ('username', 'tarek')] - self.assertEquals(config, waited) + self.assertEqual(config, waited) def test_server_empty_registration(self): cmd = self._cmd(self.dist) @@ -109,7 +109,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, f = open(rc) try: content = f.read() - self.assertEquals(content, WANTED) + self.assertEqual(content, WANTED) finally: f.close() diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index c224a5c3..4f7ebdd9 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -35,7 +35,7 @@ class ConfigTestCase(support.LoggingSilencer, f.close() dump_file(this_file, 'I am the header') - self.assertEquals(len(self._logs), numlines+1) + self.assertEqual(len(self._logs), numlines+1) def test_search_cpp(self): if sys.platform == 'win32': @@ -45,10 +45,10 @@ class ConfigTestCase(support.LoggingSilencer, # simple pattern searches match = cmd.search_cpp(pattern='xxx', body='// xxx') - self.assertEquals(match, 0) + self.assertEqual(match, 0) match = cmd.search_cpp(pattern='_configtest', body='// xxx') - self.assertEquals(match, 1) + self.assertEqual(match, 1) def test_finalize_options(self): # finalize_options does a bit of transformation @@ -60,9 +60,9 @@ class ConfigTestCase(support.LoggingSilencer, cmd.library_dirs = 'three%sfour' % os.pathsep cmd.ensure_finalized() - self.assertEquals(cmd.include_dirs, ['one', 'two']) - self.assertEquals(cmd.libraries, ['one']) - self.assertEquals(cmd.library_dirs, ['three', 'four']) + self.assertEqual(cmd.include_dirs, ['one', 'two']) + self.assertEqual(cmd.libraries, ['one']) + self.assertEqual(cmd.library_dirs, ['three', 'four']) def test_clean(self): # _clean removes files diff --git a/tests/test_core.py b/tests/test_core.py index 47fae245..41321f7d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -89,7 +89,7 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): with captured_stdout() as stdout: distutils.core.setup(name='bar') stdout.seek(0) - self.assertEquals(stdout.read(), 'bar\n') + self.assertEqual(stdout.read(), 'bar\n') distutils.core.DEBUG = True try: @@ -99,7 +99,7 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): distutils.core.DEBUG = False stdout.seek(0) wanted = "options (after parsing config files):\n" - self.assertEquals(stdout.readlines()[0], wanted) + self.assertEqual(stdout.readlines()[0], wanted) def test_suite(): return unittest.makeSuite(CoreTestCase) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index e57bab26..85692167 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -66,82 +66,82 @@ class CygwinCCompilerTestCase(support.TempdirManager, sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' '4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEquals(check_config_h()[0], CONFIG_H_OK) + self.assertEqual(check_config_h()[0], CONFIG_H_OK) # then it tries to see if it can find "__GNUC__" in pyconfig.h sys.version = 'something without the *CC word' # if the file doesn't exist it returns CONFIG_H_UNCERTAIN - self.assertEquals(check_config_h()[0], CONFIG_H_UNCERTAIN) + self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN) # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK self.write_file(self.python_h, 'xxx') - self.assertEquals(check_config_h()[0], CONFIG_H_NOTOK) + self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK) # and CONFIG_H_OK if __GNUC__ is found self.write_file(self.python_h, 'xxx __GNUC__ xxx') - self.assertEquals(check_config_h()[0], CONFIG_H_OK) + self.assertEqual(check_config_h()[0], CONFIG_H_OK) def test_get_versions(self): # get_versions calls distutils.spawn.find_executable on # 'gcc', 'ld' and 'dllwrap' - self.assertEquals(get_versions(), (None, None, None)) + self.assertEqual(get_versions(), (None, None, None)) # Let's fake we have 'gcc' and it returns '3.4.5' self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' res = get_versions() - self.assertEquals(str(res[0]), '3.4.5') + self.assertEqual(str(res[0]), '3.4.5') # and let's see what happens when the version # doesn't match the regular expression # (\d+\.\d+(\.\d+)*) self._exes['gcc'] = b'very strange output' res = get_versions() - self.assertEquals(res[0], None) + self.assertEqual(res[0], None) # same thing for ld self._exes['ld'] = b'GNU ld version 2.17.50 20060824' res = get_versions() - self.assertEquals(str(res[1]), '2.17.50') + self.assertEqual(str(res[1]), '2.17.50') self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' res = get_versions() - self.assertEquals(res[1], None) + self.assertEqual(res[1], None) # and dllwrap self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' res = get_versions() - self.assertEquals(str(res[2]), '2.17.50') + self.assertEqual(str(res[2]), '2.17.50') self._exes['dllwrap'] = b'Cheese Wrap' res = get_versions() - self.assertEquals(res[2], None) + self.assertEqual(res[2], None) def test_get_msvcr(self): # none sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEquals(get_msvcr(), None) + self.assertEqual(get_msvcr(), None) # MSVC 7.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1300 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr70']) + self.assertEqual(get_msvcr(), ['msvcr70']) # MSVC 7.1 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1310 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr71']) + self.assertEqual(get_msvcr(), ['msvcr71']) # VS2005 / MSVC 8.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1400 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr80']) + self.assertEqual(get_msvcr(), ['msvcr80']) # VS2008 / MSVC 9.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1500 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr90']) + self.assertEqual(get_msvcr(), ['msvcr90']) # unknown sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' diff --git a/tests/test_dep_util.py b/tests/test_dep_util.py index 390ed9b6..3e1c3668 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -43,8 +43,8 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase): self.write_file(two) self.write_file(four) - self.assertEquals(newer_pairwise([one, two], [three, four]), - ([one],[three])) + self.assertEqual(newer_pairwise([one, two], [three, four]), + ([one],[three])) def test_newer_group(self): tmpdir = self.mkdtemp() diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 7356c766..ce74589d 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -38,18 +38,18 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): mkpath(self.target, verbose=0) wanted = [] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) mkpath(self.target, verbose=1) wanted = ['creating %s' % self.root_target, 'creating %s' % self.target] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) self._logs = [] remove_tree(self.root_target, verbose=1) wanted = ["removing '%s' (and everything under it)" % self.root_target] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) @unittest.skipIf(sys.platform.startswith('win'), "This test is only appropriate for POSIX-like systems.") @@ -67,12 +67,12 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): def test_create_tree_verbosity(self): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) - self.assertEquals(self._logs, []) + self.assertEqual(self._logs, []) remove_tree(self.root_target, verbose=0) wanted = ['creating %s' % self.root_target] create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) @@ -82,7 +82,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): mkpath(self.target, verbose=0) copy_tree(self.target, self.target2, verbose=0) - self.assertEquals(self._logs, []) + self.assertEqual(self._logs, []) remove_tree(self.root_target, verbose=0) @@ -96,18 +96,18 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ['copying %s -> %s' % (a_file, self.target2)] copy_tree(self.target, self.target2, verbose=1) - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) remove_tree(self.target2, verbose=0) def test_ensure_relative(self): if os.sep == '/': - self.assertEquals(ensure_relative('/home/foo'), 'home/foo') - self.assertEquals(ensure_relative('some/path'), 'some/path') + self.assertEqual(ensure_relative('/home/foo'), 'home/foo') + self.assertEqual(ensure_relative('some/path'), 'some/path') else: # \\ - self.assertEquals(ensure_relative('c:\\home\\foo'), 'c:home\\foo') - self.assertEquals(ensure_relative('home\\foo'), 'home\\foo') + self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') + self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') def test_suite(): return unittest.makeSuite(DirUtilTestCase) diff --git a/tests/test_dist.py b/tests/test_dist.py index a8eb8b1d..a20d6c8f 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -124,7 +124,7 @@ class DistributionTestCase(support.LoggingSilencer, finally: warnings.warn = old_warn - self.assertEquals(len(warns), 0) + self.assertEqual(len(warns), 0) def test_finalize_options(self): @@ -135,20 +135,20 @@ class DistributionTestCase(support.LoggingSilencer, dist.finalize_options() # finalize_option splits platforms and keywords - self.assertEquals(dist.metadata.platforms, ['one', 'two']) - self.assertEquals(dist.metadata.keywords, ['one', 'two']) + self.assertEqual(dist.metadata.platforms, ['one', 'two']) + self.assertEqual(dist.metadata.keywords, ['one', 'two']) def test_get_command_packages(self): dist = Distribution() - self.assertEquals(dist.command_packages, None) + self.assertEqual(dist.command_packages, None) cmds = dist.get_command_packages() - self.assertEquals(cmds, ['distutils.command']) - self.assertEquals(dist.command_packages, - ['distutils.command']) + self.assertEqual(cmds, ['distutils.command']) + self.assertEqual(dist.command_packages, + ['distutils.command']) dist.command_packages = 'one,two' cmds = dist.get_command_packages() - self.assertEquals(cmds, ['distutils.command', 'one', 'two']) + self.assertEqual(cmds, ['distutils.command', 'one', 'two']) def test_announce(self): @@ -287,8 +287,8 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_fix_help_options(self): help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] fancy_options = fix_help_options(help_tuples) - self.assertEquals(fancy_options[0], ('a', 'b', 'c')) - self.assertEquals(fancy_options[1], (1, 2, 3)) + self.assertEqual(fancy_options[0], ('a', 'b', 'c')) + self.assertEqual(fancy_options[1], (1, 2, 3)) def test_show_help(self): # smoke test, just makes sure some help is displayed diff --git a/tests/test_extension.py b/tests/test_extension.py index c9c89657..e35f2738 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -28,38 +28,38 @@ class ExtensionTestCase(unittest.TestCase): 'rect', 'rwobject', 'scrap', 'surface', 'surflock', 'time', 'transform'] - self.assertEquals(names, wanted) + self.assertEqual(names, wanted) def test_extension_init(self): # the first argument, which is the name, must be a string self.assertRaises(AssertionError, Extension, 1, []) ext = Extension('name', []) - self.assertEquals(ext.name, 'name') + self.assertEqual(ext.name, 'name') # the second argument, which is the list of files, must # be a list of strings self.assertRaises(AssertionError, Extension, 'name', 'file') self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) ext = Extension('name', ['file1', 'file2']) - self.assertEquals(ext.sources, ['file1', 'file2']) + self.assertEqual(ext.sources, ['file1', 'file2']) # others arguments have defaults for attr in ('include_dirs', 'define_macros', 'undef_macros', 'library_dirs', 'libraries', 'runtime_library_dirs', 'extra_objects', 'extra_compile_args', 'extra_link_args', 'export_symbols', 'swig_opts', 'depends'): - self.assertEquals(getattr(ext, attr), []) + self.assertEqual(getattr(ext, attr), []) - self.assertEquals(ext.language, None) - self.assertEquals(ext.optional, None) + self.assertEqual(ext.language, None) + self.assertEqual(ext.optional, None) # if there are unknown keyword options, warn about them with check_warnings() as w: warnings.simplefilter('always') ext = Extension('name', ['file1', 'file2'], chic=True) - self.assertEquals(len(w.warnings), 1) - self.assertEquals(str(w.warnings[0].message), + self.assertEqual(len(w.warnings), 1) + self.assertEqual(str(w.warnings[0].message), "Unknown Extension options: 'chic'") def test_suite(): diff --git a/tests/test_file_util.py b/tests/test_file_util.py index be743f34..3c3e3dcb 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -39,14 +39,14 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): move_file(self.source, self.target, verbose=0) wanted = [] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) # back to original state move_file(self.target, self.source, verbose=0) move_file(self.source, self.target, verbose=1) wanted = ['moving %s -> %s' % (self.source, self.target)] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) # back to original state move_file(self.target, self.source, verbose=0) @@ -56,7 +56,7 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): os.mkdir(self.target_dir) move_file(self.source, self.target_dir, verbose=1) wanted = ['moving %s -> %s' % (self.source, self.target_dir)] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) def test_suite(): diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 6312a294..c7e5201b 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -9,29 +9,29 @@ class FileListTestCase(unittest.TestCase): def test_glob_to_re(self): # simple cases - self.assertEquals(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)') - self.assertEquals(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)') - self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)') + self.assertEqual(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)') + self.assertEqual(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)') + self.assertEqual(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)') # special cases - self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)') - self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)') - self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') - self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') + self.assertEqual(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)') + self.assertEqual(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)') + self.assertEqual(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') + self.assertEqual(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') def test_debug_print(self): file_list = FileList() with captured_stdout() as stdout: file_list.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), '') + self.assertEqual(stdout.read(), '') debug.DEBUG = True try: with captured_stdout() as stdout: file_list.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), 'xxx\n') + self.assertEqual(stdout.read(), 'xxx\n') finally: debug.DEBUG = False diff --git a/tests/test_install.py b/tests/test_install.py index 22e79b89..a76e3e7a 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -123,23 +123,23 @@ class InstallTestCase(support.TempdirManager, # two elements cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, ['path', 'dirs']) - self.assertEquals(cmd.extra_dirs, 'dirs') - self.assertEquals(cmd.path_file, 'path') + self.assertEqual(cmd.extra_path, ['path', 'dirs']) + self.assertEqual(cmd.extra_dirs, 'dirs') + self.assertEqual(cmd.path_file, 'path') # one element cmd.extra_path = ['path'] cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, ['path']) - self.assertEquals(cmd.extra_dirs, 'path') - self.assertEquals(cmd.path_file, 'path') + self.assertEqual(cmd.extra_path, ['path']) + self.assertEqual(cmd.extra_dirs, 'path') + self.assertEqual(cmd.path_file, 'path') # none dist.extra_path = cmd.extra_path = None cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, None) - self.assertEquals(cmd.extra_dirs, '') - self.assertEquals(cmd.path_file, None) + self.assertEqual(cmd.extra_path, None) + self.assertEqual(cmd.extra_dirs, '') + self.assertEqual(cmd.path_file, None) # three elements (no way !) cmd.extra_path = 'path,dirs,again' @@ -184,7 +184,7 @@ class InstallTestCase(support.TempdirManager, # line (the egg info file) f = open(cmd.record) try: - self.assertEquals(len(f.readlines()), 1) + self.assertEqual(len(f.readlines()), 1) finally: f.close() diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 6b3b4c82..4d8c00ac 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -28,14 +28,14 @@ class InstallDataTestCase(support.TempdirManager, self.write_file(two, 'xxx') cmd.data_files = [one, (inst2, [two])] - self.assertEquals(cmd.get_inputs(), [one, (inst2, [two])]) + self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])]) # let's run the command cmd.ensure_finalized() cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) rtwo = os.path.split(two)[-1] self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) rone = os.path.split(one)[-1] @@ -48,7 +48,7 @@ class InstallDataTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] @@ -66,7 +66,7 @@ class InstallDataTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 4) + self.assertEqual(len(cmd.get_outputs()), 4) self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index dc74c58d..d953157b 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -24,7 +24,7 @@ class InstallHeadersTestCase(support.TempdirManager, pkg_dir, dist = self.create_dist(headers=headers) cmd = install_headers(dist) - self.assertEquals(cmd.get_inputs(), headers) + self.assertEqual(cmd.get_inputs(), headers) # let's run the command cmd.install_dir = os.path.join(pkg_dir, 'inst') @@ -32,7 +32,7 @@ class InstallHeadersTestCase(support.TempdirManager, cmd.run() # let's check the results - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) def test_suite(): return unittest.makeSuite(InstallHeadersTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 790d4ce3..fddaabe1 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -19,8 +19,8 @@ class InstallLibTestCase(support.TempdirManager, cmd = install_lib(dist) cmd.finalize_options() - self.assertEquals(cmd.compile, 1) - self.assertEquals(cmd.optimize, 0) + self.assertEqual(cmd.compile, 1) + self.assertEqual(cmd.optimize, 0) # optimize must be 0, 1, or 2 cmd.optimize = 'foo' @@ -30,7 +30,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.optimize = '2' cmd.finalize_options() - self.assertEquals(cmd.optimize, 2) + self.assertEqual(cmd.optimize, 2) @unittest.skipUnless(not sys.dont_write_bytecode, 'byte-compile not supported') @@ -77,7 +77,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.distribution.script_name = 'setup.py' # get_input should return 2 elements - self.assertEquals(len(cmd.get_inputs()), 2) + self.assertEqual(len(cmd.get_inputs()), 2) def test_dont_write_bytecode(self): # makes sure byte_compile is not used diff --git a/tests/test_log.py b/tests/test_log.py index 5f87076b..ce66ee51 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -23,9 +23,9 @@ class TestLog(unittest.TestCase): log.debug("debug:\xe9") log.fatal("fatal:\xe9") stdout.seek(0) - self.assertEquals(stdout.read().rstrip(), "debug:\\xe9") + self.assertEqual(stdout.read().rstrip(), "debug:\\xe9") stderr.seek(0) - self.assertEquals(stderr.read().rstrip(), "fatal:\\xe9") + self.assertEqual(stderr.read().rstrip(), "fatal:\\xe9") finally: sys.stdout = old_stdout sys.stderr = old_stderr diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 45bae77a..a0d62efc 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -104,7 +104,7 @@ class msvc9compilerTestCase(support.TempdirManager, import winreg HKCU = winreg.HKEY_CURRENT_USER keys = Reg.read_keys(HKCU, 'xxxx') - self.assertEquals(keys, None) + self.assertEqual(keys, None) keys = Reg.read_keys(HKCU, r'Control Panel') self.assertTrue('Desktop' in keys) @@ -131,7 +131,7 @@ class msvc9compilerTestCase(support.TempdirManager, f.close() # makes sure the manifest was properly cleaned - self.assertEquals(content, _CLEANED_MANIFEST) + self.assertEqual(content, _CLEANED_MANIFEST) def test_suite(): diff --git a/tests/test_register.py b/tests/test_register.py index 9336ac86..c712f56a 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -121,7 +121,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): f = open(self.rc) try: content = f.read() - self.assertEquals(content, WANTED_PYPIRC) + self.assertEqual(content, WANTED_PYPIRC) finally: f.close() @@ -141,8 +141,8 @@ class RegisterTestCase(PyPIRCCommandTestCase): req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) - self.assertEquals(req1['Content-length'], '1374') - self.assertEquals(req2['Content-length'], '1374') + self.assertEqual(req1['Content-length'], '1374') + self.assertEqual(req2['Content-length'], '1374') self.assertTrue((b'xxx') in self.conn.reqs[1].data) def test_password_not_in_file(self): @@ -155,7 +155,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): # dist.password should be set # therefore used afterwards by other commands - self.assertEquals(cmd.distribution.password, 'password') + self.assertEqual(cmd.distribution.password, 'password') def test_registering(self): # this test runs choice 2 @@ -172,7 +172,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) - self.assertEquals(headers['Content-length'], '608') + self.assertEqual(headers['Content-length'], '608') self.assertTrue((b'tarek') in req.data) def test_password_reset(self): @@ -190,7 +190,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) - self.assertEquals(headers['Content-length'], '290') + self.assertEqual(headers['Content-length'], '290') self.assertTrue((b'tarek') in req.data) def test_strict(self): @@ -253,7 +253,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): with check_warnings() as w: warnings.simplefilter("always") cmd.check_metadata() - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) def test_suite(): return unittest.makeSuite(RegisterTestCase) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 73962d34..655e50dc 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -108,7 +108,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # now let's check what we have dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) - self.assertEquals(files, ['fake-1.0.zip']) + self.assertEqual(files, ['fake-1.0.zip']) zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) try: @@ -117,7 +117,7 @@ class SDistTestCase(PyPIRCCommandTestCase): zip_file.close() # making sure everything has been pruned correctly - self.assertEquals(len(content), 4) + self.assertEqual(len(content), 4) def test_make_distribution(self): @@ -138,8 +138,7 @@ class SDistTestCase(PyPIRCCommandTestCase): dist_folder = join(self.tmp_dir, 'dist') result = os.listdir(dist_folder) result.sort() - self.assertEquals(result, - ['fake-1.0.tar', 'fake-1.0.tar.gz'] ) + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'] ) os.remove(join(dist_folder, 'fake-1.0.tar')) os.remove(join(dist_folder, 'fake-1.0.tar.gz')) @@ -152,8 +151,7 @@ class SDistTestCase(PyPIRCCommandTestCase): result = os.listdir(dist_folder) result.sort() - self.assertEquals(result, - ['fake-1.0.tar', 'fake-1.0.tar.gz']) + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) def test_add_defaults(self): @@ -201,7 +199,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # now let's check what we have dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) - self.assertEquals(files, ['fake-1.0.zip']) + self.assertEqual(files, ['fake-1.0.zip']) zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) try: @@ -210,13 +208,13 @@ class SDistTestCase(PyPIRCCommandTestCase): zip_file.close() # making sure everything was added - self.assertEquals(len(content), 11) + self.assertEqual(len(content), 11) # checking the MANIFEST f = open(join(self.tmp_dir, 'MANIFEST')) try: manifest = f.read() - self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + self.assertEqual(manifest, MANIFEST % {'sep': os.sep}) finally: f.close() @@ -229,7 +227,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.ensure_finalized() cmd.run() warnings = self.get_logs(WARN) - self.assertEquals(len(warnings), 2) + self.assertEqual(len(warnings), 2) # trying with a complete set of metadata self.clear_logs() @@ -238,7 +236,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.metadata_check = 0 cmd.run() warnings = self.get_logs(WARN) - self.assertEquals(len(warnings), 0) + self.assertEqual(len(warnings), 0) def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated @@ -246,7 +244,7 @@ class SDistTestCase(PyPIRCCommandTestCase): with check_warnings() as w: warnings.simplefilter("always") cmd.check_metadata() - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) def test_show_formats(self): with captured_stdout() as stdout: @@ -256,7 +254,7 @@ class SDistTestCase(PyPIRCCommandTestCase): num_formats = len(ARCHIVE_FORMATS.keys()) output = [line for line in stdout.getvalue().split('\n') if line.strip().startswith('--formats=')] - self.assertEquals(len(output), num_formats) + self.assertEqual(len(output), num_formats) def test_finalize_options(self): @@ -264,9 +262,9 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.finalize_options() # default options set by finalize - self.assertEquals(cmd.manifest, 'MANIFEST') - self.assertEquals(cmd.template, 'MANIFEST.in') - self.assertEquals(cmd.dist_dir, 'dist') + self.assertEqual(cmd.manifest, 'MANIFEST') + self.assertEqual(cmd.template, 'MANIFEST.in') + self.assertEqual(cmd.dist_dir, 'dist') # formats has to be a string splitable on (' ', ',') or # a stringlist @@ -297,7 +295,7 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: f.close() - self.assertEquals(len(manifest), 5) + self.assertEqual(len(manifest), 5) # adding a file self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') @@ -317,7 +315,7 @@ class SDistTestCase(PyPIRCCommandTestCase): f.close() # do we have the new file in MANIFEST ? - self.assertEquals(len(manifest2), 6) + self.assertEqual(len(manifest2), 6) self.assertIn('doc2.txt', manifest2[-1]) def test_manifest_marker(self): diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 5b91aa5a..6c7eb20c 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -20,7 +20,7 @@ class SpawnTestCase(support.TempdirManager, (['nochange', 'nospace'], ['nochange', 'nospace'])): res = _nt_quote_args(args) - self.assertEquals(res, wanted) + self.assertEqual(res, wanted) @unittest.skipUnless(os.name in ('nt', 'posix'), diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 215dc82d..336c6a5b 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -70,7 +70,7 @@ class SysconfigTestCase(support.EnvironGuard, comp = compiler() sysconfig.customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') + self.assertEqual(comp.exes['archiver'], 'my_ar -arflags') def test_parse_makefile_base(self): self.makefile = TESTFN @@ -81,8 +81,8 @@ class SysconfigTestCase(support.EnvironGuard, finally: fd.close() d = sysconfig.parse_makefile(self.makefile) - self.assertEquals(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", - 'OTHER': 'foo'}) + self.assertEqual(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", + 'OTHER': 'foo'}) def test_parse_makefile_literal_dollar(self): self.makefile = TESTFN @@ -93,16 +93,16 @@ class SysconfigTestCase(support.EnvironGuard, finally: fd.close() d = sysconfig.parse_makefile(self.makefile) - self.assertEquals(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", - 'OTHER': 'foo'}) + self.assertEqual(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", + 'OTHER': 'foo'}) def test_sysconfig_module(self): import sysconfig as global_sysconfig - self.assertEquals(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS')) - self.assertEquals(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS')) - self.assertEquals(global_sysconfig.get_config_var('LDSHARED'),sysconfig.get_config_var('LDSHARED')) - self.assertEquals(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC')) + self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS')) + self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS')) + self.assertEqual(global_sysconfig.get_config_var('LDSHARED'),sysconfig.get_config_var('LDSHARED')) + self.assertEqual(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC')) diff --git a/tests/test_text_file.py b/tests/test_text_file.py index 953cb8ea..7e76240a 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -49,7 +49,7 @@ class TextFileTestCase(support.TempdirManager, unittest.TestCase): def test_input(count, description, file, expected_result): result = file.readlines() - self.assertEquals(result, expected_result) + self.assertEqual(result, expected_result) tmpdir = self.mkdtemp() filename = os.path.join(tmpdir, "test.txt") diff --git a/tests/test_upload.py b/tests/test_upload.py index 4661ed32..4c6464a3 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -89,7 +89,7 @@ class uploadTestCase(PyPIRCCommandTestCase): for attr, waited in (('username', 'me'), ('password', 'secret'), ('realm', 'pypi'), ('repository', 'http://pypi.python.org/pypi')): - self.assertEquals(getattr(cmd, attr), waited) + self.assertEqual(getattr(cmd, attr), waited) def test_saved_password(self): # file with no password @@ -99,14 +99,14 @@ class uploadTestCase(PyPIRCCommandTestCase): dist = Distribution() cmd = upload(dist) cmd.finalize_options() - self.assertEquals(cmd.password, None) + self.assertEqual(cmd.password, None) # make sure we get it as well, if another command # initialized it at the dist level dist.password = 'xxx' cmd = upload(dist) cmd.finalize_options() - self.assertEquals(cmd.password, 'xxx') + self.assertEqual(cmd.password, 'xxx') def test_upload(self): tmp = self.mkdtemp() @@ -124,12 +124,12 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.conn.headers) - self.assertEquals(headers['Content-length'], '2087') + self.assertEqual(headers['Content-length'], '2087') self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) self.assertFalse('\n' in headers['Authorization']) - self.assertEquals(self.conn.requests, [('POST', '/pypi')]) - self.assert_((b'xxx') in self.conn.body) + self.assertEqual(self.conn.requests, [('POST', '/pypi')]) + self.assertTrue((b'xxx') in self.conn.body) def test_suite(): return unittest.makeSuite(uploadTestCase) diff --git a/tests/test_util.py b/tests/test_util.py index 3b7159d7..8ff5ae20 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -67,21 +67,21 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Intel)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win32') + self.assertEqual(get_platform(), 'win32') # windows XP, amd64 os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Amd64)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-amd64') + self.assertEqual(get_platform(), 'win-amd64') # windows XP, itanium os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Itanium)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-ia64') + self.assertEqual(get_platform(), 'win-ia64') # macbook os.name = 'posix' @@ -100,7 +100,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): cursize = sys.maxsize sys.maxsize = (2 ** 31)-1 try: - self.assertEquals(get_platform(), 'macosx-10.3-i386') + self.assertEqual(get_platform(), 'macosx-10.3-i386') finally: sys.maxsize = cursize @@ -111,33 +111,33 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat') + self.assertEqual(get_platform(), 'macosx-10.4-fat') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-intel') + self.assertEqual(get_platform(), 'macosx-10.4-intel') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat3') + self.assertEqual(get_platform(), 'macosx-10.4-fat3') get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-universal') + self.assertEqual(get_platform(), 'macosx-10.4-universal') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat64') + self.assertEqual(get_platform(), 'macosx-10.4-fat64') for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' @@ -145,7 +145,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3'%(arch,)) - self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) + self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,)) # linux debian sarge os.name = 'posix' @@ -155,7 +155,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) - self.assertEquals(get_platform(), 'linux-i686') + self.assertEqual(get_platform(), 'linux-i686') # XXX more platforms to tests here @@ -166,8 +166,8 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): return '/'.join(path) os.path.join = _join - self.assertEquals(convert_path('/home/to/my/stuff'), - '/home/to/my/stuff') + self.assertEqual(convert_path('/home/to/my/stuff'), + '/home/to/my/stuff') # win os.sep = '\\' @@ -178,10 +178,10 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') - self.assertEquals(convert_path('home/to/my/stuff'), - 'home\\to\\my\\stuff') - self.assertEquals(convert_path('.'), - os.curdir) + self.assertEqual(convert_path('home/to/my/stuff'), + 'home\\to\\my\\stuff') + self.assertEqual(convert_path('.'), + os.curdir) def test_change_root(self): # linux/mac @@ -193,10 +193,10 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): return '/'.join(path) os.path.join = _join - self.assertEquals(change_root('/root', '/old/its/here'), - '/root/old/its/here') - self.assertEquals(change_root('/root', 'its/here'), - '/root/its/here') + self.assertEqual(change_root('/root', '/old/its/here'), + '/root/old/its/here') + self.assertEqual(change_root('/root', 'its/here'), + '/root/its/here') # windows os.name = 'nt' @@ -212,10 +212,10 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): return '\\'.join(path) os.path.join = _join - self.assertEquals(change_root('c:\\root', 'c:\\old\\its\\here'), - 'c:\\root\\old\\its\\here') - self.assertEquals(change_root('c:\\root', 'its\\here'), - 'c:\\root\\its\\here') + self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'), + 'c:\\root\\old\\its\\here') + self.assertEqual(change_root('c:\\root', 'its\\here'), + 'c:\\root\\its\\here') # BugsBunny os (it's a great os) os.name = 'BugsBunny' @@ -233,16 +233,16 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): if os.name == 'posix': # this test won't run on windows check_environ() import pwd - self.assertEquals(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) + self.assertEqual(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) else: check_environ() - self.assertEquals(os.environ['PLAT'], get_platform()) - self.assertEquals(util._environ_checked, 1) + self.assertEqual(os.environ['PLAT'], get_platform()) + self.assertEqual(util._environ_checked, 1) def test_split_quoted(self): - self.assertEquals(split_quoted('""one"" "two" \'three\' \\four'), - ['one', 'two', 'three', 'four']) + self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), + ['one', 'two', 'three', 'four']) def test_strtobool(self): yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') @@ -259,7 +259,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): res = rfc822_escape(header) wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' 'header%(8s)s') % {'8s': '\n'+8*' '} - self.assertEquals(res, wanted) + self.assertEqual(res, wanted) def test_dont_write_bytecode(self): # makes sure byte_compile raise a DistutilsError diff --git a/tests/test_version.py b/tests/test_version.py index ff40f6b4..15f14c7d 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -8,12 +8,12 @@ class VersionTestCase(unittest.TestCase): def test_prerelease(self): version = StrictVersion('1.2.3a1') - self.assertEquals(version.version, (1, 2, 3)) - self.assertEquals(version.prerelease, ('a', 1)) - self.assertEquals(str(version), '1.2.3a1') + self.assertEqual(version.version, (1, 2, 3)) + self.assertEqual(version.prerelease, ('a', 1)) + self.assertEqual(str(version), '1.2.3a1') version = StrictVersion('1.2.0') - self.assertEquals(str(version), '1.2') + self.assertEqual(str(version), '1.2') def test_cmp_strict(self): versions = (('1.5.1', '1.5.2b2', -1), @@ -42,9 +42,9 @@ class VersionTestCase(unittest.TestCase): raise AssertionError(("cmp(%s, %s) " "shouldn't raise ValueError") % (v1, v2)) - self.assertEquals(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) def test_cmp(self): @@ -60,9 +60,9 @@ class VersionTestCase(unittest.TestCase): for v1, v2, wanted in versions: res = LooseVersion(v1)._cmp(LooseVersion(v2)) - self.assertEquals(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) def test_suite(): return unittest.makeSuite(VersionTestCase) -- cgit v1.2.1 From 0ed78e843c5c79f31e9664a39820e21722b537d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Nov 2010 19:35:27 +0000 Subject: Fix two NameErrors in distutils (#10407) --- dir_util.py | 1 + tests/test_sysconfig.py | 1 + 2 files changed, 2 insertions(+) diff --git a/dir_util.py b/dir_util.py index c7c9fccf..5b005f08 100644 --- a/dir_util.py +++ b/dir_util.py @@ -5,6 +5,7 @@ Utility functions for manipulating directories and directory trees.""" __revision__ = "$Id$" import os, sys +import errno from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 336c6a5b..fbe26bf6 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,5 +1,6 @@ """Tests for distutils.sysconfig.""" import os +import shutil import test import unittest -- cgit v1.2.1 From 33f7d87cb4ff994bfed3923052bde36b9698a6f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Nov 2010 19:45:32 +0000 Subject: Merged revisions 86601,86605 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86601 | eric.araujo | 2010-11-20 20:35:27 +0100 (sam., 20 nov. 2010) | 2 lines Fix two NameErrors in distutils (#10407) ........ r86605 | eric.araujo | 2010-11-20 20:37:28 +0100 (sam., 20 nov. 2010) | 2 lines Add entry for r86601 ........ --- dir_util.py | 1 + tests/test_sysconfig.py | 1 + 2 files changed, 2 insertions(+) diff --git a/dir_util.py b/dir_util.py index c7c9fccf..5b005f08 100644 --- a/dir_util.py +++ b/dir_util.py @@ -5,6 +5,7 @@ Utility functions for manipulating directories and directory trees.""" __revision__ = "$Id$" import os, sys +import errno from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 78bce946..309be7b8 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,5 +1,6 @@ """Tests for distutils.sysconfig.""" import os +import shutil import test import unittest -- cgit v1.2.1 From d9cf33af24cb0537a0dc26fb94e79959467483ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Nov 2010 20:02:41 +0000 Subject: Merged revisions 86601,86605 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k In 2.7, there was only one NameError. ........ r86601 | eric.araujo | 2010-11-20 20:35:27 +0100 (sam., 20 nov. 2010) | 2 lines Fix two NameErrors in distutils (#10407) ........ r86605 | eric.araujo | 2010-11-20 20:37:28 +0100 (sam., 20 nov. 2010) | 2 lines Add entry for r86601 ........ --- dir_util.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dir_util.py b/dir_util.py index d6875936..9c5cf337 100644 --- a/dir_util.py +++ b/dir_util.py @@ -5,6 +5,7 @@ Utility functions for manipulating directories and directory trees.""" __revision__ = "$Id$" import os +import errno from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log -- cgit v1.2.1 From dcc7727442e3d4796d7d2bb8db40c8e04f42703a Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sun, 21 Nov 2010 01:30:29 +0000 Subject: Merged revisions 86596 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86596 | ezio.melotti | 2010-11-20 21:04:17 +0200 (Sat, 20 Nov 2010) | 1 line #9424: Replace deprecated assert* methods in the Python test suite. ........ --- tests/test_archive_util.py | 14 +++++----- tests/test_bdist.py | 4 +-- tests/test_bdist_dumb.py | 2 +- tests/test_build_clib.py | 10 +++---- tests/test_build_ext.py | 56 +++++++++++++++++++------------------- tests/test_check.py | 14 +++++----- tests/test_cmd.py | 10 +++---- tests/test_config.py | 6 ++--- tests/test_config_cmd.py | 12 ++++----- tests/test_core.py | 4 +-- tests/test_cygwinccompiler.py | 32 +++++++++++----------- tests/test_dir_util.py | 22 +++++++-------- tests/test_dist.py | 20 +++++++------- tests/test_extension.py | 16 +++++------ tests/test_file_util.py | 6 ++--- tests/test_filelist.py | 18 ++++++------- tests/test_install.py | 20 +++++++------- tests/test_install_data.py | 8 +++--- tests/test_install_headers.py | 4 +-- tests/test_install_lib.py | 8 +++--- tests/test_log.py | 4 +-- tests/test_msvc9compiler.py | 4 +-- tests/test_register.py | 14 +++++----- tests/test_sdist.py | 34 +++++++++++------------- tests/test_spawn.py | 2 +- tests/test_sysconfig.py | 8 +++--- tests/test_text_file.py | 2 +- tests/test_upload.py | 12 ++++----- tests/test_util.py | 62 +++++++++++++++++++++---------------------- tests/test_version.py | 20 +++++++------- 30 files changed, 223 insertions(+), 225 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index c6e08cbc..d77302d5 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -113,7 +113,7 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertTrue(os.path.exists(tarball2)) # let's compare both tarballs - self.assertEquals(self._tarinfo(tarball), self._tarinfo(tarball2)) + self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2)) # trying an uncompressed one base_name = os.path.join(tmpdir2, 'archive') @@ -153,7 +153,7 @@ class ArchiveUtilTestCase(support.TempdirManager, os.chdir(old_dir) tarball = base_name + '.tar.Z' self.assertTrue(os.path.exists(tarball)) - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) # same test with dry_run os.remove(tarball) @@ -167,7 +167,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) self.assertTrue(not os.path.exists(tarball)) - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile(self): @@ -184,9 +184,9 @@ class ArchiveUtilTestCase(support.TempdirManager, tarball = base_name + '.zip' def test_check_archive_formats(self): - self.assertEquals(check_archive_formats(['gztar', 'xxx', 'zip']), - 'xxx') - self.assertEquals(check_archive_formats(['gztar', 'zip']), None) + self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), + 'xxx') + self.assertEqual(check_archive_formats(['gztar', 'zip']), None) def test_make_archive(self): tmpdir = self.mkdtemp() @@ -203,7 +203,7 @@ class ArchiveUtilTestCase(support.TempdirManager, make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) except: pass - self.assertEquals(os.getcwd(), current_dir) + self.assertEqual(os.getcwd(), current_dir) finally: del ARCHIVE_FORMATS['xxx'] diff --git a/tests/test_bdist.py b/tests/test_bdist.py index f2849a97..715283cb 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -23,7 +23,7 @@ class BuildTestCase(support.TempdirManager, cmd = bdist(dist) cmd.formats = ['msi'] cmd.ensure_finalized() - self.assertEquals(cmd.formats, ['msi']) + self.assertEqual(cmd.formats, ['msi']) # what format bdist offers ? # XXX an explicit list in bdist is @@ -34,7 +34,7 @@ class BuildTestCase(support.TempdirManager, formats.sort() founded = list(cmd.format_command.keys()) founded.sort() - self.assertEquals(founded, formats) + self.assertEqual(founded, formats) def test_suite(): return unittest.makeSuite(BuildTestCase) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 5e76809f..bf146eb5 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -68,7 +68,7 @@ class BuildDumbTestCase(support.TempdirManager, base = base.replace(':', '-') wanted = ['%s.zip' % base] - self.assertEquals(dist_created, wanted) + self.assertEqual(dist_created, wanted) # now let's check what we have in the zip file # XXX to be done diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 536cd673..d6d1a4d0 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -55,14 +55,14 @@ class BuildCLibTestCase(support.TempdirManager, self.assertRaises(DistutilsSetupError, cmd.get_source_files) cmd.libraries = [('name', {'sources': ['a', 'b']})] - self.assertEquals(cmd.get_source_files(), ['a', 'b']) + self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')})] - self.assertEquals(cmd.get_source_files(), ['a', 'b']) + self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')}), ('name2', {'sources': ['c', 'd']})] - self.assertEquals(cmd.get_source_files(), ['a', 'b', 'c', 'd']) + self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd']) def test_build_libraries(self): @@ -91,11 +91,11 @@ class BuildCLibTestCase(support.TempdirManager, cmd.include_dirs = 'one-dir' cmd.finalize_options() - self.assertEquals(cmd.include_dirs, ['one-dir']) + self.assertEqual(cmd.include_dirs, ['one-dir']) cmd.include_dirs = None cmd.finalize_options() - self.assertEquals(cmd.include_dirs, []) + self.assertEqual(cmd.include_dirs, []) cmd.distribution.libraries = 'WONTWORK' self.assertRaises(DistutilsSetupError, cmd.finalize_options) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 226f7bb1..e8b15f15 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -96,11 +96,11 @@ class BuildExtTestCase(TempdirManager, for attr in ('error', 'foo', 'new', 'roj'): self.assertTrue(hasattr(xx, attr)) - self.assertEquals(xx.foo(2, 5), 7) - self.assertEquals(xx.foo(13,15), 28) - self.assertEquals(xx.new().demo(), None) + self.assertEqual(xx.foo(2, 5), 7) + self.assertEqual(xx.foo(13,15), 28) + self.assertEqual(xx.new().demo(), None) doc = 'This is a template module just for instruction.' - self.assertEquals(xx.__doc__, doc) + self.assertEqual(xx.__doc__, doc) self.assertTrue(isinstance(xx.Null(), xx.Null)) self.assertTrue(isinstance(xx.Str(), xx.Str)) @@ -206,7 +206,7 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.libraries = 'my_lib' cmd.finalize_options() - self.assertEquals(cmd.libraries, ['my_lib']) + self.assertEqual(cmd.libraries, ['my_lib']) # make sure cmd.library_dirs is turned into a list # if it's a string @@ -220,7 +220,7 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.rpath = os.pathsep.join(['one', 'two']) cmd.finalize_options() - self.assertEquals(cmd.rpath, ['one', 'two']) + self.assertEqual(cmd.rpath, ['one', 'two']) # XXX more tests to perform for win32 @@ -229,25 +229,25 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.define = 'one,two' cmd.finalize_options() - self.assertEquals(cmd.define, [('one', '1'), ('two', '1')]) + self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) # make sure undef is turned into a list of # strings if they are ','-separated strings cmd = build_ext(dist) cmd.undef = 'one,two' cmd.finalize_options() - self.assertEquals(cmd.undef, ['one', 'two']) + self.assertEqual(cmd.undef, ['one', 'two']) # make sure swig_opts is turned into a list cmd = build_ext(dist) cmd.swig_opts = None cmd.finalize_options() - self.assertEquals(cmd.swig_opts, []) + self.assertEqual(cmd.swig_opts, []) cmd = build_ext(dist) cmd.swig_opts = '1 2' cmd.finalize_options() - self.assertEquals(cmd.swig_opts, ['1', '2']) + self.assertEqual(cmd.swig_opts, ['1', '2']) def test_check_extensions_list(self): dist = Distribution() @@ -283,7 +283,7 @@ class BuildExtTestCase(TempdirManager, # check_extensions_list adds in ext the values passed # when they are in ('include_dirs', 'library_dirs', 'libraries' # 'extra_objects', 'extra_compile_args', 'extra_link_args') - self.assertEquals(ext.libraries, 'foo') + self.assertEqual(ext.libraries, 'foo') self.assertTrue(not hasattr(ext, 'some')) # 'macros' element of build info dict must be 1- or 2-tuple @@ -293,15 +293,15 @@ class BuildExtTestCase(TempdirManager, exts[0][1]['macros'] = [('1', '2'), ('3',)] cmd.check_extensions_list(exts) - self.assertEquals(exts[0].undef_macros, ['3']) - self.assertEquals(exts[0].define_macros, [('1', '2')]) + self.assertEqual(exts[0].undef_macros, ['3']) + self.assertEqual(exts[0].define_macros, [('1', '2')]) def test_get_source_files(self): modules = [Extension('foo', ['xxx'], optional=False)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = build_ext(dist) cmd.ensure_finalized() - self.assertEquals(cmd.get_source_files(), ['xxx']) + self.assertEqual(cmd.get_source_files(), ['xxx']) def test_compiler_option(self): # cmd.compiler is an option and @@ -312,7 +312,7 @@ class BuildExtTestCase(TempdirManager, cmd.compiler = 'unix' cmd.ensure_finalized() cmd.run() - self.assertEquals(cmd.compiler, 'unix') + self.assertEqual(cmd.compiler, 'unix') def test_get_outputs(self): tmp_dir = self.mkdtemp() @@ -324,7 +324,7 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) self._fixup_command(cmd) cmd.ensure_finalized() - self.assertEquals(len(cmd.get_outputs()), 1) + self.assertEqual(len(cmd.get_outputs()), 1) if os.name == "nt": cmd.debug = sys.executable.endswith("_d.exe") @@ -344,20 +344,20 @@ class BuildExtTestCase(TempdirManager, finally: os.chdir(old_wd) self.assertTrue(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], - sysconfig.get_config_var('SO')) + self.assertEqual(os.path.splitext(so_file)[-1], + sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, other_tmp_dir) + self.assertEqual(so_dir, other_tmp_dir) cmd.inplace = 0 cmd.compiler = None cmd.run() so_file = cmd.get_outputs()[0] self.assertTrue(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], - sysconfig.get_config_var('SO')) + self.assertEqual(os.path.splitext(so_file)[-1], + sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, cmd.build_lib) + self.assertEqual(so_dir, cmd.build_lib) # inplace = 0, cmd.package = 'bar' build_py = cmd.get_finalized_command('build_py') @@ -365,7 +365,7 @@ class BuildExtTestCase(TempdirManager, path = cmd.get_ext_fullpath('foo') # checking that the last directory is the build_dir path = os.path.split(path)[0] - self.assertEquals(path, cmd.build_lib) + self.assertEqual(path, cmd.build_lib) # inplace = 1, cmd.package = 'bar' cmd.inplace = 1 @@ -379,7 +379,7 @@ class BuildExtTestCase(TempdirManager, # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, 'bar') + self.assertEqual(lastdir, 'bar') def test_ext_fullpath(self): ext = sysconfig.get_config_vars()['SO'] @@ -395,14 +395,14 @@ class BuildExtTestCase(TempdirManager, curdir = os.getcwd() wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building lxml.etree not inplace cmd.inplace = 0 cmd.build_lib = os.path.join(curdir, 'tmpdir') wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building twisted.runner.portmap not inplace build_py = cmd.get_finalized_command('build_py') @@ -411,13 +411,13 @@ class BuildExtTestCase(TempdirManager, path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', 'portmap' + ext) - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building twisted.runner.portmap inplace cmd.inplace = 1 path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) def test_suite(): src = _get_source_filename() diff --git a/tests/test_check.py b/tests/test_check.py index 372bae36..7c56c043 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -26,7 +26,7 @@ class CheckTestCase(support.LoggingSilencer, # by default, check is checking the metadata # should have some warnings cmd = self._run() - self.assertEquals(cmd._warnings, 2) + self.assertEqual(cmd._warnings, 2) # now let's add the required fields # and run it again, to make sure we don't get @@ -35,7 +35,7 @@ class CheckTestCase(support.LoggingSilencer, 'author_email': 'xxx', 'name': 'xxx', 'version': 'xxx'} cmd = self._run(metadata) - self.assertEquals(cmd._warnings, 0) + self.assertEqual(cmd._warnings, 0) # now with the strict mode, we should # get an error if there are missing metadata @@ -43,7 +43,7 @@ class CheckTestCase(support.LoggingSilencer, # and of course, no error when all metadata are present cmd = self._run(metadata, strict=1) - self.assertEquals(cmd._warnings, 0) + self.assertEqual(cmd._warnings, 0) def test_check_document(self): if not HAS_DOCUTILS: # won't test without docutils @@ -54,12 +54,12 @@ class CheckTestCase(support.LoggingSilencer, # let's see if it detects broken rest broken_rest = 'title\n===\n\ntest' msgs = cmd._check_rst_data(broken_rest) - self.assertEquals(len(msgs), 1) + self.assertEqual(len(msgs), 1) # and non-broken rest rest = 'title\n=====\n\ntest' msgs = cmd._check_rst_data(rest) - self.assertEquals(len(msgs), 0) + self.assertEqual(len(msgs), 0) def test_check_restructuredtext(self): if not HAS_DOCUTILS: # won't test without docutils @@ -69,7 +69,7 @@ class CheckTestCase(support.LoggingSilencer, pkg_info, dist = self.create_dist(long_description=broken_rest) cmd = check(dist) cmd.check_restructuredtext() - self.assertEquals(cmd._warnings, 1) + self.assertEqual(cmd._warnings, 1) # let's see if we have an error with strict=1 metadata = {'url': 'xxx', 'author': 'xxx', @@ -82,7 +82,7 @@ class CheckTestCase(support.LoggingSilencer, # and non-broken rest metadata['long_description'] = 'title\n=====\n\ntest' cmd = self._run(metadata, strict=1, restructuredtext=1) - self.assertEquals(cmd._warnings, 0) + self.assertEqual(cmd._warnings, 0) def test_check_all(self): diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 55ae421d..5821fcd2 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -44,7 +44,7 @@ class CommandTestCase(unittest.TestCase): # making sure execute gets called properly def _execute(func, args, exec_msg, level): - self.assertEquals(exec_msg, 'generating out from in') + self.assertEqual(exec_msg, 'generating out from in') cmd.force = True cmd.execute = _execute cmd.make_file(infiles='in', outfile='out', func='func', args=()) @@ -63,7 +63,7 @@ class CommandTestCase(unittest.TestCase): wanted = ["command options for 'MyCmd':", ' option1 = 1', ' option2 = 1'] - self.assertEquals(msgs, wanted) + self.assertEqual(msgs, wanted) def test_ensure_string(self): cmd = self.cmd @@ -81,7 +81,7 @@ class CommandTestCase(unittest.TestCase): cmd = self.cmd cmd.option1 = 'ok,dok' cmd.ensure_string_list('option1') - self.assertEquals(cmd.option1, ['ok', 'dok']) + self.assertEqual(cmd.option1, ['ok', 'dok']) cmd.option2 = ['xxx', 'www'] cmd.ensure_string_list('option2') @@ -109,14 +109,14 @@ class CommandTestCase(unittest.TestCase): with captured_stdout() as stdout: cmd.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), '') + self.assertEqual(stdout.read(), '') debug.DEBUG = True try: with captured_stdout() as stdout: cmd.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), 'xxx\n') + self.assertEqual(stdout.read(), 'xxx\n') finally: debug.DEBUG = False diff --git a/tests/test_config.py b/tests/test_config.py index 6a45a328..2c075d7e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -88,7 +88,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, waited = [('password', 'secret'), ('realm', 'pypi'), ('repository', 'http://pypi.python.org/pypi'), ('server', 'server1'), ('username', 'me')] - self.assertEquals(config, waited) + self.assertEqual(config, waited) # old format self.write_file(self.rc, PYPIRC_OLD) @@ -97,7 +97,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, waited = [('password', 'secret'), ('realm', 'pypi'), ('repository', 'http://pypi.python.org/pypi'), ('server', 'server-login'), ('username', 'tarek')] - self.assertEquals(config, waited) + self.assertEqual(config, waited) def test_server_empty_registration(self): cmd = self._cmd(self.dist) @@ -108,7 +108,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, f = open(rc) try: content = f.read() - self.assertEquals(content, WANTED) + self.assertEqual(content, WANTED) finally: f.close() diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index ef2e7bc3..fcb798e7 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -34,7 +34,7 @@ class ConfigTestCase(support.LoggingSilencer, f.close() dump_file(this_file, 'I am the header') - self.assertEquals(len(self._logs), numlines+1) + self.assertEqual(len(self._logs), numlines+1) def test_search_cpp(self): if sys.platform == 'win32': @@ -44,10 +44,10 @@ class ConfigTestCase(support.LoggingSilencer, # simple pattern searches match = cmd.search_cpp(pattern='xxx', body='// xxx') - self.assertEquals(match, 0) + self.assertEqual(match, 0) match = cmd.search_cpp(pattern='_configtest', body='// xxx') - self.assertEquals(match, 1) + self.assertEqual(match, 1) def test_finalize_options(self): # finalize_options does a bit of transformation @@ -59,9 +59,9 @@ class ConfigTestCase(support.LoggingSilencer, cmd.library_dirs = 'three%sfour' % os.pathsep cmd.ensure_finalized() - self.assertEquals(cmd.include_dirs, ['one', 'two']) - self.assertEquals(cmd.libraries, ['one']) - self.assertEquals(cmd.library_dirs, ['three', 'four']) + self.assertEqual(cmd.include_dirs, ['one', 'two']) + self.assertEqual(cmd.libraries, ['one']) + self.assertEqual(cmd.library_dirs, ['three', 'four']) def test_clean(self): # _clean removes files diff --git a/tests/test_core.py b/tests/test_core.py index e937b45a..d781555d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -89,7 +89,7 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): with captured_stdout() as stdout: distutils.core.setup(name='bar') stdout.seek(0) - self.assertEquals(stdout.read(), 'bar\n') + self.assertEqual(stdout.read(), 'bar\n') distutils.core.DEBUG = True try: @@ -99,7 +99,7 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): distutils.core.DEBUG = False stdout.seek(0) wanted = "options (after parsing config files):\n" - self.assertEquals(stdout.readlines()[0], wanted) + self.assertEqual(stdout.readlines()[0], wanted) def test_suite(): return unittest.makeSuite(CoreTestCase) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index a57694d4..79377a77 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -65,82 +65,82 @@ class CygwinCCompilerTestCase(support.TempdirManager, sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' '4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEquals(check_config_h()[0], CONFIG_H_OK) + self.assertEqual(check_config_h()[0], CONFIG_H_OK) # then it tries to see if it can find "__GNUC__" in pyconfig.h sys.version = 'something without the *CC word' # if the file doesn't exist it returns CONFIG_H_UNCERTAIN - self.assertEquals(check_config_h()[0], CONFIG_H_UNCERTAIN) + self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN) # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK self.write_file(self.python_h, 'xxx') - self.assertEquals(check_config_h()[0], CONFIG_H_NOTOK) + self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK) # and CONFIG_H_OK if __GNUC__ is found self.write_file(self.python_h, 'xxx __GNUC__ xxx') - self.assertEquals(check_config_h()[0], CONFIG_H_OK) + self.assertEqual(check_config_h()[0], CONFIG_H_OK) def test_get_versions(self): # get_versions calls distutils.spawn.find_executable on # 'gcc', 'ld' and 'dllwrap' - self.assertEquals(get_versions(), (None, None, None)) + self.assertEqual(get_versions(), (None, None, None)) # Let's fake we have 'gcc' and it returns '3.4.5' self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' res = get_versions() - self.assertEquals(str(res[0]), '3.4.5') + self.assertEqual(str(res[0]), '3.4.5') # and let's see what happens when the version # doesn't match the regular expression # (\d+\.\d+(\.\d+)*) self._exes['gcc'] = b'very strange output' res = get_versions() - self.assertEquals(res[0], None) + self.assertEqual(res[0], None) # same thing for ld self._exes['ld'] = b'GNU ld version 2.17.50 20060824' res = get_versions() - self.assertEquals(str(res[1]), '2.17.50') + self.assertEqual(str(res[1]), '2.17.50') self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' res = get_versions() - self.assertEquals(res[1], None) + self.assertEqual(res[1], None) # and dllwrap self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' res = get_versions() - self.assertEquals(str(res[2]), '2.17.50') + self.assertEqual(str(res[2]), '2.17.50') self._exes['dllwrap'] = b'Cheese Wrap' res = get_versions() - self.assertEquals(res[2], None) + self.assertEqual(res[2], None) def test_get_msvcr(self): # none sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEquals(get_msvcr(), None) + self.assertEqual(get_msvcr(), None) # MSVC 7.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1300 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr70']) + self.assertEqual(get_msvcr(), ['msvcr70']) # MSVC 7.1 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1310 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr71']) + self.assertEqual(get_msvcr(), ['msvcr71']) # VS2005 / MSVC 8.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1400 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr80']) + self.assertEqual(get_msvcr(), ['msvcr80']) # VS2008 / MSVC 9.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1500 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr90']) + self.assertEqual(get_msvcr(), ['msvcr90']) # unknown sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index aa9f9ebb..84a0ec6c 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -37,18 +37,18 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): mkpath(self.target, verbose=0) wanted = [] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) mkpath(self.target, verbose=1) wanted = ['creating %s' % self.root_target, 'creating %s' % self.target] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) self._logs = [] remove_tree(self.root_target, verbose=1) wanted = ["removing '%s' (and everything under it)" % self.root_target] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) @unittest.skipIf(sys.platform.startswith('win'), "This test is only appropriate for POSIX-like systems.") @@ -66,12 +66,12 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): def test_create_tree_verbosity(self): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) - self.assertEquals(self._logs, []) + self.assertEqual(self._logs, []) remove_tree(self.root_target, verbose=0) wanted = ['creating %s' % self.root_target] create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) @@ -81,7 +81,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): mkpath(self.target, verbose=0) copy_tree(self.target, self.target2, verbose=0) - self.assertEquals(self._logs, []) + self.assertEqual(self._logs, []) remove_tree(self.root_target, verbose=0) @@ -95,18 +95,18 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ['copying %s -> %s' % (a_file, self.target2)] copy_tree(self.target, self.target2, verbose=1) - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) remove_tree(self.target2, verbose=0) def test_ensure_relative(self): if os.sep == '/': - self.assertEquals(ensure_relative('/home/foo'), 'home/foo') - self.assertEquals(ensure_relative('some/path'), 'some/path') + self.assertEqual(ensure_relative('/home/foo'), 'home/foo') + self.assertEqual(ensure_relative('some/path'), 'some/path') else: # \\ - self.assertEquals(ensure_relative('c:\\home\\foo'), 'c:home\\foo') - self.assertEquals(ensure_relative('home\\foo'), 'home\\foo') + self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') + self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') def test_suite(): return unittest.makeSuite(DirUtilTestCase) diff --git a/tests/test_dist.py b/tests/test_dist.py index f9c5a4fd..2c19d892 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -125,7 +125,7 @@ class DistributionTestCase(support.LoggingSilencer, finally: warnings.warn = old_warn - self.assertEquals(len(warns), 0) + self.assertEqual(len(warns), 0) def test_finalize_options(self): @@ -136,20 +136,20 @@ class DistributionTestCase(support.LoggingSilencer, dist.finalize_options() # finalize_option splits platforms and keywords - self.assertEquals(dist.metadata.platforms, ['one', 'two']) - self.assertEquals(dist.metadata.keywords, ['one', 'two']) + self.assertEqual(dist.metadata.platforms, ['one', 'two']) + self.assertEqual(dist.metadata.keywords, ['one', 'two']) def test_get_command_packages(self): dist = Distribution() - self.assertEquals(dist.command_packages, None) + self.assertEqual(dist.command_packages, None) cmds = dist.get_command_packages() - self.assertEquals(cmds, ['distutils.command']) - self.assertEquals(dist.command_packages, - ['distutils.command']) + self.assertEqual(cmds, ['distutils.command']) + self.assertEqual(dist.command_packages, + ['distutils.command']) dist.command_packages = 'one,two' cmds = dist.get_command_packages() - self.assertEquals(cmds, ['distutils.command', 'one', 'two']) + self.assertEqual(cmds, ['distutils.command', 'one', 'two']) def test_announce(self): @@ -288,8 +288,8 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_fix_help_options(self): help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] fancy_options = fix_help_options(help_tuples) - self.assertEquals(fancy_options[0], ('a', 'b', 'c')) - self.assertEquals(fancy_options[1], (1, 2, 3)) + self.assertEqual(fancy_options[0], ('a', 'b', 'c')) + self.assertEqual(fancy_options[1], (1, 2, 3)) def test_show_help(self): # smoke test, just makes sure some help is displayed diff --git a/tests/test_extension.py b/tests/test_extension.py index 1ee30585..d9a47a89 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -28,38 +28,38 @@ class ExtensionTestCase(unittest.TestCase): 'rect', 'rwobject', 'scrap', 'surface', 'surflock', 'time', 'transform'] - self.assertEquals(names, wanted) + self.assertEqual(names, wanted) def test_extension_init(self): # the first argument, which is the name, must be a string self.assertRaises(AssertionError, Extension, 1, []) ext = Extension('name', []) - self.assertEquals(ext.name, 'name') + self.assertEqual(ext.name, 'name') # the second argument, which is the list of files, must # be a list of strings self.assertRaises(AssertionError, Extension, 'name', 'file') self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) ext = Extension('name', ['file1', 'file2']) - self.assertEquals(ext.sources, ['file1', 'file2']) + self.assertEqual(ext.sources, ['file1', 'file2']) # others arguments have defaults for attr in ('include_dirs', 'define_macros', 'undef_macros', 'library_dirs', 'libraries', 'runtime_library_dirs', 'extra_objects', 'extra_compile_args', 'extra_link_args', 'export_symbols', 'swig_opts', 'depends'): - self.assertEquals(getattr(ext, attr), []) + self.assertEqual(getattr(ext, attr), []) - self.assertEquals(ext.language, None) - self.assertEquals(ext.optional, None) + self.assertEqual(ext.language, None) + self.assertEqual(ext.optional, None) # if there are unknown keyword options, warn about them with check_warnings() as w: warnings.simplefilter('always') ext = Extension('name', ['file1', 'file2'], chic=True) - self.assertEquals(len(w.warnings), 1) - self.assertEquals(str(w.warnings[0].message), + self.assertEqual(len(w.warnings), 1) + self.assertEqual(str(w.warnings[0].message), "Unknown Extension options: 'chic'") def test_suite(): diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 74618b52..730ffde4 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -38,14 +38,14 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): move_file(self.source, self.target, verbose=0) wanted = [] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) # back to original state move_file(self.target, self.source, verbose=0) move_file(self.source, self.target, verbose=1) wanted = ['moving %s -> %s' % (self.source, self.target)] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) # back to original state move_file(self.target, self.source, verbose=0) @@ -55,7 +55,7 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): os.mkdir(self.target_dir) move_file(self.source, self.target_dir, verbose=1) wanted = ['moving %s -> %s' % (self.source, self.target_dir)] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) def test_suite(): diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 331180d2..d2a2b449 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -9,29 +9,29 @@ class FileListTestCase(unittest.TestCase): def test_glob_to_re(self): # simple cases - self.assertEquals(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)') - self.assertEquals(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)') - self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)') + self.assertEqual(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)') + self.assertEqual(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)') + self.assertEqual(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)') # special cases - self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)') - self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)') - self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') - self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') + self.assertEqual(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)') + self.assertEqual(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)') + self.assertEqual(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') + self.assertEqual(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') def test_debug_print(self): file_list = FileList() with captured_stdout() as stdout: file_list.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), '') + self.assertEqual(stdout.read(), '') debug.DEBUG = True try: with captured_stdout() as stdout: file_list.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), 'xxx\n') + self.assertEqual(stdout.read(), 'xxx\n') finally: debug.DEBUG = False diff --git a/tests/test_install.py b/tests/test_install.py index bc407cf9..f9c142e4 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -123,23 +123,23 @@ class InstallTestCase(support.TempdirManager, # two elements cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, ['path', 'dirs']) - self.assertEquals(cmd.extra_dirs, 'dirs') - self.assertEquals(cmd.path_file, 'path') + self.assertEqual(cmd.extra_path, ['path', 'dirs']) + self.assertEqual(cmd.extra_dirs, 'dirs') + self.assertEqual(cmd.path_file, 'path') # one element cmd.extra_path = ['path'] cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, ['path']) - self.assertEquals(cmd.extra_dirs, 'path') - self.assertEquals(cmd.path_file, 'path') + self.assertEqual(cmd.extra_path, ['path']) + self.assertEqual(cmd.extra_dirs, 'path') + self.assertEqual(cmd.path_file, 'path') # none dist.extra_path = cmd.extra_path = None cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, None) - self.assertEquals(cmd.extra_dirs, '') - self.assertEquals(cmd.path_file, None) + self.assertEqual(cmd.extra_path, None) + self.assertEqual(cmd.extra_dirs, '') + self.assertEqual(cmd.path_file, None) # three elements (no way !) cmd.extra_path = 'path,dirs,again' @@ -184,7 +184,7 @@ class InstallTestCase(support.TempdirManager, # line (the egg info file) f = open(cmd.record) try: - self.assertEquals(len(f.readlines()), 1) + self.assertEqual(len(f.readlines()), 1) finally: f.close() diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 377ae86e..86db4a12 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -27,14 +27,14 @@ class InstallDataTestCase(support.TempdirManager, self.write_file(two, 'xxx') cmd.data_files = [one, (inst2, [two])] - self.assertEquals(cmd.get_inputs(), [one, (inst2, [two])]) + self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])]) # let's run the command cmd.ensure_finalized() cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) rtwo = os.path.split(two)[-1] self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) rone = os.path.split(one)[-1] @@ -47,7 +47,7 @@ class InstallDataTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] @@ -65,7 +65,7 @@ class InstallDataTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 4) + self.assertEqual(len(cmd.get_outputs()), 4) self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index 5b32b13e..aa8a4e60 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -23,7 +23,7 @@ class InstallHeadersTestCase(support.TempdirManager, pkg_dir, dist = self.create_dist(headers=headers) cmd = install_headers(dist) - self.assertEquals(cmd.get_inputs(), headers) + self.assertEqual(cmd.get_inputs(), headers) # let's run the command cmd.install_dir = os.path.join(pkg_dir, 'inst') @@ -31,7 +31,7 @@ class InstallHeadersTestCase(support.TempdirManager, cmd.run() # let's check the results - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) def test_suite(): return unittest.makeSuite(InstallHeadersTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 99a6d906..46363046 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -18,8 +18,8 @@ class InstallLibTestCase(support.TempdirManager, cmd = install_lib(dist) cmd.finalize_options() - self.assertEquals(cmd.compile, 1) - self.assertEquals(cmd.optimize, 0) + self.assertEqual(cmd.compile, 1) + self.assertEqual(cmd.optimize, 0) # optimize must be 0, 1, or 2 cmd.optimize = 'foo' @@ -29,7 +29,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.optimize = '2' cmd.finalize_options() - self.assertEquals(cmd.optimize, 2) + self.assertEqual(cmd.optimize, 2) @unittest.skipUnless(not sys.dont_write_bytecode, 'byte-compile not supported') @@ -76,7 +76,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.distribution.script_name = 'setup.py' # get_input should return 2 elements - self.assertEquals(len(cmd.get_inputs()), 2) + self.assertEqual(len(cmd.get_inputs()), 2) def test_dont_write_bytecode(self): # makes sure byte_compile is not used diff --git a/tests/test_log.py b/tests/test_log.py index d35de345..9d40dcd7 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -22,9 +22,9 @@ class TestLog(unittest.TestCase): log.debug("debug:\xe9") log.fatal("fatal:\xe9") stdout.seek(0) - self.assertEquals(stdout.read().rstrip(), "debug:\\xe9") + self.assertEqual(stdout.read().rstrip(), "debug:\\xe9") stderr.seek(0) - self.assertEquals(stderr.read().rstrip(), "fatal:\\xe9") + self.assertEqual(stderr.read().rstrip(), "fatal:\\xe9") finally: sys.stdout = old_stdout sys.stderr = old_stderr diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 40cb8be6..570fda12 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -103,7 +103,7 @@ class msvc9compilerTestCase(support.TempdirManager, import winreg HKCU = winreg.HKEY_CURRENT_USER keys = Reg.read_keys(HKCU, 'xxxx') - self.assertEquals(keys, None) + self.assertEqual(keys, None) keys = Reg.read_keys(HKCU, r'Control Panel') self.assertTrue('Desktop' in keys) @@ -130,7 +130,7 @@ class msvc9compilerTestCase(support.TempdirManager, f.close() # makes sure the manifest was properly cleaned - self.assertEquals(content, _CLEANED_MANIFEST) + self.assertEqual(content, _CLEANED_MANIFEST) def test_suite(): diff --git a/tests/test_register.py b/tests/test_register.py index 3b80b6dc..c98e28a1 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -121,7 +121,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): f = open(self.rc) try: content = f.read() - self.assertEquals(content, WANTED_PYPIRC) + self.assertEqual(content, WANTED_PYPIRC) finally: f.close() @@ -141,8 +141,8 @@ class RegisterTestCase(PyPIRCCommandTestCase): req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) - self.assertEquals(req1['Content-length'], '1374') - self.assertEquals(req2['Content-length'], '1374') + self.assertEqual(req1['Content-length'], '1374') + self.assertEqual(req2['Content-length'], '1374') self.assertTrue((b'xxx') in self.conn.reqs[1].data) def test_password_not_in_file(self): @@ -155,7 +155,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): # dist.password should be set # therefore used afterwards by other commands - self.assertEquals(cmd.distribution.password, 'password') + self.assertEqual(cmd.distribution.password, 'password') def test_registering(self): # this test runs choice 2 @@ -172,7 +172,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) - self.assertEquals(headers['Content-length'], '608') + self.assertEqual(headers['Content-length'], '608') self.assertTrue((b'tarek') in req.data) def test_password_reset(self): @@ -190,7 +190,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) - self.assertEquals(headers['Content-length'], '290') + self.assertEqual(headers['Content-length'], '290') self.assertTrue((b'tarek') in req.data) def test_strict(self): @@ -253,7 +253,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): with check_warnings() as w: warnings.simplefilter("always") cmd.check_metadata() - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) def test_suite(): return unittest.makeSuite(RegisterTestCase) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index ad527c7d..d734ea0a 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -110,7 +110,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # now let's check what we have dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) - self.assertEquals(files, ['fake-1.0.zip']) + self.assertEqual(files, ['fake-1.0.zip']) zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) try: @@ -119,7 +119,7 @@ class SDistTestCase(PyPIRCCommandTestCase): zip_file.close() # making sure everything has been pruned correctly - self.assertEquals(len(content), 4) + self.assertEqual(len(content), 4) def test_make_distribution(self): @@ -140,8 +140,7 @@ class SDistTestCase(PyPIRCCommandTestCase): dist_folder = join(self.tmp_dir, 'dist') result = os.listdir(dist_folder) result.sort() - self.assertEquals(result, - ['fake-1.0.tar', 'fake-1.0.tar.gz'] ) + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'] ) os.remove(join(dist_folder, 'fake-1.0.tar')) os.remove(join(dist_folder, 'fake-1.0.tar.gz')) @@ -154,8 +153,7 @@ class SDistTestCase(PyPIRCCommandTestCase): result = os.listdir(dist_folder) result.sort() - self.assertEquals(result, - ['fake-1.0.tar', 'fake-1.0.tar.gz']) + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) def test_add_defaults(self): @@ -203,7 +201,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # now let's check what we have dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) - self.assertEquals(files, ['fake-1.0.zip']) + self.assertEqual(files, ['fake-1.0.zip']) zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) try: @@ -212,13 +210,13 @@ class SDistTestCase(PyPIRCCommandTestCase): zip_file.close() # making sure everything was added - self.assertEquals(len(content), 11) + self.assertEqual(len(content), 11) # checking the MANIFEST f = open(join(self.tmp_dir, 'MANIFEST')) try: manifest = f.read() - self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + self.assertEqual(manifest, MANIFEST % {'sep': os.sep}) finally: f.close() @@ -231,7 +229,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.ensure_finalized() cmd.run() warnings = self.get_logs(WARN) - self.assertEquals(len(warnings), 2) + self.assertEqual(len(warnings), 2) # trying with a complete set of metadata self.clear_logs() @@ -240,7 +238,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.metadata_check = 0 cmd.run() warnings = self.get_logs(WARN) - self.assertEquals(len(warnings), 0) + self.assertEqual(len(warnings), 0) def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated @@ -248,7 +246,7 @@ class SDistTestCase(PyPIRCCommandTestCase): with check_warnings() as w: warnings.simplefilter("always") cmd.check_metadata() - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) def test_show_formats(self): with captured_stdout() as stdout: @@ -258,7 +256,7 @@ class SDistTestCase(PyPIRCCommandTestCase): num_formats = len(ARCHIVE_FORMATS.keys()) output = [line for line in stdout.getvalue().split('\n') if line.strip().startswith('--formats=')] - self.assertEquals(len(output), num_formats) + self.assertEqual(len(output), num_formats) def test_finalize_options(self): @@ -266,9 +264,9 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.finalize_options() # default options set by finalize - self.assertEquals(cmd.manifest, 'MANIFEST') - self.assertEquals(cmd.template, 'MANIFEST.in') - self.assertEquals(cmd.dist_dir, 'dist') + self.assertEqual(cmd.manifest, 'MANIFEST') + self.assertEqual(cmd.template, 'MANIFEST.in') + self.assertEqual(cmd.dist_dir, 'dist') # formats has to be a string splitable on (' ', ',') or # a stringlist @@ -299,7 +297,7 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: f.close() - self.assertEquals(len(manifest), 5) + self.assertEqual(len(manifest), 5) # adding a file self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') @@ -319,7 +317,7 @@ class SDistTestCase(PyPIRCCommandTestCase): f.close() # do we have the new file in MANIFEST ? - self.assertEquals(len(manifest2), 6) + self.assertEqual(len(manifest2), 6) self.assertIn('doc2.txt', manifest2[-1]) def test_manifest_marker(self): diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 950e5789..0616c9f2 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -20,7 +20,7 @@ class SpawnTestCase(support.TempdirManager, (['nochange', 'nospace'], ['nochange', 'nospace'])): res = _nt_quote_args(args) - self.assertEquals(res, wanted) + self.assertEqual(res, wanted) @unittest.skipUnless(os.name in ('nt', 'posix'), diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 309be7b8..41414bb5 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -71,7 +71,7 @@ class SysconfigTestCase(support.EnvironGuard, comp = compiler() sysconfig.customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') + self.assertEqual(comp.exes['archiver'], 'my_ar -arflags') def test_parse_makefile_base(self): self.makefile = TESTFN @@ -82,7 +82,7 @@ class SysconfigTestCase(support.EnvironGuard, finally: fd.close() d = sysconfig.parse_makefile(self.makefile) - self.assertEquals(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", + self.assertEqual(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", 'OTHER': 'foo'}) def test_parse_makefile_literal_dollar(self): @@ -94,8 +94,8 @@ class SysconfigTestCase(support.EnvironGuard, finally: fd.close() d = sysconfig.parse_makefile(self.makefile) - self.assertEquals(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", - 'OTHER': 'foo'}) + self.assertEqual(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", + 'OTHER': 'foo'}) def test_suite(): diff --git a/tests/test_text_file.py b/tests/test_text_file.py index 3093097d..f1e32b6c 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -48,7 +48,7 @@ class TextFileTestCase(support.TempdirManager, unittest.TestCase): def test_input(count, description, file, expected_result): result = file.readlines() - self.assertEquals(result, expected_result) + self.assertEqual(result, expected_result) tmpdir = self.mkdtemp() filename = os.path.join(tmpdir, "test.txt") diff --git a/tests/test_upload.py b/tests/test_upload.py index 35e97005..8891820d 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -90,7 +90,7 @@ class uploadTestCase(PyPIRCCommandTestCase): for attr, waited in (('username', 'me'), ('password', 'secret'), ('realm', 'pypi'), ('repository', 'http://pypi.python.org/pypi')): - self.assertEquals(getattr(cmd, attr), waited) + self.assertEqual(getattr(cmd, attr), waited) def test_saved_password(self): # file with no password @@ -100,14 +100,14 @@ class uploadTestCase(PyPIRCCommandTestCase): dist = Distribution() cmd = upload(dist) cmd.finalize_options() - self.assertEquals(cmd.password, None) + self.assertEqual(cmd.password, None) # make sure we get it as well, if another command # initialized it at the dist level dist.password = 'xxx' cmd = upload(dist) cmd.finalize_options() - self.assertEquals(cmd.password, 'xxx') + self.assertEqual(cmd.password, 'xxx') def test_upload(self): tmp = self.mkdtemp() @@ -125,12 +125,12 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.conn.headers) - self.assertEquals(headers['Content-length'], '2087') + self.assertEqual(headers['Content-length'], '2087') self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) self.assertFalse('\n' in headers['Authorization']) - self.assertEquals(self.conn.requests, [('POST', '/pypi')]) - self.assert_((b'xxx') in self.conn.body) + self.assertEqual(self.conn.requests, [('POST', '/pypi')]) + self.assertTrue((b'xxx') in self.conn.body) def test_suite(): return unittest.makeSuite(uploadTestCase) diff --git a/tests/test_util.py b/tests/test_util.py index 0c732f82..f40caac8 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -66,21 +66,21 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Intel)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win32') + self.assertEqual(get_platform(), 'win32') # windows XP, amd64 os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Amd64)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-amd64') + self.assertEqual(get_platform(), 'win-amd64') # windows XP, itanium os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Itanium)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-ia64') + self.assertEqual(get_platform(), 'win-ia64') # macbook os.name = 'posix' @@ -99,7 +99,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): cursize = sys.maxsize sys.maxsize = (2 ** 31)-1 try: - self.assertEquals(get_platform(), 'macosx-10.3-i386') + self.assertEqual(get_platform(), 'macosx-10.3-i386') finally: sys.maxsize = cursize @@ -110,33 +110,33 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat') + self.assertEqual(get_platform(), 'macosx-10.4-fat') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-intel') + self.assertEqual(get_platform(), 'macosx-10.4-intel') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat3') + self.assertEqual(get_platform(), 'macosx-10.4-fat3') get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-universal') + self.assertEqual(get_platform(), 'macosx-10.4-universal') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat64') + self.assertEqual(get_platform(), 'macosx-10.4-fat64') for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' @@ -144,7 +144,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3'%(arch,)) - self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) + self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,)) # linux debian sarge os.name = 'posix' @@ -154,7 +154,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) - self.assertEquals(get_platform(), 'linux-i686') + self.assertEqual(get_platform(), 'linux-i686') # XXX more platforms to tests here @@ -165,8 +165,8 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): return '/'.join(path) os.path.join = _join - self.assertEquals(convert_path('/home/to/my/stuff'), - '/home/to/my/stuff') + self.assertEqual(convert_path('/home/to/my/stuff'), + '/home/to/my/stuff') # win os.sep = '\\' @@ -177,10 +177,10 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') - self.assertEquals(convert_path('home/to/my/stuff'), - 'home\\to\\my\\stuff') - self.assertEquals(convert_path('.'), - os.curdir) + self.assertEqual(convert_path('home/to/my/stuff'), + 'home\\to\\my\\stuff') + self.assertEqual(convert_path('.'), + os.curdir) def test_change_root(self): # linux/mac @@ -192,10 +192,10 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): return '/'.join(path) os.path.join = _join - self.assertEquals(change_root('/root', '/old/its/here'), - '/root/old/its/here') - self.assertEquals(change_root('/root', 'its/here'), - '/root/its/here') + self.assertEqual(change_root('/root', '/old/its/here'), + '/root/old/its/here') + self.assertEqual(change_root('/root', 'its/here'), + '/root/its/here') # windows os.name = 'nt' @@ -211,10 +211,10 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): return '\\'.join(path) os.path.join = _join - self.assertEquals(change_root('c:\\root', 'c:\\old\\its\\here'), - 'c:\\root\\old\\its\\here') - self.assertEquals(change_root('c:\\root', 'its\\here'), - 'c:\\root\\its\\here') + self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'), + 'c:\\root\\old\\its\\here') + self.assertEqual(change_root('c:\\root', 'its\\here'), + 'c:\\root\\its\\here') # BugsBunny os (it's a great os) os.name = 'BugsBunny' @@ -232,16 +232,16 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): if os.name == 'posix': # this test won't run on windows check_environ() import pwd - self.assertEquals(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) + self.assertEqual(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) else: check_environ() - self.assertEquals(os.environ['PLAT'], get_platform()) - self.assertEquals(util._environ_checked, 1) + self.assertEqual(os.environ['PLAT'], get_platform()) + self.assertEqual(util._environ_checked, 1) def test_split_quoted(self): - self.assertEquals(split_quoted('""one"" "two" \'three\' \\four'), - ['one', 'two', 'three', 'four']) + self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), + ['one', 'two', 'three', 'four']) def test_strtobool(self): yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') @@ -258,7 +258,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): res = rfc822_escape(header) wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' 'header%(8s)s') % {'8s': '\n'+8*' '} - self.assertEquals(res, wanted) + self.assertEqual(res, wanted) def test_dont_write_bytecode(self): # makes sure byte_compile raise a DistutilsError diff --git a/tests/test_version.py b/tests/test_version.py index fa214330..980f83f2 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -7,12 +7,12 @@ class VersionTestCase(unittest.TestCase): def test_prerelease(self): version = StrictVersion('1.2.3a1') - self.assertEquals(version.version, (1, 2, 3)) - self.assertEquals(version.prerelease, ('a', 1)) - self.assertEquals(str(version), '1.2.3a1') + self.assertEqual(version.version, (1, 2, 3)) + self.assertEqual(version.prerelease, ('a', 1)) + self.assertEqual(str(version), '1.2.3a1') version = StrictVersion('1.2.0') - self.assertEquals(str(version), '1.2') + self.assertEqual(str(version), '1.2') def test_cmp_strict(self): versions = (('1.5.1', '1.5.2b2', -1), @@ -41,9 +41,9 @@ class VersionTestCase(unittest.TestCase): raise AssertionError(("cmp(%s, %s) " "shouldn't raise ValueError") % (v1, v2)) - self.assertEquals(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) def test_cmp(self): @@ -59,9 +59,9 @@ class VersionTestCase(unittest.TestCase): for v1, v2, wanted in versions: res = LooseVersion(v1)._cmp(LooseVersion(v2)) - self.assertEquals(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) def test_suite(): return unittest.makeSuite(VersionTestCase) -- cgit v1.2.1 From d82693d941869773f3654c04da63d586c5ad1a2c Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sun, 21 Nov 2010 13:34:58 +0000 Subject: Merged revisions 86596 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86596 | ezio.melotti | 2010-11-20 21:04:17 +0200 (Sat, 20 Nov 2010) | 1 line #9424: Replace deprecated assert* methods in the Python test suite. ........ --- tests/test_archive_util.py | 18 ++++----- tests/test_bdist.py | 4 +- tests/test_bdist_dumb.py | 8 ++-- tests/test_build.py | 14 +++---- tests/test_build_clib.py | 10 ++--- tests/test_build_ext.py | 86 +++++++++++++++++++++---------------------- tests/test_ccompiler.py | 8 ++-- tests/test_check.py | 14 +++---- tests/test_cmd.py | 10 ++--- tests/test_config.py | 6 +-- tests/test_config_cmd.py | 12 +++--- tests/test_core.py | 4 +- tests/test_dep_util.py | 4 +- tests/test_dir_util.py | 22 +++++------ tests/test_dist.py | 42 ++++++++++----------- tests/test_file_util.py | 8 ++-- tests/test_filelist.py | 20 +++++----- tests/test_install_data.py | 8 ++-- tests/test_install_headers.py | 4 +- tests/test_install_lib.py | 8 ++-- tests/test_msvc9compiler.py | 4 +- tests/test_register.py | 12 +++--- tests/test_sdist.py | 40 ++++++++++---------- tests/test_spawn.py | 2 +- tests/test_sysconfig.py | 10 ++--- tests/test_text_file.py | 2 +- tests/test_upload.py | 14 +++---- tests/test_version.py | 20 +++++----- 28 files changed, 206 insertions(+), 208 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index a9b46d8e..bab91577 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -129,7 +129,7 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertTrue(os.path.exists(tarball2)) # let's compare both tarballs - self.assertEquals(self._tarinfo(tarball), self._tarinfo(tarball2)) + self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2)) # trying an uncompressed one base_name = os.path.join(tmpdir2, 'archive') @@ -169,7 +169,7 @@ class ArchiveUtilTestCase(support.TempdirManager, os.chdir(old_dir) tarball = base_name + '.tar.Z' self.assertTrue(os.path.exists(tarball)) - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) # same test with dry_run os.remove(tarball) @@ -183,7 +183,7 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: os.chdir(old_dir) self.assertTrue(not os.path.exists(tarball)) - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) @unittest.skipUnless(zlib, "Requires zlib") @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') @@ -201,9 +201,9 @@ class ArchiveUtilTestCase(support.TempdirManager, tarball = base_name + '.zip' def test_check_archive_formats(self): - self.assertEquals(check_archive_formats(['gztar', 'xxx', 'zip']), - 'xxx') - self.assertEquals(check_archive_formats(['gztar', 'zip']), None) + self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), + 'xxx') + self.assertEqual(check_archive_formats(['gztar', 'zip']), None) def test_make_archive(self): tmpdir = self.mkdtemp() @@ -258,8 +258,8 @@ class ArchiveUtilTestCase(support.TempdirManager, archive = tarfile.open(archive_name) try: for member in archive.getmembers(): - self.assertEquals(member.uid, 0) - self.assertEquals(member.gid, 0) + self.assertEqual(member.uid, 0) + self.assertEqual(member.gid, 0) finally: archive.close() @@ -273,7 +273,7 @@ class ArchiveUtilTestCase(support.TempdirManager, make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) except: pass - self.assertEquals(os.getcwd(), current_dir) + self.assertEqual(os.getcwd(), current_dir) finally: del ARCHIVE_FORMATS['xxx'] diff --git a/tests/test_bdist.py b/tests/test_bdist.py index a37f4a9b..fa7cd5ad 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -25,7 +25,7 @@ class BuildTestCase(support.TempdirManager, cmd = bdist(dist) cmd.formats = ['msi'] cmd.ensure_finalized() - self.assertEquals(cmd.formats, ['msi']) + self.assertEqual(cmd.formats, ['msi']) # what format bdist offers ? # XXX an explicit list in bdist is @@ -36,7 +36,7 @@ class BuildTestCase(support.TempdirManager, formats.sort() founded = cmd.format_command.keys() founded.sort() - self.assertEquals(founded, formats) + self.assertEqual(founded, formats) def test_suite(): return unittest.makeSuite(BuildTestCase) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index f2220f47..5a22a10e 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -78,7 +78,7 @@ class BuildDumbTestCase(support.TempdirManager, base = base.replace(':', '-') wanted = ['%s.zip' % base] - self.assertEquals(dist_created, wanted) + self.assertEqual(dist_created, wanted) # now let's check what we have in the zip file # XXX to be done @@ -87,16 +87,16 @@ class BuildDumbTestCase(support.TempdirManager, pkg_dir, dist = self.create_dist() os.chdir(pkg_dir) cmd = bdist_dumb(dist) - self.assertEquals(cmd.bdist_dir, None) + self.assertEqual(cmd.bdist_dir, None) cmd.finalize_options() # bdist_dir is initialized to bdist_base/dumb if not set base = cmd.get_finalized_command('bdist').bdist_base - self.assertEquals(cmd.bdist_dir, os.path.join(base, 'dumb')) + self.assertEqual(cmd.bdist_dir, os.path.join(base, 'dumb')) # the format is set to a default value depending on the os.name default = cmd.default_format[os.name] - self.assertEquals(cmd.format, default) + self.assertEqual(cmd.format, default) def test_suite(): return unittest.makeSuite(BuildDumbTestCase) diff --git a/tests/test_build.py b/tests/test_build.py index 2418e165..3db57038 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -17,11 +17,11 @@ class BuildTestCase(support.TempdirManager, cmd.finalize_options() # if not specified, plat_name gets the current platform - self.assertEquals(cmd.plat_name, get_platform()) + self.assertEqual(cmd.plat_name, get_platform()) # build_purelib is build + lib wanted = os.path.join(cmd.build_base, 'lib') - self.assertEquals(cmd.build_purelib, wanted) + self.assertEqual(cmd.build_purelib, wanted) # build_platlib is 'build/lib.platform-x.x[-pydebug]' # examples: @@ -31,21 +31,21 @@ class BuildTestCase(support.TempdirManager, self.assertTrue(cmd.build_platlib.endswith('-pydebug')) plat_spec += '-pydebug' wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) - self.assertEquals(cmd.build_platlib, wanted) + self.assertEqual(cmd.build_platlib, wanted) # by default, build_lib = build_purelib - self.assertEquals(cmd.build_lib, cmd.build_purelib) + self.assertEqual(cmd.build_lib, cmd.build_purelib) # build_temp is build/temp. wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) - self.assertEquals(cmd.build_temp, wanted) + self.assertEqual(cmd.build_temp, wanted) # build_scripts is build/scripts-x.x wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3]) - self.assertEquals(cmd.build_scripts, wanted) + self.assertEqual(cmd.build_scripts, wanted) # executable is os.path.normpath(sys.executable) - self.assertEquals(cmd.executable, os.path.normpath(sys.executable)) + self.assertEqual(cmd.executable, os.path.normpath(sys.executable)) def test_suite(): return unittest.makeSuite(BuildTestCase) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 145eff54..d77912ae 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -55,14 +55,14 @@ class BuildCLibTestCase(support.TempdirManager, self.assertRaises(DistutilsSetupError, cmd.get_source_files) cmd.libraries = [('name', {'sources': ['a', 'b']})] - self.assertEquals(cmd.get_source_files(), ['a', 'b']) + self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')})] - self.assertEquals(cmd.get_source_files(), ['a', 'b']) + self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')}), ('name2', {'sources': ['c', 'd']})] - self.assertEquals(cmd.get_source_files(), ['a', 'b', 'c', 'd']) + self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd']) def test_build_libraries(self): @@ -91,11 +91,11 @@ class BuildCLibTestCase(support.TempdirManager, cmd.include_dirs = 'one-dir' cmd.finalize_options() - self.assertEquals(cmd.include_dirs, ['one-dir']) + self.assertEqual(cmd.include_dirs, ['one-dir']) cmd.include_dirs = None cmd.finalize_options() - self.assertEquals(cmd.include_dirs, []) + self.assertEqual(cmd.include_dirs, []) cmd.distribution.libraries = 'WONTWORK' self.assertRaises(DistutilsSetupError, cmd.finalize_options) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index d14c5f6b..86568ebb 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -103,15 +103,15 @@ class BuildExtTestCase(support.TempdirManager, import xx for attr in ('error', 'foo', 'new', 'roj'): - self.assert_(hasattr(xx, attr)) + self.assertTrue(hasattr(xx, attr)) - self.assertEquals(xx.foo(2, 5), 7) - self.assertEquals(xx.foo(13,15), 28) - self.assertEquals(xx.new().demo(), None) + self.assertEqual(xx.foo(2, 5), 7) + self.assertEqual(xx.foo(13,15), 28) + self.assertEqual(xx.new().demo(), None) doc = 'This is a template module just for instruction.' - self.assertEquals(xx.__doc__, doc) - self.assert_(isinstance(xx.Null(), xx.Null)) - self.assert_(isinstance(xx.Str(), xx.Str)) + self.assertEqual(xx.__doc__, doc) + self.assertTrue(isinstance(xx.Null(), xx.Null)) + self.assertTrue(isinstance(xx.Str(), xx.Str)) def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) @@ -132,7 +132,7 @@ class BuildExtTestCase(support.TempdirManager, _config_vars['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris - self.assert_(len(cmd.library_dirs) > 0) + self.assertTrue(len(cmd.library_dirs) > 0) def test_finalize_options(self): # Make sure Python's include directories (for Python.h, pyconfig.h, @@ -144,31 +144,31 @@ class BuildExtTestCase(support.TempdirManager, from distutils import sysconfig py_include = sysconfig.get_python_inc() - self.assert_(py_include in cmd.include_dirs) + self.assertTrue(py_include in cmd.include_dirs) plat_py_include = sysconfig.get_python_inc(plat_specific=1) - self.assert_(plat_py_include in cmd.include_dirs) + self.assertTrue(plat_py_include in cmd.include_dirs) # make sure cmd.libraries is turned into a list # if it's a string cmd = build_ext(dist) cmd.libraries = 'my_lib' cmd.finalize_options() - self.assertEquals(cmd.libraries, ['my_lib']) + self.assertEqual(cmd.libraries, ['my_lib']) # make sure cmd.library_dirs is turned into a list # if it's a string cmd = build_ext(dist) cmd.library_dirs = 'my_lib_dir' cmd.finalize_options() - self.assert_('my_lib_dir' in cmd.library_dirs) + self.assertTrue('my_lib_dir' in cmd.library_dirs) # make sure rpath is turned into a list # if it's a list of os.pathsep's paths cmd = build_ext(dist) cmd.rpath = os.pathsep.join(['one', 'two']) cmd.finalize_options() - self.assertEquals(cmd.rpath, ['one', 'two']) + self.assertEqual(cmd.rpath, ['one', 'two']) # XXX more tests to perform for win32 @@ -177,25 +177,25 @@ class BuildExtTestCase(support.TempdirManager, cmd = build_ext(dist) cmd.define = 'one,two' cmd.finalize_options() - self.assertEquals(cmd.define, [('one', '1'), ('two', '1')]) + self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) # make sure undef is turned into a list of # strings if they are ','-separated strings cmd = build_ext(dist) cmd.undef = 'one,two' cmd.finalize_options() - self.assertEquals(cmd.undef, ['one', 'two']) + self.assertEqual(cmd.undef, ['one', 'two']) # make sure swig_opts is turned into a list cmd = build_ext(dist) cmd.swig_opts = None cmd.finalize_options() - self.assertEquals(cmd.swig_opts, []) + self.assertEqual(cmd.swig_opts, []) cmd = build_ext(dist) cmd.swig_opts = '1 2' cmd.finalize_options() - self.assertEquals(cmd.swig_opts, ['1', '2']) + self.assertEqual(cmd.swig_opts, ['1', '2']) def test_check_extensions_list(self): dist = Distribution() @@ -226,13 +226,13 @@ class BuildExtTestCase(support.TempdirManager, 'some': 'bar'})] cmd.check_extensions_list(exts) ext = exts[0] - self.assert_(isinstance(ext, Extension)) + self.assertTrue(isinstance(ext, Extension)) # check_extensions_list adds in ext the values passed # when they are in ('include_dirs', 'library_dirs', 'libraries' # 'extra_objects', 'extra_compile_args', 'extra_link_args') - self.assertEquals(ext.libraries, 'foo') - self.assert_(not hasattr(ext, 'some')) + self.assertEqual(ext.libraries, 'foo') + self.assertTrue(not hasattr(ext, 'some')) # 'macros' element of build info dict must be 1- or 2-tuple exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', @@ -241,15 +241,15 @@ class BuildExtTestCase(support.TempdirManager, exts[0][1]['macros'] = [('1', '2'), ('3',)] cmd.check_extensions_list(exts) - self.assertEquals(exts[0].undef_macros, ['3']) - self.assertEquals(exts[0].define_macros, [('1', '2')]) + self.assertEqual(exts[0].undef_macros, ['3']) + self.assertEqual(exts[0].define_macros, [('1', '2')]) def test_get_source_files(self): modules = [Extension('foo', ['xxx'])] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = build_ext(dist) cmd.ensure_finalized() - self.assertEquals(cmd.get_source_files(), ['xxx']) + self.assertEqual(cmd.get_source_files(), ['xxx']) def test_compiler_option(self): # cmd.compiler is an option and @@ -260,7 +260,7 @@ class BuildExtTestCase(support.TempdirManager, cmd.compiler = 'unix' cmd.ensure_finalized() cmd.run() - self.assertEquals(cmd.compiler, 'unix') + self.assertEqual(cmd.compiler, 'unix') def test_get_outputs(self): tmp_dir = self.mkdtemp() @@ -272,7 +272,7 @@ class BuildExtTestCase(support.TempdirManager, cmd = build_ext(dist) self._fixup_command(cmd) cmd.ensure_finalized() - self.assertEquals(len(cmd.get_outputs()), 1) + self.assertEqual(len(cmd.get_outputs()), 1) if os.name == "nt": cmd.debug = sys.executable.endswith("_d.exe") @@ -291,20 +291,20 @@ class BuildExtTestCase(support.TempdirManager, so_file = cmd.get_outputs()[0] finally: os.chdir(old_wd) - self.assert_(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], - sysconfig.get_config_var('SO')) + self.assertTrue(os.path.exists(so_file)) + self.assertEqual(os.path.splitext(so_file)[-1], + sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, other_tmp_dir) + self.assertEqual(so_dir, other_tmp_dir) cmd.compiler = None cmd.inplace = 0 cmd.run() so_file = cmd.get_outputs()[0] - self.assert_(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], - sysconfig.get_config_var('SO')) + self.assertTrue(os.path.exists(so_file)) + self.assertEqual(os.path.splitext(so_file)[-1], + sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, cmd.build_lib) + self.assertEqual(so_dir, cmd.build_lib) # inplace = 0, cmd.package = 'bar' build_py = cmd.get_finalized_command('build_py') @@ -312,7 +312,7 @@ class BuildExtTestCase(support.TempdirManager, path = cmd.get_ext_fullpath('foo') # checking that the last directory is the build_dir path = os.path.split(path)[0] - self.assertEquals(path, cmd.build_lib) + self.assertEqual(path, cmd.build_lib) # inplace = 1, cmd.package = 'bar' cmd.inplace = 1 @@ -326,7 +326,7 @@ class BuildExtTestCase(support.TempdirManager, # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, 'bar') + self.assertEqual(lastdir, 'bar') def test_ext_fullpath(self): ext = sysconfig.get_config_vars()['SO'] @@ -338,14 +338,14 @@ class BuildExtTestCase(support.TempdirManager, curdir = os.getcwd() wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building lxml.etree not inplace cmd.inplace = 0 cmd.build_lib = os.path.join(curdir, 'tmpdir') wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building twisted.runner.portmap not inplace build_py = cmd.get_finalized_command('build_py') @@ -354,13 +354,13 @@ class BuildExtTestCase(support.TempdirManager, path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', 'portmap' + ext) - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building twisted.runner.portmap inplace cmd.inplace = 1 path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) def test_build_ext_inplace(self): etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') @@ -375,7 +375,7 @@ class BuildExtTestCase(support.TempdirManager, ext = sysconfig.get_config_var("SO") wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) def test_setuptools_compat(self): import distutils.core, distutils.extension, distutils.command.build_ext @@ -400,7 +400,7 @@ class BuildExtTestCase(support.TempdirManager, ext = sysconfig.get_config_var("SO") wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) finally: # restoring Distutils' Extension class otherwise its broken distutils.extension.Extension = saved_ext @@ -415,7 +415,7 @@ class BuildExtTestCase(support.TempdirManager, ext_name = os.path.join('UpdateManager', 'fdsend') ext_path = cmd.get_ext_fullpath(ext_name) wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext) - self.assertEquals(ext_path, wanted) + self.assertEqual(ext_path, wanted) def test_build_ext_path_cross_platform(self): if sys.platform != 'win32': @@ -428,7 +428,7 @@ class BuildExtTestCase(support.TempdirManager, ext_name = 'UpdateManager/fdsend' ext_path = cmd.get_ext_fullpath(ext_name) wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext) - self.assertEquals(ext_path, wanted) + self.assertEqual(ext_path, wanted) def test_suite(): return unittest.makeSuite(BuildExtTestCase) diff --git a/tests/test_ccompiler.py b/tests/test_ccompiler.py index 317e77fd..e21873e8 100644 --- a/tests/test_ccompiler.py +++ b/tests/test_ccompiler.py @@ -32,7 +32,7 @@ class CCompilerTestCase(support.EnvironGuard, unittest.TestCase): opts = gen_lib_options(compiler, libdirs, runlibdirs, libs) wanted = ['-Llib1', '-Llib2', '-cool', '-Rrunlib1', 'found', '-lname2'] - self.assertEquals(opts, wanted) + self.assertEqual(opts, wanted) def test_debug_print(self): @@ -43,14 +43,14 @@ class CCompilerTestCase(support.EnvironGuard, unittest.TestCase): with captured_stdout() as stdout: compiler.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), '') + self.assertEqual(stdout.read(), '') debug.DEBUG = True try: with captured_stdout() as stdout: compiler.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), 'xxx\n') + self.assertEqual(stdout.read(), 'xxx\n') finally: debug.DEBUG = False @@ -72,7 +72,7 @@ class CCompilerTestCase(support.EnvironGuard, unittest.TestCase): comp = compiler() customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') + self.assertEqual(comp.exes['archiver'], 'my_ar -arflags') def test_suite(): return unittest.makeSuite(CCompilerTestCase) diff --git a/tests/test_check.py b/tests/test_check.py index 372bae36..7c56c043 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -26,7 +26,7 @@ class CheckTestCase(support.LoggingSilencer, # by default, check is checking the metadata # should have some warnings cmd = self._run() - self.assertEquals(cmd._warnings, 2) + self.assertEqual(cmd._warnings, 2) # now let's add the required fields # and run it again, to make sure we don't get @@ -35,7 +35,7 @@ class CheckTestCase(support.LoggingSilencer, 'author_email': 'xxx', 'name': 'xxx', 'version': 'xxx'} cmd = self._run(metadata) - self.assertEquals(cmd._warnings, 0) + self.assertEqual(cmd._warnings, 0) # now with the strict mode, we should # get an error if there are missing metadata @@ -43,7 +43,7 @@ class CheckTestCase(support.LoggingSilencer, # and of course, no error when all metadata are present cmd = self._run(metadata, strict=1) - self.assertEquals(cmd._warnings, 0) + self.assertEqual(cmd._warnings, 0) def test_check_document(self): if not HAS_DOCUTILS: # won't test without docutils @@ -54,12 +54,12 @@ class CheckTestCase(support.LoggingSilencer, # let's see if it detects broken rest broken_rest = 'title\n===\n\ntest' msgs = cmd._check_rst_data(broken_rest) - self.assertEquals(len(msgs), 1) + self.assertEqual(len(msgs), 1) # and non-broken rest rest = 'title\n=====\n\ntest' msgs = cmd._check_rst_data(rest) - self.assertEquals(len(msgs), 0) + self.assertEqual(len(msgs), 0) def test_check_restructuredtext(self): if not HAS_DOCUTILS: # won't test without docutils @@ -69,7 +69,7 @@ class CheckTestCase(support.LoggingSilencer, pkg_info, dist = self.create_dist(long_description=broken_rest) cmd = check(dist) cmd.check_restructuredtext() - self.assertEquals(cmd._warnings, 1) + self.assertEqual(cmd._warnings, 1) # let's see if we have an error with strict=1 metadata = {'url': 'xxx', 'author': 'xxx', @@ -82,7 +82,7 @@ class CheckTestCase(support.LoggingSilencer, # and non-broken rest metadata['long_description'] = 'title\n=====\n\ntest' cmd = self._run(metadata, strict=1, restructuredtext=1) - self.assertEquals(cmd._warnings, 0) + self.assertEqual(cmd._warnings, 0) def test_check_all(self): diff --git a/tests/test_cmd.py b/tests/test_cmd.py index cfd64851..97cdb8ad 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -44,7 +44,7 @@ class CommandTestCase(unittest.TestCase): # making sure execute gets called properly def _execute(func, args, exec_msg, level): - self.assertEquals(exec_msg, 'generating out from in') + self.assertEqual(exec_msg, 'generating out from in') cmd.force = True cmd.execute = _execute cmd.make_file(infiles='in', outfile='out', func='func', args=()) @@ -63,7 +63,7 @@ class CommandTestCase(unittest.TestCase): wanted = ["command options for 'MyCmd':", ' option1 = 1', ' option2 = 1'] - self.assertEquals(msgs, wanted) + self.assertEqual(msgs, wanted) def test_ensure_string(self): cmd = self.cmd @@ -81,7 +81,7 @@ class CommandTestCase(unittest.TestCase): cmd = self.cmd cmd.option1 = 'ok,dok' cmd.ensure_string_list('option1') - self.assertEquals(cmd.option1, ['ok', 'dok']) + self.assertEqual(cmd.option1, ['ok', 'dok']) cmd.option2 = ['xxx', 'www'] cmd.ensure_string_list('option2') @@ -109,14 +109,14 @@ class CommandTestCase(unittest.TestCase): with captured_stdout() as stdout: cmd.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), '') + self.assertEqual(stdout.read(), '') debug.DEBUG = True try: with captured_stdout() as stdout: cmd.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), 'xxx\n') + self.assertEqual(stdout.read(), 'xxx\n') finally: debug.DEBUG = False diff --git a/tests/test_config.py b/tests/test_config.py index 6c85efad..8d71234d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -90,7 +90,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, waited = [('password', 'secret'), ('realm', 'pypi'), ('repository', 'http://pypi.python.org/pypi'), ('server', 'server1'), ('username', 'me')] - self.assertEquals(config, waited) + self.assertEqual(config, waited) # old format self.write_file(self.rc, PYPIRC_OLD) @@ -100,7 +100,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, waited = [('password', 'secret'), ('realm', 'pypi'), ('repository', 'http://pypi.python.org/pypi'), ('server', 'server-login'), ('username', 'tarek')] - self.assertEquals(config, waited) + self.assertEqual(config, waited) def test_server_empty_registration(self): cmd = self._cmd(self.dist) @@ -111,7 +111,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, f = open(rc) try: content = f.read() - self.assertEquals(content, WANTED) + self.assertEqual(content, WANTED) finally: f.close() diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index ef2e7bc3..fcb798e7 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -34,7 +34,7 @@ class ConfigTestCase(support.LoggingSilencer, f.close() dump_file(this_file, 'I am the header') - self.assertEquals(len(self._logs), numlines+1) + self.assertEqual(len(self._logs), numlines+1) def test_search_cpp(self): if sys.platform == 'win32': @@ -44,10 +44,10 @@ class ConfigTestCase(support.LoggingSilencer, # simple pattern searches match = cmd.search_cpp(pattern='xxx', body='// xxx') - self.assertEquals(match, 0) + self.assertEqual(match, 0) match = cmd.search_cpp(pattern='_configtest', body='// xxx') - self.assertEquals(match, 1) + self.assertEqual(match, 1) def test_finalize_options(self): # finalize_options does a bit of transformation @@ -59,9 +59,9 @@ class ConfigTestCase(support.LoggingSilencer, cmd.library_dirs = 'three%sfour' % os.pathsep cmd.ensure_finalized() - self.assertEquals(cmd.include_dirs, ['one', 'two']) - self.assertEquals(cmd.libraries, ['one']) - self.assertEquals(cmd.library_dirs, ['three', 'four']) + self.assertEqual(cmd.include_dirs, ['one', 'two']) + self.assertEqual(cmd.libraries, ['one']) + self.assertEqual(cmd.library_dirs, ['three', 'four']) def test_clean(self): # _clean removes files diff --git a/tests/test_core.py b/tests/test_core.py index 63f6b31d..74d1f3b2 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -89,7 +89,7 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): with captured_stdout() as stdout: distutils.core.setup(name='bar') stdout.seek(0) - self.assertEquals(stdout.read(), 'bar\n') + self.assertEqual(stdout.read(), 'bar\n') distutils.core.DEBUG = True try: @@ -99,7 +99,7 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): distutils.core.DEBUG = False stdout.seek(0) wanted = "options (after parsing config files):\n" - self.assertEquals(stdout.readlines()[0], wanted) + self.assertEqual(stdout.readlines()[0], wanted) def test_suite(): return unittest.makeSuite(CoreTestCase) diff --git a/tests/test_dep_util.py b/tests/test_dep_util.py index d81d9143..35508196 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -42,8 +42,8 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase): self.write_file(two) self.write_file(four) - self.assertEquals(newer_pairwise([one, two], [three, four]), - ([one],[three])) + self.assertEqual(newer_pairwise([one, two], [three, four]), + ([one],[three])) def test_newer_group(self): tmpdir = self.mkdtemp() diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index aa9f9ebb..84a0ec6c 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -37,18 +37,18 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): mkpath(self.target, verbose=0) wanted = [] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) mkpath(self.target, verbose=1) wanted = ['creating %s' % self.root_target, 'creating %s' % self.target] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) self._logs = [] remove_tree(self.root_target, verbose=1) wanted = ["removing '%s' (and everything under it)" % self.root_target] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) @unittest.skipIf(sys.platform.startswith('win'), "This test is only appropriate for POSIX-like systems.") @@ -66,12 +66,12 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): def test_create_tree_verbosity(self): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) - self.assertEquals(self._logs, []) + self.assertEqual(self._logs, []) remove_tree(self.root_target, verbose=0) wanted = ['creating %s' % self.root_target] create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) @@ -81,7 +81,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): mkpath(self.target, verbose=0) copy_tree(self.target, self.target2, verbose=0) - self.assertEquals(self._logs, []) + self.assertEqual(self._logs, []) remove_tree(self.root_target, verbose=0) @@ -95,18 +95,18 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ['copying %s -> %s' % (a_file, self.target2)] copy_tree(self.target, self.target2, verbose=1) - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) remove_tree(self.target2, verbose=0) def test_ensure_relative(self): if os.sep == '/': - self.assertEquals(ensure_relative('/home/foo'), 'home/foo') - self.assertEquals(ensure_relative('some/path'), 'some/path') + self.assertEqual(ensure_relative('/home/foo'), 'home/foo') + self.assertEqual(ensure_relative('some/path'), 'some/path') else: # \\ - self.assertEquals(ensure_relative('c:\\home\\foo'), 'c:home\\foo') - self.assertEquals(ensure_relative('home\\foo'), 'home\\foo') + self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') + self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') def test_suite(): return unittest.makeSuite(DirUtilTestCase) diff --git a/tests/test_dist.py b/tests/test_dist.py index d9b413f9..8ab7e9fc 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -70,13 +70,13 @@ class DistributionTestCase(support.TempdirManager, with captured_stdout() as stdout: self.create_distribution(files) stdout.seek(0) - self.assertEquals(stdout.read(), '') + self.assertEqual(stdout.read(), '') distutils.dist.DEBUG = True try: with captured_stdout() as stdout: self.create_distribution(files) stdout.seek(0) - self.assertEquals(stdout.read(), '') + self.assertEqual(stdout.read(), '') finally: distutils.dist.DEBUG = False @@ -175,7 +175,7 @@ class DistributionTestCase(support.TempdirManager, finally: warnings.warn = old_warn - self.assertEquals(len(warns), 0) + self.assertEqual(len(warns), 0) def test_finalize_options(self): @@ -186,20 +186,20 @@ class DistributionTestCase(support.TempdirManager, dist.finalize_options() # finalize_option splits platforms and keywords - self.assertEquals(dist.metadata.platforms, ['one', 'two']) - self.assertEquals(dist.metadata.keywords, ['one', 'two']) + self.assertEqual(dist.metadata.platforms, ['one', 'two']) + self.assertEqual(dist.metadata.keywords, ['one', 'two']) def test_get_command_packages(self): dist = Distribution() - self.assertEquals(dist.command_packages, None) + self.assertEqual(dist.command_packages, None) cmds = dist.get_command_packages() - self.assertEquals(cmds, ['distutils.command']) - self.assertEquals(dist.command_packages, - ['distutils.command']) + self.assertEqual(cmds, ['distutils.command']) + self.assertEqual(dist.command_packages, + ['distutils.command']) dist.command_packages = 'one,two' cmds = dist.get_command_packages() - self.assertEquals(cmds, ['distutils.command', 'one', 'two']) + self.assertEqual(cmds, ['distutils.command', 'one', 'two']) def test_announce(self): @@ -236,7 +236,7 @@ class DistributionTestCase(support.TempdirManager, os.path.expanduser = old_expander # make sure --no-user-cfg disables the user cfg file - self.assertEquals(len(all_files)-1, len(files)) + self.assertEqual(len(all_files)-1, len(files)) class MetadataTestCase(support.TempdirManager, support.EnvironGuard, @@ -368,8 +368,8 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_fix_help_options(self): help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] fancy_options = fix_help_options(help_tuples) - self.assertEquals(fancy_options[0], ('a', 'b', 'c')) - self.assertEquals(fancy_options[1], (1, 2, 3)) + self.assertEqual(fancy_options[0], ('a', 'b', 'c')) + self.assertEqual(fancy_options[1], (1, 2, 3)) def test_show_help(self): # smoke test, just makes sure some help is displayed @@ -417,14 +417,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, PKG_INFO.seek(0) metadata.read_pkg_file(PKG_INFO) - self.assertEquals(metadata.name, "package") - self.assertEquals(metadata.version, "1.0") - self.assertEquals(metadata.description, "xxx") - self.assertEquals(metadata.download_url, 'http://example.com') - self.assertEquals(metadata.keywords, ['one', 'two']) - self.assertEquals(metadata.platforms, ['UNKNOWN']) - self.assertEquals(metadata.obsoletes, None) - self.assertEquals(metadata.requires, ['foo']) + self.assertEqual(metadata.name, "package") + self.assertEqual(metadata.version, "1.0") + self.assertEqual(metadata.description, "xxx") + self.assertEqual(metadata.download_url, 'http://example.com') + self.assertEqual(metadata.keywords, ['one', 'two']) + self.assertEqual(metadata.platforms, ['UNKNOWN']) + self.assertEqual(metadata.obsoletes, None) + self.assertEqual(metadata.requires, ['foo']) def test_suite(): suite = unittest.TestSuite() diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 823f2110..dbc62839 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -38,14 +38,14 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): move_file(self.source, self.target, verbose=0) wanted = [] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) # back to original state move_file(self.target, self.source, verbose=0) move_file(self.source, self.target, verbose=1) wanted = ['moving %s -> %s' % (self.source, self.target)] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) # back to original state move_file(self.target, self.source, verbose=0) @@ -55,7 +55,7 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): os.mkdir(self.target_dir) move_file(self.source, self.target_dir, verbose=1) wanted = ['moving %s -> %s' % (self.source, self.target_dir)] - self.assertEquals(self._logs, wanted) + self.assertEqual(self._logs, wanted) def test_write_file(self): lines = ['a', 'b', 'c'] @@ -63,7 +63,7 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): foo = os.path.join(dir, 'foo') write_file(foo, lines) content = [line.strip() for line in open(foo).readlines()] - self.assertEquals(content, lines) + self.assertEqual(content, lines) def test_copy_file(self): src_dir = self.mkdtemp() diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 0cbb48bc..32c56c75 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -24,15 +24,15 @@ class FileListTestCase(unittest.TestCase): def test_glob_to_re(self): # simple cases - self.assertEquals(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)') - self.assertEquals(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)') - self.assertEquals(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)') + self.assertEqual(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)') + self.assertEqual(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)') + self.assertEqual(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)') # special cases - self.assertEquals(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)') - self.assertEquals(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)') - self.assertEquals(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') - self.assertEquals(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') + self.assertEqual(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)') + self.assertEqual(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)') + self.assertEqual(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)') + self.assertEqual(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)') def test_process_template_line(self): # testing all MANIFEST.in template patterns @@ -60,21 +60,21 @@ class FileListTestCase(unittest.TestCase): join('global', 'two.txt'), join('f', 'o', 'f.oo'), join('dir', 'graft-one'), join('dir', 'dir2', 'graft2')] - self.assertEquals(file_list.files, wanted) + self.assertEqual(file_list.files, wanted) def test_debug_print(self): file_list = FileList() with captured_stdout() as stdout: file_list.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), '') + self.assertEqual(stdout.read(), '') debug.DEBUG = True try: with captured_stdout() as stdout: file_list.debug_print('xxx') stdout.seek(0) - self.assertEquals(stdout.read(), 'xxx\n') + self.assertEqual(stdout.read(), 'xxx\n') finally: debug.DEBUG = False diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 377ae86e..86db4a12 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -27,14 +27,14 @@ class InstallDataTestCase(support.TempdirManager, self.write_file(two, 'xxx') cmd.data_files = [one, (inst2, [two])] - self.assertEquals(cmd.get_inputs(), [one, (inst2, [two])]) + self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])]) # let's run the command cmd.ensure_finalized() cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) rtwo = os.path.split(two)[-1] self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) rone = os.path.split(one)[-1] @@ -47,7 +47,7 @@ class InstallDataTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] @@ -65,7 +65,7 @@ class InstallDataTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 4) + self.assertEqual(len(cmd.get_outputs()), 4) self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index 5b32b13e..aa8a4e60 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -23,7 +23,7 @@ class InstallHeadersTestCase(support.TempdirManager, pkg_dir, dist = self.create_dist(headers=headers) cmd = install_headers(dist) - self.assertEquals(cmd.get_inputs(), headers) + self.assertEqual(cmd.get_inputs(), headers) # let's run the command cmd.install_dir = os.path.join(pkg_dir, 'inst') @@ -31,7 +31,7 @@ class InstallHeadersTestCase(support.TempdirManager, cmd.run() # let's check the results - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) def test_suite(): return unittest.makeSuite(InstallHeadersTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 13d27aba..754ea636 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -18,8 +18,8 @@ class InstallLibTestCase(support.TempdirManager, cmd = install_lib(dist) cmd.finalize_options() - self.assertEquals(cmd.compile, 1) - self.assertEquals(cmd.optimize, 0) + self.assertEqual(cmd.compile, 1) + self.assertEqual(cmd.optimize, 0) # optimize must be 0, 1, or 2 cmd.optimize = 'foo' @@ -29,7 +29,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.optimize = '2' cmd.finalize_options() - self.assertEquals(cmd.optimize, 2) + self.assertEqual(cmd.optimize, 2) def _setup_byte_compile(self): pkg_dir, dist = self.create_dist() @@ -81,7 +81,7 @@ class InstallLibTestCase(support.TempdirManager, cmd.distribution.script_name = 'setup.py' # get_input should return 2 elements - self.assertEquals(len(cmd.get_inputs()), 2) + self.assertEqual(len(cmd.get_inputs()), 2) def test_dont_write_bytecode(self): # makes sure byte_compile is not used diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 567505dd..b8e22094 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -103,7 +103,7 @@ class msvc9compilerTestCase(support.TempdirManager, import _winreg HKCU = _winreg.HKEY_CURRENT_USER keys = Reg.read_keys(HKCU, 'xxxx') - self.assertEquals(keys, None) + self.assertEqual(keys, None) keys = Reg.read_keys(HKCU, r'Control Panel') self.assertTrue('Desktop' in keys) @@ -130,7 +130,7 @@ class msvc9compilerTestCase(support.TempdirManager, f.close() # makes sure the manifest was properly cleaned - self.assertEquals(content, _CLEANED_MANIFEST) + self.assertEqual(content, _CLEANED_MANIFEST) def test_suite(): diff --git a/tests/test_register.py b/tests/test_register.py index 6da479b1..915427b7 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -122,7 +122,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): f = open(self.rc) try: content = f.read() - self.assertEquals(content, WANTED_PYPIRC) + self.assertEqual(content, WANTED_PYPIRC) finally: f.close() @@ -141,7 +141,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(self.conn.reqs, 2) req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) - self.assertEquals(req2['Content-length'], req1['Content-length']) + self.assertEqual(req2['Content-length'], req1['Content-length']) self.assertTrue('xxx' in self.conn.reqs[1].data) def test_password_not_in_file(self): @@ -154,7 +154,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): # dist.password should be set # therefore used afterwards by other commands - self.assertEquals(cmd.distribution.password, 'password') + self.assertEqual(cmd.distribution.password, 'password') def test_registering(self): # this test runs choice 2 @@ -171,7 +171,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) - self.assertEquals(headers['Content-length'], '608') + self.assertEqual(headers['Content-length'], '608') self.assertTrue('tarek' in req.data) def test_password_reset(self): @@ -189,7 +189,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) - self.assertEquals(headers['Content-length'], '290') + self.assertEqual(headers['Content-length'], '290') self.assertTrue('tarek' in req.data) def test_strict(self): @@ -252,7 +252,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): with check_warnings() as w: warnings.simplefilter("always") cmd.check_metadata() - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) def test_suite(): return unittest.makeSuite(RegisterTestCase) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 7cebbf53..b9d86bb0 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -127,7 +127,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # now let's check what we have dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) - self.assertEquals(files, ['fake-1.0.zip']) + self.assertEqual(files, ['fake-1.0.zip']) zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) try: @@ -136,7 +136,7 @@ class SDistTestCase(PyPIRCCommandTestCase): zip_file.close() # making sure everything has been pruned correctly - self.assertEquals(len(content), 4) + self.assertEqual(len(content), 4) @unittest.skipUnless(zlib, "requires zlib") def test_make_distribution(self): @@ -158,8 +158,7 @@ class SDistTestCase(PyPIRCCommandTestCase): dist_folder = join(self.tmp_dir, 'dist') result = os.listdir(dist_folder) result.sort() - self.assertEquals(result, - ['fake-1.0.tar', 'fake-1.0.tar.gz'] ) + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'] ) os.remove(join(dist_folder, 'fake-1.0.tar')) os.remove(join(dist_folder, 'fake-1.0.tar.gz')) @@ -172,8 +171,7 @@ class SDistTestCase(PyPIRCCommandTestCase): result = os.listdir(dist_folder) result.sort() - self.assertEquals(result, - ['fake-1.0.tar', 'fake-1.0.tar.gz']) + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) @unittest.skipUnless(zlib, "requires zlib") def test_add_defaults(self): @@ -222,7 +220,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # now let's check what we have dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) - self.assertEquals(files, ['fake-1.0.zip']) + self.assertEqual(files, ['fake-1.0.zip']) zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) try: @@ -231,13 +229,13 @@ class SDistTestCase(PyPIRCCommandTestCase): zip_file.close() # making sure everything was added - self.assertEquals(len(content), 11) + self.assertEqual(len(content), 11) # checking the MANIFEST f = open(join(self.tmp_dir, 'MANIFEST')) try: manifest = f.read() - self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + self.assertEqual(manifest, MANIFEST % {'sep': os.sep}) finally: f.close() @@ -251,7 +249,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.ensure_finalized() cmd.run() warnings = self.get_logs(WARN) - self.assertEquals(len(warnings), 2) + self.assertEqual(len(warnings), 2) # trying with a complete set of metadata self.clear_logs() @@ -260,7 +258,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.metadata_check = 0 cmd.run() warnings = self.get_logs(WARN) - self.assertEquals(len(warnings), 0) + self.assertEqual(len(warnings), 0) def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated @@ -268,7 +266,7 @@ class SDistTestCase(PyPIRCCommandTestCase): with check_warnings() as w: warnings.simplefilter("always") cmd.check_metadata() - self.assertEquals(len(w.warnings), 1) + self.assertEqual(len(w.warnings), 1) def test_show_formats(self): with captured_stdout() as stdout: @@ -278,7 +276,7 @@ class SDistTestCase(PyPIRCCommandTestCase): num_formats = len(ARCHIVE_FORMATS.keys()) output = [line for line in stdout.getvalue().split('\n') if line.strip().startswith('--formats=')] - self.assertEquals(len(output), num_formats) + self.assertEqual(len(output), num_formats) def test_finalize_options(self): @@ -286,9 +284,9 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.finalize_options() # default options set by finalize - self.assertEquals(cmd.manifest, 'MANIFEST') - self.assertEquals(cmd.template, 'MANIFEST.in') - self.assertEquals(cmd.dist_dir, 'dist') + self.assertEqual(cmd.manifest, 'MANIFEST') + self.assertEqual(cmd.template, 'MANIFEST.in') + self.assertEqual(cmd.dist_dir, 'dist') # formats has to be a string splitable on (' ', ',') or # a stringlist @@ -325,8 +323,8 @@ class SDistTestCase(PyPIRCCommandTestCase): archive = tarfile.open(archive_name) try: for member in archive.getmembers(): - self.assertEquals(member.uid, 0) - self.assertEquals(member.gid, 0) + self.assertEqual(member.uid, 0) + self.assertEqual(member.gid, 0) finally: archive.close() @@ -347,7 +345,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # rights (see #7408) try: for member in archive.getmembers(): - self.assertEquals(member.uid, os.getuid()) + self.assertEqual(member.uid, os.getuid()) finally: archive.close() @@ -369,7 +367,7 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: f.close() - self.assertEquals(len(manifest), 5) + self.assertEqual(len(manifest), 5) # adding a file self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') @@ -389,7 +387,7 @@ class SDistTestCase(PyPIRCCommandTestCase): f.close() # do we have the new file in MANIFEST ? - self.assertEquals(len(manifest2), 6) + self.assertEqual(len(manifest2), 6) self.assertIn('doc2.txt', manifest2[-1]) def test_manifest_marker(self): diff --git a/tests/test_spawn.py b/tests/test_spawn.py index b9fd610e..6caf0390 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -20,7 +20,7 @@ class SpawnTestCase(support.TempdirManager, (['nochange', 'nospace'], ['nochange', 'nospace'])): res = _nt_quote_args(args) - self.assertEquals(res, wanted) + self.assertEqual(res, wanted) @unittest.skipUnless(os.name in ('nt', 'posix'), diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 8449ddca..49570c4c 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -36,7 +36,7 @@ class SysconfigTestCase(support.EnvironGuard, sysconfig.get_python_lib(prefix=TESTFN)) _sysconfig = __import__('sysconfig') res = sysconfig.get_python_lib(True, True) - self.assertEquals(_sysconfig.get_path('platstdlib'), res) + self.assertEqual(_sysconfig.get_path('platstdlib'), res) def test_get_python_inc(self): inc_dir = sysconfig.get_python_inc() @@ -56,8 +56,8 @@ class SysconfigTestCase(support.EnvironGuard, finally: fd.close() d = sysconfig.parse_makefile(self.makefile) - self.assertEquals(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", - 'OTHER': 'foo'}) + self.assertEqual(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", + 'OTHER': 'foo'}) def test_parse_makefile_literal_dollar(self): self.makefile = test.test_support.TESTFN @@ -68,8 +68,8 @@ class SysconfigTestCase(support.EnvironGuard, finally: fd.close() d = sysconfig.parse_makefile(self.makefile) - self.assertEquals(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", - 'OTHER': 'foo'}) + self.assertEqual(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", + 'OTHER': 'foo'}) def test_suite(): diff --git a/tests/test_text_file.py b/tests/test_text_file.py index 3093097d..f1e32b6c 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -48,7 +48,7 @@ class TextFileTestCase(support.TempdirManager, unittest.TestCase): def test_input(count, description, file, expected_result): result = file.readlines() - self.assertEquals(result, expected_result) + self.assertEqual(result, expected_result) tmpdir = self.mkdtemp() filename = os.path.join(tmpdir, "test.txt") diff --git a/tests/test_upload.py b/tests/test_upload.py index 3ae89498..f45ee41e 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -80,7 +80,7 @@ class uploadTestCase(PyPIRCCommandTestCase): for attr, waited in (('username', 'me'), ('password', 'secret'), ('realm', 'pypi'), ('repository', 'http://pypi.python.org/pypi')): - self.assertEquals(getattr(cmd, attr), waited) + self.assertEqual(getattr(cmd, attr), waited) def test_saved_password(self): # file with no password @@ -90,14 +90,14 @@ class uploadTestCase(PyPIRCCommandTestCase): dist = Distribution() cmd = upload(dist) cmd.finalize_options() - self.assertEquals(cmd.password, None) + self.assertEqual(cmd.password, None) # make sure we get it as well, if another command # initialized it at the dist level dist.password = 'xxx' cmd = upload(dist) cmd.finalize_options() - self.assertEquals(cmd.password, 'xxx') + self.assertEqual(cmd.password, 'xxx') def test_upload(self): tmp = self.mkdtemp() @@ -116,11 +116,11 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? self.assertIn('dédé', self.last_open.req.data) headers = dict(self.last_open.req.headers) - self.assertEquals(headers['Content-length'], '2085') + self.assertEqual(headers['Content-length'], '2085') self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) - self.assertEquals(self.last_open.req.get_method(), 'POST') - self.assertEquals(self.last_open.req.get_full_url(), - 'http://pypi.python.org/pypi') + self.assertEqual(self.last_open.req.get_method(), 'POST') + self.assertEqual(self.last_open.req.get_full_url(), + 'http://pypi.python.org/pypi') self.assertTrue('xxx' in self.last_open.req.data) auth = self.last_open.req.headers['Authorization'] self.assertFalse('\n' in auth) diff --git a/tests/test_version.py b/tests/test_version.py index 747db948..1d9fbc79 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -7,12 +7,12 @@ class VersionTestCase(unittest.TestCase): def test_prerelease(self): version = StrictVersion('1.2.3a1') - self.assertEquals(version.version, (1, 2, 3)) - self.assertEquals(version.prerelease, ('a', 1)) - self.assertEquals(str(version), '1.2.3a1') + self.assertEqual(version.version, (1, 2, 3)) + self.assertEqual(version.prerelease, ('a', 1)) + self.assertEqual(str(version), '1.2.3a1') version = StrictVersion('1.2.0') - self.assertEquals(str(version), '1.2') + self.assertEqual(str(version), '1.2') def test_cmp_strict(self): versions = (('1.5.1', '1.5.2b2', -1), @@ -41,9 +41,9 @@ class VersionTestCase(unittest.TestCase): raise AssertionError(("cmp(%s, %s) " "shouldn't raise ValueError") % (v1, v2)) - self.assertEquals(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) def test_cmp(self): @@ -59,9 +59,9 @@ class VersionTestCase(unittest.TestCase): for v1, v2, wanted in versions: res = LooseVersion(v1).__cmp__(LooseVersion(v2)) - self.assertEquals(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) def test_suite(): return unittest.makeSuite(VersionTestCase) -- cgit v1.2.1 From bc76c36d325cd64296b2d7a7420479c87e51be22 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 24 Nov 2010 19:43:47 +0000 Subject: Final patch for issue 9807. --- command/install.py | 6 ++++-- sysconfig.py | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/command/install.py b/command/install.py index 58f6ec5b..7f9d00f2 100644 --- a/command/install.py +++ b/command/install.py @@ -48,7 +48,7 @@ INSTALL_SCHEMES = { 'unix_prefix': { 'purelib': '$base/lib/python$py_version_short/site-packages', 'platlib': '$platbase/lib/python$py_version_short/site-packages', - 'headers': '$base/include/python$py_version_short/$dist_name', + 'headers': '$base/include/python$py_version_short$abiflags/$dist_name', 'scripts': '$base/bin', 'data' : '$base', }, @@ -82,7 +82,8 @@ if HAS_USER_SITE: INSTALL_SCHEMES['unix_user'] = { 'purelib': '$usersite', 'platlib': '$usersite', - 'headers': '$userbase/include/python$py_version_short/$dist_name', + 'headers': + '$userbase/include/python$py_version_short$abiflags/$dist_name', 'scripts': '$userbase/bin', 'data' : '$userbase', } @@ -322,6 +323,7 @@ class install(Command): 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, + 'abiflags': sys.abiflags, } if HAS_USER_SITE: diff --git a/sysconfig.py b/sysconfig.py index be91f92c..897b7d63 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -11,7 +11,6 @@ Email: __revision__ = "$Id$" -import io import os import re import sys @@ -49,6 +48,18 @@ def _python_build(): return False python_build = _python_build() +# Calculate the build qualifier flags if they are defined. Adding the flags +# to the include and lib directories only makes sense for an installation, not +# an in-source build. +build_flags = '' +try: + if not python_build: + build_flags = sys.abiflags +except AttributeError: + # It's not a configure-based build, so the sys module doesn't have + # this attribute, which is fine. + pass + def get_python_version(): """Return a string containing the major and minor Python version, leaving off the patchlevel. Sample return values could be '1.5' @@ -83,7 +94,8 @@ def get_python_inc(plat_specific=0, prefix=None): else: incdir = os.path.join(get_config_var('srcdir'), 'Include') return os.path.normpath(incdir) - return os.path.join(prefix, "include", "python" + get_python_version()) + python_dir = 'python' + get_python_version() + build_flags + return os.path.join(prefix, "include", python_dir) elif os.name == "nt": return os.path.join(prefix, "include") elif os.name == "os2": @@ -209,7 +221,8 @@ def get_makefile_filename(): if python_build: return os.path.join(os.path.dirname(sys.executable), "Makefile") lib_dir = get_python_lib(plat_specific=1, standard_lib=1) - return os.path.join(lib_dir, "config", "Makefile") + config_file = 'config-{}{}'.format(get_python_version(), build_flags) + return os.path.join(lib_dir, config_file, 'Makefile') def parse_config_h(fp, g=None): -- cgit v1.2.1 From 47c1750f35348e99498ca6e8fcec38b67efe95d9 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 25 Nov 2010 03:46:44 +0000 Subject: sys.abiflags is not defined on all platforms. --- command/install.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 7f9d00f2..bdc3a09b 100644 --- a/command/install.py +++ b/command/install.py @@ -313,6 +313,11 @@ class install(Command): py_version = sys.version.split()[0] (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') + try: + abiflags = sys.abiflags + except AttributeError: + # sys.abiflags may not be defined on all platforms. + abiflags = '' self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), 'dist_fullname': self.distribution.get_fullname(), @@ -323,7 +328,7 @@ class install(Command): 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, - 'abiflags': sys.abiflags, + 'abiflags': abiflags, } if HAS_USER_SITE: -- cgit v1.2.1 From 22eaecf1e9401c320a4da6e387798e094de0679b Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Nov 2010 14:40:29 +0000 Subject: 2.7.1 final version bump --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f9f97927..a82d4cb5 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.1rc1" +__version__ = "2.7.1" #--end constants-- -- cgit v1.2.1 From 8e91a860e081fec22be2322c09613ebac1a1968c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Nov 2010 14:46:13 +0000 Subject: 3.1.3 final version bump --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 9684a11c..6f55f016 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.3rc1" +__version__ = "3.1.3" #--end constants-- -- cgit v1.2.1 From a708da6b241d23acd2ae934de3a42d36a2bcaefb Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Fri, 3 Dec 2010 17:00:40 +0000 Subject: Fix for issue10367, courtesy of Daniel Tavares. --- command/upload.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/upload.py b/command/upload.py index 980cf68d..d0133353 100644 --- a/command/upload.py +++ b/command/upload.py @@ -176,6 +176,9 @@ class upload(PyPIRCCommand): result = urlopen(request) status = result.getcode() reason = result.msg + if self.show_response: + msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) + self.announce(msg, log.INFO) except socket.error, e: self.announce(str(e), log.ERROR) return @@ -189,6 +192,3 @@ class upload(PyPIRCCommand): else: self.announce('Upload failed (%s): %s' % (status, reason), log.ERROR) - if self.show_response: - msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) - self.announce(msg, log.INFO) -- cgit v1.2.1 From 13652894fa830ff6d9eff472a956034f09f0922e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Dec 2010 19:09:24 +0000 Subject: Bump to 3.2b1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index bae33b55..8b85f35f 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a4" +__version__ = "3.2b1" #--end constants-- -- cgit v1.2.1 From 549a7e010309ec57035d877a7b2ca9da993d1772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 15 Dec 2010 20:26:30 +0000 Subject: Fix wrong name in docstring and doc (#10693). Original patch by Eli Bendersky. --- archive_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archive_util.py b/archive_util.py index 16164c7f..6dd0445d 100644 --- a/archive_util.py +++ b/archive_util.py @@ -68,7 +68,7 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): """Create a zip file from all the files under 'base_dir'. - The output zip file will be named 'base_dir' + ".zip". Uses either the + The output zip file will be named 'base_name' + ".zip". Uses either the "zipfile" Python module (if available) or the InfoZIP "zip" utility (if installed and found on the default search path). If neither tool is available, raises DistutilsExecError. Returns the name of the output zip -- cgit v1.2.1 From ffc0137f6db0847ca5701ca4735511f3a816ef24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 15 Dec 2010 20:30:51 +0000 Subject: Merged revisions 87277 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r87277 | eric.araujo | 2010-12-15 21:26:30 +0100 (mer., 15 déc. 2010) | 2 lines Fix wrong name in docstring and doc (#10693). Original patch by Eli Bendersky. ........ --- archive_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archive_util.py b/archive_util.py index 16164c7f..6dd0445d 100644 --- a/archive_util.py +++ b/archive_util.py @@ -68,7 +68,7 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): """Create a zip file from all the files under 'base_dir'. - The output zip file will be named 'base_dir' + ".zip". Uses either the + The output zip file will be named 'base_name' + ".zip". Uses either the "zipfile" Python module (if available) or the InfoZIP "zip" utility (if installed and found on the default search path). If neither tool is available, raises DistutilsExecError. Returns the name of the output zip -- cgit v1.2.1 From 2b3c0661ab064462c5b404b65befe70842f2ca88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 15 Dec 2010 20:33:50 +0000 Subject: Merged revisions 87277 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r87277 | eric.araujo | 2010-12-15 21:26:30 +0100 (mer., 15 déc. 2010) | 2 lines Fix wrong name in docstring and doc (#10693). Original patch by Eli Benderski. ........ --- archive_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/archive_util.py b/archive_util.py index c741cc01..834b722e 100644 --- a/archive_util.py +++ b/archive_util.py @@ -121,7 +121,7 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): """Create a zip file from all the files under 'base_dir'. - The output zip file will be named 'base_dir' + ".zip". Uses either the + The output zip file will be named 'base_name' + ".zip". Uses either the "zipfile" Python module (if available) or the InfoZIP "zip" utility (if installed and found on the default search path). If neither tool is available, raises DistutilsExecError. Returns the name of the output zip -- cgit v1.2.1 From 034ae816b59d337b63d8fc521bb786a4cefe7541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 15 Dec 2010 21:07:22 +0000 Subject: Fix build_ext with VS 8.0. Patch by Hirokazu Yamamoto (#9558). --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index cc0d414c..fb316489 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -214,7 +214,7 @@ class build_ext(Command): elif MSVC_VERSION == 8: self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PC', 'VS8.0', 'win32release')) + 'PC', 'VS8.0')) elif MSVC_VERSION == 7: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS7.1')) -- cgit v1.2.1 From 2b9d979b2156e50f3649eb0e72c0dfb54d40abec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 15 Dec 2010 21:12:12 +0000 Subject: Merged revisions 87280 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r87280 | eric.araujo | 2010-12-15 22:07:22 +0100 (mer., 15 déc. 2010) | 2 lines Fix build_ext with VS 8.0. Patch by Hirokazu Yamamoto (#9558). ........ --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 4e664642..502b39a8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -214,7 +214,7 @@ class build_ext(Command): elif MSVC_VERSION == 8: self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PC', 'VS8.0', 'win32release')) + 'PC', 'VS8.0')) elif MSVC_VERSION == 7: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS7.1')) -- cgit v1.2.1 From 673ea4dd320b5e8abc19b0d6766f3c285e3c9eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 15 Dec 2010 21:15:25 +0000 Subject: Merged revisions 87280 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ........ r87280 | eric.araujo | 2010-12-15 22:07:22 +0100 (mer., 15 déc. 2010) | 2 lines Fix build_ext with VS 8.0. Patch by Hirokazu Yamamoto (#9558). ........ --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 5b3be6fb..55a4288a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -207,7 +207,7 @@ class build_ext (Command): elif MSVC_VERSION == 8: self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PC', 'VS8.0', 'win32release')) + 'PC', 'VS8.0')) elif MSVC_VERSION == 7: self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VS7.1')) -- cgit v1.2.1 From 6451e39efcacf15bdbe420fd9a21e46dac0a0b2e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 19 Dec 2010 10:30:28 +0000 Subject: Bump to 3.2b2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8b85f35f..1d752e5e 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2b1" +__version__ = "3.2b2" #--end constants-- -- cgit v1.2.1 From c0b2f9e8190d95578e94c2a550144a91aa9c5e18 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 15 Jan 2011 17:08:53 +0000 Subject: Bump to 3.2rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 1d752e5e..40ca9b2a 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2b2" +__version__ = "3.2rc1" #--end constants-- -- cgit v1.2.1 From 663bdd57f912467e076d8e7f0be95a4755c99e4f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 30 Jan 2011 14:03:33 +0000 Subject: Bump version. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 40ca9b2a..4bb50123 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2rc1" +__version__ = "3.2rc2" #--end constants-- -- cgit v1.2.1 From 23a12ce1d87ed000f879d4a702e0f5c1b6ad8a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 2 Feb 2011 21:38:37 +0000 Subject: Merged revisions 86236,86240,86332,86340,87271,87273,87447 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The missing NEWS entries correspond to changes that were made before 3.1.3, but I think it’s not usual to edit entries of released versions, so I put them at the top. ........ r86236 | eric.araujo | 2010-11-06 03:44:43 +0100 (sam., 06 nov. 2010) | 2 lines Make sure each test can be run standalone (./python Lib/distutils/tests/x.py) ........ r86240 | eric.araujo | 2010-11-06 05:11:59 +0100 (sam., 06 nov. 2010) | 2 lines Prevent ResourceWarnings in test_gettext ........ r86332 | eric.araujo | 2010-11-08 19:15:17 +0100 (lun., 08 nov. 2010) | 4 lines Add missing NEWS entry for a fix committed by Senthil. All recent modifications to distutils should now be covered in NEWS. ........ r86340 | eric.araujo | 2010-11-08 22:48:23 +0100 (lun., 08 nov. 2010) | 2 lines This was actually fixed for the previous alpha. ........ r87271 | eric.araujo | 2010-12-15 20:09:58 +0100 (mer., 15 déc. 2010) | 2 lines Improve trace documentation (#9264). Patch by Eli Bendersky. ........ r87273 | eric.araujo | 2010-12-15 20:30:15 +0100 (mer., 15 déc. 2010) | 2 lines Use nested method directives, rewrap long lines, fix whitespace. ........ r87447 | eric.araujo | 2010-12-23 20:13:05 +0100 (jeu., 23 déc. 2010) | 2 lines Fix typo in superclass method name ........ --- tests/__init__.py | 5 +++-- tests/test_archive_util.py | 4 ++-- tests/test_bdist.py | 3 ++- tests/test_bdist_dumb.py | 3 ++- tests/test_bdist_rpm.py | 3 ++- tests/test_bdist_wininst.py | 3 ++- tests/test_build_clib.py | 4 +++- tests/test_build_ext.py | 1 + tests/test_build_py.py | 3 ++- tests/test_build_scripts.py | 3 ++- tests/test_check.py | 3 ++- tests/test_clean.py | 3 ++- tests/test_cmd.py | 6 +++--- tests/test_config.py | 3 ++- tests/test_config_cmd.py | 3 ++- tests/test_core.py | 4 ++-- tests/test_cygwinccompiler.py | 3 ++- tests/test_dir_util.py | 3 ++- tests/test_dist.py | 4 ++-- tests/test_extension.py | 4 ++-- tests/test_file_util.py | 3 ++- tests/test_filelist.py | 4 ++-- tests/test_install.py | 4 ++-- tests/test_install_data.py | 3 ++- tests/test_install_headers.py | 3 ++- tests/test_install_lib.py | 3 ++- tests/test_install_scripts.py | 3 ++- tests/test_log.py | 3 ++- tests/test_msvc9compiler.py | 3 ++- tests/test_register.py | 4 ++-- tests/test_sdist.py | 8 +++----- tests/test_spawn.py | 4 ++-- tests/test_text_file.py | 3 ++- tests/test_unixccompiler.py | 3 ++- tests/test_upload.py | 5 ++--- tests/test_util.py | 3 ++- tests/test_version.py | 3 ++- tests/test_versionpredicate.py | 4 ++++ 38 files changed, 81 insertions(+), 53 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 7bdb9124..1b939cbd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -15,9 +15,10 @@ by import rather than matching pre-defined names. import os import sys import unittest +from test.support import run_unittest -here = os.path.dirname(__file__) +here = os.path.dirname(__file__) or os.curdir def test_suite(): @@ -32,4 +33,4 @@ def test_suite(): if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index d77302d5..aff42652 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -12,7 +12,7 @@ from distutils.archive_util import (check_archive_formats, make_tarball, ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support -from test.support import check_warnings +from test.support import check_warnings, run_unittest try: import zipfile @@ -211,4 +211,4 @@ def test_suite(): return unittest.makeSuite(ArchiveUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_bdist.py b/tests/test_bdist.py index 715283cb..94d40cc2 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -4,6 +4,7 @@ import sys import os import tempfile import shutil +from test.support import run_unittest from distutils.core import Distribution from distutils.command.bdist import bdist @@ -40,4 +41,4 @@ def test_suite(): return unittest.makeSuite(BuildTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index bf146eb5..cc37fef8 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -3,6 +3,7 @@ import unittest import sys import os +from test.support import run_unittest from distutils.core import Distribution from distutils.command.bdist_dumb import bdist_dumb @@ -77,4 +78,4 @@ def test_suite(): return unittest.makeSuite(BuildDumbTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 2aa257f7..804fb135 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -5,6 +5,7 @@ import sys import os import tempfile import shutil +from test.support import run_unittest from distutils.core import Distribution from distutils.command.bdist_rpm import bdist_rpm @@ -122,4 +123,4 @@ def test_suite(): return unittest.makeSuite(BuildRpmTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index 9b1ba6d1..f9e8f89e 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -1,5 +1,6 @@ """Tests for distutils.command.bdist_wininst.""" import unittest +from test.support import run_unittest from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support @@ -27,4 +28,4 @@ def test_suite(): return unittest.makeSuite(BuildWinInstTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index d6d1a4d0..69bd2bf6 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -3,6 +3,8 @@ import unittest import os import sys +from test.support import run_unittest + from distutils.command.build_clib import build_clib from distutils.errors import DistutilsSetupError from distutils.tests import support @@ -141,4 +143,4 @@ def test_suite(): return unittest.makeSuite(BuildCLibTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index e8b15f15..11844d66 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -15,6 +15,7 @@ from distutils.errors import (UnknownFileError, DistutilsSetupError, import unittest from test import support +from test.support import run_unittest # http://bugs.python.org/issue4373 # Don't load the xx module more than once. diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 8a69aaa0..da3232ce 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -10,6 +10,7 @@ from distutils.core import Distribution from distutils.errors import DistutilsFileError from distutils.tests import support +from test.support import run_unittest class BuildPyTestCase(support.TempdirManager, @@ -114,4 +115,4 @@ def test_suite(): return unittest.makeSuite(BuildPyTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index 85b04004..e3326b85 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -8,6 +8,7 @@ from distutils.core import Distribution from distutils import sysconfig from distutils.tests import support +from test.support import run_unittest class BuildScriptsTestCase(support.TempdirManager, @@ -108,4 +109,4 @@ def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_check.py b/tests/test_check.py index 7c56c043..229ae25c 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -1,5 +1,6 @@ """Tests for distutils.command.check.""" import unittest +from test.support import run_unittest from distutils.command.check import check, HAS_DOCUTILS from distutils.tests import support @@ -95,4 +96,4 @@ def test_suite(): return unittest.makeSuite(CheckTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_clean.py b/tests/test_clean.py index dbc4ee2a..649855f7 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -6,6 +6,7 @@ import getpass from distutils.command.clean import clean from distutils.tests import support +from test.support import run_unittest class cleanTestCase(support.TempdirManager, support.LoggingSilencer, @@ -47,4 +48,4 @@ def test_suite(): return unittest.makeSuite(cleanTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 5821fcd2..195045cc 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -1,7 +1,7 @@ """Tests for distutils.cmd.""" import unittest import os -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils.cmd import Command from distutils.dist import Distribution @@ -99,7 +99,7 @@ class CommandTestCase(unittest.TestCase): def test_ensure_dirname(self): cmd = self.cmd - cmd.option1 = os.path.dirname(__file__) + cmd.option1 = os.path.dirname(__file__) or os.curdir cmd.ensure_dirname('option1') cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') @@ -124,4 +124,4 @@ def test_suite(): return unittest.makeSuite(CommandTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_config.py b/tests/test_config.py index 2c075d7e..525bee94 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -10,6 +10,7 @@ from distutils.log import set_threshold from distutils.log import WARN from distutils.tests import support +from test.support import run_unittest PYPIRC = """\ [distutils] @@ -116,4 +117,4 @@ def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index fcb798e7..4f7ebdd9 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -2,6 +2,7 @@ import unittest import os import sys +from test.support import run_unittest from distutils.command.config import dump_file, config from distutils.tests import support @@ -86,4 +87,4 @@ def test_suite(): return unittest.makeSuite(ConfigTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_core.py b/tests/test_core.py index d781555d..41321f7d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,7 +6,7 @@ import os import shutil import sys import test.support -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest import unittest from distutils.tests import support @@ -105,4 +105,4 @@ def test_suite(): return unittest.makeSuite(CoreTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index 79377a77..85692167 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -4,6 +4,7 @@ import sys import os from io import BytesIO import subprocess +from test.support import run_unittest from distutils import cygwinccompiler from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, @@ -151,4 +152,4 @@ def test_suite(): return unittest.makeSuite(CygwinCCompilerTestCase) if __name__ == '__main__': - test_support.run_unittest(test_suite()) + run_unittest(test_suite()) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 84a0ec6c..ce74589d 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -10,6 +10,7 @@ from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, from distutils import log from distutils.tests import support +from test.support import run_unittest class DirUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -112,4 +113,4 @@ def test_suite(): return unittest.makeSuite(DirUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_dist.py b/tests/test_dist.py index 2c19d892..1624f00e 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -10,7 +10,7 @@ import textwrap from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command -from test.support import TESTFN, captured_stdout +from test.support import TESTFN, captured_stdout, run_unittest from distutils.tests import support @@ -326,4 +326,4 @@ def test_suite(): return suite if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_extension.py b/tests/test_extension.py index d9a47a89..e35f2738 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -3,7 +3,7 @@ import unittest import os import warnings -from test.support import check_warnings +from test.support import check_warnings, run_unittest from distutils.extension import read_setup_file, Extension class ExtensionTestCase(unittest.TestCase): @@ -66,4 +66,4 @@ def test_suite(): return unittest.makeSuite(ExtensionTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 730ffde4..3c3e3dcb 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -6,6 +6,7 @@ import shutil from distutils.file_util import move_file from distutils import log from distutils.tests import support +from test.support import run_unittest class FileUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -62,4 +63,4 @@ def test_suite(): return unittest.makeSuite(FileUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index d2a2b449..c7e5201b 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -2,7 +2,7 @@ import unittest from distutils.filelist import glob_to_re, FileList -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils import debug class FileListTestCase(unittest.TestCase): @@ -39,4 +39,4 @@ def test_suite(): return unittest.makeSuite(FileListTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install.py b/tests/test_install.py index f9c142e4..a76e3e7a 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -6,7 +6,7 @@ import sys import unittest import site -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils.command.install import install from distutils.command import install as install_module @@ -203,4 +203,4 @@ def test_suite(): return unittest.makeSuite(InstallTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 86db4a12..4d8c00ac 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -6,6 +6,7 @@ import getpass from distutils.command.install_data import install_data from distutils.tests import support +from test.support import run_unittest class InstallDataTestCase(support.TempdirManager, support.LoggingSilencer, @@ -73,4 +74,4 @@ def test_suite(): return unittest.makeSuite(InstallDataTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index aa8a4e60..d953157b 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -6,6 +6,7 @@ import getpass from distutils.command.install_headers import install_headers from distutils.tests import support +from test.support import run_unittest class InstallHeadersTestCase(support.TempdirManager, support.LoggingSilencer, @@ -37,4 +38,4 @@ def test_suite(): return unittest.makeSuite(InstallHeadersTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 46363046..fddaabe1 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -7,6 +7,7 @@ from distutils.command.install_lib import install_lib from distutils.extension import Extension from distutils.tests import support from distutils.errors import DistutilsOptionError +from test.support import run_unittest class InstallLibTestCase(support.TempdirManager, support.LoggingSilencer, @@ -98,4 +99,4 @@ def test_suite(): return unittest.makeSuite(InstallLibTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index 08360d29..8952e744 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -7,6 +7,7 @@ from distutils.command.install_scripts import install_scripts from distutils.core import Distribution from distutils.tests import support +from test.support import run_unittest class InstallScriptsTestCase(support.TempdirManager, @@ -78,4 +79,4 @@ def test_suite(): return unittest.makeSuite(InstallScriptsTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_log.py b/tests/test_log.py index 9d40dcd7..ce66ee51 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -3,6 +3,7 @@ import sys import unittest from tempfile import NamedTemporaryFile +from test.support import run_unittest from distutils import log @@ -33,4 +34,4 @@ def test_suite(): return unittest.makeSuite(TestLog) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 570fda12..a0d62efc 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -5,6 +5,7 @@ import os from distutils.errors import DistutilsPlatformError from distutils.tests import support +from test.support import run_unittest _MANIFEST = """\ @@ -137,4 +138,4 @@ def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_register.py b/tests/test_register.py index c98e28a1..c712f56a 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -6,7 +6,7 @@ import getpass import urllib import warnings -from test.support import check_warnings +from test.support import check_warnings, run_unittest from distutils.command import register as register_module from distutils.command.register import register @@ -259,4 +259,4 @@ def test_suite(): return unittest.makeSuite(RegisterTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index d734ea0a..655e50dc 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -8,11 +8,9 @@ import sys import tempfile import warnings -from test.support import check_warnings -from test.support import captured_stdout +from test.support import captured_stdout, check_warnings, run_unittest -from distutils.command.sdist import sdist -from distutils.command.sdist import show_formats +from distutils.command.sdist import sdist, show_formats from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError, DistutilsOptionError @@ -356,4 +354,4 @@ def test_suite(): return unittest.makeSuite(SDistTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 0616c9f2..6c7eb20c 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -2,7 +2,7 @@ import unittest import os import time -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils.spawn import _nt_quote_args from distutils.spawn import spawn, find_executable @@ -55,4 +55,4 @@ def test_suite(): return unittest.makeSuite(SpawnTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_text_file.py b/tests/test_text_file.py index f1e32b6c..7e76240a 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -3,6 +3,7 @@ import os import unittest from distutils.text_file import TextFile from distutils.tests import support +from test.support import run_unittest TEST_DATA = """# test file @@ -103,4 +104,4 @@ def test_suite(): return unittest.makeSuite(TextFileTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 3a41e6fc..1bff38e9 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -1,6 +1,7 @@ """Tests for distutils.unixccompiler.""" import sys import unittest +from test.support import run_unittest from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler @@ -118,4 +119,4 @@ def test_suite(): return unittest.makeSuite(UnixCCompilerTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_upload.py b/tests/test_upload.py index 8891820d..4c6464a3 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -1,13 +1,12 @@ """Tests for distutils.command.upload.""" -import sys import os import unittest import http.client as httpclient +from test.support import run_unittest from distutils.command.upload import upload from distutils.core import Distribution -from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase PYPIRC_LONG_PASSWORD = """\ @@ -136,4 +135,4 @@ def test_suite(): return unittest.makeSuite(uploadTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_util.py b/tests/test_util.py index f40caac8..8ff5ae20 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -3,6 +3,7 @@ import os import sys import unittest from copy import copy +from test.support import run_unittest from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import (get_platform, convert_path, change_root, @@ -274,4 +275,4 @@ def test_suite(): return unittest.makeSuite(UtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_version.py b/tests/test_version.py index 980f83f2..15f14c7d 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -2,6 +2,7 @@ import unittest from distutils.version import LooseVersion from distutils.version import StrictVersion +from test.support import run_unittest class VersionTestCase(unittest.TestCase): @@ -67,4 +68,4 @@ def test_suite(): return unittest.makeSuite(VersionTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_versionpredicate.py b/tests/test_versionpredicate.py index 8a60dbe8..28ae09dc 100644 --- a/tests/test_versionpredicate.py +++ b/tests/test_versionpredicate.py @@ -4,6 +4,10 @@ import distutils.versionpredicate import doctest +from test.support import run_unittest def test_suite(): return doctest.DocTestSuite(distutils.versionpredicate) + +if __name__ == '__main__': + run_unittest(test_suite()) -- cgit v1.2.1 From b33ff8df881999f3d2ec8b99e15f5b66faca4837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Thu, 3 Feb 2011 00:12:18 +0000 Subject: Merged revisions 86236,86240,86332,86340,87271,87273,87447 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To comply with the 2.x doc style, the methods in trace.rst use brackets around optional arguments. The rest is a mostly straight merge, modulo support changed to test_support and use of the old super call style in test_tuple. ........ r86236 | eric.araujo | 2010-11-06 03:44:43 +0100 (sam., 06 nov. 2010) | 2 lines Make sure each test can be run standalone (./python Lib/distutils/tests/x.py) ........ r86240 | eric.araujo | 2010-11-06 05:11:59 +0100 (sam., 06 nov. 2010) | 2 lines Prevent ResourceWarnings in test_gettext ........ r86332 | eric.araujo | 2010-11-08 19:15:17 +0100 (lun., 08 nov. 2010) | 4 lines Add missing NEWS entry for a fix committed by Senthil. All recent modifications to distutils should now be covered in NEWS. ........ r86340 | eric.araujo | 2010-11-08 22:48:23 +0100 (lun., 08 nov. 2010) | 2 lines This was actually fixed for the previous alpha. ........ r87271 | eric.araujo | 2010-12-15 20:09:58 +0100 (mer., 15 déc. 2010) | 2 lines Improve trace documentation (#9264). Patch by Eli Bendersky. ........ r87273 | eric.araujo | 2010-12-15 20:30:15 +0100 (mer., 15 déc. 2010) | 2 lines Use nested method directives, rewrap long lines, fix whitespace. ........ r87447 | eric.araujo | 2010-12-23 20:13:05 +0100 (jeu., 23 déc. 2010) | 2 lines Fix typo in superclass method name ........ --- tests/__init__.py | 5 +++-- tests/test_archive_util.py | 4 ++-- tests/test_bdist_msi.py | 2 +- tests/test_build.py | 3 ++- tests/test_build_clib.py | 4 +++- tests/test_build_py.py | 3 ++- tests/test_build_scripts.py | 3 ++- tests/test_check.py | 3 ++- tests/test_clean.py | 3 ++- tests/test_cmd.py | 2 +- tests/test_config.py | 3 ++- tests/test_config_cmd.py | 3 ++- tests/test_core.py | 4 ++-- tests/test_dep_util.py | 3 ++- tests/test_dir_util.py | 3 ++- tests/test_dist.py | 4 ++-- tests/test_file_util.py | 3 ++- tests/test_filelist.py | 4 ++-- tests/test_install.py | 4 +++- tests/test_install_data.py | 3 ++- tests/test_install_headers.py | 3 ++- tests/test_install_lib.py | 3 ++- tests/test_install_scripts.py | 3 ++- tests/test_msvc9compiler.py | 3 ++- tests/test_register.py | 4 ++-- tests/test_sdist.py | 8 +++----- tests/test_spawn.py | 4 ++-- tests/test_text_file.py | 3 ++- tests/test_unixccompiler.py | 3 ++- tests/test_upload.py | 7 +++---- tests/test_util.py | 3 ++- tests/test_version.py | 3 ++- tests/test_versionpredicate.py | 4 ++++ 33 files changed, 71 insertions(+), 46 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 7bdb9124..697ff840 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -15,9 +15,10 @@ by import rather than matching pre-defined names. import os import sys import unittest +from test.test_support import run_unittest -here = os.path.dirname(__file__) +here = os.path.dirname(__file__) or os.curdir def test_suite(): @@ -32,4 +33,4 @@ def test_suite(): if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index bab91577..1f7106f8 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -12,7 +12,7 @@ from distutils.archive_util import (check_archive_formats, make_tarball, ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support -from test.test_support import check_warnings +from test.test_support import check_warnings, run_unittest try: import grp @@ -281,4 +281,4 @@ def test_suite(): return unittest.makeSuite(ArchiveUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index 7554e9f1..1c897ab0 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -11,7 +11,7 @@ class BDistMSITestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): - def test_minial(self): + def test_minimal(self): # minimal test XXX need more tests from distutils.command.bdist_msi import bdist_msi pkg_pth, dist = self.create_dist() diff --git a/tests/test_build.py b/tests/test_build.py index 3db57038..eeb8d73e 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -2,6 +2,7 @@ import unittest import os import sys +from test.test_support import run_unittest from distutils.command.build import build from distutils.tests import support @@ -51,4 +52,4 @@ def test_suite(): return unittest.makeSuite(BuildTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index d77912ae..4f4e2bc7 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -3,6 +3,8 @@ import unittest import os import sys +from test.test_support import run_unittest + from distutils.command.build_clib import build_clib from distutils.errors import DistutilsSetupError from distutils.tests import support @@ -140,4 +142,4 @@ def test_suite(): return unittest.makeSuite(BuildCLibTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 03517392..937fa0ce 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -10,6 +10,7 @@ from distutils.core import Distribution from distutils.errors import DistutilsFileError from distutils.tests import support +from test.test_support import run_unittest class BuildPyTestCase(support.TempdirManager, @@ -123,4 +124,4 @@ def test_suite(): return unittest.makeSuite(BuildPyTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index df89cdec..4da93cc1 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -8,6 +8,7 @@ from distutils.core import Distribution import sysconfig from distutils.tests import support +from test.test_support import run_unittest class BuildScriptsTestCase(support.TempdirManager, @@ -108,4 +109,4 @@ def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_check.py b/tests/test_check.py index 7c56c043..4ea83dcb 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -1,5 +1,6 @@ """Tests for distutils.command.check.""" import unittest +from test.test_support import run_unittest from distutils.command.check import check, HAS_DOCUTILS from distutils.tests import support @@ -95,4 +96,4 @@ def test_suite(): return unittest.makeSuite(CheckTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_clean.py b/tests/test_clean.py index dbc4ee2a..2d1610da 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -6,6 +6,7 @@ import getpass from distutils.command.clean import clean from distutils.tests import support +from test.test_support import run_unittest class cleanTestCase(support.TempdirManager, support.LoggingSilencer, @@ -47,4 +48,4 @@ def test_suite(): return unittest.makeSuite(cleanTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 97cdb8ad..e0740996 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -99,7 +99,7 @@ class CommandTestCase(unittest.TestCase): def test_ensure_dirname(self): cmd = self.cmd - cmd.option1 = os.path.dirname(__file__) + cmd.option1 = os.path.dirname(__file__) or os.curdir cmd.ensure_dirname('option1') cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') diff --git a/tests/test_config.py b/tests/test_config.py index 8d71234d..cfd096eb 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -11,6 +11,7 @@ from distutils.log import set_threshold from distutils.log import WARN from distutils.tests import support +from test.test_support import run_unittest PYPIRC = """\ [distutils] @@ -119,4 +120,4 @@ def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index fcb798e7..a36a1d5b 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -2,6 +2,7 @@ import unittest import os import sys +from test.test_support import run_unittest from distutils.command.config import dump_file, config from distutils.tests import support @@ -86,4 +87,4 @@ def test_suite(): return unittest.makeSuite(ConfigTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_core.py b/tests/test_core.py index 74d1f3b2..0d979bcd 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,7 +6,7 @@ import os import shutil import sys import test.test_support -from test.test_support import captured_stdout +from test.test_support import captured_stdout, run_unittest import unittest from distutils.tests import support @@ -105,4 +105,4 @@ def test_suite(): return unittest.makeSuite(CoreTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_dep_util.py b/tests/test_dep_util.py index 35508196..75104343 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -6,6 +6,7 @@ import time from distutils.dep_util import newer, newer_pairwise, newer_group from distutils.errors import DistutilsFileError from distutils.tests import support +from test.test_support import run_unittest class DepUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -77,4 +78,4 @@ def test_suite(): return unittest.makeSuite(DepUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 84a0ec6c..693f77cf 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -10,6 +10,7 @@ from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, from distutils import log from distutils.tests import support +from test.test_support import run_unittest class DirUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -112,4 +113,4 @@ def test_suite(): return unittest.makeSuite(DirUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_dist.py b/tests/test_dist.py index 8ab7e9fc..ba606381 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -11,7 +11,7 @@ import textwrap from distutils.dist import Distribution, fix_help_options, DistributionMetadata from distutils.cmd import Command import distutils.dist -from test.test_support import TESTFN, captured_stdout +from test.test_support import TESTFN, captured_stdout, run_unittest from distutils.tests import support class test_dist(Command): @@ -433,4 +433,4 @@ def test_suite(): return suite if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_file_util.py b/tests/test_file_util.py index dbc62839..7dbcf52c 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -6,6 +6,7 @@ import shutil from distutils.file_util import move_file, write_file, copy_file from distutils import log from distutils.tests import support +from test.test_support import run_unittest class FileUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -77,4 +78,4 @@ def test_suite(): return unittest.makeSuite(FileUtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 32c56c75..be11ea5d 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -1,7 +1,7 @@ """Tests for distutils.filelist.""" from os.path import join import unittest -from test.test_support import captured_stdout +from test.test_support import captured_stdout, run_unittest from distutils.filelist import glob_to_re, FileList from distutils import debug @@ -82,4 +82,4 @@ def test_suite(): return unittest.makeSuite(FileListTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install.py b/tests/test_install.py index c834b91b..4f976f34 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -3,6 +3,8 @@ import os import unittest +from test.test_support import run_unittest + from distutils.command.install import install from distutils.core import Distribution @@ -52,4 +54,4 @@ def test_suite(): return unittest.makeSuite(InstallTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 86db4a12..47756944 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -6,6 +6,7 @@ import getpass from distutils.command.install_data import install_data from distutils.tests import support +from test.test_support import run_unittest class InstallDataTestCase(support.TempdirManager, support.LoggingSilencer, @@ -73,4 +74,4 @@ def test_suite(): return unittest.makeSuite(InstallDataTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index aa8a4e60..b37224b9 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -6,6 +6,7 @@ import getpass from distutils.command.install_headers import install_headers from distutils.tests import support +from test.test_support import run_unittest class InstallHeadersTestCase(support.TempdirManager, support.LoggingSilencer, @@ -37,4 +38,4 @@ def test_suite(): return unittest.makeSuite(InstallHeadersTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 754ea636..4d863089 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -7,6 +7,7 @@ from distutils.command.install_lib import install_lib from distutils.extension import Extension from distutils.tests import support from distutils.errors import DistutilsOptionError +from test.test_support import run_unittest class InstallLibTestCase(support.TempdirManager, support.LoggingSilencer, @@ -103,4 +104,4 @@ def test_suite(): return unittest.makeSuite(InstallLibTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index 08360d29..46085458 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -7,6 +7,7 @@ from distutils.command.install_scripts import install_scripts from distutils.core import Distribution from distutils.tests import support +from test.test_support import run_unittest class InstallScriptsTestCase(support.TempdirManager, @@ -78,4 +79,4 @@ def test_suite(): return unittest.makeSuite(InstallScriptsTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index b8e22094..e155dbfe 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -5,6 +5,7 @@ import os from distutils.errors import DistutilsPlatformError from distutils.tests import support +from test.test_support import run_unittest _MANIFEST = """\ @@ -137,4 +138,4 @@ def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_register.py b/tests/test_register.py index 915427b7..dd60f8c8 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -7,7 +7,7 @@ import getpass import urllib2 import warnings -from test.test_support import check_warnings +from test.test_support import check_warnings, run_unittest from distutils.command import register as register_module from distutils.command.register import register @@ -258,4 +258,4 @@ def test_suite(): return unittest.makeSuite(RegisterTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index b9d86bb0..ab8c3d91 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -24,11 +24,9 @@ import sys import tempfile import warnings -from test.test_support import check_warnings -from test.test_support import captured_stdout +from test.test_support import captured_stdout, check_warnings, run_unittest -from distutils.command.sdist import sdist -from distutils.command.sdist import show_formats +from distutils.command.sdist import sdist, show_formats from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsExecError, DistutilsOptionError @@ -426,4 +424,4 @@ def test_suite(): return unittest.makeSuite(SDistTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 6caf0390..defa54d8 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -2,7 +2,7 @@ import unittest import os import time -from test.test_support import captured_stdout +from test.test_support import captured_stdout, run_unittest from distutils.spawn import _nt_quote_args from distutils.spawn import spawn, find_executable @@ -57,4 +57,4 @@ def test_suite(): return unittest.makeSuite(SpawnTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_text_file.py b/tests/test_text_file.py index f1e32b6c..ce19cd4d 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -3,6 +3,7 @@ import os import unittest from distutils.text_file import TextFile from distutils.tests import support +from test.test_support import run_unittest TEST_DATA = """# test file @@ -103,4 +104,4 @@ def test_suite(): return unittest.makeSuite(TextFileTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 3f233e28..40c908a2 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -1,6 +1,7 @@ """Tests for distutils.unixccompiler.""" import sys import unittest +from test.test_support import run_unittest from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler @@ -126,4 +127,4 @@ def test_suite(): return unittest.makeSuite(UnixCCompilerTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_upload.py b/tests/test_upload.py index f45ee41e..99111999 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -1,14 +1,13 @@ -"""Tests for distutils.command.upload.""" # -*- encoding: utf8 -*- -import sys +"""Tests for distutils.command.upload.""" import os import unittest +from test.test_support import run_unittest from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution -from distutils.tests import support from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase PYPIRC_LONG_PASSWORD = """\ @@ -129,4 +128,4 @@ def test_suite(): return unittest.makeSuite(uploadTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_util.py b/tests/test_util.py index 981ad000..67cd4cc7 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,6 +1,7 @@ """Tests for distutils.util.""" import sys import unittest +from test.test_support import run_unittest from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import byte_compile @@ -21,4 +22,4 @@ def test_suite(): return unittest.makeSuite(UtilTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_version.py b/tests/test_version.py index 1d9fbc79..21899564 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -2,6 +2,7 @@ import unittest from distutils.version import LooseVersion from distutils.version import StrictVersion +from test.test_support import run_unittest class VersionTestCase(unittest.TestCase): @@ -67,4 +68,4 @@ def test_suite(): return unittest.makeSuite(VersionTestCase) if __name__ == "__main__": - unittest.main(defaultTest="test_suite") + run_unittest(test_suite()) diff --git a/tests/test_versionpredicate.py b/tests/test_versionpredicate.py index 8a60dbe8..1d6c8d5a 100644 --- a/tests/test_versionpredicate.py +++ b/tests/test_versionpredicate.py @@ -4,6 +4,10 @@ import distutils.versionpredicate import doctest +from test.test_support import run_unittest def test_suite(): return doctest.DocTestSuite(distutils.versionpredicate) + +if __name__ == '__main__': + run_unittest(test_suite()) -- cgit v1.2.1 From d3d4f43f285902d969a2cfb2e8f4523c4937a299 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 13 Feb 2011 10:00:57 +0000 Subject: Bump for 3.2rc3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 4bb50123..8a7e3ff9 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2rc2" +__version__ = "3.2rc3" #--end constants-- -- cgit v1.2.1 From 41ef9736602618dc38120340185ef00b954058ca Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 20 Feb 2011 10:29:04 +0000 Subject: Version bump to 3.2 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8a7e3ff9..49b6d517 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2rc3" +__version__ = "3.2" #--end constants-- -- cgit v1.2.1 From d6f2a6b6356b8963212a475e3488df3bb585fb80 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 20 Feb 2011 10:37:07 +0000 Subject: Bump trunk to 3.3 alpha 0. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 49b6d517..bd7d9dd6 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2" +__version__ = "3.3a0" #--end constants-- -- cgit v1.2.1 From 6a40d111b0a9aeab8353793baaf6cfd4e61089f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lemburg?= Date: Fri, 25 Feb 2011 15:42:01 +0000 Subject: Normalize the encoding names for Latin-1 and UTF-8 to 'latin-1' and 'utf-8'. These are optimized in the Python Unicode implementation to result in more direct processing, bypassing the codec registry. Also see issue11303. --- command/bdist_wininst.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index b2e2fc6d..b886055f 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -263,11 +263,11 @@ class bdist_wininst(Command): cfgdata = cfgdata + b"\0" if self.pre_install_script: # We need to normalize newlines, so we open in text mode and - # convert back to bytes. "latin1" simply avoids any possible + # convert back to bytes. "latin-1" simply avoids any possible # failures. with open(self.pre_install_script, "r", - encoding="latin1") as script: - script_data = script.read().encode("latin1") + encoding="latin-1") as script: + script_data = script.read().encode("latin-1") cfgdata = cfgdata + script_data + b"\n\0" else: # empty pre-install script -- cgit v1.2.1 From 3860cc209929f5e6ec927d71cc41420665311434 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 14 Mar 2011 20:03:36 -0400 Subject: Issue #3080: skip test_bdist_rpm if sys.executable is not encodable to UTF-8 --- tests/test_bdist_rpm.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 804fb135..030933f1 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -28,6 +28,11 @@ class BuildRpmTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): + try: + sys.executable.encode("UTF-8") + except UnicodeEncodeError: + raise unittest.SkipTest("sys.executable is not encodable to UTF-8") + super(BuildRpmTestCase, self).setUp() self.old_location = os.getcwd() self.old_sys_argv = sys.argv, sys.argv[:] -- cgit v1.2.1 From 5bff24b86b4ad1400e9f063f3e371cac0fdd0e59 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Tue, 15 Mar 2011 21:02:59 +0100 Subject: On behalf of Tarek: Issue #11501: disutils.archive_utils.make_zipfile no longer fails if zlib is not installed. Instead, the zipfile.ZIP_STORED compression is used to create the ZipFile. Patch by Natalia B. Bidart. --- archive_util.py | 19 ++++++++++++------- tests/test_archive_util.py | 42 ++++++++++++++++++++++++++++++++++++++---- tests/test_bdist_dumb.py | 8 ++++++++ tests/test_sdist.py | 15 ++++++++++++++- 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/archive_util.py b/archive_util.py index 6dd0445d..c06eba35 100644 --- a/archive_util.py +++ b/archive_util.py @@ -9,6 +9,12 @@ import os from warnings import warn import sys +try: + import zipfile +except ImportError: + zipfile = None + + from distutils.errors import DistutilsExecError from distutils.spawn import spawn from distutils.dir_util import mkpath @@ -74,11 +80,6 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): available, raises DistutilsExecError. Returns the name of the output zip file. """ - try: - import zipfile - except ImportError: - zipfile = None - zip_filename = base_name + ".zip" mkpath(os.path.dirname(zip_filename), dry_run=dry_run) @@ -105,8 +106,12 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): zip_filename, base_dir) if not dry_run: - zip = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) + try: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) + except RuntimeError: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_STORED) for dirpath, dirnames, filenames in os.walk(base_dir): for name in filenames: diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index aff42652..f9698495 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -7,12 +7,13 @@ import tarfile from os.path import splitdrive import warnings +from distutils import archive_util from distutils.archive_util import (check_archive_formats, make_tarball, make_zipfile, make_archive, ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support -from test.support import check_warnings, run_unittest +from test.support import check_warnings, run_unittest, patch try: import zipfile @@ -20,10 +21,18 @@ try: except ImportError: ZIP_SUPPORT = find_executable('zip') +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + + class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_make_tarball(self): # creating something to tar tmpdir = self.mkdtemp() @@ -84,8 +93,9 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir2, 'archive') return tmpdir, tmpdir2, base_name - @unittest.skipUnless(find_executable('tar') and find_executable('gzip'), - 'Need the tar command to run') + @unittest.skipUnless(find_executable('tar') and find_executable('gzip') + and ZLIB_SUPPORT, + 'Need the tar, gzip and zlib command to run') def test_tarfile_vs_tar(self): tmpdir, tmpdir2, base_name = self._create_files() old_dir = os.getcwd() @@ -169,7 +179,8 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertTrue(not os.path.exists(tarball)) self.assertEqual(len(w.warnings), 1) - @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') + @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, + 'Need zip and zlib support to run') def test_make_zipfile(self): # creating something to tar tmpdir = self.mkdtemp() @@ -182,6 +193,29 @@ class ArchiveUtilTestCase(support.TempdirManager, # check if the compressed tarball was created tarball = base_name + '.zip' + self.assertTrue(os.path.exists(tarball)) + + @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') + def test_make_zipfile_no_zlib(self): + patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError + + called = [] + zipfile_class = zipfile.ZipFile + def fake_zipfile(*a, **kw): + if kw.get('compression', None) == zipfile.ZIP_STORED: + called.append((a, kw)) + return zipfile_class(*a, **kw) + + patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile) + + # create something to tar and compress + tmpdir, tmpdir2, base_name = self._create_files() + make_zipfile(base_name, tmpdir) + + tarball = base_name + '.zip' + self.assertEqual(called, + [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) + self.assertTrue(os.path.exists(tarball)) def test_check_archive_formats(self): self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index cc37fef8..55ba58d1 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -18,6 +18,13 @@ setup(name='foo', version='0.1', py_modules=['foo'], """ +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + + class BuildDumbTestCase(support.TempdirManager, support.LoggingSilencer, support.EnvironGuard, @@ -34,6 +41,7 @@ class BuildDumbTestCase(support.TempdirManager, sys.argv[:] = self.old_sys_argv[1] super(BuildDumbTestCase, self).tearDown() + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_simple_built(self): # let's create a simple package diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 655e50dc..eaf39a45 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -40,6 +40,13 @@ somecode%(sep)sdoc.dat somecode%(sep)sdoc.txt """ +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + + class SDistTestCase(PyPIRCCommandTestCase): def setUp(self): @@ -78,6 +85,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.warn = _warn return dist, cmd + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_prune_file_list(self): # this test creates a package with some vcs dirs in it # and launch sdist to make sure they get pruned @@ -119,6 +127,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # making sure everything has been pruned correctly self.assertEqual(len(content), 4) + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_make_distribution(self): # check if tar and gzip are installed @@ -153,6 +162,7 @@ class SDistTestCase(PyPIRCCommandTestCase): result.sort() self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_add_defaults(self): # http://bugs.python.org/issue2279 @@ -218,6 +228,7 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: f.close() + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_metadata_check_option(self): # testing the `medata-check` option dist, cmd = self.get_cmd(metadata={}) @@ -277,7 +288,7 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.formats = 'supazipa' self.assertRaises(DistutilsOptionError, cmd.finalize_options) - + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_get_file_list(self): # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() @@ -318,6 +329,7 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(len(manifest2), 6) self.assertIn('doc2.txt', manifest2[-1]) + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_manifest_marker(self): # check that autogenerated MANIFESTs have a marker dist, cmd = self.get_cmd() @@ -334,6 +346,7 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(manifest[0], '# file GENERATED by distutils, do NOT edit') + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() -- cgit v1.2.1 From 877161a90666a528acaa1169d33db58abbf2c964 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Wed, 16 Mar 2011 11:05:33 +0200 Subject: #11565: Fix several typos. Patch by Piotr Kasprzyk. --- cmd.py | 2 +- cygwinccompiler.py | 2 +- tests/test_clean.py | 2 +- tests/test_install.py | 2 +- tests/test_sdist.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd.py b/cmd.py index ae4efc7e..5b1d085c 100644 --- a/cmd.py +++ b/cmd.py @@ -359,7 +359,7 @@ class Command: not self.force, dry_run=self.dry_run) def move_file (self, src, dst, level=1): - """Move a file respectin dry-run flag.""" + """Move a file respecting dry-run flag.""" return file_util.move_file(src, dst, dry_run=self.dry_run) def spawn(self, cmd, search_path=1, level=1): diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 95fa3ed3..536aa6b6 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -155,7 +155,7 @@ class CygwinCCompiler(UnixCCompiler): self.dll_libraries = get_msvcr() def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): - """Compiles the source by spawing GCC and windres if needed.""" + """Compiles the source by spawning GCC and windres if needed.""" if ext == '.rc' or ext == '.res': # gcc needs '.res' and '.rc' compiled to object files !!! try: diff --git a/tests/test_clean.py b/tests/test_clean.py index 649855f7..eb8958bf 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -39,7 +39,7 @@ class cleanTestCase(support.TempdirManager, self.assertTrue(not os.path.exists(path), '%s was not removed' % path) - # let's run the command again (should spit warnings but suceed) + # let's run the command again (should spit warnings but succeed) cmd.all = 1 cmd.ensure_finalized() cmd.run() diff --git a/tests/test_install.py b/tests/test_install.py index a76e3e7a..ed69b0cb 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -62,7 +62,7 @@ class InstallTestCase(support.TempdirManager, if sys.version < '2.6': return - # preparing the environement for the test + # preparing the environment for the test self.old_user_base = site.USER_BASE self.old_user_site = site.USER_SITE self.tmpdir = self.mkdtemp() diff --git a/tests/test_sdist.py b/tests/test_sdist.py index eaf39a45..c7dd47fb 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -311,7 +311,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # adding a file self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') - # make sure build_py is reinitinialized, like a fresh run + # make sure build_py is reinitialized, like a fresh run build_py = dist.get_command_obj('build_py') build_py.finalized = False build_py.ensure_finalized() -- cgit v1.2.1 From 14b28f28de2b0ac9e40d9838ea72df93fb661b51 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Wed, 16 Mar 2011 12:34:31 +0200 Subject: #11565: Fix several typos. Patch by Piotr Kasprzyk. --- cmd.py | 2 +- tests/test_clean.py | 2 +- tests/test_sdist.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd.py b/cmd.py index dc40c14d..9ad5657e 100644 --- a/cmd.py +++ b/cmd.py @@ -377,7 +377,7 @@ class Command: dry_run=self.dry_run) def move_file (self, src, dst, level=1): - """Move a file respectin dry-run flag.""" + """Move a file respecting dry-run flag.""" return file_util.move_file(src, dst, dry_run = self.dry_run) def spawn (self, cmd, search_path=1, level=1): diff --git a/tests/test_clean.py b/tests/test_clean.py index 2d1610da..7b988f7f 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -39,7 +39,7 @@ class cleanTestCase(support.TempdirManager, self.assertTrue(not os.path.exists(path), '%s was not removed' % path) - # let's run the command again (should spit warnings but suceed) + # let's run the command again (should spit warnings but succeed) cmd.all = 1 cmd.ensure_finalized() cmd.run() diff --git a/tests/test_sdist.py b/tests/test_sdist.py index ab8c3d91..54a32b88 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -370,7 +370,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # adding a file self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') - # make sure build_py is reinitinialized, like a fresh run + # make sure build_py is reinitialized, like a fresh run build_py = dist.get_command_obj('build_py') build_py.finalized = False build_py.ensure_finalized() -- cgit v1.2.1 From 623c67f82759f75b20834aa82085593d553c6ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Thu, 14 Apr 2011 03:49:19 +0200 Subject: Fix improper tests in RegisterTestCase --- tests/test_register.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_register.py b/tests/test_register.py index c712f56a..cb72a115 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -137,7 +137,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): # let's see what the server received : we should # have 2 similar requests - self.assertTrue(self.conn.reqs, 2) + self.assertEqual(len(self.conn.reqs), 2) req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) @@ -169,7 +169,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.input # we should have send a request - self.assertTrue(self.conn.reqs, 1) + self.assertEqual(len(self.conn.reqs), 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEqual(headers['Content-length'], '608') @@ -187,7 +187,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.input # we should have send a request - self.assertTrue(self.conn.reqs, 1) + self.assertEqual(len(self.conn.reqs), 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEqual(headers['Content-length'], '290') -- cgit v1.2.1 From fb7e64a2c2d32923a81b4ea701e61d65e970bdd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Thu, 14 Apr 2011 03:49:19 +0200 Subject: Fix improper tests in RegisterTestCase --- tests/test_register.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_register.py b/tests/test_register.py index dd60f8c8..bf634870 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -138,7 +138,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): # let's see what the server received : we should # have 2 similar requests - self.assertTrue(self.conn.reqs, 2) + self.assertEqual(len(self.conn.reqs), 2) req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) self.assertEqual(req2['Content-length'], req1['Content-length']) @@ -168,7 +168,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.raw_input # we should have send a request - self.assertTrue(self.conn.reqs, 1) + self.assertEqual(len(self.conn.reqs), 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEqual(headers['Content-length'], '608') @@ -186,7 +186,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): del register_module.raw_input # we should have send a request - self.assertTrue(self.conn.reqs, 1) + self.assertEqual(len(self.conn.reqs), 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEqual(headers['Content-length'], '290') -- cgit v1.2.1 From f55d96faafe20a6b938a4706834abb69aa0b48c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 17 Apr 2011 14:27:07 +0200 Subject: Fix resource warning found manually --- command/sdist.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 1118060c..fdbebd7e 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -294,17 +294,20 @@ class sdist(Command): join_lines=1, lstrip_ws=1, rstrip_ws=1, collapse_join=1) - while True: - line = template.readline() - if line is None: # end of file - break - - try: - self.filelist.process_template_line(line) - except DistutilsTemplateError as msg: - self.warn("%s, line %d: %s" % (template.filename, - template.current_line, - msg)) + try: + while True: + line = template.readline() + if line is None: # end of file + break + + try: + self.filelist.process_template_line(line) + except DistutilsTemplateError as msg: + self.warn("%s, line %d: %s" % (template.filename, + template.current_line, + msg)) + finally: + template.close() def prune_file_list(self): """Prune off branches that might slip into the file list as created -- cgit v1.2.1 From c9e28986af40be12d8e52c4e2b5445946cfeb640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 1 May 2011 02:05:58 +0200 Subject: Fix file handle leak --- command/sdist.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 0c3b0b55..cf8982bd 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -306,17 +306,20 @@ class sdist(Command): rstrip_ws=1, collapse_join=1) - while 1: - line = template.readline() - if line is None: # end of file - break - - try: - self.filelist.process_template_line(line) - except DistutilsTemplateError, msg: - self.warn("%s, line %d: %s" % (template.filename, - template.current_line, - msg)) + try: + while 1: + line = template.readline() + if line is None: # end of file + break + + try: + self.filelist.process_template_line(line) + except DistutilsTemplateError, msg: + self.warn("%s, line %d: %s" % (template.filename, + template.current_line, + msg)) + finally: + template.close() def prune_file_list(self): """Prune off branches that might slip into the file list as created -- cgit v1.2.1 From 540753de877e149efb1ff20e132031d004ab33de Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 8 May 2011 09:03:36 +0200 Subject: Bump to 3.2.1b1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 49b6d517..17c89ee8 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2" +__version__ = "3.2.1b1" #--end constants-- -- cgit v1.2.1 From dcd99c95a5aef0137ccf177c6830de9d7c5d52ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 8 May 2011 16:27:13 +0200 Subject: Make test_distutils pass without zlib (fixes #9435) --- tests/test_sdist.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 54a32b88..61f9c1f7 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -388,6 +388,7 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(len(manifest2), 6) self.assertIn('doc2.txt', manifest2[-1]) + @unittest.skipUnless(zlib, "requires zlib") def test_manifest_marker(self): # check that autogenerated MANIFESTs have a marker dist, cmd = self.get_cmd() @@ -404,6 +405,7 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(manifest[0], '# file GENERATED by distutils, do NOT edit') + @unittest.skipUnless(zlib, "requires zlib") def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() -- cgit v1.2.1 From 942c7a73182fd88a290e89dc3e06ac2871cfbced Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 11 May 2011 00:14:28 +0200 Subject: Close #10419, issue #6011: build_scripts command of distutils handles correctly non-ASCII path (path to the Python executable). Open and write the script in binary mode, but ensure that the shebang is decodable from UTF-8 and from the encoding of the script. --- command/build_scripts.py | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 8b08bfea..a43a7c30 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -11,9 +11,10 @@ from distutils.core import Command from distutils.dep_util import newer from distutils.util import convert_path, Mixin2to3 from distutils import log +import tokenize # check if Python is called on the first line with this expression -first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$') +first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$') class build_scripts(Command): @@ -74,12 +75,14 @@ class build_scripts(Command): # that way, we'll get accurate feedback if we can read the # script. try: - f = open(script, "r") + f = open(script, "rb") except IOError: if not self.dry_run: raise f = None else: + encoding, lines = tokenize.detect_encoding(f.readline) + f.seek(0) first_line = f.readline() if not first_line: self.warn("%s is an empty file (skipping)" % script) @@ -88,25 +91,45 @@ class build_scripts(Command): match = first_line_re.match(first_line) if match: adjust = True - post_interp = match.group(1) or '' + post_interp = match.group(1) or b'' if adjust: log.info("copying and adjusting %s -> %s", script, self.build_dir) updated_files.append(outfile) if not self.dry_run: - outf = open(outfile, "w") if not sysconfig.python_build: - outf.write("#!%s%s\n" % - (self.executable, - post_interp)) + executable = self.executable else: - outf.write("#!%s%s\n" % - (os.path.join( + executable = os.path.join( sysconfig.get_config_var("BINDIR"), "python%s%s" % (sysconfig.get_config_var("VERSION"), - sysconfig.get_config_var("EXE"))), - post_interp)) + sysconfig.get_config_var("EXE"))) + executable = os.fsencode(executable) + shebang = b"#!" + executable + post_interp + b"\n" + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: + raise ValueError( + "The shebang ({!r}) is not decodable " + "from utf-8".format(shebang)) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + try: + shebang.decode(encoding) + except UnicodeDecodeError: + raise ValueError( + "The shebang ({!r}) is not decodable " + "from the script encoding ({})" + .format(shebang, encoding)) + outf = open(outfile, "wb") + outf.write(shebang) outf.writelines(f.readlines()) outf.close() if f: -- cgit v1.2.1 From aadd806fbbb624e62206009ca056d30847176d9a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 11 May 2011 00:14:28 +0200 Subject: Close #10419, issue #6011: build_scripts command of distutils handles correctly non-ASCII path (path to the Python executable). Open and write the script in binary mode, but ensure that the shebang is decodable from UTF-8 and from the encoding of the script. --- command/build_scripts.py | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 8b08bfea..a43a7c30 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -11,9 +11,10 @@ from distutils.core import Command from distutils.dep_util import newer from distutils.util import convert_path, Mixin2to3 from distutils import log +import tokenize # check if Python is called on the first line with this expression -first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$') +first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$') class build_scripts(Command): @@ -74,12 +75,14 @@ class build_scripts(Command): # that way, we'll get accurate feedback if we can read the # script. try: - f = open(script, "r") + f = open(script, "rb") except IOError: if not self.dry_run: raise f = None else: + encoding, lines = tokenize.detect_encoding(f.readline) + f.seek(0) first_line = f.readline() if not first_line: self.warn("%s is an empty file (skipping)" % script) @@ -88,25 +91,45 @@ class build_scripts(Command): match = first_line_re.match(first_line) if match: adjust = True - post_interp = match.group(1) or '' + post_interp = match.group(1) or b'' if adjust: log.info("copying and adjusting %s -> %s", script, self.build_dir) updated_files.append(outfile) if not self.dry_run: - outf = open(outfile, "w") if not sysconfig.python_build: - outf.write("#!%s%s\n" % - (self.executable, - post_interp)) + executable = self.executable else: - outf.write("#!%s%s\n" % - (os.path.join( + executable = os.path.join( sysconfig.get_config_var("BINDIR"), "python%s%s" % (sysconfig.get_config_var("VERSION"), - sysconfig.get_config_var("EXE"))), - post_interp)) + sysconfig.get_config_var("EXE"))) + executable = os.fsencode(executable) + shebang = b"#!" + executable + post_interp + b"\n" + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: + raise ValueError( + "The shebang ({!r}) is not decodable " + "from utf-8".format(shebang)) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + try: + shebang.decode(encoding) + except UnicodeDecodeError: + raise ValueError( + "The shebang ({!r}) is not decodable " + "from the script encoding ({})" + .format(shebang, encoding)) + outf = open(outfile, "wb") + outf.write(shebang) outf.writelines(f.readlines()) outf.close() if f: -- cgit v1.2.1 From 8c2ad1791ef7504de9f6483af7617da21cc53daf Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 11 May 2011 00:57:29 +0200 Subject: Issue #10419: Fix build_scripts command of distutils to handle correctly non-ASCII scripts. Open and write the script in binary mode, but ensure that the shebang is decodable from UTF-8 and from the encoding of the script. --- command/build_scripts.py | 47 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 8b08bfea..b3c767e3 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -11,9 +11,11 @@ from distutils.core import Command from distutils.dep_util import newer from distutils.util import convert_path, Mixin2to3 from distutils import log +import sys +import tokenize # check if Python is called on the first line with this expression -first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$') +first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$') class build_scripts(Command): @@ -74,12 +76,14 @@ class build_scripts(Command): # that way, we'll get accurate feedback if we can read the # script. try: - f = open(script, "r") + f = open(script, "rb") except IOError: if not self.dry_run: raise f = None else: + encoding, lines = tokenize.detect_encoding(f.readline) + f.seek(0) first_line = f.readline() if not first_line: self.warn("%s is an empty file (skipping)" % script) @@ -88,25 +92,46 @@ class build_scripts(Command): match = first_line_re.match(first_line) if match: adjust = True - post_interp = match.group(1) or '' + post_interp = match.group(1) or b'' if adjust: log.info("copying and adjusting %s -> %s", script, self.build_dir) updated_files.append(outfile) if not self.dry_run: - outf = open(outfile, "w") if not sysconfig.python_build: - outf.write("#!%s%s\n" % - (self.executable, - post_interp)) + executable = self.executable else: - outf.write("#!%s%s\n" % - (os.path.join( + executable = os.path.join( sysconfig.get_config_var("BINDIR"), "python%s%s" % (sysconfig.get_config_var("VERSION"), - sysconfig.get_config_var("EXE"))), - post_interp)) + sysconfig.get_config_var("EXE"))) + executable = executable.encode(sys.getfilesystemencoding(), + 'surrogateescape') + shebang = b"#!" + executable + post_interp + b"\n" + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: + raise ValueError( + "The shebang ({!r}) is not decodable " + "from utf-8".format(shebang)) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + try: + shebang.decode(encoding) + except UnicodeDecodeError: + raise ValueError( + "The shebang ({!r}) is not decodable " + "from the script encoding ({})" + .format(shebang, encoding)) + outf = open(outfile, "wb") + outf.write(shebang) outf.writelines(f.readlines()) outf.close() if f: -- cgit v1.2.1 From 563a44dea6e49eff7883c6c9c8b08a887e096738 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 15 May 2011 16:44:27 +0200 Subject: Issue #9516: avoid errors in sysconfig when MACOSX_DEPLOYMENT_TARGET is set in shell. Without this patch python will fail to start properly when the environment variable MACOSX_DEPLOYMENT_TARGET is set on MacOSX and has a value that is not compatible with the value during Python's build. This is caused by code in sysconfig that was only meant to be used in disutils. --- sysconfig.py | 2 +- tests/test_build_ext.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++++- util.py | 4 +--- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 33cc5a38..d206e0cd 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -389,7 +389,7 @@ def _init_posix(): cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') if cur_target == '': cur_target = cfg_target - os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = cfg_target elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')): my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' % (cur_target, cfg_target)) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 86568ebb..46dcb5ed 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -3,12 +3,13 @@ import os import tempfile import shutil from StringIO import StringIO +import textwrap from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig from distutils.tests import support -from distutils.errors import DistutilsSetupError +from distutils.errors import DistutilsSetupError, CompileError import unittest from test import test_support @@ -430,6 +431,59 @@ class BuildExtTestCase(support.TempdirManager, wanted = os.path.join(cmd.build_lib, 'UpdateManager', 'fdsend' + ext) self.assertEqual(ext_path, wanted) + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target(self): + self._try_compile_deployment_target() + + orig_environ = os.environ + os.environ = orig_environ.copy() + self.addCleanup(setattr, os, 'environ', orig_environ) + + os.environ['MACOSX_DEPLOYMENT_TARGET']='10.1' + self._try_compile_deployment_target() + + + def _try_compile_deployment_target(self): + deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') + + with open(deptarget_c, 'w') as fp: + fp.write(textwrap.dedent('''\ + #include + + int dummy; + + #if TARGET != MAC_OS_X_VERSION_MIN_REQUIRED + #error "Unexpected target" + #endif + + ''')) + + target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') + target = tuple(map(int, target.split('.'))) + target = '%02d%01d0' % target + + deptarget_ext = Extension( + 'deptarget', + [deptarget_c], + extra_compile_args=['-DTARGET=%s'%(target,)], + ) + dist = Distribution({ + 'name': 'deptarget', + 'ext_modules': [deptarget_ext] + }) + dist.package_dir = self.tmp_dir + cmd = build_ext(dist) + cmd.build_lib = self.tmp_dir + cmd.build_temp = self.tmp_dir + + try: + old_stdout = sys.stdout + cmd.ensure_finalized() + cmd.run() + + except CompileError: + self.fail("Wrong deployment target during compilation") + def test_suite(): return unittest.makeSuite(BuildExtTestCase) diff --git a/util.py b/util.py index f06e4fdf..6c49f0b1 100644 --- a/util.py +++ b/util.py @@ -97,9 +97,7 @@ def get_platform (): from distutils.sysconfig import get_config_vars cfgvars = get_config_vars() - macver = os.environ.get('MACOSX_DEPLOYMENT_TARGET') - if not macver: - macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') if 1: # Always calculate the release of the running machine, -- cgit v1.2.1 From bc38a51782ce2e7d290cc6afbb7a22a8fde5e109 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 15 May 2011 16:46:11 +0200 Subject: Issue #9516: avoid errors in sysconfig when MACOSX_DEPLOYMENT_TARGET is set in shell. Without this patch python will fail to start properly when the environment variable MACOSX_DEPLOYMENT_TARGET is set on MacOSX and has a value that is not compatible with the value during Python's build. This is caused by code in sysconfig that was only meant to be used in disutils. --- sysconfig.py | 2 +- tests/test_build_ext.py | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_util.py | 9 +++++-- util.py | 4 +--- 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 897b7d63..06bbc01c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -428,7 +428,7 @@ def _init_posix(): cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') if cur_target == '': cur_target = cfg_target - os.putenv('MACOSX_DEPLOYMENT_TARGET', cfg_target) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = cfg_target elif [int(x) for x in cfg_target.split('.')] > [int(x) for x in cur_target.split('.')]: my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' % (cur_target, cfg_target)) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index dcba75f7..0aa99bab 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -2,6 +2,7 @@ import sys import os import shutil from io import StringIO +import textwrap from distutils.core import Distribution from distutils.command.build_ext import build_ext @@ -419,6 +420,67 @@ class BuildExtTestCase(TempdirManager, wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) self.assertEqual(wanted, path) + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target(self): + self._try_compile_deployment_target() + + orig_environ = os.environ + os.environ = orig_environ.copy() + self.addCleanup(setattr, os, 'environ', orig_environ) + + os.environ['MACOSX_DEPLOYMENT_TARGET']='10.1' + self._try_compile_deployment_target() + + + def _try_compile_deployment_target(self): + deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') + + with open(deptarget_c, 'w') as fp: + fp.write(textwrap.dedent('''\ + #include + + int dummy; + + #if TARGET != MAC_OS_X_VERSION_MIN_REQUIRED + #error "Unexpected target" + #endif + + ''')) + + target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') + target = tuple(map(int, target.split('.'))) + target = '%02d%01d0' % target + + deptarget_ext = Extension( + 'deptarget', + [deptarget_c], + extra_compile_args=['-DTARGET=%s'%(target,)], + ) + dist = Distribution({ + 'name': 'deptarget', + 'ext_modules': [deptarget_ext] + }) + dist.package_dir = self.tmp_dir + cmd = build_ext(dist) + cmd.build_lib = self.tmp_dir + cmd.build_temp = self.tmp_dir + + try: + old_stdout = sys.stdout + if not support.verbose: + # silence compiler output + sys.stdout = StringIO() + try: + cmd.ensure_finalized() + cmd.run() + finally: + sys.stdout = old_stdout + + except CompileError: + self.fail("Wrong deployment target during compilation") + + def test_suite(): src = _get_source_filename() if not os.path.exists(src): diff --git a/tests/test_util.py b/tests/test_util.py index 8ff5ae20..1a06d4c4 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -92,7 +92,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3' get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') @@ -105,7 +105,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sys.maxsize = cursize # macbook with fat binaries (fat, universal or fat64) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4' get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -113,6 +113,10 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEqual(get_platform(), 'macosx-10.4-fat') + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1' + self.assertEqual(get_platform(), 'macosx-10.4-fat') + + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -147,6 +151,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,)) + # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' diff --git a/util.py b/util.py index ce3cd6ca..d6f89d65 100644 --- a/util.py +++ b/util.py @@ -96,9 +96,7 @@ def get_platform (): from distutils.sysconfig import get_config_vars cfgvars = get_config_vars() - macver = os.environ.get('MACOSX_DEPLOYMENT_TARGET') - if not macver: - macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') + macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') if 1: # Always calculate the release of the running machine, -- cgit v1.2.1 From 47892871bc741f9c21e2cdec4384b5aec2f2bb1a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 15 May 2011 17:52:42 +0200 Subject: Bump to 3.2.1rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 17c89ee8..70ab7dbf 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.1b1" +__version__ = "3.2.1rc1" #--end constants-- -- cgit v1.2.1 From 09653b8554161ad38cf06341a4893c610da44f5a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 19 May 2011 15:18:36 +0200 Subject: Issue #10419, issue #6011: port 6ad356525381 fix from distutils to packaging build_scripts command of packaging now handles correctly non-ASCII path (path to the Python executable). Open and write the script in binary mode, but ensure that the shebang is decodable from UTF-8 and from the encoding of the script. --- command/build_scripts.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index a43a7c30..31be7930 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -128,10 +128,9 @@ class build_scripts(Command): "The shebang ({!r}) is not decodable " "from the script encoding ({})" .format(shebang, encoding)) - outf = open(outfile, "wb") - outf.write(shebang) - outf.writelines(f.readlines()) - outf.close() + with open(outfile, "wb") as outf: + outf.write(shebang) + outf.writelines(f.readlines()) if f: f.close() else: -- cgit v1.2.1 From d71f44e082fb25025dd678e6440aad3e623dcb2e Mon Sep 17 00:00:00 2001 From: Tarek Ziade Date: Thu, 19 May 2011 19:56:12 +0200 Subject: Issue #12120, Issue #12119: tests were missing a sys.dont_write_bytecode check --- tests/test_build_py.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index da3232ce..00a57cc9 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -58,7 +58,8 @@ class BuildPyTestCase(support.TempdirManager, pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) self.assertTrue("__init__.py" in files) - self.assertTrue("__init__.pyc" in files) + if not sys.dont_write_bytecode: + self.assertTrue("__init__.pyc" in files) self.assertTrue("README.txt" in files) def test_empty_package_dir (self): -- cgit v1.2.1 From 2c6268e3d2cddb476763b780ffaaba62a154a38d Mon Sep 17 00:00:00 2001 From: Tarek Ziade Date: Sun, 22 May 2011 22:09:55 +0200 Subject: Issue 12132 - skip the test_buil_ext test if the xx module is not found --- tests/test_build_ext.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 0aa99bab..a5b9700f 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -35,7 +35,9 @@ class BuildExtTestCase(TempdirManager, self.tmp_dir = self.mkdtemp() self.sys_path = sys.path, sys.path[:] sys.path.append(self.tmp_dir) - shutil.copy(_get_source_filename(), self.tmp_dir) + filename = _get_source_filename() + if os.path.exists(filename): + shutil.copy(filename, self.tmp_dir) if sys.version > "2.6": import site self.old_user_base = site.USER_BASE @@ -65,6 +67,8 @@ class BuildExtTestCase(TempdirManager, def test_build_ext(self): global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') + if not os.path.exists(xx_c): + return xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir -- cgit v1.2.1 From d9b753aa56fa9bd0b9f6d9aa0d7ea32a5cdbc750 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 23 May 2011 15:22:56 -0400 Subject: Replay changeset 70238:03e488b5c009 from fubar branch. Original commit message: Reconcile with the 2.6svn branch. The 2.6.7 release will be made from Subversion, but there were differences, so this brings them in sync. These changes should *not* propagate to any newer versions. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 90f96fea..6f4c021c 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.6" +__version__ = "2.6.7rc1" #--end constants-- -- cgit v1.2.1 From 744a4d498494ca6760eae00670adf90c777e32e5 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 23 May 2011 15:26:11 -0400 Subject: Replay changeset 70248:c714e2f92f63 from fubar branch. Original commit message: Cross-port changes for 2.6.7rc2 from the Subversion branch. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6f4c021c..b2a20820 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.7rc1" +__version__ = "2.6.7rc2" #--end constants-- -- cgit v1.2.1 From ce2661f4246098d8d1dbc3336b8cf0e58f514eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 28 May 2011 23:21:19 +0200 Subject: Fix test_distutils when sys.dont_write_bytecode is true (#9831). The tests now pass all combinations of -O/-OO and -B. See also #7071 and #6292 for previous variations on the same theme. --- tests/test_build_py.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 937fa0ce..6c6ec208 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -17,7 +17,7 @@ class BuildPyTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): - def _setup_package_data(self): + def test_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") try: @@ -57,20 +57,15 @@ class BuildPyTestCase(support.TempdirManager, self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) - return files - - def test_package_data(self): - files = self._setup_package_data() - self.assertTrue("__init__.py" in files) - self.assertTrue("README.txt" in files) - - @unittest.skipIf(sys.flags.optimize >= 2, - "pyc files are not written with -O2 and above") - def test_package_data_pyc(self): - files = self._setup_package_data() - self.assertTrue("__init__.pyc" in files) - - def test_empty_package_dir (self): + self.assertIn("__init__.py", files) + self.assertIn("README.txt", files) + # XXX even with -O, distutils writes pyc, not pyo; bug? + if sys.dont_write_bytecode: + self.assertNotIn("__init__.pyc", files) + else: + self.assertIn("__init__.pyc", files) + + def test_empty_package_dir(self): # See SF 1668596/1720897. cwd = os.getcwd() @@ -118,7 +113,7 @@ class BuildPyTestCase(support.TempdirManager, finally: sys.dont_write_bytecode = old_dont_write_bytecode - self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) + self.assertIn('byte-compiling is disabled', self.logs[0][1]) def test_suite(): return unittest.makeSuite(BuildPyTestCase) -- cgit v1.2.1 From b62fd2a2e3fed4d2ad97131f67cbc77c19246431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 28 May 2011 23:32:50 +0200 Subject: Fix test_build_py when sys.dont_write_bytecode is true (#9831). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests now pass all combinations of -O/-OO and -B. See also #7071 and #6292 for previous variations on the same theme. test_versionpredicate needs a skip when sys.flags.optimize is true, but I don’t know how to make that work with a DocTestSuite. --- tests/test_build_py.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index da3232ce..4e46339b 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -57,11 +57,15 @@ class BuildPyTestCase(support.TempdirManager, self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) - self.assertTrue("__init__.py" in files) - self.assertTrue("__init__.pyc" in files) - self.assertTrue("README.txt" in files) - - def test_empty_package_dir (self): + self.assertIn("__init__.py", files) + self.assertIn("README.txt", files) + # XXX even with -O, distutils writes pyc, not pyo; bug? + if sys.dont_write_bytecode: + self.assertNotIn("__init__.pyc", files) + else: + self.assertIn("__init__.pyc", files) + + def test_empty_package_dir(self): # See SF 1668596/1720897. cwd = os.getcwd() @@ -109,7 +113,7 @@ class BuildPyTestCase(support.TempdirManager, finally: sys.dont_write_bytecode = old_dont_write_bytecode - self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) + self.assertIn('byte-compiling is disabled', self.logs[0][1]) def test_suite(): return unittest.makeSuite(BuildPyTestCase) -- cgit v1.2.1 From b93d39b5f98d32aea6a9dc32ba80f2822018b953 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 29 May 2011 16:06:00 -0500 Subject: bump to 3.1.4rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6f55f016..5f8425fb 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.3" +__version__ = "3.1.4rc1" #--end constants-- -- cgit v1.2.1 From 8dff8ac3dff450113681b7510636be1c8e9d0270 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 29 May 2011 16:50:27 -0500 Subject: bump to 2.7.2rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a82d4cb5..e9fc2d6c 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.1" +__version__ = "2.7.2rc1" #--end constants-- -- cgit v1.2.1 From 9b7671822624669b22aa7574daa190b990c6906a Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 3 Jun 2011 20:02:47 -0400 Subject: Replay svn r88850. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b2a20820..beb65bb5 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.7rc2" +__version__ = "2.6.7" #--end constants-- -- cgit v1.2.1 From e3ba28ec2b8a5d8d5585de892f9e42f7021b436c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 4 Jun 2011 20:45:33 +0200 Subject: Remove unnecessary executable bit on one distutils file --- tests/Setup.sample | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tests/Setup.sample diff --git a/tests/Setup.sample b/tests/Setup.sample old mode 100755 new mode 100644 -- cgit v1.2.1 From 1151498802d17fe753a722f275bbf67485d2f637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 4 Jun 2011 20:47:26 +0200 Subject: Remove unneeded executable bit on two distutils files --- tests/Setup.sample | 0 tests/test_extension.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tests/Setup.sample mode change 100755 => 100644 tests/test_extension.py diff --git a/tests/Setup.sample b/tests/Setup.sample old mode 100755 new mode 100644 diff --git a/tests/test_extension.py b/tests/test_extension.py old mode 100755 new mode 100644 -- cgit v1.2.1 From e7cc03bf1da67e855bed7ac89acb895a51bfeb9a Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 11 Jun 2011 09:42:44 -0500 Subject: bump to 2.7.2 final --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e9fc2d6c..a849f1a9 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.2rc1" +__version__ = "2.7.2" #--end constants-- -- cgit v1.2.1 From edb4ad2deb371136eba9ab0d9c27687c3aa5df23 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 11 Jun 2011 09:58:58 -0500 Subject: bump to 3.1.4 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 5f8425fb..a3e29936 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.4rc1" +__version__ = "3.1.4" #--end constants-- -- cgit v1.2.1 From d6e07e40b2eb968c89070316983beed527c2b111 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 00:39:19 -0700 Subject: Issue #12141: Install a copy of template C module file so that test_build_ext of test_distutils is no longer silently skipped when run outside of a build directory. --- tests/test_build_ext.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 46dcb5ed..44fc980a 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,6 +19,11 @@ from test import test_support ALREADY_TESTED = False def _get_source_filename(): + # use installed copy if available + tests_f = os.path.join(os.path.dirname(__file__), 'xxmodule.c') + if os.path.exists(tests_f): + return tests_f + # otherwise try using copy from build directory srcdir = sysconfig.get_config_var('srcdir') if srcdir is None: return os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') -- cgit v1.2.1 From 32d401bb9f75df6acc4dd67a361d5b3de036f98a Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 00:42:50 -0700 Subject: Issue #12141: Install a copy of template C module file so that test_build_ext of test_distutils is no longer silently skipped when run outside of a build directory. --- tests/test_build_ext.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 0aa99bab..d924f585 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -22,6 +22,11 @@ from test.support import run_unittest ALREADY_TESTED = False def _get_source_filename(): + # use installed copy if available + tests_f = os.path.join(os.path.dirname(__file__), 'xxmodule.c') + if os.path.exists(tests_f): + return tests_f + # otherwise try using copy from build directory srcdir = sysconfig.get_config_var('srcdir') return os.path.join(srcdir, 'Modules', 'xxmodule.c') @@ -35,7 +40,9 @@ class BuildExtTestCase(TempdirManager, self.tmp_dir = self.mkdtemp() self.sys_path = sys.path, sys.path[:] sys.path.append(self.tmp_dir) - shutil.copy(_get_source_filename(), self.tmp_dir) + filename = _get_source_filename() + if os.path.exists(filename): + shutil.copy(filename, self.tmp_dir) if sys.version > "2.6": import site self.old_user_base = site.USER_BASE @@ -65,6 +72,8 @@ class BuildExtTestCase(TempdirManager, def test_build_ext(self): global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') + if not os.path.exists(xx_c): + return xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir -- cgit v1.2.1 From 303c2b4c7af695706369e68cd6b32606ab5401d6 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 19:39:10 -0700 Subject: Issue #9516: Correct and expand OS X deployment target tests in distutils test_build_ext. --- tests/test_build_ext.py | 49 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 44fc980a..ced1329e 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -9,7 +9,8 @@ from distutils.core import Extension, Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig from distutils.tests import support -from distutils.errors import DistutilsSetupError, CompileError +from distutils.errors import (DistutilsSetupError, CompileError, + DistutilsPlatformError) import unittest from test import test_support @@ -437,18 +438,43 @@ class BuildExtTestCase(support.TempdirManager, self.assertEqual(ext_path, wanted) @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') - def test_deployment_target(self): - self._try_compile_deployment_target() + def test_deployment_target_default(self): + # Issue 9516: Test that, in the absence of the environment variable, + # an extension module is compiled with the same deployment target as + # the interpreter. + self._try_compile_deployment_target('==', None) + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target_too_low(self): + # Issue 9516: Test that an extension module is not allowed to be + # compiled with a deployment target less than that of the interpreter. + self.assertRaises(DistutilsPlatformError, + self._try_compile_deployment_target, '>', '10.1') + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target_higher_ok(self): + # Issue 9516: Test that an extension module can be compiled with a + # deployment target higher than that of the interpreter: the ext + # module may depend on some newer OS feature. + deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') + if deptarget: + # increment the minor version number (i.e. 10.6 -> 10.7) + deptarget = [int(x) for x in deptarget.split('.')] + deptarget[-1] += 1 + deptarget = '.'.join(str(i) for i in deptarget) + self._try_compile_deployment_target('<', deptarget) + + def _try_compile_deployment_target(self, operator, target): orig_environ = os.environ os.environ = orig_environ.copy() self.addCleanup(setattr, os, 'environ', orig_environ) - os.environ['MACOSX_DEPLOYMENT_TARGET']='10.1' - self._try_compile_deployment_target() - + if target is None: + if os.environ.get('MACOSX_DEPLOYMENT_TARGET'): + del os.environ['MACOSX_DEPLOYMENT_TARGET'] + else: + os.environ['MACOSX_DEPLOYMENT_TARGET'] = target - def _try_compile_deployment_target(self): deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') with open(deptarget_c, 'w') as fp: @@ -457,16 +483,17 @@ class BuildExtTestCase(support.TempdirManager, int dummy; - #if TARGET != MAC_OS_X_VERSION_MIN_REQUIRED + #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED + #else #error "Unexpected target" - #endif + #endif - ''')) + ''' % operator)) + # get the deployment target that the interpreter was built with target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') target = tuple(map(int, target.split('.'))) target = '%02d%01d0' % target - deptarget_ext = Extension( 'deptarget', [deptarget_c], -- cgit v1.2.1 From d671b9c866f6f54493dfa07e5004a92b104b205d Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 19:40:39 -0700 Subject: Issue #9516: Change distutils to no longer globally attempt to check and set the MACOSX_DEPLOYMENT_TARGET env variable for the interpreter process on OS X. This could cause failures in non-distutils subprocesses and was unreliable since tests or user programs could modify the interpreter environment after distutils set it. Instead, have distutils set the the deployment target only in the environment of each build subprocess. Continue to use the previous algorithm for deriving the deployment target value: if MACOSX_DEPLOYMENT_TARGET is not set in the interpreter's env: use the interpreter build configure MACOSX_DEPLOYMENT_TARGET elif the MACOSX_DEPLOYMENT_TARGET env value >= configure value: use the env MACOSX_DEPLOYMENT_TARGET else: # env value less than interpreter build configure value raise exception This allows building extensions that can only run on newer versions of the OS than the version python was built for, for example with a python built for 10.3 or later and an extension that needs to be built for 10.5. --- spawn.py | 28 +++++++++++++++++++++++++++- sysconfig.py | 15 --------------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/spawn.py b/spawn.py index 5c014c4b..7306099f 100644 --- a/spawn.py +++ b/spawn.py @@ -96,17 +96,43 @@ def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): raise DistutilsExecError, \ "command '%s' failed with exit status %d" % (cmd[0], rc) +if sys.platform == 'darwin': + from distutils import sysconfig + _cfg_target = None + _cfg_target_split = None def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): log.info(' '.join(cmd)) if dry_run: return exec_fn = search_path and os.execvp or os.execv + exec_args = [cmd[0], cmd] + if sys.platform == 'darwin': + global _cfg_target, _cfg_target_split + if _cfg_target is None: + _cfg_target = sysconfig.get_config_var( + 'MACOSX_DEPLOYMENT_TARGET') or '' + if _cfg_target: + _cfg_target_split = [int(x) for x in _cfg_target.split('.')] + if _cfg_target: + # ensure that the deployment target of build process is not less + # than that used when the interpreter was built. This ensures + # extension modules are built with correct compatibility values + cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target) + if _cfg_target_split > [int(x) for x in cur_target.split('.')]: + my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: ' + 'now "%s" but "%s" during configure' + % (cur_target, _cfg_target)) + raise DistutilsPlatformError(my_msg) + env = dict(os.environ, + MACOSX_DEPLOYMENT_TARGET=cur_target) + exec_fn = search_path and os.execvpe or os.execve + exec_args.append(env) pid = os.fork() if pid == 0: # in the child try: - exec_fn(cmd[0], cmd) + exec_fn(*exec_args) except OSError, e: sys.stderr.write("unable to execute %s: %s\n" % (cmd[0], e.strerror)) diff --git a/sysconfig.py b/sysconfig.py index d206e0cd..0d6d4c4f 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -380,21 +380,6 @@ def _init_posix(): raise DistutilsPlatformError(my_msg) - # On MacOSX we need to check the setting of the environment variable - # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so - # it needs to be compatible. - # If it isn't set we set it to the configure-time value - if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g: - cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] - cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') - if cur_target == '': - cur_target = cfg_target - os.environ['MACOSX_DEPLOYMENT_TARGET'] = cfg_target - elif map(int, cfg_target.split('.')) > map(int, cur_target.split('.')): - my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' - % (cur_target, cfg_target)) - raise DistutilsPlatformError(my_msg) - # On AIX, there are wrong paths to the linker scripts in the Makefile # -- these paths are relative to the Python source, but when installed # the scripts are in another directory. -- cgit v1.2.1 From 140120ec23e70358cb6dfeadf92b9aada4af835b Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 19:43:15 -0700 Subject: Issue #9516: Correct and expand OS X deployment target tests in distutils test_build_ext. --- tests/test_build_ext.py | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index d924f585..0ce7f0f8 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -11,7 +11,8 @@ from distutils.tests.support import TempdirManager from distutils.tests.support import LoggingSilencer from distutils.extension import Extension from distutils.errors import ( - CompileError, DistutilsSetupError, UnknownFileError) + CompileError, DistutilsPlatformError, DistutilsSetupError, + UnknownFileError) import unittest from test import support @@ -431,18 +432,43 @@ class BuildExtTestCase(TempdirManager, @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') - def test_deployment_target(self): - self._try_compile_deployment_target() + def test_deployment_target_default(self): + # Issue 9516: Test that, in the absence of the environment variable, + # an extension module is compiled with the same deployment target as + # the interpreter. + self._try_compile_deployment_target('==', None) + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target_too_low(self): + # Issue 9516: Test that an extension module is not allowed to be + # compiled with a deployment target less than that of the interpreter. + self.assertRaises(DistutilsPlatformError, + self._try_compile_deployment_target, '>', '10.1') + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target_higher_ok(self): + # Issue 9516: Test that an extension module can be compiled with a + # deployment target higher than that of the interpreter: the ext + # module may depend on some newer OS feature. + deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') + if deptarget: + # increment the minor version number (i.e. 10.6 -> 10.7) + deptarget = [int(x) for x in deptarget.split('.')] + deptarget[-1] += 1 + deptarget = '.'.join(str(i) for i in deptarget) + self._try_compile_deployment_target('<', deptarget) + + def _try_compile_deployment_target(self, operator, target): orig_environ = os.environ os.environ = orig_environ.copy() self.addCleanup(setattr, os, 'environ', orig_environ) - os.environ['MACOSX_DEPLOYMENT_TARGET']='10.1' - self._try_compile_deployment_target() - + if target is None: + if os.environ.get('MACOSX_DEPLOYMENT_TARGET'): + del os.environ['MACOSX_DEPLOYMENT_TARGET'] + else: + os.environ['MACOSX_DEPLOYMENT_TARGET'] = target - def _try_compile_deployment_target(self): deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') with open(deptarget_c, 'w') as fp: @@ -451,16 +477,17 @@ class BuildExtTestCase(TempdirManager, int dummy; - #if TARGET != MAC_OS_X_VERSION_MIN_REQUIRED + #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED + #else #error "Unexpected target" #endif - ''')) + ''' % operator)) + # get the deployment target that the interpreter was built with target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') target = tuple(map(int, target.split('.'))) target = '%02d%01d0' % target - deptarget_ext = Extension( 'deptarget', [deptarget_c], -- cgit v1.2.1 From 40bee45e0bd8a042c3a8f893328f6a6a52698fdf Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 19:44:24 -0700 Subject: Issue #9516: Change distutils to no longer globally attempt to check and set the MACOSX_DEPLOYMENT_TARGET env variable for the interpreter process on OS X. This could cause failures in non-distutils subprocesses and was unreliable since tests or user programs could modify the interpreter environment after distutils set it. Instead, have distutils set the the deployment target only in the environment of each build subprocess. Continue to use the previous algorithm for deriving the deployment target value: if MACOSX_DEPLOYMENT_TARGET is not set in the interpreter's env: use the interpreter build configure MACOSX_DEPLOYMENT_TARGET elif the MACOSX_DEPLOYMENT_TARGET env value >= configure value: use the env MACOSX_DEPLOYMENT_TARGET else: # env value less than interpreter build configure value raise exception This allows building extensions that can only run on newer versions of the OS than the version python was built for, for example with a python built for 10.3 or later and an extension that needs to be built for 10.5. --- spawn.py | 29 ++++++++++++++++++++++++++++- sysconfig.py | 15 --------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/spawn.py b/spawn.py index 8c476dc2..2b62c968 100644 --- a/spawn.py +++ b/spawn.py @@ -96,15 +96,42 @@ def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): raise DistutilsExecError( "command '%s' failed with exit status %d" % (cmd[0], rc)) +if sys.platform == 'darwin': + from distutils import sysconfig + _cfg_target = None + _cfg_target_split = None + def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): log.info(' '.join(cmd)) if dry_run: return exec_fn = search_path and os.execvp or os.execv + exec_args = [cmd[0], cmd] + if sys.platform == 'darwin': + global _cfg_target, _cfg_target_split + if _cfg_target is None: + _cfg_target = sysconfig.get_config_var( + 'MACOSX_DEPLOYMENT_TARGET') or '' + if _cfg_target: + _cfg_target_split = [int(x) for x in _cfg_target.split('.')] + if _cfg_target: + # ensure that the deployment target of build process is not less + # than that used when the interpreter was built. This ensures + # extension modules are built with correct compatibility values + cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target) + if _cfg_target_split > [int(x) for x in cur_target.split('.')]: + my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: ' + 'now "%s" but "%s" during configure' + % (cur_target, _cfg_target)) + raise DistutilsPlatformError(my_msg) + env = dict(os.environ, + MACOSX_DEPLOYMENT_TARGET=cur_target) + exec_fn = search_path and os.execvpe or os.execve + exec_args.append(env) pid = os.fork() if pid == 0: # in the child try: - exec_fn(cmd[0], cmd) + exec_fn(*exec_args) except OSError as e: sys.stderr.write("unable to execute %s: %s\n" % (cmd[0], e.strerror)) diff --git a/sysconfig.py b/sysconfig.py index 06bbc01c..9d7d1902 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -419,21 +419,6 @@ def _init_posix(): raise DistutilsPlatformError(my_msg) - # On MacOSX we need to check the setting of the environment variable - # MACOSX_DEPLOYMENT_TARGET: configure bases some choices on it so - # it needs to be compatible. - # If it isn't set we set it to the configure-time value - if sys.platform == 'darwin' and 'MACOSX_DEPLOYMENT_TARGET' in g: - cfg_target = g['MACOSX_DEPLOYMENT_TARGET'] - cur_target = os.getenv('MACOSX_DEPLOYMENT_TARGET', '') - if cur_target == '': - cur_target = cfg_target - os.environ['MACOSX_DEPLOYMENT_TARGET'] = cfg_target - elif [int(x) for x in cfg_target.split('.')] > [int(x) for x in cur_target.split('.')]: - my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: now "%s" but "%s" during configure' - % (cur_target, cfg_target)) - raise DistutilsPlatformError(my_msg) - # On AIX, there are wrong paths to the linker scripts in the Makefile # -- these paths are relative to the Python source, but when installed # the scripts are in another directory. -- cgit v1.2.1 From bf799fa9dfd51f6ff2bda9e768f4f4333d351f76 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jun 2011 15:40:22 +0200 Subject: Issue #12451: distutils now opens the setup script in binary mode to read the encoding cookie, instead of opening it in UTF-8. --- core.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core.py b/core.py index fd2a43d7..c0a04de3 100644 --- a/core.py +++ b/core.py @@ -8,7 +8,8 @@ really defined in distutils.dist and distutils.cmd. __revision__ = "$Id$" -import sys, os +import os +import sys from distutils.debug import DEBUG from distutils.errors import * @@ -215,11 +216,8 @@ def run_setup (script_name, script_args=None, stop_after="run"): sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args - f = open(script_name) - try: + with open(script_name, 'rb') as f: exec(f.read(), g, l) - finally: - f.close() finally: sys.argv = save_argv _setup_stop_after = None -- cgit v1.2.1 From f2b3625193af66b537f99fc0c7843b4051f1bd1b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jun 2011 23:25:47 +0200 Subject: Issue #12451: Add support.create_empty_file() We don't need to create a temporary buffered binary or text file object just to create an empty file. Replace also os.fdopen(handle).close() by os.close(handle). --- tests/test_build_py.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 4e46339b..c7c36f3c 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -10,7 +10,7 @@ from distutils.core import Distribution from distutils.errors import DistutilsFileError from distutils.tests import support -from test.support import run_unittest +from test.support import run_unittest, create_empty_file class BuildPyTestCase(support.TempdirManager, @@ -71,11 +71,11 @@ class BuildPyTestCase(support.TempdirManager, # create the distribution files. sources = self.mkdtemp() - open(os.path.join(sources, "__init__.py"), "w").close() + create_empty_file(os.path.join(sources, "__init__.py")) testdir = os.path.join(sources, "doc") os.mkdir(testdir) - open(os.path.join(testdir, "testfile"), "w").close() + create_empty_file(os.path.join(testdir, "testfile")) os.chdir(sources) old_stdout = sys.stdout -- cgit v1.2.1 From 4758a6576c8847d6848ebe98f99f6526673f35b8 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 3 Jul 2011 09:41:27 +0200 Subject: Bump to 3.2.1rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 70ab7dbf..2602c507 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.1rc1" +__version__ = "3.2.1rc2" #--end constants-- -- cgit v1.2.1 From 7d80bbfb319d76b2306d4193fdb082acd919927d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 9 Jul 2011 08:56:21 +0200 Subject: Bump version to 3.2.1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2602c507..a470ade9 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.1rc2" +__version__ = "3.2.1" #--end constants-- -- cgit v1.2.1 From 4320a750da7c64db5dd3c18714655f5673198b33 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Thu, 28 Jul 2011 22:32:49 +0800 Subject: Fix closes Issue11439 Remove the SVN keywords from the code as it is no longer applicable in hg. Patch Contributed by Neil Muller. --- __init__.py | 2 -- archive_util.py | 2 -- bcppcompiler.py | 2 -- ccompiler.py | 2 -- cmd.py | 2 -- command/__init__.py | 2 -- command/bdist.py | 2 -- command/bdist_dumb.py | 2 -- command/bdist_rpm.py | 2 -- command/bdist_wininst.py | 2 -- command/build.py | 2 -- command/build_clib.py | 2 -- command/build_ext.py | 2 -- command/build_py.py | 2 -- command/build_scripts.py | 2 -- command/check.py | 2 -- command/clean.py | 2 -- command/config.py | 2 -- command/install.py | 2 -- command/install_data.py | 2 -- command/install_headers.py | 2 -- command/install_lib.py | 2 -- command/install_scripts.py | 2 -- command/register.py | 2 -- command/sdist.py | 2 -- core.py | 2 -- cygwinccompiler.py | 2 -- debug.py | 2 -- dep_util.py | 2 -- dir_util.py | 2 -- dist.py | 2 -- emxccompiler.py | 2 -- errors.py | 2 -- extension.py | 2 -- fancy_getopt.py | 2 -- file_util.py | 2 -- filelist.py | 2 -- msvc9compiler.py | 2 -- msvccompiler.py | 2 -- spawn.py | 2 -- sysconfig.py | 2 -- tests/test_archive_util.py | 2 -- text_file.py | 2 -- unixccompiler.py | 2 -- util.py | 2 -- 45 files changed, 90 deletions(-) diff --git a/__init__.py b/__init__.py index a470ade9..c06002ea 100644 --- a/__init__.py +++ b/__init__.py @@ -8,8 +8,6 @@ used from a setup script as setup (...) """ -__revision__ = "$Id$" - # Distutils version # # Updated automatically by the Python release process. diff --git a/archive_util.py b/archive_util.py index c06eba35..fcda08e2 100644 --- a/archive_util.py +++ b/archive_util.py @@ -3,8 +3,6 @@ Utility functions for creating archive files (tarballs, zip files, that sort of thing).""" -__revision__ = "$Id$" - import os from warnings import warn import sys diff --git a/bcppcompiler.py b/bcppcompiler.py index c5e5cd25..9f4c432d 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,8 +11,6 @@ for the Borland C++ compiler. # someone should sit down and factor out the common code as # WindowsCCompiler! --GPW -__revision__ = "$Id$" - import os from distutils.errors import \ diff --git a/ccompiler.py b/ccompiler.py index 291c008f..c795c958 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -3,8 +3,6 @@ Contains CCompiler, an abstract base class that defines the interface for the Distutils compiler abstraction model.""" -__revision__ = "$Id$" - import sys, os, re from distutils.errors import * from distutils.spawn import spawn diff --git a/cmd.py b/cmd.py index 5b1d085c..3ea08101 100644 --- a/cmd.py +++ b/cmd.py @@ -4,8 +4,6 @@ Provides the Command class, the base class for the command classes in the distutils.command package. """ -__revision__ = "$Id$" - import sys, os, re from distutils.errors import DistutilsOptionError from distutils import util, dir_util, file_util, archive_util, dep_util diff --git a/command/__init__.py b/command/__init__.py index c379edbe..481eea9f 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -3,8 +3,6 @@ Package containing implementation of all the standard Distutils commands.""" -__revision__ = "$Id$" - __all__ = ['build', 'build_py', 'build_ext', diff --git a/command/bdist.py b/command/bdist.py index 1a360b59..c5188eb3 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -3,8 +3,6 @@ Implements the Distutils 'bdist' command (create a built [binary] distribution).""" -__revision__ = "$Id$" - import os from distutils.core import Command from distutils.errors import * diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 2d399226..170e8894 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,8 +4,6 @@ Implements the Distutils 'bdist_dumb' command (create a "dumb" built distribution -- i.e., just an archive to be unpacked under $prefix or $exec_prefix).""" -__revision__ = "$Id$" - import os from distutils.core import Command from distutils.util import get_platform diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index e2ae877d..678e1188 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -3,8 +3,6 @@ Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions).""" -__revision__ = "$Id$" - import sys, os from distutils.core import Command from distutils.debug import DEBUG diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index b2e2fc6d..b7916e31 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -3,8 +3,6 @@ Implements the Distutils 'bdist_wininst' command: create a windows installer exe-program.""" -__revision__ = "$Id$" - import sys, os from distutils.core import Command from distutils.util import get_platform diff --git a/command/build.py b/command/build.py index 9c2667cf..cfc15cf0 100644 --- a/command/build.py +++ b/command/build.py @@ -2,8 +2,6 @@ Implements the Distutils 'build' command.""" -__revision__ = "$Id$" - import sys, os from distutils.core import Command from distutils.errors import DistutilsOptionError diff --git a/command/build_clib.py b/command/build_clib.py index 428011a6..3e20ef23 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,8 +4,6 @@ Implements the Distutils 'build_clib' command, to build a C/C++ library that is included in the module distribution and needed by an extension module.""" -__revision__ = "$Id$" - # XXX this module has *lots* of code ripped-off quite transparently from # build_ext.py -- not surprisingly really, as the work required to build diff --git a/command/build_ext.py b/command/build_ext.py index fb316489..8d843d68 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,8 +4,6 @@ Implements the Distutils 'build_ext' command, for building extension modules (currently limited to C extensions, should accommodate C++ extensions ASAP).""" -__revision__ = "$Id$" - import sys, os, re from distutils.core import Command from distutils.errors import * diff --git a/command/build_py.py b/command/build_py.py index 26002e4b..3868c12f 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -2,8 +2,6 @@ Implements the Distutils 'build_py' command.""" -__revision__ = "$Id$" - import sys, os import sys from glob import glob diff --git a/command/build_scripts.py b/command/build_scripts.py index a43a7c30..ec434770 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -2,8 +2,6 @@ Implements the Distutils 'build_scripts' command.""" -__revision__ = "$Id$" - import os, re from stat import ST_MODE from distutils import sysconfig diff --git a/command/check.py b/command/check.py index 2657c696..b67c7953 100644 --- a/command/check.py +++ b/command/check.py @@ -2,8 +2,6 @@ Implements the Distutils 'check' command. """ -__revision__ = "$Id$" - from distutils.core import Command from distutils.errors import DistutilsSetupError diff --git a/command/clean.py b/command/clean.py index ae1d22c3..0cb27016 100644 --- a/command/clean.py +++ b/command/clean.py @@ -4,8 +4,6 @@ Implements the Distutils 'clean' command.""" # contributed by Bastian Kleineidam , added 2000-03-18 -__revision__ = "$Id$" - import os from distutils.core import Command from distutils.dir_util import remove_tree diff --git a/command/config.py b/command/config.py index ac80a54e..847e8581 100644 --- a/command/config.py +++ b/command/config.py @@ -9,8 +9,6 @@ configure-like tasks: "try to compile this C code", or "figure out where this header file lives". """ -__revision__ = "$Id$" - import sys, os, re from distutils.core import Command diff --git a/command/install.py b/command/install.py index bdc3a09b..0161898f 100644 --- a/command/install.py +++ b/command/install.py @@ -2,8 +2,6 @@ Implements the Distutils 'install' command.""" -__revision__ = "$Id$" - import sys import os diff --git a/command/install_data.py b/command/install_data.py index ab40797b..947cd76a 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -5,8 +5,6 @@ platform-independent data files.""" # contributed by Bastian Kleineidam -__revision__ = "$Id$" - import os from distutils.core import Command from distutils.util import change_root, convert_path diff --git a/command/install_headers.py b/command/install_headers.py index 38125b55..9bb0b18d 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -3,8 +3,6 @@ Implements the Distutils 'install_headers' command, to install C/C++ header files to the Python include directory.""" -__revision__ = "$Id$" - from distutils.core import Command diff --git a/command/install_lib.py b/command/install_lib.py index 6022d30f..3d01d071 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -3,8 +3,6 @@ Implements the Distutils 'install_lib' command (install all Python modules).""" -__revision__ = "$Id$" - import os import sys diff --git a/command/install_scripts.py b/command/install_scripts.py index ea8d5aa6..31a1130e 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -5,8 +5,6 @@ Python scripts.""" # contributed by Bastian Kleineidam -__revision__ = "$Id$" - import os from distutils.core import Command from distutils import log diff --git a/command/register.py b/command/register.py index bdf5f8f0..99545aff 100644 --- a/command/register.py +++ b/command/register.py @@ -5,8 +5,6 @@ Implements the Distutils 'register' command (register with the repository). # created 2002/10/21, Richard Jones -__revision__ = "$Id$" - import os, string, getpass import io import urllib.parse, urllib.request diff --git a/command/sdist.py b/command/sdist.py index fdbebd7e..48cb26b6 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -2,8 +2,6 @@ Implements the Distutils 'sdist' command (create a source distribution).""" -__revision__ = "$Id$" - import os import string import sys diff --git a/core.py b/core.py index c0a04de3..260332a2 100644 --- a/core.py +++ b/core.py @@ -6,8 +6,6 @@ indirectly provides the Distribution and Command classes, although they are really defined in distutils.dist and distutils.cmd. """ -__revision__ = "$Id$" - import os import sys diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 536aa6b6..819e1a97 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -45,8 +45,6 @@ cygwin in no-cygwin mode). # * mingw gcc 3.2/ld 2.13 works # (ld supports -shared) -__revision__ = "$Id$" - import os import sys import copy diff --git a/debug.py b/debug.py index 28867444..daf1660f 100644 --- a/debug.py +++ b/debug.py @@ -1,7 +1,5 @@ import os -__revision__ = "$Id$" - # If DISTUTILS_DEBUG is anything other than the empty string, we run in # debug mode. DEBUG = os.environ.get('DISTUTILS_DEBUG') diff --git a/dep_util.py b/dep_util.py index 07b3549c..d74f5e4e 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,8 +4,6 @@ Utility functions for simple, timestamp-based dependency of files and groups of files; also, function based entirely on such timestamp dependency analysis.""" -__revision__ = "$Id$" - import os from distutils.errors import DistutilsFileError diff --git a/dir_util.py b/dir_util.py index 5b005f08..30daf49a 100644 --- a/dir_util.py +++ b/dir_util.py @@ -2,8 +2,6 @@ Utility functions for manipulating directories and directory trees.""" -__revision__ = "$Id$" - import os, sys import errno from distutils.errors import DistutilsFileError, DistutilsInternalError diff --git a/dist.py b/dist.py index 01f1f1cf..02cd79ba 100644 --- a/dist.py +++ b/dist.py @@ -4,8 +4,6 @@ Provides the Distribution class, which represents the module distribution being built/installed/distributed. """ -__revision__ = "$Id$" - import sys, os, re try: diff --git a/emxccompiler.py b/emxccompiler.py index 16dce535..3675f8df 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -19,8 +19,6 @@ handles the EMX port of the GNU C compiler to OS/2. # # * EMX gcc 2.81/EMX 0.9d fix03 -__revision__ = "$Id$" - import os,sys,copy from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler diff --git a/errors.py b/errors.py index acecaccc..eb13c983 100644 --- a/errors.py +++ b/errors.py @@ -8,8 +8,6 @@ usually raised for errors that are obviously the end-user's fault This module is safe to use in "from ... import *" mode; it only exports symbols whose names start with "Distutils" and end with "Error".""" -__revision__ = "$Id$" - class DistutilsError (Exception): """The root of all Distutils evil.""" pass diff --git a/extension.py b/extension.py index 2d1c36bd..a93655af 100644 --- a/extension.py +++ b/extension.py @@ -3,8 +3,6 @@ Provides the Extension class, used to describe C/C++ extension modules in setup scripts.""" -__revision__ = "$Id$" - import os import sys import warnings diff --git a/fancy_getopt.py b/fancy_getopt.py index 879d4d25..7d170dd2 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,8 +8,6 @@ additional features: * options set attributes of a passed-in object """ -__revision__ = "$Id$" - import sys, string, re import getopt from distutils.errors import * diff --git a/file_util.py b/file_util.py index e1eb9329..9bdd14e4 100644 --- a/file_util.py +++ b/file_util.py @@ -3,8 +3,6 @@ Utility functions for operating on single files. """ -__revision__ = "$Id$" - import os from distutils.errors import DistutilsFileError from distutils import log diff --git a/filelist.py b/filelist.py index 06a8da9a..a94b5c8e 100644 --- a/filelist.py +++ b/filelist.py @@ -4,8 +4,6 @@ Provides the FileList class, used for poking about the filesystem and building lists of files. """ -__revision__ = "$Id$" - import os, re import fnmatch from distutils.util import convert_path diff --git a/msvc9compiler.py b/msvc9compiler.py index e849e16d..0cddb5c8 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -12,8 +12,6 @@ for older versions of VS in distutils.msvccompiler. # finding DevStudio (through the registry) # ported to VS2005 and VS 2008 by Christian Heimes -__revision__ = "$Id$" - import os import subprocess import sys diff --git a/msvccompiler.py b/msvccompiler.py index 1cd0f91d..81166569 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -8,8 +8,6 @@ for the Microsoft Visual Studio. # hacked by Robin Becker and Thomas Heller to do a better job of # finding DevStudio (through the registry) -__revision__ = "$Id$" - import sys, os from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ diff --git a/spawn.py b/spawn.py index 2b62c968..f58c55f9 100644 --- a/spawn.py +++ b/spawn.py @@ -6,8 +6,6 @@ Also provides the 'find_executable()' to search the path for a given executable name. """ -__revision__ = "$Id$" - import sys import os diff --git a/sysconfig.py b/sysconfig.py index 9d7d1902..5ea724c0 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -9,8 +9,6 @@ Written by: Fred L. Drake, Jr. Email: """ -__revision__ = "$Id$" - import os import re import sys diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index f9698495..8edfab49 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -1,6 +1,4 @@ """Tests for distutils.archive_util.""" -__revision__ = "$Id$" - import unittest import os import tarfile diff --git a/text_file.py b/text_file.py index 454725c6..40b8484a 100644 --- a/text_file.py +++ b/text_file.py @@ -4,8 +4,6 @@ provides the TextFile class, which gives an interface to text files that (optionally) takes care of stripping comments, ignoring blank lines, and joining lines with backslashes.""" -__revision__ = "$Id$" - import sys, os, io diff --git a/unixccompiler.py b/unixccompiler.py index bf734161..c70a3cc5 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -13,8 +13,6 @@ the "typical" Unix-style command-line C compiler: * link shared library handled by 'cc -shared' """ -__revision__ = "$Id$" - import os, sys, re from distutils import sysconfig diff --git a/util.py b/util.py index d6f89d65..023ddffc 100644 --- a/util.py +++ b/util.py @@ -4,8 +4,6 @@ Miscellaneous utility functions -- anything that doesn't fit into one of the other *util.py modules. """ -__revision__ = "$Id$" - import sys, os, string, re from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer -- cgit v1.2.1 From 69896eb64dc3f52097d8cb26a693b794dbf81504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 31 Jul 2011 02:04:00 +0200 Subject: Fix regression with distutils MANIFEST handing (#11104, #8688). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The changed behavior of sdist in 2.7 broke packaging for projects that wanted to use a manually-maintained MANIFEST file (instead of having a MANIFEST.in template and letting distutils generate the MANIFEST). The fixes that were committed for #8688 (d29399100973 by Tarek and f7639dcdffc3 by me) did not fix all issues exposed in the bug report, and also added one problem: the MANIFEST file format gained comments, but the read_manifest method was not updated to handle (i.e. ignore) them. This changeset should fix everything; the tests have been expanded and I successfully tested with Mercurial, which suffered from this regression. I have grouped the versionchanged directives for these bugs in one place and added micro version numbers to help users know the quirks of the exact version they’re using. I also removed a stanza in the docs that was forgotten in Tarek’s first changeset. Initial report, thorough diagnosis and patch by John Dennis, further work on the patch by Stephen Thorne, and a few edits and additions by me. --- command/sdist.py | 48 +++++++++++++++++++++++++++++------------------- tests/test_sdist.py | 43 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 63 insertions(+), 28 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index cf8982bd..75950f3d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -182,14 +182,20 @@ class sdist(Command): reading the manifest, or just using the default file set -- it all depends on the user's options. """ - # new behavior: + # new behavior when using a template: # the file list is recalculated everytime because # even if MANIFEST.in or setup.py are not changed # the user might have added some files in the tree that # need to be included. # - # This makes --force the default and only behavior. + # This makes --force the default and only behavior with templates. template_exists = os.path.isfile(self.template) + if not template_exists and self._manifest_is_not_generated(): + self.read_manifest() + self.filelist.sort() + self.filelist.remove_duplicates() + return + if not template_exists: self.warn(("manifest template '%s' does not exist " + "(using default file list)") % @@ -352,23 +358,28 @@ class sdist(Command): by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. """ - if os.path.isfile(self.manifest): - fp = open(self.manifest) - try: - first_line = fp.readline() - finally: - fp.close() - - if first_line != '# file GENERATED by distutils, do NOT edit\n': - log.info("not writing to manually maintained " - "manifest file '%s'" % self.manifest) - return + if self._manifest_is_not_generated(): + log.info("not writing to manually maintained " + "manifest file '%s'" % self.manifest) + return content = self.filelist.files[:] content.insert(0, '# file GENERATED by distutils, do NOT edit') self.execute(file_util.write_file, (self.manifest, content), "writing manifest file '%s'" % self.manifest) + def _manifest_is_not_generated(self): + # check for special comment used in 2.7.1 and higher + if not os.path.isfile(self.manifest): + return False + + fp = open(self.manifest, 'rU') + try: + first_line = fp.readline() + finally: + fp.close() + return first_line != '# file GENERATED by distutils, do NOT edit\n' + def read_manifest(self): """Read the manifest file (named by 'self.manifest') and use it to fill in 'self.filelist', the list of files to include in the source @@ -376,12 +387,11 @@ class sdist(Command): """ log.info("reading manifest file '%s'", self.manifest) manifest = open(self.manifest) - while 1: - line = manifest.readline() - if line == '': # end of file - break - if line[-1] == '\n': - line = line[0:-1] + for line in manifest: + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue self.filelist.append(line) manifest.close() diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 61f9c1f7..8da6fe4d 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -1,9 +1,11 @@ """Tests for distutils.command.sdist.""" import os +import tarfile import unittest -import shutil +import warnings import zipfile -import tarfile +from os.path import join +from textwrap import dedent # zlib is not used here, but if it's not available # the tests that use zipfile may fail @@ -19,19 +21,13 @@ try: except ImportError: UID_GID_SUPPORT = False -from os.path import join -import sys -import tempfile -import warnings - from test.test_support import captured_stdout, check_warnings, run_unittest from distutils.command.sdist import sdist, show_formats from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase -from distutils.errors import DistutilsExecError, DistutilsOptionError +from distutils.errors import DistutilsOptionError from distutils.spawn import find_executable -from distutils.tests import support from distutils.log import WARN from distutils.archive_util import ARCHIVE_FORMATS @@ -405,13 +401,33 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(manifest[0], '# file GENERATED by distutils, do NOT edit') + @unittest.skipUnless(zlib, 'requires zlib') + def test_manifest_comments(self): + # make sure comments don't cause exceptions or wrong includes + contents = dedent("""\ + # bad.py + #bad.py + good.py + """) + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), contents) + self.write_file((self.tmp_dir, 'good.py'), '# pick me!') + self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!") + self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!") + cmd.run() + self.assertEqual(cmd.filelist.files, ['good.py']) + @unittest.skipUnless(zlib, "requires zlib") def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() cmd.ensure_finalized() self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') + self.write_file((self.tmp_dir, 'README.manual'), + 'This project maintains its MANIFEST file itself.') cmd.run() + self.assertEqual(cmd.filelist.files, ['README.manual']) f = open(cmd.manifest) try: @@ -422,6 +438,15 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(manifest, ['README.manual']) + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + filenames = [tarinfo.name for tarinfo in archive] + finally: + archive.close() + self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO', + 'fake-1.0/README.manual']) + def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From 2659dbffbdccd9365d0e81be91a230884a7430f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 31 Jul 2011 04:06:12 +0200 Subject: Fix regression with distutils MANIFEST handing (#11104, #8688). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The changed behavior of sdist in 3.1 broke packaging for projects that wanted to use a manually-maintained MANIFEST file (instead of having a MANIFEST.in template and letting distutils generate the MANIFEST). The fixes that were committed for #8688 (76643c286b9f by Tarek and d54da9248ed9 by me) did not fix all issues exposed in the bug report, and also added one problem: the MANIFEST file format gained comments, but the read_manifest method was not updated to handle (i.e. ignore) them. This changeset should fix everything; the tests have been expanded and I successfully tested the 2.7 version with Mercurial, which suffered from this regression. I have grouped the versionchanged directives for these bugs in one place and added micro version numbers to help users know the quirks of the exact version they’re using. Initial report, thorough diagnosis and patch by John Dennis, further work on the patch by Stephen Thorne, and a few edits and additions by me. --- command/sdist.py | 48 +++++++++++++++++++++++++++++------------------- tests/test_sdist.py | 39 +++++++++++++++++++++++++++++++++------ 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 48cb26b6..21ea61d9 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -174,14 +174,20 @@ class sdist(Command): reading the manifest, or just using the default file set -- it all depends on the user's options. """ - # new behavior: + # new behavior when using a template: # the file list is recalculated everytime because # even if MANIFEST.in or setup.py are not changed # the user might have added some files in the tree that # need to be included. # - # This makes --force the default and only behavior. + # This makes --force the default and only behavior with templates. template_exists = os.path.isfile(self.template) + if not template_exists and self._manifest_is_not_generated(): + self.read_manifest() + self.filelist.sort() + self.filelist.remove_duplicates() + return + if not template_exists: self.warn(("manifest template '%s' does not exist " + "(using default file list)") % @@ -336,23 +342,28 @@ class sdist(Command): by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. """ - if os.path.isfile(self.manifest): - fp = open(self.manifest) - try: - first_line = fp.readline() - finally: - fp.close() - - if first_line != '# file GENERATED by distutils, do NOT edit\n': - log.info("not writing to manually maintained " - "manifest file '%s'" % self.manifest) - return + if self._manifest_is_not_generated(): + log.info("not writing to manually maintained " + "manifest file '%s'" % self.manifest) + return content = self.filelist.files[:] content.insert(0, '# file GENERATED by distutils, do NOT edit') self.execute(file_util.write_file, (self.manifest, content), "writing manifest file '%s'" % self.manifest) + def _manifest_is_not_generated(self): + # check for special comment used in 3.1.3 and higher + if not os.path.isfile(self.manifest): + return False + + fp = open(self.manifest) + try: + first_line = fp.readline() + finally: + fp.close() + return first_line != '# file GENERATED by distutils, do NOT edit\n' + def read_manifest(self): """Read the manifest file (named by 'self.manifest') and use it to fill in 'self.filelist', the list of files to include in the source @@ -360,12 +371,11 @@ class sdist(Command): """ log.info("reading manifest file '%s'", self.manifest) manifest = open(self.manifest) - while True: - line = manifest.readline() - if line == '': # end of file - break - if line[-1] == '\n': - line = line[0:-1] + for line in manifest: + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue self.filelist.append(line) manifest.close() diff --git a/tests/test_sdist.py b/tests/test_sdist.py index c7dd47fb..440af988 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -1,21 +1,19 @@ """Tests for distutils.command.sdist.""" import os +import tarfile import unittest -import shutil +import warnings import zipfile from os.path import join -import sys -import tempfile -import warnings +from textwrap import dedent from test.support import captured_stdout, check_warnings, run_unittest from distutils.command.sdist import sdist, show_formats from distutils.core import Distribution from distutils.tests.test_config import PyPIRCCommandTestCase -from distutils.errors import DistutilsExecError, DistutilsOptionError +from distutils.errors import DistutilsOptionError from distutils.spawn import find_executable -from distutils.tests import support from distutils.log import WARN from distutils.archive_util import ARCHIVE_FORMATS @@ -346,13 +344,33 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(manifest[0], '# file GENERATED by distutils, do NOT edit') + @unittest.skipUnless(ZLIB_SUPPORT, "Need zlib support to run") + def test_manifest_comments(self): + # make sure comments don't cause exceptions or wrong includes + contents = dedent("""\ + # bad.py + #bad.py + good.py + """) + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), contents) + self.write_file((self.tmp_dir, 'good.py'), '# pick me!') + self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!") + self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!") + cmd.run() + self.assertEqual(cmd.filelist.files, ['good.py']) + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() cmd.ensure_finalized() self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') + self.write_file((self.tmp_dir, 'README.manual'), + 'This project maintains its MANIFEST file itself.') cmd.run() + self.assertEqual(cmd.filelist.files, ['README.manual']) f = open(cmd.manifest) try: @@ -363,6 +381,15 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(manifest, ['README.manual']) + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + filenames = [tarinfo.name for tarinfo in archive] + finally: + archive.close() + self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO', + 'fake-1.0/README.manual']) + def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From 4a58c74f7e7e12cc7ee6e6832249c8b39dc58f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 2 Aug 2011 03:16:12 +0200 Subject: Fix incorrect mtime comparison in distutils (#11933). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a regression introduced in 9211a5d7d0b4, when uses of ST_MTIME constants were changed to uses of st_mtime attributes. As diagnosed in the bug report, this change is not merely stylistic: st_mtime is a float but ST_MTIME’s resolution is rounded to the seconds, so there was a mismatch between the values seen by file_util and dep_util which caused an sdist to be unnecessarily created a second time on an ext4 filesystem. This patch has been tested by John S. Gruber, who reported the bug. As this is a simple code revert, I think it’s okay to commit without a unit test. --- dep_util.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dep_util.py b/dep_util.py index 4e40df68..2b759056 100644 --- a/dep_util.py +++ b/dep_util.py @@ -7,6 +7,7 @@ timestamp dependency analysis.""" __revision__ = "$Id$" import os +from stat import ST_MTIME from distutils.errors import DistutilsFileError def newer(source, target): @@ -27,7 +28,7 @@ def newer(source, target): if not os.path.exists(target): return True - return os.stat(source).st_mtime > os.stat(target).st_mtime + return os.stat(source)[ST_MTIME] > os.stat(target)[ST_MTIME] def newer_pairwise(sources, targets): """Walk two filename lists in parallel, testing if each source is newer @@ -71,7 +72,7 @@ def newer_group(sources, target, missing='error'): # is more recent than 'target', then 'target' is out-of-date and # we can immediately return true. If we fall through to the end # of the loop, then 'target' is up-to-date and we return false. - target_mtime = os.stat(target).st_mtime + target_mtime = os.stat(target)[ST_MTIME] for source in sources: if not os.path.exists(source): @@ -82,7 +83,7 @@ def newer_group(sources, target, missing='error'): elif missing == 'newer': # missing source means target is return True # out-of-date - if os.stat(source).st_mtime > target_mtime: + if os.stat(source)[ST_MTIME] > target_mtime: return True return False -- cgit v1.2.1 From 477a6fa41f95c753e0c5d3cc4f6f872cdd96c3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 10 Aug 2011 02:46:33 +0200 Subject: Revert cosmetic change. A reminder: distutils only gets bug fixes. Cosmetic changes, especially in tests, are not worth the time spent, and can even make future merges of bugfixes a bit less easy. --- tests/test_build_py.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index c7c36f3c..4e46339b 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -10,7 +10,7 @@ from distutils.core import Distribution from distutils.errors import DistutilsFileError from distutils.tests import support -from test.support import run_unittest, create_empty_file +from test.support import run_unittest class BuildPyTestCase(support.TempdirManager, @@ -71,11 +71,11 @@ class BuildPyTestCase(support.TempdirManager, # create the distribution files. sources = self.mkdtemp() - create_empty_file(os.path.join(sources, "__init__.py")) + open(os.path.join(sources, "__init__.py"), "w").close() testdir = os.path.join(sources, "doc") os.mkdir(testdir) - create_empty_file(os.path.join(testdir, "testfile")) + open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) old_stdout = sys.stdout -- cgit v1.2.1 From 2af5d249da9647a383d27799c9bb9bf9802566c9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 13 Aug 2011 11:34:58 +0200 Subject: Bump version to 3.2.2rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c06002ea..b31b2d20 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.1" +__version__ = "3.2.2rc1" #--end constants-- -- cgit v1.2.1 From faba07dcbfdb0bec687496bd329985497f907fbb Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Aug 2011 20:49:41 +0200 Subject: Issue #12326: don't test the major version of sys.platform Use startswith, instead of ==, when testing sys.platform to support new platforms like Linux 3 or OpenBSD 5. --- tests/test_bdist_rpm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 030933f1..9b0639a5 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -47,7 +47,7 @@ class BuildRpmTestCase(support.TempdirManager, # XXX I am unable yet to make this test work without # spurious sdtout/stderr output under Mac OS X - if sys.platform != 'linux2': + if not sys.platform.startswith('linux'): return # this test will run only if the rpm commands are found @@ -87,7 +87,7 @@ class BuildRpmTestCase(support.TempdirManager, # XXX I am unable yet to make this test work without # spurious sdtout/stderr output under Mac OS X - if sys.platform != 'linux2': + if not sys.platform.startswith('linux'): return # http://bugs.python.org/issue1533164 -- cgit v1.2.1 From d2d11c0f224789430dee34ca05de87d70982d6b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Aug 2011 06:27:18 +0200 Subject: Refactor the copying of xxmodule.c in distutils tests (#12141). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I need to copy this file in another test too, so I moved the support code to distutils.tests.support and improved it: - don’t skip when run from the Lib/distutils/tests directory - use proper skip machinery instead of custom print/return/test suite fiddling. --- tests/support.py | 42 ++++++++++++++++++++++++++++++++++++++++++ tests/test_build_ext.py | 28 ++++------------------------ 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/tests/support.py b/tests/support.py index e258d2e5..0e33827f 100644 --- a/tests/support.py +++ b/tests/support.py @@ -2,12 +2,15 @@ import os import shutil import tempfile +import unittest +import sysconfig from copy import deepcopy from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution + class LoggingSilencer(object): def setUp(self): @@ -41,6 +44,7 @@ class LoggingSilencer(object): def clear_logs(self): self.logs = [] + class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. @@ -97,6 +101,7 @@ class TempdirManager(object): return pkg_dir, dist + class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" @@ -107,6 +112,7 @@ class DummyCommand: def ensure_finalized(self): pass + class EnvironGuard(object): def setUp(self): @@ -123,3 +129,39 @@ class EnvironGuard(object): del os.environ[key] super(EnvironGuard, self).tearDown() + + +def copy_xxmodule_c(directory): + """Helper for tests that need the xxmodule.c source file. + + Example use: + + def test_compile(self): + copy_xxmodule_c(self.tmpdir) + self.assertIn('xxmodule.c', os.listdir(self.tmpdir) + + If the source file can be found, it will be copied to *directory*. If not, + the test will be skipped. Errors during copy are not caught. + """ + filename = _get_xxmodule_path() + if filename is None: + raise unittest.SkipTest('cannot find xxmodule.c (test must run in ' + 'the python build dir)') + shutil.copy(filename, directory) + + +def _get_xxmodule_path(): + srcdir = sysconfig.get_config_var('srcdir') + candidates = [ + # use installed copy if available + os.path.join(os.path.dirname(__file__), 'xxmodule.c'), + # otherwise try using copy from build directory + os.path.join(srcdir, 'Modules', 'xxmodule.c'), + # srcdir mysteriously can be $srcdir/Lib/distutils/tests when + # this file is run from its parent directory, so walk up the + # tree to find the real srcdir + os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'), + ] + for path in candidates: + if os.path.exists(path): + return path diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 0ce7f0f8..de53afb1 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -1,14 +1,13 @@ import sys import os -import shutil from io import StringIO import textwrap from distutils.core import Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig -from distutils.tests.support import TempdirManager -from distutils.tests.support import LoggingSilencer +from distutils.tests.support import (TempdirManager, LoggingSilencer, + copy_xxmodule_c) from distutils.extension import Extension from distutils.errors import ( CompileError, DistutilsPlatformError, DistutilsSetupError, @@ -16,20 +15,11 @@ from distutils.errors import ( import unittest from test import support -from test.support import run_unittest # http://bugs.python.org/issue4373 # Don't load the xx module more than once. ALREADY_TESTED = False -def _get_source_filename(): - # use installed copy if available - tests_f = os.path.join(os.path.dirname(__file__), 'xxmodule.c') - if os.path.exists(tests_f): - return tests_f - # otherwise try using copy from build directory - srcdir = sysconfig.get_config_var('srcdir') - return os.path.join(srcdir, 'Modules', 'xxmodule.c') class BuildExtTestCase(TempdirManager, LoggingSilencer, @@ -41,9 +31,6 @@ class BuildExtTestCase(TempdirManager, self.tmp_dir = self.mkdtemp() self.sys_path = sys.path, sys.path[:] sys.path.append(self.tmp_dir) - filename = _get_source_filename() - if os.path.exists(filename): - shutil.copy(filename, self.tmp_dir) if sys.version > "2.6": import site self.old_user_base = site.USER_BASE @@ -72,9 +59,8 @@ class BuildExtTestCase(TempdirManager, def test_build_ext(self): global ALREADY_TESTED + copy_xxmodule_c(self.tmp_dir) xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') - if not os.path.exists(xx_c): - return xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir @@ -518,13 +504,7 @@ class BuildExtTestCase(TempdirManager, def test_suite(): - src = _get_source_filename() - if not os.path.exists(src): - if support.verbose: - print('test_build_ext: Cannot find source code (test' - ' must run in python build dir)') - return unittest.TestSuite() - else: return unittest.makeSuite(BuildExtTestCase) + return unittest.makeSuite(BuildExtTestCase) if __name__ == '__main__': support.run_unittest(test_suite()) -- cgit v1.2.1 From 5deb9c1a36539871aeb5a1fab451942aa2cf5cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Aug 2011 06:27:18 +0200 Subject: Refactor the copying of xxmodule.c in distutils tests (#12141). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I need to copy this file in another test too, so I moved the support code to distutils.tests.support and improved it: - don’t skip when run from the Lib/distutils/tests directory - use proper skip machinery instead of custom print/return/test suite fiddling. --- tests/support.py | 42 ++++++++++++++++++++++++++++++++++++++++++ tests/test_build_ext.py | 28 ++++------------------------ 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/tests/support.py b/tests/support.py index e258d2e5..0e33827f 100644 --- a/tests/support.py +++ b/tests/support.py @@ -2,12 +2,15 @@ import os import shutil import tempfile +import unittest +import sysconfig from copy import deepcopy from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution + class LoggingSilencer(object): def setUp(self): @@ -41,6 +44,7 @@ class LoggingSilencer(object): def clear_logs(self): self.logs = [] + class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. @@ -97,6 +101,7 @@ class TempdirManager(object): return pkg_dir, dist + class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" @@ -107,6 +112,7 @@ class DummyCommand: def ensure_finalized(self): pass + class EnvironGuard(object): def setUp(self): @@ -123,3 +129,39 @@ class EnvironGuard(object): del os.environ[key] super(EnvironGuard, self).tearDown() + + +def copy_xxmodule_c(directory): + """Helper for tests that need the xxmodule.c source file. + + Example use: + + def test_compile(self): + copy_xxmodule_c(self.tmpdir) + self.assertIn('xxmodule.c', os.listdir(self.tmpdir) + + If the source file can be found, it will be copied to *directory*. If not, + the test will be skipped. Errors during copy are not caught. + """ + filename = _get_xxmodule_path() + if filename is None: + raise unittest.SkipTest('cannot find xxmodule.c (test must run in ' + 'the python build dir)') + shutil.copy(filename, directory) + + +def _get_xxmodule_path(): + srcdir = sysconfig.get_config_var('srcdir') + candidates = [ + # use installed copy if available + os.path.join(os.path.dirname(__file__), 'xxmodule.c'), + # otherwise try using copy from build directory + os.path.join(srcdir, 'Modules', 'xxmodule.c'), + # srcdir mysteriously can be $srcdir/Lib/distutils/tests when + # this file is run from its parent directory, so walk up the + # tree to find the real srcdir + os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'), + ] + for path in candidates: + if os.path.exists(path): + return path diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 0ce7f0f8..de53afb1 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -1,14 +1,13 @@ import sys import os -import shutil from io import StringIO import textwrap from distutils.core import Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig -from distutils.tests.support import TempdirManager -from distutils.tests.support import LoggingSilencer +from distutils.tests.support import (TempdirManager, LoggingSilencer, + copy_xxmodule_c) from distutils.extension import Extension from distutils.errors import ( CompileError, DistutilsPlatformError, DistutilsSetupError, @@ -16,20 +15,11 @@ from distutils.errors import ( import unittest from test import support -from test.support import run_unittest # http://bugs.python.org/issue4373 # Don't load the xx module more than once. ALREADY_TESTED = False -def _get_source_filename(): - # use installed copy if available - tests_f = os.path.join(os.path.dirname(__file__), 'xxmodule.c') - if os.path.exists(tests_f): - return tests_f - # otherwise try using copy from build directory - srcdir = sysconfig.get_config_var('srcdir') - return os.path.join(srcdir, 'Modules', 'xxmodule.c') class BuildExtTestCase(TempdirManager, LoggingSilencer, @@ -41,9 +31,6 @@ class BuildExtTestCase(TempdirManager, self.tmp_dir = self.mkdtemp() self.sys_path = sys.path, sys.path[:] sys.path.append(self.tmp_dir) - filename = _get_source_filename() - if os.path.exists(filename): - shutil.copy(filename, self.tmp_dir) if sys.version > "2.6": import site self.old_user_base = site.USER_BASE @@ -72,9 +59,8 @@ class BuildExtTestCase(TempdirManager, def test_build_ext(self): global ALREADY_TESTED + copy_xxmodule_c(self.tmp_dir) xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') - if not os.path.exists(xx_c): - return xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir @@ -518,13 +504,7 @@ class BuildExtTestCase(TempdirManager, def test_suite(): - src = _get_source_filename() - if not os.path.exists(src): - if support.verbose: - print('test_build_ext: Cannot find source code (test' - ' must run in python build dir)') - return unittest.TestSuite() - else: return unittest.makeSuite(BuildExtTestCase) + return unittest.makeSuite(BuildExtTestCase) if __name__ == '__main__': support.run_unittest(test_suite()) -- cgit v1.2.1 From 1273191a70b9b8a174da03506f34fe9e3b53fc83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Aug 2011 07:00:41 +0200 Subject: Rework test_record a bit to make the test more exact --- tests/test_install.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/test_install.py b/tests/test_install.py index ed69b0cb..3e47d819 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -1,7 +1,6 @@ """Tests for distutils.command.install.""" import os -import os.path import sys import unittest import site @@ -167,33 +166,36 @@ class InstallTestCase(support.TempdirManager, self.assertRaises(DistutilsOptionError, cmd.finalize_options) def test_record(self): - install_dir = self.mkdtemp() - pkgdir, dist = self.create_dist() + project_dir, dist = self.create_dist(scripts=['hello']) + self.addCleanup(os.chdir, os.getcwd()) + os.chdir(project_dir) + self.write_file('hello', "print('o hai')") - dist = Distribution() cmd = install(dist) dist.command_obj['install'] = cmd cmd.root = install_dir - cmd.record = os.path.join(pkgdir, 'RECORD') + cmd.record = os.path.join(project_dir, 'RECORD') cmd.ensure_finalized() - cmd.run() - # let's check the RECORD file was created with one - # line (the egg info file) f = open(cmd.record) try: - self.assertEqual(len(f.readlines()), 1) + content = f.read() finally: f.close() + found = [os.path.basename(line) for line in content.splitlines()] + expected = ['hello', + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + def test_debug_mode(self): # this covers the code called when DEBUG is set old_logs_len = len(self.logs) install_module.DEBUG = True try: - with captured_stdout() as stdout: + with captured_stdout(): self.test_record() finally: install_module.DEBUG = False -- cgit v1.2.1 From 07f33c845c7823038bc7a8a3eee9a55caff2b80d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 21 Aug 2011 17:02:07 +0200 Subject: Factor out the build_ext fixup for shared Python builds. I need this to fix the failing test_install. --- tests/support.py | 27 +++++++++++++++++++++++++++ tests/test_build_ext.py | 31 ++++++------------------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/tests/support.py b/tests/support.py index 0e33827f..562a65c8 100644 --- a/tests/support.py +++ b/tests/support.py @@ -1,5 +1,6 @@ """Support code for distutils test cases.""" import os +import sys import shutil import tempfile import unittest @@ -165,3 +166,29 @@ def _get_xxmodule_path(): for path in candidates: if os.path.exists(path): return path + + +def fixup_build_ext(cmd): + """Function needed to make build_ext tests pass on shared builds. + + When Python was build with --enable-shared, -L. is not good enough to find + the libpython.so. This is because regrtest runs it under a tempdir, + not in the top level where the .so lives. By the time we've gotten here, + Python's already been chdir'd to the tempdir. This function work arounds + that. Example use: + + cmd = build_ext(dist) + support.fixup_build_ext(cmd) + cmd.ensure_finalized() + """ + # To further add to the fun, we can't just add library_dirs to the + # Extension() instance because that doesn't get plumbed through to the + # final compiler command. + if (sysconfig.get_config_var('Py_ENABLE_SHARED') and + not sys.platform.startswith('win')): + runshared = sysconfig.get_config_var('RUNSHARED') + if runshared is None: + cmd.library_dirs = ['.'] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = value.split(os.pathsep) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index de53afb1..8eb59b4d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -7,7 +7,7 @@ from distutils.core import Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig from distutils.tests.support import (TempdirManager, LoggingSilencer, - copy_xxmodule_c) + copy_xxmodule_c, fixup_build_ext) from distutils.extension import Extension from distutils.errors import ( CompileError, DistutilsPlatformError, DistutilsSetupError, @@ -38,25 +38,6 @@ class BuildExtTestCase(TempdirManager, from distutils.command import build_ext build_ext.USER_BASE = site.USER_BASE - def _fixup_command(self, cmd): - # When Python was build with --enable-shared, -L. is not good enough - # to find the libpython.so. This is because regrtest runs it - # under a tempdir, not in the top level where the .so lives. By the - # time we've gotten here, Python's already been chdir'd to the - # tempdir. - # - # To further add to the fun, we can't just add library_dirs to the - # Extension() instance because that doesn't get plumbed through to the - # final compiler command. - if (sysconfig.get_config_var('Py_ENABLE_SHARED') and - not sys.platform.startswith('win')): - runshared = sysconfig.get_config_var('RUNSHARED') - if runshared is None: - cmd.library_dirs = ['.'] - else: - name, equals, value = runshared.partition('=') - cmd.library_dirs = value.split(os.pathsep) - def test_build_ext(self): global ALREADY_TESTED copy_xxmodule_c(self.tmp_dir) @@ -65,7 +46,7 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir cmd = build_ext(dist) - self._fixup_command(cmd) + fixup_build_ext(cmd) if os.name == "nt": # On Windows, we must build a debug version iff running # a debug build of Python @@ -162,9 +143,9 @@ class BuildExtTestCase(TempdirManager, # see if include_dirs and library_dirs # were set - self.assertTrue(lib in cmd.library_dirs) - self.assertTrue(lib in cmd.rpath) - self.assertTrue(incl in cmd.include_dirs) + self.assertIn(lib, cmd.library_dirs) + self.assertIn(lib, cmd.rpath) + self.assertIn(incl, cmd.include_dirs) def test_optional_extension(self): @@ -320,7 +301,7 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) cmd = build_ext(dist) - self._fixup_command(cmd) + fixup_build_ext(cmd) cmd.ensure_finalized() self.assertEqual(len(cmd.get_outputs()), 1) -- cgit v1.2.1 From b359d8104f3393cb1eeda51a76ffc85289b91b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Aug 2011 07:00:41 +0200 Subject: Rework test_record a bit to make the test more exact --- tests/test_install.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/test_install.py b/tests/test_install.py index ed69b0cb..3e47d819 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -1,7 +1,6 @@ """Tests for distutils.command.install.""" import os -import os.path import sys import unittest import site @@ -167,33 +166,36 @@ class InstallTestCase(support.TempdirManager, self.assertRaises(DistutilsOptionError, cmd.finalize_options) def test_record(self): - install_dir = self.mkdtemp() - pkgdir, dist = self.create_dist() + project_dir, dist = self.create_dist(scripts=['hello']) + self.addCleanup(os.chdir, os.getcwd()) + os.chdir(project_dir) + self.write_file('hello', "print('o hai')") - dist = Distribution() cmd = install(dist) dist.command_obj['install'] = cmd cmd.root = install_dir - cmd.record = os.path.join(pkgdir, 'RECORD') + cmd.record = os.path.join(project_dir, 'RECORD') cmd.ensure_finalized() - cmd.run() - # let's check the RECORD file was created with one - # line (the egg info file) f = open(cmd.record) try: - self.assertEqual(len(f.readlines()), 1) + content = f.read() finally: f.close() + found = [os.path.basename(line) for line in content.splitlines()] + expected = ['hello', + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + def test_debug_mode(self): # this covers the code called when DEBUG is set old_logs_len = len(self.logs) install_module.DEBUG = True try: - with captured_stdout() as stdout: + with captured_stdout(): self.test_record() finally: install_module.DEBUG = False -- cgit v1.2.1 From 13d8a65041270b836929fe82d2c925b92bdfbb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Aug 2011 07:08:51 +0200 Subject: Add a test for extension modules in the distutils record file. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I made a note a month ago that install --record wrote incorrect entries for extension modules (I think the problem was that the first character of the file was stripped), so I’m now adding a test to try to reproduce that in the current versions. --- tests/test_install.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_install.py b/tests/test_install.py index 3e47d819..2133fa79 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -7,11 +7,14 @@ import site from test.support import captured_stdout, run_unittest +from distutils import sysconfig from distutils.command.install import install from distutils.command import install as install_module +from distutils.command.build_ext import build_ext from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution from distutils.errors import DistutilsOptionError +from distutils.extension import Extension from distutils.tests import support @@ -190,6 +193,36 @@ class InstallTestCase(support.TempdirManager, 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) + def test_record_extensions(self): + install_dir = self.mkdtemp() + project_dir, dist = self.create_dist(ext_modules=[ + Extension('xx', ['xxmodule.c'])]) + self.addCleanup(os.chdir, os.getcwd()) + os.chdir(project_dir) + support.copy_xxmodule_c(project_dir) + + buildcmd = build_ext(dist) + buildcmd.ensure_finalized() + buildcmd.run() + + cmd = install(dist) + dist.command_obj['install'] = cmd + cmd.root = install_dir + cmd.record = os.path.join(project_dir, 'RECORD') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.record) + try: + content = f.read() + finally: + f.close() + + found = [os.path.basename(line) for line in content.splitlines()] + expected = ['xx%s' % sysconfig.get_config_var('SO'), + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + def test_debug_mode(self): # this covers the code called when DEBUG is set old_logs_len = len(self.logs) -- cgit v1.2.1 From d45ea9f230f69621dd6e9fbf8b8c8526a7e28ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Aug 2011 07:08:51 +0200 Subject: Add a test for extension modules in the distutils record file. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I made a note a month ago that install --record wrote incorrect entries for extension modules (I think the problem was that the first character of the file was stripped), so I’m now adding a test to try to reproduce that in the current versions. --- tests/test_install.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_install.py b/tests/test_install.py index 3e47d819..2133fa79 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -7,11 +7,14 @@ import site from test.support import captured_stdout, run_unittest +from distutils import sysconfig from distutils.command.install import install from distutils.command import install as install_module +from distutils.command.build_ext import build_ext from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution from distutils.errors import DistutilsOptionError +from distutils.extension import Extension from distutils.tests import support @@ -190,6 +193,36 @@ class InstallTestCase(support.TempdirManager, 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) + def test_record_extensions(self): + install_dir = self.mkdtemp() + project_dir, dist = self.create_dist(ext_modules=[ + Extension('xx', ['xxmodule.c'])]) + self.addCleanup(os.chdir, os.getcwd()) + os.chdir(project_dir) + support.copy_xxmodule_c(project_dir) + + buildcmd = build_ext(dist) + buildcmd.ensure_finalized() + buildcmd.run() + + cmd = install(dist) + dist.command_obj['install'] = cmd + cmd.root = install_dir + cmd.record = os.path.join(project_dir, 'RECORD') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.record) + try: + content = f.read() + finally: + f.close() + + found = [os.path.basename(line) for line in content.splitlines()] + expected = ['xx%s' % sysconfig.get_config_var('SO'), + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + def test_debug_mode(self): # this covers the code called when DEBUG is set old_logs_len = len(self.logs) -- cgit v1.2.1 From b4d117a031abecb380ffea57535241ca339d3622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Aug 2011 07:25:39 +0200 Subject: Dedent example in docstring --- tests/support.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/support.py b/tests/support.py index 0e33827f..81289db4 100644 --- a/tests/support.py +++ b/tests/support.py @@ -136,9 +136,9 @@ def copy_xxmodule_c(directory): Example use: - def test_compile(self): - copy_xxmodule_c(self.tmpdir) - self.assertIn('xxmodule.c', os.listdir(self.tmpdir) + def test_compile(self): + copy_xxmodule_c(self.tmpdir) + self.assertIn('xxmodule.c', os.listdir(self.tmpdir) If the source file can be found, it will be copied to *directory*. If not, the test will be skipped. Errors during copy are not caught. -- cgit v1.2.1 From d6a76c9070699d8701c00123b24d204484df68e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 20 Aug 2011 19:52:07 +0200 Subject: Fix sdist test on Windows (#12678). Patch by Jeremy Kloth. --- tests/test_sdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 440af988..f34f786c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -365,6 +365,7 @@ class SDistTestCase(PyPIRCCommandTestCase): def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() + cmd.formats = ['gztar'] cmd.ensure_finalized() self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') self.write_file((self.tmp_dir, 'README.manual'), -- cgit v1.2.1 From c0f9165822276f0cd6b74ea6d0296e494b3e5a3f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 21 Aug 2011 00:39:18 +0200 Subject: Issue #12326: refactor usage of sys.platform * Use str.startswith(tuple): I didn't know this Python feature, Python rocks! * Replace sometimes sys.platform.startswith('linux') with sys.platform == 'linux' * sys.platform doesn't contain the major version on Cygwin on Mac OS X (it's just 'cygwin' and 'darwin') --- command/build_ext.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 8d843d68..8baf538f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -240,8 +240,7 @@ class build_ext(Command): # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs sysconfig.get_config_var('Py_ENABLE_SHARED') - if ((sys.platform.startswith('linux') or sys.platform.startswith('gnu') - or sys.platform.startswith('sunos')) + if (sys.platform.startswith(('linux', 'gnu', 'sunos')) and sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions -- cgit v1.2.1 From e081dd68394c4fb8961c78fd2a4d8c71f69ac0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 21 Aug 2011 12:53:37 +0200 Subject: Add missing closing paren in docstring (thanks Ezio) --- tests/support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/support.py b/tests/support.py index 81289db4..49277111 100644 --- a/tests/support.py +++ b/tests/support.py @@ -138,7 +138,7 @@ def copy_xxmodule_c(directory): def test_compile(self): copy_xxmodule_c(self.tmpdir) - self.assertIn('xxmodule.c', os.listdir(self.tmpdir) + self.assertIn('xxmodule.c', os.listdir(self.tmpdir)) If the source file can be found, it will be copied to *directory*. If not, the test will be skipped. Errors during copy are not caught. -- cgit v1.2.1 From b96da7b0feee847c2a2b51678da402731378b097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 21 Aug 2011 17:02:07 +0200 Subject: Factor out the build_ext fixup for shared Python builds. I need this to fix the failing test_install. --- tests/support.py | 27 +++++++++++++++++++++++++++ tests/test_build_ext.py | 31 ++++++------------------------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/tests/support.py b/tests/support.py index 49277111..ffef98ba 100644 --- a/tests/support.py +++ b/tests/support.py @@ -1,5 +1,6 @@ """Support code for distutils test cases.""" import os +import sys import shutil import tempfile import unittest @@ -165,3 +166,29 @@ def _get_xxmodule_path(): for path in candidates: if os.path.exists(path): return path + + +def fixup_build_ext(cmd): + """Function needed to make build_ext tests pass on shared builds. + + When Python was build with --enable-shared, -L. is not good enough to find + the libpython.so. This is because regrtest runs it under a tempdir, + not in the top level where the .so lives. By the time we've gotten here, + Python's already been chdir'd to the tempdir. This function work arounds + that. Example use: + + cmd = build_ext(dist) + support.fixup_build_ext(cmd) + cmd.ensure_finalized() + """ + # To further add to the fun, we can't just add library_dirs to the + # Extension() instance because that doesn't get plumbed through to the + # final compiler command. + if (sysconfig.get_config_var('Py_ENABLE_SHARED') and + not sys.platform.startswith('win')): + runshared = sysconfig.get_config_var('RUNSHARED') + if runshared is None: + cmd.library_dirs = ['.'] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = value.split(os.pathsep) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index de53afb1..8eb59b4d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -7,7 +7,7 @@ from distutils.core import Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig from distutils.tests.support import (TempdirManager, LoggingSilencer, - copy_xxmodule_c) + copy_xxmodule_c, fixup_build_ext) from distutils.extension import Extension from distutils.errors import ( CompileError, DistutilsPlatformError, DistutilsSetupError, @@ -38,25 +38,6 @@ class BuildExtTestCase(TempdirManager, from distutils.command import build_ext build_ext.USER_BASE = site.USER_BASE - def _fixup_command(self, cmd): - # When Python was build with --enable-shared, -L. is not good enough - # to find the libpython.so. This is because regrtest runs it - # under a tempdir, not in the top level where the .so lives. By the - # time we've gotten here, Python's already been chdir'd to the - # tempdir. - # - # To further add to the fun, we can't just add library_dirs to the - # Extension() instance because that doesn't get plumbed through to the - # final compiler command. - if (sysconfig.get_config_var('Py_ENABLE_SHARED') and - not sys.platform.startswith('win')): - runshared = sysconfig.get_config_var('RUNSHARED') - if runshared is None: - cmd.library_dirs = ['.'] - else: - name, equals, value = runshared.partition('=') - cmd.library_dirs = value.split(os.pathsep) - def test_build_ext(self): global ALREADY_TESTED copy_xxmodule_c(self.tmp_dir) @@ -65,7 +46,7 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir cmd = build_ext(dist) - self._fixup_command(cmd) + fixup_build_ext(cmd) if os.name == "nt": # On Windows, we must build a debug version iff running # a debug build of Python @@ -162,9 +143,9 @@ class BuildExtTestCase(TempdirManager, # see if include_dirs and library_dirs # were set - self.assertTrue(lib in cmd.library_dirs) - self.assertTrue(lib in cmd.rpath) - self.assertTrue(incl in cmd.include_dirs) + self.assertIn(lib, cmd.library_dirs) + self.assertIn(lib, cmd.rpath) + self.assertIn(incl, cmd.include_dirs) def test_optional_extension(self): @@ -320,7 +301,7 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) cmd = build_ext(dist) - self._fixup_command(cmd) + fixup_build_ext(cmd) cmd.ensure_finalized() self.assertEqual(len(cmd.get_outputs()), 1) -- cgit v1.2.1 From babd93f6a36c537938f716994004f31ea22e03e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 21 Aug 2011 17:03:19 +0200 Subject: Fix distutils test_install for shared CPython builds --- tests/test_install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_install.py b/tests/test_install.py index 2133fa79..e065aa3d 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -202,6 +202,7 @@ class InstallTestCase(support.TempdirManager, support.copy_xxmodule_c(project_dir) buildcmd = build_ext(dist) + support.fixup_build_ext(buildcmd) buildcmd.ensure_finalized() buildcmd.run() -- cgit v1.2.1 From 9e1eee4615f9ef0410ef1502b111c74c7880e32b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 21 Aug 2011 17:03:19 +0200 Subject: Fix distutils test_install for shared CPython builds --- tests/test_install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_install.py b/tests/test_install.py index 2133fa79..e065aa3d 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -202,6 +202,7 @@ class InstallTestCase(support.TempdirManager, support.copy_xxmodule_c(project_dir) buildcmd = build_ext(dist) + support.fixup_build_ext(buildcmd) buildcmd.ensure_finalized() buildcmd.run() -- cgit v1.2.1 From 54bac89d98770275e3801f293c88251211354d8c Mon Sep 17 00:00:00 2001 From: Nadeem Vawda Date: Sun, 21 Aug 2011 22:35:41 +0200 Subject: Issue #12678: Fix distutils sdist test on Windows. Patch by Jeremy Kloth. --- tests/test_sdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 440af988..f34f786c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -365,6 +365,7 @@ class SDistTestCase(PyPIRCCommandTestCase): def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() + cmd.formats = ['gztar'] cmd.ensure_finalized() self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') self.write_file((self.tmp_dir, 'README.manual'), -- cgit v1.2.1 From 6e23eb2a22ad885a41f58d035b7d11c3e3c0658a Mon Sep 17 00:00:00 2001 From: Nadeem Vawda Date: Sun, 21 Aug 2011 22:35:41 +0200 Subject: Issue #12678: Fix distutils sdist test on Windows. Patch by Jeremy Kloth. --- tests/test_sdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 440af988..f34f786c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -365,6 +365,7 @@ class SDistTestCase(PyPIRCCommandTestCase): def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() + cmd.formats = ['gztar'] cmd.ensure_finalized() self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') self.write_file((self.tmp_dir, 'README.manual'), -- cgit v1.2.1 From 5b49959934750e3cfd186c85fe8f5a4bcffc253b Mon Sep 17 00:00:00 2001 From: Nadeem Vawda Date: Sun, 21 Aug 2011 22:40:04 +0200 Subject: Issue #12678: Fix distutils sdist test on Windows. Patch by Jeremy Kloth. --- tests/test_sdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 8da6fe4d..42faa5d1 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -351,6 +351,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # filling data_files by pointing files in package_data dist.package_data = {'somecode': ['*.txt']} self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + cmd.formats = ['gztar'] cmd.ensure_finalized() cmd.run() -- cgit v1.2.1 From fa9b78e7d73bf1af3d6959caa43d19c7d6059240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 24 Aug 2011 01:29:10 +0200 Subject: Fix distutils tests on Windows (#12678). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - First, support.fixup_build_ext (already used to set proper library_dirs value under Unix shared builds) gains the ability to correctly set the debug attribute under Windows debug builds. - Second, the filename for the extension module gets a _d suffix under debug builds. - Third, the test code properly puts our customized build_ext object into an internal dictionary to make sure that the install command will later use our object instead of re-creating one. That’s the downside of using low-level APIs in our test code: we have to manually push knobs and turn handles that would otherwise be handled behind the scenes. Thanks to Nadeem for the testing. --- tests/support.py | 28 +++++++++++++++++----------- tests/test_build_ext.py | 7 ------- tests/test_install.py | 18 +++++++++++++----- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/tests/support.py b/tests/support.py index 562a65c8..7a76ca05 100644 --- a/tests/support.py +++ b/tests/support.py @@ -169,23 +169,29 @@ def _get_xxmodule_path(): def fixup_build_ext(cmd): - """Function needed to make build_ext tests pass on shared builds. + """Function needed to make build_ext tests pass. - When Python was build with --enable-shared, -L. is not good enough to find - the libpython.so. This is because regrtest runs it under a tempdir, - not in the top level where the .so lives. By the time we've gotten here, - Python's already been chdir'd to the tempdir. This function work arounds - that. Example use: + When Python was build with --enable-shared on Unix, -L. is not good + enough to find the libpython.so. This is because regrtest runs + it under a tempdir, not in the top level where the .so lives. By the + time we've gotten here, Python's already been chdir'd to the tempdir. + + When Python was built with in debug mode on Windows, build_ext commands + need their debug attribute set, and it is not done automatically for + some reason. + + This function handles both of these things. Example use: cmd = build_ext(dist) support.fixup_build_ext(cmd) cmd.ensure_finalized() """ - # To further add to the fun, we can't just add library_dirs to the - # Extension() instance because that doesn't get plumbed through to the - # final compiler command. - if (sysconfig.get_config_var('Py_ENABLE_SHARED') and - not sys.platform.startswith('win')): + if os.name == 'nt': + cmd.debug = sys.executable.endswith('_d.exe') + elif sysconfig.get_config_var('Py_ENABLE_SHARED'): + # To further add to the shared builds fun on Unix, we can't just add + # library_dirs to the Extension() instance because that doesn't get + # plumbed through to the final compiler command. runshared = sysconfig.get_config_var('RUNSHARED') if runshared is None: cmd.library_dirs = ['.'] diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 8eb59b4d..18274376 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -47,10 +47,6 @@ class BuildExtTestCase(TempdirManager, dist.package_dir = self.tmp_dir cmd = build_ext(dist) fixup_build_ext(cmd) - if os.name == "nt": - # On Windows, we must build a debug version iff running - # a debug build of Python - cmd.debug = sys.executable.endswith("_d.exe") cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir @@ -305,9 +301,6 @@ class BuildExtTestCase(TempdirManager, cmd.ensure_finalized() self.assertEqual(len(cmd.get_outputs()), 1) - if os.name == "nt": - cmd.debug = sys.executable.endswith("_d.exe") - cmd.build_lib = os.path.join(self.tmp_dir, 'build') cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') diff --git a/tests/test_install.py b/tests/test_install.py index e065aa3d..5c105af9 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -18,6 +18,14 @@ from distutils.extension import Extension from distutils.tests import support + +def _make_ext_name(modname): + if os.name == 'nt': + if sys.executable.endswith('_d.exe'): + modname += '_d' + return modname + sysconfig.get_config_var('SO') + + class InstallTestCase(support.TempdirManager, support.EnvironGuard, support.LoggingSilencer, @@ -201,13 +209,13 @@ class InstallTestCase(support.TempdirManager, os.chdir(project_dir) support.copy_xxmodule_c(project_dir) - buildcmd = build_ext(dist) - support.fixup_build_ext(buildcmd) - buildcmd.ensure_finalized() - buildcmd.run() + buildextcmd = build_ext(dist) + support.fixup_build_ext(buildextcmd) + buildextcmd.ensure_finalized() cmd = install(dist) dist.command_obj['install'] = cmd + dist.command_obj['build_ext'] = buildextcmd cmd.root = install_dir cmd.record = os.path.join(project_dir, 'RECORD') cmd.ensure_finalized() @@ -220,7 +228,7 @@ class InstallTestCase(support.TempdirManager, f.close() found = [os.path.basename(line) for line in content.splitlines()] - expected = ['xx%s' % sysconfig.get_config_var('SO'), + expected = [_make_ext_name('xx'), 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) -- cgit v1.2.1 From c8bf9949a369a69aa561332b150f813539dbfd87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 24 Aug 2011 01:29:10 +0200 Subject: Fix distutils tests on Windows (#12678). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - First, support.fixup_build_ext (already used to set proper library_dirs value under Unix shared builds) gains the ability to correctly set the debug attribute under Windows debug builds. - Second, the filename for the extension module gets a _d suffix under debug builds. - Third, the test code properly puts our customized build_ext object into an internal dictionary to make sure that the install command will later use our object instead of re-creating one. That’s the downside of using low-level APIs in our test code: we have to manually push knobs and turn handles that would otherwise be handled behind the scenes. Thanks to Nadeem for the testing. --- tests/support.py | 28 +++++++++++++++++----------- tests/test_build_ext.py | 7 ------- tests/test_install.py | 18 +++++++++++++----- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/tests/support.py b/tests/support.py index ffef98ba..dc0e660e 100644 --- a/tests/support.py +++ b/tests/support.py @@ -169,23 +169,29 @@ def _get_xxmodule_path(): def fixup_build_ext(cmd): - """Function needed to make build_ext tests pass on shared builds. + """Function needed to make build_ext tests pass. - When Python was build with --enable-shared, -L. is not good enough to find - the libpython.so. This is because regrtest runs it under a tempdir, - not in the top level where the .so lives. By the time we've gotten here, - Python's already been chdir'd to the tempdir. This function work arounds - that. Example use: + When Python was build with --enable-shared on Unix, -L. is not good + enough to find the libpython.so. This is because regrtest runs + it under a tempdir, not in the top level where the .so lives. By the + time we've gotten here, Python's already been chdir'd to the tempdir. + + When Python was built with in debug mode on Windows, build_ext commands + need their debug attribute set, and it is not done automatically for + some reason. + + This function handles both of these things. Example use: cmd = build_ext(dist) support.fixup_build_ext(cmd) cmd.ensure_finalized() """ - # To further add to the fun, we can't just add library_dirs to the - # Extension() instance because that doesn't get plumbed through to the - # final compiler command. - if (sysconfig.get_config_var('Py_ENABLE_SHARED') and - not sys.platform.startswith('win')): + if os.name == 'nt': + cmd.debug = sys.executable.endswith('_d.exe') + elif sysconfig.get_config_var('Py_ENABLE_SHARED'): + # To further add to the shared builds fun on Unix, we can't just add + # library_dirs to the Extension() instance because that doesn't get + # plumbed through to the final compiler command. runshared = sysconfig.get_config_var('RUNSHARED') if runshared is None: cmd.library_dirs = ['.'] diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 8eb59b4d..18274376 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -47,10 +47,6 @@ class BuildExtTestCase(TempdirManager, dist.package_dir = self.tmp_dir cmd = build_ext(dist) fixup_build_ext(cmd) - if os.name == "nt": - # On Windows, we must build a debug version iff running - # a debug build of Python - cmd.debug = sys.executable.endswith("_d.exe") cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir @@ -305,9 +301,6 @@ class BuildExtTestCase(TempdirManager, cmd.ensure_finalized() self.assertEqual(len(cmd.get_outputs()), 1) - if os.name == "nt": - cmd.debug = sys.executable.endswith("_d.exe") - cmd.build_lib = os.path.join(self.tmp_dir, 'build') cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') diff --git a/tests/test_install.py b/tests/test_install.py index e065aa3d..5c105af9 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -18,6 +18,14 @@ from distutils.extension import Extension from distutils.tests import support + +def _make_ext_name(modname): + if os.name == 'nt': + if sys.executable.endswith('_d.exe'): + modname += '_d' + return modname + sysconfig.get_config_var('SO') + + class InstallTestCase(support.TempdirManager, support.EnvironGuard, support.LoggingSilencer, @@ -201,13 +209,13 @@ class InstallTestCase(support.TempdirManager, os.chdir(project_dir) support.copy_xxmodule_c(project_dir) - buildcmd = build_ext(dist) - support.fixup_build_ext(buildcmd) - buildcmd.ensure_finalized() - buildcmd.run() + buildextcmd = build_ext(dist) + support.fixup_build_ext(buildextcmd) + buildextcmd.ensure_finalized() cmd = install(dist) dist.command_obj['install'] = cmd + dist.command_obj['build_ext'] = buildextcmd cmd.root = install_dir cmd.record = os.path.join(project_dir, 'RECORD') cmd.ensure_finalized() @@ -220,7 +228,7 @@ class InstallTestCase(support.TempdirManager, f.close() found = [os.path.basename(line) for line in content.splitlines()] - expected = ['xx%s' % sysconfig.get_config_var('SO'), + expected = [_make_ext_name('xx'), 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) -- cgit v1.2.1 From aa066f5e3d725c0b3aff6f5c10488b054d4bc846 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 25 Aug 2011 18:32:02 +0200 Subject: Issue #12333: fix test_distutils failures under Solaris and derivatives --- tests/support.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/support.py b/tests/support.py index dc0e660e..44fcd6b7 100644 --- a/tests/support.py +++ b/tests/support.py @@ -54,9 +54,13 @@ class TempdirManager(object): def setUp(self): super().setUp() + self.old_cwd = os.getcwd() self.tempdirs = [] def tearDown(self): + # Restore working dir, for Solaris and derivatives, where rmdir() + # on the current directory fails. + os.chdir(self.old_cwd) super().tearDown() while self.tempdirs: d = self.tempdirs.pop() -- cgit v1.2.1 From 6198e2b4d7424a977b9461df869598fb5404c3c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Mon, 29 Aug 2011 21:48:39 +0200 Subject: Make bdist_* commands respect --skip-build passed to bdist (#10946) --- command/bdist_dumb.py | 5 +++-- command/bdist_msi.py | 6 +++++- command/bdist_wininst.py | 6 +++++- tests/test_bdist.py | 48 +++++++++++++++++++++++++++--------------------- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 170e8894..1ab09d16 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -47,7 +47,7 @@ class bdist_dumb(Command): self.format = None self.keep_temp = 0 self.dist_dir = None - self.skip_build = 0 + self.skip_build = None self.relative = 0 def finalize_options(self): @@ -65,7 +65,8 @@ class bdist_dumb(Command): self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name')) + ('plat_name', 'plat_name'), + ('skip_build', 'skip_build')) def run(self): if not self.skip_build: diff --git a/command/bdist_msi.py b/command/bdist_msi.py index b11957a7..b3cfe9ce 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -130,18 +130,22 @@ class bdist_msi(Command): self.no_target_optimize = 0 self.target_version = None self.dist_dir = None - self.skip_build = 0 + self.skip_build = None self.install_script = None self.pre_install_script = None self.versions = None def finalize_options(self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'msi') + short_version = get_python_version() if (not self.target_version) and self.distribution.has_ext_modules(): self.target_version = short_version + if self.target_version: self.versions = [self.target_version] if not self.skip_build and self.distribution.has_ext_modules()\ diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index b7916e31..e3ed3ad8 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -65,13 +65,15 @@ class bdist_wininst(Command): self.dist_dir = None self.bitmap = None self.title = None - self.skip_build = 0 + self.skip_build = None self.install_script = None self.pre_install_script = None self.user_access_control = None def finalize_options(self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + if self.bdist_dir is None: if self.skip_build and self.plat_name: # If build is skipped and plat_name is overridden, bdist will @@ -81,8 +83,10 @@ class bdist_wininst(Command): # next the command will be initialized using that name bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'wininst') + if not self.target_version: self.target_version = "" + if not self.skip_build and self.distribution.has_ext_modules(): short_version = get_python_version() if self.target_version and self.target_version != short_version: diff --git a/tests/test_bdist.py b/tests/test_bdist.py index 94d40cc2..503a6e85 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -1,41 +1,47 @@ """Tests for distutils.command.bdist.""" -import unittest -import sys import os -import tempfile -import shutil +import unittest from test.support import run_unittest -from distutils.core import Distribution from distutils.command.bdist import bdist from distutils.tests import support -from distutils.spawn import find_executable -from distutils import spawn -from distutils.errors import DistutilsExecError + class BuildTestCase(support.TempdirManager, unittest.TestCase): def test_formats(self): - # let's create a command and make sure - # we can fix the format - pkg_pth, dist = self.create_dist() + # we can set the format + dist = self.create_dist()[1] cmd = bdist(dist) cmd.formats = ['msi'] cmd.ensure_finalized() self.assertEqual(cmd.formats, ['msi']) - # what format bdist offers ? - # XXX an explicit list in bdist is - # not the best way to bdist_* commands - # we should add a registry - formats = ['rpm', 'zip', 'gztar', 'bztar', 'ztar', - 'tar', 'wininst', 'msi'] - formats.sort() - founded = list(cmd.format_command.keys()) - founded.sort() - self.assertEqual(founded, formats) + # what formats does bdist offer? + formats = ['bztar', 'gztar', 'msi', 'rpm', 'tar', + 'wininst', 'zip', 'ztar'] + found = sorted(cmd.format_command) + self.assertEqual(found, formats) + + def test_skip_build(self): + # bug #10946: bdist --skip-build should trickle down to subcommands + dist = self.create_dist()[1] + cmd = bdist(dist) + cmd.skip_build = 1 + cmd.ensure_finalized() + dist.command_obj['bdist'] = cmd + + names = ['bdist_dumb', 'bdist_wininst'] # bdist_rpm does not support --skip-build + if os.name == 'nt': + names.append('bdist_msi') + + for name in names: + subcmd = cmd.get_finalized_command(name) + self.assertTrue(subcmd.skip_build, + '%s should take --skip-build from bdist' % name) + def test_suite(): return unittest.makeSuite(BuildTestCase) -- cgit v1.2.1 From 48f414cea0e31da0a1267b9248ac38363d006fd4 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 3 Sep 2011 11:17:55 +0200 Subject: Bump to 3.2.2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b31b2d20..9ec61658 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.2rc1" +__version__ = "3.2.2" #--end constants-- -- cgit v1.2.1 From 3b9a1c74f1e174853a335c8662a3f6c7b81e4bd3 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 25 Aug 2011 18:32:02 +0200 Subject: Issue #12333: fix test_distutils failures under Solaris and derivatives --- tests/support.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/support.py b/tests/support.py index 7a76ca05..8452feb1 100644 --- a/tests/support.py +++ b/tests/support.py @@ -54,9 +54,13 @@ class TempdirManager(object): def setUp(self): super().setUp() + self.old_cwd = os.getcwd() self.tempdirs = [] def tearDown(self): + # Restore working dir, for Solaris and derivatives, where rmdir() + # on the current directory fails. + os.chdir(self.old_cwd) super().tearDown() while self.tempdirs: d = self.tempdirs.pop() -- cgit v1.2.1 From 55ab37cb7ef7e8527ba59c5e47a1bc99227df4d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 26 Aug 2011 00:03:22 +0200 Subject: Turn two ifs into one in the code I commited a few days ago --- tests/test_install.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_install.py b/tests/test_install.py index 5c105af9..dfc46b19 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -20,9 +20,8 @@ from distutils.tests import support def _make_ext_name(modname): - if os.name == 'nt': - if sys.executable.endswith('_d.exe'): - modname += '_d' + if os.name == 'nt' and sys.executable.endswith('_d.exe'): + modname += '_d' return modname + sysconfig.get_config_var('SO') -- cgit v1.2.1 From e4a8ae8b1b59394478295eb916023e9dc4369026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 26 Aug 2011 01:56:15 +0200 Subject: Refactor helpers for compiling the xx module in distutils tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I need to copy the xxmodule.c file in other tests, so I moved the support code to distutils.tests.support and improved it: - don’t skip when run from the Lib/distutils/tests directory - use proper skip machinery instead of custom print/return/test suite fiddling. I also took out the fixup_build_ext function, which is needed for tests to pass on Unix shared builds and Windows debug builds. Finally, I cleaned up a few things: - don’t remove directories in tearDown when the parent class’ tearDown has already registered the directories for removal - simplify restoration of sys.path - remove a few unused names found by pyflakes. --- tests/support.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_build_ext.py | 68 ++++++------------------------------------- 2 files changed, 85 insertions(+), 59 deletions(-) diff --git a/tests/support.py b/tests/support.py index 783318e8..788acdaa 100644 --- a/tests/support.py +++ b/tests/support.py @@ -1,7 +1,10 @@ """Support code for distutils test cases.""" import os +import sys import shutil import tempfile +import unittest +import sysconfig from copy import deepcopy import warnings @@ -9,6 +12,7 @@ from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution + def capture_warnings(func): def _capture_warnings(*args, **kw): with warnings.catch_warnings(): @@ -16,6 +20,7 @@ def capture_warnings(func): return func(*args, **kw) return _capture_warnings + class LoggingSilencer(object): def setUp(self): @@ -49,6 +54,7 @@ class LoggingSilencer(object): def clear_logs(self): self.logs = [] + class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. @@ -105,6 +111,7 @@ class TempdirManager(object): return pkg_dir, dist + class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" @@ -115,6 +122,7 @@ class DummyCommand: def ensure_finalized(self): pass + class EnvironGuard(object): def setUp(self): @@ -131,3 +139,71 @@ class EnvironGuard(object): del os.environ[key] super(EnvironGuard, self).tearDown() + + +def copy_xxmodule_c(directory): + """Helper for tests that need the xxmodule.c source file. + + Example use: + + def test_compile(self): + copy_xxmodule_c(self.tmpdir) + self.assertIn('xxmodule.c', os.listdir(self.tmpdir)) + + If the source file can be found, it will be copied to *directory*. If not, + the test will be skipped. Errors during copy are not caught. + """ + filename = _get_xxmodule_path() + if filename is None: + raise unittest.SkipTest('cannot find xxmodule.c (test must run in ' + 'the python build dir)') + shutil.copy(filename, directory) + + +def _get_xxmodule_path(): + srcdir = sysconfig.get_config_var('srcdir') + candidates = [ + # use installed copy if available + os.path.join(os.path.dirname(__file__), 'xxmodule.c'), + # otherwise try using copy from build directory + os.path.join(srcdir, 'Modules', 'xxmodule.c'), + # srcdir mysteriously can be $srcdir/Lib/distutils/tests when + # this file is run from its parent directory, so walk up the + # tree to find the real srcdir + os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'), + ] + for path in candidates: + if os.path.exists(path): + return path + + +def fixup_build_ext(cmd): + """Function needed to make build_ext tests pass. + + When Python was build with --enable-shared on Unix, -L. is not good + enough to find the libpython.so. This is because regrtest runs + it under a tempdir, not in the top level where the .so lives. By the + time we've gotten here, Python's already been chdir'd to the tempdir. + + When Python was built with in debug mode on Windows, build_ext commands + need their debug attribute set, and it is not done automatically for + some reason. + + This function handles both of these things. Example use: + + cmd = build_ext(dist) + support.fixup_build_ext(cmd) + cmd.ensure_finalized() + """ + if os.name == 'nt': + cmd.debug = sys.executable.endswith('_d.exe') + elif sysconfig.get_config_var('Py_ENABLE_SHARED'): + # To further add to the shared builds fun on Unix, we can't just add + # library_dirs to the Extension() instance because that doesn't get + # plumbed through to the final compiler command. + runshared = sysconfig.get_config_var('RUNSHARED') + if runshared is None: + cmd.library_dirs = ['.'] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = value.split(os.pathsep) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index ced1329e..30de0e2b 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -1,7 +1,5 @@ import sys import os -import tempfile -import shutil from StringIO import StringIO import textwrap @@ -19,76 +17,34 @@ from test import test_support # Don't load the xx module more than once. ALREADY_TESTED = False -def _get_source_filename(): - # use installed copy if available - tests_f = os.path.join(os.path.dirname(__file__), 'xxmodule.c') - if os.path.exists(tests_f): - return tests_f - # otherwise try using copy from build directory - srcdir = sysconfig.get_config_var('srcdir') - if srcdir is None: - return os.path.join(sysconfig.project_base, 'Modules', 'xxmodule.c') - return os.path.join(srcdir, 'Modules', 'xxmodule.c') - -_XX_MODULE_PATH = _get_source_filename() class BuildExtTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): def setUp(self): - # Create a simple test environment - # Note that we're making changes to sys.path super(BuildExtTestCase, self).setUp() - self.tmp_dir = tempfile.mkdtemp(prefix="pythontest_") - if os.path.exists(_XX_MODULE_PATH): - self.sys_path = sys.path[:] - sys.path.append(self.tmp_dir) - shutil.copy(_XX_MODULE_PATH, self.tmp_dir) + self.tmp_dir = self.mkdtemp() + self.xx_created = False + sys.path.append(self.tmp_dir) + self.addCleanup(sys.path.remove, self.tmp_dir) def tearDown(self): - # Get everything back to normal - if os.path.exists(_XX_MODULE_PATH): + if self.xx_created: test_support.unload('xx') - sys.path[:] = self.sys_path # XXX on Windows the test leaves a directory # with xx module in TEMP - shutil.rmtree(self.tmp_dir, os.name == 'nt' or - sys.platform == 'cygwin') super(BuildExtTestCase, self).tearDown() - def _fixup_command(self, cmd): - # When Python was build with --enable-shared, -L. is not good enough - # to find the libpython.so. This is because regrtest runs it - # under a tempdir, not in the top level where the .so lives. By the - # time we've gotten here, Python's already been chdir'd to the - # tempdir. - # - # To further add to the fun, we can't just add library_dirs to the - # Extension() instance because that doesn't get plumbed through to the - # final compiler command. - if (sysconfig.get_config_var('Py_ENABLE_SHARED') and - not sys.platform.startswith('win')): - runshared = sysconfig.get_config_var('RUNSHARED') - if runshared is None: - cmd.library_dirs = ['.'] - else: - name, equals, value = runshared.partition('=') - cmd.library_dirs = value.split(os.pathsep) - - @unittest.skipIf(not os.path.exists(_XX_MODULE_PATH), - 'xxmodule.c not found') def test_build_ext(self): global ALREADY_TESTED + support.copy_xxmodule_c(self.tmp_dir) + self.xx_created = True xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir cmd = build_ext(dist) - self._fixup_command(cmd) - if os.name == "nt": - # On Windows, we must build a debug version iff running - # a debug build of Python - cmd.debug = sys.executable.endswith("_d.exe") + support.fixup_build_ext(cmd) cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir @@ -149,7 +105,6 @@ class BuildExtTestCase(support.TempdirManager, cmd = build_ext(dist) cmd.finalize_options() - from distutils import sysconfig py_include = sysconfig.get_python_inc() self.assertTrue(py_include in cmd.include_dirs) @@ -277,13 +232,10 @@ class BuildExtTestCase(support.TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) cmd = build_ext(dist) - self._fixup_command(cmd) + support.fixup_build_ext(cmd) cmd.ensure_finalized() self.assertEqual(len(cmd.get_outputs()), 1) - if os.name == "nt": - cmd.debug = sys.executable.endswith("_d.exe") - cmd.build_lib = os.path.join(self.tmp_dir, 'build') cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') @@ -509,10 +461,8 @@ class BuildExtTestCase(support.TempdirManager, cmd.build_temp = self.tmp_dir try: - old_stdout = sys.stdout cmd.ensure_finalized() cmd.run() - except CompileError: self.fail("Wrong deployment target during compilation") -- cgit v1.2.1 From 997e4e8ba78bcff625c3b4b4ef9e244c2cc7cc6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 26 Aug 2011 02:00:14 +0200 Subject: Add tests for build_ext --user (backport from 3.2) --- tests/test_build_ext.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 30de0e2b..2fa63d30 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -27,6 +27,12 @@ class BuildExtTestCase(support.TempdirManager, self.xx_created = False sys.path.append(self.tmp_dir) self.addCleanup(sys.path.remove, self.tmp_dir) + if sys.version > "2.6": + import site + self.old_user_base = site.USER_BASE + site.USER_BASE = self.mkdtemp() + from distutils.command import build_ext + build_ext.USER_BASE = site.USER_BASE def tearDown(self): if self.xx_created: @@ -97,6 +103,36 @@ class BuildExtTestCase(support.TempdirManager, # make sure we get some library dirs under solaris self.assertTrue(len(cmd.library_dirs) > 0) + def test_user_site(self): + # site.USER_SITE was introduced in 2.6 + if sys.version < '2.6': + return + + import site + dist = Distribution({'name': 'xx'}) + cmd = build_ext(dist) + + # making sure the user option is there + options = [name for name, short, label in + cmd.user_options] + self.assertIn('user', options) + + # setting a value + cmd.user = 1 + + # setting user based lib and include + lib = os.path.join(site.USER_BASE, 'lib') + incl = os.path.join(site.USER_BASE, 'include') + os.mkdir(lib) + os.mkdir(incl) + + cmd.ensure_finalized() + + # see if include_dirs and library_dirs were set + self.assertIn(lib, cmd.library_dirs) + self.assertIn(lib, cmd.rpath) + self.assertIn(incl, cmd.include_dirs) + def test_finalize_options(self): # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. -- cgit v1.2.1 From add9fe8cd89fd81c86867a7596b29d15d2a74294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 26 Aug 2011 02:05:44 +0200 Subject: Try to fix test_distutils on Windows (#12678) --- tests/test_sdist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 8da6fe4d..4c80bc0b 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -422,6 +422,7 @@ class SDistTestCase(PyPIRCCommandTestCase): def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() + cmd.formats = ['gztar'] cmd.ensure_finalized() self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') self.write_file((self.tmp_dir, 'README.manual'), -- cgit v1.2.1 From c166f6b99d408b46e92b3acaca0972085f658b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 26 Aug 2011 02:06:27 +0200 Subject: Backport tests for the distutils install command --- tests/test_install.py | 195 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 2 deletions(-) diff --git a/tests/test_install.py b/tests/test_install.py index 4f976f34..ebfb04f4 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -1,17 +1,33 @@ """Tests for distutils.command.install.""" import os +import sys import unittest +import site -from test.test_support import run_unittest +from test.test_support import captured_stdout, run_unittest +from distutils import sysconfig from distutils.command.install import install +from distutils.command import install as install_module +from distutils.command.build_ext import build_ext +from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution +from distutils.errors import DistutilsOptionError +from distutils.extension import Extension from distutils.tests import support -class InstallTestCase(support.TempdirManager, unittest.TestCase): +def _make_ext_name(modname): + if os.name == 'nt' and sys.executable.endswith('_d.exe'): + modname += '_d' + return modname + sysconfig.get_config_var('SO') + + +class InstallTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): def test_home_installation_scheme(self): # This ensure two things: @@ -49,6 +65,181 @@ class InstallTestCase(support.TempdirManager, unittest.TestCase): check_path(cmd.install_scripts, os.path.join(destination, "bin")) check_path(cmd.install_data, destination) + def test_user_site(self): + # site.USER_SITE was introduced in 2.6 + if sys.version < '2.6': + return + + # preparing the environment for the test + self.old_user_base = site.USER_BASE + self.old_user_site = site.USER_SITE + self.tmpdir = self.mkdtemp() + self.user_base = os.path.join(self.tmpdir, 'B') + self.user_site = os.path.join(self.tmpdir, 'S') + site.USER_BASE = self.user_base + site.USER_SITE = self.user_site + install_module.USER_BASE = self.user_base + install_module.USER_SITE = self.user_site + + def _expanduser(path): + return self.tmpdir + self.old_expand = os.path.expanduser + os.path.expanduser = _expanduser + + try: + # this is the actual test + self._test_user_site() + finally: + site.USER_BASE = self.old_user_base + site.USER_SITE = self.old_user_site + install_module.USER_BASE = self.old_user_base + install_module.USER_SITE = self.old_user_site + os.path.expanduser = self.old_expand + + def _test_user_site(self): + for key in ('nt_user', 'unix_user', 'os2_home'): + self.assertTrue(key in INSTALL_SCHEMES) + + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # making sure the user option is there + options = [name for name, short, lable in + cmd.user_options] + self.assertTrue('user' in options) + + # setting a value + cmd.user = 1 + + # user base and site shouldn't be created yet + self.assertTrue(not os.path.exists(self.user_base)) + self.assertTrue(not os.path.exists(self.user_site)) + + # let's run finalize + cmd.ensure_finalized() + + # now they should + self.assertTrue(os.path.exists(self.user_base)) + self.assertTrue(os.path.exists(self.user_site)) + + self.assertTrue('userbase' in cmd.config_vars) + self.assertTrue('usersite' in cmd.config_vars) + + def test_handle_extra_path(self): + dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) + cmd = install(dist) + + # two elements + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, ['path', 'dirs']) + self.assertEqual(cmd.extra_dirs, 'dirs') + self.assertEqual(cmd.path_file, 'path') + + # one element + cmd.extra_path = ['path'] + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, ['path']) + self.assertEqual(cmd.extra_dirs, 'path') + self.assertEqual(cmd.path_file, 'path') + + # none + dist.extra_path = cmd.extra_path = None + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, None) + self.assertEqual(cmd.extra_dirs, '') + self.assertEqual(cmd.path_file, None) + + # three elements (no way !) + cmd.extra_path = 'path,dirs,again' + self.assertRaises(DistutilsOptionError, cmd.handle_extra_path) + + def test_finalize_options(self): + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # must supply either prefix/exec-prefix/home or + # install-base/install-platbase -- not both + cmd.prefix = 'prefix' + cmd.install_base = 'base' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # must supply either home or prefix/exec-prefix -- not both + cmd.install_base = None + cmd.home = 'home' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # can't combine user with with prefix/exec_prefix/home or + # install_(plat)base + cmd.prefix = None + cmd.user = 'user' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + def test_record(self): + install_dir = self.mkdtemp() + project_dir, dist = self.create_dist(scripts=['hello']) + self.addCleanup(os.chdir, os.getcwd()) + os.chdir(project_dir) + self.write_file('hello', "print('o hai')") + + cmd = install(dist) + dist.command_obj['install'] = cmd + cmd.root = install_dir + cmd.record = os.path.join(project_dir, 'RECORD') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.record) + try: + content = f.read() + finally: + f.close() + + found = [os.path.basename(line) for line in content.splitlines()] + expected = ['hello', + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + + def test_record_extensions(self): + install_dir = self.mkdtemp() + project_dir, dist = self.create_dist(ext_modules=[ + Extension('xx', ['xxmodule.c'])]) + self.addCleanup(os.chdir, os.getcwd()) + os.chdir(project_dir) + support.copy_xxmodule_c(project_dir) + + buildextcmd = build_ext(dist) + support.fixup_build_ext(buildextcmd) + buildextcmd.ensure_finalized() + + cmd = install(dist) + dist.command_obj['install'] = cmd + dist.command_obj['build_ext'] = buildextcmd + cmd.root = install_dir + cmd.record = os.path.join(project_dir, 'RECORD') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.record) + try: + content = f.read() + finally: + f.close() + + found = [os.path.basename(line) for line in content.splitlines()] + expected = [_make_ext_name('xx'), + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + + def test_debug_mode(self): + # this covers the code called when DEBUG is set + old_logs_len = len(self.logs) + install_module.DEBUG = True + try: + with captured_stdout(): + self.test_record() + finally: + install_module.DEBUG = False + self.assertTrue(len(self.logs) > old_logs_len) def test_suite(): return unittest.makeSuite(InstallTestCase) -- cgit v1.2.1 From 20aa4645fe05e538fe7e1083bd87380b156d9fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 26 Aug 2011 16:35:19 +0200 Subject: Add FIXME note as a reminder --- tests/support.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/support.py b/tests/support.py index 788acdaa..648a8e41 100644 --- a/tests/support.py +++ b/tests/support.py @@ -161,6 +161,8 @@ def copy_xxmodule_c(directory): def _get_xxmodule_path(): + # FIXME when run from regrtest, srcdir seems to be '.', which does not help + # us find the xxmodule.c file srcdir = sysconfig.get_config_var('srcdir') candidates = [ # use installed copy if available -- cgit v1.2.1 From 8cd4035750552f84c14fd0d799aa848c643aae77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Mon, 29 Aug 2011 21:48:39 +0200 Subject: Make bdist_* commands respect --skip-build passed to bdist (#10946) --- command/bdist_dumb.py | 5 +++-- command/bdist_msi.py | 6 +++++- command/bdist_wininst.py | 6 +++++- tests/test_bdist.py | 48 +++++++++++++++++++++++++++--------------------- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 170e8894..1ab09d16 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -47,7 +47,7 @@ class bdist_dumb(Command): self.format = None self.keep_temp = 0 self.dist_dir = None - self.skip_build = 0 + self.skip_build = None self.relative = 0 def finalize_options(self): @@ -65,7 +65,8 @@ class bdist_dumb(Command): self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name')) + ('plat_name', 'plat_name'), + ('skip_build', 'skip_build')) def run(self): if not self.skip_build: diff --git a/command/bdist_msi.py b/command/bdist_msi.py index b11957a7..b3cfe9ce 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -130,18 +130,22 @@ class bdist_msi(Command): self.no_target_optimize = 0 self.target_version = None self.dist_dir = None - self.skip_build = 0 + self.skip_build = None self.install_script = None self.pre_install_script = None self.versions = None def finalize_options(self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'msi') + short_version = get_python_version() if (not self.target_version) and self.distribution.has_ext_modules(): self.target_version = short_version + if self.target_version: self.versions = [self.target_version] if not self.skip_build and self.distribution.has_ext_modules()\ diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index b7916e31..e3ed3ad8 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -65,13 +65,15 @@ class bdist_wininst(Command): self.dist_dir = None self.bitmap = None self.title = None - self.skip_build = 0 + self.skip_build = None self.install_script = None self.pre_install_script = None self.user_access_control = None def finalize_options(self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + if self.bdist_dir is None: if self.skip_build and self.plat_name: # If build is skipped and plat_name is overridden, bdist will @@ -81,8 +83,10 @@ class bdist_wininst(Command): # next the command will be initialized using that name bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'wininst') + if not self.target_version: self.target_version = "" + if not self.skip_build and self.distribution.has_ext_modules(): short_version = get_python_version() if self.target_version and self.target_version != short_version: diff --git a/tests/test_bdist.py b/tests/test_bdist.py index 94d40cc2..503a6e85 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -1,41 +1,47 @@ """Tests for distutils.command.bdist.""" -import unittest -import sys import os -import tempfile -import shutil +import unittest from test.support import run_unittest -from distutils.core import Distribution from distutils.command.bdist import bdist from distutils.tests import support -from distutils.spawn import find_executable -from distutils import spawn -from distutils.errors import DistutilsExecError + class BuildTestCase(support.TempdirManager, unittest.TestCase): def test_formats(self): - # let's create a command and make sure - # we can fix the format - pkg_pth, dist = self.create_dist() + # we can set the format + dist = self.create_dist()[1] cmd = bdist(dist) cmd.formats = ['msi'] cmd.ensure_finalized() self.assertEqual(cmd.formats, ['msi']) - # what format bdist offers ? - # XXX an explicit list in bdist is - # not the best way to bdist_* commands - # we should add a registry - formats = ['rpm', 'zip', 'gztar', 'bztar', 'ztar', - 'tar', 'wininst', 'msi'] - formats.sort() - founded = list(cmd.format_command.keys()) - founded.sort() - self.assertEqual(founded, formats) + # what formats does bdist offer? + formats = ['bztar', 'gztar', 'msi', 'rpm', 'tar', + 'wininst', 'zip', 'ztar'] + found = sorted(cmd.format_command) + self.assertEqual(found, formats) + + def test_skip_build(self): + # bug #10946: bdist --skip-build should trickle down to subcommands + dist = self.create_dist()[1] + cmd = bdist(dist) + cmd.skip_build = 1 + cmd.ensure_finalized() + dist.command_obj['bdist'] = cmd + + names = ['bdist_dumb', 'bdist_wininst'] # bdist_rpm does not support --skip-build + if os.name == 'nt': + names.append('bdist_msi') + + for name in names: + subcmd = cmd.get_finalized_command(name) + self.assertTrue(subcmd.skip_build, + '%s should take --skip-build from bdist' % name) + def test_suite(): return unittest.makeSuite(BuildTestCase) -- cgit v1.2.1 From 152743f52b84cd0b37f8f5ec8e10803181c41181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 30 Aug 2011 01:48:59 +0200 Subject: Make bdist_* commands respect --skip-build passed to bdist (#10946) --- command/bdist_dumb.py | 5 +++-- command/bdist_msi.py | 6 +++++- command/bdist_wininst.py | 6 +++++- tests/test_bdist.py | 49 +++++++++++++++++++++++++++--------------------- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 7c60d39b..2f3c6682 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -58,7 +58,7 @@ class bdist_dumb (Command): self.format = None self.keep_temp = 0 self.dist_dir = None - self.skip_build = 0 + self.skip_build = None self.relative = 0 self.owner = None self.group = None @@ -78,7 +78,8 @@ class bdist_dumb (Command): self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name')) + ('plat_name', 'plat_name'), + ('skip_build', 'skip_build')) def run(self): if not self.skip_build: diff --git a/command/bdist_msi.py b/command/bdist_msi.py index ded837d7..703f873b 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -131,18 +131,22 @@ class bdist_msi (Command): self.no_target_optimize = 0 self.target_version = None self.dist_dir = None - self.skip_build = 0 + self.skip_build = None self.install_script = None self.pre_install_script = None self.versions = None def finalize_options (self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'msi') + short_version = get_python_version() if (not self.target_version) and self.distribution.has_ext_modules(): self.target_version = short_version + if self.target_version: self.versions = [self.target_version] if not self.skip_build and self.distribution.has_ext_modules()\ diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 36d46bd6..aa9383af 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -71,7 +71,7 @@ class bdist_wininst (Command): self.dist_dir = None self.bitmap = None self.title = None - self.skip_build = 0 + self.skip_build = None self.install_script = None self.pre_install_script = None self.user_access_control = None @@ -80,6 +80,8 @@ class bdist_wininst (Command): def finalize_options (self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + if self.bdist_dir is None: if self.skip_build and self.plat_name: # If build is skipped and plat_name is overridden, bdist will @@ -89,8 +91,10 @@ class bdist_wininst (Command): # next the command will be initialized using that name bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'wininst') + if not self.target_version: self.target_version = "" + if not self.skip_build and self.distribution.has_ext_modules(): short_version = get_python_version() if self.target_version and self.target_version != short_version: diff --git a/tests/test_bdist.py b/tests/test_bdist.py index fa7cd5ad..121d0992 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -1,42 +1,49 @@ """Tests for distutils.command.bdist.""" -import unittest -import sys import os -import tempfile -import shutil +import unittest from test.test_support import run_unittest -from distutils.core import Distribution from distutils.command.bdist import bdist from distutils.tests import support -from distutils.spawn import find_executable -from distutils import spawn -from distutils.errors import DistutilsExecError + class BuildTestCase(support.TempdirManager, unittest.TestCase): def test_formats(self): - # let's create a command and make sure - # we can fix the format - pkg_pth, dist = self.create_dist() + # we can set the format + dist = self.create_dist()[1] cmd = bdist(dist) cmd.formats = ['msi'] cmd.ensure_finalized() self.assertEqual(cmd.formats, ['msi']) - # what format bdist offers ? - # XXX an explicit list in bdist is - # not the best way to bdist_* commands - # we should add a registry - formats = ['rpm', 'zip', 'gztar', 'bztar', 'ztar', - 'tar', 'wininst', 'msi'] - formats.sort() - founded = cmd.format_command.keys() - founded.sort() - self.assertEqual(founded, formats) + # what formats does bdist offer? + formats = ['bztar', 'gztar', 'msi', 'rpm', 'tar', + 'wininst', 'zip', 'ztar'] + found = sorted(cmd.format_command) + self.assertEqual(found, formats) + + def test_skip_build(self): + # bug #10946: bdist --skip-build should trickle down to subcommands + dist = self.create_dist()[1] + cmd = bdist(dist) + cmd.skip_build = 1 + cmd.ensure_finalized() + dist.command_obj['bdist'] = cmd + + names = ['bdist_dumb', 'bdist_wininst'] + # bdist_rpm does not support --skip-build + if os.name == 'nt': + names.append('bdist_msi') + + for name in names: + subcmd = cmd.get_finalized_command(name) + self.assertTrue(subcmd.skip_build, + '%s should take --skip-build from bdist' % name) + def test_suite(): return unittest.makeSuite(BuildTestCase) -- cgit v1.2.1 From 3c115835bc766964c0fb33fb0636538030c03135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Thu, 1 Sep 2011 23:37:56 +0200 Subject: Fix typo (was build) and remove redundancy in docstring --- tests/support.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/support.py b/tests/support.py index 44fcd6b7..d77bbee3 100644 --- a/tests/support.py +++ b/tests/support.py @@ -175,10 +175,9 @@ def _get_xxmodule_path(): def fixup_build_ext(cmd): """Function needed to make build_ext tests pass. - When Python was build with --enable-shared on Unix, -L. is not good - enough to find the libpython.so. This is because regrtest runs - it under a tempdir, not in the top level where the .so lives. By the - time we've gotten here, Python's already been chdir'd to the tempdir. + When Python was built with --enable-shared on Unix, -L. is not enough to + find libpython.so, because regrtest runs in a tempdir, not in the + source directory where the .so lives. When Python was built with in debug mode on Windows, build_ext commands need their debug attribute set, and it is not done automatically for -- cgit v1.2.1 From f2643f2baea9c954e666420396b85ee94658dc86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 3 Sep 2011 00:28:43 +0200 Subject: Enable catching WARN-level logging messages in distutils' test_sdist --- tests/test_sdist.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index f34f786c..f9e28c84 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -78,9 +78,6 @@ class SDistTestCase(PyPIRCCommandTestCase): dist.include_package_data = True cmd = sdist(dist) cmd.dist_dir = 'dist' - def _warn(*args): - pass - cmd.warn = _warn return dist, cmd @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') @@ -235,7 +232,8 @@ class SDistTestCase(PyPIRCCommandTestCase): # with the `check` subcommand cmd.ensure_finalized() cmd.run() - warnings = self.get_logs(WARN) + warnings = [msg for msg in self.get_logs(WARN) if + msg.startswith('warning: check:')] self.assertEqual(len(warnings), 2) # trying with a complete set of metadata @@ -244,7 +242,8 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.ensure_finalized() cmd.metadata_check = 0 cmd.run() - warnings = self.get_logs(WARN) + warnings = [msg for msg in self.get_logs(WARN) if + msg.startswith('warning: check:')] self.assertEqual(len(warnings), 0) def test_check_metadata_deprecated(self): -- cgit v1.2.1 From 2b01850d8817f36295cde7daf2ac670582514e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 3 Sep 2011 00:28:43 +0200 Subject: Enable catching WARN-level logging messages in distutils' test_sdist --- tests/test_sdist.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 2f09c9be..5134e6ab 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -85,9 +85,6 @@ class SDistTestCase(PyPIRCCommandTestCase): dist.include_package_data = True cmd = sdist(dist) cmd.dist_dir = 'dist' - def _warn(*args): - pass - cmd.warn = _warn return dist, cmd @unittest.skipUnless(zlib, "requires zlib") @@ -242,7 +239,8 @@ class SDistTestCase(PyPIRCCommandTestCase): # with the `check` subcommand cmd.ensure_finalized() cmd.run() - warnings = self.get_logs(WARN) + warnings = [msg for msg in self.get_logs(WARN) if + msg.startswith('warning: check:')] self.assertEqual(len(warnings), 2) # trying with a complete set of metadata @@ -251,7 +249,8 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.ensure_finalized() cmd.metadata_check = 0 cmd.run() - warnings = self.get_logs(WARN) + warnings = [msg for msg in self.get_logs(WARN) if + msg.startswith('warning: check:')] self.assertEqual(len(warnings), 0) def test_check_metadata_deprecated(self): -- cgit v1.2.1 From e5113fc0545f3bb694c82341216ced067dac3dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 3 Sep 2011 00:42:04 +0200 Subject: Warn instead of crashing because of invalid path in MANIFEST.in (#8286). sdist used to crash with a full traceback dump instead of printing a nice warning with the faulty line number. --- command/sdist.py | 5 ++++- tests/test_sdist.py | 28 +++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 21ea61d9..a9429a42 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -306,7 +306,10 @@ class sdist(Command): try: self.filelist.process_template_line(line) - except DistutilsTemplateError as msg: + # the call above can raise a DistutilsTemplateError for + # malformed lines, or a ValueError from the lower-level + # convert_path function + except (DistutilsTemplateError, ValueError) as msg: self.warn("%s, line %d: %s" % (template.filename, template.current_line, msg)) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index f9e28c84..529b4ef5 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -15,6 +15,7 @@ from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsOptionError from distutils.spawn import find_executable from distutils.log import WARN +from distutils.filelist import FileList from distutils.archive_util import ARCHIVE_FORMATS SETUP_PY = """ @@ -265,7 +266,6 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(len(output), num_formats) def test_finalize_options(self): - dist, cmd = self.get_cmd() cmd.finalize_options() @@ -285,6 +285,32 @@ class SDistTestCase(PyPIRCCommandTestCase): cmd.formats = 'supazipa' self.assertRaises(DistutilsOptionError, cmd.finalize_options) + # the following tests make sure there is a nice error message instead + # of a traceback when parsing an invalid manifest template + + def _test_template(self, content): + dist, cmd = self.get_cmd() + os.chdir(self.tmp_dir) + self.write_file('MANIFEST.in', content) + cmd.ensure_finalized() + cmd.filelist = FileList() + cmd.read_template() + warnings = self.get_logs(WARN) + self.assertEqual(len(warnings), 1) + + def test_invalid_template_unknown_command(self): + self._test_template('taunt knights *') + + def test_invalid_template_wrong_arguments(self): + # this manifest command takes one argument + self._test_template('prune') + + @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only') + def test_invalid_template_wrong_path(self): + # on Windows, trailing slashes are not allowed + # this used to crash instead of raising a warning: #8286 + self._test_template('include examples/') + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_get_file_list(self): # make sure MANIFEST is recalculated -- cgit v1.2.1 From e5ed1dfa7ad8b538fdb67ef59174ab17f0ee677d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 3 Sep 2011 00:47:07 +0200 Subject: Warn instead of crashing because of invalid path in MANIFEST.in (#8286). sdist used to crash with a full traceback dump instead of printing a nice warning with the faulty line number. --- command/sdist.py | 5 ++++- tests/test_sdist.py | 28 +++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 75950f3d..d30de106 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -320,7 +320,10 @@ class sdist(Command): try: self.filelist.process_template_line(line) - except DistutilsTemplateError, msg: + # the call above can raise a DistutilsTemplateError for + # malformed lines, or a ValueError from the lower-level + # convert_path function + except (DistutilsTemplateError, ValueError) as msg: self.warn("%s, line %d: %s" % (template.filename, template.current_line, msg)) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 5134e6ab..33f6ee69 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,6 +29,7 @@ from distutils.tests.test_config import PyPIRCCommandTestCase from distutils.errors import DistutilsOptionError from distutils.spawn import find_executable from distutils.log import WARN +from distutils.filelist import FileList from distutils.archive_util import ARCHIVE_FORMATS SETUP_PY = """ @@ -272,7 +273,6 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(len(output), num_formats) def test_finalize_options(self): - dist, cmd = self.get_cmd() cmd.finalize_options() @@ -342,6 +342,32 @@ class SDistTestCase(PyPIRCCommandTestCase): finally: archive.close() + # the following tests make sure there is a nice error message instead + # of a traceback when parsing an invalid manifest template + + def _test_template(self, content): + dist, cmd = self.get_cmd() + os.chdir(self.tmp_dir) + self.write_file('MANIFEST.in', content) + cmd.ensure_finalized() + cmd.filelist = FileList() + cmd.read_template() + warnings = self.get_logs(WARN) + self.assertEqual(len(warnings), 1) + + def test_invalid_template_unknown_command(self): + self._test_template('taunt knights *') + + def test_invalid_template_wrong_arguments(self): + # this manifest command takes one argument + self._test_template('prune') + + @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only') + def test_invalid_template_wrong_path(self): + # on Windows, trailing slashes are not allowed + # this used to crash instead of raising a warning: #8286 + self._test_template('include examples/') + @unittest.skipUnless(zlib, "requires zlib") def test_get_file_list(self): # make sure MANIFEST is recalculated -- cgit v1.2.1 From e95a6f1db95dfd2ebb63de9af3211718bb47346a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 5 Sep 2011 23:44:56 +0200 Subject: Issue #9561: distutils now reads and writes egg-info files using UTF-8 instead of the locale encoding. --- command/install_egg_info.py | 5 ++--- dist.py | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/command/install_egg_info.py b/command/install_egg_info.py index c8880310..c2a7d649 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -40,9 +40,8 @@ class install_egg_info(Command): "Creating "+self.install_dir) log.info("Writing %s", target) if not self.dry_run: - f = open(target, 'w') - self.distribution.metadata.write_pkg_file(f) - f.close() + with open(target, 'w', encoding='UTF-8') as f: + self.distribution.metadata.write_pkg_file(f) def get_outputs(self): return self.outputs diff --git a/dist.py b/dist.py index 02cd79ba..8ca5b6f4 100644 --- a/dist.py +++ b/dist.py @@ -1010,11 +1010,9 @@ class DistributionMetadata: def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. """ - pkg_info = open(os.path.join(base_dir, 'PKG-INFO'), 'w') - try: + with open(os.path.join(base_dir, 'PKG-INFO'), 'w', + encoding='UTF-8') as pkg_info: self.write_pkg_file(pkg_info) - finally: - pkg_info.close() def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. -- cgit v1.2.1 From beedb510e63fa7cf09ef572c178a0e6a161335fa Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Fri, 9 Sep 2011 18:50:59 +0200 Subject: Issue #12333: fix test_distutils failures under Solaris and derivatives. Patch by Antoine Pitrou --- tests/support.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/support.py b/tests/support.py index 648a8e41..a2190c02 100644 --- a/tests/support.py +++ b/tests/support.py @@ -63,9 +63,13 @@ class TempdirManager(object): def setUp(self): super(TempdirManager, self).setUp() + self.old_cwd = os.getcwd() self.tempdirs = [] def tearDown(self): + # Restore working dir, for Solaris and derivatives, where rmdir() + # on the current directory fails. + os.chdir(self.old_cwd) super(TempdirManager, self).tearDown() while self.tempdirs: d = self.tempdirs.pop() -- cgit v1.2.1 From 274ec9cd20e5bdf61fbb0b5c468b547607cf50f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 01:34:44 +0200 Subject: Slight cleanup in distutils test_dist. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have tests to add in this file and it’s always nice to start from a clean base. --- tests/test_dist.py | 98 ++++++++++++++++++++++++++---------------------------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index a20d6c8f..7050cbed 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -74,7 +74,7 @@ class DistributionTestCase(support.LoggingSilencer, self.assertEqual(d.get_command_packages(), ["distutils.command", "foo.bar", "distutils.tests"]) cmd = d.get_command_obj("test_dist") - self.assertTrue(isinstance(cmd, test_dist)) + self.assertIsInstance(cmd, test_dist) self.assertEqual(cmd.sample_option, "sometext") def test_command_packages_configfile(self): @@ -106,28 +106,23 @@ class DistributionTestCase(support.LoggingSilencer, def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes - klass = Distribution # catching warnings warns = [] + def _warn(msg): warns.append(msg) - old_warn = warnings.warn + self.addCleanup(setattr, warnings, 'warn', warnings.warn) warnings.warn = _warn - try: - dist = klass(attrs={'author': 'xxx', - 'name': 'xxx', - 'version': 'xxx', - 'url': 'xxxx', - 'options': {}}) - finally: - warnings.warn = old_warn + dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx', + 'version': 'xxx', 'url': 'xxxx', + 'options': {}}) self.assertEqual(len(warns), 0) + self.assertNotIn('options', dir(dist)) def test_finalize_options(self): - attrs = {'keywords': 'one,two', 'platforms': 'one,two'} @@ -150,7 +145,6 @@ class DistributionTestCase(support.LoggingSilencer, cmds = dist.get_command_packages() self.assertEqual(cmds, ['distutils.command', 'one', 'two']) - def test_announce(self): # make sure the level is known dist = Distribution() @@ -158,6 +152,7 @@ class DistributionTestCase(support.LoggingSilencer, kwargs = {'level': 'ok2'} self.assertRaises(ValueError, dist.announce, args, kwargs) + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): @@ -170,15 +165,20 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, sys.argv[:] = self.argv[1] super(MetadataTestCase, self).tearDown() + def format_metadata(self, dist): + sio = io.StringIO() + dist.metadata.write_pkg_file(sio) + return sio.getvalue() + def test_simple_metadata(self): attrs = {"name": "package", "version": "1.0"} dist = Distribution(attrs) meta = self.format_metadata(dist) - self.assertTrue("Metadata-Version: 1.0" in meta) - self.assertTrue("provides:" not in meta.lower()) - self.assertTrue("requires:" not in meta.lower()) - self.assertTrue("obsoletes:" not in meta.lower()) + self.assertIn("Metadata-Version: 1.0", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertNotIn("requires:", meta.lower()) + self.assertNotIn("obsoletes:", meta.lower()) def test_provides(self): attrs = {"name": "package", @@ -190,9 +190,9 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_provides(), ["package", "package.sub"]) meta = self.format_metadata(dist) - self.assertTrue("Metadata-Version: 1.1" in meta) - self.assertTrue("requires:" not in meta.lower()) - self.assertTrue("obsoletes:" not in meta.lower()) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("requires:", meta.lower()) + self.assertNotIn("obsoletes:", meta.lower()) def test_provides_illegal(self): self.assertRaises(ValueError, Distribution, @@ -210,11 +210,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_requires(), ["other", "another (==1.0)"]) meta = self.format_metadata(dist) - self.assertTrue("Metadata-Version: 1.1" in meta) - self.assertTrue("provides:" not in meta.lower()) - self.assertTrue("Requires: other" in meta) - self.assertTrue("Requires: another (==1.0)" in meta) - self.assertTrue("obsoletes:" not in meta.lower()) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertIn("Requires: other", meta) + self.assertIn("Requires: another (==1.0)", meta) + self.assertNotIn("obsoletes:", meta.lower()) def test_requires_illegal(self): self.assertRaises(ValueError, Distribution, @@ -232,11 +232,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_obsoletes(), ["other", "another (<1.0)"]) meta = self.format_metadata(dist) - self.assertTrue("Metadata-Version: 1.1" in meta) - self.assertTrue("provides:" not in meta.lower()) - self.assertTrue("requires:" not in meta.lower()) - self.assertTrue("Obsoletes: other" in meta) - self.assertTrue("Obsoletes: another (<1.0)" in meta) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertNotIn("requires:", meta.lower()) + self.assertIn("Obsoletes: other", meta) + self.assertIn("Obsoletes: another (<1.0)", meta) def test_obsoletes_illegal(self): self.assertRaises(ValueError, Distribution, @@ -244,10 +244,20 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, "version": "1.0", "obsoletes": ["my.pkg (splat)"]}) - def format_metadata(self, dist): - sio = io.StringIO() - dist.metadata.write_pkg_file(sio) - return sio.getvalue() + def test_long_description(self): + long_desc = textwrap.dedent("""\ + example:: + We start here + and continue here + and end here.""") + attrs = {"name": "package", + "version": "1.0", + "long_description": long_desc} + + dist = Distribution(attrs) + meta = self.format_metadata(dist) + meta = meta.replace('\n' + 8 * ' ', '\n') + self.assertIn(long_desc, meta) def test_custom_pydistutils(self): # fixes #2166 @@ -272,14 +282,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, if sys.platform in ('linux', 'darwin'): os.environ['HOME'] = temp_dir files = dist.find_config_files() - self.assertTrue(user_filename in files) + self.assertIn(user_filename, files) # win32-style if sys.platform == 'win32': # home drive should be found os.environ['HOME'] = temp_dir files = dist.find_config_files() - self.assertTrue(user_filename in files, + self.assertIn(user_filename, files, '%r not found in %r' % (user_filename, files)) finally: os.remove(user_filename) @@ -301,22 +311,8 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, output = [line for line in s.getvalue().split('\n') if line.strip() != ''] - self.assertTrue(len(output) > 0) - - def test_long_description(self): - long_desc = textwrap.dedent("""\ - example:: - We start here - and continue here - and end here.""") - attrs = {"name": "package", - "version": "1.0", - "long_description": long_desc} + self.assertTrue(output) - dist = Distribution(attrs) - meta = self.format_metadata(dist) - meta = meta.replace('\n' + 8 * ' ', '\n') - self.assertTrue(long_desc in meta) def test_suite(): suite = unittest.TestSuite() -- cgit v1.2.1 From 9da8782eff11b5aa19156596f5cfec1d776c776f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 01:51:40 +0200 Subject: =?UTF-8?q?Fix=20determination=20of=20Metadata=20version=20(#8933)?= =?UTF-8?q?.=20=20Patch=20by=20Filip=20Gruszczy=C5=84ski.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist.py | 3 ++- tests/test_dist.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 8ca5b6f4..69825f20 100644 --- a/dist.py +++ b/dist.py @@ -1018,7 +1018,8 @@ class DistributionMetadata: """Write the PKG-INFO format data to a file object. """ version = '1.0' - if self.provides or self.requires or self.obsoletes: + if (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): version = '1.1' file.write('Metadata-Version: %s\n' % version) diff --git a/tests/test_dist.py b/tests/test_dist.py index 7050cbed..8aaae88c 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -244,6 +244,20 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, "version": "1.0", "obsoletes": ["my.pkg (splat)"]}) + def test_classifier(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'classifiers': ['Programming Language :: Python :: 3']} + dist = Distribution(attrs) + meta = self.format_metadata(dist) + self.assertIn('Metadata-Version: 1.1', meta) + + def test_download_url(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'download_url': 'http://example.org/boa'} + dist = Distribution(attrs) + meta = self.format_metadata(dist) + self.assertIn('Metadata-Version: 1.1', meta) + def test_long_description(self): long_desc = textwrap.dedent("""\ example:: -- cgit v1.2.1 From 4af2ac319602a3e46acfb4c97cc34d374433dd53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 05:37:33 +0200 Subject: Slight cleanup in distutils test_dist. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have tests to add in this file and it’s always nice to start from a clean base. I’ve also changed a test that used to write an invalid config file ('[global]command_packages = etc.' on one line), but the test passes before and after this change, so either it magically works or the test is poorly written. Sigh. --- tests/test_dist.py | 103 +++++++++++++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 54 deletions(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index ba606381..ff9fa8fc 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,12 +8,13 @@ import unittest import warnings import textwrap -from distutils.dist import Distribution, fix_help_options, DistributionMetadata +from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command import distutils.dist from test.test_support import TESTFN, captured_stdout, run_unittest from distutils.tests import support + class test_dist(Command): """Sample distutils extension command.""" @@ -61,7 +62,7 @@ class DistributionTestCase(support.TempdirManager, def test_debug_mode(self): with open(TESTFN, "w") as f: - f.write("[global]") + f.write("[global]\n") f.write("command_packages = foo.bar, splat") files = [TESTFN] @@ -97,7 +98,7 @@ class DistributionTestCase(support.TempdirManager, self.assertEqual(d.get_command_packages(), ["distutils.command", "foo.bar", "distutils.tests"]) cmd = d.get_command_obj("test_dist") - self.assertTrue(isinstance(cmd, test_dist)) + self.assertIsInstance(cmd, test_dist) self.assertEqual(cmd.sample_option, "sometext") def test_command_packages_configfile(self): @@ -105,8 +106,8 @@ class DistributionTestCase(support.TempdirManager, self.addCleanup(os.unlink, TESTFN) f = open(TESTFN, "w") try: - print >>f, "[global]" - print >>f, "command_packages = foo.bar, splat" + print >> f, "[global]" + print >> f, "command_packages = foo.bar, splat" finally: f.close() @@ -138,7 +139,6 @@ class DistributionTestCase(support.TempdirManager, 'description': u'Café torréfié', 'long_description': u'Héhéhé'}) - # let's make sure the file can be written # with Unicode fields. they are encoded with # PKG_INFO_ENCODING @@ -152,33 +152,28 @@ class DistributionTestCase(support.TempdirManager, 'long_description': 'Hehehe'}) my_file2 = os.path.join(tmp_dir, 'f2') - dist.metadata.write_pkg_file(open(my_file, 'w')) + dist.metadata.write_pkg_file(open(my_file2, 'w')) def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes - klass = Distribution # catching warnings warns = [] + def _warn(msg): warns.append(msg) - old_warn = warnings.warn + self.addCleanup(setattr, warnings, 'warn', warnings.warn) warnings.warn = _warn - try: - dist = klass(attrs={'author': 'xxx', - 'name': 'xxx', - 'version': 'xxx', - 'url': 'xxxx', - 'options': {}}) - finally: - warnings.warn = old_warn + dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx', + 'version': 'xxx', 'url': 'xxxx', + 'options': {}}) self.assertEqual(len(warns), 0) + self.assertNotIn('options', dir(dist)) def test_finalize_options(self): - attrs = {'keywords': 'one,two', 'platforms': 'one,two'} @@ -201,7 +196,6 @@ class DistributionTestCase(support.TempdirManager, cmds = dist.get_command_packages() self.assertEqual(cmds, ['distutils.command', 'one', 'two']) - def test_announce(self): # make sure the level is known dist = Distribution() @@ -251,15 +245,30 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, sys.argv[:] = self.argv[1] super(MetadataTestCase, self).tearDown() + def test_long_description(self): + long_desc = textwrap.dedent("""\ + example:: + We start here + and continue here + and end here.""") + attrs = {"name": "package", + "version": "1.0", + "long_description": long_desc} + + dist = Distribution(attrs) + meta = self.format_metadata(dist) + meta = meta.replace('\n' + 8 * ' ', '\n') + self.assertIn(long_desc, meta) + def test_simple_metadata(self): attrs = {"name": "package", "version": "1.0"} dist = Distribution(attrs) meta = self.format_metadata(dist) - self.assertTrue("Metadata-Version: 1.0" in meta) - self.assertTrue("provides:" not in meta.lower()) - self.assertTrue("requires:" not in meta.lower()) - self.assertTrue("obsoletes:" not in meta.lower()) + self.assertIn("Metadata-Version: 1.0", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertNotIn("requires:", meta.lower()) + self.assertNotIn("obsoletes:", meta.lower()) def test_provides(self): attrs = {"name": "package", @@ -271,9 +280,9 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_provides(), ["package", "package.sub"]) meta = self.format_metadata(dist) - self.assertTrue("Metadata-Version: 1.1" in meta) - self.assertTrue("requires:" not in meta.lower()) - self.assertTrue("obsoletes:" not in meta.lower()) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("requires:", meta.lower()) + self.assertNotIn("obsoletes:", meta.lower()) def test_provides_illegal(self): self.assertRaises(ValueError, Distribution, @@ -291,11 +300,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_requires(), ["other", "another (==1.0)"]) meta = self.format_metadata(dist) - self.assertTrue("Metadata-Version: 1.1" in meta) - self.assertTrue("provides:" not in meta.lower()) - self.assertTrue("Requires: other" in meta) - self.assertTrue("Requires: another (==1.0)" in meta) - self.assertTrue("obsoletes:" not in meta.lower()) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertIn("Requires: other", meta) + self.assertIn("Requires: another (==1.0)", meta) + self.assertNotIn("obsoletes:", meta.lower()) def test_requires_illegal(self): self.assertRaises(ValueError, Distribution, @@ -313,11 +322,11 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(dist.get_obsoletes(), ["other", "another (<1.0)"]) meta = self.format_metadata(dist) - self.assertTrue("Metadata-Version: 1.1" in meta) - self.assertTrue("provides:" not in meta.lower()) - self.assertTrue("requires:" not in meta.lower()) - self.assertTrue("Obsoletes: other" in meta) - self.assertTrue("Obsoletes: another (<1.0)" in meta) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertNotIn("requires:", meta.lower()) + self.assertIn("Obsoletes: other", meta) + self.assertIn("Obsoletes: another (<1.0)", meta) def test_obsoletes_illegal(self): self.assertRaises(ValueError, Distribution, @@ -353,14 +362,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, if sys.platform in ('linux', 'darwin'): os.environ['HOME'] = temp_dir files = dist.find_config_files() - self.assertTrue(user_filename in files) + self.assertIn(user_filename, files) # win32-style if sys.platform == 'win32': # home drive should be found os.environ['HOME'] = temp_dir files = dist.find_config_files() - self.assertTrue(user_filename in files, + self.assertIn(user_filename, files, '%r not found in %r' % (user_filename, files)) finally: os.remove(user_filename) @@ -382,22 +391,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, output = [line for line in s.getvalue().split('\n') if line.strip() != ''] - self.assertTrue(len(output) > 0) - - def test_long_description(self): - long_desc = textwrap.dedent("""\ - example:: - We start here - and continue here - and end here.""") - attrs = {"name": "package", - "version": "1.0", - "long_description": long_desc} - - dist = distutils.dist.Distribution(attrs) - meta = self.format_metadata(dist) - meta = meta.replace('\n' + 8 * ' ', '\n') - self.assertTrue(long_desc in meta) + self.assertTrue(output) def test_read_metadata(self): attrs = {"name": "package", @@ -426,6 +420,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(metadata.obsoletes, None) self.assertEqual(metadata.requires, ['foo']) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) -- cgit v1.2.1 From d6c0aeb23999b2de522fcd76ad067de8acf34d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 10 Sep 2011 05:39:45 +0200 Subject: =?UTF-8?q?Fix=20determination=20of=20Metadata=20version=20(#8933)?= =?UTF-8?q?.=20=20Patch=20by=20Filip=20Gruszczy=C5=84ski.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist.py | 3 ++- tests/test_dist.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 597909ea..e025313d 100644 --- a/dist.py +++ b/dist.py @@ -1111,7 +1111,8 @@ class DistributionMetadata: """Write the PKG-INFO format data to a file object. """ version = '1.0' - if self.provides or self.requires or self.obsoletes: + if (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): version = '1.1' self._write_field(file, 'Metadata-Version', version) diff --git a/tests/test_dist.py b/tests/test_dist.py index ff9fa8fc..4b7bbeb3 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -245,6 +245,20 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, sys.argv[:] = self.argv[1] super(MetadataTestCase, self).tearDown() + def test_classifier(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'classifiers': ['Programming Language :: Python :: 3']} + dist = Distribution(attrs) + meta = self.format_metadata(dist) + self.assertIn('Metadata-Version: 1.1', meta) + + def test_download_url(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'download_url': 'http://example.org/boa'} + dist = Distribution(attrs) + meta = self.format_metadata(dist) + self.assertIn('Metadata-Version: 1.1', meta) + def test_long_description(self): long_desc = textwrap.dedent("""\ example:: -- cgit v1.2.1 From c29e03a0344d5914bc500782ffc243dee4e3aac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 7 Oct 2011 23:13:45 +0200 Subject: Make C code in one distutils test comply with ISO C (#10359). Patch by Hallvard B Furuseth. --- tests/test_config_cmd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 4f7ebdd9..e2e6e4eb 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -44,10 +44,10 @@ class ConfigTestCase(support.LoggingSilencer, cmd = config(dist) # simple pattern searches - match = cmd.search_cpp(pattern='xxx', body='// xxx') + match = cmd.search_cpp(pattern='xxx', body='/* xxx */') self.assertEqual(match, 0) - match = cmd.search_cpp(pattern='_configtest', body='// xxx') + match = cmd.search_cpp(pattern='_configtest', body='/* xxx */') self.assertEqual(match, 1) def test_finalize_options(self): -- cgit v1.2.1 From 37dc7d3a99a8690db8fe7db84f27a2a2a2fff76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 8 Oct 2011 00:34:13 +0200 Subject: Fix distutils byte-compilation to comply with PEP 3147 (#11254). Patch by Jeff Ramnani. Tested with -B, -O and -OO. --- tests/test_build_py.py | 9 ++++++--- tests/test_install_lib.py | 11 +++++++---- util.py | 11 +++++++++-- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 4e46339b..80316ad7 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -3,6 +3,7 @@ import os import sys import io +import imp import unittest from distutils.command.build_py import build_py @@ -57,13 +58,15 @@ class BuildPyTestCase(support.TempdirManager, self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) + pycache_dir = os.path.join(pkgdest, "__pycache__") self.assertIn("__init__.py", files) self.assertIn("README.txt", files) - # XXX even with -O, distutils writes pyc, not pyo; bug? if sys.dont_write_bytecode: - self.assertNotIn("__init__.pyc", files) + self.assertFalse(os.path.exists(pycache_dir)) else: - self.assertIn("__init__.pyc", files) + # XXX even with -O, distutils writes pyc, not pyo; bug? + pyc_files = os.listdir(pycache_dir) + self.assertIn("__init__.%s.pyc" % imp.get_tag(), pyc_files) def test_empty_package_dir(self): # See SF 1668596/1720897. diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index fddaabe1..b42b03b2 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -1,6 +1,7 @@ """Tests for distutils.command.install_data.""" import sys import os +import imp import unittest from distutils.command.install_lib import install_lib @@ -32,18 +33,20 @@ class InstallLibTestCase(support.TempdirManager, cmd.finalize_options() self.assertEqual(cmd.optimize, 2) - @unittest.skipUnless(not sys.dont_write_bytecode, - 'byte-compile not supported') + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') def test_byte_compile(self): pkg_dir, dist = self.create_dist() + os.chdir(pkg_dir) cmd = install_lib(dist) cmd.compile = cmd.optimize = 1 f = os.path.join(pkg_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc'))) - self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo'))) + pyc_file = imp.cache_from_source('foo.py') + pyo_file = imp.cache_from_source('foo.py', debug_override=False) + self.assertTrue(os.path.exists(pyc_file)) + self.assertTrue(os.path.exists(pyo_file)) def test_get_outputs(self): pkg_dir, dist = self.create_dist() diff --git a/util.py b/util.py index 023ddffc..631f5dfb 100644 --- a/util.py +++ b/util.py @@ -4,7 +4,11 @@ Miscellaneous utility functions -- anything that doesn't fit into one of the other *util.py modules. """ -import sys, os, string, re +import os +import re +import imp +import sys +import string from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer from distutils.spawn import spawn @@ -529,7 +533,10 @@ byte_compile(files, optimize=%r, force=%r, # Terminology from the py_compile module: # cfile - byte-compiled file # dfile - purported source filename (same as 'file' by default) - cfile = file + (__debug__ and "c" or "o") + if optimize >= 0: + cfile = imp.cache_from_source(file, debug_override=not optimize) + else: + cfile = imp.cache_from_source(file) dfile = file if prefix: if file[:len(prefix)] != prefix: -- cgit v1.2.1 From b51f2da1992ea985b9df544c8240936a89c162bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 8 Oct 2011 01:56:52 +0200 Subject: Fix distutils.sysconfig.get_makefile_filename when prefix != exec-prefix --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 5ea724c0..ac06313b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -218,7 +218,7 @@ def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: return os.path.join(os.path.dirname(sys.executable), "Makefile") - lib_dir = get_python_lib(plat_specific=1, standard_lib=1) + lib_dir = get_python_lib(plat_specific=0, standard_lib=1) config_file = 'config-{}{}'.format(get_python_version(), build_flags) return os.path.join(lib_dir, config_file, 'Makefile') -- cgit v1.2.1 From d90cd637fcbde15bfe066c0758f4ce2f456e4e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 8 Oct 2011 02:15:55 +0200 Subject: Make C code in one distutils test comply with ISO C (#10359). Patch by Hallvard B Furuseth. --- tests/test_config_cmd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index a36a1d5b..2cf3886c 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -44,10 +44,10 @@ class ConfigTestCase(support.LoggingSilencer, cmd = config(dist) # simple pattern searches - match = cmd.search_cpp(pattern='xxx', body='// xxx') + match = cmd.search_cpp(pattern='xxx', body='/* xxx */') self.assertEqual(match, 0) - match = cmd.search_cpp(pattern='_configtest', body='// xxx') + match = cmd.search_cpp(pattern='_configtest', body='/* xxx */') self.assertEqual(match, 1) def test_finalize_options(self): -- cgit v1.2.1 From 8cc629dc1088bc2883ebad92b778c22c0134b6be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 8 Oct 2011 03:02:37 +0200 Subject: Fix docstring of distutils.util.byte_compile (followup for #11254) --- util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util.py b/util.py index 631f5dfb..f42c6a18 100644 --- a/util.py +++ b/util.py @@ -419,9 +419,9 @@ def byte_compile (py_files, verbose=1, dry_run=0, direct=None): """Byte-compile a collection of Python source files to either .pyc - or .pyo files in the same directory. 'py_files' is a list of files - to compile; any files that don't end in ".py" are silently skipped. - 'optimize' must be one of the following: + or .pyo files in a __pycache__ subdirectory. 'py_files' is a list + of files to compile; any files that don't end in ".py" are silently + skipped. 'optimize' must be one of the following: 0 - don't optimize (generate .pyc) 1 - normal optimization (like "python -O") 2 - extra optimization (like "python -OO") -- cgit v1.2.1 From 7558986e63b1d4b62bacb37b89c13a7221ec43f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 9 Oct 2011 07:11:19 +0200 Subject: =?UTF-8?q?Fix=20distutils=E2=80=99=20check=20and=20register=20Uni?= =?UTF-8?q?code=20handling=20(#13114).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The check command was fixed by Kirill Kuzminykh. The register command was using StringIO.getvalue, which uses “''.join†and thus coerces to str using the default encoding (ASCII), so I changed the code to use one extra intermediary list and correctly encode to UTF-8. --- command/check.py | 3 +++ command/register.py | 28 ++++++++++++++++++---------- tests/test_check.py | 14 ++++++++++++-- tests/test_register.py | 20 +++++++++++++++++++- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/command/check.py b/command/check.py index bc29baab..4b64e458 100644 --- a/command/check.py +++ b/command/check.py @@ -5,6 +5,7 @@ Implements the Distutils 'check' command. __revision__ = "$Id$" from distutils.core import Command +from distutils.dist import PKG_INFO_ENCODING from distutils.errors import DistutilsSetupError try: @@ -108,6 +109,8 @@ class check(Command): def check_restructuredtext(self): """Checks if the long string fields are reST-compliant.""" data = self.distribution.get_long_description() + if not isinstance(data, unicode): + data = data.decode(PKG_INFO_ENCODING) for warning in self._check_rst_data(data): line = warning[-1].get('line') if line is None: diff --git a/command/register.py b/command/register.py index dc089902..edb42b95 100644 --- a/command/register.py +++ b/command/register.py @@ -10,7 +10,6 @@ __revision__ = "$Id$" import urllib2 import getpass import urlparse -import StringIO from warnings import warn from distutils.core import PyPIRCCommand @@ -260,21 +259,30 @@ Your selection [default 1]: ''', log.INFO) boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary end_boundary = sep_boundary + '--' - body = StringIO.StringIO() + chunks = [] for key, value in data.items(): # handle multiple entries for the same name if type(value) not in (type([]), type( () )): value = [value] for value in value: - body.write(sep_boundary) - body.write('\nContent-Disposition: form-data; name="%s"'%key) - body.write("\n\n") - body.write(value) + chunks.append(sep_boundary) + chunks.append('\nContent-Disposition: form-data; name="%s"'%key) + chunks.append("\n\n") + chunks.append(value) if value and value[-1] == '\r': - body.write('\n') # write an extra newline (lurve Macs) - body.write(end_boundary) - body.write("\n") - body = body.getvalue() + chunks.append('\n') # write an extra newline (lurve Macs) + chunks.append(end_boundary) + chunks.append("\n") + + # chunks may be bytes (str) or unicode objects that we need to encode + body = [] + for chunk in chunks: + if isinstance(chunk, unicode): + body.append(chunk.encode('utf-8')) + else: + body.append(chunk) + + body = ''.join(body) # build the Request headers = { diff --git a/tests/test_check.py b/tests/test_check.py index 4ea83dcb..f73342ad 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -1,3 +1,4 @@ +# -*- encoding: utf8 -*- """Tests for distutils.command.check.""" import unittest from test.test_support import run_unittest @@ -46,6 +47,15 @@ class CheckTestCase(support.LoggingSilencer, cmd = self._run(metadata, strict=1) self.assertEqual(cmd._warnings, 0) + # now a test with Unicode entries + metadata = {'url': u'xxx', 'author': u'\u00c9ric', + 'author_email': u'xxx', u'name': 'xxx', + 'version': u'xxx', + 'description': u'Something about esszet \u00df', + 'long_description': u'More things about esszet \u00df'} + cmd = self._run(metadata) + self.assertEqual(cmd._warnings, 0) + def test_check_document(self): if not HAS_DOCUTILS: # won't test without docutils return @@ -80,8 +90,8 @@ class CheckTestCase(support.LoggingSilencer, self.assertRaises(DistutilsSetupError, self._run, metadata, **{'strict': 1, 'restructuredtext': 1}) - # and non-broken rest - metadata['long_description'] = 'title\n=====\n\ntest' + # and non-broken rest, including a non-ASCII character to test #12114 + metadata['long_description'] = u'title\n=====\n\ntest \u00df' cmd = self._run(metadata, strict=1, restructuredtext=1) self.assertEqual(cmd._warnings, 0) diff --git a/tests/test_register.py b/tests/test_register.py index bf634870..aa9bc43c 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -1,5 +1,5 @@ -"""Tests for distutils.command.register.""" # -*- encoding: utf8 -*- +"""Tests for distutils.command.register.""" import sys import os import unittest @@ -246,6 +246,24 @@ class RegisterTestCase(PyPIRCCommandTestCase): finally: del register_module.raw_input + # and finally a Unicode test (bug #12114) + metadata = {'url': u'xxx', 'author': u'\u00c9ric', + 'author_email': u'xxx', u'name': 'xxx', + 'version': u'xxx', + 'description': u'Something about esszet \u00df', + 'long_description': u'More things about esszet \u00df'} + + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + inputs = RawInputs('1', 'tarek', 'y') + register_module.raw_input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.raw_input + def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated cmd = self._get_cmd() -- cgit v1.2.1 From c43ef144109d289030fdc9851cbd89b6700dcb24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 9 Oct 2011 07:25:33 +0200 Subject: =?UTF-8?q?Add=20tests=20for=20Unicode=20handling=20in=20distutils?= =?UTF-8?q?=E2=80=99=20check=20and=20register=20(#13114)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_check.py | 13 +++++++++++-- tests/test_register.py | 20 +++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/tests/test_check.py b/tests/test_check.py index 229ae25c..4de64734 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -46,6 +46,15 @@ class CheckTestCase(support.LoggingSilencer, cmd = self._run(metadata, strict=1) self.assertEqual(cmd._warnings, 0) + # now a test with non-ASCII characters + metadata = {'url': 'xxx', 'author': '\u00c9ric', + 'author_email': 'xxx', 'name': 'xxx', + 'version': 'xxx', + 'description': 'Something about esszet \u00df', + 'long_description': 'More things about esszet \u00df'} + cmd = self._run(metadata) + self.assertEqual(cmd._warnings, 0) + def test_check_document(self): if not HAS_DOCUTILS: # won't test without docutils return @@ -80,8 +89,8 @@ class CheckTestCase(support.LoggingSilencer, self.assertRaises(DistutilsSetupError, self._run, metadata, **{'strict': 1, 'restructuredtext': 1}) - # and non-broken rest - metadata['long_description'] = 'title\n=====\n\ntest' + # and non-broken rest, including a non-ASCII character to test #12114 + metadata['long_description'] = 'title\n=====\n\ntest \u00df' cmd = self._run(metadata, strict=1, restructuredtext=1) self.assertEqual(cmd._warnings, 0) diff --git a/tests/test_register.py b/tests/test_register.py index cb72a115..5863ae14 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -214,7 +214,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): # metadata are OK but long_description is broken metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', + 'author_email': 'éxéxé', 'name': 'xxx', 'version': 'xxx', 'long_description': 'title\n==\n\ntext'} @@ -247,6 +247,24 @@ class RegisterTestCase(PyPIRCCommandTestCase): finally: del register_module.input + # and finally a Unicode test (bug #12114) + metadata = {'url': 'xxx', 'author': '\u00c9ric', + 'author_email': 'xxx', 'name': 'xxx', + 'version': 'xxx', + 'description': 'Something about esszet \u00df', + 'long_description': 'More things about esszet \u00df'} + + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.input + def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated cmd = self._get_cmd() -- cgit v1.2.1 From a5b80fee6d16abbaa5c94ae1a6ea967cdfb3e888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 11 Oct 2011 02:45:51 +0200 Subject: Increase test coverage for distutils.filelist (#11751). Patch by Justin Love. --- tests/test_filelist.py | 201 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 194 insertions(+), 7 deletions(-) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index c7e5201b..f3304b2e 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -1,11 +1,25 @@ """Tests for distutils.filelist.""" +import re import unittest +from distutils import debug +from distutils.log import WARN +from distutils.errors import DistutilsTemplateError +from distutils.filelist import glob_to_re, translate_pattern, FileList -from distutils.filelist import glob_to_re, FileList from test.support import captured_stdout, run_unittest -from distutils import debug +from distutils.tests import support + + +class FileListTestCase(support.LoggingSilencer, + unittest.TestCase): -class FileListTestCase(unittest.TestCase): + def assertNoWarnings(self): + self.assertEqual(self.get_logs(WARN), []) + self.clear_logs() + + def assertWarnings(self): + self.assertGreater(len(self.get_logs(WARN)), 0) + self.clear_logs() def test_glob_to_re(self): # simple cases @@ -23,18 +37,191 @@ class FileListTestCase(unittest.TestCase): file_list = FileList() with captured_stdout() as stdout: file_list.debug_print('xxx') - stdout.seek(0) - self.assertEqual(stdout.read(), '') + self.assertEqual(stdout.getvalue(), '') debug.DEBUG = True try: with captured_stdout() as stdout: file_list.debug_print('xxx') - stdout.seek(0) - self.assertEqual(stdout.read(), 'xxx\n') + self.assertEqual(stdout.getvalue(), 'xxx\n') finally: debug.DEBUG = False + def test_set_allfiles(self): + file_list = FileList() + files = ['a', 'b', 'c'] + file_list.set_allfiles(files) + self.assertEqual(file_list.allfiles, files) + + def test_remove_duplicates(self): + file_list = FileList() + file_list.files = ['a', 'b', 'a', 'g', 'c', 'g'] + # files must be sorted beforehand (sdist does it) + file_list.sort() + file_list.remove_duplicates() + self.assertEqual(file_list.files, ['a', 'b', 'c', 'g']) + + def test_translate_pattern(self): + # not regex + self.assertTrue(hasattr( + translate_pattern('a', anchor=True, is_regex=False), + 'search')) + + # is a regex + regex = re.compile('a') + self.assertEqual( + translate_pattern(regex, anchor=True, is_regex=True), + regex) + + # plain string flagged as regex + self.assertTrue(hasattr( + translate_pattern('a', anchor=True, is_regex=True), + 'search')) + + # glob support + self.assertTrue(translate_pattern( + '*.py', anchor=True, is_regex=False).search('filelist.py')) + + def test_exclude_pattern(self): + # return False if no match + file_list = FileList() + self.assertFalse(file_list.exclude_pattern('*.py')) + + # return True if files match + file_list = FileList() + file_list.files = ['a.py', 'b.py'] + self.assertTrue(file_list.exclude_pattern('*.py')) + + # test excludes + file_list = FileList() + file_list.files = ['a.py', 'a.txt'] + file_list.exclude_pattern('*.py') + self.assertEqual(file_list.files, ['a.txt']) + + def test_include_pattern(self): + # return False if no match + file_list = FileList() + file_list.set_allfiles([]) + self.assertFalse(file_list.include_pattern('*.py')) + + # return True if files match + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt']) + self.assertTrue(file_list.include_pattern('*.py')) + + # test * matches all files + file_list = FileList() + self.assertIsNone(file_list.allfiles) + file_list.set_allfiles(['a.py', 'b.txt']) + file_list.include_pattern('*') + self.assertEqual(file_list.allfiles, ['a.py', 'b.txt']) + + def test_process_template(self): + # invalid lines + file_list = FileList() + for action in ('include', 'exclude', 'global-include', + 'global-exclude', 'recursive-include', + 'recursive-exclude', 'graft', 'prune', 'blarg'): + self.assertRaises(DistutilsTemplateError, + file_list.process_template_line, action) + + # include + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt', 'd/c.py']) + + file_list.process_template_line('include *.py') + self.assertEqual(file_list.files, ['a.py']) + self.assertNoWarnings() + + file_list.process_template_line('include *.rb') + self.assertEqual(file_list.files, ['a.py']) + self.assertWarnings() + + # exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', 'd/c.py'] + + file_list.process_template_line('exclude *.py') + self.assertEqual(file_list.files, ['b.txt', 'd/c.py']) + self.assertNoWarnings() + + file_list.process_template_line('exclude *.rb') + self.assertEqual(file_list.files, ['b.txt', 'd/c.py']) + self.assertWarnings() + + # global-include + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt', 'd/c.py']) + + file_list.process_template_line('global-include *.py') + self.assertEqual(file_list.files, ['a.py', 'd/c.py']) + self.assertNoWarnings() + + file_list.process_template_line('global-include *.rb') + self.assertEqual(file_list.files, ['a.py', 'd/c.py']) + self.assertWarnings() + + # global-exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', 'd/c.py'] + + file_list.process_template_line('global-exclude *.py') + self.assertEqual(file_list.files, ['b.txt']) + self.assertNoWarnings() + + file_list.process_template_line('global-exclude *.rb') + self.assertEqual(file_list.files, ['b.txt']) + self.assertWarnings() + + # recursive-include + file_list = FileList() + file_list.set_allfiles(['a.py', 'd/b.py', 'd/c.txt', 'd/d/e.py']) + + file_list.process_template_line('recursive-include d *.py') + self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py']) + self.assertNoWarnings() + + file_list.process_template_line('recursive-include e *.py') + self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py']) + self.assertWarnings() + + # recursive-exclude + file_list = FileList() + file_list.files = ['a.py', 'd/b.py', 'd/c.txt', 'd/d/e.py'] + + file_list.process_template_line('recursive-exclude d *.py') + self.assertEqual(file_list.files, ['a.py', 'd/c.txt']) + self.assertNoWarnings() + + file_list.process_template_line('recursive-exclude e *.py') + self.assertEqual(file_list.files, ['a.py', 'd/c.txt']) + self.assertWarnings() + + # graft + file_list = FileList() + file_list.set_allfiles(['a.py', 'd/b.py', 'd/d/e.py', 'f/f.py']) + + file_list.process_template_line('graft d') + self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py']) + self.assertNoWarnings() + + file_list.process_template_line('graft e') + self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py']) + self.assertWarnings() + + # prune + file_list = FileList() + file_list.files = ['a.py', 'd/b.py', 'd/d/e.py', 'f/f.py'] + + file_list.process_template_line('prune d') + self.assertEqual(file_list.files, ['a.py', 'f/f.py']) + self.assertNoWarnings() + + file_list.process_template_line('prune e') + self.assertEqual(file_list.files, ['a.py', 'f/f.py']) + self.assertWarnings() + + def test_suite(): return unittest.makeSuite(FileListTestCase) -- cgit v1.2.1 From 7b2e8854237e74d196cdb5b736838b8ac1c8ec68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Fri, 14 Oct 2011 18:15:31 +0200 Subject: Increase test coverage for distutils.filelist (#11751). Patch by Justin Love. --- tests/test_filelist.py | 207 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 197 insertions(+), 10 deletions(-) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index be11ea5d..1e04077f 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -1,10 +1,14 @@ """Tests for distutils.filelist.""" -from os.path import join +import re import unittest -from test.test_support import captured_stdout, run_unittest - -from distutils.filelist import glob_to_re, FileList +from os.path import join from distutils import debug +from distutils.log import WARN +from distutils.errors import DistutilsTemplateError +from distutils.filelist import glob_to_re, translate_pattern, FileList + +from test.test_support import captured_stdout, run_unittest +from distutils.tests import support MANIFEST_IN = """\ include ok @@ -20,7 +24,17 @@ graft dir prune dir3 """ -class FileListTestCase(unittest.TestCase): + +class FileListTestCase(support.LoggingSilencer, + unittest.TestCase): + + def assertNoWarnings(self): + self.assertEqual(self.get_logs(WARN), []) + self.clear_logs() + + def assertWarnings(self): + self.assertGreater(len(self.get_logs(WARN)), 0) + self.clear_logs() def test_glob_to_re(self): # simple cases @@ -48,7 +62,7 @@ class FileListTestCase(unittest.TestCase): join('dir', 'graft-one'), join('dir', 'dir2', 'graft2'), join('dir3', 'ok'), - join('dir3', 'sub', 'ok.txt') + join('dir3', 'sub', 'ok.txt'), ] for line in MANIFEST_IN.split('\n'): @@ -66,18 +80,191 @@ class FileListTestCase(unittest.TestCase): file_list = FileList() with captured_stdout() as stdout: file_list.debug_print('xxx') - stdout.seek(0) - self.assertEqual(stdout.read(), '') + self.assertEqual(stdout.getvalue(), '') debug.DEBUG = True try: with captured_stdout() as stdout: file_list.debug_print('xxx') - stdout.seek(0) - self.assertEqual(stdout.read(), 'xxx\n') + self.assertEqual(stdout.getvalue(), 'xxx\n') finally: debug.DEBUG = False + def test_set_allfiles(self): + file_list = FileList() + files = ['a', 'b', 'c'] + file_list.set_allfiles(files) + self.assertEqual(file_list.allfiles, files) + + def test_remove_duplicates(self): + file_list = FileList() + file_list.files = ['a', 'b', 'a', 'g', 'c', 'g'] + # files must be sorted beforehand (sdist does it) + file_list.sort() + file_list.remove_duplicates() + self.assertEqual(file_list.files, ['a', 'b', 'c', 'g']) + + def test_translate_pattern(self): + # not regex + self.assertTrue(hasattr( + translate_pattern('a', anchor=True, is_regex=False), + 'search')) + + # is a regex + regex = re.compile('a') + self.assertEqual( + translate_pattern(regex, anchor=True, is_regex=True), + regex) + + # plain string flagged as regex + self.assertTrue(hasattr( + translate_pattern('a', anchor=True, is_regex=True), + 'search')) + + # glob support + self.assertTrue(translate_pattern( + '*.py', anchor=True, is_regex=False).search('filelist.py')) + + def test_exclude_pattern(self): + # return False if no match + file_list = FileList() + self.assertFalse(file_list.exclude_pattern('*.py')) + + # return True if files match + file_list = FileList() + file_list.files = ['a.py', 'b.py'] + self.assertTrue(file_list.exclude_pattern('*.py')) + + # test excludes + file_list = FileList() + file_list.files = ['a.py', 'a.txt'] + file_list.exclude_pattern('*.py') + self.assertEqual(file_list.files, ['a.txt']) + + def test_include_pattern(self): + # return False if no match + file_list = FileList() + file_list.set_allfiles([]) + self.assertFalse(file_list.include_pattern('*.py')) + + # return True if files match + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt']) + self.assertTrue(file_list.include_pattern('*.py')) + + # test * matches all files + file_list = FileList() + self.assertIsNone(file_list.allfiles) + file_list.set_allfiles(['a.py', 'b.txt']) + file_list.include_pattern('*') + self.assertEqual(file_list.allfiles, ['a.py', 'b.txt']) + + def test_process_template(self): + # invalid lines + file_list = FileList() + for action in ('include', 'exclude', 'global-include', + 'global-exclude', 'recursive-include', + 'recursive-exclude', 'graft', 'prune', 'blarg'): + self.assertRaises(DistutilsTemplateError, + file_list.process_template_line, action) + + # include + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt', 'd/c.py']) + + file_list.process_template_line('include *.py') + self.assertEqual(file_list.files, ['a.py']) + self.assertNoWarnings() + + file_list.process_template_line('include *.rb') + self.assertEqual(file_list.files, ['a.py']) + self.assertWarnings() + + # exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', 'd/c.py'] + + file_list.process_template_line('exclude *.py') + self.assertEqual(file_list.files, ['b.txt', 'd/c.py']) + self.assertNoWarnings() + + file_list.process_template_line('exclude *.rb') + self.assertEqual(file_list.files, ['b.txt', 'd/c.py']) + self.assertWarnings() + + # global-include + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt', 'd/c.py']) + + file_list.process_template_line('global-include *.py') + self.assertEqual(file_list.files, ['a.py', 'd/c.py']) + self.assertNoWarnings() + + file_list.process_template_line('global-include *.rb') + self.assertEqual(file_list.files, ['a.py', 'd/c.py']) + self.assertWarnings() + + # global-exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', 'd/c.py'] + + file_list.process_template_line('global-exclude *.py') + self.assertEqual(file_list.files, ['b.txt']) + self.assertNoWarnings() + + file_list.process_template_line('global-exclude *.rb') + self.assertEqual(file_list.files, ['b.txt']) + self.assertWarnings() + + # recursive-include + file_list = FileList() + file_list.set_allfiles(['a.py', 'd/b.py', 'd/c.txt', 'd/d/e.py']) + + file_list.process_template_line('recursive-include d *.py') + self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py']) + self.assertNoWarnings() + + file_list.process_template_line('recursive-include e *.py') + self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py']) + self.assertWarnings() + + # recursive-exclude + file_list = FileList() + file_list.files = ['a.py', 'd/b.py', 'd/c.txt', 'd/d/e.py'] + + file_list.process_template_line('recursive-exclude d *.py') + self.assertEqual(file_list.files, ['a.py', 'd/c.txt']) + self.assertNoWarnings() + + file_list.process_template_line('recursive-exclude e *.py') + self.assertEqual(file_list.files, ['a.py', 'd/c.txt']) + self.assertWarnings() + + # graft + file_list = FileList() + file_list.set_allfiles(['a.py', 'd/b.py', 'd/d/e.py', 'f/f.py']) + + file_list.process_template_line('graft d') + self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py']) + self.assertNoWarnings() + + file_list.process_template_line('graft e') + self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py']) + self.assertWarnings() + + # prune + file_list = FileList() + file_list.files = ['a.py', 'd/b.py', 'd/d/e.py', 'f/f.py'] + + file_list.process_template_line('prune d') + self.assertEqual(file_list.files, ['a.py', 'f/f.py']) + self.assertNoWarnings() + + file_list.process_template_line('prune e') + self.assertEqual(file_list.files, ['a.py', 'f/f.py']) + self.assertWarnings() + + def test_suite(): return unittest.makeSuite(FileListTestCase) -- cgit v1.2.1 From ed45aad402e3df6f660d88c19710e56951126286 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 17 Oct 2011 11:05:36 +1100 Subject: Issue #7833: Ext. modules built using distutils on Windows no longer get a manifest --- msvc9compiler.py | 77 +++++++++++++++++++++++++++++++++------------ tests/test_msvc9compiler.py | 47 +++++++++++++++++++++++++-- 2 files changed, 102 insertions(+), 22 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index bf85ac75..9838d64a 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -640,15 +640,7 @@ class MSVCCompiler(CCompiler) : self.library_filename(dll_name)) ld_args.append ('/IMPLIB:' + implib_file) - # Embedded manifests are recommended - see MSDN article titled - # "How to: Embed a Manifest Inside a C/C++ Application" - # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) - # Ask the linker to generate the manifest in the temp dir, so - # we can embed it later. - temp_manifest = os.path.join( - build_temp, - os.path.basename(output_filename) + ".manifest") - ld_args.append('/MANIFESTFILE:' + temp_manifest) + self.manifest_setup_ldargs(output_filename, build_temp, ld_args) if extra_preargs: ld_args[:0] = extra_preargs @@ -666,20 +658,54 @@ class MSVCCompiler(CCompiler) : # will still consider the DLL up-to-date, but it will not have a # manifest. Maybe we should link to a temp file? OTOH, that # implies a build environment error that shouldn't go undetected. - if target_desc == CCompiler.EXECUTABLE: - mfid = 1 - else: - mfid = 2 - self._remove_visual_c_ref(temp_manifest) - out_arg = '-outputresource:%s;%s' % (output_filename, mfid) - try: - self.spawn(['mt.exe', '-nologo', '-manifest', - temp_manifest, out_arg]) - except DistutilsExecError, msg: - raise LinkError(msg) + mfinfo = self.manifest_get_embed_info(target_desc, ld_args) + if mfinfo is not None: + mffilename, mfid = mfinfo + out_arg = '-outputresource:%s;%s' % (output_filename, mfid) + try: + self.spawn(['mt.exe', '-nologo', '-manifest', + mffilename, out_arg]) + except DistutilsExecError, msg: + raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) + def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): + # If we need a manifest at all, an embedded manifest is recommended. + # See MSDN article titled + # "How to: Embed a Manifest Inside a C/C++ Application" + # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) + # Ask the linker to generate the manifest in the temp dir, so + # we can check it, and possibly embed it, later. + temp_manifest = os.path.join( + build_temp, + os.path.basename(output_filename) + ".manifest") + ld_args.append('/MANIFESTFILE:' + temp_manifest) + + def manifest_get_embed_info(self, target_desc, ld_args): + # If a manifest should be embedded, return a tuple of + # (manifest_filename, resource_id). Returns None if no manifest + # should be embedded. See http://bugs.python.org/issue7833 for why + # we want to avoid any manifest for extension modules if we can) + for arg in ld_args: + if arg.startswith("/MANIFESTFILE:"): + temp_manifest = arg.split(":", 1)[1] + break + else: + # no /MANIFESTFILE so nothing to do. + return None + if target_desc == CCompiler.EXECUTABLE: + # by default, executables always get the manifest with the + # CRT referenced. + mfid = 1 + else: + # Extension modules try and avoid any manifest if possible. + mfid = 2 + temp_manifest = self._remove_visual_c_ref(temp_manifest) + if temp_manifest is None: + return None + return temp_manifest, mfid + def _remove_visual_c_ref(self, manifest_file): try: # Remove references to the Visual C runtime, so they will @@ -688,6 +714,8 @@ class MSVCCompiler(CCompiler) : # runtimes are not in WinSxS folder, but in Python's own # folder), the runtimes do not need to be in every folder # with .pyd's. + # Returns either the filename of the modified manifest or + # None if no manifest should be embedded. manifest_f = open(manifest_file) try: manifest_buf = manifest_f.read() @@ -700,9 +728,18 @@ class MSVCCompiler(CCompiler) : manifest_buf = re.sub(pattern, "", manifest_buf) pattern = "\s*" manifest_buf = re.sub(pattern, "", manifest_buf) + # Now see if any other assemblies are referenced - if not, we + # don't want a manifest embedded. + pattern = re.compile( + r"""|)""", re.DOTALL) + if re.search(pattern, manifest_buf) is None: + return None + manifest_f = open(manifest_file, 'w') try: manifest_f.write(manifest_buf) + return manifest_file finally: manifest_f.close() except IOError: diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index e155dbfe..73470729 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -7,7 +7,36 @@ from distutils.errors import DistutilsPlatformError from distutils.tests import support from test.test_support import run_unittest -_MANIFEST = """\ +# A manifest with the only assembly reference being the msvcrt assembly, so +# should have the assembly completely stripped. Note that although the +# assembly has a reference the assembly is removed - that is +# currently a "feature", not a bug :) +_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\ + + + + + + + + + + + + + + + + + +""" + +# A manifest with references to assemblies other than msvcrt. When processed, +# this assembly should be returned with just the msvcrt part removed. +_MANIFEST_WITH_MULTIPLE_REFERENCES = """\ @@ -115,7 +144,7 @@ class msvc9compilerTestCase(support.TempdirManager, manifest = os.path.join(tempdir, 'manifest') f = open(manifest, 'w') try: - f.write(_MANIFEST) + f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES) finally: f.close() @@ -133,6 +162,20 @@ class msvc9compilerTestCase(support.TempdirManager, # makes sure the manifest was properly cleaned self.assertEqual(content, _CLEANED_MANIFEST) + def test_remove_entire_manifest(self): + from distutils.msvc9compiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + try: + f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE) + finally: + f.close() + + compiler = MSVCCompiler() + got = compiler._remove_visual_c_ref(manifest) + self.assertIs(got, None) + def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) -- cgit v1.2.1 From 1f218d0f3f8aa86e62b9b8c39efdb077d66481d4 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 17 Oct 2011 11:05:57 +1100 Subject: Issue #7833: Ext. modules built using distutils on Windows no longer get a manifest --- msvc9compiler.py | 78 +++++++++++++++++++++++++++++++++------------ tests/test_msvc9compiler.py | 47 +++++++++++++++++++++++++-- 2 files changed, 102 insertions(+), 23 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 0cddb5c8..0807a360 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -627,15 +627,7 @@ class MSVCCompiler(CCompiler) : self.library_filename(dll_name)) ld_args.append ('/IMPLIB:' + implib_file) - # Embedded manifests are recommended - see MSDN article titled - # "How to: Embed a Manifest Inside a C/C++ Application" - # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) - # Ask the linker to generate the manifest in the temp dir, so - # we can embed it later. - temp_manifest = os.path.join( - build_temp, - os.path.basename(output_filename) + ".manifest") - ld_args.append('/MANIFESTFILE:' + temp_manifest) + self.manifest_setup_ldargs(output_filename, build_temp, ld_args) if extra_preargs: ld_args[:0] = extra_preargs @@ -653,21 +645,54 @@ class MSVCCompiler(CCompiler) : # will still consider the DLL up-to-date, but it will not have a # manifest. Maybe we should link to a temp file? OTOH, that # implies a build environment error that shouldn't go undetected. - if target_desc == CCompiler.EXECUTABLE: - mfid = 1 - else: - mfid = 2 - # Remove references to the Visual C runtime - self._remove_visual_c_ref(temp_manifest) - out_arg = '-outputresource:%s;%s' % (output_filename, mfid) - try: - self.spawn(['mt.exe', '-nologo', '-manifest', - temp_manifest, out_arg]) - except DistutilsExecError as msg: - raise LinkError(msg) + mfinfo = self.manifest_get_embed_info(target_desc, ld_args) + if mfinfo is not None: + mffilename, mfid = mfinfo + out_arg = '-outputresource:%s;%s' % (output_filename, mfid) + try: + self.spawn(['mt.exe', '-nologo', '-manifest', + mffilename, out_arg]) + except DistutilsExecError as msg: + raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) + def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): + # If we need a manifest at all, an embedded manifest is recommended. + # See MSDN article titled + # "How to: Embed a Manifest Inside a C/C++ Application" + # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) + # Ask the linker to generate the manifest in the temp dir, so + # we can check it, and possibly embed it, later. + temp_manifest = os.path.join( + build_temp, + os.path.basename(output_filename) + ".manifest") + ld_args.append('/MANIFESTFILE:' + temp_manifest) + + def manifest_get_embed_info(self, target_desc, ld_args): + # If a manifest should be embedded, return a tuple of + # (manifest_filename, resource_id). Returns None if no manifest + # should be embedded. See http://bugs.python.org/issue7833 for why + # we want to avoid any manifest for extension modules if we can) + for arg in ld_args: + if arg.startswith("/MANIFESTFILE:"): + temp_manifest = arg.split(":", 1)[1] + break + else: + # no /MANIFESTFILE so nothing to do. + return None + if target_desc == CCompiler.EXECUTABLE: + # by default, executables always get the manifest with the + # CRT referenced. + mfid = 1 + else: + # Extension modules try and avoid any manifest if possible. + mfid = 2 + temp_manifest = self._remove_visual_c_ref(temp_manifest) + if temp_manifest is None: + return None + return temp_manifest, mfid + def _remove_visual_c_ref(self, manifest_file): try: # Remove references to the Visual C runtime, so they will @@ -676,6 +701,8 @@ class MSVCCompiler(CCompiler) : # runtimes are not in WinSxS folder, but in Python's own # folder), the runtimes do not need to be in every folder # with .pyd's. + # Returns either the filename of the modified manifest or + # None if no manifest should be embedded. manifest_f = open(manifest_file) try: manifest_buf = manifest_f.read() @@ -688,9 +715,18 @@ class MSVCCompiler(CCompiler) : manifest_buf = re.sub(pattern, "", manifest_buf) pattern = "\s*" manifest_buf = re.sub(pattern, "", manifest_buf) + # Now see if any other assemblies are referenced - if not, we + # don't want a manifest embedded. + pattern = re.compile( + r"""|)""", re.DOTALL) + if re.search(pattern, manifest_buf) is None: + return None + manifest_f = open(manifest_file, 'w') try: manifest_f.write(manifest_buf) + return manifest_file finally: manifest_f.close() except IOError: diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index a0d62efc..5fa1ca10 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -7,7 +7,36 @@ from distutils.errors import DistutilsPlatformError from distutils.tests import support from test.support import run_unittest -_MANIFEST = """\ +# A manifest with the only assembly reference being the msvcrt assembly, so +# should have the assembly completely stripped. Note that although the +# assembly has a reference the assembly is removed - that is +# currently a "feature", not a bug :) +_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\ + + + + + + + + + + + + + + + + + +""" + +# A manifest with references to assemblies other than msvcrt. When processed, +# this assembly should be returned with just the msvcrt part removed. +_MANIFEST_WITH_MULTIPLE_REFERENCES = """\ @@ -115,7 +144,7 @@ class msvc9compilerTestCase(support.TempdirManager, manifest = os.path.join(tempdir, 'manifest') f = open(manifest, 'w') try: - f.write(_MANIFEST) + f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES) finally: f.close() @@ -133,6 +162,20 @@ class msvc9compilerTestCase(support.TempdirManager, # makes sure the manifest was properly cleaned self.assertEqual(content, _CLEANED_MANIFEST) + def test_remove_entire_manifest(self): + from distutils.msvc9compiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + try: + f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE) + finally: + f.close() + + compiler = MSVCCompiler() + got = compiler._remove_visual_c_ref(manifest) + self.assertIs(got, None) + def test_suite(): return unittest.makeSuite(msvc9compilerTestCase) -- cgit v1.2.1 From a768a2ee006904565310189824cc120c62004568 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 17 Oct 2011 11:35:06 +1100 Subject: normalize whitespace in Lib/distutils/msvc9compiler.py --- msvc9compiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 9838d64a..7ec9b92a 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -685,7 +685,7 @@ class MSVCCompiler(CCompiler) : def manifest_get_embed_info(self, target_desc, ld_args): # If a manifest should be embedded, return a tuple of # (manifest_filename, resource_id). Returns None if no manifest - # should be embedded. See http://bugs.python.org/issue7833 for why + # should be embedded. See http://bugs.python.org/issue7833 for why # we want to avoid any manifest for extension modules if we can) for arg in ld_args: if arg.startswith("/MANIFESTFILE:"): @@ -728,7 +728,7 @@ class MSVCCompiler(CCompiler) : manifest_buf = re.sub(pattern, "", manifest_buf) pattern = "\s*" manifest_buf = re.sub(pattern, "", manifest_buf) - # Now see if any other assemblies are referenced - if not, we + # Now see if any other assemblies are referenced - if not, we # don't want a manifest embedded. pattern = re.compile( r""" Date: Mon, 17 Oct 2011 11:35:31 +1100 Subject: normalize whitespace in Lib/distutils/msvc9compiler.py --- msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 0807a360..b3f6ce10 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -715,7 +715,7 @@ class MSVCCompiler(CCompiler) : manifest_buf = re.sub(pattern, "", manifest_buf) pattern = "\s*" manifest_buf = re.sub(pattern, "", manifest_buf) - # Now see if any other assemblies are referenced - if not, we + # Now see if any other assemblies are referenced - if not, we # don't want a manifest embedded. pattern = re.compile( r""" Date: Fri, 28 Oct 2011 14:45:05 +0200 Subject: Closes #13258: Use callable() built-in in the standard library. --- dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 69825f20..a7025682 100644 --- a/dist.py +++ b/dist.py @@ -537,7 +537,7 @@ Common commands: (see '--help-commands' for more) for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): help_option_found=1 - if hasattr(func, '__call__'): + if callable(func): func() else: raise DistutilsClassError( -- cgit v1.2.1 From 489ea5a90ec0c0b9b72626b3420aa97854df2e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 2 Nov 2011 18:05:41 +0100 Subject: Cleanups in distutils tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Actually check the contents of the file created by bdist_dumb. - Don’t use “RECORD†as filename for non-PEP 376 record file - Don’t start method name with “_testâ€, it smells like a disabled test method instead of an helper method - Fix some idioms (assertIn, addCleanup) --- tests/test_bdist_dumb.py | 23 +++++++++++++++++------ tests/test_install.py | 26 +++++++++++--------------- tests/test_sdist.py | 8 ++++---- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 55ba58d1..1037d821 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -1,8 +1,10 @@ """Tests for distutils.command.bdist_dumb.""" -import unittest -import sys import os +import imp +import sys +import zipfile +import unittest from test.support import run_unittest from distutils.core import Distribution @@ -72,15 +74,24 @@ class BuildDumbTestCase(support.TempdirManager, # see what we have dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - base = "%s.%s" % (dist.get_fullname(), cmd.plat_name) + base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name) if os.name == 'os2': base = base.replace(':', '-') - wanted = ['%s.zip' % base] - self.assertEqual(dist_created, wanted) + self.assertEqual(dist_created, [base]) # now let's check what we have in the zip file - # XXX to be done + fp = zipfile.ZipFile(os.path.join('dist', base)) + try: + contents = fp.namelist() + finally: + fp.close() + + contents = sorted(os.path.basename(fn) for fn in contents) + wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], + 'foo.%s.pyc' % imp.get_tag(), + 'foo.py'] + self.assertEqual(contents, sorted(wanted)) def test_suite(): return unittest.makeSuite(BuildDumbTestCase) diff --git a/tests/test_install.py b/tests/test_install.py index dfc46b19..8a63af99 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -87,19 +87,17 @@ class InstallTestCase(support.TempdirManager, self.old_expand = os.path.expanduser os.path.expanduser = _expanduser - try: - # this is the actual test - self._test_user_site() - finally: + def cleanup(): site.USER_BASE = self.old_user_base site.USER_SITE = self.old_user_site install_module.USER_BASE = self.old_user_base install_module.USER_SITE = self.old_user_site os.path.expanduser = self.old_expand - def _test_user_site(self): + self.addCleanup(cleanup) + for key in ('nt_user', 'unix_user', 'os2_home'): - self.assertTrue(key in INSTALL_SCHEMES) + self.assertIn(key, INSTALL_SCHEMES) dist = Distribution({'name': 'xx'}) cmd = install(dist) @@ -107,14 +105,14 @@ class InstallTestCase(support.TempdirManager, # making sure the user option is there options = [name for name, short, lable in cmd.user_options] - self.assertTrue('user' in options) + self.assertIn('user', options) # setting a value cmd.user = 1 # user base and site shouldn't be created yet - self.assertTrue(not os.path.exists(self.user_base)) - self.assertTrue(not os.path.exists(self.user_site)) + self.assertFalse(os.path.exists(self.user_base)) + self.assertFalse(os.path.exists(self.user_site)) # let's run finalize cmd.ensure_finalized() @@ -123,8 +121,8 @@ class InstallTestCase(support.TempdirManager, self.assertTrue(os.path.exists(self.user_base)) self.assertTrue(os.path.exists(self.user_site)) - self.assertTrue('userbase' in cmd.config_vars) - self.assertTrue('usersite' in cmd.config_vars) + self.assertIn('userbase', cmd.config_vars) + self.assertIn('usersite', cmd.config_vars) def test_handle_extra_path(self): dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) @@ -178,14 +176,13 @@ class InstallTestCase(support.TempdirManager, def test_record(self): install_dir = self.mkdtemp() project_dir, dist = self.create_dist(scripts=['hello']) - self.addCleanup(os.chdir, os.getcwd()) os.chdir(project_dir) self.write_file('hello', "print('o hai')") cmd = install(dist) dist.command_obj['install'] = cmd cmd.root = install_dir - cmd.record = os.path.join(project_dir, 'RECORD') + cmd.record = os.path.join(project_dir, 'filelist') cmd.ensure_finalized() cmd.run() @@ -204,7 +201,6 @@ class InstallTestCase(support.TempdirManager, install_dir = self.mkdtemp() project_dir, dist = self.create_dist(ext_modules=[ Extension('xx', ['xxmodule.c'])]) - self.addCleanup(os.chdir, os.getcwd()) os.chdir(project_dir) support.copy_xxmodule_c(project_dir) @@ -216,7 +212,7 @@ class InstallTestCase(support.TempdirManager, dist.command_obj['install'] = cmd dist.command_obj['build_ext'] = buildextcmd cmd.root = install_dir - cmd.record = os.path.join(project_dir, 'RECORD') + cmd.record = os.path.join(project_dir, 'filelist') cmd.ensure_finalized() cmd.run() diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 529b4ef5..d0d16b2f 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -288,7 +288,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # the following tests make sure there is a nice error message instead # of a traceback when parsing an invalid manifest template - def _test_template(self, content): + def _check_template(self, content): dist, cmd = self.get_cmd() os.chdir(self.tmp_dir) self.write_file('MANIFEST.in', content) @@ -299,17 +299,17 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(len(warnings), 1) def test_invalid_template_unknown_command(self): - self._test_template('taunt knights *') + self._check_template('taunt knights *') def test_invalid_template_wrong_arguments(self): # this manifest command takes one argument - self._test_template('prune') + self._check_template('prune') @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only') def test_invalid_template_wrong_path(self): # on Windows, trailing slashes are not allowed # this used to crash instead of raising a warning: #8286 - self._test_template('include examples/') + self._check_template('include examples/') @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_get_file_list(self): -- cgit v1.2.1 From 4d25e1b74cf370c9013b6e016e0ac9da4d57f6bb Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 3 Nov 2011 02:45:46 +0100 Subject: Issue #13307: fix bdist_rpm test failures --- command/build_py.py | 6 +++--- command/install_lib.py | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 3868c12f..9e2473fb 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -3,7 +3,7 @@ Implements the Distutils 'build_py' command.""" import sys, os -import sys +import imp from glob import glob from distutils.core import Command @@ -311,9 +311,9 @@ class build_py (Command): outputs.append(filename) if include_bytecode: if self.compile: - outputs.append(filename + "c") + outputs.append(imp.cache_from_source(filename, True)) if self.optimize > 0: - outputs.append(filename + "o") + outputs.append(imp.cache_from_source(filename, False)) outputs += [ os.path.join(build_dir, filename) diff --git a/command/install_lib.py b/command/install_lib.py index 3d01d071..8a6bc7de 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -4,6 +4,7 @@ Implements the Distutils 'install_lib' command (install all Python modules).""" import os +import imp import sys from distutils.core import Command @@ -164,9 +165,9 @@ class install_lib(Command): if ext != PYTHON_SOURCE_EXTENSION: continue if self.compile: - bytecode_files.append(py_file + "c") + bytecode_files.append(imp.cache_from_source(py_file, True)) if self.optimize > 0: - bytecode_files.append(py_file + "o") + bytecode_files.append(imp.cache_from_source(py_file, False)) return bytecode_files -- cgit v1.2.1 From 21fe162a546a17caa23b72d5ddb7f055cc9d5207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Thu, 3 Nov 2011 03:45:33 +0100 Subject: More fixes for PEP 3147 compliance in distutils (#11254) --- command/build_py.py | 9 ++++-- command/install_lib.py | 7 +++-- tests/test_build_py.py | 72 ++++++++++++++++++++++++++++++----------------- tests/test_install.py | 15 +++++----- tests/test_install_lib.py | 52 +++++++++++++++++++--------------- 5 files changed, 95 insertions(+), 60 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 3868c12f..1371b3d6 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -2,7 +2,8 @@ Implements the Distutils 'build_py' command.""" -import sys, os +import os +import imp import sys from glob import glob @@ -311,9 +312,11 @@ class build_py (Command): outputs.append(filename) if include_bytecode: if self.compile: - outputs.append(filename + "c") + outputs.append(imp.cache_from_source(filename, + debug_override=True)) if self.optimize > 0: - outputs.append(filename + "o") + outputs.append(imp.cache_from_source(filename, + debug_override=False)) outputs += [ os.path.join(build_dir, filename) diff --git a/command/install_lib.py b/command/install_lib.py index 3d01d071..15c08f12 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -4,6 +4,7 @@ Implements the Distutils 'install_lib' command (install all Python modules).""" import os +import imp import sys from distutils.core import Command @@ -164,9 +165,11 @@ class install_lib(Command): if ext != PYTHON_SOURCE_EXTENSION: continue if self.compile: - bytecode_files.append(py_file + "c") + bytecode_files.append(imp.cache_from_source( + py_file, debug_override=True)) if self.optimize > 0: - bytecode_files.append(py_file + "o") + bytecode_files.append(imp.cache_from_source( + py_file, debug_override=False)) return bytecode_files diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 80316ad7..e416edd4 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -2,7 +2,6 @@ import os import sys -import io import imp import unittest @@ -54,7 +53,6 @@ class BuildPyTestCase(support.TempdirManager, # This makes sure the list of outputs includes byte-compiled # files for Python modules but not for package data files # (there shouldn't *be* byte-code files for those!). - # self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) @@ -64,15 +62,11 @@ class BuildPyTestCase(support.TempdirManager, if sys.dont_write_bytecode: self.assertFalse(os.path.exists(pycache_dir)) else: - # XXX even with -O, distutils writes pyc, not pyo; bug? pyc_files = os.listdir(pycache_dir) self.assertIn("__init__.%s.pyc" % imp.get_tag(), pyc_files) def test_empty_package_dir(self): - # See SF 1668596/1720897. - cwd = os.getcwd() - - # create the distribution files. + # See bugs #1668596/#1720897 sources = self.mkdtemp() open(os.path.join(sources, "__init__.py"), "w").close() @@ -81,30 +75,55 @@ class BuildPyTestCase(support.TempdirManager, open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) - old_stdout = sys.stdout - sys.stdout = io.StringIO() + dist = Distribution({"packages": ["pkg"], + "package_dir": {"pkg": ""}, + "package_data": {"pkg": ["doc/*"]}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.script_args = ["build"] + dist.parse_command_line() try: - dist = Distribution({"packages": ["pkg"], - "package_dir": {"pkg": ""}, - "package_data": {"pkg": ["doc/*"]}}) - # script_name need not exist, it just need to be initialized - dist.script_name = os.path.join(sources, "setup.py") - dist.script_args = ["build"] - dist.parse_command_line() - - try: - dist.run_commands() - except DistutilsFileError: - self.fail("failed package_data test when package_dir is ''") - finally: - # Restore state. - os.chdir(cwd) - sys.stdout = old_stdout + dist.run_commands() + except DistutilsFileError: + self.fail("failed package_data test when package_dir is ''") + + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') + def test_byte_compile(self): + project_dir, dist = self.create_dist(py_modules=['boiledeggs']) + os.chdir(project_dir) + self.write_file('boiledeggs.py', 'import antigravity') + cmd = build_py(dist) + cmd.compile = 1 + cmd.build_lib = 'here' + cmd.finalize_options() + cmd.run() + + found = os.listdir(cmd.build_lib) + self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) + found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) + self.assertEqual(found, ['boiledeggs.%s.pyc' % imp.get_tag()]) + + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') + def test_byte_compile_optimized(self): + project_dir, dist = self.create_dist(py_modules=['boiledeggs']) + os.chdir(project_dir) + self.write_file('boiledeggs.py', 'import antigravity') + cmd = build_py(dist) + cmd.compile = 0 + cmd.optimize = 1 + cmd.build_lib = 'here' + cmd.finalize_options() + cmd.run() + + found = os.listdir(cmd.build_lib) + self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) + found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) + self.assertEqual(sorted(found), ['boiledeggs.%s.pyo' % imp.get_tag()]) def test_dont_write_bytecode(self): # makes sure byte_compile is not used - pkg_dir, dist = self.create_dist() + dist = self.create_dist()[1] cmd = build_py(dist) cmd.compile = 1 cmd.optimize = 1 @@ -118,6 +137,7 @@ class BuildPyTestCase(support.TempdirManager, self.assertIn('byte-compiling is disabled', self.logs[0][1]) + def test_suite(): return unittest.makeSuite(BuildPyTestCase) diff --git a/tests/test_install.py b/tests/test_install.py index 8a63af99..cb2e1f28 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -1,6 +1,7 @@ """Tests for distutils.command.install.""" import os +import imp import sys import unittest import site @@ -67,10 +68,7 @@ class InstallTestCase(support.TempdirManager, check_path(cmd.install_data, destination) def test_user_site(self): - # site.USER_SITE was introduced in 2.6 - if sys.version < '2.6': - return - + # test install with --user # preparing the environment for the test self.old_user_base = site.USER_BASE self.old_user_site = site.USER_SITE @@ -175,9 +173,11 @@ class InstallTestCase(support.TempdirManager, def test_record(self): install_dir = self.mkdtemp() - project_dir, dist = self.create_dist(scripts=['hello']) + project_dir, dist = self.create_dist(py_modules=['hello'], + scripts=['sayhi']) os.chdir(project_dir) - self.write_file('hello', "print('o hai')") + self.write_file('hello.py', "def main(): print('o hai')") + self.write_file('sayhi', 'from hello import main; main()') cmd = install(dist) dist.command_obj['install'] = cmd @@ -193,7 +193,7 @@ class InstallTestCase(support.TempdirManager, f.close() found = [os.path.basename(line) for line in content.splitlines()] - expected = ['hello', + expected = ['hello.py', 'hello.%s.pyc' % imp.get_tag(), 'sayhi', 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) @@ -238,6 +238,7 @@ class InstallTestCase(support.TempdirManager, install_module.DEBUG = False self.assertTrue(len(self.logs) > old_logs_len) + def test_suite(): return unittest.makeSuite(InstallTestCase) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index b42b03b2..2bd4dc6e 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -10,13 +10,14 @@ from distutils.tests import support from distutils.errors import DistutilsOptionError from test.support import run_unittest + class InstallLibTestCase(support.TempdirManager, support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): def test_finalize_options(self): - pkg_dir, dist = self.create_dist() + dist = self.create_dist()[1] cmd = install_lib(dist) cmd.finalize_options() @@ -35,56 +36,62 @@ class InstallLibTestCase(support.TempdirManager, @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') def test_byte_compile(self): - pkg_dir, dist = self.create_dist() - os.chdir(pkg_dir) + project_dir, dist = self.create_dist() + os.chdir(project_dir) cmd = install_lib(dist) cmd.compile = cmd.optimize = 1 - f = os.path.join(pkg_dir, 'foo.py') + f = os.path.join(project_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - pyc_file = imp.cache_from_source('foo.py') + pyc_file = imp.cache_from_source('foo.py', debug_override=True) pyo_file = imp.cache_from_source('foo.py', debug_override=False) self.assertTrue(os.path.exists(pyc_file)) self.assertTrue(os.path.exists(pyo_file)) def test_get_outputs(self): - pkg_dir, dist = self.create_dist() + project_dir, dist = self.create_dist() + os.chdir(project_dir) + os.mkdir('spam') cmd = install_lib(dist) # setting up a dist environment cmd.compile = cmd.optimize = 1 - cmd.install_dir = pkg_dir - f = os.path.join(pkg_dir, 'foo.py') - self.write_file(f, '# python file') - cmd.distribution.py_modules = [pkg_dir] + cmd.install_dir = self.mkdtemp() + f = os.path.join(project_dir, 'spam', '__init__.py') + self.write_file(f, '# python package') cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] - cmd.distribution.packages = [pkg_dir] + cmd.distribution.packages = ['spam'] cmd.distribution.script_name = 'setup.py' - # get_output should return 4 elements - self.assertTrue(len(cmd.get_outputs()) >= 2) + # get_outputs should return 4 elements: spam/__init__.py, .pyc and + # .pyo, foo.import-tag-abiflags.so / foo.pyd + outputs = cmd.get_outputs() + self.assertEqual(len(outputs), 4, outputs) def test_get_inputs(self): - pkg_dir, dist = self.create_dist() + project_dir, dist = self.create_dist() + os.chdir(project_dir) + os.mkdir('spam') cmd = install_lib(dist) # setting up a dist environment cmd.compile = cmd.optimize = 1 - cmd.install_dir = pkg_dir - f = os.path.join(pkg_dir, 'foo.py') - self.write_file(f, '# python file') - cmd.distribution.py_modules = [pkg_dir] + cmd.install_dir = self.mkdtemp() + f = os.path.join(project_dir, 'spam', '__init__.py') + self.write_file(f, '# python package') cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] - cmd.distribution.packages = [pkg_dir] + cmd.distribution.packages = ['spam'] cmd.distribution.script_name = 'setup.py' - # get_input should return 2 elements - self.assertEqual(len(cmd.get_inputs()), 2) + # get_inputs should return 2 elements: spam/__init__.py and + # foo.import-tag-abiflags.so / foo.pyd + inputs = cmd.get_inputs() + self.assertEqual(len(inputs), 2, inputs) def test_dont_write_bytecode(self): # makes sure byte_compile is not used - pkg_dir, dist = self.create_dist() + dist = self.create_dist()[1] cmd = install_lib(dist) cmd.compile = 1 cmd.optimize = 1 @@ -98,6 +105,7 @@ class InstallLibTestCase(support.TempdirManager, self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) + def test_suite(): return unittest.makeSuite(InstallLibTestCase) -- cgit v1.2.1 From 658d33c3b35e0cb0cdcb75c80026192adad14a4c Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 12 Nov 2011 01:20:45 +0100 Subject: Issue #13193: fix distutils.filelist.FileList under Windows --- filelist.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/filelist.py b/filelist.py index a94b5c8e..87b2cc6b 100644 --- a/filelist.py +++ b/filelist.py @@ -313,7 +313,10 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): # ditch end of pattern character empty_pattern = glob_to_re('') prefix_re = (glob_to_re(prefix))[:-len(empty_pattern)] - pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) + # match both path separators, as in Postel's principle + sep_pat = "[" + re.escape(os.path.sep + os.path.altsep + if os.path.altsep else os.path.sep) + "]" + pattern_re = "^" + sep_pat.join([prefix_re, ".*" + pattern_re]) else: # no prefix -- respect anchor flag if anchor: pattern_re = "^" + pattern_re -- cgit v1.2.1 From 74d45d2ff91f898f30dcf8c4a47192ed8fc509c3 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 12 Nov 2011 01:33:59 +0100 Subject: Issue #13193: Fix distutils.filelist.FileList under Windows. The "recursive-include" directive now recognizes both legal path separators. --- filelist.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/filelist.py b/filelist.py index 4aac6d39..ebd14118 100644 --- a/filelist.py +++ b/filelist.py @@ -328,7 +328,10 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): # ditch end of pattern character empty_pattern = glob_to_re('') prefix_re = glob_to_re(prefix)[:-len(empty_pattern)] - pattern_re = "^" + os.path.join(prefix_re, ".*" + pattern_re) + # match both path separators, as in Postel's principle + sep_pat = "[" + re.escape(os.path.sep + os.path.altsep + if os.path.altsep else os.path.sep) + "]" + pattern_re = "^" + sep_pat.join([prefix_re, ".*" + pattern_re]) else: # no prefix -- respect anchor flag if anchor: pattern_re = "^" + pattern_re -- cgit v1.2.1 From 490e58f300e1cbed87b5efa671c3502d79e0c6a6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 26 Dec 2011 10:15:15 -0500 Subject: Issue #11638: Adding test to ensure .tar.gz files can be generated by sdist command with unicode metadata, based on David Barnett's patch. Issue #11638: Added tests to capture failures in make_tarball with various unicode strings. Following fix for Issue #13639, these tests now pass. --- tests/test_archive_util.py | 31 +++++++++++++++++++++++++++++-- tests/test_sdist.py | 22 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 1f7106f8..2881148c 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Tests for distutils.archive_util.""" __revision__ = "$Id$" @@ -40,6 +41,9 @@ class ArchiveUtilTestCase(support.TempdirManager, @unittest.skipUnless(zlib, "requires zlib") def test_make_tarball(self): + self._make_tarball('archive') + + def _make_tarball(self, target_name): # creating something to tar tmpdir = self.mkdtemp() self.write_file([tmpdir, 'file1'], 'xxx') @@ -51,7 +55,7 @@ class ArchiveUtilTestCase(support.TempdirManager, unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], "source and target should be on same drive") - base_name = os.path.join(tmpdir2, 'archive') + base_name = os.path.join(tmpdir2, target_name) # working with relative paths to avoid tar warnings old_dir = os.getcwd() @@ -66,7 +70,7 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertTrue(os.path.exists(tarball)) # trying an uncompressed one - base_name = os.path.join(tmpdir2, 'archive') + base_name = os.path.join(tmpdir2, target_name) old_dir = os.getcwd() os.chdir(tmpdir) try: @@ -277,6 +281,29 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: del ARCHIVE_FORMATS['xxx'] + @unittest.skipUnless(zlib, "requires zlib") + def test_make_tarball_unicode(self): + """ + Mirror test_make_tarball, except filename is unicode. + """ + self._make_tarball(u'archive') + + @unittest.skipUnless(zlib, "requires zlib") + def test_make_tarball_unicode_latin1(self): + """ + Mirror test_make_tarball, except filename is unicode and contains + latin characters. + """ + self._make_tarball(u'Ã¥rchiv') # note this isn't a real word + + @unittest.skipUnless(zlib, "requires zlib") + def test_make_tarball_unicode_extended(self): + """ + Mirror test_make_tarball, except filename is unicode and contains + characters outside the latin charset. + """ + self._make_tarball(u'ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–') # japanese for archive + def test_suite(): return unittest.makeSuite(ArchiveUtilTestCase) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 33f6ee69..cb7beb7b 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -165,6 +165,28 @@ class SDistTestCase(PyPIRCCommandTestCase): result.sort() self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + @unittest.skipUnless(zlib, "requires zlib") + def test_unicode_metadata_tgz(self): + """ + Unicode name or version should not break building to tar.gz format. + Reference issue #11638. + """ + + # create the sdist command with unicode parameters + dist, cmd = self.get_cmd({'name': u'fake', 'version': u'1.0'}) + + # create the sdist as gztar and run the command + cmd.formats = ['gztar'] + cmd.ensure_finalized() + cmd.run() + + # The command should have created the .tar.gz file + dist_folder = join(self.tmp_dir, 'dist') + result = os.listdir(dist_folder) + self.assertEqual(result, ['fake-1.0.tar.gz']) + + os.remove(join(dist_folder, 'fake-1.0.tar.gz')) + @unittest.skipUnless(zlib, "requires zlib") def test_add_defaults(self): -- cgit v1.2.1 From 57661611c196b2b76a8be5463ae11db361e22eb4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 26 Dec 2011 12:17:01 -0500 Subject: Ported some test cases from 2.7 for #11638 --- tests/test_archive_util.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 8edfab49..a6aeaf04 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """Tests for distutils.archive_util.""" import unittest import os @@ -32,6 +33,24 @@ class ArchiveUtilTestCase(support.TempdirManager, @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_make_tarball(self): + self._make_tarball('archive') + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_make_tarball_latin1(self): + """ + Mirror test_make_tarball, except filename contains latin characters. + """ + self._make_tarball('Ã¥rchiv') # note this isn't a real word + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_make_tarball_extended(self): + """ + Mirror test_make_tarball, except filename contains extended + characters outside the latin charset. + """ + self._make_tarball('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–') # japanese for archive + + def _make_tarball(self, target_name): # creating something to tar tmpdir = self.mkdtemp() self.write_file([tmpdir, 'file1'], 'xxx') @@ -43,7 +62,7 @@ class ArchiveUtilTestCase(support.TempdirManager, unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], "Source and target should be on same drive") - base_name = os.path.join(tmpdir2, 'archive') + base_name = os.path.join(tmpdir2, target_name) # working with relative paths to avoid tar warnings old_dir = os.getcwd() @@ -58,7 +77,7 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertTrue(os.path.exists(tarball)) # trying an uncompressed one - base_name = os.path.join(tmpdir2, 'archive') + base_name = os.path.join(tmpdir2, target_name) old_dir = os.getcwd() os.chdir(tmpdir) try: -- cgit v1.2.1 From 812d67fafa516462fdac0294a56dcf0cd1631a59 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Dec 2011 10:45:08 -0500 Subject: Limit test scope to those platforms that can save the target filenames. Reference #11638. --- tests/test_archive_util.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index a6aeaf04..1afdd462 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -2,6 +2,7 @@ """Tests for distutils.archive_util.""" import unittest import os +import sys import tarfile from os.path import splitdrive import warnings @@ -26,6 +27,18 @@ try: except ImportError: ZLIB_SUPPORT = False +def can_fs_encode(filename): + """ + Return True if the filename can be saved in the file system. + """ + if os.path.supports_unicode_filenames: + return True + try: + filename.encode(sys.getfilesystemencoding()) + except UnicodeEncodeError: + return False + return True + class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer, @@ -36,6 +49,8 @@ class ArchiveUtilTestCase(support.TempdirManager, self._make_tarball('archive') @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + @unittest.skipUnless(can_fs_encode('Ã¥rchiv'), + 'File system cannot handle this filename') def test_make_tarball_latin1(self): """ Mirror test_make_tarball, except filename contains latin characters. @@ -43,6 +58,8 @@ class ArchiveUtilTestCase(support.TempdirManager, self._make_tarball('Ã¥rchiv') # note this isn't a real word @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + @unittest.skipUnless(can_fs_encode('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–'), + 'File system cannot handle this filename') def test_make_tarball_extended(self): """ Mirror test_make_tarball, except filename contains extended -- cgit v1.2.1 From 92f16a5603d9ba55dd73f8c1ad34efb276021dc4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Dec 2011 11:42:22 -0500 Subject: Limit test scope to those platforms that can save the target filenames. Reference #11638. --- tests/test_archive_util.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 2881148c..f01cec32 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -4,6 +4,7 @@ __revision__ = "$Id$" import unittest import os +import sys import tarfile from os.path import splitdrive import warnings @@ -34,6 +35,18 @@ try: except ImportError: zlib = None +def can_fs_encode(filename): + """ + Return True if the filename can be saved in the file system. + """ + if os.path.supports_unicode_filenames: + return True + try: + filename.encode(sys.getfilesystemencoding()) + except UnicodeEncodeError: + return False + return True + class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer, @@ -289,6 +302,8 @@ class ArchiveUtilTestCase(support.TempdirManager, self._make_tarball(u'archive') @unittest.skipUnless(zlib, "requires zlib") + @unittest.skipUnless(can_fs_encode(u'Ã¥rchiv'), + 'File system cannot handle this filename') def test_make_tarball_unicode_latin1(self): """ Mirror test_make_tarball, except filename is unicode and contains @@ -297,6 +312,8 @@ class ArchiveUtilTestCase(support.TempdirManager, self._make_tarball(u'Ã¥rchiv') # note this isn't a real word @unittest.skipUnless(zlib, "requires zlib") + @unittest.skipUnless(can_fs_encode(u'ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–'), + 'File system cannot handle this filename') def test_make_tarball_unicode_extended(self): """ Mirror test_make_tarball, except filename is unicode and contains -- cgit v1.2.1 From a63bd68dca86bd47cd4424767b76e7cab6f9d136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 15 Jan 2012 02:48:55 +0100 Subject: Stop ignoring RPMs in distutils' upload command (#2945). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug reported by Hartmut Goebel and patch contributed by Carl Robben. Carl tested the fix and we have a buildbot with rpm installed, so I’m committing even though I could not run this test (but I do understand the changed code :) --- command/bdist_rpm.py | 12 ++++++++++++ tests/test_bdist_rpm.py | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 678e1188..357eaa57 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -365,16 +365,28 @@ class bdist_rpm(Command): self.spawn(rpm_cmd) if not self.dry_run: + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + if not self.binary_only: srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) assert(os.path.exists(srpm)) self.move_file(srpm, self.dist_dir) + filename = os.path.join(self.dist_dir, source_rpm) + self.distribution.dist_files.append( + ('bdist_rpm', pyversion, filename)) if not self.source_only: for rpm in binary_rpms: rpm = os.path.join(rpm_dir['RPMS'], rpm) if os.path.exists(rpm): self.move_file(rpm, self.dist_dir) + filename = os.path.join(self.dist_dir, + os.path.basename(rpm)) + self.distribution.dist_files.append( + ('bdist_rpm', pyversion, filename)) def _dist_path(self, path): return os.path.join(self.dist_dir, os.path.basename(path)) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 804fb135..ab7a1bf2 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -78,6 +78,10 @@ class BuildRpmTestCase(support.TempdirManager, dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) + # bug #2945: upload ignores bdist_rpm files + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) + def test_no_optimize_flag(self): # XXX I am unable yet to make this test work without @@ -117,6 +121,11 @@ class BuildRpmTestCase(support.TempdirManager, dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) + + # bug #2945: upload ignores bdist_rpm files + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) + os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) def test_suite(): -- cgit v1.2.1 From 91c28e06be723398c598c6412136eeec653125be Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Wed, 18 Jan 2012 03:51:38 +0100 Subject: Closes #13803: Under Solaris, distutils doesn't include bitness in the directory name --- util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util.py b/util.py index 6c49f0b1..2e4617ef 100644 --- a/util.py +++ b/util.py @@ -6,7 +6,7 @@ one of the other *util.py modules. __revision__ = "$Id$" -import sys, os, string, re +import sys, os, string, re, platform from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer from distutils.spawn import spawn @@ -76,6 +76,7 @@ def get_platform (): if release[0] >= "5": # SunOS 5 == Solaris 2 osname = "solaris" release = "%d.%s" % (int(release[0]) - 3, release[2:]) + machine += ".%s" % platform.architecture()[0] # fall through to standard osname-release-machine representation elif osname[:4] == "irix": # could be "irix64"! return "%s-%s" % (osname, release) -- cgit v1.2.1 From 379a0e19faa93540a244372b3332262916b0140a Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Wed, 18 Jan 2012 03:58:42 +0100 Subject: Closes #13803: Under Solaris, distutils doesn't include bitness in the directory name --- util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util.py b/util.py index f42c6a18..36c4a684 100644 --- a/util.py +++ b/util.py @@ -9,6 +9,7 @@ import re import imp import sys import string +import platform from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer from distutils.spawn import spawn @@ -77,6 +78,7 @@ def get_platform (): if release[0] >= "5": # SunOS 5 == Solaris 2 osname = "solaris" release = "%d.%s" % (int(release[0]) - 3, release[2:]) + machine += ".%s" % platform.architecture()[0] # fall through to standard osname-release-machine representation elif osname[:4] == "irix": # could be "irix64"! return "%s-%s" % (osname, release) -- cgit v1.2.1 From 411af75d7af96d11ff9a9fbed91298e58a2d7d07 Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Wed, 18 Jan 2012 04:25:28 +0100 Subject: Emergency fix for #13803 bootstrap issue: Under Solaris, distutils doesn't include bitness in the directory name --- util.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 2e4617ef..0c24e8ca 100644 --- a/util.py +++ b/util.py @@ -6,7 +6,7 @@ one of the other *util.py modules. __revision__ = "$Id$" -import sys, os, string, re, platform +import sys, os, string, re from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer from distutils.spawn import spawn @@ -76,7 +76,11 @@ def get_platform (): if release[0] >= "5": # SunOS 5 == Solaris 2 osname = "solaris" release = "%d.%s" % (int(release[0]) - 3, release[2:]) - machine += ".%s" % platform.architecture()[0] + # We can't use "platform.architecture()[0]" because a + # bootstrap problem. We use a dict to get an error + # if some suspicious happens. + bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} + machine += ".%s" % bitness[sys.maxint] # fall through to standard osname-release-machine representation elif osname[:4] == "irix": # could be "irix64"! return "%s-%s" % (osname, release) -- cgit v1.2.1 From 026f3483351ce08ffa47f9143aa17d7da02f10ec Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Wed, 18 Jan 2012 04:27:37 +0100 Subject: Emergency fix for #13803 bootstrap issue: Under Solaris, distutils doesn't include bitness in the directory name --- util.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 36c4a684..416deec7 100644 --- a/util.py +++ b/util.py @@ -9,7 +9,6 @@ import re import imp import sys import string -import platform from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer from distutils.spawn import spawn @@ -78,7 +77,11 @@ def get_platform (): if release[0] >= "5": # SunOS 5 == Solaris 2 osname = "solaris" release = "%d.%s" % (int(release[0]) - 3, release[2:]) - machine += ".%s" % platform.architecture()[0] + # We can't use "platform.architecture()[0]" because a + # bootstrap problem. We use a dict to get an error + # if some suspicious happens. + bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} + machine += ".%s" % bitness[sys.maxint] # fall through to standard osname-release-machine representation elif osname[:4] == "irix": # could be "irix64"! return "%s-%s" % (osname, release) -- cgit v1.2.1 From 989fd55dddc6638eab5073ab0adcc9df0ceac154 Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Wed, 18 Jan 2012 05:04:49 +0100 Subject: And yet another emergency fix for #13803 bootstrap issue: Under Solaris, distutils doesn't include bitness in the directory name --- util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.py b/util.py index 416deec7..bce84027 100644 --- a/util.py +++ b/util.py @@ -81,7 +81,7 @@ def get_platform (): # bootstrap problem. We use a dict to get an error # if some suspicious happens. bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} - machine += ".%s" % bitness[sys.maxint] + machine += ".%s" % bitness[sys.maxsize] # fall through to standard osname-release-machine representation elif osname[:4] == "irix": # could be "irix64"! return "%s-%s" % (osname, release) -- cgit v1.2.1 From e8934cbb01ea226144ff9222ad666a26a715b1a2 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 3 Feb 2012 02:39:49 +0100 Subject: Issue #13901: Prevent test_distutils failures on OS X with --enable-shared. --- tests/support.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/support.py b/tests/support.py index a2190c02..4e6058d0 100644 --- a/tests/support.py +++ b/tests/support.py @@ -200,6 +200,9 @@ def fixup_build_ext(cmd): cmd = build_ext(dist) support.fixup_build_ext(cmd) cmd.ensure_finalized() + + Unlike most other Unix platforms, Mac OS X embeds absolute paths + to shared libraries into executables, so the fixup is not needed there. """ if os.name == 'nt': cmd.debug = sys.executable.endswith('_d.exe') @@ -211,5 +214,8 @@ def fixup_build_ext(cmd): if runshared is None: cmd.library_dirs = ['.'] else: - name, equals, value = runshared.partition('=') - cmd.library_dirs = value.split(os.pathsep) + if sys.platform == 'darwin': + cmd.library_dirs = [] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = value.split(os.pathsep) -- cgit v1.2.1 From ef0dc103554be0c54d48e5bc028050423efb4594 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 3 Feb 2012 02:42:16 +0100 Subject: Issue #13901: Prevent test_distutils failures on OS X with --enable-shared. --- tests/support.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/support.py b/tests/support.py index d77bbee3..84d92323 100644 --- a/tests/support.py +++ b/tests/support.py @@ -188,6 +188,9 @@ def fixup_build_ext(cmd): cmd = build_ext(dist) support.fixup_build_ext(cmd) cmd.ensure_finalized() + + Unlike most other Unix platforms, Mac OS X embeds absolute paths + to shared libraries into executables, so the fixup is not needed there. """ if os.name == 'nt': cmd.debug = sys.executable.endswith('_d.exe') @@ -199,5 +202,8 @@ def fixup_build_ext(cmd): if runshared is None: cmd.library_dirs = ['.'] else: - name, equals, value = runshared.partition('=') - cmd.library_dirs = value.split(os.pathsep) + if sys.platform == 'darwin': + cmd.library_dirs = [] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = value.split(os.pathsep) -- cgit v1.2.1 From 8108212a71996cd25d5cb5366148d9c9dace27b5 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 10 Feb 2012 12:59:06 +0100 Subject: Issue #13590: On OS X 10.7 and 10.6 with Xcode 4.2, building Distutils-based packages with C extension modules may fail because Apple has removed gcc-4.2, the version used to build python.org 64-bit/32-bit Pythons. If the user does not explicitly override the default C compiler by setting the CC environment variable, Distutils will now attempt to compile extension modules with clang if gcc-4.2 is required but not found. Also as a convenience, if the user does explicitly set CC, substitute its value as the default compiler in the Distutils LDSHARED configuration variable for OS X. (Note, the python.org 32-bit-only Pythons use gcc-4.0 and the 10.4u SDK, neither of which are available in Xcode 4. This change does not attempt to override settings to support their use with Xcode 4.) --- sysconfig.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 0d6d4c4f..4f9041a7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -141,6 +141,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): "I don't know where Python installs its library " "on platform '%s'" % os.name) +_USE_CLANG = None def customize_compiler(compiler): """Do any platform-specific customization of a CCompiler instance. @@ -153,8 +154,38 @@ def customize_compiler(compiler): get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SO') + newcc = None if 'CC' in os.environ: - cc = os.environ['CC'] + newcc = os.environ['CC'] + elif sys.platform == 'darwin' and cc == 'gcc-4.2': + # Issue #13590: + # Since Apple removed gcc-4.2 in Xcode 4.2, we can no + # longer assume it is available for extension module builds. + # If Python was built with gcc-4.2, check first to see if + # it is available on this system; if not, try to use clang + # instead unless the caller explicitly set CC. + global _USE_CLANG + if _USE_CLANG is None: + from distutils import log + from subprocess import Popen, PIPE + p = Popen("! type gcc-4.2 && type clang && exit 2", + shell=True, stdout=PIPE, stderr=PIPE) + p.wait() + if p.returncode == 2: + _USE_CLANG = True + log.warn("gcc-4.2 not found, using clang instead") + else: + _USE_CLANG = False + if _USE_CLANG: + newcc = 'clang' + if newcc: + # On OS X, if CC is overridden, use that as the default + # command for LDSHARED as well + if (sys.platform == 'darwin' + and 'LDSHARED' not in os.environ + and ldshared.startswith(cc)): + ldshared = newcc + ldshared[len(cc):] + cc = newcc if 'CXX' in os.environ: cxx = os.environ['CXX'] if 'LDSHARED' in os.environ: -- cgit v1.2.1 From 008c905df03c73798516d88ffe6550261d4f9af3 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 10 Feb 2012 13:01:08 +0100 Subject: Issue #13590: On OS X 10.7 and 10.6 with Xcode 4.2, building Distutils-based packages with C extension modules may fail because Apple has removed gcc-4.2, the version used to build python.org 64-bit/32-bit Pythons. If the user does not explicitly override the default C compiler by setting the CC environment variable, Distutils will now attempt to compile extension modules with clang if gcc-4.2 is required but not found. Also as a convenience, if the user does explicitly set CC, substitute its value as the default compiler in the Distutils LDSHARED configuration variable for OS X. (Note, the python.org 32-bit-only Pythons use gcc-4.0 and the 10.4u SDK, neither of which are available in Xcode 4. This change does not attempt to override settings to support their use with Xcode 4.) --- sysconfig.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index ac06313b..16902ca9 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -146,6 +146,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): "I don't know where Python installs its library " "on platform '%s'" % os.name) +_USE_CLANG = None def customize_compiler(compiler): """Do any platform-specific customization of a CCompiler instance. @@ -158,8 +159,38 @@ def customize_compiler(compiler): get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS') + newcc = None if 'CC' in os.environ: - cc = os.environ['CC'] + newcc = os.environ['CC'] + elif sys.platform == 'darwin' and cc == 'gcc-4.2': + # Issue #13590: + # Since Apple removed gcc-4.2 in Xcode 4.2, we can no + # longer assume it is available for extension module builds. + # If Python was built with gcc-4.2, check first to see if + # it is available on this system; if not, try to use clang + # instead unless the caller explicitly set CC. + global _USE_CLANG + if _USE_CLANG is None: + from distutils import log + from subprocess import Popen, PIPE + p = Popen("! type gcc-4.2 && type clang && exit 2", + shell=True, stdout=PIPE, stderr=PIPE) + p.wait() + if p.returncode == 2: + _USE_CLANG = True + log.warn("gcc-4.2 not found, using clang instead") + else: + _USE_CLANG = False + if _USE_CLANG: + newcc = 'clang' + if newcc: + # On OS X, if CC is overridden, use that as the default + # command for LDSHARED as well + if (sys.platform == 'darwin' + and 'LDSHARED' not in os.environ + and ldshared.startswith(cc)): + ldshared = newcc + ldshared[len(cc):] + cc = newcc if 'CXX' in os.environ: cxx = os.environ['CXX'] if 'LDSHARED' in os.environ: -- cgit v1.2.1 From e1f6c4c9deda898b03f389edcbbe0fd16e547724 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sat, 11 Feb 2012 20:40:24 +0100 Subject: Issue #13994: Earler partial revert of Distutils enhancements in 2.7 has left two versions of customize_compiler, the original in distutils.sysconfig and another copy in distutils.ccompiler, with some parts of distutils calling one and others using the other. Complete the revert back to only having one in distutils.sysconfig as is the case in 3.x. --- ccompiler.py | 52 ------------------------------------------------ command/build_clib.py | 2 +- command/config.py | 2 +- sysconfig.py | 14 ++++++++++--- tests/test_build_clib.py | 3 ++- tests/test_ccompiler.py | 3 ++- 6 files changed, 17 insertions(+), 59 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index c2b1f6fb..7076b933 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -18,58 +18,6 @@ from distutils.dep_util import newer_group from distutils.util import split_quoted, execute from distutils import log -_sysconfig = __import__('sysconfig') - -def customize_compiler(compiler): - """Do any platform-specific customization of a CCompiler instance. - - Mainly needed on Unix, so we can plug in the information that - varies across Unices and is stored in Python's Makefile. - """ - if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ - _sysconfig.get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO', 'AR', - 'ARFLAGS') - - if 'CC' in os.environ: - cc = os.environ['CC'] - if 'CXX' in os.environ: - cxx = os.environ['CXX'] - if 'LDSHARED' in os.environ: - ldshared = os.environ['LDSHARED'] - if 'CPP' in os.environ: - cpp = os.environ['CPP'] - else: - cpp = cc + " -E" # not always - if 'LDFLAGS' in os.environ: - ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if 'CFLAGS' in os.environ: - cflags = opt + ' ' + os.environ['CFLAGS'] - ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if 'CPPFLAGS' in os.environ: - cpp = cpp + ' ' + os.environ['CPPFLAGS'] - cflags = cflags + ' ' + os.environ['CPPFLAGS'] - ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] - if 'AR' in os.environ: - ar = os.environ['AR'] - if 'ARFLAGS' in os.environ: - archiver = ar + ' ' + os.environ['ARFLAGS'] - else: - archiver = ar + ' ' + ar_flags - - cc_cmd = cc + ' ' + cflags - compiler.set_executables( - preprocessor=cpp, - compiler=cc_cmd, - compiler_so=cc_cmd + ' ' + ccshared, - compiler_cxx=cxx, - linker_so=ldshared, - linker_exe=cc, - archiver=archiver) - - compiler.shared_lib_extension = so_ext - class CCompiler: """Abstract base class to define the interface that must be implemented by real compiler classes. Also has some utility methods used by diff --git a/command/build_clib.py b/command/build_clib.py index 98a1726f..205587e7 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -19,7 +19,7 @@ __revision__ = "$Id$" import os from distutils.core import Command from distutils.errors import DistutilsSetupError -from distutils.ccompiler import customize_compiler +from distutils.sysconfig import customize_compiler from distutils import log def show_compilers(): diff --git a/command/config.py b/command/config.py index da8da595..b0849135 100644 --- a/command/config.py +++ b/command/config.py @@ -16,7 +16,7 @@ import re from distutils.core import Command from distutils.errors import DistutilsExecError -from distutils.ccompiler import customize_compiler +from distutils.sysconfig import customize_compiler from distutils import log LANG_EXT = {'c': '.c', 'c++': '.cxx'} diff --git a/sysconfig.py b/sysconfig.py index 4f9041a7..4b193b27 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -150,9 +150,10 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext) = \ + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO') + 'CCSHARED', 'LDSHARED', 'SO', 'AR', + 'ARFLAGS') newcc = None if 'CC' in os.environ: @@ -203,6 +204,12 @@ def customize_compiler(compiler): cpp = cpp + ' ' + os.environ['CPPFLAGS'] cflags = cflags + ' ' + os.environ['CPPFLAGS'] ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] + else: + archiver = ar + ' ' + ar_flags cc_cmd = cc + ' ' + cflags compiler.set_executables( @@ -211,7 +218,8 @@ def customize_compiler(compiler): compiler_so=cc_cmd + ' ' + ccshared, compiler_cxx=cxx, linker_so=ldshared, - linker_exe=cc) + linker_exe=cc, + archiver=archiver) compiler.shared_lib_extension = so_ext diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 4f4e2bc7..bef1bd99 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -122,7 +122,8 @@ class BuildCLibTestCase(support.TempdirManager, # before we run the command, we want to make sure # all commands are present on the system # by creating a compiler and checking its executables - from distutils.ccompiler import new_compiler, customize_compiler + from distutils.ccompiler import new_compiler + from distutils.sysconfig import customize_compiler compiler = new_compiler() customize_compiler(compiler) diff --git a/tests/test_ccompiler.py b/tests/test_ccompiler.py index e21873e8..45e477a4 100644 --- a/tests/test_ccompiler.py +++ b/tests/test_ccompiler.py @@ -4,7 +4,8 @@ import unittest from test.test_support import captured_stdout from distutils.ccompiler import (gen_lib_options, CCompiler, - get_default_compiler, customize_compiler) + get_default_compiler) +from distutils.sysconfig import customize_compiler from distutils import debug from distutils.tests import support -- cgit v1.2.1 From 2c62a97a9a02f45900b34cad9089a664f254ffd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 12 Feb 2012 04:41:36 +0100 Subject: Fix distutils.filelist.FileList under Windows (#13193). The code used to call os.path.join to build a regex but without escaping the backslash, which lead to test failures on Windows. Antoine Pitrou fixed it in 557a973709de by enhancing the code to accept both / and \, with proper escaping, but in my opinion this goes against the distutils feature freeze, hence this change. --- filelist.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/filelist.py b/filelist.py index ebd14118..5540b39a 100644 --- a/filelist.py +++ b/filelist.py @@ -328,10 +328,8 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): # ditch end of pattern character empty_pattern = glob_to_re('') prefix_re = glob_to_re(prefix)[:-len(empty_pattern)] - # match both path separators, as in Postel's principle - sep_pat = "[" + re.escape(os.path.sep + os.path.altsep - if os.path.altsep else os.path.sep) + "]" - pattern_re = "^" + sep_pat.join([prefix_re, ".*" + pattern_re]) + # paths should always use / in manifest templates + pattern_re = "^%s/.*%s" % (prefix_re, pattern_re) else: # no prefix -- respect anchor flag if anchor: pattern_re = "^" + pattern_re -- cgit v1.2.1 From 994f4c4872485a62b67461766025532e7144a82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 12 Feb 2012 04:52:21 +0100 Subject: Fix distutils.filelist.FileList under Windows (#13193). The code used to call os.path.join to build a regex but without escaping the backslash, which lead to test failures on Windows. Antoine Pitrou fixed it in 0a94e2f807c7 by enhancing the code to accept both / and \, with proper escaping, but in my opinion this goes against the distutils feature freeze, hence this change. --- filelist.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/filelist.py b/filelist.py index 87b2cc6b..91220321 100644 --- a/filelist.py +++ b/filelist.py @@ -313,10 +313,8 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): # ditch end of pattern character empty_pattern = glob_to_re('') prefix_re = (glob_to_re(prefix))[:-len(empty_pattern)] - # match both path separators, as in Postel's principle - sep_pat = "[" + re.escape(os.path.sep + os.path.altsep - if os.path.altsep else os.path.sep) + "]" - pattern_re = "^" + sep_pat.join([prefix_re, ".*" + pattern_re]) + # paths should always use / in manifest templates + pattern_re = "^%s/.*%s" % (prefix_re, pattern_re) else: # no prefix -- respect anchor flag if anchor: pattern_re = "^" + pattern_re -- cgit v1.2.1 From 0dcd74259a364fdc273223482084e595460ed4a1 Mon Sep 17 00:00:00 2001 From: Nadeem Vawda Date: Mon, 13 Feb 2012 21:33:51 +0200 Subject: Issue #13193: Fix distutils.filelist tests to always use / as path separator. --- tests/test_filelist.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 1e04077f..d86ea444 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -1,7 +1,6 @@ """Tests for distutils.filelist.""" import re import unittest -from os.path import join from distutils import debug from distutils.log import WARN from distutils.errors import DistutilsTemplateError @@ -54,15 +53,15 @@ class FileListTestCase(support.LoggingSilencer, # simulated file list file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt', - join('global', 'one.txt'), - join('global', 'two.txt'), - join('global', 'files.x'), - join('global', 'here.tmp'), - join('f', 'o', 'f.oo'), - join('dir', 'graft-one'), - join('dir', 'dir2', 'graft2'), - join('dir3', 'ok'), - join('dir3', 'sub', 'ok.txt'), + 'global/one.txt', + 'global/two.txt', + 'global/files.x', + 'global/here.tmp', + 'f/o/f.oo', + 'dir/graft-one', + 'dir/dir2/graft2', + 'dir3/ok', + 'dir3/sub/ok.txt', ] for line in MANIFEST_IN.split('\n'): @@ -70,9 +69,8 @@ class FileListTestCase(support.LoggingSilencer, continue file_list.process_template_line(line) - wanted = ['ok', 'four.txt', join('global', 'one.txt'), - join('global', 'two.txt'), join('f', 'o', 'f.oo'), - join('dir', 'graft-one'), join('dir', 'dir2', 'graft2')] + wanted = ['ok', 'four.txt', 'global/one.txt', 'global/two.txt', + 'f/o/f.oo', 'dir/graft-one', 'dir/dir2/graft2'] self.assertEqual(file_list.files, wanted) -- cgit v1.2.1 From d0303ebcf59e6498e2e42bd0edcd56b932441cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 15 Feb 2012 16:28:20 +0100 Subject: Fix parsing of build_ext --libraries option (#1326113) --- command/build_ext.py | 3 +-- tests/test_build_ext.py | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 55a4288a..923197ba 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -160,8 +160,7 @@ class build_ext (Command): if plat_py_include != py_include: self.include_dirs.append(plat_py_include) - if isinstance(self.libraries, str): - self.libraries = [self.libraries] + self.ensure_string_list('libraries') # Life is easier if we're not forever checking for None, so # simplify these options to empty lists if unset diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 2fa63d30..198cce32 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -150,21 +150,21 @@ class BuildExtTestCase(support.TempdirManager, # make sure cmd.libraries is turned into a list # if it's a string cmd = build_ext(dist) - cmd.libraries = 'my_lib' + cmd.libraries = 'my_lib, other_lib lastlib' cmd.finalize_options() - self.assertEqual(cmd.libraries, ['my_lib']) + self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib']) # make sure cmd.library_dirs is turned into a list # if it's a string cmd = build_ext(dist) - cmd.library_dirs = 'my_lib_dir' + cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep cmd.finalize_options() - self.assertTrue('my_lib_dir' in cmd.library_dirs) + self.assertEqual(cmd.library_dirs, ['my_lib_dir', 'other_lib_dir']) # make sure rpath is turned into a list - # if it's a list of os.pathsep's paths + # if it's a string cmd = build_ext(dist) - cmd.rpath = os.pathsep.join(['one', 'two']) + cmd.rpath = 'one%stwo' % os.pathsep cmd.finalize_options() self.assertEqual(cmd.rpath, ['one', 'two']) -- cgit v1.2.1 From 366ee8997644ce4f83c440bcd7e08ae00cf29ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 15 Feb 2012 16:44:37 +0100 Subject: Fix parsing of build_ext --libraries option (#1326113) --- command/build_ext.py | 3 +-- tests/test_build_ext.py | 12 ++++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 8d843d68..34b61bdb 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -165,8 +165,7 @@ class build_ext(Command): if plat_py_include != py_include: self.include_dirs.append(plat_py_include) - if isinstance(self.libraries, str): - self.libraries = [self.libraries] + self.ensure_string_list('libraries') # Life is easier if we're not forever checking for None, so # simplify these options to empty lists if unset diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 18274376..87cceee2 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -178,21 +178,21 @@ class BuildExtTestCase(TempdirManager, # make sure cmd.libraries is turned into a list # if it's a string cmd = build_ext(dist) - cmd.libraries = 'my_lib' + cmd.libraries = 'my_lib, other_lib lastlib' cmd.finalize_options() - self.assertEqual(cmd.libraries, ['my_lib']) + self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib']) # make sure cmd.library_dirs is turned into a list # if it's a string cmd = build_ext(dist) - cmd.library_dirs = 'my_lib_dir' + cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep cmd.finalize_options() - self.assertTrue('my_lib_dir' in cmd.library_dirs) + self.assertEqual(cmd.library_dirs, ['my_lib_dir', 'other_lib_dir']) # make sure rpath is turned into a list - # if it's a list of os.pathsep's paths + # if it's a string cmd = build_ext(dist) - cmd.rpath = os.pathsep.join(['one', 'two']) + cmd.rpath = 'one%stwo' % os.pathsep cmd.finalize_options() self.assertEqual(cmd.rpath, ['one', 'two']) -- cgit v1.2.1 From 9793abc54257ffe60b3c46e2b4a1f4ee9ef5eb98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 15 Feb 2012 18:12:12 +0100 Subject: Fix test failure for shared builds caused by #1326113 fix --- tests/test_build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 87cceee2..090eacfb 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -187,7 +187,8 @@ class BuildExtTestCase(TempdirManager, cmd = build_ext(dist) cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep cmd.finalize_options() - self.assertEqual(cmd.library_dirs, ['my_lib_dir', 'other_lib_dir']) + self.assertIn('my_lib_dir', cmd.library_dirs) + self.assertIn('other_lib_dir', cmd.library_dirs) # make sure rpath is turned into a list # if it's a string -- cgit v1.2.1 From 7348d1f553ead879a48850adf8bd17772996ec7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 15 Feb 2012 18:13:45 +0100 Subject: Fix test failure for shared builds caused by #1326113 fix --- tests/test_build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 198cce32..b71cc983 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -159,7 +159,8 @@ class BuildExtTestCase(support.TempdirManager, cmd = build_ext(dist) cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep cmd.finalize_options() - self.assertEqual(cmd.library_dirs, ['my_lib_dir', 'other_lib_dir']) + self.assertIn('my_lib_dir', cmd.library_dirs) + self.assertIn('other_lib_dir', cmd.library_dirs) # make sure rpath is turned into a list # if it's a string -- cgit v1.2.1 From 8b33fb69465d080b685d431e4ea0b8aeb17e84b5 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 23 Feb 2012 10:45:48 -0500 Subject: version now 3.1.5rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a3e29936..0e427624 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.4" +__version__ = "3.1.5rc1" #--end constants-- -- cgit v1.2.1 From eb324661c378f36df356215c350772111a978ef4 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 23 Feb 2012 10:52:17 -0500 Subject: bump to 2.7.3rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a849f1a9..83126f67 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.2" +__version__ = "2.7.3rc1" #--end constants-- -- cgit v1.2.1 From 2181299388e93cb98ddd887f4bf9fdd0c6caeeb5 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 23 Feb 2012 10:55:57 -0500 Subject: Bump to version 2.6.8rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index beb65bb5..279e17a9 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.7" +__version__ = "2.6.8rc1" #--end constants-- -- cgit v1.2.1 From 500a248b355d0e418e8fc58f787ba47cab97c2eb Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 23 Feb 2012 21:14:12 +0100 Subject: Bump version to 3.2.3rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 9ec61658..36975055 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.2" +__version__ = "3.2.3rc1" #--end constants-- -- cgit v1.2.1 From 9db46d430b09baf00228adeb5a4773a02508f7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 25 Feb 2012 16:13:53 +0100 Subject: Fix long-standing bugs with MANIFEST.in parsing on Windows (#6884). These regex changes fix a number of issues for distutils on Windows: - #6884: impossible to include a file starting with 'build' - #9691 and #14004: sdist includes too many files - #13193: test_filelist failures This commit replaces the incorrect changes done in 557a973709de, c566a3447ba1 and 3925081a7ca0 to fix #13193; we were too eager to fix the test failures and I did not study the code enough before greenlighting patches. This time we have unit tests from the problems reported by users to be sure we have the right fix. Thanks to Nadeem Vawda for his help. --- filelist.py | 20 ++++++--- tests/test_filelist.py | 113 +++++++++++++++++++++++++++++++------------------ tests/test_sdist.py | 14 ++++-- 3 files changed, 95 insertions(+), 52 deletions(-) diff --git a/filelist.py b/filelist.py index 5540b39a..2f1c457e 100644 --- a/filelist.py +++ b/filelist.py @@ -210,6 +210,7 @@ class FileList: Return 1 if files are found. """ + # XXX docstring lying about what the special chars are? files_found = 0 pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) self.debug_print("include_pattern: applying regex r'%s'" % @@ -297,11 +298,14 @@ def glob_to_re(pattern): # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, # and by extension they shouldn't match such "special characters" under # any OS. So change all non-escaped dots in the RE to match any - # character except the special characters. - # XXX currently the "special characters" are just slash -- i.e. this is - # Unix-only. - pattern_re = re.sub(r'((? Date: Sat, 25 Feb 2012 16:13:53 +0100 Subject: Fix long-standing bugs with MANIFEST.in parsing on Windows (#6884). These regex changes fix a number of issues for distutils on Windows: - #6884: impossible to include a file starting with 'build' - #9691 and #14004: sdist includes too many files - #13193: test_filelist failures This commit replaces the incorrect changes done in 557a973709de, c566a3447ba1 and 3925081a7ca0 to fix #13193; we were too eager to fix the test failures and I did not study the code enough before greenlighting patches. This time we have unit tests from the problems reported by users to be sure we have the right fix. Thanks to Nadeem Vawda for his help. --- filelist.py | 20 ++++++--- tests/test_filelist.py | 113 +++++++++++++++++++++++++++++++------------------ tests/test_sdist.py | 14 ++++-- 3 files changed, 95 insertions(+), 52 deletions(-) diff --git a/filelist.py b/filelist.py index 5540b39a..2f1c457e 100644 --- a/filelist.py +++ b/filelist.py @@ -210,6 +210,7 @@ class FileList: Return 1 if files are found. """ + # XXX docstring lying about what the special chars are? files_found = 0 pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) self.debug_print("include_pattern: applying regex r'%s'" % @@ -297,11 +298,14 @@ def glob_to_re(pattern): # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, # and by extension they shouldn't match such "special characters" under # any OS. So change all non-escaped dots in the RE to match any - # character except the special characters. - # XXX currently the "special characters" are just slash -- i.e. this is - # Unix-only. - pattern_re = re.sub(r'((? Date: Sat, 25 Feb 2012 16:28:05 +0100 Subject: Fix long-standing bugs with MANIFEST.in parsing on Windows (#6884). These regex changes fix a number of issues for distutils on Windows: - #6884: impossible to include a file starting with 'build' - #9691 and #14004: sdist includes too many files - #13193: test_filelist failures This commit replaces the incorrect changes done in 0a94e2f807c7 and 90b30d62caf2 to fix #13193; we were too eager to fix the test failures and I did not study the code enough before greenlighting patches. This time we have unit tests from the problems reported by users to be sure we have the right fix. Thanks to Nadeem Vawda for his help. --- filelist.py | 22 ++++++--- tests/test_filelist.py | 130 +++++++++++++++++++++++++++++++++++++------------ tests/test_sdist.py | 27 ++++++---- 3 files changed, 131 insertions(+), 48 deletions(-) diff --git a/filelist.py b/filelist.py index 91220321..db3f7a96 100644 --- a/filelist.py +++ b/filelist.py @@ -201,6 +201,7 @@ class FileList: Return True if files are found, False otherwise. """ + # XXX docstring lying about what the special chars are? files_found = False pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) self.debug_print("include_pattern: applying regex r'%s'" % @@ -284,11 +285,14 @@ def glob_to_re(pattern): # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, # and by extension they shouldn't match such "special characters" under # any OS. So change all non-escaped dots in the RE to match any - # character except the special characters. - # XXX currently the "special characters" are just slash -- i.e. this is - # Unix-only. - pattern_re = re.sub(r'((? Date: Sun, 26 Feb 2012 01:16:47 +0100 Subject: Stop ignoring RPMs in distutils' upload command (#2945). Bug reported by Hartmut Goebel and patch contributed by Carl Robben. Untested backport of the fix committed and tested for 3.2. --- command/bdist_rpm.py | 12 ++++++++++++ tests/test_bdist_rpm.py | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 0bba3635..59582436 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -379,16 +379,28 @@ class bdist_rpm (Command): self.spawn(rpm_cmd) if not self.dry_run: + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + if not self.binary_only: srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) assert(os.path.exists(srpm)) self.move_file(srpm, self.dist_dir) + filename = os.path.join(self.dist_dir, source_rpm) + self.distribution.dist_files.append( + ('bdist_rpm', pyversion, filename)) if not self.source_only: for rpm in binary_rpms: rpm = os.path.join(rpm_dir['RPMS'], rpm) if os.path.exists(rpm): self.move_file(rpm, self.dist_dir) + filename = os.path.join(self.dist_dir, + os.path.basename(rpm)) + self.distribution.dist_files.append( + ('bdist_rpm', pyversion, filename)) # run() def _dist_path(self, path): diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 25a5763a..37d89155 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -79,6 +79,10 @@ class BuildRpmTestCase(support.TempdirManager, dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) + # bug #2945: upload ignores bdist_rpm files + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) + def test_no_optimize_flag(self): # XXX I am unable yet to make this test work without @@ -118,6 +122,11 @@ class BuildRpmTestCase(support.TempdirManager, dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) + + # bug #2945: upload ignores bdist_rpm files + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) + os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) def test_suite(): -- cgit v1.2.1 From 760bf348a4913dfe637a06c87ca3e32e7456ddb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 26 Feb 2012 01:53:53 +0100 Subject: Synchronize some distutils tests with 3.2. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Actually check the contents of the file created by bdist_dumb. - Don’t use “RECORD†as filename for non-PEP 376 record file - Don’t start method name with “_testâ€, it looks like a disabled test method instead of an helper method - Fix some idioms (assertIn, addCleanup) --- tests/test_bdist_dumb.py | 24 ++++++++++++++++-------- tests/test_install.py | 35 +++++++++++++++++------------------ tests/test_sdist.py | 10 +++++----- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 5a22a10e..3378f49e 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -1,8 +1,10 @@ """Tests for distutils.command.bdist_dumb.""" -import unittest -import sys import os +import sys +import zipfile +import unittest +from test.test_support import run_unittest # zlib is not used here, but if it's not available # test_simple_built will fail @@ -11,8 +13,6 @@ try: except ImportError: zlib = None -from test.test_support import run_unittest - from distutils.core import Distribution from distutils.command.bdist_dumb import bdist_dumb from distutils.tests import support @@ -73,15 +73,23 @@ class BuildDumbTestCase(support.TempdirManager, # see what we have dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - base = "%s.%s" % (dist.get_fullname(), cmd.plat_name) + base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name) if os.name == 'os2': base = base.replace(':', '-') - wanted = ['%s.zip' % base] - self.assertEqual(dist_created, wanted) + self.assertEqual(dist_created, [base]) # now let's check what we have in the zip file - # XXX to be done + fp = zipfile.ZipFile(os.path.join('dist', base)) + try: + contents = fp.namelist() + finally: + fp.close() + + contents = sorted(os.path.basename(fn) for fn in contents) + wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], + 'foo.py', 'foo.pyc'] + self.assertEqual(contents, sorted(wanted)) def test_finalize_options(self): pkg_dir, dist = self.create_dist() diff --git a/tests/test_install.py b/tests/test_install.py index ebfb04f4..f17baa1e 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -86,19 +86,17 @@ class InstallTestCase(support.TempdirManager, self.old_expand = os.path.expanduser os.path.expanduser = _expanduser - try: - # this is the actual test - self._test_user_site() - finally: + def cleanup(): site.USER_BASE = self.old_user_base site.USER_SITE = self.old_user_site install_module.USER_BASE = self.old_user_base install_module.USER_SITE = self.old_user_site os.path.expanduser = self.old_expand - def _test_user_site(self): + self.addCleanup(cleanup) + for key in ('nt_user', 'unix_user', 'os2_home'): - self.assertTrue(key in INSTALL_SCHEMES) + self.assertIn(key, INSTALL_SCHEMES) dist = Distribution({'name': 'xx'}) cmd = install(dist) @@ -106,14 +104,14 @@ class InstallTestCase(support.TempdirManager, # making sure the user option is there options = [name for name, short, lable in cmd.user_options] - self.assertTrue('user' in options) + self.assertIn('user', options) # setting a value cmd.user = 1 # user base and site shouldn't be created yet - self.assertTrue(not os.path.exists(self.user_base)) - self.assertTrue(not os.path.exists(self.user_site)) + self.assertFalse(os.path.exists(self.user_base)) + self.assertFalse(os.path.exists(self.user_site)) # let's run finalize cmd.ensure_finalized() @@ -122,8 +120,8 @@ class InstallTestCase(support.TempdirManager, self.assertTrue(os.path.exists(self.user_base)) self.assertTrue(os.path.exists(self.user_site)) - self.assertTrue('userbase' in cmd.config_vars) - self.assertTrue('usersite' in cmd.config_vars) + self.assertIn('userbase', cmd.config_vars) + self.assertIn('usersite', cmd.config_vars) def test_handle_extra_path(self): dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) @@ -176,15 +174,16 @@ class InstallTestCase(support.TempdirManager, def test_record(self): install_dir = self.mkdtemp() - project_dir, dist = self.create_dist(scripts=['hello']) - self.addCleanup(os.chdir, os.getcwd()) + project_dir, dist = self.create_dist(py_modules=['hello'], + scripts=['sayhi']) os.chdir(project_dir) - self.write_file('hello', "print('o hai')") + self.write_file('hello.py', "def main(): print 'o hai'") + self.write_file('sayhi', 'from hello import main; main()') cmd = install(dist) dist.command_obj['install'] = cmd cmd.root = install_dir - cmd.record = os.path.join(project_dir, 'RECORD') + cmd.record = os.path.join(project_dir, 'filelist') cmd.ensure_finalized() cmd.run() @@ -195,7 +194,7 @@ class InstallTestCase(support.TempdirManager, f.close() found = [os.path.basename(line) for line in content.splitlines()] - expected = ['hello', + expected = ['hello.py', 'hello.pyc', 'sayhi', 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) @@ -203,7 +202,6 @@ class InstallTestCase(support.TempdirManager, install_dir = self.mkdtemp() project_dir, dist = self.create_dist(ext_modules=[ Extension('xx', ['xxmodule.c'])]) - self.addCleanup(os.chdir, os.getcwd()) os.chdir(project_dir) support.copy_xxmodule_c(project_dir) @@ -215,7 +213,7 @@ class InstallTestCase(support.TempdirManager, dist.command_obj['install'] = cmd dist.command_obj['build_ext'] = buildextcmd cmd.root = install_dir - cmd.record = os.path.join(project_dir, 'RECORD') + cmd.record = os.path.join(project_dir, 'filelist') cmd.ensure_finalized() cmd.run() @@ -241,6 +239,7 @@ class InstallTestCase(support.TempdirManager, install_module.DEBUG = False self.assertTrue(len(self.logs) > old_logs_len) + def test_suite(): return unittest.makeSuite(InstallTestCase) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 9e422fca..3a89f73f 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -6,6 +6,7 @@ import warnings import zipfile from os.path import join from textwrap import dedent +from test.test_support import captured_stdout, check_warnings, run_unittest # zlib is not used here, but if it's not available # the tests that use zipfile may fail @@ -21,7 +22,6 @@ try: except ImportError: UID_GID_SUPPORT = False -from test.test_support import captured_stdout, check_warnings, run_unittest from distutils.command.sdist import sdist, show_formats from distutils.core import Distribution @@ -375,7 +375,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # the following tests make sure there is a nice error message instead # of a traceback when parsing an invalid manifest template - def _test_template(self, content): + def _check_template(self, content): dist, cmd = self.get_cmd() os.chdir(self.tmp_dir) self.write_file('MANIFEST.in', content) @@ -386,17 +386,17 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(len(warnings), 1) def test_invalid_template_unknown_command(self): - self._test_template('taunt knights *') + self._check_template('taunt knights *') def test_invalid_template_wrong_arguments(self): # this manifest command takes one argument - self._test_template('prune') + self._check_template('prune') @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only') def test_invalid_template_wrong_path(self): # on Windows, trailing slashes are not allowed # this used to crash instead of raising a warning: #8286 - self._test_template('include examples/') + self._check_template('include examples/') @unittest.skipUnless(zlib, "requires zlib") def test_get_file_list(self): -- cgit v1.2.1 From 6e5272fa5cd907e730fabff4b6a7aaf4e6244167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 26 Feb 2012 02:14:33 +0100 Subject: Set archive format explicitly in one distutils test --- tests/test_sdist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index fd71dac8..1ba2a1a6 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -6,6 +6,7 @@ import warnings import zipfile from os.path import join from textwrap import dedent +from test.support import captured_stdout, check_warnings, run_unittest try: import zlib @@ -13,7 +14,6 @@ try: except ImportError: ZLIB_SUPPORT = False -from test.support import captured_stdout, check_warnings, run_unittest from distutils.command.sdist import sdist, show_formats from distutils.core import Distribution @@ -326,6 +326,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # filling data_files by pointing files in package_data dist.package_data = {'somecode': ['*.txt']} self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + cmd.formats = ['gztar'] cmd.ensure_finalized() cmd.run() -- cgit v1.2.1 From a38d0a2e44526c9a32aceca07fd7d09ac100a62f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 4 Mar 2012 16:23:53 +0100 Subject: Bump to 3.3.0a1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f883916f..8b4261aa 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3a0" +__version__ = "3.3.0a1" #--end constants-- -- cgit v1.2.1 From 3cbf2176b6315a9307698ff77fb0bd8afc6aa46a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Mon, 5 Mar 2012 16:09:29 +0100 Subject: =?UTF-8?q?Make=20distutils=E2=80=99=20upload=20command=20work=20w?= =?UTF-8?q?ith=20bdist=5Fmsi=20products=20(#13719).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch by Ralf Schmitt. --- command/bdist_msi.py | 2 +- tests/test_bdist_msi.py | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index b3cfe9ce..fde0f63f 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -260,7 +260,7 @@ class bdist_msi(Command): self.db.Commit() if hasattr(self.distribution, 'dist_files'): - tup = 'bdist_msi', self.target_version or 'any', fullname + tup = 'bdist_msi', self.target_version or 'any', installer_name self.distribution.dist_files.append(tup) if not self.keep_temp: diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index 9308c79d..9cbfb0c5 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -1,12 +1,11 @@ """Tests for distutils.command.bdist_msi.""" -import unittest import sys - +import unittest from test.support import run_unittest - from distutils.tests import support -@unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + +@unittest.skipUnless(sys.platform == 'win32', 'these tests require Windows') class BDistMSITestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -14,9 +13,18 @@ class BDistMSITestCase(support.TempdirManager, def test_minimal(self): # minimal test XXX need more tests from distutils.command.bdist_msi import bdist_msi - pkg_pth, dist = self.create_dist() + project_dir, dist = self.create_dist() cmd = bdist_msi(dist) cmd.ensure_finalized() + cmd.run() + + bdists = os.listdir(os.path.join(project_dir, 'dist')) + self.assertEqual(bdists, ['foo-0.1.msi']) + + # bug #13719: upload ignores bdist_msi files + self.assertEqual(dist.dist_files, + [('bdist_msi', 'any', 'dist/foo-0.1.msi')]) + def test_suite(): return unittest.makeSuite(BDistMSITestCase) -- cgit v1.2.1 From 02b07135e60f45c28414eec6cae34040d20c053c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Mon, 5 Mar 2012 16:47:33 +0100 Subject: =?UTF-8?q?Make=20distutils=E2=80=99=20upload=20command=20work=20w?= =?UTF-8?q?ith=20bdist=5Fmsi=20products=20(#13719).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch by Ralf Schmitt. --- command/bdist_msi.py | 2 +- tests/test_bdist_msi.py | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 703f873b..09c6a898 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -262,7 +262,7 @@ class bdist_msi (Command): self.db.Commit() if hasattr(self.distribution, 'dist_files'): - tup = 'bdist_msi', self.target_version or 'any', fullname + tup = 'bdist_msi', self.target_version or 'any', installer_name self.distribution.dist_files.append(tup) if not self.keep_temp: diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index 1c897ab0..f175ed44 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -1,12 +1,11 @@ """Tests for distutils.command.bdist_msi.""" -import unittest import sys - +import unittest from test.test_support import run_unittest - from distutils.tests import support -@unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + +@unittest.skipUnless(sys.platform == 'win32', 'these tests require Windows') class BDistMSITestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -14,9 +13,18 @@ class BDistMSITestCase(support.TempdirManager, def test_minimal(self): # minimal test XXX need more tests from distutils.command.bdist_msi import bdist_msi - pkg_pth, dist = self.create_dist() + project_dir, dist = self.create_dist() cmd = bdist_msi(dist) cmd.ensure_finalized() + cmd.run() + + bdists = os.listdir(os.path.join(project_dir, 'dist')) + self.assertEqual(bdists, ['foo-0.1.msi']) + + # bug #13719: upload ignores bdist_msi files + self.assertEqual(dist.dist_files, + [('bdist_msi', 'any', 'dist/foo-0.1.msi')]) + def test_suite(): return unittest.makeSuite(BDistMSITestCase) -- cgit v1.2.1 From e7f62f5444701fafb18b8135b7f1c4a10dd9626c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Mon, 5 Mar 2012 17:00:49 +0100 Subject: Fix NameError --- tests/test_bdist_msi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index f175ed44..82a20cf1 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -1,4 +1,5 @@ """Tests for distutils.command.bdist_msi.""" +import os import sys import unittest from test.test_support import run_unittest -- cgit v1.2.1 From 2bddf6fa66bff4c9886995c43f138c5cf29004ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Mon, 5 Mar 2012 17:02:31 +0100 Subject: Fix NameError from #13719 fix --- tests/test_bdist_msi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index 9cbfb0c5..e5994610 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -1,4 +1,5 @@ """Tests for distutils.command.bdist_msi.""" +import os import sys import unittest from test.support import run_unittest -- cgit v1.2.1 From a151dfbe766cda5b5d3aa1c1de39aff381788e28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 7 Mar 2012 20:48:55 +0100 Subject: Backout buggy patch committed for #13719 --- command/bdist_msi.py | 2 +- tests/test_bdist_msi.py | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index fde0f63f..b3cfe9ce 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -260,7 +260,7 @@ class bdist_msi(Command): self.db.Commit() if hasattr(self.distribution, 'dist_files'): - tup = 'bdist_msi', self.target_version or 'any', installer_name + tup = 'bdist_msi', self.target_version or 'any', fullname self.distribution.dist_files.append(tup) if not self.keep_temp: diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index e5994610..15d8bdff 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -1,5 +1,4 @@ """Tests for distutils.command.bdist_msi.""" -import os import sys import unittest from test.support import run_unittest @@ -17,14 +16,6 @@ class BDistMSITestCase(support.TempdirManager, project_dir, dist = self.create_dist() cmd = bdist_msi(dist) cmd.ensure_finalized() - cmd.run() - - bdists = os.listdir(os.path.join(project_dir, 'dist')) - self.assertEqual(bdists, ['foo-0.1.msi']) - - # bug #13719: upload ignores bdist_msi files - self.assertEqual(dist.dist_files, - [('bdist_msi', 'any', 'dist/foo-0.1.msi')]) def test_suite(): -- cgit v1.2.1 From e412535a28d474125d9ab9ce254485acd72916f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 7 Mar 2012 21:00:44 +0100 Subject: Backout buggy patch for #13719 --- command/bdist_msi.py | 2 +- tests/test_bdist_msi.py | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 09c6a898..703f873b 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -262,7 +262,7 @@ class bdist_msi (Command): self.db.Commit() if hasattr(self.distribution, 'dist_files'): - tup = 'bdist_msi', self.target_version or 'any', installer_name + tup = 'bdist_msi', self.target_version or 'any', fullname self.distribution.dist_files.append(tup) if not self.keep_temp: diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index 82a20cf1..f98b7a21 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -1,5 +1,4 @@ """Tests for distutils.command.bdist_msi.""" -import os import sys import unittest from test.test_support import run_unittest @@ -17,14 +16,6 @@ class BDistMSITestCase(support.TempdirManager, project_dir, dist = self.create_dist() cmd = bdist_msi(dist) cmd.ensure_finalized() - cmd.run() - - bdists = os.listdir(os.path.join(project_dir, 'dist')) - self.assertEqual(bdists, ['foo-0.1.msi']) - - # bug #13719: upload ignores bdist_msi files - self.assertEqual(dist.dist_files, - [('bdist_msi', 'any', 'dist/foo-0.1.msi')]) def test_suite(): -- cgit v1.2.1 From 52a047831e0c60eda6bafeed22ea1996f4d7f752 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 15 Mar 2012 12:25:54 -0500 Subject: bump to 2.7.3rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 83126f67..a137950e 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.3rc1" +__version__ = "2.7.3rc2" #--end constants-- -- cgit v1.2.1 From da495dc219c9185da495363c1bddabd93659b17f Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 15 Mar 2012 13:57:27 -0500 Subject: bump to 3.1.5rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 0e427624..6ab16625 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.5rc1" +__version__ = "3.1.5rc2" #--end constants-- -- cgit v1.2.1 From 525cc3106994d2dd8e87f1eb28c2faadf0032b64 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Sat, 17 Mar 2012 18:19:15 -0400 Subject: Bump to 2.6.8rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 279e17a9..141b377b 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.8rc1" +__version__ = "2.6.8rc2" #--end constants-- -- cgit v1.2.1 From e29e39376dd2de7a9aa9ae03a6b92409489a5856 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 18 Mar 2012 07:34:49 +0100 Subject: Bump to 3.2.3rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 36975055..d8d61c54 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.3rc1" +__version__ = "3.2.3rc2" #--end constants-- -- cgit v1.2.1 From ca25aeb8ebcee9808e9f2ead00586a1907a21f1b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 1 Apr 2012 13:49:21 +0200 Subject: Bump to 3.3.0a2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8b4261aa..661a8b6d 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0a1" +__version__ = "3.3.0a2" #--end constants-- -- cgit v1.2.1 From 54af65c686e1b04ce8d83643ff3232c9043ad335 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 6 Apr 2012 13:17:25 -0400 Subject: bump to 3.1.5 final --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6ab16625..d79bfa13 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.5rc2" +__version__ = "3.1.5" #--end constants-- -- cgit v1.2.1 From d4855653e3edb3801094088e0c2b1514b02b6b86 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 9 Apr 2012 19:04:04 -0400 Subject: bump to 2.7.3 final --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a137950e..036062cc 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.3rc2" +__version__ = "2.7.3" #--end constants-- -- cgit v1.2.1 From c2f6398480d1b115bb8436ba7b5fba0e64de251a Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 10 Apr 2012 10:59:35 -0400 Subject: Bump to 2.6.8 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 141b377b..e5008c97 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ __revision__ = "$Id$" # #--start constants-- -__version__ = "2.6.8rc2" +__version__ = "2.6.8" #--end constants-- -- cgit v1.2.1 From ad8c62a6235689d9f30f55dbb3a7547ba9961d89 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 10 Apr 2012 19:28:09 +0200 Subject: Bump to 3.2.3 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index d8d61c54..b52a9fe6 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.3rc2" +__version__ = "3.2.3" #--end constants-- -- cgit v1.2.1 From 912d0ff09025b61aec9937350e809ebeeaca61f8 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 1 May 2012 09:35:18 +0200 Subject: Bump to 3.3.0a3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 661a8b6d..d39d82ee 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0a2" +__version__ = "3.3.0a3" #--end constants-- -- cgit v1.2.1 From 12525d2ac00aeffb77cc20ffbc37f58fd086330a Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Sun, 13 May 2012 11:19:23 -0500 Subject: Fix #13210. Port the Windows build from VS2008 to VS2010. --- command/build_ext.py | 2 +- command/wininst-10.0-amd64.exe | Bin 0 -> 222208 bytes command/wininst-10.0.exe | Bin 0 -> 190976 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 command/wininst-10.0-amd64.exe create mode 100644 command/wininst-10.0.exe diff --git a/command/build_ext.py b/command/build_ext.py index 59d0cd2d..ac37c132 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -197,7 +197,7 @@ class build_ext(Command): # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) - if MSVC_VERSION == 9: + if MSVC_VERSION >= 9: # Use the .lib files for the correct architecture if self.plat_name == 'win32': suffix = '' diff --git a/command/wininst-10.0-amd64.exe b/command/wininst-10.0-amd64.exe new file mode 100644 index 00000000..6fa0dce1 Binary files /dev/null and b/command/wininst-10.0-amd64.exe differ diff --git a/command/wininst-10.0.exe b/command/wininst-10.0.exe new file mode 100644 index 00000000..afc3bc6c Binary files /dev/null and b/command/wininst-10.0.exe differ -- cgit v1.2.1 From 0863f8d2a11e722a31624503f028fca44eb19ee6 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 26 May 2012 03:45:29 +0100 Subject: Implemented PEP 405 (Python virtual environments). --- sysconfig.py | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 16902ca9..977962f7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -18,6 +18,8 @@ from .errors import DistutilsPlatformError # These are needed in a couple of spots, so just compute them once. PREFIX = os.path.normpath(sys.prefix) EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +BASE_PREFIX = os.path.normpath(sys.base_prefix) +BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) # Path to the base directory of the project. On Windows the binary may # live in project/PCBuild9. If we're dealing with an x64 Windows build, @@ -39,11 +41,18 @@ if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): # different (hard-wired) directories. # Setup.local is available for Makefile builds including VPATH builds, # Setup.dist is available on Windows -def _python_build(): +def _is_python_source_dir(d): for fn in ("Setup.dist", "Setup.local"): - if os.path.isfile(os.path.join(project_base, "Modules", fn)): + if os.path.isfile(os.path.join(d, "Modules", fn)): return True return False +_sys_home = getattr(sys, '_home', None) +if _sys_home and os.name == 'nt' and _sys_home.lower().endswith('pcbuild'): + _sys_home = os.path.dirname(_sys_home) +def _python_build(): + if _sys_home: + return _is_python_source_dir(_sys_home) + return _is_python_source_dir(project_base) python_build = _python_build() # Calculate the build qualifier flags if they are defined. Adding the flags @@ -74,11 +83,11 @@ def get_python_inc(plat_specific=0, prefix=None): otherwise, this is the path to platform-specific header files (namely pyconfig.h). - If 'prefix' is supplied, use it instead of sys.prefix or - sys.exec_prefix -- i.e., ignore 'plat_specific'. + If 'prefix' is supplied, use it instead of sys.base_prefix or + sys.base_exec_prefix -- i.e., ignore 'plat_specific'. """ if prefix is None: - prefix = plat_specific and EXEC_PREFIX or PREFIX + prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX if os.name == "posix": if python_build: # Assume the executable is in the build directory. The @@ -86,11 +95,12 @@ def get_python_inc(plat_specific=0, prefix=None): # the build directory may not be the source directory, we # must use "srcdir" from the makefile to find the "Include" # directory. - base = os.path.dirname(os.path.abspath(sys.executable)) + base = _sys_home or os.path.dirname(os.path.abspath(sys.executable)) if plat_specific: return base else: - incdir = os.path.join(get_config_var('srcdir'), 'Include') + incdir = os.path.join(_sys_home or get_config_var('srcdir'), + 'Include') return os.path.normpath(incdir) python_dir = 'python' + get_python_version() + build_flags return os.path.join(prefix, "include", python_dir) @@ -115,11 +125,14 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): containing standard Python library modules; otherwise, return the directory for site-specific modules. - If 'prefix' is supplied, use it instead of sys.prefix or - sys.exec_prefix -- i.e., ignore 'plat_specific'. + If 'prefix' is supplied, use it instead of sys.base_prefix or + sys.base_exec_prefix -- i.e., ignore 'plat_specific'. """ if prefix is None: - prefix = plat_specific and EXEC_PREFIX or PREFIX + if standard_lib: + prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX + else: + prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": libpython = os.path.join(prefix, @@ -232,9 +245,9 @@ def get_config_h_filename(): """Return full pathname of installed pyconfig.h file.""" if python_build: if os.name == "nt": - inc_dir = os.path.join(project_base, "PC") + inc_dir = os.path.join(_sys_home or project_base, "PC") else: - inc_dir = project_base + inc_dir = _sys_home or project_base else: inc_dir = get_python_inc(plat_specific=1) if get_python_version() < '2.2': @@ -248,7 +261,8 @@ def get_config_h_filename(): def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: - return os.path.join(os.path.dirname(sys.executable), "Makefile") + return os.path.join(_sys_home or os.path.dirname(sys.executable), + "Makefile") lib_dir = get_python_lib(plat_specific=0, standard_lib=1) config_file = 'config-{}{}'.format(get_python_version(), build_flags) return os.path.join(lib_dir, config_file, 'Makefile') -- cgit v1.2.1 From 0c18116bf577816e509ed9d150d36a5a9eab36f3 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 26 May 2012 20:36:12 +0100 Subject: Addressed some buildbot errors and comments on the checkin by Antoine on python-dev. --- sysconfig.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 977962f7..59315f72 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -47,7 +47,8 @@ def _is_python_source_dir(d): return True return False _sys_home = getattr(sys, '_home', None) -if _sys_home and os.name == 'nt' and _sys_home.lower().endswith('pcbuild'): +if _sys_home and os.name == 'nt' and \ + _sys_home.lower().endswith(('pcbuild', 'pcbuild\\amd64')): _sys_home = os.path.dirname(_sys_home) def _python_build(): if _sys_home: -- cgit v1.2.1 From da98f1c9ec9114743a3cacfad74a5ec37ac2281e Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sun, 27 May 2012 17:30:09 +0100 Subject: Fixed _sys_home computation and added diagnostics for Windows buildbot failures. --- sysconfig.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index 59315f72..e86cb232 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -50,6 +50,8 @@ _sys_home = getattr(sys, '_home', None) if _sys_home and os.name == 'nt' and \ _sys_home.lower().endswith(('pcbuild', 'pcbuild\\amd64')): _sys_home = os.path.dirname(_sys_home) + if _sys_home.endswith('pcbuild'): # must be amd64 + _sys_home = os.path.dirname(_sys_home) def _python_build(): if _sys_home: return _is_python_source_dir(_sys_home) -- cgit v1.2.1 From 54589c574b5f5acf9ed54dac1e1d0915cb5dc1f0 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Mon, 28 May 2012 22:34:46 +1000 Subject: Issue #14443: Tell rpmbuild to use the correct version of Python --- command/bdist_rpm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 357eaa57..401bc418 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -190,7 +190,7 @@ class bdist_rpm(Command): if self.fix_python: self.python = sys.executable else: - self.python = "python" + self.python = "python3" elif self.fix_python: raise DistutilsOptionError( "--python and --fix-python are mutually exclusive options") @@ -320,6 +320,7 @@ class bdist_rpm(Command): rpm_cmd.append('-bb') else: rpm_cmd.append('-ba') + rpm_cmd.extend(['--define', '__python %s' % self.python]) if self.rpm3_mode: rpm_cmd.extend(['--define', '_topdir %s' % os.path.abspath(self.rpm_base)]) -- cgit v1.2.1 From 844216fd4e4c528b8064a750ee0ab99f5e987894 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 30 May 2012 22:04:31 +0200 Subject: Bump version to 3.3.0a4. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index d39d82ee..0da0d849 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0a3" +__version__ = "3.3.0a4" #--end constants-- -- cgit v1.2.1 From e562c287d7ac8c6e0f1d3642db817f049a7a6dd6 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sat, 23 Jun 2012 16:02:19 -0700 Subject: Issue #13590: Improve support for OS X Xcode 4: - Try to avoid building Python or extension modules with problematic llvm-gcc compiler. - Since Xcode 4 removes ppc support, extension module builds now check for ppc compiler support and automatically remove ppc and ppc64 archs when not available. - Since Xcode 4 no longer install SDKs in default locations, extension module builds now revert to using installed headers and libs if the SDK used to build the interpreter is not available. - Update ./configure to use better defaults for universal builds; in particular, --enable-universalsdk=yes uses the Xcode default SDK and --with-universal-archs now defaults to "intel" if ppc not available. --- sysconfig.py | 147 ++++++++++++++++++++++++++++++++++++++++++------------- unixccompiler.py | 21 ++++++-- 2 files changed, 131 insertions(+), 37 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index e86cb232..dac3035f 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -162,7 +162,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): "I don't know where Python installs its library " "on platform '%s'" % os.name) -_USE_CLANG = None + def customize_compiler(compiler): """Do any platform-specific customization of a CCompiler instance. @@ -177,36 +177,7 @@ def customize_compiler(compiler): newcc = None if 'CC' in os.environ: - newcc = os.environ['CC'] - elif sys.platform == 'darwin' and cc == 'gcc-4.2': - # Issue #13590: - # Since Apple removed gcc-4.2 in Xcode 4.2, we can no - # longer assume it is available for extension module builds. - # If Python was built with gcc-4.2, check first to see if - # it is available on this system; if not, try to use clang - # instead unless the caller explicitly set CC. - global _USE_CLANG - if _USE_CLANG is None: - from distutils import log - from subprocess import Popen, PIPE - p = Popen("! type gcc-4.2 && type clang && exit 2", - shell=True, stdout=PIPE, stderr=PIPE) - p.wait() - if p.returncode == 2: - _USE_CLANG = True - log.warn("gcc-4.2 not found, using clang instead") - else: - _USE_CLANG = False - if _USE_CLANG: - newcc = 'clang' - if newcc: - # On OS X, if CC is overridden, use that as the default - # command for LDSHARED as well - if (sys.platform == 'darwin' - and 'LDSHARED' not in os.environ - and ldshared.startswith(cc)): - ldshared = newcc + ldshared[len(cc):] - cc = newcc + cc = os.environ['CC'] if 'CXX' in os.environ: cxx = os.environ['CXX'] if 'LDSHARED' in os.environ: @@ -522,6 +493,29 @@ def _init_os2(): _config_vars = g +def _read_output(commandstring): + """ + Returns os.popen(commandstring, "r").read(), but + without actually using os.popen because that + function is not usable during python bootstrap + """ + # NOTE: tempfile is also not useable during + # bootstrap + import contextlib + try: + import tempfile + fp = tempfile.NamedTemporaryFile() + except ImportError: + fp = open("/tmp/distutils.%s"%( + os.getpid(),), "w+b") + + with contextlib.closing(fp) as fp: + cmd = "%s >'%s'"%(commandstring, fp.name) + os.system(cmd) + data = fp.read() + + return data.decode('utf-8') + def get_config_vars(*args): """With no arguments, return a dictionary of all configuration variables relevant for the current platform. Generally this includes @@ -561,9 +555,70 @@ def get_config_vars(*args): _config_vars['srcdir'] = os.path.normpath(srcdir) if sys.platform == 'darwin': + from distutils.spawn import find_executable + kernel_version = os.uname()[2] # Kernel version (8.4.3) major_version = int(kernel_version.split('.')[0]) + # Issue #13590: + # The OSX location for the compiler varies between OSX + # (or rather Xcode) releases. With older releases (up-to 10.5) + # the compiler is in /usr/bin, with newer releases the compiler + # can only be found inside Xcode.app if the "Command Line Tools" + # are not installed. + # + # Futhermore, the compiler that can be used varies between + # Xcode releases. Upto Xcode 4 it was possible to use 'gcc-4.2' + # as the compiler, after that 'clang' should be used because + # gcc-4.2 is either not present, or a copy of 'llvm-gcc' that + # miscompiles Python. + + # skip checks if the compiler was overriden with a CC env variable + if 'CC' not in os.environ: + cc = oldcc = _config_vars['CC'] + if not find_executable(cc): + # Compiler is not found on the shell search PATH. + # Now search for clang, first on PATH (if the Command LIne + # Tools have been installed in / or if the user has provided + # another location via CC). If not found, try using xcrun + # to find an uninstalled clang (within a selected Xcode). + + # NOTE: Cannot use subprocess here because of bootstrap + # issues when building Python itself (and os.popen is + # implemented on top of subprocess and is therefore not + # usable as well) + + data = (find_executable('clang') or + _read_output( + "/usr/bin/xcrun -find clang 2>/dev/null").strip()) + if not data: + raise DistutilsPlatformError( + "Cannot locate working compiler") + + _config_vars['CC'] = cc = data + _config_vars['CXX'] = cc + '++' + + elif os.path.basename(cc).startswith('gcc'): + # Compiler is GCC, check if it is LLVM-GCC + data = _read_output("'%s' --version 2>/dev/null" + % (cc.replace("'", "'\"'\"'"),)) + if 'llvm-gcc' in data: + # Found LLVM-GCC, fall back to clang + data = (find_executable('clang') or + _read_output( + "/usr/bin/xcrun -find clang 2>/dev/null").strip()) + if find_executable(data): + _config_vars['CC'] = cc = data + _config_vars['CXX'] = cc + '++' + + if (cc != oldcc + and 'LDSHARED' in _config_vars + and 'LDSHARED' not in os.environ): + # modify LDSHARED if we modified CC + ldshared = _config_vars['LDSHARED'] + if ldshared.startswith(oldcc): + _config_vars['LDSHARED'] = cc + ldshared[len(oldcc):] + if major_version < 8: # On Mac OS X before 10.4, check if -arch and -isysroot # are in CFLAGS or LDFLAGS and remove them if they are. @@ -579,19 +634,45 @@ def get_config_vars(*args): _config_vars[key] = flags else: + # Different Xcode releases support different sets for '-arch' + # flags. In particular, Xcode 4.x no longer supports the + # PPC architectures. + # + # This code automatically removes '-arch ppc' and '-arch ppc64' + # when these are not supported. That makes it possible to + # build extensions on OSX 10.7 and later with the prebuilt + # 32-bit installer on the python.org website. + flags = _config_vars['CFLAGS'] + if re.search('-arch\s+ppc', flags) is not None: + # NOTE: Cannot use subprocess here because of bootstrap + # issues when building Python itself + status = os.system("'%s' -arch ppc -x c /dev/null 2>/dev/null"%( + _config_vars['CC'].replace("'", "'\"'\"'"),)) + + if status != 0: + # Compiler doesn't support PPC, remove the related + # '-arch' flags. + for key in ('LDFLAGS', 'BASECFLAGS', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED', 'LDSHARED'): + + flags = _config_vars[key] + flags = re.sub('-arch\s+ppc\w*\s', ' ', flags) + _config_vars[key] = flags + # Allow the user to override the architecture flags using # an environment variable. # NOTE: This name was introduced by Apple in OSX 10.5 and # is used by several scripting languages distributed with # that OS release. - if 'ARCHFLAGS' in os.environ: arch = os.environ['ARCHFLAGS'] for key in ('LDFLAGS', 'BASECFLAGS', # a number of derived variables. These need to be # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED', 'LDSHARED'): flags = _config_vars[key] flags = re.sub('-arch\s+\w+\s', ' ', flags) diff --git a/unixccompiler.py b/unixccompiler.py index c70a3cc5..5d45faa7 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -83,9 +83,8 @@ def _darwin_compiler_fixup(compiler_so, cc_args): except ValueError: pass - # Check if the SDK that is used during compilation actually exists, - # the universal build requires the usage of a universal SDK and not all - # users have that installed by default. + # Check if the SDK that is used during compilation actually exists. + # If not, revert to using the installed headers and hope for the best. sysroot = None if '-isysroot' in cc_args: idx = cc_args.index('-isysroot') @@ -97,7 +96,21 @@ def _darwin_compiler_fixup(compiler_so, cc_args): if sysroot and not os.path.isdir(sysroot): log.warn("Compiling with an SDK that doesn't seem to exist: %s", sysroot) - log.warn("Please check your Xcode installation") + log.warn("Attempting to compile without the SDK") + while True: + try: + index = cc_args.index('-isysroot') + # Strip this argument and the next one: + del cc_args[index:index+2] + except ValueError: + break + while True: + try: + index = compiler_so.index('-isysroot') + # Strip this argument and the next one: + del compiler_so[index:index+2] + except ValueError: + break return compiler_so -- cgit v1.2.1 From 4e6c91d7daf073463c531f79dbd4f4bf4b6898b7 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 25 Jun 2012 15:52:24 -0400 Subject: Issue #14443: ensure that brp-python-bytecompile is invoked with the correct python executable The __os_install_macro defines some post-processing activities during an rpm build; one of the scripts it calls is brp-python-bytecompile, which can take an argument: the python executable with which to byte-compile .py files in the package payload. In some older versions of rpm (e.g. in RHEL 6), this invocation doesn't pass in an argument, and brp-python-bytecompile defaults to using /usr/bin/python, which can lead to the .py files being byte-compiled for the wrong version of python. This has been fixed in later versions of rpm by passing in %{__python} as an argument to brp-python-bytecompile. Workaround this by detecting if __os_install_post has a 0-argument invocation of brp-python-bytecompile, and if so generating an equivalent macro that has the argument, and explicitly provide the new definition within the specfile. --- command/bdist_rpm.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 401bc418..ac462179 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -3,7 +3,7 @@ Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions).""" -import sys, os +import subprocess, sys, os from distutils.core import Command from distutils.debug import DEBUG from distutils.util import get_platform @@ -406,6 +406,21 @@ class bdist_rpm(Command): 'Summary: ' + self.distribution.get_description(), ] + # Workaround for #14443 which affects some RPM based systems such as + # RHEL6 (and probably derivatives) + vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}') + # Generate a potential replacement value for __os_install_post (whilst + # normalizing the whitespace to simplify the test for whether the + # invocation of brp-python-bytecompile passes in __python): + vendor_hook = '\n'.join([' %s \\' % line.strip() + for line in vendor_hook.splitlines()]) + problem = "brp-python-bytecompile \\\n" + fixed = "brp-python-bytecompile %{__python} \\\n" + fixed_hook = vendor_hook.replace(problem, fixed) + if fixed_hook != vendor_hook: + spec_file.append('# Workaround for http://bugs.python.org/issue14443') + spec_file.append('%define __os_install_post ' + fixed_hook + '\n') + # put locale summaries into spec file # XXX not supported for now (hard to put a dictionary # in a config file -- arg!) -- cgit v1.2.1 From 998406eedde8ea6686e42f77eb0e7c82486c874f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 26 Jun 2012 09:43:40 +0200 Subject: Bump version to 3.3.0b1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 0da0d849..7a7b8d6c 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0a4" +__version__ = "3.3.0b1" #--end constants-- -- cgit v1.2.1 From 2b4009c75e8ca5cb092424a21a53cbf2e231ea8d Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Tue, 26 Jun 2012 14:06:23 -0400 Subject: Issue #14443: ensure that brp-python-bytecompile is invoked with the correct python executable The __os_install_macro defines some post-processing activities during an rpm build; one of the scripts it calls is brp-python-bytecompile, which can take an argument: the python executable with which to byte-compile .py files in the package payload. In some older versions of rpm (e.g. in RHEL 6), this invocation doesn't pass in an argument, and brp-python-bytecompile defaults to using /usr/bin/python, which can lead to the .py files being byte-compiled for the wrong version of python. This has been fixed in later versions of rpm by passing in %{__python} as an argument to brp-python-bytecompile. Workaround this by detecting if __os_install_post has a 0-argument invocation of brp-python-bytecompile, and if so generating an equivalent macro that has the argument, and explicitly provide the new definition within the specfile. --- command/bdist_rpm.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 401bc418..ac462179 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -3,7 +3,7 @@ Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions).""" -import sys, os +import subprocess, sys, os from distutils.core import Command from distutils.debug import DEBUG from distutils.util import get_platform @@ -406,6 +406,21 @@ class bdist_rpm(Command): 'Summary: ' + self.distribution.get_description(), ] + # Workaround for #14443 which affects some RPM based systems such as + # RHEL6 (and probably derivatives) + vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}') + # Generate a potential replacement value for __os_install_post (whilst + # normalizing the whitespace to simplify the test for whether the + # invocation of brp-python-bytecompile passes in __python): + vendor_hook = '\n'.join([' %s \\' % line.strip() + for line in vendor_hook.splitlines()]) + problem = "brp-python-bytecompile \\\n" + fixed = "brp-python-bytecompile %{__python} \\\n" + fixed_hook = vendor_hook.replace(problem, fixed) + if fixed_hook != vendor_hook: + spec_file.append('# Workaround for http://bugs.python.org/issue14443') + spec_file.append('%define __os_install_post ' + fixed_hook + '\n') + # put locale summaries into spec file # XXX not supported for now (hard to put a dictionary # in a config file -- arg!) -- cgit v1.2.1 From 921b93fec344e0f75c80eff3cc8754215669f210 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 29 Jun 2012 01:05:26 +0200 Subject: Issue #10571: Fix the "--sign" option of distutils' upload command. Patch by Jakub Wilk. --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 4926aa3e..8b36851d 100644 --- a/command/upload.py +++ b/command/upload.py @@ -125,7 +125,7 @@ class upload(PyPIRCCommand): if self.sign: data['gpg_signature'] = (os.path.basename(filename) + ".asc", - open(filename+".asc").read()) + open(filename+".asc", "rb").read()) # set up the authentication user_pass = (self.username + ":" + self.password).encode('ascii') -- cgit v1.2.1 From f1f3ff37a4e88ffa90c3821bfc18db2009e1ed3b Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Sat, 30 Jun 2012 20:42:45 +0200 Subject: - Issue #14330: For cross builds, don't use host python, use host search paths for host compiler. --- util.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util.py b/util.py index bce84027..9833bf95 100644 --- a/util.py +++ b/util.py @@ -53,6 +53,10 @@ def get_platform (): return 'win-ia64' return sys.platform + # Set for cross builds explicitly + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_HOST_PLATFORM"] + if os.name != "posix" or not hasattr(os, 'uname'): # XXX what about the architecture? NT is Intel or Alpha, # Mac OS is M68k or PPC, etc. -- cgit v1.2.1 From 885b09228b2dc0062a31eede47183bd25983089b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 3 Jul 2012 01:12:42 -0400 Subject: Ignore .nfs* files in distutils (#7719). These files are created by some NFS clients a file is edited and removed concurrently (see added link in doc for more info). If such a file is removed between distutils calls listdir and copy, it will get confused. Other special files are ignored in sdist (namely VCS directories), but this has to be filtered out earlier. --- dir_util.py | 4 ++++ tests/test_dir_util.py | 18 ++++++++++++++++++ tests/test_sdist.py | 7 ++++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/dir_util.py b/dir_util.py index 9c5cf337..5026e246 100644 --- a/dir_util.py +++ b/dir_util.py @@ -144,6 +144,10 @@ def copy_tree(src, dst, preserve_mode=1, preserve_times=1, src_name = os.path.join(src, n) dst_name = os.path.join(dst, n) + if n.startswith('.nfs'): + # skip NFS rename files + continue + if preserve_symlinks and os.path.islink(src_name): link_dest = os.readlink(src_name) if verbose >= 1: diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 693f77cf..d82d9133 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -101,6 +101,24 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): remove_tree(self.root_target, verbose=0) remove_tree(self.target2, verbose=0) + def test_copy_tree_skips_nfs_temp_files(self): + mkpath(self.target, verbose=0) + + a_file = os.path.join(self.target, 'ok.txt') + nfs_file = os.path.join(self.target, '.nfs123abc') + for f in a_file, nfs_file: + fh = open(f, 'w') + try: + fh.write('some content') + finally: + fh.close() + + copy_tree(self.target, self.target2) + self.assertEqual(os.listdir(self.target2), ['ok.txt']) + + remove_tree(self.root_target, verbose=0) + remove_tree(self.target2, verbose=0) + def test_ensure_relative(self): if os.sep == '/': self.assertEqual(ensure_relative('/home/foo'), 'home/foo') diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 3a89f73f..7e7d98d0 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -91,9 +91,8 @@ class SDistTestCase(PyPIRCCommandTestCase): @unittest.skipUnless(zlib, "requires zlib") def test_prune_file_list(self): - # this test creates a package with some vcs dirs in it - # and launch sdist to make sure they get pruned - # on all systems + # this test creates a project with some VCS dirs and an NFS rename + # file, then launches sdist to check they get pruned on all systems # creating VCS directories with some files in them os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) @@ -107,6 +106,8 @@ class SDistTestCase(PyPIRCCommandTestCase): self.write_file((self.tmp_dir, 'somecode', '.git', 'ok'), 'xxx') + self.write_file((self.tmp_dir, 'somecode', '.nfs0001'), 'xxx') + # now building a sdist dist, cmd = self.get_cmd() -- cgit v1.2.1 From 3facc55019acde02d0454008292d6c81c9e1e8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Tue, 3 Jul 2012 01:23:46 -0400 Subject: Create ~/.pypirc securely (#13512). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There was a window between the write and the chmod where the user’s password would be exposed, depending on default permissions. Philip Jenvey’s patch fixes it. --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index afa403f2..9d8b30ea 100644 --- a/config.py +++ b/config.py @@ -42,7 +42,7 @@ class PyPIRCCommand(Command): def _store_pypirc(self, username, password): """Creates a default .pypirc file.""" rc = self._get_rc_file() - f = open(rc, 'w') + f = os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0600), 'w') try: f.write(DEFAULT_PYPIRC % (username, password)) finally: -- cgit v1.2.1 From b44cc1ee01fc6742048240e9a2e19a16d3ce41aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Tue, 10 Jul 2012 07:07:06 +0200 Subject: Issue #15315: Support VS 2010 in distutils cygwincompiler. --- cygwinccompiler.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 819e1a97..0bdd539c 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -78,6 +78,9 @@ def get_msvcr(): elif msc_ver == '1500': # VS2008 / MSVC 9.0 return ['msvcr90'] + elif msc_ver == '1600': + # VS2010 / MSVC 10.0 + return ['msvcr100'] else: raise ValueError("Unknown MS Compiler version %s " % msc_ver) -- cgit v1.2.1 From 14ab73a69d74f3ed8ba1dc8ce347271845899785 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sun, 15 Jul 2012 21:30:03 -0700 Subject: Issue #13590: Improve support for OS X Xcode 4: - fix test_distutils and test_sysconfig test failures by aligning sysconfig and distutils.sysconfig tailoring of configure variables (as in 2.7) --- sysconfig.py | 29 ++++++++++++++++++++++++++--- unixccompiler.py | 21 ++++----------------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index dac3035f..f6e5d999 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -624,7 +624,7 @@ def get_config_vars(*args): # are in CFLAGS or LDFLAGS and remove them if they are. # This is needed when building extensions on a 10.3 system # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', # a number of derived variables. These need to be # patched up as well. 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): @@ -669,16 +669,39 @@ def get_config_vars(*args): # that OS release. if 'ARCHFLAGS' in os.environ: arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', # a number of derived variables. These need to be # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED', 'LDSHARED'): + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): flags = _config_vars[key] flags = re.sub('-arch\s+\w+\s', ' ', flags) flags = flags + ' ' + arch _config_vars[key] = flags + # If we're on OSX 10.5 or later and the user tries to + # compiles an extension using an SDK that is not present + # on the current machine it is better to not use an SDK + # than to fail. + # + # The major usecase for this is users using a Python.org + # binary installer on OSX 10.6: that installer uses + # the 10.4u SDK, but that SDK is not installed by default + # when you install Xcode. + # + m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS']) + if m is not None: + sdk = m.group(1) + if not os.path.exists(sdk): + for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', + # a number of derived variables. These need to be + # patched up as well. + 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): + + flags = _config_vars[key] + flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) + _config_vars[key] = flags + if args: vals = [] for name in args: diff --git a/unixccompiler.py b/unixccompiler.py index 5d45faa7..c70a3cc5 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -83,8 +83,9 @@ def _darwin_compiler_fixup(compiler_so, cc_args): except ValueError: pass - # Check if the SDK that is used during compilation actually exists. - # If not, revert to using the installed headers and hope for the best. + # Check if the SDK that is used during compilation actually exists, + # the universal build requires the usage of a universal SDK and not all + # users have that installed by default. sysroot = None if '-isysroot' in cc_args: idx = cc_args.index('-isysroot') @@ -96,21 +97,7 @@ def _darwin_compiler_fixup(compiler_so, cc_args): if sysroot and not os.path.isdir(sysroot): log.warn("Compiling with an SDK that doesn't seem to exist: %s", sysroot) - log.warn("Attempting to compile without the SDK") - while True: - try: - index = cc_args.index('-isysroot') - # Strip this argument and the next one: - del cc_args[index:index+2] - except ValueError: - break - while True: - try: - index = compiler_so.index('-isysroot') - # Strip this argument and the next one: - del compiler_so[index:index+2] - except ValueError: - break + log.warn("Please check your Xcode installation") return compiler_so -- cgit v1.2.1 From cef0183bed891da1a3ec6e17083d5325141923b7 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 16 Jul 2012 18:24:55 +0100 Subject: Closes #15366: Corrected computation of include location for source builds. Thanks to Richard Oudkerk for the bug report and patch. --- sysconfig.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index f6e5d999..409ef74c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -101,10 +101,11 @@ def get_python_inc(plat_specific=0, prefix=None): base = _sys_home or os.path.dirname(os.path.abspath(sys.executable)) if plat_specific: return base + if _sys_home: + incdir = os.path.join(_sys_home, get_config_var('AST_H_DIR')) else: - incdir = os.path.join(_sys_home or get_config_var('srcdir'), - 'Include') - return os.path.normpath(incdir) + incdir = os.path.join(get_config_var('srcdir'), 'Include') + return os.path.normpath(incdir) python_dir = 'python' + get_python_version() + build_flags return os.path.join(prefix, "include", python_dir) elif os.name == "nt": -- cgit v1.2.1 From 7e4558cfe24f0fb019e741f5da76cfc6e7cdc40f Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 16 Jul 2012 18:30:03 +0100 Subject: Closes #15367: Corrected computation of include locations for source builds on Windows. Thanks to Richard Oudkerk for the bug report and patch. --- command/build_ext.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index ac37c132..f16e2f17 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -8,6 +8,7 @@ import sys, os, re from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version +from distutils.sysconfig import get_config_h_filename from distutils.dep_util import newer_group from distutils.extension import Extension from distutils.util import get_platform @@ -196,7 +197,10 @@ class build_ext(Command): # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree - self.include_dirs.append(os.path.join(sys.exec_prefix, 'PC')) + self.include_dirs.append(os.path.dirname(get_config_h_filename())) + _sys_home = getattr(sys, '_home', None) + if _sys_home: + self.library_dirs.append(_sys_home) if MSVC_VERSION >= 9: # Use the .lib files for the correct architecture if self.plat_name == 'win32': -- cgit v1.2.1 From 1f77c7ddcadb51accf01d3db76f23ec158a44256 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sat, 21 Jul 2012 05:36:30 -0700 Subject: Issue #15184: Ensure consistent results of OS X configuration tailoring for universal builds by factoring out common OS X-specific customizations from sysconfig, distutils.sysconfig, distutils.util, and distutils.unixccompiler into a new module _osx_support that can eventually also be used by packaging. --- sysconfig.py | 190 ++++++----------------------------------------------- tests/test_util.py | 9 +++ unixccompiler.py | 70 ++------------------ util.py | 92 ++------------------------ 4 files changed, 39 insertions(+), 322 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 409ef74c..910e1047 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -172,6 +172,21 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": + if sys.platform == "darwin": + # Perform first-time customization of compiler-related + # config vars on OS X now that we know we need a compiler. + # This is primarily to support Pythons from binary + # installers. The kind and paths to build tools on + # the user system may vary significantly from the system + # that Python itself was built on. Also the user OS + # version and build tools may not support the same set + # of CPU architectures for universal builds. + global _config_vars + if not _config_vars.get('CUSTOMIZED_OSX_COMPILER', ''): + import _osx_support + _osx_support.customize_compiler(_config_vars) + _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS') @@ -494,35 +509,12 @@ def _init_os2(): _config_vars = g -def _read_output(commandstring): - """ - Returns os.popen(commandstring, "r").read(), but - without actually using os.popen because that - function is not usable during python bootstrap - """ - # NOTE: tempfile is also not useable during - # bootstrap - import contextlib - try: - import tempfile - fp = tempfile.NamedTemporaryFile() - except ImportError: - fp = open("/tmp/distutils.%s"%( - os.getpid(),), "w+b") - - with contextlib.closing(fp) as fp: - cmd = "%s >'%s'"%(commandstring, fp.name) - os.system(cmd) - data = fp.read() - - return data.decode('utf-8') - def get_config_vars(*args): """With no arguments, return a dictionary of all configuration variables relevant for the current platform. Generally this includes everything needed to build extensions and install both pure modules and extensions. On Unix, this means every variable defined in Python's - installed Makefile; on Windows and Mac OS it's a much smaller set. + installed Makefile; on Windows it's a much smaller set. With arguments, return a list of values that result from looking up each argument in the configuration variable dictionary. @@ -555,153 +547,11 @@ def get_config_vars(*args): srcdir = os.path.join(base, _config_vars['srcdir']) _config_vars['srcdir'] = os.path.normpath(srcdir) + # OS X platforms require special customization to handle + # multi-architecture, multi-os-version installers if sys.platform == 'darwin': - from distutils.spawn import find_executable - - kernel_version = os.uname()[2] # Kernel version (8.4.3) - major_version = int(kernel_version.split('.')[0]) - - # Issue #13590: - # The OSX location for the compiler varies between OSX - # (or rather Xcode) releases. With older releases (up-to 10.5) - # the compiler is in /usr/bin, with newer releases the compiler - # can only be found inside Xcode.app if the "Command Line Tools" - # are not installed. - # - # Futhermore, the compiler that can be used varies between - # Xcode releases. Upto Xcode 4 it was possible to use 'gcc-4.2' - # as the compiler, after that 'clang' should be used because - # gcc-4.2 is either not present, or a copy of 'llvm-gcc' that - # miscompiles Python. - - # skip checks if the compiler was overriden with a CC env variable - if 'CC' not in os.environ: - cc = oldcc = _config_vars['CC'] - if not find_executable(cc): - # Compiler is not found on the shell search PATH. - # Now search for clang, first on PATH (if the Command LIne - # Tools have been installed in / or if the user has provided - # another location via CC). If not found, try using xcrun - # to find an uninstalled clang (within a selected Xcode). - - # NOTE: Cannot use subprocess here because of bootstrap - # issues when building Python itself (and os.popen is - # implemented on top of subprocess and is therefore not - # usable as well) - - data = (find_executable('clang') or - _read_output( - "/usr/bin/xcrun -find clang 2>/dev/null").strip()) - if not data: - raise DistutilsPlatformError( - "Cannot locate working compiler") - - _config_vars['CC'] = cc = data - _config_vars['CXX'] = cc + '++' - - elif os.path.basename(cc).startswith('gcc'): - # Compiler is GCC, check if it is LLVM-GCC - data = _read_output("'%s' --version 2>/dev/null" - % (cc.replace("'", "'\"'\"'"),)) - if 'llvm-gcc' in data: - # Found LLVM-GCC, fall back to clang - data = (find_executable('clang') or - _read_output( - "/usr/bin/xcrun -find clang 2>/dev/null").strip()) - if find_executable(data): - _config_vars['CC'] = cc = data - _config_vars['CXX'] = cc + '++' - - if (cc != oldcc - and 'LDSHARED' in _config_vars - and 'LDSHARED' not in os.environ): - # modify LDSHARED if we modified CC - ldshared = _config_vars['LDSHARED'] - if ldshared.startswith(oldcc): - _config_vars['LDSHARED'] = cc + ldshared[len(oldcc):] - - if major_version < 8: - # On Mac OS X before 10.4, check if -arch and -isysroot - # are in CFLAGS or LDFLAGS and remove them if they are. - # This is needed when building extensions on a 10.3 system - # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags, re.ASCII) - flags = re.sub('-isysroot [^ \t]*', ' ', flags) - _config_vars[key] = flags - - else: - # Different Xcode releases support different sets for '-arch' - # flags. In particular, Xcode 4.x no longer supports the - # PPC architectures. - # - # This code automatically removes '-arch ppc' and '-arch ppc64' - # when these are not supported. That makes it possible to - # build extensions on OSX 10.7 and later with the prebuilt - # 32-bit installer on the python.org website. - flags = _config_vars['CFLAGS'] - if re.search('-arch\s+ppc', flags) is not None: - # NOTE: Cannot use subprocess here because of bootstrap - # issues when building Python itself - status = os.system("'%s' -arch ppc -x c /dev/null 2>/dev/null"%( - _config_vars['CC'].replace("'", "'\"'\"'"),)) - - if status != 0: - # Compiler doesn't support PPC, remove the related - # '-arch' flags. - for key in ('LDFLAGS', 'BASECFLAGS', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED', 'LDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-arch\s+ppc\w*\s', ' ', flags) - _config_vars[key] = flags - - - # Allow the user to override the architecture flags using - # an environment variable. - # NOTE: This name was introduced by Apple in OSX 10.5 and - # is used by several scripting languages distributed with - # that OS release. - if 'ARCHFLAGS' in os.environ: - arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = flags + ' ' + arch - _config_vars[key] = flags - - # If we're on OSX 10.5 or later and the user tries to - # compiles an extension using an SDK that is not present - # on the current machine it is better to not use an SDK - # than to fail. - # - # The major usecase for this is users using a Python.org - # binary installer on OSX 10.6: that installer uses - # the 10.4u SDK, but that SDK is not installed by default - # when you install Xcode. - # - m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS']) - if m is not None: - sdk = m.group(1) - if not os.path.exists(sdk): - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) - _config_vars[key] = flags + import _osx_support + _osx_support.customize_config_vars(_config_vars) if args: vals = [] diff --git a/tests/test_util.py b/tests/test_util.py index 1a06d4c4..eac9b514 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -13,6 +13,7 @@ from distutils import util # used to patch _environ_checked from distutils.sysconfig import get_config_vars from distutils import sysconfig from distutils.tests import support +import _osx_support class UtilTestCase(support.EnvironGuard, unittest.TestCase): @@ -92,6 +93,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3' get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' @@ -105,6 +107,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sys.maxsize = cursize # macbook with fat binaries (fat, universal or fat64) + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4' get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' @@ -113,10 +116,12 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEqual(get_platform(), 'macosx-10.4-fat') + _osx_support._remove_original_values(get_config_vars()) os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1' self.assertEqual(get_platform(), 'macosx-10.4-fat') + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -124,18 +129,21 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEqual(get_platform(), 'macosx-10.4-intel') + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEqual(get_platform(), 'macosx-10.4-fat3') + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEqual(get_platform(), 'macosx-10.4-universal') + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -144,6 +152,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEqual(get_platform(), 'macosx-10.4-fat64') for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' diff --git a/unixccompiler.py b/unixccompiler.py index c70a3cc5..094a2f0b 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -23,6 +23,9 @@ from distutils.errors import \ DistutilsExecError, CompileError, LibError, LinkError from distutils import log +if sys.platform == 'darwin': + import _osx_support + # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's # Makefile and live with it. Is this adequate? If not, we might @@ -38,68 +41,6 @@ from distutils import log # should just happily stuff them into the preprocessor/compiler/linker # options and carry on. -def _darwin_compiler_fixup(compiler_so, cc_args): - """ - This function will strip '-isysroot PATH' and '-arch ARCH' from the - compile flags if the user has specified one them in extra_compile_flags. - - This is needed because '-arch ARCH' adds another architecture to the - build, without a way to remove an architecture. Furthermore GCC will - barf if multiple '-isysroot' arguments are present. - """ - stripArch = stripSysroot = False - - compiler_so = list(compiler_so) - kernel_version = os.uname()[2] # 8.4.3 - major_version = int(kernel_version.split('.')[0]) - - if major_version < 8: - # OSX before 10.4.0, these don't support -arch and -isysroot at - # all. - stripArch = stripSysroot = True - else: - stripArch = '-arch' in cc_args - stripSysroot = '-isysroot' in cc_args - - if stripArch or 'ARCHFLAGS' in os.environ: - while True: - try: - index = compiler_so.index('-arch') - # Strip this argument and the next one: - del compiler_so[index:index+2] - except ValueError: - break - - if 'ARCHFLAGS' in os.environ and not stripArch: - # User specified different -arch flags in the environ, - # see also distutils.sysconfig - compiler_so = compiler_so + os.environ['ARCHFLAGS'].split() - - if stripSysroot: - try: - index = compiler_so.index('-isysroot') - # Strip this argument and the next one: - del compiler_so[index:index+2] - except ValueError: - pass - - # Check if the SDK that is used during compilation actually exists, - # the universal build requires the usage of a universal SDK and not all - # users have that installed by default. - sysroot = None - if '-isysroot' in cc_args: - idx = cc_args.index('-isysroot') - sysroot = cc_args[idx+1] - elif '-isysroot' in compiler_so: - idx = compiler_so.index('-isysroot') - sysroot = compiler_so[idx+1] - - if sysroot and not os.path.isdir(sysroot): - log.warn("Compiling with an SDK that doesn't seem to exist: %s", - sysroot) - log.warn("Please check your Xcode installation") - - return compiler_so class UnixCCompiler(CCompiler): @@ -168,7 +109,8 @@ class UnixCCompiler(CCompiler): def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): compiler_so = self.compiler_so if sys.platform == 'darwin': - compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs) + compiler_so = _osx_support.compiler_fixup(compiler_so, + cc_args + extra_postargs) try: self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) @@ -247,7 +189,7 @@ class UnixCCompiler(CCompiler): linker[i] = self.compiler_cxx[i] if sys.platform == 'darwin': - linker = _darwin_compiler_fixup(linker, ld_args) + linker = _osx_support.compiler_fixup(linker, ld_args) self.spawn(linker + ld_args) except DistutilsExecError as msg: diff --git a/util.py b/util.py index 9833bf95..67d81663 100644 --- a/util.py +++ b/util.py @@ -98,94 +98,10 @@ def get_platform (): if m: release = m.group() elif osname[:6] == "darwin": - # - # For our purposes, we'll assume that the system version from - # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set - # to. This makes the compatibility story a bit more sane because the - # machine is going to compile and link as if it were - # MACOSX_DEPLOYMENT_TARGET. - from distutils.sysconfig import get_config_vars - cfgvars = get_config_vars() - - macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') - - if 1: - # Always calculate the release of the running machine, - # needed to determine if we can build fat binaries or not. - - macrelease = macver - # Get the system version. Reading this plist is a documented - # way to get the system version (see the documentation for - # the Gestalt Manager) - try: - f = open('/System/Library/CoreServices/SystemVersion.plist') - except IOError: - # We're on a plain darwin box, fall back to the default - # behaviour. - pass - else: - try: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - if m is not None: - macrelease = '.'.join(m.group(1).split('.')[:2]) - # else: fall back to the default behaviour - finally: - f.close() - - if not macver: - macver = macrelease - - if macver: - from distutils.sysconfig import get_config_vars - release = macver - osname = "macosx" - - if (macrelease + '.') >= '10.4.' and \ - '-arch' in get_config_vars().get('CFLAGS', '').strip(): - # The universal build will build fat binaries, but not on - # systems before 10.4 - # - # Try to detect 4-way universal builds, those have machine-type - # 'universal' instead of 'fat'. - - machine = 'fat' - cflags = get_config_vars().get('CFLAGS') - - archs = re.findall('-arch\s+(\S+)', cflags) - archs = tuple(sorted(set(archs))) - - if len(archs) == 1: - machine = archs[0] - elif archs == ('i386', 'ppc'): - machine = 'fat' - elif archs == ('i386', 'x86_64'): - machine = 'intel' - elif archs == ('i386', 'ppc', 'x86_64'): - machine = 'fat3' - elif archs == ('ppc64', 'x86_64'): - machine = 'fat64' - elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): - machine = 'universal' - else: - raise ValueError( - "Don't know machine value for archs=%r"%(archs,)) - - elif machine == 'i386': - # On OSX the machine type returned by uname is always the - # 32-bit variant, even if the executable architecture is - # the 64-bit variant - if sys.maxsize >= 2**32: - machine = 'x86_64' - - elif machine in ('PowerPC', 'Power_Macintosh'): - # Pick a sane name for the PPC architecture. - machine = 'ppc' - - # See 'i386' case - if sys.maxsize >= 2**32: - machine = 'ppc64' + import _osx_support, distutils.sysconfig + osname, release, machine = _osx_support.get_platform_osx( + distutils.sysconfig.get_config_vars(), + osname, release, machine) return "%s-%s-%s" % (osname, release, machine) -- cgit v1.2.1 From a0418246ba7379453fadeb07a77c10770751f25e Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sun, 22 Jul 2012 02:56:36 -0700 Subject: Issue #15184: Some config variables in test_sysconfig_module may differ between sysconfig and distutils.sysconfig due to compiler customizations on OS X. For now, move those vars into a separate test and skip if the customization has taken place in distutils. The long-term solution is to eliminate having two sysconfig modules. --- tests/test_sysconfig.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index fbe26bf6..545ef3b5 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -102,7 +102,27 @@ class SysconfigTestCase(support.EnvironGuard, import sysconfig as global_sysconfig self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS')) self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS')) - self.assertEqual(global_sysconfig.get_config_var('LDSHARED'),sysconfig.get_config_var('LDSHARED')) + + @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'),'compiler flags customized') + def test_sysconfig_compiler_vars(self): + # On OS X, binary installers support extension module building on + # various levels of the operating system with differing Xcode + # configurations. This requires customization of some of the + # compiler configuration directives to suit the environment on + # the installed machine. Some of these customizations may require + # running external programs and, so, are deferred until needed by + # the first extension module build. With Python 3.3, only + # the Distutils version of sysconfig is used for extension module + # builds, which happens earlier in the Distutils tests. This may + # cause the following tests to fail since no tests have caused + # the global version of sysconfig to call the customization yet. + # The solution for now is to simply skip this test in this case. + # The longer-term solution is to only have one version of sysconfig. + + import sysconfig as global_sysconfig + if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'): + return + self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), sysconfig.get_config_var('LDSHARED')) self.assertEqual(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC')) -- cgit v1.2.1 From 1f80bd3ac6984d9941f06f1290639fc9572d59ed Mon Sep 17 00:00:00 2001 From: Richard Oudkerk Date: Fri, 27 Jul 2012 12:06:55 +0100 Subject: Issue #15364: Fix sysconfig.get_config_var('srcdir') to be an absolute path. --- sysconfig.py | 17 +++++++++++++++++ tests/test_sysconfig.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index 910e1047..317640ca 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -533,6 +533,23 @@ def get_config_vars(*args): _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + # Always convert srcdir to an absolute path + srcdir = _config_vars.get('srcdir', project_base) + if os.name == 'posix': + if python_build: + # If srcdir is a relative path (typically '.' or '..') + # then it should be interpreted relative to the directory + # containing Makefile. + base = os.path.dirname(get_makefile_filename()) + srcdir = os.path.join(base, srcdir) + else: + # srcdir is not meaningful since the installation is + # spread about the filesystem. We choose the + # directory containing the Makefile since we know it + # exists. + srcdir = os.path.dirname(get_makefile_filename()) + _config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir)) + # Convert srcdir into an absolute path if it appears necessary. # Normally it is relative to the build directory. However, during # testing, for example, we might be running a non-installed python diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 545ef3b5..546bb721 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -53,6 +53,34 @@ class SysconfigTestCase(support.EnvironGuard, self.assertTrue(isinstance(cvars, dict)) self.assertTrue(cvars) + def test_srcdir(self): + # See Issues #15322, #15364. + srcdir = sysconfig.get_config_var('srcdir') + + self.assertTrue(os.path.isabs(srcdir), srcdir) + self.assertTrue(os.path.isdir(srcdir), srcdir) + + if sysconfig.python_build: + # The python executable has not been installed so srcdir + # should be a full source checkout. + Python_h = os.path.join(srcdir, 'Include', 'Python.h') + self.assertTrue(os.path.exists(Python_h), Python_h) + self.assertTrue(sysconfig._is_python_source_dir(srcdir)) + elif os.name == 'posix': + self.assertEqual(sysconfig.get_makefile_filename(), srcdir) + + def test_srcdir_independent_of_cwd(self): + # srcdir should be independent of the current working directory + # See Issues #15322, #15364. + srcdir = sysconfig.get_config_var('srcdir') + cwd = os.getcwd() + try: + os.chdir('..') + srcdir2 = sysconfig.get_config_var('srcdir') + finally: + os.chdir(cwd) + self.assertEqual(srcdir, srcdir2) + def test_customize_compiler(self): # not testing if default compiler is not unix -- cgit v1.2.1 From cd406fc66618543efe51a4867f8b0ecfe90c9a3e Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 27 Jul 2012 23:37:04 -0700 Subject: Issue #15364: Fix test_srcdir for the installed case. --- tests/test_sysconfig.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 546bb721..826ea424 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -67,7 +67,8 @@ class SysconfigTestCase(support.EnvironGuard, self.assertTrue(os.path.exists(Python_h), Python_h) self.assertTrue(sysconfig._is_python_source_dir(srcdir)) elif os.name == 'posix': - self.assertEqual(sysconfig.get_makefile_filename(), srcdir) + self.assertEqual(os.path.dirname(sysconfig.get_makefile_filename()), + srcdir) def test_srcdir_independent_of_cwd(self): # srcdir should be independent of the current working directory -- cgit v1.2.1 From 8a0aec34b9d22cd514bcc2404b3cab7242d90a79 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 11 Aug 2012 08:49:20 +0200 Subject: Bump to 3.3b2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7a7b8d6c..15042770 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0b1" +__version__ = "3.3.0b2" #--end constants-- -- cgit v1.2.1 From 1cfadca0b0482f1f0ab379c7427dff4b627cec32 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 25 Aug 2012 12:16:37 +0200 Subject: Bump to 3.3.0rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 15042770..8d73b51c 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0b2" +__version__ = "3.3.0rc1" #--end constants-- -- cgit v1.2.1 From f0e94e21399229808cd34bea8cba5175f14ab07d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 9 Sep 2012 08:56:46 +0200 Subject: Bump to 3.3.0rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8d73b51c..f23d8864 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0rc1" +__version__ = "3.3.0rc2" #--end constants-- -- cgit v1.2.1 From db43cb973d253a6e01945b61c92f6161921d7ce2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 23 Sep 2012 17:15:21 +0200 Subject: Bump to 3.3.0rc3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f23d8864..b755eaaf 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0rc2" +__version__ = "3.3.0rc3" #--end constants-- -- cgit v1.2.1 From a5609a81b25425fdff1ac8aab120d8d6a4313f8b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 29 Sep 2012 09:04:54 +0200 Subject: Bump version to 3.3.0 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b755eaaf..345ac4f8 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0rc3" +__version__ = "3.3.0" #--end constants-- -- cgit v1.2.1 From ff6785063112982ccd38892d21859b8a8a45318a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 29 Sep 2012 09:34:13 +0200 Subject: Bump version to 3.4.0 alpha 0. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 345ac4f8..70a72b09 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0" +__version__ = "3.4.0a0" #--end constants-- -- cgit v1.2.1 From 495e4bf6c1029b10cab8fa83ce28caf7011d0955 Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Thu, 11 Oct 2012 01:20:12 +0200 Subject: Closes #16135: Removal of OS/2 support (distutils) --- ccompiler.py | 5 +- command/bdist.py | 3 +- command/bdist_dumb.py | 8 +- command/build_ext.py | 26 +--- command/install.py | 15 --- emxccompiler.py | 315 ----------------------------------------------- spawn.py | 24 +--- sysconfig.py | 24 ---- tests/test_bdist_dumb.py | 2 - tests/test_install.py | 2 +- tests/test_util.py | 2 +- util.py | 6 - 12 files changed, 7 insertions(+), 425 deletions(-) delete mode 100644 emxccompiler.py diff --git a/ccompiler.py b/ccompiler.py index c795c958..911e84dd 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -351,7 +351,7 @@ class CCompiler: return macros, objects, extra, pp_opts, build def _get_cc_args(self, pp_opts, debug, before): - # works for unixccompiler, emxccompiler, cygwinccompiler + # works for unixccompiler, cygwinccompiler cc_args = pp_opts + ['-c'] if debug: cc_args[:0] = ['-g'] @@ -926,7 +926,6 @@ _default_compilers = ( # on a cygwin built python we can use gcc like an ordinary UNIXish # compiler ('cygwin.*', 'unix'), - ('os2emx', 'emx'), # OS name mappings ('posix', 'unix'), @@ -968,8 +967,6 @@ compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', "Mingw32 port of GNU C Compiler for Win32"), 'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"), - 'emx': ('emxccompiler', 'EMXCCompiler', - "EMX port of GNU C Compiler for OS/2"), } def show_compilers(): diff --git a/command/bdist.py b/command/bdist.py index c5188eb3..38b169af 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -52,8 +52,7 @@ class bdist(Command): # This won't do in reality: will need to distinguish RPM-ish Linux, # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. default_format = {'posix': 'gztar', - 'nt': 'zip', - 'os2': 'zip'} + 'nt': 'zip'} # Establish the preferred order (for the --help-formats option). format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 1ab09d16..eefdfea5 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -38,8 +38,7 @@ class bdist_dumb(Command): boolean_options = ['keep-temp', 'skip-build', 'relative'] default_format = { 'posix': 'gztar', - 'nt': 'zip', - 'os2': 'zip' } + 'nt': 'zip' } def initialize_options(self): self.bdist_dir = None @@ -85,11 +84,6 @@ class bdist_dumb(Command): archive_basename = "%s.%s" % (self.distribution.get_fullname(), self.plat_name) - # OS/2 objects to any ":" characters in a filename (such as when - # a timestamp is used in a version) so change them to hyphens. - if os.name == "os2": - archive_basename = archive_basename.replace(":", "-") - pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) if not self.relative: archive_root = self.bdist_dir diff --git a/command/build_ext.py b/command/build_ext.py index f16e2f17..6b6a04e8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -223,11 +223,6 @@ class build_ext(Command): self.library_dirs.append(os.path.join(sys.exec_prefix, 'PC', 'VC6')) - # OS/2 (EMX) doesn't support Debug vs Release builds, but has the - # import libraries in its "Config" subdirectory - if os.name == 'os2': - self.library_dirs.append(os.path.join(sys.exec_prefix, 'Config')) - # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': @@ -613,9 +608,6 @@ class build_ext(Command): return fn else: return "swig.exe" - elif os.name == "os2": - # assume swig available in the PATH. - return "swig.exe" else: raise DistutilsPlatformError( "I don't know how to find (much less run) SWIG " @@ -666,9 +658,6 @@ class build_ext(Command): """ from distutils.sysconfig import get_config_var ext_path = ext_name.split('.') - # OS/2 has an 8 character module (extension) limit :-( - if os.name == "os2": - ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] # extensions in debug_mode are named 'module_d.pyd' under windows so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: @@ -689,7 +678,7 @@ class build_ext(Command): def get_libraries(self, ext): """Return the list of libraries to link against when building a shared extension. On most platforms, this is just 'ext.libraries'; - on Windows and OS/2, we add the Python library (eg. python20.dll). + on Windows, we add the Python library (eg. python20.dll). """ # The python library is always needed on Windows. For MSVC, this # is redundant, since the library is mentioned in a pragma in @@ -709,19 +698,6 @@ class build_ext(Command): return ext.libraries + [pythonlib] else: return ext.libraries - elif sys.platform == "os2emx": - # EMX/GCC requires the python library explicitly, and I - # believe VACPP does as well (though not confirmed) - AIM Apr01 - template = "python%d%d" - # debug versions of the main DLL aren't supported, at least - # not at this time - AIM Apr01 - #if self.debug: - # template = template + '_d' - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - # don't extend ext.libraries, it may be shared with other - # extensions, it is a reference to the original list - return ext.libraries + [pythonlib] elif sys.platform[:6] == "cygwin": template = "python%d.%d" pythonlib = (template % diff --git a/command/install.py b/command/install.py index 0161898f..04326a17 100644 --- a/command/install.py +++ b/command/install.py @@ -58,13 +58,6 @@ INSTALL_SCHEMES = { 'data' : '$base', }, 'nt': WINDOWS_SCHEME, - 'os2': { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - }, } # user site schemes @@ -86,14 +79,6 @@ if HAS_USER_SITE: 'data' : '$userbase', } - INSTALL_SCHEMES['os2_home'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/include/python$py_version_short/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - } - # The keys to an installation scheme; if any new types of files are to be # installed, be sure to add an entry to every installation scheme above, # and to SCHEME_KEYS here. diff --git a/emxccompiler.py b/emxccompiler.py deleted file mode 100644 index 3675f8df..00000000 --- a/emxccompiler.py +++ /dev/null @@ -1,315 +0,0 @@ -"""distutils.emxccompiler - -Provides the EMXCCompiler class, a subclass of UnixCCompiler that -handles the EMX port of the GNU C compiler to OS/2. -""" - -# issues: -# -# * OS/2 insists that DLLs can have names no longer than 8 characters -# We put export_symbols in a def-file, as though the DLL can have -# an arbitrary length name, but truncate the output filename. -# -# * only use OMF objects and use LINK386 as the linker (-Zomf) -# -# * always build for multithreading (-Zmt) as the accompanying OS/2 port -# of Python is only distributed with threads enabled. -# -# tested configurations: -# -# * EMX gcc 2.81/EMX 0.9d fix03 - -import os,sys,copy -from distutils.ccompiler import gen_preprocess_options, gen_lib_options -from distutils.unixccompiler import UnixCCompiler -from distutils.file_util import write_file -from distutils.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils import log - -class EMXCCompiler (UnixCCompiler): - - compiler_type = 'emx' - obj_extension = ".obj" - static_lib_extension = ".lib" - shared_lib_extension = ".dll" - static_lib_format = "%s%s" - shared_lib_format = "%s%s" - res_extension = ".res" # compiled resource file - exe_extension = ".exe" - - def __init__ (self, - verbose=0, - dry_run=0, - force=0): - - UnixCCompiler.__init__ (self, verbose, dry_run, force) - - (status, details) = check_config_h() - self.debug_print("Python's GCC status: %s (details: %s)" % - (status, details)) - if status is not CONFIG_H_OK: - self.warn( - "Python's pyconfig.h doesn't seem to support your compiler. " + - ("Reason: %s." % details) + - "Compiling may fail because of undefined preprocessor macros.") - - (self.gcc_version, self.ld_version) = \ - get_versions() - self.debug_print(self.compiler_type + ": gcc %s, ld %s\n" % - (self.gcc_version, - self.ld_version) ) - - # Hard-code GCC because that's what this is all about. - # XXX optimization, warnings etc. should be customizable. - self.set_executables(compiler='gcc -Zomf -Zmt -O3 -fomit-frame-pointer -mprobe -Wall', - compiler_so='gcc -Zomf -Zmt -O3 -fomit-frame-pointer -mprobe -Wall', - linker_exe='gcc -Zomf -Zmt -Zcrtdll', - linker_so='gcc -Zomf -Zmt -Zcrtdll -Zdll') - - # want the gcc library statically linked (so that we don't have - # to distribute a version dependent on the compiler we have) - self.dll_libraries=["gcc"] - - # __init__ () - - def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): - if ext == '.rc': - # gcc requires '.rc' compiled to binary ('.res') files !!! - try: - self.spawn(["rc", "-r", src]) - except DistutilsExecError as msg: - raise CompileError(msg) - else: # for other files use the C-compiler - try: - self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + - extra_postargs) - except DistutilsExecError as msg: - raise CompileError(msg) - - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - # use separate copies, so we can modify the lists - extra_preargs = copy.copy(extra_preargs or []) - libraries = copy.copy(libraries or []) - objects = copy.copy(objects or []) - - # Additional libraries - libraries.extend(self.dll_libraries) - - # handle export symbols by creating a def-file - # with executables this only works with gcc/ld as linker - if ((export_symbols is not None) and - (target_desc != self.EXECUTABLE)): - # (The linker doesn't do anything if output is up-to-date. - # So it would probably better to check if we really need this, - # but for this we had to insert some unchanged parts of - # UnixCCompiler, and this is not what we want.) - - # we want to put some files in the same directory as the - # object files are, build_temp doesn't help much - # where are the object files - temp_dir = os.path.dirname(objects[0]) - # name of dll to give the helper files the same base name - (dll_name, dll_extension) = os.path.splitext( - os.path.basename(output_filename)) - - # generate the filenames for these files - def_file = os.path.join(temp_dir, dll_name + ".def") - - # Generate .def file - contents = [ - "LIBRARY %s INITINSTANCE TERMINSTANCE" % \ - os.path.splitext(os.path.basename(output_filename))[0], - "DATA MULTIPLE NONSHARED", - "EXPORTS"] - for sym in export_symbols: - contents.append(' "%s"' % sym) - self.execute(write_file, (def_file, contents), - "writing %s" % def_file) - - # next add options for def-file and to creating import libraries - # for gcc/ld the def-file is specified as any other object files - objects.append(def_file) - - #end: if ((export_symbols is not None) and - # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): - - # who wants symbols and a many times larger output file - # should explicitly switch the debug mode on - # otherwise we let dllwrap/ld strip the output file - # (On my machine: 10KB < stripped_file < ??100KB - # unstripped_file = stripped_file + XXX KB - # ( XXX=254 for a typical python extension)) - if not debug: - extra_preargs.append("-s") - - UnixCCompiler.link(self, - target_desc, - objects, - output_filename, - output_dir, - libraries, - library_dirs, - runtime_library_dirs, - None, # export_symbols, we do this in our def-file - debug, - extra_preargs, - extra_postargs, - build_temp, - target_lang) - - # link () - - # -- Miscellaneous methods ----------------------------------------- - - # override the object_filenames method from CCompiler to - # support rc and res-files - def object_filenames (self, - source_filenames, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' - obj_names = [] - for src_name in source_filenames: - # use normcase to make sure '.rc' is really '.rc' and not '.RC' - (base, ext) = os.path.splitext (os.path.normcase(src_name)) - if ext not in (self.src_extensions + ['.rc']): - raise UnknownFileError("unknown file type '%s' (from '%s')" % \ - (ext, src_name)) - if strip_dir: - base = os.path.basename (base) - if ext == '.rc': - # these need to be compiled to object files - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - else: - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) - return obj_names - - # object_filenames () - - # override the find_library_file method from UnixCCompiler - # to deal with file naming/searching differences - def find_library_file(self, dirs, lib, debug=0): - shortlib = '%s.lib' % lib - longlib = 'lib%s.lib' % lib # this form very rare - - # get EMX's default library directory search path - try: - emx_dirs = os.environ['LIBRARY_PATH'].split(';') - except KeyError: - emx_dirs = [] - - for dir in dirs + emx_dirs: - shortlibp = os.path.join(dir, shortlib) - longlibp = os.path.join(dir, longlib) - if os.path.exists(shortlibp): - return shortlibp - elif os.path.exists(longlibp): - return longlibp - - # Oops, didn't find it in *any* of 'dirs' - return None - -# class EMXCCompiler - - -# Because these compilers aren't configured in Python's pyconfig.h file by -# default, we should at least warn the user if he is using a unmodified -# version. - -CONFIG_H_OK = "ok" -CONFIG_H_NOTOK = "not ok" -CONFIG_H_UNCERTAIN = "uncertain" - -def check_config_h(): - - """Check if the current Python installation (specifically, pyconfig.h) - appears amenable to building extensions with GCC. Returns a tuple - (status, details), where 'status' is one of the following constants: - CONFIG_H_OK - all is well, go ahead and compile - CONFIG_H_NOTOK - doesn't look good - CONFIG_H_UNCERTAIN - not sure -- unable to read pyconfig.h - 'details' is a human-readable string explaining the situation. - - Note there are two ways to conclude "OK": either 'sys.version' contains - the string "GCC" (implying that this Python was built with GCC), or the - installed "pyconfig.h" contains the string "__GNUC__". - """ - - # XXX since this function also checks sys.version, it's not strictly a - # "pyconfig.h" check -- should probably be renamed... - - from distutils import sysconfig - # if sys.version contains GCC then python was compiled with - # GCC, and the pyconfig.h file should be OK - if sys.version.find("GCC") >= 0: - return (CONFIG_H_OK, "sys.version mentions 'GCC'") - - fn = sysconfig.get_config_h_filename() - try: - # It would probably better to read single lines to search. - # But we do this only once, and it is fast enough - f = open(fn) - try: - s = f.read() - finally: - f.close() - - except IOError as exc: - # if we can't read this file, we cannot say it is wrong - # the compiler will complain later about this file as missing - return (CONFIG_H_UNCERTAIN, - "couldn't read '%s': %s" % (fn, exc.strerror)) - - else: - # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar - if s.find("__GNUC__") >= 0: - return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) - else: - return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) - - -def get_versions(): - """ Try to find out the versions of gcc and ld. - If not possible it returns None for it. - """ - from distutils.version import StrictVersion - from distutils.spawn import find_executable - import re - - gcc_exe = find_executable('gcc') - if gcc_exe: - out = os.popen(gcc_exe + ' -dumpversion','r') - try: - out_string = out.read() - finally: - out.close() - result = re.search('(\d+\.\d+\.\d+)', out_string, re.ASCII) - if result: - gcc_version = StrictVersion(result.group(1)) - else: - gcc_version = None - else: - gcc_version = None - # EMX ld has no way of reporting version number, and we use GCC - # anyway - so we can link OMF DLLs - ld_version = None - return (gcc_version, ld_version) diff --git a/spawn.py b/spawn.py index f58c55f9..b1c5a442 100644 --- a/spawn.py +++ b/spawn.py @@ -32,8 +32,6 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0): _spawn_posix(cmd, search_path, dry_run=dry_run) elif os.name == 'nt': _spawn_nt(cmd, search_path, dry_run=dry_run) - elif os.name == 'os2': - _spawn_os2(cmd, search_path, dry_run=dry_run) else: raise DistutilsPlatformError( "don't know how to spawn programs on platform '%s'" % os.name) @@ -74,26 +72,6 @@ def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): raise DistutilsExecError( "command '%s' failed with exit status %d" % (cmd[0], rc)) -def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): - executable = cmd[0] - if search_path: - # either we find one or it stays the same - executable = find_executable(executable) or executable - log.info(' '.join([executable] + cmd[1:])) - if not dry_run: - # spawnv for OS/2 EMX requires a full path to the .exe - try: - rc = os.spawnv(os.P_WAIT, executable, cmd) - except OSError as exc: - # this seems to happen when the command isn't found - raise DistutilsExecError( - "command '%s' failed: %s" % (cmd[0], exc.args[-1])) - if rc != 0: - # and this reflects the command running but failing - log.debug("command '%s' failed with exit status %d" % (cmd[0], rc)) - raise DistutilsExecError( - "command '%s' failed with exit status %d" % (cmd[0], rc)) - if sys.platform == 'darwin': from distutils import sysconfig _cfg_target = None @@ -180,7 +158,7 @@ def find_executable(executable, path=None): paths = path.split(os.pathsep) base, ext = os.path.splitext(executable) - if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): + if (sys.platform == 'win32') and (ext != '.exe'): executable = executable + '.exe' if not os.path.isfile(executable): diff --git a/sysconfig.py b/sysconfig.py index 317640ca..3b6549fc 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -110,8 +110,6 @@ def get_python_inc(plat_specific=0, prefix=None): return os.path.join(prefix, "include", python_dir) elif os.name == "nt": return os.path.join(prefix, "include") - elif os.name == "os2": - return os.path.join(prefix, "Include") else: raise DistutilsPlatformError( "I don't know where Python installs its C header files " @@ -153,11 +151,6 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): return prefix else: return os.path.join(prefix, "Lib", "site-packages") - elif os.name == "os2": - if standard_lib: - return os.path.join(prefix, "Lib") - else: - return os.path.join(prefix, "Lib", "site-packages") else: raise DistutilsPlatformError( "I don't know where Python installs its library " @@ -492,23 +485,6 @@ def _init_nt(): _config_vars = g -def _init_os2(): - """Initialize the module as appropriate for OS/2""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - g['SO'] = '.pyd' - g['EXE'] = ".exe" - - global _config_vars - _config_vars = g - - def get_config_vars(*args): """With no arguments, return a dictionary of all configuration variables relevant for the current platform. Generally this includes diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 1037d821..761051c1 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -75,8 +75,6 @@ class BuildDumbTestCase(support.TempdirManager, # see what we have dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name) - if os.name == 'os2': - base = base.replace(':', '-') self.assertEqual(dist_created, [base]) diff --git a/tests/test_install.py b/tests/test_install.py index cb2e1f28..47d630cc 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -94,7 +94,7 @@ class InstallTestCase(support.TempdirManager, self.addCleanup(cleanup) - for key in ('nt_user', 'unix_user', 'os2_home'): + for key in ('nt_user', 'unix_user'): self.assertIn(key, INSTALL_SCHEMES) dist = Distribution({'name': 'xx'}) diff --git a/tests/test_util.py b/tests/test_util.py index eac9b514..b73cfe94 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -236,7 +236,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertRaises(DistutilsPlatformError, change_root, 'c:\\root', 'its\\here') - # XXX platforms to be covered: os2, mac + # XXX platforms to be covered: mac def test_check_environ(self): util._environ_checked = 0 diff --git a/util.py b/util.py index 67d81663..ba09ac45 100644 --- a/util.py +++ b/util.py @@ -154,12 +154,6 @@ def change_root (new_root, pathname): path = path[1:] return os.path.join(new_root, path) - elif os.name == 'os2': - (drive, path) = os.path.splitdrive(pathname) - if path[0] == os.sep: - path = path[1:] - return os.path.join(new_root, path) - else: raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) -- cgit v1.2.1 From 285e4e10ef6678f0b5bdb61ff5c183dd6360a4af Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Tue, 23 Oct 2012 20:26:14 +0100 Subject: Issue #16116: Now uses corrected include and library paths when building C extensions in a venv. --- command/build_ext.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index f16e2f17..b1d951e6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -160,6 +160,11 @@ class build_ext(Command): if isinstance(self.include_dirs, str): self.include_dirs = self.include_dirs.split(os.pathsep) + # If in a virtualenv, add its include directory + # Issue 16116 + if sys.exec_prefix != sys.base_exec_prefix: + self.include_dirs.append(os.path.join(sys.exec_prefix, 'include')) + # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. self.include_dirs.append(py_include) @@ -190,6 +195,8 @@ class build_ext(Command): # must be the *native* platform. But we don't really support # cross-compiling via a binary install anyway, so we let it go. self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) + if sys.base_exec_prefix != sys.prefix: # Issue 16116 + self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs')) if self.debug: self.build_temp = os.path.join(self.build_temp, "Debug") else: -- cgit v1.2.1 From 993d79149275b17b639aebd6d3ca2eb960ec6876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 8 Dec 2012 14:21:51 -0500 Subject: Ignore .nfs* files in distutils (#7719). These files are created by some NFS clients a file is edited and removed concurrently (see added link in doc for more info). If such a file is removed between distutils calls listdir and copy, it will get confused. Other special files are ignored in sdist (namely VCS directories), but this has to be filtered out earlier. --- dir_util.py | 4 ++++ tests/test_dir_util.py | 21 ++++++++++++++++----- tests/test_sdist.py | 7 ++++--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/dir_util.py b/dir_util.py index 30daf49a..2826ff80 100644 --- a/dir_util.py +++ b/dir_util.py @@ -141,6 +141,10 @@ def copy_tree(src, dst, preserve_mode=1, preserve_times=1, src_name = os.path.join(src, n) dst_name = os.path.join(dst, n) + if n.startswith('.nfs'): + # skip NFS rename files + continue + if preserve_symlinks and os.path.islink(src_name): link_dest = os.readlink(src_name) if verbose >= 1: diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index ce74589d..1589f129 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -76,7 +76,6 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): remove_tree(self.root_target, verbose=0) - def test_copy_tree_verbosity(self): mkpath(self.target, verbose=0) @@ -88,11 +87,8 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): mkpath(self.target, verbose=0) a_file = os.path.join(self.target, 'ok.txt') - f = open(a_file, 'w') - try: + with open(a_file, 'w') as f: f.write('some content') - finally: - f.close() wanted = ['copying %s -> %s' % (a_file, self.target2)] copy_tree(self.target, self.target2, verbose=1) @@ -101,6 +97,21 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): remove_tree(self.root_target, verbose=0) remove_tree(self.target2, verbose=0) + def test_copy_tree_skips_nfs_temp_files(self): + mkpath(self.target, verbose=0) + + a_file = os.path.join(self.target, 'ok.txt') + nfs_file = os.path.join(self.target, '.nfs123abc') + for f in a_file, nfs_file: + with open(f, 'w') as fh: + fh.write('some content') + + copy_tree(self.target, self.target2) + self.assertEqual(os.listdir(self.target2), ['ok.txt']) + + remove_tree(self.root_target, verbose=0) + remove_tree(self.target2, verbose=0) + def test_ensure_relative(self): if os.sep == '/': self.assertEqual(ensure_relative('/home/foo'), 'home/foo') diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 1ba2a1a6..e6359d6a 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -83,9 +83,8 @@ class SDistTestCase(PyPIRCCommandTestCase): @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_prune_file_list(self): - # this test creates a package with some vcs dirs in it - # and launch sdist to make sure they get pruned - # on all systems + # this test creates a project with some VCS dirs and an NFS rename + # file, then launches sdist to check they get pruned on all systems # creating VCS directories with some files in them os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) @@ -99,6 +98,8 @@ class SDistTestCase(PyPIRCCommandTestCase): self.write_file((self.tmp_dir, 'somecode', '.git', 'ok'), 'xxx') + self.write_file((self.tmp_dir, 'somecode', '.nfs0001'), 'xxx') + # now building a sdist dist, cmd = self.get_cmd() -- cgit v1.2.1 From 6b0394e36c24c3fa6b341b2ce607de2981043ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 8 Dec 2012 14:41:39 -0500 Subject: Remove code unneeded after f833e7ec4de1 --- config.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/config.py b/config.py index 9d8b30ea..1d327143 100644 --- a/config.py +++ b/config.py @@ -47,11 +47,6 @@ class PyPIRCCommand(Command): f.write(DEFAULT_PYPIRC % (username, password)) finally: f.close() - try: - os.chmod(rc, 0600) - except OSError: - # should do something better here - pass def _read_pypirc(self): """Reads the .pypirc file.""" -- cgit v1.2.1 From 0bca979e33bd4dca758288e68912c9a763004a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 8 Dec 2012 14:51:47 -0500 Subject: Create ~/.pypirc securely (#13512). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There was a window between the write and the chmod where the user’s password would be exposed, depending on default permissions. Philip Jenvey’s patch fixes it. --- config.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/config.py b/config.py index 5b625f3f..1fd53346 100644 --- a/config.py +++ b/config.py @@ -4,7 +4,6 @@ Provides the PyPIRCCommand class, the base class for the command classes that uses .pypirc in the distutils.command package. """ import os -import sys from configparser import ConfigParser from distutils.cmd import Command @@ -43,16 +42,8 @@ class PyPIRCCommand(Command): def _store_pypirc(self, username, password): """Creates a default .pypirc file.""" rc = self._get_rc_file() - f = open(rc, 'w') - try: + with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f: f.write(DEFAULT_PYPIRC % (username, password)) - finally: - f.close() - try: - os.chmod(rc, 0o600) - except OSError: - # should do something better here - pass def _read_pypirc(self): """Reads the .pypirc file.""" -- cgit v1.2.1 From 40e66e5eb34a9d2dc8c4fe212f99fabdd48bca7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 8 Dec 2012 22:26:57 -0500 Subject: Fix setup.py register failure with invalid rst in description (#13614). Original patch by Julien Courteau and Pierre Paul Lefebvre. --- command/check.py | 3 +++ tests/test_register.py | 25 +++++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/command/check.py b/command/check.py index 4b64e458..152bf0de 100644 --- a/command/check.py +++ b/command/check.py @@ -26,6 +26,9 @@ try: def system_message(self, level, message, *children, **kwargs): self.messages.append((level, message, children, kwargs)) + return nodes.system_message(message, level=level, + type=self.levels[level], + *children, **kwargs) HAS_DOCUTILS = True except ImportError: diff --git a/tests/test_register.py b/tests/test_register.py index aa9bc43c..9a0ff34c 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -1,6 +1,5 @@ # -*- encoding: utf8 -*- """Tests for distutils.command.register.""" -import sys import os import unittest import getpass @@ -11,11 +10,14 @@ from test.test_support import check_warnings, run_unittest from distutils.command import register as register_module from distutils.command.register import register -from distutils.core import Distribution from distutils.errors import DistutilsSetupError -from distutils.tests import support -from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase +from distutils.tests.test_config import PyPIRCCommandTestCase + +try: + import docutils +except ImportError: + docutils = None PYPIRC_NOPASSWORD = """\ [distutils] @@ -264,6 +266,21 @@ class RegisterTestCase(PyPIRCCommandTestCase): finally: del register_module.raw_input + @unittest.skipUnless(docutils is not None, 'needs docutils') + def test_register_invalid_long_description(self): + description = ':funkie:`str`' # mimic Sphinx-specific markup + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx', + 'long_description': description} + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = True + inputs = RawInputs('2', 'tarek', 'tarek@ziade.org') + register_module.raw_input = inputs + self.addCleanup(delattr, register_module, 'raw_input') + self.assertRaises(DistutilsSetupError, cmd.run) + def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated cmd = self._get_cmd() -- cgit v1.2.1 From 47a3e44bf7c675ae1c222d594d557450f1f25b37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 8 Dec 2012 22:30:47 -0500 Subject: Use proper skip instead of reporting success in one distutils test --- tests/test_register.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/test_register.py b/tests/test_register.py index 9a0ff34c..4f34b18b 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -194,6 +194,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertEqual(headers['Content-length'], '290') self.assertTrue('tarek' in req.data) + @unittest.skipUnless(docutils is not None, 'needs docutils') def test_strict(self): # testing the script option # when on, the register command stops if @@ -206,13 +207,6 @@ class RegisterTestCase(PyPIRCCommandTestCase): cmd.strict = 1 self.assertRaises(DistutilsSetupError, cmd.run) - # we don't test the reSt feature if docutils - # is not installed - try: - import docutils - except ImportError: - return - # metadata are OK but long_description is broken metadata = {'url': 'xxx', 'author': 'xxx', 'author_email': u'éxéxé', -- cgit v1.2.1 From 1bf87508cedffb239eb377928351b7d130a59e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sat, 8 Dec 2012 22:41:11 -0500 Subject: Fix setup.py register failure with invalid rst in description (#13614). Original patch by Julien Courteau and Pierre Paul Lefebvre. --- command/check.py | 3 +++ tests/test_register.py | 34 +++++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/command/check.py b/command/check.py index b67c7953..22b9349d 100644 --- a/command/check.py +++ b/command/check.py @@ -23,6 +23,9 @@ try: def system_message(self, level, message, *children, **kwargs): self.messages.append((level, message, children, kwargs)) + return nodes.system_message(message, level=level, + type=self.levels[level], + *children, **kwargs) HAS_DOCUTILS = True except Exception: diff --git a/tests/test_register.py b/tests/test_register.py index 5863ae14..a86b8606 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -1,5 +1,4 @@ """Tests for distutils.command.register.""" -import sys import os import unittest import getpass @@ -10,11 +9,14 @@ from test.support import check_warnings, run_unittest from distutils.command import register as register_module from distutils.command.register import register -from distutils.core import Distribution from distutils.errors import DistutilsSetupError -from distutils.tests import support -from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase +from distutils.tests.test_config import PyPIRCCommandTestCase + +try: + import docutils +except ImportError: + docutils = None PYPIRC_NOPASSWORD = """\ [distutils] @@ -193,6 +195,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertEqual(headers['Content-length'], '290') self.assertTrue((b'tarek') in req.data) + @unittest.skipUnless(docutils is not None, 'needs docutils') def test_strict(self): # testing the script option # when on, the register command stops if @@ -205,13 +208,6 @@ class RegisterTestCase(PyPIRCCommandTestCase): cmd.strict = 1 self.assertRaises(DistutilsSetupError, cmd.run) - # we don't test the reSt feature if docutils - # is not installed - try: - import docutils - except ImportError: - return - # metadata are OK but long_description is broken metadata = {'url': 'xxx', 'author': 'xxx', 'author_email': 'éxéxé', @@ -265,6 +261,22 @@ class RegisterTestCase(PyPIRCCommandTestCase): finally: del register_module.input + @unittest.skipUnless(docutils is not None, 'needs docutils') + def test_register_invalid_long_description(self): + description = ':funkie:`str`' # mimic Sphinx-specific markup + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx', + 'long_description': description} + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = True + inputs = Inputs('2', 'tarek', 'tarek@ziade.org') + register_module.input = inputs + self.addCleanup(delattr, register_module, 'input') + + self.assertRaises(DistutilsSetupError, cmd.run) + def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated cmd = self._get_cmd() -- cgit v1.2.1 From c24868ba84f1b66d88d6b1f5a3f9190bcd2ca5c5 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 18 Dec 2012 21:14:22 +0200 Subject: Issue #16714: use 'raise' exceptions, don't 'throw'. Patch by Serhiy Storchaka. --- tests/test_msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 5fa1ca10..301d43d2 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -104,7 +104,7 @@ class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): def test_no_compiler(self): - # makes sure query_vcvarsall throws + # makes sure query_vcvarsall raises # a DistutilsPlatformError if the compiler # is not found from distutils.msvc9compiler import query_vcvarsall -- cgit v1.2.1 From 75cf27d5a437064a0bfcf9f7e8ab7bc501260b61 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 18 Dec 2012 21:27:37 +0200 Subject: Issue #16714: use 'raise' exceptions, don't 'throw'. Patch by Serhiy Storchaka. --- tests/test_msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 73470729..2d94a111 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -104,7 +104,7 @@ class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): def test_no_compiler(self): - # makes sure query_vcvarsall throws + # makes sure query_vcvarsall raises # a DistutilsPlatformError if the compiler # is not found from distutils.msvc9compiler import query_vcvarsall -- cgit v1.2.1 From ca8d16926ecb9629ca53aa93327c0bdd2a144f5c Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 18 Dec 2012 22:02:39 +0200 Subject: Issue #16706: get rid of os.error --- core.py | 2 +- dir_util.py | 2 +- file_util.py | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core.py b/core.py index 260332a2..a43d7258 100644 --- a/core.py +++ b/core.py @@ -148,7 +148,7 @@ def setup (**attrs): dist.run_commands() except KeyboardInterrupt: raise SystemExit("interrupted") - except (IOError, os.error) as exc: + except (IOError, OSError) as exc: error = grok_environment_error(exc) if DEBUG: diff --git a/dir_util.py b/dir_util.py index 2826ff80..7d1c78ab 100644 --- a/dir_util.py +++ b/dir_util.py @@ -124,7 +124,7 @@ def copy_tree(src, dst, preserve_mode=1, preserve_times=1, "cannot copy tree '%s': not a directory" % src) try: names = os.listdir(src) - except os.error as e: + except OSError as e: (errno, errstr) = e if dry_run: names = [] diff --git a/file_util.py b/file_util.py index 9bdd14e4..f6ed290f 100644 --- a/file_util.py +++ b/file_util.py @@ -27,26 +27,26 @@ def _copy_file_contents(src, dst, buffer_size=16*1024): try: try: fsrc = open(src, 'rb') - except os.error as e: + except OSError as e: raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror)) if os.path.exists(dst): try: os.unlink(dst) - except os.error as e: + except OSError as e: raise DistutilsFileError( "could not delete '%s': %s" % (dst, e.strerror)) try: fdst = open(dst, 'wb') - except os.error as e: + except OSError as e: raise DistutilsFileError( "could not create '%s': %s" % (dst, e.strerror)) while True: try: buf = fsrc.read(buffer_size) - except os.error as e: + except OSError as e: raise DistutilsFileError( "could not read from '%s': %s" % (src, e.strerror)) @@ -55,7 +55,7 @@ def _copy_file_contents(src, dst, buffer_size=16*1024): try: fdst.write(buf) - except os.error as e: + except OSError as e: raise DistutilsFileError( "could not write to '%s': %s" % (dst, e.strerror)) finally: @@ -193,7 +193,7 @@ def move_file (src, dst, copy_it = False try: os.rename(src, dst) - except os.error as e: + except OSError as e: (num, msg) = e if num == errno.EXDEV: copy_it = True @@ -205,11 +205,11 @@ def move_file (src, dst, copy_file(src, dst, verbose=verbose) try: os.unlink(src) - except os.error as e: + except OSError as e: (num, msg) = e try: os.unlink(dst) - except os.error: + except OSError: pass raise DistutilsFileError( "couldn't move '%s' to '%s' by copy/delete: " -- cgit v1.2.1 From 437e5b731feb5f5b0e7205ff94d15c07c32e83a4 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 18 Dec 2012 23:10:48 +0200 Subject: Issue #16717: get rid of socket.error, replace with OSError --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 8b36851d..88990b2c 100644 --- a/command/upload.py +++ b/command/upload.py @@ -186,7 +186,7 @@ class upload(PyPIRCCommand): http.putheader('Authorization', auth) http.endheaders() http.send(body) - except socket.error as e: + except OSError as e: self.announce(str(e), log.ERROR) return -- cgit v1.2.1 From 8b21b15c3cebd6cf09d1ceb8abf91f0ebbafa499 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 25 Dec 2012 16:47:37 +0200 Subject: Replace IOError with OSError (#16715) --- command/build_scripts.py | 2 +- core.py | 2 +- cygwinccompiler.py | 2 +- dir_util.py | 2 +- errors.py | 4 ++-- msvc9compiler.py | 2 +- sysconfig.py | 4 ++-- util.py | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/command/build_scripts.py b/command/build_scripts.py index 4b5b22ec..90a8380a 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -74,7 +74,7 @@ class build_scripts(Command): # script. try: f = open(script, "rb") - except IOError: + except OSError: if not self.dry_run: raise f = None diff --git a/core.py b/core.py index a43d7258..91e51329 100644 --- a/core.py +++ b/core.py @@ -148,7 +148,7 @@ def setup (**attrs): dist.run_commands() except KeyboardInterrupt: raise SystemExit("interrupted") - except (IOError, OSError) as exc: + except OSError as exc: error = grok_environment_error(exc) if DEBUG: diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 0bdd539c..0c23ab1b 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -359,7 +359,7 @@ def check_config_h(): return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn finally: config_h.close() - except IOError as exc: + except OSError as exc: return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) diff --git a/dir_util.py b/dir_util.py index 7d1c78ab..2b35aa31 100644 --- a/dir_util.py +++ b/dir_util.py @@ -198,7 +198,7 @@ def remove_tree(directory, verbose=1, dry_run=0): abspath = os.path.abspath(cmd[1]) if abspath in _path_created: del _path_created[abspath] - except (IOError, OSError) as exc: + except OSError as exc: log.warn(grok_environment_error( exc, "error removing %s: " % directory)) diff --git a/errors.py b/errors.py index eb13c983..8b93059e 100644 --- a/errors.py +++ b/errors.py @@ -35,8 +35,8 @@ class DistutilsArgError (DistutilsError): class DistutilsFileError (DistutilsError): """Any problems in the filesystem: expected file not found, etc. - Typically this is for problems that we detect before IOError or - OSError could be raised.""" + Typically this is for problems that we detect before OSError + could be raised.""" pass class DistutilsOptionError (DistutilsError): diff --git a/msvc9compiler.py b/msvc9compiler.py index b3f6ce10..9688f200 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -729,7 +729,7 @@ class MSVCCompiler(CCompiler) : return manifest_file finally: manifest_f.close() - except IOError: + except OSError: pass # -- Miscellaneous methods ----------------------------------------- diff --git a/sysconfig.py b/sysconfig.py index 3b6549fc..91ed1a49 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -426,7 +426,7 @@ def _init_posix(): try: filename = get_makefile_filename() parse_makefile(filename, g) - except IOError as msg: + except OSError as msg: my_msg = "invalid Python installation: unable to open %s" % filename if hasattr(msg, "strerror"): my_msg = my_msg + " (%s)" % msg.strerror @@ -438,7 +438,7 @@ def _init_posix(): filename = get_config_h_filename() with open(filename) as file: parse_config_h(file, g) - except IOError as msg: + except OSError as msg: my_msg = "invalid Python installation: unable to open %s" % filename if hasattr(msg, "strerror"): my_msg = my_msg + " (%s)" % msg.strerror diff --git a/util.py b/util.py index ba09ac45..a2f95445 100644 --- a/util.py +++ b/util.py @@ -207,8 +207,8 @@ def subst_vars (s, local_vars): def grok_environment_error (exc, prefix="error: "): - """Generate a useful error message from an EnvironmentError (IOError or - OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and + """Generate a useful error message from an OSError + exception object. Handles Python 1.5.1 and 1.5.2 styles, and does what it can to deal with exception objects that don't have a filename (which happens when the error is due to a two-file operation, such as 'rename()' or 'link()'. Returns the error message as a string -- cgit v1.2.1 From 2e7e993256b1f737cd3ddec9a1cfc205455b2be2 Mon Sep 17 00:00:00 2001 From: "doko@python.org" Date: Fri, 25 Jan 2013 14:33:33 +0100 Subject: - Issue #15484: Fix _PYTHON_PROJECT_BASE for srcdir != builddir builds; use _PYTHON_PROJECT_BASE in distutils/sysconfig.py. --- sysconfig.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 317640ca..d125e0b4 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -24,7 +24,11 @@ BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) # Path to the base directory of the project. On Windows the binary may # live in project/PCBuild9. If we're dealing with an x64 Windows build, # it'll live in project/PCbuild/amd64. -project_base = os.path.dirname(os.path.abspath(sys.executable)) +# set for cross builds +if "_PYTHON_PROJECT_BASE" in os.environ: + project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) +else: + project_base = os.path.dirname(os.path.abspath(sys.executable)) if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) # PC/VS7.1 @@ -98,7 +102,7 @@ def get_python_inc(plat_specific=0, prefix=None): # the build directory may not be the source directory, we # must use "srcdir" from the makefile to find the "Include" # directory. - base = _sys_home or os.path.dirname(os.path.abspath(sys.executable)) + base = _sys_home or project_base if plat_specific: return base if _sys_home: @@ -251,8 +255,7 @@ def get_config_h_filename(): def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: - return os.path.join(_sys_home or os.path.dirname(sys.executable), - "Makefile") + return os.path.join(_sys_home or project_base, "Makefile") lib_dir = get_python_lib(plat_specific=0, standard_lib=1) config_file = 'config-{}{}'.format(get_python_version(), build_flags) return os.path.join(lib_dir, config_file, 'Makefile') @@ -555,7 +558,7 @@ def get_config_vars(*args): # testing, for example, we might be running a non-installed python # from a different directory. if python_build and os.name == "posix": - base = os.path.dirname(os.path.abspath(sys.executable)) + base = project_base if (not os.path.isabs(_config_vars['srcdir']) and base != os.getcwd()): # srcdir is relative and we are not in the same directory -- cgit v1.2.1 From 0ad57ca49e9ca519a62ae5da00d2217ba91f464e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 27 Jan 2013 19:45:49 +0200 Subject: - Issue #17041: Fix testing when Python is configured with the --without-doc-strings option. --- tests/test_build_ext.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index b71cc983..f6a503b5 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -77,8 +77,9 @@ class BuildExtTestCase(support.TempdirManager, self.assertEqual(xx.foo(2, 5), 7) self.assertEqual(xx.foo(13,15), 28) self.assertEqual(xx.new().demo(), None) - doc = 'This is a template module just for instruction.' - self.assertEqual(xx.__doc__, doc) + if test_support.HAVE_DOCSTRINGS: + doc = 'This is a template module just for instruction.' + self.assertEqual(xx.__doc__, doc) self.assertTrue(isinstance(xx.Null(), xx.Null)) self.assertTrue(isinstance(xx.Str(), xx.Str)) -- cgit v1.2.1 From 28dd71b74f49f8abf4f65fb05bb29fbc00f4e574 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 27 Jan 2013 19:47:45 +0200 Subject: Issue #17041: Fix testing when Python is configured with the --without-doc-strings. --- tests/test_build_ext.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 090eacfb..065a6a21 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -73,8 +73,9 @@ class BuildExtTestCase(TempdirManager, self.assertEqual(xx.foo(2, 5), 7) self.assertEqual(xx.foo(13,15), 28) self.assertEqual(xx.new().demo(), None) - doc = 'This is a template module just for instruction.' - self.assertEqual(xx.__doc__, doc) + if support.HAVE_DOCSTRINGS: + doc = 'This is a template module just for instruction.' + self.assertEqual(xx.__doc__, doc) self.assertTrue(isinstance(xx.Null(), xx.Null)) self.assertTrue(isinstance(xx.Str(), xx.Str)) -- cgit v1.2.1 From 30f6c6e37e64b06ccca07acb169bf517687ba2ec Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Thu, 31 Jan 2013 01:24:55 -0800 Subject: Issue #13590: OS X Xcode 4 - improve support for universal extension modules In particular, fix extension module build failures when trying to use 32-bit-only installer Pythons on systems with Xcode 4 (currently OS X 10.8, 10.7, and optionally 10.6). * Backport 3.3.0 fixes to 2.7 branch (for release in 2.7.4) * Since Xcode 4 removes ppc support, extension module builds now check for ppc compiler support and by default remove ppc and ppc64 archs when they are not available. * Extension module builds now revert to using system installed headers and libs (/usr and /System/Library) if the SDK used to build the interpreter is not installed or has moved. * Try to avoid building extension modules with deprecated and problematic Apple llvm-gcc compiler. If original compiler is not available, use clang instead by default. --- sysconfig.py | 111 +++++++++--------------------------------------- tests/test_sysconfig.py | 29 +++++++++++++ unixccompiler.py | 70 +++--------------------------- util.py | 92 ++------------------------------------- 4 files changed, 60 insertions(+), 242 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 4b193b27..daa4dc77 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -141,7 +141,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): "I don't know where Python installs its library " "on platform '%s'" % os.name) -_USE_CLANG = None + def customize_compiler(compiler): """Do any platform-specific customization of a CCompiler instance. @@ -150,6 +150,21 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": + if sys.platform == "darwin": + # Perform first-time customization of compiler-related + # config vars on OS X now that we know we need a compiler. + # This is primarily to support Pythons from binary + # installers. The kind and paths to build tools on + # the user system may vary significantly from the system + # that Python itself was built on. Also the user OS + # version and build tools may not support the same set + # of CPU architectures for universal builds. + global _config_vars + if not _config_vars.get('CUSTOMIZED_OSX_COMPILER', ''): + import _osx_support + _osx_support.customize_compiler(_config_vars) + _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SO', 'AR', @@ -157,36 +172,7 @@ def customize_compiler(compiler): newcc = None if 'CC' in os.environ: - newcc = os.environ['CC'] - elif sys.platform == 'darwin' and cc == 'gcc-4.2': - # Issue #13590: - # Since Apple removed gcc-4.2 in Xcode 4.2, we can no - # longer assume it is available for extension module builds. - # If Python was built with gcc-4.2, check first to see if - # it is available on this system; if not, try to use clang - # instead unless the caller explicitly set CC. - global _USE_CLANG - if _USE_CLANG is None: - from distutils import log - from subprocess import Popen, PIPE - p = Popen("! type gcc-4.2 && type clang && exit 2", - shell=True, stdout=PIPE, stderr=PIPE) - p.wait() - if p.returncode == 2: - _USE_CLANG = True - log.warn("gcc-4.2 not found, using clang instead") - else: - _USE_CLANG = False - if _USE_CLANG: - newcc = 'clang' - if newcc: - # On OS X, if CC is overridden, use that as the default - # command for LDSHARED as well - if (sys.platform == 'darwin' - and 'LDSHARED' not in os.environ - and ldshared.startswith(cc)): - ldshared = newcc + ldshared[len(cc):] - cc = newcc + cc = os.environ['CC'] if 'CXX' in os.environ: cxx = os.environ['CXX'] if 'LDSHARED' in os.environ: @@ -518,66 +504,11 @@ def get_config_vars(*args): _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + # OS X platforms require special customization to handle + # multi-architecture, multi-os-version installers if sys.platform == 'darwin': - kernel_version = os.uname()[2] # Kernel version (8.4.3) - major_version = int(kernel_version.split('.')[0]) - - if major_version < 8: - # On Mac OS X before 10.4, check if -arch and -isysroot - # are in CFLAGS or LDFLAGS and remove them if they are. - # This is needed when building extensions on a 10.3 system - # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = re.sub('-isysroot [^ \t]*', ' ', flags) - _config_vars[key] = flags - - else: - - # Allow the user to override the architecture flags using - # an environment variable. - # NOTE: This name was introduced by Apple in OSX 10.5 and - # is used by several scripting languages distributed with - # that OS release. - - if 'ARCHFLAGS' in os.environ: - arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = flags + ' ' + arch - _config_vars[key] = flags - - # If we're on OSX 10.5 or later and the user tries to - # compiles an extension using an SDK that is not present - # on the current machine it is better to not use an SDK - # than to fail. - # - # The major usecase for this is users using a Python.org - # binary installer on OSX 10.6: that installer uses - # the 10.4u SDK, but that SDK is not installed by default - # when you install Xcode. - # - m = re.search('-isysroot\s+(\S+)', _config_vars['CFLAGS']) - if m is not None: - sdk = m.group(1) - if not os.path.exists(sdk): - for key in ('LDFLAGS', 'BASECFLAGS', 'LDSHARED', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-isysroot\s+\S+(\s|$)', ' ', flags) - _config_vars[key] = flags + import _osx_support + _osx_support.customize_config_vars(_config_vars) if args: vals = [] diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 49570c4c..c064d2b0 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -72,6 +72,35 @@ class SysconfigTestCase(support.EnvironGuard, 'OTHER': 'foo'}) + def test_sysconfig_module(self): + import sysconfig as global_sysconfig + self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS')) + self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS')) + + @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'),'compiler flags customized') + def test_sysconfig_compiler_vars(self): + # On OS X, binary installers support extension module building on + # various levels of the operating system with differing Xcode + # configurations. This requires customization of some of the + # compiler configuration directives to suit the environment on + # the installed machine. Some of these customizations may require + # running external programs and, so, are deferred until needed by + # the first extension module build. With Python 3.3, only + # the Distutils version of sysconfig is used for extension module + # builds, which happens earlier in the Distutils tests. This may + # cause the following tests to fail since no tests have caused + # the global version of sysconfig to call the customization yet. + # The solution for now is to simply skip this test in this case. + # The longer-term solution is to only have one version of sysconfig. + + import sysconfig as global_sysconfig + if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'): + return + self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), sysconfig.get_config_var('LDSHARED')) + self.assertEqual(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC')) + + + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(SysconfigTestCase)) diff --git a/unixccompiler.py b/unixccompiler.py index c49ac9ba..2aa1cb1d 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -26,6 +26,9 @@ from distutils.errors import \ DistutilsExecError, CompileError, LibError, LinkError from distutils import log +if sys.platform == 'darwin': + import _osx_support + # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's # Makefile and live with it. Is this adequate? If not, we might @@ -41,68 +44,6 @@ from distutils import log # should just happily stuff them into the preprocessor/compiler/linker # options and carry on. -def _darwin_compiler_fixup(compiler_so, cc_args): - """ - This function will strip '-isysroot PATH' and '-arch ARCH' from the - compile flags if the user has specified one them in extra_compile_flags. - - This is needed because '-arch ARCH' adds another architecture to the - build, without a way to remove an architecture. Furthermore GCC will - barf if multiple '-isysroot' arguments are present. - """ - stripArch = stripSysroot = 0 - - compiler_so = list(compiler_so) - kernel_version = os.uname()[2] # 8.4.3 - major_version = int(kernel_version.split('.')[0]) - - if major_version < 8: - # OSX before 10.4.0, these don't support -arch and -isysroot at - # all. - stripArch = stripSysroot = True - else: - stripArch = '-arch' in cc_args - stripSysroot = '-isysroot' in cc_args - - if stripArch or 'ARCHFLAGS' in os.environ: - while 1: - try: - index = compiler_so.index('-arch') - # Strip this argument and the next one: - del compiler_so[index:index+2] - except ValueError: - break - - if 'ARCHFLAGS' in os.environ and not stripArch: - # User specified different -arch flags in the environ, - # see also distutils.sysconfig - compiler_so = compiler_so + os.environ['ARCHFLAGS'].split() - - if stripSysroot: - try: - index = compiler_so.index('-isysroot') - # Strip this argument and the next one: - del compiler_so[index:index+2] - except ValueError: - pass - - # Check if the SDK that is used during compilation actually exists, - # the universal build requires the usage of a universal SDK and not all - # users have that installed by default. - sysroot = None - if '-isysroot' in cc_args: - idx = cc_args.index('-isysroot') - sysroot = cc_args[idx+1] - elif '-isysroot' in compiler_so: - idx = compiler_so.index('-isysroot') - sysroot = compiler_so[idx+1] - - if sysroot and not os.path.isdir(sysroot): - log.warn("Compiling with an SDK that doesn't seem to exist: %s", - sysroot) - log.warn("Please check your Xcode installation") - - return compiler_so class UnixCCompiler(CCompiler): @@ -172,7 +113,8 @@ class UnixCCompiler(CCompiler): def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): compiler_so = self.compiler_so if sys.platform == 'darwin': - compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs) + compiler_so = _osx_support.compiler_fixup(compiler_so, + cc_args + extra_postargs) try: self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) @@ -251,7 +193,7 @@ class UnixCCompiler(CCompiler): linker[i] = self.compiler_cxx[i] if sys.platform == 'darwin': - linker = _darwin_compiler_fixup(linker, ld_args) + linker = _osx_support.compiler_fixup(linker, ld_args) self.spawn(linker + ld_args) except DistutilsExecError, msg: diff --git a/util.py b/util.py index 0c24e8ca..52794112 100644 --- a/util.py +++ b/util.py @@ -93,94 +93,10 @@ def get_platform (): if m: release = m.group() elif osname[:6] == "darwin": - # - # For our purposes, we'll assume that the system version from - # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set - # to. This makes the compatibility story a bit more sane because the - # machine is going to compile and link as if it were - # MACOSX_DEPLOYMENT_TARGET. - from distutils.sysconfig import get_config_vars - cfgvars = get_config_vars() - - macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') - - if 1: - # Always calculate the release of the running machine, - # needed to determine if we can build fat binaries or not. - - macrelease = macver - # Get the system version. Reading this plist is a documented - # way to get the system version (see the documentation for - # the Gestalt Manager) - try: - f = open('/System/Library/CoreServices/SystemVersion.plist') - except IOError: - # We're on a plain darwin box, fall back to the default - # behaviour. - pass - else: - try: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - if m is not None: - macrelease = '.'.join(m.group(1).split('.')[:2]) - # else: fall back to the default behaviour - finally: - f.close() - - if not macver: - macver = macrelease - - if macver: - from distutils.sysconfig import get_config_vars - release = macver - osname = "macosx" - - if (macrelease + '.') >= '10.4.' and \ - '-arch' in get_config_vars().get('CFLAGS', '').strip(): - # The universal build will build fat binaries, but not on - # systems before 10.4 - # - # Try to detect 4-way universal builds, those have machine-type - # 'universal' instead of 'fat'. - - machine = 'fat' - cflags = get_config_vars().get('CFLAGS') - - archs = re.findall('-arch\s+(\S+)', cflags) - archs = tuple(sorted(set(archs))) - - if len(archs) == 1: - machine = archs[0] - elif archs == ('i386', 'ppc'): - machine = 'fat' - elif archs == ('i386', 'x86_64'): - machine = 'intel' - elif archs == ('i386', 'ppc', 'x86_64'): - machine = 'fat3' - elif archs == ('ppc64', 'x86_64'): - machine = 'fat64' - elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): - machine = 'universal' - else: - raise ValueError( - "Don't know machine value for archs=%r"%(archs,)) - - elif machine == 'i386': - # On OSX the machine type returned by uname is always the - # 32-bit variant, even if the executable architecture is - # the 64-bit variant - if sys.maxint >= 2**32: - machine = 'x86_64' - - elif machine in ('PowerPC', 'Power_Macintosh'): - # Pick a sane name for the PPC architecture. - machine = 'ppc' - - # See 'i386' case - if sys.maxint >= 2**32: - machine = 'ppc64' + import _osx_support, distutils.sysconfig + osname, release, machine = _osx_support.get_platform_osx( + distutils.sysconfig.get_config_vars(), + osname, release, machine) return "%s-%s-%s" % (osname, release, machine) -- cgit v1.2.1 From 2374a4ccde18f6e6f3f20a7279dc0b1a7a2cf89d Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Thu, 31 Jan 2013 01:28:23 -0800 Subject: Issue #13590: OS X Xcode 4 - improve support for universal extension modules In particular, fix extension module build failures when trying to use 32-bit-only installer Pythons on systems with Xcode 4 (currently OS X 10.8, 10.7, and optionally 10.6). * Backport 3.3.0 fixes to 3.2 branch (for release in 3.2.4) * Since Xcode 4 removes ppc support, extension module builds now check for ppc compiler support and by default remove ppc and ppc64 archs when they are not available. * Extension module builds now revert to using system installed headers and libs (/usr and /System/Library) if the SDK used to build the interpreter is not installed or has moved. * Try to avoid building extension modules with deprecated and problematic Apple llvm-gcc compiler. If original compiler is not available, use clang instead by default. --- sysconfig.py | 88 +++++++++++----------------------------------- tests/test_sysconfig.py | 22 +++++++++++- tests/test_util.py | 9 +++++ unixccompiler.py | 70 ++++--------------------------------- util.py | 92 +++---------------------------------------------- 5 files changed, 61 insertions(+), 220 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 16902ca9..b6007a90 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -146,7 +146,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): "I don't know where Python installs its library " "on platform '%s'" % os.name) -_USE_CLANG = None + def customize_compiler(compiler): """Do any platform-specific customization of a CCompiler instance. @@ -155,42 +155,28 @@ def customize_compiler(compiler): varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": + if sys.platform == "darwin": + # Perform first-time customization of compiler-related + # config vars on OS X now that we know we need a compiler. + # This is primarily to support Pythons from binary + # installers. The kind and paths to build tools on + # the user system may vary significantly from the system + # that Python itself was built on. Also the user OS + # version and build tools may not support the same set + # of CPU architectures for universal builds. + global _config_vars + if not _config_vars.get('CUSTOMIZED_OSX_COMPILER', ''): + import _osx_support + _osx_support.customize_compiler(_config_vars) + _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' + (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS') newcc = None if 'CC' in os.environ: - newcc = os.environ['CC'] - elif sys.platform == 'darwin' and cc == 'gcc-4.2': - # Issue #13590: - # Since Apple removed gcc-4.2 in Xcode 4.2, we can no - # longer assume it is available for extension module builds. - # If Python was built with gcc-4.2, check first to see if - # it is available on this system; if not, try to use clang - # instead unless the caller explicitly set CC. - global _USE_CLANG - if _USE_CLANG is None: - from distutils import log - from subprocess import Popen, PIPE - p = Popen("! type gcc-4.2 && type clang && exit 2", - shell=True, stdout=PIPE, stderr=PIPE) - p.wait() - if p.returncode == 2: - _USE_CLANG = True - log.warn("gcc-4.2 not found, using clang instead") - else: - _USE_CLANG = False - if _USE_CLANG: - newcc = 'clang' - if newcc: - # On OS X, if CC is overridden, use that as the default - # command for LDSHARED as well - if (sys.platform == 'darwin' - and 'LDSHARED' not in os.environ - and ldshared.startswith(cc)): - ldshared = newcc + ldshared[len(cc):] - cc = newcc + cc = os.environ['CC'] if 'CXX' in os.environ: cxx = os.environ['CXX'] if 'LDSHARED' in os.environ: @@ -543,43 +529,11 @@ def get_config_vars(*args): srcdir = os.path.join(base, _config_vars['srcdir']) _config_vars['srcdir'] = os.path.normpath(srcdir) + # OS X platforms require special customization to handle + # multi-architecture, multi-os-version installers if sys.platform == 'darwin': - kernel_version = os.uname()[2] # Kernel version (8.4.3) - major_version = int(kernel_version.split('.')[0]) - - if major_version < 8: - # On Mac OS X before 10.4, check if -arch and -isysroot - # are in CFLAGS or LDFLAGS and remove them if they are. - # This is needed when building extensions on a 10.3 system - # using a universal build of python. - for key in ('LDFLAGS', 'BASECFLAGS', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags, re.ASCII) - flags = re.sub('-isysroot [^ \t]*', ' ', flags) - _config_vars[key] = flags - - else: - - # Allow the user to override the architecture flags using - # an environment variable. - # NOTE: This name was introduced by Apple in OSX 10.5 and - # is used by several scripting languages distributed with - # that OS release. - - if 'ARCHFLAGS' in os.environ: - arch = os.environ['ARCHFLAGS'] - for key in ('LDFLAGS', 'BASECFLAGS', - # a number of derived variables. These need to be - # patched up as well. - 'CFLAGS', 'PY_CFLAGS', 'BLDSHARED'): - - flags = _config_vars[key] - flags = re.sub('-arch\s+\w+\s', ' ', flags) - flags = flags + ' ' + arch - _config_vars[key] = flags + import _osx_support + _osx_support.customize_config_vars(_config_vars) if args: vals = [] diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index fbe26bf6..545ef3b5 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -102,7 +102,27 @@ class SysconfigTestCase(support.EnvironGuard, import sysconfig as global_sysconfig self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS')) self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS')) - self.assertEqual(global_sysconfig.get_config_var('LDSHARED'),sysconfig.get_config_var('LDSHARED')) + + @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'),'compiler flags customized') + def test_sysconfig_compiler_vars(self): + # On OS X, binary installers support extension module building on + # various levels of the operating system with differing Xcode + # configurations. This requires customization of some of the + # compiler configuration directives to suit the environment on + # the installed machine. Some of these customizations may require + # running external programs and, so, are deferred until needed by + # the first extension module build. With Python 3.3, only + # the Distutils version of sysconfig is used for extension module + # builds, which happens earlier in the Distutils tests. This may + # cause the following tests to fail since no tests have caused + # the global version of sysconfig to call the customization yet. + # The solution for now is to simply skip this test in this case. + # The longer-term solution is to only have one version of sysconfig. + + import sysconfig as global_sysconfig + if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'): + return + self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), sysconfig.get_config_var('LDSHARED')) self.assertEqual(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC')) diff --git a/tests/test_util.py b/tests/test_util.py index 1a06d4c4..eac9b514 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -13,6 +13,7 @@ from distutils import util # used to patch _environ_checked from distutils.sysconfig import get_config_vars from distutils import sysconfig from distutils.tests import support +import _osx_support class UtilTestCase(support.EnvironGuard, unittest.TestCase): @@ -92,6 +93,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3' get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' @@ -105,6 +107,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sys.maxsize = cursize # macbook with fat binaries (fat, universal or fat64) + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4' get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' @@ -113,10 +116,12 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEqual(get_platform(), 'macosx-10.4-fat') + _osx_support._remove_original_values(get_config_vars()) os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1' self.assertEqual(get_platform(), 'macosx-10.4-fat') + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -124,18 +129,21 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEqual(get_platform(), 'macosx-10.4-intel') + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEqual(get_platform(), 'macosx-10.4-fat3') + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEqual(get_platform(), 'macosx-10.4-universal') + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' @@ -144,6 +152,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertEqual(get_platform(), 'macosx-10.4-fat64') for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): + _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' diff --git a/unixccompiler.py b/unixccompiler.py index c70a3cc5..094a2f0b 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -23,6 +23,9 @@ from distutils.errors import \ DistutilsExecError, CompileError, LibError, LinkError from distutils import log +if sys.platform == 'darwin': + import _osx_support + # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's # Makefile and live with it. Is this adequate? If not, we might @@ -38,68 +41,6 @@ from distutils import log # should just happily stuff them into the preprocessor/compiler/linker # options and carry on. -def _darwin_compiler_fixup(compiler_so, cc_args): - """ - This function will strip '-isysroot PATH' and '-arch ARCH' from the - compile flags if the user has specified one them in extra_compile_flags. - - This is needed because '-arch ARCH' adds another architecture to the - build, without a way to remove an architecture. Furthermore GCC will - barf if multiple '-isysroot' arguments are present. - """ - stripArch = stripSysroot = False - - compiler_so = list(compiler_so) - kernel_version = os.uname()[2] # 8.4.3 - major_version = int(kernel_version.split('.')[0]) - - if major_version < 8: - # OSX before 10.4.0, these don't support -arch and -isysroot at - # all. - stripArch = stripSysroot = True - else: - stripArch = '-arch' in cc_args - stripSysroot = '-isysroot' in cc_args - - if stripArch or 'ARCHFLAGS' in os.environ: - while True: - try: - index = compiler_so.index('-arch') - # Strip this argument and the next one: - del compiler_so[index:index+2] - except ValueError: - break - - if 'ARCHFLAGS' in os.environ and not stripArch: - # User specified different -arch flags in the environ, - # see also distutils.sysconfig - compiler_so = compiler_so + os.environ['ARCHFLAGS'].split() - - if stripSysroot: - try: - index = compiler_so.index('-isysroot') - # Strip this argument and the next one: - del compiler_so[index:index+2] - except ValueError: - pass - - # Check if the SDK that is used during compilation actually exists, - # the universal build requires the usage of a universal SDK and not all - # users have that installed by default. - sysroot = None - if '-isysroot' in cc_args: - idx = cc_args.index('-isysroot') - sysroot = cc_args[idx+1] - elif '-isysroot' in compiler_so: - idx = compiler_so.index('-isysroot') - sysroot = compiler_so[idx+1] - - if sysroot and not os.path.isdir(sysroot): - log.warn("Compiling with an SDK that doesn't seem to exist: %s", - sysroot) - log.warn("Please check your Xcode installation") - - return compiler_so class UnixCCompiler(CCompiler): @@ -168,7 +109,8 @@ class UnixCCompiler(CCompiler): def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): compiler_so = self.compiler_so if sys.platform == 'darwin': - compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs) + compiler_so = _osx_support.compiler_fixup(compiler_so, + cc_args + extra_postargs) try: self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) @@ -247,7 +189,7 @@ class UnixCCompiler(CCompiler): linker[i] = self.compiler_cxx[i] if sys.platform == 'darwin': - linker = _darwin_compiler_fixup(linker, ld_args) + linker = _osx_support.compiler_fixup(linker, ld_args) self.spawn(linker + ld_args) except DistutilsExecError as msg: diff --git a/util.py b/util.py index bce84027..52280db0 100644 --- a/util.py +++ b/util.py @@ -94,94 +94,10 @@ def get_platform (): if m: release = m.group() elif osname[:6] == "darwin": - # - # For our purposes, we'll assume that the system version from - # distutils' perspective is what MACOSX_DEPLOYMENT_TARGET is set - # to. This makes the compatibility story a bit more sane because the - # machine is going to compile and link as if it were - # MACOSX_DEPLOYMENT_TARGET. - from distutils.sysconfig import get_config_vars - cfgvars = get_config_vars() - - macver = cfgvars.get('MACOSX_DEPLOYMENT_TARGET') - - if 1: - # Always calculate the release of the running machine, - # needed to determine if we can build fat binaries or not. - - macrelease = macver - # Get the system version. Reading this plist is a documented - # way to get the system version (see the documentation for - # the Gestalt Manager) - try: - f = open('/System/Library/CoreServices/SystemVersion.plist') - except IOError: - # We're on a plain darwin box, fall back to the default - # behaviour. - pass - else: - try: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - if m is not None: - macrelease = '.'.join(m.group(1).split('.')[:2]) - # else: fall back to the default behaviour - finally: - f.close() - - if not macver: - macver = macrelease - - if macver: - from distutils.sysconfig import get_config_vars - release = macver - osname = "macosx" - - if (macrelease + '.') >= '10.4.' and \ - '-arch' in get_config_vars().get('CFLAGS', '').strip(): - # The universal build will build fat binaries, but not on - # systems before 10.4 - # - # Try to detect 4-way universal builds, those have machine-type - # 'universal' instead of 'fat'. - - machine = 'fat' - cflags = get_config_vars().get('CFLAGS') - - archs = re.findall('-arch\s+(\S+)', cflags) - archs = tuple(sorted(set(archs))) - - if len(archs) == 1: - machine = archs[0] - elif archs == ('i386', 'ppc'): - machine = 'fat' - elif archs == ('i386', 'x86_64'): - machine = 'intel' - elif archs == ('i386', 'ppc', 'x86_64'): - machine = 'fat3' - elif archs == ('ppc64', 'x86_64'): - machine = 'fat64' - elif archs == ('i386', 'ppc', 'ppc64', 'x86_64'): - machine = 'universal' - else: - raise ValueError( - "Don't know machine value for archs=%r"%(archs,)) - - elif machine == 'i386': - # On OSX the machine type returned by uname is always the - # 32-bit variant, even if the executable architecture is - # the 64-bit variant - if sys.maxsize >= 2**32: - machine = 'x86_64' - - elif machine in ('PowerPC', 'Power_Macintosh'): - # Pick a sane name for the PPC architecture. - machine = 'ppc' - - # See 'i386' case - if sys.maxsize >= 2**32: - machine = 'ppc64' + import _osx_support, distutils.sysconfig + osname, release, machine = _osx_support.get_platform_osx( + distutils.sysconfig.get_config_vars(), + osname, release, machine) return "%s-%s-%s" % (osname, release, machine) -- cgit v1.2.1 From 9c2f75cbc7aeb61b3a2aad135472c72cfbc1dd44 Mon Sep 17 00:00:00 2001 From: "doko@python.org" Date: Thu, 31 Jan 2013 23:52:03 +0100 Subject: - Issue #17086: Backport the patches from the 3.3 branch to cross-build the package. --- sysconfig.py | 7 ++++++- util.py | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index daa4dc77..250ef38b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -37,6 +37,11 @@ if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, os.path.pardir)) +# set for cross builds +if "_PYTHON_PROJECT_BASE" in os.environ: + # this is the build directory, at least for posix + project_base = os.path.normpath(os.environ["_PYTHON_PROJECT_BASE"]) + # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use # different (hard-wired) directories. @@ -230,7 +235,7 @@ def get_config_h_filename(): def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: - return os.path.join(os.path.dirname(sys.executable), "Makefile") + return os.path.join(project_base, "Makefile") lib_dir = get_python_lib(plat_specific=1, standard_lib=1) return os.path.join(lib_dir, "config", "Makefile") diff --git a/util.py b/util.py index 52794112..ea6ed8a9 100644 --- a/util.py +++ b/util.py @@ -51,6 +51,10 @@ def get_platform (): return 'win-ia64' return sys.platform + # Set for cross builds explicitly + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_HOST_PLATFORM"] + if os.name != "posix" or not hasattr(os, 'uname'): # XXX what about the architecture? NT is Intel or Alpha, # Mac OS is M68k or PPC, etc. -- cgit v1.2.1 From 4627dd0881a1ab9bafe97aff94867456135d057a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 3 Feb 2013 11:41:19 -0500 Subject: Add alias to restore 2.7.2 compatibility for setup scripts (#13994). The customize_compiler function moved many times during the 2.7 series; in 2.7.3, setup scripts importing this function from ccompiler were broken. This commit restores compatibility without reintroducing the issue that #13994 originally fixed (duplication of the function). A unit test makes little sense here, as distutils tests never do imports in functions, and the fix is very simple. --- ccompiler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ccompiler.py b/ccompiler.py index 7076b933..4907a0aa 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -17,6 +17,8 @@ from distutils.dir_util import mkpath from distutils.dep_util import newer_group from distutils.util import split_quoted, execute from distutils import log +# following import is for backward compatibility +from distutils.sysconfig import customize_compiler class CCompiler: """Abstract base class to define the interface that must be implemented -- cgit v1.2.1 From 9ec4ed534ba8d93a5de4a97267807819fbdf1104 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Mon, 11 Mar 2013 17:56:17 -0400 Subject: Issue #17047: remove doubled words found in 2.7 to 3.4 Lib/*, as reported by Serhiy Storchaka and Matthew Barnett. --- command/install.py | 4 ++-- tests/test_install.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/command/install.py b/command/install.py index f1f3bd5c..b9f1c6c5 100644 --- a/command/install.py +++ b/command/install.py @@ -265,8 +265,8 @@ class install (Command): if self.user and (self.prefix or self.exec_prefix or self.home or self.install_base or self.install_platbase): - raise DistutilsOptionError("can't combine user with with prefix/" - "exec_prefix/home or install_(plat)base") + raise DistutilsOptionError("can't combine user with prefix, " + "exec_prefix/home, or install_(plat)base") # Next, stuff that's wrong (or dubious) only on certain platforms. if os.name != "posix": diff --git a/tests/test_install.py b/tests/test_install.py index f17baa1e..29961617 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -166,7 +166,7 @@ class InstallTestCase(support.TempdirManager, cmd.home = 'home' self.assertRaises(DistutilsOptionError, cmd.finalize_options) - # can't combine user with with prefix/exec_prefix/home or + # can't combine user with prefix/exec_prefix/home or # install_(plat)base cmd.prefix = None cmd.user = 'user' -- cgit v1.2.1 From 6b2cfd3d4e003b9a39b2b3a89a150bce9ecdc250 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Mon, 11 Mar 2013 17:57:08 -0400 Subject: Issue #17047: remove doubled words found in 2.7 to 3.4 Lib/*, as reported by Serhiy Storchaka and Matthew Barnett. --- command/install.py | 4 ++-- tests/test_install.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/command/install.py b/command/install.py index 0161898f..9b1c36af 100644 --- a/command/install.py +++ b/command/install.py @@ -278,8 +278,8 @@ class install(Command): if self.user and (self.prefix or self.exec_prefix or self.home or self.install_base or self.install_platbase): - raise DistutilsOptionError("can't combine user with with prefix/" - "exec_prefix/home or install_(plat)base") + raise DistutilsOptionError("can't combine user with prefix, " + "exec_prefix/home, or install_(plat)base") # Next, stuff that's wrong (or dubious) only on certain platforms. if os.name != "posix": diff --git a/tests/test_install.py b/tests/test_install.py index cb2e1f28..1bd31e24 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -165,7 +165,7 @@ class InstallTestCase(support.TempdirManager, cmd.home = 'home' self.assertRaises(DistutilsOptionError, cmd.finalize_options) - # can't combine user with with prefix/exec_prefix/home or + # can't combine user with prefix/exec_prefix/home or # install_(plat)base cmd.prefix = None cmd.user = 'user' -- cgit v1.2.1 From 5bbae64025d2dd4378bc28a08f311a61c6478565 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 16 Mar 2013 19:48:51 +0200 Subject: #11420: make test suite pass with -B/DONTWRITEBYTECODE set. Initial patch by Thomas Wouters. --- tests/test_bdist_dumb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 1037d821..0ad32d42 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -88,9 +88,9 @@ class BuildDumbTestCase(support.TempdirManager, fp.close() contents = sorted(os.path.basename(fn) for fn in contents) - wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], - 'foo.%s.pyc' % imp.get_tag(), - 'foo.py'] + wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] + if not sys.dont_write_bytecode: + wanted.append('foo.%s.pyc' % imp.get_tag()) self.assertEqual(contents, sorted(wanted)) def test_suite(): -- cgit v1.2.1 From a72d9ae0b5ea2ad5fd4aed4b3745776bede75c7b Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 16 Mar 2013 20:04:44 +0200 Subject: #11420: make test suite pass with -B/DONTWRITEBYTECODE set. Initial patch by Thomas Wouters. --- tests/test_bdist_dumb.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 3378f49e..5db3a850 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -87,8 +87,9 @@ class BuildDumbTestCase(support.TempdirManager, fp.close() contents = sorted(os.path.basename(fn) for fn in contents) - wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], - 'foo.py', 'foo.pyc'] + wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] + if not sys.dont_write_bytecode: + wanted.append('foo.pyc') self.assertEqual(contents, sorted(wanted)) def test_finalize_options(self): -- cgit v1.2.1 From 0c09bf1bc035dfbc0caf1339c05a04cca238fcd6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 18 Mar 2013 15:20:56 -0700 Subject: use the HTTPS for pypi upload --- config.py | 11 ++++++++++- tests/test_config.py | 4 ++-- tests/test_upload.py | 8 ++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/config.py b/config.py index 1fd53346..7439a83a 100644 --- a/config.py +++ b/config.py @@ -21,7 +21,7 @@ password:%s class PyPIRCCommand(Command): """Base command that knows how to handle the .pypirc file """ - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' + DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi' DEFAULT_REALM = 'pypi' repository = None realm = None @@ -83,6 +83,15 @@ class PyPIRCCommand(Command): current[key] = config.get(server, key) else: current[key] = default + + # work around people having "repository" for the "pypi" + # section of their config set to the HTTP (rather than + # HTTPS) URL + if (server == 'pypi' and + repository in (self.DEFAULT_REPOSITORY, 'pypi')): + current['repository'] = self.DEFAULT_REPOSITORY + return current + if (current['server'] == repository or current['repository'] == repository): return current diff --git a/tests/test_config.py b/tests/test_config.py index 525bee94..12593610 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -87,7 +87,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi'), + ('repository', 'https://pypi.python.org/pypi'), ('server', 'server1'), ('username', 'me')] self.assertEqual(config, waited) @@ -96,7 +96,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, config = cmd._read_pypirc() config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi'), + ('repository', 'https://pypi.python.org/pypi'), ('server', 'server-login'), ('username', 'tarek')] self.assertEqual(config, waited) diff --git a/tests/test_upload.py b/tests/test_upload.py index 4c6464a3..d2696866 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -72,11 +72,11 @@ class uploadTestCase(PyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() - self.old_class = httpclient.HTTPConnection - self.conn = httpclient.HTTPConnection = FakeConnection() + self.old_class = httpclient.HTTPSConnection + self.conn = httpclient.HTTPSConnection = FakeConnection() def tearDown(self): - httpclient.HTTPConnection = self.old_class + httpclient.HTTPSConnection = self.old_class super(uploadTestCase, self).tearDown() def test_finalize_options(self): @@ -88,7 +88,7 @@ class uploadTestCase(PyPIRCCommandTestCase): cmd.finalize_options() for attr, waited in (('username', 'me'), ('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi')): + ('repository', 'https://pypi.python.org/pypi')): self.assertEqual(getattr(cmd, attr), waited) def test_saved_password(self): -- cgit v1.2.1 From 156e81825b9ee6d636f84ffdddd25e952347678b Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Thu, 21 Mar 2013 13:21:49 -0700 Subject: - Issue #16754: Fix the incorrect shared library extension on linux. Introduce two makefile macros SHLIB_SUFFIX and EXT_SUFFIX. SO now has the value of SHLIB_SUFFIX again (as in 2.x and 3.1). The SO macro is removed in 3.4. --- command/build_ext.py | 6 +++--- sysconfig.py | 8 +++++--- tests/test_build_ext.py | 8 ++++---- tests/test_install.py | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 34b61bdb..64f634ca 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -667,10 +667,10 @@ class build_ext(Command): if os.name == "os2": ext_path[len(ext_path) - 1] = ext_path[len(ext_path) - 1][:8] # extensions in debug_mode are named 'module_d.pyd' under windows - so_ext = get_config_var('SO') + ext_suffix = get_config_var('EXT_SUFFIX') if os.name == 'nt' and self.debug: - return os.path.join(*ext_path) + '_d' + so_ext - return os.path.join(*ext_path) + so_ext + return os.path.join(*ext_path) + '_d' + ext_suffix + return os.path.join(*ext_path) + ext_suffix def get_export_symbols(self, ext): """Return the list of symbols that a shared extension has to diff --git a/sysconfig.py b/sysconfig.py index b6007a90..dec37f8b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -170,9 +170,9 @@ def customize_compiler(compiler): _osx_support.customize_compiler(_config_vars) _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' - (cc, cxx, opt, cflags, ccshared, ldshared, so_ext, ar, ar_flags) = \ + (cc, cxx, opt, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SO', 'AR', 'ARFLAGS') + 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') newcc = None if 'CC' in os.environ: @@ -211,7 +211,7 @@ def customize_compiler(compiler): linker_exe=cc, archiver=archiver) - compiler.shared_lib_extension = so_ext + compiler.shared_lib_extension = shlib_suffix def get_config_h_filename(): @@ -466,6 +466,7 @@ def _init_nt(): g['INCLUDEPY'] = get_python_inc(plat_specific=0) g['SO'] = '.pyd' + g['EXT_SUFFIX'] = '.pyd' g['EXE'] = ".exe" g['VERSION'] = get_python_version().replace(".", "") g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) @@ -485,6 +486,7 @@ def _init_os2(): g['INCLUDEPY'] = get_python_inc(plat_specific=0) g['SO'] = '.pyd' + g['EXT_SUFFIX'] = '.pyd' g['EXE'] = ".exe" global _config_vars diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 065a6a21..44a9852f 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -318,8 +318,8 @@ class BuildExtTestCase(TempdirManager, finally: os.chdir(old_wd) self.assertTrue(os.path.exists(so_file)) - so_ext = sysconfig.get_config_var('SO') - self.assertTrue(so_file.endswith(so_ext)) + ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') + self.assertTrue(so_file.endswith(ext_suffix)) so_dir = os.path.dirname(so_file) self.assertEqual(so_dir, other_tmp_dir) @@ -328,7 +328,7 @@ class BuildExtTestCase(TempdirManager, cmd.run() so_file = cmd.get_outputs()[0] self.assertTrue(os.path.exists(so_file)) - self.assertTrue(so_file.endswith(so_ext)) + self.assertTrue(so_file.endswith(ext_suffix)) so_dir = os.path.dirname(so_file) self.assertEqual(so_dir, cmd.build_lib) @@ -355,7 +355,7 @@ class BuildExtTestCase(TempdirManager, self.assertEqual(lastdir, 'bar') def test_ext_fullpath(self): - ext = sysconfig.get_config_vars()['SO'] + ext = sysconfig.get_config_var('EXT_SUFFIX') # building lxml.etree inplace #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') #etree_ext = Extension('lxml.etree', [etree_c]) diff --git a/tests/test_install.py b/tests/test_install.py index 1bd31e24..b1901273 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -23,7 +23,7 @@ from distutils.tests import support def _make_ext_name(modname): if os.name == 'nt' and sys.executable.endswith('_d.exe'): modname += '_d' - return modname + sysconfig.get_config_var('SO') + return modname + sysconfig.get_config_var('EXT_SUFFIX') class InstallTestCase(support.TempdirManager, -- cgit v1.2.1 From 98057644b0bde93fc52346011633276b87ccb3a4 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Thu, 21 Mar 2013 15:02:16 -0700 Subject: - Issue #13150: sysconfig no longer parses the Makefile and config.h files when imported, instead doing it at build time. This makes importing sysconfig faster and reduces Python startup time by 20%. --- sysconfig.py | 63 ++++-------------------------------------------------------- 1 file changed, 4 insertions(+), 59 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 250ef38b..0c726d92 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -387,66 +387,11 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" - g = {} - # load the installed Makefile: - try: - filename = get_makefile_filename() - parse_makefile(filename, g) - except IOError, msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # load the installed pyconfig.h: - try: - filename = get_config_h_filename() - parse_config_h(file(filename), g) - except IOError, msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # On AIX, there are wrong paths to the linker scripts in the Makefile - # -- these paths are relative to the Python source, but when installed - # the scripts are in another directory. - if python_build: - g['LDSHARED'] = g['BLDSHARED'] - - elif get_python_version() < '2.1': - # The following two branches are for 1.5.2 compatibility. - if sys.platform == 'aix4': # what about AIX 3.x ? - # Linker script is in the config directory, not in Modules as the - # Makefile says. - python_lib = get_python_lib(standard_lib=1) - ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') - python_exp = os.path.join(python_lib, 'config', 'python.exp') - - g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) - - elif sys.platform == 'beos': - # Linker script is in the config directory. In the Makefile it is - # relative to the srcdir, which after installation no longer makes - # sense. - python_lib = get_python_lib(standard_lib=1) - linkerscript_path = string.split(g['LDSHARED'])[0] - linkerscript_name = os.path.basename(linkerscript_path) - linkerscript = os.path.join(python_lib, 'config', - linkerscript_name) - - # XXX this isn't the right place to do this: adding the Python - # library to the link, if needed, should be in the "build_ext" - # command. (It's also needed for non-MS compilers on Windows, and - # it's taken care of for them by the 'build_ext.get_libraries()' - # method.) - g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % - (linkerscript, PREFIX, get_python_version())) - + # _sysconfigdata is generated at build time, see the sysconfig module + from _sysconfigdata import build_time_vars global _config_vars - _config_vars = g + _config_vars = {} + _config_vars.update(build_time_vars) def _init_nt(): -- cgit v1.2.1 From c02b720fc0e7ec3db46e6fcb61b680be7c18fbff Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 22 Mar 2013 09:37:13 -0500 Subject: backout 66e30c4870bb for breaking OSX (#13150) --- sysconfig.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 0c726d92..250ef38b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -387,11 +387,66 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" - # _sysconfigdata is generated at build time, see the sysconfig module - from _sysconfigdata import build_time_vars + g = {} + # load the installed Makefile: + try: + filename = get_makefile_filename() + parse_makefile(filename, g) + except IOError, msg: + my_msg = "invalid Python installation: unable to open %s" % filename + if hasattr(msg, "strerror"): + my_msg = my_msg + " (%s)" % msg.strerror + + raise DistutilsPlatformError(my_msg) + + # load the installed pyconfig.h: + try: + filename = get_config_h_filename() + parse_config_h(file(filename), g) + except IOError, msg: + my_msg = "invalid Python installation: unable to open %s" % filename + if hasattr(msg, "strerror"): + my_msg = my_msg + " (%s)" % msg.strerror + + raise DistutilsPlatformError(my_msg) + + # On AIX, there are wrong paths to the linker scripts in the Makefile + # -- these paths are relative to the Python source, but when installed + # the scripts are in another directory. + if python_build: + g['LDSHARED'] = g['BLDSHARED'] + + elif get_python_version() < '2.1': + # The following two branches are for 1.5.2 compatibility. + if sys.platform == 'aix4': # what about AIX 3.x ? + # Linker script is in the config directory, not in Modules as the + # Makefile says. + python_lib = get_python_lib(standard_lib=1) + ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') + python_exp = os.path.join(python_lib, 'config', 'python.exp') + + g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) + + elif sys.platform == 'beos': + # Linker script is in the config directory. In the Makefile it is + # relative to the srcdir, which after installation no longer makes + # sense. + python_lib = get_python_lib(standard_lib=1) + linkerscript_path = string.split(g['LDSHARED'])[0] + linkerscript_name = os.path.basename(linkerscript_path) + linkerscript = os.path.join(python_lib, 'config', + linkerscript_name) + + # XXX this isn't the right place to do this: adding the Python + # library to the link, if needed, should be in the "build_ext" + # command. (It's also needed for non-MS compilers on Windows, and + # it's taken care of for them by the 'build_ext.get_libraries()' + # method.) + g['LDSHARED'] = ("%s -L%s/lib -lpython%s" % + (linkerscript, PREFIX, get_python_version())) + global _config_vars - _config_vars = {} - _config_vars.update(build_time_vars) + _config_vars = g def _init_nt(): -- cgit v1.2.1 From 449644ec384bd9ec0e8066bf54a22d19f162977f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 23 Mar 2013 16:02:08 +0100 Subject: Bump to 3.2.4rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b52a9fe6..834d8967 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.3" +__version__ = "3.2.4rc1" #--end constants-- -- cgit v1.2.1 From b262efd97d7ac908bbefc87ad8e5eee19ea8f47a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 23 Mar 2013 16:05:12 +0100 Subject: Bump to 3.3.1rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 345ac4f8..c30da0bb 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0" +__version__ = "3.3.1rc1" #--end constants-- -- cgit v1.2.1 From 447a41bf99cf4b4dd10f41fc91a1ef7d1a94552c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 23 Mar 2013 10:17:29 -0500 Subject: version to 2.7.4rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 036062cc..15b511be 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ __revision__ = "$Id$" # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.3" +__version__ = "2.7.4rc1" #--end constants-- -- cgit v1.2.1 From 51ce4bbdaea029fbf434a0df91c7e0c0e46eab1b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 6 Apr 2013 09:36:20 +0200 Subject: Bump to 3.2.4. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 834d8967..b8f1c164 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.4rc1" +__version__ = "3.2.4" #--end constants-- -- cgit v1.2.1 From d6653309f0330f1e4e239e58226640a70a5c2f5a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 6 Apr 2013 09:40:02 +0200 Subject: Bump to 3.3.1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c30da0bb..f8af1b33 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.1rc1" +__version__ = "3.3.1" #--end constants-- -- cgit v1.2.1 From 036968d741e319006ddfcbeb48d5a22f5da574dc Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Fri, 19 Apr 2013 04:23:09 +0300 Subject: Fix uploadTestCase to work even when HTTPSConnection is not available. --- tests/test_upload.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_upload.py b/tests/test_upload.py index d2696866..4a71ca4a 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -72,13 +72,13 @@ class uploadTestCase(PyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() - self.old_class = httpclient.HTTPSConnection + if hasattr(httpclient, 'HTTPSConnection'): + self.addCleanup(setattr, httpclient, 'HTTPSConnection', + httpclient.HTTPSConnection) + else: + self.addCleanup(delattr, httpclient, 'HTTPSConnection') self.conn = httpclient.HTTPSConnection = FakeConnection() - def tearDown(self): - httpclient.HTTPSConnection = self.old_class - super(uploadTestCase, self).tearDown() - def test_finalize_options(self): # new format -- cgit v1.2.1 From a5f153fd048ccd978ab90f54f940f46d77852f51 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 May 2013 12:28:20 +0200 Subject: Bump to version 3.2.5. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b8f1c164..f9016d6d 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.4" +__version__ = "3.2.5" #--end constants-- -- cgit v1.2.1 From 590f1383d4eb948cafba8ff7bd63acad94531d19 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 May 2013 12:36:07 +0200 Subject: Closes issue #17732: ignore install-directory specific options in distutils.cfg when a venv is active. --- dist.py | 14 +++++++++++- tests/test_dist.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/dist.py b/dist.py index a7025682..f7fac089 100644 --- a/dist.py +++ b/dist.py @@ -343,6 +343,18 @@ Common commands: (see '--help-commands' for more) def parse_config_files(self, filenames=None): from configparser import ConfigParser + # Ignore install directory options if we have a venv + if sys.prefix != sys.base_prefix: + ignore_options = [ + 'install-base', 'install-platbase', 'install-lib', + 'install-platlib', 'install-purelib', 'install-headers', + 'install-scripts', 'install-data', 'prefix', 'exec-prefix', + 'home', 'user', 'root'] + else: + ignore_options = [] + + ignore_options = frozenset(ignore_options) + if filenames is None: filenames = self.find_config_files() @@ -359,7 +371,7 @@ Common commands: (see '--help-commands' for more) opt_dict = self.get_option_dict(section) for opt in options: - if opt != '__name__': + if opt != '__name__' and opt not in ignore_options: val = parser.get(section,opt) opt = opt.replace('-', '_') opt_dict[opt] = (filename, val) diff --git a/tests/test_dist.py b/tests/test_dist.py index 8aaae88c..66c20e27 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -6,6 +6,8 @@ import unittest import warnings import textwrap +from unittest import mock + from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command @@ -18,7 +20,7 @@ class test_dist(Command): user_options = [ ("sample-option=", "S", "help text"), - ] + ] def initialize_options(self): self.sample_option = None @@ -77,6 +79,64 @@ class DistributionTestCase(support.LoggingSilencer, self.assertIsInstance(cmd, test_dist) self.assertEqual(cmd.sample_option, "sometext") + def test_venv_install_options(self): + sys.argv.append("install") + self.addCleanup(os.unlink, TESTFN) + + fakepath = '/somedir' + + with open(TESTFN, "w") as f: + print(("[install]\n" + "install-base = {0}\n" + "install-platbase = {0}\n" + "install-lib = {0}\n" + "install-platlib = {0}\n" + "install-purelib = {0}\n" + "install-headers = {0}\n" + "install-scripts = {0}\n" + "install-data = {0}\n" + "prefix = {0}\n" + "exec-prefix = {0}\n" + "home = {0}\n" + "user = {0}\n" + "root = {0}").format(fakepath), file=f) + + # Base case: Not in a Virtual Environment + with mock.patch.multiple(sys, prefix='/a', base_prefix='/a') as values: + d = self.create_distribution([TESTFN]) + + option_tuple = (TESTFN, fakepath) + + result_dict = { + 'install_base': option_tuple, + 'install_platbase': option_tuple, + 'install_lib': option_tuple, + 'install_platlib': option_tuple, + 'install_purelib': option_tuple, + 'install_headers': option_tuple, + 'install_scripts': option_tuple, + 'install_data': option_tuple, + 'prefix': option_tuple, + 'exec_prefix': option_tuple, + 'home': option_tuple, + 'user': option_tuple, + 'root': option_tuple, + } + + self.assertEqual( + sorted(d.command_options.get('install').keys()), + sorted(result_dict.keys())) + + for (key, value) in d.command_options.get('install').items(): + self.assertEqual(value, result_dict[key]) + + # Test case: In a Virtual Environment + with mock.patch.multiple(sys, prefix='/a', base_prefix='/b') as values: + d = self.create_distribution([TESTFN]) + + for key in result_dict.keys(): + self.assertNotIn(key, d.command_options.get('install', {})) + def test_command_packages_configfile(self): sys.argv.append("build") self.addCleanup(os.unlink, TESTFN) @@ -304,7 +364,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assertIn(user_filename, files, - '%r not found in %r' % (user_filename, files)) + '%r not found in %r' % (user_filename, files)) finally: os.remove(user_filename) -- cgit v1.2.1 From 7c5ce9920a18c33d93d4f8c7091db6b197dcbbe2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 May 2013 12:51:38 +0200 Subject: bump to 3.3.2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f8af1b33..83d7cb7f 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.1" +__version__ = "3.3.2" #--end constants-- -- cgit v1.2.1 From 8e4909b4686ce72b8cc7798927fd75b83f5bcbff Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 May 2013 16:35:30 -0700 Subject: Issue #18080: When building a C extension module on OS X, if the compiler is overriden with the CC environment variable, use the new compiler as the default for linking if LDSHARED is not also overriden. This restores Distutils behavior introduced in 3.2.3 and inadvertently dropped in 3.3.0. --- sysconfig.py | 10 ++++++++-- tests/test_unixccompiler.py | 36 ++++++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index b73503d2..b9479889 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -195,9 +195,15 @@ def customize_compiler(compiler): get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') - newcc = None if 'CC' in os.environ: - cc = os.environ['CC'] + newcc = os.environ['CC'] + if (sys.platform == 'darwin' + and 'LDSHARED' not in os.environ + and ldshared.startswith(cc)): + # On OS X, if CC is overridden, use that as the default + # command for LDSHARED as well + ldshared = newcc + ldshared[len(cc):] + cc = newcc if 'CXX' in os.environ: cxx = os.environ['CXX'] if 'LDSHARED' in os.environ: diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 1bff38e9..a5a63fdd 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -1,7 +1,8 @@ """Tests for distutils.unixccompiler.""" +import os import sys import unittest -from test.support import run_unittest +from test.support import EnvironmentVarGuard, run_unittest from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler @@ -94,7 +95,6 @@ class UnixCCompilerTestCase(unittest.TestCase): sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') - # non-GCC GNULD sys.platform = 'bar' def gcv(v): @@ -115,6 +115,38 @@ class UnixCCompilerTestCase(unittest.TestCase): sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-R/foo') + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') + def test_osx_cc_overrides_ldshared(self): + # Issue #18080: + # ensure that setting CC env variable also changes default linker + def gcv(v): + if v == 'LDSHARED': + return 'gcc-4.2 -bundle -undefined dynamic_lookup ' + return 'gcc-4.2' + sysconfig.get_config_var = gcv + with EnvironmentVarGuard() as env: + env['CC'] = 'my_cc' + del env['LDSHARED'] + sysconfig.customize_compiler(self.cc) + self.assertEqual(self.cc.linker_so[0], 'my_cc') + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') + def test_osx_explict_ldshared(self): + # Issue #18080: + # ensure that setting CC env variable does not change + # explicit LDSHARED setting for linker + def gcv(v): + if v == 'LDSHARED': + return 'gcc-4.2 -bundle -undefined dynamic_lookup ' + return 'gcc-4.2' + sysconfig.get_config_var = gcv + with EnvironmentVarGuard() as env: + env['CC'] = 'my_cc' + env['LDSHARED'] = 'my_ld -bundle -dynamic' + sysconfig.customize_compiler(self.cc) + self.assertEqual(self.cc.linker_so[0], 'my_ld') + + def test_suite(): return unittest.makeSuite(UnixCCompilerTestCase) -- cgit v1.2.1 From e2bc5bcf0766570d98e23ccbfaf77589491cc198 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 13 Jun 2013 20:57:26 -0400 Subject: Issue #18200: Update the stdlib (except tests) to use ModuleNotFoundError. --- archive_util.py | 2 +- ccompiler.py | 7 +++---- dist.py | 9 ++++----- msvccompiler.py | 4 ++-- util.py | 2 +- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/archive_util.py b/archive_util.py index fcda08e2..ba74045d 100644 --- a/archive_util.py +++ b/archive_util.py @@ -9,7 +9,7 @@ import sys try: import zipfile -except ImportError: +except ModuleNotFoundError: zipfile = None diff --git a/ccompiler.py b/ccompiler.py index 911e84dd..bc183fc5 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -3,7 +3,7 @@ Contains CCompiler, an abstract base class that defines the interface for the Distutils compiler abstraction model.""" -import sys, os, re +import importlib, sys, os, re from distutils.errors import * from distutils.spawn import spawn from distutils.file_util import move_file @@ -1013,10 +1013,9 @@ def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0): try: module_name = "distutils." + module_name - __import__ (module_name) - module = sys.modules[module_name] + module = importlib.import_module(module_name) klass = vars(module)[class_name] - except ImportError: + except ModuleNotFoundError: raise DistutilsModuleError( "can't compile C/C++ code: unable to load module '%s'" % \ module_name) diff --git a/dist.py b/dist.py index f7fac089..11f6ff8f 100644 --- a/dist.py +++ b/dist.py @@ -4,11 +4,11 @@ Provides the Distribution class, which represents the module distribution being built/installed/distributed. """ -import sys, os, re +import importlib, sys, os, re try: import warnings -except ImportError: +except ModuleNotFoundError: warnings = None from distutils.errors import * @@ -788,9 +788,8 @@ Common commands: (see '--help-commands' for more) klass_name = command try: - __import__ (module_name) - module = sys.modules[module_name] - except ImportError: + module = importlib.import_module(module_name) + except ModuleNotFoundError: continue try: diff --git a/msvccompiler.py b/msvccompiler.py index 81166569..9a94b41a 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -28,7 +28,7 @@ try: RegEnumValue = winreg.EnumValue RegError = winreg.error -except ImportError: +except ModuleNotFoundError: try: import win32api import win32con @@ -39,7 +39,7 @@ except ImportError: RegEnumKey = win32api.RegEnumKey RegEnumValue = win32api.RegEnumValue RegError = win32api.error - except ImportError: + except ModuleNotFoundError: log.info("Warning: Can't read registry to find the " "necessary compiler setting\n" "Make sure that Python modules winreg, " diff --git a/util.py b/util.py index a2f95445..e241f59b 100644 --- a/util.py +++ b/util.py @@ -388,7 +388,7 @@ def byte_compile (py_files, try: from tempfile import mkstemp (script_fd, script_name) = mkstemp(".py") - except ImportError: + except ModuleNotFoundError: from tempfile import mktemp (script_fd, script_name) = None, mktemp(".py") log.info("writing byte-compilation script '%s'", script_name) -- cgit v1.2.1 From dc3634843234941af85a87b89458982f131e56a2 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 15 Jun 2013 12:59:53 -0400 Subject: Issue #17177: Stop using imp in distutils --- command/build_py.py | 10 +++++----- command/install_lib.py | 6 +++--- tests/test_bdist_dumb.py | 3 +-- tests/test_build_py.py | 10 ++++++---- tests/test_install.py | 4 ++-- tests/test_install_lib.py | 8 +++++--- util.py | 7 ++++--- 7 files changed, 26 insertions(+), 22 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 1371b3d6..677723f0 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -3,7 +3,7 @@ Implements the Distutils 'build_py' command.""" import os -import imp +import importlib.util import sys from glob import glob @@ -312,11 +312,11 @@ class build_py (Command): outputs.append(filename) if include_bytecode: if self.compile: - outputs.append(imp.cache_from_source(filename, - debug_override=True)) + outputs.append(importlib.util.cache_from_source( + filename, debug_override=True)) if self.optimize > 0: - outputs.append(imp.cache_from_source(filename, - debug_override=False)) + outputs.append(importlib.util.cache_from_source( + filename, debug_override=False)) outputs += [ os.path.join(build_dir, filename) diff --git a/command/install_lib.py b/command/install_lib.py index 15c08f12..215813ba 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -4,7 +4,7 @@ Implements the Distutils 'install_lib' command (install all Python modules).""" import os -import imp +import importlib.util import sys from distutils.core import Command @@ -165,10 +165,10 @@ class install_lib(Command): if ext != PYTHON_SOURCE_EXTENSION: continue if self.compile: - bytecode_files.append(imp.cache_from_source( + bytecode_files.append(importlib.util.cache_from_source( py_file, debug_override=True)) if self.optimize > 0: - bytecode_files.append(imp.cache_from_source( + bytecode_files.append(importlib.util.cache_from_source( py_file, debug_override=False)) return bytecode_files diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index 73061668..c8ccdc23 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -1,7 +1,6 @@ """Tests for distutils.command.bdist_dumb.""" import os -import imp import sys import zipfile import unittest @@ -88,7 +87,7 @@ class BuildDumbTestCase(support.TempdirManager, contents = sorted(os.path.basename(fn) for fn in contents) wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] if not sys.dont_write_bytecode: - wanted.append('foo.%s.pyc' % imp.get_tag()) + wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) self.assertEqual(contents, sorted(wanted)) def test_suite(): diff --git a/tests/test_build_py.py b/tests/test_build_py.py index e416edd4..1b410c39 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -2,7 +2,6 @@ import os import sys -import imp import unittest from distutils.command.build_py import build_py @@ -63,7 +62,8 @@ class BuildPyTestCase(support.TempdirManager, self.assertFalse(os.path.exists(pycache_dir)) else: pyc_files = os.listdir(pycache_dir) - self.assertIn("__init__.%s.pyc" % imp.get_tag(), pyc_files) + self.assertIn("__init__.%s.pyc" % sys.implementation.cache_tag, + pyc_files) def test_empty_package_dir(self): # See bugs #1668596/#1720897 @@ -102,7 +102,8 @@ class BuildPyTestCase(support.TempdirManager, found = os.listdir(cmd.build_lib) self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) - self.assertEqual(found, ['boiledeggs.%s.pyc' % imp.get_tag()]) + self.assertEqual(found, + ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]) @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') def test_byte_compile_optimized(self): @@ -119,7 +120,8 @@ class BuildPyTestCase(support.TempdirManager, found = os.listdir(cmd.build_lib) self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) - self.assertEqual(sorted(found), ['boiledeggs.%s.pyo' % imp.get_tag()]) + self.assertEqual(sorted(found), + ['boiledeggs.%s.pyo' % sys.implementation.cache_tag]) def test_dont_write_bytecode(self): # makes sure byte_compile is not used diff --git a/tests/test_install.py b/tests/test_install.py index e9b26427..42bd269b 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -1,7 +1,6 @@ """Tests for distutils.command.install.""" import os -import imp import sys import unittest import site @@ -193,7 +192,8 @@ class InstallTestCase(support.TempdirManager, f.close() found = [os.path.basename(line) for line in content.splitlines()] - expected = ['hello.py', 'hello.%s.pyc' % imp.get_tag(), 'sayhi', + expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag, + 'sayhi', 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 2bd4dc6e..dbb3e9a6 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -1,7 +1,7 @@ """Tests for distutils.command.install_data.""" import sys import os -import imp +import importlib.util import unittest from distutils.command.install_lib import install_lib @@ -44,8 +44,10 @@ class InstallLibTestCase(support.TempdirManager, f = os.path.join(project_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - pyc_file = imp.cache_from_source('foo.py', debug_override=True) - pyo_file = imp.cache_from_source('foo.py', debug_override=False) + pyc_file = importlib.util.cache_from_source('foo.py', + debug_override=True) + pyo_file = importlib.util.cache_from_source('foo.py', + debug_override=False) self.assertTrue(os.path.exists(pyc_file)) self.assertTrue(os.path.exists(pyo_file)) diff --git a/util.py b/util.py index e241f59b..257de68e 100644 --- a/util.py +++ b/util.py @@ -6,7 +6,7 @@ one of the other *util.py modules. import os import re -import imp +import importlib.util import sys import string from distutils.errors import DistutilsPlatformError @@ -453,9 +453,10 @@ byte_compile(files, optimize=%r, force=%r, # cfile - byte-compiled file # dfile - purported source filename (same as 'file' by default) if optimize >= 0: - cfile = imp.cache_from_source(file, debug_override=not optimize) + cfile = importlib.util.cache_from_source( + file, debug_override=not optimize) else: - cfile = imp.cache_from_source(file) + cfile = importlib.util.cache_from_source(file) dfile = file if prefix: if file[:len(prefix)] != prefix: -- cgit v1.2.1 From 32a9bf9e2e195447cb3d995e9d8778e8a8d5573e Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 4 Jul 2013 17:43:24 -0400 Subject: Issue #18200: Back out usage of ModuleNotFoundError (8d28d44f3a9a) --- archive_util.py | 2 +- ccompiler.py | 7 ++++--- dist.py | 9 +++++---- msvccompiler.py | 4 ++-- util.py | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/archive_util.py b/archive_util.py index ba74045d..fcda08e2 100644 --- a/archive_util.py +++ b/archive_util.py @@ -9,7 +9,7 @@ import sys try: import zipfile -except ModuleNotFoundError: +except ImportError: zipfile = None diff --git a/ccompiler.py b/ccompiler.py index bc183fc5..911e84dd 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -3,7 +3,7 @@ Contains CCompiler, an abstract base class that defines the interface for the Distutils compiler abstraction model.""" -import importlib, sys, os, re +import sys, os, re from distutils.errors import * from distutils.spawn import spawn from distutils.file_util import move_file @@ -1013,9 +1013,10 @@ def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0): try: module_name = "distutils." + module_name - module = importlib.import_module(module_name) + __import__ (module_name) + module = sys.modules[module_name] klass = vars(module)[class_name] - except ModuleNotFoundError: + except ImportError: raise DistutilsModuleError( "can't compile C/C++ code: unable to load module '%s'" % \ module_name) diff --git a/dist.py b/dist.py index 11f6ff8f..f7fac089 100644 --- a/dist.py +++ b/dist.py @@ -4,11 +4,11 @@ Provides the Distribution class, which represents the module distribution being built/installed/distributed. """ -import importlib, sys, os, re +import sys, os, re try: import warnings -except ModuleNotFoundError: +except ImportError: warnings = None from distutils.errors import * @@ -788,8 +788,9 @@ Common commands: (see '--help-commands' for more) klass_name = command try: - module = importlib.import_module(module_name) - except ModuleNotFoundError: + __import__ (module_name) + module = sys.modules[module_name] + except ImportError: continue try: diff --git a/msvccompiler.py b/msvccompiler.py index 9a94b41a..81166569 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -28,7 +28,7 @@ try: RegEnumValue = winreg.EnumValue RegError = winreg.error -except ModuleNotFoundError: +except ImportError: try: import win32api import win32con @@ -39,7 +39,7 @@ except ModuleNotFoundError: RegEnumKey = win32api.RegEnumKey RegEnumValue = win32api.RegEnumValue RegError = win32api.error - except ModuleNotFoundError: + except ImportError: log.info("Warning: Can't read registry to find the " "necessary compiler setting\n" "Make sure that Python modules winreg, " diff --git a/util.py b/util.py index 257de68e..efb3834c 100644 --- a/util.py +++ b/util.py @@ -388,7 +388,7 @@ def byte_compile (py_files, try: from tempfile import mkstemp (script_fd, script_name) = mkstemp(".py") - except ModuleNotFoundError: + except ImportError: from tempfile import mktemp (script_fd, script_name) = None, mktemp(".py") log.info("writing byte-compilation script '%s'", script_name) -- cgit v1.2.1 From 814117ef8e4aa624fc497ce1c47040f68d07daba Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 3 Aug 2013 12:58:12 -0700 Subject: Bumped version to 3.4.0a1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 70a72b09..f4076d74 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0a0" +__version__ = "3.4.0a1" #--end constants-- -- cgit v1.2.1 From 82af3811567b3e375d91163f5a0a706b2ecdc9c3 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 17 Aug 2013 16:11:40 +0300 Subject: =?UTF-8?q?#18741:=20fix=20more=20typos.=20=20Patch=20by=20F=C3=A9?= =?UTF-8?q?vry=20Thibault.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- command/install.py | 2 +- command/sdist.py | 2 +- tests/test_build_clib.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/command/install.py b/command/install.py index 9b1c36af..3c675d11 100644 --- a/command/install.py +++ b/command/install.py @@ -545,7 +545,7 @@ class install(Command): self.extra_dirs = extra_dirs def change_roots(self, *names): - """Change the install direcories pointed by name using root.""" + """Change the install directories pointed by name using root.""" for name in names: attr = "install_" + name setattr(self, attr, change_root(self.root, getattr(self, attr))) diff --git a/command/sdist.py b/command/sdist.py index a9429a42..116f67ef 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -175,7 +175,7 @@ class sdist(Command): depends on the user's options. """ # new behavior when using a template: - # the file list is recalculated everytime because + # the file list is recalculated every time because # even if MANIFEST.in or setup.py are not changed # the user might have added some files in the tree that # need to be included. diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 69bd2bf6..ee1c0416 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -77,7 +77,7 @@ class BuildCLibTestCase(support.TempdirManager, cmd.compiler = FakeCompiler() - # build_libraries is also doing a bit of typoe checking + # build_libraries is also doing a bit of typo checking lib = [('name', {'sources': 'notvalid'})] self.assertRaises(DistutilsSetupError, cmd.build_libraries, lib) -- cgit v1.2.1 From 6200cf0997fe3b832b4fda03a4a1cc0d6347c151 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 7 Sep 2013 23:42:07 +1200 Subject: Version number bump for Python 3.4.0a2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f4076d74..3afd84fe 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0a1" +__version__ = "3.4.0a2" #--end constants-- -- cgit v1.2.1 From d30e05735221377ec2823a2b2942375194753d35 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 28 Sep 2013 23:51:00 +0100 Subject: Version bump to 3.4.0a3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 3afd84fe..cc188935 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0a2" +__version__ = "3.4.0a3" #--end constants-- -- cgit v1.2.1 From 6e02dbc3db2e648c23ecbbafc81dff296e8cd262 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 29 Sep 2013 01:48:40 +0200 Subject: Issue #4366: Fix building extensions on all platforms when --enable-shared is used. --- command/build_ext.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 1ad0d5ff..bc6a23f1 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -247,11 +247,10 @@ class build_ext(Command): # building python standard extensions self.library_dirs.append('.') - # for extensions under Linux or Solaris with a shared Python library, + # For building extensions with a shared Python library, # Python's library directory must be appended to library_dirs - sysconfig.get_config_var('Py_ENABLE_SHARED') - if (sys.platform.startswith(('linux', 'gnu', 'sunos')) - and sysconfig.get_config_var('Py_ENABLE_SHARED')): + # See Issues: #1600860, #4366 + if (sysconfig.get_config_var('Py_ENABLE_SHARED')): if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) -- cgit v1.2.1 From f67dd5be98be8295fb480eb9180b730f53b3e95d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 29 Sep 2013 11:13:27 -0400 Subject: condense two tests with the same name (closes #19114) --- tests/test_cmd.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/tests/test_cmd.py b/tests/test_cmd.py index 195045cc..cf5197c3 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -34,6 +34,18 @@ class CommandTestCase(unittest.TestCase): self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, 'not_string_list2') + cmd.option1 = 'ok,dok' + cmd.ensure_string_list('option1') + self.assertEqual(cmd.option1, ['ok', 'dok']) + + cmd.option2 = ['xxx', 'www'] + cmd.ensure_string_list('option2') + + cmd.option3 = ['ok', 2] + self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, + 'option3') + + def test_make_file(self): cmd = self.cmd @@ -77,19 +89,6 @@ class CommandTestCase(unittest.TestCase): cmd.option3 = 1 self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') - def test_ensure_string_list(self): - cmd = self.cmd - cmd.option1 = 'ok,dok' - cmd.ensure_string_list('option1') - self.assertEqual(cmd.option1, ['ok', 'dok']) - - cmd.option2 = ['xxx', 'www'] - cmd.ensure_string_list('option2') - - cmd.option3 = ['ok', 2] - self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, - 'option3') - def test_ensure_filename(self): cmd = self.cmd cmd.option1 = __file__ -- cgit v1.2.1 From 5cc44b40db683dc8402c77d5c7c13e726a4f7dec Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Mon, 30 Sep 2013 22:28:10 +0200 Subject: Issue #12641: Avoid passing "-mno-cygwin" to the mingw32 compiler, except when necessary. Patch by Oscar Benjamin. --- cygwinccompiler.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 0bdd539c..e0074a18 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -48,7 +48,7 @@ cygwin in no-cygwin mode). import os import sys import copy -from subprocess import Popen, PIPE +from subprocess import Popen, PIPE, check_output import re from distutils.ccompiler import gen_preprocess_options, gen_lib_options @@ -294,13 +294,18 @@ class Mingw32CCompiler(CygwinCCompiler): else: entry_point = '' - self.set_executables(compiler='gcc -mno-cygwin -O -Wall', - compiler_so='gcc -mno-cygwin -mdll -O -Wall', - compiler_cxx='g++ -mno-cygwin -O -Wall', - linker_exe='gcc -mno-cygwin', - linker_so='%s -mno-cygwin %s %s' - % (self.linker_dll, shared_option, - entry_point)) + if self.gcc_version < '4' or is_cygwingcc(): + no_cygwin = ' -mno-cygwin' + else: + no_cygwin = '' + + self.set_executables(compiler='gcc%s -O -Wall' % no_cygwin, + compiler_so='gcc%s -mdll -O -Wall' % no_cygwin, + compiler_cxx='g++%s -O -Wall' % no_cygwin, + linker_exe='gcc%s' % no_cygwin, + linker_so='%s%s %s %s' + % (self.linker_dll, no_cygwin, + shared_option, entry_point)) # Maybe we should also append -mthreads, but then the finished # dlls need another dll (mingwm10.dll see Mingw32 docs) # (-mthreads: Support thread-safe exception handling on `Mingw32') @@ -393,3 +398,8 @@ def get_versions(): """ commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] return tuple([_find_exe_version(cmd) for cmd in commands]) + +def is_cygwingcc(): + '''Try to determine if the gcc that would be used is from cygwin.''' + out_string = check_output(['gcc', '-dumpmachine']) + return out_string.strip().endswith(b'cygwin') -- cgit v1.2.1 From 1c073f36e3b5850a448d65d90c383a92ddce83ff Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 20 Oct 2013 02:01:29 -0700 Subject: Version bump for 3.4.0a4. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index cc188935..355542ed 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0a3" +__version__ = "3.4.0a4" #--end constants-- -- cgit v1.2.1 From 6302a7c47d5c17bd2b2b51e4e604e2c3dc9378c9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 27 Oct 2013 09:22:59 +0100 Subject: Bump to 3.3.3rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 83d7cb7f..812d8f35 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.2" +__version__ = "3.3.3rc1" #--end constants-- -- cgit v1.2.1 From bdb93d0d82c82df219172499741eb33e22da359e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Nov 2013 11:29:33 -0400 Subject: Issue #19286: Adding test demonstrating the failure when a directory is found in the package_data globs. --- tests/test_build_py.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index e416edd4..2ce9d449 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -121,6 +121,37 @@ class BuildPyTestCase(support.TempdirManager, found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) self.assertEqual(sorted(found), ['boiledeggs.%s.pyo' % imp.get_tag()]) + def test_dir_in_package_data(self): + """ + A directory in package_data should not be added to the filelist. + """ + # See bug 19286 + sources = self.mkdtemp() + pkg_dir = os.path.join(sources, "pkg") + + os.mkdir(pkg_dir) + open(os.path.join(pkg_dir, "__init__.py"), "w").close() + + docdir = os.path.join(pkg_dir, "doc") + os.mkdir(docdir) + open(os.path.join(docdir, "testfile"), "w").close() + + # create the directory that could be incorrectly detected as a file + os.mkdir(os.path.join(docdir, 'otherdir')) + + os.chdir(sources) + dist = Distribution({"packages": ["pkg"], + "package_data": {"pkg": ["doc/*"]}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.script_args = ["build"] + dist.parse_command_line() + + try: + dist.run_commands() + except DistutilsFileError: + self.fail("failed package_data when data dir includes a dir") + def test_dont_write_bytecode(self): # makes sure byte_compile is not used dist = self.create_dist()[1] -- cgit v1.2.1 From 35249e2c9b4afa5baf6699573c3900e442f0910f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Nov 2013 11:07:35 -0400 Subject: Issue #19286: [distutils] Only match files in build_py.find_data_files. --- command/build_py.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_py.py b/command/build_py.py index 1371b3d6..d48eb699 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -127,7 +127,8 @@ class build_py (Command): # Each pattern has to be converted to a platform-specific path filelist = glob(os.path.join(src_dir, convert_path(pattern))) # Files that match more than one pattern are only added once - files.extend([fn for fn in filelist if fn not in files]) + files.extend([fn for fn in filelist if fn not in files + and os.path.isfile(fn)]) return files def build_package_data(self): -- cgit v1.2.1 From 60e84ced7d203fd12f5784889aeb80343d33b9b6 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 4 Nov 2013 07:43:32 +0100 Subject: Backout d80207d15294. --- tests/test_build_py.py | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 2ce9d449..e416edd4 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -121,37 +121,6 @@ class BuildPyTestCase(support.TempdirManager, found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) self.assertEqual(sorted(found), ['boiledeggs.%s.pyo' % imp.get_tag()]) - def test_dir_in_package_data(self): - """ - A directory in package_data should not be added to the filelist. - """ - # See bug 19286 - sources = self.mkdtemp() - pkg_dir = os.path.join(sources, "pkg") - - os.mkdir(pkg_dir) - open(os.path.join(pkg_dir, "__init__.py"), "w").close() - - docdir = os.path.join(pkg_dir, "doc") - os.mkdir(docdir) - open(os.path.join(docdir, "testfile"), "w").close() - - # create the directory that could be incorrectly detected as a file - os.mkdir(os.path.join(docdir, 'otherdir')) - - os.chdir(sources) - dist = Distribution({"packages": ["pkg"], - "package_data": {"pkg": ["doc/*"]}}) - # script_name need not exist, it just need to be initialized - dist.script_name = os.path.join(sources, "setup.py") - dist.script_args = ["build"] - dist.parse_command_line() - - try: - dist.run_commands() - except DistutilsFileError: - self.fail("failed package_data when data dir includes a dir") - def test_dont_write_bytecode(self): # makes sure byte_compile is not used dist = self.create_dist()[1] -- cgit v1.2.1 From abe7d6ae5236f5d11f76e9f3b8c4665dda6a29f1 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 4 Nov 2013 07:43:41 +0100 Subject: Backout 265d369ad3b9. --- command/build_py.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index d48eb699..1371b3d6 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -127,8 +127,7 @@ class build_py (Command): # Each pattern has to be converted to a platform-specific path filelist = glob(os.path.join(src_dir, convert_path(pattern))) # Files that match more than one pattern are only added once - files.extend([fn for fn in filelist if fn not in files - and os.path.isfile(fn)]) + files.extend([fn for fn in filelist if fn not in files]) return files def build_package_data(self): -- cgit v1.2.1 From b5e1e144277c1b90052d1e29451877e599b00fbd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Nov 2013 20:28:18 -0500 Subject: Fix failing test incorrectly merged in b1244046f37a --- tests/test_upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_upload.py b/tests/test_upload.py index c640b2ac..7d704cf1 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -117,7 +117,7 @@ class uploadTestCase(PyPIRCCommandTestCase): self.assert_(headers['Content-type'].startswith('multipart/form-data')) self.assertEquals(self.last_open.req.get_method(), 'POST') self.assertEquals(self.last_open.req.get_full_url(), - 'http://pypi.python.org/pypi') + 'https://pypi.python.org/pypi') self.assert_(b'xxx' in self.last_open.req.data) def test_suite(): -- cgit v1.2.1 From 5f1980bbc9dc1da41e154a2ed0bf988d91c4c7d6 Mon Sep 17 00:00:00 2001 From: Andrew Kuchling Date: Fri, 15 Nov 2013 13:01:52 -0500 Subject: Issue #19544 and Issue #6516: Restore support for --user and --group parameters to sdist command as found in Python 2.7 and originally slated for Python 3.2 but accidentally rolled back as part of the distutils2 rollback. Closes Issue #6516. --- archive_util.py | 71 ++++++++++++++++++++++++++++++++++++++++++---- cmd.py | 6 ++-- command/bdist.py | 13 +++++++++ command/bdist_dumb.py | 11 ++++++- command/sdist.py | 9 +++++- tests/test_archive_util.py | 61 ++++++++++++++++++++++++++++++++++++++- tests/test_sdist.py | 53 ++++++++++++++++++++++++++++++++++ 7 files changed, 214 insertions(+), 10 deletions(-) diff --git a/archive_util.py b/archive_util.py index fcda08e2..306ef6a8 100644 --- a/archive_util.py +++ b/archive_util.py @@ -18,15 +18,55 @@ from distutils.spawn import spawn from distutils.dir_util import mkpath from distutils import log -def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): +try: + from pwd import getpwnam +except AttributeError: + getpwnam = None + +try: + from grp import getgrnam +except AttributeError: + getgrnam = None + +def _get_gid(name): + """Returns a gid, given a group name.""" + if getgrnam is None or name is None: + return None + try: + result = getgrnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _get_uid(name): + """Returns an uid, given a user name.""" + if getpwnam is None or name is None: + return None + try: + result = getpwnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, + owner=None, group=None): """Create a (possibly compressed) tar file from all the files under 'base_dir'. 'compress' must be "gzip" (the default), "compress", "bzip2", or None. - Both "tar" and the compression utility named by 'compress' must be on - the default program search path, so this is probably Unix-specific. + (compress will be deprecated in Python 3.2) + + 'owner' and 'group' can be used to define an owner and a group for the + archive that is being built. If not provided, the current owner and group + will be used. + The output tar file will be named 'base_dir' + ".tar", possibly plus the appropriate compression extension (".gz", ".bz2" or ".Z"). + Returns the output filename. """ tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''} @@ -48,10 +88,23 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): import tarfile # late import so Python build itself doesn't break log.info('Creating tar archive') + + uid = _get_uid(owner) + gid = _get_gid(group) + + def _set_uid_gid(tarinfo): + if gid is not None: + tarinfo.gid = gid + tarinfo.gname = group + if uid is not None: + tarinfo.uid = uid + tarinfo.uname = owner + return tarinfo + if not dry_run: tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) try: - tar.add(base_dir) + tar.add(base_dir, filter=_set_uid_gid) finally: tar.close() @@ -140,7 +193,7 @@ def check_archive_formats(formats): return None def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, - dry_run=0): + dry_run=0, owner=None, group=None): """Create an archive file (eg. zip or tar). 'base_name' is the name of the file to create, minus any format-specific @@ -153,6 +206,9 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, ie. 'base_dir' will be the common prefix of all files and directories in the archive. 'root_dir' and 'base_dir' both default to the current directory. Returns the name of the archive file. + + 'owner' and 'group' are used when creating a tar archive. By default, + uses the current owner and group. """ save_cwd = os.getcwd() if root_dir is not None: @@ -174,6 +230,11 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, func = format_info[0] for arg, val in format_info[1]: kwargs[arg] = val + + if format != 'zip': + kwargs['owner'] = owner + kwargs['group'] = group + try: filename = func(base_name, base_dir, **kwargs) finally: diff --git a/cmd.py b/cmd.py index 3ea08101..c89d5efc 100644 --- a/cmd.py +++ b/cmd.py @@ -365,9 +365,11 @@ class Command: from distutils.spawn import spawn spawn(cmd, search_path, dry_run=self.dry_run) - def make_archive(self, base_name, format, root_dir=None, base_dir=None): + def make_archive(self, base_name, format, root_dir=None, base_dir=None, + owner=None, group=None): return archive_util.make_archive(base_name, format, root_dir, base_dir, - dry_run=self.dry_run) + dry_run=self.dry_run, + owner=owner, group=group) def make_file(self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): diff --git a/command/bdist.py b/command/bdist.py index 38b169af..6814a1c3 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -37,6 +37,12 @@ class bdist(Command): "[default: dist]"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), ] boolean_options = ['skip-build'] @@ -77,6 +83,8 @@ class bdist(Command): self.formats = None self.dist_dir = None self.skip_build = 0 + self.group = None + self.owner = None def finalize_options(self): # have to finalize 'plat_name' before 'bdist_base' @@ -122,6 +130,11 @@ class bdist(Command): if cmd_name not in self.no_format_option: sub_cmd.format = self.formats[i] + # passing the owner and group names for tar archiving + if cmd_name == 'bdist_dumb': + sub_cmd.owner = self.owner + sub_cmd.group = self.group + # If we're going to need to run this command again, tell it to # keep its temporary files around so subsequent runs go faster. if cmd_name in commands[i+1:]: diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index eefdfea5..4405d12c 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -33,6 +33,12 @@ class bdist_dumb(Command): ('relative', None, "build the archive using relative paths" "(default: false)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), ] boolean_options = ['keep-temp', 'skip-build', 'relative'] @@ -48,6 +54,8 @@ class bdist_dumb(Command): self.dist_dir = None self.skip_build = None self.relative = 0 + self.owner = None + self.group = None def finalize_options(self): if self.bdist_dir is None: @@ -101,7 +109,8 @@ class bdist_dumb(Command): # Make the archive filename = self.make_archive(pseudoinstall_root, - self.format, root_dir=archive_root) + self.format, root_dir=archive_root, + owner=self.owner, group=self.group) if self.distribution.has_ext_modules(): pyversion = get_python_version() else: diff --git a/command/sdist.py b/command/sdist.py index 116f67ef..7ea3d5fa 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -74,6 +74,10 @@ class sdist(Command): ('metadata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), + ('owner=', 'u', + "Owner name used when creating a tar file [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file [default: current group]"), ] boolean_options = ['use-defaults', 'prune', @@ -113,6 +117,8 @@ class sdist(Command): self.archive_files = None self.metadata_check = 1 + self.owner = None + self.group = None def finalize_options(self): if self.manifest is None: @@ -444,7 +450,8 @@ class sdist(Command): self.formats.append(self.formats.pop(self.formats.index('tar'))) for fmt in self.formats: - file = self.make_archive(base_name, fmt, base_dir=base_dir) + file = self.make_archive(base_name, fmt, base_dir=base_dir, + owner=self.owner, group=self.group) archive_files.append(file) self.distribution.dist_files.append(('sdist', '', file)) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 1afdd462..581c0cc8 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -15,6 +15,13 @@ from distutils.spawn import find_executable, spawn from distutils.tests import support from test.support import check_warnings, run_unittest, patch +try: + import grp + import pwd + UID_GID_SUPPORT = True +except ImportError: + UID_GID_SUPPORT = False + try: import zipfile ZIP_SUPPORT = True @@ -77,7 +84,7 @@ class ArchiveUtilTestCase(support.TempdirManager, tmpdir2 = self.mkdtemp() unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], - "Source and target should be on same drive") + "source and target should be on same drive") base_name = os.path.join(tmpdir2, target_name) @@ -275,6 +282,58 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: del ARCHIVE_FORMATS['xxx'] + def test_make_archive_owner_group(self): + # testing make_archive with owner and group, with various combinations + # this works even if there's not gid/uid support + if UID_GID_SUPPORT: + group = grp.getgrgid(0)[0] + owner = pwd.getpwuid(0)[0] + else: + group = owner = 'root' + + base_dir, root_dir, base_name = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, + group=group) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'zip', root_dir, base_dir) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'tar', root_dir, base_dir, + owner=owner, group=group) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'tar', root_dir, base_dir, + owner='kjhkjhkjg', group='oihohoh') + self.assertTrue(os.path.exists(res)) + + @unittest.skipUnless(zlib, "Requires zlib") + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + def test_tarfile_root_owner(self): + tmpdir, tmpdir2, base_name = self._create_files() + old_dir = os.getcwd() + os.chdir(tmpdir) + group = grp.getgrgid(0)[0] + owner = pwd.getpwuid(0)[0] + try: + archive_name = make_tarball(base_name, 'dist', compress=None, + owner=owner, group=group) + finally: + os.chdir(old_dir) + + # check if the compressed tarball was created + self.assertTrue(os.path.exists(archive_name)) + + # now checks the rights + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEquals(member.uid, 0) + self.assertEquals(member.gid, 0) + finally: + archive.close() + def test_suite(): return unittest.makeSuite(ArchiveUtilTestCase) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index e6359d6a..6170a48f 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -14,6 +14,12 @@ try: except ImportError: ZLIB_SUPPORT = False +try: + import grp + import pwd + UID_GID_SUPPORT = True +except ImportError: + UID_GID_SUPPORT = False from distutils.command.sdist import sdist, show_formats from distutils.core import Distribution @@ -425,6 +431,53 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO', 'fake-1.0/README.manual']) + @unittest.skipUnless(zlib, "requires zlib") + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + def test_make_distribution_owner_group(self): + + # check if tar and gzip are installed + if (find_executable('tar') is None or + find_executable('gzip') is None): + return + + # now building a sdist + dist, cmd = self.get_cmd() + + # creating a gztar and specifying the owner+group + cmd.formats = ['gztar'] + cmd.owner = pwd.getpwuid(0)[0] + cmd.group = grp.getgrgid(0)[0] + cmd.ensure_finalized() + cmd.run() + + # making sure we have the good rights + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEquals(member.uid, 0) + self.assertEquals(member.gid, 0) + finally: + archive.close() + + # building a sdist again + dist, cmd = self.get_cmd() + + # creating a gztar + cmd.formats = ['gztar'] + cmd.ensure_finalized() + cmd.run() + + # making sure we have the good rights + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEquals(member.uid, os.getuid()) + self.assertEquals(member.gid, os.getgid()) + finally: + archive.close() + def test_suite(): return unittest.makeSuite(SDistTestCase) -- cgit v1.2.1 From 53ae4bd7bad05c5717d8b713e57d41e90d47057c Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 15 Nov 2013 23:08:21 +0100 Subject: Issue #19544 and Issue #6516: quick workaround for failing builds --- archive_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archive_util.py b/archive_util.py index 306ef6a8..82046a8a 100644 --- a/archive_util.py +++ b/archive_util.py @@ -20,12 +20,12 @@ from distutils import log try: from pwd import getpwnam -except AttributeError: +except (ImportError, AttributeError): getpwnam = None try: from grp import getgrnam -except AttributeError: +except (ImportError, AttributeError): getgrnam = None def _get_gid(name): -- cgit v1.2.1 From 7c572215c02f83b007a506953969b34d2bb9dd9a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 15 Nov 2013 23:13:17 +0100 Subject: Issue #19544, #6516: no need to catch AttributeError on import pwd/grp --- archive_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archive_util.py b/archive_util.py index 82046a8a..4470bb02 100644 --- a/archive_util.py +++ b/archive_util.py @@ -20,12 +20,12 @@ from distutils import log try: from pwd import getpwnam -except (ImportError, AttributeError): +except ImportError: getpwnam = None try: from grp import getgrnam -except (ImportError, AttributeError): +except ImportError: getgrnam = None def _get_gid(name): -- cgit v1.2.1 From 11857584767cb29d5a3761c9a4ed00793f576164 Mon Sep 17 00:00:00 2001 From: Andrew Kuchling Date: Sun, 10 Nov 2013 18:11:00 -0500 Subject: Issue #19544 and Issue #1180: Restore global option to ignore ~/.pydistutils.cfg in Distutils, accidentally removed in backout of distutils2 changes. --- core.py | 5 +++-- dist.py | 35 ++++++++++++++++++++++++++++++----- tests/test_dist.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/core.py b/core.py index 91e51329..c811d5bd 100644 --- a/core.py +++ b/core.py @@ -128,8 +128,9 @@ def setup (**attrs): if _setup_stop_after == "config": return dist - # Parse the command line; any command-line errors are the end user's - # fault, so turn them into SystemExit to suppress tracebacks. + # Parse the command line and override config files; any + # command-line errors are the end user's fault, so turn them into + # SystemExit to suppress tracebacks. try: ok = dist.parse_command_line() except DistutilsArgError as msg: diff --git a/dist.py b/dist.py index 11a21027..7eb04bc3 100644 --- a/dist.py +++ b/dist.py @@ -52,7 +52,9 @@ class Distribution: ('quiet', 'q', "run quietly (turns verbosity off)"), ('dry-run', 'n', "don't actually do anything"), ('help', 'h', "show detailed help message"), - ] + ('no-user-cfg', None, + 'ignore pydistutils.cfg in your home directory'), + ] # 'common_usage' is a short (2-3 line) string describing the common # usage of the setup script. @@ -259,6 +261,22 @@ Common commands: (see '--help-commands' for more) else: sys.stderr.write(msg + "\n") + # no-user-cfg is handled before other command line args + # because other args override the config files, and this + # one is needed before we can load the config files. + # If attrs['script_args'] wasn't passed, assume false. + # + # This also make sure we just look at the global options + self.want_user_cfg = True + + if self.script_args is not None: + for arg in self.script_args: + if not arg.startswith('-'): + break + if arg == '--no-user-cfg': + self.want_user_cfg = False + break + self.finalize_options() def get_option_dict(self, command): @@ -310,7 +328,10 @@ Common commands: (see '--help-commands' for more) Distutils installation directory (ie. where the top-level Distutils __inst__.py file lives), a file in the user's home directory named .pydistutils.cfg on Unix and pydistutils.cfg - on Windows/Mac, and setup.cfg in the current directory. + on Windows/Mac; and setup.cfg in the current directory. + + The file in the user's home directory can be disabled with the + --no-user-cfg option. """ files = [] check_environ() @@ -330,15 +351,19 @@ Common commands: (see '--help-commands' for more) user_filename = "pydistutils.cfg" # And look for the user config file - user_file = os.path.join(os.path.expanduser('~'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) + if self.want_user_cfg: + user_file = os.path.join(os.path.expanduser('~'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" if os.path.isfile(local_file): files.append(local_file) + if DEBUG: + self.announce("using config files: %s" % ', '.join(files)) + return files def parse_config_files(self, filenames=None): diff --git a/tests/test_dist.py b/tests/test_dist.py index 9a8ca199..102c553d 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -39,6 +39,7 @@ class TestDistribution(Distribution): class DistributionTestCase(support.LoggingSilencer, + support.TempdirManager, support.EnvironGuard, unittest.TestCase): @@ -213,6 +214,34 @@ class DistributionTestCase(support.LoggingSilencer, self.assertRaises(ValueError, dist.announce, args, kwargs) + def test_find_config_files_disable(self): + # Ticket #1180: Allow user to disable their home config file. + temp_home = self.mkdtemp() + if os.name == 'posix': + user_filename = os.path.join(temp_home, ".pydistutils.cfg") + else: + user_filename = os.path.join(temp_home, "pydistutils.cfg") + + with open(user_filename, 'w') as f: + f.write('[distutils]\n') + + def _expander(path): + return temp_home + + old_expander = os.path.expanduser + os.path.expanduser = _expander + try: + d = Distribution() + all_files = d.find_config_files() + + d = Distribution(attrs={'script_args': ['--no-user-cfg']}) + files = d.find_config_files() + finally: + os.path.expanduser = old_expander + + # make sure --no-user-cfg disables the user cfg file + self.assertEquals(len(all_files)-1, len(files)) + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): -- cgit v1.2.1 From 5edd62fecb7444f731574a38a7108e002893dac7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Nov 2013 18:15:03 -0500 Subject: Issue 19544 and Issue #7457: Restore the read_pkg_file method to distutils.dist.DistributionMetadata accidentally removed in the undo of distutils2. --- dist.py | 90 +++++++++++++++++++++++++++++++++++++++++++----------- tests/test_dist.py | 29 +++++++++++++++++- 2 files changed, 101 insertions(+), 18 deletions(-) diff --git a/dist.py b/dist.py index f7fac089..11a21027 100644 --- a/dist.py +++ b/dist.py @@ -5,6 +5,7 @@ being built/installed/distributed. """ import sys, os, re +from email import message_from_file try: import warnings @@ -999,25 +1000,80 @@ class DistributionMetadata: "provides", "requires", "obsoletes", ) - def __init__ (self): - self.name = None - self.version = None - self.author = None - self.author_email = None + def __init__(self, path=None): + if path is not None: + self.read_pkg_file(open(path)) + else: + self.name = None + self.version = None + self.author = None + self.author_email = None + self.maintainer = None + self.maintainer_email = None + self.url = None + self.license = None + self.description = None + self.long_description = None + self.keywords = None + self.platforms = None + self.classifiers = None + self.download_url = None + # PEP 314 + self.provides = None + self.requires = None + self.obsoletes = None + + def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = message_from_file(file) + + def _read_field(name): + value = msg[name] + if value == 'UNKNOWN': + return None + return value + + def _read_list(name): + values = msg.get_all(name, None) + if values == []: + return None + return values + + metadata_version = msg['metadata-version'] + self.name = _read_field('name') + self.version = _read_field('version') + self.description = _read_field('summary') + # we are filling author only. + self.author = _read_field('author') self.maintainer = None + self.author_email = _read_field('author-email') self.maintainer_email = None - self.url = None - self.license = None - self.description = None - self.long_description = None - self.keywords = None - self.platforms = None - self.classifiers = None - self.download_url = None - # PEP 314 - self.provides = None - self.requires = None - self.obsoletes = None + self.url = _read_field('home-page') + self.license = _read_field('license') + + if 'download-url' in msg: + self.download_url = _read_field('download-url') + else: + self.download_url = None + + self.long_description = _read_field('description') + self.description = _read_field('summary') + + if 'keywords' in msg: + self.keywords = _read_field('keywords').split(',') + + self.platforms = _read_list('platform') + self.classifiers = _read_list('classifier') + + # PEP 314 - these fields only exist in 1.1 + if metadata_version == '1.1': + self.requires = _read_list('requires') + self.provides = _read_list('provides') + self.obsoletes = _read_list('obsoletes') + else: + self.requires = None + self.provides = None + self.obsoletes = None def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. diff --git a/tests/test_dist.py b/tests/test_dist.py index 66c20e27..9a8ca199 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,7 +8,7 @@ import textwrap from unittest import mock -from distutils.dist import Distribution, fix_help_options +from distutils.dist import Distribution, fix_help_options, DistributionMetadata from distutils.cmd import Command from test.support import TESTFN, captured_stdout, run_unittest @@ -388,6 +388,33 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertTrue(output) + def test_read_metadata(self): + attrs = {"name": "package", + "version": "1.0", + "long_description": "desc", + "description": "xxx", + "download_url": "http://example.com", + "keywords": ['one', 'two'], + "requires": ['foo']} + + dist = Distribution(attrs) + metadata = dist.metadata + + # write it then reloads it + PKG_INFO = io.StringIO() + metadata.write_pkg_file(PKG_INFO) + PKG_INFO.seek(0) + metadata.read_pkg_file(PKG_INFO) + + self.assertEquals(metadata.name, "package") + self.assertEquals(metadata.version, "1.0") + self.assertEquals(metadata.description, "xxx") + self.assertEquals(metadata.download_url, 'http://example.com') + self.assertEquals(metadata.keywords, ['one', 'two']) + self.assertEquals(metadata.platforms, ['UNKNOWN']) + self.assertEquals(metadata.obsoletes, None) + self.assertEquals(metadata.requires, ['foo']) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) -- cgit v1.2.1 From 66f3b499473504c130a52d222b827a4413127f12 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Nov 2013 18:50:10 -0500 Subject: Issue #19544 and Issue #6286: Restore use of urllib over http allowing use of http_proxy for Distutils upload command, a feature accidentally lost in the rollback of distutils2. --- command/upload.py | 60 ++++++++++++++++++++++++------------------------- tests/test_upload.py | 63 ++++++++++++++++++++++------------------------------ 2 files changed, 55 insertions(+), 68 deletions(-) diff --git a/command/upload.py b/command/upload.py index 8b36851d..d2bc82ce 100644 --- a/command/upload.py +++ b/command/upload.py @@ -10,10 +10,9 @@ import sys import os, io import socket import platform -import configparser -import http.client as httpclient from base64 import standard_b64encode -import urllib.parse +from urllib.request import urlopen, Request, HTTPError +from urllib.parse import urlparse # this keeps compatibility for 2.3 and 2.4 if sys.version < "2.5": @@ -66,6 +65,15 @@ class upload(PyPIRCCommand): self.upload_file(command, pyversion, filename) def upload_file(self, command, pyversion, filename): + # Makes sure the repository URL is compliant + schema, netloc, url, params, query, fragments = \ + urlparse(self.repository) + if params or query or fragments: + raise AssertionError("Incompatible url %s" % self.repository) + + if schema not in ('http', 'https'): + raise AssertionError("unsupported schema " + schema) + # Sign if requested if self.sign: gpg_args = ["gpg", "--detach-sign", "-a", filename] @@ -162,41 +170,31 @@ class upload(PyPIRCCommand): self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) # build the Request - # We can't use urllib since we need to send the Basic - # auth right with the first request - # TODO(jhylton): Can we fix urllib? - schema, netloc, url, params, query, fragments = \ - urllib.parse.urlparse(self.repository) - assert not params and not query and not fragments - if schema == 'http': - http = httpclient.HTTPConnection(netloc) - elif schema == 'https': - http = httpclient.HTTPSConnection(netloc) - else: - raise AssertionError("unsupported schema "+schema) - - data = '' - loglevel = log.INFO + headers = {'Content-type': + 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth} + + request = Request(self.repository, data=body, + headers=headers) + # send the data try: - http.connect() - http.putrequest("POST", url) - http.putheader('Content-type', - 'multipart/form-data; boundary=%s'%boundary) - http.putheader('Content-length', str(len(body))) - http.putheader('Authorization', auth) - http.endheaders() - http.send(body) + result = urlopen(request) + status = result.getcode() + reason = result.msg except socket.error as e: self.announce(str(e), log.ERROR) return + except HTTPError as e: + status = e.code + reason = e.msg - r = http.getresponse() - if r.status == 200: - self.announce('Server response (%s): %s' % (r.status, r.reason), + if status == 200: + self.announce('Server response (%s): %s' % (status, reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (r.status, r.reason), + self.announce('Upload failed (%s): %s' % (status, reason), log.ERROR) if self.show_response: - msg = '\n'.join(('-' * 75, r.read(), '-' * 75)) + msg = '\n'.join(('-' * 75, result.read(), '-' * 75)) self.announce(msg, log.INFO) diff --git a/tests/test_upload.py b/tests/test_upload.py index 4c6464a3..a474596e 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -1,9 +1,9 @@ """Tests for distutils.command.upload.""" import os import unittest -import http.client as httpclient from test.support import run_unittest +from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution @@ -37,48 +37,37 @@ index-servers = [server1] username:me """ -class Response(object): - def __init__(self, status=200, reason='OK'): - self.status = status - self.reason = reason -class FakeConnection(object): +class FakeOpen(object): - def __init__(self): - self.requests = [] - self.headers = [] - self.body = '' + def __init__(self, url): + self.url = url + if not isinstance(url, str): + self.req = url + else: + self.req = None + self.msg = 'OK' - def __call__(self, netloc): - return self + def getcode(self): + return 200 - def connect(self): - pass - endheaders = connect - - def putrequest(self, method, url): - self.requests.append((method, url)) - - def putheader(self, name, value): - self.headers.append((name, value)) - - def send(self, body): - self.body = body - - def getresponse(self): - return Response() class uploadTestCase(PyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() - self.old_class = httpclient.HTTPConnection - self.conn = httpclient.HTTPConnection = FakeConnection() + self.old_open = upload_mod.urlopen + upload_mod.urlopen = self._urlopen + self.last_open = None def tearDown(self): - httpclient.HTTPConnection = self.old_class + upload_mod.urlopen = self.old_open super(uploadTestCase, self).tearDown() + def _urlopen(self, url): + self.last_open = FakeOpen(url) + return self.last_open + def test_finalize_options(self): # new format @@ -122,14 +111,14 @@ class uploadTestCase(PyPIRCCommandTestCase): cmd.ensure_finalized() cmd.run() - # what did we send ? - headers = dict(self.conn.headers) + # what did we send ? + headers = dict(self.last_open.req.headers) self.assertEqual(headers['Content-length'], '2087') - self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) - self.assertFalse('\n' in headers['Authorization']) - - self.assertEqual(self.conn.requests, [('POST', '/pypi')]) - self.assertTrue((b'xxx') in self.conn.body) + self.assert_(headers['Content-type'].startswith('multipart/form-data')) + self.assertEquals(self.last_open.req.get_method(), 'POST') + self.assertEquals(self.last_open.req.get_full_url(), + 'http://pypi.python.org/pypi') + self.assert_(b'xxx' in self.last_open.req.data) def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From 341dbe79bfe1e225ff5a05731eb56e69ac989bce Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 11 Nov 2013 06:13:54 +0100 Subject: Bump to 3.3.3rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 812d8f35..0650dd27 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.3rc1" +__version__ = "3.3.3rc2" #--end constants-- -- cgit v1.2.1 From 303e2b0c71421dc5a0fa03019354124305579fbd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 Nov 2013 19:24:07 -0500 Subject: Issue #7408: Forward port limited test from Python 2.7, fixing failing buildbot tests on BSD-based platforms. --- tests/test_sdist.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 6170a48f..280b9bca 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -471,10 +471,13 @@ class SDistTestCase(PyPIRCCommandTestCase): # making sure we have the good rights archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') archive = tarfile.open(archive_name) + + # note that we are not testing the group ownership here + # because, depending on the platforms and the container + # rights (see #7408) try: for member in archive.getmembers(): self.assertEquals(member.uid, os.getuid()) - self.assertEquals(member.gid, os.getgid()) finally: archive.close() -- cgit v1.2.1 From ccce5b21bbd52d95a00704f48f6cf3923cad9776 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 Nov 2013 19:35:05 -0500 Subject: Use preferred assertEqual form. Correct indentation. --- tests/test_sdist.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 280b9bca..164586b7 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -437,7 +437,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # check if tar and gzip are installed if (find_executable('tar') is None or - find_executable('gzip') is None): + find_executable('gzip') is None): return # now building a sdist @@ -455,8 +455,8 @@ class SDistTestCase(PyPIRCCommandTestCase): archive = tarfile.open(archive_name) try: for member in archive.getmembers(): - self.assertEquals(member.uid, 0) - self.assertEquals(member.gid, 0) + self.assertEqual(member.uid, 0) + self.assertEqual(member.gid, 0) finally: archive.close() @@ -477,7 +477,7 @@ class SDistTestCase(PyPIRCCommandTestCase): # rights (see #7408) try: for member in archive.getmembers(): - self.assertEquals(member.uid, os.getuid()) + self.assertEqual(member.uid, os.getuid()) finally: archive.close() -- cgit v1.2.1 From 59262e8d7386c303a4f651ade1b5105d2b3e0c3c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 Nov 2013 19:38:51 -0500 Subject: Use preferred assertEqual --- tests/test_dist.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index 102c553d..b7fd3fbf 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -240,7 +240,7 @@ class DistributionTestCase(support.LoggingSilencer, os.path.expanduser = old_expander # make sure --no-user-cfg disables the user cfg file - self.assertEquals(len(all_files)-1, len(files)) + self.assertEqual(len(all_files)-1, len(files)) class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): @@ -435,14 +435,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, PKG_INFO.seek(0) metadata.read_pkg_file(PKG_INFO) - self.assertEquals(metadata.name, "package") - self.assertEquals(metadata.version, "1.0") - self.assertEquals(metadata.description, "xxx") - self.assertEquals(metadata.download_url, 'http://example.com') - self.assertEquals(metadata.keywords, ['one', 'two']) - self.assertEquals(metadata.platforms, ['UNKNOWN']) - self.assertEquals(metadata.obsoletes, None) - self.assertEquals(metadata.requires, ['foo']) + self.assertEqual(metadata.name, "package") + self.assertEqual(metadata.version, "1.0") + self.assertEqual(metadata.description, "xxx") + self.assertEqual(metadata.download_url, 'http://example.com') + self.assertEqual(metadata.keywords, ['one', 'two']) + self.assertEqual(metadata.platforms, ['UNKNOWN']) + self.assertEqual(metadata.obsoletes, None) + self.assertEqual(metadata.requires, ['foo']) def test_suite(): suite = unittest.TestSuite() -- cgit v1.2.1 From 5960d28f32e53337043371f95c7f349e34024f40 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 Nov 2013 19:41:57 -0500 Subject: Update more usage of assertEqual --- tests/test_archive_util.py | 4 ++-- tests/test_upload.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 581c0cc8..caa0db20 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -329,8 +329,8 @@ class ArchiveUtilTestCase(support.TempdirManager, archive = tarfile.open(archive_name) try: for member in archive.getmembers(): - self.assertEquals(member.uid, 0) - self.assertEquals(member.gid, 0) + self.assertEqual(member.uid, 0) + self.assertEqual(member.gid, 0) finally: archive.close() diff --git a/tests/test_upload.py b/tests/test_upload.py index 7d704cf1..9a2265ee 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -115,9 +115,9 @@ class uploadTestCase(PyPIRCCommandTestCase): headers = dict(self.last_open.req.headers) self.assertEqual(headers['Content-length'], '2087') self.assert_(headers['Content-type'].startswith('multipart/form-data')) - self.assertEquals(self.last_open.req.get_method(), 'POST') - self.assertEquals(self.last_open.req.get_full_url(), - 'https://pypi.python.org/pypi') + self.assertEqual(self.last_open.req.get_method(), 'POST') + expected_url = 'https://pypi.python.org/pypi' + self.assertEqual(self.last_open.req.get_full_url(), expected_url) self.assert_(b'xxx' in self.last_open.req.data) def test_suite(): -- cgit v1.2.1 From f169cafe969605899f196b8dfb86b0385e0da5ff Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 Nov 2013 10:35:46 -0500 Subject: Issue #19586: Update remaining deprecated assertions to their preferred usage. --- tests/test_upload.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_upload.py b/tests/test_upload.py index 9a2265ee..8e8ff720 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -114,11 +114,11 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.last_open.req.headers) self.assertEqual(headers['Content-length'], '2087') - self.assert_(headers['Content-type'].startswith('multipart/form-data')) + self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) self.assertEqual(self.last_open.req.get_method(), 'POST') expected_url = 'https://pypi.python.org/pypi' self.assertEqual(self.last_open.req.get_full_url(), expected_url) - self.assert_(b'xxx' in self.last_open.req.data) + self.assertTrue(b'xxx' in self.last_open.req.data) def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From ad0f0c5c29e6c8af182a9e72d995d91911cc8349 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 Nov 2013 10:47:17 -0500 Subject: Correct long line --- tests/test_upload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_upload.py b/tests/test_upload.py index 8e8ff720..1fcba889 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -114,7 +114,8 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.last_open.req.headers) self.assertEqual(headers['Content-length'], '2087') - self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) + content_type = headers['Content-type'] + self.assertTrue(content_type.startswith('multipart/form-data')) self.assertEqual(self.last_open.req.get_method(), 'POST') expected_url = 'https://pypi.python.org/pypi' self.assertEqual(self.last_open.req.get_full_url(), expected_url) -- cgit v1.2.1 From c0d4c5676e3118c7189a97a555bfb70447276f29 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 17 Nov 2013 00:17:46 +0200 Subject: Issue #19600: Use specific asserts in distutils tests. --- tests/test_archive_util.py | 2 +- tests/test_bdist_rpm.py | 4 ++-- tests/test_bdist_wininst.py | 2 +- tests/test_build_clib.py | 2 +- tests/test_build_ext.py | 16 ++++++++-------- tests/test_build_scripts.py | 8 ++++---- tests/test_clean.py | 2 +- tests/test_config.py | 2 +- tests/test_config_cmd.py | 2 +- tests/test_install.py | 2 +- tests/test_install_lib.py | 2 +- tests/test_install_scripts.py | 10 +++++----- tests/test_msvc9compiler.py | 6 +++--- tests/test_register.py | 8 ++++---- tests/test_sysconfig.py | 2 +- tests/test_util.py | 2 +- 16 files changed, 36 insertions(+), 36 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 1afdd462..d3fb24ad 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -210,7 +210,7 @@ class ArchiveUtilTestCase(support.TempdirManager, dry_run=True) finally: os.chdir(old_dir) - self.assertTrue(not os.path.exists(tarball)) + self.assertFalse(os.path.exists(tarball)) self.assertEqual(len(w.warnings), 1) @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index b090b79e..aa1445d1 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -81,7 +81,7 @@ class BuildRpmTestCase(support.TempdirManager, cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) + self.assertIn('foo-0.1-1.noarch.rpm', dist_created) # bug #2945: upload ignores bdist_rpm files self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) @@ -125,7 +125,7 @@ class BuildRpmTestCase(support.TempdirManager, cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assertTrue('foo-0.1-1.noarch.rpm' in dist_created) + self.assertIn('foo-0.1-1.noarch.rpm', dist_created) # bug #2945: upload ignores bdist_rpm files self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index f9e8f89e..5d17ab19 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -22,7 +22,7 @@ class BuildWinInstTestCase(support.TempdirManager, # and make sure it finds it and returns its content # no matter what platform we have exe_file = cmd.get_exe_bytes() - self.assertTrue(len(exe_file) > 10) + self.assertGreater(len(exe_file), 10) def test_suite(): return unittest.makeSuite(BuildWinInstTestCase) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index ee1c0416..c2b981f2 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -137,7 +137,7 @@ class BuildCLibTestCase(support.TempdirManager, cmd.run() # let's check the result - self.assertTrue('libfoo.a' in os.listdir(build_temp)) + self.assertIn('libfoo.a', os.listdir(build_temp)) def test_suite(): return unittest.makeSuite(BuildCLibTestCase) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 44a9852f..3cfaa2c6 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -76,8 +76,8 @@ class BuildExtTestCase(TempdirManager, if support.HAVE_DOCSTRINGS: doc = 'This is a template module just for instruction.' self.assertEqual(xx.__doc__, doc) - self.assertTrue(isinstance(xx.Null(), xx.Null)) - self.assertTrue(isinstance(xx.Str(), xx.Str)) + self.assertIsInstance(xx.Null(), xx.Null) + self.assertIsInstance(xx.Str(), xx.Str) def tearDown(self): # Get everything back to normal @@ -110,7 +110,7 @@ class BuildExtTestCase(TempdirManager, _config_vars['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris - self.assertTrue(len(cmd.library_dirs) > 0) + self.assertGreater(len(cmd.library_dirs), 0) def test_user_site(self): # site.USER_SITE was introduced in 2.6 @@ -124,7 +124,7 @@ class BuildExtTestCase(TempdirManager, # making sure the user option is there options = [name for name, short, lable in cmd.user_options] - self.assertTrue('user' in options) + self.assertIn('user', options) # setting a value cmd.user = 1 @@ -171,10 +171,10 @@ class BuildExtTestCase(TempdirManager, from distutils import sysconfig py_include = sysconfig.get_python_inc() - self.assertTrue(py_include in cmd.include_dirs) + self.assertIn(py_include, cmd.include_dirs) plat_py_include = sysconfig.get_python_inc(plat_specific=1) - self.assertTrue(plat_py_include in cmd.include_dirs) + self.assertIn(plat_py_include, cmd.include_dirs) # make sure cmd.libraries is turned into a list # if it's a string @@ -255,13 +255,13 @@ class BuildExtTestCase(TempdirManager, 'some': 'bar'})] cmd.check_extensions_list(exts) ext = exts[0] - self.assertTrue(isinstance(ext, Extension)) + self.assertIsInstance(ext, Extension) # check_extensions_list adds in ext the values passed # when they are in ('include_dirs', 'library_dirs', 'libraries' # 'extra_objects', 'extra_compile_args', 'extra_link_args') self.assertEqual(ext.libraries, 'foo') - self.assertTrue(not hasattr(ext, 'some')) + self.assertFalse(hasattr(ext, 'some')) # 'macros' element of build info dict must be 1- or 2-tuple exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py index e3326b85..954fc763 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -17,8 +17,8 @@ class BuildScriptsTestCase(support.TempdirManager, def test_default_settings(self): cmd = self.get_build_scripts_cmd("/foo/bar", []) - self.assertTrue(not cmd.force) - self.assertTrue(cmd.build_dir is None) + self.assertFalse(cmd.force) + self.assertIsNone(cmd.build_dir) cmd.finalize_options() @@ -38,7 +38,7 @@ class BuildScriptsTestCase(support.TempdirManager, built = os.listdir(target) for name in expected: - self.assertTrue(name in built) + self.assertIn(name, built) def get_build_scripts_cmd(self, target, scripts): import sys @@ -103,7 +103,7 @@ class BuildScriptsTestCase(support.TempdirManager, built = os.listdir(target) for name in expected: - self.assertTrue(name in built) + self.assertIn(name, built) def test_suite(): return unittest.makeSuite(BuildScriptsTestCase) diff --git a/tests/test_clean.py b/tests/test_clean.py index eb8958bf..b64f300a 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -36,7 +36,7 @@ class cleanTestCase(support.TempdirManager, # make sure the files where removed for name, path in dirs: - self.assertTrue(not os.path.exists(path), + self.assertFalse(os.path.exists(path), '%s was not removed' % path) # let's run the command again (should spit warnings but succeed) diff --git a/tests/test_config.py b/tests/test_config.py index 525bee94..0e8d65e2 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -103,7 +103,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, def test_server_empty_registration(self): cmd = self._cmd(self.dist) rc = cmd._get_rc_file() - self.assertTrue(not os.path.exists(rc)) + self.assertFalse(os.path.exists(rc)) cmd._store_pypirc('tarek', 'xxx') self.assertTrue(os.path.exists(rc)) f = open(rc) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index e2e6e4eb..79894c78 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -81,7 +81,7 @@ class ConfigTestCase(support.LoggingSilencer, cmd._clean(f1, f2) for f in (f1, f2): - self.assertTrue(not os.path.exists(f)) + self.assertFalse(os.path.exists(f)) def test_suite(): return unittest.makeSuite(ConfigTestCase) diff --git a/tests/test_install.py b/tests/test_install.py index b1901273..ede88e5d 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -236,7 +236,7 @@ class InstallTestCase(support.TempdirManager, self.test_record() finally: install_module.DEBUG = False - self.assertTrue(len(self.logs) > old_logs_len) + self.assertGreater(len(self.logs), old_logs_len) def test_suite(): diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 2bd4dc6e..d0dfca00 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -103,7 +103,7 @@ class InstallLibTestCase(support.TempdirManager, finally: sys.dont_write_bytecode = old_dont_write_bytecode - self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) + self.assertIn('byte-compiling is disabled', self.logs[0][1]) def test_suite(): diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py index 8952e744..1f7b1038 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -24,10 +24,10 @@ class InstallScriptsTestCase(support.TempdirManager, skip_build=1, ) cmd = install_scripts(dist) - self.assertTrue(not cmd.force) - self.assertTrue(not cmd.skip_build) - self.assertTrue(cmd.build_dir is None) - self.assertTrue(cmd.install_dir is None) + self.assertFalse(cmd.force) + self.assertFalse(cmd.skip_build) + self.assertIsNone(cmd.build_dir) + self.assertIsNone(cmd.install_dir) cmd.finalize_options() @@ -72,7 +72,7 @@ class InstallScriptsTestCase(support.TempdirManager, installed = os.listdir(target) for name in expected: - self.assertTrue(name in installed) + self.assertIn(name, installed) def test_suite(): diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 301d43d2..5e18c613 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -128,7 +128,7 @@ class msvc9compilerTestCase(support.TempdirManager, # windows registeries versions. path = r'Control Panel\Desktop' v = Reg.get_value(path, 'dragfullwindows') - self.assertTrue(v in ('0', '1', '2')) + self.assertIn(v, ('0', '1', '2')) import winreg HKCU = winreg.HKEY_CURRENT_USER @@ -136,7 +136,7 @@ class msvc9compilerTestCase(support.TempdirManager, self.assertEqual(keys, None) keys = Reg.read_keys(HKCU, r'Control Panel') - self.assertTrue('Desktop' in keys) + self.assertIn('Desktop', keys) def test_remove_visual_c_ref(self): from distutils.msvc9compiler import MSVCCompiler @@ -174,7 +174,7 @@ class msvc9compilerTestCase(support.TempdirManager, compiler = MSVCCompiler() got = compiler._remove_visual_c_ref(manifest) - self.assertIs(got, None) + self.assertIsNone(got) def test_suite(): diff --git a/tests/test_register.py b/tests/test_register.py index a86b8606..f4efa13d 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -98,7 +98,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): cmd = self._get_cmd() # we shouldn't have a .pypirc file yet - self.assertTrue(not os.path.exists(self.rc)) + self.assertFalse(os.path.exists(self.rc)) # patching input and getpass.getpass # so register gets happy @@ -145,7 +145,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): self.assertEqual(req1['Content-length'], '1374') self.assertEqual(req2['Content-length'], '1374') - self.assertTrue((b'xxx') in self.conn.reqs[1].data) + self.assertIn(b'xxx', self.conn.reqs[1].data) def test_password_not_in_file(self): @@ -175,7 +175,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): req = self.conn.reqs[0] headers = dict(req.headers) self.assertEqual(headers['Content-length'], '608') - self.assertTrue((b'tarek') in req.data) + self.assertIn(b'tarek', req.data) def test_password_reset(self): # this test runs choice 3 @@ -193,7 +193,7 @@ class RegisterTestCase(PyPIRCCommandTestCase): req = self.conn.reqs[0] headers = dict(req.headers) self.assertEqual(headers['Content-length'], '290') - self.assertTrue((b'tarek') in req.data) + self.assertIn(b'tarek', req.data) @unittest.skipUnless(docutils is not None, 'needs docutils') def test_strict(self): diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 826ea424..07812d80 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -50,7 +50,7 @@ class SysconfigTestCase(support.EnvironGuard, def test_get_config_vars(self): cvars = sysconfig.get_config_vars() - self.assertTrue(isinstance(cvars, dict)) + self.assertIsInstance(cvars, dict) self.assertTrue(cvars) def test_srcdir(self): diff --git a/tests/test_util.py b/tests/test_util.py index eac9b514..00219cfd 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -266,7 +266,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): self.assertTrue(strtobool(y)) for n in no: - self.assertTrue(not strtobool(n)) + self.assertFalse(strtobool(n)) def test_rfc822_escape(self): header = 'I am a\npoor\nlonesome\nheader\n' -- cgit v1.2.1 From ab27da2853e1eb770873803bd6a8008e612ac796 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 17 Nov 2013 07:58:22 +0100 Subject: Bump to 3.3.3 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 0650dd27..6747ab86 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.3rc2" +__version__ = "3.3.3" #--end constants-- -- cgit v1.2.1 From b1290f15cc21206e9b16c91fa45cf4451137f732 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 22 Nov 2013 15:31:35 -0500 Subject: Issue 19555 for distutils, plus a little clean up (pyflakes, line lengths). --- sysconfig.py | 8 ++++++++ tests/test_sysconfig.py | 45 +++++++++++++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index d9c9d658..392d63d1 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -518,6 +518,11 @@ def get_config_vars(*args): _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX + # For backward compatibility, see issue19555 + SO = _config_vars.get('EXT_SUFFIX') + if SO is not None: + _config_vars['SO'] = SO + # Always convert srcdir to an absolute path srcdir = _config_vars.get('srcdir', project_base) if os.name == 'posix': @@ -568,4 +573,7 @@ def get_config_var(name): returned by 'get_config_vars()'. Equivalent to get_config_vars().get(name) """ + if name == 'SO': + import warnings + warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning) return get_config_vars().get(name) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 07812d80..e14646ed 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,7 +1,6 @@ """Tests for distutils.sysconfig.""" import os import shutil -import test import unittest from distutils import sysconfig @@ -9,8 +8,7 @@ from distutils.ccompiler import get_default_compiler from distutils.tests import support from test.support import TESTFN, run_unittest -class SysconfigTestCase(support.EnvironGuard, - unittest.TestCase): +class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): super(SysconfigTestCase, self).setUp() self.makefile = None @@ -32,7 +30,6 @@ class SysconfigTestCase(support.EnvironGuard, self.assertTrue(os.path.isfile(config_h), config_h) def test_get_python_lib(self): - lib_dir = sysconfig.get_python_lib() # XXX doesn't work on Linux when Python was never installed before #self.assertTrue(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? @@ -67,8 +64,9 @@ class SysconfigTestCase(support.EnvironGuard, self.assertTrue(os.path.exists(Python_h), Python_h) self.assertTrue(sysconfig._is_python_source_dir(srcdir)) elif os.name == 'posix': - self.assertEqual(os.path.dirname(sysconfig.get_makefile_filename()), - srcdir) + self.assertEqual( + os.path.dirname(sysconfig.get_makefile_filename()), + srcdir) def test_srcdir_independent_of_cwd(self): # srcdir should be independent of the current working directory @@ -129,10 +127,13 @@ class SysconfigTestCase(support.EnvironGuard, def test_sysconfig_module(self): import sysconfig as global_sysconfig - self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS')) - self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS')) + self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), + sysconfig.get_config_var('CFLAGS')) + self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), + sysconfig.get_config_var('LDFLAGS')) - @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'),'compiler flags customized') + @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'), + 'compiler flags customized') def test_sysconfig_compiler_vars(self): # On OS X, binary installers support extension module building on # various levels of the operating system with differing Xcode @@ -151,9 +152,29 @@ class SysconfigTestCase(support.EnvironGuard, import sysconfig as global_sysconfig if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'): return - self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), sysconfig.get_config_var('LDSHARED')) - self.assertEqual(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC')) - + self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), + sysconfig.get_config_var('LDSHARED')) + self.assertEqual(global_sysconfig.get_config_var('CC'), + sysconfig.get_config_var('CC')) + + @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, + 'EXT_SUFFIX required for this test') + def test_SO_deprecation(self): + self.assertWarns(DeprecationWarning, + sysconfig.get_config_var, 'SO') + + @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, + 'EXT_SUFFIX required for this test') + def test_SO_value(self): + self.assertEqual(sysconfig.get_config_var('SO'), + sysconfig.get_config_var('EXT_SUFFIX')) + + @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, + 'EXT_SUFFIX required for this test') + def test_SO_in_vars(self): + vars = sysconfig.get_config_vars() + self.assertIsNotNone(vars['SO']) + self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) def test_suite(): -- cgit v1.2.1 From 58403d4abc8fdef0ff3b0fc9bb14c203cfd17349 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 24 Nov 2013 06:59:35 -0800 Subject: Bump version number to 3.4.0b1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 355542ed..38a50869 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0a4" +__version__ = "3.4.0b1" #--end constants-- -- cgit v1.2.1 From 4f57b30b05e31f7a12d2411a99d0faeb6e669196 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 26 Nov 2013 17:08:24 +0200 Subject: Issue #19760: Silence sysconfig's 'SO' key deprecation warnings in tests. Change stacklevel in warnings.warn() for 'SO' key to 2. --- sysconfig.py | 2 +- tests/test_sysconfig.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 392d63d1..75537db8 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -575,5 +575,5 @@ def get_config_var(name): """ if name == 'SO': import warnings - warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning) + warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2) return get_config_vars().get(name) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index e14646ed..b5fdc98d 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -6,7 +6,7 @@ import unittest from distutils import sysconfig from distutils.ccompiler import get_default_compiler from distutils.tests import support -from test.support import TESTFN, run_unittest +from test.support import TESTFN, run_unittest, check_warnings class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): @@ -166,8 +166,9 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, 'EXT_SUFFIX required for this test') def test_SO_value(self): - self.assertEqual(sysconfig.get_config_var('SO'), - sysconfig.get_config_var('EXT_SUFFIX')) + with check_warnings(('', DeprecationWarning)): + self.assertEqual(sysconfig.get_config_var('SO'), + sysconfig.get_config_var('EXT_SUFFIX')) @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, 'EXT_SUFFIX required for this test') -- cgit v1.2.1 From aad9a7feb8d3ce64a6ea07e76044777303d62be3 Mon Sep 17 00:00:00 2001 From: Stefan Krah Date: Tue, 3 Dec 2013 13:55:20 +0100 Subject: Issue #9709: Stop adding PyInit_" + module_name' to export_symbols. This is already done by PyMODINIT_FUNC. --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 80689b63..9be59237 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -538,7 +538,7 @@ class build_ext(Command): library_dirs=ext.library_dirs, runtime_library_dirs=ext.runtime_library_dirs, extra_postargs=extra_args, - export_symbols=self.get_export_symbols(ext), + export_symbols=ext.export_symbols, debug=self.debug, build_temp=self.build_temp, target_lang=language) -- cgit v1.2.1 From 89924f9e965ce58b5ceab3545989e6d827931d2f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 18 Dec 2013 16:41:01 +0200 Subject: Issue #19492: Silently skipped distutils tests now reported as skipped. --- tests/test_bdist_rpm.py | 40 +++++++++++++++++----------------------- tests/test_build_clib.py | 7 ++----- tests/test_build_ext.py | 8 ++------ tests/test_check.py | 6 ++---- tests/test_config_cmd.py | 3 +-- tests/test_sdist.py | 10 ++++------ tests/test_sysconfig.py | 9 +++------ tests/test_unixccompiler.py | 6 +----- 8 files changed, 32 insertions(+), 57 deletions(-) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index aa1445d1..bcbb5633 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -43,18 +43,15 @@ class BuildRpmTestCase(support.TempdirManager, sys.argv[:] = self.old_sys_argv[1] super(BuildRpmTestCase, self).tearDown() + # XXX I am unable yet to make this test work without + # spurious sdtout/stderr output under Mac OS X + @unittest.skipUnless(sys.platform.startswith('linux'), + 'spurious sdtout/stderr output under Mac OS X') + @unittest.skipIf(find_executable('rpm') is None, + 'the rpm command is not found') + @unittest.skipIf(find_executable('rpmbuild') is None, + 'the rpmbuild command is not found') def test_quiet(self): - - # XXX I am unable yet to make this test work without - # spurious sdtout/stderr output under Mac OS X - if not sys.platform.startswith('linux'): - return - - # this test will run only if the rpm commands are found - if (find_executable('rpm') is None or - find_executable('rpmbuild') is None): - return - # let's create a package tmp_dir = self.mkdtemp() pkg_dir = os.path.join(tmp_dir, 'foo') @@ -87,19 +84,16 @@ class BuildRpmTestCase(support.TempdirManager, self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) + # XXX I am unable yet to make this test work without + # spurious sdtout/stderr output under Mac OS X + @unittest.skipUnless(sys.platform.startswith('linux'), + 'spurious sdtout/stderr output under Mac OS X') + # http://bugs.python.org/issue1533164 + @unittest.skipIf(find_executable('rpm') is None, + 'the rpm command is not found') + @unittest.skipIf(find_executable('rpmbuild') is None, + 'the rpmbuild command is not found') def test_no_optimize_flag(self): - - # XXX I am unable yet to make this test work without - # spurious sdtout/stderr output under Mac OS X - if not sys.platform.startswith('linux'): - return - - # http://bugs.python.org/issue1533164 - # this test will run only if the rpm command is found - if (find_executable('rpm') is None or - find_executable('rpmbuild') is None): - return - # let's create a package that brakes bdist_rpm tmp_dir = self.mkdtemp() pkg_dir = os.path.join(tmp_dir, 'foo') diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index c2b981f2..acc99e78 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -102,11 +102,8 @@ class BuildCLibTestCase(support.TempdirManager, cmd.distribution.libraries = 'WONTWORK' self.assertRaises(DistutilsSetupError, cmd.finalize_options) + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_run(self): - # can't test on windows - if sys.platform == 'win32': - return - pkg_dir, dist = self.create_dist() cmd = build_clib(dist) @@ -131,7 +128,7 @@ class BuildCLibTestCase(support.TempdirManager, if ccmd is None: continue if find_executable(ccmd[0]) is None: - return # can't test + self.skipTest('The %r command is not found' % ccmd[0]) # this should work cmd.run() diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 3cfaa2c6..9853abd4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -61,9 +61,9 @@ class BuildExtTestCase(TempdirManager, sys.stdout = old_stdout if ALREADY_TESTED: - return + self.skipTest('Already tested in %s' % ALREADY_TESTED) else: - ALREADY_TESTED = True + ALREADY_TESTED = type(self).__name__ import xx @@ -113,10 +113,6 @@ class BuildExtTestCase(TempdirManager, self.assertGreater(len(cmd.library_dirs), 0) def test_user_site(self): - # site.USER_SITE was introduced in 2.6 - if sys.version < '2.6': - return - import site dist = Distribution({'name': 'xx'}) cmd = build_ext(dist) diff --git a/tests/test_check.py b/tests/test_check.py index 4de64734..601b6868 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -55,9 +55,8 @@ class CheckTestCase(support.LoggingSilencer, cmd = self._run(metadata) self.assertEqual(cmd._warnings, 0) + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") def test_check_document(self): - if not HAS_DOCUTILS: # won't test without docutils - return pkg_info, dist = self.create_dist() cmd = check(dist) @@ -71,9 +70,8 @@ class CheckTestCase(support.LoggingSilencer, msgs = cmd._check_rst_data(rest) self.assertEqual(len(msgs), 0) + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") def test_check_restructuredtext(self): - if not HAS_DOCUTILS: # won't test without docutils - return # let's see if it detects broken rest in long_description broken_rest = 'title\n===\n\ntest' pkg_info, dist = self.create_dist(long_description=broken_rest) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 79894c78..0c8dbd82 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -37,9 +37,8 @@ class ConfigTestCase(support.LoggingSilencer, dump_file(this_file, 'I am the header') self.assertEqual(len(self._logs), numlines+1) + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_search_cpp(self): - if sys.platform == 'win32': - return pkg_dir, dist = self.create_dist() cmd = config(dist) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index e6359d6a..c9524061 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -125,13 +125,11 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(len(content), 4) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + @unittest.skipIf(find_executable('tar') is None, + "The tar command is not found") + @unittest.skipIf(find_executable('gzip') is None, + "The gzip command is not found") def test_make_distribution(self): - - # check if tar and gzip are installed - if (find_executable('tar') is None or - find_executable('gzip') is None): - return - # now building a sdist dist, cmd = self.get_cmd() diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 07812d80..a1cb47d8 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -82,12 +82,9 @@ class SysconfigTestCase(support.EnvironGuard, os.chdir(cwd) self.assertEqual(srcdir, srcdir2) + @unittest.skipUnless(get_default_compiler() == 'unix', + 'not testing if default compiler is not unix') def test_customize_compiler(self): - - # not testing if default compiler is not unix - if get_default_compiler() != 'unix': - return - os.environ['AR'] = 'my_ar' os.environ['ARFLAGS'] = '-arflags' @@ -150,7 +147,7 @@ class SysconfigTestCase(support.EnvironGuard, import sysconfig as global_sysconfig if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'): - return + self.skipTest('compiler flags customized') self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), sysconfig.get_config_var('LDSHARED')) self.assertEqual(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC')) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index a5a63fdd..3d14e12a 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -21,12 +21,8 @@ class UnixCCompilerTestCase(unittest.TestCase): sys.platform = self._backup_platform sysconfig.get_config_var = self._backup_get_config_var + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_runtime_libdir_option(self): - - # not tested under windows - if sys.platform == 'win32': - return - # Issue#5900 # # Ensure RUNPATH is added to extension modules with RPATH if -- cgit v1.2.1 From 3beec05fa08399627b01eedec1555c8c697f4fdf Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 21 Dec 2013 22:19:46 +0100 Subject: Fix DeprecationWarnings in test suite --- tests/test_dist.py | 16 ++++++++-------- tests/test_upload.py | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/test_dist.py b/tests/test_dist.py index 9a8ca199..61ac57d2 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -406,14 +406,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, PKG_INFO.seek(0) metadata.read_pkg_file(PKG_INFO) - self.assertEquals(metadata.name, "package") - self.assertEquals(metadata.version, "1.0") - self.assertEquals(metadata.description, "xxx") - self.assertEquals(metadata.download_url, 'http://example.com') - self.assertEquals(metadata.keywords, ['one', 'two']) - self.assertEquals(metadata.platforms, ['UNKNOWN']) - self.assertEquals(metadata.obsoletes, None) - self.assertEquals(metadata.requires, ['foo']) + self.assertEqual(metadata.name, "package") + self.assertEqual(metadata.version, "1.0") + self.assertEqual(metadata.description, "xxx") + self.assertEqual(metadata.download_url, 'http://example.com') + self.assertEqual(metadata.keywords, ['one', 'two']) + self.assertEqual(metadata.platforms, ['UNKNOWN']) + self.assertEqual(metadata.obsoletes, None) + self.assertEqual(metadata.requires, ['foo']) def test_suite(): suite = unittest.TestSuite() diff --git a/tests/test_upload.py b/tests/test_upload.py index a474596e..df35b429 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -114,11 +114,11 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.last_open.req.headers) self.assertEqual(headers['Content-length'], '2087') - self.assert_(headers['Content-type'].startswith('multipart/form-data')) - self.assertEquals(self.last_open.req.get_method(), 'POST') - self.assertEquals(self.last_open.req.get_full_url(), - 'http://pypi.python.org/pypi') - self.assert_(b'xxx' in self.last_open.req.data) + self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) + self.assertEqual(self.last_open.req.get_method(), 'POST') + self.assertEqual(self.last_open.req.get_full_url(), + 'http://pypi.python.org/pypi') + self.assertIn(b'xxx', self.last_open.req.data) def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From c2c0846c8840e37fe93fc8bed174801e6799db7e Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 21 Dec 2013 22:57:56 +0100 Subject: Issue #20045: Fix "setup.py register --list-classifiers". --- command/register.py | 5 ++++- tests/support.py | 7 ++++--- tests/test_register.py | 19 +++++++++++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/command/register.py b/command/register.py index 99545aff..9b39ed36 100644 --- a/command/register.py +++ b/command/register.py @@ -5,6 +5,7 @@ Implements the Distutils 'register' command (register with the repository). # created 2002/10/21, Richard Jones +import cgi import os, string, getpass import io import urllib.parse, urllib.request @@ -87,7 +88,9 @@ class register(PyPIRCCommand): ''' url = self.repository+'?:action=list_classifiers' response = urllib.request.urlopen(url) - log.info(response.read()) + content_type = response.getheader('content-type', 'text/plain') + encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii') + log.info(response.read().decode(encoding)) def verify_metadata(self): ''' Send the metadata to the package index server to be checked. diff --git a/tests/support.py b/tests/support.py index 84d92323..71ad4f42 100644 --- a/tests/support.py +++ b/tests/support.py @@ -32,14 +32,15 @@ class LoggingSilencer(object): def _log(self, level, msg, args): if level not in (DEBUG, INFO, WARN, ERROR, FATAL): raise ValueError('%s wrong log level' % str(level)) + if not isinstance(msg, str): + raise TypeError("msg should be str, not '%.200s'" + % (type(msg).__name__)) self.logs.append((level, msg, args)) def get_logs(self, *levels): def _format(msg, args): - if len(args) == 0: - return msg return msg % args - return [_format(msg, args) for level, msg, args + return [msg % args for level, msg, args in self.logs if level in levels] def clear_logs(self): diff --git a/tests/test_register.py b/tests/test_register.py index f4efa13d..8bcc8585 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -10,6 +10,7 @@ from test.support import check_warnings, run_unittest from distutils.command import register as register_module from distutils.command.register import register from distutils.errors import DistutilsSetupError +from distutils.log import INFO from distutils.tests.test_config import PyPIRCCommandTestCase @@ -58,12 +59,18 @@ class FakeOpener(object): def __call__(self, *args): return self - def open(self, req): + def open(self, req, data=None, timeout=None): self.reqs.append(req) return self def read(self): - return 'xxx' + return b'xxx' + + def getheader(self, name, default=None): + return { + 'content-type': 'text/plain; charset=utf-8', + }.get(name.lower(), default) + class RegisterTestCase(PyPIRCCommandTestCase): @@ -285,6 +292,14 @@ class RegisterTestCase(PyPIRCCommandTestCase): cmd.check_metadata() self.assertEqual(len(w.warnings), 1) + def test_list_classifiers(self): + cmd = self._get_cmd() + cmd.list_classifiers = 1 + cmd.run() + results = self.get_logs(INFO) + self.assertEqual(results, ['running check', 'xxx']) + + def test_suite(): return unittest.makeSuite(RegisterTestCase) -- cgit v1.2.1 From 17a7b8192a8fcba2649854c844ae22fa0f512085 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 22 Dec 2013 00:44:01 +0100 Subject: Fix urllib.request.build_opener mocking in test_distutils (should fix some random buildbot failures) --- tests/test_register.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_register.py b/tests/test_register.py index 8bcc8585..61801339 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -81,11 +81,13 @@ class RegisterTestCase(PyPIRCCommandTestCase): def _getpass(prompt): return 'password' getpass.getpass = _getpass + urllib.request._opener = None self.old_opener = urllib.request.build_opener self.conn = urllib.request.build_opener = FakeOpener() def tearDown(self): getpass.getpass = self._old_getpass + urllib.request._opener = None urllib.request.build_opener = self.old_opener super(RegisterTestCase, self).tearDown() -- cgit v1.2.1 From 9f4a8134cd20941bd90e6419948c7bdc83475ad0 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 22 Dec 2013 01:35:53 +0100 Subject: Issue #12226: HTTPS is now used by default when connecting to PyPI. --- config.py | 2 +- tests/test_config.py | 4 ++-- tests/test_upload.py | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config.py b/config.py index 1fd53346..a97635f2 100644 --- a/config.py +++ b/config.py @@ -21,7 +21,7 @@ password:%s class PyPIRCCommand(Command): """Base command that knows how to handle the .pypirc file """ - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' + DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi' DEFAULT_REALM = 'pypi' repository = None realm = None diff --git a/tests/test_config.py b/tests/test_config.py index 525bee94..12593610 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -87,7 +87,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi'), + ('repository', 'https://pypi.python.org/pypi'), ('server', 'server1'), ('username', 'me')] self.assertEqual(config, waited) @@ -96,7 +96,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, config = cmd._read_pypirc() config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi'), + ('repository', 'https://pypi.python.org/pypi'), ('server', 'server-login'), ('username', 'tarek')] self.assertEqual(config, waited) diff --git a/tests/test_upload.py b/tests/test_upload.py index 4c6464a3..d2696866 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -72,11 +72,11 @@ class uploadTestCase(PyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() - self.old_class = httpclient.HTTPConnection - self.conn = httpclient.HTTPConnection = FakeConnection() + self.old_class = httpclient.HTTPSConnection + self.conn = httpclient.HTTPSConnection = FakeConnection() def tearDown(self): - httpclient.HTTPConnection = self.old_class + httpclient.HTTPSConnection = self.old_class super(uploadTestCase, self).tearDown() def test_finalize_options(self): @@ -88,7 +88,7 @@ class uploadTestCase(PyPIRCCommandTestCase): cmd.finalize_options() for attr, waited in (('username', 'me'), ('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi')): + ('repository', 'https://pypi.python.org/pypi')): self.assertEqual(getattr(cmd, attr), waited) def test_saved_password(self): -- cgit v1.2.1 From a9d129418d7569a12e225e130711524a89868eca Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 22 Dec 2013 18:13:51 +0100 Subject: Fix TypeError on "setup.py upload --show-response". --- command/register.py | 5 +---- command/upload.py | 3 ++- config.py | 7 +++++++ tests/test_upload.py | 17 ++++++++++++++++- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/command/register.py b/command/register.py index 9b39ed36..55656c23 100644 --- a/command/register.py +++ b/command/register.py @@ -5,7 +5,6 @@ Implements the Distutils 'register' command (register with the repository). # created 2002/10/21, Richard Jones -import cgi import os, string, getpass import io import urllib.parse, urllib.request @@ -88,9 +87,7 @@ class register(PyPIRCCommand): ''' url = self.repository+'?:action=list_classifiers' response = urllib.request.urlopen(url) - content_type = response.getheader('content-type', 'text/plain') - encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii') - log.info(response.read().decode(encoding)) + log.info(self._read_pypi_response(response)) def verify_metadata(self): ''' Send the metadata to the package index server to be checked. diff --git a/command/upload.py b/command/upload.py index d2bc82ce..e30c1897 100644 --- a/command/upload.py +++ b/command/upload.py @@ -196,5 +196,6 @@ class upload(PyPIRCCommand): self.announce('Upload failed (%s): %s' % (status, reason), log.ERROR) if self.show_response: - msg = '\n'.join(('-' * 75, result.read(), '-' * 75)) + text = self._read_pypi_response(result) + msg = '\n'.join(('-' * 75, text, '-' * 75)) self.announce(msg, log.INFO) diff --git a/config.py b/config.py index a97635f2..7e10fff9 100644 --- a/config.py +++ b/config.py @@ -3,6 +3,7 @@ Provides the PyPIRCCommand class, the base class for the command classes that uses .pypirc in the distutils.command package. """ +import cgi import os from configparser import ConfigParser @@ -101,6 +102,12 @@ class PyPIRCCommand(Command): return {} + def _read_pypi_response(self, response): + """Read and decode a PyPI HTTP response.""" + content_type = response.getheader('content-type', 'text/plain') + encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii') + return response.read().decode(encoding) + def initialize_options(self): """Initialize options.""" self.repository = None diff --git a/tests/test_upload.py b/tests/test_upload.py index fbf80e38..8532369a 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -6,6 +6,7 @@ from test.support import run_unittest from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution +from distutils.log import INFO from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase @@ -48,6 +49,14 @@ class FakeOpen(object): self.req = None self.msg = 'OK' + def getheader(self, name, default=None): + return { + 'content-type': 'text/plain; charset=utf-8', + }.get(name.lower(), default) + + def read(self): + return b'xyzzy' + def getcode(self): return 200 @@ -108,10 +117,11 @@ class uploadTestCase(PyPIRCCommandTestCase): # lets run it pkg_dir, dist = self.create_dist(dist_files=dist_files) cmd = upload(dist) + cmd.show_response = 1 cmd.ensure_finalized() cmd.run() - # what did we send ? + # what did we send ? headers = dict(self.last_open.req.headers) self.assertEqual(headers['Content-length'], '2087') self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) @@ -120,6 +130,11 @@ class uploadTestCase(PyPIRCCommandTestCase): 'https://pypi.python.org/pypi') self.assertIn(b'xxx', self.last_open.req.data) + # The PyPI response body was echoed + results = self.get_logs(INFO) + self.assertIn('xyzzy\n', results[-1]) + + def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From 8ee4e0365d4d31b4d0b42511567fa2c2657dcdcb Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 22 Dec 2013 19:37:17 +0100 Subject: Fix bootstrap issue by importing the cgi module lazily --- config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.py b/config.py index 7e10fff9..106e1465 100644 --- a/config.py +++ b/config.py @@ -3,7 +3,6 @@ Provides the PyPIRCCommand class, the base class for the command classes that uses .pypirc in the distutils.command package. """ -import cgi import os from configparser import ConfigParser @@ -104,6 +103,7 @@ class PyPIRCCommand(Command): def _read_pypi_response(self, response): """Read and decode a PyPI HTTP response.""" + import cgi content_type = response.getheader('content-type', 'text/plain') encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii') return response.read().decode(encoding) -- cgit v1.2.1 From 889b0255164812a81f21cec8fae9c4cd6929b532 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Mon, 30 Dec 2013 14:39:46 -0600 Subject: Issue #19544, #6516: check ZLIB_SUPPORT, not zlib (which might not be bound) --- tests/test_archive_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 6b42c5a2..2d72af4a 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -308,7 +308,7 @@ class ArchiveUtilTestCase(support.TempdirManager, owner='kjhkjhkjg', group='oihohoh') self.assertTrue(os.path.exists(res)) - @unittest.skipUnless(zlib, "Requires zlib") + @unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib") @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") def test_tarfile_root_owner(self): tmpdir, tmpdir2, base_name = self._create_files() -- cgit v1.2.1 From 8fd408904c0379e17ba17f8ee313627645562dfe Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Mon, 30 Dec 2013 15:09:20 -0600 Subject: Issue #19544, #6516: check ZLIB_SUPPORT, not zlib (which might not be bound) --- tests/test_sdist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 49e267e2..5a04e0dd 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -429,7 +429,7 @@ class SDistTestCase(PyPIRCCommandTestCase): self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO', 'fake-1.0/README.manual']) - @unittest.skipUnless(zlib, "requires zlib") + @unittest.skipUnless(ZLIB_SUPPORT, "requires zlib") @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") @unittest.skipIf(find_executable('tar') is None, "The tar command is not found") -- cgit v1.2.1 From e6808a7ee674d31e2f10310237cee1dc7d1ce6ca Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 5 Jan 2014 04:40:25 -0800 Subject: Bump version number for 3.4.0b2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 38a50869..725a33d6 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0b1" +__version__ = "3.4.0b2" #--end constants-- -- cgit v1.2.1 From 7f50db7ceebeb2134f9000f8228588c7c18e59a6 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 25 Jan 2014 09:19:50 +0100 Subject: Bump to 3.3.4rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6747ab86..8a637d1b 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.3" +__version__ = "3.3.4rc1" #--end constants-- -- cgit v1.2.1 From 4a1da2e04a6c5ca6e81bf48c37daef69c38cdb23 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 26 Jan 2014 00:48:23 -0800 Subject: Version bump for 3.4.0b3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 725a33d6..bf9badd5 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0b2" +__version__ = "3.4.0b3" #--end constants-- -- cgit v1.2.1 From 51bb181329a0d4167451376ad8168a1af005ba70 Mon Sep 17 00:00:00 2001 From: Stefan Krah Date: Tue, 28 Jan 2014 15:04:40 +0100 Subject: Issue #9709: Revert 97fb852c5c26. Many extensions are not using PyMODINIT_FUNC. --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 9be59237..80689b63 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -538,7 +538,7 @@ class build_ext(Command): library_dirs=ext.library_dirs, runtime_library_dirs=ext.runtime_library_dirs, extra_postargs=extra_args, - export_symbols=ext.export_symbols, + export_symbols=self.get_export_symbols(ext), debug=self.debug, build_temp=self.build_temp, target_lang=language) -- cgit v1.2.1 From 4988103fdd5b6a0c02e3df2a282bea48a7a34a7f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 6 Feb 2014 22:49:45 +0200 Subject: Issue #20363. Fixed BytesWarning triggerred by test suite. Patch by Berker Peksag. --- command/register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/register.py b/command/register.py index 55656c23..b49f86fe 100644 --- a/command/register.py +++ b/command/register.py @@ -300,5 +300,5 @@ Your selection [default 1]: ''', log.INFO) result = 200, 'OK' if self.show_response: dashes = '-' * 75 - self.announce('%s%s%s' % (dashes, data, dashes)) + self.announce('%s%r%s' % (dashes, data, dashes)) return result -- cgit v1.2.1 From f5efce06e4cf3367c6f316bb78847c43eb4e2054 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 9 Feb 2014 08:43:05 +0100 Subject: Bump to 3.3.4 final --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8a637d1b..de5560b4 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.4rc1" +__version__ = "3.3.4" #--end constants-- -- cgit v1.2.1 From 154e4ab5dff1cc780dcfa04140506bc574e22179 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Mon, 10 Feb 2014 14:45:05 -0800 Subject: Python 3.4.0rc1: Version bump. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index bf9badd5..4c716e20 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0b3" +__version__ = "3.4.0rc1" #--end constants-- -- cgit v1.2.1 From ff69c43e5d22036c7f433331e26808fd53f2bf4d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 23 Feb 2014 08:30:06 +0100 Subject: Bump to 3.3.5rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index de5560b4..b79071a6 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.4" +__version__ = "3.3.5rc1" #--end constants-- -- cgit v1.2.1 From 63aab90f407ec2c12fc1bfcfaa50b0714687275c Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 23 Feb 2014 02:18:24 -0600 Subject: Version bump for Python 3.4.0rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 4c716e20..c03185bd 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0rc1" +__version__ = "3.4.0rc2" #--end constants-- -- cgit v1.2.1 From 27d5cd873de20e7ff51d7b13e515b73000e8f727 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 2 Mar 2014 09:19:03 +0100 Subject: Bump to 3.3.5rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b79071a6..7d40ab47 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.5rc1" +__version__ = "3.3.5rc2" #--end constants-- -- cgit v1.2.1 From 9c925df0618a7ae0eccfea65da04faab89b32fc7 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 9 Mar 2014 09:37:14 +0100 Subject: Bump to 3.3.5 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7d40ab47..2e8719f8 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.5rc2" +__version__ = "3.3.5" #--end constants-- -- cgit v1.2.1 From 971530e1532467ef3f1d412019cfc68cf5e041d9 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 9 Mar 2014 04:13:05 -0700 Subject: Version bump for 3.4.0rc3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c03185bd..954b50db 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0rc2" +__version__ = "3.4.0rc3" #--end constants-- -- cgit v1.2.1 From 7da8f82720d47877cea2324b54952ff2b19ff6f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Wed, 12 Mar 2014 03:34:02 -0400 Subject: =?UTF-8?q?Avoid=20=E2=80=9Cerror:=20None=E2=80=9D=20messages=20fr?= =?UTF-8?q?om=20distutils=20(#4931).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to Amaury Forgeot d’Arc and Philip J. Eby. --- core.py | 7 ++----- dir_util.py | 6 ++---- tests/test_util.py | 10 +++++++++- util.py | 23 ++++------------------- 4 files changed, 17 insertions(+), 29 deletions(-) diff --git a/core.py b/core.py index 260332a2..25d91baa 100644 --- a/core.py +++ b/core.py @@ -11,7 +11,6 @@ import sys from distutils.debug import DEBUG from distutils.errors import * -from distutils.util import grok_environment_error # Mainly import these so setup scripts can "from distutils.core import" them. from distutils.dist import Distribution @@ -149,13 +148,11 @@ def setup (**attrs): except KeyboardInterrupt: raise SystemExit("interrupted") except (IOError, os.error) as exc: - error = grok_environment_error(exc) - if DEBUG: - sys.stderr.write(error + "\n") + sys.stderr.write("error: %s\n" % (exc,)) raise else: - raise SystemExit(error) + raise SystemExit("error: %s" % (exc,)) except (DistutilsError, CCompilerError) as msg: diff --git a/dir_util.py b/dir_util.py index 2826ff80..6a72bdd4 100644 --- a/dir_util.py +++ b/dir_util.py @@ -2,7 +2,7 @@ Utility functions for manipulating directories and directory trees.""" -import os, sys +import os import errno from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log @@ -182,7 +182,6 @@ def remove_tree(directory, verbose=1, dry_run=0): Any errors are ignored (apart from being reported to stdout if 'verbose' is true). """ - from distutils.util import grok_environment_error global _path_created if verbose >= 1: @@ -199,8 +198,7 @@ def remove_tree(directory, verbose=1, dry_run=0): if abspath in _path_created: del _path_created[abspath] except (IOError, OSError) as exc: - log.warn(grok_environment_error( - exc, "error removing %s: " % directory)) + log.warn("error removing %s: %s", directory, exc) def ensure_relative(path): """Take the full path 'path', and make it a relative path. diff --git a/tests/test_util.py b/tests/test_util.py index 00219cfd..a1abf8f0 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -8,7 +8,8 @@ from test.support import run_unittest from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import (get_platform, convert_path, change_root, check_environ, split_quoted, strtobool, - rfc822_escape, byte_compile) + rfc822_escape, byte_compile, + grok_environment_error) from distutils import util # used to patch _environ_checked from distutils.sysconfig import get_config_vars from distutils import sysconfig @@ -285,6 +286,13 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): finally: sys.dont_write_bytecode = old_dont_write_bytecode + def test_grok_environment_error(self): + # test obsolete function to ensure backward compat (#4931) + exc = IOError("Unable to find batch file") + msg = grok_environment_error(exc) + self.assertEqual(msg, "error: Unable to find batch file") + + def test_suite(): return unittest.makeSuite(UtilTestCase) diff --git a/util.py b/util.py index 67d81663..b5589578 100644 --- a/util.py +++ b/util.py @@ -213,25 +213,10 @@ def subst_vars (s, local_vars): def grok_environment_error (exc, prefix="error: "): - """Generate a useful error message from an EnvironmentError (IOError or - OSError) exception object. Handles Python 1.5.1 and 1.5.2 styles, and - does what it can to deal with exception objects that don't have a - filename (which happens when the error is due to a two-file operation, - such as 'rename()' or 'link()'. Returns the error message as a string - prefixed with 'prefix'. - """ - # check for Python 1.5.2-style {IO,OS}Error exception objects - if hasattr(exc, 'filename') and hasattr(exc, 'strerror'): - if exc.filename: - error = prefix + "%s: %s" % (exc.filename, exc.strerror) - else: - # two-argument functions in posix module don't - # include the filename in the exception object! - error = prefix + "%s" % exc.strerror - else: - error = prefix + str(exc.args[-1]) - - return error + # Function kept for backward compatibility. + # Used to try clever things with EnvironmentErrors, + # but nowadays str(exception) produces good messages. + return prefix + str(exc) # Needed by 'split_quoted()' -- cgit v1.2.1 From fd9aa7570cbc4241b6d2b3cd177930bab0f018f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Thu, 13 Mar 2014 04:55:35 -0400 Subject: Make distutils error messages more helpful (#11599). When running external programs such as a C compiler and getting an error code, distutils only prints the program name. With this change, one can get the full command line by setting the DISTUTILS_DEBUG environment variable. This should have no compatibility issues, unless there are tools that depend on the exact format of distutils debug messages. --- spawn.py | 63 +++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/spawn.py b/spawn.py index f58c55f9..f66ff93d 100644 --- a/spawn.py +++ b/spawn.py @@ -10,6 +10,7 @@ import sys import os from distutils.errors import DistutilsPlatformError, DistutilsExecError +from distutils.debug import DEBUG from distutils import log def spawn(cmd, search_path=1, verbose=0, dry_run=0): @@ -28,6 +29,9 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0): Raise DistutilsExecError if running the program fails in any way; just return on success. """ + # cmd is documented as a list, but just in case some code passes a tuple + # in, protect our %-formatting code against horrible death + cmd = list(cmd) if os.name == 'posix': _spawn_posix(cmd, search_path, dry_run=dry_run) elif os.name == 'nt': @@ -67,12 +71,16 @@ def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): rc = os.spawnv(os.P_WAIT, executable, cmd) except OSError as exc: # this seems to happen when the command isn't found + if not DEBUG: + cmd = executable raise DistutilsExecError( - "command '%s' failed: %s" % (cmd[0], exc.args[-1])) + "command %r failed: %s" % (cmd, exc.args[-1])) if rc != 0: # and this reflects the command running but failing + if not DEBUG: + cmd = executable raise DistutilsExecError( - "command '%s' failed with exit status %d" % (cmd[0], rc)) + "command %r failed with exit status %d" % (cmd, rc)) def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): executable = cmd[0] @@ -86,13 +94,17 @@ def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): rc = os.spawnv(os.P_WAIT, executable, cmd) except OSError as exc: # this seems to happen when the command isn't found + if not DEBUG: + cmd = executable raise DistutilsExecError( - "command '%s' failed: %s" % (cmd[0], exc.args[-1])) + "command %r failed: %s" % (cmd, exc.args[-1])) if rc != 0: # and this reflects the command running but failing - log.debug("command '%s' failed with exit status %d" % (cmd[0], rc)) + if not DEBUG: + cmd = executable + log.debug("command %r failed with exit status %d" % (cmd, rc)) raise DistutilsExecError( - "command '%s' failed with exit status %d" % (cmd[0], rc)) + "command %r failed with exit status %d" % (cmd, rc)) if sys.platform == 'darwin': from distutils import sysconfig @@ -103,8 +115,9 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): log.info(' '.join(cmd)) if dry_run: return + executable = cmd[0] exec_fn = search_path and os.execvp or os.execv - exec_args = [cmd[0], cmd] + env = None if sys.platform == 'darwin': global _cfg_target, _cfg_target_split if _cfg_target is None: @@ -125,17 +138,23 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): env = dict(os.environ, MACOSX_DEPLOYMENT_TARGET=cur_target) exec_fn = search_path and os.execvpe or os.execve - exec_args.append(env) pid = os.fork() if pid == 0: # in the child try: - exec_fn(*exec_args) + if env is None: + exec_fn(executable, cmd) + else: + exec_fn(executable, cmd, env) except OSError as e: - sys.stderr.write("unable to execute %s: %s\n" - % (cmd[0], e.strerror)) + if not DEBUG: + cmd = executable + sys.stderr.write("unable to execute %r: %s\n" + % (cmd, e.strerror)) os._exit(1) - sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0]) + if not DEBUG: + cmd = executable + sys.stderr.write("unable to execute %r for unknown reasons" % cmd) os._exit(1) else: # in the parent # Loop until the child either exits or is terminated by a signal @@ -147,26 +166,34 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): import errno if exc.errno == errno.EINTR: continue + if not DEBUG: + cmd = executable raise DistutilsExecError( - "command '%s' failed: %s" % (cmd[0], exc.args[-1])) + "command %r failed: %s" % (cmd, exc.args[-1])) if os.WIFSIGNALED(status): + if not DEBUG: + cmd = executable raise DistutilsExecError( - "command '%s' terminated by signal %d" - % (cmd[0], os.WTERMSIG(status))) + "command %r terminated by signal %d" + % (cmd, os.WTERMSIG(status))) elif os.WIFEXITED(status): exit_status = os.WEXITSTATUS(status) if exit_status == 0: return # hey, it succeeded! else: + if not DEBUG: + cmd = executable raise DistutilsExecError( - "command '%s' failed with exit status %d" - % (cmd[0], exit_status)) + "command %r failed with exit status %d" + % (cmd, exit_status)) elif os.WIFSTOPPED(status): continue else: + if not DEBUG: + cmd = executable raise DistutilsExecError( - "unknown error executing '%s': termination status %d" - % (cmd[0], status)) + "unknown error executing %r: termination status %d" + % (cmd, status)) def find_executable(executable, path=None): """Tries to find 'executable' in the directories listed in 'path'. -- cgit v1.2.1 From 8028742c28561801bde9e5a0ffdd287fc11fb5ff Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 15 Mar 2014 22:34:24 -0700 Subject: Release bump for 3.4.0 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 954b50db..04804c1f 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0rc3" +__version__ = "3.4.0" #--end constants-- -- cgit v1.2.1 From 25d93525889d4bc3e8e4dc39091482ada4fbd84b Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 16 Mar 2014 23:05:59 -0700 Subject: Version bump to 3.5, step 2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 4c716e20..328bea6f 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0rc1" +__version__ = "3.5.0a0" #--end constants-- -- cgit v1.2.1 From a42e746af0306d9d6d10f7dfc46b95394f7829a7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 20 Mar 2014 08:50:33 +0100 Subject: Issue #20978: Remove last part of OS/2 support in distutils --- spawn.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/spawn.py b/spawn.py index d9cf950a..22e87e8f 100644 --- a/spawn.py +++ b/spawn.py @@ -36,8 +36,6 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0): _spawn_posix(cmd, search_path, dry_run=dry_run) elif os.name == 'nt': _spawn_nt(cmd, search_path, dry_run=dry_run) - elif os.name == 'os2': - _spawn_os2(cmd, search_path, dry_run=dry_run) else: raise DistutilsPlatformError( "don't know how to spawn programs on platform '%s'" % os.name) -- cgit v1.2.1 From 84b76498861b75f70242a11c1751a382532e5b81 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 27 Mar 2014 14:14:16 +0100 Subject: Minor cosmetic enhancement to provide a more readable repr()esentation of Extension instances: - + --- extension.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extension.py b/extension.py index a93655af..cc04a18a 100644 --- a/extension.py +++ b/extension.py @@ -131,6 +131,14 @@ class Extension: msg = "Unknown Extension options: %s" % options warnings.warn(msg) + def __repr__(self): + return '<%s.%s(%r) at %#x>' % ( + self.__class__.__module__, + self.__class__.__name__, + self.name, + id(self)) + + def read_setup_file(filename): """Reads a Setup file and returns Extension instances.""" from distutils.sysconfig import (parse_makefile, expand_makefile_vars, -- cgit v1.2.1 From 1152b05d319cd460945037cdf09529f754c1b4db Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 4 May 2014 05:06:24 -0700 Subject: Version bump for 3.4.1rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 04804c1f..b0002494 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0" +__version__ = "3.4.1rc1" #--end constants-- -- cgit v1.2.1 From 19565334cb36e694328566d3f5011a5b57d6bee3 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Wed, 7 May 2014 04:44:42 +0200 Subject: - Issue #17752: Fix distutils tests when run from the installed location. --- tests/support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/support.py b/tests/support.py index 71ad4f42..7385c6bb 100644 --- a/tests/support.py +++ b/tests/support.py @@ -207,4 +207,4 @@ def fixup_build_ext(cmd): cmd.library_dirs = [] else: name, equals, value = runshared.partition('=') - cmd.library_dirs = value.split(os.pathsep) + cmd.library_dirs = [d for d in value.split(os.pathsep) if d] -- cgit v1.2.1 From ddf2ea79184c3b3e10f33bbab12ac995395d15fb Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Wed, 7 May 2014 12:57:44 +0200 Subject: - Issue #17752: Fix distutils tests when run from the installed location. --- tests/support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/support.py b/tests/support.py index 71ad4f42..7385c6bb 100644 --- a/tests/support.py +++ b/tests/support.py @@ -207,4 +207,4 @@ def fixup_build_ext(cmd): cmd.library_dirs = [] else: name, equals, value = runshared.partition('=') - cmd.library_dirs = value.split(os.pathsep) + cmd.library_dirs = [d for d in value.split(os.pathsep) if d] -- cgit v1.2.1 From 8cbbd8e2876d95e3f338a5342e2f11c42e780768 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 May 2014 13:20:28 -0400 Subject: Clean up style in distutils upload command --- command/upload.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/command/upload.py b/command/upload.py index d6762e46..4882db42 100644 --- a/command/upload.py +++ b/command/upload.py @@ -1,18 +1,21 @@ -"""distutils.command.upload +""" +distutils.command.upload -Implements the Distutils 'upload' subcommand (upload package to PyPI).""" +Implements the Distutils 'upload' subcommand (upload package to a package +index). +""" -from distutils.errors import * -from distutils.core import PyPIRCCommand -from distutils.spawn import spawn -from distutils import log import sys -import os, io -import socket +import os +import io import platform from base64 import standard_b64encode from urllib.request import urlopen, Request, HTTPError from urllib.parse import urlparse +from distutils.errors import * +from distutils.core import PyPIRCCommand +from distutils.spawn import spawn +from distutils import log # this keeps compatibility for 2.3 and 2.4 if sys.version < "2.5": @@ -106,7 +109,7 @@ class upload(PyPIRCCommand): 'md5_digest': md5(content).hexdigest(), # additional meta-data - 'metadata_version' : '1.0', + 'metadata_version': '1.0', 'summary': meta.get_description(), 'home_page': meta.get_url(), 'author': meta.get_contact(), @@ -167,13 +170,15 @@ class upload(PyPIRCCommand): body.write(b"\n") body = body.getvalue() - self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) + msg = "Submitting %s to %s" % (filename, self.repository) + self.announce(msg, log.INFO) # build the Request - headers = {'Content-type': - 'multipart/form-data; boundary=%s' % boundary, - 'Content-length': str(len(body)), - 'Authorization': auth} + headers = { + 'Content-type': 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth, + } request = Request(self.repository, data=body, headers=headers) -- cgit v1.2.1 From 0983f9dd893c03aabbaa826bf9c644a88b67a5b4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 May 2014 13:21:02 -0400 Subject: Replace import * with explicit import --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 4882db42..78244666 100644 --- a/command/upload.py +++ b/command/upload.py @@ -12,7 +12,7 @@ import platform from base64 import standard_b64encode from urllib.request import urlopen, Request, HTTPError from urllib.parse import urlparse -from distutils.errors import * +from distutils.errors import DistutilsOptionError from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log -- cgit v1.2.1 From 5eb3849be5200231ac49e5b2c6b58ae63f1ed023 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 May 2014 13:22:43 -0400 Subject: Drop support for Python 2.4 in upload command. --- command/upload.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/command/upload.py b/command/upload.py index 78244666..75498b14 100644 --- a/command/upload.py +++ b/command/upload.py @@ -5,10 +5,10 @@ Implements the Distutils 'upload' subcommand (upload package to a package index). """ -import sys import os import io import platform +import hashlib from base64 import standard_b64encode from urllib.request import urlopen, Request, HTTPError from urllib.parse import urlparse @@ -17,12 +17,6 @@ from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log -# this keeps compatibility for 2.3 and 2.4 -if sys.version < "2.5": - from md5 import md5 -else: - from hashlib import md5 - class upload(PyPIRCCommand): description = "upload binary package to PyPI" @@ -106,7 +100,7 @@ class upload(PyPIRCCommand): 'content': (os.path.basename(filename),content), 'filetype': command, 'pyversion': pyversion, - 'md5_digest': md5(content).hexdigest(), + 'md5_digest': hashlib.md5(content).hexdigest(), # additional meta-data 'metadata_version': '1.0', -- cgit v1.2.1 From 41140cfe90c79dcb3ac74486c192e435c0bd1c09 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 May 2014 13:24:18 -0400 Subject: Replace overly-aggressive comparison for type equality with an isinstance check. --- command/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index 75498b14..c279b591 100644 --- a/command/upload.py +++ b/command/upload.py @@ -146,7 +146,7 @@ class upload(PyPIRCCommand): for key, value in data.items(): title = '\nContent-Disposition: form-data; name="%s"' % key # handle multiple entries for the same name - if type(value) != type([]): + if not isinstance(value, list): value = [value] for value in value: if type(value) is tuple: -- cgit v1.2.1 From b160cdaa13bd69e006afee3763d05cf4d1fc6441 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 May 2014 13:24:58 -0400 Subject: Reindent long line --- command/upload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index c279b591..b9064352 100644 --- a/command/upload.py +++ b/command/upload.py @@ -57,7 +57,8 @@ class upload(PyPIRCCommand): def run(self): if not self.distribution.dist_files: - raise DistutilsOptionError("No dist file created in earlier command") + msg = "No dist file created in earlier command" + raise DistutilsOptionError(msg) for command, pyversion, filename in self.distribution.dist_files: self.upload_file(command, pyversion, filename) -- cgit v1.2.1 From 37ceab37dd39345a8dab9c88082bd1a644ad7311 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 17 May 2014 21:46:35 -0700 Subject: Version bump for 3.4.1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b0002494..9463a35c 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.1rc1" +__version__ = "3.4.1" #--end constants-- -- cgit v1.2.1 From d47b6a2fadf767fe5caa430dd660f1f774affe4f Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 18 Jun 2014 23:07:46 -0400 Subject: Issue #21722: The distutils "upload" command now exits with a non-zero return code when uploading fails. Patch by Martin Dengler. --- command/upload.py | 15 ++++++++------- tests/test_upload.py | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/command/upload.py b/command/upload.py index d6762e46..180be7c7 100644 --- a/command/upload.py +++ b/command/upload.py @@ -2,10 +2,6 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" -from distutils.errors import * -from distutils.core import PyPIRCCommand -from distutils.spawn import spawn -from distutils import log import sys import os, io import socket @@ -13,6 +9,10 @@ import platform from base64 import standard_b64encode from urllib.request import urlopen, Request, HTTPError from urllib.parse import urlparse +from distutils.errors import DistutilsError, DistutilsOptionError +from distutils.core import PyPIRCCommand +from distutils.spawn import spawn +from distutils import log # this keeps compatibility for 2.3 and 2.4 if sys.version < "2.5": @@ -184,7 +184,7 @@ class upload(PyPIRCCommand): reason = result.msg except OSError as e: self.announce(str(e), log.ERROR) - return + raise except HTTPError as e: status = e.code reason = e.msg @@ -193,8 +193,9 @@ class upload(PyPIRCCommand): self.announce('Server response (%s): %s' % (status, reason), log.INFO) else: - self.announce('Upload failed (%s): %s' % (status, reason), - log.ERROR) + msg = 'Upload failed (%s): %s' % (status, reason) + self.announce(msg, log.ERROR) + raise DistutilsError(msg) if self.show_response: text = self._read_pypi_response(result) msg = '\n'.join(('-' * 75, text, '-' * 75)) diff --git a/tests/test_upload.py b/tests/test_upload.py index f53eb266..0380f979 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -6,6 +6,7 @@ from test.support import run_unittest from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution +from distutils.errors import DistutilsError from distutils.log import INFO from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase @@ -41,13 +42,14 @@ username:me class FakeOpen(object): - def __init__(self, url): + def __init__(self, url, msg=None, code=None): self.url = url if not isinstance(url, str): self.req = url else: self.req = None - self.msg = 'OK' + self.msg = msg or 'OK' + self.code = code or 200 def getheader(self, name, default=None): return { @@ -58,7 +60,7 @@ class FakeOpen(object): return b'xyzzy' def getcode(self): - return 200 + return self.code class uploadTestCase(PyPIRCCommandTestCase): @@ -68,13 +70,15 @@ class uploadTestCase(PyPIRCCommandTestCase): self.old_open = upload_mod.urlopen upload_mod.urlopen = self._urlopen self.last_open = None + self.next_msg = None + self.next_code = None def tearDown(self): upload_mod.urlopen = self.old_open super(uploadTestCase, self).tearDown() def _urlopen(self, url): - self.last_open = FakeOpen(url) + self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code) return self.last_open def test_finalize_options(self): @@ -135,6 +139,10 @@ class uploadTestCase(PyPIRCCommandTestCase): results = self.get_logs(INFO) self.assertIn('xyzzy\n', results[-1]) + def test_upload_fails(self): + self.next_msg = "Not Found" + self.next_code = 404 + self.assertRaises(DistutilsError, self.test_upload) def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From a8edd4b874fd83cf8d53b0c410c97477a3816c38 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Wed, 25 Jun 2014 13:36:14 -0700 Subject: Issue #21811: Anticipated fixes to 3.x and 2.7 for OS X 10.10 Yosemite. --- tests/test_build_ext.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 9853abd4..e9958667 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -444,8 +444,16 @@ class BuildExtTestCase(TempdirManager, # get the deployment target that the interpreter was built with target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - target = tuple(map(int, target.split('.'))) - target = '%02d%01d0' % target + target = tuple(map(int, target.split('.')[0:2])) + # format the target value as defined in the Apple + # Availability Macros. We can't use the macro names since + # at least one value we test with will not exist yet. + if target[1] < 10: + # for 10.1 through 10.9.x -> "10n0" + target = '%02d%01d0' % target + else: + # for 10.10 and beyond -> "10nn00" + target = '%02d%02d00' % target deptarget_ext = Extension( 'deptarget', [deptarget_c], -- cgit v1.2.1 From e64901cd8e4b49102390886ee264889a226c1945 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 2 Jul 2014 08:36:19 -0400 Subject: Normalize style per PEP-8 --- dist.py | 69 ++++++++++++++++++++++++++++++++--------------------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/dist.py b/dist.py index 7eb04bc3..ffb33ff6 100644 --- a/dist.py +++ b/dist.py @@ -4,7 +4,9 @@ Provides the Distribution class, which represents the module distribution being built/installed/distributed. """ -import sys, os, re +import sys +import os +import re from email import message_from_file try: @@ -22,7 +24,7 @@ from distutils.debug import DEBUG # the same as a Python NAME -- I don't allow leading underscores. The fact # that they're very similar is no coincidence; the default naming scheme is # to look for a Python module named after the command. -command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') +command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') class Distribution: @@ -39,7 +41,6 @@ class Distribution: See the code for 'setup()', in core.py, for details. """ - # 'global_options' describes the command-line options that may be # supplied to the setup script prior to any actual commands. # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of @@ -48,12 +49,13 @@ class Distribution: # don't want to pollute the commands with too many options that they # have minimal control over. # The fourth entry for verbose means that it can be repeated. - global_options = [('verbose', 'v', "run verbosely (default)", 1), - ('quiet', 'q', "run quietly (turns verbosity off)"), - ('dry-run', 'n', "don't actually do anything"), - ('help', 'h', "show detailed help message"), - ('no-user-cfg', None, - 'ignore pydistutils.cfg in your home directory'), + global_options = [ + ('verbose', 'v', "run verbosely (default)", 1), + ('quiet', 'q', "run quietly (turns verbosity off)"), + ('dry-run', 'n', "don't actually do anything"), + ('help', 'h', "show detailed help message"), + ('no-user-cfg', None, + 'ignore pydistutils.cfg in your home directory'), ] # 'common_usage' is a short (2-3 line) string describing the common @@ -115,10 +117,9 @@ Common commands: (see '--help-commands' for more) # negative options are options that exclude other options negative_opt = {'quiet': 'verbose'} - # -- Creation/initialization methods ------------------------------- - def __init__ (self, attrs=None): + def __init__(self, attrs=None): """Construct a new Distribution instance: initialize all the attributes of a Distribution, and then use 'attrs' (a dictionary mapping attribute names to values) to assign some of those @@ -532,15 +533,15 @@ Common commands: (see '--help-commands' for more) # to be sure that the basic "command" interface is implemented. if not issubclass(cmd_class, Command): raise DistutilsClassError( - "command class %s must subclass Command" % cmd_class) + "command class %s must subclass Command" % cmd_class) # Also make sure that the command object provides a list of its # known options. if not (hasattr(cmd_class, 'user_options') and isinstance(cmd_class.user_options, list)): - raise DistutilsClassError(("command class %s must provide " + - "'user_options' attribute (a list of tuples)") % \ - cmd_class) + msg = ("command class %s must provide " + "'user_options' attribute (a list of tuples)") + raise DistutilsClassError(msg % cmd_class) # If the command class has a list of negative alias options, # merge it in with the global negative aliases. @@ -552,12 +553,11 @@ Common commands: (see '--help-commands' for more) # Check for help_options in command class. They have a different # format (tuple of four) so we need to preprocess them here. if (hasattr(cmd_class, 'help_options') and - isinstance(cmd_class.help_options, list)): + isinstance(cmd_class.help_options, list)): help_options = fix_help_options(cmd_class.help_options) else: help_options = [] - # All commands support the global options too, just by adding # in 'global_options'. parser.set_option_table(self.global_options + @@ -570,7 +570,7 @@ Common commands: (see '--help-commands' for more) return if (hasattr(cmd_class, 'help_options') and - isinstance(cmd_class.help_options, list)): + isinstance(cmd_class.help_options, list)): help_option_found=0 for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): @@ -647,7 +647,7 @@ Common commands: (see '--help-commands' for more) else: klass = self.get_command_class(command) if (hasattr(klass, 'help_options') and - isinstance(klass.help_options, list)): + isinstance(klass.help_options, list)): parser.set_option_table(klass.user_options + fix_help_options(klass.help_options)) else: @@ -814,7 +814,7 @@ Common commands: (see '--help-commands' for more) klass_name = command try: - __import__ (module_name) + __import__(module_name) module = sys.modules[module_name] except ImportError: continue @@ -823,8 +823,8 @@ Common commands: (see '--help-commands' for more) klass = getattr(module, klass_name) except AttributeError: raise DistutilsModuleError( - "invalid command '%s' (no class '%s' in module '%s')" - % (command, klass_name, module_name)) + "invalid command '%s' (no class '%s' in module '%s')" + % (command, klass_name, module_name)) self.cmdclass[command] = klass return klass @@ -840,7 +840,7 @@ Common commands: (see '--help-commands' for more) cmd_obj = self.command_obj.get(command) if not cmd_obj and create: if DEBUG: - self.announce("Distribution.get_command_obj(): " \ + self.announce("Distribution.get_command_obj(): " "creating '%s' command object" % command) klass = self.get_command_class(command) @@ -897,8 +897,8 @@ Common commands: (see '--help-commands' for more) setattr(command_obj, option, value) else: raise DistutilsOptionError( - "error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) + "error in %s: command '%s' has no such option '%s'" + % (source, command_name, option)) except ValueError as msg: raise DistutilsOptionError(msg) @@ -974,7 +974,6 @@ Common commands: (see '--help-commands' for more) cmd_obj.run() self.have_run[command] = 1 - # -- Distribution query methods ------------------------------------ def has_pure_modules(self): @@ -1112,17 +1111,17 @@ class DistributionMetadata: """ version = '1.0' if (self.provides or self.requires or self.obsoletes or - self.classifiers or self.download_url): + self.classifiers or self.download_url): version = '1.1' file.write('Metadata-Version: %s\n' % version) - file.write('Name: %s\n' % self.get_name() ) - file.write('Version: %s\n' % self.get_version() ) - file.write('Summary: %s\n' % self.get_description() ) - file.write('Home-page: %s\n' % self.get_url() ) - file.write('Author: %s\n' % self.get_contact() ) - file.write('Author-email: %s\n' % self.get_contact_email() ) - file.write('License: %s\n' % self.get_license() ) + file.write('Name: %s\n' % self.get_name()) + file.write('Version: %s\n' % self.get_version()) + file.write('Summary: %s\n' % self.get_description()) + file.write('Home-page: %s\n' % self.get_url()) + file.write('Author: %s\n' % self.get_contact()) + file.write('Author-email: %s\n' % self.get_contact_email()) + file.write('License: %s\n' % self.get_license()) if self.download_url: file.write('Download-URL: %s\n' % self.download_url) @@ -1131,7 +1130,7 @@ class DistributionMetadata: keywords = ','.join(self.get_keywords()) if keywords: - file.write('Keywords: %s\n' % keywords ) + file.write('Keywords: %s\n' % keywords) self._write_list(file, 'Platform', self.get_platforms()) self._write_list(file, 'Classifier', self.get_classifiers()) -- cgit v1.2.1 From 9c8e39e04f9391ecc42bce0000f6fba119dae6eb Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sun, 6 Jul 2014 16:14:33 -0700 Subject: Issue #21923: Prevent AttributeError in distutils.sysconfig.customize_compiler due to possible uninitialized _config_vars. Original patch by Alex Gaynor. --- sysconfig.py | 3 ++- tests/test_sysconfig.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 75537db8..5b94fa23 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -179,7 +179,8 @@ def customize_compiler(compiler): # version and build tools may not support the same set # of CPU architectures for universal builds. global _config_vars - if not _config_vars.get('CUSTOMIZED_OSX_COMPILER', ''): + # Use get_config_var() to ensure _config_vars is initialized. + if not get_config_var('CUSTOMIZED_OSX_COMPILER'): import _osx_support _osx_support.customize_compiler(_config_vars) _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 95fa9dc1..fc4d1de1 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,6 +1,9 @@ """Tests for distutils.sysconfig.""" import os import shutil +import subprocess +import sys +import textwrap import unittest from distutils import sysconfig @@ -174,6 +177,25 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): self.assertIsNotNone(vars['SO']) self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) + def test_customize_compiler_before_get_config_vars(self): + # Issue #21923: test that a Distribution compiler + # instance can be called without an explicit call to + # get_config_vars(). + with open(TESTFN, 'w') as f: + f.writelines(textwrap.dedent('''\ + from distutils.core import Distribution + config = Distribution().get_command_obj('config') + # try_compile may pass or it may fail if no compiler + # is found but it should not raise an exception. + rc = config.try_compile('int x;') + ''')) + p = subprocess.Popen([str(sys.executable), TESTFN], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + outs, errs = p.communicate() + self.assertEqual(0, p.returncode, "Subprocess failed: " + outs) + def test_suite(): suite = unittest.TestSuite() -- cgit v1.2.1 From 5ef6395c634d60e1e7aff46970590b615fc2b754 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 22 Jul 2014 15:00:37 +0300 Subject: Issue #22032: __qualname__ instead of __name__ is now always used to format fully qualified class names of Python implemented classes. --- extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.py b/extension.py index cc04a18a..7efbb74f 100644 --- a/extension.py +++ b/extension.py @@ -134,7 +134,7 @@ class Extension: def __repr__(self): return '<%s.%s(%r) at %#x>' % ( self.__class__.__module__, - self.__class__.__name__, + self.__class__.__qualname__, self.name, id(self)) -- cgit v1.2.1 From 3100d99a48f564f3d0673ce3f972a823fa514158 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 17 Aug 2014 23:00:42 -0500 Subject: remove 2.2 and 2.6 compat code (closes #22200) Patch from Thomas Kluyver. --- command/install.py | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/command/install.py b/command/install.py index 456511cd..d768dc54 100644 --- a/command/install.py +++ b/command/install.py @@ -15,32 +15,17 @@ from distutils.util import convert_path, subst_vars, change_root from distutils.util import get_platform from distutils.errors import DistutilsOptionError -# this keeps compatibility from 2.3 to 2.5 -if sys.version < "2.6": - USER_BASE = None - USER_SITE = None - HAS_USER_SITE = False -else: - from site import USER_BASE - from site import USER_SITE - HAS_USER_SITE = True - -if sys.version < "2.2": - WINDOWS_SCHEME = { - 'purelib': '$base', - 'platlib': '$base', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - } -else: - WINDOWS_SCHEME = { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - } +from site import USER_BASE +from site import USER_SITE +HAS_USER_SITE = True + +WINDOWS_SCHEME = { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', +} INSTALL_SCHEMES = { 'unix_prefix': { -- cgit v1.2.1 From 83b03cdd69f31da4b693db56f0ee8631e6ecff88 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Fri, 29 Aug 2014 07:07:35 +0300 Subject: Issue #22182: Use e.args to unpack exceptions correctly in distutils.file_util.move_file. Patch by Claudiu Popa. --- file_util.py | 4 ++-- tests/test_file_util.py | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/file_util.py b/file_util.py index f6ed290f..7b14efbc 100644 --- a/file_util.py +++ b/file_util.py @@ -194,7 +194,7 @@ def move_file (src, dst, try: os.rename(src, dst) except OSError as e: - (num, msg) = e + (num, msg) = e.args if num == errno.EXDEV: copy_it = True else: @@ -206,7 +206,7 @@ def move_file (src, dst, try: os.unlink(src) except OSError as e: - (num, msg) = e + (num, msg) = e.args try: os.unlink(dst) except OSError: diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 3c3e3dcb..270f81eb 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -2,10 +2,13 @@ import unittest import os import shutil +import errno +from unittest.mock import patch from distutils.file_util import move_file from distutils import log from distutils.tests import support +from distutils.errors import DistutilsFileError from test.support import run_unittest class FileUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -58,6 +61,23 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ['moving %s -> %s' % (self.source, self.target_dir)] self.assertEqual(self._logs, wanted) + @patch('os.rename', side_effect=OSError('wrong', 1)) + def test_move_file_exception_unpacking_rename(self, _): + # see issue 22182 + with self.assertRaises(DistutilsFileError): + with open(self.source, 'w') as fobj: + fobj.write('spam eggs') + move_file(self.source, self.target, verbose=0) + + @patch('os.rename', side_effect=OSError(errno.EXDEV, 'wrong')) + @patch('os.unlink', side_effect=OSError('wrong', 1)) + def test_move_file_exception_unpacking_unlink(self, rename, unlink): + # see issue 22182 + with self.assertRaises(DistutilsFileError): + with open(self.source, 'w') as fobj: + fobj.write('spam eggs') + move_file(self.source, self.target, verbose=0) + def test_suite(): return unittest.makeSuite(FileUtilTestCase) -- cgit v1.2.1 From c1f5d6da2805e44809fec3e76b8a3bb9f45956f5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 13:43:02 -0400 Subject: Remove unused import --- tests/test_dir_util.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 1589f129..7e84721f 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -2,7 +2,6 @@ import unittest import os import stat -import shutil import sys from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, -- cgit v1.2.1 From c73fc06c76dd902e3d913711852fd060fd3278b9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 15:00:47 -0400 Subject: Correct indent --- tests/test_dir_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 7e84721f..6181ec6e 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -51,7 +51,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): self.assertEqual(self._logs, wanted) @unittest.skipIf(sys.platform.startswith('win'), - "This test is only appropriate for POSIX-like systems.") + "This test is only appropriate for POSIX-like systems.") def test_mkpath_with_custom_mode(self): # Get and set the current umask value for testing mode bits. umask = os.umask(0o002) -- cgit v1.2.1 From cc179ffe784d64d8ad67f77d8ee74610ae4f7faa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 15:02:42 -0400 Subject: #22315: Add test to capture the failure. --- tests/test_dir_util.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 6181ec6e..eb83497f 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,7 +3,9 @@ import unittest import os import stat import sys +import contextlib +from distutils import dir_util, errors from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, ensure_relative) @@ -11,6 +13,20 @@ from distutils import log from distutils.tests import support from test.support import run_unittest + +@contextlib.context_manager +def patch_obj(obj, attr, replacement): + """ + A poor man's mock.patch.object + """ + orig = getattr(obj, attr) + try: + setattr(obj, attr, replacement) + yield + finally: + setattr(obj, attr, orig) + + class DirUtilTestCase(support.TempdirManager, unittest.TestCase): def _log(self, msg, *args): @@ -119,6 +135,19 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') + def test_copy_tree_exception_in_listdir(self): + """ + An exception in listdir should raise a DistutilsFileError + """ + def new_listdir(path): + raise OSError() + # simulate a transient network error or other failure invoking listdir + with patch_obj(os, 'listdir', new_listdir): + args = 'src', None + exc = errors.DistutilsFileError + self.assertRaises(exc, dir_util.copy_tree, *args) + + def test_suite(): return unittest.makeSuite(DirUtilTestCase) -- cgit v1.2.1 From d45818441e0698244819349b0bee441c9d9328de Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 17:31:32 -0400 Subject: #22315: Use technique outlined in test_file_util --- tests/test_dir_util.py | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index eb83497f..c9f789c8 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,7 +3,7 @@ import unittest import os import stat import sys -import contextlib +from unittest.mock import patch from distutils import dir_util, errors from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, @@ -14,19 +14,6 @@ from distutils.tests import support from test.support import run_unittest -@contextlib.context_manager -def patch_obj(obj, attr, replacement): - """ - A poor man's mock.patch.object - """ - orig = getattr(obj, attr) - try: - setattr(obj, attr, replacement) - yield - finally: - setattr(obj, attr, orig) - - class DirUtilTestCase(support.TempdirManager, unittest.TestCase): def _log(self, msg, *args): @@ -135,17 +122,13 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') - def test_copy_tree_exception_in_listdir(self): + @patch('os.listdir', side_effect=OSError()) + def test_copy_tree_exception_in_listdir(self, listdir): """ An exception in listdir should raise a DistutilsFileError """ - def new_listdir(path): - raise OSError() - # simulate a transient network error or other failure invoking listdir - with patch_obj(os, 'listdir', new_listdir): - args = 'src', None - exc = errors.DistutilsFileError - self.assertRaises(exc, dir_util.copy_tree, *args) + with self.assertRaises(errors.DistutilsFileError): + dir_util.copy_tree('src', None) def test_suite(): -- cgit v1.2.1 From 3896d0af83329e4c5e4d63bf76535a9de51c0772 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 17:37:35 -0400 Subject: #22315: Provide an actual directory during test invocation. --- tests/test_dir_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index c9f789c8..51e754a0 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -128,7 +128,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): An exception in listdir should raise a DistutilsFileError """ with self.assertRaises(errors.DistutilsFileError): - dir_util.copy_tree('src', None) + dir_util.copy_tree(self.target, None) def test_suite(): -- cgit v1.2.1 From 58b1aba47823b942ab22d5c260d96707fe15c6dd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 17:51:22 -0400 Subject: #22315: Use an existent directory for 'src' to trigger appropriate behavior. --- tests/test_dir_util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index 51e754a0..d2696b82 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -128,7 +128,8 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): An exception in listdir should raise a DistutilsFileError """ with self.assertRaises(errors.DistutilsFileError): - dir_util.copy_tree(self.target, None) + src = self.tempdirs[-1] + dir_util.copy_tree(src, None) def test_suite(): -- cgit v1.2.1 From 01dbd70d47c352d868dce71898197ebe4c04f277 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 17:42:20 -0400 Subject: #22315: Use advertised API for OSError --- dir_util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dir_util.py b/dir_util.py index 9879b0dc..0924c9b0 100644 --- a/dir_util.py +++ b/dir_util.py @@ -125,12 +125,11 @@ def copy_tree(src, dst, preserve_mode=1, preserve_times=1, try: names = os.listdir(src) except OSError as e: - (errno, errstr) = e if dry_run: names = [] else: raise DistutilsFileError( - "error listing files in '%s': %s" % (src, errstr)) + "error listing files in '%s': %s" % (src, e.strerror)) if not dry_run: mkpath(dst, verbose=verbose) -- cgit v1.2.1 From 15ea2f254869b3304ba5e840708654fb0a937edb Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 6 Sep 2014 17:24:12 -0400 Subject: remove various dead version checks (closes #22349) Patch from Thomas Kluyver. --- command/build_ext.py | 17 ++++------------- sysconfig.py | 24 +++--------------------- tests/test_build_ext.py | 20 +++++++++----------- 3 files changed, 16 insertions(+), 45 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 80689b63..3ab2d04b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -14,13 +14,7 @@ from distutils.extension import Extension from distutils.util import get_platform from distutils import log -# this keeps compatibility from 2.3 to 2.5 -if sys.version < "2.6": - USER_BASE = None - HAS_USER_SITE = False -else: - from site import USER_BASE - HAS_USER_SITE = True +from site import USER_BASE if os.name == 'nt': from distutils.msvccompiler import get_build_version @@ -97,14 +91,11 @@ class build_ext(Command): "list of SWIG command line options"), ('swig=', None, "path to the SWIG executable"), + ('user', None, + "add user include, library and rpath") ] - boolean_options = ['inplace', 'debug', 'force', 'swig-cpp'] - - if HAS_USER_SITE: - user_options.append(('user', None, - "add user include, library and rpath")) - boolean_options.append('user') + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] help_options = [ ('help-compiler', None, diff --git a/sysconfig.py b/sysconfig.py index 5b94fa23..a1452fe1 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -151,10 +151,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): if standard_lib: return os.path.join(prefix, "Lib") else: - if get_python_version() < "2.2": - return prefix - else: - return os.path.join(prefix, "Lib", "site-packages") + return os.path.join(prefix, "Lib", "site-packages") else: raise DistutilsPlatformError( "I don't know where Python installs its library " @@ -244,12 +241,8 @@ def get_config_h_filename(): inc_dir = _sys_home or project_base else: inc_dir = get_python_inc(plat_specific=1) - if get_python_version() < '2.2': - config_h = 'config.h' - else: - # The name of the config.h file changed in 2.2 - config_h = 'pyconfig.h' - return os.path.join(inc_dir, config_h) + + return os.path.join(inc_dir, 'pyconfig.h') def get_makefile_filename(): @@ -461,17 +454,6 @@ def _init_posix(): if python_build: g['LDSHARED'] = g['BLDSHARED'] - elif get_python_version() < '2.1': - # The following two branches are for 1.5.2 compatibility. - if sys.platform == 'aix4': # what about AIX 3.x ? - # Linker script is in the config directory, not in Modules as the - # Makefile says. - python_lib = get_python_lib(standard_lib=1) - ld_so_aix = os.path.join(python_lib, 'config', 'ld_so_aix') - python_exp = os.path.join(python_lib, 'config', 'python.exp') - - g['LDSHARED'] = "%s %s -bI:%s" % (ld_so_aix, g['CC'], python_exp) - global _config_vars _config_vars = g diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index e9958667..b9f407f4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -31,12 +31,11 @@ class BuildExtTestCase(TempdirManager, self.tmp_dir = self.mkdtemp() self.sys_path = sys.path, sys.path[:] sys.path.append(self.tmp_dir) - if sys.version > "2.6": - import site - self.old_user_base = site.USER_BASE - site.USER_BASE = self.mkdtemp() - from distutils.command import build_ext - build_ext.USER_BASE = site.USER_BASE + import site + self.old_user_base = site.USER_BASE + site.USER_BASE = self.mkdtemp() + from distutils.command import build_ext + build_ext.USER_BASE = site.USER_BASE def test_build_ext(self): global ALREADY_TESTED @@ -84,11 +83,10 @@ class BuildExtTestCase(TempdirManager, support.unload('xx') sys.path = self.sys_path[0] sys.path[:] = self.sys_path[1] - if sys.version > "2.6": - import site - site.USER_BASE = self.old_user_base - from distutils.command import build_ext - build_ext.USER_BASE = self.old_user_base + import site + site.USER_BASE = self.old_user_base + from distutils.command import build_ext + build_ext.USER_BASE = self.old_user_base super(BuildExtTestCase, self).tearDown() def test_solaris_enable_shared(self): -- cgit v1.2.1 From aa5115c381d6b79e8db3a860bf71bf403c6fdb99 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 20 Sep 2014 11:53:12 -0400 Subject: use patch context manager instead of decorator because the decorator 'leaks' metadata onto the function --- tests/test_dir_util.py | 6 +++--- tests/test_file_util.py | 15 +++++++-------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py index d2696b82..d436cf83 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -122,12 +122,12 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase): self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') - @patch('os.listdir', side_effect=OSError()) - def test_copy_tree_exception_in_listdir(self, listdir): + def test_copy_tree_exception_in_listdir(self): """ An exception in listdir should raise a DistutilsFileError """ - with self.assertRaises(errors.DistutilsFileError): + with patch("os.listdir", side_effect=OSError()), \ + self.assertRaises(errors.DistutilsFileError): src = self.tempdirs[-1] dir_util.copy_tree(src, None) diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 270f81eb..d3db5cef 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -61,24 +61,23 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): wanted = ['moving %s -> %s' % (self.source, self.target_dir)] self.assertEqual(self._logs, wanted) - @patch('os.rename', side_effect=OSError('wrong', 1)) - def test_move_file_exception_unpacking_rename(self, _): + def test_move_file_exception_unpacking_rename(self): # see issue 22182 - with self.assertRaises(DistutilsFileError): + with patch("os.rename", side_effect=OSError("wrong", 1)), \ + self.assertRaises(DistutilsFileError): with open(self.source, 'w') as fobj: fobj.write('spam eggs') move_file(self.source, self.target, verbose=0) - @patch('os.rename', side_effect=OSError(errno.EXDEV, 'wrong')) - @patch('os.unlink', side_effect=OSError('wrong', 1)) - def test_move_file_exception_unpacking_unlink(self, rename, unlink): + def test_move_file_exception_unpacking_unlink(self): # see issue 22182 - with self.assertRaises(DistutilsFileError): + with patch("os.rename", side_effect=OSError(errno.EXDEV, "wrong")), \ + patch("os.unlink", side_effect=OSError("wrong", 1)), \ + self.assertRaises(DistutilsFileError): with open(self.source, 'w') as fobj: fobj.write('spam eggs') move_file(self.source, self.target, verbose=0) - def test_suite(): return unittest.makeSuite(FileUtilTestCase) -- cgit v1.2.1 From 17fc42a928be89366e183179353ae2a69ce48d93 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 21 Sep 2014 00:09:56 +0100 Subject: Bump version number for 3.4.2rc1 release. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 9463a35c..750a5033 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.1" +__version__ = "3.4.2rc1" #--end constants-- -- cgit v1.2.1 From 933a2d7cf491ccade77d1382ecba1eae7a510874 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 26 Sep 2014 23:31:59 +0200 Subject: Issue #5309: distutils' build and build_ext commands now accept a ``-j`` option to enable parallel building of extension modules. --- command/build.py | 9 ++++++++ command/build_ext.py | 57 +++++++++++++++++++++++++++++++++++++++++++------ tests/test_build_ext.py | 56 ++++++++++++++++++++++++++++++------------------ 3 files changed, 94 insertions(+), 28 deletions(-) diff --git a/command/build.py b/command/build.py index cfc15cf0..337dd0bf 100644 --- a/command/build.py +++ b/command/build.py @@ -36,6 +36,8 @@ class build(Command): "(default: %s)" % get_platform()), ('compiler=', 'c', "specify the compiler type"), + ('parallel=', 'j', + "number of parallel build jobs"), ('debug', 'g', "compile extensions and libraries with debugging information"), ('force', 'f', @@ -65,6 +67,7 @@ class build(Command): self.debug = None self.force = 0 self.executable = None + self.parallel = None def finalize_options(self): if self.plat_name is None: @@ -116,6 +119,12 @@ class build(Command): if self.executable is None: self.executable = os.path.normpath(sys.executable) + if isinstance(self.parallel, str): + try: + self.parallel = int(self.parallel) + except ValueError: + raise DistutilsOptionError("parallel should be an integer") + def run(self): # Run all relevant sub-commands. This will be some subset of: # - build_py - pure Python modules diff --git a/command/build_ext.py b/command/build_ext.py index 3ab2d04b..08449e1a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,7 +4,10 @@ Implements the Distutils 'build_ext' command, for building extension modules (currently limited to C extensions, should accommodate C++ extensions ASAP).""" -import sys, os, re +import contextlib +import os +import re +import sys from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version @@ -85,6 +88,8 @@ class build_ext(Command): "forcibly build everything (ignore file timestamps)"), ('compiler=', 'c', "specify the compiler type"), + ('parallel=', 'j', + "number of parallel build jobs"), ('swig-cpp', None, "make SWIG create C++ files (default is C)"), ('swig-opts=', None, @@ -124,6 +129,7 @@ class build_ext(Command): self.swig_cpp = None self.swig_opts = None self.user = None + self.parallel = None def finalize_options(self): from distutils import sysconfig @@ -134,6 +140,7 @@ class build_ext(Command): ('compiler', 'compiler'), ('debug', 'debug'), ('force', 'force'), + ('parallel', 'parallel'), ('plat_name', 'plat_name'), ) @@ -274,6 +281,12 @@ class build_ext(Command): self.library_dirs.append(user_lib) self.rpath.append(user_lib) + if isinstance(self.parallel, str): + try: + self.parallel = int(self.parallel) + except ValueError: + raise DistutilsOptionError("parallel should be an integer") + def run(self): from distutils.ccompiler import new_compiler @@ -442,15 +455,45 @@ class build_ext(Command): def build_extensions(self): # First, sanity-check the 'extensions' list self.check_extensions_list(self.extensions) + if self.parallel: + self._build_extensions_parallel() + else: + self._build_extensions_serial() + + def _build_extensions_parallel(self): + workers = self.parallel + if self.parallel is True: + workers = os.cpu_count() # may return None + try: + from concurrent.futures import ThreadPoolExecutor + except ImportError: + workers = None + + if workers is None: + self._build_extensions_serial() + return + with ThreadPoolExecutor(max_workers=workers) as executor: + futures = [executor.submit(self.build_extension, ext) + for ext in self.extensions] + for ext, fut in zip(self.extensions, futures): + with self._filter_build_errors(ext): + fut.result() + + def _build_extensions_serial(self): for ext in self.extensions: - try: + with self._filter_build_errors(ext): self.build_extension(ext) - except (CCompilerError, DistutilsError, CompileError) as e: - if not ext.optional: - raise - self.warn('building extension "%s" failed: %s' % - (ext.name, e)) + + @contextlib.contextmanager + def _filter_build_errors(self, ext): + try: + yield + except (CCompilerError, DistutilsError, CompileError) as e: + if not ext.optional: + raise + self.warn('building extension "%s" failed: %s' % + (ext.name, e)) def build_extension(self, ext): sources = ext.sources diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index b9f407f4..366ffbec 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -37,6 +37,9 @@ class BuildExtTestCase(TempdirManager, from distutils.command import build_ext build_ext.USER_BASE = site.USER_BASE + def build_ext(self, *args, **kwargs): + return build_ext(*args, **kwargs) + def test_build_ext(self): global ALREADY_TESTED copy_xxmodule_c(self.tmp_dir) @@ -44,7 +47,7 @@ class BuildExtTestCase(TempdirManager, xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir - cmd = build_ext(dist) + cmd = self.build_ext(dist) fixup_build_ext(cmd) cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir @@ -91,7 +94,7 @@ class BuildExtTestCase(TempdirManager, def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) - cmd = build_ext(dist) + cmd = self.build_ext(dist) old = sys.platform sys.platform = 'sunos' # fooling finalize_options @@ -113,7 +116,7 @@ class BuildExtTestCase(TempdirManager, def test_user_site(self): import site dist = Distribution({'name': 'xx'}) - cmd = build_ext(dist) + cmd = self.build_ext(dist) # making sure the user option is there options = [name for name, short, lable in @@ -144,14 +147,14 @@ class BuildExtTestCase(TempdirManager, # with the optional argument. modules = [Extension('foo', ['xxx'], optional=False)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.ensure_finalized() self.assertRaises((UnknownFileError, CompileError), cmd.run) # should raise an error modules = [Extension('foo', ['xxx'], optional=True)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.ensure_finalized() cmd.run() # should pass @@ -160,7 +163,7 @@ class BuildExtTestCase(TempdirManager, # etc.) are in the include search path. modules = [Extension('foo', ['xxx'], optional=False)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.finalize_options() from distutils import sysconfig @@ -172,14 +175,14 @@ class BuildExtTestCase(TempdirManager, # make sure cmd.libraries is turned into a list # if it's a string - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.libraries = 'my_lib, other_lib lastlib' cmd.finalize_options() self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib']) # make sure cmd.library_dirs is turned into a list # if it's a string - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep cmd.finalize_options() self.assertIn('my_lib_dir', cmd.library_dirs) @@ -187,7 +190,7 @@ class BuildExtTestCase(TempdirManager, # make sure rpath is turned into a list # if it's a string - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.rpath = 'one%stwo' % os.pathsep cmd.finalize_options() self.assertEqual(cmd.rpath, ['one', 'two']) @@ -196,32 +199,32 @@ class BuildExtTestCase(TempdirManager, # make sure define is turned into 2-tuples # strings if they are ','-separated strings - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.define = 'one,two' cmd.finalize_options() self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) # make sure undef is turned into a list of # strings if they are ','-separated strings - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.undef = 'one,two' cmd.finalize_options() self.assertEqual(cmd.undef, ['one', 'two']) # make sure swig_opts is turned into a list - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.swig_opts = None cmd.finalize_options() self.assertEqual(cmd.swig_opts, []) - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.swig_opts = '1 2' cmd.finalize_options() self.assertEqual(cmd.swig_opts, ['1', '2']) def test_check_extensions_list(self): dist = Distribution() - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.finalize_options() #'extensions' option must be a list of Extension instances @@ -270,7 +273,7 @@ class BuildExtTestCase(TempdirManager, def test_get_source_files(self): modules = [Extension('foo', ['xxx'], optional=False)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.ensure_finalized() self.assertEqual(cmd.get_source_files(), ['xxx']) @@ -279,7 +282,7 @@ class BuildExtTestCase(TempdirManager, # should not be overriden by a compiler instance # when the command is run dist = Distribution() - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.compiler = 'unix' cmd.ensure_finalized() cmd.run() @@ -292,7 +295,7 @@ class BuildExtTestCase(TempdirManager, ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) - cmd = build_ext(dist) + cmd = self.build_ext(dist) fixup_build_ext(cmd) cmd.ensure_finalized() self.assertEqual(len(cmd.get_outputs()), 1) @@ -355,7 +358,7 @@ class BuildExtTestCase(TempdirManager, #etree_ext = Extension('lxml.etree', [etree_c]) #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) dist = Distribution() - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.inplace = 1 cmd.distribution.package_dir = {'': 'src'} cmd.distribution.packages = ['lxml', 'lxml.html'] @@ -462,7 +465,7 @@ class BuildExtTestCase(TempdirManager, 'ext_modules': [deptarget_ext] }) dist.package_dir = self.tmp_dir - cmd = build_ext(dist) + cmd = self.build_ext(dist) cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir @@ -481,8 +484,19 @@ class BuildExtTestCase(TempdirManager, self.fail("Wrong deployment target during compilation") +class ParallelBuildExtTestCase(BuildExtTestCase): + + def build_ext(self, *args, **kwargs): + build_ext = super().build_ext(*args, **kwargs) + build_ext.parallel = True + return build_ext + + def test_suite(): - return unittest.makeSuite(BuildExtTestCase) + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(BuildExtTestCase)) + suite.addTest(unittest.makeSuite(ParallelBuildExtTestCase)) + return suite if __name__ == '__main__': - support.run_unittest(test_suite()) + support.run_unittest(__name__) -- cgit v1.2.1 From 04d00c2dbfc4a8facae5d91617f3a30de5474312 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Sat, 27 Sep 2014 16:56:15 -0400 Subject: #10510: make distuitls upload/register use HTML standards compliant CRLF. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch by Ian Cordasco, approved by Éric Araujo. --- command/upload.py | 10 +++++----- tests/test_upload.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/command/upload.py b/command/upload.py index 180be7c7..9b15b67b 100644 --- a/command/upload.py +++ b/command/upload.py @@ -143,11 +143,11 @@ class upload(PyPIRCCommand): # Build up the MIME payload for the POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = b'\n--' + boundary.encode('ascii') - end_boundary = sep_boundary + b'--' + sep_boundary = b'\r\n--' + boundary.encode('ascii') + end_boundary = sep_boundary + b'--\r\n' body = io.BytesIO() for key, value in data.items(): - title = '\nContent-Disposition: form-data; name="%s"' % key + title = '\r\nContent-Disposition: form-data; name="%s"' % key # handle multiple entries for the same name if type(value) != type([]): value = [value] @@ -159,12 +159,12 @@ class upload(PyPIRCCommand): value = str(value).encode('utf-8') body.write(sep_boundary) body.write(title.encode('utf-8')) - body.write(b"\n\n") + body.write(b"\r\n\r\n") body.write(value) if value and value[-1:] == b'\r': body.write(b'\n') # write an extra newline (lurve Macs) body.write(end_boundary) - body.write(b"\n") + body.write(b"\r\n") body = body.getvalue() self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) diff --git a/tests/test_upload.py b/tests/test_upload.py index 0380f979..24015416 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -127,7 +127,7 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.last_open.req.headers) - self.assertEqual(headers['Content-length'], '2087') + self.assertEqual(headers['Content-length'], '2163') content_type = headers['Content-type'] self.assertTrue(content_type.startswith('multipart/form-data')) self.assertEqual(self.last_open.req.get_method(), 'POST') -- cgit v1.2.1 From 731789f97ab96b9d91f5485c801078c1a9e66972 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Sun, 28 Sep 2014 11:01:11 -0400 Subject: #10510: Fix bug in forward port of 2.7 distutils patch. Pointed out by Arfrever. --- command/upload.py | 1 - tests/test_upload.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/command/upload.py b/command/upload.py index 9b15b67b..1a96e222 100644 --- a/command/upload.py +++ b/command/upload.py @@ -164,7 +164,6 @@ class upload(PyPIRCCommand): if value and value[-1:] == b'\r': body.write(b'\n') # write an extra newline (lurve Macs) body.write(end_boundary) - body.write(b"\r\n") body = body.getvalue() self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO) diff --git a/tests/test_upload.py b/tests/test_upload.py index 24015416..dccaf77e 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -127,7 +127,7 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.last_open.req.headers) - self.assertEqual(headers['Content-length'], '2163') + self.assertEqual(headers['Content-length'], '2161') content_type = headers['Content-type'] self.assertTrue(content_type.startswith('multipart/form-data')) self.assertEqual(self.last_open.req.get_method(), 'POST') -- cgit v1.2.1 From 59423d00f1ab2ba30212906ea691db28297cc47f Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Tue, 30 Sep 2014 14:58:22 +0200 Subject: Remove pointless "vile hack" that can cause the build step to fail when some extension modules can't be imported. See issue #5309 for the build failures, issue #458343 for the original motivation. --- command/build_ext.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 08449e1a..2ffab181 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -545,15 +545,8 @@ class build_ext(Command): extra_postargs=extra_args, depends=ext.depends) - # XXX -- this is a Vile HACK! - # - # The setup.py script for Python on Unix needs to be able to - # get this list so it can perform all the clean up needed to - # avoid keeping object files around when cleaning out a failed - # build of an extension module. Since Distutils does not - # track dependencies, we have to get rid of intermediates to - # ensure all the intermediates will be properly re-built. - # + # XXX outdated variable, kept here in case third-part code + # needs it. self._built_objects = objects[:] # Now link the object files together into a "shared object" -- -- cgit v1.2.1 From 198a91ac760d28bc16e919fd5068f6e607d56d12 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Tue, 30 Sep 2014 20:53:21 -0400 Subject: #22512: move distutils rpm test's .rpmdb to testing tmpdir. Patch by Francis MB. --- tests/test_bdist_rpm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index bcbb5633..25c14abd 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -24,6 +24,7 @@ setup(name='foo', version='0.1', py_modules=['foo'], """ class BuildRpmTestCase(support.TempdirManager, + support.EnvironGuard, support.LoggingSilencer, unittest.TestCase): @@ -54,6 +55,7 @@ class BuildRpmTestCase(support.TempdirManager, def test_quiet(self): # let's create a package tmp_dir = self.mkdtemp() + os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation pkg_dir = os.path.join(tmp_dir, 'foo') os.mkdir(pkg_dir) self.write_file((pkg_dir, 'setup.py'), SETUP_PY) @@ -96,6 +98,7 @@ class BuildRpmTestCase(support.TempdirManager, def test_no_optimize_flag(self): # let's create a package that brakes bdist_rpm tmp_dir = self.mkdtemp() + os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation pkg_dir = os.path.join(tmp_dir, 'foo') os.mkdir(pkg_dir) self.write_file((pkg_dir, 'setup.py'), SETUP_PY) -- cgit v1.2.1 From 59b6796e92361bd1cc79deae7aba71ec12e32554 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Thu, 2 Oct 2014 02:10:47 +0200 Subject: - Issue #17219: Add library build dir for Python extension cross-builds. --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 3ab2d04b..acbe6480 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -237,7 +237,7 @@ class build_ext(Command): # Python's library directory must be appended to library_dirs # See Issues: #1600860, #4366 if (sysconfig.get_config_var('Py_ENABLE_SHARED')): - if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): + if not sysconfig.python_build: # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: -- cgit v1.2.1 From 0d41723615b404279c772ce9535705f4f4e830cb Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Oct 2014 14:15:42 +0200 Subject: Bump to 3.2.6rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f9016d6d..382e4b1c 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.5" +__version__ = "3.2.6rc1" #--end constants-- -- cgit v1.2.1 From 3859f945627998b2c426bf0e26bc86d54fc94731 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Oct 2014 14:22:11 +0200 Subject: Bump to 3.3.6rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2e8719f8..c58e98db 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.5" +__version__ = "3.3.6rc1" #--end constants-- -- cgit v1.2.1 From 1fc386404ed75da5f5be0cd0ca0adeff8ff6dd72 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 5 Oct 2014 19:05:50 -0700 Subject: Release bump for 3.4.2 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 750a5033..dfe62ffa 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.2rc1" +__version__ = "3.4.2" #--end constants-- -- cgit v1.2.1 From 49b4b07a6a16dff95918ab15344d3c8976cdafb2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 Oct 2014 08:50:38 +0200 Subject: Bump to 3.2.6 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 382e4b1c..6eb79cb6 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.6rc1" +__version__ = "3.2.6" #--end constants-- -- cgit v1.2.1 From 49dde81581fd34569b50071587daa71eaf9e4546 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 Oct 2014 09:03:40 +0200 Subject: Bump to 3.3.6 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c58e98db..057e90b8 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.6rc1" +__version__ = "3.3.6" #--end constants-- -- cgit v1.2.1 From 909f8ed55e9b027b28fe9de50682d4a21d99743d Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 30 Oct 2014 19:37:07 +0100 Subject: Issue #8876: distutils now falls back to copying files when hard linking doesn't work. This allows use with special filesystems such as VirtualBox shared folders. --- file_util.py | 34 +++++++++++++++++++++------------- tests/test_file_util.py | 32 +++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/file_util.py b/file_util.py index 7b14efbc..b3fee35a 100644 --- a/file_util.py +++ b/file_util.py @@ -80,7 +80,8 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, (os.symlink) instead of copying: set it to "hard" or "sym"; if it is None (the default), files are copied. Don't set 'link' on systems that don't support it: 'copy_file()' doesn't check if hard or symbolic - linking is available. + linking is available. If hardlink fails, falls back to + _copy_file_contents(). Under Mac OS, uses the native file copy function in macostools; on other systems, uses '_copy_file_contents()' to copy file contents. @@ -132,24 +133,31 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, # (Unix only, of course, but that's the caller's responsibility) elif link == 'hard': if not (os.path.exists(dst) and os.path.samefile(src, dst)): - os.link(src, dst) + try: + os.link(src, dst) + return (dst, 1) + except OSError: + # If hard linking fails, fall back on copying file + # (some special filesystems don't support hard linking + # even under Unix, see issue #8876). + pass elif link == 'sym': if not (os.path.exists(dst) and os.path.samefile(src, dst)): os.symlink(src, dst) + return (dst, 1) # Otherwise (non-Mac, not linking), copy the file contents and # (optionally) copy the times and mode. - else: - _copy_file_contents(src, dst) - if preserve_mode or preserve_times: - st = os.stat(src) - - # According to David Ascher , utime() should be done - # before chmod() (at least under NT). - if preserve_times: - os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) - if preserve_mode: - os.chmod(dst, S_IMODE(st[ST_MODE])) + _copy_file_contents(src, dst) + if preserve_mode or preserve_times: + st = os.stat(src) + + # According to David Ascher , utime() should be done + # before chmod() (at least under NT). + if preserve_times: + os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) + if preserve_mode: + os.chmod(dst, S_IMODE(st[ST_MODE])) return (dst, 1) diff --git a/tests/test_file_util.py b/tests/test_file_util.py index d3db5cef..a6d04f06 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -5,7 +5,7 @@ import shutil import errno from unittest.mock import patch -from distutils.file_util import move_file +from distutils.file_util import move_file, copy_file from distutils import log from distutils.tests import support from distutils.errors import DistutilsFileError @@ -78,6 +78,36 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): fobj.write('spam eggs') move_file(self.source, self.target, verbose=0) + def test_copy_file_hard_link(self): + with open(self.source, 'w') as f: + f.write('some content') + st = os.stat(self.source) + copy_file(self.source, self.target, link='hard') + st2 = os.stat(self.source) + st3 = os.stat(self.target) + self.assertTrue(os.path.samestat(st, st2), (st, st2)) + self.assertTrue(os.path.samestat(st2, st3), (st2, st3)) + with open(self.source, 'r') as f: + self.assertEqual(f.read(), 'some content') + + def test_copy_file_hard_link_failure(self): + # If hard linking fails, copy_file() falls back on copying file + # (some special filesystems don't support hard linking even under + # Unix, see issue #8876). + with open(self.source, 'w') as f: + f.write('some content') + st = os.stat(self.source) + with patch("os.link", side_effect=OSError(0, "linking unsupported")): + copy_file(self.source, self.target, link='hard') + st2 = os.stat(self.source) + st3 = os.stat(self.target) + self.assertTrue(os.path.samestat(st, st2), (st, st2)) + self.assertFalse(os.path.samestat(st2, st3), (st2, st3)) + for fn in (self.source, self.target): + with open(fn, 'r') as f: + self.assertEqual(f.read(), 'some content') + + def test_suite(): return unittest.makeSuite(FileUtilTestCase) -- cgit v1.2.1 From b880aac986be9e5c76411953dcaf56c443f5d584 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 22 Nov 2014 12:54:57 -0800 Subject: Issue #22919: Windows build updated to support VC 14.0 (Visual Studio 2015), which will be used for the official 3.5 release. --- command/build_ext.py | 2 +- command/wininst-14.0-amd64.exe | Bin 0 -> 84480 bytes command/wininst-14.0.exe | Bin 0 -> 75264 bytes msvc9compiler.py | 3 +++ msvccompiler.py | 3 +++ sysconfig.py | 27 +++++++++------------------ 6 files changed, 16 insertions(+), 19 deletions(-) create mode 100644 command/wininst-14.0-amd64.exe create mode 100644 command/wininst-14.0.exe diff --git a/command/build_ext.py b/command/build_ext.py index 54ce1345..605efbd6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -209,7 +209,7 @@ class build_ext(Command): if MSVC_VERSION >= 9: # Use the .lib files for the correct architecture if self.plat_name == 'win32': - suffix = '' + suffix = 'win32' else: # win-amd64 or win-ia64 suffix = self.plat_name[4:] diff --git a/command/wininst-14.0-amd64.exe b/command/wininst-14.0-amd64.exe new file mode 100644 index 00000000..9affe130 Binary files /dev/null and b/command/wininst-14.0-amd64.exe differ diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe new file mode 100644 index 00000000..3ce71b9f Binary files /dev/null and b/command/wininst-14.0.exe differ diff --git a/msvc9compiler.py b/msvc9compiler.py index 9688f200..d1374efe 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -179,6 +179,9 @@ def get_build_version(): i = i + len(prefix) s, rest = sys.version[i:].split(" ", 1) majorVersion = int(s[:-2]) - 6 + if majorVersion >= 13: + # v13 was skipped and should be v14 + majorVersion += 1 minorVersion = int(s[2:3]) / 10.0 # I don't think paths are affected by minor version in version 6 if majorVersion == 6: diff --git a/msvccompiler.py b/msvccompiler.py index 81166569..1048cd41 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -157,6 +157,9 @@ def get_build_version(): i = i + len(prefix) s, rest = sys.version[i:].split(" ", 1) majorVersion = int(s[:-2]) - 6 + if majorVersion >= 13: + # v13 was skipped and should be v14 + majorVersion += 1 minorVersion = int(s[2:3]) / 10.0 # I don't think paths are affected by minor version in version 6 if majorVersion == 6: diff --git a/sysconfig.py b/sysconfig.py index a1452fe1..573724dd 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -9,6 +9,7 @@ Written by: Fred L. Drake, Jr. Email: """ +import _imp import os import re import sys @@ -22,23 +23,15 @@ BASE_PREFIX = os.path.normpath(sys.base_prefix) BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) # Path to the base directory of the project. On Windows the binary may -# live in project/PCBuild9. If we're dealing with an x64 Windows build, -# it'll live in project/PCbuild/amd64. +# live in project/PCBuild/win32 or project/PCBuild/amd64. # set for cross builds if "_PYTHON_PROJECT_BASE" in os.environ: project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) else: project_base = os.path.dirname(os.path.abspath(sys.executable)) -if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): - project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) -# PC/VS7.1 -if os.name == "nt" and "\\pc\\v" in project_base[-10:].lower(): - project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, - os.path.pardir)) -# PC/AMD64 -if os.name == "nt" and "\\pcbuild\\amd64" in project_base[-14:].lower(): - project_base = os.path.abspath(os.path.join(project_base, os.path.pardir, - os.path.pardir)) +if (os.name == 'nt' and + project_base.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))): + project_base = os.path.dirname(os.path.dirname(project_base)) # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use @@ -51,11 +44,9 @@ def _is_python_source_dir(d): return True return False _sys_home = getattr(sys, '_home', None) -if _sys_home and os.name == 'nt' and \ - _sys_home.lower().endswith(('pcbuild', 'pcbuild\\amd64')): - _sys_home = os.path.dirname(_sys_home) - if _sys_home.endswith('pcbuild'): # must be amd64 - _sys_home = os.path.dirname(_sys_home) +if (_sys_home and os.name == 'nt' and + _sys_home.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))): + _sys_home = os.path.dirname(os.path.dirname(_sys_home)) def _python_build(): if _sys_home: return _is_python_source_dir(_sys_home) @@ -468,7 +459,7 @@ def _init_nt(): # XXX hmmm.. a normal install puts include files here g['INCLUDEPY'] = get_python_inc(plat_specific=0) - g['EXT_SUFFIX'] = '.pyd' + g['EXT_SUFFIX'] = _imp.extension_suffixes()[0] g['EXE'] = ".exe" g['VERSION'] = get_python_version().replace(".", "") g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) -- cgit v1.2.1 From e25c444f65824e882fbb6493578ee1db836721bd Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 15 Dec 2014 15:03:44 -0800 Subject: Removes bdist_wininst dependency on MFC. --- command/wininst-14.0-amd64.exe | Bin 84480 -> 84480 bytes command/wininst-14.0.exe | Bin 75264 -> 75264 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-14.0-amd64.exe b/command/wininst-14.0-amd64.exe index 9affe130..43b85b6d 100644 Binary files a/command/wininst-14.0-amd64.exe and b/command/wininst-14.0-amd64.exe differ diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe index 3ce71b9f..764524d7 100644 Binary files a/command/wininst-14.0.exe and b/command/wininst-14.0.exe differ -- cgit v1.2.1 From bebd47ea3228d5f71b06cf467410fac0eac2d3ee Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 15 Dec 2014 20:45:23 -0800 Subject: Fixes distutils adding/expecting too many _d suffixes. --- command/build_ext.py | 3 --- tests/test_install.py | 2 -- 2 files changed, 5 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 605efbd6..c5a3ce19 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -691,10 +691,7 @@ class build_ext(Command): """ from distutils.sysconfig import get_config_var ext_path = ext_name.split('.') - # extensions in debug_mode are named 'module_d.pyd' under windows ext_suffix = get_config_var('EXT_SUFFIX') - if os.name == 'nt' and self.debug: - return os.path.join(*ext_path) + '_d' + ext_suffix return os.path.join(*ext_path) + ext_suffix def get_export_symbols(self, ext): diff --git a/tests/test_install.py b/tests/test_install.py index 18e1e575..9313330e 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -20,8 +20,6 @@ from distutils.tests import support def _make_ext_name(modname): - if os.name == 'nt' and sys.executable.endswith('_d.exe'): - modname += '_d' return modname + sysconfig.get_config_var('EXT_SUFFIX') -- cgit v1.2.1 From 3fcf597f4db0a82fabde4d06bc287b6573763496 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 13 Jan 2015 09:17:24 -0500 Subject: fix instances of consecutive articles (closes #23221) Patch by Karan Goel. --- dir_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dir_util.py b/dir_util.py index 0924c9b0..d5cd8e3e 100644 --- a/dir_util.py +++ b/dir_util.py @@ -81,7 +81,7 @@ def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0): """Create all the empty directories under 'base_dir' needed to put 'files' there. - 'base_dir' is just the a name of a directory which doesn't necessarily + 'base_dir' is just the name of a directory which doesn't necessarily exist yet; 'files' is a list of filenames to be interpreted relative to 'base_dir'. 'base_dir' + the directory portion of every file in 'files' will be created if it doesn't already exist. 'mode', 'verbose' and -- cgit v1.2.1 From 3789d73e316092bc2b65960f3fa8706bc0afa3c3 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 14 Jan 2015 23:56:35 -0500 Subject: fix parsing reST with code or code-block directives (closes #23063) Patch by Marc Abramowitz. --- command/check.py | 8 ++++---- tests/test_check.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/command/check.py b/command/check.py index 22b9349d..7ebe707c 100644 --- a/command/check.py +++ b/command/check.py @@ -122,7 +122,7 @@ class check(Command): """Returns warnings when the provided data doesn't compile.""" source_path = StringIO() parser = Parser() - settings = frontend.OptionParser().get_default_values() + settings = frontend.OptionParser(components=(Parser,)).get_default_values() settings.tab_width = 4 settings.pep_references = None settings.rfc_references = None @@ -138,8 +138,8 @@ class check(Command): document.note_source(source_path, -1) try: parser.parse(data, document) - except AttributeError: - reporter.messages.append((-1, 'Could not finish the parsing.', - '', {})) + except AttributeError as e: + reporter.messages.append( + (-1, 'Could not finish the parsing: %s.' % e, '', {})) return reporter.messages diff --git a/tests/test_check.py b/tests/test_check.py index 601b6868..959fa908 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -1,4 +1,5 @@ """Tests for distutils.command.check.""" +import textwrap import unittest from test.support import run_unittest @@ -92,6 +93,36 @@ class CheckTestCase(support.LoggingSilencer, cmd = self._run(metadata, strict=1, restructuredtext=1) self.assertEqual(cmd._warnings, 0) + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") + def test_check_restructuredtext_with_syntax_highlight(self): + # Don't fail if there is a `code` or `code-block` directive + + example_rst_docs = [] + example_rst_docs.append(textwrap.dedent("""\ + Here's some code: + + .. code:: python + + def foo(): + pass + """)) + example_rst_docs.append(textwrap.dedent("""\ + Here's some code: + + .. code-block:: python + + def foo(): + pass + """)) + + for rest_with_code in example_rst_docs: + pkg_info, dist = self.create_dist(long_description=rest_with_code) + cmd = check(dist) + cmd.check_restructuredtext() + self.assertEqual(cmd._warnings, 0) + msgs = cmd._check_rst_data(rest_with_code) + self.assertEqual(len(msgs), 0) + def test_check_all(self): metadata = {'url': 'xxx', 'author': 'xxx'} -- cgit v1.2.1 From 78d6f0dc55852b9489a17c1e25e8215aef1c7df9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 31 Jan 2015 12:05:05 +0200 Subject: Issue #23326: Removed __ne__ implementations. Since fixing default __ne__ implementation in issue #21408 they are redundant. --- version.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/version.py b/version.py index ebcab84e..af14cc13 100644 --- a/version.py +++ b/version.py @@ -48,12 +48,6 @@ class Version: return c return c == 0 - def __ne__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c != 0 - def __lt__(self, other): c = self._cmp(other) if c is NotImplemented: -- cgit v1.2.1 From 3ccb5a4fba4eccdb22e325c82593b8e4e91a0354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Charles-Fran=C3=A7ois=20Natali?= Date: Sat, 7 Feb 2015 13:27:50 +0000 Subject: Issue #23285: PEP 475 -- Retry system calls failing with EINTR. --- spawn.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/spawn.py b/spawn.py index 22e87e8f..5dd415a2 100644 --- a/spawn.py +++ b/spawn.py @@ -137,9 +137,6 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): try: pid, status = os.waitpid(pid, 0) except OSError as exc: - import errno - if exc.errno == errno.EINTR: - continue if not DEBUG: cmd = executable raise DistutilsExecError( -- cgit v1.2.1 From c14a53783773ecd520e998f72a2fd8fe4d58d8ca Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 7 Feb 2015 16:00:45 -0800 Subject: Version bump for 3.4.3rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index dfe62ffa..e8782819 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.2" +__version__ = "3.4.3rc1" #--end constants-- -- cgit v1.2.1 From 5bf2ae1154e0722c9de295d4ec9f9721b629af73 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 7 Feb 2015 16:00:55 -0800 Subject: Release bump for 3.5.0a1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 328bea6f..67ec78b2 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a0" +__version__ = "3.5.0a1" #--end constants-- -- cgit v1.2.1 From 2f7d730baf32b841c1b99d58b8efe3f8ca0423b8 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 14 Feb 2015 09:50:59 -0800 Subject: Closes #23437: Make user scripts directory versioned on Windows (patch by pmoore) --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index d768dc54..67db007a 100644 --- a/command/install.py +++ b/command/install.py @@ -51,7 +51,7 @@ if HAS_USER_SITE: 'purelib': '$usersite', 'platlib': '$usersite', 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', - 'scripts': '$userbase/Scripts', + 'scripts': '$userbase/Python$py_version_nodot/Scripts', 'data' : '$userbase', } -- cgit v1.2.1 From 1f83754d9d44682de0958692d91f1ac4c9bdfec3 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 22 Feb 2015 23:55:39 -0800 Subject: Release bump for 3.4.3 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e8782819..00a58597 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.3rc1" +__version__ = "3.4.3" #--end constants-- -- cgit v1.2.1 From 4b8f2273ff4fe2d8343d79d0ecb81db7a9ad323c Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 8 Mar 2015 00:24:34 -0800 Subject: Release bump for 3.5.0a2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 67ec78b2..451c5fbe 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a1" +__version__ = "3.5.0a2" #--end constants-- -- cgit v1.2.1 From 95fd4bbb5ba5ce152673dcc1b03b370cea0fab89 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 29 Mar 2015 15:34:26 -0700 Subject: Release bump for Python 3.5.0a3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 451c5fbe..6b428709 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a2" +__version__ = "3.5.0a3" #--end constants-- -- cgit v1.2.1 From 7fa0438c7f004f96a81f810fa44e9daddad233f2 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 10 Apr 2015 13:24:41 +0300 Subject: Issue #23865: close() methods in multiple modules now are idempotent and more robust at shutdown. If needs to release multiple resources, they are released even if errors are occured. --- text_file.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text_file.py b/text_file.py index 40b8484a..478336f0 100644 --- a/text_file.py +++ b/text_file.py @@ -118,10 +118,11 @@ class TextFile: def close(self): """Close the current file and forget everything we know about it (filename, current line number).""" - self.file.close() + file = self.file self.file = None self.filename = None self.current_line = None + file.close() def gen_error(self, msg, line=None): outmsg = [] -- cgit v1.2.1 From 6b108b8442eba8295a12655281e1b32b85a94a7b Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 13 Apr 2015 14:21:02 -0400 Subject: Issue #23731: Implement PEP 488. The concept of .pyo files no longer exists. Now .pyc files have an optional `opt-` tag which specifies if any extra optimizations beyond the peepholer were applied. --- command/build_py.py | 4 ++-- command/install_lib.py | 16 ++++++++-------- tests/test_build_py.py | 4 ++-- tests/test_install_lib.py | 13 ++++++------- util.py | 9 +++++---- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index 9100b96a..cf0ca57c 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -314,10 +314,10 @@ class build_py (Command): if include_bytecode: if self.compile: outputs.append(importlib.util.cache_from_source( - filename, debug_override=True)) + filename, optimization='')) if self.optimize > 0: outputs.append(importlib.util.cache_from_source( - filename, debug_override=False)) + filename, optimization=self.optimize)) outputs += [ os.path.join(build_dir, filename) diff --git a/command/install_lib.py b/command/install_lib.py index 215813ba..6154cf09 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -22,15 +22,15 @@ class install_lib(Command): # possible scenarios: # 1) no compilation at all (--no-compile --no-optimize) # 2) compile .pyc only (--compile --no-optimize; default) - # 3) compile .pyc and "level 1" .pyo (--compile --optimize) - # 4) compile "level 1" .pyo only (--no-compile --optimize) - # 5) compile .pyc and "level 2" .pyo (--compile --optimize-more) - # 6) compile "level 2" .pyo only (--no-compile --optimize-more) + # 3) compile .pyc and "opt-1" .pyc (--compile --optimize) + # 4) compile "opt-1" .pyc only (--no-compile --optimize) + # 5) compile .pyc and "opt-2" .pyc (--compile --optimize-more) + # 6) compile "opt-2" .pyc only (--no-compile --optimize-more) # - # The UI for this is two option, 'compile' and 'optimize'. + # The UI for this is two options, 'compile' and 'optimize'. # 'compile' is strictly boolean, and only decides whether to # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and - # decides both whether to generate .pyo files and what level of + # decides both whether to generate .pyc files and what level of # optimization to use. user_options = [ @@ -166,10 +166,10 @@ class install_lib(Command): continue if self.compile: bytecode_files.append(importlib.util.cache_from_source( - py_file, debug_override=True)) + py_file, optimization='')) if self.optimize > 0: bytecode_files.append(importlib.util.cache_from_source( - py_file, debug_override=False)) + py_file, optimization=self.optimize)) return bytecode_files diff --git a/tests/test_build_py.py b/tests/test_build_py.py index c8f6b899..18283dc7 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -120,8 +120,8 @@ class BuildPyTestCase(support.TempdirManager, found = os.listdir(cmd.build_lib) self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) - self.assertEqual(sorted(found), - ['boiledeggs.%s.pyo' % sys.implementation.cache_tag]) + expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag) + self.assertEqual(sorted(found), [expect]) def test_dir_in_package_data(self): """ diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 40dd1a95..5378aa82 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -44,12 +44,11 @@ class InstallLibTestCase(support.TempdirManager, f = os.path.join(project_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) - pyc_file = importlib.util.cache_from_source('foo.py', - debug_override=True) - pyo_file = importlib.util.cache_from_source('foo.py', - debug_override=False) + pyc_file = importlib.util.cache_from_source('foo.py', optimization='') + pyc_opt_file = importlib.util.cache_from_source('foo.py', + optimization=cmd.optimize) self.assertTrue(os.path.exists(pyc_file)) - self.assertTrue(os.path.exists(pyo_file)) + self.assertTrue(os.path.exists(pyc_opt_file)) def test_get_outputs(self): project_dir, dist = self.create_dist() @@ -66,8 +65,8 @@ class InstallLibTestCase(support.TempdirManager, cmd.distribution.packages = ['spam'] cmd.distribution.script_name = 'setup.py' - # get_outputs should return 4 elements: spam/__init__.py, .pyc and - # .pyo, foo.import-tag-abiflags.so / foo.pyd + # get_outputs should return 4 elements: spam/__init__.py and .pyc, + # foo.import-tag-abiflags.so / foo.pyd outputs = cmd.get_outputs() self.assertEqual(len(outputs), 4, outputs) diff --git a/util.py b/util.py index 5adcac5a..e423325d 100644 --- a/util.py +++ b/util.py @@ -322,11 +322,11 @@ def byte_compile (py_files, prefix=None, base_dir=None, verbose=1, dry_run=0, direct=None): - """Byte-compile a collection of Python source files to either .pyc - or .pyo files in a __pycache__ subdirectory. 'py_files' is a list + """Byte-compile a collection of Python source files to .pyc + files in a __pycache__ subdirectory. 'py_files' is a list of files to compile; any files that don't end in ".py" are silently skipped. 'optimize' must be one of the following: - 0 - don't optimize (generate .pyc) + 0 - don't optimize 1 - normal optimization (like "python -O") 2 - extra optimization (like "python -OO") If 'force' is true, all files are recompiled regardless of @@ -438,8 +438,9 @@ byte_compile(files, optimize=%r, force=%r, # cfile - byte-compiled file # dfile - purported source filename (same as 'file' by default) if optimize >= 0: + opt = '' if optimize == 0 else optimize cfile = importlib.util.cache_from_source( - file, debug_override=not optimize) + file, optimization=opt) else: cfile = importlib.util.cache_from_source(file) dfile = file -- cgit v1.2.1 From 2ce0b4fac1ee4933717f90da0a0f78784194e681 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 19 Apr 2015 13:51:40 -0700 Subject: Version number bump for Python 3.5.0a4. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6b428709..37bfd5a8 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a3" +__version__ = "3.5.0a4" #--end constants-- -- cgit v1.2.1 From 766d1fbd091c92d68c85eae404aee1d792c815aa Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 16 May 2015 22:13:27 +0300 Subject: Issue #16314: Added support for the LZMA compression in distutils. --- archive_util.py | 21 ++++--- command/bdist.py | 3 +- command/bdist_dumb.py | 3 +- tests/test_archive_util.py | 154 ++++++++++++++++++++++++++++++--------------- tests/test_bdist.py | 2 +- 5 files changed, 121 insertions(+), 62 deletions(-) diff --git a/archive_util.py b/archive_util.py index 4470bb02..bed13849 100644 --- a/archive_util.py +++ b/archive_util.py @@ -57,26 +57,28 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, """Create a (possibly compressed) tar file from all the files under 'base_dir'. - 'compress' must be "gzip" (the default), "compress", "bzip2", or None. - (compress will be deprecated in Python 3.2) + 'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or + None. ("compress" will be deprecated in Python 3.2) 'owner' and 'group' can be used to define an owner and a group for the archive that is being built. If not provided, the current owner and group will be used. The output tar file will be named 'base_dir' + ".tar", possibly plus - the appropriate compression extension (".gz", ".bz2" or ".Z"). + the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z"). Returns the output filename. """ - tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''} - compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'compress': '.Z'} + tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '', + 'compress': ''} + compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', + 'compress': '.Z'} # flags for compression program, each element of list will be an argument if compress is not None and compress not in compress_ext.keys(): raise ValueError( - "bad value for 'compress': must be None, 'gzip', 'bzip2' " - "or 'compress'") + "bad value for 'compress': must be None, 'gzip', 'bzip2', " + "'xz' or 'compress'") archive_name = base_name + '.tar' if compress != 'compress': @@ -177,6 +179,7 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): ARCHIVE_FORMATS = { 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), + 'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"), 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"), 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"), 'zip': (make_zipfile, [],"ZIP file") @@ -197,8 +200,8 @@ def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, """Create an archive file (eg. zip or tar). 'base_name' is the name of the file to create, minus any format-specific - extension; 'format' is the archive format: one of "zip", "tar", "ztar", - or "gztar". + extension; 'format' is the archive format: one of "zip", "tar", "gztar", + "bztar", "xztar", or "ztar". 'root_dir' is a directory that will be the root directory of the archive; ie. we typically chdir into 'root_dir' before creating the diff --git a/command/bdist.py b/command/bdist.py index 6814a1c3..014871d2 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -61,13 +61,14 @@ class bdist(Command): 'nt': 'zip'} # Establish the preferred order (for the --help-formats option). - format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', + format_commands = ['rpm', 'gztar', 'bztar', 'xztar', 'ztar', 'tar', 'wininst', 'zip', 'msi'] # And the real information. format_command = {'rpm': ('bdist_rpm', "RPM distribution"), 'gztar': ('bdist_dumb', "gzip'ed tar file"), 'bztar': ('bdist_dumb', "bzip2'ed tar file"), + 'xztar': ('bdist_dumb', "xz'ed tar file"), 'ztar': ('bdist_dumb', "compressed tar file"), 'tar': ('bdist_dumb', "tar file"), 'wininst': ('bdist_wininst', diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index 4405d12c..f1bfb249 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -22,7 +22,8 @@ class bdist_dumb(Command): "platform name to embed in generated filenames " "(default: %s)" % get_platform()), ('format=', 'f', - "archive format to create (tar, ztar, gztar, zip)"), + "archive format to create (tar, gztar, bztar, xztar, " + "ztar, zip)"), ('keep-temp', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 2d72af4a..81d4c74a 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -13,7 +13,7 @@ from distutils.archive_util import (check_archive_formats, make_tarball, ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support -from test.support import check_warnings, run_unittest, patch +from test.support import check_warnings, run_unittest, patch, change_cwd try: import grp @@ -34,6 +34,16 @@ try: except ImportError: ZLIB_SUPPORT = False +try: + import bz2 +except ImportError: + bz2 = None + +try: + import lzma +except ImportError: + lzma = None + def can_fs_encode(filename): """ Return True if the filename can be saved in the file system. @@ -52,19 +62,36 @@ class ArchiveUtilTestCase(support.TempdirManager, unittest.TestCase): @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_make_tarball(self): - self._make_tarball('archive') + def test_make_tarball(self, name='archive'): + # creating something to tar + tmpdir = self._create_files() + self._make_tarball(tmpdir, name, '.tar.gz') + # trying an uncompressed one + self._make_tarball(tmpdir, name, '.tar', compress=None) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_make_tarball_gzip(self): + tmpdir = self._create_files() + self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip') + + @unittest.skipUnless(bz2, 'Need bz2 support to run') + def test_make_tarball_bzip2(self): + tmpdir = self._create_files() + self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2') + + @unittest.skipUnless(lzma, 'Need lzma support to run') + def test_make_tarball_xz(self): + tmpdir = self._create_files() + self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz') + @unittest.skipUnless(can_fs_encode('Ã¥rchiv'), 'File system cannot handle this filename') def test_make_tarball_latin1(self): """ Mirror test_make_tarball, except filename contains latin characters. """ - self._make_tarball('Ã¥rchiv') # note this isn't a real word + self.test_make_tarball('Ã¥rchiv') # note this isn't a real word - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') @unittest.skipUnless(can_fs_encode('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–'), 'File system cannot handle this filename') def test_make_tarball_extended(self): @@ -72,16 +99,9 @@ class ArchiveUtilTestCase(support.TempdirManager, Mirror test_make_tarball, except filename contains extended characters outside the latin charset. """ - self._make_tarball('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–') # japanese for archive - - def _make_tarball(self, target_name): - # creating something to tar - tmpdir = self.mkdtemp() - self.write_file([tmpdir, 'file1'], 'xxx') - self.write_file([tmpdir, 'file2'], 'xxx') - os.mkdir(os.path.join(tmpdir, 'sub')) - self.write_file([tmpdir, 'sub', 'file3'], 'xxx') + self.test_make_tarball('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–') # japanese for archive + def _make_tarball(self, tmpdir, target_name, suffix, **kwargs): tmpdir2 = self.mkdtemp() unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], "source and target should be on same drive") @@ -89,27 +109,13 @@ class ArchiveUtilTestCase(support.TempdirManager, base_name = os.path.join(tmpdir2, target_name) # working with relative paths to avoid tar warnings - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - make_tarball(splitdrive(base_name)[1], '.') - finally: - os.chdir(old_dir) + with change_cwd(tmpdir): + make_tarball(splitdrive(base_name)[1], 'dist', **kwargs) # check if the compressed tarball was created - tarball = base_name + '.tar.gz' - self.assertTrue(os.path.exists(tarball)) - - # trying an uncompressed one - base_name = os.path.join(tmpdir2, target_name) - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - make_tarball(splitdrive(base_name)[1], '.', compress=None) - finally: - os.chdir(old_dir) - tarball = base_name + '.tar' + tarball = base_name + suffix self.assertTrue(os.path.exists(tarball)) + self.assertEqual(self._tarinfo(tarball), self._created_files) def _tarinfo(self, path): tar = tarfile.open(path) @@ -120,6 +126,9 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: tar.close() + _created_files = ('dist', 'dist/file1', 'dist/file2', + 'dist/sub', 'dist/sub/file3', 'dist/sub2') + def _create_files(self): # creating something to tar tmpdir = self.mkdtemp() @@ -130,15 +139,15 @@ class ArchiveUtilTestCase(support.TempdirManager, os.mkdir(os.path.join(dist, 'sub')) self.write_file([dist, 'sub', 'file3'], 'xxx') os.mkdir(os.path.join(dist, 'sub2')) - tmpdir2 = self.mkdtemp() - base_name = os.path.join(tmpdir2, 'archive') - return tmpdir, tmpdir2, base_name + return tmpdir @unittest.skipUnless(find_executable('tar') and find_executable('gzip') and ZLIB_SUPPORT, 'Need the tar, gzip and zlib command to run') def test_tarfile_vs_tar(self): - tmpdir, tmpdir2, base_name = self._create_files() + tmpdir = self._create_files() + tmpdir2 = self.mkdtemp() + base_name = os.path.join(tmpdir2, 'archive') old_dir = os.getcwd() os.chdir(tmpdir) try: @@ -164,7 +173,8 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertTrue(os.path.exists(tarball2)) # let's compare both tarballs - self.assertEqual(self._tarinfo(tarball), self._tarinfo(tarball2)) + self.assertEqual(self._tarinfo(tarball), self._created_files) + self.assertEqual(self._tarinfo(tarball2), self._created_files) # trying an uncompressed one base_name = os.path.join(tmpdir2, 'archive') @@ -191,7 +201,8 @@ class ArchiveUtilTestCase(support.TempdirManager, @unittest.skipUnless(find_executable('compress'), 'The compress program is required') def test_compress_deprecated(self): - tmpdir, tmpdir2, base_name = self._create_files() + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') # using compress and testing the PendingDeprecationWarning old_dir = os.getcwd() @@ -224,17 +235,17 @@ class ArchiveUtilTestCase(support.TempdirManager, 'Need zip and zlib support to run') def test_make_zipfile(self): # creating something to tar - tmpdir = self.mkdtemp() - self.write_file([tmpdir, 'file1'], 'xxx') - self.write_file([tmpdir, 'file2'], 'xxx') - - tmpdir2 = self.mkdtemp() - base_name = os.path.join(tmpdir2, 'archive') - make_zipfile(base_name, tmpdir) + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') + with change_cwd(tmpdir): + make_zipfile(base_name, 'dist') # check if the compressed tarball was created tarball = base_name + '.zip' self.assertTrue(os.path.exists(tarball)) + with zipfile.ZipFile(tarball) as zf: + self.assertEqual(sorted(zf.namelist()), + ['dist/file1', 'dist/file2', 'dist/sub/file3']) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile_no_zlib(self): @@ -250,18 +261,24 @@ class ArchiveUtilTestCase(support.TempdirManager, patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile) # create something to tar and compress - tmpdir, tmpdir2, base_name = self._create_files() - make_zipfile(base_name, tmpdir) + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') + with change_cwd(tmpdir): + make_zipfile(base_name, 'dist') tarball = base_name + '.zip' self.assertEqual(called, [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) self.assertTrue(os.path.exists(tarball)) + with zipfile.ZipFile(tarball) as zf: + self.assertEqual(sorted(zf.namelist()), + ['dist/file1', 'dist/file2', 'dist/sub/file3']) def test_check_archive_formats(self): self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), 'xxx') - self.assertEqual(check_archive_formats(['gztar', 'zip']), None) + self.assertIsNone(check_archive_formats(['gztar', 'bztar', 'xztar', + 'ztar', 'tar', 'zip'])) def test_make_archive(self): tmpdir = self.mkdtemp() @@ -282,6 +299,41 @@ class ArchiveUtilTestCase(support.TempdirManager, finally: del ARCHIVE_FORMATS['xxx'] + def test_make_archive_tar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'tar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar') + self.assertEqual(self._tarinfo(res), self._created_files) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_make_archive_gztar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'gztar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar.gz') + self.assertEqual(self._tarinfo(res), self._created_files) + + @unittest.skipUnless(bz2, 'Need bz2 support to run') + def test_make_archive_bztar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'bztar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar.bz2') + self.assertEqual(self._tarinfo(res), self._created_files) + + @unittest.skipUnless(bz2, 'Need xz support to run') + def test_make_archive_xztar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'xztar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar.xz') + self.assertEqual(self._tarinfo(res), self._created_files) + def test_make_archive_owner_group(self): # testing make_archive with owner and group, with various combinations # this works even if there's not gid/uid support @@ -291,7 +343,8 @@ class ArchiveUtilTestCase(support.TempdirManager, else: group = owner = 'root' - base_dir, root_dir, base_name = self._create_files() + base_dir = self._create_files() + root_dir = self.mkdtemp() base_name = os.path.join(self.mkdtemp() , 'archive') res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, group=group) @@ -311,7 +364,8 @@ class ArchiveUtilTestCase(support.TempdirManager, @unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib") @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") def test_tarfile_root_owner(self): - tmpdir, tmpdir2, base_name = self._create_files() + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') old_dir = os.getcwd() os.chdir(tmpdir) group = grp.getgrgid(0)[0] diff --git a/tests/test_bdist.py b/tests/test_bdist.py index 503a6e85..f762f5d9 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -21,7 +21,7 @@ class BuildTestCase(support.TempdirManager, # what formats does bdist offer? formats = ['bztar', 'gztar', 'msi', 'rpm', 'tar', - 'wininst', 'zip', 'ztar'] + 'wininst', 'xztar', 'zip', 'ztar'] found = sorted(cmd.format_command) self.assertEqual(found, formats) -- cgit v1.2.1 From a583aac0ebff225a9b138cda7d60047d7f7f462c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 17 May 2015 02:23:02 +0300 Subject: Fixed issue #16314 test for the case when lzma is not available. --- tests/test_archive_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 81d4c74a..02fa1e27 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -325,7 +325,7 @@ class ArchiveUtilTestCase(support.TempdirManager, self.assertEqual(os.path.basename(res), 'archive.tar.bz2') self.assertEqual(self._tarinfo(res), self._created_files) - @unittest.skipUnless(bz2, 'Need xz support to run') + @unittest.skipUnless(lzma, 'Need xz support to run') def test_make_archive_xztar(self): base_dir = self._create_files() base_name = os.path.join(self.mkdtemp() , 'archive') -- cgit v1.2.1 From 37ec45e05a5c9b6dfccbfc97caf466496b727616 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 20 May 2015 16:10:04 +0300 Subject: Issue #24245: Eliminated senseless expect clauses that have no any effect. Patch by Martin Panter. --- core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/core.py b/core.py index 2bfe66aa..f05b34b2 100644 --- a/core.py +++ b/core.py @@ -221,8 +221,6 @@ def run_setup (script_name, script_args=None, stop_after="run"): # Hmm, should we do something if exiting with a non-zero code # (ie. error)? pass - except: - raise if _setup_distribution is None: raise RuntimeError(("'distutils.core.setup()' was never called -- " -- cgit v1.2.1 From d034a5ec7f707499139f90eb846b9e720923124c Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 23 May 2015 09:02:50 -0700 Subject: Issue #23970: Adds distutils._msvccompiler for new Visual Studio versions. --- _msvccompiler.py | 561 +++++++++++++++++++++++++++++++++++++++++++++ ccompiler.py | 2 +- command/bdist_wininst.py | 31 ++- command/build_ext.py | 36 +-- tests/test_msvccompiler.py | 160 +++++++++++++ 5 files changed, 752 insertions(+), 38 deletions(-) create mode 100644 _msvccompiler.py create mode 100644 tests/test_msvccompiler.py diff --git a/_msvccompiler.py b/_msvccompiler.py new file mode 100644 index 00000000..896d9d92 --- /dev/null +++ b/_msvccompiler.py @@ -0,0 +1,561 @@ +"""distutils._msvccompiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for Microsoft Visual Studio 2015. + +The module is compatible with VS 2015 and later. You can find legacy support +for older versions in distutils.msvc9compiler and distutils.msvccompiler. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) +# ported to VS 2005 and VS 2008 by Christian Heimes +# ported to VS 2015 by Steve Dower + +import os +import subprocess +import re + +from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import CCompiler, gen_lib_options +from distutils import log +from distutils.util import get_platform + +import winreg +from itertools import count + +def _find_vcvarsall(): + with winreg.OpenKeyEx( + winreg.HKEY_LOCAL_MACHINE, + r"Software\Microsoft\VisualStudio\SxS\VC7", + access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY + ) as key: + if not key: + log.debug("Visual C++ is not registered") + return None + + best_version = 0 + best_dir = None + for i in count(): + try: + v, vc_dir, vt = winreg.EnumValue(key, i) + except OSError: + break + if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir): + try: + version = int(float(v)) + except (ValueError, TypeError): + continue + if version >= 14 and version > best_version: + best_version, best_dir = version, vc_dir + if not best_version: + log.debug("No suitable Visual C++ version found") + return None + + vcvarsall = os.path.join(best_dir, "vcvarsall.bat") + if not os.path.isfile(vcvarsall): + log.debug("%s cannot be found", vcvarsall) + return None + + return vcvarsall + +def _get_vc_env(plat_spec): + if os.getenv("DISTUTILS_USE_SDK"): + return { + key.lower(): value + for key, value in os.environ.items() + } + + vcvarsall = _find_vcvarsall() + if not vcvarsall: + raise DistutilsPlatformError("Unable to find vcvarsall.bat") + + try: + out = subprocess.check_output( + '"{}" {} && set'.format(vcvarsall, plat_spec), + shell=True, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) + except subprocess.CalledProcessError as exc: + log.error(exc.output) + raise DistutilsPlatformError("Error executing {}" + .format(exc.cmd)) + + return { + key.lower(): value + for key, _, value in + (line.partition('=') for line in out.splitlines()) + if key and value + } + +def _find_exe(exe, paths=None): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + if not paths: + paths = os.getenv('path').split(os.pathsep) + for p in paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + return exe + +# A map keyed by get_platform() return values to values accepted by +# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is +# the param to cross-compile on x86 targetting amd64.) +PLAT_TO_VCVARS = { + 'win32' : 'x86', + 'win-amd64' : 'amd64', +} + +class MSVCCompiler(CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) + # target platform (.plat_name is consistent with 'bdist') + self.plat_name = None + self.initialized = False + + def initialize(self, plat_name=None): + # multi-init means we would need to check platform same each time... + assert not self.initialized, "don't init multiple times" + if plat_name is None: + plat_name = get_platform() + # sanity check for platforms to prevent obscure errors later. + if plat_name not in PLAT_TO_VCVARS: + raise DistutilsPlatformError("--plat-name must be one of {}" + .format(tuple(PLAT_TO_VCVARS))) + + # On x86, 'vcvarsall.bat amd64' creates an env that doesn't work; + # to cross compile, you use 'x86_amd64'. + # On AMD64, 'vcvarsall.bat amd64' is a native build env; to cross + # compile use 'x86' (ie, it runs the x86 compiler directly) + if plat_name == get_platform() or plat_name == 'win32': + # native build or cross-compile to win32 + plat_spec = PLAT_TO_VCVARS[plat_name] + else: + # cross compile from win32 -> some 64bit + plat_spec = '{}_{}'.format( + PLAT_TO_VCVARS[get_platform()], + PLAT_TO_VCVARS[plat_name] + ) + + vc_env = _get_vc_env(plat_spec) + if not vc_env: + raise DistutilsPlatformError("Unable to find a compatible " + "Visual Studio installation.") + + paths = vc_env.get('path', '').split(os.pathsep) + self.cc = _find_exe("cl.exe", paths) + self.linker = _find_exe("link.exe", paths) + self.lib = _find_exe("lib.exe", paths) + self.rc = _find_exe("rc.exe", paths) # resource compiler + self.mc = _find_exe("mc.exe", paths) # message compiler + self.mt = _find_exe("mt.exe", paths) # message compiler + + for dir in vc_env.get('include', '').split(os.pathsep): + if dir: + self.add_include_dir(dir) + + for dir in vc_env.get('lib', '').split(os.pathsep): + if dir: + self.add_library_dir(dir) + + self.preprocess_options = None + self.compile_options = [ + '/nologo', '/Ox', '/MD', '/W3', '/GL', '/DNDEBUG' + ] + self.compile_options_debug = [ + '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' + ] + + self.ldflags_shared = [ + '/nologo', '/DLL', '/INCREMENTAL:NO' + ] + self.ldflags_shared_debug = [ + '/nologo', '/DLL', '/INCREMENTAL:no', '/DEBUG:FULL' + ] + self.ldflags_static = [ + '/nologo' + ] + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): + ext_map = {ext: self.obj_extension for ext in self.src_extensions} + ext_map.update((ext, self.res_extension) + for ext in self._rc_extensions + self._mc_extensions) + + def make_out_path(p): + base, ext = os.path.splitext(p) + if strip_dir: + base = os.path.basename(base) + else: + _, base = os.path.splitdrive(base) + if base.startswith((os.path.sep, os.path.altsep)): + base = base[1:] + try: + return base + ext_map[ext] + except LookupError: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError("Don't know how to compile {}".format(p)) + + output_dir = output_dir or '' + return [ + os.path.join(output_dir, make_out_path(src_name)) + for src_name in source_filenames + ] + + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + + add_cpp_opts = False + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + add_cpp_opts = True + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + [output_opt, input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src]) + base, _ = os.path.splitext(os.path.basename (src)) + rc_file = os.path.join(rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc, "/fo" + obj, rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError("Don't know how to compile {} to {}" + .format(src, obj)) + + args = [self.cc] + compile_opts + pp_opts + if add_cpp_opts: + args.append('/EHsc') + args.append(input_opt) + args.append("/Fo" + obj) + args.extend(extra_postargs) + + try: + self.spawn(args) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + if not self.initialized: + self.initialize() + objects, output_dir = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + objects, output_dir = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + libraries, library_dirs, runtime_library_dirs = fixed_args + + if runtime_library_dirs: + self.warn("I don't know what to do with 'runtime_library_dirs': " + + str(runtime_library_dirs)) + + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + ldflags = (self.ldflags_shared_debug if debug + else self.ldflags_shared) + if target_desc == CCompiler.EXECUTABLE: + ldflags = ldflags[1:] + + export_opts = [] + for sym in (export_symbols or []): + export_opts.append("/EXPORT:" + sym) + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + build_temp = os.path.dirname(objects[0]) + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + build_temp, + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + + self.manifest_setup_ldargs(output_filename, build_temp, ld_args) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath(os.path.dirname(output_filename)) + try: + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + # embed the manifest + # XXX - this is somewhat fragile - if mt.exe fails, distutils + # will still consider the DLL up-to-date, but it will not have a + # manifest. Maybe we should link to a temp file? OTOH, that + # implies a build environment error that shouldn't go undetected. + mfinfo = self.manifest_get_embed_info(target_desc, ld_args) + if mfinfo is not None: + mffilename, mfid = mfinfo + out_arg = '-outputresource:{};{}'.format(output_filename, mfid) + try: + self.spawn([self.mt, '-nologo', '-manifest', + mffilename, out_arg]) + except DistutilsExecError as msg: + raise LinkError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): + # If we need a manifest at all, an embedded manifest is recommended. + # See MSDN article titled + # "How to: Embed a Manifest Inside a C/C++ Application" + # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) + # Ask the linker to generate the manifest in the temp dir, so + # we can check it, and possibly embed it, later. + temp_manifest = os.path.join( + build_temp, + os.path.basename(output_filename) + ".manifest") + ld_args.append('/MANIFESTFILE:' + temp_manifest) + + def manifest_get_embed_info(self, target_desc, ld_args): + # If a manifest should be embedded, return a tuple of + # (manifest_filename, resource_id). Returns None if no manifest + # should be embedded. See http://bugs.python.org/issue7833 for why + # we want to avoid any manifest for extension modules if we can) + for arg in ld_args: + if arg.startswith("/MANIFESTFILE:"): + temp_manifest = arg.split(":", 1)[1] + break + else: + # no /MANIFESTFILE so nothing to do. + return None + if target_desc == CCompiler.EXECUTABLE: + # by default, executables always get the manifest with the + # CRT referenced. + mfid = 1 + else: + # Extension modules try and avoid any manifest if possible. + mfid = 2 + temp_manifest = self._remove_visual_c_ref(temp_manifest) + if temp_manifest is None: + return None + return temp_manifest, mfid + + def _remove_visual_c_ref(self, manifest_file): + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + # Returns either the filename of the modified manifest or + # None if no manifest should be embedded. + manifest_f = open(manifest_file) + try: + manifest_buf = manifest_f.read() + finally: + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = "\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + # Now see if any other assemblies are referenced - if not, we + # don't want a manifest embedded. + pattern = re.compile( + r"""|)""", re.DOTALL) + if re.search(pattern, manifest_buf) is None: + return None + + manifest_f = open(manifest_file, 'w') + try: + manifest_f.write(manifest_buf) + return manifest_file + finally: + manifest_f.close() + except OSError: + pass + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC") + + def library_option(self, lib): + return self.library_filename(lib) + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename(name)) + if os.path.isfile(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None diff --git a/ccompiler.py b/ccompiler.py index 911e84dd..b7df394e 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -959,7 +959,7 @@ def get_default_compiler(osname=None, platform=None): # is assumed to be in the 'distutils' package.) compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', "standard UNIX-style compiler"), - 'msvc': ('msvccompiler', 'MSVCCompiler', + 'msvc': ('_msvccompiler', 'MSVCCompiler', "Microsoft Visual C++"), 'cygwin': ('cygwinccompiler', 'CygwinCCompiler', "Cygwin port of GNU C Compiler for Win32"), diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 959a8bf6..a3eff7e7 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -303,7 +303,6 @@ class bdist_wininst(Command): return installer_name def get_exe_bytes(self): - from distutils.msvccompiler import get_build_version # If a target-version other than the current version has been # specified, then using the MSVC version from *this* build is no good. # Without actually finding and executing the target version and parsing @@ -313,20 +312,28 @@ class bdist_wininst(Command): # We can then execute this program to obtain any info we need, such # as the real sys.version string for the build. cur_version = get_python_version() - if self.target_version and self.target_version != cur_version: - # If the target version is *later* than us, then we assume they - # use what we use - # string compares seem wrong, but are what sysconfig.py itself uses - if self.target_version > cur_version: - bv = get_build_version() + + # If the target version is *later* than us, then we assume they + # use what we use + # string compares seem wrong, but are what sysconfig.py itself uses + if self.target_version and self.target_version < cur_version: + if self.target_version < "2.4": + bv = 6.0 + elif self.target_version == "2.4": + bv = 7.1 + elif self.target_version == "2.5": + bv = 8.0 + elif self.target_version <= "3.2": + bv = 9.0 + elif self.target_version <= "3.4": + bv = 10.0 else: - if self.target_version < "2.4": - bv = 6.0 - else: - bv = 7.1 + bv = 14.0 else: # for current version - use authoritative check. - bv = get_build_version() + from msvcrt import CRT_ASSEMBLY_VERSION + bv = float('.'.join(CRT_ASSEMBLY_VERSION.split('.', 2)[:2])) + # wininst-x.y.exe is in the same directory as this file directory = os.path.dirname(__file__) diff --git a/command/build_ext.py b/command/build_ext.py index c5a3ce19..d4cb11e6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -19,10 +19,6 @@ from distutils import log from site import USER_BASE -if os.name == 'nt': - from distutils.msvccompiler import get_build_version - MSVC_VERSION = int(get_build_version()) - # An extension name is just a dot-separated list of Python NAMEs (ie. # the same as a fully-qualified module name). extension_name_re = re.compile \ @@ -206,27 +202,17 @@ class build_ext(Command): _sys_home = getattr(sys, '_home', None) if _sys_home: self.library_dirs.append(_sys_home) - if MSVC_VERSION >= 9: - # Use the .lib files for the correct architecture - if self.plat_name == 'win32': - suffix = 'win32' - else: - # win-amd64 or win-ia64 - suffix = self.plat_name[4:] - new_lib = os.path.join(sys.exec_prefix, 'PCbuild') - if suffix: - new_lib = os.path.join(new_lib, suffix) - self.library_dirs.append(new_lib) - - elif MSVC_VERSION == 8: - self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PC', 'VS8.0')) - elif MSVC_VERSION == 7: - self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PC', 'VS7.1')) + + # Use the .lib files for the correct architecture + if self.plat_name == 'win32': + suffix = 'win32' else: - self.library_dirs.append(os.path.join(sys.exec_prefix, - 'PC', 'VC6')) + # win-amd64 or win-ia64 + suffix = self.plat_name[4:] + new_lib = os.path.join(sys.exec_prefix, 'PCbuild') + if suffix: + new_lib = os.path.join(new_lib, suffix) + self.library_dirs.append(new_lib) # for extensions under Cygwin and AtheOS Python's library directory must be # appended to library_dirs @@ -716,7 +702,7 @@ class build_ext(Command): # to need it mentioned explicitly, though, so that's what we do. # Append '_d' to the python import library on debug builds. if sys.platform == "win32": - from distutils.msvccompiler import MSVCCompiler + from distutils._msvccompiler import MSVCCompiler if not isinstance(self.compiler, MSVCCompiler): template = "python%d%d" if self.debug: diff --git a/tests/test_msvccompiler.py b/tests/test_msvccompiler.py new file mode 100644 index 00000000..b4407543 --- /dev/null +++ b/tests/test_msvccompiler.py @@ -0,0 +1,160 @@ +"""Tests for distutils._msvccompiler.""" +import sys +import unittest +import os + +from distutils.errors import DistutilsPlatformError +from distutils.tests import support +from test.support import run_unittest + +# A manifest with the only assembly reference being the msvcrt assembly, so +# should have the assembly completely stripped. Note that although the +# assembly has a reference the assembly is removed - that is +# currently a "feature", not a bug :) +_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\ + + + + + + + + + + + + + + + + + +""" + +# A manifest with references to assemblies other than msvcrt. When processed, +# this assembly should be returned with just the msvcrt part removed. +_MANIFEST_WITH_MULTIPLE_REFERENCES = """\ + + + + + + + + + + + + + + + + + + + + + + +""" + +_CLEANED_MANIFEST = """\ + + + + + + + + + + + + + + + + + + +""" + +SKIP_MESSAGE = (None if sys.platform == "win32" else + "These tests are only for win32") + +@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) +class msvccompilerTestCase(support.TempdirManager, + unittest.TestCase): + + def test_no_compiler(self): + # makes sure query_vcvarsall raises + # a DistutilsPlatformError if the compiler + # is not found + from distutils._msvccompiler import _get_vc_env + def _find_vcvarsall(): + return None + + import distutils._msvccompiler as _msvccompiler + old_find_vcvarsall = _msvccompiler._find_vcvarsall + _msvccompiler._find_vcvarsall = _find_vcvarsall + try: + self.assertRaises(DistutilsPlatformError, _get_vc_env, + 'wont find this version') + finally: + _msvccompiler._find_vcvarsall = old_find_vcvarsall + + def test_remove_visual_c_ref(self): + from distutils._msvccompiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + try: + f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES) + finally: + f.close() + + compiler = MSVCCompiler() + compiler._remove_visual_c_ref(manifest) + + # see what we got + f = open(manifest) + try: + # removing trailing spaces + content = '\n'.join([line.rstrip() for line in f.readlines()]) + finally: + f.close() + + # makes sure the manifest was properly cleaned + self.assertEqual(content, _CLEANED_MANIFEST) + + def test_remove_entire_manifest(self): + from distutils._msvccompiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + try: + f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE) + finally: + f.close() + + compiler = MSVCCompiler() + got = compiler._remove_visual_c_ref(manifest) + self.assertIsNone(got) + + +def test_suite(): + return unittest.makeSuite(msvccompilerTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) -- cgit v1.2.1 From 739202377f34c4058ce96bafd0150c4299fa21c0 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 23 May 2015 12:15:57 -0700 Subject: Issue #23970: Fixes bdist_wininst not working on non-Windows platform. --- command/bdist_wininst.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index a3eff7e7..0c0e2c1a 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -331,8 +331,13 @@ class bdist_wininst(Command): bv = 14.0 else: # for current version - use authoritative check. - from msvcrt import CRT_ASSEMBLY_VERSION - bv = float('.'.join(CRT_ASSEMBLY_VERSION.split('.', 2)[:2])) + try: + from msvcrt import CRT_ASSEMBLY_VERSION + except ImportError: + # cross-building, so assume the latest version + bv = 14.0 + else: + bv = float('.'.join(CRT_ASSEMBLY_VERSION.split('.', 2)[:2])) # wininst-x.y.exe is in the same directory as this file -- cgit v1.2.1 From 1ae0b75fb909b3fa1bd42702d4ab2a943a8f7155 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 23 May 2015 17:43:05 -0700 Subject: Version bump for 3.5.0b1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 37bfd5a8..686ca1de 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ used from a setup script as # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a4" +__version__ = "3.5.0b1" #--end constants-- -- cgit v1.2.1 From 0db81b9974abf772d40f64f77c9d7220f880b4e9 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 25 May 2015 21:24:00 -0500 Subject: keep distutils version in sync with python version automatically --- __init__.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/__init__.py b/__init__.py index 6eb79cb6..d823d040 100644 --- a/__init__.py +++ b/__init__.py @@ -8,10 +8,6 @@ used from a setup script as setup (...) """ -# Distutils version -# -# Updated automatically by the Python release process. -# -#--start constants-- -__version__ = "3.2.6" -#--end constants-- +import sys + +__version__ = sys.version[:sys.version.index(' ')] -- cgit v1.2.1 From 7c42a077cf4a91e095a2bc4964927de32b5cab78 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Tue, 28 Jul 2015 15:55:07 +1200 Subject: Issue #23426: run_setup was broken in distutils. Patch from Alexander Belopolsky. --- core.py | 5 ++--- tests/test_core.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/core.py b/core.py index f05b34b2..d603d4a4 100644 --- a/core.py +++ b/core.py @@ -204,16 +204,15 @@ def run_setup (script_name, script_args=None, stop_after="run"): global _setup_stop_after, _setup_distribution _setup_stop_after = stop_after - save_argv = sys.argv + save_argv = sys.argv.copy() g = {'__file__': script_name} - l = {} try: try: sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args with open(script_name, 'rb') as f: - exec(f.read(), g, l) + exec(f.read(), g) finally: sys.argv = save_argv _setup_stop_after = None diff --git a/tests/test_core.py b/tests/test_core.py index 41321f7d..57856f19 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -28,6 +28,21 @@ from distutils.core import setup setup() """ +setup_does_nothing = """\ +from distutils.core import setup +setup() +""" + + +setup_defines_subclass = """\ +from distutils.core import setup +from distutils.command.install import install as _install + +class install(_install): + sub_commands = _install.sub_commands + ['cmd'] + +setup(cmdclass={'install': install}) +""" class CoreTestCase(support.EnvironGuard, unittest.TestCase): @@ -65,6 +80,21 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): distutils.core.run_setup( self.write_setup(setup_using___file__)) + def test_run_setup_preserves_sys_argv(self): + # Make sure run_setup does not clobber sys.argv + argv_copy = sys.argv.copy() + distutils.core.run_setup( + self.write_setup(setup_does_nothing)) + self.assertEqual(sys.argv, argv_copy) + + def test_run_setup_defines_subclass(self): + # Make sure the script can use __file__; if that's missing, the test + # setup.py script will raise NameError. + dist = distutils.core.run_setup( + self.write_setup(setup_defines_subclass)) + install = dist.get_command_obj('install') + self.assertIn('cmd', install.sub_commands) + def test_run_setup_uses_current_dir(self): # This tests that the setup script is run with the current directory # as its own current directory; this was temporarily broken by a -- cgit v1.2.1 From 84deb7074ef2c9086aeb440f84738fef0bc67f74 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 30 Jul 2015 11:51:06 -0700 Subject: Update default msvccompiler link options to match the options used for core builds. This ensures that wheels will work when moved to machines that have the same subset of the MSVC libraries as a regular CPython install. Specifically, vcruntime##0.dll may not be installed, and should not be a dependency. --- _msvccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index 896d9d92..4e2eed72 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -207,10 +207,10 @@ class MSVCCompiler(CCompiler) : ] self.ldflags_shared = [ - '/nologo', '/DLL', '/INCREMENTAL:NO' + '/nologo', '/DLL', '/INCREMENTAL:NO', '/LTCG', '/nodefaultlib:libucrt.lib', 'ucrt.lib' ] self.ldflags_shared_debug = [ - '/nologo', '/DLL', '/INCREMENTAL:no', '/DEBUG:FULL' + '/nologo', '/DLL', '/INCREMENTAL:no', '/LTCG', '/DEBUG:FULL', '/nodefaultlib:libucrtd.lib', 'ucrtd.lib' ] self.ldflags_static = [ '/nologo' -- cgit v1.2.1 From 8b68b23eea750175cdf4ee91547fac64434ce34c Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 5 Aug 2015 11:39:19 -0700 Subject: Issue #24798: _msvccompiler.py doesn't properly support manifests --- _msvccompiler.py | 167 ++++++++++++++------------------------------- tests/test_msvccompiler.py | 121 -------------------------------- 2 files changed, 50 insertions(+), 238 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index 4e2eed72..b344616e 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -15,7 +15,6 @@ for older versions in distutils.msvc9compiler and distutils.msvccompiler. import os import subprocess -import re from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -182,7 +181,8 @@ class MSVCCompiler(CCompiler) : raise DistutilsPlatformError("Unable to find a compatible " "Visual Studio installation.") - paths = vc_env.get('path', '').split(os.pathsep) + self._paths = vc_env.get('path', '') + paths = self._paths.split(os.pathsep) self.cc = _find_exe("cl.exe", paths) self.linker = _find_exe("link.exe", paths) self.lib = _find_exe("lib.exe", paths) @@ -199,23 +199,41 @@ class MSVCCompiler(CCompiler) : self.add_library_dir(dir) self.preprocess_options = None + # Use /MT[d] to build statically, then switch from libucrt[d].lib to ucrt[d].lib + # later to dynamically link to ucrtbase but not vcruntime. self.compile_options = [ - '/nologo', '/Ox', '/MD', '/W3', '/GL', '/DNDEBUG' + '/nologo', '/Ox', '/MT', '/W3', '/GL', '/DNDEBUG' ] self.compile_options_debug = [ - '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' + '/nologo', '/Od', '/MTd', '/Zi', '/W3', '/D_DEBUG' ] - self.ldflags_shared = [ - '/nologo', '/DLL', '/INCREMENTAL:NO', '/LTCG', '/nodefaultlib:libucrt.lib', 'ucrt.lib' + ldflags = [ + '/nologo', '/INCREMENTAL:NO', '/LTCG', '/nodefaultlib:libucrt.lib', 'ucrt.lib', ] - self.ldflags_shared_debug = [ - '/nologo', '/DLL', '/INCREMENTAL:no', '/LTCG', '/DEBUG:FULL', '/nodefaultlib:libucrtd.lib', 'ucrtd.lib' - ] - self.ldflags_static = [ - '/nologo' + ldflags_debug = [ + '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL', '/nodefaultlib:libucrtd.lib', 'ucrtd.lib', ] + self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1'] + self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1'] + self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] + self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] + self.ldflags_static = [*ldflags] + self.ldflags_static_debug = [*ldflags_debug] + + self._ldflags = { + (CCompiler.EXECUTABLE, None): self.ldflags_exe, + (CCompiler.EXECUTABLE, False): self.ldflags_exe, + (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug, + (CCompiler.SHARED_OBJECT, None): self.ldflags_shared, + (CCompiler.SHARED_OBJECT, False): self.ldflags_shared, + (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug, + (CCompiler.SHARED_LIBRARY, None): self.ldflags_static, + (CCompiler.SHARED_LIBRARY, False): self.ldflags_static, + (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug, + } + self.initialized = True # -- Worker methods ------------------------------------------------ @@ -224,9 +242,12 @@ class MSVCCompiler(CCompiler) : source_filenames, strip_dir=0, output_dir=''): - ext_map = {ext: self.obj_extension for ext in self.src_extensions} - ext_map.update((ext, self.res_extension) - for ext in self._rc_extensions + self._mc_extensions) + ext_map = { + **{ext: self.obj_extension for ext in self.src_extensions}, + **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions}, + } + + output_dir = output_dir or '' def make_out_path(p): base, ext = os.path.splitext(p) @@ -237,18 +258,17 @@ class MSVCCompiler(CCompiler) : if base.startswith((os.path.sep, os.path.altsep)): base = base[1:] try: - return base + ext_map[ext] + # XXX: This may produce absurdly long paths. We should check + # the length of the result and trim base until we fit within + # 260 characters. + return os.path.join(output_dir, base + ext_map[ext]) except LookupError: # Better to raise an exception instead of silently continuing # and later complain about sources and targets having # different lengths raise CompileError("Don't know how to compile {}".format(p)) - output_dir = output_dir or '' - return [ - os.path.join(output_dir, make_out_path(src_name)) - for src_name in source_filenames - ] + return list(map(make_out_path, source_filenames)) def compile(self, sources, @@ -359,6 +379,7 @@ class MSVCCompiler(CCompiler) : if debug: pass # XXX what goes here? try: + log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args)) self.spawn([self.lib] + lib_args) except DistutilsExecError as msg: raise LibError(msg) @@ -399,14 +420,9 @@ class MSVCCompiler(CCompiler) : output_filename = os.path.join(output_dir, output_filename) if self._need_link(objects, output_filename): - ldflags = (self.ldflags_shared_debug if debug - else self.ldflags_shared) - if target_desc == CCompiler.EXECUTABLE: - ldflags = ldflags[1:] + ldflags = self._ldflags[target_desc, debug] - export_opts = [] - for sym in (export_symbols or []): - export_opts.append("/EXPORT:" + sym) + export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])] ld_args = (ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]) @@ -425,8 +441,6 @@ class MSVCCompiler(CCompiler) : self.library_filename(dll_name)) ld_args.append ('/IMPLIB:' + implib_file) - self.manifest_setup_ldargs(output_filename, build_temp, ld_args) - if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: @@ -434,101 +448,20 @@ class MSVCCompiler(CCompiler) : self.mkpath(os.path.dirname(output_filename)) try: + log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args)) self.spawn([self.linker] + ld_args) except DistutilsExecError as msg: raise LinkError(msg) - - # embed the manifest - # XXX - this is somewhat fragile - if mt.exe fails, distutils - # will still consider the DLL up-to-date, but it will not have a - # manifest. Maybe we should link to a temp file? OTOH, that - # implies a build environment error that shouldn't go undetected. - mfinfo = self.manifest_get_embed_info(target_desc, ld_args) - if mfinfo is not None: - mffilename, mfid = mfinfo - out_arg = '-outputresource:{};{}'.format(output_filename, mfid) - try: - self.spawn([self.mt, '-nologo', '-manifest', - mffilename, out_arg]) - except DistutilsExecError as msg: - raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) - def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): - # If we need a manifest at all, an embedded manifest is recommended. - # See MSDN article titled - # "How to: Embed a Manifest Inside a C/C++ Application" - # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) - # Ask the linker to generate the manifest in the temp dir, so - # we can check it, and possibly embed it, later. - temp_manifest = os.path.join( - build_temp, - os.path.basename(output_filename) + ".manifest") - ld_args.append('/MANIFESTFILE:' + temp_manifest) - - def manifest_get_embed_info(self, target_desc, ld_args): - # If a manifest should be embedded, return a tuple of - # (manifest_filename, resource_id). Returns None if no manifest - # should be embedded. See http://bugs.python.org/issue7833 for why - # we want to avoid any manifest for extension modules if we can) - for arg in ld_args: - if arg.startswith("/MANIFESTFILE:"): - temp_manifest = arg.split(":", 1)[1] - break - else: - # no /MANIFESTFILE so nothing to do. - return None - if target_desc == CCompiler.EXECUTABLE: - # by default, executables always get the manifest with the - # CRT referenced. - mfid = 1 - else: - # Extension modules try and avoid any manifest if possible. - mfid = 2 - temp_manifest = self._remove_visual_c_ref(temp_manifest) - if temp_manifest is None: - return None - return temp_manifest, mfid - - def _remove_visual_c_ref(self, manifest_file): + def spawn(self, cmd): + old_path = os.getenv('path') try: - # Remove references to the Visual C runtime, so they will - # fall through to the Visual C dependency of Python.exe. - # This way, when installed for a restricted user (e.g. - # runtimes are not in WinSxS folder, but in Python's own - # folder), the runtimes do not need to be in every folder - # with .pyd's. - # Returns either the filename of the modified manifest or - # None if no manifest should be embedded. - manifest_f = open(manifest_file) - try: - manifest_buf = manifest_f.read() - finally: - manifest_f.close() - pattern = re.compile( - r"""|)""", - re.DOTALL) - manifest_buf = re.sub(pattern, "", manifest_buf) - pattern = "\s*" - manifest_buf = re.sub(pattern, "", manifest_buf) - # Now see if any other assemblies are referenced - if not, we - # don't want a manifest embedded. - pattern = re.compile( - r"""|)""", re.DOTALL) - if re.search(pattern, manifest_buf) is None: - return None - - manifest_f = open(manifest_file, 'w') - try: - manifest_f.write(manifest_buf) - return manifest_file - finally: - manifest_f.close() - except OSError: - pass + os.environ['path'] = self._paths + return super().spawn(cmd) + finally: + os.environ['path'] = old_path # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in diff --git a/tests/test_msvccompiler.py b/tests/test_msvccompiler.py index b4407543..1f889079 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -7,88 +7,6 @@ from distutils.errors import DistutilsPlatformError from distutils.tests import support from test.support import run_unittest -# A manifest with the only assembly reference being the msvcrt assembly, so -# should have the assembly completely stripped. Note that although the -# assembly has a reference the assembly is removed - that is -# currently a "feature", not a bug :) -_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\ - - - - - - - - - - - - - - - - - -""" - -# A manifest with references to assemblies other than msvcrt. When processed, -# this assembly should be returned with just the msvcrt part removed. -_MANIFEST_WITH_MULTIPLE_REFERENCES = """\ - - - - - - - - - - - - - - - - - - - - - - -""" - -_CLEANED_MANIFEST = """\ - - - - - - - - - - - - - - - - - - -""" SKIP_MESSAGE = (None if sys.platform == "win32" else "These tests are only for win32") @@ -114,45 +32,6 @@ class msvccompilerTestCase(support.TempdirManager, finally: _msvccompiler._find_vcvarsall = old_find_vcvarsall - def test_remove_visual_c_ref(self): - from distutils._msvccompiler import MSVCCompiler - tempdir = self.mkdtemp() - manifest = os.path.join(tempdir, 'manifest') - f = open(manifest, 'w') - try: - f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES) - finally: - f.close() - - compiler = MSVCCompiler() - compiler._remove_visual_c_ref(manifest) - - # see what we got - f = open(manifest) - try: - # removing trailing spaces - content = '\n'.join([line.rstrip() for line in f.readlines()]) - finally: - f.close() - - # makes sure the manifest was properly cleaned - self.assertEqual(content, _CLEANED_MANIFEST) - - def test_remove_entire_manifest(self): - from distutils._msvccompiler import MSVCCompiler - tempdir = self.mkdtemp() - manifest = os.path.join(tempdir, 'manifest') - f = open(manifest, 'w') - try: - f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE) - finally: - f.close() - - compiler = MSVCCompiler() - got = compiler._remove_visual_c_ref(manifest) - self.assertIsNone(got) - - def test_suite(): return unittest.makeSuite(msvccompilerTestCase) -- cgit v1.2.1 From 8434fc905458f1fa6cadf7c52550b9fac80e2b7d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 5 Aug 2015 11:48:14 -0700 Subject: Rebuild wininst-14.0[-amd64].exe with updated tools. --- command/wininst-14.0-amd64.exe | Bin 84480 -> 136192 bytes command/wininst-14.0.exe | Bin 75264 -> 129024 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-14.0-amd64.exe b/command/wininst-14.0-amd64.exe index 43b85b6d..7a5e78d5 100644 Binary files a/command/wininst-14.0-amd64.exe and b/command/wininst-14.0-amd64.exe differ diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe index 764524d7..cc43296b 100644 Binary files a/command/wininst-14.0.exe and b/command/wininst-14.0.exe differ -- cgit v1.2.1 From 94e5864e4ff57bfb82bca76fe8d00e15b646fe98 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 7 Aug 2015 19:48:03 -0700 Subject: Issue #4214: Remove ineffectual /pdb:none option from msvc9compiler.py --- msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 9688f200..a5a50100 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -413,7 +413,7 @@ class MSVCCompiler(CCompiler) : self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] if self.__version >= 7: self.ldflags_shared_debug = [ - '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG', '/pdb:None' + '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' ] self.ldflags_static = [ '/nologo'] -- cgit v1.2.1 From d7f14ca7b710fe301273909ea858fefbaa093e6c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2015 13:13:11 -0400 Subject: Use modern mechanism for test discovery --- tests/test_filelist.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index e82bc3d2..278809c0 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -7,7 +7,7 @@ from distutils.log import WARN from distutils.errors import DistutilsTemplateError from distutils.filelist import glob_to_re, translate_pattern, FileList -from test.support import captured_stdout, run_unittest +from test.support import captured_stdout from distutils.tests import support MANIFEST_IN = """\ @@ -292,8 +292,5 @@ class FileListTestCase(support.LoggingSilencer, self.assertWarnings() -def test_suite(): - return unittest.makeSuite(FileListTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() -- cgit v1.2.1 From 2822a45bc27876169fd57bad2fdf2d9d5ae2aede Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2015 13:22:56 -0400 Subject: Issue #12285: Add test capturing failure. --- tests/test_filelist.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 278809c0..6b3bde0d 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -6,7 +6,9 @@ from distutils import debug from distutils.log import WARN from distutils.errors import DistutilsTemplateError from distutils.filelist import glob_to_re, translate_pattern, FileList +from distutils import filelist +import test.support from test.support import captured_stdout from distutils.tests import support @@ -292,5 +294,13 @@ class FileListTestCase(support.LoggingSilencer, self.assertWarnings() +class FindAllTestCase(unittest.TestCase): + @test.support.skip_unless_symlink + def test_missing_symlink(self): + with test.support.temp_cwd(): + os.symlink('foo', 'bar') + self.assertEqual(filelist.findall(), []) + + if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From 3bf3f37dd03a1978f6c8386dd348f681241494a8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2015 13:26:48 -0400 Subject: Add another test capturing the basic discovery expectation. --- tests/test_filelist.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 6b3bde0d..45ff5653 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -301,6 +301,17 @@ class FindAllTestCase(unittest.TestCase): os.symlink('foo', 'bar') self.assertEqual(filelist.findall(), []) + def test_basic_discovery(self): + with test.support.temp_cwd(): + os.mkdir('foo') + file1 = os.path.join('foo', 'file1.txt') + test.support.create_empty_file(file1) + os.mkdir('bar') + file2 = os.path.join('bar', 'file2.txt') + test.support.create_empty_file(file2) + expected = [file1, file2] + self.assertEqual(filelist.findall(), expected) + if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From e54077ba1964fbb59f3f1df7b9290694a4dc964d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 19 Sep 2015 17:32:51 +0200 Subject: Add docstring and additional test revealing nuances of the implementation as found in setuptools. --- tests/test_filelist.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 45ff5653..571acdbc 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -302,6 +302,11 @@ class FindAllTestCase(unittest.TestCase): self.assertEqual(filelist.findall(), []) def test_basic_discovery(self): + """ + When findall is called with no parameters or with + '.' as the parameter, the dot should be omitted from + the results. + """ with test.support.temp_cwd(): os.mkdir('foo') file1 = os.path.join('foo', 'file1.txt') @@ -312,6 +317,17 @@ class FindAllTestCase(unittest.TestCase): expected = [file1, file2] self.assertEqual(filelist.findall(), expected) + def test_non_local_discovery(self): + """ + When findall is called with another path, the full + path name should be returned. + """ + with test.support.temp_dir() as temp_dir: + file1 = os.path.join(temp_dir, 'file1.txt') + test.support.create_empty_file(file1) + expected = [file1] + self.assertEqual(filelist.findall(temp_dir), expected) + if __name__ == "__main__": unittest.main() -- cgit v1.2.1 From 656ef5739b3f21070ed3a434ef4911a922b3cf5a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2015 14:05:58 -0400 Subject: Sort result to avoid spurious errors due to order. --- tests/test_filelist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 571acdbc..e719198c 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -314,8 +314,8 @@ class FindAllTestCase(unittest.TestCase): os.mkdir('bar') file2 = os.path.join('bar', 'file2.txt') test.support.create_empty_file(file2) - expected = [file1, file2] - self.assertEqual(filelist.findall(), expected) + expected = [file2, file1] + self.assertEqual(sorted(filelist.findall()), expected) def test_non_local_discovery(self): """ -- cgit v1.2.1 From 0846f06f326b9f369772b6fe1c980434bc9b9c1b Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 8 Sep 2015 21:39:01 -0700 Subject: Issue #25027: Reverts partial-static build options and adds vcruntime140.dll to Windows installation. --- _msvccompiler.py | 76 ++++++++++++++++++++++++++++++++++++++-------- tests/test_msvccompiler.py | 58 ++++++++++++++++++++++++++++++++--- 2 files changed, 116 insertions(+), 18 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index b344616e..82b78a0f 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -14,6 +14,8 @@ for older versions in distutils.msvc9compiler and distutils.msvccompiler. # ported to VS 2015 by Steve Dower import os +import shutil +import stat import subprocess from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ @@ -25,7 +27,7 @@ from distutils.util import get_platform import winreg from itertools import count -def _find_vcvarsall(): +def _find_vcvarsall(plat_spec): with winreg.OpenKeyEx( winreg.HKEY_LOCAL_MACHINE, r"Software\Microsoft\VisualStudio\SxS\VC7", @@ -33,7 +35,7 @@ def _find_vcvarsall(): ) as key: if not key: log.debug("Visual C++ is not registered") - return None + return None, None best_version = 0 best_dir = None @@ -51,14 +53,23 @@ def _find_vcvarsall(): best_version, best_dir = version, vc_dir if not best_version: log.debug("No suitable Visual C++ version found") - return None + return None, None vcvarsall = os.path.join(best_dir, "vcvarsall.bat") if not os.path.isfile(vcvarsall): log.debug("%s cannot be found", vcvarsall) - return None + return None, None - return vcvarsall + vcruntime = None + vcruntime_spec = _VCVARS_PLAT_TO_VCRUNTIME_REDIST.get(plat_spec) + if vcruntime_spec: + vcruntime = os.path.join(best_dir, + vcruntime_spec.format(best_version)) + if not os.path.isfile(vcruntime): + log.debug("%s cannot be found", vcruntime) + vcruntime = None + + return vcvarsall, vcruntime def _get_vc_env(plat_spec): if os.getenv("DISTUTILS_USE_SDK"): @@ -67,7 +78,7 @@ def _get_vc_env(plat_spec): for key, value in os.environ.items() } - vcvarsall = _find_vcvarsall() + vcvarsall, vcruntime = _find_vcvarsall(plat_spec) if not vcvarsall: raise DistutilsPlatformError("Unable to find vcvarsall.bat") @@ -83,12 +94,16 @@ def _get_vc_env(plat_spec): raise DistutilsPlatformError("Error executing {}" .format(exc.cmd)) - return { + env = { key.lower(): value for key, _, value in (line.partition('=') for line in out.splitlines()) if key and value } + + if vcruntime: + env['py_vcruntime_redist'] = vcruntime + return env def _find_exe(exe, paths=None): """Return path to an MSVC executable program. @@ -115,6 +130,20 @@ PLAT_TO_VCVARS = { 'win-amd64' : 'amd64', } +# A map keyed by get_platform() return values to the file under +# the VC install directory containing the vcruntime redistributable. +_VCVARS_PLAT_TO_VCRUNTIME_REDIST = { + 'x86' : 'redist\\x86\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll', + 'amd64' : 'redist\\x64\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll', + 'x86_amd64' : 'redist\\x64\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll', +} + +# A set containing the DLLs that are guaranteed to be available for +# all micro versions of this Python version. Known extension +# dependencies that are not in this set will be copied to the output +# path. +_BUNDLED_DLLS = frozenset(['vcruntime140.dll']) + class MSVCCompiler(CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, as defined by the CCompiler abstract class.""" @@ -189,6 +218,7 @@ class MSVCCompiler(CCompiler) : self.rc = _find_exe("rc.exe", paths) # resource compiler self.mc = _find_exe("mc.exe", paths) # message compiler self.mt = _find_exe("mt.exe", paths) # message compiler + self._vcruntime_redist = vc_env.get('py_vcruntime_redist', '') for dir in vc_env.get('include', '').split(os.pathsep): if dir: @@ -199,20 +229,26 @@ class MSVCCompiler(CCompiler) : self.add_library_dir(dir) self.preprocess_options = None - # Use /MT[d] to build statically, then switch from libucrt[d].lib to ucrt[d].lib + # If vcruntime_redist is available, link against it dynamically. Otherwise, + # use /MT[d] to build statically, then switch from libucrt[d].lib to ucrt[d].lib # later to dynamically link to ucrtbase but not vcruntime. self.compile_options = [ - '/nologo', '/Ox', '/MT', '/W3', '/GL', '/DNDEBUG' + '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG' ] + self.compile_options.append('/MD' if self._vcruntime_redist else '/MT') + self.compile_options_debug = [ - '/nologo', '/Od', '/MTd', '/Zi', '/W3', '/D_DEBUG' + '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' ] ldflags = [ - '/nologo', '/INCREMENTAL:NO', '/LTCG', '/nodefaultlib:libucrt.lib', 'ucrt.lib', + '/nologo', '/INCREMENTAL:NO', '/LTCG' ] + if not self._vcruntime_redist: + ldflags.extend(('/nodefaultlib:libucrt.lib', 'ucrt.lib')) + ldflags_debug = [ - '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL', '/nodefaultlib:libucrtd.lib', 'ucrtd.lib', + '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL' ] self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1'] @@ -446,15 +482,29 @@ class MSVCCompiler(CCompiler) : if extra_postargs: ld_args.extend(extra_postargs) - self.mkpath(os.path.dirname(output_filename)) + output_dir = os.path.dirname(os.path.abspath(output_filename)) + self.mkpath(output_dir) try: log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args)) self.spawn([self.linker] + ld_args) + self._copy_vcruntime(output_dir) except DistutilsExecError as msg: raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) + def _copy_vcruntime(self, output_dir): + vcruntime = self._vcruntime_redist + if not vcruntime or not os.path.isfile(vcruntime): + return + + if os.path.basename(vcruntime).lower() in _BUNDLED_DLLS: + return + + log.debug('Copying "%s"', vcruntime) + vcruntime = shutil.copy(vcruntime, output_dir) + os.chmod(vcruntime, stat.S_IWRITE) + def spawn(self, cmd): old_path = os.getenv('path') try: diff --git a/tests/test_msvccompiler.py b/tests/test_msvccompiler.py index 1f889079..0b8a69fa 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -3,6 +3,8 @@ import sys import unittest import os +import distutils._msvccompiler as _msvccompiler + from distutils.errors import DistutilsPlatformError from distutils.tests import support from test.support import run_unittest @@ -19,19 +21,65 @@ class msvccompilerTestCase(support.TempdirManager, # makes sure query_vcvarsall raises # a DistutilsPlatformError if the compiler # is not found - from distutils._msvccompiler import _get_vc_env - def _find_vcvarsall(): - return None + def _find_vcvarsall(plat_spec): + return None, None - import distutils._msvccompiler as _msvccompiler old_find_vcvarsall = _msvccompiler._find_vcvarsall _msvccompiler._find_vcvarsall = _find_vcvarsall try: - self.assertRaises(DistutilsPlatformError, _get_vc_env, + self.assertRaises(DistutilsPlatformError, + _msvccompiler._get_vc_env, 'wont find this version') finally: _msvccompiler._find_vcvarsall = old_find_vcvarsall + def test_compiler_options(self): + # suppress path to vcruntime from _find_vcvarsall to + # check that /MT is added to compile options + old_find_vcvarsall = _msvccompiler._find_vcvarsall + def _find_vcvarsall(plat_spec): + return old_find_vcvarsall(plat_spec)[0], None + _msvccompiler._find_vcvarsall = _find_vcvarsall + try: + compiler = _msvccompiler.MSVCCompiler() + compiler.initialize() + + self.assertIn('/MT', compiler.compile_options) + self.assertNotIn('/MD', compiler.compile_options) + finally: + _msvccompiler._find_vcvarsall = old_find_vcvarsall + + def test_vcruntime_copy(self): + # force path to a known file - it doesn't matter + # what we copy as long as its name is not in + # _msvccompiler._BUNDLED_DLLS + old_find_vcvarsall = _msvccompiler._find_vcvarsall + def _find_vcvarsall(plat_spec): + return old_find_vcvarsall(plat_spec)[0], __file__ + _msvccompiler._find_vcvarsall = _find_vcvarsall + try: + tempdir = self.mkdtemp() + compiler = _msvccompiler.MSVCCompiler() + compiler.initialize() + compiler._copy_vcruntime(tempdir) + + self.assertTrue(os.path.isfile(os.path.join( + tempdir, os.path.basename(__file__)))) + finally: + _msvccompiler._find_vcvarsall = old_find_vcvarsall + + def test_vcruntime_skip_copy(self): + tempdir = self.mkdtemp() + compiler = _msvccompiler.MSVCCompiler() + compiler.initialize() + dll = compiler._vcruntime_redist + self.assertTrue(os.path.isfile(dll)) + + compiler._copy_vcruntime(tempdir) + + self.assertFalse(os.path.isfile(os.path.join( + tempdir, os.path.basename(dll)))) + def test_suite(): return unittest.makeSuite(msvccompilerTestCase) -- cgit v1.2.1 From fd5d6fbed8f41d9ad678d2b2ace8c1d6564f00a3 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 8 Sep 2015 23:42:51 -0700 Subject: Moves distutils test import within skippable class. --- tests/test_msvccompiler.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_msvccompiler.py b/tests/test_msvccompiler.py index 0b8a69fa..874d6035 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -3,8 +3,6 @@ import sys import unittest import os -import distutils._msvccompiler as _msvccompiler - from distutils.errors import DistutilsPlatformError from distutils.tests import support from test.support import run_unittest @@ -18,6 +16,7 @@ class msvccompilerTestCase(support.TempdirManager, unittest.TestCase): def test_no_compiler(self): + import distutils._msvccompiler as _msvccompiler # makes sure query_vcvarsall raises # a DistutilsPlatformError if the compiler # is not found @@ -34,6 +33,7 @@ class msvccompilerTestCase(support.TempdirManager, _msvccompiler._find_vcvarsall = old_find_vcvarsall def test_compiler_options(self): + import distutils._msvccompiler as _msvccompiler # suppress path to vcruntime from _find_vcvarsall to # check that /MT is added to compile options old_find_vcvarsall = _msvccompiler._find_vcvarsall @@ -50,6 +50,7 @@ class msvccompilerTestCase(support.TempdirManager, _msvccompiler._find_vcvarsall = old_find_vcvarsall def test_vcruntime_copy(self): + import distutils._msvccompiler as _msvccompiler # force path to a known file - it doesn't matter # what we copy as long as its name is not in # _msvccompiler._BUNDLED_DLLS @@ -69,6 +70,8 @@ class msvccompilerTestCase(support.TempdirManager, _msvccompiler._find_vcvarsall = old_find_vcvarsall def test_vcruntime_skip_copy(self): + import distutils._msvccompiler as _msvccompiler + tempdir = self.mkdtemp() compiler = _msvccompiler.MSVCCompiler() compiler.initialize() -- cgit v1.2.1 From 465364f3af646e6285e2c6fd461fd44113164ff7 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Wed, 9 Sep 2015 06:54:57 -0700 Subject: Whitespace fixes to make the commit hook on hg.python.org happy. --- _msvccompiler.py | 4 ++-- tests/test_msvccompiler.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index 82b78a0f..03a5f10e 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -100,7 +100,7 @@ def _get_vc_env(plat_spec): (line.partition('=') for line in out.splitlines()) if key and value } - + if vcruntime: env['py_vcruntime_redist'] = vcruntime return env @@ -236,7 +236,7 @@ class MSVCCompiler(CCompiler) : '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG' ] self.compile_options.append('/MD' if self._vcruntime_redist else '/MT') - + self.compile_options_debug = [ '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' ] diff --git a/tests/test_msvccompiler.py b/tests/test_msvccompiler.py index 874d6035..c4d911ff 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -77,7 +77,7 @@ class msvccompilerTestCase(support.TempdirManager, compiler.initialize() dll = compiler._vcruntime_redist self.assertTrue(os.path.isfile(dll)) - + compiler._copy_vcruntime(tempdir) self.assertFalse(os.path.isfile(os.path.join( -- cgit v1.2.1 From 2509f6beef005a88eede79503086f8c0ba1b484c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 12 Sep 2015 17:20:47 -0700 Subject: fix name of argument in docstring and the docs (closes #25076) Patch by TAKASE Arihiro. --- ccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 911e84dd..e93a2736 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -752,7 +752,7 @@ class CCompiler: raise NotImplementedError def library_option(self, lib): - """Return the compiler option to add 'dir' to the list of libraries + """Return the compiler option to add 'lib' to the list of libraries linked into the shared library or executable. """ raise NotImplementedError -- cgit v1.2.1 From b7236c18f5a3b4f5f44d97eb67ce9e3cc56715f9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 19 Sep 2015 18:12:15 +0200 Subject: Issue #12285: Replace implementation of findall with implementation from Setuptools 7ce820d524db. --- filelist.py | 48 +++++++++++++++++++++--------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/filelist.py b/filelist.py index db3f7a96..6522e69f 100644 --- a/filelist.py +++ b/filelist.py @@ -6,6 +6,7 @@ and building lists of files. import os, re import fnmatch +import functools from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsInternalError from distutils import log @@ -242,35 +243,28 @@ class FileList: # ---------------------------------------------------------------------- # Utility functions +def _find_all_simple(path): + """ + Find all files under 'path' + """ + results = ( + os.path.join(base, file) + for base, dirs, files in os.walk(path, followlinks=True) + for file in files + ) + return filter(os.path.isfile, results) + + def findall(dir=os.curdir): - """Find all files under 'dir' and return the list of full filenames - (relative to 'dir'). """ - from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK - - list = [] - stack = [dir] - pop = stack.pop - push = stack.append - - while stack: - dir = pop() - names = os.listdir(dir) - - for name in names: - if dir != os.curdir: # avoid the dreaded "./" syndrome - fullname = os.path.join(dir, name) - else: - fullname = name - - # Avoid excess stat calls -- just one will do, thank you! - stat = os.stat(fullname) - mode = stat[ST_MODE] - if S_ISREG(mode): - list.append(fullname) - elif S_ISDIR(mode) and not S_ISLNK(mode): - push(fullname) - return list + Find all files under 'dir' and return the list of full filenames. + Unless dir is '.', return full filenames with dir prepended. + """ + files = _find_all_simple(dir) + if dir == os.curdir: + make_rel = functools.partial(os.path.relpath, start=dir) + files = map(make_rel, files) + return list(files) def glob_to_re(pattern): -- cgit v1.2.1 From f8a519501546f5d5f81ad00ecc7d160bd72e748e Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 5 Oct 2015 10:35:00 -0700 Subject: Issue #25316: distutils raises OSError instead of DistutilsPlatformError when MSVC is not installed. --- _msvccompiler.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index 03a5f10e..10a9ffda 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -28,15 +28,17 @@ import winreg from itertools import count def _find_vcvarsall(plat_spec): - with winreg.OpenKeyEx( - winreg.HKEY_LOCAL_MACHINE, - r"Software\Microsoft\VisualStudio\SxS\VC7", - access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY - ) as key: - if not key: - log.debug("Visual C++ is not registered") - return None, None + try: + key = winreg.OpenKeyEx( + winreg.HKEY_LOCAL_MACHINE, + r"Software\Microsoft\VisualStudio\SxS\VC7", + access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY + ) + except OSError: + log.debug("Visual C++ is not registered") + return None, None + with key: best_version = 0 best_dir = None for i in count(): -- cgit v1.2.1 From f06fb88362616b452cf6da3de78eb18a759526a5 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 2 Nov 2015 03:37:02 +0000 Subject: Issue #25523: Correct "a" article to "an" article This changes the main documentation, doc strings, source code comments, and a couple error messages in the test suite. In some cases the word was removed or edited some other way to fix the grammar. --- cygwinccompiler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index d28b1b36..c879646c 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -10,9 +10,9 @@ cygwin in no-cygwin mode). # # * if you use a msvc compiled python version (1.5.2) # 1. you have to insert a __GNUC__ section in its config.h -# 2. you have to generate a import library for its dll +# 2. you have to generate an import library for its dll # - create a def-file for python??.dll -# - create a import library using +# - create an import library using # dlltool --dllname python15.dll --def python15.def \ # --output-lib libpython15.a # @@ -318,7 +318,7 @@ class Mingw32CCompiler(CygwinCCompiler): self.dll_libraries = get_msvcr() # Because these compilers aren't configured in Python's pyconfig.h file by -# default, we should at least warn the user if he is using a unmodified +# default, we should at least warn the user if he is using an unmodified # version. CONFIG_H_OK = "ok" -- cgit v1.2.1 From 1b3e7be2deb5caf890dc7a53896357e4b938979e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 12 Nov 2015 13:15:41 +0200 Subject: Restore old distutils logging threshold after running test_log. --- tests/test_log.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_log.py b/tests/test_log.py index ce66ee51..0c2ad7a4 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -14,8 +14,8 @@ class TestLog(unittest.TestCase): # error handler) old_stdout = sys.stdout old_stderr = sys.stderr + old_threshold = log.set_threshold(log.DEBUG) try: - log.set_threshold(log.DEBUG) with NamedTemporaryFile(mode="w+", encoding='ascii') as stdout, \ NamedTemporaryFile(mode="w+", encoding='ascii') as stderr: sys.stdout = stdout @@ -27,6 +27,7 @@ class TestLog(unittest.TestCase): stderr.seek(0) self.assertEqual(stderr.read().rstrip(), "fatal:\\xe9") finally: + log.set_threshold(old_threshold) sys.stdout = old_stdout sys.stderr = old_stderr -- cgit v1.2.1 From 4e0ac6d444c79d2858a6d3cb8b7b523ccf3f54e3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 12 Nov 2015 19:46:23 +0200 Subject: Issue #25607: Restore old distutils logging threshold after running tests that parse command line arguments. --- tests/test_core.py | 2 ++ tests/test_dist.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index 41321f7d..654227ca 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -9,6 +9,7 @@ import test.support from test.support import captured_stdout, run_unittest import unittest from distutils.tests import support +from distutils import log # setup script that uses __file__ setup_using___file__ = """\ @@ -36,6 +37,7 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): self.old_stdout = sys.stdout self.cleanup_testfn() self.old_argv = sys.argv, sys.argv[:] + self.addCleanup(log.set_threshold, log._global_log.threshold) def tearDown(self): sys.stdout = self.old_stdout diff --git a/tests/test_dist.py b/tests/test_dist.py index b7fd3fbf..1f104cef 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -13,6 +13,7 @@ from distutils.cmd import Command from test.support import TESTFN, captured_stdout, run_unittest from distutils.tests import support +from distutils import log class test_dist(Command): @@ -405,6 +406,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_show_help(self): # smoke test, just makes sure some help is displayed + self.addCleanup(log.set_threshold, log._global_log.threshold) dist = Distribution() sys.argv = [] dist.help = 1 -- cgit v1.2.1 From bafe9fba05aefdd679770d54e3a74fc0cab538d5 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 16 Jan 2016 12:39:10 -0800 Subject: Issue #25850: Use cross-compilation by default for 64-bit Windows. --- _msvccompiler.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index 10a9ffda..d0ba7d6d 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -125,11 +125,11 @@ def _find_exe(exe, paths=None): return exe # A map keyed by get_platform() return values to values accepted by -# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is -# the param to cross-compile on x86 targetting amd64.) +# 'vcvarsall.bat'. Always cross-compile from x86 to work with the +# lighter-weight MSVC installs that do not include native 64-bit tools. PLAT_TO_VCVARS = { 'win32' : 'x86', - 'win-amd64' : 'amd64', + 'win-amd64' : 'x86_amd64', } # A map keyed by get_platform() return values to the file under @@ -193,19 +193,8 @@ class MSVCCompiler(CCompiler) : raise DistutilsPlatformError("--plat-name must be one of {}" .format(tuple(PLAT_TO_VCVARS))) - # On x86, 'vcvarsall.bat amd64' creates an env that doesn't work; - # to cross compile, you use 'x86_amd64'. - # On AMD64, 'vcvarsall.bat amd64' is a native build env; to cross - # compile use 'x86' (ie, it runs the x86 compiler directly) - if plat_name == get_platform() or plat_name == 'win32': - # native build or cross-compile to win32 - plat_spec = PLAT_TO_VCVARS[plat_name] - else: - # cross compile from win32 -> some 64bit - plat_spec = '{}_{}'.format( - PLAT_TO_VCVARS[get_platform()], - PLAT_TO_VCVARS[plat_name] - ) + # Get the vcvarsall.bat spec for the requested platform. + plat_spec = PLAT_TO_VCVARS[plat_name] vc_env = _get_vc_env(plat_spec) if not vc_env: -- cgit v1.2.1 From 7a7aaaf398456bb31074f4446d8c1290d9695019 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 16 Jan 2016 13:54:53 -0800 Subject: Issue #26071: bdist_wininst created binaries fail to start and find 32bit Python --- command/wininst-14.0-amd64.exe | Bin 136192 -> 589824 bytes command/wininst-14.0.exe | Bin 129024 -> 460288 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-14.0-amd64.exe b/command/wininst-14.0-amd64.exe index 7a5e78d5..22299543 100644 Binary files a/command/wininst-14.0-amd64.exe and b/command/wininst-14.0-amd64.exe differ diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe index cc43296b..0dac1103 100644 Binary files a/command/wininst-14.0.exe and b/command/wininst-14.0.exe differ -- cgit v1.2.1 From 34941986add5899b43614e3cfc0f590eb9d1ca20 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 18 Jan 2016 12:15:08 +0100 Subject: subprocess._optim_args_from_interpreter_flags() Issue #26100: * Add subprocess._optim_args_from_interpreter_flags() * Add test.support.optim_args_from_interpreter_flags() * Use new functions in distutils, test_cmd_line_script, test_compileall and test_inspect The change enables test_details() test of test_inspect when -O or -OO command line option is used. --- util.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/util.py b/util.py index e423325d..fdcf6fab 100644 --- a/util.py +++ b/util.py @@ -7,8 +7,8 @@ one of the other *util.py modules. import os import re import importlib.util -import sys import string +import sys from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer from distutils.spawn import spawn @@ -350,6 +350,11 @@ def byte_compile (py_files, generated in indirect mode; unless you know what you're doing, leave it set to None. """ + + # Late import to fix a bootstrap issue: _posixsubprocess is built by + # setup.py, but setup.py uses distutils. + import subprocess + # nothing is done if sys.dont_write_bytecode is True if sys.dont_write_bytecode: raise DistutilsByteCompileError('byte-compiling is disabled.') @@ -412,11 +417,9 @@ byte_compile(files, optimize=%r, force=%r, script.close() - cmd = [sys.executable, script_name] - if optimize == 1: - cmd.insert(1, "-O") - elif optimize == 2: - cmd.insert(1, "-OO") + cmd = [sys.executable] + cmd.extend(subprocess._optim_args_from_interpreter_flags()) + cmd.append(script_name) spawn(cmd, dry_run=dry_run) execute(os.remove, (script_name,), "removing %s" % script_name, dry_run=dry_run) -- cgit v1.2.1 From 605d57a077d65dba6bc554426e50fe2ba04502af Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 11 Feb 2016 13:10:36 +0200 Subject: Issue #25985: sys.version_info is now used instead of sys.version to format short Python version. --- command/bdist_msi.py | 2 +- command/bdist_wininst.py | 2 +- command/build.py | 4 ++-- command/install.py | 4 ++-- command/install_egg_info.py | 4 ++-- sysconfig.py | 2 +- tests/test_build.py | 5 +++-- 7 files changed, 12 insertions(+), 11 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index b3cfe9ce..f6c21aee 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -199,7 +199,7 @@ class bdist_msi(Command): target_version = self.target_version if not target_version: assert self.skip_build, "Should have already checked this" - target_version = sys.version[0:3] + target_version = '%d.%d' % sys.version_info[:2] plat_specifier = ".%s-%s" % (self.plat_name, target_version) build = self.get_finalized_command('build') build.build_lib = os.path.join(build.build_base, diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 0c0e2c1a..d3e1d3af 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -141,7 +141,7 @@ class bdist_wininst(Command): target_version = self.target_version if not target_version: assert self.skip_build, "Should have already checked this" - target_version = sys.version[0:3] + target_version = '%d.%d' % sys.version_info[:2] plat_specifier = ".%s-%s" % (self.plat_name, target_version) build = self.get_finalized_command('build') build.build_lib = os.path.join(build.build_base, diff --git a/command/build.py b/command/build.py index 337dd0bf..c6f52e61 100644 --- a/command/build.py +++ b/command/build.py @@ -81,7 +81,7 @@ class build(Command): "--plat-name only supported on Windows (try " "using './configure --help' on your platform)") - plat_specifier = ".%s-%s" % (self.plat_name, sys.version[0:3]) + plat_specifier = ".%s-%d.%d" % (self.plat_name, *sys.version_info[:2]) # Make it so Python 2.x and Python 2.x with --with-pydebug don't # share the same build directories. Doing so confuses the build @@ -114,7 +114,7 @@ class build(Command): 'temp' + plat_specifier) if self.build_scripts is None: self.build_scripts = os.path.join(self.build_base, - 'scripts-' + sys.version[0:3]) + 'scripts-%d.%d' % sys.version_info[:2]) if self.executable is None: self.executable = os.path.normpath(sys.executable) diff --git a/command/install.py b/command/install.py index 67db007a..9474e9c5 100644 --- a/command/install.py +++ b/command/install.py @@ -290,8 +290,8 @@ class install(Command): 'dist_version': self.distribution.get_version(), 'dist_fullname': self.distribution.get_fullname(), 'py_version': py_version, - 'py_version_short': py_version[0:3], - 'py_version_nodot': py_version[0] + py_version[2], + 'py_version_short': '%d.%d' % sys.version_info[:2], + 'py_version_nodot': '%d%d' % sys.version_info[:2], 'sys_prefix': prefix, 'prefix': prefix, 'sys_exec_prefix': exec_prefix, diff --git a/command/install_egg_info.py b/command/install_egg_info.py index c2a7d649..0ddc7367 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -21,10 +21,10 @@ class install_egg_info(Command): def finalize_options(self): self.set_undefined_options('install_lib',('install_dir','install_dir')) - basename = "%s-%s-py%s.egg-info" % ( + basename = "%s-%s-py%d.%d.egg-info" % ( to_filename(safe_name(self.distribution.get_name())), to_filename(safe_version(self.distribution.get_version())), - sys.version[:3] + *sys.version_info[:2] ) self.target = os.path.join(self.install_dir, basename) self.outputs = [self.target] diff --git a/sysconfig.py b/sysconfig.py index 573724dd..d203f8e4 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -70,7 +70,7 @@ def get_python_version(): leaving off the patchlevel. Sample return values could be '1.5' or '2.2'. """ - return sys.version[:3] + return '%d.%d' % sys.version_info[:2] def get_python_inc(plat_specific=0, prefix=None): diff --git a/tests/test_build.py b/tests/test_build.py index 3391f36d..b020a5ba 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -27,7 +27,7 @@ class BuildTestCase(support.TempdirManager, # build_platlib is 'build/lib.platform-x.x[-pydebug]' # examples: # build/lib.macosx-10.3-i386-2.7 - plat_spec = '.%s-%s' % (cmd.plat_name, sys.version[0:3]) + plat_spec = '.%s-%d.%d' % (cmd.plat_name, *sys.version_info[:2]) if hasattr(sys, 'gettotalrefcount'): self.assertTrue(cmd.build_platlib.endswith('-pydebug')) plat_spec += '-pydebug' @@ -42,7 +42,8 @@ class BuildTestCase(support.TempdirManager, self.assertEqual(cmd.build_temp, wanted) # build_scripts is build/scripts-x.x - wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3]) + wanted = os.path.join(cmd.build_base, + 'scripts-%d.%d' % sys.version_info[:2]) self.assertEqual(cmd.build_scripts, wanted) # executable is os.path.normpath(sys.executable) -- cgit v1.2.1 From ef3d89d581b83fa214d5563d4e3a9e7c901746ab Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Thu, 25 Feb 2016 00:56:38 +1100 Subject: Issue #25136: Support Apple Xcode 7's new textual SDK stub libraries. As of Xcode 7, SDKs for Apple platforms now include textual-format stub libraries whose file names have a .tbd extension rather than the standard OS X .dylib extension. The Apple compiler tool chain handles these stub libraries transparently and the installed system shared libraries are still .dylibs. However, the new stub libraries cause problems for third-party programs that support building with Apple SDKs and make build-time decisions based on the presence or paths of system-supplied shared libraries in the SDK. In particular, building Python itself with an SDK fails to find system-supplied libraries during setup.py's build of standard library extension modules. The solution is to have find_library_file() in Distutils search for .tbd files, along with the existing types (.a, .so, and .dylib). Patch by Tim Smith. --- ccompiler.py | 4 ++-- unixccompiler.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 82fd7d5c..b71d1d39 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -875,9 +875,9 @@ main (int argc, char **argv) { def library_filename(self, libname, lib_type='static', # or 'shared' strip_dir=0, output_dir=''): assert output_dir is not None - if lib_type not in ("static", "shared", "dylib"): + if lib_type not in ("static", "shared", "dylib", "xcode_stub"): raise ValueError( - "'lib_type' must be \"static\", \"shared\" or \"dylib\"") + "'lib_type' must be \"static\", \"shared\", \"dylib\", or \"xcode_stub\"") fmt = getattr(self, lib_type + "_lib_format") ext = getattr(self, lib_type + "_lib_extension") diff --git a/unixccompiler.py b/unixccompiler.py index 094a2f0b..92d14dfe 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -76,7 +76,9 @@ class UnixCCompiler(CCompiler): static_lib_extension = ".a" shared_lib_extension = ".so" dylib_lib_extension = ".dylib" + xcode_stub_lib_extension = ".tbd" static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" + xcode_stub_lib_format = dylib_lib_format if sys.platform == "cygwin": exe_extension = ".exe" @@ -255,12 +257,28 @@ class UnixCCompiler(CCompiler): def find_library_file(self, dirs, lib, debug=0): shared_f = self.library_filename(lib, lib_type='shared') dylib_f = self.library_filename(lib, lib_type='dylib') + xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub') static_f = self.library_filename(lib, lib_type='static') if sys.platform == 'darwin': # On OSX users can specify an alternate SDK using # '-isysroot', calculate the SDK root if it is specified # (and use it further on) + # + # Note that, as of Xcode 7, Apple SDKs may contain textual stub + # libraries with .tbd extensions rather than the normal .dylib + # shared libraries installed in /. The Apple compiler tool + # chain handles this transparently but it can cause problems + # for programs that are being built with an SDK and searching + # for specific libraries. Callers of find_library_file need to + # keep in mind that the base filename of the returned SDK library + # file might have a different extension from that of the library + # file installed on the running system, for example: + # /Applications/Xcode.app/Contents/Developer/Platforms/ + # MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/ + # usr/lib/libedit.tbd + # vs + # /usr/lib/libedit.dylib cflags = sysconfig.get_config_var('CFLAGS') m = re.search(r'-isysroot\s+(\S+)', cflags) if m is None: @@ -274,6 +292,7 @@ class UnixCCompiler(CCompiler): shared = os.path.join(dir, shared_f) dylib = os.path.join(dir, dylib_f) static = os.path.join(dir, static_f) + xcode_stub = os.path.join(dir, xcode_stub_f) if sys.platform == 'darwin' and ( dir.startswith('/System/') or ( @@ -282,6 +301,7 @@ class UnixCCompiler(CCompiler): shared = os.path.join(sysroot, dir[1:], shared_f) dylib = os.path.join(sysroot, dir[1:], dylib_f) static = os.path.join(sysroot, dir[1:], static_f) + xcode_stub = os.path.join(sysroot, dir[1:], xcode_stub_f) # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm @@ -289,6 +309,8 @@ class UnixCCompiler(CCompiler): # ignoring even GCC's "-static" option. So sue me. if os.path.exists(dylib): return dylib + elif os.path.exists(xcode_stub): + return xcode_stub elif os.path.exists(shared): return shared elif os.path.exists(static): -- cgit v1.2.1 From 39b80901d20354b093e8220c137ce971c35c20c5 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 24 Apr 2016 01:55:09 +0300 Subject: Issue #26089: Remove duplicate field 'license' from DistributionMetadata It was renamed to 'license' in 178d19cff163. Patch by Augustin Laville. --- dist.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dist.py b/dist.py index ffb33ff6..62a24516 100644 --- a/dist.py +++ b/dist.py @@ -1018,8 +1018,7 @@ class DistributionMetadata: "maintainer", "maintainer_email", "url", "license", "description", "long_description", "keywords", "platforms", "fullname", "contact", - "contact_email", "license", "classifiers", - "download_url", + "contact_email", "classifiers", "download_url", # PEP 314 "provides", "requires", "obsoletes", ) -- cgit v1.2.1 From 95f6afb74ad25d2b616ed44499035595fe945fc5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 24 Apr 2016 13:25:01 +0300 Subject: Issue #23277: Remove more unused sys and os imports. --- tests/test_clean.py | 1 - tests/test_config.py | 1 - tests/test_install_data.py | 1 - tests/test_install_headers.py | 1 - tests/test_unixccompiler.py | 1 - 5 files changed, 5 deletions(-) diff --git a/tests/test_clean.py b/tests/test_clean.py index b64f300a..df88ec1e 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -1,5 +1,4 @@ """Tests for distutils.command.clean.""" -import sys import os import unittest import getpass diff --git a/tests/test_config.py b/tests/test_config.py index 4de825a8..0929f8a4 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,5 +1,4 @@ """Tests for distutils.pypirc.pypirc.""" -import sys import os import unittest import tempfile diff --git a/tests/test_install_data.py b/tests/test_install_data.py index 4d8c00ac..d73624d0 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -1,5 +1,4 @@ """Tests for distutils.command.install_data.""" -import sys import os import unittest import getpass diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index d953157b..d9ed6b7f 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -1,5 +1,4 @@ """Tests for distutils.command.install_headers.""" -import sys import os import unittest import getpass diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 3d14e12a..7c95be56 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -1,5 +1,4 @@ """Tests for distutils.unixccompiler.""" -import os import sys import unittest from test.support import EnvironmentVarGuard, run_unittest -- cgit v1.2.1 From 905adfca47183d923e35135c4795a3b0439c6104 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 24 Apr 2016 21:41:02 +0300 Subject: Issue #23277: Remove unused imports in tests. --- tests/test_bdist_rpm.py | 4 ---- tests/test_build_ext.py | 1 - tests/test_clean.py | 1 - tests/test_config.py | 1 - tests/test_cygwinccompiler.py | 3 +-- tests/test_dep_util.py | 1 - tests/test_file_util.py | 1 - tests/test_install_data.py | 1 - tests/test_install_headers.py | 1 - tests/test_spawn.py | 5 ++--- 10 files changed, 3 insertions(+), 16 deletions(-) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 25c14abd..c1a2a041 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -3,16 +3,12 @@ import unittest import sys import os -import tempfile -import shutil from test.support import run_unittest from distutils.core import Distribution from distutils.command.bdist_rpm import bdist_rpm from distutils.tests import support from distutils.spawn import find_executable -from distutils import spawn -from distutils.errors import DistutilsExecError SETUP_PY = """\ from distutils.core import setup diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 366ffbec..3d84f8b1 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -166,7 +166,6 @@ class BuildExtTestCase(TempdirManager, cmd = self.build_ext(dist) cmd.finalize_options() - from distutils import sysconfig py_include = sysconfig.get_python_inc() self.assertIn(py_include, cmd.include_dirs) diff --git a/tests/test_clean.py b/tests/test_clean.py index df88ec1e..c605afd8 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -1,7 +1,6 @@ """Tests for distutils.command.clean.""" import os import unittest -import getpass from distutils.command.clean import clean from distutils.tests import support diff --git a/tests/test_config.py b/tests/test_config.py index 0929f8a4..d91dedd3 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,7 +1,6 @@ """Tests for distutils.pypirc.pypirc.""" import os import unittest -import tempfile from distutils.core import PyPIRCCommand from distutils.core import Distribution diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py index 85692167..9dc869de 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -3,11 +3,10 @@ import unittest import sys import os from io import BytesIO -import subprocess from test.support import run_unittest from distutils import cygwinccompiler -from distutils.cygwinccompiler import (CygwinCCompiler, check_config_h, +from distutils.cygwinccompiler import (check_config_h, CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_UNCERTAIN, get_versions, get_msvcr) diff --git a/tests/test_dep_util.py b/tests/test_dep_util.py index 3e1c3668..c6fae39c 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -1,7 +1,6 @@ """Tests for distutils.dep_util.""" import unittest import os -import time from distutils.dep_util import newer, newer_pairwise, newer_group from distutils.errors import DistutilsFileError diff --git a/tests/test_file_util.py b/tests/test_file_util.py index a6d04f06..03040afc 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -1,7 +1,6 @@ """Tests for distutils.file_util.""" import unittest import os -import shutil import errno from unittest.mock import patch diff --git a/tests/test_install_data.py b/tests/test_install_data.py index d73624d0..32ab296a 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -1,7 +1,6 @@ """Tests for distutils.command.install_data.""" import os import unittest -import getpass from distutils.command.install_data import install_data from distutils.tests import support diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py index d9ed6b7f..2217b321 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -1,7 +1,6 @@ """Tests for distutils.command.install_headers.""" import os import unittest -import getpass from distutils.command.install_headers import install_headers from distutils.tests import support diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 6c7eb20c..f507ef77 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -1,11 +1,10 @@ """Tests for distutils.spawn.""" import unittest import os -import time -from test.support import captured_stdout, run_unittest +from test.support import run_unittest from distutils.spawn import _nt_quote_args -from distutils.spawn import spawn, find_executable +from distutils.spawn import spawn from distutils.errors import DistutilsExecError from distutils.tests import support -- cgit v1.2.1 From 1746f3caf7a6a71c04f79da2b89c888cb5daadec Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 25 Apr 2016 00:12:32 +0300 Subject: Removed unused imports. --- command/config.py | 2 +- command/register.py | 2 +- command/sdist.py | 1 - extension.py | 1 - text_file.py | 2 +- 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/command/config.py b/command/config.py index 847e8581..b1fd09e0 100644 --- a/command/config.py +++ b/command/config.py @@ -9,7 +9,7 @@ configure-like tasks: "try to compile this C code", or "figure out where this header file lives". """ -import sys, os, re +import os, re from distutils.core import Command from distutils.errors import DistutilsExecError diff --git a/command/register.py b/command/register.py index b49f86fe..a3e0893a 100644 --- a/command/register.py +++ b/command/register.py @@ -5,7 +5,7 @@ Implements the Distutils 'register' command (register with the repository). # created 2002/10/21, Richard Jones -import os, string, getpass +import getpass import io import urllib.parse, urllib.request from warnings import warn diff --git a/command/sdist.py b/command/sdist.py index 7ea3d5fa..35a06eb0 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -3,7 +3,6 @@ Implements the Distutils 'sdist' command (create a source distribution).""" import os -import string import sys from types import * from glob import glob diff --git a/extension.py b/extension.py index 7efbb74f..c507da36 100644 --- a/extension.py +++ b/extension.py @@ -4,7 +4,6 @@ Provides the Extension class, used to describe C/C++ extension modules in setup scripts.""" import os -import sys import warnings # This class is really only used by the "build_ext" command, so it might diff --git a/text_file.py b/text_file.py index 478336f0..93abad38 100644 --- a/text_file.py +++ b/text_file.py @@ -4,7 +4,7 @@ provides the TextFile class, which gives an interface to text files that (optionally) takes care of stripping comments, ignoring blank lines, and joining lines with backslashes.""" -import sys, os, io +import sys, io class TextFile: -- cgit v1.2.1 From a1b5cdeacee878322b0aa52af208a2f826b4b800 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 4 May 2016 11:57:32 -0400 Subject: Issue #20120: Use RawConfigParser for .pypirc parsing, removing support for interpolation unintentionally added with move to Python 3. Behavior no longer does any interpolation in .pypirc files, matching behavior in Python 2.7 and Setuptools 19.0. --- config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.py b/config.py index 382aca8f..f0c73731 100644 --- a/config.py +++ b/config.py @@ -4,7 +4,7 @@ Provides the PyPIRCCommand class, the base class for the command classes that uses .pypirc in the distutils.command package. """ import os -from configparser import ConfigParser +from configparser import RawConfigParser from distutils.cmd import Command @@ -53,7 +53,7 @@ class PyPIRCCommand(Command): repository = self.repository or self.DEFAULT_REPOSITORY realm = self.realm or self.DEFAULT_REALM - config = ConfigParser() + config = RawConfigParser() config.read(rc) sections = config.sections() if 'distutils' in sections: -- cgit v1.2.1 From 30a0d1c5414ec93ffd4f83cfcc67d426847ce30d Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Thu, 26 May 2016 05:35:26 +0000 Subject: Issue #27076: Doc, comment and tests spelling fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most fixes to Doc/ and Lib/ directories by Ville Skyttä. --- msvc9compiler.py | 2 +- tests/test_unixccompiler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index da4b21d2..0b1fd19f 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -51,7 +51,7 @@ else: # A map keyed by get_platform() return values to values accepted by # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is -# the param to cross-compile on x86 targetting amd64.) +# the param to cross-compile on x86 targeting amd64.) PLAT_TO_VCVARS = { 'win32' : 'x86', 'win-amd64' : 'amd64', diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index 3d14e12a..e171ee9c 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -127,7 +127,7 @@ class UnixCCompilerTestCase(unittest.TestCase): self.assertEqual(self.cc.linker_so[0], 'my_cc') @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') - def test_osx_explict_ldshared(self): + def test_osx_explicit_ldshared(self): # Issue #18080: # ensure that setting CC env variable does not change # explicit LDSHARED setting for linker -- cgit v1.2.1 From 3f054be58270984d4c8e5b1e499f573dbae02975 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Thu, 2 Jun 2016 10:07:09 +0000 Subject: Issue #27171: Fix typos in documentation, comments, and test function names --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 366ffbec..8f62b1a8 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -279,7 +279,7 @@ class BuildExtTestCase(TempdirManager, def test_compiler_option(self): # cmd.compiler is an option and - # should not be overriden by a compiler instance + # should not be overridden by a compiler instance # when the command is run dist = Distribution() cmd = self.build_ext(dist) -- cgit v1.2.1 From 29389166af9931b6a533a60556d9c6e6929b71b1 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Thu, 2 Jun 2016 13:45:53 -0700 Subject: Issue #21776: distutils.upload now correctly handles HTTPError Initial patch by Claudiu Popa. --- command/upload.py | 14 +++++++------- tests/test_upload.py | 31 ++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/command/upload.py b/command/upload.py index 1c4fc48a..0afcbf23 100644 --- a/command/upload.py +++ b/command/upload.py @@ -181,21 +181,21 @@ class upload(PyPIRCCommand): result = urlopen(request) status = result.getcode() reason = result.msg - except OSError as e: - self.announce(str(e), log.ERROR) - raise except HTTPError as e: status = e.code reason = e.msg + except OSError as e: + self.announce(str(e), log.ERROR) + raise if status == 200: self.announce('Server response (%s): %s' % (status, reason), log.INFO) + if self.show_response: + text = self._read_pypi_response(result) + msg = '\n'.join(('-' * 75, text, '-' * 75)) + self.announce(msg, log.INFO) else: msg = 'Upload failed (%s): %s' % (status, reason) self.announce(msg, log.ERROR) raise DistutilsError(msg) - if self.show_response: - text = self._read_pypi_response(result) - msg = '\n'.join(('-' * 75, text, '-' * 75)) - self.announce(msg, log.INFO) diff --git a/tests/test_upload.py b/tests/test_upload.py index dccaf77e..19193d5b 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -1,13 +1,16 @@ """Tests for distutils.command.upload.""" import os import unittest +import unittest.mock as mock +from urllib.request import HTTPError + from test.support import run_unittest from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution from distutils.errors import DistutilsError -from distutils.log import INFO +from distutils.log import ERROR, INFO from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase @@ -144,6 +147,32 @@ class uploadTestCase(PyPIRCCommandTestCase): self.next_code = 404 self.assertRaises(DistutilsError, self.test_upload) + def test_wrong_exception_order(self): + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path) + dist_files = [('xxx', '2.6', path)] # command, pyversion, filename + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) + + pkg_dir, dist = self.create_dist(dist_files=dist_files) + tests = [ + (OSError('oserror'), 'oserror', OSError), + (HTTPError('url', 400, 'httperror', {}, None), + 'Upload failed (400): httperror', DistutilsError), + ] + for exception, expected, raised_exception in tests: + with self.subTest(exception=type(exception).__name__): + with mock.patch('distutils.command.upload.urlopen', + new=mock.Mock(side_effect=exception)): + with self.assertRaises(raised_exception): + cmd = upload(dist) + cmd.ensure_finalized() + cmd.run() + results = self.get_logs(ERROR) + self.assertIn(expected, results[-1]) + self.clear_logs() + + def test_suite(): return unittest.makeSuite(uploadTestCase) -- cgit v1.2.1 From f1c18134f23f8ad7cbf624097e242939bb540fa2 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Sun, 5 Jun 2016 00:41:58 +0200 Subject: - Issue #26884: Fix linking extension modules for cross builds. Patch by Xavier de Gaye. --- command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index d4cb11e6..f03a4e31 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -748,7 +748,7 @@ class build_ext(Command): if sysconfig.get_config_var('Py_ENABLE_SHARED'): pythonlib = 'python{}.{}{}'.format( sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff, - sys.abiflags) + sysconfig.get_config_var('ABIFLAGS')) return ext.libraries + [pythonlib] else: return ext.libraries -- cgit v1.2.1 From 5b8e477917ada86de6f23ffe5ba9831b258e9329 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Sun, 5 Jun 2016 01:17:57 +0200 Subject: - Issue #21272: Use _sysconfigdata.py to initialize distutils.sysconfig. --- sysconfig.py | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index d203f8e4..f205dcad 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -415,38 +415,11 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" - g = {} - # load the installed Makefile: - try: - filename = get_makefile_filename() - parse_makefile(filename, g) - except OSError as msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # load the installed pyconfig.h: - try: - filename = get_config_h_filename() - with open(filename) as file: - parse_config_h(file, g) - except OSError as msg: - my_msg = "invalid Python installation: unable to open %s" % filename - if hasattr(msg, "strerror"): - my_msg = my_msg + " (%s)" % msg.strerror - - raise DistutilsPlatformError(my_msg) - - # On AIX, there are wrong paths to the linker scripts in the Makefile - # -- these paths are relative to the Python source, but when installed - # the scripts are in another directory. - if python_build: - g['LDSHARED'] = g['BLDSHARED'] - + # _sysconfigdata is generated at build time, see the sysconfig module + from _sysconfigdata import build_time_vars global _config_vars - _config_vars = g + _config_vars = {} + _config_vars.update(build_time_vars) def _init_nt(): -- cgit v1.2.1 From 7c3fafbc0017fa29a42d0eda69b37b37c4b465c2 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Fri, 10 Jun 2016 23:00:52 +0300 Subject: Issue #20900: distutils register command now decodes HTTP responses correctly Initial patch by ingrid. --- command/register.py | 6 +++--- tests/test_register.py | 14 ++++++++++++++ tests/test_upload.py | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/command/register.py b/command/register.py index b49f86fe..86343c80 100644 --- a/command/register.py +++ b/command/register.py @@ -296,9 +296,9 @@ Your selection [default 1]: ''', log.INFO) result = 500, str(e) else: if self.show_response: - data = result.read() + data = self._read_pypi_response(result) result = 200, 'OK' if self.show_response: - dashes = '-' * 75 - self.announce('%s%r%s' % (dashes, data, dashes)) + msg = '\n'.join(('-' * 75, data, '-' * 75)) + self.announce(msg, log.INFO) return result diff --git a/tests/test_register.py b/tests/test_register.py index 61801339..01acf237 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -301,6 +301,20 @@ class RegisterTestCase(PyPIRCCommandTestCase): results = self.get_logs(INFO) self.assertEqual(results, ['running check', 'xxx']) + def test_show_response(self): + # test that the --show-response option return a well formatted response + cmd = self._get_cmd() + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + cmd.show_response = 1 + try: + cmd.run() + finally: + del register_module.input + + results = self.get_logs(INFO) + self.assertEqual(results[3], 75 * '-' + '\nxxx\n' + 75 * '-') + def test_suite(): return unittest.makeSuite(RegisterTestCase) diff --git a/tests/test_upload.py b/tests/test_upload.py index 19193d5b..964aac7e 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -140,7 +140,7 @@ class uploadTestCase(PyPIRCCommandTestCase): # The PyPI response body was echoed results = self.get_logs(INFO) - self.assertIn('xyzzy\n', results[-1]) + self.assertEqual(results[-1], 75 * '-' + '\nxyzzy\n' + 75 * '-') def test_upload_fails(self): self.next_msg = "Not Found" -- cgit v1.2.1 From 9070733b989cf7350ff11aab41f0d6f541700f84 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Tue, 14 Jun 2016 08:55:19 +0200 Subject: - Issue #23968: Rename the platform directory from plat-$(MACHDEP) to plat-$(PLATFORM_TRIPLET). Rename the config directory (LIBPL) from config-$(LDVERSION) to config-$(LDVERSION)-$(PLATFORM_TRIPLET). Install the platform specifc _sysconfigdata module into the platform directory and rename it to include the ABIFLAGS. --- sysconfig.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index f205dcad..e9cc4a95 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -242,6 +242,8 @@ def get_makefile_filename(): return os.path.join(_sys_home or project_base, "Makefile") lib_dir = get_python_lib(plat_specific=0, standard_lib=1) config_file = 'config-{}{}'.format(get_python_version(), build_flags) + if hasattr(sys.implementation, '_multiarch'): + config_file += '-%s' % sys.implementation._multiarch return os.path.join(lib_dir, config_file, 'Makefile') -- cgit v1.2.1 From 089c6f4fa347563cf9753ea0370723ed1b00bc89 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Tue, 14 Jun 2016 09:22:16 +0200 Subject: - Issue #23968: Update distutils/sysconfig.py to look for the renamed _sysconfigdata module too. --- sysconfig.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index e9cc4a95..f72b7f5a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -418,7 +418,9 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" # _sysconfigdata is generated at build time, see the sysconfig module - from _sysconfigdata import build_time_vars + name = '_sysconfigdata_' + sys.abiflags + _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + build_time_vars = _temp.build_time_vars global _config_vars _config_vars = {} _config_vars.update(build_time_vars) -- cgit v1.2.1 From 7b9d0828be06389c7e81946ba049231396eb9f3d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 17 Jun 2016 09:32:38 -0700 Subject: Issue #27048: Prevents distutils failing on Windows when environment variables contain non-ASCII characters --- _msvccompiler.py | 6 ++---- tests/test_msvccompiler.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index d0ba7d6d..b120273f 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -86,11 +86,9 @@ def _get_vc_env(plat_spec): try: out = subprocess.check_output( - '"{}" {} && set'.format(vcvarsall, plat_spec), - shell=True, + 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), stderr=subprocess.STDOUT, - universal_newlines=True, - ) + ).decode('utf-16le', errors='replace') except subprocess.CalledProcessError as exc: log.error(exc.output) raise DistutilsPlatformError("Error executing {}" diff --git a/tests/test_msvccompiler.py b/tests/test_msvccompiler.py index c4d911ff..4dc24886 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -83,6 +83,24 @@ class msvccompilerTestCase(support.TempdirManager, self.assertFalse(os.path.isfile(os.path.join( tempdir, os.path.basename(dll)))) + def test_get_vc_env_unicode(self): + import distutils._msvccompiler as _msvccompiler + + test_var = 'ṰḖṤṪ┅ṼẨṜ' + test_value = '₃â´â‚…' + + # Ensure we don't early exit from _get_vc_env + old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None) + os.environ[test_var] = test_value + try: + env = _msvccompiler._get_vc_env('x86') + self.assertIn(test_var.lower(), env) + self.assertEqual(test_value, env[test_var.lower()]) + finally: + os.environ.pop(test_var) + if old_distutils_use_sdk: + os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk + def test_suite(): return unittest.makeSuite(msvccompilerTestCase) -- cgit v1.2.1 From 574bde6ee98b09b974ac5e0adbe46eeebc5173b6 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 18 Jun 2016 21:42:37 +0300 Subject: Issue #27349: Fix typo in distutils upload command --- command/upload.py | 2 +- tests/test_upload.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/command/upload.py b/command/upload.py index 0afcbf23..1fd574a9 100644 --- a/command/upload.py +++ b/command/upload.py @@ -91,7 +91,7 @@ class upload(PyPIRCCommand): data = { # action ':action': 'file_upload', - 'protcol_version': '1', + 'protocol_version': '1', # identify release 'name': meta.get_name(), diff --git a/tests/test_upload.py b/tests/test_upload.py index 964aac7e..3eecf8af 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -130,13 +130,14 @@ class uploadTestCase(PyPIRCCommandTestCase): # what did we send ? headers = dict(self.last_open.req.headers) - self.assertEqual(headers['Content-length'], '2161') + self.assertEqual(headers['Content-length'], '2162') content_type = headers['Content-type'] self.assertTrue(content_type.startswith('multipart/form-data')) self.assertEqual(self.last_open.req.get_method(), 'POST') expected_url = 'https://pypi.python.org/pypi' self.assertEqual(self.last_open.req.get_full_url(), expected_url) self.assertTrue(b'xxx' in self.last_open.req.data) + self.assertIn(b'protocol_version', self.last_open.req.data) # The PyPI response body was echoed results = self.get_logs(INFO) -- cgit v1.2.1 From 32821f0c7684148df899f79932c2e138c3ace6f5 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Mon, 20 Jun 2016 21:41:34 +0300 Subject: Issue #20120: Add a test case to verify the % char can be used in .pypirc I noticed that there is no test for this feature while doing triaging work on pypa/pypi-legacy. --- tests/test_config.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_config.py b/tests/test_config.py index 4de825a8..0b91d19a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -18,6 +18,7 @@ PYPIRC = """\ index-servers = server1 server2 + server3 [server1] username:me @@ -28,6 +29,10 @@ username:meagain password: secret realm:acme repository:http://another.pypi/ + +[server3] +username:cbiggles +password:yh^%#rest-of-my-password """ PYPIRC_OLD = """\ @@ -113,6 +118,20 @@ class PyPIRCCommandTestCase(support.TempdirManager, finally: f.close() + def test_config_interpolation(self): + # using the % character in .pypirc should not raise an error (#20120) + self.write_file(self.rc, PYPIRC) + cmd = self._cmd(self.dist) + cmd.repository = 'server3' + config = cmd._read_pypirc() + + config = list(sorted(config.items())) + waited = [('password', 'yh^%#rest-of-my-password'), ('realm', 'pypi'), + ('repository', 'https://pypi.python.org/pypi'), + ('server', 'server3'), ('username', 'cbiggles')] + self.assertEqual(config, waited) + + def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) -- cgit v1.2.1 From 90d7e0e1b1e1da326f73d4dbdc81e9a4e72daa5c Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Fri, 24 Jun 2016 08:48:27 +0300 Subject: Make PyPIRCCommandTestCase derive from a base class Several test cases in distutils use PyPIRCCommandTestCase as their base class and as a result of that the following tests were ran more than once: * test_server_registration * test_server_empty_registration * test_config_interpolation This commit moves the infrastructure used by other tests into a new BasePyPIRCCommandTestCase class. --- tests/test_config.py | 9 ++++++--- tests/test_register.py | 4 ++-- tests/test_sdist.py | 4 ++-- tests/test_upload.py | 4 ++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 0b91d19a..3dd92d61 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -52,14 +52,14 @@ password:xxx """ -class PyPIRCCommandTestCase(support.TempdirManager, +class BasePyPIRCCommandTestCase(support.TempdirManager, support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): def setUp(self): """Patches the environment.""" - super(PyPIRCCommandTestCase, self).setUp() + super(BasePyPIRCCommandTestCase, self).setUp() self.tmp_dir = self.mkdtemp() os.environ['HOME'] = self.tmp_dir self.rc = os.path.join(self.tmp_dir, '.pypirc') @@ -78,7 +78,10 @@ class PyPIRCCommandTestCase(support.TempdirManager, def tearDown(self): """Removes the patch.""" set_threshold(self.old_threshold) - super(PyPIRCCommandTestCase, self).tearDown() + super(BasePyPIRCCommandTestCase, self).tearDown() + + +class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase): def test_server_registration(self): # This test makes sure PyPIRCCommand knows how to: diff --git a/tests/test_register.py b/tests/test_register.py index 01acf237..e68b0af3 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -12,7 +12,7 @@ from distutils.command.register import register from distutils.errors import DistutilsSetupError from distutils.log import INFO -from distutils.tests.test_config import PyPIRCCommandTestCase +from distutils.tests.test_config import BasePyPIRCCommandTestCase try: import docutils @@ -72,7 +72,7 @@ class FakeOpener(object): }.get(name.lower(), default) -class RegisterTestCase(PyPIRCCommandTestCase): +class RegisterTestCase(BasePyPIRCCommandTestCase): def setUp(self): super(RegisterTestCase, self).setUp() diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 5a04e0dd..5444b815 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -23,7 +23,7 @@ except ImportError: from distutils.command.sdist import sdist, show_formats from distutils.core import Distribution -from distutils.tests.test_config import PyPIRCCommandTestCase +from distutils.tests.test_config import BasePyPIRCCommandTestCase from distutils.errors import DistutilsOptionError from distutils.spawn import find_executable from distutils.log import WARN @@ -52,7 +52,7 @@ somecode%(sep)sdoc.dat somecode%(sep)sdoc.txt """ -class SDistTestCase(PyPIRCCommandTestCase): +class SDistTestCase(BasePyPIRCCommandTestCase): def setUp(self): # PyPIRCCommandTestCase creates a temp dir already diff --git a/tests/test_upload.py b/tests/test_upload.py index 3eecf8af..e836cc49 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -12,7 +12,7 @@ from distutils.core import Distribution from distutils.errors import DistutilsError from distutils.log import ERROR, INFO -from distutils.tests.test_config import PYPIRC, PyPIRCCommandTestCase +from distutils.tests.test_config import PYPIRC, BasePyPIRCCommandTestCase PYPIRC_LONG_PASSWORD = """\ [distutils] @@ -66,7 +66,7 @@ class FakeOpen(object): return self.code -class uploadTestCase(PyPIRCCommandTestCase): +class uploadTestCase(BasePyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() -- cgit v1.2.1 From eb17f80c10c171808db4f6d81709dc3c9ebf27d6 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Wed, 6 Jul 2016 15:27:35 -0400 Subject: Switch to the new upload url for PyPI --- config.py | 2 +- tests/test_config.py | 4 ++-- tests/test_upload.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config.py b/config.py index 106e1465..9b06d769 100644 --- a/config.py +++ b/config.py @@ -21,7 +21,7 @@ password:%s class PyPIRCCommand(Command): """Base command that knows how to handle the .pypirc file """ - DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi' + DEFAULT_REPOSITORY = 'https://upload.pypi.io/legacy/' DEFAULT_REALM = 'pypi' repository = None realm = None diff --git a/tests/test_config.py b/tests/test_config.py index 4de825a8..c3738155 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -87,7 +87,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://pypi.python.org/pypi'), + ('repository', 'https://upload.pypi.io/legacy/'), ('server', 'server1'), ('username', 'me')] self.assertEqual(config, waited) @@ -96,7 +96,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, config = cmd._read_pypirc() config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://pypi.python.org/pypi'), + ('repository', 'https://upload.pypi.io/legacy/'), ('server', 'server-login'), ('username', 'tarek')] self.assertEqual(config, waited) diff --git a/tests/test_upload.py b/tests/test_upload.py index 8532369a..5a462e5a 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -127,7 +127,7 @@ class uploadTestCase(PyPIRCCommandTestCase): self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) self.assertEqual(self.last_open.req.get_method(), 'POST') self.assertEqual(self.last_open.req.get_full_url(), - 'https://pypi.python.org/pypi') + 'https://upload.pypi.io/legacy/') self.assertIn(b'xxx', self.last_open.req.data) # The PyPI response body was echoed -- cgit v1.2.1 From cbbadc228b01710721939a776b4f2dd008e9c322 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Wed, 6 Jul 2016 16:18:39 -0400 Subject: Switch to the new upload url for PyPI --- config.py | 2 +- tests/test_config.py | 4 ++-- tests/test_upload.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config.py b/config.py index 382aca8f..2d5770c6 100644 --- a/config.py +++ b/config.py @@ -21,7 +21,7 @@ password:%s class PyPIRCCommand(Command): """Base command that knows how to handle the .pypirc file """ - DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi' + DEFAULT_REPOSITORY = 'https://upload.pypi.io/legacy/' DEFAULT_REALM = 'pypi' repository = None realm = None diff --git a/tests/test_config.py b/tests/test_config.py index 4de825a8..c3738155 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -87,7 +87,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://pypi.python.org/pypi'), + ('repository', 'https://upload.pypi.io/legacy/'), ('server', 'server1'), ('username', 'me')] self.assertEqual(config, waited) @@ -96,7 +96,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, config = cmd._read_pypirc() config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://pypi.python.org/pypi'), + ('repository', 'https://upload.pypi.io/legacy/'), ('server', 'server-login'), ('username', 'tarek')] self.assertEqual(config, waited) diff --git a/tests/test_upload.py b/tests/test_upload.py index dccaf77e..bf4d558b 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -90,7 +90,7 @@ class uploadTestCase(PyPIRCCommandTestCase): cmd.finalize_options() for attr, waited in (('username', 'me'), ('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://pypi.python.org/pypi')): + ('repository', 'https://upload.pypi.io/legacy/')): self.assertEqual(getattr(cmd, attr), waited) def test_saved_password(self): @@ -131,7 +131,7 @@ class uploadTestCase(PyPIRCCommandTestCase): content_type = headers['Content-type'] self.assertTrue(content_type.startswith('multipart/form-data')) self.assertEqual(self.last_open.req.get_method(), 'POST') - expected_url = 'https://pypi.python.org/pypi' + expected_url = 'https://upload.pypi.io/legacy/' self.assertEqual(self.last_open.req.get_full_url(), expected_url) self.assertTrue(b'xxx' in self.last_open.req.data) -- cgit v1.2.1 From f568a513f3be48c8f778da29719b7573b869e6c0 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Wed, 6 Jul 2016 17:46:37 -0400 Subject: Fix a test with the new upload URL --- tests/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index 6763f573..c7bbd6d1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -130,7 +130,7 @@ class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase): config = list(sorted(config.items())) waited = [('password', 'yh^%#rest-of-my-password'), ('realm', 'pypi'), - ('repository', 'https://pypi.python.org/pypi'), + ('repository', 'https://upload.pypi.io/legacy/'), ('server', 'server3'), ('username', 'cbiggles')] self.assertEqual(config, waited) -- cgit v1.2.1 From f11c5c1af6806da60cedfdbc94b4548fefec73bb Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 11 Jul 2016 07:51:37 +0000 Subject: English spelling and grammar fixes --- tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 8f62b1a8..4e397ea4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -243,7 +243,7 @@ class BuildExtTestCase(TempdirManager, self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) # second element of each tuple in 'ext_modules' - # must be a ary (build info) + # must be a dictionary (build info) exts = [('foo.bar', '')] self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) -- cgit v1.2.1 From 7c0cfe7a3e4123bbb66f540e829e98673ca1ed7f Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Fri, 22 Jul 2016 12:15:29 +0200 Subject: Issue #27472: Add test.support.unix_shell as the path to the default shell. --- tests/test_spawn.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_spawn.py b/tests/test_spawn.py index f507ef77..5edc24a3 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -1,7 +1,8 @@ """Tests for distutils.spawn.""" import unittest +import sys import os -from test.support import run_unittest +from test.support import run_unittest, unix_shell from distutils.spawn import _nt_quote_args from distutils.spawn import spawn @@ -29,9 +30,9 @@ class SpawnTestCase(support.TempdirManager, # creating something executable # through the shell that returns 1 - if os.name == 'posix': + if sys.platform != 'win32': exe = os.path.join(tmpdir, 'foo.sh') - self.write_file(exe, '#!/bin/sh\nexit 1') + self.write_file(exe, '#!%s\nexit 1' % unix_shell) else: exe = os.path.join(tmpdir, 'foo.bat') self.write_file(exe, 'exit 1') @@ -40,9 +41,9 @@ class SpawnTestCase(support.TempdirManager, self.assertRaises(DistutilsExecError, spawn, [exe]) # now something that works - if os.name == 'posix': + if sys.platform != 'win32': exe = os.path.join(tmpdir, 'foo.sh') - self.write_file(exe, '#!/bin/sh\nexit 0') + self.write_file(exe, '#!%s\nexit 0' % unix_shell) else: exe = os.path.join(tmpdir, 'foo.bat') self.write_file(exe, 'exit 0') -- cgit v1.2.1 From c3f23f70001777fd66c7c33b9101bb114540a53a Mon Sep 17 00:00:00 2001 From: Stefan Krah Date: Wed, 3 Aug 2016 11:18:26 +0200 Subject: Issue #20767: Fix -R option for FreeBSD/clang. --- unixccompiler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unixccompiler.py b/unixccompiler.py index 92d14dfe..3f321c28 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -227,6 +227,8 @@ class UnixCCompiler(CCompiler): if sys.platform[:6] == "darwin": # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir + elif sys.platform[:7] == "freebsd": + return "-Wl,-rpath=" + dir elif sys.platform[:5] == "hp-ux": if self._is_gcc(compiler): return ["-Wl,+s", "-L" + dir] -- cgit v1.2.1 From e639c9f681154bb0e5530543beefd015040406ac Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Wed, 3 Aug 2016 18:43:38 -0400 Subject: Switch upload.pypi.io to upload.pypi.org --- config.py | 2 +- tests/test_config.py | 4 ++-- tests/test_upload.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config.py b/config.py index 9b06d769..96469ca9 100644 --- a/config.py +++ b/config.py @@ -21,7 +21,7 @@ password:%s class PyPIRCCommand(Command): """Base command that knows how to handle the .pypirc file """ - DEFAULT_REPOSITORY = 'https://upload.pypi.io/legacy/' + DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/' DEFAULT_REALM = 'pypi' repository = None realm = None diff --git a/tests/test_config.py b/tests/test_config.py index c3738155..8286e1d9 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -87,7 +87,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://upload.pypi.io/legacy/'), + ('repository', 'https://upload.pypi.org/legacy/'), ('server', 'server1'), ('username', 'me')] self.assertEqual(config, waited) @@ -96,7 +96,7 @@ class PyPIRCCommandTestCase(support.TempdirManager, config = cmd._read_pypirc() config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://upload.pypi.io/legacy/'), + ('repository', 'https://upload.pypi.org/legacy/'), ('server', 'server-login'), ('username', 'tarek')] self.assertEqual(config, waited) diff --git a/tests/test_upload.py b/tests/test_upload.py index 5a462e5a..1402343e 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -127,7 +127,7 @@ class uploadTestCase(PyPIRCCommandTestCase): self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) self.assertEqual(self.last_open.req.get_method(), 'POST') self.assertEqual(self.last_open.req.get_full_url(), - 'https://upload.pypi.io/legacy/') + 'https://upload.pypi.org/legacy/') self.assertIn(b'xxx', self.last_open.req.data) # The PyPI response body was echoed -- cgit v1.2.1 From 774707dce2a31748382aaa019848014a17d0a04a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 20 Aug 2016 17:31:07 -0400 Subject: Issue #27819: Simply default to gztar for sdist formats by default on all platforms. --- command/sdist.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 35a06eb0..f1b8d919 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -91,9 +91,6 @@ class sdist(Command): negative_opt = {'no-defaults': 'use-defaults', 'no-prune': 'prune' } - default_format = {'posix': 'gztar', - 'nt': 'zip' } - sub_commands = [('check', checking_metadata)] def initialize_options(self): @@ -110,7 +107,7 @@ class sdist(Command): self.manifest_only = 0 self.force_manifest = 0 - self.formats = None + self.formats = ['gztar'] self.keep_temp = 0 self.dist_dir = None @@ -126,13 +123,6 @@ class sdist(Command): self.template = "MANIFEST.in" self.ensure_string_list('formats') - if self.formats is None: - try: - self.formats = [self.default_format[os.name]] - except KeyError: - raise DistutilsPlatformError( - "don't know how to create source distributions " - "on platform %s" % os.name) bad_format = archive_util.check_archive_formats(self.formats) if bad_format: -- cgit v1.2.1 From f3f736a749a120d124e54e325875775c9e124ee0 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 30 Aug 2016 10:47:49 -0700 Subject: =?UTF-8?q?Issue=20#27895:=20=20Spelling=20fixes=20(Contributed=20?= =?UTF-8?q?by=20Ville=20Skytt=C3=A4).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 5e18c613..77a07ef3 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -125,7 +125,7 @@ class msvc9compilerTestCase(support.TempdirManager, self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') # looking for values that should exist on all - # windows registeries versions. + # windows registry versions. path = r'Control Panel\Desktop' v = Reg.get_value(path, 'dragfullwindows') self.assertIn(v, ('0', '1', '2')) -- cgit v1.2.1 From 3ac5842595463683cbc1d980a8b770d84cef84f9 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Wed, 31 Aug 2016 08:22:29 +0100 Subject: Closes #27904: Improved logging statements to defer formatting until needed. --- archive_util.py | 2 +- cmd.py | 3 +-- command/bdist_dumb.py | 2 +- command/build_ext.py | 6 +++--- command/config.py | 2 +- command/install.py | 2 +- command/register.py | 6 +++--- command/sdist.py | 2 +- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/archive_util.py b/archive_util.py index bed13849..78ae5757 100644 --- a/archive_util.py +++ b/archive_util.py @@ -171,7 +171,7 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): path = os.path.normpath(os.path.join(dirpath, name)) if os.path.isfile(path): zip.write(path, path) - log.info("adding '%s'" % path) + log.info("adding '%s'", path) zip.close() return zip_filename diff --git a/cmd.py b/cmd.py index c89d5efc..b5d9dc38 100644 --- a/cmd.py +++ b/cmd.py @@ -329,8 +329,7 @@ class Command: # -- External world manipulation ----------------------------------- def warn(self, msg): - log.warn("warning: %s: %s\n" % - (self.get_command_name(), msg)) + log.warn("warning: %s: %s\n", self.get_command_name(), msg) def execute(self, func, args, msg=None, level=1): util.execute(func, args, msg, dry_run=self.dry_run) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index f1bfb249..e9274d92 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -85,7 +85,7 @@ class bdist_dumb(Command): install.skip_build = self.skip_build install.warn_dir = 0 - log.info("installing to %s" % self.bdist_dir) + log.info("installing to %s", self.bdist_dir) self.run_command('install') # And make an archive relative to the root of the diff --git a/command/build_ext.py b/command/build_ext.py index f03a4e31..5e51ae4b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -363,9 +363,9 @@ class build_ext(Command): ext_name, build_info = ext - log.warn(("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s'" - "-- please convert to Extension instance" % ext_name)) + log.warn("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s'" + "-- please convert to Extension instance", ext_name) if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)): diff --git a/command/config.py b/command/config.py index b1fd09e0..4ae153d1 100644 --- a/command/config.py +++ b/command/config.py @@ -337,7 +337,7 @@ def dump_file(filename, head=None): If head is not None, will be dumped before the file content. """ if head is None: - log.info('%s' % filename) + log.info('%s', filename) else: log.info(head) file = open(filename) diff --git a/command/install.py b/command/install.py index 9474e9c5..fca05d69 100644 --- a/command/install.py +++ b/command/install.py @@ -385,7 +385,7 @@ class install(Command): else: opt_name = opt_name.translate(longopt_xlate) val = getattr(self, opt_name) - log.debug(" %s: %s" % (opt_name, val)) + log.debug(" %s: %s", opt_name, val) def finalize_unix(self): """Finalizes options for posix platforms.""" diff --git a/command/register.py b/command/register.py index 456d50d5..0fac94e9 100644 --- a/command/register.py +++ b/command/register.py @@ -94,7 +94,7 @@ class register(PyPIRCCommand): ''' # send the info to the server and report the result (code, result) = self.post_to_server(self.build_post_data('verify')) - log.info('Server response (%s): %s' % (code, result)) + log.info('Server response (%s): %s', code, result) def send_metadata(self): ''' Send the metadata to the package index server. @@ -205,7 +205,7 @@ Your selection [default 1]: ''', log.INFO) data['email'] = input(' EMail: ') code, result = self.post_to_server(data) if code != 200: - log.info('Server response (%s): %s' % (code, result)) + log.info('Server response (%s): %s', code, result) else: log.info('You will receive an email shortly.') log.info(('Follow the instructions in it to ' @@ -216,7 +216,7 @@ Your selection [default 1]: ''', log.INFO) while not data['email']: data['email'] = input('Your email address: ') code, result = self.post_to_server(data) - log.info('Server response (%s): %s' % (code, result)) + log.info('Server response (%s): %s', code, result) def build_post_data(self, action): # figure the data to send - the metadata plus some additional diff --git a/command/sdist.py b/command/sdist.py index f1b8d919..4fd1d471 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -412,7 +412,7 @@ class sdist(Command): log.info(msg) for file in files: if not os.path.isfile(file): - log.warn("'%s' not a regular file -- skipping" % file) + log.warn("'%s' not a regular file -- skipping", file) else: dest = os.path.join(base_dir, file) self.copy_file(file, dest, link=link) -- cgit v1.2.1 From 0685847e2065e046808e038fd50d3368be24541f Mon Sep 17 00:00:00 2001 From: R David Murray Date: Wed, 31 Aug 2016 11:39:35 -0400 Subject: #27904: fix distutils tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch by Ville Skyttä. --- tests/test_build_py.py | 3 ++- tests/test_install_lib.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_build_py.py b/tests/test_build_py.py index 18283dc7..0712e92c 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -168,7 +168,8 @@ class BuildPyTestCase(support.TempdirManager, finally: sys.dont_write_bytecode = old_dont_write_bytecode - self.assertIn('byte-compiling is disabled', self.logs[0][1]) + self.assertIn('byte-compiling is disabled', + self.logs[0][1] % self.logs[0][2]) def test_suite(): diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py index 5378aa82..fda6315b 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -104,7 +104,8 @@ class InstallLibTestCase(support.TempdirManager, finally: sys.dont_write_bytecode = old_dont_write_bytecode - self.assertIn('byte-compiling is disabled', self.logs[0][1]) + self.assertIn('byte-compiling is disabled', + self.logs[0][1] % self.logs[0][2]) def test_suite(): -- cgit v1.2.1 From ac97b020927d8c00d43595afe46749ef6cab7159 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 1 Sep 2016 13:55:33 -0400 Subject: Issue #27919: Deprecate extra_path option in distutils. --- command/install.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/command/install.py b/command/install.py index fca05d69..0258d3de 100644 --- a/command/install.py +++ b/command/install.py @@ -175,6 +175,7 @@ class install(Command): self.compile = None self.optimize = None + # Deprecated # These two are for putting non-packagized distributions into their # own directory and creating a .pth file if it makes sense. # 'extra_path' comes from the setup file; 'install_path_file' can @@ -344,6 +345,7 @@ class install(Command): 'scripts', 'data', 'headers', 'userbase', 'usersite') + # Deprecated # Well, we're not actually fully completely finalized yet: we still # have to deal with 'extra_path', which is the hack for allowing # non-packagized module distributions (hello, Numerical Python!) to @@ -490,6 +492,10 @@ class install(Command): self.extra_path = self.distribution.extra_path if self.extra_path is not None: + log.warn( + "Distribution option extra_path is deprecated. " + "See issue27919 for details." + ) if isinstance(self.extra_path, str): self.extra_path = self.extra_path.split(',') -- cgit v1.2.1 From b15f55c3838ea7dd2719178b3c8ca995b4e9e2e7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 1 Sep 2016 21:55:22 -0400 Subject: Backed out changeset cc86e9e102e8 --- tests/test_filelist.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index e719198c..391af3cb 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -9,7 +9,7 @@ from distutils.filelist import glob_to_re, translate_pattern, FileList from distutils import filelist import test.support -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils.tests import support MANIFEST_IN = """\ @@ -329,5 +329,12 @@ class FindAllTestCase(unittest.TestCase): self.assertEqual(filelist.findall(temp_dir), expected) +def test_suite(): + return unittest.TestSuite([ + unittest.makeSuite(FileListTestCase), + unittest.makeSuite(FindAllTestCase), + ]) + + if __name__ == "__main__": - unittest.main() + run_unittest(test_suite()) -- cgit v1.2.1 From bfef652ebcddc99c4b891f96f3d42a3c51bfcccc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 1 Sep 2016 23:27:45 -0400 Subject: Issue #12885: Revert commits in 3.4 branch which is security-only fixes. --- filelist.py | 48 +++++++++++++++++++++++++++--------------------- tests/test_filelist.py | 48 ++---------------------------------------------- 2 files changed, 29 insertions(+), 67 deletions(-) diff --git a/filelist.py b/filelist.py index 6522e69f..db3f7a96 100644 --- a/filelist.py +++ b/filelist.py @@ -6,7 +6,6 @@ and building lists of files. import os, re import fnmatch -import functools from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsInternalError from distutils import log @@ -243,28 +242,35 @@ class FileList: # ---------------------------------------------------------------------- # Utility functions -def _find_all_simple(path): - """ - Find all files under 'path' - """ - results = ( - os.path.join(base, file) - for base, dirs, files in os.walk(path, followlinks=True) - for file in files - ) - return filter(os.path.isfile, results) - - def findall(dir=os.curdir): + """Find all files under 'dir' and return the list of full filenames + (relative to 'dir'). """ - Find all files under 'dir' and return the list of full filenames. - Unless dir is '.', return full filenames with dir prepended. - """ - files = _find_all_simple(dir) - if dir == os.curdir: - make_rel = functools.partial(os.path.relpath, start=dir) - files = map(make_rel, files) - return list(files) + from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK + + list = [] + stack = [dir] + pop = stack.pop + push = stack.append + + while stack: + dir = pop() + names = os.listdir(dir) + + for name in names: + if dir != os.curdir: # avoid the dreaded "./" syndrome + fullname = os.path.join(dir, name) + else: + fullname = name + + # Avoid excess stat calls -- just one will do, thank you! + stat = os.stat(fullname) + mode = stat[ST_MODE] + if S_ISREG(mode): + list.append(fullname) + elif S_ISDIR(mode) and not S_ISLNK(mode): + push(fullname) + return list def glob_to_re(pattern): diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 391af3cb..278809c0 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -6,10 +6,8 @@ from distutils import debug from distutils.log import WARN from distutils.errors import DistutilsTemplateError from distutils.filelist import glob_to_re, translate_pattern, FileList -from distutils import filelist -import test.support -from test.support import captured_stdout, run_unittest +from test.support import captured_stdout from distutils.tests import support MANIFEST_IN = """\ @@ -294,47 +292,5 @@ class FileListTestCase(support.LoggingSilencer, self.assertWarnings() -class FindAllTestCase(unittest.TestCase): - @test.support.skip_unless_symlink - def test_missing_symlink(self): - with test.support.temp_cwd(): - os.symlink('foo', 'bar') - self.assertEqual(filelist.findall(), []) - - def test_basic_discovery(self): - """ - When findall is called with no parameters or with - '.' as the parameter, the dot should be omitted from - the results. - """ - with test.support.temp_cwd(): - os.mkdir('foo') - file1 = os.path.join('foo', 'file1.txt') - test.support.create_empty_file(file1) - os.mkdir('bar') - file2 = os.path.join('bar', 'file2.txt') - test.support.create_empty_file(file2) - expected = [file2, file1] - self.assertEqual(sorted(filelist.findall()), expected) - - def test_non_local_discovery(self): - """ - When findall is called with another path, the full - path name should be returned. - """ - with test.support.temp_dir() as temp_dir: - file1 = os.path.join(temp_dir, 'file1.txt') - test.support.create_empty_file(file1) - expected = [file1] - self.assertEqual(filelist.findall(temp_dir), expected) - - -def test_suite(): - return unittest.TestSuite([ - unittest.makeSuite(FileListTestCase), - unittest.makeSuite(FindAllTestCase), - ]) - - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() -- cgit v1.2.1 From 175d71c6352e17dc2b752160ec78d2cb90e7c271 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 5 Sep 2016 22:24:01 -0400 Subject: Issue #27960: Revert state to 675e20c38fdac6, backing out all changes by developed for Issue #12885. --- tests/test_filelist.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 278809c0..e82bc3d2 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -7,7 +7,7 @@ from distutils.log import WARN from distutils.errors import DistutilsTemplateError from distutils.filelist import glob_to_re, translate_pattern, FileList -from test.support import captured_stdout +from test.support import captured_stdout, run_unittest from distutils.tests import support MANIFEST_IN = """\ @@ -292,5 +292,8 @@ class FileListTestCase(support.LoggingSilencer, self.assertWarnings() +def test_suite(): + return unittest.makeSuite(FileListTestCase) + if __name__ == "__main__": - unittest.main() + run_unittest(test_suite()) -- cgit v1.2.1 From 84f35582fc98b213e51d9924ae9f726212069560 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Wed, 7 Sep 2016 12:03:06 +0000 Subject: =?UTF-8?q?Issue=20#27895:=20=20Spelling=20fixes=20(Contributed=20?= =?UTF-8?q?by=20Ville=20Skytt=C3=A4).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py index 5e18c613..77a07ef3 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -125,7 +125,7 @@ class msvc9compilerTestCase(support.TempdirManager, self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') # looking for values that should exist on all - # windows registeries versions. + # windows registry versions. path = r'Control Panel\Desktop' v = Reg.get_value(path, 'dragfullwindows') self.assertIn(v, ('0', '1', '2')) -- cgit v1.2.1 From fe41e077426b41fee85c7bd5800029796fb4b8c9 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Thu, 8 Sep 2016 13:59:53 -0400 Subject: #27364: fix "incorrect" uses of escape character in the stdlib. And most of the tools. Patch by Emanual Barry, reviewed by me, Serhiy Storchaka, and Martin Panter. --- cmd.py | 2 +- command/bdist_msi.py | 4 ++-- command/build_scripts.py | 2 +- cygwinccompiler.py | 2 +- msvc9compiler.py | 2 +- sysconfig.py | 2 +- versionpredicate.py | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmd.py b/cmd.py index b5d9dc38..939f7959 100644 --- a/cmd.py +++ b/cmd.py @@ -221,7 +221,7 @@ class Command: self._ensure_stringlike(option, "string", default) def ensure_string_list(self, option): - """Ensure that 'option' is a list of strings. If 'option' is + r"""Ensure that 'option' is a list of strings. If 'option' is currently a string, we split it either on /,\s*/ or /\s+/, so "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become ["foo", "bar", "baz"]. diff --git a/command/bdist_msi.py b/command/bdist_msi.py index f6c21aee..a4bd5a58 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -623,7 +623,7 @@ class bdist_msi(Command): cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title, "OK", "OK", "OK", bitmap=False) cost.text("Title", 15, 6, 200, 15, 0x30003, - "{\DlgFontBold8}Disk Space Requirements") + r"{\DlgFontBold8}Disk Space Requirements") cost.text("Description", 20, 20, 280, 20, 0x30003, "The disk space required for the installation of the selected features.") cost.text("Text", 20, 53, 330, 60, 3, @@ -670,7 +670,7 @@ class bdist_msi(Command): progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title, "Cancel", "Cancel", "Cancel", bitmap=False) progress.text("Title", 20, 15, 200, 15, 0x30003, - "{\DlgFontBold8}[Progress1] [ProductName]") + r"{\DlgFontBold8}[Progress1] [ProductName]") progress.text("Text", 35, 65, 300, 30, 3, "Please wait while the Installer [Progress2] [ProductName]. " "This may take several minutes.") diff --git a/command/build_scripts.py b/command/build_scripts.py index 90a8380a..ccc70e64 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -51,7 +51,7 @@ class build_scripts(Command): def copy_scripts(self): - """Copy each script listed in 'self.scripts'; if it's marked as a + r"""Copy each script listed in 'self.scripts'; if it's marked as a Python script in the Unix way (first line matches 'first_line_re', ie. starts with "\#!" and contains "python"), then adjust the first line to refer to the current Python interpreter as we copy. diff --git a/cygwinccompiler.py b/cygwinccompiler.py index c879646c..1c369903 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -368,7 +368,7 @@ def check_config_h(): return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) -RE_VERSION = re.compile(b'(\d+\.\d+(\.\d+)*)') +RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)') def _find_exe_version(cmd): """Find the version of an executable by running `cmd` in the shell. diff --git a/msvc9compiler.py b/msvc9compiler.py index 0b1fd19f..21191276 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -716,7 +716,7 @@ class MSVCCompiler(CCompiler) : r"""VC\d{2}\.CRT("|').*?(/>|)""", re.DOTALL) manifest_buf = re.sub(pattern, "", manifest_buf) - pattern = "\s*" + pattern = r"\s*" manifest_buf = re.sub(pattern, "", manifest_buf) # Now see if any other assemblies are referenced - if not, we # don't want a manifest embedded. diff --git a/sysconfig.py b/sysconfig.py index f72b7f5a..68135987 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -278,7 +278,7 @@ def parse_config_h(fp, g=None): # Regexes needed for parsing Makefile (and similar syntaxes, # like old-style Setup files). -_variable_rx = re.compile("([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") +_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") diff --git a/versionpredicate.py b/versionpredicate.py index b0dd9f45..062c98f2 100644 --- a/versionpredicate.py +++ b/versionpredicate.py @@ -154,7 +154,7 @@ def split_provision(value): global _provision_rx if _provision_rx is None: _provision_rx = re.compile( - "([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", + r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", re.ASCII) value = value.strip() m = _provision_rx.match(value) -- cgit v1.2.1 From d0f84684de3a4c3d9dbb1602f9810d9688847e09 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Fri, 9 Sep 2016 18:29:10 -0700 Subject: Issue #28046: Fix distutils Why do we have two sysconfig modules again? --- sysconfig.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 68135987..229626e1 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -418,7 +418,11 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" # _sysconfigdata is generated at build time, see the sysconfig module - name = '_sysconfigdata_' + sys.abiflags + name = '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + abi=sys.abiflags, + platform=sys.platform, + multiarch=getattr(sys.implementation, '_multiarch', ''), + ) _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) build_time_vars = _temp.build_time_vars global _config_vars -- cgit v1.2.1 From ba17bdeda7e2d5c1ac05be611569785b3aec20bd Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 11 Sep 2016 12:50:02 +0300 Subject: Issue #22493: Inline flags now should be used only at the start of the regular expression. Deprecation warning is emitted if uses them in the middle of the regular expression. --- filelist.py | 15 ++++++++++----- tests/test_filelist.py | 14 +++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/filelist.py b/filelist.py index 6522e69f..c92d5fdb 100644 --- a/filelist.py +++ b/filelist.py @@ -302,21 +302,26 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0): else: return pattern + # ditch start and end characters + start, _, end = glob_to_re('_').partition('_') + if pattern: pattern_re = glob_to_re(pattern) + assert pattern_re.startswith(start) and pattern_re.endswith(end) else: pattern_re = '' if prefix is not None: - # ditch end of pattern character - empty_pattern = glob_to_re('') - prefix_re = glob_to_re(prefix)[:-len(empty_pattern)] + prefix_re = glob_to_re(prefix) + assert prefix_re.startswith(start) and prefix_re.endswith(end) + prefix_re = prefix_re[len(start): len(prefix_re) - len(end)] sep = os.sep if os.sep == '\\': sep = r'\\' - pattern_re = "^" + sep.join((prefix_re, ".*" + pattern_re)) + pattern_re = pattern_re[len(start): len(pattern_re) - len(end)] + pattern_re = r'%s\A%s%s.*%s%s' % (start, prefix_re, sep, pattern_re, end) else: # no prefix -- respect anchor flag if anchor: - pattern_re = "^" + pattern_re + pattern_re = r'%s\A%s' % (start, pattern_re[len(start):]) return re.compile(pattern_re) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 391af3cb..c71342d0 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -51,14 +51,14 @@ class FileListTestCase(support.LoggingSilencer, for glob, regex in ( # simple cases - ('foo*', r'foo[^%(sep)s]*\Z(?ms)'), - ('foo?', r'foo[^%(sep)s]\Z(?ms)'), - ('foo??', r'foo[^%(sep)s][^%(sep)s]\Z(?ms)'), + ('foo*', r'(?s:foo[^%(sep)s]*)\Z'), + ('foo?', r'(?s:foo[^%(sep)s])\Z'), + ('foo??', r'(?s:foo[^%(sep)s][^%(sep)s])\Z'), # special cases - (r'foo\\*', r'foo\\\\[^%(sep)s]*\Z(?ms)'), - (r'foo\\\*', r'foo\\\\\\[^%(sep)s]*\Z(?ms)'), - ('foo????', r'foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s]\Z(?ms)'), - (r'foo\\??', r'foo\\\\[^%(sep)s][^%(sep)s]\Z(?ms)')): + (r'foo\\*', r'(?s:foo\\\\[^%(sep)s]*)\Z'), + (r'foo\\\*', r'(?s:foo\\\\\\[^%(sep)s]*)\Z'), + ('foo????', r'(?s:foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s])\Z'), + (r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z')): regex = regex % {'sep': sep} self.assertEqual(glob_to_re(glob), regex) -- cgit v1.2.1 From 8a0f7a5412ea23ca0a9a2f9ef9352879b11909d1 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Sun, 11 Sep 2016 22:22:24 +0200 Subject: Issue #28046: get_sysconfigdata_name() uses the _PYTHON_SYSCONFIGDATA_NAME environment variable that is defined when cross-compiling. --- sysconfig.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 229626e1..8bf1a701 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -418,11 +418,12 @@ _config_vars = None def _init_posix(): """Initialize the module as appropriate for POSIX systems.""" # _sysconfigdata is generated at build time, see the sysconfig module - name = '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME', + '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( abi=sys.abiflags, platform=sys.platform, multiarch=getattr(sys.implementation, '_multiarch', ''), - ) + )) _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) build_time_vars = _temp.build_time_vars global _config_vars -- cgit v1.2.1 From 0c6f3d33cd15a4558e437b70b0507f221f00e3eb Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 28 Sep 2016 23:13:58 -0700 Subject: build_ext: correctly parse the link_objects user option (closes #1703178) Patch by Valerie Lambert. --- command/build_ext.py | 1 + tests/test_build_ext.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index f03a4e31..7c278ef0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -166,6 +166,7 @@ class build_ext(Command): self.include_dirs.append(plat_py_include) self.ensure_string_list('libraries') + self.ensure_string_list('link_objects') # Life is easier if we're not forever checking for None, so # simplify these options to empty lists if unset diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 4e397ea4..f3df564e 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -195,6 +195,13 @@ class BuildExtTestCase(TempdirManager, cmd.finalize_options() self.assertEqual(cmd.rpath, ['one', 'two']) + # make sure cmd.link_objects is turned into a list + # if it's a string + cmd = build_ext(dist) + cmd.link_objects = 'one two,three' + cmd.finalize_options() + self.assertEqual(cmd.link_objects, ['one', 'two', 'three']) + # XXX more tests to perform for win32 # make sure define is turned into 2-tuples -- cgit v1.2.1 From b8032e136b864af0607c781371ee6a8730f67efc Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Sat, 1 Oct 2016 16:15:09 -0500 Subject: Issue #13756: Fix building extensions modules on Cygwin Patch by Roumen Petrov, based on original patch by Jason Tishler. --- command/build_ext.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 74de782d..9155626a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -715,13 +715,6 @@ class build_ext(Command): return ext.libraries + [pythonlib] else: return ext.libraries - elif sys.platform[:6] == "cygwin": - template = "python%d.%d" - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - # don't extend ext.libraries, it may be shared with other - # extensions, it is a reference to the original list - return ext.libraries + [pythonlib] elif sys.platform[:6] == "atheos": from distutils import sysconfig -- cgit v1.2.1 From 06e8590eef402ebcb2a179875572549b5fe8d946 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Tue, 4 Oct 2016 20:54:44 +0300 Subject: Issue #28222: Don't fail if pygments is not available We can't just skip the test if docutils is available, but pygments is not because the purpose of the test was testing a bug in _check_rst_data(). --- tests/test_check.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/test_check.py b/tests/test_check.py index 959fa908..3d22868e 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -7,6 +7,12 @@ from distutils.command.check import check, HAS_DOCUTILS from distutils.tests import support from distutils.errors import DistutilsSetupError +try: + import pygments +except ImportError: + pygments = None + + class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.TestCase): @@ -119,9 +125,15 @@ class CheckTestCase(support.LoggingSilencer, pkg_info, dist = self.create_dist(long_description=rest_with_code) cmd = check(dist) cmd.check_restructuredtext() - self.assertEqual(cmd._warnings, 0) msgs = cmd._check_rst_data(rest_with_code) - self.assertEqual(len(msgs), 0) + if pygments is not None: + self.assertEqual(len(msgs), 0) + else: + self.assertEqual(len(msgs), 1) + self.assertEqual( + str(msgs[0][1]), + 'Cannot analyze code. Pygments package not found.' + ) def test_check_all(self): -- cgit v1.2.1 From 45112961a7fa764f5a26663563da5ff84abb4dc1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 14:06:28 -0400 Subject: Remove wildcard imports from distutils.command.sdist --- command/sdist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 4fd1d471..690bd66f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -4,17 +4,17 @@ Implements the Distutils 'sdist' command (create a source distribution).""" import os import sys -from types import * from glob import glob from warnings import warn from distutils.core import Command from distutils import dir_util, dep_util, file_util, archive_util from distutils.text_file import TextFile -from distutils.errors import * from distutils.filelist import FileList from distutils import log from distutils.util import convert_path +from distutils.errors import DistutilsTemplateError, DistutilsOptionError + def show_formats(): """Print all possible values for the 'formats' option (used by -- cgit v1.2.1 From dcbe64d83ff6c6f60e0fc5756f91c966da930b84 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 14:08:28 -0400 Subject: Remove unused import and reorganize imports of modules. --- command/sdist.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index 690bd66f..c13dbbf8 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -8,7 +8,9 @@ from glob import glob from warnings import warn from distutils.core import Command -from distutils import dir_util, dep_util, file_util, archive_util +from distutils import dir_util +from distutils import file_util +from distutils import archive_util from distutils.text_file import TextFile from distutils.filelist import FileList from distutils import log -- cgit v1.2.1 From 03f46fcf082aa89ab4dbb516d333918429f6e4e0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 14:10:07 -0400 Subject: Replace trailing comments with block-level comments --- command/sdist.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index c13dbbf8..28cd0d9e 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -261,11 +261,13 @@ class sdist(Command): # getting distribution.data_files if self.distribution.has_data_files(): for item in self.distribution.data_files: - if isinstance(item, str): # plain file + if isinstance(item, str): + # plain file item = convert_path(item) if os.path.isfile(item): self.filelist.append(item) - else: # a (dirname, filenames) tuple + else: + # a (dirname, filenames) tuple dirname, filenames = item for f in filenames: f = convert_path(f) -- cgit v1.2.1 From c6f609397210c4fab5163aaa8eddf16dc0bee370 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 14:44:11 -0400 Subject: Get names for README files from class attribute, allowing subclass to override. --- command/sdist.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index 28cd0d9e..e5121666 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -95,6 +95,8 @@ class sdist(Command): sub_commands = [('check', checking_metadata)] + READMES = 'README', 'README.txt' + def initialize_options(self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. @@ -218,7 +220,7 @@ class sdist(Command): Warns if (README or README.txt) or setup.py are missing; everything else is optional. """ - standards = [('README', 'README.txt'), self.distribution.script_name] + standards = [self.READMES, self.distribution.script_name] for fn in standards: if isinstance(fn, tuple): alts = fn -- cgit v1.2.1 From 8ed698da6784b13344a63c9988faa00d281bc6d9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 14:53:32 -0400 Subject: Extract methods from sdist.add_defaults, allowing subclasses to override or inject different behaviors. --- command/sdist.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/command/sdist.py b/command/sdist.py index e5121666..c66d8271 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -220,6 +220,15 @@ class sdist(Command): Warns if (README or README.txt) or setup.py are missing; everything else is optional. """ + self._add_defaults_standards() + self._add_defaults_optional() + self._add_defaults_python() + self._add_defaults_data_files() + self._add_defaults_ext() + self._add_defaults_c_libs() + self._add_defaults_scripts() + + def _add_defaults_standards(self): standards = [self.READMES, self.distribution.script_name] for fn in standards: if isinstance(fn, tuple): @@ -240,11 +249,13 @@ class sdist(Command): else: self.warn("standard file '%s' not found" % fn) + def _add_defaults_optional(self): optional = ['test/test*.py', 'setup.cfg'] for pattern in optional: files = filter(os.path.isfile, glob(pattern)) self.filelist.extend(files) + def _add_defaults_python(self): # build_py is used to get: # - python modules # - files defined in package_data @@ -260,6 +271,7 @@ class sdist(Command): for filename in filenames: self.filelist.append(os.path.join(src_dir, filename)) + def _add_defaults_data_files(self): # getting distribution.data_files if self.distribution.has_data_files(): for item in self.distribution.data_files: @@ -276,14 +288,17 @@ class sdist(Command): if os.path.isfile(f): self.filelist.append(f) + def _add_defaults_ext(self): if self.distribution.has_ext_modules(): build_ext = self.get_finalized_command('build_ext') self.filelist.extend(build_ext.get_source_files()) + def _add_defaults_c_libs(self): if self.distribution.has_c_libraries(): build_clib = self.get_finalized_command('build_clib') self.filelist.extend(build_clib.get_source_files()) + def _add_defaults_scripts(self): if self.distribution.has_scripts(): build_scripts = self.get_finalized_command('build_scripts') self.filelist.extend(build_scripts.get_source_files()) -- cgit v1.2.1 From 3b05fac47289e9835e5bd6f2efb130df59a73ffb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 15:39:01 -0400 Subject: Add case-sensitive file comparison for detecting/adding standard default files. --- command/sdist.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index c66d8271..0cc01192 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -32,6 +32,24 @@ def show_formats(): FancyGetopt(formats).print_help( "List of available source distribution formats:") + +def cs_path_exists(fspath): + """ + Case-sensitive path existence check + + >>> cs_path_exists(__file__) + True + >>> cs_path_exists(__file__.upper()) + False + """ + if not os.path.exists(fspath): + return False + # make absolute so we always have a directory + abspath = os.path.abspath(fspath) + directory, filename = os.path.split(abspath) + return filename in os.listdir(directory) + + class sdist(Command): description = "create a source distribution (tarball, zip file, etc.)" @@ -235,7 +253,7 @@ class sdist(Command): alts = fn got_it = False for fn in alts: - if os.path.exists(fn): + if cs_path_exists(fn): got_it = True self.filelist.append(fn) break @@ -244,7 +262,7 @@ class sdist(Command): self.warn("standard file not found: should have one of " + ', '.join(alts)) else: - if os.path.exists(fn): + if cs_path_exists(fn): self.filelist.append(fn) else: self.warn("standard file '%s' not found" % fn) -- cgit v1.2.1 From ed0dd7aea6253700672dc87b3abe63b3b52b63e3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 15:41:42 -0400 Subject: Make cs_path_exists a protected, static method --- command/sdist.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/command/sdist.py b/command/sdist.py index 0cc01192..180e2862 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -33,23 +33,6 @@ def show_formats(): "List of available source distribution formats:") -def cs_path_exists(fspath): - """ - Case-sensitive path existence check - - >>> cs_path_exists(__file__) - True - >>> cs_path_exists(__file__.upper()) - False - """ - if not os.path.exists(fspath): - return False - # make absolute so we always have a directory - abspath = os.path.abspath(fspath) - directory, filename = os.path.split(abspath) - return filename in os.listdir(directory) - - class sdist(Command): description = "create a source distribution (tarball, zip file, etc.)" @@ -246,6 +229,23 @@ class sdist(Command): self._add_defaults_c_libs() self._add_defaults_scripts() + @staticmethod + def _cs_path_exists(fspath): + """ + Case-sensitive path existence check + + >>> sdist._cs_path_exists(__file__) + True + >>> sdist._cs_path_exists(__file__.upper()) + False + """ + if not os.path.exists(fspath): + return False + # make absolute so we always have a directory + abspath = os.path.abspath(fspath) + directory, filename = os.path.split(abspath) + return filename in os.listdir(directory) + def _add_defaults_standards(self): standards = [self.READMES, self.distribution.script_name] for fn in standards: @@ -253,7 +253,7 @@ class sdist(Command): alts = fn got_it = False for fn in alts: - if cs_path_exists(fn): + if self._cs_path_exists(fn): got_it = True self.filelist.append(fn) break @@ -262,7 +262,7 @@ class sdist(Command): self.warn("standard file not found: should have one of " + ', '.join(alts)) else: - if cs_path_exists(fn): + if self._cs_path_exists(fn): self.filelist.append(fn) else: self.warn("standard file '%s' not found" % fn) -- cgit v1.2.1 From 2a8668d01cd6c522ce9b7af402bbd7e32a926cc3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Oct 2016 22:56:14 +0300 Subject: Some distutils tests require zlib for creating tar.gz source distribution. --- tests/test_bdist_rpm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index 25c14abd..e9795ee4 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -3,9 +3,7 @@ import unittest import sys import os -import tempfile -import shutil -from test.support import run_unittest +from test.support import run_unittest, requires_zlib from distutils.core import Distribution from distutils.command.bdist_rpm import bdist_rpm @@ -48,6 +46,7 @@ class BuildRpmTestCase(support.TempdirManager, # spurious sdtout/stderr output under Mac OS X @unittest.skipUnless(sys.platform.startswith('linux'), 'spurious sdtout/stderr output under Mac OS X') + @requires_zlib @unittest.skipIf(find_executable('rpm') is None, 'the rpm command is not found') @unittest.skipIf(find_executable('rpmbuild') is None, @@ -90,6 +89,7 @@ class BuildRpmTestCase(support.TempdirManager, # spurious sdtout/stderr output under Mac OS X @unittest.skipUnless(sys.platform.startswith('linux'), 'spurious sdtout/stderr output under Mac OS X') + @requires_zlib # http://bugs.python.org/issue1533164 @unittest.skipIf(find_executable('rpm') is None, 'the rpm command is not found') -- cgit v1.2.1 From 874f6ddbb74da00b9b323721cd79509fc1d5487b Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Thu, 17 Nov 2016 09:00:19 +0100 Subject: Issue 26931: Skip the test_distutils tests using a compiler executable that is not found --- tests/test_build_clib.py | 20 ++++++-------------- tests/test_build_ext.py | 6 ++++++ tests/test_config_cmd.py | 5 ++++- tests/test_install.py | 4 ++++ tests/test_sysconfig.py | 9 --------- 5 files changed, 20 insertions(+), 24 deletions(-) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index acc99e78..85d09906 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -3,7 +3,7 @@ import unittest import os import sys -from test.support import run_unittest +from test.support import run_unittest, missing_compiler_executable from distutils.command.build_clib import build_clib from distutils.errors import DistutilsSetupError @@ -116,19 +116,11 @@ class BuildCLibTestCase(support.TempdirManager, cmd.build_temp = build_temp cmd.build_clib = build_temp - # before we run the command, we want to make sure - # all commands are present on the system - # by creating a compiler and checking its executables - from distutils.ccompiler import new_compiler - from distutils.sysconfig import customize_compiler - - compiler = new_compiler() - customize_compiler(compiler) - for ccmd in compiler.executables.values(): - if ccmd is None: - continue - if find_executable(ccmd[0]) is None: - self.skipTest('The %r command is not found' % ccmd[0]) + # Before we run the command, we want to make sure + # all commands are present on the system. + ccmd = missing_compiler_executable() + if ccmd is not None: + self.skipTest('The %r command is not found' % ccmd) # this should work cmd.run() diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 6be0ca23..be7f5f38 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -41,6 +41,9 @@ class BuildExtTestCase(TempdirManager, return build_ext(*args, **kwargs) def test_build_ext(self): + cmd = support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) global ALREADY_TESTED copy_xxmodule_c(self.tmp_dir) xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') @@ -295,6 +298,9 @@ class BuildExtTestCase(TempdirManager, self.assertEqual(cmd.compiler, 'unix') def test_get_outputs(self): + cmd = support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') self.write_file(c_file, 'void PyInit_foo(void) {}\n') diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 0c8dbd82..6e566e79 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -2,7 +2,7 @@ import unittest import os import sys -from test.support import run_unittest +from test.support import run_unittest, missing_compiler_executable from distutils.command.config import dump_file, config from distutils.tests import support @@ -39,6 +39,9 @@ class ConfigTestCase(support.LoggingSilencer, @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_search_cpp(self): + cmd = missing_compiler_executable(['preprocessor']) + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) pkg_dir, dist = self.create_dist() cmd = config(dist) diff --git a/tests/test_install.py b/tests/test_install.py index 9313330e..287ab198 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -17,6 +17,7 @@ from distutils.errors import DistutilsOptionError from distutils.extension import Extension from distutils.tests import support +from test import support as test_support def _make_ext_name(modname): @@ -196,6 +197,9 @@ class InstallTestCase(support.TempdirManager, self.assertEqual(found, expected) def test_record_extensions(self): + cmd = test_support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) install_dir = self.mkdtemp() project_dir, dist = self.create_dist(ext_modules=[ Extension('xx', ['xxmodule.c'])]) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index fc4d1de1..fe4a2994 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -39,15 +39,6 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): self.assertNotEqual(sysconfig.get_python_lib(), sysconfig.get_python_lib(prefix=TESTFN)) - def test_get_python_inc(self): - inc_dir = sysconfig.get_python_inc() - # This is not much of a test. We make sure Python.h exists - # in the directory returned by get_python_inc() but we don't know - # it is the correct file. - self.assertTrue(os.path.isdir(inc_dir), inc_dir) - python_h = os.path.join(inc_dir, "Python.h") - self.assertTrue(os.path.isfile(python_h), python_h) - def test_get_config_vars(self): cvars = sysconfig.get_config_vars() self.assertIsInstance(cvars, dict) -- cgit v1.2.1 From f63a2b9c7a165de24a4a5b4b8c88a298d45dddad Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 13 Dec 2016 09:06:24 -0800 Subject: Issue #26071: Fixes preprocessor definition and rebuilds wininst-14.0[-amd64].exe --- command/wininst-14.0-amd64.exe | Bin 589824 -> 587776 bytes command/wininst-14.0.exe | Bin 460288 -> 458240 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/command/wininst-14.0-amd64.exe b/command/wininst-14.0-amd64.exe index 22299543..253c2e2e 100644 Binary files a/command/wininst-14.0-amd64.exe and b/command/wininst-14.0-amd64.exe differ diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe index 0dac1103..46f5f356 100644 Binary files a/command/wininst-14.0.exe and b/command/wininst-14.0.exe differ -- cgit v1.2.1 From 5b38d9b863e3a86202da4816f7ef9b67b871a3a3 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Sun, 18 Dec 2016 01:23:09 +0000 Subject: Fix spelling and grammar in code comments and documentation --- tests/test_bdist_rpm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py index e9795ee4..c5962ddd 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -96,7 +96,7 @@ class BuildRpmTestCase(support.TempdirManager, @unittest.skipIf(find_executable('rpmbuild') is None, 'the rpmbuild command is not found') def test_no_optimize_flag(self): - # let's create a package that brakes bdist_rpm + # let's create a package that breaks bdist_rpm tmp_dir = self.mkdtemp() os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation pkg_dir = os.path.join(tmp_dir, 'foo') -- cgit v1.2.1 From 4499210cb8c3c947708479e4bd9d10370aabaf83 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Wed, 1 Feb 2017 04:42:48 +0300 Subject: Issue #29218: Remove unused install_misc command It has been documented as unused since 6c6844a2fa30 (2000-05-25) Patch by Eric N. Vander Weele. --- cmd.py | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/cmd.py b/cmd.py index 939f7959..dba3191e 100644 --- a/cmd.py +++ b/cmd.py @@ -401,34 +401,3 @@ class Command: # Otherwise, print the "skip" message else: log.debug(skip_msg) - -# XXX 'install_misc' class not currently used -- it was the base class for -# both 'install_scripts' and 'install_data', but they outgrew it. It might -# still be useful for 'install_headers', though, so I'm keeping it around -# for the time being. - -class install_misc(Command): - """Common base class for installing some files in a subdirectory. - Currently used by install_data and install_scripts. - """ - - user_options = [('install-dir=', 'd', "directory to install the files to")] - - def initialize_options (self): - self.install_dir = None - self.outfiles = [] - - def _install_dir_from(self, dirname): - self.set_undefined_options('install', (dirname, 'install_dir')) - - def _copy_files(self, filelist): - self.outfiles = [] - if not filelist: - return - self.mkpath(self.install_dir) - for f in filelist: - self.copy_file(f, self.install_dir) - self.outfiles.append(os.path.join(self.install_dir, f)) - - def get_outputs(self): - return self.outfiles -- cgit v1.2.1 From 7bb68f652d75c6a8585c95a75757ef6ca0b7d151 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Fri, 14 Apr 2017 04:00:25 -0500 Subject: bpo-11913: Add README.rst to the distutils standard READMEs list (#563) --- command/sdist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/sdist.py b/command/sdist.py index 180e2862..52eaa15d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -96,7 +96,7 @@ class sdist(Command): sub_commands = [('check', checking_metadata)] - READMES = 'README', 'README.txt' + READMES = ('README', 'README.txt', 'README.rst') def initialize_options(self): # 'template' and 'manifest' are, respectively, the names of -- cgit v1.2.1 From 9381f65a8ab5cd89e262e60bdac83834e1eafd22 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 25 Apr 2017 02:11:09 +0200 Subject: bpo-30132: distutils test_build_ext() uses temp_cwd() (#1278) test_build_ext() of test_distutils now uses support.temp_cwd() to prevent the creation of a pdb file in the current working directory on Windows. --- tests/test_build_ext.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index be7f5f38..96e5f030 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -41,6 +41,13 @@ class BuildExtTestCase(TempdirManager, return build_ext(*args, **kwargs) def test_build_ext(self): + # bpo-30132: On Windows, a .pdb file may be created in the current + # working directory. Create a temporary working directory to cleanup + # everything at the end of the test. + with support.temp_cwd(): + self._test_build_ext() + + def _test_build_ext(self): cmd = support.missing_compiler_executable() if cmd is not None: self.skipTest('The %r command is not found' % cmd) -- cgit v1.2.1 From 28091832dcb224724ef4d89eaa042ff6fea00746 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 2 May 2017 13:11:50 +0200 Subject: bpo-30132: distutils BuildExtTestCase use temp_cwd (#1380) BuildExtTestCase of test_distutils now uses support.temp_cwd() in setUp() to remove files created in the current working in all BuildExtTestCase unit tests, not only test_build_ext(). Move also tearDown() just after setUp(). --- tests/test_build_ext.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 96e5f030..a7221827 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -37,17 +37,28 @@ class BuildExtTestCase(TempdirManager, from distutils.command import build_ext build_ext.USER_BASE = site.USER_BASE - def build_ext(self, *args, **kwargs): - return build_ext(*args, **kwargs) - - def test_build_ext(self): # bpo-30132: On Windows, a .pdb file may be created in the current # working directory. Create a temporary working directory to cleanup # everything at the end of the test. - with support.temp_cwd(): - self._test_build_ext() + self.temp_cwd = support.temp_cwd() + self.temp_cwd.__enter__() + self.addCleanup(self.temp_cwd.__exit__, None, None, None) + + def tearDown(self): + # Get everything back to normal + support.unload('xx') + sys.path = self.sys_path[0] + sys.path[:] = self.sys_path[1] + import site + site.USER_BASE = self.old_user_base + from distutils.command import build_ext + build_ext.USER_BASE = self.old_user_base + super(BuildExtTestCase, self).tearDown() - def _test_build_ext(self): + def build_ext(self, *args, **kwargs): + return build_ext(*args, **kwargs) + + def test_build_ext(self): cmd = support.missing_compiler_executable() if cmd is not None: self.skipTest('The %r command is not found' % cmd) @@ -91,17 +102,6 @@ class BuildExtTestCase(TempdirManager, self.assertIsInstance(xx.Null(), xx.Null) self.assertIsInstance(xx.Str(), xx.Str) - def tearDown(self): - # Get everything back to normal - support.unload('xx') - sys.path = self.sys_path[0] - sys.path[:] = self.sys_path[1] - import site - site.USER_BASE = self.old_user_base - from distutils.command import build_ext - build_ext.USER_BASE = self.old_user_base - super(BuildExtTestCase, self).tearDown() - def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) cmd = self.build_ext(dist) -- cgit v1.2.1 From 37a5021a1e623c5edf1a6a01ba71d967c4b1a4d2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 4 May 2017 23:29:09 +0200 Subject: bpo-30273: Update sysconfig (#1464) The AST_H_DIR variable was removed from Makefile.pre.in by the commit a5c62a8e9f0de6c4133825a5710984a3cd5e102b (bpo-23404). AST_H_DIR was hardcoded to "Include", so replace the removed variable by its content. Remove also ASDLGEN variable from sysconfig example since this variable was also removed. --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 8bf1a701..90004ace 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -97,7 +97,7 @@ def get_python_inc(plat_specific=0, prefix=None): if plat_specific: return base if _sys_home: - incdir = os.path.join(_sys_home, get_config_var('AST_H_DIR')) + incdir = os.path.join(_sys_home, 'Include') else: incdir = os.path.join(get_config_var('srcdir'), 'Include') return os.path.normpath(incdir) -- cgit v1.2.1 From b19c8614bc78fef2b48148e4ec4ea46274320416 Mon Sep 17 00:00:00 2001 From: Jeremy Kloth Date: Tue, 9 May 2017 09:24:13 -0600 Subject: bpo-30273: update distutils.sysconfig for venv's created from Python (#1515) compiled out-of-tree (builddir != srcdir). (see also bpo-15366) --- sysconfig.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 90004ace..2bcd1dd2 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -93,14 +93,11 @@ def get_python_inc(plat_specific=0, prefix=None): # the build directory may not be the source directory, we # must use "srcdir" from the makefile to find the "Include" # directory. - base = _sys_home or project_base if plat_specific: - return base - if _sys_home: - incdir = os.path.join(_sys_home, 'Include') + return _sys_home or project_base else: incdir = os.path.join(get_config_var('srcdir'), 'Include') - return os.path.normpath(incdir) + return os.path.normpath(incdir) python_dir = 'python' + get_python_version() + build_flags return os.path.join(prefix, "include", python_dir) elif os.name == "nt": -- cgit v1.2.1 From 252748ea7dc284747562b5de212aba51d79a2479 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Thu, 18 May 2017 07:35:54 -0700 Subject: bpo-30296 Remove unnecessary tuples, lists, sets, and dicts (#1489) * Replaced list() with list comprehension * Replaced dict() with dict comprehension * Replaced set() with set literal * Replaced builtin func() with func() when supported (e.g. any(), all(), tuple(), min(), & max()) --- msvc9compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 21191276..c401ddc8 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -255,7 +255,7 @@ def query_vcvarsall(version, arch="x86"): """Launch vcvarsall.bat and read the settings from its environment """ vcvarsall = find_vcvarsall(version) - interesting = set(("include", "lib", "libpath", "path")) + interesting = {"include", "lib", "libpath", "path"} result = {} if vcvarsall is None: -- cgit v1.2.1 From c3052243e0aa7e756645a116fef7ed9ff09bfd5e Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 4 Sep 2017 16:36:05 -0700 Subject: remove IRIX support (closes bpo-31341) (#3310) See PEP 11. --- tests/test_unixccompiler.py | 8 -------- unixccompiler.py | 2 -- util.py | 19 +++++++------------ 3 files changed, 7 insertions(+), 22 deletions(-) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index efba27e1..eef702cf 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -51,14 +51,6 @@ class UnixCCompilerTestCase(unittest.TestCase): sysconfig.get_config_var = old_gcv - # irix646 - sys.platform = 'irix646' - self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo']) - - # osf1V5 - sys.platform = 'osf1V5' - self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo']) - # GCC GNULD sys.platform = 'bar' def gcv(v): diff --git a/unixccompiler.py b/unixccompiler.py index 3f321c28..ab4d4de1 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -233,8 +233,6 @@ class UnixCCompiler(CCompiler): if self._is_gcc(compiler): return ["-Wl,+s", "-L" + dir] return ["+s", "-L" + dir] - elif sys.platform[:7] == "irix646" or sys.platform[:6] == "osf1V5": - return ["-rpath", dir] else: if self._is_gcc(compiler): # gcc on non-GNU systems does not need -Wl, but can diff --git a/util.py b/util.py index fdcf6fab..b8a69114 100644 --- a/util.py +++ b/util.py @@ -16,21 +16,17 @@ from distutils import log from distutils.errors import DistutilsByteCompileError def get_platform (): - """Return a string that identifies the current platform. This is used - mainly to distinguish platform-specific build directories and - platform-specific built distributions. Typically includes the OS name - and version and the architecture (as supplied by 'os.uname()'), - although the exact information included depends on the OS; eg. for IRIX - the architecture isn't particularly important (IRIX only runs on SGI - hardware), but for Linux the kernel version isn't particularly - important. + """Return a string that identifies the current platform. This is used mainly to + distinguish platform-specific build directories and platform-specific built + distributions. Typically includes the OS name and version and the + architecture (as supplied by 'os.uname()'), although the exact information + included depends on the OS; eg. on Linux, the kernel version isn't + particularly important. Examples of returned values: linux-i586 linux-alpha (?) solaris-2.6-sun4u - irix-5.3 - irix64-6.2 Windows will return one of: win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) @@ -38,6 +34,7 @@ def get_platform (): win32 (all others - specifically, sys.platform is returned) For other non-POSIX platforms, currently just returns 'sys.platform'. + """ if os.name == 'nt': # sniff sys.version for architecture. @@ -87,8 +84,6 @@ def get_platform (): bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} machine += ".%s" % bitness[sys.maxsize] # fall through to standard osname-release-machine representation - elif osname[:4] == "irix": # could be "irix64"! - return "%s-%s" % (osname, release) elif osname[:3] == "aix": return "%s-%s.%s" % (osname, version, release) elif osname[:6] == "cygwin": -- cgit v1.2.1 From 91a97f1145f3d78667b162907af985b8d6fcc61a Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 6 Sep 2017 10:01:38 -0700 Subject: bpo-31340: Change to building with MSVC v141 (included with Visual Studio 2017) (#3311) --- command/bdist_wininst.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index d3e1d3af..6309c3e2 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -318,26 +318,30 @@ class bdist_wininst(Command): # string compares seem wrong, but are what sysconfig.py itself uses if self.target_version and self.target_version < cur_version: if self.target_version < "2.4": - bv = 6.0 + bv = '6.0' elif self.target_version == "2.4": - bv = 7.1 + bv = '7.1' elif self.target_version == "2.5": - bv = 8.0 + bv = '8.0' elif self.target_version <= "3.2": - bv = 9.0 + bv = '9.0' elif self.target_version <= "3.4": - bv = 10.0 + bv = '10.0' else: - bv = 14.0 + bv = '14.0' else: # for current version - use authoritative check. try: from msvcrt import CRT_ASSEMBLY_VERSION except ImportError: # cross-building, so assume the latest version - bv = 14.0 + bv = '14.0' else: - bv = float('.'.join(CRT_ASSEMBLY_VERSION.split('.', 2)[:2])) + bv = '.'.join(CRT_ASSEMBLY_VERSION.split('.', 2)[:2]) + if bv == '14.11': + # v141 and v140 are binary compatible, + # so keep using the 14.0 stub. + bv = '14.0' # wininst-x.y.exe is in the same directory as this file @@ -353,7 +357,7 @@ class bdist_wininst(Command): else: sfix = '' - filename = os.path.join(directory, "wininst-%.1f%s.exe" % (bv, sfix)) + filename = os.path.join(directory, "wininst-%s%s.exe" % (bv, sfix)) f = open(filename, "rb") try: return f.read() -- cgit v1.2.1 From 3a5968943a1722688994990326d2009210179e9d Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Wed, 6 Sep 2017 15:45:25 -0700 Subject: Remove all mention of Windows IA-64 support (GH-3389) It was mostly removed long ago. --- command/build_ext.py | 2 +- msvc9compiler.py | 4 +--- msvccompiler.py | 2 +- tests/test_util.py | 7 ------- util.py | 12 +----------- 5 files changed, 4 insertions(+), 23 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 9155626a..74445655 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -208,7 +208,7 @@ class build_ext(Command): if self.plat_name == 'win32': suffix = 'win32' else: - # win-amd64 or win-ia64 + # win-amd64 suffix = self.plat_name[4:] new_lib = os.path.join(sys.exec_prefix, 'PCbuild') if suffix: diff --git a/msvc9compiler.py b/msvc9compiler.py index c401ddc8..4c0036a0 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -55,7 +55,6 @@ else: PLAT_TO_VCVARS = { 'win32' : 'x86', 'win-amd64' : 'amd64', - 'win-ia64' : 'ia64', } class Reg: @@ -344,7 +343,7 @@ class MSVCCompiler(CCompiler) : if plat_name is None: plat_name = get_platform() # sanity check for platforms to prevent obscure errors later. - ok_plats = 'win32', 'win-amd64', 'win-ia64' + ok_plats = 'win32', 'win-amd64' if plat_name not in ok_plats: raise DistutilsPlatformError("--plat-name must be one of %s" % (ok_plats,)) @@ -362,7 +361,6 @@ class MSVCCompiler(CCompiler) : # to cross compile, you use 'x86_amd64'. # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross # compile use 'x86' (ie, it runs the x86 compiler directly) - # No idea how itanium handles this, if at all. if plat_name == get_platform() or plat_name == 'win32': # native build or cross-compile to win32 plat_spec = PLAT_TO_VCVARS[plat_name] diff --git a/msvccompiler.py b/msvccompiler.py index 1048cd41..d1de2fbf 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -172,7 +172,7 @@ def get_build_version(): def get_build_architecture(): """Return the processor architecture. - Possible results are "Intel", "Itanium", or "AMD64". + Possible results are "Intel" or "AMD64". """ prefix = " bit (" diff --git a/tests/test_util.py b/tests/test_util.py index 4e9d79b7..e2fc3809 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -78,13 +78,6 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): sys.platform = 'win32' self.assertEqual(get_platform(), 'win-amd64') - # windows XP, itanium - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Itanium)]') - sys.platform = 'win32' - self.assertEqual(get_platform(), 'win-ia64') - # macbook os.name = 'posix' sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' diff --git a/util.py b/util.py index b8a69114..9394c1e0 100644 --- a/util.py +++ b/util.py @@ -30,24 +30,14 @@ def get_platform (): Windows will return one of: win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) - win-ia64 (64bit Windows on Itanium) win32 (all others - specifically, sys.platform is returned) For other non-POSIX platforms, currently just returns 'sys.platform'. """ if os.name == 'nt': - # sniff sys.version for architecture. - prefix = " bit (" - i = sys.version.find(prefix) - if i == -1: - return sys.platform - j = sys.version.find(")", i) - look = sys.version[i+len(prefix):j].lower() - if look == 'amd64': + if 'amd64' in sys.version.lower(): return 'win-amd64' - if look == 'itanium': - return 'win-ia64' return sys.platform # Set for cross builds explicitly -- cgit v1.2.1 From d3c1d635ce32d35fb3b520d1ab009e401c7c1722 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 7 Sep 2017 11:49:23 -0700 Subject: bpo-30389 Adds detection of VS 2017 to distutils._msvccompiler (#1632) --- _msvccompiler.py | 95 +++++++++++++++++++++++++++++++++------------- tests/test_msvccompiler.py | 28 +++++++++++++- 2 files changed, 94 insertions(+), 29 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index b120273f..ef1356b9 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -17,6 +17,7 @@ import os import shutil import stat import subprocess +import winreg from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -24,10 +25,9 @@ from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log from distutils.util import get_platform -import winreg from itertools import count -def _find_vcvarsall(plat_spec): +def _find_vc2015(): try: key = winreg.OpenKeyEx( winreg.HKEY_LOCAL_MACHINE, @@ -38,9 +38,9 @@ def _find_vcvarsall(plat_spec): log.debug("Visual C++ is not registered") return None, None + best_version = 0 + best_dir = None with key: - best_version = 0 - best_dir = None for i in count(): try: v, vc_dir, vt = winreg.EnumValue(key, i) @@ -53,25 +53,74 @@ def _find_vcvarsall(plat_spec): continue if version >= 14 and version > best_version: best_version, best_dir = version, vc_dir - if not best_version: - log.debug("No suitable Visual C++ version found") - return None, None + return best_version, best_dir + +def _find_vc2017(): + import _findvs + import threading + + best_version = 0, # tuple for full version comparisons + best_dir = None + + # We need to call findall() on its own thread because it will + # initialize COM. + all_packages = [] + def _getall(): + all_packages.extend(_findvs.findall()) + t = threading.Thread(target=_getall) + t.start() + t.join() + + for name, version_str, path, packages in all_packages: + if 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' in packages: + vc_dir = os.path.join(path, 'VC', 'Auxiliary', 'Build') + if not os.path.isdir(vc_dir): + continue + try: + version = tuple(int(i) for i in version_str.split('.')) + except (ValueError, TypeError): + continue + if version > best_version: + best_version, best_dir = version, vc_dir + try: + best_version = best_version[0] + except IndexError: + best_version = None + return best_version, best_dir - vcvarsall = os.path.join(best_dir, "vcvarsall.bat") - if not os.path.isfile(vcvarsall): - log.debug("%s cannot be found", vcvarsall) - return None, None +def _find_vcvarsall(plat_spec): + best_version, best_dir = _find_vc2017() + vcruntime = None + vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86' + if best_version: + vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**", + "Microsoft.VC141.CRT", "vcruntime140.dll") + try: + import glob + vcruntime = glob.glob(vcredist, recursive=True)[-1] + except (ImportError, OSError, LookupError): + vcruntime = None + + if not best_version: + best_version, best_dir = _find_vc2015() + if best_version: + vcruntime = os.path.join(best_dir, 'redist', vcruntime_plat, + "Microsoft.VC140.CRT", "vcruntime140.dll") + + if not best_version: + log.debug("No suitable Visual C++ version found") + return None, None + vcvarsall = os.path.join(best_dir, "vcvarsall.bat") + if not os.path.isfile(vcvarsall): + log.debug("%s cannot be found", vcvarsall) + return None, None + + if not vcruntime or not os.path.isfile(vcruntime): + log.debug("%s cannot be found", vcruntime) vcruntime = None - vcruntime_spec = _VCVARS_PLAT_TO_VCRUNTIME_REDIST.get(plat_spec) - if vcruntime_spec: - vcruntime = os.path.join(best_dir, - vcruntime_spec.format(best_version)) - if not os.path.isfile(vcruntime): - log.debug("%s cannot be found", vcruntime) - vcruntime = None - return vcvarsall, vcruntime + return vcvarsall, vcruntime def _get_vc_env(plat_spec): if os.getenv("DISTUTILS_USE_SDK"): @@ -130,14 +179,6 @@ PLAT_TO_VCVARS = { 'win-amd64' : 'x86_amd64', } -# A map keyed by get_platform() return values to the file under -# the VC install directory containing the vcruntime redistributable. -_VCVARS_PLAT_TO_VCRUNTIME_REDIST = { - 'x86' : 'redist\\x86\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll', - 'amd64' : 'redist\\x64\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll', - 'x86_amd64' : 'redist\\x64\\Microsoft.VC{0}0.CRT\\vcruntime{0}0.dll', -} - # A set containing the DLLs that are guaranteed to be available for # all micro versions of this Python version. Known extension # dependencies that are not in this set will be copied to the output diff --git a/tests/test_msvccompiler.py b/tests/test_msvccompiler.py index 4dc24886..70a9c93a 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -76,12 +76,12 @@ class msvccompilerTestCase(support.TempdirManager, compiler = _msvccompiler.MSVCCompiler() compiler.initialize() dll = compiler._vcruntime_redist - self.assertTrue(os.path.isfile(dll)) + self.assertTrue(os.path.isfile(dll), dll or "") compiler._copy_vcruntime(tempdir) self.assertFalse(os.path.isfile(os.path.join( - tempdir, os.path.basename(dll)))) + tempdir, os.path.basename(dll))), dll or "") def test_get_vc_env_unicode(self): import distutils._msvccompiler as _msvccompiler @@ -101,6 +101,30 @@ class msvccompilerTestCase(support.TempdirManager, if old_distutils_use_sdk: os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk + def test_get_vc2017(self): + import distutils._msvccompiler as _msvccompiler + + # This function cannot be mocked, so pass it if we find VS 2017 + # and mark it skipped if we do not. + version, path = _msvccompiler._find_vc2017() + if version: + self.assertGreaterEqual(version, 15) + self.assertTrue(os.path.isdir(path)) + else: + raise unittest.SkipTest("VS 2017 is not installed") + + def test_get_vc2015(self): + import distutils._msvccompiler as _msvccompiler + + # This function cannot be mocked, so pass it if we find VS 2015 + # and mark it skipped if we do not. + version, path = _msvccompiler._find_vc2015() + if version: + self.assertGreaterEqual(version, 14) + self.assertTrue(os.path.isdir(path)) + else: + raise unittest.SkipTest("VS 2015 is not installed") + def test_suite(): return unittest.makeSuite(msvccompilerTestCase) -- cgit v1.2.1 From bc85ee971b84a177d06751a7b4075debecd604a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gr=C3=B6nke?= Date: Mon, 25 Sep 2017 18:58:10 +0200 Subject: bpo-31569: correct PCBuild/ case to PCbuild/ in build scripts and docs (GH-3711) --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 2bcd1dd2..e07a6c8b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -23,7 +23,7 @@ BASE_PREFIX = os.path.normpath(sys.base_prefix) BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) # Path to the base directory of the project. On Windows the binary may -# live in project/PCBuild/win32 or project/PCBuild/amd64. +# live in project/PCbuild/win32 or project/PCbuild/amd64. # set for cross builds if "_PYTHON_PROJECT_BASE" in os.environ: project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) -- cgit v1.2.1 From 6b18d32f779cf78bf0b0384277d52d8bf803d621 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 28 Sep 2017 22:44:27 -0700 Subject: remove support for BSD/OS (closes bpo-31624) (#3812) --- util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util.py b/util.py index 9394c1e0..83682628 100644 --- a/util.py +++ b/util.py @@ -53,8 +53,8 @@ def get_platform (): (osname, host, release, version, machine) = os.uname() - # Convert the OS name to lowercase, remove '/' characters - # (to accommodate BSD/OS), and translate spaces (for "Power Macintosh") + # Convert the OS name to lowercase, remove '/' characters, and translate + # spaces (for "Power Macintosh") osname = osname.lower().replace('/', '') machine = machine.replace(' ', '_') machine = machine.replace('/', '-') -- cgit v1.2.1 From 65cf5a85b0b59d3bab1f4b4e153557c6484ae825 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 25 Oct 2017 23:55:14 -0700 Subject: fixes bpo-31866: remove code pertaining to AtheOS support (#4115) We stop support this OS in 2007 with commit 19fab761b71a1687aee3415db3a937b5ce31975d. Let's finish. --- command/build_ext.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 74445655..8fad9cdc 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -215,9 +215,9 @@ class build_ext(Command): new_lib = os.path.join(new_lib, suffix) self.library_dirs.append(new_lib) - # for extensions under Cygwin and AtheOS Python's library directory must be + # For extensions under Cygwin, Python's library directory must be # appended to library_dirs - if sys.platform[:6] == 'cygwin' or sys.platform[:6] == 'atheos': + if sys.platform[:6] == 'cygwin': if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", @@ -715,22 +715,6 @@ class build_ext(Command): return ext.libraries + [pythonlib] else: return ext.libraries - elif sys.platform[:6] == "atheos": - from distutils import sysconfig - - template = "python%d.%d" - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - # Get SHLIBS from Makefile - extra = [] - for lib in sysconfig.get_config_var('SHLIBS').split(): - if lib.startswith('-l'): - extra.append(lib[2:]) - else: - extra.append(lib) - # don't extend ext.libraries, it may be shared with other - # extensions, it is a reference to the original list - return ext.libraries + [pythonlib, "m"] + extra elif sys.platform == 'darwin': # Don't use the default code below return ext.libraries -- cgit v1.2.1 From 50a9c568d04fda21590254e60a27227b9a6eb650 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 8 Nov 2017 14:44:44 -0800 Subject: Replace KB unit with KiB (#4293) kB (*kilo* byte) unit means 1000 bytes, whereas KiB ("kibibyte") means 1024 bytes. KB was misused: replace kB or KB with KiB when appropriate. Same change for MB and GB which become MiB and GiB. Change the output of Tools/iobench/iobench.py. Round also the size of the documentation from 5.5 MB to 5 MiB. --- cygwinccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 1c369903..6c5d7774 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -234,8 +234,8 @@ class CygwinCCompiler(UnixCCompiler): # who wants symbols and a many times larger output file # should explicitly switch the debug mode on # otherwise we let dllwrap/ld strip the output file - # (On my machine: 10KB < stripped_file < ??100KB - # unstripped_file = stripped_file + XXX KB + # (On my machine: 10KiB < stripped_file < ??100KiB + # unstripped_file = stripped_file + XXX KiB # ( XXX=254 for a typical python extension)) if not debug: extra_preargs.append("-s") -- cgit v1.2.1 From 9d391e77e247e4718d94c3f6000d34832576d356 Mon Sep 17 00:00:00 2001 From: xdegaye Date: Sat, 18 Nov 2017 18:17:16 +0100 Subject: bpo-29185: Fix `test_distutils` failures on Android (GH-4438) * Run gzip with separate command line options (Android understands '-f9' as the name of a file). * Creation of a hard link is controled by SELinux on Android. --- tests/test_archive_util.py | 2 +- tests/test_file_util.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 02fa1e27..14ba4ca3 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -162,7 +162,7 @@ class ArchiveUtilTestCase(support.TempdirManager, # now create another tarball using `tar` tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist'] - gzip_cmd = ['gzip', '-f9', 'archive2.tar'] + gzip_cmd = ['gzip', '-f', '-9', 'archive2.tar'] old_dir = os.getcwd() os.chdir(tmpdir) try: diff --git a/tests/test_file_util.py b/tests/test_file_util.py index 03040afc..a4e2d025 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -8,7 +8,7 @@ from distutils.file_util import move_file, copy_file from distutils import log from distutils.tests import support from distutils.errors import DistutilsFileError -from test.support import run_unittest +from test.support import run_unittest, unlink class FileUtilTestCase(support.TempdirManager, unittest.TestCase): @@ -80,6 +80,14 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase): def test_copy_file_hard_link(self): with open(self.source, 'w') as f: f.write('some content') + # Check first that copy_file() will not fall back on copying the file + # instead of creating the hard link. + try: + os.link(self.source, self.target) + except OSError as e: + self.skipTest('os.link: %s' % e) + else: + unlink(self.target) st = os.stat(self.source) copy_file(self.source, self.target, link='hard') st2 = os.stat(self.source) -- cgit v1.2.1 From c644f6f0fd7b432b8ff18d03c9c646273774c75b Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Thu, 23 Nov 2017 21:34:20 +0300 Subject: bpo-19610: setup() now raises TypeError for invalid types (GH-4519) The Distribution class now explicitly raises an exception when 'classifiers', 'keywords' and 'platforms' fields are not specified as a list. --- dist.py | 26 ++++++++++++++++++++++++++ tests/test_dist.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/dist.py b/dist.py index 62a24516..78c29ede 100644 --- a/dist.py +++ b/dist.py @@ -1188,12 +1188,38 @@ class DistributionMetadata: def get_keywords(self): return self.keywords or [] + def set_keywords(self, value): + # If 'keywords' is a string, it will be converted to a list + # by Distribution.finalize_options(). To maintain backwards + # compatibility, do not raise an exception if 'keywords' is + # a string. + if not isinstance(value, (list, str)): + msg = "'keywords' should be a 'list', not %r" + raise TypeError(msg % type(value).__name__) + self.keywords = value + def get_platforms(self): return self.platforms or ["UNKNOWN"] + def set_platforms(self, value): + # If 'platforms' is a string, it will be converted to a list + # by Distribution.finalize_options(). To maintain backwards + # compatibility, do not raise an exception if 'platforms' is + # a string. + if not isinstance(value, (list, str)): + msg = "'platforms' should be a 'list', not %r" + raise TypeError(msg % type(value).__name__) + self.platforms = value + def get_classifiers(self): return self.classifiers or [] + def set_classifiers(self, value): + if not isinstance(value, list): + msg = "'classifiers' should be a 'list', not %r" + raise TypeError(msg % type(value).__name__) + self.classifiers = value + def get_download_url(self): return self.download_url or "UNKNOWN" diff --git a/tests/test_dist.py b/tests/test_dist.py index 1f104cef..50b456ec 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -195,6 +195,13 @@ class DistributionTestCase(support.LoggingSilencer, self.assertEqual(dist.metadata.platforms, ['one', 'two']) self.assertEqual(dist.metadata.keywords, ['one', 'two']) + attrs = {'keywords': 'foo bar', + 'platforms': 'foo bar'} + dist = Distribution(attrs=attrs) + dist.finalize_options() + self.assertEqual(dist.metadata.platforms, ['foo bar']) + self.assertEqual(dist.metadata.keywords, ['foo bar']) + def test_get_command_packages(self): dist = Distribution() self.assertEqual(dist.command_packages, None) @@ -338,9 +345,46 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, attrs = {'name': 'Boa', 'version': '3.0', 'classifiers': ['Programming Language :: Python :: 3']} dist = Distribution(attrs) + self.assertEqual(dist.get_classifiers(), + ['Programming Language :: Python :: 3']) meta = self.format_metadata(dist) self.assertIn('Metadata-Version: 1.1', meta) + def test_classifier_invalid_type(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'classifiers': ('Programming Language :: Python :: 3',)} + msg = "'classifiers' should be a 'list', not 'tuple'" + with self.assertRaises(TypeError, msg=msg): + Distribution(attrs) + + def test_keywords(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'keywords': ['spam', 'eggs', 'life of brian']} + dist = Distribution(attrs) + self.assertEqual(dist.get_keywords(), + ['spam', 'eggs', 'life of brian']) + + def test_keywords_invalid_type(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'keywords': ('spam', 'eggs', 'life of brian')} + msg = "'keywords' should be a 'list', not 'tuple'" + with self.assertRaises(TypeError, msg=msg): + Distribution(attrs) + + def test_platforms(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'platforms': ['GNU/Linux', 'Some Evil Platform']} + dist = Distribution(attrs) + self.assertEqual(dist.get_platforms(), + ['GNU/Linux', 'Some Evil Platform']) + + def test_platforms_invalid_types(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'platforms': ('GNU/Linux', 'Some Evil Platform')} + msg = "'platforms' should be a 'list', not 'tuple'" + with self.assertRaises(TypeError, msg=msg): + Distribution(attrs) + def test_download_url(self): attrs = {'name': 'Boa', 'version': '3.0', 'download_url': 'http://example.org/boa'} -- cgit v1.2.1 From f7194b879e22a498a573d277fbcb3b9226412f89 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 28 Nov 2017 15:30:32 +0100 Subject: bpo-32155: Bugfixes found by flake8 F841 warnings (#4608) * distutils.config: Use the PyPIRCCommand.realm attribute if set * turtledemo: wait until macOS osascript command completes to not create a zombie process * Tools/scripts/treesync.py: declare 'default_answer' and 'create_files' as globals to modify them with the command line arguments. Previously, -y, -n, -f and -a options had no effect. flake8 warning: "F841 local variable 'p' is assigned to but never used". --- config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.py b/config.py index bf8d8dd2..e66d103f 100644 --- a/config.py +++ b/config.py @@ -77,7 +77,7 @@ class PyPIRCCommand(Command): # optional params for key, default in (('repository', self.DEFAULT_REPOSITORY), - ('realm', self.DEFAULT_REALM), + ('realm', realm), ('password', None)): if config.has_option(server, key): current[key] = config.get(server, key) @@ -106,7 +106,7 @@ class PyPIRCCommand(Command): 'password': config.get(server, 'password'), 'repository': repository, 'server': server, - 'realm': self.DEFAULT_REALM} + 'realm': realm} return {} -- cgit v1.2.1 From 3c144a62210850ec965d8ebf81c0bad9558e18b0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 28 Nov 2017 23:19:26 +0100 Subject: bpo-32155: Revert distutils.config change (#4618) Revert distutils changes of the commit 696b501cd11dc429a0f661adeb598bfaf89e4112 and remove the realm variable. --- config.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/config.py b/config.py index e66d103f..2171abd6 100644 --- a/config.py +++ b/config.py @@ -51,7 +51,6 @@ class PyPIRCCommand(Command): if os.path.exists(rc): self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY - realm = self.realm or self.DEFAULT_REALM config = RawConfigParser() config.read(rc) @@ -77,7 +76,7 @@ class PyPIRCCommand(Command): # optional params for key, default in (('repository', self.DEFAULT_REPOSITORY), - ('realm', realm), + ('realm', self.DEFAULT_REALM), ('password', None)): if config.has_option(server, key): current[key] = config.get(server, key) @@ -106,7 +105,7 @@ class PyPIRCCommand(Command): 'password': config.get(server, 'password'), 'repository': repository, 'server': server, - 'realm': realm} + 'realm': self.DEFAULT_REALM} return {} -- cgit v1.2.1 From cba37f7ece298acceaa5506d24548f6ec039c9b3 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Mon, 4 Dec 2017 18:58:12 -0800 Subject: bpo-19610: Warn if distutils is provided something other than a list to some fields (#4685) * Rather than raise TypeError, warn and call list() on the value. * Fix tests, revise NEWS and whatsnew text. * Revise documentation, a string is okay as well. * Ensure 'requires' and 'obsoletes' are real lists. * Test that requires and obsoletes are turned to lists. --- dist.py | 46 ++++++++++++++++++++-------------------------- tests/test_dist.py | 48 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 58 insertions(+), 36 deletions(-) diff --git a/dist.py b/dist.py index 78c29ede..6cf0a0d6 100644 --- a/dist.py +++ b/dist.py @@ -27,6 +27,20 @@ from distutils.debug import DEBUG command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') +def _ensure_list(value, fieldname): + if isinstance(value, str): + # a string containing comma separated values is okay. It will + # be converted to a list by Distribution.finalize_options(). + pass + elif not isinstance(value, list): + # passing a tuple or an iterator perhaps, warn and convert + typename = type(value).__name__ + msg = f"Warning: '{fieldname}' should be a list, got type '{typename}'" + log.log(log.WARN, msg) + value = list(value) + return value + + class Distribution: """The core of the Distutils. Most of the work hiding behind 'setup' is really done within a Distribution instance, which farms the work out @@ -257,10 +271,7 @@ Common commands: (see '--help-commands' for more) setattr(self, key, val) else: msg = "Unknown distribution option: %s" % repr(key) - if warnings is not None: - warnings.warn(msg) - else: - sys.stderr.write(msg + "\n") + warnings.warn(msg) # no-user-cfg is handled before other command line args # because other args override the config files, and this @@ -1189,36 +1200,19 @@ class DistributionMetadata: return self.keywords or [] def set_keywords(self, value): - # If 'keywords' is a string, it will be converted to a list - # by Distribution.finalize_options(). To maintain backwards - # compatibility, do not raise an exception if 'keywords' is - # a string. - if not isinstance(value, (list, str)): - msg = "'keywords' should be a 'list', not %r" - raise TypeError(msg % type(value).__name__) - self.keywords = value + self.keywords = _ensure_list(value, 'keywords') def get_platforms(self): return self.platforms or ["UNKNOWN"] def set_platforms(self, value): - # If 'platforms' is a string, it will be converted to a list - # by Distribution.finalize_options(). To maintain backwards - # compatibility, do not raise an exception if 'platforms' is - # a string. - if not isinstance(value, (list, str)): - msg = "'platforms' should be a 'list', not %r" - raise TypeError(msg % type(value).__name__) - self.platforms = value + self.platforms = _ensure_list(value, 'platforms') def get_classifiers(self): return self.classifiers or [] def set_classifiers(self, value): - if not isinstance(value, list): - msg = "'classifiers' should be a 'list', not %r" - raise TypeError(msg % type(value).__name__) - self.classifiers = value + self.classifiers = _ensure_list(value, 'classifiers') def get_download_url(self): return self.download_url or "UNKNOWN" @@ -1231,7 +1225,7 @@ class DistributionMetadata: import distutils.versionpredicate for v in value: distutils.versionpredicate.VersionPredicate(v) - self.requires = value + self.requires = list(value) def get_provides(self): return self.provides or [] @@ -1250,7 +1244,7 @@ class DistributionMetadata: import distutils.versionpredicate for v in value: distutils.versionpredicate.VersionPredicate(v) - self.obsoletes = value + self.obsoletes = list(value) def fix_help_options(options): """Convert a 4-tuple 'help_options' list as found in various command diff --git a/tests/test_dist.py b/tests/test_dist.py index 50b456ec..0a19f0fb 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -11,7 +11,9 @@ from unittest import mock from distutils.dist import Distribution, fix_help_options, DistributionMetadata from distutils.cmd import Command -from test.support import TESTFN, captured_stdout, run_unittest +from test.support import ( + TESTFN, captured_stdout, captured_stderr, run_unittest +) from distutils.tests import support from distutils import log @@ -319,6 +321,13 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, "version": "1.0", "requires": ["my.pkg (splat)"]}) + def test_requires_to_list(self): + attrs = {"name": "package", + "requires": iter(["other"])} + dist = Distribution(attrs) + self.assertIsInstance(dist.metadata.requires, list) + + def test_obsoletes(self): attrs = {"name": "package", "version": "1.0", @@ -341,6 +350,12 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, "version": "1.0", "obsoletes": ["my.pkg (splat)"]}) + def test_obsoletes_to_list(self): + attrs = {"name": "package", + "obsoletes": iter(["other"])} + dist = Distribution(attrs) + self.assertIsInstance(dist.metadata.obsoletes, list) + def test_classifier(self): attrs = {'name': 'Boa', 'version': '3.0', 'classifiers': ['Programming Language :: Python :: 3']} @@ -353,9 +368,14 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_classifier_invalid_type(self): attrs = {'name': 'Boa', 'version': '3.0', 'classifiers': ('Programming Language :: Python :: 3',)} - msg = "'classifiers' should be a 'list', not 'tuple'" - with self.assertRaises(TypeError, msg=msg): - Distribution(attrs) + with captured_stderr() as error: + d = Distribution(attrs) + # should have warning about passing a non-list + self.assertIn('should be a list', error.getvalue()) + # should be converted to a list + self.assertIsInstance(d.metadata.classifiers, list) + self.assertEqual(d.metadata.classifiers, + list(attrs['classifiers'])) def test_keywords(self): attrs = {'name': 'Monty', 'version': '1.0', @@ -367,9 +387,13 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_keywords_invalid_type(self): attrs = {'name': 'Monty', 'version': '1.0', 'keywords': ('spam', 'eggs', 'life of brian')} - msg = "'keywords' should be a 'list', not 'tuple'" - with self.assertRaises(TypeError, msg=msg): - Distribution(attrs) + with captured_stderr() as error: + d = Distribution(attrs) + # should have warning about passing a non-list + self.assertIn('should be a list', error.getvalue()) + # should be converted to a list + self.assertIsInstance(d.metadata.keywords, list) + self.assertEqual(d.metadata.keywords, list(attrs['keywords'])) def test_platforms(self): attrs = {'name': 'Monty', 'version': '1.0', @@ -381,9 +405,13 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, def test_platforms_invalid_types(self): attrs = {'name': 'Monty', 'version': '1.0', 'platforms': ('GNU/Linux', 'Some Evil Platform')} - msg = "'platforms' should be a 'list', not 'tuple'" - with self.assertRaises(TypeError, msg=msg): - Distribution(attrs) + with captured_stderr() as error: + d = Distribution(attrs) + # should have warning about passing a non-list + self.assertIn('should be a list', error.getvalue()) + # should be converted to a list + self.assertIsInstance(d.metadata.platforms, list) + self.assertEqual(d.metadata.platforms, list(attrs['platforms'])) def test_download_url(self): attrs = {'name': 'Boa', 'version': '3.0', -- cgit v1.2.1 From b0138c4df9e3d287a5a2995be53746c9d0581725 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 14 Dec 2017 11:39:34 +0100 Subject: bpo-32302: Fix distutils bdist_wininst for CRT v142 (#4851) CRT v142 is binary compatible with CRT v140. --- command/bdist_wininst.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 6309c3e2..83f00732 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -338,8 +338,8 @@ class bdist_wininst(Command): bv = '14.0' else: bv = '.'.join(CRT_ASSEMBLY_VERSION.split('.', 2)[:2]) - if bv == '14.11': - # v141 and v140 are binary compatible, + if bv in ('14.11', '14.12'): + # v142, v141 and v140 are binary compatible, # so keep using the 14.0 stub. bv = '14.0' -- cgit v1.2.1 From 9a97c496dcd05e6b3d418903a3728daa790e29cc Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 19 Jan 2018 09:09:36 +1100 Subject: bpo-32588: Move _findvs into its own module and add missing _queue module to installer (#5227) --- _msvccompiler.py | 4 ++-- command/bdist_wininst.py | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index ef1356b9..c9d3c6c6 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -56,7 +56,7 @@ def _find_vc2015(): return best_version, best_dir def _find_vc2017(): - import _findvs + import _distutils_findvs import threading best_version = 0, # tuple for full version comparisons @@ -66,7 +66,7 @@ def _find_vc2017(): # initialize COM. all_packages = [] def _getall(): - all_packages.extend(_findvs.findall()) + all_packages.extend(_distutils_findvs.findall()) t = threading.Thread(target=_getall) t.start() t.join() diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 83f00732..0871a4f7 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -337,11 +337,10 @@ class bdist_wininst(Command): # cross-building, so assume the latest version bv = '14.0' else: - bv = '.'.join(CRT_ASSEMBLY_VERSION.split('.', 2)[:2]) - if bv in ('14.11', '14.12'): - # v142, v141 and v140 are binary compatible, - # so keep using the 14.0 stub. - bv = '14.0' + # as far as we know, CRT is binary compatible based on + # the first field, so assume 'x.0' until proven otherwise + major = CRT_ASSEMBLY_VERSION.partition('.')[0] + bv = major + '.0' # wininst-x.y.exe is in the same directory as this file -- cgit v1.2.1 From b3168e5e01e79300b17c1791eed97c4733fa7a49 Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Thu, 25 Jan 2018 18:02:03 -0600 Subject: bpo-32304: Fix distutils upload for sdists ending with \x0d (GH-5264) Patch by Bo Bayles. --- command/upload.py | 2 -- tests/test_upload.py | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/command/upload.py b/command/upload.py index 1fd574a9..f7752f9d 100644 --- a/command/upload.py +++ b/command/upload.py @@ -159,8 +159,6 @@ class upload(PyPIRCCommand): body.write(title.encode('utf-8')) body.write(b"\r\n\r\n") body.write(value) - if value and value[-1:] == b'\r': - body.write(b'\n') # write an extra newline (lurve Macs) body.write(end_boundary) body = body.getvalue() diff --git a/tests/test_upload.py b/tests/test_upload.py index 2cb2f6ce..c17d8e7d 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -143,6 +143,32 @@ class uploadTestCase(BasePyPIRCCommandTestCase): results = self.get_logs(INFO) self.assertEqual(results[-1], 75 * '-' + '\nxyzzy\n' + 75 * '-') + # bpo-32304: archives whose last byte was b'\r' were corrupted due to + # normalization intended for Mac OS 9. + def test_upload_correct_cr(self): + # content that ends with \r should not be modified. + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path, content='yy\r') + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) + + # other fields that ended with \r used to be modified, now are + # preserved. + pkg_dir, dist = self.create_dist( + dist_files=dist_files, + description='long description\r' + ) + cmd = upload(dist) + cmd.show_response = 1 + cmd.ensure_finalized() + cmd.run() + + headers = dict(self.last_open.req.headers) + self.assertEqual(headers['Content-length'], '2172') + self.assertIn(b'long description\r', self.last_open.req.data) + def test_upload_fails(self): self.next_msg = "Not Found" self.next_code = 404 -- cgit v1.2.1 From f59e08fa002c62b8a9a36eed4e068d471585e565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Araujo?= Date: Sun, 18 Feb 2018 18:14:54 -0500 Subject: Improve error message for "setup.py upload" without dist files (#21060) --- command/upload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/upload.py b/command/upload.py index f7752f9d..32dda359 100644 --- a/command/upload.py +++ b/command/upload.py @@ -57,7 +57,8 @@ class upload(PyPIRCCommand): def run(self): if not self.distribution.dist_files: - msg = "No dist file created in earlier command" + msg = ("Must create and upload files in one command " + "(e.g. setup.py sdist upload)") raise DistutilsOptionError(msg) for command, pyversion, filename in self.distribution.dist_files: self.upload_file(command, pyversion, filename) -- cgit v1.2.1 From 74d0f1dc010170301dafe46faacee84bf246c880 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Mon, 16 Jul 2018 19:03:03 +0200 Subject: bpo-32430: Rename Modules/Setup.dist to Modules/Setup (GH-8229) bpo-32430: Rename Modules/Setup.dist to Modules/Setup Remove the necessity to copy the former manually to the latter when updating the local source tree. --- sysconfig.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index e07a6c8b..b433fc86 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -36,10 +36,8 @@ if (os.name == 'nt' and # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use # different (hard-wired) directories. -# Setup.local is available for Makefile builds including VPATH builds, -# Setup.dist is available on Windows def _is_python_source_dir(d): - for fn in ("Setup.dist", "Setup.local"): + for fn in ("Setup", "Setup.local"): if os.path.isfile(os.path.join(d, "Modules", fn)): return True return False -- cgit v1.2.1 From b90515f8041352a33c6ca7099be38dee0d5a5cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Fri, 20 Jul 2018 16:48:06 +0200 Subject: Make install directory if it doesn't exist yet Problem: In Fedora, `sudo python3 setup.py install` installs to /usr/local/lib/pythonX.Y/site-packages. However that directory is not always there as it is not installed together with the python3 package (Fedora packages should not install stuff to /usr/local). When the directory does not exist an installation attempt fails with: [Errno 2] No such file or directory: '/usr/local/lib/python3.7/site-packages/test-easy-install-11875.write-test' Solution: Make the directory if it doesn't exists yet. If user has no permissions, the above error is preserved, yet with root (sudo) it works now. --- changelog.d/1431.chnage.rst | 3 +++ setuptools/command/easy_install.py | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 changelog.d/1431.chnage.rst diff --git a/changelog.d/1431.chnage.rst b/changelog.d/1431.chnage.rst new file mode 100644 index 00000000..a5cb324e --- /dev/null +++ b/changelog.d/1431.chnage.rst @@ -0,0 +1,3 @@ +Make install directory if it doesn't exist yet to prevent Fedora's +specific setup to blow up if ``/usr/local/lib/pythonX.Y/site-packages`` +doesn't exist yet. diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 05508ceb..5b65b95d 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -450,6 +450,12 @@ class easy_install(Command): instdir = normalize_path(self.install_dir) pth_file = os.path.join(instdir, 'easy-install.pth') + if not os.path.exists(instdir): + try: + os.makedirs(instdir) + except (OSError, IOError): + self.cant_write_to_target() + # Is it a configured, PYTHONPATH, implicit, or explicit site dir? is_site_dir = instdir in self.all_site_dirs -- cgit v1.2.1 From 8be16112e6817cc23ee4f317bc58db5f1c7eb108 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 26 Jul 2018 04:23:10 -0700 Subject: bpo-34225: Ensure INCLUDE and LIB directories do not end with a backslash. (GH-8464) --- _msvccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index c9d3c6c6..30b3b473 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -252,11 +252,11 @@ class MSVCCompiler(CCompiler) : for dir in vc_env.get('include', '').split(os.pathsep): if dir: - self.add_include_dir(dir) + self.add_include_dir(dir.rstrip(os.sep)) for dir in vc_env.get('lib', '').split(os.pathsep): if dir: - self.add_library_dir(dir) + self.add_library_dir(dir.rstrip(os.sep)) self.preprocess_options = None # If vcruntime_redist is available, link against it dynamically. Otherwise, -- cgit v1.2.1 From 0e48f661aab7b40bbe9c8e033594f61e7e712b4a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 4 Sep 2018 11:01:09 +0200 Subject: bpo-34530: Fix distutils find_executable() (GH-9049) distutils.spawn.find_executable() now falls back on os.defpath if the PATH environment variable is not set. --- spawn.py | 2 +- tests/test_spawn.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/spawn.py b/spawn.py index 5dd415a2..53876880 100644 --- a/spawn.py +++ b/spawn.py @@ -173,7 +173,7 @@ def find_executable(executable, path=None): os.environ['PATH']. Returns the complete filename or None if not found. """ if path is None: - path = os.environ['PATH'] + path = os.environ.get('PATH', os.defpath) paths = path.split(os.pathsep) base, ext = os.path.splitext(executable) diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 5edc24a3..0d455385 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -1,9 +1,13 @@ """Tests for distutils.spawn.""" -import unittest -import sys import os +import stat +import sys +import unittest +from unittest import mock from test.support import run_unittest, unix_shell +from test import support as test_support +from distutils.spawn import find_executable from distutils.spawn import _nt_quote_args from distutils.spawn import spawn from distutils.errors import DistutilsExecError @@ -51,6 +55,47 @@ class SpawnTestCase(support.TempdirManager, os.chmod(exe, 0o777) spawn([exe]) # should work without any error + def test_find_executable(self): + with test_support.temp_dir() as tmp_dir: + # use TESTFN to get a pseudo-unique filename + program_noeext = test_support.TESTFN + # Give the temporary program an ".exe" suffix for all. + # It's needed on Windows and not harmful on other platforms. + program = program_noeext + ".exe" + + filename = os.path.join(tmp_dir, program) + with open(filename, "wb"): + pass + os.chmod(filename, stat.S_IXUSR) + + # test path parameter + rv = find_executable(program, path=tmp_dir) + self.assertEqual(rv, filename) + + if sys.platform == 'win32': + # test without ".exe" extension + rv = find_executable(program_noeext, path=tmp_dir) + self.assertEqual(rv, filename) + + # test find in the current directory + with test_support.change_cwd(tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, program) + + # test non-existent program + dont_exist_program = "dontexist_" + program + rv = find_executable(dont_exist_program , path=tmp_dir) + self.assertIsNone(rv) + + # test os.defpath: missing PATH environment variable + with test_support.EnvironmentVarGuard() as env: + with mock.patch('distutils.spawn.os.defpath', tmp_dir): + env.pop('PATH') + + rv = find_executable(program) + self.assertEqual(rv, filename) + + def test_suite(): return unittest.makeSuite(SpawnTestCase) -- cgit v1.2.1 From d2eb595732e54ff8e785483b2a47fd3d20431057 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 7 Sep 2018 17:30:33 +0200 Subject: bpo-34605: Avoid master/slave terms (GH-9101) * Replace "master process" with "parent process" * Replace "master option mappings" with "main option mappings" * Replace "master pattern object" with "main pattern object" * ssl: replace "master" with "server" * And some other similar changes --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 0258d3de..41bf4bb9 100644 --- a/command/install.py +++ b/command/install.py @@ -223,7 +223,7 @@ class install(Command): def finalize_options(self): """Finalizes options.""" - # This method (and its pliant slaves, like 'finalize_unix()', + # This method (and its pliant childs, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default # installation directories for modules, extension modules, and # anything else we care to install from a Python module -- cgit v1.2.1 From 4a6183a2492528b3072f91cd221c86bee8853b09 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 7 Sep 2018 18:13:10 +0200 Subject: bpo-34605: childs => children (GH-9102) --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 41bf4bb9..a1d1a1ea 100644 --- a/command/install.py +++ b/command/install.py @@ -223,7 +223,7 @@ class install(Command): def finalize_options(self): """Finalizes options.""" - # This method (and its pliant childs, like 'finalize_unix()', + # This method (and its pliant children, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default # installation directories for modules, extension modules, and # anything else we care to install from a Python module -- cgit v1.2.1 From 64f5d63b221c89c3ef543f4ef10a99ff0027a63c Mon Sep 17 00:00:00 2001 From: Julien Malard Date: Sun, 9 Sep 2018 02:01:26 +0530 Subject: bpo-34421 avoid unicode error in distutils logging (GH-8799) This caused installation errors in some cases on Windows. Patch by Julien Malard. --- log.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/log.py b/log.py index b301a833..3a6602bc 100644 --- a/log.py +++ b/log.py @@ -31,7 +31,10 @@ class Log: # emulate backslashreplace error handler encoding = stream.encoding msg = msg.encode(encoding, "backslashreplace").decode(encoding) - stream.write('%s\n' % msg) + try: + stream.write('%s\n' % msg) + except UnicodeEncodeError: + stream.write('%s\n' % msg.encode('unicode-escape').decode('ascii')) stream.flush() def log(self, level, msg, *args): -- cgit v1.2.1 From b277efb9be14751c874aff02f72237bad8600df3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 12 Sep 2018 01:40:06 +0200 Subject: bpo-34605: Replace "pliant children" with "helpers" (GH-9195) In distutils.command.install, replace "pliant children" (previously, it was "pliant slaves") with "helpers". https://bugs.python.org/issue34605 --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index a1d1a1ea..c625c95b 100644 --- a/command/install.py +++ b/command/install.py @@ -223,7 +223,7 @@ class install(Command): def finalize_options(self): """Finalizes options.""" - # This method (and its pliant children, like 'finalize_unix()', + # This method (and its helpers, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default # installation directories for modules, extension modules, and # anything else we care to install from a Python module -- cgit v1.2.1 From e5fede1f013671465e4023d5048d2fbab33a8932 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 20 Sep 2018 13:38:34 -0700 Subject: bpo-34011: Fixes missing venv files and other tests (GH-9458) --- tests/test_bdist.py | 3 +++ tests/test_bdist_wininst.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/tests/test_bdist.py b/tests/test_bdist.py index f762f5d9..c80b3edc 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -39,6 +39,9 @@ class BuildTestCase(support.TempdirManager, for name in names: subcmd = cmd.get_finalized_command(name) + if getattr(subcmd, '_unsupported', False): + # command is not supported on this build + continue self.assertTrue(subcmd.skip_build, '%s should take --skip-build from bdist' % name) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index 5d17ab19..4c19bbab 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -5,6 +5,8 @@ from test.support import run_unittest from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support +@unittest.skipIf(getattr(bdist_wininst, '_unsupported', False), + 'bdist_wininst is not supported in this install') class BuildWinInstTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): -- cgit v1.2.1 From 323ff835845c6b4e7e8a8e004f5ad4eaf878e4d8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Sep 2018 09:12:59 +0300 Subject: bpo-34421: Improve distutils logging for non-ASCII strings. (GH-9126) Use "backslashreplace" instead of "unicode-escape". It is not implementation depended and escapes only non-encodable characters. Also simplify the code. --- log.py | 7 +++---- tests/test_log.py | 46 ++++++++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/log.py b/log.py index 3a6602bc..8ef6b28e 100644 --- a/log.py +++ b/log.py @@ -27,14 +27,13 @@ class Log: stream = sys.stderr else: stream = sys.stdout - if stream.errors == 'strict': + try: + stream.write('%s\n' % msg) + except UnicodeEncodeError: # emulate backslashreplace error handler encoding = stream.encoding msg = msg.encode(encoding, "backslashreplace").decode(encoding) - try: stream.write('%s\n' % msg) - except UnicodeEncodeError: - stream.write('%s\n' % msg.encode('unicode-escape').decode('ascii')) stream.flush() def log(self, level, msg, *args): diff --git a/tests/test_log.py b/tests/test_log.py index 0c2ad7a4..22c26246 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -3,33 +3,39 @@ import sys import unittest from tempfile import NamedTemporaryFile -from test.support import run_unittest +from test.support import swap_attr, run_unittest from distutils import log class TestLog(unittest.TestCase): def test_non_ascii(self): - # Issue #8663: test that non-ASCII text is escaped with - # backslashreplace error handler (stream use ASCII encoding and strict - # error handler) - old_stdout = sys.stdout - old_stderr = sys.stderr - old_threshold = log.set_threshold(log.DEBUG) - try: - with NamedTemporaryFile(mode="w+", encoding='ascii') as stdout, \ - NamedTemporaryFile(mode="w+", encoding='ascii') as stderr: - sys.stdout = stdout - sys.stderr = stderr - log.debug("debug:\xe9") - log.fatal("fatal:\xe9") + # Issues #8663, #34421: test that non-encodable text is escaped with + # backslashreplace error handler and encodable non-ASCII text is + # output as is. + for errors in ('strict', 'backslashreplace', 'surrogateescape', + 'replace', 'ignore'): + with self.subTest(errors=errors), \ + NamedTemporaryFile("w+", encoding='cp437', errors=errors) as stdout, \ + NamedTemporaryFile("w+", encoding='cp437', errors=errors) as stderr: + old_threshold = log.set_threshold(log.DEBUG) + try: + with swap_attr(sys, 'stdout', stdout), \ + swap_attr(sys, 'stderr', stderr): + log.debug('Dεbug\tMÄ—ssãge') + log.fatal('Fαtal\tÈrrÅr') + finally: + log.set_threshold(old_threshold) + stdout.seek(0) - self.assertEqual(stdout.read().rstrip(), "debug:\\xe9") + self.assertEqual(stdout.read().rstrip(), + 'Dεbug\tM?ss?ge' if errors == 'replace' else + 'Dεbug\tMssge' if errors == 'ignore' else + 'Dεbug\tM\\u0117ss\\xe3ge') stderr.seek(0) - self.assertEqual(stderr.read().rstrip(), "fatal:\\xe9") - finally: - log.set_threshold(old_threshold) - sys.stdout = old_stdout - sys.stderr = old_stderr + self.assertEqual(stderr.read().rstrip(), + 'Fαtal\t?rr?r' if errors == 'replace' else + 'Fαtal\trrr' if errors == 'ignore' else + 'Fαtal\t\\xc8rr\\u014dr') def test_suite(): return unittest.makeSuite(TestLog) -- cgit v1.2.1 From a23e74ec65eaca077c3e8f21d592823c821e98f6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Sep 2018 14:10:07 +0300 Subject: Use in-memory streams instead of NamedTemporaryFile. (GH-9508) --- tests/test_log.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_log.py b/tests/test_log.py index 22c26246..75cf9006 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -1,8 +1,8 @@ """Tests for distutils.log""" +import io import sys import unittest -from tempfile import NamedTemporaryFile from test.support import swap_attr, run_unittest from distutils import log @@ -14,9 +14,11 @@ class TestLog(unittest.TestCase): # output as is. for errors in ('strict', 'backslashreplace', 'surrogateescape', 'replace', 'ignore'): - with self.subTest(errors=errors), \ - NamedTemporaryFile("w+", encoding='cp437', errors=errors) as stdout, \ - NamedTemporaryFile("w+", encoding='cp437', errors=errors) as stderr: + with self.subTest(errors=errors): + stdout = io.TextIOWrapper(io.BytesIO(), + encoding='cp437', errors=errors) + stderr = io.TextIOWrapper(io.BytesIO(), + encoding='cp437', errors=errors) old_threshold = log.set_threshold(log.DEBUG) try: with swap_attr(sys, 'stdout', stdout), \ -- cgit v1.2.1 From f45153a292dc749defaa9c6c1ce9c4062f083b6d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 27 Oct 2018 16:48:33 -0400 Subject: bpo-35067: Remove _distutils_findvs and use vswhere.exe instead. (GH-10095) --- _msvccompiler.py | 64 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index 30b3b473..84b4ef59 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -56,43 +56,43 @@ def _find_vc2015(): return best_version, best_dir def _find_vc2017(): - import _distutils_findvs - import threading + """Returns "15, path" based on the result of invoking vswhere.exe + If no install is found, returns "None, None" - best_version = 0, # tuple for full version comparisons - best_dir = None + The version is returned to avoid unnecessarily changing the function + result. It may be ignored when the path is not None. + + If vswhere.exe is not available, by definition, VS 2017 is not + installed. + """ + import json + + root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") + if not root: + return None, None - # We need to call findall() on its own thread because it will - # initialize COM. - all_packages = [] - def _getall(): - all_packages.extend(_distutils_findvs.findall()) - t = threading.Thread(target=_getall) - t.start() - t.join() - - for name, version_str, path, packages in all_packages: - if 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64' in packages: - vc_dir = os.path.join(path, 'VC', 'Auxiliary', 'Build') - if not os.path.isdir(vc_dir): - continue - try: - version = tuple(int(i) for i in version_str.split('.')) - except (ValueError, TypeError): - continue - if version > best_version: - best_version, best_dir = version, vc_dir try: - best_version = best_version[0] - except IndexError: - best_version = None - return best_version, best_dir + path = subprocess.check_output([ + os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), + "-latest", + "-prerelease", + "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "installationPath", + ], encoding="mbcs", errors="strict").strip() + except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): + return None, None + + path = os.path.join(path, "VC", "Auxiliary", "Build") + if os.path.isdir(path): + return 15, path + + return None, None def _find_vcvarsall(plat_spec): - best_version, best_dir = _find_vc2017() + _, best_dir = _find_vc2017() vcruntime = None vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86' - if best_version: + if best_dir: vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**", "Microsoft.VC141.CRT", "vcruntime140.dll") try: @@ -101,13 +101,13 @@ def _find_vcvarsall(plat_spec): except (ImportError, OSError, LookupError): vcruntime = None - if not best_version: + if not best_dir: best_version, best_dir = _find_vc2015() if best_version: vcruntime = os.path.join(best_dir, 'redist', vcruntime_plat, "Microsoft.VC140.CRT", "vcruntime140.dll") - if not best_version: + if not best_dir: log.debug("No suitable Visual C++ version found") return None, None -- cgit v1.2.1 From 76ef30913dce00de4bca773f2dda9dd25ff66ad8 Mon Sep 17 00:00:00 2001 From: Alex Hirzel Date: Sun, 28 Oct 2018 21:44:14 -0400 Subject: change find_module to find_spec for py37 compat --- pkg_resources/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 74134701..9b5bd102 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2098,10 +2098,11 @@ def _handle_ns(packageName, path_item): # capture warnings due to #1111 with warnings.catch_warnings(): warnings.simplefilter("ignore") - loader = importer.find_module(packageName) - - if loader is None: - return None + spec = importer.find_spec(packageName) + if spec is not None: + loader = spec.loader + else: + return None module = sys.modules.get(packageName) if module is None: module = sys.modules[packageName] = types.ModuleType(packageName) -- cgit v1.2.1 From eb123bbf0cdb25d855aa9a1794646d4abd309c9a Mon Sep 17 00:00:00 2001 From: Alex Hirzel Date: Sun, 28 Oct 2018 22:00:13 -0400 Subject: default to find_module, fall-back to find_spec, fail gracefully after --- pkg_resources/__init__.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 9b5bd102..2b8c4430 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2098,11 +2098,15 @@ def _handle_ns(packageName, path_item): # capture warnings due to #1111 with warnings.catch_warnings(): warnings.simplefilter("ignore") - spec = importer.find_spec(packageName) - if spec is not None: - loader = spec.loader - else: - return None + try: + loader = importer.find_module(packageName) + except AttributeError: + try: + loader = importer.find_spec(packageName).loader + except: + loader = None + if loader is None: + return None module = sys.modules.get(packageName) if module is None: module = sys.modules[packageName] = types.ModuleType(packageName) -- cgit v1.2.1 From 63185e91406d6ba02fe42c6b7e188111054f1b14 Mon Sep 17 00:00:00 2001 From: Alex Hirzel Date: Sun, 28 Oct 2018 22:03:28 -0400 Subject: add changelog entry --- changelog.d/1563.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1563.change.rst diff --git a/changelog.d/1563.change.rst b/changelog.d/1563.change.rst new file mode 100644 index 00000000..a2452cd8 --- /dev/null +++ b/changelog.d/1563.change.rst @@ -0,0 +1 @@ +If find_module fails or does not exist, attempt to use find_spec(...).loader -- cgit v1.2.1 From b841e0aececb0e28ff3fabe29bf22c4b86fae9d8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 5 Nov 2018 16:20:25 +0200 Subject: bpo-35133: Fix mistakes when concatenate string literals on different lines. (GH-10284) Two kind of mistakes: 1. Missed space. After concatenating there is no space between words. 2. Missed comma. Causes unintentional concatenating in a list of strings. --- command/bdist_dumb.py | 2 +- command/bdist_msi.py | 4 ++-- command/bdist_rpm.py | 2 +- command/bdist_wininst.py | 4 ++-- command/build_ext.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py index e9274d92..f0d6b5b8 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -32,7 +32,7 @@ class bdist_dumb(Command): ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), ('relative', None, - "build the archive using relative paths" + "build the archive using relative paths " "(default: false)"), ('owner=', 'u', "Owner name used when creating a tar file" diff --git a/command/bdist_msi.py b/command/bdist_msi.py index a4bd5a58..80104c37 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -98,14 +98,14 @@ class bdist_msi(Command): ('no-target-compile', 'c', "do not compile .py to .pyc on the target system"), ('no-target-optimize', 'o', - "do not compile .py to .pyo (optimized)" + "do not compile .py to .pyo (optimized) " "on the target system"), ('dist-dir=', 'd', "directory to put final built distributions in"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), ('install-script=', None, - "basename of installation script to be run after" + "basename of installation script to be run after " "installation or before deinstallation"), ('pre-install-script=', None, "Fully qualified filename of a script to be run before " diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index ac462179..02f10dd8 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -58,7 +58,7 @@ class bdist_rpm(Command): "RPM \"vendor\" (eg. \"Joe Blow \") " "[default: maintainer or author from setup script]"), ('packager=', None, - "RPM packager (eg. \"Jane Doe \")" + "RPM packager (eg. \"Jane Doe \") " "[default: vendor]"), ('doc-files=', None, "list of documentation files (space or comma-separated)"), diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 0871a4f7..fde56754 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -29,7 +29,7 @@ class bdist_wininst(Command): ('no-target-compile', 'c', "do not compile .py to .pyc on the target system"), ('no-target-optimize', 'o', - "do not compile .py to .pyo (optimized)" + "do not compile .py to .pyo (optimized) " "on the target system"), ('dist-dir=', 'd', "directory to put final built distributions in"), @@ -40,7 +40,7 @@ class bdist_wininst(Command): ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), ('install-script=', None, - "basename of installation script to be run after" + "basename of installation script to be run after " "installation or before deinstallation"), ('pre-install-script=', None, "Fully qualified filename of a script to be run before " diff --git a/command/build_ext.py b/command/build_ext.py index 8fad9cdc..158465d2 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -365,7 +365,7 @@ class build_ext(Command): ext_name, build_info = ext log.warn("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s'" + "ext_modules for extension '%s' " "-- please convert to Extension instance", ext_name) if not (isinstance(ext_name, str) and -- cgit v1.2.1 From 08cac8609e2340ea09057640cd1d728f79488b99 Mon Sep 17 00:00:00 2001 From: Alex Hirzel Date: Fri, 9 Nov 2018 21:04:00 -0500 Subject: updated per comments from @pganssle in #1563 --- pkg_resources/__init__.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index e6487be0..deea96b9 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2098,16 +2098,19 @@ def _handle_ns(packageName, path_item): if importer is None: return None - # capture warnings due to #1111 - with warnings.catch_warnings(): - warnings.simplefilter("ignore") + # use find_spec (PEP 451) and fall-back to find_module (PEP 302) + try: + loader = importer.find_spec(packageName).loader + except AttributeError: try: - loader = importer.find_module(packageName) + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + loader = importer.find_module(packageName) except AttributeError: - try: - loader = importer.find_spec(packageName).loader - except: - loader = None + # not a system module + loader = None + if loader is None: return None module = sys.modules.get(packageName) -- cgit v1.2.1 From 15789c14939f1c441c95963419e1b3f941a2831d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 5 Dec 2018 21:46:25 +0200 Subject: bpo-34738: Add directory entries in ZIP files created by distutils. (GH-9419) --- archive_util.py | 8 ++++++++ tests/test_archive_util.py | 13 ++++++------- tests/test_bdist_dumb.py | 2 +- tests/test_sdist.py | 12 ++++++++++-- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/archive_util.py b/archive_util.py index 78ae5757..b002dc3b 100644 --- a/archive_util.py +++ b/archive_util.py @@ -166,7 +166,15 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED) + if base_dir != os.curdir: + path = os.path.normpath(os.path.join(base_dir, '')) + zip.write(path, path) + log.info("adding '%s'", path) for dirpath, dirnames, filenames in os.walk(base_dir): + for name in dirnames: + path = os.path.normpath(os.path.join(dirpath, name, '')) + zip.write(path, path) + log.info("adding '%s'", path) for name in filenames: path = os.path.normpath(os.path.join(dirpath, name)) if os.path.isfile(path): diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index 14ba4ca3..e9aad0e4 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -122,12 +122,13 @@ class ArchiveUtilTestCase(support.TempdirManager, try: names = tar.getnames() names.sort() - return tuple(names) + return names finally: tar.close() - _created_files = ('dist', 'dist/file1', 'dist/file2', - 'dist/sub', 'dist/sub/file3', 'dist/sub2') + _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', + 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] + _created_files = [p.rstrip('/') for p in _zip_created_files] def _create_files(self): # creating something to tar @@ -244,8 +245,7 @@ class ArchiveUtilTestCase(support.TempdirManager, tarball = base_name + '.zip' self.assertTrue(os.path.exists(tarball)) with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), - ['dist/file1', 'dist/file2', 'dist/sub/file3']) + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile_no_zlib(self): @@ -271,8 +271,7 @@ class ArchiveUtilTestCase(support.TempdirManager, [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) self.assertTrue(os.path.exists(tarball)) with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), - ['dist/file1', 'dist/file2', 'dist/sub/file3']) + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) def test_check_archive_formats(self): self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py index c8ccdc23..01a233bc 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -84,7 +84,7 @@ class BuildDumbTestCase(support.TempdirManager, finally: fp.close() - contents = sorted(os.path.basename(fn) for fn in contents) + contents = sorted(filter(None, map(os.path.basename, contents))) wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] if not sys.dont_write_bytecode: wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 5444b815..23db1269 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -128,7 +128,9 @@ class SDistTestCase(BasePyPIRCCommandTestCase): zip_file.close() # making sure everything has been pruned correctly - self.assertEqual(len(content), 4) + expected = ['', 'PKG-INFO', 'README', 'setup.py', + 'somecode/', 'somecode/__init__.py'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') @unittest.skipIf(find_executable('tar') is None, @@ -226,7 +228,13 @@ class SDistTestCase(BasePyPIRCCommandTestCase): zip_file.close() # making sure everything was added - self.assertEqual(len(content), 12) + expected = ['', 'PKG-INFO', 'README', 'buildout.cfg', + 'data/', 'data/data.dt', 'inroot.txt', + 'scripts/', 'scripts/script.py', 'setup.py', + 'some/', 'some/file.txt', 'some/other_file.txt', + 'somecode/', 'somecode/__init__.py', 'somecode/doc.dat', + 'somecode/doc.txt'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) # checking the MANIFEST f = open(join(self.tmp_dir, 'MANIFEST')) -- cgit v1.2.1 From 7843688bc33dd4e13e10130bc49da4c290fe7d7f Mon Sep 17 00:00:00 2001 From: Dmitry Kuznetsov Date: Tue, 11 Dec 2018 20:35:24 -0800 Subject: Don't keep file modes for package data. --- changelog.d/1424.change.rst | 1 + setuptools/command/build_py.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/1424.change.rst diff --git a/changelog.d/1424.change.rst b/changelog.d/1424.change.rst new file mode 100644 index 00000000..361997dd --- /dev/null +++ b/changelog.d/1424.change.rst @@ -0,0 +1 @@ +Prevent keeping files mode for package_data build. It may break a build if user's package data has read only flag. \ No newline at end of file diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index b0314fd4..6fc0a4e4 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -120,7 +120,7 @@ class build_py(orig.build_py, Mixin2to3): target = os.path.join(build_dir, filename) self.mkpath(os.path.dirname(target)) srcfile = os.path.join(src_dir, filename) - outf, copied = self.copy_file(srcfile, target) + outf, copied = self.copy_file(srcfile, target, preserve_mode=False) srcfile = os.path.abspath(srcfile) if (copied and srcfile in self.distribution.convert_2to3_doctests): -- cgit v1.2.1 From 95c0e2810d32cfe69d84f5af41f7874fb57facca Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 17 Dec 2018 02:59:02 -0500 Subject: bpo-35186: Remove "built with" comment in setup.py upload (GH-10414) platform.dist() is deprecated and slated for removal in Python 3.8. The upload command itself should also not be used to upload to PyPI, but while it continues to exist it should not use deprecated functions. --- command/upload.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/command/upload.py b/command/upload.py index 32dda359..613ea711 100644 --- a/command/upload.py +++ b/command/upload.py @@ -121,14 +121,8 @@ class upload(PyPIRCCommand): 'requires': meta.get_requires(), 'obsoletes': meta.get_obsoletes(), } - comment = '' - if command == 'bdist_rpm': - dist, version, id = platform.dist() - if dist: - comment = 'built for %s %s' % (dist, version) - elif command == 'bdist_dumb': - comment = 'built for %s' % platform.platform(terse=1) - data['comment'] = comment + + data['comment'] = '' if self.sign: data['gpg_signature'] = (os.path.basename(filename) + ".asc", -- cgit v1.2.1 From f7d1e8f497ec20e55a7757e34d0a6229642ba59a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 18 Dec 2018 16:17:56 +0100 Subject: bpo-10496: distutils check_environ() handles getpwuid() error (GH-10931) check_environ() of distutils.utils now catchs KeyError on calling pwd.getpwuid(): don't create the HOME environment variable in this case. --- tests/test_util.py | 34 +++++++++++++++++++++++++--------- util.py | 9 +++++++-- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/tests/test_util.py b/tests/test_util.py index e2fc3809..bf0d4333 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -4,6 +4,7 @@ import sys import unittest from copy import copy from test.support import run_unittest +from unittest import mock from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import (get_platform, convert_path, change_root, @@ -234,20 +235,35 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase): def test_check_environ(self): util._environ_checked = 0 - if 'HOME' in os.environ: - del os.environ['HOME'] + os.environ.pop('HOME', None) - # posix without HOME - if os.name == 'posix': # this test won't run on windows - check_environ() - import pwd - self.assertEqual(os.environ['HOME'], pwd.getpwuid(os.getuid())[5]) - else: - check_environ() + check_environ() self.assertEqual(os.environ['PLAT'], get_platform()) self.assertEqual(util._environ_checked, 1) + @unittest.skipUnless(os.name == 'posix', 'specific to posix') + def test_check_environ_getpwuid(self): + util._environ_checked = 0 + os.environ.pop('HOME', None) + + import pwd + + # only set pw_dir field, other fields are not used + result = pwd.struct_passwd((None, None, None, None, None, + '/home/distutils', None)) + with mock.patch.object(pwd, 'getpwuid', return_value=result): + check_environ() + self.assertEqual(os.environ['HOME'], '/home/distutils') + + util._environ_checked = 0 + os.environ.pop('HOME', None) + + # bpo-10496: Catch pwd.getpwuid() error + with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError): + check_environ() + self.assertNotIn('HOME', os.environ) + def test_split_quoted(self): self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), ['one', 'two', 'three', 'four']) diff --git a/util.py b/util.py index 83682628..30a21e4a 100644 --- a/util.py +++ b/util.py @@ -157,8 +157,13 @@ def check_environ (): return if os.name == 'posix' and 'HOME' not in os.environ: - import pwd - os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + try: + import pwd + os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + except (ImportError, KeyError): + # bpo-10496: if the current user identifier doesn't exist in the + # password database, do nothing + pass if 'PLAT' not in os.environ: os.environ['PLAT'] = get_platform() -- cgit v1.2.1 From 8fa927e2b2fc81adb420285e470439fc3a28fcee Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 20 Dec 2018 19:00:14 +0200 Subject: bpo-22831: Use "with" to avoid possible fd leaks in distutils. (GH-10921) --- archive_util.py | 22 +++++++++++----------- command/bdist_msi.py | 24 ++++++++++++------------ command/config.py | 36 +++++++++++++++++------------------- command/sdist.py | 15 +++++++-------- util.py | 37 ++++++++++++++++++------------------- 5 files changed, 65 insertions(+), 69 deletions(-) diff --git a/archive_util.py b/archive_util.py index b002dc3b..565a3117 100644 --- a/archive_util.py +++ b/archive_util.py @@ -166,21 +166,21 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED) - if base_dir != os.curdir: - path = os.path.normpath(os.path.join(base_dir, '')) - zip.write(path, path) - log.info("adding '%s'", path) - for dirpath, dirnames, filenames in os.walk(base_dir): - for name in dirnames: - path = os.path.normpath(os.path.join(dirpath, name, '')) + with zip: + if base_dir != os.curdir: + path = os.path.normpath(os.path.join(base_dir, '')) zip.write(path, path) log.info("adding '%s'", path) - for name in filenames: - path = os.path.normpath(os.path.join(dirpath, name)) - if os.path.isfile(path): + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in dirnames: + path = os.path.normpath(os.path.join(dirpath, name, '')) zip.write(path, path) log.info("adding '%s'", path) - zip.close() + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zip.write(path, path) + log.info("adding '%s'", path) return zip_filename diff --git a/command/bdist_msi.py b/command/bdist_msi.py index 80104c37..f335a348 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -390,18 +390,18 @@ class bdist_msi(Command): # entries for each version as the above code does if self.pre_install_script: scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") - f = open(scriptfn, "w") - # The batch file will be executed with [PYTHON], so that %1 - # is the path to the Python interpreter; %0 will be the path - # of the batch file. - # rem =""" - # %1 %0 - # exit - # """ - # - f.write('rem ="""\n%1 %0\nexit\n"""\n') - f.write(open(self.pre_install_script).read()) - f.close() + with open(scriptfn, "w") as f: + # The batch file will be executed with [PYTHON], so that %1 + # is the path to the Python interpreter; %0 will be the path + # of the batch file. + # rem =""" + # %1 %0 + # exit + # """ + # + f.write('rem ="""\n%1 %0\nexit\n"""\n') + with open(self.pre_install_script) as fin: + f.write(fin.read()) add_data(self.db, "Binary", [("PreInstall", msilib.Binary(scriptfn)) ]) diff --git a/command/config.py b/command/config.py index 4ae153d1..f511b888 100644 --- a/command/config.py +++ b/command/config.py @@ -106,15 +106,14 @@ class config(Command): def _gen_temp_sourcefile(self, body, headers, lang): filename = "_configtest" + LANG_EXT[lang] - file = open(filename, "w") - if headers: - for header in headers: - file.write("#include <%s>\n" % header) - file.write("\n") - file.write(body) - if body[-1] != "\n": - file.write("\n") - file.close() + with open(filename, "w") as file: + if headers: + for header in headers: + file.write("#include <%s>\n" % header) + file.write("\n") + file.write(body) + if body[-1] != "\n": + file.write("\n") return filename def _preprocess(self, body, headers, include_dirs, lang): @@ -203,17 +202,16 @@ class config(Command): if isinstance(pattern, str): pattern = re.compile(pattern) - file = open(out) - match = False - while True: - line = file.readline() - if line == '': - break - if pattern.search(line): - match = True - break + with open(out) as file: + match = False + while True: + line = file.readline() + if line == '': + break + if pattern.search(line): + match = True + break - file.close() self._clean() return match diff --git a/command/sdist.py b/command/sdist.py index 52eaa15d..b4996fcb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -407,14 +407,13 @@ class sdist(Command): distribution. """ log.info("reading manifest file '%s'", self.manifest) - manifest = open(self.manifest) - for line in manifest: - # ignore comments and blank lines - line = line.strip() - if line.startswith('#') or not line: - continue - self.filelist.append(line) - manifest.close() + with open(self.manifest) as manifest: + for line in manifest: + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue + self.filelist.append(line) def make_release_tree(self, base_dir, files): """Create the directory tree that will become the source diff --git a/util.py b/util.py index 30a21e4a..15cd2ad9 100644 --- a/util.py +++ b/util.py @@ -378,35 +378,34 @@ def byte_compile (py_files, else: script = open(script_name, "w") - script.write("""\ + with script: + script.write("""\ from distutils.util import byte_compile files = [ """) - # XXX would be nice to write absolute filenames, just for - # safety's sake (script should be more robust in the face of - # chdir'ing before running it). But this requires abspath'ing - # 'prefix' as well, and that breaks the hack in build_lib's - # 'byte_compile()' method that carefully tacks on a trailing - # slash (os.sep really) to make sure the prefix here is "just - # right". This whole prefix business is rather delicate -- the - # problem is that it's really a directory, but I'm treating it - # as a dumb string, so trailing slashes and so forth matter. - - #py_files = map(os.path.abspath, py_files) - #if prefix: - # prefix = os.path.abspath(prefix) - - script.write(",\n".join(map(repr, py_files)) + "]\n") - script.write(""" + # XXX would be nice to write absolute filenames, just for + # safety's sake (script should be more robust in the face of + # chdir'ing before running it). But this requires abspath'ing + # 'prefix' as well, and that breaks the hack in build_lib's + # 'byte_compile()' method that carefully tacks on a trailing + # slash (os.sep really) to make sure the prefix here is "just + # right". This whole prefix business is rather delicate -- the + # problem is that it's really a directory, but I'm treating it + # as a dumb string, so trailing slashes and so forth matter. + + #py_files = map(os.path.abspath, py_files) + #if prefix: + # prefix = os.path.abspath(prefix) + + script.write(",\n".join(map(repr, py_files)) + "]\n") + script.write(""" byte_compile(files, optimize=%r, force=%r, prefix=%r, base_dir=%r, verbose=%r, dry_run=0, direct=1) """ % (optimize, force, prefix, base_dir, verbose)) - script.close() - cmd = [sys.executable] cmd.extend(subprocess._optim_args_from_interpreter_flags()) cmd.append(script_name) -- cgit v1.2.1 From 1db80e46af67c5aa0ffebe3fc61dd5e0787a42c5 Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Fri, 28 Dec 2018 15:03:17 +0100 Subject: bpo-11191: skip unsupported test_distutils case for AIX with xlc (GH-8709) Command line options for the xlc compiler behave differently from gcc and clang, so skip this test case for now when xlc is the compiler. Patch by aixtools (Michael Felt) --- command/config.py | 1 - tests/test_config_cmd.py | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/command/config.py b/command/config.py index f511b888..aeda408e 100644 --- a/command/config.py +++ b/command/config.py @@ -328,7 +328,6 @@ class config(Command): return self.try_cpp(body="/* No body */", headers=[header], include_dirs=include_dirs) - def dump_file(filename, head=None): """Dumps a file content into log.info. diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 6e566e79..b735fd33 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -39,11 +39,17 @@ class ConfigTestCase(support.LoggingSilencer, @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_search_cpp(self): + import shutil cmd = missing_compiler_executable(['preprocessor']) if cmd is not None: self.skipTest('The %r command is not found' % cmd) pkg_dir, dist = self.create_dist() cmd = config(dist) + cmd._check_compiler() + compiler = cmd.compiler + is_xlc = shutil.which(compiler.preprocessor[0]).startswith("/usr/vac") + if is_xlc: + self.skipTest('xlc: The -E option overrides the -P, -o, and -qsyntaxonly options') # simple pattern searches match = cmd.search_cpp(pattern='xxx', body='/* xxx */') -- cgit v1.2.1 From 18e2218e6220614cd5c9737cac10be598c3bf8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 10 Jan 2019 00:55:03 +0100 Subject: Distutils no longer needs to remain compatible with 2.3 (GH-11423) --- README | 2 -- 1 file changed, 2 deletions(-) diff --git a/README b/README index 408a203b..23f48850 100644 --- a/README +++ b/README @@ -8,6 +8,4 @@ The Distutils-SIG web page is also a good starting point: http://www.python.org/sigs/distutils-sig/ -WARNING : Distutils must remain compatible with 2.3 - $Id$ -- cgit v1.2.1 From cb20330b1f00ca7864338c7518a8f0883b3844a1 Mon Sep 17 00:00:00 2001 From: Marc Schlaich Date: Sun, 20 Jan 2019 19:47:42 +0100 Subject: bpo-35699: fix distuils cannot detect Build Tools 2017 anymore (GH-11495) --- _msvccompiler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/_msvccompiler.py b/_msvccompiler.py index 84b4ef59..58b20a21 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -78,6 +78,7 @@ def _find_vc2017(): "-prerelease", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "-property", "installationPath", + "-products", "*", ], encoding="mbcs", errors="strict").strip() except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): return None, None -- cgit v1.2.1 From 95d86241efd33f8db71e72352b31185389f4d68d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 4 Feb 2019 17:15:13 -0800 Subject: bpo-35299: Fixed sysconfig and distutils during PGO profiling (GH-11744) --- command/build_ext.py | 5 +++-- sysconfig.py | 25 +++++++++++++++++++------ tests/test_build_ext.py | 6 ++++-- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 158465d2..0428466b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -161,9 +161,10 @@ class build_ext(Command): # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. - self.include_dirs.append(py_include) + self.include_dirs.extend(py_include.split(os.path.pathsep)) if plat_py_include != py_include: - self.include_dirs.append(plat_py_include) + self.include_dirs.extend( + plat_py_include.split(os.path.pathsep)) self.ensure_string_list('libraries') self.ensure_string_list('link_objects') diff --git a/sysconfig.py b/sysconfig.py index b433fc86..40af493c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -29,9 +29,7 @@ if "_PYTHON_PROJECT_BASE" in os.environ: project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) else: project_base = os.path.dirname(os.path.abspath(sys.executable)) -if (os.name == 'nt' and - project_base.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))): - project_base = os.path.dirname(os.path.dirname(project_base)) + # python_build: (Boolean) if true, we're either building Python or # building an extension with an un-installed Python, so we use @@ -41,16 +39,26 @@ def _is_python_source_dir(d): if os.path.isfile(os.path.join(d, "Modules", fn)): return True return False + _sys_home = getattr(sys, '_home', None) -if (_sys_home and os.name == 'nt' and - _sys_home.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))): - _sys_home = os.path.dirname(os.path.dirname(_sys_home)) + +if os.name == 'nt': + def _fix_pcbuild(d): + if d and os.path.normcase(d).startswith( + os.path.normcase(os.path.join(PREFIX, "PCbuild"))): + return PREFIX + return d + project_base = _fix_pcbuild(project_base) + _sys_home = _fix_pcbuild(_sys_home) + def _python_build(): if _sys_home: return _is_python_source_dir(_sys_home) return _is_python_source_dir(project_base) + python_build = _python_build() + # Calculate the build qualifier flags if they are defined. Adding the flags # to the include and lib directories only makes sense for an installation, not # an in-source build. @@ -99,6 +107,11 @@ def get_python_inc(plat_specific=0, prefix=None): python_dir = 'python' + get_python_version() + build_flags return os.path.join(prefix, "include", python_dir) elif os.name == "nt": + if python_build: + # Include both the include and PC dir to ensure we can find + # pyconfig.h + return (os.path.join(prefix, "include") + os.path.pathsep + + os.path.join(prefix, "PC")) return os.path.join(prefix, "include") else: raise DistutilsPlatformError( diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index a7221827..88847f9e 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -177,10 +177,12 @@ class BuildExtTestCase(TempdirManager, cmd.finalize_options() py_include = sysconfig.get_python_inc() - self.assertIn(py_include, cmd.include_dirs) + for p in py_include.split(os.path.pathsep): + self.assertIn(p, cmd.include_dirs) plat_py_include = sysconfig.get_python_inc(plat_specific=1) - self.assertIn(plat_py_include, cmd.include_dirs) + for p in plat_py_include.split(os.path.pathsep): + self.assertIn(p, cmd.include_dirs) # make sure cmd.libraries is turned into a list # if it's a string -- cgit v1.2.1 From 9d329c1bbe7e35a3a4b5810f90ff292d51d7a66d Mon Sep 17 00:00:00 2001 From: Kevin Adler Date: Mon, 4 Mar 2019 08:48:40 -0600 Subject: bpo-35198 Fix C++ extension compilation on AIX (GH-10437) For C++ extensions, distutils tries to replace the C compiler with the C++ compiler, but it assumes that C compiler is the first element after any environment variables set. On AIX, linking goes through ld_so_aix, so it is the first element and the compiler is the next element. Thus the replacement is faulty: ld_so_aix gcc ... -> g++ gcc ... Also, it assumed that self.compiler_cxx had only 1 element or that there were the same number of elements as the linker has and in the same order. This might not be the case, so instead concatenate everything together. --- unixccompiler.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index ab4d4de1..d10a78da 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -188,7 +188,15 @@ class UnixCCompiler(CCompiler): i = 1 while '=' in linker[i]: i += 1 - linker[i] = self.compiler_cxx[i] + + if os.path.basename(linker[i]) == 'ld_so_aix': + # AIX platforms prefix the compiler with the ld_so_aix + # script, so we need to adjust our linker index + offset = 1 + else: + offset = 0 + + linker[i+offset] = self.compiler_cxx[i] if sys.platform == 'darwin': linker = _osx_support.compiler_fixup(linker, ld_args) -- cgit v1.2.1 From f3e20900c492253924944707f390a9c12d7a30fd Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 12 Mar 2019 08:39:57 -0700 Subject: bpo-36264: Don't honor POSIX HOME in os.path.expanduser on Windows (GH-12282) --- tests/test_config.py | 1 + tests/test_dist.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index 77ef788e..344084af 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -60,6 +60,7 @@ class BasePyPIRCCommandTestCase(support.TempdirManager, super(BasePyPIRCCommandTestCase, self).setUp() self.tmp_dir = self.mkdtemp() os.environ['HOME'] = self.tmp_dir + os.environ['USERPROFILE'] = self.tmp_dir self.rc = os.path.join(self.tmp_dir, '.pypirc') self.dist = Distribution() diff --git a/tests/test_dist.py b/tests/test_dist.py index 0a19f0fb..cc34725a 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -463,7 +463,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, # win32-style if sys.platform == 'win32': # home drive should be found - os.environ['HOME'] = temp_dir + os.environ['USERPROFILE'] = temp_dir files = dist.find_config_files() self.assertIn(user_filename, files, '%r not found in %r' % (user_filename, files)) -- cgit v1.2.1 From 1078515edfad6bf60085a2611fe24ffd497474ca Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 15 Mar 2019 14:57:52 +0100 Subject: bpo-36235: Fix CFLAGS in distutils customize_compiler() (GH-12236) Fix CFLAGS in customize_compiler() of distutils.sysconfig: when the CFLAGS environment variable is defined, don't override CFLAGS variable with the OPT variable anymore. Initial patch written by David Malcolm. Co-Authored-By: David Malcolm --- sysconfig.py | 6 +++--- tests/test_sysconfig.py | 15 +++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 40af493c..a3494670 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -181,8 +181,8 @@ def customize_compiler(compiler): _osx_support.customize_compiler(_config_vars) _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' - (cc, cxx, opt, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ - get_config_vars('CC', 'CXX', 'OPT', 'CFLAGS', + (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ + get_config_vars('CC', 'CXX', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') if 'CC' in os.environ: @@ -205,7 +205,7 @@ def customize_compiler(compiler): if 'LDFLAGS' in os.environ: ldshared = ldshared + ' ' + os.environ['LDFLAGS'] if 'CFLAGS' in os.environ: - cflags = opt + ' ' + os.environ['CFLAGS'] + cflags = cflags + ' ' + os.environ['CFLAGS'] ldshared = ldshared + ' ' + os.environ['CFLAGS'] if 'CPPFLAGS' in os.environ: cpp = cpp + ' ' + os.environ['CPPFLAGS'] diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index fe4a2994..4bf6a067 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -9,7 +9,7 @@ import unittest from distutils import sysconfig from distutils.ccompiler import get_default_compiler from distutils.tests import support -from test.support import TESTFN, run_unittest, check_warnings +from test.support import TESTFN, run_unittest, check_warnings, swap_item class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): @@ -78,7 +78,9 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): 'not testing if default compiler is not unix') def test_customize_compiler(self): os.environ['AR'] = 'my_ar' - os.environ['ARFLAGS'] = '-arflags' + os.environ['CC'] = 'my_cc' + os.environ['ARFLAGS'] = '--myarflags' + os.environ['CFLAGS'] = '--mycflags' # make sure AR gets caught class compiler: @@ -87,9 +89,14 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): def set_executables(self, **kw): self.exes = kw + # Make sure that sysconfig._config_vars is initialized + sysconfig.get_config_vars() + comp = compiler() - sysconfig.customize_compiler(comp) - self.assertEqual(comp.exes['archiver'], 'my_ar -arflags') + with swap_item(sysconfig._config_vars, 'CFLAGS', '--sysconfig-cflags'): + sysconfig.customize_compiler(comp) + self.assertEqual(comp.exes['archiver'], 'my_ar --myarflags') + self.assertEqual(comp.exes['compiler'], 'my_cc --sysconfig-cflags --mycflags') def test_parse_makefile_base(self): self.makefile = TESTFN -- cgit v1.2.1 From 4c6000461394d2a969c17b98ff7b64e7d6f6eee4 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 18 Mar 2019 17:19:02 +0100 Subject: bpo-36235: Enhance distutils test_customize_compiler() (GH-12403) The test test_customize_compiler() now mocks all sysconfig variables and all environment variables used by customize_compiler(). --- tests/test_sysconfig.py | 92 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 13 deletions(-) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 4bf6a067..245a6c86 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -1,4 +1,5 @@ """Tests for distutils.sysconfig.""" +import contextlib import os import shutil import subprocess @@ -74,14 +75,7 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): os.chdir(cwd) self.assertEqual(srcdir, srcdir2) - @unittest.skipUnless(get_default_compiler() == 'unix', - 'not testing if default compiler is not unix') - def test_customize_compiler(self): - os.environ['AR'] = 'my_ar' - os.environ['CC'] = 'my_cc' - os.environ['ARFLAGS'] = '--myarflags' - os.environ['CFLAGS'] = '--mycflags' - + def customize_compiler(self): # make sure AR gets caught class compiler: compiler_type = 'unix' @@ -89,14 +83,86 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): def set_executables(self, **kw): self.exes = kw - # Make sure that sysconfig._config_vars is initialized - sysconfig.get_config_vars() + sysconfig_vars = { + 'AR': 'sc_ar', + 'CC': 'sc_cc', + 'CXX': 'sc_cxx', + 'ARFLAGS': '--sc-arflags', + 'CFLAGS': '--sc-cflags', + 'CCSHARED': '--sc-ccshared', + 'LDSHARED': 'sc_ldshared', + 'SHLIB_SUFFIX': 'sc_shutil_suffix', + } comp = compiler() - with swap_item(sysconfig._config_vars, 'CFLAGS', '--sysconfig-cflags'): + with contextlib.ExitStack() as cm: + for key, value in sysconfig_vars.items(): + cm.enter_context(swap_item(sysconfig._config_vars, key, value)) sysconfig.customize_compiler(comp) - self.assertEqual(comp.exes['archiver'], 'my_ar --myarflags') - self.assertEqual(comp.exes['compiler'], 'my_cc --sysconfig-cflags --mycflags') + + return comp + + @unittest.skipUnless(get_default_compiler() == 'unix', + 'not testing if default compiler is not unix') + def test_customize_compiler(self): + # Make sure that sysconfig._config_vars is initialized + sysconfig.get_config_vars() + + os.environ['AR'] = 'env_ar' + os.environ['CC'] = 'env_cc' + os.environ['CPP'] = 'env_cpp' + os.environ['CXX'] = 'env_cxx --env-cxx-flags' + os.environ['LDSHARED'] = 'env_ldshared' + os.environ['LDFLAGS'] = '--env-ldflags' + os.environ['ARFLAGS'] = '--env-arflags' + os.environ['CFLAGS'] = '--env-cflags' + os.environ['CPPFLAGS'] = '--env-cppflags' + + comp = self.customize_compiler() + self.assertEqual(comp.exes['archiver'], + 'env_ar --env-arflags') + self.assertEqual(comp.exes['preprocessor'], + 'env_cpp --env-cppflags') + self.assertEqual(comp.exes['compiler'], + 'env_cc --sc-cflags --env-cflags --env-cppflags') + self.assertEqual(comp.exes['compiler_so'], + ('env_cc --sc-cflags ' + '--env-cflags ''--env-cppflags --sc-ccshared')) + self.assertEqual(comp.exes['compiler_cxx'], + 'env_cxx --env-cxx-flags') + self.assertEqual(comp.exes['linker_exe'], + 'env_cc') + self.assertEqual(comp.exes['linker_so'], + ('env_ldshared --env-ldflags --env-cflags' + ' --env-cppflags')) + self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') + + del os.environ['AR'] + del os.environ['CC'] + del os.environ['CPP'] + del os.environ['CXX'] + del os.environ['LDSHARED'] + del os.environ['LDFLAGS'] + del os.environ['ARFLAGS'] + del os.environ['CFLAGS'] + del os.environ['CPPFLAGS'] + + comp = self.customize_compiler() + self.assertEqual(comp.exes['archiver'], + 'sc_ar --sc-arflags') + self.assertEqual(comp.exes['preprocessor'], + 'sc_cc -E') + self.assertEqual(comp.exes['compiler'], + 'sc_cc --sc-cflags') + self.assertEqual(comp.exes['compiler_so'], + 'sc_cc --sc-cflags --sc-ccshared') + self.assertEqual(comp.exes['compiler_cxx'], + 'sc_cxx') + self.assertEqual(comp.exes['linker_exe'], + 'sc_cc') + self.assertEqual(comp.exes['linker_so'], + 'sc_ldshared') + self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') def test_parse_makefile_base(self): self.makefile = TESTFN -- cgit v1.2.1 From 868d8498bbccf63c274056959055800d02cac0d5 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Wed, 27 Mar 2019 22:34:19 +0100 Subject: bpo-31292: Fixed distutils check --restructuredtext for include directives (GH-10605) --- command/check.py | 3 ++- tests/includetest.rst | 1 + tests/test_check.py | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/includetest.rst diff --git a/command/check.py b/command/check.py index 7ebe707c..04c2f964 100644 --- a/command/check.py +++ b/command/check.py @@ -120,7 +120,8 @@ class check(Command): def _check_rst_data(self, data): """Returns warnings when the provided data doesn't compile.""" - source_path = StringIO() + # the include and csv_table directives need this to be a path + source_path = self.distribution.script_name or 'setup.py' parser = Parser() settings = frontend.OptionParser(components=(Parser,)).get_default_values() settings.tab_width = 4 diff --git a/tests/includetest.rst b/tests/includetest.rst new file mode 100644 index 00000000..d7b4ae38 --- /dev/null +++ b/tests/includetest.rst @@ -0,0 +1 @@ +This should be included. diff --git a/tests/test_check.py b/tests/test_check.py index 3d22868e..e534aca1 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -1,4 +1,5 @@ """Tests for distutils.command.check.""" +import os import textwrap import unittest from test.support import run_unittest @@ -13,13 +14,19 @@ except ImportError: pygments = None +HERE = os.path.dirname(__file__) + + class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.TestCase): - def _run(self, metadata=None, **options): + def _run(self, metadata=None, cwd=None, **options): if metadata is None: metadata = {} + if cwd is not None: + old_dir = os.getcwd() + os.chdir(cwd) pkg_info, dist = self.create_dist(**metadata) cmd = check(dist) cmd.initialize_options() @@ -27,6 +34,8 @@ class CheckTestCase(support.LoggingSilencer, setattr(cmd, name, value) cmd.ensure_finalized() cmd.run() + if cwd is not None: + os.chdir(old_dir) return cmd def test_check_metadata(self): @@ -99,6 +108,11 @@ class CheckTestCase(support.LoggingSilencer, cmd = self._run(metadata, strict=1, restructuredtext=1) self.assertEqual(cmd._warnings, 0) + # check that includes work to test #31292 + metadata['long_description'] = 'title\n=====\n\n.. include:: includetest.rst' + cmd = self._run(metadata, cwd=HERE, strict=1, restructuredtext=1) + self.assertEqual(cmd._warnings, 0) + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") def test_check_restructuredtext_with_syntax_highlight(self): # Don't fail if there is a `code` or `code-block` directive -- cgit v1.2.1 From 817e0bd71433db065c3c74b0b04de7c734ba9ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Schoentgen?= Date: Mon, 8 Apr 2019 13:08:48 +0000 Subject: bpo-35416: fix potential resource warnings in distutils (GH-10918) --- command/bdist_rpm.py | 3 +- command/bdist_wininst.py | 74 +++++++++++++++++++++++++----------------------- command/upload.py | 5 ++-- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 02f10dd8..20ca7ac6 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -537,7 +537,8 @@ class bdist_rpm(Command): '', '%' + rpm_opt,]) if val: - spec_file.extend(open(val, 'r').read().split('\n')) + with open(val) as f: + spec_file.extend(f.read().split('\n')) else: spec_file.append(default) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index fde56754..1cf2e963 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -247,47 +247,49 @@ class bdist_wininst(Command): self.announce("creating %s" % installer_name) if bitmap: - bitmapdata = open(bitmap, "rb").read() + with open(bitmap, "rb") as f: + bitmapdata = f.read() bitmaplen = len(bitmapdata) else: bitmaplen = 0 - file = open(installer_name, "wb") - file.write(self.get_exe_bytes()) - if bitmap: - file.write(bitmapdata) - - # Convert cfgdata from unicode to ascii, mbcs encoded - if isinstance(cfgdata, str): - cfgdata = cfgdata.encode("mbcs") - - # Append the pre-install script - cfgdata = cfgdata + b"\0" - if self.pre_install_script: - # We need to normalize newlines, so we open in text mode and - # convert back to bytes. "latin-1" simply avoids any possible - # failures. - with open(self.pre_install_script, "r", - encoding="latin-1") as script: - script_data = script.read().encode("latin-1") - cfgdata = cfgdata + script_data + b"\n\0" - else: - # empty pre-install script + with open(installer_name, "wb") as file: + file.write(self.get_exe_bytes()) + if bitmap: + file.write(bitmapdata) + + # Convert cfgdata from unicode to ascii, mbcs encoded + if isinstance(cfgdata, str): + cfgdata = cfgdata.encode("mbcs") + + # Append the pre-install script cfgdata = cfgdata + b"\0" - file.write(cfgdata) - - # The 'magic number' 0x1234567B is used to make sure that the - # binary layout of 'cfgdata' is what the wininst.exe binary - # expects. If the layout changes, increment that number, make - # the corresponding changes to the wininst.exe sources, and - # recompile them. - header = struct.pack(" Date: Tue, 9 Apr 2019 14:54:30 +0900 Subject: fix code styling (GH-12737) --- command/bdist_wininst.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 1cf2e963..3a616883 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -269,7 +269,7 @@ class bdist_wininst(Command): # convert back to bytes. "latin-1" simply avoids any possible # failures. with open(self.pre_install_script, "r", - encoding="latin-1") as script: + encoding="latin-1") as script: script_data = script.read().encode("latin-1") cfgdata = cfgdata + script_data + b"\n\0" else: -- cgit v1.2.1 From ef2792fe08aabeb870e8f489d2bbfe2898ce1766 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 11 Apr 2019 01:38:48 +0200 Subject: bpo-36235: Fix distutils test_customize_compiler() on macOS (GH-12764) Set CUSTOMIZED_OSX_COMPILER to True to disable _osx_support.customize_compiler(). --- tests/test_sysconfig.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 245a6c86..236755d0 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -92,6 +92,9 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): 'CCSHARED': '--sc-ccshared', 'LDSHARED': 'sc_ldshared', 'SHLIB_SUFFIX': 'sc_shutil_suffix', + + # On macOS, disable _osx_support.customize_compiler() + 'CUSTOMIZED_OSX_COMPILER': 'True', } comp = compiler() -- cgit v1.2.1 From 1d1c462903645fa567508a1272ed28c5f334c24c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Apr 2019 16:26:36 +0200 Subject: bpo-35755: shutil.which() uses os.confstr("CS_PATH") (GH-12858) shutil.which() and distutils.spawn.find_executable() now use os.confstr("CS_PATH") if available instead of os.defpath, if the PATH environment variable is not set. Don't use os.confstr("CS_PATH") nor os.defpath if the PATH environment variable is set to an empty string to mimick Unix 'which' command behavior. Changes: * find_executable() now starts by checking for the executable in the current working directly case. Add an explicit "if not path: return None". * Add tests for PATH='' (empty string), PATH=':' and for PATHEXT. --- spawn.py | 39 +++++++++++++++++++++++++-------------- tests/test_spawn.py | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 17 deletions(-) diff --git a/spawn.py b/spawn.py index 53876880..88832727 100644 --- a/spawn.py +++ b/spawn.py @@ -172,21 +172,32 @@ def find_executable(executable, path=None): A string listing directories separated by 'os.pathsep'; defaults to os.environ['PATH']. Returns the complete filename or None if not found. """ - if path is None: - path = os.environ.get('PATH', os.defpath) - - paths = path.split(os.pathsep) - base, ext = os.path.splitext(executable) - + _, ext = os.path.splitext(executable) if (sys.platform == 'win32') and (ext != '.exe'): executable = executable + '.exe' - if not os.path.isfile(executable): - for p in paths: - f = os.path.join(p, executable) - if os.path.isfile(f): - # the file exists, we have a shot at spawn working - return f - return None - else: + if os.path.isfile(executable): return executable + + if path is None: + path = os.environ.get('PATH', None) + if path is None: + try: + path = os.confstr("CS_PATH") + except (AttributeError, ValueError): + # os.confstr() or CS_PATH is not available + path = os.defpath + # bpo-35755: Don't use os.defpath if the PATH environment variable is + # set to an empty string to mimick Unix which command behavior + + # PATH='' doesn't match, whereas PATH=':' looks in the current directory + if not path: + return None + + paths = path.split(os.pathsep) + for p in paths: + f = os.path.join(p, executable) + if os.path.isfile(f): + # the file exists, we have a shot at spawn working + return f + return None diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 0d455385..f9ae69ef 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -87,11 +87,52 @@ class SpawnTestCase(support.TempdirManager, rv = find_executable(dont_exist_program , path=tmp_dir) self.assertIsNone(rv) - # test os.defpath: missing PATH environment variable + # PATH='': no match, except in the current directory with test_support.EnvironmentVarGuard() as env: - with mock.patch('distutils.spawn.os.defpath', tmp_dir): - env.pop('PATH') + env['PATH'] = '' + with unittest.mock.patch('distutils.spawn.os.confstr', + return_value=tmp_dir, create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', + tmp_dir): + rv = find_executable(program) + self.assertIsNone(rv) + + # look in current directory + with test_support.change_cwd(tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, program) + + # PATH=':': explicitly looks in the current directory + with test_support.EnvironmentVarGuard() as env: + env['PATH'] = os.pathsep + with unittest.mock.patch('distutils.spawn.os.confstr', + return_value='', create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', ''): + rv = find_executable(program) + self.assertIsNone(rv) + + # look in current directory + with test_support.change_cwd(tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, program) + + # missing PATH: test os.confstr("CS_PATH") and os.defpath + with test_support.EnvironmentVarGuard() as env: + env.pop('PATH', None) + + # without confstr + with unittest.mock.patch('distutils.spawn.os.confstr', + side_effect=ValueError, + create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', + tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, filename) + # with confstr + with unittest.mock.patch('distutils.spawn.os.confstr', + return_value=tmp_dir, create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', ''): rv = find_executable(program) self.assertEqual(rv, filename) -- cgit v1.2.1 From 3dd8d681a9db9ca36372f3a6a8fe6457076ea8b0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Apr 2019 17:44:06 +0200 Subject: bpo-35755: Don't say "to mimick Unix which command behavior" (GH-12861) --- spawn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spawn.py b/spawn.py index 88832727..d3a12c28 100644 --- a/spawn.py +++ b/spawn.py @@ -188,7 +188,7 @@ def find_executable(executable, path=None): # os.confstr() or CS_PATH is not available path = os.defpath # bpo-35755: Don't use os.defpath if the PATH environment variable is - # set to an empty string to mimick Unix which command behavior + # set to an empty string # PATH='' doesn't match, whereas PATH=':' looks in the current directory if not path: -- cgit v1.2.1 From 89a5c175e47d95d33a700eedecf402e78d2a964a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 25 Apr 2019 11:59:34 +0200 Subject: bpo-28552: Fix distutils.sysconfig for empty sys.executable (GH-12875) bpo-28552, bpo-7774: Fix distutils.sysconfig if sys.executable is None or an empty string: use os.getcwd() to initialize project_base. Fix also the distutils build command: don't use sys.executable if it's evaluated as false (None or empty string). --- command/build.py | 2 +- sysconfig.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/command/build.py b/command/build.py index c6f52e61..a86df0bc 100644 --- a/command/build.py +++ b/command/build.py @@ -116,7 +116,7 @@ class build(Command): self.build_scripts = os.path.join(self.build_base, 'scripts-%d.%d' % sys.version_info[:2]) - if self.executable is None: + if self.executable is None and sys.executable: self.executable = os.path.normpath(sys.executable) if isinstance(self.parallel, str): diff --git a/sysconfig.py b/sysconfig.py index a3494670..570a612d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -28,7 +28,12 @@ BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) if "_PYTHON_PROJECT_BASE" in os.environ: project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) else: - project_base = os.path.dirname(os.path.abspath(sys.executable)) + if sys.executable: + project_base = os.path.dirname(os.path.abspath(sys.executable)) + else: + # sys.executable can be empty if argv[0] has been changed and Python is + # unable to retrieve the real program name + project_base = os.getcwd() # python_build: (Boolean) if true, we're either building Python or -- cgit v1.2.1 From eb3478f2cdccd5489b64f7bb8cec4988fba985c7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 25 Apr 2019 20:13:10 +0200 Subject: bpo-21536: C extensions are no longer linked to libpython (GH-12946) On Unix, C extensions are no longer linked to libpython. It is now possible to load a C extension built using a shared library Python with a statically linked Python. When Python is embedded, libpython must not be loaded with RTLD_LOCAL, but RTLD_GLOBAL instead. Previously, using RTLD_LOCAL, it was already not possible to load C extensions which were not linked to libpython, like C extensions of the standard library built by the "*shared*" section of Modules/Setup. distutils, python-config and python-config.py have been modified. --- command/build_ext.py | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index 0428466b..1672d02a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -714,20 +714,5 @@ class build_ext(Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] - else: - return ext.libraries - elif sys.platform == 'darwin': - # Don't use the default code below - return ext.libraries - elif sys.platform[:3] == 'aix': - # Don't use the default code below - return ext.libraries - else: - from distutils import sysconfig - if sysconfig.get_config_var('Py_ENABLE_SHARED'): - pythonlib = 'python{}.{}{}'.format( - sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff, - sysconfig.get_config_var('ABIFLAGS')) - return ext.libraries + [pythonlib] - else: - return ext.libraries + + return ext.libraries -- cgit v1.2.1 From 03ae9d8b1618be1a9dc5c9a3349fb01b4c7c7d14 Mon Sep 17 00:00:00 2001 From: Paul Monson Date: Thu, 25 Apr 2019 11:36:45 -0700 Subject: bpo-35920: Windows 10 ARM32 platform support (GH-11774) --- _msvccompiler.py | 16 ++++++++++++++-- spawn.py | 2 +- sysconfig.py | 1 + util.py | 16 +++++++++++++--- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index 58b20a21..c7ac3f04 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -89,13 +89,24 @@ def _find_vc2017(): return None, None +PLAT_SPEC_TO_RUNTIME = { + 'x86' : 'x86', + 'x86_amd64' : 'x64', + 'x86_arm' : 'arm', +} + def _find_vcvarsall(plat_spec): _, best_dir = _find_vc2017() vcruntime = None - vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86' + + if plat_spec in PLAT_SPEC_TO_RUNTIME: + vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec] + else: + vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86' + if best_dir: vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**", - "Microsoft.VC141.CRT", "vcruntime140.dll") + vcruntime_plat, "Microsoft.VC141.CRT", "vcruntime140.dll") try: import glob vcruntime = glob.glob(vcredist, recursive=True)[-1] @@ -178,6 +189,7 @@ def _find_exe(exe, paths=None): PLAT_TO_VCVARS = { 'win32' : 'x86', 'win-amd64' : 'x86_amd64', + 'win-arm32' : 'x86_arm', } # A set containing the DLLs that are guaranteed to be available for diff --git a/spawn.py b/spawn.py index d3a12c28..ceb94945 100644 --- a/spawn.py +++ b/spawn.py @@ -81,7 +81,6 @@ def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): "command %r failed with exit status %d" % (cmd, rc)) if sys.platform == 'darwin': - from distutils import sysconfig _cfg_target = None _cfg_target_split = None @@ -95,6 +94,7 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): if sys.platform == 'darwin': global _cfg_target, _cfg_target_split if _cfg_target is None: + from distutils import sysconfig _cfg_target = sysconfig.get_config_var( 'MACOSX_DEPLOYMENT_TARGET') or '' if _cfg_target: diff --git a/sysconfig.py b/sysconfig.py index 570a612d..b51629eb 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -15,6 +15,7 @@ import re import sys from .errors import DistutilsPlatformError +from .util import get_platform, get_host_platform # These are needed in a couple of spots, so just compute them once. PREFIX = os.path.normpath(sys.prefix) diff --git a/util.py b/util.py index 15cd2ad9..50550e18 100644 --- a/util.py +++ b/util.py @@ -15,7 +15,7 @@ from distutils.spawn import spawn from distutils import log from distutils.errors import DistutilsByteCompileError -def get_platform (): +def get_host_platform(): """Return a string that identifies the current platform. This is used mainly to distinguish platform-specific build directories and platform-specific built distributions. Typically includes the OS name and version and the @@ -38,6 +38,8 @@ def get_platform (): if os.name == 'nt': if 'amd64' in sys.version.lower(): return 'win-amd64' + if '(arm)' in sys.version.lower(): + return 'win-arm32' return sys.platform # Set for cross builds explicitly @@ -90,8 +92,16 @@ def get_platform (): return "%s-%s-%s" % (osname, release, machine) -# get_platform () - +def get_platform(): + if os.name == 'nt': + TARGET_TO_PLAT = { + 'x86' : 'win32', + 'x64' : 'win-amd64', + 'arm' : 'win-arm32', + } + return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform() + else: + return get_host_platform() def convert_path (pathname): """Return 'pathname' as a name that will work on the native filesystem, -- cgit v1.2.1 From 736a36a6342656cf7fda44eb3060ce2d3d092ef0 Mon Sep 17 00:00:00 2001 From: xdegaye Date: Mon, 29 Apr 2019 09:27:40 +0200 Subject: bpo-21536: On Android, C extensions are linked to libpython (GH-12989) --- command/build_ext.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/command/build_ext.py b/command/build_ext.py index 1672d02a..c3b96024 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -714,5 +714,20 @@ class build_ext(Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] + # On Android only the main executable and LD_PRELOADs are considered + # to be RTLD_GLOBAL, all the dependencies of the main executable + # remain RTLD_LOCAL and so the shared libraries must be linked with + # libpython when python is built with a shared python library (issue + # bpo-21536). + else: + from distutils.sysconfig import get_config_var + if get_config_var('Py_ENABLE_SHARED'): + # Either a native build on an Android device or the + # cross-compilation of Python. + if (hasattr(sys, 'getandroidapilevel') or + ('_PYTHON_HOST_PLATFORM' in os.environ and + get_config_var('ANDROID_API_LEVEL') != 0)): + ldversion = get_config_var('LDVERSION') + return ext.libraries + ['python' + ldversion] return ext.libraries -- cgit v1.2.1 From d307b46c898fa5ee902344dab7ecc6e8ed595987 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Thu, 16 May 2019 09:23:51 +0200 Subject: Moved keywords section into separate document, added link in index --- docs/index.txt | 1 + docs/keywords.txt | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++ docs/setuptools.txt | 173 ---------------------------------------------------- 3 files changed, 173 insertions(+), 173 deletions(-) create mode 100644 docs/keywords.txt diff --git a/docs/index.txt b/docs/index.txt index 13a46e74..60c4f331 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -17,6 +17,7 @@ Documentation content: :maxdepth: 2 setuptools + keywords pkg_resources python3 development diff --git a/docs/keywords.txt b/docs/keywords.txt new file mode 100644 index 00000000..a1097aca --- /dev/null +++ b/docs/keywords.txt @@ -0,0 +1,172 @@ +============================== +Supported ``setup()`` Keywords +============================== + +The following keyword arguments to ``setup()`` are supported by ``setuptools``. +All of them are optional; you do not have to supply them unless you need the +associated ``setuptools`` feature. + +``include_package_data`` + If set to ``True``, this tells ``setuptools`` to automatically include any + data files it finds inside your package directories that are specified by + your ``MANIFEST.in`` file. For more information, see the section below on + `Including Data Files`_. + +``exclude_package_data`` + A dictionary mapping package names to lists of glob patterns that should + be *excluded* from your package directories. You can use this to trim back + any excess files included by ``include_package_data``. For a complete + description and examples, see the section below on `Including Data Files`_. + +``package_data`` + A dictionary mapping package names to lists of glob patterns. For a + complete description and examples, see the section below on `Including + Data Files`_. You do not need to use this option if you are using + ``include_package_data``, unless you need to add e.g. files that are + generated by your setup script and build process. (And are therefore not + in source control or are files that you don't want to include in your + source distribution.) + +``zip_safe`` + A boolean (True or False) flag specifying whether the project can be + safely installed and run from a zip file. If this argument is not + supplied, the ``bdist_egg`` command will have to analyze all of your + project's contents for possible problems each time it builds an egg. + +``install_requires`` + A string or list of strings specifying what other distributions need to + be installed when this one is. See the section below on `Declaring + Dependencies`_ for details and examples of the format of this argument. + +``entry_points`` + A dictionary mapping entry point group names to strings or lists of strings + defining the entry points. Entry points are used to support dynamic + discovery of services or plugins provided by a project. See `Dynamic + Discovery of Services and Plugins`_ for details and examples of the format + of this argument. In addition, this keyword is used to support `Automatic + Script Creation`_. + +``extras_require`` + A dictionary mapping names of "extras" (optional features of your project) + to strings or lists of strings specifying what other distributions must be + installed to support those features. See the section below on `Declaring + Dependencies`_ for details and examples of the format of this argument. + +``python_requires`` + A string corresponding to a version specifier (as defined in PEP 440) for + the Python version, used to specify the Requires-Python defined in PEP 345. + +``setup_requires`` + A string or list of strings specifying what other distributions need to + be present in order for the *setup script* to run. ``setuptools`` will + attempt to obtain these (even going so far as to download them using + ``EasyInstall``) before processing the rest of the setup script or commands. + This argument is needed if you are using distutils extensions as part of + your build process; for example, extensions that process setup() arguments + and turn them into EGG-INFO metadata files. + + (Note: projects listed in ``setup_requires`` will NOT be automatically + installed on the system where the setup script is being run. They are + simply downloaded to the ./.eggs directory if they're not locally available + already. If you want them to be installed, as well as being available + when the setup script is run, you should add them to ``install_requires`` + **and** ``setup_requires``.) + +``dependency_links`` + A list of strings naming URLs to be searched when satisfying dependencies. + These links will be used if needed to install packages specified by + ``setup_requires`` or ``tests_require``. They will also be written into + the egg's metadata for use by tools like EasyInstall to use when installing + an ``.egg`` file. + +``namespace_packages`` + A list of strings naming the project's "namespace packages". A namespace + package is a package that may be split across multiple project + distributions. For example, Zope 3's ``zope`` package is a namespace + package, because subpackages like ``zope.interface`` and ``zope.publisher`` + may be distributed separately. The egg runtime system can automatically + merge such subpackages into a single parent package at runtime, as long + as you declare them in each project that contains any subpackages of the + namespace package, and as long as the namespace package's ``__init__.py`` + does not contain any code other than a namespace declaration. See the + section below on `Namespace Packages`_ for more information. + +``test_suite`` + A string naming a ``unittest.TestCase`` subclass (or a package or module + containing one or more of them, or a method of such a subclass), or naming + a function that can be called with no arguments and returns a + ``unittest.TestSuite``. If the named suite is a module, and the module + has an ``additional_tests()`` function, it is called and the results are + added to the tests to be run. If the named suite is a package, any + submodules and subpackages are recursively added to the overall test suite. + + Specifying this argument enables use of the `test`_ command to run the + specified test suite, e.g. via ``setup.py test``. See the section on the + `test`_ command below for more details. + +``tests_require`` + If your project's tests need one or more additional packages besides those + needed to install it, you can use this option to specify them. It should + be a string or list of strings specifying what other distributions need to + be present for the package's tests to run. When you run the ``test`` + command, ``setuptools`` will attempt to obtain these (even going + so far as to download them using ``EasyInstall``). Note that these + required projects will *not* be installed on the system where the tests + are run, but only downloaded to the project's setup directory if they're + not already installed locally. + +.. _test_loader: + +``test_loader`` + If you would like to use a different way of finding tests to run than what + setuptools normally uses, you can specify a module name and class name in + this argument. The named class must be instantiable with no arguments, and + its instances must support the ``loadTestsFromNames()`` method as defined + in the Python ``unittest`` module's ``TestLoader`` class. Setuptools will + pass only one test "name" in the `names` argument: the value supplied for + the ``test_suite`` argument. The loader you specify may interpret this + string in any way it likes, as there are no restrictions on what may be + contained in a ``test_suite`` string. + + The module name and class name must be separated by a ``:``. The default + value of this argument is ``"setuptools.command.test:ScanningLoader"``. If + you want to use the default ``unittest`` behavior, you can specify + ``"unittest:TestLoader"`` as your ``test_loader`` argument instead. This + will prevent automatic scanning of submodules and subpackages. + + The module and class you specify here may be contained in another package, + as long as you use the ``tests_require`` option to ensure that the package + containing the loader class is available when the ``test`` command is run. + +``eager_resources`` + A list of strings naming resources that should be extracted together, if + any of them is needed, or if any C extensions included in the project are + imported. This argument is only useful if the project will be installed as + a zipfile, and there is a need to have all of the listed resources be + extracted to the filesystem *as a unit*. Resources listed here + should be '/'-separated paths, relative to the source root, so to list a + resource ``foo.png`` in package ``bar.baz``, you would include the string + ``bar/baz/foo.png`` in this argument. + + If you only need to obtain resources one at a time, or you don't have any C + extensions that access other files in the project (such as data files or + shared libraries), you probably do NOT need this argument and shouldn't + mess with it. For more details on how this argument works, see the section + below on `Automatic Resource Extraction`_. + +``use_2to3`` + Convert the source code from Python 2 to Python 3 with 2to3 during the + build process. See :doc:`python3` for more details. + +``convert_2to3_doctests`` + List of doctest source files that need to be converted with 2to3. + See :doc:`python3` for more details. + +``use_2to3_fixers`` + A list of modules to search for additional fixers to be used during + the 2to3 conversion. See :doc:`python3` for more details. + +``project_urls`` + An arbitrary map of URL names to hyperlinks, allowing more extensible + documentation of where various resources can be found than the simple + ``url`` and ``download_url`` options provide. diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 64b385cb..7c0535d0 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -239,179 +239,6 @@ pre- or post-release tags. See the following sections for more details: * The `egg_info`_ command -New and Changed ``setup()`` Keywords -==================================== - -The following keyword arguments to ``setup()`` are added or changed by -``setuptools``. All of them are optional; you do not have to supply them -unless you need the associated ``setuptools`` feature. - -``include_package_data`` - If set to ``True``, this tells ``setuptools`` to automatically include any - data files it finds inside your package directories that are specified by - your ``MANIFEST.in`` file. For more information, see the section below on - `Including Data Files`_. - -``exclude_package_data`` - A dictionary mapping package names to lists of glob patterns that should - be *excluded* from your package directories. You can use this to trim back - any excess files included by ``include_package_data``. For a complete - description and examples, see the section below on `Including Data Files`_. - -``package_data`` - A dictionary mapping package names to lists of glob patterns. For a - complete description and examples, see the section below on `Including - Data Files`_. You do not need to use this option if you are using - ``include_package_data``, unless you need to add e.g. files that are - generated by your setup script and build process. (And are therefore not - in source control or are files that you don't want to include in your - source distribution.) - -``zip_safe`` - A boolean (True or False) flag specifying whether the project can be - safely installed and run from a zip file. If this argument is not - supplied, the ``bdist_egg`` command will have to analyze all of your - project's contents for possible problems each time it builds an egg. - -``install_requires`` - A string or list of strings specifying what other distributions need to - be installed when this one is. See the section below on `Declaring - Dependencies`_ for details and examples of the format of this argument. - -``entry_points`` - A dictionary mapping entry point group names to strings or lists of strings - defining the entry points. Entry points are used to support dynamic - discovery of services or plugins provided by a project. See `Dynamic - Discovery of Services and Plugins`_ for details and examples of the format - of this argument. In addition, this keyword is used to support `Automatic - Script Creation`_. - -``extras_require`` - A dictionary mapping names of "extras" (optional features of your project) - to strings or lists of strings specifying what other distributions must be - installed to support those features. See the section below on `Declaring - Dependencies`_ for details and examples of the format of this argument. - -``python_requires`` - A string corresponding to a version specifier (as defined in PEP 440) for - the Python version, used to specify the Requires-Python defined in PEP 345. - -``setup_requires`` - A string or list of strings specifying what other distributions need to - be present in order for the *setup script* to run. ``setuptools`` will - attempt to obtain these (even going so far as to download them using - ``EasyInstall``) before processing the rest of the setup script or commands. - This argument is needed if you are using distutils extensions as part of - your build process; for example, extensions that process setup() arguments - and turn them into EGG-INFO metadata files. - - (Note: projects listed in ``setup_requires`` will NOT be automatically - installed on the system where the setup script is being run. They are - simply downloaded to the ./.eggs directory if they're not locally available - already. If you want them to be installed, as well as being available - when the setup script is run, you should add them to ``install_requires`` - **and** ``setup_requires``.) - -``dependency_links`` - A list of strings naming URLs to be searched when satisfying dependencies. - These links will be used if needed to install packages specified by - ``setup_requires`` or ``tests_require``. They will also be written into - the egg's metadata for use by tools like EasyInstall to use when installing - an ``.egg`` file. - -``namespace_packages`` - A list of strings naming the project's "namespace packages". A namespace - package is a package that may be split across multiple project - distributions. For example, Zope 3's ``zope`` package is a namespace - package, because subpackages like ``zope.interface`` and ``zope.publisher`` - may be distributed separately. The egg runtime system can automatically - merge such subpackages into a single parent package at runtime, as long - as you declare them in each project that contains any subpackages of the - namespace package, and as long as the namespace package's ``__init__.py`` - does not contain any code other than a namespace declaration. See the - section below on `Namespace Packages`_ for more information. - -``test_suite`` - A string naming a ``unittest.TestCase`` subclass (or a package or module - containing one or more of them, or a method of such a subclass), or naming - a function that can be called with no arguments and returns a - ``unittest.TestSuite``. If the named suite is a module, and the module - has an ``additional_tests()`` function, it is called and the results are - added to the tests to be run. If the named suite is a package, any - submodules and subpackages are recursively added to the overall test suite. - - Specifying this argument enables use of the `test`_ command to run the - specified test suite, e.g. via ``setup.py test``. See the section on the - `test`_ command below for more details. - -``tests_require`` - If your project's tests need one or more additional packages besides those - needed to install it, you can use this option to specify them. It should - be a string or list of strings specifying what other distributions need to - be present for the package's tests to run. When you run the ``test`` - command, ``setuptools`` will attempt to obtain these (even going - so far as to download them using ``EasyInstall``). Note that these - required projects will *not* be installed on the system where the tests - are run, but only downloaded to the project's setup directory if they're - not already installed locally. - -.. _test_loader: - -``test_loader`` - If you would like to use a different way of finding tests to run than what - setuptools normally uses, you can specify a module name and class name in - this argument. The named class must be instantiable with no arguments, and - its instances must support the ``loadTestsFromNames()`` method as defined - in the Python ``unittest`` module's ``TestLoader`` class. Setuptools will - pass only one test "name" in the `names` argument: the value supplied for - the ``test_suite`` argument. The loader you specify may interpret this - string in any way it likes, as there are no restrictions on what may be - contained in a ``test_suite`` string. - - The module name and class name must be separated by a ``:``. The default - value of this argument is ``"setuptools.command.test:ScanningLoader"``. If - you want to use the default ``unittest`` behavior, you can specify - ``"unittest:TestLoader"`` as your ``test_loader`` argument instead. This - will prevent automatic scanning of submodules and subpackages. - - The module and class you specify here may be contained in another package, - as long as you use the ``tests_require`` option to ensure that the package - containing the loader class is available when the ``test`` command is run. - -``eager_resources`` - A list of strings naming resources that should be extracted together, if - any of them is needed, or if any C extensions included in the project are - imported. This argument is only useful if the project will be installed as - a zipfile, and there is a need to have all of the listed resources be - extracted to the filesystem *as a unit*. Resources listed here - should be '/'-separated paths, relative to the source root, so to list a - resource ``foo.png`` in package ``bar.baz``, you would include the string - ``bar/baz/foo.png`` in this argument. - - If you only need to obtain resources one at a time, or you don't have any C - extensions that access other files in the project (such as data files or - shared libraries), you probably do NOT need this argument and shouldn't - mess with it. For more details on how this argument works, see the section - below on `Automatic Resource Extraction`_. - -``use_2to3`` - Convert the source code from Python 2 to Python 3 with 2to3 during the - build process. See :doc:`python3` for more details. - -``convert_2to3_doctests`` - List of doctest source files that need to be converted with 2to3. - See :doc:`python3` for more details. - -``use_2to3_fixers`` - A list of modules to search for additional fixers to be used during - the 2to3 conversion. See :doc:`python3` for more details. - -``project_urls`` - An arbitrary map of URL names to hyperlinks, allowing more extensible - documentation of where various resources can be found than the simple - ``url`` and ``download_url`` options provide. - - Using ``find_packages()`` ------------------------- -- cgit v1.2.1 From 46a73d96faedee345834f29a7ac218de64519da2 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Thu, 16 May 2019 10:29:11 +0200 Subject: Fixed broken implicit links --- docs/keywords.txt | 34 +++++++++++++++++----------------- docs/setuptools.txt | 10 +++++++++- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/docs/keywords.txt b/docs/keywords.txt index a1097aca..6a8fc2f2 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -9,19 +9,19 @@ associated ``setuptools`` feature. ``include_package_data`` If set to ``True``, this tells ``setuptools`` to automatically include any data files it finds inside your package directories that are specified by - your ``MANIFEST.in`` file. For more information, see the section below on - `Including Data Files`_. + your ``MANIFEST.in`` file. For more information, see the section on + :ref:`Including Data Files`. ``exclude_package_data`` A dictionary mapping package names to lists of glob patterns that should be *excluded* from your package directories. You can use this to trim back any excess files included by ``include_package_data``. For a complete - description and examples, see the section below on `Including Data Files`_. + description and examples, see the section on :ref:`Including Data Files`. ``package_data`` A dictionary mapping package names to lists of glob patterns. For a - complete description and examples, see the section below on `Including - Data Files`_. You do not need to use this option if you are using + complete description and examples, see the section on :ref:`Including Data + Files`. You do not need to use this option if you are using ``include_package_data``, unless you need to add e.g. files that are generated by your setup script and build process. (And are therefore not in source control or are files that you don't want to include in your @@ -35,22 +35,22 @@ associated ``setuptools`` feature. ``install_requires`` A string or list of strings specifying what other distributions need to - be installed when this one is. See the section below on `Declaring - Dependencies`_ for details and examples of the format of this argument. + be installed when this one is. See the section on :ref:`Declaring + Dependencies` for details and examples of the format of this argument. ``entry_points`` A dictionary mapping entry point group names to strings or lists of strings defining the entry points. Entry points are used to support dynamic - discovery of services or plugins provided by a project. See `Dynamic - Discovery of Services and Plugins`_ for details and examples of the format - of this argument. In addition, this keyword is used to support `Automatic - Script Creation`_. + discovery of services or plugins provided by a project. See :ref:`Dynamic + Discovery of Services and Plugins` for details and examples of the format + of this argument. In addition, this keyword is used to support + :ref:`Automatic Script Creation`. ``extras_require`` A dictionary mapping names of "extras" (optional features of your project) to strings or lists of strings specifying what other distributions must be - installed to support those features. See the section below on `Declaring - Dependencies`_ for details and examples of the format of this argument. + installed to support those features. See the section on :ref:`Declaring + Dependencies` for details and examples of the format of this argument. ``python_requires`` A string corresponding to a version specifier (as defined in PEP 440) for @@ -89,7 +89,7 @@ associated ``setuptools`` feature. as you declare them in each project that contains any subpackages of the namespace package, and as long as the namespace package's ``__init__.py`` does not contain any code other than a namespace declaration. See the - section below on `Namespace Packages`_ for more information. + section on :ref:`Namespace Packages` for more information. ``test_suite`` A string naming a ``unittest.TestCase`` subclass (or a package or module @@ -100,9 +100,9 @@ associated ``setuptools`` feature. added to the tests to be run. If the named suite is a package, any submodules and subpackages are recursively added to the overall test suite. - Specifying this argument enables use of the `test`_ command to run the + Specifying this argument enables use of the :ref:`test` command to run the specified test suite, e.g. via ``setup.py test``. See the section on the - `test`_ command below for more details. + :ref:`test` command below for more details. ``tests_require`` If your project's tests need one or more additional packages besides those @@ -152,7 +152,7 @@ associated ``setuptools`` feature. extensions that access other files in the project (such as data files or shared libraries), you probably do NOT need this argument and shouldn't mess with it. For more details on how this argument works, see the section - below on `Automatic Resource Extraction`_. + below on :ref:`Automatic Resource Extraction`. ``use_2to3`` Convert the source code from Python 2 to Python 3 with 2to3 during the diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 7c0535d0..e07ac296 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -345,6 +345,8 @@ With this layout, the package directory is specified as ``src``, as such:: .. _PEP 420: https://www.python.org/dev/peps/pep-0420/ +.. _Automatic Script Creation: + Automatic Script Creation ========================= @@ -432,6 +434,7 @@ and version is in use. The header script will check this and exit with an error if the ``.egg`` file has been renamed or is invoked via a symlink that changes its base name. +.. _Declaring Dependencies: Declaring Dependencies ====================== @@ -685,6 +688,8 @@ detailed in `PEP 508`_. .. _PEP 508: https://www.python.org/dev/peps/pep-0508/ +.. _Including Data Files: + Including Data Files ==================== @@ -860,6 +865,7 @@ no supported facility to reliably retrieve these resources. Instead, the PyPA recommends that any data files you wish to be accessible at run time be included in the package. +.. _Automatic Resource Extraction: Automatic Resource Extraction ----------------------------- @@ -905,6 +911,8 @@ Extensible Applications and Frameworks .. _Entry Points: +.. _Dynamic Discovery of Services and Plugins: + Dynamic Discovery of Services and Plugins ----------------------------------------- @@ -1977,7 +1985,7 @@ result (which must be a ``unittest.TestSuite``) is added to the tests to be run. If the named suite is a package, any submodules and subpackages are recursively added to the overall test suite. (Note: if your project specifies a ``test_loader``, the rules for processing the chosen ``test_suite`` may -differ; see the `test_loader`_ documentation for more details.) +differ; see the :ref:`test_loader ` documentation for more details.) Note that many test systems including ``doctest`` support wrapping their non-``unittest`` tests in ``TestSuite`` objects. So, if you are using a test -- cgit v1.2.1 From d2de0b92773c4304d6264258228a008499ccfd21 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Thu, 16 May 2019 11:03:51 +0200 Subject: Transcibed all keywords from https://docs.python.org/3/distutils/apiref.html#distutils.core.setup --- docs/keywords.txt | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/docs/keywords.txt b/docs/keywords.txt index 6a8fc2f2..877a4caf 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -3,9 +3,96 @@ Supported ``setup()`` Keywords ============================== The following keyword arguments to ``setup()`` are supported by ``setuptools``. -All of them are optional; you do not have to supply them unless you need the +Some of them are optional; you do not have to supply them unless you need the associated ``setuptools`` feature. +``name`` + A string specifying the name of the package. + +``version`` + A string specifying the version number of the package. + +``description`` + A string describing the package in a single line. + +``long_description`` + A string providing a longer description of the package. + +``author`` + A string specifying the author of the package. + +``author_email`` + A string specifying the email address of the package author. + +``maintainer`` + A string specifying the name of the current maintainer, if different from + the author. Note that if the maintainer is provided, setuptools will use it + as the author in ``PKG-INFO``. + +``maintainer_email`` + A string specifying the email address of the current maintainer, if + different from the author. + +``url`` + A string specifying the URL for the package homepage. + +``download_url`` + A string specifying the URL to download the package. + +``packages`` + A list of strings specifying the packages that setuptools will manipulate. + +``py_modules`` + A list of strings specifying the modules that setuptools will manipulate. + +``scripts`` + A list of strings specifying the standalone script files to be built and + installed. + +``ext_modules`` + A list of instances of ``setuptools.Extension`` providing the list of + Python extensions to be built. + +``classifiers`` + A list of strings describing the categories for the package. + +``distclass`` + A subclass of ``Distribution`` to use. + +``script_name`` + A string specifying the name of the setup.py script -- defaults to + ``sys.argv[0]`` + +``script_args`` + A list of strings defining the arguments to supply to the setup script. + +``options`` + A dictionary providing the default options for the setup script. + +``license`` + A string specifying the license of the package. + +``keywords`` + A list of strings or a comma-separated string providing descriptive + meta-data. See: `PEP 0314`_. + +.. _PEP 0314: https://www.python.org/dev/peps/pep-0314/ + +``platforms`` + A list of strings or comma-separated string. + +``cmdclass`` + A dictionary providing a mapping of command names to ``Command`` + subclasses. + +``data_files`` + A list of strings specifying the data files to install. + +``package_dir`` + A dictionary providing a mapping of package to directory names. + +.. Below are setuptools keywords, above are distutils + ``include_package_data`` If set to ``True``, this tells ``setuptools`` to automatically include any data files it finds inside your package directories that are specified by -- cgit v1.2.1 From 734e5106be681eec7f1717f9feb4836b025dbe97 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Thu, 16 May 2019 11:07:58 +0200 Subject: Added changelog fragment --- changelog.d/1700.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1700.change.rst diff --git a/changelog.d/1700.change.rst b/changelog.d/1700.change.rst new file mode 100644 index 00000000..f66046a2 --- /dev/null +++ b/changelog.d/1700.change.rst @@ -0,0 +1 @@ +Document all supported keywords by migrating the ones from distutils. -- cgit v1.2.1 From a7fcfcdf8e224f42aa83c32363df3a39100ca69c Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Thu, 16 May 2019 14:18:59 +0200 Subject: Added warnings for data_files, setup_requires and dependency_links --- docs/keywords.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/keywords.txt b/docs/keywords.txt index 877a4caf..81b80344 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -86,6 +86,11 @@ associated ``setuptools`` feature. subclasses. ``data_files`` + + .. warning:: + ``data_files`` is deprecated. It does not work with wheels, so it + should be avoided. + A list of strings specifying the data files to install. ``package_dir`` @@ -144,6 +149,10 @@ associated ``setuptools`` feature. the Python version, used to specify the Requires-Python defined in PEP 345. ``setup_requires`` + + .. warning:: + Using ``setup_requires`` is discouraged in favour for `PEP-518`_ + A string or list of strings specifying what other distributions need to be present in order for the *setup script* to run. ``setuptools`` will attempt to obtain these (even going so far as to download them using @@ -159,7 +168,13 @@ associated ``setuptools`` feature. when the setup script is run, you should add them to ``install_requires`` **and** ``setup_requires``.) +.. _PEP-518: http://www.python.org/dev/peps/pep-0518/ + ``dependency_links`` + + .. warning:: + ``dependency_links`` is deprecated. It is not supported anymore by pip. + A list of strings naming URLs to be searched when satisfying dependencies. These links will be used if needed to install packages specified by ``setup_requires`` or ``tests_require``. They will also be written into -- cgit v1.2.1 From 13c1825df412b6ed63ff05594af0e4dde5ef0455 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Thu, 16 May 2019 14:19:25 +0200 Subject: Maintain old header and link to new keywords document --- docs/keywords.txt | 2 ++ docs/setuptools.txt | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/docs/keywords.txt b/docs/keywords.txt index 81b80344..2666ba85 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -1,3 +1,5 @@ +.. _Supported setup() Keywords: + ============================== Supported ``setup()`` Keywords ============================== diff --git a/docs/setuptools.txt b/docs/setuptools.txt index e07ac296..4ddc2b7a 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -239,6 +239,13 @@ pre- or post-release tags. See the following sections for more details: * The `egg_info`_ command +New and Changed ``setup()`` Keywords +==================================== + +The keywords supported by setuptools can be found in :ref:`Supported setup() +Keywords` + + Using ``find_packages()`` ------------------------- -- cgit v1.2.1 From cc9e041823a6a06cabeaa27949abc4e2ff08aee0 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Sat, 18 May 2019 12:37:06 +0200 Subject: Added unused requires, obsolete and requires for completenes --- docs/keywords.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/keywords.txt b/docs/keywords.txt index 2666ba85..ff757231 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -98,6 +98,23 @@ associated ``setuptools`` feature. ``package_dir`` A dictionary providing a mapping of package to directory names. +``requires`` + + .. warning:: + ``requires`` is superseded by ``install_requires`` and should not be used + anymore. + +``obsoletes`` + + .. warning:: + ``obsoletes`` is not supported by ``pip`` and should not be used. + +``requires`` + + .. warning:: + ``requires`` is not supported by ``pip`` and should not be used. + + .. Below are setuptools keywords, above are distutils ``include_package_data`` -- cgit v1.2.1 From 0b323b5e158bfd284cdd6c99b130cfc05eb05187 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Sat, 18 May 2019 12:37:48 +0200 Subject: Added ext_package --- docs/keywords.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/keywords.txt b/docs/keywords.txt index ff757231..4d7efd38 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -51,6 +51,10 @@ associated ``setuptools`` feature. A list of strings specifying the standalone script files to be built and installed. +``ext_package`` + A string specifying the base package name for the extensions provided by + this package. + ``ext_modules`` A list of instances of ``setuptools.Extension`` providing the list of Python extensions to be built. -- cgit v1.2.1 From e21b663802f7ca97c1cd4c2866338c0129e9a502 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Sat, 18 May 2019 12:37:58 +0200 Subject: added use_2to3_exclude_fixers --- docs/keywords.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/keywords.txt b/docs/keywords.txt index 4d7efd38..7881e285 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -291,6 +291,9 @@ associated ``setuptools`` feature. A list of modules to search for additional fixers to be used during the 2to3 conversion. See :doc:`python3` for more details. +``use_2to3_exclude_fixers`` + List of fixer names to be skipped. + ``project_urls`` An arbitrary map of URL names to hyperlinks, allowing more extensible documentation of where various resources can be found than the simple -- cgit v1.2.1 From 4521d88caac6a68a1baf55c401589f8ce7b04243 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Sat, 18 May 2019 12:38:09 +0200 Subject: added long_description_content_type --- docs/keywords.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/keywords.txt b/docs/keywords.txt index 7881e285..5086bcf1 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -20,6 +20,10 @@ associated ``setuptools`` feature. ``long_description`` A string providing a longer description of the package. +``long_description_content_type`` + A string specifying the content type is used for the ``long_description`` + (e.g. ``text/markdown``) + ``author`` A string specifying the author of the package. -- cgit v1.2.1 From 4786eb4053e1eac5cbf869b290a90436a9cfe347 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Sat, 18 May 2019 12:59:56 +0200 Subject: Made keywords a subpage (via include directive) maintaining the old structure --- docs/index.txt | 1 - docs/keywords.txt | 10 ---------- docs/setuptools.txt | 7 +++++-- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/docs/index.txt b/docs/index.txt index 60c4f331..13a46e74 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -17,7 +17,6 @@ Documentation content: :maxdepth: 2 setuptools - keywords pkg_resources python3 development diff --git a/docs/keywords.txt b/docs/keywords.txt index 5086bcf1..cbaf7ae9 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -1,13 +1,3 @@ -.. _Supported setup() Keywords: - -============================== -Supported ``setup()`` Keywords -============================== - -The following keyword arguments to ``setup()`` are supported by ``setuptools``. -Some of them are optional; you do not have to supply them unless you need the -associated ``setuptools`` feature. - ``name`` A string specifying the name of the package. diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 4ddc2b7a..ae85b8f5 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -242,8 +242,11 @@ pre- or post-release tags. See the following sections for more details: New and Changed ``setup()`` Keywords ==================================== -The keywords supported by setuptools can be found in :ref:`Supported setup() -Keywords` +The following keyword arguments to ``setup()`` are supported by ``setuptools``. +Some of them are optional; you do not have to supply them unless you need the +associated ``setuptools`` feature. + +.. include:: keywords.txt Using ``find_packages()`` -- cgit v1.2.1 From 345f7e6fbc3118f3fd6c97a307dbbd9cda30a84c Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Sat, 18 May 2019 13:05:39 +0200 Subject: s/requires/provides/ --- docs/keywords.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/keywords.txt b/docs/keywords.txt index cbaf7ae9..488ed20b 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -107,10 +107,10 @@ .. warning:: ``obsoletes`` is not supported by ``pip`` and should not be used. -``requires`` +``provides`` .. warning:: - ``requires`` is not supported by ``pip`` and should not be used. + ``provides`` is not supported by ``pip`` and should not be used. .. Below are setuptools keywords, above are distutils -- cgit v1.2.1 From 73dbe0ec07e3738bc592e92def65209b45a14dfb Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Sat, 18 May 2019 13:06:53 +0200 Subject: s/favour/favor/ Co-Authored-By: Benoit Pierre --- docs/keywords.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/keywords.txt b/docs/keywords.txt index 488ed20b..db50163a 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -168,7 +168,7 @@ ``setup_requires`` .. warning:: - Using ``setup_requires`` is discouraged in favour for `PEP-518`_ + Using ``setup_requires`` is discouraged in favor of `PEP-518`_ A string or list of strings specifying what other distributions need to be present in order for the *setup script* to run. ``setuptools`` will -- cgit v1.2.1 From 6a3674a18d22dbea4d6b513b8e8a7ac28ada5eba Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Sat, 18 May 2019 22:37:49 +0200 Subject: Added meat to obsoletes and provides, removed that they should not be used. --- docs/keywords.txt | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/docs/keywords.txt b/docs/keywords.txt index db50163a..d56014e0 100644 --- a/docs/keywords.txt +++ b/docs/keywords.txt @@ -105,13 +105,49 @@ ``obsoletes`` .. warning:: - ``obsoletes`` is not supported by ``pip`` and should not be used. + ``obsoletes`` is currently ignored by ``pip``. + + List of strings describing packages which this package renders obsolete, + meaning that the two projects should not be installed at the same time. + + Version declarations can be supplied. Version numbers must be in the format + specified in Version specifiers (e.g. ``foo (<3.0)``). + + This field may be followed by an environment marker after a semicolon (e.g. + ``foo; os_name == "posix"``) + + The most common use of this field will be in case a project name changes, + e.g. Gorgon 2.3 gets subsumed into Torqued Python 1.0. When you install + Torqued Python, the Gorgon distribution should be removed. ``provides`` .. warning:: - ``provides`` is not supported by ``pip`` and should not be used. - + ``provides`` is currently ignored by ``pip``. + + List of strings describing package- and virtual package names contained + within this package. + + A package may provide additional names, e.g. to indicate that multiple + projects have been bundled together. For instance, source distributions of + the ZODB project have historically included the transaction project, which + is now available as a separate distribution. Installing such a source + distribution satisfies requirements for both ZODB and transaction. + + A package may also provide a “virtual†project name, which does not + correspond to any separately-distributed project: such a name might be used + to indicate an abstract capability which could be supplied by one of + multiple projects. E.g., multiple projects might supply RDBMS bindings for + use by a given ORM: each project might declare that it provides + ORM-bindings, allowing other projects to depend only on having at most one + of them installed. + + A version declaration may be supplied and must follow the rules described in + Version specifiers. The distribution’s version number will be implied if + none is specified (e.g. ``foo (<3.0)``). + + Each package may be followed by an environment marker after a semicolon + (e.g. ``foo; os_name == "posix"``). .. Below are setuptools keywords, above are distutils -- cgit v1.2.1 From 9d9a6d30b814bfdd7265e67ae9eea2f6e2db1657 Mon Sep 17 00:00:00 2001 From: "E. M. Bray" Date: Fri, 24 May 2019 17:33:47 +0200 Subject: bpo-21536: On Cygwin, C extensions must be linked with libpython (GH-13549) It is also possible to link against a library or executable with a statically linked libpython, but not both with the same DLL. In fact building a statically linked python is currently broken on Cygwin for other (related) reasons. The same problem applies to other POSIX-like layers over Windows (MinGW, MSYS) but Python's build system does not seem to attempt to support those platforms at the moment. --- command/build_ext.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/command/build_ext.py b/command/build_ext.py index c3b96024..2d7cdf06 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -714,20 +714,32 @@ class build_ext(Command): # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] - # On Android only the main executable and LD_PRELOADs are considered - # to be RTLD_GLOBAL, all the dependencies of the main executable - # remain RTLD_LOCAL and so the shared libraries must be linked with - # libpython when python is built with a shared python library (issue - # bpo-21536). else: + # On Android only the main executable and LD_PRELOADs are considered + # to be RTLD_GLOBAL, all the dependencies of the main executable + # remain RTLD_LOCAL and so the shared libraries must be linked with + # libpython when python is built with a shared python library (issue + # bpo-21536). + # On Cygwin (and if required, other POSIX-like platforms based on + # Windows like MinGW) it is simply necessary that all symbols in + # shared libraries are resolved at link time. from distutils.sysconfig import get_config_var + link_libpython = False if get_config_var('Py_ENABLE_SHARED'): - # Either a native build on an Android device or the - # cross-compilation of Python. - if (hasattr(sys, 'getandroidapilevel') or - ('_PYTHON_HOST_PLATFORM' in os.environ and - get_config_var('ANDROID_API_LEVEL') != 0)): - ldversion = get_config_var('LDVERSION') - return ext.libraries + ['python' + ldversion] + # A native build on an Android device or on Cygwin + if hasattr(sys, 'getandroidapilevel'): + link_libpython = True + elif sys.platform == 'cygwin': + link_libpython = True + elif '_PYTHON_HOST_PLATFORM' in os.environ: + # We are cross-compiling for one of the relevant platforms + if get_config_var('ANDROID_API_LEVEL') != 0: + link_libpython = True + elif get_config_var('MACHDEP') == 'cygwin': + link_libpython = True + + if link_libpython: + ldversion = get_config_var('LDVERSION') + return ext.libraries + ['python' + ldversion] return ext.libraries -- cgit v1.2.1 From b30cb4676572d51a26f3b74a40035df97f8a41c7 Mon Sep 17 00:00:00 2001 From: Xtreak Date: Mon, 3 Jun 2019 04:42:33 +0530 Subject: Fix typos in docs and docstrings (GH-13745) --- ccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index b71d1d39..1a411ed1 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -545,7 +545,7 @@ class CCompiler: 'extra_preargs' and 'extra_postargs' are implementation- dependent. On platforms that have the notion of a command-line (e.g. Unix, DOS/Windows), they are most likely lists of strings: extra - command-line arguments to prepand/append to the compiler command + command-line arguments to prepend/append to the compiler command line. On other platforms, consult the implementation class documentation. In any event, they are intended as an escape hatch for those occasions when the abstract compiler framework doesn't -- cgit v1.2.1 From d048c412f43c2b9eaa1e282fa2d3bacec2a2d82e Mon Sep 17 00:00:00 2001 From: Marcin Niemira Date: Sun, 9 Jun 2019 07:05:06 +1000 Subject: bpo-11122: fix hardcoded path checking for rpmbuild in bdist_rpm.py (GH-10594) --- command/bdist_rpm.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 20ca7ac6..74381cc6 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -309,10 +309,7 @@ class bdist_rpm(Command): # build package log.info("building RPMs") - rpm_cmd = ['rpm'] - if os.path.exists('/usr/bin/rpmbuild') or \ - os.path.exists('/bin/rpmbuild'): - rpm_cmd = ['rpmbuild'] + rpm_cmd = ['rpmbuild'] if self.source_only: # what kind of RPMs? rpm_cmd.append('-bs') -- cgit v1.2.1 From 69b935025661716ac64b3e2b049ed9b703dfe96a Mon Sep 17 00:00:00 2001 From: Paul Monson Date: Wed, 12 Jun 2019 10:16:49 -0700 Subject: bpo-37201: fix test_distutils failures for Windows ARM64 (GH-13902) --- _msvccompiler.py | 2 ++ tests/test_bdist_wininst.py | 4 ++++ util.py | 2 ++ 3 files changed, 8 insertions(+) diff --git a/_msvccompiler.py b/_msvccompiler.py index c7ac3f04..6e14f330 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -93,6 +93,7 @@ PLAT_SPEC_TO_RUNTIME = { 'x86' : 'x86', 'x86_amd64' : 'x64', 'x86_arm' : 'arm', + 'x86_arm64' : 'arm64' } def _find_vcvarsall(plat_spec): @@ -190,6 +191,7 @@ PLAT_TO_VCVARS = { 'win32' : 'x86', 'win-amd64' : 'x86_amd64', 'win-arm32' : 'x86_arm', + 'win-arm64' : 'x86_arm64' } # A set containing the DLLs that are guaranteed to be available for diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index 4c19bbab..163f1cc9 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -1,10 +1,14 @@ """Tests for distutils.command.bdist_wininst.""" +import sys +import platform import unittest from test.support import run_unittest from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support +@unittest.skipIf(sys.platform == 'win32' and platform.machine() == 'ARM64', + 'bdist_wininst is not supported in this install') @unittest.skipIf(getattr(bdist_wininst, '_unsupported', False), 'bdist_wininst is not supported in this install') class BuildWinInstTestCase(support.TempdirManager, diff --git a/util.py b/util.py index 50550e18..17a94bc4 100644 --- a/util.py +++ b/util.py @@ -40,6 +40,8 @@ def get_host_platform(): return 'win-amd64' if '(arm)' in sys.version.lower(): return 'win-arm32' + if '(arm64)' in sys.version.lower(): + return 'win-arm64' return sys.platform # Set for cross builds explicitly -- cgit v1.2.1 From eb834d03cc5a681a43ad8b76d0761e718199e5bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 1 Jul 2019 14:12:40 +0200 Subject: bpo-10945: Drop support for bdist_wininst on non-Windows systems (GH-14506) bdist_wininst depends on MBCS codec, unavailable on non-Windows, and bdist_wininst have not worked since at least Python 3.2, possibly never on Python 3. Here we document that bdist_wininst is only supported on Windows, and we mark it unsupported otherwise to skip tests. Distributors of Python 3 can now safely drop the bdist_wininst .exe files without the need to skip bdist_wininst related tests. --- command/bdist_wininst.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 3a616883..acaa184b 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -55,6 +55,9 @@ class bdist_wininst(Command): boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', 'skip-build'] + # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows + _unsupported = (sys.platform != "win32") + def initialize_options(self): self.bdist_dir = None self.plat_name = None -- cgit v1.2.1 From 6b9a31e6602723d98fce4c37f7151fdbab17367b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 3 Jul 2019 11:12:27 +0200 Subject: bpo-37421: Fix test_distutils.test_build_ext() (GH-14564) test_distutils.test_build_ext() is now able to remove the temporary directory on Windows: don't import the newly built C extension ("xx") in the current process, but test it in a separated process. --- tests/support.py | 5 +++-- tests/test_build_ext.py | 51 +++++++++++++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/tests/support.py b/tests/support.py index 7385c6bb..04130985 100644 --- a/tests/support.py +++ b/tests/support.py @@ -6,6 +6,7 @@ import tempfile import unittest import sysconfig from copy import deepcopy +import test.support from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL @@ -64,8 +65,8 @@ class TempdirManager(object): os.chdir(self.old_cwd) super().tearDown() while self.tempdirs: - d = self.tempdirs.pop() - shutil.rmtree(d, os.name in ('nt', 'cygwin')) + tmpdir = self.tempdirs.pop() + test.support.rmtree(tmpdir) def mkdtemp(self): """Create a temporary directory that will be cleaned up. diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 88847f9e..52d36b24 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -15,6 +15,7 @@ from distutils.errors import ( import unittest from test import support +from test.support.script_helper import assert_python_ok # http://bugs.python.org/issue4373 # Don't load the xx module more than once. @@ -26,11 +27,8 @@ class BuildExtTestCase(TempdirManager, unittest.TestCase): def setUp(self): # Create a simple test environment - # Note that we're making changes to sys.path super(BuildExtTestCase, self).setUp() self.tmp_dir = self.mkdtemp() - self.sys_path = sys.path, sys.path[:] - sys.path.append(self.tmp_dir) import site self.old_user_base = site.USER_BASE site.USER_BASE = self.mkdtemp() @@ -40,15 +38,11 @@ class BuildExtTestCase(TempdirManager, # bpo-30132: On Windows, a .pdb file may be created in the current # working directory. Create a temporary working directory to cleanup # everything at the end of the test. - self.temp_cwd = support.temp_cwd() - self.temp_cwd.__enter__() - self.addCleanup(self.temp_cwd.__exit__, None, None, None) + change_cwd = support.change_cwd(self.tmp_dir) + change_cwd.__enter__() + self.addCleanup(change_cwd.__exit__, None, None, None) def tearDown(self): - # Get everything back to normal - support.unload('xx') - sys.path = self.sys_path[0] - sys.path[:] = self.sys_path[1] import site site.USER_BASE = self.old_user_base from distutils.command import build_ext @@ -88,19 +82,34 @@ class BuildExtTestCase(TempdirManager, else: ALREADY_TESTED = type(self).__name__ - import xx + code = textwrap.dedent(f""" + tmp_dir = {self.tmp_dir!r} - for attr in ('error', 'foo', 'new', 'roj'): - self.assertTrue(hasattr(xx, attr)) + import sys + import unittest + from test import support - self.assertEqual(xx.foo(2, 5), 7) - self.assertEqual(xx.foo(13,15), 28) - self.assertEqual(xx.new().demo(), None) - if support.HAVE_DOCSTRINGS: - doc = 'This is a template module just for instruction.' - self.assertEqual(xx.__doc__, doc) - self.assertIsInstance(xx.Null(), xx.Null) - self.assertIsInstance(xx.Str(), xx.Str) + sys.path.insert(0, tmp_dir) + import xx + + class Tests(unittest.TestCase): + def test_xx(self): + for attr in ('error', 'foo', 'new', 'roj'): + self.assertTrue(hasattr(xx, attr)) + + self.assertEqual(xx.foo(2, 5), 7) + self.assertEqual(xx.foo(13,15), 28) + self.assertEqual(xx.new().demo(), None) + if support.HAVE_DOCSTRINGS: + doc = 'This is a template module just for instruction.' + self.assertEqual(xx.__doc__, doc) + self.assertIsInstance(xx.Null(), xx.Null) + self.assertIsInstance(xx.Str(), xx.Str) + + + unittest.main() + """) + assert_python_ok('-c', code) def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) -- cgit v1.2.1 From 850d4faed59006e8dbfcee2bbec13d1985d6538a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Jul 2019 10:44:12 +0200 Subject: bpo-37481: Deprecate distutils bdist_wininst command (GH-14553) The distutils bdist_wininst command is now deprecated, use bdist_wheel (wheel packages) instead. --- command/bdist_wininst.py | 10 +++++++++- tests/test_bdist_wininst.py | 5 +++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index acaa184b..b5ed6f04 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -3,7 +3,9 @@ Implements the Distutils 'bdist_wininst' command: create a windows installer exe-program.""" -import sys, os +import os +import sys +import warnings from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import create_tree, remove_tree @@ -58,6 +60,12 @@ class bdist_wininst(Command): # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows _unsupported = (sys.platform != "win32") + def __init__(self, *args, **kw): + super().__init__(*args, **kw) + warnings.warn("bdist_wininst command is deprecated since Python 3.8, " + "use bdist_wheel (wheel packages) instead", + DeprecationWarning, 2) + def initialize_options(self): self.bdist_dir = None self.plat_name = None diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index 163f1cc9..5c3d025d 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -2,7 +2,7 @@ import sys import platform import unittest -from test.support import run_unittest +from test.support import run_unittest, check_warnings from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support @@ -21,7 +21,8 @@ class BuildWinInstTestCase(support.TempdirManager, # this test makes sure it works now for every platform # let's create a command pkg_pth, dist = self.create_dist() - cmd = bdist_wininst(dist) + with check_warnings(("", DeprecationWarning)): + cmd = bdist_wininst(dist) cmd.ensure_finalized() # let's run the code that finds the right wininst*.exe file -- cgit v1.2.1 From 8f1f09b7f93f2fa86bafcd6502ec51d4680b4499 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 22 Jul 2019 17:52:55 -0400 Subject: Run docs build as part of CI --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index ffcad998..074e86e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,8 @@ jobs: - <<: *latest_py3 env: LANG=C - python: 3.8-dev + - <<: *latest_py3 + env: TOXENV=docs DISABLE_COVERAGE=1 - <<: *default_py stage: deploy (to PyPI for tagged commits) if: tag IS present -- cgit v1.2.1 From 3665ddcf056ef72aa6e8e92aee81684d146d5dde Mon Sep 17 00:00:00 2001 From: "Bernhard M. Wiedemann" Date: Thu, 1 Aug 2019 15:18:03 +0200 Subject: bpo-36302: Sort list of sources (GH-12341) When building packages (e.g. for openSUSE Linux) (random) filesystem order of input files influences ordering of functions in the output .so files. Thus without the patch, builds (in disposable VMs) would usually differ. Without this patch, all callers have to be patched individually https://github.com/dugsong/libdnet/pull/42 https://github.com/sass/libsass-python/pull/212 https://github.com/tahoe-lafs/pycryptopp/pull/41 https://github.com/yt-project/yt/pull/2206 https://github.com/pyproj4/pyproj/pull/142 https://github.com/pytries/datrie/pull/49 https://github.com/Roche/pyreadstat/pull/37 but that is an infinite effort. See https://reproducible-builds.org/ for why this matters. --- command/build_ext.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 2d7cdf06..38bb8fd9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -490,7 +490,8 @@ class build_ext(Command): "in 'ext_modules' option (extension '%s'), " "'sources' must be present and must be " "a list of source filenames" % ext.name) - sources = list(sources) + # sort to make the resulting .so file build reproducible + sources = sorted(sources) ext_path = self.get_ext_fullpath(ext.name) depends = sources + ext.depends -- cgit v1.2.1 From cab8dd1a30b14542fcfe7ab63f8cd8b5358222da Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 8 Aug 2019 08:42:54 +0300 Subject: bpo-37685: Fixed __eq__, __lt__ etc implementations in some classes. (GH-14952) They now return NotImplemented for unsupported type of the other operand. --- tests/test_version.py | 16 ++++++++++++++++ version.py | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/tests/test_version.py b/tests/test_version.py index 15f14c7d..8671cd2f 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -45,6 +45,14 @@ class VersionTestCase(unittest.TestCase): self.assertEqual(res, wanted, 'cmp(%s, %s) should be %s, got %s' % (v1, v2, wanted, res)) + res = StrictVersion(v1)._cmp(v2) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + res = StrictVersion(v1)._cmp(object()) + self.assertIs(res, NotImplemented, + 'cmp(%s, %s) should be NotImplemented, got %s' % + (v1, v2, res)) def test_cmp(self): @@ -63,6 +71,14 @@ class VersionTestCase(unittest.TestCase): self.assertEqual(res, wanted, 'cmp(%s, %s) should be %s, got %s' % (v1, v2, wanted, res)) + res = LooseVersion(v1)._cmp(v2) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + res = LooseVersion(v1)._cmp(object()) + self.assertIs(res, NotImplemented, + 'cmp(%s, %s) should be NotImplemented, got %s' % + (v1, v2, res)) def test_suite(): return unittest.makeSuite(VersionTestCase) diff --git a/version.py b/version.py index af14cc13..c33bebae 100644 --- a/version.py +++ b/version.py @@ -166,6 +166,8 @@ class StrictVersion (Version): def _cmp (self, other): if isinstance(other, str): other = StrictVersion(other) + elif not isinstance(other, StrictVersion): + return NotImplemented if self.version != other.version: # numeric versions don't match @@ -331,6 +333,8 @@ class LooseVersion (Version): def _cmp (self, other): if isinstance(other, str): other = LooseVersion(other) + elif not isinstance(other, LooseVersion): + return NotImplemented if self.version == other.version: return 0 -- cgit v1.2.1 From a85a63840b076c7a9d165db731ca6d1be28287e8 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 8 Aug 2019 23:25:46 +0100 Subject: bpo-37795: Capture DeprecationWarnings in the test suite (GH-15184) --- tests/test_bdist.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_bdist.py b/tests/test_bdist.py index c80b3edc..130d8bf1 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -2,6 +2,7 @@ import os import unittest from test.support import run_unittest +import warnings from distutils.command.bdist import bdist from distutils.tests import support @@ -38,7 +39,10 @@ class BuildTestCase(support.TempdirManager, names.append('bdist_msi') for name in names: - subcmd = cmd.get_finalized_command(name) + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', 'bdist_wininst command is deprecated', + DeprecationWarning) + subcmd = cmd.get_finalized_command(name) if getattr(subcmd, '_unsupported', False): # command is not supported on this build continue -- cgit v1.2.1 From cd848cbc425ba0735e0987040f201de0b13f0486 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Wed, 28 Aug 2019 19:11:03 +0200 Subject: closes bpo-37965: Fix compiler warning of distutils CCompiler.test_function. (GH-15560) https://bugs.python.org/issue37965 https://bugs.python.org/issue37965 Automerge-Triggered-By: @benjaminp --- ccompiler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index 1a411ed1..4cfc6c70 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -781,8 +781,9 @@ class CCompiler: for incl in includes: f.write("""#include "%s"\n""" % incl) f.write("""\ -main (int argc, char **argv) { +int main (int argc, char **argv) { %s(); + return 0; } """ % funcname) finally: -- cgit v1.2.1 From 5ffd6d26a7806d8b8cc0aad93e50cc63bae294a1 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 10 Sep 2019 14:52:23 +0100 Subject: bpo-38088: Fixes distutils not finding vcruntime140.dll with only v142 toolset installed (GH-15849) --- _msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index 6e14f330..e8e4b717 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -107,7 +107,7 @@ def _find_vcvarsall(plat_spec): if best_dir: vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**", - vcruntime_plat, "Microsoft.VC141.CRT", "vcruntime140.dll") + vcruntime_plat, "Microsoft.VC14*.CRT", "vcruntime140.dll") try: import glob vcruntime = glob.glob(vcredist, recursive=True)[-1] -- cgit v1.2.1 From 2d25ca89318922e63b74c37e36d099173cf0da5a Mon Sep 17 00:00:00 2001 From: Dan Rose Date: Sun, 27 Oct 2019 02:31:59 -0500 Subject: Remove sys.modules hack Fix #1888 (metadata accidentally not picklable), and removes a case where reimporting a vendored module results in a second copy of the same module. --- changelog.d/1890.change.rst | 1 + setuptools/extern/__init__.py | 7 ------- setuptools/tests/test_extern.py | 22 ++++++++++++++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 changelog.d/1890.change.rst create mode 100644 setuptools/tests/test_extern.py diff --git a/changelog.d/1890.change.rst b/changelog.d/1890.change.rst new file mode 100644 index 00000000..458d8117 --- /dev/null +++ b/changelog.d/1890.change.rst @@ -0,0 +1 @@ +Fix vendored dependencies so importing ``setuptools.extern.some_module`` gives the same object as ``setuptools._vendor.some_module``. This makes Metadata picklable again. \ No newline at end of file diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py index e8c616f9..4e79aa17 100644 --- a/setuptools/extern/__init__.py +++ b/setuptools/extern/__init__.py @@ -43,13 +43,6 @@ class VendorImporter: __import__(extant) mod = sys.modules[extant] sys.modules[fullname] = mod - # mysterious hack: - # Remove the reference to the extant package/module - # on later Python versions to cause relative imports - # in the vendor package to resolve the same modules - # as those going through this importer. - if sys.version_info >= (3, ): - del sys.modules[extant] return mod except ImportError: pass diff --git a/setuptools/tests/test_extern.py b/setuptools/tests/test_extern.py new file mode 100644 index 00000000..3519a680 --- /dev/null +++ b/setuptools/tests/test_extern.py @@ -0,0 +1,22 @@ +import importlib +import pickle + +from setuptools import Distribution +from setuptools.extern import ordered_set +from setuptools.tests import py3_only + + +def test_reimport_extern(): + ordered_set2 = importlib.import_module(ordered_set.__name__) + assert ordered_set is ordered_set2 + + +def test_orderedset_pickle_roundtrip(): + o1 = ordered_set.OrderedSet([1, 2, 5]) + o2 = pickle.loads(pickle.dumps(o1)) + assert o1 == o2 + + +@py3_only +def test_distribution_picklable(): + pickle.loads(pickle.dumps(Distribution())) -- cgit v1.2.1 From b2ab6f7c90b4daf5c05f08be230f329e8cb2f8e2 Mon Sep 17 00:00:00 2001 From: Andrew Taylor Date: Tue, 29 Oct 2019 17:03:11 -0600 Subject: Add info message when authentication error encountered processing package index. --- setuptools/package_index.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index f419d471..d9668e4e 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -348,6 +348,8 @@ class PackageIndex(Environment): f = self.open_url(url, tmpl % url) if f is None: return + if isinstance(f, urllib.error.HTTPError) and f.code == 401: + self.info("Authentication error: %s" % f.msg) self.fetched_urls[f.url] = True if 'html' not in f.headers.get('content-type', '').lower(): f.close() # not html, we can't process it -- cgit v1.2.1 From 2fffeb6513a50af996ee07dfc4a62fb0bf5dce7f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 31 Oct 2019 15:33:12 -0400 Subject: Error on warnings. Fixes #1823. --- pytest.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/pytest.ini b/pytest.ini index 0370f7f8..6786be4f 100644 --- a/pytest.ini +++ b/pytest.ini @@ -8,3 +8,4 @@ doctest_optionflags=ELLIPSIS ALLOW_UNICODE filterwarnings = # https://github.com/pypa/setuptools/issues/1823 ignore:bdist_wininst command is deprecated + error -- cgit v1.2.1 From 894aa5bede94d01639a5e6e95be02cb06164318f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 31 Oct 2019 15:36:32 -0400 Subject: Move the error directive to the top, as it's more permanent. --- pytest.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 6786be4f..3b3d4b8b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -6,6 +6,7 @@ flake8-ignore = setuptools/py*compat.py F811 doctest_optionflags=ELLIPSIS ALLOW_UNICODE filterwarnings = + # Fail on warnings + error # https://github.com/pypa/setuptools/issues/1823 ignore:bdist_wininst command is deprecated - error -- cgit v1.2.1 From 96bc30807092c6f6a014d71510a23ab2dd07f3a7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 31 Oct 2019 15:36:52 -0400 Subject: Add changelog entry --- changelog.d/1899.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1899.change.rst diff --git a/changelog.d/1899.change.rst b/changelog.d/1899.change.rst new file mode 100644 index 00000000..30768439 --- /dev/null +++ b/changelog.d/1899.change.rst @@ -0,0 +1 @@ +Test suite now fails on warnings. -- cgit v1.2.1 From f265e4089c63bfa17d8024d0d6a574c2aaaa992d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 Nov 2019 20:18:24 -0500 Subject: normalize indentation --- pytest.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pytest.ini b/pytest.ini index 3b3d4b8b..1400d122 100644 --- a/pytest.ini +++ b/pytest.ini @@ -6,7 +6,7 @@ flake8-ignore = setuptools/py*compat.py F811 doctest_optionflags=ELLIPSIS ALLOW_UNICODE filterwarnings = - # Fail on warnings - error - # https://github.com/pypa/setuptools/issues/1823 - ignore:bdist_wininst command is deprecated + # Fail on warnings + error + # https://github.com/pypa/setuptools/issues/1823 + ignore:bdist_wininst command is deprecated -- cgit v1.2.1 From b09909462acad0d0175cf2fcef76cf1e1c7adc76 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Tue, 19 Nov 2019 19:45:20 +0000 Subject: bpo-38839: Fix some unused functions in tests (GH-17189) --- tests/support.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/support.py b/tests/support.py index 04130985..259af882 100644 --- a/tests/support.py +++ b/tests/support.py @@ -39,8 +39,6 @@ class LoggingSilencer(object): self.logs.append((level, msg, args)) def get_logs(self, *levels): - def _format(msg, args): - return msg % args return [msg % args for level, msg, args in self.logs if level in levels] -- cgit v1.2.1 From 2bf131d79252ce71e82c64ffbfdf1126d9602e90 Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Sun, 15 Dec 2019 15:17:53 +0100 Subject: bpo-38021: Modify AIX platform_tag so it covers PEP 425 needs (GH-17303) Provides a richer platform tag for AIX that we expect to be sufficient for PEP 425 binary distribution identification. Any backports to earlier Python versions will be handled via setuptools. Patch by Michael Felt. --- util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util.py b/util.py index 17a94bc4..4b002ece 100644 --- a/util.py +++ b/util.py @@ -79,7 +79,8 @@ def get_host_platform(): machine += ".%s" % bitness[sys.maxsize] # fall through to standard osname-release-machine representation elif osname[:3] == "aix": - return "%s-%s.%s" % (osname, version, release) + from _aix_support import aix_platform + return aix_platform() elif osname[:6] == "cygwin": osname = "cygwin" rel_re = re.compile (r'[\d.]+', re.ASCII) -- cgit v1.2.1 From c4419ed0099645ef3c27a617f93e9a6182962693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Gmach?= Date: Mon, 23 Dec 2019 15:53:18 +0100 Subject: bpo-38914 Do not require email field in setup.py. (GH-17388) When checking `setup.py` and when the `author` field was provided, but the `author_email` field was missing, erroneously a warning message was displayed that the `author_email` field is required. The specs do not require the `author_email`field: https://packaging.python.org/specifications/core-metadata/#author The same is valid for `maintainer` and `maintainer_email`. The warning message has been adjusted. modified: Doc/distutils/examples.rst modified: Lib/distutils/command/check.py https://bugs.python.org/issue38914 --- command/check.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/command/check.py b/command/check.py index 04c2f964..7ceabd3a 100644 --- a/command/check.py +++ b/command/check.py @@ -80,8 +80,11 @@ class check(Command): def check_metadata(self): """Ensures that all required elements of meta-data are supplied. - name, version, URL, (author and author_email) or - (maintainer and maintainer_email)). + Required fields: + name, version, URL + + Recommended fields: + (author and author_email) or (maintainer and maintainer_email)) Warns if any are missing. """ @@ -97,15 +100,15 @@ class check(Command): if metadata.author: if not metadata.author_email: self.warn("missing meta-data: if 'author' supplied, " + - "'author_email' must be supplied too") + "'author_email' should be supplied too") elif metadata.maintainer: if not metadata.maintainer_email: self.warn("missing meta-data: if 'maintainer' supplied, " + - "'maintainer_email' must be supplied too") + "'maintainer_email' should be supplied too") else: self.warn("missing meta-data: either (author and author_email) " + "or (maintainer and maintainer_email) " + - "must be supplied") + "should be supplied") def check_restructuredtext(self): """Checks if the long string fields are reST-compliant.""" -- cgit v1.2.1 From 5399c8597bcb86a08db4b423f914c474cd125c31 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sun, 7 Apr 2019 23:22:46 +0200 Subject: =?UTF-8?q?=F0=9F=93=9D=20Document=20setup.cfg-only=20projects?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/requirements.txt | 1 + docs/setuptools.txt | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index bc27165b..e82df49c 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,5 @@ sphinx!=1.8.0 +pygments-github-lexers==0.0.5 rst.linker>=1.9 jaraco.packaging>=6.1 diff --git a/docs/setuptools.txt b/docs/setuptools.txt index d214ca99..3a2028ce 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -2195,6 +2195,48 @@ Metadata and options are set in the config sections of the same name. * Unknown keys are ignored. +setup.cfg-only projects +======================= + +.. versionadded:: 40.9.0 + +If ``setup.py`` is missing from the project directory when a PEP 517 +build is invoked, ``setuptools`` emulates a dummy ``setup.py`` file containing +only a ``setuptools.setup()`` call. + +.. note:: + + PEP 517 doesn't support editable installs so this is currently + incompatible with ``pip install -e .``. + +This means that you can have a Python project with all build configuration +specified in ``setup.cfg``, without a ``setup.py`` file, if you **can rely +on** your project always being built by a PEP 517/518 compatible frontend. + +To use this feature: + +* Specify build requirements and PEP 517 build backend in + ``pyproject.toml``. + For example: + + .. code-block:: toml + + [build-system] + requires = [ + "setuptools >= 40.9.0", + "wheel", + ] + build-backend = "setuptools.build_meta" + +* Use a PEP 517 compatible build frontend, such as ``pip >= 19`` or ``pep517``. + + .. warning:: + + As PEP 517 is new, support is not universal, and frontends that + do support it may still have bugs. For compatibility, you may want to + put a ``setup.py`` file containing only a ``setuptools.setup()`` + invocation. + Using a ``src/`` layout ======================= -- cgit v1.2.1 From 9e4e2e0e4d9ea3a0f26cc1dd05c975f4cc660f11 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sat, 25 May 2019 12:28:35 +0200 Subject: Mark PEP mentions as :pep: SphinX refs --- docs/setuptools.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 3a2028ce..e2a7bb1c 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -2200,22 +2200,23 @@ setup.cfg-only projects .. versionadded:: 40.9.0 -If ``setup.py`` is missing from the project directory when a PEP 517 +If ``setup.py`` is missing from the project directory when a :pep:`517` build is invoked, ``setuptools`` emulates a dummy ``setup.py`` file containing only a ``setuptools.setup()`` call. .. note:: - PEP 517 doesn't support editable installs so this is currently + :pep:`517` doesn't support editable installs so this is currently incompatible with ``pip install -e .``. This means that you can have a Python project with all build configuration specified in ``setup.cfg``, without a ``setup.py`` file, if you **can rely -on** your project always being built by a PEP 517/518 compatible frontend. +on** your project always being built by a :pep:`517`/:pep:`518` compatible +frontend. To use this feature: -* Specify build requirements and PEP 517 build backend in +* Specify build requirements and :pep:`517` build backend in ``pyproject.toml``. For example: @@ -2228,11 +2229,11 @@ To use this feature: ] build-backend = "setuptools.build_meta" -* Use a PEP 517 compatible build frontend, such as ``pip >= 19`` or ``pep517``. +* Use a :pep:`517` compatible build frontend, such as ``pip >= 19`` or ``pep517``. .. warning:: - As PEP 517 is new, support is not universal, and frontends that + As :pep:`517` is new, support is not universal, and frontends that do support it may still have bugs. For compatibility, you may want to put a ``setup.py`` file containing only a ``setuptools.setup()`` invocation. -- cgit v1.2.1 From b0657c80db7891a9eca038199d5d4c2e2bafed03 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sun, 26 May 2019 19:46:36 +0200 Subject: =?UTF-8?q?=F0=9F=93=9D=20Improve=20the=20note=20about=20editable?= =?UTF-8?q?=20installs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Paul Ganssle --- docs/setuptools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setuptools.txt b/docs/setuptools.txt index e2a7bb1c..e5663fc4 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -2207,7 +2207,7 @@ only a ``setuptools.setup()`` call. .. note:: :pep:`517` doesn't support editable installs so this is currently - incompatible with ``pip install -e .``. + incompatible with ``pip install -e .``, as :pep:`517` does not support editable installs. This means that you can have a Python project with all build configuration specified in ``setup.cfg``, without a ``setup.py`` file, if you **can rely -- cgit v1.2.1 From c71969013d726c5cbd06dc81770ab064f3e783ff Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 19 Jan 2020 09:53:27 -0500 Subject: Remove the Features feature. Fixes #65. This commit reverts e4460fad043f4fa0edc7b7e1eef0b209f4588fe5. --- setup.py | 1 - setuptools/__init__.py | 7 +- setuptools/dist.py | 260 +----------------------------------- setuptools/tests/test_setuptools.py | 83 +----------- 4 files changed, 7 insertions(+), 344 deletions(-) diff --git a/setup.py b/setup.py index 277b6640..1fe18bd1 100755 --- a/setup.py +++ b/setup.py @@ -91,7 +91,6 @@ setup_params = dict( ], "setuptools.finalize_distribution_options": [ "parent_finalize = setuptools.dist:_Distribution.finalize_options", - "features = setuptools.dist:Distribution._finalize_feature_opts", "keywords = setuptools.dist:Distribution._finalize_setup_keywords", "2to3_doctests = " "setuptools.dist:Distribution._finalize_2to3_doctests", diff --git a/setuptools/__init__.py b/setuptools/__init__.py index a71b2bbd..dc0f9893 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -1,7 +1,6 @@ """Extensions to the 'distutils' for large or complex distributions""" import os -import sys import functools import distutils.core import distutils.filelist @@ -17,7 +16,7 @@ from setuptools.extern.six.moves import filter, map import setuptools.version from setuptools.extension import Extension -from setuptools.dist import Distribution, Feature +from setuptools.dist import Distribution from setuptools.depends import Require from . import monkey @@ -25,13 +24,13 @@ __metaclass__ = type __all__ = [ - 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', + 'setup', 'Distribution', 'Command', 'Extension', 'Require', 'SetuptoolsDeprecationWarning', 'find_packages' ] if PY3: - __all__.append('find_namespace_packages') + __all__.append('find_namespace_packages') __version__ = setuptools.version.__version__ diff --git a/setuptools/dist.py b/setuptools/dist.py index fe5adf46..ff5a9ff3 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -19,9 +19,7 @@ import itertools from collections import defaultdict from email import message_from_file -from distutils.errors import ( - DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, -) +from distutils.errors import DistutilsOptionError, DistutilsSetupError from distutils.util import rfc822_escape from distutils.version import StrictVersion @@ -32,7 +30,6 @@ from setuptools.extern.six.moves import map, filter, filterfalse from . import SetuptoolsDeprecationWarning -from setuptools.depends import Require from setuptools import windows_support from setuptools.monkey import get_unpatched from setuptools.config import parse_configuration @@ -338,7 +335,7 @@ _Distribution = get_unpatched(distutils.core.Distribution) class Distribution(_Distribution): - """Distribution with support for features, tests, and package data + """Distribution with support for tests and package data This is an enhanced version of 'distutils.dist.Distribution' that effectively adds the following new optional keyword arguments to 'setup()': @@ -365,21 +362,6 @@ class Distribution(_Distribution): EasyInstall and requests one of your extras, the corresponding additional requirements will be installed if needed. - 'features' **deprecated** -- a dictionary mapping option names to - 'setuptools.Feature' - objects. Features are a portion of the distribution that can be - included or excluded based on user options, inter-feature dependencies, - and availability on the current system. Excluded features are omitted - from all setup commands, including source and binary distributions, so - you can create multiple distributions from the same source tree. - Feature names should be valid Python identifiers, except that they may - contain the '-' (minus) sign. Features can be included or excluded - via the command line options '--with-X' and '--without-X', where 'X' is - the name of the feature. Whether a feature is included by default, and - whether you are allowed to control this from the command line, is - determined by the Feature object. See the 'Feature' class for more - information. - 'test_suite' -- the name of a test suite to run for the 'test' command. If the user runs 'python setup.py test', the package will be installed, and the named test suite will be run. The format is the same as @@ -401,8 +383,7 @@ class Distribution(_Distribution): for manipulating the distribution's contents. For example, the 'include()' and 'exclude()' methods can be thought of as in-place add and subtract commands that add or remove packages, modules, extensions, and so on from - the distribution. They are used by the feature subsystem to configure the - distribution for the included and excluded features. + the distribution. """ _DISTUTILS_UNSUPPORTED_METADATA = { @@ -432,10 +413,6 @@ class Distribution(_Distribution): if not have_package_data: self.package_data = {} attrs = attrs or {} - if 'features' in attrs or 'require_features' in attrs: - Feature.warn_deprecated() - self.require_features = [] - self.features = {} self.dist_files = [] # Filter-out setuptools' specific options. self.src_root = attrs.pop("src_root", None) @@ -702,17 +679,6 @@ class Distribution(_Distribution): ignore_option_errors=ignore_option_errors) self._finalize_requires() - def parse_command_line(self): - """Process features after parsing command line options""" - result = _Distribution.parse_command_line(self) - if self.features: - self._finalize_features() - return result - - def _feature_attrname(self, name): - """Convert feature name to corresponding option attribute name""" - return 'with_' + name.replace('-', '_') - def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" resolved_dists = pkg_resources.working_set.resolve( @@ -776,53 +742,6 @@ class Distribution(_Distribution): from setuptools.installer import fetch_build_egg return fetch_build_egg(self, req) - def _finalize_feature_opts(self): - """Add --with-X/--without-X options based on optional features""" - - if not self.features: - return - - go = [] - no = self.negative_opt.copy() - - for name, feature in self.features.items(): - self._set_feature(name, None) - feature.validate(self) - - if feature.optional: - descr = feature.description - incdef = ' (default)' - excdef = '' - if not feature.include_by_default(): - excdef, incdef = incdef, excdef - - new = ( - ('with-' + name, None, 'include ' + descr + incdef), - ('without-' + name, None, 'exclude ' + descr + excdef), - ) - go.extend(new) - no['without-' + name] = 'with-' + name - - self.global_options = self.feature_options = go + self.global_options - self.negative_opt = self.feature_negopt = no - - def _finalize_features(self): - """Add/remove features and resolve dependencies between them""" - - # First, flag all the enabled items (and thus their dependencies) - for name, feature in self.features.items(): - enabled = self.feature_is_included(name) - if enabled or (enabled is None and feature.include_by_default()): - feature.include_in(self) - self._set_feature(name, 1) - - # Then disable the rest, so that off-by-default features don't - # get flagged as errors when they're required by an enabled feature - for name, feature in self.features.items(): - if not self.feature_is_included(name): - feature.exclude_from(self) - self._set_feature(name, 0) - def get_command_class(self, command): """Pluggable version of get_command_class()""" if command in self.cmdclass: @@ -852,25 +771,6 @@ class Distribution(_Distribution): self.cmdclass[ep.name] = cmdclass return _Distribution.get_command_list(self) - def _set_feature(self, name, status): - """Set feature's inclusion status""" - setattr(self, self._feature_attrname(name), status) - - def feature_is_included(self, name): - """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" - return getattr(self, self._feature_attrname(name)) - - def include_feature(self, name): - """Request inclusion of feature named 'name'""" - - if self.feature_is_included(name) == 0: - descr = self.features[name].description - raise DistutilsOptionError( - descr + " is required, but was excluded or is not available" - ) - self.features[name].include_in(self) - self._set_feature(name, 1) - def include(self, **attrs): """Add items to distribution that are named in keyword arguments @@ -1115,160 +1015,6 @@ class Distribution(_Distribution): sys.stdout.detach(), encoding, errors, newline, line_buffering) -class Feature: - """ - **deprecated** -- The `Feature` facility was never completely implemented - or supported, `has reported issues - `_ and will be removed in - a future version. - - A subset of the distribution that can be excluded if unneeded/wanted - - Features are created using these keyword arguments: - - 'description' -- a short, human readable description of the feature, to - be used in error messages, and option help messages. - - 'standard' -- if true, the feature is included by default if it is - available on the current system. Otherwise, the feature is only - included if requested via a command line '--with-X' option, or if - another included feature requires it. The default setting is 'False'. - - 'available' -- if true, the feature is available for installation on the - current system. The default setting is 'True'. - - 'optional' -- if true, the feature's inclusion can be controlled from the - command line, using the '--with-X' or '--without-X' options. If - false, the feature's inclusion status is determined automatically, - based on 'availabile', 'standard', and whether any other feature - requires it. The default setting is 'True'. - - 'require_features' -- a string or sequence of strings naming features - that should also be included if this feature is included. Defaults to - empty list. May also contain 'Require' objects that should be - added/removed from the distribution. - - 'remove' -- a string or list of strings naming packages to be removed - from the distribution if this feature is *not* included. If the - feature *is* included, this argument is ignored. This argument exists - to support removing features that "crosscut" a distribution, such as - defining a 'tests' feature that removes all the 'tests' subpackages - provided by other features. The default for this argument is an empty - list. (Note: the named package(s) or modules must exist in the base - distribution when the 'setup()' function is initially called.) - - other keywords -- any other keyword arguments are saved, and passed to - the distribution's 'include()' and 'exclude()' methods when the - feature is included or excluded, respectively. So, for example, you - could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be - added or removed from the distribution as appropriate. - - A feature must include at least one 'requires', 'remove', or other - keyword argument. Otherwise, it can't affect the distribution in any way. - Note also that you can subclass 'Feature' to create your own specialized - feature types that modify the distribution in other ways when included or - excluded. See the docstrings for the various methods here for more detail. - Aside from the methods, the only feature attributes that distributions look - at are 'description' and 'optional'. - """ - - @staticmethod - def warn_deprecated(): - msg = ( - "Features are deprecated and will be removed in a future " - "version. See https://github.com/pypa/setuptools/issues/65." - ) - warnings.warn(msg, DistDeprecationWarning, stacklevel=3) - - def __init__( - self, description, standard=False, available=True, - optional=True, require_features=(), remove=(), **extras): - self.warn_deprecated() - - self.description = description - self.standard = standard - self.available = available - self.optional = optional - if isinstance(require_features, (str, Require)): - require_features = require_features, - - self.require_features = [ - r for r in require_features if isinstance(r, str) - ] - er = [r for r in require_features if not isinstance(r, str)] - if er: - extras['require_features'] = er - - if isinstance(remove, str): - remove = remove, - self.remove = remove - self.extras = extras - - if not remove and not require_features and not extras: - raise DistutilsSetupError( - "Feature %s: must define 'require_features', 'remove', or " - "at least one of 'packages', 'py_modules', etc." - ) - - def include_by_default(self): - """Should this feature be included by default?""" - return self.available and self.standard - - def include_in(self, dist): - """Ensure feature and its requirements are included in distribution - - You may override this in a subclass to perform additional operations on - the distribution. Note that this method may be called more than once - per feature, and so should be idempotent. - - """ - - if not self.available: - raise DistutilsPlatformError( - self.description + " is required, " - "but is not available on this platform" - ) - - dist.include(**self.extras) - - for f in self.require_features: - dist.include_feature(f) - - def exclude_from(self, dist): - """Ensure feature is excluded from distribution - - You may override this in a subclass to perform additional operations on - the distribution. This method will be called at most once per - feature, and only after all included features have been asked to - include themselves. - """ - - dist.exclude(**self.extras) - - if self.remove: - for item in self.remove: - dist.exclude_package(item) - - def validate(self, dist): - """Verify that feature makes sense in context of distribution - - This method is called by the distribution just before it parses its - command line. It checks to ensure that the 'remove' attribute, if any, - contains only valid package/module names that are present in the base - distribution when 'setup()' is called. You may override it in a - subclass to perform any other required validation of the feature - against a target distribution. - """ - - for item in self.remove: - if not dist.has_contents_for(item): - raise DistutilsSetupError( - "%s wants to be able to remove %s, but the distribution" - " doesn't contain any packages or modules under %s" - % (self.description, item, item) - ) - - class DistDeprecationWarning(SetuptoolsDeprecationWarning): """Class for warning about deprecations in dist in setuptools. Not ignored by default, unlike DeprecationWarning.""" diff --git a/setuptools/tests/test_setuptools.py b/setuptools/tests/test_setuptools.py index 5896a69a..26a6ab52 100644 --- a/setuptools/tests/test_setuptools.py +++ b/setuptools/tests/test_setuptools.py @@ -4,7 +4,7 @@ import sys import os import distutils.core import distutils.cmd -from distutils.errors import DistutilsOptionError, DistutilsPlatformError +from distutils.errors import DistutilsOptionError from distutils.errors import DistutilsSetupError from distutils.core import Extension from distutils.version import LooseVersion @@ -14,7 +14,6 @@ import pytest import setuptools import setuptools.dist import setuptools.depends as dep -from setuptools import Feature from setuptools.depends import Require from setuptools.extern import six @@ -211,86 +210,6 @@ class TestDistro: self.dist.exclude(package_dir=['q']) -@pytest.mark.filterwarnings('ignore:Features are deprecated') -class TestFeatures: - def setup_method(self, method): - self.req = Require('Distutils', '1.0.3', 'distutils') - self.dist = makeSetup( - features={ - 'foo': Feature( - "foo", standard=True, require_features=['baz', self.req]), - 'bar': Feature("bar", standard=True, packages=['pkg.bar'], - py_modules=['bar_et'], remove=['bar.ext'], - ), - 'baz': Feature( - "baz", optional=False, packages=['pkg.baz'], - scripts=['scripts/baz_it'], - libraries=[('libfoo', 'foo/foofoo.c')] - ), - 'dwim': Feature("DWIM", available=False, remove='bazish'), - }, - script_args=['--without-bar', 'install'], - packages=['pkg.bar', 'pkg.foo'], - py_modules=['bar_et', 'bazish'], - ext_modules=[Extension('bar.ext', ['bar.c'])] - ) - - def testDefaults(self): - assert not Feature( - "test", standard=True, remove='x', available=False - ).include_by_default() - assert Feature("test", standard=True, remove='x').include_by_default() - # Feature must have either kwargs, removes, or require_features - with pytest.raises(DistutilsSetupError): - Feature("test") - - def testAvailability(self): - with pytest.raises(DistutilsPlatformError): - self.dist.features['dwim'].include_in(self.dist) - - def testFeatureOptions(self): - dist = self.dist - assert ( - ('with-dwim', None, 'include DWIM') in dist.feature_options - ) - assert ( - ('without-dwim', None, 'exclude DWIM (default)') - in dist.feature_options - ) - assert ( - ('with-bar', None, 'include bar (default)') in dist.feature_options - ) - assert ( - ('without-bar', None, 'exclude bar') in dist.feature_options - ) - assert dist.feature_negopt['without-foo'] == 'with-foo' - assert dist.feature_negopt['without-bar'] == 'with-bar' - assert dist.feature_negopt['without-dwim'] == 'with-dwim' - assert ('without-baz' not in dist.feature_negopt) - - def testUseFeatures(self): - dist = self.dist - assert dist.with_foo == 1 - assert dist.with_bar == 0 - assert dist.with_baz == 1 - assert ('bar_et' not in dist.py_modules) - assert ('pkg.bar' not in dist.packages) - assert ('pkg.baz' in dist.packages) - assert ('scripts/baz_it' in dist.scripts) - assert (('libfoo', 'foo/foofoo.c') in dist.libraries) - assert dist.ext_modules == [] - assert dist.require_features == [self.req] - - # If we ask for bar, it should fail because we explicitly disabled - # it on the command line - with pytest.raises(DistutilsOptionError): - dist.include_feature('bar') - - def testFeatureWithInvalidRemove(self): - with pytest.raises(SystemExit): - makeSetup(features={'x': Feature('x', remove='y')}) - - class TestCommandTests: def testTestIsCommand(self): test_cmd = makeSetup().get_command_obj('test') -- cgit v1.2.1 From 6980b9c8fed113f1046099924e16287662beff5f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 19 Jan 2020 10:06:53 -0500 Subject: Update changelog. --- changelog.d/65.breaking.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/65.breaking.rst diff --git a/changelog.d/65.breaking.rst b/changelog.d/65.breaking.rst new file mode 100644 index 00000000..bde42740 --- /dev/null +++ b/changelog.d/65.breaking.rst @@ -0,0 +1 @@ +Once again as in 3.0, removed the Features feature. -- cgit v1.2.1 From daf18dd027a08dd318f348e12e75e9f4d8aaed0a Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 4 Feb 2020 16:24:30 +0100 Subject: bpo-39432: Implement PEP-489 algorithm for non-ascii "PyInit_*" symbol names in distutils (GH-18150) Make it export the correct init symbol also on Windows. https://bugs.python.org/issue39432 --- command/build_ext.py | 10 +++++++++- tests/test_build_ext.py | 13 +++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/command/build_ext.py b/command/build_ext.py index 38bb8fd9..1a9bd120 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -689,7 +689,15 @@ class build_ext(Command): provided, "PyInit_" + module_name. Only relevant on Windows, where the .pyd file (DLL) must export the module "PyInit_" function. """ - initfunc_name = "PyInit_" + ext.name.split('.')[-1] + suffix = '_' + ext.name.split('.')[-1] + try: + # Unicode module name support as defined in PEP-489 + # https://www.python.org/dev/peps/pep-0489/#export-hook-name + suffix.encode('ascii') + except UnicodeEncodeError: + suffix = 'U' + suffix.encode('punycode').replace(b'-', b'_').decode('ascii') + + initfunc_name = "PyInit" + suffix if initfunc_name not in ext.export_symbols: ext.export_symbols.append(initfunc_name) return ext.export_symbols diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 52d36b24..7e3eafa8 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -304,6 +304,19 @@ class BuildExtTestCase(TempdirManager, cmd.ensure_finalized() self.assertEqual(cmd.get_source_files(), ['xxx']) + def test_unicode_module_names(self): + modules = [ + Extension('foo', ['aaa'], optional=False), + Extension('föö', ['uuu'], optional=False), + ] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.ensure_finalized() + self.assertRegex(cmd.get_ext_filename(modules[0].name), r'foo\..*') + self.assertRegex(cmd.get_ext_filename(modules[1].name), r'föö\..*') + self.assertEqual(cmd.get_export_symbols(modules[0]), ['PyInit_foo']) + self.assertEqual(cmd.get_export_symbols(modules[1]), ['PyInitU_f_gkaa']) + def test_compiler_option(self): # cmd.compiler is an option and # should not be overridden by a compiler instance -- cgit v1.2.1 From d6bd6b1ea7efff2df20ca970de45fb0c2fe701e7 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 6 Feb 2020 15:48:10 +1100 Subject: bpo-39555: Fix distutils test to handle _d suffix on Windows debug build (GH-18357) --- tests/test_build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 7e3eafa8..5e47e077 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -312,8 +312,8 @@ class BuildExtTestCase(TempdirManager, dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = self.build_ext(dist) cmd.ensure_finalized() - self.assertRegex(cmd.get_ext_filename(modules[0].name), r'foo\..*') - self.assertRegex(cmd.get_ext_filename(modules[1].name), r'föö\..*') + self.assertRegex(cmd.get_ext_filename(modules[0].name), r'foo(_d)?\..*') + self.assertRegex(cmd.get_ext_filename(modules[1].name), r'föö(_d)?\..*') self.assertEqual(cmd.get_export_symbols(modules[0]), ['PyInit_foo']) self.assertEqual(cmd.get_export_symbols(modules[1]), ['PyInitU_f_gkaa']) -- cgit v1.2.1 From 8e5f7c22088ad8ee03096d1e591449bed2f14e44 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 10 Feb 2020 15:26:40 +0200 Subject: bpo-39586: Deprecate distutils bdist_msi command (GH-18415) --- command/bdist_msi.py | 10 +++++++++- tests/test_bdist_msi.py | 5 +++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/command/bdist_msi.py b/command/bdist_msi.py index f335a348..0863a188 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -6,7 +6,9 @@ Implements the bdist_msi command. """ -import sys, os +import os +import sys +import warnings from distutils.core import Command from distutils.dir_util import remove_tree from distutils.sysconfig import get_python_version @@ -122,6 +124,12 @@ class bdist_msi(Command): '3.5', '3.6', '3.7', '3.8', '3.9'] other_version = 'X' + def __init__(self, *args, **kw): + super().__init__(*args, **kw) + warnings.warn("bdist_msi command is deprecated since Python 3.9, " + "use bdist_wheel (wheel packages) instead", + DeprecationWarning, 2) + def initialize_options(self): self.bdist_dir = None self.plat_name = None diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index 15d8bdff..418e60ec 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -1,7 +1,7 @@ """Tests for distutils.command.bdist_msi.""" import sys import unittest -from test.support import run_unittest +from test.support import run_unittest, check_warnings from distutils.tests import support @@ -14,7 +14,8 @@ class BDistMSITestCase(support.TempdirManager, # minimal test XXX need more tests from distutils.command.bdist_msi import bdist_msi project_dir, dist = self.create_dist() - cmd = bdist_msi(dist) + with check_warnings(("", DeprecationWarning)): + cmd = bdist_msi(dist) cmd.ensure_finalized() -- cgit v1.2.1 From 0b6a065ea1415f3491d998fefee47ce800d5afe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 11 Feb 2020 16:49:23 +0100 Subject: Include pkg_resources test data in sdist This is the error otherwise: _____________ TestFindDistributions.test_standalone_egg_directory ______________ self = target_dir = local('/tmp/pytest-of-mockbuild/pytest-0/test_standalone_egg_directory0/target') def test_standalone_egg_directory(self, target_dir): (TESTS_DATA_DIR / 'my-test-package_unpacked-egg').copy(target_dir) dists = pkg_resources.find_distributions(str(target_dir)) > assert [dist.project_name for dist in dists] == ['my-test-package'] E AssertionError: assert [] == ['my-test-package'] E Right contains one more item: 'my-test-package' E Use -v to get the full diff pkg_resources/tests/test_find_distributions.py:25: AssertionError ____________________ TestFindDistributions.test_zipped_egg _____________________ self = , func = args = ('/builddir/build/BUILD/setuptools-45.2.0/pkg_resources/tests/data/my-test-package_zipped-egg',) kwargs = {}, __tracebackhide__ = False, cls = value = FileNotFoundError(2, 'No such file or directory') tb = , errno = 2 def checked_call(self, func, *args, **kwargs): """ call a function and raise an errno-exception if applicable. """ __tracebackhide__ = True try: > return func(*args, **kwargs) E FileNotFoundError: [Errno 2] No such file or directory: '/builddir/build/BUILD/setuptools-45.2.0/pkg_resources/tests/data/my-test-package_zipped-egg' /usr/lib/python3.8/site-packages/py/_error.py:66: FileNotFoundError During handling of the above exception, another exception occurred: self = target_dir = local('/tmp/pytest-of-mockbuild/pytest-0/test_zipped_egg0/target') def test_zipped_egg(self, target_dir): > (TESTS_DATA_DIR / 'my-test-package_zipped-egg').copy(target_dir) pkg_resources/tests/test_find_distributions.py:30: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /usr/lib/python3.8/site-packages/py/_path/local.py:437: in copy for x in self.visit(rec=rec): /usr/lib/python3.8/site-packages/py/_path/common.py:377: in visit for x in Visitor(fil, rec, ignore, bf, sort).gen(self): /usr/lib/python3.8/site-packages/py/_path/common.py:414: in gen entries = path.listdir() /usr/lib/python3.8/site-packages/py/_path/local.py:392: in listdir names = py.error.checked_call(os.listdir, self.strpath) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = , func = args = ('/builddir/build/BUILD/setuptools-45.2.0/pkg_resources/tests/data/my-test-package_zipped-egg',) kwargs = {}, __tracebackhide__ = False, cls = value = FileNotFoundError(2, 'No such file or directory') tb = , errno = 2 def checked_call(self, func, *args, **kwargs): """ call a function and raise an errno-exception if applicable. """ __tracebackhide__ = True try: return func(*args, **kwargs) except self.Error: raise except (OSError, EnvironmentError): cls, value, tb = sys.exc_info() if not hasattr(value, 'errno'): raise __tracebackhide__ = False errno = value.errno try: if not isinstance(value, WindowsError): raise NameError except NameError: # we are not on Windows, or we got a proper OSError cls = self._geterrnoclass(errno) else: try: cls = self._geterrnoclass(_winerrnomap[errno]) except KeyError: raise value > raise cls("%s%r" % (func.__name__, args)) E py.error.ENOENT: [No such file or directory]: listdir('/builddir/build/BUILD/setuptools-45.2.0/pkg_resources/tests/data/my-test-package_zipped-egg',) /usr/lib/python3.8/site-packages/py/_error.py:86: ENOENT --- MANIFEST.in | 1 + changelog.d/1991.misc.rst | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog.d/1991.misc.rst diff --git a/MANIFEST.in b/MANIFEST.in index 16d60e5f..128ae280 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,6 +4,7 @@ recursive-include setuptools/tests *.html recursive-include docs *.py *.txt *.conf *.css *.css_t Makefile indexsidebar.html recursive-include setuptools/_vendor *.py *.txt recursive-include pkg_resources *.py *.txt +recursive-include pkg_resources/tests/data * include *.py include *.rst include MANIFEST.in diff --git a/changelog.d/1991.misc.rst b/changelog.d/1991.misc.rst new file mode 100644 index 00000000..ac6904a2 --- /dev/null +++ b/changelog.d/1991.misc.rst @@ -0,0 +1 @@ +Include pkg_resources test data in sdist, so tests can be executed from it. -- cgit v1.2.1 From 4835f01c41f784e1bcd24752dfecb96eb44a1a16 Mon Sep 17 00:00:00 2001 From: con-f-use Date: Fri, 14 Feb 2020 11:23:43 +0100 Subject: use finalize_distribution_options entrypoint order fixes #1993 --- setuptools/dist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index fe5adf46..f6453a08 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -731,13 +731,13 @@ class Distribution(_Distribution): to influence the order of execution. Smaller numbers go first and the default is 0. """ - hook_key = 'setuptools.finalize_distribution_options' + group = 'setuptools.finalize_distribution_options' def by_order(hook): return getattr(hook, 'order', 0) - eps = pkg_resources.iter_entry_points(hook_key) + eps = map(lambda e: e.load(), pkg_resources.iter_entry_points(group)) for ep in sorted(eps, key=by_order): - ep.load()(self) + ep(self) def _finalize_setup_keywords(self): for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): -- cgit v1.2.1 From 3f8fb00e442d110129e7d77b8104e4774a11ef92 Mon Sep 17 00:00:00 2001 From: con-f-use Date: Sat, 15 Feb 2020 11:12:32 +0100 Subject: changelog for #1994 --- changelog.d/1994.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1994.change.rst diff --git a/changelog.d/1994.change.rst b/changelog.d/1994.change.rst new file mode 100644 index 00000000..4a6cc742 --- /dev/null +++ b/changelog.d/1994.change.rst @@ -0,0 +1 @@ +Fixed a bug in the "setuptools.finalize_distribution_options" hook that lead to ignoring the order attribute of entry points managed by this hook. -- cgit v1.2.1 From c1d7cc6701d33ee161d514ff61fe0e2017efee6c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 16 Feb 2020 12:30:03 -0500 Subject: Copy docs requirements to a separate file due to RTD constraints. Fixes #2001. Ref readthedocs/readthedocs.org#6662 --- .readthedocs.yml | 3 ++- docs/requirements.txt | 4 ++++ setup.cfg | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 docs/requirements.txt diff --git a/.readthedocs.yml b/.readthedocs.yml index 7b994a35..cb10a7f9 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -2,4 +2,5 @@ python: version: 3 extra_requirements: - docs - pip_install: true + pip_install: false + requirements: docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..6c35bf64 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,4 @@ +# keep these in sync with setup.cfg +sphinx +jaraco.packaging>=6.1 +rst.linker>=1.9 diff --git a/setup.cfg b/setup.cfg index 8cca6477..4ffb2f5e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -74,6 +74,7 @@ tests = pip>=19.1 # For proper file:// URLs support. docs = + # Keep these in sync with docs/requirements.txt sphinx jaraco.packaging>=6.1 rst.linker>=1.9 -- cgit v1.2.1 From b3de7989665740cd4218d7d814e719d90e75de73 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Sat, 18 Jan 2020 08:13:23 -0800 Subject: Remove pkg_resources.py31compat.makedirs() in favor of the stdlib As setuptools is now python 3.5+, this compatibility shim is no longer necessary. --- changelog.d/1973.breaking.rst | 1 + pkg_resources/__init__.py | 3 +-- pkg_resources/py31compat.py | 23 ----------------------- setuptools/build_meta.py | 3 +-- setuptools/command/easy_install.py | 4 ++-- setuptools/sandbox.py | 4 ++-- setuptools/tests/files.py | 5 +---- setuptools/tests/test_manifest.py | 3 +-- 8 files changed, 9 insertions(+), 37 deletions(-) create mode 100644 changelog.d/1973.breaking.rst delete mode 100644 pkg_resources/py31compat.py diff --git a/changelog.d/1973.breaking.rst b/changelog.d/1973.breaking.rst new file mode 100644 index 00000000..398fdd71 --- /dev/null +++ b/changelog.d/1973.breaking.rst @@ -0,0 +1 @@ +Removed ``pkg_resources.py31compat.makedirs`` in favor of the stdlib. Use ``os.makedirs()`` instead. diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 75563f95..149ab6d2 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -76,7 +76,6 @@ try: except ImportError: importlib_machinery = None -from . import py31compat from pkg_resources.extern import appdirs from pkg_resources.extern import packaging __import__('pkg_resources.extern.packaging.version') @@ -3172,7 +3171,7 @@ def _find_adapter(registry, ob): def ensure_directory(path): """Ensure that the parent directory of `path` exists""" dirname = os.path.dirname(path) - py31compat.makedirs(dirname, exist_ok=True) + os.makedirs(dirname, exist_ok=True) def _bypass_ensure_directory(path): diff --git a/pkg_resources/py31compat.py b/pkg_resources/py31compat.py deleted file mode 100644 index a381c424..00000000 --- a/pkg_resources/py31compat.py +++ /dev/null @@ -1,23 +0,0 @@ -import os -import errno -import sys - -from .extern import six - - -def _makedirs_31(path, exist_ok=False): - try: - os.makedirs(path) - except OSError as exc: - if not exist_ok or exc.errno != errno.EEXIST: - raise - - -# rely on compatibility behavior until mode considerations -# and exists_ok considerations are disentangled. -# See https://github.com/pypa/setuptools/pull/1083#issuecomment-315168663 -needs_makedirs = ( - six.PY2 or - (3, 4) <= sys.version_info < (3, 4, 1) -) -makedirs = _makedirs_31 if needs_makedirs else os.makedirs diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py index a1c951cf..46266814 100644 --- a/setuptools/build_meta.py +++ b/setuptools/build_meta.py @@ -38,7 +38,6 @@ import distutils from setuptools.py31compat import TemporaryDirectory from pkg_resources import parse_requirements -from pkg_resources.py31compat import makedirs __all__ = ['get_requires_for_build_sdist', 'get_requires_for_build_wheel', @@ -190,7 +189,7 @@ class _BuildMetaBackend(object): result_directory = os.path.abspath(result_directory) # Build in a temporary directory, then copy to the target. - makedirs(result_directory, exist_ok=True) + os.makedirs(result_directory, exist_ok=True) with TemporaryDirectory(dir=result_directory) as tmp_dist_dir: sys.argv = (sys.argv[:1] + setup_command + ['--dist-dir', tmp_dist_dir] + diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index abca1ae1..d224ea05 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -64,7 +64,7 @@ from pkg_resources import ( Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound, VersionConflict, DEVELOP_DIST, ) -import pkg_resources.py31compat +import pkg_resources __metaclass__ = type @@ -559,7 +559,7 @@ class easy_install(Command): if ok_exists: os.unlink(ok_file) dirname = os.path.dirname(ok_file) - pkg_resources.py31compat.makedirs(dirname, exist_ok=True) + os.makedirs(dirname, exist_ok=True) f = open(pth_file, 'w') except (OSError, IOError): self.cant_write_to_target() diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index e46dfc8d..93ae8eb4 100644 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -12,7 +12,7 @@ import textwrap from setuptools.extern import six from setuptools.extern.six.moves import builtins, map -import pkg_resources.py31compat +import pkg_resources from distutils.errors import DistutilsError from pkg_resources import working_set @@ -70,7 +70,7 @@ def override_temp(replacement): """ Monkey-patch tempfile.tempdir with replacement, ensuring it exists """ - pkg_resources.py31compat.makedirs(replacement, exist_ok=True) + os.makedirs(replacement, exist_ok=True) saved = tempfile.tempdir diff --git a/setuptools/tests/files.py b/setuptools/tests/files.py index bad2189d..71194b9d 100644 --- a/setuptools/tests/files.py +++ b/setuptools/tests/files.py @@ -1,9 +1,6 @@ import os -import pkg_resources.py31compat - - def build_files(file_defs, prefix=""): """ Build a set of files/directories, as described by the @@ -30,7 +27,7 @@ def build_files(file_defs, prefix=""): for name, contents in file_defs.items(): full_name = os.path.join(prefix, name) if isinstance(contents, dict): - pkg_resources.py31compat.makedirs(full_name, exist_ok=True) + os.makedirs(full_name, exist_ok=True) build_files(contents, prefix=full_name) else: if isinstance(contents, bytes): diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index 2a0e9c86..042a8b17 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -10,7 +10,6 @@ import itertools from distutils import log from distutils.errors import DistutilsTemplateError -import pkg_resources.py31compat from setuptools.command.egg_info import FileList, egg_info, translate_pattern from setuptools.dist import Distribution from setuptools.extern import six @@ -364,7 +363,7 @@ class TestFileListTest(TempDirTestCase): for file in files: file = os.path.join(self.temp_dir, file) dirname, basename = os.path.split(file) - pkg_resources.py31compat.makedirs(dirname, exist_ok=True) + os.makedirs(dirname, exist_ok=True) open(file, 'w').close() def test_process_template_line(self): -- cgit v1.2.1 From abaa683c2140204b40051c80926c334ce4bba41c Mon Sep 17 00:00:00 2001 From: Ye-hyoung Kang Date: Mon, 2 Mar 2020 23:54:57 +0900 Subject: Fix broken link to Python docs The broken link used to point to Python 2 docs (which has been moved). Since Python 2 has reached its end-of-life, link to Python 3 docs instead. --- docs/setuptools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setuptools.txt b/docs/setuptools.txt index efcd0a86..22e3c872 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -941,7 +941,7 @@ python.org website. If using the setuptools-specific ``include_package_data`` argument, files specified by ``package_data`` will *not* be automatically added to the manifest unless they are listed in the MANIFEST.in file.) -__ http://docs.python.org/dist/node11.html +__ https://docs.python.org/3/distutils/setupscript.html#installing-package-data Sometimes, the ``include_package_data`` or ``package_data`` options alone aren't sufficient to precisely define what files you want included. For -- cgit v1.2.1 From 6a3729676d6aef85396d6fcf593a140e56ce32c7 Mon Sep 17 00:00:00 2001 From: Ye-hyoung Kang Date: Tue, 3 Mar 2020 00:05:19 +0900 Subject: Create 2011.doc.rst Add news fragment --- changelog.d/2011.doc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2011.doc.rst diff --git a/changelog.d/2011.doc.rst b/changelog.d/2011.doc.rst new file mode 100644 index 00000000..e36fb6a7 --- /dev/null +++ b/changelog.d/2011.doc.rst @@ -0,0 +1 @@ +Fix broken link to distutils docs on package_data -- cgit v1.2.1 From d8d513d728552adee0677d5814eecc06d7775749 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 3 Mar 2020 00:04:11 +0000 Subject: bpo-38597: Never statically link extension initialization code on Windows (GH-18724) --- _msvccompiler.py | 60 ++++++---------------------------------------- tests/test_msvccompiler.py | 51 --------------------------------------- 2 files changed, 7 insertions(+), 104 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index e8e4b717..03a5986d 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -97,28 +97,11 @@ PLAT_SPEC_TO_RUNTIME = { } def _find_vcvarsall(plat_spec): + # bpo-38597: Removed vcruntime return value _, best_dir = _find_vc2017() - vcruntime = None - - if plat_spec in PLAT_SPEC_TO_RUNTIME: - vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec] - else: - vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86' - - if best_dir: - vcredist = os.path.join(best_dir, "..", "..", "redist", "MSVC", "**", - vcruntime_plat, "Microsoft.VC14*.CRT", "vcruntime140.dll") - try: - import glob - vcruntime = glob.glob(vcredist, recursive=True)[-1] - except (ImportError, OSError, LookupError): - vcruntime = None if not best_dir: best_version, best_dir = _find_vc2015() - if best_version: - vcruntime = os.path.join(best_dir, 'redist', vcruntime_plat, - "Microsoft.VC140.CRT", "vcruntime140.dll") if not best_dir: log.debug("No suitable Visual C++ version found") @@ -129,11 +112,7 @@ def _find_vcvarsall(plat_spec): log.debug("%s cannot be found", vcvarsall) return None, None - if not vcruntime or not os.path.isfile(vcruntime): - log.debug("%s cannot be found", vcruntime) - vcruntime = None - - return vcvarsall, vcruntime + return vcvarsall, None def _get_vc_env(plat_spec): if os.getenv("DISTUTILS_USE_SDK"): @@ -142,7 +121,7 @@ def _get_vc_env(plat_spec): for key, value in os.environ.items() } - vcvarsall, vcruntime = _find_vcvarsall(plat_spec) + vcvarsall, _ = _find_vcvarsall(plat_spec) if not vcvarsall: raise DistutilsPlatformError("Unable to find vcvarsall.bat") @@ -163,8 +142,6 @@ def _get_vc_env(plat_spec): if key and value } - if vcruntime: - env['py_vcruntime_redist'] = vcruntime return env def _find_exe(exe, paths=None): @@ -194,12 +171,6 @@ PLAT_TO_VCVARS = { 'win-arm64' : 'x86_arm64' } -# A set containing the DLLs that are guaranteed to be available for -# all micro versions of this Python version. Known extension -# dependencies that are not in this set will be copied to the output -# path. -_BUNDLED_DLLS = frozenset(['vcruntime140.dll']) - class MSVCCompiler(CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, as defined by the CCompiler abstract class.""" @@ -263,7 +234,6 @@ class MSVCCompiler(CCompiler) : self.rc = _find_exe("rc.exe", paths) # resource compiler self.mc = _find_exe("mc.exe", paths) # message compiler self.mt = _find_exe("mt.exe", paths) # message compiler - self._vcruntime_redist = vc_env.get('py_vcruntime_redist', '') for dir in vc_env.get('include', '').split(os.pathsep): if dir: @@ -274,13 +244,12 @@ class MSVCCompiler(CCompiler) : self.add_library_dir(dir.rstrip(os.sep)) self.preprocess_options = None - # If vcruntime_redist is available, link against it dynamically. Otherwise, - # use /MT[d] to build statically, then switch from libucrt[d].lib to ucrt[d].lib - # later to dynamically link to ucrtbase but not vcruntime. + # bpo-38597: Always compile with dynamic linking + # Future releases of Python 3.x will include all past + # versions of vcruntime*.dll for compatibility. self.compile_options = [ - '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG' + '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD' ] - self.compile_options.append('/MD' if self._vcruntime_redist else '/MT') self.compile_options_debug = [ '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' @@ -289,8 +258,6 @@ class MSVCCompiler(CCompiler) : ldflags = [ '/nologo', '/INCREMENTAL:NO', '/LTCG' ] - if not self._vcruntime_redist: - ldflags.extend(('/nodefaultlib:libucrt.lib', 'ucrt.lib')) ldflags_debug = [ '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL' @@ -532,24 +499,11 @@ class MSVCCompiler(CCompiler) : try: log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args)) self.spawn([self.linker] + ld_args) - self._copy_vcruntime(output_dir) except DistutilsExecError as msg: raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) - def _copy_vcruntime(self, output_dir): - vcruntime = self._vcruntime_redist - if not vcruntime or not os.path.isfile(vcruntime): - return - - if os.path.basename(vcruntime).lower() in _BUNDLED_DLLS: - return - - log.debug('Copying "%s"', vcruntime) - vcruntime = shutil.copy(vcruntime, output_dir) - os.chmod(vcruntime, stat.S_IWRITE) - def spawn(self, cmd): old_path = os.getenv('path') try: diff --git a/tests/test_msvccompiler.py b/tests/test_msvccompiler.py index 70a9c93a..b518d6a7 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -32,57 +32,6 @@ class msvccompilerTestCase(support.TempdirManager, finally: _msvccompiler._find_vcvarsall = old_find_vcvarsall - def test_compiler_options(self): - import distutils._msvccompiler as _msvccompiler - # suppress path to vcruntime from _find_vcvarsall to - # check that /MT is added to compile options - old_find_vcvarsall = _msvccompiler._find_vcvarsall - def _find_vcvarsall(plat_spec): - return old_find_vcvarsall(plat_spec)[0], None - _msvccompiler._find_vcvarsall = _find_vcvarsall - try: - compiler = _msvccompiler.MSVCCompiler() - compiler.initialize() - - self.assertIn('/MT', compiler.compile_options) - self.assertNotIn('/MD', compiler.compile_options) - finally: - _msvccompiler._find_vcvarsall = old_find_vcvarsall - - def test_vcruntime_copy(self): - import distutils._msvccompiler as _msvccompiler - # force path to a known file - it doesn't matter - # what we copy as long as its name is not in - # _msvccompiler._BUNDLED_DLLS - old_find_vcvarsall = _msvccompiler._find_vcvarsall - def _find_vcvarsall(plat_spec): - return old_find_vcvarsall(plat_spec)[0], __file__ - _msvccompiler._find_vcvarsall = _find_vcvarsall - try: - tempdir = self.mkdtemp() - compiler = _msvccompiler.MSVCCompiler() - compiler.initialize() - compiler._copy_vcruntime(tempdir) - - self.assertTrue(os.path.isfile(os.path.join( - tempdir, os.path.basename(__file__)))) - finally: - _msvccompiler._find_vcvarsall = old_find_vcvarsall - - def test_vcruntime_skip_copy(self): - import distutils._msvccompiler as _msvccompiler - - tempdir = self.mkdtemp() - compiler = _msvccompiler.MSVCCompiler() - compiler.initialize() - dll = compiler._vcruntime_redist - self.assertTrue(os.path.isfile(dll), dll or "") - - compiler._copy_vcruntime(tempdir) - - self.assertFalse(os.path.isfile(os.path.join( - tempdir, os.path.basename(dll))), dll or "") - def test_get_vc_env_unicode(self): import distutils._msvccompiler as _msvccompiler -- cgit v1.2.1 From c6f749b2b761da2b88cac62d8c6aa29f7276e3a7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 4 Mar 2020 14:50:19 +0100 Subject: bpo-39763: distutils.spawn now uses subprocess (GH-18743) Reimplement distutils.spawn.spawn() function with the subprocess module. setup.py now uses a basic implementation of the subprocess module if the subprocess module is not available: before required C extension modules are built. --- spawn.py | 128 +++++++++------------------------------------------- tests/test_spawn.py | 11 ----- 2 files changed, 22 insertions(+), 117 deletions(-) diff --git a/spawn.py b/spawn.py index ceb94945..aad277b0 100644 --- a/spawn.py +++ b/spawn.py @@ -8,11 +8,18 @@ executable name. import sys import os +import subprocess from distutils.errors import DistutilsPlatformError, DistutilsExecError from distutils.debug import DEBUG from distutils import log + +if sys.platform == 'darwin': + _cfg_target = None + _cfg_target_split = None + + def spawn(cmd, search_path=1, verbose=0, dry_run=0): """Run another program, specified as a command list 'cmd', in a new process. @@ -32,64 +39,16 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0): # cmd is documented as a list, but just in case some code passes a tuple # in, protect our %-formatting code against horrible death cmd = list(cmd) - if os.name == 'posix': - _spawn_posix(cmd, search_path, dry_run=dry_run) - elif os.name == 'nt': - _spawn_nt(cmd, search_path, dry_run=dry_run) - else: - raise DistutilsPlatformError( - "don't know how to spawn programs on platform '%s'" % os.name) - -def _nt_quote_args(args): - """Quote command-line arguments for DOS/Windows conventions. - - Just wraps every argument which contains blanks in double quotes, and - returns a new argument list. - """ - # XXX this doesn't seem very robust to me -- but if the Windows guys - # say it'll work, I guess I'll have to accept it. (What if an arg - # contains quotes? What other magic characters, other than spaces, - # have to be escaped? Is there an escaping mechanism other than - # quoting?) - for i, arg in enumerate(args): - if ' ' in arg: - args[i] = '"%s"' % arg - return args - -def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): - executable = cmd[0] - cmd = _nt_quote_args(cmd) - if search_path: - # either we find one or it stays the same - executable = find_executable(executable) or executable - log.info(' '.join([executable] + cmd[1:])) - if not dry_run: - # spawn for NT requires a full path to the .exe - try: - rc = os.spawnv(os.P_WAIT, executable, cmd) - except OSError as exc: - # this seems to happen when the command isn't found - if not DEBUG: - cmd = executable - raise DistutilsExecError( - "command %r failed: %s" % (cmd, exc.args[-1])) - if rc != 0: - # and this reflects the command running but failing - if not DEBUG: - cmd = executable - raise DistutilsExecError( - "command %r failed with exit status %d" % (cmd, rc)) - -if sys.platform == 'darwin': - _cfg_target = None - _cfg_target_split = None -def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): log.info(' '.join(cmd)) if dry_run: return - executable = cmd[0] - exec_fn = search_path and os.execvp or os.execv + + if search_path: + executable = find_executable(cmd[0]) + if executable is not None: + cmd[0] = executable + env = None if sys.platform == 'darwin': global _cfg_target, _cfg_target_split @@ -111,60 +70,17 @@ def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): raise DistutilsPlatformError(my_msg) env = dict(os.environ, MACOSX_DEPLOYMENT_TARGET=cur_target) - exec_fn = search_path and os.execvpe or os.execve - pid = os.fork() - if pid == 0: # in the child - try: - if env is None: - exec_fn(executable, cmd) - else: - exec_fn(executable, cmd, env) - except OSError as e: - if not DEBUG: - cmd = executable - sys.stderr.write("unable to execute %r: %s\n" - % (cmd, e.strerror)) - os._exit(1) + proc = subprocess.Popen(cmd, env=env) + proc.wait() + exitcode = proc.returncode + + if exitcode: if not DEBUG: - cmd = executable - sys.stderr.write("unable to execute %r for unknown reasons" % cmd) - os._exit(1) - else: # in the parent - # Loop until the child either exits or is terminated by a signal - # (ie. keep waiting if it's merely stopped) - while True: - try: - pid, status = os.waitpid(pid, 0) - except OSError as exc: - if not DEBUG: - cmd = executable - raise DistutilsExecError( - "command %r failed: %s" % (cmd, exc.args[-1])) - if os.WIFSIGNALED(status): - if not DEBUG: - cmd = executable - raise DistutilsExecError( - "command %r terminated by signal %d" - % (cmd, os.WTERMSIG(status))) - elif os.WIFEXITED(status): - exit_status = os.WEXITSTATUS(status) - if exit_status == 0: - return # hey, it succeeded! - else: - if not DEBUG: - cmd = executable - raise DistutilsExecError( - "command %r failed with exit status %d" - % (cmd, exit_status)) - elif os.WIFSTOPPED(status): - continue - else: - if not DEBUG: - cmd = executable - raise DistutilsExecError( - "unknown error executing %r: termination status %d" - % (cmd, status)) + cmd = cmd[0] + raise DistutilsExecError( + "command %r failed with exit code %s" % (cmd, exitcode)) + def find_executable(executable, path=None): """Tries to find 'executable' in the directories listed in 'path'. diff --git a/tests/test_spawn.py b/tests/test_spawn.py index f9ae69ef..73b0f5cb 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -8,7 +8,6 @@ from test.support import run_unittest, unix_shell from test import support as test_support from distutils.spawn import find_executable -from distutils.spawn import _nt_quote_args from distutils.spawn import spawn from distutils.errors import DistutilsExecError from distutils.tests import support @@ -17,16 +16,6 @@ class SpawnTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): - def test_nt_quote_args(self): - - for (args, wanted) in ((['with space', 'nospace'], - ['"with space"', 'nospace']), - (['nochange', 'nospace'], - ['nochange', 'nospace'])): - res = _nt_quote_args(args) - self.assertEqual(res, wanted) - - @unittest.skipUnless(os.name in ('nt', 'posix'), 'Runs only under posix or nt') def test_spawn(self): -- cgit v1.2.1 From 65fa92095484eb14b994574650809fca7cae2d2e Mon Sep 17 00:00:00 2001 From: mayeut Date: Mon, 11 Nov 2019 12:00:16 +0100 Subject: Use CPython 3.8.0 mechanism to find msvc 14+ --- appveyor.yml | 9 +++ changelog.d/1904.change.rst | 1 + setuptools/msvc.py | 159 ++++++++++++++++++++++++++++++++++++++-- setuptools/tests/test_msvc14.py | 84 +++++++++++++++++++++ tox.ini | 2 +- 5 files changed, 246 insertions(+), 9 deletions(-) create mode 100644 changelog.d/1904.change.rst create mode 100644 setuptools/tests/test_msvc14.py diff --git a/appveyor.yml b/appveyor.yml index f7ab22f6..de4e6c66 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,6 +7,15 @@ environment: CODECOV_ENV: APPVEYOR_JOB_NAME matrix: + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + APPVEYOR_JOB_NAME: "python35-x64-vs2015" + PYTHON: "C:\\Python35-x64" + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + APPVEYOR_JOB_NAME: "python35-x64-vs2017" + PYTHON: "C:\\Python35-x64" + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + APPVEYOR_JOB_NAME: "python35-x64-vs2019" + PYTHON: "C:\\Python35-x64" - APPVEYOR_JOB_NAME: "python36-x64" PYTHON: "C:\\Python36-x64" - APPVEYOR_JOB_NAME: "python37-x64" diff --git a/changelog.d/1904.change.rst b/changelog.d/1904.change.rst new file mode 100644 index 00000000..fa4bbfd9 --- /dev/null +++ b/changelog.d/1904.change.rst @@ -0,0 +1 @@ +Update msvc.py to use CPython 3.8.0 mechanism to find msvc 14+ diff --git a/setuptools/msvc.py b/setuptools/msvc.py index c2cbd1e5..213e39c9 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -26,6 +26,7 @@ from os.path import join, isfile, isdir, dirname import sys import platform import itertools +import subprocess import distutils.errors from setuptools.extern.packaging.version import LegacyVersion @@ -142,6 +143,154 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): raise +def _msvc14_find_vc2015(): + """Python 3.8 "distutils/_msvccompiler.py" backport""" + try: + key = winreg.OpenKey( + winreg.HKEY_LOCAL_MACHINE, + r"Software\Microsoft\VisualStudio\SxS\VC7", + 0, + winreg.KEY_READ | winreg.KEY_WOW64_32KEY + ) + except OSError: + return None, None + + best_version = 0 + best_dir = None + with key: + for i in itertools.count(): + try: + v, vc_dir, vt = winreg.EnumValue(key, i) + except OSError: + break + if v and vt == winreg.REG_SZ and isdir(vc_dir): + try: + version = int(float(v)) + except (ValueError, TypeError): + continue + if version >= 14 and version > best_version: + best_version, best_dir = version, vc_dir + return best_version, best_dir + + +def _msvc14_find_vc2017(): + """Python 3.8 "distutils/_msvccompiler.py" backport + + Returns "15, path" based on the result of invoking vswhere.exe + If no install is found, returns "None, None" + + The version is returned to avoid unnecessarily changing the function + result. It may be ignored when the path is not None. + + If vswhere.exe is not available, by definition, VS 2017 is not + installed. + """ + root = environ.get("ProgramFiles(x86)") or environ.get("ProgramFiles") + if not root: + return None, None + + try: + path = subprocess.check_output([ + join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), + "-latest", + "-prerelease", + "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "installationPath", + "-products", "*", + ]).decode(encoding="mbcs", errors="strict").strip() + except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): + return None, None + + path = join(path, "VC", "Auxiliary", "Build") + if isdir(path): + return 15, path + + return None, None + + +PLAT_SPEC_TO_RUNTIME = { + 'x86': 'x86', + 'x86_amd64': 'x64', + 'x86_arm': 'arm', + 'x86_arm64': 'arm64' +} + + +def _msvc14_find_vcvarsall(plat_spec): + """Python 3.8 "distutils/_msvccompiler.py" backport""" + _, best_dir = _msvc14_find_vc2017() + vcruntime = None + + if plat_spec in PLAT_SPEC_TO_RUNTIME: + vcruntime_plat = PLAT_SPEC_TO_RUNTIME[plat_spec] + else: + vcruntime_plat = 'x64' if 'amd64' in plat_spec else 'x86' + + if best_dir: + vcredist = join(best_dir, "..", "..", "redist", "MSVC", "**", + vcruntime_plat, "Microsoft.VC14*.CRT", + "vcruntime140.dll") + try: + import glob + vcruntime = glob.glob(vcredist, recursive=True)[-1] + except (ImportError, OSError, LookupError): + vcruntime = None + + if not best_dir: + best_version, best_dir = _msvc14_find_vc2015() + if best_version: + vcruntime = join(best_dir, 'redist', vcruntime_plat, + "Microsoft.VC140.CRT", "vcruntime140.dll") + + if not best_dir: + return None, None + + vcvarsall = join(best_dir, "vcvarsall.bat") + if not isfile(vcvarsall): + return None, None + + if not vcruntime or not isfile(vcruntime): + vcruntime = None + + return vcvarsall, vcruntime + + +def _msvc14_get_vc_env(plat_spec): + """Python 3.8 "distutils/_msvccompiler.py" backport""" + if "DISTUTILS_USE_SDK" in environ: + return { + key.lower(): value + for key, value in environ.items() + } + + vcvarsall, vcruntime = _msvc14_find_vcvarsall(plat_spec) + if not vcvarsall: + raise distutils.errors.DistutilsPlatformError( + "Unable to find vcvarsall.bat" + ) + + try: + out = subprocess.check_output( + 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), + stderr=subprocess.STDOUT, + ).decode('utf-16le', errors='replace') + except subprocess.CalledProcessError as exc: + raise distutils.errors.DistutilsPlatformError( + "Error executing {}".format(exc.cmd) + ) + + env = { + key.lower(): value + for key, _, value in + (line.partition('=') for line in out.splitlines()) + if key and value + } + + if vcruntime: + env['py_vcruntime_redist'] = vcruntime + return env + + def msvc14_get_vc_env(plat_spec): """ Patched "distutils._msvccompiler._get_vc_env" for support extra @@ -159,16 +308,10 @@ def msvc14_get_vc_env(plat_spec): dict environment """ - # Try to get environment from vcvarsall.bat (Classical way) - try: - return get_unpatched(msvc14_get_vc_env)(plat_spec) - except distutils.errors.DistutilsPlatformError: - # Pass error Vcvarsall.bat is missing - pass - # If error, try to set environment directly + # Always use backport from CPython 3.8 try: - return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env() + return _msvc14_get_vc_env(plat_spec) except distutils.errors.DistutilsPlatformError as exc: _augment_exception(exc, 14.0) raise diff --git a/setuptools/tests/test_msvc14.py b/setuptools/tests/test_msvc14.py new file mode 100644 index 00000000..7833aab4 --- /dev/null +++ b/setuptools/tests/test_msvc14.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +""" +Tests for msvc support module (msvc14 unit tests). +""" + +import os +from distutils.errors import DistutilsPlatformError +import pytest +import sys + + +@pytest.mark.skipif(sys.platform != "win32", + reason="These tests are only for win32") +class TestMSVC14: + """Python 3.8 "distutils/tests/test_msvccompiler.py" backport""" + def test_no_compiler(self): + import setuptools.msvc as _msvccompiler + # makes sure query_vcvarsall raises + # a DistutilsPlatformError if the compiler + # is not found + + def _find_vcvarsall(plat_spec): + return None, None + + old_find_vcvarsall = _msvccompiler._msvc14_find_vcvarsall + _msvccompiler._msvc14_find_vcvarsall = _find_vcvarsall + try: + pytest.raises(DistutilsPlatformError, + _msvccompiler._msvc14_get_vc_env, + 'wont find this version') + finally: + _msvccompiler._msvc14_find_vcvarsall = old_find_vcvarsall + + @pytest.mark.skipif(sys.version_info[0] < 3, + reason="Unicode requires encode/decode on Python 2") + def test_get_vc_env_unicode(self): + import setuptools.msvc as _msvccompiler + + test_var = 'ṰḖṤṪ┅ṼẨṜ' + test_value = '₃â´â‚…' + + # Ensure we don't early exit from _get_vc_env + old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None) + os.environ[test_var] = test_value + try: + env = _msvccompiler._msvc14_get_vc_env('x86') + assert test_var.lower() in env + assert test_value == env[test_var.lower()] + finally: + os.environ.pop(test_var) + if old_distutils_use_sdk: + os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk + + def test_get_vc2017(self): + import setuptools.msvc as _msvccompiler + + # This function cannot be mocked, so pass it if we find VS 2017 + # and mark it skipped if we do not. + version, path = _msvccompiler._msvc14_find_vc2017() + if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') in [ + 'Visual Studio 2017' + ]: + assert version + if version: + assert version >= 15 + assert os.path.isdir(path) + else: + pytest.skip("VS 2017 is not installed") + + def test_get_vc2015(self): + import setuptools.msvc as _msvccompiler + + # This function cannot be mocked, so pass it if we find VS 2015 + # and mark it skipped if we do not. + version, path = _msvccompiler._msvc14_find_vc2015() + if os.environ.get('APPVEYOR_BUILD_WORKER_IMAGE', '') in [ + 'Visual Studio 2015', 'Visual Studio 2017' + ]: + assert version + if version: + assert version >= 14 + assert os.path.isdir(path) + else: + pytest.skip("VS 2015 is not installed") diff --git a/tox.ini b/tox.ini index 959d7dad..1ebb922b 100644 --- a/tox.ini +++ b/tox.ini @@ -22,7 +22,7 @@ setenv = COVERAGE_FILE={toxworkdir}/.coverage.{envname} # TODO: The passed environment variables came from copying other tox.ini files # These should probably be individually annotated to explain what needs them. -passenv=APPDATA HOMEDRIVE HOMEPATH windir APPVEYOR APPVEYOR_* CI CODECOV_* TRAVIS TRAVIS_* NETWORK_REQUIRED +passenv=APPDATA HOMEDRIVE HOMEPATH windir Program* CommonProgram* VS* APPVEYOR APPVEYOR_* CI CODECOV_* TRAVIS TRAVIS_* NETWORK_REQUIRED commands=pytest --cov-config={toxinidir}/tox.ini --cov-report= {posargs} usedevelop=True extras = -- cgit v1.2.1 From dbbc6bba1c00ff193cea59a7eadee639d6ac3a11 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 7 Mar 2020 14:55:12 -0500 Subject: =?UTF-8?q?Bump=20version:=2045.2.0=20=E2=86=92=2045.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 7 +++++++ changelog.d/1557.change.rst | 1 - changelog.d/1904.change.rst | 1 - setup.cfg | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 changelog.d/1557.change.rst delete mode 100644 changelog.d/1904.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 8e86f31f..c438b5a8 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 45.2.0 +current_version = 45.3.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index f97f5142..e35c4472 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v45.3.0 +------- + +* #1557: Deprecated eggsecutable scripts and updated docs. +* #1904: Update msvc.py to use CPython 3.8.0 mechanism to find msvc 14+ + + v45.2.0 ------- diff --git a/changelog.d/1557.change.rst b/changelog.d/1557.change.rst deleted file mode 100644 index 9f8af4a6..00000000 --- a/changelog.d/1557.change.rst +++ /dev/null @@ -1 +0,0 @@ -Deprecated eggsecutable scripts and updated docs. diff --git a/changelog.d/1904.change.rst b/changelog.d/1904.change.rst deleted file mode 100644 index fa4bbfd9..00000000 --- a/changelog.d/1904.change.rst +++ /dev/null @@ -1 +0,0 @@ -Update msvc.py to use CPython 3.8.0 mechanism to find msvc 14+ diff --git a/setup.cfg b/setup.cfg index 4ffb2f5e..2e65b7a1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 45.2.0 +version = 45.3.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From c5958f26680b8bfad10d0ec03a613725c9a4c580 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 7 Mar 2020 16:50:40 -0500 Subject: Also remove mysterious hack from pkg_resources.extern --- pkg_resources/extern/__init__.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py index c1eb9e99..bf98d8f2 100644 --- a/pkg_resources/extern/__init__.py +++ b/pkg_resources/extern/__init__.py @@ -43,13 +43,6 @@ class VendorImporter: __import__(extant) mod = sys.modules[extant] sys.modules[fullname] = mod - # mysterious hack: - # Remove the reference to the extant package/module - # on later Python versions to cause relative imports - # in the vendor package to resolve the same modules - # as those going through this importer. - if prefix and sys.version_info > (3, 3): - del sys.modules[extant] return mod except ImportError: pass -- cgit v1.2.1 From a8f61a435ebc10638889b4be086254e525314bb2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 7 Mar 2020 17:05:59 -0500 Subject: Try building docs with python3. Ref #1992. --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index ec21e7be..aa84fe00 100644 --- a/netlify.toml +++ b/netlify.toml @@ -2,4 +2,4 @@ [build] publish = "docs/build/html" - command = "pip install tox && tox -e docs" + command = "python3 -m pip install tox && tox -e docs" -- cgit v1.2.1 From 35cdda926151d52963baf32e0770f2d5f16e5dfd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 7 Mar 2020 17:23:01 -0500 Subject: Reword UserWarning for insecure extraction path so that the message can be keyed for ignoring it. Ref #1899. --- pkg_resources/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 75563f95..88d4bdca 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1235,12 +1235,13 @@ class ResourceManager: mode = os.stat(path).st_mode if mode & stat.S_IWOTH or mode & stat.S_IWGRP: msg = ( - "%s is writable by group/others and vulnerable to attack " - "when " - "used with get_resource_filename. Consider a more secure " + "Extraction path is writable by group/others " + "and vulnerable to attack when " + "used with get_resource_filename ({path}). " + "Consider a more secure " "location (set with .set_extraction_path or the " - "PYTHON_EGG_CACHE environment variable)." % path - ) + "PYTHON_EGG_CACHE environment variable)." + ).format(**locals()) warnings.warn(msg, UserWarning) def postprocess(self, tempname, filename): -- cgit v1.2.1 From e63e131686be6bae3249a67958bdb2ef5abd1622 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 7 Mar 2020 17:25:23 -0500 Subject: Suppress UserWarning in pkg_resources. --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index 4cd94d5a..517fe3ce 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,3 +7,5 @@ filterwarnings = error # https://github.com/pypa/setuptools/issues/1823 ignore:bdist_wininst command is deprecated + # Suppress this error; unimportant for CI tests + ignore:Extraction path is writable by group/others:UserWarning -- cgit v1.2.1 From 8344ec66ee148ec841311f865c65b4129644e454 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 09:50:36 -0400 Subject: Try installing pip --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index aa84fe00..5fffee44 100644 --- a/netlify.toml +++ b/netlify.toml @@ -2,4 +2,4 @@ [build] publish = "docs/build/html" - command = "python3 -m pip install tox && tox -e docs" + command = "(wget https://bootstrap.pypa.io/get-pip.py -O - | python3) && python3 -m pip install tox && tox -e docs" -- cgit v1.2.1 From 098562e1fd63835d92883d02331e11165df92d13 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 10:55:09 -0400 Subject: Use pep517 to avoid bb://pypa/distlib#136. --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index 5fffee44..d08df89e 100644 --- a/netlify.toml +++ b/netlify.toml @@ -2,4 +2,4 @@ [build] publish = "docs/build/html" - command = "(wget https://bootstrap.pypa.io/get-pip.py -O - | python3) && python3 -m pip install tox && tox -e docs" + command = "(wget https://bootstrap.pypa.io/get-pip.py -O - | python3) && python3 -m pip install --use-pep517 tox && tox -e docs" -- cgit v1.2.1 From b5b3b2e8af0535361543d0307bf24b97b2699ba3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 12:03:56 -0400 Subject: Emit the Python version --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index d08df89e..77bd5b50 100644 --- a/netlify.toml +++ b/netlify.toml @@ -2,4 +2,4 @@ [build] publish = "docs/build/html" - command = "(wget https://bootstrap.pypa.io/get-pip.py -O - | python3) && python3 -m pip install --use-pep517 tox && tox -e docs" + command = "python3 -V && (wget https://bootstrap.pypa.io/get-pip.py -O - | python3) && python3 -m pip install --use-pep517 tox && tox -e docs" -- cgit v1.2.1 From 7aebb4624ddc29869b41ce0a21530b4398e8877e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 13:04:49 -0400 Subject: Add a file 'runtime.txt' to let Netlify know to use that env. Fixes #1992. --- netlify.toml | 2 +- runtime.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 runtime.txt diff --git a/netlify.toml b/netlify.toml index 77bd5b50..ec21e7be 100644 --- a/netlify.toml +++ b/netlify.toml @@ -2,4 +2,4 @@ [build] publish = "docs/build/html" - command = "python3 -V && (wget https://bootstrap.pypa.io/get-pip.py -O - | python3) && python3 -m pip install --use-pep517 tox && tox -e docs" + command = "pip install tox && tox -e docs" diff --git a/runtime.txt b/runtime.txt new file mode 100644 index 00000000..475ba515 --- /dev/null +++ b/runtime.txt @@ -0,0 +1 @@ +3.7 -- cgit v1.2.1 From 432baadc434e75f4ea5da9ff4c3546006e226c44 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 13:11:51 -0400 Subject: Docs land in build/html --- netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index ec21e7be..145ab020 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,5 +1,5 @@ # Configuration for pull request documentation previews via Netlify [build] - publish = "docs/build/html" + publish = "build/html" command = "pip install tox && tox -e docs" -- cgit v1.2.1 From a8942b4a29799d15b6f0feb6656118fc6b9ce43b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 13:19:37 -0400 Subject: Add a note about runtime.txt --- netlify.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/netlify.toml b/netlify.toml index 145ab020..5828132e 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,5 +1,7 @@ # Configuration for pull request documentation previews via Netlify +# Netlify relies on there being a ./runtime.txt to indicate Python 3. + [build] publish = "build/html" command = "pip install tox && tox -e docs" -- cgit v1.2.1 From dd250ca952bb3f109086eca3353b65a92fbcbf4d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 13:49:03 -0400 Subject: Suppress RuntimeWarning from sandbox module on Python 2 --- pytest.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pytest.ini b/pytest.ini index 517fe3ce..0d1824e4 100644 --- a/pytest.ini +++ b/pytest.ini @@ -9,3 +9,7 @@ filterwarnings = ignore:bdist_wininst command is deprecated # Suppress this error; unimportant for CI tests ignore:Extraction path is writable by group/others:UserWarning + # Suppress Python 2 deprecation warning + ignore:Setuptools will stop working on Python 2:UserWarning + # Suppress weird RuntimeWarning. + ignore:Parent module 'setuptools' not found while handling absolute import:RuntimeWarning -- cgit v1.2.1 From 47ca64bc1138960d7f6d8f0161ed2d74a3670ee4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 13:57:19 -0400 Subject: Suppress Windows bytes API warning until it can be fixed. Ref #2016. --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index 0d1824e4..0c2c8a17 100644 --- a/pytest.ini +++ b/pytest.ini @@ -13,3 +13,5 @@ filterwarnings = ignore:Setuptools will stop working on Python 2:UserWarning # Suppress weird RuntimeWarning. ignore:Parent module 'setuptools' not found while handling absolute import:RuntimeWarning + # Suppress use of bytes for filenames on Windows until fixed #2016 + ignore:The Windows bytes API has been deprecated:DeprecationWarning -- cgit v1.2.1 From 46430d4f340da778a86d92727bbd608c16d63ec0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 14:08:35 -0400 Subject: Suppress another warning --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index 0c2c8a17..a0d6dea6 100644 --- a/pytest.ini +++ b/pytest.ini @@ -15,3 +15,5 @@ filterwarnings = ignore:Parent module 'setuptools' not found while handling absolute import:RuntimeWarning # Suppress use of bytes for filenames on Windows until fixed #2016 ignore:The Windows bytes API has been deprecated:DeprecationWarning + # Suppress another Python 2 UnicodeWarning + ignore:Unicode equal comparison failed to convert:UnicodeWarning -- cgit v1.2.1 From d8e103d12af18bb8100952b4bc08eeed491b97c6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 14:28:59 -0400 Subject: Get both UnicodeWarnings --- pytest.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index a0d6dea6..af61043f 100644 --- a/pytest.ini +++ b/pytest.ini @@ -15,5 +15,6 @@ filterwarnings = ignore:Parent module 'setuptools' not found while handling absolute import:RuntimeWarning # Suppress use of bytes for filenames on Windows until fixed #2016 ignore:The Windows bytes API has been deprecated:DeprecationWarning - # Suppress another Python 2 UnicodeWarning + # Suppress other Python 2 UnicodeWarnings ignore:Unicode equal comparison failed to convert:UnicodeWarning + ignore:Unicode unequal comparison failed to convert:UnicodeWarning -- cgit v1.2.1 From 50f3575da42ce5b7c013c28ff623ce16c231455d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 15:53:33 -0400 Subject: =?UTF-8?q?Bump=20version:=2045.3.0=20=E2=86=92=2046.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 10 ++++++++++ changelog.d/1890.change.rst | 1 - changelog.d/1899.change.rst | 1 - changelog.d/1991.misc.rst | 1 - changelog.d/2011.doc.rst | 1 - changelog.d/65.breaking.rst | 1 - setup.cfg | 2 +- 8 files changed, 12 insertions(+), 7 deletions(-) delete mode 100644 changelog.d/1890.change.rst delete mode 100644 changelog.d/1899.change.rst delete mode 100644 changelog.d/1991.misc.rst delete mode 100644 changelog.d/2011.doc.rst delete mode 100644 changelog.d/65.breaking.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index c438b5a8..5fabdff0 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 45.3.0 +current_version = 46.0.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index e35c4472..93c1f890 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,13 @@ +v46.0.0 +------- + +* #65: Once again as in 3.0, removed the Features feature. +* #1890: Fix vendored dependencies so importing ``setuptools.extern.some_module`` gives the same object as ``setuptools._vendor.some_module``. This makes Metadata picklable again. +* #1899: Test suite now fails on warnings. +* #2011: Fix broken link to distutils docs on package_data +* #1991: Include pkg_resources test data in sdist, so tests can be executed from it. + + v45.3.0 ------- diff --git a/changelog.d/1890.change.rst b/changelog.d/1890.change.rst deleted file mode 100644 index 458d8117..00000000 --- a/changelog.d/1890.change.rst +++ /dev/null @@ -1 +0,0 @@ -Fix vendored dependencies so importing ``setuptools.extern.some_module`` gives the same object as ``setuptools._vendor.some_module``. This makes Metadata picklable again. \ No newline at end of file diff --git a/changelog.d/1899.change.rst b/changelog.d/1899.change.rst deleted file mode 100644 index 30768439..00000000 --- a/changelog.d/1899.change.rst +++ /dev/null @@ -1 +0,0 @@ -Test suite now fails on warnings. diff --git a/changelog.d/1991.misc.rst b/changelog.d/1991.misc.rst deleted file mode 100644 index ac6904a2..00000000 --- a/changelog.d/1991.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Include pkg_resources test data in sdist, so tests can be executed from it. diff --git a/changelog.d/2011.doc.rst b/changelog.d/2011.doc.rst deleted file mode 100644 index e36fb6a7..00000000 --- a/changelog.d/2011.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Fix broken link to distutils docs on package_data diff --git a/changelog.d/65.breaking.rst b/changelog.d/65.breaking.rst deleted file mode 100644 index bde42740..00000000 --- a/changelog.d/65.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -Once again as in 3.0, removed the Features feature. diff --git a/setup.cfg b/setup.cfg index 2e65b7a1..d231a3e1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 45.3.0 +version = 46.0.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From b13dcdee2f2ed9affdf9f52700710789f5a04803 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 16:37:43 -0400 Subject: Replace playbook with code for finalizing a release. --- docs/releases.txt | 40 +++++++------------------------------ tools/finalize.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tox.ini | 7 +++++++ 3 files changed, 73 insertions(+), 33 deletions(-) create mode 100644 tools/finalize.py diff --git a/docs/releases.txt b/docs/releases.txt index 98ba39e8..35b415c2 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -3,39 +3,13 @@ Release Process =============== In order to allow for rapid, predictable releases, Setuptools uses a -mechanical technique for releases, enacted by Travis following a -successful build of a tagged release per -`PyPI deployment `_. - -Prior to cutting a release, please use `towncrier`_ to update -``CHANGES.rst`` to summarize the changes since the last release. -To update the changelog: - -1. Install towncrier via ``pip install towncrier`` if not already installed. -2. Preview the new ``CHANGES.rst`` entry by running - ``towncrier --draft --version {new.version.number}`` (enter the desired - version number for the next release). If any changes are needed, make - them and generate a new preview until the output is acceptable. Run - ``git add`` for any modified files. -3. Run ``towncrier --version {new.version.number}`` to stage the changelog - updates in git. -4. Verify that there are no remaining ``changelog.d/*.rst`` files. If a - file was named incorrectly, it may be ignored by towncrier. -5. Review the updated ``CHANGES.rst`` file. If any changes are needed, - make the edits and stage them via ``git add CHANGES.rst``. - -Once the changelog edits are staged and ready to commit, cut a release by -installing and running ``bump2version --allow-dirty {part}`` where ``part`` -is major, minor, or patch based on the scope of the changes in the -release. Then, push the commits to the master branch:: - - $ git push origin master - $ git push --tags - -If tests pass, the release will be uploaded to PyPI (from the Python 3.6 -tests). - -.. _towncrier: https://pypi.org/project/towncrier/ +mechanical technique for releases, enacted on tagged commits by +continuous integration. + +To finalize a release, run ``tox -e finalize``, review, then push +the changes. + +If tests pass, the release will be uploaded to PyPI. Release Frequency ----------------- diff --git a/tools/finalize.py b/tools/finalize.py new file mode 100644 index 00000000..3b66341a --- /dev/null +++ b/tools/finalize.py @@ -0,0 +1,59 @@ +""" +Finalize the repo for a release. Invokes towncrier and bumpversion. +""" + +__requires__ = ['bump2version', 'towncrier'] + + +import subprocess +import pathlib +import re +import sys + + +def release_kind(): + """ + Determine which release to make based on the files in the + changelog. + """ + # use min here as 'major' < 'minor' < 'patch' + return min( + 'major' if 'breaking' in file.name else + 'minor' if 'change' in file.name else + 'patch' + for file in pathlib.Path('changelog.d').iterdir() + ) + + +bump_version_command = [ + sys.executable, + '-m', 'bumpversion', + release_kind(), +] + + +def get_version(): + cmd = bump_version_command + ['--dry-run', '--verbose'] + out = subprocess.check_output(cmd, text=True) + return re.search('^new_version=(.*)', out, re.MULTILINE).group(1) + + +def update_changelog(): + cmd = [ + sys.executable, '-m', + 'towncrier', + '--version', get_version(), + '--yes', + ] + subprocess.check_call(cmd) + + +def bump_version(): + cmd = bump_version_command + ['--allow-dirty'] + subprocess.check_call(cmd) + + +if __name__ == '__main__': + print("Cutting release at", get_version()) + update_changelog() + bump_version() diff --git a/tox.ini b/tox.ini index 1ebb922b..17201ca2 100644 --- a/tox.ini +++ b/tox.ini @@ -60,6 +60,13 @@ source= omit= */_vendor/* +[testenv:finalize] +deps = + towncrier + bump2version +commands = + python tools/finalize.py + [testenv:release] skip_install = True deps = -- cgit v1.2.1 From a04ec132fedb9d180f302b302fa16cfd194667d5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 16:40:07 -0400 Subject: Avoid install during finalize --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 17201ca2..347106ec 100644 --- a/tox.ini +++ b/tox.ini @@ -61,6 +61,7 @@ omit= */_vendor/* [testenv:finalize] +skip_install = True deps = towncrier bump2version -- cgit v1.2.1 From 6e7341b2cb740741d77e0184db2b9028b1b93773 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 10 Mar 2020 09:53:09 +0100 Subject: bpo-1294959: Add sys.platlibdir attribute (GH-18381) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add --with-platlibdir option to the configure script: name of the platform-specific library directory, stored in the new sys.platlitdir attribute. It is used to build the path of platform-specific dynamic libraries and the path of the standard library. It is equal to "lib" on most platforms. On Fedora and SuSE, it is equal to "lib64" on 64-bit systems. Co-Authored-By: Jan MatÄ›jek Co-Authored-By: MatÄ›j Cepl Co-Authored-By: Charalampos Stratakis --- command/install.py | 5 +++-- sysconfig.py | 11 +++++++++-- tests/test_install.py | 3 ++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/command/install.py b/command/install.py index c625c95b..aaa300ef 100644 --- a/command/install.py +++ b/command/install.py @@ -30,14 +30,14 @@ WINDOWS_SCHEME = { INSTALL_SCHEMES = { 'unix_prefix': { 'purelib': '$base/lib/python$py_version_short/site-packages', - 'platlib': '$platbase/lib/python$py_version_short/site-packages', + 'platlib': '$platbase/$platlibdir/python$py_version_short/site-packages', 'headers': '$base/include/python$py_version_short$abiflags/$dist_name', 'scripts': '$base/bin', 'data' : '$base', }, 'unix_home': { 'purelib': '$base/lib/python', - 'platlib': '$base/lib/python', + 'platlib': '$base/$platlibdir/python', 'headers': '$base/include/python/$dist_name', 'scripts': '$base/bin', 'data' : '$base', @@ -298,6 +298,7 @@ class install(Command): 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, 'abiflags': abiflags, + 'platlibdir': sys.platlibdir, } if HAS_USER_SITE: diff --git a/sysconfig.py b/sysconfig.py index b51629eb..01ee5197 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -146,8 +146,15 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": - libpython = os.path.join(prefix, - "lib", "python" + get_python_version()) + if plat_specific or standard_lib: + # Platform-specific modules (any module from a non-pure-Python + # module distribution) or standard Python library modules. + libdir = sys.platlibdir + else: + # Pure Python + libdir = "lib" + libpython = os.path.join(prefix, libdir, + "python" + get_python_version()) if standard_lib: return libpython else: diff --git a/tests/test_install.py b/tests/test_install.py index 287ab198..51c80e04 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -58,7 +58,8 @@ class InstallTestCase(support.TempdirManager, libdir = os.path.join(destination, "lib", "python") check_path(cmd.install_lib, libdir) - check_path(cmd.install_platlib, libdir) + platlibdir = os.path.join(destination, sys.platlibdir, "python") + check_path(cmd.install_platlib, platlibdir) check_path(cmd.install_purelib, libdir) check_path(cmd.install_headers, os.path.join(destination, "include", "python", "foopkg")) -- cgit v1.2.1 From a753df58651cb2d685dc721a40b450a7dba611ce Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 15:04:22 -0400 Subject: Suppress Pytest 5.4 warnings. Closes #2025 --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index af61043f..b13b7f33 100644 --- a/pytest.ini +++ b/pytest.ini @@ -18,3 +18,5 @@ filterwarnings = # Suppress other Python 2 UnicodeWarnings ignore:Unicode equal comparison failed to convert:UnicodeWarning ignore:Unicode unequal comparison failed to convert:UnicodeWarning + # https://github.com/pypa/setuptools/issues/2025 + ignore:direct construction of .*Item has been deprecated:DeprecationWarning -- cgit v1.2.1 From 24fa288a98a14e6ec57b996b151a48203322c936 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 14:57:18 -0400 Subject: Extract method for validating version. --- setuptools/dist.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index 7ffe0ba1..0f71dd98 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -438,30 +438,35 @@ class Distribution(_Distribution): value = default() if default else None setattr(self.metadata, option, value) - if isinstance(self.metadata.version, numbers.Number): + self.metadata.version = self._validate_version(self.metadata.version) + self._finalize_requires() + + @staticmethod + def _validate_version(version): + if isinstance(version, numbers.Number): # Some people apparently take "version number" too literally :) - self.metadata.version = str(self.metadata.version) + version = str(version) - if self.metadata.version is not None: + if version is not None: try: - ver = packaging.version.Version(self.metadata.version) + ver = packaging.version.Version(version) normalized_version = str(ver) - if self.metadata.version != normalized_version: + if version != normalized_version: warnings.warn( "Normalizing '%s' to '%s'" % ( - self.metadata.version, + version, normalized_version, ) ) - self.metadata.version = normalized_version + version = normalized_version except (packaging.version.InvalidVersion, TypeError): warnings.warn( "The version specified (%r) is an invalid version, this " "may not work as expected with newer versions of " "setuptools, pip, and PyPI. Please see PEP 440 for more " - "details." % self.metadata.version + "details." % version ) - self._finalize_requires() + return version def _finalize_requires(self): """ -- cgit v1.2.1 From a323a4962eef39b6af7c5d07cdeb88bb0c307ce4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 15:31:09 -0400 Subject: Extract method for normalization, allowing for bypass when the version is wrapped in 'sic'. Fixes #308. --- setuptools/__init__.py | 4 ++++ setuptools/dist.py | 28 +++++++++++++++++----------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 4485852f..811f3fd2 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -224,5 +224,9 @@ def findall(dir=os.curdir): return list(files) +class sic(str): + """Treat this string as-is (https://en.wikipedia.org/wiki/Sic)""" + + # Apply monkey patches monkey.patch_all() diff --git a/setuptools/dist.py b/setuptools/dist.py index 0f71dd98..a2f8ea0d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -30,6 +30,7 @@ from setuptools.extern.six.moves import map, filter, filterfalse from . import SetuptoolsDeprecationWarning +import setuptools from setuptools import windows_support from setuptools.monkey import get_unpatched from setuptools.config import parse_configuration @@ -438,9 +439,22 @@ class Distribution(_Distribution): value = default() if default else None setattr(self.metadata, option, value) - self.metadata.version = self._validate_version(self.metadata.version) + self.metadata.version = self._normalize_version( + self._validate_version(self.metadata.version)) self._finalize_requires() + @staticmethod + def _normalize_version(version): + if isinstance(version, setuptools.sic) or version is None: + return version + + normalized = str(packaging.version.Version(version)) + if version != normalized: + tmpl = "Normalizing '{version}' to '{normalized}'" + warnings.warn(tmpl.format(**locals())) + return normalized + return version + @staticmethod def _validate_version(version): if isinstance(version, numbers.Number): @@ -449,16 +463,7 @@ class Distribution(_Distribution): if version is not None: try: - ver = packaging.version.Version(version) - normalized_version = str(ver) - if version != normalized_version: - warnings.warn( - "Normalizing '%s' to '%s'" % ( - version, - normalized_version, - ) - ) - version = normalized_version + packaging.version.Version(version) except (packaging.version.InvalidVersion, TypeError): warnings.warn( "The version specified (%r) is an invalid version, this " @@ -466,6 +471,7 @@ class Distribution(_Distribution): "setuptools, pip, and PyPI. Please see PEP 440 for more " "details." % version ) + return setuptools.sic(version) return version def _finalize_requires(self): -- cgit v1.2.1 From 64c742406982f000e42dca065b4a34498312fb65 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 15:32:49 -0400 Subject: Add changelog entry. --- changelog.d/308.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/308.change.rst diff --git a/changelog.d/308.change.rst b/changelog.d/308.change.rst new file mode 100644 index 00000000..5806d80e --- /dev/null +++ b/changelog.d/308.change.rst @@ -0,0 +1 @@ +Allow version number normalization to be bypassed by wrapping in a 'setuptools.sic()' call. -- cgit v1.2.1 From b6076bee61984e6e05bf0d02e1daf09f0469e526 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 21:14:59 -0400 Subject: Add test capturing use-case for normalized version. Ref #308. --- setuptools/tests/test_dist.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index 6e8c45fd..26c271b1 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -139,6 +139,13 @@ def __read_test_cases(): {'name': 'foo', 'version': '1.0.0', 'author': 'Snorri Sturluson'}), + ( + 'Normalized version', + dict( + name='foo', + version='1.0.0a', + ), + ), ] return test_cases -- cgit v1.2.1 From b43c5b81b76d687b50b05f05155838c69919c42c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 21:43:11 -0400 Subject: Remove superfluous test and re-organize tests for clarity. --- setuptools/tests/test_dist.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index 6e8c45fd..97055d97 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -125,8 +125,6 @@ def __read_test_cases(): merge_dicts(base_attrs, { 'provides_extras': ['foo', 'bar'] }), marks=pytest.mark.xfail(reason="provides_extras not read")), - ('Missing author, missing author e-mail', - {'name': 'foo', 'version': '1.0.0'}), ('Missing author', {'name': 'foo', 'version': '1.0.0', @@ -135,10 +133,8 @@ def __read_test_cases(): {'name': 'foo', 'version': '1.0.0', 'author': 'Snorri Sturluson'}), - ('Missing author', - {'name': 'foo', - 'version': '1.0.0', - 'author': 'Snorri Sturluson'}), + ('Missing author and e-mail', + {'name': 'foo', 'version': '1.0.0'}), ] return test_cases -- cgit v1.2.1 From 0e1c968f36a5d6cf268fe3f1ffca9405bb99e20d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 22:08:36 -0400 Subject: Trim up syntax on test cases, including removing hanging indents. --- setuptools/tests/test_dist.py | 110 +++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 56 deletions(-) diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index 97055d97..47c2de3e 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -70,71 +70,69 @@ def test_dist__get_unpatched_deprecated(): def __read_test_cases(): - # Metadata version 1.0 - base_attrs = { - "name": "package", - "version": "0.0.1", - "author": "Foo Bar", - "author_email": "foo@bar.net", - "long_description": "Long\ndescription", - "description": "Short description", - "keywords": ["one", "two"] - } - - def merge_dicts(d1, d2): - d1 = d1.copy() - d1.update(d2) - - return d1 + base = dict( + name="package", + version="0.0.1", + author="Foo Bar", + author_email="foo@bar.net", + long_description="Long\ndescription", + description="Short description", + keywords=["one", "two"], + ) + + def params(**update): + return dict(base, **update) test_cases = [ - ('Metadata version 1.0', base_attrs.copy()), - ('Metadata version 1.1: Provides', merge_dicts(base_attrs, { - 'provides': ['package'] - })), - ('Metadata version 1.1: Obsoletes', merge_dicts(base_attrs, { - 'obsoletes': ['foo'] - })), - ('Metadata version 1.1: Classifiers', merge_dicts(base_attrs, { - 'classifiers': [ + ('Metadata version 1.0', params()), + ('Metadata version 1.1: Provides', params( + provides=['package'], + )), + ('Metadata version 1.1: Obsoletes', params( + obsoletes=['foo'], + )), + ('Metadata version 1.1: Classifiers', params( + classifiers=[ 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.7', 'License :: OSI Approved :: MIT License', - ]})), - ('Metadata version 1.1: Download URL', merge_dicts(base_attrs, { - 'download_url': 'https://example.com' - })), - ('Metadata Version 1.2: Requires-Python', merge_dicts(base_attrs, { - 'python_requires': '>=3.7' - })), + ], + )), + ('Metadata version 1.1: Download URL', params( + download_url='https://example.com', + )), + ('Metadata Version 1.2: Requires-Python', params( + python_requires='>=3.7', + )), pytest.param( 'Metadata Version 1.2: Project-Url', - merge_dicts(base_attrs, { - 'project_urls': { - 'Foo': 'https://example.bar' - } - }), marks=pytest.mark.xfail( - reason="Issue #1578: project_urls not read" - )), - ('Metadata Version 2.1: Long Description Content Type', - merge_dicts(base_attrs, { - 'long_description_content_type': 'text/x-rst; charset=UTF-8' - })), + params(project_urls=dict(Foo='https://example.bar')), + marks=pytest.mark.xfail( + reason="Issue #1578: project_urls not read", + ), + ), + ('Metadata Version 2.1: Long Description Content Type', params( + long_description_content_type='text/x-rst; charset=UTF-8', + )), pytest.param( 'Metadata Version 2.1: Provides Extra', - merge_dicts(base_attrs, { - 'provides_extras': ['foo', 'bar'] - }), marks=pytest.mark.xfail(reason="provides_extras not read")), - ('Missing author', - {'name': 'foo', - 'version': '1.0.0', - 'author_email': 'snorri@sturluson.name'}), - ('Missing author e-mail', - {'name': 'foo', - 'version': '1.0.0', - 'author': 'Snorri Sturluson'}), - ('Missing author and e-mail', - {'name': 'foo', 'version': '1.0.0'}), + params(provides_extras=['foo', 'bar']), + marks=pytest.mark.xfail(reason="provides_extras not read"), + ), + ('Missing author', dict( + name='foo', + version='1.0.0', + author_email='snorri@sturluson.name', + )), + ('Missing author e-mail', dict( + name='foo', + version='1.0.0', + author='Snorri Sturluson', + )), + ('Missing author and e-mail', dict( + name='foo', + version='1.0.0', + )), ] return test_cases -- cgit v1.2.1 From b8c2ae517db14b03dfba263aa66707ca67ecee8f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 22:22:04 -0400 Subject: Rely on partial now that pattern makes sense --- setuptools/tests/test_dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index 47c2de3e..a9837b16 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import io import collections import re +import functools from distutils.errors import DistutilsSetupError from setuptools.dist import ( _get_unpatched, @@ -80,8 +81,7 @@ def __read_test_cases(): keywords=["one", "two"], ) - def params(**update): - return dict(base, **update) + params = functools.partial(dict, base) test_cases = [ ('Metadata version 1.0', params()), -- cgit v1.2.1 From 8356ff3c6816d2a075098421252e096955d1fdbd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 20 Mar 2020 21:43:22 -0400 Subject: Trim excess whitespace --- setuptools/tests/test_dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index c531b440..531ea1b4 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -137,7 +137,7 @@ def __read_test_cases(): ('Bypass normalized version', dict( name='foo', version=sic('1.0.0a'), - )), + )), ] return test_cases -- cgit v1.2.1 From a3e1afead418ae25119bf19a587d9696c27beb9a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 05:13:37 -0400 Subject: Rename changefile and clean up changelog. --- changelog.d/1431.change.rst | 1 + changelog.d/1431.chnage.rst | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) create mode 100644 changelog.d/1431.change.rst delete mode 100644 changelog.d/1431.chnage.rst diff --git a/changelog.d/1431.change.rst b/changelog.d/1431.change.rst new file mode 100644 index 00000000..a3848afa --- /dev/null +++ b/changelog.d/1431.change.rst @@ -0,0 +1 @@ +In ``easy_install.check_site_dir``, ensure the installation directory exists. diff --git a/changelog.d/1431.chnage.rst b/changelog.d/1431.chnage.rst deleted file mode 100644 index a5cb324e..00000000 --- a/changelog.d/1431.chnage.rst +++ /dev/null @@ -1,3 +0,0 @@ -Make install directory if it doesn't exist yet to prevent Fedora's -specific setup to blow up if ``/usr/local/lib/pythonX.Y/site-packages`` -doesn't exist yet. -- cgit v1.2.1 From 7db9dc2fc4b64e45979dc17ff844f997ff9fa63c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 05:38:29 -0400 Subject: Update changelog entry --- changelog.d/1563.change.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/1563.change.rst b/changelog.d/1563.change.rst index a2452cd8..4ae16384 100644 --- a/changelog.d/1563.change.rst +++ b/changelog.d/1563.change.rst @@ -1 +1 @@ -If find_module fails or does not exist, attempt to use find_spec(...).loader +In ``pkg_resources`` prefer ``find_spec`` (PEP 451) to ``find_module``. -- cgit v1.2.1 From 6b5a5872503290d6c90ab18cd72e46776977064a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 05:41:02 -0400 Subject: Trap AttributeError in exactly one place. --- pkg_resources/__init__.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index deea96b9..2f6c0cbf 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2102,14 +2102,10 @@ def _handle_ns(packageName, path_item): try: loader = importer.find_spec(packageName).loader except AttributeError: - try: - # capture warnings due to #1111 - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - loader = importer.find_module(packageName) - except AttributeError: - # not a system module - loader = None + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + loader = importer.find_module(packageName) if loader is None: return None -- cgit v1.2.1 From 52e718872259617203556e4889d451167f209343 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 06:28:17 -0400 Subject: Add test capturing expectation. Ref #1451. --- setuptools/tests/test_build_py.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py index b3a99f56..92b455dd 100644 --- a/setuptools/tests/test_build_py.py +++ b/setuptools/tests/test_build_py.py @@ -1,4 +1,6 @@ import os +import stat +import shutil from setuptools.dist import Distribution @@ -20,3 +22,28 @@ def test_directories_in_package_data_glob(tmpdir_cwd): os.makedirs('path/subpath') dist.parse_command_line() dist.run_commands() + + +def test_read_only(tmpdir_cwd): + """ + Ensure mode is not preserved in copy for package modules + and package data, as that causes problems + with deleting read-only files on Windows. + + #1451 + """ + dist = Distribution(dict( + script_name='setup.py', + script_args=['build_py'], + packages=['pkg'], + package_data={'pkg': ['data.dat']}, + name='pkg', + )) + os.makedirs('pkg') + open('pkg/__init__.py', 'w').close() + open('pkg/data.dat', 'w').close() + os.chmod('pkg/__init__.py', stat.S_IREAD) + os.chmod('pkg/data.dat', stat.S_IREAD) + dist.parse_command_line() + dist.run_commands() + shutil.rmtree('build') -- cgit v1.2.1 From b1abc23f0124de8ad99149674613ff8e2c71706a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 16:37:43 -0400 Subject: Replace playbook with code for finalizing a release. --- docs/releases.txt | 40 +++++++------------------------------ tools/finalize.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tox.ini | 7 +++++++ 3 files changed, 73 insertions(+), 33 deletions(-) create mode 100644 tools/finalize.py diff --git a/docs/releases.txt b/docs/releases.txt index 98ba39e8..35b415c2 100644 --- a/docs/releases.txt +++ b/docs/releases.txt @@ -3,39 +3,13 @@ Release Process =============== In order to allow for rapid, predictable releases, Setuptools uses a -mechanical technique for releases, enacted by Travis following a -successful build of a tagged release per -`PyPI deployment `_. - -Prior to cutting a release, please use `towncrier`_ to update -``CHANGES.rst`` to summarize the changes since the last release. -To update the changelog: - -1. Install towncrier via ``pip install towncrier`` if not already installed. -2. Preview the new ``CHANGES.rst`` entry by running - ``towncrier --draft --version {new.version.number}`` (enter the desired - version number for the next release). If any changes are needed, make - them and generate a new preview until the output is acceptable. Run - ``git add`` for any modified files. -3. Run ``towncrier --version {new.version.number}`` to stage the changelog - updates in git. -4. Verify that there are no remaining ``changelog.d/*.rst`` files. If a - file was named incorrectly, it may be ignored by towncrier. -5. Review the updated ``CHANGES.rst`` file. If any changes are needed, - make the edits and stage them via ``git add CHANGES.rst``. - -Once the changelog edits are staged and ready to commit, cut a release by -installing and running ``bump2version --allow-dirty {part}`` where ``part`` -is major, minor, or patch based on the scope of the changes in the -release. Then, push the commits to the master branch:: - - $ git push origin master - $ git push --tags - -If tests pass, the release will be uploaded to PyPI (from the Python 3.6 -tests). - -.. _towncrier: https://pypi.org/project/towncrier/ +mechanical technique for releases, enacted on tagged commits by +continuous integration. + +To finalize a release, run ``tox -e finalize``, review, then push +the changes. + +If tests pass, the release will be uploaded to PyPI. Release Frequency ----------------- diff --git a/tools/finalize.py b/tools/finalize.py new file mode 100644 index 00000000..3b66341a --- /dev/null +++ b/tools/finalize.py @@ -0,0 +1,59 @@ +""" +Finalize the repo for a release. Invokes towncrier and bumpversion. +""" + +__requires__ = ['bump2version', 'towncrier'] + + +import subprocess +import pathlib +import re +import sys + + +def release_kind(): + """ + Determine which release to make based on the files in the + changelog. + """ + # use min here as 'major' < 'minor' < 'patch' + return min( + 'major' if 'breaking' in file.name else + 'minor' if 'change' in file.name else + 'patch' + for file in pathlib.Path('changelog.d').iterdir() + ) + + +bump_version_command = [ + sys.executable, + '-m', 'bumpversion', + release_kind(), +] + + +def get_version(): + cmd = bump_version_command + ['--dry-run', '--verbose'] + out = subprocess.check_output(cmd, text=True) + return re.search('^new_version=(.*)', out, re.MULTILINE).group(1) + + +def update_changelog(): + cmd = [ + sys.executable, '-m', + 'towncrier', + '--version', get_version(), + '--yes', + ] + subprocess.check_call(cmd) + + +def bump_version(): + cmd = bump_version_command + ['--allow-dirty'] + subprocess.check_call(cmd) + + +if __name__ == '__main__': + print("Cutting release at", get_version()) + update_changelog() + bump_version() diff --git a/tox.ini b/tox.ini index 2d43e76f..b1069220 100644 --- a/tox.ini +++ b/tox.ini @@ -58,6 +58,13 @@ source= omit= */_vendor/* +[testenv:finalize] +deps = + towncrier + bump2version +commands = + python tools/finalize.py + [testenv:release] skip_install = True deps = -- cgit v1.2.1 From c020b5f88579487350ea38a40d3ea2cab1318144 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Mar 2020 16:40:07 -0400 Subject: Avoid install during finalize --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index b1069220..3fc6a564 100644 --- a/tox.ini +++ b/tox.ini @@ -59,6 +59,7 @@ omit= */_vendor/* [testenv:finalize] +skip_install = True deps = towncrier bump2version -- cgit v1.2.1 From 1988125e800b3b64f9cf9311fea5525cc81546f9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 14:41:31 -0400 Subject: =?UTF-8?q?Bump=20version:=2044.0.0=20=E2=86=92=2044.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 3 +-- CHANGES.rst | 8 ++++++++ changelog.d/1704.change.rst | 1 - changelog.d/1959.change.rst | 1 - changelog.d/1994.change.rst | 1 - setup.cfg | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) delete mode 100644 changelog.d/1704.change.rst delete mode 100644 changelog.d/1959.change.rst delete mode 100644 changelog.d/1994.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index e1bfa898..c8c9f544 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,7 +1,6 @@ [bumpversion] -current_version = 44.0.0 +current_version = 44.1.0 commit = True tag = True [bumpversion:file:setup.cfg] - diff --git a/CHANGES.rst b/CHANGES.rst index 109a3f48..83cd8c78 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,11 @@ +v44.1.0 +------- + +* #1704: Set sys.argv[0] in setup script run by build_meta.__legacy__ +* #1959: Fix for Python 4: replace unsafe six.PY3 with six.PY2 +* #1994: Fixed a bug in the "setuptools.finalize_distribution_options" hook that lead to ignoring the order attribute of entry points managed by this hook. + + v44.0.0 ------- diff --git a/changelog.d/1704.change.rst b/changelog.d/1704.change.rst deleted file mode 100644 index 62450835..00000000 --- a/changelog.d/1704.change.rst +++ /dev/null @@ -1 +0,0 @@ -Set sys.argv[0] in setup script run by build_meta.__legacy__ diff --git a/changelog.d/1959.change.rst b/changelog.d/1959.change.rst deleted file mode 100644 index c0cc8975..00000000 --- a/changelog.d/1959.change.rst +++ /dev/null @@ -1 +0,0 @@ -Fix for Python 4: replace unsafe six.PY3 with six.PY2 diff --git a/changelog.d/1994.change.rst b/changelog.d/1994.change.rst deleted file mode 100644 index 4a6cc742..00000000 --- a/changelog.d/1994.change.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a bug in the "setuptools.finalize_distribution_options" hook that lead to ignoring the order attribute of entry points managed by this hook. diff --git a/setup.cfg b/setup.cfg index ecef8609..cb5ae73b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,7 +19,7 @@ universal = 1 [metadata] name = setuptools -version = 44.0.0 +version = 44.1.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From ee4d0dd1c0bd4c3efb6d2b933c2027e88917bfd1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 14:45:16 -0400 Subject: =?UTF-8?q?Bump=20version:=2046.0.0=20=E2=86=92=2046.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 15 +++++++++++++++ changelog.d/1424.change.rst | 1 - changelog.d/1431.change.rst | 1 - changelog.d/1563.change.rst | 1 - changelog.d/308.change.rst | 1 - setup.cfg | 2 +- 7 files changed, 17 insertions(+), 6 deletions(-) delete mode 100644 changelog.d/1424.change.rst delete mode 100644 changelog.d/1431.change.rst delete mode 100644 changelog.d/1563.change.rst delete mode 100644 changelog.d/308.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 8f2ef1d8..28a327b2 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 46.0.0 +current_version = 46.1.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 1e8da46e..12680a4d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,18 @@ +v46.1.0 +------- + +* #308: Allow version number normalization to be bypassed by wrapping in a 'setuptools.sic()' call. +* #1424: Prevent keeping files mode for package_data build. It may break a build if user's package data has read only flag. +* #1431: In ``easy_install.check_site_dir``, ensure the installation directory exists. +* #1563: In ``pkg_resources`` prefer ``find_spec`` (PEP 451) to ``find_module``. + +Incorporate changes from v44.1.0: + +* #1704: Set sys.argv[0] in setup script run by build_meta.__legacy__ +* #1959: Fix for Python 4: replace unsafe six.PY3 with six.PY2 +* #1994: Fixed a bug in the "setuptools.finalize_distribution_options" hook that lead to ignoring the order attribute of entry points managed by this hook. + + v44.1.0 ------- diff --git a/changelog.d/1424.change.rst b/changelog.d/1424.change.rst deleted file mode 100644 index 361997dd..00000000 --- a/changelog.d/1424.change.rst +++ /dev/null @@ -1 +0,0 @@ -Prevent keeping files mode for package_data build. It may break a build if user's package data has read only flag. \ No newline at end of file diff --git a/changelog.d/1431.change.rst b/changelog.d/1431.change.rst deleted file mode 100644 index a3848afa..00000000 --- a/changelog.d/1431.change.rst +++ /dev/null @@ -1 +0,0 @@ -In ``easy_install.check_site_dir``, ensure the installation directory exists. diff --git a/changelog.d/1563.change.rst b/changelog.d/1563.change.rst deleted file mode 100644 index 4ae16384..00000000 --- a/changelog.d/1563.change.rst +++ /dev/null @@ -1 +0,0 @@ -In ``pkg_resources`` prefer ``find_spec`` (PEP 451) to ``find_module``. diff --git a/changelog.d/308.change.rst b/changelog.d/308.change.rst deleted file mode 100644 index 5806d80e..00000000 --- a/changelog.d/308.change.rst +++ /dev/null @@ -1 +0,0 @@ -Allow version number normalization to be bypassed by wrapping in a 'setuptools.sic()' call. diff --git a/setup.cfg b/setup.cfg index d231a3e1..0e9a05ed 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 46.0.0 +version = 46.1.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 512565ee5090b8c1b685102c9ffb6195c99e32c2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 15:00:03 -0400 Subject: =?UTF-8?q?=F0=9F=91=B9=20Feed=20the=20hobgoblins=20(delint).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setuptools/command/build_py.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index 6fc0a4e4..bac4fb1c 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -120,7 +120,8 @@ class build_py(orig.build_py, Mixin2to3): target = os.path.join(build_dir, filename) self.mkpath(os.path.dirname(target)) srcfile = os.path.join(src_dir, filename) - outf, copied = self.copy_file(srcfile, target, preserve_mode=False) + outf, copied = self.copy_file( + srcfile, target, preserve_mode=False) srcfile = os.path.abspath(srcfile) if (copied and srcfile in self.distribution.convert_2to3_doctests): -- cgit v1.2.1 From 936a8c68afacd56d4bcdf8412fe0c9ed6fe4dc09 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 15:01:02 -0400 Subject: Update changelog. Ref #1424. --- changelog.d/1424.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1424.bugfix.rst diff --git a/changelog.d/1424.bugfix.rst b/changelog.d/1424.bugfix.rst new file mode 100644 index 00000000..2c7209f2 --- /dev/null +++ b/changelog.d/1424.bugfix.rst @@ -0,0 +1 @@ +Fixed lint error (long line) in #1424. -- cgit v1.2.1 From 87fbe76a1d544a14abea34ca0824096eb7c37b38 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 15:01:42 -0400 Subject: =?UTF-8?q?Bump=20version:=2046.1.0=20=E2=86=92=2046.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ setup.cfg | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 28a327b2..03fe7b43 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 46.1.0 +current_version = 46.1.1 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 12680a4d..1edbd12a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v46.1.1 +------- + +No significant changes. + + v46.1.0 ------- diff --git a/setup.cfg b/setup.cfg index 0e9a05ed..14426181 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 46.1.0 +version = 46.1.1 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 7bdef458b4b35e0a8fa7025266c7c1aa6b0f20c4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 15:06:44 -0400 Subject: Add badge for Azure pipelines --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index da0549a9..8a6bf91d 100644 --- a/README.rst +++ b/README.rst @@ -4,6 +4,9 @@ .. image:: https://img.shields.io/readthedocs/setuptools/latest.svg :target: https://setuptools.readthedocs.io +.. image:: https://dev.azure.com/jaraco/setuptools/_apis/build/status/jaraco.setuptools?branchName=master + :target: https://dev.azure.com/jaraco/setuptools/_build/latest?definitionId=1&branchName=master + .. image:: https://img.shields.io/travis/pypa/setuptools/master.svg?label=Linux%20CI&logo=travis&logoColor=white :target: https://travis-ci.org/pypa/setuptools -- cgit v1.2.1 From 5a1e4ea8a0671fc2e9d7559ae4e07ba1afe53d59 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 15:11:25 -0400 Subject: Solicit tidelift token from environment variables. Add Funding.yml. --- .github/FUNDING.yml | 1 + azure-pipelines.yml | 1 + 2 files changed, 2 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..27de01d0 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +tidelift: pypi/setuptools diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3e80bf44..0253a770 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -66,6 +66,7 @@ stages: tox -e release env: TWINE_PASSWORD: $(PyPI-token) + TIDELIFT_TOKEN: $(Tidelift-token) displayName: 'publish to PyPI' condition: contains(variables['Build.SourceBranch'], 'tags') -- cgit v1.2.1 From 487ec9bc067e823bfe8c725f15fed452b6e3adc6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 15:14:24 -0400 Subject: Sync badges with jaraco/skeleton --- README.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 8a6bf91d..6caaa756 100644 --- a/README.rst +++ b/README.rst @@ -1,8 +1,10 @@ .. image:: https://img.shields.io/pypi/v/setuptools.svg - :target: https://pypi.org/project/setuptools + :target: `PyPI link`_ -.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg - :target: https://setuptools.readthedocs.io +.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/setuptools .. image:: https://dev.azure.com/jaraco/setuptools/_apis/build/status/jaraco.setuptools?branchName=master :target: https://dev.azure.com/jaraco/setuptools/_build/latest?definitionId=1&branchName=master @@ -13,14 +15,15 @@ .. image:: https://img.shields.io/appveyor/ci/pypa/setuptools/master.svg?label=Windows%20CI&logo=appveyor&logoColor=white :target: https://ci.appveyor.com/project/pypa/setuptools/branch/master +.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg + :target: https://setuptools.readthedocs.io + .. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white :target: https://codecov.io/gh/pypa/setuptools .. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme -.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg - See the `Installation Instructions `_ in the Python Packaging User's Guide for instructions on installing, upgrading, and uninstalling -- cgit v1.2.1 From f63c4335178512ad1db730222d54fc0df84ae798 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 15:15:53 -0400 Subject: Remove deploy step from travis. Let Azure pipelines cut the release. --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 38a66f32..3e97f353 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,11 +21,6 @@ jobs: - python: 3.8-dev - <<: *latest_py3 env: TOXENV=docs DISABLE_COVERAGE=1 - - <<: *latest_py3 - stage: deploy - if: tag IS present - script: tox -e release - after_success: skip allow_failures: # suppress failures due to pypa/setuptools#2000 - python: pypy3 -- cgit v1.2.1 From e581a6186ffb7f00be82182543e833d7f0756510 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 21 Mar 2020 15:19:46 -0400 Subject: Remove changelog entry, intended for v46.1.1 --- changelog.d/1424.bugfix.rst | 1 - 1 file changed, 1 deletion(-) delete mode 100644 changelog.d/1424.bugfix.rst diff --git a/changelog.d/1424.bugfix.rst b/changelog.d/1424.bugfix.rst deleted file mode 100644 index 2c7209f2..00000000 --- a/changelog.d/1424.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed lint error (long line) in #1424. -- cgit v1.2.1 From 9781208e4c78509ae0e819bf3da472190c5a7bc0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 25 Mar 2020 10:51:10 -0400 Subject: Add template for Python 2 incompatibility --- ...uptools-warns-about-python-2-incompatibility.md | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md diff --git a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md new file mode 100644 index 00000000..4bed6c4f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md @@ -0,0 +1,48 @@ +--- +name: Setuptools warns about Python 2 incompatibility +about: Report the issue where setuptools 45 or later stops working on Python 2 +title: Incompatible install in (summarize your environment) +labels: Python 2 +assignees: '' + +--- + + + +## Prerequisites + + + +- [ ] Python 2 is required for this application. +- [ ] Setuptools installed with pip 9 or later. +- [ ] Pinning Setuptools to `setuptools<45` in the environment was unsuccessful. + +## Environment Details + +- Operating System and version: +- Python version: +- Python installed how: +- Virtualenv version (if using virtualenv): n/a + +Command(s) used to install setuptools (and output): + +``` +``` + +Output of `pip --version` when installing setuptools: + +``` +``` + +## Other notes -- cgit v1.2.1 From 734eec6e3fca725c6b18cad2fffbd1f3e3a56fa7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 25 Mar 2020 10:57:15 -0400 Subject: Update warning message to direct users to file a template instead of commenting in #1458. --- changelog.d/1458.misc.rst | 1 + pkg_resources/py2_warn.py | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 changelog.d/1458.misc.rst diff --git a/changelog.d/1458.misc.rst b/changelog.d/1458.misc.rst new file mode 100644 index 00000000..6a8e58a3 --- /dev/null +++ b/changelog.d/1458.misc.rst @@ -0,0 +1 @@ +Added template for reporting Python 2 incompatibilities. diff --git a/pkg_resources/py2_warn.py b/pkg_resources/py2_warn.py index 1b151956..bfc35234 100644 --- a/pkg_resources/py2_warn.py +++ b/pkg_resources/py2_warn.py @@ -12,9 +12,8 @@ msg = textwrap.dedent(""" Setuptools using pip 9.x or later or pin to `setuptools<45` in your environment. If you have done those things and are still encountering - this message, please comment in - https://github.com/pypa/setuptools/issues/1458 - about the steps that led to this unsupported combination. + this message, please follow up at + https://bit.ly/setuptools-py2-warning. """) pre = "Setuptools will stop working on Python 2\n" -- cgit v1.2.1 From 7355cce542963d9a83c7c754dc63a0c7e68bf3aa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 25 Mar 2020 11:05:13 -0400 Subject: Update py2 warning template to direct users to report downstream. --- .../ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md index 4bed6c4f..14317d93 100644 --- a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md +++ b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md @@ -25,6 +25,7 @@ Your first course of action should be to reason about how you managed to get an try them first. --> - [ ] Python 2 is required for this application. +- [ ] I maintain the software that installs Setuptools (if not, please contact that project). - [ ] Setuptools installed with pip 9 or later. - [ ] Pinning Setuptools to `setuptools<45` in the environment was unsuccessful. -- cgit v1.2.1 From db08c60c303d64459dae479af84e3cd1cd02ba1d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 25 Mar 2020 11:05:51 -0400 Subject: =?UTF-8?q?Bump=20version:=2046.1.1=20=E2=86=92=2046.1.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/1458.misc.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/1458.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 03fe7b43..4a54253c 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 46.1.1 +current_version = 46.1.2 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 1edbd12a..672355af 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v46.1.2 +------- + +* #1458: Added template for reporting Python 2 incompatibilities. + + v46.1.1 ------- diff --git a/changelog.d/1458.misc.rst b/changelog.d/1458.misc.rst deleted file mode 100644 index 6a8e58a3..00000000 --- a/changelog.d/1458.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Added template for reporting Python 2 incompatibilities. diff --git a/setup.cfg b/setup.cfg index 14426181..8347dede 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 46.1.1 +version = 46.1.2 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From a3620a45b3df9be32316bd8807ca5a83c1380c9a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 25 Mar 2020 13:29:50 -0400 Subject: Add test asserting that an executable script retains its executable bit. Ref #2041. --- setuptools/tests/test_build_py.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py index 92b455dd..4cb11c1d 100644 --- a/setuptools/tests/test_build_py.py +++ b/setuptools/tests/test_build_py.py @@ -26,9 +26,10 @@ def test_directories_in_package_data_glob(tmpdir_cwd): def test_read_only(tmpdir_cwd): """ - Ensure mode is not preserved in copy for package modules - and package data, as that causes problems - with deleting read-only files on Windows. + Ensure read-only flag is not preserved in copy + for package modules and package data, as that + causes problems with deleting read-only files on + Windows. #1451 """ @@ -47,3 +48,29 @@ def test_read_only(tmpdir_cwd): dist.parse_command_line() dist.run_commands() shutil.rmtree('build') + + +def test_executable_data(tmpdir_cwd): + """ + Ensure executable bit is preserved in copy for + package data, as users rely on it for scripts. + + #2041 + """ + dist = Distribution(dict( + script_name='setup.py', + script_args=['build_py'], + packages=['pkg'], + package_data={'pkg': ['run-me']}, + name='pkg', + )) + os.makedirs('pkg') + open('pkg/__init__.py', 'w').close() + open('pkg/run-me', 'w').close() + os.chmod('pkg/run-me', 0o700) + + dist.parse_command_line() + dist.run_commands() + + assert os.stat('build/lib/pkg/run-me').st_mode & stat.S_IEXEC, \ + "Script is not executable" -- cgit v1.2.1 From 70e95ee66a17e1655f70c9dbda107cea958f583f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 25 Mar 2020 13:44:58 -0400 Subject: When copying package data, make sure it's writable, but otherwise preserve the mode. Fixes #2041. --- setuptools/command/build_py.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index bac4fb1c..9d0288a5 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -7,6 +7,7 @@ import textwrap import io import distutils.errors import itertools +import stat from setuptools.extern import six from setuptools.extern.six.moves import map, filter, filterfalse @@ -20,6 +21,10 @@ except ImportError: "do nothing" +def make_writable(target): + os.chmod(target, os.stat(target).st_mode | stat.S_IWRITE) + + class build_py(orig.build_py, Mixin2to3): """Enhanced 'build_py' command that includes data files with packages @@ -120,8 +125,8 @@ class build_py(orig.build_py, Mixin2to3): target = os.path.join(build_dir, filename) self.mkpath(os.path.dirname(target)) srcfile = os.path.join(src_dir, filename) - outf, copied = self.copy_file( - srcfile, target, preserve_mode=False) + outf, copied = self.copy_file(srcfile, target) + make_writable(target) srcfile = os.path.abspath(srcfile) if (copied and srcfile in self.distribution.convert_2to3_doctests): -- cgit v1.2.1 From 8f8302da51016c6c98cf29417819fd0907e3993a Mon Sep 17 00:00:00 2001 From: jorikdima Date: Tue, 24 Mar 2020 20:21:35 -0700 Subject: Added description. --- changelog.d/2041.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2041.bugfix.rst diff --git a/changelog.d/2041.bugfix.rst b/changelog.d/2041.bugfix.rst new file mode 100644 index 00000000..8db757d8 --- /dev/null +++ b/changelog.d/2041.bugfix.rst @@ -0,0 +1 @@ +Preserve file modes during pkg files copying, but clear read only flag for target afterwards. -- cgit v1.2.1 From ef3a38644ccc9f65ea20493e25de0955695b9dff Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 25 Mar 2020 14:00:29 -0400 Subject: Mark test_executable_data as xfail on Windows. --- setuptools/tests/test_build_py.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py index 4cb11c1d..78a31ac4 100644 --- a/setuptools/tests/test_build_py.py +++ b/setuptools/tests/test_build_py.py @@ -2,6 +2,8 @@ import os import stat import shutil +import pytest + from setuptools.dist import Distribution @@ -50,6 +52,12 @@ def test_read_only(tmpdir_cwd): shutil.rmtree('build') +@pytest.mark.xfail( + 'platform.system() == "Windows"', + reason="On Windows, files do not have executable bits", + raises=AssertionError, + strict=True, +) def test_executable_data(tmpdir_cwd): """ Ensure executable bit is preserved in copy for -- cgit v1.2.1 From 3aeec3f0e989516e9229d9a75f5a038929dee6a6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 25 Mar 2020 14:27:44 -0400 Subject: =?UTF-8?q?Bump=20version:=2046.1.2=20=E2=86=92=2046.1.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ setup.cfg | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4a54253c..00cbdc7a 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 46.1.2 +current_version = 46.1.3 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 672355af..ac61c178 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v46.1.3 +------- + +No significant changes. + + v46.1.2 ------- diff --git a/setup.cfg b/setup.cfg index 8347dede..60838efc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 46.1.2 +version = 46.1.3 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 578d2eca7232d429225d7de55f934ab303ad6f3f Mon Sep 17 00:00:00 2001 From: alvyjudy Date: Sun, 29 Mar 2020 21:40:38 -0400 Subject: initial draft for build_meta documentation --- docs/build_meta.txt | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 docs/build_meta.txt diff --git a/docs/build_meta.txt b/docs/build_meta.txt new file mode 100644 index 00000000..bfba1181 --- /dev/null +++ b/docs/build_meta.txt @@ -0,0 +1,88 @@ +======================================= +Documentation to setuptools.build_meta +======================================= + +What is it? +------------- + +Setuptools, or Python packaging in general, has faced many +`criticism `_ and +`PEP517 `_ attempts to fix this +issue by ``get distutils-sig out of the business of being a gatekeeper for +Python build system``. + +A quick overview on the `current state of Python packaging +`_. The ``distutils`` +package specified the de facto standard way to bundle up your packages:: + + setup.py + mypkg/__init__.py + +And then you run ``python setup.py bdist`` and compressed ``.tar.gz`` will be +available for distribution. + +Following this tradition, several other enhancements have been made: ``pip`` +was created and user can run ``pip install`` on their downloaded distribution +file and it will be installed. ``wheel`` format was created to minimize the +build process for C extension. ``PyPI`` and ``twine`` then made it easier to +upload and download the distribution and finally ``setuptools`` extends the +original ``distutils`` and emcompass everything else and become the standard +way for Python packaging. (check the timeline for accuracy) + +I'll skip the many downsides and complexity that came with the development +of setuptools. PEP517 aims to solve these issues by specifying a new +standardized way to distribute your packages which is not as compatible as +the setuptools module. + +``build_meta.py`` therefore acts as an adaptor to the PEP517 and the existing +setuptools. + +How to use it? +------------- + +Starting with a package that you want to distribute. You will need your source +scripts, a ``pyproject.toml`` file and a ``setup.cfg`` file. + + ~/meowpkg/ + pyproject.toml + setup.cfg + src/meowpkg/__init__.py + + +The pyproject.toml file is required by PEP517 and PEP518 to specify the build +system (i.e. what is being used to package your scripts). To use it with +setuptools, the content would be:: + + [build-system] + requires = ["setuptools", "wheel"] + build-backend = "setuptools.build-meta" + +The setup.cfg is used to specify your package information (essentially +replacing setup.py), specified on setuptools `documentation `_ :: + + [metadata] + name = meowpkg + version = 0.0.1 + description = a package that meows + + [options] + package_dir= + =src + packages = find: + + [options.packages.find] + where=src + +Now it's time to actually generate the distribution. PEP517 specifies two +mandatory functions, ``build_wheel`` and ``build_sdist``, implemented in +this module. Currently, it has to be done in the interpreter:: + + >>> import setuptools.build_meta + >>> setuptools.build_meta.build_wheel('wheel_dist') + 'meowpkg-0.0.1-py3-none-any.whl' + +And now it's done! The ``.whl`` file and ``.tar.gz`` can then be distributed +and installed! + -- cgit v1.2.1 From cc7186cde4238e9269beb2b19720380990759299 Mon Sep 17 00:00:00 2001 From: alvyjudy Date: Mon, 30 Mar 2020 13:25:17 -0400 Subject: First draft for build_meta documentation --- docs/build_meta.txt | 98 +++++++++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 45 deletions(-) diff --git a/docs/build_meta.txt b/docs/build_meta.txt index bfba1181..47e0aa74 100644 --- a/docs/build_meta.txt +++ b/docs/build_meta.txt @@ -5,37 +5,39 @@ Documentation to setuptools.build_meta What is it? ------------- -Setuptools, or Python packaging in general, has faced many -`criticism `_ and -`PEP517 `_ attempts to fix this -issue by ``get distutils-sig out of the business of being a gatekeeper for -Python build system``. - -A quick overview on the `current state of Python packaging -`_. The ``distutils`` -package specified the de facto standard way to bundle up your packages:: - - setup.py - mypkg/__init__.py - -And then you run ``python setup.py bdist`` and compressed ``.tar.gz`` will be -available for distribution. - -Following this tradition, several other enhancements have been made: ``pip`` -was created and user can run ``pip install`` on their downloaded distribution -file and it will be installed. ``wheel`` format was created to minimize the -build process for C extension. ``PyPI`` and ``twine`` then made it easier to -upload and download the distribution and finally ``setuptools`` extends the -original ``distutils`` and emcompass everything else and become the standard -way for Python packaging. (check the timeline for accuracy) - -I'll skip the many downsides and complexity that came with the development -of setuptools. PEP517 aims to solve these issues by specifying a new -standardized way to distribute your packages which is not as compatible as -the setuptools module. - -``build_meta.py`` therefore acts as an adaptor to the PEP517 and the existing -setuptools. +Python packaging has come `a long way `_. + +The traditional ``setuptools``'s way of packgaging Python modules +uses a ``setup()`` function within the ``setup.py`` script. Commands such as +``python setup.py bdist`` or ``python setup.py bdist_wheel`` generate a +distribution bundle and ``python setup.py install`` installs the distribution. +This interface makes it difficult to choose other packaging tools without an +overhaul. Additionally, the ``setup.py`` scripts hasn't been the most user +friendly tool. + +PEP517 therefore came to rescue and specified a new standard to +package and distribute Python modules. Under PEP517: + + a ``pyproject.toml`` file is used to specify what program to use + for generating distribution. + + Then, two functions provided by the program,``build_wheel(directory: str)`` + and ``build_sdist(directory: str)`` create the distribution bundle at the + specified ``directory``. The program is free to use its own configuration + script or extend the ``.toml`` file. + + Lastly, ``pip install *.whl`` or ``pip install *.tar.gz`` does the actual + installation. If ``*.whl`` is available, ``pip`` will go ahead and copy + the files into ``site-packages`` directory. If not, ``pip`` will look at + ``pyproject.toml`` and decide what program to use to 'build from source' + (the default is ``setuptools``) + +With this standard, switching between packaging tools becomes a lot easier and +in the case of ``setuptools``, ``setup.py`` becomes optional. + +``build_meta`` is ``setuptools``'s implementation of PEP517. It provides the +two functions, ``build_wheel`` and ``build_sdist``, amongst others and uses +a ``setup.cfg`` to specify the information about the package. How to use it? ------------- @@ -46,21 +48,20 @@ scripts, a ``pyproject.toml`` file and a ``setup.cfg`` file. ~/meowpkg/ pyproject.toml setup.cfg - src/meowpkg/__init__.py + meowpkg/__init__.py -The pyproject.toml file is required by PEP517 and PEP518 to specify the build -system (i.e. what is being used to package your scripts). To use it with +The pyproject.toml file is required to specify the build system (i.e. what is +being used to package your scripts and install from source). To use it with setuptools, the content would be:: [build-system] requires = ["setuptools", "wheel"] - build-backend = "setuptools.build-meta" + build-backend = "setuptools.build_meta" -The setup.cfg is used to specify your package information (essentially -replacing setup.py), specified on setuptools `documentation `_ :: +``setup.cfg`` is used to specify your package information, specified +`here `_ :: [metadata] name = meowpkg @@ -68,13 +69,8 @@ using-setup-cfg-files>`_ :: description = a package that meows [options] - package_dir= - =src packages = find: - [options.packages.find] - where=src - Now it's time to actually generate the distribution. PEP517 specifies two mandatory functions, ``build_wheel`` and ``build_sdist``, implemented in this module. Currently, it has to be done in the interpreter:: @@ -82,7 +78,19 @@ this module. Currently, it has to be done in the interpreter:: >>> import setuptools.build_meta >>> setuptools.build_meta.build_wheel('wheel_dist') 'meowpkg-0.0.1-py3-none-any.whl' + >>> setuptools.build_meta.build_sdist('sdist') + 'meowpkg-0.0.1.tar.gz' And now it's done! The ``.whl`` file and ``.tar.gz`` can then be distributed -and installed! +and installed:: + + ~/newcomputer/ + meowpkg-0.0.1.whl + meowpkg-0.0.1.tar.gz + + $ pip install meowpkg-0.0.1.whl + +or:: + + $ pip install meowpkg-0.0.1.tar.gz -- cgit v1.2.1 From b5697f4458e0a2bbdb0d5a46222bb7e7adad8071 Mon Sep 17 00:00:00 2001 From: alvyjudy <53921639+alvyjudy@users.noreply.github.com> Date: Mon, 30 Mar 2020 13:27:31 -0400 Subject: deleted blank lines and fixed underline --- docs/build_meta.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/build_meta.txt b/docs/build_meta.txt index 47e0aa74..8d92d804 100644 --- a/docs/build_meta.txt +++ b/docs/build_meta.txt @@ -40,7 +40,7 @@ two functions, ``build_wheel`` and ``build_sdist``, amongst others and uses a ``setup.cfg`` to specify the information about the package. How to use it? -------------- +-------------- Starting with a package that you want to distribute. You will need your source scripts, a ``pyproject.toml`` file and a ``setup.cfg`` file. @@ -49,8 +49,7 @@ scripts, a ``pyproject.toml`` file and a ``setup.cfg`` file. pyproject.toml setup.cfg meowpkg/__init__.py - - + The pyproject.toml file is required to specify the build system (i.e. what is being used to package your scripts and install from source). To use it with setuptools, the content would be:: -- cgit v1.2.1 From 71af92d2e1f00162e46fe9c72370cad7e9bd0c5f Mon Sep 17 00:00:00 2001 From: alvyjudy <53921639+alvyjudy@users.noreply.github.com> Date: Mon, 30 Mar 2020 13:30:30 -0400 Subject: fixed some RST syntax --- docs/build_meta.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/build_meta.txt b/docs/build_meta.txt index 8d92d804..4467ddda 100644 --- a/docs/build_meta.txt +++ b/docs/build_meta.txt @@ -21,7 +21,7 @@ package and distribute Python modules. Under PEP517: a ``pyproject.toml`` file is used to specify what program to use for generating distribution. - Then, two functions provided by the program,``build_wheel(directory: str)`` + Then, two functions provided by the program, ``build_wheel(directory: str)`` and ``build_sdist(directory: str)`` create the distribution bundle at the specified ``directory``. The program is free to use its own configuration script or extend the ``.toml`` file. @@ -43,7 +43,7 @@ How to use it? -------------- Starting with a package that you want to distribute. You will need your source -scripts, a ``pyproject.toml`` file and a ``setup.cfg`` file. +scripts, a ``pyproject.toml`` file and a ``setup.cfg`` file:: ~/meowpkg/ pyproject.toml -- cgit v1.2.1 From 19e45c14e49fa3e6cc849b69b3a83f85f316441c Mon Sep 17 00:00:00 2001 From: alvyjudy Date: Mon, 30 Mar 2020 13:49:29 -0400 Subject: added changelog file --- changelog.d/1698.doc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1698.doc.rst diff --git a/changelog.d/1698.doc.rst b/changelog.d/1698.doc.rst new file mode 100644 index 00000000..9b61fccb --- /dev/null +++ b/changelog.d/1698.doc.rst @@ -0,0 +1 @@ +Added documentation for ``build_meta`` (a bare minimum, not completed) -- cgit v1.2.1 From c3ae6d416043ca528e9cf88c4789afa9df223c5b Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Fri, 3 Apr 2020 16:38:28 +0200 Subject: bpo-40112: distutils test_search_cpp: Fix logic to determine if C compiler is xlc on AIX (GH-19225) --- tests/test_config_cmd.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index b735fd33..8bd2c942 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -47,8 +47,7 @@ class ConfigTestCase(support.LoggingSilencer, cmd = config(dist) cmd._check_compiler() compiler = cmd.compiler - is_xlc = shutil.which(compiler.preprocessor[0]).startswith("/usr/vac") - if is_xlc: + if sys.platform[:3] == "aix" and "xlc" in compiler.preprocessor[0].lower(): self.skipTest('xlc: The -E option overrides the -P, -o, and -qsyntaxonly options') # simple pattern searches -- cgit v1.2.1 From 03b92ca154951f16851e75b01b78b9127dad742f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 6 Apr 2020 16:33:15 -0400 Subject: Protect against situation where the Git user e-mail is not configured. Ref #2057. --- tools/finalize.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/finalize.py b/tools/finalize.py index 3b66341a..5f284568 100644 --- a/tools/finalize.py +++ b/tools/finalize.py @@ -53,7 +53,15 @@ def bump_version(): subprocess.check_call(cmd) +def ensure_config(): + """ + Double-check that Git has an e-mail configured. + """ + subprocess.check_output(['git', 'config', 'user.email']) + + if __name__ == '__main__': print("Cutting release at", get_version()) + ensure_config() update_changelog() bump_version() -- cgit v1.2.1 From 53c284b325959a6fd177e9d37d8b5dd5824db2b3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 6 Apr 2020 16:35:56 -0400 Subject: Pass environment variables when running finalize. Fixes #2057. --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 347106ec..aa99e283 100644 --- a/tox.ini +++ b/tox.ini @@ -65,6 +65,7 @@ skip_install = True deps = towncrier bump2version +passenv = * commands = python tools/finalize.py -- cgit v1.2.1 From e5f3305933e65fbebe1562a3625cf16abb10a10b Mon Sep 17 00:00:00 2001 From: Reece Dunham Date: Sat, 11 Apr 2020 18:11:31 +0000 Subject: change: Mac OS X -> macOS Signed-off-by: Reece Dunham --- .gitignore | 1 + CHANGES.rst | 10 +++++----- docs/history.txt | 2 +- docs/pkg_resources.txt | 6 +++--- pkg_resources/__init__.py | 26 +++++++++++++------------- pkg_resources/_vendor/appdirs.py | 21 ++++++++++----------- pkg_resources/api_tests.txt | 4 ++-- setuptools/package_index.py | 2 +- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 0c272d1c..90ae8050 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ setuptools.egg-info .cache .idea/ .pytest_cache/ +.mypy_cache/ diff --git a/CHANGES.rst b/CHANGES.rst index f97f5142..201ac692 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1461,7 +1461,7 @@ v21.1.0 * #572: In build_ext, now always import ``_CONFIG_VARS`` from ``distutils`` rather than from ``sysconfig`` to allow ``distutils.sysconfig.customize_compiler`` - configure the OS X compiler for ``-dynamiclib``. + configure the macOS compiler for ``-dynamiclib``. v21.0.0 ------- @@ -1881,7 +1881,7 @@ v20.6.0 require that Cython be present before building source distributions. However, for systems with this build of setuptools, Cython will be downloaded on demand. -* Issue #396: Fixed test failure on OS X. +* Issue #396: Fixed test failure on macOS. * BB Pull Request #136: Remove excessive quoting from shebang headers for Jython. @@ -3078,7 +3078,7 @@ how it parses version numbers. * Distribute #306: Even if 2to3 is used, we build in-place under Python 2. * Distribute #307: Prints the full path when .svn/entries is broken. * Distribute #313: Support for sdist subcommands (Python 2.7) -* Distribute #314: test_local_index() would fail an OS X. +* Distribute #314: test_local_index() would fail an macOS. * Distribute #310: Non-ascii characters in a namespace __init__.py causes errors. * Distribute #218: Improved documentation on behavior of `package_data` and `include_package_data`. Files indicated by `package_data` are now included @@ -4105,7 +4105,7 @@ easy_install based on a contribution by Kevin Dangoor. You may wish to delete and reinstall any eggs whose filename includes "darwin" and "Power_Macintosh", because the format for this platform information has changed so that minor - OS X upgrades (such as 10.4.1 to 10.4.2) do not cause eggs built with a + macOS upgrades (such as 10.4.1 to 10.4.2) do not cause eggs built with a previous OS version to become obsolete. * easy_install's dependency processing algorithms have changed. When using @@ -4118,7 +4118,7 @@ easy_install * Added ``--site-dirs`` option to allow adding custom "site" directories. Made ``easy-install.pth`` work in platform-specific alternate site - directories (e.g. ``~/Library/Python/2.x/site-packages`` on Mac OS X). + directories (e.g. ``~/Library/Python/2.x/site-packages`` on macOS). * If you manually delete the current version of a package, the next run of EasyInstall against the target directory will now remove the stray entry diff --git a/docs/history.txt b/docs/history.txt index 385cfa7e..faf7adfe 100644 --- a/docs/history.txt +++ b/docs/history.txt @@ -12,7 +12,7 @@ Credits * The original design for the ``.egg`` format and the ``pkg_resources`` API was co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first - version of ``pkg_resources``, and supplied the OS X operating system version + version of ``pkg_resources``, and supplied the macOS operating system version compatibility algorithm. * Ian Bicking implemented many early "creature comfort" features of diff --git a/docs/pkg_resources.txt b/docs/pkg_resources.txt index b887a923..71568c1a 100644 --- a/docs/pkg_resources.txt +++ b/docs/pkg_resources.txt @@ -1621,7 +1621,7 @@ Platform Utilities ``get_build_platform()`` Return this platform's identifier string. For Windows, the return value - is ``"win32"``, and for Mac OS X it is a string of the form + is ``"win32"``, and for macOS it is a string of the form ``"macosx-10.4-ppc"``. All other platforms return the same uname-based string that the ``distutils.util.get_platform()`` function returns. This string is the minimum platform version required by distributions built @@ -1641,7 +1641,7 @@ Platform Utilities considered a wildcard, and the platforms are therefore compatible. Likewise, if the platform strings are equal, they're also considered compatible, and ``True`` is returned. Currently, the only non-equal - platform strings that are considered compatible are Mac OS X platform + platform strings that are considered compatible are macOS platform strings with the same hardware type (e.g. ``ppc``) and major version (e.g. ``10``) with the `provided` platform's minor version being less than or equal to the `required` platform's minor version. @@ -1674,7 +1674,7 @@ File/Path Utilities the same filesystem location if they have equal ``normalized_path()`` values. Specifically, this is a shortcut for calling ``os.path.realpath`` and ``os.path.normcase`` on `path`. Unfortunately, on certain platforms - (notably Cygwin and Mac OS X) the ``normcase`` function does not accurately + (notably Cygwin and macOS) the ``normcase`` function does not accurately reflect the platform's case-sensitivity, so there is always the possibility of two apparently-different paths being equal on such platforms. diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 75563f95..e7f863b1 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -179,10 +179,10 @@ def get_supported_platform(): """Return this platform's maximum compatible version. distutils.util.get_platform() normally reports the minimum version - of Mac OS X that would be required to *use* extensions produced by + of macOS that would be required to *use* extensions produced by distutils. But what we want when checking compatibility is to know the - version of Mac OS X that we are *running*. To allow usage of packages that - explicitly require a newer version of Mac OS X, we must also know the + version of macOS that we are *running*. To allow usage of packages that + explicitly require a newer version of macOS, we must also know the current version of the OS. If this condition occurs for any other platform with a version in its @@ -192,9 +192,9 @@ def get_supported_platform(): m = macosVersionString.match(plat) if m is not None and sys.platform == "darwin": try: - plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) + plat = 'macosx-%s-%s' % ('.'.join(_macos_vers()[:2]), m.group(3)) except ValueError: - # not Mac OS X + # not macOS pass return plat @@ -365,7 +365,7 @@ def get_provider(moduleOrReq): return _find_adapter(_provider_factories, loader)(module) -def _macosx_vers(_cache=[]): +def _macos_vers(_cache=[]): if not _cache: version = platform.mac_ver()[0] # fallback for MacPorts @@ -381,7 +381,7 @@ def _macosx_vers(_cache=[]): return _cache[0] -def _macosx_arch(machine): +def _macos_arch(machine): return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) @@ -389,18 +389,18 @@ def get_build_platform(): """Return this platform's string for platform-specific distributions XXX Currently this is the same as ``distutils.util.get_platform()``, but it - needs some hacks for Linux and Mac OS X. + needs some hacks for Linux and macOS. """ from sysconfig import get_platform plat = get_platform() if sys.platform == "darwin" and not plat.startswith('macosx-'): try: - version = _macosx_vers() + version = _macos_vers() machine = os.uname()[4].replace(" ", "_") return "macosx-%d.%d-%s" % ( int(version[0]), int(version[1]), - _macosx_arch(machine), + _macos_arch(machine), ) except ValueError: # if someone is running a non-Mac darwin system, this will fall @@ -426,7 +426,7 @@ def compatible_platforms(provided, required): # easy case return True - # Mac OS X special cases + # macOS special cases reqMac = macosVersionString.match(required) if reqMac: provMac = macosVersionString.match(provided) @@ -435,7 +435,7 @@ def compatible_platforms(provided, required): if not provMac: # this is backwards compatibility for packages built before # setuptools 0.6. All packages built after this point will - # use the new macosx designation. + # use the new macOS designation. provDarwin = darwinVersionString.match(provided) if provDarwin: dversion = int(provDarwin.group(1)) @@ -443,7 +443,7 @@ def compatible_platforms(provided, required): if dversion == 7 and macosversion >= "10.3" or \ dversion == 8 and macosversion >= "10.4": return True - # egg isn't macosx or legacy darwin + # egg isn't macOS or legacy darwin return False # are they the same major version and machine type? diff --git a/pkg_resources/_vendor/appdirs.py b/pkg_resources/_vendor/appdirs.py index ae67001a..4552cbbf 100644 --- a/pkg_resources/_vendor/appdirs.py +++ b/pkg_resources/_vendor/appdirs.py @@ -8,10 +8,9 @@ See for details and usage. """ # Dev Notes: -# - MSDN on where to store app data files: -# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 -# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html -# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html +# - MSDN on where to store app data files: (TODO: needs new link) +# - macOS: (TODO: needs new link) +# - XDG spec for Un*x: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html __version_info__ = (1, 4, 3) __version__ = '.'.join(map(str, __version_info__)) @@ -64,7 +63,7 @@ def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): for a discussion of issues. Typical user data directories are: - Mac OS X: ~/Library/Application Support/ + macOS: ~/Library/Application Support/ Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined Win XP (not roaming): C:\Documents and Settings\\Application Data\\ Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ @@ -118,7 +117,7 @@ def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): if XDG_DATA_DIRS is not set Typical site data directories are: - Mac OS X: /Library/Application Support/ + macOS: /Library/Application Support/ Unix: /usr/local/share/ or /usr/share/ Win XP: C:\Documents and Settings\All Users\Application Data\\ Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) @@ -185,7 +184,7 @@ def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): for a discussion of issues. Typical user config directories are: - Mac OS X: same as user_data_dir + macOS: same as user_data_dir Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined Win *: same as user_data_dir @@ -223,7 +222,7 @@ def site_config_dir(appname=None, appauthor=None, version=None, multipath=False) returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set Typical site config directories are: - Mac OS X: same as site_data_dir + macOS: same as site_data_dir Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in $XDG_CONFIG_DIRS Win *: same as site_data_dir @@ -273,7 +272,7 @@ def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): discussion below. Typical user cache directories are: - Mac OS X: ~/Library/Caches/ + macOS: ~/Library/Caches/ Unix: ~/.cache/ (XDG default) Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache Vista: C:\Users\\AppData\Local\\\Cache @@ -333,7 +332,7 @@ def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): for a discussion of issues. Typical user state directories are: - Mac OS X: same as user_data_dir + macOS: same as user_data_dir Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined Win *: same as user_data_dir @@ -372,7 +371,7 @@ def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): base cache dir for Unix. See discussion below. Typical user log directories are: - Mac OS X: ~/Library/Logs/ + macOS: ~/Library/Logs/ Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs Vista: C:\Users\\AppData\Local\\\Logs diff --git a/pkg_resources/api_tests.txt b/pkg_resources/api_tests.txt index 7ae5a038..ded18800 100644 --- a/pkg_resources/api_tests.txt +++ b/pkg_resources/api_tests.txt @@ -290,8 +290,8 @@ Platform Compatibility Rules ---------------------------- On the Mac, there are potential compatibility issues for modules compiled -on newer versions of Mac OS X than what the user is running. Additionally, -Mac OS X will soon have two platforms to contend with: Intel and PowerPC. +on newer versions of macOS than what the user is running. Additionally, +macOS will soon have two platforms to contend with: Intel and PowerPC. Basic equality works as on other platforms:: diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 82eb4516..91806091 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -1051,7 +1051,7 @@ def open_with_auth(url, opener=urllib.request.urlopen): parsed = urllib.parse.urlparse(url) scheme, netloc, path, params, query, frag = parsed - # Double scheme does not raise on Mac OS X as revealed by a + # Double scheme does not raise on macOS as revealed by a # failing test. We would expect "nonnumeric port". Refs #20. if netloc.endswith(':'): raise http_client.InvalidURL("nonnumeric port: ''") -- cgit v1.2.1 From bafe5abd8c715fa18a06bfd62e685802a2a74ce8 Mon Sep 17 00:00:00 2001 From: Reece Dunham Date: Sat, 11 Apr 2020 18:11:31 +0000 Subject: change: Mac OS X -> macOS Signed-off-by: Reece Dunham --- .gitignore | 1 + CHANGES.rst | 10 +++++----- docs/history.txt | 2 +- docs/pkg_resources.txt | 6 +++--- pkg_resources/__init__.py | 26 +++++++++++++------------- pkg_resources/_vendor/appdirs.py | 21 ++++++++++----------- pkg_resources/api_tests.txt | 4 ++-- setuptools/package_index.py | 2 +- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 0c272d1c..90ae8050 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ setuptools.egg-info .cache .idea/ .pytest_cache/ +.mypy_cache/ diff --git a/CHANGES.rst b/CHANGES.rst index ac61c178..c145eb39 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1519,7 +1519,7 @@ v21.1.0 * #572: In build_ext, now always import ``_CONFIG_VARS`` from ``distutils`` rather than from ``sysconfig`` to allow ``distutils.sysconfig.customize_compiler`` - configure the OS X compiler for ``-dynamiclib``. + configure the macOS compiler for ``-dynamiclib``. v21.0.0 ------- @@ -1939,7 +1939,7 @@ v20.6.0 require that Cython be present before building source distributions. However, for systems with this build of setuptools, Cython will be downloaded on demand. -* Issue #396: Fixed test failure on OS X. +* Issue #396: Fixed test failure on macOS. * BB Pull Request #136: Remove excessive quoting from shebang headers for Jython. @@ -3136,7 +3136,7 @@ how it parses version numbers. * Distribute #306: Even if 2to3 is used, we build in-place under Python 2. * Distribute #307: Prints the full path when .svn/entries is broken. * Distribute #313: Support for sdist subcommands (Python 2.7) -* Distribute #314: test_local_index() would fail an OS X. +* Distribute #314: test_local_index() would fail an macOS. * Distribute #310: Non-ascii characters in a namespace __init__.py causes errors. * Distribute #218: Improved documentation on behavior of `package_data` and `include_package_data`. Files indicated by `package_data` are now included @@ -4163,7 +4163,7 @@ easy_install based on a contribution by Kevin Dangoor. You may wish to delete and reinstall any eggs whose filename includes "darwin" and "Power_Macintosh", because the format for this platform information has changed so that minor - OS X upgrades (such as 10.4.1 to 10.4.2) do not cause eggs built with a + macOS upgrades (such as 10.4.1 to 10.4.2) do not cause eggs built with a previous OS version to become obsolete. * easy_install's dependency processing algorithms have changed. When using @@ -4176,7 +4176,7 @@ easy_install * Added ``--site-dirs`` option to allow adding custom "site" directories. Made ``easy-install.pth`` work in platform-specific alternate site - directories (e.g. ``~/Library/Python/2.x/site-packages`` on Mac OS X). + directories (e.g. ``~/Library/Python/2.x/site-packages`` on macOS). * If you manually delete the current version of a package, the next run of EasyInstall against the target directory will now remove the stray entry diff --git a/docs/history.txt b/docs/history.txt index 385cfa7e..faf7adfe 100644 --- a/docs/history.txt +++ b/docs/history.txt @@ -12,7 +12,7 @@ Credits * The original design for the ``.egg`` format and the ``pkg_resources`` API was co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first - version of ``pkg_resources``, and supplied the OS X operating system version + version of ``pkg_resources``, and supplied the macOS operating system version compatibility algorithm. * Ian Bicking implemented many early "creature comfort" features of diff --git a/docs/pkg_resources.txt b/docs/pkg_resources.txt index b887a923..71568c1a 100644 --- a/docs/pkg_resources.txt +++ b/docs/pkg_resources.txt @@ -1621,7 +1621,7 @@ Platform Utilities ``get_build_platform()`` Return this platform's identifier string. For Windows, the return value - is ``"win32"``, and for Mac OS X it is a string of the form + is ``"win32"``, and for macOS it is a string of the form ``"macosx-10.4-ppc"``. All other platforms return the same uname-based string that the ``distutils.util.get_platform()`` function returns. This string is the minimum platform version required by distributions built @@ -1641,7 +1641,7 @@ Platform Utilities considered a wildcard, and the platforms are therefore compatible. Likewise, if the platform strings are equal, they're also considered compatible, and ``True`` is returned. Currently, the only non-equal - platform strings that are considered compatible are Mac OS X platform + platform strings that are considered compatible are macOS platform strings with the same hardware type (e.g. ``ppc``) and major version (e.g. ``10``) with the `provided` platform's minor version being less than or equal to the `required` platform's minor version. @@ -1674,7 +1674,7 @@ File/Path Utilities the same filesystem location if they have equal ``normalized_path()`` values. Specifically, this is a shortcut for calling ``os.path.realpath`` and ``os.path.normcase`` on `path`. Unfortunately, on certain platforms - (notably Cygwin and Mac OS X) the ``normcase`` function does not accurately + (notably Cygwin and macOS) the ``normcase`` function does not accurately reflect the platform's case-sensitivity, so there is always the possibility of two apparently-different paths being equal on such platforms. diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 15a4401a..b0a49d86 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -179,10 +179,10 @@ def get_supported_platform(): """Return this platform's maximum compatible version. distutils.util.get_platform() normally reports the minimum version - of Mac OS X that would be required to *use* extensions produced by + of macOS that would be required to *use* extensions produced by distutils. But what we want when checking compatibility is to know the - version of Mac OS X that we are *running*. To allow usage of packages that - explicitly require a newer version of Mac OS X, we must also know the + version of macOS that we are *running*. To allow usage of packages that + explicitly require a newer version of macOS, we must also know the current version of the OS. If this condition occurs for any other platform with a version in its @@ -192,9 +192,9 @@ def get_supported_platform(): m = macosVersionString.match(plat) if m is not None and sys.platform == "darwin": try: - plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) + plat = 'macosx-%s-%s' % ('.'.join(_macos_vers()[:2]), m.group(3)) except ValueError: - # not Mac OS X + # not macOS pass return plat @@ -365,7 +365,7 @@ def get_provider(moduleOrReq): return _find_adapter(_provider_factories, loader)(module) -def _macosx_vers(_cache=[]): +def _macos_vers(_cache=[]): if not _cache: version = platform.mac_ver()[0] # fallback for MacPorts @@ -381,7 +381,7 @@ def _macosx_vers(_cache=[]): return _cache[0] -def _macosx_arch(machine): +def _macos_arch(machine): return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) @@ -389,18 +389,18 @@ def get_build_platform(): """Return this platform's string for platform-specific distributions XXX Currently this is the same as ``distutils.util.get_platform()``, but it - needs some hacks for Linux and Mac OS X. + needs some hacks for Linux and macOS. """ from sysconfig import get_platform plat = get_platform() if sys.platform == "darwin" and not plat.startswith('macosx-'): try: - version = _macosx_vers() + version = _macos_vers() machine = os.uname()[4].replace(" ", "_") return "macosx-%d.%d-%s" % ( int(version[0]), int(version[1]), - _macosx_arch(machine), + _macos_arch(machine), ) except ValueError: # if someone is running a non-Mac darwin system, this will fall @@ -426,7 +426,7 @@ def compatible_platforms(provided, required): # easy case return True - # Mac OS X special cases + # macOS special cases reqMac = macosVersionString.match(required) if reqMac: provMac = macosVersionString.match(provided) @@ -435,7 +435,7 @@ def compatible_platforms(provided, required): if not provMac: # this is backwards compatibility for packages built before # setuptools 0.6. All packages built after this point will - # use the new macosx designation. + # use the new macOS designation. provDarwin = darwinVersionString.match(provided) if provDarwin: dversion = int(provDarwin.group(1)) @@ -443,7 +443,7 @@ def compatible_platforms(provided, required): if dversion == 7 and macosversion >= "10.3" or \ dversion == 8 and macosversion >= "10.4": return True - # egg isn't macosx or legacy darwin + # egg isn't macOS or legacy darwin return False # are they the same major version and machine type? diff --git a/pkg_resources/_vendor/appdirs.py b/pkg_resources/_vendor/appdirs.py index ae67001a..4552cbbf 100644 --- a/pkg_resources/_vendor/appdirs.py +++ b/pkg_resources/_vendor/appdirs.py @@ -8,10 +8,9 @@ See for details and usage. """ # Dev Notes: -# - MSDN on where to store app data files: -# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 -# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html -# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html +# - MSDN on where to store app data files: (TODO: needs new link) +# - macOS: (TODO: needs new link) +# - XDG spec for Un*x: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html __version_info__ = (1, 4, 3) __version__ = '.'.join(map(str, __version_info__)) @@ -64,7 +63,7 @@ def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): for a discussion of issues. Typical user data directories are: - Mac OS X: ~/Library/Application Support/ + macOS: ~/Library/Application Support/ Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined Win XP (not roaming): C:\Documents and Settings\\Application Data\\ Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ @@ -118,7 +117,7 @@ def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): if XDG_DATA_DIRS is not set Typical site data directories are: - Mac OS X: /Library/Application Support/ + macOS: /Library/Application Support/ Unix: /usr/local/share/ or /usr/share/ Win XP: C:\Documents and Settings\All Users\Application Data\\ Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) @@ -185,7 +184,7 @@ def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): for a discussion of issues. Typical user config directories are: - Mac OS X: same as user_data_dir + macOS: same as user_data_dir Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined Win *: same as user_data_dir @@ -223,7 +222,7 @@ def site_config_dir(appname=None, appauthor=None, version=None, multipath=False) returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set Typical site config directories are: - Mac OS X: same as site_data_dir + macOS: same as site_data_dir Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in $XDG_CONFIG_DIRS Win *: same as site_data_dir @@ -273,7 +272,7 @@ def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): discussion below. Typical user cache directories are: - Mac OS X: ~/Library/Caches/ + macOS: ~/Library/Caches/ Unix: ~/.cache/ (XDG default) Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache Vista: C:\Users\\AppData\Local\\\Cache @@ -333,7 +332,7 @@ def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): for a discussion of issues. Typical user state directories are: - Mac OS X: same as user_data_dir + macOS: same as user_data_dir Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined Win *: same as user_data_dir @@ -372,7 +371,7 @@ def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): base cache dir for Unix. See discussion below. Typical user log directories are: - Mac OS X: ~/Library/Logs/ + macOS: ~/Library/Logs/ Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs Vista: C:\Users\\AppData\Local\\\Logs diff --git a/pkg_resources/api_tests.txt b/pkg_resources/api_tests.txt index 7ae5a038..ded18800 100644 --- a/pkg_resources/api_tests.txt +++ b/pkg_resources/api_tests.txt @@ -290,8 +290,8 @@ Platform Compatibility Rules ---------------------------- On the Mac, there are potential compatibility issues for modules compiled -on newer versions of Mac OS X than what the user is running. Additionally, -Mac OS X will soon have two platforms to contend with: Intel and PowerPC. +on newer versions of macOS than what the user is running. Additionally, +macOS will soon have two platforms to contend with: Intel and PowerPC. Basic equality works as on other platforms:: diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 7a802413..0744ea2a 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -1053,7 +1053,7 @@ def open_with_auth(url, opener=urllib.request.urlopen): parsed = urllib.parse.urlparse(url) scheme, netloc, path, params, query, frag = parsed - # Double scheme does not raise on Mac OS X as revealed by a + # Double scheme does not raise on macOS as revealed by a # failing test. We would expect "nonnumeric port". Refs #20. if netloc.endswith(':'): raise http_client.InvalidURL("nonnumeric port: ''") -- cgit v1.2.1 From 63b03e3610303163727308152590bcf78d5dc439 Mon Sep 17 00:00:00 2001 From: Reece Dunham Date: Sat, 11 Apr 2020 18:16:22 +0000 Subject: news fragment --- changelog.d/2062.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2062.change.rst diff --git a/changelog.d/2062.change.rst b/changelog.d/2062.change.rst new file mode 100644 index 00000000..1f5fd812 --- /dev/null +++ b/changelog.d/2062.change.rst @@ -0,0 +1 @@ +Change 'Mac OS X' to 'macOS' in code. -- cgit v1.2.1 From 9bfc5fa5fd0da9254c2e95edb42da3fb65893f75 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 16 Feb 2020 16:07:02 +0200 Subject: Deprecate bdist_wininst --- setuptools/command/bdist_wininst.py | 9 +++++++++ setuptools/tests/test_bdist_deprecations.py | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 setuptools/tests/test_bdist_deprecations.py diff --git a/setuptools/command/bdist_wininst.py b/setuptools/command/bdist_wininst.py index 073de97b..ff4b6345 100644 --- a/setuptools/command/bdist_wininst.py +++ b/setuptools/command/bdist_wininst.py @@ -1,4 +1,7 @@ import distutils.command.bdist_wininst as orig +import warnings + +from setuptools import SetuptoolsDeprecationWarning class bdist_wininst(orig.bdist_wininst): @@ -14,6 +17,12 @@ class bdist_wininst(orig.bdist_wininst): return cmd def run(self): + warnings.warn( + "bdist_wininst is deprecated and will be removed in a future " + "version. Use bdist_wheel (wheel packages) instead.", + SetuptoolsDeprecationWarning + ) + self._is_running = True try: orig.bdist_wininst.run(self) diff --git a/setuptools/tests/test_bdist_deprecations.py b/setuptools/tests/test_bdist_deprecations.py new file mode 100644 index 00000000..704164aa --- /dev/null +++ b/setuptools/tests/test_bdist_deprecations.py @@ -0,0 +1,23 @@ +"""develop tests +""" +import mock + +import pytest + +from setuptools.dist import Distribution +from setuptools import SetuptoolsDeprecationWarning + + +@mock.patch("distutils.command.bdist_wininst.bdist_wininst") +def test_bdist_wininst_warning(distutils_cmd): + dist = Distribution(dict( + script_name='setup.py', + script_args=['bdist_wininst'], + name='foo', + py_modules=['hi'], + )) + dist.parse_command_line() + with pytest.warns(SetuptoolsDeprecationWarning): + dist.run_commands() + + distutils_cmd.run.assert_called_once() -- cgit v1.2.1 From 8487f1ae61f50578d9adb314429eb578e8e9f0c9 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 22 Mar 2020 20:53:33 +0200 Subject: Add news fragment --- changelog.d/2040.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2040.change.rst diff --git a/changelog.d/2040.change.rst b/changelog.d/2040.change.rst new file mode 100644 index 00000000..b8f36f6f --- /dev/null +++ b/changelog.d/2040.change.rst @@ -0,0 +1 @@ +Deprecated the ``bdist_wininst`` command. Binary packages should be built as wheels instead. -- cgit v1.2.1 From 74e9f56dc7857cee680727406517c2c086511e12 Mon Sep 17 00:00:00 2001 From: Joshua Root Date: Wed, 22 Apr 2020 17:44:10 +1000 Subject: bpo-38360: macOS: support alternate form of -isysroot flag (GH-16480) It is possible to use either '-isysroot /some/path' (with a space) or '-isysroot/some/path' (no space in between). Support both forms in places where special handling of -isysroot is done, rather than just the first form. Co-authored-by: Ned Deily --- unixccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index d10a78da..4d7a6de7 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -288,7 +288,7 @@ class UnixCCompiler(CCompiler): # vs # /usr/lib/libedit.dylib cflags = sysconfig.get_config_var('CFLAGS') - m = re.search(r'-isysroot\s+(\S+)', cflags) + m = re.search(r'-isysroot\s*(\S+)', cflags) if m is None: sysroot = '/' else: -- cgit v1.2.1 From d11428b1cc059ff6aa0ddec0bd8354a1df68d752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Fri, 24 Apr 2020 22:34:10 +0700 Subject: Stop recognizing files ending with .dist-info as dist As proposed in PEP 376, dist-info distributions must be directories. --- changelog.d/2075.breaking.rst | 1 + pkg_resources/__init__.py | 30 ++++++++++++++---------------- pkg_resources/tests/test_pkg_resources.py | 8 ++++++++ 3 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 changelog.d/2075.breaking.rst diff --git a/changelog.d/2075.breaking.rst b/changelog.d/2075.breaking.rst new file mode 100644 index 00000000..8d88b33f --- /dev/null +++ b/changelog.d/2075.breaking.rst @@ -0,0 +1 @@ +Stop recognizing files ending with ``.dist-info`` as distribution metadata diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 15a4401a..4d15086f 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2056,33 +2056,31 @@ def find_on_path(importer, path_item, only=False): filtered = ( entry for entry in entries - if dist_factory(path_item, entry, only) + if dist_factory(entry, only) ) # scan for .egg and .egg-info in directory path_item_entries = _by_version_descending(filtered) for entry in path_item_entries: fullpath = os.path.join(path_item, entry) - factory = dist_factory(path_item, entry, only) + factory = dist_factory(entry, only) for dist in factory(fullpath): yield dist -def dist_factory(path_item, entry, only): - """ - Return a dist_factory for a path_item and entry - """ +def dist_factory(entry, only): + """Return a dist_factory for the given entry.""" lower = entry.lower() - is_meta = any(map(lower.endswith, ('.egg-info', '.dist-info'))) - return ( - distributions_from_metadata - if is_meta else - find_distributions - if not only and _is_egg_path(entry) else - resolve_egg_link - if not only and lower.endswith('.egg-link') else - NoDists() - ) + if lower.endswith('.egg-info'): + return distributions_from_metadata + elif lower.endswith('.dist-info') and os.path.isdir(entry): + return distributions_from_metadata + elif not only and _is_egg_path(entry): + return find_distributions + elif not only and lower.endswith('.egg-link'): + return resolve_egg_link + else: + return NoDists() class NoDists: diff --git a/pkg_resources/tests/test_pkg_resources.py b/pkg_resources/tests/test_pkg_resources.py index 78281869..1c66dec0 100644 --- a/pkg_resources/tests/test_pkg_resources.py +++ b/pkg_resources/tests/test_pkg_resources.py @@ -330,6 +330,14 @@ def test_distribution_version_missing_undetected_path(): assert msg == expected +@pytest.mark.parametrize('only', [False, True]) +def test_dist_info_is_not_dir(tmp_path, only): + """Test path containing a file with dist-info extension.""" + dist_info = tmp_path / 'foobar.dist-info' + dist_info.touch() + assert not pkg_resources.dist_factory(str(dist_info), only) + + class TestDeepVersionLookupDistutils: @pytest.fixture def env(self, tmpdir): -- cgit v1.2.1 From ea19518aa139d623a4fad7d01159674ee43b9f15 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Apr 2020 02:21:30 +0200 Subject: bpo-40443: Remove unused imports in tests (GH-19804) --- tests/test_build_clib.py | 1 - tests/test_config_cmd.py | 1 - tests/test_dist.py | 2 +- tests/test_spawn.py | 3 +-- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py index 85d09906..abd83137 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -8,7 +8,6 @@ from test.support import run_unittest, missing_compiler_executable from distutils.command.build_clib import build_clib from distutils.errors import DistutilsSetupError from distutils.tests import support -from distutils.spawn import find_executable class BuildCLibTestCase(support.TempdirManager, support.LoggingSilencer, diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py index 8bd2c942..9aeab07b 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -39,7 +39,6 @@ class ConfigTestCase(support.LoggingSilencer, @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_search_cpp(self): - import shutil cmd = missing_compiler_executable(['preprocessor']) if cmd is not None: self.skipTest('The %r command is not found' % cmd) diff --git a/tests/test_dist.py b/tests/test_dist.py index cc34725a..60956dad 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,7 +8,7 @@ import textwrap from unittest import mock -from distutils.dist import Distribution, fix_help_options, DistributionMetadata +from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command from test.support import ( diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 73b0f5cb..cf1faad5 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -2,8 +2,7 @@ import os import stat import sys -import unittest -from unittest import mock +import unittest.mock from test.support import run_unittest, unix_shell from test import support as test_support -- cgit v1.2.1 From b36de51a8f36849b126ba46846cbddd00cd2ba8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 30 Apr 2020 10:19:03 +0200 Subject: Filter lib2to3 (Pending)DeprecationWarning in testes lib2to3 is deprecated in Python 3.9: https://bugs.python.org/issue40360 Workarounds https://github.com/pypa/setuptools/issues/2081 --- changelog.d/2082.misc.rst | 2 ++ pytest.ini | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 changelog.d/2082.misc.rst diff --git a/changelog.d/2082.misc.rst b/changelog.d/2082.misc.rst new file mode 100644 index 00000000..81ad5d58 --- /dev/null +++ b/changelog.d/2082.misc.rst @@ -0,0 +1,2 @@ +Filter ``lib2to3`` ``PendingDeprecationWarning`` and ``DeprecationWarning`` in testes, +because ``lib2to3`` is `deprecated in Python 3.9 `_. diff --git a/pytest.ini b/pytest.ini index b13b7f33..479a2965 100644 --- a/pytest.ini +++ b/pytest.ini @@ -20,3 +20,6 @@ filterwarnings = ignore:Unicode unequal comparison failed to convert:UnicodeWarning # https://github.com/pypa/setuptools/issues/2025 ignore:direct construction of .*Item has been deprecated:DeprecationWarning + # https://github.com/pypa/setuptools/issues/2081 + ignore:lib2to3 package is deprecated:PendingDeprecationWarning + ignore:lib2to3 package is deprecated:DeprecationWarning -- cgit v1.2.1 From a672328f1e2515f5b103df12d19fa3c2ffba68fc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Apr 2020 11:28:09 +0200 Subject: bpo-40443: Remove unused imports in distutils (GH-19802) --- _msvccompiler.py | 4 ---- bcppcompiler.py | 4 ++-- ccompiler.py | 2 +- command/bdist_rpm.py | 1 - command/bdist_wininst.py | 2 +- command/check.py | 1 - command/upload.py | 1 - cygwinccompiler.py | 2 -- msvc9compiler.py | 3 +-- msvccompiler.py | 2 +- sysconfig.py | 1 - 11 files changed, 6 insertions(+), 17 deletions(-) diff --git a/_msvccompiler.py b/_msvccompiler.py index 03a5986d..af8099a4 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -14,8 +14,6 @@ for older versions in distutils.msvc9compiler and distutils.msvccompiler. # ported to VS 2015 by Steve Dower import os -import shutil -import stat import subprocess import winreg @@ -65,8 +63,6 @@ def _find_vc2017(): If vswhere.exe is not available, by definition, VS 2017 is not installed. """ - import json - root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") if not root: return None, None diff --git a/bcppcompiler.py b/bcppcompiler.py index 9f4c432d..071fea5d 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -14,10 +14,10 @@ for the Borland C++ compiler. import os from distutils.errors import \ - DistutilsExecError, DistutilsPlatformError, \ + DistutilsExecError, \ CompileError, LibError, LinkError, UnknownFileError from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options + CCompiler, gen_preprocess_options from distutils.file_util import write_file from distutils.dep_util import newer from distutils import log diff --git a/ccompiler.py b/ccompiler.py index 4cfc6c70..b5ef143e 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -8,7 +8,7 @@ from distutils.errors import * from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath -from distutils.dep_util import newer_pairwise, newer_group +from distutils.dep_util import newer_group from distutils.util import split_quoted, execute from distutils import log diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 74381cc6..550cbfa1 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -6,7 +6,6 @@ distributions).""" import subprocess, sys, os from distutils.core import Command from distutils.debug import DEBUG -from distutils.util import get_platform from distutils.file_util import write_file from distutils.errors import * from distutils.sysconfig import get_python_version diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index b5ed6f04..0e9ddaa2 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -8,7 +8,7 @@ import sys import warnings from distutils.core import Command from distutils.util import get_platform -from distutils.dir_util import create_tree, remove_tree +from distutils.dir_util import remove_tree from distutils.errors import * from distutils.sysconfig import get_python_version from distutils import log diff --git a/command/check.py b/command/check.py index 7ceabd3a..ada25006 100644 --- a/command/check.py +++ b/command/check.py @@ -11,7 +11,6 @@ try: from docutils.parsers.rst import Parser from docutils import frontend from docutils import nodes - from io import StringIO class SilentReporter(Reporter): diff --git a/command/upload.py b/command/upload.py index 11afa24b..d822ba01 100644 --- a/command/upload.py +++ b/command/upload.py @@ -7,7 +7,6 @@ index). import os import io -import platform import hashlib from base64 import standard_b64encode from urllib.request import urlopen, Request, HTTPError diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 6c5d7774..66c12dd3 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -51,12 +51,10 @@ import copy from subprocess import Popen, PIPE, check_output import re -from distutils.ccompiler import gen_preprocess_options, gen_lib_options from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import (DistutilsExecError, CCompilerError, CompileError, UnknownFileError) -from distutils import log from distutils.version import LooseVersion from distutils.spawn import find_executable diff --git a/msvc9compiler.py b/msvc9compiler.py index 4c0036a0..6934e964 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -19,8 +19,7 @@ import re from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError -from distutils.ccompiler import CCompiler, gen_preprocess_options, \ - gen_lib_options +from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log from distutils.util import get_platform diff --git a/msvccompiler.py b/msvccompiler.py index d1de2fbf..d5857cb1 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -13,7 +13,7 @@ from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options + CCompiler, gen_lib_options from distutils import log _can_read_reg = False diff --git a/sysconfig.py b/sysconfig.py index 01ee5197..37feae5d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -15,7 +15,6 @@ import re import sys from .errors import DistutilsPlatformError -from .util import get_platform, get_host_platform # These are needed in a couple of spots, so just compute them once. PREFIX = os.path.normpath(sys.prefix) -- cgit v1.2.1 From d2a64aebe4fd7b02c9d05a0cac30faac38f18977 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 May 2020 05:51:59 -0400 Subject: Apply suggestions from code review --- changelog.d/1698.doc.rst | 2 +- docs/build_meta.txt | 42 +++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/changelog.d/1698.doc.rst b/changelog.d/1698.doc.rst index 9b61fccb..90dc14c1 100644 --- a/changelog.d/1698.doc.rst +++ b/changelog.d/1698.doc.rst @@ -1 +1 @@ -Added documentation for ``build_meta`` (a bare minimum, not completed) +Added documentation for ``build_meta`` (a bare minimum, not completed). diff --git a/docs/build_meta.txt b/docs/build_meta.txt index 4467ddda..67497891 100644 --- a/docs/build_meta.txt +++ b/docs/build_meta.txt @@ -1,5 +1,5 @@ ======================================= -Documentation to setuptools.build_meta +Build System Support ======================================= What is it? @@ -7,16 +7,18 @@ What is it? Python packaging has come `a long way `_. -The traditional ``setuptools``'s way of packgaging Python modules +The traditional ``setuptools`` way of packgaging Python modules uses a ``setup()`` function within the ``setup.py`` script. Commands such as ``python setup.py bdist`` or ``python setup.py bdist_wheel`` generate a distribution bundle and ``python setup.py install`` installs the distribution. This interface makes it difficult to choose other packaging tools without an -overhaul. Additionally, the ``setup.py`` scripts hasn't been the most user -friendly tool. +overhaul. Because ``setup.py`` scripts allowed for arbitrary execution, it +proved difficult to provide a reliable user experience across environments +and history. -PEP517 therefore came to rescue and specified a new standard to -package and distribute Python modules. Under PEP517: +`PEP 517 `_ therefore came to +rescue and specified a new standard to +package and distribute Python modules. Under PEP 517: a ``pyproject.toml`` file is used to specify what program to use for generating distribution. @@ -35,8 +37,8 @@ package and distribute Python modules. Under PEP517: With this standard, switching between packaging tools becomes a lot easier and in the case of ``setuptools``, ``setup.py`` becomes optional. -``build_meta`` is ``setuptools``'s implementation of PEP517. It provides the -two functions, ``build_wheel`` and ``build_sdist``, amongst others and uses +``build_meta`` is ``setuptools``'s implementation of PEP 517. It provides the +two functions, ``build_wheel`` and ``build_sdist`` amongst others, and uses a ``setup.cfg`` to specify the information about the package. How to use it? @@ -58,7 +60,7 @@ setuptools, the content would be:: requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" -``setup.cfg`` is used to specify your package information, specified +Use ``setup.cfg`` to specify your package information, specified `here `_ :: @@ -70,26 +72,24 @@ setuptools, the content would be:: [options] packages = find: -Now it's time to actually generate the distribution. PEP517 specifies two -mandatory functions, ``build_wheel`` and ``build_sdist``, implemented in -this module. Currently, it has to be done in the interpreter:: +Now generate the distribution. Although the PyPA is still working to +`provide a recommended tool `_ +to build packages, the `pep517 package >> import setuptools.build_meta - >>> setuptools.build_meta.build_wheel('wheel_dist') - 'meowpkg-0.0.1-py3-none-any.whl' - >>> setuptools.build_meta.build_sdist('sdist') - 'meowpkg-0.0.1.tar.gz' + $ pip install -q pep517 + $ mkdir dist + $ python -m pep517.build . And now it's done! The ``.whl`` file and ``.tar.gz`` can then be distributed and installed:: - ~/newcomputer/ + dist/ meowpkg-0.0.1.whl meowpkg-0.0.1.tar.gz - $ pip install meowpkg-0.0.1.whl + $ pip install dist/meowpkg-0.0.1.whl or:: - $ pip install meowpkg-0.0.1.tar.gz - + $ pip install dist/meowpkg-0.0.1.tar.gz -- cgit v1.2.1 From cc5b5ec305100ecd897edfc7918f184ffc93c197 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 May 2020 05:59:41 -0400 Subject: Apply suggestions from code review --- docs/build_meta.txt | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/docs/build_meta.txt b/docs/build_meta.txt index 67497891..ef9fb2ac 100644 --- a/docs/build_meta.txt +++ b/docs/build_meta.txt @@ -34,12 +34,8 @@ package and distribute Python modules. Under PEP 517: ``pyproject.toml`` and decide what program to use to 'build from source' (the default is ``setuptools``) -With this standard, switching between packaging tools becomes a lot easier and -in the case of ``setuptools``, ``setup.py`` becomes optional. - -``build_meta`` is ``setuptools``'s implementation of PEP 517. It provides the -two functions, ``build_wheel`` and ``build_sdist`` amongst others, and uses -a ``setup.cfg`` to specify the information about the package. +With this standard, switching between packaging tools becomes a lot easier. ``build_meta`` +implements ``setuptools``' build system support. How to use it? -------------- @@ -60,9 +56,7 @@ setuptools, the content would be:: requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" -Use ``setup.cfg`` to specify your package information, specified -`here `_ :: +Use ``setuptools``' `declarative config`_ to specify the package information:: [metadata] name = meowpkg -- cgit v1.2.1 From 8c360dfd6361a15d1bbdfadb5fd0927a9a4a4cef Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 May 2020 06:30:31 -0400 Subject: Revert changes to historical notes and vendored packages. --- CHANGES.rst | 10 +++++----- pkg_resources/_vendor/appdirs.py | 21 +++++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index c145eb39..ac61c178 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1519,7 +1519,7 @@ v21.1.0 * #572: In build_ext, now always import ``_CONFIG_VARS`` from ``distutils`` rather than from ``sysconfig`` to allow ``distutils.sysconfig.customize_compiler`` - configure the macOS compiler for ``-dynamiclib``. + configure the OS X compiler for ``-dynamiclib``. v21.0.0 ------- @@ -1939,7 +1939,7 @@ v20.6.0 require that Cython be present before building source distributions. However, for systems with this build of setuptools, Cython will be downloaded on demand. -* Issue #396: Fixed test failure on macOS. +* Issue #396: Fixed test failure on OS X. * BB Pull Request #136: Remove excessive quoting from shebang headers for Jython. @@ -3136,7 +3136,7 @@ how it parses version numbers. * Distribute #306: Even if 2to3 is used, we build in-place under Python 2. * Distribute #307: Prints the full path when .svn/entries is broken. * Distribute #313: Support for sdist subcommands (Python 2.7) -* Distribute #314: test_local_index() would fail an macOS. +* Distribute #314: test_local_index() would fail an OS X. * Distribute #310: Non-ascii characters in a namespace __init__.py causes errors. * Distribute #218: Improved documentation on behavior of `package_data` and `include_package_data`. Files indicated by `package_data` are now included @@ -4163,7 +4163,7 @@ easy_install based on a contribution by Kevin Dangoor. You may wish to delete and reinstall any eggs whose filename includes "darwin" and "Power_Macintosh", because the format for this platform information has changed so that minor - macOS upgrades (such as 10.4.1 to 10.4.2) do not cause eggs built with a + OS X upgrades (such as 10.4.1 to 10.4.2) do not cause eggs built with a previous OS version to become obsolete. * easy_install's dependency processing algorithms have changed. When using @@ -4176,7 +4176,7 @@ easy_install * Added ``--site-dirs`` option to allow adding custom "site" directories. Made ``easy-install.pth`` work in platform-specific alternate site - directories (e.g. ``~/Library/Python/2.x/site-packages`` on macOS). + directories (e.g. ``~/Library/Python/2.x/site-packages`` on Mac OS X). * If you manually delete the current version of a package, the next run of EasyInstall against the target directory will now remove the stray entry diff --git a/pkg_resources/_vendor/appdirs.py b/pkg_resources/_vendor/appdirs.py index 4552cbbf..ae67001a 100644 --- a/pkg_resources/_vendor/appdirs.py +++ b/pkg_resources/_vendor/appdirs.py @@ -8,9 +8,10 @@ See for details and usage. """ # Dev Notes: -# - MSDN on where to store app data files: (TODO: needs new link) -# - macOS: (TODO: needs new link) -# - XDG spec for Un*x: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html __version_info__ = (1, 4, 3) __version__ = '.'.join(map(str, __version_info__)) @@ -63,7 +64,7 @@ def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): for a discussion of issues. Typical user data directories are: - macOS: ~/Library/Application Support/ + Mac OS X: ~/Library/Application Support/ Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined Win XP (not roaming): C:\Documents and Settings\\Application Data\\ Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ @@ -117,7 +118,7 @@ def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): if XDG_DATA_DIRS is not set Typical site data directories are: - macOS: /Library/Application Support/ + Mac OS X: /Library/Application Support/ Unix: /usr/local/share/ or /usr/share/ Win XP: C:\Documents and Settings\All Users\Application Data\\ Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) @@ -184,7 +185,7 @@ def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): for a discussion of issues. Typical user config directories are: - macOS: same as user_data_dir + Mac OS X: same as user_data_dir Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined Win *: same as user_data_dir @@ -222,7 +223,7 @@ def site_config_dir(appname=None, appauthor=None, version=None, multipath=False) returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set Typical site config directories are: - macOS: same as site_data_dir + Mac OS X: same as site_data_dir Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in $XDG_CONFIG_DIRS Win *: same as site_data_dir @@ -272,7 +273,7 @@ def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): discussion below. Typical user cache directories are: - macOS: ~/Library/Caches/ + Mac OS X: ~/Library/Caches/ Unix: ~/.cache/ (XDG default) Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache Vista: C:\Users\\AppData\Local\\\Cache @@ -332,7 +333,7 @@ def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): for a discussion of issues. Typical user state directories are: - macOS: same as user_data_dir + Mac OS X: same as user_data_dir Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined Win *: same as user_data_dir @@ -371,7 +372,7 @@ def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): base cache dir for Unix. See discussion below. Typical user log directories are: - macOS: ~/Library/Logs/ + Mac OS X: ~/Library/Logs/ Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs Vista: C:\Users\\AppData\Local\\\Logs -- cgit v1.2.1 From 194ae0ee00f16cf6a30202ef1f35237f730f4b21 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 May 2020 07:33:04 -0400 Subject: Update template to give more guidance, hoping to limit the blank submissions. --- .../setuptools-warns-about-python-2-incompatibility.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md index 14317d93..2f5fe53d 100644 --- a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md +++ b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md @@ -9,6 +9,10 @@ assignees: '' ## Environment Details - Operating System and version: -- Python version: -- Python installed how: +- Python version: +- Python installed how: - Virtualenv version (if using virtualenv): n/a Command(s) used to install setuptools (and output): -- cgit v1.2.1 From 40f81af021b899b76b96eef52bf020c2348e1aea Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 May 2020 07:40:44 -0400 Subject: Remove shebang. Fixes #2076. --- setuptools/command/easy_install.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 64ff0457..5a9576ff 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ Easy Install ------------ -- cgit v1.2.1 From 61ca49c08e71fee962b760fb90f8f89bb158738b Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Thu, 7 May 2020 11:39:55 -0400 Subject: Tweak note about setup.cfg This note has gotten a bit out of date, since setup.py is no longer required. --- docs/setuptools.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 22e3c872..30a30c26 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -2106,8 +2106,9 @@ Configuring setup() using setup.cfg files .. note:: New in 30.3.0 (8 Dec 2016). .. important:: - A ``setup.py`` file containing a ``setup()`` function call is still - required even if your configuration resides in ``setup.cfg``. + If compatibility with legacy builds (i.e. those not using the :pep:`517` + build API) is desired, a ``setup.py`` file containing a ``setup()`` function + call is still required even if your configuration resides in ``setup.cfg``. ``Setuptools`` allows using configuration files (usually :file:`setup.cfg`) to define a package’s metadata and other options that are normally supplied -- cgit v1.2.1 From 2d607a9e59aa854b387f22d79301acb8739b133a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 May 2020 13:19:52 -0400 Subject: Emit deprecation warning when 2to3 is used. Ref #2086. --- setuptools/lib2to3_ex.py | 7 +++++++ setuptools/tests/test_test.py | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/setuptools/lib2to3_ex.py b/setuptools/lib2to3_ex.py index 4b1a73fe..817dce40 100644 --- a/setuptools/lib2to3_ex.py +++ b/setuptools/lib2to3_ex.py @@ -7,6 +7,7 @@ Customized Mixin2to3 support: This module raises an ImportError on Python 2. """ +import warnings from distutils.util import Mixin2to3 as _Mixin2to3 from distutils import log from lib2to3.refactor import RefactoringTool, get_fixers_from_package @@ -33,6 +34,12 @@ class Mixin2to3(_Mixin2to3): return if not files: return + + warnings.warn( + "2to3 support is deprecated. Please migrate to " + "a single-codebase solution or roll your own " + "conversion process.", + DeprecationWarning) log.info("Fixing " + " ".join(files)) self.__build_fixer_names() self.__exclude_fixers() diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py index 8ee70a7e..0f77d8ff 100644 --- a/setuptools/tests/test_test.py +++ b/setuptools/tests/test_test.py @@ -73,7 +73,11 @@ def quiet_log(): log.set_verbosity(0) +ack_2to3 = pytest.mark.filterwarnings('ignore:2to3 support is deprecated') + + @pytest.mark.usefixtures('sample_test', 'quiet_log') +@ack_2to3 def test_test(capfd): params = dict( name='foo', @@ -124,6 +128,7 @@ def test_tests_are_run_once(capfd): @pytest.mark.usefixtures('sample_test') +@ack_2to3 def test_warns_deprecation(capfd): params = dict( name='foo', @@ -149,6 +154,7 @@ def test_warns_deprecation(capfd): @pytest.mark.usefixtures('sample_test') +@ack_2to3 def test_deprecation_stderr(capfd): params = dict( name='foo', -- cgit v1.2.1 From b286525862fa0a4839f37b5836a20178293d7335 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 May 2020 13:27:50 -0400 Subject: Update changelog. --- changelog.d/2086.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2086.change.rst diff --git a/changelog.d/2086.change.rst b/changelog.d/2086.change.rst new file mode 100644 index 00000000..9fa54e5a --- /dev/null +++ b/changelog.d/2086.change.rst @@ -0,0 +1 @@ +Deprecate 'use_2to3' functionality. Packagers are encouraged to use single-source solutions or build tool chains to manage conversions outside of setuptools. -- cgit v1.2.1 From 0fffb84a1bc7925fbf862365787ab20e9ab89a0c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 May 2020 14:03:30 -0400 Subject: In the deprecation warning, acknowledge that it's only for projects that still require Python 2 support. --- setuptools/lib2to3_ex.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setuptools/lib2to3_ex.py b/setuptools/lib2to3_ex.py index 817dce40..6d9b147c 100644 --- a/setuptools/lib2to3_ex.py +++ b/setuptools/lib2to3_ex.py @@ -36,9 +36,10 @@ class Mixin2to3(_Mixin2to3): return warnings.warn( - "2to3 support is deprecated. Please migrate to " - "a single-codebase solution or roll your own " - "conversion process.", + "2to3 support is deprecated. If the project still " + "requires Python 2 support, please migrate to " + "a single-codebase solution or employ an " + "independent conversion process.", DeprecationWarning) log.info("Fixing " + " ".join(files)) self.__build_fixer_names() -- cgit v1.2.1 From a354d7bc1b7737fc1b4a9d238a247364035ab3d8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 May 2020 14:23:10 -0400 Subject: Use the SetuptoolsDeprecationWarning to make the warning more visible outside test runners. --- setuptools/lib2to3_ex.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setuptools/lib2to3_ex.py b/setuptools/lib2to3_ex.py index 6d9b147c..017f7285 100644 --- a/setuptools/lib2to3_ex.py +++ b/setuptools/lib2to3_ex.py @@ -13,6 +13,7 @@ from distutils import log from lib2to3.refactor import RefactoringTool, get_fixers_from_package import setuptools +from ._deprecation_warning import SetuptoolsDeprecationWarning class DistutilsRefactoringTool(RefactoringTool): @@ -40,7 +41,7 @@ class Mixin2to3(_Mixin2to3): "requires Python 2 support, please migrate to " "a single-codebase solution or employ an " "independent conversion process.", - DeprecationWarning) + SetuptoolsDeprecationWarning) log.info("Fixing " + " ".join(files)) self.__build_fixer_names() self.__exclude_fixers() -- cgit v1.2.1 From fdee8026f5d58ee7cee0fad3c0b7e0ae7d5272ba Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 May 2020 15:51:53 -0400 Subject: Restore parameter --- pkg_resources/__init__.py | 6 +++--- pkg_resources/tests/test_pkg_resources.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 4d15086f..a50ad1ab 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2056,19 +2056,19 @@ def find_on_path(importer, path_item, only=False): filtered = ( entry for entry in entries - if dist_factory(entry, only) + if dist_factory(path_item, entry, only) ) # scan for .egg and .egg-info in directory path_item_entries = _by_version_descending(filtered) for entry in path_item_entries: fullpath = os.path.join(path_item, entry) - factory = dist_factory(entry, only) + factory = dist_factory(path_item, entry, only) for dist in factory(fullpath): yield dist -def dist_factory(entry, only): +def dist_factory(path_item, entry, only): """Return a dist_factory for the given entry.""" lower = entry.lower() if lower.endswith('.egg-info'): diff --git a/pkg_resources/tests/test_pkg_resources.py b/pkg_resources/tests/test_pkg_resources.py index 1c66dec0..9991402c 100644 --- a/pkg_resources/tests/test_pkg_resources.py +++ b/pkg_resources/tests/test_pkg_resources.py @@ -335,7 +335,7 @@ def test_dist_info_is_not_dir(tmp_path, only): """Test path containing a file with dist-info extension.""" dist_info = tmp_path / 'foobar.dist-info' dist_info.touch() - assert not pkg_resources.dist_factory(str(dist_info), only) + assert not pkg_resources.dist_factory(None, str(dist_info), only) class TestDeepVersionLookupDistutils: -- cgit v1.2.1 From da3d39f88d3aad2281b78600e03cf05c1e983f71 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 May 2020 15:56:20 -0400 Subject: Restore single return --- pkg_resources/__init__.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index a50ad1ab..6a6bda2c 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2071,16 +2071,18 @@ def find_on_path(importer, path_item, only=False): def dist_factory(path_item, entry, only): """Return a dist_factory for the given entry.""" lower = entry.lower() - if lower.endswith('.egg-info'): - return distributions_from_metadata - elif lower.endswith('.dist-info') and os.path.isdir(entry): - return distributions_from_metadata - elif not only and _is_egg_path(entry): - return find_distributions - elif not only and lower.endswith('.egg-link'): - return resolve_egg_link - else: - return NoDists() + is_egg_info = lower.endswith('.egg-info') + is_dist_info = lower.endswith('.dist-info') and os.path.isdir(entry) + is_meta = is_egg_info or is_dist_info + return ( + distributions_from_metadata + if is_meta else + find_distributions + if not only and _is_egg_path(entry) else + resolve_egg_link + if not only and lower.endswith('.egg-link') else + NoDists() + ) class NoDists: -- cgit v1.2.1 From 7b0a818f9a877d5e1fe558cf1a817bf2034ddf9f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 May 2020 16:05:51 -0400 Subject: Fix test failures when 'foo.dist-info' does not exist --- pkg_resources/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index b840b4d8..0575a989 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2072,7 +2072,7 @@ def dist_factory(path_item, entry, only): """Return a dist_factory for the given entry.""" lower = entry.lower() is_egg_info = lower.endswith('.egg-info') - is_dist_info = lower.endswith('.dist-info') and os.path.isdir(entry) + is_dist_info = lower.endswith('.dist-info') and not os.path.isfile(entry) is_meta = is_egg_info or is_dist_info return ( distributions_from_metadata -- cgit v1.2.1 From 5638e4783fc0f4f5cc40e7ecfab0500983826fa0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 May 2020 16:18:22 -0400 Subject: To assess the directoriness of an entry, include the path of that entry. --- pkg_resources/__init__.py | 5 ++++- pkg_resources/tests/test_pkg_resources.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 0575a989..0512731d 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2072,7 +2072,10 @@ def dist_factory(path_item, entry, only): """Return a dist_factory for the given entry.""" lower = entry.lower() is_egg_info = lower.endswith('.egg-info') - is_dist_info = lower.endswith('.dist-info') and not os.path.isfile(entry) + is_dist_info = ( + lower.endswith('.dist-info') and + os.path.isdir(os.path.join(path_item, entry)) + ) is_meta = is_egg_info or is_dist_info return ( distributions_from_metadata diff --git a/pkg_resources/tests/test_pkg_resources.py b/pkg_resources/tests/test_pkg_resources.py index 9991402c..189a8668 100644 --- a/pkg_resources/tests/test_pkg_resources.py +++ b/pkg_resources/tests/test_pkg_resources.py @@ -335,7 +335,7 @@ def test_dist_info_is_not_dir(tmp_path, only): """Test path containing a file with dist-info extension.""" dist_info = tmp_path / 'foobar.dist-info' dist_info.touch() - assert not pkg_resources.dist_factory(None, str(dist_info), only) + assert not pkg_resources.dist_factory(str(tmp_path), str(dist_info), only) class TestDeepVersionLookupDistutils: -- cgit v1.2.1 From b1b0134ba75dfefdad23d7410e14c74c8ec83aaa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 May 2020 17:40:36 -0400 Subject: Based on my understanding, this is more of a bugfix than a breaking change. --- changelog.d/2075.breaking.rst | 1 - changelog.d/2075.change.rst | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelog.d/2075.breaking.rst create mode 100644 changelog.d/2075.change.rst diff --git a/changelog.d/2075.breaking.rst b/changelog.d/2075.breaking.rst deleted file mode 100644 index 8d88b33f..00000000 --- a/changelog.d/2075.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -Stop recognizing files ending with ``.dist-info`` as distribution metadata diff --git a/changelog.d/2075.change.rst b/changelog.d/2075.change.rst new file mode 100644 index 00000000..abb13000 --- /dev/null +++ b/changelog.d/2075.change.rst @@ -0,0 +1 @@ +Stop recognizing files ending with ``.dist-info`` as distribution metadata. -- cgit v1.2.1 From d57adafdd9550376ef54ad3eb9f6a913adb2b214 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 May 2020 17:42:41 -0400 Subject: =?UTF-8?q?Bump=20version:=2046.1.3=20=E2=86=92=2046.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 12 ++++++++++++ changelog.d/1698.doc.rst | 1 - changelog.d/2040.change.rst | 1 - changelog.d/2062.change.rst | 1 - changelog.d/2075.change.rst | 1 - changelog.d/2082.misc.rst | 2 -- changelog.d/2086.change.rst | 1 - setup.cfg | 2 +- 9 files changed, 14 insertions(+), 9 deletions(-) delete mode 100644 changelog.d/1698.doc.rst delete mode 100644 changelog.d/2040.change.rst delete mode 100644 changelog.d/2062.change.rst delete mode 100644 changelog.d/2075.change.rst delete mode 100644 changelog.d/2082.misc.rst delete mode 100644 changelog.d/2086.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 00cbdc7a..c1b062e4 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 46.1.3 +current_version = 46.2.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index ac61c178..03c89be6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,15 @@ +v46.2.0 +------- + +* #2040: Deprecated the ``bdist_wininst`` command. Binary packages should be built as wheels instead. +* #2062: Change 'Mac OS X' to 'macOS' in code. +* #2075: Stop recognizing files ending with ``.dist-info`` as distribution metadata. +* #2086: Deprecate 'use_2to3' functionality. Packagers are encouraged to use single-source solutions or build tool chains to manage conversions outside of setuptools. +* #1698: Added documentation for ``build_meta`` (a bare minimum, not completed). +* #2082: Filter ``lib2to3`` ``PendingDeprecationWarning`` and ``DeprecationWarning`` in testes, + because ``lib2to3`` is `deprecated in Python 3.9 `_. + + v46.1.3 ------- diff --git a/changelog.d/1698.doc.rst b/changelog.d/1698.doc.rst deleted file mode 100644 index 90dc14c1..00000000 --- a/changelog.d/1698.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Added documentation for ``build_meta`` (a bare minimum, not completed). diff --git a/changelog.d/2040.change.rst b/changelog.d/2040.change.rst deleted file mode 100644 index b8f36f6f..00000000 --- a/changelog.d/2040.change.rst +++ /dev/null @@ -1 +0,0 @@ -Deprecated the ``bdist_wininst`` command. Binary packages should be built as wheels instead. diff --git a/changelog.d/2062.change.rst b/changelog.d/2062.change.rst deleted file mode 100644 index 1f5fd812..00000000 --- a/changelog.d/2062.change.rst +++ /dev/null @@ -1 +0,0 @@ -Change 'Mac OS X' to 'macOS' in code. diff --git a/changelog.d/2075.change.rst b/changelog.d/2075.change.rst deleted file mode 100644 index abb13000..00000000 --- a/changelog.d/2075.change.rst +++ /dev/null @@ -1 +0,0 @@ -Stop recognizing files ending with ``.dist-info`` as distribution metadata. diff --git a/changelog.d/2082.misc.rst b/changelog.d/2082.misc.rst deleted file mode 100644 index 81ad5d58..00000000 --- a/changelog.d/2082.misc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Filter ``lib2to3`` ``PendingDeprecationWarning`` and ``DeprecationWarning`` in testes, -because ``lib2to3`` is `deprecated in Python 3.9 `_. diff --git a/changelog.d/2086.change.rst b/changelog.d/2086.change.rst deleted file mode 100644 index 9fa54e5a..00000000 --- a/changelog.d/2086.change.rst +++ /dev/null @@ -1 +0,0 @@ -Deprecate 'use_2to3' functionality. Packagers are encouraged to use single-source solutions or build tool chains to manage conversions outside of setuptools. diff --git a/setup.cfg b/setup.cfg index 60838efc..2f5525f3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 46.1.3 +version = 46.2.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From bb5af519860192d3bca638d903cb0ea123c7abd8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 11 May 2020 23:49:53 +0300 Subject: Fix typo --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 03c89be6..a935be56 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,7 +6,7 @@ v46.2.0 * #2075: Stop recognizing files ending with ``.dist-info`` as distribution metadata. * #2086: Deprecate 'use_2to3' functionality. Packagers are encouraged to use single-source solutions or build tool chains to manage conversions outside of setuptools. * #1698: Added documentation for ``build_meta`` (a bare minimum, not completed). -* #2082: Filter ``lib2to3`` ``PendingDeprecationWarning`` and ``DeprecationWarning`` in testes, +* #2082: Filter ``lib2to3`` ``PendingDeprecationWarning`` and ``DeprecationWarning`` in tests, because ``lib2to3`` is `deprecated in Python 3.9 `_. -- cgit v1.2.1 From 56bcce894e99059a8abda29d8b919b0bee7fd1b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 12 May 2020 13:33:04 +0200 Subject: Reuse @ack_2to3 in TestDevelop.test_2to3_user_mode Fixes https://github.com/pypa/setuptools/issues/2100 --- changelog.d/2105.misc.rst | 1 + setuptools/tests/__init__.py | 4 +++- setuptools/tests/test_develop.py | 2 ++ setuptools/tests/test_test.py | 4 +--- 4 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 changelog.d/2105.misc.rst diff --git a/changelog.d/2105.misc.rst b/changelog.d/2105.misc.rst new file mode 100644 index 00000000..75eaf2cb --- /dev/null +++ b/changelog.d/2105.misc.rst @@ -0,0 +1 @@ +Filter ``2to3`` deprecation warnings from ``TestDevelop.test_2to3_user_mode``. diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 9c77b51f..6377d785 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -6,7 +6,7 @@ from setuptools.extern.six import PY2, PY3 __all__ = [ - 'fail_on_ascii', 'py2_only', 'py3_only' + 'fail_on_ascii', 'py2_only', 'py3_only', 'ack_2to3' ] @@ -16,3 +16,5 @@ fail_on_ascii = pytest.mark.xfail(is_ascii, reason="Test fails in this locale") py2_only = pytest.mark.skipif(not PY2, reason="Test runs on Python 2 only") py3_only = pytest.mark.skipif(not PY3, reason="Test runs on Python 3 only") + +ack_2to3 = pytest.mark.filterwarnings('ignore:2to3 support is deprecated') diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index 792975fd..bb89a865 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -17,6 +17,7 @@ import pytest from setuptools.command.develop import develop from setuptools.dist import Distribution +from setuptools.tests import ack_2to3 from . import contexts from . import namespaces @@ -65,6 +66,7 @@ class TestDevelop: @pytest.mark.skipif( in_virtualenv or in_venv, reason="Cannot run when invoked in a virtualenv or venv") + @ack_2to3 def test_2to3_user_mode(self, test_env): settings = dict( name='foo', diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py index 0f77d8ff..892fd120 100644 --- a/setuptools/tests/test_test.py +++ b/setuptools/tests/test_test.py @@ -10,6 +10,7 @@ import pytest from setuptools.command.test import test from setuptools.dist import Distribution +from setuptools.tests import ack_2to3 from .textwrap import DALS @@ -73,9 +74,6 @@ def quiet_log(): log.set_verbosity(0) -ack_2to3 = pytest.mark.filterwarnings('ignore:2to3 support is deprecated') - - @pytest.mark.usefixtures('sample_test', 'quiet_log') @ack_2to3 def test_test(capfd): -- cgit v1.2.1 From 322734cfa00d3d5bffb9af02c780ee8e33142e5b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 12 May 2020 20:34:20 -0400 Subject: =?UTF-8?q?=F0=9F=91=B9=20Feed=20the=20hobgoblins=20(delint).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #2107 --- pkg_resources/tests/test_working_set.py | 4 ++-- setuptools/tests/test_easy_install.py | 2 +- setuptools/tests/test_packageindex.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg_resources/tests/test_working_set.py b/pkg_resources/tests/test_working_set.py index b3ca4ea8..7a759bbb 100644 --- a/pkg_resources/tests/test_working_set.py +++ b/pkg_resources/tests/test_working_set.py @@ -14,8 +14,8 @@ __metaclass__ = type def strip_comments(s): return '\n'.join( - l for l in s.split('\n') - if l.strip() and not l.strip().startswith('#') + line for line in s.split('\n') + if line.strip() and not line.strip().startswith('#') ) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 534392b9..3044cbd0 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -738,7 +738,7 @@ class TestSetupRequires: dep_2_0_url=dep_2_0_url, dep_2_0_sdist=dep_2_0_sdist, dep_2_0_python_requires=dep_2_0_python_requires, - ), 'utf-8') + ), 'utf-8') index_url = path_to_url(str(index)) with contexts.save_pkg_resources_state(): test_pkg = create_setup_requires_package( diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 60d968fd..fc8f2a70 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -221,11 +221,11 @@ class TestPackageIndex: ('+ubuntu_0', '+ubuntu.0'), ] versions = [ - [''.join([e, r, p, l]) for l in ll] + [''.join([e, r, p, loc]) for loc in locs] for e in epoch for r in releases for p in sum([pre, post, dev], ['']) - for ll in local] + for locs in local] for v, vc in versions: dists = list(setuptools.package_index.distros_for_url( 'http://example.com/example.zip#egg=example-' + v)) -- cgit v1.2.1 From d1dccc76ef90497788b5c839918d775585044f5a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 12 May 2020 19:58:54 -0400 Subject: Just remove fragment remover. PyPI no longer supplies these md5 values anyway. Fixes #2089. --- pkg_resources/__init__.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 0512731d..edd3d2e8 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -55,7 +55,7 @@ except NameError: FileExistsError = OSError from pkg_resources.extern import six -from pkg_resources.extern.six.moves import urllib, map, filter +from pkg_resources.extern.six.moves import map, filter # capture these to bypass sandboxing from os import utime @@ -2546,15 +2546,6 @@ class EntryPoint: return maps -def _remove_md5_fragment(location): - if not location: - return '' - parsed = urllib.parse.urlparse(location) - if parsed[-1].startswith('md5='): - return urllib.parse.urlunparse(parsed[:-1] + ('',)) - return location - - def _version_from_file(lines): """ Given an iterable of lines from a Metadata file, return @@ -2611,7 +2602,7 @@ class Distribution: self.parsed_version, self.precedence, self.key, - _remove_md5_fragment(self.location), + self.location, self.py_version or '', self.platform or '', ) -- cgit v1.2.1 From 932d88b5e5972f6da11ebff4e516b787dca8e393 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 12 May 2020 20:22:53 -0400 Subject: Remove the test, no longer relevant now that external links are no longer supported and PyPI no longer allows replacing packages. --- setuptools/tests/test_packageindex.py | 39 ----------------------------------- 1 file changed, 39 deletions(-) diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index fc8f2a70..45296897 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -9,9 +9,7 @@ from setuptools.extern.six.moves import urllib, http_client import mock import pytest -import pkg_resources import setuptools.package_index -from setuptools.tests.server import IndexServer from .textwrap import DALS @@ -114,43 +112,6 @@ class TestPackageIndex: url = 'file:///tmp/test_package_index' assert index.url_ok(url, True) - def test_links_priority(self): - """ - Download links from the pypi simple index should be used before - external download links. - https://bitbucket.org/tarek/distribute/issue/163 - - Usecase : - - someone uploads a package on pypi, a md5 is generated - - someone manually copies this link (with the md5 in the url) onto an - external page accessible from the package page. - - someone reuploads the package (with a different md5) - - while easy_installing, an MD5 error occurs because the external link - is used - -> Setuptools should use the link from pypi, not the external one. - """ - if sys.platform.startswith('java'): - # Skip this test on jython because binding to :0 fails - return - - # start an index server - server = IndexServer() - server.start() - index_url = server.base_url() + 'test_links_priority/simple/' - - # scan a test index - pi = setuptools.package_index.PackageIndex(index_url) - requirement = pkg_resources.Requirement.parse('foobar') - pi.find_packages(requirement) - server.stop() - - # the distribution has been found - assert 'foobar' in pi - # we have only one link, because links are compared without md5 - assert len(pi['foobar']) == 1 - # the link should be from the index - assert 'correct_md5' in pi['foobar'][0].location - def test_parse_bdist_wininst(self): parse = setuptools.package_index.parse_bdist_wininst -- cgit v1.2.1 From 6a73d1f0bc1e25833974eb0ec943b65d2381a7a9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 12 May 2020 20:49:54 -0400 Subject: Update changelog --- changelog.d/2089.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2089.change.rst diff --git a/changelog.d/2089.change.rst b/changelog.d/2089.change.rst new file mode 100644 index 00000000..0977cfca --- /dev/null +++ b/changelog.d/2089.change.rst @@ -0,0 +1 @@ +Package index functionality no longer attempts to remove an md5 fragment from the index URL. This functionality, added for distribute #163 is no longer relevant. -- cgit v1.2.1 From fb09f9c44e0f6a93798d52080cb00741e6c766f6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 13 May 2020 11:56:48 -0400 Subject: bugfix isn't a thing --- changelog.d/2041.bugfix.rst | 1 - changelog.d/2041.misc.rst | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelog.d/2041.bugfix.rst create mode 100644 changelog.d/2041.misc.rst diff --git a/changelog.d/2041.bugfix.rst b/changelog.d/2041.bugfix.rst deleted file mode 100644 index 8db757d8..00000000 --- a/changelog.d/2041.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Preserve file modes during pkg files copying, but clear read only flag for target afterwards. diff --git a/changelog.d/2041.misc.rst b/changelog.d/2041.misc.rst new file mode 100644 index 00000000..8db757d8 --- /dev/null +++ b/changelog.d/2041.misc.rst @@ -0,0 +1 @@ +Preserve file modes during pkg files copying, but clear read only flag for target afterwards. -- cgit v1.2.1 From fc0249f24ad39746ae955a2eae0d87ba201454df Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 13 May 2020 11:56:56 -0400 Subject: =?UTF-8?q?Bump=20version:=2046.2.0=20=E2=86=92=2046.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 8 ++++++++ changelog.d/2041.misc.rst | 1 - changelog.d/2089.change.rst | 1 - changelog.d/2105.misc.rst | 1 - setup.cfg | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 changelog.d/2041.misc.rst delete mode 100644 changelog.d/2089.change.rst delete mode 100644 changelog.d/2105.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index c1b062e4..09a3be97 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 46.2.0 +current_version = 46.3.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index a935be56..97934531 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,11 @@ +v46.3.0 +------- + +* #2089: Package index functionality no longer attempts to remove an md5 fragment from the index URL. This functionality, added for distribute #163 is no longer relevant. +* #2041: Preserve file modes during pkg files copying, but clear read only flag for target afterwards. +* #2105: Filter ``2to3`` deprecation warnings from ``TestDevelop.test_2to3_user_mode``. + + v46.2.0 ------- diff --git a/changelog.d/2041.misc.rst b/changelog.d/2041.misc.rst deleted file mode 100644 index 8db757d8..00000000 --- a/changelog.d/2041.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Preserve file modes during pkg files copying, but clear read only flag for target afterwards. diff --git a/changelog.d/2089.change.rst b/changelog.d/2089.change.rst deleted file mode 100644 index 0977cfca..00000000 --- a/changelog.d/2089.change.rst +++ /dev/null @@ -1 +0,0 @@ -Package index functionality no longer attempts to remove an md5 fragment from the index URL. This functionality, added for distribute #163 is no longer relevant. diff --git a/changelog.d/2105.misc.rst b/changelog.d/2105.misc.rst deleted file mode 100644 index 75eaf2cb..00000000 --- a/changelog.d/2105.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Filter ``2to3`` deprecation warnings from ``TestDevelop.test_2to3_user_mode``. diff --git a/setup.cfg b/setup.cfg index 2f5525f3..54953432 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 46.2.0 +version = 46.3.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From c79400a2839527d0749798637d182b1cb3d84a01 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 13 May 2020 14:24:42 -0400 Subject: Ensure that the changelog.d doesn't contain files that won't match. --- tools/finalize.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/finalize.py b/tools/finalize.py index 5f284568..98b06c07 100644 --- a/tools/finalize.py +++ b/tools/finalize.py @@ -60,8 +60,22 @@ def ensure_config(): subprocess.check_output(['git', 'config', 'user.email']) +def check_changes(): + """ + Verify that all of the files in changelog.d have the appropriate + names. + """ + allowed = 'deprecation', 'breaking', 'change', 'doc', 'misc' + assert all( + any(key in file.name for key in allowed) + for file in pathlib.Path('changelog.d').iterdir() + if file.name != '.gitignore' + ) + + if __name__ == '__main__': print("Cutting release at", get_version()) ensure_config() + check_changes() update_changelog() bump_version() -- cgit v1.2.1 From 6463fee5c9ed5864eb554ee99ad292e5507d4b55 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 May 2020 04:33:57 -0400 Subject: Sync azure pipelines config with jaraco/skeleton, adding support for building on macOS and Windows. --- azure-pipelines.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0253a770..ee772682 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -11,10 +11,12 @@ trigger: - '*' pool: - vmimage: 'Ubuntu-18.04' + vmImage: $(pool_vm_image) variables: - group: Azure secrets +- name: pool_vm_image + value: Ubuntu-18.04 stages: - stage: Test @@ -23,10 +25,17 @@ stages: - job: 'Test' strategy: matrix: - Python36: + Bionic Python 3.6: python.version: '3.6' - Python38: + Bionic Python 3.8: python.version: '3.8' + Windows: + python.version: '3.8' + pool_vm_image: vs2017-win2016 + MacOS: + python.version: '3.8' + pool_vm_image: macos-10.15 + maxParallel: 4 steps: -- cgit v1.2.1 From 2f1d7173a196268008938e234e9e796bc6761b27 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 May 2020 05:00:02 -0400 Subject: Avoid reliance on shell details in test_virtualenv. --- setuptools/tests/test_virtualenv.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py index 6549a6c0..a62e564c 100644 --- a/setuptools/tests/test_virtualenv.py +++ b/setuptools/tests/test_virtualenv.py @@ -57,10 +57,7 @@ def test_clean_env_install(bare_virtualenv): """ Check setuptools can be installed in a clean environment. """ - bare_virtualenv.run(' && '.join(( - 'cd {source}', - 'python setup.py install', - )).format(source=SOURCE_DIR)) + bare_virtualenv.run(['python', 'setup.py', 'install'], cd=SOURCE_DIR) def _get_pip_versions(): -- cgit v1.2.1 From c48365a763ea41d3f9e6298b3becbe73045ff9a1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 May 2020 05:13:50 -0400 Subject: Avoid reliance on shell details in test_virtualenv. --- setuptools/tests/test_virtualenv.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py index a62e564c..555273ae 100644 --- a/setuptools/tests/test_virtualenv.py +++ b/setuptools/tests/test_virtualenv.py @@ -112,10 +112,9 @@ def test_pip_upgrade_from_source(pip_version, virtualenv): dist_dir = virtualenv.workspace # Generate source distribution / wheel. virtualenv.run(' && '.join(( - 'cd {source}', 'python setup.py -q sdist -d {dist}', 'python setup.py -q bdist_wheel -d {dist}', - )).format(source=SOURCE_DIR, dist=dist_dir)) + )).format(dist=dist_dir), cd=SOURCE_DIR) sdist = glob.glob(os.path.join(dist_dir, '*.zip'))[0] wheel = glob.glob(os.path.join(dist_dir, '*.whl'))[0] # Then update from wheel. @@ -180,10 +179,8 @@ def _check_test_command_install_requirements(virtualenv, tmpdir): open('success', 'w').close() ''')) # Run test command for test package. - virtualenv.run(' && '.join(( - 'cd {tmpdir}', - 'python setup.py test -s test', - )).format(tmpdir=tmpdir)) + virtualenv.run( + ['python', 'setup.py', 'test', '-s', 'test'], cd=str(tmpdir)) assert tmpdir.join('success').check() @@ -204,7 +201,5 @@ def test_no_missing_dependencies(bare_virtualenv): Quick and dirty test to ensure all external dependencies are vendored. """ for command in ('upload',): # sorted(distutils.command.__all__): - bare_virtualenv.run(' && '.join(( - 'cd {source}', - 'python setup.py {command} -h', - )).format(command=command, source=SOURCE_DIR)) + bare_virtualenv.run( + ['python', 'setup.py', command, '-h'], cd=SOURCE_DIR) -- cgit v1.2.1 From f6f25adfc81df76e186bf6c3738a1baa0f92be05 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 May 2020 05:15:42 -0400 Subject: Just write --- setuptools/tests/test_packageindex.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 45296897..c6016c09 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -287,13 +287,12 @@ class TestPyPIConfig: def test_percent_in_password(self, tmpdir, monkeypatch): monkeypatch.setitem(os.environ, 'HOME', str(tmpdir)) pypirc = tmpdir / '.pypirc' - with pypirc.open('w') as strm: - strm.write(DALS(""" - [pypi] - repository=https://pypi.org - username=jaraco - password=pity% - """)) + pypirc.write(DALS(""" + [pypi] + repository=https://pypi.org + username=jaraco + password=pity% + """)) cfg = setuptools.package_index.PyPIConfig() cred = cfg.creds_by_repository['https://pypi.org'] assert cred.username == 'jaraco' -- cgit v1.2.1 From 2187d0c770de3a93f9a6cdcf53d9a217167dcc83 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 May 2020 05:57:10 -0400 Subject: Extract fixture for a temp home --- setuptools/tests/test_packageindex.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index c6016c09..7b7815ab 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -283,10 +283,15 @@ class TestContentCheckers: assert rep == 'My message about md5' +@pytest.fixture +def temp_home(tmpdir, monkeypatch): + monkeypatch.setitem(os.environ, 'HOME', str(tmpdir)) + return tmpdir + + class TestPyPIConfig: - def test_percent_in_password(self, tmpdir, monkeypatch): - monkeypatch.setitem(os.environ, 'HOME', str(tmpdir)) - pypirc = tmpdir / '.pypirc' + def test_percent_in_password(self, temp_home): + pypirc = temp_home / '.pypirc' pypirc.write(DALS(""" [pypi] repository=https://pypi.org -- cgit v1.2.1 From f866311d60f54499c3637309e3429780d8c8f218 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 May 2020 06:00:35 -0400 Subject: Add platform-specific code to override the home directory to honor bpo-36264. Fixes #2112. --- setuptools/tests/test_packageindex.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 7b7815ab..29aace13 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -3,6 +3,7 @@ from __future__ import absolute_import import sys import os import distutils.errors +import platform from setuptools.extern import six from setuptools.extern.six.moves import urllib, http_client @@ -285,7 +286,13 @@ class TestContentCheckers: @pytest.fixture def temp_home(tmpdir, monkeypatch): - monkeypatch.setitem(os.environ, 'HOME', str(tmpdir)) + key = ( + 'USERPROFILE' + if platform.system() == 'Windows' and sys.version_info > (3, 8) else + 'HOME' + ) + + monkeypatch.setitem(os.environ, key, str(tmpdir)) return tmpdir -- cgit v1.2.1 From 5ccaa70e0917bba8e411ebd8b8f15384ed352fcd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 May 2020 06:09:56 -0400 Subject: Update badge for Azure Pipelines --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 6caaa756..9cbf7b86 100644 --- a/README.rst +++ b/README.rst @@ -6,7 +6,7 @@ .. _PyPI link: https://pypi.org/project/setuptools -.. image:: https://dev.azure.com/jaraco/setuptools/_apis/build/status/jaraco.setuptools?branchName=master +.. image:: https://dev.azure.com/jaraco/setuptools/_apis/build/status/pypa.setuptools?branchName=master :target: https://dev.azure.com/jaraco/setuptools/_build/latest?definitionId=1&branchName=master .. image:: https://img.shields.io/travis/pypa/setuptools/master.svg?label=Linux%20CI&logo=travis&logoColor=white -- cgit v1.2.1 From e04c75ab906caadff4609ef34de8973c8e92eff8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 May 2020 06:10:33 -0400 Subject: =?UTF-8?q?Bump=20version:=2046.3.0=20=E2=86=92=2046.3.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ setup.cfg | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 09a3be97..4ee92185 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 46.3.0 +current_version = 46.3.1 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 97934531..fd3c16ba 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v46.3.1 +------- + +No significant changes. + + v46.3.0 ------- diff --git a/setup.cfg b/setup.cfg index 54953432..467d1fa7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 46.3.0 +version = 46.3.1 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From bccf87f70b41ee288db4b8e91df02a64ec42579a Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Fri, 15 May 2020 16:58:38 +0200 Subject: Fix an RST link typo in the dev guide --- docs/developer-guide.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide.txt b/docs/developer-guide.txt index 0b4ae4d4..e6171e4e 100644 --- a/docs/developer-guide.txt +++ b/docs/developer-guide.txt @@ -139,7 +139,7 @@ Vendored Dependencies --------------------- Setuptools has some dependencies, but due to `bootstrapping issues -`, those dependencies +`_, those dependencies cannot be declared as they won't be resolved soon enough to build setuptools from source. Eventually, this limitation may be lifted as PEP 517/518 reach ubiquitous adoption, but for now, Setuptools -- cgit v1.2.1 From b9d48323ce2571376ba34c05d65450f66e1581e9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 15 May 2020 18:06:23 +0200 Subject: bpo-40055: test_distutils leaves warnings filters unchanged (GH-20095) distutils.tests now saves/restores warnings filters to leave them unchanged. Importing tests imports docutils which imports pkg_resources which adds a warnings filter. --- tests/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/__init__.py b/tests/__init__.py index 1b939cbd..5d2e69e3 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -15,6 +15,7 @@ by import rather than matching pre-defined names. import os import sys import unittest +import warnings from test.support import run_unittest @@ -22,6 +23,7 @@ here = os.path.dirname(__file__) or os.curdir def test_suite(): + old_filters = warnings.filters[:] suite = unittest.TestSuite() for fn in os.listdir(here): if fn.startswith("test") and fn.endswith(".py"): @@ -29,6 +31,10 @@ def test_suite(): __import__(modname) module = sys.modules[modname] suite.addTest(module.test_suite()) + # bpo-40055: Save/restore warnings filters to leave them unchanged. + # Importing tests imports docutils which imports pkg_resources which adds a + # warnings filter. + warnings.filters[:] = old_filters return suite -- cgit v1.2.1 From 6de971f158553c47ce11e7be9d38d268e0398193 Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Sat, 27 Apr 2019 19:21:50 +0000 Subject: Implement a "literal_attr:" config directive --- setuptools/config.py | 62 +++++++++++++++++++++++++++++++++------ setuptools/tests/test_config.py | 64 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 9 deletions(-) diff --git a/setuptools/config.py b/setuptools/config.py index 9b9a0c45..d1456cac 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -1,4 +1,5 @@ from __future__ import absolute_import, unicode_literals +import ast import io import os import sys @@ -316,15 +317,22 @@ class ConfigHandler: Examples: attr: package.attr attr: package.module.attr + literal_attr: package.attr + literal_attr: package.module.attr :param str value: :rtype: str """ attr_directive = 'attr:' - if not value.startswith(attr_directive): + literal_attr_directive = 'literal_attr:' + if value.startswith(attr_directive): + directive = attr_directive + elif value.startswith(literal_attr_directive): + directive = literal_attr_directive + else: return value - attrs_path = value.replace(attr_directive, '').strip().split('.') + attrs_path = value.replace(directive, '').strip().split('.') attr_name = attrs_path.pop() module_name = '.'.join(attrs_path) @@ -344,13 +352,49 @@ class ConfigHandler: elif '' in package_dir: # A custom parent directory was specified for all root modules parent_path = os.path.join(os.getcwd(), package_dir['']) - sys.path.insert(0, parent_path) - try: - module = import_module(module_name) - value = getattr(module, attr_name) - - finally: - sys.path = sys.path[1:] + if directive == attr_directive: + sys.path.insert(0, parent_path) + try: + module = import_module(module_name) + value = getattr(module, attr_name) + finally: + sys.path = sys.path[1:] + + elif directive == literal_attr_directive: + fpath = os.path.join(parent_path, *module_name.split('.')) + if os.path.exists(fpath + '.py'): + fpath += '.py' + elif os.path.isdir(fpath): + fpath = os.path.join(fpath, '__init__.py') + else: + raise DistutilsOptionError( + 'Could not find module ' + module_name + ) + with open(fpath, 'rb') as fp: + src = fp.read() + found = False + top_level = ast.parse(src) + for statement in top_level.body: + if isinstance(statement, ast.Assign): + for target in statement.targets: + if isinstance(target, ast.Name) \ + and target.id == attr_name: + value = ast.literal_eval(statement.value) + found = True + elif isinstance(target, ast.Tuple) \ + and any(isinstance(t, ast.Name) and t.id==attr_name + for t in target.elts): + stmnt_value = ast.literal_eval(statement.value) + for t,v in zip(target.elts, stmnt_value): + if isinstance(t, ast.Name) \ + and t.id == attr_name: + value = v + found = True + if not found: + raise DistutilsOptionError( + 'No literal assignment to {!r} found in file' + .format(attr_name) + ) return value diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 2fa0b374..03e6916b 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -300,6 +300,37 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' + def test_literal_version(self, tmpdir): + + _, config = fake_env( + tmpdir, + '[metadata]\n' + 'version = literal_attr: fake_package.VERSION\n' + ) + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1.2.3' + + config.write( + '[metadata]\n' + 'version = literal_attr: fake_package.VERSION_MAJOR\n' + ) + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1' + + subpack = tmpdir.join('fake_package').mkdir('subpackage') + subpack.join('__init__.py').write('') + subpack.join('submodule.py').write( + 'import third_party_module\n' + 'VERSION = (2016, 11, 26)' + ) + + config.write( + '[metadata]\n' + 'version = attr: fake_package.subpackage.submodule.VERSION\n' + ) + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '2016.11.26' + def test_version_file(self, tmpdir): _, config = fake_env( @@ -332,6 +363,17 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' + config.write( + '[metadata]\n' + 'version = literal_attr: fake_package_simple.VERSION\n' + '[options]\n' + 'package_dir =\n' + ' = src\n' + ) + + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1.2.3' + def test_version_with_package_dir_rename(self, tmpdir): _, config = fake_env( @@ -347,6 +389,17 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' + config.write( + '[metadata]\n' + 'version = literal_attr: fake_package_rename.VERSION\n' + '[options]\n' + 'package_dir =\n' + ' fake_package_rename = fake_dir\n' + ) + + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1.2.3' + def test_version_with_package_dir_complex(self, tmpdir): _, config = fake_env( @@ -362,6 +415,17 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' + config.write( + '[metadata]\n' + 'version = literal_attr: fake_package_complex.VERSION\n' + '[options]\n' + 'package_dir =\n' + ' fake_package_complex = src/fake_dir\n' + ) + + with get_dist(tmpdir) as dist: + assert dist.metadata.version == '1.2.3' + def test_unknown_meta_item(self, tmpdir): fake_env( -- cgit v1.2.1 From 2bbd4d72225ad4f717be65460940292b50bd781e Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Sat, 27 Apr 2019 19:31:21 +0000 Subject: Update documentation --- docs/setuptools.txt | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/docs/setuptools.txt b/docs/setuptools.txt index ec58b754..3e616582 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -2193,7 +2193,7 @@ Metadata and options are set in the config sections of the same name. * In some cases, complex values can be provided in dedicated subsections for clarity. -* Some keys allow ``file:``, ``attr:``, and ``find:`` and ``find_namespace:`` directives in +* Some keys allow ``file:``, ``attr:``, ``literal_attr:``, ``find:``, and ``find_namespace:`` directives in order to cover common usecases. * Unknown keys are ignored. @@ -2290,6 +2290,15 @@ Special directives: * ``attr:`` - Value is read from a module attribute. ``attr:`` supports callables and iterables; unsupported types are cast using ``str()``. + +* ``literal_attr:`` — Like ``attr:``, except that the value is parsed using + ``ast.literal_eval()`` instead of by importing the module. This allows one + to specify an attribute of a module that imports one or more third-party + modules without having to install those modules first; as a downside, + ``literal_attr:`` only supports variables that are assigned constant + expressions, not more complex assignments like ``__version__ = + '.'.join(map(str, (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)))``. + * ``file:`` - Value is read from a list of files and then concatenated @@ -2305,14 +2314,14 @@ Metadata The aliases given below are supported for compatibility reasons, but their use is not advised. -============================== ================= ================= =============== ===== -Key Aliases Type Minimum Version Notes -============================== ================= ================= =============== ===== +============================== ================= ================================ =============== ===== +Key Aliases Type Minimum Version Notes +============================== ================= ================================ =============== ===== name str -version attr:, file:, str 39.2.0 (1) +version attr:, literal_attr:, file:, str 39.2.0 (1) url home-page str download_url download-url str -project_urls dict 38.3.0 +project_urls dict 38.3.0 author str author_email author-email str maintainer str @@ -2323,13 +2332,13 @@ license_file str license_files list-comma description summary file:, str long_description long-description file:, str -long_description_content_type str 38.6.0 +long_description_content_type str 38.6.0 keywords list-comma platforms platform list-comma provides list-comma requires list-comma obsoletes list-comma -============================== ================= ================= =============== ===== +============================== ================= ================================ =============== ===== .. note:: A version loaded using the ``file:`` directive must comply with PEP 440. -- cgit v1.2.1 From 130cbede42d3d351fc21bb35c18c1be7e108df46 Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Sat, 27 Apr 2019 19:54:51 +0000 Subject: Added changelog.d news fragment --- changelog.d/1753.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1753.change.rst diff --git a/changelog.d/1753.change.rst b/changelog.d/1753.change.rst new file mode 100644 index 00000000..0f27bb2e --- /dev/null +++ b/changelog.d/1753.change.rst @@ -0,0 +1 @@ +Added a ``literal_attr:`` config directive to support reading versions from attributes of modules that import third-party modules -- cgit v1.2.1 From d6bcf5e89ef6a523c2476b249aba810af9808d8b Mon Sep 17 00:00:00 2001 From: "John T. Wodder II" Date: Fri, 15 May 2020 21:17:49 +0000 Subject: Merge `literal_attr:` functionality into `attr:` --- changelog.d/1753.change.rst | 5 ++- docs/setuptools.txt | 27 ++++++------- setuptools/config.py | 87 +++++++++++++++++++---------------------- setuptools/tests/test_config.py | 54 +------------------------ 4 files changed, 57 insertions(+), 116 deletions(-) diff --git a/changelog.d/1753.change.rst b/changelog.d/1753.change.rst index 0f27bb2e..c8b68026 100644 --- a/changelog.d/1753.change.rst +++ b/changelog.d/1753.change.rst @@ -1 +1,4 @@ -Added a ``literal_attr:`` config directive to support reading versions from attributes of modules that import third-party modules +``attr:`` now extracts variables through rudimentary examination of the AST, +thereby supporting modules with third-party imports. If examining the AST +fails to find the variable, ``attr:`` falls back to the old behavior of +importing the module. diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 3e616582..c37b7ec5 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -2193,7 +2193,7 @@ Metadata and options are set in the config sections of the same name. * In some cases, complex values can be provided in dedicated subsections for clarity. -* Some keys allow ``file:``, ``attr:``, ``literal_attr:``, ``find:``, and ``find_namespace:`` directives in +* Some keys allow ``file:``, ``attr:``, ``find:``, and ``find_namespace:`` directives in order to cover common usecases. * Unknown keys are ignored. @@ -2291,13 +2291,10 @@ Special directives: * ``attr:`` - Value is read from a module attribute. ``attr:`` supports callables and iterables; unsupported types are cast using ``str()``. -* ``literal_attr:`` — Like ``attr:``, except that the value is parsed using - ``ast.literal_eval()`` instead of by importing the module. This allows one - to specify an attribute of a module that imports one or more third-party - modules without having to install those modules first; as a downside, - ``literal_attr:`` only supports variables that are assigned constant - expressions, not more complex assignments like ``__version__ = - '.'.join(map(str, (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)))``. + In order to support the common case of a literal value assigned to a variable + in a module containing (directly or indirectly) third-party imports, + ``attr:`` first tries to read the value from the module by examining the + module's AST. If that fails, ``attr:`` falls back to importing the module. * ``file:`` - Value is read from a list of files and then concatenated @@ -2314,14 +2311,14 @@ Metadata The aliases given below are supported for compatibility reasons, but their use is not advised. -============================== ================= ================================ =============== ===== -Key Aliases Type Minimum Version Notes -============================== ================= ================================ =============== ===== +============================== ================= ================= =============== ===== +Key Aliases Type Minimum Version Notes +============================== ================= ================= =============== ===== name str -version attr:, literal_attr:, file:, str 39.2.0 (1) +version attr:, file:, str 39.2.0 (1) url home-page str download_url download-url str -project_urls dict 38.3.0 +project_urls dict 38.3.0 author str author_email author-email str maintainer str @@ -2332,13 +2329,13 @@ license_file str license_files list-comma description summary file:, str long_description long-description file:, str -long_description_content_type str 38.6.0 +long_description_content_type str 38.6.0 keywords list-comma platforms platform list-comma provides list-comma requires list-comma obsoletes list-comma -============================== ================= ================================ =============== ===== +============================== ================= ================= =============== ===== .. note:: A version loaded using the ``file:`` directive must comply with PEP 440. diff --git a/setuptools/config.py b/setuptools/config.py index d1456cac..0a2f51e2 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -317,22 +317,15 @@ class ConfigHandler: Examples: attr: package.attr attr: package.module.attr - literal_attr: package.attr - literal_attr: package.module.attr :param str value: :rtype: str """ attr_directive = 'attr:' - literal_attr_directive = 'literal_attr:' - if value.startswith(attr_directive): - directive = attr_directive - elif value.startswith(literal_attr_directive): - directive = literal_attr_directive - else: + if not value.startswith(attr_directive): return value - attrs_path = value.replace(directive, '').strip().split('.') + attrs_path = value.replace(attr_directive, '').strip().split('.') attr_name = attrs_path.pop() module_name = '.'.join(attrs_path) @@ -352,50 +345,50 @@ class ConfigHandler: elif '' in package_dir: # A custom parent directory was specified for all root modules parent_path = os.path.join(os.getcwd(), package_dir['']) - if directive == attr_directive: + + fpath = os.path.join(parent_path, *module_name.split('.')) + if os.path.exists(fpath + '.py'): + fpath += '.py' + elif os.path.isdir(fpath): + fpath = os.path.join(fpath, '__init__.py') + else: + raise DistutilsOptionError('Could not find module ' + module_name) + with open(fpath, 'rb') as fp: + src = fp.read() + found = False + top_level = ast.parse(src) + for statement in top_level.body: + if isinstance(statement, ast.Assign): + for target in statement.targets: + if isinstance(target, ast.Name) \ + and target.id == attr_name: + try: + value = ast.literal_eval(statement.value) + except ValueError: + found = False + else: + found = True + elif isinstance(target, ast.Tuple) \ + and any(isinstance(t, ast.Name) and t.id == attr_name + for t in target.elts): + try: + stmnt_value = ast.literal_eval(statement.value) + except ValueError: + found = False + else: + for t, v in zip(target.elts, stmnt_value): + if isinstance(t, ast.Name) \ + and t.id == attr_name: + value = v + found = True + if not found: + # Fall back to extracting attribute via importing sys.path.insert(0, parent_path) try: module = import_module(module_name) value = getattr(module, attr_name) finally: sys.path = sys.path[1:] - - elif directive == literal_attr_directive: - fpath = os.path.join(parent_path, *module_name.split('.')) - if os.path.exists(fpath + '.py'): - fpath += '.py' - elif os.path.isdir(fpath): - fpath = os.path.join(fpath, '__init__.py') - else: - raise DistutilsOptionError( - 'Could not find module ' + module_name - ) - with open(fpath, 'rb') as fp: - src = fp.read() - found = False - top_level = ast.parse(src) - for statement in top_level.body: - if isinstance(statement, ast.Assign): - for target in statement.targets: - if isinstance(target, ast.Name) \ - and target.id == attr_name: - value = ast.literal_eval(statement.value) - found = True - elif isinstance(target, ast.Tuple) \ - and any(isinstance(t, ast.Name) and t.id==attr_name - for t in target.elts): - stmnt_value = ast.literal_eval(statement.value) - for t,v in zip(target.elts, stmnt_value): - if isinstance(t, ast.Name) \ - and t.id == attr_name: - value = v - found = True - if not found: - raise DistutilsOptionError( - 'No literal assignment to {!r} found in file' - .format(attr_name) - ) - return value @classmethod diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 03e6916b..d8347c78 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -103,7 +103,7 @@ class TestConfigurationReader: 'version = attr: none.VERSION\n' 'keywords = one, two\n' ) - with pytest.raises(ImportError): + with pytest.raises(DistutilsOptionError): read_configuration('%s' % config) config_dict = read_configuration( @@ -300,25 +300,6 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' - def test_literal_version(self, tmpdir): - - _, config = fake_env( - tmpdir, - '[metadata]\n' - 'version = literal_attr: fake_package.VERSION\n' - ) - with get_dist(tmpdir) as dist: - assert dist.metadata.version == '1.2.3' - - config.write( - '[metadata]\n' - 'version = literal_attr: fake_package.VERSION_MAJOR\n' - ) - with get_dist(tmpdir) as dist: - assert dist.metadata.version == '1' - - subpack = tmpdir.join('fake_package').mkdir('subpackage') - subpack.join('__init__.py').write('') subpack.join('submodule.py').write( 'import third_party_module\n' 'VERSION = (2016, 11, 26)' @@ -363,17 +344,6 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' - config.write( - '[metadata]\n' - 'version = literal_attr: fake_package_simple.VERSION\n' - '[options]\n' - 'package_dir =\n' - ' = src\n' - ) - - with get_dist(tmpdir) as dist: - assert dist.metadata.version == '1.2.3' - def test_version_with_package_dir_rename(self, tmpdir): _, config = fake_env( @@ -389,17 +359,6 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' - config.write( - '[metadata]\n' - 'version = literal_attr: fake_package_rename.VERSION\n' - '[options]\n' - 'package_dir =\n' - ' fake_package_rename = fake_dir\n' - ) - - with get_dist(tmpdir) as dist: - assert dist.metadata.version == '1.2.3' - def test_version_with_package_dir_complex(self, tmpdir): _, config = fake_env( @@ -415,17 +374,6 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' - config.write( - '[metadata]\n' - 'version = literal_attr: fake_package_complex.VERSION\n' - '[options]\n' - 'package_dir =\n' - ' fake_package_complex = src/fake_dir\n' - ) - - with get_dist(tmpdir) as dist: - assert dist.metadata.version == '1.2.3' - def test_unknown_meta_item(self, tmpdir): fake_env( -- cgit v1.2.1 From e1824c093bf89e8875ddd329f316b9ed3e7dd533 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 May 2020 19:54:13 -0400 Subject: Extract StaticModule and patch_path helpers. --- setuptools/config.py | 88 +++++++++++++++++++++-------------------- setuptools/tests/test_config.py | 2 +- 2 files changed, 47 insertions(+), 43 deletions(-) diff --git a/setuptools/config.py b/setuptools/config.py index 0a2f51e2..cd1b115e 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -10,6 +10,7 @@ from collections import defaultdict from functools import partial from functools import wraps from importlib import import_module +import contextlib from distutils.errors import DistutilsOptionError, DistutilsFileError from setuptools.extern.packaging.version import LegacyVersion, parse @@ -20,6 +21,44 @@ from setuptools.extern.six import string_types, PY3 __metaclass__ = type +class StaticModule: + """ + Attempt to load the module by the name + """ + def __init__(self, name): + spec = importlib.util.find_spec(module_name) + with open(spec.origin) as strm: + src = strm.read() + module = ast.parse(src) + vars(self).update(locals()) + del self.self + + def __getattr__(self, attr): + try: + return next( + ast.literal_eval(statement.value) + for statement in self.module.body + if isinstance(statement, ast.Assign) + for target in statement.targets + if isinstance(target, ast.Name) and target.id == attr + ) + except Exception: + raise AttributeError( + "{self.name} has no attribute {attr}".format(**locals())) + + +@contextlib.contextmanager +def patch_path(path): + """ + Add path to front of sys.path for the duration of the context. + """ + try: + sys.path.insert(0, path) + yield + finally: + sys.path.remove(path) + + def read_configuration( filepath, find_others=False, ignore_option_errors=False): """Read given configuration file and returns options from it as a dict. @@ -346,50 +385,15 @@ class ConfigHandler: # A custom parent directory was specified for all root modules parent_path = os.path.join(os.getcwd(), package_dir['']) - fpath = os.path.join(parent_path, *module_name.split('.')) - if os.path.exists(fpath + '.py'): - fpath += '.py' - elif os.path.isdir(fpath): - fpath = os.path.join(fpath, '__init__.py') - else: - raise DistutilsOptionError('Could not find module ' + module_name) - with open(fpath, 'rb') as fp: - src = fp.read() - found = False - top_level = ast.parse(src) - for statement in top_level.body: - if isinstance(statement, ast.Assign): - for target in statement.targets: - if isinstance(target, ast.Name) \ - and target.id == attr_name: - try: - value = ast.literal_eval(statement.value) - except ValueError: - found = False - else: - found = True - elif isinstance(target, ast.Tuple) \ - and any(isinstance(t, ast.Name) and t.id == attr_name - for t in target.elts): - try: - stmnt_value = ast.literal_eval(statement.value) - except ValueError: - found = False - else: - for t, v in zip(target.elts, stmnt_value): - if isinstance(t, ast.Name) \ - and t.id == attr_name: - value = v - found = True - if not found: - # Fall back to extracting attribute via importing - sys.path.insert(0, parent_path) + with patch_path(parent_path): try: + # attempt to load value statically + return getattr(StaticModule(module_name), attr_name) + except Exception: + # fallback to simple import module = import_module(module_name) - value = getattr(module, attr_name) - finally: - sys.path = sys.path[1:] - return value + + return getattr(module, attr_name) @classmethod def _get_parser_compound(cls, *parse_methods): diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index d8347c78..961f8d42 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -103,7 +103,7 @@ class TestConfigurationReader: 'version = attr: none.VERSION\n' 'keywords = one, two\n' ) - with pytest.raises(DistutilsOptionError): + with pytest.raises(ImportError): read_configuration('%s' % config) config_dict = read_configuration( -- cgit v1.2.1 From 43bbaa5827d38eede4ca8c837c9fc4994f9ab665 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 May 2020 20:13:05 -0400 Subject: Fix imports --- setuptools/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/config.py b/setuptools/config.py index cd1b115e..b39ac718 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -6,10 +6,10 @@ import sys import warnings import functools +import importlib from collections import defaultdict from functools import partial from functools import wraps -from importlib import import_module import contextlib from distutils.errors import DistutilsOptionError, DistutilsFileError @@ -391,7 +391,7 @@ class ConfigHandler: return getattr(StaticModule(module_name), attr_name) except Exception: # fallback to simple import - module = import_module(module_name) + module = importlib.import_module(module_name) return getattr(module, attr_name) -- cgit v1.2.1 From a11c8eac4bf7e1b97f489395565d96076285617d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 May 2020 20:18:54 -0400 Subject: Alter test so it actually triggers the intended behavior. --- setuptools/tests/test_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 961f8d42..6840a8e2 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -300,14 +300,14 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' - subpack.join('submodule.py').write( + subpack.join('othersub.py').write( 'import third_party_module\n' 'VERSION = (2016, 11, 26)' ) config.write( '[metadata]\n' - 'version = attr: fake_package.subpackage.submodule.VERSION\n' + 'version = attr: fake_package.subpackage.othersub.VERSION\n' ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' -- cgit v1.2.1 From af199e99549df07f9457567a26a0da8af069f513 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 May 2020 20:23:25 -0400 Subject: Fix error in StaticModule implementation. --- setuptools/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/config.py b/setuptools/config.py index b39ac718..45df2e3f 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -26,7 +26,7 @@ class StaticModule: Attempt to load the module by the name """ def __init__(self, name): - spec = importlib.util.find_spec(module_name) + spec = importlib.util.find_spec(name) with open(spec.origin) as strm: src = strm.read() module = ast.parse(src) -- cgit v1.2.1 From 7681ff9f70f33651f40c7b64a8186471a7014515 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 May 2020 20:57:15 -0400 Subject: Delete packages from sys.modules --- setuptools/tests/test_config.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 6840a8e2..942050e0 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import sys import contextlib + import pytest from distutils.errors import DistutilsOptionError, DistutilsFileError @@ -300,6 +302,9 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' + del sys.modules['fake_package'] + del sys.modules['fake_package.subpackage'] + subpack.join('othersub.py').write( 'import third_party_module\n' 'VERSION = (2016, 11, 26)' -- cgit v1.2.1 From 4c62d634784a935eb0fbeedc174a25b82f05e1d6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 May 2020 21:21:30 -0400 Subject: Update test to create separate subpackages. Hoping that avoids issues with caching. --- setuptools/tests/test_config.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 942050e0..eeac32ce 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -import sys import contextlib import pytest @@ -291,28 +290,27 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '1' - subpack = tmpdir.join('fake_package').mkdir('subpackage') - subpack.join('__init__.py').write('') - subpack.join('submodule.py').write('VERSION = (2016, 11, 26)') + sub_a = tmpdir.join('fake_package').mkdir('subpkg_a') + sub_a.join('__init__.py').write('') + sub_a.join('mod.py').write('VERSION = (2016, 11, 26)') config.write( '[metadata]\n' - 'version = attr: fake_package.subpackage.submodule.VERSION\n' + 'version = attr: fake_package.subpkg_a.mod.VERSION\n' ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' - del sys.modules['fake_package'] - del sys.modules['fake_package.subpackage'] - - subpack.join('othersub.py').write( + sub_b = tmpdir.join('fake_package').mkdir('subpkg_b') + sub_b.join('__init__.py').write('') + sub_b.join('mod.py').write( 'import third_party_module\n' 'VERSION = (2016, 11, 26)' ) config.write( '[metadata]\n' - 'version = attr: fake_package.subpackage.othersub.VERSION\n' + 'version = attr: fake_package.subpkg_b.mod.VERSION\n' ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' -- cgit v1.2.1 From 55456fe32ab2b5d7a4d476149ba935dbfd6e5fca Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 May 2020 21:45:40 -0400 Subject: Try constructing the fake package at the beginning of the test. --- setuptools/tests/test_config.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index eeac32ce..77b853eb 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -54,6 +54,7 @@ def fake_env( ' return [3, 4, 5, "dev"]\n' '\n' ) + return package_dir, config @@ -268,11 +269,23 @@ class TestMetadata: def test_version(self, tmpdir): - _, config = fake_env( + package_dir, config = fake_env( tmpdir, '[metadata]\n' 'version = attr: fake_package.VERSION\n' ) + + sub_a = package_dir.mkdir('subpkg_a') + sub_a.join('__init__.py').write('') + sub_a.join('mod.py').write('VERSION = (2016, 11, 26)') + + sub_b = package_dir.mkdir('subpkg_b') + sub_b.join('__init__.py').write('') + sub_b.join('mod.py').write( + 'import third_party_module\n' + 'VERSION = (2016, 11, 26)' + ) + with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' @@ -290,10 +303,6 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '1' - sub_a = tmpdir.join('fake_package').mkdir('subpkg_a') - sub_a.join('__init__.py').write('') - sub_a.join('mod.py').write('VERSION = (2016, 11, 26)') - config.write( '[metadata]\n' 'version = attr: fake_package.subpkg_a.mod.VERSION\n' @@ -301,13 +310,6 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' - sub_b = tmpdir.join('fake_package').mkdir('subpkg_b') - sub_b.join('__init__.py').write('') - sub_b.join('mod.py').write( - 'import third_party_module\n' - 'VERSION = (2016, 11, 26)' - ) - config.write( '[metadata]\n' 'version = attr: fake_package.subpkg_b.mod.VERSION\n' -- cgit v1.2.1 From 39a37c0758f43b130e5163156facffbbe89cf9fa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 May 2020 22:18:25 -0400 Subject: Disable test on Python 2. --- changelog.d/1753.change.rst | 4 ++-- setuptools/tests/test_config.py | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/changelog.d/1753.change.rst b/changelog.d/1753.change.rst index c8b68026..08fa9ea4 100644 --- a/changelog.d/1753.change.rst +++ b/changelog.d/1753.change.rst @@ -1,4 +1,4 @@ ``attr:`` now extracts variables through rudimentary examination of the AST, -thereby supporting modules with third-party imports. If examining the AST +thereby supporting modules with third-party imports. If examining the AST fails to find the variable, ``attr:`` falls back to the old behavior of -importing the module. +importing the module. Works on Python 3 only. diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 77b853eb..67992c04 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -10,6 +10,7 @@ from mock import patch from setuptools.dist import Distribution, _Distribution from setuptools.config import ConfigHandler, read_configuration from setuptools.extern.six.moves import configparser +from setuptools.extern import six from . import py2_only, py3_only from .textwrap import DALS @@ -310,6 +311,10 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' + if six.PY2: + # static version loading is unsupported on Python 2 + return + config.write( '[metadata]\n' 'version = attr: fake_package.subpkg_b.mod.VERSION\n' -- cgit v1.2.1 From b92164bd9ecc08374e1e5e810cf0bc37c8fb9aca Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 May 2020 22:20:49 -0400 Subject: =?UTF-8?q?Bump=20version:=2046.3.1=20=E2=86=92=2046.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 9 +++++++++ changelog.d/1753.change.rst | 4 ---- setup.cfg | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) delete mode 100644 changelog.d/1753.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4ee92185..72d02f2b 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 46.3.1 +current_version = 46.4.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index fd3c16ba..ea667028 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,12 @@ +v46.4.0 +------- + +* #1753: ``attr:`` now extracts variables through rudimentary examination of the AST, + thereby supporting modules with third-party imports. If examining the AST + fails to find the variable, ``attr:`` falls back to the old behavior of + importing the module. Works on Python 3 only. + + v46.3.1 ------- diff --git a/changelog.d/1753.change.rst b/changelog.d/1753.change.rst deleted file mode 100644 index 08fa9ea4..00000000 --- a/changelog.d/1753.change.rst +++ /dev/null @@ -1,4 +0,0 @@ -``attr:`` now extracts variables through rudimentary examination of the AST, -thereby supporting modules with third-party imports. If examining the AST -fails to find the variable, ``attr:`` falls back to the old behavior of -importing the module. Works on Python 3 only. diff --git a/setup.cfg b/setup.cfg index 3933714b..72d4dce9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 46.3.1 +version = 46.4.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From c849875356f0ca3b12257cd22a2a1c0bd1603884 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Tue, 19 May 2020 00:52:04 -0500 Subject: use a less confusing example for requirements parsing --- docs/pkg_resources.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pkg_resources.txt b/docs/pkg_resources.txt index 71568c1a..f2e554f4 100644 --- a/docs/pkg_resources.txt +++ b/docs/pkg_resources.txt @@ -594,7 +594,7 @@ Requirements Parsing FooProject >= 1.2 Fizzy [foo, bar] - PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1 + PickyThing>1.6,<=1.9,!=1.8.6 SomethingWhoseVersionIDontCareAbout SomethingWithMarker[foo]>1.0;python_version<"2.7" -- cgit v1.2.1 From 5210488f65e41038e5721d31792fae784c39d649 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 20 May 2020 16:37:25 +0200 Subject: bpo-40698: Improve distutils upload hash digests (GH-20260) - Fix upload test on systems that blocks MD5 - Add SHA2-256 and Blake2b-256 digests based on new Warehous and twine specs. Signed-off-by: Christian Heimes --- command/upload.py | 22 +++++++++++++++++++++- tests/test_upload.py | 24 ++++++++++++++++++++---- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/command/upload.py b/command/upload.py index d822ba01..95e9fda1 100644 --- a/command/upload.py +++ b/command/upload.py @@ -16,6 +16,16 @@ from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log + +# PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256) +# https://bugs.python.org/issue40698 +_FILE_CONTENT_DIGESTS = { + "md5_digest": getattr(hashlib, "md5", None), + "sha256_digest": getattr(hashlib, "sha256", None), + "blake2_256_digest": getattr(hashlib, "blake2b", None), +} + + class upload(PyPIRCCommand): description = "upload binary package to PyPI" @@ -87,6 +97,7 @@ class upload(PyPIRCCommand): content = f.read() finally: f.close() + meta = self.distribution.metadata data = { # action @@ -101,7 +112,6 @@ class upload(PyPIRCCommand): 'content': (os.path.basename(filename),content), 'filetype': command, 'pyversion': pyversion, - 'md5_digest': hashlib.md5(content).hexdigest(), # additional meta-data 'metadata_version': '1.0', @@ -123,6 +133,16 @@ class upload(PyPIRCCommand): data['comment'] = '' + # file content digests + for digest_name, digest_cons in _FILE_CONTENT_DIGESTS.items(): + if digest_cons is None: + continue + try: + data[digest_name] = digest_cons(content).hexdigest() + except ValueError: + # hash digest not available or blocked by security policy + pass + if self.sign: with open(filename + ".asc", "rb") as f: data['gpg_signature'] = (os.path.basename(filename) + ".asc", diff --git a/tests/test_upload.py b/tests/test_upload.py index c17d8e7d..bca5516d 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -130,14 +130,30 @@ class uploadTestCase(BasePyPIRCCommandTestCase): # what did we send ? headers = dict(self.last_open.req.headers) - self.assertEqual(headers['Content-length'], '2162') + self.assertGreaterEqual(int(headers['Content-length']), 2162) content_type = headers['Content-type'] self.assertTrue(content_type.startswith('multipart/form-data')) self.assertEqual(self.last_open.req.get_method(), 'POST') expected_url = 'https://upload.pypi.org/legacy/' self.assertEqual(self.last_open.req.get_full_url(), expected_url) - self.assertTrue(b'xxx' in self.last_open.req.data) - self.assertIn(b'protocol_version', self.last_open.req.data) + data = self.last_open.req.data + self.assertIn(b'xxx',data) + self.assertIn(b'protocol_version', data) + self.assertIn(b'sha256_digest', data) + self.assertIn( + b'cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf' + b'6860', + data + ) + if b'md5_digest' in data: + self.assertIn(b'f561aaf6ef0bf14d4208bb46a4ccb3ad', data) + if b'blake2_256_digest' in data: + self.assertIn( + b'b6f289a27d4fe90da63c503bfe0a9b761a8f76bb86148565065f040be' + b'6d1c3044cf7ded78ef800509bccb4b648e507d88dc6383d67642aadcc' + b'ce443f1534330a', + data + ) # The PyPI response body was echoed results = self.get_logs(INFO) @@ -166,7 +182,7 @@ class uploadTestCase(BasePyPIRCCommandTestCase): cmd.run() headers = dict(self.last_open.req.headers) - self.assertEqual(headers['Content-length'], '2172') + self.assertGreaterEqual(int(headers['Content-length']), 2172) self.assertIn(b'long description\r', self.last_open.req.data) def test_upload_fails(self): -- cgit v1.2.1 From 46dab46d6debf69331b646bff11052dc731d4ae4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 24 May 2020 15:50:07 -0400 Subject: Add landing page for Python 2 sunset. Ref #2094. --- ...uptools-warns-about-python-2-incompatibility.md | 5 ++++ docs/python 2 sunset.txt | 35 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 docs/python 2 sunset.txt diff --git a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md index 2f5fe53d..e2d5ed53 100644 --- a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md +++ b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md @@ -40,6 +40,11 @@ try them first. --> - Python installed how: - Virtualenv version (if using virtualenv): n/a +Command(s) that triggered the warning/error (and output): + +``` +``` + Command(s) used to install setuptools (and output): ``` diff --git a/docs/python 2 sunset.txt b/docs/python 2 sunset.txt new file mode 100644 index 00000000..543487d8 --- /dev/null +++ b/docs/python 2 sunset.txt @@ -0,0 +1,35 @@ +:orphan: + +Python 2 Sunset +=============== + +Since January 2020 and the release of Setuptools 45, Python 2 is no longer supported by the most current release (`discussion `_). Setuptools as a project continues to support Python 2 with bugfixes and important features on Setuptools 44.x. + +By design, most users will be unaffected by this change. That's because Setuptools 45 declares its supported Python versions to exclude Python 2.7, and installers such as pip 9 or later will honor this declaration and prevent installation of Setuptools 45 or later in Python 2 environments. + +Users that do import any portion of Setuptools 45 or later on Python 2 are directed to this documentation to provide guidance on how to work around the issues. + +Workarounds +----------- + +The best recommendation is to avoid Python 2 and move to Python 3 where possible. This project acknowledges that not all environments can drop Python 2 support, so provides other options. + +In less common scenarios, later versions of Setuptools can be installed on unsupported Python versions. In these environments, the installer is advised to first install ``setuptools<45`` to "pin Setuptools" to a compatible version. + +- When using older versions of pip (before 9.0), the ``Requires-Python`` directive is not honored and invalid versions can be installed. Users are advised first to upgrade pip and retry or to pin Setuptools. Use ``pip --version`` to determine the version of pip. +- When using ``easy_install``, ``Requires-Python`` is not honored and later versions can be installed. In this case, users are advised to pin Setuptools. This applies to ``setup.py install`` invocations as well, as they use Setuptools under the hood. + +It's still not working +---------------------- + +If after trying the above steps, the Python environment still has incompatible versions of Setuptools installed, here are some things to try. + +1. Uninstall and reinstall Setuptools. Run ``pip uninstall -y setuptools`` for the relevant environment. Repeat until there is no Setuptools installed. Then ``pip install setuptools``. + +2. If possible, attempt to replicate the problem in a second environment (virtual machine, friend's computer, etc). If the issue is isolated to just one unique enviornment, first determine what is different about those environments (or reinstall/reset the failing one to defaults). + +3. End users who are not themselves the maintainers for the package they are trying to install should contact the support channels for the relevant application. Please be respectful by searching for existing issues and following the latest guidance before reaching out for support. If you do file an issue, be sure to give as much detail as possible to help the maintainers understand how you encountered the issue following their recommended guidance. + +4. Reach out to your local support groups. There's a good chance someone near you has the expertise and willingness to help. + +5. If all else fails, `file this template `_ with Setuptools. Please complete the whole template, providing as much detail about how you encountered the issue. Setuptools maintainers will summarily close tickets filed without any meaningful detail or engagement with the issue. -- cgit v1.2.1 From 64b1a6fa12f7d4f6508008541adbebdcd6ee20a8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 24 May 2020 16:06:38 -0400 Subject: Force fail on Python 2. When doing so, emit an error that directs users to the latest guidance. Fixes #2094. --- ...setuptools-warns-about-python-2-incompatibility.md | 1 + .travis.yml | 5 ----- pkg_resources/py2_warn.py | 19 +++++++------------ 3 files changed, 8 insertions(+), 17 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md index e2d5ed53..b5fe8abf 100644 --- a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md +++ b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md @@ -28,6 +28,7 @@ Your first course of action should be to reason about how you managed to get an +- [ ] Read [Python 2 Sunset docs](https://setuptools.readthedocs.io/en/latest/python%202%20sunset.html). - [ ] Python 2 is required for this application. - [ ] I maintain the software that installs Setuptools (if not, please contact that project). - [ ] Setuptools installed with pip 9 or later. diff --git a/.travis.yml b/.travis.yml index 3e97f353..21716ea6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,6 @@ language: python jobs: fast_finish: true include: - - &latest_py2 - python: 2.7 - env: TOXENV=py27 - - <<: *latest_py2 - env: LANG=C TOXENV=py27 - python: pypy3 env: DISABLE_COVERAGE=1 # Don't run coverage on pypy (too slow). - python: 3.5 diff --git a/pkg_resources/py2_warn.py b/pkg_resources/py2_warn.py index bfc35234..00cc8bc7 100644 --- a/pkg_resources/py2_warn.py +++ b/pkg_resources/py2_warn.py @@ -4,18 +4,13 @@ import textwrap msg = textwrap.dedent(""" - You are running Setuptools on Python 2, which is no longer - supported and - >>> SETUPTOOLS WILL STOP WORKING <<< - in a subsequent release (no sooner than 2020-04-20). - Please ensure you are installing - Setuptools using pip 9.x or later or pin to `setuptools<45` - in your environment. - If you have done those things and are still encountering - this message, please follow up at - https://bit.ly/setuptools-py2-warning. + Encountered a version of Setuptools that no longer supports + this version of Python. Please head to + https://bit.ly/setuptools-py2-warning for support. """) -pre = "Setuptools will stop working on Python 2\n" +pre = "Setuptools no longer works on Python 2\n" -sys.version_info < (3,) and warnings.warn(pre + "*" * 60 + msg + "*" * 60) +if sys.version_info < (3,): + warnings.warn(pre + "*" * 60 + msg + "*" * 60) + raise SystemExit(32) -- cgit v1.2.1 From e23403355f87272c8b0c027b98bf18309c38fa31 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 24 May 2020 16:14:54 -0400 Subject: Prefer imperative voice --- docs/python 2 sunset.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/python 2 sunset.txt b/docs/python 2 sunset.txt index 543487d8..6b8016fb 100644 --- a/docs/python 2 sunset.txt +++ b/docs/python 2 sunset.txt @@ -28,8 +28,8 @@ If after trying the above steps, the Python environment still has incompatible v 2. If possible, attempt to replicate the problem in a second environment (virtual machine, friend's computer, etc). If the issue is isolated to just one unique enviornment, first determine what is different about those environments (or reinstall/reset the failing one to defaults). -3. End users who are not themselves the maintainers for the package they are trying to install should contact the support channels for the relevant application. Please be respectful by searching for existing issues and following the latest guidance before reaching out for support. If you do file an issue, be sure to give as much detail as possible to help the maintainers understand how you encountered the issue following their recommended guidance. +3. End users who are not themselves the maintainers for the package they are trying to install should contact the support channels for the relevant application. Please be respectful by searching for existing issues and following the latest guidance before reaching out for support. When filing an issue, be sure to give as much detail as possible to help the maintainers understand what factors led to the issue after following their recommended guidance. -4. Reach out to your local support groups. There's a good chance someone near you has the expertise and willingness to help. +4. Reach out to your local support groups. There's a good chance someone nearby has the expertise and willingness to help. -5. If all else fails, `file this template `_ with Setuptools. Please complete the whole template, providing as much detail about how you encountered the issue. Setuptools maintainers will summarily close tickets filed without any meaningful detail or engagement with the issue. +5. If all else fails, `file this template `_ with Setuptools. Please complete the whole template, providing as much detail about what factors led to the issue. Setuptools maintainers will summarily close tickets filed without any meaningful detail or engagement with the issue. -- cgit v1.2.1 From eba78d63e3a4b181153a011caec2a3e50eecfee0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 24 May 2020 19:57:55 -0400 Subject: Remove superfluous RequirementParseError. Ref #1832. --- pkg_resources/__init__.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index edd3d2e8..56be3c22 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -3068,11 +3068,6 @@ def issue_warning(*args, **kw): warnings.warn(stacklevel=level + 1, *args, **kw) -class RequirementParseError(ValueError): - def __str__(self): - return ' '.join(self.args) - - def parse_requirements(strs): """Yield ``Requirement`` objects for each specification in `strs` @@ -3098,10 +3093,7 @@ def parse_requirements(strs): class Requirement(packaging.requirements.Requirement): def __init__(self, requirement_string): """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" - try: - super(Requirement, self).__init__(requirement_string) - except packaging.requirements.InvalidRequirement as e: - raise RequirementParseError(str(e)) + super(Requirement, self).__init__(requirement_string) self.unsafe_name = self.name project_name = safe_name(self.name) self.project_name, self.key = project_name, project_name.lower() -- cgit v1.2.1 From 7c571c264e0f49965ee2dabbcb9f360aa9c91e28 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 10:31:53 -0400 Subject: Reword and simplify note about supported versions. --- .../ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md index b5fe8abf..1a4f58f2 100644 --- a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md +++ b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md @@ -13,7 +13,7 @@ Please DO NOT SUBMIT this template without first investigating the issue and ans If you did not intend to use this template, but only meant to file a blank issue, just hit the back button and click "Open a blank issue". -It's by design that Setuptools 45 and later will stop working on Python 2. To ease the transition, Setuptools 45 was released to continue to have Python 2 compatibility, but emit a strenuous warning that it will stop working. +Setuptools 45 dropped support for Python 2 with a strenuous warning and Setuptools 47 fails to run on Python 2. In most cases, using pip 9 or later to install Setuptools from PyPI or any index supporting the Requires-Python metadata will do the right thing and install Setuptools 44.x on Python 2. -- cgit v1.2.1 From e0b3acb4868f8283565fae462b71fb75d1c5a2bd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 10:33:46 -0400 Subject: Add changelog entry. --- changelog.d/2094.breaking.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2094.breaking.rst diff --git a/changelog.d/2094.breaking.rst b/changelog.d/2094.breaking.rst new file mode 100644 index 00000000..c278d0fd --- /dev/null +++ b/changelog.d/2094.breaking.rst @@ -0,0 +1 @@ +Setuptools now actively crashes under Python 2. Python 3.5 or later is required. Users of Python 2 should use ``setuptools<45``. -- cgit v1.2.1 From 96f0781f08b3da918ffaafae656623c3c3152682 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 10:39:33 -0400 Subject: Wrap lines and remove newlines between enumerated paragraphs. --- docs/python 2 sunset.txt | 68 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/docs/python 2 sunset.txt b/docs/python 2 sunset.txt index 6b8016fb..a6d768cb 100644 --- a/docs/python 2 sunset.txt +++ b/docs/python 2 sunset.txt @@ -3,33 +3,67 @@ Python 2 Sunset =============== -Since January 2020 and the release of Setuptools 45, Python 2 is no longer supported by the most current release (`discussion `_). Setuptools as a project continues to support Python 2 with bugfixes and important features on Setuptools 44.x. +Since January 2020 and the release of Setuptools 45, Python 2 is no longer +supported by the most current release (`discussion +`_). Setuptools as a project +continues to support Python 2 with bugfixes and important features on +Setuptools 44.x. -By design, most users will be unaffected by this change. That's because Setuptools 45 declares its supported Python versions to exclude Python 2.7, and installers such as pip 9 or later will honor this declaration and prevent installation of Setuptools 45 or later in Python 2 environments. +By design, most users will be unaffected by this change. That's because +Setuptools 45 declares its supported Python versions to exclude Python 2.7, +and installers such as pip 9 or later will honor this declaration and prevent +installation of Setuptools 45 or later in Python 2 environments. -Users that do import any portion of Setuptools 45 or later on Python 2 are directed to this documentation to provide guidance on how to work around the issues. +Users that do import any portion of Setuptools 45 or later on Python 2 are +directed to this documentation to provide guidance on how to work around the +issues. Workarounds ----------- -The best recommendation is to avoid Python 2 and move to Python 3 where possible. This project acknowledges that not all environments can drop Python 2 support, so provides other options. +The best recommendation is to avoid Python 2 and move to Python 3 where +possible. This project acknowledges that not all environments can drop Python +2 support, so provides other options. -In less common scenarios, later versions of Setuptools can be installed on unsupported Python versions. In these environments, the installer is advised to first install ``setuptools<45`` to "pin Setuptools" to a compatible version. +In less common scenarios, later versions of Setuptools can be installed on +unsupported Python versions. In these environments, the installer is advised +to first install ``setuptools<45`` to "pin Setuptools" to a compatible +version. -- When using older versions of pip (before 9.0), the ``Requires-Python`` directive is not honored and invalid versions can be installed. Users are advised first to upgrade pip and retry or to pin Setuptools. Use ``pip --version`` to determine the version of pip. -- When using ``easy_install``, ``Requires-Python`` is not honored and later versions can be installed. In this case, users are advised to pin Setuptools. This applies to ``setup.py install`` invocations as well, as they use Setuptools under the hood. +- When using older versions of pip (before 9.0), the ``Requires-Python`` + directive is not honored and invalid versions can be installed. Users are + advised first to upgrade pip and retry or to pin Setuptools. Use ``pip + --version`` to determine the version of pip. +- When using ``easy_install``, ``Requires-Python`` is not honored and later + versions can be installed. In this case, users are advised to pin + Setuptools. This applies to ``setup.py install`` invocations as well, as + they use Setuptools under the hood. It's still not working ---------------------- -If after trying the above steps, the Python environment still has incompatible versions of Setuptools installed, here are some things to try. +If after trying the above steps, the Python environment still has incompatible +versions of Setuptools installed, here are some things to try. -1. Uninstall and reinstall Setuptools. Run ``pip uninstall -y setuptools`` for the relevant environment. Repeat until there is no Setuptools installed. Then ``pip install setuptools``. - -2. If possible, attempt to replicate the problem in a second environment (virtual machine, friend's computer, etc). If the issue is isolated to just one unique enviornment, first determine what is different about those environments (or reinstall/reset the failing one to defaults). - -3. End users who are not themselves the maintainers for the package they are trying to install should contact the support channels for the relevant application. Please be respectful by searching for existing issues and following the latest guidance before reaching out for support. When filing an issue, be sure to give as much detail as possible to help the maintainers understand what factors led to the issue after following their recommended guidance. - -4. Reach out to your local support groups. There's a good chance someone nearby has the expertise and willingness to help. - -5. If all else fails, `file this template `_ with Setuptools. Please complete the whole template, providing as much detail about what factors led to the issue. Setuptools maintainers will summarily close tickets filed without any meaningful detail or engagement with the issue. +1. Uninstall and reinstall Setuptools. Run ``pip uninstall -y setuptools`` for + the relevant environment. Repeat until there is no Setuptools installed. + Then ``pip install setuptools``. +2. If possible, attempt to replicate the problem in a second environment + (virtual machine, friend's computer, etc). If the issue is isolated to just + one unique enviornment, first determine what is different about those + environments (or reinstall/reset the failing one to defaults). +3. End users who are not themselves the maintainers for the package they are + trying to install should contact the support channels for the relevant + application. Please be respectful by searching for existing issues and + following the latest guidance before reaching out for support. When filing + an issue, be sure to give as much detail as possible to help the + maintainers understand what factors led to the issue after following their + recommended guidance. +4. Reach out to your local support groups. There's a good chance someone + nearby has the expertise and willingness to help. +5. If all else fails, `file this template + `_ + with Setuptools. Please complete the whole template, providing as much + detail about what factors led to the issue. Setuptools maintainers will + summarily close tickets filed without any meaningful detail or engagement + with the issue. -- cgit v1.2.1 From dbdd19d4212a35ac501c3f56987c035512ab6557 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 10:40:09 -0400 Subject: s/respectful/considerate of those projects/ --- docs/python 2 sunset.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/python 2 sunset.txt b/docs/python 2 sunset.txt index a6d768cb..f7b7ee25 100644 --- a/docs/python 2 sunset.txt +++ b/docs/python 2 sunset.txt @@ -54,11 +54,11 @@ versions of Setuptools installed, here are some things to try. environments (or reinstall/reset the failing one to defaults). 3. End users who are not themselves the maintainers for the package they are trying to install should contact the support channels for the relevant - application. Please be respectful by searching for existing issues and - following the latest guidance before reaching out for support. When filing - an issue, be sure to give as much detail as possible to help the - maintainers understand what factors led to the issue after following their - recommended guidance. + application. Please be considerate of those projects by searching for + existing issues and following the latest guidance before reaching out for + support. When filing an issue, be sure to give as much detail as possible + to help the maintainers understand what factors led to the issue after + following their recommended guidance. 4. Reach out to your local support groups. There's a good chance someone nearby has the expertise and willingness to help. 5. If all else fails, `file this template -- cgit v1.2.1 From cd7d6d56a1cf84cafea44f7cf7e357a926821f03 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 8 Nov 2017 16:41:32 -0600 Subject: [maint] move all files into subfolder --- README | 11 - __init__.py | 13 - _msvccompiler.py | 539 ------------- archive_util.py | 256 ------ bcppcompiler.py | 393 ---------- ccompiler.py | 1116 -------------------------- cmd.py | 403 ---------- command/__init__.py | 31 - command/bdist.py | 143 ---- command/bdist_dumb.py | 123 --- command/bdist_msi.py | 749 ------------------ command/bdist_rpm.py | 579 -------------- command/bdist_wininst.py | 377 --------- command/build.py | 157 ---- command/build_clib.py | 209 ----- command/build_ext.py | 754 ------------------ command/build_py.py | 416 ---------- command/build_scripts.py | 160 ---- command/check.py | 148 ---- command/clean.py | 76 -- command/command_template | 33 - command/config.py | 344 -------- command/install.py | 657 ---------------- command/install_data.py | 79 -- command/install_egg_info.py | 77 -- command/install_headers.py | 47 -- command/install_lib.py | 217 ------ command/install_scripts.py | 60 -- command/register.py | 304 -------- command/sdist.py | 494 ------------ command/upload.py | 214 ----- command/wininst-10.0-amd64.exe | Bin 222208 -> 0 bytes command/wininst-10.0.exe | Bin 190976 -> 0 bytes command/wininst-14.0-amd64.exe | Bin 587776 -> 0 bytes command/wininst-14.0.exe | Bin 458240 -> 0 bytes command/wininst-6.0.exe | Bin 61440 -> 0 bytes command/wininst-7.1.exe | Bin 65536 -> 0 bytes command/wininst-8.0.exe | Bin 61440 -> 0 bytes command/wininst-9.0-amd64.exe | Bin 224256 -> 0 bytes command/wininst-9.0.exe | Bin 196096 -> 0 bytes config.py | 130 ---- core.py | 234 ------ cygwinccompiler.py | 403 ---------- debug.py | 5 - dep_util.py | 92 --- dir_util.py | 210 ----- dist.py | 1256 ------------------------------ distutils/README | 11 + distutils/__init__.py | 13 + distutils/_msvccompiler.py | 539 +++++++++++++ distutils/archive_util.py | 256 ++++++ distutils/bcppcompiler.py | 393 ++++++++++ distutils/ccompiler.py | 1116 ++++++++++++++++++++++++++ distutils/cmd.py | 403 ++++++++++ distutils/command/__init__.py | 31 + distutils/command/bdist.py | 143 ++++ distutils/command/bdist_dumb.py | 123 +++ distutils/command/bdist_msi.py | 749 ++++++++++++++++++ distutils/command/bdist_rpm.py | 579 ++++++++++++++ distutils/command/bdist_wininst.py | 377 +++++++++ distutils/command/build.py | 157 ++++ distutils/command/build_clib.py | 209 +++++ distutils/command/build_ext.py | 754 ++++++++++++++++++ distutils/command/build_py.py | 416 ++++++++++ distutils/command/build_scripts.py | 160 ++++ distutils/command/check.py | 148 ++++ distutils/command/clean.py | 76 ++ distutils/command/command_template | 33 + distutils/command/config.py | 344 ++++++++ distutils/command/install.py | 657 ++++++++++++++++ distutils/command/install_data.py | 79 ++ distutils/command/install_egg_info.py | 77 ++ distutils/command/install_headers.py | 47 ++ distutils/command/install_lib.py | 217 ++++++ distutils/command/install_scripts.py | 60 ++ distutils/command/register.py | 304 ++++++++ distutils/command/sdist.py | 494 ++++++++++++ distutils/command/upload.py | 214 +++++ distutils/command/wininst-10.0-amd64.exe | Bin 0 -> 222208 bytes distutils/command/wininst-10.0.exe | Bin 0 -> 190976 bytes distutils/command/wininst-14.0-amd64.exe | Bin 0 -> 587776 bytes distutils/command/wininst-14.0.exe | Bin 0 -> 458240 bytes distutils/command/wininst-6.0.exe | Bin 0 -> 61440 bytes distutils/command/wininst-7.1.exe | Bin 0 -> 65536 bytes distutils/command/wininst-8.0.exe | Bin 0 -> 61440 bytes distutils/command/wininst-9.0-amd64.exe | Bin 0 -> 224256 bytes distutils/command/wininst-9.0.exe | Bin 0 -> 196096 bytes distutils/config.py | 130 ++++ distutils/core.py | 234 ++++++ distutils/cygwinccompiler.py | 403 ++++++++++ distutils/debug.py | 5 + distutils/dep_util.py | 92 +++ distutils/dir_util.py | 210 +++++ distutils/dist.py | 1256 ++++++++++++++++++++++++++++++ distutils/errors.py | 97 +++ distutils/extension.py | 240 ++++++ distutils/fancy_getopt.py | 457 +++++++++++ distutils/file_util.py | 238 ++++++ distutils/filelist.py | 327 ++++++++ distutils/log.py | 77 ++ distutils/msvc9compiler.py | 788 +++++++++++++++++++ distutils/msvccompiler.py | 643 +++++++++++++++ distutils/spawn.py | 119 +++ distutils/sysconfig.py | 555 +++++++++++++ distutils/tests/Setup.sample | 67 ++ distutils/tests/__init__.py | 42 + distutils/tests/includetest.rst | 1 + distutils/tests/support.py | 209 +++++ distutils/tests/test_archive_util.py | 394 ++++++++++ distutils/tests/test_bdist.py | 57 ++ distutils/tests/test_bdist_dumb.py | 97 +++ distutils/tests/test_bdist_msi.py | 26 + distutils/tests/test_bdist_rpm.py | 135 ++++ distutils/tests/test_bdist_wininst.py | 38 + distutils/tests/test_build.py | 56 ++ distutils/tests/test_build_clib.py | 134 ++++ distutils/tests/test_build_ext.py | 545 +++++++++++++ distutils/tests/test_build_py.py | 179 +++++ distutils/tests/test_build_scripts.py | 112 +++ distutils/tests/test_check.py | 163 ++++ distutils/tests/test_clean.py | 49 ++ distutils/tests/test_cmd.py | 126 +++ distutils/tests/test_config.py | 141 ++++ distutils/tests/test_config_cmd.py | 96 +++ distutils/tests/test_core.py | 140 ++++ distutils/tests/test_cygwinccompiler.py | 154 ++++ distutils/tests/test_dep_util.py | 80 ++ distutils/tests/test_dir_util.py | 139 ++++ distutils/tests/test_dist.py | 528 +++++++++++++ distutils/tests/test_extension.py | 69 ++ distutils/tests/test_file_util.py | 122 +++ distutils/tests/test_filelist.py | 340 ++++++++ distutils/tests/test_install.py | 249 ++++++ distutils/tests/test_install_data.py | 75 ++ distutils/tests/test_install_headers.py | 39 + distutils/tests/test_install_lib.py | 115 +++ distutils/tests/test_install_scripts.py | 82 ++ distutils/tests/test_log.py | 46 ++ distutils/tests/test_msvc9compiler.py | 184 +++++ distutils/tests/test_msvccompiler.py | 81 ++ distutils/tests/test_register.py | 323 ++++++++ distutils/tests/test_sdist.py | 492 ++++++++++++ distutils/tests/test_spawn.py | 132 ++++ distutils/tests/test_sysconfig.py | 274 +++++++ distutils/tests/test_text_file.py | 107 +++ distutils/tests/test_unixccompiler.py | 141 ++++ distutils/tests/test_upload.py | 223 ++++++ distutils/tests/test_util.py | 309 ++++++++ distutils/tests/test_version.py | 87 +++ distutils/tests/test_versionpredicate.py | 13 + distutils/text_file.py | 286 +++++++ distutils/unixccompiler.py | 328 ++++++++ distutils/util.py | 559 +++++++++++++ distutils/version.py | 347 +++++++++ distutils/versionpredicate.py | 166 ++++ errors.py | 97 --- extension.py | 240 ------ fancy_getopt.py | 457 ----------- file_util.py | 238 ------ filelist.py | 327 -------- log.py | 77 -- msvc9compiler.py | 788 ------------------- msvccompiler.py | 643 --------------- spawn.py | 119 --- sysconfig.py | 555 ------------- tests/Setup.sample | 67 -- tests/__init__.py | 42 - tests/includetest.rst | 1 - tests/support.py | 209 ----- tests/test_archive_util.py | 394 ---------- tests/test_bdist.py | 57 -- tests/test_bdist_dumb.py | 97 --- tests/test_bdist_msi.py | 26 - tests/test_bdist_rpm.py | 135 ---- tests/test_bdist_wininst.py | 38 - tests/test_build.py | 56 -- tests/test_build_clib.py | 134 ---- tests/test_build_ext.py | 545 ------------- tests/test_build_py.py | 179 ----- tests/test_build_scripts.py | 112 --- tests/test_check.py | 163 ---- tests/test_clean.py | 49 -- tests/test_cmd.py | 126 --- tests/test_config.py | 141 ---- tests/test_config_cmd.py | 96 --- tests/test_core.py | 140 ---- tests/test_cygwinccompiler.py | 154 ---- tests/test_dep_util.py | 80 -- tests/test_dir_util.py | 139 ---- tests/test_dist.py | 528 ------------- tests/test_extension.py | 69 -- tests/test_file_util.py | 122 --- tests/test_filelist.py | 340 -------- tests/test_install.py | 249 ------ tests/test_install_data.py | 75 -- tests/test_install_headers.py | 39 - tests/test_install_lib.py | 115 --- tests/test_install_scripts.py | 82 -- tests/test_log.py | 46 -- tests/test_msvc9compiler.py | 184 ----- tests/test_msvccompiler.py | 81 -- tests/test_register.py | 323 -------- tests/test_sdist.py | 492 ------------ tests/test_spawn.py | 132 ---- tests/test_sysconfig.py | 274 ------- tests/test_text_file.py | 107 --- tests/test_unixccompiler.py | 141 ---- tests/test_upload.py | 223 ------ tests/test_util.py | 309 -------- tests/test_version.py | 87 --- tests/test_versionpredicate.py | 13 - text_file.py | 286 ------- unixccompiler.py | 328 -------- util.py | 559 ------------- version.py | 347 --------- versionpredicate.py | 166 ---- 216 files changed, 23947 insertions(+), 23947 deletions(-) delete mode 100644 README delete mode 100644 __init__.py delete mode 100644 _msvccompiler.py delete mode 100644 archive_util.py delete mode 100644 bcppcompiler.py delete mode 100644 ccompiler.py delete mode 100644 cmd.py delete mode 100644 command/__init__.py delete mode 100644 command/bdist.py delete mode 100644 command/bdist_dumb.py delete mode 100644 command/bdist_msi.py delete mode 100644 command/bdist_rpm.py delete mode 100644 command/bdist_wininst.py delete mode 100644 command/build.py delete mode 100644 command/build_clib.py delete mode 100644 command/build_ext.py delete mode 100644 command/build_py.py delete mode 100644 command/build_scripts.py delete mode 100644 command/check.py delete mode 100644 command/clean.py delete mode 100644 command/command_template delete mode 100644 command/config.py delete mode 100644 command/install.py delete mode 100644 command/install_data.py delete mode 100644 command/install_egg_info.py delete mode 100644 command/install_headers.py delete mode 100644 command/install_lib.py delete mode 100644 command/install_scripts.py delete mode 100644 command/register.py delete mode 100644 command/sdist.py delete mode 100644 command/upload.py delete mode 100644 command/wininst-10.0-amd64.exe delete mode 100644 command/wininst-10.0.exe delete mode 100644 command/wininst-14.0-amd64.exe delete mode 100644 command/wininst-14.0.exe delete mode 100644 command/wininst-6.0.exe delete mode 100644 command/wininst-7.1.exe delete mode 100644 command/wininst-8.0.exe delete mode 100644 command/wininst-9.0-amd64.exe delete mode 100644 command/wininst-9.0.exe delete mode 100644 config.py delete mode 100644 core.py delete mode 100644 cygwinccompiler.py delete mode 100644 debug.py delete mode 100644 dep_util.py delete mode 100644 dir_util.py delete mode 100644 dist.py create mode 100644 distutils/README create mode 100644 distutils/__init__.py create mode 100644 distutils/_msvccompiler.py create mode 100644 distutils/archive_util.py create mode 100644 distutils/bcppcompiler.py create mode 100644 distutils/ccompiler.py create mode 100644 distutils/cmd.py create mode 100644 distutils/command/__init__.py create mode 100644 distutils/command/bdist.py create mode 100644 distutils/command/bdist_dumb.py create mode 100644 distutils/command/bdist_msi.py create mode 100644 distutils/command/bdist_rpm.py create mode 100644 distutils/command/bdist_wininst.py create mode 100644 distutils/command/build.py create mode 100644 distutils/command/build_clib.py create mode 100644 distutils/command/build_ext.py create mode 100644 distutils/command/build_py.py create mode 100644 distutils/command/build_scripts.py create mode 100644 distutils/command/check.py create mode 100644 distutils/command/clean.py create mode 100644 distutils/command/command_template create mode 100644 distutils/command/config.py create mode 100644 distutils/command/install.py create mode 100644 distutils/command/install_data.py create mode 100644 distutils/command/install_egg_info.py create mode 100644 distutils/command/install_headers.py create mode 100644 distutils/command/install_lib.py create mode 100644 distutils/command/install_scripts.py create mode 100644 distutils/command/register.py create mode 100644 distutils/command/sdist.py create mode 100644 distutils/command/upload.py create mode 100644 distutils/command/wininst-10.0-amd64.exe create mode 100644 distutils/command/wininst-10.0.exe create mode 100644 distutils/command/wininst-14.0-amd64.exe create mode 100644 distutils/command/wininst-14.0.exe create mode 100644 distutils/command/wininst-6.0.exe create mode 100644 distutils/command/wininst-7.1.exe create mode 100644 distutils/command/wininst-8.0.exe create mode 100644 distutils/command/wininst-9.0-amd64.exe create mode 100644 distutils/command/wininst-9.0.exe create mode 100644 distutils/config.py create mode 100644 distutils/core.py create mode 100644 distutils/cygwinccompiler.py create mode 100644 distutils/debug.py create mode 100644 distutils/dep_util.py create mode 100644 distutils/dir_util.py create mode 100644 distutils/dist.py create mode 100644 distutils/errors.py create mode 100644 distutils/extension.py create mode 100644 distutils/fancy_getopt.py create mode 100644 distutils/file_util.py create mode 100644 distutils/filelist.py create mode 100644 distutils/log.py create mode 100644 distutils/msvc9compiler.py create mode 100644 distutils/msvccompiler.py create mode 100644 distutils/spawn.py create mode 100644 distutils/sysconfig.py create mode 100644 distutils/tests/Setup.sample create mode 100644 distutils/tests/__init__.py create mode 100644 distutils/tests/includetest.rst create mode 100644 distutils/tests/support.py create mode 100644 distutils/tests/test_archive_util.py create mode 100644 distutils/tests/test_bdist.py create mode 100644 distutils/tests/test_bdist_dumb.py create mode 100644 distutils/tests/test_bdist_msi.py create mode 100644 distutils/tests/test_bdist_rpm.py create mode 100644 distutils/tests/test_bdist_wininst.py create mode 100644 distutils/tests/test_build.py create mode 100644 distutils/tests/test_build_clib.py create mode 100644 distutils/tests/test_build_ext.py create mode 100644 distutils/tests/test_build_py.py create mode 100644 distutils/tests/test_build_scripts.py create mode 100644 distutils/tests/test_check.py create mode 100644 distutils/tests/test_clean.py create mode 100644 distutils/tests/test_cmd.py create mode 100644 distutils/tests/test_config.py create mode 100644 distutils/tests/test_config_cmd.py create mode 100644 distutils/tests/test_core.py create mode 100644 distutils/tests/test_cygwinccompiler.py create mode 100644 distutils/tests/test_dep_util.py create mode 100644 distutils/tests/test_dir_util.py create mode 100644 distutils/tests/test_dist.py create mode 100644 distutils/tests/test_extension.py create mode 100644 distutils/tests/test_file_util.py create mode 100644 distutils/tests/test_filelist.py create mode 100644 distutils/tests/test_install.py create mode 100644 distutils/tests/test_install_data.py create mode 100644 distutils/tests/test_install_headers.py create mode 100644 distutils/tests/test_install_lib.py create mode 100644 distutils/tests/test_install_scripts.py create mode 100644 distutils/tests/test_log.py create mode 100644 distutils/tests/test_msvc9compiler.py create mode 100644 distutils/tests/test_msvccompiler.py create mode 100644 distutils/tests/test_register.py create mode 100644 distutils/tests/test_sdist.py create mode 100644 distutils/tests/test_spawn.py create mode 100644 distutils/tests/test_sysconfig.py create mode 100644 distutils/tests/test_text_file.py create mode 100644 distutils/tests/test_unixccompiler.py create mode 100644 distutils/tests/test_upload.py create mode 100644 distutils/tests/test_util.py create mode 100644 distutils/tests/test_version.py create mode 100644 distutils/tests/test_versionpredicate.py create mode 100644 distutils/text_file.py create mode 100644 distutils/unixccompiler.py create mode 100644 distutils/util.py create mode 100644 distutils/version.py create mode 100644 distutils/versionpredicate.py delete mode 100644 errors.py delete mode 100644 extension.py delete mode 100644 fancy_getopt.py delete mode 100644 file_util.py delete mode 100644 filelist.py delete mode 100644 log.py delete mode 100644 msvc9compiler.py delete mode 100644 msvccompiler.py delete mode 100644 spawn.py delete mode 100644 sysconfig.py delete mode 100644 tests/Setup.sample delete mode 100644 tests/__init__.py delete mode 100644 tests/includetest.rst delete mode 100644 tests/support.py delete mode 100644 tests/test_archive_util.py delete mode 100644 tests/test_bdist.py delete mode 100644 tests/test_bdist_dumb.py delete mode 100644 tests/test_bdist_msi.py delete mode 100644 tests/test_bdist_rpm.py delete mode 100644 tests/test_bdist_wininst.py delete mode 100644 tests/test_build.py delete mode 100644 tests/test_build_clib.py delete mode 100644 tests/test_build_ext.py delete mode 100644 tests/test_build_py.py delete mode 100644 tests/test_build_scripts.py delete mode 100644 tests/test_check.py delete mode 100644 tests/test_clean.py delete mode 100644 tests/test_cmd.py delete mode 100644 tests/test_config.py delete mode 100644 tests/test_config_cmd.py delete mode 100644 tests/test_core.py delete mode 100644 tests/test_cygwinccompiler.py delete mode 100644 tests/test_dep_util.py delete mode 100644 tests/test_dir_util.py delete mode 100644 tests/test_dist.py delete mode 100644 tests/test_extension.py delete mode 100644 tests/test_file_util.py delete mode 100644 tests/test_filelist.py delete mode 100644 tests/test_install.py delete mode 100644 tests/test_install_data.py delete mode 100644 tests/test_install_headers.py delete mode 100644 tests/test_install_lib.py delete mode 100644 tests/test_install_scripts.py delete mode 100644 tests/test_log.py delete mode 100644 tests/test_msvc9compiler.py delete mode 100644 tests/test_msvccompiler.py delete mode 100644 tests/test_register.py delete mode 100644 tests/test_sdist.py delete mode 100644 tests/test_spawn.py delete mode 100644 tests/test_sysconfig.py delete mode 100644 tests/test_text_file.py delete mode 100644 tests/test_unixccompiler.py delete mode 100644 tests/test_upload.py delete mode 100644 tests/test_util.py delete mode 100644 tests/test_version.py delete mode 100644 tests/test_versionpredicate.py delete mode 100644 text_file.py delete mode 100644 unixccompiler.py delete mode 100644 util.py delete mode 100644 version.py delete mode 100644 versionpredicate.py diff --git a/README b/README deleted file mode 100644 index 23f48850..00000000 --- a/README +++ /dev/null @@ -1,11 +0,0 @@ -This directory contains the Distutils package. - -There's a full documentation available at: - - http://docs.python.org/distutils/ - -The Distutils-SIG web page is also a good starting point: - - http://www.python.org/sigs/distutils-sig/ - -$Id$ diff --git a/__init__.py b/__init__.py deleted file mode 100644 index d823d040..00000000 --- a/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -"""distutils - -The main package for the Python Module Distribution Utilities. Normally -used from a setup script as - - from distutils.core import setup - - setup (...) -""" - -import sys - -__version__ = sys.version[:sys.version.index(' ')] diff --git a/_msvccompiler.py b/_msvccompiler.py deleted file mode 100644 index af8099a4..00000000 --- a/_msvccompiler.py +++ /dev/null @@ -1,539 +0,0 @@ -"""distutils._msvccompiler - -Contains MSVCCompiler, an implementation of the abstract CCompiler class -for Microsoft Visual Studio 2015. - -The module is compatible with VS 2015 and later. You can find legacy support -for older versions in distutils.msvc9compiler and distutils.msvccompiler. -""" - -# Written by Perry Stoll -# hacked by Robin Becker and Thomas Heller to do a better job of -# finding DevStudio (through the registry) -# ported to VS 2005 and VS 2008 by Christian Heimes -# ported to VS 2015 by Steve Dower - -import os -import subprocess -import winreg - -from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import CCompiler, gen_lib_options -from distutils import log -from distutils.util import get_platform - -from itertools import count - -def _find_vc2015(): - try: - key = winreg.OpenKeyEx( - winreg.HKEY_LOCAL_MACHINE, - r"Software\Microsoft\VisualStudio\SxS\VC7", - access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY - ) - except OSError: - log.debug("Visual C++ is not registered") - return None, None - - best_version = 0 - best_dir = None - with key: - for i in count(): - try: - v, vc_dir, vt = winreg.EnumValue(key, i) - except OSError: - break - if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir): - try: - version = int(float(v)) - except (ValueError, TypeError): - continue - if version >= 14 and version > best_version: - best_version, best_dir = version, vc_dir - return best_version, best_dir - -def _find_vc2017(): - """Returns "15, path" based on the result of invoking vswhere.exe - If no install is found, returns "None, None" - - The version is returned to avoid unnecessarily changing the function - result. It may be ignored when the path is not None. - - If vswhere.exe is not available, by definition, VS 2017 is not - installed. - """ - root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") - if not root: - return None, None - - try: - path = subprocess.check_output([ - os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), - "-latest", - "-prerelease", - "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property", "installationPath", - "-products", "*", - ], encoding="mbcs", errors="strict").strip() - except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): - return None, None - - path = os.path.join(path, "VC", "Auxiliary", "Build") - if os.path.isdir(path): - return 15, path - - return None, None - -PLAT_SPEC_TO_RUNTIME = { - 'x86' : 'x86', - 'x86_amd64' : 'x64', - 'x86_arm' : 'arm', - 'x86_arm64' : 'arm64' -} - -def _find_vcvarsall(plat_spec): - # bpo-38597: Removed vcruntime return value - _, best_dir = _find_vc2017() - - if not best_dir: - best_version, best_dir = _find_vc2015() - - if not best_dir: - log.debug("No suitable Visual C++ version found") - return None, None - - vcvarsall = os.path.join(best_dir, "vcvarsall.bat") - if not os.path.isfile(vcvarsall): - log.debug("%s cannot be found", vcvarsall) - return None, None - - return vcvarsall, None - -def _get_vc_env(plat_spec): - if os.getenv("DISTUTILS_USE_SDK"): - return { - key.lower(): value - for key, value in os.environ.items() - } - - vcvarsall, _ = _find_vcvarsall(plat_spec) - if not vcvarsall: - raise DistutilsPlatformError("Unable to find vcvarsall.bat") - - try: - out = subprocess.check_output( - 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), - stderr=subprocess.STDOUT, - ).decode('utf-16le', errors='replace') - except subprocess.CalledProcessError as exc: - log.error(exc.output) - raise DistutilsPlatformError("Error executing {}" - .format(exc.cmd)) - - env = { - key.lower(): value - for key, _, value in - (line.partition('=') for line in out.splitlines()) - if key and value - } - - return env - -def _find_exe(exe, paths=None): - """Return path to an MSVC executable program. - - Tries to find the program in several places: first, one of the - MSVC program search paths from the registry; next, the directories - in the PATH environment variable. If any of those work, return an - absolute path that is known to exist. If none of them work, just - return the original program name, 'exe'. - """ - if not paths: - paths = os.getenv('path').split(os.pathsep) - for p in paths: - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - return exe - -# A map keyed by get_platform() return values to values accepted by -# 'vcvarsall.bat'. Always cross-compile from x86 to work with the -# lighter-weight MSVC installs that do not include native 64-bit tools. -PLAT_TO_VCVARS = { - 'win32' : 'x86', - 'win-amd64' : 'x86_amd64', - 'win-arm32' : 'x86_arm', - 'win-arm64' : 'x86_arm64' -} - -class MSVCCompiler(CCompiler) : - """Concrete class that implements an interface to Microsoft Visual C++, - as defined by the CCompiler abstract class.""" - - compiler_type = 'msvc' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.rc'] - _mc_extensions = ['.mc'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = (_c_extensions + _cpp_extensions + - _rc_extensions + _mc_extensions) - res_extension = '.res' - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - - def __init__(self, verbose=0, dry_run=0, force=0): - CCompiler.__init__ (self, verbose, dry_run, force) - # target platform (.plat_name is consistent with 'bdist') - self.plat_name = None - self.initialized = False - - def initialize(self, plat_name=None): - # multi-init means we would need to check platform same each time... - assert not self.initialized, "don't init multiple times" - if plat_name is None: - plat_name = get_platform() - # sanity check for platforms to prevent obscure errors later. - if plat_name not in PLAT_TO_VCVARS: - raise DistutilsPlatformError("--plat-name must be one of {}" - .format(tuple(PLAT_TO_VCVARS))) - - # Get the vcvarsall.bat spec for the requested platform. - plat_spec = PLAT_TO_VCVARS[plat_name] - - vc_env = _get_vc_env(plat_spec) - if not vc_env: - raise DistutilsPlatformError("Unable to find a compatible " - "Visual Studio installation.") - - self._paths = vc_env.get('path', '') - paths = self._paths.split(os.pathsep) - self.cc = _find_exe("cl.exe", paths) - self.linker = _find_exe("link.exe", paths) - self.lib = _find_exe("lib.exe", paths) - self.rc = _find_exe("rc.exe", paths) # resource compiler - self.mc = _find_exe("mc.exe", paths) # message compiler - self.mt = _find_exe("mt.exe", paths) # message compiler - - for dir in vc_env.get('include', '').split(os.pathsep): - if dir: - self.add_include_dir(dir.rstrip(os.sep)) - - for dir in vc_env.get('lib', '').split(os.pathsep): - if dir: - self.add_library_dir(dir.rstrip(os.sep)) - - self.preprocess_options = None - # bpo-38597: Always compile with dynamic linking - # Future releases of Python 3.x will include all past - # versions of vcruntime*.dll for compatibility. - self.compile_options = [ - '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD' - ] - - self.compile_options_debug = [ - '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' - ] - - ldflags = [ - '/nologo', '/INCREMENTAL:NO', '/LTCG' - ] - - ldflags_debug = [ - '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL' - ] - - self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1'] - self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1'] - self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] - self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] - self.ldflags_static = [*ldflags] - self.ldflags_static_debug = [*ldflags_debug] - - self._ldflags = { - (CCompiler.EXECUTABLE, None): self.ldflags_exe, - (CCompiler.EXECUTABLE, False): self.ldflags_exe, - (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug, - (CCompiler.SHARED_OBJECT, None): self.ldflags_shared, - (CCompiler.SHARED_OBJECT, False): self.ldflags_shared, - (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug, - (CCompiler.SHARED_LIBRARY, None): self.ldflags_static, - (CCompiler.SHARED_LIBRARY, False): self.ldflags_static, - (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug, - } - - self.initialized = True - - # -- Worker methods ------------------------------------------------ - - def object_filenames(self, - source_filenames, - strip_dir=0, - output_dir=''): - ext_map = { - **{ext: self.obj_extension for ext in self.src_extensions}, - **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions}, - } - - output_dir = output_dir or '' - - def make_out_path(p): - base, ext = os.path.splitext(p) - if strip_dir: - base = os.path.basename(base) - else: - _, base = os.path.splitdrive(base) - if base.startswith((os.path.sep, os.path.altsep)): - base = base[1:] - try: - # XXX: This may produce absurdly long paths. We should check - # the length of the result and trim base until we fit within - # 260 characters. - return os.path.join(output_dir, base + ext_map[ext]) - except LookupError: - # Better to raise an exception instead of silently continuing - # and later complain about sources and targets having - # different lengths - raise CompileError("Don't know how to compile {}".format(p)) - - return list(map(make_out_path, source_filenames)) - - - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - if not self.initialized: - self.initialize() - compile_info = self._setup_compile(output_dir, macros, include_dirs, - sources, depends, extra_postargs) - macros, objects, extra_postargs, pp_opts, build = compile_info - - compile_opts = extra_preargs or [] - compile_opts.append('/c') - if debug: - compile_opts.extend(self.compile_options_debug) - else: - compile_opts.extend(self.compile_options) - - - add_cpp_opts = False - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - if debug: - # pass the full pathname to MSVC in debug mode, - # this allows the debugger to find the source file - # without asking the user to browse for it - src = os.path.abspath(src) - - if ext in self._c_extensions: - input_opt = "/Tc" + src - elif ext in self._cpp_extensions: - input_opt = "/Tp" + src - add_cpp_opts = True - elif ext in self._rc_extensions: - # compile .RC to .RES file - input_opt = src - output_opt = "/fo" + obj - try: - self.spawn([self.rc] + pp_opts + [output_opt, input_opt]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue - elif ext in self._mc_extensions: - # Compile .MC to .RC file to .RES file. - # * '-h dir' specifies the directory for the - # generated include file - # * '-r dir' specifies the target directory of the - # generated RC file and the binary message resource - # it includes - # - # For now (since there are no options to change this), - # we use the source-directory for the include file and - # the build directory for the RC file and message - # resources. This works at least for win32all. - h_dir = os.path.dirname(src) - rc_dir = os.path.dirname(obj) - try: - # first compile .MC to .RC and .H file - self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src]) - base, _ = os.path.splitext(os.path.basename (src)) - rc_file = os.path.join(rc_dir, base + '.rc') - # then compile .RC to .RES file - self.spawn([self.rc, "/fo" + obj, rc_file]) - - except DistutilsExecError as msg: - raise CompileError(msg) - continue - else: - # how to handle this file? - raise CompileError("Don't know how to compile {} to {}" - .format(src, obj)) - - args = [self.cc] + compile_opts + pp_opts - if add_cpp_opts: - args.append('/EHsc') - args.append(input_opt) - args.append("/Fo" + obj) - args.extend(extra_postargs) - - try: - self.spawn(args) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - - def create_static_lib(self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): - - if not self.initialized: - self.initialize() - objects, output_dir = self._fix_object_args(objects, output_dir) - output_filename = self.library_filename(output_libname, - output_dir=output_dir) - - if self._need_link(objects, output_filename): - lib_args = objects + ['/OUT:' + output_filename] - if debug: - pass # XXX what goes here? - try: - log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args)) - self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - if not self.initialized: - self.initialize() - objects, output_dir = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, - runtime_library_dirs) - libraries, library_dirs, runtime_library_dirs = fixed_args - - if runtime_library_dirs: - self.warn("I don't know what to do with 'runtime_library_dirs': " - + str(runtime_library_dirs)) - - lib_opts = gen_lib_options(self, - library_dirs, runtime_library_dirs, - libraries) - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - ldflags = self._ldflags[target_desc, debug] - - export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])] - - ld_args = (ldflags + lib_opts + export_opts + - objects + ['/OUT:' + output_filename]) - - # The MSVC linker generates .lib and .exp files, which cannot be - # suppressed by any linker switches. The .lib files may even be - # needed! Make sure they are generated in the temporary build - # directory. Since they have different names for debug and release - # builds, they can go into the same directory. - build_temp = os.path.dirname(objects[0]) - if export_symbols is not None: - (dll_name, dll_ext) = os.path.splitext( - os.path.basename(output_filename)) - implib_file = os.path.join( - build_temp, - self.library_filename(dll_name)) - ld_args.append ('/IMPLIB:' + implib_file) - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - output_dir = os.path.dirname(os.path.abspath(output_filename)) - self.mkpath(output_dir) - try: - log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args)) - self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - def spawn(self, cmd): - old_path = os.getenv('path') - try: - os.environ['path'] = self._paths - return super().spawn(cmd) - finally: - os.environ['path'] = old_path - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "/LIBPATH:" + dir - - def runtime_library_dir_option(self, dir): - raise DistutilsPlatformError( - "don't know how to set runtime library search path for MSVC") - - def library_option(self, lib): - return self.library_filename(lib) - - def find_library_file(self, dirs, lib, debug=0): - # Prefer a debugging library if found (and requested), but deal - # with it if we don't have one. - if debug: - try_names = [lib + "_d", lib] - else: - try_names = [lib] - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename(name)) - if os.path.isfile(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None diff --git a/archive_util.py b/archive_util.py deleted file mode 100644 index 565a3117..00000000 --- a/archive_util.py +++ /dev/null @@ -1,256 +0,0 @@ -"""distutils.archive_util - -Utility functions for creating archive files (tarballs, zip files, -that sort of thing).""" - -import os -from warnings import warn -import sys - -try: - import zipfile -except ImportError: - zipfile = None - - -from distutils.errors import DistutilsExecError -from distutils.spawn import spawn -from distutils.dir_util import mkpath -from distutils import log - -try: - from pwd import getpwnam -except ImportError: - getpwnam = None - -try: - from grp import getgrnam -except ImportError: - getgrnam = None - -def _get_gid(name): - """Returns a gid, given a group name.""" - if getgrnam is None or name is None: - return None - try: - result = getgrnam(name) - except KeyError: - result = None - if result is not None: - return result[2] - return None - -def _get_uid(name): - """Returns an uid, given a user name.""" - if getpwnam is None or name is None: - return None - try: - result = getpwnam(name) - except KeyError: - result = None - if result is not None: - return result[2] - return None - -def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, - owner=None, group=None): - """Create a (possibly compressed) tar file from all the files under - 'base_dir'. - - 'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or - None. ("compress" will be deprecated in Python 3.2) - - 'owner' and 'group' can be used to define an owner and a group for the - archive that is being built. If not provided, the current owner and group - will be used. - - The output tar file will be named 'base_dir' + ".tar", possibly plus - the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z"). - - Returns the output filename. - """ - tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '', - 'compress': ''} - compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', - 'compress': '.Z'} - - # flags for compression program, each element of list will be an argument - if compress is not None and compress not in compress_ext.keys(): - raise ValueError( - "bad value for 'compress': must be None, 'gzip', 'bzip2', " - "'xz' or 'compress'") - - archive_name = base_name + '.tar' - if compress != 'compress': - archive_name += compress_ext.get(compress, '') - - mkpath(os.path.dirname(archive_name), dry_run=dry_run) - - # creating the tarball - import tarfile # late import so Python build itself doesn't break - - log.info('Creating tar archive') - - uid = _get_uid(owner) - gid = _get_gid(group) - - def _set_uid_gid(tarinfo): - if gid is not None: - tarinfo.gid = gid - tarinfo.gname = group - if uid is not None: - tarinfo.uid = uid - tarinfo.uname = owner - return tarinfo - - if not dry_run: - tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) - try: - tar.add(base_dir, filter=_set_uid_gid) - finally: - tar.close() - - # compression using `compress` - if compress == 'compress': - warn("'compress' will be deprecated.", PendingDeprecationWarning) - # the option varies depending on the platform - compressed_name = archive_name + compress_ext[compress] - if sys.platform == 'win32': - cmd = [compress, archive_name, compressed_name] - else: - cmd = [compress, '-f', archive_name] - spawn(cmd, dry_run=dry_run) - return compressed_name - - return archive_name - -def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): - """Create a zip file from all the files under 'base_dir'. - - The output zip file will be named 'base_name' + ".zip". Uses either the - "zipfile" Python module (if available) or the InfoZIP "zip" utility - (if installed and found on the default search path). If neither tool is - available, raises DistutilsExecError. Returns the name of the output zip - file. - """ - zip_filename = base_name + ".zip" - mkpath(os.path.dirname(zip_filename), dry_run=dry_run) - - # If zipfile module is not available, try spawning an external - # 'zip' command. - if zipfile is None: - if verbose: - zipoptions = "-r" - else: - zipoptions = "-rq" - - try: - spawn(["zip", zipoptions, zip_filename, base_dir], - dry_run=dry_run) - except DistutilsExecError: - # XXX really should distinguish between "couldn't find - # external 'zip' command" and "zip failed". - raise DistutilsExecError(("unable to create zip file '%s': " - "could neither import the 'zipfile' module nor " - "find a standalone zip utility") % zip_filename) - - else: - log.info("creating '%s' and adding '%s' to it", - zip_filename, base_dir) - - if not dry_run: - try: - zip = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) - except RuntimeError: - zip = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_STORED) - - with zip: - if base_dir != os.curdir: - path = os.path.normpath(os.path.join(base_dir, '')) - zip.write(path, path) - log.info("adding '%s'", path) - for dirpath, dirnames, filenames in os.walk(base_dir): - for name in dirnames: - path = os.path.normpath(os.path.join(dirpath, name, '')) - zip.write(path, path) - log.info("adding '%s'", path) - for name in filenames: - path = os.path.normpath(os.path.join(dirpath, name)) - if os.path.isfile(path): - zip.write(path, path) - log.info("adding '%s'", path) - - return zip_filename - -ARCHIVE_FORMATS = { - 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), - 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), - 'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"), - 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"), - 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"), - 'zip': (make_zipfile, [],"ZIP file") - } - -def check_archive_formats(formats): - """Returns the first format from the 'format' list that is unknown. - - If all formats are known, returns None - """ - for format in formats: - if format not in ARCHIVE_FORMATS: - return format - return None - -def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, - dry_run=0, owner=None, group=None): - """Create an archive file (eg. zip or tar). - - 'base_name' is the name of the file to create, minus any format-specific - extension; 'format' is the archive format: one of "zip", "tar", "gztar", - "bztar", "xztar", or "ztar". - - 'root_dir' is a directory that will be the root directory of the - archive; ie. we typically chdir into 'root_dir' before creating the - archive. 'base_dir' is the directory where we start archiving from; - ie. 'base_dir' will be the common prefix of all files and - directories in the archive. 'root_dir' and 'base_dir' both default - to the current directory. Returns the name of the archive file. - - 'owner' and 'group' are used when creating a tar archive. By default, - uses the current owner and group. - """ - save_cwd = os.getcwd() - if root_dir is not None: - log.debug("changing into '%s'", root_dir) - base_name = os.path.abspath(base_name) - if not dry_run: - os.chdir(root_dir) - - if base_dir is None: - base_dir = os.curdir - - kwargs = {'dry_run': dry_run} - - try: - format_info = ARCHIVE_FORMATS[format] - except KeyError: - raise ValueError("unknown archive format '%s'" % format) - - func = format_info[0] - for arg, val in format_info[1]: - kwargs[arg] = val - - if format != 'zip': - kwargs['owner'] = owner - kwargs['group'] = group - - try: - filename = func(base_name, base_dir, **kwargs) - finally: - if root_dir is not None: - log.debug("changing back to '%s'", save_cwd) - os.chdir(save_cwd) - - return filename diff --git a/bcppcompiler.py b/bcppcompiler.py deleted file mode 100644 index 071fea5d..00000000 --- a/bcppcompiler.py +++ /dev/null @@ -1,393 +0,0 @@ -"""distutils.bcppcompiler - -Contains BorlandCCompiler, an implementation of the abstract CCompiler class -for the Borland C++ compiler. -""" - -# This implementation by Lyle Johnson, based on the original msvccompiler.py -# module and using the directions originally published by Gordon Williams. - -# XXX looks like there's a LOT of overlap between these two classes: -# someone should sit down and factor out the common code as -# WindowsCCompiler! --GPW - - -import os -from distutils.errors import \ - DistutilsExecError, \ - CompileError, LibError, LinkError, UnknownFileError -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options -from distutils.file_util import write_file -from distutils.dep_util import newer -from distutils import log - -class BCPPCompiler(CCompiler) : - """Concrete class that implements an interface to the Borland C/C++ - compiler, as defined by the CCompiler abstract class. - """ - - compiler_type = 'bcpp' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = _c_extensions + _cpp_extensions - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - - def __init__ (self, - verbose=0, - dry_run=0, - force=0): - - CCompiler.__init__ (self, verbose, dry_run, force) - - # These executables are assumed to all be in the path. - # Borland doesn't seem to use any special registry settings to - # indicate their installation locations. - - self.cc = "bcc32.exe" - self.linker = "ilink32.exe" - self.lib = "tlib.exe" - - self.preprocess_options = None - self.compile_options = ['/tWM', '/O2', '/q', '/g0'] - self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0'] - - self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x'] - self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x'] - self.ldflags_static = [] - self.ldflags_exe = ['/Gn', '/q', '/x'] - self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r'] - - - # -- Worker methods ------------------------------------------------ - - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - macros, objects, extra_postargs, pp_opts, build = \ - self._setup_compile(output_dir, macros, include_dirs, sources, - depends, extra_postargs) - compile_opts = extra_preargs or [] - compile_opts.append ('-c') - if debug: - compile_opts.extend (self.compile_options_debug) - else: - compile_opts.extend (self.compile_options) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - # XXX why do the normpath here? - src = os.path.normpath(src) - obj = os.path.normpath(obj) - # XXX _setup_compile() did a mkpath() too but before the normpath. - # Is it possible to skip the normpath? - self.mkpath(os.path.dirname(obj)) - - if ext == '.res': - # This is already a binary file -- skip it. - continue # the 'for' loop - if ext == '.rc': - # This needs to be compiled to a .res file -- do it now. - try: - self.spawn (["brcc32", "-fo", obj, src]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue # the 'for' loop - - # The next two are both for the real compiler. - if ext in self._c_extensions: - input_opt = "" - elif ext in self._cpp_extensions: - input_opt = "-P" - else: - # Unknown file type -- no extra options. The compiler - # will probably fail, but let it just in case this is a - # file the compiler recognizes even if we don't. - input_opt = "" - - output_opt = "-o" + obj - - # Compiler command line syntax is: "bcc32 [options] file(s)". - # Note that the source file names must appear at the end of - # the command line. - try: - self.spawn ([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs + [src]) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - # compile () - - - def create_static_lib (self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): - - (objects, output_dir) = self._fix_object_args (objects, output_dir) - output_filename = \ - self.library_filename (output_libname, output_dir=output_dir) - - if self._need_link (objects, output_filename): - lib_args = [output_filename, '/u'] + objects - if debug: - pass # XXX what goes here? - try: - self.spawn ([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - # create_static_lib () - - - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - # XXX this ignores 'build_temp'! should follow the lead of - # msvccompiler.py - - (objects, output_dir) = self._fix_object_args (objects, output_dir) - (libraries, library_dirs, runtime_library_dirs) = \ - self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) - - if runtime_library_dirs: - log.warn("I don't know what to do with 'runtime_library_dirs': %s", - str(runtime_library_dirs)) - - if output_dir is not None: - output_filename = os.path.join (output_dir, output_filename) - - if self._need_link (objects, output_filename): - - # Figure out linker args based on type of target. - if target_desc == CCompiler.EXECUTABLE: - startup_obj = 'c0w32' - if debug: - ld_args = self.ldflags_exe_debug[:] - else: - ld_args = self.ldflags_exe[:] - else: - startup_obj = 'c0d32' - if debug: - ld_args = self.ldflags_shared_debug[:] - else: - ld_args = self.ldflags_shared[:] - - - # Create a temporary exports file for use by the linker - if export_symbols is None: - def_file = '' - else: - head, tail = os.path.split (output_filename) - modname, ext = os.path.splitext (tail) - temp_dir = os.path.dirname(objects[0]) # preserve tree structure - def_file = os.path.join (temp_dir, '%s.def' % modname) - contents = ['EXPORTS'] - for sym in (export_symbols or []): - contents.append(' %s=_%s' % (sym, sym)) - self.execute(write_file, (def_file, contents), - "writing %s" % def_file) - - # Borland C++ has problems with '/' in paths - objects2 = map(os.path.normpath, objects) - # split objects in .obj and .res files - # Borland C++ needs them at different positions in the command line - objects = [startup_obj] - resources = [] - for file in objects2: - (base, ext) = os.path.splitext(os.path.normcase(file)) - if ext == '.res': - resources.append(file) - else: - objects.append(file) - - - for l in library_dirs: - ld_args.append("/L%s" % os.path.normpath(l)) - ld_args.append("/L.") # we sometimes use relative paths - - # list of object files - ld_args.extend(objects) - - # XXX the command-line syntax for Borland C++ is a bit wonky; - # certain filenames are jammed together in one big string, but - # comma-delimited. This doesn't mesh too well with the - # Unix-centric attitude (with a DOS/Windows quoting hack) of - # 'spawn()', so constructing the argument list is a bit - # awkward. Note that doing the obvious thing and jamming all - # the filenames and commas into one argument would be wrong, - # because 'spawn()' would quote any filenames with spaces in - # them. Arghghh!. Apparently it works fine as coded... - - # name of dll/exe file - ld_args.extend([',',output_filename]) - # no map file and start libraries - ld_args.append(',,') - - for lib in libraries: - # see if we find it and if there is a bcpp specific lib - # (xxx_bcpp.lib) - libfile = self.find_library_file(library_dirs, lib, debug) - if libfile is None: - ld_args.append(lib) - # probably a BCPP internal library -- don't warn - else: - # full name which prefers bcpp_xxx.lib over xxx.lib - ld_args.append(libfile) - - # some default libraries - ld_args.append ('import32') - ld_args.append ('cw32mt') - - # def file for export symbols - ld_args.extend([',',def_file]) - # add resource files - ld_args.append(',') - ld_args.extend(resources) - - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - self.mkpath (os.path.dirname (output_filename)) - try: - self.spawn ([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - - else: - log.debug("skipping %s (up-to-date)", output_filename) - - # link () - - # -- Miscellaneous methods ----------------------------------------- - - - def find_library_file (self, dirs, lib, debug=0): - # List of effective library names to try, in order of preference: - # xxx_bcpp.lib is better than xxx.lib - # and xxx_d.lib is better than xxx.lib if debug is set - # - # The "_bcpp" suffix is to handle a Python installation for people - # with multiple compilers (primarily Distutils hackers, I suspect - # ;-). The idea is they'd have one static library for each - # compiler they care about, since (almost?) every Windows compiler - # seems to have a different format for static libraries. - if debug: - dlib = (lib + "_d") - try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib) - else: - try_names = (lib + "_bcpp", lib) - - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename(name)) - if os.path.exists(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None - - # overwrite the one from CCompiler to support rc and res-files - def object_filenames (self, - source_filenames, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' - obj_names = [] - for src_name in source_filenames: - # use normcase to make sure '.rc' is really '.rc' and not '.RC' - (base, ext) = os.path.splitext (os.path.normcase(src_name)) - if ext not in (self.src_extensions + ['.rc','.res']): - raise UnknownFileError("unknown file type '%s' (from '%s')" % \ - (ext, src_name)) - if strip_dir: - base = os.path.basename (base) - if ext == '.res': - # these can go unchanged - obj_names.append (os.path.join (output_dir, base + ext)) - elif ext == '.rc': - # these need to be compiled to .res-files - obj_names.append (os.path.join (output_dir, base + '.res')) - else: - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) - return obj_names - - # object_filenames () - - def preprocess (self, - source, - output_file=None, - macros=None, - include_dirs=None, - extra_preargs=None, - extra_postargs=None): - - (_, macros, include_dirs) = \ - self._fix_compile_args(None, macros, include_dirs) - pp_opts = gen_preprocess_options(macros, include_dirs) - pp_args = ['cpp32.exe'] + pp_opts - if output_file is not None: - pp_args.append('-o' + output_file) - if extra_preargs: - pp_args[:0] = extra_preargs - if extra_postargs: - pp_args.extend(extra_postargs) - pp_args.append(source) - - # We need to preprocess: either we're being forced to, or the - # source file is newer than the target (or the target doesn't - # exist). - if self.force or output_file is None or newer(source, output_file): - if output_file: - self.mkpath(os.path.dirname(output_file)) - try: - self.spawn(pp_args) - except DistutilsExecError as msg: - print(msg) - raise CompileError(msg) - - # preprocess() diff --git a/ccompiler.py b/ccompiler.py deleted file mode 100644 index b5ef143e..00000000 --- a/ccompiler.py +++ /dev/null @@ -1,1116 +0,0 @@ -"""distutils.ccompiler - -Contains CCompiler, an abstract base class that defines the interface -for the Distutils compiler abstraction model.""" - -import sys, os, re -from distutils.errors import * -from distutils.spawn import spawn -from distutils.file_util import move_file -from distutils.dir_util import mkpath -from distutils.dep_util import newer_group -from distutils.util import split_quoted, execute -from distutils import log - -class CCompiler: - """Abstract base class to define the interface that must be implemented - by real compiler classes. Also has some utility methods used by - several compiler classes. - - The basic idea behind a compiler abstraction class is that each - instance can be used for all the compile/link steps in building a - single project. Thus, attributes common to all of those compile and - link steps -- include directories, macros to define, libraries to link - against, etc. -- are attributes of the compiler instance. To allow for - variability in how individual files are treated, most of those - attributes may be varied on a per-compilation or per-link basis. - """ - - # 'compiler_type' is a class attribute that identifies this class. It - # keeps code that wants to know what kind of compiler it's dealing with - # from having to import all possible compiler classes just to do an - # 'isinstance'. In concrete CCompiler subclasses, 'compiler_type' - # should really, really be one of the keys of the 'compiler_class' - # dictionary (see below -- used by the 'new_compiler()' factory - # function) -- authors of new compiler interface classes are - # responsible for updating 'compiler_class'! - compiler_type = None - - # XXX things not handled by this compiler abstraction model: - # * client can't provide additional options for a compiler, - # e.g. warning, optimization, debugging flags. Perhaps this - # should be the domain of concrete compiler abstraction classes - # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base - # class should have methods for the common ones. - # * can't completely override the include or library searchg - # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". - # I'm not sure how widely supported this is even by Unix - # compilers, much less on other platforms. And I'm even less - # sure how useful it is; maybe for cross-compiling, but - # support for that is a ways off. (And anyways, cross - # compilers probably have a dedicated binary with the - # right paths compiled in. I hope.) - # * can't do really freaky things with the library list/library - # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against - # different versions of libfoo.a in different locations. I - # think this is useless without the ability to null out the - # library search path anyways. - - - # Subclasses that rely on the standard filename generation methods - # implemented below should override these; see the comment near - # those methods ('object_filenames()' et. al.) for details: - src_extensions = None # list of strings - obj_extension = None # string - static_lib_extension = None - shared_lib_extension = None # string - static_lib_format = None # format string - shared_lib_format = None # prob. same as static_lib_format - exe_extension = None # string - - # Default language settings. language_map is used to detect a source - # file or Extension target language, checking source filenames. - # language_order is used to detect the language precedence, when deciding - # what language to use when mixing source types. For example, if some - # extension has two files with ".c" extension, and one with ".cpp", it - # is still linked as c++. - language_map = {".c" : "c", - ".cc" : "c++", - ".cpp" : "c++", - ".cxx" : "c++", - ".m" : "objc", - } - language_order = ["c++", "objc", "c"] - - def __init__(self, verbose=0, dry_run=0, force=0): - self.dry_run = dry_run - self.force = force - self.verbose = verbose - - # 'output_dir': a common output directory for object, library, - # shared object, and shared library files - self.output_dir = None - - # 'macros': a list of macro definitions (or undefinitions). A - # macro definition is a 2-tuple (name, value), where the value is - # either a string or None (no explicit value). A macro - # undefinition is a 1-tuple (name,). - self.macros = [] - - # 'include_dirs': a list of directories to search for include files - self.include_dirs = [] - - # 'libraries': a list of libraries to include in any link - # (library names, not filenames: eg. "foo" not "libfoo.a") - self.libraries = [] - - # 'library_dirs': a list of directories to search for libraries - self.library_dirs = [] - - # 'runtime_library_dirs': a list of directories to search for - # shared libraries/objects at runtime - self.runtime_library_dirs = [] - - # 'objects': a list of object files (or similar, such as explicitly - # named library files) to include on any link - self.objects = [] - - for key in self.executables.keys(): - self.set_executable(key, self.executables[key]) - - def set_executables(self, **kwargs): - """Define the executables (and options for them) that will be run - to perform the various stages of compilation. The exact set of - executables that may be specified here depends on the compiler - class (via the 'executables' class attribute), but most will have: - compiler the C/C++ compiler - linker_so linker used to create shared objects and libraries - linker_exe linker used to create binary executables - archiver static library creator - - On platforms with a command-line (Unix, DOS/Windows), each of these - is a string that will be split into executable name and (optional) - list of arguments. (Splitting the string is done similarly to how - Unix shells operate: words are delimited by spaces, but quotes and - backslashes can override this. See - 'distutils.util.split_quoted()'.) - """ - - # Note that some CCompiler implementation classes will define class - # attributes 'cpp', 'cc', etc. with hard-coded executable names; - # this is appropriate when a compiler class is for exactly one - # compiler/OS combination (eg. MSVCCompiler). Other compiler - # classes (UnixCCompiler, in particular) are driven by information - # discovered at run-time, since there are many different ways to do - # basically the same things with Unix C compilers. - - for key in kwargs: - if key not in self.executables: - raise ValueError("unknown executable '%s' for class %s" % - (key, self.__class__.__name__)) - self.set_executable(key, kwargs[key]) - - def set_executable(self, key, value): - if isinstance(value, str): - setattr(self, key, split_quoted(value)) - else: - setattr(self, key, value) - - def _find_macro(self, name): - i = 0 - for defn in self.macros: - if defn[0] == name: - return i - i += 1 - return None - - def _check_macro_definitions(self, definitions): - """Ensures that every element of 'definitions' is a valid macro - definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do - nothing if all definitions are OK, raise TypeError otherwise. - """ - for defn in definitions: - if not (isinstance(defn, tuple) and - (len(defn) in (1, 2) and - (isinstance (defn[1], str) or defn[1] is None)) and - isinstance (defn[0], str)): - raise TypeError(("invalid macro definition '%s': " % defn) + \ - "must be tuple (string,), (string, string), or " + \ - "(string, None)") - - - # -- Bookkeeping methods ------------------------------------------- - - def define_macro(self, name, value=None): - """Define a preprocessor macro for all compilations driven by this - compiler object. The optional parameter 'value' should be a - string; if it is not supplied, then the macro will be defined - without an explicit value and the exact outcome depends on the - compiler used (XXX true? does ANSI say anything about this?) - """ - # Delete from the list of macro definitions/undefinitions if - # already there (so that this one will take precedence). - i = self._find_macro (name) - if i is not None: - del self.macros[i] - - self.macros.append((name, value)) - - def undefine_macro(self, name): - """Undefine a preprocessor macro for all compilations driven by - this compiler object. If the same macro is defined by - 'define_macro()' and undefined by 'undefine_macro()' the last call - takes precedence (including multiple redefinitions or - undefinitions). If the macro is redefined/undefined on a - per-compilation basis (ie. in the call to 'compile()'), then that - takes precedence. - """ - # Delete from the list of macro definitions/undefinitions if - # already there (so that this one will take precedence). - i = self._find_macro (name) - if i is not None: - del self.macros[i] - - undefn = (name,) - self.macros.append(undefn) - - def add_include_dir(self, dir): - """Add 'dir' to the list of directories that will be searched for - header files. The compiler is instructed to search directories in - the order in which they are supplied by successive calls to - 'add_include_dir()'. - """ - self.include_dirs.append(dir) - - def set_include_dirs(self, dirs): - """Set the list of directories that will be searched to 'dirs' (a - list of strings). Overrides any preceding calls to - 'add_include_dir()'; subsequence calls to 'add_include_dir()' add - to the list passed to 'set_include_dirs()'. This does not affect - any list of standard include directories that the compiler may - search by default. - """ - self.include_dirs = dirs[:] - - def add_library(self, libname): - """Add 'libname' to the list of libraries that will be included in - all links driven by this compiler object. Note that 'libname' - should *not* be the name of a file containing a library, but the - name of the library itself: the actual filename will be inferred by - the linker, the compiler, or the compiler class (depending on the - platform). - - The linker will be instructed to link against libraries in the - order they were supplied to 'add_library()' and/or - 'set_libraries()'. It is perfectly valid to duplicate library - names; the linker will be instructed to link against libraries as - many times as they are mentioned. - """ - self.libraries.append(libname) - - def set_libraries(self, libnames): - """Set the list of libraries to be included in all links driven by - this compiler object to 'libnames' (a list of strings). This does - not affect any standard system libraries that the linker may - include by default. - """ - self.libraries = libnames[:] - - def add_library_dir(self, dir): - """Add 'dir' to the list of directories that will be searched for - libraries specified to 'add_library()' and 'set_libraries()'. The - linker will be instructed to search for libraries in the order they - are supplied to 'add_library_dir()' and/or 'set_library_dirs()'. - """ - self.library_dirs.append(dir) - - def set_library_dirs(self, dirs): - """Set the list of library search directories to 'dirs' (a list of - strings). This does not affect any standard library search path - that the linker may search by default. - """ - self.library_dirs = dirs[:] - - def add_runtime_library_dir(self, dir): - """Add 'dir' to the list of directories that will be searched for - shared libraries at runtime. - """ - self.runtime_library_dirs.append(dir) - - def set_runtime_library_dirs(self, dirs): - """Set the list of directories to search for shared libraries at - runtime to 'dirs' (a list of strings). This does not affect any - standard search path that the runtime linker may search by - default. - """ - self.runtime_library_dirs = dirs[:] - - def add_link_object(self, object): - """Add 'object' to the list of object files (or analogues, such as - explicitly named library files or the output of "resource - compilers") to be included in every link driven by this compiler - object. - """ - self.objects.append(object) - - def set_link_objects(self, objects): - """Set the list of object files (or analogues) to be included in - every link to 'objects'. This does not affect any standard object - files that the linker may include by default (such as system - libraries). - """ - self.objects = objects[:] - - - # -- Private utility methods -------------------------------------- - # (here for the convenience of subclasses) - - # Helper method to prep compiler in subclass compile() methods - - def _setup_compile(self, outdir, macros, incdirs, sources, depends, - extra): - """Process arguments and decide which source files to compile.""" - if outdir is None: - outdir = self.output_dir - elif not isinstance(outdir, str): - raise TypeError("'output_dir' must be a string or None") - - if macros is None: - macros = self.macros - elif isinstance(macros, list): - macros = macros + (self.macros or []) - else: - raise TypeError("'macros' (if supplied) must be a list of tuples") - - if incdirs is None: - incdirs = self.include_dirs - elif isinstance(incdirs, (list, tuple)): - incdirs = list(incdirs) + (self.include_dirs or []) - else: - raise TypeError( - "'include_dirs' (if supplied) must be a list of strings") - - if extra is None: - extra = [] - - # Get the list of expected output (object) files - objects = self.object_filenames(sources, strip_dir=0, - output_dir=outdir) - assert len(objects) == len(sources) - - pp_opts = gen_preprocess_options(macros, incdirs) - - build = {} - for i in range(len(sources)): - src = sources[i] - obj = objects[i] - ext = os.path.splitext(src)[1] - self.mkpath(os.path.dirname(obj)) - build[obj] = (src, ext) - - return macros, objects, extra, pp_opts, build - - def _get_cc_args(self, pp_opts, debug, before): - # works for unixccompiler, cygwinccompiler - cc_args = pp_opts + ['-c'] - if debug: - cc_args[:0] = ['-g'] - if before: - cc_args[:0] = before - return cc_args - - def _fix_compile_args(self, output_dir, macros, include_dirs): - """Typecheck and fix-up some of the arguments to the 'compile()' - method, and return fixed-up values. Specifically: if 'output_dir' - is None, replaces it with 'self.output_dir'; ensures that 'macros' - is a list, and augments it with 'self.macros'; ensures that - 'include_dirs' is a list, and augments it with 'self.include_dirs'. - Guarantees that the returned values are of the correct type, - i.e. for 'output_dir' either string or None, and for 'macros' and - 'include_dirs' either list or None. - """ - if output_dir is None: - output_dir = self.output_dir - elif not isinstance(output_dir, str): - raise TypeError("'output_dir' must be a string or None") - - if macros is None: - macros = self.macros - elif isinstance(macros, list): - macros = macros + (self.macros or []) - else: - raise TypeError("'macros' (if supplied) must be a list of tuples") - - if include_dirs is None: - include_dirs = self.include_dirs - elif isinstance(include_dirs, (list, tuple)): - include_dirs = list(include_dirs) + (self.include_dirs or []) - else: - raise TypeError( - "'include_dirs' (if supplied) must be a list of strings") - - return output_dir, macros, include_dirs - - def _prep_compile(self, sources, output_dir, depends=None): - """Decide which souce files must be recompiled. - - Determine the list of object files corresponding to 'sources', - and figure out which ones really need to be recompiled. - Return a list of all object files and a dictionary telling - which source files can be skipped. - """ - # Get the list of expected output (object) files - objects = self.object_filenames(sources, output_dir=output_dir) - assert len(objects) == len(sources) - - # Return an empty dict for the "which source files can be skipped" - # return value to preserve API compatibility. - return objects, {} - - def _fix_object_args(self, objects, output_dir): - """Typecheck and fix up some arguments supplied to various methods. - Specifically: ensure that 'objects' is a list; if output_dir is - None, replace with self.output_dir. Return fixed versions of - 'objects' and 'output_dir'. - """ - if not isinstance(objects, (list, tuple)): - raise TypeError("'objects' must be a list or tuple of strings") - objects = list(objects) - - if output_dir is None: - output_dir = self.output_dir - elif not isinstance(output_dir, str): - raise TypeError("'output_dir' must be a string or None") - - return (objects, output_dir) - - def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs): - """Typecheck and fix up some of the arguments supplied to the - 'link_*' methods. Specifically: ensure that all arguments are - lists, and augment them with their permanent versions - (eg. 'self.libraries' augments 'libraries'). Return a tuple with - fixed versions of all arguments. - """ - if libraries is None: - libraries = self.libraries - elif isinstance(libraries, (list, tuple)): - libraries = list (libraries) + (self.libraries or []) - else: - raise TypeError( - "'libraries' (if supplied) must be a list of strings") - - if library_dirs is None: - library_dirs = self.library_dirs - elif isinstance(library_dirs, (list, tuple)): - library_dirs = list (library_dirs) + (self.library_dirs or []) - else: - raise TypeError( - "'library_dirs' (if supplied) must be a list of strings") - - if runtime_library_dirs is None: - runtime_library_dirs = self.runtime_library_dirs - elif isinstance(runtime_library_dirs, (list, tuple)): - runtime_library_dirs = (list(runtime_library_dirs) + - (self.runtime_library_dirs or [])) - else: - raise TypeError("'runtime_library_dirs' (if supplied) " - "must be a list of strings") - - return (libraries, library_dirs, runtime_library_dirs) - - def _need_link(self, objects, output_file): - """Return true if we need to relink the files listed in 'objects' - to recreate 'output_file'. - """ - if self.force: - return True - else: - if self.dry_run: - newer = newer_group (objects, output_file, missing='newer') - else: - newer = newer_group (objects, output_file) - return newer - - def detect_language(self, sources): - """Detect the language of a given file, or list of files. Uses - language_map, and language_order to do the job. - """ - if not isinstance(sources, list): - sources = [sources] - lang = None - index = len(self.language_order) - for source in sources: - base, ext = os.path.splitext(source) - extlang = self.language_map.get(ext) - try: - extindex = self.language_order.index(extlang) - if extindex < index: - lang = extlang - index = extindex - except ValueError: - pass - return lang - - - # -- Worker methods ------------------------------------------------ - # (must be implemented by subclasses) - - def preprocess(self, source, output_file=None, macros=None, - include_dirs=None, extra_preargs=None, extra_postargs=None): - """Preprocess a single C/C++ source file, named in 'source'. - Output will be written to file named 'output_file', or stdout if - 'output_file' not supplied. 'macros' is a list of macro - definitions as for 'compile()', which will augment the macros set - with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a - list of directory names that will be added to the default list. - - Raises PreprocessError on failure. - """ - pass - - def compile(self, sources, output_dir=None, macros=None, - include_dirs=None, debug=0, extra_preargs=None, - extra_postargs=None, depends=None): - """Compile one or more source files. - - 'sources' must be a list of filenames, most likely C/C++ - files, but in reality anything that can be handled by a - particular compiler and compiler class (eg. MSVCCompiler can - handle resource files in 'sources'). Return a list of object - filenames, one per source filename in 'sources'. Depending on - the implementation, not all source files will necessarily be - compiled, but all corresponding object filenames will be - returned. - - If 'output_dir' is given, object files will be put under it, while - retaining their original path component. That is, "foo/bar.c" - normally compiles to "foo/bar.o" (for a Unix implementation); if - 'output_dir' is "build", then it would compile to - "build/foo/bar.o". - - 'macros', if given, must be a list of macro definitions. A macro - definition is either a (name, value) 2-tuple or a (name,) 1-tuple. - The former defines a macro; if the value is None, the macro is - defined without an explicit value. The 1-tuple case undefines a - macro. Later definitions/redefinitions/ undefinitions take - precedence. - - 'include_dirs', if given, must be a list of strings, the - directories to add to the default include file search path for this - compilation only. - - 'debug' is a boolean; if true, the compiler will be instructed to - output debug symbols in (or alongside) the object file(s). - - 'extra_preargs' and 'extra_postargs' are implementation- dependent. - On platforms that have the notion of a command-line (e.g. Unix, - DOS/Windows), they are most likely lists of strings: extra - command-line arguments to prepend/append to the compiler command - line. On other platforms, consult the implementation class - documentation. In any event, they are intended as an escape hatch - for those occasions when the abstract compiler framework doesn't - cut the mustard. - - 'depends', if given, is a list of filenames that all targets - depend on. If a source file is older than any file in - depends, then the source file will be recompiled. This - supports dependency tracking, but only at a coarse - granularity. - - Raises CompileError on failure. - """ - # A concrete compiler class can either override this method - # entirely or implement _compile(). - macros, objects, extra_postargs, pp_opts, build = \ - self._setup_compile(output_dir, macros, include_dirs, sources, - depends, extra_postargs) - cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) - - # Return *all* object filenames, not just the ones we just built. - return objects - - def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): - """Compile 'src' to product 'obj'.""" - # A concrete compiler class that does not override compile() - # should implement _compile(). - pass - - def create_static_lib(self, objects, output_libname, output_dir=None, - debug=0, target_lang=None): - """Link a bunch of stuff together to create a static library file. - The "bunch of stuff" consists of the list of object files supplied - as 'objects', the extra object files supplied to - 'add_link_object()' and/or 'set_link_objects()', the libraries - supplied to 'add_library()' and/or 'set_libraries()', and the - libraries supplied as 'libraries' (if any). - - 'output_libname' should be a library name, not a filename; the - filename will be inferred from the library name. 'output_dir' is - the directory where the library file will be put. - - 'debug' is a boolean; if true, debugging information will be - included in the library (note that on most platforms, it is the - compile step where this matters: the 'debug' flag is included here - just for consistency). - - 'target_lang' is the target language for which the given objects - are being compiled. This allows specific linkage time treatment of - certain languages. - - Raises LibError on failure. - """ - pass - - - # values for target_desc parameter in link() - SHARED_OBJECT = "shared_object" - SHARED_LIBRARY = "shared_library" - EXECUTABLE = "executable" - - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - """Link a bunch of stuff together to create an executable or - shared library file. - - The "bunch of stuff" consists of the list of object files supplied - as 'objects'. 'output_filename' should be a filename. If - 'output_dir' is supplied, 'output_filename' is relative to it - (i.e. 'output_filename' can provide directory components if - needed). - - 'libraries' is a list of libraries to link against. These are - library names, not filenames, since they're translated into - filenames in a platform-specific way (eg. "foo" becomes "libfoo.a" - on Unix and "foo.lib" on DOS/Windows). However, they can include a - directory component, which means the linker will look in that - specific directory rather than searching all the normal locations. - - 'library_dirs', if supplied, should be a list of directories to - search for libraries that were specified as bare library names - (ie. no directory component). These are on top of the system - default and those supplied to 'add_library_dir()' and/or - 'set_library_dirs()'. 'runtime_library_dirs' is a list of - directories that will be embedded into the shared library and used - to search for other shared libraries that *it* depends on at - run-time. (This may only be relevant on Unix.) - - 'export_symbols' is a list of symbols that the shared library will - export. (This appears to be relevant only on Windows.) - - 'debug' is as for 'compile()' and 'create_static_lib()', with the - slight distinction that it actually matters on most platforms (as - opposed to 'create_static_lib()', which includes a 'debug' flag - mostly for form's sake). - - 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except - of course that they supply command-line arguments for the - particular linker being used). - - 'target_lang' is the target language for which the given objects - are being compiled. This allows specific linkage time treatment of - certain languages. - - Raises LinkError on failure. - """ - raise NotImplementedError - - - # Old 'link_*()' methods, rewritten to use the new 'link()' method. - - def link_shared_lib(self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - self.link(CCompiler.SHARED_LIBRARY, objects, - self.library_filename(output_libname, lib_type='shared'), - output_dir, - libraries, library_dirs, runtime_library_dirs, - export_symbols, debug, - extra_preargs, extra_postargs, build_temp, target_lang) - - - def link_shared_object(self, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - self.link(CCompiler.SHARED_OBJECT, objects, - output_filename, output_dir, - libraries, library_dirs, runtime_library_dirs, - export_symbols, debug, - extra_preargs, extra_postargs, build_temp, target_lang) - - - def link_executable(self, - objects, - output_progname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - target_lang=None): - self.link(CCompiler.EXECUTABLE, objects, - self.executable_filename(output_progname), output_dir, - libraries, library_dirs, runtime_library_dirs, None, - debug, extra_preargs, extra_postargs, None, target_lang) - - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function; there is - # no appropriate default implementation so subclasses should - # implement all of these. - - def library_dir_option(self, dir): - """Return the compiler option to add 'dir' to the list of - directories searched for libraries. - """ - raise NotImplementedError - - def runtime_library_dir_option(self, dir): - """Return the compiler option to add 'dir' to the list of - directories searched for runtime libraries. - """ - raise NotImplementedError - - def library_option(self, lib): - """Return the compiler option to add 'lib' to the list of libraries - linked into the shared library or executable. - """ - raise NotImplementedError - - def has_function(self, funcname, includes=None, include_dirs=None, - libraries=None, library_dirs=None): - """Return a boolean indicating whether funcname is supported on - the current platform. The optional arguments can be used to - augment the compilation environment. - """ - # this can't be included at module scope because it tries to - # import math which might not be available at that point - maybe - # the necessary logic should just be inlined? - import tempfile - if includes is None: - includes = [] - if include_dirs is None: - include_dirs = [] - if libraries is None: - libraries = [] - if library_dirs is None: - library_dirs = [] - fd, fname = tempfile.mkstemp(".c", funcname, text=True) - f = os.fdopen(fd, "w") - try: - for incl in includes: - f.write("""#include "%s"\n""" % incl) - f.write("""\ -int main (int argc, char **argv) { - %s(); - return 0; -} -""" % funcname) - finally: - f.close() - try: - objects = self.compile([fname], include_dirs=include_dirs) - except CompileError: - return False - - try: - self.link_executable(objects, "a.out", - libraries=libraries, - library_dirs=library_dirs) - except (LinkError, TypeError): - return False - return True - - def find_library_file (self, dirs, lib, debug=0): - """Search the specified list of directories for a static or shared - library file 'lib' and return the full path to that file. If - 'debug' true, look for a debugging version (if that makes sense on - the current platform). Return None if 'lib' wasn't found in any of - the specified directories. - """ - raise NotImplementedError - - # -- Filename generation methods ----------------------------------- - - # The default implementation of the filename generating methods are - # prejudiced towards the Unix/DOS/Windows view of the world: - # * object files are named by replacing the source file extension - # (eg. .c/.cpp -> .o/.obj) - # * library files (shared or static) are named by plugging the - # library name and extension into a format string, eg. - # "lib%s.%s" % (lib_name, ".a") for Unix static libraries - # * executables are named by appending an extension (possibly - # empty) to the program name: eg. progname + ".exe" for - # Windows - # - # To reduce redundant code, these methods expect to find - # several attributes in the current object (presumably defined - # as class attributes): - # * src_extensions - - # list of C/C++ source file extensions, eg. ['.c', '.cpp'] - # * obj_extension - - # object file extension, eg. '.o' or '.obj' - # * static_lib_extension - - # extension for static library files, eg. '.a' or '.lib' - # * shared_lib_extension - - # extension for shared library/object files, eg. '.so', '.dll' - # * static_lib_format - - # format string for generating static library filenames, - # eg. 'lib%s.%s' or '%s.%s' - # * shared_lib_format - # format string for generating shared library filenames - # (probably same as static_lib_format, since the extension - # is one of the intended parameters to the format string) - # * exe_extension - - # extension for executable files, eg. '' or '.exe' - - def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): - if output_dir is None: - output_dir = '' - obj_names = [] - for src_name in source_filenames: - base, ext = os.path.splitext(src_name) - base = os.path.splitdrive(base)[1] # Chop off the drive - base = base[os.path.isabs(base):] # If abs, chop off leading / - if ext not in self.src_extensions: - raise UnknownFileError( - "unknown file type '%s' (from '%s')" % (ext, src_name)) - if strip_dir: - base = os.path.basename(base) - obj_names.append(os.path.join(output_dir, - base + self.obj_extension)) - return obj_names - - def shared_object_filename(self, basename, strip_dir=0, output_dir=''): - assert output_dir is not None - if strip_dir: - basename = os.path.basename(basename) - return os.path.join(output_dir, basename + self.shared_lib_extension) - - def executable_filename(self, basename, strip_dir=0, output_dir=''): - assert output_dir is not None - if strip_dir: - basename = os.path.basename(basename) - return os.path.join(output_dir, basename + (self.exe_extension or '')) - - def library_filename(self, libname, lib_type='static', # or 'shared' - strip_dir=0, output_dir=''): - assert output_dir is not None - if lib_type not in ("static", "shared", "dylib", "xcode_stub"): - raise ValueError( - "'lib_type' must be \"static\", \"shared\", \"dylib\", or \"xcode_stub\"") - fmt = getattr(self, lib_type + "_lib_format") - ext = getattr(self, lib_type + "_lib_extension") - - dir, base = os.path.split(libname) - filename = fmt % (base, ext) - if strip_dir: - dir = '' - - return os.path.join(output_dir, dir, filename) - - - # -- Utility methods ----------------------------------------------- - - def announce(self, msg, level=1): - log.debug(msg) - - def debug_print(self, msg): - from distutils.debug import DEBUG - if DEBUG: - print(msg) - - def warn(self, msg): - sys.stderr.write("warning: %s\n" % msg) - - def execute(self, func, args, msg=None, level=1): - execute(func, args, msg, self.dry_run) - - def spawn(self, cmd): - spawn(cmd, dry_run=self.dry_run) - - def move_file(self, src, dst): - return move_file(src, dst, dry_run=self.dry_run) - - def mkpath (self, name, mode=0o777): - mkpath(name, mode, dry_run=self.dry_run) - - -# Map a sys.platform/os.name ('posix', 'nt') to the default compiler -# type for that platform. Keys are interpreted as re match -# patterns. Order is important; platform mappings are preferred over -# OS names. -_default_compilers = ( - - # Platform string mappings - - # on a cygwin built python we can use gcc like an ordinary UNIXish - # compiler - ('cygwin.*', 'unix'), - - # OS name mappings - ('posix', 'unix'), - ('nt', 'msvc'), - - ) - -def get_default_compiler(osname=None, platform=None): - """Determine the default compiler to use for the given platform. - - osname should be one of the standard Python OS names (i.e. the - ones returned by os.name) and platform the common value - returned by sys.platform for the platform in question. - - The default values are os.name and sys.platform in case the - parameters are not given. - """ - if osname is None: - osname = os.name - if platform is None: - platform = sys.platform - for pattern, compiler in _default_compilers: - if re.match(pattern, platform) is not None or \ - re.match(pattern, osname) is not None: - return compiler - # Default to Unix compiler - return 'unix' - -# Map compiler types to (module_name, class_name) pairs -- ie. where to -# find the code that implements an interface to this compiler. (The module -# is assumed to be in the 'distutils' package.) -compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', - "standard UNIX-style compiler"), - 'msvc': ('_msvccompiler', 'MSVCCompiler', - "Microsoft Visual C++"), - 'cygwin': ('cygwinccompiler', 'CygwinCCompiler', - "Cygwin port of GNU C Compiler for Win32"), - 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler', - "Mingw32 port of GNU C Compiler for Win32"), - 'bcpp': ('bcppcompiler', 'BCPPCompiler', - "Borland C++ Compiler"), - } - -def show_compilers(): - """Print list of available compilers (used by the "--help-compiler" - options to "build", "build_ext", "build_clib"). - """ - # XXX this "knows" that the compiler option it's describing is - # "--compiler", which just happens to be the case for the three - # commands that use it. - from distutils.fancy_getopt import FancyGetopt - compilers = [] - for compiler in compiler_class.keys(): - compilers.append(("compiler="+compiler, None, - compiler_class[compiler][2])) - compilers.sort() - pretty_printer = FancyGetopt(compilers) - pretty_printer.print_help("List of available compilers:") - - -def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0): - """Generate an instance of some CCompiler subclass for the supplied - platform/compiler combination. 'plat' defaults to 'os.name' - (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler - for that platform. Currently only 'posix' and 'nt' are supported, and - the default compilers are "traditional Unix interface" (UnixCCompiler - class) and Visual C++ (MSVCCompiler class). Note that it's perfectly - possible to ask for a Unix compiler object under Windows, and a - Microsoft compiler object under Unix -- if you supply a value for - 'compiler', 'plat' is ignored. - """ - if plat is None: - plat = os.name - - try: - if compiler is None: - compiler = get_default_compiler(plat) - - (module_name, class_name, long_description) = compiler_class[compiler] - except KeyError: - msg = "don't know how to compile C/C++ code on platform '%s'" % plat - if compiler is not None: - msg = msg + " with '%s' compiler" % compiler - raise DistutilsPlatformError(msg) - - try: - module_name = "distutils." + module_name - __import__ (module_name) - module = sys.modules[module_name] - klass = vars(module)[class_name] - except ImportError: - raise DistutilsModuleError( - "can't compile C/C++ code: unable to load module '%s'" % \ - module_name) - except KeyError: - raise DistutilsModuleError( - "can't compile C/C++ code: unable to find class '%s' " - "in module '%s'" % (class_name, module_name)) - - # XXX The None is necessary to preserve backwards compatibility - # with classes that expect verbose to be the first positional - # argument. - return klass(None, dry_run, force) - - -def gen_preprocess_options(macros, include_dirs): - """Generate C pre-processor options (-D, -U, -I) as used by at least - two types of compilers: the typical Unix compiler and Visual C++. - 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,) - means undefine (-U) macro 'name', and (name,value) means define (-D) - macro 'name' to 'value'. 'include_dirs' is just a list of directory - names to be added to the header file search path (-I). Returns a list - of command-line options suitable for either Unix compilers or Visual - C++. - """ - # XXX it would be nice (mainly aesthetic, and so we don't generate - # stupid-looking command lines) to go over 'macros' and eliminate - # redundant definitions/undefinitions (ie. ensure that only the - # latest mention of a particular macro winds up on the command - # line). I don't think it's essential, though, since most (all?) - # Unix C compilers only pay attention to the latest -D or -U - # mention of a macro on their command line. Similar situation for - # 'include_dirs'. I'm punting on both for now. Anyways, weeding out - # redundancies like this should probably be the province of - # CCompiler, since the data structures used are inherited from it - # and therefore common to all CCompiler classes. - pp_opts = [] - for macro in macros: - if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2): - raise TypeError( - "bad macro definition '%s': " - "each element of 'macros' list must be a 1- or 2-tuple" - % macro) - - if len(macro) == 1: # undefine this macro - pp_opts.append("-U%s" % macro[0]) - elif len(macro) == 2: - if macro[1] is None: # define with no explicit value - pp_opts.append("-D%s" % macro[0]) - else: - # XXX *don't* need to be clever about quoting the - # macro value here, because we're going to avoid the - # shell at all costs when we spawn the command! - pp_opts.append("-D%s=%s" % macro) - - for dir in include_dirs: - pp_opts.append("-I%s" % dir) - return pp_opts - - -def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): - """Generate linker options for searching library directories and - linking with specific libraries. 'libraries' and 'library_dirs' are, - respectively, lists of library names (not filenames!) and search - directories. Returns a list of command-line options suitable for use - with some compiler (depending on the two format strings passed in). - """ - lib_opts = [] - - for dir in library_dirs: - lib_opts.append(compiler.library_dir_option(dir)) - - for dir in runtime_library_dirs: - opt = compiler.runtime_library_dir_option(dir) - if isinstance(opt, list): - lib_opts = lib_opts + opt - else: - lib_opts.append(opt) - - # XXX it's important that we *not* remove redundant library mentions! - # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to - # resolve all symbols. I just hope we never have to say "-lfoo obj.o - # -lbar" to get things to work -- that's certainly a possibility, but a - # pretty nasty way to arrange your C code. - - for lib in libraries: - (lib_dir, lib_name) = os.path.split(lib) - if lib_dir: - lib_file = compiler.find_library_file([lib_dir], lib_name) - if lib_file: - lib_opts.append(lib_file) - else: - compiler.warn("no library file corresponding to " - "'%s' found (skipping)" % lib) - else: - lib_opts.append(compiler.library_option (lib)) - return lib_opts diff --git a/cmd.py b/cmd.py deleted file mode 100644 index dba3191e..00000000 --- a/cmd.py +++ /dev/null @@ -1,403 +0,0 @@ -"""distutils.cmd - -Provides the Command class, the base class for the command classes -in the distutils.command package. -""" - -import sys, os, re -from distutils.errors import DistutilsOptionError -from distutils import util, dir_util, file_util, archive_util, dep_util -from distutils import log - -class Command: - """Abstract base class for defining command classes, the "worker bees" - of the Distutils. A useful analogy for command classes is to think of - them as subroutines with local variables called "options". The options - are "declared" in 'initialize_options()' and "defined" (given their - final values, aka "finalized") in 'finalize_options()', both of which - must be defined by every command class. The distinction between the - two is necessary because option values might come from the outside - world (command line, config file, ...), and any options dependent on - other options must be computed *after* these outside influences have - been processed -- hence 'finalize_options()'. The "body" of the - subroutine, where it does all its work based on the values of its - options, is the 'run()' method, which must also be implemented by every - command class. - """ - - # 'sub_commands' formalizes the notion of a "family" of commands, - # eg. "install" as the parent with sub-commands "install_lib", - # "install_headers", etc. The parent of a family of commands - # defines 'sub_commands' as a class attribute; it's a list of - # (command_name : string, predicate : unbound_method | string | None) - # tuples, where 'predicate' is a method of the parent command that - # determines whether the corresponding command is applicable in the - # current situation. (Eg. we "install_headers" is only applicable if - # we have any C header files to install.) If 'predicate' is None, - # that command is always applicable. - # - # 'sub_commands' is usually defined at the *end* of a class, because - # predicates can be unbound methods, so they must already have been - # defined. The canonical example is the "install" command. - sub_commands = [] - - - # -- Creation/initialization methods ------------------------------- - - def __init__(self, dist): - """Create and initialize a new Command object. Most importantly, - invokes the 'initialize_options()' method, which is the real - initializer and depends on the actual command being - instantiated. - """ - # late import because of mutual dependence between these classes - from distutils.dist import Distribution - - if not isinstance(dist, Distribution): - raise TypeError("dist must be a Distribution instance") - if self.__class__ is Command: - raise RuntimeError("Command is an abstract class") - - self.distribution = dist - self.initialize_options() - - # Per-command versions of the global flags, so that the user can - # customize Distutils' behaviour command-by-command and let some - # commands fall back on the Distribution's behaviour. None means - # "not defined, check self.distribution's copy", while 0 or 1 mean - # false and true (duh). Note that this means figuring out the real - # value of each flag is a touch complicated -- hence "self._dry_run" - # will be handled by __getattr__, below. - # XXX This needs to be fixed. - self._dry_run = None - - # verbose is largely ignored, but needs to be set for - # backwards compatibility (I think)? - self.verbose = dist.verbose - - # Some commands define a 'self.force' option to ignore file - # timestamps, but methods defined *here* assume that - # 'self.force' exists for all commands. So define it here - # just to be safe. - self.force = None - - # The 'help' flag is just used for command-line parsing, so - # none of that complicated bureaucracy is needed. - self.help = 0 - - # 'finalized' records whether or not 'finalize_options()' has been - # called. 'finalize_options()' itself should not pay attention to - # this flag: it is the business of 'ensure_finalized()', which - # always calls 'finalize_options()', to respect/update it. - self.finalized = 0 - - # XXX A more explicit way to customize dry_run would be better. - def __getattr__(self, attr): - if attr == 'dry_run': - myval = getattr(self, "_" + attr) - if myval is None: - return getattr(self.distribution, attr) - else: - return myval - else: - raise AttributeError(attr) - - def ensure_finalized(self): - if not self.finalized: - self.finalize_options() - self.finalized = 1 - - # Subclasses must define: - # initialize_options() - # provide default values for all options; may be customized by - # setup script, by options from config file(s), or by command-line - # options - # finalize_options() - # decide on the final values for all options; this is called - # after all possible intervention from the outside world - # (command-line, option file, etc.) has been processed - # run() - # run the command: do whatever it is we're here to do, - # controlled by the command's various option values - - def initialize_options(self): - """Set default values for all the options that this command - supports. Note that these defaults may be overridden by other - commands, by the setup script, by config files, or by the - command-line. Thus, this is not the place to code dependencies - between options; generally, 'initialize_options()' implementations - are just a bunch of "self.foo = None" assignments. - - This method must be implemented by all command classes. - """ - raise RuntimeError("abstract method -- subclass %s must override" - % self.__class__) - - def finalize_options(self): - """Set final values for all the options that this command supports. - This is always called as late as possible, ie. after any option - assignments from the command-line or from other commands have been - done. Thus, this is the place to code option dependencies: if - 'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as - long as 'foo' still has the same value it was assigned in - 'initialize_options()'. - - This method must be implemented by all command classes. - """ - raise RuntimeError("abstract method -- subclass %s must override" - % self.__class__) - - - def dump_options(self, header=None, indent=""): - from distutils.fancy_getopt import longopt_xlate - if header is None: - header = "command options for '%s':" % self.get_command_name() - self.announce(indent + header, level=log.INFO) - indent = indent + " " - for (option, _, _) in self.user_options: - option = option.translate(longopt_xlate) - if option[-1] == "=": - option = option[:-1] - value = getattr(self, option) - self.announce(indent + "%s = %s" % (option, value), - level=log.INFO) - - def run(self): - """A command's raison d'etre: carry out the action it exists to - perform, controlled by the options initialized in - 'initialize_options()', customized by other commands, the setup - script, the command-line, and config files, and finalized in - 'finalize_options()'. All terminal output and filesystem - interaction should be done by 'run()'. - - This method must be implemented by all command classes. - """ - raise RuntimeError("abstract method -- subclass %s must override" - % self.__class__) - - def announce(self, msg, level=1): - """If the current verbosity level is of greater than or equal to - 'level' print 'msg' to stdout. - """ - log.log(level, msg) - - def debug_print(self, msg): - """Print 'msg' to stdout if the global DEBUG (taken from the - DISTUTILS_DEBUG environment variable) flag is true. - """ - from distutils.debug import DEBUG - if DEBUG: - print(msg) - sys.stdout.flush() - - - # -- Option validation methods ------------------------------------- - # (these are very handy in writing the 'finalize_options()' method) - # - # NB. the general philosophy here is to ensure that a particular option - # value meets certain type and value constraints. If not, we try to - # force it into conformance (eg. if we expect a list but have a string, - # split the string on comma and/or whitespace). If we can't force the - # option into conformance, raise DistutilsOptionError. Thus, command - # classes need do nothing more than (eg.) - # self.ensure_string_list('foo') - # and they can be guaranteed that thereafter, self.foo will be - # a list of strings. - - def _ensure_stringlike(self, option, what, default=None): - val = getattr(self, option) - if val is None: - setattr(self, option, default) - return default - elif not isinstance(val, str): - raise DistutilsOptionError("'%s' must be a %s (got `%s`)" - % (option, what, val)) - return val - - def ensure_string(self, option, default=None): - """Ensure that 'option' is a string; if not defined, set it to - 'default'. - """ - self._ensure_stringlike(option, "string", default) - - def ensure_string_list(self, option): - r"""Ensure that 'option' is a list of strings. If 'option' is - currently a string, we split it either on /,\s*/ or /\s+/, so - "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become - ["foo", "bar", "baz"]. - """ - val = getattr(self, option) - if val is None: - return - elif isinstance(val, str): - setattr(self, option, re.split(r',\s*|\s+', val)) - else: - if isinstance(val, list): - ok = all(isinstance(v, str) for v in val) - else: - ok = False - if not ok: - raise DistutilsOptionError( - "'%s' must be a list of strings (got %r)" - % (option, val)) - - def _ensure_tested_string(self, option, tester, what, error_fmt, - default=None): - val = self._ensure_stringlike(option, what, default) - if val is not None and not tester(val): - raise DistutilsOptionError(("error in '%s' option: " + error_fmt) - % (option, val)) - - def ensure_filename(self, option): - """Ensure that 'option' is the name of an existing file.""" - self._ensure_tested_string(option, os.path.isfile, - "filename", - "'%s' does not exist or is not a file") - - def ensure_dirname(self, option): - self._ensure_tested_string(option, os.path.isdir, - "directory name", - "'%s' does not exist or is not a directory") - - - # -- Convenience methods for commands ------------------------------ - - def get_command_name(self): - if hasattr(self, 'command_name'): - return self.command_name - else: - return self.__class__.__name__ - - def set_undefined_options(self, src_cmd, *option_pairs): - """Set the values of any "undefined" options from corresponding - option values in some other command object. "Undefined" here means - "is None", which is the convention used to indicate that an option - has not been changed between 'initialize_options()' and - 'finalize_options()'. Usually called from 'finalize_options()' for - options that depend on some other command rather than another - option of the same command. 'src_cmd' is the other command from - which option values will be taken (a command object will be created - for it if necessary); the remaining arguments are - '(src_option,dst_option)' tuples which mean "take the value of - 'src_option' in the 'src_cmd' command object, and copy it to - 'dst_option' in the current command object". - """ - # Option_pairs: list of (src_option, dst_option) tuples - src_cmd_obj = self.distribution.get_command_obj(src_cmd) - src_cmd_obj.ensure_finalized() - for (src_option, dst_option) in option_pairs: - if getattr(self, dst_option) is None: - setattr(self, dst_option, getattr(src_cmd_obj, src_option)) - - def get_finalized_command(self, command, create=1): - """Wrapper around Distribution's 'get_command_obj()' method: find - (create if necessary and 'create' is true) the command object for - 'command', call its 'ensure_finalized()' method, and return the - finalized command object. - """ - cmd_obj = self.distribution.get_command_obj(command, create) - cmd_obj.ensure_finalized() - return cmd_obj - - # XXX rename to 'get_reinitialized_command()'? (should do the - # same in dist.py, if so) - def reinitialize_command(self, command, reinit_subcommands=0): - return self.distribution.reinitialize_command(command, - reinit_subcommands) - - def run_command(self, command): - """Run some other command: uses the 'run_command()' method of - Distribution, which creates and finalizes the command object if - necessary and then invokes its 'run()' method. - """ - self.distribution.run_command(command) - - def get_sub_commands(self): - """Determine the sub-commands that are relevant in the current - distribution (ie., that need to be run). This is based on the - 'sub_commands' class attribute: each tuple in that list may include - a method that we call to determine if the subcommand needs to be - run for the current distribution. Return a list of command names. - """ - commands = [] - for (cmd_name, method) in self.sub_commands: - if method is None or method(self): - commands.append(cmd_name) - return commands - - - # -- External world manipulation ----------------------------------- - - def warn(self, msg): - log.warn("warning: %s: %s\n", self.get_command_name(), msg) - - def execute(self, func, args, msg=None, level=1): - util.execute(func, args, msg, dry_run=self.dry_run) - - def mkpath(self, name, mode=0o777): - dir_util.mkpath(name, mode, dry_run=self.dry_run) - - def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1, - link=None, level=1): - """Copy a file respecting verbose, dry-run and force flags. (The - former two default to whatever is in the Distribution object, and - the latter defaults to false for commands that don't define it.)""" - return file_util.copy_file(infile, outfile, preserve_mode, - preserve_times, not self.force, link, - dry_run=self.dry_run) - - def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1, - preserve_symlinks=0, level=1): - """Copy an entire directory tree respecting verbose, dry-run, - and force flags. - """ - return dir_util.copy_tree(infile, outfile, preserve_mode, - preserve_times, preserve_symlinks, - not self.force, dry_run=self.dry_run) - - def move_file (self, src, dst, level=1): - """Move a file respecting dry-run flag.""" - return file_util.move_file(src, dst, dry_run=self.dry_run) - - def spawn(self, cmd, search_path=1, level=1): - """Spawn an external command respecting dry-run flag.""" - from distutils.spawn import spawn - spawn(cmd, search_path, dry_run=self.dry_run) - - def make_archive(self, base_name, format, root_dir=None, base_dir=None, - owner=None, group=None): - return archive_util.make_archive(base_name, format, root_dir, base_dir, - dry_run=self.dry_run, - owner=owner, group=group) - - def make_file(self, infiles, outfile, func, args, - exec_msg=None, skip_msg=None, level=1): - """Special case of 'execute()' for operations that process one or - more input files and generate one output file. Works just like - 'execute()', except the operation is skipped and a different - message printed if 'outfile' already exists and is newer than all - files listed in 'infiles'. If the command defined 'self.force', - and it is true, then the command is unconditionally run -- does no - timestamp checks. - """ - if skip_msg is None: - skip_msg = "skipping %s (inputs unchanged)" % outfile - - # Allow 'infiles' to be a single string - if isinstance(infiles, str): - infiles = (infiles,) - elif not isinstance(infiles, (list, tuple)): - raise TypeError( - "'infiles' must be a string, or a list or tuple of strings") - - if exec_msg is None: - exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles)) - - # If 'outfile' must be regenerated (either because it doesn't - # exist, is out-of-date, or the 'force' flag is true) then - # perform the action that presumably regenerates it - if self.force or dep_util.newer_group(infiles, outfile): - self.execute(func, args, exec_msg, level) - # Otherwise, print the "skip" message - else: - log.debug(skip_msg) diff --git a/command/__init__.py b/command/__init__.py deleted file mode 100644 index 481eea9f..00000000 --- a/command/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -"""distutils.command - -Package containing implementation of all the standard Distutils -commands.""" - -__all__ = ['build', - 'build_py', - 'build_ext', - 'build_clib', - 'build_scripts', - 'clean', - 'install', - 'install_lib', - 'install_headers', - 'install_scripts', - 'install_data', - 'sdist', - 'register', - 'bdist', - 'bdist_dumb', - 'bdist_rpm', - 'bdist_wininst', - 'check', - 'upload', - # These two are reserved for future use: - #'bdist_sdux', - #'bdist_pkgtool', - # Note: - # bdist_packager is not included because it only provides - # an abstract base class - ] diff --git a/command/bdist.py b/command/bdist.py deleted file mode 100644 index 014871d2..00000000 --- a/command/bdist.py +++ /dev/null @@ -1,143 +0,0 @@ -"""distutils.command.bdist - -Implements the Distutils 'bdist' command (create a built [binary] -distribution).""" - -import os -from distutils.core import Command -from distutils.errors import * -from distutils.util import get_platform - - -def show_formats(): - """Print list of available formats (arguments to "--format" option). - """ - from distutils.fancy_getopt import FancyGetopt - formats = [] - for format in bdist.format_commands: - formats.append(("formats=" + format, None, - bdist.format_command[format][1])) - pretty_printer = FancyGetopt(formats) - pretty_printer.print_help("List of available distribution formats:") - - -class bdist(Command): - - description = "create a built (binary) distribution" - - user_options = [('bdist-base=', 'b', - "temporary directory for creating built distributions"), - ('plat-name=', 'p', - "platform name to embed in generated filenames " - "(default: %s)" % get_platform()), - ('formats=', None, - "formats for distribution (comma-separated list)"), - ('dist-dir=', 'd', - "directory to put final built distributions in " - "[default: dist]"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ('owner=', 'u', - "Owner name used when creating a tar file" - " [default: current user]"), - ('group=', 'g', - "Group name used when creating a tar file" - " [default: current group]"), - ] - - boolean_options = ['skip-build'] - - help_options = [ - ('help-formats', None, - "lists available distribution formats", show_formats), - ] - - # The following commands do not take a format option from bdist - no_format_option = ('bdist_rpm',) - - # This won't do in reality: will need to distinguish RPM-ish Linux, - # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. - default_format = {'posix': 'gztar', - 'nt': 'zip'} - - # Establish the preferred order (for the --help-formats option). - format_commands = ['rpm', 'gztar', 'bztar', 'xztar', 'ztar', 'tar', - 'wininst', 'zip', 'msi'] - - # And the real information. - format_command = {'rpm': ('bdist_rpm', "RPM distribution"), - 'gztar': ('bdist_dumb', "gzip'ed tar file"), - 'bztar': ('bdist_dumb', "bzip2'ed tar file"), - 'xztar': ('bdist_dumb', "xz'ed tar file"), - 'ztar': ('bdist_dumb', "compressed tar file"), - 'tar': ('bdist_dumb', "tar file"), - 'wininst': ('bdist_wininst', - "Windows executable installer"), - 'zip': ('bdist_dumb', "ZIP file"), - 'msi': ('bdist_msi', "Microsoft Installer") - } - - - def initialize_options(self): - self.bdist_base = None - self.plat_name = None - self.formats = None - self.dist_dir = None - self.skip_build = 0 - self.group = None - self.owner = None - - def finalize_options(self): - # have to finalize 'plat_name' before 'bdist_base' - if self.plat_name is None: - if self.skip_build: - self.plat_name = get_platform() - else: - self.plat_name = self.get_finalized_command('build').plat_name - - # 'bdist_base' -- parent of per-built-distribution-format - # temporary directories (eg. we'll probably have - # "build/bdist./dumb", "build/bdist./rpm", etc.) - if self.bdist_base is None: - build_base = self.get_finalized_command('build').build_base - self.bdist_base = os.path.join(build_base, - 'bdist.' + self.plat_name) - - self.ensure_string_list('formats') - if self.formats is None: - try: - self.formats = [self.default_format[os.name]] - except KeyError: - raise DistutilsPlatformError( - "don't know how to create built distributions " - "on platform %s" % os.name) - - if self.dist_dir is None: - self.dist_dir = "dist" - - def run(self): - # Figure out which sub-commands we need to run. - commands = [] - for format in self.formats: - try: - commands.append(self.format_command[format][0]) - except KeyError: - raise DistutilsOptionError("invalid format '%s'" % format) - - # Reinitialize and run each command. - for i in range(len(self.formats)): - cmd_name = commands[i] - sub_cmd = self.reinitialize_command(cmd_name) - if cmd_name not in self.no_format_option: - sub_cmd.format = self.formats[i] - - # passing the owner and group names for tar archiving - if cmd_name == 'bdist_dumb': - sub_cmd.owner = self.owner - sub_cmd.group = self.group - - # If we're going to need to run this command again, tell it to - # keep its temporary files around so subsequent runs go faster. - if cmd_name in commands[i+1:]: - sub_cmd.keep_temp = 1 - self.run_command(cmd_name) diff --git a/command/bdist_dumb.py b/command/bdist_dumb.py deleted file mode 100644 index f0d6b5b8..00000000 --- a/command/bdist_dumb.py +++ /dev/null @@ -1,123 +0,0 @@ -"""distutils.command.bdist_dumb - -Implements the Distutils 'bdist_dumb' command (create a "dumb" built -distribution -- i.e., just an archive to be unpacked under $prefix or -$exec_prefix).""" - -import os -from distutils.core import Command -from distutils.util import get_platform -from distutils.dir_util import remove_tree, ensure_relative -from distutils.errors import * -from distutils.sysconfig import get_python_version -from distutils import log - -class bdist_dumb(Command): - - description = "create a \"dumb\" built distribution" - - user_options = [('bdist-dir=', 'd', - "temporary directory for creating the distribution"), - ('plat-name=', 'p', - "platform name to embed in generated filenames " - "(default: %s)" % get_platform()), - ('format=', 'f', - "archive format to create (tar, gztar, bztar, xztar, " - "ztar, zip)"), - ('keep-temp', 'k', - "keep the pseudo-installation tree around after " + - "creating the distribution archive"), - ('dist-dir=', 'd', - "directory to put final built distributions in"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ('relative', None, - "build the archive using relative paths " - "(default: false)"), - ('owner=', 'u', - "Owner name used when creating a tar file" - " [default: current user]"), - ('group=', 'g', - "Group name used when creating a tar file" - " [default: current group]"), - ] - - boolean_options = ['keep-temp', 'skip-build', 'relative'] - - default_format = { 'posix': 'gztar', - 'nt': 'zip' } - - def initialize_options(self): - self.bdist_dir = None - self.plat_name = None - self.format = None - self.keep_temp = 0 - self.dist_dir = None - self.skip_build = None - self.relative = 0 - self.owner = None - self.group = None - - def finalize_options(self): - if self.bdist_dir is None: - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'dumb') - - if self.format is None: - try: - self.format = self.default_format[os.name] - except KeyError: - raise DistutilsPlatformError( - "don't know how to create dumb built distributions " - "on platform %s" % os.name) - - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ('skip_build', 'skip_build')) - - def run(self): - if not self.skip_build: - self.run_command('build') - - install = self.reinitialize_command('install', reinit_subcommands=1) - install.root = self.bdist_dir - install.skip_build = self.skip_build - install.warn_dir = 0 - - log.info("installing to %s", self.bdist_dir) - self.run_command('install') - - # And make an archive relative to the root of the - # pseudo-installation tree. - archive_basename = "%s.%s" % (self.distribution.get_fullname(), - self.plat_name) - - pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) - if not self.relative: - archive_root = self.bdist_dir - else: - if (self.distribution.has_ext_modules() and - (install.install_base != install.install_platbase)): - raise DistutilsPlatformError( - "can't make a dumb built distribution where " - "base and platbase are different (%s, %s)" - % (repr(install.install_base), - repr(install.install_platbase))) - else: - archive_root = os.path.join(self.bdist_dir, - ensure_relative(install.install_base)) - - # Make the archive - filename = self.make_archive(pseudoinstall_root, - self.format, root_dir=archive_root, - owner=self.owner, group=self.group) - if self.distribution.has_ext_modules(): - pyversion = get_python_version() - else: - pyversion = 'any' - self.distribution.dist_files.append(('bdist_dumb', pyversion, - filename)) - - if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) diff --git a/command/bdist_msi.py b/command/bdist_msi.py deleted file mode 100644 index 0863a188..00000000 --- a/command/bdist_msi.py +++ /dev/null @@ -1,749 +0,0 @@ -# Copyright (C) 2005, 2006 Martin von Löwis -# Licensed to PSF under a Contributor Agreement. -# The bdist_wininst command proper -# based on bdist_wininst -""" -Implements the bdist_msi command. -""" - -import os -import sys -import warnings -from distutils.core import Command -from distutils.dir_util import remove_tree -from distutils.sysconfig import get_python_version -from distutils.version import StrictVersion -from distutils.errors import DistutilsOptionError -from distutils.util import get_platform -from distutils import log -import msilib -from msilib import schema, sequence, text -from msilib import Directory, Feature, Dialog, add_data - -class PyDialog(Dialog): - """Dialog class with a fixed layout: controls at the top, then a ruler, - then a list of buttons: back, next, cancel. Optionally a bitmap at the - left.""" - def __init__(self, *args, **kw): - """Dialog(database, name, x, y, w, h, attributes, title, first, - default, cancel, bitmap=true)""" - Dialog.__init__(self, *args) - ruler = self.h - 36 - bmwidth = 152*ruler/328 - #if kw.get("bitmap", True): - # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin") - self.line("BottomLine", 0, ruler, self.w, 0) - - def title(self, title): - "Set the title text of the dialog at the top." - # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix, - # text, in VerdanaBold10 - self.text("Title", 15, 10, 320, 60, 0x30003, - r"{\VerdanaBold10}%s" % title) - - def back(self, title, next, name = "Back", active = 1): - """Add a back button with a given title, the tab-next button, - its name in the Control table, possibly initially disabled. - - Return the button, so that events can be associated""" - if active: - flags = 3 # Visible|Enabled - else: - flags = 1 # Visible - return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next) - - def cancel(self, title, next, name = "Cancel", active = 1): - """Add a cancel button with a given title, the tab-next button, - its name in the Control table, possibly initially disabled. - - Return the button, so that events can be associated""" - if active: - flags = 3 # Visible|Enabled - else: - flags = 1 # Visible - return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next) - - def next(self, title, next, name = "Next", active = 1): - """Add a Next button with a given title, the tab-next button, - its name in the Control table, possibly initially disabled. - - Return the button, so that events can be associated""" - if active: - flags = 3 # Visible|Enabled - else: - flags = 1 # Visible - return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next) - - def xbutton(self, name, title, next, xpos): - """Add a button with a given title, the tab-next button, - its name in the Control table, giving its x position; the - y-position is aligned with the other buttons. - - Return the button, so that events can be associated""" - return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next) - -class bdist_msi(Command): - - description = "create a Microsoft Installer (.msi) binary distribution" - - user_options = [('bdist-dir=', None, - "temporary directory for creating the distribution"), - ('plat-name=', 'p', - "platform name to embed in generated filenames " - "(default: %s)" % get_platform()), - ('keep-temp', 'k', - "keep the pseudo-installation tree around after " + - "creating the distribution archive"), - ('target-version=', None, - "require a specific python version" + - " on the target system"), - ('no-target-compile', 'c', - "do not compile .py to .pyc on the target system"), - ('no-target-optimize', 'o', - "do not compile .py to .pyo (optimized) " - "on the target system"), - ('dist-dir=', 'd', - "directory to put final built distributions in"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ('install-script=', None, - "basename of installation script to be run after " - "installation or before deinstallation"), - ('pre-install-script=', None, - "Fully qualified filename of a script to be run before " - "any files are installed. This script need not be in the " - "distribution"), - ] - - boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', - 'skip-build'] - - all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4', - '2.5', '2.6', '2.7', '2.8', '2.9', - '3.0', '3.1', '3.2', '3.3', '3.4', - '3.5', '3.6', '3.7', '3.8', '3.9'] - other_version = 'X' - - def __init__(self, *args, **kw): - super().__init__(*args, **kw) - warnings.warn("bdist_msi command is deprecated since Python 3.9, " - "use bdist_wheel (wheel packages) instead", - DeprecationWarning, 2) - - def initialize_options(self): - self.bdist_dir = None - self.plat_name = None - self.keep_temp = 0 - self.no_target_compile = 0 - self.no_target_optimize = 0 - self.target_version = None - self.dist_dir = None - self.skip_build = None - self.install_script = None - self.pre_install_script = None - self.versions = None - - def finalize_options(self): - self.set_undefined_options('bdist', ('skip_build', 'skip_build')) - - if self.bdist_dir is None: - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'msi') - - short_version = get_python_version() - if (not self.target_version) and self.distribution.has_ext_modules(): - self.target_version = short_version - - if self.target_version: - self.versions = [self.target_version] - if not self.skip_build and self.distribution.has_ext_modules()\ - and self.target_version != short_version: - raise DistutilsOptionError( - "target version can only be %s, or the '--skip-build'" - " option must be specified" % (short_version,)) - else: - self.versions = list(self.all_versions) - - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ) - - if self.pre_install_script: - raise DistutilsOptionError( - "the pre-install-script feature is not yet implemented") - - if self.install_script: - for script in self.distribution.scripts: - if self.install_script == os.path.basename(script): - break - else: - raise DistutilsOptionError( - "install_script '%s' not found in scripts" - % self.install_script) - self.install_script_key = None - - def run(self): - if not self.skip_build: - self.run_command('build') - - install = self.reinitialize_command('install', reinit_subcommands=1) - install.prefix = self.bdist_dir - install.skip_build = self.skip_build - install.warn_dir = 0 - - install_lib = self.reinitialize_command('install_lib') - # we do not want to include pyc or pyo files - install_lib.compile = 0 - install_lib.optimize = 0 - - if self.distribution.has_ext_modules(): - # If we are building an installer for a Python version other - # than the one we are currently running, then we need to ensure - # our build_lib reflects the other Python version rather than ours. - # Note that for target_version!=sys.version, we must have skipped the - # build step, so there is no issue with enforcing the build of this - # version. - target_version = self.target_version - if not target_version: - assert self.skip_build, "Should have already checked this" - target_version = '%d.%d' % sys.version_info[:2] - plat_specifier = ".%s-%s" % (self.plat_name, target_version) - build = self.get_finalized_command('build') - build.build_lib = os.path.join(build.build_base, - 'lib' + plat_specifier) - - log.info("installing to %s", self.bdist_dir) - install.ensure_finalized() - - # avoid warning of 'install_lib' about installing - # into a directory not in sys.path - sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) - - install.run() - - del sys.path[0] - - self.mkpath(self.dist_dir) - fullname = self.distribution.get_fullname() - installer_name = self.get_installer_filename(fullname) - installer_name = os.path.abspath(installer_name) - if os.path.exists(installer_name): os.unlink(installer_name) - - metadata = self.distribution.metadata - author = metadata.author - if not author: - author = metadata.maintainer - if not author: - author = "UNKNOWN" - version = metadata.get_version() - # ProductVersion must be strictly numeric - # XXX need to deal with prerelease versions - sversion = "%d.%d.%d" % StrictVersion(version).version - # Prefix ProductName with Python x.y, so that - # it sorts together with the other Python packages - # in Add-Remove-Programs (APR) - fullname = self.distribution.get_fullname() - if self.target_version: - product_name = "Python %s %s" % (self.target_version, fullname) - else: - product_name = "Python %s" % (fullname) - self.db = msilib.init_database(installer_name, schema, - product_name, msilib.gen_uuid(), - sversion, author) - msilib.add_tables(self.db, sequence) - props = [('DistVersion', version)] - email = metadata.author_email or metadata.maintainer_email - if email: - props.append(("ARPCONTACT", email)) - if metadata.url: - props.append(("ARPURLINFOABOUT", metadata.url)) - if props: - add_data(self.db, 'Property', props) - - self.add_find_python() - self.add_files() - self.add_scripts() - self.add_ui() - self.db.Commit() - - if hasattr(self.distribution, 'dist_files'): - tup = 'bdist_msi', self.target_version or 'any', fullname - self.distribution.dist_files.append(tup) - - if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) - - def add_files(self): - db = self.db - cab = msilib.CAB("distfiles") - rootdir = os.path.abspath(self.bdist_dir) - - root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir") - f = Feature(db, "Python", "Python", "Everything", - 0, 1, directory="TARGETDIR") - - items = [(f, root, '')] - for version in self.versions + [self.other_version]: - target = "TARGETDIR" + version - name = default = "Python" + version - desc = "Everything" - if version is self.other_version: - title = "Python from another location" - level = 2 - else: - title = "Python %s from registry" % version - level = 1 - f = Feature(db, name, title, desc, 1, level, directory=target) - dir = Directory(db, cab, root, rootdir, target, default) - items.append((f, dir, version)) - db.Commit() - - seen = {} - for feature, dir, version in items: - todo = [dir] - while todo: - dir = todo.pop() - for file in os.listdir(dir.absolute): - afile = os.path.join(dir.absolute, file) - if os.path.isdir(afile): - short = "%s|%s" % (dir.make_short(file), file) - default = file + version - newdir = Directory(db, cab, dir, file, default, short) - todo.append(newdir) - else: - if not dir.component: - dir.start_component(dir.logical, feature, 0) - if afile not in seen: - key = seen[afile] = dir.add_file(file) - if file==self.install_script: - if self.install_script_key: - raise DistutilsOptionError( - "Multiple files with name %s" % file) - self.install_script_key = '[#%s]' % key - else: - key = seen[afile] - add_data(self.db, "DuplicateFile", - [(key + version, dir.component, key, None, dir.logical)]) - db.Commit() - cab.commit(db) - - def add_find_python(self): - """Adds code to the installer to compute the location of Python. - - Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the - registry for each version of Python. - - Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined, - else from PYTHON.MACHINE.X.Y. - - Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe""" - - start = 402 - for ver in self.versions: - install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver - machine_reg = "python.machine." + ver - user_reg = "python.user." + ver - machine_prop = "PYTHON.MACHINE." + ver - user_prop = "PYTHON.USER." + ver - machine_action = "PythonFromMachine" + ver - user_action = "PythonFromUser" + ver - exe_action = "PythonExe" + ver - target_dir_prop = "TARGETDIR" + ver - exe_prop = "PYTHON" + ver - if msilib.Win64: - # type: msidbLocatorTypeRawValue + msidbLocatorType64bit - Type = 2+16 - else: - Type = 2 - add_data(self.db, "RegLocator", - [(machine_reg, 2, install_path, None, Type), - (user_reg, 1, install_path, None, Type)]) - add_data(self.db, "AppSearch", - [(machine_prop, machine_reg), - (user_prop, user_reg)]) - add_data(self.db, "CustomAction", - [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"), - (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"), - (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"), - ]) - add_data(self.db, "InstallExecuteSequence", - [(machine_action, machine_prop, start), - (user_action, user_prop, start + 1), - (exe_action, None, start + 2), - ]) - add_data(self.db, "InstallUISequence", - [(machine_action, machine_prop, start), - (user_action, user_prop, start + 1), - (exe_action, None, start + 2), - ]) - add_data(self.db, "Condition", - [("Python" + ver, 0, "NOT TARGETDIR" + ver)]) - start += 4 - assert start < 500 - - def add_scripts(self): - if self.install_script: - start = 6800 - for ver in self.versions + [self.other_version]: - install_action = "install_script." + ver - exe_prop = "PYTHON" + ver - add_data(self.db, "CustomAction", - [(install_action, 50, exe_prop, self.install_script_key)]) - add_data(self.db, "InstallExecuteSequence", - [(install_action, "&Python%s=3" % ver, start)]) - start += 1 - # XXX pre-install scripts are currently refused in finalize_options() - # but if this feature is completed, it will also need to add - # entries for each version as the above code does - if self.pre_install_script: - scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") - with open(scriptfn, "w") as f: - # The batch file will be executed with [PYTHON], so that %1 - # is the path to the Python interpreter; %0 will be the path - # of the batch file. - # rem =""" - # %1 %0 - # exit - # """ - # - f.write('rem ="""\n%1 %0\nexit\n"""\n') - with open(self.pre_install_script) as fin: - f.write(fin.read()) - add_data(self.db, "Binary", - [("PreInstall", msilib.Binary(scriptfn)) - ]) - add_data(self.db, "CustomAction", - [("PreInstall", 2, "PreInstall", None) - ]) - add_data(self.db, "InstallExecuteSequence", - [("PreInstall", "NOT Installed", 450)]) - - - def add_ui(self): - db = self.db - x = y = 50 - w = 370 - h = 300 - title = "[ProductName] Setup" - - # see "Dialog Style Bits" - modal = 3 # visible | modal - modeless = 1 # visible - track_disk_space = 32 - - # UI customization properties - add_data(db, "Property", - # See "DefaultUIFont Property" - [("DefaultUIFont", "DlgFont8"), - # See "ErrorDialog Style Bit" - ("ErrorDialog", "ErrorDlg"), - ("Progress1", "Install"), # modified in maintenance type dlg - ("Progress2", "installs"), - ("MaintenanceForm_Action", "Repair"), - # possible values: ALL, JUSTME - ("WhichUsers", "ALL") - ]) - - # Fonts, see "TextStyle Table" - add_data(db, "TextStyle", - [("DlgFont8", "Tahoma", 9, None, 0), - ("DlgFontBold8", "Tahoma", 8, None, 1), #bold - ("VerdanaBold10", "Verdana", 10, None, 1), - ("VerdanaRed9", "Verdana", 9, 255, 0), - ]) - - # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table" - # Numbers indicate sequence; see sequence.py for how these action integrate - add_data(db, "InstallUISequence", - [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140), - ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141), - # In the user interface, assume all-users installation if privileged. - ("SelectFeaturesDlg", "Not Installed", 1230), - # XXX no support for resume installations yet - #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240), - ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), - ("ProgressDlg", None, 1280)]) - - add_data(db, 'ActionText', text.ActionText) - add_data(db, 'UIText', text.UIText) - ##################################################################### - # Standard dialogs: FatalError, UserExit, ExitDialog - fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title, - "Finish", "Finish", "Finish") - fatal.title("[ProductName] Installer ended prematurely") - fatal.back("< Back", "Finish", active = 0) - fatal.cancel("Cancel", "Back", active = 0) - fatal.text("Description1", 15, 70, 320, 80, 0x30003, - "[ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again.") - fatal.text("Description2", 15, 155, 320, 20, 0x30003, - "Click the Finish button to exit the Installer.") - c=fatal.next("Finish", "Cancel", name="Finish") - c.event("EndDialog", "Exit") - - user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title, - "Finish", "Finish", "Finish") - user_exit.title("[ProductName] Installer was interrupted") - user_exit.back("< Back", "Finish", active = 0) - user_exit.cancel("Cancel", "Back", active = 0) - user_exit.text("Description1", 15, 70, 320, 80, 0x30003, - "[ProductName] setup was interrupted. Your system has not been modified. " - "To install this program at a later time, please run the installation again.") - user_exit.text("Description2", 15, 155, 320, 20, 0x30003, - "Click the Finish button to exit the Installer.") - c = user_exit.next("Finish", "Cancel", name="Finish") - c.event("EndDialog", "Exit") - - exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title, - "Finish", "Finish", "Finish") - exit_dialog.title("Completing the [ProductName] Installer") - exit_dialog.back("< Back", "Finish", active = 0) - exit_dialog.cancel("Cancel", "Back", active = 0) - exit_dialog.text("Description", 15, 235, 320, 20, 0x30003, - "Click the Finish button to exit the Installer.") - c = exit_dialog.next("Finish", "Cancel", name="Finish") - c.event("EndDialog", "Return") - - ##################################################################### - # Required dialog: FilesInUse, ErrorDlg - inuse = PyDialog(db, "FilesInUse", - x, y, w, h, - 19, # KeepModeless|Modal|Visible - title, - "Retry", "Retry", "Retry", bitmap=False) - inuse.text("Title", 15, 6, 200, 15, 0x30003, - r"{\DlgFontBold8}Files in Use") - inuse.text("Description", 20, 23, 280, 20, 0x30003, - "Some files that need to be updated are currently in use.") - inuse.text("Text", 20, 55, 330, 50, 3, - "The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.") - inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess", - None, None, None) - c=inuse.back("Exit", "Ignore", name="Exit") - c.event("EndDialog", "Exit") - c=inuse.next("Ignore", "Retry", name="Ignore") - c.event("EndDialog", "Ignore") - c=inuse.cancel("Retry", "Exit", name="Retry") - c.event("EndDialog","Retry") - - # See "Error Dialog". See "ICE20" for the required names of the controls. - error = Dialog(db, "ErrorDlg", - 50, 10, 330, 101, - 65543, # Error|Minimize|Modal|Visible - title, - "ErrorText", None, None) - error.text("ErrorText", 50,9,280,48,3, "") - #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None) - error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo") - error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes") - error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort") - error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel") - error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore") - error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk") - error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry") - - ##################################################################### - # Global "Query Cancel" dialog - cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title, - "No", "No", "No") - cancel.text("Text", 48, 15, 194, 30, 3, - "Are you sure you want to cancel [ProductName] installation?") - #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None, - # "py.ico", None, None) - c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No") - c.event("EndDialog", "Exit") - - c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes") - c.event("EndDialog", "Return") - - ##################################################################### - # Global "Wait for costing" dialog - costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title, - "Return", "Return", "Return") - costing.text("Text", 48, 15, 194, 30, 3, - "Please wait while the installer finishes determining your disk space requirements.") - c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None) - c.event("EndDialog", "Exit") - - ##################################################################### - # Preparation dialog: no user input except cancellation - prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title, - "Cancel", "Cancel", "Cancel") - prep.text("Description", 15, 70, 320, 40, 0x30003, - "Please wait while the Installer prepares to guide you through the installation.") - prep.title("Welcome to the [ProductName] Installer") - c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...") - c.mapping("ActionText", "Text") - c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None) - c.mapping("ActionData", "Text") - prep.back("Back", None, active=0) - prep.next("Next", None, active=0) - c=prep.cancel("Cancel", None) - c.event("SpawnDialog", "CancelDlg") - - ##################################################################### - # Feature (Python directory) selection - seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title, - "Next", "Next", "Cancel") - seldlg.title("Select Python Installations") - - seldlg.text("Hint", 15, 30, 300, 20, 3, - "Select the Python locations where %s should be installed." - % self.distribution.get_fullname()) - - seldlg.back("< Back", None, active=0) - c = seldlg.next("Next >", "Cancel") - order = 1 - c.event("[TARGETDIR]", "[SourceDir]", ordering=order) - for version in self.versions + [self.other_version]: - order += 1 - c.event("[TARGETDIR]", "[TARGETDIR%s]" % version, - "FEATURE_SELECTED AND &Python%s=3" % version, - ordering=order) - c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1) - c.event("EndDialog", "Return", ordering=order + 2) - c = seldlg.cancel("Cancel", "Features") - c.event("SpawnDialog", "CancelDlg") - - c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3, - "FEATURE", None, "PathEdit", None) - c.event("[FEATURE_SELECTED]", "1") - ver = self.other_version - install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver - dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver - - c = seldlg.text("Other", 15, 200, 300, 15, 3, - "Provide an alternate Python location") - c.condition("Enable", install_other_cond) - c.condition("Show", install_other_cond) - c.condition("Disable", dont_install_other_cond) - c.condition("Hide", dont_install_other_cond) - - c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1, - "TARGETDIR" + ver, None, "Next", None) - c.condition("Enable", install_other_cond) - c.condition("Show", install_other_cond) - c.condition("Disable", dont_install_other_cond) - c.condition("Hide", dont_install_other_cond) - - ##################################################################### - # Disk cost - cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title, - "OK", "OK", "OK", bitmap=False) - cost.text("Title", 15, 6, 200, 15, 0x30003, - r"{\DlgFontBold8}Disk Space Requirements") - cost.text("Description", 20, 20, 280, 20, 0x30003, - "The disk space required for the installation of the selected features.") - cost.text("Text", 20, 53, 330, 60, 3, - "The highlighted volumes (if any) do not have enough disk space " - "available for the currently selected features. You can either " - "remove some files from the highlighted volumes, or choose to " - "install less features onto local drive(s), or select different " - "destination drive(s).") - cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223, - None, "{120}{70}{70}{70}{70}", None, None) - cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return") - - ##################################################################### - # WhichUsers Dialog. Only available on NT, and for privileged users. - # This must be run before FindRelatedProducts, because that will - # take into account whether the previous installation was per-user - # or per-machine. We currently don't support going back to this - # dialog after "Next" was selected; to support this, we would need to - # find how to reset the ALLUSERS property, and how to re-run - # FindRelatedProducts. - # On Windows9x, the ALLUSERS property is ignored on the command line - # and in the Property table, but installer fails according to the documentation - # if a dialog attempts to set ALLUSERS. - whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title, - "AdminInstall", "Next", "Cancel") - whichusers.title("Select whether to install [ProductName] for all users of this computer.") - # A radio group with two options: allusers, justme - g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3, - "WhichUsers", "", "Next") - g.add("ALL", 0, 5, 150, 20, "Install for all users") - g.add("JUSTME", 0, 25, 150, 20, "Install just for me") - - whichusers.back("Back", None, active=0) - - c = whichusers.next("Next >", "Cancel") - c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) - c.event("EndDialog", "Return", ordering = 2) - - c = whichusers.cancel("Cancel", "AdminInstall") - c.event("SpawnDialog", "CancelDlg") - - ##################################################################### - # Installation Progress dialog (modeless) - progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title, - "Cancel", "Cancel", "Cancel", bitmap=False) - progress.text("Title", 20, 15, 200, 15, 0x30003, - r"{\DlgFontBold8}[Progress1] [ProductName]") - progress.text("Text", 35, 65, 300, 30, 3, - "Please wait while the Installer [Progress2] [ProductName]. " - "This may take several minutes.") - progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:") - - c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...") - c.mapping("ActionText", "Text") - - #c=progress.text("ActionData", 35, 140, 300, 20, 3, None) - #c.mapping("ActionData", "Text") - - c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537, - None, "Progress done", None, None) - c.mapping("SetProgress", "Progress") - - progress.back("< Back", "Next", active=False) - progress.next("Next >", "Cancel", active=False) - progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg") - - ################################################################### - # Maintenance type: repair/uninstall - maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title, - "Next", "Next", "Cancel") - maint.title("Welcome to the [ProductName] Setup Wizard") - maint.text("BodyText", 15, 63, 330, 42, 3, - "Select whether you want to repair or remove [ProductName].") - g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3, - "MaintenanceForm_Action", "", "Next") - #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]") - g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]") - g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]") - - maint.back("< Back", None, active=False) - c=maint.next("Finish", "Cancel") - # Change installation: Change progress dialog to "Change", then ask - # for feature selection - #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1) - #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2) - - # Reinstall: Change progress dialog to "Repair", then invoke reinstall - # Also set list of reinstalled features to "ALL" - c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5) - c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6) - c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7) - c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8) - - # Uninstall: Change progress to "Remove", then invoke uninstall - # Also set list of removed features to "ALL" - c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11) - c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12) - c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13) - c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14) - - # Close dialog when maintenance action scheduled - c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20) - #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21) - - maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg") - - def get_installer_filename(self, fullname): - # Factored out to allow overriding in subclasses - if self.target_version: - base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, - self.target_version) - else: - base_name = "%s.%s.msi" % (fullname, self.plat_name) - installer_name = os.path.join(self.dist_dir, base_name) - return installer_name diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py deleted file mode 100644 index 550cbfa1..00000000 --- a/command/bdist_rpm.py +++ /dev/null @@ -1,579 +0,0 @@ -"""distutils.command.bdist_rpm - -Implements the Distutils 'bdist_rpm' command (create RPM source and binary -distributions).""" - -import subprocess, sys, os -from distutils.core import Command -from distutils.debug import DEBUG -from distutils.file_util import write_file -from distutils.errors import * -from distutils.sysconfig import get_python_version -from distutils import log - -class bdist_rpm(Command): - - description = "create an RPM distribution" - - user_options = [ - ('bdist-base=', None, - "base directory for creating built distributions"), - ('rpm-base=', None, - "base directory for creating RPMs (defaults to \"rpm\" under " - "--bdist-base; must be specified for RPM 2)"), - ('dist-dir=', 'd', - "directory to put final RPM files in " - "(and .spec files if --spec-only)"), - ('python=', None, - "path to Python interpreter to hard-code in the .spec file " - "(default: \"python\")"), - ('fix-python', None, - "hard-code the exact path to the current Python interpreter in " - "the .spec file"), - ('spec-only', None, - "only regenerate spec file"), - ('source-only', None, - "only generate source RPM"), - ('binary-only', None, - "only generate binary RPM"), - ('use-bzip2', None, - "use bzip2 instead of gzip to create source distribution"), - - # More meta-data: too RPM-specific to put in the setup script, - # but needs to go in the .spec file -- so we make these options - # to "bdist_rpm". The idea is that packagers would put this - # info in setup.cfg, although they are of course free to - # supply it on the command line. - ('distribution-name=', None, - "name of the (Linux) distribution to which this " - "RPM applies (*not* the name of the module distribution!)"), - ('group=', None, - "package classification [default: \"Development/Libraries\"]"), - ('release=', None, - "RPM release number"), - ('serial=', None, - "RPM serial number"), - ('vendor=', None, - "RPM \"vendor\" (eg. \"Joe Blow \") " - "[default: maintainer or author from setup script]"), - ('packager=', None, - "RPM packager (eg. \"Jane Doe \") " - "[default: vendor]"), - ('doc-files=', None, - "list of documentation files (space or comma-separated)"), - ('changelog=', None, - "RPM changelog"), - ('icon=', None, - "name of icon file"), - ('provides=', None, - "capabilities provided by this package"), - ('requires=', None, - "capabilities required by this package"), - ('conflicts=', None, - "capabilities which conflict with this package"), - ('build-requires=', None, - "capabilities required to build this package"), - ('obsoletes=', None, - "capabilities made obsolete by this package"), - ('no-autoreq', None, - "do not automatically calculate dependencies"), - - # Actions to take when building RPM - ('keep-temp', 'k', - "don't clean up RPM build directory"), - ('no-keep-temp', None, - "clean up RPM build directory [default]"), - ('use-rpm-opt-flags', None, - "compile with RPM_OPT_FLAGS when building from source RPM"), - ('no-rpm-opt-flags', None, - "do not pass any RPM CFLAGS to compiler"), - ('rpm3-mode', None, - "RPM 3 compatibility mode (default)"), - ('rpm2-mode', None, - "RPM 2 compatibility mode"), - - # Add the hooks necessary for specifying custom scripts - ('prep-script=', None, - "Specify a script for the PREP phase of RPM building"), - ('build-script=', None, - "Specify a script for the BUILD phase of RPM building"), - - ('pre-install=', None, - "Specify a script for the pre-INSTALL phase of RPM building"), - ('install-script=', None, - "Specify a script for the INSTALL phase of RPM building"), - ('post-install=', None, - "Specify a script for the post-INSTALL phase of RPM building"), - - ('pre-uninstall=', None, - "Specify a script for the pre-UNINSTALL phase of RPM building"), - ('post-uninstall=', None, - "Specify a script for the post-UNINSTALL phase of RPM building"), - - ('clean-script=', None, - "Specify a script for the CLEAN phase of RPM building"), - - ('verify-script=', None, - "Specify a script for the VERIFY phase of the RPM build"), - - # Allow a packager to explicitly force an architecture - ('force-arch=', None, - "Force an architecture onto the RPM build process"), - - ('quiet', 'q', - "Run the INSTALL phase of RPM building in quiet mode"), - ] - - boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', - 'no-autoreq', 'quiet'] - - negative_opt = {'no-keep-temp': 'keep-temp', - 'no-rpm-opt-flags': 'use-rpm-opt-flags', - 'rpm2-mode': 'rpm3-mode'} - - - def initialize_options(self): - self.bdist_base = None - self.rpm_base = None - self.dist_dir = None - self.python = None - self.fix_python = None - self.spec_only = None - self.binary_only = None - self.source_only = None - self.use_bzip2 = None - - self.distribution_name = None - self.group = None - self.release = None - self.serial = None - self.vendor = None - self.packager = None - self.doc_files = None - self.changelog = None - self.icon = None - - self.prep_script = None - self.build_script = None - self.install_script = None - self.clean_script = None - self.verify_script = None - self.pre_install = None - self.post_install = None - self.pre_uninstall = None - self.post_uninstall = None - self.prep = None - self.provides = None - self.requires = None - self.conflicts = None - self.build_requires = None - self.obsoletes = None - - self.keep_temp = 0 - self.use_rpm_opt_flags = 1 - self.rpm3_mode = 1 - self.no_autoreq = 0 - - self.force_arch = None - self.quiet = 0 - - def finalize_options(self): - self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) - if self.rpm_base is None: - if not self.rpm3_mode: - raise DistutilsOptionError( - "you must specify --rpm-base in RPM 2 mode") - self.rpm_base = os.path.join(self.bdist_base, "rpm") - - if self.python is None: - if self.fix_python: - self.python = sys.executable - else: - self.python = "python3" - elif self.fix_python: - raise DistutilsOptionError( - "--python and --fix-python are mutually exclusive options") - - if os.name != 'posix': - raise DistutilsPlatformError("don't know how to create RPM " - "distributions on platform %s" % os.name) - if self.binary_only and self.source_only: - raise DistutilsOptionError( - "cannot supply both '--source-only' and '--binary-only'") - - # don't pass CFLAGS to pure python distributions - if not self.distribution.has_ext_modules(): - self.use_rpm_opt_flags = 0 - - self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) - self.finalize_package_data() - - def finalize_package_data(self): - self.ensure_string('group', "Development/Libraries") - self.ensure_string('vendor', - "%s <%s>" % (self.distribution.get_contact(), - self.distribution.get_contact_email())) - self.ensure_string('packager') - self.ensure_string_list('doc_files') - if isinstance(self.doc_files, list): - for readme in ('README', 'README.txt'): - if os.path.exists(readme) and readme not in self.doc_files: - self.doc_files.append(readme) - - self.ensure_string('release', "1") - self.ensure_string('serial') # should it be an int? - - self.ensure_string('distribution_name') - - self.ensure_string('changelog') - # Format changelog correctly - self.changelog = self._format_changelog(self.changelog) - - self.ensure_filename('icon') - - self.ensure_filename('prep_script') - self.ensure_filename('build_script') - self.ensure_filename('install_script') - self.ensure_filename('clean_script') - self.ensure_filename('verify_script') - self.ensure_filename('pre_install') - self.ensure_filename('post_install') - self.ensure_filename('pre_uninstall') - self.ensure_filename('post_uninstall') - - # XXX don't forget we punted on summaries and descriptions -- they - # should be handled here eventually! - - # Now *this* is some meta-data that belongs in the setup script... - self.ensure_string_list('provides') - self.ensure_string_list('requires') - self.ensure_string_list('conflicts') - self.ensure_string_list('build_requires') - self.ensure_string_list('obsoletes') - - self.ensure_string('force_arch') - - def run(self): - if DEBUG: - print("before _get_package_data():") - print("vendor =", self.vendor) - print("packager =", self.packager) - print("doc_files =", self.doc_files) - print("changelog =", self.changelog) - - # make directories - if self.spec_only: - spec_dir = self.dist_dir - self.mkpath(spec_dir) - else: - rpm_dir = {} - for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): - rpm_dir[d] = os.path.join(self.rpm_base, d) - self.mkpath(rpm_dir[d]) - spec_dir = rpm_dir['SPECS'] - - # Spec file goes into 'dist_dir' if '--spec-only specified', - # build/rpm. otherwise. - spec_path = os.path.join(spec_dir, - "%s.spec" % self.distribution.get_name()) - self.execute(write_file, - (spec_path, - self._make_spec_file()), - "writing '%s'" % spec_path) - - if self.spec_only: # stop if requested - return - - # Make a source distribution and copy to SOURCES directory with - # optional icon. - saved_dist_files = self.distribution.dist_files[:] - sdist = self.reinitialize_command('sdist') - if self.use_bzip2: - sdist.formats = ['bztar'] - else: - sdist.formats = ['gztar'] - self.run_command('sdist') - self.distribution.dist_files = saved_dist_files - - source = sdist.get_archive_files()[0] - source_dir = rpm_dir['SOURCES'] - self.copy_file(source, source_dir) - - if self.icon: - if os.path.exists(self.icon): - self.copy_file(self.icon, source_dir) - else: - raise DistutilsFileError( - "icon file '%s' does not exist" % self.icon) - - # build package - log.info("building RPMs") - rpm_cmd = ['rpmbuild'] - - if self.source_only: # what kind of RPMs? - rpm_cmd.append('-bs') - elif self.binary_only: - rpm_cmd.append('-bb') - else: - rpm_cmd.append('-ba') - rpm_cmd.extend(['--define', '__python %s' % self.python]) - if self.rpm3_mode: - rpm_cmd.extend(['--define', - '_topdir %s' % os.path.abspath(self.rpm_base)]) - if not self.keep_temp: - rpm_cmd.append('--clean') - - if self.quiet: - rpm_cmd.append('--quiet') - - rpm_cmd.append(spec_path) - # Determine the binary rpm names that should be built out of this spec - # file - # Note that some of these may not be really built (if the file - # list is empty) - nvr_string = "%{name}-%{version}-%{release}" - src_rpm = nvr_string + ".src.rpm" - non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" - q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % ( - src_rpm, non_src_rpm, spec_path) - - out = os.popen(q_cmd) - try: - binary_rpms = [] - source_rpm = None - while True: - line = out.readline() - if not line: - break - l = line.strip().split() - assert(len(l) == 2) - binary_rpms.append(l[1]) - # The source rpm is named after the first entry in the spec file - if source_rpm is None: - source_rpm = l[0] - - status = out.close() - if status: - raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) - - finally: - out.close() - - self.spawn(rpm_cmd) - - if not self.dry_run: - if self.distribution.has_ext_modules(): - pyversion = get_python_version() - else: - pyversion = 'any' - - if not self.binary_only: - srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) - assert(os.path.exists(srpm)) - self.move_file(srpm, self.dist_dir) - filename = os.path.join(self.dist_dir, source_rpm) - self.distribution.dist_files.append( - ('bdist_rpm', pyversion, filename)) - - if not self.source_only: - for rpm in binary_rpms: - rpm = os.path.join(rpm_dir['RPMS'], rpm) - if os.path.exists(rpm): - self.move_file(rpm, self.dist_dir) - filename = os.path.join(self.dist_dir, - os.path.basename(rpm)) - self.distribution.dist_files.append( - ('bdist_rpm', pyversion, filename)) - - def _dist_path(self, path): - return os.path.join(self.dist_dir, os.path.basename(path)) - - def _make_spec_file(self): - """Generate the text of an RPM spec file and return it as a - list of strings (one per line). - """ - # definitions and headers - spec_file = [ - '%define name ' + self.distribution.get_name(), - '%define version ' + self.distribution.get_version().replace('-','_'), - '%define unmangled_version ' + self.distribution.get_version(), - '%define release ' + self.release.replace('-','_'), - '', - 'Summary: ' + self.distribution.get_description(), - ] - - # Workaround for #14443 which affects some RPM based systems such as - # RHEL6 (and probably derivatives) - vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}') - # Generate a potential replacement value for __os_install_post (whilst - # normalizing the whitespace to simplify the test for whether the - # invocation of brp-python-bytecompile passes in __python): - vendor_hook = '\n'.join([' %s \\' % line.strip() - for line in vendor_hook.splitlines()]) - problem = "brp-python-bytecompile \\\n" - fixed = "brp-python-bytecompile %{__python} \\\n" - fixed_hook = vendor_hook.replace(problem, fixed) - if fixed_hook != vendor_hook: - spec_file.append('# Workaround for http://bugs.python.org/issue14443') - spec_file.append('%define __os_install_post ' + fixed_hook + '\n') - - # put locale summaries into spec file - # XXX not supported for now (hard to put a dictionary - # in a config file -- arg!) - #for locale in self.summaries.keys(): - # spec_file.append('Summary(%s): %s' % (locale, - # self.summaries[locale])) - - spec_file.extend([ - 'Name: %{name}', - 'Version: %{version}', - 'Release: %{release}',]) - - # XXX yuck! this filename is available from the "sdist" command, - # but only after it has run: and we create the spec file before - # running "sdist", in case of --spec-only. - if self.use_bzip2: - spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') - else: - spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') - - spec_file.extend([ - 'License: ' + self.distribution.get_license(), - 'Group: ' + self.group, - 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', - 'Prefix: %{_prefix}', ]) - - if not self.force_arch: - # noarch if no extension modules - if not self.distribution.has_ext_modules(): - spec_file.append('BuildArch: noarch') - else: - spec_file.append( 'BuildArch: %s' % self.force_arch ) - - for field in ('Vendor', - 'Packager', - 'Provides', - 'Requires', - 'Conflicts', - 'Obsoletes', - ): - val = getattr(self, field.lower()) - if isinstance(val, list): - spec_file.append('%s: %s' % (field, ' '.join(val))) - elif val is not None: - spec_file.append('%s: %s' % (field, val)) - - - if self.distribution.get_url() != 'UNKNOWN': - spec_file.append('Url: ' + self.distribution.get_url()) - - if self.distribution_name: - spec_file.append('Distribution: ' + self.distribution_name) - - if self.build_requires: - spec_file.append('BuildRequires: ' + - ' '.join(self.build_requires)) - - if self.icon: - spec_file.append('Icon: ' + os.path.basename(self.icon)) - - if self.no_autoreq: - spec_file.append('AutoReq: 0') - - spec_file.extend([ - '', - '%description', - self.distribution.get_long_description() - ]) - - # put locale descriptions into spec file - # XXX again, suppressed because config file syntax doesn't - # easily support this ;-( - #for locale in self.descriptions.keys(): - # spec_file.extend([ - # '', - # '%description -l ' + locale, - # self.descriptions[locale], - # ]) - - # rpm scripts - # figure out default build script - def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0])) - def_build = "%s build" % def_setup_call - if self.use_rpm_opt_flags: - def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build - - # insert contents of files - - # XXX this is kind of misleading: user-supplied options are files - # that we open and interpolate into the spec file, but the defaults - # are just text that we drop in as-is. Hmmm. - - install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT ' - '--record=INSTALLED_FILES') % def_setup_call - - script_options = [ - ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), - ('build', 'build_script', def_build), - ('install', 'install_script', install_cmd), - ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), - ('verifyscript', 'verify_script', None), - ('pre', 'pre_install', None), - ('post', 'post_install', None), - ('preun', 'pre_uninstall', None), - ('postun', 'post_uninstall', None), - ] - - for (rpm_opt, attr, default) in script_options: - # Insert contents of file referred to, if no file is referred to - # use 'default' as contents of script - val = getattr(self, attr) - if val or default: - spec_file.extend([ - '', - '%' + rpm_opt,]) - if val: - with open(val) as f: - spec_file.extend(f.read().split('\n')) - else: - spec_file.append(default) - - - # files section - spec_file.extend([ - '', - '%files -f INSTALLED_FILES', - '%defattr(-,root,root)', - ]) - - if self.doc_files: - spec_file.append('%doc ' + ' '.join(self.doc_files)) - - if self.changelog: - spec_file.extend([ - '', - '%changelog',]) - spec_file.extend(self.changelog) - - return spec_file - - def _format_changelog(self, changelog): - """Format the changelog correctly and convert it to a list of strings - """ - if not changelog: - return changelog - new_changelog = [] - for line in changelog.strip().split('\n'): - line = line.strip() - if line[0] == '*': - new_changelog.extend(['', line]) - elif line[0] == '-': - new_changelog.append(line) - else: - new_changelog.append(' ' + line) - - # strip trailing newline inserted by first changelog entry - if not new_changelog[0]: - del new_changelog[0] - - return new_changelog diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py deleted file mode 100644 index 0e9ddaa2..00000000 --- a/command/bdist_wininst.py +++ /dev/null @@ -1,377 +0,0 @@ -"""distutils.command.bdist_wininst - -Implements the Distutils 'bdist_wininst' command: create a windows installer -exe-program.""" - -import os -import sys -import warnings -from distutils.core import Command -from distutils.util import get_platform -from distutils.dir_util import remove_tree -from distutils.errors import * -from distutils.sysconfig import get_python_version -from distutils import log - -class bdist_wininst(Command): - - description = "create an executable installer for MS Windows" - - user_options = [('bdist-dir=', None, - "temporary directory for creating the distribution"), - ('plat-name=', 'p', - "platform name to embed in generated filenames " - "(default: %s)" % get_platform()), - ('keep-temp', 'k', - "keep the pseudo-installation tree around after " + - "creating the distribution archive"), - ('target-version=', None, - "require a specific python version" + - " on the target system"), - ('no-target-compile', 'c', - "do not compile .py to .pyc on the target system"), - ('no-target-optimize', 'o', - "do not compile .py to .pyo (optimized) " - "on the target system"), - ('dist-dir=', 'd', - "directory to put final built distributions in"), - ('bitmap=', 'b', - "bitmap to use for the installer instead of python-powered logo"), - ('title=', 't', - "title to display on the installer background instead of default"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ('install-script=', None, - "basename of installation script to be run after " - "installation or before deinstallation"), - ('pre-install-script=', None, - "Fully qualified filename of a script to be run before " - "any files are installed. This script need not be in the " - "distribution"), - ('user-access-control=', None, - "specify Vista's UAC handling - 'none'/default=no " - "handling, 'auto'=use UAC if target Python installed for " - "all users, 'force'=always use UAC"), - ] - - boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', - 'skip-build'] - - # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows - _unsupported = (sys.platform != "win32") - - def __init__(self, *args, **kw): - super().__init__(*args, **kw) - warnings.warn("bdist_wininst command is deprecated since Python 3.8, " - "use bdist_wheel (wheel packages) instead", - DeprecationWarning, 2) - - def initialize_options(self): - self.bdist_dir = None - self.plat_name = None - self.keep_temp = 0 - self.no_target_compile = 0 - self.no_target_optimize = 0 - self.target_version = None - self.dist_dir = None - self.bitmap = None - self.title = None - self.skip_build = None - self.install_script = None - self.pre_install_script = None - self.user_access_control = None - - - def finalize_options(self): - self.set_undefined_options('bdist', ('skip_build', 'skip_build')) - - if self.bdist_dir is None: - if self.skip_build and self.plat_name: - # If build is skipped and plat_name is overridden, bdist will - # not see the correct 'plat_name' - so set that up manually. - bdist = self.distribution.get_command_obj('bdist') - bdist.plat_name = self.plat_name - # next the command will be initialized using that name - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'wininst') - - if not self.target_version: - self.target_version = "" - - if not self.skip_build and self.distribution.has_ext_modules(): - short_version = get_python_version() - if self.target_version and self.target_version != short_version: - raise DistutilsOptionError( - "target version can only be %s, or the '--skip-build'" \ - " option must be specified" % (short_version,)) - self.target_version = short_version - - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ) - - if self.install_script: - for script in self.distribution.scripts: - if self.install_script == os.path.basename(script): - break - else: - raise DistutilsOptionError( - "install_script '%s' not found in scripts" - % self.install_script) - - def run(self): - if (sys.platform != "win32" and - (self.distribution.has_ext_modules() or - self.distribution.has_c_libraries())): - raise DistutilsPlatformError \ - ("distribution contains extensions and/or C libraries; " - "must be compiled on a Windows 32 platform") - - if not self.skip_build: - self.run_command('build') - - install = self.reinitialize_command('install', reinit_subcommands=1) - install.root = self.bdist_dir - install.skip_build = self.skip_build - install.warn_dir = 0 - install.plat_name = self.plat_name - - install_lib = self.reinitialize_command('install_lib') - # we do not want to include pyc or pyo files - install_lib.compile = 0 - install_lib.optimize = 0 - - if self.distribution.has_ext_modules(): - # If we are building an installer for a Python version other - # than the one we are currently running, then we need to ensure - # our build_lib reflects the other Python version rather than ours. - # Note that for target_version!=sys.version, we must have skipped the - # build step, so there is no issue with enforcing the build of this - # version. - target_version = self.target_version - if not target_version: - assert self.skip_build, "Should have already checked this" - target_version = '%d.%d' % sys.version_info[:2] - plat_specifier = ".%s-%s" % (self.plat_name, target_version) - build = self.get_finalized_command('build') - build.build_lib = os.path.join(build.build_base, - 'lib' + plat_specifier) - - # Use a custom scheme for the zip-file, because we have to decide - # at installation time which scheme to use. - for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): - value = key.upper() - if key == 'headers': - value = value + '/Include/$dist_name' - setattr(install, - 'install_' + key, - value) - - log.info("installing to %s", self.bdist_dir) - install.ensure_finalized() - - # avoid warning of 'install_lib' about installing - # into a directory not in sys.path - sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) - - install.run() - - del sys.path[0] - - # And make an archive relative to the root of the - # pseudo-installation tree. - from tempfile import mktemp - archive_basename = mktemp() - fullname = self.distribution.get_fullname() - arcname = self.make_archive(archive_basename, "zip", - root_dir=self.bdist_dir) - # create an exe containing the zip-file - self.create_exe(arcname, fullname, self.bitmap) - if self.distribution.has_ext_modules(): - pyversion = get_python_version() - else: - pyversion = 'any' - self.distribution.dist_files.append(('bdist_wininst', pyversion, - self.get_installer_filename(fullname))) - # remove the zip-file again - log.debug("removing temporary file '%s'", arcname) - os.remove(arcname) - - if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) - - def get_inidata(self): - # Return data describing the installation. - lines = [] - metadata = self.distribution.metadata - - # Write the [metadata] section. - lines.append("[metadata]") - - # 'info' will be displayed in the installer's dialog box, - # describing the items to be installed. - info = (metadata.long_description or '') + '\n' - - # Escape newline characters - def escape(s): - return s.replace("\n", "\\n") - - for name in ["author", "author_email", "description", "maintainer", - "maintainer_email", "name", "url", "version"]: - data = getattr(metadata, name, "") - if data: - info = info + ("\n %s: %s" % \ - (name.capitalize(), escape(data))) - lines.append("%s=%s" % (name, escape(data))) - - # The [setup] section contains entries controlling - # the installer runtime. - lines.append("\n[Setup]") - if self.install_script: - lines.append("install_script=%s" % self.install_script) - lines.append("info=%s" % escape(info)) - lines.append("target_compile=%d" % (not self.no_target_compile)) - lines.append("target_optimize=%d" % (not self.no_target_optimize)) - if self.target_version: - lines.append("target_version=%s" % self.target_version) - if self.user_access_control: - lines.append("user_access_control=%s" % self.user_access_control) - - title = self.title or self.distribution.get_fullname() - lines.append("title=%s" % escape(title)) - import time - import distutils - build_info = "Built %s with distutils-%s" % \ - (time.ctime(time.time()), distutils.__version__) - lines.append("build_info=%s" % build_info) - return "\n".join(lines) - - def create_exe(self, arcname, fullname, bitmap=None): - import struct - - self.mkpath(self.dist_dir) - - cfgdata = self.get_inidata() - - installer_name = self.get_installer_filename(fullname) - self.announce("creating %s" % installer_name) - - if bitmap: - with open(bitmap, "rb") as f: - bitmapdata = f.read() - bitmaplen = len(bitmapdata) - else: - bitmaplen = 0 - - with open(installer_name, "wb") as file: - file.write(self.get_exe_bytes()) - if bitmap: - file.write(bitmapdata) - - # Convert cfgdata from unicode to ascii, mbcs encoded - if isinstance(cfgdata, str): - cfgdata = cfgdata.encode("mbcs") - - # Append the pre-install script - cfgdata = cfgdata + b"\0" - if self.pre_install_script: - # We need to normalize newlines, so we open in text mode and - # convert back to bytes. "latin-1" simply avoids any possible - # failures. - with open(self.pre_install_script, "r", - encoding="latin-1") as script: - script_data = script.read().encode("latin-1") - cfgdata = cfgdata + script_data + b"\n\0" - else: - # empty pre-install script - cfgdata = cfgdata + b"\0" - file.write(cfgdata) - - # The 'magic number' 0x1234567B is used to make sure that the - # binary layout of 'cfgdata' is what the wininst.exe binary - # expects. If the layout changes, increment that number, make - # the corresponding changes to the wininst.exe sources, and - # recompile them. - header = struct.pack("' under the base build directory. We only use one of - # them for a given distribution, though -- - if self.build_purelib is None: - self.build_purelib = os.path.join(self.build_base, 'lib') - if self.build_platlib is None: - self.build_platlib = os.path.join(self.build_base, - 'lib' + plat_specifier) - - # 'build_lib' is the actual directory that we will use for this - # particular module distribution -- if user didn't supply it, pick - # one of 'build_purelib' or 'build_platlib'. - if self.build_lib is None: - if self.distribution.ext_modules: - self.build_lib = self.build_platlib - else: - self.build_lib = self.build_purelib - - # 'build_temp' -- temporary directory for compiler turds, - # "build/temp." - if self.build_temp is None: - self.build_temp = os.path.join(self.build_base, - 'temp' + plat_specifier) - if self.build_scripts is None: - self.build_scripts = os.path.join(self.build_base, - 'scripts-%d.%d' % sys.version_info[:2]) - - if self.executable is None and sys.executable: - self.executable = os.path.normpath(sys.executable) - - if isinstance(self.parallel, str): - try: - self.parallel = int(self.parallel) - except ValueError: - raise DistutilsOptionError("parallel should be an integer") - - def run(self): - # Run all relevant sub-commands. This will be some subset of: - # - build_py - pure Python modules - # - build_clib - standalone C libraries - # - build_ext - Python extensions - # - build_scripts - (Python) scripts - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) - - - # -- Predicates for the sub-command list --------------------------- - - def has_pure_modules(self): - return self.distribution.has_pure_modules() - - def has_c_libraries(self): - return self.distribution.has_c_libraries() - - def has_ext_modules(self): - return self.distribution.has_ext_modules() - - def has_scripts(self): - return self.distribution.has_scripts() - - - sub_commands = [('build_py', has_pure_modules), - ('build_clib', has_c_libraries), - ('build_ext', has_ext_modules), - ('build_scripts', has_scripts), - ] diff --git a/command/build_clib.py b/command/build_clib.py deleted file mode 100644 index 3e20ef23..00000000 --- a/command/build_clib.py +++ /dev/null @@ -1,209 +0,0 @@ -"""distutils.command.build_clib - -Implements the Distutils 'build_clib' command, to build a C/C++ library -that is included in the module distribution and needed by an extension -module.""" - - -# XXX this module has *lots* of code ripped-off quite transparently from -# build_ext.py -- not surprisingly really, as the work required to build -# a static library from a collection of C source files is not really all -# that different from what's required to build a shared object file from -# a collection of C source files. Nevertheless, I haven't done the -# necessary refactoring to account for the overlap in code between the -# two modules, mainly because a number of subtle details changed in the -# cut 'n paste. Sigh. - -import os -from distutils.core import Command -from distutils.errors import * -from distutils.sysconfig import customize_compiler -from distutils import log - -def show_compilers(): - from distutils.ccompiler import show_compilers - show_compilers() - - -class build_clib(Command): - - description = "build C/C++ libraries used by Python extensions" - - user_options = [ - ('build-clib=', 'b', - "directory to build C/C++ libraries to"), - ('build-temp=', 't', - "directory to put temporary build by-products"), - ('debug', 'g', - "compile with debugging information"), - ('force', 'f', - "forcibly build everything (ignore file timestamps)"), - ('compiler=', 'c', - "specify the compiler type"), - ] - - boolean_options = ['debug', 'force'] - - help_options = [ - ('help-compiler', None, - "list available compilers", show_compilers), - ] - - def initialize_options(self): - self.build_clib = None - self.build_temp = None - - # List of libraries to build - self.libraries = None - - # Compilation options for all libraries - self.include_dirs = None - self.define = None - self.undef = None - self.debug = None - self.force = 0 - self.compiler = None - - - def finalize_options(self): - # This might be confusing: both build-clib and build-temp default - # to build-temp as defined by the "build" command. This is because - # I think that C libraries are really just temporary build - # by-products, at least from the point of view of building Python - # extensions -- but I want to keep my options open. - self.set_undefined_options('build', - ('build_temp', 'build_clib'), - ('build_temp', 'build_temp'), - ('compiler', 'compiler'), - ('debug', 'debug'), - ('force', 'force')) - - self.libraries = self.distribution.libraries - if self.libraries: - self.check_library_list(self.libraries) - - if self.include_dirs is None: - self.include_dirs = self.distribution.include_dirs or [] - if isinstance(self.include_dirs, str): - self.include_dirs = self.include_dirs.split(os.pathsep) - - # XXX same as for build_ext -- what about 'self.define' and - # 'self.undef' ? - - - def run(self): - if not self.libraries: - return - - # Yech -- this is cut 'n pasted from build_ext.py! - from distutils.ccompiler import new_compiler - self.compiler = new_compiler(compiler=self.compiler, - dry_run=self.dry_run, - force=self.force) - customize_compiler(self.compiler) - - if self.include_dirs is not None: - self.compiler.set_include_dirs(self.include_dirs) - if self.define is not None: - # 'define' option is a list of (name,value) tuples - for (name,value) in self.define: - self.compiler.define_macro(name, value) - if self.undef is not None: - for macro in self.undef: - self.compiler.undefine_macro(macro) - - self.build_libraries(self.libraries) - - - def check_library_list(self, libraries): - """Ensure that the list of libraries is valid. - - `library` is presumably provided as a command option 'libraries'. - This method checks that it is a list of 2-tuples, where the tuples - are (library_name, build_info_dict). - - Raise DistutilsSetupError if the structure is invalid anywhere; - just returns otherwise. - """ - if not isinstance(libraries, list): - raise DistutilsSetupError( - "'libraries' option must be a list of tuples") - - for lib in libraries: - if not isinstance(lib, tuple) and len(lib) != 2: - raise DistutilsSetupError( - "each element of 'libraries' must a 2-tuple") - - name, build_info = lib - - if not isinstance(name, str): - raise DistutilsSetupError( - "first element of each tuple in 'libraries' " - "must be a string (the library name)") - - if '/' in name or (os.sep != '/' and os.sep in name): - raise DistutilsSetupError("bad library name '%s': " - "may not contain directory separators" % lib[0]) - - if not isinstance(build_info, dict): - raise DistutilsSetupError( - "second element of each tuple in 'libraries' " - "must be a dictionary (build info)") - - - def get_library_names(self): - # Assume the library list is valid -- 'check_library_list()' is - # called from 'finalize_options()', so it should be! - if not self.libraries: - return None - - lib_names = [] - for (lib_name, build_info) in self.libraries: - lib_names.append(lib_name) - return lib_names - - - def get_source_files(self): - self.check_library_list(self.libraries) - filenames = [] - for (lib_name, build_info) in self.libraries: - sources = build_info.get('sources') - if sources is None or not isinstance(sources, (list, tuple)): - raise DistutilsSetupError( - "in 'libraries' option (library '%s'), " - "'sources' must be present and must be " - "a list of source filenames" % lib_name) - - filenames.extend(sources) - return filenames - - - def build_libraries(self, libraries): - for (lib_name, build_info) in libraries: - sources = build_info.get('sources') - if sources is None or not isinstance(sources, (list, tuple)): - raise DistutilsSetupError( - "in 'libraries' option (library '%s'), " - "'sources' must be present and must be " - "a list of source filenames" % lib_name) - sources = list(sources) - - log.info("building '%s' library", lib_name) - - # First, compile the source code to object files in the library - # directory. (This should probably change to putting object - # files in a temporary build directory.) - macros = build_info.get('macros') - include_dirs = build_info.get('include_dirs') - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=include_dirs, - debug=self.debug) - - # Now "link" the object files together into a static library. - # (On Unix at least, this isn't really linking -- it just - # builds an archive. Whatever.) - self.compiler.create_static_lib(objects, lib_name, - output_dir=self.build_clib, - debug=self.debug) diff --git a/command/build_ext.py b/command/build_ext.py deleted file mode 100644 index 1a9bd120..00000000 --- a/command/build_ext.py +++ /dev/null @@ -1,754 +0,0 @@ -"""distutils.command.build_ext - -Implements the Distutils 'build_ext' command, for building extension -modules (currently limited to C extensions, should accommodate C++ -extensions ASAP).""" - -import contextlib -import os -import re -import sys -from distutils.core import Command -from distutils.errors import * -from distutils.sysconfig import customize_compiler, get_python_version -from distutils.sysconfig import get_config_h_filename -from distutils.dep_util import newer_group -from distutils.extension import Extension -from distutils.util import get_platform -from distutils import log - -from site import USER_BASE - -# An extension name is just a dot-separated list of Python NAMEs (ie. -# the same as a fully-qualified module name). -extension_name_re = re.compile \ - (r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$') - - -def show_compilers (): - from distutils.ccompiler import show_compilers - show_compilers() - - -class build_ext(Command): - - description = "build C/C++ extensions (compile/link to build directory)" - - # XXX thoughts on how to deal with complex command-line options like - # these, i.e. how to make it so fancy_getopt can suck them off the - # command line and make it look like setup.py defined the appropriate - # lists of tuples of what-have-you. - # - each command needs a callback to process its command-line options - # - Command.__init__() needs access to its share of the whole - # command line (must ultimately come from - # Distribution.parse_command_line()) - # - it then calls the current command class' option-parsing - # callback to deal with weird options like -D, which have to - # parse the option text and churn out some custom data - # structure - # - that data structure (in this case, a list of 2-tuples) - # will then be present in the command object by the time - # we get to finalize_options() (i.e. the constructor - # takes care of both command-line and client options - # in between initialize_options() and finalize_options()) - - sep_by = " (separated by '%s')" % os.pathsep - user_options = [ - ('build-lib=', 'b', - "directory for compiled extension modules"), - ('build-temp=', 't', - "directory for temporary files (build by-products)"), - ('plat-name=', 'p', - "platform name to cross-compile for, if supported " - "(default: %s)" % get_platform()), - ('inplace', 'i', - "ignore build-lib and put compiled extensions into the source " + - "directory alongside your pure Python modules"), - ('include-dirs=', 'I', - "list of directories to search for header files" + sep_by), - ('define=', 'D', - "C preprocessor macros to define"), - ('undef=', 'U', - "C preprocessor macros to undefine"), - ('libraries=', 'l', - "external C libraries to link with"), - ('library-dirs=', 'L', - "directories to search for external C libraries" + sep_by), - ('rpath=', 'R', - "directories to search for shared C libraries at runtime"), - ('link-objects=', 'O', - "extra explicit link objects to include in the link"), - ('debug', 'g', - "compile/link with debugging information"), - ('force', 'f', - "forcibly build everything (ignore file timestamps)"), - ('compiler=', 'c', - "specify the compiler type"), - ('parallel=', 'j', - "number of parallel build jobs"), - ('swig-cpp', None, - "make SWIG create C++ files (default is C)"), - ('swig-opts=', None, - "list of SWIG command line options"), - ('swig=', None, - "path to the SWIG executable"), - ('user', None, - "add user include, library and rpath") - ] - - boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] - - help_options = [ - ('help-compiler', None, - "list available compilers", show_compilers), - ] - - def initialize_options(self): - self.extensions = None - self.build_lib = None - self.plat_name = None - self.build_temp = None - self.inplace = 0 - self.package = None - - self.include_dirs = None - self.define = None - self.undef = None - self.libraries = None - self.library_dirs = None - self.rpath = None - self.link_objects = None - self.debug = None - self.force = None - self.compiler = None - self.swig = None - self.swig_cpp = None - self.swig_opts = None - self.user = None - self.parallel = None - - def finalize_options(self): - from distutils import sysconfig - - self.set_undefined_options('build', - ('build_lib', 'build_lib'), - ('build_temp', 'build_temp'), - ('compiler', 'compiler'), - ('debug', 'debug'), - ('force', 'force'), - ('parallel', 'parallel'), - ('plat_name', 'plat_name'), - ) - - if self.package is None: - self.package = self.distribution.ext_package - - self.extensions = self.distribution.ext_modules - - # Make sure Python's include directories (for Python.h, pyconfig.h, - # etc.) are in the include search path. - py_include = sysconfig.get_python_inc() - plat_py_include = sysconfig.get_python_inc(plat_specific=1) - if self.include_dirs is None: - self.include_dirs = self.distribution.include_dirs or [] - if isinstance(self.include_dirs, str): - self.include_dirs = self.include_dirs.split(os.pathsep) - - # If in a virtualenv, add its include directory - # Issue 16116 - if sys.exec_prefix != sys.base_exec_prefix: - self.include_dirs.append(os.path.join(sys.exec_prefix, 'include')) - - # Put the Python "system" include dir at the end, so that - # any local include dirs take precedence. - self.include_dirs.extend(py_include.split(os.path.pathsep)) - if plat_py_include != py_include: - self.include_dirs.extend( - plat_py_include.split(os.path.pathsep)) - - self.ensure_string_list('libraries') - self.ensure_string_list('link_objects') - - # Life is easier if we're not forever checking for None, so - # simplify these options to empty lists if unset - if self.libraries is None: - self.libraries = [] - if self.library_dirs is None: - self.library_dirs = [] - elif isinstance(self.library_dirs, str): - self.library_dirs = self.library_dirs.split(os.pathsep) - - if self.rpath is None: - self.rpath = [] - elif isinstance(self.rpath, str): - self.rpath = self.rpath.split(os.pathsep) - - # for extensions under windows use different directories - # for Release and Debug builds. - # also Python's library directory must be appended to library_dirs - if os.name == 'nt': - # the 'libs' directory is for binary installs - we assume that - # must be the *native* platform. But we don't really support - # cross-compiling via a binary install anyway, so we let it go. - self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) - if sys.base_exec_prefix != sys.prefix: # Issue 16116 - self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs')) - if self.debug: - self.build_temp = os.path.join(self.build_temp, "Debug") - else: - self.build_temp = os.path.join(self.build_temp, "Release") - - # Append the source distribution include and library directories, - # this allows distutils on windows to work in the source tree - self.include_dirs.append(os.path.dirname(get_config_h_filename())) - _sys_home = getattr(sys, '_home', None) - if _sys_home: - self.library_dirs.append(_sys_home) - - # Use the .lib files for the correct architecture - if self.plat_name == 'win32': - suffix = 'win32' - else: - # win-amd64 - suffix = self.plat_name[4:] - new_lib = os.path.join(sys.exec_prefix, 'PCbuild') - if suffix: - new_lib = os.path.join(new_lib, suffix) - self.library_dirs.append(new_lib) - - # For extensions under Cygwin, Python's library directory must be - # appended to library_dirs - if sys.platform[:6] == 'cygwin': - if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): - # building third party extensions - self.library_dirs.append(os.path.join(sys.prefix, "lib", - "python" + get_python_version(), - "config")) - else: - # building python standard extensions - self.library_dirs.append('.') - - # For building extensions with a shared Python library, - # Python's library directory must be appended to library_dirs - # See Issues: #1600860, #4366 - if (sysconfig.get_config_var('Py_ENABLE_SHARED')): - if not sysconfig.python_build: - # building third party extensions - self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) - else: - # building python standard extensions - self.library_dirs.append('.') - - # The argument parsing will result in self.define being a string, but - # it has to be a list of 2-tuples. All the preprocessor symbols - # specified by the 'define' option will be set to '1'. Multiple - # symbols can be separated with commas. - - if self.define: - defines = self.define.split(',') - self.define = [(symbol, '1') for symbol in defines] - - # The option for macros to undefine is also a string from the - # option parsing, but has to be a list. Multiple symbols can also - # be separated with commas here. - if self.undef: - self.undef = self.undef.split(',') - - if self.swig_opts is None: - self.swig_opts = [] - else: - self.swig_opts = self.swig_opts.split(' ') - - # Finally add the user include and library directories if requested - if self.user: - user_include = os.path.join(USER_BASE, "include") - user_lib = os.path.join(USER_BASE, "lib") - if os.path.isdir(user_include): - self.include_dirs.append(user_include) - if os.path.isdir(user_lib): - self.library_dirs.append(user_lib) - self.rpath.append(user_lib) - - if isinstance(self.parallel, str): - try: - self.parallel = int(self.parallel) - except ValueError: - raise DistutilsOptionError("parallel should be an integer") - - def run(self): - from distutils.ccompiler import new_compiler - - # 'self.extensions', as supplied by setup.py, is a list of - # Extension instances. See the documentation for Extension (in - # distutils.extension) for details. - # - # For backwards compatibility with Distutils 0.8.2 and earlier, we - # also allow the 'extensions' list to be a list of tuples: - # (ext_name, build_info) - # where build_info is a dictionary containing everything that - # Extension instances do except the name, with a few things being - # differently named. We convert these 2-tuples to Extension - # instances as needed. - - if not self.extensions: - return - - # If we were asked to build any C/C++ libraries, make sure that the - # directory where we put them is in the library search path for - # linking extensions. - if self.distribution.has_c_libraries(): - build_clib = self.get_finalized_command('build_clib') - self.libraries.extend(build_clib.get_library_names() or []) - self.library_dirs.append(build_clib.build_clib) - - # Setup the CCompiler object that we'll use to do all the - # compiling and linking - self.compiler = new_compiler(compiler=self.compiler, - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force) - customize_compiler(self.compiler) - # If we are cross-compiling, init the compiler now (if we are not - # cross-compiling, init would not hurt, but people may rely on - # late initialization of compiler even if they shouldn't...) - if os.name == 'nt' and self.plat_name != get_platform(): - self.compiler.initialize(self.plat_name) - - # And make sure that any compile/link-related options (which might - # come from the command-line or from the setup script) are set in - # that CCompiler object -- that way, they automatically apply to - # all compiling and linking done here. - if self.include_dirs is not None: - self.compiler.set_include_dirs(self.include_dirs) - if self.define is not None: - # 'define' option is a list of (name,value) tuples - for (name, value) in self.define: - self.compiler.define_macro(name, value) - if self.undef is not None: - for macro in self.undef: - self.compiler.undefine_macro(macro) - if self.libraries is not None: - self.compiler.set_libraries(self.libraries) - if self.library_dirs is not None: - self.compiler.set_library_dirs(self.library_dirs) - if self.rpath is not None: - self.compiler.set_runtime_library_dirs(self.rpath) - if self.link_objects is not None: - self.compiler.set_link_objects(self.link_objects) - - # Now actually compile and link everything. - self.build_extensions() - - def check_extensions_list(self, extensions): - """Ensure that the list of extensions (presumably provided as a - command option 'extensions') is valid, i.e. it is a list of - Extension objects. We also support the old-style list of 2-tuples, - where the tuples are (ext_name, build_info), which are converted to - Extension instances here. - - Raise DistutilsSetupError if the structure is invalid anywhere; - just returns otherwise. - """ - if not isinstance(extensions, list): - raise DistutilsSetupError( - "'ext_modules' option must be a list of Extension instances") - - for i, ext in enumerate(extensions): - if isinstance(ext, Extension): - continue # OK! (assume type-checking done - # by Extension constructor) - - if not isinstance(ext, tuple) or len(ext) != 2: - raise DistutilsSetupError( - "each element of 'ext_modules' option must be an " - "Extension instance or 2-tuple") - - ext_name, build_info = ext - - log.warn("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s' " - "-- please convert to Extension instance", ext_name) - - if not (isinstance(ext_name, str) and - extension_name_re.match(ext_name)): - raise DistutilsSetupError( - "first element of each tuple in 'ext_modules' " - "must be the extension name (a string)") - - if not isinstance(build_info, dict): - raise DistutilsSetupError( - "second element of each tuple in 'ext_modules' " - "must be a dictionary (build info)") - - # OK, the (ext_name, build_info) dict is type-safe: convert it - # to an Extension instance. - ext = Extension(ext_name, build_info['sources']) - - # Easy stuff: one-to-one mapping from dict elements to - # instance attributes. - for key in ('include_dirs', 'library_dirs', 'libraries', - 'extra_objects', 'extra_compile_args', - 'extra_link_args'): - val = build_info.get(key) - if val is not None: - setattr(ext, key, val) - - # Medium-easy stuff: same syntax/semantics, different names. - ext.runtime_library_dirs = build_info.get('rpath') - if 'def_file' in build_info: - log.warn("'def_file' element of build info dict " - "no longer supported") - - # Non-trivial stuff: 'macros' split into 'define_macros' - # and 'undef_macros'. - macros = build_info.get('macros') - if macros: - ext.define_macros = [] - ext.undef_macros = [] - for macro in macros: - if not (isinstance(macro, tuple) and len(macro) in (1, 2)): - raise DistutilsSetupError( - "'macros' element of build info dict " - "must be 1- or 2-tuple") - if len(macro) == 1: - ext.undef_macros.append(macro[0]) - elif len(macro) == 2: - ext.define_macros.append(macro) - - extensions[i] = ext - - def get_source_files(self): - self.check_extensions_list(self.extensions) - filenames = [] - - # Wouldn't it be neat if we knew the names of header files too... - for ext in self.extensions: - filenames.extend(ext.sources) - return filenames - - def get_outputs(self): - # Sanity check the 'extensions' list -- can't assume this is being - # done in the same run as a 'build_extensions()' call (in fact, we - # can probably assume that it *isn't*!). - self.check_extensions_list(self.extensions) - - # And build the list of output (built) filenames. Note that this - # ignores the 'inplace' flag, and assumes everything goes in the - # "build" tree. - outputs = [] - for ext in self.extensions: - outputs.append(self.get_ext_fullpath(ext.name)) - return outputs - - def build_extensions(self): - # First, sanity-check the 'extensions' list - self.check_extensions_list(self.extensions) - if self.parallel: - self._build_extensions_parallel() - else: - self._build_extensions_serial() - - def _build_extensions_parallel(self): - workers = self.parallel - if self.parallel is True: - workers = os.cpu_count() # may return None - try: - from concurrent.futures import ThreadPoolExecutor - except ImportError: - workers = None - - if workers is None: - self._build_extensions_serial() - return - - with ThreadPoolExecutor(max_workers=workers) as executor: - futures = [executor.submit(self.build_extension, ext) - for ext in self.extensions] - for ext, fut in zip(self.extensions, futures): - with self._filter_build_errors(ext): - fut.result() - - def _build_extensions_serial(self): - for ext in self.extensions: - with self._filter_build_errors(ext): - self.build_extension(ext) - - @contextlib.contextmanager - def _filter_build_errors(self, ext): - try: - yield - except (CCompilerError, DistutilsError, CompileError) as e: - if not ext.optional: - raise - self.warn('building extension "%s" failed: %s' % - (ext.name, e)) - - def build_extension(self, ext): - sources = ext.sources - if sources is None or not isinstance(sources, (list, tuple)): - raise DistutilsSetupError( - "in 'ext_modules' option (extension '%s'), " - "'sources' must be present and must be " - "a list of source filenames" % ext.name) - # sort to make the resulting .so file build reproducible - sources = sorted(sources) - - ext_path = self.get_ext_fullpath(ext.name) - depends = sources + ext.depends - if not (self.force or newer_group(depends, ext_path, 'newer')): - log.debug("skipping '%s' extension (up-to-date)", ext.name) - return - else: - log.info("building '%s' extension", ext.name) - - # First, scan the sources for SWIG definition files (.i), run - # SWIG on 'em to create .c files, and modify the sources list - # accordingly. - sources = self.swig_sources(sources, ext) - - # Next, compile the source code to object files. - - # XXX not honouring 'define_macros' or 'undef_macros' -- the - # CCompiler API needs to change to accommodate this, and I - # want to do one thing at a time! - - # Two possible sources for extra compiler arguments: - # - 'extra_compile_args' in Extension object - # - CFLAGS environment variable (not particularly - # elegant, but people seem to expect it and I - # guess it's useful) - # The environment variable should take precedence, and - # any sensible compiler will give precedence to later - # command line args. Hence we combine them in order: - extra_args = ext.extra_compile_args or [] - - macros = ext.define_macros[:] - for undef in ext.undef_macros: - macros.append((undef,)) - - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args, - depends=ext.depends) - - # XXX outdated variable, kept here in case third-part code - # needs it. - self._built_objects = objects[:] - - # Now link the object files together into a "shared object" -- - # of course, first we have to figure out all the other things - # that go into the mix. - if ext.extra_objects: - objects.extend(ext.extra_objects) - extra_args = ext.extra_link_args or [] - - # Detect target language, if not provided - language = ext.language or self.compiler.detect_language(sources) - - self.compiler.link_shared_object( - objects, ext_path, - libraries=self.get_libraries(ext), - library_dirs=ext.library_dirs, - runtime_library_dirs=ext.runtime_library_dirs, - extra_postargs=extra_args, - export_symbols=self.get_export_symbols(ext), - debug=self.debug, - build_temp=self.build_temp, - target_lang=language) - - def swig_sources(self, sources, extension): - """Walk the list of source files in 'sources', looking for SWIG - interface (.i) files. Run SWIG on all that are found, and - return a modified 'sources' list with SWIG source files replaced - by the generated C (or C++) files. - """ - new_sources = [] - swig_sources = [] - swig_targets = {} - - # XXX this drops generated C/C++ files into the source tree, which - # is fine for developers who want to distribute the generated - # source -- but there should be an option to put SWIG output in - # the temp dir. - - if self.swig_cpp: - log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") - - if self.swig_cpp or ('-c++' in self.swig_opts) or \ - ('-c++' in extension.swig_opts): - target_ext = '.cpp' - else: - target_ext = '.c' - - for source in sources: - (base, ext) = os.path.splitext(source) - if ext == ".i": # SWIG interface file - new_sources.append(base + '_wrap' + target_ext) - swig_sources.append(source) - swig_targets[source] = new_sources[-1] - else: - new_sources.append(source) - - if not swig_sources: - return new_sources - - swig = self.swig or self.find_swig() - swig_cmd = [swig, "-python"] - swig_cmd.extend(self.swig_opts) - if self.swig_cpp: - swig_cmd.append("-c++") - - # Do not override commandline arguments - if not self.swig_opts: - for o in extension.swig_opts: - swig_cmd.append(o) - - for source in swig_sources: - target = swig_targets[source] - log.info("swigging %s to %s", source, target) - self.spawn(swig_cmd + ["-o", target, source]) - - return new_sources - - def find_swig(self): - """Return the name of the SWIG executable. On Unix, this is - just "swig" -- it should be in the PATH. Tries a bit harder on - Windows. - """ - if os.name == "posix": - return "swig" - elif os.name == "nt": - # Look for SWIG in its standard installation directory on - # Windows (or so I presume!). If we find it there, great; - # if not, act like Unix and assume it's in the PATH. - for vers in ("1.3", "1.2", "1.1"): - fn = os.path.join("c:\\swig%s" % vers, "swig.exe") - if os.path.isfile(fn): - return fn - else: - return "swig.exe" - else: - raise DistutilsPlatformError( - "I don't know how to find (much less run) SWIG " - "on platform '%s'" % os.name) - - # -- Name generators ----------------------------------------------- - # (extension names, filenames, whatever) - def get_ext_fullpath(self, ext_name): - """Returns the path of the filename for a given extension. - - The file is located in `build_lib` or directly in the package - (inplace option). - """ - fullname = self.get_ext_fullname(ext_name) - modpath = fullname.split('.') - filename = self.get_ext_filename(modpath[-1]) - - if not self.inplace: - # no further work needed - # returning : - # build_dir/package/path/filename - filename = os.path.join(*modpath[:-1]+[filename]) - return os.path.join(self.build_lib, filename) - - # the inplace option requires to find the package directory - # using the build_py command for that - package = '.'.join(modpath[0:-1]) - build_py = self.get_finalized_command('build_py') - package_dir = os.path.abspath(build_py.get_package_dir(package)) - - # returning - # package_dir/filename - return os.path.join(package_dir, filename) - - def get_ext_fullname(self, ext_name): - """Returns the fullname of a given extension name. - - Adds the `package.` prefix""" - if self.package is None: - return ext_name - else: - return self.package + '.' + ext_name - - def get_ext_filename(self, ext_name): - r"""Convert the name of an extension (eg. "foo.bar") into the name - of the file from which it will be loaded (eg. "foo/bar.so", or - "foo\bar.pyd"). - """ - from distutils.sysconfig import get_config_var - ext_path = ext_name.split('.') - ext_suffix = get_config_var('EXT_SUFFIX') - return os.path.join(*ext_path) + ext_suffix - - def get_export_symbols(self, ext): - """Return the list of symbols that a shared extension has to - export. This either uses 'ext.export_symbols' or, if it's not - provided, "PyInit_" + module_name. Only relevant on Windows, where - the .pyd file (DLL) must export the module "PyInit_" function. - """ - suffix = '_' + ext.name.split('.')[-1] - try: - # Unicode module name support as defined in PEP-489 - # https://www.python.org/dev/peps/pep-0489/#export-hook-name - suffix.encode('ascii') - except UnicodeEncodeError: - suffix = 'U' + suffix.encode('punycode').replace(b'-', b'_').decode('ascii') - - initfunc_name = "PyInit" + suffix - if initfunc_name not in ext.export_symbols: - ext.export_symbols.append(initfunc_name) - return ext.export_symbols - - def get_libraries(self, ext): - """Return the list of libraries to link against when building a - shared extension. On most platforms, this is just 'ext.libraries'; - on Windows, we add the Python library (eg. python20.dll). - """ - # The python library is always needed on Windows. For MSVC, this - # is redundant, since the library is mentioned in a pragma in - # pyconfig.h that MSVC groks. The other Windows compilers all seem - # to need it mentioned explicitly, though, so that's what we do. - # Append '_d' to the python import library on debug builds. - if sys.platform == "win32": - from distutils._msvccompiler import MSVCCompiler - if not isinstance(self.compiler, MSVCCompiler): - template = "python%d%d" - if self.debug: - template = template + '_d' - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - # don't extend ext.libraries, it may be shared with other - # extensions, it is a reference to the original list - return ext.libraries + [pythonlib] - else: - # On Android only the main executable and LD_PRELOADs are considered - # to be RTLD_GLOBAL, all the dependencies of the main executable - # remain RTLD_LOCAL and so the shared libraries must be linked with - # libpython when python is built with a shared python library (issue - # bpo-21536). - # On Cygwin (and if required, other POSIX-like platforms based on - # Windows like MinGW) it is simply necessary that all symbols in - # shared libraries are resolved at link time. - from distutils.sysconfig import get_config_var - link_libpython = False - if get_config_var('Py_ENABLE_SHARED'): - # A native build on an Android device or on Cygwin - if hasattr(sys, 'getandroidapilevel'): - link_libpython = True - elif sys.platform == 'cygwin': - link_libpython = True - elif '_PYTHON_HOST_PLATFORM' in os.environ: - # We are cross-compiling for one of the relevant platforms - if get_config_var('ANDROID_API_LEVEL') != 0: - link_libpython = True - elif get_config_var('MACHDEP') == 'cygwin': - link_libpython = True - - if link_libpython: - ldversion = get_config_var('LDVERSION') - return ext.libraries + ['python' + ldversion] - - return ext.libraries diff --git a/command/build_py.py b/command/build_py.py deleted file mode 100644 index cf0ca57c..00000000 --- a/command/build_py.py +++ /dev/null @@ -1,416 +0,0 @@ -"""distutils.command.build_py - -Implements the Distutils 'build_py' command.""" - -import os -import importlib.util -import sys -from glob import glob - -from distutils.core import Command -from distutils.errors import * -from distutils.util import convert_path, Mixin2to3 -from distutils import log - -class build_py (Command): - - description = "\"build\" pure Python modules (copy to build directory)" - - user_options = [ - ('build-lib=', 'd', "directory to \"build\" (copy) to"), - ('compile', 'c', "compile .py to .pyc"), - ('no-compile', None, "don't compile .py files [default]"), - ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), - ('force', 'f', "forcibly build everything (ignore file timestamps)"), - ] - - boolean_options = ['compile', 'force'] - negative_opt = {'no-compile' : 'compile'} - - def initialize_options(self): - self.build_lib = None - self.py_modules = None - self.package = None - self.package_data = None - self.package_dir = None - self.compile = 0 - self.optimize = 0 - self.force = None - - def finalize_options(self): - self.set_undefined_options('build', - ('build_lib', 'build_lib'), - ('force', 'force')) - - # Get the distribution options that are aliases for build_py - # options -- list of packages and list of modules. - self.packages = self.distribution.packages - self.py_modules = self.distribution.py_modules - self.package_data = self.distribution.package_data - self.package_dir = {} - if self.distribution.package_dir: - for name, path in self.distribution.package_dir.items(): - self.package_dir[name] = convert_path(path) - self.data_files = self.get_data_files() - - # Ick, copied straight from install_lib.py (fancy_getopt needs a - # type system! Hell, *everything* needs a type system!!!) - if not isinstance(self.optimize, int): - try: - self.optimize = int(self.optimize) - assert 0 <= self.optimize <= 2 - except (ValueError, AssertionError): - raise DistutilsOptionError("optimize must be 0, 1, or 2") - - def run(self): - # XXX copy_file by default preserves atime and mtime. IMHO this is - # the right thing to do, but perhaps it should be an option -- in - # particular, a site administrator might want installed files to - # reflect the time of installation rather than the last - # modification time before the installed release. - - # XXX copy_file by default preserves mode, which appears to be the - # wrong thing to do: if a file is read-only in the working - # directory, we want it to be installed read/write so that the next - # installation of the same module distribution can overwrite it - # without problems. (This might be a Unix-specific issue.) Thus - # we turn off 'preserve_mode' when copying to the build directory, - # since the build directory is supposed to be exactly what the - # installation will look like (ie. we preserve mode when - # installing). - - # Two options control which modules will be installed: 'packages' - # and 'py_modules'. The former lets us work with whole packages, not - # specifying individual modules at all; the latter is for - # specifying modules one-at-a-time. - - if self.py_modules: - self.build_modules() - if self.packages: - self.build_packages() - self.build_package_data() - - self.byte_compile(self.get_outputs(include_bytecode=0)) - - def get_data_files(self): - """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" - data = [] - if not self.packages: - return data - for package in self.packages: - # Locate package source directory - src_dir = self.get_package_dir(package) - - # Compute package build directory - build_dir = os.path.join(*([self.build_lib] + package.split('.'))) - - # Length of path to strip from found files - plen = 0 - if src_dir: - plen = len(src_dir)+1 - - # Strip directory from globbed filenames - filenames = [ - file[plen:] for file in self.find_data_files(package, src_dir) - ] - data.append((package, src_dir, build_dir, filenames)) - return data - - def find_data_files(self, package, src_dir): - """Return filenames for package's data files in 'src_dir'""" - globs = (self.package_data.get('', []) - + self.package_data.get(package, [])) - files = [] - for pattern in globs: - # Each pattern has to be converted to a platform-specific path - filelist = glob(os.path.join(src_dir, convert_path(pattern))) - # Files that match more than one pattern are only added once - files.extend([fn for fn in filelist if fn not in files - and os.path.isfile(fn)]) - return files - - def build_package_data(self): - """Copy data files into build directory""" - lastdir = None - for package, src_dir, build_dir, filenames in self.data_files: - for filename in filenames: - target = os.path.join(build_dir, filename) - self.mkpath(os.path.dirname(target)) - self.copy_file(os.path.join(src_dir, filename), target, - preserve_mode=False) - - def get_package_dir(self, package): - """Return the directory, relative to the top of the source - distribution, where package 'package' should be found - (at least according to the 'package_dir' option, if any).""" - path = package.split('.') - - if not self.package_dir: - if path: - return os.path.join(*path) - else: - return '' - else: - tail = [] - while path: - try: - pdir = self.package_dir['.'.join(path)] - except KeyError: - tail.insert(0, path[-1]) - del path[-1] - else: - tail.insert(0, pdir) - return os.path.join(*tail) - else: - # Oops, got all the way through 'path' without finding a - # match in package_dir. If package_dir defines a directory - # for the root (nameless) package, then fallback on it; - # otherwise, we might as well have not consulted - # package_dir at all, as we just use the directory implied - # by 'tail' (which should be the same as the original value - # of 'path' at this point). - pdir = self.package_dir.get('') - if pdir is not None: - tail.insert(0, pdir) - - if tail: - return os.path.join(*tail) - else: - return '' - - def check_package(self, package, package_dir): - # Empty dir name means current directory, which we can probably - # assume exists. Also, os.path.exists and isdir don't know about - # my "empty string means current dir" convention, so we have to - # circumvent them. - if package_dir != "": - if not os.path.exists(package_dir): - raise DistutilsFileError( - "package directory '%s' does not exist" % package_dir) - if not os.path.isdir(package_dir): - raise DistutilsFileError( - "supposed package directory '%s' exists, " - "but is not a directory" % package_dir) - - # Require __init__.py for all but the "root package" - if package: - init_py = os.path.join(package_dir, "__init__.py") - if os.path.isfile(init_py): - return init_py - else: - log.warn(("package init file '%s' not found " + - "(or not a regular file)"), init_py) - - # Either not in a package at all (__init__.py not expected), or - # __init__.py doesn't exist -- so don't return the filename. - return None - - def check_module(self, module, module_file): - if not os.path.isfile(module_file): - log.warn("file %s (for module %s) not found", module_file, module) - return False - else: - return True - - def find_package_modules(self, package, package_dir): - self.check_package(package, package_dir) - module_files = glob(os.path.join(package_dir, "*.py")) - modules = [] - setup_script = os.path.abspath(self.distribution.script_name) - - for f in module_files: - abs_f = os.path.abspath(f) - if abs_f != setup_script: - module = os.path.splitext(os.path.basename(f))[0] - modules.append((package, module, f)) - else: - self.debug_print("excluding %s" % setup_script) - return modules - - def find_modules(self): - """Finds individually-specified Python modules, ie. those listed by - module name in 'self.py_modules'. Returns a list of tuples (package, - module_base, filename): 'package' is a tuple of the path through - package-space to the module; 'module_base' is the bare (no - packages, no dots) module name, and 'filename' is the path to the - ".py" file (relative to the distribution root) that implements the - module. - """ - # Map package names to tuples of useful info about the package: - # (package_dir, checked) - # package_dir - the directory where we'll find source files for - # this package - # checked - true if we have checked that the package directory - # is valid (exists, contains __init__.py, ... ?) - packages = {} - - # List of (package, module, filename) tuples to return - modules = [] - - # We treat modules-in-packages almost the same as toplevel modules, - # just the "package" for a toplevel is empty (either an empty - # string or empty list, depending on context). Differences: - # - don't check for __init__.py in directory for empty package - for module in self.py_modules: - path = module.split('.') - package = '.'.join(path[0:-1]) - module_base = path[-1] - - try: - (package_dir, checked) = packages[package] - except KeyError: - package_dir = self.get_package_dir(package) - checked = 0 - - if not checked: - init_py = self.check_package(package, package_dir) - packages[package] = (package_dir, 1) - if init_py: - modules.append((package, "__init__", init_py)) - - # XXX perhaps we should also check for just .pyc files - # (so greedy closed-source bastards can distribute Python - # modules too) - module_file = os.path.join(package_dir, module_base + ".py") - if not self.check_module(module, module_file): - continue - - modules.append((package, module_base, module_file)) - - return modules - - def find_all_modules(self): - """Compute the list of all modules that will be built, whether - they are specified one-module-at-a-time ('self.py_modules') or - by whole packages ('self.packages'). Return a list of tuples - (package, module, module_file), just like 'find_modules()' and - 'find_package_modules()' do.""" - modules = [] - if self.py_modules: - modules.extend(self.find_modules()) - if self.packages: - for package in self.packages: - package_dir = self.get_package_dir(package) - m = self.find_package_modules(package, package_dir) - modules.extend(m) - return modules - - def get_source_files(self): - return [module[-1] for module in self.find_all_modules()] - - def get_module_outfile(self, build_dir, package, module): - outfile_path = [build_dir] + list(package) + [module + ".py"] - return os.path.join(*outfile_path) - - def get_outputs(self, include_bytecode=1): - modules = self.find_all_modules() - outputs = [] - for (package, module, module_file) in modules: - package = package.split('.') - filename = self.get_module_outfile(self.build_lib, package, module) - outputs.append(filename) - if include_bytecode: - if self.compile: - outputs.append(importlib.util.cache_from_source( - filename, optimization='')) - if self.optimize > 0: - outputs.append(importlib.util.cache_from_source( - filename, optimization=self.optimize)) - - outputs += [ - os.path.join(build_dir, filename) - for package, src_dir, build_dir, filenames in self.data_files - for filename in filenames - ] - - return outputs - - def build_module(self, module, module_file, package): - if isinstance(package, str): - package = package.split('.') - elif not isinstance(package, (list, tuple)): - raise TypeError( - "'package' must be a string (dot-separated), list, or tuple") - - # Now put the module source file into the "build" area -- this is - # easy, we just copy it somewhere under self.build_lib (the build - # directory for Python source). - outfile = self.get_module_outfile(self.build_lib, package, module) - dir = os.path.dirname(outfile) - self.mkpath(dir) - return self.copy_file(module_file, outfile, preserve_mode=0) - - def build_modules(self): - modules = self.find_modules() - for (package, module, module_file) in modules: - # Now "build" the module -- ie. copy the source file to - # self.build_lib (the build directory for Python source). - # (Actually, it gets copied to the directory for this package - # under self.build_lib.) - self.build_module(module, module_file, package) - - def build_packages(self): - for package in self.packages: - # Get list of (package, module, module_file) tuples based on - # scanning the package directory. 'package' is only included - # in the tuple so that 'find_modules()' and - # 'find_package_tuples()' have a consistent interface; it's - # ignored here (apart from a sanity check). Also, 'module' is - # the *unqualified* module name (ie. no dots, no package -- we - # already know its package!), and 'module_file' is the path to - # the .py file, relative to the current directory - # (ie. including 'package_dir'). - package_dir = self.get_package_dir(package) - modules = self.find_package_modules(package, package_dir) - - # Now loop over the modules we found, "building" each one (just - # copy it to self.build_lib). - for (package_, module, module_file) in modules: - assert package == package_ - self.build_module(module, module_file, package) - - def byte_compile(self, files): - if sys.dont_write_bytecode: - self.warn('byte-compiling is disabled, skipping.') - return - - from distutils.util import byte_compile - prefix = self.build_lib - if prefix[-1] != os.sep: - prefix = prefix + os.sep - - # XXX this code is essentially the same as the 'byte_compile() - # method of the "install_lib" command, except for the determination - # of the 'prefix' string. Hmmm. - if self.compile: - byte_compile(files, optimize=0, - force=self.force, prefix=prefix, dry_run=self.dry_run) - if self.optimize > 0: - byte_compile(files, optimize=self.optimize, - force=self.force, prefix=prefix, dry_run=self.dry_run) - -class build_py_2to3(build_py, Mixin2to3): - def run(self): - self.updated_files = [] - - # Base class code - if self.py_modules: - self.build_modules() - if self.packages: - self.build_packages() - self.build_package_data() - - # 2to3 - self.run_2to3(self.updated_files) - - # Remaining base class code - self.byte_compile(self.get_outputs(include_bytecode=0)) - - def build_module(self, module, module_file, package): - res = build_py.build_module(self, module, module_file, package) - if res[1]: - # file was copied - self.updated_files.append(res[0]) - return res diff --git a/command/build_scripts.py b/command/build_scripts.py deleted file mode 100644 index ccc70e64..00000000 --- a/command/build_scripts.py +++ /dev/null @@ -1,160 +0,0 @@ -"""distutils.command.build_scripts - -Implements the Distutils 'build_scripts' command.""" - -import os, re -from stat import ST_MODE -from distutils import sysconfig -from distutils.core import Command -from distutils.dep_util import newer -from distutils.util import convert_path, Mixin2to3 -from distutils import log -import tokenize - -# check if Python is called on the first line with this expression -first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$') - -class build_scripts(Command): - - description = "\"build\" scripts (copy and fixup #! line)" - - user_options = [ - ('build-dir=', 'd', "directory to \"build\" (copy) to"), - ('force', 'f', "forcibly build everything (ignore file timestamps"), - ('executable=', 'e', "specify final destination interpreter path"), - ] - - boolean_options = ['force'] - - - def initialize_options(self): - self.build_dir = None - self.scripts = None - self.force = None - self.executable = None - self.outfiles = None - - def finalize_options(self): - self.set_undefined_options('build', - ('build_scripts', 'build_dir'), - ('force', 'force'), - ('executable', 'executable')) - self.scripts = self.distribution.scripts - - def get_source_files(self): - return self.scripts - - def run(self): - if not self.scripts: - return - self.copy_scripts() - - - def copy_scripts(self): - r"""Copy each script listed in 'self.scripts'; if it's marked as a - Python script in the Unix way (first line matches 'first_line_re', - ie. starts with "\#!" and contains "python"), then adjust the first - line to refer to the current Python interpreter as we copy. - """ - self.mkpath(self.build_dir) - outfiles = [] - updated_files = [] - for script in self.scripts: - adjust = False - script = convert_path(script) - outfile = os.path.join(self.build_dir, os.path.basename(script)) - outfiles.append(outfile) - - if not self.force and not newer(script, outfile): - log.debug("not copying %s (up-to-date)", script) - continue - - # Always open the file, but ignore failures in dry-run mode -- - # that way, we'll get accurate feedback if we can read the - # script. - try: - f = open(script, "rb") - except OSError: - if not self.dry_run: - raise - f = None - else: - encoding, lines = tokenize.detect_encoding(f.readline) - f.seek(0) - first_line = f.readline() - if not first_line: - self.warn("%s is an empty file (skipping)" % script) - continue - - match = first_line_re.match(first_line) - if match: - adjust = True - post_interp = match.group(1) or b'' - - if adjust: - log.info("copying and adjusting %s -> %s", script, - self.build_dir) - updated_files.append(outfile) - if not self.dry_run: - if not sysconfig.python_build: - executable = self.executable - else: - executable = os.path.join( - sysconfig.get_config_var("BINDIR"), - "python%s%s" % (sysconfig.get_config_var("VERSION"), - sysconfig.get_config_var("EXE"))) - executable = os.fsencode(executable) - shebang = b"#!" + executable + post_interp + b"\n" - # Python parser starts to read a script using UTF-8 until - # it gets a #coding:xxx cookie. The shebang has to be the - # first line of a file, the #coding:xxx cookie cannot be - # written before. So the shebang has to be decodable from - # UTF-8. - try: - shebang.decode('utf-8') - except UnicodeDecodeError: - raise ValueError( - "The shebang ({!r}) is not decodable " - "from utf-8".format(shebang)) - # If the script is encoded to a custom encoding (use a - # #coding:xxx cookie), the shebang has to be decodable from - # the script encoding too. - try: - shebang.decode(encoding) - except UnicodeDecodeError: - raise ValueError( - "The shebang ({!r}) is not decodable " - "from the script encoding ({})" - .format(shebang, encoding)) - with open(outfile, "wb") as outf: - outf.write(shebang) - outf.writelines(f.readlines()) - if f: - f.close() - else: - if f: - f.close() - updated_files.append(outfile) - self.copy_file(script, outfile) - - if os.name == 'posix': - for file in outfiles: - if self.dry_run: - log.info("changing mode of %s", file) - else: - oldmode = os.stat(file)[ST_MODE] & 0o7777 - newmode = (oldmode | 0o555) & 0o7777 - if newmode != oldmode: - log.info("changing mode of %s from %o to %o", - file, oldmode, newmode) - os.chmod(file, newmode) - # XXX should we modify self.outfiles? - return outfiles, updated_files - -class build_scripts_2to3(build_scripts, Mixin2to3): - - def copy_scripts(self): - outfiles, updated_files = build_scripts.copy_scripts(self) - if not self.dry_run: - self.run_2to3(updated_files) - return outfiles, updated_files diff --git a/command/check.py b/command/check.py deleted file mode 100644 index ada25006..00000000 --- a/command/check.py +++ /dev/null @@ -1,148 +0,0 @@ -"""distutils.command.check - -Implements the Distutils 'check' command. -""" -from distutils.core import Command -from distutils.errors import DistutilsSetupError - -try: - # docutils is installed - from docutils.utils import Reporter - from docutils.parsers.rst import Parser - from docutils import frontend - from docutils import nodes - - class SilentReporter(Reporter): - - def __init__(self, source, report_level, halt_level, stream=None, - debug=0, encoding='ascii', error_handler='replace'): - self.messages = [] - Reporter.__init__(self, source, report_level, halt_level, stream, - debug, encoding, error_handler) - - def system_message(self, level, message, *children, **kwargs): - self.messages.append((level, message, children, kwargs)) - return nodes.system_message(message, level=level, - type=self.levels[level], - *children, **kwargs) - - HAS_DOCUTILS = True -except Exception: - # Catch all exceptions because exceptions besides ImportError probably - # indicate that docutils is not ported to Py3k. - HAS_DOCUTILS = False - -class check(Command): - """This command checks the meta-data of the package. - """ - description = ("perform some checks on the package") - user_options = [('metadata', 'm', 'Verify meta-data'), - ('restructuredtext', 'r', - ('Checks if long string meta-data syntax ' - 'are reStructuredText-compliant')), - ('strict', 's', - 'Will exit with an error if a check fails')] - - boolean_options = ['metadata', 'restructuredtext', 'strict'] - - def initialize_options(self): - """Sets default values for options.""" - self.restructuredtext = 0 - self.metadata = 1 - self.strict = 0 - self._warnings = 0 - - def finalize_options(self): - pass - - def warn(self, msg): - """Counts the number of warnings that occurs.""" - self._warnings += 1 - return Command.warn(self, msg) - - def run(self): - """Runs the command.""" - # perform the various tests - if self.metadata: - self.check_metadata() - if self.restructuredtext: - if HAS_DOCUTILS: - self.check_restructuredtext() - elif self.strict: - raise DistutilsSetupError('The docutils package is needed.') - - # let's raise an error in strict mode, if we have at least - # one warning - if self.strict and self._warnings > 0: - raise DistutilsSetupError('Please correct your package.') - - def check_metadata(self): - """Ensures that all required elements of meta-data are supplied. - - Required fields: - name, version, URL - - Recommended fields: - (author and author_email) or (maintainer and maintainer_email)) - - Warns if any are missing. - """ - metadata = self.distribution.metadata - - missing = [] - for attr in ('name', 'version', 'url'): - if not (hasattr(metadata, attr) and getattr(metadata, attr)): - missing.append(attr) - - if missing: - self.warn("missing required meta-data: %s" % ', '.join(missing)) - if metadata.author: - if not metadata.author_email: - self.warn("missing meta-data: if 'author' supplied, " + - "'author_email' should be supplied too") - elif metadata.maintainer: - if not metadata.maintainer_email: - self.warn("missing meta-data: if 'maintainer' supplied, " + - "'maintainer_email' should be supplied too") - else: - self.warn("missing meta-data: either (author and author_email) " + - "or (maintainer and maintainer_email) " + - "should be supplied") - - def check_restructuredtext(self): - """Checks if the long string fields are reST-compliant.""" - data = self.distribution.get_long_description() - for warning in self._check_rst_data(data): - line = warning[-1].get('line') - if line is None: - warning = warning[1] - else: - warning = '%s (line %s)' % (warning[1], line) - self.warn(warning) - - def _check_rst_data(self, data): - """Returns warnings when the provided data doesn't compile.""" - # the include and csv_table directives need this to be a path - source_path = self.distribution.script_name or 'setup.py' - parser = Parser() - settings = frontend.OptionParser(components=(Parser,)).get_default_values() - settings.tab_width = 4 - settings.pep_references = None - settings.rfc_references = None - reporter = SilentReporter(source_path, - settings.report_level, - settings.halt_level, - stream=settings.warning_stream, - debug=settings.debug, - encoding=settings.error_encoding, - error_handler=settings.error_encoding_error_handler) - - document = nodes.document(settings, reporter, source=source_path) - document.note_source(source_path, -1) - try: - parser.parse(data, document) - except AttributeError as e: - reporter.messages.append( - (-1, 'Could not finish the parsing: %s.' % e, '', {})) - - return reporter.messages diff --git a/command/clean.py b/command/clean.py deleted file mode 100644 index 0cb27016..00000000 --- a/command/clean.py +++ /dev/null @@ -1,76 +0,0 @@ -"""distutils.command.clean - -Implements the Distutils 'clean' command.""" - -# contributed by Bastian Kleineidam , added 2000-03-18 - -import os -from distutils.core import Command -from distutils.dir_util import remove_tree -from distutils import log - -class clean(Command): - - description = "clean up temporary files from 'build' command" - user_options = [ - ('build-base=', 'b', - "base build directory (default: 'build.build-base')"), - ('build-lib=', None, - "build directory for all modules (default: 'build.build-lib')"), - ('build-temp=', 't', - "temporary build directory (default: 'build.build-temp')"), - ('build-scripts=', None, - "build directory for scripts (default: 'build.build-scripts')"), - ('bdist-base=', None, - "temporary directory for built distributions"), - ('all', 'a', - "remove all build output, not just temporary by-products") - ] - - boolean_options = ['all'] - - def initialize_options(self): - self.build_base = None - self.build_lib = None - self.build_temp = None - self.build_scripts = None - self.bdist_base = None - self.all = None - - def finalize_options(self): - self.set_undefined_options('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib'), - ('build_scripts', 'build_scripts'), - ('build_temp', 'build_temp')) - self.set_undefined_options('bdist', - ('bdist_base', 'bdist_base')) - - def run(self): - # remove the build/temp. directory (unless it's already - # gone) - if os.path.exists(self.build_temp): - remove_tree(self.build_temp, dry_run=self.dry_run) - else: - log.debug("'%s' does not exist -- can't clean it", - self.build_temp) - - if self.all: - # remove build directories - for directory in (self.build_lib, - self.bdist_base, - self.build_scripts): - if os.path.exists(directory): - remove_tree(directory, dry_run=self.dry_run) - else: - log.warn("'%s' does not exist -- can't clean it", - directory) - - # just for the heck of it, try to remove the base build directory: - # we might have emptied it right now, but if not we don't care - if not self.dry_run: - try: - os.rmdir(self.build_base) - log.info("removing '%s'", self.build_base) - except OSError: - pass diff --git a/command/command_template b/command/command_template deleted file mode 100644 index 6106819d..00000000 --- a/command/command_template +++ /dev/null @@ -1,33 +0,0 @@ -"""distutils.command.x - -Implements the Distutils 'x' command. -""" - -# created 2000/mm/dd, John Doe - -__revision__ = "$Id$" - -from distutils.core import Command - - -class x(Command): - - # Brief (40-50 characters) description of the command - description = "" - - # List of option tuples: long name, short name (None if no short - # name), and help string. - user_options = [('', '', - ""), - ] - - def initialize_options(self): - self. = None - self. = None - self. = None - - def finalize_options(self): - if self.x is None: - self.x = - - def run(self): diff --git a/command/config.py b/command/config.py deleted file mode 100644 index aeda408e..00000000 --- a/command/config.py +++ /dev/null @@ -1,344 +0,0 @@ -"""distutils.command.config - -Implements the Distutils 'config' command, a (mostly) empty command class -that exists mainly to be sub-classed by specific module distributions and -applications. The idea is that while every "config" command is different, -at least they're all named the same, and users always see "config" in the -list of standard commands. Also, this is a good place to put common -configure-like tasks: "try to compile this C code", or "figure out where -this header file lives". -""" - -import os, re - -from distutils.core import Command -from distutils.errors import DistutilsExecError -from distutils.sysconfig import customize_compiler -from distutils import log - -LANG_EXT = {"c": ".c", "c++": ".cxx"} - -class config(Command): - - description = "prepare to build" - - user_options = [ - ('compiler=', None, - "specify the compiler type"), - ('cc=', None, - "specify the compiler executable"), - ('include-dirs=', 'I', - "list of directories to search for header files"), - ('define=', 'D', - "C preprocessor macros to define"), - ('undef=', 'U', - "C preprocessor macros to undefine"), - ('libraries=', 'l', - "external C libraries to link with"), - ('library-dirs=', 'L', - "directories to search for external C libraries"), - - ('noisy', None, - "show every action (compile, link, run, ...) taken"), - ('dump-source', None, - "dump generated source files before attempting to compile them"), - ] - - - # The three standard command methods: since the "config" command - # does nothing by default, these are empty. - - def initialize_options(self): - self.compiler = None - self.cc = None - self.include_dirs = None - self.libraries = None - self.library_dirs = None - - # maximal output for now - self.noisy = 1 - self.dump_source = 1 - - # list of temporary files generated along-the-way that we have - # to clean at some point - self.temp_files = [] - - def finalize_options(self): - if self.include_dirs is None: - self.include_dirs = self.distribution.include_dirs or [] - elif isinstance(self.include_dirs, str): - self.include_dirs = self.include_dirs.split(os.pathsep) - - if self.libraries is None: - self.libraries = [] - elif isinstance(self.libraries, str): - self.libraries = [self.libraries] - - if self.library_dirs is None: - self.library_dirs = [] - elif isinstance(self.library_dirs, str): - self.library_dirs = self.library_dirs.split(os.pathsep) - - def run(self): - pass - - # Utility methods for actual "config" commands. The interfaces are - # loosely based on Autoconf macros of similar names. Sub-classes - # may use these freely. - - def _check_compiler(self): - """Check that 'self.compiler' really is a CCompiler object; - if not, make it one. - """ - # We do this late, and only on-demand, because this is an expensive - # import. - from distutils.ccompiler import CCompiler, new_compiler - if not isinstance(self.compiler, CCompiler): - self.compiler = new_compiler(compiler=self.compiler, - dry_run=self.dry_run, force=1) - customize_compiler(self.compiler) - if self.include_dirs: - self.compiler.set_include_dirs(self.include_dirs) - if self.libraries: - self.compiler.set_libraries(self.libraries) - if self.library_dirs: - self.compiler.set_library_dirs(self.library_dirs) - - def _gen_temp_sourcefile(self, body, headers, lang): - filename = "_configtest" + LANG_EXT[lang] - with open(filename, "w") as file: - if headers: - for header in headers: - file.write("#include <%s>\n" % header) - file.write("\n") - file.write(body) - if body[-1] != "\n": - file.write("\n") - return filename - - def _preprocess(self, body, headers, include_dirs, lang): - src = self._gen_temp_sourcefile(body, headers, lang) - out = "_configtest.i" - self.temp_files.extend([src, out]) - self.compiler.preprocess(src, out, include_dirs=include_dirs) - return (src, out) - - def _compile(self, body, headers, include_dirs, lang): - src = self._gen_temp_sourcefile(body, headers, lang) - if self.dump_source: - dump_file(src, "compiling '%s':" % src) - (obj,) = self.compiler.object_filenames([src]) - self.temp_files.extend([src, obj]) - self.compiler.compile([src], include_dirs=include_dirs) - return (src, obj) - - def _link(self, body, headers, include_dirs, libraries, library_dirs, - lang): - (src, obj) = self._compile(body, headers, include_dirs, lang) - prog = os.path.splitext(os.path.basename(src))[0] - self.compiler.link_executable([obj], prog, - libraries=libraries, - library_dirs=library_dirs, - target_lang=lang) - - if self.compiler.exe_extension is not None: - prog = prog + self.compiler.exe_extension - self.temp_files.append(prog) - - return (src, obj, prog) - - def _clean(self, *filenames): - if not filenames: - filenames = self.temp_files - self.temp_files = [] - log.info("removing: %s", ' '.join(filenames)) - for filename in filenames: - try: - os.remove(filename) - except OSError: - pass - - - # XXX these ignore the dry-run flag: what to do, what to do? even if - # you want a dry-run build, you still need some sort of configuration - # info. My inclination is to make it up to the real config command to - # consult 'dry_run', and assume a default (minimal) configuration if - # true. The problem with trying to do it here is that you'd have to - # return either true or false from all the 'try' methods, neither of - # which is correct. - - # XXX need access to the header search path and maybe default macros. - - def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): - """Construct a source file from 'body' (a string containing lines - of C/C++ code) and 'headers' (a list of header files to include) - and run it through the preprocessor. Return true if the - preprocessor succeeded, false if there were any errors. - ('body' probably isn't of much use, but what the heck.) - """ - from distutils.ccompiler import CompileError - self._check_compiler() - ok = True - try: - self._preprocess(body, headers, include_dirs, lang) - except CompileError: - ok = False - - self._clean() - return ok - - def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, - lang="c"): - """Construct a source file (just like 'try_cpp()'), run it through - the preprocessor, and return true if any line of the output matches - 'pattern'. 'pattern' should either be a compiled regex object or a - string containing a regex. If both 'body' and 'headers' are None, - preprocesses an empty file -- which can be useful to determine the - symbols the preprocessor and compiler set by default. - """ - self._check_compiler() - src, out = self._preprocess(body, headers, include_dirs, lang) - - if isinstance(pattern, str): - pattern = re.compile(pattern) - - with open(out) as file: - match = False - while True: - line = file.readline() - if line == '': - break - if pattern.search(line): - match = True - break - - self._clean() - return match - - def try_compile(self, body, headers=None, include_dirs=None, lang="c"): - """Try to compile a source file built from 'body' and 'headers'. - Return true on success, false otherwise. - """ - from distutils.ccompiler import CompileError - self._check_compiler() - try: - self._compile(body, headers, include_dirs, lang) - ok = True - except CompileError: - ok = False - - log.info(ok and "success!" or "failure.") - self._clean() - return ok - - def try_link(self, body, headers=None, include_dirs=None, libraries=None, - library_dirs=None, lang="c"): - """Try to compile and link a source file, built from 'body' and - 'headers', to executable form. Return true on success, false - otherwise. - """ - from distutils.ccompiler import CompileError, LinkError - self._check_compiler() - try: - self._link(body, headers, include_dirs, - libraries, library_dirs, lang) - ok = True - except (CompileError, LinkError): - ok = False - - log.info(ok and "success!" or "failure.") - self._clean() - return ok - - def try_run(self, body, headers=None, include_dirs=None, libraries=None, - library_dirs=None, lang="c"): - """Try to compile, link to an executable, and run a program - built from 'body' and 'headers'. Return true on success, false - otherwise. - """ - from distutils.ccompiler import CompileError, LinkError - self._check_compiler() - try: - src, obj, exe = self._link(body, headers, include_dirs, - libraries, library_dirs, lang) - self.spawn([exe]) - ok = True - except (CompileError, LinkError, DistutilsExecError): - ok = False - - log.info(ok and "success!" or "failure.") - self._clean() - return ok - - - # -- High-level methods -------------------------------------------- - # (these are the ones that are actually likely to be useful - # when implementing a real-world config command!) - - def check_func(self, func, headers=None, include_dirs=None, - libraries=None, library_dirs=None, decl=0, call=0): - """Determine if function 'func' is available by constructing a - source file that refers to 'func', and compiles and links it. - If everything succeeds, returns true; otherwise returns false. - - The constructed source file starts out by including the header - files listed in 'headers'. If 'decl' is true, it then declares - 'func' (as "int func()"); you probably shouldn't supply 'headers' - and set 'decl' true in the same call, or you might get errors about - a conflicting declarations for 'func'. Finally, the constructed - 'main()' function either references 'func' or (if 'call' is true) - calls it. 'libraries' and 'library_dirs' are used when - linking. - """ - self._check_compiler() - body = [] - if decl: - body.append("int %s ();" % func) - body.append("int main () {") - if call: - body.append(" %s();" % func) - else: - body.append(" %s;" % func) - body.append("}") - body = "\n".join(body) + "\n" - - return self.try_link(body, headers, include_dirs, - libraries, library_dirs) - - def check_lib(self, library, library_dirs=None, headers=None, - include_dirs=None, other_libraries=[]): - """Determine if 'library' is available to be linked against, - without actually checking that any particular symbols are provided - by it. 'headers' will be used in constructing the source file to - be compiled, but the only effect of this is to check if all the - header files listed are available. Any libraries listed in - 'other_libraries' will be included in the link, in case 'library' - has symbols that depend on other libraries. - """ - self._check_compiler() - return self.try_link("int main (void) { }", headers, include_dirs, - [library] + other_libraries, library_dirs) - - def check_header(self, header, include_dirs=None, library_dirs=None, - lang="c"): - """Determine if the system header file named by 'header_file' - exists and can be found by the preprocessor; return true if so, - false otherwise. - """ - return self.try_cpp(body="/* No body */", headers=[header], - include_dirs=include_dirs) - -def dump_file(filename, head=None): - """Dumps a file content into log.info. - - If head is not None, will be dumped before the file content. - """ - if head is None: - log.info('%s', filename) - else: - log.info(head) - file = open(filename) - try: - log.info(file.read()) - finally: - file.close() diff --git a/command/install.py b/command/install.py deleted file mode 100644 index aaa300ef..00000000 --- a/command/install.py +++ /dev/null @@ -1,657 +0,0 @@ -"""distutils.command.install - -Implements the Distutils 'install' command.""" - -import sys -import os - -from distutils import log -from distutils.core import Command -from distutils.debug import DEBUG -from distutils.sysconfig import get_config_vars -from distutils.errors import DistutilsPlatformError -from distutils.file_util import write_file -from distutils.util import convert_path, subst_vars, change_root -from distutils.util import get_platform -from distutils.errors import DistutilsOptionError - -from site import USER_BASE -from site import USER_SITE -HAS_USER_SITE = True - -WINDOWS_SCHEME = { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', -} - -INSTALL_SCHEMES = { - 'unix_prefix': { - 'purelib': '$base/lib/python$py_version_short/site-packages', - 'platlib': '$platbase/$platlibdir/python$py_version_short/site-packages', - 'headers': '$base/include/python$py_version_short$abiflags/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', - }, - 'unix_home': { - 'purelib': '$base/lib/python', - 'platlib': '$base/$platlibdir/python', - 'headers': '$base/include/python/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', - }, - 'nt': WINDOWS_SCHEME, - } - -# user site schemes -if HAS_USER_SITE: - INSTALL_SCHEMES['nt_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', - 'scripts': '$userbase/Python$py_version_nodot/Scripts', - 'data' : '$userbase', - } - - INSTALL_SCHEMES['unix_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': - '$userbase/include/python$py_version_short$abiflags/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - } - -# The keys to an installation scheme; if any new types of files are to be -# installed, be sure to add an entry to every installation scheme above, -# and to SCHEME_KEYS here. -SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') - - -class install(Command): - - description = "install everything from build directory" - - user_options = [ - # Select installation scheme and set base director(y|ies) - ('prefix=', None, - "installation prefix"), - ('exec-prefix=', None, - "(Unix only) prefix for platform-specific files"), - ('home=', None, - "(Unix only) home directory to install under"), - - # Or, just set the base director(y|ies) - ('install-base=', None, - "base installation directory (instead of --prefix or --home)"), - ('install-platbase=', None, - "base installation directory for platform-specific files " + - "(instead of --exec-prefix or --home)"), - ('root=', None, - "install everything relative to this alternate root directory"), - - # Or, explicitly set the installation scheme - ('install-purelib=', None, - "installation directory for pure Python module distributions"), - ('install-platlib=', None, - "installation directory for non-pure module distributions"), - ('install-lib=', None, - "installation directory for all module distributions " + - "(overrides --install-purelib and --install-platlib)"), - - ('install-headers=', None, - "installation directory for C/C++ headers"), - ('install-scripts=', None, - "installation directory for Python scripts"), - ('install-data=', None, - "installation directory for data files"), - - # Byte-compilation options -- see install_lib.py for details, as - # these are duplicated from there (but only install_lib does - # anything with them). - ('compile', 'c', "compile .py to .pyc [default]"), - ('no-compile', None, "don't compile .py files"), - ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), - - # Miscellaneous control options - ('force', 'f', - "force installation (overwrite any existing files)"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - - # Where to install documentation (eventually!) - #('doc-format=', None, "format of documentation to generate"), - #('install-man=', None, "directory for Unix man pages"), - #('install-html=', None, "directory for HTML documentation"), - #('install-info=', None, "directory for GNU info files"), - - ('record=', None, - "filename in which to record list of installed files"), - ] - - boolean_options = ['compile', 'force', 'skip-build'] - - if HAS_USER_SITE: - user_options.append(('user', None, - "install in user site-package '%s'" % USER_SITE)) - boolean_options.append('user') - - negative_opt = {'no-compile' : 'compile'} - - - def initialize_options(self): - """Initializes options.""" - # High-level options: these select both an installation base - # and scheme. - self.prefix = None - self.exec_prefix = None - self.home = None - self.user = 0 - - # These select only the installation base; it's up to the user to - # specify the installation scheme (currently, that means supplying - # the --install-{platlib,purelib,scripts,data} options). - self.install_base = None - self.install_platbase = None - self.root = None - - # These options are the actual installation directories; if not - # supplied by the user, they are filled in using the installation - # scheme implied by prefix/exec-prefix/home and the contents of - # that installation scheme. - self.install_purelib = None # for pure module distributions - self.install_platlib = None # non-pure (dists w/ extensions) - self.install_headers = None # for C/C++ headers - self.install_lib = None # set to either purelib or platlib - self.install_scripts = None - self.install_data = None - self.install_userbase = USER_BASE - self.install_usersite = USER_SITE - - self.compile = None - self.optimize = None - - # Deprecated - # These two are for putting non-packagized distributions into their - # own directory and creating a .pth file if it makes sense. - # 'extra_path' comes from the setup file; 'install_path_file' can - # be turned off if it makes no sense to install a .pth file. (But - # better to install it uselessly than to guess wrong and not - # install it when it's necessary and would be used!) Currently, - # 'install_path_file' is always true unless some outsider meddles - # with it. - self.extra_path = None - self.install_path_file = 1 - - # 'force' forces installation, even if target files are not - # out-of-date. 'skip_build' skips running the "build" command, - # handy if you know it's not necessary. 'warn_dir' (which is *not* - # a user option, it's just there so the bdist_* commands can turn - # it off) determines whether we warn about installing to a - # directory not in sys.path. - self.force = 0 - self.skip_build = 0 - self.warn_dir = 1 - - # These are only here as a conduit from the 'build' command to the - # 'install_*' commands that do the real work. ('build_base' isn't - # actually used anywhere, but it might be useful in future.) They - # are not user options, because if the user told the install - # command where the build directory is, that wouldn't affect the - # build command. - self.build_base = None - self.build_lib = None - - # Not defined yet because we don't know anything about - # documentation yet. - #self.install_man = None - #self.install_html = None - #self.install_info = None - - self.record = None - - - # -- Option finalizing methods ------------------------------------- - # (This is rather more involved than for most commands, - # because this is where the policy for installing third- - # party Python modules on various platforms given a wide - # array of user input is decided. Yes, it's quite complex!) - - def finalize_options(self): - """Finalizes options.""" - # This method (and its helpers, like 'finalize_unix()', - # 'finalize_other()', and 'select_scheme()') is where the default - # installation directories for modules, extension modules, and - # anything else we care to install from a Python module - # distribution. Thus, this code makes a pretty important policy - # statement about how third-party stuff is added to a Python - # installation! Note that the actual work of installation is done - # by the relatively simple 'install_*' commands; they just take - # their orders from the installation directory options determined - # here. - - # Check for errors/inconsistencies in the options; first, stuff - # that's wrong on any platform. - - if ((self.prefix or self.exec_prefix or self.home) and - (self.install_base or self.install_platbase)): - raise DistutilsOptionError( - "must supply either prefix/exec-prefix/home or " + - "install-base/install-platbase -- not both") - - if self.home and (self.prefix or self.exec_prefix): - raise DistutilsOptionError( - "must supply either home or prefix/exec-prefix -- not both") - - if self.user and (self.prefix or self.exec_prefix or self.home or - self.install_base or self.install_platbase): - raise DistutilsOptionError("can't combine user with prefix, " - "exec_prefix/home, or install_(plat)base") - - # Next, stuff that's wrong (or dubious) only on certain platforms. - if os.name != "posix": - if self.exec_prefix: - self.warn("exec-prefix option ignored on this platform") - self.exec_prefix = None - - # Now the interesting logic -- so interesting that we farm it out - # to other methods. The goal of these methods is to set the final - # values for the install_{lib,scripts,data,...} options, using as - # input a heady brew of prefix, exec_prefix, home, install_base, - # install_platbase, user-supplied versions of - # install_{purelib,platlib,lib,scripts,data,...}, and the - # INSTALL_SCHEME dictionary above. Phew! - - self.dump_dirs("pre-finalize_{unix,other}") - - if os.name == 'posix': - self.finalize_unix() - else: - self.finalize_other() - - self.dump_dirs("post-finalize_{unix,other}()") - - # Expand configuration variables, tilde, etc. in self.install_base - # and self.install_platbase -- that way, we can use $base or - # $platbase in the other installation directories and not worry - # about needing recursive variable expansion (shudder). - - py_version = sys.version.split()[0] - (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') - try: - abiflags = sys.abiflags - except AttributeError: - # sys.abiflags may not be defined on all platforms. - abiflags = '' - self.config_vars = {'dist_name': self.distribution.get_name(), - 'dist_version': self.distribution.get_version(), - 'dist_fullname': self.distribution.get_fullname(), - 'py_version': py_version, - 'py_version_short': '%d.%d' % sys.version_info[:2], - 'py_version_nodot': '%d%d' % sys.version_info[:2], - 'sys_prefix': prefix, - 'prefix': prefix, - 'sys_exec_prefix': exec_prefix, - 'exec_prefix': exec_prefix, - 'abiflags': abiflags, - 'platlibdir': sys.platlibdir, - } - - if HAS_USER_SITE: - self.config_vars['userbase'] = self.install_userbase - self.config_vars['usersite'] = self.install_usersite - - self.expand_basedirs() - - self.dump_dirs("post-expand_basedirs()") - - # Now define config vars for the base directories so we can expand - # everything else. - self.config_vars['base'] = self.install_base - self.config_vars['platbase'] = self.install_platbase - - if DEBUG: - from pprint import pprint - print("config vars:") - pprint(self.config_vars) - - # Expand "~" and configuration variables in the installation - # directories. - self.expand_dirs() - - self.dump_dirs("post-expand_dirs()") - - # Create directories in the home dir: - if self.user: - self.create_home_path() - - # Pick the actual directory to install all modules to: either - # install_purelib or install_platlib, depending on whether this - # module distribution is pure or not. Of course, if the user - # already specified install_lib, use their selection. - if self.install_lib is None: - if self.distribution.ext_modules: # has extensions: non-pure - self.install_lib = self.install_platlib - else: - self.install_lib = self.install_purelib - - - # Convert directories from Unix /-separated syntax to the local - # convention. - self.convert_paths('lib', 'purelib', 'platlib', - 'scripts', 'data', 'headers', - 'userbase', 'usersite') - - # Deprecated - # Well, we're not actually fully completely finalized yet: we still - # have to deal with 'extra_path', which is the hack for allowing - # non-packagized module distributions (hello, Numerical Python!) to - # get their own directories. - self.handle_extra_path() - self.install_libbase = self.install_lib # needed for .pth file - self.install_lib = os.path.join(self.install_lib, self.extra_dirs) - - # If a new root directory was supplied, make all the installation - # dirs relative to it. - if self.root is not None: - self.change_roots('libbase', 'lib', 'purelib', 'platlib', - 'scripts', 'data', 'headers') - - self.dump_dirs("after prepending root") - - # Find out the build directories, ie. where to install from. - self.set_undefined_options('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib')) - - # Punt on doc directories for now -- after all, we're punting on - # documentation completely! - - def dump_dirs(self, msg): - """Dumps the list of user options.""" - if not DEBUG: - return - from distutils.fancy_getopt import longopt_xlate - log.debug(msg + ":") - for opt in self.user_options: - opt_name = opt[0] - if opt_name[-1] == "=": - opt_name = opt_name[0:-1] - if opt_name in self.negative_opt: - opt_name = self.negative_opt[opt_name] - opt_name = opt_name.translate(longopt_xlate) - val = not getattr(self, opt_name) - else: - opt_name = opt_name.translate(longopt_xlate) - val = getattr(self, opt_name) - log.debug(" %s: %s", opt_name, val) - - def finalize_unix(self): - """Finalizes options for posix platforms.""" - if self.install_base is not None or self.install_platbase is not None: - if ((self.install_lib is None and - self.install_purelib is None and - self.install_platlib is None) or - self.install_headers is None or - self.install_scripts is None or - self.install_data is None): - raise DistutilsOptionError( - "install-base or install-platbase supplied, but " - "installation scheme is incomplete") - return - - if self.user: - if self.install_userbase is None: - raise DistutilsPlatformError( - "User base directory is not specified") - self.install_base = self.install_platbase = self.install_userbase - self.select_scheme("unix_user") - elif self.home is not None: - self.install_base = self.install_platbase = self.home - self.select_scheme("unix_home") - else: - if self.prefix is None: - if self.exec_prefix is not None: - raise DistutilsOptionError( - "must not supply exec-prefix without prefix") - - self.prefix = os.path.normpath(sys.prefix) - self.exec_prefix = os.path.normpath(sys.exec_prefix) - - else: - if self.exec_prefix is None: - self.exec_prefix = self.prefix - - self.install_base = self.prefix - self.install_platbase = self.exec_prefix - self.select_scheme("unix_prefix") - - def finalize_other(self): - """Finalizes options for non-posix platforms""" - if self.user: - if self.install_userbase is None: - raise DistutilsPlatformError( - "User base directory is not specified") - self.install_base = self.install_platbase = self.install_userbase - self.select_scheme(os.name + "_user") - elif self.home is not None: - self.install_base = self.install_platbase = self.home - self.select_scheme("unix_home") - else: - if self.prefix is None: - self.prefix = os.path.normpath(sys.prefix) - - self.install_base = self.install_platbase = self.prefix - try: - self.select_scheme(os.name) - except KeyError: - raise DistutilsPlatformError( - "I don't know how to install stuff on '%s'" % os.name) - - def select_scheme(self, name): - """Sets the install directories by applying the install schemes.""" - # it's the caller's problem if they supply a bad name! - scheme = INSTALL_SCHEMES[name] - for key in SCHEME_KEYS: - attrname = 'install_' + key - if getattr(self, attrname) is None: - setattr(self, attrname, scheme[key]) - - def _expand_attrs(self, attrs): - for attr in attrs: - val = getattr(self, attr) - if val is not None: - if os.name == 'posix' or os.name == 'nt': - val = os.path.expanduser(val) - val = subst_vars(val, self.config_vars) - setattr(self, attr, val) - - def expand_basedirs(self): - """Calls `os.path.expanduser` on install_base, install_platbase and - root.""" - self._expand_attrs(['install_base', 'install_platbase', 'root']) - - def expand_dirs(self): - """Calls `os.path.expanduser` on install dirs.""" - self._expand_attrs(['install_purelib', 'install_platlib', - 'install_lib', 'install_headers', - 'install_scripts', 'install_data',]) - - def convert_paths(self, *names): - """Call `convert_path` over `names`.""" - for name in names: - attr = "install_" + name - setattr(self, attr, convert_path(getattr(self, attr))) - - def handle_extra_path(self): - """Set `path_file` and `extra_dirs` using `extra_path`.""" - if self.extra_path is None: - self.extra_path = self.distribution.extra_path - - if self.extra_path is not None: - log.warn( - "Distribution option extra_path is deprecated. " - "See issue27919 for details." - ) - if isinstance(self.extra_path, str): - self.extra_path = self.extra_path.split(',') - - if len(self.extra_path) == 1: - path_file = extra_dirs = self.extra_path[0] - elif len(self.extra_path) == 2: - path_file, extra_dirs = self.extra_path - else: - raise DistutilsOptionError( - "'extra_path' option must be a list, tuple, or " - "comma-separated string with 1 or 2 elements") - - # convert to local form in case Unix notation used (as it - # should be in setup scripts) - extra_dirs = convert_path(extra_dirs) - else: - path_file = None - extra_dirs = '' - - # XXX should we warn if path_file and not extra_dirs? (in which - # case the path file would be harmless but pointless) - self.path_file = path_file - self.extra_dirs = extra_dirs - - def change_roots(self, *names): - """Change the install directories pointed by name using root.""" - for name in names: - attr = "install_" + name - setattr(self, attr, change_root(self.root, getattr(self, attr))) - - def create_home_path(self): - """Create directories under ~.""" - if not self.user: - return - home = convert_path(os.path.expanduser("~")) - for name, path in self.config_vars.items(): - if path.startswith(home) and not os.path.isdir(path): - self.debug_print("os.makedirs('%s', 0o700)" % path) - os.makedirs(path, 0o700) - - # -- Command execution methods ------------------------------------- - - def run(self): - """Runs the command.""" - # Obviously have to build before we can install - if not self.skip_build: - self.run_command('build') - # If we built for any other platform, we can't install. - build_plat = self.distribution.get_command_obj('build').plat_name - # check warn_dir - it is a clue that the 'install' is happening - # internally, and not to sys.path, so we don't check the platform - # matches what we are running. - if self.warn_dir and build_plat != get_platform(): - raise DistutilsPlatformError("Can't install when " - "cross-compiling") - - # Run all sub-commands (at least those that need to be run) - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) - - if self.path_file: - self.create_path_file() - - # write list of installed files, if requested. - if self.record: - outputs = self.get_outputs() - if self.root: # strip any package prefix - root_len = len(self.root) - for counter in range(len(outputs)): - outputs[counter] = outputs[counter][root_len:] - self.execute(write_file, - (self.record, outputs), - "writing list of installed files to '%s'" % - self.record) - - sys_path = map(os.path.normpath, sys.path) - sys_path = map(os.path.normcase, sys_path) - install_lib = os.path.normcase(os.path.normpath(self.install_lib)) - if (self.warn_dir and - not (self.path_file and self.install_path_file) and - install_lib not in sys_path): - log.debug(("modules installed to '%s', which is not in " - "Python's module search path (sys.path) -- " - "you'll have to change the search path yourself"), - self.install_lib) - - def create_path_file(self): - """Creates the .pth file""" - filename = os.path.join(self.install_libbase, - self.path_file + ".pth") - if self.install_path_file: - self.execute(write_file, - (filename, [self.extra_dirs]), - "creating %s" % filename) - else: - self.warn("path file '%s' not created" % filename) - - - # -- Reporting methods --------------------------------------------- - - def get_outputs(self): - """Assembles the outputs of all the sub-commands.""" - outputs = [] - for cmd_name in self.get_sub_commands(): - cmd = self.get_finalized_command(cmd_name) - # Add the contents of cmd.get_outputs(), ensuring - # that outputs doesn't contain duplicate entries - for filename in cmd.get_outputs(): - if filename not in outputs: - outputs.append(filename) - - if self.path_file and self.install_path_file: - outputs.append(os.path.join(self.install_libbase, - self.path_file + ".pth")) - - return outputs - - def get_inputs(self): - """Returns the inputs of all the sub-commands""" - # XXX gee, this looks familiar ;-( - inputs = [] - for cmd_name in self.get_sub_commands(): - cmd = self.get_finalized_command(cmd_name) - inputs.extend(cmd.get_inputs()) - - return inputs - - # -- Predicates for sub-command list ------------------------------- - - def has_lib(self): - """Returns true if the current distribution has any Python - modules to install.""" - return (self.distribution.has_pure_modules() or - self.distribution.has_ext_modules()) - - def has_headers(self): - """Returns true if the current distribution has any headers to - install.""" - return self.distribution.has_headers() - - def has_scripts(self): - """Returns true if the current distribution has any scripts to. - install.""" - return self.distribution.has_scripts() - - def has_data(self): - """Returns true if the current distribution has any data to. - install.""" - return self.distribution.has_data_files() - - # 'sub_commands': a list of commands this command might have to run to - # get its work done. See cmd.py for more info. - sub_commands = [('install_lib', has_lib), - ('install_headers', has_headers), - ('install_scripts', has_scripts), - ('install_data', has_data), - ('install_egg_info', lambda self:True), - ] diff --git a/command/install_data.py b/command/install_data.py deleted file mode 100644 index 947cd76a..00000000 --- a/command/install_data.py +++ /dev/null @@ -1,79 +0,0 @@ -"""distutils.command.install_data - -Implements the Distutils 'install_data' command, for installing -platform-independent data files.""" - -# contributed by Bastian Kleineidam - -import os -from distutils.core import Command -from distutils.util import change_root, convert_path - -class install_data(Command): - - description = "install data files" - - user_options = [ - ('install-dir=', 'd', - "base directory for installing data files " - "(default: installation base dir)"), - ('root=', None, - "install everything relative to this alternate root directory"), - ('force', 'f', "force installation (overwrite existing files)"), - ] - - boolean_options = ['force'] - - def initialize_options(self): - self.install_dir = None - self.outfiles = [] - self.root = None - self.force = 0 - self.data_files = self.distribution.data_files - self.warn_dir = 1 - - def finalize_options(self): - self.set_undefined_options('install', - ('install_data', 'install_dir'), - ('root', 'root'), - ('force', 'force'), - ) - - def run(self): - self.mkpath(self.install_dir) - for f in self.data_files: - if isinstance(f, str): - # it's a simple file, so copy it - f = convert_path(f) - if self.warn_dir: - self.warn("setup script did not provide a directory for " - "'%s' -- installing right in '%s'" % - (f, self.install_dir)) - (out, _) = self.copy_file(f, self.install_dir) - self.outfiles.append(out) - else: - # it's a tuple with path to install to and a list of files - dir = convert_path(f[0]) - if not os.path.isabs(dir): - dir = os.path.join(self.install_dir, dir) - elif self.root: - dir = change_root(self.root, dir) - self.mkpath(dir) - - if f[1] == []: - # If there are no files listed, the user must be - # trying to create an empty directory, so add the - # directory to the list of output files. - self.outfiles.append(dir) - else: - # Copy files, adding them to the list of output files. - for data in f[1]: - data = convert_path(data) - (out, _) = self.copy_file(data, dir) - self.outfiles.append(out) - - def get_inputs(self): - return self.data_files or [] - - def get_outputs(self): - return self.outfiles diff --git a/command/install_egg_info.py b/command/install_egg_info.py deleted file mode 100644 index 0ddc7367..00000000 --- a/command/install_egg_info.py +++ /dev/null @@ -1,77 +0,0 @@ -"""distutils.command.install_egg_info - -Implements the Distutils 'install_egg_info' command, for installing -a package's PKG-INFO metadata.""" - - -from distutils.cmd import Command -from distutils import log, dir_util -import os, sys, re - -class install_egg_info(Command): - """Install an .egg-info file for the package""" - - description = "Install package's PKG-INFO metadata as an .egg-info file" - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ] - - def initialize_options(self): - self.install_dir = None - - def finalize_options(self): - self.set_undefined_options('install_lib',('install_dir','install_dir')) - basename = "%s-%s-py%d.%d.egg-info" % ( - to_filename(safe_name(self.distribution.get_name())), - to_filename(safe_version(self.distribution.get_version())), - *sys.version_info[:2] - ) - self.target = os.path.join(self.install_dir, basename) - self.outputs = [self.target] - - def run(self): - target = self.target - if os.path.isdir(target) and not os.path.islink(target): - dir_util.remove_tree(target, dry_run=self.dry_run) - elif os.path.exists(target): - self.execute(os.unlink,(self.target,),"Removing "+target) - elif not os.path.isdir(self.install_dir): - self.execute(os.makedirs, (self.install_dir,), - "Creating "+self.install_dir) - log.info("Writing %s", target) - if not self.dry_run: - with open(target, 'w', encoding='UTF-8') as f: - self.distribution.metadata.write_pkg_file(f) - - def get_outputs(self): - return self.outputs - - -# The following routines are taken from setuptools' pkg_resources module and -# can be replaced by importing them from pkg_resources once it is included -# in the stdlib. - -def safe_name(name): - """Convert an arbitrary string to a standard distribution name - - Any runs of non-alphanumeric/. characters are replaced with a single '-'. - """ - return re.sub('[^A-Za-z0-9.]+', '-', name) - - -def safe_version(version): - """Convert an arbitrary string to a standard version string - - Spaces become dots, and all other non-alphanumeric characters become - dashes, with runs of multiple dashes condensed to a single dash. - """ - version = version.replace(' ','.') - return re.sub('[^A-Za-z0-9.]+', '-', version) - - -def to_filename(name): - """Convert a project or version name to its filename-escaped form - - Any '-' characters are currently replaced with '_'. - """ - return name.replace('-','_') diff --git a/command/install_headers.py b/command/install_headers.py deleted file mode 100644 index 9bb0b18d..00000000 --- a/command/install_headers.py +++ /dev/null @@ -1,47 +0,0 @@ -"""distutils.command.install_headers - -Implements the Distutils 'install_headers' command, to install C/C++ header -files to the Python include directory.""" - -from distutils.core import Command - - -# XXX force is never used -class install_headers(Command): - - description = "install C/C++ header files" - - user_options = [('install-dir=', 'd', - "directory to install header files to"), - ('force', 'f', - "force installation (overwrite existing files)"), - ] - - boolean_options = ['force'] - - def initialize_options(self): - self.install_dir = None - self.force = 0 - self.outfiles = [] - - def finalize_options(self): - self.set_undefined_options('install', - ('install_headers', 'install_dir'), - ('force', 'force')) - - - def run(self): - headers = self.distribution.headers - if not headers: - return - - self.mkpath(self.install_dir) - for header in headers: - (out, _) = self.copy_file(header, self.install_dir) - self.outfiles.append(out) - - def get_inputs(self): - return self.distribution.headers or [] - - def get_outputs(self): - return self.outfiles diff --git a/command/install_lib.py b/command/install_lib.py deleted file mode 100644 index 6154cf09..00000000 --- a/command/install_lib.py +++ /dev/null @@ -1,217 +0,0 @@ -"""distutils.command.install_lib - -Implements the Distutils 'install_lib' command -(install all Python modules).""" - -import os -import importlib.util -import sys - -from distutils.core import Command -from distutils.errors import DistutilsOptionError - - -# Extension for Python source files. -PYTHON_SOURCE_EXTENSION = ".py" - -class install_lib(Command): - - description = "install all Python modules (extensions and pure Python)" - - # The byte-compilation options are a tad confusing. Here are the - # possible scenarios: - # 1) no compilation at all (--no-compile --no-optimize) - # 2) compile .pyc only (--compile --no-optimize; default) - # 3) compile .pyc and "opt-1" .pyc (--compile --optimize) - # 4) compile "opt-1" .pyc only (--no-compile --optimize) - # 5) compile .pyc and "opt-2" .pyc (--compile --optimize-more) - # 6) compile "opt-2" .pyc only (--no-compile --optimize-more) - # - # The UI for this is two options, 'compile' and 'optimize'. - # 'compile' is strictly boolean, and only decides whether to - # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and - # decides both whether to generate .pyc files and what level of - # optimization to use. - - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ('build-dir=','b', "build directory (where to install from)"), - ('force', 'f', "force installation (overwrite existing files)"), - ('compile', 'c', "compile .py to .pyc [default]"), - ('no-compile', None, "don't compile .py files"), - ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), - ('skip-build', None, "skip the build steps"), - ] - - boolean_options = ['force', 'compile', 'skip-build'] - negative_opt = {'no-compile' : 'compile'} - - def initialize_options(self): - # let the 'install' command dictate our installation directory - self.install_dir = None - self.build_dir = None - self.force = 0 - self.compile = None - self.optimize = None - self.skip_build = None - - def finalize_options(self): - # Get all the information we need to install pure Python modules - # from the umbrella 'install' command -- build (source) directory, - # install (target) directory, and whether to compile .py files. - self.set_undefined_options('install', - ('build_lib', 'build_dir'), - ('install_lib', 'install_dir'), - ('force', 'force'), - ('compile', 'compile'), - ('optimize', 'optimize'), - ('skip_build', 'skip_build'), - ) - - if self.compile is None: - self.compile = True - if self.optimize is None: - self.optimize = False - - if not isinstance(self.optimize, int): - try: - self.optimize = int(self.optimize) - if self.optimize not in (0, 1, 2): - raise AssertionError - except (ValueError, AssertionError): - raise DistutilsOptionError("optimize must be 0, 1, or 2") - - def run(self): - # Make sure we have built everything we need first - self.build() - - # Install everything: simply dump the entire contents of the build - # directory to the installation directory (that's the beauty of - # having a build directory!) - outfiles = self.install() - - # (Optionally) compile .py to .pyc - if outfiles is not None and self.distribution.has_pure_modules(): - self.byte_compile(outfiles) - - # -- Top-level worker functions ------------------------------------ - # (called from 'run()') - - def build(self): - if not self.skip_build: - if self.distribution.has_pure_modules(): - self.run_command('build_py') - if self.distribution.has_ext_modules(): - self.run_command('build_ext') - - def install(self): - if os.path.isdir(self.build_dir): - outfiles = self.copy_tree(self.build_dir, self.install_dir) - else: - self.warn("'%s' does not exist -- no Python modules to install" % - self.build_dir) - return - return outfiles - - def byte_compile(self, files): - if sys.dont_write_bytecode: - self.warn('byte-compiling is disabled, skipping.') - return - - from distutils.util import byte_compile - - # Get the "--root" directory supplied to the "install" command, - # and use it as a prefix to strip off the purported filename - # encoded in bytecode files. This is far from complete, but it - # should at least generate usable bytecode in RPM distributions. - install_root = self.get_finalized_command('install').root - - if self.compile: - byte_compile(files, optimize=0, - force=self.force, prefix=install_root, - dry_run=self.dry_run) - if self.optimize > 0: - byte_compile(files, optimize=self.optimize, - force=self.force, prefix=install_root, - verbose=self.verbose, dry_run=self.dry_run) - - - # -- Utility methods ----------------------------------------------- - - def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir): - if not has_any: - return [] - - build_cmd = self.get_finalized_command(build_cmd) - build_files = build_cmd.get_outputs() - build_dir = getattr(build_cmd, cmd_option) - - prefix_len = len(build_dir) + len(os.sep) - outputs = [] - for file in build_files: - outputs.append(os.path.join(output_dir, file[prefix_len:])) - - return outputs - - def _bytecode_filenames(self, py_filenames): - bytecode_files = [] - for py_file in py_filenames: - # Since build_py handles package data installation, the - # list of outputs can contain more than just .py files. - # Make sure we only report bytecode for the .py files. - ext = os.path.splitext(os.path.normcase(py_file))[1] - if ext != PYTHON_SOURCE_EXTENSION: - continue - if self.compile: - bytecode_files.append(importlib.util.cache_from_source( - py_file, optimization='')) - if self.optimize > 0: - bytecode_files.append(importlib.util.cache_from_source( - py_file, optimization=self.optimize)) - - return bytecode_files - - - # -- External interface -------------------------------------------- - # (called by outsiders) - - def get_outputs(self): - """Return the list of files that would be installed if this command - were actually run. Not affected by the "dry-run" flag or whether - modules have actually been built yet. - """ - pure_outputs = \ - self._mutate_outputs(self.distribution.has_pure_modules(), - 'build_py', 'build_lib', - self.install_dir) - if self.compile: - bytecode_outputs = self._bytecode_filenames(pure_outputs) - else: - bytecode_outputs = [] - - ext_outputs = \ - self._mutate_outputs(self.distribution.has_ext_modules(), - 'build_ext', 'build_lib', - self.install_dir) - - return pure_outputs + bytecode_outputs + ext_outputs - - def get_inputs(self): - """Get the list of files that are input to this command, ie. the - files that get installed as they are named in the build tree. - The files in this list correspond one-to-one to the output - filenames returned by 'get_outputs()'. - """ - inputs = [] - - if self.distribution.has_pure_modules(): - build_py = self.get_finalized_command('build_py') - inputs.extend(build_py.get_outputs()) - - if self.distribution.has_ext_modules(): - build_ext = self.get_finalized_command('build_ext') - inputs.extend(build_ext.get_outputs()) - - return inputs diff --git a/command/install_scripts.py b/command/install_scripts.py deleted file mode 100644 index 31a1130e..00000000 --- a/command/install_scripts.py +++ /dev/null @@ -1,60 +0,0 @@ -"""distutils.command.install_scripts - -Implements the Distutils 'install_scripts' command, for installing -Python scripts.""" - -# contributed by Bastian Kleineidam - -import os -from distutils.core import Command -from distutils import log -from stat import ST_MODE - - -class install_scripts(Command): - - description = "install scripts (Python or otherwise)" - - user_options = [ - ('install-dir=', 'd', "directory to install scripts to"), - ('build-dir=','b', "build directory (where to install from)"), - ('force', 'f', "force installation (overwrite existing files)"), - ('skip-build', None, "skip the build steps"), - ] - - boolean_options = ['force', 'skip-build'] - - def initialize_options(self): - self.install_dir = None - self.force = 0 - self.build_dir = None - self.skip_build = None - - def finalize_options(self): - self.set_undefined_options('build', ('build_scripts', 'build_dir')) - self.set_undefined_options('install', - ('install_scripts', 'install_dir'), - ('force', 'force'), - ('skip_build', 'skip_build'), - ) - - def run(self): - if not self.skip_build: - self.run_command('build_scripts') - self.outfiles = self.copy_tree(self.build_dir, self.install_dir) - if os.name == 'posix': - # Set the executable bits (owner, group, and world) on - # all the scripts we just installed. - for file in self.get_outputs(): - if self.dry_run: - log.info("changing mode of %s", file) - else: - mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777 - log.info("changing mode of %s to %o", file, mode) - os.chmod(file, mode) - - def get_inputs(self): - return self.distribution.scripts or [] - - def get_outputs(self): - return self.outfiles or [] diff --git a/command/register.py b/command/register.py deleted file mode 100644 index 0fac94e9..00000000 --- a/command/register.py +++ /dev/null @@ -1,304 +0,0 @@ -"""distutils.command.register - -Implements the Distutils 'register' command (register with the repository). -""" - -# created 2002/10/21, Richard Jones - -import getpass -import io -import urllib.parse, urllib.request -from warnings import warn - -from distutils.core import PyPIRCCommand -from distutils.errors import * -from distutils import log - -class register(PyPIRCCommand): - - description = ("register the distribution with the Python package index") - user_options = PyPIRCCommand.user_options + [ - ('list-classifiers', None, - 'list the valid Trove classifiers'), - ('strict', None , - 'Will stop the registering if the meta-data are not fully compliant') - ] - boolean_options = PyPIRCCommand.boolean_options + [ - 'verify', 'list-classifiers', 'strict'] - - sub_commands = [('check', lambda self: True)] - - def initialize_options(self): - PyPIRCCommand.initialize_options(self) - self.list_classifiers = 0 - self.strict = 0 - - def finalize_options(self): - PyPIRCCommand.finalize_options(self) - # setting options for the `check` subcommand - check_options = {'strict': ('register', self.strict), - 'restructuredtext': ('register', 1)} - self.distribution.command_options['check'] = check_options - - def run(self): - self.finalize_options() - self._set_config() - - # Run sub commands - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) - - if self.dry_run: - self.verify_metadata() - elif self.list_classifiers: - self.classifiers() - else: - self.send_metadata() - - def check_metadata(self): - """Deprecated API.""" - warn("distutils.command.register.check_metadata is deprecated, \ - use the check command instead", PendingDeprecationWarning) - check = self.distribution.get_command_obj('check') - check.ensure_finalized() - check.strict = self.strict - check.restructuredtext = 1 - check.run() - - def _set_config(self): - ''' Reads the configuration file and set attributes. - ''' - config = self._read_pypirc() - if config != {}: - self.username = config['username'] - self.password = config['password'] - self.repository = config['repository'] - self.realm = config['realm'] - self.has_config = True - else: - if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): - raise ValueError('%s not found in .pypirc' % self.repository) - if self.repository == 'pypi': - self.repository = self.DEFAULT_REPOSITORY - self.has_config = False - - def classifiers(self): - ''' Fetch the list of classifiers from the server. - ''' - url = self.repository+'?:action=list_classifiers' - response = urllib.request.urlopen(url) - log.info(self._read_pypi_response(response)) - - def verify_metadata(self): - ''' Send the metadata to the package index server to be checked. - ''' - # send the info to the server and report the result - (code, result) = self.post_to_server(self.build_post_data('verify')) - log.info('Server response (%s): %s', code, result) - - def send_metadata(self): - ''' Send the metadata to the package index server. - - Well, do the following: - 1. figure who the user is, and then - 2. send the data as a Basic auth'ed POST. - - First we try to read the username/password from $HOME/.pypirc, - which is a ConfigParser-formatted file with a section - [distutils] containing username and password entries (both - in clear text). Eg: - - [distutils] - index-servers = - pypi - - [pypi] - username: fred - password: sekrit - - Otherwise, to figure who the user is, we offer the user three - choices: - - 1. use existing login, - 2. register as a new user, or - 3. set the password to a random string and email the user. - - ''' - # see if we can short-cut and get the username/password from the - # config - if self.has_config: - choice = '1' - username = self.username - password = self.password - else: - choice = 'x' - username = password = '' - - # get the user's login info - choices = '1 2 3 4'.split() - while choice not in choices: - self.announce('''\ -We need to know who you are, so please choose either: - 1. use your existing login, - 2. register as a new user, - 3. have the server generate a new password for you (and email it to you), or - 4. quit -Your selection [default 1]: ''', log.INFO) - choice = input() - if not choice: - choice = '1' - elif choice not in choices: - print('Please choose one of the four options!') - - if choice == '1': - # get the username and password - while not username: - username = input('Username: ') - while not password: - password = getpass.getpass('Password: ') - - # set up the authentication - auth = urllib.request.HTTPPasswordMgr() - host = urllib.parse.urlparse(self.repository)[1] - auth.add_password(self.realm, host, username, password) - # send the info to the server and report the result - code, result = self.post_to_server(self.build_post_data('submit'), - auth) - self.announce('Server response (%s): %s' % (code, result), - log.INFO) - - # possibly save the login - if code == 200: - if self.has_config: - # sharing the password in the distribution instance - # so the upload command can reuse it - self.distribution.password = password - else: - self.announce(('I can store your PyPI login so future ' - 'submissions will be faster.'), log.INFO) - self.announce('(the login will be stored in %s)' % \ - self._get_rc_file(), log.INFO) - choice = 'X' - while choice.lower() not in 'yn': - choice = input('Save your login (y/N)?') - if not choice: - choice = 'n' - if choice.lower() == 'y': - self._store_pypirc(username, password) - - elif choice == '2': - data = {':action': 'user'} - data['name'] = data['password'] = data['email'] = '' - data['confirm'] = None - while not data['name']: - data['name'] = input('Username: ') - while data['password'] != data['confirm']: - while not data['password']: - data['password'] = getpass.getpass('Password: ') - while not data['confirm']: - data['confirm'] = getpass.getpass(' Confirm: ') - if data['password'] != data['confirm']: - data['password'] = '' - data['confirm'] = None - print("Password and confirm don't match!") - while not data['email']: - data['email'] = input(' EMail: ') - code, result = self.post_to_server(data) - if code != 200: - log.info('Server response (%s): %s', code, result) - else: - log.info('You will receive an email shortly.') - log.info(('Follow the instructions in it to ' - 'complete registration.')) - elif choice == '3': - data = {':action': 'password_reset'} - data['email'] = '' - while not data['email']: - data['email'] = input('Your email address: ') - code, result = self.post_to_server(data) - log.info('Server response (%s): %s', code, result) - - def build_post_data(self, action): - # figure the data to send - the metadata plus some additional - # information used by the package server - meta = self.distribution.metadata - data = { - ':action': action, - 'metadata_version' : '1.0', - 'name': meta.get_name(), - 'version': meta.get_version(), - 'summary': meta.get_description(), - 'home_page': meta.get_url(), - 'author': meta.get_contact(), - 'author_email': meta.get_contact_email(), - 'license': meta.get_licence(), - 'description': meta.get_long_description(), - 'keywords': meta.get_keywords(), - 'platform': meta.get_platforms(), - 'classifiers': meta.get_classifiers(), - 'download_url': meta.get_download_url(), - # PEP 314 - 'provides': meta.get_provides(), - 'requires': meta.get_requires(), - 'obsoletes': meta.get_obsoletes(), - } - if data['provides'] or data['requires'] or data['obsoletes']: - data['metadata_version'] = '1.1' - return data - - def post_to_server(self, data, auth=None): - ''' Post a query to the server, and return a string response. - ''' - if 'name' in data: - self.announce('Registering %s to %s' % (data['name'], - self.repository), - log.INFO) - # Build up the MIME payload for the urllib2 POST data - boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = '\n--' + boundary - end_boundary = sep_boundary + '--' - body = io.StringIO() - for key, value in data.items(): - # handle multiple entries for the same name - if type(value) not in (type([]), type( () )): - value = [value] - for value in value: - value = str(value) - body.write(sep_boundary) - body.write('\nContent-Disposition: form-data; name="%s"'%key) - body.write("\n\n") - body.write(value) - if value and value[-1] == '\r': - body.write('\n') # write an extra newline (lurve Macs) - body.write(end_boundary) - body.write("\n") - body = body.getvalue().encode("utf-8") - - # build the Request - headers = { - 'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary, - 'Content-length': str(len(body)) - } - req = urllib.request.Request(self.repository, body, headers) - - # handle HTTP and include the Basic Auth handler - opener = urllib.request.build_opener( - urllib.request.HTTPBasicAuthHandler(password_mgr=auth) - ) - data = '' - try: - result = opener.open(req) - except urllib.error.HTTPError as e: - if self.show_response: - data = e.fp.read() - result = e.code, e.msg - except urllib.error.URLError as e: - result = 500, str(e) - else: - if self.show_response: - data = self._read_pypi_response(result) - result = 200, 'OK' - if self.show_response: - msg = '\n'.join(('-' * 75, data, '-' * 75)) - self.announce(msg, log.INFO) - return result diff --git a/command/sdist.py b/command/sdist.py deleted file mode 100644 index b4996fcb..00000000 --- a/command/sdist.py +++ /dev/null @@ -1,494 +0,0 @@ -"""distutils.command.sdist - -Implements the Distutils 'sdist' command (create a source distribution).""" - -import os -import sys -from glob import glob -from warnings import warn - -from distutils.core import Command -from distutils import dir_util -from distutils import file_util -from distutils import archive_util -from distutils.text_file import TextFile -from distutils.filelist import FileList -from distutils import log -from distutils.util import convert_path -from distutils.errors import DistutilsTemplateError, DistutilsOptionError - - -def show_formats(): - """Print all possible values for the 'formats' option (used by - the "--help-formats" command-line option). - """ - from distutils.fancy_getopt import FancyGetopt - from distutils.archive_util import ARCHIVE_FORMATS - formats = [] - for format in ARCHIVE_FORMATS.keys(): - formats.append(("formats=" + format, None, - ARCHIVE_FORMATS[format][2])) - formats.sort() - FancyGetopt(formats).print_help( - "List of available source distribution formats:") - - -class sdist(Command): - - description = "create a source distribution (tarball, zip file, etc.)" - - def checking_metadata(self): - """Callable used for the check sub-command. - - Placed here so user_options can view it""" - return self.metadata_check - - user_options = [ - ('template=', 't', - "name of manifest template file [default: MANIFEST.in]"), - ('manifest=', 'm', - "name of manifest file [default: MANIFEST]"), - ('use-defaults', None, - "include the default file set in the manifest " - "[default; disable with --no-defaults]"), - ('no-defaults', None, - "don't include the default file set"), - ('prune', None, - "specifically exclude files/directories that should not be " - "distributed (build tree, RCS/CVS dirs, etc.) " - "[default; disable with --no-prune]"), - ('no-prune', None, - "don't automatically exclude anything"), - ('manifest-only', 'o', - "just regenerate the manifest and then stop " - "(implies --force-manifest)"), - ('force-manifest', 'f', - "forcibly regenerate the manifest and carry on as usual. " - "Deprecated: now the manifest is always regenerated."), - ('formats=', None, - "formats for source distribution (comma-separated list)"), - ('keep-temp', 'k', - "keep the distribution tree around after creating " + - "archive file(s)"), - ('dist-dir=', 'd', - "directory to put the source distribution archive(s) in " - "[default: dist]"), - ('metadata-check', None, - "Ensure that all required elements of meta-data " - "are supplied. Warn if any missing. [default]"), - ('owner=', 'u', - "Owner name used when creating a tar file [default: current user]"), - ('group=', 'g', - "Group name used when creating a tar file [default: current group]"), - ] - - boolean_options = ['use-defaults', 'prune', - 'manifest-only', 'force-manifest', - 'keep-temp', 'metadata-check'] - - help_options = [ - ('help-formats', None, - "list available distribution formats", show_formats), - ] - - negative_opt = {'no-defaults': 'use-defaults', - 'no-prune': 'prune' } - - sub_commands = [('check', checking_metadata)] - - READMES = ('README', 'README.txt', 'README.rst') - - def initialize_options(self): - # 'template' and 'manifest' are, respectively, the names of - # the manifest template and manifest file. - self.template = None - self.manifest = None - - # 'use_defaults': if true, we will include the default file set - # in the manifest - self.use_defaults = 1 - self.prune = 1 - - self.manifest_only = 0 - self.force_manifest = 0 - - self.formats = ['gztar'] - self.keep_temp = 0 - self.dist_dir = None - - self.archive_files = None - self.metadata_check = 1 - self.owner = None - self.group = None - - def finalize_options(self): - if self.manifest is None: - self.manifest = "MANIFEST" - if self.template is None: - self.template = "MANIFEST.in" - - self.ensure_string_list('formats') - - bad_format = archive_util.check_archive_formats(self.formats) - if bad_format: - raise DistutilsOptionError( - "unknown archive format '%s'" % bad_format) - - if self.dist_dir is None: - self.dist_dir = "dist" - - def run(self): - # 'filelist' contains the list of files that will make up the - # manifest - self.filelist = FileList() - - # Run sub commands - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) - - # Do whatever it takes to get the list of files to process - # (process the manifest template, read an existing manifest, - # whatever). File list is accumulated in 'self.filelist'. - self.get_file_list() - - # If user just wanted us to regenerate the manifest, stop now. - if self.manifest_only: - return - - # Otherwise, go ahead and create the source distribution tarball, - # or zipfile, or whatever. - self.make_distribution() - - def check_metadata(self): - """Deprecated API.""" - warn("distutils.command.sdist.check_metadata is deprecated, \ - use the check command instead", PendingDeprecationWarning) - check = self.distribution.get_command_obj('check') - check.ensure_finalized() - check.run() - - def get_file_list(self): - """Figure out the list of files to include in the source - distribution, and put it in 'self.filelist'. This might involve - reading the manifest template (and writing the manifest), or just - reading the manifest, or just using the default file set -- it all - depends on the user's options. - """ - # new behavior when using a template: - # the file list is recalculated every time because - # even if MANIFEST.in or setup.py are not changed - # the user might have added some files in the tree that - # need to be included. - # - # This makes --force the default and only behavior with templates. - template_exists = os.path.isfile(self.template) - if not template_exists and self._manifest_is_not_generated(): - self.read_manifest() - self.filelist.sort() - self.filelist.remove_duplicates() - return - - if not template_exists: - self.warn(("manifest template '%s' does not exist " + - "(using default file list)") % - self.template) - self.filelist.findall() - - if self.use_defaults: - self.add_defaults() - - if template_exists: - self.read_template() - - if self.prune: - self.prune_file_list() - - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() - - def add_defaults(self): - """Add all the default files to self.filelist: - - README or README.txt - - setup.py - - test/test*.py - - all pure Python modules mentioned in setup script - - all files pointed by package_data (build_py) - - all files defined in data_files. - - all files defined as scripts. - - all C sources listed as part of extensions or C libraries - in the setup script (doesn't catch C headers!) - Warns if (README or README.txt) or setup.py are missing; everything - else is optional. - """ - self._add_defaults_standards() - self._add_defaults_optional() - self._add_defaults_python() - self._add_defaults_data_files() - self._add_defaults_ext() - self._add_defaults_c_libs() - self._add_defaults_scripts() - - @staticmethod - def _cs_path_exists(fspath): - """ - Case-sensitive path existence check - - >>> sdist._cs_path_exists(__file__) - True - >>> sdist._cs_path_exists(__file__.upper()) - False - """ - if not os.path.exists(fspath): - return False - # make absolute so we always have a directory - abspath = os.path.abspath(fspath) - directory, filename = os.path.split(abspath) - return filename in os.listdir(directory) - - def _add_defaults_standards(self): - standards = [self.READMES, self.distribution.script_name] - for fn in standards: - if isinstance(fn, tuple): - alts = fn - got_it = False - for fn in alts: - if self._cs_path_exists(fn): - got_it = True - self.filelist.append(fn) - break - - if not got_it: - self.warn("standard file not found: should have one of " + - ', '.join(alts)) - else: - if self._cs_path_exists(fn): - self.filelist.append(fn) - else: - self.warn("standard file '%s' not found" % fn) - - def _add_defaults_optional(self): - optional = ['test/test*.py', 'setup.cfg'] - for pattern in optional: - files = filter(os.path.isfile, glob(pattern)) - self.filelist.extend(files) - - def _add_defaults_python(self): - # build_py is used to get: - # - python modules - # - files defined in package_data - build_py = self.get_finalized_command('build_py') - - # getting python files - if self.distribution.has_pure_modules(): - self.filelist.extend(build_py.get_source_files()) - - # getting package_data files - # (computed in build_py.data_files by build_py.finalize_options) - for pkg, src_dir, build_dir, filenames in build_py.data_files: - for filename in filenames: - self.filelist.append(os.path.join(src_dir, filename)) - - def _add_defaults_data_files(self): - # getting distribution.data_files - if self.distribution.has_data_files(): - for item in self.distribution.data_files: - if isinstance(item, str): - # plain file - item = convert_path(item) - if os.path.isfile(item): - self.filelist.append(item) - else: - # a (dirname, filenames) tuple - dirname, filenames = item - for f in filenames: - f = convert_path(f) - if os.path.isfile(f): - self.filelist.append(f) - - def _add_defaults_ext(self): - if self.distribution.has_ext_modules(): - build_ext = self.get_finalized_command('build_ext') - self.filelist.extend(build_ext.get_source_files()) - - def _add_defaults_c_libs(self): - if self.distribution.has_c_libraries(): - build_clib = self.get_finalized_command('build_clib') - self.filelist.extend(build_clib.get_source_files()) - - def _add_defaults_scripts(self): - if self.distribution.has_scripts(): - build_scripts = self.get_finalized_command('build_scripts') - self.filelist.extend(build_scripts.get_source_files()) - - def read_template(self): - """Read and parse manifest template file named by self.template. - - (usually "MANIFEST.in") The parsing and processing is done by - 'self.filelist', which updates itself accordingly. - """ - log.info("reading manifest template '%s'", self.template) - template = TextFile(self.template, strip_comments=1, skip_blanks=1, - join_lines=1, lstrip_ws=1, rstrip_ws=1, - collapse_join=1) - - try: - while True: - line = template.readline() - if line is None: # end of file - break - - try: - self.filelist.process_template_line(line) - # the call above can raise a DistutilsTemplateError for - # malformed lines, or a ValueError from the lower-level - # convert_path function - except (DistutilsTemplateError, ValueError) as msg: - self.warn("%s, line %d: %s" % (template.filename, - template.current_line, - msg)) - finally: - template.close() - - def prune_file_list(self): - """Prune off branches that might slip into the file list as created - by 'read_template()', but really don't belong there: - * the build tree (typically "build") - * the release tree itself (only an issue if we ran "sdist" - previously with --keep-temp, or it aborted) - * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories - """ - build = self.get_finalized_command('build') - base_dir = self.distribution.get_fullname() - - self.filelist.exclude_pattern(None, prefix=build.build_base) - self.filelist.exclude_pattern(None, prefix=base_dir) - - if sys.platform == 'win32': - seps = r'/|\\' - else: - seps = '/' - - vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', - '_darcs'] - vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) - self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) - - def write_manifest(self): - """Write the file list in 'self.filelist' (presumably as filled in - by 'add_defaults()' and 'read_template()') to the manifest file - named by 'self.manifest'. - """ - if self._manifest_is_not_generated(): - log.info("not writing to manually maintained " - "manifest file '%s'" % self.manifest) - return - - content = self.filelist.files[:] - content.insert(0, '# file GENERATED by distutils, do NOT edit') - self.execute(file_util.write_file, (self.manifest, content), - "writing manifest file '%s'" % self.manifest) - - def _manifest_is_not_generated(self): - # check for special comment used in 3.1.3 and higher - if not os.path.isfile(self.manifest): - return False - - fp = open(self.manifest) - try: - first_line = fp.readline() - finally: - fp.close() - return first_line != '# file GENERATED by distutils, do NOT edit\n' - - def read_manifest(self): - """Read the manifest file (named by 'self.manifest') and use it to - fill in 'self.filelist', the list of files to include in the source - distribution. - """ - log.info("reading manifest file '%s'", self.manifest) - with open(self.manifest) as manifest: - for line in manifest: - # ignore comments and blank lines - line = line.strip() - if line.startswith('#') or not line: - continue - self.filelist.append(line) - - def make_release_tree(self, base_dir, files): - """Create the directory tree that will become the source - distribution archive. All directories implied by the filenames in - 'files' are created under 'base_dir', and then we hard link or copy - (if hard linking is unavailable) those files into place. - Essentially, this duplicates the developer's source tree, but in a - directory named after the distribution, containing only the files - to be distributed. - """ - # Create all the directories under 'base_dir' necessary to - # put 'files' there; the 'mkpath()' is just so we don't die - # if the manifest happens to be empty. - self.mkpath(base_dir) - dir_util.create_tree(base_dir, files, dry_run=self.dry_run) - - # And walk over the list of files, either making a hard link (if - # os.link exists) to each one that doesn't already exist in its - # corresponding location under 'base_dir', or copying each file - # that's out-of-date in 'base_dir'. (Usually, all files will be - # out-of-date, because by default we blow away 'base_dir' when - # we're done making the distribution archives.) - - if hasattr(os, 'link'): # can make hard links on this system - link = 'hard' - msg = "making hard links in %s..." % base_dir - else: # nope, have to copy - link = None - msg = "copying files to %s..." % base_dir - - if not files: - log.warn("no files to distribute -- empty manifest?") - else: - log.info(msg) - for file in files: - if not os.path.isfile(file): - log.warn("'%s' not a regular file -- skipping", file) - else: - dest = os.path.join(base_dir, file) - self.copy_file(file, dest, link=link) - - self.distribution.metadata.write_pkg_info(base_dir) - - def make_distribution(self): - """Create the source distribution(s). First, we create the release - tree with 'make_release_tree()'; then, we create all required - archive files (according to 'self.formats') from the release tree. - Finally, we clean up by blowing away the release tree (unless - 'self.keep_temp' is true). The list of archive files created is - stored so it can be retrieved later by 'get_archive_files()'. - """ - # Don't warn about missing meta-data here -- should be (and is!) - # done elsewhere. - base_dir = self.distribution.get_fullname() - base_name = os.path.join(self.dist_dir, base_dir) - - self.make_release_tree(base_dir, self.filelist.files) - archive_files = [] # remember names of files we create - # tar archive must be created last to avoid overwrite and remove - if 'tar' in self.formats: - self.formats.append(self.formats.pop(self.formats.index('tar'))) - - for fmt in self.formats: - file = self.make_archive(base_name, fmt, base_dir=base_dir, - owner=self.owner, group=self.group) - archive_files.append(file) - self.distribution.dist_files.append(('sdist', '', file)) - - self.archive_files = archive_files - - if not self.keep_temp: - dir_util.remove_tree(base_dir, dry_run=self.dry_run) - - def get_archive_files(self): - """Return the list of archive files created when the command - was run, or None if the command hasn't run yet. - """ - return self.archive_files diff --git a/command/upload.py b/command/upload.py deleted file mode 100644 index 95e9fda1..00000000 --- a/command/upload.py +++ /dev/null @@ -1,214 +0,0 @@ -""" -distutils.command.upload - -Implements the Distutils 'upload' subcommand (upload package to a package -index). -""" - -import os -import io -import hashlib -from base64 import standard_b64encode -from urllib.request import urlopen, Request, HTTPError -from urllib.parse import urlparse -from distutils.errors import DistutilsError, DistutilsOptionError -from distutils.core import PyPIRCCommand -from distutils.spawn import spawn -from distutils import log - - -# PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256) -# https://bugs.python.org/issue40698 -_FILE_CONTENT_DIGESTS = { - "md5_digest": getattr(hashlib, "md5", None), - "sha256_digest": getattr(hashlib, "sha256", None), - "blake2_256_digest": getattr(hashlib, "blake2b", None), -} - - -class upload(PyPIRCCommand): - - description = "upload binary package to PyPI" - - user_options = PyPIRCCommand.user_options + [ - ('sign', 's', - 'sign files to upload using gpg'), - ('identity=', 'i', 'GPG identity used to sign files'), - ] - - boolean_options = PyPIRCCommand.boolean_options + ['sign'] - - def initialize_options(self): - PyPIRCCommand.initialize_options(self) - self.username = '' - self.password = '' - self.show_response = 0 - self.sign = False - self.identity = None - - def finalize_options(self): - PyPIRCCommand.finalize_options(self) - if self.identity and not self.sign: - raise DistutilsOptionError( - "Must use --sign for --identity to have meaning" - ) - config = self._read_pypirc() - if config != {}: - self.username = config['username'] - self.password = config['password'] - self.repository = config['repository'] - self.realm = config['realm'] - - # getting the password from the distribution - # if previously set by the register command - if not self.password and self.distribution.password: - self.password = self.distribution.password - - def run(self): - if not self.distribution.dist_files: - msg = ("Must create and upload files in one command " - "(e.g. setup.py sdist upload)") - raise DistutilsOptionError(msg) - for command, pyversion, filename in self.distribution.dist_files: - self.upload_file(command, pyversion, filename) - - def upload_file(self, command, pyversion, filename): - # Makes sure the repository URL is compliant - schema, netloc, url, params, query, fragments = \ - urlparse(self.repository) - if params or query or fragments: - raise AssertionError("Incompatible url %s" % self.repository) - - if schema not in ('http', 'https'): - raise AssertionError("unsupported schema " + schema) - - # Sign if requested - if self.sign: - gpg_args = ["gpg", "--detach-sign", "-a", filename] - if self.identity: - gpg_args[2:2] = ["--local-user", self.identity] - spawn(gpg_args, - dry_run=self.dry_run) - - # Fill in the data - send all the meta-data in case we need to - # register a new release - f = open(filename,'rb') - try: - content = f.read() - finally: - f.close() - - meta = self.distribution.metadata - data = { - # action - ':action': 'file_upload', - 'protocol_version': '1', - - # identify release - 'name': meta.get_name(), - 'version': meta.get_version(), - - # file content - 'content': (os.path.basename(filename),content), - 'filetype': command, - 'pyversion': pyversion, - - # additional meta-data - 'metadata_version': '1.0', - 'summary': meta.get_description(), - 'home_page': meta.get_url(), - 'author': meta.get_contact(), - 'author_email': meta.get_contact_email(), - 'license': meta.get_licence(), - 'description': meta.get_long_description(), - 'keywords': meta.get_keywords(), - 'platform': meta.get_platforms(), - 'classifiers': meta.get_classifiers(), - 'download_url': meta.get_download_url(), - # PEP 314 - 'provides': meta.get_provides(), - 'requires': meta.get_requires(), - 'obsoletes': meta.get_obsoletes(), - } - - data['comment'] = '' - - # file content digests - for digest_name, digest_cons in _FILE_CONTENT_DIGESTS.items(): - if digest_cons is None: - continue - try: - data[digest_name] = digest_cons(content).hexdigest() - except ValueError: - # hash digest not available or blocked by security policy - pass - - if self.sign: - with open(filename + ".asc", "rb") as f: - data['gpg_signature'] = (os.path.basename(filename) + ".asc", - f.read()) - - # set up the authentication - user_pass = (self.username + ":" + self.password).encode('ascii') - # The exact encoding of the authentication string is debated. - # Anyway PyPI only accepts ascii for both username or password. - auth = "Basic " + standard_b64encode(user_pass).decode('ascii') - - # Build up the MIME payload for the POST data - boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = b'\r\n--' + boundary.encode('ascii') - end_boundary = sep_boundary + b'--\r\n' - body = io.BytesIO() - for key, value in data.items(): - title = '\r\nContent-Disposition: form-data; name="%s"' % key - # handle multiple entries for the same name - if not isinstance(value, list): - value = [value] - for value in value: - if type(value) is tuple: - title += '; filename="%s"' % value[0] - value = value[1] - else: - value = str(value).encode('utf-8') - body.write(sep_boundary) - body.write(title.encode('utf-8')) - body.write(b"\r\n\r\n") - body.write(value) - body.write(end_boundary) - body = body.getvalue() - - msg = "Submitting %s to %s" % (filename, self.repository) - self.announce(msg, log.INFO) - - # build the Request - headers = { - 'Content-type': 'multipart/form-data; boundary=%s' % boundary, - 'Content-length': str(len(body)), - 'Authorization': auth, - } - - request = Request(self.repository, data=body, - headers=headers) - # send the data - try: - result = urlopen(request) - status = result.getcode() - reason = result.msg - except HTTPError as e: - status = e.code - reason = e.msg - except OSError as e: - self.announce(str(e), log.ERROR) - raise - - if status == 200: - self.announce('Server response (%s): %s' % (status, reason), - log.INFO) - if self.show_response: - text = self._read_pypi_response(result) - msg = '\n'.join(('-' * 75, text, '-' * 75)) - self.announce(msg, log.INFO) - else: - msg = 'Upload failed (%s): %s' % (status, reason) - self.announce(msg, log.ERROR) - raise DistutilsError(msg) diff --git a/command/wininst-10.0-amd64.exe b/command/wininst-10.0-amd64.exe deleted file mode 100644 index 6fa0dce1..00000000 Binary files a/command/wininst-10.0-amd64.exe and /dev/null differ diff --git a/command/wininst-10.0.exe b/command/wininst-10.0.exe deleted file mode 100644 index afc3bc6c..00000000 Binary files a/command/wininst-10.0.exe and /dev/null differ diff --git a/command/wininst-14.0-amd64.exe b/command/wininst-14.0-amd64.exe deleted file mode 100644 index 253c2e2e..00000000 Binary files a/command/wininst-14.0-amd64.exe and /dev/null differ diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe deleted file mode 100644 index 46f5f356..00000000 Binary files a/command/wininst-14.0.exe and /dev/null differ diff --git a/command/wininst-6.0.exe b/command/wininst-6.0.exe deleted file mode 100644 index f57c855a..00000000 Binary files a/command/wininst-6.0.exe and /dev/null differ diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe deleted file mode 100644 index 1433bc1a..00000000 Binary files a/command/wininst-7.1.exe and /dev/null differ diff --git a/command/wininst-8.0.exe b/command/wininst-8.0.exe deleted file mode 100644 index 7403bfab..00000000 Binary files a/command/wininst-8.0.exe and /dev/null differ diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe deleted file mode 100644 index 94fbd434..00000000 Binary files a/command/wininst-9.0-amd64.exe and /dev/null differ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe deleted file mode 100644 index 2ec261f9..00000000 Binary files a/command/wininst-9.0.exe and /dev/null differ diff --git a/config.py b/config.py deleted file mode 100644 index 2171abd6..00000000 --- a/config.py +++ /dev/null @@ -1,130 +0,0 @@ -"""distutils.pypirc - -Provides the PyPIRCCommand class, the base class for the command classes -that uses .pypirc in the distutils.command package. -""" -import os -from configparser import RawConfigParser - -from distutils.cmd import Command - -DEFAULT_PYPIRC = """\ -[distutils] -index-servers = - pypi - -[pypi] -username:%s -password:%s -""" - -class PyPIRCCommand(Command): - """Base command that knows how to handle the .pypirc file - """ - DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/' - DEFAULT_REALM = 'pypi' - repository = None - realm = None - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]" % \ - DEFAULT_REPOSITORY), - ('show-response', None, - 'display full response text from server')] - - boolean_options = ['show-response'] - - def _get_rc_file(self): - """Returns rc file path.""" - return os.path.join(os.path.expanduser('~'), '.pypirc') - - def _store_pypirc(self, username, password): - """Creates a default .pypirc file.""" - rc = self._get_rc_file() - with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f: - f.write(DEFAULT_PYPIRC % (username, password)) - - def _read_pypirc(self): - """Reads the .pypirc file.""" - rc = self._get_rc_file() - if os.path.exists(rc): - self.announce('Using PyPI login from %s' % rc) - repository = self.repository or self.DEFAULT_REPOSITORY - - config = RawConfigParser() - config.read(rc) - sections = config.sections() - if 'distutils' in sections: - # let's get the list of servers - index_servers = config.get('distutils', 'index-servers') - _servers = [server.strip() for server in - index_servers.split('\n') - if server.strip() != ''] - if _servers == []: - # nothing set, let's try to get the default pypi - if 'pypi' in sections: - _servers = ['pypi'] - else: - # the file is not properly defined, returning - # an empty dict - return {} - for server in _servers: - current = {'server': server} - current['username'] = config.get(server, 'username') - - # optional params - for key, default in (('repository', - self.DEFAULT_REPOSITORY), - ('realm', self.DEFAULT_REALM), - ('password', None)): - if config.has_option(server, key): - current[key] = config.get(server, key) - else: - current[key] = default - - # work around people having "repository" for the "pypi" - # section of their config set to the HTTP (rather than - # HTTPS) URL - if (server == 'pypi' and - repository in (self.DEFAULT_REPOSITORY, 'pypi')): - current['repository'] = self.DEFAULT_REPOSITORY - return current - - if (current['server'] == repository or - current['repository'] == repository): - return current - elif 'server-login' in sections: - # old format - server = 'server-login' - if config.has_option(server, 'repository'): - repository = config.get(server, 'repository') - else: - repository = self.DEFAULT_REPOSITORY - return {'username': config.get(server, 'username'), - 'password': config.get(server, 'password'), - 'repository': repository, - 'server': server, - 'realm': self.DEFAULT_REALM} - - return {} - - def _read_pypi_response(self, response): - """Read and decode a PyPI HTTP response.""" - import cgi - content_type = response.getheader('content-type', 'text/plain') - encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii') - return response.read().decode(encoding) - - def initialize_options(self): - """Initialize options.""" - self.repository = None - self.realm = None - self.show_response = 0 - - def finalize_options(self): - """Finalizes options.""" - if self.repository is None: - self.repository = self.DEFAULT_REPOSITORY - if self.realm is None: - self.realm = self.DEFAULT_REALM diff --git a/core.py b/core.py deleted file mode 100644 index d603d4a4..00000000 --- a/core.py +++ /dev/null @@ -1,234 +0,0 @@ -"""distutils.core - -The only module that needs to be imported to use the Distutils; provides -the 'setup' function (which is to be called from the setup script). Also -indirectly provides the Distribution and Command classes, although they are -really defined in distutils.dist and distutils.cmd. -""" - -import os -import sys - -from distutils.debug import DEBUG -from distutils.errors import * - -# Mainly import these so setup scripts can "from distutils.core import" them. -from distutils.dist import Distribution -from distutils.cmd import Command -from distutils.config import PyPIRCCommand -from distutils.extension import Extension - -# This is a barebones help message generated displayed when the user -# runs the setup script with no arguments at all. More useful help -# is generated with various --help options: global help, list commands, -# and per-command help. -USAGE = """\ -usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] - or: %(script)s --help [cmd1 cmd2 ...] - or: %(script)s --help-commands - or: %(script)s cmd --help -""" - -def gen_usage (script_name): - script = os.path.basename(script_name) - return USAGE % vars() - - -# Some mild magic to control the behaviour of 'setup()' from 'run_setup()'. -_setup_stop_after = None -_setup_distribution = None - -# Legal keyword arguments for the setup() function -setup_keywords = ('distclass', 'script_name', 'script_args', 'options', - 'name', 'version', 'author', 'author_email', - 'maintainer', 'maintainer_email', 'url', 'license', - 'description', 'long_description', 'keywords', - 'platforms', 'classifiers', 'download_url', - 'requires', 'provides', 'obsoletes', - ) - -# Legal keyword arguments for the Extension constructor -extension_keywords = ('name', 'sources', 'include_dirs', - 'define_macros', 'undef_macros', - 'library_dirs', 'libraries', 'runtime_library_dirs', - 'extra_objects', 'extra_compile_args', 'extra_link_args', - 'swig_opts', 'export_symbols', 'depends', 'language') - -def setup (**attrs): - """The gateway to the Distutils: do everything your setup script needs - to do, in a highly flexible and user-driven way. Briefly: create a - Distribution instance; find and parse config files; parse the command - line; run each Distutils command found there, customized by the options - supplied to 'setup()' (as keyword arguments), in config files, and on - the command line. - - The Distribution instance might be an instance of a class supplied via - the 'distclass' keyword argument to 'setup'; if no such class is - supplied, then the Distribution class (in dist.py) is instantiated. - All other arguments to 'setup' (except for 'cmdclass') are used to set - attributes of the Distribution instance. - - The 'cmdclass' argument, if supplied, is a dictionary mapping command - names to command classes. Each command encountered on the command line - will be turned into a command class, which is in turn instantiated; any - class found in 'cmdclass' is used in place of the default, which is - (for command 'foo_bar') class 'foo_bar' in module - 'distutils.command.foo_bar'. The command class must provide a - 'user_options' attribute which is a list of option specifiers for - 'distutils.fancy_getopt'. Any command-line options between the current - and the next command are used to set attributes of the current command - object. - - When the entire command-line has been successfully parsed, calls the - 'run()' method on each command object in turn. This method will be - driven entirely by the Distribution object (which each command object - has a reference to, thanks to its constructor), and the - command-specific options that became attributes of each command - object. - """ - - global _setup_stop_after, _setup_distribution - - # Determine the distribution class -- either caller-supplied or - # our Distribution (see below). - klass = attrs.get('distclass') - if klass: - del attrs['distclass'] - else: - klass = Distribution - - if 'script_name' not in attrs: - attrs['script_name'] = os.path.basename(sys.argv[0]) - if 'script_args' not in attrs: - attrs['script_args'] = sys.argv[1:] - - # Create the Distribution instance, using the remaining arguments - # (ie. everything except distclass) to initialize it - try: - _setup_distribution = dist = klass(attrs) - except DistutilsSetupError as msg: - if 'name' not in attrs: - raise SystemExit("error in setup command: %s" % msg) - else: - raise SystemExit("error in %s setup command: %s" % \ - (attrs['name'], msg)) - - if _setup_stop_after == "init": - return dist - - # Find and parse the config file(s): they will override options from - # the setup script, but be overridden by the command line. - dist.parse_config_files() - - if DEBUG: - print("options (after parsing config files):") - dist.dump_option_dicts() - - if _setup_stop_after == "config": - return dist - - # Parse the command line and override config files; any - # command-line errors are the end user's fault, so turn them into - # SystemExit to suppress tracebacks. - try: - ok = dist.parse_command_line() - except DistutilsArgError as msg: - raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg) - - if DEBUG: - print("options (after parsing command line):") - dist.dump_option_dicts() - - if _setup_stop_after == "commandline": - return dist - - # And finally, run all the commands found on the command line. - if ok: - try: - dist.run_commands() - except KeyboardInterrupt: - raise SystemExit("interrupted") - except OSError as exc: - if DEBUG: - sys.stderr.write("error: %s\n" % (exc,)) - raise - else: - raise SystemExit("error: %s" % (exc,)) - - except (DistutilsError, - CCompilerError) as msg: - if DEBUG: - raise - else: - raise SystemExit("error: " + str(msg)) - - return dist - -# setup () - - -def run_setup (script_name, script_args=None, stop_after="run"): - """Run a setup script in a somewhat controlled environment, and - return the Distribution instance that drives things. This is useful - if you need to find out the distribution meta-data (passed as - keyword args from 'script' to 'setup()', or the contents of the - config files or command-line. - - 'script_name' is a file that will be read and run with 'exec()'; - 'sys.argv[0]' will be replaced with 'script' for the duration of the - call. 'script_args' is a list of strings; if supplied, - 'sys.argv[1:]' will be replaced by 'script_args' for the duration of - the call. - - 'stop_after' tells 'setup()' when to stop processing; possible - values: - init - stop after the Distribution instance has been created and - populated with the keyword arguments to 'setup()' - config - stop after config files have been parsed (and their data - stored in the Distribution instance) - commandline - stop after the command-line ('sys.argv[1:]' or 'script_args') - have been parsed (and the data stored in the Distribution) - run [default] - stop after all commands have been run (the same as if 'setup()' - had been called in the usual way - - Returns the Distribution instance, which provides all information - used to drive the Distutils. - """ - if stop_after not in ('init', 'config', 'commandline', 'run'): - raise ValueError("invalid value for 'stop_after': %r" % (stop_after,)) - - global _setup_stop_after, _setup_distribution - _setup_stop_after = stop_after - - save_argv = sys.argv.copy() - g = {'__file__': script_name} - try: - try: - sys.argv[0] = script_name - if script_args is not None: - sys.argv[1:] = script_args - with open(script_name, 'rb') as f: - exec(f.read(), g) - finally: - sys.argv = save_argv - _setup_stop_after = None - except SystemExit: - # Hmm, should we do something if exiting with a non-zero code - # (ie. error)? - pass - - if _setup_distribution is None: - raise RuntimeError(("'distutils.core.setup()' was never called -- " - "perhaps '%s' is not a Distutils setup script?") % \ - script_name) - - # I wonder if the setup script's namespace -- g and l -- would be of - # any interest to callers? - #print "_setup_distribution:", _setup_distribution - return _setup_distribution - -# run_setup () diff --git a/cygwinccompiler.py b/cygwinccompiler.py deleted file mode 100644 index 66c12dd3..00000000 --- a/cygwinccompiler.py +++ /dev/null @@ -1,403 +0,0 @@ -"""distutils.cygwinccompiler - -Provides the CygwinCCompiler class, a subclass of UnixCCompiler that -handles the Cygwin port of the GNU C compiler to Windows. It also contains -the Mingw32CCompiler class which handles the mingw32 port of GCC (same as -cygwin in no-cygwin mode). -""" - -# problems: -# -# * if you use a msvc compiled python version (1.5.2) -# 1. you have to insert a __GNUC__ section in its config.h -# 2. you have to generate an import library for its dll -# - create a def-file for python??.dll -# - create an import library using -# dlltool --dllname python15.dll --def python15.def \ -# --output-lib libpython15.a -# -# see also http://starship.python.net/crew/kernr/mingw32/Notes.html -# -# * We put export_symbols in a def-file, and don't use -# --export-all-symbols because it doesn't worked reliable in some -# tested configurations. And because other windows compilers also -# need their symbols specified this no serious problem. -# -# tested configurations: -# -# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works -# (after patching python's config.h and for C++ some other include files) -# see also http://starship.python.net/crew/kernr/mingw32/Notes.html -# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works -# (ld doesn't support -shared, so we use dllwrap) -# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now -# - its dllwrap doesn't work, there is a bug in binutils 2.10.90 -# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html -# - using gcc -mdll instead dllwrap doesn't work without -static because -# it tries to link against dlls instead their import libraries. (If -# it finds the dll first.) -# By specifying -static we force ld to link against the import libraries, -# this is windows standard and there are normally not the necessary symbols -# in the dlls. -# *** only the version of June 2000 shows these problems -# * cygwin gcc 3.2/ld 2.13.90 works -# (ld supports -shared) -# * mingw gcc 3.2/ld 2.13 works -# (ld supports -shared) - -import os -import sys -import copy -from subprocess import Popen, PIPE, check_output -import re - -from distutils.unixccompiler import UnixCCompiler -from distutils.file_util import write_file -from distutils.errors import (DistutilsExecError, CCompilerError, - CompileError, UnknownFileError) -from distutils.version import LooseVersion -from distutils.spawn import find_executable - -def get_msvcr(): - """Include the appropriate MSVC runtime library if Python was built - with MSVC 7.0 or later. - """ - msc_pos = sys.version.find('MSC v.') - if msc_pos != -1: - msc_ver = sys.version[msc_pos+6:msc_pos+10] - if msc_ver == '1300': - # MSVC 7.0 - return ['msvcr70'] - elif msc_ver == '1310': - # MSVC 7.1 - return ['msvcr71'] - elif msc_ver == '1400': - # VS2005 / MSVC 8.0 - return ['msvcr80'] - elif msc_ver == '1500': - # VS2008 / MSVC 9.0 - return ['msvcr90'] - elif msc_ver == '1600': - # VS2010 / MSVC 10.0 - return ['msvcr100'] - else: - raise ValueError("Unknown MS Compiler version %s " % msc_ver) - - -class CygwinCCompiler(UnixCCompiler): - """ Handles the Cygwin port of the GNU C compiler to Windows. - """ - compiler_type = 'cygwin' - obj_extension = ".o" - static_lib_extension = ".a" - shared_lib_extension = ".dll" - static_lib_format = "lib%s%s" - shared_lib_format = "%s%s" - exe_extension = ".exe" - - def __init__(self, verbose=0, dry_run=0, force=0): - - UnixCCompiler.__init__(self, verbose, dry_run, force) - - status, details = check_config_h() - self.debug_print("Python's GCC status: %s (details: %s)" % - (status, details)) - if status is not CONFIG_H_OK: - self.warn( - "Python's pyconfig.h doesn't seem to support your compiler. " - "Reason: %s. " - "Compiling may fail because of undefined preprocessor macros." - % details) - - self.gcc_version, self.ld_version, self.dllwrap_version = \ - get_versions() - self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % - (self.gcc_version, - self.ld_version, - self.dllwrap_version) ) - - # ld_version >= "2.10.90" and < "2.13" should also be able to use - # gcc -mdll instead of dllwrap - # Older dllwraps had own version numbers, newer ones use the - # same as the rest of binutils ( also ld ) - # dllwrap 2.10.90 is buggy - if self.ld_version >= "2.10.90": - self.linker_dll = "gcc" - else: - self.linker_dll = "dllwrap" - - # ld_version >= "2.13" support -shared so use it instead of - # -mdll -static - if self.ld_version >= "2.13": - shared_option = "-shared" - else: - shared_option = "-mdll -static" - - # Hard-code GCC because that's what this is all about. - # XXX optimization, warnings etc. should be customizable. - self.set_executables(compiler='gcc -mcygwin -O -Wall', - compiler_so='gcc -mcygwin -mdll -O -Wall', - compiler_cxx='g++ -mcygwin -O -Wall', - linker_exe='gcc -mcygwin', - linker_so=('%s -mcygwin %s' % - (self.linker_dll, shared_option))) - - # cygwin and mingw32 need different sets of libraries - if self.gcc_version == "2.91.57": - # cygwin shouldn't need msvcrt, but without the dlls will crash - # (gcc version 2.91.57) -- perhaps something about initialization - self.dll_libraries=["msvcrt"] - self.warn( - "Consider upgrading to a newer version of gcc") - else: - # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or later. - self.dll_libraries = get_msvcr() - - def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): - """Compiles the source by spawning GCC and windres if needed.""" - if ext == '.rc' or ext == '.res': - # gcc needs '.res' and '.rc' compiled to object files !!! - try: - self.spawn(["windres", "-i", src, "-o", obj]) - except DistutilsExecError as msg: - raise CompileError(msg) - else: # for other files use the C-compiler - try: - self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + - extra_postargs) - except DistutilsExecError as msg: - raise CompileError(msg) - - def link(self, target_desc, objects, output_filename, output_dir=None, - libraries=None, library_dirs=None, runtime_library_dirs=None, - export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None, build_temp=None, target_lang=None): - """Link the objects.""" - # use separate copies, so we can modify the lists - extra_preargs = copy.copy(extra_preargs or []) - libraries = copy.copy(libraries or []) - objects = copy.copy(objects or []) - - # Additional libraries - libraries.extend(self.dll_libraries) - - # handle export symbols by creating a def-file - # with executables this only works with gcc/ld as linker - if ((export_symbols is not None) and - (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): - # (The linker doesn't do anything if output is up-to-date. - # So it would probably better to check if we really need this, - # but for this we had to insert some unchanged parts of - # UnixCCompiler, and this is not what we want.) - - # we want to put some files in the same directory as the - # object files are, build_temp doesn't help much - # where are the object files - temp_dir = os.path.dirname(objects[0]) - # name of dll to give the helper files the same base name - (dll_name, dll_extension) = os.path.splitext( - os.path.basename(output_filename)) - - # generate the filenames for these files - def_file = os.path.join(temp_dir, dll_name + ".def") - lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") - - # Generate .def file - contents = [ - "LIBRARY %s" % os.path.basename(output_filename), - "EXPORTS"] - for sym in export_symbols: - contents.append(sym) - self.execute(write_file, (def_file, contents), - "writing %s" % def_file) - - # next add options for def-file and to creating import libraries - - # dllwrap uses different options than gcc/ld - if self.linker_dll == "dllwrap": - extra_preargs.extend(["--output-lib", lib_file]) - # for dllwrap we have to use a special option - extra_preargs.extend(["--def", def_file]) - # we use gcc/ld here and can be sure ld is >= 2.9.10 - else: - # doesn't work: bfd_close build\...\libfoo.a: Invalid operation - #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) - # for gcc/ld the def-file is specified as any object files - objects.append(def_file) - - #end: if ((export_symbols is not None) and - # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): - - # who wants symbols and a many times larger output file - # should explicitly switch the debug mode on - # otherwise we let dllwrap/ld strip the output file - # (On my machine: 10KiB < stripped_file < ??100KiB - # unstripped_file = stripped_file + XXX KiB - # ( XXX=254 for a typical python extension)) - if not debug: - extra_preargs.append("-s") - - UnixCCompiler.link(self, target_desc, objects, output_filename, - output_dir, libraries, library_dirs, - runtime_library_dirs, - None, # export_symbols, we do this in our def-file - debug, extra_preargs, extra_postargs, build_temp, - target_lang) - - # -- Miscellaneous methods ----------------------------------------- - - def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): - """Adds supports for rc and res files.""" - if output_dir is None: - output_dir = '' - obj_names = [] - for src_name in source_filenames: - # use normcase to make sure '.rc' is really '.rc' and not '.RC' - base, ext = os.path.splitext(os.path.normcase(src_name)) - if ext not in (self.src_extensions + ['.rc','.res']): - raise UnknownFileError("unknown file type '%s' (from '%s')" % \ - (ext, src_name)) - if strip_dir: - base = os.path.basename (base) - if ext in ('.res', '.rc'): - # these need to be compiled to object files - obj_names.append (os.path.join(output_dir, - base + ext + self.obj_extension)) - else: - obj_names.append (os.path.join(output_dir, - base + self.obj_extension)) - return obj_names - -# the same as cygwin plus some additional parameters -class Mingw32CCompiler(CygwinCCompiler): - """ Handles the Mingw32 port of the GNU C compiler to Windows. - """ - compiler_type = 'mingw32' - - def __init__(self, verbose=0, dry_run=0, force=0): - - CygwinCCompiler.__init__ (self, verbose, dry_run, force) - - # ld_version >= "2.13" support -shared so use it instead of - # -mdll -static - if self.ld_version >= "2.13": - shared_option = "-shared" - else: - shared_option = "-mdll -static" - - # A real mingw32 doesn't need to specify a different entry point, - # but cygwin 2.91.57 in no-cygwin-mode needs it. - if self.gcc_version <= "2.91.57": - entry_point = '--entry _DllMain@12' - else: - entry_point = '' - - if is_cygwingcc(): - raise CCompilerError( - 'Cygwin gcc cannot be used with --compiler=mingw32') - - self.set_executables(compiler='gcc -O -Wall', - compiler_so='gcc -mdll -O -Wall', - compiler_cxx='g++ -O -Wall', - linker_exe='gcc', - linker_so='%s %s %s' - % (self.linker_dll, shared_option, - entry_point)) - # Maybe we should also append -mthreads, but then the finished - # dlls need another dll (mingwm10.dll see Mingw32 docs) - # (-mthreads: Support thread-safe exception handling on `Mingw32') - - # no additional libraries needed - self.dll_libraries=[] - - # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or later. - self.dll_libraries = get_msvcr() - -# Because these compilers aren't configured in Python's pyconfig.h file by -# default, we should at least warn the user if he is using an unmodified -# version. - -CONFIG_H_OK = "ok" -CONFIG_H_NOTOK = "not ok" -CONFIG_H_UNCERTAIN = "uncertain" - -def check_config_h(): - """Check if the current Python installation appears amenable to building - extensions with GCC. - - Returns a tuple (status, details), where 'status' is one of the following - constants: - - - CONFIG_H_OK: all is well, go ahead and compile - - CONFIG_H_NOTOK: doesn't look good - - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h - - 'details' is a human-readable string explaining the situation. - - Note there are two ways to conclude "OK": either 'sys.version' contains - the string "GCC" (implying that this Python was built with GCC), or the - installed "pyconfig.h" contains the string "__GNUC__". - """ - - # XXX since this function also checks sys.version, it's not strictly a - # "pyconfig.h" check -- should probably be renamed... - - from distutils import sysconfig - - # if sys.version contains GCC then python was compiled with GCC, and the - # pyconfig.h file should be OK - if "GCC" in sys.version: - return CONFIG_H_OK, "sys.version mentions 'GCC'" - - # let's see if __GNUC__ is mentioned in python.h - fn = sysconfig.get_config_h_filename() - try: - config_h = open(fn) - try: - if "__GNUC__" in config_h.read(): - return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn - else: - return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn - finally: - config_h.close() - except OSError as exc: - return (CONFIG_H_UNCERTAIN, - "couldn't read '%s': %s" % (fn, exc.strerror)) - -RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)') - -def _find_exe_version(cmd): - """Find the version of an executable by running `cmd` in the shell. - - If the command is not found, or the output does not match - `RE_VERSION`, returns None. - """ - executable = cmd.split()[0] - if find_executable(executable) is None: - return None - out = Popen(cmd, shell=True, stdout=PIPE).stdout - try: - out_string = out.read() - finally: - out.close() - result = RE_VERSION.search(out_string) - if result is None: - return None - # LooseVersion works with strings - # so we need to decode our bytes - return LooseVersion(result.group(1).decode()) - -def get_versions(): - """ Try to find out the versions of gcc, ld and dllwrap. - - If not possible it returns None for it. - """ - commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] - return tuple([_find_exe_version(cmd) for cmd in commands]) - -def is_cygwingcc(): - '''Try to determine if the gcc that would be used is from cygwin.''' - out_string = check_output(['gcc', '-dumpmachine']) - return out_string.strip().endswith(b'cygwin') diff --git a/debug.py b/debug.py deleted file mode 100644 index daf1660f..00000000 --- a/debug.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - -# If DISTUTILS_DEBUG is anything other than the empty string, we run in -# debug mode. -DEBUG = os.environ.get('DISTUTILS_DEBUG') diff --git a/dep_util.py b/dep_util.py deleted file mode 100644 index d74f5e4e..00000000 --- a/dep_util.py +++ /dev/null @@ -1,92 +0,0 @@ -"""distutils.dep_util - -Utility functions for simple, timestamp-based dependency of files -and groups of files; also, function based entirely on such -timestamp dependency analysis.""" - -import os -from distutils.errors import DistutilsFileError - - -def newer (source, target): - """Return true if 'source' exists and is more recently modified than - 'target', or if 'source' exists and 'target' doesn't. Return false if - both exist and 'target' is the same age or younger than 'source'. - Raise DistutilsFileError if 'source' does not exist. - """ - if not os.path.exists(source): - raise DistutilsFileError("file '%s' does not exist" % - os.path.abspath(source)) - if not os.path.exists(target): - return 1 - - from stat import ST_MTIME - mtime1 = os.stat(source)[ST_MTIME] - mtime2 = os.stat(target)[ST_MTIME] - - return mtime1 > mtime2 - -# newer () - - -def newer_pairwise (sources, targets): - """Walk two filename lists in parallel, testing if each source is newer - than its corresponding target. Return a pair of lists (sources, - targets) where source is newer than target, according to the semantics - of 'newer()'. - """ - if len(sources) != len(targets): - raise ValueError("'sources' and 'targets' must be same length") - - # build a pair of lists (sources, targets) where source is newer - n_sources = [] - n_targets = [] - for i in range(len(sources)): - if newer(sources[i], targets[i]): - n_sources.append(sources[i]) - n_targets.append(targets[i]) - - return (n_sources, n_targets) - -# newer_pairwise () - - -def newer_group (sources, target, missing='error'): - """Return true if 'target' is out-of-date with respect to any file - listed in 'sources'. In other words, if 'target' exists and is newer - than every file in 'sources', return false; otherwise return true. - 'missing' controls what we do when a source file is missing; the - default ("error") is to blow up with an OSError from inside 'stat()'; - if it is "ignore", we silently drop any missing source files; if it is - "newer", any missing source files make us assume that 'target' is - out-of-date (this is handy in "dry-run" mode: it'll make you pretend to - carry out commands that wouldn't work because inputs are missing, but - that doesn't matter because you're not actually going to run the - commands). - """ - # If the target doesn't even exist, then it's definitely out-of-date. - if not os.path.exists(target): - return 1 - - # Otherwise we have to find out the hard way: if *any* source file - # is more recent than 'target', then 'target' is out-of-date and - # we can immediately return true. If we fall through to the end - # of the loop, then 'target' is up-to-date and we return false. - from stat import ST_MTIME - target_mtime = os.stat(target)[ST_MTIME] - for source in sources: - if not os.path.exists(source): - if missing == 'error': # blow up when we stat() the file - pass - elif missing == 'ignore': # missing source dropped from - continue # target's dependency list - elif missing == 'newer': # missing source means target is - return 1 # out-of-date - - source_mtime = os.stat(source)[ST_MTIME] - if source_mtime > target_mtime: - return 1 - else: - return 0 - -# newer_group () diff --git a/dir_util.py b/dir_util.py deleted file mode 100644 index d5cd8e3e..00000000 --- a/dir_util.py +++ /dev/null @@ -1,210 +0,0 @@ -"""distutils.dir_util - -Utility functions for manipulating directories and directory trees.""" - -import os -import errno -from distutils.errors import DistutilsFileError, DistutilsInternalError -from distutils import log - -# cache for by mkpath() -- in addition to cheapening redundant calls, -# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode -_path_created = {} - -# I don't use os.makedirs because a) it's new to Python 1.5.2, and -# b) it blows up if the directory already exists (I want to silently -# succeed in that case). -def mkpath(name, mode=0o777, verbose=1, dry_run=0): - """Create a directory and any missing ancestor directories. - - If the directory already exists (or if 'name' is the empty string, which - means the current directory, which of course exists), then do nothing. - Raise DistutilsFileError if unable to create some directory along the way - (eg. some sub-path exists, but is a file rather than a directory). - If 'verbose' is true, print a one-line summary of each mkdir to stdout. - Return the list of directories actually created. - """ - - global _path_created - - # Detect a common bug -- name is None - if not isinstance(name, str): - raise DistutilsInternalError( - "mkpath: 'name' must be a string (got %r)" % (name,)) - - # XXX what's the better way to handle verbosity? print as we create - # each directory in the path (the current behaviour), or only announce - # the creation of the whole path? (quite easy to do the latter since - # we're not using a recursive algorithm) - - name = os.path.normpath(name) - created_dirs = [] - if os.path.isdir(name) or name == '': - return created_dirs - if _path_created.get(os.path.abspath(name)): - return created_dirs - - (head, tail) = os.path.split(name) - tails = [tail] # stack of lone dirs to create - - while head and tail and not os.path.isdir(head): - (head, tail) = os.path.split(head) - tails.insert(0, tail) # push next higher dir onto stack - - # now 'head' contains the deepest directory that already exists - # (that is, the child of 'head' in 'name' is the highest directory - # that does *not* exist) - for d in tails: - #print "head = %s, d = %s: " % (head, d), - head = os.path.join(head, d) - abs_head = os.path.abspath(head) - - if _path_created.get(abs_head): - continue - - if verbose >= 1: - log.info("creating %s", head) - - if not dry_run: - try: - os.mkdir(head, mode) - except OSError as exc: - if not (exc.errno == errno.EEXIST and os.path.isdir(head)): - raise DistutilsFileError( - "could not create '%s': %s" % (head, exc.args[-1])) - created_dirs.append(head) - - _path_created[abs_head] = 1 - return created_dirs - -def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0): - """Create all the empty directories under 'base_dir' needed to put 'files' - there. - - 'base_dir' is just the name of a directory which doesn't necessarily - exist yet; 'files' is a list of filenames to be interpreted relative to - 'base_dir'. 'base_dir' + the directory portion of every file in 'files' - will be created if it doesn't already exist. 'mode', 'verbose' and - 'dry_run' flags are as for 'mkpath()'. - """ - # First get the list of directories to create - need_dir = set() - for file in files: - need_dir.add(os.path.join(base_dir, os.path.dirname(file))) - - # Now create them - for dir in sorted(need_dir): - mkpath(dir, mode, verbose=verbose, dry_run=dry_run) - -def copy_tree(src, dst, preserve_mode=1, preserve_times=1, - preserve_symlinks=0, update=0, verbose=1, dry_run=0): - """Copy an entire directory tree 'src' to a new location 'dst'. - - Both 'src' and 'dst' must be directory names. If 'src' is not a - directory, raise DistutilsFileError. If 'dst' does not exist, it is - created with 'mkpath()'. The end result of the copy is that every - file in 'src' is copied to 'dst', and directories under 'src' are - recursively copied to 'dst'. Return the list of files that were - copied or might have been copied, using their output name. The - return value is unaffected by 'update' or 'dry_run': it is simply - the list of all files under 'src', with the names changed to be - under 'dst'. - - 'preserve_mode' and 'preserve_times' are the same as for - 'copy_file'; note that they only apply to regular files, not to - directories. If 'preserve_symlinks' is true, symlinks will be - copied as symlinks (on platforms that support them!); otherwise - (the default), the destination of the symlink will be copied. - 'update' and 'verbose' are the same as for 'copy_file'. - """ - from distutils.file_util import copy_file - - if not dry_run and not os.path.isdir(src): - raise DistutilsFileError( - "cannot copy tree '%s': not a directory" % src) - try: - names = os.listdir(src) - except OSError as e: - if dry_run: - names = [] - else: - raise DistutilsFileError( - "error listing files in '%s': %s" % (src, e.strerror)) - - if not dry_run: - mkpath(dst, verbose=verbose) - - outputs = [] - - for n in names: - src_name = os.path.join(src, n) - dst_name = os.path.join(dst, n) - - if n.startswith('.nfs'): - # skip NFS rename files - continue - - if preserve_symlinks and os.path.islink(src_name): - link_dest = os.readlink(src_name) - if verbose >= 1: - log.info("linking %s -> %s", dst_name, link_dest) - if not dry_run: - os.symlink(link_dest, dst_name) - outputs.append(dst_name) - - elif os.path.isdir(src_name): - outputs.extend( - copy_tree(src_name, dst_name, preserve_mode, - preserve_times, preserve_symlinks, update, - verbose=verbose, dry_run=dry_run)) - else: - copy_file(src_name, dst_name, preserve_mode, - preserve_times, update, verbose=verbose, - dry_run=dry_run) - outputs.append(dst_name) - - return outputs - -def _build_cmdtuple(path, cmdtuples): - """Helper for remove_tree().""" - for f in os.listdir(path): - real_f = os.path.join(path,f) - if os.path.isdir(real_f) and not os.path.islink(real_f): - _build_cmdtuple(real_f, cmdtuples) - else: - cmdtuples.append((os.remove, real_f)) - cmdtuples.append((os.rmdir, path)) - -def remove_tree(directory, verbose=1, dry_run=0): - """Recursively remove an entire directory tree. - - Any errors are ignored (apart from being reported to stdout if 'verbose' - is true). - """ - global _path_created - - if verbose >= 1: - log.info("removing '%s' (and everything under it)", directory) - if dry_run: - return - cmdtuples = [] - _build_cmdtuple(directory, cmdtuples) - for cmd in cmdtuples: - try: - cmd[0](cmd[1]) - # remove dir from cache if it's already there - abspath = os.path.abspath(cmd[1]) - if abspath in _path_created: - del _path_created[abspath] - except OSError as exc: - log.warn("error removing %s: %s", directory, exc) - -def ensure_relative(path): - """Take the full path 'path', and make it a relative path. - - This is useful to make 'path' the second argument to os.path.join(). - """ - drive, path = os.path.splitdrive(path) - if path[0:1] == os.sep: - path = drive + path[1:] - return path diff --git a/dist.py b/dist.py deleted file mode 100644 index 6cf0a0d6..00000000 --- a/dist.py +++ /dev/null @@ -1,1256 +0,0 @@ -"""distutils.dist - -Provides the Distribution class, which represents the module distribution -being built/installed/distributed. -""" - -import sys -import os -import re -from email import message_from_file - -try: - import warnings -except ImportError: - warnings = None - -from distutils.errors import * -from distutils.fancy_getopt import FancyGetopt, translate_longopt -from distutils.util import check_environ, strtobool, rfc822_escape -from distutils import log -from distutils.debug import DEBUG - -# Regex to define acceptable Distutils command names. This is not *quite* -# the same as a Python NAME -- I don't allow leading underscores. The fact -# that they're very similar is no coincidence; the default naming scheme is -# to look for a Python module named after the command. -command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') - - -def _ensure_list(value, fieldname): - if isinstance(value, str): - # a string containing comma separated values is okay. It will - # be converted to a list by Distribution.finalize_options(). - pass - elif not isinstance(value, list): - # passing a tuple or an iterator perhaps, warn and convert - typename = type(value).__name__ - msg = f"Warning: '{fieldname}' should be a list, got type '{typename}'" - log.log(log.WARN, msg) - value = list(value) - return value - - -class Distribution: - """The core of the Distutils. Most of the work hiding behind 'setup' - is really done within a Distribution instance, which farms the work out - to the Distutils commands specified on the command line. - - Setup scripts will almost never instantiate Distribution directly, - unless the 'setup()' function is totally inadequate to their needs. - However, it is conceivable that a setup script might wish to subclass - Distribution for some specialized purpose, and then pass the subclass - to 'setup()' as the 'distclass' keyword argument. If so, it is - necessary to respect the expectations that 'setup' has of Distribution. - See the code for 'setup()', in core.py, for details. - """ - - # 'global_options' describes the command-line options that may be - # supplied to the setup script prior to any actual commands. - # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of - # these global options. This list should be kept to a bare minimum, - # since every global option is also valid as a command option -- and we - # don't want to pollute the commands with too many options that they - # have minimal control over. - # The fourth entry for verbose means that it can be repeated. - global_options = [ - ('verbose', 'v', "run verbosely (default)", 1), - ('quiet', 'q', "run quietly (turns verbosity off)"), - ('dry-run', 'n', "don't actually do anything"), - ('help', 'h', "show detailed help message"), - ('no-user-cfg', None, - 'ignore pydistutils.cfg in your home directory'), - ] - - # 'common_usage' is a short (2-3 line) string describing the common - # usage of the setup script. - common_usage = """\ -Common commands: (see '--help-commands' for more) - - setup.py build will build the package underneath 'build/' - setup.py install will install the package -""" - - # options that are not propagated to the commands - display_options = [ - ('help-commands', None, - "list all available commands"), - ('name', None, - "print package name"), - ('version', 'V', - "print package version"), - ('fullname', None, - "print -"), - ('author', None, - "print the author's name"), - ('author-email', None, - "print the author's email address"), - ('maintainer', None, - "print the maintainer's name"), - ('maintainer-email', None, - "print the maintainer's email address"), - ('contact', None, - "print the maintainer's name if known, else the author's"), - ('contact-email', None, - "print the maintainer's email address if known, else the author's"), - ('url', None, - "print the URL for this package"), - ('license', None, - "print the license of the package"), - ('licence', None, - "alias for --license"), - ('description', None, - "print the package description"), - ('long-description', None, - "print the long package description"), - ('platforms', None, - "print the list of platforms"), - ('classifiers', None, - "print the list of classifiers"), - ('keywords', None, - "print the list of keywords"), - ('provides', None, - "print the list of packages/modules provided"), - ('requires', None, - "print the list of packages/modules required"), - ('obsoletes', None, - "print the list of packages/modules made obsolete") - ] - display_option_names = [translate_longopt(x[0]) for x in display_options] - - # negative options are options that exclude other options - negative_opt = {'quiet': 'verbose'} - - # -- Creation/initialization methods ------------------------------- - - def __init__(self, attrs=None): - """Construct a new Distribution instance: initialize all the - attributes of a Distribution, and then use 'attrs' (a dictionary - mapping attribute names to values) to assign some of those - attributes their "real" values. (Any attributes not mentioned in - 'attrs' will be assigned to some null value: 0, None, an empty list - or dictionary, etc.) Most importantly, initialize the - 'command_obj' attribute to the empty dictionary; this will be - filled in with real command objects by 'parse_command_line()'. - """ - - # Default values for our command-line options - self.verbose = 1 - self.dry_run = 0 - self.help = 0 - for attr in self.display_option_names: - setattr(self, attr, 0) - - # Store the distribution meta-data (name, version, author, and so - # forth) in a separate object -- we're getting to have enough - # information here (and enough command-line options) that it's - # worth it. Also delegate 'get_XXX()' methods to the 'metadata' - # object in a sneaky and underhanded (but efficient!) way. - self.metadata = DistributionMetadata() - for basename in self.metadata._METHOD_BASENAMES: - method_name = "get_" + basename - setattr(self, method_name, getattr(self.metadata, method_name)) - - # 'cmdclass' maps command names to class objects, so we - # can 1) quickly figure out which class to instantiate when - # we need to create a new command object, and 2) have a way - # for the setup script to override command classes - self.cmdclass = {} - - # 'command_packages' is a list of packages in which commands - # are searched for. The factory for command 'foo' is expected - # to be named 'foo' in the module 'foo' in one of the packages - # named here. This list is searched from the left; an error - # is raised if no named package provides the command being - # searched for. (Always access using get_command_packages().) - self.command_packages = None - - # 'script_name' and 'script_args' are usually set to sys.argv[0] - # and sys.argv[1:], but they can be overridden when the caller is - # not necessarily a setup script run from the command-line. - self.script_name = None - self.script_args = None - - # 'command_options' is where we store command options between - # parsing them (from config files, the command-line, etc.) and when - # they are actually needed -- ie. when the command in question is - # instantiated. It is a dictionary of dictionaries of 2-tuples: - # command_options = { command_name : { option : (source, value) } } - self.command_options = {} - - # 'dist_files' is the list of (command, pyversion, file) that - # have been created by any dist commands run so far. This is - # filled regardless of whether the run is dry or not. pyversion - # gives sysconfig.get_python_version() if the dist file is - # specific to a Python version, 'any' if it is good for all - # Python versions on the target platform, and '' for a source - # file. pyversion should not be used to specify minimum or - # maximum required Python versions; use the metainfo for that - # instead. - self.dist_files = [] - - # These options are really the business of various commands, rather - # than of the Distribution itself. We provide aliases for them in - # Distribution as a convenience to the developer. - self.packages = None - self.package_data = {} - self.package_dir = None - self.py_modules = None - self.libraries = None - self.headers = None - self.ext_modules = None - self.ext_package = None - self.include_dirs = None - self.extra_path = None - self.scripts = None - self.data_files = None - self.password = '' - - # And now initialize bookkeeping stuff that can't be supplied by - # the caller at all. 'command_obj' maps command names to - # Command instances -- that's how we enforce that every command - # class is a singleton. - self.command_obj = {} - - # 'have_run' maps command names to boolean values; it keeps track - # of whether we have actually run a particular command, to make it - # cheap to "run" a command whenever we think we might need to -- if - # it's already been done, no need for expensive filesystem - # operations, we just check the 'have_run' dictionary and carry on. - # It's only safe to query 'have_run' for a command class that has - # been instantiated -- a false value will be inserted when the - # command object is created, and replaced with a true value when - # the command is successfully run. Thus it's probably best to use - # '.get()' rather than a straight lookup. - self.have_run = {} - - # Now we'll use the attrs dictionary (ultimately, keyword args from - # the setup script) to possibly override any or all of these - # distribution options. - - if attrs: - # Pull out the set of command options and work on them - # specifically. Note that this order guarantees that aliased - # command options will override any supplied redundantly - # through the general options dictionary. - options = attrs.get('options') - if options is not None: - del attrs['options'] - for (command, cmd_options) in options.items(): - opt_dict = self.get_option_dict(command) - for (opt, val) in cmd_options.items(): - opt_dict[opt] = ("setup script", val) - - if 'licence' in attrs: - attrs['license'] = attrs['licence'] - del attrs['licence'] - msg = "'licence' distribution option is deprecated; use 'license'" - if warnings is not None: - warnings.warn(msg) - else: - sys.stderr.write(msg + "\n") - - # Now work on the rest of the attributes. Any attribute that's - # not already defined is invalid! - for (key, val) in attrs.items(): - if hasattr(self.metadata, "set_" + key): - getattr(self.metadata, "set_" + key)(val) - elif hasattr(self.metadata, key): - setattr(self.metadata, key, val) - elif hasattr(self, key): - setattr(self, key, val) - else: - msg = "Unknown distribution option: %s" % repr(key) - warnings.warn(msg) - - # no-user-cfg is handled before other command line args - # because other args override the config files, and this - # one is needed before we can load the config files. - # If attrs['script_args'] wasn't passed, assume false. - # - # This also make sure we just look at the global options - self.want_user_cfg = True - - if self.script_args is not None: - for arg in self.script_args: - if not arg.startswith('-'): - break - if arg == '--no-user-cfg': - self.want_user_cfg = False - break - - self.finalize_options() - - def get_option_dict(self, command): - """Get the option dictionary for a given command. If that - command's option dictionary hasn't been created yet, then create it - and return the new dictionary; otherwise, return the existing - option dictionary. - """ - dict = self.command_options.get(command) - if dict is None: - dict = self.command_options[command] = {} - return dict - - def dump_option_dicts(self, header=None, commands=None, indent=""): - from pprint import pformat - - if commands is None: # dump all command option dicts - commands = sorted(self.command_options.keys()) - - if header is not None: - self.announce(indent + header) - indent = indent + " " - - if not commands: - self.announce(indent + "no commands known yet") - return - - for cmd_name in commands: - opt_dict = self.command_options.get(cmd_name) - if opt_dict is None: - self.announce(indent + - "no option dict for '%s' command" % cmd_name) - else: - self.announce(indent + - "option dict for '%s' command:" % cmd_name) - out = pformat(opt_dict) - for line in out.split('\n'): - self.announce(indent + " " + line) - - # -- Config file finding/parsing methods --------------------------- - - def find_config_files(self): - """Find as many configuration files as should be processed for this - platform, and return a list of filenames in the order in which they - should be parsed. The filenames returned are guaranteed to exist - (modulo nasty race conditions). - - There are three possible config files: distutils.cfg in the - Distutils installation directory (ie. where the top-level - Distutils __inst__.py file lives), a file in the user's home - directory named .pydistutils.cfg on Unix and pydistutils.cfg - on Windows/Mac; and setup.cfg in the current directory. - - The file in the user's home directory can be disabled with the - --no-user-cfg option. - """ - files = [] - check_environ() - - # Where to look for the system-wide Distutils config file - sys_dir = os.path.dirname(sys.modules['distutils'].__file__) - - # Look for the system config file - sys_file = os.path.join(sys_dir, "distutils.cfg") - if os.path.isfile(sys_file): - files.append(sys_file) - - # What to call the per-user config file - if os.name == 'posix': - user_filename = ".pydistutils.cfg" - else: - user_filename = "pydistutils.cfg" - - # And look for the user config file - if self.want_user_cfg: - user_file = os.path.join(os.path.expanduser('~'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) - - # All platforms support local setup.cfg - local_file = "setup.cfg" - if os.path.isfile(local_file): - files.append(local_file) - - if DEBUG: - self.announce("using config files: %s" % ', '.join(files)) - - return files - - def parse_config_files(self, filenames=None): - from configparser import ConfigParser - - # Ignore install directory options if we have a venv - if sys.prefix != sys.base_prefix: - ignore_options = [ - 'install-base', 'install-platbase', 'install-lib', - 'install-platlib', 'install-purelib', 'install-headers', - 'install-scripts', 'install-data', 'prefix', 'exec-prefix', - 'home', 'user', 'root'] - else: - ignore_options = [] - - ignore_options = frozenset(ignore_options) - - if filenames is None: - filenames = self.find_config_files() - - if DEBUG: - self.announce("Distribution.parse_config_files():") - - parser = ConfigParser() - for filename in filenames: - if DEBUG: - self.announce(" reading %s" % filename) - parser.read(filename) - for section in parser.sections(): - options = parser.options(section) - opt_dict = self.get_option_dict(section) - - for opt in options: - if opt != '__name__' and opt not in ignore_options: - val = parser.get(section,opt) - opt = opt.replace('-', '_') - opt_dict[opt] = (filename, val) - - # Make the ConfigParser forget everything (so we retain - # the original filenames that options come from) - parser.__init__() - - # If there was a "global" section in the config file, use it - # to set Distribution options. - - if 'global' in self.command_options: - for (opt, (src, val)) in self.command_options['global'].items(): - alias = self.negative_opt.get(opt) - try: - if alias: - setattr(self, alias, not strtobool(val)) - elif opt in ('verbose', 'dry_run'): # ugh! - setattr(self, opt, strtobool(val)) - else: - setattr(self, opt, val) - except ValueError as msg: - raise DistutilsOptionError(msg) - - # -- Command-line parsing methods ---------------------------------- - - def parse_command_line(self): - """Parse the setup script's command line, taken from the - 'script_args' instance attribute (which defaults to 'sys.argv[1:]' - -- see 'setup()' in core.py). This list is first processed for - "global options" -- options that set attributes of the Distribution - instance. Then, it is alternately scanned for Distutils commands - and options for that command. Each new command terminates the - options for the previous command. The allowed options for a - command are determined by the 'user_options' attribute of the - command class -- thus, we have to be able to load command classes - in order to parse the command line. Any error in that 'options' - attribute raises DistutilsGetoptError; any error on the - command-line raises DistutilsArgError. If no Distutils commands - were found on the command line, raises DistutilsArgError. Return - true if command-line was successfully parsed and we should carry - on with executing commands; false if no errors but we shouldn't - execute commands (currently, this only happens if user asks for - help). - """ - # - # We now have enough information to show the Macintosh dialog - # that allows the user to interactively specify the "command line". - # - toplevel_options = self._get_toplevel_options() - - # We have to parse the command line a bit at a time -- global - # options, then the first command, then its options, and so on -- - # because each command will be handled by a different class, and - # the options that are valid for a particular class aren't known - # until we have loaded the command class, which doesn't happen - # until we know what the command is. - - self.commands = [] - parser = FancyGetopt(toplevel_options + self.display_options) - parser.set_negative_aliases(self.negative_opt) - parser.set_aliases({'licence': 'license'}) - args = parser.getopt(args=self.script_args, object=self) - option_order = parser.get_option_order() - log.set_verbosity(self.verbose) - - # for display options we return immediately - if self.handle_display_options(option_order): - return - while args: - args = self._parse_command_opts(parser, args) - if args is None: # user asked for help (and got it) - return - - # Handle the cases of --help as a "global" option, ie. - # "setup.py --help" and "setup.py --help command ...". For the - # former, we show global options (--verbose, --dry-run, etc.) - # and display-only options (--name, --version, etc.); for the - # latter, we omit the display-only options and show help for - # each command listed on the command line. - if self.help: - self._show_help(parser, - display_options=len(self.commands) == 0, - commands=self.commands) - return - - # Oops, no commands found -- an end-user error - if not self.commands: - raise DistutilsArgError("no commands supplied") - - # All is well: return true - return True - - def _get_toplevel_options(self): - """Return the non-display options recognized at the top level. - - This includes options that are recognized *only* at the top - level as well as options recognized for commands. - """ - return self.global_options + [ - ("command-packages=", None, - "list of packages that provide distutils commands"), - ] - - def _parse_command_opts(self, parser, args): - """Parse the command-line options for a single command. - 'parser' must be a FancyGetopt instance; 'args' must be the list - of arguments, starting with the current command (whose options - we are about to parse). Returns a new version of 'args' with - the next command at the front of the list; will be the empty - list if there are no more commands on the command line. Returns - None if the user asked for help on this command. - """ - # late import because of mutual dependence between these modules - from distutils.cmd import Command - - # Pull the current command from the head of the command line - command = args[0] - if not command_re.match(command): - raise SystemExit("invalid command name '%s'" % command) - self.commands.append(command) - - # Dig up the command class that implements this command, so we - # 1) know that it's a valid command, and 2) know which options - # it takes. - try: - cmd_class = self.get_command_class(command) - except DistutilsModuleError as msg: - raise DistutilsArgError(msg) - - # Require that the command class be derived from Command -- want - # to be sure that the basic "command" interface is implemented. - if not issubclass(cmd_class, Command): - raise DistutilsClassError( - "command class %s must subclass Command" % cmd_class) - - # Also make sure that the command object provides a list of its - # known options. - if not (hasattr(cmd_class, 'user_options') and - isinstance(cmd_class.user_options, list)): - msg = ("command class %s must provide " - "'user_options' attribute (a list of tuples)") - raise DistutilsClassError(msg % cmd_class) - - # If the command class has a list of negative alias options, - # merge it in with the global negative aliases. - negative_opt = self.negative_opt - if hasattr(cmd_class, 'negative_opt'): - negative_opt = negative_opt.copy() - negative_opt.update(cmd_class.negative_opt) - - # Check for help_options in command class. They have a different - # format (tuple of four) so we need to preprocess them here. - if (hasattr(cmd_class, 'help_options') and - isinstance(cmd_class.help_options, list)): - help_options = fix_help_options(cmd_class.help_options) - else: - help_options = [] - - # All commands support the global options too, just by adding - # in 'global_options'. - parser.set_option_table(self.global_options + - cmd_class.user_options + - help_options) - parser.set_negative_aliases(negative_opt) - (args, opts) = parser.getopt(args[1:]) - if hasattr(opts, 'help') and opts.help: - self._show_help(parser, display_options=0, commands=[cmd_class]) - return - - if (hasattr(cmd_class, 'help_options') and - isinstance(cmd_class.help_options, list)): - help_option_found=0 - for (help_option, short, desc, func) in cmd_class.help_options: - if hasattr(opts, parser.get_attr_name(help_option)): - help_option_found=1 - if callable(func): - func() - else: - raise DistutilsClassError( - "invalid help function %r for help option '%s': " - "must be a callable object (function, etc.)" - % (func, help_option)) - - if help_option_found: - return - - # Put the options from the command-line into their official - # holding pen, the 'command_options' dictionary. - opt_dict = self.get_option_dict(command) - for (name, value) in vars(opts).items(): - opt_dict[name] = ("command line", value) - - return args - - def finalize_options(self): - """Set final values for all the options on the Distribution - instance, analogous to the .finalize_options() method of Command - objects. - """ - for attr in ('keywords', 'platforms'): - value = getattr(self.metadata, attr) - if value is None: - continue - if isinstance(value, str): - value = [elm.strip() for elm in value.split(',')] - setattr(self.metadata, attr, value) - - def _show_help(self, parser, global_options=1, display_options=1, - commands=[]): - """Show help for the setup script command-line in the form of - several lists of command-line options. 'parser' should be a - FancyGetopt instance; do not expect it to be returned in the - same state, as its option table will be reset to make it - generate the correct help text. - - If 'global_options' is true, lists the global options: - --verbose, --dry-run, etc. If 'display_options' is true, lists - the "display-only" options: --name, --version, etc. Finally, - lists per-command help for every command name or command class - in 'commands'. - """ - # late import because of mutual dependence between these modules - from distutils.core import gen_usage - from distutils.cmd import Command - - if global_options: - if display_options: - options = self._get_toplevel_options() - else: - options = self.global_options - parser.set_option_table(options) - parser.print_help(self.common_usage + "\nGlobal options:") - print('') - - if display_options: - parser.set_option_table(self.display_options) - parser.print_help( - "Information display options (just display " + - "information, ignore any commands)") - print('') - - for command in self.commands: - if isinstance(command, type) and issubclass(command, Command): - klass = command - else: - klass = self.get_command_class(command) - if (hasattr(klass, 'help_options') and - isinstance(klass.help_options, list)): - parser.set_option_table(klass.user_options + - fix_help_options(klass.help_options)) - else: - parser.set_option_table(klass.user_options) - parser.print_help("Options for '%s' command:" % klass.__name__) - print('') - - print(gen_usage(self.script_name)) - - def handle_display_options(self, option_order): - """If there were any non-global "display-only" options - (--help-commands or the metadata display options) on the command - line, display the requested info and return true; else return - false. - """ - from distutils.core import gen_usage - - # User just wants a list of commands -- we'll print it out and stop - # processing now (ie. if they ran "setup --help-commands foo bar", - # we ignore "foo bar"). - if self.help_commands: - self.print_commands() - print('') - print(gen_usage(self.script_name)) - return 1 - - # If user supplied any of the "display metadata" options, then - # display that metadata in the order in which the user supplied the - # metadata options. - any_display_options = 0 - is_display_option = {} - for option in self.display_options: - is_display_option[option[0]] = 1 - - for (opt, val) in option_order: - if val and is_display_option.get(opt): - opt = translate_longopt(opt) - value = getattr(self.metadata, "get_"+opt)() - if opt in ['keywords', 'platforms']: - print(','.join(value)) - elif opt in ('classifiers', 'provides', 'requires', - 'obsoletes'): - print('\n'.join(value)) - else: - print(value) - any_display_options = 1 - - return any_display_options - - def print_command_list(self, commands, header, max_length): - """Print a subset of the list of all commands -- used by - 'print_commands()'. - """ - print(header + ":") - - for cmd in commands: - klass = self.cmdclass.get(cmd) - if not klass: - klass = self.get_command_class(cmd) - try: - description = klass.description - except AttributeError: - description = "(no description available)" - - print(" %-*s %s" % (max_length, cmd, description)) - - def print_commands(self): - """Print out a help message listing all available commands with a - description of each. The list is divided into "standard commands" - (listed in distutils.command.__all__) and "extra commands" - (mentioned in self.cmdclass, but not a standard command). The - descriptions come from the command class attribute - 'description'. - """ - import distutils.command - std_commands = distutils.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append(cmd) - - max_length = 0 - for cmd in (std_commands + extra_commands): - if len(cmd) > max_length: - max_length = len(cmd) - - self.print_command_list(std_commands, - "Standard commands", - max_length) - if extra_commands: - print() - self.print_command_list(extra_commands, - "Extra commands", - max_length) - - def get_command_list(self): - """Get a list of (command, description) tuples. - The list is divided into "standard commands" (listed in - distutils.command.__all__) and "extra commands" (mentioned in - self.cmdclass, but not a standard command). The descriptions come - from the command class attribute 'description'. - """ - # Currently this is only used on Mac OS, for the Mac-only GUI - # Distutils interface (by Jack Jansen) - import distutils.command - std_commands = distutils.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append(cmd) - - rv = [] - for cmd in (std_commands + extra_commands): - klass = self.cmdclass.get(cmd) - if not klass: - klass = self.get_command_class(cmd) - try: - description = klass.description - except AttributeError: - description = "(no description available)" - rv.append((cmd, description)) - return rv - - # -- Command class/object methods ---------------------------------- - - def get_command_packages(self): - """Return a list of packages from which commands are loaded.""" - pkgs = self.command_packages - if not isinstance(pkgs, list): - if pkgs is None: - pkgs = '' - pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != ''] - if "distutils.command" not in pkgs: - pkgs.insert(0, "distutils.command") - self.command_packages = pkgs - return pkgs - - def get_command_class(self, command): - """Return the class that implements the Distutils command named by - 'command'. First we check the 'cmdclass' dictionary; if the - command is mentioned there, we fetch the class object from the - dictionary and return it. Otherwise we load the command module - ("distutils.command." + command) and fetch the command class from - the module. The loaded class is also stored in 'cmdclass' - to speed future calls to 'get_command_class()'. - - Raises DistutilsModuleError if the expected module could not be - found, or if that module does not define the expected class. - """ - klass = self.cmdclass.get(command) - if klass: - return klass - - for pkgname in self.get_command_packages(): - module_name = "%s.%s" % (pkgname, command) - klass_name = command - - try: - __import__(module_name) - module = sys.modules[module_name] - except ImportError: - continue - - try: - klass = getattr(module, klass_name) - except AttributeError: - raise DistutilsModuleError( - "invalid command '%s' (no class '%s' in module '%s')" - % (command, klass_name, module_name)) - - self.cmdclass[command] = klass - return klass - - raise DistutilsModuleError("invalid command '%s'" % command) - - def get_command_obj(self, command, create=1): - """Return the command object for 'command'. Normally this object - is cached on a previous call to 'get_command_obj()'; if no command - object for 'command' is in the cache, then we either create and - return it (if 'create' is true) or return None. - """ - cmd_obj = self.command_obj.get(command) - if not cmd_obj and create: - if DEBUG: - self.announce("Distribution.get_command_obj(): " - "creating '%s' command object" % command) - - klass = self.get_command_class(command) - cmd_obj = self.command_obj[command] = klass(self) - self.have_run[command] = 0 - - # Set any options that were supplied in config files - # or on the command line. (NB. support for error - # reporting is lame here: any errors aren't reported - # until 'finalize_options()' is called, which means - # we won't report the source of the error.) - options = self.command_options.get(command) - if options: - self._set_command_options(cmd_obj, options) - - return cmd_obj - - def _set_command_options(self, command_obj, option_dict=None): - """Set the options for 'command_obj' from 'option_dict'. Basically - this means copying elements of a dictionary ('option_dict') to - attributes of an instance ('command'). - - 'command_obj' must be a Command instance. If 'option_dict' is not - supplied, uses the standard option dictionary for this command - (from 'self.command_options'). - """ - command_name = command_obj.get_command_name() - if option_dict is None: - option_dict = self.get_option_dict(command_name) - - if DEBUG: - self.announce(" setting options for '%s' command:" % command_name) - for (option, (source, value)) in option_dict.items(): - if DEBUG: - self.announce(" %s = %s (from %s)" % (option, value, - source)) - try: - bool_opts = [translate_longopt(o) - for o in command_obj.boolean_options] - except AttributeError: - bool_opts = [] - try: - neg_opt = command_obj.negative_opt - except AttributeError: - neg_opt = {} - - try: - is_string = isinstance(value, str) - if option in neg_opt and is_string: - setattr(command_obj, neg_opt[option], not strtobool(value)) - elif option in bool_opts and is_string: - setattr(command_obj, option, strtobool(value)) - elif hasattr(command_obj, option): - setattr(command_obj, option, value) - else: - raise DistutilsOptionError( - "error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) - except ValueError as msg: - raise DistutilsOptionError(msg) - - def reinitialize_command(self, command, reinit_subcommands=0): - """Reinitializes a command to the state it was in when first - returned by 'get_command_obj()': ie., initialized but not yet - finalized. This provides the opportunity to sneak option - values in programmatically, overriding or supplementing - user-supplied values from the config files and command line. - You'll have to re-finalize the command object (by calling - 'finalize_options()' or 'ensure_finalized()') before using it for - real. - - 'command' should be a command name (string) or command object. If - 'reinit_subcommands' is true, also reinitializes the command's - sub-commands, as declared by the 'sub_commands' class attribute (if - it has one). See the "install" command for an example. Only - reinitializes the sub-commands that actually matter, ie. those - whose test predicates return true. - - Returns the reinitialized command object. - """ - from distutils.cmd import Command - if not isinstance(command, Command): - command_name = command - command = self.get_command_obj(command_name) - else: - command_name = command.get_command_name() - - if not command.finalized: - return command - command.initialize_options() - command.finalized = 0 - self.have_run[command_name] = 0 - self._set_command_options(command) - - if reinit_subcommands: - for sub in command.get_sub_commands(): - self.reinitialize_command(sub, reinit_subcommands) - - return command - - # -- Methods that operate on the Distribution ---------------------- - - def announce(self, msg, level=log.INFO): - log.log(level, msg) - - def run_commands(self): - """Run each command that was seen on the setup script command line. - Uses the list of commands found and cache of command objects - created by 'get_command_obj()'. - """ - for cmd in self.commands: - self.run_command(cmd) - - # -- Methods that operate on its Commands -------------------------- - - def run_command(self, command): - """Do whatever it takes to run a command (including nothing at all, - if the command has already been run). Specifically: if we have - already created and run the command named by 'command', return - silently without doing anything. If the command named by 'command' - doesn't even have a command object yet, create one. Then invoke - 'run()' on that command object (or an existing one). - """ - # Already been here, done that? then return silently. - if self.have_run.get(command): - return - - log.info("running %s", command) - cmd_obj = self.get_command_obj(command) - cmd_obj.ensure_finalized() - cmd_obj.run() - self.have_run[command] = 1 - - # -- Distribution query methods ------------------------------------ - - def has_pure_modules(self): - return len(self.packages or self.py_modules or []) > 0 - - def has_ext_modules(self): - return self.ext_modules and len(self.ext_modules) > 0 - - def has_c_libraries(self): - return self.libraries and len(self.libraries) > 0 - - def has_modules(self): - return self.has_pure_modules() or self.has_ext_modules() - - def has_headers(self): - return self.headers and len(self.headers) > 0 - - def has_scripts(self): - return self.scripts and len(self.scripts) > 0 - - def has_data_files(self): - return self.data_files and len(self.data_files) > 0 - - def is_pure(self): - return (self.has_pure_modules() and - not self.has_ext_modules() and - not self.has_c_libraries()) - - # -- Metadata query methods ---------------------------------------- - - # If you're looking for 'get_name()', 'get_version()', and so forth, - # they are defined in a sneaky way: the constructor binds self.get_XXX - # to self.metadata.get_XXX. The actual code is in the - # DistributionMetadata class, below. - -class DistributionMetadata: - """Dummy class to hold the distribution meta-data: name, version, - author, and so forth. - """ - - _METHOD_BASENAMES = ("name", "version", "author", "author_email", - "maintainer", "maintainer_email", "url", - "license", "description", "long_description", - "keywords", "platforms", "fullname", "contact", - "contact_email", "classifiers", "download_url", - # PEP 314 - "provides", "requires", "obsoletes", - ) - - def __init__(self, path=None): - if path is not None: - self.read_pkg_file(open(path)) - else: - self.name = None - self.version = None - self.author = None - self.author_email = None - self.maintainer = None - self.maintainer_email = None - self.url = None - self.license = None - self.description = None - self.long_description = None - self.keywords = None - self.platforms = None - self.classifiers = None - self.download_url = None - # PEP 314 - self.provides = None - self.requires = None - self.obsoletes = None - - def read_pkg_file(self, file): - """Reads the metadata values from a file object.""" - msg = message_from_file(file) - - def _read_field(name): - value = msg[name] - if value == 'UNKNOWN': - return None - return value - - def _read_list(name): - values = msg.get_all(name, None) - if values == []: - return None - return values - - metadata_version = msg['metadata-version'] - self.name = _read_field('name') - self.version = _read_field('version') - self.description = _read_field('summary') - # we are filling author only. - self.author = _read_field('author') - self.maintainer = None - self.author_email = _read_field('author-email') - self.maintainer_email = None - self.url = _read_field('home-page') - self.license = _read_field('license') - - if 'download-url' in msg: - self.download_url = _read_field('download-url') - else: - self.download_url = None - - self.long_description = _read_field('description') - self.description = _read_field('summary') - - if 'keywords' in msg: - self.keywords = _read_field('keywords').split(',') - - self.platforms = _read_list('platform') - self.classifiers = _read_list('classifier') - - # PEP 314 - these fields only exist in 1.1 - if metadata_version == '1.1': - self.requires = _read_list('requires') - self.provides = _read_list('provides') - self.obsoletes = _read_list('obsoletes') - else: - self.requires = None - self.provides = None - self.obsoletes = None - - def write_pkg_info(self, base_dir): - """Write the PKG-INFO file into the release tree. - """ - with open(os.path.join(base_dir, 'PKG-INFO'), 'w', - encoding='UTF-8') as pkg_info: - self.write_pkg_file(pkg_info) - - def write_pkg_file(self, file): - """Write the PKG-INFO format data to a file object. - """ - version = '1.0' - if (self.provides or self.requires or self.obsoletes or - self.classifiers or self.download_url): - version = '1.1' - - file.write('Metadata-Version: %s\n' % version) - file.write('Name: %s\n' % self.get_name()) - file.write('Version: %s\n' % self.get_version()) - file.write('Summary: %s\n' % self.get_description()) - file.write('Home-page: %s\n' % self.get_url()) - file.write('Author: %s\n' % self.get_contact()) - file.write('Author-email: %s\n' % self.get_contact_email()) - file.write('License: %s\n' % self.get_license()) - if self.download_url: - file.write('Download-URL: %s\n' % self.download_url) - - long_desc = rfc822_escape(self.get_long_description()) - file.write('Description: %s\n' % long_desc) - - keywords = ','.join(self.get_keywords()) - if keywords: - file.write('Keywords: %s\n' % keywords) - - self._write_list(file, 'Platform', self.get_platforms()) - self._write_list(file, 'Classifier', self.get_classifiers()) - - # PEP 314 - self._write_list(file, 'Requires', self.get_requires()) - self._write_list(file, 'Provides', self.get_provides()) - self._write_list(file, 'Obsoletes', self.get_obsoletes()) - - def _write_list(self, file, name, values): - for value in values: - file.write('%s: %s\n' % (name, value)) - - # -- Metadata query methods ---------------------------------------- - - def get_name(self): - return self.name or "UNKNOWN" - - def get_version(self): - return self.version or "0.0.0" - - def get_fullname(self): - return "%s-%s" % (self.get_name(), self.get_version()) - - def get_author(self): - return self.author or "UNKNOWN" - - def get_author_email(self): - return self.author_email or "UNKNOWN" - - def get_maintainer(self): - return self.maintainer or "UNKNOWN" - - def get_maintainer_email(self): - return self.maintainer_email or "UNKNOWN" - - def get_contact(self): - return self.maintainer or self.author or "UNKNOWN" - - def get_contact_email(self): - return self.maintainer_email or self.author_email or "UNKNOWN" - - def get_url(self): - return self.url or "UNKNOWN" - - def get_license(self): - return self.license or "UNKNOWN" - get_licence = get_license - - def get_description(self): - return self.description or "UNKNOWN" - - def get_long_description(self): - return self.long_description or "UNKNOWN" - - def get_keywords(self): - return self.keywords or [] - - def set_keywords(self, value): - self.keywords = _ensure_list(value, 'keywords') - - def get_platforms(self): - return self.platforms or ["UNKNOWN"] - - def set_platforms(self, value): - self.platforms = _ensure_list(value, 'platforms') - - def get_classifiers(self): - return self.classifiers or [] - - def set_classifiers(self, value): - self.classifiers = _ensure_list(value, 'classifiers') - - def get_download_url(self): - return self.download_url or "UNKNOWN" - - # PEP 314 - def get_requires(self): - return self.requires or [] - - def set_requires(self, value): - import distutils.versionpredicate - for v in value: - distutils.versionpredicate.VersionPredicate(v) - self.requires = list(value) - - def get_provides(self): - return self.provides or [] - - def set_provides(self, value): - value = [v.strip() for v in value] - for v in value: - import distutils.versionpredicate - distutils.versionpredicate.split_provision(v) - self.provides = value - - def get_obsoletes(self): - return self.obsoletes or [] - - def set_obsoletes(self, value): - import distutils.versionpredicate - for v in value: - distutils.versionpredicate.VersionPredicate(v) - self.obsoletes = list(value) - -def fix_help_options(options): - """Convert a 4-tuple 'help_options' list as found in various command - classes to the 3-tuple form required by FancyGetopt. - """ - new_options = [] - for help_tuple in options: - new_options.append(help_tuple[0:3]) - return new_options diff --git a/distutils/README b/distutils/README new file mode 100644 index 00000000..23f48850 --- /dev/null +++ b/distutils/README @@ -0,0 +1,11 @@ +This directory contains the Distutils package. + +There's a full documentation available at: + + http://docs.python.org/distutils/ + +The Distutils-SIG web page is also a good starting point: + + http://www.python.org/sigs/distutils-sig/ + +$Id$ diff --git a/distutils/__init__.py b/distutils/__init__.py new file mode 100644 index 00000000..d823d040 --- /dev/null +++ b/distutils/__init__.py @@ -0,0 +1,13 @@ +"""distutils + +The main package for the Python Module Distribution Utilities. Normally +used from a setup script as + + from distutils.core import setup + + setup (...) +""" + +import sys + +__version__ = sys.version[:sys.version.index(' ')] diff --git a/distutils/_msvccompiler.py b/distutils/_msvccompiler.py new file mode 100644 index 00000000..af8099a4 --- /dev/null +++ b/distutils/_msvccompiler.py @@ -0,0 +1,539 @@ +"""distutils._msvccompiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for Microsoft Visual Studio 2015. + +The module is compatible with VS 2015 and later. You can find legacy support +for older versions in distutils.msvc9compiler and distutils.msvccompiler. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) +# ported to VS 2005 and VS 2008 by Christian Heimes +# ported to VS 2015 by Steve Dower + +import os +import subprocess +import winreg + +from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import CCompiler, gen_lib_options +from distutils import log +from distutils.util import get_platform + +from itertools import count + +def _find_vc2015(): + try: + key = winreg.OpenKeyEx( + winreg.HKEY_LOCAL_MACHINE, + r"Software\Microsoft\VisualStudio\SxS\VC7", + access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY + ) + except OSError: + log.debug("Visual C++ is not registered") + return None, None + + best_version = 0 + best_dir = None + with key: + for i in count(): + try: + v, vc_dir, vt = winreg.EnumValue(key, i) + except OSError: + break + if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir): + try: + version = int(float(v)) + except (ValueError, TypeError): + continue + if version >= 14 and version > best_version: + best_version, best_dir = version, vc_dir + return best_version, best_dir + +def _find_vc2017(): + """Returns "15, path" based on the result of invoking vswhere.exe + If no install is found, returns "None, None" + + The version is returned to avoid unnecessarily changing the function + result. It may be ignored when the path is not None. + + If vswhere.exe is not available, by definition, VS 2017 is not + installed. + """ + root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") + if not root: + return None, None + + try: + path = subprocess.check_output([ + os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), + "-latest", + "-prerelease", + "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "installationPath", + "-products", "*", + ], encoding="mbcs", errors="strict").strip() + except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): + return None, None + + path = os.path.join(path, "VC", "Auxiliary", "Build") + if os.path.isdir(path): + return 15, path + + return None, None + +PLAT_SPEC_TO_RUNTIME = { + 'x86' : 'x86', + 'x86_amd64' : 'x64', + 'x86_arm' : 'arm', + 'x86_arm64' : 'arm64' +} + +def _find_vcvarsall(plat_spec): + # bpo-38597: Removed vcruntime return value + _, best_dir = _find_vc2017() + + if not best_dir: + best_version, best_dir = _find_vc2015() + + if not best_dir: + log.debug("No suitable Visual C++ version found") + return None, None + + vcvarsall = os.path.join(best_dir, "vcvarsall.bat") + if not os.path.isfile(vcvarsall): + log.debug("%s cannot be found", vcvarsall) + return None, None + + return vcvarsall, None + +def _get_vc_env(plat_spec): + if os.getenv("DISTUTILS_USE_SDK"): + return { + key.lower(): value + for key, value in os.environ.items() + } + + vcvarsall, _ = _find_vcvarsall(plat_spec) + if not vcvarsall: + raise DistutilsPlatformError("Unable to find vcvarsall.bat") + + try: + out = subprocess.check_output( + 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), + stderr=subprocess.STDOUT, + ).decode('utf-16le', errors='replace') + except subprocess.CalledProcessError as exc: + log.error(exc.output) + raise DistutilsPlatformError("Error executing {}" + .format(exc.cmd)) + + env = { + key.lower(): value + for key, _, value in + (line.partition('=') for line in out.splitlines()) + if key and value + } + + return env + +def _find_exe(exe, paths=None): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + if not paths: + paths = os.getenv('path').split(os.pathsep) + for p in paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + return exe + +# A map keyed by get_platform() return values to values accepted by +# 'vcvarsall.bat'. Always cross-compile from x86 to work with the +# lighter-weight MSVC installs that do not include native 64-bit tools. +PLAT_TO_VCVARS = { + 'win32' : 'x86', + 'win-amd64' : 'x86_amd64', + 'win-arm32' : 'x86_arm', + 'win-arm64' : 'x86_arm64' +} + +class MSVCCompiler(CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) + # target platform (.plat_name is consistent with 'bdist') + self.plat_name = None + self.initialized = False + + def initialize(self, plat_name=None): + # multi-init means we would need to check platform same each time... + assert not self.initialized, "don't init multiple times" + if plat_name is None: + plat_name = get_platform() + # sanity check for platforms to prevent obscure errors later. + if plat_name not in PLAT_TO_VCVARS: + raise DistutilsPlatformError("--plat-name must be one of {}" + .format(tuple(PLAT_TO_VCVARS))) + + # Get the vcvarsall.bat spec for the requested platform. + plat_spec = PLAT_TO_VCVARS[plat_name] + + vc_env = _get_vc_env(plat_spec) + if not vc_env: + raise DistutilsPlatformError("Unable to find a compatible " + "Visual Studio installation.") + + self._paths = vc_env.get('path', '') + paths = self._paths.split(os.pathsep) + self.cc = _find_exe("cl.exe", paths) + self.linker = _find_exe("link.exe", paths) + self.lib = _find_exe("lib.exe", paths) + self.rc = _find_exe("rc.exe", paths) # resource compiler + self.mc = _find_exe("mc.exe", paths) # message compiler + self.mt = _find_exe("mt.exe", paths) # message compiler + + for dir in vc_env.get('include', '').split(os.pathsep): + if dir: + self.add_include_dir(dir.rstrip(os.sep)) + + for dir in vc_env.get('lib', '').split(os.pathsep): + if dir: + self.add_library_dir(dir.rstrip(os.sep)) + + self.preprocess_options = None + # bpo-38597: Always compile with dynamic linking + # Future releases of Python 3.x will include all past + # versions of vcruntime*.dll for compatibility. + self.compile_options = [ + '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD' + ] + + self.compile_options_debug = [ + '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' + ] + + ldflags = [ + '/nologo', '/INCREMENTAL:NO', '/LTCG' + ] + + ldflags_debug = [ + '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL' + ] + + self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1'] + self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1'] + self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] + self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] + self.ldflags_static = [*ldflags] + self.ldflags_static_debug = [*ldflags_debug] + + self._ldflags = { + (CCompiler.EXECUTABLE, None): self.ldflags_exe, + (CCompiler.EXECUTABLE, False): self.ldflags_exe, + (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug, + (CCompiler.SHARED_OBJECT, None): self.ldflags_shared, + (CCompiler.SHARED_OBJECT, False): self.ldflags_shared, + (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug, + (CCompiler.SHARED_LIBRARY, None): self.ldflags_static, + (CCompiler.SHARED_LIBRARY, False): self.ldflags_static, + (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug, + } + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): + ext_map = { + **{ext: self.obj_extension for ext in self.src_extensions}, + **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions}, + } + + output_dir = output_dir or '' + + def make_out_path(p): + base, ext = os.path.splitext(p) + if strip_dir: + base = os.path.basename(base) + else: + _, base = os.path.splitdrive(base) + if base.startswith((os.path.sep, os.path.altsep)): + base = base[1:] + try: + # XXX: This may produce absurdly long paths. We should check + # the length of the result and trim base until we fit within + # 260 characters. + return os.path.join(output_dir, base + ext_map[ext]) + except LookupError: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError("Don't know how to compile {}".format(p)) + + return list(map(make_out_path, source_filenames)) + + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + + add_cpp_opts = False + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + add_cpp_opts = True + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + [output_opt, input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src]) + base, _ = os.path.splitext(os.path.basename (src)) + rc_file = os.path.join(rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc, "/fo" + obj, rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError("Don't know how to compile {} to {}" + .format(src, obj)) + + args = [self.cc] + compile_opts + pp_opts + if add_cpp_opts: + args.append('/EHsc') + args.append(input_opt) + args.append("/Fo" + obj) + args.extend(extra_postargs) + + try: + self.spawn(args) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + if not self.initialized: + self.initialize() + objects, output_dir = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args)) + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + objects, output_dir = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + libraries, library_dirs, runtime_library_dirs = fixed_args + + if runtime_library_dirs: + self.warn("I don't know what to do with 'runtime_library_dirs': " + + str(runtime_library_dirs)) + + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + ldflags = self._ldflags[target_desc, debug] + + export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])] + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + build_temp = os.path.dirname(objects[0]) + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + build_temp, + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + output_dir = os.path.dirname(os.path.abspath(output_filename)) + self.mkpath(output_dir) + try: + log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args)) + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def spawn(self, cmd): + old_path = os.getenv('path') + try: + os.environ['path'] = self._paths + return super().spawn(cmd) + finally: + os.environ['path'] = old_path + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC") + + def library_option(self, lib): + return self.library_filename(lib) + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename(name)) + if os.path.isfile(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None diff --git a/distutils/archive_util.py b/distutils/archive_util.py new file mode 100644 index 00000000..565a3117 --- /dev/null +++ b/distutils/archive_util.py @@ -0,0 +1,256 @@ +"""distutils.archive_util + +Utility functions for creating archive files (tarballs, zip files, +that sort of thing).""" + +import os +from warnings import warn +import sys + +try: + import zipfile +except ImportError: + zipfile = None + + +from distutils.errors import DistutilsExecError +from distutils.spawn import spawn +from distutils.dir_util import mkpath +from distutils import log + +try: + from pwd import getpwnam +except ImportError: + getpwnam = None + +try: + from grp import getgrnam +except ImportError: + getgrnam = None + +def _get_gid(name): + """Returns a gid, given a group name.""" + if getgrnam is None or name is None: + return None + try: + result = getgrnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _get_uid(name): + """Returns an uid, given a user name.""" + if getpwnam is None or name is None: + return None + try: + result = getpwnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, + owner=None, group=None): + """Create a (possibly compressed) tar file from all the files under + 'base_dir'. + + 'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or + None. ("compress" will be deprecated in Python 3.2) + + 'owner' and 'group' can be used to define an owner and a group for the + archive that is being built. If not provided, the current owner and group + will be used. + + The output tar file will be named 'base_dir' + ".tar", possibly plus + the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z"). + + Returns the output filename. + """ + tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '', + 'compress': ''} + compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', + 'compress': '.Z'} + + # flags for compression program, each element of list will be an argument + if compress is not None and compress not in compress_ext.keys(): + raise ValueError( + "bad value for 'compress': must be None, 'gzip', 'bzip2', " + "'xz' or 'compress'") + + archive_name = base_name + '.tar' + if compress != 'compress': + archive_name += compress_ext.get(compress, '') + + mkpath(os.path.dirname(archive_name), dry_run=dry_run) + + # creating the tarball + import tarfile # late import so Python build itself doesn't break + + log.info('Creating tar archive') + + uid = _get_uid(owner) + gid = _get_gid(group) + + def _set_uid_gid(tarinfo): + if gid is not None: + tarinfo.gid = gid + tarinfo.gname = group + if uid is not None: + tarinfo.uid = uid + tarinfo.uname = owner + return tarinfo + + if not dry_run: + tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) + try: + tar.add(base_dir, filter=_set_uid_gid) + finally: + tar.close() + + # compression using `compress` + if compress == 'compress': + warn("'compress' will be deprecated.", PendingDeprecationWarning) + # the option varies depending on the platform + compressed_name = archive_name + compress_ext[compress] + if sys.platform == 'win32': + cmd = [compress, archive_name, compressed_name] + else: + cmd = [compress, '-f', archive_name] + spawn(cmd, dry_run=dry_run) + return compressed_name + + return archive_name + +def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): + """Create a zip file from all the files under 'base_dir'. + + The output zip file will be named 'base_name' + ".zip". Uses either the + "zipfile" Python module (if available) or the InfoZIP "zip" utility + (if installed and found on the default search path). If neither tool is + available, raises DistutilsExecError. Returns the name of the output zip + file. + """ + zip_filename = base_name + ".zip" + mkpath(os.path.dirname(zip_filename), dry_run=dry_run) + + # If zipfile module is not available, try spawning an external + # 'zip' command. + if zipfile is None: + if verbose: + zipoptions = "-r" + else: + zipoptions = "-rq" + + try: + spawn(["zip", zipoptions, zip_filename, base_dir], + dry_run=dry_run) + except DistutilsExecError: + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed". + raise DistutilsExecError(("unable to create zip file '%s': " + "could neither import the 'zipfile' module nor " + "find a standalone zip utility") % zip_filename) + + else: + log.info("creating '%s' and adding '%s' to it", + zip_filename, base_dir) + + if not dry_run: + try: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) + except RuntimeError: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_STORED) + + with zip: + if base_dir != os.curdir: + path = os.path.normpath(os.path.join(base_dir, '')) + zip.write(path, path) + log.info("adding '%s'", path) + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in dirnames: + path = os.path.normpath(os.path.join(dirpath, name, '')) + zip.write(path, path) + log.info("adding '%s'", path) + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zip.write(path, path) + log.info("adding '%s'", path) + + return zip_filename + +ARCHIVE_FORMATS = { + 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), + 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), + 'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"), + 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"), + 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"), + 'zip': (make_zipfile, [],"ZIP file") + } + +def check_archive_formats(formats): + """Returns the first format from the 'format' list that is unknown. + + If all formats are known, returns None + """ + for format in formats: + if format not in ARCHIVE_FORMATS: + return format + return None + +def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, + dry_run=0, owner=None, group=None): + """Create an archive file (eg. zip or tar). + + 'base_name' is the name of the file to create, minus any format-specific + extension; 'format' is the archive format: one of "zip", "tar", "gztar", + "bztar", "xztar", or "ztar". + + 'root_dir' is a directory that will be the root directory of the + archive; ie. we typically chdir into 'root_dir' before creating the + archive. 'base_dir' is the directory where we start archiving from; + ie. 'base_dir' will be the common prefix of all files and + directories in the archive. 'root_dir' and 'base_dir' both default + to the current directory. Returns the name of the archive file. + + 'owner' and 'group' are used when creating a tar archive. By default, + uses the current owner and group. + """ + save_cwd = os.getcwd() + if root_dir is not None: + log.debug("changing into '%s'", root_dir) + base_name = os.path.abspath(base_name) + if not dry_run: + os.chdir(root_dir) + + if base_dir is None: + base_dir = os.curdir + + kwargs = {'dry_run': dry_run} + + try: + format_info = ARCHIVE_FORMATS[format] + except KeyError: + raise ValueError("unknown archive format '%s'" % format) + + func = format_info[0] + for arg, val in format_info[1]: + kwargs[arg] = val + + if format != 'zip': + kwargs['owner'] = owner + kwargs['group'] = group + + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + log.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) + + return filename diff --git a/distutils/bcppcompiler.py b/distutils/bcppcompiler.py new file mode 100644 index 00000000..071fea5d --- /dev/null +++ b/distutils/bcppcompiler.py @@ -0,0 +1,393 @@ +"""distutils.bcppcompiler + +Contains BorlandCCompiler, an implementation of the abstract CCompiler class +for the Borland C++ compiler. +""" + +# This implementation by Lyle Johnson, based on the original msvccompiler.py +# module and using the directions originally published by Gordon Williams. + +# XXX looks like there's a LOT of overlap between these two classes: +# someone should sit down and factor out the common code as +# WindowsCCompiler! --GPW + + +import os +from distutils.errors import \ + DistutilsExecError, \ + CompileError, LibError, LinkError, UnknownFileError +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options +from distutils.file_util import write_file +from distutils.dep_util import newer +from distutils import log + +class BCPPCompiler(CCompiler) : + """Concrete class that implements an interface to the Borland C/C++ + compiler, as defined by the CCompiler abstract class. + """ + + compiler_type = 'bcpp' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = _c_extensions + _cpp_extensions + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + + def __init__ (self, + verbose=0, + dry_run=0, + force=0): + + CCompiler.__init__ (self, verbose, dry_run, force) + + # These executables are assumed to all be in the path. + # Borland doesn't seem to use any special registry settings to + # indicate their installation locations. + + self.cc = "bcc32.exe" + self.linker = "ilink32.exe" + self.lib = "tlib.exe" + + self.preprocess_options = None + self.compile_options = ['/tWM', '/O2', '/q', '/g0'] + self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0'] + + self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x'] + self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x'] + self.ldflags_static = [] + self.ldflags_exe = ['/Gn', '/q', '/x'] + self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r'] + + + # -- Worker methods ------------------------------------------------ + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + macros, objects, extra_postargs, pp_opts, build = \ + self._setup_compile(output_dir, macros, include_dirs, sources, + depends, extra_postargs) + compile_opts = extra_preargs or [] + compile_opts.append ('-c') + if debug: + compile_opts.extend (self.compile_options_debug) + else: + compile_opts.extend (self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + # XXX why do the normpath here? + src = os.path.normpath(src) + obj = os.path.normpath(obj) + # XXX _setup_compile() did a mkpath() too but before the normpath. + # Is it possible to skip the normpath? + self.mkpath(os.path.dirname(obj)) + + if ext == '.res': + # This is already a binary file -- skip it. + continue # the 'for' loop + if ext == '.rc': + # This needs to be compiled to a .res file -- do it now. + try: + self.spawn (["brcc32", "-fo", obj, src]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue # the 'for' loop + + # The next two are both for the real compiler. + if ext in self._c_extensions: + input_opt = "" + elif ext in self._cpp_extensions: + input_opt = "-P" + else: + # Unknown file type -- no extra options. The compiler + # will probably fail, but let it just in case this is a + # file the compiler recognizes even if we don't. + input_opt = "" + + output_opt = "-o" + obj + + # Compiler command line syntax is: "bcc32 [options] file(s)". + # Note that the source file names must appear at the end of + # the command line. + try: + self.spawn ([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs + [src]) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + # compile () + + + def create_static_lib (self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + (objects, output_dir) = self._fix_object_args (objects, output_dir) + output_filename = \ + self.library_filename (output_libname, output_dir=output_dir) + + if self._need_link (objects, output_filename): + lib_args = [output_filename, '/u'] + objects + if debug: + pass # XXX what goes here? + try: + self.spawn ([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + # create_static_lib () + + + def link (self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + # XXX this ignores 'build_temp'! should follow the lead of + # msvccompiler.py + + (objects, output_dir) = self._fix_object_args (objects, output_dir) + (libraries, library_dirs, runtime_library_dirs) = \ + self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) + + if runtime_library_dirs: + log.warn("I don't know what to do with 'runtime_library_dirs': %s", + str(runtime_library_dirs)) + + if output_dir is not None: + output_filename = os.path.join (output_dir, output_filename) + + if self._need_link (objects, output_filename): + + # Figure out linker args based on type of target. + if target_desc == CCompiler.EXECUTABLE: + startup_obj = 'c0w32' + if debug: + ld_args = self.ldflags_exe_debug[:] + else: + ld_args = self.ldflags_exe[:] + else: + startup_obj = 'c0d32' + if debug: + ld_args = self.ldflags_shared_debug[:] + else: + ld_args = self.ldflags_shared[:] + + + # Create a temporary exports file for use by the linker + if export_symbols is None: + def_file = '' + else: + head, tail = os.path.split (output_filename) + modname, ext = os.path.splitext (tail) + temp_dir = os.path.dirname(objects[0]) # preserve tree structure + def_file = os.path.join (temp_dir, '%s.def' % modname) + contents = ['EXPORTS'] + for sym in (export_symbols or []): + contents.append(' %s=_%s' % (sym, sym)) + self.execute(write_file, (def_file, contents), + "writing %s" % def_file) + + # Borland C++ has problems with '/' in paths + objects2 = map(os.path.normpath, objects) + # split objects in .obj and .res files + # Borland C++ needs them at different positions in the command line + objects = [startup_obj] + resources = [] + for file in objects2: + (base, ext) = os.path.splitext(os.path.normcase(file)) + if ext == '.res': + resources.append(file) + else: + objects.append(file) + + + for l in library_dirs: + ld_args.append("/L%s" % os.path.normpath(l)) + ld_args.append("/L.") # we sometimes use relative paths + + # list of object files + ld_args.extend(objects) + + # XXX the command-line syntax for Borland C++ is a bit wonky; + # certain filenames are jammed together in one big string, but + # comma-delimited. This doesn't mesh too well with the + # Unix-centric attitude (with a DOS/Windows quoting hack) of + # 'spawn()', so constructing the argument list is a bit + # awkward. Note that doing the obvious thing and jamming all + # the filenames and commas into one argument would be wrong, + # because 'spawn()' would quote any filenames with spaces in + # them. Arghghh!. Apparently it works fine as coded... + + # name of dll/exe file + ld_args.extend([',',output_filename]) + # no map file and start libraries + ld_args.append(',,') + + for lib in libraries: + # see if we find it and if there is a bcpp specific lib + # (xxx_bcpp.lib) + libfile = self.find_library_file(library_dirs, lib, debug) + if libfile is None: + ld_args.append(lib) + # probably a BCPP internal library -- don't warn + else: + # full name which prefers bcpp_xxx.lib over xxx.lib + ld_args.append(libfile) + + # some default libraries + ld_args.append ('import32') + ld_args.append ('cw32mt') + + # def file for export symbols + ld_args.extend([',',def_file]) + # add resource files + ld_args.append(',') + ld_args.extend(resources) + + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath (os.path.dirname (output_filename)) + try: + self.spawn ([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + else: + log.debug("skipping %s (up-to-date)", output_filename) + + # link () + + # -- Miscellaneous methods ----------------------------------------- + + + def find_library_file (self, dirs, lib, debug=0): + # List of effective library names to try, in order of preference: + # xxx_bcpp.lib is better than xxx.lib + # and xxx_d.lib is better than xxx.lib if debug is set + # + # The "_bcpp" suffix is to handle a Python installation for people + # with multiple compilers (primarily Distutils hackers, I suspect + # ;-). The idea is they'd have one static library for each + # compiler they care about, since (almost?) every Windows compiler + # seems to have a different format for static libraries. + if debug: + dlib = (lib + "_d") + try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib) + else: + try_names = (lib + "_bcpp", lib) + + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename(name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # overwrite the one from CCompiler to support rc and res-files + def object_filenames (self, + source_filenames, + strip_dir=0, + output_dir=''): + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + # use normcase to make sure '.rc' is really '.rc' and not '.RC' + (base, ext) = os.path.splitext (os.path.normcase(src_name)) + if ext not in (self.src_extensions + ['.rc','.res']): + raise UnknownFileError("unknown file type '%s' (from '%s')" % \ + (ext, src_name)) + if strip_dir: + base = os.path.basename (base) + if ext == '.res': + # these can go unchanged + obj_names.append (os.path.join (output_dir, base + ext)) + elif ext == '.rc': + # these need to be compiled to .res-files + obj_names.append (os.path.join (output_dir, base + '.res')) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + # object_filenames () + + def preprocess (self, + source, + output_file=None, + macros=None, + include_dirs=None, + extra_preargs=None, + extra_postargs=None): + + (_, macros, include_dirs) = \ + self._fix_compile_args(None, macros, include_dirs) + pp_opts = gen_preprocess_options(macros, include_dirs) + pp_args = ['cpp32.exe'] + pp_opts + if output_file is not None: + pp_args.append('-o' + output_file) + if extra_preargs: + pp_args[:0] = extra_preargs + if extra_postargs: + pp_args.extend(extra_postargs) + pp_args.append(source) + + # We need to preprocess: either we're being forced to, or the + # source file is newer than the target (or the target doesn't + # exist). + if self.force or output_file is None or newer(source, output_file): + if output_file: + self.mkpath(os.path.dirname(output_file)) + try: + self.spawn(pp_args) + except DistutilsExecError as msg: + print(msg) + raise CompileError(msg) + + # preprocess() diff --git a/distutils/ccompiler.py b/distutils/ccompiler.py new file mode 100644 index 00000000..b5ef143e --- /dev/null +++ b/distutils/ccompiler.py @@ -0,0 +1,1116 @@ +"""distutils.ccompiler + +Contains CCompiler, an abstract base class that defines the interface +for the Distutils compiler abstraction model.""" + +import sys, os, re +from distutils.errors import * +from distutils.spawn import spawn +from distutils.file_util import move_file +from distutils.dir_util import mkpath +from distutils.dep_util import newer_group +from distutils.util import split_quoted, execute +from distutils import log + +class CCompiler: + """Abstract base class to define the interface that must be implemented + by real compiler classes. Also has some utility methods used by + several compiler classes. + + The basic idea behind a compiler abstraction class is that each + instance can be used for all the compile/link steps in building a + single project. Thus, attributes common to all of those compile and + link steps -- include directories, macros to define, libraries to link + against, etc. -- are attributes of the compiler instance. To allow for + variability in how individual files are treated, most of those + attributes may be varied on a per-compilation or per-link basis. + """ + + # 'compiler_type' is a class attribute that identifies this class. It + # keeps code that wants to know what kind of compiler it's dealing with + # from having to import all possible compiler classes just to do an + # 'isinstance'. In concrete CCompiler subclasses, 'compiler_type' + # should really, really be one of the keys of the 'compiler_class' + # dictionary (see below -- used by the 'new_compiler()' factory + # function) -- authors of new compiler interface classes are + # responsible for updating 'compiler_class'! + compiler_type = None + + # XXX things not handled by this compiler abstraction model: + # * client can't provide additional options for a compiler, + # e.g. warning, optimization, debugging flags. Perhaps this + # should be the domain of concrete compiler abstraction classes + # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base + # class should have methods for the common ones. + # * can't completely override the include or library searchg + # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". + # I'm not sure how widely supported this is even by Unix + # compilers, much less on other platforms. And I'm even less + # sure how useful it is; maybe for cross-compiling, but + # support for that is a ways off. (And anyways, cross + # compilers probably have a dedicated binary with the + # right paths compiled in. I hope.) + # * can't do really freaky things with the library list/library + # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against + # different versions of libfoo.a in different locations. I + # think this is useless without the ability to null out the + # library search path anyways. + + + # Subclasses that rely on the standard filename generation methods + # implemented below should override these; see the comment near + # those methods ('object_filenames()' et. al.) for details: + src_extensions = None # list of strings + obj_extension = None # string + static_lib_extension = None + shared_lib_extension = None # string + static_lib_format = None # format string + shared_lib_format = None # prob. same as static_lib_format + exe_extension = None # string + + # Default language settings. language_map is used to detect a source + # file or Extension target language, checking source filenames. + # language_order is used to detect the language precedence, when deciding + # what language to use when mixing source types. For example, if some + # extension has two files with ".c" extension, and one with ".cpp", it + # is still linked as c++. + language_map = {".c" : "c", + ".cc" : "c++", + ".cpp" : "c++", + ".cxx" : "c++", + ".m" : "objc", + } + language_order = ["c++", "objc", "c"] + + def __init__(self, verbose=0, dry_run=0, force=0): + self.dry_run = dry_run + self.force = force + self.verbose = verbose + + # 'output_dir': a common output directory for object, library, + # shared object, and shared library files + self.output_dir = None + + # 'macros': a list of macro definitions (or undefinitions). A + # macro definition is a 2-tuple (name, value), where the value is + # either a string or None (no explicit value). A macro + # undefinition is a 1-tuple (name,). + self.macros = [] + + # 'include_dirs': a list of directories to search for include files + self.include_dirs = [] + + # 'libraries': a list of libraries to include in any link + # (library names, not filenames: eg. "foo" not "libfoo.a") + self.libraries = [] + + # 'library_dirs': a list of directories to search for libraries + self.library_dirs = [] + + # 'runtime_library_dirs': a list of directories to search for + # shared libraries/objects at runtime + self.runtime_library_dirs = [] + + # 'objects': a list of object files (or similar, such as explicitly + # named library files) to include on any link + self.objects = [] + + for key in self.executables.keys(): + self.set_executable(key, self.executables[key]) + + def set_executables(self, **kwargs): + """Define the executables (and options for them) that will be run + to perform the various stages of compilation. The exact set of + executables that may be specified here depends on the compiler + class (via the 'executables' class attribute), but most will have: + compiler the C/C++ compiler + linker_so linker used to create shared objects and libraries + linker_exe linker used to create binary executables + archiver static library creator + + On platforms with a command-line (Unix, DOS/Windows), each of these + is a string that will be split into executable name and (optional) + list of arguments. (Splitting the string is done similarly to how + Unix shells operate: words are delimited by spaces, but quotes and + backslashes can override this. See + 'distutils.util.split_quoted()'.) + """ + + # Note that some CCompiler implementation classes will define class + # attributes 'cpp', 'cc', etc. with hard-coded executable names; + # this is appropriate when a compiler class is for exactly one + # compiler/OS combination (eg. MSVCCompiler). Other compiler + # classes (UnixCCompiler, in particular) are driven by information + # discovered at run-time, since there are many different ways to do + # basically the same things with Unix C compilers. + + for key in kwargs: + if key not in self.executables: + raise ValueError("unknown executable '%s' for class %s" % + (key, self.__class__.__name__)) + self.set_executable(key, kwargs[key]) + + def set_executable(self, key, value): + if isinstance(value, str): + setattr(self, key, split_quoted(value)) + else: + setattr(self, key, value) + + def _find_macro(self, name): + i = 0 + for defn in self.macros: + if defn[0] == name: + return i + i += 1 + return None + + def _check_macro_definitions(self, definitions): + """Ensures that every element of 'definitions' is a valid macro + definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do + nothing if all definitions are OK, raise TypeError otherwise. + """ + for defn in definitions: + if not (isinstance(defn, tuple) and + (len(defn) in (1, 2) and + (isinstance (defn[1], str) or defn[1] is None)) and + isinstance (defn[0], str)): + raise TypeError(("invalid macro definition '%s': " % defn) + \ + "must be tuple (string,), (string, string), or " + \ + "(string, None)") + + + # -- Bookkeeping methods ------------------------------------------- + + def define_macro(self, name, value=None): + """Define a preprocessor macro for all compilations driven by this + compiler object. The optional parameter 'value' should be a + string; if it is not supplied, then the macro will be defined + without an explicit value and the exact outcome depends on the + compiler used (XXX true? does ANSI say anything about this?) + """ + # Delete from the list of macro definitions/undefinitions if + # already there (so that this one will take precedence). + i = self._find_macro (name) + if i is not None: + del self.macros[i] + + self.macros.append((name, value)) + + def undefine_macro(self, name): + """Undefine a preprocessor macro for all compilations driven by + this compiler object. If the same macro is defined by + 'define_macro()' and undefined by 'undefine_macro()' the last call + takes precedence (including multiple redefinitions or + undefinitions). If the macro is redefined/undefined on a + per-compilation basis (ie. in the call to 'compile()'), then that + takes precedence. + """ + # Delete from the list of macro definitions/undefinitions if + # already there (so that this one will take precedence). + i = self._find_macro (name) + if i is not None: + del self.macros[i] + + undefn = (name,) + self.macros.append(undefn) + + def add_include_dir(self, dir): + """Add 'dir' to the list of directories that will be searched for + header files. The compiler is instructed to search directories in + the order in which they are supplied by successive calls to + 'add_include_dir()'. + """ + self.include_dirs.append(dir) + + def set_include_dirs(self, dirs): + """Set the list of directories that will be searched to 'dirs' (a + list of strings). Overrides any preceding calls to + 'add_include_dir()'; subsequence calls to 'add_include_dir()' add + to the list passed to 'set_include_dirs()'. This does not affect + any list of standard include directories that the compiler may + search by default. + """ + self.include_dirs = dirs[:] + + def add_library(self, libname): + """Add 'libname' to the list of libraries that will be included in + all links driven by this compiler object. Note that 'libname' + should *not* be the name of a file containing a library, but the + name of the library itself: the actual filename will be inferred by + the linker, the compiler, or the compiler class (depending on the + platform). + + The linker will be instructed to link against libraries in the + order they were supplied to 'add_library()' and/or + 'set_libraries()'. It is perfectly valid to duplicate library + names; the linker will be instructed to link against libraries as + many times as they are mentioned. + """ + self.libraries.append(libname) + + def set_libraries(self, libnames): + """Set the list of libraries to be included in all links driven by + this compiler object to 'libnames' (a list of strings). This does + not affect any standard system libraries that the linker may + include by default. + """ + self.libraries = libnames[:] + + def add_library_dir(self, dir): + """Add 'dir' to the list of directories that will be searched for + libraries specified to 'add_library()' and 'set_libraries()'. The + linker will be instructed to search for libraries in the order they + are supplied to 'add_library_dir()' and/or 'set_library_dirs()'. + """ + self.library_dirs.append(dir) + + def set_library_dirs(self, dirs): + """Set the list of library search directories to 'dirs' (a list of + strings). This does not affect any standard library search path + that the linker may search by default. + """ + self.library_dirs = dirs[:] + + def add_runtime_library_dir(self, dir): + """Add 'dir' to the list of directories that will be searched for + shared libraries at runtime. + """ + self.runtime_library_dirs.append(dir) + + def set_runtime_library_dirs(self, dirs): + """Set the list of directories to search for shared libraries at + runtime to 'dirs' (a list of strings). This does not affect any + standard search path that the runtime linker may search by + default. + """ + self.runtime_library_dirs = dirs[:] + + def add_link_object(self, object): + """Add 'object' to the list of object files (or analogues, such as + explicitly named library files or the output of "resource + compilers") to be included in every link driven by this compiler + object. + """ + self.objects.append(object) + + def set_link_objects(self, objects): + """Set the list of object files (or analogues) to be included in + every link to 'objects'. This does not affect any standard object + files that the linker may include by default (such as system + libraries). + """ + self.objects = objects[:] + + + # -- Private utility methods -------------------------------------- + # (here for the convenience of subclasses) + + # Helper method to prep compiler in subclass compile() methods + + def _setup_compile(self, outdir, macros, incdirs, sources, depends, + extra): + """Process arguments and decide which source files to compile.""" + if outdir is None: + outdir = self.output_dir + elif not isinstance(outdir, str): + raise TypeError("'output_dir' must be a string or None") + + if macros is None: + macros = self.macros + elif isinstance(macros, list): + macros = macros + (self.macros or []) + else: + raise TypeError("'macros' (if supplied) must be a list of tuples") + + if incdirs is None: + incdirs = self.include_dirs + elif isinstance(incdirs, (list, tuple)): + incdirs = list(incdirs) + (self.include_dirs or []) + else: + raise TypeError( + "'include_dirs' (if supplied) must be a list of strings") + + if extra is None: + extra = [] + + # Get the list of expected output (object) files + objects = self.object_filenames(sources, strip_dir=0, + output_dir=outdir) + assert len(objects) == len(sources) + + pp_opts = gen_preprocess_options(macros, incdirs) + + build = {} + for i in range(len(sources)): + src = sources[i] + obj = objects[i] + ext = os.path.splitext(src)[1] + self.mkpath(os.path.dirname(obj)) + build[obj] = (src, ext) + + return macros, objects, extra, pp_opts, build + + def _get_cc_args(self, pp_opts, debug, before): + # works for unixccompiler, cygwinccompiler + cc_args = pp_opts + ['-c'] + if debug: + cc_args[:0] = ['-g'] + if before: + cc_args[:0] = before + return cc_args + + def _fix_compile_args(self, output_dir, macros, include_dirs): + """Typecheck and fix-up some of the arguments to the 'compile()' + method, and return fixed-up values. Specifically: if 'output_dir' + is None, replaces it with 'self.output_dir'; ensures that 'macros' + is a list, and augments it with 'self.macros'; ensures that + 'include_dirs' is a list, and augments it with 'self.include_dirs'. + Guarantees that the returned values are of the correct type, + i.e. for 'output_dir' either string or None, and for 'macros' and + 'include_dirs' either list or None. + """ + if output_dir is None: + output_dir = self.output_dir + elif not isinstance(output_dir, str): + raise TypeError("'output_dir' must be a string or None") + + if macros is None: + macros = self.macros + elif isinstance(macros, list): + macros = macros + (self.macros or []) + else: + raise TypeError("'macros' (if supplied) must be a list of tuples") + + if include_dirs is None: + include_dirs = self.include_dirs + elif isinstance(include_dirs, (list, tuple)): + include_dirs = list(include_dirs) + (self.include_dirs or []) + else: + raise TypeError( + "'include_dirs' (if supplied) must be a list of strings") + + return output_dir, macros, include_dirs + + def _prep_compile(self, sources, output_dir, depends=None): + """Decide which souce files must be recompiled. + + Determine the list of object files corresponding to 'sources', + and figure out which ones really need to be recompiled. + Return a list of all object files and a dictionary telling + which source files can be skipped. + """ + # Get the list of expected output (object) files + objects = self.object_filenames(sources, output_dir=output_dir) + assert len(objects) == len(sources) + + # Return an empty dict for the "which source files can be skipped" + # return value to preserve API compatibility. + return objects, {} + + def _fix_object_args(self, objects, output_dir): + """Typecheck and fix up some arguments supplied to various methods. + Specifically: ensure that 'objects' is a list; if output_dir is + None, replace with self.output_dir. Return fixed versions of + 'objects' and 'output_dir'. + """ + if not isinstance(objects, (list, tuple)): + raise TypeError("'objects' must be a list or tuple of strings") + objects = list(objects) + + if output_dir is None: + output_dir = self.output_dir + elif not isinstance(output_dir, str): + raise TypeError("'output_dir' must be a string or None") + + return (objects, output_dir) + + def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs): + """Typecheck and fix up some of the arguments supplied to the + 'link_*' methods. Specifically: ensure that all arguments are + lists, and augment them with their permanent versions + (eg. 'self.libraries' augments 'libraries'). Return a tuple with + fixed versions of all arguments. + """ + if libraries is None: + libraries = self.libraries + elif isinstance(libraries, (list, tuple)): + libraries = list (libraries) + (self.libraries or []) + else: + raise TypeError( + "'libraries' (if supplied) must be a list of strings") + + if library_dirs is None: + library_dirs = self.library_dirs + elif isinstance(library_dirs, (list, tuple)): + library_dirs = list (library_dirs) + (self.library_dirs or []) + else: + raise TypeError( + "'library_dirs' (if supplied) must be a list of strings") + + if runtime_library_dirs is None: + runtime_library_dirs = self.runtime_library_dirs + elif isinstance(runtime_library_dirs, (list, tuple)): + runtime_library_dirs = (list(runtime_library_dirs) + + (self.runtime_library_dirs or [])) + else: + raise TypeError("'runtime_library_dirs' (if supplied) " + "must be a list of strings") + + return (libraries, library_dirs, runtime_library_dirs) + + def _need_link(self, objects, output_file): + """Return true if we need to relink the files listed in 'objects' + to recreate 'output_file'. + """ + if self.force: + return True + else: + if self.dry_run: + newer = newer_group (objects, output_file, missing='newer') + else: + newer = newer_group (objects, output_file) + return newer + + def detect_language(self, sources): + """Detect the language of a given file, or list of files. Uses + language_map, and language_order to do the job. + """ + if not isinstance(sources, list): + sources = [sources] + lang = None + index = len(self.language_order) + for source in sources: + base, ext = os.path.splitext(source) + extlang = self.language_map.get(ext) + try: + extindex = self.language_order.index(extlang) + if extindex < index: + lang = extlang + index = extindex + except ValueError: + pass + return lang + + + # -- Worker methods ------------------------------------------------ + # (must be implemented by subclasses) + + def preprocess(self, source, output_file=None, macros=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): + """Preprocess a single C/C++ source file, named in 'source'. + Output will be written to file named 'output_file', or stdout if + 'output_file' not supplied. 'macros' is a list of macro + definitions as for 'compile()', which will augment the macros set + with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a + list of directory names that will be added to the default list. + + Raises PreprocessError on failure. + """ + pass + + def compile(self, sources, output_dir=None, macros=None, + include_dirs=None, debug=0, extra_preargs=None, + extra_postargs=None, depends=None): + """Compile one or more source files. + + 'sources' must be a list of filenames, most likely C/C++ + files, but in reality anything that can be handled by a + particular compiler and compiler class (eg. MSVCCompiler can + handle resource files in 'sources'). Return a list of object + filenames, one per source filename in 'sources'. Depending on + the implementation, not all source files will necessarily be + compiled, but all corresponding object filenames will be + returned. + + If 'output_dir' is given, object files will be put under it, while + retaining their original path component. That is, "foo/bar.c" + normally compiles to "foo/bar.o" (for a Unix implementation); if + 'output_dir' is "build", then it would compile to + "build/foo/bar.o". + + 'macros', if given, must be a list of macro definitions. A macro + definition is either a (name, value) 2-tuple or a (name,) 1-tuple. + The former defines a macro; if the value is None, the macro is + defined without an explicit value. The 1-tuple case undefines a + macro. Later definitions/redefinitions/ undefinitions take + precedence. + + 'include_dirs', if given, must be a list of strings, the + directories to add to the default include file search path for this + compilation only. + + 'debug' is a boolean; if true, the compiler will be instructed to + output debug symbols in (or alongside) the object file(s). + + 'extra_preargs' and 'extra_postargs' are implementation- dependent. + On platforms that have the notion of a command-line (e.g. Unix, + DOS/Windows), they are most likely lists of strings: extra + command-line arguments to prepend/append to the compiler command + line. On other platforms, consult the implementation class + documentation. In any event, they are intended as an escape hatch + for those occasions when the abstract compiler framework doesn't + cut the mustard. + + 'depends', if given, is a list of filenames that all targets + depend on. If a source file is older than any file in + depends, then the source file will be recompiled. This + supports dependency tracking, but only at a coarse + granularity. + + Raises CompileError on failure. + """ + # A concrete compiler class can either override this method + # entirely or implement _compile(). + macros, objects, extra_postargs, pp_opts, build = \ + self._setup_compile(output_dir, macros, include_dirs, sources, + depends, extra_postargs) + cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) + + # Return *all* object filenames, not just the ones we just built. + return objects + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + """Compile 'src' to product 'obj'.""" + # A concrete compiler class that does not override compile() + # should implement _compile(). + pass + + def create_static_lib(self, objects, output_libname, output_dir=None, + debug=0, target_lang=None): + """Link a bunch of stuff together to create a static library file. + The "bunch of stuff" consists of the list of object files supplied + as 'objects', the extra object files supplied to + 'add_link_object()' and/or 'set_link_objects()', the libraries + supplied to 'add_library()' and/or 'set_libraries()', and the + libraries supplied as 'libraries' (if any). + + 'output_libname' should be a library name, not a filename; the + filename will be inferred from the library name. 'output_dir' is + the directory where the library file will be put. + + 'debug' is a boolean; if true, debugging information will be + included in the library (note that on most platforms, it is the + compile step where this matters: the 'debug' flag is included here + just for consistency). + + 'target_lang' is the target language for which the given objects + are being compiled. This allows specific linkage time treatment of + certain languages. + + Raises LibError on failure. + """ + pass + + + # values for target_desc parameter in link() + SHARED_OBJECT = "shared_object" + SHARED_LIBRARY = "shared_library" + EXECUTABLE = "executable" + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + """Link a bunch of stuff together to create an executable or + shared library file. + + The "bunch of stuff" consists of the list of object files supplied + as 'objects'. 'output_filename' should be a filename. If + 'output_dir' is supplied, 'output_filename' is relative to it + (i.e. 'output_filename' can provide directory components if + needed). + + 'libraries' is a list of libraries to link against. These are + library names, not filenames, since they're translated into + filenames in a platform-specific way (eg. "foo" becomes "libfoo.a" + on Unix and "foo.lib" on DOS/Windows). However, they can include a + directory component, which means the linker will look in that + specific directory rather than searching all the normal locations. + + 'library_dirs', if supplied, should be a list of directories to + search for libraries that were specified as bare library names + (ie. no directory component). These are on top of the system + default and those supplied to 'add_library_dir()' and/or + 'set_library_dirs()'. 'runtime_library_dirs' is a list of + directories that will be embedded into the shared library and used + to search for other shared libraries that *it* depends on at + run-time. (This may only be relevant on Unix.) + + 'export_symbols' is a list of symbols that the shared library will + export. (This appears to be relevant only on Windows.) + + 'debug' is as for 'compile()' and 'create_static_lib()', with the + slight distinction that it actually matters on most platforms (as + opposed to 'create_static_lib()', which includes a 'debug' flag + mostly for form's sake). + + 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except + of course that they supply command-line arguments for the + particular linker being used). + + 'target_lang' is the target language for which the given objects + are being compiled. This allows specific linkage time treatment of + certain languages. + + Raises LinkError on failure. + """ + raise NotImplementedError + + + # Old 'link_*()' methods, rewritten to use the new 'link()' method. + + def link_shared_lib(self, + objects, + output_libname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + self.link(CCompiler.SHARED_LIBRARY, objects, + self.library_filename(output_libname, lib_type='shared'), + output_dir, + libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, + extra_preargs, extra_postargs, build_temp, target_lang) + + + def link_shared_object(self, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + self.link(CCompiler.SHARED_OBJECT, objects, + output_filename, output_dir, + libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, + extra_preargs, extra_postargs, build_temp, target_lang) + + + def link_executable(self, + objects, + output_progname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + target_lang=None): + self.link(CCompiler.EXECUTABLE, objects, + self.executable_filename(output_progname), output_dir, + libraries, library_dirs, runtime_library_dirs, None, + debug, extra_preargs, extra_postargs, None, target_lang) + + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function; there is + # no appropriate default implementation so subclasses should + # implement all of these. + + def library_dir_option(self, dir): + """Return the compiler option to add 'dir' to the list of + directories searched for libraries. + """ + raise NotImplementedError + + def runtime_library_dir_option(self, dir): + """Return the compiler option to add 'dir' to the list of + directories searched for runtime libraries. + """ + raise NotImplementedError + + def library_option(self, lib): + """Return the compiler option to add 'lib' to the list of libraries + linked into the shared library or executable. + """ + raise NotImplementedError + + def has_function(self, funcname, includes=None, include_dirs=None, + libraries=None, library_dirs=None): + """Return a boolean indicating whether funcname is supported on + the current platform. The optional arguments can be used to + augment the compilation environment. + """ + # this can't be included at module scope because it tries to + # import math which might not be available at that point - maybe + # the necessary logic should just be inlined? + import tempfile + if includes is None: + includes = [] + if include_dirs is None: + include_dirs = [] + if libraries is None: + libraries = [] + if library_dirs is None: + library_dirs = [] + fd, fname = tempfile.mkstemp(".c", funcname, text=True) + f = os.fdopen(fd, "w") + try: + for incl in includes: + f.write("""#include "%s"\n""" % incl) + f.write("""\ +int main (int argc, char **argv) { + %s(); + return 0; +} +""" % funcname) + finally: + f.close() + try: + objects = self.compile([fname], include_dirs=include_dirs) + except CompileError: + return False + + try: + self.link_executable(objects, "a.out", + libraries=libraries, + library_dirs=library_dirs) + except (LinkError, TypeError): + return False + return True + + def find_library_file (self, dirs, lib, debug=0): + """Search the specified list of directories for a static or shared + library file 'lib' and return the full path to that file. If + 'debug' true, look for a debugging version (if that makes sense on + the current platform). Return None if 'lib' wasn't found in any of + the specified directories. + """ + raise NotImplementedError + + # -- Filename generation methods ----------------------------------- + + # The default implementation of the filename generating methods are + # prejudiced towards the Unix/DOS/Windows view of the world: + # * object files are named by replacing the source file extension + # (eg. .c/.cpp -> .o/.obj) + # * library files (shared or static) are named by plugging the + # library name and extension into a format string, eg. + # "lib%s.%s" % (lib_name, ".a") for Unix static libraries + # * executables are named by appending an extension (possibly + # empty) to the program name: eg. progname + ".exe" for + # Windows + # + # To reduce redundant code, these methods expect to find + # several attributes in the current object (presumably defined + # as class attributes): + # * src_extensions - + # list of C/C++ source file extensions, eg. ['.c', '.cpp'] + # * obj_extension - + # object file extension, eg. '.o' or '.obj' + # * static_lib_extension - + # extension for static library files, eg. '.a' or '.lib' + # * shared_lib_extension - + # extension for shared library/object files, eg. '.so', '.dll' + # * static_lib_format - + # format string for generating static library filenames, + # eg. 'lib%s.%s' or '%s.%s' + # * shared_lib_format + # format string for generating shared library filenames + # (probably same as static_lib_format, since the extension + # is one of the intended parameters to the format string) + # * exe_extension - + # extension for executable files, eg. '' or '.exe' + + def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + if output_dir is None: + output_dir = '' + obj_names = [] + for src_name in source_filenames: + base, ext = os.path.splitext(src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / + if ext not in self.src_extensions: + raise UnknownFileError( + "unknown file type '%s' (from '%s')" % (ext, src_name)) + if strip_dir: + base = os.path.basename(base) + obj_names.append(os.path.join(output_dir, + base + self.obj_extension)) + return obj_names + + def shared_object_filename(self, basename, strip_dir=0, output_dir=''): + assert output_dir is not None + if strip_dir: + basename = os.path.basename(basename) + return os.path.join(output_dir, basename + self.shared_lib_extension) + + def executable_filename(self, basename, strip_dir=0, output_dir=''): + assert output_dir is not None + if strip_dir: + basename = os.path.basename(basename) + return os.path.join(output_dir, basename + (self.exe_extension or '')) + + def library_filename(self, libname, lib_type='static', # or 'shared' + strip_dir=0, output_dir=''): + assert output_dir is not None + if lib_type not in ("static", "shared", "dylib", "xcode_stub"): + raise ValueError( + "'lib_type' must be \"static\", \"shared\", \"dylib\", or \"xcode_stub\"") + fmt = getattr(self, lib_type + "_lib_format") + ext = getattr(self, lib_type + "_lib_extension") + + dir, base = os.path.split(libname) + filename = fmt % (base, ext) + if strip_dir: + dir = '' + + return os.path.join(output_dir, dir, filename) + + + # -- Utility methods ----------------------------------------------- + + def announce(self, msg, level=1): + log.debug(msg) + + def debug_print(self, msg): + from distutils.debug import DEBUG + if DEBUG: + print(msg) + + def warn(self, msg): + sys.stderr.write("warning: %s\n" % msg) + + def execute(self, func, args, msg=None, level=1): + execute(func, args, msg, self.dry_run) + + def spawn(self, cmd): + spawn(cmd, dry_run=self.dry_run) + + def move_file(self, src, dst): + return move_file(src, dst, dry_run=self.dry_run) + + def mkpath (self, name, mode=0o777): + mkpath(name, mode, dry_run=self.dry_run) + + +# Map a sys.platform/os.name ('posix', 'nt') to the default compiler +# type for that platform. Keys are interpreted as re match +# patterns. Order is important; platform mappings are preferred over +# OS names. +_default_compilers = ( + + # Platform string mappings + + # on a cygwin built python we can use gcc like an ordinary UNIXish + # compiler + ('cygwin.*', 'unix'), + + # OS name mappings + ('posix', 'unix'), + ('nt', 'msvc'), + + ) + +def get_default_compiler(osname=None, platform=None): + """Determine the default compiler to use for the given platform. + + osname should be one of the standard Python OS names (i.e. the + ones returned by os.name) and platform the common value + returned by sys.platform for the platform in question. + + The default values are os.name and sys.platform in case the + parameters are not given. + """ + if osname is None: + osname = os.name + if platform is None: + platform = sys.platform + for pattern, compiler in _default_compilers: + if re.match(pattern, platform) is not None or \ + re.match(pattern, osname) is not None: + return compiler + # Default to Unix compiler + return 'unix' + +# Map compiler types to (module_name, class_name) pairs -- ie. where to +# find the code that implements an interface to this compiler. (The module +# is assumed to be in the 'distutils' package.) +compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', + "standard UNIX-style compiler"), + 'msvc': ('_msvccompiler', 'MSVCCompiler', + "Microsoft Visual C++"), + 'cygwin': ('cygwinccompiler', 'CygwinCCompiler', + "Cygwin port of GNU C Compiler for Win32"), + 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler', + "Mingw32 port of GNU C Compiler for Win32"), + 'bcpp': ('bcppcompiler', 'BCPPCompiler', + "Borland C++ Compiler"), + } + +def show_compilers(): + """Print list of available compilers (used by the "--help-compiler" + options to "build", "build_ext", "build_clib"). + """ + # XXX this "knows" that the compiler option it's describing is + # "--compiler", which just happens to be the case for the three + # commands that use it. + from distutils.fancy_getopt import FancyGetopt + compilers = [] + for compiler in compiler_class.keys(): + compilers.append(("compiler="+compiler, None, + compiler_class[compiler][2])) + compilers.sort() + pretty_printer = FancyGetopt(compilers) + pretty_printer.print_help("List of available compilers:") + + +def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0): + """Generate an instance of some CCompiler subclass for the supplied + platform/compiler combination. 'plat' defaults to 'os.name' + (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler + for that platform. Currently only 'posix' and 'nt' are supported, and + the default compilers are "traditional Unix interface" (UnixCCompiler + class) and Visual C++ (MSVCCompiler class). Note that it's perfectly + possible to ask for a Unix compiler object under Windows, and a + Microsoft compiler object under Unix -- if you supply a value for + 'compiler', 'plat' is ignored. + """ + if plat is None: + plat = os.name + + try: + if compiler is None: + compiler = get_default_compiler(plat) + + (module_name, class_name, long_description) = compiler_class[compiler] + except KeyError: + msg = "don't know how to compile C/C++ code on platform '%s'" % plat + if compiler is not None: + msg = msg + " with '%s' compiler" % compiler + raise DistutilsPlatformError(msg) + + try: + module_name = "distutils." + module_name + __import__ (module_name) + module = sys.modules[module_name] + klass = vars(module)[class_name] + except ImportError: + raise DistutilsModuleError( + "can't compile C/C++ code: unable to load module '%s'" % \ + module_name) + except KeyError: + raise DistutilsModuleError( + "can't compile C/C++ code: unable to find class '%s' " + "in module '%s'" % (class_name, module_name)) + + # XXX The None is necessary to preserve backwards compatibility + # with classes that expect verbose to be the first positional + # argument. + return klass(None, dry_run, force) + + +def gen_preprocess_options(macros, include_dirs): + """Generate C pre-processor options (-D, -U, -I) as used by at least + two types of compilers: the typical Unix compiler and Visual C++. + 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,) + means undefine (-U) macro 'name', and (name,value) means define (-D) + macro 'name' to 'value'. 'include_dirs' is just a list of directory + names to be added to the header file search path (-I). Returns a list + of command-line options suitable for either Unix compilers or Visual + C++. + """ + # XXX it would be nice (mainly aesthetic, and so we don't generate + # stupid-looking command lines) to go over 'macros' and eliminate + # redundant definitions/undefinitions (ie. ensure that only the + # latest mention of a particular macro winds up on the command + # line). I don't think it's essential, though, since most (all?) + # Unix C compilers only pay attention to the latest -D or -U + # mention of a macro on their command line. Similar situation for + # 'include_dirs'. I'm punting on both for now. Anyways, weeding out + # redundancies like this should probably be the province of + # CCompiler, since the data structures used are inherited from it + # and therefore common to all CCompiler classes. + pp_opts = [] + for macro in macros: + if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2): + raise TypeError( + "bad macro definition '%s': " + "each element of 'macros' list must be a 1- or 2-tuple" + % macro) + + if len(macro) == 1: # undefine this macro + pp_opts.append("-U%s" % macro[0]) + elif len(macro) == 2: + if macro[1] is None: # define with no explicit value + pp_opts.append("-D%s" % macro[0]) + else: + # XXX *don't* need to be clever about quoting the + # macro value here, because we're going to avoid the + # shell at all costs when we spawn the command! + pp_opts.append("-D%s=%s" % macro) + + for dir in include_dirs: + pp_opts.append("-I%s" % dir) + return pp_opts + + +def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): + """Generate linker options for searching library directories and + linking with specific libraries. 'libraries' and 'library_dirs' are, + respectively, lists of library names (not filenames!) and search + directories. Returns a list of command-line options suitable for use + with some compiler (depending on the two format strings passed in). + """ + lib_opts = [] + + for dir in library_dirs: + lib_opts.append(compiler.library_dir_option(dir)) + + for dir in runtime_library_dirs: + opt = compiler.runtime_library_dir_option(dir) + if isinstance(opt, list): + lib_opts = lib_opts + opt + else: + lib_opts.append(opt) + + # XXX it's important that we *not* remove redundant library mentions! + # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to + # resolve all symbols. I just hope we never have to say "-lfoo obj.o + # -lbar" to get things to work -- that's certainly a possibility, but a + # pretty nasty way to arrange your C code. + + for lib in libraries: + (lib_dir, lib_name) = os.path.split(lib) + if lib_dir: + lib_file = compiler.find_library_file([lib_dir], lib_name) + if lib_file: + lib_opts.append(lib_file) + else: + compiler.warn("no library file corresponding to " + "'%s' found (skipping)" % lib) + else: + lib_opts.append(compiler.library_option (lib)) + return lib_opts diff --git a/distutils/cmd.py b/distutils/cmd.py new file mode 100644 index 00000000..dba3191e --- /dev/null +++ b/distutils/cmd.py @@ -0,0 +1,403 @@ +"""distutils.cmd + +Provides the Command class, the base class for the command classes +in the distutils.command package. +""" + +import sys, os, re +from distutils.errors import DistutilsOptionError +from distutils import util, dir_util, file_util, archive_util, dep_util +from distutils import log + +class Command: + """Abstract base class for defining command classes, the "worker bees" + of the Distutils. A useful analogy for command classes is to think of + them as subroutines with local variables called "options". The options + are "declared" in 'initialize_options()' and "defined" (given their + final values, aka "finalized") in 'finalize_options()', both of which + must be defined by every command class. The distinction between the + two is necessary because option values might come from the outside + world (command line, config file, ...), and any options dependent on + other options must be computed *after* these outside influences have + been processed -- hence 'finalize_options()'. The "body" of the + subroutine, where it does all its work based on the values of its + options, is the 'run()' method, which must also be implemented by every + command class. + """ + + # 'sub_commands' formalizes the notion of a "family" of commands, + # eg. "install" as the parent with sub-commands "install_lib", + # "install_headers", etc. The parent of a family of commands + # defines 'sub_commands' as a class attribute; it's a list of + # (command_name : string, predicate : unbound_method | string | None) + # tuples, where 'predicate' is a method of the parent command that + # determines whether the corresponding command is applicable in the + # current situation. (Eg. we "install_headers" is only applicable if + # we have any C header files to install.) If 'predicate' is None, + # that command is always applicable. + # + # 'sub_commands' is usually defined at the *end* of a class, because + # predicates can be unbound methods, so they must already have been + # defined. The canonical example is the "install" command. + sub_commands = [] + + + # -- Creation/initialization methods ------------------------------- + + def __init__(self, dist): + """Create and initialize a new Command object. Most importantly, + invokes the 'initialize_options()' method, which is the real + initializer and depends on the actual command being + instantiated. + """ + # late import because of mutual dependence between these classes + from distutils.dist import Distribution + + if not isinstance(dist, Distribution): + raise TypeError("dist must be a Distribution instance") + if self.__class__ is Command: + raise RuntimeError("Command is an abstract class") + + self.distribution = dist + self.initialize_options() + + # Per-command versions of the global flags, so that the user can + # customize Distutils' behaviour command-by-command and let some + # commands fall back on the Distribution's behaviour. None means + # "not defined, check self.distribution's copy", while 0 or 1 mean + # false and true (duh). Note that this means figuring out the real + # value of each flag is a touch complicated -- hence "self._dry_run" + # will be handled by __getattr__, below. + # XXX This needs to be fixed. + self._dry_run = None + + # verbose is largely ignored, but needs to be set for + # backwards compatibility (I think)? + self.verbose = dist.verbose + + # Some commands define a 'self.force' option to ignore file + # timestamps, but methods defined *here* assume that + # 'self.force' exists for all commands. So define it here + # just to be safe. + self.force = None + + # The 'help' flag is just used for command-line parsing, so + # none of that complicated bureaucracy is needed. + self.help = 0 + + # 'finalized' records whether or not 'finalize_options()' has been + # called. 'finalize_options()' itself should not pay attention to + # this flag: it is the business of 'ensure_finalized()', which + # always calls 'finalize_options()', to respect/update it. + self.finalized = 0 + + # XXX A more explicit way to customize dry_run would be better. + def __getattr__(self, attr): + if attr == 'dry_run': + myval = getattr(self, "_" + attr) + if myval is None: + return getattr(self.distribution, attr) + else: + return myval + else: + raise AttributeError(attr) + + def ensure_finalized(self): + if not self.finalized: + self.finalize_options() + self.finalized = 1 + + # Subclasses must define: + # initialize_options() + # provide default values for all options; may be customized by + # setup script, by options from config file(s), or by command-line + # options + # finalize_options() + # decide on the final values for all options; this is called + # after all possible intervention from the outside world + # (command-line, option file, etc.) has been processed + # run() + # run the command: do whatever it is we're here to do, + # controlled by the command's various option values + + def initialize_options(self): + """Set default values for all the options that this command + supports. Note that these defaults may be overridden by other + commands, by the setup script, by config files, or by the + command-line. Thus, this is not the place to code dependencies + between options; generally, 'initialize_options()' implementations + are just a bunch of "self.foo = None" assignments. + + This method must be implemented by all command classes. + """ + raise RuntimeError("abstract method -- subclass %s must override" + % self.__class__) + + def finalize_options(self): + """Set final values for all the options that this command supports. + This is always called as late as possible, ie. after any option + assignments from the command-line or from other commands have been + done. Thus, this is the place to code option dependencies: if + 'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as + long as 'foo' still has the same value it was assigned in + 'initialize_options()'. + + This method must be implemented by all command classes. + """ + raise RuntimeError("abstract method -- subclass %s must override" + % self.__class__) + + + def dump_options(self, header=None, indent=""): + from distutils.fancy_getopt import longopt_xlate + if header is None: + header = "command options for '%s':" % self.get_command_name() + self.announce(indent + header, level=log.INFO) + indent = indent + " " + for (option, _, _) in self.user_options: + option = option.translate(longopt_xlate) + if option[-1] == "=": + option = option[:-1] + value = getattr(self, option) + self.announce(indent + "%s = %s" % (option, value), + level=log.INFO) + + def run(self): + """A command's raison d'etre: carry out the action it exists to + perform, controlled by the options initialized in + 'initialize_options()', customized by other commands, the setup + script, the command-line, and config files, and finalized in + 'finalize_options()'. All terminal output and filesystem + interaction should be done by 'run()'. + + This method must be implemented by all command classes. + """ + raise RuntimeError("abstract method -- subclass %s must override" + % self.__class__) + + def announce(self, msg, level=1): + """If the current verbosity level is of greater than or equal to + 'level' print 'msg' to stdout. + """ + log.log(level, msg) + + def debug_print(self, msg): + """Print 'msg' to stdout if the global DEBUG (taken from the + DISTUTILS_DEBUG environment variable) flag is true. + """ + from distutils.debug import DEBUG + if DEBUG: + print(msg) + sys.stdout.flush() + + + # -- Option validation methods ------------------------------------- + # (these are very handy in writing the 'finalize_options()' method) + # + # NB. the general philosophy here is to ensure that a particular option + # value meets certain type and value constraints. If not, we try to + # force it into conformance (eg. if we expect a list but have a string, + # split the string on comma and/or whitespace). If we can't force the + # option into conformance, raise DistutilsOptionError. Thus, command + # classes need do nothing more than (eg.) + # self.ensure_string_list('foo') + # and they can be guaranteed that thereafter, self.foo will be + # a list of strings. + + def _ensure_stringlike(self, option, what, default=None): + val = getattr(self, option) + if val is None: + setattr(self, option, default) + return default + elif not isinstance(val, str): + raise DistutilsOptionError("'%s' must be a %s (got `%s`)" + % (option, what, val)) + return val + + def ensure_string(self, option, default=None): + """Ensure that 'option' is a string; if not defined, set it to + 'default'. + """ + self._ensure_stringlike(option, "string", default) + + def ensure_string_list(self, option): + r"""Ensure that 'option' is a list of strings. If 'option' is + currently a string, we split it either on /,\s*/ or /\s+/, so + "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become + ["foo", "bar", "baz"]. + """ + val = getattr(self, option) + if val is None: + return + elif isinstance(val, str): + setattr(self, option, re.split(r',\s*|\s+', val)) + else: + if isinstance(val, list): + ok = all(isinstance(v, str) for v in val) + else: + ok = False + if not ok: + raise DistutilsOptionError( + "'%s' must be a list of strings (got %r)" + % (option, val)) + + def _ensure_tested_string(self, option, tester, what, error_fmt, + default=None): + val = self._ensure_stringlike(option, what, default) + if val is not None and not tester(val): + raise DistutilsOptionError(("error in '%s' option: " + error_fmt) + % (option, val)) + + def ensure_filename(self, option): + """Ensure that 'option' is the name of an existing file.""" + self._ensure_tested_string(option, os.path.isfile, + "filename", + "'%s' does not exist or is not a file") + + def ensure_dirname(self, option): + self._ensure_tested_string(option, os.path.isdir, + "directory name", + "'%s' does not exist or is not a directory") + + + # -- Convenience methods for commands ------------------------------ + + def get_command_name(self): + if hasattr(self, 'command_name'): + return self.command_name + else: + return self.__class__.__name__ + + def set_undefined_options(self, src_cmd, *option_pairs): + """Set the values of any "undefined" options from corresponding + option values in some other command object. "Undefined" here means + "is None", which is the convention used to indicate that an option + has not been changed between 'initialize_options()' and + 'finalize_options()'. Usually called from 'finalize_options()' for + options that depend on some other command rather than another + option of the same command. 'src_cmd' is the other command from + which option values will be taken (a command object will be created + for it if necessary); the remaining arguments are + '(src_option,dst_option)' tuples which mean "take the value of + 'src_option' in the 'src_cmd' command object, and copy it to + 'dst_option' in the current command object". + """ + # Option_pairs: list of (src_option, dst_option) tuples + src_cmd_obj = self.distribution.get_command_obj(src_cmd) + src_cmd_obj.ensure_finalized() + for (src_option, dst_option) in option_pairs: + if getattr(self, dst_option) is None: + setattr(self, dst_option, getattr(src_cmd_obj, src_option)) + + def get_finalized_command(self, command, create=1): + """Wrapper around Distribution's 'get_command_obj()' method: find + (create if necessary and 'create' is true) the command object for + 'command', call its 'ensure_finalized()' method, and return the + finalized command object. + """ + cmd_obj = self.distribution.get_command_obj(command, create) + cmd_obj.ensure_finalized() + return cmd_obj + + # XXX rename to 'get_reinitialized_command()'? (should do the + # same in dist.py, if so) + def reinitialize_command(self, command, reinit_subcommands=0): + return self.distribution.reinitialize_command(command, + reinit_subcommands) + + def run_command(self, command): + """Run some other command: uses the 'run_command()' method of + Distribution, which creates and finalizes the command object if + necessary and then invokes its 'run()' method. + """ + self.distribution.run_command(command) + + def get_sub_commands(self): + """Determine the sub-commands that are relevant in the current + distribution (ie., that need to be run). This is based on the + 'sub_commands' class attribute: each tuple in that list may include + a method that we call to determine if the subcommand needs to be + run for the current distribution. Return a list of command names. + """ + commands = [] + for (cmd_name, method) in self.sub_commands: + if method is None or method(self): + commands.append(cmd_name) + return commands + + + # -- External world manipulation ----------------------------------- + + def warn(self, msg): + log.warn("warning: %s: %s\n", self.get_command_name(), msg) + + def execute(self, func, args, msg=None, level=1): + util.execute(func, args, msg, dry_run=self.dry_run) + + def mkpath(self, name, mode=0o777): + dir_util.mkpath(name, mode, dry_run=self.dry_run) + + def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1, + link=None, level=1): + """Copy a file respecting verbose, dry-run and force flags. (The + former two default to whatever is in the Distribution object, and + the latter defaults to false for commands that don't define it.)""" + return file_util.copy_file(infile, outfile, preserve_mode, + preserve_times, not self.force, link, + dry_run=self.dry_run) + + def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1, + preserve_symlinks=0, level=1): + """Copy an entire directory tree respecting verbose, dry-run, + and force flags. + """ + return dir_util.copy_tree(infile, outfile, preserve_mode, + preserve_times, preserve_symlinks, + not self.force, dry_run=self.dry_run) + + def move_file (self, src, dst, level=1): + """Move a file respecting dry-run flag.""" + return file_util.move_file(src, dst, dry_run=self.dry_run) + + def spawn(self, cmd, search_path=1, level=1): + """Spawn an external command respecting dry-run flag.""" + from distutils.spawn import spawn + spawn(cmd, search_path, dry_run=self.dry_run) + + def make_archive(self, base_name, format, root_dir=None, base_dir=None, + owner=None, group=None): + return archive_util.make_archive(base_name, format, root_dir, base_dir, + dry_run=self.dry_run, + owner=owner, group=group) + + def make_file(self, infiles, outfile, func, args, + exec_msg=None, skip_msg=None, level=1): + """Special case of 'execute()' for operations that process one or + more input files and generate one output file. Works just like + 'execute()', except the operation is skipped and a different + message printed if 'outfile' already exists and is newer than all + files listed in 'infiles'. If the command defined 'self.force', + and it is true, then the command is unconditionally run -- does no + timestamp checks. + """ + if skip_msg is None: + skip_msg = "skipping %s (inputs unchanged)" % outfile + + # Allow 'infiles' to be a single string + if isinstance(infiles, str): + infiles = (infiles,) + elif not isinstance(infiles, (list, tuple)): + raise TypeError( + "'infiles' must be a string, or a list or tuple of strings") + + if exec_msg is None: + exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles)) + + # If 'outfile' must be regenerated (either because it doesn't + # exist, is out-of-date, or the 'force' flag is true) then + # perform the action that presumably regenerates it + if self.force or dep_util.newer_group(infiles, outfile): + self.execute(func, args, exec_msg, level) + # Otherwise, print the "skip" message + else: + log.debug(skip_msg) diff --git a/distutils/command/__init__.py b/distutils/command/__init__.py new file mode 100644 index 00000000..481eea9f --- /dev/null +++ b/distutils/command/__init__.py @@ -0,0 +1,31 @@ +"""distutils.command + +Package containing implementation of all the standard Distutils +commands.""" + +__all__ = ['build', + 'build_py', + 'build_ext', + 'build_clib', + 'build_scripts', + 'clean', + 'install', + 'install_lib', + 'install_headers', + 'install_scripts', + 'install_data', + 'sdist', + 'register', + 'bdist', + 'bdist_dumb', + 'bdist_rpm', + 'bdist_wininst', + 'check', + 'upload', + # These two are reserved for future use: + #'bdist_sdux', + #'bdist_pkgtool', + # Note: + # bdist_packager is not included because it only provides + # an abstract base class + ] diff --git a/distutils/command/bdist.py b/distutils/command/bdist.py new file mode 100644 index 00000000..014871d2 --- /dev/null +++ b/distutils/command/bdist.py @@ -0,0 +1,143 @@ +"""distutils.command.bdist + +Implements the Distutils 'bdist' command (create a built [binary] +distribution).""" + +import os +from distutils.core import Command +from distutils.errors import * +from distutils.util import get_platform + + +def show_formats(): + """Print list of available formats (arguments to "--format" option). + """ + from distutils.fancy_getopt import FancyGetopt + formats = [] + for format in bdist.format_commands: + formats.append(("formats=" + format, None, + bdist.format_command[format][1])) + pretty_printer = FancyGetopt(formats) + pretty_printer.print_help("List of available distribution formats:") + + +class bdist(Command): + + description = "create a built (binary) distribution" + + user_options = [('bdist-base=', 'b', + "temporary directory for creating built distributions"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('formats=', None, + "formats for distribution (comma-separated list)"), + ('dist-dir=', 'd', + "directory to put final built distributions in " + "[default: dist]"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), + ] + + boolean_options = ['skip-build'] + + help_options = [ + ('help-formats', None, + "lists available distribution formats", show_formats), + ] + + # The following commands do not take a format option from bdist + no_format_option = ('bdist_rpm',) + + # This won't do in reality: will need to distinguish RPM-ish Linux, + # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. + default_format = {'posix': 'gztar', + 'nt': 'zip'} + + # Establish the preferred order (for the --help-formats option). + format_commands = ['rpm', 'gztar', 'bztar', 'xztar', 'ztar', 'tar', + 'wininst', 'zip', 'msi'] + + # And the real information. + format_command = {'rpm': ('bdist_rpm', "RPM distribution"), + 'gztar': ('bdist_dumb', "gzip'ed tar file"), + 'bztar': ('bdist_dumb', "bzip2'ed tar file"), + 'xztar': ('bdist_dumb', "xz'ed tar file"), + 'ztar': ('bdist_dumb', "compressed tar file"), + 'tar': ('bdist_dumb', "tar file"), + 'wininst': ('bdist_wininst', + "Windows executable installer"), + 'zip': ('bdist_dumb', "ZIP file"), + 'msi': ('bdist_msi', "Microsoft Installer") + } + + + def initialize_options(self): + self.bdist_base = None + self.plat_name = None + self.formats = None + self.dist_dir = None + self.skip_build = 0 + self.group = None + self.owner = None + + def finalize_options(self): + # have to finalize 'plat_name' before 'bdist_base' + if self.plat_name is None: + if self.skip_build: + self.plat_name = get_platform() + else: + self.plat_name = self.get_finalized_command('build').plat_name + + # 'bdist_base' -- parent of per-built-distribution-format + # temporary directories (eg. we'll probably have + # "build/bdist./dumb", "build/bdist./rpm", etc.) + if self.bdist_base is None: + build_base = self.get_finalized_command('build').build_base + self.bdist_base = os.path.join(build_base, + 'bdist.' + self.plat_name) + + self.ensure_string_list('formats') + if self.formats is None: + try: + self.formats = [self.default_format[os.name]] + except KeyError: + raise DistutilsPlatformError( + "don't know how to create built distributions " + "on platform %s" % os.name) + + if self.dist_dir is None: + self.dist_dir = "dist" + + def run(self): + # Figure out which sub-commands we need to run. + commands = [] + for format in self.formats: + try: + commands.append(self.format_command[format][0]) + except KeyError: + raise DistutilsOptionError("invalid format '%s'" % format) + + # Reinitialize and run each command. + for i in range(len(self.formats)): + cmd_name = commands[i] + sub_cmd = self.reinitialize_command(cmd_name) + if cmd_name not in self.no_format_option: + sub_cmd.format = self.formats[i] + + # passing the owner and group names for tar archiving + if cmd_name == 'bdist_dumb': + sub_cmd.owner = self.owner + sub_cmd.group = self.group + + # If we're going to need to run this command again, tell it to + # keep its temporary files around so subsequent runs go faster. + if cmd_name in commands[i+1:]: + sub_cmd.keep_temp = 1 + self.run_command(cmd_name) diff --git a/distutils/command/bdist_dumb.py b/distutils/command/bdist_dumb.py new file mode 100644 index 00000000..f0d6b5b8 --- /dev/null +++ b/distutils/command/bdist_dumb.py @@ -0,0 +1,123 @@ +"""distutils.command.bdist_dumb + +Implements the Distutils 'bdist_dumb' command (create a "dumb" built +distribution -- i.e., just an archive to be unpacked under $prefix or +$exec_prefix).""" + +import os +from distutils.core import Command +from distutils.util import get_platform +from distutils.dir_util import remove_tree, ensure_relative +from distutils.errors import * +from distutils.sysconfig import get_python_version +from distutils import log + +class bdist_dumb(Command): + + description = "create a \"dumb\" built distribution" + + user_options = [('bdist-dir=', 'd', + "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('format=', 'f', + "archive format to create (tar, gztar, bztar, xztar, " + "ztar, zip)"), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('relative', None, + "build the archive using relative paths " + "(default: false)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), + ] + + boolean_options = ['keep-temp', 'skip-build', 'relative'] + + default_format = { 'posix': 'gztar', + 'nt': 'zip' } + + def initialize_options(self): + self.bdist_dir = None + self.plat_name = None + self.format = None + self.keep_temp = 0 + self.dist_dir = None + self.skip_build = None + self.relative = 0 + self.owner = None + self.group = None + + def finalize_options(self): + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'dumb') + + if self.format is None: + try: + self.format = self.default_format[os.name] + except KeyError: + raise DistutilsPlatformError( + "don't know how to create dumb built distributions " + "on platform %s" % os.name) + + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ('skip_build', 'skip_build')) + + def run(self): + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.root = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + + log.info("installing to %s", self.bdist_dir) + self.run_command('install') + + # And make an archive relative to the root of the + # pseudo-installation tree. + archive_basename = "%s.%s" % (self.distribution.get_fullname(), + self.plat_name) + + pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) + if not self.relative: + archive_root = self.bdist_dir + else: + if (self.distribution.has_ext_modules() and + (install.install_base != install.install_platbase)): + raise DistutilsPlatformError( + "can't make a dumb built distribution where " + "base and platbase are different (%s, %s)" + % (repr(install.install_base), + repr(install.install_platbase))) + else: + archive_root = os.path.join(self.bdist_dir, + ensure_relative(install.install_base)) + + # Make the archive + filename = self.make_archive(pseudoinstall_root, + self.format, root_dir=archive_root, + owner=self.owner, group=self.group) + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + self.distribution.dist_files.append(('bdist_dumb', pyversion, + filename)) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) diff --git a/distutils/command/bdist_msi.py b/distutils/command/bdist_msi.py new file mode 100644 index 00000000..0863a188 --- /dev/null +++ b/distutils/command/bdist_msi.py @@ -0,0 +1,749 @@ +# Copyright (C) 2005, 2006 Martin von Löwis +# Licensed to PSF under a Contributor Agreement. +# The bdist_wininst command proper +# based on bdist_wininst +""" +Implements the bdist_msi command. +""" + +import os +import sys +import warnings +from distutils.core import Command +from distutils.dir_util import remove_tree +from distutils.sysconfig import get_python_version +from distutils.version import StrictVersion +from distutils.errors import DistutilsOptionError +from distutils.util import get_platform +from distutils import log +import msilib +from msilib import schema, sequence, text +from msilib import Directory, Feature, Dialog, add_data + +class PyDialog(Dialog): + """Dialog class with a fixed layout: controls at the top, then a ruler, + then a list of buttons: back, next, cancel. Optionally a bitmap at the + left.""" + def __init__(self, *args, **kw): + """Dialog(database, name, x, y, w, h, attributes, title, first, + default, cancel, bitmap=true)""" + Dialog.__init__(self, *args) + ruler = self.h - 36 + bmwidth = 152*ruler/328 + #if kw.get("bitmap", True): + # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin") + self.line("BottomLine", 0, ruler, self.w, 0) + + def title(self, title): + "Set the title text of the dialog at the top." + # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix, + # text, in VerdanaBold10 + self.text("Title", 15, 10, 320, 60, 0x30003, + r"{\VerdanaBold10}%s" % title) + + def back(self, title, next, name = "Back", active = 1): + """Add a back button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next) + + def cancel(self, title, next, name = "Cancel", active = 1): + """Add a cancel button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next) + + def next(self, title, next, name = "Next", active = 1): + """Add a Next button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next) + + def xbutton(self, name, title, next, xpos): + """Add a button with a given title, the tab-next button, + its name in the Control table, giving its x position; the + y-position is aligned with the other buttons. + + Return the button, so that events can be associated""" + return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next) + +class bdist_msi(Command): + + description = "create a Microsoft Installer (.msi) binary distribution" + + user_options = [('bdist-dir=', None, + "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('target-version=', None, + "require a specific python version" + + " on the target system"), + ('no-target-compile', 'c', + "do not compile .py to .pyc on the target system"), + ('no-target-optimize', 'o', + "do not compile .py to .pyo (optimized) " + "on the target system"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('install-script=', None, + "basename of installation script to be run after " + "installation or before deinstallation"), + ('pre-install-script=', None, + "Fully qualified filename of a script to be run before " + "any files are installed. This script need not be in the " + "distribution"), + ] + + boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', + 'skip-build'] + + all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4', + '2.5', '2.6', '2.7', '2.8', '2.9', + '3.0', '3.1', '3.2', '3.3', '3.4', + '3.5', '3.6', '3.7', '3.8', '3.9'] + other_version = 'X' + + def __init__(self, *args, **kw): + super().__init__(*args, **kw) + warnings.warn("bdist_msi command is deprecated since Python 3.9, " + "use bdist_wheel (wheel packages) instead", + DeprecationWarning, 2) + + def initialize_options(self): + self.bdist_dir = None + self.plat_name = None + self.keep_temp = 0 + self.no_target_compile = 0 + self.no_target_optimize = 0 + self.target_version = None + self.dist_dir = None + self.skip_build = None + self.install_script = None + self.pre_install_script = None + self.versions = None + + def finalize_options(self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'msi') + + short_version = get_python_version() + if (not self.target_version) and self.distribution.has_ext_modules(): + self.target_version = short_version + + if self.target_version: + self.versions = [self.target_version] + if not self.skip_build and self.distribution.has_ext_modules()\ + and self.target_version != short_version: + raise DistutilsOptionError( + "target version can only be %s, or the '--skip-build'" + " option must be specified" % (short_version,)) + else: + self.versions = list(self.all_versions) + + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ) + + if self.pre_install_script: + raise DistutilsOptionError( + "the pre-install-script feature is not yet implemented") + + if self.install_script: + for script in self.distribution.scripts: + if self.install_script == os.path.basename(script): + break + else: + raise DistutilsOptionError( + "install_script '%s' not found in scripts" + % self.install_script) + self.install_script_key = None + + def run(self): + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.prefix = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + + install_lib = self.reinitialize_command('install_lib') + # we do not want to include pyc or pyo files + install_lib.compile = 0 + install_lib.optimize = 0 + + if self.distribution.has_ext_modules(): + # If we are building an installer for a Python version other + # than the one we are currently running, then we need to ensure + # our build_lib reflects the other Python version rather than ours. + # Note that for target_version!=sys.version, we must have skipped the + # build step, so there is no issue with enforcing the build of this + # version. + target_version = self.target_version + if not target_version: + assert self.skip_build, "Should have already checked this" + target_version = '%d.%d' % sys.version_info[:2] + plat_specifier = ".%s-%s" % (self.plat_name, target_version) + build = self.get_finalized_command('build') + build.build_lib = os.path.join(build.build_base, + 'lib' + plat_specifier) + + log.info("installing to %s", self.bdist_dir) + install.ensure_finalized() + + # avoid warning of 'install_lib' about installing + # into a directory not in sys.path + sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) + + install.run() + + del sys.path[0] + + self.mkpath(self.dist_dir) + fullname = self.distribution.get_fullname() + installer_name = self.get_installer_filename(fullname) + installer_name = os.path.abspath(installer_name) + if os.path.exists(installer_name): os.unlink(installer_name) + + metadata = self.distribution.metadata + author = metadata.author + if not author: + author = metadata.maintainer + if not author: + author = "UNKNOWN" + version = metadata.get_version() + # ProductVersion must be strictly numeric + # XXX need to deal with prerelease versions + sversion = "%d.%d.%d" % StrictVersion(version).version + # Prefix ProductName with Python x.y, so that + # it sorts together with the other Python packages + # in Add-Remove-Programs (APR) + fullname = self.distribution.get_fullname() + if self.target_version: + product_name = "Python %s %s" % (self.target_version, fullname) + else: + product_name = "Python %s" % (fullname) + self.db = msilib.init_database(installer_name, schema, + product_name, msilib.gen_uuid(), + sversion, author) + msilib.add_tables(self.db, sequence) + props = [('DistVersion', version)] + email = metadata.author_email or metadata.maintainer_email + if email: + props.append(("ARPCONTACT", email)) + if metadata.url: + props.append(("ARPURLINFOABOUT", metadata.url)) + if props: + add_data(self.db, 'Property', props) + + self.add_find_python() + self.add_files() + self.add_scripts() + self.add_ui() + self.db.Commit() + + if hasattr(self.distribution, 'dist_files'): + tup = 'bdist_msi', self.target_version or 'any', fullname + self.distribution.dist_files.append(tup) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) + + def add_files(self): + db = self.db + cab = msilib.CAB("distfiles") + rootdir = os.path.abspath(self.bdist_dir) + + root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir") + f = Feature(db, "Python", "Python", "Everything", + 0, 1, directory="TARGETDIR") + + items = [(f, root, '')] + for version in self.versions + [self.other_version]: + target = "TARGETDIR" + version + name = default = "Python" + version + desc = "Everything" + if version is self.other_version: + title = "Python from another location" + level = 2 + else: + title = "Python %s from registry" % version + level = 1 + f = Feature(db, name, title, desc, 1, level, directory=target) + dir = Directory(db, cab, root, rootdir, target, default) + items.append((f, dir, version)) + db.Commit() + + seen = {} + for feature, dir, version in items: + todo = [dir] + while todo: + dir = todo.pop() + for file in os.listdir(dir.absolute): + afile = os.path.join(dir.absolute, file) + if os.path.isdir(afile): + short = "%s|%s" % (dir.make_short(file), file) + default = file + version + newdir = Directory(db, cab, dir, file, default, short) + todo.append(newdir) + else: + if not dir.component: + dir.start_component(dir.logical, feature, 0) + if afile not in seen: + key = seen[afile] = dir.add_file(file) + if file==self.install_script: + if self.install_script_key: + raise DistutilsOptionError( + "Multiple files with name %s" % file) + self.install_script_key = '[#%s]' % key + else: + key = seen[afile] + add_data(self.db, "DuplicateFile", + [(key + version, dir.component, key, None, dir.logical)]) + db.Commit() + cab.commit(db) + + def add_find_python(self): + """Adds code to the installer to compute the location of Python. + + Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the + registry for each version of Python. + + Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined, + else from PYTHON.MACHINE.X.Y. + + Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe""" + + start = 402 + for ver in self.versions: + install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver + machine_reg = "python.machine." + ver + user_reg = "python.user." + ver + machine_prop = "PYTHON.MACHINE." + ver + user_prop = "PYTHON.USER." + ver + machine_action = "PythonFromMachine" + ver + user_action = "PythonFromUser" + ver + exe_action = "PythonExe" + ver + target_dir_prop = "TARGETDIR" + ver + exe_prop = "PYTHON" + ver + if msilib.Win64: + # type: msidbLocatorTypeRawValue + msidbLocatorType64bit + Type = 2+16 + else: + Type = 2 + add_data(self.db, "RegLocator", + [(machine_reg, 2, install_path, None, Type), + (user_reg, 1, install_path, None, Type)]) + add_data(self.db, "AppSearch", + [(machine_prop, machine_reg), + (user_prop, user_reg)]) + add_data(self.db, "CustomAction", + [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"), + (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"), + (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"), + ]) + add_data(self.db, "InstallExecuteSequence", + [(machine_action, machine_prop, start), + (user_action, user_prop, start + 1), + (exe_action, None, start + 2), + ]) + add_data(self.db, "InstallUISequence", + [(machine_action, machine_prop, start), + (user_action, user_prop, start + 1), + (exe_action, None, start + 2), + ]) + add_data(self.db, "Condition", + [("Python" + ver, 0, "NOT TARGETDIR" + ver)]) + start += 4 + assert start < 500 + + def add_scripts(self): + if self.install_script: + start = 6800 + for ver in self.versions + [self.other_version]: + install_action = "install_script." + ver + exe_prop = "PYTHON" + ver + add_data(self.db, "CustomAction", + [(install_action, 50, exe_prop, self.install_script_key)]) + add_data(self.db, "InstallExecuteSequence", + [(install_action, "&Python%s=3" % ver, start)]) + start += 1 + # XXX pre-install scripts are currently refused in finalize_options() + # but if this feature is completed, it will also need to add + # entries for each version as the above code does + if self.pre_install_script: + scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") + with open(scriptfn, "w") as f: + # The batch file will be executed with [PYTHON], so that %1 + # is the path to the Python interpreter; %0 will be the path + # of the batch file. + # rem =""" + # %1 %0 + # exit + # """ + # + f.write('rem ="""\n%1 %0\nexit\n"""\n') + with open(self.pre_install_script) as fin: + f.write(fin.read()) + add_data(self.db, "Binary", + [("PreInstall", msilib.Binary(scriptfn)) + ]) + add_data(self.db, "CustomAction", + [("PreInstall", 2, "PreInstall", None) + ]) + add_data(self.db, "InstallExecuteSequence", + [("PreInstall", "NOT Installed", 450)]) + + + def add_ui(self): + db = self.db + x = y = 50 + w = 370 + h = 300 + title = "[ProductName] Setup" + + # see "Dialog Style Bits" + modal = 3 # visible | modal + modeless = 1 # visible + track_disk_space = 32 + + # UI customization properties + add_data(db, "Property", + # See "DefaultUIFont Property" + [("DefaultUIFont", "DlgFont8"), + # See "ErrorDialog Style Bit" + ("ErrorDialog", "ErrorDlg"), + ("Progress1", "Install"), # modified in maintenance type dlg + ("Progress2", "installs"), + ("MaintenanceForm_Action", "Repair"), + # possible values: ALL, JUSTME + ("WhichUsers", "ALL") + ]) + + # Fonts, see "TextStyle Table" + add_data(db, "TextStyle", + [("DlgFont8", "Tahoma", 9, None, 0), + ("DlgFontBold8", "Tahoma", 8, None, 1), #bold + ("VerdanaBold10", "Verdana", 10, None, 1), + ("VerdanaRed9", "Verdana", 9, 255, 0), + ]) + + # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table" + # Numbers indicate sequence; see sequence.py for how these action integrate + add_data(db, "InstallUISequence", + [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140), + ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141), + # In the user interface, assume all-users installation if privileged. + ("SelectFeaturesDlg", "Not Installed", 1230), + # XXX no support for resume installations yet + #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240), + ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), + ("ProgressDlg", None, 1280)]) + + add_data(db, 'ActionText', text.ActionText) + add_data(db, 'UIText', text.UIText) + ##################################################################### + # Standard dialogs: FatalError, UserExit, ExitDialog + fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + fatal.title("[ProductName] Installer ended prematurely") + fatal.back("< Back", "Finish", active = 0) + fatal.cancel("Cancel", "Back", active = 0) + fatal.text("Description1", 15, 70, 320, 80, 0x30003, + "[ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again.") + fatal.text("Description2", 15, 155, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c=fatal.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Exit") + + user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + user_exit.title("[ProductName] Installer was interrupted") + user_exit.back("< Back", "Finish", active = 0) + user_exit.cancel("Cancel", "Back", active = 0) + user_exit.text("Description1", 15, 70, 320, 80, 0x30003, + "[ProductName] setup was interrupted. Your system has not been modified. " + "To install this program at a later time, please run the installation again.") + user_exit.text("Description2", 15, 155, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c = user_exit.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Exit") + + exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + exit_dialog.title("Completing the [ProductName] Installer") + exit_dialog.back("< Back", "Finish", active = 0) + exit_dialog.cancel("Cancel", "Back", active = 0) + exit_dialog.text("Description", 15, 235, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c = exit_dialog.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Return") + + ##################################################################### + # Required dialog: FilesInUse, ErrorDlg + inuse = PyDialog(db, "FilesInUse", + x, y, w, h, + 19, # KeepModeless|Modal|Visible + title, + "Retry", "Retry", "Retry", bitmap=False) + inuse.text("Title", 15, 6, 200, 15, 0x30003, + r"{\DlgFontBold8}Files in Use") + inuse.text("Description", 20, 23, 280, 20, 0x30003, + "Some files that need to be updated are currently in use.") + inuse.text("Text", 20, 55, 330, 50, 3, + "The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.") + inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess", + None, None, None) + c=inuse.back("Exit", "Ignore", name="Exit") + c.event("EndDialog", "Exit") + c=inuse.next("Ignore", "Retry", name="Ignore") + c.event("EndDialog", "Ignore") + c=inuse.cancel("Retry", "Exit", name="Retry") + c.event("EndDialog","Retry") + + # See "Error Dialog". See "ICE20" for the required names of the controls. + error = Dialog(db, "ErrorDlg", + 50, 10, 330, 101, + 65543, # Error|Minimize|Modal|Visible + title, + "ErrorText", None, None) + error.text("ErrorText", 50,9,280,48,3, "") + #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None) + error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo") + error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes") + error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort") + error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel") + error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore") + error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk") + error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry") + + ##################################################################### + # Global "Query Cancel" dialog + cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title, + "No", "No", "No") + cancel.text("Text", 48, 15, 194, 30, 3, + "Are you sure you want to cancel [ProductName] installation?") + #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None, + # "py.ico", None, None) + c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No") + c.event("EndDialog", "Exit") + + c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes") + c.event("EndDialog", "Return") + + ##################################################################### + # Global "Wait for costing" dialog + costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title, + "Return", "Return", "Return") + costing.text("Text", 48, 15, 194, 30, 3, + "Please wait while the installer finishes determining your disk space requirements.") + c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None) + c.event("EndDialog", "Exit") + + ##################################################################### + # Preparation dialog: no user input except cancellation + prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title, + "Cancel", "Cancel", "Cancel") + prep.text("Description", 15, 70, 320, 40, 0x30003, + "Please wait while the Installer prepares to guide you through the installation.") + prep.title("Welcome to the [ProductName] Installer") + c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...") + c.mapping("ActionText", "Text") + c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None) + c.mapping("ActionData", "Text") + prep.back("Back", None, active=0) + prep.next("Next", None, active=0) + c=prep.cancel("Cancel", None) + c.event("SpawnDialog", "CancelDlg") + + ##################################################################### + # Feature (Python directory) selection + seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title, + "Next", "Next", "Cancel") + seldlg.title("Select Python Installations") + + seldlg.text("Hint", 15, 30, 300, 20, 3, + "Select the Python locations where %s should be installed." + % self.distribution.get_fullname()) + + seldlg.back("< Back", None, active=0) + c = seldlg.next("Next >", "Cancel") + order = 1 + c.event("[TARGETDIR]", "[SourceDir]", ordering=order) + for version in self.versions + [self.other_version]: + order += 1 + c.event("[TARGETDIR]", "[TARGETDIR%s]" % version, + "FEATURE_SELECTED AND &Python%s=3" % version, + ordering=order) + c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1) + c.event("EndDialog", "Return", ordering=order + 2) + c = seldlg.cancel("Cancel", "Features") + c.event("SpawnDialog", "CancelDlg") + + c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3, + "FEATURE", None, "PathEdit", None) + c.event("[FEATURE_SELECTED]", "1") + ver = self.other_version + install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver + dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver + + c = seldlg.text("Other", 15, 200, 300, 15, 3, + "Provide an alternate Python location") + c.condition("Enable", install_other_cond) + c.condition("Show", install_other_cond) + c.condition("Disable", dont_install_other_cond) + c.condition("Hide", dont_install_other_cond) + + c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1, + "TARGETDIR" + ver, None, "Next", None) + c.condition("Enable", install_other_cond) + c.condition("Show", install_other_cond) + c.condition("Disable", dont_install_other_cond) + c.condition("Hide", dont_install_other_cond) + + ##################################################################### + # Disk cost + cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title, + "OK", "OK", "OK", bitmap=False) + cost.text("Title", 15, 6, 200, 15, 0x30003, + r"{\DlgFontBold8}Disk Space Requirements") + cost.text("Description", 20, 20, 280, 20, 0x30003, + "The disk space required for the installation of the selected features.") + cost.text("Text", 20, 53, 330, 60, 3, + "The highlighted volumes (if any) do not have enough disk space " + "available for the currently selected features. You can either " + "remove some files from the highlighted volumes, or choose to " + "install less features onto local drive(s), or select different " + "destination drive(s).") + cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223, + None, "{120}{70}{70}{70}{70}", None, None) + cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return") + + ##################################################################### + # WhichUsers Dialog. Only available on NT, and for privileged users. + # This must be run before FindRelatedProducts, because that will + # take into account whether the previous installation was per-user + # or per-machine. We currently don't support going back to this + # dialog after "Next" was selected; to support this, we would need to + # find how to reset the ALLUSERS property, and how to re-run + # FindRelatedProducts. + # On Windows9x, the ALLUSERS property is ignored on the command line + # and in the Property table, but installer fails according to the documentation + # if a dialog attempts to set ALLUSERS. + whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title, + "AdminInstall", "Next", "Cancel") + whichusers.title("Select whether to install [ProductName] for all users of this computer.") + # A radio group with two options: allusers, justme + g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3, + "WhichUsers", "", "Next") + g.add("ALL", 0, 5, 150, 20, "Install for all users") + g.add("JUSTME", 0, 25, 150, 20, "Install just for me") + + whichusers.back("Back", None, active=0) + + c = whichusers.next("Next >", "Cancel") + c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) + c.event("EndDialog", "Return", ordering = 2) + + c = whichusers.cancel("Cancel", "AdminInstall") + c.event("SpawnDialog", "CancelDlg") + + ##################################################################### + # Installation Progress dialog (modeless) + progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title, + "Cancel", "Cancel", "Cancel", bitmap=False) + progress.text("Title", 20, 15, 200, 15, 0x30003, + r"{\DlgFontBold8}[Progress1] [ProductName]") + progress.text("Text", 35, 65, 300, 30, 3, + "Please wait while the Installer [Progress2] [ProductName]. " + "This may take several minutes.") + progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:") + + c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...") + c.mapping("ActionText", "Text") + + #c=progress.text("ActionData", 35, 140, 300, 20, 3, None) + #c.mapping("ActionData", "Text") + + c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537, + None, "Progress done", None, None) + c.mapping("SetProgress", "Progress") + + progress.back("< Back", "Next", active=False) + progress.next("Next >", "Cancel", active=False) + progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg") + + ################################################################### + # Maintenance type: repair/uninstall + maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title, + "Next", "Next", "Cancel") + maint.title("Welcome to the [ProductName] Setup Wizard") + maint.text("BodyText", 15, 63, 330, 42, 3, + "Select whether you want to repair or remove [ProductName].") + g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3, + "MaintenanceForm_Action", "", "Next") + #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]") + g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]") + g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]") + + maint.back("< Back", None, active=False) + c=maint.next("Finish", "Cancel") + # Change installation: Change progress dialog to "Change", then ask + # for feature selection + #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1) + #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2) + + # Reinstall: Change progress dialog to "Repair", then invoke reinstall + # Also set list of reinstalled features to "ALL" + c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5) + c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6) + c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7) + c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8) + + # Uninstall: Change progress to "Remove", then invoke uninstall + # Also set list of removed features to "ALL" + c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11) + c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12) + c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13) + c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14) + + # Close dialog when maintenance action scheduled + c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20) + #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21) + + maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg") + + def get_installer_filename(self, fullname): + # Factored out to allow overriding in subclasses + if self.target_version: + base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, + self.target_version) + else: + base_name = "%s.%s.msi" % (fullname, self.plat_name) + installer_name = os.path.join(self.dist_dir, base_name) + return installer_name diff --git a/distutils/command/bdist_rpm.py b/distutils/command/bdist_rpm.py new file mode 100644 index 00000000..550cbfa1 --- /dev/null +++ b/distutils/command/bdist_rpm.py @@ -0,0 +1,579 @@ +"""distutils.command.bdist_rpm + +Implements the Distutils 'bdist_rpm' command (create RPM source and binary +distributions).""" + +import subprocess, sys, os +from distutils.core import Command +from distutils.debug import DEBUG +from distutils.file_util import write_file +from distutils.errors import * +from distutils.sysconfig import get_python_version +from distutils import log + +class bdist_rpm(Command): + + description = "create an RPM distribution" + + user_options = [ + ('bdist-base=', None, + "base directory for creating built distributions"), + ('rpm-base=', None, + "base directory for creating RPMs (defaults to \"rpm\" under " + "--bdist-base; must be specified for RPM 2)"), + ('dist-dir=', 'd', + "directory to put final RPM files in " + "(and .spec files if --spec-only)"), + ('python=', None, + "path to Python interpreter to hard-code in the .spec file " + "(default: \"python\")"), + ('fix-python', None, + "hard-code the exact path to the current Python interpreter in " + "the .spec file"), + ('spec-only', None, + "only regenerate spec file"), + ('source-only', None, + "only generate source RPM"), + ('binary-only', None, + "only generate binary RPM"), + ('use-bzip2', None, + "use bzip2 instead of gzip to create source distribution"), + + # More meta-data: too RPM-specific to put in the setup script, + # but needs to go in the .spec file -- so we make these options + # to "bdist_rpm". The idea is that packagers would put this + # info in setup.cfg, although they are of course free to + # supply it on the command line. + ('distribution-name=', None, + "name of the (Linux) distribution to which this " + "RPM applies (*not* the name of the module distribution!)"), + ('group=', None, + "package classification [default: \"Development/Libraries\"]"), + ('release=', None, + "RPM release number"), + ('serial=', None, + "RPM serial number"), + ('vendor=', None, + "RPM \"vendor\" (eg. \"Joe Blow \") " + "[default: maintainer or author from setup script]"), + ('packager=', None, + "RPM packager (eg. \"Jane Doe \") " + "[default: vendor]"), + ('doc-files=', None, + "list of documentation files (space or comma-separated)"), + ('changelog=', None, + "RPM changelog"), + ('icon=', None, + "name of icon file"), + ('provides=', None, + "capabilities provided by this package"), + ('requires=', None, + "capabilities required by this package"), + ('conflicts=', None, + "capabilities which conflict with this package"), + ('build-requires=', None, + "capabilities required to build this package"), + ('obsoletes=', None, + "capabilities made obsolete by this package"), + ('no-autoreq', None, + "do not automatically calculate dependencies"), + + # Actions to take when building RPM + ('keep-temp', 'k', + "don't clean up RPM build directory"), + ('no-keep-temp', None, + "clean up RPM build directory [default]"), + ('use-rpm-opt-flags', None, + "compile with RPM_OPT_FLAGS when building from source RPM"), + ('no-rpm-opt-flags', None, + "do not pass any RPM CFLAGS to compiler"), + ('rpm3-mode', None, + "RPM 3 compatibility mode (default)"), + ('rpm2-mode', None, + "RPM 2 compatibility mode"), + + # Add the hooks necessary for specifying custom scripts + ('prep-script=', None, + "Specify a script for the PREP phase of RPM building"), + ('build-script=', None, + "Specify a script for the BUILD phase of RPM building"), + + ('pre-install=', None, + "Specify a script for the pre-INSTALL phase of RPM building"), + ('install-script=', None, + "Specify a script for the INSTALL phase of RPM building"), + ('post-install=', None, + "Specify a script for the post-INSTALL phase of RPM building"), + + ('pre-uninstall=', None, + "Specify a script for the pre-UNINSTALL phase of RPM building"), + ('post-uninstall=', None, + "Specify a script for the post-UNINSTALL phase of RPM building"), + + ('clean-script=', None, + "Specify a script for the CLEAN phase of RPM building"), + + ('verify-script=', None, + "Specify a script for the VERIFY phase of the RPM build"), + + # Allow a packager to explicitly force an architecture + ('force-arch=', None, + "Force an architecture onto the RPM build process"), + + ('quiet', 'q', + "Run the INSTALL phase of RPM building in quiet mode"), + ] + + boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', + 'no-autoreq', 'quiet'] + + negative_opt = {'no-keep-temp': 'keep-temp', + 'no-rpm-opt-flags': 'use-rpm-opt-flags', + 'rpm2-mode': 'rpm3-mode'} + + + def initialize_options(self): + self.bdist_base = None + self.rpm_base = None + self.dist_dir = None + self.python = None + self.fix_python = None + self.spec_only = None + self.binary_only = None + self.source_only = None + self.use_bzip2 = None + + self.distribution_name = None + self.group = None + self.release = None + self.serial = None + self.vendor = None + self.packager = None + self.doc_files = None + self.changelog = None + self.icon = None + + self.prep_script = None + self.build_script = None + self.install_script = None + self.clean_script = None + self.verify_script = None + self.pre_install = None + self.post_install = None + self.pre_uninstall = None + self.post_uninstall = None + self.prep = None + self.provides = None + self.requires = None + self.conflicts = None + self.build_requires = None + self.obsoletes = None + + self.keep_temp = 0 + self.use_rpm_opt_flags = 1 + self.rpm3_mode = 1 + self.no_autoreq = 0 + + self.force_arch = None + self.quiet = 0 + + def finalize_options(self): + self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) + if self.rpm_base is None: + if not self.rpm3_mode: + raise DistutilsOptionError( + "you must specify --rpm-base in RPM 2 mode") + self.rpm_base = os.path.join(self.bdist_base, "rpm") + + if self.python is None: + if self.fix_python: + self.python = sys.executable + else: + self.python = "python3" + elif self.fix_python: + raise DistutilsOptionError( + "--python and --fix-python are mutually exclusive options") + + if os.name != 'posix': + raise DistutilsPlatformError("don't know how to create RPM " + "distributions on platform %s" % os.name) + if self.binary_only and self.source_only: + raise DistutilsOptionError( + "cannot supply both '--source-only' and '--binary-only'") + + # don't pass CFLAGS to pure python distributions + if not self.distribution.has_ext_modules(): + self.use_rpm_opt_flags = 0 + + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + self.finalize_package_data() + + def finalize_package_data(self): + self.ensure_string('group', "Development/Libraries") + self.ensure_string('vendor', + "%s <%s>" % (self.distribution.get_contact(), + self.distribution.get_contact_email())) + self.ensure_string('packager') + self.ensure_string_list('doc_files') + if isinstance(self.doc_files, list): + for readme in ('README', 'README.txt'): + if os.path.exists(readme) and readme not in self.doc_files: + self.doc_files.append(readme) + + self.ensure_string('release', "1") + self.ensure_string('serial') # should it be an int? + + self.ensure_string('distribution_name') + + self.ensure_string('changelog') + # Format changelog correctly + self.changelog = self._format_changelog(self.changelog) + + self.ensure_filename('icon') + + self.ensure_filename('prep_script') + self.ensure_filename('build_script') + self.ensure_filename('install_script') + self.ensure_filename('clean_script') + self.ensure_filename('verify_script') + self.ensure_filename('pre_install') + self.ensure_filename('post_install') + self.ensure_filename('pre_uninstall') + self.ensure_filename('post_uninstall') + + # XXX don't forget we punted on summaries and descriptions -- they + # should be handled here eventually! + + # Now *this* is some meta-data that belongs in the setup script... + self.ensure_string_list('provides') + self.ensure_string_list('requires') + self.ensure_string_list('conflicts') + self.ensure_string_list('build_requires') + self.ensure_string_list('obsoletes') + + self.ensure_string('force_arch') + + def run(self): + if DEBUG: + print("before _get_package_data():") + print("vendor =", self.vendor) + print("packager =", self.packager) + print("doc_files =", self.doc_files) + print("changelog =", self.changelog) + + # make directories + if self.spec_only: + spec_dir = self.dist_dir + self.mkpath(spec_dir) + else: + rpm_dir = {} + for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): + rpm_dir[d] = os.path.join(self.rpm_base, d) + self.mkpath(rpm_dir[d]) + spec_dir = rpm_dir['SPECS'] + + # Spec file goes into 'dist_dir' if '--spec-only specified', + # build/rpm. otherwise. + spec_path = os.path.join(spec_dir, + "%s.spec" % self.distribution.get_name()) + self.execute(write_file, + (spec_path, + self._make_spec_file()), + "writing '%s'" % spec_path) + + if self.spec_only: # stop if requested + return + + # Make a source distribution and copy to SOURCES directory with + # optional icon. + saved_dist_files = self.distribution.dist_files[:] + sdist = self.reinitialize_command('sdist') + if self.use_bzip2: + sdist.formats = ['bztar'] + else: + sdist.formats = ['gztar'] + self.run_command('sdist') + self.distribution.dist_files = saved_dist_files + + source = sdist.get_archive_files()[0] + source_dir = rpm_dir['SOURCES'] + self.copy_file(source, source_dir) + + if self.icon: + if os.path.exists(self.icon): + self.copy_file(self.icon, source_dir) + else: + raise DistutilsFileError( + "icon file '%s' does not exist" % self.icon) + + # build package + log.info("building RPMs") + rpm_cmd = ['rpmbuild'] + + if self.source_only: # what kind of RPMs? + rpm_cmd.append('-bs') + elif self.binary_only: + rpm_cmd.append('-bb') + else: + rpm_cmd.append('-ba') + rpm_cmd.extend(['--define', '__python %s' % self.python]) + if self.rpm3_mode: + rpm_cmd.extend(['--define', + '_topdir %s' % os.path.abspath(self.rpm_base)]) + if not self.keep_temp: + rpm_cmd.append('--clean') + + if self.quiet: + rpm_cmd.append('--quiet') + + rpm_cmd.append(spec_path) + # Determine the binary rpm names that should be built out of this spec + # file + # Note that some of these may not be really built (if the file + # list is empty) + nvr_string = "%{name}-%{version}-%{release}" + src_rpm = nvr_string + ".src.rpm" + non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" + q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % ( + src_rpm, non_src_rpm, spec_path) + + out = os.popen(q_cmd) + try: + binary_rpms = [] + source_rpm = None + while True: + line = out.readline() + if not line: + break + l = line.strip().split() + assert(len(l) == 2) + binary_rpms.append(l[1]) + # The source rpm is named after the first entry in the spec file + if source_rpm is None: + source_rpm = l[0] + + status = out.close() + if status: + raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + + finally: + out.close() + + self.spawn(rpm_cmd) + + if not self.dry_run: + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + + if not self.binary_only: + srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) + assert(os.path.exists(srpm)) + self.move_file(srpm, self.dist_dir) + filename = os.path.join(self.dist_dir, source_rpm) + self.distribution.dist_files.append( + ('bdist_rpm', pyversion, filename)) + + if not self.source_only: + for rpm in binary_rpms: + rpm = os.path.join(rpm_dir['RPMS'], rpm) + if os.path.exists(rpm): + self.move_file(rpm, self.dist_dir) + filename = os.path.join(self.dist_dir, + os.path.basename(rpm)) + self.distribution.dist_files.append( + ('bdist_rpm', pyversion, filename)) + + def _dist_path(self, path): + return os.path.join(self.dist_dir, os.path.basename(path)) + + def _make_spec_file(self): + """Generate the text of an RPM spec file and return it as a + list of strings (one per line). + """ + # definitions and headers + spec_file = [ + '%define name ' + self.distribution.get_name(), + '%define version ' + self.distribution.get_version().replace('-','_'), + '%define unmangled_version ' + self.distribution.get_version(), + '%define release ' + self.release.replace('-','_'), + '', + 'Summary: ' + self.distribution.get_description(), + ] + + # Workaround for #14443 which affects some RPM based systems such as + # RHEL6 (and probably derivatives) + vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}') + # Generate a potential replacement value for __os_install_post (whilst + # normalizing the whitespace to simplify the test for whether the + # invocation of brp-python-bytecompile passes in __python): + vendor_hook = '\n'.join([' %s \\' % line.strip() + for line in vendor_hook.splitlines()]) + problem = "brp-python-bytecompile \\\n" + fixed = "brp-python-bytecompile %{__python} \\\n" + fixed_hook = vendor_hook.replace(problem, fixed) + if fixed_hook != vendor_hook: + spec_file.append('# Workaround for http://bugs.python.org/issue14443') + spec_file.append('%define __os_install_post ' + fixed_hook + '\n') + + # put locale summaries into spec file + # XXX not supported for now (hard to put a dictionary + # in a config file -- arg!) + #for locale in self.summaries.keys(): + # spec_file.append('Summary(%s): %s' % (locale, + # self.summaries[locale])) + + spec_file.extend([ + 'Name: %{name}', + 'Version: %{version}', + 'Release: %{release}',]) + + # XXX yuck! this filename is available from the "sdist" command, + # but only after it has run: and we create the spec file before + # running "sdist", in case of --spec-only. + if self.use_bzip2: + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') + else: + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') + + spec_file.extend([ + 'License: ' + self.distribution.get_license(), + 'Group: ' + self.group, + 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', + 'Prefix: %{_prefix}', ]) + + if not self.force_arch: + # noarch if no extension modules + if not self.distribution.has_ext_modules(): + spec_file.append('BuildArch: noarch') + else: + spec_file.append( 'BuildArch: %s' % self.force_arch ) + + for field in ('Vendor', + 'Packager', + 'Provides', + 'Requires', + 'Conflicts', + 'Obsoletes', + ): + val = getattr(self, field.lower()) + if isinstance(val, list): + spec_file.append('%s: %s' % (field, ' '.join(val))) + elif val is not None: + spec_file.append('%s: %s' % (field, val)) + + + if self.distribution.get_url() != 'UNKNOWN': + spec_file.append('Url: ' + self.distribution.get_url()) + + if self.distribution_name: + spec_file.append('Distribution: ' + self.distribution_name) + + if self.build_requires: + spec_file.append('BuildRequires: ' + + ' '.join(self.build_requires)) + + if self.icon: + spec_file.append('Icon: ' + os.path.basename(self.icon)) + + if self.no_autoreq: + spec_file.append('AutoReq: 0') + + spec_file.extend([ + '', + '%description', + self.distribution.get_long_description() + ]) + + # put locale descriptions into spec file + # XXX again, suppressed because config file syntax doesn't + # easily support this ;-( + #for locale in self.descriptions.keys(): + # spec_file.extend([ + # '', + # '%description -l ' + locale, + # self.descriptions[locale], + # ]) + + # rpm scripts + # figure out default build script + def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0])) + def_build = "%s build" % def_setup_call + if self.use_rpm_opt_flags: + def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build + + # insert contents of files + + # XXX this is kind of misleading: user-supplied options are files + # that we open and interpolate into the spec file, but the defaults + # are just text that we drop in as-is. Hmmm. + + install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT ' + '--record=INSTALLED_FILES') % def_setup_call + + script_options = [ + ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), + ('build', 'build_script', def_build), + ('install', 'install_script', install_cmd), + ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), + ('verifyscript', 'verify_script', None), + ('pre', 'pre_install', None), + ('post', 'post_install', None), + ('preun', 'pre_uninstall', None), + ('postun', 'post_uninstall', None), + ] + + for (rpm_opt, attr, default) in script_options: + # Insert contents of file referred to, if no file is referred to + # use 'default' as contents of script + val = getattr(self, attr) + if val or default: + spec_file.extend([ + '', + '%' + rpm_opt,]) + if val: + with open(val) as f: + spec_file.extend(f.read().split('\n')) + else: + spec_file.append(default) + + + # files section + spec_file.extend([ + '', + '%files -f INSTALLED_FILES', + '%defattr(-,root,root)', + ]) + + if self.doc_files: + spec_file.append('%doc ' + ' '.join(self.doc_files)) + + if self.changelog: + spec_file.extend([ + '', + '%changelog',]) + spec_file.extend(self.changelog) + + return spec_file + + def _format_changelog(self, changelog): + """Format the changelog correctly and convert it to a list of strings + """ + if not changelog: + return changelog + new_changelog = [] + for line in changelog.strip().split('\n'): + line = line.strip() + if line[0] == '*': + new_changelog.extend(['', line]) + elif line[0] == '-': + new_changelog.append(line) + else: + new_changelog.append(' ' + line) + + # strip trailing newline inserted by first changelog entry + if not new_changelog[0]: + del new_changelog[0] + + return new_changelog diff --git a/distutils/command/bdist_wininst.py b/distutils/command/bdist_wininst.py new file mode 100644 index 00000000..0e9ddaa2 --- /dev/null +++ b/distutils/command/bdist_wininst.py @@ -0,0 +1,377 @@ +"""distutils.command.bdist_wininst + +Implements the Distutils 'bdist_wininst' command: create a windows installer +exe-program.""" + +import os +import sys +import warnings +from distutils.core import Command +from distutils.util import get_platform +from distutils.dir_util import remove_tree +from distutils.errors import * +from distutils.sysconfig import get_python_version +from distutils import log + +class bdist_wininst(Command): + + description = "create an executable installer for MS Windows" + + user_options = [('bdist-dir=', None, + "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('target-version=', None, + "require a specific python version" + + " on the target system"), + ('no-target-compile', 'c', + "do not compile .py to .pyc on the target system"), + ('no-target-optimize', 'o', + "do not compile .py to .pyo (optimized) " + "on the target system"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('bitmap=', 'b', + "bitmap to use for the installer instead of python-powered logo"), + ('title=', 't', + "title to display on the installer background instead of default"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('install-script=', None, + "basename of installation script to be run after " + "installation or before deinstallation"), + ('pre-install-script=', None, + "Fully qualified filename of a script to be run before " + "any files are installed. This script need not be in the " + "distribution"), + ('user-access-control=', None, + "specify Vista's UAC handling - 'none'/default=no " + "handling, 'auto'=use UAC if target Python installed for " + "all users, 'force'=always use UAC"), + ] + + boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', + 'skip-build'] + + # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows + _unsupported = (sys.platform != "win32") + + def __init__(self, *args, **kw): + super().__init__(*args, **kw) + warnings.warn("bdist_wininst command is deprecated since Python 3.8, " + "use bdist_wheel (wheel packages) instead", + DeprecationWarning, 2) + + def initialize_options(self): + self.bdist_dir = None + self.plat_name = None + self.keep_temp = 0 + self.no_target_compile = 0 + self.no_target_optimize = 0 + self.target_version = None + self.dist_dir = None + self.bitmap = None + self.title = None + self.skip_build = None + self.install_script = None + self.pre_install_script = None + self.user_access_control = None + + + def finalize_options(self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + + if self.bdist_dir is None: + if self.skip_build and self.plat_name: + # If build is skipped and plat_name is overridden, bdist will + # not see the correct 'plat_name' - so set that up manually. + bdist = self.distribution.get_command_obj('bdist') + bdist.plat_name = self.plat_name + # next the command will be initialized using that name + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'wininst') + + if not self.target_version: + self.target_version = "" + + if not self.skip_build and self.distribution.has_ext_modules(): + short_version = get_python_version() + if self.target_version and self.target_version != short_version: + raise DistutilsOptionError( + "target version can only be %s, or the '--skip-build'" \ + " option must be specified" % (short_version,)) + self.target_version = short_version + + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ) + + if self.install_script: + for script in self.distribution.scripts: + if self.install_script == os.path.basename(script): + break + else: + raise DistutilsOptionError( + "install_script '%s' not found in scripts" + % self.install_script) + + def run(self): + if (sys.platform != "win32" and + (self.distribution.has_ext_modules() or + self.distribution.has_c_libraries())): + raise DistutilsPlatformError \ + ("distribution contains extensions and/or C libraries; " + "must be compiled on a Windows 32 platform") + + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.root = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + install.plat_name = self.plat_name + + install_lib = self.reinitialize_command('install_lib') + # we do not want to include pyc or pyo files + install_lib.compile = 0 + install_lib.optimize = 0 + + if self.distribution.has_ext_modules(): + # If we are building an installer for a Python version other + # than the one we are currently running, then we need to ensure + # our build_lib reflects the other Python version rather than ours. + # Note that for target_version!=sys.version, we must have skipped the + # build step, so there is no issue with enforcing the build of this + # version. + target_version = self.target_version + if not target_version: + assert self.skip_build, "Should have already checked this" + target_version = '%d.%d' % sys.version_info[:2] + plat_specifier = ".%s-%s" % (self.plat_name, target_version) + build = self.get_finalized_command('build') + build.build_lib = os.path.join(build.build_base, + 'lib' + plat_specifier) + + # Use a custom scheme for the zip-file, because we have to decide + # at installation time which scheme to use. + for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): + value = key.upper() + if key == 'headers': + value = value + '/Include/$dist_name' + setattr(install, + 'install_' + key, + value) + + log.info("installing to %s", self.bdist_dir) + install.ensure_finalized() + + # avoid warning of 'install_lib' about installing + # into a directory not in sys.path + sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) + + install.run() + + del sys.path[0] + + # And make an archive relative to the root of the + # pseudo-installation tree. + from tempfile import mktemp + archive_basename = mktemp() + fullname = self.distribution.get_fullname() + arcname = self.make_archive(archive_basename, "zip", + root_dir=self.bdist_dir) + # create an exe containing the zip-file + self.create_exe(arcname, fullname, self.bitmap) + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + self.distribution.dist_files.append(('bdist_wininst', pyversion, + self.get_installer_filename(fullname))) + # remove the zip-file again + log.debug("removing temporary file '%s'", arcname) + os.remove(arcname) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) + + def get_inidata(self): + # Return data describing the installation. + lines = [] + metadata = self.distribution.metadata + + # Write the [metadata] section. + lines.append("[metadata]") + + # 'info' will be displayed in the installer's dialog box, + # describing the items to be installed. + info = (metadata.long_description or '') + '\n' + + # Escape newline characters + def escape(s): + return s.replace("\n", "\\n") + + for name in ["author", "author_email", "description", "maintainer", + "maintainer_email", "name", "url", "version"]: + data = getattr(metadata, name, "") + if data: + info = info + ("\n %s: %s" % \ + (name.capitalize(), escape(data))) + lines.append("%s=%s" % (name, escape(data))) + + # The [setup] section contains entries controlling + # the installer runtime. + lines.append("\n[Setup]") + if self.install_script: + lines.append("install_script=%s" % self.install_script) + lines.append("info=%s" % escape(info)) + lines.append("target_compile=%d" % (not self.no_target_compile)) + lines.append("target_optimize=%d" % (not self.no_target_optimize)) + if self.target_version: + lines.append("target_version=%s" % self.target_version) + if self.user_access_control: + lines.append("user_access_control=%s" % self.user_access_control) + + title = self.title or self.distribution.get_fullname() + lines.append("title=%s" % escape(title)) + import time + import distutils + build_info = "Built %s with distutils-%s" % \ + (time.ctime(time.time()), distutils.__version__) + lines.append("build_info=%s" % build_info) + return "\n".join(lines) + + def create_exe(self, arcname, fullname, bitmap=None): + import struct + + self.mkpath(self.dist_dir) + + cfgdata = self.get_inidata() + + installer_name = self.get_installer_filename(fullname) + self.announce("creating %s" % installer_name) + + if bitmap: + with open(bitmap, "rb") as f: + bitmapdata = f.read() + bitmaplen = len(bitmapdata) + else: + bitmaplen = 0 + + with open(installer_name, "wb") as file: + file.write(self.get_exe_bytes()) + if bitmap: + file.write(bitmapdata) + + # Convert cfgdata from unicode to ascii, mbcs encoded + if isinstance(cfgdata, str): + cfgdata = cfgdata.encode("mbcs") + + # Append the pre-install script + cfgdata = cfgdata + b"\0" + if self.pre_install_script: + # We need to normalize newlines, so we open in text mode and + # convert back to bytes. "latin-1" simply avoids any possible + # failures. + with open(self.pre_install_script, "r", + encoding="latin-1") as script: + script_data = script.read().encode("latin-1") + cfgdata = cfgdata + script_data + b"\n\0" + else: + # empty pre-install script + cfgdata = cfgdata + b"\0" + file.write(cfgdata) + + # The 'magic number' 0x1234567B is used to make sure that the + # binary layout of 'cfgdata' is what the wininst.exe binary + # expects. If the layout changes, increment that number, make + # the corresponding changes to the wininst.exe sources, and + # recompile them. + header = struct.pack("' under the base build directory. We only use one of + # them for a given distribution, though -- + if self.build_purelib is None: + self.build_purelib = os.path.join(self.build_base, 'lib') + if self.build_platlib is None: + self.build_platlib = os.path.join(self.build_base, + 'lib' + plat_specifier) + + # 'build_lib' is the actual directory that we will use for this + # particular module distribution -- if user didn't supply it, pick + # one of 'build_purelib' or 'build_platlib'. + if self.build_lib is None: + if self.distribution.ext_modules: + self.build_lib = self.build_platlib + else: + self.build_lib = self.build_purelib + + # 'build_temp' -- temporary directory for compiler turds, + # "build/temp." + if self.build_temp is None: + self.build_temp = os.path.join(self.build_base, + 'temp' + plat_specifier) + if self.build_scripts is None: + self.build_scripts = os.path.join(self.build_base, + 'scripts-%d.%d' % sys.version_info[:2]) + + if self.executable is None and sys.executable: + self.executable = os.path.normpath(sys.executable) + + if isinstance(self.parallel, str): + try: + self.parallel = int(self.parallel) + except ValueError: + raise DistutilsOptionError("parallel should be an integer") + + def run(self): + # Run all relevant sub-commands. This will be some subset of: + # - build_py - pure Python modules + # - build_clib - standalone C libraries + # - build_ext - Python extensions + # - build_scripts - (Python) scripts + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + + # -- Predicates for the sub-command list --------------------------- + + def has_pure_modules(self): + return self.distribution.has_pure_modules() + + def has_c_libraries(self): + return self.distribution.has_c_libraries() + + def has_ext_modules(self): + return self.distribution.has_ext_modules() + + def has_scripts(self): + return self.distribution.has_scripts() + + + sub_commands = [('build_py', has_pure_modules), + ('build_clib', has_c_libraries), + ('build_ext', has_ext_modules), + ('build_scripts', has_scripts), + ] diff --git a/distutils/command/build_clib.py b/distutils/command/build_clib.py new file mode 100644 index 00000000..3e20ef23 --- /dev/null +++ b/distutils/command/build_clib.py @@ -0,0 +1,209 @@ +"""distutils.command.build_clib + +Implements the Distutils 'build_clib' command, to build a C/C++ library +that is included in the module distribution and needed by an extension +module.""" + + +# XXX this module has *lots* of code ripped-off quite transparently from +# build_ext.py -- not surprisingly really, as the work required to build +# a static library from a collection of C source files is not really all +# that different from what's required to build a shared object file from +# a collection of C source files. Nevertheless, I haven't done the +# necessary refactoring to account for the overlap in code between the +# two modules, mainly because a number of subtle details changed in the +# cut 'n paste. Sigh. + +import os +from distutils.core import Command +from distutils.errors import * +from distutils.sysconfig import customize_compiler +from distutils import log + +def show_compilers(): + from distutils.ccompiler import show_compilers + show_compilers() + + +class build_clib(Command): + + description = "build C/C++ libraries used by Python extensions" + + user_options = [ + ('build-clib=', 'b', + "directory to build C/C++ libraries to"), + ('build-temp=', 't', + "directory to put temporary build by-products"), + ('debug', 'g', + "compile with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps)"), + ('compiler=', 'c', + "specify the compiler type"), + ] + + boolean_options = ['debug', 'force'] + + help_options = [ + ('help-compiler', None, + "list available compilers", show_compilers), + ] + + def initialize_options(self): + self.build_clib = None + self.build_temp = None + + # List of libraries to build + self.libraries = None + + # Compilation options for all libraries + self.include_dirs = None + self.define = None + self.undef = None + self.debug = None + self.force = 0 + self.compiler = None + + + def finalize_options(self): + # This might be confusing: both build-clib and build-temp default + # to build-temp as defined by the "build" command. This is because + # I think that C libraries are really just temporary build + # by-products, at least from the point of view of building Python + # extensions -- but I want to keep my options open. + self.set_undefined_options('build', + ('build_temp', 'build_clib'), + ('build_temp', 'build_temp'), + ('compiler', 'compiler'), + ('debug', 'debug'), + ('force', 'force')) + + self.libraries = self.distribution.libraries + if self.libraries: + self.check_library_list(self.libraries) + + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + if isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + # XXX same as for build_ext -- what about 'self.define' and + # 'self.undef' ? + + + def run(self): + if not self.libraries: + return + + # Yech -- this is cut 'n pasted from build_ext.py! + from distutils.ccompiler import new_compiler + self.compiler = new_compiler(compiler=self.compiler, + dry_run=self.dry_run, + force=self.force) + customize_compiler(self.compiler) + + if self.include_dirs is not None: + self.compiler.set_include_dirs(self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name,value) in self.define: + self.compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro(macro) + + self.build_libraries(self.libraries) + + + def check_library_list(self, libraries): + """Ensure that the list of libraries is valid. + + `library` is presumably provided as a command option 'libraries'. + This method checks that it is a list of 2-tuples, where the tuples + are (library_name, build_info_dict). + + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ + if not isinstance(libraries, list): + raise DistutilsSetupError( + "'libraries' option must be a list of tuples") + + for lib in libraries: + if not isinstance(lib, tuple) and len(lib) != 2: + raise DistutilsSetupError( + "each element of 'libraries' must a 2-tuple") + + name, build_info = lib + + if not isinstance(name, str): + raise DistutilsSetupError( + "first element of each tuple in 'libraries' " + "must be a string (the library name)") + + if '/' in name or (os.sep != '/' and os.sep in name): + raise DistutilsSetupError("bad library name '%s': " + "may not contain directory separators" % lib[0]) + + if not isinstance(build_info, dict): + raise DistutilsSetupError( + "second element of each tuple in 'libraries' " + "must be a dictionary (build info)") + + + def get_library_names(self): + # Assume the library list is valid -- 'check_library_list()' is + # called from 'finalize_options()', so it should be! + if not self.libraries: + return None + + lib_names = [] + for (lib_name, build_info) in self.libraries: + lib_names.append(lib_name) + return lib_names + + + def get_source_files(self): + self.check_library_list(self.libraries) + filenames = [] + for (lib_name, build_info) in self.libraries: + sources = build_info.get('sources') + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % lib_name) + + filenames.extend(sources) + return filenames + + + def build_libraries(self, libraries): + for (lib_name, build_info) in libraries: + sources = build_info.get('sources') + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % lib_name) + sources = list(sources) + + log.info("building '%s' library", lib_name) + + # First, compile the source code to object files in the library + # directory. (This should probably change to putting object + # files in a temporary build directory.) + macros = build_info.get('macros') + include_dirs = build_info.get('include_dirs') + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=include_dirs, + debug=self.debug) + + # Now "link" the object files together into a static library. + # (On Unix at least, this isn't really linking -- it just + # builds an archive. Whatever.) + self.compiler.create_static_lib(objects, lib_name, + output_dir=self.build_clib, + debug=self.debug) diff --git a/distutils/command/build_ext.py b/distutils/command/build_ext.py new file mode 100644 index 00000000..1a9bd120 --- /dev/null +++ b/distutils/command/build_ext.py @@ -0,0 +1,754 @@ +"""distutils.command.build_ext + +Implements the Distutils 'build_ext' command, for building extension +modules (currently limited to C extensions, should accommodate C++ +extensions ASAP).""" + +import contextlib +import os +import re +import sys +from distutils.core import Command +from distutils.errors import * +from distutils.sysconfig import customize_compiler, get_python_version +from distutils.sysconfig import get_config_h_filename +from distutils.dep_util import newer_group +from distutils.extension import Extension +from distutils.util import get_platform +from distutils import log + +from site import USER_BASE + +# An extension name is just a dot-separated list of Python NAMEs (ie. +# the same as a fully-qualified module name). +extension_name_re = re.compile \ + (r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$') + + +def show_compilers (): + from distutils.ccompiler import show_compilers + show_compilers() + + +class build_ext(Command): + + description = "build C/C++ extensions (compile/link to build directory)" + + # XXX thoughts on how to deal with complex command-line options like + # these, i.e. how to make it so fancy_getopt can suck them off the + # command line and make it look like setup.py defined the appropriate + # lists of tuples of what-have-you. + # - each command needs a callback to process its command-line options + # - Command.__init__() needs access to its share of the whole + # command line (must ultimately come from + # Distribution.parse_command_line()) + # - it then calls the current command class' option-parsing + # callback to deal with weird options like -D, which have to + # parse the option text and churn out some custom data + # structure + # - that data structure (in this case, a list of 2-tuples) + # will then be present in the command object by the time + # we get to finalize_options() (i.e. the constructor + # takes care of both command-line and client options + # in between initialize_options() and finalize_options()) + + sep_by = " (separated by '%s')" % os.pathsep + user_options = [ + ('build-lib=', 'b', + "directory for compiled extension modules"), + ('build-temp=', 't', + "directory for temporary files (build by-products)"), + ('plat-name=', 'p', + "platform name to cross-compile for, if supported " + "(default: %s)" % get_platform()), + ('inplace', 'i', + "ignore build-lib and put compiled extensions into the source " + + "directory alongside your pure Python modules"), + ('include-dirs=', 'I', + "list of directories to search for header files" + sep_by), + ('define=', 'D', + "C preprocessor macros to define"), + ('undef=', 'U', + "C preprocessor macros to undefine"), + ('libraries=', 'l', + "external C libraries to link with"), + ('library-dirs=', 'L', + "directories to search for external C libraries" + sep_by), + ('rpath=', 'R', + "directories to search for shared C libraries at runtime"), + ('link-objects=', 'O', + "extra explicit link objects to include in the link"), + ('debug', 'g', + "compile/link with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps)"), + ('compiler=', 'c', + "specify the compiler type"), + ('parallel=', 'j', + "number of parallel build jobs"), + ('swig-cpp', None, + "make SWIG create C++ files (default is C)"), + ('swig-opts=', None, + "list of SWIG command line options"), + ('swig=', None, + "path to the SWIG executable"), + ('user', None, + "add user include, library and rpath") + ] + + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] + + help_options = [ + ('help-compiler', None, + "list available compilers", show_compilers), + ] + + def initialize_options(self): + self.extensions = None + self.build_lib = None + self.plat_name = None + self.build_temp = None + self.inplace = 0 + self.package = None + + self.include_dirs = None + self.define = None + self.undef = None + self.libraries = None + self.library_dirs = None + self.rpath = None + self.link_objects = None + self.debug = None + self.force = None + self.compiler = None + self.swig = None + self.swig_cpp = None + self.swig_opts = None + self.user = None + self.parallel = None + + def finalize_options(self): + from distutils import sysconfig + + self.set_undefined_options('build', + ('build_lib', 'build_lib'), + ('build_temp', 'build_temp'), + ('compiler', 'compiler'), + ('debug', 'debug'), + ('force', 'force'), + ('parallel', 'parallel'), + ('plat_name', 'plat_name'), + ) + + if self.package is None: + self.package = self.distribution.ext_package + + self.extensions = self.distribution.ext_modules + + # Make sure Python's include directories (for Python.h, pyconfig.h, + # etc.) are in the include search path. + py_include = sysconfig.get_python_inc() + plat_py_include = sysconfig.get_python_inc(plat_specific=1) + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + if isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + # If in a virtualenv, add its include directory + # Issue 16116 + if sys.exec_prefix != sys.base_exec_prefix: + self.include_dirs.append(os.path.join(sys.exec_prefix, 'include')) + + # Put the Python "system" include dir at the end, so that + # any local include dirs take precedence. + self.include_dirs.extend(py_include.split(os.path.pathsep)) + if plat_py_include != py_include: + self.include_dirs.extend( + plat_py_include.split(os.path.pathsep)) + + self.ensure_string_list('libraries') + self.ensure_string_list('link_objects') + + # Life is easier if we're not forever checking for None, so + # simplify these options to empty lists if unset + if self.libraries is None: + self.libraries = [] + if self.library_dirs is None: + self.library_dirs = [] + elif isinstance(self.library_dirs, str): + self.library_dirs = self.library_dirs.split(os.pathsep) + + if self.rpath is None: + self.rpath = [] + elif isinstance(self.rpath, str): + self.rpath = self.rpath.split(os.pathsep) + + # for extensions under windows use different directories + # for Release and Debug builds. + # also Python's library directory must be appended to library_dirs + if os.name == 'nt': + # the 'libs' directory is for binary installs - we assume that + # must be the *native* platform. But we don't really support + # cross-compiling via a binary install anyway, so we let it go. + self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) + if sys.base_exec_prefix != sys.prefix: # Issue 16116 + self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs')) + if self.debug: + self.build_temp = os.path.join(self.build_temp, "Debug") + else: + self.build_temp = os.path.join(self.build_temp, "Release") + + # Append the source distribution include and library directories, + # this allows distutils on windows to work in the source tree + self.include_dirs.append(os.path.dirname(get_config_h_filename())) + _sys_home = getattr(sys, '_home', None) + if _sys_home: + self.library_dirs.append(_sys_home) + + # Use the .lib files for the correct architecture + if self.plat_name == 'win32': + suffix = 'win32' + else: + # win-amd64 + suffix = self.plat_name[4:] + new_lib = os.path.join(sys.exec_prefix, 'PCbuild') + if suffix: + new_lib = os.path.join(new_lib, suffix) + self.library_dirs.append(new_lib) + + # For extensions under Cygwin, Python's library directory must be + # appended to library_dirs + if sys.platform[:6] == 'cygwin': + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): + # building third party extensions + self.library_dirs.append(os.path.join(sys.prefix, "lib", + "python" + get_python_version(), + "config")) + else: + # building python standard extensions + self.library_dirs.append('.') + + # For building extensions with a shared Python library, + # Python's library directory must be appended to library_dirs + # See Issues: #1600860, #4366 + if (sysconfig.get_config_var('Py_ENABLE_SHARED')): + if not sysconfig.python_build: + # building third party extensions + self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) + else: + # building python standard extensions + self.library_dirs.append('.') + + # The argument parsing will result in self.define being a string, but + # it has to be a list of 2-tuples. All the preprocessor symbols + # specified by the 'define' option will be set to '1'. Multiple + # symbols can be separated with commas. + + if self.define: + defines = self.define.split(',') + self.define = [(symbol, '1') for symbol in defines] + + # The option for macros to undefine is also a string from the + # option parsing, but has to be a list. Multiple symbols can also + # be separated with commas here. + if self.undef: + self.undef = self.undef.split(',') + + if self.swig_opts is None: + self.swig_opts = [] + else: + self.swig_opts = self.swig_opts.split(' ') + + # Finally add the user include and library directories if requested + if self.user: + user_include = os.path.join(USER_BASE, "include") + user_lib = os.path.join(USER_BASE, "lib") + if os.path.isdir(user_include): + self.include_dirs.append(user_include) + if os.path.isdir(user_lib): + self.library_dirs.append(user_lib) + self.rpath.append(user_lib) + + if isinstance(self.parallel, str): + try: + self.parallel = int(self.parallel) + except ValueError: + raise DistutilsOptionError("parallel should be an integer") + + def run(self): + from distutils.ccompiler import new_compiler + + # 'self.extensions', as supplied by setup.py, is a list of + # Extension instances. See the documentation for Extension (in + # distutils.extension) for details. + # + # For backwards compatibility with Distutils 0.8.2 and earlier, we + # also allow the 'extensions' list to be a list of tuples: + # (ext_name, build_info) + # where build_info is a dictionary containing everything that + # Extension instances do except the name, with a few things being + # differently named. We convert these 2-tuples to Extension + # instances as needed. + + if not self.extensions: + return + + # If we were asked to build any C/C++ libraries, make sure that the + # directory where we put them is in the library search path for + # linking extensions. + if self.distribution.has_c_libraries(): + build_clib = self.get_finalized_command('build_clib') + self.libraries.extend(build_clib.get_library_names() or []) + self.library_dirs.append(build_clib.build_clib) + + # Setup the CCompiler object that we'll use to do all the + # compiling and linking + self.compiler = new_compiler(compiler=self.compiler, + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) + customize_compiler(self.compiler) + # If we are cross-compiling, init the compiler now (if we are not + # cross-compiling, init would not hurt, but people may rely on + # late initialization of compiler even if they shouldn't...) + if os.name == 'nt' and self.plat_name != get_platform(): + self.compiler.initialize(self.plat_name) + + # And make sure that any compile/link-related options (which might + # come from the command-line or from the setup script) are set in + # that CCompiler object -- that way, they automatically apply to + # all compiling and linking done here. + if self.include_dirs is not None: + self.compiler.set_include_dirs(self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name, value) in self.define: + self.compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro(macro) + if self.libraries is not None: + self.compiler.set_libraries(self.libraries) + if self.library_dirs is not None: + self.compiler.set_library_dirs(self.library_dirs) + if self.rpath is not None: + self.compiler.set_runtime_library_dirs(self.rpath) + if self.link_objects is not None: + self.compiler.set_link_objects(self.link_objects) + + # Now actually compile and link everything. + self.build_extensions() + + def check_extensions_list(self, extensions): + """Ensure that the list of extensions (presumably provided as a + command option 'extensions') is valid, i.e. it is a list of + Extension objects. We also support the old-style list of 2-tuples, + where the tuples are (ext_name, build_info), which are converted to + Extension instances here. + + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ + if not isinstance(extensions, list): + raise DistutilsSetupError( + "'ext_modules' option must be a list of Extension instances") + + for i, ext in enumerate(extensions): + if isinstance(ext, Extension): + continue # OK! (assume type-checking done + # by Extension constructor) + + if not isinstance(ext, tuple) or len(ext) != 2: + raise DistutilsSetupError( + "each element of 'ext_modules' option must be an " + "Extension instance or 2-tuple") + + ext_name, build_info = ext + + log.warn("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s' " + "-- please convert to Extension instance", ext_name) + + if not (isinstance(ext_name, str) and + extension_name_re.match(ext_name)): + raise DistutilsSetupError( + "first element of each tuple in 'ext_modules' " + "must be the extension name (a string)") + + if not isinstance(build_info, dict): + raise DistutilsSetupError( + "second element of each tuple in 'ext_modules' " + "must be a dictionary (build info)") + + # OK, the (ext_name, build_info) dict is type-safe: convert it + # to an Extension instance. + ext = Extension(ext_name, build_info['sources']) + + # Easy stuff: one-to-one mapping from dict elements to + # instance attributes. + for key in ('include_dirs', 'library_dirs', 'libraries', + 'extra_objects', 'extra_compile_args', + 'extra_link_args'): + val = build_info.get(key) + if val is not None: + setattr(ext, key, val) + + # Medium-easy stuff: same syntax/semantics, different names. + ext.runtime_library_dirs = build_info.get('rpath') + if 'def_file' in build_info: + log.warn("'def_file' element of build info dict " + "no longer supported") + + # Non-trivial stuff: 'macros' split into 'define_macros' + # and 'undef_macros'. + macros = build_info.get('macros') + if macros: + ext.define_macros = [] + ext.undef_macros = [] + for macro in macros: + if not (isinstance(macro, tuple) and len(macro) in (1, 2)): + raise DistutilsSetupError( + "'macros' element of build info dict " + "must be 1- or 2-tuple") + if len(macro) == 1: + ext.undef_macros.append(macro[0]) + elif len(macro) == 2: + ext.define_macros.append(macro) + + extensions[i] = ext + + def get_source_files(self): + self.check_extensions_list(self.extensions) + filenames = [] + + # Wouldn't it be neat if we knew the names of header files too... + for ext in self.extensions: + filenames.extend(ext.sources) + return filenames + + def get_outputs(self): + # Sanity check the 'extensions' list -- can't assume this is being + # done in the same run as a 'build_extensions()' call (in fact, we + # can probably assume that it *isn't*!). + self.check_extensions_list(self.extensions) + + # And build the list of output (built) filenames. Note that this + # ignores the 'inplace' flag, and assumes everything goes in the + # "build" tree. + outputs = [] + for ext in self.extensions: + outputs.append(self.get_ext_fullpath(ext.name)) + return outputs + + def build_extensions(self): + # First, sanity-check the 'extensions' list + self.check_extensions_list(self.extensions) + if self.parallel: + self._build_extensions_parallel() + else: + self._build_extensions_serial() + + def _build_extensions_parallel(self): + workers = self.parallel + if self.parallel is True: + workers = os.cpu_count() # may return None + try: + from concurrent.futures import ThreadPoolExecutor + except ImportError: + workers = None + + if workers is None: + self._build_extensions_serial() + return + + with ThreadPoolExecutor(max_workers=workers) as executor: + futures = [executor.submit(self.build_extension, ext) + for ext in self.extensions] + for ext, fut in zip(self.extensions, futures): + with self._filter_build_errors(ext): + fut.result() + + def _build_extensions_serial(self): + for ext in self.extensions: + with self._filter_build_errors(ext): + self.build_extension(ext) + + @contextlib.contextmanager + def _filter_build_errors(self, ext): + try: + yield + except (CCompilerError, DistutilsError, CompileError) as e: + if not ext.optional: + raise + self.warn('building extension "%s" failed: %s' % + (ext.name, e)) + + def build_extension(self, ext): + sources = ext.sources + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'ext_modules' option (extension '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % ext.name) + # sort to make the resulting .so file build reproducible + sources = sorted(sources) + + ext_path = self.get_ext_fullpath(ext.name) + depends = sources + ext.depends + if not (self.force or newer_group(depends, ext_path, 'newer')): + log.debug("skipping '%s' extension (up-to-date)", ext.name) + return + else: + log.info("building '%s' extension", ext.name) + + # First, scan the sources for SWIG definition files (.i), run + # SWIG on 'em to create .c files, and modify the sources list + # accordingly. + sources = self.swig_sources(sources, ext) + + # Next, compile the source code to object files. + + # XXX not honouring 'define_macros' or 'undef_macros' -- the + # CCompiler API needs to change to accommodate this, and I + # want to do one thing at a time! + + # Two possible sources for extra compiler arguments: + # - 'extra_compile_args' in Extension object + # - CFLAGS environment variable (not particularly + # elegant, but people seem to expect it and I + # guess it's useful) + # The environment variable should take precedence, and + # any sensible compiler will give precedence to later + # command line args. Hence we combine them in order: + extra_args = ext.extra_compile_args or [] + + macros = ext.define_macros[:] + for undef in ext.undef_macros: + macros.append((undef,)) + + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends) + + # XXX outdated variable, kept here in case third-part code + # needs it. + self._built_objects = objects[:] + + # Now link the object files together into a "shared object" -- + # of course, first we have to figure out all the other things + # that go into the mix. + if ext.extra_objects: + objects.extend(ext.extra_objects) + extra_args = ext.extra_link_args or [] + + # Detect target language, if not provided + language = ext.language or self.compiler.detect_language(sources) + + self.compiler.link_shared_object( + objects, ext_path, + libraries=self.get_libraries(ext), + library_dirs=ext.library_dirs, + runtime_library_dirs=ext.runtime_library_dirs, + extra_postargs=extra_args, + export_symbols=self.get_export_symbols(ext), + debug=self.debug, + build_temp=self.build_temp, + target_lang=language) + + def swig_sources(self, sources, extension): + """Walk the list of source files in 'sources', looking for SWIG + interface (.i) files. Run SWIG on all that are found, and + return a modified 'sources' list with SWIG source files replaced + by the generated C (or C++) files. + """ + new_sources = [] + swig_sources = [] + swig_targets = {} + + # XXX this drops generated C/C++ files into the source tree, which + # is fine for developers who want to distribute the generated + # source -- but there should be an option to put SWIG output in + # the temp dir. + + if self.swig_cpp: + log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") + + if self.swig_cpp or ('-c++' in self.swig_opts) or \ + ('-c++' in extension.swig_opts): + target_ext = '.cpp' + else: + target_ext = '.c' + + for source in sources: + (base, ext) = os.path.splitext(source) + if ext == ".i": # SWIG interface file + new_sources.append(base + '_wrap' + target_ext) + swig_sources.append(source) + swig_targets[source] = new_sources[-1] + else: + new_sources.append(source) + + if not swig_sources: + return new_sources + + swig = self.swig or self.find_swig() + swig_cmd = [swig, "-python"] + swig_cmd.extend(self.swig_opts) + if self.swig_cpp: + swig_cmd.append("-c++") + + # Do not override commandline arguments + if not self.swig_opts: + for o in extension.swig_opts: + swig_cmd.append(o) + + for source in swig_sources: + target = swig_targets[source] + log.info("swigging %s to %s", source, target) + self.spawn(swig_cmd + ["-o", target, source]) + + return new_sources + + def find_swig(self): + """Return the name of the SWIG executable. On Unix, this is + just "swig" -- it should be in the PATH. Tries a bit harder on + Windows. + """ + if os.name == "posix": + return "swig" + elif os.name == "nt": + # Look for SWIG in its standard installation directory on + # Windows (or so I presume!). If we find it there, great; + # if not, act like Unix and assume it's in the PATH. + for vers in ("1.3", "1.2", "1.1"): + fn = os.path.join("c:\\swig%s" % vers, "swig.exe") + if os.path.isfile(fn): + return fn + else: + return "swig.exe" + else: + raise DistutilsPlatformError( + "I don't know how to find (much less run) SWIG " + "on platform '%s'" % os.name) + + # -- Name generators ----------------------------------------------- + # (extension names, filenames, whatever) + def get_ext_fullpath(self, ext_name): + """Returns the path of the filename for a given extension. + + The file is located in `build_lib` or directly in the package + (inplace option). + """ + fullname = self.get_ext_fullname(ext_name) + modpath = fullname.split('.') + filename = self.get_ext_filename(modpath[-1]) + + if not self.inplace: + # no further work needed + # returning : + # build_dir/package/path/filename + filename = os.path.join(*modpath[:-1]+[filename]) + return os.path.join(self.build_lib, filename) + + # the inplace option requires to find the package directory + # using the build_py command for that + package = '.'.join(modpath[0:-1]) + build_py = self.get_finalized_command('build_py') + package_dir = os.path.abspath(build_py.get_package_dir(package)) + + # returning + # package_dir/filename + return os.path.join(package_dir, filename) + + def get_ext_fullname(self, ext_name): + """Returns the fullname of a given extension name. + + Adds the `package.` prefix""" + if self.package is None: + return ext_name + else: + return self.package + '.' + ext_name + + def get_ext_filename(self, ext_name): + r"""Convert the name of an extension (eg. "foo.bar") into the name + of the file from which it will be loaded (eg. "foo/bar.so", or + "foo\bar.pyd"). + """ + from distutils.sysconfig import get_config_var + ext_path = ext_name.split('.') + ext_suffix = get_config_var('EXT_SUFFIX') + return os.path.join(*ext_path) + ext_suffix + + def get_export_symbols(self, ext): + """Return the list of symbols that a shared extension has to + export. This either uses 'ext.export_symbols' or, if it's not + provided, "PyInit_" + module_name. Only relevant on Windows, where + the .pyd file (DLL) must export the module "PyInit_" function. + """ + suffix = '_' + ext.name.split('.')[-1] + try: + # Unicode module name support as defined in PEP-489 + # https://www.python.org/dev/peps/pep-0489/#export-hook-name + suffix.encode('ascii') + except UnicodeEncodeError: + suffix = 'U' + suffix.encode('punycode').replace(b'-', b'_').decode('ascii') + + initfunc_name = "PyInit" + suffix + if initfunc_name not in ext.export_symbols: + ext.export_symbols.append(initfunc_name) + return ext.export_symbols + + def get_libraries(self, ext): + """Return the list of libraries to link against when building a + shared extension. On most platforms, this is just 'ext.libraries'; + on Windows, we add the Python library (eg. python20.dll). + """ + # The python library is always needed on Windows. For MSVC, this + # is redundant, since the library is mentioned in a pragma in + # pyconfig.h that MSVC groks. The other Windows compilers all seem + # to need it mentioned explicitly, though, so that's what we do. + # Append '_d' to the python import library on debug builds. + if sys.platform == "win32": + from distutils._msvccompiler import MSVCCompiler + if not isinstance(self.compiler, MSVCCompiler): + template = "python%d%d" + if self.debug: + template = template + '_d' + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] + else: + # On Android only the main executable and LD_PRELOADs are considered + # to be RTLD_GLOBAL, all the dependencies of the main executable + # remain RTLD_LOCAL and so the shared libraries must be linked with + # libpython when python is built with a shared python library (issue + # bpo-21536). + # On Cygwin (and if required, other POSIX-like platforms based on + # Windows like MinGW) it is simply necessary that all symbols in + # shared libraries are resolved at link time. + from distutils.sysconfig import get_config_var + link_libpython = False + if get_config_var('Py_ENABLE_SHARED'): + # A native build on an Android device or on Cygwin + if hasattr(sys, 'getandroidapilevel'): + link_libpython = True + elif sys.platform == 'cygwin': + link_libpython = True + elif '_PYTHON_HOST_PLATFORM' in os.environ: + # We are cross-compiling for one of the relevant platforms + if get_config_var('ANDROID_API_LEVEL') != 0: + link_libpython = True + elif get_config_var('MACHDEP') == 'cygwin': + link_libpython = True + + if link_libpython: + ldversion = get_config_var('LDVERSION') + return ext.libraries + ['python' + ldversion] + + return ext.libraries diff --git a/distutils/command/build_py.py b/distutils/command/build_py.py new file mode 100644 index 00000000..cf0ca57c --- /dev/null +++ b/distutils/command/build_py.py @@ -0,0 +1,416 @@ +"""distutils.command.build_py + +Implements the Distutils 'build_py' command.""" + +import os +import importlib.util +import sys +from glob import glob + +from distutils.core import Command +from distutils.errors import * +from distutils.util import convert_path, Mixin2to3 +from distutils import log + +class build_py (Command): + + description = "\"build\" pure Python modules (copy to build directory)" + + user_options = [ + ('build-lib=', 'd', "directory to \"build\" (copy) to"), + ('compile', 'c', "compile .py to .pyc"), + ('no-compile', None, "don't compile .py files [default]"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + ('force', 'f', "forcibly build everything (ignore file timestamps)"), + ] + + boolean_options = ['compile', 'force'] + negative_opt = {'no-compile' : 'compile'} + + def initialize_options(self): + self.build_lib = None + self.py_modules = None + self.package = None + self.package_data = None + self.package_dir = None + self.compile = 0 + self.optimize = 0 + self.force = None + + def finalize_options(self): + self.set_undefined_options('build', + ('build_lib', 'build_lib'), + ('force', 'force')) + + # Get the distribution options that are aliases for build_py + # options -- list of packages and list of modules. + self.packages = self.distribution.packages + self.py_modules = self.distribution.py_modules + self.package_data = self.distribution.package_data + self.package_dir = {} + if self.distribution.package_dir: + for name, path in self.distribution.package_dir.items(): + self.package_dir[name] = convert_path(path) + self.data_files = self.get_data_files() + + # Ick, copied straight from install_lib.py (fancy_getopt needs a + # type system! Hell, *everything* needs a type system!!!) + if not isinstance(self.optimize, int): + try: + self.optimize = int(self.optimize) + assert 0 <= self.optimize <= 2 + except (ValueError, AssertionError): + raise DistutilsOptionError("optimize must be 0, 1, or 2") + + def run(self): + # XXX copy_file by default preserves atime and mtime. IMHO this is + # the right thing to do, but perhaps it should be an option -- in + # particular, a site administrator might want installed files to + # reflect the time of installation rather than the last + # modification time before the installed release. + + # XXX copy_file by default preserves mode, which appears to be the + # wrong thing to do: if a file is read-only in the working + # directory, we want it to be installed read/write so that the next + # installation of the same module distribution can overwrite it + # without problems. (This might be a Unix-specific issue.) Thus + # we turn off 'preserve_mode' when copying to the build directory, + # since the build directory is supposed to be exactly what the + # installation will look like (ie. we preserve mode when + # installing). + + # Two options control which modules will be installed: 'packages' + # and 'py_modules'. The former lets us work with whole packages, not + # specifying individual modules at all; the latter is for + # specifying modules one-at-a-time. + + if self.py_modules: + self.build_modules() + if self.packages: + self.build_packages() + self.build_package_data() + + self.byte_compile(self.get_outputs(include_bytecode=0)) + + def get_data_files(self): + """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" + data = [] + if not self.packages: + return data + for package in self.packages: + # Locate package source directory + src_dir = self.get_package_dir(package) + + # Compute package build directory + build_dir = os.path.join(*([self.build_lib] + package.split('.'))) + + # Length of path to strip from found files + plen = 0 + if src_dir: + plen = len(src_dir)+1 + + # Strip directory from globbed filenames + filenames = [ + file[plen:] for file in self.find_data_files(package, src_dir) + ] + data.append((package, src_dir, build_dir, filenames)) + return data + + def find_data_files(self, package, src_dir): + """Return filenames for package's data files in 'src_dir'""" + globs = (self.package_data.get('', []) + + self.package_data.get(package, [])) + files = [] + for pattern in globs: + # Each pattern has to be converted to a platform-specific path + filelist = glob(os.path.join(src_dir, convert_path(pattern))) + # Files that match more than one pattern are only added once + files.extend([fn for fn in filelist if fn not in files + and os.path.isfile(fn)]) + return files + + def build_package_data(self): + """Copy data files into build directory""" + lastdir = None + for package, src_dir, build_dir, filenames in self.data_files: + for filename in filenames: + target = os.path.join(build_dir, filename) + self.mkpath(os.path.dirname(target)) + self.copy_file(os.path.join(src_dir, filename), target, + preserve_mode=False) + + def get_package_dir(self, package): + """Return the directory, relative to the top of the source + distribution, where package 'package' should be found + (at least according to the 'package_dir' option, if any).""" + path = package.split('.') + + if not self.package_dir: + if path: + return os.path.join(*path) + else: + return '' + else: + tail = [] + while path: + try: + pdir = self.package_dir['.'.join(path)] + except KeyError: + tail.insert(0, path[-1]) + del path[-1] + else: + tail.insert(0, pdir) + return os.path.join(*tail) + else: + # Oops, got all the way through 'path' without finding a + # match in package_dir. If package_dir defines a directory + # for the root (nameless) package, then fallback on it; + # otherwise, we might as well have not consulted + # package_dir at all, as we just use the directory implied + # by 'tail' (which should be the same as the original value + # of 'path' at this point). + pdir = self.package_dir.get('') + if pdir is not None: + tail.insert(0, pdir) + + if tail: + return os.path.join(*tail) + else: + return '' + + def check_package(self, package, package_dir): + # Empty dir name means current directory, which we can probably + # assume exists. Also, os.path.exists and isdir don't know about + # my "empty string means current dir" convention, so we have to + # circumvent them. + if package_dir != "": + if not os.path.exists(package_dir): + raise DistutilsFileError( + "package directory '%s' does not exist" % package_dir) + if not os.path.isdir(package_dir): + raise DistutilsFileError( + "supposed package directory '%s' exists, " + "but is not a directory" % package_dir) + + # Require __init__.py for all but the "root package" + if package: + init_py = os.path.join(package_dir, "__init__.py") + if os.path.isfile(init_py): + return init_py + else: + log.warn(("package init file '%s' not found " + + "(or not a regular file)"), init_py) + + # Either not in a package at all (__init__.py not expected), or + # __init__.py doesn't exist -- so don't return the filename. + return None + + def check_module(self, module, module_file): + if not os.path.isfile(module_file): + log.warn("file %s (for module %s) not found", module_file, module) + return False + else: + return True + + def find_package_modules(self, package, package_dir): + self.check_package(package, package_dir) + module_files = glob(os.path.join(package_dir, "*.py")) + modules = [] + setup_script = os.path.abspath(self.distribution.script_name) + + for f in module_files: + abs_f = os.path.abspath(f) + if abs_f != setup_script: + module = os.path.splitext(os.path.basename(f))[0] + modules.append((package, module, f)) + else: + self.debug_print("excluding %s" % setup_script) + return modules + + def find_modules(self): + """Finds individually-specified Python modules, ie. those listed by + module name in 'self.py_modules'. Returns a list of tuples (package, + module_base, filename): 'package' is a tuple of the path through + package-space to the module; 'module_base' is the bare (no + packages, no dots) module name, and 'filename' is the path to the + ".py" file (relative to the distribution root) that implements the + module. + """ + # Map package names to tuples of useful info about the package: + # (package_dir, checked) + # package_dir - the directory where we'll find source files for + # this package + # checked - true if we have checked that the package directory + # is valid (exists, contains __init__.py, ... ?) + packages = {} + + # List of (package, module, filename) tuples to return + modules = [] + + # We treat modules-in-packages almost the same as toplevel modules, + # just the "package" for a toplevel is empty (either an empty + # string or empty list, depending on context). Differences: + # - don't check for __init__.py in directory for empty package + for module in self.py_modules: + path = module.split('.') + package = '.'.join(path[0:-1]) + module_base = path[-1] + + try: + (package_dir, checked) = packages[package] + except KeyError: + package_dir = self.get_package_dir(package) + checked = 0 + + if not checked: + init_py = self.check_package(package, package_dir) + packages[package] = (package_dir, 1) + if init_py: + modules.append((package, "__init__", init_py)) + + # XXX perhaps we should also check for just .pyc files + # (so greedy closed-source bastards can distribute Python + # modules too) + module_file = os.path.join(package_dir, module_base + ".py") + if not self.check_module(module, module_file): + continue + + modules.append((package, module_base, module_file)) + + return modules + + def find_all_modules(self): + """Compute the list of all modules that will be built, whether + they are specified one-module-at-a-time ('self.py_modules') or + by whole packages ('self.packages'). Return a list of tuples + (package, module, module_file), just like 'find_modules()' and + 'find_package_modules()' do.""" + modules = [] + if self.py_modules: + modules.extend(self.find_modules()) + if self.packages: + for package in self.packages: + package_dir = self.get_package_dir(package) + m = self.find_package_modules(package, package_dir) + modules.extend(m) + return modules + + def get_source_files(self): + return [module[-1] for module in self.find_all_modules()] + + def get_module_outfile(self, build_dir, package, module): + outfile_path = [build_dir] + list(package) + [module + ".py"] + return os.path.join(*outfile_path) + + def get_outputs(self, include_bytecode=1): + modules = self.find_all_modules() + outputs = [] + for (package, module, module_file) in modules: + package = package.split('.') + filename = self.get_module_outfile(self.build_lib, package, module) + outputs.append(filename) + if include_bytecode: + if self.compile: + outputs.append(importlib.util.cache_from_source( + filename, optimization='')) + if self.optimize > 0: + outputs.append(importlib.util.cache_from_source( + filename, optimization=self.optimize)) + + outputs += [ + os.path.join(build_dir, filename) + for package, src_dir, build_dir, filenames in self.data_files + for filename in filenames + ] + + return outputs + + def build_module(self, module, module_file, package): + if isinstance(package, str): + package = package.split('.') + elif not isinstance(package, (list, tuple)): + raise TypeError( + "'package' must be a string (dot-separated), list, or tuple") + + # Now put the module source file into the "build" area -- this is + # easy, we just copy it somewhere under self.build_lib (the build + # directory for Python source). + outfile = self.get_module_outfile(self.build_lib, package, module) + dir = os.path.dirname(outfile) + self.mkpath(dir) + return self.copy_file(module_file, outfile, preserve_mode=0) + + def build_modules(self): + modules = self.find_modules() + for (package, module, module_file) in modules: + # Now "build" the module -- ie. copy the source file to + # self.build_lib (the build directory for Python source). + # (Actually, it gets copied to the directory for this package + # under self.build_lib.) + self.build_module(module, module_file, package) + + def build_packages(self): + for package in self.packages: + # Get list of (package, module, module_file) tuples based on + # scanning the package directory. 'package' is only included + # in the tuple so that 'find_modules()' and + # 'find_package_tuples()' have a consistent interface; it's + # ignored here (apart from a sanity check). Also, 'module' is + # the *unqualified* module name (ie. no dots, no package -- we + # already know its package!), and 'module_file' is the path to + # the .py file, relative to the current directory + # (ie. including 'package_dir'). + package_dir = self.get_package_dir(package) + modules = self.find_package_modules(package, package_dir) + + # Now loop over the modules we found, "building" each one (just + # copy it to self.build_lib). + for (package_, module, module_file) in modules: + assert package == package_ + self.build_module(module, module_file, package) + + def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + + from distutils.util import byte_compile + prefix = self.build_lib + if prefix[-1] != os.sep: + prefix = prefix + os.sep + + # XXX this code is essentially the same as the 'byte_compile() + # method of the "install_lib" command, except for the determination + # of the 'prefix' string. Hmmm. + if self.compile: + byte_compile(files, optimize=0, + force=self.force, prefix=prefix, dry_run=self.dry_run) + if self.optimize > 0: + byte_compile(files, optimize=self.optimize, + force=self.force, prefix=prefix, dry_run=self.dry_run) + +class build_py_2to3(build_py, Mixin2to3): + def run(self): + self.updated_files = [] + + # Base class code + if self.py_modules: + self.build_modules() + if self.packages: + self.build_packages() + self.build_package_data() + + # 2to3 + self.run_2to3(self.updated_files) + + # Remaining base class code + self.byte_compile(self.get_outputs(include_bytecode=0)) + + def build_module(self, module, module_file, package): + res = build_py.build_module(self, module, module_file, package) + if res[1]: + # file was copied + self.updated_files.append(res[0]) + return res diff --git a/distutils/command/build_scripts.py b/distutils/command/build_scripts.py new file mode 100644 index 00000000..ccc70e64 --- /dev/null +++ b/distutils/command/build_scripts.py @@ -0,0 +1,160 @@ +"""distutils.command.build_scripts + +Implements the Distutils 'build_scripts' command.""" + +import os, re +from stat import ST_MODE +from distutils import sysconfig +from distutils.core import Command +from distutils.dep_util import newer +from distutils.util import convert_path, Mixin2to3 +from distutils import log +import tokenize + +# check if Python is called on the first line with this expression +first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$') + +class build_scripts(Command): + + description = "\"build\" scripts (copy and fixup #! line)" + + user_options = [ + ('build-dir=', 'd', "directory to \"build\" (copy) to"), + ('force', 'f', "forcibly build everything (ignore file timestamps"), + ('executable=', 'e', "specify final destination interpreter path"), + ] + + boolean_options = ['force'] + + + def initialize_options(self): + self.build_dir = None + self.scripts = None + self.force = None + self.executable = None + self.outfiles = None + + def finalize_options(self): + self.set_undefined_options('build', + ('build_scripts', 'build_dir'), + ('force', 'force'), + ('executable', 'executable')) + self.scripts = self.distribution.scripts + + def get_source_files(self): + return self.scripts + + def run(self): + if not self.scripts: + return + self.copy_scripts() + + + def copy_scripts(self): + r"""Copy each script listed in 'self.scripts'; if it's marked as a + Python script in the Unix way (first line matches 'first_line_re', + ie. starts with "\#!" and contains "python"), then adjust the first + line to refer to the current Python interpreter as we copy. + """ + self.mkpath(self.build_dir) + outfiles = [] + updated_files = [] + for script in self.scripts: + adjust = False + script = convert_path(script) + outfile = os.path.join(self.build_dir, os.path.basename(script)) + outfiles.append(outfile) + + if not self.force and not newer(script, outfile): + log.debug("not copying %s (up-to-date)", script) + continue + + # Always open the file, but ignore failures in dry-run mode -- + # that way, we'll get accurate feedback if we can read the + # script. + try: + f = open(script, "rb") + except OSError: + if not self.dry_run: + raise + f = None + else: + encoding, lines = tokenize.detect_encoding(f.readline) + f.seek(0) + first_line = f.readline() + if not first_line: + self.warn("%s is an empty file (skipping)" % script) + continue + + match = first_line_re.match(first_line) + if match: + adjust = True + post_interp = match.group(1) or b'' + + if adjust: + log.info("copying and adjusting %s -> %s", script, + self.build_dir) + updated_files.append(outfile) + if not self.dry_run: + if not sysconfig.python_build: + executable = self.executable + else: + executable = os.path.join( + sysconfig.get_config_var("BINDIR"), + "python%s%s" % (sysconfig.get_config_var("VERSION"), + sysconfig.get_config_var("EXE"))) + executable = os.fsencode(executable) + shebang = b"#!" + executable + post_interp + b"\n" + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: + raise ValueError( + "The shebang ({!r}) is not decodable " + "from utf-8".format(shebang)) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + try: + shebang.decode(encoding) + except UnicodeDecodeError: + raise ValueError( + "The shebang ({!r}) is not decodable " + "from the script encoding ({})" + .format(shebang, encoding)) + with open(outfile, "wb") as outf: + outf.write(shebang) + outf.writelines(f.readlines()) + if f: + f.close() + else: + if f: + f.close() + updated_files.append(outfile) + self.copy_file(script, outfile) + + if os.name == 'posix': + for file in outfiles: + if self.dry_run: + log.info("changing mode of %s", file) + else: + oldmode = os.stat(file)[ST_MODE] & 0o7777 + newmode = (oldmode | 0o555) & 0o7777 + if newmode != oldmode: + log.info("changing mode of %s from %o to %o", + file, oldmode, newmode) + os.chmod(file, newmode) + # XXX should we modify self.outfiles? + return outfiles, updated_files + +class build_scripts_2to3(build_scripts, Mixin2to3): + + def copy_scripts(self): + outfiles, updated_files = build_scripts.copy_scripts(self) + if not self.dry_run: + self.run_2to3(updated_files) + return outfiles, updated_files diff --git a/distutils/command/check.py b/distutils/command/check.py new file mode 100644 index 00000000..ada25006 --- /dev/null +++ b/distutils/command/check.py @@ -0,0 +1,148 @@ +"""distutils.command.check + +Implements the Distutils 'check' command. +""" +from distutils.core import Command +from distutils.errors import DistutilsSetupError + +try: + # docutils is installed + from docutils.utils import Reporter + from docutils.parsers.rst import Parser + from docutils import frontend + from docutils import nodes + + class SilentReporter(Reporter): + + def __init__(self, source, report_level, halt_level, stream=None, + debug=0, encoding='ascii', error_handler='replace'): + self.messages = [] + Reporter.__init__(self, source, report_level, halt_level, stream, + debug, encoding, error_handler) + + def system_message(self, level, message, *children, **kwargs): + self.messages.append((level, message, children, kwargs)) + return nodes.system_message(message, level=level, + type=self.levels[level], + *children, **kwargs) + + HAS_DOCUTILS = True +except Exception: + # Catch all exceptions because exceptions besides ImportError probably + # indicate that docutils is not ported to Py3k. + HAS_DOCUTILS = False + +class check(Command): + """This command checks the meta-data of the package. + """ + description = ("perform some checks on the package") + user_options = [('metadata', 'm', 'Verify meta-data'), + ('restructuredtext', 'r', + ('Checks if long string meta-data syntax ' + 'are reStructuredText-compliant')), + ('strict', 's', + 'Will exit with an error if a check fails')] + + boolean_options = ['metadata', 'restructuredtext', 'strict'] + + def initialize_options(self): + """Sets default values for options.""" + self.restructuredtext = 0 + self.metadata = 1 + self.strict = 0 + self._warnings = 0 + + def finalize_options(self): + pass + + def warn(self, msg): + """Counts the number of warnings that occurs.""" + self._warnings += 1 + return Command.warn(self, msg) + + def run(self): + """Runs the command.""" + # perform the various tests + if self.metadata: + self.check_metadata() + if self.restructuredtext: + if HAS_DOCUTILS: + self.check_restructuredtext() + elif self.strict: + raise DistutilsSetupError('The docutils package is needed.') + + # let's raise an error in strict mode, if we have at least + # one warning + if self.strict and self._warnings > 0: + raise DistutilsSetupError('Please correct your package.') + + def check_metadata(self): + """Ensures that all required elements of meta-data are supplied. + + Required fields: + name, version, URL + + Recommended fields: + (author and author_email) or (maintainer and maintainer_email)) + + Warns if any are missing. + """ + metadata = self.distribution.metadata + + missing = [] + for attr in ('name', 'version', 'url'): + if not (hasattr(metadata, attr) and getattr(metadata, attr)): + missing.append(attr) + + if missing: + self.warn("missing required meta-data: %s" % ', '.join(missing)) + if metadata.author: + if not metadata.author_email: + self.warn("missing meta-data: if 'author' supplied, " + + "'author_email' should be supplied too") + elif metadata.maintainer: + if not metadata.maintainer_email: + self.warn("missing meta-data: if 'maintainer' supplied, " + + "'maintainer_email' should be supplied too") + else: + self.warn("missing meta-data: either (author and author_email) " + + "or (maintainer and maintainer_email) " + + "should be supplied") + + def check_restructuredtext(self): + """Checks if the long string fields are reST-compliant.""" + data = self.distribution.get_long_description() + for warning in self._check_rst_data(data): + line = warning[-1].get('line') + if line is None: + warning = warning[1] + else: + warning = '%s (line %s)' % (warning[1], line) + self.warn(warning) + + def _check_rst_data(self, data): + """Returns warnings when the provided data doesn't compile.""" + # the include and csv_table directives need this to be a path + source_path = self.distribution.script_name or 'setup.py' + parser = Parser() + settings = frontend.OptionParser(components=(Parser,)).get_default_values() + settings.tab_width = 4 + settings.pep_references = None + settings.rfc_references = None + reporter = SilentReporter(source_path, + settings.report_level, + settings.halt_level, + stream=settings.warning_stream, + debug=settings.debug, + encoding=settings.error_encoding, + error_handler=settings.error_encoding_error_handler) + + document = nodes.document(settings, reporter, source=source_path) + document.note_source(source_path, -1) + try: + parser.parse(data, document) + except AttributeError as e: + reporter.messages.append( + (-1, 'Could not finish the parsing: %s.' % e, '', {})) + + return reporter.messages diff --git a/distutils/command/clean.py b/distutils/command/clean.py new file mode 100644 index 00000000..0cb27016 --- /dev/null +++ b/distutils/command/clean.py @@ -0,0 +1,76 @@ +"""distutils.command.clean + +Implements the Distutils 'clean' command.""" + +# contributed by Bastian Kleineidam , added 2000-03-18 + +import os +from distutils.core import Command +from distutils.dir_util import remove_tree +from distutils import log + +class clean(Command): + + description = "clean up temporary files from 'build' command" + user_options = [ + ('build-base=', 'b', + "base build directory (default: 'build.build-base')"), + ('build-lib=', None, + "build directory for all modules (default: 'build.build-lib')"), + ('build-temp=', 't', + "temporary build directory (default: 'build.build-temp')"), + ('build-scripts=', None, + "build directory for scripts (default: 'build.build-scripts')"), + ('bdist-base=', None, + "temporary directory for built distributions"), + ('all', 'a', + "remove all build output, not just temporary by-products") + ] + + boolean_options = ['all'] + + def initialize_options(self): + self.build_base = None + self.build_lib = None + self.build_temp = None + self.build_scripts = None + self.bdist_base = None + self.all = None + + def finalize_options(self): + self.set_undefined_options('build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib'), + ('build_scripts', 'build_scripts'), + ('build_temp', 'build_temp')) + self.set_undefined_options('bdist', + ('bdist_base', 'bdist_base')) + + def run(self): + # remove the build/temp. directory (unless it's already + # gone) + if os.path.exists(self.build_temp): + remove_tree(self.build_temp, dry_run=self.dry_run) + else: + log.debug("'%s' does not exist -- can't clean it", + self.build_temp) + + if self.all: + # remove build directories + for directory in (self.build_lib, + self.bdist_base, + self.build_scripts): + if os.path.exists(directory): + remove_tree(directory, dry_run=self.dry_run) + else: + log.warn("'%s' does not exist -- can't clean it", + directory) + + # just for the heck of it, try to remove the base build directory: + # we might have emptied it right now, but if not we don't care + if not self.dry_run: + try: + os.rmdir(self.build_base) + log.info("removing '%s'", self.build_base) + except OSError: + pass diff --git a/distutils/command/command_template b/distutils/command/command_template new file mode 100644 index 00000000..6106819d --- /dev/null +++ b/distutils/command/command_template @@ -0,0 +1,33 @@ +"""distutils.command.x + +Implements the Distutils 'x' command. +""" + +# created 2000/mm/dd, John Doe + +__revision__ = "$Id$" + +from distutils.core import Command + + +class x(Command): + + # Brief (40-50 characters) description of the command + description = "" + + # List of option tuples: long name, short name (None if no short + # name), and help string. + user_options = [('', '', + ""), + ] + + def initialize_options(self): + self. = None + self. = None + self. = None + + def finalize_options(self): + if self.x is None: + self.x = + + def run(self): diff --git a/distutils/command/config.py b/distutils/command/config.py new file mode 100644 index 00000000..aeda408e --- /dev/null +++ b/distutils/command/config.py @@ -0,0 +1,344 @@ +"""distutils.command.config + +Implements the Distutils 'config' command, a (mostly) empty command class +that exists mainly to be sub-classed by specific module distributions and +applications. The idea is that while every "config" command is different, +at least they're all named the same, and users always see "config" in the +list of standard commands. Also, this is a good place to put common +configure-like tasks: "try to compile this C code", or "figure out where +this header file lives". +""" + +import os, re + +from distutils.core import Command +from distutils.errors import DistutilsExecError +from distutils.sysconfig import customize_compiler +from distutils import log + +LANG_EXT = {"c": ".c", "c++": ".cxx"} + +class config(Command): + + description = "prepare to build" + + user_options = [ + ('compiler=', None, + "specify the compiler type"), + ('cc=', None, + "specify the compiler executable"), + ('include-dirs=', 'I', + "list of directories to search for header files"), + ('define=', 'D', + "C preprocessor macros to define"), + ('undef=', 'U', + "C preprocessor macros to undefine"), + ('libraries=', 'l', + "external C libraries to link with"), + ('library-dirs=', 'L', + "directories to search for external C libraries"), + + ('noisy', None, + "show every action (compile, link, run, ...) taken"), + ('dump-source', None, + "dump generated source files before attempting to compile them"), + ] + + + # The three standard command methods: since the "config" command + # does nothing by default, these are empty. + + def initialize_options(self): + self.compiler = None + self.cc = None + self.include_dirs = None + self.libraries = None + self.library_dirs = None + + # maximal output for now + self.noisy = 1 + self.dump_source = 1 + + # list of temporary files generated along-the-way that we have + # to clean at some point + self.temp_files = [] + + def finalize_options(self): + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + elif isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + if self.libraries is None: + self.libraries = [] + elif isinstance(self.libraries, str): + self.libraries = [self.libraries] + + if self.library_dirs is None: + self.library_dirs = [] + elif isinstance(self.library_dirs, str): + self.library_dirs = self.library_dirs.split(os.pathsep) + + def run(self): + pass + + # Utility methods for actual "config" commands. The interfaces are + # loosely based on Autoconf macros of similar names. Sub-classes + # may use these freely. + + def _check_compiler(self): + """Check that 'self.compiler' really is a CCompiler object; + if not, make it one. + """ + # We do this late, and only on-demand, because this is an expensive + # import. + from distutils.ccompiler import CCompiler, new_compiler + if not isinstance(self.compiler, CCompiler): + self.compiler = new_compiler(compiler=self.compiler, + dry_run=self.dry_run, force=1) + customize_compiler(self.compiler) + if self.include_dirs: + self.compiler.set_include_dirs(self.include_dirs) + if self.libraries: + self.compiler.set_libraries(self.libraries) + if self.library_dirs: + self.compiler.set_library_dirs(self.library_dirs) + + def _gen_temp_sourcefile(self, body, headers, lang): + filename = "_configtest" + LANG_EXT[lang] + with open(filename, "w") as file: + if headers: + for header in headers: + file.write("#include <%s>\n" % header) + file.write("\n") + file.write(body) + if body[-1] != "\n": + file.write("\n") + return filename + + def _preprocess(self, body, headers, include_dirs, lang): + src = self._gen_temp_sourcefile(body, headers, lang) + out = "_configtest.i" + self.temp_files.extend([src, out]) + self.compiler.preprocess(src, out, include_dirs=include_dirs) + return (src, out) + + def _compile(self, body, headers, include_dirs, lang): + src = self._gen_temp_sourcefile(body, headers, lang) + if self.dump_source: + dump_file(src, "compiling '%s':" % src) + (obj,) = self.compiler.object_filenames([src]) + self.temp_files.extend([src, obj]) + self.compiler.compile([src], include_dirs=include_dirs) + return (src, obj) + + def _link(self, body, headers, include_dirs, libraries, library_dirs, + lang): + (src, obj) = self._compile(body, headers, include_dirs, lang) + prog = os.path.splitext(os.path.basename(src))[0] + self.compiler.link_executable([obj], prog, + libraries=libraries, + library_dirs=library_dirs, + target_lang=lang) + + if self.compiler.exe_extension is not None: + prog = prog + self.compiler.exe_extension + self.temp_files.append(prog) + + return (src, obj, prog) + + def _clean(self, *filenames): + if not filenames: + filenames = self.temp_files + self.temp_files = [] + log.info("removing: %s", ' '.join(filenames)) + for filename in filenames: + try: + os.remove(filename) + except OSError: + pass + + + # XXX these ignore the dry-run flag: what to do, what to do? even if + # you want a dry-run build, you still need some sort of configuration + # info. My inclination is to make it up to the real config command to + # consult 'dry_run', and assume a default (minimal) configuration if + # true. The problem with trying to do it here is that you'd have to + # return either true or false from all the 'try' methods, neither of + # which is correct. + + # XXX need access to the header search path and maybe default macros. + + def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): + """Construct a source file from 'body' (a string containing lines + of C/C++ code) and 'headers' (a list of header files to include) + and run it through the preprocessor. Return true if the + preprocessor succeeded, false if there were any errors. + ('body' probably isn't of much use, but what the heck.) + """ + from distutils.ccompiler import CompileError + self._check_compiler() + ok = True + try: + self._preprocess(body, headers, include_dirs, lang) + except CompileError: + ok = False + + self._clean() + return ok + + def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, + lang="c"): + """Construct a source file (just like 'try_cpp()'), run it through + the preprocessor, and return true if any line of the output matches + 'pattern'. 'pattern' should either be a compiled regex object or a + string containing a regex. If both 'body' and 'headers' are None, + preprocesses an empty file -- which can be useful to determine the + symbols the preprocessor and compiler set by default. + """ + self._check_compiler() + src, out = self._preprocess(body, headers, include_dirs, lang) + + if isinstance(pattern, str): + pattern = re.compile(pattern) + + with open(out) as file: + match = False + while True: + line = file.readline() + if line == '': + break + if pattern.search(line): + match = True + break + + self._clean() + return match + + def try_compile(self, body, headers=None, include_dirs=None, lang="c"): + """Try to compile a source file built from 'body' and 'headers'. + Return true on success, false otherwise. + """ + from distutils.ccompiler import CompileError + self._check_compiler() + try: + self._compile(body, headers, include_dirs, lang) + ok = True + except CompileError: + ok = False + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + def try_link(self, body, headers=None, include_dirs=None, libraries=None, + library_dirs=None, lang="c"): + """Try to compile and link a source file, built from 'body' and + 'headers', to executable form. Return true on success, false + otherwise. + """ + from distutils.ccompiler import CompileError, LinkError + self._check_compiler() + try: + self._link(body, headers, include_dirs, + libraries, library_dirs, lang) + ok = True + except (CompileError, LinkError): + ok = False + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + def try_run(self, body, headers=None, include_dirs=None, libraries=None, + library_dirs=None, lang="c"): + """Try to compile, link to an executable, and run a program + built from 'body' and 'headers'. Return true on success, false + otherwise. + """ + from distutils.ccompiler import CompileError, LinkError + self._check_compiler() + try: + src, obj, exe = self._link(body, headers, include_dirs, + libraries, library_dirs, lang) + self.spawn([exe]) + ok = True + except (CompileError, LinkError, DistutilsExecError): + ok = False + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + + # -- High-level methods -------------------------------------------- + # (these are the ones that are actually likely to be useful + # when implementing a real-world config command!) + + def check_func(self, func, headers=None, include_dirs=None, + libraries=None, library_dirs=None, decl=0, call=0): + """Determine if function 'func' is available by constructing a + source file that refers to 'func', and compiles and links it. + If everything succeeds, returns true; otherwise returns false. + + The constructed source file starts out by including the header + files listed in 'headers'. If 'decl' is true, it then declares + 'func' (as "int func()"); you probably shouldn't supply 'headers' + and set 'decl' true in the same call, or you might get errors about + a conflicting declarations for 'func'. Finally, the constructed + 'main()' function either references 'func' or (if 'call' is true) + calls it. 'libraries' and 'library_dirs' are used when + linking. + """ + self._check_compiler() + body = [] + if decl: + body.append("int %s ();" % func) + body.append("int main () {") + if call: + body.append(" %s();" % func) + else: + body.append(" %s;" % func) + body.append("}") + body = "\n".join(body) + "\n" + + return self.try_link(body, headers, include_dirs, + libraries, library_dirs) + + def check_lib(self, library, library_dirs=None, headers=None, + include_dirs=None, other_libraries=[]): + """Determine if 'library' is available to be linked against, + without actually checking that any particular symbols are provided + by it. 'headers' will be used in constructing the source file to + be compiled, but the only effect of this is to check if all the + header files listed are available. Any libraries listed in + 'other_libraries' will be included in the link, in case 'library' + has symbols that depend on other libraries. + """ + self._check_compiler() + return self.try_link("int main (void) { }", headers, include_dirs, + [library] + other_libraries, library_dirs) + + def check_header(self, header, include_dirs=None, library_dirs=None, + lang="c"): + """Determine if the system header file named by 'header_file' + exists and can be found by the preprocessor; return true if so, + false otherwise. + """ + return self.try_cpp(body="/* No body */", headers=[header], + include_dirs=include_dirs) + +def dump_file(filename, head=None): + """Dumps a file content into log.info. + + If head is not None, will be dumped before the file content. + """ + if head is None: + log.info('%s', filename) + else: + log.info(head) + file = open(filename) + try: + log.info(file.read()) + finally: + file.close() diff --git a/distutils/command/install.py b/distutils/command/install.py new file mode 100644 index 00000000..aaa300ef --- /dev/null +++ b/distutils/command/install.py @@ -0,0 +1,657 @@ +"""distutils.command.install + +Implements the Distutils 'install' command.""" + +import sys +import os + +from distutils import log +from distutils.core import Command +from distutils.debug import DEBUG +from distutils.sysconfig import get_config_vars +from distutils.errors import DistutilsPlatformError +from distutils.file_util import write_file +from distutils.util import convert_path, subst_vars, change_root +from distutils.util import get_platform +from distutils.errors import DistutilsOptionError + +from site import USER_BASE +from site import USER_SITE +HAS_USER_SITE = True + +WINDOWS_SCHEME = { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', +} + +INSTALL_SCHEMES = { + 'unix_prefix': { + 'purelib': '$base/lib/python$py_version_short/site-packages', + 'platlib': '$platbase/$platlibdir/python$py_version_short/site-packages', + 'headers': '$base/include/python$py_version_short$abiflags/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'unix_home': { + 'purelib': '$base/lib/python', + 'platlib': '$base/$platlibdir/python', + 'headers': '$base/include/python/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'nt': WINDOWS_SCHEME, + } + +# user site schemes +if HAS_USER_SITE: + INSTALL_SCHEMES['nt_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', + 'scripts': '$userbase/Python$py_version_nodot/Scripts', + 'data' : '$userbase', + } + + INSTALL_SCHEMES['unix_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': + '$userbase/include/python$py_version_short$abiflags/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + } + +# The keys to an installation scheme; if any new types of files are to be +# installed, be sure to add an entry to every installation scheme above, +# and to SCHEME_KEYS here. +SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') + + +class install(Command): + + description = "install everything from build directory" + + user_options = [ + # Select installation scheme and set base director(y|ies) + ('prefix=', None, + "installation prefix"), + ('exec-prefix=', None, + "(Unix only) prefix for platform-specific files"), + ('home=', None, + "(Unix only) home directory to install under"), + + # Or, just set the base director(y|ies) + ('install-base=', None, + "base installation directory (instead of --prefix or --home)"), + ('install-platbase=', None, + "base installation directory for platform-specific files " + + "(instead of --exec-prefix or --home)"), + ('root=', None, + "install everything relative to this alternate root directory"), + + # Or, explicitly set the installation scheme + ('install-purelib=', None, + "installation directory for pure Python module distributions"), + ('install-platlib=', None, + "installation directory for non-pure module distributions"), + ('install-lib=', None, + "installation directory for all module distributions " + + "(overrides --install-purelib and --install-platlib)"), + + ('install-headers=', None, + "installation directory for C/C++ headers"), + ('install-scripts=', None, + "installation directory for Python scripts"), + ('install-data=', None, + "installation directory for data files"), + + # Byte-compilation options -- see install_lib.py for details, as + # these are duplicated from there (but only install_lib does + # anything with them). + ('compile', 'c', "compile .py to .pyc [default]"), + ('no-compile', None, "don't compile .py files"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + + # Miscellaneous control options + ('force', 'f', + "force installation (overwrite any existing files)"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + + # Where to install documentation (eventually!) + #('doc-format=', None, "format of documentation to generate"), + #('install-man=', None, "directory for Unix man pages"), + #('install-html=', None, "directory for HTML documentation"), + #('install-info=', None, "directory for GNU info files"), + + ('record=', None, + "filename in which to record list of installed files"), + ] + + boolean_options = ['compile', 'force', 'skip-build'] + + if HAS_USER_SITE: + user_options.append(('user', None, + "install in user site-package '%s'" % USER_SITE)) + boolean_options.append('user') + + negative_opt = {'no-compile' : 'compile'} + + + def initialize_options(self): + """Initializes options.""" + # High-level options: these select both an installation base + # and scheme. + self.prefix = None + self.exec_prefix = None + self.home = None + self.user = 0 + + # These select only the installation base; it's up to the user to + # specify the installation scheme (currently, that means supplying + # the --install-{platlib,purelib,scripts,data} options). + self.install_base = None + self.install_platbase = None + self.root = None + + # These options are the actual installation directories; if not + # supplied by the user, they are filled in using the installation + # scheme implied by prefix/exec-prefix/home and the contents of + # that installation scheme. + self.install_purelib = None # for pure module distributions + self.install_platlib = None # non-pure (dists w/ extensions) + self.install_headers = None # for C/C++ headers + self.install_lib = None # set to either purelib or platlib + self.install_scripts = None + self.install_data = None + self.install_userbase = USER_BASE + self.install_usersite = USER_SITE + + self.compile = None + self.optimize = None + + # Deprecated + # These two are for putting non-packagized distributions into their + # own directory and creating a .pth file if it makes sense. + # 'extra_path' comes from the setup file; 'install_path_file' can + # be turned off if it makes no sense to install a .pth file. (But + # better to install it uselessly than to guess wrong and not + # install it when it's necessary and would be used!) Currently, + # 'install_path_file' is always true unless some outsider meddles + # with it. + self.extra_path = None + self.install_path_file = 1 + + # 'force' forces installation, even if target files are not + # out-of-date. 'skip_build' skips running the "build" command, + # handy if you know it's not necessary. 'warn_dir' (which is *not* + # a user option, it's just there so the bdist_* commands can turn + # it off) determines whether we warn about installing to a + # directory not in sys.path. + self.force = 0 + self.skip_build = 0 + self.warn_dir = 1 + + # These are only here as a conduit from the 'build' command to the + # 'install_*' commands that do the real work. ('build_base' isn't + # actually used anywhere, but it might be useful in future.) They + # are not user options, because if the user told the install + # command where the build directory is, that wouldn't affect the + # build command. + self.build_base = None + self.build_lib = None + + # Not defined yet because we don't know anything about + # documentation yet. + #self.install_man = None + #self.install_html = None + #self.install_info = None + + self.record = None + + + # -- Option finalizing methods ------------------------------------- + # (This is rather more involved than for most commands, + # because this is where the policy for installing third- + # party Python modules on various platforms given a wide + # array of user input is decided. Yes, it's quite complex!) + + def finalize_options(self): + """Finalizes options.""" + # This method (and its helpers, like 'finalize_unix()', + # 'finalize_other()', and 'select_scheme()') is where the default + # installation directories for modules, extension modules, and + # anything else we care to install from a Python module + # distribution. Thus, this code makes a pretty important policy + # statement about how third-party stuff is added to a Python + # installation! Note that the actual work of installation is done + # by the relatively simple 'install_*' commands; they just take + # their orders from the installation directory options determined + # here. + + # Check for errors/inconsistencies in the options; first, stuff + # that's wrong on any platform. + + if ((self.prefix or self.exec_prefix or self.home) and + (self.install_base or self.install_platbase)): + raise DistutilsOptionError( + "must supply either prefix/exec-prefix/home or " + + "install-base/install-platbase -- not both") + + if self.home and (self.prefix or self.exec_prefix): + raise DistutilsOptionError( + "must supply either home or prefix/exec-prefix -- not both") + + if self.user and (self.prefix or self.exec_prefix or self.home or + self.install_base or self.install_platbase): + raise DistutilsOptionError("can't combine user with prefix, " + "exec_prefix/home, or install_(plat)base") + + # Next, stuff that's wrong (or dubious) only on certain platforms. + if os.name != "posix": + if self.exec_prefix: + self.warn("exec-prefix option ignored on this platform") + self.exec_prefix = None + + # Now the interesting logic -- so interesting that we farm it out + # to other methods. The goal of these methods is to set the final + # values for the install_{lib,scripts,data,...} options, using as + # input a heady brew of prefix, exec_prefix, home, install_base, + # install_platbase, user-supplied versions of + # install_{purelib,platlib,lib,scripts,data,...}, and the + # INSTALL_SCHEME dictionary above. Phew! + + self.dump_dirs("pre-finalize_{unix,other}") + + if os.name == 'posix': + self.finalize_unix() + else: + self.finalize_other() + + self.dump_dirs("post-finalize_{unix,other}()") + + # Expand configuration variables, tilde, etc. in self.install_base + # and self.install_platbase -- that way, we can use $base or + # $platbase in the other installation directories and not worry + # about needing recursive variable expansion (shudder). + + py_version = sys.version.split()[0] + (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') + try: + abiflags = sys.abiflags + except AttributeError: + # sys.abiflags may not be defined on all platforms. + abiflags = '' + self.config_vars = {'dist_name': self.distribution.get_name(), + 'dist_version': self.distribution.get_version(), + 'dist_fullname': self.distribution.get_fullname(), + 'py_version': py_version, + 'py_version_short': '%d.%d' % sys.version_info[:2], + 'py_version_nodot': '%d%d' % sys.version_info[:2], + 'sys_prefix': prefix, + 'prefix': prefix, + 'sys_exec_prefix': exec_prefix, + 'exec_prefix': exec_prefix, + 'abiflags': abiflags, + 'platlibdir': sys.platlibdir, + } + + if HAS_USER_SITE: + self.config_vars['userbase'] = self.install_userbase + self.config_vars['usersite'] = self.install_usersite + + self.expand_basedirs() + + self.dump_dirs("post-expand_basedirs()") + + # Now define config vars for the base directories so we can expand + # everything else. + self.config_vars['base'] = self.install_base + self.config_vars['platbase'] = self.install_platbase + + if DEBUG: + from pprint import pprint + print("config vars:") + pprint(self.config_vars) + + # Expand "~" and configuration variables in the installation + # directories. + self.expand_dirs() + + self.dump_dirs("post-expand_dirs()") + + # Create directories in the home dir: + if self.user: + self.create_home_path() + + # Pick the actual directory to install all modules to: either + # install_purelib or install_platlib, depending on whether this + # module distribution is pure or not. Of course, if the user + # already specified install_lib, use their selection. + if self.install_lib is None: + if self.distribution.ext_modules: # has extensions: non-pure + self.install_lib = self.install_platlib + else: + self.install_lib = self.install_purelib + + + # Convert directories from Unix /-separated syntax to the local + # convention. + self.convert_paths('lib', 'purelib', 'platlib', + 'scripts', 'data', 'headers', + 'userbase', 'usersite') + + # Deprecated + # Well, we're not actually fully completely finalized yet: we still + # have to deal with 'extra_path', which is the hack for allowing + # non-packagized module distributions (hello, Numerical Python!) to + # get their own directories. + self.handle_extra_path() + self.install_libbase = self.install_lib # needed for .pth file + self.install_lib = os.path.join(self.install_lib, self.extra_dirs) + + # If a new root directory was supplied, make all the installation + # dirs relative to it. + if self.root is not None: + self.change_roots('libbase', 'lib', 'purelib', 'platlib', + 'scripts', 'data', 'headers') + + self.dump_dirs("after prepending root") + + # Find out the build directories, ie. where to install from. + self.set_undefined_options('build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib')) + + # Punt on doc directories for now -- after all, we're punting on + # documentation completely! + + def dump_dirs(self, msg): + """Dumps the list of user options.""" + if not DEBUG: + return + from distutils.fancy_getopt import longopt_xlate + log.debug(msg + ":") + for opt in self.user_options: + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + if opt_name in self.negative_opt: + opt_name = self.negative_opt[opt_name] + opt_name = opt_name.translate(longopt_xlate) + val = not getattr(self, opt_name) + else: + opt_name = opt_name.translate(longopt_xlate) + val = getattr(self, opt_name) + log.debug(" %s: %s", opt_name, val) + + def finalize_unix(self): + """Finalizes options for posix platforms.""" + if self.install_base is not None or self.install_platbase is not None: + if ((self.install_lib is None and + self.install_purelib is None and + self.install_platlib is None) or + self.install_headers is None or + self.install_scripts is None or + self.install_data is None): + raise DistutilsOptionError( + "install-base or install-platbase supplied, but " + "installation scheme is incomplete") + return + + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError( + "User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme("unix_user") + elif self.home is not None: + self.install_base = self.install_platbase = self.home + self.select_scheme("unix_home") + else: + if self.prefix is None: + if self.exec_prefix is not None: + raise DistutilsOptionError( + "must not supply exec-prefix without prefix") + + self.prefix = os.path.normpath(sys.prefix) + self.exec_prefix = os.path.normpath(sys.exec_prefix) + + else: + if self.exec_prefix is None: + self.exec_prefix = self.prefix + + self.install_base = self.prefix + self.install_platbase = self.exec_prefix + self.select_scheme("unix_prefix") + + def finalize_other(self): + """Finalizes options for non-posix platforms""" + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError( + "User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme(os.name + "_user") + elif self.home is not None: + self.install_base = self.install_platbase = self.home + self.select_scheme("unix_home") + else: + if self.prefix is None: + self.prefix = os.path.normpath(sys.prefix) + + self.install_base = self.install_platbase = self.prefix + try: + self.select_scheme(os.name) + except KeyError: + raise DistutilsPlatformError( + "I don't know how to install stuff on '%s'" % os.name) + + def select_scheme(self, name): + """Sets the install directories by applying the install schemes.""" + # it's the caller's problem if they supply a bad name! + scheme = INSTALL_SCHEMES[name] + for key in SCHEME_KEYS: + attrname = 'install_' + key + if getattr(self, attrname) is None: + setattr(self, attrname, scheme[key]) + + def _expand_attrs(self, attrs): + for attr in attrs: + val = getattr(self, attr) + if val is not None: + if os.name == 'posix' or os.name == 'nt': + val = os.path.expanduser(val) + val = subst_vars(val, self.config_vars) + setattr(self, attr, val) + + def expand_basedirs(self): + """Calls `os.path.expanduser` on install_base, install_platbase and + root.""" + self._expand_attrs(['install_base', 'install_platbase', 'root']) + + def expand_dirs(self): + """Calls `os.path.expanduser` on install dirs.""" + self._expand_attrs(['install_purelib', 'install_platlib', + 'install_lib', 'install_headers', + 'install_scripts', 'install_data',]) + + def convert_paths(self, *names): + """Call `convert_path` over `names`.""" + for name in names: + attr = "install_" + name + setattr(self, attr, convert_path(getattr(self, attr))) + + def handle_extra_path(self): + """Set `path_file` and `extra_dirs` using `extra_path`.""" + if self.extra_path is None: + self.extra_path = self.distribution.extra_path + + if self.extra_path is not None: + log.warn( + "Distribution option extra_path is deprecated. " + "See issue27919 for details." + ) + if isinstance(self.extra_path, str): + self.extra_path = self.extra_path.split(',') + + if len(self.extra_path) == 1: + path_file = extra_dirs = self.extra_path[0] + elif len(self.extra_path) == 2: + path_file, extra_dirs = self.extra_path + else: + raise DistutilsOptionError( + "'extra_path' option must be a list, tuple, or " + "comma-separated string with 1 or 2 elements") + + # convert to local form in case Unix notation used (as it + # should be in setup scripts) + extra_dirs = convert_path(extra_dirs) + else: + path_file = None + extra_dirs = '' + + # XXX should we warn if path_file and not extra_dirs? (in which + # case the path file would be harmless but pointless) + self.path_file = path_file + self.extra_dirs = extra_dirs + + def change_roots(self, *names): + """Change the install directories pointed by name using root.""" + for name in names: + attr = "install_" + name + setattr(self, attr, change_root(self.root, getattr(self, attr))) + + def create_home_path(self): + """Create directories under ~.""" + if not self.user: + return + home = convert_path(os.path.expanduser("~")) + for name, path in self.config_vars.items(): + if path.startswith(home) and not os.path.isdir(path): + self.debug_print("os.makedirs('%s', 0o700)" % path) + os.makedirs(path, 0o700) + + # -- Command execution methods ------------------------------------- + + def run(self): + """Runs the command.""" + # Obviously have to build before we can install + if not self.skip_build: + self.run_command('build') + # If we built for any other platform, we can't install. + build_plat = self.distribution.get_command_obj('build').plat_name + # check warn_dir - it is a clue that the 'install' is happening + # internally, and not to sys.path, so we don't check the platform + # matches what we are running. + if self.warn_dir and build_plat != get_platform(): + raise DistutilsPlatformError("Can't install when " + "cross-compiling") + + # Run all sub-commands (at least those that need to be run) + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + if self.path_file: + self.create_path_file() + + # write list of installed files, if requested. + if self.record: + outputs = self.get_outputs() + if self.root: # strip any package prefix + root_len = len(self.root) + for counter in range(len(outputs)): + outputs[counter] = outputs[counter][root_len:] + self.execute(write_file, + (self.record, outputs), + "writing list of installed files to '%s'" % + self.record) + + sys_path = map(os.path.normpath, sys.path) + sys_path = map(os.path.normcase, sys_path) + install_lib = os.path.normcase(os.path.normpath(self.install_lib)) + if (self.warn_dir and + not (self.path_file and self.install_path_file) and + install_lib not in sys_path): + log.debug(("modules installed to '%s', which is not in " + "Python's module search path (sys.path) -- " + "you'll have to change the search path yourself"), + self.install_lib) + + def create_path_file(self): + """Creates the .pth file""" + filename = os.path.join(self.install_libbase, + self.path_file + ".pth") + if self.install_path_file: + self.execute(write_file, + (filename, [self.extra_dirs]), + "creating %s" % filename) + else: + self.warn("path file '%s' not created" % filename) + + + # -- Reporting methods --------------------------------------------- + + def get_outputs(self): + """Assembles the outputs of all the sub-commands.""" + outputs = [] + for cmd_name in self.get_sub_commands(): + cmd = self.get_finalized_command(cmd_name) + # Add the contents of cmd.get_outputs(), ensuring + # that outputs doesn't contain duplicate entries + for filename in cmd.get_outputs(): + if filename not in outputs: + outputs.append(filename) + + if self.path_file and self.install_path_file: + outputs.append(os.path.join(self.install_libbase, + self.path_file + ".pth")) + + return outputs + + def get_inputs(self): + """Returns the inputs of all the sub-commands""" + # XXX gee, this looks familiar ;-( + inputs = [] + for cmd_name in self.get_sub_commands(): + cmd = self.get_finalized_command(cmd_name) + inputs.extend(cmd.get_inputs()) + + return inputs + + # -- Predicates for sub-command list ------------------------------- + + def has_lib(self): + """Returns true if the current distribution has any Python + modules to install.""" + return (self.distribution.has_pure_modules() or + self.distribution.has_ext_modules()) + + def has_headers(self): + """Returns true if the current distribution has any headers to + install.""" + return self.distribution.has_headers() + + def has_scripts(self): + """Returns true if the current distribution has any scripts to. + install.""" + return self.distribution.has_scripts() + + def has_data(self): + """Returns true if the current distribution has any data to. + install.""" + return self.distribution.has_data_files() + + # 'sub_commands': a list of commands this command might have to run to + # get its work done. See cmd.py for more info. + sub_commands = [('install_lib', has_lib), + ('install_headers', has_headers), + ('install_scripts', has_scripts), + ('install_data', has_data), + ('install_egg_info', lambda self:True), + ] diff --git a/distutils/command/install_data.py b/distutils/command/install_data.py new file mode 100644 index 00000000..947cd76a --- /dev/null +++ b/distutils/command/install_data.py @@ -0,0 +1,79 @@ +"""distutils.command.install_data + +Implements the Distutils 'install_data' command, for installing +platform-independent data files.""" + +# contributed by Bastian Kleineidam + +import os +from distutils.core import Command +from distutils.util import change_root, convert_path + +class install_data(Command): + + description = "install data files" + + user_options = [ + ('install-dir=', 'd', + "base directory for installing data files " + "(default: installation base dir)"), + ('root=', None, + "install everything relative to this alternate root directory"), + ('force', 'f', "force installation (overwrite existing files)"), + ] + + boolean_options = ['force'] + + def initialize_options(self): + self.install_dir = None + self.outfiles = [] + self.root = None + self.force = 0 + self.data_files = self.distribution.data_files + self.warn_dir = 1 + + def finalize_options(self): + self.set_undefined_options('install', + ('install_data', 'install_dir'), + ('root', 'root'), + ('force', 'force'), + ) + + def run(self): + self.mkpath(self.install_dir) + for f in self.data_files: + if isinstance(f, str): + # it's a simple file, so copy it + f = convert_path(f) + if self.warn_dir: + self.warn("setup script did not provide a directory for " + "'%s' -- installing right in '%s'" % + (f, self.install_dir)) + (out, _) = self.copy_file(f, self.install_dir) + self.outfiles.append(out) + else: + # it's a tuple with path to install to and a list of files + dir = convert_path(f[0]) + if not os.path.isabs(dir): + dir = os.path.join(self.install_dir, dir) + elif self.root: + dir = change_root(self.root, dir) + self.mkpath(dir) + + if f[1] == []: + # If there are no files listed, the user must be + # trying to create an empty directory, so add the + # directory to the list of output files. + self.outfiles.append(dir) + else: + # Copy files, adding them to the list of output files. + for data in f[1]: + data = convert_path(data) + (out, _) = self.copy_file(data, dir) + self.outfiles.append(out) + + def get_inputs(self): + return self.data_files or [] + + def get_outputs(self): + return self.outfiles diff --git a/distutils/command/install_egg_info.py b/distutils/command/install_egg_info.py new file mode 100644 index 00000000..0ddc7367 --- /dev/null +++ b/distutils/command/install_egg_info.py @@ -0,0 +1,77 @@ +"""distutils.command.install_egg_info + +Implements the Distutils 'install_egg_info' command, for installing +a package's PKG-INFO metadata.""" + + +from distutils.cmd import Command +from distutils import log, dir_util +import os, sys, re + +class install_egg_info(Command): + """Install an .egg-info file for the package""" + + description = "Install package's PKG-INFO metadata as an .egg-info file" + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ] + + def initialize_options(self): + self.install_dir = None + + def finalize_options(self): + self.set_undefined_options('install_lib',('install_dir','install_dir')) + basename = "%s-%s-py%d.%d.egg-info" % ( + to_filename(safe_name(self.distribution.get_name())), + to_filename(safe_version(self.distribution.get_version())), + *sys.version_info[:2] + ) + self.target = os.path.join(self.install_dir, basename) + self.outputs = [self.target] + + def run(self): + target = self.target + if os.path.isdir(target) and not os.path.islink(target): + dir_util.remove_tree(target, dry_run=self.dry_run) + elif os.path.exists(target): + self.execute(os.unlink,(self.target,),"Removing "+target) + elif not os.path.isdir(self.install_dir): + self.execute(os.makedirs, (self.install_dir,), + "Creating "+self.install_dir) + log.info("Writing %s", target) + if not self.dry_run: + with open(target, 'w', encoding='UTF-8') as f: + self.distribution.metadata.write_pkg_file(f) + + def get_outputs(self): + return self.outputs + + +# The following routines are taken from setuptools' pkg_resources module and +# can be replaced by importing them from pkg_resources once it is included +# in the stdlib. + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """Convert an arbitrary string to a standard version string + + Spaces become dots, and all other non-alphanumeric characters become + dashes, with runs of multiple dashes condensed to a single dash. + """ + version = version.replace(' ','.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-','_') diff --git a/distutils/command/install_headers.py b/distutils/command/install_headers.py new file mode 100644 index 00000000..9bb0b18d --- /dev/null +++ b/distutils/command/install_headers.py @@ -0,0 +1,47 @@ +"""distutils.command.install_headers + +Implements the Distutils 'install_headers' command, to install C/C++ header +files to the Python include directory.""" + +from distutils.core import Command + + +# XXX force is never used +class install_headers(Command): + + description = "install C/C++ header files" + + user_options = [('install-dir=', 'd', + "directory to install header files to"), + ('force', 'f', + "force installation (overwrite existing files)"), + ] + + boolean_options = ['force'] + + def initialize_options(self): + self.install_dir = None + self.force = 0 + self.outfiles = [] + + def finalize_options(self): + self.set_undefined_options('install', + ('install_headers', 'install_dir'), + ('force', 'force')) + + + def run(self): + headers = self.distribution.headers + if not headers: + return + + self.mkpath(self.install_dir) + for header in headers: + (out, _) = self.copy_file(header, self.install_dir) + self.outfiles.append(out) + + def get_inputs(self): + return self.distribution.headers or [] + + def get_outputs(self): + return self.outfiles diff --git a/distutils/command/install_lib.py b/distutils/command/install_lib.py new file mode 100644 index 00000000..6154cf09 --- /dev/null +++ b/distutils/command/install_lib.py @@ -0,0 +1,217 @@ +"""distutils.command.install_lib + +Implements the Distutils 'install_lib' command +(install all Python modules).""" + +import os +import importlib.util +import sys + +from distutils.core import Command +from distutils.errors import DistutilsOptionError + + +# Extension for Python source files. +PYTHON_SOURCE_EXTENSION = ".py" + +class install_lib(Command): + + description = "install all Python modules (extensions and pure Python)" + + # The byte-compilation options are a tad confusing. Here are the + # possible scenarios: + # 1) no compilation at all (--no-compile --no-optimize) + # 2) compile .pyc only (--compile --no-optimize; default) + # 3) compile .pyc and "opt-1" .pyc (--compile --optimize) + # 4) compile "opt-1" .pyc only (--no-compile --optimize) + # 5) compile .pyc and "opt-2" .pyc (--compile --optimize-more) + # 6) compile "opt-2" .pyc only (--no-compile --optimize-more) + # + # The UI for this is two options, 'compile' and 'optimize'. + # 'compile' is strictly boolean, and only decides whether to + # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and + # decides both whether to generate .pyc files and what level of + # optimization to use. + + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ('build-dir=','b', "build directory (where to install from)"), + ('force', 'f', "force installation (overwrite existing files)"), + ('compile', 'c', "compile .py to .pyc [default]"), + ('no-compile', None, "don't compile .py files"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + ('skip-build', None, "skip the build steps"), + ] + + boolean_options = ['force', 'compile', 'skip-build'] + negative_opt = {'no-compile' : 'compile'} + + def initialize_options(self): + # let the 'install' command dictate our installation directory + self.install_dir = None + self.build_dir = None + self.force = 0 + self.compile = None + self.optimize = None + self.skip_build = None + + def finalize_options(self): + # Get all the information we need to install pure Python modules + # from the umbrella 'install' command -- build (source) directory, + # install (target) directory, and whether to compile .py files. + self.set_undefined_options('install', + ('build_lib', 'build_dir'), + ('install_lib', 'install_dir'), + ('force', 'force'), + ('compile', 'compile'), + ('optimize', 'optimize'), + ('skip_build', 'skip_build'), + ) + + if self.compile is None: + self.compile = True + if self.optimize is None: + self.optimize = False + + if not isinstance(self.optimize, int): + try: + self.optimize = int(self.optimize) + if self.optimize not in (0, 1, 2): + raise AssertionError + except (ValueError, AssertionError): + raise DistutilsOptionError("optimize must be 0, 1, or 2") + + def run(self): + # Make sure we have built everything we need first + self.build() + + # Install everything: simply dump the entire contents of the build + # directory to the installation directory (that's the beauty of + # having a build directory!) + outfiles = self.install() + + # (Optionally) compile .py to .pyc + if outfiles is not None and self.distribution.has_pure_modules(): + self.byte_compile(outfiles) + + # -- Top-level worker functions ------------------------------------ + # (called from 'run()') + + def build(self): + if not self.skip_build: + if self.distribution.has_pure_modules(): + self.run_command('build_py') + if self.distribution.has_ext_modules(): + self.run_command('build_ext') + + def install(self): + if os.path.isdir(self.build_dir): + outfiles = self.copy_tree(self.build_dir, self.install_dir) + else: + self.warn("'%s' does not exist -- no Python modules to install" % + self.build_dir) + return + return outfiles + + def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + + from distutils.util import byte_compile + + # Get the "--root" directory supplied to the "install" command, + # and use it as a prefix to strip off the purported filename + # encoded in bytecode files. This is far from complete, but it + # should at least generate usable bytecode in RPM distributions. + install_root = self.get_finalized_command('install').root + + if self.compile: + byte_compile(files, optimize=0, + force=self.force, prefix=install_root, + dry_run=self.dry_run) + if self.optimize > 0: + byte_compile(files, optimize=self.optimize, + force=self.force, prefix=install_root, + verbose=self.verbose, dry_run=self.dry_run) + + + # -- Utility methods ----------------------------------------------- + + def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir): + if not has_any: + return [] + + build_cmd = self.get_finalized_command(build_cmd) + build_files = build_cmd.get_outputs() + build_dir = getattr(build_cmd, cmd_option) + + prefix_len = len(build_dir) + len(os.sep) + outputs = [] + for file in build_files: + outputs.append(os.path.join(output_dir, file[prefix_len:])) + + return outputs + + def _bytecode_filenames(self, py_filenames): + bytecode_files = [] + for py_file in py_filenames: + # Since build_py handles package data installation, the + # list of outputs can contain more than just .py files. + # Make sure we only report bytecode for the .py files. + ext = os.path.splitext(os.path.normcase(py_file))[1] + if ext != PYTHON_SOURCE_EXTENSION: + continue + if self.compile: + bytecode_files.append(importlib.util.cache_from_source( + py_file, optimization='')) + if self.optimize > 0: + bytecode_files.append(importlib.util.cache_from_source( + py_file, optimization=self.optimize)) + + return bytecode_files + + + # -- External interface -------------------------------------------- + # (called by outsiders) + + def get_outputs(self): + """Return the list of files that would be installed if this command + were actually run. Not affected by the "dry-run" flag or whether + modules have actually been built yet. + """ + pure_outputs = \ + self._mutate_outputs(self.distribution.has_pure_modules(), + 'build_py', 'build_lib', + self.install_dir) + if self.compile: + bytecode_outputs = self._bytecode_filenames(pure_outputs) + else: + bytecode_outputs = [] + + ext_outputs = \ + self._mutate_outputs(self.distribution.has_ext_modules(), + 'build_ext', 'build_lib', + self.install_dir) + + return pure_outputs + bytecode_outputs + ext_outputs + + def get_inputs(self): + """Get the list of files that are input to this command, ie. the + files that get installed as they are named in the build tree. + The files in this list correspond one-to-one to the output + filenames returned by 'get_outputs()'. + """ + inputs = [] + + if self.distribution.has_pure_modules(): + build_py = self.get_finalized_command('build_py') + inputs.extend(build_py.get_outputs()) + + if self.distribution.has_ext_modules(): + build_ext = self.get_finalized_command('build_ext') + inputs.extend(build_ext.get_outputs()) + + return inputs diff --git a/distutils/command/install_scripts.py b/distutils/command/install_scripts.py new file mode 100644 index 00000000..31a1130e --- /dev/null +++ b/distutils/command/install_scripts.py @@ -0,0 +1,60 @@ +"""distutils.command.install_scripts + +Implements the Distutils 'install_scripts' command, for installing +Python scripts.""" + +# contributed by Bastian Kleineidam + +import os +from distutils.core import Command +from distutils import log +from stat import ST_MODE + + +class install_scripts(Command): + + description = "install scripts (Python or otherwise)" + + user_options = [ + ('install-dir=', 'd', "directory to install scripts to"), + ('build-dir=','b', "build directory (where to install from)"), + ('force', 'f', "force installation (overwrite existing files)"), + ('skip-build', None, "skip the build steps"), + ] + + boolean_options = ['force', 'skip-build'] + + def initialize_options(self): + self.install_dir = None + self.force = 0 + self.build_dir = None + self.skip_build = None + + def finalize_options(self): + self.set_undefined_options('build', ('build_scripts', 'build_dir')) + self.set_undefined_options('install', + ('install_scripts', 'install_dir'), + ('force', 'force'), + ('skip_build', 'skip_build'), + ) + + def run(self): + if not self.skip_build: + self.run_command('build_scripts') + self.outfiles = self.copy_tree(self.build_dir, self.install_dir) + if os.name == 'posix': + # Set the executable bits (owner, group, and world) on + # all the scripts we just installed. + for file in self.get_outputs(): + if self.dry_run: + log.info("changing mode of %s", file) + else: + mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777 + log.info("changing mode of %s to %o", file, mode) + os.chmod(file, mode) + + def get_inputs(self): + return self.distribution.scripts or [] + + def get_outputs(self): + return self.outfiles or [] diff --git a/distutils/command/register.py b/distutils/command/register.py new file mode 100644 index 00000000..0fac94e9 --- /dev/null +++ b/distutils/command/register.py @@ -0,0 +1,304 @@ +"""distutils.command.register + +Implements the Distutils 'register' command (register with the repository). +""" + +# created 2002/10/21, Richard Jones + +import getpass +import io +import urllib.parse, urllib.request +from warnings import warn + +from distutils.core import PyPIRCCommand +from distutils.errors import * +from distutils import log + +class register(PyPIRCCommand): + + description = ("register the distribution with the Python package index") + user_options = PyPIRCCommand.user_options + [ + ('list-classifiers', None, + 'list the valid Trove classifiers'), + ('strict', None , + 'Will stop the registering if the meta-data are not fully compliant') + ] + boolean_options = PyPIRCCommand.boolean_options + [ + 'verify', 'list-classifiers', 'strict'] + + sub_commands = [('check', lambda self: True)] + + def initialize_options(self): + PyPIRCCommand.initialize_options(self) + self.list_classifiers = 0 + self.strict = 0 + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + # setting options for the `check` subcommand + check_options = {'strict': ('register', self.strict), + 'restructuredtext': ('register', 1)} + self.distribution.command_options['check'] = check_options + + def run(self): + self.finalize_options() + self._set_config() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + if self.dry_run: + self.verify_metadata() + elif self.list_classifiers: + self.classifiers() + else: + self.send_metadata() + + def check_metadata(self): + """Deprecated API.""" + warn("distutils.command.register.check_metadata is deprecated, \ + use the check command instead", PendingDeprecationWarning) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.strict = self.strict + check.restructuredtext = 1 + check.run() + + def _set_config(self): + ''' Reads the configuration file and set attributes. + ''' + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + self.has_config = True + else: + if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): + raise ValueError('%s not found in .pypirc' % self.repository) + if self.repository == 'pypi': + self.repository = self.DEFAULT_REPOSITORY + self.has_config = False + + def classifiers(self): + ''' Fetch the list of classifiers from the server. + ''' + url = self.repository+'?:action=list_classifiers' + response = urllib.request.urlopen(url) + log.info(self._read_pypi_response(response)) + + def verify_metadata(self): + ''' Send the metadata to the package index server to be checked. + ''' + # send the info to the server and report the result + (code, result) = self.post_to_server(self.build_post_data('verify')) + log.info('Server response (%s): %s', code, result) + + def send_metadata(self): + ''' Send the metadata to the package index server. + + Well, do the following: + 1. figure who the user is, and then + 2. send the data as a Basic auth'ed POST. + + First we try to read the username/password from $HOME/.pypirc, + which is a ConfigParser-formatted file with a section + [distutils] containing username and password entries (both + in clear text). Eg: + + [distutils] + index-servers = + pypi + + [pypi] + username: fred + password: sekrit + + Otherwise, to figure who the user is, we offer the user three + choices: + + 1. use existing login, + 2. register as a new user, or + 3. set the password to a random string and email the user. + + ''' + # see if we can short-cut and get the username/password from the + # config + if self.has_config: + choice = '1' + username = self.username + password = self.password + else: + choice = 'x' + username = password = '' + + # get the user's login info + choices = '1 2 3 4'.split() + while choice not in choices: + self.announce('''\ +We need to know who you are, so please choose either: + 1. use your existing login, + 2. register as a new user, + 3. have the server generate a new password for you (and email it to you), or + 4. quit +Your selection [default 1]: ''', log.INFO) + choice = input() + if not choice: + choice = '1' + elif choice not in choices: + print('Please choose one of the four options!') + + if choice == '1': + # get the username and password + while not username: + username = input('Username: ') + while not password: + password = getpass.getpass('Password: ') + + # set up the authentication + auth = urllib.request.HTTPPasswordMgr() + host = urllib.parse.urlparse(self.repository)[1] + auth.add_password(self.realm, host, username, password) + # send the info to the server and report the result + code, result = self.post_to_server(self.build_post_data('submit'), + auth) + self.announce('Server response (%s): %s' % (code, result), + log.INFO) + + # possibly save the login + if code == 200: + if self.has_config: + # sharing the password in the distribution instance + # so the upload command can reuse it + self.distribution.password = password + else: + self.announce(('I can store your PyPI login so future ' + 'submissions will be faster.'), log.INFO) + self.announce('(the login will be stored in %s)' % \ + self._get_rc_file(), log.INFO) + choice = 'X' + while choice.lower() not in 'yn': + choice = input('Save your login (y/N)?') + if not choice: + choice = 'n' + if choice.lower() == 'y': + self._store_pypirc(username, password) + + elif choice == '2': + data = {':action': 'user'} + data['name'] = data['password'] = data['email'] = '' + data['confirm'] = None + while not data['name']: + data['name'] = input('Username: ') + while data['password'] != data['confirm']: + while not data['password']: + data['password'] = getpass.getpass('Password: ') + while not data['confirm']: + data['confirm'] = getpass.getpass(' Confirm: ') + if data['password'] != data['confirm']: + data['password'] = '' + data['confirm'] = None + print("Password and confirm don't match!") + while not data['email']: + data['email'] = input(' EMail: ') + code, result = self.post_to_server(data) + if code != 200: + log.info('Server response (%s): %s', code, result) + else: + log.info('You will receive an email shortly.') + log.info(('Follow the instructions in it to ' + 'complete registration.')) + elif choice == '3': + data = {':action': 'password_reset'} + data['email'] = '' + while not data['email']: + data['email'] = input('Your email address: ') + code, result = self.post_to_server(data) + log.info('Server response (%s): %s', code, result) + + def build_post_data(self, action): + # figure the data to send - the metadata plus some additional + # information used by the package server + meta = self.distribution.metadata + data = { + ':action': action, + 'metadata_version' : '1.0', + 'name': meta.get_name(), + 'version': meta.get_version(), + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), + } + if data['provides'] or data['requires'] or data['obsoletes']: + data['metadata_version'] = '1.1' + return data + + def post_to_server(self, data, auth=None): + ''' Post a query to the server, and return a string response. + ''' + if 'name' in data: + self.announce('Registering %s to %s' % (data['name'], + self.repository), + log.INFO) + # Build up the MIME payload for the urllib2 POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = '\n--' + boundary + end_boundary = sep_boundary + '--' + body = io.StringIO() + for key, value in data.items(): + # handle multiple entries for the same name + if type(value) not in (type([]), type( () )): + value = [value] + for value in value: + value = str(value) + body.write(sep_boundary) + body.write('\nContent-Disposition: form-data; name="%s"'%key) + body.write("\n\n") + body.write(value) + if value and value[-1] == '\r': + body.write('\n') # write an extra newline (lurve Macs) + body.write(end_boundary) + body.write("\n") + body = body.getvalue().encode("utf-8") + + # build the Request + headers = { + 'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary, + 'Content-length': str(len(body)) + } + req = urllib.request.Request(self.repository, body, headers) + + # handle HTTP and include the Basic Auth handler + opener = urllib.request.build_opener( + urllib.request.HTTPBasicAuthHandler(password_mgr=auth) + ) + data = '' + try: + result = opener.open(req) + except urllib.error.HTTPError as e: + if self.show_response: + data = e.fp.read() + result = e.code, e.msg + except urllib.error.URLError as e: + result = 500, str(e) + else: + if self.show_response: + data = self._read_pypi_response(result) + result = 200, 'OK' + if self.show_response: + msg = '\n'.join(('-' * 75, data, '-' * 75)) + self.announce(msg, log.INFO) + return result diff --git a/distutils/command/sdist.py b/distutils/command/sdist.py new file mode 100644 index 00000000..b4996fcb --- /dev/null +++ b/distutils/command/sdist.py @@ -0,0 +1,494 @@ +"""distutils.command.sdist + +Implements the Distutils 'sdist' command (create a source distribution).""" + +import os +import sys +from glob import glob +from warnings import warn + +from distutils.core import Command +from distutils import dir_util +from distutils import file_util +from distutils import archive_util +from distutils.text_file import TextFile +from distutils.filelist import FileList +from distutils import log +from distutils.util import convert_path +from distutils.errors import DistutilsTemplateError, DistutilsOptionError + + +def show_formats(): + """Print all possible values for the 'formats' option (used by + the "--help-formats" command-line option). + """ + from distutils.fancy_getopt import FancyGetopt + from distutils.archive_util import ARCHIVE_FORMATS + formats = [] + for format in ARCHIVE_FORMATS.keys(): + formats.append(("formats=" + format, None, + ARCHIVE_FORMATS[format][2])) + formats.sort() + FancyGetopt(formats).print_help( + "List of available source distribution formats:") + + +class sdist(Command): + + description = "create a source distribution (tarball, zip file, etc.)" + + def checking_metadata(self): + """Callable used for the check sub-command. + + Placed here so user_options can view it""" + return self.metadata_check + + user_options = [ + ('template=', 't', + "name of manifest template file [default: MANIFEST.in]"), + ('manifest=', 'm', + "name of manifest file [default: MANIFEST]"), + ('use-defaults', None, + "include the default file set in the manifest " + "[default; disable with --no-defaults]"), + ('no-defaults', None, + "don't include the default file set"), + ('prune', None, + "specifically exclude files/directories that should not be " + "distributed (build tree, RCS/CVS dirs, etc.) " + "[default; disable with --no-prune]"), + ('no-prune', None, + "don't automatically exclude anything"), + ('manifest-only', 'o', + "just regenerate the manifest and then stop " + "(implies --force-manifest)"), + ('force-manifest', 'f', + "forcibly regenerate the manifest and carry on as usual. " + "Deprecated: now the manifest is always regenerated."), + ('formats=', None, + "formats for source distribution (comma-separated list)"), + ('keep-temp', 'k', + "keep the distribution tree around after creating " + + "archive file(s)"), + ('dist-dir=', 'd', + "directory to put the source distribution archive(s) in " + "[default: dist]"), + ('metadata-check', None, + "Ensure that all required elements of meta-data " + "are supplied. Warn if any missing. [default]"), + ('owner=', 'u', + "Owner name used when creating a tar file [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file [default: current group]"), + ] + + boolean_options = ['use-defaults', 'prune', + 'manifest-only', 'force-manifest', + 'keep-temp', 'metadata-check'] + + help_options = [ + ('help-formats', None, + "list available distribution formats", show_formats), + ] + + negative_opt = {'no-defaults': 'use-defaults', + 'no-prune': 'prune' } + + sub_commands = [('check', checking_metadata)] + + READMES = ('README', 'README.txt', 'README.rst') + + def initialize_options(self): + # 'template' and 'manifest' are, respectively, the names of + # the manifest template and manifest file. + self.template = None + self.manifest = None + + # 'use_defaults': if true, we will include the default file set + # in the manifest + self.use_defaults = 1 + self.prune = 1 + + self.manifest_only = 0 + self.force_manifest = 0 + + self.formats = ['gztar'] + self.keep_temp = 0 + self.dist_dir = None + + self.archive_files = None + self.metadata_check = 1 + self.owner = None + self.group = None + + def finalize_options(self): + if self.manifest is None: + self.manifest = "MANIFEST" + if self.template is None: + self.template = "MANIFEST.in" + + self.ensure_string_list('formats') + + bad_format = archive_util.check_archive_formats(self.formats) + if bad_format: + raise DistutilsOptionError( + "unknown archive format '%s'" % bad_format) + + if self.dist_dir is None: + self.dist_dir = "dist" + + def run(self): + # 'filelist' contains the list of files that will make up the + # manifest + self.filelist = FileList() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + # Do whatever it takes to get the list of files to process + # (process the manifest template, read an existing manifest, + # whatever). File list is accumulated in 'self.filelist'. + self.get_file_list() + + # If user just wanted us to regenerate the manifest, stop now. + if self.manifest_only: + return + + # Otherwise, go ahead and create the source distribution tarball, + # or zipfile, or whatever. + self.make_distribution() + + def check_metadata(self): + """Deprecated API.""" + warn("distutils.command.sdist.check_metadata is deprecated, \ + use the check command instead", PendingDeprecationWarning) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.run() + + def get_file_list(self): + """Figure out the list of files to include in the source + distribution, and put it in 'self.filelist'. This might involve + reading the manifest template (and writing the manifest), or just + reading the manifest, or just using the default file set -- it all + depends on the user's options. + """ + # new behavior when using a template: + # the file list is recalculated every time because + # even if MANIFEST.in or setup.py are not changed + # the user might have added some files in the tree that + # need to be included. + # + # This makes --force the default and only behavior with templates. + template_exists = os.path.isfile(self.template) + if not template_exists and self._manifest_is_not_generated(): + self.read_manifest() + self.filelist.sort() + self.filelist.remove_duplicates() + return + + if not template_exists: + self.warn(("manifest template '%s' does not exist " + + "(using default file list)") % + self.template) + self.filelist.findall() + + if self.use_defaults: + self.add_defaults() + + if template_exists: + self.read_template() + + if self.prune: + self.prune_file_list() + + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() + + def add_defaults(self): + """Add all the default files to self.filelist: + - README or README.txt + - setup.py + - test/test*.py + - all pure Python modules mentioned in setup script + - all files pointed by package_data (build_py) + - all files defined in data_files. + - all files defined as scripts. + - all C sources listed as part of extensions or C libraries + in the setup script (doesn't catch C headers!) + Warns if (README or README.txt) or setup.py are missing; everything + else is optional. + """ + self._add_defaults_standards() + self._add_defaults_optional() + self._add_defaults_python() + self._add_defaults_data_files() + self._add_defaults_ext() + self._add_defaults_c_libs() + self._add_defaults_scripts() + + @staticmethod + def _cs_path_exists(fspath): + """ + Case-sensitive path existence check + + >>> sdist._cs_path_exists(__file__) + True + >>> sdist._cs_path_exists(__file__.upper()) + False + """ + if not os.path.exists(fspath): + return False + # make absolute so we always have a directory + abspath = os.path.abspath(fspath) + directory, filename = os.path.split(abspath) + return filename in os.listdir(directory) + + def _add_defaults_standards(self): + standards = [self.READMES, self.distribution.script_name] + for fn in standards: + if isinstance(fn, tuple): + alts = fn + got_it = False + for fn in alts: + if self._cs_path_exists(fn): + got_it = True + self.filelist.append(fn) + break + + if not got_it: + self.warn("standard file not found: should have one of " + + ', '.join(alts)) + else: + if self._cs_path_exists(fn): + self.filelist.append(fn) + else: + self.warn("standard file '%s' not found" % fn) + + def _add_defaults_optional(self): + optional = ['test/test*.py', 'setup.cfg'] + for pattern in optional: + files = filter(os.path.isfile, glob(pattern)) + self.filelist.extend(files) + + def _add_defaults_python(self): + # build_py is used to get: + # - python modules + # - files defined in package_data + build_py = self.get_finalized_command('build_py') + + # getting python files + if self.distribution.has_pure_modules(): + self.filelist.extend(build_py.get_source_files()) + + # getting package_data files + # (computed in build_py.data_files by build_py.finalize_options) + for pkg, src_dir, build_dir, filenames in build_py.data_files: + for filename in filenames: + self.filelist.append(os.path.join(src_dir, filename)) + + def _add_defaults_data_files(self): + # getting distribution.data_files + if self.distribution.has_data_files(): + for item in self.distribution.data_files: + if isinstance(item, str): + # plain file + item = convert_path(item) + if os.path.isfile(item): + self.filelist.append(item) + else: + # a (dirname, filenames) tuple + dirname, filenames = item + for f in filenames: + f = convert_path(f) + if os.path.isfile(f): + self.filelist.append(f) + + def _add_defaults_ext(self): + if self.distribution.has_ext_modules(): + build_ext = self.get_finalized_command('build_ext') + self.filelist.extend(build_ext.get_source_files()) + + def _add_defaults_c_libs(self): + if self.distribution.has_c_libraries(): + build_clib = self.get_finalized_command('build_clib') + self.filelist.extend(build_clib.get_source_files()) + + def _add_defaults_scripts(self): + if self.distribution.has_scripts(): + build_scripts = self.get_finalized_command('build_scripts') + self.filelist.extend(build_scripts.get_source_files()) + + def read_template(self): + """Read and parse manifest template file named by self.template. + + (usually "MANIFEST.in") The parsing and processing is done by + 'self.filelist', which updates itself accordingly. + """ + log.info("reading manifest template '%s'", self.template) + template = TextFile(self.template, strip_comments=1, skip_blanks=1, + join_lines=1, lstrip_ws=1, rstrip_ws=1, + collapse_join=1) + + try: + while True: + line = template.readline() + if line is None: # end of file + break + + try: + self.filelist.process_template_line(line) + # the call above can raise a DistutilsTemplateError for + # malformed lines, or a ValueError from the lower-level + # convert_path function + except (DistutilsTemplateError, ValueError) as msg: + self.warn("%s, line %d: %s" % (template.filename, + template.current_line, + msg)) + finally: + template.close() + + def prune_file_list(self): + """Prune off branches that might slip into the file list as created + by 'read_template()', but really don't belong there: + * the build tree (typically "build") + * the release tree itself (only an issue if we ran "sdist" + previously with --keep-temp, or it aborted) + * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories + """ + build = self.get_finalized_command('build') + base_dir = self.distribution.get_fullname() + + self.filelist.exclude_pattern(None, prefix=build.build_base) + self.filelist.exclude_pattern(None, prefix=base_dir) + + if sys.platform == 'win32': + seps = r'/|\\' + else: + seps = '/' + + vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', + '_darcs'] + vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) + self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) + + def write_manifest(self): + """Write the file list in 'self.filelist' (presumably as filled in + by 'add_defaults()' and 'read_template()') to the manifest file + named by 'self.manifest'. + """ + if self._manifest_is_not_generated(): + log.info("not writing to manually maintained " + "manifest file '%s'" % self.manifest) + return + + content = self.filelist.files[:] + content.insert(0, '# file GENERATED by distutils, do NOT edit') + self.execute(file_util.write_file, (self.manifest, content), + "writing manifest file '%s'" % self.manifest) + + def _manifest_is_not_generated(self): + # check for special comment used in 3.1.3 and higher + if not os.path.isfile(self.manifest): + return False + + fp = open(self.manifest) + try: + first_line = fp.readline() + finally: + fp.close() + return first_line != '# file GENERATED by distutils, do NOT edit\n' + + def read_manifest(self): + """Read the manifest file (named by 'self.manifest') and use it to + fill in 'self.filelist', the list of files to include in the source + distribution. + """ + log.info("reading manifest file '%s'", self.manifest) + with open(self.manifest) as manifest: + for line in manifest: + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue + self.filelist.append(line) + + def make_release_tree(self, base_dir, files): + """Create the directory tree that will become the source + distribution archive. All directories implied by the filenames in + 'files' are created under 'base_dir', and then we hard link or copy + (if hard linking is unavailable) those files into place. + Essentially, this duplicates the developer's source tree, but in a + directory named after the distribution, containing only the files + to be distributed. + """ + # Create all the directories under 'base_dir' necessary to + # put 'files' there; the 'mkpath()' is just so we don't die + # if the manifest happens to be empty. + self.mkpath(base_dir) + dir_util.create_tree(base_dir, files, dry_run=self.dry_run) + + # And walk over the list of files, either making a hard link (if + # os.link exists) to each one that doesn't already exist in its + # corresponding location under 'base_dir', or copying each file + # that's out-of-date in 'base_dir'. (Usually, all files will be + # out-of-date, because by default we blow away 'base_dir' when + # we're done making the distribution archives.) + + if hasattr(os, 'link'): # can make hard links on this system + link = 'hard' + msg = "making hard links in %s..." % base_dir + else: # nope, have to copy + link = None + msg = "copying files to %s..." % base_dir + + if not files: + log.warn("no files to distribute -- empty manifest?") + else: + log.info(msg) + for file in files: + if not os.path.isfile(file): + log.warn("'%s' not a regular file -- skipping", file) + else: + dest = os.path.join(base_dir, file) + self.copy_file(file, dest, link=link) + + self.distribution.metadata.write_pkg_info(base_dir) + + def make_distribution(self): + """Create the source distribution(s). First, we create the release + tree with 'make_release_tree()'; then, we create all required + archive files (according to 'self.formats') from the release tree. + Finally, we clean up by blowing away the release tree (unless + 'self.keep_temp' is true). The list of archive files created is + stored so it can be retrieved later by 'get_archive_files()'. + """ + # Don't warn about missing meta-data here -- should be (and is!) + # done elsewhere. + base_dir = self.distribution.get_fullname() + base_name = os.path.join(self.dist_dir, base_dir) + + self.make_release_tree(base_dir, self.filelist.files) + archive_files = [] # remember names of files we create + # tar archive must be created last to avoid overwrite and remove + if 'tar' in self.formats: + self.formats.append(self.formats.pop(self.formats.index('tar'))) + + for fmt in self.formats: + file = self.make_archive(base_name, fmt, base_dir=base_dir, + owner=self.owner, group=self.group) + archive_files.append(file) + self.distribution.dist_files.append(('sdist', '', file)) + + self.archive_files = archive_files + + if not self.keep_temp: + dir_util.remove_tree(base_dir, dry_run=self.dry_run) + + def get_archive_files(self): + """Return the list of archive files created when the command + was run, or None if the command hasn't run yet. + """ + return self.archive_files diff --git a/distutils/command/upload.py b/distutils/command/upload.py new file mode 100644 index 00000000..95e9fda1 --- /dev/null +++ b/distutils/command/upload.py @@ -0,0 +1,214 @@ +""" +distutils.command.upload + +Implements the Distutils 'upload' subcommand (upload package to a package +index). +""" + +import os +import io +import hashlib +from base64 import standard_b64encode +from urllib.request import urlopen, Request, HTTPError +from urllib.parse import urlparse +from distutils.errors import DistutilsError, DistutilsOptionError +from distutils.core import PyPIRCCommand +from distutils.spawn import spawn +from distutils import log + + +# PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256) +# https://bugs.python.org/issue40698 +_FILE_CONTENT_DIGESTS = { + "md5_digest": getattr(hashlib, "md5", None), + "sha256_digest": getattr(hashlib, "sha256", None), + "blake2_256_digest": getattr(hashlib, "blake2b", None), +} + + +class upload(PyPIRCCommand): + + description = "upload binary package to PyPI" + + user_options = PyPIRCCommand.user_options + [ + ('sign', 's', + 'sign files to upload using gpg'), + ('identity=', 'i', 'GPG identity used to sign files'), + ] + + boolean_options = PyPIRCCommand.boolean_options + ['sign'] + + def initialize_options(self): + PyPIRCCommand.initialize_options(self) + self.username = '' + self.password = '' + self.show_response = 0 + self.sign = False + self.identity = None + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + if self.identity and not self.sign: + raise DistutilsOptionError( + "Must use --sign for --identity to have meaning" + ) + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + + # getting the password from the distribution + # if previously set by the register command + if not self.password and self.distribution.password: + self.password = self.distribution.password + + def run(self): + if not self.distribution.dist_files: + msg = ("Must create and upload files in one command " + "(e.g. setup.py sdist upload)") + raise DistutilsOptionError(msg) + for command, pyversion, filename in self.distribution.dist_files: + self.upload_file(command, pyversion, filename) + + def upload_file(self, command, pyversion, filename): + # Makes sure the repository URL is compliant + schema, netloc, url, params, query, fragments = \ + urlparse(self.repository) + if params or query or fragments: + raise AssertionError("Incompatible url %s" % self.repository) + + if schema not in ('http', 'https'): + raise AssertionError("unsupported schema " + schema) + + # Sign if requested + if self.sign: + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity] + spawn(gpg_args, + dry_run=self.dry_run) + + # Fill in the data - send all the meta-data in case we need to + # register a new release + f = open(filename,'rb') + try: + content = f.read() + finally: + f.close() + + meta = self.distribution.metadata + data = { + # action + ':action': 'file_upload', + 'protocol_version': '1', + + # identify release + 'name': meta.get_name(), + 'version': meta.get_version(), + + # file content + 'content': (os.path.basename(filename),content), + 'filetype': command, + 'pyversion': pyversion, + + # additional meta-data + 'metadata_version': '1.0', + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), + } + + data['comment'] = '' + + # file content digests + for digest_name, digest_cons in _FILE_CONTENT_DIGESTS.items(): + if digest_cons is None: + continue + try: + data[digest_name] = digest_cons(content).hexdigest() + except ValueError: + # hash digest not available or blocked by security policy + pass + + if self.sign: + with open(filename + ".asc", "rb") as f: + data['gpg_signature'] = (os.path.basename(filename) + ".asc", + f.read()) + + # set up the authentication + user_pass = (self.username + ":" + self.password).encode('ascii') + # The exact encoding of the authentication string is debated. + # Anyway PyPI only accepts ascii for both username or password. + auth = "Basic " + standard_b64encode(user_pass).decode('ascii') + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = b'\r\n--' + boundary.encode('ascii') + end_boundary = sep_boundary + b'--\r\n' + body = io.BytesIO() + for key, value in data.items(): + title = '\r\nContent-Disposition: form-data; name="%s"' % key + # handle multiple entries for the same name + if not isinstance(value, list): + value = [value] + for value in value: + if type(value) is tuple: + title += '; filename="%s"' % value[0] + value = value[1] + else: + value = str(value).encode('utf-8') + body.write(sep_boundary) + body.write(title.encode('utf-8')) + body.write(b"\r\n\r\n") + body.write(value) + body.write(end_boundary) + body = body.getvalue() + + msg = "Submitting %s to %s" % (filename, self.repository) + self.announce(msg, log.INFO) + + # build the Request + headers = { + 'Content-type': 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth, + } + + request = Request(self.repository, data=body, + headers=headers) + # send the data + try: + result = urlopen(request) + status = result.getcode() + reason = result.msg + except HTTPError as e: + status = e.code + reason = e.msg + except OSError as e: + self.announce(str(e), log.ERROR) + raise + + if status == 200: + self.announce('Server response (%s): %s' % (status, reason), + log.INFO) + if self.show_response: + text = self._read_pypi_response(result) + msg = '\n'.join(('-' * 75, text, '-' * 75)) + self.announce(msg, log.INFO) + else: + msg = 'Upload failed (%s): %s' % (status, reason) + self.announce(msg, log.ERROR) + raise DistutilsError(msg) diff --git a/distutils/command/wininst-10.0-amd64.exe b/distutils/command/wininst-10.0-amd64.exe new file mode 100644 index 00000000..6fa0dce1 Binary files /dev/null and b/distutils/command/wininst-10.0-amd64.exe differ diff --git a/distutils/command/wininst-10.0.exe b/distutils/command/wininst-10.0.exe new file mode 100644 index 00000000..afc3bc6c Binary files /dev/null and b/distutils/command/wininst-10.0.exe differ diff --git a/distutils/command/wininst-14.0-amd64.exe b/distutils/command/wininst-14.0-amd64.exe new file mode 100644 index 00000000..253c2e2e Binary files /dev/null and b/distutils/command/wininst-14.0-amd64.exe differ diff --git a/distutils/command/wininst-14.0.exe b/distutils/command/wininst-14.0.exe new file mode 100644 index 00000000..46f5f356 Binary files /dev/null and b/distutils/command/wininst-14.0.exe differ diff --git a/distutils/command/wininst-6.0.exe b/distutils/command/wininst-6.0.exe new file mode 100644 index 00000000..f57c855a Binary files /dev/null and b/distutils/command/wininst-6.0.exe differ diff --git a/distutils/command/wininst-7.1.exe b/distutils/command/wininst-7.1.exe new file mode 100644 index 00000000..1433bc1a Binary files /dev/null and b/distutils/command/wininst-7.1.exe differ diff --git a/distutils/command/wininst-8.0.exe b/distutils/command/wininst-8.0.exe new file mode 100644 index 00000000..7403bfab Binary files /dev/null and b/distutils/command/wininst-8.0.exe differ diff --git a/distutils/command/wininst-9.0-amd64.exe b/distutils/command/wininst-9.0-amd64.exe new file mode 100644 index 00000000..94fbd434 Binary files /dev/null and b/distutils/command/wininst-9.0-amd64.exe differ diff --git a/distutils/command/wininst-9.0.exe b/distutils/command/wininst-9.0.exe new file mode 100644 index 00000000..2ec261f9 Binary files /dev/null and b/distutils/command/wininst-9.0.exe differ diff --git a/distutils/config.py b/distutils/config.py new file mode 100644 index 00000000..2171abd6 --- /dev/null +++ b/distutils/config.py @@ -0,0 +1,130 @@ +"""distutils.pypirc + +Provides the PyPIRCCommand class, the base class for the command classes +that uses .pypirc in the distutils.command package. +""" +import os +from configparser import RawConfigParser + +from distutils.cmd import Command + +DEFAULT_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:%s +password:%s +""" + +class PyPIRCCommand(Command): + """Base command that knows how to handle the .pypirc file + """ + DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/' + DEFAULT_REALM = 'pypi' + repository = None + realm = None + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % \ + DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server')] + + boolean_options = ['show-response'] + + def _get_rc_file(self): + """Returns rc file path.""" + return os.path.join(os.path.expanduser('~'), '.pypirc') + + def _store_pypirc(self, username, password): + """Creates a default .pypirc file.""" + rc = self._get_rc_file() + with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f: + f.write(DEFAULT_PYPIRC % (username, password)) + + def _read_pypirc(self): + """Reads the .pypirc file.""" + rc = self._get_rc_file() + if os.path.exists(rc): + self.announce('Using PyPI login from %s' % rc) + repository = self.repository or self.DEFAULT_REPOSITORY + + config = RawConfigParser() + config.read(rc) + sections = config.sections() + if 'distutils' in sections: + # let's get the list of servers + index_servers = config.get('distutils', 'index-servers') + _servers = [server.strip() for server in + index_servers.split('\n') + if server.strip() != ''] + if _servers == []: + # nothing set, let's try to get the default pypi + if 'pypi' in sections: + _servers = ['pypi'] + else: + # the file is not properly defined, returning + # an empty dict + return {} + for server in _servers: + current = {'server': server} + current['username'] = config.get(server, 'username') + + # optional params + for key, default in (('repository', + self.DEFAULT_REPOSITORY), + ('realm', self.DEFAULT_REALM), + ('password', None)): + if config.has_option(server, key): + current[key] = config.get(server, key) + else: + current[key] = default + + # work around people having "repository" for the "pypi" + # section of their config set to the HTTP (rather than + # HTTPS) URL + if (server == 'pypi' and + repository in (self.DEFAULT_REPOSITORY, 'pypi')): + current['repository'] = self.DEFAULT_REPOSITORY + return current + + if (current['server'] == repository or + current['repository'] == repository): + return current + elif 'server-login' in sections: + # old format + server = 'server-login' + if config.has_option(server, 'repository'): + repository = config.get(server, 'repository') + else: + repository = self.DEFAULT_REPOSITORY + return {'username': config.get(server, 'username'), + 'password': config.get(server, 'password'), + 'repository': repository, + 'server': server, + 'realm': self.DEFAULT_REALM} + + return {} + + def _read_pypi_response(self, response): + """Read and decode a PyPI HTTP response.""" + import cgi + content_type = response.getheader('content-type', 'text/plain') + encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii') + return response.read().decode(encoding) + + def initialize_options(self): + """Initialize options.""" + self.repository = None + self.realm = None + self.show_response = 0 + + def finalize_options(self): + """Finalizes options.""" + if self.repository is None: + self.repository = self.DEFAULT_REPOSITORY + if self.realm is None: + self.realm = self.DEFAULT_REALM diff --git a/distutils/core.py b/distutils/core.py new file mode 100644 index 00000000..d603d4a4 --- /dev/null +++ b/distutils/core.py @@ -0,0 +1,234 @@ +"""distutils.core + +The only module that needs to be imported to use the Distutils; provides +the 'setup' function (which is to be called from the setup script). Also +indirectly provides the Distribution and Command classes, although they are +really defined in distutils.dist and distutils.cmd. +""" + +import os +import sys + +from distutils.debug import DEBUG +from distutils.errors import * + +# Mainly import these so setup scripts can "from distutils.core import" them. +from distutils.dist import Distribution +from distutils.cmd import Command +from distutils.config import PyPIRCCommand +from distutils.extension import Extension + +# This is a barebones help message generated displayed when the user +# runs the setup script with no arguments at all. More useful help +# is generated with various --help options: global help, list commands, +# and per-command help. +USAGE = """\ +usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] + or: %(script)s --help [cmd1 cmd2 ...] + or: %(script)s --help-commands + or: %(script)s cmd --help +""" + +def gen_usage (script_name): + script = os.path.basename(script_name) + return USAGE % vars() + + +# Some mild magic to control the behaviour of 'setup()' from 'run_setup()'. +_setup_stop_after = None +_setup_distribution = None + +# Legal keyword arguments for the setup() function +setup_keywords = ('distclass', 'script_name', 'script_args', 'options', + 'name', 'version', 'author', 'author_email', + 'maintainer', 'maintainer_email', 'url', 'license', + 'description', 'long_description', 'keywords', + 'platforms', 'classifiers', 'download_url', + 'requires', 'provides', 'obsoletes', + ) + +# Legal keyword arguments for the Extension constructor +extension_keywords = ('name', 'sources', 'include_dirs', + 'define_macros', 'undef_macros', + 'library_dirs', 'libraries', 'runtime_library_dirs', + 'extra_objects', 'extra_compile_args', 'extra_link_args', + 'swig_opts', 'export_symbols', 'depends', 'language') + +def setup (**attrs): + """The gateway to the Distutils: do everything your setup script needs + to do, in a highly flexible and user-driven way. Briefly: create a + Distribution instance; find and parse config files; parse the command + line; run each Distutils command found there, customized by the options + supplied to 'setup()' (as keyword arguments), in config files, and on + the command line. + + The Distribution instance might be an instance of a class supplied via + the 'distclass' keyword argument to 'setup'; if no such class is + supplied, then the Distribution class (in dist.py) is instantiated. + All other arguments to 'setup' (except for 'cmdclass') are used to set + attributes of the Distribution instance. + + The 'cmdclass' argument, if supplied, is a dictionary mapping command + names to command classes. Each command encountered on the command line + will be turned into a command class, which is in turn instantiated; any + class found in 'cmdclass' is used in place of the default, which is + (for command 'foo_bar') class 'foo_bar' in module + 'distutils.command.foo_bar'. The command class must provide a + 'user_options' attribute which is a list of option specifiers for + 'distutils.fancy_getopt'. Any command-line options between the current + and the next command are used to set attributes of the current command + object. + + When the entire command-line has been successfully parsed, calls the + 'run()' method on each command object in turn. This method will be + driven entirely by the Distribution object (which each command object + has a reference to, thanks to its constructor), and the + command-specific options that became attributes of each command + object. + """ + + global _setup_stop_after, _setup_distribution + + # Determine the distribution class -- either caller-supplied or + # our Distribution (see below). + klass = attrs.get('distclass') + if klass: + del attrs['distclass'] + else: + klass = Distribution + + if 'script_name' not in attrs: + attrs['script_name'] = os.path.basename(sys.argv[0]) + if 'script_args' not in attrs: + attrs['script_args'] = sys.argv[1:] + + # Create the Distribution instance, using the remaining arguments + # (ie. everything except distclass) to initialize it + try: + _setup_distribution = dist = klass(attrs) + except DistutilsSetupError as msg: + if 'name' not in attrs: + raise SystemExit("error in setup command: %s" % msg) + else: + raise SystemExit("error in %s setup command: %s" % \ + (attrs['name'], msg)) + + if _setup_stop_after == "init": + return dist + + # Find and parse the config file(s): they will override options from + # the setup script, but be overridden by the command line. + dist.parse_config_files() + + if DEBUG: + print("options (after parsing config files):") + dist.dump_option_dicts() + + if _setup_stop_after == "config": + return dist + + # Parse the command line and override config files; any + # command-line errors are the end user's fault, so turn them into + # SystemExit to suppress tracebacks. + try: + ok = dist.parse_command_line() + except DistutilsArgError as msg: + raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg) + + if DEBUG: + print("options (after parsing command line):") + dist.dump_option_dicts() + + if _setup_stop_after == "commandline": + return dist + + # And finally, run all the commands found on the command line. + if ok: + try: + dist.run_commands() + except KeyboardInterrupt: + raise SystemExit("interrupted") + except OSError as exc: + if DEBUG: + sys.stderr.write("error: %s\n" % (exc,)) + raise + else: + raise SystemExit("error: %s" % (exc,)) + + except (DistutilsError, + CCompilerError) as msg: + if DEBUG: + raise + else: + raise SystemExit("error: " + str(msg)) + + return dist + +# setup () + + +def run_setup (script_name, script_args=None, stop_after="run"): + """Run a setup script in a somewhat controlled environment, and + return the Distribution instance that drives things. This is useful + if you need to find out the distribution meta-data (passed as + keyword args from 'script' to 'setup()', or the contents of the + config files or command-line. + + 'script_name' is a file that will be read and run with 'exec()'; + 'sys.argv[0]' will be replaced with 'script' for the duration of the + call. 'script_args' is a list of strings; if supplied, + 'sys.argv[1:]' will be replaced by 'script_args' for the duration of + the call. + + 'stop_after' tells 'setup()' when to stop processing; possible + values: + init + stop after the Distribution instance has been created and + populated with the keyword arguments to 'setup()' + config + stop after config files have been parsed (and their data + stored in the Distribution instance) + commandline + stop after the command-line ('sys.argv[1:]' or 'script_args') + have been parsed (and the data stored in the Distribution) + run [default] + stop after all commands have been run (the same as if 'setup()' + had been called in the usual way + + Returns the Distribution instance, which provides all information + used to drive the Distutils. + """ + if stop_after not in ('init', 'config', 'commandline', 'run'): + raise ValueError("invalid value for 'stop_after': %r" % (stop_after,)) + + global _setup_stop_after, _setup_distribution + _setup_stop_after = stop_after + + save_argv = sys.argv.copy() + g = {'__file__': script_name} + try: + try: + sys.argv[0] = script_name + if script_args is not None: + sys.argv[1:] = script_args + with open(script_name, 'rb') as f: + exec(f.read(), g) + finally: + sys.argv = save_argv + _setup_stop_after = None + except SystemExit: + # Hmm, should we do something if exiting with a non-zero code + # (ie. error)? + pass + + if _setup_distribution is None: + raise RuntimeError(("'distutils.core.setup()' was never called -- " + "perhaps '%s' is not a Distutils setup script?") % \ + script_name) + + # I wonder if the setup script's namespace -- g and l -- would be of + # any interest to callers? + #print "_setup_distribution:", _setup_distribution + return _setup_distribution + +# run_setup () diff --git a/distutils/cygwinccompiler.py b/distutils/cygwinccompiler.py new file mode 100644 index 00000000..66c12dd3 --- /dev/null +++ b/distutils/cygwinccompiler.py @@ -0,0 +1,403 @@ +"""distutils.cygwinccompiler + +Provides the CygwinCCompiler class, a subclass of UnixCCompiler that +handles the Cygwin port of the GNU C compiler to Windows. It also contains +the Mingw32CCompiler class which handles the mingw32 port of GCC (same as +cygwin in no-cygwin mode). +""" + +# problems: +# +# * if you use a msvc compiled python version (1.5.2) +# 1. you have to insert a __GNUC__ section in its config.h +# 2. you have to generate an import library for its dll +# - create a def-file for python??.dll +# - create an import library using +# dlltool --dllname python15.dll --def python15.def \ +# --output-lib libpython15.a +# +# see also http://starship.python.net/crew/kernr/mingw32/Notes.html +# +# * We put export_symbols in a def-file, and don't use +# --export-all-symbols because it doesn't worked reliable in some +# tested configurations. And because other windows compilers also +# need their symbols specified this no serious problem. +# +# tested configurations: +# +# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works +# (after patching python's config.h and for C++ some other include files) +# see also http://starship.python.net/crew/kernr/mingw32/Notes.html +# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works +# (ld doesn't support -shared, so we use dllwrap) +# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now +# - its dllwrap doesn't work, there is a bug in binutils 2.10.90 +# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html +# - using gcc -mdll instead dllwrap doesn't work without -static because +# it tries to link against dlls instead their import libraries. (If +# it finds the dll first.) +# By specifying -static we force ld to link against the import libraries, +# this is windows standard and there are normally not the necessary symbols +# in the dlls. +# *** only the version of June 2000 shows these problems +# * cygwin gcc 3.2/ld 2.13.90 works +# (ld supports -shared) +# * mingw gcc 3.2/ld 2.13 works +# (ld supports -shared) + +import os +import sys +import copy +from subprocess import Popen, PIPE, check_output +import re + +from distutils.unixccompiler import UnixCCompiler +from distutils.file_util import write_file +from distutils.errors import (DistutilsExecError, CCompilerError, + CompileError, UnknownFileError) +from distutils.version import LooseVersion +from distutils.spawn import find_executable + +def get_msvcr(): + """Include the appropriate MSVC runtime library if Python was built + with MSVC 7.0 or later. + """ + msc_pos = sys.version.find('MSC v.') + if msc_pos != -1: + msc_ver = sys.version[msc_pos+6:msc_pos+10] + if msc_ver == '1300': + # MSVC 7.0 + return ['msvcr70'] + elif msc_ver == '1310': + # MSVC 7.1 + return ['msvcr71'] + elif msc_ver == '1400': + # VS2005 / MSVC 8.0 + return ['msvcr80'] + elif msc_ver == '1500': + # VS2008 / MSVC 9.0 + return ['msvcr90'] + elif msc_ver == '1600': + # VS2010 / MSVC 10.0 + return ['msvcr100'] + else: + raise ValueError("Unknown MS Compiler version %s " % msc_ver) + + +class CygwinCCompiler(UnixCCompiler): + """ Handles the Cygwin port of the GNU C compiler to Windows. + """ + compiler_type = 'cygwin' + obj_extension = ".o" + static_lib_extension = ".a" + shared_lib_extension = ".dll" + static_lib_format = "lib%s%s" + shared_lib_format = "%s%s" + exe_extension = ".exe" + + def __init__(self, verbose=0, dry_run=0, force=0): + + UnixCCompiler.__init__(self, verbose, dry_run, force) + + status, details = check_config_h() + self.debug_print("Python's GCC status: %s (details: %s)" % + (status, details)) + if status is not CONFIG_H_OK: + self.warn( + "Python's pyconfig.h doesn't seem to support your compiler. " + "Reason: %s. " + "Compiling may fail because of undefined preprocessor macros." + % details) + + self.gcc_version, self.ld_version, self.dllwrap_version = \ + get_versions() + self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % + (self.gcc_version, + self.ld_version, + self.dllwrap_version) ) + + # ld_version >= "2.10.90" and < "2.13" should also be able to use + # gcc -mdll instead of dllwrap + # Older dllwraps had own version numbers, newer ones use the + # same as the rest of binutils ( also ld ) + # dllwrap 2.10.90 is buggy + if self.ld_version >= "2.10.90": + self.linker_dll = "gcc" + else: + self.linker_dll = "dllwrap" + + # ld_version >= "2.13" support -shared so use it instead of + # -mdll -static + if self.ld_version >= "2.13": + shared_option = "-shared" + else: + shared_option = "-mdll -static" + + # Hard-code GCC because that's what this is all about. + # XXX optimization, warnings etc. should be customizable. + self.set_executables(compiler='gcc -mcygwin -O -Wall', + compiler_so='gcc -mcygwin -mdll -O -Wall', + compiler_cxx='g++ -mcygwin -O -Wall', + linker_exe='gcc -mcygwin', + linker_so=('%s -mcygwin %s' % + (self.linker_dll, shared_option))) + + # cygwin and mingw32 need different sets of libraries + if self.gcc_version == "2.91.57": + # cygwin shouldn't need msvcrt, but without the dlls will crash + # (gcc version 2.91.57) -- perhaps something about initialization + self.dll_libraries=["msvcrt"] + self.warn( + "Consider upgrading to a newer version of gcc") + else: + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or later. + self.dll_libraries = get_msvcr() + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + """Compiles the source by spawning GCC and windres if needed.""" + if ext == '.rc' or ext == '.res': + # gcc needs '.res' and '.rc' compiled to object files !!! + try: + self.spawn(["windres", "-i", src, "-o", obj]) + except DistutilsExecError as msg: + raise CompileError(msg) + else: # for other files use the C-compiler + try: + self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + def link(self, target_desc, objects, output_filename, output_dir=None, + libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None): + """Link the objects.""" + # use separate copies, so we can modify the lists + extra_preargs = copy.copy(extra_preargs or []) + libraries = copy.copy(libraries or []) + objects = copy.copy(objects or []) + + # Additional libraries + libraries.extend(self.dll_libraries) + + # handle export symbols by creating a def-file + # with executables this only works with gcc/ld as linker + if ((export_symbols is not None) and + (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): + # (The linker doesn't do anything if output is up-to-date. + # So it would probably better to check if we really need this, + # but for this we had to insert some unchanged parts of + # UnixCCompiler, and this is not what we want.) + + # we want to put some files in the same directory as the + # object files are, build_temp doesn't help much + # where are the object files + temp_dir = os.path.dirname(objects[0]) + # name of dll to give the helper files the same base name + (dll_name, dll_extension) = os.path.splitext( + os.path.basename(output_filename)) + + # generate the filenames for these files + def_file = os.path.join(temp_dir, dll_name + ".def") + lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") + + # Generate .def file + contents = [ + "LIBRARY %s" % os.path.basename(output_filename), + "EXPORTS"] + for sym in export_symbols: + contents.append(sym) + self.execute(write_file, (def_file, contents), + "writing %s" % def_file) + + # next add options for def-file and to creating import libraries + + # dllwrap uses different options than gcc/ld + if self.linker_dll == "dllwrap": + extra_preargs.extend(["--output-lib", lib_file]) + # for dllwrap we have to use a special option + extra_preargs.extend(["--def", def_file]) + # we use gcc/ld here and can be sure ld is >= 2.9.10 + else: + # doesn't work: bfd_close build\...\libfoo.a: Invalid operation + #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) + # for gcc/ld the def-file is specified as any object files + objects.append(def_file) + + #end: if ((export_symbols is not None) and + # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): + + # who wants symbols and a many times larger output file + # should explicitly switch the debug mode on + # otherwise we let dllwrap/ld strip the output file + # (On my machine: 10KiB < stripped_file < ??100KiB + # unstripped_file = stripped_file + XXX KiB + # ( XXX=254 for a typical python extension)) + if not debug: + extra_preargs.append("-s") + + UnixCCompiler.link(self, target_desc, objects, output_filename, + output_dir, libraries, library_dirs, + runtime_library_dirs, + None, # export_symbols, we do this in our def-file + debug, extra_preargs, extra_postargs, build_temp, + target_lang) + + # -- Miscellaneous methods ----------------------------------------- + + def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + """Adds supports for rc and res files.""" + if output_dir is None: + output_dir = '' + obj_names = [] + for src_name in source_filenames: + # use normcase to make sure '.rc' is really '.rc' and not '.RC' + base, ext = os.path.splitext(os.path.normcase(src_name)) + if ext not in (self.src_extensions + ['.rc','.res']): + raise UnknownFileError("unknown file type '%s' (from '%s')" % \ + (ext, src_name)) + if strip_dir: + base = os.path.basename (base) + if ext in ('.res', '.rc'): + # these need to be compiled to object files + obj_names.append (os.path.join(output_dir, + base + ext + self.obj_extension)) + else: + obj_names.append (os.path.join(output_dir, + base + self.obj_extension)) + return obj_names + +# the same as cygwin plus some additional parameters +class Mingw32CCompiler(CygwinCCompiler): + """ Handles the Mingw32 port of the GNU C compiler to Windows. + """ + compiler_type = 'mingw32' + + def __init__(self, verbose=0, dry_run=0, force=0): + + CygwinCCompiler.__init__ (self, verbose, dry_run, force) + + # ld_version >= "2.13" support -shared so use it instead of + # -mdll -static + if self.ld_version >= "2.13": + shared_option = "-shared" + else: + shared_option = "-mdll -static" + + # A real mingw32 doesn't need to specify a different entry point, + # but cygwin 2.91.57 in no-cygwin-mode needs it. + if self.gcc_version <= "2.91.57": + entry_point = '--entry _DllMain@12' + else: + entry_point = '' + + if is_cygwingcc(): + raise CCompilerError( + 'Cygwin gcc cannot be used with --compiler=mingw32') + + self.set_executables(compiler='gcc -O -Wall', + compiler_so='gcc -mdll -O -Wall', + compiler_cxx='g++ -O -Wall', + linker_exe='gcc', + linker_so='%s %s %s' + % (self.linker_dll, shared_option, + entry_point)) + # Maybe we should also append -mthreads, but then the finished + # dlls need another dll (mingwm10.dll see Mingw32 docs) + # (-mthreads: Support thread-safe exception handling on `Mingw32') + + # no additional libraries needed + self.dll_libraries=[] + + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or later. + self.dll_libraries = get_msvcr() + +# Because these compilers aren't configured in Python's pyconfig.h file by +# default, we should at least warn the user if he is using an unmodified +# version. + +CONFIG_H_OK = "ok" +CONFIG_H_NOTOK = "not ok" +CONFIG_H_UNCERTAIN = "uncertain" + +def check_config_h(): + """Check if the current Python installation appears amenable to building + extensions with GCC. + + Returns a tuple (status, details), where 'status' is one of the following + constants: + + - CONFIG_H_OK: all is well, go ahead and compile + - CONFIG_H_NOTOK: doesn't look good + - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h + + 'details' is a human-readable string explaining the situation. + + Note there are two ways to conclude "OK": either 'sys.version' contains + the string "GCC" (implying that this Python was built with GCC), or the + installed "pyconfig.h" contains the string "__GNUC__". + """ + + # XXX since this function also checks sys.version, it's not strictly a + # "pyconfig.h" check -- should probably be renamed... + + from distutils import sysconfig + + # if sys.version contains GCC then python was compiled with GCC, and the + # pyconfig.h file should be OK + if "GCC" in sys.version: + return CONFIG_H_OK, "sys.version mentions 'GCC'" + + # let's see if __GNUC__ is mentioned in python.h + fn = sysconfig.get_config_h_filename() + try: + config_h = open(fn) + try: + if "__GNUC__" in config_h.read(): + return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn + else: + return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn + finally: + config_h.close() + except OSError as exc: + return (CONFIG_H_UNCERTAIN, + "couldn't read '%s': %s" % (fn, exc.strerror)) + +RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)') + +def _find_exe_version(cmd): + """Find the version of an executable by running `cmd` in the shell. + + If the command is not found, or the output does not match + `RE_VERSION`, returns None. + """ + executable = cmd.split()[0] + if find_executable(executable) is None: + return None + out = Popen(cmd, shell=True, stdout=PIPE).stdout + try: + out_string = out.read() + finally: + out.close() + result = RE_VERSION.search(out_string) + if result is None: + return None + # LooseVersion works with strings + # so we need to decode our bytes + return LooseVersion(result.group(1).decode()) + +def get_versions(): + """ Try to find out the versions of gcc, ld and dllwrap. + + If not possible it returns None for it. + """ + commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] + return tuple([_find_exe_version(cmd) for cmd in commands]) + +def is_cygwingcc(): + '''Try to determine if the gcc that would be used is from cygwin.''' + out_string = check_output(['gcc', '-dumpmachine']) + return out_string.strip().endswith(b'cygwin') diff --git a/distutils/debug.py b/distutils/debug.py new file mode 100644 index 00000000..daf1660f --- /dev/null +++ b/distutils/debug.py @@ -0,0 +1,5 @@ +import os + +# If DISTUTILS_DEBUG is anything other than the empty string, we run in +# debug mode. +DEBUG = os.environ.get('DISTUTILS_DEBUG') diff --git a/distutils/dep_util.py b/distutils/dep_util.py new file mode 100644 index 00000000..d74f5e4e --- /dev/null +++ b/distutils/dep_util.py @@ -0,0 +1,92 @@ +"""distutils.dep_util + +Utility functions for simple, timestamp-based dependency of files +and groups of files; also, function based entirely on such +timestamp dependency analysis.""" + +import os +from distutils.errors import DistutilsFileError + + +def newer (source, target): + """Return true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. Return false if + both exist and 'target' is the same age or younger than 'source'. + Raise DistutilsFileError if 'source' does not exist. + """ + if not os.path.exists(source): + raise DistutilsFileError("file '%s' does not exist" % + os.path.abspath(source)) + if not os.path.exists(target): + return 1 + + from stat import ST_MTIME + mtime1 = os.stat(source)[ST_MTIME] + mtime2 = os.stat(target)[ST_MTIME] + + return mtime1 > mtime2 + +# newer () + + +def newer_pairwise (sources, targets): + """Walk two filename lists in parallel, testing if each source is newer + than its corresponding target. Return a pair of lists (sources, + targets) where source is newer than target, according to the semantics + of 'newer()'. + """ + if len(sources) != len(targets): + raise ValueError("'sources' and 'targets' must be same length") + + # build a pair of lists (sources, targets) where source is newer + n_sources = [] + n_targets = [] + for i in range(len(sources)): + if newer(sources[i], targets[i]): + n_sources.append(sources[i]) + n_targets.append(targets[i]) + + return (n_sources, n_targets) + +# newer_pairwise () + + +def newer_group (sources, target, missing='error'): + """Return true if 'target' is out-of-date with respect to any file + listed in 'sources'. In other words, if 'target' exists and is newer + than every file in 'sources', return false; otherwise return true. + 'missing' controls what we do when a source file is missing; the + default ("error") is to blow up with an OSError from inside 'stat()'; + if it is "ignore", we silently drop any missing source files; if it is + "newer", any missing source files make us assume that 'target' is + out-of-date (this is handy in "dry-run" mode: it'll make you pretend to + carry out commands that wouldn't work because inputs are missing, but + that doesn't matter because you're not actually going to run the + commands). + """ + # If the target doesn't even exist, then it's definitely out-of-date. + if not os.path.exists(target): + return 1 + + # Otherwise we have to find out the hard way: if *any* source file + # is more recent than 'target', then 'target' is out-of-date and + # we can immediately return true. If we fall through to the end + # of the loop, then 'target' is up-to-date and we return false. + from stat import ST_MTIME + target_mtime = os.stat(target)[ST_MTIME] + for source in sources: + if not os.path.exists(source): + if missing == 'error': # blow up when we stat() the file + pass + elif missing == 'ignore': # missing source dropped from + continue # target's dependency list + elif missing == 'newer': # missing source means target is + return 1 # out-of-date + + source_mtime = os.stat(source)[ST_MTIME] + if source_mtime > target_mtime: + return 1 + else: + return 0 + +# newer_group () diff --git a/distutils/dir_util.py b/distutils/dir_util.py new file mode 100644 index 00000000..d5cd8e3e --- /dev/null +++ b/distutils/dir_util.py @@ -0,0 +1,210 @@ +"""distutils.dir_util + +Utility functions for manipulating directories and directory trees.""" + +import os +import errno +from distutils.errors import DistutilsFileError, DistutilsInternalError +from distutils import log + +# cache for by mkpath() -- in addition to cheapening redundant calls, +# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode +_path_created = {} + +# I don't use os.makedirs because a) it's new to Python 1.5.2, and +# b) it blows up if the directory already exists (I want to silently +# succeed in that case). +def mkpath(name, mode=0o777, verbose=1, dry_run=0): + """Create a directory and any missing ancestor directories. + + If the directory already exists (or if 'name' is the empty string, which + means the current directory, which of course exists), then do nothing. + Raise DistutilsFileError if unable to create some directory along the way + (eg. some sub-path exists, but is a file rather than a directory). + If 'verbose' is true, print a one-line summary of each mkdir to stdout. + Return the list of directories actually created. + """ + + global _path_created + + # Detect a common bug -- name is None + if not isinstance(name, str): + raise DistutilsInternalError( + "mkpath: 'name' must be a string (got %r)" % (name,)) + + # XXX what's the better way to handle verbosity? print as we create + # each directory in the path (the current behaviour), or only announce + # the creation of the whole path? (quite easy to do the latter since + # we're not using a recursive algorithm) + + name = os.path.normpath(name) + created_dirs = [] + if os.path.isdir(name) or name == '': + return created_dirs + if _path_created.get(os.path.abspath(name)): + return created_dirs + + (head, tail) = os.path.split(name) + tails = [tail] # stack of lone dirs to create + + while head and tail and not os.path.isdir(head): + (head, tail) = os.path.split(head) + tails.insert(0, tail) # push next higher dir onto stack + + # now 'head' contains the deepest directory that already exists + # (that is, the child of 'head' in 'name' is the highest directory + # that does *not* exist) + for d in tails: + #print "head = %s, d = %s: " % (head, d), + head = os.path.join(head, d) + abs_head = os.path.abspath(head) + + if _path_created.get(abs_head): + continue + + if verbose >= 1: + log.info("creating %s", head) + + if not dry_run: + try: + os.mkdir(head, mode) + except OSError as exc: + if not (exc.errno == errno.EEXIST and os.path.isdir(head)): + raise DistutilsFileError( + "could not create '%s': %s" % (head, exc.args[-1])) + created_dirs.append(head) + + _path_created[abs_head] = 1 + return created_dirs + +def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0): + """Create all the empty directories under 'base_dir' needed to put 'files' + there. + + 'base_dir' is just the name of a directory which doesn't necessarily + exist yet; 'files' is a list of filenames to be interpreted relative to + 'base_dir'. 'base_dir' + the directory portion of every file in 'files' + will be created if it doesn't already exist. 'mode', 'verbose' and + 'dry_run' flags are as for 'mkpath()'. + """ + # First get the list of directories to create + need_dir = set() + for file in files: + need_dir.add(os.path.join(base_dir, os.path.dirname(file))) + + # Now create them + for dir in sorted(need_dir): + mkpath(dir, mode, verbose=verbose, dry_run=dry_run) + +def copy_tree(src, dst, preserve_mode=1, preserve_times=1, + preserve_symlinks=0, update=0, verbose=1, dry_run=0): + """Copy an entire directory tree 'src' to a new location 'dst'. + + Both 'src' and 'dst' must be directory names. If 'src' is not a + directory, raise DistutilsFileError. If 'dst' does not exist, it is + created with 'mkpath()'. The end result of the copy is that every + file in 'src' is copied to 'dst', and directories under 'src' are + recursively copied to 'dst'. Return the list of files that were + copied or might have been copied, using their output name. The + return value is unaffected by 'update' or 'dry_run': it is simply + the list of all files under 'src', with the names changed to be + under 'dst'. + + 'preserve_mode' and 'preserve_times' are the same as for + 'copy_file'; note that they only apply to regular files, not to + directories. If 'preserve_symlinks' is true, symlinks will be + copied as symlinks (on platforms that support them!); otherwise + (the default), the destination of the symlink will be copied. + 'update' and 'verbose' are the same as for 'copy_file'. + """ + from distutils.file_util import copy_file + + if not dry_run and not os.path.isdir(src): + raise DistutilsFileError( + "cannot copy tree '%s': not a directory" % src) + try: + names = os.listdir(src) + except OSError as e: + if dry_run: + names = [] + else: + raise DistutilsFileError( + "error listing files in '%s': %s" % (src, e.strerror)) + + if not dry_run: + mkpath(dst, verbose=verbose) + + outputs = [] + + for n in names: + src_name = os.path.join(src, n) + dst_name = os.path.join(dst, n) + + if n.startswith('.nfs'): + # skip NFS rename files + continue + + if preserve_symlinks and os.path.islink(src_name): + link_dest = os.readlink(src_name) + if verbose >= 1: + log.info("linking %s -> %s", dst_name, link_dest) + if not dry_run: + os.symlink(link_dest, dst_name) + outputs.append(dst_name) + + elif os.path.isdir(src_name): + outputs.extend( + copy_tree(src_name, dst_name, preserve_mode, + preserve_times, preserve_symlinks, update, + verbose=verbose, dry_run=dry_run)) + else: + copy_file(src_name, dst_name, preserve_mode, + preserve_times, update, verbose=verbose, + dry_run=dry_run) + outputs.append(dst_name) + + return outputs + +def _build_cmdtuple(path, cmdtuples): + """Helper for remove_tree().""" + for f in os.listdir(path): + real_f = os.path.join(path,f) + if os.path.isdir(real_f) and not os.path.islink(real_f): + _build_cmdtuple(real_f, cmdtuples) + else: + cmdtuples.append((os.remove, real_f)) + cmdtuples.append((os.rmdir, path)) + +def remove_tree(directory, verbose=1, dry_run=0): + """Recursively remove an entire directory tree. + + Any errors are ignored (apart from being reported to stdout if 'verbose' + is true). + """ + global _path_created + + if verbose >= 1: + log.info("removing '%s' (and everything under it)", directory) + if dry_run: + return + cmdtuples = [] + _build_cmdtuple(directory, cmdtuples) + for cmd in cmdtuples: + try: + cmd[0](cmd[1]) + # remove dir from cache if it's already there + abspath = os.path.abspath(cmd[1]) + if abspath in _path_created: + del _path_created[abspath] + except OSError as exc: + log.warn("error removing %s: %s", directory, exc) + +def ensure_relative(path): + """Take the full path 'path', and make it a relative path. + + This is useful to make 'path' the second argument to os.path.join(). + """ + drive, path = os.path.splitdrive(path) + if path[0:1] == os.sep: + path = drive + path[1:] + return path diff --git a/distutils/dist.py b/distutils/dist.py new file mode 100644 index 00000000..6cf0a0d6 --- /dev/null +++ b/distutils/dist.py @@ -0,0 +1,1256 @@ +"""distutils.dist + +Provides the Distribution class, which represents the module distribution +being built/installed/distributed. +""" + +import sys +import os +import re +from email import message_from_file + +try: + import warnings +except ImportError: + warnings = None + +from distutils.errors import * +from distutils.fancy_getopt import FancyGetopt, translate_longopt +from distutils.util import check_environ, strtobool, rfc822_escape +from distutils import log +from distutils.debug import DEBUG + +# Regex to define acceptable Distutils command names. This is not *quite* +# the same as a Python NAME -- I don't allow leading underscores. The fact +# that they're very similar is no coincidence; the default naming scheme is +# to look for a Python module named after the command. +command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') + + +def _ensure_list(value, fieldname): + if isinstance(value, str): + # a string containing comma separated values is okay. It will + # be converted to a list by Distribution.finalize_options(). + pass + elif not isinstance(value, list): + # passing a tuple or an iterator perhaps, warn and convert + typename = type(value).__name__ + msg = f"Warning: '{fieldname}' should be a list, got type '{typename}'" + log.log(log.WARN, msg) + value = list(value) + return value + + +class Distribution: + """The core of the Distutils. Most of the work hiding behind 'setup' + is really done within a Distribution instance, which farms the work out + to the Distutils commands specified on the command line. + + Setup scripts will almost never instantiate Distribution directly, + unless the 'setup()' function is totally inadequate to their needs. + However, it is conceivable that a setup script might wish to subclass + Distribution for some specialized purpose, and then pass the subclass + to 'setup()' as the 'distclass' keyword argument. If so, it is + necessary to respect the expectations that 'setup' has of Distribution. + See the code for 'setup()', in core.py, for details. + """ + + # 'global_options' describes the command-line options that may be + # supplied to the setup script prior to any actual commands. + # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of + # these global options. This list should be kept to a bare minimum, + # since every global option is also valid as a command option -- and we + # don't want to pollute the commands with too many options that they + # have minimal control over. + # The fourth entry for verbose means that it can be repeated. + global_options = [ + ('verbose', 'v', "run verbosely (default)", 1), + ('quiet', 'q', "run quietly (turns verbosity off)"), + ('dry-run', 'n', "don't actually do anything"), + ('help', 'h', "show detailed help message"), + ('no-user-cfg', None, + 'ignore pydistutils.cfg in your home directory'), + ] + + # 'common_usage' is a short (2-3 line) string describing the common + # usage of the setup script. + common_usage = """\ +Common commands: (see '--help-commands' for more) + + setup.py build will build the package underneath 'build/' + setup.py install will install the package +""" + + # options that are not propagated to the commands + display_options = [ + ('help-commands', None, + "list all available commands"), + ('name', None, + "print package name"), + ('version', 'V', + "print package version"), + ('fullname', None, + "print -"), + ('author', None, + "print the author's name"), + ('author-email', None, + "print the author's email address"), + ('maintainer', None, + "print the maintainer's name"), + ('maintainer-email', None, + "print the maintainer's email address"), + ('contact', None, + "print the maintainer's name if known, else the author's"), + ('contact-email', None, + "print the maintainer's email address if known, else the author's"), + ('url', None, + "print the URL for this package"), + ('license', None, + "print the license of the package"), + ('licence', None, + "alias for --license"), + ('description', None, + "print the package description"), + ('long-description', None, + "print the long package description"), + ('platforms', None, + "print the list of platforms"), + ('classifiers', None, + "print the list of classifiers"), + ('keywords', None, + "print the list of keywords"), + ('provides', None, + "print the list of packages/modules provided"), + ('requires', None, + "print the list of packages/modules required"), + ('obsoletes', None, + "print the list of packages/modules made obsolete") + ] + display_option_names = [translate_longopt(x[0]) for x in display_options] + + # negative options are options that exclude other options + negative_opt = {'quiet': 'verbose'} + + # -- Creation/initialization methods ------------------------------- + + def __init__(self, attrs=None): + """Construct a new Distribution instance: initialize all the + attributes of a Distribution, and then use 'attrs' (a dictionary + mapping attribute names to values) to assign some of those + attributes their "real" values. (Any attributes not mentioned in + 'attrs' will be assigned to some null value: 0, None, an empty list + or dictionary, etc.) Most importantly, initialize the + 'command_obj' attribute to the empty dictionary; this will be + filled in with real command objects by 'parse_command_line()'. + """ + + # Default values for our command-line options + self.verbose = 1 + self.dry_run = 0 + self.help = 0 + for attr in self.display_option_names: + setattr(self, attr, 0) + + # Store the distribution meta-data (name, version, author, and so + # forth) in a separate object -- we're getting to have enough + # information here (and enough command-line options) that it's + # worth it. Also delegate 'get_XXX()' methods to the 'metadata' + # object in a sneaky and underhanded (but efficient!) way. + self.metadata = DistributionMetadata() + for basename in self.metadata._METHOD_BASENAMES: + method_name = "get_" + basename + setattr(self, method_name, getattr(self.metadata, method_name)) + + # 'cmdclass' maps command names to class objects, so we + # can 1) quickly figure out which class to instantiate when + # we need to create a new command object, and 2) have a way + # for the setup script to override command classes + self.cmdclass = {} + + # 'command_packages' is a list of packages in which commands + # are searched for. The factory for command 'foo' is expected + # to be named 'foo' in the module 'foo' in one of the packages + # named here. This list is searched from the left; an error + # is raised if no named package provides the command being + # searched for. (Always access using get_command_packages().) + self.command_packages = None + + # 'script_name' and 'script_args' are usually set to sys.argv[0] + # and sys.argv[1:], but they can be overridden when the caller is + # not necessarily a setup script run from the command-line. + self.script_name = None + self.script_args = None + + # 'command_options' is where we store command options between + # parsing them (from config files, the command-line, etc.) and when + # they are actually needed -- ie. when the command in question is + # instantiated. It is a dictionary of dictionaries of 2-tuples: + # command_options = { command_name : { option : (source, value) } } + self.command_options = {} + + # 'dist_files' is the list of (command, pyversion, file) that + # have been created by any dist commands run so far. This is + # filled regardless of whether the run is dry or not. pyversion + # gives sysconfig.get_python_version() if the dist file is + # specific to a Python version, 'any' if it is good for all + # Python versions on the target platform, and '' for a source + # file. pyversion should not be used to specify minimum or + # maximum required Python versions; use the metainfo for that + # instead. + self.dist_files = [] + + # These options are really the business of various commands, rather + # than of the Distribution itself. We provide aliases for them in + # Distribution as a convenience to the developer. + self.packages = None + self.package_data = {} + self.package_dir = None + self.py_modules = None + self.libraries = None + self.headers = None + self.ext_modules = None + self.ext_package = None + self.include_dirs = None + self.extra_path = None + self.scripts = None + self.data_files = None + self.password = '' + + # And now initialize bookkeeping stuff that can't be supplied by + # the caller at all. 'command_obj' maps command names to + # Command instances -- that's how we enforce that every command + # class is a singleton. + self.command_obj = {} + + # 'have_run' maps command names to boolean values; it keeps track + # of whether we have actually run a particular command, to make it + # cheap to "run" a command whenever we think we might need to -- if + # it's already been done, no need for expensive filesystem + # operations, we just check the 'have_run' dictionary and carry on. + # It's only safe to query 'have_run' for a command class that has + # been instantiated -- a false value will be inserted when the + # command object is created, and replaced with a true value when + # the command is successfully run. Thus it's probably best to use + # '.get()' rather than a straight lookup. + self.have_run = {} + + # Now we'll use the attrs dictionary (ultimately, keyword args from + # the setup script) to possibly override any or all of these + # distribution options. + + if attrs: + # Pull out the set of command options and work on them + # specifically. Note that this order guarantees that aliased + # command options will override any supplied redundantly + # through the general options dictionary. + options = attrs.get('options') + if options is not None: + del attrs['options'] + for (command, cmd_options) in options.items(): + opt_dict = self.get_option_dict(command) + for (opt, val) in cmd_options.items(): + opt_dict[opt] = ("setup script", val) + + if 'licence' in attrs: + attrs['license'] = attrs['licence'] + del attrs['licence'] + msg = "'licence' distribution option is deprecated; use 'license'" + if warnings is not None: + warnings.warn(msg) + else: + sys.stderr.write(msg + "\n") + + # Now work on the rest of the attributes. Any attribute that's + # not already defined is invalid! + for (key, val) in attrs.items(): + if hasattr(self.metadata, "set_" + key): + getattr(self.metadata, "set_" + key)(val) + elif hasattr(self.metadata, key): + setattr(self.metadata, key, val) + elif hasattr(self, key): + setattr(self, key, val) + else: + msg = "Unknown distribution option: %s" % repr(key) + warnings.warn(msg) + + # no-user-cfg is handled before other command line args + # because other args override the config files, and this + # one is needed before we can load the config files. + # If attrs['script_args'] wasn't passed, assume false. + # + # This also make sure we just look at the global options + self.want_user_cfg = True + + if self.script_args is not None: + for arg in self.script_args: + if not arg.startswith('-'): + break + if arg == '--no-user-cfg': + self.want_user_cfg = False + break + + self.finalize_options() + + def get_option_dict(self, command): + """Get the option dictionary for a given command. If that + command's option dictionary hasn't been created yet, then create it + and return the new dictionary; otherwise, return the existing + option dictionary. + """ + dict = self.command_options.get(command) + if dict is None: + dict = self.command_options[command] = {} + return dict + + def dump_option_dicts(self, header=None, commands=None, indent=""): + from pprint import pformat + + if commands is None: # dump all command option dicts + commands = sorted(self.command_options.keys()) + + if header is not None: + self.announce(indent + header) + indent = indent + " " + + if not commands: + self.announce(indent + "no commands known yet") + return + + for cmd_name in commands: + opt_dict = self.command_options.get(cmd_name) + if opt_dict is None: + self.announce(indent + + "no option dict for '%s' command" % cmd_name) + else: + self.announce(indent + + "option dict for '%s' command:" % cmd_name) + out = pformat(opt_dict) + for line in out.split('\n'): + self.announce(indent + " " + line) + + # -- Config file finding/parsing methods --------------------------- + + def find_config_files(self): + """Find as many configuration files as should be processed for this + platform, and return a list of filenames in the order in which they + should be parsed. The filenames returned are guaranteed to exist + (modulo nasty race conditions). + + There are three possible config files: distutils.cfg in the + Distutils installation directory (ie. where the top-level + Distutils __inst__.py file lives), a file in the user's home + directory named .pydistutils.cfg on Unix and pydistutils.cfg + on Windows/Mac; and setup.cfg in the current directory. + + The file in the user's home directory can be disabled with the + --no-user-cfg option. + """ + files = [] + check_environ() + + # Where to look for the system-wide Distutils config file + sys_dir = os.path.dirname(sys.modules['distutils'].__file__) + + # Look for the system config file + sys_file = os.path.join(sys_dir, "distutils.cfg") + if os.path.isfile(sys_file): + files.append(sys_file) + + # What to call the per-user config file + if os.name == 'posix': + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + + # And look for the user config file + if self.want_user_cfg: + user_file = os.path.join(os.path.expanduser('~'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) + + # All platforms support local setup.cfg + local_file = "setup.cfg" + if os.path.isfile(local_file): + files.append(local_file) + + if DEBUG: + self.announce("using config files: %s" % ', '.join(files)) + + return files + + def parse_config_files(self, filenames=None): + from configparser import ConfigParser + + # Ignore install directory options if we have a venv + if sys.prefix != sys.base_prefix: + ignore_options = [ + 'install-base', 'install-platbase', 'install-lib', + 'install-platlib', 'install-purelib', 'install-headers', + 'install-scripts', 'install-data', 'prefix', 'exec-prefix', + 'home', 'user', 'root'] + else: + ignore_options = [] + + ignore_options = frozenset(ignore_options) + + if filenames is None: + filenames = self.find_config_files() + + if DEBUG: + self.announce("Distribution.parse_config_files():") + + parser = ConfigParser() + for filename in filenames: + if DEBUG: + self.announce(" reading %s" % filename) + parser.read(filename) + for section in parser.sections(): + options = parser.options(section) + opt_dict = self.get_option_dict(section) + + for opt in options: + if opt != '__name__' and opt not in ignore_options: + val = parser.get(section,opt) + opt = opt.replace('-', '_') + opt_dict[opt] = (filename, val) + + # Make the ConfigParser forget everything (so we retain + # the original filenames that options come from) + parser.__init__() + + # If there was a "global" section in the config file, use it + # to set Distribution options. + + if 'global' in self.command_options: + for (opt, (src, val)) in self.command_options['global'].items(): + alias = self.negative_opt.get(opt) + try: + if alias: + setattr(self, alias, not strtobool(val)) + elif opt in ('verbose', 'dry_run'): # ugh! + setattr(self, opt, strtobool(val)) + else: + setattr(self, opt, val) + except ValueError as msg: + raise DistutilsOptionError(msg) + + # -- Command-line parsing methods ---------------------------------- + + def parse_command_line(self): + """Parse the setup script's command line, taken from the + 'script_args' instance attribute (which defaults to 'sys.argv[1:]' + -- see 'setup()' in core.py). This list is first processed for + "global options" -- options that set attributes of the Distribution + instance. Then, it is alternately scanned for Distutils commands + and options for that command. Each new command terminates the + options for the previous command. The allowed options for a + command are determined by the 'user_options' attribute of the + command class -- thus, we have to be able to load command classes + in order to parse the command line. Any error in that 'options' + attribute raises DistutilsGetoptError; any error on the + command-line raises DistutilsArgError. If no Distutils commands + were found on the command line, raises DistutilsArgError. Return + true if command-line was successfully parsed and we should carry + on with executing commands; false if no errors but we shouldn't + execute commands (currently, this only happens if user asks for + help). + """ + # + # We now have enough information to show the Macintosh dialog + # that allows the user to interactively specify the "command line". + # + toplevel_options = self._get_toplevel_options() + + # We have to parse the command line a bit at a time -- global + # options, then the first command, then its options, and so on -- + # because each command will be handled by a different class, and + # the options that are valid for a particular class aren't known + # until we have loaded the command class, which doesn't happen + # until we know what the command is. + + self.commands = [] + parser = FancyGetopt(toplevel_options + self.display_options) + parser.set_negative_aliases(self.negative_opt) + parser.set_aliases({'licence': 'license'}) + args = parser.getopt(args=self.script_args, object=self) + option_order = parser.get_option_order() + log.set_verbosity(self.verbose) + + # for display options we return immediately + if self.handle_display_options(option_order): + return + while args: + args = self._parse_command_opts(parser, args) + if args is None: # user asked for help (and got it) + return + + # Handle the cases of --help as a "global" option, ie. + # "setup.py --help" and "setup.py --help command ...". For the + # former, we show global options (--verbose, --dry-run, etc.) + # and display-only options (--name, --version, etc.); for the + # latter, we omit the display-only options and show help for + # each command listed on the command line. + if self.help: + self._show_help(parser, + display_options=len(self.commands) == 0, + commands=self.commands) + return + + # Oops, no commands found -- an end-user error + if not self.commands: + raise DistutilsArgError("no commands supplied") + + # All is well: return true + return True + + def _get_toplevel_options(self): + """Return the non-display options recognized at the top level. + + This includes options that are recognized *only* at the top + level as well as options recognized for commands. + """ + return self.global_options + [ + ("command-packages=", None, + "list of packages that provide distutils commands"), + ] + + def _parse_command_opts(self, parser, args): + """Parse the command-line options for a single command. + 'parser' must be a FancyGetopt instance; 'args' must be the list + of arguments, starting with the current command (whose options + we are about to parse). Returns a new version of 'args' with + the next command at the front of the list; will be the empty + list if there are no more commands on the command line. Returns + None if the user asked for help on this command. + """ + # late import because of mutual dependence between these modules + from distutils.cmd import Command + + # Pull the current command from the head of the command line + command = args[0] + if not command_re.match(command): + raise SystemExit("invalid command name '%s'" % command) + self.commands.append(command) + + # Dig up the command class that implements this command, so we + # 1) know that it's a valid command, and 2) know which options + # it takes. + try: + cmd_class = self.get_command_class(command) + except DistutilsModuleError as msg: + raise DistutilsArgError(msg) + + # Require that the command class be derived from Command -- want + # to be sure that the basic "command" interface is implemented. + if not issubclass(cmd_class, Command): + raise DistutilsClassError( + "command class %s must subclass Command" % cmd_class) + + # Also make sure that the command object provides a list of its + # known options. + if not (hasattr(cmd_class, 'user_options') and + isinstance(cmd_class.user_options, list)): + msg = ("command class %s must provide " + "'user_options' attribute (a list of tuples)") + raise DistutilsClassError(msg % cmd_class) + + # If the command class has a list of negative alias options, + # merge it in with the global negative aliases. + negative_opt = self.negative_opt + if hasattr(cmd_class, 'negative_opt'): + negative_opt = negative_opt.copy() + negative_opt.update(cmd_class.negative_opt) + + # Check for help_options in command class. They have a different + # format (tuple of four) so we need to preprocess them here. + if (hasattr(cmd_class, 'help_options') and + isinstance(cmd_class.help_options, list)): + help_options = fix_help_options(cmd_class.help_options) + else: + help_options = [] + + # All commands support the global options too, just by adding + # in 'global_options'. + parser.set_option_table(self.global_options + + cmd_class.user_options + + help_options) + parser.set_negative_aliases(negative_opt) + (args, opts) = parser.getopt(args[1:]) + if hasattr(opts, 'help') and opts.help: + self._show_help(parser, display_options=0, commands=[cmd_class]) + return + + if (hasattr(cmd_class, 'help_options') and + isinstance(cmd_class.help_options, list)): + help_option_found=0 + for (help_option, short, desc, func) in cmd_class.help_options: + if hasattr(opts, parser.get_attr_name(help_option)): + help_option_found=1 + if callable(func): + func() + else: + raise DistutilsClassError( + "invalid help function %r for help option '%s': " + "must be a callable object (function, etc.)" + % (func, help_option)) + + if help_option_found: + return + + # Put the options from the command-line into their official + # holding pen, the 'command_options' dictionary. + opt_dict = self.get_option_dict(command) + for (name, value) in vars(opts).items(): + opt_dict[name] = ("command line", value) + + return args + + def finalize_options(self): + """Set final values for all the options on the Distribution + instance, analogous to the .finalize_options() method of Command + objects. + """ + for attr in ('keywords', 'platforms'): + value = getattr(self.metadata, attr) + if value is None: + continue + if isinstance(value, str): + value = [elm.strip() for elm in value.split(',')] + setattr(self.metadata, attr, value) + + def _show_help(self, parser, global_options=1, display_options=1, + commands=[]): + """Show help for the setup script command-line in the form of + several lists of command-line options. 'parser' should be a + FancyGetopt instance; do not expect it to be returned in the + same state, as its option table will be reset to make it + generate the correct help text. + + If 'global_options' is true, lists the global options: + --verbose, --dry-run, etc. If 'display_options' is true, lists + the "display-only" options: --name, --version, etc. Finally, + lists per-command help for every command name or command class + in 'commands'. + """ + # late import because of mutual dependence between these modules + from distutils.core import gen_usage + from distutils.cmd import Command + + if global_options: + if display_options: + options = self._get_toplevel_options() + else: + options = self.global_options + parser.set_option_table(options) + parser.print_help(self.common_usage + "\nGlobal options:") + print('') + + if display_options: + parser.set_option_table(self.display_options) + parser.print_help( + "Information display options (just display " + + "information, ignore any commands)") + print('') + + for command in self.commands: + if isinstance(command, type) and issubclass(command, Command): + klass = command + else: + klass = self.get_command_class(command) + if (hasattr(klass, 'help_options') and + isinstance(klass.help_options, list)): + parser.set_option_table(klass.user_options + + fix_help_options(klass.help_options)) + else: + parser.set_option_table(klass.user_options) + parser.print_help("Options for '%s' command:" % klass.__name__) + print('') + + print(gen_usage(self.script_name)) + + def handle_display_options(self, option_order): + """If there were any non-global "display-only" options + (--help-commands or the metadata display options) on the command + line, display the requested info and return true; else return + false. + """ + from distutils.core import gen_usage + + # User just wants a list of commands -- we'll print it out and stop + # processing now (ie. if they ran "setup --help-commands foo bar", + # we ignore "foo bar"). + if self.help_commands: + self.print_commands() + print('') + print(gen_usage(self.script_name)) + return 1 + + # If user supplied any of the "display metadata" options, then + # display that metadata in the order in which the user supplied the + # metadata options. + any_display_options = 0 + is_display_option = {} + for option in self.display_options: + is_display_option[option[0]] = 1 + + for (opt, val) in option_order: + if val and is_display_option.get(opt): + opt = translate_longopt(opt) + value = getattr(self.metadata, "get_"+opt)() + if opt in ['keywords', 'platforms']: + print(','.join(value)) + elif opt in ('classifiers', 'provides', 'requires', + 'obsoletes'): + print('\n'.join(value)) + else: + print(value) + any_display_options = 1 + + return any_display_options + + def print_command_list(self, commands, header, max_length): + """Print a subset of the list of all commands -- used by + 'print_commands()'. + """ + print(header + ":") + + for cmd in commands: + klass = self.cmdclass.get(cmd) + if not klass: + klass = self.get_command_class(cmd) + try: + description = klass.description + except AttributeError: + description = "(no description available)" + + print(" %-*s %s" % (max_length, cmd, description)) + + def print_commands(self): + """Print out a help message listing all available commands with a + description of each. The list is divided into "standard commands" + (listed in distutils.command.__all__) and "extra commands" + (mentioned in self.cmdclass, but not a standard command). The + descriptions come from the command class attribute + 'description'. + """ + import distutils.command + std_commands = distutils.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass.keys(): + if not is_std.get(cmd): + extra_commands.append(cmd) + + max_length = 0 + for cmd in (std_commands + extra_commands): + if len(cmd) > max_length: + max_length = len(cmd) + + self.print_command_list(std_commands, + "Standard commands", + max_length) + if extra_commands: + print() + self.print_command_list(extra_commands, + "Extra commands", + max_length) + + def get_command_list(self): + """Get a list of (command, description) tuples. + The list is divided into "standard commands" (listed in + distutils.command.__all__) and "extra commands" (mentioned in + self.cmdclass, but not a standard command). The descriptions come + from the command class attribute 'description'. + """ + # Currently this is only used on Mac OS, for the Mac-only GUI + # Distutils interface (by Jack Jansen) + import distutils.command + std_commands = distutils.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass.keys(): + if not is_std.get(cmd): + extra_commands.append(cmd) + + rv = [] + for cmd in (std_commands + extra_commands): + klass = self.cmdclass.get(cmd) + if not klass: + klass = self.get_command_class(cmd) + try: + description = klass.description + except AttributeError: + description = "(no description available)" + rv.append((cmd, description)) + return rv + + # -- Command class/object methods ---------------------------------- + + def get_command_packages(self): + """Return a list of packages from which commands are loaded.""" + pkgs = self.command_packages + if not isinstance(pkgs, list): + if pkgs is None: + pkgs = '' + pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != ''] + if "distutils.command" not in pkgs: + pkgs.insert(0, "distutils.command") + self.command_packages = pkgs + return pkgs + + def get_command_class(self, command): + """Return the class that implements the Distutils command named by + 'command'. First we check the 'cmdclass' dictionary; if the + command is mentioned there, we fetch the class object from the + dictionary and return it. Otherwise we load the command module + ("distutils.command." + command) and fetch the command class from + the module. The loaded class is also stored in 'cmdclass' + to speed future calls to 'get_command_class()'. + + Raises DistutilsModuleError if the expected module could not be + found, or if that module does not define the expected class. + """ + klass = self.cmdclass.get(command) + if klass: + return klass + + for pkgname in self.get_command_packages(): + module_name = "%s.%s" % (pkgname, command) + klass_name = command + + try: + __import__(module_name) + module = sys.modules[module_name] + except ImportError: + continue + + try: + klass = getattr(module, klass_name) + except AttributeError: + raise DistutilsModuleError( + "invalid command '%s' (no class '%s' in module '%s')" + % (command, klass_name, module_name)) + + self.cmdclass[command] = klass + return klass + + raise DistutilsModuleError("invalid command '%s'" % command) + + def get_command_obj(self, command, create=1): + """Return the command object for 'command'. Normally this object + is cached on a previous call to 'get_command_obj()'; if no command + object for 'command' is in the cache, then we either create and + return it (if 'create' is true) or return None. + """ + cmd_obj = self.command_obj.get(command) + if not cmd_obj and create: + if DEBUG: + self.announce("Distribution.get_command_obj(): " + "creating '%s' command object" % command) + + klass = self.get_command_class(command) + cmd_obj = self.command_obj[command] = klass(self) + self.have_run[command] = 0 + + # Set any options that were supplied in config files + # or on the command line. (NB. support for error + # reporting is lame here: any errors aren't reported + # until 'finalize_options()' is called, which means + # we won't report the source of the error.) + options = self.command_options.get(command) + if options: + self._set_command_options(cmd_obj, options) + + return cmd_obj + + def _set_command_options(self, command_obj, option_dict=None): + """Set the options for 'command_obj' from 'option_dict'. Basically + this means copying elements of a dictionary ('option_dict') to + attributes of an instance ('command'). + + 'command_obj' must be a Command instance. If 'option_dict' is not + supplied, uses the standard option dictionary for this command + (from 'self.command_options'). + """ + command_name = command_obj.get_command_name() + if option_dict is None: + option_dict = self.get_option_dict(command_name) + + if DEBUG: + self.announce(" setting options for '%s' command:" % command_name) + for (option, (source, value)) in option_dict.items(): + if DEBUG: + self.announce(" %s = %s (from %s)" % (option, value, + source)) + try: + bool_opts = [translate_longopt(o) + for o in command_obj.boolean_options] + except AttributeError: + bool_opts = [] + try: + neg_opt = command_obj.negative_opt + except AttributeError: + neg_opt = {} + + try: + is_string = isinstance(value, str) + if option in neg_opt and is_string: + setattr(command_obj, neg_opt[option], not strtobool(value)) + elif option in bool_opts and is_string: + setattr(command_obj, option, strtobool(value)) + elif hasattr(command_obj, option): + setattr(command_obj, option, value) + else: + raise DistutilsOptionError( + "error in %s: command '%s' has no such option '%s'" + % (source, command_name, option)) + except ValueError as msg: + raise DistutilsOptionError(msg) + + def reinitialize_command(self, command, reinit_subcommands=0): + """Reinitializes a command to the state it was in when first + returned by 'get_command_obj()': ie., initialized but not yet + finalized. This provides the opportunity to sneak option + values in programmatically, overriding or supplementing + user-supplied values from the config files and command line. + You'll have to re-finalize the command object (by calling + 'finalize_options()' or 'ensure_finalized()') before using it for + real. + + 'command' should be a command name (string) or command object. If + 'reinit_subcommands' is true, also reinitializes the command's + sub-commands, as declared by the 'sub_commands' class attribute (if + it has one). See the "install" command for an example. Only + reinitializes the sub-commands that actually matter, ie. those + whose test predicates return true. + + Returns the reinitialized command object. + """ + from distutils.cmd import Command + if not isinstance(command, Command): + command_name = command + command = self.get_command_obj(command_name) + else: + command_name = command.get_command_name() + + if not command.finalized: + return command + command.initialize_options() + command.finalized = 0 + self.have_run[command_name] = 0 + self._set_command_options(command) + + if reinit_subcommands: + for sub in command.get_sub_commands(): + self.reinitialize_command(sub, reinit_subcommands) + + return command + + # -- Methods that operate on the Distribution ---------------------- + + def announce(self, msg, level=log.INFO): + log.log(level, msg) + + def run_commands(self): + """Run each command that was seen on the setup script command line. + Uses the list of commands found and cache of command objects + created by 'get_command_obj()'. + """ + for cmd in self.commands: + self.run_command(cmd) + + # -- Methods that operate on its Commands -------------------------- + + def run_command(self, command): + """Do whatever it takes to run a command (including nothing at all, + if the command has already been run). Specifically: if we have + already created and run the command named by 'command', return + silently without doing anything. If the command named by 'command' + doesn't even have a command object yet, create one. Then invoke + 'run()' on that command object (or an existing one). + """ + # Already been here, done that? then return silently. + if self.have_run.get(command): + return + + log.info("running %s", command) + cmd_obj = self.get_command_obj(command) + cmd_obj.ensure_finalized() + cmd_obj.run() + self.have_run[command] = 1 + + # -- Distribution query methods ------------------------------------ + + def has_pure_modules(self): + return len(self.packages or self.py_modules or []) > 0 + + def has_ext_modules(self): + return self.ext_modules and len(self.ext_modules) > 0 + + def has_c_libraries(self): + return self.libraries and len(self.libraries) > 0 + + def has_modules(self): + return self.has_pure_modules() or self.has_ext_modules() + + def has_headers(self): + return self.headers and len(self.headers) > 0 + + def has_scripts(self): + return self.scripts and len(self.scripts) > 0 + + def has_data_files(self): + return self.data_files and len(self.data_files) > 0 + + def is_pure(self): + return (self.has_pure_modules() and + not self.has_ext_modules() and + not self.has_c_libraries()) + + # -- Metadata query methods ---------------------------------------- + + # If you're looking for 'get_name()', 'get_version()', and so forth, + # they are defined in a sneaky way: the constructor binds self.get_XXX + # to self.metadata.get_XXX. The actual code is in the + # DistributionMetadata class, below. + +class DistributionMetadata: + """Dummy class to hold the distribution meta-data: name, version, + author, and so forth. + """ + + _METHOD_BASENAMES = ("name", "version", "author", "author_email", + "maintainer", "maintainer_email", "url", + "license", "description", "long_description", + "keywords", "platforms", "fullname", "contact", + "contact_email", "classifiers", "download_url", + # PEP 314 + "provides", "requires", "obsoletes", + ) + + def __init__(self, path=None): + if path is not None: + self.read_pkg_file(open(path)) + else: + self.name = None + self.version = None + self.author = None + self.author_email = None + self.maintainer = None + self.maintainer_email = None + self.url = None + self.license = None + self.description = None + self.long_description = None + self.keywords = None + self.platforms = None + self.classifiers = None + self.download_url = None + # PEP 314 + self.provides = None + self.requires = None + self.obsoletes = None + + def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = message_from_file(file) + + def _read_field(name): + value = msg[name] + if value == 'UNKNOWN': + return None + return value + + def _read_list(name): + values = msg.get_all(name, None) + if values == []: + return None + return values + + metadata_version = msg['metadata-version'] + self.name = _read_field('name') + self.version = _read_field('version') + self.description = _read_field('summary') + # we are filling author only. + self.author = _read_field('author') + self.maintainer = None + self.author_email = _read_field('author-email') + self.maintainer_email = None + self.url = _read_field('home-page') + self.license = _read_field('license') + + if 'download-url' in msg: + self.download_url = _read_field('download-url') + else: + self.download_url = None + + self.long_description = _read_field('description') + self.description = _read_field('summary') + + if 'keywords' in msg: + self.keywords = _read_field('keywords').split(',') + + self.platforms = _read_list('platform') + self.classifiers = _read_list('classifier') + + # PEP 314 - these fields only exist in 1.1 + if metadata_version == '1.1': + self.requires = _read_list('requires') + self.provides = _read_list('provides') + self.obsoletes = _read_list('obsoletes') + else: + self.requires = None + self.provides = None + self.obsoletes = None + + def write_pkg_info(self, base_dir): + """Write the PKG-INFO file into the release tree. + """ + with open(os.path.join(base_dir, 'PKG-INFO'), 'w', + encoding='UTF-8') as pkg_info: + self.write_pkg_file(pkg_info) + + def write_pkg_file(self, file): + """Write the PKG-INFO format data to a file object. + """ + version = '1.0' + if (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): + version = '1.1' + + file.write('Metadata-Version: %s\n' % version) + file.write('Name: %s\n' % self.get_name()) + file.write('Version: %s\n' % self.get_version()) + file.write('Summary: %s\n' % self.get_description()) + file.write('Home-page: %s\n' % self.get_url()) + file.write('Author: %s\n' % self.get_contact()) + file.write('Author-email: %s\n' % self.get_contact_email()) + file.write('License: %s\n' % self.get_license()) + if self.download_url: + file.write('Download-URL: %s\n' % self.download_url) + + long_desc = rfc822_escape(self.get_long_description()) + file.write('Description: %s\n' % long_desc) + + keywords = ','.join(self.get_keywords()) + if keywords: + file.write('Keywords: %s\n' % keywords) + + self._write_list(file, 'Platform', self.get_platforms()) + self._write_list(file, 'Classifier', self.get_classifiers()) + + # PEP 314 + self._write_list(file, 'Requires', self.get_requires()) + self._write_list(file, 'Provides', self.get_provides()) + self._write_list(file, 'Obsoletes', self.get_obsoletes()) + + def _write_list(self, file, name, values): + for value in values: + file.write('%s: %s\n' % (name, value)) + + # -- Metadata query methods ---------------------------------------- + + def get_name(self): + return self.name or "UNKNOWN" + + def get_version(self): + return self.version or "0.0.0" + + def get_fullname(self): + return "%s-%s" % (self.get_name(), self.get_version()) + + def get_author(self): + return self.author or "UNKNOWN" + + def get_author_email(self): + return self.author_email or "UNKNOWN" + + def get_maintainer(self): + return self.maintainer or "UNKNOWN" + + def get_maintainer_email(self): + return self.maintainer_email or "UNKNOWN" + + def get_contact(self): + return self.maintainer or self.author or "UNKNOWN" + + def get_contact_email(self): + return self.maintainer_email or self.author_email or "UNKNOWN" + + def get_url(self): + return self.url or "UNKNOWN" + + def get_license(self): + return self.license or "UNKNOWN" + get_licence = get_license + + def get_description(self): + return self.description or "UNKNOWN" + + def get_long_description(self): + return self.long_description or "UNKNOWN" + + def get_keywords(self): + return self.keywords or [] + + def set_keywords(self, value): + self.keywords = _ensure_list(value, 'keywords') + + def get_platforms(self): + return self.platforms or ["UNKNOWN"] + + def set_platforms(self, value): + self.platforms = _ensure_list(value, 'platforms') + + def get_classifiers(self): + return self.classifiers or [] + + def set_classifiers(self, value): + self.classifiers = _ensure_list(value, 'classifiers') + + def get_download_url(self): + return self.download_url or "UNKNOWN" + + # PEP 314 + def get_requires(self): + return self.requires or [] + + def set_requires(self, value): + import distutils.versionpredicate + for v in value: + distutils.versionpredicate.VersionPredicate(v) + self.requires = list(value) + + def get_provides(self): + return self.provides or [] + + def set_provides(self, value): + value = [v.strip() for v in value] + for v in value: + import distutils.versionpredicate + distutils.versionpredicate.split_provision(v) + self.provides = value + + def get_obsoletes(self): + return self.obsoletes or [] + + def set_obsoletes(self, value): + import distutils.versionpredicate + for v in value: + distutils.versionpredicate.VersionPredicate(v) + self.obsoletes = list(value) + +def fix_help_options(options): + """Convert a 4-tuple 'help_options' list as found in various command + classes to the 3-tuple form required by FancyGetopt. + """ + new_options = [] + for help_tuple in options: + new_options.append(help_tuple[0:3]) + return new_options diff --git a/distutils/errors.py b/distutils/errors.py new file mode 100644 index 00000000..8b93059e --- /dev/null +++ b/distutils/errors.py @@ -0,0 +1,97 @@ +"""distutils.errors + +Provides exceptions used by the Distutils modules. Note that Distutils +modules may raise standard exceptions; in particular, SystemExit is +usually raised for errors that are obviously the end-user's fault +(eg. bad command-line arguments). + +This module is safe to use in "from ... import *" mode; it only exports +symbols whose names start with "Distutils" and end with "Error".""" + +class DistutilsError (Exception): + """The root of all Distutils evil.""" + pass + +class DistutilsModuleError (DistutilsError): + """Unable to load an expected module, or to find an expected class + within some module (in particular, command modules and classes).""" + pass + +class DistutilsClassError (DistutilsError): + """Some command class (or possibly distribution class, if anyone + feels a need to subclass Distribution) is found not to be holding + up its end of the bargain, ie. implementing some part of the + "command "interface.""" + pass + +class DistutilsGetoptError (DistutilsError): + """The option table provided to 'fancy_getopt()' is bogus.""" + pass + +class DistutilsArgError (DistutilsError): + """Raised by fancy_getopt in response to getopt.error -- ie. an + error in the command line usage.""" + pass + +class DistutilsFileError (DistutilsError): + """Any problems in the filesystem: expected file not found, etc. + Typically this is for problems that we detect before OSError + could be raised.""" + pass + +class DistutilsOptionError (DistutilsError): + """Syntactic/semantic errors in command options, such as use of + mutually conflicting options, or inconsistent options, + badly-spelled values, etc. No distinction is made between option + values originating in the setup script, the command line, config + files, or what-have-you -- but if we *know* something originated in + the setup script, we'll raise DistutilsSetupError instead.""" + pass + +class DistutilsSetupError (DistutilsError): + """For errors that can be definitely blamed on the setup script, + such as invalid keyword arguments to 'setup()'.""" + pass + +class DistutilsPlatformError (DistutilsError): + """We don't know how to do something on the current platform (but + we do know how to do it on some platform) -- eg. trying to compile + C files on a platform not supported by a CCompiler subclass.""" + pass + +class DistutilsExecError (DistutilsError): + """Any problems executing an external program (such as the C + compiler, when compiling C files).""" + pass + +class DistutilsInternalError (DistutilsError): + """Internal inconsistencies or impossibilities (obviously, this + should never be seen if the code is working!).""" + pass + +class DistutilsTemplateError (DistutilsError): + """Syntax error in a file list template.""" + +class DistutilsByteCompileError(DistutilsError): + """Byte compile error.""" + +# Exception classes used by the CCompiler implementation classes +class CCompilerError (Exception): + """Some compile/link operation failed.""" + +class PreprocessError (CCompilerError): + """Failure to preprocess one or more C/C++ files.""" + +class CompileError (CCompilerError): + """Failure to compile one or more C/C++ source files.""" + +class LibError (CCompilerError): + """Failure to create a static library from one or more C/C++ object + files.""" + +class LinkError (CCompilerError): + """Failure to link one or more C/C++ object files into an executable + or shared library file.""" + +class UnknownFileError (CCompilerError): + """Attempt to process an unknown file type.""" diff --git a/distutils/extension.py b/distutils/extension.py new file mode 100644 index 00000000..c507da36 --- /dev/null +++ b/distutils/extension.py @@ -0,0 +1,240 @@ +"""distutils.extension + +Provides the Extension class, used to describe C/C++ extension +modules in setup scripts.""" + +import os +import warnings + +# This class is really only used by the "build_ext" command, so it might +# make sense to put it in distutils.command.build_ext. However, that +# module is already big enough, and I want to make this class a bit more +# complex to simplify some common cases ("foo" module in "foo.c") and do +# better error-checking ("foo.c" actually exists). +# +# Also, putting this in build_ext.py means every setup script would have to +# import that large-ish module (indirectly, through distutils.core) in +# order to do anything. + +class Extension: + """Just a collection of attributes that describes an extension + module and everything needed to build it (hopefully in a portable + way, but there are hooks that let you be as unportable as you need). + + Instance attributes: + name : string + the full name of the extension, including any packages -- ie. + *not* a filename or pathname, but Python dotted name + sources : [string] + list of source filenames, relative to the distribution root + (where the setup script lives), in Unix form (slash-separated) + for portability. Source files may be C, C++, SWIG (.i), + platform-specific resource files, or whatever else is recognized + by the "build_ext" command as source for a Python extension. + include_dirs : [string] + list of directories to search for C/C++ header files (in Unix + form for portability) + define_macros : [(name : string, value : string|None)] + list of macros to define; each macro is defined using a 2-tuple, + where 'value' is either the string to define it to or None to + define it without a particular value (equivalent of "#define + FOO" in source or -DFOO on Unix C compiler command line) + undef_macros : [string] + list of macros to undefine explicitly + library_dirs : [string] + list of directories to search for C/C++ libraries at link time + libraries : [string] + list of library names (not filenames or paths) to link against + runtime_library_dirs : [string] + list of directories to search for C/C++ libraries at run time + (for shared extensions, this is when the extension is loaded) + extra_objects : [string] + list of extra files to link with (eg. object files not implied + by 'sources', static library that must be explicitly specified, + binary resource files, etc.) + extra_compile_args : [string] + any extra platform- and compiler-specific information to use + when compiling the source files in 'sources'. For platforms and + compilers where "command line" makes sense, this is typically a + list of command-line arguments, but for other platforms it could + be anything. + extra_link_args : [string] + any extra platform- and compiler-specific information to use + when linking object files together to create the extension (or + to create a new static Python interpreter). Similar + interpretation as for 'extra_compile_args'. + export_symbols : [string] + list of symbols to be exported from a shared extension. Not + used on all platforms, and not generally necessary for Python + extensions, which typically export exactly one symbol: "init" + + extension_name. + swig_opts : [string] + any extra options to pass to SWIG if a source file has the .i + extension. + depends : [string] + list of files that the extension depends on + language : string + extension language (i.e. "c", "c++", "objc"). Will be detected + from the source extensions if not provided. + optional : boolean + specifies that a build failure in the extension should not abort the + build process, but simply not install the failing extension. + """ + + # When adding arguments to this constructor, be sure to update + # setup_keywords in core.py. + def __init__(self, name, sources, + include_dirs=None, + define_macros=None, + undef_macros=None, + library_dirs=None, + libraries=None, + runtime_library_dirs=None, + extra_objects=None, + extra_compile_args=None, + extra_link_args=None, + export_symbols=None, + swig_opts = None, + depends=None, + language=None, + optional=None, + **kw # To catch unknown keywords + ): + if not isinstance(name, str): + raise AssertionError("'name' must be a string") + if not (isinstance(sources, list) and + all(isinstance(v, str) for v in sources)): + raise AssertionError("'sources' must be a list of strings") + + self.name = name + self.sources = sources + self.include_dirs = include_dirs or [] + self.define_macros = define_macros or [] + self.undef_macros = undef_macros or [] + self.library_dirs = library_dirs or [] + self.libraries = libraries or [] + self.runtime_library_dirs = runtime_library_dirs or [] + self.extra_objects = extra_objects or [] + self.extra_compile_args = extra_compile_args or [] + self.extra_link_args = extra_link_args or [] + self.export_symbols = export_symbols or [] + self.swig_opts = swig_opts or [] + self.depends = depends or [] + self.language = language + self.optional = optional + + # If there are unknown keyword options, warn about them + if len(kw) > 0: + options = [repr(option) for option in kw] + options = ', '.join(sorted(options)) + msg = "Unknown Extension options: %s" % options + warnings.warn(msg) + + def __repr__(self): + return '<%s.%s(%r) at %#x>' % ( + self.__class__.__module__, + self.__class__.__qualname__, + self.name, + id(self)) + + +def read_setup_file(filename): + """Reads a Setup file and returns Extension instances.""" + from distutils.sysconfig import (parse_makefile, expand_makefile_vars, + _variable_rx) + + from distutils.text_file import TextFile + from distutils.util import split_quoted + + # First pass over the file to gather "VAR = VALUE" assignments. + vars = parse_makefile(filename) + + # Second pass to gobble up the real content: lines of the form + # ... [ ...] [ ...] [ ...] + file = TextFile(filename, + strip_comments=1, skip_blanks=1, join_lines=1, + lstrip_ws=1, rstrip_ws=1) + try: + extensions = [] + + while True: + line = file.readline() + if line is None: # eof + break + if _variable_rx.match(line): # VAR=VALUE, handled in first pass + continue + + if line[0] == line[-1] == "*": + file.warn("'%s' lines not handled yet" % line) + continue + + line = expand_makefile_vars(line, vars) + words = split_quoted(line) + + # NB. this parses a slightly different syntax than the old + # makesetup script: here, there must be exactly one extension per + # line, and it must be the first word of the line. I have no idea + # why the old syntax supported multiple extensions per line, as + # they all wind up being the same. + + module = words[0] + ext = Extension(module, []) + append_next_word = None + + for word in words[1:]: + if append_next_word is not None: + append_next_word.append(word) + append_next_word = None + continue + + suffix = os.path.splitext(word)[1] + switch = word[0:2] ; value = word[2:] + + if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): + # hmm, should we do something about C vs. C++ sources? + # or leave it up to the CCompiler implementation to + # worry about? + ext.sources.append(word) + elif switch == "-I": + ext.include_dirs.append(value) + elif switch == "-D": + equals = value.find("=") + if equals == -1: # bare "-DFOO" -- no value + ext.define_macros.append((value, None)) + else: # "-DFOO=blah" + ext.define_macros.append((value[0:equals], + value[equals+2:])) + elif switch == "-U": + ext.undef_macros.append(value) + elif switch == "-C": # only here 'cause makesetup has it! + ext.extra_compile_args.append(word) + elif switch == "-l": + ext.libraries.append(value) + elif switch == "-L": + ext.library_dirs.append(value) + elif switch == "-R": + ext.runtime_library_dirs.append(value) + elif word == "-rpath": + append_next_word = ext.runtime_library_dirs + elif word == "-Xlinker": + append_next_word = ext.extra_link_args + elif word == "-Xcompiler": + append_next_word = ext.extra_compile_args + elif switch == "-u": + ext.extra_link_args.append(word) + if not value: + append_next_word = ext.extra_link_args + elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): + # NB. a really faithful emulation of makesetup would + # append a .o file to extra_objects only if it + # had a slash in it; otherwise, it would s/.o/.c/ + # and append it to sources. Hmmmm. + ext.extra_objects.append(word) + else: + file.warn("unrecognized argument '%s'" % word) + + extensions.append(ext) + finally: + file.close() + + return extensions diff --git a/distutils/fancy_getopt.py b/distutils/fancy_getopt.py new file mode 100644 index 00000000..7d170dd2 --- /dev/null +++ b/distutils/fancy_getopt.py @@ -0,0 +1,457 @@ +"""distutils.fancy_getopt + +Wrapper around the standard getopt module that provides the following +additional features: + * short and long options are tied together + * options have help strings, so fancy_getopt could potentially + create a complete usage summary + * options set attributes of a passed-in object +""" + +import sys, string, re +import getopt +from distutils.errors import * + +# Much like command_re in distutils.core, this is close to but not quite +# the same as a Python NAME -- except, in the spirit of most GNU +# utilities, we use '-' in place of '_'. (The spirit of LISP lives on!) +# The similarities to NAME are again not a coincidence... +longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)' +longopt_re = re.compile(r'^%s$' % longopt_pat) + +# For recognizing "negative alias" options, eg. "quiet=!verbose" +neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) + +# This is used to translate long options to legitimate Python identifiers +# (for use as attributes of some object). +longopt_xlate = str.maketrans('-', '_') + +class FancyGetopt: + """Wrapper around the standard 'getopt()' module that provides some + handy extra functionality: + * short and long options are tied together + * options have help strings, and help text can be assembled + from them + * options set attributes of a passed-in object + * boolean options can have "negative aliases" -- eg. if + --quiet is the "negative alias" of --verbose, then "--quiet" + on the command line sets 'verbose' to false + """ + + def __init__(self, option_table=None): + # The option table is (currently) a list of tuples. The + # tuples may have 3 or four values: + # (long_option, short_option, help_string [, repeatable]) + # if an option takes an argument, its long_option should have '=' + # appended; short_option should just be a single character, no ':' + # in any case. If a long_option doesn't have a corresponding + # short_option, short_option should be None. All option tuples + # must have long options. + self.option_table = option_table + + # 'option_index' maps long option names to entries in the option + # table (ie. those 3-tuples). + self.option_index = {} + if self.option_table: + self._build_index() + + # 'alias' records (duh) alias options; {'foo': 'bar'} means + # --foo is an alias for --bar + self.alias = {} + + # 'negative_alias' keeps track of options that are the boolean + # opposite of some other option + self.negative_alias = {} + + # These keep track of the information in the option table. We + # don't actually populate these structures until we're ready to + # parse the command-line, since the 'option_table' passed in here + # isn't necessarily the final word. + self.short_opts = [] + self.long_opts = [] + self.short2long = {} + self.attr_name = {} + self.takes_arg = {} + + # And 'option_order' is filled up in 'getopt()'; it records the + # original order of options (and their values) on the command-line, + # but expands short options, converts aliases, etc. + self.option_order = [] + + def _build_index(self): + self.option_index.clear() + for option in self.option_table: + self.option_index[option[0]] = option + + def set_option_table(self, option_table): + self.option_table = option_table + self._build_index() + + def add_option(self, long_option, short_option=None, help_string=None): + if long_option in self.option_index: + raise DistutilsGetoptError( + "option conflict: already an option '%s'" % long_option) + else: + option = (long_option, short_option, help_string) + self.option_table.append(option) + self.option_index[long_option] = option + + def has_option(self, long_option): + """Return true if the option table for this parser has an + option with long name 'long_option'.""" + return long_option in self.option_index + + def get_attr_name(self, long_option): + """Translate long option name 'long_option' to the form it + has as an attribute of some object: ie., translate hyphens + to underscores.""" + return long_option.translate(longopt_xlate) + + def _check_alias_dict(self, aliases, what): + assert isinstance(aliases, dict) + for (alias, opt) in aliases.items(): + if alias not in self.option_index: + raise DistutilsGetoptError(("invalid %s '%s': " + "option '%s' not defined") % (what, alias, alias)) + if opt not in self.option_index: + raise DistutilsGetoptError(("invalid %s '%s': " + "aliased option '%s' not defined") % (what, alias, opt)) + + def set_aliases(self, alias): + """Set the aliases for this option parser.""" + self._check_alias_dict(alias, "alias") + self.alias = alias + + def set_negative_aliases(self, negative_alias): + """Set the negative aliases for this option parser. + 'negative_alias' should be a dictionary mapping option names to + option names, both the key and value must already be defined + in the option table.""" + self._check_alias_dict(negative_alias, "negative alias") + self.negative_alias = negative_alias + + def _grok_option_table(self): + """Populate the various data structures that keep tabs on the + option table. Called by 'getopt()' before it can do anything + worthwhile. + """ + self.long_opts = [] + self.short_opts = [] + self.short2long.clear() + self.repeat = {} + + for option in self.option_table: + if len(option) == 3: + long, short, help = option + repeat = 0 + elif len(option) == 4: + long, short, help, repeat = option + else: + # the option table is part of the code, so simply + # assert that it is correct + raise ValueError("invalid option tuple: %r" % (option,)) + + # Type- and value-check the option names + if not isinstance(long, str) or len(long) < 2: + raise DistutilsGetoptError(("invalid long option '%s': " + "must be a string of length >= 2") % long) + + if (not ((short is None) or + (isinstance(short, str) and len(short) == 1))): + raise DistutilsGetoptError("invalid short option '%s': " + "must a single character or None" % short) + + self.repeat[long] = repeat + self.long_opts.append(long) + + if long[-1] == '=': # option takes an argument? + if short: short = short + ':' + long = long[0:-1] + self.takes_arg[long] = 1 + else: + # Is option is a "negative alias" for some other option (eg. + # "quiet" == "!verbose")? + alias_to = self.negative_alias.get(long) + if alias_to is not None: + if self.takes_arg[alias_to]: + raise DistutilsGetoptError( + "invalid negative alias '%s': " + "aliased option '%s' takes a value" + % (long, alias_to)) + + self.long_opts[-1] = long # XXX redundant?! + self.takes_arg[long] = 0 + + # If this is an alias option, make sure its "takes arg" flag is + # the same as the option it's aliased to. + alias_to = self.alias.get(long) + if alias_to is not None: + if self.takes_arg[long] != self.takes_arg[alias_to]: + raise DistutilsGetoptError( + "invalid alias '%s': inconsistent with " + "aliased option '%s' (one of them takes a value, " + "the other doesn't" + % (long, alias_to)) + + # Now enforce some bondage on the long option name, so we can + # later translate it to an attribute name on some object. Have + # to do this a bit late to make sure we've removed any trailing + # '='. + if not longopt_re.match(long): + raise DistutilsGetoptError( + "invalid long option name '%s' " + "(must be letters, numbers, hyphens only" % long) + + self.attr_name[long] = self.get_attr_name(long) + if short: + self.short_opts.append(short) + self.short2long[short[0]] = long + + def getopt(self, args=None, object=None): + """Parse command-line options in args. Store as attributes on object. + + If 'args' is None or not supplied, uses 'sys.argv[1:]'. If + 'object' is None or not supplied, creates a new OptionDummy + object, stores option values there, and returns a tuple (args, + object). If 'object' is supplied, it is modified in place and + 'getopt()' just returns 'args'; in both cases, the returned + 'args' is a modified copy of the passed-in 'args' list, which + is left untouched. + """ + if args is None: + args = sys.argv[1:] + if object is None: + object = OptionDummy() + created_object = True + else: + created_object = False + + self._grok_option_table() + + short_opts = ' '.join(self.short_opts) + try: + opts, args = getopt.getopt(args, short_opts, self.long_opts) + except getopt.error as msg: + raise DistutilsArgError(msg) + + for opt, val in opts: + if len(opt) == 2 and opt[0] == '-': # it's a short option + opt = self.short2long[opt[1]] + else: + assert len(opt) > 2 and opt[:2] == '--' + opt = opt[2:] + + alias = self.alias.get(opt) + if alias: + opt = alias + + if not self.takes_arg[opt]: # boolean option? + assert val == '', "boolean option can't have value" + alias = self.negative_alias.get(opt) + if alias: + opt = alias + val = 0 + else: + val = 1 + + attr = self.attr_name[opt] + # The only repeating option at the moment is 'verbose'. + # It has a negative option -q quiet, which should set verbose = 0. + if val and self.repeat.get(attr) is not None: + val = getattr(object, attr, 0) + 1 + setattr(object, attr, val) + self.option_order.append((opt, val)) + + # for opts + if created_object: + return args, object + else: + return args + + def get_option_order(self): + """Returns the list of (option, value) tuples processed by the + previous run of 'getopt()'. Raises RuntimeError if + 'getopt()' hasn't been called yet. + """ + if self.option_order is None: + raise RuntimeError("'getopt()' hasn't been called yet") + else: + return self.option_order + + def generate_help(self, header=None): + """Generate help text (a list of strings, one per suggested line of + output) from the option table for this FancyGetopt object. + """ + # Blithely assume the option table is good: probably wouldn't call + # 'generate_help()' unless you've already called 'getopt()'. + + # First pass: determine maximum length of long option names + max_opt = 0 + for option in self.option_table: + long = option[0] + short = option[1] + l = len(long) + if long[-1] == '=': + l = l - 1 + if short is not None: + l = l + 5 # " (-x)" where short == 'x' + if l > max_opt: + max_opt = l + + opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter + + # Typical help block looks like this: + # --foo controls foonabulation + # Help block for longest option looks like this: + # --flimflam set the flim-flam level + # and with wrapped text: + # --flimflam set the flim-flam level (must be between + # 0 and 100, except on Tuesdays) + # Options with short names will have the short name shown (but + # it doesn't contribute to max_opt): + # --foo (-f) controls foonabulation + # If adding the short option would make the left column too wide, + # we push the explanation off to the next line + # --flimflam (-l) + # set the flim-flam level + # Important parameters: + # - 2 spaces before option block start lines + # - 2 dashes for each long option name + # - min. 2 spaces between option and explanation (gutter) + # - 5 characters (incl. space) for short option name + + # Now generate lines of help text. (If 80 columns were good enough + # for Jesus, then 78 columns are good enough for me!) + line_width = 78 + text_width = line_width - opt_width + big_indent = ' ' * opt_width + if header: + lines = [header] + else: + lines = ['Option summary:'] + + for option in self.option_table: + long, short, help = option[:3] + text = wrap_text(help, text_width) + if long[-1] == '=': + long = long[0:-1] + + # Case 1: no short option at all (makes life easy) + if short is None: + if text: + lines.append(" --%-*s %s" % (max_opt, long, text[0])) + else: + lines.append(" --%-*s " % (max_opt, long)) + + # Case 2: we have a short option, so we have to include it + # just after the long option + else: + opt_names = "%s (-%s)" % (long, short) + if text: + lines.append(" --%-*s %s" % + (max_opt, opt_names, text[0])) + else: + lines.append(" --%-*s" % opt_names) + + for l in text[1:]: + lines.append(big_indent + l) + return lines + + def print_help(self, header=None, file=None): + if file is None: + file = sys.stdout + for line in self.generate_help(header): + file.write(line + "\n") + + +def fancy_getopt(options, negative_opt, object, args): + parser = FancyGetopt(options) + parser.set_negative_aliases(negative_opt) + return parser.getopt(args, object) + + +WS_TRANS = {ord(_wschar) : ' ' for _wschar in string.whitespace} + +def wrap_text(text, width): + """wrap_text(text : string, width : int) -> [string] + + Split 'text' into multiple lines of no more than 'width' characters + each, and return the list of strings that results. + """ + if text is None: + return [] + if len(text) <= width: + return [text] + + text = text.expandtabs() + text = text.translate(WS_TRANS) + chunks = re.split(r'( +|-+)', text) + chunks = [ch for ch in chunks if ch] # ' - ' results in empty strings + lines = [] + + while chunks: + cur_line = [] # list of chunks (to-be-joined) + cur_len = 0 # length of current line + + while chunks: + l = len(chunks[0]) + if cur_len + l <= width: # can squeeze (at least) this chunk in + cur_line.append(chunks[0]) + del chunks[0] + cur_len = cur_len + l + else: # this line is full + # drop last chunk if all space + if cur_line and cur_line[-1][0] == ' ': + del cur_line[-1] + break + + if chunks: # any chunks left to process? + # if the current line is still empty, then we had a single + # chunk that's too big too fit on a line -- so we break + # down and break it up at the line width + if cur_len == 0: + cur_line.append(chunks[0][0:width]) + chunks[0] = chunks[0][width:] + + # all-whitespace chunks at the end of a line can be discarded + # (and we know from the re.split above that if a chunk has + # *any* whitespace, it is *all* whitespace) + if chunks[0][0] == ' ': + del chunks[0] + + # and store this line in the list-of-all-lines -- as a single + # string, of course! + lines.append(''.join(cur_line)) + + return lines + + +def translate_longopt(opt): + """Convert a long option name to a valid Python identifier by + changing "-" to "_". + """ + return opt.translate(longopt_xlate) + + +class OptionDummy: + """Dummy class just used as a place to hold command-line option + values as instance attributes.""" + + def __init__(self, options=[]): + """Create a new OptionDummy instance. The attributes listed in + 'options' will be initialized to None.""" + for opt in options: + setattr(self, opt, None) + + +if __name__ == "__main__": + text = """\ +Tra-la-la, supercalifragilisticexpialidocious. +How *do* you spell that odd word, anyways? +(Someone ask Mary -- she'll know [or she'll +say, "How should I know?"].)""" + + for w in (10, 20, 30, 40): + print("width: %d" % w) + print("\n".join(wrap_text(text, w))) + print() diff --git a/distutils/file_util.py b/distutils/file_util.py new file mode 100644 index 00000000..b3fee35a --- /dev/null +++ b/distutils/file_util.py @@ -0,0 +1,238 @@ +"""distutils.file_util + +Utility functions for operating on single files. +""" + +import os +from distutils.errors import DistutilsFileError +from distutils import log + +# for generating verbose output in 'copy_file()' +_copy_action = { None: 'copying', + 'hard': 'hard linking', + 'sym': 'symbolically linking' } + + +def _copy_file_contents(src, dst, buffer_size=16*1024): + """Copy the file 'src' to 'dst'; both must be filenames. Any error + opening either file, reading from 'src', or writing to 'dst', raises + DistutilsFileError. Data is read/written in chunks of 'buffer_size' + bytes (default 16k). No attempt is made to handle anything apart from + regular files. + """ + # Stolen from shutil module in the standard library, but with + # custom error-handling added. + fsrc = None + fdst = None + try: + try: + fsrc = open(src, 'rb') + except OSError as e: + raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror)) + + if os.path.exists(dst): + try: + os.unlink(dst) + except OSError as e: + raise DistutilsFileError( + "could not delete '%s': %s" % (dst, e.strerror)) + + try: + fdst = open(dst, 'wb') + except OSError as e: + raise DistutilsFileError( + "could not create '%s': %s" % (dst, e.strerror)) + + while True: + try: + buf = fsrc.read(buffer_size) + except OSError as e: + raise DistutilsFileError( + "could not read from '%s': %s" % (src, e.strerror)) + + if not buf: + break + + try: + fdst.write(buf) + except OSError as e: + raise DistutilsFileError( + "could not write to '%s': %s" % (dst, e.strerror)) + finally: + if fdst: + fdst.close() + if fsrc: + fsrc.close() + +def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, + link=None, verbose=1, dry_run=0): + """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is + copied there with the same name; otherwise, it must be a filename. (If + the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' + is true (the default), the file's mode (type and permission bits, or + whatever is analogous on the current platform) is copied. If + 'preserve_times' is true (the default), the last-modified and + last-access times are copied as well. If 'update' is true, 'src' will + only be copied if 'dst' does not exist, or if 'dst' does exist but is + older than 'src'. + + 'link' allows you to make hard links (os.link) or symbolic links + (os.symlink) instead of copying: set it to "hard" or "sym"; if it is + None (the default), files are copied. Don't set 'link' on systems that + don't support it: 'copy_file()' doesn't check if hard or symbolic + linking is available. If hardlink fails, falls back to + _copy_file_contents(). + + Under Mac OS, uses the native file copy function in macostools; on + other systems, uses '_copy_file_contents()' to copy file contents. + + Return a tuple (dest_name, copied): 'dest_name' is the actual name of + the output file, and 'copied' is true if the file was copied (or would + have been copied, if 'dry_run' true). + """ + # XXX if the destination file already exists, we clobber it if + # copying, but blow up if linking. Hmmm. And I don't know what + # macostools.copyfile() does. Should definitely be consistent, and + # should probably blow up if destination exists and we would be + # changing it (ie. it's not already a hard/soft link to src OR + # (not update) and (src newer than dst). + + from distutils.dep_util import newer + from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE + + if not os.path.isfile(src): + raise DistutilsFileError( + "can't copy '%s': doesn't exist or not a regular file" % src) + + if os.path.isdir(dst): + dir = dst + dst = os.path.join(dst, os.path.basename(src)) + else: + dir = os.path.dirname(dst) + + if update and not newer(src, dst): + if verbose >= 1: + log.debug("not copying %s (output up-to-date)", src) + return (dst, 0) + + try: + action = _copy_action[link] + except KeyError: + raise ValueError("invalid value '%s' for 'link' argument" % link) + + if verbose >= 1: + if os.path.basename(dst) == os.path.basename(src): + log.info("%s %s -> %s", action, src, dir) + else: + log.info("%s %s -> %s", action, src, dst) + + if dry_run: + return (dst, 1) + + # If linking (hard or symbolic), use the appropriate system call + # (Unix only, of course, but that's the caller's responsibility) + elif link == 'hard': + if not (os.path.exists(dst) and os.path.samefile(src, dst)): + try: + os.link(src, dst) + return (dst, 1) + except OSError: + # If hard linking fails, fall back on copying file + # (some special filesystems don't support hard linking + # even under Unix, see issue #8876). + pass + elif link == 'sym': + if not (os.path.exists(dst) and os.path.samefile(src, dst)): + os.symlink(src, dst) + return (dst, 1) + + # Otherwise (non-Mac, not linking), copy the file contents and + # (optionally) copy the times and mode. + _copy_file_contents(src, dst) + if preserve_mode or preserve_times: + st = os.stat(src) + + # According to David Ascher , utime() should be done + # before chmod() (at least under NT). + if preserve_times: + os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) + if preserve_mode: + os.chmod(dst, S_IMODE(st[ST_MODE])) + + return (dst, 1) + + +# XXX I suspect this is Unix-specific -- need porting help! +def move_file (src, dst, + verbose=1, + dry_run=0): + + """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will + be moved into it with the same name; otherwise, 'src' is just renamed + to 'dst'. Return the new full name of the file. + + Handles cross-device moves on Unix using 'copy_file()'. What about + other systems??? + """ + from os.path import exists, isfile, isdir, basename, dirname + import errno + + if verbose >= 1: + log.info("moving %s -> %s", src, dst) + + if dry_run: + return dst + + if not isfile(src): + raise DistutilsFileError("can't move '%s': not a regular file" % src) + + if isdir(dst): + dst = os.path.join(dst, basename(src)) + elif exists(dst): + raise DistutilsFileError( + "can't move '%s': destination '%s' already exists" % + (src, dst)) + + if not isdir(dirname(dst)): + raise DistutilsFileError( + "can't move '%s': destination '%s' not a valid path" % + (src, dst)) + + copy_it = False + try: + os.rename(src, dst) + except OSError as e: + (num, msg) = e.args + if num == errno.EXDEV: + copy_it = True + else: + raise DistutilsFileError( + "couldn't move '%s' to '%s': %s" % (src, dst, msg)) + + if copy_it: + copy_file(src, dst, verbose=verbose) + try: + os.unlink(src) + except OSError as e: + (num, msg) = e.args + try: + os.unlink(dst) + except OSError: + pass + raise DistutilsFileError( + "couldn't move '%s' to '%s' by copy/delete: " + "delete '%s' failed: %s" + % (src, dst, src, msg)) + return dst + + +def write_file (filename, contents): + """Create a file with the specified name and write 'contents' (a + sequence of strings without line terminators) to it. + """ + f = open(filename, "w") + try: + for line in contents: + f.write(line + "\n") + finally: + f.close() diff --git a/distutils/filelist.py b/distutils/filelist.py new file mode 100644 index 00000000..c92d5fdb --- /dev/null +++ b/distutils/filelist.py @@ -0,0 +1,327 @@ +"""distutils.filelist + +Provides the FileList class, used for poking about the filesystem +and building lists of files. +""" + +import os, re +import fnmatch +import functools +from distutils.util import convert_path +from distutils.errors import DistutilsTemplateError, DistutilsInternalError +from distutils import log + +class FileList: + """A list of files built by on exploring the filesystem and filtered by + applying various patterns to what we find there. + + Instance attributes: + dir + directory from which files will be taken -- only used if + 'allfiles' not supplied to constructor + files + list of filenames currently being built/filtered/manipulated + allfiles + complete list of files under consideration (ie. without any + filtering applied) + """ + + def __init__(self, warn=None, debug_print=None): + # ignore argument to FileList, but keep them for backwards + # compatibility + self.allfiles = None + self.files = [] + + def set_allfiles(self, allfiles): + self.allfiles = allfiles + + def findall(self, dir=os.curdir): + self.allfiles = findall(dir) + + def debug_print(self, msg): + """Print 'msg' to stdout if the global DEBUG (taken from the + DISTUTILS_DEBUG environment variable) flag is true. + """ + from distutils.debug import DEBUG + if DEBUG: + print(msg) + + # -- List-like methods --------------------------------------------- + + def append(self, item): + self.files.append(item) + + def extend(self, items): + self.files.extend(items) + + def sort(self): + # Not a strict lexical sort! + sortable_files = sorted(map(os.path.split, self.files)) + self.files = [] + for sort_tuple in sortable_files: + self.files.append(os.path.join(*sort_tuple)) + + + # -- Other miscellaneous utility methods --------------------------- + + def remove_duplicates(self): + # Assumes list has been sorted! + for i in range(len(self.files) - 1, 0, -1): + if self.files[i] == self.files[i - 1]: + del self.files[i] + + + # -- "File template" methods --------------------------------------- + + def _parse_template_line(self, line): + words = line.split() + action = words[0] + + patterns = dir = dir_pattern = None + + if action in ('include', 'exclude', + 'global-include', 'global-exclude'): + if len(words) < 2: + raise DistutilsTemplateError( + "'%s' expects ..." % action) + patterns = [convert_path(w) for w in words[1:]] + elif action in ('recursive-include', 'recursive-exclude'): + if len(words) < 3: + raise DistutilsTemplateError( + "'%s' expects ..." % action) + dir = convert_path(words[1]) + patterns = [convert_path(w) for w in words[2:]] + elif action in ('graft', 'prune'): + if len(words) != 2: + raise DistutilsTemplateError( + "'%s' expects a single " % action) + dir_pattern = convert_path(words[1]) + else: + raise DistutilsTemplateError("unknown action '%s'" % action) + + return (action, patterns, dir, dir_pattern) + + def process_template_line(self, line): + # Parse the line: split it up, make sure the right number of words + # is there, and return the relevant words. 'action' is always + # defined: it's the first word of the line. Which of the other + # three are defined depends on the action; it'll be either + # patterns, (dir and patterns), or (dir_pattern). + (action, patterns, dir, dir_pattern) = self._parse_template_line(line) + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. + if action == 'include': + self.debug_print("include " + ' '.join(patterns)) + for pattern in patterns: + if not self.include_pattern(pattern, anchor=1): + log.warn("warning: no files found matching '%s'", + pattern) + + elif action == 'exclude': + self.debug_print("exclude " + ' '.join(patterns)) + for pattern in patterns: + if not self.exclude_pattern(pattern, anchor=1): + log.warn(("warning: no previously-included files " + "found matching '%s'"), pattern) + + elif action == 'global-include': + self.debug_print("global-include " + ' '.join(patterns)) + for pattern in patterns: + if not self.include_pattern(pattern, anchor=0): + log.warn(("warning: no files found matching '%s' " + "anywhere in distribution"), pattern) + + elif action == 'global-exclude': + self.debug_print("global-exclude " + ' '.join(patterns)) + for pattern in patterns: + if not self.exclude_pattern(pattern, anchor=0): + log.warn(("warning: no previously-included files matching " + "'%s' found anywhere in distribution"), + pattern) + + elif action == 'recursive-include': + self.debug_print("recursive-include %s %s" % + (dir, ' '.join(patterns))) + for pattern in patterns: + if not self.include_pattern(pattern, prefix=dir): + log.warn(("warning: no files found matching '%s' " + "under directory '%s'"), + pattern, dir) + + elif action == 'recursive-exclude': + self.debug_print("recursive-exclude %s %s" % + (dir, ' '.join(patterns))) + for pattern in patterns: + if not self.exclude_pattern(pattern, prefix=dir): + log.warn(("warning: no previously-included files matching " + "'%s' found under directory '%s'"), + pattern, dir) + + elif action == 'graft': + self.debug_print("graft " + dir_pattern) + if not self.include_pattern(None, prefix=dir_pattern): + log.warn("warning: no directories found matching '%s'", + dir_pattern) + + elif action == 'prune': + self.debug_print("prune " + dir_pattern) + if not self.exclude_pattern(None, prefix=dir_pattern): + log.warn(("no previously-included directories found " + "matching '%s'"), dir_pattern) + else: + raise DistutilsInternalError( + "this cannot happen: invalid action '%s'" % action) + + + # -- Filtering/selection methods ----------------------------------- + + def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): + """Select strings (presumably filenames) from 'self.files' that + match 'pattern', a Unix-style wildcard (glob) pattern. Patterns + are not quite the same as implemented by the 'fnmatch' module: '*' + and '?' match non-special characters, where "special" is platform- + dependent: slash on Unix; colon, slash, and backslash on + DOS/Windows; and colon on Mac OS. + + If 'anchor' is true (the default), then the pattern match is more + stringent: "*.py" will match "foo.py" but not "foo/bar.py". If + 'anchor' is false, both of these will match. + + If 'prefix' is supplied, then only filenames starting with 'prefix' + (itself a pattern) and ending with 'pattern', with anything in between + them, will match. 'anchor' is ignored in this case. + + If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and + 'pattern' is assumed to be either a string containing a regex or a + regex object -- no translation is done, the regex is just compiled + and used as-is. + + Selected strings will be added to self.files. + + Return True if files are found, False otherwise. + """ + # XXX docstring lying about what the special chars are? + files_found = False + pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) + self.debug_print("include_pattern: applying regex r'%s'" % + pattern_re.pattern) + + # delayed loading of allfiles list + if self.allfiles is None: + self.findall() + + for name in self.allfiles: + if pattern_re.search(name): + self.debug_print(" adding " + name) + self.files.append(name) + files_found = True + return files_found + + + def exclude_pattern (self, pattern, + anchor=1, prefix=None, is_regex=0): + """Remove strings (presumably filenames) from 'files' that match + 'pattern'. Other parameters are the same as for + 'include_pattern()', above. + The list 'self.files' is modified in place. + Return True if files are found, False otherwise. + """ + files_found = False + pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) + self.debug_print("exclude_pattern: applying regex r'%s'" % + pattern_re.pattern) + for i in range(len(self.files)-1, -1, -1): + if pattern_re.search(self.files[i]): + self.debug_print(" removing " + self.files[i]) + del self.files[i] + files_found = True + return files_found + + +# ---------------------------------------------------------------------- +# Utility functions + +def _find_all_simple(path): + """ + Find all files under 'path' + """ + results = ( + os.path.join(base, file) + for base, dirs, files in os.walk(path, followlinks=True) + for file in files + ) + return filter(os.path.isfile, results) + + +def findall(dir=os.curdir): + """ + Find all files under 'dir' and return the list of full filenames. + Unless dir is '.', return full filenames with dir prepended. + """ + files = _find_all_simple(dir) + if dir == os.curdir: + make_rel = functools.partial(os.path.relpath, start=dir) + files = map(make_rel, files) + return list(files) + + +def glob_to_re(pattern): + """Translate a shell-like glob pattern to a regular expression; return + a string containing the regex. Differs from 'fnmatch.translate()' in + that '*' does not match "special characters" (which are + platform-specific). + """ + pattern_re = fnmatch.translate(pattern) + + # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which + # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, + # and by extension they shouldn't match such "special characters" under + # any OS. So change all non-escaped dots in the RE to match any + # character except the special characters (currently: just os.sep). + sep = os.sep + if os.sep == '\\': + # we're using a regex to manipulate a regex, so we need + # to escape the backslash twice + sep = r'\\\\' + escaped = r'\1[^%s]' % sep + pattern_re = re.sub(r'((?= self.threshold: + if args: + msg = msg % args + if level in (WARN, ERROR, FATAL): + stream = sys.stderr + else: + stream = sys.stdout + try: + stream.write('%s\n' % msg) + except UnicodeEncodeError: + # emulate backslashreplace error handler + encoding = stream.encoding + msg = msg.encode(encoding, "backslashreplace").decode(encoding) + stream.write('%s\n' % msg) + stream.flush() + + def log(self, level, msg, *args): + self._log(level, msg, args) + + def debug(self, msg, *args): + self._log(DEBUG, msg, args) + + def info(self, msg, *args): + self._log(INFO, msg, args) + + def warn(self, msg, *args): + self._log(WARN, msg, args) + + def error(self, msg, *args): + self._log(ERROR, msg, args) + + def fatal(self, msg, *args): + self._log(FATAL, msg, args) + +_global_log = Log() +log = _global_log.log +debug = _global_log.debug +info = _global_log.info +warn = _global_log.warn +error = _global_log.error +fatal = _global_log.fatal + +def set_threshold(level): + # return the old threshold for use from tests + old = _global_log.threshold + _global_log.threshold = level + return old + +def set_verbosity(v): + if v <= 0: + set_threshold(WARN) + elif v == 1: + set_threshold(INFO) + elif v >= 2: + set_threshold(DEBUG) diff --git a/distutils/msvc9compiler.py b/distutils/msvc9compiler.py new file mode 100644 index 00000000..6934e964 --- /dev/null +++ b/distutils/msvc9compiler.py @@ -0,0 +1,788 @@ +"""distutils.msvc9compiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for the Microsoft Visual Studio 2008. + +The module is compatible with VS 2005 and VS 2008. You can find legacy support +for older versions of VS in distutils.msvccompiler. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) +# ported to VS2005 and VS 2008 by Christian Heimes + +import os +import subprocess +import sys +import re + +from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import CCompiler, gen_lib_options +from distutils import log +from distutils.util import get_platform + +import winreg + +RegOpenKeyEx = winreg.OpenKeyEx +RegEnumKey = winreg.EnumKey +RegEnumValue = winreg.EnumValue +RegError = winreg.error + +HKEYS = (winreg.HKEY_USERS, + winreg.HKEY_CURRENT_USER, + winreg.HKEY_LOCAL_MACHINE, + winreg.HKEY_CLASSES_ROOT) + +NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32) +if NATIVE_WIN64: + # Visual C++ is a 32-bit application, so we need to look in + # the corresponding registry branch, if we're running a + # 64-bit Python on Win64 + VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" +else: + VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Microsoft\.NETFramework" + +# A map keyed by get_platform() return values to values accepted by +# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is +# the param to cross-compile on x86 targeting amd64.) +PLAT_TO_VCVARS = { + 'win32' : 'x86', + 'win-amd64' : 'amd64', +} + +class Reg: + """Helper class to read values from the registry + """ + + def get_value(cls, path, key): + for base in HKEYS: + d = cls.read_values(base, path) + if d and key in d: + return d[key] + raise KeyError(key) + get_value = classmethod(get_value) + + def read_keys(cls, base, key): + """Return list of registry keys.""" + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + L = [] + i = 0 + while True: + try: + k = RegEnumKey(handle, i) + except RegError: + break + L.append(k) + i += 1 + return L + read_keys = classmethod(read_keys) + + def read_values(cls, base, key): + """Return dict of registry keys and values. + + All names are converted to lowercase. + """ + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + d = {} + i = 0 + while True: + try: + name, value, type = RegEnumValue(handle, i) + except RegError: + break + name = name.lower() + d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) + i += 1 + return d + read_values = classmethod(read_values) + + def convert_mbcs(s): + dec = getattr(s, "decode", None) + if dec is not None: + try: + s = dec("mbcs") + except UnicodeError: + pass + return s + convert_mbcs = staticmethod(convert_mbcs) + +class MacroExpander: + + def __init__(self, version): + self.macros = {} + self.vsbase = VS_BASE % version + self.load_macros(version) + + def set_macro(self, macro, path, key): + self.macros["$(%s)" % macro] = Reg.get_value(path, key) + + def load_macros(self, version): + self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") + self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") + self.set_macro("FrameworkDir", NET_BASE, "installroot") + try: + if version >= 8.0: + self.set_macro("FrameworkSDKDir", NET_BASE, + "sdkinstallrootv2.0") + else: + raise KeyError("sdkinstallrootv2.0") + except KeyError: + raise DistutilsPlatformError( + """Python was built with Visual Studio 2008; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2008 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") + + if version >= 9.0: + self.set_macro("FrameworkVersion", self.vsbase, "clr version") + self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") + else: + p = r"Software\Microsoft\NET Framework Setup\Product" + for base in HKEYS: + try: + h = RegOpenKeyEx(base, p) + except RegError: + continue + key = RegEnumKey(h, 0) + d = Reg.get_value(base, r"%s\%s" % (p, key)) + self.macros["$(FrameworkVersion)"] = d["version"] + + def sub(self, s): + for k, v in self.macros.items(): + s = s.replace(k, v) + return s + +def get_build_version(): + """Return the version of MSVC that was used to build Python. + + For Python 2.3 and up, the version number is included in + sys.version. For earlier versions, assume the compiler is MSVC 6. + """ + prefix = "MSC v." + i = sys.version.find(prefix) + if i == -1: + return 6 + i = i + len(prefix) + s, rest = sys.version[i:].split(" ", 1) + majorVersion = int(s[:-2]) - 6 + if majorVersion >= 13: + # v13 was skipped and should be v14 + majorVersion += 1 + minorVersion = int(s[2:3]) / 10.0 + # I don't think paths are affected by minor version in version 6 + if majorVersion == 6: + minorVersion = 0 + if majorVersion >= 6: + return majorVersion + minorVersion + # else we don't know what version of the compiler this is + return None + +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths + +def removeDuplicates(variable): + """Remove duplicate values of an environment variable. + """ + oldList = variable.split(os.pathsep) + newList = [] + for i in oldList: + if i not in newList: + newList.append(i) + newVariable = os.pathsep.join(newList) + return newVariable + +def find_vcvarsall(version): + """Find the vcvarsall.bat file + + At first it tries to find the productdir of VS 2008 in the registry. If + that fails it falls back to the VS90COMNTOOLS env var. + """ + vsbase = VS_BASE % version + try: + productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, + "productdir") + except KeyError: + log.debug("Unable to find productdir in registry") + productdir = None + + if not productdir or not os.path.isdir(productdir): + toolskey = "VS%0.f0COMNTOOLS" % version + toolsdir = os.environ.get(toolskey, None) + + if toolsdir and os.path.isdir(toolsdir): + productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") + productdir = os.path.abspath(productdir) + if not os.path.isdir(productdir): + log.debug("%s is not a valid directory" % productdir) + return None + else: + log.debug("Env var %s is not set or invalid" % toolskey) + if not productdir: + log.debug("No productdir found") + return None + vcvarsall = os.path.join(productdir, "vcvarsall.bat") + if os.path.isfile(vcvarsall): + return vcvarsall + log.debug("Unable to find vcvarsall.bat") + return None + +def query_vcvarsall(version, arch="x86"): + """Launch vcvarsall.bat and read the settings from its environment + """ + vcvarsall = find_vcvarsall(version) + interesting = {"include", "lib", "libpath", "path"} + result = {} + + if vcvarsall is None: + raise DistutilsPlatformError("Unable to find vcvarsall.bat") + log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) + popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + try: + stdout, stderr = popen.communicate() + if popen.wait() != 0: + raise DistutilsPlatformError(stderr.decode("mbcs")) + + stdout = stdout.decode("mbcs") + for line in stdout.split("\n"): + line = Reg.convert_mbcs(line) + if '=' not in line: + continue + line = line.strip() + key, value = line.split('=', 1) + key = key.lower() + if key in interesting: + if value.endswith(os.pathsep): + value = value[:-1] + result[key] = removeDuplicates(value) + + finally: + popen.stdout.close() + popen.stderr.close() + + if len(result) != len(interesting): + raise ValueError(str(list(result.keys()))) + + return result + +# More globals +VERSION = get_build_version() +if VERSION < 8.0: + raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) +# MACROS = MacroExpander(VERSION) + +class MSVCCompiler(CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) + self.__version = VERSION + self.__root = r"Software\Microsoft\VisualStudio" + # self.__macros = MACROS + self.__paths = [] + # target platform (.plat_name is consistent with 'bdist') + self.plat_name = None + self.__arch = None # deprecated name + self.initialized = False + + def initialize(self, plat_name=None): + # multi-init means we would need to check platform same each time... + assert not self.initialized, "don't init multiple times" + if plat_name is None: + plat_name = get_platform() + # sanity check for platforms to prevent obscure errors later. + ok_plats = 'win32', 'win-amd64' + if plat_name not in ok_plats: + raise DistutilsPlatformError("--plat-name must be one of %s" % + (ok_plats,)) + + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): + # Assume that the SDK set up everything alright; don't try to be + # smarter + self.cc = "cl.exe" + self.linker = "link.exe" + self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" + else: + # On x86, 'vcvars32.bat amd64' creates an env that doesn't work; + # to cross compile, you use 'x86_amd64'. + # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross + # compile use 'x86' (ie, it runs the x86 compiler directly) + if plat_name == get_platform() or plat_name == 'win32': + # native build or cross-compile to win32 + plat_spec = PLAT_TO_VCVARS[plat_name] + else: + # cross compile from win32 -> some 64bit + plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ + PLAT_TO_VCVARS[plat_name] + + vc_env = query_vcvarsall(VERSION, plat_spec) + + self.__paths = vc_env['path'].split(os.pathsep) + os.environ['lib'] = vc_env['lib'] + os.environ['include'] = vc_env['include'] + + if len(self.__paths) == 0: + raise DistutilsPlatformError("Python was built with %s, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." + % self.__product) + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + #self.set_path_env_var('lib') + #self.set_path_env_var('include') + + # extend the MSVC path with the current path + try: + for p in os.environ['path'].split(';'): + self.__paths.append(p) + except KeyError: + pass + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) + + self.preprocess_options = None + if self.__arch == "x86": + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', + '/Z7', '/D_DEBUG'] + else: + # Win64 + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', + '/Z7', '/D_DEBUG'] + + self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] + if self.__version >= 7: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' + ] + self.ldflags_static = [ '/nologo'] + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): + # Copied from ccompiler.py, extended to return .res as 'object'-file + # for .rc input file + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext (src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / + if ext not in self.src_extensions: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError ("Don't know how to compile %s" % src_name) + if strip_dir: + base = os.path.basename (base) + if ext in self._rc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + elif ext in self._mc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append ('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + + [output_opt] + [input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc] + + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext (os.path.basename (src)) + rc_file = os.path.join (rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc] + + ["/fo" + obj] + [rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError("Don't know how to compile %s to %s" + % (src, obj)) + + output_opt = "/Fo" + obj + try: + self.spawn([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + (libraries, library_dirs, runtime_library_dirs) = fixed_args + + if runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) + + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + if target_desc == CCompiler.EXECUTABLE: + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + else: + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared + + export_opts = [] + for sym in (export_symbols or []): + export_opts.append("/EXPORT:" + sym) + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + build_temp = os.path.dirname(objects[0]) + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + build_temp, + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + + self.manifest_setup_ldargs(output_filename, build_temp, ld_args) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath(os.path.dirname(output_filename)) + try: + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + # embed the manifest + # XXX - this is somewhat fragile - if mt.exe fails, distutils + # will still consider the DLL up-to-date, but it will not have a + # manifest. Maybe we should link to a temp file? OTOH, that + # implies a build environment error that shouldn't go undetected. + mfinfo = self.manifest_get_embed_info(target_desc, ld_args) + if mfinfo is not None: + mffilename, mfid = mfinfo + out_arg = '-outputresource:%s;%s' % (output_filename, mfid) + try: + self.spawn(['mt.exe', '-nologo', '-manifest', + mffilename, out_arg]) + except DistutilsExecError as msg: + raise LinkError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): + # If we need a manifest at all, an embedded manifest is recommended. + # See MSDN article titled + # "How to: Embed a Manifest Inside a C/C++ Application" + # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) + # Ask the linker to generate the manifest in the temp dir, so + # we can check it, and possibly embed it, later. + temp_manifest = os.path.join( + build_temp, + os.path.basename(output_filename) + ".manifest") + ld_args.append('/MANIFESTFILE:' + temp_manifest) + + def manifest_get_embed_info(self, target_desc, ld_args): + # If a manifest should be embedded, return a tuple of + # (manifest_filename, resource_id). Returns None if no manifest + # should be embedded. See http://bugs.python.org/issue7833 for why + # we want to avoid any manifest for extension modules if we can) + for arg in ld_args: + if arg.startswith("/MANIFESTFILE:"): + temp_manifest = arg.split(":", 1)[1] + break + else: + # no /MANIFESTFILE so nothing to do. + return None + if target_desc == CCompiler.EXECUTABLE: + # by default, executables always get the manifest with the + # CRT referenced. + mfid = 1 + else: + # Extension modules try and avoid any manifest if possible. + mfid = 2 + temp_manifest = self._remove_visual_c_ref(temp_manifest) + if temp_manifest is None: + return None + return temp_manifest, mfid + + def _remove_visual_c_ref(self, manifest_file): + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + # Returns either the filename of the modified manifest or + # None if no manifest should be embedded. + manifest_f = open(manifest_file) + try: + manifest_buf = manifest_f.read() + finally: + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = r"\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + # Now see if any other assemblies are referenced - if not, we + # don't want a manifest embedded. + pattern = re.compile( + r"""|)""", re.DOTALL) + if re.search(pattern, manifest_buf) is None: + return None + + manifest_f = open(manifest_file, 'w') + try: + manifest_f.write(manifest_buf) + return manifest_file + finally: + manifest_f.close() + except OSError: + pass + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC++") + + def library_option(self, lib): + return self.library_filename(lib) + + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename (name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # Helper methods for using the MSVC registry settings + + def find_exe(self, exe): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + for p in self.__paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in os.environ['Path'].split(';'): + fn = os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): + return fn + + return exe diff --git a/distutils/msvccompiler.py b/distutils/msvccompiler.py new file mode 100644 index 00000000..d5857cb1 --- /dev/null +++ b/distutils/msvccompiler.py @@ -0,0 +1,643 @@ +"""distutils.msvccompiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for the Microsoft Visual Studio. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) + +import sys, os +from distutils.errors import \ + DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import \ + CCompiler, gen_lib_options +from distutils import log + +_can_read_reg = False +try: + import winreg + + _can_read_reg = True + hkey_mod = winreg + + RegOpenKeyEx = winreg.OpenKeyEx + RegEnumKey = winreg.EnumKey + RegEnumValue = winreg.EnumValue + RegError = winreg.error + +except ImportError: + try: + import win32api + import win32con + _can_read_reg = True + hkey_mod = win32con + + RegOpenKeyEx = win32api.RegOpenKeyEx + RegEnumKey = win32api.RegEnumKey + RegEnumValue = win32api.RegEnumValue + RegError = win32api.error + except ImportError: + log.info("Warning: Can't read registry to find the " + "necessary compiler setting\n" + "Make sure that Python modules winreg, " + "win32api or win32con are installed.") + pass + +if _can_read_reg: + HKEYS = (hkey_mod.HKEY_USERS, + hkey_mod.HKEY_CURRENT_USER, + hkey_mod.HKEY_LOCAL_MACHINE, + hkey_mod.HKEY_CLASSES_ROOT) + +def read_keys(base, key): + """Return list of registry keys.""" + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + L = [] + i = 0 + while True: + try: + k = RegEnumKey(handle, i) + except RegError: + break + L.append(k) + i += 1 + return L + +def read_values(base, key): + """Return dict of registry keys and values. + + All names are converted to lowercase. + """ + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + d = {} + i = 0 + while True: + try: + name, value, type = RegEnumValue(handle, i) + except RegError: + break + name = name.lower() + d[convert_mbcs(name)] = convert_mbcs(value) + i += 1 + return d + +def convert_mbcs(s): + dec = getattr(s, "decode", None) + if dec is not None: + try: + s = dec("mbcs") + except UnicodeError: + pass + return s + +class MacroExpander: + def __init__(self, version): + self.macros = {} + self.load_macros(version) + + def set_macro(self, macro, path, key): + for base in HKEYS: + d = read_values(base, path) + if d: + self.macros["$(%s)" % macro] = d[key] + break + + def load_macros(self, version): + vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version + self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") + self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") + net = r"Software\Microsoft\.NETFramework" + self.set_macro("FrameworkDir", net, "installroot") + try: + if version > 7.0: + self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") + else: + self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") + except KeyError as exc: # + raise DistutilsPlatformError( + """Python was built with Visual Studio 2003; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2003 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") + + p = r"Software\Microsoft\NET Framework Setup\Product" + for base in HKEYS: + try: + h = RegOpenKeyEx(base, p) + except RegError: + continue + key = RegEnumKey(h, 0) + d = read_values(base, r"%s\%s" % (p, key)) + self.macros["$(FrameworkVersion)"] = d["version"] + + def sub(self, s): + for k, v in self.macros.items(): + s = s.replace(k, v) + return s + +def get_build_version(): + """Return the version of MSVC that was used to build Python. + + For Python 2.3 and up, the version number is included in + sys.version. For earlier versions, assume the compiler is MSVC 6. + """ + prefix = "MSC v." + i = sys.version.find(prefix) + if i == -1: + return 6 + i = i + len(prefix) + s, rest = sys.version[i:].split(" ", 1) + majorVersion = int(s[:-2]) - 6 + if majorVersion >= 13: + # v13 was skipped and should be v14 + majorVersion += 1 + minorVersion = int(s[2:3]) / 10.0 + # I don't think paths are affected by minor version in version 6 + if majorVersion == 6: + minorVersion = 0 + if majorVersion >= 6: + return majorVersion + minorVersion + # else we don't know what version of the compiler this is + return None + +def get_build_architecture(): + """Return the processor architecture. + + Possible results are "Intel" or "AMD64". + """ + + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return "Intel" + j = sys.version.find(")", i) + return sys.version[i+len(prefix):j] + +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths + + +class MSVCCompiler(CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) + self.__version = get_build_version() + self.__arch = get_build_architecture() + if self.__arch == "Intel": + # x86 + if self.__version >= 7: + self.__root = r"Software\Microsoft\VisualStudio" + self.__macros = MacroExpander(self.__version) + else: + self.__root = r"Software\Microsoft\Devstudio" + self.__product = "Visual Studio version %s" % self.__version + else: + # Win64. Assume this was built with the platform SDK + self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) + + self.initialized = False + + def initialize(self): + self.__paths = [] + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): + # Assume that the SDK set up everything alright; don't try to be + # smarter + self.cc = "cl.exe" + self.linker = "link.exe" + self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" + else: + self.__paths = self.get_msvc_paths("path") + + if len(self.__paths) == 0: + raise DistutilsPlatformError("Python was built with %s, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." + % self.__product) + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + self.set_path_env_var('lib') + self.set_path_env_var('include') + + # extend the MSVC path with the current path + try: + for p in os.environ['path'].split(';'): + self.__paths.append(p) + except KeyError: + pass + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) + + self.preprocess_options = None + if self.__arch == "Intel": + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', + '/Z7', '/D_DEBUG'] + else: + # Win64 + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', + '/Z7', '/D_DEBUG'] + + self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] + if self.__version >= 7: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' + ] + else: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG' + ] + self.ldflags_static = [ '/nologo'] + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): + # Copied from ccompiler.py, extended to return .res as 'object'-file + # for .rc input file + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext (src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / + if ext not in self.src_extensions: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError ("Don't know how to compile %s" % src_name) + if strip_dir: + base = os.path.basename (base) + if ext in self._rc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + elif ext in self._mc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append ('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + + [output_opt] + [input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc] + + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext (os.path.basename (src)) + rc_file = os.path.join (rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc] + + ["/fo" + obj] + [rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError("Don't know how to compile %s to %s" + % (src, obj)) + + output_opt = "/Fo" + obj + try: + self.spawn([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + (libraries, library_dirs, runtime_library_dirs) = fixed_args + + if runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) + + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + if target_desc == CCompiler.EXECUTABLE: + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + else: + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared + + export_opts = [] + for sym in (export_symbols or []): + export_opts.append("/EXPORT:" + sym) + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + os.path.dirname(objects[0]), + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath(os.path.dirname(output_filename)) + try: + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC++") + + def library_option(self, lib): + return self.library_filename(lib) + + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename (name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # Helper methods for using the MSVC registry settings + + def find_exe(self, exe): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + for p in self.__paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in os.environ['Path'].split(';'): + fn = os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): + return fn + + return exe + + def get_msvc_paths(self, path, platform='x86'): + """Get a list of devstudio directories (include, lib or path). + + Return a list of strings. The list will be empty if unable to + access the registry or appropriate registry keys not found. + """ + if not _can_read_reg: + return [] + + path = path + " dirs" + if self.__version >= 7: + key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" + % (self.__root, self.__version)) + else: + key = (r"%s\6.0\Build System\Components\Platforms" + r"\Win32 (%s)\Directories" % (self.__root, platform)) + + for base in HKEYS: + d = read_values(base, key) + if d: + if self.__version >= 7: + return self.__macros.sub(d[path]).split(";") + else: + return d[path].split(";") + # MSVC 6 seems to create the registry entries we need only when + # the GUI is run. + if self.__version == 6: + for base in HKEYS: + if read_values(base, r"%s\6.0" % self.__root) is not None: + self.warn("It seems you have Visual Studio 6 installed, " + "but the expected registry settings are not present.\n" + "You must at least run the Visual Studio GUI once " + "so that these entries are created.") + break + return [] + + def set_path_env_var(self, name): + """Set environment variable 'name' to an MSVC path type value. + + This is equivalent to a SET command prior to execution of spawned + commands. + """ + + if name == "lib": + p = self.get_msvc_paths("library") + else: + p = self.get_msvc_paths(name) + if p: + os.environ[name] = ';'.join(p) + + +if get_build_version() >= 8.0: + log.debug("Importing new compiler from distutils.msvc9compiler") + OldMSVCCompiler = MSVCCompiler + from distutils.msvc9compiler import MSVCCompiler + # get_build_architecture not really relevant now we support cross-compile + from distutils.msvc9compiler import MacroExpander diff --git a/distutils/spawn.py b/distutils/spawn.py new file mode 100644 index 00000000..aad277b0 --- /dev/null +++ b/distutils/spawn.py @@ -0,0 +1,119 @@ +"""distutils.spawn + +Provides the 'spawn()' function, a front-end to various platform- +specific functions for launching another program in a sub-process. +Also provides the 'find_executable()' to search the path for a given +executable name. +""" + +import sys +import os +import subprocess + +from distutils.errors import DistutilsPlatformError, DistutilsExecError +from distutils.debug import DEBUG +from distutils import log + + +if sys.platform == 'darwin': + _cfg_target = None + _cfg_target_split = None + + +def spawn(cmd, search_path=1, verbose=0, dry_run=0): + """Run another program, specified as a command list 'cmd', in a new process. + + 'cmd' is just the argument list for the new process, ie. + cmd[0] is the program to run and cmd[1:] are the rest of its arguments. + There is no way to run a program with a name different from that of its + executable. + + If 'search_path' is true (the default), the system's executable + search path will be used to find the program; otherwise, cmd[0] + must be the exact path to the executable. If 'dry_run' is true, + the command will not actually be run. + + Raise DistutilsExecError if running the program fails in any way; just + return on success. + """ + # cmd is documented as a list, but just in case some code passes a tuple + # in, protect our %-formatting code against horrible death + cmd = list(cmd) + + log.info(' '.join(cmd)) + if dry_run: + return + + if search_path: + executable = find_executable(cmd[0]) + if executable is not None: + cmd[0] = executable + + env = None + if sys.platform == 'darwin': + global _cfg_target, _cfg_target_split + if _cfg_target is None: + from distutils import sysconfig + _cfg_target = sysconfig.get_config_var( + 'MACOSX_DEPLOYMENT_TARGET') or '' + if _cfg_target: + _cfg_target_split = [int(x) for x in _cfg_target.split('.')] + if _cfg_target: + # ensure that the deployment target of build process is not less + # than that used when the interpreter was built. This ensures + # extension modules are built with correct compatibility values + cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target) + if _cfg_target_split > [int(x) for x in cur_target.split('.')]: + my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: ' + 'now "%s" but "%s" during configure' + % (cur_target, _cfg_target)) + raise DistutilsPlatformError(my_msg) + env = dict(os.environ, + MACOSX_DEPLOYMENT_TARGET=cur_target) + + proc = subprocess.Popen(cmd, env=env) + proc.wait() + exitcode = proc.returncode + + if exitcode: + if not DEBUG: + cmd = cmd[0] + raise DistutilsExecError( + "command %r failed with exit code %s" % (cmd, exitcode)) + + +def find_executable(executable, path=None): + """Tries to find 'executable' in the directories listed in 'path'. + + A string listing directories separated by 'os.pathsep'; defaults to + os.environ['PATH']. Returns the complete filename or None if not found. + """ + _, ext = os.path.splitext(executable) + if (sys.platform == 'win32') and (ext != '.exe'): + executable = executable + '.exe' + + if os.path.isfile(executable): + return executable + + if path is None: + path = os.environ.get('PATH', None) + if path is None: + try: + path = os.confstr("CS_PATH") + except (AttributeError, ValueError): + # os.confstr() or CS_PATH is not available + path = os.defpath + # bpo-35755: Don't use os.defpath if the PATH environment variable is + # set to an empty string + + # PATH='' doesn't match, whereas PATH=':' looks in the current directory + if not path: + return None + + paths = path.split(os.pathsep) + for p in paths: + f = os.path.join(p, executable) + if os.path.isfile(f): + # the file exists, we have a shot at spawn working + return f + return None diff --git a/distutils/sysconfig.py b/distutils/sysconfig.py new file mode 100644 index 00000000..37feae5d --- /dev/null +++ b/distutils/sysconfig.py @@ -0,0 +1,555 @@ +"""Provide access to Python's configuration information. The specific +configuration variables available depend heavily on the platform and +configuration. The values may be retrieved using +get_config_var(name), and the list of variables is available via +get_config_vars().keys(). Additional convenience functions are also +available. + +Written by: Fred L. Drake, Jr. +Email: +""" + +import _imp +import os +import re +import sys + +from .errors import DistutilsPlatformError + +# These are needed in a couple of spots, so just compute them once. +PREFIX = os.path.normpath(sys.prefix) +EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +BASE_PREFIX = os.path.normpath(sys.base_prefix) +BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) + +# Path to the base directory of the project. On Windows the binary may +# live in project/PCbuild/win32 or project/PCbuild/amd64. +# set for cross builds +if "_PYTHON_PROJECT_BASE" in os.environ: + project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) +else: + if sys.executable: + project_base = os.path.dirname(os.path.abspath(sys.executable)) + else: + # sys.executable can be empty if argv[0] has been changed and Python is + # unable to retrieve the real program name + project_base = os.getcwd() + + +# python_build: (Boolean) if true, we're either building Python or +# building an extension with an un-installed Python, so we use +# different (hard-wired) directories. +def _is_python_source_dir(d): + for fn in ("Setup", "Setup.local"): + if os.path.isfile(os.path.join(d, "Modules", fn)): + return True + return False + +_sys_home = getattr(sys, '_home', None) + +if os.name == 'nt': + def _fix_pcbuild(d): + if d and os.path.normcase(d).startswith( + os.path.normcase(os.path.join(PREFIX, "PCbuild"))): + return PREFIX + return d + project_base = _fix_pcbuild(project_base) + _sys_home = _fix_pcbuild(_sys_home) + +def _python_build(): + if _sys_home: + return _is_python_source_dir(_sys_home) + return _is_python_source_dir(project_base) + +python_build = _python_build() + + +# Calculate the build qualifier flags if they are defined. Adding the flags +# to the include and lib directories only makes sense for an installation, not +# an in-source build. +build_flags = '' +try: + if not python_build: + build_flags = sys.abiflags +except AttributeError: + # It's not a configure-based build, so the sys module doesn't have + # this attribute, which is fine. + pass + +def get_python_version(): + """Return a string containing the major and minor Python version, + leaving off the patchlevel. Sample return values could be '1.5' + or '2.2'. + """ + return '%d.%d' % sys.version_info[:2] + + +def get_python_inc(plat_specific=0, prefix=None): + """Return the directory containing installed Python header files. + + If 'plat_specific' is false (the default), this is the path to the + non-platform-specific header files, i.e. Python.h and so on; + otherwise, this is the path to platform-specific header files + (namely pyconfig.h). + + If 'prefix' is supplied, use it instead of sys.base_prefix or + sys.base_exec_prefix -- i.e., ignore 'plat_specific'. + """ + if prefix is None: + prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX + if os.name == "posix": + if python_build: + # Assume the executable is in the build directory. The + # pyconfig.h file should be in the same directory. Since + # the build directory may not be the source directory, we + # must use "srcdir" from the makefile to find the "Include" + # directory. + if plat_specific: + return _sys_home or project_base + else: + incdir = os.path.join(get_config_var('srcdir'), 'Include') + return os.path.normpath(incdir) + python_dir = 'python' + get_python_version() + build_flags + return os.path.join(prefix, "include", python_dir) + elif os.name == "nt": + if python_build: + # Include both the include and PC dir to ensure we can find + # pyconfig.h + return (os.path.join(prefix, "include") + os.path.pathsep + + os.path.join(prefix, "PC")) + return os.path.join(prefix, "include") + else: + raise DistutilsPlatformError( + "I don't know where Python installs its C header files " + "on platform '%s'" % os.name) + + +def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): + """Return the directory containing the Python library (standard or + site additions). + + If 'plat_specific' is true, return the directory containing + platform-specific modules, i.e. any module from a non-pure-Python + module distribution; otherwise, return the platform-shared library + directory. If 'standard_lib' is true, return the directory + containing standard Python library modules; otherwise, return the + directory for site-specific modules. + + If 'prefix' is supplied, use it instead of sys.base_prefix or + sys.base_exec_prefix -- i.e., ignore 'plat_specific'. + """ + if prefix is None: + if standard_lib: + prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX + else: + prefix = plat_specific and EXEC_PREFIX or PREFIX + + if os.name == "posix": + if plat_specific or standard_lib: + # Platform-specific modules (any module from a non-pure-Python + # module distribution) or standard Python library modules. + libdir = sys.platlibdir + else: + # Pure Python + libdir = "lib" + libpython = os.path.join(prefix, libdir, + "python" + get_python_version()) + if standard_lib: + return libpython + else: + return os.path.join(libpython, "site-packages") + elif os.name == "nt": + if standard_lib: + return os.path.join(prefix, "Lib") + else: + return os.path.join(prefix, "Lib", "site-packages") + else: + raise DistutilsPlatformError( + "I don't know where Python installs its library " + "on platform '%s'" % os.name) + + + +def customize_compiler(compiler): + """Do any platform-specific customization of a CCompiler instance. + + Mainly needed on Unix, so we can plug in the information that + varies across Unices and is stored in Python's Makefile. + """ + if compiler.compiler_type == "unix": + if sys.platform == "darwin": + # Perform first-time customization of compiler-related + # config vars on OS X now that we know we need a compiler. + # This is primarily to support Pythons from binary + # installers. The kind and paths to build tools on + # the user system may vary significantly from the system + # that Python itself was built on. Also the user OS + # version and build tools may not support the same set + # of CPU architectures for universal builds. + global _config_vars + # Use get_config_var() to ensure _config_vars is initialized. + if not get_config_var('CUSTOMIZED_OSX_COMPILER'): + import _osx_support + _osx_support.customize_compiler(_config_vars) + _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' + + (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ + get_config_vars('CC', 'CXX', 'CFLAGS', + 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') + + if 'CC' in os.environ: + newcc = os.environ['CC'] + if (sys.platform == 'darwin' + and 'LDSHARED' not in os.environ + and ldshared.startswith(cc)): + # On OS X, if CC is overridden, use that as the default + # command for LDSHARED as well + ldshared = newcc + ldshared[len(cc):] + cc = newcc + if 'CXX' in os.environ: + cxx = os.environ['CXX'] + if 'LDSHARED' in os.environ: + ldshared = os.environ['LDSHARED'] + if 'CPP' in os.environ: + cpp = os.environ['CPP'] + else: + cpp = cc + " -E" # not always + if 'LDFLAGS' in os.environ: + ldshared = ldshared + ' ' + os.environ['LDFLAGS'] + if 'CFLAGS' in os.environ: + cflags = cflags + ' ' + os.environ['CFLAGS'] + ldshared = ldshared + ' ' + os.environ['CFLAGS'] + if 'CPPFLAGS' in os.environ: + cpp = cpp + ' ' + os.environ['CPPFLAGS'] + cflags = cflags + ' ' + os.environ['CPPFLAGS'] + ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] + else: + archiver = ar + ' ' + ar_flags + + cc_cmd = cc + ' ' + cflags + compiler.set_executables( + preprocessor=cpp, + compiler=cc_cmd, + compiler_so=cc_cmd + ' ' + ccshared, + compiler_cxx=cxx, + linker_so=ldshared, + linker_exe=cc, + archiver=archiver) + + compiler.shared_lib_extension = shlib_suffix + + +def get_config_h_filename(): + """Return full pathname of installed pyconfig.h file.""" + if python_build: + if os.name == "nt": + inc_dir = os.path.join(_sys_home or project_base, "PC") + else: + inc_dir = _sys_home or project_base + else: + inc_dir = get_python_inc(plat_specific=1) + + return os.path.join(inc_dir, 'pyconfig.h') + + +def get_makefile_filename(): + """Return full pathname of installed Makefile from the Python build.""" + if python_build: + return os.path.join(_sys_home or project_base, "Makefile") + lib_dir = get_python_lib(plat_specific=0, standard_lib=1) + config_file = 'config-{}{}'.format(get_python_version(), build_flags) + if hasattr(sys.implementation, '_multiarch'): + config_file += '-%s' % sys.implementation._multiarch + return os.path.join(lib_dir, config_file, 'Makefile') + + +def parse_config_h(fp, g=None): + """Parse a config.h-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + if g is None: + g = {} + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + # + while True: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + try: v = int(v) + except ValueError: pass + g[n] = v + else: + m = undef_rx.match(line) + if m: + g[m.group(1)] = 0 + return g + + +# Regexes needed for parsing Makefile (and similar syntaxes, +# like old-style Setup files). +_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") +_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") +_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + +def parse_makefile(fn, g=None): + """Parse a Makefile-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + from distutils.text_file import TextFile + fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape") + + if g is None: + g = {} + done = {} + notdone = {} + + while True: + line = fp.readline() + if line is None: # eof + break + m = _variable_rx.match(line) + if m: + n, v = m.group(1, 2) + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: + notdone[n] = v + else: + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v + + # Variables with a 'PY_' prefix in the makefile. These need to + # be made available without that prefix through sysconfig. + # Special care is needed to ensure that variable expansion works, even + # if the expansion uses the name without a prefix. + renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') + + # do variable interpolation here + while notdone: + for name in list(notdone): + value = notdone[name] + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) + if m: + n = m.group(1) + found = True + if n in done: + item = str(done[n]) + elif n in notdone: + # get it on a subsequent round + found = False + elif n in os.environ: + # do it like make: fall back to environment + item = os.environ[n] + + elif n in renamed_variables: + if name.startswith('PY_') and name[3:] in renamed_variables: + item = "" + + elif 'PY_' + n in notdone: + found = False + + else: + item = str(done['PY_' + n]) + else: + done[n] = item = "" + if found: + after = value[m.end():] + value = value[:m.start()] + item + after + if "$" in after: + notdone[name] = value + else: + try: value = int(value) + except ValueError: + done[name] = value.strip() + else: + done[name] = value + del notdone[name] + + if name.startswith('PY_') \ + and name[3:] in renamed_variables: + + name = name[3:] + if name not in done: + done[name] = value + else: + # bogus variable reference; just drop it since we can't deal + del notdone[name] + + fp.close() + + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + + # save the results in the global dictionary + g.update(done) + return g + + +def expand_makefile_vars(s, vars): + """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in + 'string' according to 'vars' (a dictionary mapping variable names to + values). Variables not present in 'vars' are silently expanded to the + empty string. The variable values in 'vars' should not contain further + variable expansions; if 'vars' is the output of 'parse_makefile()', + you're fine. Returns a variable-expanded version of 's'. + """ + + # This algorithm does multiple expansion, so if vars['foo'] contains + # "${bar}", it will expand ${foo} to ${bar}, and then expand + # ${bar}... and so forth. This is fine as long as 'vars' comes from + # 'parse_makefile()', which takes care of such expansions eagerly, + # according to make's variable expansion semantics. + + while True: + m = _findvar1_rx.search(s) or _findvar2_rx.search(s) + if m: + (beg, end) = m.span() + s = s[0:beg] + vars.get(m.group(1)) + s[end:] + else: + break + return s + + +_config_vars = None + +def _init_posix(): + """Initialize the module as appropriate for POSIX systems.""" + # _sysconfigdata is generated at build time, see the sysconfig module + name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME', + '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + abi=sys.abiflags, + platform=sys.platform, + multiarch=getattr(sys.implementation, '_multiarch', ''), + )) + _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + build_time_vars = _temp.build_time_vars + global _config_vars + _config_vars = {} + _config_vars.update(build_time_vars) + + +def _init_nt(): + """Initialize the module as appropriate for NT""" + g = {} + # set basic install directories + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) + + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = get_python_inc(plat_specific=0) + + g['EXT_SUFFIX'] = _imp.extension_suffixes()[0] + g['EXE'] = ".exe" + g['VERSION'] = get_python_version().replace(".", "") + g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) + + global _config_vars + _config_vars = g + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. Generally this includes + everything needed to build extensions and install both pure modules and + extensions. On Unix, this means every variable defined in Python's + installed Makefile; on Windows it's a much smaller set. + + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _config_vars + if _config_vars is None: + func = globals().get("_init_" + os.name) + if func: + func() + else: + _config_vars = {} + + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # Distutils. + _config_vars['prefix'] = PREFIX + _config_vars['exec_prefix'] = EXEC_PREFIX + + # For backward compatibility, see issue19555 + SO = _config_vars.get('EXT_SUFFIX') + if SO is not None: + _config_vars['SO'] = SO + + # Always convert srcdir to an absolute path + srcdir = _config_vars.get('srcdir', project_base) + if os.name == 'posix': + if python_build: + # If srcdir is a relative path (typically '.' or '..') + # then it should be interpreted relative to the directory + # containing Makefile. + base = os.path.dirname(get_makefile_filename()) + srcdir = os.path.join(base, srcdir) + else: + # srcdir is not meaningful since the installation is + # spread about the filesystem. We choose the + # directory containing the Makefile since we know it + # exists. + srcdir = os.path.dirname(get_makefile_filename()) + _config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir)) + + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if python_build and os.name == "posix": + base = project_base + if (not os.path.isabs(_config_vars['srcdir']) and + base != os.getcwd()): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _config_vars['srcdir']) + _config_vars['srcdir'] = os.path.normpath(srcdir) + + # OS X platforms require special customization to handle + # multi-architecture, multi-os-version installers + if sys.platform == 'darwin': + import _osx_support + _osx_support.customize_config_vars(_config_vars) + + if args: + vals = [] + for name in args: + vals.append(_config_vars.get(name)) + return vals + else: + return _config_vars + +def get_config_var(name): + """Return the value of a single variable using the dictionary + returned by 'get_config_vars()'. Equivalent to + get_config_vars().get(name) + """ + if name == 'SO': + import warnings + warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2) + return get_config_vars().get(name) diff --git a/distutils/tests/Setup.sample b/distutils/tests/Setup.sample new file mode 100644 index 00000000..36c4290d --- /dev/null +++ b/distutils/tests/Setup.sample @@ -0,0 +1,67 @@ +# Setup file from the pygame project + +#--StartConfig +SDL = -I/usr/include/SDL -D_REENTRANT -lSDL +FONT = -lSDL_ttf +IMAGE = -lSDL_image +MIXER = -lSDL_mixer +SMPEG = -lsmpeg +PNG = -lpng +JPEG = -ljpeg +SCRAP = -lX11 +PORTMIDI = -lportmidi +PORTTIME = -lporttime +#--EndConfig + +#DEBUG = -C-W -C-Wall +DEBUG = + +#the following modules are optional. you will want to compile +#everything you can, but you can ignore ones you don't have +#dependencies for, just comment them out + +imageext src/imageext.c $(SDL) $(IMAGE) $(PNG) $(JPEG) $(DEBUG) +font src/font.c $(SDL) $(FONT) $(DEBUG) +mixer src/mixer.c $(SDL) $(MIXER) $(DEBUG) +mixer_music src/music.c $(SDL) $(MIXER) $(DEBUG) +_numericsurfarray src/_numericsurfarray.c $(SDL) $(DEBUG) +_numericsndarray src/_numericsndarray.c $(SDL) $(MIXER) $(DEBUG) +movie src/movie.c $(SDL) $(SMPEG) $(DEBUG) +scrap src/scrap.c $(SDL) $(SCRAP) $(DEBUG) +_camera src/_camera.c src/camera_v4l2.c src/camera_v4l.c $(SDL) $(DEBUG) +pypm src/pypm.c $(SDL) $(PORTMIDI) $(PORTTIME) $(DEBUG) + +GFX = src/SDL_gfx/SDL_gfxPrimitives.c +#GFX = src/SDL_gfx/SDL_gfxBlitFunc.c src/SDL_gfx/SDL_gfxPrimitives.c +gfxdraw src/gfxdraw.c $(SDL) $(GFX) $(DEBUG) + + + +#these modules are required for pygame to run. they only require +#SDL as a dependency. these should not be altered + +base src/base.c $(SDL) $(DEBUG) +cdrom src/cdrom.c $(SDL) $(DEBUG) +color src/color.c $(SDL) $(DEBUG) +constants src/constants.c $(SDL) $(DEBUG) +display src/display.c $(SDL) $(DEBUG) +event src/event.c $(SDL) $(DEBUG) +fastevent src/fastevent.c src/fastevents.c $(SDL) $(DEBUG) +key src/key.c $(SDL) $(DEBUG) +mouse src/mouse.c $(SDL) $(DEBUG) +rect src/rect.c $(SDL) $(DEBUG) +rwobject src/rwobject.c $(SDL) $(DEBUG) +surface src/surface.c src/alphablit.c src/surface_fill.c $(SDL) $(DEBUG) +surflock src/surflock.c $(SDL) $(DEBUG) +time src/time.c $(SDL) $(DEBUG) +joystick src/joystick.c $(SDL) $(DEBUG) +draw src/draw.c $(SDL) $(DEBUG) +image src/image.c $(SDL) $(DEBUG) +overlay src/overlay.c $(SDL) $(DEBUG) +transform src/transform.c src/rotozoom.c src/scale2x.c src/scale_mmx.c $(SDL) $(DEBUG) +mask src/mask.c src/bitmask.c $(SDL) $(DEBUG) +bufferproxy src/bufferproxy.c $(SDL) $(DEBUG) +pixelarray src/pixelarray.c $(SDL) $(DEBUG) +_arraysurfarray src/_arraysurfarray.c $(SDL) $(DEBUG) + + diff --git a/distutils/tests/__init__.py b/distutils/tests/__init__.py new file mode 100644 index 00000000..5d2e69e3 --- /dev/null +++ b/distutils/tests/__init__.py @@ -0,0 +1,42 @@ +"""Test suite for distutils. + +This test suite consists of a collection of test modules in the +distutils.tests package. Each test module has a name starting with +'test' and contains a function test_suite(). The function is expected +to return an initialized unittest.TestSuite instance. + +Tests for the command classes in the distutils.command package are +included in distutils.tests as well, instead of using a separate +distutils.command.tests package, since command identification is done +by import rather than matching pre-defined names. + +""" + +import os +import sys +import unittest +import warnings +from test.support import run_unittest + + +here = os.path.dirname(__file__) or os.curdir + + +def test_suite(): + old_filters = warnings.filters[:] + suite = unittest.TestSuite() + for fn in os.listdir(here): + if fn.startswith("test") and fn.endswith(".py"): + modname = "distutils.tests." + fn[:-3] + __import__(modname) + module = sys.modules[modname] + suite.addTest(module.test_suite()) + # bpo-40055: Save/restore warnings filters to leave them unchanged. + # Importing tests imports docutils which imports pkg_resources which adds a + # warnings filter. + warnings.filters[:] = old_filters + return suite + + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/includetest.rst b/distutils/tests/includetest.rst new file mode 100644 index 00000000..d7b4ae38 --- /dev/null +++ b/distutils/tests/includetest.rst @@ -0,0 +1 @@ +This should be included. diff --git a/distutils/tests/support.py b/distutils/tests/support.py new file mode 100644 index 00000000..259af882 --- /dev/null +++ b/distutils/tests/support.py @@ -0,0 +1,209 @@ +"""Support code for distutils test cases.""" +import os +import sys +import shutil +import tempfile +import unittest +import sysconfig +from copy import deepcopy +import test.support + +from distutils import log +from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL +from distutils.core import Distribution + + +class LoggingSilencer(object): + + def setUp(self): + super().setUp() + self.threshold = log.set_threshold(log.FATAL) + # catching warnings + # when log will be replaced by logging + # we won't need such monkey-patch anymore + self._old_log = log.Log._log + log.Log._log = self._log + self.logs = [] + + def tearDown(self): + log.set_threshold(self.threshold) + log.Log._log = self._old_log + super().tearDown() + + def _log(self, level, msg, args): + if level not in (DEBUG, INFO, WARN, ERROR, FATAL): + raise ValueError('%s wrong log level' % str(level)) + if not isinstance(msg, str): + raise TypeError("msg should be str, not '%.200s'" + % (type(msg).__name__)) + self.logs.append((level, msg, args)) + + def get_logs(self, *levels): + return [msg % args for level, msg, args + in self.logs if level in levels] + + def clear_logs(self): + self.logs = [] + + +class TempdirManager(object): + """Mix-in class that handles temporary directories for test cases. + + This is intended to be used with unittest.TestCase. + """ + + def setUp(self): + super().setUp() + self.old_cwd = os.getcwd() + self.tempdirs = [] + + def tearDown(self): + # Restore working dir, for Solaris and derivatives, where rmdir() + # on the current directory fails. + os.chdir(self.old_cwd) + super().tearDown() + while self.tempdirs: + tmpdir = self.tempdirs.pop() + test.support.rmtree(tmpdir) + + def mkdtemp(self): + """Create a temporary directory that will be cleaned up. + + Returns the path of the directory. + """ + d = tempfile.mkdtemp() + self.tempdirs.append(d) + return d + + def write_file(self, path, content='xxx'): + """Writes a file in the given path. + + + path can be a string or a sequence. + """ + if isinstance(path, (list, tuple)): + path = os.path.join(*path) + f = open(path, 'w') + try: + f.write(content) + finally: + f.close() + + def create_dist(self, pkg_name='foo', **kw): + """Will generate a test environment. + + This function creates: + - a Distribution instance using keywords + - a temporary directory with a package structure + + It returns the package directory and the distribution + instance. + """ + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, pkg_name) + os.mkdir(pkg_dir) + dist = Distribution(attrs=kw) + + return pkg_dir, dist + + +class DummyCommand: + """Class to store options for retrieval via set_undefined_options().""" + + def __init__(self, **kwargs): + for kw, val in kwargs.items(): + setattr(self, kw, val) + + def ensure_finalized(self): + pass + + +class EnvironGuard(object): + + def setUp(self): + super(EnvironGuard, self).setUp() + self.old_environ = deepcopy(os.environ) + + def tearDown(self): + for key, value in self.old_environ.items(): + if os.environ.get(key) != value: + os.environ[key] = value + + for key in tuple(os.environ.keys()): + if key not in self.old_environ: + del os.environ[key] + + super(EnvironGuard, self).tearDown() + + +def copy_xxmodule_c(directory): + """Helper for tests that need the xxmodule.c source file. + + Example use: + + def test_compile(self): + copy_xxmodule_c(self.tmpdir) + self.assertIn('xxmodule.c', os.listdir(self.tmpdir)) + + If the source file can be found, it will be copied to *directory*. If not, + the test will be skipped. Errors during copy are not caught. + """ + filename = _get_xxmodule_path() + if filename is None: + raise unittest.SkipTest('cannot find xxmodule.c (test must run in ' + 'the python build dir)') + shutil.copy(filename, directory) + + +def _get_xxmodule_path(): + srcdir = sysconfig.get_config_var('srcdir') + candidates = [ + # use installed copy if available + os.path.join(os.path.dirname(__file__), 'xxmodule.c'), + # otherwise try using copy from build directory + os.path.join(srcdir, 'Modules', 'xxmodule.c'), + # srcdir mysteriously can be $srcdir/Lib/distutils/tests when + # this file is run from its parent directory, so walk up the + # tree to find the real srcdir + os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'), + ] + for path in candidates: + if os.path.exists(path): + return path + + +def fixup_build_ext(cmd): + """Function needed to make build_ext tests pass. + + When Python was built with --enable-shared on Unix, -L. is not enough to + find libpython.so, because regrtest runs in a tempdir, not in the + source directory where the .so lives. + + When Python was built with in debug mode on Windows, build_ext commands + need their debug attribute set, and it is not done automatically for + some reason. + + This function handles both of these things. Example use: + + cmd = build_ext(dist) + support.fixup_build_ext(cmd) + cmd.ensure_finalized() + + Unlike most other Unix platforms, Mac OS X embeds absolute paths + to shared libraries into executables, so the fixup is not needed there. + """ + if os.name == 'nt': + cmd.debug = sys.executable.endswith('_d.exe') + elif sysconfig.get_config_var('Py_ENABLE_SHARED'): + # To further add to the shared builds fun on Unix, we can't just add + # library_dirs to the Extension() instance because that doesn't get + # plumbed through to the final compiler command. + runshared = sysconfig.get_config_var('RUNSHARED') + if runshared is None: + cmd.library_dirs = ['.'] + else: + if sys.platform == 'darwin': + cmd.library_dirs = [] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = [d for d in value.split(os.pathsep) if d] diff --git a/distutils/tests/test_archive_util.py b/distutils/tests/test_archive_util.py new file mode 100644 index 00000000..e9aad0e4 --- /dev/null +++ b/distutils/tests/test_archive_util.py @@ -0,0 +1,394 @@ +# -*- coding: utf-8 -*- +"""Tests for distutils.archive_util.""" +import unittest +import os +import sys +import tarfile +from os.path import splitdrive +import warnings + +from distutils import archive_util +from distutils.archive_util import (check_archive_formats, make_tarball, + make_zipfile, make_archive, + ARCHIVE_FORMATS) +from distutils.spawn import find_executable, spawn +from distutils.tests import support +from test.support import check_warnings, run_unittest, patch, change_cwd + +try: + import grp + import pwd + UID_GID_SUPPORT = True +except ImportError: + UID_GID_SUPPORT = False + +try: + import zipfile + ZIP_SUPPORT = True +except ImportError: + ZIP_SUPPORT = find_executable('zip') + +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + +try: + import bz2 +except ImportError: + bz2 = None + +try: + import lzma +except ImportError: + lzma = None + +def can_fs_encode(filename): + """ + Return True if the filename can be saved in the file system. + """ + if os.path.supports_unicode_filenames: + return True + try: + filename.encode(sys.getfilesystemencoding()) + except UnicodeEncodeError: + return False + return True + + +class ArchiveUtilTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_make_tarball(self, name='archive'): + # creating something to tar + tmpdir = self._create_files() + self._make_tarball(tmpdir, name, '.tar.gz') + # trying an uncompressed one + self._make_tarball(tmpdir, name, '.tar', compress=None) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_make_tarball_gzip(self): + tmpdir = self._create_files() + self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip') + + @unittest.skipUnless(bz2, 'Need bz2 support to run') + def test_make_tarball_bzip2(self): + tmpdir = self._create_files() + self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2') + + @unittest.skipUnless(lzma, 'Need lzma support to run') + def test_make_tarball_xz(self): + tmpdir = self._create_files() + self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz') + + @unittest.skipUnless(can_fs_encode('Ã¥rchiv'), + 'File system cannot handle this filename') + def test_make_tarball_latin1(self): + """ + Mirror test_make_tarball, except filename contains latin characters. + """ + self.test_make_tarball('Ã¥rchiv') # note this isn't a real word + + @unittest.skipUnless(can_fs_encode('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–'), + 'File system cannot handle this filename') + def test_make_tarball_extended(self): + """ + Mirror test_make_tarball, except filename contains extended + characters outside the latin charset. + """ + self.test_make_tarball('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–') # japanese for archive + + def _make_tarball(self, tmpdir, target_name, suffix, **kwargs): + tmpdir2 = self.mkdtemp() + unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], + "source and target should be on same drive") + + base_name = os.path.join(tmpdir2, target_name) + + # working with relative paths to avoid tar warnings + with change_cwd(tmpdir): + make_tarball(splitdrive(base_name)[1], 'dist', **kwargs) + + # check if the compressed tarball was created + tarball = base_name + suffix + self.assertTrue(os.path.exists(tarball)) + self.assertEqual(self._tarinfo(tarball), self._created_files) + + def _tarinfo(self, path): + tar = tarfile.open(path) + try: + names = tar.getnames() + names.sort() + return names + finally: + tar.close() + + _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', + 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] + _created_files = [p.rstrip('/') for p in _zip_created_files] + + def _create_files(self): + # creating something to tar + tmpdir = self.mkdtemp() + dist = os.path.join(tmpdir, 'dist') + os.mkdir(dist) + self.write_file([dist, 'file1'], 'xxx') + self.write_file([dist, 'file2'], 'xxx') + os.mkdir(os.path.join(dist, 'sub')) + self.write_file([dist, 'sub', 'file3'], 'xxx') + os.mkdir(os.path.join(dist, 'sub2')) + return tmpdir + + @unittest.skipUnless(find_executable('tar') and find_executable('gzip') + and ZLIB_SUPPORT, + 'Need the tar, gzip and zlib command to run') + def test_tarfile_vs_tar(self): + tmpdir = self._create_files() + tmpdir2 = self.mkdtemp() + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist') + finally: + os.chdir(old_dir) + + # check if the compressed tarball was created + tarball = base_name + '.tar.gz' + self.assertTrue(os.path.exists(tarball)) + + # now create another tarball using `tar` + tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') + tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist'] + gzip_cmd = ['gzip', '-f', '-9', 'archive2.tar'] + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + spawn(tar_cmd) + spawn(gzip_cmd) + finally: + os.chdir(old_dir) + + self.assertTrue(os.path.exists(tarball2)) + # let's compare both tarballs + self.assertEqual(self._tarinfo(tarball), self._created_files) + self.assertEqual(self._tarinfo(tarball2), self._created_files) + + # trying an uncompressed one + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist', compress=None) + finally: + os.chdir(old_dir) + tarball = base_name + '.tar' + self.assertTrue(os.path.exists(tarball)) + + # now for a dry_run + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist', compress=None, dry_run=True) + finally: + os.chdir(old_dir) + tarball = base_name + '.tar' + self.assertTrue(os.path.exists(tarball)) + + @unittest.skipUnless(find_executable('compress'), + 'The compress program is required') + def test_compress_deprecated(self): + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') + + # using compress and testing the PendingDeprecationWarning + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + with check_warnings() as w: + warnings.simplefilter("always") + make_tarball(base_name, 'dist', compress='compress') + finally: + os.chdir(old_dir) + tarball = base_name + '.tar.Z' + self.assertTrue(os.path.exists(tarball)) + self.assertEqual(len(w.warnings), 1) + + # same test with dry_run + os.remove(tarball) + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + with check_warnings() as w: + warnings.simplefilter("always") + make_tarball(base_name, 'dist', compress='compress', + dry_run=True) + finally: + os.chdir(old_dir) + self.assertFalse(os.path.exists(tarball)) + self.assertEqual(len(w.warnings), 1) + + @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, + 'Need zip and zlib support to run') + def test_make_zipfile(self): + # creating something to tar + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') + with change_cwd(tmpdir): + make_zipfile(base_name, 'dist') + + # check if the compressed tarball was created + tarball = base_name + '.zip' + self.assertTrue(os.path.exists(tarball)) + with zipfile.ZipFile(tarball) as zf: + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) + + @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') + def test_make_zipfile_no_zlib(self): + patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError + + called = [] + zipfile_class = zipfile.ZipFile + def fake_zipfile(*a, **kw): + if kw.get('compression', None) == zipfile.ZIP_STORED: + called.append((a, kw)) + return zipfile_class(*a, **kw) + + patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile) + + # create something to tar and compress + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') + with change_cwd(tmpdir): + make_zipfile(base_name, 'dist') + + tarball = base_name + '.zip' + self.assertEqual(called, + [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) + self.assertTrue(os.path.exists(tarball)) + with zipfile.ZipFile(tarball) as zf: + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) + + def test_check_archive_formats(self): + self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), + 'xxx') + self.assertIsNone(check_archive_formats(['gztar', 'bztar', 'xztar', + 'ztar', 'tar', 'zip'])) + + def test_make_archive(self): + tmpdir = self.mkdtemp() + base_name = os.path.join(tmpdir, 'archive') + self.assertRaises(ValueError, make_archive, base_name, 'xxx') + + def test_make_archive_cwd(self): + current_dir = os.getcwd() + def _breaks(*args, **kw): + raise RuntimeError() + ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file') + try: + try: + make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) + except: + pass + self.assertEqual(os.getcwd(), current_dir) + finally: + del ARCHIVE_FORMATS['xxx'] + + def test_make_archive_tar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'tar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar') + self.assertEqual(self._tarinfo(res), self._created_files) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_make_archive_gztar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'gztar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar.gz') + self.assertEqual(self._tarinfo(res), self._created_files) + + @unittest.skipUnless(bz2, 'Need bz2 support to run') + def test_make_archive_bztar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'bztar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar.bz2') + self.assertEqual(self._tarinfo(res), self._created_files) + + @unittest.skipUnless(lzma, 'Need xz support to run') + def test_make_archive_xztar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'xztar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar.xz') + self.assertEqual(self._tarinfo(res), self._created_files) + + def test_make_archive_owner_group(self): + # testing make_archive with owner and group, with various combinations + # this works even if there's not gid/uid support + if UID_GID_SUPPORT: + group = grp.getgrgid(0)[0] + owner = pwd.getpwuid(0)[0] + else: + group = owner = 'root' + + base_dir = self._create_files() + root_dir = self.mkdtemp() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, + group=group) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'zip', root_dir, base_dir) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'tar', root_dir, base_dir, + owner=owner, group=group) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'tar', root_dir, base_dir, + owner='kjhkjhkjg', group='oihohoh') + self.assertTrue(os.path.exists(res)) + + @unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib") + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + def test_tarfile_root_owner(self): + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + group = grp.getgrgid(0)[0] + owner = pwd.getpwuid(0)[0] + try: + archive_name = make_tarball(base_name, 'dist', compress=None, + owner=owner, group=group) + finally: + os.chdir(old_dir) + + # check if the compressed tarball was created + self.assertTrue(os.path.exists(archive_name)) + + # now checks the rights + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEqual(member.uid, 0) + self.assertEqual(member.gid, 0) + finally: + archive.close() + +def test_suite(): + return unittest.makeSuite(ArchiveUtilTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_bdist.py b/distutils/tests/test_bdist.py new file mode 100644 index 00000000..130d8bf1 --- /dev/null +++ b/distutils/tests/test_bdist.py @@ -0,0 +1,57 @@ +"""Tests for distutils.command.bdist.""" +import os +import unittest +from test.support import run_unittest +import warnings + +from distutils.command.bdist import bdist +from distutils.tests import support + + +class BuildTestCase(support.TempdirManager, + unittest.TestCase): + + def test_formats(self): + # let's create a command and make sure + # we can set the format + dist = self.create_dist()[1] + cmd = bdist(dist) + cmd.formats = ['msi'] + cmd.ensure_finalized() + self.assertEqual(cmd.formats, ['msi']) + + # what formats does bdist offer? + formats = ['bztar', 'gztar', 'msi', 'rpm', 'tar', + 'wininst', 'xztar', 'zip', 'ztar'] + found = sorted(cmd.format_command) + self.assertEqual(found, formats) + + def test_skip_build(self): + # bug #10946: bdist --skip-build should trickle down to subcommands + dist = self.create_dist()[1] + cmd = bdist(dist) + cmd.skip_build = 1 + cmd.ensure_finalized() + dist.command_obj['bdist'] = cmd + + names = ['bdist_dumb', 'bdist_wininst'] # bdist_rpm does not support --skip-build + if os.name == 'nt': + names.append('bdist_msi') + + for name in names: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', 'bdist_wininst command is deprecated', + DeprecationWarning) + subcmd = cmd.get_finalized_command(name) + if getattr(subcmd, '_unsupported', False): + # command is not supported on this build + continue + self.assertTrue(subcmd.skip_build, + '%s should take --skip-build from bdist' % name) + + +def test_suite(): + return unittest.makeSuite(BuildTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/distutils/tests/test_bdist_dumb.py b/distutils/tests/test_bdist_dumb.py new file mode 100644 index 00000000..01a233bc --- /dev/null +++ b/distutils/tests/test_bdist_dumb.py @@ -0,0 +1,97 @@ +"""Tests for distutils.command.bdist_dumb.""" + +import os +import sys +import zipfile +import unittest +from test.support import run_unittest + +from distutils.core import Distribution +from distutils.command.bdist_dumb import bdist_dumb +from distutils.tests import support + +SETUP_PY = """\ +from distutils.core import setup +import foo + +setup(name='foo', version='0.1', py_modules=['foo'], + url='xxx', author='xxx', author_email='xxx') + +""" + +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + + +class BuildDumbTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + def setUp(self): + super(BuildDumbTestCase, self).setUp() + self.old_location = os.getcwd() + self.old_sys_argv = sys.argv, sys.argv[:] + + def tearDown(self): + os.chdir(self.old_location) + sys.argv = self.old_sys_argv[0] + sys.argv[:] = self.old_sys_argv[1] + super(BuildDumbTestCase, self).tearDown() + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_simple_built(self): + + # let's create a simple package + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_dumb(dist) + + # so the output is the same no matter + # what is the platform + cmd.format = 'zip' + + cmd.ensure_finalized() + cmd.run() + + # see what we have + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name) + + self.assertEqual(dist_created, [base]) + + # now let's check what we have in the zip file + fp = zipfile.ZipFile(os.path.join('dist', base)) + try: + contents = fp.namelist() + finally: + fp.close() + + contents = sorted(filter(None, map(os.path.basename, contents))) + wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] + if not sys.dont_write_bytecode: + wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) + self.assertEqual(contents, sorted(wanted)) + +def test_suite(): + return unittest.makeSuite(BuildDumbTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/distutils/tests/test_bdist_msi.py b/distutils/tests/test_bdist_msi.py new file mode 100644 index 00000000..418e60ec --- /dev/null +++ b/distutils/tests/test_bdist_msi.py @@ -0,0 +1,26 @@ +"""Tests for distutils.command.bdist_msi.""" +import sys +import unittest +from test.support import run_unittest, check_warnings +from distutils.tests import support + + +@unittest.skipUnless(sys.platform == 'win32', 'these tests require Windows') +class BDistMSITestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_minimal(self): + # minimal test XXX need more tests + from distutils.command.bdist_msi import bdist_msi + project_dir, dist = self.create_dist() + with check_warnings(("", DeprecationWarning)): + cmd = bdist_msi(dist) + cmd.ensure_finalized() + + +def test_suite(): + return unittest.makeSuite(BDistMSITestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/distutils/tests/test_bdist_rpm.py b/distutils/tests/test_bdist_rpm.py new file mode 100644 index 00000000..6453a02b --- /dev/null +++ b/distutils/tests/test_bdist_rpm.py @@ -0,0 +1,135 @@ +"""Tests for distutils.command.bdist_rpm.""" + +import unittest +import sys +import os +from test.support import run_unittest, requires_zlib + +from distutils.core import Distribution +from distutils.command.bdist_rpm import bdist_rpm +from distutils.tests import support +from distutils.spawn import find_executable + +SETUP_PY = """\ +from distutils.core import setup +import foo + +setup(name='foo', version='0.1', py_modules=['foo'], + url='xxx', author='xxx', author_email='xxx') + +""" + +class BuildRpmTestCase(support.TempdirManager, + support.EnvironGuard, + support.LoggingSilencer, + unittest.TestCase): + + def setUp(self): + try: + sys.executable.encode("UTF-8") + except UnicodeEncodeError: + raise unittest.SkipTest("sys.executable is not encodable to UTF-8") + + super(BuildRpmTestCase, self).setUp() + self.old_location = os.getcwd() + self.old_sys_argv = sys.argv, sys.argv[:] + + def tearDown(self): + os.chdir(self.old_location) + sys.argv = self.old_sys_argv[0] + sys.argv[:] = self.old_sys_argv[1] + super(BuildRpmTestCase, self).tearDown() + + # XXX I am unable yet to make this test work without + # spurious sdtout/stderr output under Mac OS X + @unittest.skipUnless(sys.platform.startswith('linux'), + 'spurious sdtout/stderr output under Mac OS X') + @requires_zlib + @unittest.skipIf(find_executable('rpm') is None, + 'the rpm command is not found') + @unittest.skipIf(find_executable('rpmbuild') is None, + 'the rpmbuild command is not found') + def test_quiet(self): + # let's create a package + tmp_dir = self.mkdtemp() + os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_rpm(dist) + cmd.fix_python = True + + # running in quiet mode + cmd.quiet = 1 + cmd.ensure_finalized() + cmd.run() + + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + self.assertIn('foo-0.1-1.noarch.rpm', dist_created) + + # bug #2945: upload ignores bdist_rpm files + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) + + # XXX I am unable yet to make this test work without + # spurious sdtout/stderr output under Mac OS X + @unittest.skipUnless(sys.platform.startswith('linux'), + 'spurious sdtout/stderr output under Mac OS X') + @requires_zlib + # http://bugs.python.org/issue1533164 + @unittest.skipIf(find_executable('rpm') is None, + 'the rpm command is not found') + @unittest.skipIf(find_executable('rpmbuild') is None, + 'the rpmbuild command is not found') + def test_no_optimize_flag(self): + # let's create a package that breaks bdist_rpm + tmp_dir = self.mkdtemp() + os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_rpm(dist) + cmd.fix_python = True + + cmd.quiet = 1 + cmd.ensure_finalized() + cmd.run() + + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + self.assertIn('foo-0.1-1.noarch.rpm', dist_created) + + # bug #2945: upload ignores bdist_rpm files + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) + + os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) + +def test_suite(): + return unittest.makeSuite(BuildRpmTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/distutils/tests/test_bdist_wininst.py b/distutils/tests/test_bdist_wininst.py new file mode 100644 index 00000000..5c3d025d --- /dev/null +++ b/distutils/tests/test_bdist_wininst.py @@ -0,0 +1,38 @@ +"""Tests for distutils.command.bdist_wininst.""" +import sys +import platform +import unittest +from test.support import run_unittest, check_warnings + +from distutils.command.bdist_wininst import bdist_wininst +from distutils.tests import support + +@unittest.skipIf(sys.platform == 'win32' and platform.machine() == 'ARM64', + 'bdist_wininst is not supported in this install') +@unittest.skipIf(getattr(bdist_wininst, '_unsupported', False), + 'bdist_wininst is not supported in this install') +class BuildWinInstTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_get_exe_bytes(self): + + # issue5731: command was broken on non-windows platforms + # this test makes sure it works now for every platform + # let's create a command + pkg_pth, dist = self.create_dist() + with check_warnings(("", DeprecationWarning)): + cmd = bdist_wininst(dist) + cmd.ensure_finalized() + + # let's run the code that finds the right wininst*.exe file + # and make sure it finds it and returns its content + # no matter what platform we have + exe_file = cmd.get_exe_bytes() + self.assertGreater(len(exe_file), 10) + +def test_suite(): + return unittest.makeSuite(BuildWinInstTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/distutils/tests/test_build.py b/distutils/tests/test_build.py new file mode 100644 index 00000000..b020a5ba --- /dev/null +++ b/distutils/tests/test_build.py @@ -0,0 +1,56 @@ +"""Tests for distutils.command.build.""" +import unittest +import os +import sys +from test.support import run_unittest + +from distutils.command.build import build +from distutils.tests import support +from sysconfig import get_platform + +class BuildTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + cmd = build(dist) + cmd.finalize_options() + + # if not specified, plat_name gets the current platform + self.assertEqual(cmd.plat_name, get_platform()) + + # build_purelib is build + lib + wanted = os.path.join(cmd.build_base, 'lib') + self.assertEqual(cmd.build_purelib, wanted) + + # build_platlib is 'build/lib.platform-x.x[-pydebug]' + # examples: + # build/lib.macosx-10.3-i386-2.7 + plat_spec = '.%s-%d.%d' % (cmd.plat_name, *sys.version_info[:2]) + if hasattr(sys, 'gettotalrefcount'): + self.assertTrue(cmd.build_platlib.endswith('-pydebug')) + plat_spec += '-pydebug' + wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) + self.assertEqual(cmd.build_platlib, wanted) + + # by default, build_lib = build_purelib + self.assertEqual(cmd.build_lib, cmd.build_purelib) + + # build_temp is build/temp. + wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) + self.assertEqual(cmd.build_temp, wanted) + + # build_scripts is build/scripts-x.x + wanted = os.path.join(cmd.build_base, + 'scripts-%d.%d' % sys.version_info[:2]) + self.assertEqual(cmd.build_scripts, wanted) + + # executable is os.path.normpath(sys.executable) + self.assertEqual(cmd.executable, os.path.normpath(sys.executable)) + +def test_suite(): + return unittest.makeSuite(BuildTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_build_clib.py b/distutils/tests/test_build_clib.py new file mode 100644 index 00000000..abd83137 --- /dev/null +++ b/distutils/tests/test_build_clib.py @@ -0,0 +1,134 @@ +"""Tests for distutils.command.build_clib.""" +import unittest +import os +import sys + +from test.support import run_unittest, missing_compiler_executable + +from distutils.command.build_clib import build_clib +from distutils.errors import DistutilsSetupError +from distutils.tests import support + +class BuildCLibTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_check_library_dist(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + # 'libraries' option must be a list + self.assertRaises(DistutilsSetupError, cmd.check_library_list, 'foo') + + # each element of 'libraries' must a 2-tuple + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + ['foo1', 'foo2']) + + # first element of each tuple in 'libraries' + # must be a string (the library name) + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [(1, 'foo1'), ('name', 'foo2')]) + + # library name may not contain directory separators + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [('name', 'foo1'), + ('another/name', 'foo2')]) + + # second element of each tuple must be a dictionary (build info) + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [('name', {}), + ('another', 'foo2')]) + + # those work + libs = [('name', {}), ('name', {'ok': 'good'})] + cmd.check_library_list(libs) + + def test_get_source_files(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + # "in 'libraries' option 'sources' must be present and must be + # a list of source filenames + cmd.libraries = [('name', {})] + self.assertRaises(DistutilsSetupError, cmd.get_source_files) + + cmd.libraries = [('name', {'sources': 1})] + self.assertRaises(DistutilsSetupError, cmd.get_source_files) + + cmd.libraries = [('name', {'sources': ['a', 'b']})] + self.assertEqual(cmd.get_source_files(), ['a', 'b']) + + cmd.libraries = [('name', {'sources': ('a', 'b')})] + self.assertEqual(cmd.get_source_files(), ['a', 'b']) + + cmd.libraries = [('name', {'sources': ('a', 'b')}), + ('name2', {'sources': ['c', 'd']})] + self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd']) + + def test_build_libraries(self): + + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + class FakeCompiler: + def compile(*args, **kw): + pass + create_static_lib = compile + + cmd.compiler = FakeCompiler() + + # build_libraries is also doing a bit of typo checking + lib = [('name', {'sources': 'notvalid'})] + self.assertRaises(DistutilsSetupError, cmd.build_libraries, lib) + + lib = [('name', {'sources': list()})] + cmd.build_libraries(lib) + + lib = [('name', {'sources': tuple()})] + cmd.build_libraries(lib) + + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + cmd.include_dirs = 'one-dir' + cmd.finalize_options() + self.assertEqual(cmd.include_dirs, ['one-dir']) + + cmd.include_dirs = None + cmd.finalize_options() + self.assertEqual(cmd.include_dirs, []) + + cmd.distribution.libraries = 'WONTWORK' + self.assertRaises(DistutilsSetupError, cmd.finalize_options) + + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") + def test_run(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + foo_c = os.path.join(pkg_dir, 'foo.c') + self.write_file(foo_c, 'int main(void) { return 1;}\n') + cmd.libraries = [('foo', {'sources': [foo_c]})] + + build_temp = os.path.join(pkg_dir, 'build') + os.mkdir(build_temp) + cmd.build_temp = build_temp + cmd.build_clib = build_temp + + # Before we run the command, we want to make sure + # all commands are present on the system. + ccmd = missing_compiler_executable() + if ccmd is not None: + self.skipTest('The %r command is not found' % ccmd) + + # this should work + cmd.run() + + # let's check the result + self.assertIn('libfoo.a', os.listdir(build_temp)) + +def test_suite(): + return unittest.makeSuite(BuildCLibTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_build_ext.py b/distutils/tests/test_build_ext.py new file mode 100644 index 00000000..5e47e077 --- /dev/null +++ b/distutils/tests/test_build_ext.py @@ -0,0 +1,545 @@ +import sys +import os +from io import StringIO +import textwrap + +from distutils.core import Distribution +from distutils.command.build_ext import build_ext +from distutils import sysconfig +from distutils.tests.support import (TempdirManager, LoggingSilencer, + copy_xxmodule_c, fixup_build_ext) +from distutils.extension import Extension +from distutils.errors import ( + CompileError, DistutilsPlatformError, DistutilsSetupError, + UnknownFileError) + +import unittest +from test import support +from test.support.script_helper import assert_python_ok + +# http://bugs.python.org/issue4373 +# Don't load the xx module more than once. +ALREADY_TESTED = False + + +class BuildExtTestCase(TempdirManager, + LoggingSilencer, + unittest.TestCase): + def setUp(self): + # Create a simple test environment + super(BuildExtTestCase, self).setUp() + self.tmp_dir = self.mkdtemp() + import site + self.old_user_base = site.USER_BASE + site.USER_BASE = self.mkdtemp() + from distutils.command import build_ext + build_ext.USER_BASE = site.USER_BASE + + # bpo-30132: On Windows, a .pdb file may be created in the current + # working directory. Create a temporary working directory to cleanup + # everything at the end of the test. + change_cwd = support.change_cwd(self.tmp_dir) + change_cwd.__enter__() + self.addCleanup(change_cwd.__exit__, None, None, None) + + def tearDown(self): + import site + site.USER_BASE = self.old_user_base + from distutils.command import build_ext + build_ext.USER_BASE = self.old_user_base + super(BuildExtTestCase, self).tearDown() + + def build_ext(self, *args, **kwargs): + return build_ext(*args, **kwargs) + + def test_build_ext(self): + cmd = support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) + global ALREADY_TESTED + copy_xxmodule_c(self.tmp_dir) + xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') + xx_ext = Extension('xx', [xx_c]) + dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) + dist.package_dir = self.tmp_dir + cmd = self.build_ext(dist) + fixup_build_ext(cmd) + cmd.build_lib = self.tmp_dir + cmd.build_temp = self.tmp_dir + + old_stdout = sys.stdout + if not support.verbose: + # silence compiler output + sys.stdout = StringIO() + try: + cmd.ensure_finalized() + cmd.run() + finally: + sys.stdout = old_stdout + + if ALREADY_TESTED: + self.skipTest('Already tested in %s' % ALREADY_TESTED) + else: + ALREADY_TESTED = type(self).__name__ + + code = textwrap.dedent(f""" + tmp_dir = {self.tmp_dir!r} + + import sys + import unittest + from test import support + + sys.path.insert(0, tmp_dir) + import xx + + class Tests(unittest.TestCase): + def test_xx(self): + for attr in ('error', 'foo', 'new', 'roj'): + self.assertTrue(hasattr(xx, attr)) + + self.assertEqual(xx.foo(2, 5), 7) + self.assertEqual(xx.foo(13,15), 28) + self.assertEqual(xx.new().demo(), None) + if support.HAVE_DOCSTRINGS: + doc = 'This is a template module just for instruction.' + self.assertEqual(xx.__doc__, doc) + self.assertIsInstance(xx.Null(), xx.Null) + self.assertIsInstance(xx.Str(), xx.Str) + + + unittest.main() + """) + assert_python_ok('-c', code) + + def test_solaris_enable_shared(self): + dist = Distribution({'name': 'xx'}) + cmd = self.build_ext(dist) + old = sys.platform + + sys.platform = 'sunos' # fooling finalize_options + from distutils.sysconfig import _config_vars + old_var = _config_vars.get('Py_ENABLE_SHARED') + _config_vars['Py_ENABLE_SHARED'] = 1 + try: + cmd.ensure_finalized() + finally: + sys.platform = old + if old_var is None: + del _config_vars['Py_ENABLE_SHARED'] + else: + _config_vars['Py_ENABLE_SHARED'] = old_var + + # make sure we get some library dirs under solaris + self.assertGreater(len(cmd.library_dirs), 0) + + def test_user_site(self): + import site + dist = Distribution({'name': 'xx'}) + cmd = self.build_ext(dist) + + # making sure the user option is there + options = [name for name, short, lable in + cmd.user_options] + self.assertIn('user', options) + + # setting a value + cmd.user = 1 + + # setting user based lib and include + lib = os.path.join(site.USER_BASE, 'lib') + incl = os.path.join(site.USER_BASE, 'include') + os.mkdir(lib) + os.mkdir(incl) + + # let's run finalize + cmd.ensure_finalized() + + # see if include_dirs and library_dirs + # were set + self.assertIn(lib, cmd.library_dirs) + self.assertIn(lib, cmd.rpath) + self.assertIn(incl, cmd.include_dirs) + + def test_optional_extension(self): + + # this extension will fail, but let's ignore this failure + # with the optional argument. + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.ensure_finalized() + self.assertRaises((UnknownFileError, CompileError), + cmd.run) # should raise an error + + modules = [Extension('foo', ['xxx'], optional=True)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.ensure_finalized() + cmd.run() # should pass + + def test_finalize_options(self): + # Make sure Python's include directories (for Python.h, pyconfig.h, + # etc.) are in the include search path. + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.finalize_options() + + py_include = sysconfig.get_python_inc() + for p in py_include.split(os.path.pathsep): + self.assertIn(p, cmd.include_dirs) + + plat_py_include = sysconfig.get_python_inc(plat_specific=1) + for p in plat_py_include.split(os.path.pathsep): + self.assertIn(p, cmd.include_dirs) + + # make sure cmd.libraries is turned into a list + # if it's a string + cmd = self.build_ext(dist) + cmd.libraries = 'my_lib, other_lib lastlib' + cmd.finalize_options() + self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib']) + + # make sure cmd.library_dirs is turned into a list + # if it's a string + cmd = self.build_ext(dist) + cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep + cmd.finalize_options() + self.assertIn('my_lib_dir', cmd.library_dirs) + self.assertIn('other_lib_dir', cmd.library_dirs) + + # make sure rpath is turned into a list + # if it's a string + cmd = self.build_ext(dist) + cmd.rpath = 'one%stwo' % os.pathsep + cmd.finalize_options() + self.assertEqual(cmd.rpath, ['one', 'two']) + + # make sure cmd.link_objects is turned into a list + # if it's a string + cmd = build_ext(dist) + cmd.link_objects = 'one two,three' + cmd.finalize_options() + self.assertEqual(cmd.link_objects, ['one', 'two', 'three']) + + # XXX more tests to perform for win32 + + # make sure define is turned into 2-tuples + # strings if they are ','-separated strings + cmd = self.build_ext(dist) + cmd.define = 'one,two' + cmd.finalize_options() + self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) + + # make sure undef is turned into a list of + # strings if they are ','-separated strings + cmd = self.build_ext(dist) + cmd.undef = 'one,two' + cmd.finalize_options() + self.assertEqual(cmd.undef, ['one', 'two']) + + # make sure swig_opts is turned into a list + cmd = self.build_ext(dist) + cmd.swig_opts = None + cmd.finalize_options() + self.assertEqual(cmd.swig_opts, []) + + cmd = self.build_ext(dist) + cmd.swig_opts = '1 2' + cmd.finalize_options() + self.assertEqual(cmd.swig_opts, ['1', '2']) + + def test_check_extensions_list(self): + dist = Distribution() + cmd = self.build_ext(dist) + cmd.finalize_options() + + #'extensions' option must be a list of Extension instances + self.assertRaises(DistutilsSetupError, + cmd.check_extensions_list, 'foo') + + # each element of 'ext_modules' option must be an + # Extension instance or 2-tuple + exts = [('bar', 'foo', 'bar'), 'foo'] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # first element of each tuple in 'ext_modules' + # must be the extension name (a string) and match + # a python dotted-separated name + exts = [('foo-bar', '')] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # second element of each tuple in 'ext_modules' + # must be a dictionary (build info) + exts = [('foo.bar', '')] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # ok this one should pass + exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', + 'some': 'bar'})] + cmd.check_extensions_list(exts) + ext = exts[0] + self.assertIsInstance(ext, Extension) + + # check_extensions_list adds in ext the values passed + # when they are in ('include_dirs', 'library_dirs', 'libraries' + # 'extra_objects', 'extra_compile_args', 'extra_link_args') + self.assertEqual(ext.libraries, 'foo') + self.assertFalse(hasattr(ext, 'some')) + + # 'macros' element of build info dict must be 1- or 2-tuple + exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', + 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + exts[0][1]['macros'] = [('1', '2'), ('3',)] + cmd.check_extensions_list(exts) + self.assertEqual(exts[0].undef_macros, ['3']) + self.assertEqual(exts[0].define_macros, [('1', '2')]) + + def test_get_source_files(self): + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.ensure_finalized() + self.assertEqual(cmd.get_source_files(), ['xxx']) + + def test_unicode_module_names(self): + modules = [ + Extension('foo', ['aaa'], optional=False), + Extension('föö', ['uuu'], optional=False), + ] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.ensure_finalized() + self.assertRegex(cmd.get_ext_filename(modules[0].name), r'foo(_d)?\..*') + self.assertRegex(cmd.get_ext_filename(modules[1].name), r'föö(_d)?\..*') + self.assertEqual(cmd.get_export_symbols(modules[0]), ['PyInit_foo']) + self.assertEqual(cmd.get_export_symbols(modules[1]), ['PyInitU_f_gkaa']) + + def test_compiler_option(self): + # cmd.compiler is an option and + # should not be overridden by a compiler instance + # when the command is run + dist = Distribution() + cmd = self.build_ext(dist) + cmd.compiler = 'unix' + cmd.ensure_finalized() + cmd.run() + self.assertEqual(cmd.compiler, 'unix') + + def test_get_outputs(self): + cmd = support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) + tmp_dir = self.mkdtemp() + c_file = os.path.join(tmp_dir, 'foo.c') + self.write_file(c_file, 'void PyInit_foo(void) {}\n') + ext = Extension('foo', [c_file], optional=False) + dist = Distribution({'name': 'xx', + 'ext_modules': [ext]}) + cmd = self.build_ext(dist) + fixup_build_ext(cmd) + cmd.ensure_finalized() + self.assertEqual(len(cmd.get_outputs()), 1) + + cmd.build_lib = os.path.join(self.tmp_dir, 'build') + cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') + + # issue #5977 : distutils build_ext.get_outputs + # returns wrong result with --inplace + other_tmp_dir = os.path.realpath(self.mkdtemp()) + old_wd = os.getcwd() + os.chdir(other_tmp_dir) + try: + cmd.inplace = 1 + cmd.run() + so_file = cmd.get_outputs()[0] + finally: + os.chdir(old_wd) + self.assertTrue(os.path.exists(so_file)) + ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') + self.assertTrue(so_file.endswith(ext_suffix)) + so_dir = os.path.dirname(so_file) + self.assertEqual(so_dir, other_tmp_dir) + + cmd.inplace = 0 + cmd.compiler = None + cmd.run() + so_file = cmd.get_outputs()[0] + self.assertTrue(os.path.exists(so_file)) + self.assertTrue(so_file.endswith(ext_suffix)) + so_dir = os.path.dirname(so_file) + self.assertEqual(so_dir, cmd.build_lib) + + # inplace = 0, cmd.package = 'bar' + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {'': 'bar'} + path = cmd.get_ext_fullpath('foo') + # checking that the last directory is the build_dir + path = os.path.split(path)[0] + self.assertEqual(path, cmd.build_lib) + + # inplace = 1, cmd.package = 'bar' + cmd.inplace = 1 + other_tmp_dir = os.path.realpath(self.mkdtemp()) + old_wd = os.getcwd() + os.chdir(other_tmp_dir) + try: + path = cmd.get_ext_fullpath('foo') + finally: + os.chdir(old_wd) + # checking that the last directory is bar + path = os.path.split(path)[0] + lastdir = os.path.split(path)[-1] + self.assertEqual(lastdir, 'bar') + + def test_ext_fullpath(self): + ext = sysconfig.get_config_var('EXT_SUFFIX') + # building lxml.etree inplace + #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + #etree_ext = Extension('lxml.etree', [etree_c]) + #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + dist = Distribution() + cmd = self.build_ext(dist) + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEqual(wanted, path) + + # building lxml.etree not inplace + cmd.inplace = 0 + cmd.build_lib = os.path.join(curdir, 'tmpdir') + wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEqual(wanted, path) + + # building twisted.runner.portmap not inplace + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {} + cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', + 'portmap' + ext) + self.assertEqual(wanted, path) + + # building twisted.runner.portmap inplace + cmd.inplace = 1 + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) + self.assertEqual(wanted, path) + + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target_default(self): + # Issue 9516: Test that, in the absence of the environment variable, + # an extension module is compiled with the same deployment target as + # the interpreter. + self._try_compile_deployment_target('==', None) + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target_too_low(self): + # Issue 9516: Test that an extension module is not allowed to be + # compiled with a deployment target less than that of the interpreter. + self.assertRaises(DistutilsPlatformError, + self._try_compile_deployment_target, '>', '10.1') + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target_higher_ok(self): + # Issue 9516: Test that an extension module can be compiled with a + # deployment target higher than that of the interpreter: the ext + # module may depend on some newer OS feature. + deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') + if deptarget: + # increment the minor version number (i.e. 10.6 -> 10.7) + deptarget = [int(x) for x in deptarget.split('.')] + deptarget[-1] += 1 + deptarget = '.'.join(str(i) for i in deptarget) + self._try_compile_deployment_target('<', deptarget) + + def _try_compile_deployment_target(self, operator, target): + orig_environ = os.environ + os.environ = orig_environ.copy() + self.addCleanup(setattr, os, 'environ', orig_environ) + + if target is None: + if os.environ.get('MACOSX_DEPLOYMENT_TARGET'): + del os.environ['MACOSX_DEPLOYMENT_TARGET'] + else: + os.environ['MACOSX_DEPLOYMENT_TARGET'] = target + + deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') + + with open(deptarget_c, 'w') as fp: + fp.write(textwrap.dedent('''\ + #include + + int dummy; + + #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED + #else + #error "Unexpected target" + #endif + + ''' % operator)) + + # get the deployment target that the interpreter was built with + target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') + target = tuple(map(int, target.split('.')[0:2])) + # format the target value as defined in the Apple + # Availability Macros. We can't use the macro names since + # at least one value we test with will not exist yet. + if target[1] < 10: + # for 10.1 through 10.9.x -> "10n0" + target = '%02d%01d0' % target + else: + # for 10.10 and beyond -> "10nn00" + target = '%02d%02d00' % target + deptarget_ext = Extension( + 'deptarget', + [deptarget_c], + extra_compile_args=['-DTARGET=%s'%(target,)], + ) + dist = Distribution({ + 'name': 'deptarget', + 'ext_modules': [deptarget_ext] + }) + dist.package_dir = self.tmp_dir + cmd = self.build_ext(dist) + cmd.build_lib = self.tmp_dir + cmd.build_temp = self.tmp_dir + + try: + old_stdout = sys.stdout + if not support.verbose: + # silence compiler output + sys.stdout = StringIO() + try: + cmd.ensure_finalized() + cmd.run() + finally: + sys.stdout = old_stdout + + except CompileError: + self.fail("Wrong deployment target during compilation") + + +class ParallelBuildExtTestCase(BuildExtTestCase): + + def build_ext(self, *args, **kwargs): + build_ext = super().build_ext(*args, **kwargs) + build_ext.parallel = True + return build_ext + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(BuildExtTestCase)) + suite.addTest(unittest.makeSuite(ParallelBuildExtTestCase)) + return suite + +if __name__ == '__main__': + support.run_unittest(__name__) diff --git a/distutils/tests/test_build_py.py b/distutils/tests/test_build_py.py new file mode 100644 index 00000000..0712e92c --- /dev/null +++ b/distutils/tests/test_build_py.py @@ -0,0 +1,179 @@ +"""Tests for distutils.command.build_py.""" + +import os +import sys +import unittest + +from distutils.command.build_py import build_py +from distutils.core import Distribution +from distutils.errors import DistutilsFileError + +from distutils.tests import support +from test.support import run_unittest + + +class BuildPyTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_package_data(self): + sources = self.mkdtemp() + f = open(os.path.join(sources, "__init__.py"), "w") + try: + f.write("# Pretend this is a package.") + finally: + f.close() + f = open(os.path.join(sources, "README.txt"), "w") + try: + f.write("Info about this package") + finally: + f.close() + + destination = self.mkdtemp() + + dist = Distribution({"packages": ["pkg"], + "package_dir": {"pkg": sources}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.command_obj["build"] = support.DummyCommand( + force=0, + build_lib=destination) + dist.packages = ["pkg"] + dist.package_data = {"pkg": ["README.txt"]} + dist.package_dir = {"pkg": sources} + + cmd = build_py(dist) + cmd.compile = 1 + cmd.ensure_finalized() + self.assertEqual(cmd.package_data, dist.package_data) + + cmd.run() + + # This makes sure the list of outputs includes byte-compiled + # files for Python modules but not for package data files + # (there shouldn't *be* byte-code files for those!). + self.assertEqual(len(cmd.get_outputs()), 3) + pkgdest = os.path.join(destination, "pkg") + files = os.listdir(pkgdest) + pycache_dir = os.path.join(pkgdest, "__pycache__") + self.assertIn("__init__.py", files) + self.assertIn("README.txt", files) + if sys.dont_write_bytecode: + self.assertFalse(os.path.exists(pycache_dir)) + else: + pyc_files = os.listdir(pycache_dir) + self.assertIn("__init__.%s.pyc" % sys.implementation.cache_tag, + pyc_files) + + def test_empty_package_dir(self): + # See bugs #1668596/#1720897 + sources = self.mkdtemp() + open(os.path.join(sources, "__init__.py"), "w").close() + + testdir = os.path.join(sources, "doc") + os.mkdir(testdir) + open(os.path.join(testdir, "testfile"), "w").close() + + os.chdir(sources) + dist = Distribution({"packages": ["pkg"], + "package_dir": {"pkg": ""}, + "package_data": {"pkg": ["doc/*"]}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.script_args = ["build"] + dist.parse_command_line() + + try: + dist.run_commands() + except DistutilsFileError: + self.fail("failed package_data test when package_dir is ''") + + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') + def test_byte_compile(self): + project_dir, dist = self.create_dist(py_modules=['boiledeggs']) + os.chdir(project_dir) + self.write_file('boiledeggs.py', 'import antigravity') + cmd = build_py(dist) + cmd.compile = 1 + cmd.build_lib = 'here' + cmd.finalize_options() + cmd.run() + + found = os.listdir(cmd.build_lib) + self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) + found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) + self.assertEqual(found, + ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]) + + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') + def test_byte_compile_optimized(self): + project_dir, dist = self.create_dist(py_modules=['boiledeggs']) + os.chdir(project_dir) + self.write_file('boiledeggs.py', 'import antigravity') + cmd = build_py(dist) + cmd.compile = 0 + cmd.optimize = 1 + cmd.build_lib = 'here' + cmd.finalize_options() + cmd.run() + + found = os.listdir(cmd.build_lib) + self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) + found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) + expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag) + self.assertEqual(sorted(found), [expect]) + + def test_dir_in_package_data(self): + """ + A directory in package_data should not be added to the filelist. + """ + # See bug 19286 + sources = self.mkdtemp() + pkg_dir = os.path.join(sources, "pkg") + + os.mkdir(pkg_dir) + open(os.path.join(pkg_dir, "__init__.py"), "w").close() + + docdir = os.path.join(pkg_dir, "doc") + os.mkdir(docdir) + open(os.path.join(docdir, "testfile"), "w").close() + + # create the directory that could be incorrectly detected as a file + os.mkdir(os.path.join(docdir, 'otherdir')) + + os.chdir(sources) + dist = Distribution({"packages": ["pkg"], + "package_data": {"pkg": ["doc/*"]}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.script_args = ["build"] + dist.parse_command_line() + + try: + dist.run_commands() + except DistutilsFileError: + self.fail("failed package_data when data dir includes a dir") + + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + dist = self.create_dist()[1] + cmd = build_py(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertIn('byte-compiling is disabled', + self.logs[0][1] % self.logs[0][2]) + + +def test_suite(): + return unittest.makeSuite(BuildPyTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_build_scripts.py b/distutils/tests/test_build_scripts.py new file mode 100644 index 00000000..954fc763 --- /dev/null +++ b/distutils/tests/test_build_scripts.py @@ -0,0 +1,112 @@ +"""Tests for distutils.command.build_scripts.""" + +import os +import unittest + +from distutils.command.build_scripts import build_scripts +from distutils.core import Distribution +from distutils import sysconfig + +from distutils.tests import support +from test.support import run_unittest + + +class BuildScriptsTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_default_settings(self): + cmd = self.get_build_scripts_cmd("/foo/bar", []) + self.assertFalse(cmd.force) + self.assertIsNone(cmd.build_dir) + + cmd.finalize_options() + + self.assertTrue(cmd.force) + self.assertEqual(cmd.build_dir, "/foo/bar") + + def test_build(self): + source = self.mkdtemp() + target = self.mkdtemp() + expected = self.write_sample_scripts(source) + + cmd = self.get_build_scripts_cmd(target, + [os.path.join(source, fn) + for fn in expected]) + cmd.finalize_options() + cmd.run() + + built = os.listdir(target) + for name in expected: + self.assertIn(name, built) + + def get_build_scripts_cmd(self, target, scripts): + import sys + dist = Distribution() + dist.scripts = scripts + dist.command_obj["build"] = support.DummyCommand( + build_scripts=target, + force=1, + executable=sys.executable + ) + return build_scripts(dist) + + def write_sample_scripts(self, dir): + expected = [] + expected.append("script1.py") + self.write_script(dir, "script1.py", + ("#! /usr/bin/env python2.3\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + expected.append("script2.py") + self.write_script(dir, "script2.py", + ("#!/usr/bin/python\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + expected.append("shell.sh") + self.write_script(dir, "shell.sh", + ("#!/bin/sh\n" + "# bogus shell script w/ sh-bang\n" + "exit 0\n")) + return expected + + def write_script(self, dir, name, text): + f = open(os.path.join(dir, name), "w") + try: + f.write(text) + finally: + f.close() + + def test_version_int(self): + source = self.mkdtemp() + target = self.mkdtemp() + expected = self.write_sample_scripts(source) + + + cmd = self.get_build_scripts_cmd(target, + [os.path.join(source, fn) + for fn in expected]) + cmd.finalize_options() + + # http://bugs.python.org/issue4524 + # + # On linux-g++-32 with command line `./configure --enable-ipv6 + # --with-suffix=3`, python is compiled okay but the build scripts + # failed when writing the name of the executable + old = sysconfig.get_config_vars().get('VERSION') + sysconfig._config_vars['VERSION'] = 4 + try: + cmd.run() + finally: + if old is not None: + sysconfig._config_vars['VERSION'] = old + + built = os.listdir(target) + for name in expected: + self.assertIn(name, built) + +def test_suite(): + return unittest.makeSuite(BuildScriptsTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_check.py b/distutils/tests/test_check.py new file mode 100644 index 00000000..e534aca1 --- /dev/null +++ b/distutils/tests/test_check.py @@ -0,0 +1,163 @@ +"""Tests for distutils.command.check.""" +import os +import textwrap +import unittest +from test.support import run_unittest + +from distutils.command.check import check, HAS_DOCUTILS +from distutils.tests import support +from distutils.errors import DistutilsSetupError + +try: + import pygments +except ImportError: + pygments = None + + +HERE = os.path.dirname(__file__) + + +class CheckTestCase(support.LoggingSilencer, + support.TempdirManager, + unittest.TestCase): + + def _run(self, metadata=None, cwd=None, **options): + if metadata is None: + metadata = {} + if cwd is not None: + old_dir = os.getcwd() + os.chdir(cwd) + pkg_info, dist = self.create_dist(**metadata) + cmd = check(dist) + cmd.initialize_options() + for name, value in options.items(): + setattr(cmd, name, value) + cmd.ensure_finalized() + cmd.run() + if cwd is not None: + os.chdir(old_dir) + return cmd + + def test_check_metadata(self): + # let's run the command with no metadata at all + # by default, check is checking the metadata + # should have some warnings + cmd = self._run() + self.assertEqual(cmd._warnings, 2) + + # now let's add the required fields + # and run it again, to make sure we don't get + # any warning anymore + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx'} + cmd = self._run(metadata) + self.assertEqual(cmd._warnings, 0) + + # now with the strict mode, we should + # get an error if there are missing metadata + self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1}) + + # and of course, no error when all metadata are present + cmd = self._run(metadata, strict=1) + self.assertEqual(cmd._warnings, 0) + + # now a test with non-ASCII characters + metadata = {'url': 'xxx', 'author': '\u00c9ric', + 'author_email': 'xxx', 'name': 'xxx', + 'version': 'xxx', + 'description': 'Something about esszet \u00df', + 'long_description': 'More things about esszet \u00df'} + cmd = self._run(metadata) + self.assertEqual(cmd._warnings, 0) + + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") + def test_check_document(self): + pkg_info, dist = self.create_dist() + cmd = check(dist) + + # let's see if it detects broken rest + broken_rest = 'title\n===\n\ntest' + msgs = cmd._check_rst_data(broken_rest) + self.assertEqual(len(msgs), 1) + + # and non-broken rest + rest = 'title\n=====\n\ntest' + msgs = cmd._check_rst_data(rest) + self.assertEqual(len(msgs), 0) + + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") + def test_check_restructuredtext(self): + # let's see if it detects broken rest in long_description + broken_rest = 'title\n===\n\ntest' + pkg_info, dist = self.create_dist(long_description=broken_rest) + cmd = check(dist) + cmd.check_restructuredtext() + self.assertEqual(cmd._warnings, 1) + + # let's see if we have an error with strict=1 + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx', + 'long_description': broken_rest} + self.assertRaises(DistutilsSetupError, self._run, metadata, + **{'strict': 1, 'restructuredtext': 1}) + + # and non-broken rest, including a non-ASCII character to test #12114 + metadata['long_description'] = 'title\n=====\n\ntest \u00df' + cmd = self._run(metadata, strict=1, restructuredtext=1) + self.assertEqual(cmd._warnings, 0) + + # check that includes work to test #31292 + metadata['long_description'] = 'title\n=====\n\n.. include:: includetest.rst' + cmd = self._run(metadata, cwd=HERE, strict=1, restructuredtext=1) + self.assertEqual(cmd._warnings, 0) + + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") + def test_check_restructuredtext_with_syntax_highlight(self): + # Don't fail if there is a `code` or `code-block` directive + + example_rst_docs = [] + example_rst_docs.append(textwrap.dedent("""\ + Here's some code: + + .. code:: python + + def foo(): + pass + """)) + example_rst_docs.append(textwrap.dedent("""\ + Here's some code: + + .. code-block:: python + + def foo(): + pass + """)) + + for rest_with_code in example_rst_docs: + pkg_info, dist = self.create_dist(long_description=rest_with_code) + cmd = check(dist) + cmd.check_restructuredtext() + msgs = cmd._check_rst_data(rest_with_code) + if pygments is not None: + self.assertEqual(len(msgs), 0) + else: + self.assertEqual(len(msgs), 1) + self.assertEqual( + str(msgs[0][1]), + 'Cannot analyze code. Pygments package not found.' + ) + + def test_check_all(self): + + metadata = {'url': 'xxx', 'author': 'xxx'} + self.assertRaises(DistutilsSetupError, self._run, + {}, **{'strict': 1, + 'restructuredtext': 1}) + +def test_suite(): + return unittest.makeSuite(CheckTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_clean.py b/distutils/tests/test_clean.py new file mode 100644 index 00000000..c605afd8 --- /dev/null +++ b/distutils/tests/test_clean.py @@ -0,0 +1,49 @@ +"""Tests for distutils.command.clean.""" +import os +import unittest + +from distutils.command.clean import clean +from distutils.tests import support +from test.support import run_unittest + +class cleanTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_simple_run(self): + pkg_dir, dist = self.create_dist() + cmd = clean(dist) + + # let's add some elements clean should remove + dirs = [(d, os.path.join(pkg_dir, d)) + for d in ('build_temp', 'build_lib', 'bdist_base', + 'build_scripts', 'build_base')] + + for name, path in dirs: + os.mkdir(path) + setattr(cmd, name, path) + if name == 'build_base': + continue + for f in ('one', 'two', 'three'): + self.write_file(os.path.join(path, f)) + + # let's run the command + cmd.all = 1 + cmd.ensure_finalized() + cmd.run() + + # make sure the files where removed + for name, path in dirs: + self.assertFalse(os.path.exists(path), + '%s was not removed' % path) + + # let's run the command again (should spit warnings but succeed) + cmd.all = 1 + cmd.ensure_finalized() + cmd.run() + +def test_suite(): + return unittest.makeSuite(cleanTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_cmd.py b/distutils/tests/test_cmd.py new file mode 100644 index 00000000..cf5197c3 --- /dev/null +++ b/distutils/tests/test_cmd.py @@ -0,0 +1,126 @@ +"""Tests for distutils.cmd.""" +import unittest +import os +from test.support import captured_stdout, run_unittest + +from distutils.cmd import Command +from distutils.dist import Distribution +from distutils.errors import DistutilsOptionError +from distutils import debug + +class MyCmd(Command): + def initialize_options(self): + pass + +class CommandTestCase(unittest.TestCase): + + def setUp(self): + dist = Distribution() + self.cmd = MyCmd(dist) + + def test_ensure_string_list(self): + + cmd = self.cmd + cmd.not_string_list = ['one', 2, 'three'] + cmd.yes_string_list = ['one', 'two', 'three'] + cmd.not_string_list2 = object() + cmd.yes_string_list2 = 'ok' + cmd.ensure_string_list('yes_string_list') + cmd.ensure_string_list('yes_string_list2') + + self.assertRaises(DistutilsOptionError, + cmd.ensure_string_list, 'not_string_list') + + self.assertRaises(DistutilsOptionError, + cmd.ensure_string_list, 'not_string_list2') + + cmd.option1 = 'ok,dok' + cmd.ensure_string_list('option1') + self.assertEqual(cmd.option1, ['ok', 'dok']) + + cmd.option2 = ['xxx', 'www'] + cmd.ensure_string_list('option2') + + cmd.option3 = ['ok', 2] + self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, + 'option3') + + + def test_make_file(self): + + cmd = self.cmd + + # making sure it raises when infiles is not a string or a list/tuple + self.assertRaises(TypeError, cmd.make_file, + infiles=1, outfile='', func='func', args=()) + + # making sure execute gets called properly + def _execute(func, args, exec_msg, level): + self.assertEqual(exec_msg, 'generating out from in') + cmd.force = True + cmd.execute = _execute + cmd.make_file(infiles='in', outfile='out', func='func', args=()) + + def test_dump_options(self): + + msgs = [] + def _announce(msg, level): + msgs.append(msg) + cmd = self.cmd + cmd.announce = _announce + cmd.option1 = 1 + cmd.option2 = 1 + cmd.user_options = [('option1', '', ''), ('option2', '', '')] + cmd.dump_options() + + wanted = ["command options for 'MyCmd':", ' option1 = 1', + ' option2 = 1'] + self.assertEqual(msgs, wanted) + + def test_ensure_string(self): + cmd = self.cmd + cmd.option1 = 'ok' + cmd.ensure_string('option1') + + cmd.option2 = None + cmd.ensure_string('option2', 'xxx') + self.assertTrue(hasattr(cmd, 'option2')) + + cmd.option3 = 1 + self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') + + def test_ensure_filename(self): + cmd = self.cmd + cmd.option1 = __file__ + cmd.ensure_filename('option1') + cmd.option2 = 'xxx' + self.assertRaises(DistutilsOptionError, cmd.ensure_filename, 'option2') + + def test_ensure_dirname(self): + cmd = self.cmd + cmd.option1 = os.path.dirname(__file__) or os.curdir + cmd.ensure_dirname('option1') + cmd.option2 = 'xxx' + self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') + + def test_debug_print(self): + cmd = self.cmd + with captured_stdout() as stdout: + cmd.debug_print('xxx') + stdout.seek(0) + self.assertEqual(stdout.read(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + cmd.debug_print('xxx') + stdout.seek(0) + self.assertEqual(stdout.read(), 'xxx\n') + finally: + debug.DEBUG = False + +def test_suite(): + return unittest.makeSuite(CommandTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/distutils/tests/test_config.py b/distutils/tests/test_config.py new file mode 100644 index 00000000..344084af --- /dev/null +++ b/distutils/tests/test_config.py @@ -0,0 +1,141 @@ +"""Tests for distutils.pypirc.pypirc.""" +import os +import unittest + +from distutils.core import PyPIRCCommand +from distutils.core import Distribution +from distutils.log import set_threshold +from distutils.log import WARN + +from distutils.tests import support +from test.support import run_unittest + +PYPIRC = """\ +[distutils] + +index-servers = + server1 + server2 + server3 + +[server1] +username:me +password:secret + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ + +[server3] +username:cbiggles +password:yh^%#rest-of-my-password +""" + +PYPIRC_OLD = """\ +[server-login] +username:tarek +password:secret +""" + +WANTED = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:xxx +""" + + +class BasePyPIRCCommandTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + def setUp(self): + """Patches the environment.""" + super(BasePyPIRCCommandTestCase, self).setUp() + self.tmp_dir = self.mkdtemp() + os.environ['HOME'] = self.tmp_dir + os.environ['USERPROFILE'] = self.tmp_dir + self.rc = os.path.join(self.tmp_dir, '.pypirc') + self.dist = Distribution() + + class command(PyPIRCCommand): + def __init__(self, dist): + PyPIRCCommand.__init__(self, dist) + def initialize_options(self): + pass + finalize_options = initialize_options + + self._cmd = command + self.old_threshold = set_threshold(WARN) + + def tearDown(self): + """Removes the patch.""" + set_threshold(self.old_threshold) + super(BasePyPIRCCommandTestCase, self).tearDown() + + +class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase): + + def test_server_registration(self): + # This test makes sure PyPIRCCommand knows how to: + # 1. handle several sections in .pypirc + # 2. handle the old format + + # new format + self.write_file(self.rc, PYPIRC) + cmd = self._cmd(self.dist) + config = cmd._read_pypirc() + + config = list(sorted(config.items())) + waited = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'https://upload.pypi.org/legacy/'), + ('server', 'server1'), ('username', 'me')] + self.assertEqual(config, waited) + + # old format + self.write_file(self.rc, PYPIRC_OLD) + config = cmd._read_pypirc() + config = list(sorted(config.items())) + waited = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'https://upload.pypi.org/legacy/'), + ('server', 'server-login'), ('username', 'tarek')] + self.assertEqual(config, waited) + + def test_server_empty_registration(self): + cmd = self._cmd(self.dist) + rc = cmd._get_rc_file() + self.assertFalse(os.path.exists(rc)) + cmd._store_pypirc('tarek', 'xxx') + self.assertTrue(os.path.exists(rc)) + f = open(rc) + try: + content = f.read() + self.assertEqual(content, WANTED) + finally: + f.close() + + def test_config_interpolation(self): + # using the % character in .pypirc should not raise an error (#20120) + self.write_file(self.rc, PYPIRC) + cmd = self._cmd(self.dist) + cmd.repository = 'server3' + config = cmd._read_pypirc() + + config = list(sorted(config.items())) + waited = [('password', 'yh^%#rest-of-my-password'), ('realm', 'pypi'), + ('repository', 'https://upload.pypi.org/legacy/'), + ('server', 'server3'), ('username', 'cbiggles')] + self.assertEqual(config, waited) + + +def test_suite(): + return unittest.makeSuite(PyPIRCCommandTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_config_cmd.py b/distutils/tests/test_config_cmd.py new file mode 100644 index 00000000..9aeab07b --- /dev/null +++ b/distutils/tests/test_config_cmd.py @@ -0,0 +1,96 @@ +"""Tests for distutils.command.config.""" +import unittest +import os +import sys +from test.support import run_unittest, missing_compiler_executable + +from distutils.command.config import dump_file, config +from distutils.tests import support +from distutils import log + +class ConfigTestCase(support.LoggingSilencer, + support.TempdirManager, + unittest.TestCase): + + def _info(self, msg, *args): + for line in msg.splitlines(): + self._logs.append(line) + + def setUp(self): + super(ConfigTestCase, self).setUp() + self._logs = [] + self.old_log = log.info + log.info = self._info + + def tearDown(self): + log.info = self.old_log + super(ConfigTestCase, self).tearDown() + + def test_dump_file(self): + this_file = os.path.splitext(__file__)[0] + '.py' + f = open(this_file) + try: + numlines = len(f.readlines()) + finally: + f.close() + + dump_file(this_file, 'I am the header') + self.assertEqual(len(self._logs), numlines+1) + + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") + def test_search_cpp(self): + cmd = missing_compiler_executable(['preprocessor']) + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) + pkg_dir, dist = self.create_dist() + cmd = config(dist) + cmd._check_compiler() + compiler = cmd.compiler + if sys.platform[:3] == "aix" and "xlc" in compiler.preprocessor[0].lower(): + self.skipTest('xlc: The -E option overrides the -P, -o, and -qsyntaxonly options') + + # simple pattern searches + match = cmd.search_cpp(pattern='xxx', body='/* xxx */') + self.assertEqual(match, 0) + + match = cmd.search_cpp(pattern='_configtest', body='/* xxx */') + self.assertEqual(match, 1) + + def test_finalize_options(self): + # finalize_options does a bit of transformation + # on options + pkg_dir, dist = self.create_dist() + cmd = config(dist) + cmd.include_dirs = 'one%stwo' % os.pathsep + cmd.libraries = 'one' + cmd.library_dirs = 'three%sfour' % os.pathsep + cmd.ensure_finalized() + + self.assertEqual(cmd.include_dirs, ['one', 'two']) + self.assertEqual(cmd.libraries, ['one']) + self.assertEqual(cmd.library_dirs, ['three', 'four']) + + def test_clean(self): + # _clean removes files + tmp_dir = self.mkdtemp() + f1 = os.path.join(tmp_dir, 'one') + f2 = os.path.join(tmp_dir, 'two') + + self.write_file(f1, 'xxx') + self.write_file(f2, 'xxx') + + for f in (f1, f2): + self.assertTrue(os.path.exists(f)) + + pkg_dir, dist = self.create_dist() + cmd = config(dist) + cmd._clean(f1, f2) + + for f in (f1, f2): + self.assertFalse(os.path.exists(f)) + +def test_suite(): + return unittest.makeSuite(ConfigTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_core.py b/distutils/tests/test_core.py new file mode 100644 index 00000000..27ce7324 --- /dev/null +++ b/distutils/tests/test_core.py @@ -0,0 +1,140 @@ +"""Tests for distutils.core.""" + +import io +import distutils.core +import os +import shutil +import sys +import test.support +from test.support import captured_stdout, run_unittest +import unittest +from distutils.tests import support +from distutils import log + +# setup script that uses __file__ +setup_using___file__ = """\ + +__file__ + +from distutils.core import setup +setup() +""" + +setup_prints_cwd = """\ + +import os +print(os.getcwd()) + +from distutils.core import setup +setup() +""" + +setup_does_nothing = """\ +from distutils.core import setup +setup() +""" + + +setup_defines_subclass = """\ +from distutils.core import setup +from distutils.command.install import install as _install + +class install(_install): + sub_commands = _install.sub_commands + ['cmd'] + +setup(cmdclass={'install': install}) +""" + +class CoreTestCase(support.EnvironGuard, unittest.TestCase): + + def setUp(self): + super(CoreTestCase, self).setUp() + self.old_stdout = sys.stdout + self.cleanup_testfn() + self.old_argv = sys.argv, sys.argv[:] + self.addCleanup(log.set_threshold, log._global_log.threshold) + + def tearDown(self): + sys.stdout = self.old_stdout + self.cleanup_testfn() + sys.argv = self.old_argv[0] + sys.argv[:] = self.old_argv[1] + super(CoreTestCase, self).tearDown() + + def cleanup_testfn(self): + path = test.support.TESTFN + if os.path.isfile(path): + os.remove(path) + elif os.path.isdir(path): + shutil.rmtree(path) + + def write_setup(self, text, path=test.support.TESTFN): + f = open(path, "w") + try: + f.write(text) + finally: + f.close() + return path + + def test_run_setup_provides_file(self): + # Make sure the script can use __file__; if that's missing, the test + # setup.py script will raise NameError. + distutils.core.run_setup( + self.write_setup(setup_using___file__)) + + def test_run_setup_preserves_sys_argv(self): + # Make sure run_setup does not clobber sys.argv + argv_copy = sys.argv.copy() + distutils.core.run_setup( + self.write_setup(setup_does_nothing)) + self.assertEqual(sys.argv, argv_copy) + + def test_run_setup_defines_subclass(self): + # Make sure the script can use __file__; if that's missing, the test + # setup.py script will raise NameError. + dist = distutils.core.run_setup( + self.write_setup(setup_defines_subclass)) + install = dist.get_command_obj('install') + self.assertIn('cmd', install.sub_commands) + + def test_run_setup_uses_current_dir(self): + # This tests that the setup script is run with the current directory + # as its own current directory; this was temporarily broken by a + # previous patch when TESTFN did not use the current directory. + sys.stdout = io.StringIO() + cwd = os.getcwd() + + # Create a directory and write the setup.py file there: + os.mkdir(test.support.TESTFN) + setup_py = os.path.join(test.support.TESTFN, "setup.py") + distutils.core.run_setup( + self.write_setup(setup_prints_cwd, path=setup_py)) + + output = sys.stdout.getvalue() + if output.endswith("\n"): + output = output[:-1] + self.assertEqual(cwd, output) + + def test_debug_mode(self): + # this covers the code called when DEBUG is set + sys.argv = ['setup.py', '--name'] + with captured_stdout() as stdout: + distutils.core.setup(name='bar') + stdout.seek(0) + self.assertEqual(stdout.read(), 'bar\n') + + distutils.core.DEBUG = True + try: + with captured_stdout() as stdout: + distutils.core.setup(name='bar') + finally: + distutils.core.DEBUG = False + stdout.seek(0) + wanted = "options (after parsing config files):\n" + self.assertEqual(stdout.readlines()[0], wanted) + +def test_suite(): + return unittest.makeSuite(CoreTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_cygwinccompiler.py b/distutils/tests/test_cygwinccompiler.py new file mode 100644 index 00000000..9dc869de --- /dev/null +++ b/distutils/tests/test_cygwinccompiler.py @@ -0,0 +1,154 @@ +"""Tests for distutils.cygwinccompiler.""" +import unittest +import sys +import os +from io import BytesIO +from test.support import run_unittest + +from distutils import cygwinccompiler +from distutils.cygwinccompiler import (check_config_h, + CONFIG_H_OK, CONFIG_H_NOTOK, + CONFIG_H_UNCERTAIN, get_versions, + get_msvcr) +from distutils.tests import support + +class FakePopen(object): + test_class = None + + def __init__(self, cmd, shell, stdout): + self.cmd = cmd.split()[0] + exes = self.test_class._exes + if self.cmd in exes: + # issue #6438 in Python 3.x, Popen returns bytes + self.stdout = BytesIO(exes[self.cmd]) + else: + self.stdout = os.popen(cmd, 'r') + + +class CygwinCCompilerTestCase(support.TempdirManager, + unittest.TestCase): + + def setUp(self): + super(CygwinCCompilerTestCase, self).setUp() + self.version = sys.version + self.python_h = os.path.join(self.mkdtemp(), 'python.h') + from distutils import sysconfig + self.old_get_config_h_filename = sysconfig.get_config_h_filename + sysconfig.get_config_h_filename = self._get_config_h_filename + self.old_find_executable = cygwinccompiler.find_executable + cygwinccompiler.find_executable = self._find_executable + self._exes = {} + self.old_popen = cygwinccompiler.Popen + FakePopen.test_class = self + cygwinccompiler.Popen = FakePopen + + def tearDown(self): + sys.version = self.version + from distutils import sysconfig + sysconfig.get_config_h_filename = self.old_get_config_h_filename + cygwinccompiler.find_executable = self.old_find_executable + cygwinccompiler.Popen = self.old_popen + super(CygwinCCompilerTestCase, self).tearDown() + + def _get_config_h_filename(self): + return self.python_h + + def _find_executable(self, name): + if name in self._exes: + return name + return None + + def test_check_config_h(self): + + # check_config_h looks for "GCC" in sys.version first + # returns CONFIG_H_OK if found + sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' + '4.0.1 (Apple Computer, Inc. build 5370)]') + + self.assertEqual(check_config_h()[0], CONFIG_H_OK) + + # then it tries to see if it can find "__GNUC__" in pyconfig.h + sys.version = 'something without the *CC word' + + # if the file doesn't exist it returns CONFIG_H_UNCERTAIN + self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN) + + # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK + self.write_file(self.python_h, 'xxx') + self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK) + + # and CONFIG_H_OK if __GNUC__ is found + self.write_file(self.python_h, 'xxx __GNUC__ xxx') + self.assertEqual(check_config_h()[0], CONFIG_H_OK) + + def test_get_versions(self): + + # get_versions calls distutils.spawn.find_executable on + # 'gcc', 'ld' and 'dllwrap' + self.assertEqual(get_versions(), (None, None, None)) + + # Let's fake we have 'gcc' and it returns '3.4.5' + self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' + res = get_versions() + self.assertEqual(str(res[0]), '3.4.5') + + # and let's see what happens when the version + # doesn't match the regular expression + # (\d+\.\d+(\.\d+)*) + self._exes['gcc'] = b'very strange output' + res = get_versions() + self.assertEqual(res[0], None) + + # same thing for ld + self._exes['ld'] = b'GNU ld version 2.17.50 20060824' + res = get_versions() + self.assertEqual(str(res[1]), '2.17.50') + self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_versions() + self.assertEqual(res[1], None) + + # and dllwrap + self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' + res = get_versions() + self.assertEqual(str(res[2]), '2.17.50') + self._exes['dllwrap'] = b'Cheese Wrap' + res = get_versions() + self.assertEqual(res[2], None) + + def test_get_msvcr(self): + + # none + sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' + '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') + self.assertEqual(get_msvcr(), None) + + # MSVC 7.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1300 32 bits (Intel)]') + self.assertEqual(get_msvcr(), ['msvcr70']) + + # MSVC 7.1 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1310 32 bits (Intel)]') + self.assertEqual(get_msvcr(), ['msvcr71']) + + # VS2005 / MSVC 8.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1400 32 bits (Intel)]') + self.assertEqual(get_msvcr(), ['msvcr80']) + + # VS2008 / MSVC 9.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1500 32 bits (Intel)]') + self.assertEqual(get_msvcr(), ['msvcr90']) + + # unknown + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1999 32 bits (Intel)]') + self.assertRaises(ValueError, get_msvcr) + +def test_suite(): + return unittest.makeSuite(CygwinCCompilerTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/distutils/tests/test_dep_util.py b/distutils/tests/test_dep_util.py new file mode 100644 index 00000000..c6fae39c --- /dev/null +++ b/distutils/tests/test_dep_util.py @@ -0,0 +1,80 @@ +"""Tests for distutils.dep_util.""" +import unittest +import os + +from distutils.dep_util import newer, newer_pairwise, newer_group +from distutils.errors import DistutilsFileError +from distutils.tests import support +from test.support import run_unittest + +class DepUtilTestCase(support.TempdirManager, unittest.TestCase): + + def test_newer(self): + + tmpdir = self.mkdtemp() + new_file = os.path.join(tmpdir, 'new') + old_file = os.path.abspath(__file__) + + # Raise DistutilsFileError if 'new_file' does not exist. + self.assertRaises(DistutilsFileError, newer, new_file, old_file) + + # Return true if 'new_file' exists and is more recently modified than + # 'old_file', or if 'new_file' exists and 'old_file' doesn't. + self.write_file(new_file) + self.assertTrue(newer(new_file, 'I_dont_exist')) + self.assertTrue(newer(new_file, old_file)) + + # Return false if both exist and 'old_file' is the same age or younger + # than 'new_file'. + self.assertFalse(newer(old_file, new_file)) + + def test_newer_pairwise(self): + tmpdir = self.mkdtemp() + sources = os.path.join(tmpdir, 'sources') + targets = os.path.join(tmpdir, 'targets') + os.mkdir(sources) + os.mkdir(targets) + one = os.path.join(sources, 'one') + two = os.path.join(sources, 'two') + three = os.path.abspath(__file__) # I am the old file + four = os.path.join(targets, 'four') + self.write_file(one) + self.write_file(two) + self.write_file(four) + + self.assertEqual(newer_pairwise([one, two], [three, four]), + ([one],[three])) + + def test_newer_group(self): + tmpdir = self.mkdtemp() + sources = os.path.join(tmpdir, 'sources') + os.mkdir(sources) + one = os.path.join(sources, 'one') + two = os.path.join(sources, 'two') + three = os.path.join(sources, 'three') + old_file = os.path.abspath(__file__) + + # return true if 'old_file' is out-of-date with respect to any file + # listed in 'sources'. + self.write_file(one) + self.write_file(two) + self.write_file(three) + self.assertTrue(newer_group([one, two, three], old_file)) + self.assertFalse(newer_group([one, two, old_file], three)) + + # missing handling + os.remove(one) + self.assertRaises(OSError, newer_group, [one, two, old_file], three) + + self.assertFalse(newer_group([one, two, old_file], three, + missing='ignore')) + + self.assertTrue(newer_group([one, two, old_file], three, + missing='newer')) + + +def test_suite(): + return unittest.makeSuite(DepUtilTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_dir_util.py b/distutils/tests/test_dir_util.py new file mode 100644 index 00000000..d436cf83 --- /dev/null +++ b/distutils/tests/test_dir_util.py @@ -0,0 +1,139 @@ +"""Tests for distutils.dir_util.""" +import unittest +import os +import stat +import sys +from unittest.mock import patch + +from distutils import dir_util, errors +from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, + ensure_relative) + +from distutils import log +from distutils.tests import support +from test.support import run_unittest + + +class DirUtilTestCase(support.TempdirManager, unittest.TestCase): + + def _log(self, msg, *args): + if len(args) > 0: + self._logs.append(msg % args) + else: + self._logs.append(msg) + + def setUp(self): + super(DirUtilTestCase, self).setUp() + self._logs = [] + tmp_dir = self.mkdtemp() + self.root_target = os.path.join(tmp_dir, 'deep') + self.target = os.path.join(self.root_target, 'here') + self.target2 = os.path.join(tmp_dir, 'deep2') + self.old_log = log.info + log.info = self._log + + def tearDown(self): + log.info = self.old_log + super(DirUtilTestCase, self).tearDown() + + def test_mkpath_remove_tree_verbosity(self): + + mkpath(self.target, verbose=0) + wanted = [] + self.assertEqual(self._logs, wanted) + remove_tree(self.root_target, verbose=0) + + mkpath(self.target, verbose=1) + wanted = ['creating %s' % self.root_target, + 'creating %s' % self.target] + self.assertEqual(self._logs, wanted) + self._logs = [] + + remove_tree(self.root_target, verbose=1) + wanted = ["removing '%s' (and everything under it)" % self.root_target] + self.assertEqual(self._logs, wanted) + + @unittest.skipIf(sys.platform.startswith('win'), + "This test is only appropriate for POSIX-like systems.") + def test_mkpath_with_custom_mode(self): + # Get and set the current umask value for testing mode bits. + umask = os.umask(0o002) + os.umask(umask) + mkpath(self.target, 0o700) + self.assertEqual( + stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) + mkpath(self.target2, 0o555) + self.assertEqual( + stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) + + def test_create_tree_verbosity(self): + + create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) + self.assertEqual(self._logs, []) + remove_tree(self.root_target, verbose=0) + + wanted = ['creating %s' % self.root_target] + create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) + self.assertEqual(self._logs, wanted) + + remove_tree(self.root_target, verbose=0) + + def test_copy_tree_verbosity(self): + + mkpath(self.target, verbose=0) + + copy_tree(self.target, self.target2, verbose=0) + self.assertEqual(self._logs, []) + + remove_tree(self.root_target, verbose=0) + + mkpath(self.target, verbose=0) + a_file = os.path.join(self.target, 'ok.txt') + with open(a_file, 'w') as f: + f.write('some content') + + wanted = ['copying %s -> %s' % (a_file, self.target2)] + copy_tree(self.target, self.target2, verbose=1) + self.assertEqual(self._logs, wanted) + + remove_tree(self.root_target, verbose=0) + remove_tree(self.target2, verbose=0) + + def test_copy_tree_skips_nfs_temp_files(self): + mkpath(self.target, verbose=0) + + a_file = os.path.join(self.target, 'ok.txt') + nfs_file = os.path.join(self.target, '.nfs123abc') + for f in a_file, nfs_file: + with open(f, 'w') as fh: + fh.write('some content') + + copy_tree(self.target, self.target2) + self.assertEqual(os.listdir(self.target2), ['ok.txt']) + + remove_tree(self.root_target, verbose=0) + remove_tree(self.target2, verbose=0) + + def test_ensure_relative(self): + if os.sep == '/': + self.assertEqual(ensure_relative('/home/foo'), 'home/foo') + self.assertEqual(ensure_relative('some/path'), 'some/path') + else: # \\ + self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') + self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') + + def test_copy_tree_exception_in_listdir(self): + """ + An exception in listdir should raise a DistutilsFileError + """ + with patch("os.listdir", side_effect=OSError()), \ + self.assertRaises(errors.DistutilsFileError): + src = self.tempdirs[-1] + dir_util.copy_tree(src, None) + + +def test_suite(): + return unittest.makeSuite(DirUtilTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_dist.py b/distutils/tests/test_dist.py new file mode 100644 index 00000000..60956dad --- /dev/null +++ b/distutils/tests/test_dist.py @@ -0,0 +1,528 @@ +"""Tests for distutils.dist.""" +import os +import io +import sys +import unittest +import warnings +import textwrap + +from unittest import mock + +from distutils.dist import Distribution, fix_help_options +from distutils.cmd import Command + +from test.support import ( + TESTFN, captured_stdout, captured_stderr, run_unittest +) +from distutils.tests import support +from distutils import log + + +class test_dist(Command): + """Sample distutils extension command.""" + + user_options = [ + ("sample-option=", "S", "help text"), + ] + + def initialize_options(self): + self.sample_option = None + + +class TestDistribution(Distribution): + """Distribution subclasses that avoids the default search for + configuration files. + + The ._config_files attribute must be set before + .parse_config_files() is called. + """ + + def find_config_files(self): + return self._config_files + + +class DistributionTestCase(support.LoggingSilencer, + support.TempdirManager, + support.EnvironGuard, + unittest.TestCase): + + def setUp(self): + super(DistributionTestCase, self).setUp() + self.argv = sys.argv, sys.argv[:] + del sys.argv[1:] + + def tearDown(self): + sys.argv = self.argv[0] + sys.argv[:] = self.argv[1] + super(DistributionTestCase, self).tearDown() + + def create_distribution(self, configfiles=()): + d = TestDistribution() + d._config_files = configfiles + d.parse_config_files() + d.parse_command_line() + return d + + def test_command_packages_unspecified(self): + sys.argv.append("build") + d = self.create_distribution() + self.assertEqual(d.get_command_packages(), ["distutils.command"]) + + def test_command_packages_cmdline(self): + from distutils.tests.test_dist import test_dist + sys.argv.extend(["--command-packages", + "foo.bar,distutils.tests", + "test_dist", + "-Ssometext", + ]) + d = self.create_distribution() + # let's actually try to load our test command: + self.assertEqual(d.get_command_packages(), + ["distutils.command", "foo.bar", "distutils.tests"]) + cmd = d.get_command_obj("test_dist") + self.assertIsInstance(cmd, test_dist) + self.assertEqual(cmd.sample_option, "sometext") + + def test_venv_install_options(self): + sys.argv.append("install") + self.addCleanup(os.unlink, TESTFN) + + fakepath = '/somedir' + + with open(TESTFN, "w") as f: + print(("[install]\n" + "install-base = {0}\n" + "install-platbase = {0}\n" + "install-lib = {0}\n" + "install-platlib = {0}\n" + "install-purelib = {0}\n" + "install-headers = {0}\n" + "install-scripts = {0}\n" + "install-data = {0}\n" + "prefix = {0}\n" + "exec-prefix = {0}\n" + "home = {0}\n" + "user = {0}\n" + "root = {0}").format(fakepath), file=f) + + # Base case: Not in a Virtual Environment + with mock.patch.multiple(sys, prefix='/a', base_prefix='/a') as values: + d = self.create_distribution([TESTFN]) + + option_tuple = (TESTFN, fakepath) + + result_dict = { + 'install_base': option_tuple, + 'install_platbase': option_tuple, + 'install_lib': option_tuple, + 'install_platlib': option_tuple, + 'install_purelib': option_tuple, + 'install_headers': option_tuple, + 'install_scripts': option_tuple, + 'install_data': option_tuple, + 'prefix': option_tuple, + 'exec_prefix': option_tuple, + 'home': option_tuple, + 'user': option_tuple, + 'root': option_tuple, + } + + self.assertEqual( + sorted(d.command_options.get('install').keys()), + sorted(result_dict.keys())) + + for (key, value) in d.command_options.get('install').items(): + self.assertEqual(value, result_dict[key]) + + # Test case: In a Virtual Environment + with mock.patch.multiple(sys, prefix='/a', base_prefix='/b') as values: + d = self.create_distribution([TESTFN]) + + for key in result_dict.keys(): + self.assertNotIn(key, d.command_options.get('install', {})) + + def test_command_packages_configfile(self): + sys.argv.append("build") + self.addCleanup(os.unlink, TESTFN) + f = open(TESTFN, "w") + try: + print("[global]", file=f) + print("command_packages = foo.bar, splat", file=f) + finally: + f.close() + + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "foo.bar", "splat"]) + + # ensure command line overrides config: + sys.argv[1:] = ["--command-packages", "spork", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "spork"]) + + # Setting --command-packages to '' should cause the default to + # be used even if a config file specified something else: + sys.argv[1:] = ["--command-packages", "", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), ["distutils.command"]) + + def test_empty_options(self): + # an empty options dictionary should not stay in the + # list of attributes + + # catching warnings + warns = [] + + def _warn(msg): + warns.append(msg) + + self.addCleanup(setattr, warnings, 'warn', warnings.warn) + warnings.warn = _warn + dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx', + 'version': 'xxx', 'url': 'xxxx', + 'options': {}}) + + self.assertEqual(len(warns), 0) + self.assertNotIn('options', dir(dist)) + + def test_finalize_options(self): + attrs = {'keywords': 'one,two', + 'platforms': 'one,two'} + + dist = Distribution(attrs=attrs) + dist.finalize_options() + + # finalize_option splits platforms and keywords + self.assertEqual(dist.metadata.platforms, ['one', 'two']) + self.assertEqual(dist.metadata.keywords, ['one', 'two']) + + attrs = {'keywords': 'foo bar', + 'platforms': 'foo bar'} + dist = Distribution(attrs=attrs) + dist.finalize_options() + self.assertEqual(dist.metadata.platforms, ['foo bar']) + self.assertEqual(dist.metadata.keywords, ['foo bar']) + + def test_get_command_packages(self): + dist = Distribution() + self.assertEqual(dist.command_packages, None) + cmds = dist.get_command_packages() + self.assertEqual(cmds, ['distutils.command']) + self.assertEqual(dist.command_packages, + ['distutils.command']) + + dist.command_packages = 'one,two' + cmds = dist.get_command_packages() + self.assertEqual(cmds, ['distutils.command', 'one', 'two']) + + def test_announce(self): + # make sure the level is known + dist = Distribution() + args = ('ok',) + kwargs = {'level': 'ok2'} + self.assertRaises(ValueError, dist.announce, args, kwargs) + + + def test_find_config_files_disable(self): + # Ticket #1180: Allow user to disable their home config file. + temp_home = self.mkdtemp() + if os.name == 'posix': + user_filename = os.path.join(temp_home, ".pydistutils.cfg") + else: + user_filename = os.path.join(temp_home, "pydistutils.cfg") + + with open(user_filename, 'w') as f: + f.write('[distutils]\n') + + def _expander(path): + return temp_home + + old_expander = os.path.expanduser + os.path.expanduser = _expander + try: + d = Distribution() + all_files = d.find_config_files() + + d = Distribution(attrs={'script_args': ['--no-user-cfg']}) + files = d.find_config_files() + finally: + os.path.expanduser = old_expander + + # make sure --no-user-cfg disables the user cfg file + self.assertEqual(len(all_files)-1, len(files)) + +class MetadataTestCase(support.TempdirManager, support.EnvironGuard, + unittest.TestCase): + + def setUp(self): + super(MetadataTestCase, self).setUp() + self.argv = sys.argv, sys.argv[:] + + def tearDown(self): + sys.argv = self.argv[0] + sys.argv[:] = self.argv[1] + super(MetadataTestCase, self).tearDown() + + def format_metadata(self, dist): + sio = io.StringIO() + dist.metadata.write_pkg_file(sio) + return sio.getvalue() + + def test_simple_metadata(self): + attrs = {"name": "package", + "version": "1.0"} + dist = Distribution(attrs) + meta = self.format_metadata(dist) + self.assertIn("Metadata-Version: 1.0", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertNotIn("requires:", meta.lower()) + self.assertNotIn("obsoletes:", meta.lower()) + + def test_provides(self): + attrs = {"name": "package", + "version": "1.0", + "provides": ["package", "package.sub"]} + dist = Distribution(attrs) + self.assertEqual(dist.metadata.get_provides(), + ["package", "package.sub"]) + self.assertEqual(dist.get_provides(), + ["package", "package.sub"]) + meta = self.format_metadata(dist) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("requires:", meta.lower()) + self.assertNotIn("obsoletes:", meta.lower()) + + def test_provides_illegal(self): + self.assertRaises(ValueError, Distribution, + {"name": "package", + "version": "1.0", + "provides": ["my.pkg (splat)"]}) + + def test_requires(self): + attrs = {"name": "package", + "version": "1.0", + "requires": ["other", "another (==1.0)"]} + dist = Distribution(attrs) + self.assertEqual(dist.metadata.get_requires(), + ["other", "another (==1.0)"]) + self.assertEqual(dist.get_requires(), + ["other", "another (==1.0)"]) + meta = self.format_metadata(dist) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertIn("Requires: other", meta) + self.assertIn("Requires: another (==1.0)", meta) + self.assertNotIn("obsoletes:", meta.lower()) + + def test_requires_illegal(self): + self.assertRaises(ValueError, Distribution, + {"name": "package", + "version": "1.0", + "requires": ["my.pkg (splat)"]}) + + def test_requires_to_list(self): + attrs = {"name": "package", + "requires": iter(["other"])} + dist = Distribution(attrs) + self.assertIsInstance(dist.metadata.requires, list) + + + def test_obsoletes(self): + attrs = {"name": "package", + "version": "1.0", + "obsoletes": ["other", "another (<1.0)"]} + dist = Distribution(attrs) + self.assertEqual(dist.metadata.get_obsoletes(), + ["other", "another (<1.0)"]) + self.assertEqual(dist.get_obsoletes(), + ["other", "another (<1.0)"]) + meta = self.format_metadata(dist) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertNotIn("requires:", meta.lower()) + self.assertIn("Obsoletes: other", meta) + self.assertIn("Obsoletes: another (<1.0)", meta) + + def test_obsoletes_illegal(self): + self.assertRaises(ValueError, Distribution, + {"name": "package", + "version": "1.0", + "obsoletes": ["my.pkg (splat)"]}) + + def test_obsoletes_to_list(self): + attrs = {"name": "package", + "obsoletes": iter(["other"])} + dist = Distribution(attrs) + self.assertIsInstance(dist.metadata.obsoletes, list) + + def test_classifier(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'classifiers': ['Programming Language :: Python :: 3']} + dist = Distribution(attrs) + self.assertEqual(dist.get_classifiers(), + ['Programming Language :: Python :: 3']) + meta = self.format_metadata(dist) + self.assertIn('Metadata-Version: 1.1', meta) + + def test_classifier_invalid_type(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'classifiers': ('Programming Language :: Python :: 3',)} + with captured_stderr() as error: + d = Distribution(attrs) + # should have warning about passing a non-list + self.assertIn('should be a list', error.getvalue()) + # should be converted to a list + self.assertIsInstance(d.metadata.classifiers, list) + self.assertEqual(d.metadata.classifiers, + list(attrs['classifiers'])) + + def test_keywords(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'keywords': ['spam', 'eggs', 'life of brian']} + dist = Distribution(attrs) + self.assertEqual(dist.get_keywords(), + ['spam', 'eggs', 'life of brian']) + + def test_keywords_invalid_type(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'keywords': ('spam', 'eggs', 'life of brian')} + with captured_stderr() as error: + d = Distribution(attrs) + # should have warning about passing a non-list + self.assertIn('should be a list', error.getvalue()) + # should be converted to a list + self.assertIsInstance(d.metadata.keywords, list) + self.assertEqual(d.metadata.keywords, list(attrs['keywords'])) + + def test_platforms(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'platforms': ['GNU/Linux', 'Some Evil Platform']} + dist = Distribution(attrs) + self.assertEqual(dist.get_platforms(), + ['GNU/Linux', 'Some Evil Platform']) + + def test_platforms_invalid_types(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'platforms': ('GNU/Linux', 'Some Evil Platform')} + with captured_stderr() as error: + d = Distribution(attrs) + # should have warning about passing a non-list + self.assertIn('should be a list', error.getvalue()) + # should be converted to a list + self.assertIsInstance(d.metadata.platforms, list) + self.assertEqual(d.metadata.platforms, list(attrs['platforms'])) + + def test_download_url(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'download_url': 'http://example.org/boa'} + dist = Distribution(attrs) + meta = self.format_metadata(dist) + self.assertIn('Metadata-Version: 1.1', meta) + + def test_long_description(self): + long_desc = textwrap.dedent("""\ + example:: + We start here + and continue here + and end here.""") + attrs = {"name": "package", + "version": "1.0", + "long_description": long_desc} + + dist = Distribution(attrs) + meta = self.format_metadata(dist) + meta = meta.replace('\n' + 8 * ' ', '\n') + self.assertIn(long_desc, meta) + + def test_custom_pydistutils(self): + # fixes #2166 + # make sure pydistutils.cfg is found + if os.name == 'posix': + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + + temp_dir = self.mkdtemp() + user_filename = os.path.join(temp_dir, user_filename) + f = open(user_filename, 'w') + try: + f.write('.') + finally: + f.close() + + try: + dist = Distribution() + + # linux-style + if sys.platform in ('linux', 'darwin'): + os.environ['HOME'] = temp_dir + files = dist.find_config_files() + self.assertIn(user_filename, files) + + # win32-style + if sys.platform == 'win32': + # home drive should be found + os.environ['USERPROFILE'] = temp_dir + files = dist.find_config_files() + self.assertIn(user_filename, files, + '%r not found in %r' % (user_filename, files)) + finally: + os.remove(user_filename) + + def test_fix_help_options(self): + help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] + fancy_options = fix_help_options(help_tuples) + self.assertEqual(fancy_options[0], ('a', 'b', 'c')) + self.assertEqual(fancy_options[1], (1, 2, 3)) + + def test_show_help(self): + # smoke test, just makes sure some help is displayed + self.addCleanup(log.set_threshold, log._global_log.threshold) + dist = Distribution() + sys.argv = [] + dist.help = 1 + dist.script_name = 'setup.py' + with captured_stdout() as s: + dist.parse_command_line() + + output = [line for line in s.getvalue().split('\n') + if line.strip() != ''] + self.assertTrue(output) + + + def test_read_metadata(self): + attrs = {"name": "package", + "version": "1.0", + "long_description": "desc", + "description": "xxx", + "download_url": "http://example.com", + "keywords": ['one', 'two'], + "requires": ['foo']} + + dist = Distribution(attrs) + metadata = dist.metadata + + # write it then reloads it + PKG_INFO = io.StringIO() + metadata.write_pkg_file(PKG_INFO) + PKG_INFO.seek(0) + metadata.read_pkg_file(PKG_INFO) + + self.assertEqual(metadata.name, "package") + self.assertEqual(metadata.version, "1.0") + self.assertEqual(metadata.description, "xxx") + self.assertEqual(metadata.download_url, 'http://example.com') + self.assertEqual(metadata.keywords, ['one', 'two']) + self.assertEqual(metadata.platforms, ['UNKNOWN']) + self.assertEqual(metadata.obsoletes, None) + self.assertEqual(metadata.requires, ['foo']) + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(DistributionTestCase)) + suite.addTest(unittest.makeSuite(MetadataTestCase)) + return suite + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_extension.py b/distutils/tests/test_extension.py new file mode 100644 index 00000000..e35f2738 --- /dev/null +++ b/distutils/tests/test_extension.py @@ -0,0 +1,69 @@ +"""Tests for distutils.extension.""" +import unittest +import os +import warnings + +from test.support import check_warnings, run_unittest +from distutils.extension import read_setup_file, Extension + +class ExtensionTestCase(unittest.TestCase): + + def test_read_setup_file(self): + # trying to read a Setup file + # (sample extracted from the PyGame project) + setup = os.path.join(os.path.dirname(__file__), 'Setup.sample') + + exts = read_setup_file(setup) + names = [ext.name for ext in exts] + names.sort() + + # here are the extensions read_setup_file should have created + # out of the file + wanted = ['_arraysurfarray', '_camera', '_numericsndarray', + '_numericsurfarray', 'base', 'bufferproxy', 'cdrom', + 'color', 'constants', 'display', 'draw', 'event', + 'fastevent', 'font', 'gfxdraw', 'image', 'imageext', + 'joystick', 'key', 'mask', 'mixer', 'mixer_music', + 'mouse', 'movie', 'overlay', 'pixelarray', 'pypm', + 'rect', 'rwobject', 'scrap', 'surface', 'surflock', + 'time', 'transform'] + + self.assertEqual(names, wanted) + + def test_extension_init(self): + # the first argument, which is the name, must be a string + self.assertRaises(AssertionError, Extension, 1, []) + ext = Extension('name', []) + self.assertEqual(ext.name, 'name') + + # the second argument, which is the list of files, must + # be a list of strings + self.assertRaises(AssertionError, Extension, 'name', 'file') + self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) + ext = Extension('name', ['file1', 'file2']) + self.assertEqual(ext.sources, ['file1', 'file2']) + + # others arguments have defaults + for attr in ('include_dirs', 'define_macros', 'undef_macros', + 'library_dirs', 'libraries', 'runtime_library_dirs', + 'extra_objects', 'extra_compile_args', 'extra_link_args', + 'export_symbols', 'swig_opts', 'depends'): + self.assertEqual(getattr(ext, attr), []) + + self.assertEqual(ext.language, None) + self.assertEqual(ext.optional, None) + + # if there are unknown keyword options, warn about them + with check_warnings() as w: + warnings.simplefilter('always') + ext = Extension('name', ['file1', 'file2'], chic=True) + + self.assertEqual(len(w.warnings), 1) + self.assertEqual(str(w.warnings[0].message), + "Unknown Extension options: 'chic'") + +def test_suite(): + return unittest.makeSuite(ExtensionTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_file_util.py b/distutils/tests/test_file_util.py new file mode 100644 index 00000000..a4e2d025 --- /dev/null +++ b/distutils/tests/test_file_util.py @@ -0,0 +1,122 @@ +"""Tests for distutils.file_util.""" +import unittest +import os +import errno +from unittest.mock import patch + +from distutils.file_util import move_file, copy_file +from distutils import log +from distutils.tests import support +from distutils.errors import DistutilsFileError +from test.support import run_unittest, unlink + +class FileUtilTestCase(support.TempdirManager, unittest.TestCase): + + def _log(self, msg, *args): + if len(args) > 0: + self._logs.append(msg % args) + else: + self._logs.append(msg) + + def setUp(self): + super(FileUtilTestCase, self).setUp() + self._logs = [] + self.old_log = log.info + log.info = self._log + tmp_dir = self.mkdtemp() + self.source = os.path.join(tmp_dir, 'f1') + self.target = os.path.join(tmp_dir, 'f2') + self.target_dir = os.path.join(tmp_dir, 'd1') + + def tearDown(self): + log.info = self.old_log + super(FileUtilTestCase, self).tearDown() + + def test_move_file_verbosity(self): + f = open(self.source, 'w') + try: + f.write('some content') + finally: + f.close() + + move_file(self.source, self.target, verbose=0) + wanted = [] + self.assertEqual(self._logs, wanted) + + # back to original state + move_file(self.target, self.source, verbose=0) + + move_file(self.source, self.target, verbose=1) + wanted = ['moving %s -> %s' % (self.source, self.target)] + self.assertEqual(self._logs, wanted) + + # back to original state + move_file(self.target, self.source, verbose=0) + + self._logs = [] + # now the target is a dir + os.mkdir(self.target_dir) + move_file(self.source, self.target_dir, verbose=1) + wanted = ['moving %s -> %s' % (self.source, self.target_dir)] + self.assertEqual(self._logs, wanted) + + def test_move_file_exception_unpacking_rename(self): + # see issue 22182 + with patch("os.rename", side_effect=OSError("wrong", 1)), \ + self.assertRaises(DistutilsFileError): + with open(self.source, 'w') as fobj: + fobj.write('spam eggs') + move_file(self.source, self.target, verbose=0) + + def test_move_file_exception_unpacking_unlink(self): + # see issue 22182 + with patch("os.rename", side_effect=OSError(errno.EXDEV, "wrong")), \ + patch("os.unlink", side_effect=OSError("wrong", 1)), \ + self.assertRaises(DistutilsFileError): + with open(self.source, 'w') as fobj: + fobj.write('spam eggs') + move_file(self.source, self.target, verbose=0) + + def test_copy_file_hard_link(self): + with open(self.source, 'w') as f: + f.write('some content') + # Check first that copy_file() will not fall back on copying the file + # instead of creating the hard link. + try: + os.link(self.source, self.target) + except OSError as e: + self.skipTest('os.link: %s' % e) + else: + unlink(self.target) + st = os.stat(self.source) + copy_file(self.source, self.target, link='hard') + st2 = os.stat(self.source) + st3 = os.stat(self.target) + self.assertTrue(os.path.samestat(st, st2), (st, st2)) + self.assertTrue(os.path.samestat(st2, st3), (st2, st3)) + with open(self.source, 'r') as f: + self.assertEqual(f.read(), 'some content') + + def test_copy_file_hard_link_failure(self): + # If hard linking fails, copy_file() falls back on copying file + # (some special filesystems don't support hard linking even under + # Unix, see issue #8876). + with open(self.source, 'w') as f: + f.write('some content') + st = os.stat(self.source) + with patch("os.link", side_effect=OSError(0, "linking unsupported")): + copy_file(self.source, self.target, link='hard') + st2 = os.stat(self.source) + st3 = os.stat(self.target) + self.assertTrue(os.path.samestat(st, st2), (st, st2)) + self.assertFalse(os.path.samestat(st2, st3), (st2, st3)) + for fn in (self.source, self.target): + with open(fn, 'r') as f: + self.assertEqual(f.read(), 'some content') + + +def test_suite(): + return unittest.makeSuite(FileUtilTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_filelist.py b/distutils/tests/test_filelist.py new file mode 100644 index 00000000..c71342d0 --- /dev/null +++ b/distutils/tests/test_filelist.py @@ -0,0 +1,340 @@ +"""Tests for distutils.filelist.""" +import os +import re +import unittest +from distutils import debug +from distutils.log import WARN +from distutils.errors import DistutilsTemplateError +from distutils.filelist import glob_to_re, translate_pattern, FileList +from distutils import filelist + +import test.support +from test.support import captured_stdout, run_unittest +from distutils.tests import support + +MANIFEST_IN = """\ +include ok +include xo +exclude xo +include foo.tmp +include buildout.cfg +global-include *.x +global-include *.txt +global-exclude *.tmp +recursive-include f *.oo +recursive-exclude global *.x +graft dir +prune dir3 +""" + + +def make_local_path(s): + """Converts '/' in a string to os.sep""" + return s.replace('/', os.sep) + + +class FileListTestCase(support.LoggingSilencer, + unittest.TestCase): + + def assertNoWarnings(self): + self.assertEqual(self.get_logs(WARN), []) + self.clear_logs() + + def assertWarnings(self): + self.assertGreater(len(self.get_logs(WARN)), 0) + self.clear_logs() + + def test_glob_to_re(self): + sep = os.sep + if os.sep == '\\': + sep = re.escape(os.sep) + + for glob, regex in ( + # simple cases + ('foo*', r'(?s:foo[^%(sep)s]*)\Z'), + ('foo?', r'(?s:foo[^%(sep)s])\Z'), + ('foo??', r'(?s:foo[^%(sep)s][^%(sep)s])\Z'), + # special cases + (r'foo\\*', r'(?s:foo\\\\[^%(sep)s]*)\Z'), + (r'foo\\\*', r'(?s:foo\\\\\\[^%(sep)s]*)\Z'), + ('foo????', r'(?s:foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s])\Z'), + (r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z')): + regex = regex % {'sep': sep} + self.assertEqual(glob_to_re(glob), regex) + + def test_process_template_line(self): + # testing all MANIFEST.in template patterns + file_list = FileList() + l = make_local_path + + # simulated file list + file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt', + 'buildout.cfg', + # filelist does not filter out VCS directories, + # it's sdist that does + l('.hg/last-message.txt'), + l('global/one.txt'), + l('global/two.txt'), + l('global/files.x'), + l('global/here.tmp'), + l('f/o/f.oo'), + l('dir/graft-one'), + l('dir/dir2/graft2'), + l('dir3/ok'), + l('dir3/sub/ok.txt'), + ] + + for line in MANIFEST_IN.split('\n'): + if line.strip() == '': + continue + file_list.process_template_line(line) + + wanted = ['ok', + 'buildout.cfg', + 'four.txt', + l('.hg/last-message.txt'), + l('global/one.txt'), + l('global/two.txt'), + l('f/o/f.oo'), + l('dir/graft-one'), + l('dir/dir2/graft2'), + ] + + self.assertEqual(file_list.files, wanted) + + def test_debug_print(self): + file_list = FileList() + with captured_stdout() as stdout: + file_list.debug_print('xxx') + self.assertEqual(stdout.getvalue(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + file_list.debug_print('xxx') + self.assertEqual(stdout.getvalue(), 'xxx\n') + finally: + debug.DEBUG = False + + def test_set_allfiles(self): + file_list = FileList() + files = ['a', 'b', 'c'] + file_list.set_allfiles(files) + self.assertEqual(file_list.allfiles, files) + + def test_remove_duplicates(self): + file_list = FileList() + file_list.files = ['a', 'b', 'a', 'g', 'c', 'g'] + # files must be sorted beforehand (sdist does it) + file_list.sort() + file_list.remove_duplicates() + self.assertEqual(file_list.files, ['a', 'b', 'c', 'g']) + + def test_translate_pattern(self): + # not regex + self.assertTrue(hasattr( + translate_pattern('a', anchor=True, is_regex=False), + 'search')) + + # is a regex + regex = re.compile('a') + self.assertEqual( + translate_pattern(regex, anchor=True, is_regex=True), + regex) + + # plain string flagged as regex + self.assertTrue(hasattr( + translate_pattern('a', anchor=True, is_regex=True), + 'search')) + + # glob support + self.assertTrue(translate_pattern( + '*.py', anchor=True, is_regex=False).search('filelist.py')) + + def test_exclude_pattern(self): + # return False if no match + file_list = FileList() + self.assertFalse(file_list.exclude_pattern('*.py')) + + # return True if files match + file_list = FileList() + file_list.files = ['a.py', 'b.py'] + self.assertTrue(file_list.exclude_pattern('*.py')) + + # test excludes + file_list = FileList() + file_list.files = ['a.py', 'a.txt'] + file_list.exclude_pattern('*.py') + self.assertEqual(file_list.files, ['a.txt']) + + def test_include_pattern(self): + # return False if no match + file_list = FileList() + file_list.set_allfiles([]) + self.assertFalse(file_list.include_pattern('*.py')) + + # return True if files match + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt']) + self.assertTrue(file_list.include_pattern('*.py')) + + # test * matches all files + file_list = FileList() + self.assertIsNone(file_list.allfiles) + file_list.set_allfiles(['a.py', 'b.txt']) + file_list.include_pattern('*') + self.assertEqual(file_list.allfiles, ['a.py', 'b.txt']) + + def test_process_template(self): + l = make_local_path + # invalid lines + file_list = FileList() + for action in ('include', 'exclude', 'global-include', + 'global-exclude', 'recursive-include', + 'recursive-exclude', 'graft', 'prune', 'blarg'): + self.assertRaises(DistutilsTemplateError, + file_list.process_template_line, action) + + # include + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) + + file_list.process_template_line('include *.py') + self.assertEqual(file_list.files, ['a.py']) + self.assertNoWarnings() + + file_list.process_template_line('include *.rb') + self.assertEqual(file_list.files, ['a.py']) + self.assertWarnings() + + # exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', l('d/c.py')] + + file_list.process_template_line('exclude *.py') + self.assertEqual(file_list.files, ['b.txt', l('d/c.py')]) + self.assertNoWarnings() + + file_list.process_template_line('exclude *.rb') + self.assertEqual(file_list.files, ['b.txt', l('d/c.py')]) + self.assertWarnings() + + # global-include + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) + + file_list.process_template_line('global-include *.py') + self.assertEqual(file_list.files, ['a.py', l('d/c.py')]) + self.assertNoWarnings() + + file_list.process_template_line('global-include *.rb') + self.assertEqual(file_list.files, ['a.py', l('d/c.py')]) + self.assertWarnings() + + # global-exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', l('d/c.py')] + + file_list.process_template_line('global-exclude *.py') + self.assertEqual(file_list.files, ['b.txt']) + self.assertNoWarnings() + + file_list.process_template_line('global-exclude *.rb') + self.assertEqual(file_list.files, ['b.txt']) + self.assertWarnings() + + # recursive-include + file_list = FileList() + file_list.set_allfiles(['a.py', l('d/b.py'), l('d/c.txt'), + l('d/d/e.py')]) + + file_list.process_template_line('recursive-include d *.py') + self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) + self.assertNoWarnings() + + file_list.process_template_line('recursive-include e *.py') + self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) + self.assertWarnings() + + # recursive-exclude + file_list = FileList() + file_list.files = ['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')] + + file_list.process_template_line('recursive-exclude d *.py') + self.assertEqual(file_list.files, ['a.py', l('d/c.txt')]) + self.assertNoWarnings() + + file_list.process_template_line('recursive-exclude e *.py') + self.assertEqual(file_list.files, ['a.py', l('d/c.txt')]) + self.assertWarnings() + + # graft + file_list = FileList() + file_list.set_allfiles(['a.py', l('d/b.py'), l('d/d/e.py'), + l('f/f.py')]) + + file_list.process_template_line('graft d') + self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) + self.assertNoWarnings() + + file_list.process_template_line('graft e') + self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) + self.assertWarnings() + + # prune + file_list = FileList() + file_list.files = ['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')] + + file_list.process_template_line('prune d') + self.assertEqual(file_list.files, ['a.py', l('f/f.py')]) + self.assertNoWarnings() + + file_list.process_template_line('prune e') + self.assertEqual(file_list.files, ['a.py', l('f/f.py')]) + self.assertWarnings() + + +class FindAllTestCase(unittest.TestCase): + @test.support.skip_unless_symlink + def test_missing_symlink(self): + with test.support.temp_cwd(): + os.symlink('foo', 'bar') + self.assertEqual(filelist.findall(), []) + + def test_basic_discovery(self): + """ + When findall is called with no parameters or with + '.' as the parameter, the dot should be omitted from + the results. + """ + with test.support.temp_cwd(): + os.mkdir('foo') + file1 = os.path.join('foo', 'file1.txt') + test.support.create_empty_file(file1) + os.mkdir('bar') + file2 = os.path.join('bar', 'file2.txt') + test.support.create_empty_file(file2) + expected = [file2, file1] + self.assertEqual(sorted(filelist.findall()), expected) + + def test_non_local_discovery(self): + """ + When findall is called with another path, the full + path name should be returned. + """ + with test.support.temp_dir() as temp_dir: + file1 = os.path.join(temp_dir, 'file1.txt') + test.support.create_empty_file(file1) + expected = [file1] + self.assertEqual(filelist.findall(temp_dir), expected) + + +def test_suite(): + return unittest.TestSuite([ + unittest.makeSuite(FileListTestCase), + unittest.makeSuite(FindAllTestCase), + ]) + + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_install.py b/distutils/tests/test_install.py new file mode 100644 index 00000000..51c80e04 --- /dev/null +++ b/distutils/tests/test_install.py @@ -0,0 +1,249 @@ +"""Tests for distutils.command.install.""" + +import os +import sys +import unittest +import site + +from test.support import captured_stdout, run_unittest + +from distutils import sysconfig +from distutils.command.install import install +from distutils.command import install as install_module +from distutils.command.build_ext import build_ext +from distutils.command.install import INSTALL_SCHEMES +from distutils.core import Distribution +from distutils.errors import DistutilsOptionError +from distutils.extension import Extension + +from distutils.tests import support +from test import support as test_support + + +def _make_ext_name(modname): + return modname + sysconfig.get_config_var('EXT_SUFFIX') + + +class InstallTestCase(support.TempdirManager, + support.EnvironGuard, + support.LoggingSilencer, + unittest.TestCase): + + def test_home_installation_scheme(self): + # This ensure two things: + # - that --home generates the desired set of directory names + # - test --home is supported on all platforms + builddir = self.mkdtemp() + destination = os.path.join(builddir, "installation") + + dist = Distribution({"name": "foopkg"}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(builddir, "setup.py") + dist.command_obj["build"] = support.DummyCommand( + build_base=builddir, + build_lib=os.path.join(builddir, "lib"), + ) + + cmd = install(dist) + cmd.home = destination + cmd.ensure_finalized() + + self.assertEqual(cmd.install_base, destination) + self.assertEqual(cmd.install_platbase, destination) + + def check_path(got, expected): + got = os.path.normpath(got) + expected = os.path.normpath(expected) + self.assertEqual(got, expected) + + libdir = os.path.join(destination, "lib", "python") + check_path(cmd.install_lib, libdir) + platlibdir = os.path.join(destination, sys.platlibdir, "python") + check_path(cmd.install_platlib, platlibdir) + check_path(cmd.install_purelib, libdir) + check_path(cmd.install_headers, + os.path.join(destination, "include", "python", "foopkg")) + check_path(cmd.install_scripts, os.path.join(destination, "bin")) + check_path(cmd.install_data, destination) + + def test_user_site(self): + # test install with --user + # preparing the environment for the test + self.old_user_base = site.USER_BASE + self.old_user_site = site.USER_SITE + self.tmpdir = self.mkdtemp() + self.user_base = os.path.join(self.tmpdir, 'B') + self.user_site = os.path.join(self.tmpdir, 'S') + site.USER_BASE = self.user_base + site.USER_SITE = self.user_site + install_module.USER_BASE = self.user_base + install_module.USER_SITE = self.user_site + + def _expanduser(path): + return self.tmpdir + self.old_expand = os.path.expanduser + os.path.expanduser = _expanduser + + def cleanup(): + site.USER_BASE = self.old_user_base + site.USER_SITE = self.old_user_site + install_module.USER_BASE = self.old_user_base + install_module.USER_SITE = self.old_user_site + os.path.expanduser = self.old_expand + + self.addCleanup(cleanup) + + for key in ('nt_user', 'unix_user'): + self.assertIn(key, INSTALL_SCHEMES) + + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # making sure the user option is there + options = [name for name, short, lable in + cmd.user_options] + self.assertIn('user', options) + + # setting a value + cmd.user = 1 + + # user base and site shouldn't be created yet + self.assertFalse(os.path.exists(self.user_base)) + self.assertFalse(os.path.exists(self.user_site)) + + # let's run finalize + cmd.ensure_finalized() + + # now they should + self.assertTrue(os.path.exists(self.user_base)) + self.assertTrue(os.path.exists(self.user_site)) + + self.assertIn('userbase', cmd.config_vars) + self.assertIn('usersite', cmd.config_vars) + + def test_handle_extra_path(self): + dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) + cmd = install(dist) + + # two elements + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, ['path', 'dirs']) + self.assertEqual(cmd.extra_dirs, 'dirs') + self.assertEqual(cmd.path_file, 'path') + + # one element + cmd.extra_path = ['path'] + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, ['path']) + self.assertEqual(cmd.extra_dirs, 'path') + self.assertEqual(cmd.path_file, 'path') + + # none + dist.extra_path = cmd.extra_path = None + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, None) + self.assertEqual(cmd.extra_dirs, '') + self.assertEqual(cmd.path_file, None) + + # three elements (no way !) + cmd.extra_path = 'path,dirs,again' + self.assertRaises(DistutilsOptionError, cmd.handle_extra_path) + + def test_finalize_options(self): + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # must supply either prefix/exec-prefix/home or + # install-base/install-platbase -- not both + cmd.prefix = 'prefix' + cmd.install_base = 'base' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # must supply either home or prefix/exec-prefix -- not both + cmd.install_base = None + cmd.home = 'home' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # can't combine user with prefix/exec_prefix/home or + # install_(plat)base + cmd.prefix = None + cmd.user = 'user' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + def test_record(self): + install_dir = self.mkdtemp() + project_dir, dist = self.create_dist(py_modules=['hello'], + scripts=['sayhi']) + os.chdir(project_dir) + self.write_file('hello.py', "def main(): print('o hai')") + self.write_file('sayhi', 'from hello import main; main()') + + cmd = install(dist) + dist.command_obj['install'] = cmd + cmd.root = install_dir + cmd.record = os.path.join(project_dir, 'filelist') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.record) + try: + content = f.read() + finally: + f.close() + + found = [os.path.basename(line) for line in content.splitlines()] + expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag, + 'sayhi', + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + + def test_record_extensions(self): + cmd = test_support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) + install_dir = self.mkdtemp() + project_dir, dist = self.create_dist(ext_modules=[ + Extension('xx', ['xxmodule.c'])]) + os.chdir(project_dir) + support.copy_xxmodule_c(project_dir) + + buildextcmd = build_ext(dist) + support.fixup_build_ext(buildextcmd) + buildextcmd.ensure_finalized() + + cmd = install(dist) + dist.command_obj['install'] = cmd + dist.command_obj['build_ext'] = buildextcmd + cmd.root = install_dir + cmd.record = os.path.join(project_dir, 'filelist') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.record) + try: + content = f.read() + finally: + f.close() + + found = [os.path.basename(line) for line in content.splitlines()] + expected = [_make_ext_name('xx'), + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + + def test_debug_mode(self): + # this covers the code called when DEBUG is set + old_logs_len = len(self.logs) + install_module.DEBUG = True + try: + with captured_stdout(): + self.test_record() + finally: + install_module.DEBUG = False + self.assertGreater(len(self.logs), old_logs_len) + + +def test_suite(): + return unittest.makeSuite(InstallTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_install_data.py b/distutils/tests/test_install_data.py new file mode 100644 index 00000000..32ab296a --- /dev/null +++ b/distutils/tests/test_install_data.py @@ -0,0 +1,75 @@ +"""Tests for distutils.command.install_data.""" +import os +import unittest + +from distutils.command.install_data import install_data +from distutils.tests import support +from test.support import run_unittest + +class InstallDataTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + def test_simple_run(self): + pkg_dir, dist = self.create_dist() + cmd = install_data(dist) + cmd.install_dir = inst = os.path.join(pkg_dir, 'inst') + + # data_files can contain + # - simple files + # - a tuple with a path, and a list of file + one = os.path.join(pkg_dir, 'one') + self.write_file(one, 'xxx') + inst2 = os.path.join(pkg_dir, 'inst2') + two = os.path.join(pkg_dir, 'two') + self.write_file(two, 'xxx') + + cmd.data_files = [one, (inst2, [two])] + self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])]) + + # let's run the command + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEqual(len(cmd.get_outputs()), 2) + rtwo = os.path.split(two)[-1] + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + rone = os.path.split(one)[-1] + self.assertTrue(os.path.exists(os.path.join(inst, rone))) + cmd.outfiles = [] + + # let's try with warn_dir one + cmd.warn_dir = 1 + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEqual(len(cmd.get_outputs()), 2) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) + cmd.outfiles = [] + + # now using root and empty dir + cmd.root = os.path.join(pkg_dir, 'root') + inst3 = os.path.join(cmd.install_dir, 'inst3') + inst4 = os.path.join(pkg_dir, 'inst4') + three = os.path.join(cmd.install_dir, 'three') + self.write_file(three, 'xx') + cmd.data_files = [one, (inst2, [two]), + ('inst3', [three]), + (inst4, [])] + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEqual(len(cmd.get_outputs()), 4) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) + +def test_suite(): + return unittest.makeSuite(InstallDataTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_install_headers.py b/distutils/tests/test_install_headers.py new file mode 100644 index 00000000..2217b321 --- /dev/null +++ b/distutils/tests/test_install_headers.py @@ -0,0 +1,39 @@ +"""Tests for distutils.command.install_headers.""" +import os +import unittest + +from distutils.command.install_headers import install_headers +from distutils.tests import support +from test.support import run_unittest + +class InstallHeadersTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + def test_simple_run(self): + # we have two headers + header_list = self.mkdtemp() + header1 = os.path.join(header_list, 'header1') + header2 = os.path.join(header_list, 'header2') + self.write_file(header1) + self.write_file(header2) + headers = [header1, header2] + + pkg_dir, dist = self.create_dist(headers=headers) + cmd = install_headers(dist) + self.assertEqual(cmd.get_inputs(), headers) + + # let's run the command + cmd.install_dir = os.path.join(pkg_dir, 'inst') + cmd.ensure_finalized() + cmd.run() + + # let's check the results + self.assertEqual(len(cmd.get_outputs()), 2) + +def test_suite(): + return unittest.makeSuite(InstallHeadersTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_install_lib.py b/distutils/tests/test_install_lib.py new file mode 100644 index 00000000..fda6315b --- /dev/null +++ b/distutils/tests/test_install_lib.py @@ -0,0 +1,115 @@ +"""Tests for distutils.command.install_data.""" +import sys +import os +import importlib.util +import unittest + +from distutils.command.install_lib import install_lib +from distutils.extension import Extension +from distutils.tests import support +from distutils.errors import DistutilsOptionError +from test.support import run_unittest + + +class InstallLibTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + def test_finalize_options(self): + dist = self.create_dist()[1] + cmd = install_lib(dist) + + cmd.finalize_options() + self.assertEqual(cmd.compile, 1) + self.assertEqual(cmd.optimize, 0) + + # optimize must be 0, 1, or 2 + cmd.optimize = 'foo' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + cmd.optimize = '4' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + cmd.optimize = '2' + cmd.finalize_options() + self.assertEqual(cmd.optimize, 2) + + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') + def test_byte_compile(self): + project_dir, dist = self.create_dist() + os.chdir(project_dir) + cmd = install_lib(dist) + cmd.compile = cmd.optimize = 1 + + f = os.path.join(project_dir, 'foo.py') + self.write_file(f, '# python file') + cmd.byte_compile([f]) + pyc_file = importlib.util.cache_from_source('foo.py', optimization='') + pyc_opt_file = importlib.util.cache_from_source('foo.py', + optimization=cmd.optimize) + self.assertTrue(os.path.exists(pyc_file)) + self.assertTrue(os.path.exists(pyc_opt_file)) + + def test_get_outputs(self): + project_dir, dist = self.create_dist() + os.chdir(project_dir) + os.mkdir('spam') + cmd = install_lib(dist) + + # setting up a dist environment + cmd.compile = cmd.optimize = 1 + cmd.install_dir = self.mkdtemp() + f = os.path.join(project_dir, 'spam', '__init__.py') + self.write_file(f, '# python package') + cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] + cmd.distribution.packages = ['spam'] + cmd.distribution.script_name = 'setup.py' + + # get_outputs should return 4 elements: spam/__init__.py and .pyc, + # foo.import-tag-abiflags.so / foo.pyd + outputs = cmd.get_outputs() + self.assertEqual(len(outputs), 4, outputs) + + def test_get_inputs(self): + project_dir, dist = self.create_dist() + os.chdir(project_dir) + os.mkdir('spam') + cmd = install_lib(dist) + + # setting up a dist environment + cmd.compile = cmd.optimize = 1 + cmd.install_dir = self.mkdtemp() + f = os.path.join(project_dir, 'spam', '__init__.py') + self.write_file(f, '# python package') + cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] + cmd.distribution.packages = ['spam'] + cmd.distribution.script_name = 'setup.py' + + # get_inputs should return 2 elements: spam/__init__.py and + # foo.import-tag-abiflags.so / foo.pyd + inputs = cmd.get_inputs() + self.assertEqual(len(inputs), 2, inputs) + + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + dist = self.create_dist()[1] + cmd = install_lib(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertIn('byte-compiling is disabled', + self.logs[0][1] % self.logs[0][2]) + + +def test_suite(): + return unittest.makeSuite(InstallLibTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_install_scripts.py b/distutils/tests/test_install_scripts.py new file mode 100644 index 00000000..1f7b1038 --- /dev/null +++ b/distutils/tests/test_install_scripts.py @@ -0,0 +1,82 @@ +"""Tests for distutils.command.install_scripts.""" + +import os +import unittest + +from distutils.command.install_scripts import install_scripts +from distutils.core import Distribution + +from distutils.tests import support +from test.support import run_unittest + + +class InstallScriptsTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_default_settings(self): + dist = Distribution() + dist.command_obj["build"] = support.DummyCommand( + build_scripts="/foo/bar") + dist.command_obj["install"] = support.DummyCommand( + install_scripts="/splat/funk", + force=1, + skip_build=1, + ) + cmd = install_scripts(dist) + self.assertFalse(cmd.force) + self.assertFalse(cmd.skip_build) + self.assertIsNone(cmd.build_dir) + self.assertIsNone(cmd.install_dir) + + cmd.finalize_options() + + self.assertTrue(cmd.force) + self.assertTrue(cmd.skip_build) + self.assertEqual(cmd.build_dir, "/foo/bar") + self.assertEqual(cmd.install_dir, "/splat/funk") + + def test_installation(self): + source = self.mkdtemp() + expected = [] + + def write_script(name, text): + expected.append(name) + f = open(os.path.join(source, name), "w") + try: + f.write(text) + finally: + f.close() + + write_script("script1.py", ("#! /usr/bin/env python2.3\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + write_script("script2.py", ("#!/usr/bin/python\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + write_script("shell.sh", ("#!/bin/sh\n" + "# bogus shell script w/ sh-bang\n" + "exit 0\n")) + + target = self.mkdtemp() + dist = Distribution() + dist.command_obj["build"] = support.DummyCommand(build_scripts=source) + dist.command_obj["install"] = support.DummyCommand( + install_scripts=target, + force=1, + skip_build=1, + ) + cmd = install_scripts(dist) + cmd.finalize_options() + cmd.run() + + installed = os.listdir(target) + for name in expected: + self.assertIn(name, installed) + + +def test_suite(): + return unittest.makeSuite(InstallScriptsTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_log.py b/distutils/tests/test_log.py new file mode 100644 index 00000000..75cf9006 --- /dev/null +++ b/distutils/tests/test_log.py @@ -0,0 +1,46 @@ +"""Tests for distutils.log""" + +import io +import sys +import unittest +from test.support import swap_attr, run_unittest + +from distutils import log + +class TestLog(unittest.TestCase): + def test_non_ascii(self): + # Issues #8663, #34421: test that non-encodable text is escaped with + # backslashreplace error handler and encodable non-ASCII text is + # output as is. + for errors in ('strict', 'backslashreplace', 'surrogateescape', + 'replace', 'ignore'): + with self.subTest(errors=errors): + stdout = io.TextIOWrapper(io.BytesIO(), + encoding='cp437', errors=errors) + stderr = io.TextIOWrapper(io.BytesIO(), + encoding='cp437', errors=errors) + old_threshold = log.set_threshold(log.DEBUG) + try: + with swap_attr(sys, 'stdout', stdout), \ + swap_attr(sys, 'stderr', stderr): + log.debug('Dεbug\tMÄ—ssãge') + log.fatal('Fαtal\tÈrrÅr') + finally: + log.set_threshold(old_threshold) + + stdout.seek(0) + self.assertEqual(stdout.read().rstrip(), + 'Dεbug\tM?ss?ge' if errors == 'replace' else + 'Dεbug\tMssge' if errors == 'ignore' else + 'Dεbug\tM\\u0117ss\\xe3ge') + stderr.seek(0) + self.assertEqual(stderr.read().rstrip(), + 'Fαtal\t?rr?r' if errors == 'replace' else + 'Fαtal\trrr' if errors == 'ignore' else + 'Fαtal\t\\xc8rr\\u014dr') + +def test_suite(): + return unittest.makeSuite(TestLog) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_msvc9compiler.py b/distutils/tests/test_msvc9compiler.py new file mode 100644 index 00000000..77a07ef3 --- /dev/null +++ b/distutils/tests/test_msvc9compiler.py @@ -0,0 +1,184 @@ +"""Tests for distutils.msvc9compiler.""" +import sys +import unittest +import os + +from distutils.errors import DistutilsPlatformError +from distutils.tests import support +from test.support import run_unittest + +# A manifest with the only assembly reference being the msvcrt assembly, so +# should have the assembly completely stripped. Note that although the +# assembly has a reference the assembly is removed - that is +# currently a "feature", not a bug :) +_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\ + + + + + + + + + + + + + + + + + +""" + +# A manifest with references to assemblies other than msvcrt. When processed, +# this assembly should be returned with just the msvcrt part removed. +_MANIFEST_WITH_MULTIPLE_REFERENCES = """\ + + + + + + + + + + + + + + + + + + + + + + +""" + +_CLEANED_MANIFEST = """\ + + + + + + + + + + + + + + + + + + +""" + +if sys.platform=="win32": + from distutils.msvccompiler import get_build_version + if get_build_version()>=8.0: + SKIP_MESSAGE = None + else: + SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" +else: + SKIP_MESSAGE = "These tests are only for win32" + +@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) +class msvc9compilerTestCase(support.TempdirManager, + unittest.TestCase): + + def test_no_compiler(self): + # makes sure query_vcvarsall raises + # a DistutilsPlatformError if the compiler + # is not found + from distutils.msvc9compiler import query_vcvarsall + def _find_vcvarsall(version): + return None + + from distutils import msvc9compiler + old_find_vcvarsall = msvc9compiler.find_vcvarsall + msvc9compiler.find_vcvarsall = _find_vcvarsall + try: + self.assertRaises(DistutilsPlatformError, query_vcvarsall, + 'wont find this version') + finally: + msvc9compiler.find_vcvarsall = old_find_vcvarsall + + def test_reg_class(self): + from distutils.msvc9compiler import Reg + self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') + + # looking for values that should exist on all + # windows registry versions. + path = r'Control Panel\Desktop' + v = Reg.get_value(path, 'dragfullwindows') + self.assertIn(v, ('0', '1', '2')) + + import winreg + HKCU = winreg.HKEY_CURRENT_USER + keys = Reg.read_keys(HKCU, 'xxxx') + self.assertEqual(keys, None) + + keys = Reg.read_keys(HKCU, r'Control Panel') + self.assertIn('Desktop', keys) + + def test_remove_visual_c_ref(self): + from distutils.msvc9compiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + try: + f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES) + finally: + f.close() + + compiler = MSVCCompiler() + compiler._remove_visual_c_ref(manifest) + + # see what we got + f = open(manifest) + try: + # removing trailing spaces + content = '\n'.join([line.rstrip() for line in f.readlines()]) + finally: + f.close() + + # makes sure the manifest was properly cleaned + self.assertEqual(content, _CLEANED_MANIFEST) + + def test_remove_entire_manifest(self): + from distutils.msvc9compiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + try: + f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE) + finally: + f.close() + + compiler = MSVCCompiler() + got = compiler._remove_visual_c_ref(manifest) + self.assertIsNone(got) + + +def test_suite(): + return unittest.makeSuite(msvc9compilerTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_msvccompiler.py b/distutils/tests/test_msvccompiler.py new file mode 100644 index 00000000..b518d6a7 --- /dev/null +++ b/distutils/tests/test_msvccompiler.py @@ -0,0 +1,81 @@ +"""Tests for distutils._msvccompiler.""" +import sys +import unittest +import os + +from distutils.errors import DistutilsPlatformError +from distutils.tests import support +from test.support import run_unittest + + +SKIP_MESSAGE = (None if sys.platform == "win32" else + "These tests are only for win32") + +@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) +class msvccompilerTestCase(support.TempdirManager, + unittest.TestCase): + + def test_no_compiler(self): + import distutils._msvccompiler as _msvccompiler + # makes sure query_vcvarsall raises + # a DistutilsPlatformError if the compiler + # is not found + def _find_vcvarsall(plat_spec): + return None, None + + old_find_vcvarsall = _msvccompiler._find_vcvarsall + _msvccompiler._find_vcvarsall = _find_vcvarsall + try: + self.assertRaises(DistutilsPlatformError, + _msvccompiler._get_vc_env, + 'wont find this version') + finally: + _msvccompiler._find_vcvarsall = old_find_vcvarsall + + def test_get_vc_env_unicode(self): + import distutils._msvccompiler as _msvccompiler + + test_var = 'ṰḖṤṪ┅ṼẨṜ' + test_value = '₃â´â‚…' + + # Ensure we don't early exit from _get_vc_env + old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None) + os.environ[test_var] = test_value + try: + env = _msvccompiler._get_vc_env('x86') + self.assertIn(test_var.lower(), env) + self.assertEqual(test_value, env[test_var.lower()]) + finally: + os.environ.pop(test_var) + if old_distutils_use_sdk: + os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk + + def test_get_vc2017(self): + import distutils._msvccompiler as _msvccompiler + + # This function cannot be mocked, so pass it if we find VS 2017 + # and mark it skipped if we do not. + version, path = _msvccompiler._find_vc2017() + if version: + self.assertGreaterEqual(version, 15) + self.assertTrue(os.path.isdir(path)) + else: + raise unittest.SkipTest("VS 2017 is not installed") + + def test_get_vc2015(self): + import distutils._msvccompiler as _msvccompiler + + # This function cannot be mocked, so pass it if we find VS 2015 + # and mark it skipped if we do not. + version, path = _msvccompiler._find_vc2015() + if version: + self.assertGreaterEqual(version, 14) + self.assertTrue(os.path.isdir(path)) + else: + raise unittest.SkipTest("VS 2015 is not installed") + +def test_suite(): + return unittest.makeSuite(msvccompilerTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_register.py b/distutils/tests/test_register.py new file mode 100644 index 00000000..e68b0af3 --- /dev/null +++ b/distutils/tests/test_register.py @@ -0,0 +1,323 @@ +"""Tests for distutils.command.register.""" +import os +import unittest +import getpass +import urllib +import warnings + +from test.support import check_warnings, run_unittest + +from distutils.command import register as register_module +from distutils.command.register import register +from distutils.errors import DistutilsSetupError +from distutils.log import INFO + +from distutils.tests.test_config import BasePyPIRCCommandTestCase + +try: + import docutils +except ImportError: + docutils = None + +PYPIRC_NOPASSWORD = """\ +[distutils] + +index-servers = + server1 + +[server1] +username:me +""" + +WANTED_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:password +""" + +class Inputs(object): + """Fakes user inputs.""" + def __init__(self, *answers): + self.answers = answers + self.index = 0 + + def __call__(self, prompt=''): + try: + return self.answers[self.index] + finally: + self.index += 1 + +class FakeOpener(object): + """Fakes a PyPI server""" + def __init__(self): + self.reqs = [] + + def __call__(self, *args): + return self + + def open(self, req, data=None, timeout=None): + self.reqs.append(req) + return self + + def read(self): + return b'xxx' + + def getheader(self, name, default=None): + return { + 'content-type': 'text/plain; charset=utf-8', + }.get(name.lower(), default) + + +class RegisterTestCase(BasePyPIRCCommandTestCase): + + def setUp(self): + super(RegisterTestCase, self).setUp() + # patching the password prompt + self._old_getpass = getpass.getpass + def _getpass(prompt): + return 'password' + getpass.getpass = _getpass + urllib.request._opener = None + self.old_opener = urllib.request.build_opener + self.conn = urllib.request.build_opener = FakeOpener() + + def tearDown(self): + getpass.getpass = self._old_getpass + urllib.request._opener = None + urllib.request.build_opener = self.old_opener + super(RegisterTestCase, self).tearDown() + + def _get_cmd(self, metadata=None): + if metadata is None: + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx'} + pkg_info, dist = self.create_dist(**metadata) + return register(dist) + + def test_create_pypirc(self): + # this test makes sure a .pypirc file + # is created when requested. + + # let's create a register instance + cmd = self._get_cmd() + + # we shouldn't have a .pypirc file yet + self.assertFalse(os.path.exists(self.rc)) + + # patching input and getpass.getpass + # so register gets happy + # + # Here's what we are faking : + # use your existing login (choice 1.) + # Username : 'tarek' + # Password : 'password' + # Save your login (y/N)? : 'y' + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.input + + # we should have a brand new .pypirc file + self.assertTrue(os.path.exists(self.rc)) + + # with the content similar to WANTED_PYPIRC + f = open(self.rc) + try: + content = f.read() + self.assertEqual(content, WANTED_PYPIRC) + finally: + f.close() + + # now let's make sure the .pypirc file generated + # really works : we shouldn't be asked anything + # if we run the command again + def _no_way(prompt=''): + raise AssertionError(prompt) + register_module.input = _no_way + + cmd.show_response = 1 + cmd.run() + + # let's see what the server received : we should + # have 2 similar requests + self.assertEqual(len(self.conn.reqs), 2) + req1 = dict(self.conn.reqs[0].headers) + req2 = dict(self.conn.reqs[1].headers) + + self.assertEqual(req1['Content-length'], '1374') + self.assertEqual(req2['Content-length'], '1374') + self.assertIn(b'xxx', self.conn.reqs[1].data) + + def test_password_not_in_file(self): + + self.write_file(self.rc, PYPIRC_NOPASSWORD) + cmd = self._get_cmd() + cmd._set_config() + cmd.finalize_options() + cmd.send_metadata() + + # dist.password should be set + # therefore used afterwards by other commands + self.assertEqual(cmd.distribution.password, 'password') + + def test_registering(self): + # this test runs choice 2 + cmd = self._get_cmd() + inputs = Inputs('2', 'tarek', 'tarek@ziade.org') + register_module.input = inputs.__call__ + try: + # let's run the command + cmd.run() + finally: + del register_module.input + + # we should have send a request + self.assertEqual(len(self.conn.reqs), 1) + req = self.conn.reqs[0] + headers = dict(req.headers) + self.assertEqual(headers['Content-length'], '608') + self.assertIn(b'tarek', req.data) + + def test_password_reset(self): + # this test runs choice 3 + cmd = self._get_cmd() + inputs = Inputs('3', 'tarek@ziade.org') + register_module.input = inputs.__call__ + try: + # let's run the command + cmd.run() + finally: + del register_module.input + + # we should have send a request + self.assertEqual(len(self.conn.reqs), 1) + req = self.conn.reqs[0] + headers = dict(req.headers) + self.assertEqual(headers['Content-length'], '290') + self.assertIn(b'tarek', req.data) + + @unittest.skipUnless(docutils is not None, 'needs docutils') + def test_strict(self): + # testing the script option + # when on, the register command stops if + # the metadata is incomplete or if + # long_description is not reSt compliant + + # empty metadata + cmd = self._get_cmd({}) + cmd.ensure_finalized() + cmd.strict = 1 + self.assertRaises(DistutilsSetupError, cmd.run) + + # metadata are OK but long_description is broken + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'éxéxé', + 'name': 'xxx', 'version': 'xxx', + 'long_description': 'title\n==\n\ntext'} + + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + self.assertRaises(DistutilsSetupError, cmd.run) + + # now something that works + metadata['long_description'] = 'title\n=====\n\ntext' + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.input + + # strict is not by default + cmd = self._get_cmd() + cmd.ensure_finalized() + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.input + + # and finally a Unicode test (bug #12114) + metadata = {'url': 'xxx', 'author': '\u00c9ric', + 'author_email': 'xxx', 'name': 'xxx', + 'version': 'xxx', + 'description': 'Something about esszet \u00df', + 'long_description': 'More things about esszet \u00df'} + + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.input + + @unittest.skipUnless(docutils is not None, 'needs docutils') + def test_register_invalid_long_description(self): + description = ':funkie:`str`' # mimic Sphinx-specific markup + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx', + 'long_description': description} + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = True + inputs = Inputs('2', 'tarek', 'tarek@ziade.org') + register_module.input = inputs + self.addCleanup(delattr, register_module, 'input') + + self.assertRaises(DistutilsSetupError, cmd.run) + + def test_check_metadata_deprecated(self): + # makes sure make_metadata is deprecated + cmd = self._get_cmd() + with check_warnings() as w: + warnings.simplefilter("always") + cmd.check_metadata() + self.assertEqual(len(w.warnings), 1) + + def test_list_classifiers(self): + cmd = self._get_cmd() + cmd.list_classifiers = 1 + cmd.run() + results = self.get_logs(INFO) + self.assertEqual(results, ['running check', 'xxx']) + + def test_show_response(self): + # test that the --show-response option return a well formatted response + cmd = self._get_cmd() + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + cmd.show_response = 1 + try: + cmd.run() + finally: + del register_module.input + + results = self.get_logs(INFO) + self.assertEqual(results[3], 75 * '-' + '\nxxx\n' + 75 * '-') + + +def test_suite(): + return unittest.makeSuite(RegisterTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_sdist.py b/distutils/tests/test_sdist.py new file mode 100644 index 00000000..23db1269 --- /dev/null +++ b/distutils/tests/test_sdist.py @@ -0,0 +1,492 @@ +"""Tests for distutils.command.sdist.""" +import os +import tarfile +import unittest +import warnings +import zipfile +from os.path import join +from textwrap import dedent +from test.support import captured_stdout, check_warnings, run_unittest + +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + +try: + import grp + import pwd + UID_GID_SUPPORT = True +except ImportError: + UID_GID_SUPPORT = False + +from distutils.command.sdist import sdist, show_formats +from distutils.core import Distribution +from distutils.tests.test_config import BasePyPIRCCommandTestCase +from distutils.errors import DistutilsOptionError +from distutils.spawn import find_executable +from distutils.log import WARN +from distutils.filelist import FileList +from distutils.archive_util import ARCHIVE_FORMATS + +SETUP_PY = """ +from distutils.core import setup +import somecode + +setup(name='fake') +""" + +MANIFEST = """\ +# file GENERATED by distutils, do NOT edit +README +buildout.cfg +inroot.txt +setup.py +data%(sep)sdata.dt +scripts%(sep)sscript.py +some%(sep)sfile.txt +some%(sep)sother_file.txt +somecode%(sep)s__init__.py +somecode%(sep)sdoc.dat +somecode%(sep)sdoc.txt +""" + +class SDistTestCase(BasePyPIRCCommandTestCase): + + def setUp(self): + # PyPIRCCommandTestCase creates a temp dir already + # and put it in self.tmp_dir + super(SDistTestCase, self).setUp() + # setting up an environment + self.old_path = os.getcwd() + os.mkdir(join(self.tmp_dir, 'somecode')) + os.mkdir(join(self.tmp_dir, 'dist')) + # a package, and a README + self.write_file((self.tmp_dir, 'README'), 'xxx') + self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#') + self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY) + os.chdir(self.tmp_dir) + + def tearDown(self): + # back to normal + os.chdir(self.old_path) + super(SDistTestCase, self).tearDown() + + def get_cmd(self, metadata=None): + """Returns a cmd""" + if metadata is None: + metadata = {'name': 'fake', 'version': '1.0', + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'} + dist = Distribution(metadata) + dist.script_name = 'setup.py' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.dist_dir = 'dist' + return dist, cmd + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_prune_file_list(self): + # this test creates a project with some VCS dirs and an NFS rename + # file, then launches sdist to check they get pruned on all systems + + # creating VCS directories with some files in them + os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) + self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') + + os.mkdir(join(self.tmp_dir, 'somecode', '.hg')) + self.write_file((self.tmp_dir, 'somecode', '.hg', + 'ok'), 'xxx') + + os.mkdir(join(self.tmp_dir, 'somecode', '.git')) + self.write_file((self.tmp_dir, 'somecode', '.git', + 'ok'), 'xxx') + + self.write_file((self.tmp_dir, 'somecode', '.nfs0001'), 'xxx') + + # now building a sdist + dist, cmd = self.get_cmd() + + # zip is available universally + # (tar might not be installed under win32) + cmd.formats = ['zip'] + + cmd.ensure_finalized() + cmd.run() + + # now let's check what we have + dist_folder = join(self.tmp_dir, 'dist') + files = os.listdir(dist_folder) + self.assertEqual(files, ['fake-1.0.zip']) + + zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) + try: + content = zip_file.namelist() + finally: + zip_file.close() + + # making sure everything has been pruned correctly + expected = ['', 'PKG-INFO', 'README', 'setup.py', + 'somecode/', 'somecode/__init__.py'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + @unittest.skipIf(find_executable('tar') is None, + "The tar command is not found") + @unittest.skipIf(find_executable('gzip') is None, + "The gzip command is not found") + def test_make_distribution(self): + # now building a sdist + dist, cmd = self.get_cmd() + + # creating a gztar then a tar + cmd.formats = ['gztar', 'tar'] + cmd.ensure_finalized() + cmd.run() + + # making sure we have two files + dist_folder = join(self.tmp_dir, 'dist') + result = os.listdir(dist_folder) + result.sort() + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + + os.remove(join(dist_folder, 'fake-1.0.tar')) + os.remove(join(dist_folder, 'fake-1.0.tar.gz')) + + # now trying a tar then a gztar + cmd.formats = ['tar', 'gztar'] + + cmd.ensure_finalized() + cmd.run() + + result = os.listdir(dist_folder) + result.sort() + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_add_defaults(self): + + # http://bugs.python.org/issue2279 + + # add_default should also include + # data_files and package_data + dist, cmd = self.get_cmd() + + # filling data_files by pointing files + # in package_data + dist.package_data = {'': ['*.cfg', '*.dat'], + 'somecode': ['*.txt']} + self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#') + + # adding some data in data_files + data_dir = join(self.tmp_dir, 'data') + os.mkdir(data_dir) + self.write_file((data_dir, 'data.dt'), '#') + some_dir = join(self.tmp_dir, 'some') + os.mkdir(some_dir) + # make sure VCS directories are pruned (#14004) + hg_dir = join(self.tmp_dir, '.hg') + os.mkdir(hg_dir) + self.write_file((hg_dir, 'last-message.txt'), '#') + # a buggy regex used to prevent this from working on windows (#6884) + self.write_file((self.tmp_dir, 'buildout.cfg'), '#') + self.write_file((self.tmp_dir, 'inroot.txt'), '#') + self.write_file((some_dir, 'file.txt'), '#') + self.write_file((some_dir, 'other_file.txt'), '#') + + dist.data_files = [('data', ['data/data.dt', + 'buildout.cfg', + 'inroot.txt', + 'notexisting']), + 'some/file.txt', + 'some/other_file.txt'] + + # adding a script + script_dir = join(self.tmp_dir, 'scripts') + os.mkdir(script_dir) + self.write_file((script_dir, 'script.py'), '#') + dist.scripts = [join('scripts', 'script.py')] + + cmd.formats = ['zip'] + cmd.use_defaults = True + + cmd.ensure_finalized() + cmd.run() + + # now let's check what we have + dist_folder = join(self.tmp_dir, 'dist') + files = os.listdir(dist_folder) + self.assertEqual(files, ['fake-1.0.zip']) + + zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) + try: + content = zip_file.namelist() + finally: + zip_file.close() + + # making sure everything was added + expected = ['', 'PKG-INFO', 'README', 'buildout.cfg', + 'data/', 'data/data.dt', 'inroot.txt', + 'scripts/', 'scripts/script.py', 'setup.py', + 'some/', 'some/file.txt', 'some/other_file.txt', + 'somecode/', 'somecode/__init__.py', 'somecode/doc.dat', + 'somecode/doc.txt'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) + + # checking the MANIFEST + f = open(join(self.tmp_dir, 'MANIFEST')) + try: + manifest = f.read() + finally: + f.close() + self.assertEqual(manifest, MANIFEST % {'sep': os.sep}) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_metadata_check_option(self): + # testing the `medata-check` option + dist, cmd = self.get_cmd(metadata={}) + + # this should raise some warnings ! + # with the `check` subcommand + cmd.ensure_finalized() + cmd.run() + warnings = [msg for msg in self.get_logs(WARN) if + msg.startswith('warning: check:')] + self.assertEqual(len(warnings), 2) + + # trying with a complete set of metadata + self.clear_logs() + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + cmd.metadata_check = 0 + cmd.run() + warnings = [msg for msg in self.get_logs(WARN) if + msg.startswith('warning: check:')] + self.assertEqual(len(warnings), 0) + + def test_check_metadata_deprecated(self): + # makes sure make_metadata is deprecated + dist, cmd = self.get_cmd() + with check_warnings() as w: + warnings.simplefilter("always") + cmd.check_metadata() + self.assertEqual(len(w.warnings), 1) + + def test_show_formats(self): + with captured_stdout() as stdout: + show_formats() + + # the output should be a header line + one line per format + num_formats = len(ARCHIVE_FORMATS.keys()) + output = [line for line in stdout.getvalue().split('\n') + if line.strip().startswith('--formats=')] + self.assertEqual(len(output), num_formats) + + def test_finalize_options(self): + dist, cmd = self.get_cmd() + cmd.finalize_options() + + # default options set by finalize + self.assertEqual(cmd.manifest, 'MANIFEST') + self.assertEqual(cmd.template, 'MANIFEST.in') + self.assertEqual(cmd.dist_dir, 'dist') + + # formats has to be a string splitable on (' ', ',') or + # a stringlist + cmd.formats = 1 + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + cmd.formats = ['zip'] + cmd.finalize_options() + + # formats has to be known + cmd.formats = 'supazipa' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # the following tests make sure there is a nice error message instead + # of a traceback when parsing an invalid manifest template + + def _check_template(self, content): + dist, cmd = self.get_cmd() + os.chdir(self.tmp_dir) + self.write_file('MANIFEST.in', content) + cmd.ensure_finalized() + cmd.filelist = FileList() + cmd.read_template() + warnings = self.get_logs(WARN) + self.assertEqual(len(warnings), 1) + + def test_invalid_template_unknown_command(self): + self._check_template('taunt knights *') + + def test_invalid_template_wrong_arguments(self): + # this manifest command takes one argument + self._check_template('prune') + + @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only') + def test_invalid_template_wrong_path(self): + # on Windows, trailing slashes are not allowed + # this used to crash instead of raising a warning: #8286 + self._check_template('include examples/') + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_get_file_list(self): + # make sure MANIFEST is recalculated + dist, cmd = self.get_cmd() + + # filling data_files by pointing files in package_data + dist.package_data = {'somecode': ['*.txt']} + self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + cmd.formats = ['gztar'] + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(len(manifest), 5) + + # adding a file + self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') + + # make sure build_py is reinitialized, like a fresh run + build_py = dist.get_command_obj('build_py') + build_py.finalized = False + build_py.ensure_finalized() + + cmd.run() + + f = open(cmd.manifest) + try: + manifest2 = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + # do we have the new file in MANIFEST ? + self.assertEqual(len(manifest2), 6) + self.assertIn('doc2.txt', manifest2[-1]) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_manifest_marker(self): + # check that autogenerated MANIFESTs have a marker + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest[0], + '# file GENERATED by distutils, do NOT edit') + + @unittest.skipUnless(ZLIB_SUPPORT, "Need zlib support to run") + def test_manifest_comments(self): + # make sure comments don't cause exceptions or wrong includes + contents = dedent("""\ + # bad.py + #bad.py + good.py + """) + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), contents) + self.write_file((self.tmp_dir, 'good.py'), '# pick me!') + self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!") + self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!") + cmd.run() + self.assertEqual(cmd.filelist.files, ['good.py']) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_manual_manifest(self): + # check that a MANIFEST without a marker is left alone + dist, cmd = self.get_cmd() + cmd.formats = ['gztar'] + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') + self.write_file((self.tmp_dir, 'README.manual'), + 'This project maintains its MANIFEST file itself.') + cmd.run() + self.assertEqual(cmd.filelist.files, ['README.manual']) + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest, ['README.manual']) + + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + filenames = [tarinfo.name for tarinfo in archive] + finally: + archive.close() + self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO', + 'fake-1.0/README.manual']) + + @unittest.skipUnless(ZLIB_SUPPORT, "requires zlib") + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + @unittest.skipIf(find_executable('tar') is None, + "The tar command is not found") + @unittest.skipIf(find_executable('gzip') is None, + "The gzip command is not found") + def test_make_distribution_owner_group(self): + # now building a sdist + dist, cmd = self.get_cmd() + + # creating a gztar and specifying the owner+group + cmd.formats = ['gztar'] + cmd.owner = pwd.getpwuid(0)[0] + cmd.group = grp.getgrgid(0)[0] + cmd.ensure_finalized() + cmd.run() + + # making sure we have the good rights + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEqual(member.uid, 0) + self.assertEqual(member.gid, 0) + finally: + archive.close() + + # building a sdist again + dist, cmd = self.get_cmd() + + # creating a gztar + cmd.formats = ['gztar'] + cmd.ensure_finalized() + cmd.run() + + # making sure we have the good rights + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + + # note that we are not testing the group ownership here + # because, depending on the platforms and the container + # rights (see #7408) + try: + for member in archive.getmembers(): + self.assertEqual(member.uid, os.getuid()) + finally: + archive.close() + +def test_suite(): + return unittest.makeSuite(SDistTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_spawn.py b/distutils/tests/test_spawn.py new file mode 100644 index 00000000..cf1faad5 --- /dev/null +++ b/distutils/tests/test_spawn.py @@ -0,0 +1,132 @@ +"""Tests for distutils.spawn.""" +import os +import stat +import sys +import unittest.mock +from test.support import run_unittest, unix_shell +from test import support as test_support + +from distutils.spawn import find_executable +from distutils.spawn import spawn +from distutils.errors import DistutilsExecError +from distutils.tests import support + +class SpawnTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + @unittest.skipUnless(os.name in ('nt', 'posix'), + 'Runs only under posix or nt') + def test_spawn(self): + tmpdir = self.mkdtemp() + + # creating something executable + # through the shell that returns 1 + if sys.platform != 'win32': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!%s\nexit 1' % unix_shell) + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 1') + + os.chmod(exe, 0o777) + self.assertRaises(DistutilsExecError, spawn, [exe]) + + # now something that works + if sys.platform != 'win32': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!%s\nexit 0' % unix_shell) + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 0') + + os.chmod(exe, 0o777) + spawn([exe]) # should work without any error + + def test_find_executable(self): + with test_support.temp_dir() as tmp_dir: + # use TESTFN to get a pseudo-unique filename + program_noeext = test_support.TESTFN + # Give the temporary program an ".exe" suffix for all. + # It's needed on Windows and not harmful on other platforms. + program = program_noeext + ".exe" + + filename = os.path.join(tmp_dir, program) + with open(filename, "wb"): + pass + os.chmod(filename, stat.S_IXUSR) + + # test path parameter + rv = find_executable(program, path=tmp_dir) + self.assertEqual(rv, filename) + + if sys.platform == 'win32': + # test without ".exe" extension + rv = find_executable(program_noeext, path=tmp_dir) + self.assertEqual(rv, filename) + + # test find in the current directory + with test_support.change_cwd(tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, program) + + # test non-existent program + dont_exist_program = "dontexist_" + program + rv = find_executable(dont_exist_program , path=tmp_dir) + self.assertIsNone(rv) + + # PATH='': no match, except in the current directory + with test_support.EnvironmentVarGuard() as env: + env['PATH'] = '' + with unittest.mock.patch('distutils.spawn.os.confstr', + return_value=tmp_dir, create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', + tmp_dir): + rv = find_executable(program) + self.assertIsNone(rv) + + # look in current directory + with test_support.change_cwd(tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, program) + + # PATH=':': explicitly looks in the current directory + with test_support.EnvironmentVarGuard() as env: + env['PATH'] = os.pathsep + with unittest.mock.patch('distutils.spawn.os.confstr', + return_value='', create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', ''): + rv = find_executable(program) + self.assertIsNone(rv) + + # look in current directory + with test_support.change_cwd(tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, program) + + # missing PATH: test os.confstr("CS_PATH") and os.defpath + with test_support.EnvironmentVarGuard() as env: + env.pop('PATH', None) + + # without confstr + with unittest.mock.patch('distutils.spawn.os.confstr', + side_effect=ValueError, + create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', + tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, filename) + + # with confstr + with unittest.mock.patch('distutils.spawn.os.confstr', + return_value=tmp_dir, create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', ''): + rv = find_executable(program) + self.assertEqual(rv, filename) + + +def test_suite(): + return unittest.makeSuite(SpawnTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_sysconfig.py b/distutils/tests/test_sysconfig.py new file mode 100644 index 00000000..236755d0 --- /dev/null +++ b/distutils/tests/test_sysconfig.py @@ -0,0 +1,274 @@ +"""Tests for distutils.sysconfig.""" +import contextlib +import os +import shutil +import subprocess +import sys +import textwrap +import unittest + +from distutils import sysconfig +from distutils.ccompiler import get_default_compiler +from distutils.tests import support +from test.support import TESTFN, run_unittest, check_warnings, swap_item + +class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): + def setUp(self): + super(SysconfigTestCase, self).setUp() + self.makefile = None + + def tearDown(self): + if self.makefile is not None: + os.unlink(self.makefile) + self.cleanup_testfn() + super(SysconfigTestCase, self).tearDown() + + def cleanup_testfn(self): + if os.path.isfile(TESTFN): + os.remove(TESTFN) + elif os.path.isdir(TESTFN): + shutil.rmtree(TESTFN) + + def test_get_config_h_filename(self): + config_h = sysconfig.get_config_h_filename() + self.assertTrue(os.path.isfile(config_h), config_h) + + def test_get_python_lib(self): + # XXX doesn't work on Linux when Python was never installed before + #self.assertTrue(os.path.isdir(lib_dir), lib_dir) + # test for pythonxx.lib? + self.assertNotEqual(sysconfig.get_python_lib(), + sysconfig.get_python_lib(prefix=TESTFN)) + + def test_get_config_vars(self): + cvars = sysconfig.get_config_vars() + self.assertIsInstance(cvars, dict) + self.assertTrue(cvars) + + def test_srcdir(self): + # See Issues #15322, #15364. + srcdir = sysconfig.get_config_var('srcdir') + + self.assertTrue(os.path.isabs(srcdir), srcdir) + self.assertTrue(os.path.isdir(srcdir), srcdir) + + if sysconfig.python_build: + # The python executable has not been installed so srcdir + # should be a full source checkout. + Python_h = os.path.join(srcdir, 'Include', 'Python.h') + self.assertTrue(os.path.exists(Python_h), Python_h) + self.assertTrue(sysconfig._is_python_source_dir(srcdir)) + elif os.name == 'posix': + self.assertEqual( + os.path.dirname(sysconfig.get_makefile_filename()), + srcdir) + + def test_srcdir_independent_of_cwd(self): + # srcdir should be independent of the current working directory + # See Issues #15322, #15364. + srcdir = sysconfig.get_config_var('srcdir') + cwd = os.getcwd() + try: + os.chdir('..') + srcdir2 = sysconfig.get_config_var('srcdir') + finally: + os.chdir(cwd) + self.assertEqual(srcdir, srcdir2) + + def customize_compiler(self): + # make sure AR gets caught + class compiler: + compiler_type = 'unix' + + def set_executables(self, **kw): + self.exes = kw + + sysconfig_vars = { + 'AR': 'sc_ar', + 'CC': 'sc_cc', + 'CXX': 'sc_cxx', + 'ARFLAGS': '--sc-arflags', + 'CFLAGS': '--sc-cflags', + 'CCSHARED': '--sc-ccshared', + 'LDSHARED': 'sc_ldshared', + 'SHLIB_SUFFIX': 'sc_shutil_suffix', + + # On macOS, disable _osx_support.customize_compiler() + 'CUSTOMIZED_OSX_COMPILER': 'True', + } + + comp = compiler() + with contextlib.ExitStack() as cm: + for key, value in sysconfig_vars.items(): + cm.enter_context(swap_item(sysconfig._config_vars, key, value)) + sysconfig.customize_compiler(comp) + + return comp + + @unittest.skipUnless(get_default_compiler() == 'unix', + 'not testing if default compiler is not unix') + def test_customize_compiler(self): + # Make sure that sysconfig._config_vars is initialized + sysconfig.get_config_vars() + + os.environ['AR'] = 'env_ar' + os.environ['CC'] = 'env_cc' + os.environ['CPP'] = 'env_cpp' + os.environ['CXX'] = 'env_cxx --env-cxx-flags' + os.environ['LDSHARED'] = 'env_ldshared' + os.environ['LDFLAGS'] = '--env-ldflags' + os.environ['ARFLAGS'] = '--env-arflags' + os.environ['CFLAGS'] = '--env-cflags' + os.environ['CPPFLAGS'] = '--env-cppflags' + + comp = self.customize_compiler() + self.assertEqual(comp.exes['archiver'], + 'env_ar --env-arflags') + self.assertEqual(comp.exes['preprocessor'], + 'env_cpp --env-cppflags') + self.assertEqual(comp.exes['compiler'], + 'env_cc --sc-cflags --env-cflags --env-cppflags') + self.assertEqual(comp.exes['compiler_so'], + ('env_cc --sc-cflags ' + '--env-cflags ''--env-cppflags --sc-ccshared')) + self.assertEqual(comp.exes['compiler_cxx'], + 'env_cxx --env-cxx-flags') + self.assertEqual(comp.exes['linker_exe'], + 'env_cc') + self.assertEqual(comp.exes['linker_so'], + ('env_ldshared --env-ldflags --env-cflags' + ' --env-cppflags')) + self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') + + del os.environ['AR'] + del os.environ['CC'] + del os.environ['CPP'] + del os.environ['CXX'] + del os.environ['LDSHARED'] + del os.environ['LDFLAGS'] + del os.environ['ARFLAGS'] + del os.environ['CFLAGS'] + del os.environ['CPPFLAGS'] + + comp = self.customize_compiler() + self.assertEqual(comp.exes['archiver'], + 'sc_ar --sc-arflags') + self.assertEqual(comp.exes['preprocessor'], + 'sc_cc -E') + self.assertEqual(comp.exes['compiler'], + 'sc_cc --sc-cflags') + self.assertEqual(comp.exes['compiler_so'], + 'sc_cc --sc-cflags --sc-ccshared') + self.assertEqual(comp.exes['compiler_cxx'], + 'sc_cxx') + self.assertEqual(comp.exes['linker_exe'], + 'sc_cc') + self.assertEqual(comp.exes['linker_so'], + 'sc_ldshared') + self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') + + def test_parse_makefile_base(self): + self.makefile = TESTFN + fd = open(self.makefile, 'w') + try: + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + finally: + fd.close() + d = sysconfig.parse_makefile(self.makefile) + self.assertEqual(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", + 'OTHER': 'foo'}) + + def test_parse_makefile_literal_dollar(self): + self.makefile = TESTFN + fd = open(self.makefile, 'w') + try: + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + finally: + fd.close() + d = sysconfig.parse_makefile(self.makefile) + self.assertEqual(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", + 'OTHER': 'foo'}) + + + def test_sysconfig_module(self): + import sysconfig as global_sysconfig + self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), + sysconfig.get_config_var('CFLAGS')) + self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), + sysconfig.get_config_var('LDFLAGS')) + + @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'), + 'compiler flags customized') + def test_sysconfig_compiler_vars(self): + # On OS X, binary installers support extension module building on + # various levels of the operating system with differing Xcode + # configurations. This requires customization of some of the + # compiler configuration directives to suit the environment on + # the installed machine. Some of these customizations may require + # running external programs and, so, are deferred until needed by + # the first extension module build. With Python 3.3, only + # the Distutils version of sysconfig is used for extension module + # builds, which happens earlier in the Distutils tests. This may + # cause the following tests to fail since no tests have caused + # the global version of sysconfig to call the customization yet. + # The solution for now is to simply skip this test in this case. + # The longer-term solution is to only have one version of sysconfig. + + import sysconfig as global_sysconfig + if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'): + self.skipTest('compiler flags customized') + self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), + sysconfig.get_config_var('LDSHARED')) + self.assertEqual(global_sysconfig.get_config_var('CC'), + sysconfig.get_config_var('CC')) + + @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, + 'EXT_SUFFIX required for this test') + def test_SO_deprecation(self): + self.assertWarns(DeprecationWarning, + sysconfig.get_config_var, 'SO') + + @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, + 'EXT_SUFFIX required for this test') + def test_SO_value(self): + with check_warnings(('', DeprecationWarning)): + self.assertEqual(sysconfig.get_config_var('SO'), + sysconfig.get_config_var('EXT_SUFFIX')) + + @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, + 'EXT_SUFFIX required for this test') + def test_SO_in_vars(self): + vars = sysconfig.get_config_vars() + self.assertIsNotNone(vars['SO']) + self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) + + def test_customize_compiler_before_get_config_vars(self): + # Issue #21923: test that a Distribution compiler + # instance can be called without an explicit call to + # get_config_vars(). + with open(TESTFN, 'w') as f: + f.writelines(textwrap.dedent('''\ + from distutils.core import Distribution + config = Distribution().get_command_obj('config') + # try_compile may pass or it may fail if no compiler + # is found but it should not raise an exception. + rc = config.try_compile('int x;') + ''')) + p = subprocess.Popen([str(sys.executable), TESTFN], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + outs, errs = p.communicate() + self.assertEqual(0, p.returncode, "Subprocess failed: " + outs) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(SysconfigTestCase)) + return suite + + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/distutils/tests/test_text_file.py b/distutils/tests/test_text_file.py new file mode 100644 index 00000000..7e76240a --- /dev/null +++ b/distutils/tests/test_text_file.py @@ -0,0 +1,107 @@ +"""Tests for distutils.text_file.""" +import os +import unittest +from distutils.text_file import TextFile +from distutils.tests import support +from test.support import run_unittest + +TEST_DATA = """# test file + +line 3 \\ +# intervening comment + continues on next line +""" + +class TextFileTestCase(support.TempdirManager, unittest.TestCase): + + def test_class(self): + # old tests moved from text_file.__main__ + # so they are really called by the buildbots + + # result 1: no fancy options + result1 = ['# test file\n', '\n', 'line 3 \\\n', + '# intervening comment\n', + ' continues on next line\n'] + + # result 2: just strip comments + result2 = ["\n", + "line 3 \\\n", + " continues on next line\n"] + + # result 3: just strip blank lines + result3 = ["# test file\n", + "line 3 \\\n", + "# intervening comment\n", + " continues on next line\n"] + + # result 4: default, strip comments, blank lines, + # and trailing whitespace + result4 = ["line 3 \\", + " continues on next line"] + + # result 5: strip comments and blanks, plus join lines (but don't + # "collapse" joined lines + result5 = ["line 3 continues on next line"] + + # result 6: strip comments and blanks, plus join lines (and + # "collapse" joined lines + result6 = ["line 3 continues on next line"] + + def test_input(count, description, file, expected_result): + result = file.readlines() + self.assertEqual(result, expected_result) + + tmpdir = self.mkdtemp() + filename = os.path.join(tmpdir, "test.txt") + out_file = open(filename, "w") + try: + out_file.write(TEST_DATA) + finally: + out_file.close() + + in_file = TextFile(filename, strip_comments=0, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(1, "no processing", in_file, result1) + finally: + in_file.close() + + in_file = TextFile(filename, strip_comments=1, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(2, "strip comments", in_file, result2) + finally: + in_file.close() + + in_file = TextFile(filename, strip_comments=0, skip_blanks=1, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(3, "strip blanks", in_file, result3) + finally: + in_file.close() + + in_file = TextFile(filename) + try: + test_input(4, "default processing", in_file, result4) + finally: + in_file.close() + + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1) + try: + test_input(5, "join lines without collapsing", in_file, result5) + finally: + in_file.close() + + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1, collapse_join=1) + try: + test_input(6, "join lines with collapsing", in_file, result6) + finally: + in_file.close() + +def test_suite(): + return unittest.makeSuite(TextFileTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_unixccompiler.py b/distutils/tests/test_unixccompiler.py new file mode 100644 index 00000000..eef702cf --- /dev/null +++ b/distutils/tests/test_unixccompiler.py @@ -0,0 +1,141 @@ +"""Tests for distutils.unixccompiler.""" +import sys +import unittest +from test.support import EnvironmentVarGuard, run_unittest + +from distutils import sysconfig +from distutils.unixccompiler import UnixCCompiler + +class UnixCCompilerTestCase(unittest.TestCase): + + def setUp(self): + self._backup_platform = sys.platform + self._backup_get_config_var = sysconfig.get_config_var + class CompilerWrapper(UnixCCompiler): + def rpath_foo(self): + return self.runtime_library_dir_option('/foo') + self.cc = CompilerWrapper() + + def tearDown(self): + sys.platform = self._backup_platform + sysconfig.get_config_var = self._backup_get_config_var + + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") + def test_runtime_libdir_option(self): + # Issue#5900 + # + # Ensure RUNPATH is added to extension modules with RPATH if + # GNU ld is used + + # darwin + sys.platform = 'darwin' + self.assertEqual(self.cc.rpath_foo(), '-L/foo') + + # hp-ux + sys.platform = 'hp-ux' + old_gcv = sysconfig.get_config_var + def gcv(v): + return 'xxx' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo']) + + def gcv(v): + return 'gcc' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + def gcv(v): + return 'g++' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + sysconfig.get_config_var = old_gcv + + # GCC GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'gcc' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + + # GCC non-GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'gcc' + elif v == 'GNULD': + return 'no' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') + + # GCC GNULD with fully qualified configuration prefix + # see #7617 + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'x86_64-pc-linux-gnu-gcc-4.4.2' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + + # non-GCC GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'cc' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + + # non-GCC non-GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'cc' + elif v == 'GNULD': + return 'no' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') + def test_osx_cc_overrides_ldshared(self): + # Issue #18080: + # ensure that setting CC env variable also changes default linker + def gcv(v): + if v == 'LDSHARED': + return 'gcc-4.2 -bundle -undefined dynamic_lookup ' + return 'gcc-4.2' + sysconfig.get_config_var = gcv + with EnvironmentVarGuard() as env: + env['CC'] = 'my_cc' + del env['LDSHARED'] + sysconfig.customize_compiler(self.cc) + self.assertEqual(self.cc.linker_so[0], 'my_cc') + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') + def test_osx_explicit_ldshared(self): + # Issue #18080: + # ensure that setting CC env variable does not change + # explicit LDSHARED setting for linker + def gcv(v): + if v == 'LDSHARED': + return 'gcc-4.2 -bundle -undefined dynamic_lookup ' + return 'gcc-4.2' + sysconfig.get_config_var = gcv + with EnvironmentVarGuard() as env: + env['CC'] = 'my_cc' + env['LDSHARED'] = 'my_ld -bundle -dynamic' + sysconfig.customize_compiler(self.cc) + self.assertEqual(self.cc.linker_so[0], 'my_ld') + + +def test_suite(): + return unittest.makeSuite(UnixCCompilerTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_upload.py b/distutils/tests/test_upload.py new file mode 100644 index 00000000..bca5516d --- /dev/null +++ b/distutils/tests/test_upload.py @@ -0,0 +1,223 @@ +"""Tests for distutils.command.upload.""" +import os +import unittest +import unittest.mock as mock +from urllib.request import HTTPError + +from test.support import run_unittest + +from distutils.command import upload as upload_mod +from distutils.command.upload import upload +from distutils.core import Distribution +from distutils.errors import DistutilsError +from distutils.log import ERROR, INFO + +from distutils.tests.test_config import PYPIRC, BasePyPIRCCommandTestCase + +PYPIRC_LONG_PASSWORD = """\ +[distutils] + +index-servers = + server1 + server2 + +[server1] +username:me +password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ +""" + + +PYPIRC_NOPASSWORD = """\ +[distutils] + +index-servers = + server1 + +[server1] +username:me +""" + +class FakeOpen(object): + + def __init__(self, url, msg=None, code=None): + self.url = url + if not isinstance(url, str): + self.req = url + else: + self.req = None + self.msg = msg or 'OK' + self.code = code or 200 + + def getheader(self, name, default=None): + return { + 'content-type': 'text/plain; charset=utf-8', + }.get(name.lower(), default) + + def read(self): + return b'xyzzy' + + def getcode(self): + return self.code + + +class uploadTestCase(BasePyPIRCCommandTestCase): + + def setUp(self): + super(uploadTestCase, self).setUp() + self.old_open = upload_mod.urlopen + upload_mod.urlopen = self._urlopen + self.last_open = None + self.next_msg = None + self.next_code = None + + def tearDown(self): + upload_mod.urlopen = self.old_open + super(uploadTestCase, self).tearDown() + + def _urlopen(self, url): + self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code) + return self.last_open + + def test_finalize_options(self): + + # new format + self.write_file(self.rc, PYPIRC) + dist = Distribution() + cmd = upload(dist) + cmd.finalize_options() + for attr, waited in (('username', 'me'), ('password', 'secret'), + ('realm', 'pypi'), + ('repository', 'https://upload.pypi.org/legacy/')): + self.assertEqual(getattr(cmd, attr), waited) + + def test_saved_password(self): + # file with no password + self.write_file(self.rc, PYPIRC_NOPASSWORD) + + # make sure it passes + dist = Distribution() + cmd = upload(dist) + cmd.finalize_options() + self.assertEqual(cmd.password, None) + + # make sure we get it as well, if another command + # initialized it at the dist level + dist.password = 'xxx' + cmd = upload(dist) + cmd.finalize_options() + self.assertEqual(cmd.password, 'xxx') + + def test_upload(self): + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path) + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) + + # lets run it + pkg_dir, dist = self.create_dist(dist_files=dist_files) + cmd = upload(dist) + cmd.show_response = 1 + cmd.ensure_finalized() + cmd.run() + + # what did we send ? + headers = dict(self.last_open.req.headers) + self.assertGreaterEqual(int(headers['Content-length']), 2162) + content_type = headers['Content-type'] + self.assertTrue(content_type.startswith('multipart/form-data')) + self.assertEqual(self.last_open.req.get_method(), 'POST') + expected_url = 'https://upload.pypi.org/legacy/' + self.assertEqual(self.last_open.req.get_full_url(), expected_url) + data = self.last_open.req.data + self.assertIn(b'xxx',data) + self.assertIn(b'protocol_version', data) + self.assertIn(b'sha256_digest', data) + self.assertIn( + b'cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf' + b'6860', + data + ) + if b'md5_digest' in data: + self.assertIn(b'f561aaf6ef0bf14d4208bb46a4ccb3ad', data) + if b'blake2_256_digest' in data: + self.assertIn( + b'b6f289a27d4fe90da63c503bfe0a9b761a8f76bb86148565065f040be' + b'6d1c3044cf7ded78ef800509bccb4b648e507d88dc6383d67642aadcc' + b'ce443f1534330a', + data + ) + + # The PyPI response body was echoed + results = self.get_logs(INFO) + self.assertEqual(results[-1], 75 * '-' + '\nxyzzy\n' + 75 * '-') + + # bpo-32304: archives whose last byte was b'\r' were corrupted due to + # normalization intended for Mac OS 9. + def test_upload_correct_cr(self): + # content that ends with \r should not be modified. + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path, content='yy\r') + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) + + # other fields that ended with \r used to be modified, now are + # preserved. + pkg_dir, dist = self.create_dist( + dist_files=dist_files, + description='long description\r' + ) + cmd = upload(dist) + cmd.show_response = 1 + cmd.ensure_finalized() + cmd.run() + + headers = dict(self.last_open.req.headers) + self.assertGreaterEqual(int(headers['Content-length']), 2172) + self.assertIn(b'long description\r', self.last_open.req.data) + + def test_upload_fails(self): + self.next_msg = "Not Found" + self.next_code = 404 + self.assertRaises(DistutilsError, self.test_upload) + + def test_wrong_exception_order(self): + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path) + dist_files = [('xxx', '2.6', path)] # command, pyversion, filename + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) + + pkg_dir, dist = self.create_dist(dist_files=dist_files) + tests = [ + (OSError('oserror'), 'oserror', OSError), + (HTTPError('url', 400, 'httperror', {}, None), + 'Upload failed (400): httperror', DistutilsError), + ] + for exception, expected, raised_exception in tests: + with self.subTest(exception=type(exception).__name__): + with mock.patch('distutils.command.upload.urlopen', + new=mock.Mock(side_effect=exception)): + with self.assertRaises(raised_exception): + cmd = upload(dist) + cmd.ensure_finalized() + cmd.run() + results = self.get_logs(ERROR) + self.assertIn(expected, results[-1]) + self.clear_logs() + + +def test_suite(): + return unittest.makeSuite(uploadTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_util.py b/distutils/tests/test_util.py new file mode 100644 index 00000000..bf0d4333 --- /dev/null +++ b/distutils/tests/test_util.py @@ -0,0 +1,309 @@ +"""Tests for distutils.util.""" +import os +import sys +import unittest +from copy import copy +from test.support import run_unittest +from unittest import mock + +from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError +from distutils.util import (get_platform, convert_path, change_root, + check_environ, split_quoted, strtobool, + rfc822_escape, byte_compile, + grok_environment_error) +from distutils import util # used to patch _environ_checked +from distutils.sysconfig import get_config_vars +from distutils import sysconfig +from distutils.tests import support +import _osx_support + +class UtilTestCase(support.EnvironGuard, unittest.TestCase): + + def setUp(self): + super(UtilTestCase, self).setUp() + # saving the environment + self.name = os.name + self.platform = sys.platform + self.version = sys.version + self.sep = os.sep + self.join = os.path.join + self.isabs = os.path.isabs + self.splitdrive = os.path.splitdrive + self._config_vars = copy(sysconfig._config_vars) + + # patching os.uname + if hasattr(os, 'uname'): + self.uname = os.uname + self._uname = os.uname() + else: + self.uname = None + self._uname = None + + os.uname = self._get_uname + + def tearDown(self): + # getting back the environment + os.name = self.name + sys.platform = self.platform + sys.version = self.version + os.sep = self.sep + os.path.join = self.join + os.path.isabs = self.isabs + os.path.splitdrive = self.splitdrive + if self.uname is not None: + os.uname = self.uname + else: + del os.uname + sysconfig._config_vars = copy(self._config_vars) + super(UtilTestCase, self).tearDown() + + def _set_uname(self, uname): + self._uname = uname + + def _get_uname(self): + return self._uname + + def test_get_platform(self): + + # windows XP, 32bits + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Intel)]') + sys.platform = 'win32' + self.assertEqual(get_platform(), 'win32') + + # windows XP, amd64 + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Amd64)]') + sys.platform = 'win32' + self.assertEqual(get_platform(), 'win-amd64') + + # macbook + os.name = 'posix' + sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' + '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') + sys.platform = 'darwin' + self._set_uname(('Darwin', 'macziade', '8.11.1', + ('Darwin Kernel Version 8.11.1: ' + 'Wed Oct 10 18:23:28 PDT 2007; ' + 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + + get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' + '-fwrapv -O3 -Wall -Wstrict-prototypes') + + cursize = sys.maxsize + sys.maxsize = (2 ** 31)-1 + try: + self.assertEqual(get_platform(), 'macosx-10.3-i386') + finally: + sys.maxsize = cursize + + # macbook with fat binaries (fat, universal or fat64) + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEqual(get_platform(), 'macosx-10.4-fat') + + _osx_support._remove_original_values(get_config_vars()) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1' + self.assertEqual(get_platform(), 'macosx-10.4-fat') + + + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEqual(get_platform(), 'macosx-10.4-intel') + + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + self.assertEqual(get_platform(), 'macosx-10.4-fat3') + + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + self.assertEqual(get_platform(), 'macosx-10.4-universal') + + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEqual(get_platform(), 'macosx-10.4-fat64') + + for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3'%(arch,)) + + self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,)) + + + # linux debian sarge + os.name = 'posix' + sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' + '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]') + sys.platform = 'linux2' + self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', + '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) + + self.assertEqual(get_platform(), 'linux-i686') + + # XXX more platforms to tests here + + def test_convert_path(self): + # linux/mac + os.sep = '/' + def _join(path): + return '/'.join(path) + os.path.join = _join + + self.assertEqual(convert_path('/home/to/my/stuff'), + '/home/to/my/stuff') + + # win + os.sep = '\\' + def _join(*path): + return '\\'.join(path) + os.path.join = _join + + self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') + self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') + + self.assertEqual(convert_path('home/to/my/stuff'), + 'home\\to\\my\\stuff') + self.assertEqual(convert_path('.'), + os.curdir) + + def test_change_root(self): + # linux/mac + os.name = 'posix' + def _isabs(path): + return path[0] == '/' + os.path.isabs = _isabs + def _join(*path): + return '/'.join(path) + os.path.join = _join + + self.assertEqual(change_root('/root', '/old/its/here'), + '/root/old/its/here') + self.assertEqual(change_root('/root', 'its/here'), + '/root/its/here') + + # windows + os.name = 'nt' + def _isabs(path): + return path.startswith('c:\\') + os.path.isabs = _isabs + def _splitdrive(path): + if path.startswith('c:'): + return ('', path.replace('c:', '')) + return ('', path) + os.path.splitdrive = _splitdrive + def _join(*path): + return '\\'.join(path) + os.path.join = _join + + self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'), + 'c:\\root\\old\\its\\here') + self.assertEqual(change_root('c:\\root', 'its\\here'), + 'c:\\root\\its\\here') + + # BugsBunny os (it's a great os) + os.name = 'BugsBunny' + self.assertRaises(DistutilsPlatformError, + change_root, 'c:\\root', 'its\\here') + + # XXX platforms to be covered: mac + + def test_check_environ(self): + util._environ_checked = 0 + os.environ.pop('HOME', None) + + check_environ() + + self.assertEqual(os.environ['PLAT'], get_platform()) + self.assertEqual(util._environ_checked, 1) + + @unittest.skipUnless(os.name == 'posix', 'specific to posix') + def test_check_environ_getpwuid(self): + util._environ_checked = 0 + os.environ.pop('HOME', None) + + import pwd + + # only set pw_dir field, other fields are not used + result = pwd.struct_passwd((None, None, None, None, None, + '/home/distutils', None)) + with mock.patch.object(pwd, 'getpwuid', return_value=result): + check_environ() + self.assertEqual(os.environ['HOME'], '/home/distutils') + + util._environ_checked = 0 + os.environ.pop('HOME', None) + + # bpo-10496: Catch pwd.getpwuid() error + with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError): + check_environ() + self.assertNotIn('HOME', os.environ) + + def test_split_quoted(self): + self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), + ['one', 'two', 'three', 'four']) + + def test_strtobool(self): + yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') + no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') + + for y in yes: + self.assertTrue(strtobool(y)) + + for n in no: + self.assertFalse(strtobool(n)) + + def test_rfc822_escape(self): + header = 'I am a\npoor\nlonesome\nheader\n' + res = rfc822_escape(header) + wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' + 'header%(8s)s') % {'8s': '\n'+8*' '} + self.assertEqual(res, wanted) + + def test_dont_write_bytecode(self): + # makes sure byte_compile raise a DistutilsError + # if sys.dont_write_bytecode is True + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + self.assertRaises(DistutilsByteCompileError, byte_compile, []) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + def test_grok_environment_error(self): + # test obsolete function to ensure backward compat (#4931) + exc = IOError("Unable to find batch file") + msg = grok_environment_error(exc) + self.assertEqual(msg, "error: Unable to find batch file") + + +def test_suite(): + return unittest.makeSuite(UtilTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_version.py b/distutils/tests/test_version.py new file mode 100644 index 00000000..8671cd2f --- /dev/null +++ b/distutils/tests/test_version.py @@ -0,0 +1,87 @@ +"""Tests for distutils.version.""" +import unittest +from distutils.version import LooseVersion +from distutils.version import StrictVersion +from test.support import run_unittest + +class VersionTestCase(unittest.TestCase): + + def test_prerelease(self): + version = StrictVersion('1.2.3a1') + self.assertEqual(version.version, (1, 2, 3)) + self.assertEqual(version.prerelease, ('a', 1)) + self.assertEqual(str(version), '1.2.3a1') + + version = StrictVersion('1.2.0') + self.assertEqual(str(version), '1.2') + + def test_cmp_strict(self): + versions = (('1.5.1', '1.5.2b2', -1), + ('161', '3.10a', ValueError), + ('8.02', '8.02', 0), + ('3.4j', '1996.07.12', ValueError), + ('3.2.pl0', '3.1.1.6', ValueError), + ('2g6', '11g', ValueError), + ('0.9', '2.2', -1), + ('1.2.1', '1.2', 1), + ('1.1', '1.2.2', -1), + ('1.2', '1.1', 1), + ('1.2.1', '1.2.2', -1), + ('1.2.2', '1.2', 1), + ('1.2', '1.2.2', -1), + ('0.4.0', '0.4', 0), + ('1.13++', '5.5.kw', ValueError)) + + for v1, v2, wanted in versions: + try: + res = StrictVersion(v1)._cmp(StrictVersion(v2)) + except ValueError: + if wanted is ValueError: + continue + else: + raise AssertionError(("cmp(%s, %s) " + "shouldn't raise ValueError") + % (v1, v2)) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + res = StrictVersion(v1)._cmp(v2) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + res = StrictVersion(v1)._cmp(object()) + self.assertIs(res, NotImplemented, + 'cmp(%s, %s) should be NotImplemented, got %s' % + (v1, v2, res)) + + + def test_cmp(self): + versions = (('1.5.1', '1.5.2b2', -1), + ('161', '3.10a', 1), + ('8.02', '8.02', 0), + ('3.4j', '1996.07.12', -1), + ('3.2.pl0', '3.1.1.6', 1), + ('2g6', '11g', -1), + ('0.960923', '2.2beta29', -1), + ('1.13++', '5.5.kw', -1)) + + + for v1, v2, wanted in versions: + res = LooseVersion(v1)._cmp(LooseVersion(v2)) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + res = LooseVersion(v1)._cmp(v2) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + res = LooseVersion(v1)._cmp(object()) + self.assertIs(res, NotImplemented, + 'cmp(%s, %s) should be NotImplemented, got %s' % + (v1, v2, res)) + +def test_suite(): + return unittest.makeSuite(VersionTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/distutils/tests/test_versionpredicate.py b/distutils/tests/test_versionpredicate.py new file mode 100644 index 00000000..28ae09dc --- /dev/null +++ b/distutils/tests/test_versionpredicate.py @@ -0,0 +1,13 @@ +"""Tests harness for distutils.versionpredicate. + +""" + +import distutils.versionpredicate +import doctest +from test.support import run_unittest + +def test_suite(): + return doctest.DocTestSuite(distutils.versionpredicate) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/distutils/text_file.py b/distutils/text_file.py new file mode 100644 index 00000000..93abad38 --- /dev/null +++ b/distutils/text_file.py @@ -0,0 +1,286 @@ +"""text_file + +provides the TextFile class, which gives an interface to text files +that (optionally) takes care of stripping comments, ignoring blank +lines, and joining lines with backslashes.""" + +import sys, io + + +class TextFile: + """Provides a file-like object that takes care of all the things you + commonly want to do when processing a text file that has some + line-by-line syntax: strip comments (as long as "#" is your + comment character), skip blank lines, join adjacent lines by + escaping the newline (ie. backslash at end of line), strip + leading and/or trailing whitespace. All of these are optional + and independently controllable. + + Provides a 'warn()' method so you can generate warning messages that + report physical line number, even if the logical line in question + spans multiple physical lines. Also provides 'unreadline()' for + implementing line-at-a-time lookahead. + + Constructor is called as: + + TextFile (filename=None, file=None, **options) + + It bombs (RuntimeError) if both 'filename' and 'file' are None; + 'filename' should be a string, and 'file' a file object (or + something that provides 'readline()' and 'close()' methods). It is + recommended that you supply at least 'filename', so that TextFile + can include it in warning messages. If 'file' is not supplied, + TextFile creates its own using 'io.open()'. + + The options are all boolean, and affect the value returned by + 'readline()': + strip_comments [default: true] + strip from "#" to end-of-line, as well as any whitespace + leading up to the "#" -- unless it is escaped by a backslash + lstrip_ws [default: false] + strip leading whitespace from each line before returning it + rstrip_ws [default: true] + strip trailing whitespace (including line terminator!) from + each line before returning it + skip_blanks [default: true} + skip lines that are empty *after* stripping comments and + whitespace. (If both lstrip_ws and rstrip_ws are false, + then some lines may consist of solely whitespace: these will + *not* be skipped, even if 'skip_blanks' is true.) + join_lines [default: false] + if a backslash is the last non-newline character on a line + after stripping comments and whitespace, join the following line + to it to form one "logical line"; if N consecutive lines end + with a backslash, then N+1 physical lines will be joined to + form one logical line. + collapse_join [default: false] + strip leading whitespace from lines that are joined to their + predecessor; only matters if (join_lines and not lstrip_ws) + errors [default: 'strict'] + error handler used to decode the file content + + Note that since 'rstrip_ws' can strip the trailing newline, the + semantics of 'readline()' must differ from those of the builtin file + object's 'readline()' method! In particular, 'readline()' returns + None for end-of-file: an empty string might just be a blank line (or + an all-whitespace line), if 'rstrip_ws' is true but 'skip_blanks' is + not.""" + + default_options = { 'strip_comments': 1, + 'skip_blanks': 1, + 'lstrip_ws': 0, + 'rstrip_ws': 1, + 'join_lines': 0, + 'collapse_join': 0, + 'errors': 'strict', + } + + def __init__(self, filename=None, file=None, **options): + """Construct a new TextFile object. At least one of 'filename' + (a string) and 'file' (a file-like object) must be supplied. + They keyword argument options are described above and affect + the values returned by 'readline()'.""" + if filename is None and file is None: + raise RuntimeError("you must supply either or both of 'filename' and 'file'") + + # set values for all options -- either from client option hash + # or fallback to default_options + for opt in self.default_options.keys(): + if opt in options: + setattr(self, opt, options[opt]) + else: + setattr(self, opt, self.default_options[opt]) + + # sanity check client option hash + for opt in options.keys(): + if opt not in self.default_options: + raise KeyError("invalid TextFile option '%s'" % opt) + + if file is None: + self.open(filename) + else: + self.filename = filename + self.file = file + self.current_line = 0 # assuming that file is at BOF! + + # 'linebuf' is a stack of lines that will be emptied before we + # actually read from the file; it's only populated by an + # 'unreadline()' operation + self.linebuf = [] + + def open(self, filename): + """Open a new file named 'filename'. This overrides both the + 'filename' and 'file' arguments to the constructor.""" + self.filename = filename + self.file = io.open(self.filename, 'r', errors=self.errors) + self.current_line = 0 + + def close(self): + """Close the current file and forget everything we know about it + (filename, current line number).""" + file = self.file + self.file = None + self.filename = None + self.current_line = None + file.close() + + def gen_error(self, msg, line=None): + outmsg = [] + if line is None: + line = self.current_line + outmsg.append(self.filename + ", ") + if isinstance(line, (list, tuple)): + outmsg.append("lines %d-%d: " % tuple(line)) + else: + outmsg.append("line %d: " % line) + outmsg.append(str(msg)) + return "".join(outmsg) + + def error(self, msg, line=None): + raise ValueError("error: " + self.gen_error(msg, line)) + + def warn(self, msg, line=None): + """Print (to stderr) a warning message tied to the current logical + line in the current file. If the current logical line in the + file spans multiple physical lines, the warning refers to the + whole range, eg. "lines 3-5". If 'line' supplied, it overrides + the current line number; it may be a list or tuple to indicate a + range of physical lines, or an integer for a single physical + line.""" + sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n") + + def readline(self): + """Read and return a single logical line from the current file (or + from an internal buffer if lines have previously been "unread" + with 'unreadline()'). If the 'join_lines' option is true, this + may involve reading multiple physical lines concatenated into a + single string. Updates the current line number, so calling + 'warn()' after 'readline()' emits a warning about the physical + line(s) just read. Returns None on end-of-file, since the empty + string can occur if 'rstrip_ws' is true but 'strip_blanks' is + not.""" + # If any "unread" lines waiting in 'linebuf', return the top + # one. (We don't actually buffer read-ahead data -- lines only + # get put in 'linebuf' if the client explicitly does an + # 'unreadline()'. + if self.linebuf: + line = self.linebuf[-1] + del self.linebuf[-1] + return line + + buildup_line = '' + + while True: + # read the line, make it None if EOF + line = self.file.readline() + if line == '': + line = None + + if self.strip_comments and line: + + # Look for the first "#" in the line. If none, never + # mind. If we find one and it's the first character, or + # is not preceded by "\", then it starts a comment -- + # strip the comment, strip whitespace before it, and + # carry on. Otherwise, it's just an escaped "#", so + # unescape it (and any other escaped "#"'s that might be + # lurking in there) and otherwise leave the line alone. + + pos = line.find("#") + if pos == -1: # no "#" -- no comments + pass + + # It's definitely a comment -- either "#" is the first + # character, or it's elsewhere and unescaped. + elif pos == 0 or line[pos-1] != "\\": + # Have to preserve the trailing newline, because it's + # the job of a later step (rstrip_ws) to remove it -- + # and if rstrip_ws is false, we'd better preserve it! + # (NB. this means that if the final line is all comment + # and has no trailing newline, we will think that it's + # EOF; I think that's OK.) + eol = (line[-1] == '\n') and '\n' or '' + line = line[0:pos] + eol + + # If all that's left is whitespace, then skip line + # *now*, before we try to join it to 'buildup_line' -- + # that way constructs like + # hello \\ + # # comment that should be ignored + # there + # result in "hello there". + if line.strip() == "": + continue + else: # it's an escaped "#" + line = line.replace("\\#", "#") + + # did previous line end with a backslash? then accumulate + if self.join_lines and buildup_line: + # oops: end of file + if line is None: + self.warn("continuation line immediately precedes " + "end-of-file") + return buildup_line + + if self.collapse_join: + line = line.lstrip() + line = buildup_line + line + + # careful: pay attention to line number when incrementing it + if isinstance(self.current_line, list): + self.current_line[1] = self.current_line[1] + 1 + else: + self.current_line = [self.current_line, + self.current_line + 1] + # just an ordinary line, read it as usual + else: + if line is None: # eof + return None + + # still have to be careful about incrementing the line number! + if isinstance(self.current_line, list): + self.current_line = self.current_line[1] + 1 + else: + self.current_line = self.current_line + 1 + + # strip whitespace however the client wants (leading and + # trailing, or one or the other, or neither) + if self.lstrip_ws and self.rstrip_ws: + line = line.strip() + elif self.lstrip_ws: + line = line.lstrip() + elif self.rstrip_ws: + line = line.rstrip() + + # blank line (whether we rstrip'ed or not)? skip to next line + # if appropriate + if (line == '' or line == '\n') and self.skip_blanks: + continue + + if self.join_lines: + if line[-1] == '\\': + buildup_line = line[:-1] + continue + + if line[-2:] == '\\\n': + buildup_line = line[0:-2] + '\n' + continue + + # well, I guess there's some actual content there: return it + return line + + def readlines(self): + """Read and return the list of all logical lines remaining in the + current file.""" + lines = [] + while True: + line = self.readline() + if line is None: + return lines + lines.append(line) + + def unreadline(self, line): + """Push 'line' (a string) onto an internal buffer that will be + checked by future 'readline()' calls. Handy for implementing + a parser with line-at-a-time lookahead.""" + self.linebuf.append(line) diff --git a/distutils/unixccompiler.py b/distutils/unixccompiler.py new file mode 100644 index 00000000..4d7a6de7 --- /dev/null +++ b/distutils/unixccompiler.py @@ -0,0 +1,328 @@ +"""distutils.unixccompiler + +Contains the UnixCCompiler class, a subclass of CCompiler that handles +the "typical" Unix-style command-line C compiler: + * macros defined with -Dname[=value] + * macros undefined with -Uname + * include search directories specified with -Idir + * libraries specified with -lllib + * library search directories specified with -Ldir + * compile handled by 'cc' (or similar) executable with -c option: + compiles .c to .o + * link static library handled by 'ar' command (possibly with 'ranlib') + * link shared library handled by 'cc -shared' +""" + +import os, sys, re + +from distutils import sysconfig +from distutils.dep_util import newer +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options, gen_lib_options +from distutils.errors import \ + DistutilsExecError, CompileError, LibError, LinkError +from distutils import log + +if sys.platform == 'darwin': + import _osx_support + +# XXX Things not currently handled: +# * optimization/debug/warning flags; we just use whatever's in Python's +# Makefile and live with it. Is this adequate? If not, we might +# have to have a bunch of subclasses GNUCCompiler, SGICCompiler, +# SunCCompiler, and I suspect down that road lies madness. +# * even if we don't know a warning flag from an optimization flag, +# we need some way for outsiders to feed preprocessor/compiler/linker +# flags in to us -- eg. a sysadmin might want to mandate certain flags +# via a site config file, or a user might want to set something for +# compiling this module distribution only via the setup.py command +# line, whatever. As long as these options come from something on the +# current system, they can be as system-dependent as they like, and we +# should just happily stuff them into the preprocessor/compiler/linker +# options and carry on. + + +class UnixCCompiler(CCompiler): + + compiler_type = 'unix' + + # These are used by CCompiler in two places: the constructor sets + # instance attributes 'preprocessor', 'compiler', etc. from them, and + # 'set_executable()' allows any of these to be set. The defaults here + # are pretty generic; they will probably have to be set by an outsider + # (eg. using information discovered by the sysconfig about building + # Python extensions). + executables = {'preprocessor' : None, + 'compiler' : ["cc"], + 'compiler_so' : ["cc"], + 'compiler_cxx' : ["cc"], + 'linker_so' : ["cc", "-shared"], + 'linker_exe' : ["cc"], + 'archiver' : ["ar", "-cr"], + 'ranlib' : None, + } + + if sys.platform[:6] == "darwin": + executables['ranlib'] = ["ranlib"] + + # Needed for the filename generation methods provided by the base + # class, CCompiler. NB. whoever instantiates/uses a particular + # UnixCCompiler instance should set 'shared_lib_ext' -- we set a + # reasonable common default here, but it's not necessarily used on all + # Unices! + + src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"] + obj_extension = ".o" + static_lib_extension = ".a" + shared_lib_extension = ".so" + dylib_lib_extension = ".dylib" + xcode_stub_lib_extension = ".tbd" + static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" + xcode_stub_lib_format = dylib_lib_format + if sys.platform == "cygwin": + exe_extension = ".exe" + + def preprocess(self, source, output_file=None, macros=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): + fixed_args = self._fix_compile_args(None, macros, include_dirs) + ignore, macros, include_dirs = fixed_args + pp_opts = gen_preprocess_options(macros, include_dirs) + pp_args = self.preprocessor + pp_opts + if output_file: + pp_args.extend(['-o', output_file]) + if extra_preargs: + pp_args[:0] = extra_preargs + if extra_postargs: + pp_args.extend(extra_postargs) + pp_args.append(source) + + # We need to preprocess: either we're being forced to, or we're + # generating output to stdout, or there's a target output file and + # the source file is newer than the target (or the target doesn't + # exist). + if self.force or output_file is None or newer(source, output_file): + if output_file: + self.mkpath(os.path.dirname(output_file)) + try: + self.spawn(pp_args) + except DistutilsExecError as msg: + raise CompileError(msg) + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + compiler_so = self.compiler_so + if sys.platform == 'darwin': + compiler_so = _osx_support.compiler_fixup(compiler_so, + cc_args + extra_postargs) + try: + self.spawn(compiler_so + cc_args + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + def create_static_lib(self, objects, output_libname, + output_dir=None, debug=0, target_lang=None): + objects, output_dir = self._fix_object_args(objects, output_dir) + + output_filename = \ + self.library_filename(output_libname, output_dir=output_dir) + + if self._need_link(objects, output_filename): + self.mkpath(os.path.dirname(output_filename)) + self.spawn(self.archiver + + [output_filename] + + objects + self.objects) + + # Not many Unices required ranlib anymore -- SunOS 4.x is, I + # think the only major Unix that does. Maybe we need some + # platform intelligence here to skip ranlib if it's not + # needed -- or maybe Python's configure script took care of + # it for us, hence the check for leading colon. + if self.ranlib: + try: + self.spawn(self.ranlib + [output_filename]) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def link(self, target_desc, objects, + output_filename, output_dir=None, libraries=None, + library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None): + objects, output_dir = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + libraries, library_dirs, runtime_library_dirs = fixed_args + + lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, + libraries) + if not isinstance(output_dir, (str, type(None))): + raise TypeError("'output_dir' must be a string or None") + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + ld_args = (objects + self.objects + + lib_opts + ['-o', output_filename]) + if debug: + ld_args[:0] = ['-g'] + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + self.mkpath(os.path.dirname(output_filename)) + try: + if target_desc == CCompiler.EXECUTABLE: + linker = self.linker_exe[:] + else: + linker = self.linker_so[:] + if target_lang == "c++" and self.compiler_cxx: + # skip over environment variable settings if /usr/bin/env + # is used to set up the linker's environment. + # This is needed on OSX. Note: this assumes that the + # normal and C++ compiler have the same environment + # settings. + i = 0 + if os.path.basename(linker[0]) == "env": + i = 1 + while '=' in linker[i]: + i += 1 + + if os.path.basename(linker[i]) == 'ld_so_aix': + # AIX platforms prefix the compiler with the ld_so_aix + # script, so we need to adjust our linker index + offset = 1 + else: + offset = 0 + + linker[i+offset] = self.compiler_cxx[i] + + if sys.platform == 'darwin': + linker = _osx_support.compiler_fixup(linker, ld_args) + + self.spawn(linker + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "-L" + dir + + def _is_gcc(self, compiler_name): + return "gcc" in compiler_name or "g++" in compiler_name + + def runtime_library_dir_option(self, dir): + # XXX Hackish, at the very least. See Python bug #445902: + # http://sourceforge.net/tracker/index.php + # ?func=detail&aid=445902&group_id=5470&atid=105470 + # Linkers on different platforms need different options to + # specify that directories need to be added to the list of + # directories searched for dependencies when a dynamic library + # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to + # be told to pass the -R option through to the linker, whereas + # other compilers and gcc on other systems just know this. + # Other compilers may need something slightly different. At + # this time, there's no way to determine this information from + # the configuration data stored in the Python installation, so + # we use this hack. + compiler = os.path.basename(sysconfig.get_config_var("CC")) + if sys.platform[:6] == "darwin": + # MacOSX's linker doesn't understand the -R flag at all + return "-L" + dir + elif sys.platform[:7] == "freebsd": + return "-Wl,-rpath=" + dir + elif sys.platform[:5] == "hp-ux": + if self._is_gcc(compiler): + return ["-Wl,+s", "-L" + dir] + return ["+s", "-L" + dir] + else: + if self._is_gcc(compiler): + # gcc on non-GNU systems does not need -Wl, but can + # use it anyway. Since distutils has always passed in + # -Wl whenever gcc was used in the past it is probably + # safest to keep doing so. + if sysconfig.get_config_var("GNULD") == "yes": + # GNU ld needs an extra option to get a RUNPATH + # instead of just an RPATH. + return "-Wl,--enable-new-dtags,-R" + dir + else: + return "-Wl,-R" + dir + else: + # No idea how --enable-new-dtags would be passed on to + # ld if this system was using GNU ld. Don't know if a + # system like this even exists. + return "-R" + dir + + def library_option(self, lib): + return "-l" + lib + + def find_library_file(self, dirs, lib, debug=0): + shared_f = self.library_filename(lib, lib_type='shared') + dylib_f = self.library_filename(lib, lib_type='dylib') + xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub') + static_f = self.library_filename(lib, lib_type='static') + + if sys.platform == 'darwin': + # On OSX users can specify an alternate SDK using + # '-isysroot', calculate the SDK root if it is specified + # (and use it further on) + # + # Note that, as of Xcode 7, Apple SDKs may contain textual stub + # libraries with .tbd extensions rather than the normal .dylib + # shared libraries installed in /. The Apple compiler tool + # chain handles this transparently but it can cause problems + # for programs that are being built with an SDK and searching + # for specific libraries. Callers of find_library_file need to + # keep in mind that the base filename of the returned SDK library + # file might have a different extension from that of the library + # file installed on the running system, for example: + # /Applications/Xcode.app/Contents/Developer/Platforms/ + # MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/ + # usr/lib/libedit.tbd + # vs + # /usr/lib/libedit.dylib + cflags = sysconfig.get_config_var('CFLAGS') + m = re.search(r'-isysroot\s*(\S+)', cflags) + if m is None: + sysroot = '/' + else: + sysroot = m.group(1) + + + + for dir in dirs: + shared = os.path.join(dir, shared_f) + dylib = os.path.join(dir, dylib_f) + static = os.path.join(dir, static_f) + xcode_stub = os.path.join(dir, xcode_stub_f) + + if sys.platform == 'darwin' and ( + dir.startswith('/System/') or ( + dir.startswith('/usr/') and not dir.startswith('/usr/local/'))): + + shared = os.path.join(sysroot, dir[1:], shared_f) + dylib = os.path.join(sysroot, dir[1:], dylib_f) + static = os.path.join(sysroot, dir[1:], static_f) + xcode_stub = os.path.join(sysroot, dir[1:], xcode_stub_f) + + # We're second-guessing the linker here, with not much hard + # data to go on: GCC seems to prefer the shared library, so I'm + # assuming that *all* Unix C compilers do. And of course I'm + # ignoring even GCC's "-static" option. So sue me. + if os.path.exists(dylib): + return dylib + elif os.path.exists(xcode_stub): + return xcode_stub + elif os.path.exists(shared): + return shared + elif os.path.exists(static): + return static + + # Oops, didn't find it in *any* of 'dirs' + return None diff --git a/distutils/util.py b/distutils/util.py new file mode 100644 index 00000000..4b002ece --- /dev/null +++ b/distutils/util.py @@ -0,0 +1,559 @@ +"""distutils.util + +Miscellaneous utility functions -- anything that doesn't fit into +one of the other *util.py modules. +""" + +import os +import re +import importlib.util +import string +import sys +from distutils.errors import DistutilsPlatformError +from distutils.dep_util import newer +from distutils.spawn import spawn +from distutils import log +from distutils.errors import DistutilsByteCompileError + +def get_host_platform(): + """Return a string that identifies the current platform. This is used mainly to + distinguish platform-specific build directories and platform-specific built + distributions. Typically includes the OS name and version and the + architecture (as supplied by 'os.uname()'), although the exact information + included depends on the OS; eg. on Linux, the kernel version isn't + particularly important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + + """ + if os.name == 'nt': + if 'amd64' in sys.version.lower(): + return 'win-amd64' + if '(arm)' in sys.version.lower(): + return 'win-arm32' + if '(arm64)' in sys.version.lower(): + return 'win-arm64' + return sys.platform + + # Set for cross builds explicitly + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_HOST_PLATFORM"] + + if os.name != "posix" or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + + (osname, host, release, version, machine) = os.uname() + + # Convert the OS name to lowercase, remove '/' characters, and translate + # spaces (for "Power Macintosh") + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') + + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # We can't use "platform.architecture()[0]" because a + # bootstrap problem. We use a dict to get an error + # if some suspicious happens. + bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} + machine += ".%s" % bitness[sys.maxsize] + # fall through to standard osname-release-machine representation + elif osname[:3] == "aix": + from _aix_support import aix_platform + return aix_platform() + elif osname[:6] == "cygwin": + osname = "cygwin" + rel_re = re.compile (r'[\d.]+', re.ASCII) + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == "darwin": + import _osx_support, distutils.sysconfig + osname, release, machine = _osx_support.get_platform_osx( + distutils.sysconfig.get_config_vars(), + osname, release, machine) + + return "%s-%s-%s" % (osname, release, machine) + +def get_platform(): + if os.name == 'nt': + TARGET_TO_PLAT = { + 'x86' : 'win32', + 'x64' : 'win-amd64', + 'arm' : 'win-arm32', + } + return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform() + else: + return get_host_platform() + +def convert_path (pathname): + """Return 'pathname' as a name that will work on the native filesystem, + i.e. split it on '/' and put it back together again using the current + directory separator. Needed because filenames in the setup script are + always supplied in Unix style, and have to be converted to the local + convention before we can actually use them in the filesystem. Raises + ValueError on non-Unix-ish systems if 'pathname' either starts or + ends with a slash. + """ + if os.sep == '/': + return pathname + if not pathname: + return pathname + if pathname[0] == '/': + raise ValueError("path '%s' cannot be absolute" % pathname) + if pathname[-1] == '/': + raise ValueError("path '%s' cannot end with '/'" % pathname) + + paths = pathname.split('/') + while '.' in paths: + paths.remove('.') + if not paths: + return os.curdir + return os.path.join(*paths) + +# convert_path () + + +def change_root (new_root, pathname): + """Return 'pathname' with 'new_root' prepended. If 'pathname' is + relative, this is equivalent to "os.path.join(new_root,pathname)". + Otherwise, it requires making 'pathname' relative and then joining the + two, which is tricky on DOS/Windows and Mac OS. + """ + if os.name == 'posix': + if not os.path.isabs(pathname): + return os.path.join(new_root, pathname) + else: + return os.path.join(new_root, pathname[1:]) + + elif os.name == 'nt': + (drive, path) = os.path.splitdrive(pathname) + if path[0] == '\\': + path = path[1:] + return os.path.join(new_root, path) + + else: + raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) + + +_environ_checked = 0 +def check_environ (): + """Ensure that 'os.environ' has all the environment variables we + guarantee that users can use in config files, command-line options, + etc. Currently this includes: + HOME - user's home directory (Unix only) + PLAT - description of the current platform, including hardware + and OS (see 'get_platform()') + """ + global _environ_checked + if _environ_checked: + return + + if os.name == 'posix' and 'HOME' not in os.environ: + try: + import pwd + os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + except (ImportError, KeyError): + # bpo-10496: if the current user identifier doesn't exist in the + # password database, do nothing + pass + + if 'PLAT' not in os.environ: + os.environ['PLAT'] = get_platform() + + _environ_checked = 1 + + +def subst_vars (s, local_vars): + """Perform shell/Perl-style variable substitution on 'string'. Every + occurrence of '$' followed by a name is considered a variable, and + variable is substituted by the value found in the 'local_vars' + dictionary, or in 'os.environ' if it's not in 'local_vars'. + 'os.environ' is first checked/augmented to guarantee that it contains + certain values: see 'check_environ()'. Raise ValueError for any + variables not found in either 'local_vars' or 'os.environ'. + """ + check_environ() + def _subst (match, local_vars=local_vars): + var_name = match.group(1) + if var_name in local_vars: + return str(local_vars[var_name]) + else: + return os.environ[var_name] + + try: + return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) + except KeyError as var: + raise ValueError("invalid variable '$%s'" % var) + +# subst_vars () + + +def grok_environment_error (exc, prefix="error: "): + # Function kept for backward compatibility. + # Used to try clever things with EnvironmentErrors, + # but nowadays str(exception) produces good messages. + return prefix + str(exc) + + +# Needed by 'split_quoted()' +_wordchars_re = _squote_re = _dquote_re = None +def _init_regex(): + global _wordchars_re, _squote_re, _dquote_re + _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) + _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") + _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') + +def split_quoted (s): + """Split a string up according to Unix shell-like rules for quotes and + backslashes. In short: words are delimited by spaces, as long as those + spaces are not escaped by a backslash, or inside a quoted string. + Single and double quotes are equivalent, and the quote characters can + be backslash-escaped. The backslash is stripped from any two-character + escape sequence, leaving only the escaped character. The quote + characters are stripped from any quoted string. Returns a list of + words. + """ + + # This is a nice algorithm for splitting up a single string, since it + # doesn't require character-by-character examination. It was a little + # bit of a brain-bender to get it working right, though... + if _wordchars_re is None: _init_regex() + + s = s.strip() + words = [] + pos = 0 + + while s: + m = _wordchars_re.match(s, pos) + end = m.end() + if end == len(s): + words.append(s[:end]) + break + + if s[end] in string.whitespace: # unescaped, unquoted whitespace: now + words.append(s[:end]) # we definitely have a word delimiter + s = s[end:].lstrip() + pos = 0 + + elif s[end] == '\\': # preserve whatever is being escaped; + # will become part of the current word + s = s[:end] + s[end+1:] + pos = end+1 + + else: + if s[end] == "'": # slurp singly-quoted string + m = _squote_re.match(s, end) + elif s[end] == '"': # slurp doubly-quoted string + m = _dquote_re.match(s, end) + else: + raise RuntimeError("this can't happen (bad char '%c')" % s[end]) + + if m is None: + raise ValueError("bad string (mismatched %s quotes?)" % s[end]) + + (beg, end) = m.span() + s = s[:beg] + s[beg+1:end-1] + s[end:] + pos = m.end() - 2 + + if pos >= len(s): + words.append(s) + break + + return words + +# split_quoted () + + +def execute (func, args, msg=None, verbose=0, dry_run=0): + """Perform some action that affects the outside world (eg. by + writing to the filesystem). Such actions are special because they + are disabled by the 'dry_run' flag. This method takes care of all + that bureaucracy for you; all you have to do is supply the + function to call and an argument tuple for it (to embody the + "external action" being performed), and an optional message to + print. + """ + if msg is None: + msg = "%s%r" % (func.__name__, args) + if msg[-2:] == ',)': # correct for singleton tuple + msg = msg[0:-2] + ')' + + log.info(msg) + if not dry_run: + func(*args) + + +def strtobool (val): + """Convert a string representation of truth to true (1) or false (0). + + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + """ + val = val.lower() + if val in ('y', 'yes', 't', 'true', 'on', '1'): + return 1 + elif val in ('n', 'no', 'f', 'false', 'off', '0'): + return 0 + else: + raise ValueError("invalid truth value %r" % (val,)) + + +def byte_compile (py_files, + optimize=0, force=0, + prefix=None, base_dir=None, + verbose=1, dry_run=0, + direct=None): + """Byte-compile a collection of Python source files to .pyc + files in a __pycache__ subdirectory. 'py_files' is a list + of files to compile; any files that don't end in ".py" are silently + skipped. 'optimize' must be one of the following: + 0 - don't optimize + 1 - normal optimization (like "python -O") + 2 - extra optimization (like "python -OO") + If 'force' is true, all files are recompiled regardless of + timestamps. + + The source filename encoded in each bytecode file defaults to the + filenames listed in 'py_files'; you can modify these with 'prefix' and + 'basedir'. 'prefix' is a string that will be stripped off of each + source filename, and 'base_dir' is a directory name that will be + prepended (after 'prefix' is stripped). You can supply either or both + (or neither) of 'prefix' and 'base_dir', as you wish. + + If 'dry_run' is true, doesn't actually do anything that would + affect the filesystem. + + Byte-compilation is either done directly in this interpreter process + with the standard py_compile module, or indirectly by writing a + temporary script and executing it. Normally, you should let + 'byte_compile()' figure out to use direct compilation or not (see + the source for details). The 'direct' flag is used by the script + generated in indirect mode; unless you know what you're doing, leave + it set to None. + """ + + # Late import to fix a bootstrap issue: _posixsubprocess is built by + # setup.py, but setup.py uses distutils. + import subprocess + + # nothing is done if sys.dont_write_bytecode is True + if sys.dont_write_bytecode: + raise DistutilsByteCompileError('byte-compiling is disabled.') + + # First, if the caller didn't force us into direct or indirect mode, + # figure out which mode we should be in. We take a conservative + # approach: choose direct mode *only* if the current interpreter is + # in debug mode and optimize is 0. If we're not in debug mode (-O + # or -OO), we don't know which level of optimization this + # interpreter is running with, so we can't do direct + # byte-compilation and be certain that it's the right thing. Thus, + # always compile indirectly if the current interpreter is in either + # optimize mode, or if either optimization level was requested by + # the caller. + if direct is None: + direct = (__debug__ and optimize == 0) + + # "Indirect" byte-compilation: write a temporary script and then + # run it with the appropriate flags. + if not direct: + try: + from tempfile import mkstemp + (script_fd, script_name) = mkstemp(".py") + except ImportError: + from tempfile import mktemp + (script_fd, script_name) = None, mktemp(".py") + log.info("writing byte-compilation script '%s'", script_name) + if not dry_run: + if script_fd is not None: + script = os.fdopen(script_fd, "w") + else: + script = open(script_name, "w") + + with script: + script.write("""\ +from distutils.util import byte_compile +files = [ +""") + + # XXX would be nice to write absolute filenames, just for + # safety's sake (script should be more robust in the face of + # chdir'ing before running it). But this requires abspath'ing + # 'prefix' as well, and that breaks the hack in build_lib's + # 'byte_compile()' method that carefully tacks on a trailing + # slash (os.sep really) to make sure the prefix here is "just + # right". This whole prefix business is rather delicate -- the + # problem is that it's really a directory, but I'm treating it + # as a dumb string, so trailing slashes and so forth matter. + + #py_files = map(os.path.abspath, py_files) + #if prefix: + # prefix = os.path.abspath(prefix) + + script.write(",\n".join(map(repr, py_files)) + "]\n") + script.write(""" +byte_compile(files, optimize=%r, force=%r, + prefix=%r, base_dir=%r, + verbose=%r, dry_run=0, + direct=1) +""" % (optimize, force, prefix, base_dir, verbose)) + + cmd = [sys.executable] + cmd.extend(subprocess._optim_args_from_interpreter_flags()) + cmd.append(script_name) + spawn(cmd, dry_run=dry_run) + execute(os.remove, (script_name,), "removing %s" % script_name, + dry_run=dry_run) + + # "Direct" byte-compilation: use the py_compile module to compile + # right here, right now. Note that the script generated in indirect + # mode simply calls 'byte_compile()' in direct mode, a weird sort of + # cross-process recursion. Hey, it works! + else: + from py_compile import compile + + for file in py_files: + if file[-3:] != ".py": + # This lets us be lazy and not filter filenames in + # the "install_lib" command. + continue + + # Terminology from the py_compile module: + # cfile - byte-compiled file + # dfile - purported source filename (same as 'file' by default) + if optimize >= 0: + opt = '' if optimize == 0 else optimize + cfile = importlib.util.cache_from_source( + file, optimization=opt) + else: + cfile = importlib.util.cache_from_source(file) + dfile = file + if prefix: + if file[:len(prefix)] != prefix: + raise ValueError("invalid prefix: filename %r doesn't start with %r" + % (file, prefix)) + dfile = dfile[len(prefix):] + if base_dir: + dfile = os.path.join(base_dir, dfile) + + cfile_base = os.path.basename(cfile) + if direct: + if force or newer(file, cfile): + log.info("byte-compiling %s to %s", file, cfile_base) + if not dry_run: + compile(file, cfile, dfile) + else: + log.debug("skipping byte-compilation of %s to %s", + file, cfile_base) + +# byte_compile () + +def rfc822_escape (header): + """Return a version of the string escaped for inclusion in an + RFC-822 header, by ensuring there are 8 spaces space after each newline. + """ + lines = header.split('\n') + sep = '\n' + 8 * ' ' + return sep.join(lines) + +# 2to3 support + +def run_2to3(files, fixer_names=None, options=None, explicit=None): + """Invoke 2to3 on a list of Python files. + The files should all come from the build area, as the + modification is done in-place. To reduce the build time, + only files modified since the last invocation of this + function should be passed in the files argument.""" + + if not files: + return + + # Make this class local, to delay import of 2to3 + from lib2to3.refactor import RefactoringTool, get_fixers_from_package + class DistutilsRefactoringTool(RefactoringTool): + def log_error(self, msg, *args, **kw): + log.error(msg, *args) + + def log_message(self, msg, *args): + log.info(msg, *args) + + def log_debug(self, msg, *args): + log.debug(msg, *args) + + if fixer_names is None: + fixer_names = get_fixers_from_package('lib2to3.fixes') + r = DistutilsRefactoringTool(fixer_names, options=options) + r.refactor(files, write=True) + +def copydir_run_2to3(src, dest, template=None, fixer_names=None, + options=None, explicit=None): + """Recursively copy a directory, only copying new and changed files, + running run_2to3 over all newly copied Python modules afterward. + + If you give a template string, it's parsed like a MANIFEST.in. + """ + from distutils.dir_util import mkpath + from distutils.file_util import copy_file + from distutils.filelist import FileList + filelist = FileList() + curdir = os.getcwd() + os.chdir(src) + try: + filelist.findall() + finally: + os.chdir(curdir) + filelist.files[:] = filelist.allfiles + if template: + for line in template.splitlines(): + line = line.strip() + if not line: continue + filelist.process_template_line(line) + copied = [] + for filename in filelist.files: + outname = os.path.join(dest, filename) + mkpath(os.path.dirname(outname)) + res = copy_file(os.path.join(src, filename), outname, update=1) + if res[1]: copied.append(outname) + run_2to3([fn for fn in copied if fn.lower().endswith('.py')], + fixer_names=fixer_names, options=options, explicit=explicit) + return copied + +class Mixin2to3: + '''Mixin class for commands that run 2to3. + To configure 2to3, setup scripts may either change + the class variables, or inherit from individual commands + to override how 2to3 is invoked.''' + + # provide list of fixers to run; + # defaults to all from lib2to3.fixers + fixer_names = None + + # options dictionary + options = None + + # list of fixers to invoke even though they are marked as explicit + explicit = None + + def run_2to3(self, files): + return run_2to3(files, self.fixer_names, self.options, self.explicit) diff --git a/distutils/version.py b/distutils/version.py new file mode 100644 index 00000000..c33bebae --- /dev/null +++ b/distutils/version.py @@ -0,0 +1,347 @@ +# +# distutils/version.py +# +# Implements multiple version numbering conventions for the +# Python Module Distribution Utilities. +# +# $Id$ +# + +"""Provides classes to represent module version numbers (one class for +each style of version numbering). There are currently two such classes +implemented: StrictVersion and LooseVersion. + +Every version number class implements the following interface: + * the 'parse' method takes a string and parses it to some internal + representation; if the string is an invalid version number, + 'parse' raises a ValueError exception + * the class constructor takes an optional string argument which, + if supplied, is passed to 'parse' + * __str__ reconstructs the string that was passed to 'parse' (or + an equivalent string -- ie. one that will generate an equivalent + version number instance) + * __repr__ generates Python code to recreate the version number instance + * _cmp compares the current instance with either another instance + of the same class or a string (which will be parsed to an instance + of the same class, thus must follow the same rules) +""" + +import re + +class Version: + """Abstract base class for version numbering classes. Just provides + constructor (__init__) and reproducer (__repr__), because those + seem to be the same for all version numbering classes; and route + rich comparisons to _cmp. + """ + + def __init__ (self, vstring=None): + if vstring: + self.parse(vstring) + + def __repr__ (self): + return "%s ('%s')" % (self.__class__.__name__, str(self)) + + def __eq__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c == 0 + + def __lt__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c < 0 + + def __le__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c <= 0 + + def __gt__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c > 0 + + def __ge__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c >= 0 + + +# Interface for version-number classes -- must be implemented +# by the following classes (the concrete ones -- Version should +# be treated as an abstract class). +# __init__ (string) - create and take same action as 'parse' +# (string parameter is optional) +# parse (string) - convert a string representation to whatever +# internal representation is appropriate for +# this style of version numbering +# __str__ (self) - convert back to a string; should be very similar +# (if not identical to) the string supplied to parse +# __repr__ (self) - generate Python code to recreate +# the instance +# _cmp (self, other) - compare two version numbers ('other' may +# be an unparsed version string, or another +# instance of your version class) + + +class StrictVersion (Version): + + """Version numbering for anal retentives and software idealists. + Implements the standard interface for version number classes as + described above. A version number consists of two or three + dot-separated numeric components, with an optional "pre-release" tag + on the end. The pre-release tag consists of the letter 'a' or 'b' + followed by a number. If the numeric components of two version + numbers are equal, then one with a pre-release tag will always + be deemed earlier (lesser) than one without. + + The following are valid version numbers (shown in the order that + would be obtained by sorting according to the supplied cmp function): + + 0.4 0.4.0 (these two are equivalent) + 0.4.1 + 0.5a1 + 0.5b3 + 0.5 + 0.9.6 + 1.0 + 1.0.4a3 + 1.0.4b1 + 1.0.4 + + The following are examples of invalid version numbers: + + 1 + 2.7.2.2 + 1.3.a4 + 1.3pl1 + 1.3c4 + + The rationale for this version numbering system will be explained + in the distutils documentation. + """ + + version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', + re.VERBOSE | re.ASCII) + + + def parse (self, vstring): + match = self.version_re.match(vstring) + if not match: + raise ValueError("invalid version number '%s'" % vstring) + + (major, minor, patch, prerelease, prerelease_num) = \ + match.group(1, 2, 4, 5, 6) + + if patch: + self.version = tuple(map(int, [major, minor, patch])) + else: + self.version = tuple(map(int, [major, minor])) + (0,) + + if prerelease: + self.prerelease = (prerelease[0], int(prerelease_num)) + else: + self.prerelease = None + + + def __str__ (self): + + if self.version[2] == 0: + vstring = '.'.join(map(str, self.version[0:2])) + else: + vstring = '.'.join(map(str, self.version)) + + if self.prerelease: + vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) + + return vstring + + + def _cmp (self, other): + if isinstance(other, str): + other = StrictVersion(other) + elif not isinstance(other, StrictVersion): + return NotImplemented + + if self.version != other.version: + # numeric versions don't match + # prerelease stuff doesn't matter + if self.version < other.version: + return -1 + else: + return 1 + + # have to compare prerelease + # case 1: neither has prerelease; they're equal + # case 2: self has prerelease, other doesn't; other is greater + # case 3: self doesn't have prerelease, other does: self is greater + # case 4: both have prerelease: must compare them! + + if (not self.prerelease and not other.prerelease): + return 0 + elif (self.prerelease and not other.prerelease): + return -1 + elif (not self.prerelease and other.prerelease): + return 1 + elif (self.prerelease and other.prerelease): + if self.prerelease == other.prerelease: + return 0 + elif self.prerelease < other.prerelease: + return -1 + else: + return 1 + else: + assert False, "never get here" + +# end class StrictVersion + + +# The rules according to Greg Stein: +# 1) a version number has 1 or more numbers separated by a period or by +# sequences of letters. If only periods, then these are compared +# left-to-right to determine an ordering. +# 2) sequences of letters are part of the tuple for comparison and are +# compared lexicographically +# 3) recognize the numeric components may have leading zeroes +# +# The LooseVersion class below implements these rules: a version number +# string is split up into a tuple of integer and string components, and +# comparison is a simple tuple comparison. This means that version +# numbers behave in a predictable and obvious way, but a way that might +# not necessarily be how people *want* version numbers to behave. There +# wouldn't be a problem if people could stick to purely numeric version +# numbers: just split on period and compare the numbers as tuples. +# However, people insist on putting letters into their version numbers; +# the most common purpose seems to be: +# - indicating a "pre-release" version +# ('alpha', 'beta', 'a', 'b', 'pre', 'p') +# - indicating a post-release patch ('p', 'pl', 'patch') +# but of course this can't cover all version number schemes, and there's +# no way to know what a programmer means without asking him. +# +# The problem is what to do with letters (and other non-numeric +# characters) in a version number. The current implementation does the +# obvious and predictable thing: keep them as strings and compare +# lexically within a tuple comparison. This has the desired effect if +# an appended letter sequence implies something "post-release": +# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002". +# +# However, if letters in a version number imply a pre-release version, +# the "obvious" thing isn't correct. Eg. you would expect that +# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison +# implemented here, this just isn't so. +# +# Two possible solutions come to mind. The first is to tie the +# comparison algorithm to a particular set of semantic rules, as has +# been done in the StrictVersion class above. This works great as long +# as everyone can go along with bondage and discipline. Hopefully a +# (large) subset of Python module programmers will agree that the +# particular flavour of bondage and discipline provided by StrictVersion +# provides enough benefit to be worth using, and will submit their +# version numbering scheme to its domination. The free-thinking +# anarchists in the lot will never give in, though, and something needs +# to be done to accommodate them. +# +# Perhaps a "moderately strict" version class could be implemented that +# lets almost anything slide (syntactically), and makes some heuristic +# assumptions about non-digits in version number strings. This could +# sink into special-case-hell, though; if I was as talented and +# idiosyncratic as Larry Wall, I'd go ahead and implement a class that +# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is +# just as happy dealing with things like "2g6" and "1.13++". I don't +# think I'm smart enough to do it right though. +# +# In any case, I've coded the test suite for this module (see +# ../test/test_version.py) specifically to fail on things like comparing +# "1.2a2" and "1.2". That's not because the *code* is doing anything +# wrong, it's because the simple, obvious design doesn't match my +# complicated, hairy expectations for real-world version numbers. It +# would be a snap to fix the test suite to say, "Yep, LooseVersion does +# the Right Thing" (ie. the code matches the conception). But I'd rather +# have a conception that matches common notions about version numbers. + +class LooseVersion (Version): + + """Version numbering for anarchists and software realists. + Implements the standard interface for version number classes as + described above. A version number consists of a series of numbers, + separated by either periods or strings of letters. When comparing + version numbers, the numeric components will be compared + numerically, and the alphabetic components lexically. The following + are all valid version numbers, in no particular order: + + 1.5.1 + 1.5.2b2 + 161 + 3.10a + 8.02 + 3.4j + 1996.07.12 + 3.2.pl0 + 3.1.1.6 + 2g6 + 11g + 0.960923 + 2.2beta29 + 1.13++ + 5.5.kw + 2.0b1pl0 + + In fact, there is no such thing as an invalid version number under + this scheme; the rules for comparison are simple and predictable, + but may not always give the results you want (for some definition + of "want"). + """ + + component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) + + def __init__ (self, vstring=None): + if vstring: + self.parse(vstring) + + + def parse (self, vstring): + # I've given up on thinking I can reconstruct the version string + # from the parsed tuple -- so I just store the string here for + # use by __str__ + self.vstring = vstring + components = [x for x in self.component_re.split(vstring) + if x and x != '.'] + for i, obj in enumerate(components): + try: + components[i] = int(obj) + except ValueError: + pass + + self.version = components + + + def __str__ (self): + return self.vstring + + + def __repr__ (self): + return "LooseVersion ('%s')" % str(self) + + + def _cmp (self, other): + if isinstance(other, str): + other = LooseVersion(other) + elif not isinstance(other, LooseVersion): + return NotImplemented + + if self.version == other.version: + return 0 + if self.version < other.version: + return -1 + if self.version > other.version: + return 1 + + +# end class LooseVersion diff --git a/distutils/versionpredicate.py b/distutils/versionpredicate.py new file mode 100644 index 00000000..062c98f2 --- /dev/null +++ b/distutils/versionpredicate.py @@ -0,0 +1,166 @@ +"""Module for parsing and testing package version predicate strings. +""" +import re +import distutils.version +import operator + + +re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)", + re.ASCII) +# (package) (rest) + +re_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses +re_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") +# (comp) (version) + + +def splitUp(pred): + """Parse a single version comparison. + + Return (comparison string, StrictVersion) + """ + res = re_splitComparison.match(pred) + if not res: + raise ValueError("bad package restriction syntax: %r" % pred) + comp, verStr = res.groups() + return (comp, distutils.version.StrictVersion(verStr)) + +compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq, + ">": operator.gt, ">=": operator.ge, "!=": operator.ne} + +class VersionPredicate: + """Parse and test package version predicates. + + >>> v = VersionPredicate('pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)') + + The `name` attribute provides the full dotted name that is given:: + + >>> v.name + 'pyepat.abc' + + The str() of a `VersionPredicate` provides a normalized + human-readable version of the expression:: + + >>> print(v) + pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) + + The `satisfied_by()` method can be used to determine with a given + version number is included in the set described by the version + restrictions:: + + >>> v.satisfied_by('1.1') + True + >>> v.satisfied_by('1.4') + True + >>> v.satisfied_by('1.0') + False + >>> v.satisfied_by('4444.4') + False + >>> v.satisfied_by('1555.1b3') + False + + `VersionPredicate` is flexible in accepting extra whitespace:: + + >>> v = VersionPredicate(' pat( == 0.1 ) ') + >>> v.name + 'pat' + >>> v.satisfied_by('0.1') + True + >>> v.satisfied_by('0.2') + False + + If any version numbers passed in do not conform to the + restrictions of `StrictVersion`, a `ValueError` is raised:: + + >>> v = VersionPredicate('p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)') + Traceback (most recent call last): + ... + ValueError: invalid version number '1.2zb3' + + It the module or package name given does not conform to what's + allowed as a legal module or package name, `ValueError` is + raised:: + + >>> v = VersionPredicate('foo-bar') + Traceback (most recent call last): + ... + ValueError: expected parenthesized list: '-bar' + + >>> v = VersionPredicate('foo bar (12.21)') + Traceback (most recent call last): + ... + ValueError: expected parenthesized list: 'bar (12.21)' + + """ + + def __init__(self, versionPredicateStr): + """Parse a version predicate string. + """ + # Fields: + # name: package name + # pred: list of (comparison string, StrictVersion) + + versionPredicateStr = versionPredicateStr.strip() + if not versionPredicateStr: + raise ValueError("empty package restriction") + match = re_validPackage.match(versionPredicateStr) + if not match: + raise ValueError("bad package name in %r" % versionPredicateStr) + self.name, paren = match.groups() + paren = paren.strip() + if paren: + match = re_paren.match(paren) + if not match: + raise ValueError("expected parenthesized list: %r" % paren) + str = match.groups()[0] + self.pred = [splitUp(aPred) for aPred in str.split(",")] + if not self.pred: + raise ValueError("empty parenthesized list in %r" + % versionPredicateStr) + else: + self.pred = [] + + def __str__(self): + if self.pred: + seq = [cond + " " + str(ver) for cond, ver in self.pred] + return self.name + " (" + ", ".join(seq) + ")" + else: + return self.name + + def satisfied_by(self, version): + """True if version is compatible with all the predicates in self. + The parameter version must be acceptable to the StrictVersion + constructor. It may be either a string or StrictVersion. + """ + for cond, ver in self.pred: + if not compmap[cond](version, ver): + return False + return True + + +_provision_rx = None + +def split_provision(value): + """Return the name and optional version number of a provision. + + The version number, if given, will be returned as a `StrictVersion` + instance, otherwise it will be `None`. + + >>> split_provision('mypkg') + ('mypkg', None) + >>> split_provision(' mypkg( 1.2 ) ') + ('mypkg', StrictVersion ('1.2')) + """ + global _provision_rx + if _provision_rx is None: + _provision_rx = re.compile( + r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", + re.ASCII) + value = value.strip() + m = _provision_rx.match(value) + if not m: + raise ValueError("illegal provides specification: %r" % value) + ver = m.group(2) or None + if ver: + ver = distutils.version.StrictVersion(ver) + return m.group(1), ver diff --git a/errors.py b/errors.py deleted file mode 100644 index 8b93059e..00000000 --- a/errors.py +++ /dev/null @@ -1,97 +0,0 @@ -"""distutils.errors - -Provides exceptions used by the Distutils modules. Note that Distutils -modules may raise standard exceptions; in particular, SystemExit is -usually raised for errors that are obviously the end-user's fault -(eg. bad command-line arguments). - -This module is safe to use in "from ... import *" mode; it only exports -symbols whose names start with "Distutils" and end with "Error".""" - -class DistutilsError (Exception): - """The root of all Distutils evil.""" - pass - -class DistutilsModuleError (DistutilsError): - """Unable to load an expected module, or to find an expected class - within some module (in particular, command modules and classes).""" - pass - -class DistutilsClassError (DistutilsError): - """Some command class (or possibly distribution class, if anyone - feels a need to subclass Distribution) is found not to be holding - up its end of the bargain, ie. implementing some part of the - "command "interface.""" - pass - -class DistutilsGetoptError (DistutilsError): - """The option table provided to 'fancy_getopt()' is bogus.""" - pass - -class DistutilsArgError (DistutilsError): - """Raised by fancy_getopt in response to getopt.error -- ie. an - error in the command line usage.""" - pass - -class DistutilsFileError (DistutilsError): - """Any problems in the filesystem: expected file not found, etc. - Typically this is for problems that we detect before OSError - could be raised.""" - pass - -class DistutilsOptionError (DistutilsError): - """Syntactic/semantic errors in command options, such as use of - mutually conflicting options, or inconsistent options, - badly-spelled values, etc. No distinction is made between option - values originating in the setup script, the command line, config - files, or what-have-you -- but if we *know* something originated in - the setup script, we'll raise DistutilsSetupError instead.""" - pass - -class DistutilsSetupError (DistutilsError): - """For errors that can be definitely blamed on the setup script, - such as invalid keyword arguments to 'setup()'.""" - pass - -class DistutilsPlatformError (DistutilsError): - """We don't know how to do something on the current platform (but - we do know how to do it on some platform) -- eg. trying to compile - C files on a platform not supported by a CCompiler subclass.""" - pass - -class DistutilsExecError (DistutilsError): - """Any problems executing an external program (such as the C - compiler, when compiling C files).""" - pass - -class DistutilsInternalError (DistutilsError): - """Internal inconsistencies or impossibilities (obviously, this - should never be seen if the code is working!).""" - pass - -class DistutilsTemplateError (DistutilsError): - """Syntax error in a file list template.""" - -class DistutilsByteCompileError(DistutilsError): - """Byte compile error.""" - -# Exception classes used by the CCompiler implementation classes -class CCompilerError (Exception): - """Some compile/link operation failed.""" - -class PreprocessError (CCompilerError): - """Failure to preprocess one or more C/C++ files.""" - -class CompileError (CCompilerError): - """Failure to compile one or more C/C++ source files.""" - -class LibError (CCompilerError): - """Failure to create a static library from one or more C/C++ object - files.""" - -class LinkError (CCompilerError): - """Failure to link one or more C/C++ object files into an executable - or shared library file.""" - -class UnknownFileError (CCompilerError): - """Attempt to process an unknown file type.""" diff --git a/extension.py b/extension.py deleted file mode 100644 index c507da36..00000000 --- a/extension.py +++ /dev/null @@ -1,240 +0,0 @@ -"""distutils.extension - -Provides the Extension class, used to describe C/C++ extension -modules in setup scripts.""" - -import os -import warnings - -# This class is really only used by the "build_ext" command, so it might -# make sense to put it in distutils.command.build_ext. However, that -# module is already big enough, and I want to make this class a bit more -# complex to simplify some common cases ("foo" module in "foo.c") and do -# better error-checking ("foo.c" actually exists). -# -# Also, putting this in build_ext.py means every setup script would have to -# import that large-ish module (indirectly, through distutils.core) in -# order to do anything. - -class Extension: - """Just a collection of attributes that describes an extension - module and everything needed to build it (hopefully in a portable - way, but there are hooks that let you be as unportable as you need). - - Instance attributes: - name : string - the full name of the extension, including any packages -- ie. - *not* a filename or pathname, but Python dotted name - sources : [string] - list of source filenames, relative to the distribution root - (where the setup script lives), in Unix form (slash-separated) - for portability. Source files may be C, C++, SWIG (.i), - platform-specific resource files, or whatever else is recognized - by the "build_ext" command as source for a Python extension. - include_dirs : [string] - list of directories to search for C/C++ header files (in Unix - form for portability) - define_macros : [(name : string, value : string|None)] - list of macros to define; each macro is defined using a 2-tuple, - where 'value' is either the string to define it to or None to - define it without a particular value (equivalent of "#define - FOO" in source or -DFOO on Unix C compiler command line) - undef_macros : [string] - list of macros to undefine explicitly - library_dirs : [string] - list of directories to search for C/C++ libraries at link time - libraries : [string] - list of library names (not filenames or paths) to link against - runtime_library_dirs : [string] - list of directories to search for C/C++ libraries at run time - (for shared extensions, this is when the extension is loaded) - extra_objects : [string] - list of extra files to link with (eg. object files not implied - by 'sources', static library that must be explicitly specified, - binary resource files, etc.) - extra_compile_args : [string] - any extra platform- and compiler-specific information to use - when compiling the source files in 'sources'. For platforms and - compilers where "command line" makes sense, this is typically a - list of command-line arguments, but for other platforms it could - be anything. - extra_link_args : [string] - any extra platform- and compiler-specific information to use - when linking object files together to create the extension (or - to create a new static Python interpreter). Similar - interpretation as for 'extra_compile_args'. - export_symbols : [string] - list of symbols to be exported from a shared extension. Not - used on all platforms, and not generally necessary for Python - extensions, which typically export exactly one symbol: "init" + - extension_name. - swig_opts : [string] - any extra options to pass to SWIG if a source file has the .i - extension. - depends : [string] - list of files that the extension depends on - language : string - extension language (i.e. "c", "c++", "objc"). Will be detected - from the source extensions if not provided. - optional : boolean - specifies that a build failure in the extension should not abort the - build process, but simply not install the failing extension. - """ - - # When adding arguments to this constructor, be sure to update - # setup_keywords in core.py. - def __init__(self, name, sources, - include_dirs=None, - define_macros=None, - undef_macros=None, - library_dirs=None, - libraries=None, - runtime_library_dirs=None, - extra_objects=None, - extra_compile_args=None, - extra_link_args=None, - export_symbols=None, - swig_opts = None, - depends=None, - language=None, - optional=None, - **kw # To catch unknown keywords - ): - if not isinstance(name, str): - raise AssertionError("'name' must be a string") - if not (isinstance(sources, list) and - all(isinstance(v, str) for v in sources)): - raise AssertionError("'sources' must be a list of strings") - - self.name = name - self.sources = sources - self.include_dirs = include_dirs or [] - self.define_macros = define_macros or [] - self.undef_macros = undef_macros or [] - self.library_dirs = library_dirs or [] - self.libraries = libraries or [] - self.runtime_library_dirs = runtime_library_dirs or [] - self.extra_objects = extra_objects or [] - self.extra_compile_args = extra_compile_args or [] - self.extra_link_args = extra_link_args or [] - self.export_symbols = export_symbols or [] - self.swig_opts = swig_opts or [] - self.depends = depends or [] - self.language = language - self.optional = optional - - # If there are unknown keyword options, warn about them - if len(kw) > 0: - options = [repr(option) for option in kw] - options = ', '.join(sorted(options)) - msg = "Unknown Extension options: %s" % options - warnings.warn(msg) - - def __repr__(self): - return '<%s.%s(%r) at %#x>' % ( - self.__class__.__module__, - self.__class__.__qualname__, - self.name, - id(self)) - - -def read_setup_file(filename): - """Reads a Setup file and returns Extension instances.""" - from distutils.sysconfig import (parse_makefile, expand_makefile_vars, - _variable_rx) - - from distutils.text_file import TextFile - from distutils.util import split_quoted - - # First pass over the file to gather "VAR = VALUE" assignments. - vars = parse_makefile(filename) - - # Second pass to gobble up the real content: lines of the form - # ... [ ...] [ ...] [ ...] - file = TextFile(filename, - strip_comments=1, skip_blanks=1, join_lines=1, - lstrip_ws=1, rstrip_ws=1) - try: - extensions = [] - - while True: - line = file.readline() - if line is None: # eof - break - if _variable_rx.match(line): # VAR=VALUE, handled in first pass - continue - - if line[0] == line[-1] == "*": - file.warn("'%s' lines not handled yet" % line) - continue - - line = expand_makefile_vars(line, vars) - words = split_quoted(line) - - # NB. this parses a slightly different syntax than the old - # makesetup script: here, there must be exactly one extension per - # line, and it must be the first word of the line. I have no idea - # why the old syntax supported multiple extensions per line, as - # they all wind up being the same. - - module = words[0] - ext = Extension(module, []) - append_next_word = None - - for word in words[1:]: - if append_next_word is not None: - append_next_word.append(word) - append_next_word = None - continue - - suffix = os.path.splitext(word)[1] - switch = word[0:2] ; value = word[2:] - - if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): - # hmm, should we do something about C vs. C++ sources? - # or leave it up to the CCompiler implementation to - # worry about? - ext.sources.append(word) - elif switch == "-I": - ext.include_dirs.append(value) - elif switch == "-D": - equals = value.find("=") - if equals == -1: # bare "-DFOO" -- no value - ext.define_macros.append((value, None)) - else: # "-DFOO=blah" - ext.define_macros.append((value[0:equals], - value[equals+2:])) - elif switch == "-U": - ext.undef_macros.append(value) - elif switch == "-C": # only here 'cause makesetup has it! - ext.extra_compile_args.append(word) - elif switch == "-l": - ext.libraries.append(value) - elif switch == "-L": - ext.library_dirs.append(value) - elif switch == "-R": - ext.runtime_library_dirs.append(value) - elif word == "-rpath": - append_next_word = ext.runtime_library_dirs - elif word == "-Xlinker": - append_next_word = ext.extra_link_args - elif word == "-Xcompiler": - append_next_word = ext.extra_compile_args - elif switch == "-u": - ext.extra_link_args.append(word) - if not value: - append_next_word = ext.extra_link_args - elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): - # NB. a really faithful emulation of makesetup would - # append a .o file to extra_objects only if it - # had a slash in it; otherwise, it would s/.o/.c/ - # and append it to sources. Hmmmm. - ext.extra_objects.append(word) - else: - file.warn("unrecognized argument '%s'" % word) - - extensions.append(ext) - finally: - file.close() - - return extensions diff --git a/fancy_getopt.py b/fancy_getopt.py deleted file mode 100644 index 7d170dd2..00000000 --- a/fancy_getopt.py +++ /dev/null @@ -1,457 +0,0 @@ -"""distutils.fancy_getopt - -Wrapper around the standard getopt module that provides the following -additional features: - * short and long options are tied together - * options have help strings, so fancy_getopt could potentially - create a complete usage summary - * options set attributes of a passed-in object -""" - -import sys, string, re -import getopt -from distutils.errors import * - -# Much like command_re in distutils.core, this is close to but not quite -# the same as a Python NAME -- except, in the spirit of most GNU -# utilities, we use '-' in place of '_'. (The spirit of LISP lives on!) -# The similarities to NAME are again not a coincidence... -longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)' -longopt_re = re.compile(r'^%s$' % longopt_pat) - -# For recognizing "negative alias" options, eg. "quiet=!verbose" -neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) - -# This is used to translate long options to legitimate Python identifiers -# (for use as attributes of some object). -longopt_xlate = str.maketrans('-', '_') - -class FancyGetopt: - """Wrapper around the standard 'getopt()' module that provides some - handy extra functionality: - * short and long options are tied together - * options have help strings, and help text can be assembled - from them - * options set attributes of a passed-in object - * boolean options can have "negative aliases" -- eg. if - --quiet is the "negative alias" of --verbose, then "--quiet" - on the command line sets 'verbose' to false - """ - - def __init__(self, option_table=None): - # The option table is (currently) a list of tuples. The - # tuples may have 3 or four values: - # (long_option, short_option, help_string [, repeatable]) - # if an option takes an argument, its long_option should have '=' - # appended; short_option should just be a single character, no ':' - # in any case. If a long_option doesn't have a corresponding - # short_option, short_option should be None. All option tuples - # must have long options. - self.option_table = option_table - - # 'option_index' maps long option names to entries in the option - # table (ie. those 3-tuples). - self.option_index = {} - if self.option_table: - self._build_index() - - # 'alias' records (duh) alias options; {'foo': 'bar'} means - # --foo is an alias for --bar - self.alias = {} - - # 'negative_alias' keeps track of options that are the boolean - # opposite of some other option - self.negative_alias = {} - - # These keep track of the information in the option table. We - # don't actually populate these structures until we're ready to - # parse the command-line, since the 'option_table' passed in here - # isn't necessarily the final word. - self.short_opts = [] - self.long_opts = [] - self.short2long = {} - self.attr_name = {} - self.takes_arg = {} - - # And 'option_order' is filled up in 'getopt()'; it records the - # original order of options (and their values) on the command-line, - # but expands short options, converts aliases, etc. - self.option_order = [] - - def _build_index(self): - self.option_index.clear() - for option in self.option_table: - self.option_index[option[0]] = option - - def set_option_table(self, option_table): - self.option_table = option_table - self._build_index() - - def add_option(self, long_option, short_option=None, help_string=None): - if long_option in self.option_index: - raise DistutilsGetoptError( - "option conflict: already an option '%s'" % long_option) - else: - option = (long_option, short_option, help_string) - self.option_table.append(option) - self.option_index[long_option] = option - - def has_option(self, long_option): - """Return true if the option table for this parser has an - option with long name 'long_option'.""" - return long_option in self.option_index - - def get_attr_name(self, long_option): - """Translate long option name 'long_option' to the form it - has as an attribute of some object: ie., translate hyphens - to underscores.""" - return long_option.translate(longopt_xlate) - - def _check_alias_dict(self, aliases, what): - assert isinstance(aliases, dict) - for (alias, opt) in aliases.items(): - if alias not in self.option_index: - raise DistutilsGetoptError(("invalid %s '%s': " - "option '%s' not defined") % (what, alias, alias)) - if opt not in self.option_index: - raise DistutilsGetoptError(("invalid %s '%s': " - "aliased option '%s' not defined") % (what, alias, opt)) - - def set_aliases(self, alias): - """Set the aliases for this option parser.""" - self._check_alias_dict(alias, "alias") - self.alias = alias - - def set_negative_aliases(self, negative_alias): - """Set the negative aliases for this option parser. - 'negative_alias' should be a dictionary mapping option names to - option names, both the key and value must already be defined - in the option table.""" - self._check_alias_dict(negative_alias, "negative alias") - self.negative_alias = negative_alias - - def _grok_option_table(self): - """Populate the various data structures that keep tabs on the - option table. Called by 'getopt()' before it can do anything - worthwhile. - """ - self.long_opts = [] - self.short_opts = [] - self.short2long.clear() - self.repeat = {} - - for option in self.option_table: - if len(option) == 3: - long, short, help = option - repeat = 0 - elif len(option) == 4: - long, short, help, repeat = option - else: - # the option table is part of the code, so simply - # assert that it is correct - raise ValueError("invalid option tuple: %r" % (option,)) - - # Type- and value-check the option names - if not isinstance(long, str) or len(long) < 2: - raise DistutilsGetoptError(("invalid long option '%s': " - "must be a string of length >= 2") % long) - - if (not ((short is None) or - (isinstance(short, str) and len(short) == 1))): - raise DistutilsGetoptError("invalid short option '%s': " - "must a single character or None" % short) - - self.repeat[long] = repeat - self.long_opts.append(long) - - if long[-1] == '=': # option takes an argument? - if short: short = short + ':' - long = long[0:-1] - self.takes_arg[long] = 1 - else: - # Is option is a "negative alias" for some other option (eg. - # "quiet" == "!verbose")? - alias_to = self.negative_alias.get(long) - if alias_to is not None: - if self.takes_arg[alias_to]: - raise DistutilsGetoptError( - "invalid negative alias '%s': " - "aliased option '%s' takes a value" - % (long, alias_to)) - - self.long_opts[-1] = long # XXX redundant?! - self.takes_arg[long] = 0 - - # If this is an alias option, make sure its "takes arg" flag is - # the same as the option it's aliased to. - alias_to = self.alias.get(long) - if alias_to is not None: - if self.takes_arg[long] != self.takes_arg[alias_to]: - raise DistutilsGetoptError( - "invalid alias '%s': inconsistent with " - "aliased option '%s' (one of them takes a value, " - "the other doesn't" - % (long, alias_to)) - - # Now enforce some bondage on the long option name, so we can - # later translate it to an attribute name on some object. Have - # to do this a bit late to make sure we've removed any trailing - # '='. - if not longopt_re.match(long): - raise DistutilsGetoptError( - "invalid long option name '%s' " - "(must be letters, numbers, hyphens only" % long) - - self.attr_name[long] = self.get_attr_name(long) - if short: - self.short_opts.append(short) - self.short2long[short[0]] = long - - def getopt(self, args=None, object=None): - """Parse command-line options in args. Store as attributes on object. - - If 'args' is None or not supplied, uses 'sys.argv[1:]'. If - 'object' is None or not supplied, creates a new OptionDummy - object, stores option values there, and returns a tuple (args, - object). If 'object' is supplied, it is modified in place and - 'getopt()' just returns 'args'; in both cases, the returned - 'args' is a modified copy of the passed-in 'args' list, which - is left untouched. - """ - if args is None: - args = sys.argv[1:] - if object is None: - object = OptionDummy() - created_object = True - else: - created_object = False - - self._grok_option_table() - - short_opts = ' '.join(self.short_opts) - try: - opts, args = getopt.getopt(args, short_opts, self.long_opts) - except getopt.error as msg: - raise DistutilsArgError(msg) - - for opt, val in opts: - if len(opt) == 2 and opt[0] == '-': # it's a short option - opt = self.short2long[opt[1]] - else: - assert len(opt) > 2 and opt[:2] == '--' - opt = opt[2:] - - alias = self.alias.get(opt) - if alias: - opt = alias - - if not self.takes_arg[opt]: # boolean option? - assert val == '', "boolean option can't have value" - alias = self.negative_alias.get(opt) - if alias: - opt = alias - val = 0 - else: - val = 1 - - attr = self.attr_name[opt] - # The only repeating option at the moment is 'verbose'. - # It has a negative option -q quiet, which should set verbose = 0. - if val and self.repeat.get(attr) is not None: - val = getattr(object, attr, 0) + 1 - setattr(object, attr, val) - self.option_order.append((opt, val)) - - # for opts - if created_object: - return args, object - else: - return args - - def get_option_order(self): - """Returns the list of (option, value) tuples processed by the - previous run of 'getopt()'. Raises RuntimeError if - 'getopt()' hasn't been called yet. - """ - if self.option_order is None: - raise RuntimeError("'getopt()' hasn't been called yet") - else: - return self.option_order - - def generate_help(self, header=None): - """Generate help text (a list of strings, one per suggested line of - output) from the option table for this FancyGetopt object. - """ - # Blithely assume the option table is good: probably wouldn't call - # 'generate_help()' unless you've already called 'getopt()'. - - # First pass: determine maximum length of long option names - max_opt = 0 - for option in self.option_table: - long = option[0] - short = option[1] - l = len(long) - if long[-1] == '=': - l = l - 1 - if short is not None: - l = l + 5 # " (-x)" where short == 'x' - if l > max_opt: - max_opt = l - - opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter - - # Typical help block looks like this: - # --foo controls foonabulation - # Help block for longest option looks like this: - # --flimflam set the flim-flam level - # and with wrapped text: - # --flimflam set the flim-flam level (must be between - # 0 and 100, except on Tuesdays) - # Options with short names will have the short name shown (but - # it doesn't contribute to max_opt): - # --foo (-f) controls foonabulation - # If adding the short option would make the left column too wide, - # we push the explanation off to the next line - # --flimflam (-l) - # set the flim-flam level - # Important parameters: - # - 2 spaces before option block start lines - # - 2 dashes for each long option name - # - min. 2 spaces between option and explanation (gutter) - # - 5 characters (incl. space) for short option name - - # Now generate lines of help text. (If 80 columns were good enough - # for Jesus, then 78 columns are good enough for me!) - line_width = 78 - text_width = line_width - opt_width - big_indent = ' ' * opt_width - if header: - lines = [header] - else: - lines = ['Option summary:'] - - for option in self.option_table: - long, short, help = option[:3] - text = wrap_text(help, text_width) - if long[-1] == '=': - long = long[0:-1] - - # Case 1: no short option at all (makes life easy) - if short is None: - if text: - lines.append(" --%-*s %s" % (max_opt, long, text[0])) - else: - lines.append(" --%-*s " % (max_opt, long)) - - # Case 2: we have a short option, so we have to include it - # just after the long option - else: - opt_names = "%s (-%s)" % (long, short) - if text: - lines.append(" --%-*s %s" % - (max_opt, opt_names, text[0])) - else: - lines.append(" --%-*s" % opt_names) - - for l in text[1:]: - lines.append(big_indent + l) - return lines - - def print_help(self, header=None, file=None): - if file is None: - file = sys.stdout - for line in self.generate_help(header): - file.write(line + "\n") - - -def fancy_getopt(options, negative_opt, object, args): - parser = FancyGetopt(options) - parser.set_negative_aliases(negative_opt) - return parser.getopt(args, object) - - -WS_TRANS = {ord(_wschar) : ' ' for _wschar in string.whitespace} - -def wrap_text(text, width): - """wrap_text(text : string, width : int) -> [string] - - Split 'text' into multiple lines of no more than 'width' characters - each, and return the list of strings that results. - """ - if text is None: - return [] - if len(text) <= width: - return [text] - - text = text.expandtabs() - text = text.translate(WS_TRANS) - chunks = re.split(r'( +|-+)', text) - chunks = [ch for ch in chunks if ch] # ' - ' results in empty strings - lines = [] - - while chunks: - cur_line = [] # list of chunks (to-be-joined) - cur_len = 0 # length of current line - - while chunks: - l = len(chunks[0]) - if cur_len + l <= width: # can squeeze (at least) this chunk in - cur_line.append(chunks[0]) - del chunks[0] - cur_len = cur_len + l - else: # this line is full - # drop last chunk if all space - if cur_line and cur_line[-1][0] == ' ': - del cur_line[-1] - break - - if chunks: # any chunks left to process? - # if the current line is still empty, then we had a single - # chunk that's too big too fit on a line -- so we break - # down and break it up at the line width - if cur_len == 0: - cur_line.append(chunks[0][0:width]) - chunks[0] = chunks[0][width:] - - # all-whitespace chunks at the end of a line can be discarded - # (and we know from the re.split above that if a chunk has - # *any* whitespace, it is *all* whitespace) - if chunks[0][0] == ' ': - del chunks[0] - - # and store this line in the list-of-all-lines -- as a single - # string, of course! - lines.append(''.join(cur_line)) - - return lines - - -def translate_longopt(opt): - """Convert a long option name to a valid Python identifier by - changing "-" to "_". - """ - return opt.translate(longopt_xlate) - - -class OptionDummy: - """Dummy class just used as a place to hold command-line option - values as instance attributes.""" - - def __init__(self, options=[]): - """Create a new OptionDummy instance. The attributes listed in - 'options' will be initialized to None.""" - for opt in options: - setattr(self, opt, None) - - -if __name__ == "__main__": - text = """\ -Tra-la-la, supercalifragilisticexpialidocious. -How *do* you spell that odd word, anyways? -(Someone ask Mary -- she'll know [or she'll -say, "How should I know?"].)""" - - for w in (10, 20, 30, 40): - print("width: %d" % w) - print("\n".join(wrap_text(text, w))) - print() diff --git a/file_util.py b/file_util.py deleted file mode 100644 index b3fee35a..00000000 --- a/file_util.py +++ /dev/null @@ -1,238 +0,0 @@ -"""distutils.file_util - -Utility functions for operating on single files. -""" - -import os -from distutils.errors import DistutilsFileError -from distutils import log - -# for generating verbose output in 'copy_file()' -_copy_action = { None: 'copying', - 'hard': 'hard linking', - 'sym': 'symbolically linking' } - - -def _copy_file_contents(src, dst, buffer_size=16*1024): - """Copy the file 'src' to 'dst'; both must be filenames. Any error - opening either file, reading from 'src', or writing to 'dst', raises - DistutilsFileError. Data is read/written in chunks of 'buffer_size' - bytes (default 16k). No attempt is made to handle anything apart from - regular files. - """ - # Stolen from shutil module in the standard library, but with - # custom error-handling added. - fsrc = None - fdst = None - try: - try: - fsrc = open(src, 'rb') - except OSError as e: - raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror)) - - if os.path.exists(dst): - try: - os.unlink(dst) - except OSError as e: - raise DistutilsFileError( - "could not delete '%s': %s" % (dst, e.strerror)) - - try: - fdst = open(dst, 'wb') - except OSError as e: - raise DistutilsFileError( - "could not create '%s': %s" % (dst, e.strerror)) - - while True: - try: - buf = fsrc.read(buffer_size) - except OSError as e: - raise DistutilsFileError( - "could not read from '%s': %s" % (src, e.strerror)) - - if not buf: - break - - try: - fdst.write(buf) - except OSError as e: - raise DistutilsFileError( - "could not write to '%s': %s" % (dst, e.strerror)) - finally: - if fdst: - fdst.close() - if fsrc: - fsrc.close() - -def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, - link=None, verbose=1, dry_run=0): - """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is - copied there with the same name; otherwise, it must be a filename. (If - the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' - is true (the default), the file's mode (type and permission bits, or - whatever is analogous on the current platform) is copied. If - 'preserve_times' is true (the default), the last-modified and - last-access times are copied as well. If 'update' is true, 'src' will - only be copied if 'dst' does not exist, or if 'dst' does exist but is - older than 'src'. - - 'link' allows you to make hard links (os.link) or symbolic links - (os.symlink) instead of copying: set it to "hard" or "sym"; if it is - None (the default), files are copied. Don't set 'link' on systems that - don't support it: 'copy_file()' doesn't check if hard or symbolic - linking is available. If hardlink fails, falls back to - _copy_file_contents(). - - Under Mac OS, uses the native file copy function in macostools; on - other systems, uses '_copy_file_contents()' to copy file contents. - - Return a tuple (dest_name, copied): 'dest_name' is the actual name of - the output file, and 'copied' is true if the file was copied (or would - have been copied, if 'dry_run' true). - """ - # XXX if the destination file already exists, we clobber it if - # copying, but blow up if linking. Hmmm. And I don't know what - # macostools.copyfile() does. Should definitely be consistent, and - # should probably blow up if destination exists and we would be - # changing it (ie. it's not already a hard/soft link to src OR - # (not update) and (src newer than dst). - - from distutils.dep_util import newer - from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE - - if not os.path.isfile(src): - raise DistutilsFileError( - "can't copy '%s': doesn't exist or not a regular file" % src) - - if os.path.isdir(dst): - dir = dst - dst = os.path.join(dst, os.path.basename(src)) - else: - dir = os.path.dirname(dst) - - if update and not newer(src, dst): - if verbose >= 1: - log.debug("not copying %s (output up-to-date)", src) - return (dst, 0) - - try: - action = _copy_action[link] - except KeyError: - raise ValueError("invalid value '%s' for 'link' argument" % link) - - if verbose >= 1: - if os.path.basename(dst) == os.path.basename(src): - log.info("%s %s -> %s", action, src, dir) - else: - log.info("%s %s -> %s", action, src, dst) - - if dry_run: - return (dst, 1) - - # If linking (hard or symbolic), use the appropriate system call - # (Unix only, of course, but that's the caller's responsibility) - elif link == 'hard': - if not (os.path.exists(dst) and os.path.samefile(src, dst)): - try: - os.link(src, dst) - return (dst, 1) - except OSError: - # If hard linking fails, fall back on copying file - # (some special filesystems don't support hard linking - # even under Unix, see issue #8876). - pass - elif link == 'sym': - if not (os.path.exists(dst) and os.path.samefile(src, dst)): - os.symlink(src, dst) - return (dst, 1) - - # Otherwise (non-Mac, not linking), copy the file contents and - # (optionally) copy the times and mode. - _copy_file_contents(src, dst) - if preserve_mode or preserve_times: - st = os.stat(src) - - # According to David Ascher , utime() should be done - # before chmod() (at least under NT). - if preserve_times: - os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) - if preserve_mode: - os.chmod(dst, S_IMODE(st[ST_MODE])) - - return (dst, 1) - - -# XXX I suspect this is Unix-specific -- need porting help! -def move_file (src, dst, - verbose=1, - dry_run=0): - - """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will - be moved into it with the same name; otherwise, 'src' is just renamed - to 'dst'. Return the new full name of the file. - - Handles cross-device moves on Unix using 'copy_file()'. What about - other systems??? - """ - from os.path import exists, isfile, isdir, basename, dirname - import errno - - if verbose >= 1: - log.info("moving %s -> %s", src, dst) - - if dry_run: - return dst - - if not isfile(src): - raise DistutilsFileError("can't move '%s': not a regular file" % src) - - if isdir(dst): - dst = os.path.join(dst, basename(src)) - elif exists(dst): - raise DistutilsFileError( - "can't move '%s': destination '%s' already exists" % - (src, dst)) - - if not isdir(dirname(dst)): - raise DistutilsFileError( - "can't move '%s': destination '%s' not a valid path" % - (src, dst)) - - copy_it = False - try: - os.rename(src, dst) - except OSError as e: - (num, msg) = e.args - if num == errno.EXDEV: - copy_it = True - else: - raise DistutilsFileError( - "couldn't move '%s' to '%s': %s" % (src, dst, msg)) - - if copy_it: - copy_file(src, dst, verbose=verbose) - try: - os.unlink(src) - except OSError as e: - (num, msg) = e.args - try: - os.unlink(dst) - except OSError: - pass - raise DistutilsFileError( - "couldn't move '%s' to '%s' by copy/delete: " - "delete '%s' failed: %s" - % (src, dst, src, msg)) - return dst - - -def write_file (filename, contents): - """Create a file with the specified name and write 'contents' (a - sequence of strings without line terminators) to it. - """ - f = open(filename, "w") - try: - for line in contents: - f.write(line + "\n") - finally: - f.close() diff --git a/filelist.py b/filelist.py deleted file mode 100644 index c92d5fdb..00000000 --- a/filelist.py +++ /dev/null @@ -1,327 +0,0 @@ -"""distutils.filelist - -Provides the FileList class, used for poking about the filesystem -and building lists of files. -""" - -import os, re -import fnmatch -import functools -from distutils.util import convert_path -from distutils.errors import DistutilsTemplateError, DistutilsInternalError -from distutils import log - -class FileList: - """A list of files built by on exploring the filesystem and filtered by - applying various patterns to what we find there. - - Instance attributes: - dir - directory from which files will be taken -- only used if - 'allfiles' not supplied to constructor - files - list of filenames currently being built/filtered/manipulated - allfiles - complete list of files under consideration (ie. without any - filtering applied) - """ - - def __init__(self, warn=None, debug_print=None): - # ignore argument to FileList, but keep them for backwards - # compatibility - self.allfiles = None - self.files = [] - - def set_allfiles(self, allfiles): - self.allfiles = allfiles - - def findall(self, dir=os.curdir): - self.allfiles = findall(dir) - - def debug_print(self, msg): - """Print 'msg' to stdout if the global DEBUG (taken from the - DISTUTILS_DEBUG environment variable) flag is true. - """ - from distutils.debug import DEBUG - if DEBUG: - print(msg) - - # -- List-like methods --------------------------------------------- - - def append(self, item): - self.files.append(item) - - def extend(self, items): - self.files.extend(items) - - def sort(self): - # Not a strict lexical sort! - sortable_files = sorted(map(os.path.split, self.files)) - self.files = [] - for sort_tuple in sortable_files: - self.files.append(os.path.join(*sort_tuple)) - - - # -- Other miscellaneous utility methods --------------------------- - - def remove_duplicates(self): - # Assumes list has been sorted! - for i in range(len(self.files) - 1, 0, -1): - if self.files[i] == self.files[i - 1]: - del self.files[i] - - - # -- "File template" methods --------------------------------------- - - def _parse_template_line(self, line): - words = line.split() - action = words[0] - - patterns = dir = dir_pattern = None - - if action in ('include', 'exclude', - 'global-include', 'global-exclude'): - if len(words) < 2: - raise DistutilsTemplateError( - "'%s' expects ..." % action) - patterns = [convert_path(w) for w in words[1:]] - elif action in ('recursive-include', 'recursive-exclude'): - if len(words) < 3: - raise DistutilsTemplateError( - "'%s' expects ..." % action) - dir = convert_path(words[1]) - patterns = [convert_path(w) for w in words[2:]] - elif action in ('graft', 'prune'): - if len(words) != 2: - raise DistutilsTemplateError( - "'%s' expects a single " % action) - dir_pattern = convert_path(words[1]) - else: - raise DistutilsTemplateError("unknown action '%s'" % action) - - return (action, patterns, dir, dir_pattern) - - def process_template_line(self, line): - # Parse the line: split it up, make sure the right number of words - # is there, and return the relevant words. 'action' is always - # defined: it's the first word of the line. Which of the other - # three are defined depends on the action; it'll be either - # patterns, (dir and patterns), or (dir_pattern). - (action, patterns, dir, dir_pattern) = self._parse_template_line(line) - - # OK, now we know that the action is valid and we have the - # right number of words on the line for that action -- so we - # can proceed with minimal error-checking. - if action == 'include': - self.debug_print("include " + ' '.join(patterns)) - for pattern in patterns: - if not self.include_pattern(pattern, anchor=1): - log.warn("warning: no files found matching '%s'", - pattern) - - elif action == 'exclude': - self.debug_print("exclude " + ' '.join(patterns)) - for pattern in patterns: - if not self.exclude_pattern(pattern, anchor=1): - log.warn(("warning: no previously-included files " - "found matching '%s'"), pattern) - - elif action == 'global-include': - self.debug_print("global-include " + ' '.join(patterns)) - for pattern in patterns: - if not self.include_pattern(pattern, anchor=0): - log.warn(("warning: no files found matching '%s' " - "anywhere in distribution"), pattern) - - elif action == 'global-exclude': - self.debug_print("global-exclude " + ' '.join(patterns)) - for pattern in patterns: - if not self.exclude_pattern(pattern, anchor=0): - log.warn(("warning: no previously-included files matching " - "'%s' found anywhere in distribution"), - pattern) - - elif action == 'recursive-include': - self.debug_print("recursive-include %s %s" % - (dir, ' '.join(patterns))) - for pattern in patterns: - if not self.include_pattern(pattern, prefix=dir): - log.warn(("warning: no files found matching '%s' " - "under directory '%s'"), - pattern, dir) - - elif action == 'recursive-exclude': - self.debug_print("recursive-exclude %s %s" % - (dir, ' '.join(patterns))) - for pattern in patterns: - if not self.exclude_pattern(pattern, prefix=dir): - log.warn(("warning: no previously-included files matching " - "'%s' found under directory '%s'"), - pattern, dir) - - elif action == 'graft': - self.debug_print("graft " + dir_pattern) - if not self.include_pattern(None, prefix=dir_pattern): - log.warn("warning: no directories found matching '%s'", - dir_pattern) - - elif action == 'prune': - self.debug_print("prune " + dir_pattern) - if not self.exclude_pattern(None, prefix=dir_pattern): - log.warn(("no previously-included directories found " - "matching '%s'"), dir_pattern) - else: - raise DistutilsInternalError( - "this cannot happen: invalid action '%s'" % action) - - - # -- Filtering/selection methods ----------------------------------- - - def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): - """Select strings (presumably filenames) from 'self.files' that - match 'pattern', a Unix-style wildcard (glob) pattern. Patterns - are not quite the same as implemented by the 'fnmatch' module: '*' - and '?' match non-special characters, where "special" is platform- - dependent: slash on Unix; colon, slash, and backslash on - DOS/Windows; and colon on Mac OS. - - If 'anchor' is true (the default), then the pattern match is more - stringent: "*.py" will match "foo.py" but not "foo/bar.py". If - 'anchor' is false, both of these will match. - - If 'prefix' is supplied, then only filenames starting with 'prefix' - (itself a pattern) and ending with 'pattern', with anything in between - them, will match. 'anchor' is ignored in this case. - - If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and - 'pattern' is assumed to be either a string containing a regex or a - regex object -- no translation is done, the regex is just compiled - and used as-is. - - Selected strings will be added to self.files. - - Return True if files are found, False otherwise. - """ - # XXX docstring lying about what the special chars are? - files_found = False - pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) - self.debug_print("include_pattern: applying regex r'%s'" % - pattern_re.pattern) - - # delayed loading of allfiles list - if self.allfiles is None: - self.findall() - - for name in self.allfiles: - if pattern_re.search(name): - self.debug_print(" adding " + name) - self.files.append(name) - files_found = True - return files_found - - - def exclude_pattern (self, pattern, - anchor=1, prefix=None, is_regex=0): - """Remove strings (presumably filenames) from 'files' that match - 'pattern'. Other parameters are the same as for - 'include_pattern()', above. - The list 'self.files' is modified in place. - Return True if files are found, False otherwise. - """ - files_found = False - pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) - self.debug_print("exclude_pattern: applying regex r'%s'" % - pattern_re.pattern) - for i in range(len(self.files)-1, -1, -1): - if pattern_re.search(self.files[i]): - self.debug_print(" removing " + self.files[i]) - del self.files[i] - files_found = True - return files_found - - -# ---------------------------------------------------------------------- -# Utility functions - -def _find_all_simple(path): - """ - Find all files under 'path' - """ - results = ( - os.path.join(base, file) - for base, dirs, files in os.walk(path, followlinks=True) - for file in files - ) - return filter(os.path.isfile, results) - - -def findall(dir=os.curdir): - """ - Find all files under 'dir' and return the list of full filenames. - Unless dir is '.', return full filenames with dir prepended. - """ - files = _find_all_simple(dir) - if dir == os.curdir: - make_rel = functools.partial(os.path.relpath, start=dir) - files = map(make_rel, files) - return list(files) - - -def glob_to_re(pattern): - """Translate a shell-like glob pattern to a regular expression; return - a string containing the regex. Differs from 'fnmatch.translate()' in - that '*' does not match "special characters" (which are - platform-specific). - """ - pattern_re = fnmatch.translate(pattern) - - # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which - # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, - # and by extension they shouldn't match such "special characters" under - # any OS. So change all non-escaped dots in the RE to match any - # character except the special characters (currently: just os.sep). - sep = os.sep - if os.sep == '\\': - # we're using a regex to manipulate a regex, so we need - # to escape the backslash twice - sep = r'\\\\' - escaped = r'\1[^%s]' % sep - pattern_re = re.sub(r'((?= self.threshold: - if args: - msg = msg % args - if level in (WARN, ERROR, FATAL): - stream = sys.stderr - else: - stream = sys.stdout - try: - stream.write('%s\n' % msg) - except UnicodeEncodeError: - # emulate backslashreplace error handler - encoding = stream.encoding - msg = msg.encode(encoding, "backslashreplace").decode(encoding) - stream.write('%s\n' % msg) - stream.flush() - - def log(self, level, msg, *args): - self._log(level, msg, args) - - def debug(self, msg, *args): - self._log(DEBUG, msg, args) - - def info(self, msg, *args): - self._log(INFO, msg, args) - - def warn(self, msg, *args): - self._log(WARN, msg, args) - - def error(self, msg, *args): - self._log(ERROR, msg, args) - - def fatal(self, msg, *args): - self._log(FATAL, msg, args) - -_global_log = Log() -log = _global_log.log -debug = _global_log.debug -info = _global_log.info -warn = _global_log.warn -error = _global_log.error -fatal = _global_log.fatal - -def set_threshold(level): - # return the old threshold for use from tests - old = _global_log.threshold - _global_log.threshold = level - return old - -def set_verbosity(v): - if v <= 0: - set_threshold(WARN) - elif v == 1: - set_threshold(INFO) - elif v >= 2: - set_threshold(DEBUG) diff --git a/msvc9compiler.py b/msvc9compiler.py deleted file mode 100644 index 6934e964..00000000 --- a/msvc9compiler.py +++ /dev/null @@ -1,788 +0,0 @@ -"""distutils.msvc9compiler - -Contains MSVCCompiler, an implementation of the abstract CCompiler class -for the Microsoft Visual Studio 2008. - -The module is compatible with VS 2005 and VS 2008. You can find legacy support -for older versions of VS in distutils.msvccompiler. -""" - -# Written by Perry Stoll -# hacked by Robin Becker and Thomas Heller to do a better job of -# finding DevStudio (through the registry) -# ported to VS2005 and VS 2008 by Christian Heimes - -import os -import subprocess -import sys -import re - -from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import CCompiler, gen_lib_options -from distutils import log -from distutils.util import get_platform - -import winreg - -RegOpenKeyEx = winreg.OpenKeyEx -RegEnumKey = winreg.EnumKey -RegEnumValue = winreg.EnumValue -RegError = winreg.error - -HKEYS = (winreg.HKEY_USERS, - winreg.HKEY_CURRENT_USER, - winreg.HKEY_LOCAL_MACHINE, - winreg.HKEY_CLASSES_ROOT) - -NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32) -if NATIVE_WIN64: - # Visual C++ is a 32-bit application, so we need to look in - # the corresponding registry branch, if we're running a - # 64-bit Python on Win64 - VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" - WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" - NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" -else: - VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" - WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" - NET_BASE = r"Software\Microsoft\.NETFramework" - -# A map keyed by get_platform() return values to values accepted by -# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is -# the param to cross-compile on x86 targeting amd64.) -PLAT_TO_VCVARS = { - 'win32' : 'x86', - 'win-amd64' : 'amd64', -} - -class Reg: - """Helper class to read values from the registry - """ - - def get_value(cls, path, key): - for base in HKEYS: - d = cls.read_values(base, path) - if d and key in d: - return d[key] - raise KeyError(key) - get_value = classmethod(get_value) - - def read_keys(cls, base, key): - """Return list of registry keys.""" - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - L = [] - i = 0 - while True: - try: - k = RegEnumKey(handle, i) - except RegError: - break - L.append(k) - i += 1 - return L - read_keys = classmethod(read_keys) - - def read_values(cls, base, key): - """Return dict of registry keys and values. - - All names are converted to lowercase. - """ - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - d = {} - i = 0 - while True: - try: - name, value, type = RegEnumValue(handle, i) - except RegError: - break - name = name.lower() - d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) - i += 1 - return d - read_values = classmethod(read_values) - - def convert_mbcs(s): - dec = getattr(s, "decode", None) - if dec is not None: - try: - s = dec("mbcs") - except UnicodeError: - pass - return s - convert_mbcs = staticmethod(convert_mbcs) - -class MacroExpander: - - def __init__(self, version): - self.macros = {} - self.vsbase = VS_BASE % version - self.load_macros(version) - - def set_macro(self, macro, path, key): - self.macros["$(%s)" % macro] = Reg.get_value(path, key) - - def load_macros(self, version): - self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") - self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") - self.set_macro("FrameworkDir", NET_BASE, "installroot") - try: - if version >= 8.0: - self.set_macro("FrameworkSDKDir", NET_BASE, - "sdkinstallrootv2.0") - else: - raise KeyError("sdkinstallrootv2.0") - except KeyError: - raise DistutilsPlatformError( - """Python was built with Visual Studio 2008; -extensions must be built with a compiler than can generate compatible binaries. -Visual Studio 2008 was not found on this system. If you have Cygwin installed, -you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") - - if version >= 9.0: - self.set_macro("FrameworkVersion", self.vsbase, "clr version") - self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") - else: - p = r"Software\Microsoft\NET Framework Setup\Product" - for base in HKEYS: - try: - h = RegOpenKeyEx(base, p) - except RegError: - continue - key = RegEnumKey(h, 0) - d = Reg.get_value(base, r"%s\%s" % (p, key)) - self.macros["$(FrameworkVersion)"] = d["version"] - - def sub(self, s): - for k, v in self.macros.items(): - s = s.replace(k, v) - return s - -def get_build_version(): - """Return the version of MSVC that was used to build Python. - - For Python 2.3 and up, the version number is included in - sys.version. For earlier versions, assume the compiler is MSVC 6. - """ - prefix = "MSC v." - i = sys.version.find(prefix) - if i == -1: - return 6 - i = i + len(prefix) - s, rest = sys.version[i:].split(" ", 1) - majorVersion = int(s[:-2]) - 6 - if majorVersion >= 13: - # v13 was skipped and should be v14 - majorVersion += 1 - minorVersion = int(s[2:3]) / 10.0 - # I don't think paths are affected by minor version in version 6 - if majorVersion == 6: - minorVersion = 0 - if majorVersion >= 6: - return majorVersion + minorVersion - # else we don't know what version of the compiler this is - return None - -def normalize_and_reduce_paths(paths): - """Return a list of normalized paths with duplicates removed. - - The current order of paths is maintained. - """ - # Paths are normalized so things like: /a and /a/ aren't both preserved. - reduced_paths = [] - for p in paths: - np = os.path.normpath(p) - # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. - if np not in reduced_paths: - reduced_paths.append(np) - return reduced_paths - -def removeDuplicates(variable): - """Remove duplicate values of an environment variable. - """ - oldList = variable.split(os.pathsep) - newList = [] - for i in oldList: - if i not in newList: - newList.append(i) - newVariable = os.pathsep.join(newList) - return newVariable - -def find_vcvarsall(version): - """Find the vcvarsall.bat file - - At first it tries to find the productdir of VS 2008 in the registry. If - that fails it falls back to the VS90COMNTOOLS env var. - """ - vsbase = VS_BASE % version - try: - productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, - "productdir") - except KeyError: - log.debug("Unable to find productdir in registry") - productdir = None - - if not productdir or not os.path.isdir(productdir): - toolskey = "VS%0.f0COMNTOOLS" % version - toolsdir = os.environ.get(toolskey, None) - - if toolsdir and os.path.isdir(toolsdir): - productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") - productdir = os.path.abspath(productdir) - if not os.path.isdir(productdir): - log.debug("%s is not a valid directory" % productdir) - return None - else: - log.debug("Env var %s is not set or invalid" % toolskey) - if not productdir: - log.debug("No productdir found") - return None - vcvarsall = os.path.join(productdir, "vcvarsall.bat") - if os.path.isfile(vcvarsall): - return vcvarsall - log.debug("Unable to find vcvarsall.bat") - return None - -def query_vcvarsall(version, arch="x86"): - """Launch vcvarsall.bat and read the settings from its environment - """ - vcvarsall = find_vcvarsall(version) - interesting = {"include", "lib", "libpath", "path"} - result = {} - - if vcvarsall is None: - raise DistutilsPlatformError("Unable to find vcvarsall.bat") - log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) - popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - try: - stdout, stderr = popen.communicate() - if popen.wait() != 0: - raise DistutilsPlatformError(stderr.decode("mbcs")) - - stdout = stdout.decode("mbcs") - for line in stdout.split("\n"): - line = Reg.convert_mbcs(line) - if '=' not in line: - continue - line = line.strip() - key, value = line.split('=', 1) - key = key.lower() - if key in interesting: - if value.endswith(os.pathsep): - value = value[:-1] - result[key] = removeDuplicates(value) - - finally: - popen.stdout.close() - popen.stderr.close() - - if len(result) != len(interesting): - raise ValueError(str(list(result.keys()))) - - return result - -# More globals -VERSION = get_build_version() -if VERSION < 8.0: - raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) -# MACROS = MacroExpander(VERSION) - -class MSVCCompiler(CCompiler) : - """Concrete class that implements an interface to Microsoft Visual C++, - as defined by the CCompiler abstract class.""" - - compiler_type = 'msvc' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.rc'] - _mc_extensions = ['.mc'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = (_c_extensions + _cpp_extensions + - _rc_extensions + _mc_extensions) - res_extension = '.res' - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - def __init__(self, verbose=0, dry_run=0, force=0): - CCompiler.__init__ (self, verbose, dry_run, force) - self.__version = VERSION - self.__root = r"Software\Microsoft\VisualStudio" - # self.__macros = MACROS - self.__paths = [] - # target platform (.plat_name is consistent with 'bdist') - self.plat_name = None - self.__arch = None # deprecated name - self.initialized = False - - def initialize(self, plat_name=None): - # multi-init means we would need to check platform same each time... - assert not self.initialized, "don't init multiple times" - if plat_name is None: - plat_name = get_platform() - # sanity check for platforms to prevent obscure errors later. - ok_plats = 'win32', 'win-amd64' - if plat_name not in ok_plats: - raise DistutilsPlatformError("--plat-name must be one of %s" % - (ok_plats,)) - - if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): - # Assume that the SDK set up everything alright; don't try to be - # smarter - self.cc = "cl.exe" - self.linker = "link.exe" - self.lib = "lib.exe" - self.rc = "rc.exe" - self.mc = "mc.exe" - else: - # On x86, 'vcvars32.bat amd64' creates an env that doesn't work; - # to cross compile, you use 'x86_amd64'. - # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross - # compile use 'x86' (ie, it runs the x86 compiler directly) - if plat_name == get_platform() or plat_name == 'win32': - # native build or cross-compile to win32 - plat_spec = PLAT_TO_VCVARS[plat_name] - else: - # cross compile from win32 -> some 64bit - plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ - PLAT_TO_VCVARS[plat_name] - - vc_env = query_vcvarsall(VERSION, plat_spec) - - self.__paths = vc_env['path'].split(os.pathsep) - os.environ['lib'] = vc_env['lib'] - os.environ['include'] = vc_env['include'] - - if len(self.__paths) == 0: - raise DistutilsPlatformError("Python was built with %s, " - "and extensions need to be built with the same " - "version of the compiler, but it isn't installed." - % self.__product) - - self.cc = self.find_exe("cl.exe") - self.linker = self.find_exe("link.exe") - self.lib = self.find_exe("lib.exe") - self.rc = self.find_exe("rc.exe") # resource compiler - self.mc = self.find_exe("mc.exe") # message compiler - #self.set_path_env_var('lib') - #self.set_path_env_var('include') - - # extend the MSVC path with the current path - try: - for p in os.environ['path'].split(';'): - self.__paths.append(p) - except KeyError: - pass - self.__paths = normalize_and_reduce_paths(self.__paths) - os.environ['path'] = ";".join(self.__paths) - - self.preprocess_options = None - if self.__arch == "x86": - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', - '/Z7', '/D_DEBUG'] - else: - # Win64 - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', - '/Z7', '/D_DEBUG'] - - self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] - if self.__version >= 7: - self.ldflags_shared_debug = [ - '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' - ] - self.ldflags_static = [ '/nologo'] - - self.initialized = True - - # -- Worker methods ------------------------------------------------ - - def object_filenames(self, - source_filenames, - strip_dir=0, - output_dir=''): - # Copied from ccompiler.py, extended to return .res as 'object'-file - # for .rc input file - if output_dir is None: output_dir = '' - obj_names = [] - for src_name in source_filenames: - (base, ext) = os.path.splitext (src_name) - base = os.path.splitdrive(base)[1] # Chop off the drive - base = base[os.path.isabs(base):] # If abs, chop off leading / - if ext not in self.src_extensions: - # Better to raise an exception instead of silently continuing - # and later complain about sources and targets having - # different lengths - raise CompileError ("Don't know how to compile %s" % src_name) - if strip_dir: - base = os.path.basename (base) - if ext in self._rc_extensions: - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - elif ext in self._mc_extensions: - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - else: - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) - return obj_names - - - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - if not self.initialized: - self.initialize() - compile_info = self._setup_compile(output_dir, macros, include_dirs, - sources, depends, extra_postargs) - macros, objects, extra_postargs, pp_opts, build = compile_info - - compile_opts = extra_preargs or [] - compile_opts.append ('/c') - if debug: - compile_opts.extend(self.compile_options_debug) - else: - compile_opts.extend(self.compile_options) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - if debug: - # pass the full pathname to MSVC in debug mode, - # this allows the debugger to find the source file - # without asking the user to browse for it - src = os.path.abspath(src) - - if ext in self._c_extensions: - input_opt = "/Tc" + src - elif ext in self._cpp_extensions: - input_opt = "/Tp" + src - elif ext in self._rc_extensions: - # compile .RC to .RES file - input_opt = src - output_opt = "/fo" + obj - try: - self.spawn([self.rc] + pp_opts + - [output_opt] + [input_opt]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue - elif ext in self._mc_extensions: - # Compile .MC to .RC file to .RES file. - # * '-h dir' specifies the directory for the - # generated include file - # * '-r dir' specifies the target directory of the - # generated RC file and the binary message resource - # it includes - # - # For now (since there are no options to change this), - # we use the source-directory for the include file and - # the build directory for the RC file and message - # resources. This works at least for win32all. - h_dir = os.path.dirname(src) - rc_dir = os.path.dirname(obj) - try: - # first compile .MC to .RC and .H file - self.spawn([self.mc] + - ['-h', h_dir, '-r', rc_dir] + [src]) - base, _ = os.path.splitext (os.path.basename (src)) - rc_file = os.path.join (rc_dir, base + '.rc') - # then compile .RC to .RES file - self.spawn([self.rc] + - ["/fo" + obj] + [rc_file]) - - except DistutilsExecError as msg: - raise CompileError(msg) - continue - else: - # how to handle this file? - raise CompileError("Don't know how to compile %s to %s" - % (src, obj)) - - output_opt = "/Fo" + obj - try: - self.spawn([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - - def create_static_lib(self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): - - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - output_filename = self.library_filename(output_libname, - output_dir=output_dir) - - if self._need_link(objects, output_filename): - lib_args = objects + ['/OUT:' + output_filename] - if debug: - pass # XXX what goes here? - try: - self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, - runtime_library_dirs) - (libraries, library_dirs, runtime_library_dirs) = fixed_args - - if runtime_library_dirs: - self.warn ("I don't know what to do with 'runtime_library_dirs': " - + str (runtime_library_dirs)) - - lib_opts = gen_lib_options(self, - library_dirs, runtime_library_dirs, - libraries) - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - if target_desc == CCompiler.EXECUTABLE: - if debug: - ldflags = self.ldflags_shared_debug[1:] - else: - ldflags = self.ldflags_shared[1:] - else: - if debug: - ldflags = self.ldflags_shared_debug - else: - ldflags = self.ldflags_shared - - export_opts = [] - for sym in (export_symbols or []): - export_opts.append("/EXPORT:" + sym) - - ld_args = (ldflags + lib_opts + export_opts + - objects + ['/OUT:' + output_filename]) - - # The MSVC linker generates .lib and .exp files, which cannot be - # suppressed by any linker switches. The .lib files may even be - # needed! Make sure they are generated in the temporary build - # directory. Since they have different names for debug and release - # builds, they can go into the same directory. - build_temp = os.path.dirname(objects[0]) - if export_symbols is not None: - (dll_name, dll_ext) = os.path.splitext( - os.path.basename(output_filename)) - implib_file = os.path.join( - build_temp, - self.library_filename(dll_name)) - ld_args.append ('/IMPLIB:' + implib_file) - - self.manifest_setup_ldargs(output_filename, build_temp, ld_args) - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - self.mkpath(os.path.dirname(output_filename)) - try: - self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - - # embed the manifest - # XXX - this is somewhat fragile - if mt.exe fails, distutils - # will still consider the DLL up-to-date, but it will not have a - # manifest. Maybe we should link to a temp file? OTOH, that - # implies a build environment error that shouldn't go undetected. - mfinfo = self.manifest_get_embed_info(target_desc, ld_args) - if mfinfo is not None: - mffilename, mfid = mfinfo - out_arg = '-outputresource:%s;%s' % (output_filename, mfid) - try: - self.spawn(['mt.exe', '-nologo', '-manifest', - mffilename, out_arg]) - except DistutilsExecError as msg: - raise LinkError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): - # If we need a manifest at all, an embedded manifest is recommended. - # See MSDN article titled - # "How to: Embed a Manifest Inside a C/C++ Application" - # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) - # Ask the linker to generate the manifest in the temp dir, so - # we can check it, and possibly embed it, later. - temp_manifest = os.path.join( - build_temp, - os.path.basename(output_filename) + ".manifest") - ld_args.append('/MANIFESTFILE:' + temp_manifest) - - def manifest_get_embed_info(self, target_desc, ld_args): - # If a manifest should be embedded, return a tuple of - # (manifest_filename, resource_id). Returns None if no manifest - # should be embedded. See http://bugs.python.org/issue7833 for why - # we want to avoid any manifest for extension modules if we can) - for arg in ld_args: - if arg.startswith("/MANIFESTFILE:"): - temp_manifest = arg.split(":", 1)[1] - break - else: - # no /MANIFESTFILE so nothing to do. - return None - if target_desc == CCompiler.EXECUTABLE: - # by default, executables always get the manifest with the - # CRT referenced. - mfid = 1 - else: - # Extension modules try and avoid any manifest if possible. - mfid = 2 - temp_manifest = self._remove_visual_c_ref(temp_manifest) - if temp_manifest is None: - return None - return temp_manifest, mfid - - def _remove_visual_c_ref(self, manifest_file): - try: - # Remove references to the Visual C runtime, so they will - # fall through to the Visual C dependency of Python.exe. - # This way, when installed for a restricted user (e.g. - # runtimes are not in WinSxS folder, but in Python's own - # folder), the runtimes do not need to be in every folder - # with .pyd's. - # Returns either the filename of the modified manifest or - # None if no manifest should be embedded. - manifest_f = open(manifest_file) - try: - manifest_buf = manifest_f.read() - finally: - manifest_f.close() - pattern = re.compile( - r"""|)""", - re.DOTALL) - manifest_buf = re.sub(pattern, "", manifest_buf) - pattern = r"\s*" - manifest_buf = re.sub(pattern, "", manifest_buf) - # Now see if any other assemblies are referenced - if not, we - # don't want a manifest embedded. - pattern = re.compile( - r"""|)""", re.DOTALL) - if re.search(pattern, manifest_buf) is None: - return None - - manifest_f = open(manifest_file, 'w') - try: - manifest_f.write(manifest_buf) - return manifest_file - finally: - manifest_f.close() - except OSError: - pass - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "/LIBPATH:" + dir - - def runtime_library_dir_option(self, dir): - raise DistutilsPlatformError( - "don't know how to set runtime library search path for MSVC++") - - def library_option(self, lib): - return self.library_filename(lib) - - - def find_library_file(self, dirs, lib, debug=0): - # Prefer a debugging library if found (and requested), but deal - # with it if we don't have one. - if debug: - try_names = [lib + "_d", lib] - else: - try_names = [lib] - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename (name)) - if os.path.exists(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None - - # Helper methods for using the MSVC registry settings - - def find_exe(self, exe): - """Return path to an MSVC executable program. - - Tries to find the program in several places: first, one of the - MSVC program search paths from the registry; next, the directories - in the PATH environment variable. If any of those work, return an - absolute path that is known to exist. If none of them work, just - return the original program name, 'exe'. - """ - for p in self.__paths: - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - - # didn't find it; try existing path - for p in os.environ['Path'].split(';'): - fn = os.path.join(os.path.abspath(p),exe) - if os.path.isfile(fn): - return fn - - return exe diff --git a/msvccompiler.py b/msvccompiler.py deleted file mode 100644 index d5857cb1..00000000 --- a/msvccompiler.py +++ /dev/null @@ -1,643 +0,0 @@ -"""distutils.msvccompiler - -Contains MSVCCompiler, an implementation of the abstract CCompiler class -for the Microsoft Visual Studio. -""" - -# Written by Perry Stoll -# hacked by Robin Becker and Thomas Heller to do a better job of -# finding DevStudio (through the registry) - -import sys, os -from distutils.errors import \ - DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import \ - CCompiler, gen_lib_options -from distutils import log - -_can_read_reg = False -try: - import winreg - - _can_read_reg = True - hkey_mod = winreg - - RegOpenKeyEx = winreg.OpenKeyEx - RegEnumKey = winreg.EnumKey - RegEnumValue = winreg.EnumValue - RegError = winreg.error - -except ImportError: - try: - import win32api - import win32con - _can_read_reg = True - hkey_mod = win32con - - RegOpenKeyEx = win32api.RegOpenKeyEx - RegEnumKey = win32api.RegEnumKey - RegEnumValue = win32api.RegEnumValue - RegError = win32api.error - except ImportError: - log.info("Warning: Can't read registry to find the " - "necessary compiler setting\n" - "Make sure that Python modules winreg, " - "win32api or win32con are installed.") - pass - -if _can_read_reg: - HKEYS = (hkey_mod.HKEY_USERS, - hkey_mod.HKEY_CURRENT_USER, - hkey_mod.HKEY_LOCAL_MACHINE, - hkey_mod.HKEY_CLASSES_ROOT) - -def read_keys(base, key): - """Return list of registry keys.""" - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - L = [] - i = 0 - while True: - try: - k = RegEnumKey(handle, i) - except RegError: - break - L.append(k) - i += 1 - return L - -def read_values(base, key): - """Return dict of registry keys and values. - - All names are converted to lowercase. - """ - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - d = {} - i = 0 - while True: - try: - name, value, type = RegEnumValue(handle, i) - except RegError: - break - name = name.lower() - d[convert_mbcs(name)] = convert_mbcs(value) - i += 1 - return d - -def convert_mbcs(s): - dec = getattr(s, "decode", None) - if dec is not None: - try: - s = dec("mbcs") - except UnicodeError: - pass - return s - -class MacroExpander: - def __init__(self, version): - self.macros = {} - self.load_macros(version) - - def set_macro(self, macro, path, key): - for base in HKEYS: - d = read_values(base, path) - if d: - self.macros["$(%s)" % macro] = d[key] - break - - def load_macros(self, version): - vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version - self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") - self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") - net = r"Software\Microsoft\.NETFramework" - self.set_macro("FrameworkDir", net, "installroot") - try: - if version > 7.0: - self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") - else: - self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") - except KeyError as exc: # - raise DistutilsPlatformError( - """Python was built with Visual Studio 2003; -extensions must be built with a compiler than can generate compatible binaries. -Visual Studio 2003 was not found on this system. If you have Cygwin installed, -you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") - - p = r"Software\Microsoft\NET Framework Setup\Product" - for base in HKEYS: - try: - h = RegOpenKeyEx(base, p) - except RegError: - continue - key = RegEnumKey(h, 0) - d = read_values(base, r"%s\%s" % (p, key)) - self.macros["$(FrameworkVersion)"] = d["version"] - - def sub(self, s): - for k, v in self.macros.items(): - s = s.replace(k, v) - return s - -def get_build_version(): - """Return the version of MSVC that was used to build Python. - - For Python 2.3 and up, the version number is included in - sys.version. For earlier versions, assume the compiler is MSVC 6. - """ - prefix = "MSC v." - i = sys.version.find(prefix) - if i == -1: - return 6 - i = i + len(prefix) - s, rest = sys.version[i:].split(" ", 1) - majorVersion = int(s[:-2]) - 6 - if majorVersion >= 13: - # v13 was skipped and should be v14 - majorVersion += 1 - minorVersion = int(s[2:3]) / 10.0 - # I don't think paths are affected by minor version in version 6 - if majorVersion == 6: - minorVersion = 0 - if majorVersion >= 6: - return majorVersion + minorVersion - # else we don't know what version of the compiler this is - return None - -def get_build_architecture(): - """Return the processor architecture. - - Possible results are "Intel" or "AMD64". - """ - - prefix = " bit (" - i = sys.version.find(prefix) - if i == -1: - return "Intel" - j = sys.version.find(")", i) - return sys.version[i+len(prefix):j] - -def normalize_and_reduce_paths(paths): - """Return a list of normalized paths with duplicates removed. - - The current order of paths is maintained. - """ - # Paths are normalized so things like: /a and /a/ aren't both preserved. - reduced_paths = [] - for p in paths: - np = os.path.normpath(p) - # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. - if np not in reduced_paths: - reduced_paths.append(np) - return reduced_paths - - -class MSVCCompiler(CCompiler) : - """Concrete class that implements an interface to Microsoft Visual C++, - as defined by the CCompiler abstract class.""" - - compiler_type = 'msvc' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.rc'] - _mc_extensions = ['.mc'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = (_c_extensions + _cpp_extensions + - _rc_extensions + _mc_extensions) - res_extension = '.res' - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - def __init__(self, verbose=0, dry_run=0, force=0): - CCompiler.__init__ (self, verbose, dry_run, force) - self.__version = get_build_version() - self.__arch = get_build_architecture() - if self.__arch == "Intel": - # x86 - if self.__version >= 7: - self.__root = r"Software\Microsoft\VisualStudio" - self.__macros = MacroExpander(self.__version) - else: - self.__root = r"Software\Microsoft\Devstudio" - self.__product = "Visual Studio version %s" % self.__version - else: - # Win64. Assume this was built with the platform SDK - self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) - - self.initialized = False - - def initialize(self): - self.__paths = [] - if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): - # Assume that the SDK set up everything alright; don't try to be - # smarter - self.cc = "cl.exe" - self.linker = "link.exe" - self.lib = "lib.exe" - self.rc = "rc.exe" - self.mc = "mc.exe" - else: - self.__paths = self.get_msvc_paths("path") - - if len(self.__paths) == 0: - raise DistutilsPlatformError("Python was built with %s, " - "and extensions need to be built with the same " - "version of the compiler, but it isn't installed." - % self.__product) - - self.cc = self.find_exe("cl.exe") - self.linker = self.find_exe("link.exe") - self.lib = self.find_exe("lib.exe") - self.rc = self.find_exe("rc.exe") # resource compiler - self.mc = self.find_exe("mc.exe") # message compiler - self.set_path_env_var('lib') - self.set_path_env_var('include') - - # extend the MSVC path with the current path - try: - for p in os.environ['path'].split(';'): - self.__paths.append(p) - except KeyError: - pass - self.__paths = normalize_and_reduce_paths(self.__paths) - os.environ['path'] = ";".join(self.__paths) - - self.preprocess_options = None - if self.__arch == "Intel": - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', - '/Z7', '/D_DEBUG'] - else: - # Win64 - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', - '/Z7', '/D_DEBUG'] - - self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] - if self.__version >= 7: - self.ldflags_shared_debug = [ - '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' - ] - else: - self.ldflags_shared_debug = [ - '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG' - ] - self.ldflags_static = [ '/nologo'] - - self.initialized = True - - # -- Worker methods ------------------------------------------------ - - def object_filenames(self, - source_filenames, - strip_dir=0, - output_dir=''): - # Copied from ccompiler.py, extended to return .res as 'object'-file - # for .rc input file - if output_dir is None: output_dir = '' - obj_names = [] - for src_name in source_filenames: - (base, ext) = os.path.splitext (src_name) - base = os.path.splitdrive(base)[1] # Chop off the drive - base = base[os.path.isabs(base):] # If abs, chop off leading / - if ext not in self.src_extensions: - # Better to raise an exception instead of silently continuing - # and later complain about sources and targets having - # different lengths - raise CompileError ("Don't know how to compile %s" % src_name) - if strip_dir: - base = os.path.basename (base) - if ext in self._rc_extensions: - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - elif ext in self._mc_extensions: - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - else: - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) - return obj_names - - - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - if not self.initialized: - self.initialize() - compile_info = self._setup_compile(output_dir, macros, include_dirs, - sources, depends, extra_postargs) - macros, objects, extra_postargs, pp_opts, build = compile_info - - compile_opts = extra_preargs or [] - compile_opts.append ('/c') - if debug: - compile_opts.extend(self.compile_options_debug) - else: - compile_opts.extend(self.compile_options) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - if debug: - # pass the full pathname to MSVC in debug mode, - # this allows the debugger to find the source file - # without asking the user to browse for it - src = os.path.abspath(src) - - if ext in self._c_extensions: - input_opt = "/Tc" + src - elif ext in self._cpp_extensions: - input_opt = "/Tp" + src - elif ext in self._rc_extensions: - # compile .RC to .RES file - input_opt = src - output_opt = "/fo" + obj - try: - self.spawn([self.rc] + pp_opts + - [output_opt] + [input_opt]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue - elif ext in self._mc_extensions: - # Compile .MC to .RC file to .RES file. - # * '-h dir' specifies the directory for the - # generated include file - # * '-r dir' specifies the target directory of the - # generated RC file and the binary message resource - # it includes - # - # For now (since there are no options to change this), - # we use the source-directory for the include file and - # the build directory for the RC file and message - # resources. This works at least for win32all. - h_dir = os.path.dirname(src) - rc_dir = os.path.dirname(obj) - try: - # first compile .MC to .RC and .H file - self.spawn([self.mc] + - ['-h', h_dir, '-r', rc_dir] + [src]) - base, _ = os.path.splitext (os.path.basename (src)) - rc_file = os.path.join (rc_dir, base + '.rc') - # then compile .RC to .RES file - self.spawn([self.rc] + - ["/fo" + obj] + [rc_file]) - - except DistutilsExecError as msg: - raise CompileError(msg) - continue - else: - # how to handle this file? - raise CompileError("Don't know how to compile %s to %s" - % (src, obj)) - - output_opt = "/Fo" + obj - try: - self.spawn([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - - def create_static_lib(self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): - - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - output_filename = self.library_filename(output_libname, - output_dir=output_dir) - - if self._need_link(objects, output_filename): - lib_args = objects + ['/OUT:' + output_filename] - if debug: - pass # XXX what goes here? - try: - self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, - runtime_library_dirs) - (libraries, library_dirs, runtime_library_dirs) = fixed_args - - if runtime_library_dirs: - self.warn ("I don't know what to do with 'runtime_library_dirs': " - + str (runtime_library_dirs)) - - lib_opts = gen_lib_options(self, - library_dirs, runtime_library_dirs, - libraries) - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - if target_desc == CCompiler.EXECUTABLE: - if debug: - ldflags = self.ldflags_shared_debug[1:] - else: - ldflags = self.ldflags_shared[1:] - else: - if debug: - ldflags = self.ldflags_shared_debug - else: - ldflags = self.ldflags_shared - - export_opts = [] - for sym in (export_symbols or []): - export_opts.append("/EXPORT:" + sym) - - ld_args = (ldflags + lib_opts + export_opts + - objects + ['/OUT:' + output_filename]) - - # The MSVC linker generates .lib and .exp files, which cannot be - # suppressed by any linker switches. The .lib files may even be - # needed! Make sure they are generated in the temporary build - # directory. Since they have different names for debug and release - # builds, they can go into the same directory. - if export_symbols is not None: - (dll_name, dll_ext) = os.path.splitext( - os.path.basename(output_filename)) - implib_file = os.path.join( - os.path.dirname(objects[0]), - self.library_filename(dll_name)) - ld_args.append ('/IMPLIB:' + implib_file) - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - self.mkpath(os.path.dirname(output_filename)) - try: - self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - - else: - log.debug("skipping %s (up-to-date)", output_filename) - - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "/LIBPATH:" + dir - - def runtime_library_dir_option(self, dir): - raise DistutilsPlatformError( - "don't know how to set runtime library search path for MSVC++") - - def library_option(self, lib): - return self.library_filename(lib) - - - def find_library_file(self, dirs, lib, debug=0): - # Prefer a debugging library if found (and requested), but deal - # with it if we don't have one. - if debug: - try_names = [lib + "_d", lib] - else: - try_names = [lib] - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename (name)) - if os.path.exists(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None - - # Helper methods for using the MSVC registry settings - - def find_exe(self, exe): - """Return path to an MSVC executable program. - - Tries to find the program in several places: first, one of the - MSVC program search paths from the registry; next, the directories - in the PATH environment variable. If any of those work, return an - absolute path that is known to exist. If none of them work, just - return the original program name, 'exe'. - """ - for p in self.__paths: - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - - # didn't find it; try existing path - for p in os.environ['Path'].split(';'): - fn = os.path.join(os.path.abspath(p),exe) - if os.path.isfile(fn): - return fn - - return exe - - def get_msvc_paths(self, path, platform='x86'): - """Get a list of devstudio directories (include, lib or path). - - Return a list of strings. The list will be empty if unable to - access the registry or appropriate registry keys not found. - """ - if not _can_read_reg: - return [] - - path = path + " dirs" - if self.__version >= 7: - key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" - % (self.__root, self.__version)) - else: - key = (r"%s\6.0\Build System\Components\Platforms" - r"\Win32 (%s)\Directories" % (self.__root, platform)) - - for base in HKEYS: - d = read_values(base, key) - if d: - if self.__version >= 7: - return self.__macros.sub(d[path]).split(";") - else: - return d[path].split(";") - # MSVC 6 seems to create the registry entries we need only when - # the GUI is run. - if self.__version == 6: - for base in HKEYS: - if read_values(base, r"%s\6.0" % self.__root) is not None: - self.warn("It seems you have Visual Studio 6 installed, " - "but the expected registry settings are not present.\n" - "You must at least run the Visual Studio GUI once " - "so that these entries are created.") - break - return [] - - def set_path_env_var(self, name): - """Set environment variable 'name' to an MSVC path type value. - - This is equivalent to a SET command prior to execution of spawned - commands. - """ - - if name == "lib": - p = self.get_msvc_paths("library") - else: - p = self.get_msvc_paths(name) - if p: - os.environ[name] = ';'.join(p) - - -if get_build_version() >= 8.0: - log.debug("Importing new compiler from distutils.msvc9compiler") - OldMSVCCompiler = MSVCCompiler - from distutils.msvc9compiler import MSVCCompiler - # get_build_architecture not really relevant now we support cross-compile - from distutils.msvc9compiler import MacroExpander diff --git a/spawn.py b/spawn.py deleted file mode 100644 index aad277b0..00000000 --- a/spawn.py +++ /dev/null @@ -1,119 +0,0 @@ -"""distutils.spawn - -Provides the 'spawn()' function, a front-end to various platform- -specific functions for launching another program in a sub-process. -Also provides the 'find_executable()' to search the path for a given -executable name. -""" - -import sys -import os -import subprocess - -from distutils.errors import DistutilsPlatformError, DistutilsExecError -from distutils.debug import DEBUG -from distutils import log - - -if sys.platform == 'darwin': - _cfg_target = None - _cfg_target_split = None - - -def spawn(cmd, search_path=1, verbose=0, dry_run=0): - """Run another program, specified as a command list 'cmd', in a new process. - - 'cmd' is just the argument list for the new process, ie. - cmd[0] is the program to run and cmd[1:] are the rest of its arguments. - There is no way to run a program with a name different from that of its - executable. - - If 'search_path' is true (the default), the system's executable - search path will be used to find the program; otherwise, cmd[0] - must be the exact path to the executable. If 'dry_run' is true, - the command will not actually be run. - - Raise DistutilsExecError if running the program fails in any way; just - return on success. - """ - # cmd is documented as a list, but just in case some code passes a tuple - # in, protect our %-formatting code against horrible death - cmd = list(cmd) - - log.info(' '.join(cmd)) - if dry_run: - return - - if search_path: - executable = find_executable(cmd[0]) - if executable is not None: - cmd[0] = executable - - env = None - if sys.platform == 'darwin': - global _cfg_target, _cfg_target_split - if _cfg_target is None: - from distutils import sysconfig - _cfg_target = sysconfig.get_config_var( - 'MACOSX_DEPLOYMENT_TARGET') or '' - if _cfg_target: - _cfg_target_split = [int(x) for x in _cfg_target.split('.')] - if _cfg_target: - # ensure that the deployment target of build process is not less - # than that used when the interpreter was built. This ensures - # extension modules are built with correct compatibility values - cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target) - if _cfg_target_split > [int(x) for x in cur_target.split('.')]: - my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: ' - 'now "%s" but "%s" during configure' - % (cur_target, _cfg_target)) - raise DistutilsPlatformError(my_msg) - env = dict(os.environ, - MACOSX_DEPLOYMENT_TARGET=cur_target) - - proc = subprocess.Popen(cmd, env=env) - proc.wait() - exitcode = proc.returncode - - if exitcode: - if not DEBUG: - cmd = cmd[0] - raise DistutilsExecError( - "command %r failed with exit code %s" % (cmd, exitcode)) - - -def find_executable(executable, path=None): - """Tries to find 'executable' in the directories listed in 'path'. - - A string listing directories separated by 'os.pathsep'; defaults to - os.environ['PATH']. Returns the complete filename or None if not found. - """ - _, ext = os.path.splitext(executable) - if (sys.platform == 'win32') and (ext != '.exe'): - executable = executable + '.exe' - - if os.path.isfile(executable): - return executable - - if path is None: - path = os.environ.get('PATH', None) - if path is None: - try: - path = os.confstr("CS_PATH") - except (AttributeError, ValueError): - # os.confstr() or CS_PATH is not available - path = os.defpath - # bpo-35755: Don't use os.defpath if the PATH environment variable is - # set to an empty string - - # PATH='' doesn't match, whereas PATH=':' looks in the current directory - if not path: - return None - - paths = path.split(os.pathsep) - for p in paths: - f = os.path.join(p, executable) - if os.path.isfile(f): - # the file exists, we have a shot at spawn working - return f - return None diff --git a/sysconfig.py b/sysconfig.py deleted file mode 100644 index 37feae5d..00000000 --- a/sysconfig.py +++ /dev/null @@ -1,555 +0,0 @@ -"""Provide access to Python's configuration information. The specific -configuration variables available depend heavily on the platform and -configuration. The values may be retrieved using -get_config_var(name), and the list of variables is available via -get_config_vars().keys(). Additional convenience functions are also -available. - -Written by: Fred L. Drake, Jr. -Email: -""" - -import _imp -import os -import re -import sys - -from .errors import DistutilsPlatformError - -# These are needed in a couple of spots, so just compute them once. -PREFIX = os.path.normpath(sys.prefix) -EXEC_PREFIX = os.path.normpath(sys.exec_prefix) -BASE_PREFIX = os.path.normpath(sys.base_prefix) -BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) - -# Path to the base directory of the project. On Windows the binary may -# live in project/PCbuild/win32 or project/PCbuild/amd64. -# set for cross builds -if "_PYTHON_PROJECT_BASE" in os.environ: - project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) -else: - if sys.executable: - project_base = os.path.dirname(os.path.abspath(sys.executable)) - else: - # sys.executable can be empty if argv[0] has been changed and Python is - # unable to retrieve the real program name - project_base = os.getcwd() - - -# python_build: (Boolean) if true, we're either building Python or -# building an extension with an un-installed Python, so we use -# different (hard-wired) directories. -def _is_python_source_dir(d): - for fn in ("Setup", "Setup.local"): - if os.path.isfile(os.path.join(d, "Modules", fn)): - return True - return False - -_sys_home = getattr(sys, '_home', None) - -if os.name == 'nt': - def _fix_pcbuild(d): - if d and os.path.normcase(d).startswith( - os.path.normcase(os.path.join(PREFIX, "PCbuild"))): - return PREFIX - return d - project_base = _fix_pcbuild(project_base) - _sys_home = _fix_pcbuild(_sys_home) - -def _python_build(): - if _sys_home: - return _is_python_source_dir(_sys_home) - return _is_python_source_dir(project_base) - -python_build = _python_build() - - -# Calculate the build qualifier flags if they are defined. Adding the flags -# to the include and lib directories only makes sense for an installation, not -# an in-source build. -build_flags = '' -try: - if not python_build: - build_flags = sys.abiflags -except AttributeError: - # It's not a configure-based build, so the sys module doesn't have - # this attribute, which is fine. - pass - -def get_python_version(): - """Return a string containing the major and minor Python version, - leaving off the patchlevel. Sample return values could be '1.5' - or '2.2'. - """ - return '%d.%d' % sys.version_info[:2] - - -def get_python_inc(plat_specific=0, prefix=None): - """Return the directory containing installed Python header files. - - If 'plat_specific' is false (the default), this is the path to the - non-platform-specific header files, i.e. Python.h and so on; - otherwise, this is the path to platform-specific header files - (namely pyconfig.h). - - If 'prefix' is supplied, use it instead of sys.base_prefix or - sys.base_exec_prefix -- i.e., ignore 'plat_specific'. - """ - if prefix is None: - prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX - if os.name == "posix": - if python_build: - # Assume the executable is in the build directory. The - # pyconfig.h file should be in the same directory. Since - # the build directory may not be the source directory, we - # must use "srcdir" from the makefile to find the "Include" - # directory. - if plat_specific: - return _sys_home or project_base - else: - incdir = os.path.join(get_config_var('srcdir'), 'Include') - return os.path.normpath(incdir) - python_dir = 'python' + get_python_version() + build_flags - return os.path.join(prefix, "include", python_dir) - elif os.name == "nt": - if python_build: - # Include both the include and PC dir to ensure we can find - # pyconfig.h - return (os.path.join(prefix, "include") + os.path.pathsep + - os.path.join(prefix, "PC")) - return os.path.join(prefix, "include") - else: - raise DistutilsPlatformError( - "I don't know where Python installs its C header files " - "on platform '%s'" % os.name) - - -def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): - """Return the directory containing the Python library (standard or - site additions). - - If 'plat_specific' is true, return the directory containing - platform-specific modules, i.e. any module from a non-pure-Python - module distribution; otherwise, return the platform-shared library - directory. If 'standard_lib' is true, return the directory - containing standard Python library modules; otherwise, return the - directory for site-specific modules. - - If 'prefix' is supplied, use it instead of sys.base_prefix or - sys.base_exec_prefix -- i.e., ignore 'plat_specific'. - """ - if prefix is None: - if standard_lib: - prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX - else: - prefix = plat_specific and EXEC_PREFIX or PREFIX - - if os.name == "posix": - if plat_specific or standard_lib: - # Platform-specific modules (any module from a non-pure-Python - # module distribution) or standard Python library modules. - libdir = sys.platlibdir - else: - # Pure Python - libdir = "lib" - libpython = os.path.join(prefix, libdir, - "python" + get_python_version()) - if standard_lib: - return libpython - else: - return os.path.join(libpython, "site-packages") - elif os.name == "nt": - if standard_lib: - return os.path.join(prefix, "Lib") - else: - return os.path.join(prefix, "Lib", "site-packages") - else: - raise DistutilsPlatformError( - "I don't know where Python installs its library " - "on platform '%s'" % os.name) - - - -def customize_compiler(compiler): - """Do any platform-specific customization of a CCompiler instance. - - Mainly needed on Unix, so we can plug in the information that - varies across Unices and is stored in Python's Makefile. - """ - if compiler.compiler_type == "unix": - if sys.platform == "darwin": - # Perform first-time customization of compiler-related - # config vars on OS X now that we know we need a compiler. - # This is primarily to support Pythons from binary - # installers. The kind and paths to build tools on - # the user system may vary significantly from the system - # that Python itself was built on. Also the user OS - # version and build tools may not support the same set - # of CPU architectures for universal builds. - global _config_vars - # Use get_config_var() to ensure _config_vars is initialized. - if not get_config_var('CUSTOMIZED_OSX_COMPILER'): - import _osx_support - _osx_support.customize_compiler(_config_vars) - _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' - - (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ - get_config_vars('CC', 'CXX', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') - - if 'CC' in os.environ: - newcc = os.environ['CC'] - if (sys.platform == 'darwin' - and 'LDSHARED' not in os.environ - and ldshared.startswith(cc)): - # On OS X, if CC is overridden, use that as the default - # command for LDSHARED as well - ldshared = newcc + ldshared[len(cc):] - cc = newcc - if 'CXX' in os.environ: - cxx = os.environ['CXX'] - if 'LDSHARED' in os.environ: - ldshared = os.environ['LDSHARED'] - if 'CPP' in os.environ: - cpp = os.environ['CPP'] - else: - cpp = cc + " -E" # not always - if 'LDFLAGS' in os.environ: - ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if 'CFLAGS' in os.environ: - cflags = cflags + ' ' + os.environ['CFLAGS'] - ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if 'CPPFLAGS' in os.environ: - cpp = cpp + ' ' + os.environ['CPPFLAGS'] - cflags = cflags + ' ' + os.environ['CPPFLAGS'] - ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] - if 'AR' in os.environ: - ar = os.environ['AR'] - if 'ARFLAGS' in os.environ: - archiver = ar + ' ' + os.environ['ARFLAGS'] - else: - archiver = ar + ' ' + ar_flags - - cc_cmd = cc + ' ' + cflags - compiler.set_executables( - preprocessor=cpp, - compiler=cc_cmd, - compiler_so=cc_cmd + ' ' + ccshared, - compiler_cxx=cxx, - linker_so=ldshared, - linker_exe=cc, - archiver=archiver) - - compiler.shared_lib_extension = shlib_suffix - - -def get_config_h_filename(): - """Return full pathname of installed pyconfig.h file.""" - if python_build: - if os.name == "nt": - inc_dir = os.path.join(_sys_home or project_base, "PC") - else: - inc_dir = _sys_home or project_base - else: - inc_dir = get_python_inc(plat_specific=1) - - return os.path.join(inc_dir, 'pyconfig.h') - - -def get_makefile_filename(): - """Return full pathname of installed Makefile from the Python build.""" - if python_build: - return os.path.join(_sys_home or project_base, "Makefile") - lib_dir = get_python_lib(plat_specific=0, standard_lib=1) - config_file = 'config-{}{}'.format(get_python_version(), build_flags) - if hasattr(sys.implementation, '_multiarch'): - config_file += '-%s' % sys.implementation._multiarch - return os.path.join(lib_dir, config_file, 'Makefile') - - -def parse_config_h(fp, g=None): - """Parse a config.h-style file. - - A dictionary containing name/value pairs is returned. If an - optional dictionary is passed in as the second argument, it is - used instead of a new dictionary. - """ - if g is None: - g = {} - define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") - undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") - # - while True: - line = fp.readline() - if not line: - break - m = define_rx.match(line) - if m: - n, v = m.group(1, 2) - try: v = int(v) - except ValueError: pass - g[n] = v - else: - m = undef_rx.match(line) - if m: - g[m.group(1)] = 0 - return g - - -# Regexes needed for parsing Makefile (and similar syntaxes, -# like old-style Setup files). -_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") -_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") -_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") - -def parse_makefile(fn, g=None): - """Parse a Makefile-style file. - - A dictionary containing name/value pairs is returned. If an - optional dictionary is passed in as the second argument, it is - used instead of a new dictionary. - """ - from distutils.text_file import TextFile - fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape") - - if g is None: - g = {} - done = {} - notdone = {} - - while True: - line = fp.readline() - if line is None: # eof - break - m = _variable_rx.match(line) - if m: - n, v = m.group(1, 2) - v = v.strip() - # `$$' is a literal `$' in make - tmpv = v.replace('$$', '') - - if "$" in tmpv: - notdone[n] = v - else: - try: - v = int(v) - except ValueError: - # insert literal `$' - done[n] = v.replace('$$', '$') - else: - done[n] = v - - # Variables with a 'PY_' prefix in the makefile. These need to - # be made available without that prefix through sysconfig. - # Special care is needed to ensure that variable expansion works, even - # if the expansion uses the name without a prefix. - renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') - - # do variable interpolation here - while notdone: - for name in list(notdone): - value = notdone[name] - m = _findvar1_rx.search(value) or _findvar2_rx.search(value) - if m: - n = m.group(1) - found = True - if n in done: - item = str(done[n]) - elif n in notdone: - # get it on a subsequent round - found = False - elif n in os.environ: - # do it like make: fall back to environment - item = os.environ[n] - - elif n in renamed_variables: - if name.startswith('PY_') and name[3:] in renamed_variables: - item = "" - - elif 'PY_' + n in notdone: - found = False - - else: - item = str(done['PY_' + n]) - else: - done[n] = item = "" - if found: - after = value[m.end():] - value = value[:m.start()] + item + after - if "$" in after: - notdone[name] = value - else: - try: value = int(value) - except ValueError: - done[name] = value.strip() - else: - done[name] = value - del notdone[name] - - if name.startswith('PY_') \ - and name[3:] in renamed_variables: - - name = name[3:] - if name not in done: - done[name] = value - else: - # bogus variable reference; just drop it since we can't deal - del notdone[name] - - fp.close() - - # strip spurious spaces - for k, v in done.items(): - if isinstance(v, str): - done[k] = v.strip() - - # save the results in the global dictionary - g.update(done) - return g - - -def expand_makefile_vars(s, vars): - """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in - 'string' according to 'vars' (a dictionary mapping variable names to - values). Variables not present in 'vars' are silently expanded to the - empty string. The variable values in 'vars' should not contain further - variable expansions; if 'vars' is the output of 'parse_makefile()', - you're fine. Returns a variable-expanded version of 's'. - """ - - # This algorithm does multiple expansion, so if vars['foo'] contains - # "${bar}", it will expand ${foo} to ${bar}, and then expand - # ${bar}... and so forth. This is fine as long as 'vars' comes from - # 'parse_makefile()', which takes care of such expansions eagerly, - # according to make's variable expansion semantics. - - while True: - m = _findvar1_rx.search(s) or _findvar2_rx.search(s) - if m: - (beg, end) = m.span() - s = s[0:beg] + vars.get(m.group(1)) + s[end:] - else: - break - return s - - -_config_vars = None - -def _init_posix(): - """Initialize the module as appropriate for POSIX systems.""" - # _sysconfigdata is generated at build time, see the sysconfig module - name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME', - '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( - abi=sys.abiflags, - platform=sys.platform, - multiarch=getattr(sys.implementation, '_multiarch', ''), - )) - _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) - build_time_vars = _temp.build_time_vars - global _config_vars - _config_vars = {} - _config_vars.update(build_time_vars) - - -def _init_nt(): - """Initialize the module as appropriate for NT""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - g['EXT_SUFFIX'] = _imp.extension_suffixes()[0] - g['EXE'] = ".exe" - g['VERSION'] = get_python_version().replace(".", "") - g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) - - global _config_vars - _config_vars = g - - -def get_config_vars(*args): - """With no arguments, return a dictionary of all configuration - variables relevant for the current platform. Generally this includes - everything needed to build extensions and install both pure modules and - extensions. On Unix, this means every variable defined in Python's - installed Makefile; on Windows it's a much smaller set. - - With arguments, return a list of values that result from looking up - each argument in the configuration variable dictionary. - """ - global _config_vars - if _config_vars is None: - func = globals().get("_init_" + os.name) - if func: - func() - else: - _config_vars = {} - - # Normalized versions of prefix and exec_prefix are handy to have; - # in fact, these are the standard versions used most places in the - # Distutils. - _config_vars['prefix'] = PREFIX - _config_vars['exec_prefix'] = EXEC_PREFIX - - # For backward compatibility, see issue19555 - SO = _config_vars.get('EXT_SUFFIX') - if SO is not None: - _config_vars['SO'] = SO - - # Always convert srcdir to an absolute path - srcdir = _config_vars.get('srcdir', project_base) - if os.name == 'posix': - if python_build: - # If srcdir is a relative path (typically '.' or '..') - # then it should be interpreted relative to the directory - # containing Makefile. - base = os.path.dirname(get_makefile_filename()) - srcdir = os.path.join(base, srcdir) - else: - # srcdir is not meaningful since the installation is - # spread about the filesystem. We choose the - # directory containing the Makefile since we know it - # exists. - srcdir = os.path.dirname(get_makefile_filename()) - _config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir)) - - # Convert srcdir into an absolute path if it appears necessary. - # Normally it is relative to the build directory. However, during - # testing, for example, we might be running a non-installed python - # from a different directory. - if python_build and os.name == "posix": - base = project_base - if (not os.path.isabs(_config_vars['srcdir']) and - base != os.getcwd()): - # srcdir is relative and we are not in the same directory - # as the executable. Assume executable is in the build - # directory and make srcdir absolute. - srcdir = os.path.join(base, _config_vars['srcdir']) - _config_vars['srcdir'] = os.path.normpath(srcdir) - - # OS X platforms require special customization to handle - # multi-architecture, multi-os-version installers - if sys.platform == 'darwin': - import _osx_support - _osx_support.customize_config_vars(_config_vars) - - if args: - vals = [] - for name in args: - vals.append(_config_vars.get(name)) - return vals - else: - return _config_vars - -def get_config_var(name): - """Return the value of a single variable using the dictionary - returned by 'get_config_vars()'. Equivalent to - get_config_vars().get(name) - """ - if name == 'SO': - import warnings - warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2) - return get_config_vars().get(name) diff --git a/tests/Setup.sample b/tests/Setup.sample deleted file mode 100644 index 36c4290d..00000000 --- a/tests/Setup.sample +++ /dev/null @@ -1,67 +0,0 @@ -# Setup file from the pygame project - -#--StartConfig -SDL = -I/usr/include/SDL -D_REENTRANT -lSDL -FONT = -lSDL_ttf -IMAGE = -lSDL_image -MIXER = -lSDL_mixer -SMPEG = -lsmpeg -PNG = -lpng -JPEG = -ljpeg -SCRAP = -lX11 -PORTMIDI = -lportmidi -PORTTIME = -lporttime -#--EndConfig - -#DEBUG = -C-W -C-Wall -DEBUG = - -#the following modules are optional. you will want to compile -#everything you can, but you can ignore ones you don't have -#dependencies for, just comment them out - -imageext src/imageext.c $(SDL) $(IMAGE) $(PNG) $(JPEG) $(DEBUG) -font src/font.c $(SDL) $(FONT) $(DEBUG) -mixer src/mixer.c $(SDL) $(MIXER) $(DEBUG) -mixer_music src/music.c $(SDL) $(MIXER) $(DEBUG) -_numericsurfarray src/_numericsurfarray.c $(SDL) $(DEBUG) -_numericsndarray src/_numericsndarray.c $(SDL) $(MIXER) $(DEBUG) -movie src/movie.c $(SDL) $(SMPEG) $(DEBUG) -scrap src/scrap.c $(SDL) $(SCRAP) $(DEBUG) -_camera src/_camera.c src/camera_v4l2.c src/camera_v4l.c $(SDL) $(DEBUG) -pypm src/pypm.c $(SDL) $(PORTMIDI) $(PORTTIME) $(DEBUG) - -GFX = src/SDL_gfx/SDL_gfxPrimitives.c -#GFX = src/SDL_gfx/SDL_gfxBlitFunc.c src/SDL_gfx/SDL_gfxPrimitives.c -gfxdraw src/gfxdraw.c $(SDL) $(GFX) $(DEBUG) - - - -#these modules are required for pygame to run. they only require -#SDL as a dependency. these should not be altered - -base src/base.c $(SDL) $(DEBUG) -cdrom src/cdrom.c $(SDL) $(DEBUG) -color src/color.c $(SDL) $(DEBUG) -constants src/constants.c $(SDL) $(DEBUG) -display src/display.c $(SDL) $(DEBUG) -event src/event.c $(SDL) $(DEBUG) -fastevent src/fastevent.c src/fastevents.c $(SDL) $(DEBUG) -key src/key.c $(SDL) $(DEBUG) -mouse src/mouse.c $(SDL) $(DEBUG) -rect src/rect.c $(SDL) $(DEBUG) -rwobject src/rwobject.c $(SDL) $(DEBUG) -surface src/surface.c src/alphablit.c src/surface_fill.c $(SDL) $(DEBUG) -surflock src/surflock.c $(SDL) $(DEBUG) -time src/time.c $(SDL) $(DEBUG) -joystick src/joystick.c $(SDL) $(DEBUG) -draw src/draw.c $(SDL) $(DEBUG) -image src/image.c $(SDL) $(DEBUG) -overlay src/overlay.c $(SDL) $(DEBUG) -transform src/transform.c src/rotozoom.c src/scale2x.c src/scale_mmx.c $(SDL) $(DEBUG) -mask src/mask.c src/bitmask.c $(SDL) $(DEBUG) -bufferproxy src/bufferproxy.c $(SDL) $(DEBUG) -pixelarray src/pixelarray.c $(SDL) $(DEBUG) -_arraysurfarray src/_arraysurfarray.c $(SDL) $(DEBUG) - - diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 5d2e69e3..00000000 --- a/tests/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Test suite for distutils. - -This test suite consists of a collection of test modules in the -distutils.tests package. Each test module has a name starting with -'test' and contains a function test_suite(). The function is expected -to return an initialized unittest.TestSuite instance. - -Tests for the command classes in the distutils.command package are -included in distutils.tests as well, instead of using a separate -distutils.command.tests package, since command identification is done -by import rather than matching pre-defined names. - -""" - -import os -import sys -import unittest -import warnings -from test.support import run_unittest - - -here = os.path.dirname(__file__) or os.curdir - - -def test_suite(): - old_filters = warnings.filters[:] - suite = unittest.TestSuite() - for fn in os.listdir(here): - if fn.startswith("test") and fn.endswith(".py"): - modname = "distutils.tests." + fn[:-3] - __import__(modname) - module = sys.modules[modname] - suite.addTest(module.test_suite()) - # bpo-40055: Save/restore warnings filters to leave them unchanged. - # Importing tests imports docutils which imports pkg_resources which adds a - # warnings filter. - warnings.filters[:] = old_filters - return suite - - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/includetest.rst b/tests/includetest.rst deleted file mode 100644 index d7b4ae38..00000000 --- a/tests/includetest.rst +++ /dev/null @@ -1 +0,0 @@ -This should be included. diff --git a/tests/support.py b/tests/support.py deleted file mode 100644 index 259af882..00000000 --- a/tests/support.py +++ /dev/null @@ -1,209 +0,0 @@ -"""Support code for distutils test cases.""" -import os -import sys -import shutil -import tempfile -import unittest -import sysconfig -from copy import deepcopy -import test.support - -from distutils import log -from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL -from distutils.core import Distribution - - -class LoggingSilencer(object): - - def setUp(self): - super().setUp() - self.threshold = log.set_threshold(log.FATAL) - # catching warnings - # when log will be replaced by logging - # we won't need such monkey-patch anymore - self._old_log = log.Log._log - log.Log._log = self._log - self.logs = [] - - def tearDown(self): - log.set_threshold(self.threshold) - log.Log._log = self._old_log - super().tearDown() - - def _log(self, level, msg, args): - if level not in (DEBUG, INFO, WARN, ERROR, FATAL): - raise ValueError('%s wrong log level' % str(level)) - if not isinstance(msg, str): - raise TypeError("msg should be str, not '%.200s'" - % (type(msg).__name__)) - self.logs.append((level, msg, args)) - - def get_logs(self, *levels): - return [msg % args for level, msg, args - in self.logs if level in levels] - - def clear_logs(self): - self.logs = [] - - -class TempdirManager(object): - """Mix-in class that handles temporary directories for test cases. - - This is intended to be used with unittest.TestCase. - """ - - def setUp(self): - super().setUp() - self.old_cwd = os.getcwd() - self.tempdirs = [] - - def tearDown(self): - # Restore working dir, for Solaris and derivatives, where rmdir() - # on the current directory fails. - os.chdir(self.old_cwd) - super().tearDown() - while self.tempdirs: - tmpdir = self.tempdirs.pop() - test.support.rmtree(tmpdir) - - def mkdtemp(self): - """Create a temporary directory that will be cleaned up. - - Returns the path of the directory. - """ - d = tempfile.mkdtemp() - self.tempdirs.append(d) - return d - - def write_file(self, path, content='xxx'): - """Writes a file in the given path. - - - path can be a string or a sequence. - """ - if isinstance(path, (list, tuple)): - path = os.path.join(*path) - f = open(path, 'w') - try: - f.write(content) - finally: - f.close() - - def create_dist(self, pkg_name='foo', **kw): - """Will generate a test environment. - - This function creates: - - a Distribution instance using keywords - - a temporary directory with a package structure - - It returns the package directory and the distribution - instance. - """ - tmp_dir = self.mkdtemp() - pkg_dir = os.path.join(tmp_dir, pkg_name) - os.mkdir(pkg_dir) - dist = Distribution(attrs=kw) - - return pkg_dir, dist - - -class DummyCommand: - """Class to store options for retrieval via set_undefined_options().""" - - def __init__(self, **kwargs): - for kw, val in kwargs.items(): - setattr(self, kw, val) - - def ensure_finalized(self): - pass - - -class EnvironGuard(object): - - def setUp(self): - super(EnvironGuard, self).setUp() - self.old_environ = deepcopy(os.environ) - - def tearDown(self): - for key, value in self.old_environ.items(): - if os.environ.get(key) != value: - os.environ[key] = value - - for key in tuple(os.environ.keys()): - if key not in self.old_environ: - del os.environ[key] - - super(EnvironGuard, self).tearDown() - - -def copy_xxmodule_c(directory): - """Helper for tests that need the xxmodule.c source file. - - Example use: - - def test_compile(self): - copy_xxmodule_c(self.tmpdir) - self.assertIn('xxmodule.c', os.listdir(self.tmpdir)) - - If the source file can be found, it will be copied to *directory*. If not, - the test will be skipped. Errors during copy are not caught. - """ - filename = _get_xxmodule_path() - if filename is None: - raise unittest.SkipTest('cannot find xxmodule.c (test must run in ' - 'the python build dir)') - shutil.copy(filename, directory) - - -def _get_xxmodule_path(): - srcdir = sysconfig.get_config_var('srcdir') - candidates = [ - # use installed copy if available - os.path.join(os.path.dirname(__file__), 'xxmodule.c'), - # otherwise try using copy from build directory - os.path.join(srcdir, 'Modules', 'xxmodule.c'), - # srcdir mysteriously can be $srcdir/Lib/distutils/tests when - # this file is run from its parent directory, so walk up the - # tree to find the real srcdir - os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'), - ] - for path in candidates: - if os.path.exists(path): - return path - - -def fixup_build_ext(cmd): - """Function needed to make build_ext tests pass. - - When Python was built with --enable-shared on Unix, -L. is not enough to - find libpython.so, because regrtest runs in a tempdir, not in the - source directory where the .so lives. - - When Python was built with in debug mode on Windows, build_ext commands - need their debug attribute set, and it is not done automatically for - some reason. - - This function handles both of these things. Example use: - - cmd = build_ext(dist) - support.fixup_build_ext(cmd) - cmd.ensure_finalized() - - Unlike most other Unix platforms, Mac OS X embeds absolute paths - to shared libraries into executables, so the fixup is not needed there. - """ - if os.name == 'nt': - cmd.debug = sys.executable.endswith('_d.exe') - elif sysconfig.get_config_var('Py_ENABLE_SHARED'): - # To further add to the shared builds fun on Unix, we can't just add - # library_dirs to the Extension() instance because that doesn't get - # plumbed through to the final compiler command. - runshared = sysconfig.get_config_var('RUNSHARED') - if runshared is None: - cmd.library_dirs = ['.'] - else: - if sys.platform == 'darwin': - cmd.library_dirs = [] - else: - name, equals, value = runshared.partition('=') - cmd.library_dirs = [d for d in value.split(os.pathsep) if d] diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py deleted file mode 100644 index e9aad0e4..00000000 --- a/tests/test_archive_util.py +++ /dev/null @@ -1,394 +0,0 @@ -# -*- coding: utf-8 -*- -"""Tests for distutils.archive_util.""" -import unittest -import os -import sys -import tarfile -from os.path import splitdrive -import warnings - -from distutils import archive_util -from distutils.archive_util import (check_archive_formats, make_tarball, - make_zipfile, make_archive, - ARCHIVE_FORMATS) -from distutils.spawn import find_executable, spawn -from distutils.tests import support -from test.support import check_warnings, run_unittest, patch, change_cwd - -try: - import grp - import pwd - UID_GID_SUPPORT = True -except ImportError: - UID_GID_SUPPORT = False - -try: - import zipfile - ZIP_SUPPORT = True -except ImportError: - ZIP_SUPPORT = find_executable('zip') - -try: - import zlib - ZLIB_SUPPORT = True -except ImportError: - ZLIB_SUPPORT = False - -try: - import bz2 -except ImportError: - bz2 = None - -try: - import lzma -except ImportError: - lzma = None - -def can_fs_encode(filename): - """ - Return True if the filename can be saved in the file system. - """ - if os.path.supports_unicode_filenames: - return True - try: - filename.encode(sys.getfilesystemencoding()) - except UnicodeEncodeError: - return False - return True - - -class ArchiveUtilTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_make_tarball(self, name='archive'): - # creating something to tar - tmpdir = self._create_files() - self._make_tarball(tmpdir, name, '.tar.gz') - # trying an uncompressed one - self._make_tarball(tmpdir, name, '.tar', compress=None) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_make_tarball_gzip(self): - tmpdir = self._create_files() - self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip') - - @unittest.skipUnless(bz2, 'Need bz2 support to run') - def test_make_tarball_bzip2(self): - tmpdir = self._create_files() - self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2') - - @unittest.skipUnless(lzma, 'Need lzma support to run') - def test_make_tarball_xz(self): - tmpdir = self._create_files() - self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz') - - @unittest.skipUnless(can_fs_encode('Ã¥rchiv'), - 'File system cannot handle this filename') - def test_make_tarball_latin1(self): - """ - Mirror test_make_tarball, except filename contains latin characters. - """ - self.test_make_tarball('Ã¥rchiv') # note this isn't a real word - - @unittest.skipUnless(can_fs_encode('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–'), - 'File system cannot handle this filename') - def test_make_tarball_extended(self): - """ - Mirror test_make_tarball, except filename contains extended - characters outside the latin charset. - """ - self.test_make_tarball('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–') # japanese for archive - - def _make_tarball(self, tmpdir, target_name, suffix, **kwargs): - tmpdir2 = self.mkdtemp() - unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], - "source and target should be on same drive") - - base_name = os.path.join(tmpdir2, target_name) - - # working with relative paths to avoid tar warnings - with change_cwd(tmpdir): - make_tarball(splitdrive(base_name)[1], 'dist', **kwargs) - - # check if the compressed tarball was created - tarball = base_name + suffix - self.assertTrue(os.path.exists(tarball)) - self.assertEqual(self._tarinfo(tarball), self._created_files) - - def _tarinfo(self, path): - tar = tarfile.open(path) - try: - names = tar.getnames() - names.sort() - return names - finally: - tar.close() - - _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', - 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] - _created_files = [p.rstrip('/') for p in _zip_created_files] - - def _create_files(self): - # creating something to tar - tmpdir = self.mkdtemp() - dist = os.path.join(tmpdir, 'dist') - os.mkdir(dist) - self.write_file([dist, 'file1'], 'xxx') - self.write_file([dist, 'file2'], 'xxx') - os.mkdir(os.path.join(dist, 'sub')) - self.write_file([dist, 'sub', 'file3'], 'xxx') - os.mkdir(os.path.join(dist, 'sub2')) - return tmpdir - - @unittest.skipUnless(find_executable('tar') and find_executable('gzip') - and ZLIB_SUPPORT, - 'Need the tar, gzip and zlib command to run') - def test_tarfile_vs_tar(self): - tmpdir = self._create_files() - tmpdir2 = self.mkdtemp() - base_name = os.path.join(tmpdir2, 'archive') - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - make_tarball(base_name, 'dist') - finally: - os.chdir(old_dir) - - # check if the compressed tarball was created - tarball = base_name + '.tar.gz' - self.assertTrue(os.path.exists(tarball)) - - # now create another tarball using `tar` - tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') - tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist'] - gzip_cmd = ['gzip', '-f', '-9', 'archive2.tar'] - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - spawn(tar_cmd) - spawn(gzip_cmd) - finally: - os.chdir(old_dir) - - self.assertTrue(os.path.exists(tarball2)) - # let's compare both tarballs - self.assertEqual(self._tarinfo(tarball), self._created_files) - self.assertEqual(self._tarinfo(tarball2), self._created_files) - - # trying an uncompressed one - base_name = os.path.join(tmpdir2, 'archive') - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - make_tarball(base_name, 'dist', compress=None) - finally: - os.chdir(old_dir) - tarball = base_name + '.tar' - self.assertTrue(os.path.exists(tarball)) - - # now for a dry_run - base_name = os.path.join(tmpdir2, 'archive') - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - make_tarball(base_name, 'dist', compress=None, dry_run=True) - finally: - os.chdir(old_dir) - tarball = base_name + '.tar' - self.assertTrue(os.path.exists(tarball)) - - @unittest.skipUnless(find_executable('compress'), - 'The compress program is required') - def test_compress_deprecated(self): - tmpdir = self._create_files() - base_name = os.path.join(self.mkdtemp(), 'archive') - - # using compress and testing the PendingDeprecationWarning - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - with check_warnings() as w: - warnings.simplefilter("always") - make_tarball(base_name, 'dist', compress='compress') - finally: - os.chdir(old_dir) - tarball = base_name + '.tar.Z' - self.assertTrue(os.path.exists(tarball)) - self.assertEqual(len(w.warnings), 1) - - # same test with dry_run - os.remove(tarball) - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - with check_warnings() as w: - warnings.simplefilter("always") - make_tarball(base_name, 'dist', compress='compress', - dry_run=True) - finally: - os.chdir(old_dir) - self.assertFalse(os.path.exists(tarball)) - self.assertEqual(len(w.warnings), 1) - - @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, - 'Need zip and zlib support to run') - def test_make_zipfile(self): - # creating something to tar - tmpdir = self._create_files() - base_name = os.path.join(self.mkdtemp(), 'archive') - with change_cwd(tmpdir): - make_zipfile(base_name, 'dist') - - # check if the compressed tarball was created - tarball = base_name + '.zip' - self.assertTrue(os.path.exists(tarball)) - with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), self._zip_created_files) - - @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') - def test_make_zipfile_no_zlib(self): - patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError - - called = [] - zipfile_class = zipfile.ZipFile - def fake_zipfile(*a, **kw): - if kw.get('compression', None) == zipfile.ZIP_STORED: - called.append((a, kw)) - return zipfile_class(*a, **kw) - - patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile) - - # create something to tar and compress - tmpdir = self._create_files() - base_name = os.path.join(self.mkdtemp(), 'archive') - with change_cwd(tmpdir): - make_zipfile(base_name, 'dist') - - tarball = base_name + '.zip' - self.assertEqual(called, - [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) - self.assertTrue(os.path.exists(tarball)) - with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), self._zip_created_files) - - def test_check_archive_formats(self): - self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), - 'xxx') - self.assertIsNone(check_archive_formats(['gztar', 'bztar', 'xztar', - 'ztar', 'tar', 'zip'])) - - def test_make_archive(self): - tmpdir = self.mkdtemp() - base_name = os.path.join(tmpdir, 'archive') - self.assertRaises(ValueError, make_archive, base_name, 'xxx') - - def test_make_archive_cwd(self): - current_dir = os.getcwd() - def _breaks(*args, **kw): - raise RuntimeError() - ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file') - try: - try: - make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) - except: - pass - self.assertEqual(os.getcwd(), current_dir) - finally: - del ARCHIVE_FORMATS['xxx'] - - def test_make_archive_tar(self): - base_dir = self._create_files() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'tar', base_dir, 'dist') - self.assertTrue(os.path.exists(res)) - self.assertEqual(os.path.basename(res), 'archive.tar') - self.assertEqual(self._tarinfo(res), self._created_files) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_make_archive_gztar(self): - base_dir = self._create_files() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'gztar', base_dir, 'dist') - self.assertTrue(os.path.exists(res)) - self.assertEqual(os.path.basename(res), 'archive.tar.gz') - self.assertEqual(self._tarinfo(res), self._created_files) - - @unittest.skipUnless(bz2, 'Need bz2 support to run') - def test_make_archive_bztar(self): - base_dir = self._create_files() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'bztar', base_dir, 'dist') - self.assertTrue(os.path.exists(res)) - self.assertEqual(os.path.basename(res), 'archive.tar.bz2') - self.assertEqual(self._tarinfo(res), self._created_files) - - @unittest.skipUnless(lzma, 'Need xz support to run') - def test_make_archive_xztar(self): - base_dir = self._create_files() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'xztar', base_dir, 'dist') - self.assertTrue(os.path.exists(res)) - self.assertEqual(os.path.basename(res), 'archive.tar.xz') - self.assertEqual(self._tarinfo(res), self._created_files) - - def test_make_archive_owner_group(self): - # testing make_archive with owner and group, with various combinations - # this works even if there's not gid/uid support - if UID_GID_SUPPORT: - group = grp.getgrgid(0)[0] - owner = pwd.getpwuid(0)[0] - else: - group = owner = 'root' - - base_dir = self._create_files() - root_dir = self.mkdtemp() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, - group=group) - self.assertTrue(os.path.exists(res)) - - res = make_archive(base_name, 'zip', root_dir, base_dir) - self.assertTrue(os.path.exists(res)) - - res = make_archive(base_name, 'tar', root_dir, base_dir, - owner=owner, group=group) - self.assertTrue(os.path.exists(res)) - - res = make_archive(base_name, 'tar', root_dir, base_dir, - owner='kjhkjhkjg', group='oihohoh') - self.assertTrue(os.path.exists(res)) - - @unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib") - @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") - def test_tarfile_root_owner(self): - tmpdir = self._create_files() - base_name = os.path.join(self.mkdtemp(), 'archive') - old_dir = os.getcwd() - os.chdir(tmpdir) - group = grp.getgrgid(0)[0] - owner = pwd.getpwuid(0)[0] - try: - archive_name = make_tarball(base_name, 'dist', compress=None, - owner=owner, group=group) - finally: - os.chdir(old_dir) - - # check if the compressed tarball was created - self.assertTrue(os.path.exists(archive_name)) - - # now checks the rights - archive = tarfile.open(archive_name) - try: - for member in archive.getmembers(): - self.assertEqual(member.uid, 0) - self.assertEqual(member.gid, 0) - finally: - archive.close() - -def test_suite(): - return unittest.makeSuite(ArchiveUtilTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_bdist.py b/tests/test_bdist.py deleted file mode 100644 index 130d8bf1..00000000 --- a/tests/test_bdist.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Tests for distutils.command.bdist.""" -import os -import unittest -from test.support import run_unittest -import warnings - -from distutils.command.bdist import bdist -from distutils.tests import support - - -class BuildTestCase(support.TempdirManager, - unittest.TestCase): - - def test_formats(self): - # let's create a command and make sure - # we can set the format - dist = self.create_dist()[1] - cmd = bdist(dist) - cmd.formats = ['msi'] - cmd.ensure_finalized() - self.assertEqual(cmd.formats, ['msi']) - - # what formats does bdist offer? - formats = ['bztar', 'gztar', 'msi', 'rpm', 'tar', - 'wininst', 'xztar', 'zip', 'ztar'] - found = sorted(cmd.format_command) - self.assertEqual(found, formats) - - def test_skip_build(self): - # bug #10946: bdist --skip-build should trickle down to subcommands - dist = self.create_dist()[1] - cmd = bdist(dist) - cmd.skip_build = 1 - cmd.ensure_finalized() - dist.command_obj['bdist'] = cmd - - names = ['bdist_dumb', 'bdist_wininst'] # bdist_rpm does not support --skip-build - if os.name == 'nt': - names.append('bdist_msi') - - for name in names: - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', 'bdist_wininst command is deprecated', - DeprecationWarning) - subcmd = cmd.get_finalized_command(name) - if getattr(subcmd, '_unsupported', False): - # command is not supported on this build - continue - self.assertTrue(subcmd.skip_build, - '%s should take --skip-build from bdist' % name) - - -def test_suite(): - return unittest.makeSuite(BuildTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_bdist_dumb.py b/tests/test_bdist_dumb.py deleted file mode 100644 index 01a233bc..00000000 --- a/tests/test_bdist_dumb.py +++ /dev/null @@ -1,97 +0,0 @@ -"""Tests for distutils.command.bdist_dumb.""" - -import os -import sys -import zipfile -import unittest -from test.support import run_unittest - -from distutils.core import Distribution -from distutils.command.bdist_dumb import bdist_dumb -from distutils.tests import support - -SETUP_PY = """\ -from distutils.core import setup -import foo - -setup(name='foo', version='0.1', py_modules=['foo'], - url='xxx', author='xxx', author_email='xxx') - -""" - -try: - import zlib - ZLIB_SUPPORT = True -except ImportError: - ZLIB_SUPPORT = False - - -class BuildDumbTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def setUp(self): - super(BuildDumbTestCase, self).setUp() - self.old_location = os.getcwd() - self.old_sys_argv = sys.argv, sys.argv[:] - - def tearDown(self): - os.chdir(self.old_location) - sys.argv = self.old_sys_argv[0] - sys.argv[:] = self.old_sys_argv[1] - super(BuildDumbTestCase, self).tearDown() - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_simple_built(self): - - # let's create a simple package - tmp_dir = self.mkdtemp() - pkg_dir = os.path.join(tmp_dir, 'foo') - os.mkdir(pkg_dir) - self.write_file((pkg_dir, 'setup.py'), SETUP_PY) - self.write_file((pkg_dir, 'foo.py'), '#') - self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') - self.write_file((pkg_dir, 'README'), '') - - dist = Distribution({'name': 'foo', 'version': '0.1', - 'py_modules': ['foo'], - 'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx'}) - dist.script_name = 'setup.py' - os.chdir(pkg_dir) - - sys.argv = ['setup.py'] - cmd = bdist_dumb(dist) - - # so the output is the same no matter - # what is the platform - cmd.format = 'zip' - - cmd.ensure_finalized() - cmd.run() - - # see what we have - dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name) - - self.assertEqual(dist_created, [base]) - - # now let's check what we have in the zip file - fp = zipfile.ZipFile(os.path.join('dist', base)) - try: - contents = fp.namelist() - finally: - fp.close() - - contents = sorted(filter(None, map(os.path.basename, contents))) - wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] - if not sys.dont_write_bytecode: - wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) - self.assertEqual(contents, sorted(wanted)) - -def test_suite(): - return unittest.makeSuite(BuildDumbTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py deleted file mode 100644 index 418e60ec..00000000 --- a/tests/test_bdist_msi.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Tests for distutils.command.bdist_msi.""" -import sys -import unittest -from test.support import run_unittest, check_warnings -from distutils.tests import support - - -@unittest.skipUnless(sys.platform == 'win32', 'these tests require Windows') -class BDistMSITestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_minimal(self): - # minimal test XXX need more tests - from distutils.command.bdist_msi import bdist_msi - project_dir, dist = self.create_dist() - with check_warnings(("", DeprecationWarning)): - cmd = bdist_msi(dist) - cmd.ensure_finalized() - - -def test_suite(): - return unittest.makeSuite(BDistMSITestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_bdist_rpm.py b/tests/test_bdist_rpm.py deleted file mode 100644 index 6453a02b..00000000 --- a/tests/test_bdist_rpm.py +++ /dev/null @@ -1,135 +0,0 @@ -"""Tests for distutils.command.bdist_rpm.""" - -import unittest -import sys -import os -from test.support import run_unittest, requires_zlib - -from distutils.core import Distribution -from distutils.command.bdist_rpm import bdist_rpm -from distutils.tests import support -from distutils.spawn import find_executable - -SETUP_PY = """\ -from distutils.core import setup -import foo - -setup(name='foo', version='0.1', py_modules=['foo'], - url='xxx', author='xxx', author_email='xxx') - -""" - -class BuildRpmTestCase(support.TempdirManager, - support.EnvironGuard, - support.LoggingSilencer, - unittest.TestCase): - - def setUp(self): - try: - sys.executable.encode("UTF-8") - except UnicodeEncodeError: - raise unittest.SkipTest("sys.executable is not encodable to UTF-8") - - super(BuildRpmTestCase, self).setUp() - self.old_location = os.getcwd() - self.old_sys_argv = sys.argv, sys.argv[:] - - def tearDown(self): - os.chdir(self.old_location) - sys.argv = self.old_sys_argv[0] - sys.argv[:] = self.old_sys_argv[1] - super(BuildRpmTestCase, self).tearDown() - - # XXX I am unable yet to make this test work without - # spurious sdtout/stderr output under Mac OS X - @unittest.skipUnless(sys.platform.startswith('linux'), - 'spurious sdtout/stderr output under Mac OS X') - @requires_zlib - @unittest.skipIf(find_executable('rpm') is None, - 'the rpm command is not found') - @unittest.skipIf(find_executable('rpmbuild') is None, - 'the rpmbuild command is not found') - def test_quiet(self): - # let's create a package - tmp_dir = self.mkdtemp() - os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation - pkg_dir = os.path.join(tmp_dir, 'foo') - os.mkdir(pkg_dir) - self.write_file((pkg_dir, 'setup.py'), SETUP_PY) - self.write_file((pkg_dir, 'foo.py'), '#') - self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') - self.write_file((pkg_dir, 'README'), '') - - dist = Distribution({'name': 'foo', 'version': '0.1', - 'py_modules': ['foo'], - 'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx'}) - dist.script_name = 'setup.py' - os.chdir(pkg_dir) - - sys.argv = ['setup.py'] - cmd = bdist_rpm(dist) - cmd.fix_python = True - - # running in quiet mode - cmd.quiet = 1 - cmd.ensure_finalized() - cmd.run() - - dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assertIn('foo-0.1-1.noarch.rpm', dist_created) - - # bug #2945: upload ignores bdist_rpm files - self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) - self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) - - # XXX I am unable yet to make this test work without - # spurious sdtout/stderr output under Mac OS X - @unittest.skipUnless(sys.platform.startswith('linux'), - 'spurious sdtout/stderr output under Mac OS X') - @requires_zlib - # http://bugs.python.org/issue1533164 - @unittest.skipIf(find_executable('rpm') is None, - 'the rpm command is not found') - @unittest.skipIf(find_executable('rpmbuild') is None, - 'the rpmbuild command is not found') - def test_no_optimize_flag(self): - # let's create a package that breaks bdist_rpm - tmp_dir = self.mkdtemp() - os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation - pkg_dir = os.path.join(tmp_dir, 'foo') - os.mkdir(pkg_dir) - self.write_file((pkg_dir, 'setup.py'), SETUP_PY) - self.write_file((pkg_dir, 'foo.py'), '#') - self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') - self.write_file((pkg_dir, 'README'), '') - - dist = Distribution({'name': 'foo', 'version': '0.1', - 'py_modules': ['foo'], - 'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx'}) - dist.script_name = 'setup.py' - os.chdir(pkg_dir) - - sys.argv = ['setup.py'] - cmd = bdist_rpm(dist) - cmd.fix_python = True - - cmd.quiet = 1 - cmd.ensure_finalized() - cmd.run() - - dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assertIn('foo-0.1-1.noarch.rpm', dist_created) - - # bug #2945: upload ignores bdist_rpm files - self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) - self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) - - os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) - -def test_suite(): - return unittest.makeSuite(BuildRpmTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py deleted file mode 100644 index 5c3d025d..00000000 --- a/tests/test_bdist_wininst.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Tests for distutils.command.bdist_wininst.""" -import sys -import platform -import unittest -from test.support import run_unittest, check_warnings - -from distutils.command.bdist_wininst import bdist_wininst -from distutils.tests import support - -@unittest.skipIf(sys.platform == 'win32' and platform.machine() == 'ARM64', - 'bdist_wininst is not supported in this install') -@unittest.skipIf(getattr(bdist_wininst, '_unsupported', False), - 'bdist_wininst is not supported in this install') -class BuildWinInstTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_get_exe_bytes(self): - - # issue5731: command was broken on non-windows platforms - # this test makes sure it works now for every platform - # let's create a command - pkg_pth, dist = self.create_dist() - with check_warnings(("", DeprecationWarning)): - cmd = bdist_wininst(dist) - cmd.ensure_finalized() - - # let's run the code that finds the right wininst*.exe file - # and make sure it finds it and returns its content - # no matter what platform we have - exe_file = cmd.get_exe_bytes() - self.assertGreater(len(exe_file), 10) - -def test_suite(): - return unittest.makeSuite(BuildWinInstTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_build.py b/tests/test_build.py deleted file mode 100644 index b020a5ba..00000000 --- a/tests/test_build.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Tests for distutils.command.build.""" -import unittest -import os -import sys -from test.support import run_unittest - -from distutils.command.build import build -from distutils.tests import support -from sysconfig import get_platform - -class BuildTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_finalize_options(self): - pkg_dir, dist = self.create_dist() - cmd = build(dist) - cmd.finalize_options() - - # if not specified, plat_name gets the current platform - self.assertEqual(cmd.plat_name, get_platform()) - - # build_purelib is build + lib - wanted = os.path.join(cmd.build_base, 'lib') - self.assertEqual(cmd.build_purelib, wanted) - - # build_platlib is 'build/lib.platform-x.x[-pydebug]' - # examples: - # build/lib.macosx-10.3-i386-2.7 - plat_spec = '.%s-%d.%d' % (cmd.plat_name, *sys.version_info[:2]) - if hasattr(sys, 'gettotalrefcount'): - self.assertTrue(cmd.build_platlib.endswith('-pydebug')) - plat_spec += '-pydebug' - wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) - self.assertEqual(cmd.build_platlib, wanted) - - # by default, build_lib = build_purelib - self.assertEqual(cmd.build_lib, cmd.build_purelib) - - # build_temp is build/temp. - wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) - self.assertEqual(cmd.build_temp, wanted) - - # build_scripts is build/scripts-x.x - wanted = os.path.join(cmd.build_base, - 'scripts-%d.%d' % sys.version_info[:2]) - self.assertEqual(cmd.build_scripts, wanted) - - # executable is os.path.normpath(sys.executable) - self.assertEqual(cmd.executable, os.path.normpath(sys.executable)) - -def test_suite(): - return unittest.makeSuite(BuildTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_build_clib.py b/tests/test_build_clib.py deleted file mode 100644 index abd83137..00000000 --- a/tests/test_build_clib.py +++ /dev/null @@ -1,134 +0,0 @@ -"""Tests for distutils.command.build_clib.""" -import unittest -import os -import sys - -from test.support import run_unittest, missing_compiler_executable - -from distutils.command.build_clib import build_clib -from distutils.errors import DistutilsSetupError -from distutils.tests import support - -class BuildCLibTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_check_library_dist(self): - pkg_dir, dist = self.create_dist() - cmd = build_clib(dist) - - # 'libraries' option must be a list - self.assertRaises(DistutilsSetupError, cmd.check_library_list, 'foo') - - # each element of 'libraries' must a 2-tuple - self.assertRaises(DistutilsSetupError, cmd.check_library_list, - ['foo1', 'foo2']) - - # first element of each tuple in 'libraries' - # must be a string (the library name) - self.assertRaises(DistutilsSetupError, cmd.check_library_list, - [(1, 'foo1'), ('name', 'foo2')]) - - # library name may not contain directory separators - self.assertRaises(DistutilsSetupError, cmd.check_library_list, - [('name', 'foo1'), - ('another/name', 'foo2')]) - - # second element of each tuple must be a dictionary (build info) - self.assertRaises(DistutilsSetupError, cmd.check_library_list, - [('name', {}), - ('another', 'foo2')]) - - # those work - libs = [('name', {}), ('name', {'ok': 'good'})] - cmd.check_library_list(libs) - - def test_get_source_files(self): - pkg_dir, dist = self.create_dist() - cmd = build_clib(dist) - - # "in 'libraries' option 'sources' must be present and must be - # a list of source filenames - cmd.libraries = [('name', {})] - self.assertRaises(DistutilsSetupError, cmd.get_source_files) - - cmd.libraries = [('name', {'sources': 1})] - self.assertRaises(DistutilsSetupError, cmd.get_source_files) - - cmd.libraries = [('name', {'sources': ['a', 'b']})] - self.assertEqual(cmd.get_source_files(), ['a', 'b']) - - cmd.libraries = [('name', {'sources': ('a', 'b')})] - self.assertEqual(cmd.get_source_files(), ['a', 'b']) - - cmd.libraries = [('name', {'sources': ('a', 'b')}), - ('name2', {'sources': ['c', 'd']})] - self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd']) - - def test_build_libraries(self): - - pkg_dir, dist = self.create_dist() - cmd = build_clib(dist) - class FakeCompiler: - def compile(*args, **kw): - pass - create_static_lib = compile - - cmd.compiler = FakeCompiler() - - # build_libraries is also doing a bit of typo checking - lib = [('name', {'sources': 'notvalid'})] - self.assertRaises(DistutilsSetupError, cmd.build_libraries, lib) - - lib = [('name', {'sources': list()})] - cmd.build_libraries(lib) - - lib = [('name', {'sources': tuple()})] - cmd.build_libraries(lib) - - def test_finalize_options(self): - pkg_dir, dist = self.create_dist() - cmd = build_clib(dist) - - cmd.include_dirs = 'one-dir' - cmd.finalize_options() - self.assertEqual(cmd.include_dirs, ['one-dir']) - - cmd.include_dirs = None - cmd.finalize_options() - self.assertEqual(cmd.include_dirs, []) - - cmd.distribution.libraries = 'WONTWORK' - self.assertRaises(DistutilsSetupError, cmd.finalize_options) - - @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") - def test_run(self): - pkg_dir, dist = self.create_dist() - cmd = build_clib(dist) - - foo_c = os.path.join(pkg_dir, 'foo.c') - self.write_file(foo_c, 'int main(void) { return 1;}\n') - cmd.libraries = [('foo', {'sources': [foo_c]})] - - build_temp = os.path.join(pkg_dir, 'build') - os.mkdir(build_temp) - cmd.build_temp = build_temp - cmd.build_clib = build_temp - - # Before we run the command, we want to make sure - # all commands are present on the system. - ccmd = missing_compiler_executable() - if ccmd is not None: - self.skipTest('The %r command is not found' % ccmd) - - # this should work - cmd.run() - - # let's check the result - self.assertIn('libfoo.a', os.listdir(build_temp)) - -def test_suite(): - return unittest.makeSuite(BuildCLibTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py deleted file mode 100644 index 5e47e077..00000000 --- a/tests/test_build_ext.py +++ /dev/null @@ -1,545 +0,0 @@ -import sys -import os -from io import StringIO -import textwrap - -from distutils.core import Distribution -from distutils.command.build_ext import build_ext -from distutils import sysconfig -from distutils.tests.support import (TempdirManager, LoggingSilencer, - copy_xxmodule_c, fixup_build_ext) -from distutils.extension import Extension -from distutils.errors import ( - CompileError, DistutilsPlatformError, DistutilsSetupError, - UnknownFileError) - -import unittest -from test import support -from test.support.script_helper import assert_python_ok - -# http://bugs.python.org/issue4373 -# Don't load the xx module more than once. -ALREADY_TESTED = False - - -class BuildExtTestCase(TempdirManager, - LoggingSilencer, - unittest.TestCase): - def setUp(self): - # Create a simple test environment - super(BuildExtTestCase, self).setUp() - self.tmp_dir = self.mkdtemp() - import site - self.old_user_base = site.USER_BASE - site.USER_BASE = self.mkdtemp() - from distutils.command import build_ext - build_ext.USER_BASE = site.USER_BASE - - # bpo-30132: On Windows, a .pdb file may be created in the current - # working directory. Create a temporary working directory to cleanup - # everything at the end of the test. - change_cwd = support.change_cwd(self.tmp_dir) - change_cwd.__enter__() - self.addCleanup(change_cwd.__exit__, None, None, None) - - def tearDown(self): - import site - site.USER_BASE = self.old_user_base - from distutils.command import build_ext - build_ext.USER_BASE = self.old_user_base - super(BuildExtTestCase, self).tearDown() - - def build_ext(self, *args, **kwargs): - return build_ext(*args, **kwargs) - - def test_build_ext(self): - cmd = support.missing_compiler_executable() - if cmd is not None: - self.skipTest('The %r command is not found' % cmd) - global ALREADY_TESTED - copy_xxmodule_c(self.tmp_dir) - xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') - xx_ext = Extension('xx', [xx_c]) - dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) - dist.package_dir = self.tmp_dir - cmd = self.build_ext(dist) - fixup_build_ext(cmd) - cmd.build_lib = self.tmp_dir - cmd.build_temp = self.tmp_dir - - old_stdout = sys.stdout - if not support.verbose: - # silence compiler output - sys.stdout = StringIO() - try: - cmd.ensure_finalized() - cmd.run() - finally: - sys.stdout = old_stdout - - if ALREADY_TESTED: - self.skipTest('Already tested in %s' % ALREADY_TESTED) - else: - ALREADY_TESTED = type(self).__name__ - - code = textwrap.dedent(f""" - tmp_dir = {self.tmp_dir!r} - - import sys - import unittest - from test import support - - sys.path.insert(0, tmp_dir) - import xx - - class Tests(unittest.TestCase): - def test_xx(self): - for attr in ('error', 'foo', 'new', 'roj'): - self.assertTrue(hasattr(xx, attr)) - - self.assertEqual(xx.foo(2, 5), 7) - self.assertEqual(xx.foo(13,15), 28) - self.assertEqual(xx.new().demo(), None) - if support.HAVE_DOCSTRINGS: - doc = 'This is a template module just for instruction.' - self.assertEqual(xx.__doc__, doc) - self.assertIsInstance(xx.Null(), xx.Null) - self.assertIsInstance(xx.Str(), xx.Str) - - - unittest.main() - """) - assert_python_ok('-c', code) - - def test_solaris_enable_shared(self): - dist = Distribution({'name': 'xx'}) - cmd = self.build_ext(dist) - old = sys.platform - - sys.platform = 'sunos' # fooling finalize_options - from distutils.sysconfig import _config_vars - old_var = _config_vars.get('Py_ENABLE_SHARED') - _config_vars['Py_ENABLE_SHARED'] = 1 - try: - cmd.ensure_finalized() - finally: - sys.platform = old - if old_var is None: - del _config_vars['Py_ENABLE_SHARED'] - else: - _config_vars['Py_ENABLE_SHARED'] = old_var - - # make sure we get some library dirs under solaris - self.assertGreater(len(cmd.library_dirs), 0) - - def test_user_site(self): - import site - dist = Distribution({'name': 'xx'}) - cmd = self.build_ext(dist) - - # making sure the user option is there - options = [name for name, short, lable in - cmd.user_options] - self.assertIn('user', options) - - # setting a value - cmd.user = 1 - - # setting user based lib and include - lib = os.path.join(site.USER_BASE, 'lib') - incl = os.path.join(site.USER_BASE, 'include') - os.mkdir(lib) - os.mkdir(incl) - - # let's run finalize - cmd.ensure_finalized() - - # see if include_dirs and library_dirs - # were set - self.assertIn(lib, cmd.library_dirs) - self.assertIn(lib, cmd.rpath) - self.assertIn(incl, cmd.include_dirs) - - def test_optional_extension(self): - - # this extension will fail, but let's ignore this failure - # with the optional argument. - modules = [Extension('foo', ['xxx'], optional=False)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = self.build_ext(dist) - cmd.ensure_finalized() - self.assertRaises((UnknownFileError, CompileError), - cmd.run) # should raise an error - - modules = [Extension('foo', ['xxx'], optional=True)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = self.build_ext(dist) - cmd.ensure_finalized() - cmd.run() # should pass - - def test_finalize_options(self): - # Make sure Python's include directories (for Python.h, pyconfig.h, - # etc.) are in the include search path. - modules = [Extension('foo', ['xxx'], optional=False)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = self.build_ext(dist) - cmd.finalize_options() - - py_include = sysconfig.get_python_inc() - for p in py_include.split(os.path.pathsep): - self.assertIn(p, cmd.include_dirs) - - plat_py_include = sysconfig.get_python_inc(plat_specific=1) - for p in plat_py_include.split(os.path.pathsep): - self.assertIn(p, cmd.include_dirs) - - # make sure cmd.libraries is turned into a list - # if it's a string - cmd = self.build_ext(dist) - cmd.libraries = 'my_lib, other_lib lastlib' - cmd.finalize_options() - self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib']) - - # make sure cmd.library_dirs is turned into a list - # if it's a string - cmd = self.build_ext(dist) - cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep - cmd.finalize_options() - self.assertIn('my_lib_dir', cmd.library_dirs) - self.assertIn('other_lib_dir', cmd.library_dirs) - - # make sure rpath is turned into a list - # if it's a string - cmd = self.build_ext(dist) - cmd.rpath = 'one%stwo' % os.pathsep - cmd.finalize_options() - self.assertEqual(cmd.rpath, ['one', 'two']) - - # make sure cmd.link_objects is turned into a list - # if it's a string - cmd = build_ext(dist) - cmd.link_objects = 'one two,three' - cmd.finalize_options() - self.assertEqual(cmd.link_objects, ['one', 'two', 'three']) - - # XXX more tests to perform for win32 - - # make sure define is turned into 2-tuples - # strings if they are ','-separated strings - cmd = self.build_ext(dist) - cmd.define = 'one,two' - cmd.finalize_options() - self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) - - # make sure undef is turned into a list of - # strings if they are ','-separated strings - cmd = self.build_ext(dist) - cmd.undef = 'one,two' - cmd.finalize_options() - self.assertEqual(cmd.undef, ['one', 'two']) - - # make sure swig_opts is turned into a list - cmd = self.build_ext(dist) - cmd.swig_opts = None - cmd.finalize_options() - self.assertEqual(cmd.swig_opts, []) - - cmd = self.build_ext(dist) - cmd.swig_opts = '1 2' - cmd.finalize_options() - self.assertEqual(cmd.swig_opts, ['1', '2']) - - def test_check_extensions_list(self): - dist = Distribution() - cmd = self.build_ext(dist) - cmd.finalize_options() - - #'extensions' option must be a list of Extension instances - self.assertRaises(DistutilsSetupError, - cmd.check_extensions_list, 'foo') - - # each element of 'ext_modules' option must be an - # Extension instance or 2-tuple - exts = [('bar', 'foo', 'bar'), 'foo'] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # first element of each tuple in 'ext_modules' - # must be the extension name (a string) and match - # a python dotted-separated name - exts = [('foo-bar', '')] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # second element of each tuple in 'ext_modules' - # must be a dictionary (build info) - exts = [('foo.bar', '')] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # ok this one should pass - exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', - 'some': 'bar'})] - cmd.check_extensions_list(exts) - ext = exts[0] - self.assertIsInstance(ext, Extension) - - # check_extensions_list adds in ext the values passed - # when they are in ('include_dirs', 'library_dirs', 'libraries' - # 'extra_objects', 'extra_compile_args', 'extra_link_args') - self.assertEqual(ext.libraries, 'foo') - self.assertFalse(hasattr(ext, 'some')) - - # 'macros' element of build info dict must be 1- or 2-tuple - exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', - 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - exts[0][1]['macros'] = [('1', '2'), ('3',)] - cmd.check_extensions_list(exts) - self.assertEqual(exts[0].undef_macros, ['3']) - self.assertEqual(exts[0].define_macros, [('1', '2')]) - - def test_get_source_files(self): - modules = [Extension('foo', ['xxx'], optional=False)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = self.build_ext(dist) - cmd.ensure_finalized() - self.assertEqual(cmd.get_source_files(), ['xxx']) - - def test_unicode_module_names(self): - modules = [ - Extension('foo', ['aaa'], optional=False), - Extension('föö', ['uuu'], optional=False), - ] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = self.build_ext(dist) - cmd.ensure_finalized() - self.assertRegex(cmd.get_ext_filename(modules[0].name), r'foo(_d)?\..*') - self.assertRegex(cmd.get_ext_filename(modules[1].name), r'föö(_d)?\..*') - self.assertEqual(cmd.get_export_symbols(modules[0]), ['PyInit_foo']) - self.assertEqual(cmd.get_export_symbols(modules[1]), ['PyInitU_f_gkaa']) - - def test_compiler_option(self): - # cmd.compiler is an option and - # should not be overridden by a compiler instance - # when the command is run - dist = Distribution() - cmd = self.build_ext(dist) - cmd.compiler = 'unix' - cmd.ensure_finalized() - cmd.run() - self.assertEqual(cmd.compiler, 'unix') - - def test_get_outputs(self): - cmd = support.missing_compiler_executable() - if cmd is not None: - self.skipTest('The %r command is not found' % cmd) - tmp_dir = self.mkdtemp() - c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void PyInit_foo(void) {}\n') - ext = Extension('foo', [c_file], optional=False) - dist = Distribution({'name': 'xx', - 'ext_modules': [ext]}) - cmd = self.build_ext(dist) - fixup_build_ext(cmd) - cmd.ensure_finalized() - self.assertEqual(len(cmd.get_outputs()), 1) - - cmd.build_lib = os.path.join(self.tmp_dir, 'build') - cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') - - # issue #5977 : distutils build_ext.get_outputs - # returns wrong result with --inplace - other_tmp_dir = os.path.realpath(self.mkdtemp()) - old_wd = os.getcwd() - os.chdir(other_tmp_dir) - try: - cmd.inplace = 1 - cmd.run() - so_file = cmd.get_outputs()[0] - finally: - os.chdir(old_wd) - self.assertTrue(os.path.exists(so_file)) - ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') - self.assertTrue(so_file.endswith(ext_suffix)) - so_dir = os.path.dirname(so_file) - self.assertEqual(so_dir, other_tmp_dir) - - cmd.inplace = 0 - cmd.compiler = None - cmd.run() - so_file = cmd.get_outputs()[0] - self.assertTrue(os.path.exists(so_file)) - self.assertTrue(so_file.endswith(ext_suffix)) - so_dir = os.path.dirname(so_file) - self.assertEqual(so_dir, cmd.build_lib) - - # inplace = 0, cmd.package = 'bar' - build_py = cmd.get_finalized_command('build_py') - build_py.package_dir = {'': 'bar'} - path = cmd.get_ext_fullpath('foo') - # checking that the last directory is the build_dir - path = os.path.split(path)[0] - self.assertEqual(path, cmd.build_lib) - - # inplace = 1, cmd.package = 'bar' - cmd.inplace = 1 - other_tmp_dir = os.path.realpath(self.mkdtemp()) - old_wd = os.getcwd() - os.chdir(other_tmp_dir) - try: - path = cmd.get_ext_fullpath('foo') - finally: - os.chdir(old_wd) - # checking that the last directory is bar - path = os.path.split(path)[0] - lastdir = os.path.split(path)[-1] - self.assertEqual(lastdir, 'bar') - - def test_ext_fullpath(self): - ext = sysconfig.get_config_var('EXT_SUFFIX') - # building lxml.etree inplace - #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') - #etree_ext = Extension('lxml.etree', [etree_c]) - #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) - dist = Distribution() - cmd = self.build_ext(dist) - cmd.inplace = 1 - cmd.distribution.package_dir = {'': 'src'} - cmd.distribution.packages = ['lxml', 'lxml.html'] - curdir = os.getcwd() - wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) - path = cmd.get_ext_fullpath('lxml.etree') - self.assertEqual(wanted, path) - - # building lxml.etree not inplace - cmd.inplace = 0 - cmd.build_lib = os.path.join(curdir, 'tmpdir') - wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) - path = cmd.get_ext_fullpath('lxml.etree') - self.assertEqual(wanted, path) - - # building twisted.runner.portmap not inplace - build_py = cmd.get_finalized_command('build_py') - build_py.package_dir = {} - cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] - path = cmd.get_ext_fullpath('twisted.runner.portmap') - wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', - 'portmap' + ext) - self.assertEqual(wanted, path) - - # building twisted.runner.portmap inplace - cmd.inplace = 1 - path = cmd.get_ext_fullpath('twisted.runner.portmap') - wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) - self.assertEqual(wanted, path) - - - @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') - def test_deployment_target_default(self): - # Issue 9516: Test that, in the absence of the environment variable, - # an extension module is compiled with the same deployment target as - # the interpreter. - self._try_compile_deployment_target('==', None) - - @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') - def test_deployment_target_too_low(self): - # Issue 9516: Test that an extension module is not allowed to be - # compiled with a deployment target less than that of the interpreter. - self.assertRaises(DistutilsPlatformError, - self._try_compile_deployment_target, '>', '10.1') - - @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') - def test_deployment_target_higher_ok(self): - # Issue 9516: Test that an extension module can be compiled with a - # deployment target higher than that of the interpreter: the ext - # module may depend on some newer OS feature. - deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - if deptarget: - # increment the minor version number (i.e. 10.6 -> 10.7) - deptarget = [int(x) for x in deptarget.split('.')] - deptarget[-1] += 1 - deptarget = '.'.join(str(i) for i in deptarget) - self._try_compile_deployment_target('<', deptarget) - - def _try_compile_deployment_target(self, operator, target): - orig_environ = os.environ - os.environ = orig_environ.copy() - self.addCleanup(setattr, os, 'environ', orig_environ) - - if target is None: - if os.environ.get('MACOSX_DEPLOYMENT_TARGET'): - del os.environ['MACOSX_DEPLOYMENT_TARGET'] - else: - os.environ['MACOSX_DEPLOYMENT_TARGET'] = target - - deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') - - with open(deptarget_c, 'w') as fp: - fp.write(textwrap.dedent('''\ - #include - - int dummy; - - #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED - #else - #error "Unexpected target" - #endif - - ''' % operator)) - - # get the deployment target that the interpreter was built with - target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - target = tuple(map(int, target.split('.')[0:2])) - # format the target value as defined in the Apple - # Availability Macros. We can't use the macro names since - # at least one value we test with will not exist yet. - if target[1] < 10: - # for 10.1 through 10.9.x -> "10n0" - target = '%02d%01d0' % target - else: - # for 10.10 and beyond -> "10nn00" - target = '%02d%02d00' % target - deptarget_ext = Extension( - 'deptarget', - [deptarget_c], - extra_compile_args=['-DTARGET=%s'%(target,)], - ) - dist = Distribution({ - 'name': 'deptarget', - 'ext_modules': [deptarget_ext] - }) - dist.package_dir = self.tmp_dir - cmd = self.build_ext(dist) - cmd.build_lib = self.tmp_dir - cmd.build_temp = self.tmp_dir - - try: - old_stdout = sys.stdout - if not support.verbose: - # silence compiler output - sys.stdout = StringIO() - try: - cmd.ensure_finalized() - cmd.run() - finally: - sys.stdout = old_stdout - - except CompileError: - self.fail("Wrong deployment target during compilation") - - -class ParallelBuildExtTestCase(BuildExtTestCase): - - def build_ext(self, *args, **kwargs): - build_ext = super().build_ext(*args, **kwargs) - build_ext.parallel = True - return build_ext - - -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(BuildExtTestCase)) - suite.addTest(unittest.makeSuite(ParallelBuildExtTestCase)) - return suite - -if __name__ == '__main__': - support.run_unittest(__name__) diff --git a/tests/test_build_py.py b/tests/test_build_py.py deleted file mode 100644 index 0712e92c..00000000 --- a/tests/test_build_py.py +++ /dev/null @@ -1,179 +0,0 @@ -"""Tests for distutils.command.build_py.""" - -import os -import sys -import unittest - -from distutils.command.build_py import build_py -from distutils.core import Distribution -from distutils.errors import DistutilsFileError - -from distutils.tests import support -from test.support import run_unittest - - -class BuildPyTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_package_data(self): - sources = self.mkdtemp() - f = open(os.path.join(sources, "__init__.py"), "w") - try: - f.write("# Pretend this is a package.") - finally: - f.close() - f = open(os.path.join(sources, "README.txt"), "w") - try: - f.write("Info about this package") - finally: - f.close() - - destination = self.mkdtemp() - - dist = Distribution({"packages": ["pkg"], - "package_dir": {"pkg": sources}}) - # script_name need not exist, it just need to be initialized - dist.script_name = os.path.join(sources, "setup.py") - dist.command_obj["build"] = support.DummyCommand( - force=0, - build_lib=destination) - dist.packages = ["pkg"] - dist.package_data = {"pkg": ["README.txt"]} - dist.package_dir = {"pkg": sources} - - cmd = build_py(dist) - cmd.compile = 1 - cmd.ensure_finalized() - self.assertEqual(cmd.package_data, dist.package_data) - - cmd.run() - - # This makes sure the list of outputs includes byte-compiled - # files for Python modules but not for package data files - # (there shouldn't *be* byte-code files for those!). - self.assertEqual(len(cmd.get_outputs()), 3) - pkgdest = os.path.join(destination, "pkg") - files = os.listdir(pkgdest) - pycache_dir = os.path.join(pkgdest, "__pycache__") - self.assertIn("__init__.py", files) - self.assertIn("README.txt", files) - if sys.dont_write_bytecode: - self.assertFalse(os.path.exists(pycache_dir)) - else: - pyc_files = os.listdir(pycache_dir) - self.assertIn("__init__.%s.pyc" % sys.implementation.cache_tag, - pyc_files) - - def test_empty_package_dir(self): - # See bugs #1668596/#1720897 - sources = self.mkdtemp() - open(os.path.join(sources, "__init__.py"), "w").close() - - testdir = os.path.join(sources, "doc") - os.mkdir(testdir) - open(os.path.join(testdir, "testfile"), "w").close() - - os.chdir(sources) - dist = Distribution({"packages": ["pkg"], - "package_dir": {"pkg": ""}, - "package_data": {"pkg": ["doc/*"]}}) - # script_name need not exist, it just need to be initialized - dist.script_name = os.path.join(sources, "setup.py") - dist.script_args = ["build"] - dist.parse_command_line() - - try: - dist.run_commands() - except DistutilsFileError: - self.fail("failed package_data test when package_dir is ''") - - @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') - def test_byte_compile(self): - project_dir, dist = self.create_dist(py_modules=['boiledeggs']) - os.chdir(project_dir) - self.write_file('boiledeggs.py', 'import antigravity') - cmd = build_py(dist) - cmd.compile = 1 - cmd.build_lib = 'here' - cmd.finalize_options() - cmd.run() - - found = os.listdir(cmd.build_lib) - self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) - found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) - self.assertEqual(found, - ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]) - - @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') - def test_byte_compile_optimized(self): - project_dir, dist = self.create_dist(py_modules=['boiledeggs']) - os.chdir(project_dir) - self.write_file('boiledeggs.py', 'import antigravity') - cmd = build_py(dist) - cmd.compile = 0 - cmd.optimize = 1 - cmd.build_lib = 'here' - cmd.finalize_options() - cmd.run() - - found = os.listdir(cmd.build_lib) - self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) - found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) - expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag) - self.assertEqual(sorted(found), [expect]) - - def test_dir_in_package_data(self): - """ - A directory in package_data should not be added to the filelist. - """ - # See bug 19286 - sources = self.mkdtemp() - pkg_dir = os.path.join(sources, "pkg") - - os.mkdir(pkg_dir) - open(os.path.join(pkg_dir, "__init__.py"), "w").close() - - docdir = os.path.join(pkg_dir, "doc") - os.mkdir(docdir) - open(os.path.join(docdir, "testfile"), "w").close() - - # create the directory that could be incorrectly detected as a file - os.mkdir(os.path.join(docdir, 'otherdir')) - - os.chdir(sources) - dist = Distribution({"packages": ["pkg"], - "package_data": {"pkg": ["doc/*"]}}) - # script_name need not exist, it just need to be initialized - dist.script_name = os.path.join(sources, "setup.py") - dist.script_args = ["build"] - dist.parse_command_line() - - try: - dist.run_commands() - except DistutilsFileError: - self.fail("failed package_data when data dir includes a dir") - - def test_dont_write_bytecode(self): - # makes sure byte_compile is not used - dist = self.create_dist()[1] - cmd = build_py(dist) - cmd.compile = 1 - cmd.optimize = 1 - - old_dont_write_bytecode = sys.dont_write_bytecode - sys.dont_write_bytecode = True - try: - cmd.byte_compile([]) - finally: - sys.dont_write_bytecode = old_dont_write_bytecode - - self.assertIn('byte-compiling is disabled', - self.logs[0][1] % self.logs[0][2]) - - -def test_suite(): - return unittest.makeSuite(BuildPyTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_build_scripts.py b/tests/test_build_scripts.py deleted file mode 100644 index 954fc763..00000000 --- a/tests/test_build_scripts.py +++ /dev/null @@ -1,112 +0,0 @@ -"""Tests for distutils.command.build_scripts.""" - -import os -import unittest - -from distutils.command.build_scripts import build_scripts -from distutils.core import Distribution -from distutils import sysconfig - -from distutils.tests import support -from test.support import run_unittest - - -class BuildScriptsTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_default_settings(self): - cmd = self.get_build_scripts_cmd("/foo/bar", []) - self.assertFalse(cmd.force) - self.assertIsNone(cmd.build_dir) - - cmd.finalize_options() - - self.assertTrue(cmd.force) - self.assertEqual(cmd.build_dir, "/foo/bar") - - def test_build(self): - source = self.mkdtemp() - target = self.mkdtemp() - expected = self.write_sample_scripts(source) - - cmd = self.get_build_scripts_cmd(target, - [os.path.join(source, fn) - for fn in expected]) - cmd.finalize_options() - cmd.run() - - built = os.listdir(target) - for name in expected: - self.assertIn(name, built) - - def get_build_scripts_cmd(self, target, scripts): - import sys - dist = Distribution() - dist.scripts = scripts - dist.command_obj["build"] = support.DummyCommand( - build_scripts=target, - force=1, - executable=sys.executable - ) - return build_scripts(dist) - - def write_sample_scripts(self, dir): - expected = [] - expected.append("script1.py") - self.write_script(dir, "script1.py", - ("#! /usr/bin/env python2.3\n" - "# bogus script w/ Python sh-bang\n" - "pass\n")) - expected.append("script2.py") - self.write_script(dir, "script2.py", - ("#!/usr/bin/python\n" - "# bogus script w/ Python sh-bang\n" - "pass\n")) - expected.append("shell.sh") - self.write_script(dir, "shell.sh", - ("#!/bin/sh\n" - "# bogus shell script w/ sh-bang\n" - "exit 0\n")) - return expected - - def write_script(self, dir, name, text): - f = open(os.path.join(dir, name), "w") - try: - f.write(text) - finally: - f.close() - - def test_version_int(self): - source = self.mkdtemp() - target = self.mkdtemp() - expected = self.write_sample_scripts(source) - - - cmd = self.get_build_scripts_cmd(target, - [os.path.join(source, fn) - for fn in expected]) - cmd.finalize_options() - - # http://bugs.python.org/issue4524 - # - # On linux-g++-32 with command line `./configure --enable-ipv6 - # --with-suffix=3`, python is compiled okay but the build scripts - # failed when writing the name of the executable - old = sysconfig.get_config_vars().get('VERSION') - sysconfig._config_vars['VERSION'] = 4 - try: - cmd.run() - finally: - if old is not None: - sysconfig._config_vars['VERSION'] = old - - built = os.listdir(target) - for name in expected: - self.assertIn(name, built) - -def test_suite(): - return unittest.makeSuite(BuildScriptsTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_check.py b/tests/test_check.py deleted file mode 100644 index e534aca1..00000000 --- a/tests/test_check.py +++ /dev/null @@ -1,163 +0,0 @@ -"""Tests for distutils.command.check.""" -import os -import textwrap -import unittest -from test.support import run_unittest - -from distutils.command.check import check, HAS_DOCUTILS -from distutils.tests import support -from distutils.errors import DistutilsSetupError - -try: - import pygments -except ImportError: - pygments = None - - -HERE = os.path.dirname(__file__) - - -class CheckTestCase(support.LoggingSilencer, - support.TempdirManager, - unittest.TestCase): - - def _run(self, metadata=None, cwd=None, **options): - if metadata is None: - metadata = {} - if cwd is not None: - old_dir = os.getcwd() - os.chdir(cwd) - pkg_info, dist = self.create_dist(**metadata) - cmd = check(dist) - cmd.initialize_options() - for name, value in options.items(): - setattr(cmd, name, value) - cmd.ensure_finalized() - cmd.run() - if cwd is not None: - os.chdir(old_dir) - return cmd - - def test_check_metadata(self): - # let's run the command with no metadata at all - # by default, check is checking the metadata - # should have some warnings - cmd = self._run() - self.assertEqual(cmd._warnings, 2) - - # now let's add the required fields - # and run it again, to make sure we don't get - # any warning anymore - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', - 'name': 'xxx', 'version': 'xxx'} - cmd = self._run(metadata) - self.assertEqual(cmd._warnings, 0) - - # now with the strict mode, we should - # get an error if there are missing metadata - self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1}) - - # and of course, no error when all metadata are present - cmd = self._run(metadata, strict=1) - self.assertEqual(cmd._warnings, 0) - - # now a test with non-ASCII characters - metadata = {'url': 'xxx', 'author': '\u00c9ric', - 'author_email': 'xxx', 'name': 'xxx', - 'version': 'xxx', - 'description': 'Something about esszet \u00df', - 'long_description': 'More things about esszet \u00df'} - cmd = self._run(metadata) - self.assertEqual(cmd._warnings, 0) - - @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") - def test_check_document(self): - pkg_info, dist = self.create_dist() - cmd = check(dist) - - # let's see if it detects broken rest - broken_rest = 'title\n===\n\ntest' - msgs = cmd._check_rst_data(broken_rest) - self.assertEqual(len(msgs), 1) - - # and non-broken rest - rest = 'title\n=====\n\ntest' - msgs = cmd._check_rst_data(rest) - self.assertEqual(len(msgs), 0) - - @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") - def test_check_restructuredtext(self): - # let's see if it detects broken rest in long_description - broken_rest = 'title\n===\n\ntest' - pkg_info, dist = self.create_dist(long_description=broken_rest) - cmd = check(dist) - cmd.check_restructuredtext() - self.assertEqual(cmd._warnings, 1) - - # let's see if we have an error with strict=1 - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', - 'name': 'xxx', 'version': 'xxx', - 'long_description': broken_rest} - self.assertRaises(DistutilsSetupError, self._run, metadata, - **{'strict': 1, 'restructuredtext': 1}) - - # and non-broken rest, including a non-ASCII character to test #12114 - metadata['long_description'] = 'title\n=====\n\ntest \u00df' - cmd = self._run(metadata, strict=1, restructuredtext=1) - self.assertEqual(cmd._warnings, 0) - - # check that includes work to test #31292 - metadata['long_description'] = 'title\n=====\n\n.. include:: includetest.rst' - cmd = self._run(metadata, cwd=HERE, strict=1, restructuredtext=1) - self.assertEqual(cmd._warnings, 0) - - @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") - def test_check_restructuredtext_with_syntax_highlight(self): - # Don't fail if there is a `code` or `code-block` directive - - example_rst_docs = [] - example_rst_docs.append(textwrap.dedent("""\ - Here's some code: - - .. code:: python - - def foo(): - pass - """)) - example_rst_docs.append(textwrap.dedent("""\ - Here's some code: - - .. code-block:: python - - def foo(): - pass - """)) - - for rest_with_code in example_rst_docs: - pkg_info, dist = self.create_dist(long_description=rest_with_code) - cmd = check(dist) - cmd.check_restructuredtext() - msgs = cmd._check_rst_data(rest_with_code) - if pygments is not None: - self.assertEqual(len(msgs), 0) - else: - self.assertEqual(len(msgs), 1) - self.assertEqual( - str(msgs[0][1]), - 'Cannot analyze code. Pygments package not found.' - ) - - def test_check_all(self): - - metadata = {'url': 'xxx', 'author': 'xxx'} - self.assertRaises(DistutilsSetupError, self._run, - {}, **{'strict': 1, - 'restructuredtext': 1}) - -def test_suite(): - return unittest.makeSuite(CheckTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_clean.py b/tests/test_clean.py deleted file mode 100644 index c605afd8..00000000 --- a/tests/test_clean.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Tests for distutils.command.clean.""" -import os -import unittest - -from distutils.command.clean import clean -from distutils.tests import support -from test.support import run_unittest - -class cleanTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_simple_run(self): - pkg_dir, dist = self.create_dist() - cmd = clean(dist) - - # let's add some elements clean should remove - dirs = [(d, os.path.join(pkg_dir, d)) - for d in ('build_temp', 'build_lib', 'bdist_base', - 'build_scripts', 'build_base')] - - for name, path in dirs: - os.mkdir(path) - setattr(cmd, name, path) - if name == 'build_base': - continue - for f in ('one', 'two', 'three'): - self.write_file(os.path.join(path, f)) - - # let's run the command - cmd.all = 1 - cmd.ensure_finalized() - cmd.run() - - # make sure the files where removed - for name, path in dirs: - self.assertFalse(os.path.exists(path), - '%s was not removed' % path) - - # let's run the command again (should spit warnings but succeed) - cmd.all = 1 - cmd.ensure_finalized() - cmd.run() - -def test_suite(): - return unittest.makeSuite(cleanTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_cmd.py b/tests/test_cmd.py deleted file mode 100644 index cf5197c3..00000000 --- a/tests/test_cmd.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Tests for distutils.cmd.""" -import unittest -import os -from test.support import captured_stdout, run_unittest - -from distutils.cmd import Command -from distutils.dist import Distribution -from distutils.errors import DistutilsOptionError -from distutils import debug - -class MyCmd(Command): - def initialize_options(self): - pass - -class CommandTestCase(unittest.TestCase): - - def setUp(self): - dist = Distribution() - self.cmd = MyCmd(dist) - - def test_ensure_string_list(self): - - cmd = self.cmd - cmd.not_string_list = ['one', 2, 'three'] - cmd.yes_string_list = ['one', 'two', 'three'] - cmd.not_string_list2 = object() - cmd.yes_string_list2 = 'ok' - cmd.ensure_string_list('yes_string_list') - cmd.ensure_string_list('yes_string_list2') - - self.assertRaises(DistutilsOptionError, - cmd.ensure_string_list, 'not_string_list') - - self.assertRaises(DistutilsOptionError, - cmd.ensure_string_list, 'not_string_list2') - - cmd.option1 = 'ok,dok' - cmd.ensure_string_list('option1') - self.assertEqual(cmd.option1, ['ok', 'dok']) - - cmd.option2 = ['xxx', 'www'] - cmd.ensure_string_list('option2') - - cmd.option3 = ['ok', 2] - self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, - 'option3') - - - def test_make_file(self): - - cmd = self.cmd - - # making sure it raises when infiles is not a string or a list/tuple - self.assertRaises(TypeError, cmd.make_file, - infiles=1, outfile='', func='func', args=()) - - # making sure execute gets called properly - def _execute(func, args, exec_msg, level): - self.assertEqual(exec_msg, 'generating out from in') - cmd.force = True - cmd.execute = _execute - cmd.make_file(infiles='in', outfile='out', func='func', args=()) - - def test_dump_options(self): - - msgs = [] - def _announce(msg, level): - msgs.append(msg) - cmd = self.cmd - cmd.announce = _announce - cmd.option1 = 1 - cmd.option2 = 1 - cmd.user_options = [('option1', '', ''), ('option2', '', '')] - cmd.dump_options() - - wanted = ["command options for 'MyCmd':", ' option1 = 1', - ' option2 = 1'] - self.assertEqual(msgs, wanted) - - def test_ensure_string(self): - cmd = self.cmd - cmd.option1 = 'ok' - cmd.ensure_string('option1') - - cmd.option2 = None - cmd.ensure_string('option2', 'xxx') - self.assertTrue(hasattr(cmd, 'option2')) - - cmd.option3 = 1 - self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') - - def test_ensure_filename(self): - cmd = self.cmd - cmd.option1 = __file__ - cmd.ensure_filename('option1') - cmd.option2 = 'xxx' - self.assertRaises(DistutilsOptionError, cmd.ensure_filename, 'option2') - - def test_ensure_dirname(self): - cmd = self.cmd - cmd.option1 = os.path.dirname(__file__) or os.curdir - cmd.ensure_dirname('option1') - cmd.option2 = 'xxx' - self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') - - def test_debug_print(self): - cmd = self.cmd - with captured_stdout() as stdout: - cmd.debug_print('xxx') - stdout.seek(0) - self.assertEqual(stdout.read(), '') - - debug.DEBUG = True - try: - with captured_stdout() as stdout: - cmd.debug_print('xxx') - stdout.seek(0) - self.assertEqual(stdout.read(), 'xxx\n') - finally: - debug.DEBUG = False - -def test_suite(): - return unittest.makeSuite(CommandTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_config.py b/tests/test_config.py deleted file mode 100644 index 344084af..00000000 --- a/tests/test_config.py +++ /dev/null @@ -1,141 +0,0 @@ -"""Tests for distutils.pypirc.pypirc.""" -import os -import unittest - -from distutils.core import PyPIRCCommand -from distutils.core import Distribution -from distutils.log import set_threshold -from distutils.log import WARN - -from distutils.tests import support -from test.support import run_unittest - -PYPIRC = """\ -[distutils] - -index-servers = - server1 - server2 - server3 - -[server1] -username:me -password:secret - -[server2] -username:meagain -password: secret -realm:acme -repository:http://another.pypi/ - -[server3] -username:cbiggles -password:yh^%#rest-of-my-password -""" - -PYPIRC_OLD = """\ -[server-login] -username:tarek -password:secret -""" - -WANTED = """\ -[distutils] -index-servers = - pypi - -[pypi] -username:tarek -password:xxx -""" - - -class BasePyPIRCCommandTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def setUp(self): - """Patches the environment.""" - super(BasePyPIRCCommandTestCase, self).setUp() - self.tmp_dir = self.mkdtemp() - os.environ['HOME'] = self.tmp_dir - os.environ['USERPROFILE'] = self.tmp_dir - self.rc = os.path.join(self.tmp_dir, '.pypirc') - self.dist = Distribution() - - class command(PyPIRCCommand): - def __init__(self, dist): - PyPIRCCommand.__init__(self, dist) - def initialize_options(self): - pass - finalize_options = initialize_options - - self._cmd = command - self.old_threshold = set_threshold(WARN) - - def tearDown(self): - """Removes the patch.""" - set_threshold(self.old_threshold) - super(BasePyPIRCCommandTestCase, self).tearDown() - - -class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase): - - def test_server_registration(self): - # This test makes sure PyPIRCCommand knows how to: - # 1. handle several sections in .pypirc - # 2. handle the old format - - # new format - self.write_file(self.rc, PYPIRC) - cmd = self._cmd(self.dist) - config = cmd._read_pypirc() - - config = list(sorted(config.items())) - waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://upload.pypi.org/legacy/'), - ('server', 'server1'), ('username', 'me')] - self.assertEqual(config, waited) - - # old format - self.write_file(self.rc, PYPIRC_OLD) - config = cmd._read_pypirc() - config = list(sorted(config.items())) - waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://upload.pypi.org/legacy/'), - ('server', 'server-login'), ('username', 'tarek')] - self.assertEqual(config, waited) - - def test_server_empty_registration(self): - cmd = self._cmd(self.dist) - rc = cmd._get_rc_file() - self.assertFalse(os.path.exists(rc)) - cmd._store_pypirc('tarek', 'xxx') - self.assertTrue(os.path.exists(rc)) - f = open(rc) - try: - content = f.read() - self.assertEqual(content, WANTED) - finally: - f.close() - - def test_config_interpolation(self): - # using the % character in .pypirc should not raise an error (#20120) - self.write_file(self.rc, PYPIRC) - cmd = self._cmd(self.dist) - cmd.repository = 'server3' - config = cmd._read_pypirc() - - config = list(sorted(config.items())) - waited = [('password', 'yh^%#rest-of-my-password'), ('realm', 'pypi'), - ('repository', 'https://upload.pypi.org/legacy/'), - ('server', 'server3'), ('username', 'cbiggles')] - self.assertEqual(config, waited) - - -def test_suite(): - return unittest.makeSuite(PyPIRCCommandTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_config_cmd.py b/tests/test_config_cmd.py deleted file mode 100644 index 9aeab07b..00000000 --- a/tests/test_config_cmd.py +++ /dev/null @@ -1,96 +0,0 @@ -"""Tests for distutils.command.config.""" -import unittest -import os -import sys -from test.support import run_unittest, missing_compiler_executable - -from distutils.command.config import dump_file, config -from distutils.tests import support -from distutils import log - -class ConfigTestCase(support.LoggingSilencer, - support.TempdirManager, - unittest.TestCase): - - def _info(self, msg, *args): - for line in msg.splitlines(): - self._logs.append(line) - - def setUp(self): - super(ConfigTestCase, self).setUp() - self._logs = [] - self.old_log = log.info - log.info = self._info - - def tearDown(self): - log.info = self.old_log - super(ConfigTestCase, self).tearDown() - - def test_dump_file(self): - this_file = os.path.splitext(__file__)[0] + '.py' - f = open(this_file) - try: - numlines = len(f.readlines()) - finally: - f.close() - - dump_file(this_file, 'I am the header') - self.assertEqual(len(self._logs), numlines+1) - - @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") - def test_search_cpp(self): - cmd = missing_compiler_executable(['preprocessor']) - if cmd is not None: - self.skipTest('The %r command is not found' % cmd) - pkg_dir, dist = self.create_dist() - cmd = config(dist) - cmd._check_compiler() - compiler = cmd.compiler - if sys.platform[:3] == "aix" and "xlc" in compiler.preprocessor[0].lower(): - self.skipTest('xlc: The -E option overrides the -P, -o, and -qsyntaxonly options') - - # simple pattern searches - match = cmd.search_cpp(pattern='xxx', body='/* xxx */') - self.assertEqual(match, 0) - - match = cmd.search_cpp(pattern='_configtest', body='/* xxx */') - self.assertEqual(match, 1) - - def test_finalize_options(self): - # finalize_options does a bit of transformation - # on options - pkg_dir, dist = self.create_dist() - cmd = config(dist) - cmd.include_dirs = 'one%stwo' % os.pathsep - cmd.libraries = 'one' - cmd.library_dirs = 'three%sfour' % os.pathsep - cmd.ensure_finalized() - - self.assertEqual(cmd.include_dirs, ['one', 'two']) - self.assertEqual(cmd.libraries, ['one']) - self.assertEqual(cmd.library_dirs, ['three', 'four']) - - def test_clean(self): - # _clean removes files - tmp_dir = self.mkdtemp() - f1 = os.path.join(tmp_dir, 'one') - f2 = os.path.join(tmp_dir, 'two') - - self.write_file(f1, 'xxx') - self.write_file(f2, 'xxx') - - for f in (f1, f2): - self.assertTrue(os.path.exists(f)) - - pkg_dir, dist = self.create_dist() - cmd = config(dist) - cmd._clean(f1, f2) - - for f in (f1, f2): - self.assertFalse(os.path.exists(f)) - -def test_suite(): - return unittest.makeSuite(ConfigTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_core.py b/tests/test_core.py deleted file mode 100644 index 27ce7324..00000000 --- a/tests/test_core.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Tests for distutils.core.""" - -import io -import distutils.core -import os -import shutil -import sys -import test.support -from test.support import captured_stdout, run_unittest -import unittest -from distutils.tests import support -from distutils import log - -# setup script that uses __file__ -setup_using___file__ = """\ - -__file__ - -from distutils.core import setup -setup() -""" - -setup_prints_cwd = """\ - -import os -print(os.getcwd()) - -from distutils.core import setup -setup() -""" - -setup_does_nothing = """\ -from distutils.core import setup -setup() -""" - - -setup_defines_subclass = """\ -from distutils.core import setup -from distutils.command.install import install as _install - -class install(_install): - sub_commands = _install.sub_commands + ['cmd'] - -setup(cmdclass={'install': install}) -""" - -class CoreTestCase(support.EnvironGuard, unittest.TestCase): - - def setUp(self): - super(CoreTestCase, self).setUp() - self.old_stdout = sys.stdout - self.cleanup_testfn() - self.old_argv = sys.argv, sys.argv[:] - self.addCleanup(log.set_threshold, log._global_log.threshold) - - def tearDown(self): - sys.stdout = self.old_stdout - self.cleanup_testfn() - sys.argv = self.old_argv[0] - sys.argv[:] = self.old_argv[1] - super(CoreTestCase, self).tearDown() - - def cleanup_testfn(self): - path = test.support.TESTFN - if os.path.isfile(path): - os.remove(path) - elif os.path.isdir(path): - shutil.rmtree(path) - - def write_setup(self, text, path=test.support.TESTFN): - f = open(path, "w") - try: - f.write(text) - finally: - f.close() - return path - - def test_run_setup_provides_file(self): - # Make sure the script can use __file__; if that's missing, the test - # setup.py script will raise NameError. - distutils.core.run_setup( - self.write_setup(setup_using___file__)) - - def test_run_setup_preserves_sys_argv(self): - # Make sure run_setup does not clobber sys.argv - argv_copy = sys.argv.copy() - distutils.core.run_setup( - self.write_setup(setup_does_nothing)) - self.assertEqual(sys.argv, argv_copy) - - def test_run_setup_defines_subclass(self): - # Make sure the script can use __file__; if that's missing, the test - # setup.py script will raise NameError. - dist = distutils.core.run_setup( - self.write_setup(setup_defines_subclass)) - install = dist.get_command_obj('install') - self.assertIn('cmd', install.sub_commands) - - def test_run_setup_uses_current_dir(self): - # This tests that the setup script is run with the current directory - # as its own current directory; this was temporarily broken by a - # previous patch when TESTFN did not use the current directory. - sys.stdout = io.StringIO() - cwd = os.getcwd() - - # Create a directory and write the setup.py file there: - os.mkdir(test.support.TESTFN) - setup_py = os.path.join(test.support.TESTFN, "setup.py") - distutils.core.run_setup( - self.write_setup(setup_prints_cwd, path=setup_py)) - - output = sys.stdout.getvalue() - if output.endswith("\n"): - output = output[:-1] - self.assertEqual(cwd, output) - - def test_debug_mode(self): - # this covers the code called when DEBUG is set - sys.argv = ['setup.py', '--name'] - with captured_stdout() as stdout: - distutils.core.setup(name='bar') - stdout.seek(0) - self.assertEqual(stdout.read(), 'bar\n') - - distutils.core.DEBUG = True - try: - with captured_stdout() as stdout: - distutils.core.setup(name='bar') - finally: - distutils.core.DEBUG = False - stdout.seek(0) - wanted = "options (after parsing config files):\n" - self.assertEqual(stdout.readlines()[0], wanted) - -def test_suite(): - return unittest.makeSuite(CoreTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_cygwinccompiler.py b/tests/test_cygwinccompiler.py deleted file mode 100644 index 9dc869de..00000000 --- a/tests/test_cygwinccompiler.py +++ /dev/null @@ -1,154 +0,0 @@ -"""Tests for distutils.cygwinccompiler.""" -import unittest -import sys -import os -from io import BytesIO -from test.support import run_unittest - -from distutils import cygwinccompiler -from distutils.cygwinccompiler import (check_config_h, - CONFIG_H_OK, CONFIG_H_NOTOK, - CONFIG_H_UNCERTAIN, get_versions, - get_msvcr) -from distutils.tests import support - -class FakePopen(object): - test_class = None - - def __init__(self, cmd, shell, stdout): - self.cmd = cmd.split()[0] - exes = self.test_class._exes - if self.cmd in exes: - # issue #6438 in Python 3.x, Popen returns bytes - self.stdout = BytesIO(exes[self.cmd]) - else: - self.stdout = os.popen(cmd, 'r') - - -class CygwinCCompilerTestCase(support.TempdirManager, - unittest.TestCase): - - def setUp(self): - super(CygwinCCompilerTestCase, self).setUp() - self.version = sys.version - self.python_h = os.path.join(self.mkdtemp(), 'python.h') - from distutils import sysconfig - self.old_get_config_h_filename = sysconfig.get_config_h_filename - sysconfig.get_config_h_filename = self._get_config_h_filename - self.old_find_executable = cygwinccompiler.find_executable - cygwinccompiler.find_executable = self._find_executable - self._exes = {} - self.old_popen = cygwinccompiler.Popen - FakePopen.test_class = self - cygwinccompiler.Popen = FakePopen - - def tearDown(self): - sys.version = self.version - from distutils import sysconfig - sysconfig.get_config_h_filename = self.old_get_config_h_filename - cygwinccompiler.find_executable = self.old_find_executable - cygwinccompiler.Popen = self.old_popen - super(CygwinCCompilerTestCase, self).tearDown() - - def _get_config_h_filename(self): - return self.python_h - - def _find_executable(self, name): - if name in self._exes: - return name - return None - - def test_check_config_h(self): - - # check_config_h looks for "GCC" in sys.version first - # returns CONFIG_H_OK if found - sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' - '4.0.1 (Apple Computer, Inc. build 5370)]') - - self.assertEqual(check_config_h()[0], CONFIG_H_OK) - - # then it tries to see if it can find "__GNUC__" in pyconfig.h - sys.version = 'something without the *CC word' - - # if the file doesn't exist it returns CONFIG_H_UNCERTAIN - self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN) - - # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK - self.write_file(self.python_h, 'xxx') - self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK) - - # and CONFIG_H_OK if __GNUC__ is found - self.write_file(self.python_h, 'xxx __GNUC__ xxx') - self.assertEqual(check_config_h()[0], CONFIG_H_OK) - - def test_get_versions(self): - - # get_versions calls distutils.spawn.find_executable on - # 'gcc', 'ld' and 'dllwrap' - self.assertEqual(get_versions(), (None, None, None)) - - # Let's fake we have 'gcc' and it returns '3.4.5' - self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' - res = get_versions() - self.assertEqual(str(res[0]), '3.4.5') - - # and let's see what happens when the version - # doesn't match the regular expression - # (\d+\.\d+(\.\d+)*) - self._exes['gcc'] = b'very strange output' - res = get_versions() - self.assertEqual(res[0], None) - - # same thing for ld - self._exes['ld'] = b'GNU ld version 2.17.50 20060824' - res = get_versions() - self.assertEqual(str(res[1]), '2.17.50') - self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' - res = get_versions() - self.assertEqual(res[1], None) - - # and dllwrap - self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' - res = get_versions() - self.assertEqual(str(res[2]), '2.17.50') - self._exes['dllwrap'] = b'Cheese Wrap' - res = get_versions() - self.assertEqual(res[2], None) - - def test_get_msvcr(self): - - # none - sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' - '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEqual(get_msvcr(), None) - - # MSVC 7.0 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1300 32 bits (Intel)]') - self.assertEqual(get_msvcr(), ['msvcr70']) - - # MSVC 7.1 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1310 32 bits (Intel)]') - self.assertEqual(get_msvcr(), ['msvcr71']) - - # VS2005 / MSVC 8.0 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1400 32 bits (Intel)]') - self.assertEqual(get_msvcr(), ['msvcr80']) - - # VS2008 / MSVC 9.0 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1500 32 bits (Intel)]') - self.assertEqual(get_msvcr(), ['msvcr90']) - - # unknown - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1999 32 bits (Intel)]') - self.assertRaises(ValueError, get_msvcr) - -def test_suite(): - return unittest.makeSuite(CygwinCCompilerTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_dep_util.py b/tests/test_dep_util.py deleted file mode 100644 index c6fae39c..00000000 --- a/tests/test_dep_util.py +++ /dev/null @@ -1,80 +0,0 @@ -"""Tests for distutils.dep_util.""" -import unittest -import os - -from distutils.dep_util import newer, newer_pairwise, newer_group -from distutils.errors import DistutilsFileError -from distutils.tests import support -from test.support import run_unittest - -class DepUtilTestCase(support.TempdirManager, unittest.TestCase): - - def test_newer(self): - - tmpdir = self.mkdtemp() - new_file = os.path.join(tmpdir, 'new') - old_file = os.path.abspath(__file__) - - # Raise DistutilsFileError if 'new_file' does not exist. - self.assertRaises(DistutilsFileError, newer, new_file, old_file) - - # Return true if 'new_file' exists and is more recently modified than - # 'old_file', or if 'new_file' exists and 'old_file' doesn't. - self.write_file(new_file) - self.assertTrue(newer(new_file, 'I_dont_exist')) - self.assertTrue(newer(new_file, old_file)) - - # Return false if both exist and 'old_file' is the same age or younger - # than 'new_file'. - self.assertFalse(newer(old_file, new_file)) - - def test_newer_pairwise(self): - tmpdir = self.mkdtemp() - sources = os.path.join(tmpdir, 'sources') - targets = os.path.join(tmpdir, 'targets') - os.mkdir(sources) - os.mkdir(targets) - one = os.path.join(sources, 'one') - two = os.path.join(sources, 'two') - three = os.path.abspath(__file__) # I am the old file - four = os.path.join(targets, 'four') - self.write_file(one) - self.write_file(two) - self.write_file(four) - - self.assertEqual(newer_pairwise([one, two], [three, four]), - ([one],[three])) - - def test_newer_group(self): - tmpdir = self.mkdtemp() - sources = os.path.join(tmpdir, 'sources') - os.mkdir(sources) - one = os.path.join(sources, 'one') - two = os.path.join(sources, 'two') - three = os.path.join(sources, 'three') - old_file = os.path.abspath(__file__) - - # return true if 'old_file' is out-of-date with respect to any file - # listed in 'sources'. - self.write_file(one) - self.write_file(two) - self.write_file(three) - self.assertTrue(newer_group([one, two, three], old_file)) - self.assertFalse(newer_group([one, two, old_file], three)) - - # missing handling - os.remove(one) - self.assertRaises(OSError, newer_group, [one, two, old_file], three) - - self.assertFalse(newer_group([one, two, old_file], three, - missing='ignore')) - - self.assertTrue(newer_group([one, two, old_file], three, - missing='newer')) - - -def test_suite(): - return unittest.makeSuite(DepUtilTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_dir_util.py b/tests/test_dir_util.py deleted file mode 100644 index d436cf83..00000000 --- a/tests/test_dir_util.py +++ /dev/null @@ -1,139 +0,0 @@ -"""Tests for distutils.dir_util.""" -import unittest -import os -import stat -import sys -from unittest.mock import patch - -from distutils import dir_util, errors -from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, - ensure_relative) - -from distutils import log -from distutils.tests import support -from test.support import run_unittest - - -class DirUtilTestCase(support.TempdirManager, unittest.TestCase): - - def _log(self, msg, *args): - if len(args) > 0: - self._logs.append(msg % args) - else: - self._logs.append(msg) - - def setUp(self): - super(DirUtilTestCase, self).setUp() - self._logs = [] - tmp_dir = self.mkdtemp() - self.root_target = os.path.join(tmp_dir, 'deep') - self.target = os.path.join(self.root_target, 'here') - self.target2 = os.path.join(tmp_dir, 'deep2') - self.old_log = log.info - log.info = self._log - - def tearDown(self): - log.info = self.old_log - super(DirUtilTestCase, self).tearDown() - - def test_mkpath_remove_tree_verbosity(self): - - mkpath(self.target, verbose=0) - wanted = [] - self.assertEqual(self._logs, wanted) - remove_tree(self.root_target, verbose=0) - - mkpath(self.target, verbose=1) - wanted = ['creating %s' % self.root_target, - 'creating %s' % self.target] - self.assertEqual(self._logs, wanted) - self._logs = [] - - remove_tree(self.root_target, verbose=1) - wanted = ["removing '%s' (and everything under it)" % self.root_target] - self.assertEqual(self._logs, wanted) - - @unittest.skipIf(sys.platform.startswith('win'), - "This test is only appropriate for POSIX-like systems.") - def test_mkpath_with_custom_mode(self): - # Get and set the current umask value for testing mode bits. - umask = os.umask(0o002) - os.umask(umask) - mkpath(self.target, 0o700) - self.assertEqual( - stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) - mkpath(self.target2, 0o555) - self.assertEqual( - stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) - - def test_create_tree_verbosity(self): - - create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) - self.assertEqual(self._logs, []) - remove_tree(self.root_target, verbose=0) - - wanted = ['creating %s' % self.root_target] - create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) - self.assertEqual(self._logs, wanted) - - remove_tree(self.root_target, verbose=0) - - def test_copy_tree_verbosity(self): - - mkpath(self.target, verbose=0) - - copy_tree(self.target, self.target2, verbose=0) - self.assertEqual(self._logs, []) - - remove_tree(self.root_target, verbose=0) - - mkpath(self.target, verbose=0) - a_file = os.path.join(self.target, 'ok.txt') - with open(a_file, 'w') as f: - f.write('some content') - - wanted = ['copying %s -> %s' % (a_file, self.target2)] - copy_tree(self.target, self.target2, verbose=1) - self.assertEqual(self._logs, wanted) - - remove_tree(self.root_target, verbose=0) - remove_tree(self.target2, verbose=0) - - def test_copy_tree_skips_nfs_temp_files(self): - mkpath(self.target, verbose=0) - - a_file = os.path.join(self.target, 'ok.txt') - nfs_file = os.path.join(self.target, '.nfs123abc') - for f in a_file, nfs_file: - with open(f, 'w') as fh: - fh.write('some content') - - copy_tree(self.target, self.target2) - self.assertEqual(os.listdir(self.target2), ['ok.txt']) - - remove_tree(self.root_target, verbose=0) - remove_tree(self.target2, verbose=0) - - def test_ensure_relative(self): - if os.sep == '/': - self.assertEqual(ensure_relative('/home/foo'), 'home/foo') - self.assertEqual(ensure_relative('some/path'), 'some/path') - else: # \\ - self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') - self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') - - def test_copy_tree_exception_in_listdir(self): - """ - An exception in listdir should raise a DistutilsFileError - """ - with patch("os.listdir", side_effect=OSError()), \ - self.assertRaises(errors.DistutilsFileError): - src = self.tempdirs[-1] - dir_util.copy_tree(src, None) - - -def test_suite(): - return unittest.makeSuite(DirUtilTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_dist.py b/tests/test_dist.py deleted file mode 100644 index 60956dad..00000000 --- a/tests/test_dist.py +++ /dev/null @@ -1,528 +0,0 @@ -"""Tests for distutils.dist.""" -import os -import io -import sys -import unittest -import warnings -import textwrap - -from unittest import mock - -from distutils.dist import Distribution, fix_help_options -from distutils.cmd import Command - -from test.support import ( - TESTFN, captured_stdout, captured_stderr, run_unittest -) -from distutils.tests import support -from distutils import log - - -class test_dist(Command): - """Sample distutils extension command.""" - - user_options = [ - ("sample-option=", "S", "help text"), - ] - - def initialize_options(self): - self.sample_option = None - - -class TestDistribution(Distribution): - """Distribution subclasses that avoids the default search for - configuration files. - - The ._config_files attribute must be set before - .parse_config_files() is called. - """ - - def find_config_files(self): - return self._config_files - - -class DistributionTestCase(support.LoggingSilencer, - support.TempdirManager, - support.EnvironGuard, - unittest.TestCase): - - def setUp(self): - super(DistributionTestCase, self).setUp() - self.argv = sys.argv, sys.argv[:] - del sys.argv[1:] - - def tearDown(self): - sys.argv = self.argv[0] - sys.argv[:] = self.argv[1] - super(DistributionTestCase, self).tearDown() - - def create_distribution(self, configfiles=()): - d = TestDistribution() - d._config_files = configfiles - d.parse_config_files() - d.parse_command_line() - return d - - def test_command_packages_unspecified(self): - sys.argv.append("build") - d = self.create_distribution() - self.assertEqual(d.get_command_packages(), ["distutils.command"]) - - def test_command_packages_cmdline(self): - from distutils.tests.test_dist import test_dist - sys.argv.extend(["--command-packages", - "foo.bar,distutils.tests", - "test_dist", - "-Ssometext", - ]) - d = self.create_distribution() - # let's actually try to load our test command: - self.assertEqual(d.get_command_packages(), - ["distutils.command", "foo.bar", "distutils.tests"]) - cmd = d.get_command_obj("test_dist") - self.assertIsInstance(cmd, test_dist) - self.assertEqual(cmd.sample_option, "sometext") - - def test_venv_install_options(self): - sys.argv.append("install") - self.addCleanup(os.unlink, TESTFN) - - fakepath = '/somedir' - - with open(TESTFN, "w") as f: - print(("[install]\n" - "install-base = {0}\n" - "install-platbase = {0}\n" - "install-lib = {0}\n" - "install-platlib = {0}\n" - "install-purelib = {0}\n" - "install-headers = {0}\n" - "install-scripts = {0}\n" - "install-data = {0}\n" - "prefix = {0}\n" - "exec-prefix = {0}\n" - "home = {0}\n" - "user = {0}\n" - "root = {0}").format(fakepath), file=f) - - # Base case: Not in a Virtual Environment - with mock.patch.multiple(sys, prefix='/a', base_prefix='/a') as values: - d = self.create_distribution([TESTFN]) - - option_tuple = (TESTFN, fakepath) - - result_dict = { - 'install_base': option_tuple, - 'install_platbase': option_tuple, - 'install_lib': option_tuple, - 'install_platlib': option_tuple, - 'install_purelib': option_tuple, - 'install_headers': option_tuple, - 'install_scripts': option_tuple, - 'install_data': option_tuple, - 'prefix': option_tuple, - 'exec_prefix': option_tuple, - 'home': option_tuple, - 'user': option_tuple, - 'root': option_tuple, - } - - self.assertEqual( - sorted(d.command_options.get('install').keys()), - sorted(result_dict.keys())) - - for (key, value) in d.command_options.get('install').items(): - self.assertEqual(value, result_dict[key]) - - # Test case: In a Virtual Environment - with mock.patch.multiple(sys, prefix='/a', base_prefix='/b') as values: - d = self.create_distribution([TESTFN]) - - for key in result_dict.keys(): - self.assertNotIn(key, d.command_options.get('install', {})) - - def test_command_packages_configfile(self): - sys.argv.append("build") - self.addCleanup(os.unlink, TESTFN) - f = open(TESTFN, "w") - try: - print("[global]", file=f) - print("command_packages = foo.bar, splat", file=f) - finally: - f.close() - - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), - ["distutils.command", "foo.bar", "splat"]) - - # ensure command line overrides config: - sys.argv[1:] = ["--command-packages", "spork", "build"] - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), - ["distutils.command", "spork"]) - - # Setting --command-packages to '' should cause the default to - # be used even if a config file specified something else: - sys.argv[1:] = ["--command-packages", "", "build"] - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), ["distutils.command"]) - - def test_empty_options(self): - # an empty options dictionary should not stay in the - # list of attributes - - # catching warnings - warns = [] - - def _warn(msg): - warns.append(msg) - - self.addCleanup(setattr, warnings, 'warn', warnings.warn) - warnings.warn = _warn - dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx', - 'version': 'xxx', 'url': 'xxxx', - 'options': {}}) - - self.assertEqual(len(warns), 0) - self.assertNotIn('options', dir(dist)) - - def test_finalize_options(self): - attrs = {'keywords': 'one,two', - 'platforms': 'one,two'} - - dist = Distribution(attrs=attrs) - dist.finalize_options() - - # finalize_option splits platforms and keywords - self.assertEqual(dist.metadata.platforms, ['one', 'two']) - self.assertEqual(dist.metadata.keywords, ['one', 'two']) - - attrs = {'keywords': 'foo bar', - 'platforms': 'foo bar'} - dist = Distribution(attrs=attrs) - dist.finalize_options() - self.assertEqual(dist.metadata.platforms, ['foo bar']) - self.assertEqual(dist.metadata.keywords, ['foo bar']) - - def test_get_command_packages(self): - dist = Distribution() - self.assertEqual(dist.command_packages, None) - cmds = dist.get_command_packages() - self.assertEqual(cmds, ['distutils.command']) - self.assertEqual(dist.command_packages, - ['distutils.command']) - - dist.command_packages = 'one,two' - cmds = dist.get_command_packages() - self.assertEqual(cmds, ['distutils.command', 'one', 'two']) - - def test_announce(self): - # make sure the level is known - dist = Distribution() - args = ('ok',) - kwargs = {'level': 'ok2'} - self.assertRaises(ValueError, dist.announce, args, kwargs) - - - def test_find_config_files_disable(self): - # Ticket #1180: Allow user to disable their home config file. - temp_home = self.mkdtemp() - if os.name == 'posix': - user_filename = os.path.join(temp_home, ".pydistutils.cfg") - else: - user_filename = os.path.join(temp_home, "pydistutils.cfg") - - with open(user_filename, 'w') as f: - f.write('[distutils]\n') - - def _expander(path): - return temp_home - - old_expander = os.path.expanduser - os.path.expanduser = _expander - try: - d = Distribution() - all_files = d.find_config_files() - - d = Distribution(attrs={'script_args': ['--no-user-cfg']}) - files = d.find_config_files() - finally: - os.path.expanduser = old_expander - - # make sure --no-user-cfg disables the user cfg file - self.assertEqual(len(all_files)-1, len(files)) - -class MetadataTestCase(support.TempdirManager, support.EnvironGuard, - unittest.TestCase): - - def setUp(self): - super(MetadataTestCase, self).setUp() - self.argv = sys.argv, sys.argv[:] - - def tearDown(self): - sys.argv = self.argv[0] - sys.argv[:] = self.argv[1] - super(MetadataTestCase, self).tearDown() - - def format_metadata(self, dist): - sio = io.StringIO() - dist.metadata.write_pkg_file(sio) - return sio.getvalue() - - def test_simple_metadata(self): - attrs = {"name": "package", - "version": "1.0"} - dist = Distribution(attrs) - meta = self.format_metadata(dist) - self.assertIn("Metadata-Version: 1.0", meta) - self.assertNotIn("provides:", meta.lower()) - self.assertNotIn("requires:", meta.lower()) - self.assertNotIn("obsoletes:", meta.lower()) - - def test_provides(self): - attrs = {"name": "package", - "version": "1.0", - "provides": ["package", "package.sub"]} - dist = Distribution(attrs) - self.assertEqual(dist.metadata.get_provides(), - ["package", "package.sub"]) - self.assertEqual(dist.get_provides(), - ["package", "package.sub"]) - meta = self.format_metadata(dist) - self.assertIn("Metadata-Version: 1.1", meta) - self.assertNotIn("requires:", meta.lower()) - self.assertNotIn("obsoletes:", meta.lower()) - - def test_provides_illegal(self): - self.assertRaises(ValueError, Distribution, - {"name": "package", - "version": "1.0", - "provides": ["my.pkg (splat)"]}) - - def test_requires(self): - attrs = {"name": "package", - "version": "1.0", - "requires": ["other", "another (==1.0)"]} - dist = Distribution(attrs) - self.assertEqual(dist.metadata.get_requires(), - ["other", "another (==1.0)"]) - self.assertEqual(dist.get_requires(), - ["other", "another (==1.0)"]) - meta = self.format_metadata(dist) - self.assertIn("Metadata-Version: 1.1", meta) - self.assertNotIn("provides:", meta.lower()) - self.assertIn("Requires: other", meta) - self.assertIn("Requires: another (==1.0)", meta) - self.assertNotIn("obsoletes:", meta.lower()) - - def test_requires_illegal(self): - self.assertRaises(ValueError, Distribution, - {"name": "package", - "version": "1.0", - "requires": ["my.pkg (splat)"]}) - - def test_requires_to_list(self): - attrs = {"name": "package", - "requires": iter(["other"])} - dist = Distribution(attrs) - self.assertIsInstance(dist.metadata.requires, list) - - - def test_obsoletes(self): - attrs = {"name": "package", - "version": "1.0", - "obsoletes": ["other", "another (<1.0)"]} - dist = Distribution(attrs) - self.assertEqual(dist.metadata.get_obsoletes(), - ["other", "another (<1.0)"]) - self.assertEqual(dist.get_obsoletes(), - ["other", "another (<1.0)"]) - meta = self.format_metadata(dist) - self.assertIn("Metadata-Version: 1.1", meta) - self.assertNotIn("provides:", meta.lower()) - self.assertNotIn("requires:", meta.lower()) - self.assertIn("Obsoletes: other", meta) - self.assertIn("Obsoletes: another (<1.0)", meta) - - def test_obsoletes_illegal(self): - self.assertRaises(ValueError, Distribution, - {"name": "package", - "version": "1.0", - "obsoletes": ["my.pkg (splat)"]}) - - def test_obsoletes_to_list(self): - attrs = {"name": "package", - "obsoletes": iter(["other"])} - dist = Distribution(attrs) - self.assertIsInstance(dist.metadata.obsoletes, list) - - def test_classifier(self): - attrs = {'name': 'Boa', 'version': '3.0', - 'classifiers': ['Programming Language :: Python :: 3']} - dist = Distribution(attrs) - self.assertEqual(dist.get_classifiers(), - ['Programming Language :: Python :: 3']) - meta = self.format_metadata(dist) - self.assertIn('Metadata-Version: 1.1', meta) - - def test_classifier_invalid_type(self): - attrs = {'name': 'Boa', 'version': '3.0', - 'classifiers': ('Programming Language :: Python :: 3',)} - with captured_stderr() as error: - d = Distribution(attrs) - # should have warning about passing a non-list - self.assertIn('should be a list', error.getvalue()) - # should be converted to a list - self.assertIsInstance(d.metadata.classifiers, list) - self.assertEqual(d.metadata.classifiers, - list(attrs['classifiers'])) - - def test_keywords(self): - attrs = {'name': 'Monty', 'version': '1.0', - 'keywords': ['spam', 'eggs', 'life of brian']} - dist = Distribution(attrs) - self.assertEqual(dist.get_keywords(), - ['spam', 'eggs', 'life of brian']) - - def test_keywords_invalid_type(self): - attrs = {'name': 'Monty', 'version': '1.0', - 'keywords': ('spam', 'eggs', 'life of brian')} - with captured_stderr() as error: - d = Distribution(attrs) - # should have warning about passing a non-list - self.assertIn('should be a list', error.getvalue()) - # should be converted to a list - self.assertIsInstance(d.metadata.keywords, list) - self.assertEqual(d.metadata.keywords, list(attrs['keywords'])) - - def test_platforms(self): - attrs = {'name': 'Monty', 'version': '1.0', - 'platforms': ['GNU/Linux', 'Some Evil Platform']} - dist = Distribution(attrs) - self.assertEqual(dist.get_platforms(), - ['GNU/Linux', 'Some Evil Platform']) - - def test_platforms_invalid_types(self): - attrs = {'name': 'Monty', 'version': '1.0', - 'platforms': ('GNU/Linux', 'Some Evil Platform')} - with captured_stderr() as error: - d = Distribution(attrs) - # should have warning about passing a non-list - self.assertIn('should be a list', error.getvalue()) - # should be converted to a list - self.assertIsInstance(d.metadata.platforms, list) - self.assertEqual(d.metadata.platforms, list(attrs['platforms'])) - - def test_download_url(self): - attrs = {'name': 'Boa', 'version': '3.0', - 'download_url': 'http://example.org/boa'} - dist = Distribution(attrs) - meta = self.format_metadata(dist) - self.assertIn('Metadata-Version: 1.1', meta) - - def test_long_description(self): - long_desc = textwrap.dedent("""\ - example:: - We start here - and continue here - and end here.""") - attrs = {"name": "package", - "version": "1.0", - "long_description": long_desc} - - dist = Distribution(attrs) - meta = self.format_metadata(dist) - meta = meta.replace('\n' + 8 * ' ', '\n') - self.assertIn(long_desc, meta) - - def test_custom_pydistutils(self): - # fixes #2166 - # make sure pydistutils.cfg is found - if os.name == 'posix': - user_filename = ".pydistutils.cfg" - else: - user_filename = "pydistutils.cfg" - - temp_dir = self.mkdtemp() - user_filename = os.path.join(temp_dir, user_filename) - f = open(user_filename, 'w') - try: - f.write('.') - finally: - f.close() - - try: - dist = Distribution() - - # linux-style - if sys.platform in ('linux', 'darwin'): - os.environ['HOME'] = temp_dir - files = dist.find_config_files() - self.assertIn(user_filename, files) - - # win32-style - if sys.platform == 'win32': - # home drive should be found - os.environ['USERPROFILE'] = temp_dir - files = dist.find_config_files() - self.assertIn(user_filename, files, - '%r not found in %r' % (user_filename, files)) - finally: - os.remove(user_filename) - - def test_fix_help_options(self): - help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] - fancy_options = fix_help_options(help_tuples) - self.assertEqual(fancy_options[0], ('a', 'b', 'c')) - self.assertEqual(fancy_options[1], (1, 2, 3)) - - def test_show_help(self): - # smoke test, just makes sure some help is displayed - self.addCleanup(log.set_threshold, log._global_log.threshold) - dist = Distribution() - sys.argv = [] - dist.help = 1 - dist.script_name = 'setup.py' - with captured_stdout() as s: - dist.parse_command_line() - - output = [line for line in s.getvalue().split('\n') - if line.strip() != ''] - self.assertTrue(output) - - - def test_read_metadata(self): - attrs = {"name": "package", - "version": "1.0", - "long_description": "desc", - "description": "xxx", - "download_url": "http://example.com", - "keywords": ['one', 'two'], - "requires": ['foo']} - - dist = Distribution(attrs) - metadata = dist.metadata - - # write it then reloads it - PKG_INFO = io.StringIO() - metadata.write_pkg_file(PKG_INFO) - PKG_INFO.seek(0) - metadata.read_pkg_file(PKG_INFO) - - self.assertEqual(metadata.name, "package") - self.assertEqual(metadata.version, "1.0") - self.assertEqual(metadata.description, "xxx") - self.assertEqual(metadata.download_url, 'http://example.com') - self.assertEqual(metadata.keywords, ['one', 'two']) - self.assertEqual(metadata.platforms, ['UNKNOWN']) - self.assertEqual(metadata.obsoletes, None) - self.assertEqual(metadata.requires, ['foo']) - -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(DistributionTestCase)) - suite.addTest(unittest.makeSuite(MetadataTestCase)) - return suite - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_extension.py b/tests/test_extension.py deleted file mode 100644 index e35f2738..00000000 --- a/tests/test_extension.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Tests for distutils.extension.""" -import unittest -import os -import warnings - -from test.support import check_warnings, run_unittest -from distutils.extension import read_setup_file, Extension - -class ExtensionTestCase(unittest.TestCase): - - def test_read_setup_file(self): - # trying to read a Setup file - # (sample extracted from the PyGame project) - setup = os.path.join(os.path.dirname(__file__), 'Setup.sample') - - exts = read_setup_file(setup) - names = [ext.name for ext in exts] - names.sort() - - # here are the extensions read_setup_file should have created - # out of the file - wanted = ['_arraysurfarray', '_camera', '_numericsndarray', - '_numericsurfarray', 'base', 'bufferproxy', 'cdrom', - 'color', 'constants', 'display', 'draw', 'event', - 'fastevent', 'font', 'gfxdraw', 'image', 'imageext', - 'joystick', 'key', 'mask', 'mixer', 'mixer_music', - 'mouse', 'movie', 'overlay', 'pixelarray', 'pypm', - 'rect', 'rwobject', 'scrap', 'surface', 'surflock', - 'time', 'transform'] - - self.assertEqual(names, wanted) - - def test_extension_init(self): - # the first argument, which is the name, must be a string - self.assertRaises(AssertionError, Extension, 1, []) - ext = Extension('name', []) - self.assertEqual(ext.name, 'name') - - # the second argument, which is the list of files, must - # be a list of strings - self.assertRaises(AssertionError, Extension, 'name', 'file') - self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) - ext = Extension('name', ['file1', 'file2']) - self.assertEqual(ext.sources, ['file1', 'file2']) - - # others arguments have defaults - for attr in ('include_dirs', 'define_macros', 'undef_macros', - 'library_dirs', 'libraries', 'runtime_library_dirs', - 'extra_objects', 'extra_compile_args', 'extra_link_args', - 'export_symbols', 'swig_opts', 'depends'): - self.assertEqual(getattr(ext, attr), []) - - self.assertEqual(ext.language, None) - self.assertEqual(ext.optional, None) - - # if there are unknown keyword options, warn about them - with check_warnings() as w: - warnings.simplefilter('always') - ext = Extension('name', ['file1', 'file2'], chic=True) - - self.assertEqual(len(w.warnings), 1) - self.assertEqual(str(w.warnings[0].message), - "Unknown Extension options: 'chic'") - -def test_suite(): - return unittest.makeSuite(ExtensionTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_file_util.py b/tests/test_file_util.py deleted file mode 100644 index a4e2d025..00000000 --- a/tests/test_file_util.py +++ /dev/null @@ -1,122 +0,0 @@ -"""Tests for distutils.file_util.""" -import unittest -import os -import errno -from unittest.mock import patch - -from distutils.file_util import move_file, copy_file -from distutils import log -from distutils.tests import support -from distutils.errors import DistutilsFileError -from test.support import run_unittest, unlink - -class FileUtilTestCase(support.TempdirManager, unittest.TestCase): - - def _log(self, msg, *args): - if len(args) > 0: - self._logs.append(msg % args) - else: - self._logs.append(msg) - - def setUp(self): - super(FileUtilTestCase, self).setUp() - self._logs = [] - self.old_log = log.info - log.info = self._log - tmp_dir = self.mkdtemp() - self.source = os.path.join(tmp_dir, 'f1') - self.target = os.path.join(tmp_dir, 'f2') - self.target_dir = os.path.join(tmp_dir, 'd1') - - def tearDown(self): - log.info = self.old_log - super(FileUtilTestCase, self).tearDown() - - def test_move_file_verbosity(self): - f = open(self.source, 'w') - try: - f.write('some content') - finally: - f.close() - - move_file(self.source, self.target, verbose=0) - wanted = [] - self.assertEqual(self._logs, wanted) - - # back to original state - move_file(self.target, self.source, verbose=0) - - move_file(self.source, self.target, verbose=1) - wanted = ['moving %s -> %s' % (self.source, self.target)] - self.assertEqual(self._logs, wanted) - - # back to original state - move_file(self.target, self.source, verbose=0) - - self._logs = [] - # now the target is a dir - os.mkdir(self.target_dir) - move_file(self.source, self.target_dir, verbose=1) - wanted = ['moving %s -> %s' % (self.source, self.target_dir)] - self.assertEqual(self._logs, wanted) - - def test_move_file_exception_unpacking_rename(self): - # see issue 22182 - with patch("os.rename", side_effect=OSError("wrong", 1)), \ - self.assertRaises(DistutilsFileError): - with open(self.source, 'w') as fobj: - fobj.write('spam eggs') - move_file(self.source, self.target, verbose=0) - - def test_move_file_exception_unpacking_unlink(self): - # see issue 22182 - with patch("os.rename", side_effect=OSError(errno.EXDEV, "wrong")), \ - patch("os.unlink", side_effect=OSError("wrong", 1)), \ - self.assertRaises(DistutilsFileError): - with open(self.source, 'w') as fobj: - fobj.write('spam eggs') - move_file(self.source, self.target, verbose=0) - - def test_copy_file_hard_link(self): - with open(self.source, 'w') as f: - f.write('some content') - # Check first that copy_file() will not fall back on copying the file - # instead of creating the hard link. - try: - os.link(self.source, self.target) - except OSError as e: - self.skipTest('os.link: %s' % e) - else: - unlink(self.target) - st = os.stat(self.source) - copy_file(self.source, self.target, link='hard') - st2 = os.stat(self.source) - st3 = os.stat(self.target) - self.assertTrue(os.path.samestat(st, st2), (st, st2)) - self.assertTrue(os.path.samestat(st2, st3), (st2, st3)) - with open(self.source, 'r') as f: - self.assertEqual(f.read(), 'some content') - - def test_copy_file_hard_link_failure(self): - # If hard linking fails, copy_file() falls back on copying file - # (some special filesystems don't support hard linking even under - # Unix, see issue #8876). - with open(self.source, 'w') as f: - f.write('some content') - st = os.stat(self.source) - with patch("os.link", side_effect=OSError(0, "linking unsupported")): - copy_file(self.source, self.target, link='hard') - st2 = os.stat(self.source) - st3 = os.stat(self.target) - self.assertTrue(os.path.samestat(st, st2), (st, st2)) - self.assertFalse(os.path.samestat(st2, st3), (st2, st3)) - for fn in (self.source, self.target): - with open(fn, 'r') as f: - self.assertEqual(f.read(), 'some content') - - -def test_suite(): - return unittest.makeSuite(FileUtilTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_filelist.py b/tests/test_filelist.py deleted file mode 100644 index c71342d0..00000000 --- a/tests/test_filelist.py +++ /dev/null @@ -1,340 +0,0 @@ -"""Tests for distutils.filelist.""" -import os -import re -import unittest -from distutils import debug -from distutils.log import WARN -from distutils.errors import DistutilsTemplateError -from distutils.filelist import glob_to_re, translate_pattern, FileList -from distutils import filelist - -import test.support -from test.support import captured_stdout, run_unittest -from distutils.tests import support - -MANIFEST_IN = """\ -include ok -include xo -exclude xo -include foo.tmp -include buildout.cfg -global-include *.x -global-include *.txt -global-exclude *.tmp -recursive-include f *.oo -recursive-exclude global *.x -graft dir -prune dir3 -""" - - -def make_local_path(s): - """Converts '/' in a string to os.sep""" - return s.replace('/', os.sep) - - -class FileListTestCase(support.LoggingSilencer, - unittest.TestCase): - - def assertNoWarnings(self): - self.assertEqual(self.get_logs(WARN), []) - self.clear_logs() - - def assertWarnings(self): - self.assertGreater(len(self.get_logs(WARN)), 0) - self.clear_logs() - - def test_glob_to_re(self): - sep = os.sep - if os.sep == '\\': - sep = re.escape(os.sep) - - for glob, regex in ( - # simple cases - ('foo*', r'(?s:foo[^%(sep)s]*)\Z'), - ('foo?', r'(?s:foo[^%(sep)s])\Z'), - ('foo??', r'(?s:foo[^%(sep)s][^%(sep)s])\Z'), - # special cases - (r'foo\\*', r'(?s:foo\\\\[^%(sep)s]*)\Z'), - (r'foo\\\*', r'(?s:foo\\\\\\[^%(sep)s]*)\Z'), - ('foo????', r'(?s:foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s])\Z'), - (r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z')): - regex = regex % {'sep': sep} - self.assertEqual(glob_to_re(glob), regex) - - def test_process_template_line(self): - # testing all MANIFEST.in template patterns - file_list = FileList() - l = make_local_path - - # simulated file list - file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt', - 'buildout.cfg', - # filelist does not filter out VCS directories, - # it's sdist that does - l('.hg/last-message.txt'), - l('global/one.txt'), - l('global/two.txt'), - l('global/files.x'), - l('global/here.tmp'), - l('f/o/f.oo'), - l('dir/graft-one'), - l('dir/dir2/graft2'), - l('dir3/ok'), - l('dir3/sub/ok.txt'), - ] - - for line in MANIFEST_IN.split('\n'): - if line.strip() == '': - continue - file_list.process_template_line(line) - - wanted = ['ok', - 'buildout.cfg', - 'four.txt', - l('.hg/last-message.txt'), - l('global/one.txt'), - l('global/two.txt'), - l('f/o/f.oo'), - l('dir/graft-one'), - l('dir/dir2/graft2'), - ] - - self.assertEqual(file_list.files, wanted) - - def test_debug_print(self): - file_list = FileList() - with captured_stdout() as stdout: - file_list.debug_print('xxx') - self.assertEqual(stdout.getvalue(), '') - - debug.DEBUG = True - try: - with captured_stdout() as stdout: - file_list.debug_print('xxx') - self.assertEqual(stdout.getvalue(), 'xxx\n') - finally: - debug.DEBUG = False - - def test_set_allfiles(self): - file_list = FileList() - files = ['a', 'b', 'c'] - file_list.set_allfiles(files) - self.assertEqual(file_list.allfiles, files) - - def test_remove_duplicates(self): - file_list = FileList() - file_list.files = ['a', 'b', 'a', 'g', 'c', 'g'] - # files must be sorted beforehand (sdist does it) - file_list.sort() - file_list.remove_duplicates() - self.assertEqual(file_list.files, ['a', 'b', 'c', 'g']) - - def test_translate_pattern(self): - # not regex - self.assertTrue(hasattr( - translate_pattern('a', anchor=True, is_regex=False), - 'search')) - - # is a regex - regex = re.compile('a') - self.assertEqual( - translate_pattern(regex, anchor=True, is_regex=True), - regex) - - # plain string flagged as regex - self.assertTrue(hasattr( - translate_pattern('a', anchor=True, is_regex=True), - 'search')) - - # glob support - self.assertTrue(translate_pattern( - '*.py', anchor=True, is_regex=False).search('filelist.py')) - - def test_exclude_pattern(self): - # return False if no match - file_list = FileList() - self.assertFalse(file_list.exclude_pattern('*.py')) - - # return True if files match - file_list = FileList() - file_list.files = ['a.py', 'b.py'] - self.assertTrue(file_list.exclude_pattern('*.py')) - - # test excludes - file_list = FileList() - file_list.files = ['a.py', 'a.txt'] - file_list.exclude_pattern('*.py') - self.assertEqual(file_list.files, ['a.txt']) - - def test_include_pattern(self): - # return False if no match - file_list = FileList() - file_list.set_allfiles([]) - self.assertFalse(file_list.include_pattern('*.py')) - - # return True if files match - file_list = FileList() - file_list.set_allfiles(['a.py', 'b.txt']) - self.assertTrue(file_list.include_pattern('*.py')) - - # test * matches all files - file_list = FileList() - self.assertIsNone(file_list.allfiles) - file_list.set_allfiles(['a.py', 'b.txt']) - file_list.include_pattern('*') - self.assertEqual(file_list.allfiles, ['a.py', 'b.txt']) - - def test_process_template(self): - l = make_local_path - # invalid lines - file_list = FileList() - for action in ('include', 'exclude', 'global-include', - 'global-exclude', 'recursive-include', - 'recursive-exclude', 'graft', 'prune', 'blarg'): - self.assertRaises(DistutilsTemplateError, - file_list.process_template_line, action) - - # include - file_list = FileList() - file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) - - file_list.process_template_line('include *.py') - self.assertEqual(file_list.files, ['a.py']) - self.assertNoWarnings() - - file_list.process_template_line('include *.rb') - self.assertEqual(file_list.files, ['a.py']) - self.assertWarnings() - - # exclude - file_list = FileList() - file_list.files = ['a.py', 'b.txt', l('d/c.py')] - - file_list.process_template_line('exclude *.py') - self.assertEqual(file_list.files, ['b.txt', l('d/c.py')]) - self.assertNoWarnings() - - file_list.process_template_line('exclude *.rb') - self.assertEqual(file_list.files, ['b.txt', l('d/c.py')]) - self.assertWarnings() - - # global-include - file_list = FileList() - file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) - - file_list.process_template_line('global-include *.py') - self.assertEqual(file_list.files, ['a.py', l('d/c.py')]) - self.assertNoWarnings() - - file_list.process_template_line('global-include *.rb') - self.assertEqual(file_list.files, ['a.py', l('d/c.py')]) - self.assertWarnings() - - # global-exclude - file_list = FileList() - file_list.files = ['a.py', 'b.txt', l('d/c.py')] - - file_list.process_template_line('global-exclude *.py') - self.assertEqual(file_list.files, ['b.txt']) - self.assertNoWarnings() - - file_list.process_template_line('global-exclude *.rb') - self.assertEqual(file_list.files, ['b.txt']) - self.assertWarnings() - - # recursive-include - file_list = FileList() - file_list.set_allfiles(['a.py', l('d/b.py'), l('d/c.txt'), - l('d/d/e.py')]) - - file_list.process_template_line('recursive-include d *.py') - self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) - self.assertNoWarnings() - - file_list.process_template_line('recursive-include e *.py') - self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) - self.assertWarnings() - - # recursive-exclude - file_list = FileList() - file_list.files = ['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')] - - file_list.process_template_line('recursive-exclude d *.py') - self.assertEqual(file_list.files, ['a.py', l('d/c.txt')]) - self.assertNoWarnings() - - file_list.process_template_line('recursive-exclude e *.py') - self.assertEqual(file_list.files, ['a.py', l('d/c.txt')]) - self.assertWarnings() - - # graft - file_list = FileList() - file_list.set_allfiles(['a.py', l('d/b.py'), l('d/d/e.py'), - l('f/f.py')]) - - file_list.process_template_line('graft d') - self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) - self.assertNoWarnings() - - file_list.process_template_line('graft e') - self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) - self.assertWarnings() - - # prune - file_list = FileList() - file_list.files = ['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')] - - file_list.process_template_line('prune d') - self.assertEqual(file_list.files, ['a.py', l('f/f.py')]) - self.assertNoWarnings() - - file_list.process_template_line('prune e') - self.assertEqual(file_list.files, ['a.py', l('f/f.py')]) - self.assertWarnings() - - -class FindAllTestCase(unittest.TestCase): - @test.support.skip_unless_symlink - def test_missing_symlink(self): - with test.support.temp_cwd(): - os.symlink('foo', 'bar') - self.assertEqual(filelist.findall(), []) - - def test_basic_discovery(self): - """ - When findall is called with no parameters or with - '.' as the parameter, the dot should be omitted from - the results. - """ - with test.support.temp_cwd(): - os.mkdir('foo') - file1 = os.path.join('foo', 'file1.txt') - test.support.create_empty_file(file1) - os.mkdir('bar') - file2 = os.path.join('bar', 'file2.txt') - test.support.create_empty_file(file2) - expected = [file2, file1] - self.assertEqual(sorted(filelist.findall()), expected) - - def test_non_local_discovery(self): - """ - When findall is called with another path, the full - path name should be returned. - """ - with test.support.temp_dir() as temp_dir: - file1 = os.path.join(temp_dir, 'file1.txt') - test.support.create_empty_file(file1) - expected = [file1] - self.assertEqual(filelist.findall(temp_dir), expected) - - -def test_suite(): - return unittest.TestSuite([ - unittest.makeSuite(FileListTestCase), - unittest.makeSuite(FindAllTestCase), - ]) - - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_install.py b/tests/test_install.py deleted file mode 100644 index 51c80e04..00000000 --- a/tests/test_install.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Tests for distutils.command.install.""" - -import os -import sys -import unittest -import site - -from test.support import captured_stdout, run_unittest - -from distutils import sysconfig -from distutils.command.install import install -from distutils.command import install as install_module -from distutils.command.build_ext import build_ext -from distutils.command.install import INSTALL_SCHEMES -from distutils.core import Distribution -from distutils.errors import DistutilsOptionError -from distutils.extension import Extension - -from distutils.tests import support -from test import support as test_support - - -def _make_ext_name(modname): - return modname + sysconfig.get_config_var('EXT_SUFFIX') - - -class InstallTestCase(support.TempdirManager, - support.EnvironGuard, - support.LoggingSilencer, - unittest.TestCase): - - def test_home_installation_scheme(self): - # This ensure two things: - # - that --home generates the desired set of directory names - # - test --home is supported on all platforms - builddir = self.mkdtemp() - destination = os.path.join(builddir, "installation") - - dist = Distribution({"name": "foopkg"}) - # script_name need not exist, it just need to be initialized - dist.script_name = os.path.join(builddir, "setup.py") - dist.command_obj["build"] = support.DummyCommand( - build_base=builddir, - build_lib=os.path.join(builddir, "lib"), - ) - - cmd = install(dist) - cmd.home = destination - cmd.ensure_finalized() - - self.assertEqual(cmd.install_base, destination) - self.assertEqual(cmd.install_platbase, destination) - - def check_path(got, expected): - got = os.path.normpath(got) - expected = os.path.normpath(expected) - self.assertEqual(got, expected) - - libdir = os.path.join(destination, "lib", "python") - check_path(cmd.install_lib, libdir) - platlibdir = os.path.join(destination, sys.platlibdir, "python") - check_path(cmd.install_platlib, platlibdir) - check_path(cmd.install_purelib, libdir) - check_path(cmd.install_headers, - os.path.join(destination, "include", "python", "foopkg")) - check_path(cmd.install_scripts, os.path.join(destination, "bin")) - check_path(cmd.install_data, destination) - - def test_user_site(self): - # test install with --user - # preparing the environment for the test - self.old_user_base = site.USER_BASE - self.old_user_site = site.USER_SITE - self.tmpdir = self.mkdtemp() - self.user_base = os.path.join(self.tmpdir, 'B') - self.user_site = os.path.join(self.tmpdir, 'S') - site.USER_BASE = self.user_base - site.USER_SITE = self.user_site - install_module.USER_BASE = self.user_base - install_module.USER_SITE = self.user_site - - def _expanduser(path): - return self.tmpdir - self.old_expand = os.path.expanduser - os.path.expanduser = _expanduser - - def cleanup(): - site.USER_BASE = self.old_user_base - site.USER_SITE = self.old_user_site - install_module.USER_BASE = self.old_user_base - install_module.USER_SITE = self.old_user_site - os.path.expanduser = self.old_expand - - self.addCleanup(cleanup) - - for key in ('nt_user', 'unix_user'): - self.assertIn(key, INSTALL_SCHEMES) - - dist = Distribution({'name': 'xx'}) - cmd = install(dist) - - # making sure the user option is there - options = [name for name, short, lable in - cmd.user_options] - self.assertIn('user', options) - - # setting a value - cmd.user = 1 - - # user base and site shouldn't be created yet - self.assertFalse(os.path.exists(self.user_base)) - self.assertFalse(os.path.exists(self.user_site)) - - # let's run finalize - cmd.ensure_finalized() - - # now they should - self.assertTrue(os.path.exists(self.user_base)) - self.assertTrue(os.path.exists(self.user_site)) - - self.assertIn('userbase', cmd.config_vars) - self.assertIn('usersite', cmd.config_vars) - - def test_handle_extra_path(self): - dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) - cmd = install(dist) - - # two elements - cmd.handle_extra_path() - self.assertEqual(cmd.extra_path, ['path', 'dirs']) - self.assertEqual(cmd.extra_dirs, 'dirs') - self.assertEqual(cmd.path_file, 'path') - - # one element - cmd.extra_path = ['path'] - cmd.handle_extra_path() - self.assertEqual(cmd.extra_path, ['path']) - self.assertEqual(cmd.extra_dirs, 'path') - self.assertEqual(cmd.path_file, 'path') - - # none - dist.extra_path = cmd.extra_path = None - cmd.handle_extra_path() - self.assertEqual(cmd.extra_path, None) - self.assertEqual(cmd.extra_dirs, '') - self.assertEqual(cmd.path_file, None) - - # three elements (no way !) - cmd.extra_path = 'path,dirs,again' - self.assertRaises(DistutilsOptionError, cmd.handle_extra_path) - - def test_finalize_options(self): - dist = Distribution({'name': 'xx'}) - cmd = install(dist) - - # must supply either prefix/exec-prefix/home or - # install-base/install-platbase -- not both - cmd.prefix = 'prefix' - cmd.install_base = 'base' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - # must supply either home or prefix/exec-prefix -- not both - cmd.install_base = None - cmd.home = 'home' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - # can't combine user with prefix/exec_prefix/home or - # install_(plat)base - cmd.prefix = None - cmd.user = 'user' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - def test_record(self): - install_dir = self.mkdtemp() - project_dir, dist = self.create_dist(py_modules=['hello'], - scripts=['sayhi']) - os.chdir(project_dir) - self.write_file('hello.py', "def main(): print('o hai')") - self.write_file('sayhi', 'from hello import main; main()') - - cmd = install(dist) - dist.command_obj['install'] = cmd - cmd.root = install_dir - cmd.record = os.path.join(project_dir, 'filelist') - cmd.ensure_finalized() - cmd.run() - - f = open(cmd.record) - try: - content = f.read() - finally: - f.close() - - found = [os.path.basename(line) for line in content.splitlines()] - expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag, - 'sayhi', - 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] - self.assertEqual(found, expected) - - def test_record_extensions(self): - cmd = test_support.missing_compiler_executable() - if cmd is not None: - self.skipTest('The %r command is not found' % cmd) - install_dir = self.mkdtemp() - project_dir, dist = self.create_dist(ext_modules=[ - Extension('xx', ['xxmodule.c'])]) - os.chdir(project_dir) - support.copy_xxmodule_c(project_dir) - - buildextcmd = build_ext(dist) - support.fixup_build_ext(buildextcmd) - buildextcmd.ensure_finalized() - - cmd = install(dist) - dist.command_obj['install'] = cmd - dist.command_obj['build_ext'] = buildextcmd - cmd.root = install_dir - cmd.record = os.path.join(project_dir, 'filelist') - cmd.ensure_finalized() - cmd.run() - - f = open(cmd.record) - try: - content = f.read() - finally: - f.close() - - found = [os.path.basename(line) for line in content.splitlines()] - expected = [_make_ext_name('xx'), - 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] - self.assertEqual(found, expected) - - def test_debug_mode(self): - # this covers the code called when DEBUG is set - old_logs_len = len(self.logs) - install_module.DEBUG = True - try: - with captured_stdout(): - self.test_record() - finally: - install_module.DEBUG = False - self.assertGreater(len(self.logs), old_logs_len) - - -def test_suite(): - return unittest.makeSuite(InstallTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_install_data.py b/tests/test_install_data.py deleted file mode 100644 index 32ab296a..00000000 --- a/tests/test_install_data.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Tests for distutils.command.install_data.""" -import os -import unittest - -from distutils.command.install_data import install_data -from distutils.tests import support -from test.support import run_unittest - -class InstallDataTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def test_simple_run(self): - pkg_dir, dist = self.create_dist() - cmd = install_data(dist) - cmd.install_dir = inst = os.path.join(pkg_dir, 'inst') - - # data_files can contain - # - simple files - # - a tuple with a path, and a list of file - one = os.path.join(pkg_dir, 'one') - self.write_file(one, 'xxx') - inst2 = os.path.join(pkg_dir, 'inst2') - two = os.path.join(pkg_dir, 'two') - self.write_file(two, 'xxx') - - cmd.data_files = [one, (inst2, [two])] - self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])]) - - # let's run the command - cmd.ensure_finalized() - cmd.run() - - # let's check the result - self.assertEqual(len(cmd.get_outputs()), 2) - rtwo = os.path.split(two)[-1] - self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) - rone = os.path.split(one)[-1] - self.assertTrue(os.path.exists(os.path.join(inst, rone))) - cmd.outfiles = [] - - # let's try with warn_dir one - cmd.warn_dir = 1 - cmd.ensure_finalized() - cmd.run() - - # let's check the result - self.assertEqual(len(cmd.get_outputs()), 2) - self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) - self.assertTrue(os.path.exists(os.path.join(inst, rone))) - cmd.outfiles = [] - - # now using root and empty dir - cmd.root = os.path.join(pkg_dir, 'root') - inst3 = os.path.join(cmd.install_dir, 'inst3') - inst4 = os.path.join(pkg_dir, 'inst4') - three = os.path.join(cmd.install_dir, 'three') - self.write_file(three, 'xx') - cmd.data_files = [one, (inst2, [two]), - ('inst3', [three]), - (inst4, [])] - cmd.ensure_finalized() - cmd.run() - - # let's check the result - self.assertEqual(len(cmd.get_outputs()), 4) - self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) - self.assertTrue(os.path.exists(os.path.join(inst, rone))) - -def test_suite(): - return unittest.makeSuite(InstallDataTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_install_headers.py b/tests/test_install_headers.py deleted file mode 100644 index 2217b321..00000000 --- a/tests/test_install_headers.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Tests for distutils.command.install_headers.""" -import os -import unittest - -from distutils.command.install_headers import install_headers -from distutils.tests import support -from test.support import run_unittest - -class InstallHeadersTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def test_simple_run(self): - # we have two headers - header_list = self.mkdtemp() - header1 = os.path.join(header_list, 'header1') - header2 = os.path.join(header_list, 'header2') - self.write_file(header1) - self.write_file(header2) - headers = [header1, header2] - - pkg_dir, dist = self.create_dist(headers=headers) - cmd = install_headers(dist) - self.assertEqual(cmd.get_inputs(), headers) - - # let's run the command - cmd.install_dir = os.path.join(pkg_dir, 'inst') - cmd.ensure_finalized() - cmd.run() - - # let's check the results - self.assertEqual(len(cmd.get_outputs()), 2) - -def test_suite(): - return unittest.makeSuite(InstallHeadersTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_install_lib.py b/tests/test_install_lib.py deleted file mode 100644 index fda6315b..00000000 --- a/tests/test_install_lib.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Tests for distutils.command.install_data.""" -import sys -import os -import importlib.util -import unittest - -from distutils.command.install_lib import install_lib -from distutils.extension import Extension -from distutils.tests import support -from distutils.errors import DistutilsOptionError -from test.support import run_unittest - - -class InstallLibTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def test_finalize_options(self): - dist = self.create_dist()[1] - cmd = install_lib(dist) - - cmd.finalize_options() - self.assertEqual(cmd.compile, 1) - self.assertEqual(cmd.optimize, 0) - - # optimize must be 0, 1, or 2 - cmd.optimize = 'foo' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - cmd.optimize = '4' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - cmd.optimize = '2' - cmd.finalize_options() - self.assertEqual(cmd.optimize, 2) - - @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') - def test_byte_compile(self): - project_dir, dist = self.create_dist() - os.chdir(project_dir) - cmd = install_lib(dist) - cmd.compile = cmd.optimize = 1 - - f = os.path.join(project_dir, 'foo.py') - self.write_file(f, '# python file') - cmd.byte_compile([f]) - pyc_file = importlib.util.cache_from_source('foo.py', optimization='') - pyc_opt_file = importlib.util.cache_from_source('foo.py', - optimization=cmd.optimize) - self.assertTrue(os.path.exists(pyc_file)) - self.assertTrue(os.path.exists(pyc_opt_file)) - - def test_get_outputs(self): - project_dir, dist = self.create_dist() - os.chdir(project_dir) - os.mkdir('spam') - cmd = install_lib(dist) - - # setting up a dist environment - cmd.compile = cmd.optimize = 1 - cmd.install_dir = self.mkdtemp() - f = os.path.join(project_dir, 'spam', '__init__.py') - self.write_file(f, '# python package') - cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] - cmd.distribution.packages = ['spam'] - cmd.distribution.script_name = 'setup.py' - - # get_outputs should return 4 elements: spam/__init__.py and .pyc, - # foo.import-tag-abiflags.so / foo.pyd - outputs = cmd.get_outputs() - self.assertEqual(len(outputs), 4, outputs) - - def test_get_inputs(self): - project_dir, dist = self.create_dist() - os.chdir(project_dir) - os.mkdir('spam') - cmd = install_lib(dist) - - # setting up a dist environment - cmd.compile = cmd.optimize = 1 - cmd.install_dir = self.mkdtemp() - f = os.path.join(project_dir, 'spam', '__init__.py') - self.write_file(f, '# python package') - cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] - cmd.distribution.packages = ['spam'] - cmd.distribution.script_name = 'setup.py' - - # get_inputs should return 2 elements: spam/__init__.py and - # foo.import-tag-abiflags.so / foo.pyd - inputs = cmd.get_inputs() - self.assertEqual(len(inputs), 2, inputs) - - def test_dont_write_bytecode(self): - # makes sure byte_compile is not used - dist = self.create_dist()[1] - cmd = install_lib(dist) - cmd.compile = 1 - cmd.optimize = 1 - - old_dont_write_bytecode = sys.dont_write_bytecode - sys.dont_write_bytecode = True - try: - cmd.byte_compile([]) - finally: - sys.dont_write_bytecode = old_dont_write_bytecode - - self.assertIn('byte-compiling is disabled', - self.logs[0][1] % self.logs[0][2]) - - -def test_suite(): - return unittest.makeSuite(InstallLibTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_install_scripts.py b/tests/test_install_scripts.py deleted file mode 100644 index 1f7b1038..00000000 --- a/tests/test_install_scripts.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Tests for distutils.command.install_scripts.""" - -import os -import unittest - -from distutils.command.install_scripts import install_scripts -from distutils.core import Distribution - -from distutils.tests import support -from test.support import run_unittest - - -class InstallScriptsTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_default_settings(self): - dist = Distribution() - dist.command_obj["build"] = support.DummyCommand( - build_scripts="/foo/bar") - dist.command_obj["install"] = support.DummyCommand( - install_scripts="/splat/funk", - force=1, - skip_build=1, - ) - cmd = install_scripts(dist) - self.assertFalse(cmd.force) - self.assertFalse(cmd.skip_build) - self.assertIsNone(cmd.build_dir) - self.assertIsNone(cmd.install_dir) - - cmd.finalize_options() - - self.assertTrue(cmd.force) - self.assertTrue(cmd.skip_build) - self.assertEqual(cmd.build_dir, "/foo/bar") - self.assertEqual(cmd.install_dir, "/splat/funk") - - def test_installation(self): - source = self.mkdtemp() - expected = [] - - def write_script(name, text): - expected.append(name) - f = open(os.path.join(source, name), "w") - try: - f.write(text) - finally: - f.close() - - write_script("script1.py", ("#! /usr/bin/env python2.3\n" - "# bogus script w/ Python sh-bang\n" - "pass\n")) - write_script("script2.py", ("#!/usr/bin/python\n" - "# bogus script w/ Python sh-bang\n" - "pass\n")) - write_script("shell.sh", ("#!/bin/sh\n" - "# bogus shell script w/ sh-bang\n" - "exit 0\n")) - - target = self.mkdtemp() - dist = Distribution() - dist.command_obj["build"] = support.DummyCommand(build_scripts=source) - dist.command_obj["install"] = support.DummyCommand( - install_scripts=target, - force=1, - skip_build=1, - ) - cmd = install_scripts(dist) - cmd.finalize_options() - cmd.run() - - installed = os.listdir(target) - for name in expected: - self.assertIn(name, installed) - - -def test_suite(): - return unittest.makeSuite(InstallScriptsTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_log.py b/tests/test_log.py deleted file mode 100644 index 75cf9006..00000000 --- a/tests/test_log.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Tests for distutils.log""" - -import io -import sys -import unittest -from test.support import swap_attr, run_unittest - -from distutils import log - -class TestLog(unittest.TestCase): - def test_non_ascii(self): - # Issues #8663, #34421: test that non-encodable text is escaped with - # backslashreplace error handler and encodable non-ASCII text is - # output as is. - for errors in ('strict', 'backslashreplace', 'surrogateescape', - 'replace', 'ignore'): - with self.subTest(errors=errors): - stdout = io.TextIOWrapper(io.BytesIO(), - encoding='cp437', errors=errors) - stderr = io.TextIOWrapper(io.BytesIO(), - encoding='cp437', errors=errors) - old_threshold = log.set_threshold(log.DEBUG) - try: - with swap_attr(sys, 'stdout', stdout), \ - swap_attr(sys, 'stderr', stderr): - log.debug('Dεbug\tMÄ—ssãge') - log.fatal('Fαtal\tÈrrÅr') - finally: - log.set_threshold(old_threshold) - - stdout.seek(0) - self.assertEqual(stdout.read().rstrip(), - 'Dεbug\tM?ss?ge' if errors == 'replace' else - 'Dεbug\tMssge' if errors == 'ignore' else - 'Dεbug\tM\\u0117ss\\xe3ge') - stderr.seek(0) - self.assertEqual(stderr.read().rstrip(), - 'Fαtal\t?rr?r' if errors == 'replace' else - 'Fαtal\trrr' if errors == 'ignore' else - 'Fαtal\t\\xc8rr\\u014dr') - -def test_suite(): - return unittest.makeSuite(TestLog) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_msvc9compiler.py b/tests/test_msvc9compiler.py deleted file mode 100644 index 77a07ef3..00000000 --- a/tests/test_msvc9compiler.py +++ /dev/null @@ -1,184 +0,0 @@ -"""Tests for distutils.msvc9compiler.""" -import sys -import unittest -import os - -from distutils.errors import DistutilsPlatformError -from distutils.tests import support -from test.support import run_unittest - -# A manifest with the only assembly reference being the msvcrt assembly, so -# should have the assembly completely stripped. Note that although the -# assembly has a reference the assembly is removed - that is -# currently a "feature", not a bug :) -_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\ - - - - - - - - - - - - - - - - - -""" - -# A manifest with references to assemblies other than msvcrt. When processed, -# this assembly should be returned with just the msvcrt part removed. -_MANIFEST_WITH_MULTIPLE_REFERENCES = """\ - - - - - - - - - - - - - - - - - - - - - - -""" - -_CLEANED_MANIFEST = """\ - - - - - - - - - - - - - - - - - - -""" - -if sys.platform=="win32": - from distutils.msvccompiler import get_build_version - if get_build_version()>=8.0: - SKIP_MESSAGE = None - else: - SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" -else: - SKIP_MESSAGE = "These tests are only for win32" - -@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) -class msvc9compilerTestCase(support.TempdirManager, - unittest.TestCase): - - def test_no_compiler(self): - # makes sure query_vcvarsall raises - # a DistutilsPlatformError if the compiler - # is not found - from distutils.msvc9compiler import query_vcvarsall - def _find_vcvarsall(version): - return None - - from distutils import msvc9compiler - old_find_vcvarsall = msvc9compiler.find_vcvarsall - msvc9compiler.find_vcvarsall = _find_vcvarsall - try: - self.assertRaises(DistutilsPlatformError, query_vcvarsall, - 'wont find this version') - finally: - msvc9compiler.find_vcvarsall = old_find_vcvarsall - - def test_reg_class(self): - from distutils.msvc9compiler import Reg - self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') - - # looking for values that should exist on all - # windows registry versions. - path = r'Control Panel\Desktop' - v = Reg.get_value(path, 'dragfullwindows') - self.assertIn(v, ('0', '1', '2')) - - import winreg - HKCU = winreg.HKEY_CURRENT_USER - keys = Reg.read_keys(HKCU, 'xxxx') - self.assertEqual(keys, None) - - keys = Reg.read_keys(HKCU, r'Control Panel') - self.assertIn('Desktop', keys) - - def test_remove_visual_c_ref(self): - from distutils.msvc9compiler import MSVCCompiler - tempdir = self.mkdtemp() - manifest = os.path.join(tempdir, 'manifest') - f = open(manifest, 'w') - try: - f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES) - finally: - f.close() - - compiler = MSVCCompiler() - compiler._remove_visual_c_ref(manifest) - - # see what we got - f = open(manifest) - try: - # removing trailing spaces - content = '\n'.join([line.rstrip() for line in f.readlines()]) - finally: - f.close() - - # makes sure the manifest was properly cleaned - self.assertEqual(content, _CLEANED_MANIFEST) - - def test_remove_entire_manifest(self): - from distutils.msvc9compiler import MSVCCompiler - tempdir = self.mkdtemp() - manifest = os.path.join(tempdir, 'manifest') - f = open(manifest, 'w') - try: - f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE) - finally: - f.close() - - compiler = MSVCCompiler() - got = compiler._remove_visual_c_ref(manifest) - self.assertIsNone(got) - - -def test_suite(): - return unittest.makeSuite(msvc9compilerTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_msvccompiler.py b/tests/test_msvccompiler.py deleted file mode 100644 index b518d6a7..00000000 --- a/tests/test_msvccompiler.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Tests for distutils._msvccompiler.""" -import sys -import unittest -import os - -from distutils.errors import DistutilsPlatformError -from distutils.tests import support -from test.support import run_unittest - - -SKIP_MESSAGE = (None if sys.platform == "win32" else - "These tests are only for win32") - -@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) -class msvccompilerTestCase(support.TempdirManager, - unittest.TestCase): - - def test_no_compiler(self): - import distutils._msvccompiler as _msvccompiler - # makes sure query_vcvarsall raises - # a DistutilsPlatformError if the compiler - # is not found - def _find_vcvarsall(plat_spec): - return None, None - - old_find_vcvarsall = _msvccompiler._find_vcvarsall - _msvccompiler._find_vcvarsall = _find_vcvarsall - try: - self.assertRaises(DistutilsPlatformError, - _msvccompiler._get_vc_env, - 'wont find this version') - finally: - _msvccompiler._find_vcvarsall = old_find_vcvarsall - - def test_get_vc_env_unicode(self): - import distutils._msvccompiler as _msvccompiler - - test_var = 'ṰḖṤṪ┅ṼẨṜ' - test_value = '₃â´â‚…' - - # Ensure we don't early exit from _get_vc_env - old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None) - os.environ[test_var] = test_value - try: - env = _msvccompiler._get_vc_env('x86') - self.assertIn(test_var.lower(), env) - self.assertEqual(test_value, env[test_var.lower()]) - finally: - os.environ.pop(test_var) - if old_distutils_use_sdk: - os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk - - def test_get_vc2017(self): - import distutils._msvccompiler as _msvccompiler - - # This function cannot be mocked, so pass it if we find VS 2017 - # and mark it skipped if we do not. - version, path = _msvccompiler._find_vc2017() - if version: - self.assertGreaterEqual(version, 15) - self.assertTrue(os.path.isdir(path)) - else: - raise unittest.SkipTest("VS 2017 is not installed") - - def test_get_vc2015(self): - import distutils._msvccompiler as _msvccompiler - - # This function cannot be mocked, so pass it if we find VS 2015 - # and mark it skipped if we do not. - version, path = _msvccompiler._find_vc2015() - if version: - self.assertGreaterEqual(version, 14) - self.assertTrue(os.path.isdir(path)) - else: - raise unittest.SkipTest("VS 2015 is not installed") - -def test_suite(): - return unittest.makeSuite(msvccompilerTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_register.py b/tests/test_register.py deleted file mode 100644 index e68b0af3..00000000 --- a/tests/test_register.py +++ /dev/null @@ -1,323 +0,0 @@ -"""Tests for distutils.command.register.""" -import os -import unittest -import getpass -import urllib -import warnings - -from test.support import check_warnings, run_unittest - -from distutils.command import register as register_module -from distutils.command.register import register -from distutils.errors import DistutilsSetupError -from distutils.log import INFO - -from distutils.tests.test_config import BasePyPIRCCommandTestCase - -try: - import docutils -except ImportError: - docutils = None - -PYPIRC_NOPASSWORD = """\ -[distutils] - -index-servers = - server1 - -[server1] -username:me -""" - -WANTED_PYPIRC = """\ -[distutils] -index-servers = - pypi - -[pypi] -username:tarek -password:password -""" - -class Inputs(object): - """Fakes user inputs.""" - def __init__(self, *answers): - self.answers = answers - self.index = 0 - - def __call__(self, prompt=''): - try: - return self.answers[self.index] - finally: - self.index += 1 - -class FakeOpener(object): - """Fakes a PyPI server""" - def __init__(self): - self.reqs = [] - - def __call__(self, *args): - return self - - def open(self, req, data=None, timeout=None): - self.reqs.append(req) - return self - - def read(self): - return b'xxx' - - def getheader(self, name, default=None): - return { - 'content-type': 'text/plain; charset=utf-8', - }.get(name.lower(), default) - - -class RegisterTestCase(BasePyPIRCCommandTestCase): - - def setUp(self): - super(RegisterTestCase, self).setUp() - # patching the password prompt - self._old_getpass = getpass.getpass - def _getpass(prompt): - return 'password' - getpass.getpass = _getpass - urllib.request._opener = None - self.old_opener = urllib.request.build_opener - self.conn = urllib.request.build_opener = FakeOpener() - - def tearDown(self): - getpass.getpass = self._old_getpass - urllib.request._opener = None - urllib.request.build_opener = self.old_opener - super(RegisterTestCase, self).tearDown() - - def _get_cmd(self, metadata=None): - if metadata is None: - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', - 'name': 'xxx', 'version': 'xxx'} - pkg_info, dist = self.create_dist(**metadata) - return register(dist) - - def test_create_pypirc(self): - # this test makes sure a .pypirc file - # is created when requested. - - # let's create a register instance - cmd = self._get_cmd() - - # we shouldn't have a .pypirc file yet - self.assertFalse(os.path.exists(self.rc)) - - # patching input and getpass.getpass - # so register gets happy - # - # Here's what we are faking : - # use your existing login (choice 1.) - # Username : 'tarek' - # Password : 'password' - # Save your login (y/N)? : 'y' - inputs = Inputs('1', 'tarek', 'y') - register_module.input = inputs.__call__ - # let's run the command - try: - cmd.run() - finally: - del register_module.input - - # we should have a brand new .pypirc file - self.assertTrue(os.path.exists(self.rc)) - - # with the content similar to WANTED_PYPIRC - f = open(self.rc) - try: - content = f.read() - self.assertEqual(content, WANTED_PYPIRC) - finally: - f.close() - - # now let's make sure the .pypirc file generated - # really works : we shouldn't be asked anything - # if we run the command again - def _no_way(prompt=''): - raise AssertionError(prompt) - register_module.input = _no_way - - cmd.show_response = 1 - cmd.run() - - # let's see what the server received : we should - # have 2 similar requests - self.assertEqual(len(self.conn.reqs), 2) - req1 = dict(self.conn.reqs[0].headers) - req2 = dict(self.conn.reqs[1].headers) - - self.assertEqual(req1['Content-length'], '1374') - self.assertEqual(req2['Content-length'], '1374') - self.assertIn(b'xxx', self.conn.reqs[1].data) - - def test_password_not_in_file(self): - - self.write_file(self.rc, PYPIRC_NOPASSWORD) - cmd = self._get_cmd() - cmd._set_config() - cmd.finalize_options() - cmd.send_metadata() - - # dist.password should be set - # therefore used afterwards by other commands - self.assertEqual(cmd.distribution.password, 'password') - - def test_registering(self): - # this test runs choice 2 - cmd = self._get_cmd() - inputs = Inputs('2', 'tarek', 'tarek@ziade.org') - register_module.input = inputs.__call__ - try: - # let's run the command - cmd.run() - finally: - del register_module.input - - # we should have send a request - self.assertEqual(len(self.conn.reqs), 1) - req = self.conn.reqs[0] - headers = dict(req.headers) - self.assertEqual(headers['Content-length'], '608') - self.assertIn(b'tarek', req.data) - - def test_password_reset(self): - # this test runs choice 3 - cmd = self._get_cmd() - inputs = Inputs('3', 'tarek@ziade.org') - register_module.input = inputs.__call__ - try: - # let's run the command - cmd.run() - finally: - del register_module.input - - # we should have send a request - self.assertEqual(len(self.conn.reqs), 1) - req = self.conn.reqs[0] - headers = dict(req.headers) - self.assertEqual(headers['Content-length'], '290') - self.assertIn(b'tarek', req.data) - - @unittest.skipUnless(docutils is not None, 'needs docutils') - def test_strict(self): - # testing the script option - # when on, the register command stops if - # the metadata is incomplete or if - # long_description is not reSt compliant - - # empty metadata - cmd = self._get_cmd({}) - cmd.ensure_finalized() - cmd.strict = 1 - self.assertRaises(DistutilsSetupError, cmd.run) - - # metadata are OK but long_description is broken - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'éxéxé', - 'name': 'xxx', 'version': 'xxx', - 'long_description': 'title\n==\n\ntext'} - - cmd = self._get_cmd(metadata) - cmd.ensure_finalized() - cmd.strict = 1 - self.assertRaises(DistutilsSetupError, cmd.run) - - # now something that works - metadata['long_description'] = 'title\n=====\n\ntext' - cmd = self._get_cmd(metadata) - cmd.ensure_finalized() - cmd.strict = 1 - inputs = Inputs('1', 'tarek', 'y') - register_module.input = inputs.__call__ - # let's run the command - try: - cmd.run() - finally: - del register_module.input - - # strict is not by default - cmd = self._get_cmd() - cmd.ensure_finalized() - inputs = Inputs('1', 'tarek', 'y') - register_module.input = inputs.__call__ - # let's run the command - try: - cmd.run() - finally: - del register_module.input - - # and finally a Unicode test (bug #12114) - metadata = {'url': 'xxx', 'author': '\u00c9ric', - 'author_email': 'xxx', 'name': 'xxx', - 'version': 'xxx', - 'description': 'Something about esszet \u00df', - 'long_description': 'More things about esszet \u00df'} - - cmd = self._get_cmd(metadata) - cmd.ensure_finalized() - cmd.strict = 1 - inputs = Inputs('1', 'tarek', 'y') - register_module.input = inputs.__call__ - # let's run the command - try: - cmd.run() - finally: - del register_module.input - - @unittest.skipUnless(docutils is not None, 'needs docutils') - def test_register_invalid_long_description(self): - description = ':funkie:`str`' # mimic Sphinx-specific markup - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', - 'name': 'xxx', 'version': 'xxx', - 'long_description': description} - cmd = self._get_cmd(metadata) - cmd.ensure_finalized() - cmd.strict = True - inputs = Inputs('2', 'tarek', 'tarek@ziade.org') - register_module.input = inputs - self.addCleanup(delattr, register_module, 'input') - - self.assertRaises(DistutilsSetupError, cmd.run) - - def test_check_metadata_deprecated(self): - # makes sure make_metadata is deprecated - cmd = self._get_cmd() - with check_warnings() as w: - warnings.simplefilter("always") - cmd.check_metadata() - self.assertEqual(len(w.warnings), 1) - - def test_list_classifiers(self): - cmd = self._get_cmd() - cmd.list_classifiers = 1 - cmd.run() - results = self.get_logs(INFO) - self.assertEqual(results, ['running check', 'xxx']) - - def test_show_response(self): - # test that the --show-response option return a well formatted response - cmd = self._get_cmd() - inputs = Inputs('1', 'tarek', 'y') - register_module.input = inputs.__call__ - cmd.show_response = 1 - try: - cmd.run() - finally: - del register_module.input - - results = self.get_logs(INFO) - self.assertEqual(results[3], 75 * '-' + '\nxxx\n' + 75 * '-') - - -def test_suite(): - return unittest.makeSuite(RegisterTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_sdist.py b/tests/test_sdist.py deleted file mode 100644 index 23db1269..00000000 --- a/tests/test_sdist.py +++ /dev/null @@ -1,492 +0,0 @@ -"""Tests for distutils.command.sdist.""" -import os -import tarfile -import unittest -import warnings -import zipfile -from os.path import join -from textwrap import dedent -from test.support import captured_stdout, check_warnings, run_unittest - -try: - import zlib - ZLIB_SUPPORT = True -except ImportError: - ZLIB_SUPPORT = False - -try: - import grp - import pwd - UID_GID_SUPPORT = True -except ImportError: - UID_GID_SUPPORT = False - -from distutils.command.sdist import sdist, show_formats -from distutils.core import Distribution -from distutils.tests.test_config import BasePyPIRCCommandTestCase -from distutils.errors import DistutilsOptionError -from distutils.spawn import find_executable -from distutils.log import WARN -from distutils.filelist import FileList -from distutils.archive_util import ARCHIVE_FORMATS - -SETUP_PY = """ -from distutils.core import setup -import somecode - -setup(name='fake') -""" - -MANIFEST = """\ -# file GENERATED by distutils, do NOT edit -README -buildout.cfg -inroot.txt -setup.py -data%(sep)sdata.dt -scripts%(sep)sscript.py -some%(sep)sfile.txt -some%(sep)sother_file.txt -somecode%(sep)s__init__.py -somecode%(sep)sdoc.dat -somecode%(sep)sdoc.txt -""" - -class SDistTestCase(BasePyPIRCCommandTestCase): - - def setUp(self): - # PyPIRCCommandTestCase creates a temp dir already - # and put it in self.tmp_dir - super(SDistTestCase, self).setUp() - # setting up an environment - self.old_path = os.getcwd() - os.mkdir(join(self.tmp_dir, 'somecode')) - os.mkdir(join(self.tmp_dir, 'dist')) - # a package, and a README - self.write_file((self.tmp_dir, 'README'), 'xxx') - self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#') - self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY) - os.chdir(self.tmp_dir) - - def tearDown(self): - # back to normal - os.chdir(self.old_path) - super(SDistTestCase, self).tearDown() - - def get_cmd(self, metadata=None): - """Returns a cmd""" - if metadata is None: - metadata = {'name': 'fake', 'version': '1.0', - 'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx'} - dist = Distribution(metadata) - dist.script_name = 'setup.py' - dist.packages = ['somecode'] - dist.include_package_data = True - cmd = sdist(dist) - cmd.dist_dir = 'dist' - return dist, cmd - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_prune_file_list(self): - # this test creates a project with some VCS dirs and an NFS rename - # file, then launches sdist to check they get pruned on all systems - - # creating VCS directories with some files in them - os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) - self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') - - os.mkdir(join(self.tmp_dir, 'somecode', '.hg')) - self.write_file((self.tmp_dir, 'somecode', '.hg', - 'ok'), 'xxx') - - os.mkdir(join(self.tmp_dir, 'somecode', '.git')) - self.write_file((self.tmp_dir, 'somecode', '.git', - 'ok'), 'xxx') - - self.write_file((self.tmp_dir, 'somecode', '.nfs0001'), 'xxx') - - # now building a sdist - dist, cmd = self.get_cmd() - - # zip is available universally - # (tar might not be installed under win32) - cmd.formats = ['zip'] - - cmd.ensure_finalized() - cmd.run() - - # now let's check what we have - dist_folder = join(self.tmp_dir, 'dist') - files = os.listdir(dist_folder) - self.assertEqual(files, ['fake-1.0.zip']) - - zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) - try: - content = zip_file.namelist() - finally: - zip_file.close() - - # making sure everything has been pruned correctly - expected = ['', 'PKG-INFO', 'README', 'setup.py', - 'somecode/', 'somecode/__init__.py'] - self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - @unittest.skipIf(find_executable('tar') is None, - "The tar command is not found") - @unittest.skipIf(find_executable('gzip') is None, - "The gzip command is not found") - def test_make_distribution(self): - # now building a sdist - dist, cmd = self.get_cmd() - - # creating a gztar then a tar - cmd.formats = ['gztar', 'tar'] - cmd.ensure_finalized() - cmd.run() - - # making sure we have two files - dist_folder = join(self.tmp_dir, 'dist') - result = os.listdir(dist_folder) - result.sort() - self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) - - os.remove(join(dist_folder, 'fake-1.0.tar')) - os.remove(join(dist_folder, 'fake-1.0.tar.gz')) - - # now trying a tar then a gztar - cmd.formats = ['tar', 'gztar'] - - cmd.ensure_finalized() - cmd.run() - - result = os.listdir(dist_folder) - result.sort() - self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_add_defaults(self): - - # http://bugs.python.org/issue2279 - - # add_default should also include - # data_files and package_data - dist, cmd = self.get_cmd() - - # filling data_files by pointing files - # in package_data - dist.package_data = {'': ['*.cfg', '*.dat'], - 'somecode': ['*.txt']} - self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') - self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#') - - # adding some data in data_files - data_dir = join(self.tmp_dir, 'data') - os.mkdir(data_dir) - self.write_file((data_dir, 'data.dt'), '#') - some_dir = join(self.tmp_dir, 'some') - os.mkdir(some_dir) - # make sure VCS directories are pruned (#14004) - hg_dir = join(self.tmp_dir, '.hg') - os.mkdir(hg_dir) - self.write_file((hg_dir, 'last-message.txt'), '#') - # a buggy regex used to prevent this from working on windows (#6884) - self.write_file((self.tmp_dir, 'buildout.cfg'), '#') - self.write_file((self.tmp_dir, 'inroot.txt'), '#') - self.write_file((some_dir, 'file.txt'), '#') - self.write_file((some_dir, 'other_file.txt'), '#') - - dist.data_files = [('data', ['data/data.dt', - 'buildout.cfg', - 'inroot.txt', - 'notexisting']), - 'some/file.txt', - 'some/other_file.txt'] - - # adding a script - script_dir = join(self.tmp_dir, 'scripts') - os.mkdir(script_dir) - self.write_file((script_dir, 'script.py'), '#') - dist.scripts = [join('scripts', 'script.py')] - - cmd.formats = ['zip'] - cmd.use_defaults = True - - cmd.ensure_finalized() - cmd.run() - - # now let's check what we have - dist_folder = join(self.tmp_dir, 'dist') - files = os.listdir(dist_folder) - self.assertEqual(files, ['fake-1.0.zip']) - - zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) - try: - content = zip_file.namelist() - finally: - zip_file.close() - - # making sure everything was added - expected = ['', 'PKG-INFO', 'README', 'buildout.cfg', - 'data/', 'data/data.dt', 'inroot.txt', - 'scripts/', 'scripts/script.py', 'setup.py', - 'some/', 'some/file.txt', 'some/other_file.txt', - 'somecode/', 'somecode/__init__.py', 'somecode/doc.dat', - 'somecode/doc.txt'] - self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) - - # checking the MANIFEST - f = open(join(self.tmp_dir, 'MANIFEST')) - try: - manifest = f.read() - finally: - f.close() - self.assertEqual(manifest, MANIFEST % {'sep': os.sep}) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_metadata_check_option(self): - # testing the `medata-check` option - dist, cmd = self.get_cmd(metadata={}) - - # this should raise some warnings ! - # with the `check` subcommand - cmd.ensure_finalized() - cmd.run() - warnings = [msg for msg in self.get_logs(WARN) if - msg.startswith('warning: check:')] - self.assertEqual(len(warnings), 2) - - # trying with a complete set of metadata - self.clear_logs() - dist, cmd = self.get_cmd() - cmd.ensure_finalized() - cmd.metadata_check = 0 - cmd.run() - warnings = [msg for msg in self.get_logs(WARN) if - msg.startswith('warning: check:')] - self.assertEqual(len(warnings), 0) - - def test_check_metadata_deprecated(self): - # makes sure make_metadata is deprecated - dist, cmd = self.get_cmd() - with check_warnings() as w: - warnings.simplefilter("always") - cmd.check_metadata() - self.assertEqual(len(w.warnings), 1) - - def test_show_formats(self): - with captured_stdout() as stdout: - show_formats() - - # the output should be a header line + one line per format - num_formats = len(ARCHIVE_FORMATS.keys()) - output = [line for line in stdout.getvalue().split('\n') - if line.strip().startswith('--formats=')] - self.assertEqual(len(output), num_formats) - - def test_finalize_options(self): - dist, cmd = self.get_cmd() - cmd.finalize_options() - - # default options set by finalize - self.assertEqual(cmd.manifest, 'MANIFEST') - self.assertEqual(cmd.template, 'MANIFEST.in') - self.assertEqual(cmd.dist_dir, 'dist') - - # formats has to be a string splitable on (' ', ',') or - # a stringlist - cmd.formats = 1 - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - cmd.formats = ['zip'] - cmd.finalize_options() - - # formats has to be known - cmd.formats = 'supazipa' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - # the following tests make sure there is a nice error message instead - # of a traceback when parsing an invalid manifest template - - def _check_template(self, content): - dist, cmd = self.get_cmd() - os.chdir(self.tmp_dir) - self.write_file('MANIFEST.in', content) - cmd.ensure_finalized() - cmd.filelist = FileList() - cmd.read_template() - warnings = self.get_logs(WARN) - self.assertEqual(len(warnings), 1) - - def test_invalid_template_unknown_command(self): - self._check_template('taunt knights *') - - def test_invalid_template_wrong_arguments(self): - # this manifest command takes one argument - self._check_template('prune') - - @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only') - def test_invalid_template_wrong_path(self): - # on Windows, trailing slashes are not allowed - # this used to crash instead of raising a warning: #8286 - self._check_template('include examples/') - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_get_file_list(self): - # make sure MANIFEST is recalculated - dist, cmd = self.get_cmd() - - # filling data_files by pointing files in package_data - dist.package_data = {'somecode': ['*.txt']} - self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') - cmd.formats = ['gztar'] - cmd.ensure_finalized() - cmd.run() - - f = open(cmd.manifest) - try: - manifest = [line.strip() for line in f.read().split('\n') - if line.strip() != ''] - finally: - f.close() - - self.assertEqual(len(manifest), 5) - - # adding a file - self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') - - # make sure build_py is reinitialized, like a fresh run - build_py = dist.get_command_obj('build_py') - build_py.finalized = False - build_py.ensure_finalized() - - cmd.run() - - f = open(cmd.manifest) - try: - manifest2 = [line.strip() for line in f.read().split('\n') - if line.strip() != ''] - finally: - f.close() - - # do we have the new file in MANIFEST ? - self.assertEqual(len(manifest2), 6) - self.assertIn('doc2.txt', manifest2[-1]) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_manifest_marker(self): - # check that autogenerated MANIFESTs have a marker - dist, cmd = self.get_cmd() - cmd.ensure_finalized() - cmd.run() - - f = open(cmd.manifest) - try: - manifest = [line.strip() for line in f.read().split('\n') - if line.strip() != ''] - finally: - f.close() - - self.assertEqual(manifest[0], - '# file GENERATED by distutils, do NOT edit') - - @unittest.skipUnless(ZLIB_SUPPORT, "Need zlib support to run") - def test_manifest_comments(self): - # make sure comments don't cause exceptions or wrong includes - contents = dedent("""\ - # bad.py - #bad.py - good.py - """) - dist, cmd = self.get_cmd() - cmd.ensure_finalized() - self.write_file((self.tmp_dir, cmd.manifest), contents) - self.write_file((self.tmp_dir, 'good.py'), '# pick me!') - self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!") - self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!") - cmd.run() - self.assertEqual(cmd.filelist.files, ['good.py']) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_manual_manifest(self): - # check that a MANIFEST without a marker is left alone - dist, cmd = self.get_cmd() - cmd.formats = ['gztar'] - cmd.ensure_finalized() - self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') - self.write_file((self.tmp_dir, 'README.manual'), - 'This project maintains its MANIFEST file itself.') - cmd.run() - self.assertEqual(cmd.filelist.files, ['README.manual']) - - f = open(cmd.manifest) - try: - manifest = [line.strip() for line in f.read().split('\n') - if line.strip() != ''] - finally: - f.close() - - self.assertEqual(manifest, ['README.manual']) - - archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') - archive = tarfile.open(archive_name) - try: - filenames = [tarinfo.name for tarinfo in archive] - finally: - archive.close() - self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO', - 'fake-1.0/README.manual']) - - @unittest.skipUnless(ZLIB_SUPPORT, "requires zlib") - @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") - @unittest.skipIf(find_executable('tar') is None, - "The tar command is not found") - @unittest.skipIf(find_executable('gzip') is None, - "The gzip command is not found") - def test_make_distribution_owner_group(self): - # now building a sdist - dist, cmd = self.get_cmd() - - # creating a gztar and specifying the owner+group - cmd.formats = ['gztar'] - cmd.owner = pwd.getpwuid(0)[0] - cmd.group = grp.getgrgid(0)[0] - cmd.ensure_finalized() - cmd.run() - - # making sure we have the good rights - archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') - archive = tarfile.open(archive_name) - try: - for member in archive.getmembers(): - self.assertEqual(member.uid, 0) - self.assertEqual(member.gid, 0) - finally: - archive.close() - - # building a sdist again - dist, cmd = self.get_cmd() - - # creating a gztar - cmd.formats = ['gztar'] - cmd.ensure_finalized() - cmd.run() - - # making sure we have the good rights - archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') - archive = tarfile.open(archive_name) - - # note that we are not testing the group ownership here - # because, depending on the platforms and the container - # rights (see #7408) - try: - for member in archive.getmembers(): - self.assertEqual(member.uid, os.getuid()) - finally: - archive.close() - -def test_suite(): - return unittest.makeSuite(SDistTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_spawn.py b/tests/test_spawn.py deleted file mode 100644 index cf1faad5..00000000 --- a/tests/test_spawn.py +++ /dev/null @@ -1,132 +0,0 @@ -"""Tests for distutils.spawn.""" -import os -import stat -import sys -import unittest.mock -from test.support import run_unittest, unix_shell -from test import support as test_support - -from distutils.spawn import find_executable -from distutils.spawn import spawn -from distutils.errors import DistutilsExecError -from distutils.tests import support - -class SpawnTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - @unittest.skipUnless(os.name in ('nt', 'posix'), - 'Runs only under posix or nt') - def test_spawn(self): - tmpdir = self.mkdtemp() - - # creating something executable - # through the shell that returns 1 - if sys.platform != 'win32': - exe = os.path.join(tmpdir, 'foo.sh') - self.write_file(exe, '#!%s\nexit 1' % unix_shell) - else: - exe = os.path.join(tmpdir, 'foo.bat') - self.write_file(exe, 'exit 1') - - os.chmod(exe, 0o777) - self.assertRaises(DistutilsExecError, spawn, [exe]) - - # now something that works - if sys.platform != 'win32': - exe = os.path.join(tmpdir, 'foo.sh') - self.write_file(exe, '#!%s\nexit 0' % unix_shell) - else: - exe = os.path.join(tmpdir, 'foo.bat') - self.write_file(exe, 'exit 0') - - os.chmod(exe, 0o777) - spawn([exe]) # should work without any error - - def test_find_executable(self): - with test_support.temp_dir() as tmp_dir: - # use TESTFN to get a pseudo-unique filename - program_noeext = test_support.TESTFN - # Give the temporary program an ".exe" suffix for all. - # It's needed on Windows and not harmful on other platforms. - program = program_noeext + ".exe" - - filename = os.path.join(tmp_dir, program) - with open(filename, "wb"): - pass - os.chmod(filename, stat.S_IXUSR) - - # test path parameter - rv = find_executable(program, path=tmp_dir) - self.assertEqual(rv, filename) - - if sys.platform == 'win32': - # test without ".exe" extension - rv = find_executable(program_noeext, path=tmp_dir) - self.assertEqual(rv, filename) - - # test find in the current directory - with test_support.change_cwd(tmp_dir): - rv = find_executable(program) - self.assertEqual(rv, program) - - # test non-existent program - dont_exist_program = "dontexist_" + program - rv = find_executable(dont_exist_program , path=tmp_dir) - self.assertIsNone(rv) - - # PATH='': no match, except in the current directory - with test_support.EnvironmentVarGuard() as env: - env['PATH'] = '' - with unittest.mock.patch('distutils.spawn.os.confstr', - return_value=tmp_dir, create=True), \ - unittest.mock.patch('distutils.spawn.os.defpath', - tmp_dir): - rv = find_executable(program) - self.assertIsNone(rv) - - # look in current directory - with test_support.change_cwd(tmp_dir): - rv = find_executable(program) - self.assertEqual(rv, program) - - # PATH=':': explicitly looks in the current directory - with test_support.EnvironmentVarGuard() as env: - env['PATH'] = os.pathsep - with unittest.mock.patch('distutils.spawn.os.confstr', - return_value='', create=True), \ - unittest.mock.patch('distutils.spawn.os.defpath', ''): - rv = find_executable(program) - self.assertIsNone(rv) - - # look in current directory - with test_support.change_cwd(tmp_dir): - rv = find_executable(program) - self.assertEqual(rv, program) - - # missing PATH: test os.confstr("CS_PATH") and os.defpath - with test_support.EnvironmentVarGuard() as env: - env.pop('PATH', None) - - # without confstr - with unittest.mock.patch('distutils.spawn.os.confstr', - side_effect=ValueError, - create=True), \ - unittest.mock.patch('distutils.spawn.os.defpath', - tmp_dir): - rv = find_executable(program) - self.assertEqual(rv, filename) - - # with confstr - with unittest.mock.patch('distutils.spawn.os.confstr', - return_value=tmp_dir, create=True), \ - unittest.mock.patch('distutils.spawn.os.defpath', ''): - rv = find_executable(program) - self.assertEqual(rv, filename) - - -def test_suite(): - return unittest.makeSuite(SpawnTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py deleted file mode 100644 index 236755d0..00000000 --- a/tests/test_sysconfig.py +++ /dev/null @@ -1,274 +0,0 @@ -"""Tests for distutils.sysconfig.""" -import contextlib -import os -import shutil -import subprocess -import sys -import textwrap -import unittest - -from distutils import sysconfig -from distutils.ccompiler import get_default_compiler -from distutils.tests import support -from test.support import TESTFN, run_unittest, check_warnings, swap_item - -class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): - def setUp(self): - super(SysconfigTestCase, self).setUp() - self.makefile = None - - def tearDown(self): - if self.makefile is not None: - os.unlink(self.makefile) - self.cleanup_testfn() - super(SysconfigTestCase, self).tearDown() - - def cleanup_testfn(self): - if os.path.isfile(TESTFN): - os.remove(TESTFN) - elif os.path.isdir(TESTFN): - shutil.rmtree(TESTFN) - - def test_get_config_h_filename(self): - config_h = sysconfig.get_config_h_filename() - self.assertTrue(os.path.isfile(config_h), config_h) - - def test_get_python_lib(self): - # XXX doesn't work on Linux when Python was never installed before - #self.assertTrue(os.path.isdir(lib_dir), lib_dir) - # test for pythonxx.lib? - self.assertNotEqual(sysconfig.get_python_lib(), - sysconfig.get_python_lib(prefix=TESTFN)) - - def test_get_config_vars(self): - cvars = sysconfig.get_config_vars() - self.assertIsInstance(cvars, dict) - self.assertTrue(cvars) - - def test_srcdir(self): - # See Issues #15322, #15364. - srcdir = sysconfig.get_config_var('srcdir') - - self.assertTrue(os.path.isabs(srcdir), srcdir) - self.assertTrue(os.path.isdir(srcdir), srcdir) - - if sysconfig.python_build: - # The python executable has not been installed so srcdir - # should be a full source checkout. - Python_h = os.path.join(srcdir, 'Include', 'Python.h') - self.assertTrue(os.path.exists(Python_h), Python_h) - self.assertTrue(sysconfig._is_python_source_dir(srcdir)) - elif os.name == 'posix': - self.assertEqual( - os.path.dirname(sysconfig.get_makefile_filename()), - srcdir) - - def test_srcdir_independent_of_cwd(self): - # srcdir should be independent of the current working directory - # See Issues #15322, #15364. - srcdir = sysconfig.get_config_var('srcdir') - cwd = os.getcwd() - try: - os.chdir('..') - srcdir2 = sysconfig.get_config_var('srcdir') - finally: - os.chdir(cwd) - self.assertEqual(srcdir, srcdir2) - - def customize_compiler(self): - # make sure AR gets caught - class compiler: - compiler_type = 'unix' - - def set_executables(self, **kw): - self.exes = kw - - sysconfig_vars = { - 'AR': 'sc_ar', - 'CC': 'sc_cc', - 'CXX': 'sc_cxx', - 'ARFLAGS': '--sc-arflags', - 'CFLAGS': '--sc-cflags', - 'CCSHARED': '--sc-ccshared', - 'LDSHARED': 'sc_ldshared', - 'SHLIB_SUFFIX': 'sc_shutil_suffix', - - # On macOS, disable _osx_support.customize_compiler() - 'CUSTOMIZED_OSX_COMPILER': 'True', - } - - comp = compiler() - with contextlib.ExitStack() as cm: - for key, value in sysconfig_vars.items(): - cm.enter_context(swap_item(sysconfig._config_vars, key, value)) - sysconfig.customize_compiler(comp) - - return comp - - @unittest.skipUnless(get_default_compiler() == 'unix', - 'not testing if default compiler is not unix') - def test_customize_compiler(self): - # Make sure that sysconfig._config_vars is initialized - sysconfig.get_config_vars() - - os.environ['AR'] = 'env_ar' - os.environ['CC'] = 'env_cc' - os.environ['CPP'] = 'env_cpp' - os.environ['CXX'] = 'env_cxx --env-cxx-flags' - os.environ['LDSHARED'] = 'env_ldshared' - os.environ['LDFLAGS'] = '--env-ldflags' - os.environ['ARFLAGS'] = '--env-arflags' - os.environ['CFLAGS'] = '--env-cflags' - os.environ['CPPFLAGS'] = '--env-cppflags' - - comp = self.customize_compiler() - self.assertEqual(comp.exes['archiver'], - 'env_ar --env-arflags') - self.assertEqual(comp.exes['preprocessor'], - 'env_cpp --env-cppflags') - self.assertEqual(comp.exes['compiler'], - 'env_cc --sc-cflags --env-cflags --env-cppflags') - self.assertEqual(comp.exes['compiler_so'], - ('env_cc --sc-cflags ' - '--env-cflags ''--env-cppflags --sc-ccshared')) - self.assertEqual(comp.exes['compiler_cxx'], - 'env_cxx --env-cxx-flags') - self.assertEqual(comp.exes['linker_exe'], - 'env_cc') - self.assertEqual(comp.exes['linker_so'], - ('env_ldshared --env-ldflags --env-cflags' - ' --env-cppflags')) - self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') - - del os.environ['AR'] - del os.environ['CC'] - del os.environ['CPP'] - del os.environ['CXX'] - del os.environ['LDSHARED'] - del os.environ['LDFLAGS'] - del os.environ['ARFLAGS'] - del os.environ['CFLAGS'] - del os.environ['CPPFLAGS'] - - comp = self.customize_compiler() - self.assertEqual(comp.exes['archiver'], - 'sc_ar --sc-arflags') - self.assertEqual(comp.exes['preprocessor'], - 'sc_cc -E') - self.assertEqual(comp.exes['compiler'], - 'sc_cc --sc-cflags') - self.assertEqual(comp.exes['compiler_so'], - 'sc_cc --sc-cflags --sc-ccshared') - self.assertEqual(comp.exes['compiler_cxx'], - 'sc_cxx') - self.assertEqual(comp.exes['linker_exe'], - 'sc_cc') - self.assertEqual(comp.exes['linker_so'], - 'sc_ldshared') - self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') - - def test_parse_makefile_base(self): - self.makefile = TESTFN - fd = open(self.makefile, 'w') - try: - fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') - fd.write('VAR=$OTHER\nOTHER=foo') - finally: - fd.close() - d = sysconfig.parse_makefile(self.makefile) - self.assertEqual(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", - 'OTHER': 'foo'}) - - def test_parse_makefile_literal_dollar(self): - self.makefile = TESTFN - fd = open(self.makefile, 'w') - try: - fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') - fd.write('VAR=$OTHER\nOTHER=foo') - finally: - fd.close() - d = sysconfig.parse_makefile(self.makefile) - self.assertEqual(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", - 'OTHER': 'foo'}) - - - def test_sysconfig_module(self): - import sysconfig as global_sysconfig - self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), - sysconfig.get_config_var('CFLAGS')) - self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), - sysconfig.get_config_var('LDFLAGS')) - - @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'), - 'compiler flags customized') - def test_sysconfig_compiler_vars(self): - # On OS X, binary installers support extension module building on - # various levels of the operating system with differing Xcode - # configurations. This requires customization of some of the - # compiler configuration directives to suit the environment on - # the installed machine. Some of these customizations may require - # running external programs and, so, are deferred until needed by - # the first extension module build. With Python 3.3, only - # the Distutils version of sysconfig is used for extension module - # builds, which happens earlier in the Distutils tests. This may - # cause the following tests to fail since no tests have caused - # the global version of sysconfig to call the customization yet. - # The solution for now is to simply skip this test in this case. - # The longer-term solution is to only have one version of sysconfig. - - import sysconfig as global_sysconfig - if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'): - self.skipTest('compiler flags customized') - self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), - sysconfig.get_config_var('LDSHARED')) - self.assertEqual(global_sysconfig.get_config_var('CC'), - sysconfig.get_config_var('CC')) - - @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, - 'EXT_SUFFIX required for this test') - def test_SO_deprecation(self): - self.assertWarns(DeprecationWarning, - sysconfig.get_config_var, 'SO') - - @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, - 'EXT_SUFFIX required for this test') - def test_SO_value(self): - with check_warnings(('', DeprecationWarning)): - self.assertEqual(sysconfig.get_config_var('SO'), - sysconfig.get_config_var('EXT_SUFFIX')) - - @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, - 'EXT_SUFFIX required for this test') - def test_SO_in_vars(self): - vars = sysconfig.get_config_vars() - self.assertIsNotNone(vars['SO']) - self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) - - def test_customize_compiler_before_get_config_vars(self): - # Issue #21923: test that a Distribution compiler - # instance can be called without an explicit call to - # get_config_vars(). - with open(TESTFN, 'w') as f: - f.writelines(textwrap.dedent('''\ - from distutils.core import Distribution - config = Distribution().get_command_obj('config') - # try_compile may pass or it may fail if no compiler - # is found but it should not raise an exception. - rc = config.try_compile('int x;') - ''')) - p = subprocess.Popen([str(sys.executable), TESTFN], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - outs, errs = p.communicate() - self.assertEqual(0, p.returncode, "Subprocess failed: " + outs) - - -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(SysconfigTestCase)) - return suite - - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/tests/test_text_file.py b/tests/test_text_file.py deleted file mode 100644 index 7e76240a..00000000 --- a/tests/test_text_file.py +++ /dev/null @@ -1,107 +0,0 @@ -"""Tests for distutils.text_file.""" -import os -import unittest -from distutils.text_file import TextFile -from distutils.tests import support -from test.support import run_unittest - -TEST_DATA = """# test file - -line 3 \\ -# intervening comment - continues on next line -""" - -class TextFileTestCase(support.TempdirManager, unittest.TestCase): - - def test_class(self): - # old tests moved from text_file.__main__ - # so they are really called by the buildbots - - # result 1: no fancy options - result1 = ['# test file\n', '\n', 'line 3 \\\n', - '# intervening comment\n', - ' continues on next line\n'] - - # result 2: just strip comments - result2 = ["\n", - "line 3 \\\n", - " continues on next line\n"] - - # result 3: just strip blank lines - result3 = ["# test file\n", - "line 3 \\\n", - "# intervening comment\n", - " continues on next line\n"] - - # result 4: default, strip comments, blank lines, - # and trailing whitespace - result4 = ["line 3 \\", - " continues on next line"] - - # result 5: strip comments and blanks, plus join lines (but don't - # "collapse" joined lines - result5 = ["line 3 continues on next line"] - - # result 6: strip comments and blanks, plus join lines (and - # "collapse" joined lines - result6 = ["line 3 continues on next line"] - - def test_input(count, description, file, expected_result): - result = file.readlines() - self.assertEqual(result, expected_result) - - tmpdir = self.mkdtemp() - filename = os.path.join(tmpdir, "test.txt") - out_file = open(filename, "w") - try: - out_file.write(TEST_DATA) - finally: - out_file.close() - - in_file = TextFile(filename, strip_comments=0, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - try: - test_input(1, "no processing", in_file, result1) - finally: - in_file.close() - - in_file = TextFile(filename, strip_comments=1, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - try: - test_input(2, "strip comments", in_file, result2) - finally: - in_file.close() - - in_file = TextFile(filename, strip_comments=0, skip_blanks=1, - lstrip_ws=0, rstrip_ws=0) - try: - test_input(3, "strip blanks", in_file, result3) - finally: - in_file.close() - - in_file = TextFile(filename) - try: - test_input(4, "default processing", in_file, result4) - finally: - in_file.close() - - in_file = TextFile(filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1) - try: - test_input(5, "join lines without collapsing", in_file, result5) - finally: - in_file.close() - - in_file = TextFile(filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1, collapse_join=1) - try: - test_input(6, "join lines with collapsing", in_file, result6) - finally: - in_file.close() - -def test_suite(): - return unittest.makeSuite(TextFileTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py deleted file mode 100644 index eef702cf..00000000 --- a/tests/test_unixccompiler.py +++ /dev/null @@ -1,141 +0,0 @@ -"""Tests for distutils.unixccompiler.""" -import sys -import unittest -from test.support import EnvironmentVarGuard, run_unittest - -from distutils import sysconfig -from distutils.unixccompiler import UnixCCompiler - -class UnixCCompilerTestCase(unittest.TestCase): - - def setUp(self): - self._backup_platform = sys.platform - self._backup_get_config_var = sysconfig.get_config_var - class CompilerWrapper(UnixCCompiler): - def rpath_foo(self): - return self.runtime_library_dir_option('/foo') - self.cc = CompilerWrapper() - - def tearDown(self): - sys.platform = self._backup_platform - sysconfig.get_config_var = self._backup_get_config_var - - @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") - def test_runtime_libdir_option(self): - # Issue#5900 - # - # Ensure RUNPATH is added to extension modules with RPATH if - # GNU ld is used - - # darwin - sys.platform = 'darwin' - self.assertEqual(self.cc.rpath_foo(), '-L/foo') - - # hp-ux - sys.platform = 'hp-ux' - old_gcv = sysconfig.get_config_var - def gcv(v): - return 'xxx' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo']) - - def gcv(v): - return 'gcc' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) - - def gcv(v): - return 'g++' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) - - sysconfig.get_config_var = old_gcv - - # GCC GNULD - sys.platform = 'bar' - def gcv(v): - if v == 'CC': - return 'gcc' - elif v == 'GNULD': - return 'yes' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') - - # GCC non-GNULD - sys.platform = 'bar' - def gcv(v): - if v == 'CC': - return 'gcc' - elif v == 'GNULD': - return 'no' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') - - # GCC GNULD with fully qualified configuration prefix - # see #7617 - sys.platform = 'bar' - def gcv(v): - if v == 'CC': - return 'x86_64-pc-linux-gnu-gcc-4.4.2' - elif v == 'GNULD': - return 'yes' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') - - # non-GCC GNULD - sys.platform = 'bar' - def gcv(v): - if v == 'CC': - return 'cc' - elif v == 'GNULD': - return 'yes' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-R/foo') - - # non-GCC non-GNULD - sys.platform = 'bar' - def gcv(v): - if v == 'CC': - return 'cc' - elif v == 'GNULD': - return 'no' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-R/foo') - - @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') - def test_osx_cc_overrides_ldshared(self): - # Issue #18080: - # ensure that setting CC env variable also changes default linker - def gcv(v): - if v == 'LDSHARED': - return 'gcc-4.2 -bundle -undefined dynamic_lookup ' - return 'gcc-4.2' - sysconfig.get_config_var = gcv - with EnvironmentVarGuard() as env: - env['CC'] = 'my_cc' - del env['LDSHARED'] - sysconfig.customize_compiler(self.cc) - self.assertEqual(self.cc.linker_so[0], 'my_cc') - - @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') - def test_osx_explicit_ldshared(self): - # Issue #18080: - # ensure that setting CC env variable does not change - # explicit LDSHARED setting for linker - def gcv(v): - if v == 'LDSHARED': - return 'gcc-4.2 -bundle -undefined dynamic_lookup ' - return 'gcc-4.2' - sysconfig.get_config_var = gcv - with EnvironmentVarGuard() as env: - env['CC'] = 'my_cc' - env['LDSHARED'] = 'my_ld -bundle -dynamic' - sysconfig.customize_compiler(self.cc) - self.assertEqual(self.cc.linker_so[0], 'my_ld') - - -def test_suite(): - return unittest.makeSuite(UnixCCompilerTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_upload.py b/tests/test_upload.py deleted file mode 100644 index bca5516d..00000000 --- a/tests/test_upload.py +++ /dev/null @@ -1,223 +0,0 @@ -"""Tests for distutils.command.upload.""" -import os -import unittest -import unittest.mock as mock -from urllib.request import HTTPError - -from test.support import run_unittest - -from distutils.command import upload as upload_mod -from distutils.command.upload import upload -from distutils.core import Distribution -from distutils.errors import DistutilsError -from distutils.log import ERROR, INFO - -from distutils.tests.test_config import PYPIRC, BasePyPIRCCommandTestCase - -PYPIRC_LONG_PASSWORD = """\ -[distutils] - -index-servers = - server1 - server2 - -[server1] -username:me -password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - -[server2] -username:meagain -password: secret -realm:acme -repository:http://another.pypi/ -""" - - -PYPIRC_NOPASSWORD = """\ -[distutils] - -index-servers = - server1 - -[server1] -username:me -""" - -class FakeOpen(object): - - def __init__(self, url, msg=None, code=None): - self.url = url - if not isinstance(url, str): - self.req = url - else: - self.req = None - self.msg = msg or 'OK' - self.code = code or 200 - - def getheader(self, name, default=None): - return { - 'content-type': 'text/plain; charset=utf-8', - }.get(name.lower(), default) - - def read(self): - return b'xyzzy' - - def getcode(self): - return self.code - - -class uploadTestCase(BasePyPIRCCommandTestCase): - - def setUp(self): - super(uploadTestCase, self).setUp() - self.old_open = upload_mod.urlopen - upload_mod.urlopen = self._urlopen - self.last_open = None - self.next_msg = None - self.next_code = None - - def tearDown(self): - upload_mod.urlopen = self.old_open - super(uploadTestCase, self).tearDown() - - def _urlopen(self, url): - self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code) - return self.last_open - - def test_finalize_options(self): - - # new format - self.write_file(self.rc, PYPIRC) - dist = Distribution() - cmd = upload(dist) - cmd.finalize_options() - for attr, waited in (('username', 'me'), ('password', 'secret'), - ('realm', 'pypi'), - ('repository', 'https://upload.pypi.org/legacy/')): - self.assertEqual(getattr(cmd, attr), waited) - - def test_saved_password(self): - # file with no password - self.write_file(self.rc, PYPIRC_NOPASSWORD) - - # make sure it passes - dist = Distribution() - cmd = upload(dist) - cmd.finalize_options() - self.assertEqual(cmd.password, None) - - # make sure we get it as well, if another command - # initialized it at the dist level - dist.password = 'xxx' - cmd = upload(dist) - cmd.finalize_options() - self.assertEqual(cmd.password, 'xxx') - - def test_upload(self): - tmp = self.mkdtemp() - path = os.path.join(tmp, 'xxx') - self.write_file(path) - command, pyversion, filename = 'xxx', '2.6', path - dist_files = [(command, pyversion, filename)] - self.write_file(self.rc, PYPIRC_LONG_PASSWORD) - - # lets run it - pkg_dir, dist = self.create_dist(dist_files=dist_files) - cmd = upload(dist) - cmd.show_response = 1 - cmd.ensure_finalized() - cmd.run() - - # what did we send ? - headers = dict(self.last_open.req.headers) - self.assertGreaterEqual(int(headers['Content-length']), 2162) - content_type = headers['Content-type'] - self.assertTrue(content_type.startswith('multipart/form-data')) - self.assertEqual(self.last_open.req.get_method(), 'POST') - expected_url = 'https://upload.pypi.org/legacy/' - self.assertEqual(self.last_open.req.get_full_url(), expected_url) - data = self.last_open.req.data - self.assertIn(b'xxx',data) - self.assertIn(b'protocol_version', data) - self.assertIn(b'sha256_digest', data) - self.assertIn( - b'cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf' - b'6860', - data - ) - if b'md5_digest' in data: - self.assertIn(b'f561aaf6ef0bf14d4208bb46a4ccb3ad', data) - if b'blake2_256_digest' in data: - self.assertIn( - b'b6f289a27d4fe90da63c503bfe0a9b761a8f76bb86148565065f040be' - b'6d1c3044cf7ded78ef800509bccb4b648e507d88dc6383d67642aadcc' - b'ce443f1534330a', - data - ) - - # The PyPI response body was echoed - results = self.get_logs(INFO) - self.assertEqual(results[-1], 75 * '-' + '\nxyzzy\n' + 75 * '-') - - # bpo-32304: archives whose last byte was b'\r' were corrupted due to - # normalization intended for Mac OS 9. - def test_upload_correct_cr(self): - # content that ends with \r should not be modified. - tmp = self.mkdtemp() - path = os.path.join(tmp, 'xxx') - self.write_file(path, content='yy\r') - command, pyversion, filename = 'xxx', '2.6', path - dist_files = [(command, pyversion, filename)] - self.write_file(self.rc, PYPIRC_LONG_PASSWORD) - - # other fields that ended with \r used to be modified, now are - # preserved. - pkg_dir, dist = self.create_dist( - dist_files=dist_files, - description='long description\r' - ) - cmd = upload(dist) - cmd.show_response = 1 - cmd.ensure_finalized() - cmd.run() - - headers = dict(self.last_open.req.headers) - self.assertGreaterEqual(int(headers['Content-length']), 2172) - self.assertIn(b'long description\r', self.last_open.req.data) - - def test_upload_fails(self): - self.next_msg = "Not Found" - self.next_code = 404 - self.assertRaises(DistutilsError, self.test_upload) - - def test_wrong_exception_order(self): - tmp = self.mkdtemp() - path = os.path.join(tmp, 'xxx') - self.write_file(path) - dist_files = [('xxx', '2.6', path)] # command, pyversion, filename - self.write_file(self.rc, PYPIRC_LONG_PASSWORD) - - pkg_dir, dist = self.create_dist(dist_files=dist_files) - tests = [ - (OSError('oserror'), 'oserror', OSError), - (HTTPError('url', 400, 'httperror', {}, None), - 'Upload failed (400): httperror', DistutilsError), - ] - for exception, expected, raised_exception in tests: - with self.subTest(exception=type(exception).__name__): - with mock.patch('distutils.command.upload.urlopen', - new=mock.Mock(side_effect=exception)): - with self.assertRaises(raised_exception): - cmd = upload(dist) - cmd.ensure_finalized() - cmd.run() - results = self.get_logs(ERROR) - self.assertIn(expected, results[-1]) - self.clear_logs() - - -def test_suite(): - return unittest.makeSuite(uploadTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_util.py b/tests/test_util.py deleted file mode 100644 index bf0d4333..00000000 --- a/tests/test_util.py +++ /dev/null @@ -1,309 +0,0 @@ -"""Tests for distutils.util.""" -import os -import sys -import unittest -from copy import copy -from test.support import run_unittest -from unittest import mock - -from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError -from distutils.util import (get_platform, convert_path, change_root, - check_environ, split_quoted, strtobool, - rfc822_escape, byte_compile, - grok_environment_error) -from distutils import util # used to patch _environ_checked -from distutils.sysconfig import get_config_vars -from distutils import sysconfig -from distutils.tests import support -import _osx_support - -class UtilTestCase(support.EnvironGuard, unittest.TestCase): - - def setUp(self): - super(UtilTestCase, self).setUp() - # saving the environment - self.name = os.name - self.platform = sys.platform - self.version = sys.version - self.sep = os.sep - self.join = os.path.join - self.isabs = os.path.isabs - self.splitdrive = os.path.splitdrive - self._config_vars = copy(sysconfig._config_vars) - - # patching os.uname - if hasattr(os, 'uname'): - self.uname = os.uname - self._uname = os.uname() - else: - self.uname = None - self._uname = None - - os.uname = self._get_uname - - def tearDown(self): - # getting back the environment - os.name = self.name - sys.platform = self.platform - sys.version = self.version - os.sep = self.sep - os.path.join = self.join - os.path.isabs = self.isabs - os.path.splitdrive = self.splitdrive - if self.uname is not None: - os.uname = self.uname - else: - del os.uname - sysconfig._config_vars = copy(self._config_vars) - super(UtilTestCase, self).tearDown() - - def _set_uname(self, uname): - self._uname = uname - - def _get_uname(self): - return self._uname - - def test_get_platform(self): - - # windows XP, 32bits - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Intel)]') - sys.platform = 'win32' - self.assertEqual(get_platform(), 'win32') - - # windows XP, amd64 - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Amd64)]') - sys.platform = 'win32' - self.assertEqual(get_platform(), 'win-amd64') - - # macbook - os.name = 'posix' - sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' - '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') - sys.platform = 'darwin' - self._set_uname(('Darwin', 'macziade', '8.11.1', - ('Darwin Kernel Version 8.11.1: ' - 'Wed Oct 10 18:23:28 PDT 2007; ' - 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3' - - get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' - '-fwrapv -O3 -Wall -Wstrict-prototypes') - - cursize = sys.maxsize - sys.maxsize = (2 ** 31)-1 - try: - self.assertEqual(get_platform(), 'macosx-10.3-i386') - finally: - sys.maxsize = cursize - - # macbook with fat binaries (fat, universal or fat64) - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4' - get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEqual(get_platform(), 'macosx-10.4-fat') - - _osx_support._remove_original_values(get_config_vars()) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1' - self.assertEqual(get_platform(), 'macosx-10.4-fat') - - - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEqual(get_platform(), 'macosx-10.4-intel') - - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - self.assertEqual(get_platform(), 'macosx-10.4-fat3') - - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - self.assertEqual(get_platform(), 'macosx-10.4-universal') - - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEqual(get_platform(), 'macosx-10.4-fat64') - - for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3'%(arch,)) - - self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,)) - - - # linux debian sarge - os.name = 'posix' - sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' - '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]') - sys.platform = 'linux2' - self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', - '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) - - self.assertEqual(get_platform(), 'linux-i686') - - # XXX more platforms to tests here - - def test_convert_path(self): - # linux/mac - os.sep = '/' - def _join(path): - return '/'.join(path) - os.path.join = _join - - self.assertEqual(convert_path('/home/to/my/stuff'), - '/home/to/my/stuff') - - # win - os.sep = '\\' - def _join(*path): - return '\\'.join(path) - os.path.join = _join - - self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') - self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') - - self.assertEqual(convert_path('home/to/my/stuff'), - 'home\\to\\my\\stuff') - self.assertEqual(convert_path('.'), - os.curdir) - - def test_change_root(self): - # linux/mac - os.name = 'posix' - def _isabs(path): - return path[0] == '/' - os.path.isabs = _isabs - def _join(*path): - return '/'.join(path) - os.path.join = _join - - self.assertEqual(change_root('/root', '/old/its/here'), - '/root/old/its/here') - self.assertEqual(change_root('/root', 'its/here'), - '/root/its/here') - - # windows - os.name = 'nt' - def _isabs(path): - return path.startswith('c:\\') - os.path.isabs = _isabs - def _splitdrive(path): - if path.startswith('c:'): - return ('', path.replace('c:', '')) - return ('', path) - os.path.splitdrive = _splitdrive - def _join(*path): - return '\\'.join(path) - os.path.join = _join - - self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'), - 'c:\\root\\old\\its\\here') - self.assertEqual(change_root('c:\\root', 'its\\here'), - 'c:\\root\\its\\here') - - # BugsBunny os (it's a great os) - os.name = 'BugsBunny' - self.assertRaises(DistutilsPlatformError, - change_root, 'c:\\root', 'its\\here') - - # XXX platforms to be covered: mac - - def test_check_environ(self): - util._environ_checked = 0 - os.environ.pop('HOME', None) - - check_environ() - - self.assertEqual(os.environ['PLAT'], get_platform()) - self.assertEqual(util._environ_checked, 1) - - @unittest.skipUnless(os.name == 'posix', 'specific to posix') - def test_check_environ_getpwuid(self): - util._environ_checked = 0 - os.environ.pop('HOME', None) - - import pwd - - # only set pw_dir field, other fields are not used - result = pwd.struct_passwd((None, None, None, None, None, - '/home/distutils', None)) - with mock.patch.object(pwd, 'getpwuid', return_value=result): - check_environ() - self.assertEqual(os.environ['HOME'], '/home/distutils') - - util._environ_checked = 0 - os.environ.pop('HOME', None) - - # bpo-10496: Catch pwd.getpwuid() error - with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError): - check_environ() - self.assertNotIn('HOME', os.environ) - - def test_split_quoted(self): - self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), - ['one', 'two', 'three', 'four']) - - def test_strtobool(self): - yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') - no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') - - for y in yes: - self.assertTrue(strtobool(y)) - - for n in no: - self.assertFalse(strtobool(n)) - - def test_rfc822_escape(self): - header = 'I am a\npoor\nlonesome\nheader\n' - res = rfc822_escape(header) - wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' - 'header%(8s)s') % {'8s': '\n'+8*' '} - self.assertEqual(res, wanted) - - def test_dont_write_bytecode(self): - # makes sure byte_compile raise a DistutilsError - # if sys.dont_write_bytecode is True - old_dont_write_bytecode = sys.dont_write_bytecode - sys.dont_write_bytecode = True - try: - self.assertRaises(DistutilsByteCompileError, byte_compile, []) - finally: - sys.dont_write_bytecode = old_dont_write_bytecode - - def test_grok_environment_error(self): - # test obsolete function to ensure backward compat (#4931) - exc = IOError("Unable to find batch file") - msg = grok_environment_error(exc) - self.assertEqual(msg, "error: Unable to find batch file") - - -def test_suite(): - return unittest.makeSuite(UtilTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_version.py b/tests/test_version.py deleted file mode 100644 index 8671cd2f..00000000 --- a/tests/test_version.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Tests for distutils.version.""" -import unittest -from distutils.version import LooseVersion -from distutils.version import StrictVersion -from test.support import run_unittest - -class VersionTestCase(unittest.TestCase): - - def test_prerelease(self): - version = StrictVersion('1.2.3a1') - self.assertEqual(version.version, (1, 2, 3)) - self.assertEqual(version.prerelease, ('a', 1)) - self.assertEqual(str(version), '1.2.3a1') - - version = StrictVersion('1.2.0') - self.assertEqual(str(version), '1.2') - - def test_cmp_strict(self): - versions = (('1.5.1', '1.5.2b2', -1), - ('161', '3.10a', ValueError), - ('8.02', '8.02', 0), - ('3.4j', '1996.07.12', ValueError), - ('3.2.pl0', '3.1.1.6', ValueError), - ('2g6', '11g', ValueError), - ('0.9', '2.2', -1), - ('1.2.1', '1.2', 1), - ('1.1', '1.2.2', -1), - ('1.2', '1.1', 1), - ('1.2.1', '1.2.2', -1), - ('1.2.2', '1.2', 1), - ('1.2', '1.2.2', -1), - ('0.4.0', '0.4', 0), - ('1.13++', '5.5.kw', ValueError)) - - for v1, v2, wanted in versions: - try: - res = StrictVersion(v1)._cmp(StrictVersion(v2)) - except ValueError: - if wanted is ValueError: - continue - else: - raise AssertionError(("cmp(%s, %s) " - "shouldn't raise ValueError") - % (v1, v2)) - self.assertEqual(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) - res = StrictVersion(v1)._cmp(v2) - self.assertEqual(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) - res = StrictVersion(v1)._cmp(object()) - self.assertIs(res, NotImplemented, - 'cmp(%s, %s) should be NotImplemented, got %s' % - (v1, v2, res)) - - - def test_cmp(self): - versions = (('1.5.1', '1.5.2b2', -1), - ('161', '3.10a', 1), - ('8.02', '8.02', 0), - ('3.4j', '1996.07.12', -1), - ('3.2.pl0', '3.1.1.6', 1), - ('2g6', '11g', -1), - ('0.960923', '2.2beta29', -1), - ('1.13++', '5.5.kw', -1)) - - - for v1, v2, wanted in versions: - res = LooseVersion(v1)._cmp(LooseVersion(v2)) - self.assertEqual(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) - res = LooseVersion(v1)._cmp(v2) - self.assertEqual(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) - res = LooseVersion(v1)._cmp(object()) - self.assertIs(res, NotImplemented, - 'cmp(%s, %s) should be NotImplemented, got %s' % - (v1, v2, res)) - -def test_suite(): - return unittest.makeSuite(VersionTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/tests/test_versionpredicate.py b/tests/test_versionpredicate.py deleted file mode 100644 index 28ae09dc..00000000 --- a/tests/test_versionpredicate.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Tests harness for distutils.versionpredicate. - -""" - -import distutils.versionpredicate -import doctest -from test.support import run_unittest - -def test_suite(): - return doctest.DocTestSuite(distutils.versionpredicate) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/text_file.py b/text_file.py deleted file mode 100644 index 93abad38..00000000 --- a/text_file.py +++ /dev/null @@ -1,286 +0,0 @@ -"""text_file - -provides the TextFile class, which gives an interface to text files -that (optionally) takes care of stripping comments, ignoring blank -lines, and joining lines with backslashes.""" - -import sys, io - - -class TextFile: - """Provides a file-like object that takes care of all the things you - commonly want to do when processing a text file that has some - line-by-line syntax: strip comments (as long as "#" is your - comment character), skip blank lines, join adjacent lines by - escaping the newline (ie. backslash at end of line), strip - leading and/or trailing whitespace. All of these are optional - and independently controllable. - - Provides a 'warn()' method so you can generate warning messages that - report physical line number, even if the logical line in question - spans multiple physical lines. Also provides 'unreadline()' for - implementing line-at-a-time lookahead. - - Constructor is called as: - - TextFile (filename=None, file=None, **options) - - It bombs (RuntimeError) if both 'filename' and 'file' are None; - 'filename' should be a string, and 'file' a file object (or - something that provides 'readline()' and 'close()' methods). It is - recommended that you supply at least 'filename', so that TextFile - can include it in warning messages. If 'file' is not supplied, - TextFile creates its own using 'io.open()'. - - The options are all boolean, and affect the value returned by - 'readline()': - strip_comments [default: true] - strip from "#" to end-of-line, as well as any whitespace - leading up to the "#" -- unless it is escaped by a backslash - lstrip_ws [default: false] - strip leading whitespace from each line before returning it - rstrip_ws [default: true] - strip trailing whitespace (including line terminator!) from - each line before returning it - skip_blanks [default: true} - skip lines that are empty *after* stripping comments and - whitespace. (If both lstrip_ws and rstrip_ws are false, - then some lines may consist of solely whitespace: these will - *not* be skipped, even if 'skip_blanks' is true.) - join_lines [default: false] - if a backslash is the last non-newline character on a line - after stripping comments and whitespace, join the following line - to it to form one "logical line"; if N consecutive lines end - with a backslash, then N+1 physical lines will be joined to - form one logical line. - collapse_join [default: false] - strip leading whitespace from lines that are joined to their - predecessor; only matters if (join_lines and not lstrip_ws) - errors [default: 'strict'] - error handler used to decode the file content - - Note that since 'rstrip_ws' can strip the trailing newline, the - semantics of 'readline()' must differ from those of the builtin file - object's 'readline()' method! In particular, 'readline()' returns - None for end-of-file: an empty string might just be a blank line (or - an all-whitespace line), if 'rstrip_ws' is true but 'skip_blanks' is - not.""" - - default_options = { 'strip_comments': 1, - 'skip_blanks': 1, - 'lstrip_ws': 0, - 'rstrip_ws': 1, - 'join_lines': 0, - 'collapse_join': 0, - 'errors': 'strict', - } - - def __init__(self, filename=None, file=None, **options): - """Construct a new TextFile object. At least one of 'filename' - (a string) and 'file' (a file-like object) must be supplied. - They keyword argument options are described above and affect - the values returned by 'readline()'.""" - if filename is None and file is None: - raise RuntimeError("you must supply either or both of 'filename' and 'file'") - - # set values for all options -- either from client option hash - # or fallback to default_options - for opt in self.default_options.keys(): - if opt in options: - setattr(self, opt, options[opt]) - else: - setattr(self, opt, self.default_options[opt]) - - # sanity check client option hash - for opt in options.keys(): - if opt not in self.default_options: - raise KeyError("invalid TextFile option '%s'" % opt) - - if file is None: - self.open(filename) - else: - self.filename = filename - self.file = file - self.current_line = 0 # assuming that file is at BOF! - - # 'linebuf' is a stack of lines that will be emptied before we - # actually read from the file; it's only populated by an - # 'unreadline()' operation - self.linebuf = [] - - def open(self, filename): - """Open a new file named 'filename'. This overrides both the - 'filename' and 'file' arguments to the constructor.""" - self.filename = filename - self.file = io.open(self.filename, 'r', errors=self.errors) - self.current_line = 0 - - def close(self): - """Close the current file and forget everything we know about it - (filename, current line number).""" - file = self.file - self.file = None - self.filename = None - self.current_line = None - file.close() - - def gen_error(self, msg, line=None): - outmsg = [] - if line is None: - line = self.current_line - outmsg.append(self.filename + ", ") - if isinstance(line, (list, tuple)): - outmsg.append("lines %d-%d: " % tuple(line)) - else: - outmsg.append("line %d: " % line) - outmsg.append(str(msg)) - return "".join(outmsg) - - def error(self, msg, line=None): - raise ValueError("error: " + self.gen_error(msg, line)) - - def warn(self, msg, line=None): - """Print (to stderr) a warning message tied to the current logical - line in the current file. If the current logical line in the - file spans multiple physical lines, the warning refers to the - whole range, eg. "lines 3-5". If 'line' supplied, it overrides - the current line number; it may be a list or tuple to indicate a - range of physical lines, or an integer for a single physical - line.""" - sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n") - - def readline(self): - """Read and return a single logical line from the current file (or - from an internal buffer if lines have previously been "unread" - with 'unreadline()'). If the 'join_lines' option is true, this - may involve reading multiple physical lines concatenated into a - single string. Updates the current line number, so calling - 'warn()' after 'readline()' emits a warning about the physical - line(s) just read. Returns None on end-of-file, since the empty - string can occur if 'rstrip_ws' is true but 'strip_blanks' is - not.""" - # If any "unread" lines waiting in 'linebuf', return the top - # one. (We don't actually buffer read-ahead data -- lines only - # get put in 'linebuf' if the client explicitly does an - # 'unreadline()'. - if self.linebuf: - line = self.linebuf[-1] - del self.linebuf[-1] - return line - - buildup_line = '' - - while True: - # read the line, make it None if EOF - line = self.file.readline() - if line == '': - line = None - - if self.strip_comments and line: - - # Look for the first "#" in the line. If none, never - # mind. If we find one and it's the first character, or - # is not preceded by "\", then it starts a comment -- - # strip the comment, strip whitespace before it, and - # carry on. Otherwise, it's just an escaped "#", so - # unescape it (and any other escaped "#"'s that might be - # lurking in there) and otherwise leave the line alone. - - pos = line.find("#") - if pos == -1: # no "#" -- no comments - pass - - # It's definitely a comment -- either "#" is the first - # character, or it's elsewhere and unescaped. - elif pos == 0 or line[pos-1] != "\\": - # Have to preserve the trailing newline, because it's - # the job of a later step (rstrip_ws) to remove it -- - # and if rstrip_ws is false, we'd better preserve it! - # (NB. this means that if the final line is all comment - # and has no trailing newline, we will think that it's - # EOF; I think that's OK.) - eol = (line[-1] == '\n') and '\n' or '' - line = line[0:pos] + eol - - # If all that's left is whitespace, then skip line - # *now*, before we try to join it to 'buildup_line' -- - # that way constructs like - # hello \\ - # # comment that should be ignored - # there - # result in "hello there". - if line.strip() == "": - continue - else: # it's an escaped "#" - line = line.replace("\\#", "#") - - # did previous line end with a backslash? then accumulate - if self.join_lines and buildup_line: - # oops: end of file - if line is None: - self.warn("continuation line immediately precedes " - "end-of-file") - return buildup_line - - if self.collapse_join: - line = line.lstrip() - line = buildup_line + line - - # careful: pay attention to line number when incrementing it - if isinstance(self.current_line, list): - self.current_line[1] = self.current_line[1] + 1 - else: - self.current_line = [self.current_line, - self.current_line + 1] - # just an ordinary line, read it as usual - else: - if line is None: # eof - return None - - # still have to be careful about incrementing the line number! - if isinstance(self.current_line, list): - self.current_line = self.current_line[1] + 1 - else: - self.current_line = self.current_line + 1 - - # strip whitespace however the client wants (leading and - # trailing, or one or the other, or neither) - if self.lstrip_ws and self.rstrip_ws: - line = line.strip() - elif self.lstrip_ws: - line = line.lstrip() - elif self.rstrip_ws: - line = line.rstrip() - - # blank line (whether we rstrip'ed or not)? skip to next line - # if appropriate - if (line == '' or line == '\n') and self.skip_blanks: - continue - - if self.join_lines: - if line[-1] == '\\': - buildup_line = line[:-1] - continue - - if line[-2:] == '\\\n': - buildup_line = line[0:-2] + '\n' - continue - - # well, I guess there's some actual content there: return it - return line - - def readlines(self): - """Read and return the list of all logical lines remaining in the - current file.""" - lines = [] - while True: - line = self.readline() - if line is None: - return lines - lines.append(line) - - def unreadline(self, line): - """Push 'line' (a string) onto an internal buffer that will be - checked by future 'readline()' calls. Handy for implementing - a parser with line-at-a-time lookahead.""" - self.linebuf.append(line) diff --git a/unixccompiler.py b/unixccompiler.py deleted file mode 100644 index 4d7a6de7..00000000 --- a/unixccompiler.py +++ /dev/null @@ -1,328 +0,0 @@ -"""distutils.unixccompiler - -Contains the UnixCCompiler class, a subclass of CCompiler that handles -the "typical" Unix-style command-line C compiler: - * macros defined with -Dname[=value] - * macros undefined with -Uname - * include search directories specified with -Idir - * libraries specified with -lllib - * library search directories specified with -Ldir - * compile handled by 'cc' (or similar) executable with -c option: - compiles .c to .o - * link static library handled by 'ar' command (possibly with 'ranlib') - * link shared library handled by 'cc -shared' -""" - -import os, sys, re - -from distutils import sysconfig -from distutils.dep_util import newer -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options -from distutils.errors import \ - DistutilsExecError, CompileError, LibError, LinkError -from distutils import log - -if sys.platform == 'darwin': - import _osx_support - -# XXX Things not currently handled: -# * optimization/debug/warning flags; we just use whatever's in Python's -# Makefile and live with it. Is this adequate? If not, we might -# have to have a bunch of subclasses GNUCCompiler, SGICCompiler, -# SunCCompiler, and I suspect down that road lies madness. -# * even if we don't know a warning flag from an optimization flag, -# we need some way for outsiders to feed preprocessor/compiler/linker -# flags in to us -- eg. a sysadmin might want to mandate certain flags -# via a site config file, or a user might want to set something for -# compiling this module distribution only via the setup.py command -# line, whatever. As long as these options come from something on the -# current system, they can be as system-dependent as they like, and we -# should just happily stuff them into the preprocessor/compiler/linker -# options and carry on. - - -class UnixCCompiler(CCompiler): - - compiler_type = 'unix' - - # These are used by CCompiler in two places: the constructor sets - # instance attributes 'preprocessor', 'compiler', etc. from them, and - # 'set_executable()' allows any of these to be set. The defaults here - # are pretty generic; they will probably have to be set by an outsider - # (eg. using information discovered by the sysconfig about building - # Python extensions). - executables = {'preprocessor' : None, - 'compiler' : ["cc"], - 'compiler_so' : ["cc"], - 'compiler_cxx' : ["cc"], - 'linker_so' : ["cc", "-shared"], - 'linker_exe' : ["cc"], - 'archiver' : ["ar", "-cr"], - 'ranlib' : None, - } - - if sys.platform[:6] == "darwin": - executables['ranlib'] = ["ranlib"] - - # Needed for the filename generation methods provided by the base - # class, CCompiler. NB. whoever instantiates/uses a particular - # UnixCCompiler instance should set 'shared_lib_ext' -- we set a - # reasonable common default here, but it's not necessarily used on all - # Unices! - - src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"] - obj_extension = ".o" - static_lib_extension = ".a" - shared_lib_extension = ".so" - dylib_lib_extension = ".dylib" - xcode_stub_lib_extension = ".tbd" - static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" - xcode_stub_lib_format = dylib_lib_format - if sys.platform == "cygwin": - exe_extension = ".exe" - - def preprocess(self, source, output_file=None, macros=None, - include_dirs=None, extra_preargs=None, extra_postargs=None): - fixed_args = self._fix_compile_args(None, macros, include_dirs) - ignore, macros, include_dirs = fixed_args - pp_opts = gen_preprocess_options(macros, include_dirs) - pp_args = self.preprocessor + pp_opts - if output_file: - pp_args.extend(['-o', output_file]) - if extra_preargs: - pp_args[:0] = extra_preargs - if extra_postargs: - pp_args.extend(extra_postargs) - pp_args.append(source) - - # We need to preprocess: either we're being forced to, or we're - # generating output to stdout, or there's a target output file and - # the source file is newer than the target (or the target doesn't - # exist). - if self.force or output_file is None or newer(source, output_file): - if output_file: - self.mkpath(os.path.dirname(output_file)) - try: - self.spawn(pp_args) - except DistutilsExecError as msg: - raise CompileError(msg) - - def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): - compiler_so = self.compiler_so - if sys.platform == 'darwin': - compiler_so = _osx_support.compiler_fixup(compiler_so, - cc_args + extra_postargs) - try: - self.spawn(compiler_so + cc_args + [src, '-o', obj] + - extra_postargs) - except DistutilsExecError as msg: - raise CompileError(msg) - - def create_static_lib(self, objects, output_libname, - output_dir=None, debug=0, target_lang=None): - objects, output_dir = self._fix_object_args(objects, output_dir) - - output_filename = \ - self.library_filename(output_libname, output_dir=output_dir) - - if self._need_link(objects, output_filename): - self.mkpath(os.path.dirname(output_filename)) - self.spawn(self.archiver + - [output_filename] + - objects + self.objects) - - # Not many Unices required ranlib anymore -- SunOS 4.x is, I - # think the only major Unix that does. Maybe we need some - # platform intelligence here to skip ranlib if it's not - # needed -- or maybe Python's configure script took care of - # it for us, hence the check for leading colon. - if self.ranlib: - try: - self.spawn(self.ranlib + [output_filename]) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - def link(self, target_desc, objects, - output_filename, output_dir=None, libraries=None, - library_dirs=None, runtime_library_dirs=None, - export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None, build_temp=None, target_lang=None): - objects, output_dir = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, - runtime_library_dirs) - libraries, library_dirs, runtime_library_dirs = fixed_args - - lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, - libraries) - if not isinstance(output_dir, (str, type(None))): - raise TypeError("'output_dir' must be a string or None") - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - ld_args = (objects + self.objects + - lib_opts + ['-o', output_filename]) - if debug: - ld_args[:0] = ['-g'] - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - self.mkpath(os.path.dirname(output_filename)) - try: - if target_desc == CCompiler.EXECUTABLE: - linker = self.linker_exe[:] - else: - linker = self.linker_so[:] - if target_lang == "c++" and self.compiler_cxx: - # skip over environment variable settings if /usr/bin/env - # is used to set up the linker's environment. - # This is needed on OSX. Note: this assumes that the - # normal and C++ compiler have the same environment - # settings. - i = 0 - if os.path.basename(linker[0]) == "env": - i = 1 - while '=' in linker[i]: - i += 1 - - if os.path.basename(linker[i]) == 'ld_so_aix': - # AIX platforms prefix the compiler with the ld_so_aix - # script, so we need to adjust our linker index - offset = 1 - else: - offset = 0 - - linker[i+offset] = self.compiler_cxx[i] - - if sys.platform == 'darwin': - linker = _osx_support.compiler_fixup(linker, ld_args) - - self.spawn(linker + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "-L" + dir - - def _is_gcc(self, compiler_name): - return "gcc" in compiler_name or "g++" in compiler_name - - def runtime_library_dir_option(self, dir): - # XXX Hackish, at the very least. See Python bug #445902: - # http://sourceforge.net/tracker/index.php - # ?func=detail&aid=445902&group_id=5470&atid=105470 - # Linkers on different platforms need different options to - # specify that directories need to be added to the list of - # directories searched for dependencies when a dynamic library - # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to - # be told to pass the -R option through to the linker, whereas - # other compilers and gcc on other systems just know this. - # Other compilers may need something slightly different. At - # this time, there's no way to determine this information from - # the configuration data stored in the Python installation, so - # we use this hack. - compiler = os.path.basename(sysconfig.get_config_var("CC")) - if sys.platform[:6] == "darwin": - # MacOSX's linker doesn't understand the -R flag at all - return "-L" + dir - elif sys.platform[:7] == "freebsd": - return "-Wl,-rpath=" + dir - elif sys.platform[:5] == "hp-ux": - if self._is_gcc(compiler): - return ["-Wl,+s", "-L" + dir] - return ["+s", "-L" + dir] - else: - if self._is_gcc(compiler): - # gcc on non-GNU systems does not need -Wl, but can - # use it anyway. Since distutils has always passed in - # -Wl whenever gcc was used in the past it is probably - # safest to keep doing so. - if sysconfig.get_config_var("GNULD") == "yes": - # GNU ld needs an extra option to get a RUNPATH - # instead of just an RPATH. - return "-Wl,--enable-new-dtags,-R" + dir - else: - return "-Wl,-R" + dir - else: - # No idea how --enable-new-dtags would be passed on to - # ld if this system was using GNU ld. Don't know if a - # system like this even exists. - return "-R" + dir - - def library_option(self, lib): - return "-l" + lib - - def find_library_file(self, dirs, lib, debug=0): - shared_f = self.library_filename(lib, lib_type='shared') - dylib_f = self.library_filename(lib, lib_type='dylib') - xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub') - static_f = self.library_filename(lib, lib_type='static') - - if sys.platform == 'darwin': - # On OSX users can specify an alternate SDK using - # '-isysroot', calculate the SDK root if it is specified - # (and use it further on) - # - # Note that, as of Xcode 7, Apple SDKs may contain textual stub - # libraries with .tbd extensions rather than the normal .dylib - # shared libraries installed in /. The Apple compiler tool - # chain handles this transparently but it can cause problems - # for programs that are being built with an SDK and searching - # for specific libraries. Callers of find_library_file need to - # keep in mind that the base filename of the returned SDK library - # file might have a different extension from that of the library - # file installed on the running system, for example: - # /Applications/Xcode.app/Contents/Developer/Platforms/ - # MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/ - # usr/lib/libedit.tbd - # vs - # /usr/lib/libedit.dylib - cflags = sysconfig.get_config_var('CFLAGS') - m = re.search(r'-isysroot\s*(\S+)', cflags) - if m is None: - sysroot = '/' - else: - sysroot = m.group(1) - - - - for dir in dirs: - shared = os.path.join(dir, shared_f) - dylib = os.path.join(dir, dylib_f) - static = os.path.join(dir, static_f) - xcode_stub = os.path.join(dir, xcode_stub_f) - - if sys.platform == 'darwin' and ( - dir.startswith('/System/') or ( - dir.startswith('/usr/') and not dir.startswith('/usr/local/'))): - - shared = os.path.join(sysroot, dir[1:], shared_f) - dylib = os.path.join(sysroot, dir[1:], dylib_f) - static = os.path.join(sysroot, dir[1:], static_f) - xcode_stub = os.path.join(sysroot, dir[1:], xcode_stub_f) - - # We're second-guessing the linker here, with not much hard - # data to go on: GCC seems to prefer the shared library, so I'm - # assuming that *all* Unix C compilers do. And of course I'm - # ignoring even GCC's "-static" option. So sue me. - if os.path.exists(dylib): - return dylib - elif os.path.exists(xcode_stub): - return xcode_stub - elif os.path.exists(shared): - return shared - elif os.path.exists(static): - return static - - # Oops, didn't find it in *any* of 'dirs' - return None diff --git a/util.py b/util.py deleted file mode 100644 index 4b002ece..00000000 --- a/util.py +++ /dev/null @@ -1,559 +0,0 @@ -"""distutils.util - -Miscellaneous utility functions -- anything that doesn't fit into -one of the other *util.py modules. -""" - -import os -import re -import importlib.util -import string -import sys -from distutils.errors import DistutilsPlatformError -from distutils.dep_util import newer -from distutils.spawn import spawn -from distutils import log -from distutils.errors import DistutilsByteCompileError - -def get_host_platform(): - """Return a string that identifies the current platform. This is used mainly to - distinguish platform-specific build directories and platform-specific built - distributions. Typically includes the OS name and version and the - architecture (as supplied by 'os.uname()'), although the exact information - included depends on the OS; eg. on Linux, the kernel version isn't - particularly important. - - Examples of returned values: - linux-i586 - linux-alpha (?) - solaris-2.6-sun4u - - Windows will return one of: - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) - win32 (all others - specifically, sys.platform is returned) - - For other non-POSIX platforms, currently just returns 'sys.platform'. - - """ - if os.name == 'nt': - if 'amd64' in sys.version.lower(): - return 'win-amd64' - if '(arm)' in sys.version.lower(): - return 'win-arm32' - if '(arm64)' in sys.version.lower(): - return 'win-arm64' - return sys.platform - - # Set for cross builds explicitly - if "_PYTHON_HOST_PLATFORM" in os.environ: - return os.environ["_PYTHON_HOST_PLATFORM"] - - if os.name != "posix" or not hasattr(os, 'uname'): - # XXX what about the architecture? NT is Intel or Alpha, - # Mac OS is M68k or PPC, etc. - return sys.platform - - # Try to distinguish various flavours of Unix - - (osname, host, release, version, machine) = os.uname() - - # Convert the OS name to lowercase, remove '/' characters, and translate - # spaces (for "Power Macintosh") - osname = osname.lower().replace('/', '') - machine = machine.replace(' ', '_') - machine = machine.replace('/', '-') - - if osname[:5] == "linux": - # At least on Linux/Intel, 'machine' is the processor -- - # i386, etc. - # XXX what about Alpha, SPARC, etc? - return "%s-%s" % (osname, machine) - elif osname[:5] == "sunos": - if release[0] >= "5": # SunOS 5 == Solaris 2 - osname = "solaris" - release = "%d.%s" % (int(release[0]) - 3, release[2:]) - # We can't use "platform.architecture()[0]" because a - # bootstrap problem. We use a dict to get an error - # if some suspicious happens. - bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} - machine += ".%s" % bitness[sys.maxsize] - # fall through to standard osname-release-machine representation - elif osname[:3] == "aix": - from _aix_support import aix_platform - return aix_platform() - elif osname[:6] == "cygwin": - osname = "cygwin" - rel_re = re.compile (r'[\d.]+', re.ASCII) - m = rel_re.match(release) - if m: - release = m.group() - elif osname[:6] == "darwin": - import _osx_support, distutils.sysconfig - osname, release, machine = _osx_support.get_platform_osx( - distutils.sysconfig.get_config_vars(), - osname, release, machine) - - return "%s-%s-%s" % (osname, release, machine) - -def get_platform(): - if os.name == 'nt': - TARGET_TO_PLAT = { - 'x86' : 'win32', - 'x64' : 'win-amd64', - 'arm' : 'win-arm32', - } - return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform() - else: - return get_host_platform() - -def convert_path (pathname): - """Return 'pathname' as a name that will work on the native filesystem, - i.e. split it on '/' and put it back together again using the current - directory separator. Needed because filenames in the setup script are - always supplied in Unix style, and have to be converted to the local - convention before we can actually use them in the filesystem. Raises - ValueError on non-Unix-ish systems if 'pathname' either starts or - ends with a slash. - """ - if os.sep == '/': - return pathname - if not pathname: - return pathname - if pathname[0] == '/': - raise ValueError("path '%s' cannot be absolute" % pathname) - if pathname[-1] == '/': - raise ValueError("path '%s' cannot end with '/'" % pathname) - - paths = pathname.split('/') - while '.' in paths: - paths.remove('.') - if not paths: - return os.curdir - return os.path.join(*paths) - -# convert_path () - - -def change_root (new_root, pathname): - """Return 'pathname' with 'new_root' prepended. If 'pathname' is - relative, this is equivalent to "os.path.join(new_root,pathname)". - Otherwise, it requires making 'pathname' relative and then joining the - two, which is tricky on DOS/Windows and Mac OS. - """ - if os.name == 'posix': - if not os.path.isabs(pathname): - return os.path.join(new_root, pathname) - else: - return os.path.join(new_root, pathname[1:]) - - elif os.name == 'nt': - (drive, path) = os.path.splitdrive(pathname) - if path[0] == '\\': - path = path[1:] - return os.path.join(new_root, path) - - else: - raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) - - -_environ_checked = 0 -def check_environ (): - """Ensure that 'os.environ' has all the environment variables we - guarantee that users can use in config files, command-line options, - etc. Currently this includes: - HOME - user's home directory (Unix only) - PLAT - description of the current platform, including hardware - and OS (see 'get_platform()') - """ - global _environ_checked - if _environ_checked: - return - - if os.name == 'posix' and 'HOME' not in os.environ: - try: - import pwd - os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] - except (ImportError, KeyError): - # bpo-10496: if the current user identifier doesn't exist in the - # password database, do nothing - pass - - if 'PLAT' not in os.environ: - os.environ['PLAT'] = get_platform() - - _environ_checked = 1 - - -def subst_vars (s, local_vars): - """Perform shell/Perl-style variable substitution on 'string'. Every - occurrence of '$' followed by a name is considered a variable, and - variable is substituted by the value found in the 'local_vars' - dictionary, or in 'os.environ' if it's not in 'local_vars'. - 'os.environ' is first checked/augmented to guarantee that it contains - certain values: see 'check_environ()'. Raise ValueError for any - variables not found in either 'local_vars' or 'os.environ'. - """ - check_environ() - def _subst (match, local_vars=local_vars): - var_name = match.group(1) - if var_name in local_vars: - return str(local_vars[var_name]) - else: - return os.environ[var_name] - - try: - return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) - except KeyError as var: - raise ValueError("invalid variable '$%s'" % var) - -# subst_vars () - - -def grok_environment_error (exc, prefix="error: "): - # Function kept for backward compatibility. - # Used to try clever things with EnvironmentErrors, - # but nowadays str(exception) produces good messages. - return prefix + str(exc) - - -# Needed by 'split_quoted()' -_wordchars_re = _squote_re = _dquote_re = None -def _init_regex(): - global _wordchars_re, _squote_re, _dquote_re - _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) - _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") - _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') - -def split_quoted (s): - """Split a string up according to Unix shell-like rules for quotes and - backslashes. In short: words are delimited by spaces, as long as those - spaces are not escaped by a backslash, or inside a quoted string. - Single and double quotes are equivalent, and the quote characters can - be backslash-escaped. The backslash is stripped from any two-character - escape sequence, leaving only the escaped character. The quote - characters are stripped from any quoted string. Returns a list of - words. - """ - - # This is a nice algorithm for splitting up a single string, since it - # doesn't require character-by-character examination. It was a little - # bit of a brain-bender to get it working right, though... - if _wordchars_re is None: _init_regex() - - s = s.strip() - words = [] - pos = 0 - - while s: - m = _wordchars_re.match(s, pos) - end = m.end() - if end == len(s): - words.append(s[:end]) - break - - if s[end] in string.whitespace: # unescaped, unquoted whitespace: now - words.append(s[:end]) # we definitely have a word delimiter - s = s[end:].lstrip() - pos = 0 - - elif s[end] == '\\': # preserve whatever is being escaped; - # will become part of the current word - s = s[:end] + s[end+1:] - pos = end+1 - - else: - if s[end] == "'": # slurp singly-quoted string - m = _squote_re.match(s, end) - elif s[end] == '"': # slurp doubly-quoted string - m = _dquote_re.match(s, end) - else: - raise RuntimeError("this can't happen (bad char '%c')" % s[end]) - - if m is None: - raise ValueError("bad string (mismatched %s quotes?)" % s[end]) - - (beg, end) = m.span() - s = s[:beg] + s[beg+1:end-1] + s[end:] - pos = m.end() - 2 - - if pos >= len(s): - words.append(s) - break - - return words - -# split_quoted () - - -def execute (func, args, msg=None, verbose=0, dry_run=0): - """Perform some action that affects the outside world (eg. by - writing to the filesystem). Such actions are special because they - are disabled by the 'dry_run' flag. This method takes care of all - that bureaucracy for you; all you have to do is supply the - function to call and an argument tuple for it (to embody the - "external action" being performed), and an optional message to - print. - """ - if msg is None: - msg = "%s%r" % (func.__name__, args) - if msg[-2:] == ',)': # correct for singleton tuple - msg = msg[0:-2] + ')' - - log.info(msg) - if not dry_run: - func(*args) - - -def strtobool (val): - """Convert a string representation of truth to true (1) or false (0). - - True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values - are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if - 'val' is anything else. - """ - val = val.lower() - if val in ('y', 'yes', 't', 'true', 'on', '1'): - return 1 - elif val in ('n', 'no', 'f', 'false', 'off', '0'): - return 0 - else: - raise ValueError("invalid truth value %r" % (val,)) - - -def byte_compile (py_files, - optimize=0, force=0, - prefix=None, base_dir=None, - verbose=1, dry_run=0, - direct=None): - """Byte-compile a collection of Python source files to .pyc - files in a __pycache__ subdirectory. 'py_files' is a list - of files to compile; any files that don't end in ".py" are silently - skipped. 'optimize' must be one of the following: - 0 - don't optimize - 1 - normal optimization (like "python -O") - 2 - extra optimization (like "python -OO") - If 'force' is true, all files are recompiled regardless of - timestamps. - - The source filename encoded in each bytecode file defaults to the - filenames listed in 'py_files'; you can modify these with 'prefix' and - 'basedir'. 'prefix' is a string that will be stripped off of each - source filename, and 'base_dir' is a directory name that will be - prepended (after 'prefix' is stripped). You can supply either or both - (or neither) of 'prefix' and 'base_dir', as you wish. - - If 'dry_run' is true, doesn't actually do anything that would - affect the filesystem. - - Byte-compilation is either done directly in this interpreter process - with the standard py_compile module, or indirectly by writing a - temporary script and executing it. Normally, you should let - 'byte_compile()' figure out to use direct compilation or not (see - the source for details). The 'direct' flag is used by the script - generated in indirect mode; unless you know what you're doing, leave - it set to None. - """ - - # Late import to fix a bootstrap issue: _posixsubprocess is built by - # setup.py, but setup.py uses distutils. - import subprocess - - # nothing is done if sys.dont_write_bytecode is True - if sys.dont_write_bytecode: - raise DistutilsByteCompileError('byte-compiling is disabled.') - - # First, if the caller didn't force us into direct or indirect mode, - # figure out which mode we should be in. We take a conservative - # approach: choose direct mode *only* if the current interpreter is - # in debug mode and optimize is 0. If we're not in debug mode (-O - # or -OO), we don't know which level of optimization this - # interpreter is running with, so we can't do direct - # byte-compilation and be certain that it's the right thing. Thus, - # always compile indirectly if the current interpreter is in either - # optimize mode, or if either optimization level was requested by - # the caller. - if direct is None: - direct = (__debug__ and optimize == 0) - - # "Indirect" byte-compilation: write a temporary script and then - # run it with the appropriate flags. - if not direct: - try: - from tempfile import mkstemp - (script_fd, script_name) = mkstemp(".py") - except ImportError: - from tempfile import mktemp - (script_fd, script_name) = None, mktemp(".py") - log.info("writing byte-compilation script '%s'", script_name) - if not dry_run: - if script_fd is not None: - script = os.fdopen(script_fd, "w") - else: - script = open(script_name, "w") - - with script: - script.write("""\ -from distutils.util import byte_compile -files = [ -""") - - # XXX would be nice to write absolute filenames, just for - # safety's sake (script should be more robust in the face of - # chdir'ing before running it). But this requires abspath'ing - # 'prefix' as well, and that breaks the hack in build_lib's - # 'byte_compile()' method that carefully tacks on a trailing - # slash (os.sep really) to make sure the prefix here is "just - # right". This whole prefix business is rather delicate -- the - # problem is that it's really a directory, but I'm treating it - # as a dumb string, so trailing slashes and so forth matter. - - #py_files = map(os.path.abspath, py_files) - #if prefix: - # prefix = os.path.abspath(prefix) - - script.write(",\n".join(map(repr, py_files)) + "]\n") - script.write(""" -byte_compile(files, optimize=%r, force=%r, - prefix=%r, base_dir=%r, - verbose=%r, dry_run=0, - direct=1) -""" % (optimize, force, prefix, base_dir, verbose)) - - cmd = [sys.executable] - cmd.extend(subprocess._optim_args_from_interpreter_flags()) - cmd.append(script_name) - spawn(cmd, dry_run=dry_run) - execute(os.remove, (script_name,), "removing %s" % script_name, - dry_run=dry_run) - - # "Direct" byte-compilation: use the py_compile module to compile - # right here, right now. Note that the script generated in indirect - # mode simply calls 'byte_compile()' in direct mode, a weird sort of - # cross-process recursion. Hey, it works! - else: - from py_compile import compile - - for file in py_files: - if file[-3:] != ".py": - # This lets us be lazy and not filter filenames in - # the "install_lib" command. - continue - - # Terminology from the py_compile module: - # cfile - byte-compiled file - # dfile - purported source filename (same as 'file' by default) - if optimize >= 0: - opt = '' if optimize == 0 else optimize - cfile = importlib.util.cache_from_source( - file, optimization=opt) - else: - cfile = importlib.util.cache_from_source(file) - dfile = file - if prefix: - if file[:len(prefix)] != prefix: - raise ValueError("invalid prefix: filename %r doesn't start with %r" - % (file, prefix)) - dfile = dfile[len(prefix):] - if base_dir: - dfile = os.path.join(base_dir, dfile) - - cfile_base = os.path.basename(cfile) - if direct: - if force or newer(file, cfile): - log.info("byte-compiling %s to %s", file, cfile_base) - if not dry_run: - compile(file, cfile, dfile) - else: - log.debug("skipping byte-compilation of %s to %s", - file, cfile_base) - -# byte_compile () - -def rfc822_escape (header): - """Return a version of the string escaped for inclusion in an - RFC-822 header, by ensuring there are 8 spaces space after each newline. - """ - lines = header.split('\n') - sep = '\n' + 8 * ' ' - return sep.join(lines) - -# 2to3 support - -def run_2to3(files, fixer_names=None, options=None, explicit=None): - """Invoke 2to3 on a list of Python files. - The files should all come from the build area, as the - modification is done in-place. To reduce the build time, - only files modified since the last invocation of this - function should be passed in the files argument.""" - - if not files: - return - - # Make this class local, to delay import of 2to3 - from lib2to3.refactor import RefactoringTool, get_fixers_from_package - class DistutilsRefactoringTool(RefactoringTool): - def log_error(self, msg, *args, **kw): - log.error(msg, *args) - - def log_message(self, msg, *args): - log.info(msg, *args) - - def log_debug(self, msg, *args): - log.debug(msg, *args) - - if fixer_names is None: - fixer_names = get_fixers_from_package('lib2to3.fixes') - r = DistutilsRefactoringTool(fixer_names, options=options) - r.refactor(files, write=True) - -def copydir_run_2to3(src, dest, template=None, fixer_names=None, - options=None, explicit=None): - """Recursively copy a directory, only copying new and changed files, - running run_2to3 over all newly copied Python modules afterward. - - If you give a template string, it's parsed like a MANIFEST.in. - """ - from distutils.dir_util import mkpath - from distutils.file_util import copy_file - from distutils.filelist import FileList - filelist = FileList() - curdir = os.getcwd() - os.chdir(src) - try: - filelist.findall() - finally: - os.chdir(curdir) - filelist.files[:] = filelist.allfiles - if template: - for line in template.splitlines(): - line = line.strip() - if not line: continue - filelist.process_template_line(line) - copied = [] - for filename in filelist.files: - outname = os.path.join(dest, filename) - mkpath(os.path.dirname(outname)) - res = copy_file(os.path.join(src, filename), outname, update=1) - if res[1]: copied.append(outname) - run_2to3([fn for fn in copied if fn.lower().endswith('.py')], - fixer_names=fixer_names, options=options, explicit=explicit) - return copied - -class Mixin2to3: - '''Mixin class for commands that run 2to3. - To configure 2to3, setup scripts may either change - the class variables, or inherit from individual commands - to override how 2to3 is invoked.''' - - # provide list of fixers to run; - # defaults to all from lib2to3.fixers - fixer_names = None - - # options dictionary - options = None - - # list of fixers to invoke even though they are marked as explicit - explicit = None - - def run_2to3(self, files): - return run_2to3(files, self.fixer_names, self.options, self.explicit) diff --git a/version.py b/version.py deleted file mode 100644 index c33bebae..00000000 --- a/version.py +++ /dev/null @@ -1,347 +0,0 @@ -# -# distutils/version.py -# -# Implements multiple version numbering conventions for the -# Python Module Distribution Utilities. -# -# $Id$ -# - -"""Provides classes to represent module version numbers (one class for -each style of version numbering). There are currently two such classes -implemented: StrictVersion and LooseVersion. - -Every version number class implements the following interface: - * the 'parse' method takes a string and parses it to some internal - representation; if the string is an invalid version number, - 'parse' raises a ValueError exception - * the class constructor takes an optional string argument which, - if supplied, is passed to 'parse' - * __str__ reconstructs the string that was passed to 'parse' (or - an equivalent string -- ie. one that will generate an equivalent - version number instance) - * __repr__ generates Python code to recreate the version number instance - * _cmp compares the current instance with either another instance - of the same class or a string (which will be parsed to an instance - of the same class, thus must follow the same rules) -""" - -import re - -class Version: - """Abstract base class for version numbering classes. Just provides - constructor (__init__) and reproducer (__repr__), because those - seem to be the same for all version numbering classes; and route - rich comparisons to _cmp. - """ - - def __init__ (self, vstring=None): - if vstring: - self.parse(vstring) - - def __repr__ (self): - return "%s ('%s')" % (self.__class__.__name__, str(self)) - - def __eq__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c == 0 - - def __lt__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c < 0 - - def __le__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c <= 0 - - def __gt__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c > 0 - - def __ge__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c >= 0 - - -# Interface for version-number classes -- must be implemented -# by the following classes (the concrete ones -- Version should -# be treated as an abstract class). -# __init__ (string) - create and take same action as 'parse' -# (string parameter is optional) -# parse (string) - convert a string representation to whatever -# internal representation is appropriate for -# this style of version numbering -# __str__ (self) - convert back to a string; should be very similar -# (if not identical to) the string supplied to parse -# __repr__ (self) - generate Python code to recreate -# the instance -# _cmp (self, other) - compare two version numbers ('other' may -# be an unparsed version string, or another -# instance of your version class) - - -class StrictVersion (Version): - - """Version numbering for anal retentives and software idealists. - Implements the standard interface for version number classes as - described above. A version number consists of two or three - dot-separated numeric components, with an optional "pre-release" tag - on the end. The pre-release tag consists of the letter 'a' or 'b' - followed by a number. If the numeric components of two version - numbers are equal, then one with a pre-release tag will always - be deemed earlier (lesser) than one without. - - The following are valid version numbers (shown in the order that - would be obtained by sorting according to the supplied cmp function): - - 0.4 0.4.0 (these two are equivalent) - 0.4.1 - 0.5a1 - 0.5b3 - 0.5 - 0.9.6 - 1.0 - 1.0.4a3 - 1.0.4b1 - 1.0.4 - - The following are examples of invalid version numbers: - - 1 - 2.7.2.2 - 1.3.a4 - 1.3pl1 - 1.3c4 - - The rationale for this version numbering system will be explained - in the distutils documentation. - """ - - version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', - re.VERBOSE | re.ASCII) - - - def parse (self, vstring): - match = self.version_re.match(vstring) - if not match: - raise ValueError("invalid version number '%s'" % vstring) - - (major, minor, patch, prerelease, prerelease_num) = \ - match.group(1, 2, 4, 5, 6) - - if patch: - self.version = tuple(map(int, [major, minor, patch])) - else: - self.version = tuple(map(int, [major, minor])) + (0,) - - if prerelease: - self.prerelease = (prerelease[0], int(prerelease_num)) - else: - self.prerelease = None - - - def __str__ (self): - - if self.version[2] == 0: - vstring = '.'.join(map(str, self.version[0:2])) - else: - vstring = '.'.join(map(str, self.version)) - - if self.prerelease: - vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) - - return vstring - - - def _cmp (self, other): - if isinstance(other, str): - other = StrictVersion(other) - elif not isinstance(other, StrictVersion): - return NotImplemented - - if self.version != other.version: - # numeric versions don't match - # prerelease stuff doesn't matter - if self.version < other.version: - return -1 - else: - return 1 - - # have to compare prerelease - # case 1: neither has prerelease; they're equal - # case 2: self has prerelease, other doesn't; other is greater - # case 3: self doesn't have prerelease, other does: self is greater - # case 4: both have prerelease: must compare them! - - if (not self.prerelease and not other.prerelease): - return 0 - elif (self.prerelease and not other.prerelease): - return -1 - elif (not self.prerelease and other.prerelease): - return 1 - elif (self.prerelease and other.prerelease): - if self.prerelease == other.prerelease: - return 0 - elif self.prerelease < other.prerelease: - return -1 - else: - return 1 - else: - assert False, "never get here" - -# end class StrictVersion - - -# The rules according to Greg Stein: -# 1) a version number has 1 or more numbers separated by a period or by -# sequences of letters. If only periods, then these are compared -# left-to-right to determine an ordering. -# 2) sequences of letters are part of the tuple for comparison and are -# compared lexicographically -# 3) recognize the numeric components may have leading zeroes -# -# The LooseVersion class below implements these rules: a version number -# string is split up into a tuple of integer and string components, and -# comparison is a simple tuple comparison. This means that version -# numbers behave in a predictable and obvious way, but a way that might -# not necessarily be how people *want* version numbers to behave. There -# wouldn't be a problem if people could stick to purely numeric version -# numbers: just split on period and compare the numbers as tuples. -# However, people insist on putting letters into their version numbers; -# the most common purpose seems to be: -# - indicating a "pre-release" version -# ('alpha', 'beta', 'a', 'b', 'pre', 'p') -# - indicating a post-release patch ('p', 'pl', 'patch') -# but of course this can't cover all version number schemes, and there's -# no way to know what a programmer means without asking him. -# -# The problem is what to do with letters (and other non-numeric -# characters) in a version number. The current implementation does the -# obvious and predictable thing: keep them as strings and compare -# lexically within a tuple comparison. This has the desired effect if -# an appended letter sequence implies something "post-release": -# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002". -# -# However, if letters in a version number imply a pre-release version, -# the "obvious" thing isn't correct. Eg. you would expect that -# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison -# implemented here, this just isn't so. -# -# Two possible solutions come to mind. The first is to tie the -# comparison algorithm to a particular set of semantic rules, as has -# been done in the StrictVersion class above. This works great as long -# as everyone can go along with bondage and discipline. Hopefully a -# (large) subset of Python module programmers will agree that the -# particular flavour of bondage and discipline provided by StrictVersion -# provides enough benefit to be worth using, and will submit their -# version numbering scheme to its domination. The free-thinking -# anarchists in the lot will never give in, though, and something needs -# to be done to accommodate them. -# -# Perhaps a "moderately strict" version class could be implemented that -# lets almost anything slide (syntactically), and makes some heuristic -# assumptions about non-digits in version number strings. This could -# sink into special-case-hell, though; if I was as talented and -# idiosyncratic as Larry Wall, I'd go ahead and implement a class that -# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is -# just as happy dealing with things like "2g6" and "1.13++". I don't -# think I'm smart enough to do it right though. -# -# In any case, I've coded the test suite for this module (see -# ../test/test_version.py) specifically to fail on things like comparing -# "1.2a2" and "1.2". That's not because the *code* is doing anything -# wrong, it's because the simple, obvious design doesn't match my -# complicated, hairy expectations for real-world version numbers. It -# would be a snap to fix the test suite to say, "Yep, LooseVersion does -# the Right Thing" (ie. the code matches the conception). But I'd rather -# have a conception that matches common notions about version numbers. - -class LooseVersion (Version): - - """Version numbering for anarchists and software realists. - Implements the standard interface for version number classes as - described above. A version number consists of a series of numbers, - separated by either periods or strings of letters. When comparing - version numbers, the numeric components will be compared - numerically, and the alphabetic components lexically. The following - are all valid version numbers, in no particular order: - - 1.5.1 - 1.5.2b2 - 161 - 3.10a - 8.02 - 3.4j - 1996.07.12 - 3.2.pl0 - 3.1.1.6 - 2g6 - 11g - 0.960923 - 2.2beta29 - 1.13++ - 5.5.kw - 2.0b1pl0 - - In fact, there is no such thing as an invalid version number under - this scheme; the rules for comparison are simple and predictable, - but may not always give the results you want (for some definition - of "want"). - """ - - component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) - - def __init__ (self, vstring=None): - if vstring: - self.parse(vstring) - - - def parse (self, vstring): - # I've given up on thinking I can reconstruct the version string - # from the parsed tuple -- so I just store the string here for - # use by __str__ - self.vstring = vstring - components = [x for x in self.component_re.split(vstring) - if x and x != '.'] - for i, obj in enumerate(components): - try: - components[i] = int(obj) - except ValueError: - pass - - self.version = components - - - def __str__ (self): - return self.vstring - - - def __repr__ (self): - return "LooseVersion ('%s')" % str(self) - - - def _cmp (self, other): - if isinstance(other, str): - other = LooseVersion(other) - elif not isinstance(other, LooseVersion): - return NotImplemented - - if self.version == other.version: - return 0 - if self.version < other.version: - return -1 - if self.version > other.version: - return 1 - - -# end class LooseVersion diff --git a/versionpredicate.py b/versionpredicate.py deleted file mode 100644 index 062c98f2..00000000 --- a/versionpredicate.py +++ /dev/null @@ -1,166 +0,0 @@ -"""Module for parsing and testing package version predicate strings. -""" -import re -import distutils.version -import operator - - -re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)", - re.ASCII) -# (package) (rest) - -re_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses -re_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") -# (comp) (version) - - -def splitUp(pred): - """Parse a single version comparison. - - Return (comparison string, StrictVersion) - """ - res = re_splitComparison.match(pred) - if not res: - raise ValueError("bad package restriction syntax: %r" % pred) - comp, verStr = res.groups() - return (comp, distutils.version.StrictVersion(verStr)) - -compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq, - ">": operator.gt, ">=": operator.ge, "!=": operator.ne} - -class VersionPredicate: - """Parse and test package version predicates. - - >>> v = VersionPredicate('pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)') - - The `name` attribute provides the full dotted name that is given:: - - >>> v.name - 'pyepat.abc' - - The str() of a `VersionPredicate` provides a normalized - human-readable version of the expression:: - - >>> print(v) - pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) - - The `satisfied_by()` method can be used to determine with a given - version number is included in the set described by the version - restrictions:: - - >>> v.satisfied_by('1.1') - True - >>> v.satisfied_by('1.4') - True - >>> v.satisfied_by('1.0') - False - >>> v.satisfied_by('4444.4') - False - >>> v.satisfied_by('1555.1b3') - False - - `VersionPredicate` is flexible in accepting extra whitespace:: - - >>> v = VersionPredicate(' pat( == 0.1 ) ') - >>> v.name - 'pat' - >>> v.satisfied_by('0.1') - True - >>> v.satisfied_by('0.2') - False - - If any version numbers passed in do not conform to the - restrictions of `StrictVersion`, a `ValueError` is raised:: - - >>> v = VersionPredicate('p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)') - Traceback (most recent call last): - ... - ValueError: invalid version number '1.2zb3' - - It the module or package name given does not conform to what's - allowed as a legal module or package name, `ValueError` is - raised:: - - >>> v = VersionPredicate('foo-bar') - Traceback (most recent call last): - ... - ValueError: expected parenthesized list: '-bar' - - >>> v = VersionPredicate('foo bar (12.21)') - Traceback (most recent call last): - ... - ValueError: expected parenthesized list: 'bar (12.21)' - - """ - - def __init__(self, versionPredicateStr): - """Parse a version predicate string. - """ - # Fields: - # name: package name - # pred: list of (comparison string, StrictVersion) - - versionPredicateStr = versionPredicateStr.strip() - if not versionPredicateStr: - raise ValueError("empty package restriction") - match = re_validPackage.match(versionPredicateStr) - if not match: - raise ValueError("bad package name in %r" % versionPredicateStr) - self.name, paren = match.groups() - paren = paren.strip() - if paren: - match = re_paren.match(paren) - if not match: - raise ValueError("expected parenthesized list: %r" % paren) - str = match.groups()[0] - self.pred = [splitUp(aPred) for aPred in str.split(",")] - if not self.pred: - raise ValueError("empty parenthesized list in %r" - % versionPredicateStr) - else: - self.pred = [] - - def __str__(self): - if self.pred: - seq = [cond + " " + str(ver) for cond, ver in self.pred] - return self.name + " (" + ", ".join(seq) + ")" - else: - return self.name - - def satisfied_by(self, version): - """True if version is compatible with all the predicates in self. - The parameter version must be acceptable to the StrictVersion - constructor. It may be either a string or StrictVersion. - """ - for cond, ver in self.pred: - if not compmap[cond](version, ver): - return False - return True - - -_provision_rx = None - -def split_provision(value): - """Return the name and optional version number of a provision. - - The version number, if given, will be returned as a `StrictVersion` - instance, otherwise it will be `None`. - - >>> split_provision('mypkg') - ('mypkg', None) - >>> split_provision(' mypkg( 1.2 ) ') - ('mypkg', StrictVersion ('1.2')) - """ - global _provision_rx - if _provision_rx is None: - _provision_rx = re.compile( - r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", - re.ASCII) - value = value.strip() - m = _provision_rx.match(value) - if not m: - raise ValueError("illegal provides specification: %r" % value) - ver = m.group(2) or None - if ver: - ver = distutils.version.StrictVersion(ver) - return m.group(1), ver -- cgit v1.2.1 From b678ce30a356abb36cd49d523731a9f978fce0bf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 13:13:44 -0400 Subject: Move distutils import to a separate file to avoid linter errors. --- setuptools/__init__.py | 3 +++ setuptools/distutils_patch.py | 15 +++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 setuptools/distutils_patch.py diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 811f3fd2..9df71a6d 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -2,6 +2,9 @@ import os import functools + +import setuptools.distutils_patch # noqa: F401 + import distutils.core import distutils.filelist import re diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py new file mode 100644 index 00000000..a2fc1a8c --- /dev/null +++ b/setuptools/distutils_patch.py @@ -0,0 +1,15 @@ +""" +Ensure that the local copy of distutils is preferred over stdlib. + +See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 +for more motivation. +""" + +import sys +import importlib +from os.path import dirname + + +sys.path.insert(0, dirname(dirname(__file__))) +importlib.import_module('distutils') +sys.path.pop(0) -- cgit v1.2.1 From 151599602b9d626ebcfe5ae6960ea216b767fec2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 13:38:15 -0400 Subject: Update distutils patch to monkeypatch all paths from sys.path to ensure that distutils is never imported except from the same path as setuptools. Assert that 'distutils' is not already in sys.modules. --- setuptools/distutils_patch.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index a2fc1a8c..e0abbd77 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -7,9 +7,23 @@ for more motivation. import sys import importlib +import contextlib from os.path import dirname -sys.path.insert(0, dirname(dirname(__file__))) -importlib.import_module('distutils') -sys.path.pop(0) +@contextlib.contextmanager +def patch_sys_path(): + orig = sys.path[:] + sys.path[:] = [dirname(dirname(__file__))] + try: + yield + finally: + sys.path[:] = orig + + +if 'distutils' in sys.path: + raise RuntimeError("Distutils must not be imported before setuptools") + + +with patch_sys_path(): + importlib.import_module('distutils') -- cgit v1.2.1 From 81aab2da23caf1a66298847d93b209ba16eb878e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 16:35:24 -0400 Subject: Ignore distutils when running tests. --- conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conftest.py b/conftest.py index 1746bfb5..3f7e59b4 100644 --- a/conftest.py +++ b/conftest.py @@ -14,6 +14,7 @@ def pytest_addoption(parser): collect_ignore = [ 'tests/manual_test.py', 'setuptools/tests/mod_with_constant.py', + 'distutils', ] -- cgit v1.2.1 From 781d42dffe2913f1a1f27128effa7198c27f569f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 16:35:50 -0400 Subject: Fallback to 'lib' when 'sys.platlibdir' does not exist. --- distutils/command/install.py | 2 +- distutils/sysconfig.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/distutils/command/install.py b/distutils/command/install.py index aaa300ef..21f8c27c 100644 --- a/distutils/command/install.py +++ b/distutils/command/install.py @@ -298,7 +298,7 @@ class install(Command): 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, 'abiflags': abiflags, - 'platlibdir': sys.platlibdir, + 'platlibdir': getattr(sys, 'platlibdir', 'lib'), } if HAS_USER_SITE: diff --git a/distutils/sysconfig.py b/distutils/sysconfig.py index 37feae5d..2109f746 100644 --- a/distutils/sysconfig.py +++ b/distutils/sysconfig.py @@ -148,7 +148,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): if plat_specific or standard_lib: # Platform-specific modules (any module from a non-pure-Python # module distribution) or standard Python library modules. - libdir = sys.platlibdir + libdir = getattr(sys, "platlibdir", "lib") else: # Pure Python libdir = "lib" -- cgit v1.2.1 From 7b25b1a6a70577fdadbadb75b55a80a328143848 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 26 May 2020 18:12:33 -0400 Subject: Use new link to sunset page. Ref #2134. --- pkg_resources/py2_warn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg_resources/py2_warn.py b/pkg_resources/py2_warn.py index 00cc8bc7..6855aa24 100644 --- a/pkg_resources/py2_warn.py +++ b/pkg_resources/py2_warn.py @@ -6,7 +6,7 @@ import textwrap msg = textwrap.dedent(""" Encountered a version of Setuptools that no longer supports this version of Python. Please head to - https://bit.ly/setuptools-py2-warning for support. + https://bit.ly/setuptools-py2-sunset for support. """) pre = "Setuptools no longer works on Python 2\n" -- cgit v1.2.1 From 96a1acb471eb8b2745a7ef98eceafb1671d7812c Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Wed, 27 May 2020 09:06:45 -0700 Subject: DOC: fix link missing > The `decalrative config`_ link present higher in the document is also unresolved, I'm not quite sure what the intended target was. --- docs/build_meta.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build_meta.txt b/docs/build_meta.txt index ef9fb2ac..fcc2b7fe 100644 --- a/docs/build_meta.txt +++ b/docs/build_meta.txt @@ -68,7 +68,7 @@ Use ``setuptools``' `declarative config`_ to specify the package information:: Now generate the distribution. Although the PyPA is still working to `provide a recommended tool `_ -to build packages, the `pep517 package `_ provides this functionality. To build the package:: $ pip install -q pep517 -- cgit v1.2.1 From 11051abd79b04782d759720a4d9dac2d2bbbf49c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 May 2020 07:10:59 -0400 Subject: Extract _parents function and _set_egg method. Reword comment. --- pkg_resources/__init__.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index edd3d2e8..2e7d5059 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1577,6 +1577,17 @@ is not allowed. register_loader_type(object, NullProvider) +def _parents(path): + """ + yield all parents of path including path + """ + last = None + while path != last: + yield path + last = path + path, _ = os.path.split(path) + + class EggProvider(NullProvider): """Provider based on a virtual filesystem""" @@ -1585,18 +1596,16 @@ class EggProvider(NullProvider): self._setup_prefix() def _setup_prefix(self): - # we assume here that our metadata may be nested inside a "basket" - # of multiple eggs; that's why we use module_path instead of .archive - path = self.module_path - old = None - while path != old: - if _is_egg_path(path): - self.egg_name = os.path.basename(path) - self.egg_info = os.path.join(path, 'EGG-INFO') - self.egg_root = path - break - old = path - path, base = os.path.split(path) + # Assume that metadata may be nested inside a "basket" + # of multiple eggs and use module_path instead of .archive. + eggs = filter(_is_egg_path, _parents(self.module_path)) + egg = next(eggs, None) + egg and self._set_egg(egg) + + def _set_egg(self, path): + self.egg_name = os.path.basename(path) + self.egg_info = os.path.join(path, 'EGG-INFO') + self.egg_root = path class DefaultProvider(EggProvider): -- cgit v1.2.1 From 965ec0df1ffa98ba5d8913a2770daaf5b92b0a0d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 May 2020 07:03:19 -0400 Subject: In pkg_resources, no longer detect any pathname ending in .egg as a Python egg. Now the path must be an unpacked egg or a zip file. Fixes #2129. --- changelog.d/2129.change.rst | 1 + pkg_resources/__init__.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 changelog.d/2129.change.rst diff --git a/changelog.d/2129.change.rst b/changelog.d/2129.change.rst new file mode 100644 index 00000000..b7d38862 --- /dev/null +++ b/changelog.d/2129.change.rst @@ -0,0 +1 @@ +In pkg_resources, no longer detect any pathname ending in .egg as a Python egg. Now the path must be an unpacked egg or a zip file. diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 2e7d5059..3c826eb0 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2373,7 +2373,15 @@ def _is_egg_path(path): """ Determine if given path appears to be an egg. """ - return path.lower().endswith('.egg') + return _is_zip_egg(path) or _is_unpacked_egg(path) + + +def _is_zip_egg(path): + return ( + path.lower().endswith('.egg') and + os.path.isfile(path) and + zipfile.is_zipfile(path) + ) def _is_unpacked_egg(path): @@ -2381,7 +2389,7 @@ def _is_unpacked_egg(path): Determine if given path appears to be an unpacked egg. """ return ( - _is_egg_path(path) and + path.lower().endswith('.egg') and os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO')) ) -- cgit v1.2.1 From 7339f2c1cea8fa5275b699f494300a309d58c580 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 May 2020 07:27:00 -0400 Subject: =?UTF-8?q?Bump=20version:=2046.4.0=20=E2=86=92=2047.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 7 +++++++ changelog.d/1700.change.rst | 1 - changelog.d/2094.breaking.rst | 1 - setup.cfg | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 changelog.d/1700.change.rst delete mode 100644 changelog.d/2094.breaking.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 72d02f2b..2639380e 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 46.4.0 +current_version = 47.0.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index ea667028..db1c67f7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v47.0.0 +------- + +* #2094: Setuptools now actively crashes under Python 2. Python 3.5 or later is required. Users of Python 2 should use ``setuptools<45``. +* #1700: Document all supported keywords by migrating the ones from distutils. + + v46.4.0 ------- diff --git a/changelog.d/1700.change.rst b/changelog.d/1700.change.rst deleted file mode 100644 index f66046a2..00000000 --- a/changelog.d/1700.change.rst +++ /dev/null @@ -1 +0,0 @@ -Document all supported keywords by migrating the ones from distutils. diff --git a/changelog.d/2094.breaking.rst b/changelog.d/2094.breaking.rst deleted file mode 100644 index c278d0fd..00000000 --- a/changelog.d/2094.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -Setuptools now actively crashes under Python 2. Python 3.5 or later is required. Users of Python 2 should use ``setuptools<45``. diff --git a/setup.cfg b/setup.cfg index 72d4dce9..ed31c450 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 46.4.0 +version = 47.0.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 3b32ab28d78eb38f681d36ecef505a65d1cbad38 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 May 2020 07:54:35 -0400 Subject: In wheel-to-egg conversion, use simple pkg_resources-style namespace declaration for packages that declare namespace_packages. Fixes #2070. --- setuptools/wheel.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/setuptools/wheel.py b/setuptools/wheel.py index ec1106a7..ca09bd19 100644 --- a/setuptools/wheel.py +++ b/setuptools/wheel.py @@ -27,12 +27,8 @@ WHEEL_NAME = re.compile( )\.whl$""", re.VERBOSE).match -NAMESPACE_PACKAGE_INIT = '''\ -try: - __import__('pkg_resources').declare_namespace(__name__) -except ImportError: - __path__ = __import__('pkgutil').extend_path(__path__, __name__) -''' +NAMESPACE_PACKAGE_INIT = \ + "__import__('pkg_resources').declare_namespace(__name__)\n" def unpack(src_dir, dst_dir): -- cgit v1.2.1 From 6bf44e11960fa31aaa5b910879fdd4435b524244 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 May 2020 07:55:09 -0400 Subject: =?UTF-8?q?Bump=20version:=2047.0.0=20=E2=86=92=2047.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ setup.cfg | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 2639380e..1fa442e2 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 47.0.0 +current_version = 47.1.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index db1c67f7..dbade950 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v47.1.0 +------- + +* #2070: In wheel-to-egg conversion, use simple pkg_resources-style namespace declaration for packages that declare namespace_packages. + + v47.0.0 ------- diff --git a/setup.cfg b/setup.cfg index ed31c450..c8f72e97 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 47.0.0 +version = 47.1.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From ca5e271eed022a492a60db8eed1e55e135d1210e Mon Sep 17 00:00:00 2001 From: Sumana Harihareswara Date: Thu, 28 May 2020 17:47:07 -0400 Subject: DOC: Update mailing list link Per https://groups.google.com/d/msg/pypa-dev/rUNsfIbruHM/LCEx-CB5AgAJ the pypa-dev Google Group is now decommissioned. Pointing to distutils-sig instead. Signed-off-by: Sumana Harihareswara --- changelog.d/2156.doc.rst | 1 + docs/developer-guide.txt | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 changelog.d/2156.doc.rst diff --git a/changelog.d/2156.doc.rst b/changelog.d/2156.doc.rst new file mode 100644 index 00000000..8d604979 --- /dev/null +++ b/changelog.d/2156.doc.rst @@ -0,0 +1 @@ +Update mailing list pointer in developer docs diff --git a/docs/developer-guide.txt b/docs/developer-guide.txt index e6171e4e..4a78e22e 100644 --- a/docs/developer-guide.txt +++ b/docs/developer-guide.txt @@ -23,16 +23,16 @@ contribution. Project Management ------------------ -Setuptools is maintained primarily in Github at `this home +Setuptools is maintained primarily in GitHub at `this home `_. Setuptools is maintained under the Python Packaging Authority (PyPA) with several core contributors. All bugs -for Setuptools are filed and the canonical source is maintained in Github. +for Setuptools are filed and the canonical source is maintained in GitHub. User support and discussions are done through the issue tracker (for specific) -issues, through the distutils-sig mailing list, or on IRC (Freenode) at +issues, through the `distutils-sig mailing list `_, or on IRC (Freenode) at #pypa. -Discussions about development happen on the pypa-dev mailing list or on +Discussions about development happen on the distutils-sig mailing list or on `Gitter `_. ----------------- @@ -44,7 +44,7 @@ describing the motivation behind making changes. First search to see if a ticket already exists for your issue. If not, create one. Try to think from the perspective of the reader. Explain what behavior you expected, what you got instead, and what factors might have contributed to the unexpected -behavior. In Github, surround a block of code or traceback with the triple +behavior. In GitHub, surround a block of code or traceback with the triple backtick "\`\`\`" so that it is formatted nicely. Filing a ticket provides a forum for justification, discussion, and -- cgit v1.2.1 From 4020c9a5fe3c30e1192412786ce299dd8a909266 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 May 2020 20:04:58 -0400 Subject: Avoid loading working set during `Distribution.finalize_options` prior to invoking `_install_setup_requires`, broken since v42.0.0. Fixes #2158. --- setuptools/__init__.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index a71b2bbd..9d8ae1ed 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -129,10 +129,27 @@ if PY3: def _install_setup_requires(attrs): # Note: do not use `setuptools.Distribution` directly, as # our PEP 517 backend patch `distutils.core.Distribution`. - dist = distutils.core.Distribution(dict( - (k, v) for k, v in attrs.items() - if k in ('dependency_links', 'setup_requires') - )) + class MinimalDistribution(distutils.core.Distribution): + """ + A minimal version of a distribution for supporting the + fetch_build_eggs interface. + """ + def __init__(self, attrs): + _incl = 'dependency_links', 'setup_requires' + filtered = { + k: attrs[k] + for k in set(_incl) & set(attrs) + } + distutils.core.Distribution.__init__(self, filtered) + + def finalize_options(self): + """ + Disable finalize_options to avoid building the working set. + Ref #2158. + """ + + dist = MinimalDistribution(attrs) + # Honor setup.cfg's options. dist.parse_config_files(ignore_option_errors=True) if dist.setup_requires: -- cgit v1.2.1 From 2dc4b62da3d32bd765686d62c8ce5ec69b96ff92 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 May 2020 20:05:26 -0400 Subject: Update changelog. --- changelog.d/2158.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2158.misc.rst diff --git a/changelog.d/2158.misc.rst b/changelog.d/2158.misc.rst new file mode 100644 index 00000000..16a5a504 --- /dev/null +++ b/changelog.d/2158.misc.rst @@ -0,0 +1 @@ +Avoid loading working set during ``Distribution.finalize_options`` prior to invoking ``_install_setup_requires``, broken since v42.0.0. -- cgit v1.2.1 From df51e62984850e58e8f29e8223b714091e0b8a5b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 May 2020 21:00:39 -0400 Subject: =?UTF-8?q?Bump=20version:=2044.1.0=20=E2=86=92=2044.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2158.misc.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2158.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index c8c9f544..4bea6cca 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 44.1.0 +current_version = 44.1.1 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 83cd8c78..43b65eb3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v44.1.1 +------- + +* #2158: Avoid loading working set during ``Distribution.finalize_options`` prior to invoking ``_install_setup_requires``, broken since v42.0.0. + + v44.1.0 ------- diff --git a/changelog.d/2158.misc.rst b/changelog.d/2158.misc.rst deleted file mode 100644 index 16a5a504..00000000 --- a/changelog.d/2158.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Avoid loading working set during ``Distribution.finalize_options`` prior to invoking ``_install_setup_requires``, broken since v42.0.0. diff --git a/setup.cfg b/setup.cfg index cb5ae73b..323d2cf5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,7 +19,7 @@ universal = 1 [metadata] name = setuptools -version = 44.1.0 +version = 44.1.1 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 6214d4d20a1c329f0b333abcdaaf7d242eed5e3f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 May 2020 21:13:12 -0400 Subject: =?UTF-8?q?Bump=20version:=2047.1.0=20=E2=86=92=2047.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 10 ++++++++++ changelog.d/2156.doc.rst | 1 - setup.cfg | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2156.doc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 1fa442e2..23226c35 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 47.1.0 +current_version = 47.1.1 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 048fef6b..b018cbea 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,13 @@ +v47.1.1 +------- + +* #2156: Update mailing list pointer in developer docs + +Incorporate changes from v44.1.1: + +* #2158: Avoid loading working set during ``Distribution.finalize_options`` prior to invoking ``_install_setup_requires``, broken since v42.0.0. + + v44.1.1 ------- diff --git a/changelog.d/2156.doc.rst b/changelog.d/2156.doc.rst deleted file mode 100644 index 8d604979..00000000 --- a/changelog.d/2156.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Update mailing list pointer in developer docs diff --git a/setup.cfg b/setup.cfg index c8f72e97..66fb8921 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 47.1.0 +version = 47.1.1 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 74de4e985eda49e38ece5805e05197dd4d2d9c8a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 29 May 2020 17:50:32 -0400 Subject: The workaround is only needed for Python 2. Ref #1998. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index aa99e283..d3df21bf 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,7 @@ minversion = 3.2 requires = tox-pip-version >= 0.0.6 # workaround for #1998 - virtualenv < 20 + virtualenv < 20; python_version=="2.7" [helpers] # Custom pip behavior -- cgit v1.2.1 From e4aa9070e7196975edb41f8dcaccf8eccbf83b2e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 29 May 2020 18:21:24 -0400 Subject: Setuptools no longer installs a site.py file during easy_install or develop installs. Ref #2165. --- setuptools/command/develop.py | 1 - setuptools/command/easy_install.py | 44 +------------------- setuptools/site-patch.py | 76 ----------------------------------- setuptools/tests/test_easy_install.py | 8 ---- tox.ini | 2 - 5 files changed, 2 insertions(+), 129 deletions(-) delete mode 100644 setuptools/site-patch.py diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index b5619246..e7e03cd4 100644 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -139,7 +139,6 @@ class develop(namespaces.DevelopInstaller, easy_install): self.reinitialize_command('build_ext', inplace=1) self.run_command('build_ext') - self.install_site_py() # ensure that target dir is site-safe if setuptools.bootstrap_install_from: self.easy_install(setuptools.bootstrap_install_from) setuptools.bootstrap_install_from = None diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 5a9576ff..a68490ab 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -205,7 +205,6 @@ class easy_install(Command): self.pth_file = self.always_copy_from = None self.site_dirs = None self.installed_projects = {} - self.sitepy_installed = False # Always read easy_install options, even if we are subclassed, or have # an independent instance created. This ensures that defaults will # always come from the standard configuration file(s)' "easy_install" @@ -494,12 +493,8 @@ class easy_install(Command): else: self.pth_file = None - if instdir not in map(normalize_path, _pythonpath()): - # only PYTHONPATH dirs need a site.py, so pretend it's there - self.sitepy_installed = True - elif self.multi_version and not os.path.exists(pth_file): - self.sitepy_installed = True # don't need site.py in this case - self.pth_file = None # and don't create a .pth file + if self.multi_version and not os.path.exists(pth_file): + self.pth_file = None # don't create a .pth file self.install_dir = instdir __cant_write_msg = textwrap.dedent(""" @@ -656,9 +651,6 @@ class easy_install(Command): os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir)) def easy_install(self, spec, deps=False): - if not self.editable: - self.install_site_py() - with self._tmpdir() as tmpdir: if not isinstance(spec, Requirement): if URL_SCHEME(spec): @@ -1317,38 +1309,6 @@ class easy_install(Command): Please make the appropriate changes for your system and try again. """).strip() - def install_site_py(self): - """Make sure there's a site.py in the target dir, if needed""" - - if self.sitepy_installed: - return # already did it, or don't need to - - sitepy = os.path.join(self.install_dir, "site.py") - source = resource_string("setuptools", "site-patch.py") - source = source.decode('utf-8') - current = "" - - if os.path.exists(sitepy): - log.debug("Checking existing site.py in %s", self.install_dir) - with io.open(sitepy) as strm: - current = strm.read() - - if not current.startswith('def __boot():'): - raise DistutilsError( - "%s is not a setuptools-generated site.py; please" - " remove it." % sitepy - ) - - if current != source: - log.info("Creating %s", sitepy) - if not self.dry_run: - ensure_directory(sitepy) - with io.open(sitepy, 'w', encoding='utf-8') as strm: - strm.write(source) - self.byte_compile([sitepy]) - - self.sitepy_installed = True - def create_home_path(self): """Create directories under ~.""" if not self.user: diff --git a/setuptools/site-patch.py b/setuptools/site-patch.py deleted file mode 100644 index be0d43d3..00000000 --- a/setuptools/site-patch.py +++ /dev/null @@ -1,76 +0,0 @@ -def __boot(): - import sys - import os - PYTHONPATH = os.environ.get('PYTHONPATH') - if PYTHONPATH is None or (sys.platform == 'win32' and not PYTHONPATH): - PYTHONPATH = [] - else: - PYTHONPATH = PYTHONPATH.split(os.pathsep) - - pic = getattr(sys, 'path_importer_cache', {}) - stdpath = sys.path[len(PYTHONPATH):] - mydir = os.path.dirname(__file__) - - for item in stdpath: - if item == mydir or not item: - continue # skip if current dir. on Windows, or my own directory - importer = pic.get(item) - if importer is not None: - loader = importer.find_module('site') - if loader is not None: - # This should actually reload the current module - loader.load_module('site') - break - else: - try: - import imp # Avoid import loop in Python 3 - stream, path, descr = imp.find_module('site', [item]) - except ImportError: - continue - if stream is None: - continue - try: - # This should actually reload the current module - imp.load_module('site', stream, path, descr) - finally: - stream.close() - break - else: - raise ImportError("Couldn't find the real 'site' module") - - # 2.2 comp - known_paths = dict([( - makepath(item)[1], 1) for item in sys.path]) # noqa - - oldpos = getattr(sys, '__egginsert', 0) # save old insertion position - sys.__egginsert = 0 # and reset the current one - - for item in PYTHONPATH: - addsitedir(item) # noqa - - sys.__egginsert += oldpos # restore effective old position - - d, nd = makepath(stdpath[0]) # noqa - insert_at = None - new_path = [] - - for item in sys.path: - p, np = makepath(item) # noqa - - if np == nd and insert_at is None: - # We've hit the first 'system' path entry, so added entries go here - insert_at = len(new_path) - - if np in known_paths or insert_at is None: - new_path.append(item) - else: - # new path after the insert point, back-insert it - new_path.insert(insert_at, item) - insert_at += 1 - - sys.path[:] = new_path - - -if __name__ == 'site': - __boot() - del __boot diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 3044cbd0..e8ebd320 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -61,14 +61,6 @@ SETUP_PY = DALS(""" class TestEasyInstallTest: - def test_install_site_py(self, tmpdir): - dist = Distribution() - cmd = ei.easy_install(dist) - cmd.sitepy_installed = False - cmd.install_dir = str(tmpdir) - cmd.install_site_py() - assert (tmpdir / 'site.py').exists() - def test_get_script_args(self): header = ei.CommandSpec.best().from_environment().as_header() expected = header + DALS(r""" diff --git a/tox.ini b/tox.ini index d3df21bf..a1cc0b6e 100644 --- a/tox.ini +++ b/tox.ini @@ -7,8 +7,6 @@ envlist=python minversion = 3.2 requires = tox-pip-version >= 0.0.6 - # workaround for #1998 - virtualenv < 20; python_version=="2.7" [helpers] # Custom pip behavior -- cgit v1.2.1 From d9a3b57cfabcae25539b5118977b2f6ef78d641b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 29 May 2020 18:22:39 -0400 Subject: Update changelog. --- changelog.d/2165.breaking.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2165.breaking.rst diff --git a/changelog.d/2165.breaking.rst b/changelog.d/2165.breaking.rst new file mode 100644 index 00000000..a9b598b9 --- /dev/null +++ b/changelog.d/2165.breaking.rst @@ -0,0 +1 @@ +Setuptools no longer installs a site.py file during easy_install or develop installs. As a result, .eggs on PYTHONPATH will no longer take precedence over other packages on sys.path. If this issue affects your production environment, please reach out to the maintainers at #2165. -- cgit v1.2.1 From 4ac101a1805a3773649b42c7682038f41314738e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 08:43:36 -0400 Subject: Restore Python 3.5 syntax compatibility in distutils.dist. --- distutils/dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distutils/dist.py b/distutils/dist.py index 6cf0a0d6..37db4d6c 100644 --- a/distutils/dist.py +++ b/distutils/dist.py @@ -35,7 +35,8 @@ def _ensure_list(value, fieldname): elif not isinstance(value, list): # passing a tuple or an iterator perhaps, warn and convert typename = type(value).__name__ - msg = f"Warning: '{fieldname}' should be a list, got type '{typename}'" + msg = "Warning: '{fieldname}' should be a list, got type '{typename}'" + msg = msg.format(**locals()) log.log(log.WARN, msg) value = list(value) return value -- cgit v1.2.1 From 9962c0af814ccd13c370ce8b04742a52f77025c6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 08:50:31 -0400 Subject: Extract function for ensure_local_distuils --- setuptools/distutils_patch.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index e0abbd77..43b64c0f 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -21,9 +21,11 @@ def patch_sys_path(): sys.path[:] = orig -if 'distutils' in sys.path: - raise RuntimeError("Distutils must not be imported before setuptools") +def ensure_local_distutils(): + if 'distutils' in sys.path: + raise RuntimeError("Distutils must not be imported before setuptools") + with patch_sys_path(): + importlib.import_module('distutils') -with patch_sys_path(): - importlib.import_module('distutils') +ensure_local_distutils() -- cgit v1.2.1 From 38a8632835c90bfb0a220f91a8b2e982a69b81ab Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 09:34:34 -0400 Subject: Fallback to '_sysconfigdata' when platform-specific sysconfigdata is unavailable. --- distutils/sysconfig.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/distutils/sysconfig.py b/distutils/sysconfig.py index 2109f746..79391e86 100644 --- a/distutils/sysconfig.py +++ b/distutils/sysconfig.py @@ -444,7 +444,12 @@ def _init_posix(): platform=sys.platform, multiarch=getattr(sys.implementation, '_multiarch', ''), )) - _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + try: + _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + except ImportError: + # Python 3.5 and pypy 7.3.1 + _temp = __import__( + '_sysconfigdata', globals(), locals(), ['build_time_vars'], 0) build_time_vars = _temp.build_time_vars global _config_vars _config_vars = {} -- cgit v1.2.1 From db7ff2e1b0ea51fee56b7f626a548cd2ef590e5e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 12:26:45 -0400 Subject: Add changelog entry. --- changelog.d/2143.breaking.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2143.breaking.rst diff --git a/changelog.d/2143.breaking.rst b/changelog.d/2143.breaking.rst new file mode 100644 index 00000000..185542ad --- /dev/null +++ b/changelog.d/2143.breaking.rst @@ -0,0 +1 @@ +Setuptools adopts distutils from the standard library as a top-level ``distutils`` package and no longer depends on distutils in the standard library. This new ``distutils`` package will be masked by ``distutils`` in the standard library unless that library has been stripped by a downstream package manager or gets removed in a future version. Although this change is not expected to break any use-cases, it will likely affect tool-chains that are monkey-patching distutils or relying on Setuptools' own monkey patching of distutils. -- cgit v1.2.1 From 6588760710433951a1df48c298bdc49fa5bb7c6c Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Thu, 11 Jun 2020 22:09:29 +0300 Subject: Fix exception causes in package_index.py --- setuptools/package_index.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 0744ea2a..1702c7c6 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -53,10 +53,10 @@ user_agent = _tmpl.format( def parse_requirement_arg(spec): try: return Requirement.parse(spec) - except ValueError: + except ValueError as e: raise DistutilsError( "Not a URL, existing file, or requirement spec: %r" % (spec,) - ) + ) from e def parse_bdist_wininst(name): @@ -772,7 +772,7 @@ class PackageIndex(Environment): if warning: self.warn(warning, msg) else: - raise DistutilsError('%s %s' % (url, msg)) + raise DistutilsError('%s %s' % (url, msg)) from v except urllib.error.HTTPError as v: return v except urllib.error.URLError as v: @@ -780,7 +780,7 @@ class PackageIndex(Environment): self.warn(warning, v.reason) else: raise DistutilsError("Download error for %s: %s" - % (url, v.reason)) + % (url, v.reason)) from v except http_client.BadStatusLine as v: if warning: self.warn(warning, v.line) @@ -789,13 +789,13 @@ class PackageIndex(Environment): '%s returned a bad status line. The server might be ' 'down, %s' % (url, v.line) - ) + ) from v except (http_client.HTTPException, socket.error) as v: if warning: self.warn(warning, v) else: raise DistutilsError("Download error for %s: %s" - % (url, v)) + % (url, v)) from v def _download_url(self, scheme, url, tmpdir): # Determine download filename -- cgit v1.2.1 From 01121d057bea0128c34a92813c51ba3ec4902fce Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 14 Jun 2020 12:58:52 -0400 Subject: Decrease start-up time of editable-installed entry points on newer versions of Python --- changelog.d/2194.change.rst | 1 + setuptools/command/easy_install.py | 43 +++++++++++++++++++++++----------- setuptools/tests/test_easy_install.py | 44 ++++++++++++++++++++++++----------- 3 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 changelog.d/2194.change.rst diff --git a/changelog.d/2194.change.rst b/changelog.d/2194.change.rst new file mode 100644 index 00000000..8b5adb31 --- /dev/null +++ b/changelog.d/2194.change.rst @@ -0,0 +1 @@ +Editable-installed entry points now load significantly faster on Python versions 3.8+. diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 5a9576ff..a82b1655 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2070,19 +2070,36 @@ class ScriptWriter: gui apps. """ - template = textwrap.dedent(r""" - # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r - __requires__ = %(spec)r - import re - import sys - from pkg_resources import load_entry_point - - if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) - sys.exit( - load_entry_point(%(spec)r, %(group)r, %(name)r)() - ) - """).lstrip() + try: + from importlib.metadata import distribution # noqa: F401 + + template = textwrap.dedent(r""" + # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r + __requires__ = %(spec)r + import re + import sys + from importlib.metadata import distribution + + if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + for entry_point in distribution(%(spec)r).entry_points: + if entry_point.group == %(group)r and entry_point.name == %(name)r: + sys.exit(entry_point.load()()) + """).lstrip() # noqa: E501 + except ImportError: + template = textwrap.dedent(r""" + # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r + __requires__ = %(spec)r + import re + import sys + from pkg_resources import load_entry_point + + if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit( + load_entry_point(%(spec)r, %(group)r, %(name)r)() + ) + """).lstrip() # noqa: E501 command_spec_class = CommandSpec diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 3044cbd0..58108db6 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -71,19 +71,37 @@ class TestEasyInstallTest: def test_get_script_args(self): header = ei.CommandSpec.best().from_environment().as_header() - expected = header + DALS(r""" - # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name' - __requires__ = 'spec' - import re - import sys - from pkg_resources import load_entry_point - - if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) - sys.exit( - load_entry_point('spec', 'console_scripts', 'name')() - ) - """) # noqa: E501 + try: + from importlib.metadata import distribution # noqa: F401 + + expected = header + DALS(r""" + # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name' + __requires__ = 'spec' + import re + import sys + from importlib.metadata import distribution + + if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + for entry_point in distribution('spec').entry_points: + if entry_point.group == 'console_scripts' and entry_point.name == 'name': + sys.exit(entry_point.load()()) + """) # noqa: E501 + except ImportError: + expected = header + DALS(r""" + # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name' + __requires__ = 'spec' + import re + import sys + from pkg_resources import load_entry_point + + if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit( + load_entry_point('spec', 'console_scripts', 'name')() + ) + """) # noqa: E501 + dist = FakeDist() args = next(ei.ScriptWriter.get_args(dist)) -- cgit v1.2.1 From d6501f3c75384340f1742a864c1ffa76977437b6 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 14 Jun 2020 14:21:26 -0400 Subject: address --- setuptools/command/easy_install.py | 6 ++---- setuptools/tests/test_easy_install.py | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index a82b1655..15e46cfc 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2070,9 +2070,7 @@ class ScriptWriter: gui apps. """ - try: - from importlib.metadata import distribution # noqa: F401 - + if sys.version_info >= (3, 8): template = textwrap.dedent(r""" # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r __requires__ = %(spec)r @@ -2086,7 +2084,7 @@ class ScriptWriter: if entry_point.group == %(group)r and entry_point.name == %(name)r: sys.exit(entry_point.load()()) """).lstrip() # noqa: E501 - except ImportError: + else: template = textwrap.dedent(r""" # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r __requires__ = %(spec)r diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 58108db6..9aa2bd63 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -71,9 +71,7 @@ class TestEasyInstallTest: def test_get_script_args(self): header = ei.CommandSpec.best().from_environment().as_header() - try: - from importlib.metadata import distribution # noqa: F401 - + if sys.version_info >= (3, 8): expected = header + DALS(r""" # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name' __requires__ = 'spec' @@ -87,7 +85,7 @@ class TestEasyInstallTest: if entry_point.group == 'console_scripts' and entry_point.name == 'name': sys.exit(entry_point.load()()) """) # noqa: E501 - except ImportError: + else: expected = header + DALS(r""" # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name' __requires__ = 'spec' -- cgit v1.2.1 From f8cd1f258f4b3f52521db2a12bfbd0832fb2e9fa Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 14 Jun 2020 21:04:04 -0400 Subject: address --- setuptools/command/easy_install.py | 1 - setuptools/tests/test_easy_install.py | 1 - 2 files changed, 2 deletions(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 15e46cfc..ab8258ca 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2073,7 +2073,6 @@ class ScriptWriter: if sys.version_info >= (3, 8): template = textwrap.dedent(r""" # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r - __requires__ = %(spec)r import re import sys from importlib.metadata import distribution diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 9aa2bd63..345823cf 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -74,7 +74,6 @@ class TestEasyInstallTest: if sys.version_info >= (3, 8): expected = header + DALS(r""" # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name' - __requires__ = 'spec' import re import sys from importlib.metadata import distribution -- cgit v1.2.1 From a4d8518c7b3cadf5e008871cf0d8c2d02a6a4492 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Jun 2020 08:55:00 -0400 Subject: Make a few assertions about the entry point script rather than keeping a fully-rendered copy. --- setuptools/tests/test_easy_install.py | 38 +++++++---------------------------- 1 file changed, 7 insertions(+), 31 deletions(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 345823cf..8611dc16 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -16,6 +16,7 @@ import io import zipfile import mock import time +import re from setuptools.extern import six @@ -71,40 +72,15 @@ class TestEasyInstallTest: def test_get_script_args(self): header = ei.CommandSpec.best().from_environment().as_header() - if sys.version_info >= (3, 8): - expected = header + DALS(r""" - # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name' - import re - import sys - from importlib.metadata import distribution - - if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) - for entry_point in distribution('spec').entry_points: - if entry_point.group == 'console_scripts' and entry_point.name == 'name': - sys.exit(entry_point.load()()) - """) # noqa: E501 - else: - expected = header + DALS(r""" - # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name' - __requires__ = 'spec' - import re - import sys - from pkg_resources import load_entry_point - - if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) - sys.exit( - load_entry_point('spec', 'console_scripts', 'name')() - ) - """) # noqa: E501 - dist = FakeDist() - args = next(ei.ScriptWriter.get_args(dist)) name, script = itertools.islice(args, 2) - - assert script == expected + assert script.startswith(header) + assert "'spec'" in script + assert "'console_scripts'" in script + assert "'name'" in script + assert re.search( + '^# EASY-INSTALL-ENTRY-SCRIPT', script, flags=re.MULTILINE) def test_no_find_links(self): # new option '--no-find-links', that blocks find-links added at -- cgit v1.2.1 From c999ba42e3cc99cfdf8c1eea88b9b0136104af12 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Jun 2020 08:56:45 -0400 Subject: =?UTF-8?q?Bump=20version:=2047.1.1=20=E2=86=92=2047.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2194.change.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2194.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 23226c35..ffb0f2e8 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 47.1.1 +current_version = 47.2.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index b018cbea..44abab75 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v47.2.0 +------- + +* #2194: Editable-installed entry points now load significantly faster on Python versions 3.8+. + + v47.1.1 ------- diff --git a/changelog.d/2194.change.rst b/changelog.d/2194.change.rst deleted file mode 100644 index 8b5adb31..00000000 --- a/changelog.d/2194.change.rst +++ /dev/null @@ -1 +0,0 @@ -Editable-installed entry points now load significantly faster on Python versions 3.8+. diff --git a/setup.cfg b/setup.cfg index 66fb8921..4beae652 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 47.1.1 +version = 47.2.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 2b4b5de16c8dc782bde3ee6ed54ef8868d90eb6e Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Mon, 15 Jun 2020 11:54:04 -0400 Subject: Fix entry point scripts --- setuptools/command/easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index ab8258ca..2563f313 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2079,7 +2079,7 @@ class ScriptWriter: if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) - for entry_point in distribution(%(spec)r).entry_points: + for entry_point in distribution(%(spec)r.split('==')[0]).entry_points: if entry_point.group == %(group)r and entry_point.name == %(name)r: sys.exit(entry_point.load()()) """).lstrip() # noqa: E501 -- cgit v1.2.1 From d74a16061950593f388a72871533b303b21e2a4e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Jun 2020 16:17:38 -0400 Subject: Update changelog. --- changelog.d/2195.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2195.misc.rst diff --git a/changelog.d/2195.misc.rst b/changelog.d/2195.misc.rst new file mode 100644 index 00000000..f73994dd --- /dev/null +++ b/changelog.d/2195.misc.rst @@ -0,0 +1 @@ +Fix broken entry points generated by easy-install (pip editable installs). -- cgit v1.2.1 From 9bb11490f57d8d77cd789e9719588a8603dc375b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Jun 2020 16:34:22 -0400 Subject: Unify the entry point template. --- setuptools/command/easy_install.py | 54 ++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 2563f313..81526b9a 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2070,33 +2070,37 @@ class ScriptWriter: gui apps. """ - if sys.version_info >= (3, 8): - template = textwrap.dedent(r""" - # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r - import re - import sys + template = textwrap.dedent(r""" + # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r + import re + import sys + + try: from importlib.metadata import distribution + except ImportError: + try: + from importlib_metadata import distribution + except ImportError: + from pkg_resources import load_entry_point - if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) - for entry_point in distribution(%(spec)r.split('==')[0]).entry_points: - if entry_point.group == %(group)r and entry_point.name == %(name)r: - sys.exit(entry_point.load()()) - """).lstrip() # noqa: E501 - else: - template = textwrap.dedent(r""" - # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r - __requires__ = %(spec)r - import re - import sys - from pkg_resources import load_entry_point - - if __name__ == '__main__': - sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) - sys.exit( - load_entry_point(%(spec)r, %(group)r, %(name)r)() - ) - """).lstrip() # noqa: E501 + + def importlib_load_entry_point(spec, group, name): + dist_name, _, _ = spec.partition('==') + matches = ( + entry_point + for entry_point in distribution(dist_name).entry_points + if entry_point.group == group and entry_point.name == name + ) + return next(matches).load() + + + globals().setdefault('load_entry_point', importlib_load_entry_point) + + + if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(load_entry_point(%(spec)r, %(group)r, %(name)r)()) + """).lstrip() command_spec_class = CommandSpec -- cgit v1.2.1 From 9db4553f1ce7d9ab4ae42e0e8a37a54bda3fd900 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Jun 2020 16:38:56 -0400 Subject: Update changelog. --- changelog.d/2197.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2197.change.rst diff --git a/changelog.d/2197.change.rst b/changelog.d/2197.change.rst new file mode 100644 index 00000000..67b0435a --- /dev/null +++ b/changelog.d/2197.change.rst @@ -0,0 +1 @@ +Console script wrapper for editable installs now has a unified template and honors importlib_metadata if present for faster script execution on older Pythons. -- cgit v1.2.1 From 3955acbb0da75df804d86a52d6fbcc269075a9d3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Jun 2020 16:47:28 -0400 Subject: =?UTF-8?q?Bump=20version:=2047.2.0=20=E2=86=92=2047.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 7 +++++++ changelog.d/2195.misc.rst | 1 - changelog.d/2197.change.rst | 1 - setup.cfg | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 changelog.d/2195.misc.rst delete mode 100644 changelog.d/2197.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index ffb0f2e8..1bdc4be3 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 47.2.0 +current_version = 47.3.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 44abab75..16608ddc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v47.3.0 +------- + +* #2197: Console script wrapper for editable installs now has a unified template and honors importlib_metadata if present for faster script execution on older Pythons. +* #2195: Fix broken entry points generated by easy-install (pip editable installs). + + v47.2.0 ------- diff --git a/changelog.d/2195.misc.rst b/changelog.d/2195.misc.rst deleted file mode 100644 index f73994dd..00000000 --- a/changelog.d/2195.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Fix broken entry points generated by easy-install (pip editable installs). diff --git a/changelog.d/2197.change.rst b/changelog.d/2197.change.rst deleted file mode 100644 index 67b0435a..00000000 --- a/changelog.d/2197.change.rst +++ /dev/null @@ -1 +0,0 @@ -Console script wrapper for editable installs now has a unified template and honors importlib_metadata if present for faster script execution on older Pythons. diff --git a/setup.cfg b/setup.cfg index 4beae652..9d3cad52 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 47.2.0 +version = 47.3.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 09df3c7adba9a1e687151805b61aec72f6c8b13f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Jun 2020 17:45:10 -0400 Subject: Add a compatibility wrapper, so downstream consumers trapping a RequirementParseError will now trap an InvalidRequirement, allowing transition to only trapping an InvalidRequirement. --- pkg_resources/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 56be3c22..1433409a 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -3090,6 +3090,10 @@ def parse_requirements(strs): yield Requirement(line) +class RequirementParseError(packaging.requirements.InvalidRequirement): + "Compatibility wrapper for InvalidRequirement" + + class Requirement(packaging.requirements.Requirement): def __init__(self, requirement_string): """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" -- cgit v1.2.1 From 94491a19ae999901b812fdf878c798cba11fc590 Mon Sep 17 00:00:00 2001 From: Samuel Watkins <35979561+slwatkins@users.noreply.github.com> Date: Tue, 16 Jun 2020 12:33:08 -0700 Subject: docs: fix typo in path to vendored.txt --- docs/developer-guide.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide.txt b/docs/developer-guide.txt index 4a78e22e..e84cd640 100644 --- a/docs/developer-guide.txt +++ b/docs/developer-guide.txt @@ -145,5 +145,5 @@ setuptools from source. Eventually, this limitation may be lifted as PEP 517/518 reach ubiquitous adoption, but for now, Setuptools cannot declare dependencies other than through ``setuptools/_vendor/vendored.txt`` and -``pkg_reosurces/_vendor/vendored.txt`` and refreshed by way of +``pkg_resources/_vendor/vendored.txt`` and refreshed by way of ``paver update_vendored`` (pavement.py). -- cgit v1.2.1 From 97a686d4e65fb30f0db4be3e498792b531942128 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 16 Jun 2020 17:21:27 -0400 Subject: Restore __requires__ directive for compatibility. Fixes #2198. --- changelog.d/2198.misc.rst | 1 + setuptools/command/easy_install.py | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 changelog.d/2198.misc.rst diff --git a/changelog.d/2198.misc.rst b/changelog.d/2198.misc.rst new file mode 100644 index 00000000..d4b4be38 --- /dev/null +++ b/changelog.d/2198.misc.rst @@ -0,0 +1 @@ +Restore ``__requires__`` directive in easy-install wrapper scripts. diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 27b4558b..89be91ac 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2075,6 +2075,9 @@ class ScriptWriter: import re import sys + # for compatibility with easy_install; see #2198 + __requires__ = %(spec)r + try: from importlib.metadata import distribution except ImportError: -- cgit v1.2.1 From 08ae4203c9feef98d4c2dffd2bd48308fdc5bee6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 16 Jun 2020 17:29:18 -0400 Subject: Make the change backward-compatible, since v47 was the breaking change. Ref #1973. --- changelog.d/1973.breaking.rst | 1 - changelog.d/1973.misc.rst | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelog.d/1973.breaking.rst create mode 100644 changelog.d/1973.misc.rst diff --git a/changelog.d/1973.breaking.rst b/changelog.d/1973.breaking.rst deleted file mode 100644 index 398fdd71..00000000 --- a/changelog.d/1973.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -Removed ``pkg_resources.py31compat.makedirs`` in favor of the stdlib. Use ``os.makedirs()`` instead. diff --git a/changelog.d/1973.misc.rst b/changelog.d/1973.misc.rst new file mode 100644 index 00000000..398fdd71 --- /dev/null +++ b/changelog.d/1973.misc.rst @@ -0,0 +1 @@ +Removed ``pkg_resources.py31compat.makedirs`` in favor of the stdlib. Use ``os.makedirs()`` instead. -- cgit v1.2.1 From 145a78cdc1afb4ce0c0208ae86a00708eb79d27b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 16 Jun 2020 17:29:27 -0400 Subject: =?UTF-8?q?Bump=20version:=2047.3.0=20=E2=86=92=2047.3.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 7 +++++++ changelog.d/1973.misc.rst | 1 - changelog.d/2198.misc.rst | 1 - setup.cfg | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 changelog.d/1973.misc.rst delete mode 100644 changelog.d/2198.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 1bdc4be3..9b106cfa 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 47.3.0 +current_version = 47.3.1 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 16608ddc..799163ed 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v47.3.1 +------- + +* #1973: Removed ``pkg_resources.py31compat.makedirs`` in favor of the stdlib. Use ``os.makedirs()`` instead. +* #2198: Restore ``__requires__`` directive in easy-install wrapper scripts. + + v47.3.0 ------- diff --git a/changelog.d/1973.misc.rst b/changelog.d/1973.misc.rst deleted file mode 100644 index 398fdd71..00000000 --- a/changelog.d/1973.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Removed ``pkg_resources.py31compat.makedirs`` in favor of the stdlib. Use ``os.makedirs()`` instead. diff --git a/changelog.d/2198.misc.rst b/changelog.d/2198.misc.rst deleted file mode 100644 index d4b4be38..00000000 --- a/changelog.d/2198.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Restore ``__requires__`` directive in easy-install wrapper scripts. diff --git a/setup.cfg b/setup.cfg index 9d3cad52..f23714b6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 47.3.0 +version = 47.3.1 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 4b54a8a038d5f9f2ead224b030f87f393d57d40b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Jun 2020 18:07:13 +0200 Subject: bpo-41003: Fix test_copyreg when numpy is installed (GH-20935) Fix test_copyreg when numpy is installed: test.pickletester now saves/restores warnings.filters when importing numpy, to ignore filters installed by numpy. Add the save_restore_warnings_filters() function to the test.support.warnings_helper module. --- tests/__init__.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 5d2e69e3..16d011fd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -15,26 +15,25 @@ by import rather than matching pre-defined names. import os import sys import unittest -import warnings from test.support import run_unittest +from test.support.warnings_helper import save_restore_warnings_filters here = os.path.dirname(__file__) or os.curdir def test_suite(): - old_filters = warnings.filters[:] suite = unittest.TestSuite() for fn in os.listdir(here): if fn.startswith("test") and fn.endswith(".py"): modname = "distutils.tests." + fn[:-3] - __import__(modname) + # bpo-40055: Save/restore warnings filters to leave them unchanged. + # Importing tests imports docutils which imports pkg_resources + # which adds a warnings filter. + with save_restore_warnings_filters(): + __import__(modname) module = sys.modules[modname] suite.addTest(module.test_suite()) - # bpo-40055: Save/restore warnings filters to leave them unchanged. - # Importing tests imports docutils which imports pkg_resources which adds a - # warnings filter. - warnings.filters[:] = old_filters return suite -- cgit v1.2.1 From fba62111e62d2ef06097cd319a0838c5baf006ca Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 20 Jun 2020 11:10:31 +0300 Subject: bpo-41043: Escape literal part of the path for glob(). (GH-20994) --- command/build_py.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/build_py.py b/command/build_py.py index cf0ca57c..edc2171c 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -5,7 +5,7 @@ Implements the Distutils 'build_py' command.""" import os import importlib.util import sys -from glob import glob +import glob from distutils.core import Command from distutils.errors import * @@ -125,7 +125,7 @@ class build_py (Command): files = [] for pattern in globs: # Each pattern has to be converted to a platform-specific path - filelist = glob(os.path.join(src_dir, convert_path(pattern))) + filelist = glob.glob(os.path.join(glob.escape(src_dir), convert_path(pattern))) # Files that match more than one pattern are only added once files.extend([fn for fn in filelist if fn not in files and os.path.isfile(fn)]) @@ -216,7 +216,7 @@ class build_py (Command): def find_package_modules(self, package, package_dir): self.check_package(package, package_dir) - module_files = glob(os.path.join(package_dir, "*.py")) + module_files = glob.glob(os.path.join(glob.escape(package_dir), "*.py")) modules = [] setup_script = os.path.abspath(self.distribution.script_name) -- cgit v1.2.1 From a8081a19cc48fc285dfd50953bb2c522cb4b8b02 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Jun 2020 12:09:39 -0400 Subject: Update setuptools/distutils_patch.py Co-authored-by: Steve Dower --- setuptools/distutils_patch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index 43b64c0f..1416a7a3 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -22,7 +22,7 @@ def patch_sys_path(): def ensure_local_distutils(): - if 'distutils' in sys.path: + if 'distutils' in sys.modules: raise RuntimeError("Distutils must not be imported before setuptools") with patch_sys_path(): importlib.import_module('distutils') -- cgit v1.2.1 From 51e062754df592b105547abbd6e20851972435d4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Jun 2020 12:49:50 -0400 Subject: Wordsmith --- changelog.d/2143.breaking.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/2143.breaking.rst b/changelog.d/2143.breaking.rst index 185542ad..29030b32 100644 --- a/changelog.d/2143.breaking.rst +++ b/changelog.d/2143.breaking.rst @@ -1 +1 @@ -Setuptools adopts distutils from the standard library as a top-level ``distutils`` package and no longer depends on distutils in the standard library. This new ``distutils`` package will be masked by ``distutils`` in the standard library unless that library has been stripped by a downstream package manager or gets removed in a future version. Although this change is not expected to break any use-cases, it will likely affect tool-chains that are monkey-patching distutils or relying on Setuptools' own monkey patching of distutils. +Setuptools adopts distutils from the standard library as a top-level ``distutils`` package and no longer depends on distutils in the standard library. This new ``distutils`` package will be masked by ``distutils`` in the standard library unless that library has been stripped by a downstream package manager or gets removed in a future version. Although this change is not expected to break any use cases, it will likely affect tool chains that are monkey-patching distutils or relying on Setuptools' own monkey-patching of distutils. -- cgit v1.2.1 From b596e4b0f684f5ac11673598e1de3aaa5bc74162 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Jun 2020 14:47:11 -0400 Subject: Replace distutils rather than requiring it to be present in advanec. Instead of crashing, issue a warning when Setuptools is replacing distutils. --- distutils/__init__.py | 2 ++ setuptools/distutils_patch.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/distutils/__init__.py b/distutils/__init__.py index d823d040..7dac55b6 100644 --- a/distutils/__init__.py +++ b/distutils/__init__.py @@ -11,3 +11,5 @@ used from a setup script as import sys __version__ = sys.version[:sys.version.index(' ')] + +local = True diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index 1416a7a3..f9e63798 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -6,8 +6,10 @@ for more motivation. """ import sys +import re import importlib import contextlib +import warnings from os.path import dirname @@ -21,11 +23,20 @@ def patch_sys_path(): sys.path[:] = orig +def clear_distutils(): + if 'distutils' not in sys.modules: + return + warnings.warn("Setuptools is replacing distutils") + mods = [name for name in sys.modules if re.match(r'distutils\b', name)] + for name in mods: + del sys.modules[name] + + def ensure_local_distutils(): - if 'distutils' in sys.modules: - raise RuntimeError("Distutils must not be imported before setuptools") + clear_distutils() with patch_sys_path(): importlib.import_module('distutils') + assert sys.modules['distutils'].local ensure_local_distutils() -- cgit v1.2.1 From 4516d87036f971a4c98900301b6354c0aae73dc8 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Sun, 21 Jun 2020 16:39:53 -0600 Subject: Use importlib instead of imp in __bootstrap__ functions --- changelog.d/2071.misc.rst | 1 + setuptools/command/bdist_egg.py | 5 +++-- setuptools/command/build_ext.py | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 changelog.d/2071.misc.rst diff --git a/changelog.d/2071.misc.rst b/changelog.d/2071.misc.rst new file mode 100644 index 00000000..eb36480c --- /dev/null +++ b/changelog.d/2071.misc.rst @@ -0,0 +1 @@ +Replaced references to the deprecated imp package with references to importlib diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 1b28d4c9..e94fe252 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -55,10 +55,11 @@ def write_stub(resource, pyfile): _stub_template = textwrap.dedent(""" def __bootstrap__(): global __bootstrap__, __loader__, __file__ - import sys, pkg_resources, imp + import sys, pkg_resources + from importlib.machinery import ExtensionFileLoader __file__ = pkg_resources.resource_filename(__name__, %r) __loader__ = None; del __bootstrap__, __loader__ - imp.load_dynamic(__name__,__file__) + ExtensionFileLoader(__name__,__file__).exec_module() __bootstrap__() """).lstrip() with open(pyfile, 'w') as f: diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 03b6f346..327fa063 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -254,7 +254,8 @@ class build_ext(_build_ext): '\n'.join([ "def __bootstrap__():", " global __bootstrap__, __file__, __loader__", - " import sys, os, pkg_resources, imp" + if_dl(", dl"), + " import sys, os, pkg_resources" + if_dl(", dl"), + " from importlib.machinery import ExtensionFileLoader", " __file__ = pkg_resources.resource_filename" "(__name__,%r)" % os.path.basename(ext._file_name), @@ -266,7 +267,8 @@ class build_ext(_build_ext): " try:", " os.chdir(os.path.dirname(__file__))", if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), - " imp.load_dynamic(__name__,__file__)", + " ExtensionFileLoader(__name__,", + " __file__).exec_module()", " finally:", if_dl(" sys.setdlopenflags(old_flags)"), " os.chdir(old_dir)", -- cgit v1.2.1 From 71d869a55cde5711800918cb0dfc17f987012b66 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Fri, 26 Jun 2020 01:17:57 +0800 Subject: bpo-40275: Use new test.support helper submodules in tests (GH-21151) Use new test.support helper submodules in tests: * distutils tests * test_buffer * test_compile * test_filecmp * test_fileinput * test_readline * test_smtpnet * test_structmembers * test_tools --- tests/test_archive_util.py | 4 +++- tests/test_bdist_msi.py | 3 ++- tests/test_bdist_wininst.py | 3 ++- tests/test_core.py | 10 +++++----- tests/test_dist.py | 3 ++- tests/test_extension.py | 3 ++- tests/test_file_util.py | 4 +++- tests/test_filelist.py | 3 ++- tests/test_register.py | 3 ++- tests/test_sdist.py | 3 ++- tests/test_sysconfig.py | 5 ++++- tests/test_unixccompiler.py | 3 ++- 12 files changed, 31 insertions(+), 16 deletions(-) diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index e9aad0e4..edcec251 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -13,7 +13,9 @@ from distutils.archive_util import (check_archive_formats, make_tarball, ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support -from test.support import check_warnings, run_unittest, patch, change_cwd +from test.support import run_unittest, patch +from test.support.os_helper import change_cwd +from test.support.warnings_helper import check_warnings try: import grp diff --git a/tests/test_bdist_msi.py b/tests/test_bdist_msi.py index 418e60ec..a61266a1 100644 --- a/tests/test_bdist_msi.py +++ b/tests/test_bdist_msi.py @@ -1,7 +1,8 @@ """Tests for distutils.command.bdist_msi.""" import sys import unittest -from test.support import run_unittest, check_warnings +from test.support import run_unittest +from test.support.warnings_helper import check_warnings from distutils.tests import support diff --git a/tests/test_bdist_wininst.py b/tests/test_bdist_wininst.py index 5c3d025d..c338069a 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -2,7 +2,8 @@ import sys import platform import unittest -from test.support import run_unittest, check_warnings +from test.support import run_unittest +from test.support.warnings_helper import check_warnings from distutils.command.bdist_wininst import bdist_wininst from distutils.tests import support diff --git a/tests/test_core.py b/tests/test_core.py index 27ce7324..4e6694a3 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5,8 +5,8 @@ import distutils.core import os import shutil import sys -import test.support from test.support import captured_stdout, run_unittest +from test.support import os_helper import unittest from distutils.tests import support from distutils import log @@ -62,13 +62,13 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): super(CoreTestCase, self).tearDown() def cleanup_testfn(self): - path = test.support.TESTFN + path = os_helper.TESTFN if os.path.isfile(path): os.remove(path) elif os.path.isdir(path): shutil.rmtree(path) - def write_setup(self, text, path=test.support.TESTFN): + def write_setup(self, text, path=os_helper.TESTFN): f = open(path, "w") try: f.write(text) @@ -105,8 +105,8 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase): cwd = os.getcwd() # Create a directory and write the setup.py file there: - os.mkdir(test.support.TESTFN) - setup_py = os.path.join(test.support.TESTFN, "setup.py") + os.mkdir(os_helper.TESTFN) + setup_py = os.path.join(os_helper.TESTFN, "setup.py") distutils.core.run_setup( self.write_setup(setup_prints_cwd, path=setup_py)) diff --git a/tests/test_dist.py b/tests/test_dist.py index 60956dad..f8a9e86b 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -12,8 +12,9 @@ from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command from test.support import ( - TESTFN, captured_stdout, captured_stderr, run_unittest + captured_stdout, captured_stderr, run_unittest ) +from test.support.os_helper import TESTFN from distutils.tests import support from distutils import log diff --git a/tests/test_extension.py b/tests/test_extension.py index e35f2738..81fad02d 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -3,7 +3,8 @@ import unittest import os import warnings -from test.support import check_warnings, run_unittest +from test.support import run_unittest +from test.support.warnings_helper import check_warnings from distutils.extension import read_setup_file, Extension class ExtensionTestCase(unittest.TestCase): diff --git a/tests/test_file_util.py b/tests/test_file_util.py index a4e2d025..c7783b85 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -8,7 +8,9 @@ from distutils.file_util import move_file, copy_file from distutils import log from distutils.tests import support from distutils.errors import DistutilsFileError -from test.support import run_unittest, unlink +from test.support import run_unittest +from test.support.os_helper import unlink + class FileUtilTestCase(support.TempdirManager, unittest.TestCase): diff --git a/tests/test_filelist.py b/tests/test_filelist.py index c71342d0..2c26c226 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -9,6 +9,7 @@ from distutils.filelist import glob_to_re, translate_pattern, FileList from distutils import filelist import test.support +from test.support import os_helper from test.support import captured_stdout, run_unittest from distutils.tests import support @@ -295,7 +296,7 @@ class FileListTestCase(support.LoggingSilencer, class FindAllTestCase(unittest.TestCase): - @test.support.skip_unless_symlink + @os_helper.skip_unless_symlink def test_missing_symlink(self): with test.support.temp_cwd(): os.symlink('foo', 'bar') diff --git a/tests/test_register.py b/tests/test_register.py index e68b0af3..bba48633 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -5,7 +5,8 @@ import getpass import urllib import warnings -from test.support import check_warnings, run_unittest +from test.support import run_unittest +from test.support.warnings_helper import check_warnings from distutils.command import register as register_module from distutils.command.register import register diff --git a/tests/test_sdist.py b/tests/test_sdist.py index 23db1269..752e9db5 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -6,7 +6,8 @@ import warnings import zipfile from os.path import join from textwrap import dedent -from test.support import captured_stdout, check_warnings, run_unittest +from test.support import captured_stdout, run_unittest +from test.support.warnings_helper import check_warnings try: import zlib diff --git a/tests/test_sysconfig.py b/tests/test_sysconfig.py index 236755d0..59676b0e 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -10,7 +10,10 @@ import unittest from distutils import sysconfig from distutils.ccompiler import get_default_compiler from distutils.tests import support -from test.support import TESTFN, run_unittest, check_warnings, swap_item +from test.support import run_unittest, swap_item +from test.support.os_helper import TESTFN +from test.support.warnings_helper import check_warnings + class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): diff --git a/tests/test_unixccompiler.py b/tests/test_unixccompiler.py index eef702cf..eefe4ba4 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -1,7 +1,8 @@ """Tests for distutils.unixccompiler.""" import sys import unittest -from test.support import EnvironmentVarGuard, run_unittest +from test.support import run_unittest +from test.support.os_helper import EnvironmentVarGuard from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler -- cgit v1.2.1 From a9eb9e73def8ca6c469e59f1b008746e368ad4c1 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 16 Jun 2020 13:31:12 +0300 Subject: Fix exception causes all over the codebase --- changelog.d/2199.misc.rst | 1 + pkg_resources/__init__.py | 18 +++++++++--------- setuptools/archive_util.py | 4 ++-- setuptools/command/easy_install.py | 14 +++++++++----- setuptools/command/egg_info.py | 4 ++-- setuptools/command/rotate.py | 4 ++-- setuptools/config.py | 5 +++-- setuptools/dist.py | 38 +++++++++++++++++++++----------------- setuptools/installer.py | 2 +- setuptools/msvc.py | 2 +- 10 files changed, 51 insertions(+), 41 deletions(-) create mode 100644 changelog.d/2199.misc.rst diff --git a/changelog.d/2199.misc.rst b/changelog.d/2199.misc.rst new file mode 100644 index 00000000..f795256b --- /dev/null +++ b/changelog.d/2199.misc.rst @@ -0,0 +1 @@ +Fix exception causes all over the codebase by using ``raise new_exception from old_exception`` \ No newline at end of file diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 2c589d55..c40f184a 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1378,7 +1378,7 @@ def evaluate_marker(text, extra=None): marker = packaging.markers.Marker(text) return marker.evaluate() except packaging.markers.InvalidMarker as e: - raise SyntaxError(e) + raise SyntaxError(e) from e class NullProvider: @@ -2287,8 +2287,8 @@ def declare_namespace(packageName): __import__(parent) try: path = sys.modules[parent].__path__ - except AttributeError: - raise TypeError("Not a package:", parent) + except AttributeError as e: + raise TypeError("Not a package:", parent) from e # Track what packages are namespaces, so when new path items are added, # they can be updated @@ -2468,7 +2468,7 @@ class EntryPoint: try: return functools.reduce(getattr, self.attrs, module) except AttributeError as exc: - raise ImportError(str(exc)) + raise ImportError(str(exc)) from exc def require(self, env=None, installer=None): if self.extras and not self.dist: @@ -2688,14 +2688,14 @@ class Distribution: def version(self): try: return self._version - except AttributeError: + except AttributeError as e: version = self._get_version() if version is None: path = self._get_metadata_path_for_display(self.PKG_INFO) msg = ( "Missing 'Version:' header and/or {} file at path: {}" ).format(self.PKG_INFO, path) - raise ValueError(msg, self) + raise ValueError(msg, self) from e return version @@ -2748,10 +2748,10 @@ class Distribution: for ext in extras: try: deps.extend(dm[safe_extra(ext)]) - except KeyError: + except KeyError as e: raise UnknownExtra( "%s has no such extra feature %r" % (self, ext) - ) + ) from e return deps def _get_metadata_path_for_display(self, name): @@ -3109,7 +3109,7 @@ class Requirement(packaging.requirements.Requirement): try: super(Requirement, self).__init__(requirement_string) except packaging.requirements.InvalidRequirement as e: - raise RequirementParseError(str(e)) + raise RequirementParseError(str(e)) from e self.unsafe_name = self.name project_name = safe_name(self.name) self.project_name, self.key = project_name, project_name.lower() diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index 64528ca7..0ce190b8 100644 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -134,10 +134,10 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): """ try: tarobj = tarfile.open(filename) - except tarfile.TarError: + except tarfile.TarError as e: raise UnrecognizedFormat( "%s is not a compressed or uncompressed tar file" % (filename,) - ) + ) from e with contextlib.closing(tarobj): # don't do any chowning! tarobj.chown = lambda *args: None diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 27b4558b..8890ec88 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -356,8 +356,10 @@ class easy_install(Command): self.optimize = int(self.optimize) if not (0 <= self.optimize <= 2): raise ValueError - except ValueError: - raise DistutilsOptionError("--optimize must be 0, 1, or 2") + except ValueError as e: + raise DistutilsOptionError( + "--optimize must be 0, 1, or 2" + ) from e if self.editable and not self.build_directory: raise DistutilsArgError( @@ -765,9 +767,9 @@ class easy_install(Command): [requirement], self.local_index, self.easy_install ) except DistributionNotFound as e: - raise DistutilsError(str(e)) + raise DistutilsError(str(e)) from e except VersionConflict as e: - raise DistutilsError(e.report()) + raise DistutilsError(e.report()) from e if self.always_copy or self.always_copy_from: # Force all the relevant distros to be copied or activated for dist in distros: @@ -1156,7 +1158,9 @@ class easy_install(Command): try: run_setup(setup_script, args) except SystemExit as v: - raise DistutilsError("Setup script exited with %s" % (v.args[0],)) + raise DistutilsError( + "Setup script exited with %s" % (v.args[0],) + ) from v def build_and_install(self, setup_script, setup_base): args = ['bdist_egg', '--dist-dir'] diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 7fa89541..0855207c 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -208,11 +208,11 @@ class egg_info(InfoCommon, Command): list( parse_requirements(spec % (self.egg_name, self.egg_version)) ) - except ValueError: + except ValueError as e: raise distutils.errors.DistutilsOptionError( "Invalid distribution name or version syntax: %s-%s" % (self.egg_name, self.egg_version) - ) + ) from e if self.egg_base is None: dirs = self.distribution.package_dir diff --git a/setuptools/command/rotate.py b/setuptools/command/rotate.py index b89353f5..e398834f 100644 --- a/setuptools/command/rotate.py +++ b/setuptools/command/rotate.py @@ -36,8 +36,8 @@ class rotate(Command): raise DistutilsOptionError("Must specify number of files to keep") try: self.keep = int(self.keep) - except ValueError: - raise DistutilsOptionError("--keep must be an integer") + except ValueError as e: + raise DistutilsOptionError("--keep must be an integer") from e if isinstance(self.match, six.string_types): self.match = [ convert_path(p.strip()) for p in self.match.split(',') diff --git a/setuptools/config.py b/setuptools/config.py index 45df2e3f..a8f8b6b0 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -42,9 +42,10 @@ class StaticModule: for target in statement.targets if isinstance(target, ast.Name) and target.id == attr ) - except Exception: + except Exception as e: raise AttributeError( - "{self.name} has no attribute {attr}".format(**locals())) + "{self.name} has no attribute {attr}".format(**locals()) + ) from e @contextlib.contextmanager diff --git a/setuptools/dist.py b/setuptools/dist.py index fe64afa9..e813b11c 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -204,11 +204,11 @@ def check_importable(dist, attr, value): try: ep = pkg_resources.EntryPoint.parse('x=' + value) assert not ep.extras - except (TypeError, ValueError, AttributeError, AssertionError): + except (TypeError, ValueError, AttributeError, AssertionError) as e: raise DistutilsSetupError( "%r must be importable 'module:attrs' string (got %r)" % (attr, value) - ) + ) from e def assert_string_list(dist, attr, value): @@ -219,10 +219,10 @@ def assert_string_list(dist, attr, value): assert isinstance(value, (list, tuple)) # verify that elements of value are strings assert ''.join(value) != value - except (TypeError, ValueError, AttributeError, AssertionError): + except (TypeError, ValueError, AttributeError, AssertionError) as e: raise DistutilsSetupError( "%r must be a list of strings (got %r)" % (attr, value) - ) + ) from e def check_nsp(dist, attr, value): @@ -247,12 +247,12 @@ def check_extras(dist, attr, value): """Verify that extras_require mapping is valid""" try: list(itertools.starmap(_check_extra, value.items())) - except (TypeError, ValueError, AttributeError): + except (TypeError, ValueError, AttributeError) as e: raise DistutilsSetupError( "'extras_require' must be a dictionary whose values are " "strings or lists of strings containing valid project/version " "requirement specifiers." - ) + ) from e def _check_extra(extra, reqs): @@ -280,7 +280,9 @@ def check_requirements(dist, attr, value): "{attr!r} must be a string or list of strings " "containing valid project/version requirement specifiers; {error}" ) - raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + raise DistutilsSetupError( + tmpl.format(attr=attr, error=error) + ) from error def check_specifier(dist, attr, value): @@ -292,7 +294,9 @@ def check_specifier(dist, attr, value): "{attr!r} must be a string " "containing valid version specifiers; {error}" ) - raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + raise DistutilsSetupError( + tmpl.format(attr=attr, error=error) + ) from error def check_entry_points(dist, attr, value): @@ -300,7 +304,7 @@ def check_entry_points(dist, attr, value): try: pkg_resources.EntryPoint.parse_map(value) except ValueError as e: - raise DistutilsSetupError(e) + raise DistutilsSetupError(e) from e def check_test_suite(dist, attr, value): @@ -609,8 +613,8 @@ class Distribution(_Distribution): setattr(self, opt, strtobool(val)) else: setattr(self, opt, val) - except ValueError as msg: - raise DistutilsOptionError(msg) + except ValueError as e: + raise DistutilsOptionError(e) from e @staticmethod def _try_str(val): @@ -676,8 +680,8 @@ class Distribution(_Distribution): raise DistutilsOptionError( "error in %s: command '%s' has no such option '%s'" % (source, command_name, option)) - except ValueError as msg: - raise DistutilsOptionError(msg) + except ValueError as e: + raise DistutilsOptionError(e) from e def parse_config_files(self, filenames=None, ignore_option_errors=False): """Parses configuration files from various levels @@ -843,10 +847,10 @@ class Distribution(_Distribution): ) try: old = getattr(self, name) - except AttributeError: + except AttributeError as e: raise DistutilsSetupError( "%s: No such distribution setting" % name - ) + ) from e if old is not None and not isinstance(old, sequence): raise DistutilsSetupError( name + ": this setting cannot be changed via include/exclude" @@ -863,10 +867,10 @@ class Distribution(_Distribution): ) try: old = getattr(self, name) - except AttributeError: + except AttributeError as e: raise DistutilsSetupError( "%s: No such distribution setting" % name - ) + ) from e if old is None: setattr(self, name, value) elif not isinstance(old, sequence): diff --git a/setuptools/installer.py b/setuptools/installer.py index 1f183bd9..e5acec27 100644 --- a/setuptools/installer.py +++ b/setuptools/installer.py @@ -127,7 +127,7 @@ def fetch_build_egg(dist, req): try: subprocess.check_call(cmd) except subprocess.CalledProcessError as e: - raise DistutilsError(str(e)) + raise DistutilsError(str(e)) from e wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0]) dist_location = os.path.join(eggs_dir, wheel.egg_name()) wheel.install_as_egg(dist_location) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 213e39c9..72ba0d0c 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -277,7 +277,7 @@ def _msvc14_get_vc_env(plat_spec): except subprocess.CalledProcessError as exc: raise distutils.errors.DistutilsPlatformError( "Error executing {}".format(exc.cmd) - ) + ) from exc env = { key.lower(): value -- cgit v1.2.1 From 5f8eebf06792a32d955689daeb5679fc6d3d019e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Jun 2020 20:21:35 -0400 Subject: Add conftest so that tests can run under pytest --- conftest.py | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 conftest.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..f0ec98e7 --- /dev/null +++ b/conftest.py @@ -0,0 +1,5 @@ +import sys +import os + +sys.path.insert(0, os.getcwd()) +__import__('distutils') -- cgit v1.2.1 From b8abfd21934b8b109050696bd99c8fc823f901f4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Jun 2020 21:10:28 -0400 Subject: Use tox for tests --- conftest.py | 5 ----- tox.ini | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) delete mode 100644 conftest.py create mode 100644 tox.ini diff --git a/conftest.py b/conftest.py deleted file mode 100644 index f0ec98e7..00000000 --- a/conftest.py +++ /dev/null @@ -1,5 +0,0 @@ -import sys -import os - -sys.path.insert(0, os.getcwd()) -__import__('distutils') diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..1590e308 --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[testenv] +deps = + pytest +commands = + pytest {posargs} +setenv = + PYTHONPATH = {toxinidir} +skip_install = True -- cgit v1.2.1 From 73ac895667b5bc6e3f5d685e7bad3d4535f07a80 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Jun 2020 21:16:53 -0400 Subject: Fix test on Python 3.8 and earlier. --- distutils/tests/test_install.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distutils/tests/test_install.py b/distutils/tests/test_install.py index 51c80e04..eb684a09 100644 --- a/distutils/tests/test_install.py +++ b/distutils/tests/test_install.py @@ -58,7 +58,8 @@ class InstallTestCase(support.TempdirManager, libdir = os.path.join(destination, "lib", "python") check_path(cmd.install_lib, libdir) - platlibdir = os.path.join(destination, sys.platlibdir, "python") + _platlibdir = getattr(sys, "platlibdir", "lib") + platlibdir = os.path.join(destination, _platlibdir, "python") check_path(cmd.install_platlib, platlibdir) check_path(cmd.install_purelib, libdir) check_path(cmd.install_headers, -- cgit v1.2.1 From c202075affc8a4d03401a4877639a96907438d83 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Jun 2020 22:38:33 -0400 Subject: Mark test_venv to be skipped when running under a virtualenv as virtualenv monkey patches distutils. --- distutils/tests/test_dist.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/distutils/tests/test_dist.py b/distutils/tests/test_dist.py index 60956dad..d431085b 100644 --- a/distutils/tests/test_dist.py +++ b/distutils/tests/test_dist.py @@ -83,6 +83,10 @@ class DistributionTestCase(support.LoggingSilencer, self.assertIsInstance(cmd, test_dist) self.assertEqual(cmd.sample_option, "sometext") + @unittest.skipIf( + 'distutils' not in Distribution.parse_config_files.__module__, + 'Cannot test when virtualenv has monkey-patched Distribution.', + ) def test_venv_install_options(self): sys.argv.append("install") self.addCleanup(os.unlink, TESTFN) -- cgit v1.2.1 From 00ce1222d90ca28c6096d847c6ffe52e26bd5db2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 08:43:36 -0400 Subject: Restore Python 3.5 syntax compatibility in distutils.dist. --- distutils/dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/distutils/dist.py b/distutils/dist.py index 6cf0a0d6..37db4d6c 100644 --- a/distutils/dist.py +++ b/distutils/dist.py @@ -35,7 +35,8 @@ def _ensure_list(value, fieldname): elif not isinstance(value, list): # passing a tuple or an iterator perhaps, warn and convert typename = type(value).__name__ - msg = f"Warning: '{fieldname}' should be a list, got type '{typename}'" + msg = "Warning: '{fieldname}' should be a list, got type '{typename}'" + msg = msg.format(**locals()) log.log(log.WARN, msg) value = list(value) return value -- cgit v1.2.1 From 3a4c22103551a68630b2e4b38f91deb4c78eae99 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 09:34:34 -0400 Subject: Fallback to '_sysconfigdata' when platform-specific sysconfigdata is unavailable. --- distutils/sysconfig.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/distutils/sysconfig.py b/distutils/sysconfig.py index 2109f746..79391e86 100644 --- a/distutils/sysconfig.py +++ b/distutils/sysconfig.py @@ -444,7 +444,12 @@ def _init_posix(): platform=sys.platform, multiarch=getattr(sys.implementation, '_multiarch', ''), )) - _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + try: + _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + except ImportError: + # Python 3.5 and pypy 7.3.1 + _temp = __import__( + '_sysconfigdata', globals(), locals(), ['build_time_vars'], 0) build_time_vars = _temp.build_time_vars global _config_vars _config_vars = {} -- cgit v1.2.1 From 71d738a1f6eced80defe3587f6adbbe4ce1fb7d1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 Jul 2020 20:19:22 -0400 Subject: Restore Python 3.5 syntax compatibility in distutils.tests.test_build_ext --- distutils/tests/test_build_ext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distutils/tests/test_build_ext.py b/distutils/tests/test_build_ext.py index 5e47e077..1aec1537 100644 --- a/distutils/tests/test_build_ext.py +++ b/distutils/tests/test_build_ext.py @@ -82,7 +82,7 @@ class BuildExtTestCase(TempdirManager, else: ALREADY_TESTED = type(self).__name__ - code = textwrap.dedent(f""" + code = textwrap.dedent(""" tmp_dir = {self.tmp_dir!r} import sys @@ -108,7 +108,7 @@ class BuildExtTestCase(TempdirManager, unittest.main() - """) + """.format(**locals())) assert_python_ok('-c', code) def test_solaris_enable_shared(self): -- cgit v1.2.1 From 253d03cad23ed022d020ae635ce419255240feef Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 Jul 2020 20:42:12 -0400 Subject: Add compatibility module to fix failing tests on Python 3.5 due to missing functionality. --- distutils/tests/py35compat.py | 68 ++++++++++++++++++++++++++++++++++++++ distutils/tests/test_build_clib.py | 4 ++- distutils/tests/test_config_cmd.py | 4 ++- distutils/tests/test_spawn.py | 4 ++- 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 distutils/tests/py35compat.py diff --git a/distutils/tests/py35compat.py b/distutils/tests/py35compat.py new file mode 100644 index 00000000..3eb86b5f --- /dev/null +++ b/distutils/tests/py35compat.py @@ -0,0 +1,68 @@ +""" +Backward compatibility support for Python 3.5 +""" + +import sys +import test.support +import subprocess + + +# copied from Python 3.9 test.support module +def _missing_compiler_executable(cmd_names=[]): + """Check if the compiler components used to build the interpreter exist. + + Check for the existence of the compiler executables whose names are listed + in 'cmd_names' or all the compiler executables when 'cmd_names' is empty + and return the first missing executable or None when none is found + missing. + + """ + from distutils import ccompiler, sysconfig, spawn + compiler = ccompiler.new_compiler() + sysconfig.customize_compiler(compiler) + for name in compiler.executables: + if cmd_names and name not in cmd_names: + continue + cmd = getattr(compiler, name) + if cmd_names: + assert cmd is not None, \ + "the '%s' executable is not configured" % name + elif not cmd: + continue + if spawn.find_executable(cmd[0]) is None: + return cmd[0] + + +missing_compiler_executable = vars(test.support).setdefault( + 'missing_compiler_executable', + _missing_compiler_executable, +) + + +try: + from test.support import unix_shell +except ImportError: + # Adapted from Python 3.9 test.support module + is_android = hasattr(sys, 'getandroidapilevel') + unix_shell = ( + None if sys.platform == 'win32' else + '/system/bin/sh' if is_android else + '/bin/sh' + ) + + +# copied from Python 3.9 subprocess module +def _optim_args_from_interpreter_flags(): + """Return a list of command-line arguments reproducing the current + optimization settings in sys.flags.""" + args = [] + value = sys.flags.optimize + if value > 0: + args.append('-' + 'O' * value) + return args + + +vars(subprocess).setdefault( + '_optim_args_from_interpreter_flags', + _optim_args_from_interpreter_flags, +) diff --git a/distutils/tests/test_build_clib.py b/distutils/tests/test_build_clib.py index abd83137..259c4352 100644 --- a/distutils/tests/test_build_clib.py +++ b/distutils/tests/test_build_clib.py @@ -3,7 +3,9 @@ import unittest import os import sys -from test.support import run_unittest, missing_compiler_executable +from test.support import run_unittest + +from .py35compat import missing_compiler_executable from distutils.command.build_clib import build_clib from distutils.errors import DistutilsSetupError diff --git a/distutils/tests/test_config_cmd.py b/distutils/tests/test_config_cmd.py index 9aeab07b..4cd9a6b9 100644 --- a/distutils/tests/test_config_cmd.py +++ b/distutils/tests/test_config_cmd.py @@ -2,7 +2,9 @@ import unittest import os import sys -from test.support import run_unittest, missing_compiler_executable +from test.support import run_unittest + +from .py35compat import missing_compiler_executable from distutils.command.config import dump_file, config from distutils.tests import support diff --git a/distutils/tests/test_spawn.py b/distutils/tests/test_spawn.py index cf1faad5..919d0ad9 100644 --- a/distutils/tests/test_spawn.py +++ b/distutils/tests/test_spawn.py @@ -3,9 +3,11 @@ import os import stat import sys import unittest.mock -from test.support import run_unittest, unix_shell +from test.support import run_unittest from test import support as test_support +from .py35compat import unix_shell + from distutils.spawn import find_executable from distutils.spawn import spawn from distutils.errors import DistutilsExecError -- cgit v1.2.1 From a4eb1127303fbe514ab8f77d6c3896614de02c5d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 Jul 2020 20:49:30 -0400 Subject: Fix failing test in test_fileutil by adapting expectation based on Python version. --- distutils/tests/py35compat.py | 9 +++++++++ distutils/tests/test_filelist.py | 5 ++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/distutils/tests/py35compat.py b/distutils/tests/py35compat.py index 3eb86b5f..0c755261 100644 --- a/distutils/tests/py35compat.py +++ b/distutils/tests/py35compat.py @@ -66,3 +66,12 @@ vars(subprocess).setdefault( '_optim_args_from_interpreter_flags', _optim_args_from_interpreter_flags, ) + + +def adapt_glob(regex): + """ + Supply legacy expectation on Python 3.5 + """ + if sys.version_info > (3, 6): + return regex + return regex.replace('(?s:', '').replace(r')\Z', r'\Z(?ms)') diff --git a/distutils/tests/test_filelist.py b/distutils/tests/test_filelist.py index c71342d0..71fde2b7 100644 --- a/distutils/tests/test_filelist.py +++ b/distutils/tests/test_filelist.py @@ -12,6 +12,9 @@ import test.support from test.support import captured_stdout, run_unittest from distutils.tests import support +from .py35compat import adapt_glob + + MANIFEST_IN = """\ include ok include xo @@ -60,7 +63,7 @@ class FileListTestCase(support.LoggingSilencer, ('foo????', r'(?s:foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s])\Z'), (r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z')): regex = regex % {'sep': sep} - self.assertEqual(glob_to_re(glob), regex) + self.assertEqual(glob_to_re(glob), adapt_glob(regex)) def test_process_template_line(self): # testing all MANIFEST.in template patterns -- cgit v1.2.1 From 4f1d15983b650c20dc6de48f6675c9ce84c0c3a9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 Jul 2020 21:09:47 -0400 Subject: Acknowledge and ignore warning about TestDistribution (it's a "test" distribution, not a "test of distributions"). --- pytest.ini | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 pytest.ini diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..3e01b439 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +filterwarnings= + # acknowledge that TestDistribution isn't a test + ignore:cannot collect test class 'TestDistribution' -- cgit v1.2.1 From 2f1fae1bfceb88cf812a45c44cd18ce604b69c69 Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 29 Jun 2020 00:25:26 +0300 Subject: add pypy schemas --- distutils/command/install.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/distutils/command/install.py b/distutils/command/install.py index 21f8c27c..13feeb89 100644 --- a/distutils/command/install.py +++ b/distutils/command/install.py @@ -43,6 +43,20 @@ INSTALL_SCHEMES = { 'data' : '$base', }, 'nt': WINDOWS_SCHEME, + 'pypy': { + 'purelib': '$base/site-packages', + 'platlib': '$base/site-packages', + 'headers': '$base/include/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'pypy_nt': { + 'purelib': '$base/site-packages', + 'platlib': '$base/site-packages', + 'headers': '$base/include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + }, } # user site schemes @@ -455,6 +469,12 @@ class install(Command): def select_scheme(self, name): """Sets the install directories by applying the install schemes.""" # it's the caller's problem if they supply a bad name! + if (hasattr(sys, 'pypy_version_info') and + not name.endswith(('_user', '_home'))): + if os.name == 'nt': + name = 'pypy_nt' + else: + name = 'pypy' scheme = INSTALL_SCHEMES[name] for key in SCHEME_KEYS: attrname = 'install_' + key -- cgit v1.2.1 From 12f74fb9280897a07199db0df76103487263136b Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 29 Jun 2020 20:00:07 +0300 Subject: do the minimum to fix sysconfig.py for PyPy, more will probably be needed --- distutils/sysconfig.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/distutils/sysconfig.py b/distutils/sysconfig.py index 79391e86..255243ad 100644 --- a/distutils/sysconfig.py +++ b/distutils/sysconfig.py @@ -16,6 +16,8 @@ import sys from .errors import DistutilsPlatformError +IS_PYPY = '__pypy__' in sys.builtin_module_names + # These are needed in a couple of spots, so just compute them once. PREFIX = os.path.normpath(sys.prefix) EXEC_PREFIX = os.path.normpath(sys.exec_prefix) @@ -97,7 +99,9 @@ def get_python_inc(plat_specific=0, prefix=None): """ if prefix is None: prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX - if os.name == "posix": + if IS_PYPY: + return os.path.join(prefix, 'include') + elif os.name == "posix": if python_build: # Assume the executable is in the build directory. The # pyconfig.h file should be in the same directory. Since -- cgit v1.2.1 From 50d17611942813f840a69c4838173b2c4dc27f24 Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 29 Jun 2020 20:23:28 +0300 Subject: no Makefile with PyPy and has own layout for python stdlib, site-packages --- distutils/sysconfig.py | 79 ++++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/distutils/sysconfig.py b/distutils/sysconfig.py index 255243ad..879b6981 100644 --- a/distutils/sysconfig.py +++ b/distutils/sysconfig.py @@ -142,6 +142,14 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): If 'prefix' is supplied, use it instead of sys.base_prefix or sys.base_exec_prefix -- i.e., ignore 'plat_specific'. """ + if IS_PYPY: + # PyPy-specific schema + if prefix is None: + prefix = PREFIX + if standard_lib: + return os.path.join(prefix, "lib-python", sys.version[0]) + return os.path.join(prefix, 'site-packages') + if prefix is None: if standard_lib: prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX @@ -503,41 +511,42 @@ def get_config_vars(*args): _config_vars['prefix'] = PREFIX _config_vars['exec_prefix'] = EXEC_PREFIX - # For backward compatibility, see issue19555 - SO = _config_vars.get('EXT_SUFFIX') - if SO is not None: - _config_vars['SO'] = SO - - # Always convert srcdir to an absolute path - srcdir = _config_vars.get('srcdir', project_base) - if os.name == 'posix': - if python_build: - # If srcdir is a relative path (typically '.' or '..') - # then it should be interpreted relative to the directory - # containing Makefile. - base = os.path.dirname(get_makefile_filename()) - srcdir = os.path.join(base, srcdir) - else: - # srcdir is not meaningful since the installation is - # spread about the filesystem. We choose the - # directory containing the Makefile since we know it - # exists. - srcdir = os.path.dirname(get_makefile_filename()) - _config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir)) - - # Convert srcdir into an absolute path if it appears necessary. - # Normally it is relative to the build directory. However, during - # testing, for example, we might be running a non-installed python - # from a different directory. - if python_build and os.name == "posix": - base = project_base - if (not os.path.isabs(_config_vars['srcdir']) and - base != os.getcwd()): - # srcdir is relative and we are not in the same directory - # as the executable. Assume executable is in the build - # directory and make srcdir absolute. - srcdir = os.path.join(base, _config_vars['srcdir']) - _config_vars['srcdir'] = os.path.normpath(srcdir) + if not IS_PYPY: + # For backward compatibility, see issue19555 + SO = _config_vars.get('EXT_SUFFIX') + if SO is not None: + _config_vars['SO'] = SO + + # Always convert srcdir to an absolute path + srcdir = _config_vars.get('srcdir', project_base) + if os.name == 'posix': + if python_build: + # If srcdir is a relative path (typically '.' or '..') + # then it should be interpreted relative to the directory + # containing Makefile. + base = os.path.dirname(get_makefile_filename()) + srcdir = os.path.join(base, srcdir) + else: + # srcdir is not meaningful since the installation is + # spread about the filesystem. We choose the + # directory containing the Makefile since we know it + # exists. + srcdir = os.path.dirname(get_makefile_filename()) + _config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir)) + + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if python_build and os.name == "posix": + base = project_base + if (not os.path.isabs(_config_vars['srcdir']) and + base != os.getcwd()): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _config_vars['srcdir']) + _config_vars['srcdir'] = os.path.normpath(srcdir) # OS X platforms require special customization to handle # multi-architecture, multi-os-version installers -- cgit v1.2.1 From 89d64e81e194d5b8b8a500e837b9cea4f2e1063e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 Jul 2020 22:09:26 -0400 Subject: Skip test on PyPy where the functionality is disabled. --- distutils/tests/test_sysconfig.py | 1 + 1 file changed, 1 insertion(+) diff --git a/distutils/tests/test_sysconfig.py b/distutils/tests/test_sysconfig.py index 236755d0..d5076391 100644 --- a/distutils/tests/test_sysconfig.py +++ b/distutils/tests/test_sysconfig.py @@ -45,6 +45,7 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): self.assertIsInstance(cvars, dict) self.assertTrue(cvars) + @unittest.skip('sysconfig.IS_PYPY') def test_srcdir(self): # See Issues #15322, #15364. srcdir = sysconfig.get_config_var('srcdir') -- cgit v1.2.1 From c3a052aefbba0d5fda10790e676223c0dc12f0ed Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 2 Jul 2020 05:06:16 -0400 Subject: In test_unixcompiler.test_osx*, also patch sysconfig.get_config_vars following the patterns of prior implementations. --- distutils/tests/test_unixccompiler.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/distutils/tests/test_unixccompiler.py b/distutils/tests/test_unixccompiler.py index eef702cf..f2159662 100644 --- a/distutils/tests/test_unixccompiler.py +++ b/distutils/tests/test_unixccompiler.py @@ -11,6 +11,7 @@ class UnixCCompilerTestCase(unittest.TestCase): def setUp(self): self._backup_platform = sys.platform self._backup_get_config_var = sysconfig.get_config_var + self._backup_get_config_vars = sysconfig.get_config_vars class CompilerWrapper(UnixCCompiler): def rpath_foo(self): return self.runtime_library_dir_option('/foo') @@ -19,6 +20,7 @@ class UnixCCompilerTestCase(unittest.TestCase): def tearDown(self): sys.platform = self._backup_platform sysconfig.get_config_var = self._backup_get_config_var + sysconfig.get_config_vars = self._backup_get_config_vars @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_runtime_libdir_option(self): @@ -110,7 +112,13 @@ class UnixCCompilerTestCase(unittest.TestCase): if v == 'LDSHARED': return 'gcc-4.2 -bundle -undefined dynamic_lookup ' return 'gcc-4.2' + + def gcvs(*args, _orig=sysconfig.get_config_vars): + if args: + return list(map(sysconfig.get_config_var, args)) + return _orig() sysconfig.get_config_var = gcv + sysconfig.get_config_vars = gcvs with EnvironmentVarGuard() as env: env['CC'] = 'my_cc' del env['LDSHARED'] @@ -126,7 +134,13 @@ class UnixCCompilerTestCase(unittest.TestCase): if v == 'LDSHARED': return 'gcc-4.2 -bundle -undefined dynamic_lookup ' return 'gcc-4.2' + + def gcvs(*args, _orig=sysconfig.get_config_vars): + if args: + return list(map(sysconfig.get_config_var, args)) + return _orig() sysconfig.get_config_var = gcv + sysconfig.get_config_vars = gcvs with EnvironmentVarGuard() as env: env['CC'] = 'my_cc' env['LDSHARED'] = 'my_ld -bundle -dynamic' -- cgit v1.2.1 From 4bac4653d026922eefcedc40bdf90b2db2f90e75 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 2 Jul 2020 12:07:44 -0400 Subject: Move coverage configuration to .coveragerc. Saves characters and separates concerns. --- .coveragerc | 8 ++++++++ tox.ini | 9 +-------- 2 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..2f0e8714 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,8 @@ +[run] +source= + pkg_resources + setuptools +omit= + */_vendor/* + +[report] diff --git a/tox.ini b/tox.ini index d3df21bf..a1774006 100644 --- a/tox.ini +++ b/tox.ini @@ -23,7 +23,7 @@ setenv = # TODO: The passed environment variables came from copying other tox.ini files # These should probably be individually annotated to explain what needs them. passenv=APPDATA HOMEDRIVE HOMEPATH windir Program* CommonProgram* VS* APPVEYOR APPVEYOR_* CI CODECOV_* TRAVIS TRAVIS_* NETWORK_REQUIRED -commands=pytest --cov-config={toxinidir}/tox.ini --cov-report= {posargs} +commands=pytest {posargs} usedevelop=True extras = tests @@ -53,13 +53,6 @@ changedir = docs commands = python -m sphinx . {toxinidir}/build/html -[coverage:run] -source= - pkg_resources - setuptools -omit= - */_vendor/* - [testenv:finalize] skip_install = True deps = -- cgit v1.2.1 From c897b90cbcd2d5a907d3b677f7229c2be7c44528 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 2 Jul 2020 12:26:16 -0400 Subject: More directly disable coverage when running tests on pypy. --- .github/workflows/python-tests.yml | 2 -- .travis.yml | 25 +++++-------------------- pytest.ini | 2 +- tox.ini | 6 +++++- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index e3663cf0..5a598084 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -98,5 +98,3 @@ jobs: python -m tox --parallel auto - -- - --cov diff --git a/.travis.yml b/.travis.yml index f97abc51..e8bc7574 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ jobs: fast_finish: true include: - python: pypy3 - env: DISABLE_COVERAGE=1 # Don't run coverage on pypy (too slow). - python: 3.5 - python: 3.6 - python: 3.7 @@ -15,12 +14,12 @@ jobs: env: LANG=C - python: 3.8-dev - <<: *latest_py3 - env: TOXENV=docs DISABLE_COVERAGE=1 + env: TOXENV=docs allow_failures: # suppress failures due to pypa/setuptools#2000 - python: pypy3 - <<: *latest_py3 - env: TOXENV=docs DISABLE_COVERAGE=1 + env: TOXENV=docs cache: pip @@ -39,22 +38,8 @@ install: script: - export NETWORK_REQUIRED=1 - - | - ( # Run testsuite. - if [ -z "$DISABLE_COVERAGE" ] - then - tox -- --cov - else - tox - fi - ) + - tox after_success: - - | - ( # Upload coverage data. - if [ -z "$DISABLE_COVERAGE" ] - then - export TRAVIS_JOB_NAME="${TRAVIS_PYTHON_VERSION} (LANG=$LANG)" CODECOV_ENV=TRAVIS_JOB_NAME - tox -e coverage,codecov - fi - ) + - export TRAVIS_JOB_NAME="${TRAVIS_PYTHON_VERSION} (LANG=$LANG)" CODECOV_ENV=TRAVIS_JOB_NAME + - tox -e coverage,codecov diff --git a/pytest.ini b/pytest.ini index 479a2965..ddcad08b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts=--doctest-modules --flake8 --doctest-glob=pkg_resources/api_tests.txt -r sxX +addopts=--doctest-modules --flake8 --doctest-glob=pkg_resources/api_tests.txt --cov -r sxX norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern pkg_resources/tests/data tools .* setuptools/_vendor pkg_resources/_vendor doctest_optionflags=ELLIPSIS ALLOW_UNICODE filterwarnings = diff --git a/tox.ini b/tox.ini index a1774006..59213e88 100644 --- a/tox.ini +++ b/tox.ini @@ -23,12 +23,16 @@ setenv = # TODO: The passed environment variables came from copying other tox.ini files # These should probably be individually annotated to explain what needs them. passenv=APPDATA HOMEDRIVE HOMEPATH windir Program* CommonProgram* VS* APPVEYOR APPVEYOR_* CI CODECOV_* TRAVIS TRAVIS_* NETWORK_REQUIRED -commands=pytest {posargs} +commands = pytest {posargs} usedevelop=True extras = tests +[testenv:pypy{,3}] +commands = pytest --no-cov {posargs} + + [testenv:coverage] description=Combine coverage data and create report deps=coverage -- cgit v1.2.1 From d9998e6281cbf4bb90cfd8c90e7f34b4ea3a350d Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 29 Jun 2020 20:00:24 +0300 Subject: fix test for deprecation warning --- setuptools/tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index 3dc87ca3..2ef8521d 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -42,7 +42,7 @@ class TestBuildExt: res = cmd.get_ext_filename('spam.eggs') if six.PY2 or not get_abi3_suffix(): - assert res.endswith(get_config_var('SO')) + assert res.endswith(get_config_var('EXT_SUFFIX')) elif sys.platform == 'win32': assert res.endswith('eggs.pyd') else: -- cgit v1.2.1 From 9013321c25606a5cd63271cd029c2539490b16d3 Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 29 Jun 2020 20:37:43 +0300 Subject: catch some resource leaks --- pkg_resources/__init__.py | 3 ++- setuptools/launch.py | 3 ++- setuptools/msvc.py | 36 +++++++++++++++++++++--------------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 2e7d5059..61f24461 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1459,7 +1459,8 @@ class NullProvider: script_filename = self._fn(self.egg_info, script) namespace['__file__'] = script_filename if os.path.exists(script_filename): - source = open(script_filename).read() + with open(script_filename) as fid: + source = fid.read() code = compile(source, script_filename, 'exec') exec(code, namespace, namespace) else: diff --git a/setuptools/launch.py b/setuptools/launch.py index 308283ea..0208fdf3 100644 --- a/setuptools/launch.py +++ b/setuptools/launch.py @@ -25,7 +25,8 @@ def run(): sys.argv[:] = sys.argv[1:] open_ = getattr(tokenize, 'open', open) - script = open_(script_name).read() + with open_(script_name) as fid: + script = fid.read() norm_script = script.replace('\\r\\n', '\\n') code = compile(norm_script, script_name, 'exec') exec(code, namespace) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 213e39c9..09f8565e 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -643,8 +643,10 @@ class RegistryInfo: """ key_read = winreg.KEY_READ openkey = winreg.OpenKey + closekey = winreg.CloseKey ms = self.microsoft for hkey in self.HKEYS: + bkey = None try: bkey = openkey(hkey, ms(key), 0, key_read) except (OSError, IOError): @@ -659,6 +661,9 @@ class RegistryInfo: return winreg.QueryValueEx(bkey, name)[0] except (OSError, IOError): pass + finally: + if bkey: + closekey(bkey) class SystemInfo: @@ -726,21 +731,22 @@ class SystemInfo: bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) except (OSError, IOError): continue - subkeys, values, _ = winreg.QueryInfoKey(bkey) - for i in range(values): - try: - ver = float(winreg.EnumValue(bkey, i)[0]) - if ver not in vs_vers: - vs_vers.append(ver) - except ValueError: - pass - for i in range(subkeys): - try: - ver = float(winreg.EnumKey(bkey, i)) - if ver not in vs_vers: - vs_vers.append(ver) - except ValueError: - pass + with bkey: + subkeys, values, _ = winreg.QueryInfoKey(bkey) + for i in range(values): + try: + ver = float(winreg.EnumValue(bkey, i)[0]) + if ver not in vs_vers: + vs_vers.append(ver) + except ValueError: + pass + for i in range(subkeys): + try: + ver = float(winreg.EnumKey(bkey, i)) + if ver not in vs_vers: + vs_vers.append(ver) + except ValueError: + pass return sorted(vs_vers) def find_programdata_vs_vers(self): -- cgit v1.2.1 From bb9fb1fcfe37c1ef1e29e1e6d1fc4e483c743380 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 03:53:46 -0400 Subject: Move distutils into a submodule of setuptools. --- conftest.py | 2 +- distutils/README | 11 - distutils/__init__.py | 15 - distutils/_msvccompiler.py | 539 --------- distutils/archive_util.py | 256 ---- distutils/bcppcompiler.py | 393 ------ distutils/ccompiler.py | 1116 ----------------- distutils/cmd.py | 403 ------- distutils/command/__init__.py | 31 - distutils/command/bdist.py | 143 --- distutils/command/bdist_dumb.py | 123 -- distutils/command/bdist_msi.py | 749 ------------ distutils/command/bdist_rpm.py | 579 --------- distutils/command/bdist_wininst.py | 377 ------ distutils/command/build.py | 157 --- distutils/command/build_clib.py | 209 ---- distutils/command/build_ext.py | 754 ------------ distutils/command/build_py.py | 416 ------- distutils/command/build_scripts.py | 160 --- distutils/command/check.py | 148 --- distutils/command/clean.py | 76 -- distutils/command/command_template | 33 - distutils/command/config.py | 344 ------ distutils/command/install.py | 677 ----------- distutils/command/install_data.py | 79 -- distutils/command/install_egg_info.py | 77 -- distutils/command/install_headers.py | 47 - distutils/command/install_lib.py | 217 ---- distutils/command/install_scripts.py | 60 - distutils/command/register.py | 304 ----- distutils/command/sdist.py | 494 -------- distutils/command/upload.py | 214 ---- distutils/command/wininst-10.0-amd64.exe | Bin 222208 -> 0 bytes distutils/command/wininst-10.0.exe | Bin 190976 -> 0 bytes distutils/command/wininst-14.0-amd64.exe | Bin 587776 -> 0 bytes distutils/command/wininst-14.0.exe | Bin 458240 -> 0 bytes distutils/command/wininst-6.0.exe | Bin 61440 -> 0 bytes distutils/command/wininst-7.1.exe | Bin 65536 -> 0 bytes distutils/command/wininst-8.0.exe | Bin 61440 -> 0 bytes distutils/command/wininst-9.0-amd64.exe | Bin 224256 -> 0 bytes distutils/command/wininst-9.0.exe | Bin 196096 -> 0 bytes distutils/config.py | 130 -- distutils/core.py | 234 ---- distutils/cygwinccompiler.py | 403 ------- distutils/debug.py | 5 - distutils/dep_util.py | 92 -- distutils/dir_util.py | 210 ---- distutils/dist.py | 1257 -------------------- distutils/errors.py | 97 -- distutils/extension.py | 240 ---- distutils/fancy_getopt.py | 457 ------- distutils/file_util.py | 238 ---- distutils/filelist.py | 327 ----- distutils/log.py | 77 -- distutils/msvc9compiler.py | 788 ------------ distutils/msvccompiler.py | 643 ---------- distutils/spawn.py | 119 -- distutils/sysconfig.py | 573 --------- distutils/tests/Setup.sample | 67 -- distutils/tests/__init__.py | 42 - distutils/tests/includetest.rst | 1 - distutils/tests/py35compat.py | 77 -- distutils/tests/support.py | 209 ---- distutils/tests/test_archive_util.py | 394 ------ distutils/tests/test_bdist.py | 57 - distutils/tests/test_bdist_dumb.py | 97 -- distutils/tests/test_bdist_msi.py | 26 - distutils/tests/test_bdist_rpm.py | 135 --- distutils/tests/test_bdist_wininst.py | 38 - distutils/tests/test_build.py | 56 - distutils/tests/test_build_clib.py | 136 --- distutils/tests/test_build_ext.py | 545 --------- distutils/tests/test_build_py.py | 179 --- distutils/tests/test_build_scripts.py | 112 -- distutils/tests/test_check.py | 163 --- distutils/tests/test_clean.py | 49 - distutils/tests/test_cmd.py | 126 -- distutils/tests/test_config.py | 141 --- distutils/tests/test_config_cmd.py | 98 -- distutils/tests/test_core.py | 140 --- distutils/tests/test_cygwinccompiler.py | 154 --- distutils/tests/test_dep_util.py | 80 -- distutils/tests/test_dir_util.py | 139 --- distutils/tests/test_dist.py | 532 --------- distutils/tests/test_extension.py | 69 -- distutils/tests/test_file_util.py | 122 -- distutils/tests/test_filelist.py | 343 ------ distutils/tests/test_install.py | 250 ---- distutils/tests/test_install_data.py | 75 -- distutils/tests/test_install_headers.py | 39 - distutils/tests/test_install_lib.py | 115 -- distutils/tests/test_install_scripts.py | 82 -- distutils/tests/test_log.py | 46 - distutils/tests/test_msvc9compiler.py | 184 --- distutils/tests/test_msvccompiler.py | 81 -- distutils/tests/test_register.py | 323 ----- distutils/tests/test_sdist.py | 492 -------- distutils/tests/test_spawn.py | 134 --- distutils/tests/test_sysconfig.py | 275 ----- distutils/tests/test_text_file.py | 107 -- distutils/tests/test_unixccompiler.py | 155 --- distutils/tests/test_upload.py | 223 ---- distutils/tests/test_util.py | 309 ----- distutils/tests/test_version.py | 87 -- distutils/tests/test_versionpredicate.py | 13 - distutils/text_file.py | 286 ----- distutils/unixccompiler.py | 328 ----- distutils/util.py | 559 --------- distutils/version.py | 347 ------ distutils/versionpredicate.py | 166 --- setuptools/_distutils/README | 11 + setuptools/_distutils/__init__.py | 15 + setuptools/_distutils/_msvccompiler.py | 539 +++++++++ setuptools/_distutils/archive_util.py | 256 ++++ setuptools/_distutils/bcppcompiler.py | 393 ++++++ setuptools/_distutils/ccompiler.py | 1116 +++++++++++++++++ setuptools/_distutils/cmd.py | 403 +++++++ setuptools/_distutils/command/__init__.py | 31 + setuptools/_distutils/command/bdist.py | 143 +++ setuptools/_distutils/command/bdist_dumb.py | 123 ++ setuptools/_distutils/command/bdist_msi.py | 749 ++++++++++++ setuptools/_distutils/command/bdist_rpm.py | 579 +++++++++ setuptools/_distutils/command/bdist_wininst.py | 377 ++++++ setuptools/_distutils/command/build.py | 157 +++ setuptools/_distutils/command/build_clib.py | 209 ++++ setuptools/_distutils/command/build_ext.py | 754 ++++++++++++ setuptools/_distutils/command/build_py.py | 416 +++++++ setuptools/_distutils/command/build_scripts.py | 160 +++ setuptools/_distutils/command/check.py | 148 +++ setuptools/_distutils/command/clean.py | 76 ++ setuptools/_distutils/command/command_template | 33 + setuptools/_distutils/command/config.py | 344 ++++++ setuptools/_distutils/command/install.py | 677 +++++++++++ setuptools/_distutils/command/install_data.py | 79 ++ setuptools/_distutils/command/install_egg_info.py | 77 ++ setuptools/_distutils/command/install_headers.py | 47 + setuptools/_distutils/command/install_lib.py | 217 ++++ setuptools/_distutils/command/install_scripts.py | 60 + setuptools/_distutils/command/register.py | 304 +++++ setuptools/_distutils/command/sdist.py | 494 ++++++++ setuptools/_distutils/command/upload.py | 214 ++++ .../_distutils/command/wininst-10.0-amd64.exe | Bin 0 -> 222208 bytes setuptools/_distutils/command/wininst-10.0.exe | Bin 0 -> 190976 bytes .../_distutils/command/wininst-14.0-amd64.exe | Bin 0 -> 587776 bytes setuptools/_distutils/command/wininst-14.0.exe | Bin 0 -> 458240 bytes setuptools/_distutils/command/wininst-6.0.exe | Bin 0 -> 61440 bytes setuptools/_distutils/command/wininst-7.1.exe | Bin 0 -> 65536 bytes setuptools/_distutils/command/wininst-8.0.exe | Bin 0 -> 61440 bytes .../_distutils/command/wininst-9.0-amd64.exe | Bin 0 -> 224256 bytes setuptools/_distutils/command/wininst-9.0.exe | Bin 0 -> 196096 bytes setuptools/_distutils/config.py | 130 ++ setuptools/_distutils/core.py | 234 ++++ setuptools/_distutils/cygwinccompiler.py | 403 +++++++ setuptools/_distutils/debug.py | 5 + setuptools/_distutils/dep_util.py | 92 ++ setuptools/_distutils/dir_util.py | 210 ++++ setuptools/_distutils/dist.py | 1257 ++++++++++++++++++++ setuptools/_distutils/errors.py | 97 ++ setuptools/_distutils/extension.py | 240 ++++ setuptools/_distutils/fancy_getopt.py | 457 +++++++ setuptools/_distutils/file_util.py | 238 ++++ setuptools/_distutils/filelist.py | 327 +++++ setuptools/_distutils/log.py | 77 ++ setuptools/_distutils/msvc9compiler.py | 788 ++++++++++++ setuptools/_distutils/msvccompiler.py | 643 ++++++++++ setuptools/_distutils/spawn.py | 119 ++ setuptools/_distutils/sysconfig.py | 573 +++++++++ setuptools/_distutils/tests/Setup.sample | 67 ++ setuptools/_distutils/tests/__init__.py | 42 + setuptools/_distutils/tests/includetest.rst | 1 + setuptools/_distutils/tests/py35compat.py | 77 ++ setuptools/_distutils/tests/support.py | 209 ++++ setuptools/_distutils/tests/test_archive_util.py | 394 ++++++ setuptools/_distutils/tests/test_bdist.py | 57 + setuptools/_distutils/tests/test_bdist_dumb.py | 97 ++ setuptools/_distutils/tests/test_bdist_msi.py | 26 + setuptools/_distutils/tests/test_bdist_rpm.py | 135 +++ setuptools/_distutils/tests/test_bdist_wininst.py | 38 + setuptools/_distutils/tests/test_build.py | 56 + setuptools/_distutils/tests/test_build_clib.py | 136 +++ setuptools/_distutils/tests/test_build_ext.py | 545 +++++++++ setuptools/_distutils/tests/test_build_py.py | 179 +++ setuptools/_distutils/tests/test_build_scripts.py | 112 ++ setuptools/_distutils/tests/test_check.py | 163 +++ setuptools/_distutils/tests/test_clean.py | 49 + setuptools/_distutils/tests/test_cmd.py | 126 ++ setuptools/_distutils/tests/test_config.py | 141 +++ setuptools/_distutils/tests/test_config_cmd.py | 98 ++ setuptools/_distutils/tests/test_core.py | 140 +++ .../_distutils/tests/test_cygwinccompiler.py | 154 +++ setuptools/_distutils/tests/test_dep_util.py | 80 ++ setuptools/_distutils/tests/test_dir_util.py | 139 +++ setuptools/_distutils/tests/test_dist.py | 532 +++++++++ setuptools/_distutils/tests/test_extension.py | 69 ++ setuptools/_distutils/tests/test_file_util.py | 122 ++ setuptools/_distutils/tests/test_filelist.py | 343 ++++++ setuptools/_distutils/tests/test_install.py | 250 ++++ setuptools/_distutils/tests/test_install_data.py | 75 ++ .../_distutils/tests/test_install_headers.py | 39 + setuptools/_distutils/tests/test_install_lib.py | 115 ++ .../_distutils/tests/test_install_scripts.py | 82 ++ setuptools/_distutils/tests/test_log.py | 46 + setuptools/_distutils/tests/test_msvc9compiler.py | 184 +++ setuptools/_distutils/tests/test_msvccompiler.py | 81 ++ setuptools/_distutils/tests/test_register.py | 323 +++++ setuptools/_distutils/tests/test_sdist.py | 492 ++++++++ setuptools/_distutils/tests/test_spawn.py | 134 +++ setuptools/_distutils/tests/test_sysconfig.py | 275 +++++ setuptools/_distutils/tests/test_text_file.py | 107 ++ setuptools/_distutils/tests/test_unixccompiler.py | 155 +++ setuptools/_distutils/tests/test_upload.py | 223 ++++ setuptools/_distutils/tests/test_util.py | 309 +++++ setuptools/_distutils/tests/test_version.py | 87 ++ .../_distutils/tests/test_versionpredicate.py | 13 + setuptools/_distutils/text_file.py | 286 +++++ setuptools/_distutils/unixccompiler.py | 328 +++++ setuptools/_distutils/util.py | 559 +++++++++ setuptools/_distutils/version.py | 347 ++++++ setuptools/_distutils/versionpredicate.py | 166 +++ setuptools/distutils_patch.py | 21 +- 220 files changed, 24101 insertions(+), 24110 deletions(-) delete mode 100644 distutils/README delete mode 100644 distutils/__init__.py delete mode 100644 distutils/_msvccompiler.py delete mode 100644 distutils/archive_util.py delete mode 100644 distutils/bcppcompiler.py delete mode 100644 distutils/ccompiler.py delete mode 100644 distutils/cmd.py delete mode 100644 distutils/command/__init__.py delete mode 100644 distutils/command/bdist.py delete mode 100644 distutils/command/bdist_dumb.py delete mode 100644 distutils/command/bdist_msi.py delete mode 100644 distutils/command/bdist_rpm.py delete mode 100644 distutils/command/bdist_wininst.py delete mode 100644 distutils/command/build.py delete mode 100644 distutils/command/build_clib.py delete mode 100644 distutils/command/build_ext.py delete mode 100644 distutils/command/build_py.py delete mode 100644 distutils/command/build_scripts.py delete mode 100644 distutils/command/check.py delete mode 100644 distutils/command/clean.py delete mode 100644 distutils/command/command_template delete mode 100644 distutils/command/config.py delete mode 100644 distutils/command/install.py delete mode 100644 distutils/command/install_data.py delete mode 100644 distutils/command/install_egg_info.py delete mode 100644 distutils/command/install_headers.py delete mode 100644 distutils/command/install_lib.py delete mode 100644 distutils/command/install_scripts.py delete mode 100644 distutils/command/register.py delete mode 100644 distutils/command/sdist.py delete mode 100644 distutils/command/upload.py delete mode 100644 distutils/command/wininst-10.0-amd64.exe delete mode 100644 distutils/command/wininst-10.0.exe delete mode 100644 distutils/command/wininst-14.0-amd64.exe delete mode 100644 distutils/command/wininst-14.0.exe delete mode 100644 distutils/command/wininst-6.0.exe delete mode 100644 distutils/command/wininst-7.1.exe delete mode 100644 distutils/command/wininst-8.0.exe delete mode 100644 distutils/command/wininst-9.0-amd64.exe delete mode 100644 distutils/command/wininst-9.0.exe delete mode 100644 distutils/config.py delete mode 100644 distutils/core.py delete mode 100644 distutils/cygwinccompiler.py delete mode 100644 distutils/debug.py delete mode 100644 distutils/dep_util.py delete mode 100644 distutils/dir_util.py delete mode 100644 distutils/dist.py delete mode 100644 distutils/errors.py delete mode 100644 distutils/extension.py delete mode 100644 distutils/fancy_getopt.py delete mode 100644 distutils/file_util.py delete mode 100644 distutils/filelist.py delete mode 100644 distutils/log.py delete mode 100644 distutils/msvc9compiler.py delete mode 100644 distutils/msvccompiler.py delete mode 100644 distutils/spawn.py delete mode 100644 distutils/sysconfig.py delete mode 100644 distutils/tests/Setup.sample delete mode 100644 distutils/tests/__init__.py delete mode 100644 distutils/tests/includetest.rst delete mode 100644 distutils/tests/py35compat.py delete mode 100644 distutils/tests/support.py delete mode 100644 distutils/tests/test_archive_util.py delete mode 100644 distutils/tests/test_bdist.py delete mode 100644 distutils/tests/test_bdist_dumb.py delete mode 100644 distutils/tests/test_bdist_msi.py delete mode 100644 distutils/tests/test_bdist_rpm.py delete mode 100644 distutils/tests/test_bdist_wininst.py delete mode 100644 distutils/tests/test_build.py delete mode 100644 distutils/tests/test_build_clib.py delete mode 100644 distutils/tests/test_build_ext.py delete mode 100644 distutils/tests/test_build_py.py delete mode 100644 distutils/tests/test_build_scripts.py delete mode 100644 distutils/tests/test_check.py delete mode 100644 distutils/tests/test_clean.py delete mode 100644 distutils/tests/test_cmd.py delete mode 100644 distutils/tests/test_config.py delete mode 100644 distutils/tests/test_config_cmd.py delete mode 100644 distutils/tests/test_core.py delete mode 100644 distutils/tests/test_cygwinccompiler.py delete mode 100644 distutils/tests/test_dep_util.py delete mode 100644 distutils/tests/test_dir_util.py delete mode 100644 distutils/tests/test_dist.py delete mode 100644 distutils/tests/test_extension.py delete mode 100644 distutils/tests/test_file_util.py delete mode 100644 distutils/tests/test_filelist.py delete mode 100644 distutils/tests/test_install.py delete mode 100644 distutils/tests/test_install_data.py delete mode 100644 distutils/tests/test_install_headers.py delete mode 100644 distutils/tests/test_install_lib.py delete mode 100644 distutils/tests/test_install_scripts.py delete mode 100644 distutils/tests/test_log.py delete mode 100644 distutils/tests/test_msvc9compiler.py delete mode 100644 distutils/tests/test_msvccompiler.py delete mode 100644 distutils/tests/test_register.py delete mode 100644 distutils/tests/test_sdist.py delete mode 100644 distutils/tests/test_spawn.py delete mode 100644 distutils/tests/test_sysconfig.py delete mode 100644 distutils/tests/test_text_file.py delete mode 100644 distutils/tests/test_unixccompiler.py delete mode 100644 distutils/tests/test_upload.py delete mode 100644 distutils/tests/test_util.py delete mode 100644 distutils/tests/test_version.py delete mode 100644 distutils/tests/test_versionpredicate.py delete mode 100644 distutils/text_file.py delete mode 100644 distutils/unixccompiler.py delete mode 100644 distutils/util.py delete mode 100644 distutils/version.py delete mode 100644 distutils/versionpredicate.py create mode 100644 setuptools/_distutils/README create mode 100644 setuptools/_distutils/__init__.py create mode 100644 setuptools/_distutils/_msvccompiler.py create mode 100644 setuptools/_distutils/archive_util.py create mode 100644 setuptools/_distutils/bcppcompiler.py create mode 100644 setuptools/_distutils/ccompiler.py create mode 100644 setuptools/_distutils/cmd.py create mode 100644 setuptools/_distutils/command/__init__.py create mode 100644 setuptools/_distutils/command/bdist.py create mode 100644 setuptools/_distutils/command/bdist_dumb.py create mode 100644 setuptools/_distutils/command/bdist_msi.py create mode 100644 setuptools/_distutils/command/bdist_rpm.py create mode 100644 setuptools/_distutils/command/bdist_wininst.py create mode 100644 setuptools/_distutils/command/build.py create mode 100644 setuptools/_distutils/command/build_clib.py create mode 100644 setuptools/_distutils/command/build_ext.py create mode 100644 setuptools/_distutils/command/build_py.py create mode 100644 setuptools/_distutils/command/build_scripts.py create mode 100644 setuptools/_distutils/command/check.py create mode 100644 setuptools/_distutils/command/clean.py create mode 100644 setuptools/_distutils/command/command_template create mode 100644 setuptools/_distutils/command/config.py create mode 100644 setuptools/_distutils/command/install.py create mode 100644 setuptools/_distutils/command/install_data.py create mode 100644 setuptools/_distutils/command/install_egg_info.py create mode 100644 setuptools/_distutils/command/install_headers.py create mode 100644 setuptools/_distutils/command/install_lib.py create mode 100644 setuptools/_distutils/command/install_scripts.py create mode 100644 setuptools/_distutils/command/register.py create mode 100644 setuptools/_distutils/command/sdist.py create mode 100644 setuptools/_distutils/command/upload.py create mode 100644 setuptools/_distutils/command/wininst-10.0-amd64.exe create mode 100644 setuptools/_distutils/command/wininst-10.0.exe create mode 100644 setuptools/_distutils/command/wininst-14.0-amd64.exe create mode 100644 setuptools/_distutils/command/wininst-14.0.exe create mode 100644 setuptools/_distutils/command/wininst-6.0.exe create mode 100644 setuptools/_distutils/command/wininst-7.1.exe create mode 100644 setuptools/_distutils/command/wininst-8.0.exe create mode 100644 setuptools/_distutils/command/wininst-9.0-amd64.exe create mode 100644 setuptools/_distutils/command/wininst-9.0.exe create mode 100644 setuptools/_distutils/config.py create mode 100644 setuptools/_distutils/core.py create mode 100644 setuptools/_distutils/cygwinccompiler.py create mode 100644 setuptools/_distutils/debug.py create mode 100644 setuptools/_distutils/dep_util.py create mode 100644 setuptools/_distutils/dir_util.py create mode 100644 setuptools/_distutils/dist.py create mode 100644 setuptools/_distutils/errors.py create mode 100644 setuptools/_distutils/extension.py create mode 100644 setuptools/_distutils/fancy_getopt.py create mode 100644 setuptools/_distutils/file_util.py create mode 100644 setuptools/_distutils/filelist.py create mode 100644 setuptools/_distutils/log.py create mode 100644 setuptools/_distutils/msvc9compiler.py create mode 100644 setuptools/_distutils/msvccompiler.py create mode 100644 setuptools/_distutils/spawn.py create mode 100644 setuptools/_distutils/sysconfig.py create mode 100644 setuptools/_distutils/tests/Setup.sample create mode 100644 setuptools/_distutils/tests/__init__.py create mode 100644 setuptools/_distutils/tests/includetest.rst create mode 100644 setuptools/_distutils/tests/py35compat.py create mode 100644 setuptools/_distutils/tests/support.py create mode 100644 setuptools/_distutils/tests/test_archive_util.py create mode 100644 setuptools/_distutils/tests/test_bdist.py create mode 100644 setuptools/_distutils/tests/test_bdist_dumb.py create mode 100644 setuptools/_distutils/tests/test_bdist_msi.py create mode 100644 setuptools/_distutils/tests/test_bdist_rpm.py create mode 100644 setuptools/_distutils/tests/test_bdist_wininst.py create mode 100644 setuptools/_distutils/tests/test_build.py create mode 100644 setuptools/_distutils/tests/test_build_clib.py create mode 100644 setuptools/_distutils/tests/test_build_ext.py create mode 100644 setuptools/_distutils/tests/test_build_py.py create mode 100644 setuptools/_distutils/tests/test_build_scripts.py create mode 100644 setuptools/_distutils/tests/test_check.py create mode 100644 setuptools/_distutils/tests/test_clean.py create mode 100644 setuptools/_distutils/tests/test_cmd.py create mode 100644 setuptools/_distutils/tests/test_config.py create mode 100644 setuptools/_distutils/tests/test_config_cmd.py create mode 100644 setuptools/_distutils/tests/test_core.py create mode 100644 setuptools/_distutils/tests/test_cygwinccompiler.py create mode 100644 setuptools/_distutils/tests/test_dep_util.py create mode 100644 setuptools/_distutils/tests/test_dir_util.py create mode 100644 setuptools/_distutils/tests/test_dist.py create mode 100644 setuptools/_distutils/tests/test_extension.py create mode 100644 setuptools/_distutils/tests/test_file_util.py create mode 100644 setuptools/_distutils/tests/test_filelist.py create mode 100644 setuptools/_distutils/tests/test_install.py create mode 100644 setuptools/_distutils/tests/test_install_data.py create mode 100644 setuptools/_distutils/tests/test_install_headers.py create mode 100644 setuptools/_distutils/tests/test_install_lib.py create mode 100644 setuptools/_distutils/tests/test_install_scripts.py create mode 100644 setuptools/_distutils/tests/test_log.py create mode 100644 setuptools/_distutils/tests/test_msvc9compiler.py create mode 100644 setuptools/_distutils/tests/test_msvccompiler.py create mode 100644 setuptools/_distutils/tests/test_register.py create mode 100644 setuptools/_distutils/tests/test_sdist.py create mode 100644 setuptools/_distutils/tests/test_spawn.py create mode 100644 setuptools/_distutils/tests/test_sysconfig.py create mode 100644 setuptools/_distutils/tests/test_text_file.py create mode 100644 setuptools/_distutils/tests/test_unixccompiler.py create mode 100644 setuptools/_distutils/tests/test_upload.py create mode 100644 setuptools/_distutils/tests/test_util.py create mode 100644 setuptools/_distutils/tests/test_version.py create mode 100644 setuptools/_distutils/tests/test_versionpredicate.py create mode 100644 setuptools/_distutils/text_file.py create mode 100644 setuptools/_distutils/unixccompiler.py create mode 100644 setuptools/_distutils/util.py create mode 100644 setuptools/_distutils/version.py create mode 100644 setuptools/_distutils/versionpredicate.py diff --git a/conftest.py b/conftest.py index 3f7e59b4..72edcf14 100644 --- a/conftest.py +++ b/conftest.py @@ -14,7 +14,7 @@ def pytest_addoption(parser): collect_ignore = [ 'tests/manual_test.py', 'setuptools/tests/mod_with_constant.py', - 'distutils', + 'setuptools/_distutils', ] diff --git a/distutils/README b/distutils/README deleted file mode 100644 index 23f48850..00000000 --- a/distutils/README +++ /dev/null @@ -1,11 +0,0 @@ -This directory contains the Distutils package. - -There's a full documentation available at: - - http://docs.python.org/distutils/ - -The Distutils-SIG web page is also a good starting point: - - http://www.python.org/sigs/distutils-sig/ - -$Id$ diff --git a/distutils/__init__.py b/distutils/__init__.py deleted file mode 100644 index 7dac55b6..00000000 --- a/distutils/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -"""distutils - -The main package for the Python Module Distribution Utilities. Normally -used from a setup script as - - from distutils.core import setup - - setup (...) -""" - -import sys - -__version__ = sys.version[:sys.version.index(' ')] - -local = True diff --git a/distutils/_msvccompiler.py b/distutils/_msvccompiler.py deleted file mode 100644 index af8099a4..00000000 --- a/distutils/_msvccompiler.py +++ /dev/null @@ -1,539 +0,0 @@ -"""distutils._msvccompiler - -Contains MSVCCompiler, an implementation of the abstract CCompiler class -for Microsoft Visual Studio 2015. - -The module is compatible with VS 2015 and later. You can find legacy support -for older versions in distutils.msvc9compiler and distutils.msvccompiler. -""" - -# Written by Perry Stoll -# hacked by Robin Becker and Thomas Heller to do a better job of -# finding DevStudio (through the registry) -# ported to VS 2005 and VS 2008 by Christian Heimes -# ported to VS 2015 by Steve Dower - -import os -import subprocess -import winreg - -from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import CCompiler, gen_lib_options -from distutils import log -from distutils.util import get_platform - -from itertools import count - -def _find_vc2015(): - try: - key = winreg.OpenKeyEx( - winreg.HKEY_LOCAL_MACHINE, - r"Software\Microsoft\VisualStudio\SxS\VC7", - access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY - ) - except OSError: - log.debug("Visual C++ is not registered") - return None, None - - best_version = 0 - best_dir = None - with key: - for i in count(): - try: - v, vc_dir, vt = winreg.EnumValue(key, i) - except OSError: - break - if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir): - try: - version = int(float(v)) - except (ValueError, TypeError): - continue - if version >= 14 and version > best_version: - best_version, best_dir = version, vc_dir - return best_version, best_dir - -def _find_vc2017(): - """Returns "15, path" based on the result of invoking vswhere.exe - If no install is found, returns "None, None" - - The version is returned to avoid unnecessarily changing the function - result. It may be ignored when the path is not None. - - If vswhere.exe is not available, by definition, VS 2017 is not - installed. - """ - root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") - if not root: - return None, None - - try: - path = subprocess.check_output([ - os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), - "-latest", - "-prerelease", - "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", - "-property", "installationPath", - "-products", "*", - ], encoding="mbcs", errors="strict").strip() - except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): - return None, None - - path = os.path.join(path, "VC", "Auxiliary", "Build") - if os.path.isdir(path): - return 15, path - - return None, None - -PLAT_SPEC_TO_RUNTIME = { - 'x86' : 'x86', - 'x86_amd64' : 'x64', - 'x86_arm' : 'arm', - 'x86_arm64' : 'arm64' -} - -def _find_vcvarsall(plat_spec): - # bpo-38597: Removed vcruntime return value - _, best_dir = _find_vc2017() - - if not best_dir: - best_version, best_dir = _find_vc2015() - - if not best_dir: - log.debug("No suitable Visual C++ version found") - return None, None - - vcvarsall = os.path.join(best_dir, "vcvarsall.bat") - if not os.path.isfile(vcvarsall): - log.debug("%s cannot be found", vcvarsall) - return None, None - - return vcvarsall, None - -def _get_vc_env(plat_spec): - if os.getenv("DISTUTILS_USE_SDK"): - return { - key.lower(): value - for key, value in os.environ.items() - } - - vcvarsall, _ = _find_vcvarsall(plat_spec) - if not vcvarsall: - raise DistutilsPlatformError("Unable to find vcvarsall.bat") - - try: - out = subprocess.check_output( - 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), - stderr=subprocess.STDOUT, - ).decode('utf-16le', errors='replace') - except subprocess.CalledProcessError as exc: - log.error(exc.output) - raise DistutilsPlatformError("Error executing {}" - .format(exc.cmd)) - - env = { - key.lower(): value - for key, _, value in - (line.partition('=') for line in out.splitlines()) - if key and value - } - - return env - -def _find_exe(exe, paths=None): - """Return path to an MSVC executable program. - - Tries to find the program in several places: first, one of the - MSVC program search paths from the registry; next, the directories - in the PATH environment variable. If any of those work, return an - absolute path that is known to exist. If none of them work, just - return the original program name, 'exe'. - """ - if not paths: - paths = os.getenv('path').split(os.pathsep) - for p in paths: - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - return exe - -# A map keyed by get_platform() return values to values accepted by -# 'vcvarsall.bat'. Always cross-compile from x86 to work with the -# lighter-weight MSVC installs that do not include native 64-bit tools. -PLAT_TO_VCVARS = { - 'win32' : 'x86', - 'win-amd64' : 'x86_amd64', - 'win-arm32' : 'x86_arm', - 'win-arm64' : 'x86_arm64' -} - -class MSVCCompiler(CCompiler) : - """Concrete class that implements an interface to Microsoft Visual C++, - as defined by the CCompiler abstract class.""" - - compiler_type = 'msvc' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.rc'] - _mc_extensions = ['.mc'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = (_c_extensions + _cpp_extensions + - _rc_extensions + _mc_extensions) - res_extension = '.res' - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - - def __init__(self, verbose=0, dry_run=0, force=0): - CCompiler.__init__ (self, verbose, dry_run, force) - # target platform (.plat_name is consistent with 'bdist') - self.plat_name = None - self.initialized = False - - def initialize(self, plat_name=None): - # multi-init means we would need to check platform same each time... - assert not self.initialized, "don't init multiple times" - if plat_name is None: - plat_name = get_platform() - # sanity check for platforms to prevent obscure errors later. - if plat_name not in PLAT_TO_VCVARS: - raise DistutilsPlatformError("--plat-name must be one of {}" - .format(tuple(PLAT_TO_VCVARS))) - - # Get the vcvarsall.bat spec for the requested platform. - plat_spec = PLAT_TO_VCVARS[plat_name] - - vc_env = _get_vc_env(plat_spec) - if not vc_env: - raise DistutilsPlatformError("Unable to find a compatible " - "Visual Studio installation.") - - self._paths = vc_env.get('path', '') - paths = self._paths.split(os.pathsep) - self.cc = _find_exe("cl.exe", paths) - self.linker = _find_exe("link.exe", paths) - self.lib = _find_exe("lib.exe", paths) - self.rc = _find_exe("rc.exe", paths) # resource compiler - self.mc = _find_exe("mc.exe", paths) # message compiler - self.mt = _find_exe("mt.exe", paths) # message compiler - - for dir in vc_env.get('include', '').split(os.pathsep): - if dir: - self.add_include_dir(dir.rstrip(os.sep)) - - for dir in vc_env.get('lib', '').split(os.pathsep): - if dir: - self.add_library_dir(dir.rstrip(os.sep)) - - self.preprocess_options = None - # bpo-38597: Always compile with dynamic linking - # Future releases of Python 3.x will include all past - # versions of vcruntime*.dll for compatibility. - self.compile_options = [ - '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD' - ] - - self.compile_options_debug = [ - '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' - ] - - ldflags = [ - '/nologo', '/INCREMENTAL:NO', '/LTCG' - ] - - ldflags_debug = [ - '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL' - ] - - self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1'] - self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1'] - self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] - self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] - self.ldflags_static = [*ldflags] - self.ldflags_static_debug = [*ldflags_debug] - - self._ldflags = { - (CCompiler.EXECUTABLE, None): self.ldflags_exe, - (CCompiler.EXECUTABLE, False): self.ldflags_exe, - (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug, - (CCompiler.SHARED_OBJECT, None): self.ldflags_shared, - (CCompiler.SHARED_OBJECT, False): self.ldflags_shared, - (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug, - (CCompiler.SHARED_LIBRARY, None): self.ldflags_static, - (CCompiler.SHARED_LIBRARY, False): self.ldflags_static, - (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug, - } - - self.initialized = True - - # -- Worker methods ------------------------------------------------ - - def object_filenames(self, - source_filenames, - strip_dir=0, - output_dir=''): - ext_map = { - **{ext: self.obj_extension for ext in self.src_extensions}, - **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions}, - } - - output_dir = output_dir or '' - - def make_out_path(p): - base, ext = os.path.splitext(p) - if strip_dir: - base = os.path.basename(base) - else: - _, base = os.path.splitdrive(base) - if base.startswith((os.path.sep, os.path.altsep)): - base = base[1:] - try: - # XXX: This may produce absurdly long paths. We should check - # the length of the result and trim base until we fit within - # 260 characters. - return os.path.join(output_dir, base + ext_map[ext]) - except LookupError: - # Better to raise an exception instead of silently continuing - # and later complain about sources and targets having - # different lengths - raise CompileError("Don't know how to compile {}".format(p)) - - return list(map(make_out_path, source_filenames)) - - - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - if not self.initialized: - self.initialize() - compile_info = self._setup_compile(output_dir, macros, include_dirs, - sources, depends, extra_postargs) - macros, objects, extra_postargs, pp_opts, build = compile_info - - compile_opts = extra_preargs or [] - compile_opts.append('/c') - if debug: - compile_opts.extend(self.compile_options_debug) - else: - compile_opts.extend(self.compile_options) - - - add_cpp_opts = False - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - if debug: - # pass the full pathname to MSVC in debug mode, - # this allows the debugger to find the source file - # without asking the user to browse for it - src = os.path.abspath(src) - - if ext in self._c_extensions: - input_opt = "/Tc" + src - elif ext in self._cpp_extensions: - input_opt = "/Tp" + src - add_cpp_opts = True - elif ext in self._rc_extensions: - # compile .RC to .RES file - input_opt = src - output_opt = "/fo" + obj - try: - self.spawn([self.rc] + pp_opts + [output_opt, input_opt]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue - elif ext in self._mc_extensions: - # Compile .MC to .RC file to .RES file. - # * '-h dir' specifies the directory for the - # generated include file - # * '-r dir' specifies the target directory of the - # generated RC file and the binary message resource - # it includes - # - # For now (since there are no options to change this), - # we use the source-directory for the include file and - # the build directory for the RC file and message - # resources. This works at least for win32all. - h_dir = os.path.dirname(src) - rc_dir = os.path.dirname(obj) - try: - # first compile .MC to .RC and .H file - self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src]) - base, _ = os.path.splitext(os.path.basename (src)) - rc_file = os.path.join(rc_dir, base + '.rc') - # then compile .RC to .RES file - self.spawn([self.rc, "/fo" + obj, rc_file]) - - except DistutilsExecError as msg: - raise CompileError(msg) - continue - else: - # how to handle this file? - raise CompileError("Don't know how to compile {} to {}" - .format(src, obj)) - - args = [self.cc] + compile_opts + pp_opts - if add_cpp_opts: - args.append('/EHsc') - args.append(input_opt) - args.append("/Fo" + obj) - args.extend(extra_postargs) - - try: - self.spawn(args) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - - def create_static_lib(self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): - - if not self.initialized: - self.initialize() - objects, output_dir = self._fix_object_args(objects, output_dir) - output_filename = self.library_filename(output_libname, - output_dir=output_dir) - - if self._need_link(objects, output_filename): - lib_args = objects + ['/OUT:' + output_filename] - if debug: - pass # XXX what goes here? - try: - log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args)) - self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - if not self.initialized: - self.initialize() - objects, output_dir = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, - runtime_library_dirs) - libraries, library_dirs, runtime_library_dirs = fixed_args - - if runtime_library_dirs: - self.warn("I don't know what to do with 'runtime_library_dirs': " - + str(runtime_library_dirs)) - - lib_opts = gen_lib_options(self, - library_dirs, runtime_library_dirs, - libraries) - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - ldflags = self._ldflags[target_desc, debug] - - export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])] - - ld_args = (ldflags + lib_opts + export_opts + - objects + ['/OUT:' + output_filename]) - - # The MSVC linker generates .lib and .exp files, which cannot be - # suppressed by any linker switches. The .lib files may even be - # needed! Make sure they are generated in the temporary build - # directory. Since they have different names for debug and release - # builds, they can go into the same directory. - build_temp = os.path.dirname(objects[0]) - if export_symbols is not None: - (dll_name, dll_ext) = os.path.splitext( - os.path.basename(output_filename)) - implib_file = os.path.join( - build_temp, - self.library_filename(dll_name)) - ld_args.append ('/IMPLIB:' + implib_file) - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - output_dir = os.path.dirname(os.path.abspath(output_filename)) - self.mkpath(output_dir) - try: - log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args)) - self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - def spawn(self, cmd): - old_path = os.getenv('path') - try: - os.environ['path'] = self._paths - return super().spawn(cmd) - finally: - os.environ['path'] = old_path - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "/LIBPATH:" + dir - - def runtime_library_dir_option(self, dir): - raise DistutilsPlatformError( - "don't know how to set runtime library search path for MSVC") - - def library_option(self, lib): - return self.library_filename(lib) - - def find_library_file(self, dirs, lib, debug=0): - # Prefer a debugging library if found (and requested), but deal - # with it if we don't have one. - if debug: - try_names = [lib + "_d", lib] - else: - try_names = [lib] - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename(name)) - if os.path.isfile(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None diff --git a/distutils/archive_util.py b/distutils/archive_util.py deleted file mode 100644 index 565a3117..00000000 --- a/distutils/archive_util.py +++ /dev/null @@ -1,256 +0,0 @@ -"""distutils.archive_util - -Utility functions for creating archive files (tarballs, zip files, -that sort of thing).""" - -import os -from warnings import warn -import sys - -try: - import zipfile -except ImportError: - zipfile = None - - -from distutils.errors import DistutilsExecError -from distutils.spawn import spawn -from distutils.dir_util import mkpath -from distutils import log - -try: - from pwd import getpwnam -except ImportError: - getpwnam = None - -try: - from grp import getgrnam -except ImportError: - getgrnam = None - -def _get_gid(name): - """Returns a gid, given a group name.""" - if getgrnam is None or name is None: - return None - try: - result = getgrnam(name) - except KeyError: - result = None - if result is not None: - return result[2] - return None - -def _get_uid(name): - """Returns an uid, given a user name.""" - if getpwnam is None or name is None: - return None - try: - result = getpwnam(name) - except KeyError: - result = None - if result is not None: - return result[2] - return None - -def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, - owner=None, group=None): - """Create a (possibly compressed) tar file from all the files under - 'base_dir'. - - 'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or - None. ("compress" will be deprecated in Python 3.2) - - 'owner' and 'group' can be used to define an owner and a group for the - archive that is being built. If not provided, the current owner and group - will be used. - - The output tar file will be named 'base_dir' + ".tar", possibly plus - the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z"). - - Returns the output filename. - """ - tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '', - 'compress': ''} - compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', - 'compress': '.Z'} - - # flags for compression program, each element of list will be an argument - if compress is not None and compress not in compress_ext.keys(): - raise ValueError( - "bad value for 'compress': must be None, 'gzip', 'bzip2', " - "'xz' or 'compress'") - - archive_name = base_name + '.tar' - if compress != 'compress': - archive_name += compress_ext.get(compress, '') - - mkpath(os.path.dirname(archive_name), dry_run=dry_run) - - # creating the tarball - import tarfile # late import so Python build itself doesn't break - - log.info('Creating tar archive') - - uid = _get_uid(owner) - gid = _get_gid(group) - - def _set_uid_gid(tarinfo): - if gid is not None: - tarinfo.gid = gid - tarinfo.gname = group - if uid is not None: - tarinfo.uid = uid - tarinfo.uname = owner - return tarinfo - - if not dry_run: - tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) - try: - tar.add(base_dir, filter=_set_uid_gid) - finally: - tar.close() - - # compression using `compress` - if compress == 'compress': - warn("'compress' will be deprecated.", PendingDeprecationWarning) - # the option varies depending on the platform - compressed_name = archive_name + compress_ext[compress] - if sys.platform == 'win32': - cmd = [compress, archive_name, compressed_name] - else: - cmd = [compress, '-f', archive_name] - spawn(cmd, dry_run=dry_run) - return compressed_name - - return archive_name - -def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): - """Create a zip file from all the files under 'base_dir'. - - The output zip file will be named 'base_name' + ".zip". Uses either the - "zipfile" Python module (if available) or the InfoZIP "zip" utility - (if installed and found on the default search path). If neither tool is - available, raises DistutilsExecError. Returns the name of the output zip - file. - """ - zip_filename = base_name + ".zip" - mkpath(os.path.dirname(zip_filename), dry_run=dry_run) - - # If zipfile module is not available, try spawning an external - # 'zip' command. - if zipfile is None: - if verbose: - zipoptions = "-r" - else: - zipoptions = "-rq" - - try: - spawn(["zip", zipoptions, zip_filename, base_dir], - dry_run=dry_run) - except DistutilsExecError: - # XXX really should distinguish between "couldn't find - # external 'zip' command" and "zip failed". - raise DistutilsExecError(("unable to create zip file '%s': " - "could neither import the 'zipfile' module nor " - "find a standalone zip utility") % zip_filename) - - else: - log.info("creating '%s' and adding '%s' to it", - zip_filename, base_dir) - - if not dry_run: - try: - zip = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) - except RuntimeError: - zip = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_STORED) - - with zip: - if base_dir != os.curdir: - path = os.path.normpath(os.path.join(base_dir, '')) - zip.write(path, path) - log.info("adding '%s'", path) - for dirpath, dirnames, filenames in os.walk(base_dir): - for name in dirnames: - path = os.path.normpath(os.path.join(dirpath, name, '')) - zip.write(path, path) - log.info("adding '%s'", path) - for name in filenames: - path = os.path.normpath(os.path.join(dirpath, name)) - if os.path.isfile(path): - zip.write(path, path) - log.info("adding '%s'", path) - - return zip_filename - -ARCHIVE_FORMATS = { - 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), - 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), - 'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"), - 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"), - 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"), - 'zip': (make_zipfile, [],"ZIP file") - } - -def check_archive_formats(formats): - """Returns the first format from the 'format' list that is unknown. - - If all formats are known, returns None - """ - for format in formats: - if format not in ARCHIVE_FORMATS: - return format - return None - -def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, - dry_run=0, owner=None, group=None): - """Create an archive file (eg. zip or tar). - - 'base_name' is the name of the file to create, minus any format-specific - extension; 'format' is the archive format: one of "zip", "tar", "gztar", - "bztar", "xztar", or "ztar". - - 'root_dir' is a directory that will be the root directory of the - archive; ie. we typically chdir into 'root_dir' before creating the - archive. 'base_dir' is the directory where we start archiving from; - ie. 'base_dir' will be the common prefix of all files and - directories in the archive. 'root_dir' and 'base_dir' both default - to the current directory. Returns the name of the archive file. - - 'owner' and 'group' are used when creating a tar archive. By default, - uses the current owner and group. - """ - save_cwd = os.getcwd() - if root_dir is not None: - log.debug("changing into '%s'", root_dir) - base_name = os.path.abspath(base_name) - if not dry_run: - os.chdir(root_dir) - - if base_dir is None: - base_dir = os.curdir - - kwargs = {'dry_run': dry_run} - - try: - format_info = ARCHIVE_FORMATS[format] - except KeyError: - raise ValueError("unknown archive format '%s'" % format) - - func = format_info[0] - for arg, val in format_info[1]: - kwargs[arg] = val - - if format != 'zip': - kwargs['owner'] = owner - kwargs['group'] = group - - try: - filename = func(base_name, base_dir, **kwargs) - finally: - if root_dir is not None: - log.debug("changing back to '%s'", save_cwd) - os.chdir(save_cwd) - - return filename diff --git a/distutils/bcppcompiler.py b/distutils/bcppcompiler.py deleted file mode 100644 index 071fea5d..00000000 --- a/distutils/bcppcompiler.py +++ /dev/null @@ -1,393 +0,0 @@ -"""distutils.bcppcompiler - -Contains BorlandCCompiler, an implementation of the abstract CCompiler class -for the Borland C++ compiler. -""" - -# This implementation by Lyle Johnson, based on the original msvccompiler.py -# module and using the directions originally published by Gordon Williams. - -# XXX looks like there's a LOT of overlap between these two classes: -# someone should sit down and factor out the common code as -# WindowsCCompiler! --GPW - - -import os -from distutils.errors import \ - DistutilsExecError, \ - CompileError, LibError, LinkError, UnknownFileError -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options -from distutils.file_util import write_file -from distutils.dep_util import newer -from distutils import log - -class BCPPCompiler(CCompiler) : - """Concrete class that implements an interface to the Borland C/C++ - compiler, as defined by the CCompiler abstract class. - """ - - compiler_type = 'bcpp' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = _c_extensions + _cpp_extensions - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - - def __init__ (self, - verbose=0, - dry_run=0, - force=0): - - CCompiler.__init__ (self, verbose, dry_run, force) - - # These executables are assumed to all be in the path. - # Borland doesn't seem to use any special registry settings to - # indicate their installation locations. - - self.cc = "bcc32.exe" - self.linker = "ilink32.exe" - self.lib = "tlib.exe" - - self.preprocess_options = None - self.compile_options = ['/tWM', '/O2', '/q', '/g0'] - self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0'] - - self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x'] - self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x'] - self.ldflags_static = [] - self.ldflags_exe = ['/Gn', '/q', '/x'] - self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r'] - - - # -- Worker methods ------------------------------------------------ - - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - macros, objects, extra_postargs, pp_opts, build = \ - self._setup_compile(output_dir, macros, include_dirs, sources, - depends, extra_postargs) - compile_opts = extra_preargs or [] - compile_opts.append ('-c') - if debug: - compile_opts.extend (self.compile_options_debug) - else: - compile_opts.extend (self.compile_options) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - # XXX why do the normpath here? - src = os.path.normpath(src) - obj = os.path.normpath(obj) - # XXX _setup_compile() did a mkpath() too but before the normpath. - # Is it possible to skip the normpath? - self.mkpath(os.path.dirname(obj)) - - if ext == '.res': - # This is already a binary file -- skip it. - continue # the 'for' loop - if ext == '.rc': - # This needs to be compiled to a .res file -- do it now. - try: - self.spawn (["brcc32", "-fo", obj, src]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue # the 'for' loop - - # The next two are both for the real compiler. - if ext in self._c_extensions: - input_opt = "" - elif ext in self._cpp_extensions: - input_opt = "-P" - else: - # Unknown file type -- no extra options. The compiler - # will probably fail, but let it just in case this is a - # file the compiler recognizes even if we don't. - input_opt = "" - - output_opt = "-o" + obj - - # Compiler command line syntax is: "bcc32 [options] file(s)". - # Note that the source file names must appear at the end of - # the command line. - try: - self.spawn ([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs + [src]) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - # compile () - - - def create_static_lib (self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): - - (objects, output_dir) = self._fix_object_args (objects, output_dir) - output_filename = \ - self.library_filename (output_libname, output_dir=output_dir) - - if self._need_link (objects, output_filename): - lib_args = [output_filename, '/u'] + objects - if debug: - pass # XXX what goes here? - try: - self.spawn ([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - # create_static_lib () - - - def link (self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - # XXX this ignores 'build_temp'! should follow the lead of - # msvccompiler.py - - (objects, output_dir) = self._fix_object_args (objects, output_dir) - (libraries, library_dirs, runtime_library_dirs) = \ - self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) - - if runtime_library_dirs: - log.warn("I don't know what to do with 'runtime_library_dirs': %s", - str(runtime_library_dirs)) - - if output_dir is not None: - output_filename = os.path.join (output_dir, output_filename) - - if self._need_link (objects, output_filename): - - # Figure out linker args based on type of target. - if target_desc == CCompiler.EXECUTABLE: - startup_obj = 'c0w32' - if debug: - ld_args = self.ldflags_exe_debug[:] - else: - ld_args = self.ldflags_exe[:] - else: - startup_obj = 'c0d32' - if debug: - ld_args = self.ldflags_shared_debug[:] - else: - ld_args = self.ldflags_shared[:] - - - # Create a temporary exports file for use by the linker - if export_symbols is None: - def_file = '' - else: - head, tail = os.path.split (output_filename) - modname, ext = os.path.splitext (tail) - temp_dir = os.path.dirname(objects[0]) # preserve tree structure - def_file = os.path.join (temp_dir, '%s.def' % modname) - contents = ['EXPORTS'] - for sym in (export_symbols or []): - contents.append(' %s=_%s' % (sym, sym)) - self.execute(write_file, (def_file, contents), - "writing %s" % def_file) - - # Borland C++ has problems with '/' in paths - objects2 = map(os.path.normpath, objects) - # split objects in .obj and .res files - # Borland C++ needs them at different positions in the command line - objects = [startup_obj] - resources = [] - for file in objects2: - (base, ext) = os.path.splitext(os.path.normcase(file)) - if ext == '.res': - resources.append(file) - else: - objects.append(file) - - - for l in library_dirs: - ld_args.append("/L%s" % os.path.normpath(l)) - ld_args.append("/L.") # we sometimes use relative paths - - # list of object files - ld_args.extend(objects) - - # XXX the command-line syntax for Borland C++ is a bit wonky; - # certain filenames are jammed together in one big string, but - # comma-delimited. This doesn't mesh too well with the - # Unix-centric attitude (with a DOS/Windows quoting hack) of - # 'spawn()', so constructing the argument list is a bit - # awkward. Note that doing the obvious thing and jamming all - # the filenames and commas into one argument would be wrong, - # because 'spawn()' would quote any filenames with spaces in - # them. Arghghh!. Apparently it works fine as coded... - - # name of dll/exe file - ld_args.extend([',',output_filename]) - # no map file and start libraries - ld_args.append(',,') - - for lib in libraries: - # see if we find it and if there is a bcpp specific lib - # (xxx_bcpp.lib) - libfile = self.find_library_file(library_dirs, lib, debug) - if libfile is None: - ld_args.append(lib) - # probably a BCPP internal library -- don't warn - else: - # full name which prefers bcpp_xxx.lib over xxx.lib - ld_args.append(libfile) - - # some default libraries - ld_args.append ('import32') - ld_args.append ('cw32mt') - - # def file for export symbols - ld_args.extend([',',def_file]) - # add resource files - ld_args.append(',') - ld_args.extend(resources) - - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - self.mkpath (os.path.dirname (output_filename)) - try: - self.spawn ([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - - else: - log.debug("skipping %s (up-to-date)", output_filename) - - # link () - - # -- Miscellaneous methods ----------------------------------------- - - - def find_library_file (self, dirs, lib, debug=0): - # List of effective library names to try, in order of preference: - # xxx_bcpp.lib is better than xxx.lib - # and xxx_d.lib is better than xxx.lib if debug is set - # - # The "_bcpp" suffix is to handle a Python installation for people - # with multiple compilers (primarily Distutils hackers, I suspect - # ;-). The idea is they'd have one static library for each - # compiler they care about, since (almost?) every Windows compiler - # seems to have a different format for static libraries. - if debug: - dlib = (lib + "_d") - try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib) - else: - try_names = (lib + "_bcpp", lib) - - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename(name)) - if os.path.exists(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None - - # overwrite the one from CCompiler to support rc and res-files - def object_filenames (self, - source_filenames, - strip_dir=0, - output_dir=''): - if output_dir is None: output_dir = '' - obj_names = [] - for src_name in source_filenames: - # use normcase to make sure '.rc' is really '.rc' and not '.RC' - (base, ext) = os.path.splitext (os.path.normcase(src_name)) - if ext not in (self.src_extensions + ['.rc','.res']): - raise UnknownFileError("unknown file type '%s' (from '%s')" % \ - (ext, src_name)) - if strip_dir: - base = os.path.basename (base) - if ext == '.res': - # these can go unchanged - obj_names.append (os.path.join (output_dir, base + ext)) - elif ext == '.rc': - # these need to be compiled to .res-files - obj_names.append (os.path.join (output_dir, base + '.res')) - else: - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) - return obj_names - - # object_filenames () - - def preprocess (self, - source, - output_file=None, - macros=None, - include_dirs=None, - extra_preargs=None, - extra_postargs=None): - - (_, macros, include_dirs) = \ - self._fix_compile_args(None, macros, include_dirs) - pp_opts = gen_preprocess_options(macros, include_dirs) - pp_args = ['cpp32.exe'] + pp_opts - if output_file is not None: - pp_args.append('-o' + output_file) - if extra_preargs: - pp_args[:0] = extra_preargs - if extra_postargs: - pp_args.extend(extra_postargs) - pp_args.append(source) - - # We need to preprocess: either we're being forced to, or the - # source file is newer than the target (or the target doesn't - # exist). - if self.force or output_file is None or newer(source, output_file): - if output_file: - self.mkpath(os.path.dirname(output_file)) - try: - self.spawn(pp_args) - except DistutilsExecError as msg: - print(msg) - raise CompileError(msg) - - # preprocess() diff --git a/distutils/ccompiler.py b/distutils/ccompiler.py deleted file mode 100644 index b5ef143e..00000000 --- a/distutils/ccompiler.py +++ /dev/null @@ -1,1116 +0,0 @@ -"""distutils.ccompiler - -Contains CCompiler, an abstract base class that defines the interface -for the Distutils compiler abstraction model.""" - -import sys, os, re -from distutils.errors import * -from distutils.spawn import spawn -from distutils.file_util import move_file -from distutils.dir_util import mkpath -from distutils.dep_util import newer_group -from distutils.util import split_quoted, execute -from distutils import log - -class CCompiler: - """Abstract base class to define the interface that must be implemented - by real compiler classes. Also has some utility methods used by - several compiler classes. - - The basic idea behind a compiler abstraction class is that each - instance can be used for all the compile/link steps in building a - single project. Thus, attributes common to all of those compile and - link steps -- include directories, macros to define, libraries to link - against, etc. -- are attributes of the compiler instance. To allow for - variability in how individual files are treated, most of those - attributes may be varied on a per-compilation or per-link basis. - """ - - # 'compiler_type' is a class attribute that identifies this class. It - # keeps code that wants to know what kind of compiler it's dealing with - # from having to import all possible compiler classes just to do an - # 'isinstance'. In concrete CCompiler subclasses, 'compiler_type' - # should really, really be one of the keys of the 'compiler_class' - # dictionary (see below -- used by the 'new_compiler()' factory - # function) -- authors of new compiler interface classes are - # responsible for updating 'compiler_class'! - compiler_type = None - - # XXX things not handled by this compiler abstraction model: - # * client can't provide additional options for a compiler, - # e.g. warning, optimization, debugging flags. Perhaps this - # should be the domain of concrete compiler abstraction classes - # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base - # class should have methods for the common ones. - # * can't completely override the include or library searchg - # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". - # I'm not sure how widely supported this is even by Unix - # compilers, much less on other platforms. And I'm even less - # sure how useful it is; maybe for cross-compiling, but - # support for that is a ways off. (And anyways, cross - # compilers probably have a dedicated binary with the - # right paths compiled in. I hope.) - # * can't do really freaky things with the library list/library - # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against - # different versions of libfoo.a in different locations. I - # think this is useless without the ability to null out the - # library search path anyways. - - - # Subclasses that rely on the standard filename generation methods - # implemented below should override these; see the comment near - # those methods ('object_filenames()' et. al.) for details: - src_extensions = None # list of strings - obj_extension = None # string - static_lib_extension = None - shared_lib_extension = None # string - static_lib_format = None # format string - shared_lib_format = None # prob. same as static_lib_format - exe_extension = None # string - - # Default language settings. language_map is used to detect a source - # file or Extension target language, checking source filenames. - # language_order is used to detect the language precedence, when deciding - # what language to use when mixing source types. For example, if some - # extension has two files with ".c" extension, and one with ".cpp", it - # is still linked as c++. - language_map = {".c" : "c", - ".cc" : "c++", - ".cpp" : "c++", - ".cxx" : "c++", - ".m" : "objc", - } - language_order = ["c++", "objc", "c"] - - def __init__(self, verbose=0, dry_run=0, force=0): - self.dry_run = dry_run - self.force = force - self.verbose = verbose - - # 'output_dir': a common output directory for object, library, - # shared object, and shared library files - self.output_dir = None - - # 'macros': a list of macro definitions (or undefinitions). A - # macro definition is a 2-tuple (name, value), where the value is - # either a string or None (no explicit value). A macro - # undefinition is a 1-tuple (name,). - self.macros = [] - - # 'include_dirs': a list of directories to search for include files - self.include_dirs = [] - - # 'libraries': a list of libraries to include in any link - # (library names, not filenames: eg. "foo" not "libfoo.a") - self.libraries = [] - - # 'library_dirs': a list of directories to search for libraries - self.library_dirs = [] - - # 'runtime_library_dirs': a list of directories to search for - # shared libraries/objects at runtime - self.runtime_library_dirs = [] - - # 'objects': a list of object files (or similar, such as explicitly - # named library files) to include on any link - self.objects = [] - - for key in self.executables.keys(): - self.set_executable(key, self.executables[key]) - - def set_executables(self, **kwargs): - """Define the executables (and options for them) that will be run - to perform the various stages of compilation. The exact set of - executables that may be specified here depends on the compiler - class (via the 'executables' class attribute), but most will have: - compiler the C/C++ compiler - linker_so linker used to create shared objects and libraries - linker_exe linker used to create binary executables - archiver static library creator - - On platforms with a command-line (Unix, DOS/Windows), each of these - is a string that will be split into executable name and (optional) - list of arguments. (Splitting the string is done similarly to how - Unix shells operate: words are delimited by spaces, but quotes and - backslashes can override this. See - 'distutils.util.split_quoted()'.) - """ - - # Note that some CCompiler implementation classes will define class - # attributes 'cpp', 'cc', etc. with hard-coded executable names; - # this is appropriate when a compiler class is for exactly one - # compiler/OS combination (eg. MSVCCompiler). Other compiler - # classes (UnixCCompiler, in particular) are driven by information - # discovered at run-time, since there are many different ways to do - # basically the same things with Unix C compilers. - - for key in kwargs: - if key not in self.executables: - raise ValueError("unknown executable '%s' for class %s" % - (key, self.__class__.__name__)) - self.set_executable(key, kwargs[key]) - - def set_executable(self, key, value): - if isinstance(value, str): - setattr(self, key, split_quoted(value)) - else: - setattr(self, key, value) - - def _find_macro(self, name): - i = 0 - for defn in self.macros: - if defn[0] == name: - return i - i += 1 - return None - - def _check_macro_definitions(self, definitions): - """Ensures that every element of 'definitions' is a valid macro - definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do - nothing if all definitions are OK, raise TypeError otherwise. - """ - for defn in definitions: - if not (isinstance(defn, tuple) and - (len(defn) in (1, 2) and - (isinstance (defn[1], str) or defn[1] is None)) and - isinstance (defn[0], str)): - raise TypeError(("invalid macro definition '%s': " % defn) + \ - "must be tuple (string,), (string, string), or " + \ - "(string, None)") - - - # -- Bookkeeping methods ------------------------------------------- - - def define_macro(self, name, value=None): - """Define a preprocessor macro for all compilations driven by this - compiler object. The optional parameter 'value' should be a - string; if it is not supplied, then the macro will be defined - without an explicit value and the exact outcome depends on the - compiler used (XXX true? does ANSI say anything about this?) - """ - # Delete from the list of macro definitions/undefinitions if - # already there (so that this one will take precedence). - i = self._find_macro (name) - if i is not None: - del self.macros[i] - - self.macros.append((name, value)) - - def undefine_macro(self, name): - """Undefine a preprocessor macro for all compilations driven by - this compiler object. If the same macro is defined by - 'define_macro()' and undefined by 'undefine_macro()' the last call - takes precedence (including multiple redefinitions or - undefinitions). If the macro is redefined/undefined on a - per-compilation basis (ie. in the call to 'compile()'), then that - takes precedence. - """ - # Delete from the list of macro definitions/undefinitions if - # already there (so that this one will take precedence). - i = self._find_macro (name) - if i is not None: - del self.macros[i] - - undefn = (name,) - self.macros.append(undefn) - - def add_include_dir(self, dir): - """Add 'dir' to the list of directories that will be searched for - header files. The compiler is instructed to search directories in - the order in which they are supplied by successive calls to - 'add_include_dir()'. - """ - self.include_dirs.append(dir) - - def set_include_dirs(self, dirs): - """Set the list of directories that will be searched to 'dirs' (a - list of strings). Overrides any preceding calls to - 'add_include_dir()'; subsequence calls to 'add_include_dir()' add - to the list passed to 'set_include_dirs()'. This does not affect - any list of standard include directories that the compiler may - search by default. - """ - self.include_dirs = dirs[:] - - def add_library(self, libname): - """Add 'libname' to the list of libraries that will be included in - all links driven by this compiler object. Note that 'libname' - should *not* be the name of a file containing a library, but the - name of the library itself: the actual filename will be inferred by - the linker, the compiler, or the compiler class (depending on the - platform). - - The linker will be instructed to link against libraries in the - order they were supplied to 'add_library()' and/or - 'set_libraries()'. It is perfectly valid to duplicate library - names; the linker will be instructed to link against libraries as - many times as they are mentioned. - """ - self.libraries.append(libname) - - def set_libraries(self, libnames): - """Set the list of libraries to be included in all links driven by - this compiler object to 'libnames' (a list of strings). This does - not affect any standard system libraries that the linker may - include by default. - """ - self.libraries = libnames[:] - - def add_library_dir(self, dir): - """Add 'dir' to the list of directories that will be searched for - libraries specified to 'add_library()' and 'set_libraries()'. The - linker will be instructed to search for libraries in the order they - are supplied to 'add_library_dir()' and/or 'set_library_dirs()'. - """ - self.library_dirs.append(dir) - - def set_library_dirs(self, dirs): - """Set the list of library search directories to 'dirs' (a list of - strings). This does not affect any standard library search path - that the linker may search by default. - """ - self.library_dirs = dirs[:] - - def add_runtime_library_dir(self, dir): - """Add 'dir' to the list of directories that will be searched for - shared libraries at runtime. - """ - self.runtime_library_dirs.append(dir) - - def set_runtime_library_dirs(self, dirs): - """Set the list of directories to search for shared libraries at - runtime to 'dirs' (a list of strings). This does not affect any - standard search path that the runtime linker may search by - default. - """ - self.runtime_library_dirs = dirs[:] - - def add_link_object(self, object): - """Add 'object' to the list of object files (or analogues, such as - explicitly named library files or the output of "resource - compilers") to be included in every link driven by this compiler - object. - """ - self.objects.append(object) - - def set_link_objects(self, objects): - """Set the list of object files (or analogues) to be included in - every link to 'objects'. This does not affect any standard object - files that the linker may include by default (such as system - libraries). - """ - self.objects = objects[:] - - - # -- Private utility methods -------------------------------------- - # (here for the convenience of subclasses) - - # Helper method to prep compiler in subclass compile() methods - - def _setup_compile(self, outdir, macros, incdirs, sources, depends, - extra): - """Process arguments and decide which source files to compile.""" - if outdir is None: - outdir = self.output_dir - elif not isinstance(outdir, str): - raise TypeError("'output_dir' must be a string or None") - - if macros is None: - macros = self.macros - elif isinstance(macros, list): - macros = macros + (self.macros or []) - else: - raise TypeError("'macros' (if supplied) must be a list of tuples") - - if incdirs is None: - incdirs = self.include_dirs - elif isinstance(incdirs, (list, tuple)): - incdirs = list(incdirs) + (self.include_dirs or []) - else: - raise TypeError( - "'include_dirs' (if supplied) must be a list of strings") - - if extra is None: - extra = [] - - # Get the list of expected output (object) files - objects = self.object_filenames(sources, strip_dir=0, - output_dir=outdir) - assert len(objects) == len(sources) - - pp_opts = gen_preprocess_options(macros, incdirs) - - build = {} - for i in range(len(sources)): - src = sources[i] - obj = objects[i] - ext = os.path.splitext(src)[1] - self.mkpath(os.path.dirname(obj)) - build[obj] = (src, ext) - - return macros, objects, extra, pp_opts, build - - def _get_cc_args(self, pp_opts, debug, before): - # works for unixccompiler, cygwinccompiler - cc_args = pp_opts + ['-c'] - if debug: - cc_args[:0] = ['-g'] - if before: - cc_args[:0] = before - return cc_args - - def _fix_compile_args(self, output_dir, macros, include_dirs): - """Typecheck and fix-up some of the arguments to the 'compile()' - method, and return fixed-up values. Specifically: if 'output_dir' - is None, replaces it with 'self.output_dir'; ensures that 'macros' - is a list, and augments it with 'self.macros'; ensures that - 'include_dirs' is a list, and augments it with 'self.include_dirs'. - Guarantees that the returned values are of the correct type, - i.e. for 'output_dir' either string or None, and for 'macros' and - 'include_dirs' either list or None. - """ - if output_dir is None: - output_dir = self.output_dir - elif not isinstance(output_dir, str): - raise TypeError("'output_dir' must be a string or None") - - if macros is None: - macros = self.macros - elif isinstance(macros, list): - macros = macros + (self.macros or []) - else: - raise TypeError("'macros' (if supplied) must be a list of tuples") - - if include_dirs is None: - include_dirs = self.include_dirs - elif isinstance(include_dirs, (list, tuple)): - include_dirs = list(include_dirs) + (self.include_dirs or []) - else: - raise TypeError( - "'include_dirs' (if supplied) must be a list of strings") - - return output_dir, macros, include_dirs - - def _prep_compile(self, sources, output_dir, depends=None): - """Decide which souce files must be recompiled. - - Determine the list of object files corresponding to 'sources', - and figure out which ones really need to be recompiled. - Return a list of all object files and a dictionary telling - which source files can be skipped. - """ - # Get the list of expected output (object) files - objects = self.object_filenames(sources, output_dir=output_dir) - assert len(objects) == len(sources) - - # Return an empty dict for the "which source files can be skipped" - # return value to preserve API compatibility. - return objects, {} - - def _fix_object_args(self, objects, output_dir): - """Typecheck and fix up some arguments supplied to various methods. - Specifically: ensure that 'objects' is a list; if output_dir is - None, replace with self.output_dir. Return fixed versions of - 'objects' and 'output_dir'. - """ - if not isinstance(objects, (list, tuple)): - raise TypeError("'objects' must be a list or tuple of strings") - objects = list(objects) - - if output_dir is None: - output_dir = self.output_dir - elif not isinstance(output_dir, str): - raise TypeError("'output_dir' must be a string or None") - - return (objects, output_dir) - - def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs): - """Typecheck and fix up some of the arguments supplied to the - 'link_*' methods. Specifically: ensure that all arguments are - lists, and augment them with their permanent versions - (eg. 'self.libraries' augments 'libraries'). Return a tuple with - fixed versions of all arguments. - """ - if libraries is None: - libraries = self.libraries - elif isinstance(libraries, (list, tuple)): - libraries = list (libraries) + (self.libraries or []) - else: - raise TypeError( - "'libraries' (if supplied) must be a list of strings") - - if library_dirs is None: - library_dirs = self.library_dirs - elif isinstance(library_dirs, (list, tuple)): - library_dirs = list (library_dirs) + (self.library_dirs or []) - else: - raise TypeError( - "'library_dirs' (if supplied) must be a list of strings") - - if runtime_library_dirs is None: - runtime_library_dirs = self.runtime_library_dirs - elif isinstance(runtime_library_dirs, (list, tuple)): - runtime_library_dirs = (list(runtime_library_dirs) + - (self.runtime_library_dirs or [])) - else: - raise TypeError("'runtime_library_dirs' (if supplied) " - "must be a list of strings") - - return (libraries, library_dirs, runtime_library_dirs) - - def _need_link(self, objects, output_file): - """Return true if we need to relink the files listed in 'objects' - to recreate 'output_file'. - """ - if self.force: - return True - else: - if self.dry_run: - newer = newer_group (objects, output_file, missing='newer') - else: - newer = newer_group (objects, output_file) - return newer - - def detect_language(self, sources): - """Detect the language of a given file, or list of files. Uses - language_map, and language_order to do the job. - """ - if not isinstance(sources, list): - sources = [sources] - lang = None - index = len(self.language_order) - for source in sources: - base, ext = os.path.splitext(source) - extlang = self.language_map.get(ext) - try: - extindex = self.language_order.index(extlang) - if extindex < index: - lang = extlang - index = extindex - except ValueError: - pass - return lang - - - # -- Worker methods ------------------------------------------------ - # (must be implemented by subclasses) - - def preprocess(self, source, output_file=None, macros=None, - include_dirs=None, extra_preargs=None, extra_postargs=None): - """Preprocess a single C/C++ source file, named in 'source'. - Output will be written to file named 'output_file', or stdout if - 'output_file' not supplied. 'macros' is a list of macro - definitions as for 'compile()', which will augment the macros set - with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a - list of directory names that will be added to the default list. - - Raises PreprocessError on failure. - """ - pass - - def compile(self, sources, output_dir=None, macros=None, - include_dirs=None, debug=0, extra_preargs=None, - extra_postargs=None, depends=None): - """Compile one or more source files. - - 'sources' must be a list of filenames, most likely C/C++ - files, but in reality anything that can be handled by a - particular compiler and compiler class (eg. MSVCCompiler can - handle resource files in 'sources'). Return a list of object - filenames, one per source filename in 'sources'. Depending on - the implementation, not all source files will necessarily be - compiled, but all corresponding object filenames will be - returned. - - If 'output_dir' is given, object files will be put under it, while - retaining their original path component. That is, "foo/bar.c" - normally compiles to "foo/bar.o" (for a Unix implementation); if - 'output_dir' is "build", then it would compile to - "build/foo/bar.o". - - 'macros', if given, must be a list of macro definitions. A macro - definition is either a (name, value) 2-tuple or a (name,) 1-tuple. - The former defines a macro; if the value is None, the macro is - defined without an explicit value. The 1-tuple case undefines a - macro. Later definitions/redefinitions/ undefinitions take - precedence. - - 'include_dirs', if given, must be a list of strings, the - directories to add to the default include file search path for this - compilation only. - - 'debug' is a boolean; if true, the compiler will be instructed to - output debug symbols in (or alongside) the object file(s). - - 'extra_preargs' and 'extra_postargs' are implementation- dependent. - On platforms that have the notion of a command-line (e.g. Unix, - DOS/Windows), they are most likely lists of strings: extra - command-line arguments to prepend/append to the compiler command - line. On other platforms, consult the implementation class - documentation. In any event, they are intended as an escape hatch - for those occasions when the abstract compiler framework doesn't - cut the mustard. - - 'depends', if given, is a list of filenames that all targets - depend on. If a source file is older than any file in - depends, then the source file will be recompiled. This - supports dependency tracking, but only at a coarse - granularity. - - Raises CompileError on failure. - """ - # A concrete compiler class can either override this method - # entirely or implement _compile(). - macros, objects, extra_postargs, pp_opts, build = \ - self._setup_compile(output_dir, macros, include_dirs, sources, - depends, extra_postargs) - cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) - - # Return *all* object filenames, not just the ones we just built. - return objects - - def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): - """Compile 'src' to product 'obj'.""" - # A concrete compiler class that does not override compile() - # should implement _compile(). - pass - - def create_static_lib(self, objects, output_libname, output_dir=None, - debug=0, target_lang=None): - """Link a bunch of stuff together to create a static library file. - The "bunch of stuff" consists of the list of object files supplied - as 'objects', the extra object files supplied to - 'add_link_object()' and/or 'set_link_objects()', the libraries - supplied to 'add_library()' and/or 'set_libraries()', and the - libraries supplied as 'libraries' (if any). - - 'output_libname' should be a library name, not a filename; the - filename will be inferred from the library name. 'output_dir' is - the directory where the library file will be put. - - 'debug' is a boolean; if true, debugging information will be - included in the library (note that on most platforms, it is the - compile step where this matters: the 'debug' flag is included here - just for consistency). - - 'target_lang' is the target language for which the given objects - are being compiled. This allows specific linkage time treatment of - certain languages. - - Raises LibError on failure. - """ - pass - - - # values for target_desc parameter in link() - SHARED_OBJECT = "shared_object" - SHARED_LIBRARY = "shared_library" - EXECUTABLE = "executable" - - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - """Link a bunch of stuff together to create an executable or - shared library file. - - The "bunch of stuff" consists of the list of object files supplied - as 'objects'. 'output_filename' should be a filename. If - 'output_dir' is supplied, 'output_filename' is relative to it - (i.e. 'output_filename' can provide directory components if - needed). - - 'libraries' is a list of libraries to link against. These are - library names, not filenames, since they're translated into - filenames in a platform-specific way (eg. "foo" becomes "libfoo.a" - on Unix and "foo.lib" on DOS/Windows). However, they can include a - directory component, which means the linker will look in that - specific directory rather than searching all the normal locations. - - 'library_dirs', if supplied, should be a list of directories to - search for libraries that were specified as bare library names - (ie. no directory component). These are on top of the system - default and those supplied to 'add_library_dir()' and/or - 'set_library_dirs()'. 'runtime_library_dirs' is a list of - directories that will be embedded into the shared library and used - to search for other shared libraries that *it* depends on at - run-time. (This may only be relevant on Unix.) - - 'export_symbols' is a list of symbols that the shared library will - export. (This appears to be relevant only on Windows.) - - 'debug' is as for 'compile()' and 'create_static_lib()', with the - slight distinction that it actually matters on most platforms (as - opposed to 'create_static_lib()', which includes a 'debug' flag - mostly for form's sake). - - 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except - of course that they supply command-line arguments for the - particular linker being used). - - 'target_lang' is the target language for which the given objects - are being compiled. This allows specific linkage time treatment of - certain languages. - - Raises LinkError on failure. - """ - raise NotImplementedError - - - # Old 'link_*()' methods, rewritten to use the new 'link()' method. - - def link_shared_lib(self, - objects, - output_libname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - self.link(CCompiler.SHARED_LIBRARY, objects, - self.library_filename(output_libname, lib_type='shared'), - output_dir, - libraries, library_dirs, runtime_library_dirs, - export_symbols, debug, - extra_preargs, extra_postargs, build_temp, target_lang) - - - def link_shared_object(self, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - self.link(CCompiler.SHARED_OBJECT, objects, - output_filename, output_dir, - libraries, library_dirs, runtime_library_dirs, - export_symbols, debug, - extra_preargs, extra_postargs, build_temp, target_lang) - - - def link_executable(self, - objects, - output_progname, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - target_lang=None): - self.link(CCompiler.EXECUTABLE, objects, - self.executable_filename(output_progname), output_dir, - libraries, library_dirs, runtime_library_dirs, None, - debug, extra_preargs, extra_postargs, None, target_lang) - - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function; there is - # no appropriate default implementation so subclasses should - # implement all of these. - - def library_dir_option(self, dir): - """Return the compiler option to add 'dir' to the list of - directories searched for libraries. - """ - raise NotImplementedError - - def runtime_library_dir_option(self, dir): - """Return the compiler option to add 'dir' to the list of - directories searched for runtime libraries. - """ - raise NotImplementedError - - def library_option(self, lib): - """Return the compiler option to add 'lib' to the list of libraries - linked into the shared library or executable. - """ - raise NotImplementedError - - def has_function(self, funcname, includes=None, include_dirs=None, - libraries=None, library_dirs=None): - """Return a boolean indicating whether funcname is supported on - the current platform. The optional arguments can be used to - augment the compilation environment. - """ - # this can't be included at module scope because it tries to - # import math which might not be available at that point - maybe - # the necessary logic should just be inlined? - import tempfile - if includes is None: - includes = [] - if include_dirs is None: - include_dirs = [] - if libraries is None: - libraries = [] - if library_dirs is None: - library_dirs = [] - fd, fname = tempfile.mkstemp(".c", funcname, text=True) - f = os.fdopen(fd, "w") - try: - for incl in includes: - f.write("""#include "%s"\n""" % incl) - f.write("""\ -int main (int argc, char **argv) { - %s(); - return 0; -} -""" % funcname) - finally: - f.close() - try: - objects = self.compile([fname], include_dirs=include_dirs) - except CompileError: - return False - - try: - self.link_executable(objects, "a.out", - libraries=libraries, - library_dirs=library_dirs) - except (LinkError, TypeError): - return False - return True - - def find_library_file (self, dirs, lib, debug=0): - """Search the specified list of directories for a static or shared - library file 'lib' and return the full path to that file. If - 'debug' true, look for a debugging version (if that makes sense on - the current platform). Return None if 'lib' wasn't found in any of - the specified directories. - """ - raise NotImplementedError - - # -- Filename generation methods ----------------------------------- - - # The default implementation of the filename generating methods are - # prejudiced towards the Unix/DOS/Windows view of the world: - # * object files are named by replacing the source file extension - # (eg. .c/.cpp -> .o/.obj) - # * library files (shared or static) are named by plugging the - # library name and extension into a format string, eg. - # "lib%s.%s" % (lib_name, ".a") for Unix static libraries - # * executables are named by appending an extension (possibly - # empty) to the program name: eg. progname + ".exe" for - # Windows - # - # To reduce redundant code, these methods expect to find - # several attributes in the current object (presumably defined - # as class attributes): - # * src_extensions - - # list of C/C++ source file extensions, eg. ['.c', '.cpp'] - # * obj_extension - - # object file extension, eg. '.o' or '.obj' - # * static_lib_extension - - # extension for static library files, eg. '.a' or '.lib' - # * shared_lib_extension - - # extension for shared library/object files, eg. '.so', '.dll' - # * static_lib_format - - # format string for generating static library filenames, - # eg. 'lib%s.%s' or '%s.%s' - # * shared_lib_format - # format string for generating shared library filenames - # (probably same as static_lib_format, since the extension - # is one of the intended parameters to the format string) - # * exe_extension - - # extension for executable files, eg. '' or '.exe' - - def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): - if output_dir is None: - output_dir = '' - obj_names = [] - for src_name in source_filenames: - base, ext = os.path.splitext(src_name) - base = os.path.splitdrive(base)[1] # Chop off the drive - base = base[os.path.isabs(base):] # If abs, chop off leading / - if ext not in self.src_extensions: - raise UnknownFileError( - "unknown file type '%s' (from '%s')" % (ext, src_name)) - if strip_dir: - base = os.path.basename(base) - obj_names.append(os.path.join(output_dir, - base + self.obj_extension)) - return obj_names - - def shared_object_filename(self, basename, strip_dir=0, output_dir=''): - assert output_dir is not None - if strip_dir: - basename = os.path.basename(basename) - return os.path.join(output_dir, basename + self.shared_lib_extension) - - def executable_filename(self, basename, strip_dir=0, output_dir=''): - assert output_dir is not None - if strip_dir: - basename = os.path.basename(basename) - return os.path.join(output_dir, basename + (self.exe_extension or '')) - - def library_filename(self, libname, lib_type='static', # or 'shared' - strip_dir=0, output_dir=''): - assert output_dir is not None - if lib_type not in ("static", "shared", "dylib", "xcode_stub"): - raise ValueError( - "'lib_type' must be \"static\", \"shared\", \"dylib\", or \"xcode_stub\"") - fmt = getattr(self, lib_type + "_lib_format") - ext = getattr(self, lib_type + "_lib_extension") - - dir, base = os.path.split(libname) - filename = fmt % (base, ext) - if strip_dir: - dir = '' - - return os.path.join(output_dir, dir, filename) - - - # -- Utility methods ----------------------------------------------- - - def announce(self, msg, level=1): - log.debug(msg) - - def debug_print(self, msg): - from distutils.debug import DEBUG - if DEBUG: - print(msg) - - def warn(self, msg): - sys.stderr.write("warning: %s\n" % msg) - - def execute(self, func, args, msg=None, level=1): - execute(func, args, msg, self.dry_run) - - def spawn(self, cmd): - spawn(cmd, dry_run=self.dry_run) - - def move_file(self, src, dst): - return move_file(src, dst, dry_run=self.dry_run) - - def mkpath (self, name, mode=0o777): - mkpath(name, mode, dry_run=self.dry_run) - - -# Map a sys.platform/os.name ('posix', 'nt') to the default compiler -# type for that platform. Keys are interpreted as re match -# patterns. Order is important; platform mappings are preferred over -# OS names. -_default_compilers = ( - - # Platform string mappings - - # on a cygwin built python we can use gcc like an ordinary UNIXish - # compiler - ('cygwin.*', 'unix'), - - # OS name mappings - ('posix', 'unix'), - ('nt', 'msvc'), - - ) - -def get_default_compiler(osname=None, platform=None): - """Determine the default compiler to use for the given platform. - - osname should be one of the standard Python OS names (i.e. the - ones returned by os.name) and platform the common value - returned by sys.platform for the platform in question. - - The default values are os.name and sys.platform in case the - parameters are not given. - """ - if osname is None: - osname = os.name - if platform is None: - platform = sys.platform - for pattern, compiler in _default_compilers: - if re.match(pattern, platform) is not None or \ - re.match(pattern, osname) is not None: - return compiler - # Default to Unix compiler - return 'unix' - -# Map compiler types to (module_name, class_name) pairs -- ie. where to -# find the code that implements an interface to this compiler. (The module -# is assumed to be in the 'distutils' package.) -compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', - "standard UNIX-style compiler"), - 'msvc': ('_msvccompiler', 'MSVCCompiler', - "Microsoft Visual C++"), - 'cygwin': ('cygwinccompiler', 'CygwinCCompiler', - "Cygwin port of GNU C Compiler for Win32"), - 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler', - "Mingw32 port of GNU C Compiler for Win32"), - 'bcpp': ('bcppcompiler', 'BCPPCompiler', - "Borland C++ Compiler"), - } - -def show_compilers(): - """Print list of available compilers (used by the "--help-compiler" - options to "build", "build_ext", "build_clib"). - """ - # XXX this "knows" that the compiler option it's describing is - # "--compiler", which just happens to be the case for the three - # commands that use it. - from distutils.fancy_getopt import FancyGetopt - compilers = [] - for compiler in compiler_class.keys(): - compilers.append(("compiler="+compiler, None, - compiler_class[compiler][2])) - compilers.sort() - pretty_printer = FancyGetopt(compilers) - pretty_printer.print_help("List of available compilers:") - - -def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0): - """Generate an instance of some CCompiler subclass for the supplied - platform/compiler combination. 'plat' defaults to 'os.name' - (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler - for that platform. Currently only 'posix' and 'nt' are supported, and - the default compilers are "traditional Unix interface" (UnixCCompiler - class) and Visual C++ (MSVCCompiler class). Note that it's perfectly - possible to ask for a Unix compiler object under Windows, and a - Microsoft compiler object under Unix -- if you supply a value for - 'compiler', 'plat' is ignored. - """ - if plat is None: - plat = os.name - - try: - if compiler is None: - compiler = get_default_compiler(plat) - - (module_name, class_name, long_description) = compiler_class[compiler] - except KeyError: - msg = "don't know how to compile C/C++ code on platform '%s'" % plat - if compiler is not None: - msg = msg + " with '%s' compiler" % compiler - raise DistutilsPlatformError(msg) - - try: - module_name = "distutils." + module_name - __import__ (module_name) - module = sys.modules[module_name] - klass = vars(module)[class_name] - except ImportError: - raise DistutilsModuleError( - "can't compile C/C++ code: unable to load module '%s'" % \ - module_name) - except KeyError: - raise DistutilsModuleError( - "can't compile C/C++ code: unable to find class '%s' " - "in module '%s'" % (class_name, module_name)) - - # XXX The None is necessary to preserve backwards compatibility - # with classes that expect verbose to be the first positional - # argument. - return klass(None, dry_run, force) - - -def gen_preprocess_options(macros, include_dirs): - """Generate C pre-processor options (-D, -U, -I) as used by at least - two types of compilers: the typical Unix compiler and Visual C++. - 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,) - means undefine (-U) macro 'name', and (name,value) means define (-D) - macro 'name' to 'value'. 'include_dirs' is just a list of directory - names to be added to the header file search path (-I). Returns a list - of command-line options suitable for either Unix compilers or Visual - C++. - """ - # XXX it would be nice (mainly aesthetic, and so we don't generate - # stupid-looking command lines) to go over 'macros' and eliminate - # redundant definitions/undefinitions (ie. ensure that only the - # latest mention of a particular macro winds up on the command - # line). I don't think it's essential, though, since most (all?) - # Unix C compilers only pay attention to the latest -D or -U - # mention of a macro on their command line. Similar situation for - # 'include_dirs'. I'm punting on both for now. Anyways, weeding out - # redundancies like this should probably be the province of - # CCompiler, since the data structures used are inherited from it - # and therefore common to all CCompiler classes. - pp_opts = [] - for macro in macros: - if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2): - raise TypeError( - "bad macro definition '%s': " - "each element of 'macros' list must be a 1- or 2-tuple" - % macro) - - if len(macro) == 1: # undefine this macro - pp_opts.append("-U%s" % macro[0]) - elif len(macro) == 2: - if macro[1] is None: # define with no explicit value - pp_opts.append("-D%s" % macro[0]) - else: - # XXX *don't* need to be clever about quoting the - # macro value here, because we're going to avoid the - # shell at all costs when we spawn the command! - pp_opts.append("-D%s=%s" % macro) - - for dir in include_dirs: - pp_opts.append("-I%s" % dir) - return pp_opts - - -def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): - """Generate linker options for searching library directories and - linking with specific libraries. 'libraries' and 'library_dirs' are, - respectively, lists of library names (not filenames!) and search - directories. Returns a list of command-line options suitable for use - with some compiler (depending on the two format strings passed in). - """ - lib_opts = [] - - for dir in library_dirs: - lib_opts.append(compiler.library_dir_option(dir)) - - for dir in runtime_library_dirs: - opt = compiler.runtime_library_dir_option(dir) - if isinstance(opt, list): - lib_opts = lib_opts + opt - else: - lib_opts.append(opt) - - # XXX it's important that we *not* remove redundant library mentions! - # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to - # resolve all symbols. I just hope we never have to say "-lfoo obj.o - # -lbar" to get things to work -- that's certainly a possibility, but a - # pretty nasty way to arrange your C code. - - for lib in libraries: - (lib_dir, lib_name) = os.path.split(lib) - if lib_dir: - lib_file = compiler.find_library_file([lib_dir], lib_name) - if lib_file: - lib_opts.append(lib_file) - else: - compiler.warn("no library file corresponding to " - "'%s' found (skipping)" % lib) - else: - lib_opts.append(compiler.library_option (lib)) - return lib_opts diff --git a/distutils/cmd.py b/distutils/cmd.py deleted file mode 100644 index dba3191e..00000000 --- a/distutils/cmd.py +++ /dev/null @@ -1,403 +0,0 @@ -"""distutils.cmd - -Provides the Command class, the base class for the command classes -in the distutils.command package. -""" - -import sys, os, re -from distutils.errors import DistutilsOptionError -from distutils import util, dir_util, file_util, archive_util, dep_util -from distutils import log - -class Command: - """Abstract base class for defining command classes, the "worker bees" - of the Distutils. A useful analogy for command classes is to think of - them as subroutines with local variables called "options". The options - are "declared" in 'initialize_options()' and "defined" (given their - final values, aka "finalized") in 'finalize_options()', both of which - must be defined by every command class. The distinction between the - two is necessary because option values might come from the outside - world (command line, config file, ...), and any options dependent on - other options must be computed *after* these outside influences have - been processed -- hence 'finalize_options()'. The "body" of the - subroutine, where it does all its work based on the values of its - options, is the 'run()' method, which must also be implemented by every - command class. - """ - - # 'sub_commands' formalizes the notion of a "family" of commands, - # eg. "install" as the parent with sub-commands "install_lib", - # "install_headers", etc. The parent of a family of commands - # defines 'sub_commands' as a class attribute; it's a list of - # (command_name : string, predicate : unbound_method | string | None) - # tuples, where 'predicate' is a method of the parent command that - # determines whether the corresponding command is applicable in the - # current situation. (Eg. we "install_headers" is only applicable if - # we have any C header files to install.) If 'predicate' is None, - # that command is always applicable. - # - # 'sub_commands' is usually defined at the *end* of a class, because - # predicates can be unbound methods, so they must already have been - # defined. The canonical example is the "install" command. - sub_commands = [] - - - # -- Creation/initialization methods ------------------------------- - - def __init__(self, dist): - """Create and initialize a new Command object. Most importantly, - invokes the 'initialize_options()' method, which is the real - initializer and depends on the actual command being - instantiated. - """ - # late import because of mutual dependence between these classes - from distutils.dist import Distribution - - if not isinstance(dist, Distribution): - raise TypeError("dist must be a Distribution instance") - if self.__class__ is Command: - raise RuntimeError("Command is an abstract class") - - self.distribution = dist - self.initialize_options() - - # Per-command versions of the global flags, so that the user can - # customize Distutils' behaviour command-by-command and let some - # commands fall back on the Distribution's behaviour. None means - # "not defined, check self.distribution's copy", while 0 or 1 mean - # false and true (duh). Note that this means figuring out the real - # value of each flag is a touch complicated -- hence "self._dry_run" - # will be handled by __getattr__, below. - # XXX This needs to be fixed. - self._dry_run = None - - # verbose is largely ignored, but needs to be set for - # backwards compatibility (I think)? - self.verbose = dist.verbose - - # Some commands define a 'self.force' option to ignore file - # timestamps, but methods defined *here* assume that - # 'self.force' exists for all commands. So define it here - # just to be safe. - self.force = None - - # The 'help' flag is just used for command-line parsing, so - # none of that complicated bureaucracy is needed. - self.help = 0 - - # 'finalized' records whether or not 'finalize_options()' has been - # called. 'finalize_options()' itself should not pay attention to - # this flag: it is the business of 'ensure_finalized()', which - # always calls 'finalize_options()', to respect/update it. - self.finalized = 0 - - # XXX A more explicit way to customize dry_run would be better. - def __getattr__(self, attr): - if attr == 'dry_run': - myval = getattr(self, "_" + attr) - if myval is None: - return getattr(self.distribution, attr) - else: - return myval - else: - raise AttributeError(attr) - - def ensure_finalized(self): - if not self.finalized: - self.finalize_options() - self.finalized = 1 - - # Subclasses must define: - # initialize_options() - # provide default values for all options; may be customized by - # setup script, by options from config file(s), or by command-line - # options - # finalize_options() - # decide on the final values for all options; this is called - # after all possible intervention from the outside world - # (command-line, option file, etc.) has been processed - # run() - # run the command: do whatever it is we're here to do, - # controlled by the command's various option values - - def initialize_options(self): - """Set default values for all the options that this command - supports. Note that these defaults may be overridden by other - commands, by the setup script, by config files, or by the - command-line. Thus, this is not the place to code dependencies - between options; generally, 'initialize_options()' implementations - are just a bunch of "self.foo = None" assignments. - - This method must be implemented by all command classes. - """ - raise RuntimeError("abstract method -- subclass %s must override" - % self.__class__) - - def finalize_options(self): - """Set final values for all the options that this command supports. - This is always called as late as possible, ie. after any option - assignments from the command-line or from other commands have been - done. Thus, this is the place to code option dependencies: if - 'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as - long as 'foo' still has the same value it was assigned in - 'initialize_options()'. - - This method must be implemented by all command classes. - """ - raise RuntimeError("abstract method -- subclass %s must override" - % self.__class__) - - - def dump_options(self, header=None, indent=""): - from distutils.fancy_getopt import longopt_xlate - if header is None: - header = "command options for '%s':" % self.get_command_name() - self.announce(indent + header, level=log.INFO) - indent = indent + " " - for (option, _, _) in self.user_options: - option = option.translate(longopt_xlate) - if option[-1] == "=": - option = option[:-1] - value = getattr(self, option) - self.announce(indent + "%s = %s" % (option, value), - level=log.INFO) - - def run(self): - """A command's raison d'etre: carry out the action it exists to - perform, controlled by the options initialized in - 'initialize_options()', customized by other commands, the setup - script, the command-line, and config files, and finalized in - 'finalize_options()'. All terminal output and filesystem - interaction should be done by 'run()'. - - This method must be implemented by all command classes. - """ - raise RuntimeError("abstract method -- subclass %s must override" - % self.__class__) - - def announce(self, msg, level=1): - """If the current verbosity level is of greater than or equal to - 'level' print 'msg' to stdout. - """ - log.log(level, msg) - - def debug_print(self, msg): - """Print 'msg' to stdout if the global DEBUG (taken from the - DISTUTILS_DEBUG environment variable) flag is true. - """ - from distutils.debug import DEBUG - if DEBUG: - print(msg) - sys.stdout.flush() - - - # -- Option validation methods ------------------------------------- - # (these are very handy in writing the 'finalize_options()' method) - # - # NB. the general philosophy here is to ensure that a particular option - # value meets certain type and value constraints. If not, we try to - # force it into conformance (eg. if we expect a list but have a string, - # split the string on comma and/or whitespace). If we can't force the - # option into conformance, raise DistutilsOptionError. Thus, command - # classes need do nothing more than (eg.) - # self.ensure_string_list('foo') - # and they can be guaranteed that thereafter, self.foo will be - # a list of strings. - - def _ensure_stringlike(self, option, what, default=None): - val = getattr(self, option) - if val is None: - setattr(self, option, default) - return default - elif not isinstance(val, str): - raise DistutilsOptionError("'%s' must be a %s (got `%s`)" - % (option, what, val)) - return val - - def ensure_string(self, option, default=None): - """Ensure that 'option' is a string; if not defined, set it to - 'default'. - """ - self._ensure_stringlike(option, "string", default) - - def ensure_string_list(self, option): - r"""Ensure that 'option' is a list of strings. If 'option' is - currently a string, we split it either on /,\s*/ or /\s+/, so - "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become - ["foo", "bar", "baz"]. - """ - val = getattr(self, option) - if val is None: - return - elif isinstance(val, str): - setattr(self, option, re.split(r',\s*|\s+', val)) - else: - if isinstance(val, list): - ok = all(isinstance(v, str) for v in val) - else: - ok = False - if not ok: - raise DistutilsOptionError( - "'%s' must be a list of strings (got %r)" - % (option, val)) - - def _ensure_tested_string(self, option, tester, what, error_fmt, - default=None): - val = self._ensure_stringlike(option, what, default) - if val is not None and not tester(val): - raise DistutilsOptionError(("error in '%s' option: " + error_fmt) - % (option, val)) - - def ensure_filename(self, option): - """Ensure that 'option' is the name of an existing file.""" - self._ensure_tested_string(option, os.path.isfile, - "filename", - "'%s' does not exist or is not a file") - - def ensure_dirname(self, option): - self._ensure_tested_string(option, os.path.isdir, - "directory name", - "'%s' does not exist or is not a directory") - - - # -- Convenience methods for commands ------------------------------ - - def get_command_name(self): - if hasattr(self, 'command_name'): - return self.command_name - else: - return self.__class__.__name__ - - def set_undefined_options(self, src_cmd, *option_pairs): - """Set the values of any "undefined" options from corresponding - option values in some other command object. "Undefined" here means - "is None", which is the convention used to indicate that an option - has not been changed between 'initialize_options()' and - 'finalize_options()'. Usually called from 'finalize_options()' for - options that depend on some other command rather than another - option of the same command. 'src_cmd' is the other command from - which option values will be taken (a command object will be created - for it if necessary); the remaining arguments are - '(src_option,dst_option)' tuples which mean "take the value of - 'src_option' in the 'src_cmd' command object, and copy it to - 'dst_option' in the current command object". - """ - # Option_pairs: list of (src_option, dst_option) tuples - src_cmd_obj = self.distribution.get_command_obj(src_cmd) - src_cmd_obj.ensure_finalized() - for (src_option, dst_option) in option_pairs: - if getattr(self, dst_option) is None: - setattr(self, dst_option, getattr(src_cmd_obj, src_option)) - - def get_finalized_command(self, command, create=1): - """Wrapper around Distribution's 'get_command_obj()' method: find - (create if necessary and 'create' is true) the command object for - 'command', call its 'ensure_finalized()' method, and return the - finalized command object. - """ - cmd_obj = self.distribution.get_command_obj(command, create) - cmd_obj.ensure_finalized() - return cmd_obj - - # XXX rename to 'get_reinitialized_command()'? (should do the - # same in dist.py, if so) - def reinitialize_command(self, command, reinit_subcommands=0): - return self.distribution.reinitialize_command(command, - reinit_subcommands) - - def run_command(self, command): - """Run some other command: uses the 'run_command()' method of - Distribution, which creates and finalizes the command object if - necessary and then invokes its 'run()' method. - """ - self.distribution.run_command(command) - - def get_sub_commands(self): - """Determine the sub-commands that are relevant in the current - distribution (ie., that need to be run). This is based on the - 'sub_commands' class attribute: each tuple in that list may include - a method that we call to determine if the subcommand needs to be - run for the current distribution. Return a list of command names. - """ - commands = [] - for (cmd_name, method) in self.sub_commands: - if method is None or method(self): - commands.append(cmd_name) - return commands - - - # -- External world manipulation ----------------------------------- - - def warn(self, msg): - log.warn("warning: %s: %s\n", self.get_command_name(), msg) - - def execute(self, func, args, msg=None, level=1): - util.execute(func, args, msg, dry_run=self.dry_run) - - def mkpath(self, name, mode=0o777): - dir_util.mkpath(name, mode, dry_run=self.dry_run) - - def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1, - link=None, level=1): - """Copy a file respecting verbose, dry-run and force flags. (The - former two default to whatever is in the Distribution object, and - the latter defaults to false for commands that don't define it.)""" - return file_util.copy_file(infile, outfile, preserve_mode, - preserve_times, not self.force, link, - dry_run=self.dry_run) - - def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1, - preserve_symlinks=0, level=1): - """Copy an entire directory tree respecting verbose, dry-run, - and force flags. - """ - return dir_util.copy_tree(infile, outfile, preserve_mode, - preserve_times, preserve_symlinks, - not self.force, dry_run=self.dry_run) - - def move_file (self, src, dst, level=1): - """Move a file respecting dry-run flag.""" - return file_util.move_file(src, dst, dry_run=self.dry_run) - - def spawn(self, cmd, search_path=1, level=1): - """Spawn an external command respecting dry-run flag.""" - from distutils.spawn import spawn - spawn(cmd, search_path, dry_run=self.dry_run) - - def make_archive(self, base_name, format, root_dir=None, base_dir=None, - owner=None, group=None): - return archive_util.make_archive(base_name, format, root_dir, base_dir, - dry_run=self.dry_run, - owner=owner, group=group) - - def make_file(self, infiles, outfile, func, args, - exec_msg=None, skip_msg=None, level=1): - """Special case of 'execute()' for operations that process one or - more input files and generate one output file. Works just like - 'execute()', except the operation is skipped and a different - message printed if 'outfile' already exists and is newer than all - files listed in 'infiles'. If the command defined 'self.force', - and it is true, then the command is unconditionally run -- does no - timestamp checks. - """ - if skip_msg is None: - skip_msg = "skipping %s (inputs unchanged)" % outfile - - # Allow 'infiles' to be a single string - if isinstance(infiles, str): - infiles = (infiles,) - elif not isinstance(infiles, (list, tuple)): - raise TypeError( - "'infiles' must be a string, or a list or tuple of strings") - - if exec_msg is None: - exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles)) - - # If 'outfile' must be regenerated (either because it doesn't - # exist, is out-of-date, or the 'force' flag is true) then - # perform the action that presumably regenerates it - if self.force or dep_util.newer_group(infiles, outfile): - self.execute(func, args, exec_msg, level) - # Otherwise, print the "skip" message - else: - log.debug(skip_msg) diff --git a/distutils/command/__init__.py b/distutils/command/__init__.py deleted file mode 100644 index 481eea9f..00000000 --- a/distutils/command/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -"""distutils.command - -Package containing implementation of all the standard Distutils -commands.""" - -__all__ = ['build', - 'build_py', - 'build_ext', - 'build_clib', - 'build_scripts', - 'clean', - 'install', - 'install_lib', - 'install_headers', - 'install_scripts', - 'install_data', - 'sdist', - 'register', - 'bdist', - 'bdist_dumb', - 'bdist_rpm', - 'bdist_wininst', - 'check', - 'upload', - # These two are reserved for future use: - #'bdist_sdux', - #'bdist_pkgtool', - # Note: - # bdist_packager is not included because it only provides - # an abstract base class - ] diff --git a/distutils/command/bdist.py b/distutils/command/bdist.py deleted file mode 100644 index 014871d2..00000000 --- a/distutils/command/bdist.py +++ /dev/null @@ -1,143 +0,0 @@ -"""distutils.command.bdist - -Implements the Distutils 'bdist' command (create a built [binary] -distribution).""" - -import os -from distutils.core import Command -from distutils.errors import * -from distutils.util import get_platform - - -def show_formats(): - """Print list of available formats (arguments to "--format" option). - """ - from distutils.fancy_getopt import FancyGetopt - formats = [] - for format in bdist.format_commands: - formats.append(("formats=" + format, None, - bdist.format_command[format][1])) - pretty_printer = FancyGetopt(formats) - pretty_printer.print_help("List of available distribution formats:") - - -class bdist(Command): - - description = "create a built (binary) distribution" - - user_options = [('bdist-base=', 'b', - "temporary directory for creating built distributions"), - ('plat-name=', 'p', - "platform name to embed in generated filenames " - "(default: %s)" % get_platform()), - ('formats=', None, - "formats for distribution (comma-separated list)"), - ('dist-dir=', 'd', - "directory to put final built distributions in " - "[default: dist]"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ('owner=', 'u', - "Owner name used when creating a tar file" - " [default: current user]"), - ('group=', 'g', - "Group name used when creating a tar file" - " [default: current group]"), - ] - - boolean_options = ['skip-build'] - - help_options = [ - ('help-formats', None, - "lists available distribution formats", show_formats), - ] - - # The following commands do not take a format option from bdist - no_format_option = ('bdist_rpm',) - - # This won't do in reality: will need to distinguish RPM-ish Linux, - # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. - default_format = {'posix': 'gztar', - 'nt': 'zip'} - - # Establish the preferred order (for the --help-formats option). - format_commands = ['rpm', 'gztar', 'bztar', 'xztar', 'ztar', 'tar', - 'wininst', 'zip', 'msi'] - - # And the real information. - format_command = {'rpm': ('bdist_rpm', "RPM distribution"), - 'gztar': ('bdist_dumb', "gzip'ed tar file"), - 'bztar': ('bdist_dumb', "bzip2'ed tar file"), - 'xztar': ('bdist_dumb', "xz'ed tar file"), - 'ztar': ('bdist_dumb', "compressed tar file"), - 'tar': ('bdist_dumb', "tar file"), - 'wininst': ('bdist_wininst', - "Windows executable installer"), - 'zip': ('bdist_dumb', "ZIP file"), - 'msi': ('bdist_msi', "Microsoft Installer") - } - - - def initialize_options(self): - self.bdist_base = None - self.plat_name = None - self.formats = None - self.dist_dir = None - self.skip_build = 0 - self.group = None - self.owner = None - - def finalize_options(self): - # have to finalize 'plat_name' before 'bdist_base' - if self.plat_name is None: - if self.skip_build: - self.plat_name = get_platform() - else: - self.plat_name = self.get_finalized_command('build').plat_name - - # 'bdist_base' -- parent of per-built-distribution-format - # temporary directories (eg. we'll probably have - # "build/bdist./dumb", "build/bdist./rpm", etc.) - if self.bdist_base is None: - build_base = self.get_finalized_command('build').build_base - self.bdist_base = os.path.join(build_base, - 'bdist.' + self.plat_name) - - self.ensure_string_list('formats') - if self.formats is None: - try: - self.formats = [self.default_format[os.name]] - except KeyError: - raise DistutilsPlatformError( - "don't know how to create built distributions " - "on platform %s" % os.name) - - if self.dist_dir is None: - self.dist_dir = "dist" - - def run(self): - # Figure out which sub-commands we need to run. - commands = [] - for format in self.formats: - try: - commands.append(self.format_command[format][0]) - except KeyError: - raise DistutilsOptionError("invalid format '%s'" % format) - - # Reinitialize and run each command. - for i in range(len(self.formats)): - cmd_name = commands[i] - sub_cmd = self.reinitialize_command(cmd_name) - if cmd_name not in self.no_format_option: - sub_cmd.format = self.formats[i] - - # passing the owner and group names for tar archiving - if cmd_name == 'bdist_dumb': - sub_cmd.owner = self.owner - sub_cmd.group = self.group - - # If we're going to need to run this command again, tell it to - # keep its temporary files around so subsequent runs go faster. - if cmd_name in commands[i+1:]: - sub_cmd.keep_temp = 1 - self.run_command(cmd_name) diff --git a/distutils/command/bdist_dumb.py b/distutils/command/bdist_dumb.py deleted file mode 100644 index f0d6b5b8..00000000 --- a/distutils/command/bdist_dumb.py +++ /dev/null @@ -1,123 +0,0 @@ -"""distutils.command.bdist_dumb - -Implements the Distutils 'bdist_dumb' command (create a "dumb" built -distribution -- i.e., just an archive to be unpacked under $prefix or -$exec_prefix).""" - -import os -from distutils.core import Command -from distutils.util import get_platform -from distutils.dir_util import remove_tree, ensure_relative -from distutils.errors import * -from distutils.sysconfig import get_python_version -from distutils import log - -class bdist_dumb(Command): - - description = "create a \"dumb\" built distribution" - - user_options = [('bdist-dir=', 'd', - "temporary directory for creating the distribution"), - ('plat-name=', 'p', - "platform name to embed in generated filenames " - "(default: %s)" % get_platform()), - ('format=', 'f', - "archive format to create (tar, gztar, bztar, xztar, " - "ztar, zip)"), - ('keep-temp', 'k', - "keep the pseudo-installation tree around after " + - "creating the distribution archive"), - ('dist-dir=', 'd', - "directory to put final built distributions in"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ('relative', None, - "build the archive using relative paths " - "(default: false)"), - ('owner=', 'u', - "Owner name used when creating a tar file" - " [default: current user]"), - ('group=', 'g', - "Group name used when creating a tar file" - " [default: current group]"), - ] - - boolean_options = ['keep-temp', 'skip-build', 'relative'] - - default_format = { 'posix': 'gztar', - 'nt': 'zip' } - - def initialize_options(self): - self.bdist_dir = None - self.plat_name = None - self.format = None - self.keep_temp = 0 - self.dist_dir = None - self.skip_build = None - self.relative = 0 - self.owner = None - self.group = None - - def finalize_options(self): - if self.bdist_dir is None: - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'dumb') - - if self.format is None: - try: - self.format = self.default_format[os.name] - except KeyError: - raise DistutilsPlatformError( - "don't know how to create dumb built distributions " - "on platform %s" % os.name) - - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ('skip_build', 'skip_build')) - - def run(self): - if not self.skip_build: - self.run_command('build') - - install = self.reinitialize_command('install', reinit_subcommands=1) - install.root = self.bdist_dir - install.skip_build = self.skip_build - install.warn_dir = 0 - - log.info("installing to %s", self.bdist_dir) - self.run_command('install') - - # And make an archive relative to the root of the - # pseudo-installation tree. - archive_basename = "%s.%s" % (self.distribution.get_fullname(), - self.plat_name) - - pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) - if not self.relative: - archive_root = self.bdist_dir - else: - if (self.distribution.has_ext_modules() and - (install.install_base != install.install_platbase)): - raise DistutilsPlatformError( - "can't make a dumb built distribution where " - "base and platbase are different (%s, %s)" - % (repr(install.install_base), - repr(install.install_platbase))) - else: - archive_root = os.path.join(self.bdist_dir, - ensure_relative(install.install_base)) - - # Make the archive - filename = self.make_archive(pseudoinstall_root, - self.format, root_dir=archive_root, - owner=self.owner, group=self.group) - if self.distribution.has_ext_modules(): - pyversion = get_python_version() - else: - pyversion = 'any' - self.distribution.dist_files.append(('bdist_dumb', pyversion, - filename)) - - if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) diff --git a/distutils/command/bdist_msi.py b/distutils/command/bdist_msi.py deleted file mode 100644 index 0863a188..00000000 --- a/distutils/command/bdist_msi.py +++ /dev/null @@ -1,749 +0,0 @@ -# Copyright (C) 2005, 2006 Martin von Löwis -# Licensed to PSF under a Contributor Agreement. -# The bdist_wininst command proper -# based on bdist_wininst -""" -Implements the bdist_msi command. -""" - -import os -import sys -import warnings -from distutils.core import Command -from distutils.dir_util import remove_tree -from distutils.sysconfig import get_python_version -from distutils.version import StrictVersion -from distutils.errors import DistutilsOptionError -from distutils.util import get_platform -from distutils import log -import msilib -from msilib import schema, sequence, text -from msilib import Directory, Feature, Dialog, add_data - -class PyDialog(Dialog): - """Dialog class with a fixed layout: controls at the top, then a ruler, - then a list of buttons: back, next, cancel. Optionally a bitmap at the - left.""" - def __init__(self, *args, **kw): - """Dialog(database, name, x, y, w, h, attributes, title, first, - default, cancel, bitmap=true)""" - Dialog.__init__(self, *args) - ruler = self.h - 36 - bmwidth = 152*ruler/328 - #if kw.get("bitmap", True): - # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin") - self.line("BottomLine", 0, ruler, self.w, 0) - - def title(self, title): - "Set the title text of the dialog at the top." - # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix, - # text, in VerdanaBold10 - self.text("Title", 15, 10, 320, 60, 0x30003, - r"{\VerdanaBold10}%s" % title) - - def back(self, title, next, name = "Back", active = 1): - """Add a back button with a given title, the tab-next button, - its name in the Control table, possibly initially disabled. - - Return the button, so that events can be associated""" - if active: - flags = 3 # Visible|Enabled - else: - flags = 1 # Visible - return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next) - - def cancel(self, title, next, name = "Cancel", active = 1): - """Add a cancel button with a given title, the tab-next button, - its name in the Control table, possibly initially disabled. - - Return the button, so that events can be associated""" - if active: - flags = 3 # Visible|Enabled - else: - flags = 1 # Visible - return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next) - - def next(self, title, next, name = "Next", active = 1): - """Add a Next button with a given title, the tab-next button, - its name in the Control table, possibly initially disabled. - - Return the button, so that events can be associated""" - if active: - flags = 3 # Visible|Enabled - else: - flags = 1 # Visible - return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next) - - def xbutton(self, name, title, next, xpos): - """Add a button with a given title, the tab-next button, - its name in the Control table, giving its x position; the - y-position is aligned with the other buttons. - - Return the button, so that events can be associated""" - return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next) - -class bdist_msi(Command): - - description = "create a Microsoft Installer (.msi) binary distribution" - - user_options = [('bdist-dir=', None, - "temporary directory for creating the distribution"), - ('plat-name=', 'p', - "platform name to embed in generated filenames " - "(default: %s)" % get_platform()), - ('keep-temp', 'k', - "keep the pseudo-installation tree around after " + - "creating the distribution archive"), - ('target-version=', None, - "require a specific python version" + - " on the target system"), - ('no-target-compile', 'c', - "do not compile .py to .pyc on the target system"), - ('no-target-optimize', 'o', - "do not compile .py to .pyo (optimized) " - "on the target system"), - ('dist-dir=', 'd', - "directory to put final built distributions in"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ('install-script=', None, - "basename of installation script to be run after " - "installation or before deinstallation"), - ('pre-install-script=', None, - "Fully qualified filename of a script to be run before " - "any files are installed. This script need not be in the " - "distribution"), - ] - - boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', - 'skip-build'] - - all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4', - '2.5', '2.6', '2.7', '2.8', '2.9', - '3.0', '3.1', '3.2', '3.3', '3.4', - '3.5', '3.6', '3.7', '3.8', '3.9'] - other_version = 'X' - - def __init__(self, *args, **kw): - super().__init__(*args, **kw) - warnings.warn("bdist_msi command is deprecated since Python 3.9, " - "use bdist_wheel (wheel packages) instead", - DeprecationWarning, 2) - - def initialize_options(self): - self.bdist_dir = None - self.plat_name = None - self.keep_temp = 0 - self.no_target_compile = 0 - self.no_target_optimize = 0 - self.target_version = None - self.dist_dir = None - self.skip_build = None - self.install_script = None - self.pre_install_script = None - self.versions = None - - def finalize_options(self): - self.set_undefined_options('bdist', ('skip_build', 'skip_build')) - - if self.bdist_dir is None: - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'msi') - - short_version = get_python_version() - if (not self.target_version) and self.distribution.has_ext_modules(): - self.target_version = short_version - - if self.target_version: - self.versions = [self.target_version] - if not self.skip_build and self.distribution.has_ext_modules()\ - and self.target_version != short_version: - raise DistutilsOptionError( - "target version can only be %s, or the '--skip-build'" - " option must be specified" % (short_version,)) - else: - self.versions = list(self.all_versions) - - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ) - - if self.pre_install_script: - raise DistutilsOptionError( - "the pre-install-script feature is not yet implemented") - - if self.install_script: - for script in self.distribution.scripts: - if self.install_script == os.path.basename(script): - break - else: - raise DistutilsOptionError( - "install_script '%s' not found in scripts" - % self.install_script) - self.install_script_key = None - - def run(self): - if not self.skip_build: - self.run_command('build') - - install = self.reinitialize_command('install', reinit_subcommands=1) - install.prefix = self.bdist_dir - install.skip_build = self.skip_build - install.warn_dir = 0 - - install_lib = self.reinitialize_command('install_lib') - # we do not want to include pyc or pyo files - install_lib.compile = 0 - install_lib.optimize = 0 - - if self.distribution.has_ext_modules(): - # If we are building an installer for a Python version other - # than the one we are currently running, then we need to ensure - # our build_lib reflects the other Python version rather than ours. - # Note that for target_version!=sys.version, we must have skipped the - # build step, so there is no issue with enforcing the build of this - # version. - target_version = self.target_version - if not target_version: - assert self.skip_build, "Should have already checked this" - target_version = '%d.%d' % sys.version_info[:2] - plat_specifier = ".%s-%s" % (self.plat_name, target_version) - build = self.get_finalized_command('build') - build.build_lib = os.path.join(build.build_base, - 'lib' + plat_specifier) - - log.info("installing to %s", self.bdist_dir) - install.ensure_finalized() - - # avoid warning of 'install_lib' about installing - # into a directory not in sys.path - sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) - - install.run() - - del sys.path[0] - - self.mkpath(self.dist_dir) - fullname = self.distribution.get_fullname() - installer_name = self.get_installer_filename(fullname) - installer_name = os.path.abspath(installer_name) - if os.path.exists(installer_name): os.unlink(installer_name) - - metadata = self.distribution.metadata - author = metadata.author - if not author: - author = metadata.maintainer - if not author: - author = "UNKNOWN" - version = metadata.get_version() - # ProductVersion must be strictly numeric - # XXX need to deal with prerelease versions - sversion = "%d.%d.%d" % StrictVersion(version).version - # Prefix ProductName with Python x.y, so that - # it sorts together with the other Python packages - # in Add-Remove-Programs (APR) - fullname = self.distribution.get_fullname() - if self.target_version: - product_name = "Python %s %s" % (self.target_version, fullname) - else: - product_name = "Python %s" % (fullname) - self.db = msilib.init_database(installer_name, schema, - product_name, msilib.gen_uuid(), - sversion, author) - msilib.add_tables(self.db, sequence) - props = [('DistVersion', version)] - email = metadata.author_email or metadata.maintainer_email - if email: - props.append(("ARPCONTACT", email)) - if metadata.url: - props.append(("ARPURLINFOABOUT", metadata.url)) - if props: - add_data(self.db, 'Property', props) - - self.add_find_python() - self.add_files() - self.add_scripts() - self.add_ui() - self.db.Commit() - - if hasattr(self.distribution, 'dist_files'): - tup = 'bdist_msi', self.target_version or 'any', fullname - self.distribution.dist_files.append(tup) - - if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) - - def add_files(self): - db = self.db - cab = msilib.CAB("distfiles") - rootdir = os.path.abspath(self.bdist_dir) - - root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir") - f = Feature(db, "Python", "Python", "Everything", - 0, 1, directory="TARGETDIR") - - items = [(f, root, '')] - for version in self.versions + [self.other_version]: - target = "TARGETDIR" + version - name = default = "Python" + version - desc = "Everything" - if version is self.other_version: - title = "Python from another location" - level = 2 - else: - title = "Python %s from registry" % version - level = 1 - f = Feature(db, name, title, desc, 1, level, directory=target) - dir = Directory(db, cab, root, rootdir, target, default) - items.append((f, dir, version)) - db.Commit() - - seen = {} - for feature, dir, version in items: - todo = [dir] - while todo: - dir = todo.pop() - for file in os.listdir(dir.absolute): - afile = os.path.join(dir.absolute, file) - if os.path.isdir(afile): - short = "%s|%s" % (dir.make_short(file), file) - default = file + version - newdir = Directory(db, cab, dir, file, default, short) - todo.append(newdir) - else: - if not dir.component: - dir.start_component(dir.logical, feature, 0) - if afile not in seen: - key = seen[afile] = dir.add_file(file) - if file==self.install_script: - if self.install_script_key: - raise DistutilsOptionError( - "Multiple files with name %s" % file) - self.install_script_key = '[#%s]' % key - else: - key = seen[afile] - add_data(self.db, "DuplicateFile", - [(key + version, dir.component, key, None, dir.logical)]) - db.Commit() - cab.commit(db) - - def add_find_python(self): - """Adds code to the installer to compute the location of Python. - - Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the - registry for each version of Python. - - Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined, - else from PYTHON.MACHINE.X.Y. - - Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe""" - - start = 402 - for ver in self.versions: - install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver - machine_reg = "python.machine." + ver - user_reg = "python.user." + ver - machine_prop = "PYTHON.MACHINE." + ver - user_prop = "PYTHON.USER." + ver - machine_action = "PythonFromMachine" + ver - user_action = "PythonFromUser" + ver - exe_action = "PythonExe" + ver - target_dir_prop = "TARGETDIR" + ver - exe_prop = "PYTHON" + ver - if msilib.Win64: - # type: msidbLocatorTypeRawValue + msidbLocatorType64bit - Type = 2+16 - else: - Type = 2 - add_data(self.db, "RegLocator", - [(machine_reg, 2, install_path, None, Type), - (user_reg, 1, install_path, None, Type)]) - add_data(self.db, "AppSearch", - [(machine_prop, machine_reg), - (user_prop, user_reg)]) - add_data(self.db, "CustomAction", - [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"), - (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"), - (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"), - ]) - add_data(self.db, "InstallExecuteSequence", - [(machine_action, machine_prop, start), - (user_action, user_prop, start + 1), - (exe_action, None, start + 2), - ]) - add_data(self.db, "InstallUISequence", - [(machine_action, machine_prop, start), - (user_action, user_prop, start + 1), - (exe_action, None, start + 2), - ]) - add_data(self.db, "Condition", - [("Python" + ver, 0, "NOT TARGETDIR" + ver)]) - start += 4 - assert start < 500 - - def add_scripts(self): - if self.install_script: - start = 6800 - for ver in self.versions + [self.other_version]: - install_action = "install_script." + ver - exe_prop = "PYTHON" + ver - add_data(self.db, "CustomAction", - [(install_action, 50, exe_prop, self.install_script_key)]) - add_data(self.db, "InstallExecuteSequence", - [(install_action, "&Python%s=3" % ver, start)]) - start += 1 - # XXX pre-install scripts are currently refused in finalize_options() - # but if this feature is completed, it will also need to add - # entries for each version as the above code does - if self.pre_install_script: - scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") - with open(scriptfn, "w") as f: - # The batch file will be executed with [PYTHON], so that %1 - # is the path to the Python interpreter; %0 will be the path - # of the batch file. - # rem =""" - # %1 %0 - # exit - # """ - # - f.write('rem ="""\n%1 %0\nexit\n"""\n') - with open(self.pre_install_script) as fin: - f.write(fin.read()) - add_data(self.db, "Binary", - [("PreInstall", msilib.Binary(scriptfn)) - ]) - add_data(self.db, "CustomAction", - [("PreInstall", 2, "PreInstall", None) - ]) - add_data(self.db, "InstallExecuteSequence", - [("PreInstall", "NOT Installed", 450)]) - - - def add_ui(self): - db = self.db - x = y = 50 - w = 370 - h = 300 - title = "[ProductName] Setup" - - # see "Dialog Style Bits" - modal = 3 # visible | modal - modeless = 1 # visible - track_disk_space = 32 - - # UI customization properties - add_data(db, "Property", - # See "DefaultUIFont Property" - [("DefaultUIFont", "DlgFont8"), - # See "ErrorDialog Style Bit" - ("ErrorDialog", "ErrorDlg"), - ("Progress1", "Install"), # modified in maintenance type dlg - ("Progress2", "installs"), - ("MaintenanceForm_Action", "Repair"), - # possible values: ALL, JUSTME - ("WhichUsers", "ALL") - ]) - - # Fonts, see "TextStyle Table" - add_data(db, "TextStyle", - [("DlgFont8", "Tahoma", 9, None, 0), - ("DlgFontBold8", "Tahoma", 8, None, 1), #bold - ("VerdanaBold10", "Verdana", 10, None, 1), - ("VerdanaRed9", "Verdana", 9, 255, 0), - ]) - - # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table" - # Numbers indicate sequence; see sequence.py for how these action integrate - add_data(db, "InstallUISequence", - [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140), - ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141), - # In the user interface, assume all-users installation if privileged. - ("SelectFeaturesDlg", "Not Installed", 1230), - # XXX no support for resume installations yet - #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240), - ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), - ("ProgressDlg", None, 1280)]) - - add_data(db, 'ActionText', text.ActionText) - add_data(db, 'UIText', text.UIText) - ##################################################################### - # Standard dialogs: FatalError, UserExit, ExitDialog - fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title, - "Finish", "Finish", "Finish") - fatal.title("[ProductName] Installer ended prematurely") - fatal.back("< Back", "Finish", active = 0) - fatal.cancel("Cancel", "Back", active = 0) - fatal.text("Description1", 15, 70, 320, 80, 0x30003, - "[ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again.") - fatal.text("Description2", 15, 155, 320, 20, 0x30003, - "Click the Finish button to exit the Installer.") - c=fatal.next("Finish", "Cancel", name="Finish") - c.event("EndDialog", "Exit") - - user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title, - "Finish", "Finish", "Finish") - user_exit.title("[ProductName] Installer was interrupted") - user_exit.back("< Back", "Finish", active = 0) - user_exit.cancel("Cancel", "Back", active = 0) - user_exit.text("Description1", 15, 70, 320, 80, 0x30003, - "[ProductName] setup was interrupted. Your system has not been modified. " - "To install this program at a later time, please run the installation again.") - user_exit.text("Description2", 15, 155, 320, 20, 0x30003, - "Click the Finish button to exit the Installer.") - c = user_exit.next("Finish", "Cancel", name="Finish") - c.event("EndDialog", "Exit") - - exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title, - "Finish", "Finish", "Finish") - exit_dialog.title("Completing the [ProductName] Installer") - exit_dialog.back("< Back", "Finish", active = 0) - exit_dialog.cancel("Cancel", "Back", active = 0) - exit_dialog.text("Description", 15, 235, 320, 20, 0x30003, - "Click the Finish button to exit the Installer.") - c = exit_dialog.next("Finish", "Cancel", name="Finish") - c.event("EndDialog", "Return") - - ##################################################################### - # Required dialog: FilesInUse, ErrorDlg - inuse = PyDialog(db, "FilesInUse", - x, y, w, h, - 19, # KeepModeless|Modal|Visible - title, - "Retry", "Retry", "Retry", bitmap=False) - inuse.text("Title", 15, 6, 200, 15, 0x30003, - r"{\DlgFontBold8}Files in Use") - inuse.text("Description", 20, 23, 280, 20, 0x30003, - "Some files that need to be updated are currently in use.") - inuse.text("Text", 20, 55, 330, 50, 3, - "The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.") - inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess", - None, None, None) - c=inuse.back("Exit", "Ignore", name="Exit") - c.event("EndDialog", "Exit") - c=inuse.next("Ignore", "Retry", name="Ignore") - c.event("EndDialog", "Ignore") - c=inuse.cancel("Retry", "Exit", name="Retry") - c.event("EndDialog","Retry") - - # See "Error Dialog". See "ICE20" for the required names of the controls. - error = Dialog(db, "ErrorDlg", - 50, 10, 330, 101, - 65543, # Error|Minimize|Modal|Visible - title, - "ErrorText", None, None) - error.text("ErrorText", 50,9,280,48,3, "") - #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None) - error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo") - error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes") - error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort") - error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel") - error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore") - error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk") - error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry") - - ##################################################################### - # Global "Query Cancel" dialog - cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title, - "No", "No", "No") - cancel.text("Text", 48, 15, 194, 30, 3, - "Are you sure you want to cancel [ProductName] installation?") - #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None, - # "py.ico", None, None) - c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No") - c.event("EndDialog", "Exit") - - c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes") - c.event("EndDialog", "Return") - - ##################################################################### - # Global "Wait for costing" dialog - costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title, - "Return", "Return", "Return") - costing.text("Text", 48, 15, 194, 30, 3, - "Please wait while the installer finishes determining your disk space requirements.") - c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None) - c.event("EndDialog", "Exit") - - ##################################################################### - # Preparation dialog: no user input except cancellation - prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title, - "Cancel", "Cancel", "Cancel") - prep.text("Description", 15, 70, 320, 40, 0x30003, - "Please wait while the Installer prepares to guide you through the installation.") - prep.title("Welcome to the [ProductName] Installer") - c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...") - c.mapping("ActionText", "Text") - c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None) - c.mapping("ActionData", "Text") - prep.back("Back", None, active=0) - prep.next("Next", None, active=0) - c=prep.cancel("Cancel", None) - c.event("SpawnDialog", "CancelDlg") - - ##################################################################### - # Feature (Python directory) selection - seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title, - "Next", "Next", "Cancel") - seldlg.title("Select Python Installations") - - seldlg.text("Hint", 15, 30, 300, 20, 3, - "Select the Python locations where %s should be installed." - % self.distribution.get_fullname()) - - seldlg.back("< Back", None, active=0) - c = seldlg.next("Next >", "Cancel") - order = 1 - c.event("[TARGETDIR]", "[SourceDir]", ordering=order) - for version in self.versions + [self.other_version]: - order += 1 - c.event("[TARGETDIR]", "[TARGETDIR%s]" % version, - "FEATURE_SELECTED AND &Python%s=3" % version, - ordering=order) - c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1) - c.event("EndDialog", "Return", ordering=order + 2) - c = seldlg.cancel("Cancel", "Features") - c.event("SpawnDialog", "CancelDlg") - - c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3, - "FEATURE", None, "PathEdit", None) - c.event("[FEATURE_SELECTED]", "1") - ver = self.other_version - install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver - dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver - - c = seldlg.text("Other", 15, 200, 300, 15, 3, - "Provide an alternate Python location") - c.condition("Enable", install_other_cond) - c.condition("Show", install_other_cond) - c.condition("Disable", dont_install_other_cond) - c.condition("Hide", dont_install_other_cond) - - c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1, - "TARGETDIR" + ver, None, "Next", None) - c.condition("Enable", install_other_cond) - c.condition("Show", install_other_cond) - c.condition("Disable", dont_install_other_cond) - c.condition("Hide", dont_install_other_cond) - - ##################################################################### - # Disk cost - cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title, - "OK", "OK", "OK", bitmap=False) - cost.text("Title", 15, 6, 200, 15, 0x30003, - r"{\DlgFontBold8}Disk Space Requirements") - cost.text("Description", 20, 20, 280, 20, 0x30003, - "The disk space required for the installation of the selected features.") - cost.text("Text", 20, 53, 330, 60, 3, - "The highlighted volumes (if any) do not have enough disk space " - "available for the currently selected features. You can either " - "remove some files from the highlighted volumes, or choose to " - "install less features onto local drive(s), or select different " - "destination drive(s).") - cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223, - None, "{120}{70}{70}{70}{70}", None, None) - cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return") - - ##################################################################### - # WhichUsers Dialog. Only available on NT, and for privileged users. - # This must be run before FindRelatedProducts, because that will - # take into account whether the previous installation was per-user - # or per-machine. We currently don't support going back to this - # dialog after "Next" was selected; to support this, we would need to - # find how to reset the ALLUSERS property, and how to re-run - # FindRelatedProducts. - # On Windows9x, the ALLUSERS property is ignored on the command line - # and in the Property table, but installer fails according to the documentation - # if a dialog attempts to set ALLUSERS. - whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title, - "AdminInstall", "Next", "Cancel") - whichusers.title("Select whether to install [ProductName] for all users of this computer.") - # A radio group with two options: allusers, justme - g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3, - "WhichUsers", "", "Next") - g.add("ALL", 0, 5, 150, 20, "Install for all users") - g.add("JUSTME", 0, 25, 150, 20, "Install just for me") - - whichusers.back("Back", None, active=0) - - c = whichusers.next("Next >", "Cancel") - c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) - c.event("EndDialog", "Return", ordering = 2) - - c = whichusers.cancel("Cancel", "AdminInstall") - c.event("SpawnDialog", "CancelDlg") - - ##################################################################### - # Installation Progress dialog (modeless) - progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title, - "Cancel", "Cancel", "Cancel", bitmap=False) - progress.text("Title", 20, 15, 200, 15, 0x30003, - r"{\DlgFontBold8}[Progress1] [ProductName]") - progress.text("Text", 35, 65, 300, 30, 3, - "Please wait while the Installer [Progress2] [ProductName]. " - "This may take several minutes.") - progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:") - - c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...") - c.mapping("ActionText", "Text") - - #c=progress.text("ActionData", 35, 140, 300, 20, 3, None) - #c.mapping("ActionData", "Text") - - c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537, - None, "Progress done", None, None) - c.mapping("SetProgress", "Progress") - - progress.back("< Back", "Next", active=False) - progress.next("Next >", "Cancel", active=False) - progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg") - - ################################################################### - # Maintenance type: repair/uninstall - maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title, - "Next", "Next", "Cancel") - maint.title("Welcome to the [ProductName] Setup Wizard") - maint.text("BodyText", 15, 63, 330, 42, 3, - "Select whether you want to repair or remove [ProductName].") - g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3, - "MaintenanceForm_Action", "", "Next") - #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]") - g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]") - g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]") - - maint.back("< Back", None, active=False) - c=maint.next("Finish", "Cancel") - # Change installation: Change progress dialog to "Change", then ask - # for feature selection - #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1) - #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2) - - # Reinstall: Change progress dialog to "Repair", then invoke reinstall - # Also set list of reinstalled features to "ALL" - c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5) - c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6) - c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7) - c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8) - - # Uninstall: Change progress to "Remove", then invoke uninstall - # Also set list of removed features to "ALL" - c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11) - c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12) - c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13) - c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14) - - # Close dialog when maintenance action scheduled - c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20) - #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21) - - maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg") - - def get_installer_filename(self, fullname): - # Factored out to allow overriding in subclasses - if self.target_version: - base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, - self.target_version) - else: - base_name = "%s.%s.msi" % (fullname, self.plat_name) - installer_name = os.path.join(self.dist_dir, base_name) - return installer_name diff --git a/distutils/command/bdist_rpm.py b/distutils/command/bdist_rpm.py deleted file mode 100644 index 550cbfa1..00000000 --- a/distutils/command/bdist_rpm.py +++ /dev/null @@ -1,579 +0,0 @@ -"""distutils.command.bdist_rpm - -Implements the Distutils 'bdist_rpm' command (create RPM source and binary -distributions).""" - -import subprocess, sys, os -from distutils.core import Command -from distutils.debug import DEBUG -from distutils.file_util import write_file -from distutils.errors import * -from distutils.sysconfig import get_python_version -from distutils import log - -class bdist_rpm(Command): - - description = "create an RPM distribution" - - user_options = [ - ('bdist-base=', None, - "base directory for creating built distributions"), - ('rpm-base=', None, - "base directory for creating RPMs (defaults to \"rpm\" under " - "--bdist-base; must be specified for RPM 2)"), - ('dist-dir=', 'd', - "directory to put final RPM files in " - "(and .spec files if --spec-only)"), - ('python=', None, - "path to Python interpreter to hard-code in the .spec file " - "(default: \"python\")"), - ('fix-python', None, - "hard-code the exact path to the current Python interpreter in " - "the .spec file"), - ('spec-only', None, - "only regenerate spec file"), - ('source-only', None, - "only generate source RPM"), - ('binary-only', None, - "only generate binary RPM"), - ('use-bzip2', None, - "use bzip2 instead of gzip to create source distribution"), - - # More meta-data: too RPM-specific to put in the setup script, - # but needs to go in the .spec file -- so we make these options - # to "bdist_rpm". The idea is that packagers would put this - # info in setup.cfg, although they are of course free to - # supply it on the command line. - ('distribution-name=', None, - "name of the (Linux) distribution to which this " - "RPM applies (*not* the name of the module distribution!)"), - ('group=', None, - "package classification [default: \"Development/Libraries\"]"), - ('release=', None, - "RPM release number"), - ('serial=', None, - "RPM serial number"), - ('vendor=', None, - "RPM \"vendor\" (eg. \"Joe Blow \") " - "[default: maintainer or author from setup script]"), - ('packager=', None, - "RPM packager (eg. \"Jane Doe \") " - "[default: vendor]"), - ('doc-files=', None, - "list of documentation files (space or comma-separated)"), - ('changelog=', None, - "RPM changelog"), - ('icon=', None, - "name of icon file"), - ('provides=', None, - "capabilities provided by this package"), - ('requires=', None, - "capabilities required by this package"), - ('conflicts=', None, - "capabilities which conflict with this package"), - ('build-requires=', None, - "capabilities required to build this package"), - ('obsoletes=', None, - "capabilities made obsolete by this package"), - ('no-autoreq', None, - "do not automatically calculate dependencies"), - - # Actions to take when building RPM - ('keep-temp', 'k', - "don't clean up RPM build directory"), - ('no-keep-temp', None, - "clean up RPM build directory [default]"), - ('use-rpm-opt-flags', None, - "compile with RPM_OPT_FLAGS when building from source RPM"), - ('no-rpm-opt-flags', None, - "do not pass any RPM CFLAGS to compiler"), - ('rpm3-mode', None, - "RPM 3 compatibility mode (default)"), - ('rpm2-mode', None, - "RPM 2 compatibility mode"), - - # Add the hooks necessary for specifying custom scripts - ('prep-script=', None, - "Specify a script for the PREP phase of RPM building"), - ('build-script=', None, - "Specify a script for the BUILD phase of RPM building"), - - ('pre-install=', None, - "Specify a script for the pre-INSTALL phase of RPM building"), - ('install-script=', None, - "Specify a script for the INSTALL phase of RPM building"), - ('post-install=', None, - "Specify a script for the post-INSTALL phase of RPM building"), - - ('pre-uninstall=', None, - "Specify a script for the pre-UNINSTALL phase of RPM building"), - ('post-uninstall=', None, - "Specify a script for the post-UNINSTALL phase of RPM building"), - - ('clean-script=', None, - "Specify a script for the CLEAN phase of RPM building"), - - ('verify-script=', None, - "Specify a script for the VERIFY phase of the RPM build"), - - # Allow a packager to explicitly force an architecture - ('force-arch=', None, - "Force an architecture onto the RPM build process"), - - ('quiet', 'q', - "Run the INSTALL phase of RPM building in quiet mode"), - ] - - boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', - 'no-autoreq', 'quiet'] - - negative_opt = {'no-keep-temp': 'keep-temp', - 'no-rpm-opt-flags': 'use-rpm-opt-flags', - 'rpm2-mode': 'rpm3-mode'} - - - def initialize_options(self): - self.bdist_base = None - self.rpm_base = None - self.dist_dir = None - self.python = None - self.fix_python = None - self.spec_only = None - self.binary_only = None - self.source_only = None - self.use_bzip2 = None - - self.distribution_name = None - self.group = None - self.release = None - self.serial = None - self.vendor = None - self.packager = None - self.doc_files = None - self.changelog = None - self.icon = None - - self.prep_script = None - self.build_script = None - self.install_script = None - self.clean_script = None - self.verify_script = None - self.pre_install = None - self.post_install = None - self.pre_uninstall = None - self.post_uninstall = None - self.prep = None - self.provides = None - self.requires = None - self.conflicts = None - self.build_requires = None - self.obsoletes = None - - self.keep_temp = 0 - self.use_rpm_opt_flags = 1 - self.rpm3_mode = 1 - self.no_autoreq = 0 - - self.force_arch = None - self.quiet = 0 - - def finalize_options(self): - self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) - if self.rpm_base is None: - if not self.rpm3_mode: - raise DistutilsOptionError( - "you must specify --rpm-base in RPM 2 mode") - self.rpm_base = os.path.join(self.bdist_base, "rpm") - - if self.python is None: - if self.fix_python: - self.python = sys.executable - else: - self.python = "python3" - elif self.fix_python: - raise DistutilsOptionError( - "--python and --fix-python are mutually exclusive options") - - if os.name != 'posix': - raise DistutilsPlatformError("don't know how to create RPM " - "distributions on platform %s" % os.name) - if self.binary_only and self.source_only: - raise DistutilsOptionError( - "cannot supply both '--source-only' and '--binary-only'") - - # don't pass CFLAGS to pure python distributions - if not self.distribution.has_ext_modules(): - self.use_rpm_opt_flags = 0 - - self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) - self.finalize_package_data() - - def finalize_package_data(self): - self.ensure_string('group', "Development/Libraries") - self.ensure_string('vendor', - "%s <%s>" % (self.distribution.get_contact(), - self.distribution.get_contact_email())) - self.ensure_string('packager') - self.ensure_string_list('doc_files') - if isinstance(self.doc_files, list): - for readme in ('README', 'README.txt'): - if os.path.exists(readme) and readme not in self.doc_files: - self.doc_files.append(readme) - - self.ensure_string('release', "1") - self.ensure_string('serial') # should it be an int? - - self.ensure_string('distribution_name') - - self.ensure_string('changelog') - # Format changelog correctly - self.changelog = self._format_changelog(self.changelog) - - self.ensure_filename('icon') - - self.ensure_filename('prep_script') - self.ensure_filename('build_script') - self.ensure_filename('install_script') - self.ensure_filename('clean_script') - self.ensure_filename('verify_script') - self.ensure_filename('pre_install') - self.ensure_filename('post_install') - self.ensure_filename('pre_uninstall') - self.ensure_filename('post_uninstall') - - # XXX don't forget we punted on summaries and descriptions -- they - # should be handled here eventually! - - # Now *this* is some meta-data that belongs in the setup script... - self.ensure_string_list('provides') - self.ensure_string_list('requires') - self.ensure_string_list('conflicts') - self.ensure_string_list('build_requires') - self.ensure_string_list('obsoletes') - - self.ensure_string('force_arch') - - def run(self): - if DEBUG: - print("before _get_package_data():") - print("vendor =", self.vendor) - print("packager =", self.packager) - print("doc_files =", self.doc_files) - print("changelog =", self.changelog) - - # make directories - if self.spec_only: - spec_dir = self.dist_dir - self.mkpath(spec_dir) - else: - rpm_dir = {} - for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): - rpm_dir[d] = os.path.join(self.rpm_base, d) - self.mkpath(rpm_dir[d]) - spec_dir = rpm_dir['SPECS'] - - # Spec file goes into 'dist_dir' if '--spec-only specified', - # build/rpm. otherwise. - spec_path = os.path.join(spec_dir, - "%s.spec" % self.distribution.get_name()) - self.execute(write_file, - (spec_path, - self._make_spec_file()), - "writing '%s'" % spec_path) - - if self.spec_only: # stop if requested - return - - # Make a source distribution and copy to SOURCES directory with - # optional icon. - saved_dist_files = self.distribution.dist_files[:] - sdist = self.reinitialize_command('sdist') - if self.use_bzip2: - sdist.formats = ['bztar'] - else: - sdist.formats = ['gztar'] - self.run_command('sdist') - self.distribution.dist_files = saved_dist_files - - source = sdist.get_archive_files()[0] - source_dir = rpm_dir['SOURCES'] - self.copy_file(source, source_dir) - - if self.icon: - if os.path.exists(self.icon): - self.copy_file(self.icon, source_dir) - else: - raise DistutilsFileError( - "icon file '%s' does not exist" % self.icon) - - # build package - log.info("building RPMs") - rpm_cmd = ['rpmbuild'] - - if self.source_only: # what kind of RPMs? - rpm_cmd.append('-bs') - elif self.binary_only: - rpm_cmd.append('-bb') - else: - rpm_cmd.append('-ba') - rpm_cmd.extend(['--define', '__python %s' % self.python]) - if self.rpm3_mode: - rpm_cmd.extend(['--define', - '_topdir %s' % os.path.abspath(self.rpm_base)]) - if not self.keep_temp: - rpm_cmd.append('--clean') - - if self.quiet: - rpm_cmd.append('--quiet') - - rpm_cmd.append(spec_path) - # Determine the binary rpm names that should be built out of this spec - # file - # Note that some of these may not be really built (if the file - # list is empty) - nvr_string = "%{name}-%{version}-%{release}" - src_rpm = nvr_string + ".src.rpm" - non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" - q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % ( - src_rpm, non_src_rpm, spec_path) - - out = os.popen(q_cmd) - try: - binary_rpms = [] - source_rpm = None - while True: - line = out.readline() - if not line: - break - l = line.strip().split() - assert(len(l) == 2) - binary_rpms.append(l[1]) - # The source rpm is named after the first entry in the spec file - if source_rpm is None: - source_rpm = l[0] - - status = out.close() - if status: - raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) - - finally: - out.close() - - self.spawn(rpm_cmd) - - if not self.dry_run: - if self.distribution.has_ext_modules(): - pyversion = get_python_version() - else: - pyversion = 'any' - - if not self.binary_only: - srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) - assert(os.path.exists(srpm)) - self.move_file(srpm, self.dist_dir) - filename = os.path.join(self.dist_dir, source_rpm) - self.distribution.dist_files.append( - ('bdist_rpm', pyversion, filename)) - - if not self.source_only: - for rpm in binary_rpms: - rpm = os.path.join(rpm_dir['RPMS'], rpm) - if os.path.exists(rpm): - self.move_file(rpm, self.dist_dir) - filename = os.path.join(self.dist_dir, - os.path.basename(rpm)) - self.distribution.dist_files.append( - ('bdist_rpm', pyversion, filename)) - - def _dist_path(self, path): - return os.path.join(self.dist_dir, os.path.basename(path)) - - def _make_spec_file(self): - """Generate the text of an RPM spec file and return it as a - list of strings (one per line). - """ - # definitions and headers - spec_file = [ - '%define name ' + self.distribution.get_name(), - '%define version ' + self.distribution.get_version().replace('-','_'), - '%define unmangled_version ' + self.distribution.get_version(), - '%define release ' + self.release.replace('-','_'), - '', - 'Summary: ' + self.distribution.get_description(), - ] - - # Workaround for #14443 which affects some RPM based systems such as - # RHEL6 (and probably derivatives) - vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}') - # Generate a potential replacement value for __os_install_post (whilst - # normalizing the whitespace to simplify the test for whether the - # invocation of brp-python-bytecompile passes in __python): - vendor_hook = '\n'.join([' %s \\' % line.strip() - for line in vendor_hook.splitlines()]) - problem = "brp-python-bytecompile \\\n" - fixed = "brp-python-bytecompile %{__python} \\\n" - fixed_hook = vendor_hook.replace(problem, fixed) - if fixed_hook != vendor_hook: - spec_file.append('# Workaround for http://bugs.python.org/issue14443') - spec_file.append('%define __os_install_post ' + fixed_hook + '\n') - - # put locale summaries into spec file - # XXX not supported for now (hard to put a dictionary - # in a config file -- arg!) - #for locale in self.summaries.keys(): - # spec_file.append('Summary(%s): %s' % (locale, - # self.summaries[locale])) - - spec_file.extend([ - 'Name: %{name}', - 'Version: %{version}', - 'Release: %{release}',]) - - # XXX yuck! this filename is available from the "sdist" command, - # but only after it has run: and we create the spec file before - # running "sdist", in case of --spec-only. - if self.use_bzip2: - spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') - else: - spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') - - spec_file.extend([ - 'License: ' + self.distribution.get_license(), - 'Group: ' + self.group, - 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', - 'Prefix: %{_prefix}', ]) - - if not self.force_arch: - # noarch if no extension modules - if not self.distribution.has_ext_modules(): - spec_file.append('BuildArch: noarch') - else: - spec_file.append( 'BuildArch: %s' % self.force_arch ) - - for field in ('Vendor', - 'Packager', - 'Provides', - 'Requires', - 'Conflicts', - 'Obsoletes', - ): - val = getattr(self, field.lower()) - if isinstance(val, list): - spec_file.append('%s: %s' % (field, ' '.join(val))) - elif val is not None: - spec_file.append('%s: %s' % (field, val)) - - - if self.distribution.get_url() != 'UNKNOWN': - spec_file.append('Url: ' + self.distribution.get_url()) - - if self.distribution_name: - spec_file.append('Distribution: ' + self.distribution_name) - - if self.build_requires: - spec_file.append('BuildRequires: ' + - ' '.join(self.build_requires)) - - if self.icon: - spec_file.append('Icon: ' + os.path.basename(self.icon)) - - if self.no_autoreq: - spec_file.append('AutoReq: 0') - - spec_file.extend([ - '', - '%description', - self.distribution.get_long_description() - ]) - - # put locale descriptions into spec file - # XXX again, suppressed because config file syntax doesn't - # easily support this ;-( - #for locale in self.descriptions.keys(): - # spec_file.extend([ - # '', - # '%description -l ' + locale, - # self.descriptions[locale], - # ]) - - # rpm scripts - # figure out default build script - def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0])) - def_build = "%s build" % def_setup_call - if self.use_rpm_opt_flags: - def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build - - # insert contents of files - - # XXX this is kind of misleading: user-supplied options are files - # that we open and interpolate into the spec file, but the defaults - # are just text that we drop in as-is. Hmmm. - - install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT ' - '--record=INSTALLED_FILES') % def_setup_call - - script_options = [ - ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), - ('build', 'build_script', def_build), - ('install', 'install_script', install_cmd), - ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), - ('verifyscript', 'verify_script', None), - ('pre', 'pre_install', None), - ('post', 'post_install', None), - ('preun', 'pre_uninstall', None), - ('postun', 'post_uninstall', None), - ] - - for (rpm_opt, attr, default) in script_options: - # Insert contents of file referred to, if no file is referred to - # use 'default' as contents of script - val = getattr(self, attr) - if val or default: - spec_file.extend([ - '', - '%' + rpm_opt,]) - if val: - with open(val) as f: - spec_file.extend(f.read().split('\n')) - else: - spec_file.append(default) - - - # files section - spec_file.extend([ - '', - '%files -f INSTALLED_FILES', - '%defattr(-,root,root)', - ]) - - if self.doc_files: - spec_file.append('%doc ' + ' '.join(self.doc_files)) - - if self.changelog: - spec_file.extend([ - '', - '%changelog',]) - spec_file.extend(self.changelog) - - return spec_file - - def _format_changelog(self, changelog): - """Format the changelog correctly and convert it to a list of strings - """ - if not changelog: - return changelog - new_changelog = [] - for line in changelog.strip().split('\n'): - line = line.strip() - if line[0] == '*': - new_changelog.extend(['', line]) - elif line[0] == '-': - new_changelog.append(line) - else: - new_changelog.append(' ' + line) - - # strip trailing newline inserted by first changelog entry - if not new_changelog[0]: - del new_changelog[0] - - return new_changelog diff --git a/distutils/command/bdist_wininst.py b/distutils/command/bdist_wininst.py deleted file mode 100644 index 0e9ddaa2..00000000 --- a/distutils/command/bdist_wininst.py +++ /dev/null @@ -1,377 +0,0 @@ -"""distutils.command.bdist_wininst - -Implements the Distutils 'bdist_wininst' command: create a windows installer -exe-program.""" - -import os -import sys -import warnings -from distutils.core import Command -from distutils.util import get_platform -from distutils.dir_util import remove_tree -from distutils.errors import * -from distutils.sysconfig import get_python_version -from distutils import log - -class bdist_wininst(Command): - - description = "create an executable installer for MS Windows" - - user_options = [('bdist-dir=', None, - "temporary directory for creating the distribution"), - ('plat-name=', 'p', - "platform name to embed in generated filenames " - "(default: %s)" % get_platform()), - ('keep-temp', 'k', - "keep the pseudo-installation tree around after " + - "creating the distribution archive"), - ('target-version=', None, - "require a specific python version" + - " on the target system"), - ('no-target-compile', 'c', - "do not compile .py to .pyc on the target system"), - ('no-target-optimize', 'o', - "do not compile .py to .pyo (optimized) " - "on the target system"), - ('dist-dir=', 'd', - "directory to put final built distributions in"), - ('bitmap=', 'b', - "bitmap to use for the installer instead of python-powered logo"), - ('title=', 't', - "title to display on the installer background instead of default"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - ('install-script=', None, - "basename of installation script to be run after " - "installation or before deinstallation"), - ('pre-install-script=', None, - "Fully qualified filename of a script to be run before " - "any files are installed. This script need not be in the " - "distribution"), - ('user-access-control=', None, - "specify Vista's UAC handling - 'none'/default=no " - "handling, 'auto'=use UAC if target Python installed for " - "all users, 'force'=always use UAC"), - ] - - boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', - 'skip-build'] - - # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows - _unsupported = (sys.platform != "win32") - - def __init__(self, *args, **kw): - super().__init__(*args, **kw) - warnings.warn("bdist_wininst command is deprecated since Python 3.8, " - "use bdist_wheel (wheel packages) instead", - DeprecationWarning, 2) - - def initialize_options(self): - self.bdist_dir = None - self.plat_name = None - self.keep_temp = 0 - self.no_target_compile = 0 - self.no_target_optimize = 0 - self.target_version = None - self.dist_dir = None - self.bitmap = None - self.title = None - self.skip_build = None - self.install_script = None - self.pre_install_script = None - self.user_access_control = None - - - def finalize_options(self): - self.set_undefined_options('bdist', ('skip_build', 'skip_build')) - - if self.bdist_dir is None: - if self.skip_build and self.plat_name: - # If build is skipped and plat_name is overridden, bdist will - # not see the correct 'plat_name' - so set that up manually. - bdist = self.distribution.get_command_obj('bdist') - bdist.plat_name = self.plat_name - # next the command will be initialized using that name - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'wininst') - - if not self.target_version: - self.target_version = "" - - if not self.skip_build and self.distribution.has_ext_modules(): - short_version = get_python_version() - if self.target_version and self.target_version != short_version: - raise DistutilsOptionError( - "target version can only be %s, or the '--skip-build'" \ - " option must be specified" % (short_version,)) - self.target_version = short_version - - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ) - - if self.install_script: - for script in self.distribution.scripts: - if self.install_script == os.path.basename(script): - break - else: - raise DistutilsOptionError( - "install_script '%s' not found in scripts" - % self.install_script) - - def run(self): - if (sys.platform != "win32" and - (self.distribution.has_ext_modules() or - self.distribution.has_c_libraries())): - raise DistutilsPlatformError \ - ("distribution contains extensions and/or C libraries; " - "must be compiled on a Windows 32 platform") - - if not self.skip_build: - self.run_command('build') - - install = self.reinitialize_command('install', reinit_subcommands=1) - install.root = self.bdist_dir - install.skip_build = self.skip_build - install.warn_dir = 0 - install.plat_name = self.plat_name - - install_lib = self.reinitialize_command('install_lib') - # we do not want to include pyc or pyo files - install_lib.compile = 0 - install_lib.optimize = 0 - - if self.distribution.has_ext_modules(): - # If we are building an installer for a Python version other - # than the one we are currently running, then we need to ensure - # our build_lib reflects the other Python version rather than ours. - # Note that for target_version!=sys.version, we must have skipped the - # build step, so there is no issue with enforcing the build of this - # version. - target_version = self.target_version - if not target_version: - assert self.skip_build, "Should have already checked this" - target_version = '%d.%d' % sys.version_info[:2] - plat_specifier = ".%s-%s" % (self.plat_name, target_version) - build = self.get_finalized_command('build') - build.build_lib = os.path.join(build.build_base, - 'lib' + plat_specifier) - - # Use a custom scheme for the zip-file, because we have to decide - # at installation time which scheme to use. - for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): - value = key.upper() - if key == 'headers': - value = value + '/Include/$dist_name' - setattr(install, - 'install_' + key, - value) - - log.info("installing to %s", self.bdist_dir) - install.ensure_finalized() - - # avoid warning of 'install_lib' about installing - # into a directory not in sys.path - sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) - - install.run() - - del sys.path[0] - - # And make an archive relative to the root of the - # pseudo-installation tree. - from tempfile import mktemp - archive_basename = mktemp() - fullname = self.distribution.get_fullname() - arcname = self.make_archive(archive_basename, "zip", - root_dir=self.bdist_dir) - # create an exe containing the zip-file - self.create_exe(arcname, fullname, self.bitmap) - if self.distribution.has_ext_modules(): - pyversion = get_python_version() - else: - pyversion = 'any' - self.distribution.dist_files.append(('bdist_wininst', pyversion, - self.get_installer_filename(fullname))) - # remove the zip-file again - log.debug("removing temporary file '%s'", arcname) - os.remove(arcname) - - if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) - - def get_inidata(self): - # Return data describing the installation. - lines = [] - metadata = self.distribution.metadata - - # Write the [metadata] section. - lines.append("[metadata]") - - # 'info' will be displayed in the installer's dialog box, - # describing the items to be installed. - info = (metadata.long_description or '') + '\n' - - # Escape newline characters - def escape(s): - return s.replace("\n", "\\n") - - for name in ["author", "author_email", "description", "maintainer", - "maintainer_email", "name", "url", "version"]: - data = getattr(metadata, name, "") - if data: - info = info + ("\n %s: %s" % \ - (name.capitalize(), escape(data))) - lines.append("%s=%s" % (name, escape(data))) - - # The [setup] section contains entries controlling - # the installer runtime. - lines.append("\n[Setup]") - if self.install_script: - lines.append("install_script=%s" % self.install_script) - lines.append("info=%s" % escape(info)) - lines.append("target_compile=%d" % (not self.no_target_compile)) - lines.append("target_optimize=%d" % (not self.no_target_optimize)) - if self.target_version: - lines.append("target_version=%s" % self.target_version) - if self.user_access_control: - lines.append("user_access_control=%s" % self.user_access_control) - - title = self.title or self.distribution.get_fullname() - lines.append("title=%s" % escape(title)) - import time - import distutils - build_info = "Built %s with distutils-%s" % \ - (time.ctime(time.time()), distutils.__version__) - lines.append("build_info=%s" % build_info) - return "\n".join(lines) - - def create_exe(self, arcname, fullname, bitmap=None): - import struct - - self.mkpath(self.dist_dir) - - cfgdata = self.get_inidata() - - installer_name = self.get_installer_filename(fullname) - self.announce("creating %s" % installer_name) - - if bitmap: - with open(bitmap, "rb") as f: - bitmapdata = f.read() - bitmaplen = len(bitmapdata) - else: - bitmaplen = 0 - - with open(installer_name, "wb") as file: - file.write(self.get_exe_bytes()) - if bitmap: - file.write(bitmapdata) - - # Convert cfgdata from unicode to ascii, mbcs encoded - if isinstance(cfgdata, str): - cfgdata = cfgdata.encode("mbcs") - - # Append the pre-install script - cfgdata = cfgdata + b"\0" - if self.pre_install_script: - # We need to normalize newlines, so we open in text mode and - # convert back to bytes. "latin-1" simply avoids any possible - # failures. - with open(self.pre_install_script, "r", - encoding="latin-1") as script: - script_data = script.read().encode("latin-1") - cfgdata = cfgdata + script_data + b"\n\0" - else: - # empty pre-install script - cfgdata = cfgdata + b"\0" - file.write(cfgdata) - - # The 'magic number' 0x1234567B is used to make sure that the - # binary layout of 'cfgdata' is what the wininst.exe binary - # expects. If the layout changes, increment that number, make - # the corresponding changes to the wininst.exe sources, and - # recompile them. - header = struct.pack("' under the base build directory. We only use one of - # them for a given distribution, though -- - if self.build_purelib is None: - self.build_purelib = os.path.join(self.build_base, 'lib') - if self.build_platlib is None: - self.build_platlib = os.path.join(self.build_base, - 'lib' + plat_specifier) - - # 'build_lib' is the actual directory that we will use for this - # particular module distribution -- if user didn't supply it, pick - # one of 'build_purelib' or 'build_platlib'. - if self.build_lib is None: - if self.distribution.ext_modules: - self.build_lib = self.build_platlib - else: - self.build_lib = self.build_purelib - - # 'build_temp' -- temporary directory for compiler turds, - # "build/temp." - if self.build_temp is None: - self.build_temp = os.path.join(self.build_base, - 'temp' + plat_specifier) - if self.build_scripts is None: - self.build_scripts = os.path.join(self.build_base, - 'scripts-%d.%d' % sys.version_info[:2]) - - if self.executable is None and sys.executable: - self.executable = os.path.normpath(sys.executable) - - if isinstance(self.parallel, str): - try: - self.parallel = int(self.parallel) - except ValueError: - raise DistutilsOptionError("parallel should be an integer") - - def run(self): - # Run all relevant sub-commands. This will be some subset of: - # - build_py - pure Python modules - # - build_clib - standalone C libraries - # - build_ext - Python extensions - # - build_scripts - (Python) scripts - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) - - - # -- Predicates for the sub-command list --------------------------- - - def has_pure_modules(self): - return self.distribution.has_pure_modules() - - def has_c_libraries(self): - return self.distribution.has_c_libraries() - - def has_ext_modules(self): - return self.distribution.has_ext_modules() - - def has_scripts(self): - return self.distribution.has_scripts() - - - sub_commands = [('build_py', has_pure_modules), - ('build_clib', has_c_libraries), - ('build_ext', has_ext_modules), - ('build_scripts', has_scripts), - ] diff --git a/distutils/command/build_clib.py b/distutils/command/build_clib.py deleted file mode 100644 index 3e20ef23..00000000 --- a/distutils/command/build_clib.py +++ /dev/null @@ -1,209 +0,0 @@ -"""distutils.command.build_clib - -Implements the Distutils 'build_clib' command, to build a C/C++ library -that is included in the module distribution and needed by an extension -module.""" - - -# XXX this module has *lots* of code ripped-off quite transparently from -# build_ext.py -- not surprisingly really, as the work required to build -# a static library from a collection of C source files is not really all -# that different from what's required to build a shared object file from -# a collection of C source files. Nevertheless, I haven't done the -# necessary refactoring to account for the overlap in code between the -# two modules, mainly because a number of subtle details changed in the -# cut 'n paste. Sigh. - -import os -from distutils.core import Command -from distutils.errors import * -from distutils.sysconfig import customize_compiler -from distutils import log - -def show_compilers(): - from distutils.ccompiler import show_compilers - show_compilers() - - -class build_clib(Command): - - description = "build C/C++ libraries used by Python extensions" - - user_options = [ - ('build-clib=', 'b', - "directory to build C/C++ libraries to"), - ('build-temp=', 't', - "directory to put temporary build by-products"), - ('debug', 'g', - "compile with debugging information"), - ('force', 'f', - "forcibly build everything (ignore file timestamps)"), - ('compiler=', 'c', - "specify the compiler type"), - ] - - boolean_options = ['debug', 'force'] - - help_options = [ - ('help-compiler', None, - "list available compilers", show_compilers), - ] - - def initialize_options(self): - self.build_clib = None - self.build_temp = None - - # List of libraries to build - self.libraries = None - - # Compilation options for all libraries - self.include_dirs = None - self.define = None - self.undef = None - self.debug = None - self.force = 0 - self.compiler = None - - - def finalize_options(self): - # This might be confusing: both build-clib and build-temp default - # to build-temp as defined by the "build" command. This is because - # I think that C libraries are really just temporary build - # by-products, at least from the point of view of building Python - # extensions -- but I want to keep my options open. - self.set_undefined_options('build', - ('build_temp', 'build_clib'), - ('build_temp', 'build_temp'), - ('compiler', 'compiler'), - ('debug', 'debug'), - ('force', 'force')) - - self.libraries = self.distribution.libraries - if self.libraries: - self.check_library_list(self.libraries) - - if self.include_dirs is None: - self.include_dirs = self.distribution.include_dirs or [] - if isinstance(self.include_dirs, str): - self.include_dirs = self.include_dirs.split(os.pathsep) - - # XXX same as for build_ext -- what about 'self.define' and - # 'self.undef' ? - - - def run(self): - if not self.libraries: - return - - # Yech -- this is cut 'n pasted from build_ext.py! - from distutils.ccompiler import new_compiler - self.compiler = new_compiler(compiler=self.compiler, - dry_run=self.dry_run, - force=self.force) - customize_compiler(self.compiler) - - if self.include_dirs is not None: - self.compiler.set_include_dirs(self.include_dirs) - if self.define is not None: - # 'define' option is a list of (name,value) tuples - for (name,value) in self.define: - self.compiler.define_macro(name, value) - if self.undef is not None: - for macro in self.undef: - self.compiler.undefine_macro(macro) - - self.build_libraries(self.libraries) - - - def check_library_list(self, libraries): - """Ensure that the list of libraries is valid. - - `library` is presumably provided as a command option 'libraries'. - This method checks that it is a list of 2-tuples, where the tuples - are (library_name, build_info_dict). - - Raise DistutilsSetupError if the structure is invalid anywhere; - just returns otherwise. - """ - if not isinstance(libraries, list): - raise DistutilsSetupError( - "'libraries' option must be a list of tuples") - - for lib in libraries: - if not isinstance(lib, tuple) and len(lib) != 2: - raise DistutilsSetupError( - "each element of 'libraries' must a 2-tuple") - - name, build_info = lib - - if not isinstance(name, str): - raise DistutilsSetupError( - "first element of each tuple in 'libraries' " - "must be a string (the library name)") - - if '/' in name or (os.sep != '/' and os.sep in name): - raise DistutilsSetupError("bad library name '%s': " - "may not contain directory separators" % lib[0]) - - if not isinstance(build_info, dict): - raise DistutilsSetupError( - "second element of each tuple in 'libraries' " - "must be a dictionary (build info)") - - - def get_library_names(self): - # Assume the library list is valid -- 'check_library_list()' is - # called from 'finalize_options()', so it should be! - if not self.libraries: - return None - - lib_names = [] - for (lib_name, build_info) in self.libraries: - lib_names.append(lib_name) - return lib_names - - - def get_source_files(self): - self.check_library_list(self.libraries) - filenames = [] - for (lib_name, build_info) in self.libraries: - sources = build_info.get('sources') - if sources is None or not isinstance(sources, (list, tuple)): - raise DistutilsSetupError( - "in 'libraries' option (library '%s'), " - "'sources' must be present and must be " - "a list of source filenames" % lib_name) - - filenames.extend(sources) - return filenames - - - def build_libraries(self, libraries): - for (lib_name, build_info) in libraries: - sources = build_info.get('sources') - if sources is None or not isinstance(sources, (list, tuple)): - raise DistutilsSetupError( - "in 'libraries' option (library '%s'), " - "'sources' must be present and must be " - "a list of source filenames" % lib_name) - sources = list(sources) - - log.info("building '%s' library", lib_name) - - # First, compile the source code to object files in the library - # directory. (This should probably change to putting object - # files in a temporary build directory.) - macros = build_info.get('macros') - include_dirs = build_info.get('include_dirs') - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=include_dirs, - debug=self.debug) - - # Now "link" the object files together into a static library. - # (On Unix at least, this isn't really linking -- it just - # builds an archive. Whatever.) - self.compiler.create_static_lib(objects, lib_name, - output_dir=self.build_clib, - debug=self.debug) diff --git a/distutils/command/build_ext.py b/distutils/command/build_ext.py deleted file mode 100644 index 1a9bd120..00000000 --- a/distutils/command/build_ext.py +++ /dev/null @@ -1,754 +0,0 @@ -"""distutils.command.build_ext - -Implements the Distutils 'build_ext' command, for building extension -modules (currently limited to C extensions, should accommodate C++ -extensions ASAP).""" - -import contextlib -import os -import re -import sys -from distutils.core import Command -from distutils.errors import * -from distutils.sysconfig import customize_compiler, get_python_version -from distutils.sysconfig import get_config_h_filename -from distutils.dep_util import newer_group -from distutils.extension import Extension -from distutils.util import get_platform -from distutils import log - -from site import USER_BASE - -# An extension name is just a dot-separated list of Python NAMEs (ie. -# the same as a fully-qualified module name). -extension_name_re = re.compile \ - (r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$') - - -def show_compilers (): - from distutils.ccompiler import show_compilers - show_compilers() - - -class build_ext(Command): - - description = "build C/C++ extensions (compile/link to build directory)" - - # XXX thoughts on how to deal with complex command-line options like - # these, i.e. how to make it so fancy_getopt can suck them off the - # command line and make it look like setup.py defined the appropriate - # lists of tuples of what-have-you. - # - each command needs a callback to process its command-line options - # - Command.__init__() needs access to its share of the whole - # command line (must ultimately come from - # Distribution.parse_command_line()) - # - it then calls the current command class' option-parsing - # callback to deal with weird options like -D, which have to - # parse the option text and churn out some custom data - # structure - # - that data structure (in this case, a list of 2-tuples) - # will then be present in the command object by the time - # we get to finalize_options() (i.e. the constructor - # takes care of both command-line and client options - # in between initialize_options() and finalize_options()) - - sep_by = " (separated by '%s')" % os.pathsep - user_options = [ - ('build-lib=', 'b', - "directory for compiled extension modules"), - ('build-temp=', 't', - "directory for temporary files (build by-products)"), - ('plat-name=', 'p', - "platform name to cross-compile for, if supported " - "(default: %s)" % get_platform()), - ('inplace', 'i', - "ignore build-lib and put compiled extensions into the source " + - "directory alongside your pure Python modules"), - ('include-dirs=', 'I', - "list of directories to search for header files" + sep_by), - ('define=', 'D', - "C preprocessor macros to define"), - ('undef=', 'U', - "C preprocessor macros to undefine"), - ('libraries=', 'l', - "external C libraries to link with"), - ('library-dirs=', 'L', - "directories to search for external C libraries" + sep_by), - ('rpath=', 'R', - "directories to search for shared C libraries at runtime"), - ('link-objects=', 'O', - "extra explicit link objects to include in the link"), - ('debug', 'g', - "compile/link with debugging information"), - ('force', 'f', - "forcibly build everything (ignore file timestamps)"), - ('compiler=', 'c', - "specify the compiler type"), - ('parallel=', 'j', - "number of parallel build jobs"), - ('swig-cpp', None, - "make SWIG create C++ files (default is C)"), - ('swig-opts=', None, - "list of SWIG command line options"), - ('swig=', None, - "path to the SWIG executable"), - ('user', None, - "add user include, library and rpath") - ] - - boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] - - help_options = [ - ('help-compiler', None, - "list available compilers", show_compilers), - ] - - def initialize_options(self): - self.extensions = None - self.build_lib = None - self.plat_name = None - self.build_temp = None - self.inplace = 0 - self.package = None - - self.include_dirs = None - self.define = None - self.undef = None - self.libraries = None - self.library_dirs = None - self.rpath = None - self.link_objects = None - self.debug = None - self.force = None - self.compiler = None - self.swig = None - self.swig_cpp = None - self.swig_opts = None - self.user = None - self.parallel = None - - def finalize_options(self): - from distutils import sysconfig - - self.set_undefined_options('build', - ('build_lib', 'build_lib'), - ('build_temp', 'build_temp'), - ('compiler', 'compiler'), - ('debug', 'debug'), - ('force', 'force'), - ('parallel', 'parallel'), - ('plat_name', 'plat_name'), - ) - - if self.package is None: - self.package = self.distribution.ext_package - - self.extensions = self.distribution.ext_modules - - # Make sure Python's include directories (for Python.h, pyconfig.h, - # etc.) are in the include search path. - py_include = sysconfig.get_python_inc() - plat_py_include = sysconfig.get_python_inc(plat_specific=1) - if self.include_dirs is None: - self.include_dirs = self.distribution.include_dirs or [] - if isinstance(self.include_dirs, str): - self.include_dirs = self.include_dirs.split(os.pathsep) - - # If in a virtualenv, add its include directory - # Issue 16116 - if sys.exec_prefix != sys.base_exec_prefix: - self.include_dirs.append(os.path.join(sys.exec_prefix, 'include')) - - # Put the Python "system" include dir at the end, so that - # any local include dirs take precedence. - self.include_dirs.extend(py_include.split(os.path.pathsep)) - if plat_py_include != py_include: - self.include_dirs.extend( - plat_py_include.split(os.path.pathsep)) - - self.ensure_string_list('libraries') - self.ensure_string_list('link_objects') - - # Life is easier if we're not forever checking for None, so - # simplify these options to empty lists if unset - if self.libraries is None: - self.libraries = [] - if self.library_dirs is None: - self.library_dirs = [] - elif isinstance(self.library_dirs, str): - self.library_dirs = self.library_dirs.split(os.pathsep) - - if self.rpath is None: - self.rpath = [] - elif isinstance(self.rpath, str): - self.rpath = self.rpath.split(os.pathsep) - - # for extensions under windows use different directories - # for Release and Debug builds. - # also Python's library directory must be appended to library_dirs - if os.name == 'nt': - # the 'libs' directory is for binary installs - we assume that - # must be the *native* platform. But we don't really support - # cross-compiling via a binary install anyway, so we let it go. - self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) - if sys.base_exec_prefix != sys.prefix: # Issue 16116 - self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs')) - if self.debug: - self.build_temp = os.path.join(self.build_temp, "Debug") - else: - self.build_temp = os.path.join(self.build_temp, "Release") - - # Append the source distribution include and library directories, - # this allows distutils on windows to work in the source tree - self.include_dirs.append(os.path.dirname(get_config_h_filename())) - _sys_home = getattr(sys, '_home', None) - if _sys_home: - self.library_dirs.append(_sys_home) - - # Use the .lib files for the correct architecture - if self.plat_name == 'win32': - suffix = 'win32' - else: - # win-amd64 - suffix = self.plat_name[4:] - new_lib = os.path.join(sys.exec_prefix, 'PCbuild') - if suffix: - new_lib = os.path.join(new_lib, suffix) - self.library_dirs.append(new_lib) - - # For extensions under Cygwin, Python's library directory must be - # appended to library_dirs - if sys.platform[:6] == 'cygwin': - if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): - # building third party extensions - self.library_dirs.append(os.path.join(sys.prefix, "lib", - "python" + get_python_version(), - "config")) - else: - # building python standard extensions - self.library_dirs.append('.') - - # For building extensions with a shared Python library, - # Python's library directory must be appended to library_dirs - # See Issues: #1600860, #4366 - if (sysconfig.get_config_var('Py_ENABLE_SHARED')): - if not sysconfig.python_build: - # building third party extensions - self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) - else: - # building python standard extensions - self.library_dirs.append('.') - - # The argument parsing will result in self.define being a string, but - # it has to be a list of 2-tuples. All the preprocessor symbols - # specified by the 'define' option will be set to '1'. Multiple - # symbols can be separated with commas. - - if self.define: - defines = self.define.split(',') - self.define = [(symbol, '1') for symbol in defines] - - # The option for macros to undefine is also a string from the - # option parsing, but has to be a list. Multiple symbols can also - # be separated with commas here. - if self.undef: - self.undef = self.undef.split(',') - - if self.swig_opts is None: - self.swig_opts = [] - else: - self.swig_opts = self.swig_opts.split(' ') - - # Finally add the user include and library directories if requested - if self.user: - user_include = os.path.join(USER_BASE, "include") - user_lib = os.path.join(USER_BASE, "lib") - if os.path.isdir(user_include): - self.include_dirs.append(user_include) - if os.path.isdir(user_lib): - self.library_dirs.append(user_lib) - self.rpath.append(user_lib) - - if isinstance(self.parallel, str): - try: - self.parallel = int(self.parallel) - except ValueError: - raise DistutilsOptionError("parallel should be an integer") - - def run(self): - from distutils.ccompiler import new_compiler - - # 'self.extensions', as supplied by setup.py, is a list of - # Extension instances. See the documentation for Extension (in - # distutils.extension) for details. - # - # For backwards compatibility with Distutils 0.8.2 and earlier, we - # also allow the 'extensions' list to be a list of tuples: - # (ext_name, build_info) - # where build_info is a dictionary containing everything that - # Extension instances do except the name, with a few things being - # differently named. We convert these 2-tuples to Extension - # instances as needed. - - if not self.extensions: - return - - # If we were asked to build any C/C++ libraries, make sure that the - # directory where we put them is in the library search path for - # linking extensions. - if self.distribution.has_c_libraries(): - build_clib = self.get_finalized_command('build_clib') - self.libraries.extend(build_clib.get_library_names() or []) - self.library_dirs.append(build_clib.build_clib) - - # Setup the CCompiler object that we'll use to do all the - # compiling and linking - self.compiler = new_compiler(compiler=self.compiler, - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force) - customize_compiler(self.compiler) - # If we are cross-compiling, init the compiler now (if we are not - # cross-compiling, init would not hurt, but people may rely on - # late initialization of compiler even if they shouldn't...) - if os.name == 'nt' and self.plat_name != get_platform(): - self.compiler.initialize(self.plat_name) - - # And make sure that any compile/link-related options (which might - # come from the command-line or from the setup script) are set in - # that CCompiler object -- that way, they automatically apply to - # all compiling and linking done here. - if self.include_dirs is not None: - self.compiler.set_include_dirs(self.include_dirs) - if self.define is not None: - # 'define' option is a list of (name,value) tuples - for (name, value) in self.define: - self.compiler.define_macro(name, value) - if self.undef is not None: - for macro in self.undef: - self.compiler.undefine_macro(macro) - if self.libraries is not None: - self.compiler.set_libraries(self.libraries) - if self.library_dirs is not None: - self.compiler.set_library_dirs(self.library_dirs) - if self.rpath is not None: - self.compiler.set_runtime_library_dirs(self.rpath) - if self.link_objects is not None: - self.compiler.set_link_objects(self.link_objects) - - # Now actually compile and link everything. - self.build_extensions() - - def check_extensions_list(self, extensions): - """Ensure that the list of extensions (presumably provided as a - command option 'extensions') is valid, i.e. it is a list of - Extension objects. We also support the old-style list of 2-tuples, - where the tuples are (ext_name, build_info), which are converted to - Extension instances here. - - Raise DistutilsSetupError if the structure is invalid anywhere; - just returns otherwise. - """ - if not isinstance(extensions, list): - raise DistutilsSetupError( - "'ext_modules' option must be a list of Extension instances") - - for i, ext in enumerate(extensions): - if isinstance(ext, Extension): - continue # OK! (assume type-checking done - # by Extension constructor) - - if not isinstance(ext, tuple) or len(ext) != 2: - raise DistutilsSetupError( - "each element of 'ext_modules' option must be an " - "Extension instance or 2-tuple") - - ext_name, build_info = ext - - log.warn("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s' " - "-- please convert to Extension instance", ext_name) - - if not (isinstance(ext_name, str) and - extension_name_re.match(ext_name)): - raise DistutilsSetupError( - "first element of each tuple in 'ext_modules' " - "must be the extension name (a string)") - - if not isinstance(build_info, dict): - raise DistutilsSetupError( - "second element of each tuple in 'ext_modules' " - "must be a dictionary (build info)") - - # OK, the (ext_name, build_info) dict is type-safe: convert it - # to an Extension instance. - ext = Extension(ext_name, build_info['sources']) - - # Easy stuff: one-to-one mapping from dict elements to - # instance attributes. - for key in ('include_dirs', 'library_dirs', 'libraries', - 'extra_objects', 'extra_compile_args', - 'extra_link_args'): - val = build_info.get(key) - if val is not None: - setattr(ext, key, val) - - # Medium-easy stuff: same syntax/semantics, different names. - ext.runtime_library_dirs = build_info.get('rpath') - if 'def_file' in build_info: - log.warn("'def_file' element of build info dict " - "no longer supported") - - # Non-trivial stuff: 'macros' split into 'define_macros' - # and 'undef_macros'. - macros = build_info.get('macros') - if macros: - ext.define_macros = [] - ext.undef_macros = [] - for macro in macros: - if not (isinstance(macro, tuple) and len(macro) in (1, 2)): - raise DistutilsSetupError( - "'macros' element of build info dict " - "must be 1- or 2-tuple") - if len(macro) == 1: - ext.undef_macros.append(macro[0]) - elif len(macro) == 2: - ext.define_macros.append(macro) - - extensions[i] = ext - - def get_source_files(self): - self.check_extensions_list(self.extensions) - filenames = [] - - # Wouldn't it be neat if we knew the names of header files too... - for ext in self.extensions: - filenames.extend(ext.sources) - return filenames - - def get_outputs(self): - # Sanity check the 'extensions' list -- can't assume this is being - # done in the same run as a 'build_extensions()' call (in fact, we - # can probably assume that it *isn't*!). - self.check_extensions_list(self.extensions) - - # And build the list of output (built) filenames. Note that this - # ignores the 'inplace' flag, and assumes everything goes in the - # "build" tree. - outputs = [] - for ext in self.extensions: - outputs.append(self.get_ext_fullpath(ext.name)) - return outputs - - def build_extensions(self): - # First, sanity-check the 'extensions' list - self.check_extensions_list(self.extensions) - if self.parallel: - self._build_extensions_parallel() - else: - self._build_extensions_serial() - - def _build_extensions_parallel(self): - workers = self.parallel - if self.parallel is True: - workers = os.cpu_count() # may return None - try: - from concurrent.futures import ThreadPoolExecutor - except ImportError: - workers = None - - if workers is None: - self._build_extensions_serial() - return - - with ThreadPoolExecutor(max_workers=workers) as executor: - futures = [executor.submit(self.build_extension, ext) - for ext in self.extensions] - for ext, fut in zip(self.extensions, futures): - with self._filter_build_errors(ext): - fut.result() - - def _build_extensions_serial(self): - for ext in self.extensions: - with self._filter_build_errors(ext): - self.build_extension(ext) - - @contextlib.contextmanager - def _filter_build_errors(self, ext): - try: - yield - except (CCompilerError, DistutilsError, CompileError) as e: - if not ext.optional: - raise - self.warn('building extension "%s" failed: %s' % - (ext.name, e)) - - def build_extension(self, ext): - sources = ext.sources - if sources is None or not isinstance(sources, (list, tuple)): - raise DistutilsSetupError( - "in 'ext_modules' option (extension '%s'), " - "'sources' must be present and must be " - "a list of source filenames" % ext.name) - # sort to make the resulting .so file build reproducible - sources = sorted(sources) - - ext_path = self.get_ext_fullpath(ext.name) - depends = sources + ext.depends - if not (self.force or newer_group(depends, ext_path, 'newer')): - log.debug("skipping '%s' extension (up-to-date)", ext.name) - return - else: - log.info("building '%s' extension", ext.name) - - # First, scan the sources for SWIG definition files (.i), run - # SWIG on 'em to create .c files, and modify the sources list - # accordingly. - sources = self.swig_sources(sources, ext) - - # Next, compile the source code to object files. - - # XXX not honouring 'define_macros' or 'undef_macros' -- the - # CCompiler API needs to change to accommodate this, and I - # want to do one thing at a time! - - # Two possible sources for extra compiler arguments: - # - 'extra_compile_args' in Extension object - # - CFLAGS environment variable (not particularly - # elegant, but people seem to expect it and I - # guess it's useful) - # The environment variable should take precedence, and - # any sensible compiler will give precedence to later - # command line args. Hence we combine them in order: - extra_args = ext.extra_compile_args or [] - - macros = ext.define_macros[:] - for undef in ext.undef_macros: - macros.append((undef,)) - - objects = self.compiler.compile(sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=ext.include_dirs, - debug=self.debug, - extra_postargs=extra_args, - depends=ext.depends) - - # XXX outdated variable, kept here in case third-part code - # needs it. - self._built_objects = objects[:] - - # Now link the object files together into a "shared object" -- - # of course, first we have to figure out all the other things - # that go into the mix. - if ext.extra_objects: - objects.extend(ext.extra_objects) - extra_args = ext.extra_link_args or [] - - # Detect target language, if not provided - language = ext.language or self.compiler.detect_language(sources) - - self.compiler.link_shared_object( - objects, ext_path, - libraries=self.get_libraries(ext), - library_dirs=ext.library_dirs, - runtime_library_dirs=ext.runtime_library_dirs, - extra_postargs=extra_args, - export_symbols=self.get_export_symbols(ext), - debug=self.debug, - build_temp=self.build_temp, - target_lang=language) - - def swig_sources(self, sources, extension): - """Walk the list of source files in 'sources', looking for SWIG - interface (.i) files. Run SWIG on all that are found, and - return a modified 'sources' list with SWIG source files replaced - by the generated C (or C++) files. - """ - new_sources = [] - swig_sources = [] - swig_targets = {} - - # XXX this drops generated C/C++ files into the source tree, which - # is fine for developers who want to distribute the generated - # source -- but there should be an option to put SWIG output in - # the temp dir. - - if self.swig_cpp: - log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") - - if self.swig_cpp or ('-c++' in self.swig_opts) or \ - ('-c++' in extension.swig_opts): - target_ext = '.cpp' - else: - target_ext = '.c' - - for source in sources: - (base, ext) = os.path.splitext(source) - if ext == ".i": # SWIG interface file - new_sources.append(base + '_wrap' + target_ext) - swig_sources.append(source) - swig_targets[source] = new_sources[-1] - else: - new_sources.append(source) - - if not swig_sources: - return new_sources - - swig = self.swig or self.find_swig() - swig_cmd = [swig, "-python"] - swig_cmd.extend(self.swig_opts) - if self.swig_cpp: - swig_cmd.append("-c++") - - # Do not override commandline arguments - if not self.swig_opts: - for o in extension.swig_opts: - swig_cmd.append(o) - - for source in swig_sources: - target = swig_targets[source] - log.info("swigging %s to %s", source, target) - self.spawn(swig_cmd + ["-o", target, source]) - - return new_sources - - def find_swig(self): - """Return the name of the SWIG executable. On Unix, this is - just "swig" -- it should be in the PATH. Tries a bit harder on - Windows. - """ - if os.name == "posix": - return "swig" - elif os.name == "nt": - # Look for SWIG in its standard installation directory on - # Windows (or so I presume!). If we find it there, great; - # if not, act like Unix and assume it's in the PATH. - for vers in ("1.3", "1.2", "1.1"): - fn = os.path.join("c:\\swig%s" % vers, "swig.exe") - if os.path.isfile(fn): - return fn - else: - return "swig.exe" - else: - raise DistutilsPlatformError( - "I don't know how to find (much less run) SWIG " - "on platform '%s'" % os.name) - - # -- Name generators ----------------------------------------------- - # (extension names, filenames, whatever) - def get_ext_fullpath(self, ext_name): - """Returns the path of the filename for a given extension. - - The file is located in `build_lib` or directly in the package - (inplace option). - """ - fullname = self.get_ext_fullname(ext_name) - modpath = fullname.split('.') - filename = self.get_ext_filename(modpath[-1]) - - if not self.inplace: - # no further work needed - # returning : - # build_dir/package/path/filename - filename = os.path.join(*modpath[:-1]+[filename]) - return os.path.join(self.build_lib, filename) - - # the inplace option requires to find the package directory - # using the build_py command for that - package = '.'.join(modpath[0:-1]) - build_py = self.get_finalized_command('build_py') - package_dir = os.path.abspath(build_py.get_package_dir(package)) - - # returning - # package_dir/filename - return os.path.join(package_dir, filename) - - def get_ext_fullname(self, ext_name): - """Returns the fullname of a given extension name. - - Adds the `package.` prefix""" - if self.package is None: - return ext_name - else: - return self.package + '.' + ext_name - - def get_ext_filename(self, ext_name): - r"""Convert the name of an extension (eg. "foo.bar") into the name - of the file from which it will be loaded (eg. "foo/bar.so", or - "foo\bar.pyd"). - """ - from distutils.sysconfig import get_config_var - ext_path = ext_name.split('.') - ext_suffix = get_config_var('EXT_SUFFIX') - return os.path.join(*ext_path) + ext_suffix - - def get_export_symbols(self, ext): - """Return the list of symbols that a shared extension has to - export. This either uses 'ext.export_symbols' or, if it's not - provided, "PyInit_" + module_name. Only relevant on Windows, where - the .pyd file (DLL) must export the module "PyInit_" function. - """ - suffix = '_' + ext.name.split('.')[-1] - try: - # Unicode module name support as defined in PEP-489 - # https://www.python.org/dev/peps/pep-0489/#export-hook-name - suffix.encode('ascii') - except UnicodeEncodeError: - suffix = 'U' + suffix.encode('punycode').replace(b'-', b'_').decode('ascii') - - initfunc_name = "PyInit" + suffix - if initfunc_name not in ext.export_symbols: - ext.export_symbols.append(initfunc_name) - return ext.export_symbols - - def get_libraries(self, ext): - """Return the list of libraries to link against when building a - shared extension. On most platforms, this is just 'ext.libraries'; - on Windows, we add the Python library (eg. python20.dll). - """ - # The python library is always needed on Windows. For MSVC, this - # is redundant, since the library is mentioned in a pragma in - # pyconfig.h that MSVC groks. The other Windows compilers all seem - # to need it mentioned explicitly, though, so that's what we do. - # Append '_d' to the python import library on debug builds. - if sys.platform == "win32": - from distutils._msvccompiler import MSVCCompiler - if not isinstance(self.compiler, MSVCCompiler): - template = "python%d%d" - if self.debug: - template = template + '_d' - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - # don't extend ext.libraries, it may be shared with other - # extensions, it is a reference to the original list - return ext.libraries + [pythonlib] - else: - # On Android only the main executable and LD_PRELOADs are considered - # to be RTLD_GLOBAL, all the dependencies of the main executable - # remain RTLD_LOCAL and so the shared libraries must be linked with - # libpython when python is built with a shared python library (issue - # bpo-21536). - # On Cygwin (and if required, other POSIX-like platforms based on - # Windows like MinGW) it is simply necessary that all symbols in - # shared libraries are resolved at link time. - from distutils.sysconfig import get_config_var - link_libpython = False - if get_config_var('Py_ENABLE_SHARED'): - # A native build on an Android device or on Cygwin - if hasattr(sys, 'getandroidapilevel'): - link_libpython = True - elif sys.platform == 'cygwin': - link_libpython = True - elif '_PYTHON_HOST_PLATFORM' in os.environ: - # We are cross-compiling for one of the relevant platforms - if get_config_var('ANDROID_API_LEVEL') != 0: - link_libpython = True - elif get_config_var('MACHDEP') == 'cygwin': - link_libpython = True - - if link_libpython: - ldversion = get_config_var('LDVERSION') - return ext.libraries + ['python' + ldversion] - - return ext.libraries diff --git a/distutils/command/build_py.py b/distutils/command/build_py.py deleted file mode 100644 index cf0ca57c..00000000 --- a/distutils/command/build_py.py +++ /dev/null @@ -1,416 +0,0 @@ -"""distutils.command.build_py - -Implements the Distutils 'build_py' command.""" - -import os -import importlib.util -import sys -from glob import glob - -from distutils.core import Command -from distutils.errors import * -from distutils.util import convert_path, Mixin2to3 -from distutils import log - -class build_py (Command): - - description = "\"build\" pure Python modules (copy to build directory)" - - user_options = [ - ('build-lib=', 'd', "directory to \"build\" (copy) to"), - ('compile', 'c', "compile .py to .pyc"), - ('no-compile', None, "don't compile .py files [default]"), - ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), - ('force', 'f', "forcibly build everything (ignore file timestamps)"), - ] - - boolean_options = ['compile', 'force'] - negative_opt = {'no-compile' : 'compile'} - - def initialize_options(self): - self.build_lib = None - self.py_modules = None - self.package = None - self.package_data = None - self.package_dir = None - self.compile = 0 - self.optimize = 0 - self.force = None - - def finalize_options(self): - self.set_undefined_options('build', - ('build_lib', 'build_lib'), - ('force', 'force')) - - # Get the distribution options that are aliases for build_py - # options -- list of packages and list of modules. - self.packages = self.distribution.packages - self.py_modules = self.distribution.py_modules - self.package_data = self.distribution.package_data - self.package_dir = {} - if self.distribution.package_dir: - for name, path in self.distribution.package_dir.items(): - self.package_dir[name] = convert_path(path) - self.data_files = self.get_data_files() - - # Ick, copied straight from install_lib.py (fancy_getopt needs a - # type system! Hell, *everything* needs a type system!!!) - if not isinstance(self.optimize, int): - try: - self.optimize = int(self.optimize) - assert 0 <= self.optimize <= 2 - except (ValueError, AssertionError): - raise DistutilsOptionError("optimize must be 0, 1, or 2") - - def run(self): - # XXX copy_file by default preserves atime and mtime. IMHO this is - # the right thing to do, but perhaps it should be an option -- in - # particular, a site administrator might want installed files to - # reflect the time of installation rather than the last - # modification time before the installed release. - - # XXX copy_file by default preserves mode, which appears to be the - # wrong thing to do: if a file is read-only in the working - # directory, we want it to be installed read/write so that the next - # installation of the same module distribution can overwrite it - # without problems. (This might be a Unix-specific issue.) Thus - # we turn off 'preserve_mode' when copying to the build directory, - # since the build directory is supposed to be exactly what the - # installation will look like (ie. we preserve mode when - # installing). - - # Two options control which modules will be installed: 'packages' - # and 'py_modules'. The former lets us work with whole packages, not - # specifying individual modules at all; the latter is for - # specifying modules one-at-a-time. - - if self.py_modules: - self.build_modules() - if self.packages: - self.build_packages() - self.build_package_data() - - self.byte_compile(self.get_outputs(include_bytecode=0)) - - def get_data_files(self): - """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" - data = [] - if not self.packages: - return data - for package in self.packages: - # Locate package source directory - src_dir = self.get_package_dir(package) - - # Compute package build directory - build_dir = os.path.join(*([self.build_lib] + package.split('.'))) - - # Length of path to strip from found files - plen = 0 - if src_dir: - plen = len(src_dir)+1 - - # Strip directory from globbed filenames - filenames = [ - file[plen:] for file in self.find_data_files(package, src_dir) - ] - data.append((package, src_dir, build_dir, filenames)) - return data - - def find_data_files(self, package, src_dir): - """Return filenames for package's data files in 'src_dir'""" - globs = (self.package_data.get('', []) - + self.package_data.get(package, [])) - files = [] - for pattern in globs: - # Each pattern has to be converted to a platform-specific path - filelist = glob(os.path.join(src_dir, convert_path(pattern))) - # Files that match more than one pattern are only added once - files.extend([fn for fn in filelist if fn not in files - and os.path.isfile(fn)]) - return files - - def build_package_data(self): - """Copy data files into build directory""" - lastdir = None - for package, src_dir, build_dir, filenames in self.data_files: - for filename in filenames: - target = os.path.join(build_dir, filename) - self.mkpath(os.path.dirname(target)) - self.copy_file(os.path.join(src_dir, filename), target, - preserve_mode=False) - - def get_package_dir(self, package): - """Return the directory, relative to the top of the source - distribution, where package 'package' should be found - (at least according to the 'package_dir' option, if any).""" - path = package.split('.') - - if not self.package_dir: - if path: - return os.path.join(*path) - else: - return '' - else: - tail = [] - while path: - try: - pdir = self.package_dir['.'.join(path)] - except KeyError: - tail.insert(0, path[-1]) - del path[-1] - else: - tail.insert(0, pdir) - return os.path.join(*tail) - else: - # Oops, got all the way through 'path' without finding a - # match in package_dir. If package_dir defines a directory - # for the root (nameless) package, then fallback on it; - # otherwise, we might as well have not consulted - # package_dir at all, as we just use the directory implied - # by 'tail' (which should be the same as the original value - # of 'path' at this point). - pdir = self.package_dir.get('') - if pdir is not None: - tail.insert(0, pdir) - - if tail: - return os.path.join(*tail) - else: - return '' - - def check_package(self, package, package_dir): - # Empty dir name means current directory, which we can probably - # assume exists. Also, os.path.exists and isdir don't know about - # my "empty string means current dir" convention, so we have to - # circumvent them. - if package_dir != "": - if not os.path.exists(package_dir): - raise DistutilsFileError( - "package directory '%s' does not exist" % package_dir) - if not os.path.isdir(package_dir): - raise DistutilsFileError( - "supposed package directory '%s' exists, " - "but is not a directory" % package_dir) - - # Require __init__.py for all but the "root package" - if package: - init_py = os.path.join(package_dir, "__init__.py") - if os.path.isfile(init_py): - return init_py - else: - log.warn(("package init file '%s' not found " + - "(or not a regular file)"), init_py) - - # Either not in a package at all (__init__.py not expected), or - # __init__.py doesn't exist -- so don't return the filename. - return None - - def check_module(self, module, module_file): - if not os.path.isfile(module_file): - log.warn("file %s (for module %s) not found", module_file, module) - return False - else: - return True - - def find_package_modules(self, package, package_dir): - self.check_package(package, package_dir) - module_files = glob(os.path.join(package_dir, "*.py")) - modules = [] - setup_script = os.path.abspath(self.distribution.script_name) - - for f in module_files: - abs_f = os.path.abspath(f) - if abs_f != setup_script: - module = os.path.splitext(os.path.basename(f))[0] - modules.append((package, module, f)) - else: - self.debug_print("excluding %s" % setup_script) - return modules - - def find_modules(self): - """Finds individually-specified Python modules, ie. those listed by - module name in 'self.py_modules'. Returns a list of tuples (package, - module_base, filename): 'package' is a tuple of the path through - package-space to the module; 'module_base' is the bare (no - packages, no dots) module name, and 'filename' is the path to the - ".py" file (relative to the distribution root) that implements the - module. - """ - # Map package names to tuples of useful info about the package: - # (package_dir, checked) - # package_dir - the directory where we'll find source files for - # this package - # checked - true if we have checked that the package directory - # is valid (exists, contains __init__.py, ... ?) - packages = {} - - # List of (package, module, filename) tuples to return - modules = [] - - # We treat modules-in-packages almost the same as toplevel modules, - # just the "package" for a toplevel is empty (either an empty - # string or empty list, depending on context). Differences: - # - don't check for __init__.py in directory for empty package - for module in self.py_modules: - path = module.split('.') - package = '.'.join(path[0:-1]) - module_base = path[-1] - - try: - (package_dir, checked) = packages[package] - except KeyError: - package_dir = self.get_package_dir(package) - checked = 0 - - if not checked: - init_py = self.check_package(package, package_dir) - packages[package] = (package_dir, 1) - if init_py: - modules.append((package, "__init__", init_py)) - - # XXX perhaps we should also check for just .pyc files - # (so greedy closed-source bastards can distribute Python - # modules too) - module_file = os.path.join(package_dir, module_base + ".py") - if not self.check_module(module, module_file): - continue - - modules.append((package, module_base, module_file)) - - return modules - - def find_all_modules(self): - """Compute the list of all modules that will be built, whether - they are specified one-module-at-a-time ('self.py_modules') or - by whole packages ('self.packages'). Return a list of tuples - (package, module, module_file), just like 'find_modules()' and - 'find_package_modules()' do.""" - modules = [] - if self.py_modules: - modules.extend(self.find_modules()) - if self.packages: - for package in self.packages: - package_dir = self.get_package_dir(package) - m = self.find_package_modules(package, package_dir) - modules.extend(m) - return modules - - def get_source_files(self): - return [module[-1] for module in self.find_all_modules()] - - def get_module_outfile(self, build_dir, package, module): - outfile_path = [build_dir] + list(package) + [module + ".py"] - return os.path.join(*outfile_path) - - def get_outputs(self, include_bytecode=1): - modules = self.find_all_modules() - outputs = [] - for (package, module, module_file) in modules: - package = package.split('.') - filename = self.get_module_outfile(self.build_lib, package, module) - outputs.append(filename) - if include_bytecode: - if self.compile: - outputs.append(importlib.util.cache_from_source( - filename, optimization='')) - if self.optimize > 0: - outputs.append(importlib.util.cache_from_source( - filename, optimization=self.optimize)) - - outputs += [ - os.path.join(build_dir, filename) - for package, src_dir, build_dir, filenames in self.data_files - for filename in filenames - ] - - return outputs - - def build_module(self, module, module_file, package): - if isinstance(package, str): - package = package.split('.') - elif not isinstance(package, (list, tuple)): - raise TypeError( - "'package' must be a string (dot-separated), list, or tuple") - - # Now put the module source file into the "build" area -- this is - # easy, we just copy it somewhere under self.build_lib (the build - # directory for Python source). - outfile = self.get_module_outfile(self.build_lib, package, module) - dir = os.path.dirname(outfile) - self.mkpath(dir) - return self.copy_file(module_file, outfile, preserve_mode=0) - - def build_modules(self): - modules = self.find_modules() - for (package, module, module_file) in modules: - # Now "build" the module -- ie. copy the source file to - # self.build_lib (the build directory for Python source). - # (Actually, it gets copied to the directory for this package - # under self.build_lib.) - self.build_module(module, module_file, package) - - def build_packages(self): - for package in self.packages: - # Get list of (package, module, module_file) tuples based on - # scanning the package directory. 'package' is only included - # in the tuple so that 'find_modules()' and - # 'find_package_tuples()' have a consistent interface; it's - # ignored here (apart from a sanity check). Also, 'module' is - # the *unqualified* module name (ie. no dots, no package -- we - # already know its package!), and 'module_file' is the path to - # the .py file, relative to the current directory - # (ie. including 'package_dir'). - package_dir = self.get_package_dir(package) - modules = self.find_package_modules(package, package_dir) - - # Now loop over the modules we found, "building" each one (just - # copy it to self.build_lib). - for (package_, module, module_file) in modules: - assert package == package_ - self.build_module(module, module_file, package) - - def byte_compile(self, files): - if sys.dont_write_bytecode: - self.warn('byte-compiling is disabled, skipping.') - return - - from distutils.util import byte_compile - prefix = self.build_lib - if prefix[-1] != os.sep: - prefix = prefix + os.sep - - # XXX this code is essentially the same as the 'byte_compile() - # method of the "install_lib" command, except for the determination - # of the 'prefix' string. Hmmm. - if self.compile: - byte_compile(files, optimize=0, - force=self.force, prefix=prefix, dry_run=self.dry_run) - if self.optimize > 0: - byte_compile(files, optimize=self.optimize, - force=self.force, prefix=prefix, dry_run=self.dry_run) - -class build_py_2to3(build_py, Mixin2to3): - def run(self): - self.updated_files = [] - - # Base class code - if self.py_modules: - self.build_modules() - if self.packages: - self.build_packages() - self.build_package_data() - - # 2to3 - self.run_2to3(self.updated_files) - - # Remaining base class code - self.byte_compile(self.get_outputs(include_bytecode=0)) - - def build_module(self, module, module_file, package): - res = build_py.build_module(self, module, module_file, package) - if res[1]: - # file was copied - self.updated_files.append(res[0]) - return res diff --git a/distutils/command/build_scripts.py b/distutils/command/build_scripts.py deleted file mode 100644 index ccc70e64..00000000 --- a/distutils/command/build_scripts.py +++ /dev/null @@ -1,160 +0,0 @@ -"""distutils.command.build_scripts - -Implements the Distutils 'build_scripts' command.""" - -import os, re -from stat import ST_MODE -from distutils import sysconfig -from distutils.core import Command -from distutils.dep_util import newer -from distutils.util import convert_path, Mixin2to3 -from distutils import log -import tokenize - -# check if Python is called on the first line with this expression -first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$') - -class build_scripts(Command): - - description = "\"build\" scripts (copy and fixup #! line)" - - user_options = [ - ('build-dir=', 'd', "directory to \"build\" (copy) to"), - ('force', 'f', "forcibly build everything (ignore file timestamps"), - ('executable=', 'e', "specify final destination interpreter path"), - ] - - boolean_options = ['force'] - - - def initialize_options(self): - self.build_dir = None - self.scripts = None - self.force = None - self.executable = None - self.outfiles = None - - def finalize_options(self): - self.set_undefined_options('build', - ('build_scripts', 'build_dir'), - ('force', 'force'), - ('executable', 'executable')) - self.scripts = self.distribution.scripts - - def get_source_files(self): - return self.scripts - - def run(self): - if not self.scripts: - return - self.copy_scripts() - - - def copy_scripts(self): - r"""Copy each script listed in 'self.scripts'; if it's marked as a - Python script in the Unix way (first line matches 'first_line_re', - ie. starts with "\#!" and contains "python"), then adjust the first - line to refer to the current Python interpreter as we copy. - """ - self.mkpath(self.build_dir) - outfiles = [] - updated_files = [] - for script in self.scripts: - adjust = False - script = convert_path(script) - outfile = os.path.join(self.build_dir, os.path.basename(script)) - outfiles.append(outfile) - - if not self.force and not newer(script, outfile): - log.debug("not copying %s (up-to-date)", script) - continue - - # Always open the file, but ignore failures in dry-run mode -- - # that way, we'll get accurate feedback if we can read the - # script. - try: - f = open(script, "rb") - except OSError: - if not self.dry_run: - raise - f = None - else: - encoding, lines = tokenize.detect_encoding(f.readline) - f.seek(0) - first_line = f.readline() - if not first_line: - self.warn("%s is an empty file (skipping)" % script) - continue - - match = first_line_re.match(first_line) - if match: - adjust = True - post_interp = match.group(1) or b'' - - if adjust: - log.info("copying and adjusting %s -> %s", script, - self.build_dir) - updated_files.append(outfile) - if not self.dry_run: - if not sysconfig.python_build: - executable = self.executable - else: - executable = os.path.join( - sysconfig.get_config_var("BINDIR"), - "python%s%s" % (sysconfig.get_config_var("VERSION"), - sysconfig.get_config_var("EXE"))) - executable = os.fsencode(executable) - shebang = b"#!" + executable + post_interp + b"\n" - # Python parser starts to read a script using UTF-8 until - # it gets a #coding:xxx cookie. The shebang has to be the - # first line of a file, the #coding:xxx cookie cannot be - # written before. So the shebang has to be decodable from - # UTF-8. - try: - shebang.decode('utf-8') - except UnicodeDecodeError: - raise ValueError( - "The shebang ({!r}) is not decodable " - "from utf-8".format(shebang)) - # If the script is encoded to a custom encoding (use a - # #coding:xxx cookie), the shebang has to be decodable from - # the script encoding too. - try: - shebang.decode(encoding) - except UnicodeDecodeError: - raise ValueError( - "The shebang ({!r}) is not decodable " - "from the script encoding ({})" - .format(shebang, encoding)) - with open(outfile, "wb") as outf: - outf.write(shebang) - outf.writelines(f.readlines()) - if f: - f.close() - else: - if f: - f.close() - updated_files.append(outfile) - self.copy_file(script, outfile) - - if os.name == 'posix': - for file in outfiles: - if self.dry_run: - log.info("changing mode of %s", file) - else: - oldmode = os.stat(file)[ST_MODE] & 0o7777 - newmode = (oldmode | 0o555) & 0o7777 - if newmode != oldmode: - log.info("changing mode of %s from %o to %o", - file, oldmode, newmode) - os.chmod(file, newmode) - # XXX should we modify self.outfiles? - return outfiles, updated_files - -class build_scripts_2to3(build_scripts, Mixin2to3): - - def copy_scripts(self): - outfiles, updated_files = build_scripts.copy_scripts(self) - if not self.dry_run: - self.run_2to3(updated_files) - return outfiles, updated_files diff --git a/distutils/command/check.py b/distutils/command/check.py deleted file mode 100644 index ada25006..00000000 --- a/distutils/command/check.py +++ /dev/null @@ -1,148 +0,0 @@ -"""distutils.command.check - -Implements the Distutils 'check' command. -""" -from distutils.core import Command -from distutils.errors import DistutilsSetupError - -try: - # docutils is installed - from docutils.utils import Reporter - from docutils.parsers.rst import Parser - from docutils import frontend - from docutils import nodes - - class SilentReporter(Reporter): - - def __init__(self, source, report_level, halt_level, stream=None, - debug=0, encoding='ascii', error_handler='replace'): - self.messages = [] - Reporter.__init__(self, source, report_level, halt_level, stream, - debug, encoding, error_handler) - - def system_message(self, level, message, *children, **kwargs): - self.messages.append((level, message, children, kwargs)) - return nodes.system_message(message, level=level, - type=self.levels[level], - *children, **kwargs) - - HAS_DOCUTILS = True -except Exception: - # Catch all exceptions because exceptions besides ImportError probably - # indicate that docutils is not ported to Py3k. - HAS_DOCUTILS = False - -class check(Command): - """This command checks the meta-data of the package. - """ - description = ("perform some checks on the package") - user_options = [('metadata', 'm', 'Verify meta-data'), - ('restructuredtext', 'r', - ('Checks if long string meta-data syntax ' - 'are reStructuredText-compliant')), - ('strict', 's', - 'Will exit with an error if a check fails')] - - boolean_options = ['metadata', 'restructuredtext', 'strict'] - - def initialize_options(self): - """Sets default values for options.""" - self.restructuredtext = 0 - self.metadata = 1 - self.strict = 0 - self._warnings = 0 - - def finalize_options(self): - pass - - def warn(self, msg): - """Counts the number of warnings that occurs.""" - self._warnings += 1 - return Command.warn(self, msg) - - def run(self): - """Runs the command.""" - # perform the various tests - if self.metadata: - self.check_metadata() - if self.restructuredtext: - if HAS_DOCUTILS: - self.check_restructuredtext() - elif self.strict: - raise DistutilsSetupError('The docutils package is needed.') - - # let's raise an error in strict mode, if we have at least - # one warning - if self.strict and self._warnings > 0: - raise DistutilsSetupError('Please correct your package.') - - def check_metadata(self): - """Ensures that all required elements of meta-data are supplied. - - Required fields: - name, version, URL - - Recommended fields: - (author and author_email) or (maintainer and maintainer_email)) - - Warns if any are missing. - """ - metadata = self.distribution.metadata - - missing = [] - for attr in ('name', 'version', 'url'): - if not (hasattr(metadata, attr) and getattr(metadata, attr)): - missing.append(attr) - - if missing: - self.warn("missing required meta-data: %s" % ', '.join(missing)) - if metadata.author: - if not metadata.author_email: - self.warn("missing meta-data: if 'author' supplied, " + - "'author_email' should be supplied too") - elif metadata.maintainer: - if not metadata.maintainer_email: - self.warn("missing meta-data: if 'maintainer' supplied, " + - "'maintainer_email' should be supplied too") - else: - self.warn("missing meta-data: either (author and author_email) " + - "or (maintainer and maintainer_email) " + - "should be supplied") - - def check_restructuredtext(self): - """Checks if the long string fields are reST-compliant.""" - data = self.distribution.get_long_description() - for warning in self._check_rst_data(data): - line = warning[-1].get('line') - if line is None: - warning = warning[1] - else: - warning = '%s (line %s)' % (warning[1], line) - self.warn(warning) - - def _check_rst_data(self, data): - """Returns warnings when the provided data doesn't compile.""" - # the include and csv_table directives need this to be a path - source_path = self.distribution.script_name or 'setup.py' - parser = Parser() - settings = frontend.OptionParser(components=(Parser,)).get_default_values() - settings.tab_width = 4 - settings.pep_references = None - settings.rfc_references = None - reporter = SilentReporter(source_path, - settings.report_level, - settings.halt_level, - stream=settings.warning_stream, - debug=settings.debug, - encoding=settings.error_encoding, - error_handler=settings.error_encoding_error_handler) - - document = nodes.document(settings, reporter, source=source_path) - document.note_source(source_path, -1) - try: - parser.parse(data, document) - except AttributeError as e: - reporter.messages.append( - (-1, 'Could not finish the parsing: %s.' % e, '', {})) - - return reporter.messages diff --git a/distutils/command/clean.py b/distutils/command/clean.py deleted file mode 100644 index 0cb27016..00000000 --- a/distutils/command/clean.py +++ /dev/null @@ -1,76 +0,0 @@ -"""distutils.command.clean - -Implements the Distutils 'clean' command.""" - -# contributed by Bastian Kleineidam , added 2000-03-18 - -import os -from distutils.core import Command -from distutils.dir_util import remove_tree -from distutils import log - -class clean(Command): - - description = "clean up temporary files from 'build' command" - user_options = [ - ('build-base=', 'b', - "base build directory (default: 'build.build-base')"), - ('build-lib=', None, - "build directory for all modules (default: 'build.build-lib')"), - ('build-temp=', 't', - "temporary build directory (default: 'build.build-temp')"), - ('build-scripts=', None, - "build directory for scripts (default: 'build.build-scripts')"), - ('bdist-base=', None, - "temporary directory for built distributions"), - ('all', 'a', - "remove all build output, not just temporary by-products") - ] - - boolean_options = ['all'] - - def initialize_options(self): - self.build_base = None - self.build_lib = None - self.build_temp = None - self.build_scripts = None - self.bdist_base = None - self.all = None - - def finalize_options(self): - self.set_undefined_options('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib'), - ('build_scripts', 'build_scripts'), - ('build_temp', 'build_temp')) - self.set_undefined_options('bdist', - ('bdist_base', 'bdist_base')) - - def run(self): - # remove the build/temp. directory (unless it's already - # gone) - if os.path.exists(self.build_temp): - remove_tree(self.build_temp, dry_run=self.dry_run) - else: - log.debug("'%s' does not exist -- can't clean it", - self.build_temp) - - if self.all: - # remove build directories - for directory in (self.build_lib, - self.bdist_base, - self.build_scripts): - if os.path.exists(directory): - remove_tree(directory, dry_run=self.dry_run) - else: - log.warn("'%s' does not exist -- can't clean it", - directory) - - # just for the heck of it, try to remove the base build directory: - # we might have emptied it right now, but if not we don't care - if not self.dry_run: - try: - os.rmdir(self.build_base) - log.info("removing '%s'", self.build_base) - except OSError: - pass diff --git a/distutils/command/command_template b/distutils/command/command_template deleted file mode 100644 index 6106819d..00000000 --- a/distutils/command/command_template +++ /dev/null @@ -1,33 +0,0 @@ -"""distutils.command.x - -Implements the Distutils 'x' command. -""" - -# created 2000/mm/dd, John Doe - -__revision__ = "$Id$" - -from distutils.core import Command - - -class x(Command): - - # Brief (40-50 characters) description of the command - description = "" - - # List of option tuples: long name, short name (None if no short - # name), and help string. - user_options = [('', '', - ""), - ] - - def initialize_options(self): - self. = None - self. = None - self. = None - - def finalize_options(self): - if self.x is None: - self.x = - - def run(self): diff --git a/distutils/command/config.py b/distutils/command/config.py deleted file mode 100644 index aeda408e..00000000 --- a/distutils/command/config.py +++ /dev/null @@ -1,344 +0,0 @@ -"""distutils.command.config - -Implements the Distutils 'config' command, a (mostly) empty command class -that exists mainly to be sub-classed by specific module distributions and -applications. The idea is that while every "config" command is different, -at least they're all named the same, and users always see "config" in the -list of standard commands. Also, this is a good place to put common -configure-like tasks: "try to compile this C code", or "figure out where -this header file lives". -""" - -import os, re - -from distutils.core import Command -from distutils.errors import DistutilsExecError -from distutils.sysconfig import customize_compiler -from distutils import log - -LANG_EXT = {"c": ".c", "c++": ".cxx"} - -class config(Command): - - description = "prepare to build" - - user_options = [ - ('compiler=', None, - "specify the compiler type"), - ('cc=', None, - "specify the compiler executable"), - ('include-dirs=', 'I', - "list of directories to search for header files"), - ('define=', 'D', - "C preprocessor macros to define"), - ('undef=', 'U', - "C preprocessor macros to undefine"), - ('libraries=', 'l', - "external C libraries to link with"), - ('library-dirs=', 'L', - "directories to search for external C libraries"), - - ('noisy', None, - "show every action (compile, link, run, ...) taken"), - ('dump-source', None, - "dump generated source files before attempting to compile them"), - ] - - - # The three standard command methods: since the "config" command - # does nothing by default, these are empty. - - def initialize_options(self): - self.compiler = None - self.cc = None - self.include_dirs = None - self.libraries = None - self.library_dirs = None - - # maximal output for now - self.noisy = 1 - self.dump_source = 1 - - # list of temporary files generated along-the-way that we have - # to clean at some point - self.temp_files = [] - - def finalize_options(self): - if self.include_dirs is None: - self.include_dirs = self.distribution.include_dirs or [] - elif isinstance(self.include_dirs, str): - self.include_dirs = self.include_dirs.split(os.pathsep) - - if self.libraries is None: - self.libraries = [] - elif isinstance(self.libraries, str): - self.libraries = [self.libraries] - - if self.library_dirs is None: - self.library_dirs = [] - elif isinstance(self.library_dirs, str): - self.library_dirs = self.library_dirs.split(os.pathsep) - - def run(self): - pass - - # Utility methods for actual "config" commands. The interfaces are - # loosely based on Autoconf macros of similar names. Sub-classes - # may use these freely. - - def _check_compiler(self): - """Check that 'self.compiler' really is a CCompiler object; - if not, make it one. - """ - # We do this late, and only on-demand, because this is an expensive - # import. - from distutils.ccompiler import CCompiler, new_compiler - if not isinstance(self.compiler, CCompiler): - self.compiler = new_compiler(compiler=self.compiler, - dry_run=self.dry_run, force=1) - customize_compiler(self.compiler) - if self.include_dirs: - self.compiler.set_include_dirs(self.include_dirs) - if self.libraries: - self.compiler.set_libraries(self.libraries) - if self.library_dirs: - self.compiler.set_library_dirs(self.library_dirs) - - def _gen_temp_sourcefile(self, body, headers, lang): - filename = "_configtest" + LANG_EXT[lang] - with open(filename, "w") as file: - if headers: - for header in headers: - file.write("#include <%s>\n" % header) - file.write("\n") - file.write(body) - if body[-1] != "\n": - file.write("\n") - return filename - - def _preprocess(self, body, headers, include_dirs, lang): - src = self._gen_temp_sourcefile(body, headers, lang) - out = "_configtest.i" - self.temp_files.extend([src, out]) - self.compiler.preprocess(src, out, include_dirs=include_dirs) - return (src, out) - - def _compile(self, body, headers, include_dirs, lang): - src = self._gen_temp_sourcefile(body, headers, lang) - if self.dump_source: - dump_file(src, "compiling '%s':" % src) - (obj,) = self.compiler.object_filenames([src]) - self.temp_files.extend([src, obj]) - self.compiler.compile([src], include_dirs=include_dirs) - return (src, obj) - - def _link(self, body, headers, include_dirs, libraries, library_dirs, - lang): - (src, obj) = self._compile(body, headers, include_dirs, lang) - prog = os.path.splitext(os.path.basename(src))[0] - self.compiler.link_executable([obj], prog, - libraries=libraries, - library_dirs=library_dirs, - target_lang=lang) - - if self.compiler.exe_extension is not None: - prog = prog + self.compiler.exe_extension - self.temp_files.append(prog) - - return (src, obj, prog) - - def _clean(self, *filenames): - if not filenames: - filenames = self.temp_files - self.temp_files = [] - log.info("removing: %s", ' '.join(filenames)) - for filename in filenames: - try: - os.remove(filename) - except OSError: - pass - - - # XXX these ignore the dry-run flag: what to do, what to do? even if - # you want a dry-run build, you still need some sort of configuration - # info. My inclination is to make it up to the real config command to - # consult 'dry_run', and assume a default (minimal) configuration if - # true. The problem with trying to do it here is that you'd have to - # return either true or false from all the 'try' methods, neither of - # which is correct. - - # XXX need access to the header search path and maybe default macros. - - def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): - """Construct a source file from 'body' (a string containing lines - of C/C++ code) and 'headers' (a list of header files to include) - and run it through the preprocessor. Return true if the - preprocessor succeeded, false if there were any errors. - ('body' probably isn't of much use, but what the heck.) - """ - from distutils.ccompiler import CompileError - self._check_compiler() - ok = True - try: - self._preprocess(body, headers, include_dirs, lang) - except CompileError: - ok = False - - self._clean() - return ok - - def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, - lang="c"): - """Construct a source file (just like 'try_cpp()'), run it through - the preprocessor, and return true if any line of the output matches - 'pattern'. 'pattern' should either be a compiled regex object or a - string containing a regex. If both 'body' and 'headers' are None, - preprocesses an empty file -- which can be useful to determine the - symbols the preprocessor and compiler set by default. - """ - self._check_compiler() - src, out = self._preprocess(body, headers, include_dirs, lang) - - if isinstance(pattern, str): - pattern = re.compile(pattern) - - with open(out) as file: - match = False - while True: - line = file.readline() - if line == '': - break - if pattern.search(line): - match = True - break - - self._clean() - return match - - def try_compile(self, body, headers=None, include_dirs=None, lang="c"): - """Try to compile a source file built from 'body' and 'headers'. - Return true on success, false otherwise. - """ - from distutils.ccompiler import CompileError - self._check_compiler() - try: - self._compile(body, headers, include_dirs, lang) - ok = True - except CompileError: - ok = False - - log.info(ok and "success!" or "failure.") - self._clean() - return ok - - def try_link(self, body, headers=None, include_dirs=None, libraries=None, - library_dirs=None, lang="c"): - """Try to compile and link a source file, built from 'body' and - 'headers', to executable form. Return true on success, false - otherwise. - """ - from distutils.ccompiler import CompileError, LinkError - self._check_compiler() - try: - self._link(body, headers, include_dirs, - libraries, library_dirs, lang) - ok = True - except (CompileError, LinkError): - ok = False - - log.info(ok and "success!" or "failure.") - self._clean() - return ok - - def try_run(self, body, headers=None, include_dirs=None, libraries=None, - library_dirs=None, lang="c"): - """Try to compile, link to an executable, and run a program - built from 'body' and 'headers'. Return true on success, false - otherwise. - """ - from distutils.ccompiler import CompileError, LinkError - self._check_compiler() - try: - src, obj, exe = self._link(body, headers, include_dirs, - libraries, library_dirs, lang) - self.spawn([exe]) - ok = True - except (CompileError, LinkError, DistutilsExecError): - ok = False - - log.info(ok and "success!" or "failure.") - self._clean() - return ok - - - # -- High-level methods -------------------------------------------- - # (these are the ones that are actually likely to be useful - # when implementing a real-world config command!) - - def check_func(self, func, headers=None, include_dirs=None, - libraries=None, library_dirs=None, decl=0, call=0): - """Determine if function 'func' is available by constructing a - source file that refers to 'func', and compiles and links it. - If everything succeeds, returns true; otherwise returns false. - - The constructed source file starts out by including the header - files listed in 'headers'. If 'decl' is true, it then declares - 'func' (as "int func()"); you probably shouldn't supply 'headers' - and set 'decl' true in the same call, or you might get errors about - a conflicting declarations for 'func'. Finally, the constructed - 'main()' function either references 'func' or (if 'call' is true) - calls it. 'libraries' and 'library_dirs' are used when - linking. - """ - self._check_compiler() - body = [] - if decl: - body.append("int %s ();" % func) - body.append("int main () {") - if call: - body.append(" %s();" % func) - else: - body.append(" %s;" % func) - body.append("}") - body = "\n".join(body) + "\n" - - return self.try_link(body, headers, include_dirs, - libraries, library_dirs) - - def check_lib(self, library, library_dirs=None, headers=None, - include_dirs=None, other_libraries=[]): - """Determine if 'library' is available to be linked against, - without actually checking that any particular symbols are provided - by it. 'headers' will be used in constructing the source file to - be compiled, but the only effect of this is to check if all the - header files listed are available. Any libraries listed in - 'other_libraries' will be included in the link, in case 'library' - has symbols that depend on other libraries. - """ - self._check_compiler() - return self.try_link("int main (void) { }", headers, include_dirs, - [library] + other_libraries, library_dirs) - - def check_header(self, header, include_dirs=None, library_dirs=None, - lang="c"): - """Determine if the system header file named by 'header_file' - exists and can be found by the preprocessor; return true if so, - false otherwise. - """ - return self.try_cpp(body="/* No body */", headers=[header], - include_dirs=include_dirs) - -def dump_file(filename, head=None): - """Dumps a file content into log.info. - - If head is not None, will be dumped before the file content. - """ - if head is None: - log.info('%s', filename) - else: - log.info(head) - file = open(filename) - try: - log.info(file.read()) - finally: - file.close() diff --git a/distutils/command/install.py b/distutils/command/install.py deleted file mode 100644 index 13feeb89..00000000 --- a/distutils/command/install.py +++ /dev/null @@ -1,677 +0,0 @@ -"""distutils.command.install - -Implements the Distutils 'install' command.""" - -import sys -import os - -from distutils import log -from distutils.core import Command -from distutils.debug import DEBUG -from distutils.sysconfig import get_config_vars -from distutils.errors import DistutilsPlatformError -from distutils.file_util import write_file -from distutils.util import convert_path, subst_vars, change_root -from distutils.util import get_platform -from distutils.errors import DistutilsOptionError - -from site import USER_BASE -from site import USER_SITE -HAS_USER_SITE = True - -WINDOWS_SCHEME = { - 'purelib': '$base/Lib/site-packages', - 'platlib': '$base/Lib/site-packages', - 'headers': '$base/Include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', -} - -INSTALL_SCHEMES = { - 'unix_prefix': { - 'purelib': '$base/lib/python$py_version_short/site-packages', - 'platlib': '$platbase/$platlibdir/python$py_version_short/site-packages', - 'headers': '$base/include/python$py_version_short$abiflags/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', - }, - 'unix_home': { - 'purelib': '$base/lib/python', - 'platlib': '$base/$platlibdir/python', - 'headers': '$base/include/python/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', - }, - 'nt': WINDOWS_SCHEME, - 'pypy': { - 'purelib': '$base/site-packages', - 'platlib': '$base/site-packages', - 'headers': '$base/include/$dist_name', - 'scripts': '$base/bin', - 'data' : '$base', - }, - 'pypy_nt': { - 'purelib': '$base/site-packages', - 'platlib': '$base/site-packages', - 'headers': '$base/include/$dist_name', - 'scripts': '$base/Scripts', - 'data' : '$base', - }, - } - -# user site schemes -if HAS_USER_SITE: - INSTALL_SCHEMES['nt_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', - 'scripts': '$userbase/Python$py_version_nodot/Scripts', - 'data' : '$userbase', - } - - INSTALL_SCHEMES['unix_user'] = { - 'purelib': '$usersite', - 'platlib': '$usersite', - 'headers': - '$userbase/include/python$py_version_short$abiflags/$dist_name', - 'scripts': '$userbase/bin', - 'data' : '$userbase', - } - -# The keys to an installation scheme; if any new types of files are to be -# installed, be sure to add an entry to every installation scheme above, -# and to SCHEME_KEYS here. -SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') - - -class install(Command): - - description = "install everything from build directory" - - user_options = [ - # Select installation scheme and set base director(y|ies) - ('prefix=', None, - "installation prefix"), - ('exec-prefix=', None, - "(Unix only) prefix for platform-specific files"), - ('home=', None, - "(Unix only) home directory to install under"), - - # Or, just set the base director(y|ies) - ('install-base=', None, - "base installation directory (instead of --prefix or --home)"), - ('install-platbase=', None, - "base installation directory for platform-specific files " + - "(instead of --exec-prefix or --home)"), - ('root=', None, - "install everything relative to this alternate root directory"), - - # Or, explicitly set the installation scheme - ('install-purelib=', None, - "installation directory for pure Python module distributions"), - ('install-platlib=', None, - "installation directory for non-pure module distributions"), - ('install-lib=', None, - "installation directory for all module distributions " + - "(overrides --install-purelib and --install-platlib)"), - - ('install-headers=', None, - "installation directory for C/C++ headers"), - ('install-scripts=', None, - "installation directory for Python scripts"), - ('install-data=', None, - "installation directory for data files"), - - # Byte-compilation options -- see install_lib.py for details, as - # these are duplicated from there (but only install_lib does - # anything with them). - ('compile', 'c', "compile .py to .pyc [default]"), - ('no-compile', None, "don't compile .py files"), - ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), - - # Miscellaneous control options - ('force', 'f', - "force installation (overwrite any existing files)"), - ('skip-build', None, - "skip rebuilding everything (for testing/debugging)"), - - # Where to install documentation (eventually!) - #('doc-format=', None, "format of documentation to generate"), - #('install-man=', None, "directory for Unix man pages"), - #('install-html=', None, "directory for HTML documentation"), - #('install-info=', None, "directory for GNU info files"), - - ('record=', None, - "filename in which to record list of installed files"), - ] - - boolean_options = ['compile', 'force', 'skip-build'] - - if HAS_USER_SITE: - user_options.append(('user', None, - "install in user site-package '%s'" % USER_SITE)) - boolean_options.append('user') - - negative_opt = {'no-compile' : 'compile'} - - - def initialize_options(self): - """Initializes options.""" - # High-level options: these select both an installation base - # and scheme. - self.prefix = None - self.exec_prefix = None - self.home = None - self.user = 0 - - # These select only the installation base; it's up to the user to - # specify the installation scheme (currently, that means supplying - # the --install-{platlib,purelib,scripts,data} options). - self.install_base = None - self.install_platbase = None - self.root = None - - # These options are the actual installation directories; if not - # supplied by the user, they are filled in using the installation - # scheme implied by prefix/exec-prefix/home and the contents of - # that installation scheme. - self.install_purelib = None # for pure module distributions - self.install_platlib = None # non-pure (dists w/ extensions) - self.install_headers = None # for C/C++ headers - self.install_lib = None # set to either purelib or platlib - self.install_scripts = None - self.install_data = None - self.install_userbase = USER_BASE - self.install_usersite = USER_SITE - - self.compile = None - self.optimize = None - - # Deprecated - # These two are for putting non-packagized distributions into their - # own directory and creating a .pth file if it makes sense. - # 'extra_path' comes from the setup file; 'install_path_file' can - # be turned off if it makes no sense to install a .pth file. (But - # better to install it uselessly than to guess wrong and not - # install it when it's necessary and would be used!) Currently, - # 'install_path_file' is always true unless some outsider meddles - # with it. - self.extra_path = None - self.install_path_file = 1 - - # 'force' forces installation, even if target files are not - # out-of-date. 'skip_build' skips running the "build" command, - # handy if you know it's not necessary. 'warn_dir' (which is *not* - # a user option, it's just there so the bdist_* commands can turn - # it off) determines whether we warn about installing to a - # directory not in sys.path. - self.force = 0 - self.skip_build = 0 - self.warn_dir = 1 - - # These are only here as a conduit from the 'build' command to the - # 'install_*' commands that do the real work. ('build_base' isn't - # actually used anywhere, but it might be useful in future.) They - # are not user options, because if the user told the install - # command where the build directory is, that wouldn't affect the - # build command. - self.build_base = None - self.build_lib = None - - # Not defined yet because we don't know anything about - # documentation yet. - #self.install_man = None - #self.install_html = None - #self.install_info = None - - self.record = None - - - # -- Option finalizing methods ------------------------------------- - # (This is rather more involved than for most commands, - # because this is where the policy for installing third- - # party Python modules on various platforms given a wide - # array of user input is decided. Yes, it's quite complex!) - - def finalize_options(self): - """Finalizes options.""" - # This method (and its helpers, like 'finalize_unix()', - # 'finalize_other()', and 'select_scheme()') is where the default - # installation directories for modules, extension modules, and - # anything else we care to install from a Python module - # distribution. Thus, this code makes a pretty important policy - # statement about how third-party stuff is added to a Python - # installation! Note that the actual work of installation is done - # by the relatively simple 'install_*' commands; they just take - # their orders from the installation directory options determined - # here. - - # Check for errors/inconsistencies in the options; first, stuff - # that's wrong on any platform. - - if ((self.prefix or self.exec_prefix or self.home) and - (self.install_base or self.install_platbase)): - raise DistutilsOptionError( - "must supply either prefix/exec-prefix/home or " + - "install-base/install-platbase -- not both") - - if self.home and (self.prefix or self.exec_prefix): - raise DistutilsOptionError( - "must supply either home or prefix/exec-prefix -- not both") - - if self.user and (self.prefix or self.exec_prefix or self.home or - self.install_base or self.install_platbase): - raise DistutilsOptionError("can't combine user with prefix, " - "exec_prefix/home, or install_(plat)base") - - # Next, stuff that's wrong (or dubious) only on certain platforms. - if os.name != "posix": - if self.exec_prefix: - self.warn("exec-prefix option ignored on this platform") - self.exec_prefix = None - - # Now the interesting logic -- so interesting that we farm it out - # to other methods. The goal of these methods is to set the final - # values for the install_{lib,scripts,data,...} options, using as - # input a heady brew of prefix, exec_prefix, home, install_base, - # install_platbase, user-supplied versions of - # install_{purelib,platlib,lib,scripts,data,...}, and the - # INSTALL_SCHEME dictionary above. Phew! - - self.dump_dirs("pre-finalize_{unix,other}") - - if os.name == 'posix': - self.finalize_unix() - else: - self.finalize_other() - - self.dump_dirs("post-finalize_{unix,other}()") - - # Expand configuration variables, tilde, etc. in self.install_base - # and self.install_platbase -- that way, we can use $base or - # $platbase in the other installation directories and not worry - # about needing recursive variable expansion (shudder). - - py_version = sys.version.split()[0] - (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') - try: - abiflags = sys.abiflags - except AttributeError: - # sys.abiflags may not be defined on all platforms. - abiflags = '' - self.config_vars = {'dist_name': self.distribution.get_name(), - 'dist_version': self.distribution.get_version(), - 'dist_fullname': self.distribution.get_fullname(), - 'py_version': py_version, - 'py_version_short': '%d.%d' % sys.version_info[:2], - 'py_version_nodot': '%d%d' % sys.version_info[:2], - 'sys_prefix': prefix, - 'prefix': prefix, - 'sys_exec_prefix': exec_prefix, - 'exec_prefix': exec_prefix, - 'abiflags': abiflags, - 'platlibdir': getattr(sys, 'platlibdir', 'lib'), - } - - if HAS_USER_SITE: - self.config_vars['userbase'] = self.install_userbase - self.config_vars['usersite'] = self.install_usersite - - self.expand_basedirs() - - self.dump_dirs("post-expand_basedirs()") - - # Now define config vars for the base directories so we can expand - # everything else. - self.config_vars['base'] = self.install_base - self.config_vars['platbase'] = self.install_platbase - - if DEBUG: - from pprint import pprint - print("config vars:") - pprint(self.config_vars) - - # Expand "~" and configuration variables in the installation - # directories. - self.expand_dirs() - - self.dump_dirs("post-expand_dirs()") - - # Create directories in the home dir: - if self.user: - self.create_home_path() - - # Pick the actual directory to install all modules to: either - # install_purelib or install_platlib, depending on whether this - # module distribution is pure or not. Of course, if the user - # already specified install_lib, use their selection. - if self.install_lib is None: - if self.distribution.ext_modules: # has extensions: non-pure - self.install_lib = self.install_platlib - else: - self.install_lib = self.install_purelib - - - # Convert directories from Unix /-separated syntax to the local - # convention. - self.convert_paths('lib', 'purelib', 'platlib', - 'scripts', 'data', 'headers', - 'userbase', 'usersite') - - # Deprecated - # Well, we're not actually fully completely finalized yet: we still - # have to deal with 'extra_path', which is the hack for allowing - # non-packagized module distributions (hello, Numerical Python!) to - # get their own directories. - self.handle_extra_path() - self.install_libbase = self.install_lib # needed for .pth file - self.install_lib = os.path.join(self.install_lib, self.extra_dirs) - - # If a new root directory was supplied, make all the installation - # dirs relative to it. - if self.root is not None: - self.change_roots('libbase', 'lib', 'purelib', 'platlib', - 'scripts', 'data', 'headers') - - self.dump_dirs("after prepending root") - - # Find out the build directories, ie. where to install from. - self.set_undefined_options('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib')) - - # Punt on doc directories for now -- after all, we're punting on - # documentation completely! - - def dump_dirs(self, msg): - """Dumps the list of user options.""" - if not DEBUG: - return - from distutils.fancy_getopt import longopt_xlate - log.debug(msg + ":") - for opt in self.user_options: - opt_name = opt[0] - if opt_name[-1] == "=": - opt_name = opt_name[0:-1] - if opt_name in self.negative_opt: - opt_name = self.negative_opt[opt_name] - opt_name = opt_name.translate(longopt_xlate) - val = not getattr(self, opt_name) - else: - opt_name = opt_name.translate(longopt_xlate) - val = getattr(self, opt_name) - log.debug(" %s: %s", opt_name, val) - - def finalize_unix(self): - """Finalizes options for posix platforms.""" - if self.install_base is not None or self.install_platbase is not None: - if ((self.install_lib is None and - self.install_purelib is None and - self.install_platlib is None) or - self.install_headers is None or - self.install_scripts is None or - self.install_data is None): - raise DistutilsOptionError( - "install-base or install-platbase supplied, but " - "installation scheme is incomplete") - return - - if self.user: - if self.install_userbase is None: - raise DistutilsPlatformError( - "User base directory is not specified") - self.install_base = self.install_platbase = self.install_userbase - self.select_scheme("unix_user") - elif self.home is not None: - self.install_base = self.install_platbase = self.home - self.select_scheme("unix_home") - else: - if self.prefix is None: - if self.exec_prefix is not None: - raise DistutilsOptionError( - "must not supply exec-prefix without prefix") - - self.prefix = os.path.normpath(sys.prefix) - self.exec_prefix = os.path.normpath(sys.exec_prefix) - - else: - if self.exec_prefix is None: - self.exec_prefix = self.prefix - - self.install_base = self.prefix - self.install_platbase = self.exec_prefix - self.select_scheme("unix_prefix") - - def finalize_other(self): - """Finalizes options for non-posix platforms""" - if self.user: - if self.install_userbase is None: - raise DistutilsPlatformError( - "User base directory is not specified") - self.install_base = self.install_platbase = self.install_userbase - self.select_scheme(os.name + "_user") - elif self.home is not None: - self.install_base = self.install_platbase = self.home - self.select_scheme("unix_home") - else: - if self.prefix is None: - self.prefix = os.path.normpath(sys.prefix) - - self.install_base = self.install_platbase = self.prefix - try: - self.select_scheme(os.name) - except KeyError: - raise DistutilsPlatformError( - "I don't know how to install stuff on '%s'" % os.name) - - def select_scheme(self, name): - """Sets the install directories by applying the install schemes.""" - # it's the caller's problem if they supply a bad name! - if (hasattr(sys, 'pypy_version_info') and - not name.endswith(('_user', '_home'))): - if os.name == 'nt': - name = 'pypy_nt' - else: - name = 'pypy' - scheme = INSTALL_SCHEMES[name] - for key in SCHEME_KEYS: - attrname = 'install_' + key - if getattr(self, attrname) is None: - setattr(self, attrname, scheme[key]) - - def _expand_attrs(self, attrs): - for attr in attrs: - val = getattr(self, attr) - if val is not None: - if os.name == 'posix' or os.name == 'nt': - val = os.path.expanduser(val) - val = subst_vars(val, self.config_vars) - setattr(self, attr, val) - - def expand_basedirs(self): - """Calls `os.path.expanduser` on install_base, install_platbase and - root.""" - self._expand_attrs(['install_base', 'install_platbase', 'root']) - - def expand_dirs(self): - """Calls `os.path.expanduser` on install dirs.""" - self._expand_attrs(['install_purelib', 'install_platlib', - 'install_lib', 'install_headers', - 'install_scripts', 'install_data',]) - - def convert_paths(self, *names): - """Call `convert_path` over `names`.""" - for name in names: - attr = "install_" + name - setattr(self, attr, convert_path(getattr(self, attr))) - - def handle_extra_path(self): - """Set `path_file` and `extra_dirs` using `extra_path`.""" - if self.extra_path is None: - self.extra_path = self.distribution.extra_path - - if self.extra_path is not None: - log.warn( - "Distribution option extra_path is deprecated. " - "See issue27919 for details." - ) - if isinstance(self.extra_path, str): - self.extra_path = self.extra_path.split(',') - - if len(self.extra_path) == 1: - path_file = extra_dirs = self.extra_path[0] - elif len(self.extra_path) == 2: - path_file, extra_dirs = self.extra_path - else: - raise DistutilsOptionError( - "'extra_path' option must be a list, tuple, or " - "comma-separated string with 1 or 2 elements") - - # convert to local form in case Unix notation used (as it - # should be in setup scripts) - extra_dirs = convert_path(extra_dirs) - else: - path_file = None - extra_dirs = '' - - # XXX should we warn if path_file and not extra_dirs? (in which - # case the path file would be harmless but pointless) - self.path_file = path_file - self.extra_dirs = extra_dirs - - def change_roots(self, *names): - """Change the install directories pointed by name using root.""" - for name in names: - attr = "install_" + name - setattr(self, attr, change_root(self.root, getattr(self, attr))) - - def create_home_path(self): - """Create directories under ~.""" - if not self.user: - return - home = convert_path(os.path.expanduser("~")) - for name, path in self.config_vars.items(): - if path.startswith(home) and not os.path.isdir(path): - self.debug_print("os.makedirs('%s', 0o700)" % path) - os.makedirs(path, 0o700) - - # -- Command execution methods ------------------------------------- - - def run(self): - """Runs the command.""" - # Obviously have to build before we can install - if not self.skip_build: - self.run_command('build') - # If we built for any other platform, we can't install. - build_plat = self.distribution.get_command_obj('build').plat_name - # check warn_dir - it is a clue that the 'install' is happening - # internally, and not to sys.path, so we don't check the platform - # matches what we are running. - if self.warn_dir and build_plat != get_platform(): - raise DistutilsPlatformError("Can't install when " - "cross-compiling") - - # Run all sub-commands (at least those that need to be run) - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) - - if self.path_file: - self.create_path_file() - - # write list of installed files, if requested. - if self.record: - outputs = self.get_outputs() - if self.root: # strip any package prefix - root_len = len(self.root) - for counter in range(len(outputs)): - outputs[counter] = outputs[counter][root_len:] - self.execute(write_file, - (self.record, outputs), - "writing list of installed files to '%s'" % - self.record) - - sys_path = map(os.path.normpath, sys.path) - sys_path = map(os.path.normcase, sys_path) - install_lib = os.path.normcase(os.path.normpath(self.install_lib)) - if (self.warn_dir and - not (self.path_file and self.install_path_file) and - install_lib not in sys_path): - log.debug(("modules installed to '%s', which is not in " - "Python's module search path (sys.path) -- " - "you'll have to change the search path yourself"), - self.install_lib) - - def create_path_file(self): - """Creates the .pth file""" - filename = os.path.join(self.install_libbase, - self.path_file + ".pth") - if self.install_path_file: - self.execute(write_file, - (filename, [self.extra_dirs]), - "creating %s" % filename) - else: - self.warn("path file '%s' not created" % filename) - - - # -- Reporting methods --------------------------------------------- - - def get_outputs(self): - """Assembles the outputs of all the sub-commands.""" - outputs = [] - for cmd_name in self.get_sub_commands(): - cmd = self.get_finalized_command(cmd_name) - # Add the contents of cmd.get_outputs(), ensuring - # that outputs doesn't contain duplicate entries - for filename in cmd.get_outputs(): - if filename not in outputs: - outputs.append(filename) - - if self.path_file and self.install_path_file: - outputs.append(os.path.join(self.install_libbase, - self.path_file + ".pth")) - - return outputs - - def get_inputs(self): - """Returns the inputs of all the sub-commands""" - # XXX gee, this looks familiar ;-( - inputs = [] - for cmd_name in self.get_sub_commands(): - cmd = self.get_finalized_command(cmd_name) - inputs.extend(cmd.get_inputs()) - - return inputs - - # -- Predicates for sub-command list ------------------------------- - - def has_lib(self): - """Returns true if the current distribution has any Python - modules to install.""" - return (self.distribution.has_pure_modules() or - self.distribution.has_ext_modules()) - - def has_headers(self): - """Returns true if the current distribution has any headers to - install.""" - return self.distribution.has_headers() - - def has_scripts(self): - """Returns true if the current distribution has any scripts to. - install.""" - return self.distribution.has_scripts() - - def has_data(self): - """Returns true if the current distribution has any data to. - install.""" - return self.distribution.has_data_files() - - # 'sub_commands': a list of commands this command might have to run to - # get its work done. See cmd.py for more info. - sub_commands = [('install_lib', has_lib), - ('install_headers', has_headers), - ('install_scripts', has_scripts), - ('install_data', has_data), - ('install_egg_info', lambda self:True), - ] diff --git a/distutils/command/install_data.py b/distutils/command/install_data.py deleted file mode 100644 index 947cd76a..00000000 --- a/distutils/command/install_data.py +++ /dev/null @@ -1,79 +0,0 @@ -"""distutils.command.install_data - -Implements the Distutils 'install_data' command, for installing -platform-independent data files.""" - -# contributed by Bastian Kleineidam - -import os -from distutils.core import Command -from distutils.util import change_root, convert_path - -class install_data(Command): - - description = "install data files" - - user_options = [ - ('install-dir=', 'd', - "base directory for installing data files " - "(default: installation base dir)"), - ('root=', None, - "install everything relative to this alternate root directory"), - ('force', 'f', "force installation (overwrite existing files)"), - ] - - boolean_options = ['force'] - - def initialize_options(self): - self.install_dir = None - self.outfiles = [] - self.root = None - self.force = 0 - self.data_files = self.distribution.data_files - self.warn_dir = 1 - - def finalize_options(self): - self.set_undefined_options('install', - ('install_data', 'install_dir'), - ('root', 'root'), - ('force', 'force'), - ) - - def run(self): - self.mkpath(self.install_dir) - for f in self.data_files: - if isinstance(f, str): - # it's a simple file, so copy it - f = convert_path(f) - if self.warn_dir: - self.warn("setup script did not provide a directory for " - "'%s' -- installing right in '%s'" % - (f, self.install_dir)) - (out, _) = self.copy_file(f, self.install_dir) - self.outfiles.append(out) - else: - # it's a tuple with path to install to and a list of files - dir = convert_path(f[0]) - if not os.path.isabs(dir): - dir = os.path.join(self.install_dir, dir) - elif self.root: - dir = change_root(self.root, dir) - self.mkpath(dir) - - if f[1] == []: - # If there are no files listed, the user must be - # trying to create an empty directory, so add the - # directory to the list of output files. - self.outfiles.append(dir) - else: - # Copy files, adding them to the list of output files. - for data in f[1]: - data = convert_path(data) - (out, _) = self.copy_file(data, dir) - self.outfiles.append(out) - - def get_inputs(self): - return self.data_files or [] - - def get_outputs(self): - return self.outfiles diff --git a/distutils/command/install_egg_info.py b/distutils/command/install_egg_info.py deleted file mode 100644 index 0ddc7367..00000000 --- a/distutils/command/install_egg_info.py +++ /dev/null @@ -1,77 +0,0 @@ -"""distutils.command.install_egg_info - -Implements the Distutils 'install_egg_info' command, for installing -a package's PKG-INFO metadata.""" - - -from distutils.cmd import Command -from distutils import log, dir_util -import os, sys, re - -class install_egg_info(Command): - """Install an .egg-info file for the package""" - - description = "Install package's PKG-INFO metadata as an .egg-info file" - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ] - - def initialize_options(self): - self.install_dir = None - - def finalize_options(self): - self.set_undefined_options('install_lib',('install_dir','install_dir')) - basename = "%s-%s-py%d.%d.egg-info" % ( - to_filename(safe_name(self.distribution.get_name())), - to_filename(safe_version(self.distribution.get_version())), - *sys.version_info[:2] - ) - self.target = os.path.join(self.install_dir, basename) - self.outputs = [self.target] - - def run(self): - target = self.target - if os.path.isdir(target) and not os.path.islink(target): - dir_util.remove_tree(target, dry_run=self.dry_run) - elif os.path.exists(target): - self.execute(os.unlink,(self.target,),"Removing "+target) - elif not os.path.isdir(self.install_dir): - self.execute(os.makedirs, (self.install_dir,), - "Creating "+self.install_dir) - log.info("Writing %s", target) - if not self.dry_run: - with open(target, 'w', encoding='UTF-8') as f: - self.distribution.metadata.write_pkg_file(f) - - def get_outputs(self): - return self.outputs - - -# The following routines are taken from setuptools' pkg_resources module and -# can be replaced by importing them from pkg_resources once it is included -# in the stdlib. - -def safe_name(name): - """Convert an arbitrary string to a standard distribution name - - Any runs of non-alphanumeric/. characters are replaced with a single '-'. - """ - return re.sub('[^A-Za-z0-9.]+', '-', name) - - -def safe_version(version): - """Convert an arbitrary string to a standard version string - - Spaces become dots, and all other non-alphanumeric characters become - dashes, with runs of multiple dashes condensed to a single dash. - """ - version = version.replace(' ','.') - return re.sub('[^A-Za-z0-9.]+', '-', version) - - -def to_filename(name): - """Convert a project or version name to its filename-escaped form - - Any '-' characters are currently replaced with '_'. - """ - return name.replace('-','_') diff --git a/distutils/command/install_headers.py b/distutils/command/install_headers.py deleted file mode 100644 index 9bb0b18d..00000000 --- a/distutils/command/install_headers.py +++ /dev/null @@ -1,47 +0,0 @@ -"""distutils.command.install_headers - -Implements the Distutils 'install_headers' command, to install C/C++ header -files to the Python include directory.""" - -from distutils.core import Command - - -# XXX force is never used -class install_headers(Command): - - description = "install C/C++ header files" - - user_options = [('install-dir=', 'd', - "directory to install header files to"), - ('force', 'f', - "force installation (overwrite existing files)"), - ] - - boolean_options = ['force'] - - def initialize_options(self): - self.install_dir = None - self.force = 0 - self.outfiles = [] - - def finalize_options(self): - self.set_undefined_options('install', - ('install_headers', 'install_dir'), - ('force', 'force')) - - - def run(self): - headers = self.distribution.headers - if not headers: - return - - self.mkpath(self.install_dir) - for header in headers: - (out, _) = self.copy_file(header, self.install_dir) - self.outfiles.append(out) - - def get_inputs(self): - return self.distribution.headers or [] - - def get_outputs(self): - return self.outfiles diff --git a/distutils/command/install_lib.py b/distutils/command/install_lib.py deleted file mode 100644 index 6154cf09..00000000 --- a/distutils/command/install_lib.py +++ /dev/null @@ -1,217 +0,0 @@ -"""distutils.command.install_lib - -Implements the Distutils 'install_lib' command -(install all Python modules).""" - -import os -import importlib.util -import sys - -from distutils.core import Command -from distutils.errors import DistutilsOptionError - - -# Extension for Python source files. -PYTHON_SOURCE_EXTENSION = ".py" - -class install_lib(Command): - - description = "install all Python modules (extensions and pure Python)" - - # The byte-compilation options are a tad confusing. Here are the - # possible scenarios: - # 1) no compilation at all (--no-compile --no-optimize) - # 2) compile .pyc only (--compile --no-optimize; default) - # 3) compile .pyc and "opt-1" .pyc (--compile --optimize) - # 4) compile "opt-1" .pyc only (--no-compile --optimize) - # 5) compile .pyc and "opt-2" .pyc (--compile --optimize-more) - # 6) compile "opt-2" .pyc only (--no-compile --optimize-more) - # - # The UI for this is two options, 'compile' and 'optimize'. - # 'compile' is strictly boolean, and only decides whether to - # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and - # decides both whether to generate .pyc files and what level of - # optimization to use. - - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ('build-dir=','b', "build directory (where to install from)"), - ('force', 'f', "force installation (overwrite existing files)"), - ('compile', 'c', "compile .py to .pyc [default]"), - ('no-compile', None, "don't compile .py files"), - ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), - ('skip-build', None, "skip the build steps"), - ] - - boolean_options = ['force', 'compile', 'skip-build'] - negative_opt = {'no-compile' : 'compile'} - - def initialize_options(self): - # let the 'install' command dictate our installation directory - self.install_dir = None - self.build_dir = None - self.force = 0 - self.compile = None - self.optimize = None - self.skip_build = None - - def finalize_options(self): - # Get all the information we need to install pure Python modules - # from the umbrella 'install' command -- build (source) directory, - # install (target) directory, and whether to compile .py files. - self.set_undefined_options('install', - ('build_lib', 'build_dir'), - ('install_lib', 'install_dir'), - ('force', 'force'), - ('compile', 'compile'), - ('optimize', 'optimize'), - ('skip_build', 'skip_build'), - ) - - if self.compile is None: - self.compile = True - if self.optimize is None: - self.optimize = False - - if not isinstance(self.optimize, int): - try: - self.optimize = int(self.optimize) - if self.optimize not in (0, 1, 2): - raise AssertionError - except (ValueError, AssertionError): - raise DistutilsOptionError("optimize must be 0, 1, or 2") - - def run(self): - # Make sure we have built everything we need first - self.build() - - # Install everything: simply dump the entire contents of the build - # directory to the installation directory (that's the beauty of - # having a build directory!) - outfiles = self.install() - - # (Optionally) compile .py to .pyc - if outfiles is not None and self.distribution.has_pure_modules(): - self.byte_compile(outfiles) - - # -- Top-level worker functions ------------------------------------ - # (called from 'run()') - - def build(self): - if not self.skip_build: - if self.distribution.has_pure_modules(): - self.run_command('build_py') - if self.distribution.has_ext_modules(): - self.run_command('build_ext') - - def install(self): - if os.path.isdir(self.build_dir): - outfiles = self.copy_tree(self.build_dir, self.install_dir) - else: - self.warn("'%s' does not exist -- no Python modules to install" % - self.build_dir) - return - return outfiles - - def byte_compile(self, files): - if sys.dont_write_bytecode: - self.warn('byte-compiling is disabled, skipping.') - return - - from distutils.util import byte_compile - - # Get the "--root" directory supplied to the "install" command, - # and use it as a prefix to strip off the purported filename - # encoded in bytecode files. This is far from complete, but it - # should at least generate usable bytecode in RPM distributions. - install_root = self.get_finalized_command('install').root - - if self.compile: - byte_compile(files, optimize=0, - force=self.force, prefix=install_root, - dry_run=self.dry_run) - if self.optimize > 0: - byte_compile(files, optimize=self.optimize, - force=self.force, prefix=install_root, - verbose=self.verbose, dry_run=self.dry_run) - - - # -- Utility methods ----------------------------------------------- - - def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir): - if not has_any: - return [] - - build_cmd = self.get_finalized_command(build_cmd) - build_files = build_cmd.get_outputs() - build_dir = getattr(build_cmd, cmd_option) - - prefix_len = len(build_dir) + len(os.sep) - outputs = [] - for file in build_files: - outputs.append(os.path.join(output_dir, file[prefix_len:])) - - return outputs - - def _bytecode_filenames(self, py_filenames): - bytecode_files = [] - for py_file in py_filenames: - # Since build_py handles package data installation, the - # list of outputs can contain more than just .py files. - # Make sure we only report bytecode for the .py files. - ext = os.path.splitext(os.path.normcase(py_file))[1] - if ext != PYTHON_SOURCE_EXTENSION: - continue - if self.compile: - bytecode_files.append(importlib.util.cache_from_source( - py_file, optimization='')) - if self.optimize > 0: - bytecode_files.append(importlib.util.cache_from_source( - py_file, optimization=self.optimize)) - - return bytecode_files - - - # -- External interface -------------------------------------------- - # (called by outsiders) - - def get_outputs(self): - """Return the list of files that would be installed if this command - were actually run. Not affected by the "dry-run" flag or whether - modules have actually been built yet. - """ - pure_outputs = \ - self._mutate_outputs(self.distribution.has_pure_modules(), - 'build_py', 'build_lib', - self.install_dir) - if self.compile: - bytecode_outputs = self._bytecode_filenames(pure_outputs) - else: - bytecode_outputs = [] - - ext_outputs = \ - self._mutate_outputs(self.distribution.has_ext_modules(), - 'build_ext', 'build_lib', - self.install_dir) - - return pure_outputs + bytecode_outputs + ext_outputs - - def get_inputs(self): - """Get the list of files that are input to this command, ie. the - files that get installed as they are named in the build tree. - The files in this list correspond one-to-one to the output - filenames returned by 'get_outputs()'. - """ - inputs = [] - - if self.distribution.has_pure_modules(): - build_py = self.get_finalized_command('build_py') - inputs.extend(build_py.get_outputs()) - - if self.distribution.has_ext_modules(): - build_ext = self.get_finalized_command('build_ext') - inputs.extend(build_ext.get_outputs()) - - return inputs diff --git a/distutils/command/install_scripts.py b/distutils/command/install_scripts.py deleted file mode 100644 index 31a1130e..00000000 --- a/distutils/command/install_scripts.py +++ /dev/null @@ -1,60 +0,0 @@ -"""distutils.command.install_scripts - -Implements the Distutils 'install_scripts' command, for installing -Python scripts.""" - -# contributed by Bastian Kleineidam - -import os -from distutils.core import Command -from distutils import log -from stat import ST_MODE - - -class install_scripts(Command): - - description = "install scripts (Python or otherwise)" - - user_options = [ - ('install-dir=', 'd', "directory to install scripts to"), - ('build-dir=','b', "build directory (where to install from)"), - ('force', 'f', "force installation (overwrite existing files)"), - ('skip-build', None, "skip the build steps"), - ] - - boolean_options = ['force', 'skip-build'] - - def initialize_options(self): - self.install_dir = None - self.force = 0 - self.build_dir = None - self.skip_build = None - - def finalize_options(self): - self.set_undefined_options('build', ('build_scripts', 'build_dir')) - self.set_undefined_options('install', - ('install_scripts', 'install_dir'), - ('force', 'force'), - ('skip_build', 'skip_build'), - ) - - def run(self): - if not self.skip_build: - self.run_command('build_scripts') - self.outfiles = self.copy_tree(self.build_dir, self.install_dir) - if os.name == 'posix': - # Set the executable bits (owner, group, and world) on - # all the scripts we just installed. - for file in self.get_outputs(): - if self.dry_run: - log.info("changing mode of %s", file) - else: - mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777 - log.info("changing mode of %s to %o", file, mode) - os.chmod(file, mode) - - def get_inputs(self): - return self.distribution.scripts or [] - - def get_outputs(self): - return self.outfiles or [] diff --git a/distutils/command/register.py b/distutils/command/register.py deleted file mode 100644 index 0fac94e9..00000000 --- a/distutils/command/register.py +++ /dev/null @@ -1,304 +0,0 @@ -"""distutils.command.register - -Implements the Distutils 'register' command (register with the repository). -""" - -# created 2002/10/21, Richard Jones - -import getpass -import io -import urllib.parse, urllib.request -from warnings import warn - -from distutils.core import PyPIRCCommand -from distutils.errors import * -from distutils import log - -class register(PyPIRCCommand): - - description = ("register the distribution with the Python package index") - user_options = PyPIRCCommand.user_options + [ - ('list-classifiers', None, - 'list the valid Trove classifiers'), - ('strict', None , - 'Will stop the registering if the meta-data are not fully compliant') - ] - boolean_options = PyPIRCCommand.boolean_options + [ - 'verify', 'list-classifiers', 'strict'] - - sub_commands = [('check', lambda self: True)] - - def initialize_options(self): - PyPIRCCommand.initialize_options(self) - self.list_classifiers = 0 - self.strict = 0 - - def finalize_options(self): - PyPIRCCommand.finalize_options(self) - # setting options for the `check` subcommand - check_options = {'strict': ('register', self.strict), - 'restructuredtext': ('register', 1)} - self.distribution.command_options['check'] = check_options - - def run(self): - self.finalize_options() - self._set_config() - - # Run sub commands - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) - - if self.dry_run: - self.verify_metadata() - elif self.list_classifiers: - self.classifiers() - else: - self.send_metadata() - - def check_metadata(self): - """Deprecated API.""" - warn("distutils.command.register.check_metadata is deprecated, \ - use the check command instead", PendingDeprecationWarning) - check = self.distribution.get_command_obj('check') - check.ensure_finalized() - check.strict = self.strict - check.restructuredtext = 1 - check.run() - - def _set_config(self): - ''' Reads the configuration file and set attributes. - ''' - config = self._read_pypirc() - if config != {}: - self.username = config['username'] - self.password = config['password'] - self.repository = config['repository'] - self.realm = config['realm'] - self.has_config = True - else: - if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): - raise ValueError('%s not found in .pypirc' % self.repository) - if self.repository == 'pypi': - self.repository = self.DEFAULT_REPOSITORY - self.has_config = False - - def classifiers(self): - ''' Fetch the list of classifiers from the server. - ''' - url = self.repository+'?:action=list_classifiers' - response = urllib.request.urlopen(url) - log.info(self._read_pypi_response(response)) - - def verify_metadata(self): - ''' Send the metadata to the package index server to be checked. - ''' - # send the info to the server and report the result - (code, result) = self.post_to_server(self.build_post_data('verify')) - log.info('Server response (%s): %s', code, result) - - def send_metadata(self): - ''' Send the metadata to the package index server. - - Well, do the following: - 1. figure who the user is, and then - 2. send the data as a Basic auth'ed POST. - - First we try to read the username/password from $HOME/.pypirc, - which is a ConfigParser-formatted file with a section - [distutils] containing username and password entries (both - in clear text). Eg: - - [distutils] - index-servers = - pypi - - [pypi] - username: fred - password: sekrit - - Otherwise, to figure who the user is, we offer the user three - choices: - - 1. use existing login, - 2. register as a new user, or - 3. set the password to a random string and email the user. - - ''' - # see if we can short-cut and get the username/password from the - # config - if self.has_config: - choice = '1' - username = self.username - password = self.password - else: - choice = 'x' - username = password = '' - - # get the user's login info - choices = '1 2 3 4'.split() - while choice not in choices: - self.announce('''\ -We need to know who you are, so please choose either: - 1. use your existing login, - 2. register as a new user, - 3. have the server generate a new password for you (and email it to you), or - 4. quit -Your selection [default 1]: ''', log.INFO) - choice = input() - if not choice: - choice = '1' - elif choice not in choices: - print('Please choose one of the four options!') - - if choice == '1': - # get the username and password - while not username: - username = input('Username: ') - while not password: - password = getpass.getpass('Password: ') - - # set up the authentication - auth = urllib.request.HTTPPasswordMgr() - host = urllib.parse.urlparse(self.repository)[1] - auth.add_password(self.realm, host, username, password) - # send the info to the server and report the result - code, result = self.post_to_server(self.build_post_data('submit'), - auth) - self.announce('Server response (%s): %s' % (code, result), - log.INFO) - - # possibly save the login - if code == 200: - if self.has_config: - # sharing the password in the distribution instance - # so the upload command can reuse it - self.distribution.password = password - else: - self.announce(('I can store your PyPI login so future ' - 'submissions will be faster.'), log.INFO) - self.announce('(the login will be stored in %s)' % \ - self._get_rc_file(), log.INFO) - choice = 'X' - while choice.lower() not in 'yn': - choice = input('Save your login (y/N)?') - if not choice: - choice = 'n' - if choice.lower() == 'y': - self._store_pypirc(username, password) - - elif choice == '2': - data = {':action': 'user'} - data['name'] = data['password'] = data['email'] = '' - data['confirm'] = None - while not data['name']: - data['name'] = input('Username: ') - while data['password'] != data['confirm']: - while not data['password']: - data['password'] = getpass.getpass('Password: ') - while not data['confirm']: - data['confirm'] = getpass.getpass(' Confirm: ') - if data['password'] != data['confirm']: - data['password'] = '' - data['confirm'] = None - print("Password and confirm don't match!") - while not data['email']: - data['email'] = input(' EMail: ') - code, result = self.post_to_server(data) - if code != 200: - log.info('Server response (%s): %s', code, result) - else: - log.info('You will receive an email shortly.') - log.info(('Follow the instructions in it to ' - 'complete registration.')) - elif choice == '3': - data = {':action': 'password_reset'} - data['email'] = '' - while not data['email']: - data['email'] = input('Your email address: ') - code, result = self.post_to_server(data) - log.info('Server response (%s): %s', code, result) - - def build_post_data(self, action): - # figure the data to send - the metadata plus some additional - # information used by the package server - meta = self.distribution.metadata - data = { - ':action': action, - 'metadata_version' : '1.0', - 'name': meta.get_name(), - 'version': meta.get_version(), - 'summary': meta.get_description(), - 'home_page': meta.get_url(), - 'author': meta.get_contact(), - 'author_email': meta.get_contact_email(), - 'license': meta.get_licence(), - 'description': meta.get_long_description(), - 'keywords': meta.get_keywords(), - 'platform': meta.get_platforms(), - 'classifiers': meta.get_classifiers(), - 'download_url': meta.get_download_url(), - # PEP 314 - 'provides': meta.get_provides(), - 'requires': meta.get_requires(), - 'obsoletes': meta.get_obsoletes(), - } - if data['provides'] or data['requires'] or data['obsoletes']: - data['metadata_version'] = '1.1' - return data - - def post_to_server(self, data, auth=None): - ''' Post a query to the server, and return a string response. - ''' - if 'name' in data: - self.announce('Registering %s to %s' % (data['name'], - self.repository), - log.INFO) - # Build up the MIME payload for the urllib2 POST data - boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = '\n--' + boundary - end_boundary = sep_boundary + '--' - body = io.StringIO() - for key, value in data.items(): - # handle multiple entries for the same name - if type(value) not in (type([]), type( () )): - value = [value] - for value in value: - value = str(value) - body.write(sep_boundary) - body.write('\nContent-Disposition: form-data; name="%s"'%key) - body.write("\n\n") - body.write(value) - if value and value[-1] == '\r': - body.write('\n') # write an extra newline (lurve Macs) - body.write(end_boundary) - body.write("\n") - body = body.getvalue().encode("utf-8") - - # build the Request - headers = { - 'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary, - 'Content-length': str(len(body)) - } - req = urllib.request.Request(self.repository, body, headers) - - # handle HTTP and include the Basic Auth handler - opener = urllib.request.build_opener( - urllib.request.HTTPBasicAuthHandler(password_mgr=auth) - ) - data = '' - try: - result = opener.open(req) - except urllib.error.HTTPError as e: - if self.show_response: - data = e.fp.read() - result = e.code, e.msg - except urllib.error.URLError as e: - result = 500, str(e) - else: - if self.show_response: - data = self._read_pypi_response(result) - result = 200, 'OK' - if self.show_response: - msg = '\n'.join(('-' * 75, data, '-' * 75)) - self.announce(msg, log.INFO) - return result diff --git a/distutils/command/sdist.py b/distutils/command/sdist.py deleted file mode 100644 index b4996fcb..00000000 --- a/distutils/command/sdist.py +++ /dev/null @@ -1,494 +0,0 @@ -"""distutils.command.sdist - -Implements the Distutils 'sdist' command (create a source distribution).""" - -import os -import sys -from glob import glob -from warnings import warn - -from distutils.core import Command -from distutils import dir_util -from distutils import file_util -from distutils import archive_util -from distutils.text_file import TextFile -from distutils.filelist import FileList -from distutils import log -from distutils.util import convert_path -from distutils.errors import DistutilsTemplateError, DistutilsOptionError - - -def show_formats(): - """Print all possible values for the 'formats' option (used by - the "--help-formats" command-line option). - """ - from distutils.fancy_getopt import FancyGetopt - from distutils.archive_util import ARCHIVE_FORMATS - formats = [] - for format in ARCHIVE_FORMATS.keys(): - formats.append(("formats=" + format, None, - ARCHIVE_FORMATS[format][2])) - formats.sort() - FancyGetopt(formats).print_help( - "List of available source distribution formats:") - - -class sdist(Command): - - description = "create a source distribution (tarball, zip file, etc.)" - - def checking_metadata(self): - """Callable used for the check sub-command. - - Placed here so user_options can view it""" - return self.metadata_check - - user_options = [ - ('template=', 't', - "name of manifest template file [default: MANIFEST.in]"), - ('manifest=', 'm', - "name of manifest file [default: MANIFEST]"), - ('use-defaults', None, - "include the default file set in the manifest " - "[default; disable with --no-defaults]"), - ('no-defaults', None, - "don't include the default file set"), - ('prune', None, - "specifically exclude files/directories that should not be " - "distributed (build tree, RCS/CVS dirs, etc.) " - "[default; disable with --no-prune]"), - ('no-prune', None, - "don't automatically exclude anything"), - ('manifest-only', 'o', - "just regenerate the manifest and then stop " - "(implies --force-manifest)"), - ('force-manifest', 'f', - "forcibly regenerate the manifest and carry on as usual. " - "Deprecated: now the manifest is always regenerated."), - ('formats=', None, - "formats for source distribution (comma-separated list)"), - ('keep-temp', 'k', - "keep the distribution tree around after creating " + - "archive file(s)"), - ('dist-dir=', 'd', - "directory to put the source distribution archive(s) in " - "[default: dist]"), - ('metadata-check', None, - "Ensure that all required elements of meta-data " - "are supplied. Warn if any missing. [default]"), - ('owner=', 'u', - "Owner name used when creating a tar file [default: current user]"), - ('group=', 'g', - "Group name used when creating a tar file [default: current group]"), - ] - - boolean_options = ['use-defaults', 'prune', - 'manifest-only', 'force-manifest', - 'keep-temp', 'metadata-check'] - - help_options = [ - ('help-formats', None, - "list available distribution formats", show_formats), - ] - - negative_opt = {'no-defaults': 'use-defaults', - 'no-prune': 'prune' } - - sub_commands = [('check', checking_metadata)] - - READMES = ('README', 'README.txt', 'README.rst') - - def initialize_options(self): - # 'template' and 'manifest' are, respectively, the names of - # the manifest template and manifest file. - self.template = None - self.manifest = None - - # 'use_defaults': if true, we will include the default file set - # in the manifest - self.use_defaults = 1 - self.prune = 1 - - self.manifest_only = 0 - self.force_manifest = 0 - - self.formats = ['gztar'] - self.keep_temp = 0 - self.dist_dir = None - - self.archive_files = None - self.metadata_check = 1 - self.owner = None - self.group = None - - def finalize_options(self): - if self.manifest is None: - self.manifest = "MANIFEST" - if self.template is None: - self.template = "MANIFEST.in" - - self.ensure_string_list('formats') - - bad_format = archive_util.check_archive_formats(self.formats) - if bad_format: - raise DistutilsOptionError( - "unknown archive format '%s'" % bad_format) - - if self.dist_dir is None: - self.dist_dir = "dist" - - def run(self): - # 'filelist' contains the list of files that will make up the - # manifest - self.filelist = FileList() - - # Run sub commands - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) - - # Do whatever it takes to get the list of files to process - # (process the manifest template, read an existing manifest, - # whatever). File list is accumulated in 'self.filelist'. - self.get_file_list() - - # If user just wanted us to regenerate the manifest, stop now. - if self.manifest_only: - return - - # Otherwise, go ahead and create the source distribution tarball, - # or zipfile, or whatever. - self.make_distribution() - - def check_metadata(self): - """Deprecated API.""" - warn("distutils.command.sdist.check_metadata is deprecated, \ - use the check command instead", PendingDeprecationWarning) - check = self.distribution.get_command_obj('check') - check.ensure_finalized() - check.run() - - def get_file_list(self): - """Figure out the list of files to include in the source - distribution, and put it in 'self.filelist'. This might involve - reading the manifest template (and writing the manifest), or just - reading the manifest, or just using the default file set -- it all - depends on the user's options. - """ - # new behavior when using a template: - # the file list is recalculated every time because - # even if MANIFEST.in or setup.py are not changed - # the user might have added some files in the tree that - # need to be included. - # - # This makes --force the default and only behavior with templates. - template_exists = os.path.isfile(self.template) - if not template_exists and self._manifest_is_not_generated(): - self.read_manifest() - self.filelist.sort() - self.filelist.remove_duplicates() - return - - if not template_exists: - self.warn(("manifest template '%s' does not exist " + - "(using default file list)") % - self.template) - self.filelist.findall() - - if self.use_defaults: - self.add_defaults() - - if template_exists: - self.read_template() - - if self.prune: - self.prune_file_list() - - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() - - def add_defaults(self): - """Add all the default files to self.filelist: - - README or README.txt - - setup.py - - test/test*.py - - all pure Python modules mentioned in setup script - - all files pointed by package_data (build_py) - - all files defined in data_files. - - all files defined as scripts. - - all C sources listed as part of extensions or C libraries - in the setup script (doesn't catch C headers!) - Warns if (README or README.txt) or setup.py are missing; everything - else is optional. - """ - self._add_defaults_standards() - self._add_defaults_optional() - self._add_defaults_python() - self._add_defaults_data_files() - self._add_defaults_ext() - self._add_defaults_c_libs() - self._add_defaults_scripts() - - @staticmethod - def _cs_path_exists(fspath): - """ - Case-sensitive path existence check - - >>> sdist._cs_path_exists(__file__) - True - >>> sdist._cs_path_exists(__file__.upper()) - False - """ - if not os.path.exists(fspath): - return False - # make absolute so we always have a directory - abspath = os.path.abspath(fspath) - directory, filename = os.path.split(abspath) - return filename in os.listdir(directory) - - def _add_defaults_standards(self): - standards = [self.READMES, self.distribution.script_name] - for fn in standards: - if isinstance(fn, tuple): - alts = fn - got_it = False - for fn in alts: - if self._cs_path_exists(fn): - got_it = True - self.filelist.append(fn) - break - - if not got_it: - self.warn("standard file not found: should have one of " + - ', '.join(alts)) - else: - if self._cs_path_exists(fn): - self.filelist.append(fn) - else: - self.warn("standard file '%s' not found" % fn) - - def _add_defaults_optional(self): - optional = ['test/test*.py', 'setup.cfg'] - for pattern in optional: - files = filter(os.path.isfile, glob(pattern)) - self.filelist.extend(files) - - def _add_defaults_python(self): - # build_py is used to get: - # - python modules - # - files defined in package_data - build_py = self.get_finalized_command('build_py') - - # getting python files - if self.distribution.has_pure_modules(): - self.filelist.extend(build_py.get_source_files()) - - # getting package_data files - # (computed in build_py.data_files by build_py.finalize_options) - for pkg, src_dir, build_dir, filenames in build_py.data_files: - for filename in filenames: - self.filelist.append(os.path.join(src_dir, filename)) - - def _add_defaults_data_files(self): - # getting distribution.data_files - if self.distribution.has_data_files(): - for item in self.distribution.data_files: - if isinstance(item, str): - # plain file - item = convert_path(item) - if os.path.isfile(item): - self.filelist.append(item) - else: - # a (dirname, filenames) tuple - dirname, filenames = item - for f in filenames: - f = convert_path(f) - if os.path.isfile(f): - self.filelist.append(f) - - def _add_defaults_ext(self): - if self.distribution.has_ext_modules(): - build_ext = self.get_finalized_command('build_ext') - self.filelist.extend(build_ext.get_source_files()) - - def _add_defaults_c_libs(self): - if self.distribution.has_c_libraries(): - build_clib = self.get_finalized_command('build_clib') - self.filelist.extend(build_clib.get_source_files()) - - def _add_defaults_scripts(self): - if self.distribution.has_scripts(): - build_scripts = self.get_finalized_command('build_scripts') - self.filelist.extend(build_scripts.get_source_files()) - - def read_template(self): - """Read and parse manifest template file named by self.template. - - (usually "MANIFEST.in") The parsing and processing is done by - 'self.filelist', which updates itself accordingly. - """ - log.info("reading manifest template '%s'", self.template) - template = TextFile(self.template, strip_comments=1, skip_blanks=1, - join_lines=1, lstrip_ws=1, rstrip_ws=1, - collapse_join=1) - - try: - while True: - line = template.readline() - if line is None: # end of file - break - - try: - self.filelist.process_template_line(line) - # the call above can raise a DistutilsTemplateError for - # malformed lines, or a ValueError from the lower-level - # convert_path function - except (DistutilsTemplateError, ValueError) as msg: - self.warn("%s, line %d: %s" % (template.filename, - template.current_line, - msg)) - finally: - template.close() - - def prune_file_list(self): - """Prune off branches that might slip into the file list as created - by 'read_template()', but really don't belong there: - * the build tree (typically "build") - * the release tree itself (only an issue if we ran "sdist" - previously with --keep-temp, or it aborted) - * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories - """ - build = self.get_finalized_command('build') - base_dir = self.distribution.get_fullname() - - self.filelist.exclude_pattern(None, prefix=build.build_base) - self.filelist.exclude_pattern(None, prefix=base_dir) - - if sys.platform == 'win32': - seps = r'/|\\' - else: - seps = '/' - - vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', - '_darcs'] - vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) - self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) - - def write_manifest(self): - """Write the file list in 'self.filelist' (presumably as filled in - by 'add_defaults()' and 'read_template()') to the manifest file - named by 'self.manifest'. - """ - if self._manifest_is_not_generated(): - log.info("not writing to manually maintained " - "manifest file '%s'" % self.manifest) - return - - content = self.filelist.files[:] - content.insert(0, '# file GENERATED by distutils, do NOT edit') - self.execute(file_util.write_file, (self.manifest, content), - "writing manifest file '%s'" % self.manifest) - - def _manifest_is_not_generated(self): - # check for special comment used in 3.1.3 and higher - if not os.path.isfile(self.manifest): - return False - - fp = open(self.manifest) - try: - first_line = fp.readline() - finally: - fp.close() - return first_line != '# file GENERATED by distutils, do NOT edit\n' - - def read_manifest(self): - """Read the manifest file (named by 'self.manifest') and use it to - fill in 'self.filelist', the list of files to include in the source - distribution. - """ - log.info("reading manifest file '%s'", self.manifest) - with open(self.manifest) as manifest: - for line in manifest: - # ignore comments and blank lines - line = line.strip() - if line.startswith('#') or not line: - continue - self.filelist.append(line) - - def make_release_tree(self, base_dir, files): - """Create the directory tree that will become the source - distribution archive. All directories implied by the filenames in - 'files' are created under 'base_dir', and then we hard link or copy - (if hard linking is unavailable) those files into place. - Essentially, this duplicates the developer's source tree, but in a - directory named after the distribution, containing only the files - to be distributed. - """ - # Create all the directories under 'base_dir' necessary to - # put 'files' there; the 'mkpath()' is just so we don't die - # if the manifest happens to be empty. - self.mkpath(base_dir) - dir_util.create_tree(base_dir, files, dry_run=self.dry_run) - - # And walk over the list of files, either making a hard link (if - # os.link exists) to each one that doesn't already exist in its - # corresponding location under 'base_dir', or copying each file - # that's out-of-date in 'base_dir'. (Usually, all files will be - # out-of-date, because by default we blow away 'base_dir' when - # we're done making the distribution archives.) - - if hasattr(os, 'link'): # can make hard links on this system - link = 'hard' - msg = "making hard links in %s..." % base_dir - else: # nope, have to copy - link = None - msg = "copying files to %s..." % base_dir - - if not files: - log.warn("no files to distribute -- empty manifest?") - else: - log.info(msg) - for file in files: - if not os.path.isfile(file): - log.warn("'%s' not a regular file -- skipping", file) - else: - dest = os.path.join(base_dir, file) - self.copy_file(file, dest, link=link) - - self.distribution.metadata.write_pkg_info(base_dir) - - def make_distribution(self): - """Create the source distribution(s). First, we create the release - tree with 'make_release_tree()'; then, we create all required - archive files (according to 'self.formats') from the release tree. - Finally, we clean up by blowing away the release tree (unless - 'self.keep_temp' is true). The list of archive files created is - stored so it can be retrieved later by 'get_archive_files()'. - """ - # Don't warn about missing meta-data here -- should be (and is!) - # done elsewhere. - base_dir = self.distribution.get_fullname() - base_name = os.path.join(self.dist_dir, base_dir) - - self.make_release_tree(base_dir, self.filelist.files) - archive_files = [] # remember names of files we create - # tar archive must be created last to avoid overwrite and remove - if 'tar' in self.formats: - self.formats.append(self.formats.pop(self.formats.index('tar'))) - - for fmt in self.formats: - file = self.make_archive(base_name, fmt, base_dir=base_dir, - owner=self.owner, group=self.group) - archive_files.append(file) - self.distribution.dist_files.append(('sdist', '', file)) - - self.archive_files = archive_files - - if not self.keep_temp: - dir_util.remove_tree(base_dir, dry_run=self.dry_run) - - def get_archive_files(self): - """Return the list of archive files created when the command - was run, or None if the command hasn't run yet. - """ - return self.archive_files diff --git a/distutils/command/upload.py b/distutils/command/upload.py deleted file mode 100644 index 95e9fda1..00000000 --- a/distutils/command/upload.py +++ /dev/null @@ -1,214 +0,0 @@ -""" -distutils.command.upload - -Implements the Distutils 'upload' subcommand (upload package to a package -index). -""" - -import os -import io -import hashlib -from base64 import standard_b64encode -from urllib.request import urlopen, Request, HTTPError -from urllib.parse import urlparse -from distutils.errors import DistutilsError, DistutilsOptionError -from distutils.core import PyPIRCCommand -from distutils.spawn import spawn -from distutils import log - - -# PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256) -# https://bugs.python.org/issue40698 -_FILE_CONTENT_DIGESTS = { - "md5_digest": getattr(hashlib, "md5", None), - "sha256_digest": getattr(hashlib, "sha256", None), - "blake2_256_digest": getattr(hashlib, "blake2b", None), -} - - -class upload(PyPIRCCommand): - - description = "upload binary package to PyPI" - - user_options = PyPIRCCommand.user_options + [ - ('sign', 's', - 'sign files to upload using gpg'), - ('identity=', 'i', 'GPG identity used to sign files'), - ] - - boolean_options = PyPIRCCommand.boolean_options + ['sign'] - - def initialize_options(self): - PyPIRCCommand.initialize_options(self) - self.username = '' - self.password = '' - self.show_response = 0 - self.sign = False - self.identity = None - - def finalize_options(self): - PyPIRCCommand.finalize_options(self) - if self.identity and not self.sign: - raise DistutilsOptionError( - "Must use --sign for --identity to have meaning" - ) - config = self._read_pypirc() - if config != {}: - self.username = config['username'] - self.password = config['password'] - self.repository = config['repository'] - self.realm = config['realm'] - - # getting the password from the distribution - # if previously set by the register command - if not self.password and self.distribution.password: - self.password = self.distribution.password - - def run(self): - if not self.distribution.dist_files: - msg = ("Must create and upload files in one command " - "(e.g. setup.py sdist upload)") - raise DistutilsOptionError(msg) - for command, pyversion, filename in self.distribution.dist_files: - self.upload_file(command, pyversion, filename) - - def upload_file(self, command, pyversion, filename): - # Makes sure the repository URL is compliant - schema, netloc, url, params, query, fragments = \ - urlparse(self.repository) - if params or query or fragments: - raise AssertionError("Incompatible url %s" % self.repository) - - if schema not in ('http', 'https'): - raise AssertionError("unsupported schema " + schema) - - # Sign if requested - if self.sign: - gpg_args = ["gpg", "--detach-sign", "-a", filename] - if self.identity: - gpg_args[2:2] = ["--local-user", self.identity] - spawn(gpg_args, - dry_run=self.dry_run) - - # Fill in the data - send all the meta-data in case we need to - # register a new release - f = open(filename,'rb') - try: - content = f.read() - finally: - f.close() - - meta = self.distribution.metadata - data = { - # action - ':action': 'file_upload', - 'protocol_version': '1', - - # identify release - 'name': meta.get_name(), - 'version': meta.get_version(), - - # file content - 'content': (os.path.basename(filename),content), - 'filetype': command, - 'pyversion': pyversion, - - # additional meta-data - 'metadata_version': '1.0', - 'summary': meta.get_description(), - 'home_page': meta.get_url(), - 'author': meta.get_contact(), - 'author_email': meta.get_contact_email(), - 'license': meta.get_licence(), - 'description': meta.get_long_description(), - 'keywords': meta.get_keywords(), - 'platform': meta.get_platforms(), - 'classifiers': meta.get_classifiers(), - 'download_url': meta.get_download_url(), - # PEP 314 - 'provides': meta.get_provides(), - 'requires': meta.get_requires(), - 'obsoletes': meta.get_obsoletes(), - } - - data['comment'] = '' - - # file content digests - for digest_name, digest_cons in _FILE_CONTENT_DIGESTS.items(): - if digest_cons is None: - continue - try: - data[digest_name] = digest_cons(content).hexdigest() - except ValueError: - # hash digest not available or blocked by security policy - pass - - if self.sign: - with open(filename + ".asc", "rb") as f: - data['gpg_signature'] = (os.path.basename(filename) + ".asc", - f.read()) - - # set up the authentication - user_pass = (self.username + ":" + self.password).encode('ascii') - # The exact encoding of the authentication string is debated. - # Anyway PyPI only accepts ascii for both username or password. - auth = "Basic " + standard_b64encode(user_pass).decode('ascii') - - # Build up the MIME payload for the POST data - boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' - sep_boundary = b'\r\n--' + boundary.encode('ascii') - end_boundary = sep_boundary + b'--\r\n' - body = io.BytesIO() - for key, value in data.items(): - title = '\r\nContent-Disposition: form-data; name="%s"' % key - # handle multiple entries for the same name - if not isinstance(value, list): - value = [value] - for value in value: - if type(value) is tuple: - title += '; filename="%s"' % value[0] - value = value[1] - else: - value = str(value).encode('utf-8') - body.write(sep_boundary) - body.write(title.encode('utf-8')) - body.write(b"\r\n\r\n") - body.write(value) - body.write(end_boundary) - body = body.getvalue() - - msg = "Submitting %s to %s" % (filename, self.repository) - self.announce(msg, log.INFO) - - # build the Request - headers = { - 'Content-type': 'multipart/form-data; boundary=%s' % boundary, - 'Content-length': str(len(body)), - 'Authorization': auth, - } - - request = Request(self.repository, data=body, - headers=headers) - # send the data - try: - result = urlopen(request) - status = result.getcode() - reason = result.msg - except HTTPError as e: - status = e.code - reason = e.msg - except OSError as e: - self.announce(str(e), log.ERROR) - raise - - if status == 200: - self.announce('Server response (%s): %s' % (status, reason), - log.INFO) - if self.show_response: - text = self._read_pypi_response(result) - msg = '\n'.join(('-' * 75, text, '-' * 75)) - self.announce(msg, log.INFO) - else: - msg = 'Upload failed (%s): %s' % (status, reason) - self.announce(msg, log.ERROR) - raise DistutilsError(msg) diff --git a/distutils/command/wininst-10.0-amd64.exe b/distutils/command/wininst-10.0-amd64.exe deleted file mode 100644 index 6fa0dce1..00000000 Binary files a/distutils/command/wininst-10.0-amd64.exe and /dev/null differ diff --git a/distutils/command/wininst-10.0.exe b/distutils/command/wininst-10.0.exe deleted file mode 100644 index afc3bc6c..00000000 Binary files a/distutils/command/wininst-10.0.exe and /dev/null differ diff --git a/distutils/command/wininst-14.0-amd64.exe b/distutils/command/wininst-14.0-amd64.exe deleted file mode 100644 index 253c2e2e..00000000 Binary files a/distutils/command/wininst-14.0-amd64.exe and /dev/null differ diff --git a/distutils/command/wininst-14.0.exe b/distutils/command/wininst-14.0.exe deleted file mode 100644 index 46f5f356..00000000 Binary files a/distutils/command/wininst-14.0.exe and /dev/null differ diff --git a/distutils/command/wininst-6.0.exe b/distutils/command/wininst-6.0.exe deleted file mode 100644 index f57c855a..00000000 Binary files a/distutils/command/wininst-6.0.exe and /dev/null differ diff --git a/distutils/command/wininst-7.1.exe b/distutils/command/wininst-7.1.exe deleted file mode 100644 index 1433bc1a..00000000 Binary files a/distutils/command/wininst-7.1.exe and /dev/null differ diff --git a/distutils/command/wininst-8.0.exe b/distutils/command/wininst-8.0.exe deleted file mode 100644 index 7403bfab..00000000 Binary files a/distutils/command/wininst-8.0.exe and /dev/null differ diff --git a/distutils/command/wininst-9.0-amd64.exe b/distutils/command/wininst-9.0-amd64.exe deleted file mode 100644 index 94fbd434..00000000 Binary files a/distutils/command/wininst-9.0-amd64.exe and /dev/null differ diff --git a/distutils/command/wininst-9.0.exe b/distutils/command/wininst-9.0.exe deleted file mode 100644 index 2ec261f9..00000000 Binary files a/distutils/command/wininst-9.0.exe and /dev/null differ diff --git a/distutils/config.py b/distutils/config.py deleted file mode 100644 index 2171abd6..00000000 --- a/distutils/config.py +++ /dev/null @@ -1,130 +0,0 @@ -"""distutils.pypirc - -Provides the PyPIRCCommand class, the base class for the command classes -that uses .pypirc in the distutils.command package. -""" -import os -from configparser import RawConfigParser - -from distutils.cmd import Command - -DEFAULT_PYPIRC = """\ -[distutils] -index-servers = - pypi - -[pypi] -username:%s -password:%s -""" - -class PyPIRCCommand(Command): - """Base command that knows how to handle the .pypirc file - """ - DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/' - DEFAULT_REALM = 'pypi' - repository = None - realm = None - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]" % \ - DEFAULT_REPOSITORY), - ('show-response', None, - 'display full response text from server')] - - boolean_options = ['show-response'] - - def _get_rc_file(self): - """Returns rc file path.""" - return os.path.join(os.path.expanduser('~'), '.pypirc') - - def _store_pypirc(self, username, password): - """Creates a default .pypirc file.""" - rc = self._get_rc_file() - with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f: - f.write(DEFAULT_PYPIRC % (username, password)) - - def _read_pypirc(self): - """Reads the .pypirc file.""" - rc = self._get_rc_file() - if os.path.exists(rc): - self.announce('Using PyPI login from %s' % rc) - repository = self.repository or self.DEFAULT_REPOSITORY - - config = RawConfigParser() - config.read(rc) - sections = config.sections() - if 'distutils' in sections: - # let's get the list of servers - index_servers = config.get('distutils', 'index-servers') - _servers = [server.strip() for server in - index_servers.split('\n') - if server.strip() != ''] - if _servers == []: - # nothing set, let's try to get the default pypi - if 'pypi' in sections: - _servers = ['pypi'] - else: - # the file is not properly defined, returning - # an empty dict - return {} - for server in _servers: - current = {'server': server} - current['username'] = config.get(server, 'username') - - # optional params - for key, default in (('repository', - self.DEFAULT_REPOSITORY), - ('realm', self.DEFAULT_REALM), - ('password', None)): - if config.has_option(server, key): - current[key] = config.get(server, key) - else: - current[key] = default - - # work around people having "repository" for the "pypi" - # section of their config set to the HTTP (rather than - # HTTPS) URL - if (server == 'pypi' and - repository in (self.DEFAULT_REPOSITORY, 'pypi')): - current['repository'] = self.DEFAULT_REPOSITORY - return current - - if (current['server'] == repository or - current['repository'] == repository): - return current - elif 'server-login' in sections: - # old format - server = 'server-login' - if config.has_option(server, 'repository'): - repository = config.get(server, 'repository') - else: - repository = self.DEFAULT_REPOSITORY - return {'username': config.get(server, 'username'), - 'password': config.get(server, 'password'), - 'repository': repository, - 'server': server, - 'realm': self.DEFAULT_REALM} - - return {} - - def _read_pypi_response(self, response): - """Read and decode a PyPI HTTP response.""" - import cgi - content_type = response.getheader('content-type', 'text/plain') - encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii') - return response.read().decode(encoding) - - def initialize_options(self): - """Initialize options.""" - self.repository = None - self.realm = None - self.show_response = 0 - - def finalize_options(self): - """Finalizes options.""" - if self.repository is None: - self.repository = self.DEFAULT_REPOSITORY - if self.realm is None: - self.realm = self.DEFAULT_REALM diff --git a/distutils/core.py b/distutils/core.py deleted file mode 100644 index d603d4a4..00000000 --- a/distutils/core.py +++ /dev/null @@ -1,234 +0,0 @@ -"""distutils.core - -The only module that needs to be imported to use the Distutils; provides -the 'setup' function (which is to be called from the setup script). Also -indirectly provides the Distribution and Command classes, although they are -really defined in distutils.dist and distutils.cmd. -""" - -import os -import sys - -from distutils.debug import DEBUG -from distutils.errors import * - -# Mainly import these so setup scripts can "from distutils.core import" them. -from distutils.dist import Distribution -from distutils.cmd import Command -from distutils.config import PyPIRCCommand -from distutils.extension import Extension - -# This is a barebones help message generated displayed when the user -# runs the setup script with no arguments at all. More useful help -# is generated with various --help options: global help, list commands, -# and per-command help. -USAGE = """\ -usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] - or: %(script)s --help [cmd1 cmd2 ...] - or: %(script)s --help-commands - or: %(script)s cmd --help -""" - -def gen_usage (script_name): - script = os.path.basename(script_name) - return USAGE % vars() - - -# Some mild magic to control the behaviour of 'setup()' from 'run_setup()'. -_setup_stop_after = None -_setup_distribution = None - -# Legal keyword arguments for the setup() function -setup_keywords = ('distclass', 'script_name', 'script_args', 'options', - 'name', 'version', 'author', 'author_email', - 'maintainer', 'maintainer_email', 'url', 'license', - 'description', 'long_description', 'keywords', - 'platforms', 'classifiers', 'download_url', - 'requires', 'provides', 'obsoletes', - ) - -# Legal keyword arguments for the Extension constructor -extension_keywords = ('name', 'sources', 'include_dirs', - 'define_macros', 'undef_macros', - 'library_dirs', 'libraries', 'runtime_library_dirs', - 'extra_objects', 'extra_compile_args', 'extra_link_args', - 'swig_opts', 'export_symbols', 'depends', 'language') - -def setup (**attrs): - """The gateway to the Distutils: do everything your setup script needs - to do, in a highly flexible and user-driven way. Briefly: create a - Distribution instance; find and parse config files; parse the command - line; run each Distutils command found there, customized by the options - supplied to 'setup()' (as keyword arguments), in config files, and on - the command line. - - The Distribution instance might be an instance of a class supplied via - the 'distclass' keyword argument to 'setup'; if no such class is - supplied, then the Distribution class (in dist.py) is instantiated. - All other arguments to 'setup' (except for 'cmdclass') are used to set - attributes of the Distribution instance. - - The 'cmdclass' argument, if supplied, is a dictionary mapping command - names to command classes. Each command encountered on the command line - will be turned into a command class, which is in turn instantiated; any - class found in 'cmdclass' is used in place of the default, which is - (for command 'foo_bar') class 'foo_bar' in module - 'distutils.command.foo_bar'. The command class must provide a - 'user_options' attribute which is a list of option specifiers for - 'distutils.fancy_getopt'. Any command-line options between the current - and the next command are used to set attributes of the current command - object. - - When the entire command-line has been successfully parsed, calls the - 'run()' method on each command object in turn. This method will be - driven entirely by the Distribution object (which each command object - has a reference to, thanks to its constructor), and the - command-specific options that became attributes of each command - object. - """ - - global _setup_stop_after, _setup_distribution - - # Determine the distribution class -- either caller-supplied or - # our Distribution (see below). - klass = attrs.get('distclass') - if klass: - del attrs['distclass'] - else: - klass = Distribution - - if 'script_name' not in attrs: - attrs['script_name'] = os.path.basename(sys.argv[0]) - if 'script_args' not in attrs: - attrs['script_args'] = sys.argv[1:] - - # Create the Distribution instance, using the remaining arguments - # (ie. everything except distclass) to initialize it - try: - _setup_distribution = dist = klass(attrs) - except DistutilsSetupError as msg: - if 'name' not in attrs: - raise SystemExit("error in setup command: %s" % msg) - else: - raise SystemExit("error in %s setup command: %s" % \ - (attrs['name'], msg)) - - if _setup_stop_after == "init": - return dist - - # Find and parse the config file(s): they will override options from - # the setup script, but be overridden by the command line. - dist.parse_config_files() - - if DEBUG: - print("options (after parsing config files):") - dist.dump_option_dicts() - - if _setup_stop_after == "config": - return dist - - # Parse the command line and override config files; any - # command-line errors are the end user's fault, so turn them into - # SystemExit to suppress tracebacks. - try: - ok = dist.parse_command_line() - except DistutilsArgError as msg: - raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg) - - if DEBUG: - print("options (after parsing command line):") - dist.dump_option_dicts() - - if _setup_stop_after == "commandline": - return dist - - # And finally, run all the commands found on the command line. - if ok: - try: - dist.run_commands() - except KeyboardInterrupt: - raise SystemExit("interrupted") - except OSError as exc: - if DEBUG: - sys.stderr.write("error: %s\n" % (exc,)) - raise - else: - raise SystemExit("error: %s" % (exc,)) - - except (DistutilsError, - CCompilerError) as msg: - if DEBUG: - raise - else: - raise SystemExit("error: " + str(msg)) - - return dist - -# setup () - - -def run_setup (script_name, script_args=None, stop_after="run"): - """Run a setup script in a somewhat controlled environment, and - return the Distribution instance that drives things. This is useful - if you need to find out the distribution meta-data (passed as - keyword args from 'script' to 'setup()', or the contents of the - config files or command-line. - - 'script_name' is a file that will be read and run with 'exec()'; - 'sys.argv[0]' will be replaced with 'script' for the duration of the - call. 'script_args' is a list of strings; if supplied, - 'sys.argv[1:]' will be replaced by 'script_args' for the duration of - the call. - - 'stop_after' tells 'setup()' when to stop processing; possible - values: - init - stop after the Distribution instance has been created and - populated with the keyword arguments to 'setup()' - config - stop after config files have been parsed (and their data - stored in the Distribution instance) - commandline - stop after the command-line ('sys.argv[1:]' or 'script_args') - have been parsed (and the data stored in the Distribution) - run [default] - stop after all commands have been run (the same as if 'setup()' - had been called in the usual way - - Returns the Distribution instance, which provides all information - used to drive the Distutils. - """ - if stop_after not in ('init', 'config', 'commandline', 'run'): - raise ValueError("invalid value for 'stop_after': %r" % (stop_after,)) - - global _setup_stop_after, _setup_distribution - _setup_stop_after = stop_after - - save_argv = sys.argv.copy() - g = {'__file__': script_name} - try: - try: - sys.argv[0] = script_name - if script_args is not None: - sys.argv[1:] = script_args - with open(script_name, 'rb') as f: - exec(f.read(), g) - finally: - sys.argv = save_argv - _setup_stop_after = None - except SystemExit: - # Hmm, should we do something if exiting with a non-zero code - # (ie. error)? - pass - - if _setup_distribution is None: - raise RuntimeError(("'distutils.core.setup()' was never called -- " - "perhaps '%s' is not a Distutils setup script?") % \ - script_name) - - # I wonder if the setup script's namespace -- g and l -- would be of - # any interest to callers? - #print "_setup_distribution:", _setup_distribution - return _setup_distribution - -# run_setup () diff --git a/distutils/cygwinccompiler.py b/distutils/cygwinccompiler.py deleted file mode 100644 index 66c12dd3..00000000 --- a/distutils/cygwinccompiler.py +++ /dev/null @@ -1,403 +0,0 @@ -"""distutils.cygwinccompiler - -Provides the CygwinCCompiler class, a subclass of UnixCCompiler that -handles the Cygwin port of the GNU C compiler to Windows. It also contains -the Mingw32CCompiler class which handles the mingw32 port of GCC (same as -cygwin in no-cygwin mode). -""" - -# problems: -# -# * if you use a msvc compiled python version (1.5.2) -# 1. you have to insert a __GNUC__ section in its config.h -# 2. you have to generate an import library for its dll -# - create a def-file for python??.dll -# - create an import library using -# dlltool --dllname python15.dll --def python15.def \ -# --output-lib libpython15.a -# -# see also http://starship.python.net/crew/kernr/mingw32/Notes.html -# -# * We put export_symbols in a def-file, and don't use -# --export-all-symbols because it doesn't worked reliable in some -# tested configurations. And because other windows compilers also -# need their symbols specified this no serious problem. -# -# tested configurations: -# -# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works -# (after patching python's config.h and for C++ some other include files) -# see also http://starship.python.net/crew/kernr/mingw32/Notes.html -# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works -# (ld doesn't support -shared, so we use dllwrap) -# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now -# - its dllwrap doesn't work, there is a bug in binutils 2.10.90 -# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html -# - using gcc -mdll instead dllwrap doesn't work without -static because -# it tries to link against dlls instead their import libraries. (If -# it finds the dll first.) -# By specifying -static we force ld to link against the import libraries, -# this is windows standard and there are normally not the necessary symbols -# in the dlls. -# *** only the version of June 2000 shows these problems -# * cygwin gcc 3.2/ld 2.13.90 works -# (ld supports -shared) -# * mingw gcc 3.2/ld 2.13 works -# (ld supports -shared) - -import os -import sys -import copy -from subprocess import Popen, PIPE, check_output -import re - -from distutils.unixccompiler import UnixCCompiler -from distutils.file_util import write_file -from distutils.errors import (DistutilsExecError, CCompilerError, - CompileError, UnknownFileError) -from distutils.version import LooseVersion -from distutils.spawn import find_executable - -def get_msvcr(): - """Include the appropriate MSVC runtime library if Python was built - with MSVC 7.0 or later. - """ - msc_pos = sys.version.find('MSC v.') - if msc_pos != -1: - msc_ver = sys.version[msc_pos+6:msc_pos+10] - if msc_ver == '1300': - # MSVC 7.0 - return ['msvcr70'] - elif msc_ver == '1310': - # MSVC 7.1 - return ['msvcr71'] - elif msc_ver == '1400': - # VS2005 / MSVC 8.0 - return ['msvcr80'] - elif msc_ver == '1500': - # VS2008 / MSVC 9.0 - return ['msvcr90'] - elif msc_ver == '1600': - # VS2010 / MSVC 10.0 - return ['msvcr100'] - else: - raise ValueError("Unknown MS Compiler version %s " % msc_ver) - - -class CygwinCCompiler(UnixCCompiler): - """ Handles the Cygwin port of the GNU C compiler to Windows. - """ - compiler_type = 'cygwin' - obj_extension = ".o" - static_lib_extension = ".a" - shared_lib_extension = ".dll" - static_lib_format = "lib%s%s" - shared_lib_format = "%s%s" - exe_extension = ".exe" - - def __init__(self, verbose=0, dry_run=0, force=0): - - UnixCCompiler.__init__(self, verbose, dry_run, force) - - status, details = check_config_h() - self.debug_print("Python's GCC status: %s (details: %s)" % - (status, details)) - if status is not CONFIG_H_OK: - self.warn( - "Python's pyconfig.h doesn't seem to support your compiler. " - "Reason: %s. " - "Compiling may fail because of undefined preprocessor macros." - % details) - - self.gcc_version, self.ld_version, self.dllwrap_version = \ - get_versions() - self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % - (self.gcc_version, - self.ld_version, - self.dllwrap_version) ) - - # ld_version >= "2.10.90" and < "2.13" should also be able to use - # gcc -mdll instead of dllwrap - # Older dllwraps had own version numbers, newer ones use the - # same as the rest of binutils ( also ld ) - # dllwrap 2.10.90 is buggy - if self.ld_version >= "2.10.90": - self.linker_dll = "gcc" - else: - self.linker_dll = "dllwrap" - - # ld_version >= "2.13" support -shared so use it instead of - # -mdll -static - if self.ld_version >= "2.13": - shared_option = "-shared" - else: - shared_option = "-mdll -static" - - # Hard-code GCC because that's what this is all about. - # XXX optimization, warnings etc. should be customizable. - self.set_executables(compiler='gcc -mcygwin -O -Wall', - compiler_so='gcc -mcygwin -mdll -O -Wall', - compiler_cxx='g++ -mcygwin -O -Wall', - linker_exe='gcc -mcygwin', - linker_so=('%s -mcygwin %s' % - (self.linker_dll, shared_option))) - - # cygwin and mingw32 need different sets of libraries - if self.gcc_version == "2.91.57": - # cygwin shouldn't need msvcrt, but without the dlls will crash - # (gcc version 2.91.57) -- perhaps something about initialization - self.dll_libraries=["msvcrt"] - self.warn( - "Consider upgrading to a newer version of gcc") - else: - # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or later. - self.dll_libraries = get_msvcr() - - def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): - """Compiles the source by spawning GCC and windres if needed.""" - if ext == '.rc' or ext == '.res': - # gcc needs '.res' and '.rc' compiled to object files !!! - try: - self.spawn(["windres", "-i", src, "-o", obj]) - except DistutilsExecError as msg: - raise CompileError(msg) - else: # for other files use the C-compiler - try: - self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + - extra_postargs) - except DistutilsExecError as msg: - raise CompileError(msg) - - def link(self, target_desc, objects, output_filename, output_dir=None, - libraries=None, library_dirs=None, runtime_library_dirs=None, - export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None, build_temp=None, target_lang=None): - """Link the objects.""" - # use separate copies, so we can modify the lists - extra_preargs = copy.copy(extra_preargs or []) - libraries = copy.copy(libraries or []) - objects = copy.copy(objects or []) - - # Additional libraries - libraries.extend(self.dll_libraries) - - # handle export symbols by creating a def-file - # with executables this only works with gcc/ld as linker - if ((export_symbols is not None) and - (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): - # (The linker doesn't do anything if output is up-to-date. - # So it would probably better to check if we really need this, - # but for this we had to insert some unchanged parts of - # UnixCCompiler, and this is not what we want.) - - # we want to put some files in the same directory as the - # object files are, build_temp doesn't help much - # where are the object files - temp_dir = os.path.dirname(objects[0]) - # name of dll to give the helper files the same base name - (dll_name, dll_extension) = os.path.splitext( - os.path.basename(output_filename)) - - # generate the filenames for these files - def_file = os.path.join(temp_dir, dll_name + ".def") - lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") - - # Generate .def file - contents = [ - "LIBRARY %s" % os.path.basename(output_filename), - "EXPORTS"] - for sym in export_symbols: - contents.append(sym) - self.execute(write_file, (def_file, contents), - "writing %s" % def_file) - - # next add options for def-file and to creating import libraries - - # dllwrap uses different options than gcc/ld - if self.linker_dll == "dllwrap": - extra_preargs.extend(["--output-lib", lib_file]) - # for dllwrap we have to use a special option - extra_preargs.extend(["--def", def_file]) - # we use gcc/ld here and can be sure ld is >= 2.9.10 - else: - # doesn't work: bfd_close build\...\libfoo.a: Invalid operation - #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) - # for gcc/ld the def-file is specified as any object files - objects.append(def_file) - - #end: if ((export_symbols is not None) and - # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): - - # who wants symbols and a many times larger output file - # should explicitly switch the debug mode on - # otherwise we let dllwrap/ld strip the output file - # (On my machine: 10KiB < stripped_file < ??100KiB - # unstripped_file = stripped_file + XXX KiB - # ( XXX=254 for a typical python extension)) - if not debug: - extra_preargs.append("-s") - - UnixCCompiler.link(self, target_desc, objects, output_filename, - output_dir, libraries, library_dirs, - runtime_library_dirs, - None, # export_symbols, we do this in our def-file - debug, extra_preargs, extra_postargs, build_temp, - target_lang) - - # -- Miscellaneous methods ----------------------------------------- - - def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): - """Adds supports for rc and res files.""" - if output_dir is None: - output_dir = '' - obj_names = [] - for src_name in source_filenames: - # use normcase to make sure '.rc' is really '.rc' and not '.RC' - base, ext = os.path.splitext(os.path.normcase(src_name)) - if ext not in (self.src_extensions + ['.rc','.res']): - raise UnknownFileError("unknown file type '%s' (from '%s')" % \ - (ext, src_name)) - if strip_dir: - base = os.path.basename (base) - if ext in ('.res', '.rc'): - # these need to be compiled to object files - obj_names.append (os.path.join(output_dir, - base + ext + self.obj_extension)) - else: - obj_names.append (os.path.join(output_dir, - base + self.obj_extension)) - return obj_names - -# the same as cygwin plus some additional parameters -class Mingw32CCompiler(CygwinCCompiler): - """ Handles the Mingw32 port of the GNU C compiler to Windows. - """ - compiler_type = 'mingw32' - - def __init__(self, verbose=0, dry_run=0, force=0): - - CygwinCCompiler.__init__ (self, verbose, dry_run, force) - - # ld_version >= "2.13" support -shared so use it instead of - # -mdll -static - if self.ld_version >= "2.13": - shared_option = "-shared" - else: - shared_option = "-mdll -static" - - # A real mingw32 doesn't need to specify a different entry point, - # but cygwin 2.91.57 in no-cygwin-mode needs it. - if self.gcc_version <= "2.91.57": - entry_point = '--entry _DllMain@12' - else: - entry_point = '' - - if is_cygwingcc(): - raise CCompilerError( - 'Cygwin gcc cannot be used with --compiler=mingw32') - - self.set_executables(compiler='gcc -O -Wall', - compiler_so='gcc -mdll -O -Wall', - compiler_cxx='g++ -O -Wall', - linker_exe='gcc', - linker_so='%s %s %s' - % (self.linker_dll, shared_option, - entry_point)) - # Maybe we should also append -mthreads, but then the finished - # dlls need another dll (mingwm10.dll see Mingw32 docs) - # (-mthreads: Support thread-safe exception handling on `Mingw32') - - # no additional libraries needed - self.dll_libraries=[] - - # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or later. - self.dll_libraries = get_msvcr() - -# Because these compilers aren't configured in Python's pyconfig.h file by -# default, we should at least warn the user if he is using an unmodified -# version. - -CONFIG_H_OK = "ok" -CONFIG_H_NOTOK = "not ok" -CONFIG_H_UNCERTAIN = "uncertain" - -def check_config_h(): - """Check if the current Python installation appears amenable to building - extensions with GCC. - - Returns a tuple (status, details), where 'status' is one of the following - constants: - - - CONFIG_H_OK: all is well, go ahead and compile - - CONFIG_H_NOTOK: doesn't look good - - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h - - 'details' is a human-readable string explaining the situation. - - Note there are two ways to conclude "OK": either 'sys.version' contains - the string "GCC" (implying that this Python was built with GCC), or the - installed "pyconfig.h" contains the string "__GNUC__". - """ - - # XXX since this function also checks sys.version, it's not strictly a - # "pyconfig.h" check -- should probably be renamed... - - from distutils import sysconfig - - # if sys.version contains GCC then python was compiled with GCC, and the - # pyconfig.h file should be OK - if "GCC" in sys.version: - return CONFIG_H_OK, "sys.version mentions 'GCC'" - - # let's see if __GNUC__ is mentioned in python.h - fn = sysconfig.get_config_h_filename() - try: - config_h = open(fn) - try: - if "__GNUC__" in config_h.read(): - return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn - else: - return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn - finally: - config_h.close() - except OSError as exc: - return (CONFIG_H_UNCERTAIN, - "couldn't read '%s': %s" % (fn, exc.strerror)) - -RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)') - -def _find_exe_version(cmd): - """Find the version of an executable by running `cmd` in the shell. - - If the command is not found, or the output does not match - `RE_VERSION`, returns None. - """ - executable = cmd.split()[0] - if find_executable(executable) is None: - return None - out = Popen(cmd, shell=True, stdout=PIPE).stdout - try: - out_string = out.read() - finally: - out.close() - result = RE_VERSION.search(out_string) - if result is None: - return None - # LooseVersion works with strings - # so we need to decode our bytes - return LooseVersion(result.group(1).decode()) - -def get_versions(): - """ Try to find out the versions of gcc, ld and dllwrap. - - If not possible it returns None for it. - """ - commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] - return tuple([_find_exe_version(cmd) for cmd in commands]) - -def is_cygwingcc(): - '''Try to determine if the gcc that would be used is from cygwin.''' - out_string = check_output(['gcc', '-dumpmachine']) - return out_string.strip().endswith(b'cygwin') diff --git a/distutils/debug.py b/distutils/debug.py deleted file mode 100644 index daf1660f..00000000 --- a/distutils/debug.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - -# If DISTUTILS_DEBUG is anything other than the empty string, we run in -# debug mode. -DEBUG = os.environ.get('DISTUTILS_DEBUG') diff --git a/distutils/dep_util.py b/distutils/dep_util.py deleted file mode 100644 index d74f5e4e..00000000 --- a/distutils/dep_util.py +++ /dev/null @@ -1,92 +0,0 @@ -"""distutils.dep_util - -Utility functions for simple, timestamp-based dependency of files -and groups of files; also, function based entirely on such -timestamp dependency analysis.""" - -import os -from distutils.errors import DistutilsFileError - - -def newer (source, target): - """Return true if 'source' exists and is more recently modified than - 'target', or if 'source' exists and 'target' doesn't. Return false if - both exist and 'target' is the same age or younger than 'source'. - Raise DistutilsFileError if 'source' does not exist. - """ - if not os.path.exists(source): - raise DistutilsFileError("file '%s' does not exist" % - os.path.abspath(source)) - if not os.path.exists(target): - return 1 - - from stat import ST_MTIME - mtime1 = os.stat(source)[ST_MTIME] - mtime2 = os.stat(target)[ST_MTIME] - - return mtime1 > mtime2 - -# newer () - - -def newer_pairwise (sources, targets): - """Walk two filename lists in parallel, testing if each source is newer - than its corresponding target. Return a pair of lists (sources, - targets) where source is newer than target, according to the semantics - of 'newer()'. - """ - if len(sources) != len(targets): - raise ValueError("'sources' and 'targets' must be same length") - - # build a pair of lists (sources, targets) where source is newer - n_sources = [] - n_targets = [] - for i in range(len(sources)): - if newer(sources[i], targets[i]): - n_sources.append(sources[i]) - n_targets.append(targets[i]) - - return (n_sources, n_targets) - -# newer_pairwise () - - -def newer_group (sources, target, missing='error'): - """Return true if 'target' is out-of-date with respect to any file - listed in 'sources'. In other words, if 'target' exists and is newer - than every file in 'sources', return false; otherwise return true. - 'missing' controls what we do when a source file is missing; the - default ("error") is to blow up with an OSError from inside 'stat()'; - if it is "ignore", we silently drop any missing source files; if it is - "newer", any missing source files make us assume that 'target' is - out-of-date (this is handy in "dry-run" mode: it'll make you pretend to - carry out commands that wouldn't work because inputs are missing, but - that doesn't matter because you're not actually going to run the - commands). - """ - # If the target doesn't even exist, then it's definitely out-of-date. - if not os.path.exists(target): - return 1 - - # Otherwise we have to find out the hard way: if *any* source file - # is more recent than 'target', then 'target' is out-of-date and - # we can immediately return true. If we fall through to the end - # of the loop, then 'target' is up-to-date and we return false. - from stat import ST_MTIME - target_mtime = os.stat(target)[ST_MTIME] - for source in sources: - if not os.path.exists(source): - if missing == 'error': # blow up when we stat() the file - pass - elif missing == 'ignore': # missing source dropped from - continue # target's dependency list - elif missing == 'newer': # missing source means target is - return 1 # out-of-date - - source_mtime = os.stat(source)[ST_MTIME] - if source_mtime > target_mtime: - return 1 - else: - return 0 - -# newer_group () diff --git a/distutils/dir_util.py b/distutils/dir_util.py deleted file mode 100644 index d5cd8e3e..00000000 --- a/distutils/dir_util.py +++ /dev/null @@ -1,210 +0,0 @@ -"""distutils.dir_util - -Utility functions for manipulating directories and directory trees.""" - -import os -import errno -from distutils.errors import DistutilsFileError, DistutilsInternalError -from distutils import log - -# cache for by mkpath() -- in addition to cheapening redundant calls, -# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode -_path_created = {} - -# I don't use os.makedirs because a) it's new to Python 1.5.2, and -# b) it blows up if the directory already exists (I want to silently -# succeed in that case). -def mkpath(name, mode=0o777, verbose=1, dry_run=0): - """Create a directory and any missing ancestor directories. - - If the directory already exists (or if 'name' is the empty string, which - means the current directory, which of course exists), then do nothing. - Raise DistutilsFileError if unable to create some directory along the way - (eg. some sub-path exists, but is a file rather than a directory). - If 'verbose' is true, print a one-line summary of each mkdir to stdout. - Return the list of directories actually created. - """ - - global _path_created - - # Detect a common bug -- name is None - if not isinstance(name, str): - raise DistutilsInternalError( - "mkpath: 'name' must be a string (got %r)" % (name,)) - - # XXX what's the better way to handle verbosity? print as we create - # each directory in the path (the current behaviour), or only announce - # the creation of the whole path? (quite easy to do the latter since - # we're not using a recursive algorithm) - - name = os.path.normpath(name) - created_dirs = [] - if os.path.isdir(name) or name == '': - return created_dirs - if _path_created.get(os.path.abspath(name)): - return created_dirs - - (head, tail) = os.path.split(name) - tails = [tail] # stack of lone dirs to create - - while head and tail and not os.path.isdir(head): - (head, tail) = os.path.split(head) - tails.insert(0, tail) # push next higher dir onto stack - - # now 'head' contains the deepest directory that already exists - # (that is, the child of 'head' in 'name' is the highest directory - # that does *not* exist) - for d in tails: - #print "head = %s, d = %s: " % (head, d), - head = os.path.join(head, d) - abs_head = os.path.abspath(head) - - if _path_created.get(abs_head): - continue - - if verbose >= 1: - log.info("creating %s", head) - - if not dry_run: - try: - os.mkdir(head, mode) - except OSError as exc: - if not (exc.errno == errno.EEXIST and os.path.isdir(head)): - raise DistutilsFileError( - "could not create '%s': %s" % (head, exc.args[-1])) - created_dirs.append(head) - - _path_created[abs_head] = 1 - return created_dirs - -def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0): - """Create all the empty directories under 'base_dir' needed to put 'files' - there. - - 'base_dir' is just the name of a directory which doesn't necessarily - exist yet; 'files' is a list of filenames to be interpreted relative to - 'base_dir'. 'base_dir' + the directory portion of every file in 'files' - will be created if it doesn't already exist. 'mode', 'verbose' and - 'dry_run' flags are as for 'mkpath()'. - """ - # First get the list of directories to create - need_dir = set() - for file in files: - need_dir.add(os.path.join(base_dir, os.path.dirname(file))) - - # Now create them - for dir in sorted(need_dir): - mkpath(dir, mode, verbose=verbose, dry_run=dry_run) - -def copy_tree(src, dst, preserve_mode=1, preserve_times=1, - preserve_symlinks=0, update=0, verbose=1, dry_run=0): - """Copy an entire directory tree 'src' to a new location 'dst'. - - Both 'src' and 'dst' must be directory names. If 'src' is not a - directory, raise DistutilsFileError. If 'dst' does not exist, it is - created with 'mkpath()'. The end result of the copy is that every - file in 'src' is copied to 'dst', and directories under 'src' are - recursively copied to 'dst'. Return the list of files that were - copied or might have been copied, using their output name. The - return value is unaffected by 'update' or 'dry_run': it is simply - the list of all files under 'src', with the names changed to be - under 'dst'. - - 'preserve_mode' and 'preserve_times' are the same as for - 'copy_file'; note that they only apply to regular files, not to - directories. If 'preserve_symlinks' is true, symlinks will be - copied as symlinks (on platforms that support them!); otherwise - (the default), the destination of the symlink will be copied. - 'update' and 'verbose' are the same as for 'copy_file'. - """ - from distutils.file_util import copy_file - - if not dry_run and not os.path.isdir(src): - raise DistutilsFileError( - "cannot copy tree '%s': not a directory" % src) - try: - names = os.listdir(src) - except OSError as e: - if dry_run: - names = [] - else: - raise DistutilsFileError( - "error listing files in '%s': %s" % (src, e.strerror)) - - if not dry_run: - mkpath(dst, verbose=verbose) - - outputs = [] - - for n in names: - src_name = os.path.join(src, n) - dst_name = os.path.join(dst, n) - - if n.startswith('.nfs'): - # skip NFS rename files - continue - - if preserve_symlinks and os.path.islink(src_name): - link_dest = os.readlink(src_name) - if verbose >= 1: - log.info("linking %s -> %s", dst_name, link_dest) - if not dry_run: - os.symlink(link_dest, dst_name) - outputs.append(dst_name) - - elif os.path.isdir(src_name): - outputs.extend( - copy_tree(src_name, dst_name, preserve_mode, - preserve_times, preserve_symlinks, update, - verbose=verbose, dry_run=dry_run)) - else: - copy_file(src_name, dst_name, preserve_mode, - preserve_times, update, verbose=verbose, - dry_run=dry_run) - outputs.append(dst_name) - - return outputs - -def _build_cmdtuple(path, cmdtuples): - """Helper for remove_tree().""" - for f in os.listdir(path): - real_f = os.path.join(path,f) - if os.path.isdir(real_f) and not os.path.islink(real_f): - _build_cmdtuple(real_f, cmdtuples) - else: - cmdtuples.append((os.remove, real_f)) - cmdtuples.append((os.rmdir, path)) - -def remove_tree(directory, verbose=1, dry_run=0): - """Recursively remove an entire directory tree. - - Any errors are ignored (apart from being reported to stdout if 'verbose' - is true). - """ - global _path_created - - if verbose >= 1: - log.info("removing '%s' (and everything under it)", directory) - if dry_run: - return - cmdtuples = [] - _build_cmdtuple(directory, cmdtuples) - for cmd in cmdtuples: - try: - cmd[0](cmd[1]) - # remove dir from cache if it's already there - abspath = os.path.abspath(cmd[1]) - if abspath in _path_created: - del _path_created[abspath] - except OSError as exc: - log.warn("error removing %s: %s", directory, exc) - -def ensure_relative(path): - """Take the full path 'path', and make it a relative path. - - This is useful to make 'path' the second argument to os.path.join(). - """ - drive, path = os.path.splitdrive(path) - if path[0:1] == os.sep: - path = drive + path[1:] - return path diff --git a/distutils/dist.py b/distutils/dist.py deleted file mode 100644 index 37db4d6c..00000000 --- a/distutils/dist.py +++ /dev/null @@ -1,1257 +0,0 @@ -"""distutils.dist - -Provides the Distribution class, which represents the module distribution -being built/installed/distributed. -""" - -import sys -import os -import re -from email import message_from_file - -try: - import warnings -except ImportError: - warnings = None - -from distutils.errors import * -from distutils.fancy_getopt import FancyGetopt, translate_longopt -from distutils.util import check_environ, strtobool, rfc822_escape -from distutils import log -from distutils.debug import DEBUG - -# Regex to define acceptable Distutils command names. This is not *quite* -# the same as a Python NAME -- I don't allow leading underscores. The fact -# that they're very similar is no coincidence; the default naming scheme is -# to look for a Python module named after the command. -command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') - - -def _ensure_list(value, fieldname): - if isinstance(value, str): - # a string containing comma separated values is okay. It will - # be converted to a list by Distribution.finalize_options(). - pass - elif not isinstance(value, list): - # passing a tuple or an iterator perhaps, warn and convert - typename = type(value).__name__ - msg = "Warning: '{fieldname}' should be a list, got type '{typename}'" - msg = msg.format(**locals()) - log.log(log.WARN, msg) - value = list(value) - return value - - -class Distribution: - """The core of the Distutils. Most of the work hiding behind 'setup' - is really done within a Distribution instance, which farms the work out - to the Distutils commands specified on the command line. - - Setup scripts will almost never instantiate Distribution directly, - unless the 'setup()' function is totally inadequate to their needs. - However, it is conceivable that a setup script might wish to subclass - Distribution for some specialized purpose, and then pass the subclass - to 'setup()' as the 'distclass' keyword argument. If so, it is - necessary to respect the expectations that 'setup' has of Distribution. - See the code for 'setup()', in core.py, for details. - """ - - # 'global_options' describes the command-line options that may be - # supplied to the setup script prior to any actual commands. - # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of - # these global options. This list should be kept to a bare minimum, - # since every global option is also valid as a command option -- and we - # don't want to pollute the commands with too many options that they - # have minimal control over. - # The fourth entry for verbose means that it can be repeated. - global_options = [ - ('verbose', 'v', "run verbosely (default)", 1), - ('quiet', 'q', "run quietly (turns verbosity off)"), - ('dry-run', 'n', "don't actually do anything"), - ('help', 'h', "show detailed help message"), - ('no-user-cfg', None, - 'ignore pydistutils.cfg in your home directory'), - ] - - # 'common_usage' is a short (2-3 line) string describing the common - # usage of the setup script. - common_usage = """\ -Common commands: (see '--help-commands' for more) - - setup.py build will build the package underneath 'build/' - setup.py install will install the package -""" - - # options that are not propagated to the commands - display_options = [ - ('help-commands', None, - "list all available commands"), - ('name', None, - "print package name"), - ('version', 'V', - "print package version"), - ('fullname', None, - "print -"), - ('author', None, - "print the author's name"), - ('author-email', None, - "print the author's email address"), - ('maintainer', None, - "print the maintainer's name"), - ('maintainer-email', None, - "print the maintainer's email address"), - ('contact', None, - "print the maintainer's name if known, else the author's"), - ('contact-email', None, - "print the maintainer's email address if known, else the author's"), - ('url', None, - "print the URL for this package"), - ('license', None, - "print the license of the package"), - ('licence', None, - "alias for --license"), - ('description', None, - "print the package description"), - ('long-description', None, - "print the long package description"), - ('platforms', None, - "print the list of platforms"), - ('classifiers', None, - "print the list of classifiers"), - ('keywords', None, - "print the list of keywords"), - ('provides', None, - "print the list of packages/modules provided"), - ('requires', None, - "print the list of packages/modules required"), - ('obsoletes', None, - "print the list of packages/modules made obsolete") - ] - display_option_names = [translate_longopt(x[0]) for x in display_options] - - # negative options are options that exclude other options - negative_opt = {'quiet': 'verbose'} - - # -- Creation/initialization methods ------------------------------- - - def __init__(self, attrs=None): - """Construct a new Distribution instance: initialize all the - attributes of a Distribution, and then use 'attrs' (a dictionary - mapping attribute names to values) to assign some of those - attributes their "real" values. (Any attributes not mentioned in - 'attrs' will be assigned to some null value: 0, None, an empty list - or dictionary, etc.) Most importantly, initialize the - 'command_obj' attribute to the empty dictionary; this will be - filled in with real command objects by 'parse_command_line()'. - """ - - # Default values for our command-line options - self.verbose = 1 - self.dry_run = 0 - self.help = 0 - for attr in self.display_option_names: - setattr(self, attr, 0) - - # Store the distribution meta-data (name, version, author, and so - # forth) in a separate object -- we're getting to have enough - # information here (and enough command-line options) that it's - # worth it. Also delegate 'get_XXX()' methods to the 'metadata' - # object in a sneaky and underhanded (but efficient!) way. - self.metadata = DistributionMetadata() - for basename in self.metadata._METHOD_BASENAMES: - method_name = "get_" + basename - setattr(self, method_name, getattr(self.metadata, method_name)) - - # 'cmdclass' maps command names to class objects, so we - # can 1) quickly figure out which class to instantiate when - # we need to create a new command object, and 2) have a way - # for the setup script to override command classes - self.cmdclass = {} - - # 'command_packages' is a list of packages in which commands - # are searched for. The factory for command 'foo' is expected - # to be named 'foo' in the module 'foo' in one of the packages - # named here. This list is searched from the left; an error - # is raised if no named package provides the command being - # searched for. (Always access using get_command_packages().) - self.command_packages = None - - # 'script_name' and 'script_args' are usually set to sys.argv[0] - # and sys.argv[1:], but they can be overridden when the caller is - # not necessarily a setup script run from the command-line. - self.script_name = None - self.script_args = None - - # 'command_options' is where we store command options between - # parsing them (from config files, the command-line, etc.) and when - # they are actually needed -- ie. when the command in question is - # instantiated. It is a dictionary of dictionaries of 2-tuples: - # command_options = { command_name : { option : (source, value) } } - self.command_options = {} - - # 'dist_files' is the list of (command, pyversion, file) that - # have been created by any dist commands run so far. This is - # filled regardless of whether the run is dry or not. pyversion - # gives sysconfig.get_python_version() if the dist file is - # specific to a Python version, 'any' if it is good for all - # Python versions on the target platform, and '' for a source - # file. pyversion should not be used to specify minimum or - # maximum required Python versions; use the metainfo for that - # instead. - self.dist_files = [] - - # These options are really the business of various commands, rather - # than of the Distribution itself. We provide aliases for them in - # Distribution as a convenience to the developer. - self.packages = None - self.package_data = {} - self.package_dir = None - self.py_modules = None - self.libraries = None - self.headers = None - self.ext_modules = None - self.ext_package = None - self.include_dirs = None - self.extra_path = None - self.scripts = None - self.data_files = None - self.password = '' - - # And now initialize bookkeeping stuff that can't be supplied by - # the caller at all. 'command_obj' maps command names to - # Command instances -- that's how we enforce that every command - # class is a singleton. - self.command_obj = {} - - # 'have_run' maps command names to boolean values; it keeps track - # of whether we have actually run a particular command, to make it - # cheap to "run" a command whenever we think we might need to -- if - # it's already been done, no need for expensive filesystem - # operations, we just check the 'have_run' dictionary and carry on. - # It's only safe to query 'have_run' for a command class that has - # been instantiated -- a false value will be inserted when the - # command object is created, and replaced with a true value when - # the command is successfully run. Thus it's probably best to use - # '.get()' rather than a straight lookup. - self.have_run = {} - - # Now we'll use the attrs dictionary (ultimately, keyword args from - # the setup script) to possibly override any or all of these - # distribution options. - - if attrs: - # Pull out the set of command options and work on them - # specifically. Note that this order guarantees that aliased - # command options will override any supplied redundantly - # through the general options dictionary. - options = attrs.get('options') - if options is not None: - del attrs['options'] - for (command, cmd_options) in options.items(): - opt_dict = self.get_option_dict(command) - for (opt, val) in cmd_options.items(): - opt_dict[opt] = ("setup script", val) - - if 'licence' in attrs: - attrs['license'] = attrs['licence'] - del attrs['licence'] - msg = "'licence' distribution option is deprecated; use 'license'" - if warnings is not None: - warnings.warn(msg) - else: - sys.stderr.write(msg + "\n") - - # Now work on the rest of the attributes. Any attribute that's - # not already defined is invalid! - for (key, val) in attrs.items(): - if hasattr(self.metadata, "set_" + key): - getattr(self.metadata, "set_" + key)(val) - elif hasattr(self.metadata, key): - setattr(self.metadata, key, val) - elif hasattr(self, key): - setattr(self, key, val) - else: - msg = "Unknown distribution option: %s" % repr(key) - warnings.warn(msg) - - # no-user-cfg is handled before other command line args - # because other args override the config files, and this - # one is needed before we can load the config files. - # If attrs['script_args'] wasn't passed, assume false. - # - # This also make sure we just look at the global options - self.want_user_cfg = True - - if self.script_args is not None: - for arg in self.script_args: - if not arg.startswith('-'): - break - if arg == '--no-user-cfg': - self.want_user_cfg = False - break - - self.finalize_options() - - def get_option_dict(self, command): - """Get the option dictionary for a given command. If that - command's option dictionary hasn't been created yet, then create it - and return the new dictionary; otherwise, return the existing - option dictionary. - """ - dict = self.command_options.get(command) - if dict is None: - dict = self.command_options[command] = {} - return dict - - def dump_option_dicts(self, header=None, commands=None, indent=""): - from pprint import pformat - - if commands is None: # dump all command option dicts - commands = sorted(self.command_options.keys()) - - if header is not None: - self.announce(indent + header) - indent = indent + " " - - if not commands: - self.announce(indent + "no commands known yet") - return - - for cmd_name in commands: - opt_dict = self.command_options.get(cmd_name) - if opt_dict is None: - self.announce(indent + - "no option dict for '%s' command" % cmd_name) - else: - self.announce(indent + - "option dict for '%s' command:" % cmd_name) - out = pformat(opt_dict) - for line in out.split('\n'): - self.announce(indent + " " + line) - - # -- Config file finding/parsing methods --------------------------- - - def find_config_files(self): - """Find as many configuration files as should be processed for this - platform, and return a list of filenames in the order in which they - should be parsed. The filenames returned are guaranteed to exist - (modulo nasty race conditions). - - There are three possible config files: distutils.cfg in the - Distutils installation directory (ie. where the top-level - Distutils __inst__.py file lives), a file in the user's home - directory named .pydistutils.cfg on Unix and pydistutils.cfg - on Windows/Mac; and setup.cfg in the current directory. - - The file in the user's home directory can be disabled with the - --no-user-cfg option. - """ - files = [] - check_environ() - - # Where to look for the system-wide Distutils config file - sys_dir = os.path.dirname(sys.modules['distutils'].__file__) - - # Look for the system config file - sys_file = os.path.join(sys_dir, "distutils.cfg") - if os.path.isfile(sys_file): - files.append(sys_file) - - # What to call the per-user config file - if os.name == 'posix': - user_filename = ".pydistutils.cfg" - else: - user_filename = "pydistutils.cfg" - - # And look for the user config file - if self.want_user_cfg: - user_file = os.path.join(os.path.expanduser('~'), user_filename) - if os.path.isfile(user_file): - files.append(user_file) - - # All platforms support local setup.cfg - local_file = "setup.cfg" - if os.path.isfile(local_file): - files.append(local_file) - - if DEBUG: - self.announce("using config files: %s" % ', '.join(files)) - - return files - - def parse_config_files(self, filenames=None): - from configparser import ConfigParser - - # Ignore install directory options if we have a venv - if sys.prefix != sys.base_prefix: - ignore_options = [ - 'install-base', 'install-platbase', 'install-lib', - 'install-platlib', 'install-purelib', 'install-headers', - 'install-scripts', 'install-data', 'prefix', 'exec-prefix', - 'home', 'user', 'root'] - else: - ignore_options = [] - - ignore_options = frozenset(ignore_options) - - if filenames is None: - filenames = self.find_config_files() - - if DEBUG: - self.announce("Distribution.parse_config_files():") - - parser = ConfigParser() - for filename in filenames: - if DEBUG: - self.announce(" reading %s" % filename) - parser.read(filename) - for section in parser.sections(): - options = parser.options(section) - opt_dict = self.get_option_dict(section) - - for opt in options: - if opt != '__name__' and opt not in ignore_options: - val = parser.get(section,opt) - opt = opt.replace('-', '_') - opt_dict[opt] = (filename, val) - - # Make the ConfigParser forget everything (so we retain - # the original filenames that options come from) - parser.__init__() - - # If there was a "global" section in the config file, use it - # to set Distribution options. - - if 'global' in self.command_options: - for (opt, (src, val)) in self.command_options['global'].items(): - alias = self.negative_opt.get(opt) - try: - if alias: - setattr(self, alias, not strtobool(val)) - elif opt in ('verbose', 'dry_run'): # ugh! - setattr(self, opt, strtobool(val)) - else: - setattr(self, opt, val) - except ValueError as msg: - raise DistutilsOptionError(msg) - - # -- Command-line parsing methods ---------------------------------- - - def parse_command_line(self): - """Parse the setup script's command line, taken from the - 'script_args' instance attribute (which defaults to 'sys.argv[1:]' - -- see 'setup()' in core.py). This list is first processed for - "global options" -- options that set attributes of the Distribution - instance. Then, it is alternately scanned for Distutils commands - and options for that command. Each new command terminates the - options for the previous command. The allowed options for a - command are determined by the 'user_options' attribute of the - command class -- thus, we have to be able to load command classes - in order to parse the command line. Any error in that 'options' - attribute raises DistutilsGetoptError; any error on the - command-line raises DistutilsArgError. If no Distutils commands - were found on the command line, raises DistutilsArgError. Return - true if command-line was successfully parsed and we should carry - on with executing commands; false if no errors but we shouldn't - execute commands (currently, this only happens if user asks for - help). - """ - # - # We now have enough information to show the Macintosh dialog - # that allows the user to interactively specify the "command line". - # - toplevel_options = self._get_toplevel_options() - - # We have to parse the command line a bit at a time -- global - # options, then the first command, then its options, and so on -- - # because each command will be handled by a different class, and - # the options that are valid for a particular class aren't known - # until we have loaded the command class, which doesn't happen - # until we know what the command is. - - self.commands = [] - parser = FancyGetopt(toplevel_options + self.display_options) - parser.set_negative_aliases(self.negative_opt) - parser.set_aliases({'licence': 'license'}) - args = parser.getopt(args=self.script_args, object=self) - option_order = parser.get_option_order() - log.set_verbosity(self.verbose) - - # for display options we return immediately - if self.handle_display_options(option_order): - return - while args: - args = self._parse_command_opts(parser, args) - if args is None: # user asked for help (and got it) - return - - # Handle the cases of --help as a "global" option, ie. - # "setup.py --help" and "setup.py --help command ...". For the - # former, we show global options (--verbose, --dry-run, etc.) - # and display-only options (--name, --version, etc.); for the - # latter, we omit the display-only options and show help for - # each command listed on the command line. - if self.help: - self._show_help(parser, - display_options=len(self.commands) == 0, - commands=self.commands) - return - - # Oops, no commands found -- an end-user error - if not self.commands: - raise DistutilsArgError("no commands supplied") - - # All is well: return true - return True - - def _get_toplevel_options(self): - """Return the non-display options recognized at the top level. - - This includes options that are recognized *only* at the top - level as well as options recognized for commands. - """ - return self.global_options + [ - ("command-packages=", None, - "list of packages that provide distutils commands"), - ] - - def _parse_command_opts(self, parser, args): - """Parse the command-line options for a single command. - 'parser' must be a FancyGetopt instance; 'args' must be the list - of arguments, starting with the current command (whose options - we are about to parse). Returns a new version of 'args' with - the next command at the front of the list; will be the empty - list if there are no more commands on the command line. Returns - None if the user asked for help on this command. - """ - # late import because of mutual dependence between these modules - from distutils.cmd import Command - - # Pull the current command from the head of the command line - command = args[0] - if not command_re.match(command): - raise SystemExit("invalid command name '%s'" % command) - self.commands.append(command) - - # Dig up the command class that implements this command, so we - # 1) know that it's a valid command, and 2) know which options - # it takes. - try: - cmd_class = self.get_command_class(command) - except DistutilsModuleError as msg: - raise DistutilsArgError(msg) - - # Require that the command class be derived from Command -- want - # to be sure that the basic "command" interface is implemented. - if not issubclass(cmd_class, Command): - raise DistutilsClassError( - "command class %s must subclass Command" % cmd_class) - - # Also make sure that the command object provides a list of its - # known options. - if not (hasattr(cmd_class, 'user_options') and - isinstance(cmd_class.user_options, list)): - msg = ("command class %s must provide " - "'user_options' attribute (a list of tuples)") - raise DistutilsClassError(msg % cmd_class) - - # If the command class has a list of negative alias options, - # merge it in with the global negative aliases. - negative_opt = self.negative_opt - if hasattr(cmd_class, 'negative_opt'): - negative_opt = negative_opt.copy() - negative_opt.update(cmd_class.negative_opt) - - # Check for help_options in command class. They have a different - # format (tuple of four) so we need to preprocess them here. - if (hasattr(cmd_class, 'help_options') and - isinstance(cmd_class.help_options, list)): - help_options = fix_help_options(cmd_class.help_options) - else: - help_options = [] - - # All commands support the global options too, just by adding - # in 'global_options'. - parser.set_option_table(self.global_options + - cmd_class.user_options + - help_options) - parser.set_negative_aliases(negative_opt) - (args, opts) = parser.getopt(args[1:]) - if hasattr(opts, 'help') and opts.help: - self._show_help(parser, display_options=0, commands=[cmd_class]) - return - - if (hasattr(cmd_class, 'help_options') and - isinstance(cmd_class.help_options, list)): - help_option_found=0 - for (help_option, short, desc, func) in cmd_class.help_options: - if hasattr(opts, parser.get_attr_name(help_option)): - help_option_found=1 - if callable(func): - func() - else: - raise DistutilsClassError( - "invalid help function %r for help option '%s': " - "must be a callable object (function, etc.)" - % (func, help_option)) - - if help_option_found: - return - - # Put the options from the command-line into their official - # holding pen, the 'command_options' dictionary. - opt_dict = self.get_option_dict(command) - for (name, value) in vars(opts).items(): - opt_dict[name] = ("command line", value) - - return args - - def finalize_options(self): - """Set final values for all the options on the Distribution - instance, analogous to the .finalize_options() method of Command - objects. - """ - for attr in ('keywords', 'platforms'): - value = getattr(self.metadata, attr) - if value is None: - continue - if isinstance(value, str): - value = [elm.strip() for elm in value.split(',')] - setattr(self.metadata, attr, value) - - def _show_help(self, parser, global_options=1, display_options=1, - commands=[]): - """Show help for the setup script command-line in the form of - several lists of command-line options. 'parser' should be a - FancyGetopt instance; do not expect it to be returned in the - same state, as its option table will be reset to make it - generate the correct help text. - - If 'global_options' is true, lists the global options: - --verbose, --dry-run, etc. If 'display_options' is true, lists - the "display-only" options: --name, --version, etc. Finally, - lists per-command help for every command name or command class - in 'commands'. - """ - # late import because of mutual dependence between these modules - from distutils.core import gen_usage - from distutils.cmd import Command - - if global_options: - if display_options: - options = self._get_toplevel_options() - else: - options = self.global_options - parser.set_option_table(options) - parser.print_help(self.common_usage + "\nGlobal options:") - print('') - - if display_options: - parser.set_option_table(self.display_options) - parser.print_help( - "Information display options (just display " + - "information, ignore any commands)") - print('') - - for command in self.commands: - if isinstance(command, type) and issubclass(command, Command): - klass = command - else: - klass = self.get_command_class(command) - if (hasattr(klass, 'help_options') and - isinstance(klass.help_options, list)): - parser.set_option_table(klass.user_options + - fix_help_options(klass.help_options)) - else: - parser.set_option_table(klass.user_options) - parser.print_help("Options for '%s' command:" % klass.__name__) - print('') - - print(gen_usage(self.script_name)) - - def handle_display_options(self, option_order): - """If there were any non-global "display-only" options - (--help-commands or the metadata display options) on the command - line, display the requested info and return true; else return - false. - """ - from distutils.core import gen_usage - - # User just wants a list of commands -- we'll print it out and stop - # processing now (ie. if they ran "setup --help-commands foo bar", - # we ignore "foo bar"). - if self.help_commands: - self.print_commands() - print('') - print(gen_usage(self.script_name)) - return 1 - - # If user supplied any of the "display metadata" options, then - # display that metadata in the order in which the user supplied the - # metadata options. - any_display_options = 0 - is_display_option = {} - for option in self.display_options: - is_display_option[option[0]] = 1 - - for (opt, val) in option_order: - if val and is_display_option.get(opt): - opt = translate_longopt(opt) - value = getattr(self.metadata, "get_"+opt)() - if opt in ['keywords', 'platforms']: - print(','.join(value)) - elif opt in ('classifiers', 'provides', 'requires', - 'obsoletes'): - print('\n'.join(value)) - else: - print(value) - any_display_options = 1 - - return any_display_options - - def print_command_list(self, commands, header, max_length): - """Print a subset of the list of all commands -- used by - 'print_commands()'. - """ - print(header + ":") - - for cmd in commands: - klass = self.cmdclass.get(cmd) - if not klass: - klass = self.get_command_class(cmd) - try: - description = klass.description - except AttributeError: - description = "(no description available)" - - print(" %-*s %s" % (max_length, cmd, description)) - - def print_commands(self): - """Print out a help message listing all available commands with a - description of each. The list is divided into "standard commands" - (listed in distutils.command.__all__) and "extra commands" - (mentioned in self.cmdclass, but not a standard command). The - descriptions come from the command class attribute - 'description'. - """ - import distutils.command - std_commands = distutils.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append(cmd) - - max_length = 0 - for cmd in (std_commands + extra_commands): - if len(cmd) > max_length: - max_length = len(cmd) - - self.print_command_list(std_commands, - "Standard commands", - max_length) - if extra_commands: - print() - self.print_command_list(extra_commands, - "Extra commands", - max_length) - - def get_command_list(self): - """Get a list of (command, description) tuples. - The list is divided into "standard commands" (listed in - distutils.command.__all__) and "extra commands" (mentioned in - self.cmdclass, but not a standard command). The descriptions come - from the command class attribute 'description'. - """ - # Currently this is only used on Mac OS, for the Mac-only GUI - # Distutils interface (by Jack Jansen) - import distutils.command - std_commands = distutils.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append(cmd) - - rv = [] - for cmd in (std_commands + extra_commands): - klass = self.cmdclass.get(cmd) - if not klass: - klass = self.get_command_class(cmd) - try: - description = klass.description - except AttributeError: - description = "(no description available)" - rv.append((cmd, description)) - return rv - - # -- Command class/object methods ---------------------------------- - - def get_command_packages(self): - """Return a list of packages from which commands are loaded.""" - pkgs = self.command_packages - if not isinstance(pkgs, list): - if pkgs is None: - pkgs = '' - pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != ''] - if "distutils.command" not in pkgs: - pkgs.insert(0, "distutils.command") - self.command_packages = pkgs - return pkgs - - def get_command_class(self, command): - """Return the class that implements the Distutils command named by - 'command'. First we check the 'cmdclass' dictionary; if the - command is mentioned there, we fetch the class object from the - dictionary and return it. Otherwise we load the command module - ("distutils.command." + command) and fetch the command class from - the module. The loaded class is also stored in 'cmdclass' - to speed future calls to 'get_command_class()'. - - Raises DistutilsModuleError if the expected module could not be - found, or if that module does not define the expected class. - """ - klass = self.cmdclass.get(command) - if klass: - return klass - - for pkgname in self.get_command_packages(): - module_name = "%s.%s" % (pkgname, command) - klass_name = command - - try: - __import__(module_name) - module = sys.modules[module_name] - except ImportError: - continue - - try: - klass = getattr(module, klass_name) - except AttributeError: - raise DistutilsModuleError( - "invalid command '%s' (no class '%s' in module '%s')" - % (command, klass_name, module_name)) - - self.cmdclass[command] = klass - return klass - - raise DistutilsModuleError("invalid command '%s'" % command) - - def get_command_obj(self, command, create=1): - """Return the command object for 'command'. Normally this object - is cached on a previous call to 'get_command_obj()'; if no command - object for 'command' is in the cache, then we either create and - return it (if 'create' is true) or return None. - """ - cmd_obj = self.command_obj.get(command) - if not cmd_obj and create: - if DEBUG: - self.announce("Distribution.get_command_obj(): " - "creating '%s' command object" % command) - - klass = self.get_command_class(command) - cmd_obj = self.command_obj[command] = klass(self) - self.have_run[command] = 0 - - # Set any options that were supplied in config files - # or on the command line. (NB. support for error - # reporting is lame here: any errors aren't reported - # until 'finalize_options()' is called, which means - # we won't report the source of the error.) - options = self.command_options.get(command) - if options: - self._set_command_options(cmd_obj, options) - - return cmd_obj - - def _set_command_options(self, command_obj, option_dict=None): - """Set the options for 'command_obj' from 'option_dict'. Basically - this means copying elements of a dictionary ('option_dict') to - attributes of an instance ('command'). - - 'command_obj' must be a Command instance. If 'option_dict' is not - supplied, uses the standard option dictionary for this command - (from 'self.command_options'). - """ - command_name = command_obj.get_command_name() - if option_dict is None: - option_dict = self.get_option_dict(command_name) - - if DEBUG: - self.announce(" setting options for '%s' command:" % command_name) - for (option, (source, value)) in option_dict.items(): - if DEBUG: - self.announce(" %s = %s (from %s)" % (option, value, - source)) - try: - bool_opts = [translate_longopt(o) - for o in command_obj.boolean_options] - except AttributeError: - bool_opts = [] - try: - neg_opt = command_obj.negative_opt - except AttributeError: - neg_opt = {} - - try: - is_string = isinstance(value, str) - if option in neg_opt and is_string: - setattr(command_obj, neg_opt[option], not strtobool(value)) - elif option in bool_opts and is_string: - setattr(command_obj, option, strtobool(value)) - elif hasattr(command_obj, option): - setattr(command_obj, option, value) - else: - raise DistutilsOptionError( - "error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) - except ValueError as msg: - raise DistutilsOptionError(msg) - - def reinitialize_command(self, command, reinit_subcommands=0): - """Reinitializes a command to the state it was in when first - returned by 'get_command_obj()': ie., initialized but not yet - finalized. This provides the opportunity to sneak option - values in programmatically, overriding or supplementing - user-supplied values from the config files and command line. - You'll have to re-finalize the command object (by calling - 'finalize_options()' or 'ensure_finalized()') before using it for - real. - - 'command' should be a command name (string) or command object. If - 'reinit_subcommands' is true, also reinitializes the command's - sub-commands, as declared by the 'sub_commands' class attribute (if - it has one). See the "install" command for an example. Only - reinitializes the sub-commands that actually matter, ie. those - whose test predicates return true. - - Returns the reinitialized command object. - """ - from distutils.cmd import Command - if not isinstance(command, Command): - command_name = command - command = self.get_command_obj(command_name) - else: - command_name = command.get_command_name() - - if not command.finalized: - return command - command.initialize_options() - command.finalized = 0 - self.have_run[command_name] = 0 - self._set_command_options(command) - - if reinit_subcommands: - for sub in command.get_sub_commands(): - self.reinitialize_command(sub, reinit_subcommands) - - return command - - # -- Methods that operate on the Distribution ---------------------- - - def announce(self, msg, level=log.INFO): - log.log(level, msg) - - def run_commands(self): - """Run each command that was seen on the setup script command line. - Uses the list of commands found and cache of command objects - created by 'get_command_obj()'. - """ - for cmd in self.commands: - self.run_command(cmd) - - # -- Methods that operate on its Commands -------------------------- - - def run_command(self, command): - """Do whatever it takes to run a command (including nothing at all, - if the command has already been run). Specifically: if we have - already created and run the command named by 'command', return - silently without doing anything. If the command named by 'command' - doesn't even have a command object yet, create one. Then invoke - 'run()' on that command object (or an existing one). - """ - # Already been here, done that? then return silently. - if self.have_run.get(command): - return - - log.info("running %s", command) - cmd_obj = self.get_command_obj(command) - cmd_obj.ensure_finalized() - cmd_obj.run() - self.have_run[command] = 1 - - # -- Distribution query methods ------------------------------------ - - def has_pure_modules(self): - return len(self.packages or self.py_modules or []) > 0 - - def has_ext_modules(self): - return self.ext_modules and len(self.ext_modules) > 0 - - def has_c_libraries(self): - return self.libraries and len(self.libraries) > 0 - - def has_modules(self): - return self.has_pure_modules() or self.has_ext_modules() - - def has_headers(self): - return self.headers and len(self.headers) > 0 - - def has_scripts(self): - return self.scripts and len(self.scripts) > 0 - - def has_data_files(self): - return self.data_files and len(self.data_files) > 0 - - def is_pure(self): - return (self.has_pure_modules() and - not self.has_ext_modules() and - not self.has_c_libraries()) - - # -- Metadata query methods ---------------------------------------- - - # If you're looking for 'get_name()', 'get_version()', and so forth, - # they are defined in a sneaky way: the constructor binds self.get_XXX - # to self.metadata.get_XXX. The actual code is in the - # DistributionMetadata class, below. - -class DistributionMetadata: - """Dummy class to hold the distribution meta-data: name, version, - author, and so forth. - """ - - _METHOD_BASENAMES = ("name", "version", "author", "author_email", - "maintainer", "maintainer_email", "url", - "license", "description", "long_description", - "keywords", "platforms", "fullname", "contact", - "contact_email", "classifiers", "download_url", - # PEP 314 - "provides", "requires", "obsoletes", - ) - - def __init__(self, path=None): - if path is not None: - self.read_pkg_file(open(path)) - else: - self.name = None - self.version = None - self.author = None - self.author_email = None - self.maintainer = None - self.maintainer_email = None - self.url = None - self.license = None - self.description = None - self.long_description = None - self.keywords = None - self.platforms = None - self.classifiers = None - self.download_url = None - # PEP 314 - self.provides = None - self.requires = None - self.obsoletes = None - - def read_pkg_file(self, file): - """Reads the metadata values from a file object.""" - msg = message_from_file(file) - - def _read_field(name): - value = msg[name] - if value == 'UNKNOWN': - return None - return value - - def _read_list(name): - values = msg.get_all(name, None) - if values == []: - return None - return values - - metadata_version = msg['metadata-version'] - self.name = _read_field('name') - self.version = _read_field('version') - self.description = _read_field('summary') - # we are filling author only. - self.author = _read_field('author') - self.maintainer = None - self.author_email = _read_field('author-email') - self.maintainer_email = None - self.url = _read_field('home-page') - self.license = _read_field('license') - - if 'download-url' in msg: - self.download_url = _read_field('download-url') - else: - self.download_url = None - - self.long_description = _read_field('description') - self.description = _read_field('summary') - - if 'keywords' in msg: - self.keywords = _read_field('keywords').split(',') - - self.platforms = _read_list('platform') - self.classifiers = _read_list('classifier') - - # PEP 314 - these fields only exist in 1.1 - if metadata_version == '1.1': - self.requires = _read_list('requires') - self.provides = _read_list('provides') - self.obsoletes = _read_list('obsoletes') - else: - self.requires = None - self.provides = None - self.obsoletes = None - - def write_pkg_info(self, base_dir): - """Write the PKG-INFO file into the release tree. - """ - with open(os.path.join(base_dir, 'PKG-INFO'), 'w', - encoding='UTF-8') as pkg_info: - self.write_pkg_file(pkg_info) - - def write_pkg_file(self, file): - """Write the PKG-INFO format data to a file object. - """ - version = '1.0' - if (self.provides or self.requires or self.obsoletes or - self.classifiers or self.download_url): - version = '1.1' - - file.write('Metadata-Version: %s\n' % version) - file.write('Name: %s\n' % self.get_name()) - file.write('Version: %s\n' % self.get_version()) - file.write('Summary: %s\n' % self.get_description()) - file.write('Home-page: %s\n' % self.get_url()) - file.write('Author: %s\n' % self.get_contact()) - file.write('Author-email: %s\n' % self.get_contact_email()) - file.write('License: %s\n' % self.get_license()) - if self.download_url: - file.write('Download-URL: %s\n' % self.download_url) - - long_desc = rfc822_escape(self.get_long_description()) - file.write('Description: %s\n' % long_desc) - - keywords = ','.join(self.get_keywords()) - if keywords: - file.write('Keywords: %s\n' % keywords) - - self._write_list(file, 'Platform', self.get_platforms()) - self._write_list(file, 'Classifier', self.get_classifiers()) - - # PEP 314 - self._write_list(file, 'Requires', self.get_requires()) - self._write_list(file, 'Provides', self.get_provides()) - self._write_list(file, 'Obsoletes', self.get_obsoletes()) - - def _write_list(self, file, name, values): - for value in values: - file.write('%s: %s\n' % (name, value)) - - # -- Metadata query methods ---------------------------------------- - - def get_name(self): - return self.name or "UNKNOWN" - - def get_version(self): - return self.version or "0.0.0" - - def get_fullname(self): - return "%s-%s" % (self.get_name(), self.get_version()) - - def get_author(self): - return self.author or "UNKNOWN" - - def get_author_email(self): - return self.author_email or "UNKNOWN" - - def get_maintainer(self): - return self.maintainer or "UNKNOWN" - - def get_maintainer_email(self): - return self.maintainer_email or "UNKNOWN" - - def get_contact(self): - return self.maintainer or self.author or "UNKNOWN" - - def get_contact_email(self): - return self.maintainer_email or self.author_email or "UNKNOWN" - - def get_url(self): - return self.url or "UNKNOWN" - - def get_license(self): - return self.license or "UNKNOWN" - get_licence = get_license - - def get_description(self): - return self.description or "UNKNOWN" - - def get_long_description(self): - return self.long_description or "UNKNOWN" - - def get_keywords(self): - return self.keywords or [] - - def set_keywords(self, value): - self.keywords = _ensure_list(value, 'keywords') - - def get_platforms(self): - return self.platforms or ["UNKNOWN"] - - def set_platforms(self, value): - self.platforms = _ensure_list(value, 'platforms') - - def get_classifiers(self): - return self.classifiers or [] - - def set_classifiers(self, value): - self.classifiers = _ensure_list(value, 'classifiers') - - def get_download_url(self): - return self.download_url or "UNKNOWN" - - # PEP 314 - def get_requires(self): - return self.requires or [] - - def set_requires(self, value): - import distutils.versionpredicate - for v in value: - distutils.versionpredicate.VersionPredicate(v) - self.requires = list(value) - - def get_provides(self): - return self.provides or [] - - def set_provides(self, value): - value = [v.strip() for v in value] - for v in value: - import distutils.versionpredicate - distutils.versionpredicate.split_provision(v) - self.provides = value - - def get_obsoletes(self): - return self.obsoletes or [] - - def set_obsoletes(self, value): - import distutils.versionpredicate - for v in value: - distutils.versionpredicate.VersionPredicate(v) - self.obsoletes = list(value) - -def fix_help_options(options): - """Convert a 4-tuple 'help_options' list as found in various command - classes to the 3-tuple form required by FancyGetopt. - """ - new_options = [] - for help_tuple in options: - new_options.append(help_tuple[0:3]) - return new_options diff --git a/distutils/errors.py b/distutils/errors.py deleted file mode 100644 index 8b93059e..00000000 --- a/distutils/errors.py +++ /dev/null @@ -1,97 +0,0 @@ -"""distutils.errors - -Provides exceptions used by the Distutils modules. Note that Distutils -modules may raise standard exceptions; in particular, SystemExit is -usually raised for errors that are obviously the end-user's fault -(eg. bad command-line arguments). - -This module is safe to use in "from ... import *" mode; it only exports -symbols whose names start with "Distutils" and end with "Error".""" - -class DistutilsError (Exception): - """The root of all Distutils evil.""" - pass - -class DistutilsModuleError (DistutilsError): - """Unable to load an expected module, or to find an expected class - within some module (in particular, command modules and classes).""" - pass - -class DistutilsClassError (DistutilsError): - """Some command class (or possibly distribution class, if anyone - feels a need to subclass Distribution) is found not to be holding - up its end of the bargain, ie. implementing some part of the - "command "interface.""" - pass - -class DistutilsGetoptError (DistutilsError): - """The option table provided to 'fancy_getopt()' is bogus.""" - pass - -class DistutilsArgError (DistutilsError): - """Raised by fancy_getopt in response to getopt.error -- ie. an - error in the command line usage.""" - pass - -class DistutilsFileError (DistutilsError): - """Any problems in the filesystem: expected file not found, etc. - Typically this is for problems that we detect before OSError - could be raised.""" - pass - -class DistutilsOptionError (DistutilsError): - """Syntactic/semantic errors in command options, such as use of - mutually conflicting options, or inconsistent options, - badly-spelled values, etc. No distinction is made between option - values originating in the setup script, the command line, config - files, or what-have-you -- but if we *know* something originated in - the setup script, we'll raise DistutilsSetupError instead.""" - pass - -class DistutilsSetupError (DistutilsError): - """For errors that can be definitely blamed on the setup script, - such as invalid keyword arguments to 'setup()'.""" - pass - -class DistutilsPlatformError (DistutilsError): - """We don't know how to do something on the current platform (but - we do know how to do it on some platform) -- eg. trying to compile - C files on a platform not supported by a CCompiler subclass.""" - pass - -class DistutilsExecError (DistutilsError): - """Any problems executing an external program (such as the C - compiler, when compiling C files).""" - pass - -class DistutilsInternalError (DistutilsError): - """Internal inconsistencies or impossibilities (obviously, this - should never be seen if the code is working!).""" - pass - -class DistutilsTemplateError (DistutilsError): - """Syntax error in a file list template.""" - -class DistutilsByteCompileError(DistutilsError): - """Byte compile error.""" - -# Exception classes used by the CCompiler implementation classes -class CCompilerError (Exception): - """Some compile/link operation failed.""" - -class PreprocessError (CCompilerError): - """Failure to preprocess one or more C/C++ files.""" - -class CompileError (CCompilerError): - """Failure to compile one or more C/C++ source files.""" - -class LibError (CCompilerError): - """Failure to create a static library from one or more C/C++ object - files.""" - -class LinkError (CCompilerError): - """Failure to link one or more C/C++ object files into an executable - or shared library file.""" - -class UnknownFileError (CCompilerError): - """Attempt to process an unknown file type.""" diff --git a/distutils/extension.py b/distutils/extension.py deleted file mode 100644 index c507da36..00000000 --- a/distutils/extension.py +++ /dev/null @@ -1,240 +0,0 @@ -"""distutils.extension - -Provides the Extension class, used to describe C/C++ extension -modules in setup scripts.""" - -import os -import warnings - -# This class is really only used by the "build_ext" command, so it might -# make sense to put it in distutils.command.build_ext. However, that -# module is already big enough, and I want to make this class a bit more -# complex to simplify some common cases ("foo" module in "foo.c") and do -# better error-checking ("foo.c" actually exists). -# -# Also, putting this in build_ext.py means every setup script would have to -# import that large-ish module (indirectly, through distutils.core) in -# order to do anything. - -class Extension: - """Just a collection of attributes that describes an extension - module and everything needed to build it (hopefully in a portable - way, but there are hooks that let you be as unportable as you need). - - Instance attributes: - name : string - the full name of the extension, including any packages -- ie. - *not* a filename or pathname, but Python dotted name - sources : [string] - list of source filenames, relative to the distribution root - (where the setup script lives), in Unix form (slash-separated) - for portability. Source files may be C, C++, SWIG (.i), - platform-specific resource files, or whatever else is recognized - by the "build_ext" command as source for a Python extension. - include_dirs : [string] - list of directories to search for C/C++ header files (in Unix - form for portability) - define_macros : [(name : string, value : string|None)] - list of macros to define; each macro is defined using a 2-tuple, - where 'value' is either the string to define it to or None to - define it without a particular value (equivalent of "#define - FOO" in source or -DFOO on Unix C compiler command line) - undef_macros : [string] - list of macros to undefine explicitly - library_dirs : [string] - list of directories to search for C/C++ libraries at link time - libraries : [string] - list of library names (not filenames or paths) to link against - runtime_library_dirs : [string] - list of directories to search for C/C++ libraries at run time - (for shared extensions, this is when the extension is loaded) - extra_objects : [string] - list of extra files to link with (eg. object files not implied - by 'sources', static library that must be explicitly specified, - binary resource files, etc.) - extra_compile_args : [string] - any extra platform- and compiler-specific information to use - when compiling the source files in 'sources'. For platforms and - compilers where "command line" makes sense, this is typically a - list of command-line arguments, but for other platforms it could - be anything. - extra_link_args : [string] - any extra platform- and compiler-specific information to use - when linking object files together to create the extension (or - to create a new static Python interpreter). Similar - interpretation as for 'extra_compile_args'. - export_symbols : [string] - list of symbols to be exported from a shared extension. Not - used on all platforms, and not generally necessary for Python - extensions, which typically export exactly one symbol: "init" + - extension_name. - swig_opts : [string] - any extra options to pass to SWIG if a source file has the .i - extension. - depends : [string] - list of files that the extension depends on - language : string - extension language (i.e. "c", "c++", "objc"). Will be detected - from the source extensions if not provided. - optional : boolean - specifies that a build failure in the extension should not abort the - build process, but simply not install the failing extension. - """ - - # When adding arguments to this constructor, be sure to update - # setup_keywords in core.py. - def __init__(self, name, sources, - include_dirs=None, - define_macros=None, - undef_macros=None, - library_dirs=None, - libraries=None, - runtime_library_dirs=None, - extra_objects=None, - extra_compile_args=None, - extra_link_args=None, - export_symbols=None, - swig_opts = None, - depends=None, - language=None, - optional=None, - **kw # To catch unknown keywords - ): - if not isinstance(name, str): - raise AssertionError("'name' must be a string") - if not (isinstance(sources, list) and - all(isinstance(v, str) for v in sources)): - raise AssertionError("'sources' must be a list of strings") - - self.name = name - self.sources = sources - self.include_dirs = include_dirs or [] - self.define_macros = define_macros or [] - self.undef_macros = undef_macros or [] - self.library_dirs = library_dirs or [] - self.libraries = libraries or [] - self.runtime_library_dirs = runtime_library_dirs or [] - self.extra_objects = extra_objects or [] - self.extra_compile_args = extra_compile_args or [] - self.extra_link_args = extra_link_args or [] - self.export_symbols = export_symbols or [] - self.swig_opts = swig_opts or [] - self.depends = depends or [] - self.language = language - self.optional = optional - - # If there are unknown keyword options, warn about them - if len(kw) > 0: - options = [repr(option) for option in kw] - options = ', '.join(sorted(options)) - msg = "Unknown Extension options: %s" % options - warnings.warn(msg) - - def __repr__(self): - return '<%s.%s(%r) at %#x>' % ( - self.__class__.__module__, - self.__class__.__qualname__, - self.name, - id(self)) - - -def read_setup_file(filename): - """Reads a Setup file and returns Extension instances.""" - from distutils.sysconfig import (parse_makefile, expand_makefile_vars, - _variable_rx) - - from distutils.text_file import TextFile - from distutils.util import split_quoted - - # First pass over the file to gather "VAR = VALUE" assignments. - vars = parse_makefile(filename) - - # Second pass to gobble up the real content: lines of the form - # ... [ ...] [ ...] [ ...] - file = TextFile(filename, - strip_comments=1, skip_blanks=1, join_lines=1, - lstrip_ws=1, rstrip_ws=1) - try: - extensions = [] - - while True: - line = file.readline() - if line is None: # eof - break - if _variable_rx.match(line): # VAR=VALUE, handled in first pass - continue - - if line[0] == line[-1] == "*": - file.warn("'%s' lines not handled yet" % line) - continue - - line = expand_makefile_vars(line, vars) - words = split_quoted(line) - - # NB. this parses a slightly different syntax than the old - # makesetup script: here, there must be exactly one extension per - # line, and it must be the first word of the line. I have no idea - # why the old syntax supported multiple extensions per line, as - # they all wind up being the same. - - module = words[0] - ext = Extension(module, []) - append_next_word = None - - for word in words[1:]: - if append_next_word is not None: - append_next_word.append(word) - append_next_word = None - continue - - suffix = os.path.splitext(word)[1] - switch = word[0:2] ; value = word[2:] - - if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): - # hmm, should we do something about C vs. C++ sources? - # or leave it up to the CCompiler implementation to - # worry about? - ext.sources.append(word) - elif switch == "-I": - ext.include_dirs.append(value) - elif switch == "-D": - equals = value.find("=") - if equals == -1: # bare "-DFOO" -- no value - ext.define_macros.append((value, None)) - else: # "-DFOO=blah" - ext.define_macros.append((value[0:equals], - value[equals+2:])) - elif switch == "-U": - ext.undef_macros.append(value) - elif switch == "-C": # only here 'cause makesetup has it! - ext.extra_compile_args.append(word) - elif switch == "-l": - ext.libraries.append(value) - elif switch == "-L": - ext.library_dirs.append(value) - elif switch == "-R": - ext.runtime_library_dirs.append(value) - elif word == "-rpath": - append_next_word = ext.runtime_library_dirs - elif word == "-Xlinker": - append_next_word = ext.extra_link_args - elif word == "-Xcompiler": - append_next_word = ext.extra_compile_args - elif switch == "-u": - ext.extra_link_args.append(word) - if not value: - append_next_word = ext.extra_link_args - elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): - # NB. a really faithful emulation of makesetup would - # append a .o file to extra_objects only if it - # had a slash in it; otherwise, it would s/.o/.c/ - # and append it to sources. Hmmmm. - ext.extra_objects.append(word) - else: - file.warn("unrecognized argument '%s'" % word) - - extensions.append(ext) - finally: - file.close() - - return extensions diff --git a/distutils/fancy_getopt.py b/distutils/fancy_getopt.py deleted file mode 100644 index 7d170dd2..00000000 --- a/distutils/fancy_getopt.py +++ /dev/null @@ -1,457 +0,0 @@ -"""distutils.fancy_getopt - -Wrapper around the standard getopt module that provides the following -additional features: - * short and long options are tied together - * options have help strings, so fancy_getopt could potentially - create a complete usage summary - * options set attributes of a passed-in object -""" - -import sys, string, re -import getopt -from distutils.errors import * - -# Much like command_re in distutils.core, this is close to but not quite -# the same as a Python NAME -- except, in the spirit of most GNU -# utilities, we use '-' in place of '_'. (The spirit of LISP lives on!) -# The similarities to NAME are again not a coincidence... -longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)' -longopt_re = re.compile(r'^%s$' % longopt_pat) - -# For recognizing "negative alias" options, eg. "quiet=!verbose" -neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) - -# This is used to translate long options to legitimate Python identifiers -# (for use as attributes of some object). -longopt_xlate = str.maketrans('-', '_') - -class FancyGetopt: - """Wrapper around the standard 'getopt()' module that provides some - handy extra functionality: - * short and long options are tied together - * options have help strings, and help text can be assembled - from them - * options set attributes of a passed-in object - * boolean options can have "negative aliases" -- eg. if - --quiet is the "negative alias" of --verbose, then "--quiet" - on the command line sets 'verbose' to false - """ - - def __init__(self, option_table=None): - # The option table is (currently) a list of tuples. The - # tuples may have 3 or four values: - # (long_option, short_option, help_string [, repeatable]) - # if an option takes an argument, its long_option should have '=' - # appended; short_option should just be a single character, no ':' - # in any case. If a long_option doesn't have a corresponding - # short_option, short_option should be None. All option tuples - # must have long options. - self.option_table = option_table - - # 'option_index' maps long option names to entries in the option - # table (ie. those 3-tuples). - self.option_index = {} - if self.option_table: - self._build_index() - - # 'alias' records (duh) alias options; {'foo': 'bar'} means - # --foo is an alias for --bar - self.alias = {} - - # 'negative_alias' keeps track of options that are the boolean - # opposite of some other option - self.negative_alias = {} - - # These keep track of the information in the option table. We - # don't actually populate these structures until we're ready to - # parse the command-line, since the 'option_table' passed in here - # isn't necessarily the final word. - self.short_opts = [] - self.long_opts = [] - self.short2long = {} - self.attr_name = {} - self.takes_arg = {} - - # And 'option_order' is filled up in 'getopt()'; it records the - # original order of options (and their values) on the command-line, - # but expands short options, converts aliases, etc. - self.option_order = [] - - def _build_index(self): - self.option_index.clear() - for option in self.option_table: - self.option_index[option[0]] = option - - def set_option_table(self, option_table): - self.option_table = option_table - self._build_index() - - def add_option(self, long_option, short_option=None, help_string=None): - if long_option in self.option_index: - raise DistutilsGetoptError( - "option conflict: already an option '%s'" % long_option) - else: - option = (long_option, short_option, help_string) - self.option_table.append(option) - self.option_index[long_option] = option - - def has_option(self, long_option): - """Return true if the option table for this parser has an - option with long name 'long_option'.""" - return long_option in self.option_index - - def get_attr_name(self, long_option): - """Translate long option name 'long_option' to the form it - has as an attribute of some object: ie., translate hyphens - to underscores.""" - return long_option.translate(longopt_xlate) - - def _check_alias_dict(self, aliases, what): - assert isinstance(aliases, dict) - for (alias, opt) in aliases.items(): - if alias not in self.option_index: - raise DistutilsGetoptError(("invalid %s '%s': " - "option '%s' not defined") % (what, alias, alias)) - if opt not in self.option_index: - raise DistutilsGetoptError(("invalid %s '%s': " - "aliased option '%s' not defined") % (what, alias, opt)) - - def set_aliases(self, alias): - """Set the aliases for this option parser.""" - self._check_alias_dict(alias, "alias") - self.alias = alias - - def set_negative_aliases(self, negative_alias): - """Set the negative aliases for this option parser. - 'negative_alias' should be a dictionary mapping option names to - option names, both the key and value must already be defined - in the option table.""" - self._check_alias_dict(negative_alias, "negative alias") - self.negative_alias = negative_alias - - def _grok_option_table(self): - """Populate the various data structures that keep tabs on the - option table. Called by 'getopt()' before it can do anything - worthwhile. - """ - self.long_opts = [] - self.short_opts = [] - self.short2long.clear() - self.repeat = {} - - for option in self.option_table: - if len(option) == 3: - long, short, help = option - repeat = 0 - elif len(option) == 4: - long, short, help, repeat = option - else: - # the option table is part of the code, so simply - # assert that it is correct - raise ValueError("invalid option tuple: %r" % (option,)) - - # Type- and value-check the option names - if not isinstance(long, str) or len(long) < 2: - raise DistutilsGetoptError(("invalid long option '%s': " - "must be a string of length >= 2") % long) - - if (not ((short is None) or - (isinstance(short, str) and len(short) == 1))): - raise DistutilsGetoptError("invalid short option '%s': " - "must a single character or None" % short) - - self.repeat[long] = repeat - self.long_opts.append(long) - - if long[-1] == '=': # option takes an argument? - if short: short = short + ':' - long = long[0:-1] - self.takes_arg[long] = 1 - else: - # Is option is a "negative alias" for some other option (eg. - # "quiet" == "!verbose")? - alias_to = self.negative_alias.get(long) - if alias_to is not None: - if self.takes_arg[alias_to]: - raise DistutilsGetoptError( - "invalid negative alias '%s': " - "aliased option '%s' takes a value" - % (long, alias_to)) - - self.long_opts[-1] = long # XXX redundant?! - self.takes_arg[long] = 0 - - # If this is an alias option, make sure its "takes arg" flag is - # the same as the option it's aliased to. - alias_to = self.alias.get(long) - if alias_to is not None: - if self.takes_arg[long] != self.takes_arg[alias_to]: - raise DistutilsGetoptError( - "invalid alias '%s': inconsistent with " - "aliased option '%s' (one of them takes a value, " - "the other doesn't" - % (long, alias_to)) - - # Now enforce some bondage on the long option name, so we can - # later translate it to an attribute name on some object. Have - # to do this a bit late to make sure we've removed any trailing - # '='. - if not longopt_re.match(long): - raise DistutilsGetoptError( - "invalid long option name '%s' " - "(must be letters, numbers, hyphens only" % long) - - self.attr_name[long] = self.get_attr_name(long) - if short: - self.short_opts.append(short) - self.short2long[short[0]] = long - - def getopt(self, args=None, object=None): - """Parse command-line options in args. Store as attributes on object. - - If 'args' is None or not supplied, uses 'sys.argv[1:]'. If - 'object' is None or not supplied, creates a new OptionDummy - object, stores option values there, and returns a tuple (args, - object). If 'object' is supplied, it is modified in place and - 'getopt()' just returns 'args'; in both cases, the returned - 'args' is a modified copy of the passed-in 'args' list, which - is left untouched. - """ - if args is None: - args = sys.argv[1:] - if object is None: - object = OptionDummy() - created_object = True - else: - created_object = False - - self._grok_option_table() - - short_opts = ' '.join(self.short_opts) - try: - opts, args = getopt.getopt(args, short_opts, self.long_opts) - except getopt.error as msg: - raise DistutilsArgError(msg) - - for opt, val in opts: - if len(opt) == 2 and opt[0] == '-': # it's a short option - opt = self.short2long[opt[1]] - else: - assert len(opt) > 2 and opt[:2] == '--' - opt = opt[2:] - - alias = self.alias.get(opt) - if alias: - opt = alias - - if not self.takes_arg[opt]: # boolean option? - assert val == '', "boolean option can't have value" - alias = self.negative_alias.get(opt) - if alias: - opt = alias - val = 0 - else: - val = 1 - - attr = self.attr_name[opt] - # The only repeating option at the moment is 'verbose'. - # It has a negative option -q quiet, which should set verbose = 0. - if val and self.repeat.get(attr) is not None: - val = getattr(object, attr, 0) + 1 - setattr(object, attr, val) - self.option_order.append((opt, val)) - - # for opts - if created_object: - return args, object - else: - return args - - def get_option_order(self): - """Returns the list of (option, value) tuples processed by the - previous run of 'getopt()'. Raises RuntimeError if - 'getopt()' hasn't been called yet. - """ - if self.option_order is None: - raise RuntimeError("'getopt()' hasn't been called yet") - else: - return self.option_order - - def generate_help(self, header=None): - """Generate help text (a list of strings, one per suggested line of - output) from the option table for this FancyGetopt object. - """ - # Blithely assume the option table is good: probably wouldn't call - # 'generate_help()' unless you've already called 'getopt()'. - - # First pass: determine maximum length of long option names - max_opt = 0 - for option in self.option_table: - long = option[0] - short = option[1] - l = len(long) - if long[-1] == '=': - l = l - 1 - if short is not None: - l = l + 5 # " (-x)" where short == 'x' - if l > max_opt: - max_opt = l - - opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter - - # Typical help block looks like this: - # --foo controls foonabulation - # Help block for longest option looks like this: - # --flimflam set the flim-flam level - # and with wrapped text: - # --flimflam set the flim-flam level (must be between - # 0 and 100, except on Tuesdays) - # Options with short names will have the short name shown (but - # it doesn't contribute to max_opt): - # --foo (-f) controls foonabulation - # If adding the short option would make the left column too wide, - # we push the explanation off to the next line - # --flimflam (-l) - # set the flim-flam level - # Important parameters: - # - 2 spaces before option block start lines - # - 2 dashes for each long option name - # - min. 2 spaces between option and explanation (gutter) - # - 5 characters (incl. space) for short option name - - # Now generate lines of help text. (If 80 columns were good enough - # for Jesus, then 78 columns are good enough for me!) - line_width = 78 - text_width = line_width - opt_width - big_indent = ' ' * opt_width - if header: - lines = [header] - else: - lines = ['Option summary:'] - - for option in self.option_table: - long, short, help = option[:3] - text = wrap_text(help, text_width) - if long[-1] == '=': - long = long[0:-1] - - # Case 1: no short option at all (makes life easy) - if short is None: - if text: - lines.append(" --%-*s %s" % (max_opt, long, text[0])) - else: - lines.append(" --%-*s " % (max_opt, long)) - - # Case 2: we have a short option, so we have to include it - # just after the long option - else: - opt_names = "%s (-%s)" % (long, short) - if text: - lines.append(" --%-*s %s" % - (max_opt, opt_names, text[0])) - else: - lines.append(" --%-*s" % opt_names) - - for l in text[1:]: - lines.append(big_indent + l) - return lines - - def print_help(self, header=None, file=None): - if file is None: - file = sys.stdout - for line in self.generate_help(header): - file.write(line + "\n") - - -def fancy_getopt(options, negative_opt, object, args): - parser = FancyGetopt(options) - parser.set_negative_aliases(negative_opt) - return parser.getopt(args, object) - - -WS_TRANS = {ord(_wschar) : ' ' for _wschar in string.whitespace} - -def wrap_text(text, width): - """wrap_text(text : string, width : int) -> [string] - - Split 'text' into multiple lines of no more than 'width' characters - each, and return the list of strings that results. - """ - if text is None: - return [] - if len(text) <= width: - return [text] - - text = text.expandtabs() - text = text.translate(WS_TRANS) - chunks = re.split(r'( +|-+)', text) - chunks = [ch for ch in chunks if ch] # ' - ' results in empty strings - lines = [] - - while chunks: - cur_line = [] # list of chunks (to-be-joined) - cur_len = 0 # length of current line - - while chunks: - l = len(chunks[0]) - if cur_len + l <= width: # can squeeze (at least) this chunk in - cur_line.append(chunks[0]) - del chunks[0] - cur_len = cur_len + l - else: # this line is full - # drop last chunk if all space - if cur_line and cur_line[-1][0] == ' ': - del cur_line[-1] - break - - if chunks: # any chunks left to process? - # if the current line is still empty, then we had a single - # chunk that's too big too fit on a line -- so we break - # down and break it up at the line width - if cur_len == 0: - cur_line.append(chunks[0][0:width]) - chunks[0] = chunks[0][width:] - - # all-whitespace chunks at the end of a line can be discarded - # (and we know from the re.split above that if a chunk has - # *any* whitespace, it is *all* whitespace) - if chunks[0][0] == ' ': - del chunks[0] - - # and store this line in the list-of-all-lines -- as a single - # string, of course! - lines.append(''.join(cur_line)) - - return lines - - -def translate_longopt(opt): - """Convert a long option name to a valid Python identifier by - changing "-" to "_". - """ - return opt.translate(longopt_xlate) - - -class OptionDummy: - """Dummy class just used as a place to hold command-line option - values as instance attributes.""" - - def __init__(self, options=[]): - """Create a new OptionDummy instance. The attributes listed in - 'options' will be initialized to None.""" - for opt in options: - setattr(self, opt, None) - - -if __name__ == "__main__": - text = """\ -Tra-la-la, supercalifragilisticexpialidocious. -How *do* you spell that odd word, anyways? -(Someone ask Mary -- she'll know [or she'll -say, "How should I know?"].)""" - - for w in (10, 20, 30, 40): - print("width: %d" % w) - print("\n".join(wrap_text(text, w))) - print() diff --git a/distutils/file_util.py b/distutils/file_util.py deleted file mode 100644 index b3fee35a..00000000 --- a/distutils/file_util.py +++ /dev/null @@ -1,238 +0,0 @@ -"""distutils.file_util - -Utility functions for operating on single files. -""" - -import os -from distutils.errors import DistutilsFileError -from distutils import log - -# for generating verbose output in 'copy_file()' -_copy_action = { None: 'copying', - 'hard': 'hard linking', - 'sym': 'symbolically linking' } - - -def _copy_file_contents(src, dst, buffer_size=16*1024): - """Copy the file 'src' to 'dst'; both must be filenames. Any error - opening either file, reading from 'src', or writing to 'dst', raises - DistutilsFileError. Data is read/written in chunks of 'buffer_size' - bytes (default 16k). No attempt is made to handle anything apart from - regular files. - """ - # Stolen from shutil module in the standard library, but with - # custom error-handling added. - fsrc = None - fdst = None - try: - try: - fsrc = open(src, 'rb') - except OSError as e: - raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror)) - - if os.path.exists(dst): - try: - os.unlink(dst) - except OSError as e: - raise DistutilsFileError( - "could not delete '%s': %s" % (dst, e.strerror)) - - try: - fdst = open(dst, 'wb') - except OSError as e: - raise DistutilsFileError( - "could not create '%s': %s" % (dst, e.strerror)) - - while True: - try: - buf = fsrc.read(buffer_size) - except OSError as e: - raise DistutilsFileError( - "could not read from '%s': %s" % (src, e.strerror)) - - if not buf: - break - - try: - fdst.write(buf) - except OSError as e: - raise DistutilsFileError( - "could not write to '%s': %s" % (dst, e.strerror)) - finally: - if fdst: - fdst.close() - if fsrc: - fsrc.close() - -def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, - link=None, verbose=1, dry_run=0): - """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is - copied there with the same name; otherwise, it must be a filename. (If - the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' - is true (the default), the file's mode (type and permission bits, or - whatever is analogous on the current platform) is copied. If - 'preserve_times' is true (the default), the last-modified and - last-access times are copied as well. If 'update' is true, 'src' will - only be copied if 'dst' does not exist, or if 'dst' does exist but is - older than 'src'. - - 'link' allows you to make hard links (os.link) or symbolic links - (os.symlink) instead of copying: set it to "hard" or "sym"; if it is - None (the default), files are copied. Don't set 'link' on systems that - don't support it: 'copy_file()' doesn't check if hard or symbolic - linking is available. If hardlink fails, falls back to - _copy_file_contents(). - - Under Mac OS, uses the native file copy function in macostools; on - other systems, uses '_copy_file_contents()' to copy file contents. - - Return a tuple (dest_name, copied): 'dest_name' is the actual name of - the output file, and 'copied' is true if the file was copied (or would - have been copied, if 'dry_run' true). - """ - # XXX if the destination file already exists, we clobber it if - # copying, but blow up if linking. Hmmm. And I don't know what - # macostools.copyfile() does. Should definitely be consistent, and - # should probably blow up if destination exists and we would be - # changing it (ie. it's not already a hard/soft link to src OR - # (not update) and (src newer than dst). - - from distutils.dep_util import newer - from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE - - if not os.path.isfile(src): - raise DistutilsFileError( - "can't copy '%s': doesn't exist or not a regular file" % src) - - if os.path.isdir(dst): - dir = dst - dst = os.path.join(dst, os.path.basename(src)) - else: - dir = os.path.dirname(dst) - - if update and not newer(src, dst): - if verbose >= 1: - log.debug("not copying %s (output up-to-date)", src) - return (dst, 0) - - try: - action = _copy_action[link] - except KeyError: - raise ValueError("invalid value '%s' for 'link' argument" % link) - - if verbose >= 1: - if os.path.basename(dst) == os.path.basename(src): - log.info("%s %s -> %s", action, src, dir) - else: - log.info("%s %s -> %s", action, src, dst) - - if dry_run: - return (dst, 1) - - # If linking (hard or symbolic), use the appropriate system call - # (Unix only, of course, but that's the caller's responsibility) - elif link == 'hard': - if not (os.path.exists(dst) and os.path.samefile(src, dst)): - try: - os.link(src, dst) - return (dst, 1) - except OSError: - # If hard linking fails, fall back on copying file - # (some special filesystems don't support hard linking - # even under Unix, see issue #8876). - pass - elif link == 'sym': - if not (os.path.exists(dst) and os.path.samefile(src, dst)): - os.symlink(src, dst) - return (dst, 1) - - # Otherwise (non-Mac, not linking), copy the file contents and - # (optionally) copy the times and mode. - _copy_file_contents(src, dst) - if preserve_mode or preserve_times: - st = os.stat(src) - - # According to David Ascher , utime() should be done - # before chmod() (at least under NT). - if preserve_times: - os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) - if preserve_mode: - os.chmod(dst, S_IMODE(st[ST_MODE])) - - return (dst, 1) - - -# XXX I suspect this is Unix-specific -- need porting help! -def move_file (src, dst, - verbose=1, - dry_run=0): - - """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will - be moved into it with the same name; otherwise, 'src' is just renamed - to 'dst'. Return the new full name of the file. - - Handles cross-device moves on Unix using 'copy_file()'. What about - other systems??? - """ - from os.path import exists, isfile, isdir, basename, dirname - import errno - - if verbose >= 1: - log.info("moving %s -> %s", src, dst) - - if dry_run: - return dst - - if not isfile(src): - raise DistutilsFileError("can't move '%s': not a regular file" % src) - - if isdir(dst): - dst = os.path.join(dst, basename(src)) - elif exists(dst): - raise DistutilsFileError( - "can't move '%s': destination '%s' already exists" % - (src, dst)) - - if not isdir(dirname(dst)): - raise DistutilsFileError( - "can't move '%s': destination '%s' not a valid path" % - (src, dst)) - - copy_it = False - try: - os.rename(src, dst) - except OSError as e: - (num, msg) = e.args - if num == errno.EXDEV: - copy_it = True - else: - raise DistutilsFileError( - "couldn't move '%s' to '%s': %s" % (src, dst, msg)) - - if copy_it: - copy_file(src, dst, verbose=verbose) - try: - os.unlink(src) - except OSError as e: - (num, msg) = e.args - try: - os.unlink(dst) - except OSError: - pass - raise DistutilsFileError( - "couldn't move '%s' to '%s' by copy/delete: " - "delete '%s' failed: %s" - % (src, dst, src, msg)) - return dst - - -def write_file (filename, contents): - """Create a file with the specified name and write 'contents' (a - sequence of strings without line terminators) to it. - """ - f = open(filename, "w") - try: - for line in contents: - f.write(line + "\n") - finally: - f.close() diff --git a/distutils/filelist.py b/distutils/filelist.py deleted file mode 100644 index c92d5fdb..00000000 --- a/distutils/filelist.py +++ /dev/null @@ -1,327 +0,0 @@ -"""distutils.filelist - -Provides the FileList class, used for poking about the filesystem -and building lists of files. -""" - -import os, re -import fnmatch -import functools -from distutils.util import convert_path -from distutils.errors import DistutilsTemplateError, DistutilsInternalError -from distutils import log - -class FileList: - """A list of files built by on exploring the filesystem and filtered by - applying various patterns to what we find there. - - Instance attributes: - dir - directory from which files will be taken -- only used if - 'allfiles' not supplied to constructor - files - list of filenames currently being built/filtered/manipulated - allfiles - complete list of files under consideration (ie. without any - filtering applied) - """ - - def __init__(self, warn=None, debug_print=None): - # ignore argument to FileList, but keep them for backwards - # compatibility - self.allfiles = None - self.files = [] - - def set_allfiles(self, allfiles): - self.allfiles = allfiles - - def findall(self, dir=os.curdir): - self.allfiles = findall(dir) - - def debug_print(self, msg): - """Print 'msg' to stdout if the global DEBUG (taken from the - DISTUTILS_DEBUG environment variable) flag is true. - """ - from distutils.debug import DEBUG - if DEBUG: - print(msg) - - # -- List-like methods --------------------------------------------- - - def append(self, item): - self.files.append(item) - - def extend(self, items): - self.files.extend(items) - - def sort(self): - # Not a strict lexical sort! - sortable_files = sorted(map(os.path.split, self.files)) - self.files = [] - for sort_tuple in sortable_files: - self.files.append(os.path.join(*sort_tuple)) - - - # -- Other miscellaneous utility methods --------------------------- - - def remove_duplicates(self): - # Assumes list has been sorted! - for i in range(len(self.files) - 1, 0, -1): - if self.files[i] == self.files[i - 1]: - del self.files[i] - - - # -- "File template" methods --------------------------------------- - - def _parse_template_line(self, line): - words = line.split() - action = words[0] - - patterns = dir = dir_pattern = None - - if action in ('include', 'exclude', - 'global-include', 'global-exclude'): - if len(words) < 2: - raise DistutilsTemplateError( - "'%s' expects ..." % action) - patterns = [convert_path(w) for w in words[1:]] - elif action in ('recursive-include', 'recursive-exclude'): - if len(words) < 3: - raise DistutilsTemplateError( - "'%s' expects ..." % action) - dir = convert_path(words[1]) - patterns = [convert_path(w) for w in words[2:]] - elif action in ('graft', 'prune'): - if len(words) != 2: - raise DistutilsTemplateError( - "'%s' expects a single " % action) - dir_pattern = convert_path(words[1]) - else: - raise DistutilsTemplateError("unknown action '%s'" % action) - - return (action, patterns, dir, dir_pattern) - - def process_template_line(self, line): - # Parse the line: split it up, make sure the right number of words - # is there, and return the relevant words. 'action' is always - # defined: it's the first word of the line. Which of the other - # three are defined depends on the action; it'll be either - # patterns, (dir and patterns), or (dir_pattern). - (action, patterns, dir, dir_pattern) = self._parse_template_line(line) - - # OK, now we know that the action is valid and we have the - # right number of words on the line for that action -- so we - # can proceed with minimal error-checking. - if action == 'include': - self.debug_print("include " + ' '.join(patterns)) - for pattern in patterns: - if not self.include_pattern(pattern, anchor=1): - log.warn("warning: no files found matching '%s'", - pattern) - - elif action == 'exclude': - self.debug_print("exclude " + ' '.join(patterns)) - for pattern in patterns: - if not self.exclude_pattern(pattern, anchor=1): - log.warn(("warning: no previously-included files " - "found matching '%s'"), pattern) - - elif action == 'global-include': - self.debug_print("global-include " + ' '.join(patterns)) - for pattern in patterns: - if not self.include_pattern(pattern, anchor=0): - log.warn(("warning: no files found matching '%s' " - "anywhere in distribution"), pattern) - - elif action == 'global-exclude': - self.debug_print("global-exclude " + ' '.join(patterns)) - for pattern in patterns: - if not self.exclude_pattern(pattern, anchor=0): - log.warn(("warning: no previously-included files matching " - "'%s' found anywhere in distribution"), - pattern) - - elif action == 'recursive-include': - self.debug_print("recursive-include %s %s" % - (dir, ' '.join(patterns))) - for pattern in patterns: - if not self.include_pattern(pattern, prefix=dir): - log.warn(("warning: no files found matching '%s' " - "under directory '%s'"), - pattern, dir) - - elif action == 'recursive-exclude': - self.debug_print("recursive-exclude %s %s" % - (dir, ' '.join(patterns))) - for pattern in patterns: - if not self.exclude_pattern(pattern, prefix=dir): - log.warn(("warning: no previously-included files matching " - "'%s' found under directory '%s'"), - pattern, dir) - - elif action == 'graft': - self.debug_print("graft " + dir_pattern) - if not self.include_pattern(None, prefix=dir_pattern): - log.warn("warning: no directories found matching '%s'", - dir_pattern) - - elif action == 'prune': - self.debug_print("prune " + dir_pattern) - if not self.exclude_pattern(None, prefix=dir_pattern): - log.warn(("no previously-included directories found " - "matching '%s'"), dir_pattern) - else: - raise DistutilsInternalError( - "this cannot happen: invalid action '%s'" % action) - - - # -- Filtering/selection methods ----------------------------------- - - def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): - """Select strings (presumably filenames) from 'self.files' that - match 'pattern', a Unix-style wildcard (glob) pattern. Patterns - are not quite the same as implemented by the 'fnmatch' module: '*' - and '?' match non-special characters, where "special" is platform- - dependent: slash on Unix; colon, slash, and backslash on - DOS/Windows; and colon on Mac OS. - - If 'anchor' is true (the default), then the pattern match is more - stringent: "*.py" will match "foo.py" but not "foo/bar.py". If - 'anchor' is false, both of these will match. - - If 'prefix' is supplied, then only filenames starting with 'prefix' - (itself a pattern) and ending with 'pattern', with anything in between - them, will match. 'anchor' is ignored in this case. - - If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and - 'pattern' is assumed to be either a string containing a regex or a - regex object -- no translation is done, the regex is just compiled - and used as-is. - - Selected strings will be added to self.files. - - Return True if files are found, False otherwise. - """ - # XXX docstring lying about what the special chars are? - files_found = False - pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) - self.debug_print("include_pattern: applying regex r'%s'" % - pattern_re.pattern) - - # delayed loading of allfiles list - if self.allfiles is None: - self.findall() - - for name in self.allfiles: - if pattern_re.search(name): - self.debug_print(" adding " + name) - self.files.append(name) - files_found = True - return files_found - - - def exclude_pattern (self, pattern, - anchor=1, prefix=None, is_regex=0): - """Remove strings (presumably filenames) from 'files' that match - 'pattern'. Other parameters are the same as for - 'include_pattern()', above. - The list 'self.files' is modified in place. - Return True if files are found, False otherwise. - """ - files_found = False - pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) - self.debug_print("exclude_pattern: applying regex r'%s'" % - pattern_re.pattern) - for i in range(len(self.files)-1, -1, -1): - if pattern_re.search(self.files[i]): - self.debug_print(" removing " + self.files[i]) - del self.files[i] - files_found = True - return files_found - - -# ---------------------------------------------------------------------- -# Utility functions - -def _find_all_simple(path): - """ - Find all files under 'path' - """ - results = ( - os.path.join(base, file) - for base, dirs, files in os.walk(path, followlinks=True) - for file in files - ) - return filter(os.path.isfile, results) - - -def findall(dir=os.curdir): - """ - Find all files under 'dir' and return the list of full filenames. - Unless dir is '.', return full filenames with dir prepended. - """ - files = _find_all_simple(dir) - if dir == os.curdir: - make_rel = functools.partial(os.path.relpath, start=dir) - files = map(make_rel, files) - return list(files) - - -def glob_to_re(pattern): - """Translate a shell-like glob pattern to a regular expression; return - a string containing the regex. Differs from 'fnmatch.translate()' in - that '*' does not match "special characters" (which are - platform-specific). - """ - pattern_re = fnmatch.translate(pattern) - - # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which - # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, - # and by extension they shouldn't match such "special characters" under - # any OS. So change all non-escaped dots in the RE to match any - # character except the special characters (currently: just os.sep). - sep = os.sep - if os.sep == '\\': - # we're using a regex to manipulate a regex, so we need - # to escape the backslash twice - sep = r'\\\\' - escaped = r'\1[^%s]' % sep - pattern_re = re.sub(r'((?= self.threshold: - if args: - msg = msg % args - if level in (WARN, ERROR, FATAL): - stream = sys.stderr - else: - stream = sys.stdout - try: - stream.write('%s\n' % msg) - except UnicodeEncodeError: - # emulate backslashreplace error handler - encoding = stream.encoding - msg = msg.encode(encoding, "backslashreplace").decode(encoding) - stream.write('%s\n' % msg) - stream.flush() - - def log(self, level, msg, *args): - self._log(level, msg, args) - - def debug(self, msg, *args): - self._log(DEBUG, msg, args) - - def info(self, msg, *args): - self._log(INFO, msg, args) - - def warn(self, msg, *args): - self._log(WARN, msg, args) - - def error(self, msg, *args): - self._log(ERROR, msg, args) - - def fatal(self, msg, *args): - self._log(FATAL, msg, args) - -_global_log = Log() -log = _global_log.log -debug = _global_log.debug -info = _global_log.info -warn = _global_log.warn -error = _global_log.error -fatal = _global_log.fatal - -def set_threshold(level): - # return the old threshold for use from tests - old = _global_log.threshold - _global_log.threshold = level - return old - -def set_verbosity(v): - if v <= 0: - set_threshold(WARN) - elif v == 1: - set_threshold(INFO) - elif v >= 2: - set_threshold(DEBUG) diff --git a/distutils/msvc9compiler.py b/distutils/msvc9compiler.py deleted file mode 100644 index 6934e964..00000000 --- a/distutils/msvc9compiler.py +++ /dev/null @@ -1,788 +0,0 @@ -"""distutils.msvc9compiler - -Contains MSVCCompiler, an implementation of the abstract CCompiler class -for the Microsoft Visual Studio 2008. - -The module is compatible with VS 2005 and VS 2008. You can find legacy support -for older versions of VS in distutils.msvccompiler. -""" - -# Written by Perry Stoll -# hacked by Robin Becker and Thomas Heller to do a better job of -# finding DevStudio (through the registry) -# ported to VS2005 and VS 2008 by Christian Heimes - -import os -import subprocess -import sys -import re - -from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import CCompiler, gen_lib_options -from distutils import log -from distutils.util import get_platform - -import winreg - -RegOpenKeyEx = winreg.OpenKeyEx -RegEnumKey = winreg.EnumKey -RegEnumValue = winreg.EnumValue -RegError = winreg.error - -HKEYS = (winreg.HKEY_USERS, - winreg.HKEY_CURRENT_USER, - winreg.HKEY_LOCAL_MACHINE, - winreg.HKEY_CLASSES_ROOT) - -NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32) -if NATIVE_WIN64: - # Visual C++ is a 32-bit application, so we need to look in - # the corresponding registry branch, if we're running a - # 64-bit Python on Win64 - VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" - WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" - NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" -else: - VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" - WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" - NET_BASE = r"Software\Microsoft\.NETFramework" - -# A map keyed by get_platform() return values to values accepted by -# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is -# the param to cross-compile on x86 targeting amd64.) -PLAT_TO_VCVARS = { - 'win32' : 'x86', - 'win-amd64' : 'amd64', -} - -class Reg: - """Helper class to read values from the registry - """ - - def get_value(cls, path, key): - for base in HKEYS: - d = cls.read_values(base, path) - if d and key in d: - return d[key] - raise KeyError(key) - get_value = classmethod(get_value) - - def read_keys(cls, base, key): - """Return list of registry keys.""" - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - L = [] - i = 0 - while True: - try: - k = RegEnumKey(handle, i) - except RegError: - break - L.append(k) - i += 1 - return L - read_keys = classmethod(read_keys) - - def read_values(cls, base, key): - """Return dict of registry keys and values. - - All names are converted to lowercase. - """ - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - d = {} - i = 0 - while True: - try: - name, value, type = RegEnumValue(handle, i) - except RegError: - break - name = name.lower() - d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) - i += 1 - return d - read_values = classmethod(read_values) - - def convert_mbcs(s): - dec = getattr(s, "decode", None) - if dec is not None: - try: - s = dec("mbcs") - except UnicodeError: - pass - return s - convert_mbcs = staticmethod(convert_mbcs) - -class MacroExpander: - - def __init__(self, version): - self.macros = {} - self.vsbase = VS_BASE % version - self.load_macros(version) - - def set_macro(self, macro, path, key): - self.macros["$(%s)" % macro] = Reg.get_value(path, key) - - def load_macros(self, version): - self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") - self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") - self.set_macro("FrameworkDir", NET_BASE, "installroot") - try: - if version >= 8.0: - self.set_macro("FrameworkSDKDir", NET_BASE, - "sdkinstallrootv2.0") - else: - raise KeyError("sdkinstallrootv2.0") - except KeyError: - raise DistutilsPlatformError( - """Python was built with Visual Studio 2008; -extensions must be built with a compiler than can generate compatible binaries. -Visual Studio 2008 was not found on this system. If you have Cygwin installed, -you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") - - if version >= 9.0: - self.set_macro("FrameworkVersion", self.vsbase, "clr version") - self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") - else: - p = r"Software\Microsoft\NET Framework Setup\Product" - for base in HKEYS: - try: - h = RegOpenKeyEx(base, p) - except RegError: - continue - key = RegEnumKey(h, 0) - d = Reg.get_value(base, r"%s\%s" % (p, key)) - self.macros["$(FrameworkVersion)"] = d["version"] - - def sub(self, s): - for k, v in self.macros.items(): - s = s.replace(k, v) - return s - -def get_build_version(): - """Return the version of MSVC that was used to build Python. - - For Python 2.3 and up, the version number is included in - sys.version. For earlier versions, assume the compiler is MSVC 6. - """ - prefix = "MSC v." - i = sys.version.find(prefix) - if i == -1: - return 6 - i = i + len(prefix) - s, rest = sys.version[i:].split(" ", 1) - majorVersion = int(s[:-2]) - 6 - if majorVersion >= 13: - # v13 was skipped and should be v14 - majorVersion += 1 - minorVersion = int(s[2:3]) / 10.0 - # I don't think paths are affected by minor version in version 6 - if majorVersion == 6: - minorVersion = 0 - if majorVersion >= 6: - return majorVersion + minorVersion - # else we don't know what version of the compiler this is - return None - -def normalize_and_reduce_paths(paths): - """Return a list of normalized paths with duplicates removed. - - The current order of paths is maintained. - """ - # Paths are normalized so things like: /a and /a/ aren't both preserved. - reduced_paths = [] - for p in paths: - np = os.path.normpath(p) - # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. - if np not in reduced_paths: - reduced_paths.append(np) - return reduced_paths - -def removeDuplicates(variable): - """Remove duplicate values of an environment variable. - """ - oldList = variable.split(os.pathsep) - newList = [] - for i in oldList: - if i not in newList: - newList.append(i) - newVariable = os.pathsep.join(newList) - return newVariable - -def find_vcvarsall(version): - """Find the vcvarsall.bat file - - At first it tries to find the productdir of VS 2008 in the registry. If - that fails it falls back to the VS90COMNTOOLS env var. - """ - vsbase = VS_BASE % version - try: - productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, - "productdir") - except KeyError: - log.debug("Unable to find productdir in registry") - productdir = None - - if not productdir or not os.path.isdir(productdir): - toolskey = "VS%0.f0COMNTOOLS" % version - toolsdir = os.environ.get(toolskey, None) - - if toolsdir and os.path.isdir(toolsdir): - productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") - productdir = os.path.abspath(productdir) - if not os.path.isdir(productdir): - log.debug("%s is not a valid directory" % productdir) - return None - else: - log.debug("Env var %s is not set or invalid" % toolskey) - if not productdir: - log.debug("No productdir found") - return None - vcvarsall = os.path.join(productdir, "vcvarsall.bat") - if os.path.isfile(vcvarsall): - return vcvarsall - log.debug("Unable to find vcvarsall.bat") - return None - -def query_vcvarsall(version, arch="x86"): - """Launch vcvarsall.bat and read the settings from its environment - """ - vcvarsall = find_vcvarsall(version) - interesting = {"include", "lib", "libpath", "path"} - result = {} - - if vcvarsall is None: - raise DistutilsPlatformError("Unable to find vcvarsall.bat") - log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) - popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - try: - stdout, stderr = popen.communicate() - if popen.wait() != 0: - raise DistutilsPlatformError(stderr.decode("mbcs")) - - stdout = stdout.decode("mbcs") - for line in stdout.split("\n"): - line = Reg.convert_mbcs(line) - if '=' not in line: - continue - line = line.strip() - key, value = line.split('=', 1) - key = key.lower() - if key in interesting: - if value.endswith(os.pathsep): - value = value[:-1] - result[key] = removeDuplicates(value) - - finally: - popen.stdout.close() - popen.stderr.close() - - if len(result) != len(interesting): - raise ValueError(str(list(result.keys()))) - - return result - -# More globals -VERSION = get_build_version() -if VERSION < 8.0: - raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) -# MACROS = MacroExpander(VERSION) - -class MSVCCompiler(CCompiler) : - """Concrete class that implements an interface to Microsoft Visual C++, - as defined by the CCompiler abstract class.""" - - compiler_type = 'msvc' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.rc'] - _mc_extensions = ['.mc'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = (_c_extensions + _cpp_extensions + - _rc_extensions + _mc_extensions) - res_extension = '.res' - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - def __init__(self, verbose=0, dry_run=0, force=0): - CCompiler.__init__ (self, verbose, dry_run, force) - self.__version = VERSION - self.__root = r"Software\Microsoft\VisualStudio" - # self.__macros = MACROS - self.__paths = [] - # target platform (.plat_name is consistent with 'bdist') - self.plat_name = None - self.__arch = None # deprecated name - self.initialized = False - - def initialize(self, plat_name=None): - # multi-init means we would need to check platform same each time... - assert not self.initialized, "don't init multiple times" - if plat_name is None: - plat_name = get_platform() - # sanity check for platforms to prevent obscure errors later. - ok_plats = 'win32', 'win-amd64' - if plat_name not in ok_plats: - raise DistutilsPlatformError("--plat-name must be one of %s" % - (ok_plats,)) - - if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): - # Assume that the SDK set up everything alright; don't try to be - # smarter - self.cc = "cl.exe" - self.linker = "link.exe" - self.lib = "lib.exe" - self.rc = "rc.exe" - self.mc = "mc.exe" - else: - # On x86, 'vcvars32.bat amd64' creates an env that doesn't work; - # to cross compile, you use 'x86_amd64'. - # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross - # compile use 'x86' (ie, it runs the x86 compiler directly) - if plat_name == get_platform() or plat_name == 'win32': - # native build or cross-compile to win32 - plat_spec = PLAT_TO_VCVARS[plat_name] - else: - # cross compile from win32 -> some 64bit - plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ - PLAT_TO_VCVARS[plat_name] - - vc_env = query_vcvarsall(VERSION, plat_spec) - - self.__paths = vc_env['path'].split(os.pathsep) - os.environ['lib'] = vc_env['lib'] - os.environ['include'] = vc_env['include'] - - if len(self.__paths) == 0: - raise DistutilsPlatformError("Python was built with %s, " - "and extensions need to be built with the same " - "version of the compiler, but it isn't installed." - % self.__product) - - self.cc = self.find_exe("cl.exe") - self.linker = self.find_exe("link.exe") - self.lib = self.find_exe("lib.exe") - self.rc = self.find_exe("rc.exe") # resource compiler - self.mc = self.find_exe("mc.exe") # message compiler - #self.set_path_env_var('lib') - #self.set_path_env_var('include') - - # extend the MSVC path with the current path - try: - for p in os.environ['path'].split(';'): - self.__paths.append(p) - except KeyError: - pass - self.__paths = normalize_and_reduce_paths(self.__paths) - os.environ['path'] = ";".join(self.__paths) - - self.preprocess_options = None - if self.__arch == "x86": - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', - '/Z7', '/D_DEBUG'] - else: - # Win64 - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', - '/Z7', '/D_DEBUG'] - - self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] - if self.__version >= 7: - self.ldflags_shared_debug = [ - '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' - ] - self.ldflags_static = [ '/nologo'] - - self.initialized = True - - # -- Worker methods ------------------------------------------------ - - def object_filenames(self, - source_filenames, - strip_dir=0, - output_dir=''): - # Copied from ccompiler.py, extended to return .res as 'object'-file - # for .rc input file - if output_dir is None: output_dir = '' - obj_names = [] - for src_name in source_filenames: - (base, ext) = os.path.splitext (src_name) - base = os.path.splitdrive(base)[1] # Chop off the drive - base = base[os.path.isabs(base):] # If abs, chop off leading / - if ext not in self.src_extensions: - # Better to raise an exception instead of silently continuing - # and later complain about sources and targets having - # different lengths - raise CompileError ("Don't know how to compile %s" % src_name) - if strip_dir: - base = os.path.basename (base) - if ext in self._rc_extensions: - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - elif ext in self._mc_extensions: - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - else: - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) - return obj_names - - - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - if not self.initialized: - self.initialize() - compile_info = self._setup_compile(output_dir, macros, include_dirs, - sources, depends, extra_postargs) - macros, objects, extra_postargs, pp_opts, build = compile_info - - compile_opts = extra_preargs or [] - compile_opts.append ('/c') - if debug: - compile_opts.extend(self.compile_options_debug) - else: - compile_opts.extend(self.compile_options) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - if debug: - # pass the full pathname to MSVC in debug mode, - # this allows the debugger to find the source file - # without asking the user to browse for it - src = os.path.abspath(src) - - if ext in self._c_extensions: - input_opt = "/Tc" + src - elif ext in self._cpp_extensions: - input_opt = "/Tp" + src - elif ext in self._rc_extensions: - # compile .RC to .RES file - input_opt = src - output_opt = "/fo" + obj - try: - self.spawn([self.rc] + pp_opts + - [output_opt] + [input_opt]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue - elif ext in self._mc_extensions: - # Compile .MC to .RC file to .RES file. - # * '-h dir' specifies the directory for the - # generated include file - # * '-r dir' specifies the target directory of the - # generated RC file and the binary message resource - # it includes - # - # For now (since there are no options to change this), - # we use the source-directory for the include file and - # the build directory for the RC file and message - # resources. This works at least for win32all. - h_dir = os.path.dirname(src) - rc_dir = os.path.dirname(obj) - try: - # first compile .MC to .RC and .H file - self.spawn([self.mc] + - ['-h', h_dir, '-r', rc_dir] + [src]) - base, _ = os.path.splitext (os.path.basename (src)) - rc_file = os.path.join (rc_dir, base + '.rc') - # then compile .RC to .RES file - self.spawn([self.rc] + - ["/fo" + obj] + [rc_file]) - - except DistutilsExecError as msg: - raise CompileError(msg) - continue - else: - # how to handle this file? - raise CompileError("Don't know how to compile %s to %s" - % (src, obj)) - - output_opt = "/Fo" + obj - try: - self.spawn([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - - def create_static_lib(self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): - - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - output_filename = self.library_filename(output_libname, - output_dir=output_dir) - - if self._need_link(objects, output_filename): - lib_args = objects + ['/OUT:' + output_filename] - if debug: - pass # XXX what goes here? - try: - self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, - runtime_library_dirs) - (libraries, library_dirs, runtime_library_dirs) = fixed_args - - if runtime_library_dirs: - self.warn ("I don't know what to do with 'runtime_library_dirs': " - + str (runtime_library_dirs)) - - lib_opts = gen_lib_options(self, - library_dirs, runtime_library_dirs, - libraries) - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - if target_desc == CCompiler.EXECUTABLE: - if debug: - ldflags = self.ldflags_shared_debug[1:] - else: - ldflags = self.ldflags_shared[1:] - else: - if debug: - ldflags = self.ldflags_shared_debug - else: - ldflags = self.ldflags_shared - - export_opts = [] - for sym in (export_symbols or []): - export_opts.append("/EXPORT:" + sym) - - ld_args = (ldflags + lib_opts + export_opts + - objects + ['/OUT:' + output_filename]) - - # The MSVC linker generates .lib and .exp files, which cannot be - # suppressed by any linker switches. The .lib files may even be - # needed! Make sure they are generated in the temporary build - # directory. Since they have different names for debug and release - # builds, they can go into the same directory. - build_temp = os.path.dirname(objects[0]) - if export_symbols is not None: - (dll_name, dll_ext) = os.path.splitext( - os.path.basename(output_filename)) - implib_file = os.path.join( - build_temp, - self.library_filename(dll_name)) - ld_args.append ('/IMPLIB:' + implib_file) - - self.manifest_setup_ldargs(output_filename, build_temp, ld_args) - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - self.mkpath(os.path.dirname(output_filename)) - try: - self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - - # embed the manifest - # XXX - this is somewhat fragile - if mt.exe fails, distutils - # will still consider the DLL up-to-date, but it will not have a - # manifest. Maybe we should link to a temp file? OTOH, that - # implies a build environment error that shouldn't go undetected. - mfinfo = self.manifest_get_embed_info(target_desc, ld_args) - if mfinfo is not None: - mffilename, mfid = mfinfo - out_arg = '-outputresource:%s;%s' % (output_filename, mfid) - try: - self.spawn(['mt.exe', '-nologo', '-manifest', - mffilename, out_arg]) - except DistutilsExecError as msg: - raise LinkError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): - # If we need a manifest at all, an embedded manifest is recommended. - # See MSDN article titled - # "How to: Embed a Manifest Inside a C/C++ Application" - # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) - # Ask the linker to generate the manifest in the temp dir, so - # we can check it, and possibly embed it, later. - temp_manifest = os.path.join( - build_temp, - os.path.basename(output_filename) + ".manifest") - ld_args.append('/MANIFESTFILE:' + temp_manifest) - - def manifest_get_embed_info(self, target_desc, ld_args): - # If a manifest should be embedded, return a tuple of - # (manifest_filename, resource_id). Returns None if no manifest - # should be embedded. See http://bugs.python.org/issue7833 for why - # we want to avoid any manifest for extension modules if we can) - for arg in ld_args: - if arg.startswith("/MANIFESTFILE:"): - temp_manifest = arg.split(":", 1)[1] - break - else: - # no /MANIFESTFILE so nothing to do. - return None - if target_desc == CCompiler.EXECUTABLE: - # by default, executables always get the manifest with the - # CRT referenced. - mfid = 1 - else: - # Extension modules try and avoid any manifest if possible. - mfid = 2 - temp_manifest = self._remove_visual_c_ref(temp_manifest) - if temp_manifest is None: - return None - return temp_manifest, mfid - - def _remove_visual_c_ref(self, manifest_file): - try: - # Remove references to the Visual C runtime, so they will - # fall through to the Visual C dependency of Python.exe. - # This way, when installed for a restricted user (e.g. - # runtimes are not in WinSxS folder, but in Python's own - # folder), the runtimes do not need to be in every folder - # with .pyd's. - # Returns either the filename of the modified manifest or - # None if no manifest should be embedded. - manifest_f = open(manifest_file) - try: - manifest_buf = manifest_f.read() - finally: - manifest_f.close() - pattern = re.compile( - r"""|)""", - re.DOTALL) - manifest_buf = re.sub(pattern, "", manifest_buf) - pattern = r"\s*" - manifest_buf = re.sub(pattern, "", manifest_buf) - # Now see if any other assemblies are referenced - if not, we - # don't want a manifest embedded. - pattern = re.compile( - r"""|)""", re.DOTALL) - if re.search(pattern, manifest_buf) is None: - return None - - manifest_f = open(manifest_file, 'w') - try: - manifest_f.write(manifest_buf) - return manifest_file - finally: - manifest_f.close() - except OSError: - pass - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "/LIBPATH:" + dir - - def runtime_library_dir_option(self, dir): - raise DistutilsPlatformError( - "don't know how to set runtime library search path for MSVC++") - - def library_option(self, lib): - return self.library_filename(lib) - - - def find_library_file(self, dirs, lib, debug=0): - # Prefer a debugging library if found (and requested), but deal - # with it if we don't have one. - if debug: - try_names = [lib + "_d", lib] - else: - try_names = [lib] - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename (name)) - if os.path.exists(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None - - # Helper methods for using the MSVC registry settings - - def find_exe(self, exe): - """Return path to an MSVC executable program. - - Tries to find the program in several places: first, one of the - MSVC program search paths from the registry; next, the directories - in the PATH environment variable. If any of those work, return an - absolute path that is known to exist. If none of them work, just - return the original program name, 'exe'. - """ - for p in self.__paths: - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - - # didn't find it; try existing path - for p in os.environ['Path'].split(';'): - fn = os.path.join(os.path.abspath(p),exe) - if os.path.isfile(fn): - return fn - - return exe diff --git a/distutils/msvccompiler.py b/distutils/msvccompiler.py deleted file mode 100644 index d5857cb1..00000000 --- a/distutils/msvccompiler.py +++ /dev/null @@ -1,643 +0,0 @@ -"""distutils.msvccompiler - -Contains MSVCCompiler, an implementation of the abstract CCompiler class -for the Microsoft Visual Studio. -""" - -# Written by Perry Stoll -# hacked by Robin Becker and Thomas Heller to do a better job of -# finding DevStudio (through the registry) - -import sys, os -from distutils.errors import \ - DistutilsExecError, DistutilsPlatformError, \ - CompileError, LibError, LinkError -from distutils.ccompiler import \ - CCompiler, gen_lib_options -from distutils import log - -_can_read_reg = False -try: - import winreg - - _can_read_reg = True - hkey_mod = winreg - - RegOpenKeyEx = winreg.OpenKeyEx - RegEnumKey = winreg.EnumKey - RegEnumValue = winreg.EnumValue - RegError = winreg.error - -except ImportError: - try: - import win32api - import win32con - _can_read_reg = True - hkey_mod = win32con - - RegOpenKeyEx = win32api.RegOpenKeyEx - RegEnumKey = win32api.RegEnumKey - RegEnumValue = win32api.RegEnumValue - RegError = win32api.error - except ImportError: - log.info("Warning: Can't read registry to find the " - "necessary compiler setting\n" - "Make sure that Python modules winreg, " - "win32api or win32con are installed.") - pass - -if _can_read_reg: - HKEYS = (hkey_mod.HKEY_USERS, - hkey_mod.HKEY_CURRENT_USER, - hkey_mod.HKEY_LOCAL_MACHINE, - hkey_mod.HKEY_CLASSES_ROOT) - -def read_keys(base, key): - """Return list of registry keys.""" - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - L = [] - i = 0 - while True: - try: - k = RegEnumKey(handle, i) - except RegError: - break - L.append(k) - i += 1 - return L - -def read_values(base, key): - """Return dict of registry keys and values. - - All names are converted to lowercase. - """ - try: - handle = RegOpenKeyEx(base, key) - except RegError: - return None - d = {} - i = 0 - while True: - try: - name, value, type = RegEnumValue(handle, i) - except RegError: - break - name = name.lower() - d[convert_mbcs(name)] = convert_mbcs(value) - i += 1 - return d - -def convert_mbcs(s): - dec = getattr(s, "decode", None) - if dec is not None: - try: - s = dec("mbcs") - except UnicodeError: - pass - return s - -class MacroExpander: - def __init__(self, version): - self.macros = {} - self.load_macros(version) - - def set_macro(self, macro, path, key): - for base in HKEYS: - d = read_values(base, path) - if d: - self.macros["$(%s)" % macro] = d[key] - break - - def load_macros(self, version): - vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version - self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") - self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") - net = r"Software\Microsoft\.NETFramework" - self.set_macro("FrameworkDir", net, "installroot") - try: - if version > 7.0: - self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") - else: - self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") - except KeyError as exc: # - raise DistutilsPlatformError( - """Python was built with Visual Studio 2003; -extensions must be built with a compiler than can generate compatible binaries. -Visual Studio 2003 was not found on this system. If you have Cygwin installed, -you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") - - p = r"Software\Microsoft\NET Framework Setup\Product" - for base in HKEYS: - try: - h = RegOpenKeyEx(base, p) - except RegError: - continue - key = RegEnumKey(h, 0) - d = read_values(base, r"%s\%s" % (p, key)) - self.macros["$(FrameworkVersion)"] = d["version"] - - def sub(self, s): - for k, v in self.macros.items(): - s = s.replace(k, v) - return s - -def get_build_version(): - """Return the version of MSVC that was used to build Python. - - For Python 2.3 and up, the version number is included in - sys.version. For earlier versions, assume the compiler is MSVC 6. - """ - prefix = "MSC v." - i = sys.version.find(prefix) - if i == -1: - return 6 - i = i + len(prefix) - s, rest = sys.version[i:].split(" ", 1) - majorVersion = int(s[:-2]) - 6 - if majorVersion >= 13: - # v13 was skipped and should be v14 - majorVersion += 1 - minorVersion = int(s[2:3]) / 10.0 - # I don't think paths are affected by minor version in version 6 - if majorVersion == 6: - minorVersion = 0 - if majorVersion >= 6: - return majorVersion + minorVersion - # else we don't know what version of the compiler this is - return None - -def get_build_architecture(): - """Return the processor architecture. - - Possible results are "Intel" or "AMD64". - """ - - prefix = " bit (" - i = sys.version.find(prefix) - if i == -1: - return "Intel" - j = sys.version.find(")", i) - return sys.version[i+len(prefix):j] - -def normalize_and_reduce_paths(paths): - """Return a list of normalized paths with duplicates removed. - - The current order of paths is maintained. - """ - # Paths are normalized so things like: /a and /a/ aren't both preserved. - reduced_paths = [] - for p in paths: - np = os.path.normpath(p) - # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. - if np not in reduced_paths: - reduced_paths.append(np) - return reduced_paths - - -class MSVCCompiler(CCompiler) : - """Concrete class that implements an interface to Microsoft Visual C++, - as defined by the CCompiler abstract class.""" - - compiler_type = 'msvc' - - # Just set this so CCompiler's constructor doesn't barf. We currently - # don't use the 'set_executables()' bureaucracy provided by CCompiler, - # as it really isn't necessary for this sort of single-compiler class. - # Would be nice to have a consistent interface with UnixCCompiler, - # though, so it's worth thinking about. - executables = {} - - # Private class data (need to distinguish C from C++ source for compiler) - _c_extensions = ['.c'] - _cpp_extensions = ['.cc', '.cpp', '.cxx'] - _rc_extensions = ['.rc'] - _mc_extensions = ['.mc'] - - # Needed for the filename generation methods provided by the - # base class, CCompiler. - src_extensions = (_c_extensions + _cpp_extensions + - _rc_extensions + _mc_extensions) - res_extension = '.res' - obj_extension = '.obj' - static_lib_extension = '.lib' - shared_lib_extension = '.dll' - static_lib_format = shared_lib_format = '%s%s' - exe_extension = '.exe' - - def __init__(self, verbose=0, dry_run=0, force=0): - CCompiler.__init__ (self, verbose, dry_run, force) - self.__version = get_build_version() - self.__arch = get_build_architecture() - if self.__arch == "Intel": - # x86 - if self.__version >= 7: - self.__root = r"Software\Microsoft\VisualStudio" - self.__macros = MacroExpander(self.__version) - else: - self.__root = r"Software\Microsoft\Devstudio" - self.__product = "Visual Studio version %s" % self.__version - else: - # Win64. Assume this was built with the platform SDK - self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) - - self.initialized = False - - def initialize(self): - self.__paths = [] - if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): - # Assume that the SDK set up everything alright; don't try to be - # smarter - self.cc = "cl.exe" - self.linker = "link.exe" - self.lib = "lib.exe" - self.rc = "rc.exe" - self.mc = "mc.exe" - else: - self.__paths = self.get_msvc_paths("path") - - if len(self.__paths) == 0: - raise DistutilsPlatformError("Python was built with %s, " - "and extensions need to be built with the same " - "version of the compiler, but it isn't installed." - % self.__product) - - self.cc = self.find_exe("cl.exe") - self.linker = self.find_exe("link.exe") - self.lib = self.find_exe("lib.exe") - self.rc = self.find_exe("rc.exe") # resource compiler - self.mc = self.find_exe("mc.exe") # message compiler - self.set_path_env_var('lib') - self.set_path_env_var('include') - - # extend the MSVC path with the current path - try: - for p in os.environ['path'].split(';'): - self.__paths.append(p) - except KeyError: - pass - self.__paths = normalize_and_reduce_paths(self.__paths) - os.environ['path'] = ";".join(self.__paths) - - self.preprocess_options = None - if self.__arch == "Intel": - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', - '/Z7', '/D_DEBUG'] - else: - # Win64 - self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , - '/DNDEBUG'] - self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', - '/Z7', '/D_DEBUG'] - - self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] - if self.__version >= 7: - self.ldflags_shared_debug = [ - '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' - ] - else: - self.ldflags_shared_debug = [ - '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG' - ] - self.ldflags_static = [ '/nologo'] - - self.initialized = True - - # -- Worker methods ------------------------------------------------ - - def object_filenames(self, - source_filenames, - strip_dir=0, - output_dir=''): - # Copied from ccompiler.py, extended to return .res as 'object'-file - # for .rc input file - if output_dir is None: output_dir = '' - obj_names = [] - for src_name in source_filenames: - (base, ext) = os.path.splitext (src_name) - base = os.path.splitdrive(base)[1] # Chop off the drive - base = base[os.path.isabs(base):] # If abs, chop off leading / - if ext not in self.src_extensions: - # Better to raise an exception instead of silently continuing - # and later complain about sources and targets having - # different lengths - raise CompileError ("Don't know how to compile %s" % src_name) - if strip_dir: - base = os.path.basename (base) - if ext in self._rc_extensions: - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - elif ext in self._mc_extensions: - obj_names.append (os.path.join (output_dir, - base + self.res_extension)) - else: - obj_names.append (os.path.join (output_dir, - base + self.obj_extension)) - return obj_names - - - def compile(self, sources, - output_dir=None, macros=None, include_dirs=None, debug=0, - extra_preargs=None, extra_postargs=None, depends=None): - - if not self.initialized: - self.initialize() - compile_info = self._setup_compile(output_dir, macros, include_dirs, - sources, depends, extra_postargs) - macros, objects, extra_postargs, pp_opts, build = compile_info - - compile_opts = extra_preargs or [] - compile_opts.append ('/c') - if debug: - compile_opts.extend(self.compile_options_debug) - else: - compile_opts.extend(self.compile_options) - - for obj in objects: - try: - src, ext = build[obj] - except KeyError: - continue - if debug: - # pass the full pathname to MSVC in debug mode, - # this allows the debugger to find the source file - # without asking the user to browse for it - src = os.path.abspath(src) - - if ext in self._c_extensions: - input_opt = "/Tc" + src - elif ext in self._cpp_extensions: - input_opt = "/Tp" + src - elif ext in self._rc_extensions: - # compile .RC to .RES file - input_opt = src - output_opt = "/fo" + obj - try: - self.spawn([self.rc] + pp_opts + - [output_opt] + [input_opt]) - except DistutilsExecError as msg: - raise CompileError(msg) - continue - elif ext in self._mc_extensions: - # Compile .MC to .RC file to .RES file. - # * '-h dir' specifies the directory for the - # generated include file - # * '-r dir' specifies the target directory of the - # generated RC file and the binary message resource - # it includes - # - # For now (since there are no options to change this), - # we use the source-directory for the include file and - # the build directory for the RC file and message - # resources. This works at least for win32all. - h_dir = os.path.dirname(src) - rc_dir = os.path.dirname(obj) - try: - # first compile .MC to .RC and .H file - self.spawn([self.mc] + - ['-h', h_dir, '-r', rc_dir] + [src]) - base, _ = os.path.splitext (os.path.basename (src)) - rc_file = os.path.join (rc_dir, base + '.rc') - # then compile .RC to .RES file - self.spawn([self.rc] + - ["/fo" + obj] + [rc_file]) - - except DistutilsExecError as msg: - raise CompileError(msg) - continue - else: - # how to handle this file? - raise CompileError("Don't know how to compile %s to %s" - % (src, obj)) - - output_opt = "/Fo" + obj - try: - self.spawn([self.cc] + compile_opts + pp_opts + - [input_opt, output_opt] + - extra_postargs) - except DistutilsExecError as msg: - raise CompileError(msg) - - return objects - - - def create_static_lib(self, - objects, - output_libname, - output_dir=None, - debug=0, - target_lang=None): - - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - output_filename = self.library_filename(output_libname, - output_dir=output_dir) - - if self._need_link(objects, output_filename): - lib_args = objects + ['/OUT:' + output_filename] - if debug: - pass # XXX what goes here? - try: - self.spawn([self.lib] + lib_args) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - - def link(self, - target_desc, - objects, - output_filename, - output_dir=None, - libraries=None, - library_dirs=None, - runtime_library_dirs=None, - export_symbols=None, - debug=0, - extra_preargs=None, - extra_postargs=None, - build_temp=None, - target_lang=None): - - if not self.initialized: - self.initialize() - (objects, output_dir) = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, - runtime_library_dirs) - (libraries, library_dirs, runtime_library_dirs) = fixed_args - - if runtime_library_dirs: - self.warn ("I don't know what to do with 'runtime_library_dirs': " - + str (runtime_library_dirs)) - - lib_opts = gen_lib_options(self, - library_dirs, runtime_library_dirs, - libraries) - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - if target_desc == CCompiler.EXECUTABLE: - if debug: - ldflags = self.ldflags_shared_debug[1:] - else: - ldflags = self.ldflags_shared[1:] - else: - if debug: - ldflags = self.ldflags_shared_debug - else: - ldflags = self.ldflags_shared - - export_opts = [] - for sym in (export_symbols or []): - export_opts.append("/EXPORT:" + sym) - - ld_args = (ldflags + lib_opts + export_opts + - objects + ['/OUT:' + output_filename]) - - # The MSVC linker generates .lib and .exp files, which cannot be - # suppressed by any linker switches. The .lib files may even be - # needed! Make sure they are generated in the temporary build - # directory. Since they have different names for debug and release - # builds, they can go into the same directory. - if export_symbols is not None: - (dll_name, dll_ext) = os.path.splitext( - os.path.basename(output_filename)) - implib_file = os.path.join( - os.path.dirname(objects[0]), - self.library_filename(dll_name)) - ld_args.append ('/IMPLIB:' + implib_file) - - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - - self.mkpath(os.path.dirname(output_filename)) - try: - self.spawn([self.linker] + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - - else: - log.debug("skipping %s (up-to-date)", output_filename) - - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "/LIBPATH:" + dir - - def runtime_library_dir_option(self, dir): - raise DistutilsPlatformError( - "don't know how to set runtime library search path for MSVC++") - - def library_option(self, lib): - return self.library_filename(lib) - - - def find_library_file(self, dirs, lib, debug=0): - # Prefer a debugging library if found (and requested), but deal - # with it if we don't have one. - if debug: - try_names = [lib + "_d", lib] - else: - try_names = [lib] - for dir in dirs: - for name in try_names: - libfile = os.path.join(dir, self.library_filename (name)) - if os.path.exists(libfile): - return libfile - else: - # Oops, didn't find it in *any* of 'dirs' - return None - - # Helper methods for using the MSVC registry settings - - def find_exe(self, exe): - """Return path to an MSVC executable program. - - Tries to find the program in several places: first, one of the - MSVC program search paths from the registry; next, the directories - in the PATH environment variable. If any of those work, return an - absolute path that is known to exist. If none of them work, just - return the original program name, 'exe'. - """ - for p in self.__paths: - fn = os.path.join(os.path.abspath(p), exe) - if os.path.isfile(fn): - return fn - - # didn't find it; try existing path - for p in os.environ['Path'].split(';'): - fn = os.path.join(os.path.abspath(p),exe) - if os.path.isfile(fn): - return fn - - return exe - - def get_msvc_paths(self, path, platform='x86'): - """Get a list of devstudio directories (include, lib or path). - - Return a list of strings. The list will be empty if unable to - access the registry or appropriate registry keys not found. - """ - if not _can_read_reg: - return [] - - path = path + " dirs" - if self.__version >= 7: - key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" - % (self.__root, self.__version)) - else: - key = (r"%s\6.0\Build System\Components\Platforms" - r"\Win32 (%s)\Directories" % (self.__root, platform)) - - for base in HKEYS: - d = read_values(base, key) - if d: - if self.__version >= 7: - return self.__macros.sub(d[path]).split(";") - else: - return d[path].split(";") - # MSVC 6 seems to create the registry entries we need only when - # the GUI is run. - if self.__version == 6: - for base in HKEYS: - if read_values(base, r"%s\6.0" % self.__root) is not None: - self.warn("It seems you have Visual Studio 6 installed, " - "but the expected registry settings are not present.\n" - "You must at least run the Visual Studio GUI once " - "so that these entries are created.") - break - return [] - - def set_path_env_var(self, name): - """Set environment variable 'name' to an MSVC path type value. - - This is equivalent to a SET command prior to execution of spawned - commands. - """ - - if name == "lib": - p = self.get_msvc_paths("library") - else: - p = self.get_msvc_paths(name) - if p: - os.environ[name] = ';'.join(p) - - -if get_build_version() >= 8.0: - log.debug("Importing new compiler from distutils.msvc9compiler") - OldMSVCCompiler = MSVCCompiler - from distutils.msvc9compiler import MSVCCompiler - # get_build_architecture not really relevant now we support cross-compile - from distutils.msvc9compiler import MacroExpander diff --git a/distutils/spawn.py b/distutils/spawn.py deleted file mode 100644 index aad277b0..00000000 --- a/distutils/spawn.py +++ /dev/null @@ -1,119 +0,0 @@ -"""distutils.spawn - -Provides the 'spawn()' function, a front-end to various platform- -specific functions for launching another program in a sub-process. -Also provides the 'find_executable()' to search the path for a given -executable name. -""" - -import sys -import os -import subprocess - -from distutils.errors import DistutilsPlatformError, DistutilsExecError -from distutils.debug import DEBUG -from distutils import log - - -if sys.platform == 'darwin': - _cfg_target = None - _cfg_target_split = None - - -def spawn(cmd, search_path=1, verbose=0, dry_run=0): - """Run another program, specified as a command list 'cmd', in a new process. - - 'cmd' is just the argument list for the new process, ie. - cmd[0] is the program to run and cmd[1:] are the rest of its arguments. - There is no way to run a program with a name different from that of its - executable. - - If 'search_path' is true (the default), the system's executable - search path will be used to find the program; otherwise, cmd[0] - must be the exact path to the executable. If 'dry_run' is true, - the command will not actually be run. - - Raise DistutilsExecError if running the program fails in any way; just - return on success. - """ - # cmd is documented as a list, but just in case some code passes a tuple - # in, protect our %-formatting code against horrible death - cmd = list(cmd) - - log.info(' '.join(cmd)) - if dry_run: - return - - if search_path: - executable = find_executable(cmd[0]) - if executable is not None: - cmd[0] = executable - - env = None - if sys.platform == 'darwin': - global _cfg_target, _cfg_target_split - if _cfg_target is None: - from distutils import sysconfig - _cfg_target = sysconfig.get_config_var( - 'MACOSX_DEPLOYMENT_TARGET') or '' - if _cfg_target: - _cfg_target_split = [int(x) for x in _cfg_target.split('.')] - if _cfg_target: - # ensure that the deployment target of build process is not less - # than that used when the interpreter was built. This ensures - # extension modules are built with correct compatibility values - cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target) - if _cfg_target_split > [int(x) for x in cur_target.split('.')]: - my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: ' - 'now "%s" but "%s" during configure' - % (cur_target, _cfg_target)) - raise DistutilsPlatformError(my_msg) - env = dict(os.environ, - MACOSX_DEPLOYMENT_TARGET=cur_target) - - proc = subprocess.Popen(cmd, env=env) - proc.wait() - exitcode = proc.returncode - - if exitcode: - if not DEBUG: - cmd = cmd[0] - raise DistutilsExecError( - "command %r failed with exit code %s" % (cmd, exitcode)) - - -def find_executable(executable, path=None): - """Tries to find 'executable' in the directories listed in 'path'. - - A string listing directories separated by 'os.pathsep'; defaults to - os.environ['PATH']. Returns the complete filename or None if not found. - """ - _, ext = os.path.splitext(executable) - if (sys.platform == 'win32') and (ext != '.exe'): - executable = executable + '.exe' - - if os.path.isfile(executable): - return executable - - if path is None: - path = os.environ.get('PATH', None) - if path is None: - try: - path = os.confstr("CS_PATH") - except (AttributeError, ValueError): - # os.confstr() or CS_PATH is not available - path = os.defpath - # bpo-35755: Don't use os.defpath if the PATH environment variable is - # set to an empty string - - # PATH='' doesn't match, whereas PATH=':' looks in the current directory - if not path: - return None - - paths = path.split(os.pathsep) - for p in paths: - f = os.path.join(p, executable) - if os.path.isfile(f): - # the file exists, we have a shot at spawn working - return f - return None diff --git a/distutils/sysconfig.py b/distutils/sysconfig.py deleted file mode 100644 index 879b6981..00000000 --- a/distutils/sysconfig.py +++ /dev/null @@ -1,573 +0,0 @@ -"""Provide access to Python's configuration information. The specific -configuration variables available depend heavily on the platform and -configuration. The values may be retrieved using -get_config_var(name), and the list of variables is available via -get_config_vars().keys(). Additional convenience functions are also -available. - -Written by: Fred L. Drake, Jr. -Email: -""" - -import _imp -import os -import re -import sys - -from .errors import DistutilsPlatformError - -IS_PYPY = '__pypy__' in sys.builtin_module_names - -# These are needed in a couple of spots, so just compute them once. -PREFIX = os.path.normpath(sys.prefix) -EXEC_PREFIX = os.path.normpath(sys.exec_prefix) -BASE_PREFIX = os.path.normpath(sys.base_prefix) -BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) - -# Path to the base directory of the project. On Windows the binary may -# live in project/PCbuild/win32 or project/PCbuild/amd64. -# set for cross builds -if "_PYTHON_PROJECT_BASE" in os.environ: - project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) -else: - if sys.executable: - project_base = os.path.dirname(os.path.abspath(sys.executable)) - else: - # sys.executable can be empty if argv[0] has been changed and Python is - # unable to retrieve the real program name - project_base = os.getcwd() - - -# python_build: (Boolean) if true, we're either building Python or -# building an extension with an un-installed Python, so we use -# different (hard-wired) directories. -def _is_python_source_dir(d): - for fn in ("Setup", "Setup.local"): - if os.path.isfile(os.path.join(d, "Modules", fn)): - return True - return False - -_sys_home = getattr(sys, '_home', None) - -if os.name == 'nt': - def _fix_pcbuild(d): - if d and os.path.normcase(d).startswith( - os.path.normcase(os.path.join(PREFIX, "PCbuild"))): - return PREFIX - return d - project_base = _fix_pcbuild(project_base) - _sys_home = _fix_pcbuild(_sys_home) - -def _python_build(): - if _sys_home: - return _is_python_source_dir(_sys_home) - return _is_python_source_dir(project_base) - -python_build = _python_build() - - -# Calculate the build qualifier flags if they are defined. Adding the flags -# to the include and lib directories only makes sense for an installation, not -# an in-source build. -build_flags = '' -try: - if not python_build: - build_flags = sys.abiflags -except AttributeError: - # It's not a configure-based build, so the sys module doesn't have - # this attribute, which is fine. - pass - -def get_python_version(): - """Return a string containing the major and minor Python version, - leaving off the patchlevel. Sample return values could be '1.5' - or '2.2'. - """ - return '%d.%d' % sys.version_info[:2] - - -def get_python_inc(plat_specific=0, prefix=None): - """Return the directory containing installed Python header files. - - If 'plat_specific' is false (the default), this is the path to the - non-platform-specific header files, i.e. Python.h and so on; - otherwise, this is the path to platform-specific header files - (namely pyconfig.h). - - If 'prefix' is supplied, use it instead of sys.base_prefix or - sys.base_exec_prefix -- i.e., ignore 'plat_specific'. - """ - if prefix is None: - prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX - if IS_PYPY: - return os.path.join(prefix, 'include') - elif os.name == "posix": - if python_build: - # Assume the executable is in the build directory. The - # pyconfig.h file should be in the same directory. Since - # the build directory may not be the source directory, we - # must use "srcdir" from the makefile to find the "Include" - # directory. - if plat_specific: - return _sys_home or project_base - else: - incdir = os.path.join(get_config_var('srcdir'), 'Include') - return os.path.normpath(incdir) - python_dir = 'python' + get_python_version() + build_flags - return os.path.join(prefix, "include", python_dir) - elif os.name == "nt": - if python_build: - # Include both the include and PC dir to ensure we can find - # pyconfig.h - return (os.path.join(prefix, "include") + os.path.pathsep + - os.path.join(prefix, "PC")) - return os.path.join(prefix, "include") - else: - raise DistutilsPlatformError( - "I don't know where Python installs its C header files " - "on platform '%s'" % os.name) - - -def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): - """Return the directory containing the Python library (standard or - site additions). - - If 'plat_specific' is true, return the directory containing - platform-specific modules, i.e. any module from a non-pure-Python - module distribution; otherwise, return the platform-shared library - directory. If 'standard_lib' is true, return the directory - containing standard Python library modules; otherwise, return the - directory for site-specific modules. - - If 'prefix' is supplied, use it instead of sys.base_prefix or - sys.base_exec_prefix -- i.e., ignore 'plat_specific'. - """ - if IS_PYPY: - # PyPy-specific schema - if prefix is None: - prefix = PREFIX - if standard_lib: - return os.path.join(prefix, "lib-python", sys.version[0]) - return os.path.join(prefix, 'site-packages') - - if prefix is None: - if standard_lib: - prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX - else: - prefix = plat_specific and EXEC_PREFIX or PREFIX - - if os.name == "posix": - if plat_specific or standard_lib: - # Platform-specific modules (any module from a non-pure-Python - # module distribution) or standard Python library modules. - libdir = getattr(sys, "platlibdir", "lib") - else: - # Pure Python - libdir = "lib" - libpython = os.path.join(prefix, libdir, - "python" + get_python_version()) - if standard_lib: - return libpython - else: - return os.path.join(libpython, "site-packages") - elif os.name == "nt": - if standard_lib: - return os.path.join(prefix, "Lib") - else: - return os.path.join(prefix, "Lib", "site-packages") - else: - raise DistutilsPlatformError( - "I don't know where Python installs its library " - "on platform '%s'" % os.name) - - - -def customize_compiler(compiler): - """Do any platform-specific customization of a CCompiler instance. - - Mainly needed on Unix, so we can plug in the information that - varies across Unices and is stored in Python's Makefile. - """ - if compiler.compiler_type == "unix": - if sys.platform == "darwin": - # Perform first-time customization of compiler-related - # config vars on OS X now that we know we need a compiler. - # This is primarily to support Pythons from binary - # installers. The kind and paths to build tools on - # the user system may vary significantly from the system - # that Python itself was built on. Also the user OS - # version and build tools may not support the same set - # of CPU architectures for universal builds. - global _config_vars - # Use get_config_var() to ensure _config_vars is initialized. - if not get_config_var('CUSTOMIZED_OSX_COMPILER'): - import _osx_support - _osx_support.customize_compiler(_config_vars) - _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' - - (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ - get_config_vars('CC', 'CXX', 'CFLAGS', - 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') - - if 'CC' in os.environ: - newcc = os.environ['CC'] - if (sys.platform == 'darwin' - and 'LDSHARED' not in os.environ - and ldshared.startswith(cc)): - # On OS X, if CC is overridden, use that as the default - # command for LDSHARED as well - ldshared = newcc + ldshared[len(cc):] - cc = newcc - if 'CXX' in os.environ: - cxx = os.environ['CXX'] - if 'LDSHARED' in os.environ: - ldshared = os.environ['LDSHARED'] - if 'CPP' in os.environ: - cpp = os.environ['CPP'] - else: - cpp = cc + " -E" # not always - if 'LDFLAGS' in os.environ: - ldshared = ldshared + ' ' + os.environ['LDFLAGS'] - if 'CFLAGS' in os.environ: - cflags = cflags + ' ' + os.environ['CFLAGS'] - ldshared = ldshared + ' ' + os.environ['CFLAGS'] - if 'CPPFLAGS' in os.environ: - cpp = cpp + ' ' + os.environ['CPPFLAGS'] - cflags = cflags + ' ' + os.environ['CPPFLAGS'] - ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] - if 'AR' in os.environ: - ar = os.environ['AR'] - if 'ARFLAGS' in os.environ: - archiver = ar + ' ' + os.environ['ARFLAGS'] - else: - archiver = ar + ' ' + ar_flags - - cc_cmd = cc + ' ' + cflags - compiler.set_executables( - preprocessor=cpp, - compiler=cc_cmd, - compiler_so=cc_cmd + ' ' + ccshared, - compiler_cxx=cxx, - linker_so=ldshared, - linker_exe=cc, - archiver=archiver) - - compiler.shared_lib_extension = shlib_suffix - - -def get_config_h_filename(): - """Return full pathname of installed pyconfig.h file.""" - if python_build: - if os.name == "nt": - inc_dir = os.path.join(_sys_home or project_base, "PC") - else: - inc_dir = _sys_home or project_base - else: - inc_dir = get_python_inc(plat_specific=1) - - return os.path.join(inc_dir, 'pyconfig.h') - - -def get_makefile_filename(): - """Return full pathname of installed Makefile from the Python build.""" - if python_build: - return os.path.join(_sys_home or project_base, "Makefile") - lib_dir = get_python_lib(plat_specific=0, standard_lib=1) - config_file = 'config-{}{}'.format(get_python_version(), build_flags) - if hasattr(sys.implementation, '_multiarch'): - config_file += '-%s' % sys.implementation._multiarch - return os.path.join(lib_dir, config_file, 'Makefile') - - -def parse_config_h(fp, g=None): - """Parse a config.h-style file. - - A dictionary containing name/value pairs is returned. If an - optional dictionary is passed in as the second argument, it is - used instead of a new dictionary. - """ - if g is None: - g = {} - define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") - undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") - # - while True: - line = fp.readline() - if not line: - break - m = define_rx.match(line) - if m: - n, v = m.group(1, 2) - try: v = int(v) - except ValueError: pass - g[n] = v - else: - m = undef_rx.match(line) - if m: - g[m.group(1)] = 0 - return g - - -# Regexes needed for parsing Makefile (and similar syntaxes, -# like old-style Setup files). -_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") -_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") -_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") - -def parse_makefile(fn, g=None): - """Parse a Makefile-style file. - - A dictionary containing name/value pairs is returned. If an - optional dictionary is passed in as the second argument, it is - used instead of a new dictionary. - """ - from distutils.text_file import TextFile - fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape") - - if g is None: - g = {} - done = {} - notdone = {} - - while True: - line = fp.readline() - if line is None: # eof - break - m = _variable_rx.match(line) - if m: - n, v = m.group(1, 2) - v = v.strip() - # `$$' is a literal `$' in make - tmpv = v.replace('$$', '') - - if "$" in tmpv: - notdone[n] = v - else: - try: - v = int(v) - except ValueError: - # insert literal `$' - done[n] = v.replace('$$', '$') - else: - done[n] = v - - # Variables with a 'PY_' prefix in the makefile. These need to - # be made available without that prefix through sysconfig. - # Special care is needed to ensure that variable expansion works, even - # if the expansion uses the name without a prefix. - renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') - - # do variable interpolation here - while notdone: - for name in list(notdone): - value = notdone[name] - m = _findvar1_rx.search(value) or _findvar2_rx.search(value) - if m: - n = m.group(1) - found = True - if n in done: - item = str(done[n]) - elif n in notdone: - # get it on a subsequent round - found = False - elif n in os.environ: - # do it like make: fall back to environment - item = os.environ[n] - - elif n in renamed_variables: - if name.startswith('PY_') and name[3:] in renamed_variables: - item = "" - - elif 'PY_' + n in notdone: - found = False - - else: - item = str(done['PY_' + n]) - else: - done[n] = item = "" - if found: - after = value[m.end():] - value = value[:m.start()] + item + after - if "$" in after: - notdone[name] = value - else: - try: value = int(value) - except ValueError: - done[name] = value.strip() - else: - done[name] = value - del notdone[name] - - if name.startswith('PY_') \ - and name[3:] in renamed_variables: - - name = name[3:] - if name not in done: - done[name] = value - else: - # bogus variable reference; just drop it since we can't deal - del notdone[name] - - fp.close() - - # strip spurious spaces - for k, v in done.items(): - if isinstance(v, str): - done[k] = v.strip() - - # save the results in the global dictionary - g.update(done) - return g - - -def expand_makefile_vars(s, vars): - """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in - 'string' according to 'vars' (a dictionary mapping variable names to - values). Variables not present in 'vars' are silently expanded to the - empty string. The variable values in 'vars' should not contain further - variable expansions; if 'vars' is the output of 'parse_makefile()', - you're fine. Returns a variable-expanded version of 's'. - """ - - # This algorithm does multiple expansion, so if vars['foo'] contains - # "${bar}", it will expand ${foo} to ${bar}, and then expand - # ${bar}... and so forth. This is fine as long as 'vars' comes from - # 'parse_makefile()', which takes care of such expansions eagerly, - # according to make's variable expansion semantics. - - while True: - m = _findvar1_rx.search(s) or _findvar2_rx.search(s) - if m: - (beg, end) = m.span() - s = s[0:beg] + vars.get(m.group(1)) + s[end:] - else: - break - return s - - -_config_vars = None - -def _init_posix(): - """Initialize the module as appropriate for POSIX systems.""" - # _sysconfigdata is generated at build time, see the sysconfig module - name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME', - '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( - abi=sys.abiflags, - platform=sys.platform, - multiarch=getattr(sys.implementation, '_multiarch', ''), - )) - try: - _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) - except ImportError: - # Python 3.5 and pypy 7.3.1 - _temp = __import__( - '_sysconfigdata', globals(), locals(), ['build_time_vars'], 0) - build_time_vars = _temp.build_time_vars - global _config_vars - _config_vars = {} - _config_vars.update(build_time_vars) - - -def _init_nt(): - """Initialize the module as appropriate for NT""" - g = {} - # set basic install directories - g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) - g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) - - # XXX hmmm.. a normal install puts include files here - g['INCLUDEPY'] = get_python_inc(plat_specific=0) - - g['EXT_SUFFIX'] = _imp.extension_suffixes()[0] - g['EXE'] = ".exe" - g['VERSION'] = get_python_version().replace(".", "") - g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) - - global _config_vars - _config_vars = g - - -def get_config_vars(*args): - """With no arguments, return a dictionary of all configuration - variables relevant for the current platform. Generally this includes - everything needed to build extensions and install both pure modules and - extensions. On Unix, this means every variable defined in Python's - installed Makefile; on Windows it's a much smaller set. - - With arguments, return a list of values that result from looking up - each argument in the configuration variable dictionary. - """ - global _config_vars - if _config_vars is None: - func = globals().get("_init_" + os.name) - if func: - func() - else: - _config_vars = {} - - # Normalized versions of prefix and exec_prefix are handy to have; - # in fact, these are the standard versions used most places in the - # Distutils. - _config_vars['prefix'] = PREFIX - _config_vars['exec_prefix'] = EXEC_PREFIX - - if not IS_PYPY: - # For backward compatibility, see issue19555 - SO = _config_vars.get('EXT_SUFFIX') - if SO is not None: - _config_vars['SO'] = SO - - # Always convert srcdir to an absolute path - srcdir = _config_vars.get('srcdir', project_base) - if os.name == 'posix': - if python_build: - # If srcdir is a relative path (typically '.' or '..') - # then it should be interpreted relative to the directory - # containing Makefile. - base = os.path.dirname(get_makefile_filename()) - srcdir = os.path.join(base, srcdir) - else: - # srcdir is not meaningful since the installation is - # spread about the filesystem. We choose the - # directory containing the Makefile since we know it - # exists. - srcdir = os.path.dirname(get_makefile_filename()) - _config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir)) - - # Convert srcdir into an absolute path if it appears necessary. - # Normally it is relative to the build directory. However, during - # testing, for example, we might be running a non-installed python - # from a different directory. - if python_build and os.name == "posix": - base = project_base - if (not os.path.isabs(_config_vars['srcdir']) and - base != os.getcwd()): - # srcdir is relative and we are not in the same directory - # as the executable. Assume executable is in the build - # directory and make srcdir absolute. - srcdir = os.path.join(base, _config_vars['srcdir']) - _config_vars['srcdir'] = os.path.normpath(srcdir) - - # OS X platforms require special customization to handle - # multi-architecture, multi-os-version installers - if sys.platform == 'darwin': - import _osx_support - _osx_support.customize_config_vars(_config_vars) - - if args: - vals = [] - for name in args: - vals.append(_config_vars.get(name)) - return vals - else: - return _config_vars - -def get_config_var(name): - """Return the value of a single variable using the dictionary - returned by 'get_config_vars()'. Equivalent to - get_config_vars().get(name) - """ - if name == 'SO': - import warnings - warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2) - return get_config_vars().get(name) diff --git a/distutils/tests/Setup.sample b/distutils/tests/Setup.sample deleted file mode 100644 index 36c4290d..00000000 --- a/distutils/tests/Setup.sample +++ /dev/null @@ -1,67 +0,0 @@ -# Setup file from the pygame project - -#--StartConfig -SDL = -I/usr/include/SDL -D_REENTRANT -lSDL -FONT = -lSDL_ttf -IMAGE = -lSDL_image -MIXER = -lSDL_mixer -SMPEG = -lsmpeg -PNG = -lpng -JPEG = -ljpeg -SCRAP = -lX11 -PORTMIDI = -lportmidi -PORTTIME = -lporttime -#--EndConfig - -#DEBUG = -C-W -C-Wall -DEBUG = - -#the following modules are optional. you will want to compile -#everything you can, but you can ignore ones you don't have -#dependencies for, just comment them out - -imageext src/imageext.c $(SDL) $(IMAGE) $(PNG) $(JPEG) $(DEBUG) -font src/font.c $(SDL) $(FONT) $(DEBUG) -mixer src/mixer.c $(SDL) $(MIXER) $(DEBUG) -mixer_music src/music.c $(SDL) $(MIXER) $(DEBUG) -_numericsurfarray src/_numericsurfarray.c $(SDL) $(DEBUG) -_numericsndarray src/_numericsndarray.c $(SDL) $(MIXER) $(DEBUG) -movie src/movie.c $(SDL) $(SMPEG) $(DEBUG) -scrap src/scrap.c $(SDL) $(SCRAP) $(DEBUG) -_camera src/_camera.c src/camera_v4l2.c src/camera_v4l.c $(SDL) $(DEBUG) -pypm src/pypm.c $(SDL) $(PORTMIDI) $(PORTTIME) $(DEBUG) - -GFX = src/SDL_gfx/SDL_gfxPrimitives.c -#GFX = src/SDL_gfx/SDL_gfxBlitFunc.c src/SDL_gfx/SDL_gfxPrimitives.c -gfxdraw src/gfxdraw.c $(SDL) $(GFX) $(DEBUG) - - - -#these modules are required for pygame to run. they only require -#SDL as a dependency. these should not be altered - -base src/base.c $(SDL) $(DEBUG) -cdrom src/cdrom.c $(SDL) $(DEBUG) -color src/color.c $(SDL) $(DEBUG) -constants src/constants.c $(SDL) $(DEBUG) -display src/display.c $(SDL) $(DEBUG) -event src/event.c $(SDL) $(DEBUG) -fastevent src/fastevent.c src/fastevents.c $(SDL) $(DEBUG) -key src/key.c $(SDL) $(DEBUG) -mouse src/mouse.c $(SDL) $(DEBUG) -rect src/rect.c $(SDL) $(DEBUG) -rwobject src/rwobject.c $(SDL) $(DEBUG) -surface src/surface.c src/alphablit.c src/surface_fill.c $(SDL) $(DEBUG) -surflock src/surflock.c $(SDL) $(DEBUG) -time src/time.c $(SDL) $(DEBUG) -joystick src/joystick.c $(SDL) $(DEBUG) -draw src/draw.c $(SDL) $(DEBUG) -image src/image.c $(SDL) $(DEBUG) -overlay src/overlay.c $(SDL) $(DEBUG) -transform src/transform.c src/rotozoom.c src/scale2x.c src/scale_mmx.c $(SDL) $(DEBUG) -mask src/mask.c src/bitmask.c $(SDL) $(DEBUG) -bufferproxy src/bufferproxy.c $(SDL) $(DEBUG) -pixelarray src/pixelarray.c $(SDL) $(DEBUG) -_arraysurfarray src/_arraysurfarray.c $(SDL) $(DEBUG) - - diff --git a/distutils/tests/__init__.py b/distutils/tests/__init__.py deleted file mode 100644 index 5d2e69e3..00000000 --- a/distutils/tests/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -"""Test suite for distutils. - -This test suite consists of a collection of test modules in the -distutils.tests package. Each test module has a name starting with -'test' and contains a function test_suite(). The function is expected -to return an initialized unittest.TestSuite instance. - -Tests for the command classes in the distutils.command package are -included in distutils.tests as well, instead of using a separate -distutils.command.tests package, since command identification is done -by import rather than matching pre-defined names. - -""" - -import os -import sys -import unittest -import warnings -from test.support import run_unittest - - -here = os.path.dirname(__file__) or os.curdir - - -def test_suite(): - old_filters = warnings.filters[:] - suite = unittest.TestSuite() - for fn in os.listdir(here): - if fn.startswith("test") and fn.endswith(".py"): - modname = "distutils.tests." + fn[:-3] - __import__(modname) - module = sys.modules[modname] - suite.addTest(module.test_suite()) - # bpo-40055: Save/restore warnings filters to leave them unchanged. - # Importing tests imports docutils which imports pkg_resources which adds a - # warnings filter. - warnings.filters[:] = old_filters - return suite - - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/includetest.rst b/distutils/tests/includetest.rst deleted file mode 100644 index d7b4ae38..00000000 --- a/distutils/tests/includetest.rst +++ /dev/null @@ -1 +0,0 @@ -This should be included. diff --git a/distutils/tests/py35compat.py b/distutils/tests/py35compat.py deleted file mode 100644 index 0c755261..00000000 --- a/distutils/tests/py35compat.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -Backward compatibility support for Python 3.5 -""" - -import sys -import test.support -import subprocess - - -# copied from Python 3.9 test.support module -def _missing_compiler_executable(cmd_names=[]): - """Check if the compiler components used to build the interpreter exist. - - Check for the existence of the compiler executables whose names are listed - in 'cmd_names' or all the compiler executables when 'cmd_names' is empty - and return the first missing executable or None when none is found - missing. - - """ - from distutils import ccompiler, sysconfig, spawn - compiler = ccompiler.new_compiler() - sysconfig.customize_compiler(compiler) - for name in compiler.executables: - if cmd_names and name not in cmd_names: - continue - cmd = getattr(compiler, name) - if cmd_names: - assert cmd is not None, \ - "the '%s' executable is not configured" % name - elif not cmd: - continue - if spawn.find_executable(cmd[0]) is None: - return cmd[0] - - -missing_compiler_executable = vars(test.support).setdefault( - 'missing_compiler_executable', - _missing_compiler_executable, -) - - -try: - from test.support import unix_shell -except ImportError: - # Adapted from Python 3.9 test.support module - is_android = hasattr(sys, 'getandroidapilevel') - unix_shell = ( - None if sys.platform == 'win32' else - '/system/bin/sh' if is_android else - '/bin/sh' - ) - - -# copied from Python 3.9 subprocess module -def _optim_args_from_interpreter_flags(): - """Return a list of command-line arguments reproducing the current - optimization settings in sys.flags.""" - args = [] - value = sys.flags.optimize - if value > 0: - args.append('-' + 'O' * value) - return args - - -vars(subprocess).setdefault( - '_optim_args_from_interpreter_flags', - _optim_args_from_interpreter_flags, -) - - -def adapt_glob(regex): - """ - Supply legacy expectation on Python 3.5 - """ - if sys.version_info > (3, 6): - return regex - return regex.replace('(?s:', '').replace(r')\Z', r'\Z(?ms)') diff --git a/distutils/tests/support.py b/distutils/tests/support.py deleted file mode 100644 index 259af882..00000000 --- a/distutils/tests/support.py +++ /dev/null @@ -1,209 +0,0 @@ -"""Support code for distutils test cases.""" -import os -import sys -import shutil -import tempfile -import unittest -import sysconfig -from copy import deepcopy -import test.support - -from distutils import log -from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL -from distutils.core import Distribution - - -class LoggingSilencer(object): - - def setUp(self): - super().setUp() - self.threshold = log.set_threshold(log.FATAL) - # catching warnings - # when log will be replaced by logging - # we won't need such monkey-patch anymore - self._old_log = log.Log._log - log.Log._log = self._log - self.logs = [] - - def tearDown(self): - log.set_threshold(self.threshold) - log.Log._log = self._old_log - super().tearDown() - - def _log(self, level, msg, args): - if level not in (DEBUG, INFO, WARN, ERROR, FATAL): - raise ValueError('%s wrong log level' % str(level)) - if not isinstance(msg, str): - raise TypeError("msg should be str, not '%.200s'" - % (type(msg).__name__)) - self.logs.append((level, msg, args)) - - def get_logs(self, *levels): - return [msg % args for level, msg, args - in self.logs if level in levels] - - def clear_logs(self): - self.logs = [] - - -class TempdirManager(object): - """Mix-in class that handles temporary directories for test cases. - - This is intended to be used with unittest.TestCase. - """ - - def setUp(self): - super().setUp() - self.old_cwd = os.getcwd() - self.tempdirs = [] - - def tearDown(self): - # Restore working dir, for Solaris and derivatives, where rmdir() - # on the current directory fails. - os.chdir(self.old_cwd) - super().tearDown() - while self.tempdirs: - tmpdir = self.tempdirs.pop() - test.support.rmtree(tmpdir) - - def mkdtemp(self): - """Create a temporary directory that will be cleaned up. - - Returns the path of the directory. - """ - d = tempfile.mkdtemp() - self.tempdirs.append(d) - return d - - def write_file(self, path, content='xxx'): - """Writes a file in the given path. - - - path can be a string or a sequence. - """ - if isinstance(path, (list, tuple)): - path = os.path.join(*path) - f = open(path, 'w') - try: - f.write(content) - finally: - f.close() - - def create_dist(self, pkg_name='foo', **kw): - """Will generate a test environment. - - This function creates: - - a Distribution instance using keywords - - a temporary directory with a package structure - - It returns the package directory and the distribution - instance. - """ - tmp_dir = self.mkdtemp() - pkg_dir = os.path.join(tmp_dir, pkg_name) - os.mkdir(pkg_dir) - dist = Distribution(attrs=kw) - - return pkg_dir, dist - - -class DummyCommand: - """Class to store options for retrieval via set_undefined_options().""" - - def __init__(self, **kwargs): - for kw, val in kwargs.items(): - setattr(self, kw, val) - - def ensure_finalized(self): - pass - - -class EnvironGuard(object): - - def setUp(self): - super(EnvironGuard, self).setUp() - self.old_environ = deepcopy(os.environ) - - def tearDown(self): - for key, value in self.old_environ.items(): - if os.environ.get(key) != value: - os.environ[key] = value - - for key in tuple(os.environ.keys()): - if key not in self.old_environ: - del os.environ[key] - - super(EnvironGuard, self).tearDown() - - -def copy_xxmodule_c(directory): - """Helper for tests that need the xxmodule.c source file. - - Example use: - - def test_compile(self): - copy_xxmodule_c(self.tmpdir) - self.assertIn('xxmodule.c', os.listdir(self.tmpdir)) - - If the source file can be found, it will be copied to *directory*. If not, - the test will be skipped. Errors during copy are not caught. - """ - filename = _get_xxmodule_path() - if filename is None: - raise unittest.SkipTest('cannot find xxmodule.c (test must run in ' - 'the python build dir)') - shutil.copy(filename, directory) - - -def _get_xxmodule_path(): - srcdir = sysconfig.get_config_var('srcdir') - candidates = [ - # use installed copy if available - os.path.join(os.path.dirname(__file__), 'xxmodule.c'), - # otherwise try using copy from build directory - os.path.join(srcdir, 'Modules', 'xxmodule.c'), - # srcdir mysteriously can be $srcdir/Lib/distutils/tests when - # this file is run from its parent directory, so walk up the - # tree to find the real srcdir - os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'), - ] - for path in candidates: - if os.path.exists(path): - return path - - -def fixup_build_ext(cmd): - """Function needed to make build_ext tests pass. - - When Python was built with --enable-shared on Unix, -L. is not enough to - find libpython.so, because regrtest runs in a tempdir, not in the - source directory where the .so lives. - - When Python was built with in debug mode on Windows, build_ext commands - need their debug attribute set, and it is not done automatically for - some reason. - - This function handles both of these things. Example use: - - cmd = build_ext(dist) - support.fixup_build_ext(cmd) - cmd.ensure_finalized() - - Unlike most other Unix platforms, Mac OS X embeds absolute paths - to shared libraries into executables, so the fixup is not needed there. - """ - if os.name == 'nt': - cmd.debug = sys.executable.endswith('_d.exe') - elif sysconfig.get_config_var('Py_ENABLE_SHARED'): - # To further add to the shared builds fun on Unix, we can't just add - # library_dirs to the Extension() instance because that doesn't get - # plumbed through to the final compiler command. - runshared = sysconfig.get_config_var('RUNSHARED') - if runshared is None: - cmd.library_dirs = ['.'] - else: - if sys.platform == 'darwin': - cmd.library_dirs = [] - else: - name, equals, value = runshared.partition('=') - cmd.library_dirs = [d for d in value.split(os.pathsep) if d] diff --git a/distutils/tests/test_archive_util.py b/distutils/tests/test_archive_util.py deleted file mode 100644 index e9aad0e4..00000000 --- a/distutils/tests/test_archive_util.py +++ /dev/null @@ -1,394 +0,0 @@ -# -*- coding: utf-8 -*- -"""Tests for distutils.archive_util.""" -import unittest -import os -import sys -import tarfile -from os.path import splitdrive -import warnings - -from distutils import archive_util -from distutils.archive_util import (check_archive_formats, make_tarball, - make_zipfile, make_archive, - ARCHIVE_FORMATS) -from distutils.spawn import find_executable, spawn -from distutils.tests import support -from test.support import check_warnings, run_unittest, patch, change_cwd - -try: - import grp - import pwd - UID_GID_SUPPORT = True -except ImportError: - UID_GID_SUPPORT = False - -try: - import zipfile - ZIP_SUPPORT = True -except ImportError: - ZIP_SUPPORT = find_executable('zip') - -try: - import zlib - ZLIB_SUPPORT = True -except ImportError: - ZLIB_SUPPORT = False - -try: - import bz2 -except ImportError: - bz2 = None - -try: - import lzma -except ImportError: - lzma = None - -def can_fs_encode(filename): - """ - Return True if the filename can be saved in the file system. - """ - if os.path.supports_unicode_filenames: - return True - try: - filename.encode(sys.getfilesystemencoding()) - except UnicodeEncodeError: - return False - return True - - -class ArchiveUtilTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_make_tarball(self, name='archive'): - # creating something to tar - tmpdir = self._create_files() - self._make_tarball(tmpdir, name, '.tar.gz') - # trying an uncompressed one - self._make_tarball(tmpdir, name, '.tar', compress=None) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_make_tarball_gzip(self): - tmpdir = self._create_files() - self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip') - - @unittest.skipUnless(bz2, 'Need bz2 support to run') - def test_make_tarball_bzip2(self): - tmpdir = self._create_files() - self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2') - - @unittest.skipUnless(lzma, 'Need lzma support to run') - def test_make_tarball_xz(self): - tmpdir = self._create_files() - self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz') - - @unittest.skipUnless(can_fs_encode('Ã¥rchiv'), - 'File system cannot handle this filename') - def test_make_tarball_latin1(self): - """ - Mirror test_make_tarball, except filename contains latin characters. - """ - self.test_make_tarball('Ã¥rchiv') # note this isn't a real word - - @unittest.skipUnless(can_fs_encode('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–'), - 'File system cannot handle this filename') - def test_make_tarball_extended(self): - """ - Mirror test_make_tarball, except filename contains extended - characters outside the latin charset. - """ - self.test_make_tarball('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–') # japanese for archive - - def _make_tarball(self, tmpdir, target_name, suffix, **kwargs): - tmpdir2 = self.mkdtemp() - unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], - "source and target should be on same drive") - - base_name = os.path.join(tmpdir2, target_name) - - # working with relative paths to avoid tar warnings - with change_cwd(tmpdir): - make_tarball(splitdrive(base_name)[1], 'dist', **kwargs) - - # check if the compressed tarball was created - tarball = base_name + suffix - self.assertTrue(os.path.exists(tarball)) - self.assertEqual(self._tarinfo(tarball), self._created_files) - - def _tarinfo(self, path): - tar = tarfile.open(path) - try: - names = tar.getnames() - names.sort() - return names - finally: - tar.close() - - _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', - 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] - _created_files = [p.rstrip('/') for p in _zip_created_files] - - def _create_files(self): - # creating something to tar - tmpdir = self.mkdtemp() - dist = os.path.join(tmpdir, 'dist') - os.mkdir(dist) - self.write_file([dist, 'file1'], 'xxx') - self.write_file([dist, 'file2'], 'xxx') - os.mkdir(os.path.join(dist, 'sub')) - self.write_file([dist, 'sub', 'file3'], 'xxx') - os.mkdir(os.path.join(dist, 'sub2')) - return tmpdir - - @unittest.skipUnless(find_executable('tar') and find_executable('gzip') - and ZLIB_SUPPORT, - 'Need the tar, gzip and zlib command to run') - def test_tarfile_vs_tar(self): - tmpdir = self._create_files() - tmpdir2 = self.mkdtemp() - base_name = os.path.join(tmpdir2, 'archive') - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - make_tarball(base_name, 'dist') - finally: - os.chdir(old_dir) - - # check if the compressed tarball was created - tarball = base_name + '.tar.gz' - self.assertTrue(os.path.exists(tarball)) - - # now create another tarball using `tar` - tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') - tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist'] - gzip_cmd = ['gzip', '-f', '-9', 'archive2.tar'] - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - spawn(tar_cmd) - spawn(gzip_cmd) - finally: - os.chdir(old_dir) - - self.assertTrue(os.path.exists(tarball2)) - # let's compare both tarballs - self.assertEqual(self._tarinfo(tarball), self._created_files) - self.assertEqual(self._tarinfo(tarball2), self._created_files) - - # trying an uncompressed one - base_name = os.path.join(tmpdir2, 'archive') - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - make_tarball(base_name, 'dist', compress=None) - finally: - os.chdir(old_dir) - tarball = base_name + '.tar' - self.assertTrue(os.path.exists(tarball)) - - # now for a dry_run - base_name = os.path.join(tmpdir2, 'archive') - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - make_tarball(base_name, 'dist', compress=None, dry_run=True) - finally: - os.chdir(old_dir) - tarball = base_name + '.tar' - self.assertTrue(os.path.exists(tarball)) - - @unittest.skipUnless(find_executable('compress'), - 'The compress program is required') - def test_compress_deprecated(self): - tmpdir = self._create_files() - base_name = os.path.join(self.mkdtemp(), 'archive') - - # using compress and testing the PendingDeprecationWarning - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - with check_warnings() as w: - warnings.simplefilter("always") - make_tarball(base_name, 'dist', compress='compress') - finally: - os.chdir(old_dir) - tarball = base_name + '.tar.Z' - self.assertTrue(os.path.exists(tarball)) - self.assertEqual(len(w.warnings), 1) - - # same test with dry_run - os.remove(tarball) - old_dir = os.getcwd() - os.chdir(tmpdir) - try: - with check_warnings() as w: - warnings.simplefilter("always") - make_tarball(base_name, 'dist', compress='compress', - dry_run=True) - finally: - os.chdir(old_dir) - self.assertFalse(os.path.exists(tarball)) - self.assertEqual(len(w.warnings), 1) - - @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, - 'Need zip and zlib support to run') - def test_make_zipfile(self): - # creating something to tar - tmpdir = self._create_files() - base_name = os.path.join(self.mkdtemp(), 'archive') - with change_cwd(tmpdir): - make_zipfile(base_name, 'dist') - - # check if the compressed tarball was created - tarball = base_name + '.zip' - self.assertTrue(os.path.exists(tarball)) - with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), self._zip_created_files) - - @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') - def test_make_zipfile_no_zlib(self): - patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError - - called = [] - zipfile_class = zipfile.ZipFile - def fake_zipfile(*a, **kw): - if kw.get('compression', None) == zipfile.ZIP_STORED: - called.append((a, kw)) - return zipfile_class(*a, **kw) - - patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile) - - # create something to tar and compress - tmpdir = self._create_files() - base_name = os.path.join(self.mkdtemp(), 'archive') - with change_cwd(tmpdir): - make_zipfile(base_name, 'dist') - - tarball = base_name + '.zip' - self.assertEqual(called, - [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) - self.assertTrue(os.path.exists(tarball)) - with zipfile.ZipFile(tarball) as zf: - self.assertEqual(sorted(zf.namelist()), self._zip_created_files) - - def test_check_archive_formats(self): - self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), - 'xxx') - self.assertIsNone(check_archive_formats(['gztar', 'bztar', 'xztar', - 'ztar', 'tar', 'zip'])) - - def test_make_archive(self): - tmpdir = self.mkdtemp() - base_name = os.path.join(tmpdir, 'archive') - self.assertRaises(ValueError, make_archive, base_name, 'xxx') - - def test_make_archive_cwd(self): - current_dir = os.getcwd() - def _breaks(*args, **kw): - raise RuntimeError() - ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file') - try: - try: - make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) - except: - pass - self.assertEqual(os.getcwd(), current_dir) - finally: - del ARCHIVE_FORMATS['xxx'] - - def test_make_archive_tar(self): - base_dir = self._create_files() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'tar', base_dir, 'dist') - self.assertTrue(os.path.exists(res)) - self.assertEqual(os.path.basename(res), 'archive.tar') - self.assertEqual(self._tarinfo(res), self._created_files) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_make_archive_gztar(self): - base_dir = self._create_files() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'gztar', base_dir, 'dist') - self.assertTrue(os.path.exists(res)) - self.assertEqual(os.path.basename(res), 'archive.tar.gz') - self.assertEqual(self._tarinfo(res), self._created_files) - - @unittest.skipUnless(bz2, 'Need bz2 support to run') - def test_make_archive_bztar(self): - base_dir = self._create_files() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'bztar', base_dir, 'dist') - self.assertTrue(os.path.exists(res)) - self.assertEqual(os.path.basename(res), 'archive.tar.bz2') - self.assertEqual(self._tarinfo(res), self._created_files) - - @unittest.skipUnless(lzma, 'Need xz support to run') - def test_make_archive_xztar(self): - base_dir = self._create_files() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'xztar', base_dir, 'dist') - self.assertTrue(os.path.exists(res)) - self.assertEqual(os.path.basename(res), 'archive.tar.xz') - self.assertEqual(self._tarinfo(res), self._created_files) - - def test_make_archive_owner_group(self): - # testing make_archive with owner and group, with various combinations - # this works even if there's not gid/uid support - if UID_GID_SUPPORT: - group = grp.getgrgid(0)[0] - owner = pwd.getpwuid(0)[0] - else: - group = owner = 'root' - - base_dir = self._create_files() - root_dir = self.mkdtemp() - base_name = os.path.join(self.mkdtemp() , 'archive') - res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, - group=group) - self.assertTrue(os.path.exists(res)) - - res = make_archive(base_name, 'zip', root_dir, base_dir) - self.assertTrue(os.path.exists(res)) - - res = make_archive(base_name, 'tar', root_dir, base_dir, - owner=owner, group=group) - self.assertTrue(os.path.exists(res)) - - res = make_archive(base_name, 'tar', root_dir, base_dir, - owner='kjhkjhkjg', group='oihohoh') - self.assertTrue(os.path.exists(res)) - - @unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib") - @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") - def test_tarfile_root_owner(self): - tmpdir = self._create_files() - base_name = os.path.join(self.mkdtemp(), 'archive') - old_dir = os.getcwd() - os.chdir(tmpdir) - group = grp.getgrgid(0)[0] - owner = pwd.getpwuid(0)[0] - try: - archive_name = make_tarball(base_name, 'dist', compress=None, - owner=owner, group=group) - finally: - os.chdir(old_dir) - - # check if the compressed tarball was created - self.assertTrue(os.path.exists(archive_name)) - - # now checks the rights - archive = tarfile.open(archive_name) - try: - for member in archive.getmembers(): - self.assertEqual(member.uid, 0) - self.assertEqual(member.gid, 0) - finally: - archive.close() - -def test_suite(): - return unittest.makeSuite(ArchiveUtilTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_bdist.py b/distutils/tests/test_bdist.py deleted file mode 100644 index 130d8bf1..00000000 --- a/distutils/tests/test_bdist.py +++ /dev/null @@ -1,57 +0,0 @@ -"""Tests for distutils.command.bdist.""" -import os -import unittest -from test.support import run_unittest -import warnings - -from distutils.command.bdist import bdist -from distutils.tests import support - - -class BuildTestCase(support.TempdirManager, - unittest.TestCase): - - def test_formats(self): - # let's create a command and make sure - # we can set the format - dist = self.create_dist()[1] - cmd = bdist(dist) - cmd.formats = ['msi'] - cmd.ensure_finalized() - self.assertEqual(cmd.formats, ['msi']) - - # what formats does bdist offer? - formats = ['bztar', 'gztar', 'msi', 'rpm', 'tar', - 'wininst', 'xztar', 'zip', 'ztar'] - found = sorted(cmd.format_command) - self.assertEqual(found, formats) - - def test_skip_build(self): - # bug #10946: bdist --skip-build should trickle down to subcommands - dist = self.create_dist()[1] - cmd = bdist(dist) - cmd.skip_build = 1 - cmd.ensure_finalized() - dist.command_obj['bdist'] = cmd - - names = ['bdist_dumb', 'bdist_wininst'] # bdist_rpm does not support --skip-build - if os.name == 'nt': - names.append('bdist_msi') - - for name in names: - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', 'bdist_wininst command is deprecated', - DeprecationWarning) - subcmd = cmd.get_finalized_command(name) - if getattr(subcmd, '_unsupported', False): - # command is not supported on this build - continue - self.assertTrue(subcmd.skip_build, - '%s should take --skip-build from bdist' % name) - - -def test_suite(): - return unittest.makeSuite(BuildTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/distutils/tests/test_bdist_dumb.py b/distutils/tests/test_bdist_dumb.py deleted file mode 100644 index 01a233bc..00000000 --- a/distutils/tests/test_bdist_dumb.py +++ /dev/null @@ -1,97 +0,0 @@ -"""Tests for distutils.command.bdist_dumb.""" - -import os -import sys -import zipfile -import unittest -from test.support import run_unittest - -from distutils.core import Distribution -from distutils.command.bdist_dumb import bdist_dumb -from distutils.tests import support - -SETUP_PY = """\ -from distutils.core import setup -import foo - -setup(name='foo', version='0.1', py_modules=['foo'], - url='xxx', author='xxx', author_email='xxx') - -""" - -try: - import zlib - ZLIB_SUPPORT = True -except ImportError: - ZLIB_SUPPORT = False - - -class BuildDumbTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def setUp(self): - super(BuildDumbTestCase, self).setUp() - self.old_location = os.getcwd() - self.old_sys_argv = sys.argv, sys.argv[:] - - def tearDown(self): - os.chdir(self.old_location) - sys.argv = self.old_sys_argv[0] - sys.argv[:] = self.old_sys_argv[1] - super(BuildDumbTestCase, self).tearDown() - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_simple_built(self): - - # let's create a simple package - tmp_dir = self.mkdtemp() - pkg_dir = os.path.join(tmp_dir, 'foo') - os.mkdir(pkg_dir) - self.write_file((pkg_dir, 'setup.py'), SETUP_PY) - self.write_file((pkg_dir, 'foo.py'), '#') - self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') - self.write_file((pkg_dir, 'README'), '') - - dist = Distribution({'name': 'foo', 'version': '0.1', - 'py_modules': ['foo'], - 'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx'}) - dist.script_name = 'setup.py' - os.chdir(pkg_dir) - - sys.argv = ['setup.py'] - cmd = bdist_dumb(dist) - - # so the output is the same no matter - # what is the platform - cmd.format = 'zip' - - cmd.ensure_finalized() - cmd.run() - - # see what we have - dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name) - - self.assertEqual(dist_created, [base]) - - # now let's check what we have in the zip file - fp = zipfile.ZipFile(os.path.join('dist', base)) - try: - contents = fp.namelist() - finally: - fp.close() - - contents = sorted(filter(None, map(os.path.basename, contents))) - wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] - if not sys.dont_write_bytecode: - wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) - self.assertEqual(contents, sorted(wanted)) - -def test_suite(): - return unittest.makeSuite(BuildDumbTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/distutils/tests/test_bdist_msi.py b/distutils/tests/test_bdist_msi.py deleted file mode 100644 index 418e60ec..00000000 --- a/distutils/tests/test_bdist_msi.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Tests for distutils.command.bdist_msi.""" -import sys -import unittest -from test.support import run_unittest, check_warnings -from distutils.tests import support - - -@unittest.skipUnless(sys.platform == 'win32', 'these tests require Windows') -class BDistMSITestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_minimal(self): - # minimal test XXX need more tests - from distutils.command.bdist_msi import bdist_msi - project_dir, dist = self.create_dist() - with check_warnings(("", DeprecationWarning)): - cmd = bdist_msi(dist) - cmd.ensure_finalized() - - -def test_suite(): - return unittest.makeSuite(BDistMSITestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/distutils/tests/test_bdist_rpm.py b/distutils/tests/test_bdist_rpm.py deleted file mode 100644 index 6453a02b..00000000 --- a/distutils/tests/test_bdist_rpm.py +++ /dev/null @@ -1,135 +0,0 @@ -"""Tests for distutils.command.bdist_rpm.""" - -import unittest -import sys -import os -from test.support import run_unittest, requires_zlib - -from distutils.core import Distribution -from distutils.command.bdist_rpm import bdist_rpm -from distutils.tests import support -from distutils.spawn import find_executable - -SETUP_PY = """\ -from distutils.core import setup -import foo - -setup(name='foo', version='0.1', py_modules=['foo'], - url='xxx', author='xxx', author_email='xxx') - -""" - -class BuildRpmTestCase(support.TempdirManager, - support.EnvironGuard, - support.LoggingSilencer, - unittest.TestCase): - - def setUp(self): - try: - sys.executable.encode("UTF-8") - except UnicodeEncodeError: - raise unittest.SkipTest("sys.executable is not encodable to UTF-8") - - super(BuildRpmTestCase, self).setUp() - self.old_location = os.getcwd() - self.old_sys_argv = sys.argv, sys.argv[:] - - def tearDown(self): - os.chdir(self.old_location) - sys.argv = self.old_sys_argv[0] - sys.argv[:] = self.old_sys_argv[1] - super(BuildRpmTestCase, self).tearDown() - - # XXX I am unable yet to make this test work without - # spurious sdtout/stderr output under Mac OS X - @unittest.skipUnless(sys.platform.startswith('linux'), - 'spurious sdtout/stderr output under Mac OS X') - @requires_zlib - @unittest.skipIf(find_executable('rpm') is None, - 'the rpm command is not found') - @unittest.skipIf(find_executable('rpmbuild') is None, - 'the rpmbuild command is not found') - def test_quiet(self): - # let's create a package - tmp_dir = self.mkdtemp() - os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation - pkg_dir = os.path.join(tmp_dir, 'foo') - os.mkdir(pkg_dir) - self.write_file((pkg_dir, 'setup.py'), SETUP_PY) - self.write_file((pkg_dir, 'foo.py'), '#') - self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') - self.write_file((pkg_dir, 'README'), '') - - dist = Distribution({'name': 'foo', 'version': '0.1', - 'py_modules': ['foo'], - 'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx'}) - dist.script_name = 'setup.py' - os.chdir(pkg_dir) - - sys.argv = ['setup.py'] - cmd = bdist_rpm(dist) - cmd.fix_python = True - - # running in quiet mode - cmd.quiet = 1 - cmd.ensure_finalized() - cmd.run() - - dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assertIn('foo-0.1-1.noarch.rpm', dist_created) - - # bug #2945: upload ignores bdist_rpm files - self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) - self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) - - # XXX I am unable yet to make this test work without - # spurious sdtout/stderr output under Mac OS X - @unittest.skipUnless(sys.platform.startswith('linux'), - 'spurious sdtout/stderr output under Mac OS X') - @requires_zlib - # http://bugs.python.org/issue1533164 - @unittest.skipIf(find_executable('rpm') is None, - 'the rpm command is not found') - @unittest.skipIf(find_executable('rpmbuild') is None, - 'the rpmbuild command is not found') - def test_no_optimize_flag(self): - # let's create a package that breaks bdist_rpm - tmp_dir = self.mkdtemp() - os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation - pkg_dir = os.path.join(tmp_dir, 'foo') - os.mkdir(pkg_dir) - self.write_file((pkg_dir, 'setup.py'), SETUP_PY) - self.write_file((pkg_dir, 'foo.py'), '#') - self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') - self.write_file((pkg_dir, 'README'), '') - - dist = Distribution({'name': 'foo', 'version': '0.1', - 'py_modules': ['foo'], - 'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx'}) - dist.script_name = 'setup.py' - os.chdir(pkg_dir) - - sys.argv = ['setup.py'] - cmd = bdist_rpm(dist) - cmd.fix_python = True - - cmd.quiet = 1 - cmd.ensure_finalized() - cmd.run() - - dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) - self.assertIn('foo-0.1-1.noarch.rpm', dist_created) - - # bug #2945: upload ignores bdist_rpm files - self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) - self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) - - os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) - -def test_suite(): - return unittest.makeSuite(BuildRpmTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/distutils/tests/test_bdist_wininst.py b/distutils/tests/test_bdist_wininst.py deleted file mode 100644 index 5c3d025d..00000000 --- a/distutils/tests/test_bdist_wininst.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Tests for distutils.command.bdist_wininst.""" -import sys -import platform -import unittest -from test.support import run_unittest, check_warnings - -from distutils.command.bdist_wininst import bdist_wininst -from distutils.tests import support - -@unittest.skipIf(sys.platform == 'win32' and platform.machine() == 'ARM64', - 'bdist_wininst is not supported in this install') -@unittest.skipIf(getattr(bdist_wininst, '_unsupported', False), - 'bdist_wininst is not supported in this install') -class BuildWinInstTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_get_exe_bytes(self): - - # issue5731: command was broken on non-windows platforms - # this test makes sure it works now for every platform - # let's create a command - pkg_pth, dist = self.create_dist() - with check_warnings(("", DeprecationWarning)): - cmd = bdist_wininst(dist) - cmd.ensure_finalized() - - # let's run the code that finds the right wininst*.exe file - # and make sure it finds it and returns its content - # no matter what platform we have - exe_file = cmd.get_exe_bytes() - self.assertGreater(len(exe_file), 10) - -def test_suite(): - return unittest.makeSuite(BuildWinInstTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/distutils/tests/test_build.py b/distutils/tests/test_build.py deleted file mode 100644 index b020a5ba..00000000 --- a/distutils/tests/test_build.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Tests for distutils.command.build.""" -import unittest -import os -import sys -from test.support import run_unittest - -from distutils.command.build import build -from distutils.tests import support -from sysconfig import get_platform - -class BuildTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_finalize_options(self): - pkg_dir, dist = self.create_dist() - cmd = build(dist) - cmd.finalize_options() - - # if not specified, plat_name gets the current platform - self.assertEqual(cmd.plat_name, get_platform()) - - # build_purelib is build + lib - wanted = os.path.join(cmd.build_base, 'lib') - self.assertEqual(cmd.build_purelib, wanted) - - # build_platlib is 'build/lib.platform-x.x[-pydebug]' - # examples: - # build/lib.macosx-10.3-i386-2.7 - plat_spec = '.%s-%d.%d' % (cmd.plat_name, *sys.version_info[:2]) - if hasattr(sys, 'gettotalrefcount'): - self.assertTrue(cmd.build_platlib.endswith('-pydebug')) - plat_spec += '-pydebug' - wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) - self.assertEqual(cmd.build_platlib, wanted) - - # by default, build_lib = build_purelib - self.assertEqual(cmd.build_lib, cmd.build_purelib) - - # build_temp is build/temp. - wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) - self.assertEqual(cmd.build_temp, wanted) - - # build_scripts is build/scripts-x.x - wanted = os.path.join(cmd.build_base, - 'scripts-%d.%d' % sys.version_info[:2]) - self.assertEqual(cmd.build_scripts, wanted) - - # executable is os.path.normpath(sys.executable) - self.assertEqual(cmd.executable, os.path.normpath(sys.executable)) - -def test_suite(): - return unittest.makeSuite(BuildTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_build_clib.py b/distutils/tests/test_build_clib.py deleted file mode 100644 index 259c4352..00000000 --- a/distutils/tests/test_build_clib.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Tests for distutils.command.build_clib.""" -import unittest -import os -import sys - -from test.support import run_unittest - -from .py35compat import missing_compiler_executable - -from distutils.command.build_clib import build_clib -from distutils.errors import DistutilsSetupError -from distutils.tests import support - -class BuildCLibTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_check_library_dist(self): - pkg_dir, dist = self.create_dist() - cmd = build_clib(dist) - - # 'libraries' option must be a list - self.assertRaises(DistutilsSetupError, cmd.check_library_list, 'foo') - - # each element of 'libraries' must a 2-tuple - self.assertRaises(DistutilsSetupError, cmd.check_library_list, - ['foo1', 'foo2']) - - # first element of each tuple in 'libraries' - # must be a string (the library name) - self.assertRaises(DistutilsSetupError, cmd.check_library_list, - [(1, 'foo1'), ('name', 'foo2')]) - - # library name may not contain directory separators - self.assertRaises(DistutilsSetupError, cmd.check_library_list, - [('name', 'foo1'), - ('another/name', 'foo2')]) - - # second element of each tuple must be a dictionary (build info) - self.assertRaises(DistutilsSetupError, cmd.check_library_list, - [('name', {}), - ('another', 'foo2')]) - - # those work - libs = [('name', {}), ('name', {'ok': 'good'})] - cmd.check_library_list(libs) - - def test_get_source_files(self): - pkg_dir, dist = self.create_dist() - cmd = build_clib(dist) - - # "in 'libraries' option 'sources' must be present and must be - # a list of source filenames - cmd.libraries = [('name', {})] - self.assertRaises(DistutilsSetupError, cmd.get_source_files) - - cmd.libraries = [('name', {'sources': 1})] - self.assertRaises(DistutilsSetupError, cmd.get_source_files) - - cmd.libraries = [('name', {'sources': ['a', 'b']})] - self.assertEqual(cmd.get_source_files(), ['a', 'b']) - - cmd.libraries = [('name', {'sources': ('a', 'b')})] - self.assertEqual(cmd.get_source_files(), ['a', 'b']) - - cmd.libraries = [('name', {'sources': ('a', 'b')}), - ('name2', {'sources': ['c', 'd']})] - self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd']) - - def test_build_libraries(self): - - pkg_dir, dist = self.create_dist() - cmd = build_clib(dist) - class FakeCompiler: - def compile(*args, **kw): - pass - create_static_lib = compile - - cmd.compiler = FakeCompiler() - - # build_libraries is also doing a bit of typo checking - lib = [('name', {'sources': 'notvalid'})] - self.assertRaises(DistutilsSetupError, cmd.build_libraries, lib) - - lib = [('name', {'sources': list()})] - cmd.build_libraries(lib) - - lib = [('name', {'sources': tuple()})] - cmd.build_libraries(lib) - - def test_finalize_options(self): - pkg_dir, dist = self.create_dist() - cmd = build_clib(dist) - - cmd.include_dirs = 'one-dir' - cmd.finalize_options() - self.assertEqual(cmd.include_dirs, ['one-dir']) - - cmd.include_dirs = None - cmd.finalize_options() - self.assertEqual(cmd.include_dirs, []) - - cmd.distribution.libraries = 'WONTWORK' - self.assertRaises(DistutilsSetupError, cmd.finalize_options) - - @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") - def test_run(self): - pkg_dir, dist = self.create_dist() - cmd = build_clib(dist) - - foo_c = os.path.join(pkg_dir, 'foo.c') - self.write_file(foo_c, 'int main(void) { return 1;}\n') - cmd.libraries = [('foo', {'sources': [foo_c]})] - - build_temp = os.path.join(pkg_dir, 'build') - os.mkdir(build_temp) - cmd.build_temp = build_temp - cmd.build_clib = build_temp - - # Before we run the command, we want to make sure - # all commands are present on the system. - ccmd = missing_compiler_executable() - if ccmd is not None: - self.skipTest('The %r command is not found' % ccmd) - - # this should work - cmd.run() - - # let's check the result - self.assertIn('libfoo.a', os.listdir(build_temp)) - -def test_suite(): - return unittest.makeSuite(BuildCLibTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_build_ext.py b/distutils/tests/test_build_ext.py deleted file mode 100644 index 1aec1537..00000000 --- a/distutils/tests/test_build_ext.py +++ /dev/null @@ -1,545 +0,0 @@ -import sys -import os -from io import StringIO -import textwrap - -from distutils.core import Distribution -from distutils.command.build_ext import build_ext -from distutils import sysconfig -from distutils.tests.support import (TempdirManager, LoggingSilencer, - copy_xxmodule_c, fixup_build_ext) -from distutils.extension import Extension -from distutils.errors import ( - CompileError, DistutilsPlatformError, DistutilsSetupError, - UnknownFileError) - -import unittest -from test import support -from test.support.script_helper import assert_python_ok - -# http://bugs.python.org/issue4373 -# Don't load the xx module more than once. -ALREADY_TESTED = False - - -class BuildExtTestCase(TempdirManager, - LoggingSilencer, - unittest.TestCase): - def setUp(self): - # Create a simple test environment - super(BuildExtTestCase, self).setUp() - self.tmp_dir = self.mkdtemp() - import site - self.old_user_base = site.USER_BASE - site.USER_BASE = self.mkdtemp() - from distutils.command import build_ext - build_ext.USER_BASE = site.USER_BASE - - # bpo-30132: On Windows, a .pdb file may be created in the current - # working directory. Create a temporary working directory to cleanup - # everything at the end of the test. - change_cwd = support.change_cwd(self.tmp_dir) - change_cwd.__enter__() - self.addCleanup(change_cwd.__exit__, None, None, None) - - def tearDown(self): - import site - site.USER_BASE = self.old_user_base - from distutils.command import build_ext - build_ext.USER_BASE = self.old_user_base - super(BuildExtTestCase, self).tearDown() - - def build_ext(self, *args, **kwargs): - return build_ext(*args, **kwargs) - - def test_build_ext(self): - cmd = support.missing_compiler_executable() - if cmd is not None: - self.skipTest('The %r command is not found' % cmd) - global ALREADY_TESTED - copy_xxmodule_c(self.tmp_dir) - xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') - xx_ext = Extension('xx', [xx_c]) - dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) - dist.package_dir = self.tmp_dir - cmd = self.build_ext(dist) - fixup_build_ext(cmd) - cmd.build_lib = self.tmp_dir - cmd.build_temp = self.tmp_dir - - old_stdout = sys.stdout - if not support.verbose: - # silence compiler output - sys.stdout = StringIO() - try: - cmd.ensure_finalized() - cmd.run() - finally: - sys.stdout = old_stdout - - if ALREADY_TESTED: - self.skipTest('Already tested in %s' % ALREADY_TESTED) - else: - ALREADY_TESTED = type(self).__name__ - - code = textwrap.dedent(""" - tmp_dir = {self.tmp_dir!r} - - import sys - import unittest - from test import support - - sys.path.insert(0, tmp_dir) - import xx - - class Tests(unittest.TestCase): - def test_xx(self): - for attr in ('error', 'foo', 'new', 'roj'): - self.assertTrue(hasattr(xx, attr)) - - self.assertEqual(xx.foo(2, 5), 7) - self.assertEqual(xx.foo(13,15), 28) - self.assertEqual(xx.new().demo(), None) - if support.HAVE_DOCSTRINGS: - doc = 'This is a template module just for instruction.' - self.assertEqual(xx.__doc__, doc) - self.assertIsInstance(xx.Null(), xx.Null) - self.assertIsInstance(xx.Str(), xx.Str) - - - unittest.main() - """.format(**locals())) - assert_python_ok('-c', code) - - def test_solaris_enable_shared(self): - dist = Distribution({'name': 'xx'}) - cmd = self.build_ext(dist) - old = sys.platform - - sys.platform = 'sunos' # fooling finalize_options - from distutils.sysconfig import _config_vars - old_var = _config_vars.get('Py_ENABLE_SHARED') - _config_vars['Py_ENABLE_SHARED'] = 1 - try: - cmd.ensure_finalized() - finally: - sys.platform = old - if old_var is None: - del _config_vars['Py_ENABLE_SHARED'] - else: - _config_vars['Py_ENABLE_SHARED'] = old_var - - # make sure we get some library dirs under solaris - self.assertGreater(len(cmd.library_dirs), 0) - - def test_user_site(self): - import site - dist = Distribution({'name': 'xx'}) - cmd = self.build_ext(dist) - - # making sure the user option is there - options = [name for name, short, lable in - cmd.user_options] - self.assertIn('user', options) - - # setting a value - cmd.user = 1 - - # setting user based lib and include - lib = os.path.join(site.USER_BASE, 'lib') - incl = os.path.join(site.USER_BASE, 'include') - os.mkdir(lib) - os.mkdir(incl) - - # let's run finalize - cmd.ensure_finalized() - - # see if include_dirs and library_dirs - # were set - self.assertIn(lib, cmd.library_dirs) - self.assertIn(lib, cmd.rpath) - self.assertIn(incl, cmd.include_dirs) - - def test_optional_extension(self): - - # this extension will fail, but let's ignore this failure - # with the optional argument. - modules = [Extension('foo', ['xxx'], optional=False)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = self.build_ext(dist) - cmd.ensure_finalized() - self.assertRaises((UnknownFileError, CompileError), - cmd.run) # should raise an error - - modules = [Extension('foo', ['xxx'], optional=True)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = self.build_ext(dist) - cmd.ensure_finalized() - cmd.run() # should pass - - def test_finalize_options(self): - # Make sure Python's include directories (for Python.h, pyconfig.h, - # etc.) are in the include search path. - modules = [Extension('foo', ['xxx'], optional=False)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = self.build_ext(dist) - cmd.finalize_options() - - py_include = sysconfig.get_python_inc() - for p in py_include.split(os.path.pathsep): - self.assertIn(p, cmd.include_dirs) - - plat_py_include = sysconfig.get_python_inc(plat_specific=1) - for p in plat_py_include.split(os.path.pathsep): - self.assertIn(p, cmd.include_dirs) - - # make sure cmd.libraries is turned into a list - # if it's a string - cmd = self.build_ext(dist) - cmd.libraries = 'my_lib, other_lib lastlib' - cmd.finalize_options() - self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib']) - - # make sure cmd.library_dirs is turned into a list - # if it's a string - cmd = self.build_ext(dist) - cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep - cmd.finalize_options() - self.assertIn('my_lib_dir', cmd.library_dirs) - self.assertIn('other_lib_dir', cmd.library_dirs) - - # make sure rpath is turned into a list - # if it's a string - cmd = self.build_ext(dist) - cmd.rpath = 'one%stwo' % os.pathsep - cmd.finalize_options() - self.assertEqual(cmd.rpath, ['one', 'two']) - - # make sure cmd.link_objects is turned into a list - # if it's a string - cmd = build_ext(dist) - cmd.link_objects = 'one two,three' - cmd.finalize_options() - self.assertEqual(cmd.link_objects, ['one', 'two', 'three']) - - # XXX more tests to perform for win32 - - # make sure define is turned into 2-tuples - # strings if they are ','-separated strings - cmd = self.build_ext(dist) - cmd.define = 'one,two' - cmd.finalize_options() - self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) - - # make sure undef is turned into a list of - # strings if they are ','-separated strings - cmd = self.build_ext(dist) - cmd.undef = 'one,two' - cmd.finalize_options() - self.assertEqual(cmd.undef, ['one', 'two']) - - # make sure swig_opts is turned into a list - cmd = self.build_ext(dist) - cmd.swig_opts = None - cmd.finalize_options() - self.assertEqual(cmd.swig_opts, []) - - cmd = self.build_ext(dist) - cmd.swig_opts = '1 2' - cmd.finalize_options() - self.assertEqual(cmd.swig_opts, ['1', '2']) - - def test_check_extensions_list(self): - dist = Distribution() - cmd = self.build_ext(dist) - cmd.finalize_options() - - #'extensions' option must be a list of Extension instances - self.assertRaises(DistutilsSetupError, - cmd.check_extensions_list, 'foo') - - # each element of 'ext_modules' option must be an - # Extension instance or 2-tuple - exts = [('bar', 'foo', 'bar'), 'foo'] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # first element of each tuple in 'ext_modules' - # must be the extension name (a string) and match - # a python dotted-separated name - exts = [('foo-bar', '')] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # second element of each tuple in 'ext_modules' - # must be a dictionary (build info) - exts = [('foo.bar', '')] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # ok this one should pass - exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', - 'some': 'bar'})] - cmd.check_extensions_list(exts) - ext = exts[0] - self.assertIsInstance(ext, Extension) - - # check_extensions_list adds in ext the values passed - # when they are in ('include_dirs', 'library_dirs', 'libraries' - # 'extra_objects', 'extra_compile_args', 'extra_link_args') - self.assertEqual(ext.libraries, 'foo') - self.assertFalse(hasattr(ext, 'some')) - - # 'macros' element of build info dict must be 1- or 2-tuple - exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', - 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - exts[0][1]['macros'] = [('1', '2'), ('3',)] - cmd.check_extensions_list(exts) - self.assertEqual(exts[0].undef_macros, ['3']) - self.assertEqual(exts[0].define_macros, [('1', '2')]) - - def test_get_source_files(self): - modules = [Extension('foo', ['xxx'], optional=False)] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = self.build_ext(dist) - cmd.ensure_finalized() - self.assertEqual(cmd.get_source_files(), ['xxx']) - - def test_unicode_module_names(self): - modules = [ - Extension('foo', ['aaa'], optional=False), - Extension('föö', ['uuu'], optional=False), - ] - dist = Distribution({'name': 'xx', 'ext_modules': modules}) - cmd = self.build_ext(dist) - cmd.ensure_finalized() - self.assertRegex(cmd.get_ext_filename(modules[0].name), r'foo(_d)?\..*') - self.assertRegex(cmd.get_ext_filename(modules[1].name), r'föö(_d)?\..*') - self.assertEqual(cmd.get_export_symbols(modules[0]), ['PyInit_foo']) - self.assertEqual(cmd.get_export_symbols(modules[1]), ['PyInitU_f_gkaa']) - - def test_compiler_option(self): - # cmd.compiler is an option and - # should not be overridden by a compiler instance - # when the command is run - dist = Distribution() - cmd = self.build_ext(dist) - cmd.compiler = 'unix' - cmd.ensure_finalized() - cmd.run() - self.assertEqual(cmd.compiler, 'unix') - - def test_get_outputs(self): - cmd = support.missing_compiler_executable() - if cmd is not None: - self.skipTest('The %r command is not found' % cmd) - tmp_dir = self.mkdtemp() - c_file = os.path.join(tmp_dir, 'foo.c') - self.write_file(c_file, 'void PyInit_foo(void) {}\n') - ext = Extension('foo', [c_file], optional=False) - dist = Distribution({'name': 'xx', - 'ext_modules': [ext]}) - cmd = self.build_ext(dist) - fixup_build_ext(cmd) - cmd.ensure_finalized() - self.assertEqual(len(cmd.get_outputs()), 1) - - cmd.build_lib = os.path.join(self.tmp_dir, 'build') - cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') - - # issue #5977 : distutils build_ext.get_outputs - # returns wrong result with --inplace - other_tmp_dir = os.path.realpath(self.mkdtemp()) - old_wd = os.getcwd() - os.chdir(other_tmp_dir) - try: - cmd.inplace = 1 - cmd.run() - so_file = cmd.get_outputs()[0] - finally: - os.chdir(old_wd) - self.assertTrue(os.path.exists(so_file)) - ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') - self.assertTrue(so_file.endswith(ext_suffix)) - so_dir = os.path.dirname(so_file) - self.assertEqual(so_dir, other_tmp_dir) - - cmd.inplace = 0 - cmd.compiler = None - cmd.run() - so_file = cmd.get_outputs()[0] - self.assertTrue(os.path.exists(so_file)) - self.assertTrue(so_file.endswith(ext_suffix)) - so_dir = os.path.dirname(so_file) - self.assertEqual(so_dir, cmd.build_lib) - - # inplace = 0, cmd.package = 'bar' - build_py = cmd.get_finalized_command('build_py') - build_py.package_dir = {'': 'bar'} - path = cmd.get_ext_fullpath('foo') - # checking that the last directory is the build_dir - path = os.path.split(path)[0] - self.assertEqual(path, cmd.build_lib) - - # inplace = 1, cmd.package = 'bar' - cmd.inplace = 1 - other_tmp_dir = os.path.realpath(self.mkdtemp()) - old_wd = os.getcwd() - os.chdir(other_tmp_dir) - try: - path = cmd.get_ext_fullpath('foo') - finally: - os.chdir(old_wd) - # checking that the last directory is bar - path = os.path.split(path)[0] - lastdir = os.path.split(path)[-1] - self.assertEqual(lastdir, 'bar') - - def test_ext_fullpath(self): - ext = sysconfig.get_config_var('EXT_SUFFIX') - # building lxml.etree inplace - #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') - #etree_ext = Extension('lxml.etree', [etree_c]) - #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) - dist = Distribution() - cmd = self.build_ext(dist) - cmd.inplace = 1 - cmd.distribution.package_dir = {'': 'src'} - cmd.distribution.packages = ['lxml', 'lxml.html'] - curdir = os.getcwd() - wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) - path = cmd.get_ext_fullpath('lxml.etree') - self.assertEqual(wanted, path) - - # building lxml.etree not inplace - cmd.inplace = 0 - cmd.build_lib = os.path.join(curdir, 'tmpdir') - wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) - path = cmd.get_ext_fullpath('lxml.etree') - self.assertEqual(wanted, path) - - # building twisted.runner.portmap not inplace - build_py = cmd.get_finalized_command('build_py') - build_py.package_dir = {} - cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] - path = cmd.get_ext_fullpath('twisted.runner.portmap') - wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', - 'portmap' + ext) - self.assertEqual(wanted, path) - - # building twisted.runner.portmap inplace - cmd.inplace = 1 - path = cmd.get_ext_fullpath('twisted.runner.portmap') - wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) - self.assertEqual(wanted, path) - - - @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') - def test_deployment_target_default(self): - # Issue 9516: Test that, in the absence of the environment variable, - # an extension module is compiled with the same deployment target as - # the interpreter. - self._try_compile_deployment_target('==', None) - - @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') - def test_deployment_target_too_low(self): - # Issue 9516: Test that an extension module is not allowed to be - # compiled with a deployment target less than that of the interpreter. - self.assertRaises(DistutilsPlatformError, - self._try_compile_deployment_target, '>', '10.1') - - @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') - def test_deployment_target_higher_ok(self): - # Issue 9516: Test that an extension module can be compiled with a - # deployment target higher than that of the interpreter: the ext - # module may depend on some newer OS feature. - deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - if deptarget: - # increment the minor version number (i.e. 10.6 -> 10.7) - deptarget = [int(x) for x in deptarget.split('.')] - deptarget[-1] += 1 - deptarget = '.'.join(str(i) for i in deptarget) - self._try_compile_deployment_target('<', deptarget) - - def _try_compile_deployment_target(self, operator, target): - orig_environ = os.environ - os.environ = orig_environ.copy() - self.addCleanup(setattr, os, 'environ', orig_environ) - - if target is None: - if os.environ.get('MACOSX_DEPLOYMENT_TARGET'): - del os.environ['MACOSX_DEPLOYMENT_TARGET'] - else: - os.environ['MACOSX_DEPLOYMENT_TARGET'] = target - - deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') - - with open(deptarget_c, 'w') as fp: - fp.write(textwrap.dedent('''\ - #include - - int dummy; - - #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED - #else - #error "Unexpected target" - #endif - - ''' % operator)) - - # get the deployment target that the interpreter was built with - target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') - target = tuple(map(int, target.split('.')[0:2])) - # format the target value as defined in the Apple - # Availability Macros. We can't use the macro names since - # at least one value we test with will not exist yet. - if target[1] < 10: - # for 10.1 through 10.9.x -> "10n0" - target = '%02d%01d0' % target - else: - # for 10.10 and beyond -> "10nn00" - target = '%02d%02d00' % target - deptarget_ext = Extension( - 'deptarget', - [deptarget_c], - extra_compile_args=['-DTARGET=%s'%(target,)], - ) - dist = Distribution({ - 'name': 'deptarget', - 'ext_modules': [deptarget_ext] - }) - dist.package_dir = self.tmp_dir - cmd = self.build_ext(dist) - cmd.build_lib = self.tmp_dir - cmd.build_temp = self.tmp_dir - - try: - old_stdout = sys.stdout - if not support.verbose: - # silence compiler output - sys.stdout = StringIO() - try: - cmd.ensure_finalized() - cmd.run() - finally: - sys.stdout = old_stdout - - except CompileError: - self.fail("Wrong deployment target during compilation") - - -class ParallelBuildExtTestCase(BuildExtTestCase): - - def build_ext(self, *args, **kwargs): - build_ext = super().build_ext(*args, **kwargs) - build_ext.parallel = True - return build_ext - - -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(BuildExtTestCase)) - suite.addTest(unittest.makeSuite(ParallelBuildExtTestCase)) - return suite - -if __name__ == '__main__': - support.run_unittest(__name__) diff --git a/distutils/tests/test_build_py.py b/distutils/tests/test_build_py.py deleted file mode 100644 index 0712e92c..00000000 --- a/distutils/tests/test_build_py.py +++ /dev/null @@ -1,179 +0,0 @@ -"""Tests for distutils.command.build_py.""" - -import os -import sys -import unittest - -from distutils.command.build_py import build_py -from distutils.core import Distribution -from distutils.errors import DistutilsFileError - -from distutils.tests import support -from test.support import run_unittest - - -class BuildPyTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_package_data(self): - sources = self.mkdtemp() - f = open(os.path.join(sources, "__init__.py"), "w") - try: - f.write("# Pretend this is a package.") - finally: - f.close() - f = open(os.path.join(sources, "README.txt"), "w") - try: - f.write("Info about this package") - finally: - f.close() - - destination = self.mkdtemp() - - dist = Distribution({"packages": ["pkg"], - "package_dir": {"pkg": sources}}) - # script_name need not exist, it just need to be initialized - dist.script_name = os.path.join(sources, "setup.py") - dist.command_obj["build"] = support.DummyCommand( - force=0, - build_lib=destination) - dist.packages = ["pkg"] - dist.package_data = {"pkg": ["README.txt"]} - dist.package_dir = {"pkg": sources} - - cmd = build_py(dist) - cmd.compile = 1 - cmd.ensure_finalized() - self.assertEqual(cmd.package_data, dist.package_data) - - cmd.run() - - # This makes sure the list of outputs includes byte-compiled - # files for Python modules but not for package data files - # (there shouldn't *be* byte-code files for those!). - self.assertEqual(len(cmd.get_outputs()), 3) - pkgdest = os.path.join(destination, "pkg") - files = os.listdir(pkgdest) - pycache_dir = os.path.join(pkgdest, "__pycache__") - self.assertIn("__init__.py", files) - self.assertIn("README.txt", files) - if sys.dont_write_bytecode: - self.assertFalse(os.path.exists(pycache_dir)) - else: - pyc_files = os.listdir(pycache_dir) - self.assertIn("__init__.%s.pyc" % sys.implementation.cache_tag, - pyc_files) - - def test_empty_package_dir(self): - # See bugs #1668596/#1720897 - sources = self.mkdtemp() - open(os.path.join(sources, "__init__.py"), "w").close() - - testdir = os.path.join(sources, "doc") - os.mkdir(testdir) - open(os.path.join(testdir, "testfile"), "w").close() - - os.chdir(sources) - dist = Distribution({"packages": ["pkg"], - "package_dir": {"pkg": ""}, - "package_data": {"pkg": ["doc/*"]}}) - # script_name need not exist, it just need to be initialized - dist.script_name = os.path.join(sources, "setup.py") - dist.script_args = ["build"] - dist.parse_command_line() - - try: - dist.run_commands() - except DistutilsFileError: - self.fail("failed package_data test when package_dir is ''") - - @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') - def test_byte_compile(self): - project_dir, dist = self.create_dist(py_modules=['boiledeggs']) - os.chdir(project_dir) - self.write_file('boiledeggs.py', 'import antigravity') - cmd = build_py(dist) - cmd.compile = 1 - cmd.build_lib = 'here' - cmd.finalize_options() - cmd.run() - - found = os.listdir(cmd.build_lib) - self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) - found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) - self.assertEqual(found, - ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]) - - @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') - def test_byte_compile_optimized(self): - project_dir, dist = self.create_dist(py_modules=['boiledeggs']) - os.chdir(project_dir) - self.write_file('boiledeggs.py', 'import antigravity') - cmd = build_py(dist) - cmd.compile = 0 - cmd.optimize = 1 - cmd.build_lib = 'here' - cmd.finalize_options() - cmd.run() - - found = os.listdir(cmd.build_lib) - self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) - found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) - expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag) - self.assertEqual(sorted(found), [expect]) - - def test_dir_in_package_data(self): - """ - A directory in package_data should not be added to the filelist. - """ - # See bug 19286 - sources = self.mkdtemp() - pkg_dir = os.path.join(sources, "pkg") - - os.mkdir(pkg_dir) - open(os.path.join(pkg_dir, "__init__.py"), "w").close() - - docdir = os.path.join(pkg_dir, "doc") - os.mkdir(docdir) - open(os.path.join(docdir, "testfile"), "w").close() - - # create the directory that could be incorrectly detected as a file - os.mkdir(os.path.join(docdir, 'otherdir')) - - os.chdir(sources) - dist = Distribution({"packages": ["pkg"], - "package_data": {"pkg": ["doc/*"]}}) - # script_name need not exist, it just need to be initialized - dist.script_name = os.path.join(sources, "setup.py") - dist.script_args = ["build"] - dist.parse_command_line() - - try: - dist.run_commands() - except DistutilsFileError: - self.fail("failed package_data when data dir includes a dir") - - def test_dont_write_bytecode(self): - # makes sure byte_compile is not used - dist = self.create_dist()[1] - cmd = build_py(dist) - cmd.compile = 1 - cmd.optimize = 1 - - old_dont_write_bytecode = sys.dont_write_bytecode - sys.dont_write_bytecode = True - try: - cmd.byte_compile([]) - finally: - sys.dont_write_bytecode = old_dont_write_bytecode - - self.assertIn('byte-compiling is disabled', - self.logs[0][1] % self.logs[0][2]) - - -def test_suite(): - return unittest.makeSuite(BuildPyTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_build_scripts.py b/distutils/tests/test_build_scripts.py deleted file mode 100644 index 954fc763..00000000 --- a/distutils/tests/test_build_scripts.py +++ /dev/null @@ -1,112 +0,0 @@ -"""Tests for distutils.command.build_scripts.""" - -import os -import unittest - -from distutils.command.build_scripts import build_scripts -from distutils.core import Distribution -from distutils import sysconfig - -from distutils.tests import support -from test.support import run_unittest - - -class BuildScriptsTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_default_settings(self): - cmd = self.get_build_scripts_cmd("/foo/bar", []) - self.assertFalse(cmd.force) - self.assertIsNone(cmd.build_dir) - - cmd.finalize_options() - - self.assertTrue(cmd.force) - self.assertEqual(cmd.build_dir, "/foo/bar") - - def test_build(self): - source = self.mkdtemp() - target = self.mkdtemp() - expected = self.write_sample_scripts(source) - - cmd = self.get_build_scripts_cmd(target, - [os.path.join(source, fn) - for fn in expected]) - cmd.finalize_options() - cmd.run() - - built = os.listdir(target) - for name in expected: - self.assertIn(name, built) - - def get_build_scripts_cmd(self, target, scripts): - import sys - dist = Distribution() - dist.scripts = scripts - dist.command_obj["build"] = support.DummyCommand( - build_scripts=target, - force=1, - executable=sys.executable - ) - return build_scripts(dist) - - def write_sample_scripts(self, dir): - expected = [] - expected.append("script1.py") - self.write_script(dir, "script1.py", - ("#! /usr/bin/env python2.3\n" - "# bogus script w/ Python sh-bang\n" - "pass\n")) - expected.append("script2.py") - self.write_script(dir, "script2.py", - ("#!/usr/bin/python\n" - "# bogus script w/ Python sh-bang\n" - "pass\n")) - expected.append("shell.sh") - self.write_script(dir, "shell.sh", - ("#!/bin/sh\n" - "# bogus shell script w/ sh-bang\n" - "exit 0\n")) - return expected - - def write_script(self, dir, name, text): - f = open(os.path.join(dir, name), "w") - try: - f.write(text) - finally: - f.close() - - def test_version_int(self): - source = self.mkdtemp() - target = self.mkdtemp() - expected = self.write_sample_scripts(source) - - - cmd = self.get_build_scripts_cmd(target, - [os.path.join(source, fn) - for fn in expected]) - cmd.finalize_options() - - # http://bugs.python.org/issue4524 - # - # On linux-g++-32 with command line `./configure --enable-ipv6 - # --with-suffix=3`, python is compiled okay but the build scripts - # failed when writing the name of the executable - old = sysconfig.get_config_vars().get('VERSION') - sysconfig._config_vars['VERSION'] = 4 - try: - cmd.run() - finally: - if old is not None: - sysconfig._config_vars['VERSION'] = old - - built = os.listdir(target) - for name in expected: - self.assertIn(name, built) - -def test_suite(): - return unittest.makeSuite(BuildScriptsTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_check.py b/distutils/tests/test_check.py deleted file mode 100644 index e534aca1..00000000 --- a/distutils/tests/test_check.py +++ /dev/null @@ -1,163 +0,0 @@ -"""Tests for distutils.command.check.""" -import os -import textwrap -import unittest -from test.support import run_unittest - -from distutils.command.check import check, HAS_DOCUTILS -from distutils.tests import support -from distutils.errors import DistutilsSetupError - -try: - import pygments -except ImportError: - pygments = None - - -HERE = os.path.dirname(__file__) - - -class CheckTestCase(support.LoggingSilencer, - support.TempdirManager, - unittest.TestCase): - - def _run(self, metadata=None, cwd=None, **options): - if metadata is None: - metadata = {} - if cwd is not None: - old_dir = os.getcwd() - os.chdir(cwd) - pkg_info, dist = self.create_dist(**metadata) - cmd = check(dist) - cmd.initialize_options() - for name, value in options.items(): - setattr(cmd, name, value) - cmd.ensure_finalized() - cmd.run() - if cwd is not None: - os.chdir(old_dir) - return cmd - - def test_check_metadata(self): - # let's run the command with no metadata at all - # by default, check is checking the metadata - # should have some warnings - cmd = self._run() - self.assertEqual(cmd._warnings, 2) - - # now let's add the required fields - # and run it again, to make sure we don't get - # any warning anymore - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', - 'name': 'xxx', 'version': 'xxx'} - cmd = self._run(metadata) - self.assertEqual(cmd._warnings, 0) - - # now with the strict mode, we should - # get an error if there are missing metadata - self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1}) - - # and of course, no error when all metadata are present - cmd = self._run(metadata, strict=1) - self.assertEqual(cmd._warnings, 0) - - # now a test with non-ASCII characters - metadata = {'url': 'xxx', 'author': '\u00c9ric', - 'author_email': 'xxx', 'name': 'xxx', - 'version': 'xxx', - 'description': 'Something about esszet \u00df', - 'long_description': 'More things about esszet \u00df'} - cmd = self._run(metadata) - self.assertEqual(cmd._warnings, 0) - - @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") - def test_check_document(self): - pkg_info, dist = self.create_dist() - cmd = check(dist) - - # let's see if it detects broken rest - broken_rest = 'title\n===\n\ntest' - msgs = cmd._check_rst_data(broken_rest) - self.assertEqual(len(msgs), 1) - - # and non-broken rest - rest = 'title\n=====\n\ntest' - msgs = cmd._check_rst_data(rest) - self.assertEqual(len(msgs), 0) - - @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") - def test_check_restructuredtext(self): - # let's see if it detects broken rest in long_description - broken_rest = 'title\n===\n\ntest' - pkg_info, dist = self.create_dist(long_description=broken_rest) - cmd = check(dist) - cmd.check_restructuredtext() - self.assertEqual(cmd._warnings, 1) - - # let's see if we have an error with strict=1 - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', - 'name': 'xxx', 'version': 'xxx', - 'long_description': broken_rest} - self.assertRaises(DistutilsSetupError, self._run, metadata, - **{'strict': 1, 'restructuredtext': 1}) - - # and non-broken rest, including a non-ASCII character to test #12114 - metadata['long_description'] = 'title\n=====\n\ntest \u00df' - cmd = self._run(metadata, strict=1, restructuredtext=1) - self.assertEqual(cmd._warnings, 0) - - # check that includes work to test #31292 - metadata['long_description'] = 'title\n=====\n\n.. include:: includetest.rst' - cmd = self._run(metadata, cwd=HERE, strict=1, restructuredtext=1) - self.assertEqual(cmd._warnings, 0) - - @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") - def test_check_restructuredtext_with_syntax_highlight(self): - # Don't fail if there is a `code` or `code-block` directive - - example_rst_docs = [] - example_rst_docs.append(textwrap.dedent("""\ - Here's some code: - - .. code:: python - - def foo(): - pass - """)) - example_rst_docs.append(textwrap.dedent("""\ - Here's some code: - - .. code-block:: python - - def foo(): - pass - """)) - - for rest_with_code in example_rst_docs: - pkg_info, dist = self.create_dist(long_description=rest_with_code) - cmd = check(dist) - cmd.check_restructuredtext() - msgs = cmd._check_rst_data(rest_with_code) - if pygments is not None: - self.assertEqual(len(msgs), 0) - else: - self.assertEqual(len(msgs), 1) - self.assertEqual( - str(msgs[0][1]), - 'Cannot analyze code. Pygments package not found.' - ) - - def test_check_all(self): - - metadata = {'url': 'xxx', 'author': 'xxx'} - self.assertRaises(DistutilsSetupError, self._run, - {}, **{'strict': 1, - 'restructuredtext': 1}) - -def test_suite(): - return unittest.makeSuite(CheckTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_clean.py b/distutils/tests/test_clean.py deleted file mode 100644 index c605afd8..00000000 --- a/distutils/tests/test_clean.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Tests for distutils.command.clean.""" -import os -import unittest - -from distutils.command.clean import clean -from distutils.tests import support -from test.support import run_unittest - -class cleanTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_simple_run(self): - pkg_dir, dist = self.create_dist() - cmd = clean(dist) - - # let's add some elements clean should remove - dirs = [(d, os.path.join(pkg_dir, d)) - for d in ('build_temp', 'build_lib', 'bdist_base', - 'build_scripts', 'build_base')] - - for name, path in dirs: - os.mkdir(path) - setattr(cmd, name, path) - if name == 'build_base': - continue - for f in ('one', 'two', 'three'): - self.write_file(os.path.join(path, f)) - - # let's run the command - cmd.all = 1 - cmd.ensure_finalized() - cmd.run() - - # make sure the files where removed - for name, path in dirs: - self.assertFalse(os.path.exists(path), - '%s was not removed' % path) - - # let's run the command again (should spit warnings but succeed) - cmd.all = 1 - cmd.ensure_finalized() - cmd.run() - -def test_suite(): - return unittest.makeSuite(cleanTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_cmd.py b/distutils/tests/test_cmd.py deleted file mode 100644 index cf5197c3..00000000 --- a/distutils/tests/test_cmd.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Tests for distutils.cmd.""" -import unittest -import os -from test.support import captured_stdout, run_unittest - -from distutils.cmd import Command -from distutils.dist import Distribution -from distutils.errors import DistutilsOptionError -from distutils import debug - -class MyCmd(Command): - def initialize_options(self): - pass - -class CommandTestCase(unittest.TestCase): - - def setUp(self): - dist = Distribution() - self.cmd = MyCmd(dist) - - def test_ensure_string_list(self): - - cmd = self.cmd - cmd.not_string_list = ['one', 2, 'three'] - cmd.yes_string_list = ['one', 'two', 'three'] - cmd.not_string_list2 = object() - cmd.yes_string_list2 = 'ok' - cmd.ensure_string_list('yes_string_list') - cmd.ensure_string_list('yes_string_list2') - - self.assertRaises(DistutilsOptionError, - cmd.ensure_string_list, 'not_string_list') - - self.assertRaises(DistutilsOptionError, - cmd.ensure_string_list, 'not_string_list2') - - cmd.option1 = 'ok,dok' - cmd.ensure_string_list('option1') - self.assertEqual(cmd.option1, ['ok', 'dok']) - - cmd.option2 = ['xxx', 'www'] - cmd.ensure_string_list('option2') - - cmd.option3 = ['ok', 2] - self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, - 'option3') - - - def test_make_file(self): - - cmd = self.cmd - - # making sure it raises when infiles is not a string or a list/tuple - self.assertRaises(TypeError, cmd.make_file, - infiles=1, outfile='', func='func', args=()) - - # making sure execute gets called properly - def _execute(func, args, exec_msg, level): - self.assertEqual(exec_msg, 'generating out from in') - cmd.force = True - cmd.execute = _execute - cmd.make_file(infiles='in', outfile='out', func='func', args=()) - - def test_dump_options(self): - - msgs = [] - def _announce(msg, level): - msgs.append(msg) - cmd = self.cmd - cmd.announce = _announce - cmd.option1 = 1 - cmd.option2 = 1 - cmd.user_options = [('option1', '', ''), ('option2', '', '')] - cmd.dump_options() - - wanted = ["command options for 'MyCmd':", ' option1 = 1', - ' option2 = 1'] - self.assertEqual(msgs, wanted) - - def test_ensure_string(self): - cmd = self.cmd - cmd.option1 = 'ok' - cmd.ensure_string('option1') - - cmd.option2 = None - cmd.ensure_string('option2', 'xxx') - self.assertTrue(hasattr(cmd, 'option2')) - - cmd.option3 = 1 - self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') - - def test_ensure_filename(self): - cmd = self.cmd - cmd.option1 = __file__ - cmd.ensure_filename('option1') - cmd.option2 = 'xxx' - self.assertRaises(DistutilsOptionError, cmd.ensure_filename, 'option2') - - def test_ensure_dirname(self): - cmd = self.cmd - cmd.option1 = os.path.dirname(__file__) or os.curdir - cmd.ensure_dirname('option1') - cmd.option2 = 'xxx' - self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') - - def test_debug_print(self): - cmd = self.cmd - with captured_stdout() as stdout: - cmd.debug_print('xxx') - stdout.seek(0) - self.assertEqual(stdout.read(), '') - - debug.DEBUG = True - try: - with captured_stdout() as stdout: - cmd.debug_print('xxx') - stdout.seek(0) - self.assertEqual(stdout.read(), 'xxx\n') - finally: - debug.DEBUG = False - -def test_suite(): - return unittest.makeSuite(CommandTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/distutils/tests/test_config.py b/distutils/tests/test_config.py deleted file mode 100644 index 344084af..00000000 --- a/distutils/tests/test_config.py +++ /dev/null @@ -1,141 +0,0 @@ -"""Tests for distutils.pypirc.pypirc.""" -import os -import unittest - -from distutils.core import PyPIRCCommand -from distutils.core import Distribution -from distutils.log import set_threshold -from distutils.log import WARN - -from distutils.tests import support -from test.support import run_unittest - -PYPIRC = """\ -[distutils] - -index-servers = - server1 - server2 - server3 - -[server1] -username:me -password:secret - -[server2] -username:meagain -password: secret -realm:acme -repository:http://another.pypi/ - -[server3] -username:cbiggles -password:yh^%#rest-of-my-password -""" - -PYPIRC_OLD = """\ -[server-login] -username:tarek -password:secret -""" - -WANTED = """\ -[distutils] -index-servers = - pypi - -[pypi] -username:tarek -password:xxx -""" - - -class BasePyPIRCCommandTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def setUp(self): - """Patches the environment.""" - super(BasePyPIRCCommandTestCase, self).setUp() - self.tmp_dir = self.mkdtemp() - os.environ['HOME'] = self.tmp_dir - os.environ['USERPROFILE'] = self.tmp_dir - self.rc = os.path.join(self.tmp_dir, '.pypirc') - self.dist = Distribution() - - class command(PyPIRCCommand): - def __init__(self, dist): - PyPIRCCommand.__init__(self, dist) - def initialize_options(self): - pass - finalize_options = initialize_options - - self._cmd = command - self.old_threshold = set_threshold(WARN) - - def tearDown(self): - """Removes the patch.""" - set_threshold(self.old_threshold) - super(BasePyPIRCCommandTestCase, self).tearDown() - - -class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase): - - def test_server_registration(self): - # This test makes sure PyPIRCCommand knows how to: - # 1. handle several sections in .pypirc - # 2. handle the old format - - # new format - self.write_file(self.rc, PYPIRC) - cmd = self._cmd(self.dist) - config = cmd._read_pypirc() - - config = list(sorted(config.items())) - waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://upload.pypi.org/legacy/'), - ('server', 'server1'), ('username', 'me')] - self.assertEqual(config, waited) - - # old format - self.write_file(self.rc, PYPIRC_OLD) - config = cmd._read_pypirc() - config = list(sorted(config.items())) - waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'https://upload.pypi.org/legacy/'), - ('server', 'server-login'), ('username', 'tarek')] - self.assertEqual(config, waited) - - def test_server_empty_registration(self): - cmd = self._cmd(self.dist) - rc = cmd._get_rc_file() - self.assertFalse(os.path.exists(rc)) - cmd._store_pypirc('tarek', 'xxx') - self.assertTrue(os.path.exists(rc)) - f = open(rc) - try: - content = f.read() - self.assertEqual(content, WANTED) - finally: - f.close() - - def test_config_interpolation(self): - # using the % character in .pypirc should not raise an error (#20120) - self.write_file(self.rc, PYPIRC) - cmd = self._cmd(self.dist) - cmd.repository = 'server3' - config = cmd._read_pypirc() - - config = list(sorted(config.items())) - waited = [('password', 'yh^%#rest-of-my-password'), ('realm', 'pypi'), - ('repository', 'https://upload.pypi.org/legacy/'), - ('server', 'server3'), ('username', 'cbiggles')] - self.assertEqual(config, waited) - - -def test_suite(): - return unittest.makeSuite(PyPIRCCommandTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_config_cmd.py b/distutils/tests/test_config_cmd.py deleted file mode 100644 index 4cd9a6b9..00000000 --- a/distutils/tests/test_config_cmd.py +++ /dev/null @@ -1,98 +0,0 @@ -"""Tests for distutils.command.config.""" -import unittest -import os -import sys -from test.support import run_unittest - -from .py35compat import missing_compiler_executable - -from distutils.command.config import dump_file, config -from distutils.tests import support -from distutils import log - -class ConfigTestCase(support.LoggingSilencer, - support.TempdirManager, - unittest.TestCase): - - def _info(self, msg, *args): - for line in msg.splitlines(): - self._logs.append(line) - - def setUp(self): - super(ConfigTestCase, self).setUp() - self._logs = [] - self.old_log = log.info - log.info = self._info - - def tearDown(self): - log.info = self.old_log - super(ConfigTestCase, self).tearDown() - - def test_dump_file(self): - this_file = os.path.splitext(__file__)[0] + '.py' - f = open(this_file) - try: - numlines = len(f.readlines()) - finally: - f.close() - - dump_file(this_file, 'I am the header') - self.assertEqual(len(self._logs), numlines+1) - - @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") - def test_search_cpp(self): - cmd = missing_compiler_executable(['preprocessor']) - if cmd is not None: - self.skipTest('The %r command is not found' % cmd) - pkg_dir, dist = self.create_dist() - cmd = config(dist) - cmd._check_compiler() - compiler = cmd.compiler - if sys.platform[:3] == "aix" and "xlc" in compiler.preprocessor[0].lower(): - self.skipTest('xlc: The -E option overrides the -P, -o, and -qsyntaxonly options') - - # simple pattern searches - match = cmd.search_cpp(pattern='xxx', body='/* xxx */') - self.assertEqual(match, 0) - - match = cmd.search_cpp(pattern='_configtest', body='/* xxx */') - self.assertEqual(match, 1) - - def test_finalize_options(self): - # finalize_options does a bit of transformation - # on options - pkg_dir, dist = self.create_dist() - cmd = config(dist) - cmd.include_dirs = 'one%stwo' % os.pathsep - cmd.libraries = 'one' - cmd.library_dirs = 'three%sfour' % os.pathsep - cmd.ensure_finalized() - - self.assertEqual(cmd.include_dirs, ['one', 'two']) - self.assertEqual(cmd.libraries, ['one']) - self.assertEqual(cmd.library_dirs, ['three', 'four']) - - def test_clean(self): - # _clean removes files - tmp_dir = self.mkdtemp() - f1 = os.path.join(tmp_dir, 'one') - f2 = os.path.join(tmp_dir, 'two') - - self.write_file(f1, 'xxx') - self.write_file(f2, 'xxx') - - for f in (f1, f2): - self.assertTrue(os.path.exists(f)) - - pkg_dir, dist = self.create_dist() - cmd = config(dist) - cmd._clean(f1, f2) - - for f in (f1, f2): - self.assertFalse(os.path.exists(f)) - -def test_suite(): - return unittest.makeSuite(ConfigTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_core.py b/distutils/tests/test_core.py deleted file mode 100644 index 27ce7324..00000000 --- a/distutils/tests/test_core.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Tests for distutils.core.""" - -import io -import distutils.core -import os -import shutil -import sys -import test.support -from test.support import captured_stdout, run_unittest -import unittest -from distutils.tests import support -from distutils import log - -# setup script that uses __file__ -setup_using___file__ = """\ - -__file__ - -from distutils.core import setup -setup() -""" - -setup_prints_cwd = """\ - -import os -print(os.getcwd()) - -from distutils.core import setup -setup() -""" - -setup_does_nothing = """\ -from distutils.core import setup -setup() -""" - - -setup_defines_subclass = """\ -from distutils.core import setup -from distutils.command.install import install as _install - -class install(_install): - sub_commands = _install.sub_commands + ['cmd'] - -setup(cmdclass={'install': install}) -""" - -class CoreTestCase(support.EnvironGuard, unittest.TestCase): - - def setUp(self): - super(CoreTestCase, self).setUp() - self.old_stdout = sys.stdout - self.cleanup_testfn() - self.old_argv = sys.argv, sys.argv[:] - self.addCleanup(log.set_threshold, log._global_log.threshold) - - def tearDown(self): - sys.stdout = self.old_stdout - self.cleanup_testfn() - sys.argv = self.old_argv[0] - sys.argv[:] = self.old_argv[1] - super(CoreTestCase, self).tearDown() - - def cleanup_testfn(self): - path = test.support.TESTFN - if os.path.isfile(path): - os.remove(path) - elif os.path.isdir(path): - shutil.rmtree(path) - - def write_setup(self, text, path=test.support.TESTFN): - f = open(path, "w") - try: - f.write(text) - finally: - f.close() - return path - - def test_run_setup_provides_file(self): - # Make sure the script can use __file__; if that's missing, the test - # setup.py script will raise NameError. - distutils.core.run_setup( - self.write_setup(setup_using___file__)) - - def test_run_setup_preserves_sys_argv(self): - # Make sure run_setup does not clobber sys.argv - argv_copy = sys.argv.copy() - distutils.core.run_setup( - self.write_setup(setup_does_nothing)) - self.assertEqual(sys.argv, argv_copy) - - def test_run_setup_defines_subclass(self): - # Make sure the script can use __file__; if that's missing, the test - # setup.py script will raise NameError. - dist = distutils.core.run_setup( - self.write_setup(setup_defines_subclass)) - install = dist.get_command_obj('install') - self.assertIn('cmd', install.sub_commands) - - def test_run_setup_uses_current_dir(self): - # This tests that the setup script is run with the current directory - # as its own current directory; this was temporarily broken by a - # previous patch when TESTFN did not use the current directory. - sys.stdout = io.StringIO() - cwd = os.getcwd() - - # Create a directory and write the setup.py file there: - os.mkdir(test.support.TESTFN) - setup_py = os.path.join(test.support.TESTFN, "setup.py") - distutils.core.run_setup( - self.write_setup(setup_prints_cwd, path=setup_py)) - - output = sys.stdout.getvalue() - if output.endswith("\n"): - output = output[:-1] - self.assertEqual(cwd, output) - - def test_debug_mode(self): - # this covers the code called when DEBUG is set - sys.argv = ['setup.py', '--name'] - with captured_stdout() as stdout: - distutils.core.setup(name='bar') - stdout.seek(0) - self.assertEqual(stdout.read(), 'bar\n') - - distutils.core.DEBUG = True - try: - with captured_stdout() as stdout: - distutils.core.setup(name='bar') - finally: - distutils.core.DEBUG = False - stdout.seek(0) - wanted = "options (after parsing config files):\n" - self.assertEqual(stdout.readlines()[0], wanted) - -def test_suite(): - return unittest.makeSuite(CoreTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_cygwinccompiler.py b/distutils/tests/test_cygwinccompiler.py deleted file mode 100644 index 9dc869de..00000000 --- a/distutils/tests/test_cygwinccompiler.py +++ /dev/null @@ -1,154 +0,0 @@ -"""Tests for distutils.cygwinccompiler.""" -import unittest -import sys -import os -from io import BytesIO -from test.support import run_unittest - -from distutils import cygwinccompiler -from distutils.cygwinccompiler import (check_config_h, - CONFIG_H_OK, CONFIG_H_NOTOK, - CONFIG_H_UNCERTAIN, get_versions, - get_msvcr) -from distutils.tests import support - -class FakePopen(object): - test_class = None - - def __init__(self, cmd, shell, stdout): - self.cmd = cmd.split()[0] - exes = self.test_class._exes - if self.cmd in exes: - # issue #6438 in Python 3.x, Popen returns bytes - self.stdout = BytesIO(exes[self.cmd]) - else: - self.stdout = os.popen(cmd, 'r') - - -class CygwinCCompilerTestCase(support.TempdirManager, - unittest.TestCase): - - def setUp(self): - super(CygwinCCompilerTestCase, self).setUp() - self.version = sys.version - self.python_h = os.path.join(self.mkdtemp(), 'python.h') - from distutils import sysconfig - self.old_get_config_h_filename = sysconfig.get_config_h_filename - sysconfig.get_config_h_filename = self._get_config_h_filename - self.old_find_executable = cygwinccompiler.find_executable - cygwinccompiler.find_executable = self._find_executable - self._exes = {} - self.old_popen = cygwinccompiler.Popen - FakePopen.test_class = self - cygwinccompiler.Popen = FakePopen - - def tearDown(self): - sys.version = self.version - from distutils import sysconfig - sysconfig.get_config_h_filename = self.old_get_config_h_filename - cygwinccompiler.find_executable = self.old_find_executable - cygwinccompiler.Popen = self.old_popen - super(CygwinCCompilerTestCase, self).tearDown() - - def _get_config_h_filename(self): - return self.python_h - - def _find_executable(self, name): - if name in self._exes: - return name - return None - - def test_check_config_h(self): - - # check_config_h looks for "GCC" in sys.version first - # returns CONFIG_H_OK if found - sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' - '4.0.1 (Apple Computer, Inc. build 5370)]') - - self.assertEqual(check_config_h()[0], CONFIG_H_OK) - - # then it tries to see if it can find "__GNUC__" in pyconfig.h - sys.version = 'something without the *CC word' - - # if the file doesn't exist it returns CONFIG_H_UNCERTAIN - self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN) - - # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK - self.write_file(self.python_h, 'xxx') - self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK) - - # and CONFIG_H_OK if __GNUC__ is found - self.write_file(self.python_h, 'xxx __GNUC__ xxx') - self.assertEqual(check_config_h()[0], CONFIG_H_OK) - - def test_get_versions(self): - - # get_versions calls distutils.spawn.find_executable on - # 'gcc', 'ld' and 'dllwrap' - self.assertEqual(get_versions(), (None, None, None)) - - # Let's fake we have 'gcc' and it returns '3.4.5' - self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' - res = get_versions() - self.assertEqual(str(res[0]), '3.4.5') - - # and let's see what happens when the version - # doesn't match the regular expression - # (\d+\.\d+(\.\d+)*) - self._exes['gcc'] = b'very strange output' - res = get_versions() - self.assertEqual(res[0], None) - - # same thing for ld - self._exes['ld'] = b'GNU ld version 2.17.50 20060824' - res = get_versions() - self.assertEqual(str(res[1]), '2.17.50') - self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' - res = get_versions() - self.assertEqual(res[1], None) - - # and dllwrap - self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' - res = get_versions() - self.assertEqual(str(res[2]), '2.17.50') - self._exes['dllwrap'] = b'Cheese Wrap' - res = get_versions() - self.assertEqual(res[2], None) - - def test_get_msvcr(self): - - # none - sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' - '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEqual(get_msvcr(), None) - - # MSVC 7.0 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1300 32 bits (Intel)]') - self.assertEqual(get_msvcr(), ['msvcr70']) - - # MSVC 7.1 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1310 32 bits (Intel)]') - self.assertEqual(get_msvcr(), ['msvcr71']) - - # VS2005 / MSVC 8.0 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1400 32 bits (Intel)]') - self.assertEqual(get_msvcr(), ['msvcr80']) - - # VS2008 / MSVC 9.0 - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1500 32 bits (Intel)]') - self.assertEqual(get_msvcr(), ['msvcr90']) - - # unknown - sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' - '[MSC v.1999 32 bits (Intel)]') - self.assertRaises(ValueError, get_msvcr) - -def test_suite(): - return unittest.makeSuite(CygwinCCompilerTestCase) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/distutils/tests/test_dep_util.py b/distutils/tests/test_dep_util.py deleted file mode 100644 index c6fae39c..00000000 --- a/distutils/tests/test_dep_util.py +++ /dev/null @@ -1,80 +0,0 @@ -"""Tests for distutils.dep_util.""" -import unittest -import os - -from distutils.dep_util import newer, newer_pairwise, newer_group -from distutils.errors import DistutilsFileError -from distutils.tests import support -from test.support import run_unittest - -class DepUtilTestCase(support.TempdirManager, unittest.TestCase): - - def test_newer(self): - - tmpdir = self.mkdtemp() - new_file = os.path.join(tmpdir, 'new') - old_file = os.path.abspath(__file__) - - # Raise DistutilsFileError if 'new_file' does not exist. - self.assertRaises(DistutilsFileError, newer, new_file, old_file) - - # Return true if 'new_file' exists and is more recently modified than - # 'old_file', or if 'new_file' exists and 'old_file' doesn't. - self.write_file(new_file) - self.assertTrue(newer(new_file, 'I_dont_exist')) - self.assertTrue(newer(new_file, old_file)) - - # Return false if both exist and 'old_file' is the same age or younger - # than 'new_file'. - self.assertFalse(newer(old_file, new_file)) - - def test_newer_pairwise(self): - tmpdir = self.mkdtemp() - sources = os.path.join(tmpdir, 'sources') - targets = os.path.join(tmpdir, 'targets') - os.mkdir(sources) - os.mkdir(targets) - one = os.path.join(sources, 'one') - two = os.path.join(sources, 'two') - three = os.path.abspath(__file__) # I am the old file - four = os.path.join(targets, 'four') - self.write_file(one) - self.write_file(two) - self.write_file(four) - - self.assertEqual(newer_pairwise([one, two], [three, four]), - ([one],[three])) - - def test_newer_group(self): - tmpdir = self.mkdtemp() - sources = os.path.join(tmpdir, 'sources') - os.mkdir(sources) - one = os.path.join(sources, 'one') - two = os.path.join(sources, 'two') - three = os.path.join(sources, 'three') - old_file = os.path.abspath(__file__) - - # return true if 'old_file' is out-of-date with respect to any file - # listed in 'sources'. - self.write_file(one) - self.write_file(two) - self.write_file(three) - self.assertTrue(newer_group([one, two, three], old_file)) - self.assertFalse(newer_group([one, two, old_file], three)) - - # missing handling - os.remove(one) - self.assertRaises(OSError, newer_group, [one, two, old_file], three) - - self.assertFalse(newer_group([one, two, old_file], three, - missing='ignore')) - - self.assertTrue(newer_group([one, two, old_file], three, - missing='newer')) - - -def test_suite(): - return unittest.makeSuite(DepUtilTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_dir_util.py b/distutils/tests/test_dir_util.py deleted file mode 100644 index d436cf83..00000000 --- a/distutils/tests/test_dir_util.py +++ /dev/null @@ -1,139 +0,0 @@ -"""Tests for distutils.dir_util.""" -import unittest -import os -import stat -import sys -from unittest.mock import patch - -from distutils import dir_util, errors -from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, - ensure_relative) - -from distutils import log -from distutils.tests import support -from test.support import run_unittest - - -class DirUtilTestCase(support.TempdirManager, unittest.TestCase): - - def _log(self, msg, *args): - if len(args) > 0: - self._logs.append(msg % args) - else: - self._logs.append(msg) - - def setUp(self): - super(DirUtilTestCase, self).setUp() - self._logs = [] - tmp_dir = self.mkdtemp() - self.root_target = os.path.join(tmp_dir, 'deep') - self.target = os.path.join(self.root_target, 'here') - self.target2 = os.path.join(tmp_dir, 'deep2') - self.old_log = log.info - log.info = self._log - - def tearDown(self): - log.info = self.old_log - super(DirUtilTestCase, self).tearDown() - - def test_mkpath_remove_tree_verbosity(self): - - mkpath(self.target, verbose=0) - wanted = [] - self.assertEqual(self._logs, wanted) - remove_tree(self.root_target, verbose=0) - - mkpath(self.target, verbose=1) - wanted = ['creating %s' % self.root_target, - 'creating %s' % self.target] - self.assertEqual(self._logs, wanted) - self._logs = [] - - remove_tree(self.root_target, verbose=1) - wanted = ["removing '%s' (and everything under it)" % self.root_target] - self.assertEqual(self._logs, wanted) - - @unittest.skipIf(sys.platform.startswith('win'), - "This test is only appropriate for POSIX-like systems.") - def test_mkpath_with_custom_mode(self): - # Get and set the current umask value for testing mode bits. - umask = os.umask(0o002) - os.umask(umask) - mkpath(self.target, 0o700) - self.assertEqual( - stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) - mkpath(self.target2, 0o555) - self.assertEqual( - stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) - - def test_create_tree_verbosity(self): - - create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) - self.assertEqual(self._logs, []) - remove_tree(self.root_target, verbose=0) - - wanted = ['creating %s' % self.root_target] - create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) - self.assertEqual(self._logs, wanted) - - remove_tree(self.root_target, verbose=0) - - def test_copy_tree_verbosity(self): - - mkpath(self.target, verbose=0) - - copy_tree(self.target, self.target2, verbose=0) - self.assertEqual(self._logs, []) - - remove_tree(self.root_target, verbose=0) - - mkpath(self.target, verbose=0) - a_file = os.path.join(self.target, 'ok.txt') - with open(a_file, 'w') as f: - f.write('some content') - - wanted = ['copying %s -> %s' % (a_file, self.target2)] - copy_tree(self.target, self.target2, verbose=1) - self.assertEqual(self._logs, wanted) - - remove_tree(self.root_target, verbose=0) - remove_tree(self.target2, verbose=0) - - def test_copy_tree_skips_nfs_temp_files(self): - mkpath(self.target, verbose=0) - - a_file = os.path.join(self.target, 'ok.txt') - nfs_file = os.path.join(self.target, '.nfs123abc') - for f in a_file, nfs_file: - with open(f, 'w') as fh: - fh.write('some content') - - copy_tree(self.target, self.target2) - self.assertEqual(os.listdir(self.target2), ['ok.txt']) - - remove_tree(self.root_target, verbose=0) - remove_tree(self.target2, verbose=0) - - def test_ensure_relative(self): - if os.sep == '/': - self.assertEqual(ensure_relative('/home/foo'), 'home/foo') - self.assertEqual(ensure_relative('some/path'), 'some/path') - else: # \\ - self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') - self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') - - def test_copy_tree_exception_in_listdir(self): - """ - An exception in listdir should raise a DistutilsFileError - """ - with patch("os.listdir", side_effect=OSError()), \ - self.assertRaises(errors.DistutilsFileError): - src = self.tempdirs[-1] - dir_util.copy_tree(src, None) - - -def test_suite(): - return unittest.makeSuite(DirUtilTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_dist.py b/distutils/tests/test_dist.py deleted file mode 100644 index d431085b..00000000 --- a/distutils/tests/test_dist.py +++ /dev/null @@ -1,532 +0,0 @@ -"""Tests for distutils.dist.""" -import os -import io -import sys -import unittest -import warnings -import textwrap - -from unittest import mock - -from distutils.dist import Distribution, fix_help_options -from distutils.cmd import Command - -from test.support import ( - TESTFN, captured_stdout, captured_stderr, run_unittest -) -from distutils.tests import support -from distutils import log - - -class test_dist(Command): - """Sample distutils extension command.""" - - user_options = [ - ("sample-option=", "S", "help text"), - ] - - def initialize_options(self): - self.sample_option = None - - -class TestDistribution(Distribution): - """Distribution subclasses that avoids the default search for - configuration files. - - The ._config_files attribute must be set before - .parse_config_files() is called. - """ - - def find_config_files(self): - return self._config_files - - -class DistributionTestCase(support.LoggingSilencer, - support.TempdirManager, - support.EnvironGuard, - unittest.TestCase): - - def setUp(self): - super(DistributionTestCase, self).setUp() - self.argv = sys.argv, sys.argv[:] - del sys.argv[1:] - - def tearDown(self): - sys.argv = self.argv[0] - sys.argv[:] = self.argv[1] - super(DistributionTestCase, self).tearDown() - - def create_distribution(self, configfiles=()): - d = TestDistribution() - d._config_files = configfiles - d.parse_config_files() - d.parse_command_line() - return d - - def test_command_packages_unspecified(self): - sys.argv.append("build") - d = self.create_distribution() - self.assertEqual(d.get_command_packages(), ["distutils.command"]) - - def test_command_packages_cmdline(self): - from distutils.tests.test_dist import test_dist - sys.argv.extend(["--command-packages", - "foo.bar,distutils.tests", - "test_dist", - "-Ssometext", - ]) - d = self.create_distribution() - # let's actually try to load our test command: - self.assertEqual(d.get_command_packages(), - ["distutils.command", "foo.bar", "distutils.tests"]) - cmd = d.get_command_obj("test_dist") - self.assertIsInstance(cmd, test_dist) - self.assertEqual(cmd.sample_option, "sometext") - - @unittest.skipIf( - 'distutils' not in Distribution.parse_config_files.__module__, - 'Cannot test when virtualenv has monkey-patched Distribution.', - ) - def test_venv_install_options(self): - sys.argv.append("install") - self.addCleanup(os.unlink, TESTFN) - - fakepath = '/somedir' - - with open(TESTFN, "w") as f: - print(("[install]\n" - "install-base = {0}\n" - "install-platbase = {0}\n" - "install-lib = {0}\n" - "install-platlib = {0}\n" - "install-purelib = {0}\n" - "install-headers = {0}\n" - "install-scripts = {0}\n" - "install-data = {0}\n" - "prefix = {0}\n" - "exec-prefix = {0}\n" - "home = {0}\n" - "user = {0}\n" - "root = {0}").format(fakepath), file=f) - - # Base case: Not in a Virtual Environment - with mock.patch.multiple(sys, prefix='/a', base_prefix='/a') as values: - d = self.create_distribution([TESTFN]) - - option_tuple = (TESTFN, fakepath) - - result_dict = { - 'install_base': option_tuple, - 'install_platbase': option_tuple, - 'install_lib': option_tuple, - 'install_platlib': option_tuple, - 'install_purelib': option_tuple, - 'install_headers': option_tuple, - 'install_scripts': option_tuple, - 'install_data': option_tuple, - 'prefix': option_tuple, - 'exec_prefix': option_tuple, - 'home': option_tuple, - 'user': option_tuple, - 'root': option_tuple, - } - - self.assertEqual( - sorted(d.command_options.get('install').keys()), - sorted(result_dict.keys())) - - for (key, value) in d.command_options.get('install').items(): - self.assertEqual(value, result_dict[key]) - - # Test case: In a Virtual Environment - with mock.patch.multiple(sys, prefix='/a', base_prefix='/b') as values: - d = self.create_distribution([TESTFN]) - - for key in result_dict.keys(): - self.assertNotIn(key, d.command_options.get('install', {})) - - def test_command_packages_configfile(self): - sys.argv.append("build") - self.addCleanup(os.unlink, TESTFN) - f = open(TESTFN, "w") - try: - print("[global]", file=f) - print("command_packages = foo.bar, splat", file=f) - finally: - f.close() - - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), - ["distutils.command", "foo.bar", "splat"]) - - # ensure command line overrides config: - sys.argv[1:] = ["--command-packages", "spork", "build"] - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), - ["distutils.command", "spork"]) - - # Setting --command-packages to '' should cause the default to - # be used even if a config file specified something else: - sys.argv[1:] = ["--command-packages", "", "build"] - d = self.create_distribution([TESTFN]) - self.assertEqual(d.get_command_packages(), ["distutils.command"]) - - def test_empty_options(self): - # an empty options dictionary should not stay in the - # list of attributes - - # catching warnings - warns = [] - - def _warn(msg): - warns.append(msg) - - self.addCleanup(setattr, warnings, 'warn', warnings.warn) - warnings.warn = _warn - dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx', - 'version': 'xxx', 'url': 'xxxx', - 'options': {}}) - - self.assertEqual(len(warns), 0) - self.assertNotIn('options', dir(dist)) - - def test_finalize_options(self): - attrs = {'keywords': 'one,two', - 'platforms': 'one,two'} - - dist = Distribution(attrs=attrs) - dist.finalize_options() - - # finalize_option splits platforms and keywords - self.assertEqual(dist.metadata.platforms, ['one', 'two']) - self.assertEqual(dist.metadata.keywords, ['one', 'two']) - - attrs = {'keywords': 'foo bar', - 'platforms': 'foo bar'} - dist = Distribution(attrs=attrs) - dist.finalize_options() - self.assertEqual(dist.metadata.platforms, ['foo bar']) - self.assertEqual(dist.metadata.keywords, ['foo bar']) - - def test_get_command_packages(self): - dist = Distribution() - self.assertEqual(dist.command_packages, None) - cmds = dist.get_command_packages() - self.assertEqual(cmds, ['distutils.command']) - self.assertEqual(dist.command_packages, - ['distutils.command']) - - dist.command_packages = 'one,two' - cmds = dist.get_command_packages() - self.assertEqual(cmds, ['distutils.command', 'one', 'two']) - - def test_announce(self): - # make sure the level is known - dist = Distribution() - args = ('ok',) - kwargs = {'level': 'ok2'} - self.assertRaises(ValueError, dist.announce, args, kwargs) - - - def test_find_config_files_disable(self): - # Ticket #1180: Allow user to disable their home config file. - temp_home = self.mkdtemp() - if os.name == 'posix': - user_filename = os.path.join(temp_home, ".pydistutils.cfg") - else: - user_filename = os.path.join(temp_home, "pydistutils.cfg") - - with open(user_filename, 'w') as f: - f.write('[distutils]\n') - - def _expander(path): - return temp_home - - old_expander = os.path.expanduser - os.path.expanduser = _expander - try: - d = Distribution() - all_files = d.find_config_files() - - d = Distribution(attrs={'script_args': ['--no-user-cfg']}) - files = d.find_config_files() - finally: - os.path.expanduser = old_expander - - # make sure --no-user-cfg disables the user cfg file - self.assertEqual(len(all_files)-1, len(files)) - -class MetadataTestCase(support.TempdirManager, support.EnvironGuard, - unittest.TestCase): - - def setUp(self): - super(MetadataTestCase, self).setUp() - self.argv = sys.argv, sys.argv[:] - - def tearDown(self): - sys.argv = self.argv[0] - sys.argv[:] = self.argv[1] - super(MetadataTestCase, self).tearDown() - - def format_metadata(self, dist): - sio = io.StringIO() - dist.metadata.write_pkg_file(sio) - return sio.getvalue() - - def test_simple_metadata(self): - attrs = {"name": "package", - "version": "1.0"} - dist = Distribution(attrs) - meta = self.format_metadata(dist) - self.assertIn("Metadata-Version: 1.0", meta) - self.assertNotIn("provides:", meta.lower()) - self.assertNotIn("requires:", meta.lower()) - self.assertNotIn("obsoletes:", meta.lower()) - - def test_provides(self): - attrs = {"name": "package", - "version": "1.0", - "provides": ["package", "package.sub"]} - dist = Distribution(attrs) - self.assertEqual(dist.metadata.get_provides(), - ["package", "package.sub"]) - self.assertEqual(dist.get_provides(), - ["package", "package.sub"]) - meta = self.format_metadata(dist) - self.assertIn("Metadata-Version: 1.1", meta) - self.assertNotIn("requires:", meta.lower()) - self.assertNotIn("obsoletes:", meta.lower()) - - def test_provides_illegal(self): - self.assertRaises(ValueError, Distribution, - {"name": "package", - "version": "1.0", - "provides": ["my.pkg (splat)"]}) - - def test_requires(self): - attrs = {"name": "package", - "version": "1.0", - "requires": ["other", "another (==1.0)"]} - dist = Distribution(attrs) - self.assertEqual(dist.metadata.get_requires(), - ["other", "another (==1.0)"]) - self.assertEqual(dist.get_requires(), - ["other", "another (==1.0)"]) - meta = self.format_metadata(dist) - self.assertIn("Metadata-Version: 1.1", meta) - self.assertNotIn("provides:", meta.lower()) - self.assertIn("Requires: other", meta) - self.assertIn("Requires: another (==1.0)", meta) - self.assertNotIn("obsoletes:", meta.lower()) - - def test_requires_illegal(self): - self.assertRaises(ValueError, Distribution, - {"name": "package", - "version": "1.0", - "requires": ["my.pkg (splat)"]}) - - def test_requires_to_list(self): - attrs = {"name": "package", - "requires": iter(["other"])} - dist = Distribution(attrs) - self.assertIsInstance(dist.metadata.requires, list) - - - def test_obsoletes(self): - attrs = {"name": "package", - "version": "1.0", - "obsoletes": ["other", "another (<1.0)"]} - dist = Distribution(attrs) - self.assertEqual(dist.metadata.get_obsoletes(), - ["other", "another (<1.0)"]) - self.assertEqual(dist.get_obsoletes(), - ["other", "another (<1.0)"]) - meta = self.format_metadata(dist) - self.assertIn("Metadata-Version: 1.1", meta) - self.assertNotIn("provides:", meta.lower()) - self.assertNotIn("requires:", meta.lower()) - self.assertIn("Obsoletes: other", meta) - self.assertIn("Obsoletes: another (<1.0)", meta) - - def test_obsoletes_illegal(self): - self.assertRaises(ValueError, Distribution, - {"name": "package", - "version": "1.0", - "obsoletes": ["my.pkg (splat)"]}) - - def test_obsoletes_to_list(self): - attrs = {"name": "package", - "obsoletes": iter(["other"])} - dist = Distribution(attrs) - self.assertIsInstance(dist.metadata.obsoletes, list) - - def test_classifier(self): - attrs = {'name': 'Boa', 'version': '3.0', - 'classifiers': ['Programming Language :: Python :: 3']} - dist = Distribution(attrs) - self.assertEqual(dist.get_classifiers(), - ['Programming Language :: Python :: 3']) - meta = self.format_metadata(dist) - self.assertIn('Metadata-Version: 1.1', meta) - - def test_classifier_invalid_type(self): - attrs = {'name': 'Boa', 'version': '3.0', - 'classifiers': ('Programming Language :: Python :: 3',)} - with captured_stderr() as error: - d = Distribution(attrs) - # should have warning about passing a non-list - self.assertIn('should be a list', error.getvalue()) - # should be converted to a list - self.assertIsInstance(d.metadata.classifiers, list) - self.assertEqual(d.metadata.classifiers, - list(attrs['classifiers'])) - - def test_keywords(self): - attrs = {'name': 'Monty', 'version': '1.0', - 'keywords': ['spam', 'eggs', 'life of brian']} - dist = Distribution(attrs) - self.assertEqual(dist.get_keywords(), - ['spam', 'eggs', 'life of brian']) - - def test_keywords_invalid_type(self): - attrs = {'name': 'Monty', 'version': '1.0', - 'keywords': ('spam', 'eggs', 'life of brian')} - with captured_stderr() as error: - d = Distribution(attrs) - # should have warning about passing a non-list - self.assertIn('should be a list', error.getvalue()) - # should be converted to a list - self.assertIsInstance(d.metadata.keywords, list) - self.assertEqual(d.metadata.keywords, list(attrs['keywords'])) - - def test_platforms(self): - attrs = {'name': 'Monty', 'version': '1.0', - 'platforms': ['GNU/Linux', 'Some Evil Platform']} - dist = Distribution(attrs) - self.assertEqual(dist.get_platforms(), - ['GNU/Linux', 'Some Evil Platform']) - - def test_platforms_invalid_types(self): - attrs = {'name': 'Monty', 'version': '1.0', - 'platforms': ('GNU/Linux', 'Some Evil Platform')} - with captured_stderr() as error: - d = Distribution(attrs) - # should have warning about passing a non-list - self.assertIn('should be a list', error.getvalue()) - # should be converted to a list - self.assertIsInstance(d.metadata.platforms, list) - self.assertEqual(d.metadata.platforms, list(attrs['platforms'])) - - def test_download_url(self): - attrs = {'name': 'Boa', 'version': '3.0', - 'download_url': 'http://example.org/boa'} - dist = Distribution(attrs) - meta = self.format_metadata(dist) - self.assertIn('Metadata-Version: 1.1', meta) - - def test_long_description(self): - long_desc = textwrap.dedent("""\ - example:: - We start here - and continue here - and end here.""") - attrs = {"name": "package", - "version": "1.0", - "long_description": long_desc} - - dist = Distribution(attrs) - meta = self.format_metadata(dist) - meta = meta.replace('\n' + 8 * ' ', '\n') - self.assertIn(long_desc, meta) - - def test_custom_pydistutils(self): - # fixes #2166 - # make sure pydistutils.cfg is found - if os.name == 'posix': - user_filename = ".pydistutils.cfg" - else: - user_filename = "pydistutils.cfg" - - temp_dir = self.mkdtemp() - user_filename = os.path.join(temp_dir, user_filename) - f = open(user_filename, 'w') - try: - f.write('.') - finally: - f.close() - - try: - dist = Distribution() - - # linux-style - if sys.platform in ('linux', 'darwin'): - os.environ['HOME'] = temp_dir - files = dist.find_config_files() - self.assertIn(user_filename, files) - - # win32-style - if sys.platform == 'win32': - # home drive should be found - os.environ['USERPROFILE'] = temp_dir - files = dist.find_config_files() - self.assertIn(user_filename, files, - '%r not found in %r' % (user_filename, files)) - finally: - os.remove(user_filename) - - def test_fix_help_options(self): - help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] - fancy_options = fix_help_options(help_tuples) - self.assertEqual(fancy_options[0], ('a', 'b', 'c')) - self.assertEqual(fancy_options[1], (1, 2, 3)) - - def test_show_help(self): - # smoke test, just makes sure some help is displayed - self.addCleanup(log.set_threshold, log._global_log.threshold) - dist = Distribution() - sys.argv = [] - dist.help = 1 - dist.script_name = 'setup.py' - with captured_stdout() as s: - dist.parse_command_line() - - output = [line for line in s.getvalue().split('\n') - if line.strip() != ''] - self.assertTrue(output) - - - def test_read_metadata(self): - attrs = {"name": "package", - "version": "1.0", - "long_description": "desc", - "description": "xxx", - "download_url": "http://example.com", - "keywords": ['one', 'two'], - "requires": ['foo']} - - dist = Distribution(attrs) - metadata = dist.metadata - - # write it then reloads it - PKG_INFO = io.StringIO() - metadata.write_pkg_file(PKG_INFO) - PKG_INFO.seek(0) - metadata.read_pkg_file(PKG_INFO) - - self.assertEqual(metadata.name, "package") - self.assertEqual(metadata.version, "1.0") - self.assertEqual(metadata.description, "xxx") - self.assertEqual(metadata.download_url, 'http://example.com') - self.assertEqual(metadata.keywords, ['one', 'two']) - self.assertEqual(metadata.platforms, ['UNKNOWN']) - self.assertEqual(metadata.obsoletes, None) - self.assertEqual(metadata.requires, ['foo']) - -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(DistributionTestCase)) - suite.addTest(unittest.makeSuite(MetadataTestCase)) - return suite - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_extension.py b/distutils/tests/test_extension.py deleted file mode 100644 index e35f2738..00000000 --- a/distutils/tests/test_extension.py +++ /dev/null @@ -1,69 +0,0 @@ -"""Tests for distutils.extension.""" -import unittest -import os -import warnings - -from test.support import check_warnings, run_unittest -from distutils.extension import read_setup_file, Extension - -class ExtensionTestCase(unittest.TestCase): - - def test_read_setup_file(self): - # trying to read a Setup file - # (sample extracted from the PyGame project) - setup = os.path.join(os.path.dirname(__file__), 'Setup.sample') - - exts = read_setup_file(setup) - names = [ext.name for ext in exts] - names.sort() - - # here are the extensions read_setup_file should have created - # out of the file - wanted = ['_arraysurfarray', '_camera', '_numericsndarray', - '_numericsurfarray', 'base', 'bufferproxy', 'cdrom', - 'color', 'constants', 'display', 'draw', 'event', - 'fastevent', 'font', 'gfxdraw', 'image', 'imageext', - 'joystick', 'key', 'mask', 'mixer', 'mixer_music', - 'mouse', 'movie', 'overlay', 'pixelarray', 'pypm', - 'rect', 'rwobject', 'scrap', 'surface', 'surflock', - 'time', 'transform'] - - self.assertEqual(names, wanted) - - def test_extension_init(self): - # the first argument, which is the name, must be a string - self.assertRaises(AssertionError, Extension, 1, []) - ext = Extension('name', []) - self.assertEqual(ext.name, 'name') - - # the second argument, which is the list of files, must - # be a list of strings - self.assertRaises(AssertionError, Extension, 'name', 'file') - self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) - ext = Extension('name', ['file1', 'file2']) - self.assertEqual(ext.sources, ['file1', 'file2']) - - # others arguments have defaults - for attr in ('include_dirs', 'define_macros', 'undef_macros', - 'library_dirs', 'libraries', 'runtime_library_dirs', - 'extra_objects', 'extra_compile_args', 'extra_link_args', - 'export_symbols', 'swig_opts', 'depends'): - self.assertEqual(getattr(ext, attr), []) - - self.assertEqual(ext.language, None) - self.assertEqual(ext.optional, None) - - # if there are unknown keyword options, warn about them - with check_warnings() as w: - warnings.simplefilter('always') - ext = Extension('name', ['file1', 'file2'], chic=True) - - self.assertEqual(len(w.warnings), 1) - self.assertEqual(str(w.warnings[0].message), - "Unknown Extension options: 'chic'") - -def test_suite(): - return unittest.makeSuite(ExtensionTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_file_util.py b/distutils/tests/test_file_util.py deleted file mode 100644 index a4e2d025..00000000 --- a/distutils/tests/test_file_util.py +++ /dev/null @@ -1,122 +0,0 @@ -"""Tests for distutils.file_util.""" -import unittest -import os -import errno -from unittest.mock import patch - -from distutils.file_util import move_file, copy_file -from distutils import log -from distutils.tests import support -from distutils.errors import DistutilsFileError -from test.support import run_unittest, unlink - -class FileUtilTestCase(support.TempdirManager, unittest.TestCase): - - def _log(self, msg, *args): - if len(args) > 0: - self._logs.append(msg % args) - else: - self._logs.append(msg) - - def setUp(self): - super(FileUtilTestCase, self).setUp() - self._logs = [] - self.old_log = log.info - log.info = self._log - tmp_dir = self.mkdtemp() - self.source = os.path.join(tmp_dir, 'f1') - self.target = os.path.join(tmp_dir, 'f2') - self.target_dir = os.path.join(tmp_dir, 'd1') - - def tearDown(self): - log.info = self.old_log - super(FileUtilTestCase, self).tearDown() - - def test_move_file_verbosity(self): - f = open(self.source, 'w') - try: - f.write('some content') - finally: - f.close() - - move_file(self.source, self.target, verbose=0) - wanted = [] - self.assertEqual(self._logs, wanted) - - # back to original state - move_file(self.target, self.source, verbose=0) - - move_file(self.source, self.target, verbose=1) - wanted = ['moving %s -> %s' % (self.source, self.target)] - self.assertEqual(self._logs, wanted) - - # back to original state - move_file(self.target, self.source, verbose=0) - - self._logs = [] - # now the target is a dir - os.mkdir(self.target_dir) - move_file(self.source, self.target_dir, verbose=1) - wanted = ['moving %s -> %s' % (self.source, self.target_dir)] - self.assertEqual(self._logs, wanted) - - def test_move_file_exception_unpacking_rename(self): - # see issue 22182 - with patch("os.rename", side_effect=OSError("wrong", 1)), \ - self.assertRaises(DistutilsFileError): - with open(self.source, 'w') as fobj: - fobj.write('spam eggs') - move_file(self.source, self.target, verbose=0) - - def test_move_file_exception_unpacking_unlink(self): - # see issue 22182 - with patch("os.rename", side_effect=OSError(errno.EXDEV, "wrong")), \ - patch("os.unlink", side_effect=OSError("wrong", 1)), \ - self.assertRaises(DistutilsFileError): - with open(self.source, 'w') as fobj: - fobj.write('spam eggs') - move_file(self.source, self.target, verbose=0) - - def test_copy_file_hard_link(self): - with open(self.source, 'w') as f: - f.write('some content') - # Check first that copy_file() will not fall back on copying the file - # instead of creating the hard link. - try: - os.link(self.source, self.target) - except OSError as e: - self.skipTest('os.link: %s' % e) - else: - unlink(self.target) - st = os.stat(self.source) - copy_file(self.source, self.target, link='hard') - st2 = os.stat(self.source) - st3 = os.stat(self.target) - self.assertTrue(os.path.samestat(st, st2), (st, st2)) - self.assertTrue(os.path.samestat(st2, st3), (st2, st3)) - with open(self.source, 'r') as f: - self.assertEqual(f.read(), 'some content') - - def test_copy_file_hard_link_failure(self): - # If hard linking fails, copy_file() falls back on copying file - # (some special filesystems don't support hard linking even under - # Unix, see issue #8876). - with open(self.source, 'w') as f: - f.write('some content') - st = os.stat(self.source) - with patch("os.link", side_effect=OSError(0, "linking unsupported")): - copy_file(self.source, self.target, link='hard') - st2 = os.stat(self.source) - st3 = os.stat(self.target) - self.assertTrue(os.path.samestat(st, st2), (st, st2)) - self.assertFalse(os.path.samestat(st2, st3), (st2, st3)) - for fn in (self.source, self.target): - with open(fn, 'r') as f: - self.assertEqual(f.read(), 'some content') - - -def test_suite(): - return unittest.makeSuite(FileUtilTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_filelist.py b/distutils/tests/test_filelist.py deleted file mode 100644 index 71fde2b7..00000000 --- a/distutils/tests/test_filelist.py +++ /dev/null @@ -1,343 +0,0 @@ -"""Tests for distutils.filelist.""" -import os -import re -import unittest -from distutils import debug -from distutils.log import WARN -from distutils.errors import DistutilsTemplateError -from distutils.filelist import glob_to_re, translate_pattern, FileList -from distutils import filelist - -import test.support -from test.support import captured_stdout, run_unittest -from distutils.tests import support - -from .py35compat import adapt_glob - - -MANIFEST_IN = """\ -include ok -include xo -exclude xo -include foo.tmp -include buildout.cfg -global-include *.x -global-include *.txt -global-exclude *.tmp -recursive-include f *.oo -recursive-exclude global *.x -graft dir -prune dir3 -""" - - -def make_local_path(s): - """Converts '/' in a string to os.sep""" - return s.replace('/', os.sep) - - -class FileListTestCase(support.LoggingSilencer, - unittest.TestCase): - - def assertNoWarnings(self): - self.assertEqual(self.get_logs(WARN), []) - self.clear_logs() - - def assertWarnings(self): - self.assertGreater(len(self.get_logs(WARN)), 0) - self.clear_logs() - - def test_glob_to_re(self): - sep = os.sep - if os.sep == '\\': - sep = re.escape(os.sep) - - for glob, regex in ( - # simple cases - ('foo*', r'(?s:foo[^%(sep)s]*)\Z'), - ('foo?', r'(?s:foo[^%(sep)s])\Z'), - ('foo??', r'(?s:foo[^%(sep)s][^%(sep)s])\Z'), - # special cases - (r'foo\\*', r'(?s:foo\\\\[^%(sep)s]*)\Z'), - (r'foo\\\*', r'(?s:foo\\\\\\[^%(sep)s]*)\Z'), - ('foo????', r'(?s:foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s])\Z'), - (r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z')): - regex = regex % {'sep': sep} - self.assertEqual(glob_to_re(glob), adapt_glob(regex)) - - def test_process_template_line(self): - # testing all MANIFEST.in template patterns - file_list = FileList() - l = make_local_path - - # simulated file list - file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt', - 'buildout.cfg', - # filelist does not filter out VCS directories, - # it's sdist that does - l('.hg/last-message.txt'), - l('global/one.txt'), - l('global/two.txt'), - l('global/files.x'), - l('global/here.tmp'), - l('f/o/f.oo'), - l('dir/graft-one'), - l('dir/dir2/graft2'), - l('dir3/ok'), - l('dir3/sub/ok.txt'), - ] - - for line in MANIFEST_IN.split('\n'): - if line.strip() == '': - continue - file_list.process_template_line(line) - - wanted = ['ok', - 'buildout.cfg', - 'four.txt', - l('.hg/last-message.txt'), - l('global/one.txt'), - l('global/two.txt'), - l('f/o/f.oo'), - l('dir/graft-one'), - l('dir/dir2/graft2'), - ] - - self.assertEqual(file_list.files, wanted) - - def test_debug_print(self): - file_list = FileList() - with captured_stdout() as stdout: - file_list.debug_print('xxx') - self.assertEqual(stdout.getvalue(), '') - - debug.DEBUG = True - try: - with captured_stdout() as stdout: - file_list.debug_print('xxx') - self.assertEqual(stdout.getvalue(), 'xxx\n') - finally: - debug.DEBUG = False - - def test_set_allfiles(self): - file_list = FileList() - files = ['a', 'b', 'c'] - file_list.set_allfiles(files) - self.assertEqual(file_list.allfiles, files) - - def test_remove_duplicates(self): - file_list = FileList() - file_list.files = ['a', 'b', 'a', 'g', 'c', 'g'] - # files must be sorted beforehand (sdist does it) - file_list.sort() - file_list.remove_duplicates() - self.assertEqual(file_list.files, ['a', 'b', 'c', 'g']) - - def test_translate_pattern(self): - # not regex - self.assertTrue(hasattr( - translate_pattern('a', anchor=True, is_regex=False), - 'search')) - - # is a regex - regex = re.compile('a') - self.assertEqual( - translate_pattern(regex, anchor=True, is_regex=True), - regex) - - # plain string flagged as regex - self.assertTrue(hasattr( - translate_pattern('a', anchor=True, is_regex=True), - 'search')) - - # glob support - self.assertTrue(translate_pattern( - '*.py', anchor=True, is_regex=False).search('filelist.py')) - - def test_exclude_pattern(self): - # return False if no match - file_list = FileList() - self.assertFalse(file_list.exclude_pattern('*.py')) - - # return True if files match - file_list = FileList() - file_list.files = ['a.py', 'b.py'] - self.assertTrue(file_list.exclude_pattern('*.py')) - - # test excludes - file_list = FileList() - file_list.files = ['a.py', 'a.txt'] - file_list.exclude_pattern('*.py') - self.assertEqual(file_list.files, ['a.txt']) - - def test_include_pattern(self): - # return False if no match - file_list = FileList() - file_list.set_allfiles([]) - self.assertFalse(file_list.include_pattern('*.py')) - - # return True if files match - file_list = FileList() - file_list.set_allfiles(['a.py', 'b.txt']) - self.assertTrue(file_list.include_pattern('*.py')) - - # test * matches all files - file_list = FileList() - self.assertIsNone(file_list.allfiles) - file_list.set_allfiles(['a.py', 'b.txt']) - file_list.include_pattern('*') - self.assertEqual(file_list.allfiles, ['a.py', 'b.txt']) - - def test_process_template(self): - l = make_local_path - # invalid lines - file_list = FileList() - for action in ('include', 'exclude', 'global-include', - 'global-exclude', 'recursive-include', - 'recursive-exclude', 'graft', 'prune', 'blarg'): - self.assertRaises(DistutilsTemplateError, - file_list.process_template_line, action) - - # include - file_list = FileList() - file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) - - file_list.process_template_line('include *.py') - self.assertEqual(file_list.files, ['a.py']) - self.assertNoWarnings() - - file_list.process_template_line('include *.rb') - self.assertEqual(file_list.files, ['a.py']) - self.assertWarnings() - - # exclude - file_list = FileList() - file_list.files = ['a.py', 'b.txt', l('d/c.py')] - - file_list.process_template_line('exclude *.py') - self.assertEqual(file_list.files, ['b.txt', l('d/c.py')]) - self.assertNoWarnings() - - file_list.process_template_line('exclude *.rb') - self.assertEqual(file_list.files, ['b.txt', l('d/c.py')]) - self.assertWarnings() - - # global-include - file_list = FileList() - file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) - - file_list.process_template_line('global-include *.py') - self.assertEqual(file_list.files, ['a.py', l('d/c.py')]) - self.assertNoWarnings() - - file_list.process_template_line('global-include *.rb') - self.assertEqual(file_list.files, ['a.py', l('d/c.py')]) - self.assertWarnings() - - # global-exclude - file_list = FileList() - file_list.files = ['a.py', 'b.txt', l('d/c.py')] - - file_list.process_template_line('global-exclude *.py') - self.assertEqual(file_list.files, ['b.txt']) - self.assertNoWarnings() - - file_list.process_template_line('global-exclude *.rb') - self.assertEqual(file_list.files, ['b.txt']) - self.assertWarnings() - - # recursive-include - file_list = FileList() - file_list.set_allfiles(['a.py', l('d/b.py'), l('d/c.txt'), - l('d/d/e.py')]) - - file_list.process_template_line('recursive-include d *.py') - self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) - self.assertNoWarnings() - - file_list.process_template_line('recursive-include e *.py') - self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) - self.assertWarnings() - - # recursive-exclude - file_list = FileList() - file_list.files = ['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')] - - file_list.process_template_line('recursive-exclude d *.py') - self.assertEqual(file_list.files, ['a.py', l('d/c.txt')]) - self.assertNoWarnings() - - file_list.process_template_line('recursive-exclude e *.py') - self.assertEqual(file_list.files, ['a.py', l('d/c.txt')]) - self.assertWarnings() - - # graft - file_list = FileList() - file_list.set_allfiles(['a.py', l('d/b.py'), l('d/d/e.py'), - l('f/f.py')]) - - file_list.process_template_line('graft d') - self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) - self.assertNoWarnings() - - file_list.process_template_line('graft e') - self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) - self.assertWarnings() - - # prune - file_list = FileList() - file_list.files = ['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')] - - file_list.process_template_line('prune d') - self.assertEqual(file_list.files, ['a.py', l('f/f.py')]) - self.assertNoWarnings() - - file_list.process_template_line('prune e') - self.assertEqual(file_list.files, ['a.py', l('f/f.py')]) - self.assertWarnings() - - -class FindAllTestCase(unittest.TestCase): - @test.support.skip_unless_symlink - def test_missing_symlink(self): - with test.support.temp_cwd(): - os.symlink('foo', 'bar') - self.assertEqual(filelist.findall(), []) - - def test_basic_discovery(self): - """ - When findall is called with no parameters or with - '.' as the parameter, the dot should be omitted from - the results. - """ - with test.support.temp_cwd(): - os.mkdir('foo') - file1 = os.path.join('foo', 'file1.txt') - test.support.create_empty_file(file1) - os.mkdir('bar') - file2 = os.path.join('bar', 'file2.txt') - test.support.create_empty_file(file2) - expected = [file2, file1] - self.assertEqual(sorted(filelist.findall()), expected) - - def test_non_local_discovery(self): - """ - When findall is called with another path, the full - path name should be returned. - """ - with test.support.temp_dir() as temp_dir: - file1 = os.path.join(temp_dir, 'file1.txt') - test.support.create_empty_file(file1) - expected = [file1] - self.assertEqual(filelist.findall(temp_dir), expected) - - -def test_suite(): - return unittest.TestSuite([ - unittest.makeSuite(FileListTestCase), - unittest.makeSuite(FindAllTestCase), - ]) - - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_install.py b/distutils/tests/test_install.py deleted file mode 100644 index eb684a09..00000000 --- a/distutils/tests/test_install.py +++ /dev/null @@ -1,250 +0,0 @@ -"""Tests for distutils.command.install.""" - -import os -import sys -import unittest -import site - -from test.support import captured_stdout, run_unittest - -from distutils import sysconfig -from distutils.command.install import install -from distutils.command import install as install_module -from distutils.command.build_ext import build_ext -from distutils.command.install import INSTALL_SCHEMES -from distutils.core import Distribution -from distutils.errors import DistutilsOptionError -from distutils.extension import Extension - -from distutils.tests import support -from test import support as test_support - - -def _make_ext_name(modname): - return modname + sysconfig.get_config_var('EXT_SUFFIX') - - -class InstallTestCase(support.TempdirManager, - support.EnvironGuard, - support.LoggingSilencer, - unittest.TestCase): - - def test_home_installation_scheme(self): - # This ensure two things: - # - that --home generates the desired set of directory names - # - test --home is supported on all platforms - builddir = self.mkdtemp() - destination = os.path.join(builddir, "installation") - - dist = Distribution({"name": "foopkg"}) - # script_name need not exist, it just need to be initialized - dist.script_name = os.path.join(builddir, "setup.py") - dist.command_obj["build"] = support.DummyCommand( - build_base=builddir, - build_lib=os.path.join(builddir, "lib"), - ) - - cmd = install(dist) - cmd.home = destination - cmd.ensure_finalized() - - self.assertEqual(cmd.install_base, destination) - self.assertEqual(cmd.install_platbase, destination) - - def check_path(got, expected): - got = os.path.normpath(got) - expected = os.path.normpath(expected) - self.assertEqual(got, expected) - - libdir = os.path.join(destination, "lib", "python") - check_path(cmd.install_lib, libdir) - _platlibdir = getattr(sys, "platlibdir", "lib") - platlibdir = os.path.join(destination, _platlibdir, "python") - check_path(cmd.install_platlib, platlibdir) - check_path(cmd.install_purelib, libdir) - check_path(cmd.install_headers, - os.path.join(destination, "include", "python", "foopkg")) - check_path(cmd.install_scripts, os.path.join(destination, "bin")) - check_path(cmd.install_data, destination) - - def test_user_site(self): - # test install with --user - # preparing the environment for the test - self.old_user_base = site.USER_BASE - self.old_user_site = site.USER_SITE - self.tmpdir = self.mkdtemp() - self.user_base = os.path.join(self.tmpdir, 'B') - self.user_site = os.path.join(self.tmpdir, 'S') - site.USER_BASE = self.user_base - site.USER_SITE = self.user_site - install_module.USER_BASE = self.user_base - install_module.USER_SITE = self.user_site - - def _expanduser(path): - return self.tmpdir - self.old_expand = os.path.expanduser - os.path.expanduser = _expanduser - - def cleanup(): - site.USER_BASE = self.old_user_base - site.USER_SITE = self.old_user_site - install_module.USER_BASE = self.old_user_base - install_module.USER_SITE = self.old_user_site - os.path.expanduser = self.old_expand - - self.addCleanup(cleanup) - - for key in ('nt_user', 'unix_user'): - self.assertIn(key, INSTALL_SCHEMES) - - dist = Distribution({'name': 'xx'}) - cmd = install(dist) - - # making sure the user option is there - options = [name for name, short, lable in - cmd.user_options] - self.assertIn('user', options) - - # setting a value - cmd.user = 1 - - # user base and site shouldn't be created yet - self.assertFalse(os.path.exists(self.user_base)) - self.assertFalse(os.path.exists(self.user_site)) - - # let's run finalize - cmd.ensure_finalized() - - # now they should - self.assertTrue(os.path.exists(self.user_base)) - self.assertTrue(os.path.exists(self.user_site)) - - self.assertIn('userbase', cmd.config_vars) - self.assertIn('usersite', cmd.config_vars) - - def test_handle_extra_path(self): - dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) - cmd = install(dist) - - # two elements - cmd.handle_extra_path() - self.assertEqual(cmd.extra_path, ['path', 'dirs']) - self.assertEqual(cmd.extra_dirs, 'dirs') - self.assertEqual(cmd.path_file, 'path') - - # one element - cmd.extra_path = ['path'] - cmd.handle_extra_path() - self.assertEqual(cmd.extra_path, ['path']) - self.assertEqual(cmd.extra_dirs, 'path') - self.assertEqual(cmd.path_file, 'path') - - # none - dist.extra_path = cmd.extra_path = None - cmd.handle_extra_path() - self.assertEqual(cmd.extra_path, None) - self.assertEqual(cmd.extra_dirs, '') - self.assertEqual(cmd.path_file, None) - - # three elements (no way !) - cmd.extra_path = 'path,dirs,again' - self.assertRaises(DistutilsOptionError, cmd.handle_extra_path) - - def test_finalize_options(self): - dist = Distribution({'name': 'xx'}) - cmd = install(dist) - - # must supply either prefix/exec-prefix/home or - # install-base/install-platbase -- not both - cmd.prefix = 'prefix' - cmd.install_base = 'base' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - # must supply either home or prefix/exec-prefix -- not both - cmd.install_base = None - cmd.home = 'home' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - # can't combine user with prefix/exec_prefix/home or - # install_(plat)base - cmd.prefix = None - cmd.user = 'user' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - def test_record(self): - install_dir = self.mkdtemp() - project_dir, dist = self.create_dist(py_modules=['hello'], - scripts=['sayhi']) - os.chdir(project_dir) - self.write_file('hello.py', "def main(): print('o hai')") - self.write_file('sayhi', 'from hello import main; main()') - - cmd = install(dist) - dist.command_obj['install'] = cmd - cmd.root = install_dir - cmd.record = os.path.join(project_dir, 'filelist') - cmd.ensure_finalized() - cmd.run() - - f = open(cmd.record) - try: - content = f.read() - finally: - f.close() - - found = [os.path.basename(line) for line in content.splitlines()] - expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag, - 'sayhi', - 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] - self.assertEqual(found, expected) - - def test_record_extensions(self): - cmd = test_support.missing_compiler_executable() - if cmd is not None: - self.skipTest('The %r command is not found' % cmd) - install_dir = self.mkdtemp() - project_dir, dist = self.create_dist(ext_modules=[ - Extension('xx', ['xxmodule.c'])]) - os.chdir(project_dir) - support.copy_xxmodule_c(project_dir) - - buildextcmd = build_ext(dist) - support.fixup_build_ext(buildextcmd) - buildextcmd.ensure_finalized() - - cmd = install(dist) - dist.command_obj['install'] = cmd - dist.command_obj['build_ext'] = buildextcmd - cmd.root = install_dir - cmd.record = os.path.join(project_dir, 'filelist') - cmd.ensure_finalized() - cmd.run() - - f = open(cmd.record) - try: - content = f.read() - finally: - f.close() - - found = [os.path.basename(line) for line in content.splitlines()] - expected = [_make_ext_name('xx'), - 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] - self.assertEqual(found, expected) - - def test_debug_mode(self): - # this covers the code called when DEBUG is set - old_logs_len = len(self.logs) - install_module.DEBUG = True - try: - with captured_stdout(): - self.test_record() - finally: - install_module.DEBUG = False - self.assertGreater(len(self.logs), old_logs_len) - - -def test_suite(): - return unittest.makeSuite(InstallTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_install_data.py b/distutils/tests/test_install_data.py deleted file mode 100644 index 32ab296a..00000000 --- a/distutils/tests/test_install_data.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Tests for distutils.command.install_data.""" -import os -import unittest - -from distutils.command.install_data import install_data -from distutils.tests import support -from test.support import run_unittest - -class InstallDataTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def test_simple_run(self): - pkg_dir, dist = self.create_dist() - cmd = install_data(dist) - cmd.install_dir = inst = os.path.join(pkg_dir, 'inst') - - # data_files can contain - # - simple files - # - a tuple with a path, and a list of file - one = os.path.join(pkg_dir, 'one') - self.write_file(one, 'xxx') - inst2 = os.path.join(pkg_dir, 'inst2') - two = os.path.join(pkg_dir, 'two') - self.write_file(two, 'xxx') - - cmd.data_files = [one, (inst2, [two])] - self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])]) - - # let's run the command - cmd.ensure_finalized() - cmd.run() - - # let's check the result - self.assertEqual(len(cmd.get_outputs()), 2) - rtwo = os.path.split(two)[-1] - self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) - rone = os.path.split(one)[-1] - self.assertTrue(os.path.exists(os.path.join(inst, rone))) - cmd.outfiles = [] - - # let's try with warn_dir one - cmd.warn_dir = 1 - cmd.ensure_finalized() - cmd.run() - - # let's check the result - self.assertEqual(len(cmd.get_outputs()), 2) - self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) - self.assertTrue(os.path.exists(os.path.join(inst, rone))) - cmd.outfiles = [] - - # now using root and empty dir - cmd.root = os.path.join(pkg_dir, 'root') - inst3 = os.path.join(cmd.install_dir, 'inst3') - inst4 = os.path.join(pkg_dir, 'inst4') - three = os.path.join(cmd.install_dir, 'three') - self.write_file(three, 'xx') - cmd.data_files = [one, (inst2, [two]), - ('inst3', [three]), - (inst4, [])] - cmd.ensure_finalized() - cmd.run() - - # let's check the result - self.assertEqual(len(cmd.get_outputs()), 4) - self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) - self.assertTrue(os.path.exists(os.path.join(inst, rone))) - -def test_suite(): - return unittest.makeSuite(InstallDataTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_install_headers.py b/distutils/tests/test_install_headers.py deleted file mode 100644 index 2217b321..00000000 --- a/distutils/tests/test_install_headers.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Tests for distutils.command.install_headers.""" -import os -import unittest - -from distutils.command.install_headers import install_headers -from distutils.tests import support -from test.support import run_unittest - -class InstallHeadersTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def test_simple_run(self): - # we have two headers - header_list = self.mkdtemp() - header1 = os.path.join(header_list, 'header1') - header2 = os.path.join(header_list, 'header2') - self.write_file(header1) - self.write_file(header2) - headers = [header1, header2] - - pkg_dir, dist = self.create_dist(headers=headers) - cmd = install_headers(dist) - self.assertEqual(cmd.get_inputs(), headers) - - # let's run the command - cmd.install_dir = os.path.join(pkg_dir, 'inst') - cmd.ensure_finalized() - cmd.run() - - # let's check the results - self.assertEqual(len(cmd.get_outputs()), 2) - -def test_suite(): - return unittest.makeSuite(InstallHeadersTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_install_lib.py b/distutils/tests/test_install_lib.py deleted file mode 100644 index fda6315b..00000000 --- a/distutils/tests/test_install_lib.py +++ /dev/null @@ -1,115 +0,0 @@ -"""Tests for distutils.command.install_data.""" -import sys -import os -import importlib.util -import unittest - -from distutils.command.install_lib import install_lib -from distutils.extension import Extension -from distutils.tests import support -from distutils.errors import DistutilsOptionError -from test.support import run_unittest - - -class InstallLibTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def test_finalize_options(self): - dist = self.create_dist()[1] - cmd = install_lib(dist) - - cmd.finalize_options() - self.assertEqual(cmd.compile, 1) - self.assertEqual(cmd.optimize, 0) - - # optimize must be 0, 1, or 2 - cmd.optimize = 'foo' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - cmd.optimize = '4' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - cmd.optimize = '2' - cmd.finalize_options() - self.assertEqual(cmd.optimize, 2) - - @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') - def test_byte_compile(self): - project_dir, dist = self.create_dist() - os.chdir(project_dir) - cmd = install_lib(dist) - cmd.compile = cmd.optimize = 1 - - f = os.path.join(project_dir, 'foo.py') - self.write_file(f, '# python file') - cmd.byte_compile([f]) - pyc_file = importlib.util.cache_from_source('foo.py', optimization='') - pyc_opt_file = importlib.util.cache_from_source('foo.py', - optimization=cmd.optimize) - self.assertTrue(os.path.exists(pyc_file)) - self.assertTrue(os.path.exists(pyc_opt_file)) - - def test_get_outputs(self): - project_dir, dist = self.create_dist() - os.chdir(project_dir) - os.mkdir('spam') - cmd = install_lib(dist) - - # setting up a dist environment - cmd.compile = cmd.optimize = 1 - cmd.install_dir = self.mkdtemp() - f = os.path.join(project_dir, 'spam', '__init__.py') - self.write_file(f, '# python package') - cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] - cmd.distribution.packages = ['spam'] - cmd.distribution.script_name = 'setup.py' - - # get_outputs should return 4 elements: spam/__init__.py and .pyc, - # foo.import-tag-abiflags.so / foo.pyd - outputs = cmd.get_outputs() - self.assertEqual(len(outputs), 4, outputs) - - def test_get_inputs(self): - project_dir, dist = self.create_dist() - os.chdir(project_dir) - os.mkdir('spam') - cmd = install_lib(dist) - - # setting up a dist environment - cmd.compile = cmd.optimize = 1 - cmd.install_dir = self.mkdtemp() - f = os.path.join(project_dir, 'spam', '__init__.py') - self.write_file(f, '# python package') - cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] - cmd.distribution.packages = ['spam'] - cmd.distribution.script_name = 'setup.py' - - # get_inputs should return 2 elements: spam/__init__.py and - # foo.import-tag-abiflags.so / foo.pyd - inputs = cmd.get_inputs() - self.assertEqual(len(inputs), 2, inputs) - - def test_dont_write_bytecode(self): - # makes sure byte_compile is not used - dist = self.create_dist()[1] - cmd = install_lib(dist) - cmd.compile = 1 - cmd.optimize = 1 - - old_dont_write_bytecode = sys.dont_write_bytecode - sys.dont_write_bytecode = True - try: - cmd.byte_compile([]) - finally: - sys.dont_write_bytecode = old_dont_write_bytecode - - self.assertIn('byte-compiling is disabled', - self.logs[0][1] % self.logs[0][2]) - - -def test_suite(): - return unittest.makeSuite(InstallLibTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_install_scripts.py b/distutils/tests/test_install_scripts.py deleted file mode 100644 index 1f7b1038..00000000 --- a/distutils/tests/test_install_scripts.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Tests for distutils.command.install_scripts.""" - -import os -import unittest - -from distutils.command.install_scripts import install_scripts -from distutils.core import Distribution - -from distutils.tests import support -from test.support import run_unittest - - -class InstallScriptsTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_default_settings(self): - dist = Distribution() - dist.command_obj["build"] = support.DummyCommand( - build_scripts="/foo/bar") - dist.command_obj["install"] = support.DummyCommand( - install_scripts="/splat/funk", - force=1, - skip_build=1, - ) - cmd = install_scripts(dist) - self.assertFalse(cmd.force) - self.assertFalse(cmd.skip_build) - self.assertIsNone(cmd.build_dir) - self.assertIsNone(cmd.install_dir) - - cmd.finalize_options() - - self.assertTrue(cmd.force) - self.assertTrue(cmd.skip_build) - self.assertEqual(cmd.build_dir, "/foo/bar") - self.assertEqual(cmd.install_dir, "/splat/funk") - - def test_installation(self): - source = self.mkdtemp() - expected = [] - - def write_script(name, text): - expected.append(name) - f = open(os.path.join(source, name), "w") - try: - f.write(text) - finally: - f.close() - - write_script("script1.py", ("#! /usr/bin/env python2.3\n" - "# bogus script w/ Python sh-bang\n" - "pass\n")) - write_script("script2.py", ("#!/usr/bin/python\n" - "# bogus script w/ Python sh-bang\n" - "pass\n")) - write_script("shell.sh", ("#!/bin/sh\n" - "# bogus shell script w/ sh-bang\n" - "exit 0\n")) - - target = self.mkdtemp() - dist = Distribution() - dist.command_obj["build"] = support.DummyCommand(build_scripts=source) - dist.command_obj["install"] = support.DummyCommand( - install_scripts=target, - force=1, - skip_build=1, - ) - cmd = install_scripts(dist) - cmd.finalize_options() - cmd.run() - - installed = os.listdir(target) - for name in expected: - self.assertIn(name, installed) - - -def test_suite(): - return unittest.makeSuite(InstallScriptsTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_log.py b/distutils/tests/test_log.py deleted file mode 100644 index 75cf9006..00000000 --- a/distutils/tests/test_log.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Tests for distutils.log""" - -import io -import sys -import unittest -from test.support import swap_attr, run_unittest - -from distutils import log - -class TestLog(unittest.TestCase): - def test_non_ascii(self): - # Issues #8663, #34421: test that non-encodable text is escaped with - # backslashreplace error handler and encodable non-ASCII text is - # output as is. - for errors in ('strict', 'backslashreplace', 'surrogateescape', - 'replace', 'ignore'): - with self.subTest(errors=errors): - stdout = io.TextIOWrapper(io.BytesIO(), - encoding='cp437', errors=errors) - stderr = io.TextIOWrapper(io.BytesIO(), - encoding='cp437', errors=errors) - old_threshold = log.set_threshold(log.DEBUG) - try: - with swap_attr(sys, 'stdout', stdout), \ - swap_attr(sys, 'stderr', stderr): - log.debug('Dεbug\tMÄ—ssãge') - log.fatal('Fαtal\tÈrrÅr') - finally: - log.set_threshold(old_threshold) - - stdout.seek(0) - self.assertEqual(stdout.read().rstrip(), - 'Dεbug\tM?ss?ge' if errors == 'replace' else - 'Dεbug\tMssge' if errors == 'ignore' else - 'Dεbug\tM\\u0117ss\\xe3ge') - stderr.seek(0) - self.assertEqual(stderr.read().rstrip(), - 'Fαtal\t?rr?r' if errors == 'replace' else - 'Fαtal\trrr' if errors == 'ignore' else - 'Fαtal\t\\xc8rr\\u014dr') - -def test_suite(): - return unittest.makeSuite(TestLog) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_msvc9compiler.py b/distutils/tests/test_msvc9compiler.py deleted file mode 100644 index 77a07ef3..00000000 --- a/distutils/tests/test_msvc9compiler.py +++ /dev/null @@ -1,184 +0,0 @@ -"""Tests for distutils.msvc9compiler.""" -import sys -import unittest -import os - -from distutils.errors import DistutilsPlatformError -from distutils.tests import support -from test.support import run_unittest - -# A manifest with the only assembly reference being the msvcrt assembly, so -# should have the assembly completely stripped. Note that although the -# assembly has a reference the assembly is removed - that is -# currently a "feature", not a bug :) -_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\ - - - - - - - - - - - - - - - - - -""" - -# A manifest with references to assemblies other than msvcrt. When processed, -# this assembly should be returned with just the msvcrt part removed. -_MANIFEST_WITH_MULTIPLE_REFERENCES = """\ - - - - - - - - - - - - - - - - - - - - - - -""" - -_CLEANED_MANIFEST = """\ - - - - - - - - - - - - - - - - - - -""" - -if sys.platform=="win32": - from distutils.msvccompiler import get_build_version - if get_build_version()>=8.0: - SKIP_MESSAGE = None - else: - SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" -else: - SKIP_MESSAGE = "These tests are only for win32" - -@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) -class msvc9compilerTestCase(support.TempdirManager, - unittest.TestCase): - - def test_no_compiler(self): - # makes sure query_vcvarsall raises - # a DistutilsPlatformError if the compiler - # is not found - from distutils.msvc9compiler import query_vcvarsall - def _find_vcvarsall(version): - return None - - from distutils import msvc9compiler - old_find_vcvarsall = msvc9compiler.find_vcvarsall - msvc9compiler.find_vcvarsall = _find_vcvarsall - try: - self.assertRaises(DistutilsPlatformError, query_vcvarsall, - 'wont find this version') - finally: - msvc9compiler.find_vcvarsall = old_find_vcvarsall - - def test_reg_class(self): - from distutils.msvc9compiler import Reg - self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') - - # looking for values that should exist on all - # windows registry versions. - path = r'Control Panel\Desktop' - v = Reg.get_value(path, 'dragfullwindows') - self.assertIn(v, ('0', '1', '2')) - - import winreg - HKCU = winreg.HKEY_CURRENT_USER - keys = Reg.read_keys(HKCU, 'xxxx') - self.assertEqual(keys, None) - - keys = Reg.read_keys(HKCU, r'Control Panel') - self.assertIn('Desktop', keys) - - def test_remove_visual_c_ref(self): - from distutils.msvc9compiler import MSVCCompiler - tempdir = self.mkdtemp() - manifest = os.path.join(tempdir, 'manifest') - f = open(manifest, 'w') - try: - f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES) - finally: - f.close() - - compiler = MSVCCompiler() - compiler._remove_visual_c_ref(manifest) - - # see what we got - f = open(manifest) - try: - # removing trailing spaces - content = '\n'.join([line.rstrip() for line in f.readlines()]) - finally: - f.close() - - # makes sure the manifest was properly cleaned - self.assertEqual(content, _CLEANED_MANIFEST) - - def test_remove_entire_manifest(self): - from distutils.msvc9compiler import MSVCCompiler - tempdir = self.mkdtemp() - manifest = os.path.join(tempdir, 'manifest') - f = open(manifest, 'w') - try: - f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE) - finally: - f.close() - - compiler = MSVCCompiler() - got = compiler._remove_visual_c_ref(manifest) - self.assertIsNone(got) - - -def test_suite(): - return unittest.makeSuite(msvc9compilerTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_msvccompiler.py b/distutils/tests/test_msvccompiler.py deleted file mode 100644 index b518d6a7..00000000 --- a/distutils/tests/test_msvccompiler.py +++ /dev/null @@ -1,81 +0,0 @@ -"""Tests for distutils._msvccompiler.""" -import sys -import unittest -import os - -from distutils.errors import DistutilsPlatformError -from distutils.tests import support -from test.support import run_unittest - - -SKIP_MESSAGE = (None if sys.platform == "win32" else - "These tests are only for win32") - -@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) -class msvccompilerTestCase(support.TempdirManager, - unittest.TestCase): - - def test_no_compiler(self): - import distutils._msvccompiler as _msvccompiler - # makes sure query_vcvarsall raises - # a DistutilsPlatformError if the compiler - # is not found - def _find_vcvarsall(plat_spec): - return None, None - - old_find_vcvarsall = _msvccompiler._find_vcvarsall - _msvccompiler._find_vcvarsall = _find_vcvarsall - try: - self.assertRaises(DistutilsPlatformError, - _msvccompiler._get_vc_env, - 'wont find this version') - finally: - _msvccompiler._find_vcvarsall = old_find_vcvarsall - - def test_get_vc_env_unicode(self): - import distutils._msvccompiler as _msvccompiler - - test_var = 'ṰḖṤṪ┅ṼẨṜ' - test_value = '₃â´â‚…' - - # Ensure we don't early exit from _get_vc_env - old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None) - os.environ[test_var] = test_value - try: - env = _msvccompiler._get_vc_env('x86') - self.assertIn(test_var.lower(), env) - self.assertEqual(test_value, env[test_var.lower()]) - finally: - os.environ.pop(test_var) - if old_distutils_use_sdk: - os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk - - def test_get_vc2017(self): - import distutils._msvccompiler as _msvccompiler - - # This function cannot be mocked, so pass it if we find VS 2017 - # and mark it skipped if we do not. - version, path = _msvccompiler._find_vc2017() - if version: - self.assertGreaterEqual(version, 15) - self.assertTrue(os.path.isdir(path)) - else: - raise unittest.SkipTest("VS 2017 is not installed") - - def test_get_vc2015(self): - import distutils._msvccompiler as _msvccompiler - - # This function cannot be mocked, so pass it if we find VS 2015 - # and mark it skipped if we do not. - version, path = _msvccompiler._find_vc2015() - if version: - self.assertGreaterEqual(version, 14) - self.assertTrue(os.path.isdir(path)) - else: - raise unittest.SkipTest("VS 2015 is not installed") - -def test_suite(): - return unittest.makeSuite(msvccompilerTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_register.py b/distutils/tests/test_register.py deleted file mode 100644 index e68b0af3..00000000 --- a/distutils/tests/test_register.py +++ /dev/null @@ -1,323 +0,0 @@ -"""Tests for distutils.command.register.""" -import os -import unittest -import getpass -import urllib -import warnings - -from test.support import check_warnings, run_unittest - -from distutils.command import register as register_module -from distutils.command.register import register -from distutils.errors import DistutilsSetupError -from distutils.log import INFO - -from distutils.tests.test_config import BasePyPIRCCommandTestCase - -try: - import docutils -except ImportError: - docutils = None - -PYPIRC_NOPASSWORD = """\ -[distutils] - -index-servers = - server1 - -[server1] -username:me -""" - -WANTED_PYPIRC = """\ -[distutils] -index-servers = - pypi - -[pypi] -username:tarek -password:password -""" - -class Inputs(object): - """Fakes user inputs.""" - def __init__(self, *answers): - self.answers = answers - self.index = 0 - - def __call__(self, prompt=''): - try: - return self.answers[self.index] - finally: - self.index += 1 - -class FakeOpener(object): - """Fakes a PyPI server""" - def __init__(self): - self.reqs = [] - - def __call__(self, *args): - return self - - def open(self, req, data=None, timeout=None): - self.reqs.append(req) - return self - - def read(self): - return b'xxx' - - def getheader(self, name, default=None): - return { - 'content-type': 'text/plain; charset=utf-8', - }.get(name.lower(), default) - - -class RegisterTestCase(BasePyPIRCCommandTestCase): - - def setUp(self): - super(RegisterTestCase, self).setUp() - # patching the password prompt - self._old_getpass = getpass.getpass - def _getpass(prompt): - return 'password' - getpass.getpass = _getpass - urllib.request._opener = None - self.old_opener = urllib.request.build_opener - self.conn = urllib.request.build_opener = FakeOpener() - - def tearDown(self): - getpass.getpass = self._old_getpass - urllib.request._opener = None - urllib.request.build_opener = self.old_opener - super(RegisterTestCase, self).tearDown() - - def _get_cmd(self, metadata=None): - if metadata is None: - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', - 'name': 'xxx', 'version': 'xxx'} - pkg_info, dist = self.create_dist(**metadata) - return register(dist) - - def test_create_pypirc(self): - # this test makes sure a .pypirc file - # is created when requested. - - # let's create a register instance - cmd = self._get_cmd() - - # we shouldn't have a .pypirc file yet - self.assertFalse(os.path.exists(self.rc)) - - # patching input and getpass.getpass - # so register gets happy - # - # Here's what we are faking : - # use your existing login (choice 1.) - # Username : 'tarek' - # Password : 'password' - # Save your login (y/N)? : 'y' - inputs = Inputs('1', 'tarek', 'y') - register_module.input = inputs.__call__ - # let's run the command - try: - cmd.run() - finally: - del register_module.input - - # we should have a brand new .pypirc file - self.assertTrue(os.path.exists(self.rc)) - - # with the content similar to WANTED_PYPIRC - f = open(self.rc) - try: - content = f.read() - self.assertEqual(content, WANTED_PYPIRC) - finally: - f.close() - - # now let's make sure the .pypirc file generated - # really works : we shouldn't be asked anything - # if we run the command again - def _no_way(prompt=''): - raise AssertionError(prompt) - register_module.input = _no_way - - cmd.show_response = 1 - cmd.run() - - # let's see what the server received : we should - # have 2 similar requests - self.assertEqual(len(self.conn.reqs), 2) - req1 = dict(self.conn.reqs[0].headers) - req2 = dict(self.conn.reqs[1].headers) - - self.assertEqual(req1['Content-length'], '1374') - self.assertEqual(req2['Content-length'], '1374') - self.assertIn(b'xxx', self.conn.reqs[1].data) - - def test_password_not_in_file(self): - - self.write_file(self.rc, PYPIRC_NOPASSWORD) - cmd = self._get_cmd() - cmd._set_config() - cmd.finalize_options() - cmd.send_metadata() - - # dist.password should be set - # therefore used afterwards by other commands - self.assertEqual(cmd.distribution.password, 'password') - - def test_registering(self): - # this test runs choice 2 - cmd = self._get_cmd() - inputs = Inputs('2', 'tarek', 'tarek@ziade.org') - register_module.input = inputs.__call__ - try: - # let's run the command - cmd.run() - finally: - del register_module.input - - # we should have send a request - self.assertEqual(len(self.conn.reqs), 1) - req = self.conn.reqs[0] - headers = dict(req.headers) - self.assertEqual(headers['Content-length'], '608') - self.assertIn(b'tarek', req.data) - - def test_password_reset(self): - # this test runs choice 3 - cmd = self._get_cmd() - inputs = Inputs('3', 'tarek@ziade.org') - register_module.input = inputs.__call__ - try: - # let's run the command - cmd.run() - finally: - del register_module.input - - # we should have send a request - self.assertEqual(len(self.conn.reqs), 1) - req = self.conn.reqs[0] - headers = dict(req.headers) - self.assertEqual(headers['Content-length'], '290') - self.assertIn(b'tarek', req.data) - - @unittest.skipUnless(docutils is not None, 'needs docutils') - def test_strict(self): - # testing the script option - # when on, the register command stops if - # the metadata is incomplete or if - # long_description is not reSt compliant - - # empty metadata - cmd = self._get_cmd({}) - cmd.ensure_finalized() - cmd.strict = 1 - self.assertRaises(DistutilsSetupError, cmd.run) - - # metadata are OK but long_description is broken - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'éxéxé', - 'name': 'xxx', 'version': 'xxx', - 'long_description': 'title\n==\n\ntext'} - - cmd = self._get_cmd(metadata) - cmd.ensure_finalized() - cmd.strict = 1 - self.assertRaises(DistutilsSetupError, cmd.run) - - # now something that works - metadata['long_description'] = 'title\n=====\n\ntext' - cmd = self._get_cmd(metadata) - cmd.ensure_finalized() - cmd.strict = 1 - inputs = Inputs('1', 'tarek', 'y') - register_module.input = inputs.__call__ - # let's run the command - try: - cmd.run() - finally: - del register_module.input - - # strict is not by default - cmd = self._get_cmd() - cmd.ensure_finalized() - inputs = Inputs('1', 'tarek', 'y') - register_module.input = inputs.__call__ - # let's run the command - try: - cmd.run() - finally: - del register_module.input - - # and finally a Unicode test (bug #12114) - metadata = {'url': 'xxx', 'author': '\u00c9ric', - 'author_email': 'xxx', 'name': 'xxx', - 'version': 'xxx', - 'description': 'Something about esszet \u00df', - 'long_description': 'More things about esszet \u00df'} - - cmd = self._get_cmd(metadata) - cmd.ensure_finalized() - cmd.strict = 1 - inputs = Inputs('1', 'tarek', 'y') - register_module.input = inputs.__call__ - # let's run the command - try: - cmd.run() - finally: - del register_module.input - - @unittest.skipUnless(docutils is not None, 'needs docutils') - def test_register_invalid_long_description(self): - description = ':funkie:`str`' # mimic Sphinx-specific markup - metadata = {'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx', - 'name': 'xxx', 'version': 'xxx', - 'long_description': description} - cmd = self._get_cmd(metadata) - cmd.ensure_finalized() - cmd.strict = True - inputs = Inputs('2', 'tarek', 'tarek@ziade.org') - register_module.input = inputs - self.addCleanup(delattr, register_module, 'input') - - self.assertRaises(DistutilsSetupError, cmd.run) - - def test_check_metadata_deprecated(self): - # makes sure make_metadata is deprecated - cmd = self._get_cmd() - with check_warnings() as w: - warnings.simplefilter("always") - cmd.check_metadata() - self.assertEqual(len(w.warnings), 1) - - def test_list_classifiers(self): - cmd = self._get_cmd() - cmd.list_classifiers = 1 - cmd.run() - results = self.get_logs(INFO) - self.assertEqual(results, ['running check', 'xxx']) - - def test_show_response(self): - # test that the --show-response option return a well formatted response - cmd = self._get_cmd() - inputs = Inputs('1', 'tarek', 'y') - register_module.input = inputs.__call__ - cmd.show_response = 1 - try: - cmd.run() - finally: - del register_module.input - - results = self.get_logs(INFO) - self.assertEqual(results[3], 75 * '-' + '\nxxx\n' + 75 * '-') - - -def test_suite(): - return unittest.makeSuite(RegisterTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_sdist.py b/distutils/tests/test_sdist.py deleted file mode 100644 index 23db1269..00000000 --- a/distutils/tests/test_sdist.py +++ /dev/null @@ -1,492 +0,0 @@ -"""Tests for distutils.command.sdist.""" -import os -import tarfile -import unittest -import warnings -import zipfile -from os.path import join -from textwrap import dedent -from test.support import captured_stdout, check_warnings, run_unittest - -try: - import zlib - ZLIB_SUPPORT = True -except ImportError: - ZLIB_SUPPORT = False - -try: - import grp - import pwd - UID_GID_SUPPORT = True -except ImportError: - UID_GID_SUPPORT = False - -from distutils.command.sdist import sdist, show_formats -from distutils.core import Distribution -from distutils.tests.test_config import BasePyPIRCCommandTestCase -from distutils.errors import DistutilsOptionError -from distutils.spawn import find_executable -from distutils.log import WARN -from distutils.filelist import FileList -from distutils.archive_util import ARCHIVE_FORMATS - -SETUP_PY = """ -from distutils.core import setup -import somecode - -setup(name='fake') -""" - -MANIFEST = """\ -# file GENERATED by distutils, do NOT edit -README -buildout.cfg -inroot.txt -setup.py -data%(sep)sdata.dt -scripts%(sep)sscript.py -some%(sep)sfile.txt -some%(sep)sother_file.txt -somecode%(sep)s__init__.py -somecode%(sep)sdoc.dat -somecode%(sep)sdoc.txt -""" - -class SDistTestCase(BasePyPIRCCommandTestCase): - - def setUp(self): - # PyPIRCCommandTestCase creates a temp dir already - # and put it in self.tmp_dir - super(SDistTestCase, self).setUp() - # setting up an environment - self.old_path = os.getcwd() - os.mkdir(join(self.tmp_dir, 'somecode')) - os.mkdir(join(self.tmp_dir, 'dist')) - # a package, and a README - self.write_file((self.tmp_dir, 'README'), 'xxx') - self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#') - self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY) - os.chdir(self.tmp_dir) - - def tearDown(self): - # back to normal - os.chdir(self.old_path) - super(SDistTestCase, self).tearDown() - - def get_cmd(self, metadata=None): - """Returns a cmd""" - if metadata is None: - metadata = {'name': 'fake', 'version': '1.0', - 'url': 'xxx', 'author': 'xxx', - 'author_email': 'xxx'} - dist = Distribution(metadata) - dist.script_name = 'setup.py' - dist.packages = ['somecode'] - dist.include_package_data = True - cmd = sdist(dist) - cmd.dist_dir = 'dist' - return dist, cmd - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_prune_file_list(self): - # this test creates a project with some VCS dirs and an NFS rename - # file, then launches sdist to check they get pruned on all systems - - # creating VCS directories with some files in them - os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) - self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') - - os.mkdir(join(self.tmp_dir, 'somecode', '.hg')) - self.write_file((self.tmp_dir, 'somecode', '.hg', - 'ok'), 'xxx') - - os.mkdir(join(self.tmp_dir, 'somecode', '.git')) - self.write_file((self.tmp_dir, 'somecode', '.git', - 'ok'), 'xxx') - - self.write_file((self.tmp_dir, 'somecode', '.nfs0001'), 'xxx') - - # now building a sdist - dist, cmd = self.get_cmd() - - # zip is available universally - # (tar might not be installed under win32) - cmd.formats = ['zip'] - - cmd.ensure_finalized() - cmd.run() - - # now let's check what we have - dist_folder = join(self.tmp_dir, 'dist') - files = os.listdir(dist_folder) - self.assertEqual(files, ['fake-1.0.zip']) - - zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) - try: - content = zip_file.namelist() - finally: - zip_file.close() - - # making sure everything has been pruned correctly - expected = ['', 'PKG-INFO', 'README', 'setup.py', - 'somecode/', 'somecode/__init__.py'] - self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - @unittest.skipIf(find_executable('tar') is None, - "The tar command is not found") - @unittest.skipIf(find_executable('gzip') is None, - "The gzip command is not found") - def test_make_distribution(self): - # now building a sdist - dist, cmd = self.get_cmd() - - # creating a gztar then a tar - cmd.formats = ['gztar', 'tar'] - cmd.ensure_finalized() - cmd.run() - - # making sure we have two files - dist_folder = join(self.tmp_dir, 'dist') - result = os.listdir(dist_folder) - result.sort() - self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) - - os.remove(join(dist_folder, 'fake-1.0.tar')) - os.remove(join(dist_folder, 'fake-1.0.tar.gz')) - - # now trying a tar then a gztar - cmd.formats = ['tar', 'gztar'] - - cmd.ensure_finalized() - cmd.run() - - result = os.listdir(dist_folder) - result.sort() - self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_add_defaults(self): - - # http://bugs.python.org/issue2279 - - # add_default should also include - # data_files and package_data - dist, cmd = self.get_cmd() - - # filling data_files by pointing files - # in package_data - dist.package_data = {'': ['*.cfg', '*.dat'], - 'somecode': ['*.txt']} - self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') - self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#') - - # adding some data in data_files - data_dir = join(self.tmp_dir, 'data') - os.mkdir(data_dir) - self.write_file((data_dir, 'data.dt'), '#') - some_dir = join(self.tmp_dir, 'some') - os.mkdir(some_dir) - # make sure VCS directories are pruned (#14004) - hg_dir = join(self.tmp_dir, '.hg') - os.mkdir(hg_dir) - self.write_file((hg_dir, 'last-message.txt'), '#') - # a buggy regex used to prevent this from working on windows (#6884) - self.write_file((self.tmp_dir, 'buildout.cfg'), '#') - self.write_file((self.tmp_dir, 'inroot.txt'), '#') - self.write_file((some_dir, 'file.txt'), '#') - self.write_file((some_dir, 'other_file.txt'), '#') - - dist.data_files = [('data', ['data/data.dt', - 'buildout.cfg', - 'inroot.txt', - 'notexisting']), - 'some/file.txt', - 'some/other_file.txt'] - - # adding a script - script_dir = join(self.tmp_dir, 'scripts') - os.mkdir(script_dir) - self.write_file((script_dir, 'script.py'), '#') - dist.scripts = [join('scripts', 'script.py')] - - cmd.formats = ['zip'] - cmd.use_defaults = True - - cmd.ensure_finalized() - cmd.run() - - # now let's check what we have - dist_folder = join(self.tmp_dir, 'dist') - files = os.listdir(dist_folder) - self.assertEqual(files, ['fake-1.0.zip']) - - zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) - try: - content = zip_file.namelist() - finally: - zip_file.close() - - # making sure everything was added - expected = ['', 'PKG-INFO', 'README', 'buildout.cfg', - 'data/', 'data/data.dt', 'inroot.txt', - 'scripts/', 'scripts/script.py', 'setup.py', - 'some/', 'some/file.txt', 'some/other_file.txt', - 'somecode/', 'somecode/__init__.py', 'somecode/doc.dat', - 'somecode/doc.txt'] - self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) - - # checking the MANIFEST - f = open(join(self.tmp_dir, 'MANIFEST')) - try: - manifest = f.read() - finally: - f.close() - self.assertEqual(manifest, MANIFEST % {'sep': os.sep}) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_metadata_check_option(self): - # testing the `medata-check` option - dist, cmd = self.get_cmd(metadata={}) - - # this should raise some warnings ! - # with the `check` subcommand - cmd.ensure_finalized() - cmd.run() - warnings = [msg for msg in self.get_logs(WARN) if - msg.startswith('warning: check:')] - self.assertEqual(len(warnings), 2) - - # trying with a complete set of metadata - self.clear_logs() - dist, cmd = self.get_cmd() - cmd.ensure_finalized() - cmd.metadata_check = 0 - cmd.run() - warnings = [msg for msg in self.get_logs(WARN) if - msg.startswith('warning: check:')] - self.assertEqual(len(warnings), 0) - - def test_check_metadata_deprecated(self): - # makes sure make_metadata is deprecated - dist, cmd = self.get_cmd() - with check_warnings() as w: - warnings.simplefilter("always") - cmd.check_metadata() - self.assertEqual(len(w.warnings), 1) - - def test_show_formats(self): - with captured_stdout() as stdout: - show_formats() - - # the output should be a header line + one line per format - num_formats = len(ARCHIVE_FORMATS.keys()) - output = [line for line in stdout.getvalue().split('\n') - if line.strip().startswith('--formats=')] - self.assertEqual(len(output), num_formats) - - def test_finalize_options(self): - dist, cmd = self.get_cmd() - cmd.finalize_options() - - # default options set by finalize - self.assertEqual(cmd.manifest, 'MANIFEST') - self.assertEqual(cmd.template, 'MANIFEST.in') - self.assertEqual(cmd.dist_dir, 'dist') - - # formats has to be a string splitable on (' ', ',') or - # a stringlist - cmd.formats = 1 - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - cmd.formats = ['zip'] - cmd.finalize_options() - - # formats has to be known - cmd.formats = 'supazipa' - self.assertRaises(DistutilsOptionError, cmd.finalize_options) - - # the following tests make sure there is a nice error message instead - # of a traceback when parsing an invalid manifest template - - def _check_template(self, content): - dist, cmd = self.get_cmd() - os.chdir(self.tmp_dir) - self.write_file('MANIFEST.in', content) - cmd.ensure_finalized() - cmd.filelist = FileList() - cmd.read_template() - warnings = self.get_logs(WARN) - self.assertEqual(len(warnings), 1) - - def test_invalid_template_unknown_command(self): - self._check_template('taunt knights *') - - def test_invalid_template_wrong_arguments(self): - # this manifest command takes one argument - self._check_template('prune') - - @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only') - def test_invalid_template_wrong_path(self): - # on Windows, trailing slashes are not allowed - # this used to crash instead of raising a warning: #8286 - self._check_template('include examples/') - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_get_file_list(self): - # make sure MANIFEST is recalculated - dist, cmd = self.get_cmd() - - # filling data_files by pointing files in package_data - dist.package_data = {'somecode': ['*.txt']} - self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') - cmd.formats = ['gztar'] - cmd.ensure_finalized() - cmd.run() - - f = open(cmd.manifest) - try: - manifest = [line.strip() for line in f.read().split('\n') - if line.strip() != ''] - finally: - f.close() - - self.assertEqual(len(manifest), 5) - - # adding a file - self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') - - # make sure build_py is reinitialized, like a fresh run - build_py = dist.get_command_obj('build_py') - build_py.finalized = False - build_py.ensure_finalized() - - cmd.run() - - f = open(cmd.manifest) - try: - manifest2 = [line.strip() for line in f.read().split('\n') - if line.strip() != ''] - finally: - f.close() - - # do we have the new file in MANIFEST ? - self.assertEqual(len(manifest2), 6) - self.assertIn('doc2.txt', manifest2[-1]) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_manifest_marker(self): - # check that autogenerated MANIFESTs have a marker - dist, cmd = self.get_cmd() - cmd.ensure_finalized() - cmd.run() - - f = open(cmd.manifest) - try: - manifest = [line.strip() for line in f.read().split('\n') - if line.strip() != ''] - finally: - f.close() - - self.assertEqual(manifest[0], - '# file GENERATED by distutils, do NOT edit') - - @unittest.skipUnless(ZLIB_SUPPORT, "Need zlib support to run") - def test_manifest_comments(self): - # make sure comments don't cause exceptions or wrong includes - contents = dedent("""\ - # bad.py - #bad.py - good.py - """) - dist, cmd = self.get_cmd() - cmd.ensure_finalized() - self.write_file((self.tmp_dir, cmd.manifest), contents) - self.write_file((self.tmp_dir, 'good.py'), '# pick me!') - self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!") - self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!") - cmd.run() - self.assertEqual(cmd.filelist.files, ['good.py']) - - @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') - def test_manual_manifest(self): - # check that a MANIFEST without a marker is left alone - dist, cmd = self.get_cmd() - cmd.formats = ['gztar'] - cmd.ensure_finalized() - self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') - self.write_file((self.tmp_dir, 'README.manual'), - 'This project maintains its MANIFEST file itself.') - cmd.run() - self.assertEqual(cmd.filelist.files, ['README.manual']) - - f = open(cmd.manifest) - try: - manifest = [line.strip() for line in f.read().split('\n') - if line.strip() != ''] - finally: - f.close() - - self.assertEqual(manifest, ['README.manual']) - - archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') - archive = tarfile.open(archive_name) - try: - filenames = [tarinfo.name for tarinfo in archive] - finally: - archive.close() - self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO', - 'fake-1.0/README.manual']) - - @unittest.skipUnless(ZLIB_SUPPORT, "requires zlib") - @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") - @unittest.skipIf(find_executable('tar') is None, - "The tar command is not found") - @unittest.skipIf(find_executable('gzip') is None, - "The gzip command is not found") - def test_make_distribution_owner_group(self): - # now building a sdist - dist, cmd = self.get_cmd() - - # creating a gztar and specifying the owner+group - cmd.formats = ['gztar'] - cmd.owner = pwd.getpwuid(0)[0] - cmd.group = grp.getgrgid(0)[0] - cmd.ensure_finalized() - cmd.run() - - # making sure we have the good rights - archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') - archive = tarfile.open(archive_name) - try: - for member in archive.getmembers(): - self.assertEqual(member.uid, 0) - self.assertEqual(member.gid, 0) - finally: - archive.close() - - # building a sdist again - dist, cmd = self.get_cmd() - - # creating a gztar - cmd.formats = ['gztar'] - cmd.ensure_finalized() - cmd.run() - - # making sure we have the good rights - archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') - archive = tarfile.open(archive_name) - - # note that we are not testing the group ownership here - # because, depending on the platforms and the container - # rights (see #7408) - try: - for member in archive.getmembers(): - self.assertEqual(member.uid, os.getuid()) - finally: - archive.close() - -def test_suite(): - return unittest.makeSuite(SDistTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_spawn.py b/distutils/tests/test_spawn.py deleted file mode 100644 index 919d0ad9..00000000 --- a/distutils/tests/test_spawn.py +++ /dev/null @@ -1,134 +0,0 @@ -"""Tests for distutils.spawn.""" -import os -import stat -import sys -import unittest.mock -from test.support import run_unittest -from test import support as test_support - -from .py35compat import unix_shell - -from distutils.spawn import find_executable -from distutils.spawn import spawn -from distutils.errors import DistutilsExecError -from distutils.tests import support - -class SpawnTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - @unittest.skipUnless(os.name in ('nt', 'posix'), - 'Runs only under posix or nt') - def test_spawn(self): - tmpdir = self.mkdtemp() - - # creating something executable - # through the shell that returns 1 - if sys.platform != 'win32': - exe = os.path.join(tmpdir, 'foo.sh') - self.write_file(exe, '#!%s\nexit 1' % unix_shell) - else: - exe = os.path.join(tmpdir, 'foo.bat') - self.write_file(exe, 'exit 1') - - os.chmod(exe, 0o777) - self.assertRaises(DistutilsExecError, spawn, [exe]) - - # now something that works - if sys.platform != 'win32': - exe = os.path.join(tmpdir, 'foo.sh') - self.write_file(exe, '#!%s\nexit 0' % unix_shell) - else: - exe = os.path.join(tmpdir, 'foo.bat') - self.write_file(exe, 'exit 0') - - os.chmod(exe, 0o777) - spawn([exe]) # should work without any error - - def test_find_executable(self): - with test_support.temp_dir() as tmp_dir: - # use TESTFN to get a pseudo-unique filename - program_noeext = test_support.TESTFN - # Give the temporary program an ".exe" suffix for all. - # It's needed on Windows and not harmful on other platforms. - program = program_noeext + ".exe" - - filename = os.path.join(tmp_dir, program) - with open(filename, "wb"): - pass - os.chmod(filename, stat.S_IXUSR) - - # test path parameter - rv = find_executable(program, path=tmp_dir) - self.assertEqual(rv, filename) - - if sys.platform == 'win32': - # test without ".exe" extension - rv = find_executable(program_noeext, path=tmp_dir) - self.assertEqual(rv, filename) - - # test find in the current directory - with test_support.change_cwd(tmp_dir): - rv = find_executable(program) - self.assertEqual(rv, program) - - # test non-existent program - dont_exist_program = "dontexist_" + program - rv = find_executable(dont_exist_program , path=tmp_dir) - self.assertIsNone(rv) - - # PATH='': no match, except in the current directory - with test_support.EnvironmentVarGuard() as env: - env['PATH'] = '' - with unittest.mock.patch('distutils.spawn.os.confstr', - return_value=tmp_dir, create=True), \ - unittest.mock.patch('distutils.spawn.os.defpath', - tmp_dir): - rv = find_executable(program) - self.assertIsNone(rv) - - # look in current directory - with test_support.change_cwd(tmp_dir): - rv = find_executable(program) - self.assertEqual(rv, program) - - # PATH=':': explicitly looks in the current directory - with test_support.EnvironmentVarGuard() as env: - env['PATH'] = os.pathsep - with unittest.mock.patch('distutils.spawn.os.confstr', - return_value='', create=True), \ - unittest.mock.patch('distutils.spawn.os.defpath', ''): - rv = find_executable(program) - self.assertIsNone(rv) - - # look in current directory - with test_support.change_cwd(tmp_dir): - rv = find_executable(program) - self.assertEqual(rv, program) - - # missing PATH: test os.confstr("CS_PATH") and os.defpath - with test_support.EnvironmentVarGuard() as env: - env.pop('PATH', None) - - # without confstr - with unittest.mock.patch('distutils.spawn.os.confstr', - side_effect=ValueError, - create=True), \ - unittest.mock.patch('distutils.spawn.os.defpath', - tmp_dir): - rv = find_executable(program) - self.assertEqual(rv, filename) - - # with confstr - with unittest.mock.patch('distutils.spawn.os.confstr', - return_value=tmp_dir, create=True), \ - unittest.mock.patch('distutils.spawn.os.defpath', ''): - rv = find_executable(program) - self.assertEqual(rv, filename) - - -def test_suite(): - return unittest.makeSuite(SpawnTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_sysconfig.py b/distutils/tests/test_sysconfig.py deleted file mode 100644 index d5076391..00000000 --- a/distutils/tests/test_sysconfig.py +++ /dev/null @@ -1,275 +0,0 @@ -"""Tests for distutils.sysconfig.""" -import contextlib -import os -import shutil -import subprocess -import sys -import textwrap -import unittest - -from distutils import sysconfig -from distutils.ccompiler import get_default_compiler -from distutils.tests import support -from test.support import TESTFN, run_unittest, check_warnings, swap_item - -class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): - def setUp(self): - super(SysconfigTestCase, self).setUp() - self.makefile = None - - def tearDown(self): - if self.makefile is not None: - os.unlink(self.makefile) - self.cleanup_testfn() - super(SysconfigTestCase, self).tearDown() - - def cleanup_testfn(self): - if os.path.isfile(TESTFN): - os.remove(TESTFN) - elif os.path.isdir(TESTFN): - shutil.rmtree(TESTFN) - - def test_get_config_h_filename(self): - config_h = sysconfig.get_config_h_filename() - self.assertTrue(os.path.isfile(config_h), config_h) - - def test_get_python_lib(self): - # XXX doesn't work on Linux when Python was never installed before - #self.assertTrue(os.path.isdir(lib_dir), lib_dir) - # test for pythonxx.lib? - self.assertNotEqual(sysconfig.get_python_lib(), - sysconfig.get_python_lib(prefix=TESTFN)) - - def test_get_config_vars(self): - cvars = sysconfig.get_config_vars() - self.assertIsInstance(cvars, dict) - self.assertTrue(cvars) - - @unittest.skip('sysconfig.IS_PYPY') - def test_srcdir(self): - # See Issues #15322, #15364. - srcdir = sysconfig.get_config_var('srcdir') - - self.assertTrue(os.path.isabs(srcdir), srcdir) - self.assertTrue(os.path.isdir(srcdir), srcdir) - - if sysconfig.python_build: - # The python executable has not been installed so srcdir - # should be a full source checkout. - Python_h = os.path.join(srcdir, 'Include', 'Python.h') - self.assertTrue(os.path.exists(Python_h), Python_h) - self.assertTrue(sysconfig._is_python_source_dir(srcdir)) - elif os.name == 'posix': - self.assertEqual( - os.path.dirname(sysconfig.get_makefile_filename()), - srcdir) - - def test_srcdir_independent_of_cwd(self): - # srcdir should be independent of the current working directory - # See Issues #15322, #15364. - srcdir = sysconfig.get_config_var('srcdir') - cwd = os.getcwd() - try: - os.chdir('..') - srcdir2 = sysconfig.get_config_var('srcdir') - finally: - os.chdir(cwd) - self.assertEqual(srcdir, srcdir2) - - def customize_compiler(self): - # make sure AR gets caught - class compiler: - compiler_type = 'unix' - - def set_executables(self, **kw): - self.exes = kw - - sysconfig_vars = { - 'AR': 'sc_ar', - 'CC': 'sc_cc', - 'CXX': 'sc_cxx', - 'ARFLAGS': '--sc-arflags', - 'CFLAGS': '--sc-cflags', - 'CCSHARED': '--sc-ccshared', - 'LDSHARED': 'sc_ldshared', - 'SHLIB_SUFFIX': 'sc_shutil_suffix', - - # On macOS, disable _osx_support.customize_compiler() - 'CUSTOMIZED_OSX_COMPILER': 'True', - } - - comp = compiler() - with contextlib.ExitStack() as cm: - for key, value in sysconfig_vars.items(): - cm.enter_context(swap_item(sysconfig._config_vars, key, value)) - sysconfig.customize_compiler(comp) - - return comp - - @unittest.skipUnless(get_default_compiler() == 'unix', - 'not testing if default compiler is not unix') - def test_customize_compiler(self): - # Make sure that sysconfig._config_vars is initialized - sysconfig.get_config_vars() - - os.environ['AR'] = 'env_ar' - os.environ['CC'] = 'env_cc' - os.environ['CPP'] = 'env_cpp' - os.environ['CXX'] = 'env_cxx --env-cxx-flags' - os.environ['LDSHARED'] = 'env_ldshared' - os.environ['LDFLAGS'] = '--env-ldflags' - os.environ['ARFLAGS'] = '--env-arflags' - os.environ['CFLAGS'] = '--env-cflags' - os.environ['CPPFLAGS'] = '--env-cppflags' - - comp = self.customize_compiler() - self.assertEqual(comp.exes['archiver'], - 'env_ar --env-arflags') - self.assertEqual(comp.exes['preprocessor'], - 'env_cpp --env-cppflags') - self.assertEqual(comp.exes['compiler'], - 'env_cc --sc-cflags --env-cflags --env-cppflags') - self.assertEqual(comp.exes['compiler_so'], - ('env_cc --sc-cflags ' - '--env-cflags ''--env-cppflags --sc-ccshared')) - self.assertEqual(comp.exes['compiler_cxx'], - 'env_cxx --env-cxx-flags') - self.assertEqual(comp.exes['linker_exe'], - 'env_cc') - self.assertEqual(comp.exes['linker_so'], - ('env_ldshared --env-ldflags --env-cflags' - ' --env-cppflags')) - self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') - - del os.environ['AR'] - del os.environ['CC'] - del os.environ['CPP'] - del os.environ['CXX'] - del os.environ['LDSHARED'] - del os.environ['LDFLAGS'] - del os.environ['ARFLAGS'] - del os.environ['CFLAGS'] - del os.environ['CPPFLAGS'] - - comp = self.customize_compiler() - self.assertEqual(comp.exes['archiver'], - 'sc_ar --sc-arflags') - self.assertEqual(comp.exes['preprocessor'], - 'sc_cc -E') - self.assertEqual(comp.exes['compiler'], - 'sc_cc --sc-cflags') - self.assertEqual(comp.exes['compiler_so'], - 'sc_cc --sc-cflags --sc-ccshared') - self.assertEqual(comp.exes['compiler_cxx'], - 'sc_cxx') - self.assertEqual(comp.exes['linker_exe'], - 'sc_cc') - self.assertEqual(comp.exes['linker_so'], - 'sc_ldshared') - self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') - - def test_parse_makefile_base(self): - self.makefile = TESTFN - fd = open(self.makefile, 'w') - try: - fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') - fd.write('VAR=$OTHER\nOTHER=foo') - finally: - fd.close() - d = sysconfig.parse_makefile(self.makefile) - self.assertEqual(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", - 'OTHER': 'foo'}) - - def test_parse_makefile_literal_dollar(self): - self.makefile = TESTFN - fd = open(self.makefile, 'w') - try: - fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') - fd.write('VAR=$OTHER\nOTHER=foo') - finally: - fd.close() - d = sysconfig.parse_makefile(self.makefile) - self.assertEqual(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", - 'OTHER': 'foo'}) - - - def test_sysconfig_module(self): - import sysconfig as global_sysconfig - self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), - sysconfig.get_config_var('CFLAGS')) - self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), - sysconfig.get_config_var('LDFLAGS')) - - @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'), - 'compiler flags customized') - def test_sysconfig_compiler_vars(self): - # On OS X, binary installers support extension module building on - # various levels of the operating system with differing Xcode - # configurations. This requires customization of some of the - # compiler configuration directives to suit the environment on - # the installed machine. Some of these customizations may require - # running external programs and, so, are deferred until needed by - # the first extension module build. With Python 3.3, only - # the Distutils version of sysconfig is used for extension module - # builds, which happens earlier in the Distutils tests. This may - # cause the following tests to fail since no tests have caused - # the global version of sysconfig to call the customization yet. - # The solution for now is to simply skip this test in this case. - # The longer-term solution is to only have one version of sysconfig. - - import sysconfig as global_sysconfig - if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'): - self.skipTest('compiler flags customized') - self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), - sysconfig.get_config_var('LDSHARED')) - self.assertEqual(global_sysconfig.get_config_var('CC'), - sysconfig.get_config_var('CC')) - - @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, - 'EXT_SUFFIX required for this test') - def test_SO_deprecation(self): - self.assertWarns(DeprecationWarning, - sysconfig.get_config_var, 'SO') - - @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, - 'EXT_SUFFIX required for this test') - def test_SO_value(self): - with check_warnings(('', DeprecationWarning)): - self.assertEqual(sysconfig.get_config_var('SO'), - sysconfig.get_config_var('EXT_SUFFIX')) - - @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, - 'EXT_SUFFIX required for this test') - def test_SO_in_vars(self): - vars = sysconfig.get_config_vars() - self.assertIsNotNone(vars['SO']) - self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) - - def test_customize_compiler_before_get_config_vars(self): - # Issue #21923: test that a Distribution compiler - # instance can be called without an explicit call to - # get_config_vars(). - with open(TESTFN, 'w') as f: - f.writelines(textwrap.dedent('''\ - from distutils.core import Distribution - config = Distribution().get_command_obj('config') - # try_compile may pass or it may fail if no compiler - # is found but it should not raise an exception. - rc = config.try_compile('int x;') - ''')) - p = subprocess.Popen([str(sys.executable), TESTFN], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - outs, errs = p.communicate() - self.assertEqual(0, p.returncode, "Subprocess failed: " + outs) - - -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(SysconfigTestCase)) - return suite - - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/distutils/tests/test_text_file.py b/distutils/tests/test_text_file.py deleted file mode 100644 index 7e76240a..00000000 --- a/distutils/tests/test_text_file.py +++ /dev/null @@ -1,107 +0,0 @@ -"""Tests for distutils.text_file.""" -import os -import unittest -from distutils.text_file import TextFile -from distutils.tests import support -from test.support import run_unittest - -TEST_DATA = """# test file - -line 3 \\ -# intervening comment - continues on next line -""" - -class TextFileTestCase(support.TempdirManager, unittest.TestCase): - - def test_class(self): - # old tests moved from text_file.__main__ - # so they are really called by the buildbots - - # result 1: no fancy options - result1 = ['# test file\n', '\n', 'line 3 \\\n', - '# intervening comment\n', - ' continues on next line\n'] - - # result 2: just strip comments - result2 = ["\n", - "line 3 \\\n", - " continues on next line\n"] - - # result 3: just strip blank lines - result3 = ["# test file\n", - "line 3 \\\n", - "# intervening comment\n", - " continues on next line\n"] - - # result 4: default, strip comments, blank lines, - # and trailing whitespace - result4 = ["line 3 \\", - " continues on next line"] - - # result 5: strip comments and blanks, plus join lines (but don't - # "collapse" joined lines - result5 = ["line 3 continues on next line"] - - # result 6: strip comments and blanks, plus join lines (and - # "collapse" joined lines - result6 = ["line 3 continues on next line"] - - def test_input(count, description, file, expected_result): - result = file.readlines() - self.assertEqual(result, expected_result) - - tmpdir = self.mkdtemp() - filename = os.path.join(tmpdir, "test.txt") - out_file = open(filename, "w") - try: - out_file.write(TEST_DATA) - finally: - out_file.close() - - in_file = TextFile(filename, strip_comments=0, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - try: - test_input(1, "no processing", in_file, result1) - finally: - in_file.close() - - in_file = TextFile(filename, strip_comments=1, skip_blanks=0, - lstrip_ws=0, rstrip_ws=0) - try: - test_input(2, "strip comments", in_file, result2) - finally: - in_file.close() - - in_file = TextFile(filename, strip_comments=0, skip_blanks=1, - lstrip_ws=0, rstrip_ws=0) - try: - test_input(3, "strip blanks", in_file, result3) - finally: - in_file.close() - - in_file = TextFile(filename) - try: - test_input(4, "default processing", in_file, result4) - finally: - in_file.close() - - in_file = TextFile(filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1) - try: - test_input(5, "join lines without collapsing", in_file, result5) - finally: - in_file.close() - - in_file = TextFile(filename, strip_comments=1, skip_blanks=1, - join_lines=1, rstrip_ws=1, collapse_join=1) - try: - test_input(6, "join lines with collapsing", in_file, result6) - finally: - in_file.close() - -def test_suite(): - return unittest.makeSuite(TextFileTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_unixccompiler.py b/distutils/tests/test_unixccompiler.py deleted file mode 100644 index f2159662..00000000 --- a/distutils/tests/test_unixccompiler.py +++ /dev/null @@ -1,155 +0,0 @@ -"""Tests for distutils.unixccompiler.""" -import sys -import unittest -from test.support import EnvironmentVarGuard, run_unittest - -from distutils import sysconfig -from distutils.unixccompiler import UnixCCompiler - -class UnixCCompilerTestCase(unittest.TestCase): - - def setUp(self): - self._backup_platform = sys.platform - self._backup_get_config_var = sysconfig.get_config_var - self._backup_get_config_vars = sysconfig.get_config_vars - class CompilerWrapper(UnixCCompiler): - def rpath_foo(self): - return self.runtime_library_dir_option('/foo') - self.cc = CompilerWrapper() - - def tearDown(self): - sys.platform = self._backup_platform - sysconfig.get_config_var = self._backup_get_config_var - sysconfig.get_config_vars = self._backup_get_config_vars - - @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") - def test_runtime_libdir_option(self): - # Issue#5900 - # - # Ensure RUNPATH is added to extension modules with RPATH if - # GNU ld is used - - # darwin - sys.platform = 'darwin' - self.assertEqual(self.cc.rpath_foo(), '-L/foo') - - # hp-ux - sys.platform = 'hp-ux' - old_gcv = sysconfig.get_config_var - def gcv(v): - return 'xxx' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo']) - - def gcv(v): - return 'gcc' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) - - def gcv(v): - return 'g++' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) - - sysconfig.get_config_var = old_gcv - - # GCC GNULD - sys.platform = 'bar' - def gcv(v): - if v == 'CC': - return 'gcc' - elif v == 'GNULD': - return 'yes' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') - - # GCC non-GNULD - sys.platform = 'bar' - def gcv(v): - if v == 'CC': - return 'gcc' - elif v == 'GNULD': - return 'no' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') - - # GCC GNULD with fully qualified configuration prefix - # see #7617 - sys.platform = 'bar' - def gcv(v): - if v == 'CC': - return 'x86_64-pc-linux-gnu-gcc-4.4.2' - elif v == 'GNULD': - return 'yes' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') - - # non-GCC GNULD - sys.platform = 'bar' - def gcv(v): - if v == 'CC': - return 'cc' - elif v == 'GNULD': - return 'yes' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-R/foo') - - # non-GCC non-GNULD - sys.platform = 'bar' - def gcv(v): - if v == 'CC': - return 'cc' - elif v == 'GNULD': - return 'no' - sysconfig.get_config_var = gcv - self.assertEqual(self.cc.rpath_foo(), '-R/foo') - - @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') - def test_osx_cc_overrides_ldshared(self): - # Issue #18080: - # ensure that setting CC env variable also changes default linker - def gcv(v): - if v == 'LDSHARED': - return 'gcc-4.2 -bundle -undefined dynamic_lookup ' - return 'gcc-4.2' - - def gcvs(*args, _orig=sysconfig.get_config_vars): - if args: - return list(map(sysconfig.get_config_var, args)) - return _orig() - sysconfig.get_config_var = gcv - sysconfig.get_config_vars = gcvs - with EnvironmentVarGuard() as env: - env['CC'] = 'my_cc' - del env['LDSHARED'] - sysconfig.customize_compiler(self.cc) - self.assertEqual(self.cc.linker_so[0], 'my_cc') - - @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') - def test_osx_explicit_ldshared(self): - # Issue #18080: - # ensure that setting CC env variable does not change - # explicit LDSHARED setting for linker - def gcv(v): - if v == 'LDSHARED': - return 'gcc-4.2 -bundle -undefined dynamic_lookup ' - return 'gcc-4.2' - - def gcvs(*args, _orig=sysconfig.get_config_vars): - if args: - return list(map(sysconfig.get_config_var, args)) - return _orig() - sysconfig.get_config_var = gcv - sysconfig.get_config_vars = gcvs - with EnvironmentVarGuard() as env: - env['CC'] = 'my_cc' - env['LDSHARED'] = 'my_ld -bundle -dynamic' - sysconfig.customize_compiler(self.cc) - self.assertEqual(self.cc.linker_so[0], 'my_ld') - - -def test_suite(): - return unittest.makeSuite(UnixCCompilerTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_upload.py b/distutils/tests/test_upload.py deleted file mode 100644 index bca5516d..00000000 --- a/distutils/tests/test_upload.py +++ /dev/null @@ -1,223 +0,0 @@ -"""Tests for distutils.command.upload.""" -import os -import unittest -import unittest.mock as mock -from urllib.request import HTTPError - -from test.support import run_unittest - -from distutils.command import upload as upload_mod -from distutils.command.upload import upload -from distutils.core import Distribution -from distutils.errors import DistutilsError -from distutils.log import ERROR, INFO - -from distutils.tests.test_config import PYPIRC, BasePyPIRCCommandTestCase - -PYPIRC_LONG_PASSWORD = """\ -[distutils] - -index-servers = - server1 - server2 - -[server1] -username:me -password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - -[server2] -username:meagain -password: secret -realm:acme -repository:http://another.pypi/ -""" - - -PYPIRC_NOPASSWORD = """\ -[distutils] - -index-servers = - server1 - -[server1] -username:me -""" - -class FakeOpen(object): - - def __init__(self, url, msg=None, code=None): - self.url = url - if not isinstance(url, str): - self.req = url - else: - self.req = None - self.msg = msg or 'OK' - self.code = code or 200 - - def getheader(self, name, default=None): - return { - 'content-type': 'text/plain; charset=utf-8', - }.get(name.lower(), default) - - def read(self): - return b'xyzzy' - - def getcode(self): - return self.code - - -class uploadTestCase(BasePyPIRCCommandTestCase): - - def setUp(self): - super(uploadTestCase, self).setUp() - self.old_open = upload_mod.urlopen - upload_mod.urlopen = self._urlopen - self.last_open = None - self.next_msg = None - self.next_code = None - - def tearDown(self): - upload_mod.urlopen = self.old_open - super(uploadTestCase, self).tearDown() - - def _urlopen(self, url): - self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code) - return self.last_open - - def test_finalize_options(self): - - # new format - self.write_file(self.rc, PYPIRC) - dist = Distribution() - cmd = upload(dist) - cmd.finalize_options() - for attr, waited in (('username', 'me'), ('password', 'secret'), - ('realm', 'pypi'), - ('repository', 'https://upload.pypi.org/legacy/')): - self.assertEqual(getattr(cmd, attr), waited) - - def test_saved_password(self): - # file with no password - self.write_file(self.rc, PYPIRC_NOPASSWORD) - - # make sure it passes - dist = Distribution() - cmd = upload(dist) - cmd.finalize_options() - self.assertEqual(cmd.password, None) - - # make sure we get it as well, if another command - # initialized it at the dist level - dist.password = 'xxx' - cmd = upload(dist) - cmd.finalize_options() - self.assertEqual(cmd.password, 'xxx') - - def test_upload(self): - tmp = self.mkdtemp() - path = os.path.join(tmp, 'xxx') - self.write_file(path) - command, pyversion, filename = 'xxx', '2.6', path - dist_files = [(command, pyversion, filename)] - self.write_file(self.rc, PYPIRC_LONG_PASSWORD) - - # lets run it - pkg_dir, dist = self.create_dist(dist_files=dist_files) - cmd = upload(dist) - cmd.show_response = 1 - cmd.ensure_finalized() - cmd.run() - - # what did we send ? - headers = dict(self.last_open.req.headers) - self.assertGreaterEqual(int(headers['Content-length']), 2162) - content_type = headers['Content-type'] - self.assertTrue(content_type.startswith('multipart/form-data')) - self.assertEqual(self.last_open.req.get_method(), 'POST') - expected_url = 'https://upload.pypi.org/legacy/' - self.assertEqual(self.last_open.req.get_full_url(), expected_url) - data = self.last_open.req.data - self.assertIn(b'xxx',data) - self.assertIn(b'protocol_version', data) - self.assertIn(b'sha256_digest', data) - self.assertIn( - b'cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf' - b'6860', - data - ) - if b'md5_digest' in data: - self.assertIn(b'f561aaf6ef0bf14d4208bb46a4ccb3ad', data) - if b'blake2_256_digest' in data: - self.assertIn( - b'b6f289a27d4fe90da63c503bfe0a9b761a8f76bb86148565065f040be' - b'6d1c3044cf7ded78ef800509bccb4b648e507d88dc6383d67642aadcc' - b'ce443f1534330a', - data - ) - - # The PyPI response body was echoed - results = self.get_logs(INFO) - self.assertEqual(results[-1], 75 * '-' + '\nxyzzy\n' + 75 * '-') - - # bpo-32304: archives whose last byte was b'\r' were corrupted due to - # normalization intended for Mac OS 9. - def test_upload_correct_cr(self): - # content that ends with \r should not be modified. - tmp = self.mkdtemp() - path = os.path.join(tmp, 'xxx') - self.write_file(path, content='yy\r') - command, pyversion, filename = 'xxx', '2.6', path - dist_files = [(command, pyversion, filename)] - self.write_file(self.rc, PYPIRC_LONG_PASSWORD) - - # other fields that ended with \r used to be modified, now are - # preserved. - pkg_dir, dist = self.create_dist( - dist_files=dist_files, - description='long description\r' - ) - cmd = upload(dist) - cmd.show_response = 1 - cmd.ensure_finalized() - cmd.run() - - headers = dict(self.last_open.req.headers) - self.assertGreaterEqual(int(headers['Content-length']), 2172) - self.assertIn(b'long description\r', self.last_open.req.data) - - def test_upload_fails(self): - self.next_msg = "Not Found" - self.next_code = 404 - self.assertRaises(DistutilsError, self.test_upload) - - def test_wrong_exception_order(self): - tmp = self.mkdtemp() - path = os.path.join(tmp, 'xxx') - self.write_file(path) - dist_files = [('xxx', '2.6', path)] # command, pyversion, filename - self.write_file(self.rc, PYPIRC_LONG_PASSWORD) - - pkg_dir, dist = self.create_dist(dist_files=dist_files) - tests = [ - (OSError('oserror'), 'oserror', OSError), - (HTTPError('url', 400, 'httperror', {}, None), - 'Upload failed (400): httperror', DistutilsError), - ] - for exception, expected, raised_exception in tests: - with self.subTest(exception=type(exception).__name__): - with mock.patch('distutils.command.upload.urlopen', - new=mock.Mock(side_effect=exception)): - with self.assertRaises(raised_exception): - cmd = upload(dist) - cmd.ensure_finalized() - cmd.run() - results = self.get_logs(ERROR) - self.assertIn(expected, results[-1]) - self.clear_logs() - - -def test_suite(): - return unittest.makeSuite(uploadTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_util.py b/distutils/tests/test_util.py deleted file mode 100644 index bf0d4333..00000000 --- a/distutils/tests/test_util.py +++ /dev/null @@ -1,309 +0,0 @@ -"""Tests for distutils.util.""" -import os -import sys -import unittest -from copy import copy -from test.support import run_unittest -from unittest import mock - -from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError -from distutils.util import (get_platform, convert_path, change_root, - check_environ, split_quoted, strtobool, - rfc822_escape, byte_compile, - grok_environment_error) -from distutils import util # used to patch _environ_checked -from distutils.sysconfig import get_config_vars -from distutils import sysconfig -from distutils.tests import support -import _osx_support - -class UtilTestCase(support.EnvironGuard, unittest.TestCase): - - def setUp(self): - super(UtilTestCase, self).setUp() - # saving the environment - self.name = os.name - self.platform = sys.platform - self.version = sys.version - self.sep = os.sep - self.join = os.path.join - self.isabs = os.path.isabs - self.splitdrive = os.path.splitdrive - self._config_vars = copy(sysconfig._config_vars) - - # patching os.uname - if hasattr(os, 'uname'): - self.uname = os.uname - self._uname = os.uname() - else: - self.uname = None - self._uname = None - - os.uname = self._get_uname - - def tearDown(self): - # getting back the environment - os.name = self.name - sys.platform = self.platform - sys.version = self.version - os.sep = self.sep - os.path.join = self.join - os.path.isabs = self.isabs - os.path.splitdrive = self.splitdrive - if self.uname is not None: - os.uname = self.uname - else: - del os.uname - sysconfig._config_vars = copy(self._config_vars) - super(UtilTestCase, self).tearDown() - - def _set_uname(self, uname): - self._uname = uname - - def _get_uname(self): - return self._uname - - def test_get_platform(self): - - # windows XP, 32bits - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Intel)]') - sys.platform = 'win32' - self.assertEqual(get_platform(), 'win32') - - # windows XP, amd64 - os.name = 'nt' - sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' - '[MSC v.1310 32 bit (Amd64)]') - sys.platform = 'win32' - self.assertEqual(get_platform(), 'win-amd64') - - # macbook - os.name = 'posix' - sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' - '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') - sys.platform = 'darwin' - self._set_uname(('Darwin', 'macziade', '8.11.1', - ('Darwin Kernel Version 8.11.1: ' - 'Wed Oct 10 18:23:28 PDT 2007; ' - 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3' - - get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' - '-fwrapv -O3 -Wall -Wstrict-prototypes') - - cursize = sys.maxsize - sys.maxsize = (2 ** 31)-1 - try: - self.assertEqual(get_platform(), 'macosx-10.3-i386') - finally: - sys.maxsize = cursize - - # macbook with fat binaries (fat, universal or fat64) - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4' - get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEqual(get_platform(), 'macosx-10.4-fat') - - _osx_support._remove_original_values(get_config_vars()) - os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1' - self.assertEqual(get_platform(), 'macosx-10.4-fat') - - - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEqual(get_platform(), 'macosx-10.4-intel') - - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - self.assertEqual(get_platform(), 'macosx-10.4-fat3') - - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - self.assertEqual(get_platform(), 'macosx-10.4-universal') - - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3') - - self.assertEqual(get_platform(), 'macosx-10.4-fat64') - - for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): - _osx_support._remove_original_values(get_config_vars()) - get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' - '/Developer/SDKs/MacOSX10.4u.sdk ' - '-fno-strict-aliasing -fno-common ' - '-dynamic -DNDEBUG -g -O3'%(arch,)) - - self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,)) - - - # linux debian sarge - os.name = 'posix' - sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' - '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]') - sys.platform = 'linux2' - self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', - '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) - - self.assertEqual(get_platform(), 'linux-i686') - - # XXX more platforms to tests here - - def test_convert_path(self): - # linux/mac - os.sep = '/' - def _join(path): - return '/'.join(path) - os.path.join = _join - - self.assertEqual(convert_path('/home/to/my/stuff'), - '/home/to/my/stuff') - - # win - os.sep = '\\' - def _join(*path): - return '\\'.join(path) - os.path.join = _join - - self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') - self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') - - self.assertEqual(convert_path('home/to/my/stuff'), - 'home\\to\\my\\stuff') - self.assertEqual(convert_path('.'), - os.curdir) - - def test_change_root(self): - # linux/mac - os.name = 'posix' - def _isabs(path): - return path[0] == '/' - os.path.isabs = _isabs - def _join(*path): - return '/'.join(path) - os.path.join = _join - - self.assertEqual(change_root('/root', '/old/its/here'), - '/root/old/its/here') - self.assertEqual(change_root('/root', 'its/here'), - '/root/its/here') - - # windows - os.name = 'nt' - def _isabs(path): - return path.startswith('c:\\') - os.path.isabs = _isabs - def _splitdrive(path): - if path.startswith('c:'): - return ('', path.replace('c:', '')) - return ('', path) - os.path.splitdrive = _splitdrive - def _join(*path): - return '\\'.join(path) - os.path.join = _join - - self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'), - 'c:\\root\\old\\its\\here') - self.assertEqual(change_root('c:\\root', 'its\\here'), - 'c:\\root\\its\\here') - - # BugsBunny os (it's a great os) - os.name = 'BugsBunny' - self.assertRaises(DistutilsPlatformError, - change_root, 'c:\\root', 'its\\here') - - # XXX platforms to be covered: mac - - def test_check_environ(self): - util._environ_checked = 0 - os.environ.pop('HOME', None) - - check_environ() - - self.assertEqual(os.environ['PLAT'], get_platform()) - self.assertEqual(util._environ_checked, 1) - - @unittest.skipUnless(os.name == 'posix', 'specific to posix') - def test_check_environ_getpwuid(self): - util._environ_checked = 0 - os.environ.pop('HOME', None) - - import pwd - - # only set pw_dir field, other fields are not used - result = pwd.struct_passwd((None, None, None, None, None, - '/home/distutils', None)) - with mock.patch.object(pwd, 'getpwuid', return_value=result): - check_environ() - self.assertEqual(os.environ['HOME'], '/home/distutils') - - util._environ_checked = 0 - os.environ.pop('HOME', None) - - # bpo-10496: Catch pwd.getpwuid() error - with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError): - check_environ() - self.assertNotIn('HOME', os.environ) - - def test_split_quoted(self): - self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), - ['one', 'two', 'three', 'four']) - - def test_strtobool(self): - yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') - no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') - - for y in yes: - self.assertTrue(strtobool(y)) - - for n in no: - self.assertFalse(strtobool(n)) - - def test_rfc822_escape(self): - header = 'I am a\npoor\nlonesome\nheader\n' - res = rfc822_escape(header) - wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' - 'header%(8s)s') % {'8s': '\n'+8*' '} - self.assertEqual(res, wanted) - - def test_dont_write_bytecode(self): - # makes sure byte_compile raise a DistutilsError - # if sys.dont_write_bytecode is True - old_dont_write_bytecode = sys.dont_write_bytecode - sys.dont_write_bytecode = True - try: - self.assertRaises(DistutilsByteCompileError, byte_compile, []) - finally: - sys.dont_write_bytecode = old_dont_write_bytecode - - def test_grok_environment_error(self): - # test obsolete function to ensure backward compat (#4931) - exc = IOError("Unable to find batch file") - msg = grok_environment_error(exc) - self.assertEqual(msg, "error: Unable to find batch file") - - -def test_suite(): - return unittest.makeSuite(UtilTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_version.py b/distutils/tests/test_version.py deleted file mode 100644 index 8671cd2f..00000000 --- a/distutils/tests/test_version.py +++ /dev/null @@ -1,87 +0,0 @@ -"""Tests for distutils.version.""" -import unittest -from distutils.version import LooseVersion -from distutils.version import StrictVersion -from test.support import run_unittest - -class VersionTestCase(unittest.TestCase): - - def test_prerelease(self): - version = StrictVersion('1.2.3a1') - self.assertEqual(version.version, (1, 2, 3)) - self.assertEqual(version.prerelease, ('a', 1)) - self.assertEqual(str(version), '1.2.3a1') - - version = StrictVersion('1.2.0') - self.assertEqual(str(version), '1.2') - - def test_cmp_strict(self): - versions = (('1.5.1', '1.5.2b2', -1), - ('161', '3.10a', ValueError), - ('8.02', '8.02', 0), - ('3.4j', '1996.07.12', ValueError), - ('3.2.pl0', '3.1.1.6', ValueError), - ('2g6', '11g', ValueError), - ('0.9', '2.2', -1), - ('1.2.1', '1.2', 1), - ('1.1', '1.2.2', -1), - ('1.2', '1.1', 1), - ('1.2.1', '1.2.2', -1), - ('1.2.2', '1.2', 1), - ('1.2', '1.2.2', -1), - ('0.4.0', '0.4', 0), - ('1.13++', '5.5.kw', ValueError)) - - for v1, v2, wanted in versions: - try: - res = StrictVersion(v1)._cmp(StrictVersion(v2)) - except ValueError: - if wanted is ValueError: - continue - else: - raise AssertionError(("cmp(%s, %s) " - "shouldn't raise ValueError") - % (v1, v2)) - self.assertEqual(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) - res = StrictVersion(v1)._cmp(v2) - self.assertEqual(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) - res = StrictVersion(v1)._cmp(object()) - self.assertIs(res, NotImplemented, - 'cmp(%s, %s) should be NotImplemented, got %s' % - (v1, v2, res)) - - - def test_cmp(self): - versions = (('1.5.1', '1.5.2b2', -1), - ('161', '3.10a', 1), - ('8.02', '8.02', 0), - ('3.4j', '1996.07.12', -1), - ('3.2.pl0', '3.1.1.6', 1), - ('2g6', '11g', -1), - ('0.960923', '2.2beta29', -1), - ('1.13++', '5.5.kw', -1)) - - - for v1, v2, wanted in versions: - res = LooseVersion(v1)._cmp(LooseVersion(v2)) - self.assertEqual(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) - res = LooseVersion(v1)._cmp(v2) - self.assertEqual(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) - res = LooseVersion(v1)._cmp(object()) - self.assertIs(res, NotImplemented, - 'cmp(%s, %s) should be NotImplemented, got %s' % - (v1, v2, res)) - -def test_suite(): - return unittest.makeSuite(VersionTestCase) - -if __name__ == "__main__": - run_unittest(test_suite()) diff --git a/distutils/tests/test_versionpredicate.py b/distutils/tests/test_versionpredicate.py deleted file mode 100644 index 28ae09dc..00000000 --- a/distutils/tests/test_versionpredicate.py +++ /dev/null @@ -1,13 +0,0 @@ -"""Tests harness for distutils.versionpredicate. - -""" - -import distutils.versionpredicate -import doctest -from test.support import run_unittest - -def test_suite(): - return doctest.DocTestSuite(distutils.versionpredicate) - -if __name__ == '__main__': - run_unittest(test_suite()) diff --git a/distutils/text_file.py b/distutils/text_file.py deleted file mode 100644 index 93abad38..00000000 --- a/distutils/text_file.py +++ /dev/null @@ -1,286 +0,0 @@ -"""text_file - -provides the TextFile class, which gives an interface to text files -that (optionally) takes care of stripping comments, ignoring blank -lines, and joining lines with backslashes.""" - -import sys, io - - -class TextFile: - """Provides a file-like object that takes care of all the things you - commonly want to do when processing a text file that has some - line-by-line syntax: strip comments (as long as "#" is your - comment character), skip blank lines, join adjacent lines by - escaping the newline (ie. backslash at end of line), strip - leading and/or trailing whitespace. All of these are optional - and independently controllable. - - Provides a 'warn()' method so you can generate warning messages that - report physical line number, even if the logical line in question - spans multiple physical lines. Also provides 'unreadline()' for - implementing line-at-a-time lookahead. - - Constructor is called as: - - TextFile (filename=None, file=None, **options) - - It bombs (RuntimeError) if both 'filename' and 'file' are None; - 'filename' should be a string, and 'file' a file object (or - something that provides 'readline()' and 'close()' methods). It is - recommended that you supply at least 'filename', so that TextFile - can include it in warning messages. If 'file' is not supplied, - TextFile creates its own using 'io.open()'. - - The options are all boolean, and affect the value returned by - 'readline()': - strip_comments [default: true] - strip from "#" to end-of-line, as well as any whitespace - leading up to the "#" -- unless it is escaped by a backslash - lstrip_ws [default: false] - strip leading whitespace from each line before returning it - rstrip_ws [default: true] - strip trailing whitespace (including line terminator!) from - each line before returning it - skip_blanks [default: true} - skip lines that are empty *after* stripping comments and - whitespace. (If both lstrip_ws and rstrip_ws are false, - then some lines may consist of solely whitespace: these will - *not* be skipped, even if 'skip_blanks' is true.) - join_lines [default: false] - if a backslash is the last non-newline character on a line - after stripping comments and whitespace, join the following line - to it to form one "logical line"; if N consecutive lines end - with a backslash, then N+1 physical lines will be joined to - form one logical line. - collapse_join [default: false] - strip leading whitespace from lines that are joined to their - predecessor; only matters if (join_lines and not lstrip_ws) - errors [default: 'strict'] - error handler used to decode the file content - - Note that since 'rstrip_ws' can strip the trailing newline, the - semantics of 'readline()' must differ from those of the builtin file - object's 'readline()' method! In particular, 'readline()' returns - None for end-of-file: an empty string might just be a blank line (or - an all-whitespace line), if 'rstrip_ws' is true but 'skip_blanks' is - not.""" - - default_options = { 'strip_comments': 1, - 'skip_blanks': 1, - 'lstrip_ws': 0, - 'rstrip_ws': 1, - 'join_lines': 0, - 'collapse_join': 0, - 'errors': 'strict', - } - - def __init__(self, filename=None, file=None, **options): - """Construct a new TextFile object. At least one of 'filename' - (a string) and 'file' (a file-like object) must be supplied. - They keyword argument options are described above and affect - the values returned by 'readline()'.""" - if filename is None and file is None: - raise RuntimeError("you must supply either or both of 'filename' and 'file'") - - # set values for all options -- either from client option hash - # or fallback to default_options - for opt in self.default_options.keys(): - if opt in options: - setattr(self, opt, options[opt]) - else: - setattr(self, opt, self.default_options[opt]) - - # sanity check client option hash - for opt in options.keys(): - if opt not in self.default_options: - raise KeyError("invalid TextFile option '%s'" % opt) - - if file is None: - self.open(filename) - else: - self.filename = filename - self.file = file - self.current_line = 0 # assuming that file is at BOF! - - # 'linebuf' is a stack of lines that will be emptied before we - # actually read from the file; it's only populated by an - # 'unreadline()' operation - self.linebuf = [] - - def open(self, filename): - """Open a new file named 'filename'. This overrides both the - 'filename' and 'file' arguments to the constructor.""" - self.filename = filename - self.file = io.open(self.filename, 'r', errors=self.errors) - self.current_line = 0 - - def close(self): - """Close the current file and forget everything we know about it - (filename, current line number).""" - file = self.file - self.file = None - self.filename = None - self.current_line = None - file.close() - - def gen_error(self, msg, line=None): - outmsg = [] - if line is None: - line = self.current_line - outmsg.append(self.filename + ", ") - if isinstance(line, (list, tuple)): - outmsg.append("lines %d-%d: " % tuple(line)) - else: - outmsg.append("line %d: " % line) - outmsg.append(str(msg)) - return "".join(outmsg) - - def error(self, msg, line=None): - raise ValueError("error: " + self.gen_error(msg, line)) - - def warn(self, msg, line=None): - """Print (to stderr) a warning message tied to the current logical - line in the current file. If the current logical line in the - file spans multiple physical lines, the warning refers to the - whole range, eg. "lines 3-5". If 'line' supplied, it overrides - the current line number; it may be a list or tuple to indicate a - range of physical lines, or an integer for a single physical - line.""" - sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n") - - def readline(self): - """Read and return a single logical line from the current file (or - from an internal buffer if lines have previously been "unread" - with 'unreadline()'). If the 'join_lines' option is true, this - may involve reading multiple physical lines concatenated into a - single string. Updates the current line number, so calling - 'warn()' after 'readline()' emits a warning about the physical - line(s) just read. Returns None on end-of-file, since the empty - string can occur if 'rstrip_ws' is true but 'strip_blanks' is - not.""" - # If any "unread" lines waiting in 'linebuf', return the top - # one. (We don't actually buffer read-ahead data -- lines only - # get put in 'linebuf' if the client explicitly does an - # 'unreadline()'. - if self.linebuf: - line = self.linebuf[-1] - del self.linebuf[-1] - return line - - buildup_line = '' - - while True: - # read the line, make it None if EOF - line = self.file.readline() - if line == '': - line = None - - if self.strip_comments and line: - - # Look for the first "#" in the line. If none, never - # mind. If we find one and it's the first character, or - # is not preceded by "\", then it starts a comment -- - # strip the comment, strip whitespace before it, and - # carry on. Otherwise, it's just an escaped "#", so - # unescape it (and any other escaped "#"'s that might be - # lurking in there) and otherwise leave the line alone. - - pos = line.find("#") - if pos == -1: # no "#" -- no comments - pass - - # It's definitely a comment -- either "#" is the first - # character, or it's elsewhere and unescaped. - elif pos == 0 or line[pos-1] != "\\": - # Have to preserve the trailing newline, because it's - # the job of a later step (rstrip_ws) to remove it -- - # and if rstrip_ws is false, we'd better preserve it! - # (NB. this means that if the final line is all comment - # and has no trailing newline, we will think that it's - # EOF; I think that's OK.) - eol = (line[-1] == '\n') and '\n' or '' - line = line[0:pos] + eol - - # If all that's left is whitespace, then skip line - # *now*, before we try to join it to 'buildup_line' -- - # that way constructs like - # hello \\ - # # comment that should be ignored - # there - # result in "hello there". - if line.strip() == "": - continue - else: # it's an escaped "#" - line = line.replace("\\#", "#") - - # did previous line end with a backslash? then accumulate - if self.join_lines and buildup_line: - # oops: end of file - if line is None: - self.warn("continuation line immediately precedes " - "end-of-file") - return buildup_line - - if self.collapse_join: - line = line.lstrip() - line = buildup_line + line - - # careful: pay attention to line number when incrementing it - if isinstance(self.current_line, list): - self.current_line[1] = self.current_line[1] + 1 - else: - self.current_line = [self.current_line, - self.current_line + 1] - # just an ordinary line, read it as usual - else: - if line is None: # eof - return None - - # still have to be careful about incrementing the line number! - if isinstance(self.current_line, list): - self.current_line = self.current_line[1] + 1 - else: - self.current_line = self.current_line + 1 - - # strip whitespace however the client wants (leading and - # trailing, or one or the other, or neither) - if self.lstrip_ws and self.rstrip_ws: - line = line.strip() - elif self.lstrip_ws: - line = line.lstrip() - elif self.rstrip_ws: - line = line.rstrip() - - # blank line (whether we rstrip'ed or not)? skip to next line - # if appropriate - if (line == '' or line == '\n') and self.skip_blanks: - continue - - if self.join_lines: - if line[-1] == '\\': - buildup_line = line[:-1] - continue - - if line[-2:] == '\\\n': - buildup_line = line[0:-2] + '\n' - continue - - # well, I guess there's some actual content there: return it - return line - - def readlines(self): - """Read and return the list of all logical lines remaining in the - current file.""" - lines = [] - while True: - line = self.readline() - if line is None: - return lines - lines.append(line) - - def unreadline(self, line): - """Push 'line' (a string) onto an internal buffer that will be - checked by future 'readline()' calls. Handy for implementing - a parser with line-at-a-time lookahead.""" - self.linebuf.append(line) diff --git a/distutils/unixccompiler.py b/distutils/unixccompiler.py deleted file mode 100644 index 4d7a6de7..00000000 --- a/distutils/unixccompiler.py +++ /dev/null @@ -1,328 +0,0 @@ -"""distutils.unixccompiler - -Contains the UnixCCompiler class, a subclass of CCompiler that handles -the "typical" Unix-style command-line C compiler: - * macros defined with -Dname[=value] - * macros undefined with -Uname - * include search directories specified with -Idir - * libraries specified with -lllib - * library search directories specified with -Ldir - * compile handled by 'cc' (or similar) executable with -c option: - compiles .c to .o - * link static library handled by 'ar' command (possibly with 'ranlib') - * link shared library handled by 'cc -shared' -""" - -import os, sys, re - -from distutils import sysconfig -from distutils.dep_util import newer -from distutils.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options -from distutils.errors import \ - DistutilsExecError, CompileError, LibError, LinkError -from distutils import log - -if sys.platform == 'darwin': - import _osx_support - -# XXX Things not currently handled: -# * optimization/debug/warning flags; we just use whatever's in Python's -# Makefile and live with it. Is this adequate? If not, we might -# have to have a bunch of subclasses GNUCCompiler, SGICCompiler, -# SunCCompiler, and I suspect down that road lies madness. -# * even if we don't know a warning flag from an optimization flag, -# we need some way for outsiders to feed preprocessor/compiler/linker -# flags in to us -- eg. a sysadmin might want to mandate certain flags -# via a site config file, or a user might want to set something for -# compiling this module distribution only via the setup.py command -# line, whatever. As long as these options come from something on the -# current system, they can be as system-dependent as they like, and we -# should just happily stuff them into the preprocessor/compiler/linker -# options and carry on. - - -class UnixCCompiler(CCompiler): - - compiler_type = 'unix' - - # These are used by CCompiler in two places: the constructor sets - # instance attributes 'preprocessor', 'compiler', etc. from them, and - # 'set_executable()' allows any of these to be set. The defaults here - # are pretty generic; they will probably have to be set by an outsider - # (eg. using information discovered by the sysconfig about building - # Python extensions). - executables = {'preprocessor' : None, - 'compiler' : ["cc"], - 'compiler_so' : ["cc"], - 'compiler_cxx' : ["cc"], - 'linker_so' : ["cc", "-shared"], - 'linker_exe' : ["cc"], - 'archiver' : ["ar", "-cr"], - 'ranlib' : None, - } - - if sys.platform[:6] == "darwin": - executables['ranlib'] = ["ranlib"] - - # Needed for the filename generation methods provided by the base - # class, CCompiler. NB. whoever instantiates/uses a particular - # UnixCCompiler instance should set 'shared_lib_ext' -- we set a - # reasonable common default here, but it's not necessarily used on all - # Unices! - - src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"] - obj_extension = ".o" - static_lib_extension = ".a" - shared_lib_extension = ".so" - dylib_lib_extension = ".dylib" - xcode_stub_lib_extension = ".tbd" - static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" - xcode_stub_lib_format = dylib_lib_format - if sys.platform == "cygwin": - exe_extension = ".exe" - - def preprocess(self, source, output_file=None, macros=None, - include_dirs=None, extra_preargs=None, extra_postargs=None): - fixed_args = self._fix_compile_args(None, macros, include_dirs) - ignore, macros, include_dirs = fixed_args - pp_opts = gen_preprocess_options(macros, include_dirs) - pp_args = self.preprocessor + pp_opts - if output_file: - pp_args.extend(['-o', output_file]) - if extra_preargs: - pp_args[:0] = extra_preargs - if extra_postargs: - pp_args.extend(extra_postargs) - pp_args.append(source) - - # We need to preprocess: either we're being forced to, or we're - # generating output to stdout, or there's a target output file and - # the source file is newer than the target (or the target doesn't - # exist). - if self.force or output_file is None or newer(source, output_file): - if output_file: - self.mkpath(os.path.dirname(output_file)) - try: - self.spawn(pp_args) - except DistutilsExecError as msg: - raise CompileError(msg) - - def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): - compiler_so = self.compiler_so - if sys.platform == 'darwin': - compiler_so = _osx_support.compiler_fixup(compiler_so, - cc_args + extra_postargs) - try: - self.spawn(compiler_so + cc_args + [src, '-o', obj] + - extra_postargs) - except DistutilsExecError as msg: - raise CompileError(msg) - - def create_static_lib(self, objects, output_libname, - output_dir=None, debug=0, target_lang=None): - objects, output_dir = self._fix_object_args(objects, output_dir) - - output_filename = \ - self.library_filename(output_libname, output_dir=output_dir) - - if self._need_link(objects, output_filename): - self.mkpath(os.path.dirname(output_filename)) - self.spawn(self.archiver + - [output_filename] + - objects + self.objects) - - # Not many Unices required ranlib anymore -- SunOS 4.x is, I - # think the only major Unix that does. Maybe we need some - # platform intelligence here to skip ranlib if it's not - # needed -- or maybe Python's configure script took care of - # it for us, hence the check for leading colon. - if self.ranlib: - try: - self.spawn(self.ranlib + [output_filename]) - except DistutilsExecError as msg: - raise LibError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - def link(self, target_desc, objects, - output_filename, output_dir=None, libraries=None, - library_dirs=None, runtime_library_dirs=None, - export_symbols=None, debug=0, extra_preargs=None, - extra_postargs=None, build_temp=None, target_lang=None): - objects, output_dir = self._fix_object_args(objects, output_dir) - fixed_args = self._fix_lib_args(libraries, library_dirs, - runtime_library_dirs) - libraries, library_dirs, runtime_library_dirs = fixed_args - - lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, - libraries) - if not isinstance(output_dir, (str, type(None))): - raise TypeError("'output_dir' must be a string or None") - if output_dir is not None: - output_filename = os.path.join(output_dir, output_filename) - - if self._need_link(objects, output_filename): - ld_args = (objects + self.objects + - lib_opts + ['-o', output_filename]) - if debug: - ld_args[:0] = ['-g'] - if extra_preargs: - ld_args[:0] = extra_preargs - if extra_postargs: - ld_args.extend(extra_postargs) - self.mkpath(os.path.dirname(output_filename)) - try: - if target_desc == CCompiler.EXECUTABLE: - linker = self.linker_exe[:] - else: - linker = self.linker_so[:] - if target_lang == "c++" and self.compiler_cxx: - # skip over environment variable settings if /usr/bin/env - # is used to set up the linker's environment. - # This is needed on OSX. Note: this assumes that the - # normal and C++ compiler have the same environment - # settings. - i = 0 - if os.path.basename(linker[0]) == "env": - i = 1 - while '=' in linker[i]: - i += 1 - - if os.path.basename(linker[i]) == 'ld_so_aix': - # AIX platforms prefix the compiler with the ld_so_aix - # script, so we need to adjust our linker index - offset = 1 - else: - offset = 0 - - linker[i+offset] = self.compiler_cxx[i] - - if sys.platform == 'darwin': - linker = _osx_support.compiler_fixup(linker, ld_args) - - self.spawn(linker + ld_args) - except DistutilsExecError as msg: - raise LinkError(msg) - else: - log.debug("skipping %s (up-to-date)", output_filename) - - # -- Miscellaneous methods ----------------------------------------- - # These are all used by the 'gen_lib_options() function, in - # ccompiler.py. - - def library_dir_option(self, dir): - return "-L" + dir - - def _is_gcc(self, compiler_name): - return "gcc" in compiler_name or "g++" in compiler_name - - def runtime_library_dir_option(self, dir): - # XXX Hackish, at the very least. See Python bug #445902: - # http://sourceforge.net/tracker/index.php - # ?func=detail&aid=445902&group_id=5470&atid=105470 - # Linkers on different platforms need different options to - # specify that directories need to be added to the list of - # directories searched for dependencies when a dynamic library - # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to - # be told to pass the -R option through to the linker, whereas - # other compilers and gcc on other systems just know this. - # Other compilers may need something slightly different. At - # this time, there's no way to determine this information from - # the configuration data stored in the Python installation, so - # we use this hack. - compiler = os.path.basename(sysconfig.get_config_var("CC")) - if sys.platform[:6] == "darwin": - # MacOSX's linker doesn't understand the -R flag at all - return "-L" + dir - elif sys.platform[:7] == "freebsd": - return "-Wl,-rpath=" + dir - elif sys.platform[:5] == "hp-ux": - if self._is_gcc(compiler): - return ["-Wl,+s", "-L" + dir] - return ["+s", "-L" + dir] - else: - if self._is_gcc(compiler): - # gcc on non-GNU systems does not need -Wl, but can - # use it anyway. Since distutils has always passed in - # -Wl whenever gcc was used in the past it is probably - # safest to keep doing so. - if sysconfig.get_config_var("GNULD") == "yes": - # GNU ld needs an extra option to get a RUNPATH - # instead of just an RPATH. - return "-Wl,--enable-new-dtags,-R" + dir - else: - return "-Wl,-R" + dir - else: - # No idea how --enable-new-dtags would be passed on to - # ld if this system was using GNU ld. Don't know if a - # system like this even exists. - return "-R" + dir - - def library_option(self, lib): - return "-l" + lib - - def find_library_file(self, dirs, lib, debug=0): - shared_f = self.library_filename(lib, lib_type='shared') - dylib_f = self.library_filename(lib, lib_type='dylib') - xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub') - static_f = self.library_filename(lib, lib_type='static') - - if sys.platform == 'darwin': - # On OSX users can specify an alternate SDK using - # '-isysroot', calculate the SDK root if it is specified - # (and use it further on) - # - # Note that, as of Xcode 7, Apple SDKs may contain textual stub - # libraries with .tbd extensions rather than the normal .dylib - # shared libraries installed in /. The Apple compiler tool - # chain handles this transparently but it can cause problems - # for programs that are being built with an SDK and searching - # for specific libraries. Callers of find_library_file need to - # keep in mind that the base filename of the returned SDK library - # file might have a different extension from that of the library - # file installed on the running system, for example: - # /Applications/Xcode.app/Contents/Developer/Platforms/ - # MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/ - # usr/lib/libedit.tbd - # vs - # /usr/lib/libedit.dylib - cflags = sysconfig.get_config_var('CFLAGS') - m = re.search(r'-isysroot\s*(\S+)', cflags) - if m is None: - sysroot = '/' - else: - sysroot = m.group(1) - - - - for dir in dirs: - shared = os.path.join(dir, shared_f) - dylib = os.path.join(dir, dylib_f) - static = os.path.join(dir, static_f) - xcode_stub = os.path.join(dir, xcode_stub_f) - - if sys.platform == 'darwin' and ( - dir.startswith('/System/') or ( - dir.startswith('/usr/') and not dir.startswith('/usr/local/'))): - - shared = os.path.join(sysroot, dir[1:], shared_f) - dylib = os.path.join(sysroot, dir[1:], dylib_f) - static = os.path.join(sysroot, dir[1:], static_f) - xcode_stub = os.path.join(sysroot, dir[1:], xcode_stub_f) - - # We're second-guessing the linker here, with not much hard - # data to go on: GCC seems to prefer the shared library, so I'm - # assuming that *all* Unix C compilers do. And of course I'm - # ignoring even GCC's "-static" option. So sue me. - if os.path.exists(dylib): - return dylib - elif os.path.exists(xcode_stub): - return xcode_stub - elif os.path.exists(shared): - return shared - elif os.path.exists(static): - return static - - # Oops, didn't find it in *any* of 'dirs' - return None diff --git a/distutils/util.py b/distutils/util.py deleted file mode 100644 index 4b002ece..00000000 --- a/distutils/util.py +++ /dev/null @@ -1,559 +0,0 @@ -"""distutils.util - -Miscellaneous utility functions -- anything that doesn't fit into -one of the other *util.py modules. -""" - -import os -import re -import importlib.util -import string -import sys -from distutils.errors import DistutilsPlatformError -from distutils.dep_util import newer -from distutils.spawn import spawn -from distutils import log -from distutils.errors import DistutilsByteCompileError - -def get_host_platform(): - """Return a string that identifies the current platform. This is used mainly to - distinguish platform-specific build directories and platform-specific built - distributions. Typically includes the OS name and version and the - architecture (as supplied by 'os.uname()'), although the exact information - included depends on the OS; eg. on Linux, the kernel version isn't - particularly important. - - Examples of returned values: - linux-i586 - linux-alpha (?) - solaris-2.6-sun4u - - Windows will return one of: - win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) - win32 (all others - specifically, sys.platform is returned) - - For other non-POSIX platforms, currently just returns 'sys.platform'. - - """ - if os.name == 'nt': - if 'amd64' in sys.version.lower(): - return 'win-amd64' - if '(arm)' in sys.version.lower(): - return 'win-arm32' - if '(arm64)' in sys.version.lower(): - return 'win-arm64' - return sys.platform - - # Set for cross builds explicitly - if "_PYTHON_HOST_PLATFORM" in os.environ: - return os.environ["_PYTHON_HOST_PLATFORM"] - - if os.name != "posix" or not hasattr(os, 'uname'): - # XXX what about the architecture? NT is Intel or Alpha, - # Mac OS is M68k or PPC, etc. - return sys.platform - - # Try to distinguish various flavours of Unix - - (osname, host, release, version, machine) = os.uname() - - # Convert the OS name to lowercase, remove '/' characters, and translate - # spaces (for "Power Macintosh") - osname = osname.lower().replace('/', '') - machine = machine.replace(' ', '_') - machine = machine.replace('/', '-') - - if osname[:5] == "linux": - # At least on Linux/Intel, 'machine' is the processor -- - # i386, etc. - # XXX what about Alpha, SPARC, etc? - return "%s-%s" % (osname, machine) - elif osname[:5] == "sunos": - if release[0] >= "5": # SunOS 5 == Solaris 2 - osname = "solaris" - release = "%d.%s" % (int(release[0]) - 3, release[2:]) - # We can't use "platform.architecture()[0]" because a - # bootstrap problem. We use a dict to get an error - # if some suspicious happens. - bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} - machine += ".%s" % bitness[sys.maxsize] - # fall through to standard osname-release-machine representation - elif osname[:3] == "aix": - from _aix_support import aix_platform - return aix_platform() - elif osname[:6] == "cygwin": - osname = "cygwin" - rel_re = re.compile (r'[\d.]+', re.ASCII) - m = rel_re.match(release) - if m: - release = m.group() - elif osname[:6] == "darwin": - import _osx_support, distutils.sysconfig - osname, release, machine = _osx_support.get_platform_osx( - distutils.sysconfig.get_config_vars(), - osname, release, machine) - - return "%s-%s-%s" % (osname, release, machine) - -def get_platform(): - if os.name == 'nt': - TARGET_TO_PLAT = { - 'x86' : 'win32', - 'x64' : 'win-amd64', - 'arm' : 'win-arm32', - } - return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform() - else: - return get_host_platform() - -def convert_path (pathname): - """Return 'pathname' as a name that will work on the native filesystem, - i.e. split it on '/' and put it back together again using the current - directory separator. Needed because filenames in the setup script are - always supplied in Unix style, and have to be converted to the local - convention before we can actually use them in the filesystem. Raises - ValueError on non-Unix-ish systems if 'pathname' either starts or - ends with a slash. - """ - if os.sep == '/': - return pathname - if not pathname: - return pathname - if pathname[0] == '/': - raise ValueError("path '%s' cannot be absolute" % pathname) - if pathname[-1] == '/': - raise ValueError("path '%s' cannot end with '/'" % pathname) - - paths = pathname.split('/') - while '.' in paths: - paths.remove('.') - if not paths: - return os.curdir - return os.path.join(*paths) - -# convert_path () - - -def change_root (new_root, pathname): - """Return 'pathname' with 'new_root' prepended. If 'pathname' is - relative, this is equivalent to "os.path.join(new_root,pathname)". - Otherwise, it requires making 'pathname' relative and then joining the - two, which is tricky on DOS/Windows and Mac OS. - """ - if os.name == 'posix': - if not os.path.isabs(pathname): - return os.path.join(new_root, pathname) - else: - return os.path.join(new_root, pathname[1:]) - - elif os.name == 'nt': - (drive, path) = os.path.splitdrive(pathname) - if path[0] == '\\': - path = path[1:] - return os.path.join(new_root, path) - - else: - raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) - - -_environ_checked = 0 -def check_environ (): - """Ensure that 'os.environ' has all the environment variables we - guarantee that users can use in config files, command-line options, - etc. Currently this includes: - HOME - user's home directory (Unix only) - PLAT - description of the current platform, including hardware - and OS (see 'get_platform()') - """ - global _environ_checked - if _environ_checked: - return - - if os.name == 'posix' and 'HOME' not in os.environ: - try: - import pwd - os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] - except (ImportError, KeyError): - # bpo-10496: if the current user identifier doesn't exist in the - # password database, do nothing - pass - - if 'PLAT' not in os.environ: - os.environ['PLAT'] = get_platform() - - _environ_checked = 1 - - -def subst_vars (s, local_vars): - """Perform shell/Perl-style variable substitution on 'string'. Every - occurrence of '$' followed by a name is considered a variable, and - variable is substituted by the value found in the 'local_vars' - dictionary, or in 'os.environ' if it's not in 'local_vars'. - 'os.environ' is first checked/augmented to guarantee that it contains - certain values: see 'check_environ()'. Raise ValueError for any - variables not found in either 'local_vars' or 'os.environ'. - """ - check_environ() - def _subst (match, local_vars=local_vars): - var_name = match.group(1) - if var_name in local_vars: - return str(local_vars[var_name]) - else: - return os.environ[var_name] - - try: - return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) - except KeyError as var: - raise ValueError("invalid variable '$%s'" % var) - -# subst_vars () - - -def grok_environment_error (exc, prefix="error: "): - # Function kept for backward compatibility. - # Used to try clever things with EnvironmentErrors, - # but nowadays str(exception) produces good messages. - return prefix + str(exc) - - -# Needed by 'split_quoted()' -_wordchars_re = _squote_re = _dquote_re = None -def _init_regex(): - global _wordchars_re, _squote_re, _dquote_re - _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) - _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") - _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') - -def split_quoted (s): - """Split a string up according to Unix shell-like rules for quotes and - backslashes. In short: words are delimited by spaces, as long as those - spaces are not escaped by a backslash, or inside a quoted string. - Single and double quotes are equivalent, and the quote characters can - be backslash-escaped. The backslash is stripped from any two-character - escape sequence, leaving only the escaped character. The quote - characters are stripped from any quoted string. Returns a list of - words. - """ - - # This is a nice algorithm for splitting up a single string, since it - # doesn't require character-by-character examination. It was a little - # bit of a brain-bender to get it working right, though... - if _wordchars_re is None: _init_regex() - - s = s.strip() - words = [] - pos = 0 - - while s: - m = _wordchars_re.match(s, pos) - end = m.end() - if end == len(s): - words.append(s[:end]) - break - - if s[end] in string.whitespace: # unescaped, unquoted whitespace: now - words.append(s[:end]) # we definitely have a word delimiter - s = s[end:].lstrip() - pos = 0 - - elif s[end] == '\\': # preserve whatever is being escaped; - # will become part of the current word - s = s[:end] + s[end+1:] - pos = end+1 - - else: - if s[end] == "'": # slurp singly-quoted string - m = _squote_re.match(s, end) - elif s[end] == '"': # slurp doubly-quoted string - m = _dquote_re.match(s, end) - else: - raise RuntimeError("this can't happen (bad char '%c')" % s[end]) - - if m is None: - raise ValueError("bad string (mismatched %s quotes?)" % s[end]) - - (beg, end) = m.span() - s = s[:beg] + s[beg+1:end-1] + s[end:] - pos = m.end() - 2 - - if pos >= len(s): - words.append(s) - break - - return words - -# split_quoted () - - -def execute (func, args, msg=None, verbose=0, dry_run=0): - """Perform some action that affects the outside world (eg. by - writing to the filesystem). Such actions are special because they - are disabled by the 'dry_run' flag. This method takes care of all - that bureaucracy for you; all you have to do is supply the - function to call and an argument tuple for it (to embody the - "external action" being performed), and an optional message to - print. - """ - if msg is None: - msg = "%s%r" % (func.__name__, args) - if msg[-2:] == ',)': # correct for singleton tuple - msg = msg[0:-2] + ')' - - log.info(msg) - if not dry_run: - func(*args) - - -def strtobool (val): - """Convert a string representation of truth to true (1) or false (0). - - True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values - are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if - 'val' is anything else. - """ - val = val.lower() - if val in ('y', 'yes', 't', 'true', 'on', '1'): - return 1 - elif val in ('n', 'no', 'f', 'false', 'off', '0'): - return 0 - else: - raise ValueError("invalid truth value %r" % (val,)) - - -def byte_compile (py_files, - optimize=0, force=0, - prefix=None, base_dir=None, - verbose=1, dry_run=0, - direct=None): - """Byte-compile a collection of Python source files to .pyc - files in a __pycache__ subdirectory. 'py_files' is a list - of files to compile; any files that don't end in ".py" are silently - skipped. 'optimize' must be one of the following: - 0 - don't optimize - 1 - normal optimization (like "python -O") - 2 - extra optimization (like "python -OO") - If 'force' is true, all files are recompiled regardless of - timestamps. - - The source filename encoded in each bytecode file defaults to the - filenames listed in 'py_files'; you can modify these with 'prefix' and - 'basedir'. 'prefix' is a string that will be stripped off of each - source filename, and 'base_dir' is a directory name that will be - prepended (after 'prefix' is stripped). You can supply either or both - (or neither) of 'prefix' and 'base_dir', as you wish. - - If 'dry_run' is true, doesn't actually do anything that would - affect the filesystem. - - Byte-compilation is either done directly in this interpreter process - with the standard py_compile module, or indirectly by writing a - temporary script and executing it. Normally, you should let - 'byte_compile()' figure out to use direct compilation or not (see - the source for details). The 'direct' flag is used by the script - generated in indirect mode; unless you know what you're doing, leave - it set to None. - """ - - # Late import to fix a bootstrap issue: _posixsubprocess is built by - # setup.py, but setup.py uses distutils. - import subprocess - - # nothing is done if sys.dont_write_bytecode is True - if sys.dont_write_bytecode: - raise DistutilsByteCompileError('byte-compiling is disabled.') - - # First, if the caller didn't force us into direct or indirect mode, - # figure out which mode we should be in. We take a conservative - # approach: choose direct mode *only* if the current interpreter is - # in debug mode and optimize is 0. If we're not in debug mode (-O - # or -OO), we don't know which level of optimization this - # interpreter is running with, so we can't do direct - # byte-compilation and be certain that it's the right thing. Thus, - # always compile indirectly if the current interpreter is in either - # optimize mode, or if either optimization level was requested by - # the caller. - if direct is None: - direct = (__debug__ and optimize == 0) - - # "Indirect" byte-compilation: write a temporary script and then - # run it with the appropriate flags. - if not direct: - try: - from tempfile import mkstemp - (script_fd, script_name) = mkstemp(".py") - except ImportError: - from tempfile import mktemp - (script_fd, script_name) = None, mktemp(".py") - log.info("writing byte-compilation script '%s'", script_name) - if not dry_run: - if script_fd is not None: - script = os.fdopen(script_fd, "w") - else: - script = open(script_name, "w") - - with script: - script.write("""\ -from distutils.util import byte_compile -files = [ -""") - - # XXX would be nice to write absolute filenames, just for - # safety's sake (script should be more robust in the face of - # chdir'ing before running it). But this requires abspath'ing - # 'prefix' as well, and that breaks the hack in build_lib's - # 'byte_compile()' method that carefully tacks on a trailing - # slash (os.sep really) to make sure the prefix here is "just - # right". This whole prefix business is rather delicate -- the - # problem is that it's really a directory, but I'm treating it - # as a dumb string, so trailing slashes and so forth matter. - - #py_files = map(os.path.abspath, py_files) - #if prefix: - # prefix = os.path.abspath(prefix) - - script.write(",\n".join(map(repr, py_files)) + "]\n") - script.write(""" -byte_compile(files, optimize=%r, force=%r, - prefix=%r, base_dir=%r, - verbose=%r, dry_run=0, - direct=1) -""" % (optimize, force, prefix, base_dir, verbose)) - - cmd = [sys.executable] - cmd.extend(subprocess._optim_args_from_interpreter_flags()) - cmd.append(script_name) - spawn(cmd, dry_run=dry_run) - execute(os.remove, (script_name,), "removing %s" % script_name, - dry_run=dry_run) - - # "Direct" byte-compilation: use the py_compile module to compile - # right here, right now. Note that the script generated in indirect - # mode simply calls 'byte_compile()' in direct mode, a weird sort of - # cross-process recursion. Hey, it works! - else: - from py_compile import compile - - for file in py_files: - if file[-3:] != ".py": - # This lets us be lazy and not filter filenames in - # the "install_lib" command. - continue - - # Terminology from the py_compile module: - # cfile - byte-compiled file - # dfile - purported source filename (same as 'file' by default) - if optimize >= 0: - opt = '' if optimize == 0 else optimize - cfile = importlib.util.cache_from_source( - file, optimization=opt) - else: - cfile = importlib.util.cache_from_source(file) - dfile = file - if prefix: - if file[:len(prefix)] != prefix: - raise ValueError("invalid prefix: filename %r doesn't start with %r" - % (file, prefix)) - dfile = dfile[len(prefix):] - if base_dir: - dfile = os.path.join(base_dir, dfile) - - cfile_base = os.path.basename(cfile) - if direct: - if force or newer(file, cfile): - log.info("byte-compiling %s to %s", file, cfile_base) - if not dry_run: - compile(file, cfile, dfile) - else: - log.debug("skipping byte-compilation of %s to %s", - file, cfile_base) - -# byte_compile () - -def rfc822_escape (header): - """Return a version of the string escaped for inclusion in an - RFC-822 header, by ensuring there are 8 spaces space after each newline. - """ - lines = header.split('\n') - sep = '\n' + 8 * ' ' - return sep.join(lines) - -# 2to3 support - -def run_2to3(files, fixer_names=None, options=None, explicit=None): - """Invoke 2to3 on a list of Python files. - The files should all come from the build area, as the - modification is done in-place. To reduce the build time, - only files modified since the last invocation of this - function should be passed in the files argument.""" - - if not files: - return - - # Make this class local, to delay import of 2to3 - from lib2to3.refactor import RefactoringTool, get_fixers_from_package - class DistutilsRefactoringTool(RefactoringTool): - def log_error(self, msg, *args, **kw): - log.error(msg, *args) - - def log_message(self, msg, *args): - log.info(msg, *args) - - def log_debug(self, msg, *args): - log.debug(msg, *args) - - if fixer_names is None: - fixer_names = get_fixers_from_package('lib2to3.fixes') - r = DistutilsRefactoringTool(fixer_names, options=options) - r.refactor(files, write=True) - -def copydir_run_2to3(src, dest, template=None, fixer_names=None, - options=None, explicit=None): - """Recursively copy a directory, only copying new and changed files, - running run_2to3 over all newly copied Python modules afterward. - - If you give a template string, it's parsed like a MANIFEST.in. - """ - from distutils.dir_util import mkpath - from distutils.file_util import copy_file - from distutils.filelist import FileList - filelist = FileList() - curdir = os.getcwd() - os.chdir(src) - try: - filelist.findall() - finally: - os.chdir(curdir) - filelist.files[:] = filelist.allfiles - if template: - for line in template.splitlines(): - line = line.strip() - if not line: continue - filelist.process_template_line(line) - copied = [] - for filename in filelist.files: - outname = os.path.join(dest, filename) - mkpath(os.path.dirname(outname)) - res = copy_file(os.path.join(src, filename), outname, update=1) - if res[1]: copied.append(outname) - run_2to3([fn for fn in copied if fn.lower().endswith('.py')], - fixer_names=fixer_names, options=options, explicit=explicit) - return copied - -class Mixin2to3: - '''Mixin class for commands that run 2to3. - To configure 2to3, setup scripts may either change - the class variables, or inherit from individual commands - to override how 2to3 is invoked.''' - - # provide list of fixers to run; - # defaults to all from lib2to3.fixers - fixer_names = None - - # options dictionary - options = None - - # list of fixers to invoke even though they are marked as explicit - explicit = None - - def run_2to3(self, files): - return run_2to3(files, self.fixer_names, self.options, self.explicit) diff --git a/distutils/version.py b/distutils/version.py deleted file mode 100644 index c33bebae..00000000 --- a/distutils/version.py +++ /dev/null @@ -1,347 +0,0 @@ -# -# distutils/version.py -# -# Implements multiple version numbering conventions for the -# Python Module Distribution Utilities. -# -# $Id$ -# - -"""Provides classes to represent module version numbers (one class for -each style of version numbering). There are currently two such classes -implemented: StrictVersion and LooseVersion. - -Every version number class implements the following interface: - * the 'parse' method takes a string and parses it to some internal - representation; if the string is an invalid version number, - 'parse' raises a ValueError exception - * the class constructor takes an optional string argument which, - if supplied, is passed to 'parse' - * __str__ reconstructs the string that was passed to 'parse' (or - an equivalent string -- ie. one that will generate an equivalent - version number instance) - * __repr__ generates Python code to recreate the version number instance - * _cmp compares the current instance with either another instance - of the same class or a string (which will be parsed to an instance - of the same class, thus must follow the same rules) -""" - -import re - -class Version: - """Abstract base class for version numbering classes. Just provides - constructor (__init__) and reproducer (__repr__), because those - seem to be the same for all version numbering classes; and route - rich comparisons to _cmp. - """ - - def __init__ (self, vstring=None): - if vstring: - self.parse(vstring) - - def __repr__ (self): - return "%s ('%s')" % (self.__class__.__name__, str(self)) - - def __eq__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c == 0 - - def __lt__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c < 0 - - def __le__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c <= 0 - - def __gt__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c > 0 - - def __ge__(self, other): - c = self._cmp(other) - if c is NotImplemented: - return c - return c >= 0 - - -# Interface for version-number classes -- must be implemented -# by the following classes (the concrete ones -- Version should -# be treated as an abstract class). -# __init__ (string) - create and take same action as 'parse' -# (string parameter is optional) -# parse (string) - convert a string representation to whatever -# internal representation is appropriate for -# this style of version numbering -# __str__ (self) - convert back to a string; should be very similar -# (if not identical to) the string supplied to parse -# __repr__ (self) - generate Python code to recreate -# the instance -# _cmp (self, other) - compare two version numbers ('other' may -# be an unparsed version string, or another -# instance of your version class) - - -class StrictVersion (Version): - - """Version numbering for anal retentives and software idealists. - Implements the standard interface for version number classes as - described above. A version number consists of two or three - dot-separated numeric components, with an optional "pre-release" tag - on the end. The pre-release tag consists of the letter 'a' or 'b' - followed by a number. If the numeric components of two version - numbers are equal, then one with a pre-release tag will always - be deemed earlier (lesser) than one without. - - The following are valid version numbers (shown in the order that - would be obtained by sorting according to the supplied cmp function): - - 0.4 0.4.0 (these two are equivalent) - 0.4.1 - 0.5a1 - 0.5b3 - 0.5 - 0.9.6 - 1.0 - 1.0.4a3 - 1.0.4b1 - 1.0.4 - - The following are examples of invalid version numbers: - - 1 - 2.7.2.2 - 1.3.a4 - 1.3pl1 - 1.3c4 - - The rationale for this version numbering system will be explained - in the distutils documentation. - """ - - version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', - re.VERBOSE | re.ASCII) - - - def parse (self, vstring): - match = self.version_re.match(vstring) - if not match: - raise ValueError("invalid version number '%s'" % vstring) - - (major, minor, patch, prerelease, prerelease_num) = \ - match.group(1, 2, 4, 5, 6) - - if patch: - self.version = tuple(map(int, [major, minor, patch])) - else: - self.version = tuple(map(int, [major, minor])) + (0,) - - if prerelease: - self.prerelease = (prerelease[0], int(prerelease_num)) - else: - self.prerelease = None - - - def __str__ (self): - - if self.version[2] == 0: - vstring = '.'.join(map(str, self.version[0:2])) - else: - vstring = '.'.join(map(str, self.version)) - - if self.prerelease: - vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) - - return vstring - - - def _cmp (self, other): - if isinstance(other, str): - other = StrictVersion(other) - elif not isinstance(other, StrictVersion): - return NotImplemented - - if self.version != other.version: - # numeric versions don't match - # prerelease stuff doesn't matter - if self.version < other.version: - return -1 - else: - return 1 - - # have to compare prerelease - # case 1: neither has prerelease; they're equal - # case 2: self has prerelease, other doesn't; other is greater - # case 3: self doesn't have prerelease, other does: self is greater - # case 4: both have prerelease: must compare them! - - if (not self.prerelease and not other.prerelease): - return 0 - elif (self.prerelease and not other.prerelease): - return -1 - elif (not self.prerelease and other.prerelease): - return 1 - elif (self.prerelease and other.prerelease): - if self.prerelease == other.prerelease: - return 0 - elif self.prerelease < other.prerelease: - return -1 - else: - return 1 - else: - assert False, "never get here" - -# end class StrictVersion - - -# The rules according to Greg Stein: -# 1) a version number has 1 or more numbers separated by a period or by -# sequences of letters. If only periods, then these are compared -# left-to-right to determine an ordering. -# 2) sequences of letters are part of the tuple for comparison and are -# compared lexicographically -# 3) recognize the numeric components may have leading zeroes -# -# The LooseVersion class below implements these rules: a version number -# string is split up into a tuple of integer and string components, and -# comparison is a simple tuple comparison. This means that version -# numbers behave in a predictable and obvious way, but a way that might -# not necessarily be how people *want* version numbers to behave. There -# wouldn't be a problem if people could stick to purely numeric version -# numbers: just split on period and compare the numbers as tuples. -# However, people insist on putting letters into their version numbers; -# the most common purpose seems to be: -# - indicating a "pre-release" version -# ('alpha', 'beta', 'a', 'b', 'pre', 'p') -# - indicating a post-release patch ('p', 'pl', 'patch') -# but of course this can't cover all version number schemes, and there's -# no way to know what a programmer means without asking him. -# -# The problem is what to do with letters (and other non-numeric -# characters) in a version number. The current implementation does the -# obvious and predictable thing: keep them as strings and compare -# lexically within a tuple comparison. This has the desired effect if -# an appended letter sequence implies something "post-release": -# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002". -# -# However, if letters in a version number imply a pre-release version, -# the "obvious" thing isn't correct. Eg. you would expect that -# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison -# implemented here, this just isn't so. -# -# Two possible solutions come to mind. The first is to tie the -# comparison algorithm to a particular set of semantic rules, as has -# been done in the StrictVersion class above. This works great as long -# as everyone can go along with bondage and discipline. Hopefully a -# (large) subset of Python module programmers will agree that the -# particular flavour of bondage and discipline provided by StrictVersion -# provides enough benefit to be worth using, and will submit their -# version numbering scheme to its domination. The free-thinking -# anarchists in the lot will never give in, though, and something needs -# to be done to accommodate them. -# -# Perhaps a "moderately strict" version class could be implemented that -# lets almost anything slide (syntactically), and makes some heuristic -# assumptions about non-digits in version number strings. This could -# sink into special-case-hell, though; if I was as talented and -# idiosyncratic as Larry Wall, I'd go ahead and implement a class that -# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is -# just as happy dealing with things like "2g6" and "1.13++". I don't -# think I'm smart enough to do it right though. -# -# In any case, I've coded the test suite for this module (see -# ../test/test_version.py) specifically to fail on things like comparing -# "1.2a2" and "1.2". That's not because the *code* is doing anything -# wrong, it's because the simple, obvious design doesn't match my -# complicated, hairy expectations for real-world version numbers. It -# would be a snap to fix the test suite to say, "Yep, LooseVersion does -# the Right Thing" (ie. the code matches the conception). But I'd rather -# have a conception that matches common notions about version numbers. - -class LooseVersion (Version): - - """Version numbering for anarchists and software realists. - Implements the standard interface for version number classes as - described above. A version number consists of a series of numbers, - separated by either periods or strings of letters. When comparing - version numbers, the numeric components will be compared - numerically, and the alphabetic components lexically. The following - are all valid version numbers, in no particular order: - - 1.5.1 - 1.5.2b2 - 161 - 3.10a - 8.02 - 3.4j - 1996.07.12 - 3.2.pl0 - 3.1.1.6 - 2g6 - 11g - 0.960923 - 2.2beta29 - 1.13++ - 5.5.kw - 2.0b1pl0 - - In fact, there is no such thing as an invalid version number under - this scheme; the rules for comparison are simple and predictable, - but may not always give the results you want (for some definition - of "want"). - """ - - component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) - - def __init__ (self, vstring=None): - if vstring: - self.parse(vstring) - - - def parse (self, vstring): - # I've given up on thinking I can reconstruct the version string - # from the parsed tuple -- so I just store the string here for - # use by __str__ - self.vstring = vstring - components = [x for x in self.component_re.split(vstring) - if x and x != '.'] - for i, obj in enumerate(components): - try: - components[i] = int(obj) - except ValueError: - pass - - self.version = components - - - def __str__ (self): - return self.vstring - - - def __repr__ (self): - return "LooseVersion ('%s')" % str(self) - - - def _cmp (self, other): - if isinstance(other, str): - other = LooseVersion(other) - elif not isinstance(other, LooseVersion): - return NotImplemented - - if self.version == other.version: - return 0 - if self.version < other.version: - return -1 - if self.version > other.version: - return 1 - - -# end class LooseVersion diff --git a/distutils/versionpredicate.py b/distutils/versionpredicate.py deleted file mode 100644 index 062c98f2..00000000 --- a/distutils/versionpredicate.py +++ /dev/null @@ -1,166 +0,0 @@ -"""Module for parsing and testing package version predicate strings. -""" -import re -import distutils.version -import operator - - -re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)", - re.ASCII) -# (package) (rest) - -re_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses -re_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") -# (comp) (version) - - -def splitUp(pred): - """Parse a single version comparison. - - Return (comparison string, StrictVersion) - """ - res = re_splitComparison.match(pred) - if not res: - raise ValueError("bad package restriction syntax: %r" % pred) - comp, verStr = res.groups() - return (comp, distutils.version.StrictVersion(verStr)) - -compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq, - ">": operator.gt, ">=": operator.ge, "!=": operator.ne} - -class VersionPredicate: - """Parse and test package version predicates. - - >>> v = VersionPredicate('pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)') - - The `name` attribute provides the full dotted name that is given:: - - >>> v.name - 'pyepat.abc' - - The str() of a `VersionPredicate` provides a normalized - human-readable version of the expression:: - - >>> print(v) - pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) - - The `satisfied_by()` method can be used to determine with a given - version number is included in the set described by the version - restrictions:: - - >>> v.satisfied_by('1.1') - True - >>> v.satisfied_by('1.4') - True - >>> v.satisfied_by('1.0') - False - >>> v.satisfied_by('4444.4') - False - >>> v.satisfied_by('1555.1b3') - False - - `VersionPredicate` is flexible in accepting extra whitespace:: - - >>> v = VersionPredicate(' pat( == 0.1 ) ') - >>> v.name - 'pat' - >>> v.satisfied_by('0.1') - True - >>> v.satisfied_by('0.2') - False - - If any version numbers passed in do not conform to the - restrictions of `StrictVersion`, a `ValueError` is raised:: - - >>> v = VersionPredicate('p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)') - Traceback (most recent call last): - ... - ValueError: invalid version number '1.2zb3' - - It the module or package name given does not conform to what's - allowed as a legal module or package name, `ValueError` is - raised:: - - >>> v = VersionPredicate('foo-bar') - Traceback (most recent call last): - ... - ValueError: expected parenthesized list: '-bar' - - >>> v = VersionPredicate('foo bar (12.21)') - Traceback (most recent call last): - ... - ValueError: expected parenthesized list: 'bar (12.21)' - - """ - - def __init__(self, versionPredicateStr): - """Parse a version predicate string. - """ - # Fields: - # name: package name - # pred: list of (comparison string, StrictVersion) - - versionPredicateStr = versionPredicateStr.strip() - if not versionPredicateStr: - raise ValueError("empty package restriction") - match = re_validPackage.match(versionPredicateStr) - if not match: - raise ValueError("bad package name in %r" % versionPredicateStr) - self.name, paren = match.groups() - paren = paren.strip() - if paren: - match = re_paren.match(paren) - if not match: - raise ValueError("expected parenthesized list: %r" % paren) - str = match.groups()[0] - self.pred = [splitUp(aPred) for aPred in str.split(",")] - if not self.pred: - raise ValueError("empty parenthesized list in %r" - % versionPredicateStr) - else: - self.pred = [] - - def __str__(self): - if self.pred: - seq = [cond + " " + str(ver) for cond, ver in self.pred] - return self.name + " (" + ", ".join(seq) + ")" - else: - return self.name - - def satisfied_by(self, version): - """True if version is compatible with all the predicates in self. - The parameter version must be acceptable to the StrictVersion - constructor. It may be either a string or StrictVersion. - """ - for cond, ver in self.pred: - if not compmap[cond](version, ver): - return False - return True - - -_provision_rx = None - -def split_provision(value): - """Return the name and optional version number of a provision. - - The version number, if given, will be returned as a `StrictVersion` - instance, otherwise it will be `None`. - - >>> split_provision('mypkg') - ('mypkg', None) - >>> split_provision(' mypkg( 1.2 ) ') - ('mypkg', StrictVersion ('1.2')) - """ - global _provision_rx - if _provision_rx is None: - _provision_rx = re.compile( - r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", - re.ASCII) - value = value.strip() - m = _provision_rx.match(value) - if not m: - raise ValueError("illegal provides specification: %r" % value) - ver = m.group(2) or None - if ver: - ver = distutils.version.StrictVersion(ver) - return m.group(1), ver diff --git a/setuptools/_distutils/README b/setuptools/_distutils/README new file mode 100644 index 00000000..23f48850 --- /dev/null +++ b/setuptools/_distutils/README @@ -0,0 +1,11 @@ +This directory contains the Distutils package. + +There's a full documentation available at: + + http://docs.python.org/distutils/ + +The Distutils-SIG web page is also a good starting point: + + http://www.python.org/sigs/distutils-sig/ + +$Id$ diff --git a/setuptools/_distutils/__init__.py b/setuptools/_distutils/__init__.py new file mode 100644 index 00000000..7dac55b6 --- /dev/null +++ b/setuptools/_distutils/__init__.py @@ -0,0 +1,15 @@ +"""distutils + +The main package for the Python Module Distribution Utilities. Normally +used from a setup script as + + from distutils.core import setup + + setup (...) +""" + +import sys + +__version__ = sys.version[:sys.version.index(' ')] + +local = True diff --git a/setuptools/_distutils/_msvccompiler.py b/setuptools/_distutils/_msvccompiler.py new file mode 100644 index 00000000..af8099a4 --- /dev/null +++ b/setuptools/_distutils/_msvccompiler.py @@ -0,0 +1,539 @@ +"""distutils._msvccompiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for Microsoft Visual Studio 2015. + +The module is compatible with VS 2015 and later. You can find legacy support +for older versions in distutils.msvc9compiler and distutils.msvccompiler. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) +# ported to VS 2005 and VS 2008 by Christian Heimes +# ported to VS 2015 by Steve Dower + +import os +import subprocess +import winreg + +from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import CCompiler, gen_lib_options +from distutils import log +from distutils.util import get_platform + +from itertools import count + +def _find_vc2015(): + try: + key = winreg.OpenKeyEx( + winreg.HKEY_LOCAL_MACHINE, + r"Software\Microsoft\VisualStudio\SxS\VC7", + access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY + ) + except OSError: + log.debug("Visual C++ is not registered") + return None, None + + best_version = 0 + best_dir = None + with key: + for i in count(): + try: + v, vc_dir, vt = winreg.EnumValue(key, i) + except OSError: + break + if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir): + try: + version = int(float(v)) + except (ValueError, TypeError): + continue + if version >= 14 and version > best_version: + best_version, best_dir = version, vc_dir + return best_version, best_dir + +def _find_vc2017(): + """Returns "15, path" based on the result of invoking vswhere.exe + If no install is found, returns "None, None" + + The version is returned to avoid unnecessarily changing the function + result. It may be ignored when the path is not None. + + If vswhere.exe is not available, by definition, VS 2017 is not + installed. + """ + root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") + if not root: + return None, None + + try: + path = subprocess.check_output([ + os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), + "-latest", + "-prerelease", + "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", + "-property", "installationPath", + "-products", "*", + ], encoding="mbcs", errors="strict").strip() + except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): + return None, None + + path = os.path.join(path, "VC", "Auxiliary", "Build") + if os.path.isdir(path): + return 15, path + + return None, None + +PLAT_SPEC_TO_RUNTIME = { + 'x86' : 'x86', + 'x86_amd64' : 'x64', + 'x86_arm' : 'arm', + 'x86_arm64' : 'arm64' +} + +def _find_vcvarsall(plat_spec): + # bpo-38597: Removed vcruntime return value + _, best_dir = _find_vc2017() + + if not best_dir: + best_version, best_dir = _find_vc2015() + + if not best_dir: + log.debug("No suitable Visual C++ version found") + return None, None + + vcvarsall = os.path.join(best_dir, "vcvarsall.bat") + if not os.path.isfile(vcvarsall): + log.debug("%s cannot be found", vcvarsall) + return None, None + + return vcvarsall, None + +def _get_vc_env(plat_spec): + if os.getenv("DISTUTILS_USE_SDK"): + return { + key.lower(): value + for key, value in os.environ.items() + } + + vcvarsall, _ = _find_vcvarsall(plat_spec) + if not vcvarsall: + raise DistutilsPlatformError("Unable to find vcvarsall.bat") + + try: + out = subprocess.check_output( + 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), + stderr=subprocess.STDOUT, + ).decode('utf-16le', errors='replace') + except subprocess.CalledProcessError as exc: + log.error(exc.output) + raise DistutilsPlatformError("Error executing {}" + .format(exc.cmd)) + + env = { + key.lower(): value + for key, _, value in + (line.partition('=') for line in out.splitlines()) + if key and value + } + + return env + +def _find_exe(exe, paths=None): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + if not paths: + paths = os.getenv('path').split(os.pathsep) + for p in paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + return exe + +# A map keyed by get_platform() return values to values accepted by +# 'vcvarsall.bat'. Always cross-compile from x86 to work with the +# lighter-weight MSVC installs that do not include native 64-bit tools. +PLAT_TO_VCVARS = { + 'win32' : 'x86', + 'win-amd64' : 'x86_amd64', + 'win-arm32' : 'x86_arm', + 'win-arm64' : 'x86_arm64' +} + +class MSVCCompiler(CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) + # target platform (.plat_name is consistent with 'bdist') + self.plat_name = None + self.initialized = False + + def initialize(self, plat_name=None): + # multi-init means we would need to check platform same each time... + assert not self.initialized, "don't init multiple times" + if plat_name is None: + plat_name = get_platform() + # sanity check for platforms to prevent obscure errors later. + if plat_name not in PLAT_TO_VCVARS: + raise DistutilsPlatformError("--plat-name must be one of {}" + .format(tuple(PLAT_TO_VCVARS))) + + # Get the vcvarsall.bat spec for the requested platform. + plat_spec = PLAT_TO_VCVARS[plat_name] + + vc_env = _get_vc_env(plat_spec) + if not vc_env: + raise DistutilsPlatformError("Unable to find a compatible " + "Visual Studio installation.") + + self._paths = vc_env.get('path', '') + paths = self._paths.split(os.pathsep) + self.cc = _find_exe("cl.exe", paths) + self.linker = _find_exe("link.exe", paths) + self.lib = _find_exe("lib.exe", paths) + self.rc = _find_exe("rc.exe", paths) # resource compiler + self.mc = _find_exe("mc.exe", paths) # message compiler + self.mt = _find_exe("mt.exe", paths) # message compiler + + for dir in vc_env.get('include', '').split(os.pathsep): + if dir: + self.add_include_dir(dir.rstrip(os.sep)) + + for dir in vc_env.get('lib', '').split(os.pathsep): + if dir: + self.add_library_dir(dir.rstrip(os.sep)) + + self.preprocess_options = None + # bpo-38597: Always compile with dynamic linking + # Future releases of Python 3.x will include all past + # versions of vcruntime*.dll for compatibility. + self.compile_options = [ + '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD' + ] + + self.compile_options_debug = [ + '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' + ] + + ldflags = [ + '/nologo', '/INCREMENTAL:NO', '/LTCG' + ] + + ldflags_debug = [ + '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL' + ] + + self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1'] + self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1'] + self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] + self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] + self.ldflags_static = [*ldflags] + self.ldflags_static_debug = [*ldflags_debug] + + self._ldflags = { + (CCompiler.EXECUTABLE, None): self.ldflags_exe, + (CCompiler.EXECUTABLE, False): self.ldflags_exe, + (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug, + (CCompiler.SHARED_OBJECT, None): self.ldflags_shared, + (CCompiler.SHARED_OBJECT, False): self.ldflags_shared, + (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug, + (CCompiler.SHARED_LIBRARY, None): self.ldflags_static, + (CCompiler.SHARED_LIBRARY, False): self.ldflags_static, + (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug, + } + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): + ext_map = { + **{ext: self.obj_extension for ext in self.src_extensions}, + **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions}, + } + + output_dir = output_dir or '' + + def make_out_path(p): + base, ext = os.path.splitext(p) + if strip_dir: + base = os.path.basename(base) + else: + _, base = os.path.splitdrive(base) + if base.startswith((os.path.sep, os.path.altsep)): + base = base[1:] + try: + # XXX: This may produce absurdly long paths. We should check + # the length of the result and trim base until we fit within + # 260 characters. + return os.path.join(output_dir, base + ext_map[ext]) + except LookupError: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError("Don't know how to compile {}".format(p)) + + return list(map(make_out_path, source_filenames)) + + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + + add_cpp_opts = False + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + add_cpp_opts = True + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + [output_opt, input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src]) + base, _ = os.path.splitext(os.path.basename (src)) + rc_file = os.path.join(rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc, "/fo" + obj, rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError("Don't know how to compile {} to {}" + .format(src, obj)) + + args = [self.cc] + compile_opts + pp_opts + if add_cpp_opts: + args.append('/EHsc') + args.append(input_opt) + args.append("/Fo" + obj) + args.extend(extra_postargs) + + try: + self.spawn(args) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + if not self.initialized: + self.initialize() + objects, output_dir = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args)) + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + objects, output_dir = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + libraries, library_dirs, runtime_library_dirs = fixed_args + + if runtime_library_dirs: + self.warn("I don't know what to do with 'runtime_library_dirs': " + + str(runtime_library_dirs)) + + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + ldflags = self._ldflags[target_desc, debug] + + export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])] + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + build_temp = os.path.dirname(objects[0]) + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + build_temp, + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + output_dir = os.path.dirname(os.path.abspath(output_filename)) + self.mkpath(output_dir) + try: + log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args)) + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def spawn(self, cmd): + old_path = os.getenv('path') + try: + os.environ['path'] = self._paths + return super().spawn(cmd) + finally: + os.environ['path'] = old_path + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC") + + def library_option(self, lib): + return self.library_filename(lib) + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename(name)) + if os.path.isfile(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None diff --git a/setuptools/_distutils/archive_util.py b/setuptools/_distutils/archive_util.py new file mode 100644 index 00000000..565a3117 --- /dev/null +++ b/setuptools/_distutils/archive_util.py @@ -0,0 +1,256 @@ +"""distutils.archive_util + +Utility functions for creating archive files (tarballs, zip files, +that sort of thing).""" + +import os +from warnings import warn +import sys + +try: + import zipfile +except ImportError: + zipfile = None + + +from distutils.errors import DistutilsExecError +from distutils.spawn import spawn +from distutils.dir_util import mkpath +from distutils import log + +try: + from pwd import getpwnam +except ImportError: + getpwnam = None + +try: + from grp import getgrnam +except ImportError: + getgrnam = None + +def _get_gid(name): + """Returns a gid, given a group name.""" + if getgrnam is None or name is None: + return None + try: + result = getgrnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def _get_uid(name): + """Returns an uid, given a user name.""" + if getpwnam is None or name is None: + return None + try: + result = getpwnam(name) + except KeyError: + result = None + if result is not None: + return result[2] + return None + +def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, + owner=None, group=None): + """Create a (possibly compressed) tar file from all the files under + 'base_dir'. + + 'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or + None. ("compress" will be deprecated in Python 3.2) + + 'owner' and 'group' can be used to define an owner and a group for the + archive that is being built. If not provided, the current owner and group + will be used. + + The output tar file will be named 'base_dir' + ".tar", possibly plus + the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z"). + + Returns the output filename. + """ + tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '', + 'compress': ''} + compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', + 'compress': '.Z'} + + # flags for compression program, each element of list will be an argument + if compress is not None and compress not in compress_ext.keys(): + raise ValueError( + "bad value for 'compress': must be None, 'gzip', 'bzip2', " + "'xz' or 'compress'") + + archive_name = base_name + '.tar' + if compress != 'compress': + archive_name += compress_ext.get(compress, '') + + mkpath(os.path.dirname(archive_name), dry_run=dry_run) + + # creating the tarball + import tarfile # late import so Python build itself doesn't break + + log.info('Creating tar archive') + + uid = _get_uid(owner) + gid = _get_gid(group) + + def _set_uid_gid(tarinfo): + if gid is not None: + tarinfo.gid = gid + tarinfo.gname = group + if uid is not None: + tarinfo.uid = uid + tarinfo.uname = owner + return tarinfo + + if not dry_run: + tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) + try: + tar.add(base_dir, filter=_set_uid_gid) + finally: + tar.close() + + # compression using `compress` + if compress == 'compress': + warn("'compress' will be deprecated.", PendingDeprecationWarning) + # the option varies depending on the platform + compressed_name = archive_name + compress_ext[compress] + if sys.platform == 'win32': + cmd = [compress, archive_name, compressed_name] + else: + cmd = [compress, '-f', archive_name] + spawn(cmd, dry_run=dry_run) + return compressed_name + + return archive_name + +def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): + """Create a zip file from all the files under 'base_dir'. + + The output zip file will be named 'base_name' + ".zip". Uses either the + "zipfile" Python module (if available) or the InfoZIP "zip" utility + (if installed and found on the default search path). If neither tool is + available, raises DistutilsExecError. Returns the name of the output zip + file. + """ + zip_filename = base_name + ".zip" + mkpath(os.path.dirname(zip_filename), dry_run=dry_run) + + # If zipfile module is not available, try spawning an external + # 'zip' command. + if zipfile is None: + if verbose: + zipoptions = "-r" + else: + zipoptions = "-rq" + + try: + spawn(["zip", zipoptions, zip_filename, base_dir], + dry_run=dry_run) + except DistutilsExecError: + # XXX really should distinguish between "couldn't find + # external 'zip' command" and "zip failed". + raise DistutilsExecError(("unable to create zip file '%s': " + "could neither import the 'zipfile' module nor " + "find a standalone zip utility") % zip_filename) + + else: + log.info("creating '%s' and adding '%s' to it", + zip_filename, base_dir) + + if not dry_run: + try: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) + except RuntimeError: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_STORED) + + with zip: + if base_dir != os.curdir: + path = os.path.normpath(os.path.join(base_dir, '')) + zip.write(path, path) + log.info("adding '%s'", path) + for dirpath, dirnames, filenames in os.walk(base_dir): + for name in dirnames: + path = os.path.normpath(os.path.join(dirpath, name, '')) + zip.write(path, path) + log.info("adding '%s'", path) + for name in filenames: + path = os.path.normpath(os.path.join(dirpath, name)) + if os.path.isfile(path): + zip.write(path, path) + log.info("adding '%s'", path) + + return zip_filename + +ARCHIVE_FORMATS = { + 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), + 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), + 'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"), + 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"), + 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"), + 'zip': (make_zipfile, [],"ZIP file") + } + +def check_archive_formats(formats): + """Returns the first format from the 'format' list that is unknown. + + If all formats are known, returns None + """ + for format in formats: + if format not in ARCHIVE_FORMATS: + return format + return None + +def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, + dry_run=0, owner=None, group=None): + """Create an archive file (eg. zip or tar). + + 'base_name' is the name of the file to create, minus any format-specific + extension; 'format' is the archive format: one of "zip", "tar", "gztar", + "bztar", "xztar", or "ztar". + + 'root_dir' is a directory that will be the root directory of the + archive; ie. we typically chdir into 'root_dir' before creating the + archive. 'base_dir' is the directory where we start archiving from; + ie. 'base_dir' will be the common prefix of all files and + directories in the archive. 'root_dir' and 'base_dir' both default + to the current directory. Returns the name of the archive file. + + 'owner' and 'group' are used when creating a tar archive. By default, + uses the current owner and group. + """ + save_cwd = os.getcwd() + if root_dir is not None: + log.debug("changing into '%s'", root_dir) + base_name = os.path.abspath(base_name) + if not dry_run: + os.chdir(root_dir) + + if base_dir is None: + base_dir = os.curdir + + kwargs = {'dry_run': dry_run} + + try: + format_info = ARCHIVE_FORMATS[format] + except KeyError: + raise ValueError("unknown archive format '%s'" % format) + + func = format_info[0] + for arg, val in format_info[1]: + kwargs[arg] = val + + if format != 'zip': + kwargs['owner'] = owner + kwargs['group'] = group + + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + log.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) + + return filename diff --git a/setuptools/_distutils/bcppcompiler.py b/setuptools/_distutils/bcppcompiler.py new file mode 100644 index 00000000..071fea5d --- /dev/null +++ b/setuptools/_distutils/bcppcompiler.py @@ -0,0 +1,393 @@ +"""distutils.bcppcompiler + +Contains BorlandCCompiler, an implementation of the abstract CCompiler class +for the Borland C++ compiler. +""" + +# This implementation by Lyle Johnson, based on the original msvccompiler.py +# module and using the directions originally published by Gordon Williams. + +# XXX looks like there's a LOT of overlap between these two classes: +# someone should sit down and factor out the common code as +# WindowsCCompiler! --GPW + + +import os +from distutils.errors import \ + DistutilsExecError, \ + CompileError, LibError, LinkError, UnknownFileError +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options +from distutils.file_util import write_file +from distutils.dep_util import newer +from distutils import log + +class BCPPCompiler(CCompiler) : + """Concrete class that implements an interface to the Borland C/C++ + compiler, as defined by the CCompiler abstract class. + """ + + compiler_type = 'bcpp' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = _c_extensions + _cpp_extensions + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + + def __init__ (self, + verbose=0, + dry_run=0, + force=0): + + CCompiler.__init__ (self, verbose, dry_run, force) + + # These executables are assumed to all be in the path. + # Borland doesn't seem to use any special registry settings to + # indicate their installation locations. + + self.cc = "bcc32.exe" + self.linker = "ilink32.exe" + self.lib = "tlib.exe" + + self.preprocess_options = None + self.compile_options = ['/tWM', '/O2', '/q', '/g0'] + self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0'] + + self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x'] + self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x'] + self.ldflags_static = [] + self.ldflags_exe = ['/Gn', '/q', '/x'] + self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r'] + + + # -- Worker methods ------------------------------------------------ + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + macros, objects, extra_postargs, pp_opts, build = \ + self._setup_compile(output_dir, macros, include_dirs, sources, + depends, extra_postargs) + compile_opts = extra_preargs or [] + compile_opts.append ('-c') + if debug: + compile_opts.extend (self.compile_options_debug) + else: + compile_opts.extend (self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + # XXX why do the normpath here? + src = os.path.normpath(src) + obj = os.path.normpath(obj) + # XXX _setup_compile() did a mkpath() too but before the normpath. + # Is it possible to skip the normpath? + self.mkpath(os.path.dirname(obj)) + + if ext == '.res': + # This is already a binary file -- skip it. + continue # the 'for' loop + if ext == '.rc': + # This needs to be compiled to a .res file -- do it now. + try: + self.spawn (["brcc32", "-fo", obj, src]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue # the 'for' loop + + # The next two are both for the real compiler. + if ext in self._c_extensions: + input_opt = "" + elif ext in self._cpp_extensions: + input_opt = "-P" + else: + # Unknown file type -- no extra options. The compiler + # will probably fail, but let it just in case this is a + # file the compiler recognizes even if we don't. + input_opt = "" + + output_opt = "-o" + obj + + # Compiler command line syntax is: "bcc32 [options] file(s)". + # Note that the source file names must appear at the end of + # the command line. + try: + self.spawn ([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs + [src]) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + # compile () + + + def create_static_lib (self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + (objects, output_dir) = self._fix_object_args (objects, output_dir) + output_filename = \ + self.library_filename (output_libname, output_dir=output_dir) + + if self._need_link (objects, output_filename): + lib_args = [output_filename, '/u'] + objects + if debug: + pass # XXX what goes here? + try: + self.spawn ([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + # create_static_lib () + + + def link (self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + # XXX this ignores 'build_temp'! should follow the lead of + # msvccompiler.py + + (objects, output_dir) = self._fix_object_args (objects, output_dir) + (libraries, library_dirs, runtime_library_dirs) = \ + self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) + + if runtime_library_dirs: + log.warn("I don't know what to do with 'runtime_library_dirs': %s", + str(runtime_library_dirs)) + + if output_dir is not None: + output_filename = os.path.join (output_dir, output_filename) + + if self._need_link (objects, output_filename): + + # Figure out linker args based on type of target. + if target_desc == CCompiler.EXECUTABLE: + startup_obj = 'c0w32' + if debug: + ld_args = self.ldflags_exe_debug[:] + else: + ld_args = self.ldflags_exe[:] + else: + startup_obj = 'c0d32' + if debug: + ld_args = self.ldflags_shared_debug[:] + else: + ld_args = self.ldflags_shared[:] + + + # Create a temporary exports file for use by the linker + if export_symbols is None: + def_file = '' + else: + head, tail = os.path.split (output_filename) + modname, ext = os.path.splitext (tail) + temp_dir = os.path.dirname(objects[0]) # preserve tree structure + def_file = os.path.join (temp_dir, '%s.def' % modname) + contents = ['EXPORTS'] + for sym in (export_symbols or []): + contents.append(' %s=_%s' % (sym, sym)) + self.execute(write_file, (def_file, contents), + "writing %s" % def_file) + + # Borland C++ has problems with '/' in paths + objects2 = map(os.path.normpath, objects) + # split objects in .obj and .res files + # Borland C++ needs them at different positions in the command line + objects = [startup_obj] + resources = [] + for file in objects2: + (base, ext) = os.path.splitext(os.path.normcase(file)) + if ext == '.res': + resources.append(file) + else: + objects.append(file) + + + for l in library_dirs: + ld_args.append("/L%s" % os.path.normpath(l)) + ld_args.append("/L.") # we sometimes use relative paths + + # list of object files + ld_args.extend(objects) + + # XXX the command-line syntax for Borland C++ is a bit wonky; + # certain filenames are jammed together in one big string, but + # comma-delimited. This doesn't mesh too well with the + # Unix-centric attitude (with a DOS/Windows quoting hack) of + # 'spawn()', so constructing the argument list is a bit + # awkward. Note that doing the obvious thing and jamming all + # the filenames and commas into one argument would be wrong, + # because 'spawn()' would quote any filenames with spaces in + # them. Arghghh!. Apparently it works fine as coded... + + # name of dll/exe file + ld_args.extend([',',output_filename]) + # no map file and start libraries + ld_args.append(',,') + + for lib in libraries: + # see if we find it and if there is a bcpp specific lib + # (xxx_bcpp.lib) + libfile = self.find_library_file(library_dirs, lib, debug) + if libfile is None: + ld_args.append(lib) + # probably a BCPP internal library -- don't warn + else: + # full name which prefers bcpp_xxx.lib over xxx.lib + ld_args.append(libfile) + + # some default libraries + ld_args.append ('import32') + ld_args.append ('cw32mt') + + # def file for export symbols + ld_args.extend([',',def_file]) + # add resource files + ld_args.append(',') + ld_args.extend(resources) + + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath (os.path.dirname (output_filename)) + try: + self.spawn ([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + else: + log.debug("skipping %s (up-to-date)", output_filename) + + # link () + + # -- Miscellaneous methods ----------------------------------------- + + + def find_library_file (self, dirs, lib, debug=0): + # List of effective library names to try, in order of preference: + # xxx_bcpp.lib is better than xxx.lib + # and xxx_d.lib is better than xxx.lib if debug is set + # + # The "_bcpp" suffix is to handle a Python installation for people + # with multiple compilers (primarily Distutils hackers, I suspect + # ;-). The idea is they'd have one static library for each + # compiler they care about, since (almost?) every Windows compiler + # seems to have a different format for static libraries. + if debug: + dlib = (lib + "_d") + try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib) + else: + try_names = (lib + "_bcpp", lib) + + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename(name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # overwrite the one from CCompiler to support rc and res-files + def object_filenames (self, + source_filenames, + strip_dir=0, + output_dir=''): + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + # use normcase to make sure '.rc' is really '.rc' and not '.RC' + (base, ext) = os.path.splitext (os.path.normcase(src_name)) + if ext not in (self.src_extensions + ['.rc','.res']): + raise UnknownFileError("unknown file type '%s' (from '%s')" % \ + (ext, src_name)) + if strip_dir: + base = os.path.basename (base) + if ext == '.res': + # these can go unchanged + obj_names.append (os.path.join (output_dir, base + ext)) + elif ext == '.rc': + # these need to be compiled to .res-files + obj_names.append (os.path.join (output_dir, base + '.res')) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + # object_filenames () + + def preprocess (self, + source, + output_file=None, + macros=None, + include_dirs=None, + extra_preargs=None, + extra_postargs=None): + + (_, macros, include_dirs) = \ + self._fix_compile_args(None, macros, include_dirs) + pp_opts = gen_preprocess_options(macros, include_dirs) + pp_args = ['cpp32.exe'] + pp_opts + if output_file is not None: + pp_args.append('-o' + output_file) + if extra_preargs: + pp_args[:0] = extra_preargs + if extra_postargs: + pp_args.extend(extra_postargs) + pp_args.append(source) + + # We need to preprocess: either we're being forced to, or the + # source file is newer than the target (or the target doesn't + # exist). + if self.force or output_file is None or newer(source, output_file): + if output_file: + self.mkpath(os.path.dirname(output_file)) + try: + self.spawn(pp_args) + except DistutilsExecError as msg: + print(msg) + raise CompileError(msg) + + # preprocess() diff --git a/setuptools/_distutils/ccompiler.py b/setuptools/_distutils/ccompiler.py new file mode 100644 index 00000000..b5ef143e --- /dev/null +++ b/setuptools/_distutils/ccompiler.py @@ -0,0 +1,1116 @@ +"""distutils.ccompiler + +Contains CCompiler, an abstract base class that defines the interface +for the Distutils compiler abstraction model.""" + +import sys, os, re +from distutils.errors import * +from distutils.spawn import spawn +from distutils.file_util import move_file +from distutils.dir_util import mkpath +from distutils.dep_util import newer_group +from distutils.util import split_quoted, execute +from distutils import log + +class CCompiler: + """Abstract base class to define the interface that must be implemented + by real compiler classes. Also has some utility methods used by + several compiler classes. + + The basic idea behind a compiler abstraction class is that each + instance can be used for all the compile/link steps in building a + single project. Thus, attributes common to all of those compile and + link steps -- include directories, macros to define, libraries to link + against, etc. -- are attributes of the compiler instance. To allow for + variability in how individual files are treated, most of those + attributes may be varied on a per-compilation or per-link basis. + """ + + # 'compiler_type' is a class attribute that identifies this class. It + # keeps code that wants to know what kind of compiler it's dealing with + # from having to import all possible compiler classes just to do an + # 'isinstance'. In concrete CCompiler subclasses, 'compiler_type' + # should really, really be one of the keys of the 'compiler_class' + # dictionary (see below -- used by the 'new_compiler()' factory + # function) -- authors of new compiler interface classes are + # responsible for updating 'compiler_class'! + compiler_type = None + + # XXX things not handled by this compiler abstraction model: + # * client can't provide additional options for a compiler, + # e.g. warning, optimization, debugging flags. Perhaps this + # should be the domain of concrete compiler abstraction classes + # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base + # class should have methods for the common ones. + # * can't completely override the include or library searchg + # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". + # I'm not sure how widely supported this is even by Unix + # compilers, much less on other platforms. And I'm even less + # sure how useful it is; maybe for cross-compiling, but + # support for that is a ways off. (And anyways, cross + # compilers probably have a dedicated binary with the + # right paths compiled in. I hope.) + # * can't do really freaky things with the library list/library + # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against + # different versions of libfoo.a in different locations. I + # think this is useless without the ability to null out the + # library search path anyways. + + + # Subclasses that rely on the standard filename generation methods + # implemented below should override these; see the comment near + # those methods ('object_filenames()' et. al.) for details: + src_extensions = None # list of strings + obj_extension = None # string + static_lib_extension = None + shared_lib_extension = None # string + static_lib_format = None # format string + shared_lib_format = None # prob. same as static_lib_format + exe_extension = None # string + + # Default language settings. language_map is used to detect a source + # file or Extension target language, checking source filenames. + # language_order is used to detect the language precedence, when deciding + # what language to use when mixing source types. For example, if some + # extension has two files with ".c" extension, and one with ".cpp", it + # is still linked as c++. + language_map = {".c" : "c", + ".cc" : "c++", + ".cpp" : "c++", + ".cxx" : "c++", + ".m" : "objc", + } + language_order = ["c++", "objc", "c"] + + def __init__(self, verbose=0, dry_run=0, force=0): + self.dry_run = dry_run + self.force = force + self.verbose = verbose + + # 'output_dir': a common output directory for object, library, + # shared object, and shared library files + self.output_dir = None + + # 'macros': a list of macro definitions (or undefinitions). A + # macro definition is a 2-tuple (name, value), where the value is + # either a string or None (no explicit value). A macro + # undefinition is a 1-tuple (name,). + self.macros = [] + + # 'include_dirs': a list of directories to search for include files + self.include_dirs = [] + + # 'libraries': a list of libraries to include in any link + # (library names, not filenames: eg. "foo" not "libfoo.a") + self.libraries = [] + + # 'library_dirs': a list of directories to search for libraries + self.library_dirs = [] + + # 'runtime_library_dirs': a list of directories to search for + # shared libraries/objects at runtime + self.runtime_library_dirs = [] + + # 'objects': a list of object files (or similar, such as explicitly + # named library files) to include on any link + self.objects = [] + + for key in self.executables.keys(): + self.set_executable(key, self.executables[key]) + + def set_executables(self, **kwargs): + """Define the executables (and options for them) that will be run + to perform the various stages of compilation. The exact set of + executables that may be specified here depends on the compiler + class (via the 'executables' class attribute), but most will have: + compiler the C/C++ compiler + linker_so linker used to create shared objects and libraries + linker_exe linker used to create binary executables + archiver static library creator + + On platforms with a command-line (Unix, DOS/Windows), each of these + is a string that will be split into executable name and (optional) + list of arguments. (Splitting the string is done similarly to how + Unix shells operate: words are delimited by spaces, but quotes and + backslashes can override this. See + 'distutils.util.split_quoted()'.) + """ + + # Note that some CCompiler implementation classes will define class + # attributes 'cpp', 'cc', etc. with hard-coded executable names; + # this is appropriate when a compiler class is for exactly one + # compiler/OS combination (eg. MSVCCompiler). Other compiler + # classes (UnixCCompiler, in particular) are driven by information + # discovered at run-time, since there are many different ways to do + # basically the same things with Unix C compilers. + + for key in kwargs: + if key not in self.executables: + raise ValueError("unknown executable '%s' for class %s" % + (key, self.__class__.__name__)) + self.set_executable(key, kwargs[key]) + + def set_executable(self, key, value): + if isinstance(value, str): + setattr(self, key, split_quoted(value)) + else: + setattr(self, key, value) + + def _find_macro(self, name): + i = 0 + for defn in self.macros: + if defn[0] == name: + return i + i += 1 + return None + + def _check_macro_definitions(self, definitions): + """Ensures that every element of 'definitions' is a valid macro + definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do + nothing if all definitions are OK, raise TypeError otherwise. + """ + for defn in definitions: + if not (isinstance(defn, tuple) and + (len(defn) in (1, 2) and + (isinstance (defn[1], str) or defn[1] is None)) and + isinstance (defn[0], str)): + raise TypeError(("invalid macro definition '%s': " % defn) + \ + "must be tuple (string,), (string, string), or " + \ + "(string, None)") + + + # -- Bookkeeping methods ------------------------------------------- + + def define_macro(self, name, value=None): + """Define a preprocessor macro for all compilations driven by this + compiler object. The optional parameter 'value' should be a + string; if it is not supplied, then the macro will be defined + without an explicit value and the exact outcome depends on the + compiler used (XXX true? does ANSI say anything about this?) + """ + # Delete from the list of macro definitions/undefinitions if + # already there (so that this one will take precedence). + i = self._find_macro (name) + if i is not None: + del self.macros[i] + + self.macros.append((name, value)) + + def undefine_macro(self, name): + """Undefine a preprocessor macro for all compilations driven by + this compiler object. If the same macro is defined by + 'define_macro()' and undefined by 'undefine_macro()' the last call + takes precedence (including multiple redefinitions or + undefinitions). If the macro is redefined/undefined on a + per-compilation basis (ie. in the call to 'compile()'), then that + takes precedence. + """ + # Delete from the list of macro definitions/undefinitions if + # already there (so that this one will take precedence). + i = self._find_macro (name) + if i is not None: + del self.macros[i] + + undefn = (name,) + self.macros.append(undefn) + + def add_include_dir(self, dir): + """Add 'dir' to the list of directories that will be searched for + header files. The compiler is instructed to search directories in + the order in which they are supplied by successive calls to + 'add_include_dir()'. + """ + self.include_dirs.append(dir) + + def set_include_dirs(self, dirs): + """Set the list of directories that will be searched to 'dirs' (a + list of strings). Overrides any preceding calls to + 'add_include_dir()'; subsequence calls to 'add_include_dir()' add + to the list passed to 'set_include_dirs()'. This does not affect + any list of standard include directories that the compiler may + search by default. + """ + self.include_dirs = dirs[:] + + def add_library(self, libname): + """Add 'libname' to the list of libraries that will be included in + all links driven by this compiler object. Note that 'libname' + should *not* be the name of a file containing a library, but the + name of the library itself: the actual filename will be inferred by + the linker, the compiler, or the compiler class (depending on the + platform). + + The linker will be instructed to link against libraries in the + order they were supplied to 'add_library()' and/or + 'set_libraries()'. It is perfectly valid to duplicate library + names; the linker will be instructed to link against libraries as + many times as they are mentioned. + """ + self.libraries.append(libname) + + def set_libraries(self, libnames): + """Set the list of libraries to be included in all links driven by + this compiler object to 'libnames' (a list of strings). This does + not affect any standard system libraries that the linker may + include by default. + """ + self.libraries = libnames[:] + + def add_library_dir(self, dir): + """Add 'dir' to the list of directories that will be searched for + libraries specified to 'add_library()' and 'set_libraries()'. The + linker will be instructed to search for libraries in the order they + are supplied to 'add_library_dir()' and/or 'set_library_dirs()'. + """ + self.library_dirs.append(dir) + + def set_library_dirs(self, dirs): + """Set the list of library search directories to 'dirs' (a list of + strings). This does not affect any standard library search path + that the linker may search by default. + """ + self.library_dirs = dirs[:] + + def add_runtime_library_dir(self, dir): + """Add 'dir' to the list of directories that will be searched for + shared libraries at runtime. + """ + self.runtime_library_dirs.append(dir) + + def set_runtime_library_dirs(self, dirs): + """Set the list of directories to search for shared libraries at + runtime to 'dirs' (a list of strings). This does not affect any + standard search path that the runtime linker may search by + default. + """ + self.runtime_library_dirs = dirs[:] + + def add_link_object(self, object): + """Add 'object' to the list of object files (or analogues, such as + explicitly named library files or the output of "resource + compilers") to be included in every link driven by this compiler + object. + """ + self.objects.append(object) + + def set_link_objects(self, objects): + """Set the list of object files (or analogues) to be included in + every link to 'objects'. This does not affect any standard object + files that the linker may include by default (such as system + libraries). + """ + self.objects = objects[:] + + + # -- Private utility methods -------------------------------------- + # (here for the convenience of subclasses) + + # Helper method to prep compiler in subclass compile() methods + + def _setup_compile(self, outdir, macros, incdirs, sources, depends, + extra): + """Process arguments and decide which source files to compile.""" + if outdir is None: + outdir = self.output_dir + elif not isinstance(outdir, str): + raise TypeError("'output_dir' must be a string or None") + + if macros is None: + macros = self.macros + elif isinstance(macros, list): + macros = macros + (self.macros or []) + else: + raise TypeError("'macros' (if supplied) must be a list of tuples") + + if incdirs is None: + incdirs = self.include_dirs + elif isinstance(incdirs, (list, tuple)): + incdirs = list(incdirs) + (self.include_dirs or []) + else: + raise TypeError( + "'include_dirs' (if supplied) must be a list of strings") + + if extra is None: + extra = [] + + # Get the list of expected output (object) files + objects = self.object_filenames(sources, strip_dir=0, + output_dir=outdir) + assert len(objects) == len(sources) + + pp_opts = gen_preprocess_options(macros, incdirs) + + build = {} + for i in range(len(sources)): + src = sources[i] + obj = objects[i] + ext = os.path.splitext(src)[1] + self.mkpath(os.path.dirname(obj)) + build[obj] = (src, ext) + + return macros, objects, extra, pp_opts, build + + def _get_cc_args(self, pp_opts, debug, before): + # works for unixccompiler, cygwinccompiler + cc_args = pp_opts + ['-c'] + if debug: + cc_args[:0] = ['-g'] + if before: + cc_args[:0] = before + return cc_args + + def _fix_compile_args(self, output_dir, macros, include_dirs): + """Typecheck and fix-up some of the arguments to the 'compile()' + method, and return fixed-up values. Specifically: if 'output_dir' + is None, replaces it with 'self.output_dir'; ensures that 'macros' + is a list, and augments it with 'self.macros'; ensures that + 'include_dirs' is a list, and augments it with 'self.include_dirs'. + Guarantees that the returned values are of the correct type, + i.e. for 'output_dir' either string or None, and for 'macros' and + 'include_dirs' either list or None. + """ + if output_dir is None: + output_dir = self.output_dir + elif not isinstance(output_dir, str): + raise TypeError("'output_dir' must be a string or None") + + if macros is None: + macros = self.macros + elif isinstance(macros, list): + macros = macros + (self.macros or []) + else: + raise TypeError("'macros' (if supplied) must be a list of tuples") + + if include_dirs is None: + include_dirs = self.include_dirs + elif isinstance(include_dirs, (list, tuple)): + include_dirs = list(include_dirs) + (self.include_dirs or []) + else: + raise TypeError( + "'include_dirs' (if supplied) must be a list of strings") + + return output_dir, macros, include_dirs + + def _prep_compile(self, sources, output_dir, depends=None): + """Decide which souce files must be recompiled. + + Determine the list of object files corresponding to 'sources', + and figure out which ones really need to be recompiled. + Return a list of all object files and a dictionary telling + which source files can be skipped. + """ + # Get the list of expected output (object) files + objects = self.object_filenames(sources, output_dir=output_dir) + assert len(objects) == len(sources) + + # Return an empty dict for the "which source files can be skipped" + # return value to preserve API compatibility. + return objects, {} + + def _fix_object_args(self, objects, output_dir): + """Typecheck and fix up some arguments supplied to various methods. + Specifically: ensure that 'objects' is a list; if output_dir is + None, replace with self.output_dir. Return fixed versions of + 'objects' and 'output_dir'. + """ + if not isinstance(objects, (list, tuple)): + raise TypeError("'objects' must be a list or tuple of strings") + objects = list(objects) + + if output_dir is None: + output_dir = self.output_dir + elif not isinstance(output_dir, str): + raise TypeError("'output_dir' must be a string or None") + + return (objects, output_dir) + + def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs): + """Typecheck and fix up some of the arguments supplied to the + 'link_*' methods. Specifically: ensure that all arguments are + lists, and augment them with their permanent versions + (eg. 'self.libraries' augments 'libraries'). Return a tuple with + fixed versions of all arguments. + """ + if libraries is None: + libraries = self.libraries + elif isinstance(libraries, (list, tuple)): + libraries = list (libraries) + (self.libraries or []) + else: + raise TypeError( + "'libraries' (if supplied) must be a list of strings") + + if library_dirs is None: + library_dirs = self.library_dirs + elif isinstance(library_dirs, (list, tuple)): + library_dirs = list (library_dirs) + (self.library_dirs or []) + else: + raise TypeError( + "'library_dirs' (if supplied) must be a list of strings") + + if runtime_library_dirs is None: + runtime_library_dirs = self.runtime_library_dirs + elif isinstance(runtime_library_dirs, (list, tuple)): + runtime_library_dirs = (list(runtime_library_dirs) + + (self.runtime_library_dirs or [])) + else: + raise TypeError("'runtime_library_dirs' (if supplied) " + "must be a list of strings") + + return (libraries, library_dirs, runtime_library_dirs) + + def _need_link(self, objects, output_file): + """Return true if we need to relink the files listed in 'objects' + to recreate 'output_file'. + """ + if self.force: + return True + else: + if self.dry_run: + newer = newer_group (objects, output_file, missing='newer') + else: + newer = newer_group (objects, output_file) + return newer + + def detect_language(self, sources): + """Detect the language of a given file, or list of files. Uses + language_map, and language_order to do the job. + """ + if not isinstance(sources, list): + sources = [sources] + lang = None + index = len(self.language_order) + for source in sources: + base, ext = os.path.splitext(source) + extlang = self.language_map.get(ext) + try: + extindex = self.language_order.index(extlang) + if extindex < index: + lang = extlang + index = extindex + except ValueError: + pass + return lang + + + # -- Worker methods ------------------------------------------------ + # (must be implemented by subclasses) + + def preprocess(self, source, output_file=None, macros=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): + """Preprocess a single C/C++ source file, named in 'source'. + Output will be written to file named 'output_file', or stdout if + 'output_file' not supplied. 'macros' is a list of macro + definitions as for 'compile()', which will augment the macros set + with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a + list of directory names that will be added to the default list. + + Raises PreprocessError on failure. + """ + pass + + def compile(self, sources, output_dir=None, macros=None, + include_dirs=None, debug=0, extra_preargs=None, + extra_postargs=None, depends=None): + """Compile one or more source files. + + 'sources' must be a list of filenames, most likely C/C++ + files, but in reality anything that can be handled by a + particular compiler and compiler class (eg. MSVCCompiler can + handle resource files in 'sources'). Return a list of object + filenames, one per source filename in 'sources'. Depending on + the implementation, not all source files will necessarily be + compiled, but all corresponding object filenames will be + returned. + + If 'output_dir' is given, object files will be put under it, while + retaining their original path component. That is, "foo/bar.c" + normally compiles to "foo/bar.o" (for a Unix implementation); if + 'output_dir' is "build", then it would compile to + "build/foo/bar.o". + + 'macros', if given, must be a list of macro definitions. A macro + definition is either a (name, value) 2-tuple or a (name,) 1-tuple. + The former defines a macro; if the value is None, the macro is + defined without an explicit value. The 1-tuple case undefines a + macro. Later definitions/redefinitions/ undefinitions take + precedence. + + 'include_dirs', if given, must be a list of strings, the + directories to add to the default include file search path for this + compilation only. + + 'debug' is a boolean; if true, the compiler will be instructed to + output debug symbols in (or alongside) the object file(s). + + 'extra_preargs' and 'extra_postargs' are implementation- dependent. + On platforms that have the notion of a command-line (e.g. Unix, + DOS/Windows), they are most likely lists of strings: extra + command-line arguments to prepend/append to the compiler command + line. On other platforms, consult the implementation class + documentation. In any event, they are intended as an escape hatch + for those occasions when the abstract compiler framework doesn't + cut the mustard. + + 'depends', if given, is a list of filenames that all targets + depend on. If a source file is older than any file in + depends, then the source file will be recompiled. This + supports dependency tracking, but only at a coarse + granularity. + + Raises CompileError on failure. + """ + # A concrete compiler class can either override this method + # entirely or implement _compile(). + macros, objects, extra_postargs, pp_opts, build = \ + self._setup_compile(output_dir, macros, include_dirs, sources, + depends, extra_postargs) + cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) + + # Return *all* object filenames, not just the ones we just built. + return objects + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + """Compile 'src' to product 'obj'.""" + # A concrete compiler class that does not override compile() + # should implement _compile(). + pass + + def create_static_lib(self, objects, output_libname, output_dir=None, + debug=0, target_lang=None): + """Link a bunch of stuff together to create a static library file. + The "bunch of stuff" consists of the list of object files supplied + as 'objects', the extra object files supplied to + 'add_link_object()' and/or 'set_link_objects()', the libraries + supplied to 'add_library()' and/or 'set_libraries()', and the + libraries supplied as 'libraries' (if any). + + 'output_libname' should be a library name, not a filename; the + filename will be inferred from the library name. 'output_dir' is + the directory where the library file will be put. + + 'debug' is a boolean; if true, debugging information will be + included in the library (note that on most platforms, it is the + compile step where this matters: the 'debug' flag is included here + just for consistency). + + 'target_lang' is the target language for which the given objects + are being compiled. This allows specific linkage time treatment of + certain languages. + + Raises LibError on failure. + """ + pass + + + # values for target_desc parameter in link() + SHARED_OBJECT = "shared_object" + SHARED_LIBRARY = "shared_library" + EXECUTABLE = "executable" + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + """Link a bunch of stuff together to create an executable or + shared library file. + + The "bunch of stuff" consists of the list of object files supplied + as 'objects'. 'output_filename' should be a filename. If + 'output_dir' is supplied, 'output_filename' is relative to it + (i.e. 'output_filename' can provide directory components if + needed). + + 'libraries' is a list of libraries to link against. These are + library names, not filenames, since they're translated into + filenames in a platform-specific way (eg. "foo" becomes "libfoo.a" + on Unix and "foo.lib" on DOS/Windows). However, they can include a + directory component, which means the linker will look in that + specific directory rather than searching all the normal locations. + + 'library_dirs', if supplied, should be a list of directories to + search for libraries that were specified as bare library names + (ie. no directory component). These are on top of the system + default and those supplied to 'add_library_dir()' and/or + 'set_library_dirs()'. 'runtime_library_dirs' is a list of + directories that will be embedded into the shared library and used + to search for other shared libraries that *it* depends on at + run-time. (This may only be relevant on Unix.) + + 'export_symbols' is a list of symbols that the shared library will + export. (This appears to be relevant only on Windows.) + + 'debug' is as for 'compile()' and 'create_static_lib()', with the + slight distinction that it actually matters on most platforms (as + opposed to 'create_static_lib()', which includes a 'debug' flag + mostly for form's sake). + + 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except + of course that they supply command-line arguments for the + particular linker being used). + + 'target_lang' is the target language for which the given objects + are being compiled. This allows specific linkage time treatment of + certain languages. + + Raises LinkError on failure. + """ + raise NotImplementedError + + + # Old 'link_*()' methods, rewritten to use the new 'link()' method. + + def link_shared_lib(self, + objects, + output_libname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + self.link(CCompiler.SHARED_LIBRARY, objects, + self.library_filename(output_libname, lib_type='shared'), + output_dir, + libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, + extra_preargs, extra_postargs, build_temp, target_lang) + + + def link_shared_object(self, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + self.link(CCompiler.SHARED_OBJECT, objects, + output_filename, output_dir, + libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, + extra_preargs, extra_postargs, build_temp, target_lang) + + + def link_executable(self, + objects, + output_progname, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + target_lang=None): + self.link(CCompiler.EXECUTABLE, objects, + self.executable_filename(output_progname), output_dir, + libraries, library_dirs, runtime_library_dirs, None, + debug, extra_preargs, extra_postargs, None, target_lang) + + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function; there is + # no appropriate default implementation so subclasses should + # implement all of these. + + def library_dir_option(self, dir): + """Return the compiler option to add 'dir' to the list of + directories searched for libraries. + """ + raise NotImplementedError + + def runtime_library_dir_option(self, dir): + """Return the compiler option to add 'dir' to the list of + directories searched for runtime libraries. + """ + raise NotImplementedError + + def library_option(self, lib): + """Return the compiler option to add 'lib' to the list of libraries + linked into the shared library or executable. + """ + raise NotImplementedError + + def has_function(self, funcname, includes=None, include_dirs=None, + libraries=None, library_dirs=None): + """Return a boolean indicating whether funcname is supported on + the current platform. The optional arguments can be used to + augment the compilation environment. + """ + # this can't be included at module scope because it tries to + # import math which might not be available at that point - maybe + # the necessary logic should just be inlined? + import tempfile + if includes is None: + includes = [] + if include_dirs is None: + include_dirs = [] + if libraries is None: + libraries = [] + if library_dirs is None: + library_dirs = [] + fd, fname = tempfile.mkstemp(".c", funcname, text=True) + f = os.fdopen(fd, "w") + try: + for incl in includes: + f.write("""#include "%s"\n""" % incl) + f.write("""\ +int main (int argc, char **argv) { + %s(); + return 0; +} +""" % funcname) + finally: + f.close() + try: + objects = self.compile([fname], include_dirs=include_dirs) + except CompileError: + return False + + try: + self.link_executable(objects, "a.out", + libraries=libraries, + library_dirs=library_dirs) + except (LinkError, TypeError): + return False + return True + + def find_library_file (self, dirs, lib, debug=0): + """Search the specified list of directories for a static or shared + library file 'lib' and return the full path to that file. If + 'debug' true, look for a debugging version (if that makes sense on + the current platform). Return None if 'lib' wasn't found in any of + the specified directories. + """ + raise NotImplementedError + + # -- Filename generation methods ----------------------------------- + + # The default implementation of the filename generating methods are + # prejudiced towards the Unix/DOS/Windows view of the world: + # * object files are named by replacing the source file extension + # (eg. .c/.cpp -> .o/.obj) + # * library files (shared or static) are named by plugging the + # library name and extension into a format string, eg. + # "lib%s.%s" % (lib_name, ".a") for Unix static libraries + # * executables are named by appending an extension (possibly + # empty) to the program name: eg. progname + ".exe" for + # Windows + # + # To reduce redundant code, these methods expect to find + # several attributes in the current object (presumably defined + # as class attributes): + # * src_extensions - + # list of C/C++ source file extensions, eg. ['.c', '.cpp'] + # * obj_extension - + # object file extension, eg. '.o' or '.obj' + # * static_lib_extension - + # extension for static library files, eg. '.a' or '.lib' + # * shared_lib_extension - + # extension for shared library/object files, eg. '.so', '.dll' + # * static_lib_format - + # format string for generating static library filenames, + # eg. 'lib%s.%s' or '%s.%s' + # * shared_lib_format + # format string for generating shared library filenames + # (probably same as static_lib_format, since the extension + # is one of the intended parameters to the format string) + # * exe_extension - + # extension for executable files, eg. '' or '.exe' + + def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + if output_dir is None: + output_dir = '' + obj_names = [] + for src_name in source_filenames: + base, ext = os.path.splitext(src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / + if ext not in self.src_extensions: + raise UnknownFileError( + "unknown file type '%s' (from '%s')" % (ext, src_name)) + if strip_dir: + base = os.path.basename(base) + obj_names.append(os.path.join(output_dir, + base + self.obj_extension)) + return obj_names + + def shared_object_filename(self, basename, strip_dir=0, output_dir=''): + assert output_dir is not None + if strip_dir: + basename = os.path.basename(basename) + return os.path.join(output_dir, basename + self.shared_lib_extension) + + def executable_filename(self, basename, strip_dir=0, output_dir=''): + assert output_dir is not None + if strip_dir: + basename = os.path.basename(basename) + return os.path.join(output_dir, basename + (self.exe_extension or '')) + + def library_filename(self, libname, lib_type='static', # or 'shared' + strip_dir=0, output_dir=''): + assert output_dir is not None + if lib_type not in ("static", "shared", "dylib", "xcode_stub"): + raise ValueError( + "'lib_type' must be \"static\", \"shared\", \"dylib\", or \"xcode_stub\"") + fmt = getattr(self, lib_type + "_lib_format") + ext = getattr(self, lib_type + "_lib_extension") + + dir, base = os.path.split(libname) + filename = fmt % (base, ext) + if strip_dir: + dir = '' + + return os.path.join(output_dir, dir, filename) + + + # -- Utility methods ----------------------------------------------- + + def announce(self, msg, level=1): + log.debug(msg) + + def debug_print(self, msg): + from distutils.debug import DEBUG + if DEBUG: + print(msg) + + def warn(self, msg): + sys.stderr.write("warning: %s\n" % msg) + + def execute(self, func, args, msg=None, level=1): + execute(func, args, msg, self.dry_run) + + def spawn(self, cmd): + spawn(cmd, dry_run=self.dry_run) + + def move_file(self, src, dst): + return move_file(src, dst, dry_run=self.dry_run) + + def mkpath (self, name, mode=0o777): + mkpath(name, mode, dry_run=self.dry_run) + + +# Map a sys.platform/os.name ('posix', 'nt') to the default compiler +# type for that platform. Keys are interpreted as re match +# patterns. Order is important; platform mappings are preferred over +# OS names. +_default_compilers = ( + + # Platform string mappings + + # on a cygwin built python we can use gcc like an ordinary UNIXish + # compiler + ('cygwin.*', 'unix'), + + # OS name mappings + ('posix', 'unix'), + ('nt', 'msvc'), + + ) + +def get_default_compiler(osname=None, platform=None): + """Determine the default compiler to use for the given platform. + + osname should be one of the standard Python OS names (i.e. the + ones returned by os.name) and platform the common value + returned by sys.platform for the platform in question. + + The default values are os.name and sys.platform in case the + parameters are not given. + """ + if osname is None: + osname = os.name + if platform is None: + platform = sys.platform + for pattern, compiler in _default_compilers: + if re.match(pattern, platform) is not None or \ + re.match(pattern, osname) is not None: + return compiler + # Default to Unix compiler + return 'unix' + +# Map compiler types to (module_name, class_name) pairs -- ie. where to +# find the code that implements an interface to this compiler. (The module +# is assumed to be in the 'distutils' package.) +compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', + "standard UNIX-style compiler"), + 'msvc': ('_msvccompiler', 'MSVCCompiler', + "Microsoft Visual C++"), + 'cygwin': ('cygwinccompiler', 'CygwinCCompiler', + "Cygwin port of GNU C Compiler for Win32"), + 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler', + "Mingw32 port of GNU C Compiler for Win32"), + 'bcpp': ('bcppcompiler', 'BCPPCompiler', + "Borland C++ Compiler"), + } + +def show_compilers(): + """Print list of available compilers (used by the "--help-compiler" + options to "build", "build_ext", "build_clib"). + """ + # XXX this "knows" that the compiler option it's describing is + # "--compiler", which just happens to be the case for the three + # commands that use it. + from distutils.fancy_getopt import FancyGetopt + compilers = [] + for compiler in compiler_class.keys(): + compilers.append(("compiler="+compiler, None, + compiler_class[compiler][2])) + compilers.sort() + pretty_printer = FancyGetopt(compilers) + pretty_printer.print_help("List of available compilers:") + + +def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0): + """Generate an instance of some CCompiler subclass for the supplied + platform/compiler combination. 'plat' defaults to 'os.name' + (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler + for that platform. Currently only 'posix' and 'nt' are supported, and + the default compilers are "traditional Unix interface" (UnixCCompiler + class) and Visual C++ (MSVCCompiler class). Note that it's perfectly + possible to ask for a Unix compiler object under Windows, and a + Microsoft compiler object under Unix -- if you supply a value for + 'compiler', 'plat' is ignored. + """ + if plat is None: + plat = os.name + + try: + if compiler is None: + compiler = get_default_compiler(plat) + + (module_name, class_name, long_description) = compiler_class[compiler] + except KeyError: + msg = "don't know how to compile C/C++ code on platform '%s'" % plat + if compiler is not None: + msg = msg + " with '%s' compiler" % compiler + raise DistutilsPlatformError(msg) + + try: + module_name = "distutils." + module_name + __import__ (module_name) + module = sys.modules[module_name] + klass = vars(module)[class_name] + except ImportError: + raise DistutilsModuleError( + "can't compile C/C++ code: unable to load module '%s'" % \ + module_name) + except KeyError: + raise DistutilsModuleError( + "can't compile C/C++ code: unable to find class '%s' " + "in module '%s'" % (class_name, module_name)) + + # XXX The None is necessary to preserve backwards compatibility + # with classes that expect verbose to be the first positional + # argument. + return klass(None, dry_run, force) + + +def gen_preprocess_options(macros, include_dirs): + """Generate C pre-processor options (-D, -U, -I) as used by at least + two types of compilers: the typical Unix compiler and Visual C++. + 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,) + means undefine (-U) macro 'name', and (name,value) means define (-D) + macro 'name' to 'value'. 'include_dirs' is just a list of directory + names to be added to the header file search path (-I). Returns a list + of command-line options suitable for either Unix compilers or Visual + C++. + """ + # XXX it would be nice (mainly aesthetic, and so we don't generate + # stupid-looking command lines) to go over 'macros' and eliminate + # redundant definitions/undefinitions (ie. ensure that only the + # latest mention of a particular macro winds up on the command + # line). I don't think it's essential, though, since most (all?) + # Unix C compilers only pay attention to the latest -D or -U + # mention of a macro on their command line. Similar situation for + # 'include_dirs'. I'm punting on both for now. Anyways, weeding out + # redundancies like this should probably be the province of + # CCompiler, since the data structures used are inherited from it + # and therefore common to all CCompiler classes. + pp_opts = [] + for macro in macros: + if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2): + raise TypeError( + "bad macro definition '%s': " + "each element of 'macros' list must be a 1- or 2-tuple" + % macro) + + if len(macro) == 1: # undefine this macro + pp_opts.append("-U%s" % macro[0]) + elif len(macro) == 2: + if macro[1] is None: # define with no explicit value + pp_opts.append("-D%s" % macro[0]) + else: + # XXX *don't* need to be clever about quoting the + # macro value here, because we're going to avoid the + # shell at all costs when we spawn the command! + pp_opts.append("-D%s=%s" % macro) + + for dir in include_dirs: + pp_opts.append("-I%s" % dir) + return pp_opts + + +def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries): + """Generate linker options for searching library directories and + linking with specific libraries. 'libraries' and 'library_dirs' are, + respectively, lists of library names (not filenames!) and search + directories. Returns a list of command-line options suitable for use + with some compiler (depending on the two format strings passed in). + """ + lib_opts = [] + + for dir in library_dirs: + lib_opts.append(compiler.library_dir_option(dir)) + + for dir in runtime_library_dirs: + opt = compiler.runtime_library_dir_option(dir) + if isinstance(opt, list): + lib_opts = lib_opts + opt + else: + lib_opts.append(opt) + + # XXX it's important that we *not* remove redundant library mentions! + # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to + # resolve all symbols. I just hope we never have to say "-lfoo obj.o + # -lbar" to get things to work -- that's certainly a possibility, but a + # pretty nasty way to arrange your C code. + + for lib in libraries: + (lib_dir, lib_name) = os.path.split(lib) + if lib_dir: + lib_file = compiler.find_library_file([lib_dir], lib_name) + if lib_file: + lib_opts.append(lib_file) + else: + compiler.warn("no library file corresponding to " + "'%s' found (skipping)" % lib) + else: + lib_opts.append(compiler.library_option (lib)) + return lib_opts diff --git a/setuptools/_distutils/cmd.py b/setuptools/_distutils/cmd.py new file mode 100644 index 00000000..dba3191e --- /dev/null +++ b/setuptools/_distutils/cmd.py @@ -0,0 +1,403 @@ +"""distutils.cmd + +Provides the Command class, the base class for the command classes +in the distutils.command package. +""" + +import sys, os, re +from distutils.errors import DistutilsOptionError +from distutils import util, dir_util, file_util, archive_util, dep_util +from distutils import log + +class Command: + """Abstract base class for defining command classes, the "worker bees" + of the Distutils. A useful analogy for command classes is to think of + them as subroutines with local variables called "options". The options + are "declared" in 'initialize_options()' and "defined" (given their + final values, aka "finalized") in 'finalize_options()', both of which + must be defined by every command class. The distinction between the + two is necessary because option values might come from the outside + world (command line, config file, ...), and any options dependent on + other options must be computed *after* these outside influences have + been processed -- hence 'finalize_options()'. The "body" of the + subroutine, where it does all its work based on the values of its + options, is the 'run()' method, which must also be implemented by every + command class. + """ + + # 'sub_commands' formalizes the notion of a "family" of commands, + # eg. "install" as the parent with sub-commands "install_lib", + # "install_headers", etc. The parent of a family of commands + # defines 'sub_commands' as a class attribute; it's a list of + # (command_name : string, predicate : unbound_method | string | None) + # tuples, where 'predicate' is a method of the parent command that + # determines whether the corresponding command is applicable in the + # current situation. (Eg. we "install_headers" is only applicable if + # we have any C header files to install.) If 'predicate' is None, + # that command is always applicable. + # + # 'sub_commands' is usually defined at the *end* of a class, because + # predicates can be unbound methods, so they must already have been + # defined. The canonical example is the "install" command. + sub_commands = [] + + + # -- Creation/initialization methods ------------------------------- + + def __init__(self, dist): + """Create and initialize a new Command object. Most importantly, + invokes the 'initialize_options()' method, which is the real + initializer and depends on the actual command being + instantiated. + """ + # late import because of mutual dependence between these classes + from distutils.dist import Distribution + + if not isinstance(dist, Distribution): + raise TypeError("dist must be a Distribution instance") + if self.__class__ is Command: + raise RuntimeError("Command is an abstract class") + + self.distribution = dist + self.initialize_options() + + # Per-command versions of the global flags, so that the user can + # customize Distutils' behaviour command-by-command and let some + # commands fall back on the Distribution's behaviour. None means + # "not defined, check self.distribution's copy", while 0 or 1 mean + # false and true (duh). Note that this means figuring out the real + # value of each flag is a touch complicated -- hence "self._dry_run" + # will be handled by __getattr__, below. + # XXX This needs to be fixed. + self._dry_run = None + + # verbose is largely ignored, but needs to be set for + # backwards compatibility (I think)? + self.verbose = dist.verbose + + # Some commands define a 'self.force' option to ignore file + # timestamps, but methods defined *here* assume that + # 'self.force' exists for all commands. So define it here + # just to be safe. + self.force = None + + # The 'help' flag is just used for command-line parsing, so + # none of that complicated bureaucracy is needed. + self.help = 0 + + # 'finalized' records whether or not 'finalize_options()' has been + # called. 'finalize_options()' itself should not pay attention to + # this flag: it is the business of 'ensure_finalized()', which + # always calls 'finalize_options()', to respect/update it. + self.finalized = 0 + + # XXX A more explicit way to customize dry_run would be better. + def __getattr__(self, attr): + if attr == 'dry_run': + myval = getattr(self, "_" + attr) + if myval is None: + return getattr(self.distribution, attr) + else: + return myval + else: + raise AttributeError(attr) + + def ensure_finalized(self): + if not self.finalized: + self.finalize_options() + self.finalized = 1 + + # Subclasses must define: + # initialize_options() + # provide default values for all options; may be customized by + # setup script, by options from config file(s), or by command-line + # options + # finalize_options() + # decide on the final values for all options; this is called + # after all possible intervention from the outside world + # (command-line, option file, etc.) has been processed + # run() + # run the command: do whatever it is we're here to do, + # controlled by the command's various option values + + def initialize_options(self): + """Set default values for all the options that this command + supports. Note that these defaults may be overridden by other + commands, by the setup script, by config files, or by the + command-line. Thus, this is not the place to code dependencies + between options; generally, 'initialize_options()' implementations + are just a bunch of "self.foo = None" assignments. + + This method must be implemented by all command classes. + """ + raise RuntimeError("abstract method -- subclass %s must override" + % self.__class__) + + def finalize_options(self): + """Set final values for all the options that this command supports. + This is always called as late as possible, ie. after any option + assignments from the command-line or from other commands have been + done. Thus, this is the place to code option dependencies: if + 'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as + long as 'foo' still has the same value it was assigned in + 'initialize_options()'. + + This method must be implemented by all command classes. + """ + raise RuntimeError("abstract method -- subclass %s must override" + % self.__class__) + + + def dump_options(self, header=None, indent=""): + from distutils.fancy_getopt import longopt_xlate + if header is None: + header = "command options for '%s':" % self.get_command_name() + self.announce(indent + header, level=log.INFO) + indent = indent + " " + for (option, _, _) in self.user_options: + option = option.translate(longopt_xlate) + if option[-1] == "=": + option = option[:-1] + value = getattr(self, option) + self.announce(indent + "%s = %s" % (option, value), + level=log.INFO) + + def run(self): + """A command's raison d'etre: carry out the action it exists to + perform, controlled by the options initialized in + 'initialize_options()', customized by other commands, the setup + script, the command-line, and config files, and finalized in + 'finalize_options()'. All terminal output and filesystem + interaction should be done by 'run()'. + + This method must be implemented by all command classes. + """ + raise RuntimeError("abstract method -- subclass %s must override" + % self.__class__) + + def announce(self, msg, level=1): + """If the current verbosity level is of greater than or equal to + 'level' print 'msg' to stdout. + """ + log.log(level, msg) + + def debug_print(self, msg): + """Print 'msg' to stdout if the global DEBUG (taken from the + DISTUTILS_DEBUG environment variable) flag is true. + """ + from distutils.debug import DEBUG + if DEBUG: + print(msg) + sys.stdout.flush() + + + # -- Option validation methods ------------------------------------- + # (these are very handy in writing the 'finalize_options()' method) + # + # NB. the general philosophy here is to ensure that a particular option + # value meets certain type and value constraints. If not, we try to + # force it into conformance (eg. if we expect a list but have a string, + # split the string on comma and/or whitespace). If we can't force the + # option into conformance, raise DistutilsOptionError. Thus, command + # classes need do nothing more than (eg.) + # self.ensure_string_list('foo') + # and they can be guaranteed that thereafter, self.foo will be + # a list of strings. + + def _ensure_stringlike(self, option, what, default=None): + val = getattr(self, option) + if val is None: + setattr(self, option, default) + return default + elif not isinstance(val, str): + raise DistutilsOptionError("'%s' must be a %s (got `%s`)" + % (option, what, val)) + return val + + def ensure_string(self, option, default=None): + """Ensure that 'option' is a string; if not defined, set it to + 'default'. + """ + self._ensure_stringlike(option, "string", default) + + def ensure_string_list(self, option): + r"""Ensure that 'option' is a list of strings. If 'option' is + currently a string, we split it either on /,\s*/ or /\s+/, so + "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become + ["foo", "bar", "baz"]. + """ + val = getattr(self, option) + if val is None: + return + elif isinstance(val, str): + setattr(self, option, re.split(r',\s*|\s+', val)) + else: + if isinstance(val, list): + ok = all(isinstance(v, str) for v in val) + else: + ok = False + if not ok: + raise DistutilsOptionError( + "'%s' must be a list of strings (got %r)" + % (option, val)) + + def _ensure_tested_string(self, option, tester, what, error_fmt, + default=None): + val = self._ensure_stringlike(option, what, default) + if val is not None and not tester(val): + raise DistutilsOptionError(("error in '%s' option: " + error_fmt) + % (option, val)) + + def ensure_filename(self, option): + """Ensure that 'option' is the name of an existing file.""" + self._ensure_tested_string(option, os.path.isfile, + "filename", + "'%s' does not exist or is not a file") + + def ensure_dirname(self, option): + self._ensure_tested_string(option, os.path.isdir, + "directory name", + "'%s' does not exist or is not a directory") + + + # -- Convenience methods for commands ------------------------------ + + def get_command_name(self): + if hasattr(self, 'command_name'): + return self.command_name + else: + return self.__class__.__name__ + + def set_undefined_options(self, src_cmd, *option_pairs): + """Set the values of any "undefined" options from corresponding + option values in some other command object. "Undefined" here means + "is None", which is the convention used to indicate that an option + has not been changed between 'initialize_options()' and + 'finalize_options()'. Usually called from 'finalize_options()' for + options that depend on some other command rather than another + option of the same command. 'src_cmd' is the other command from + which option values will be taken (a command object will be created + for it if necessary); the remaining arguments are + '(src_option,dst_option)' tuples which mean "take the value of + 'src_option' in the 'src_cmd' command object, and copy it to + 'dst_option' in the current command object". + """ + # Option_pairs: list of (src_option, dst_option) tuples + src_cmd_obj = self.distribution.get_command_obj(src_cmd) + src_cmd_obj.ensure_finalized() + for (src_option, dst_option) in option_pairs: + if getattr(self, dst_option) is None: + setattr(self, dst_option, getattr(src_cmd_obj, src_option)) + + def get_finalized_command(self, command, create=1): + """Wrapper around Distribution's 'get_command_obj()' method: find + (create if necessary and 'create' is true) the command object for + 'command', call its 'ensure_finalized()' method, and return the + finalized command object. + """ + cmd_obj = self.distribution.get_command_obj(command, create) + cmd_obj.ensure_finalized() + return cmd_obj + + # XXX rename to 'get_reinitialized_command()'? (should do the + # same in dist.py, if so) + def reinitialize_command(self, command, reinit_subcommands=0): + return self.distribution.reinitialize_command(command, + reinit_subcommands) + + def run_command(self, command): + """Run some other command: uses the 'run_command()' method of + Distribution, which creates and finalizes the command object if + necessary and then invokes its 'run()' method. + """ + self.distribution.run_command(command) + + def get_sub_commands(self): + """Determine the sub-commands that are relevant in the current + distribution (ie., that need to be run). This is based on the + 'sub_commands' class attribute: each tuple in that list may include + a method that we call to determine if the subcommand needs to be + run for the current distribution. Return a list of command names. + """ + commands = [] + for (cmd_name, method) in self.sub_commands: + if method is None or method(self): + commands.append(cmd_name) + return commands + + + # -- External world manipulation ----------------------------------- + + def warn(self, msg): + log.warn("warning: %s: %s\n", self.get_command_name(), msg) + + def execute(self, func, args, msg=None, level=1): + util.execute(func, args, msg, dry_run=self.dry_run) + + def mkpath(self, name, mode=0o777): + dir_util.mkpath(name, mode, dry_run=self.dry_run) + + def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1, + link=None, level=1): + """Copy a file respecting verbose, dry-run and force flags. (The + former two default to whatever is in the Distribution object, and + the latter defaults to false for commands that don't define it.)""" + return file_util.copy_file(infile, outfile, preserve_mode, + preserve_times, not self.force, link, + dry_run=self.dry_run) + + def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1, + preserve_symlinks=0, level=1): + """Copy an entire directory tree respecting verbose, dry-run, + and force flags. + """ + return dir_util.copy_tree(infile, outfile, preserve_mode, + preserve_times, preserve_symlinks, + not self.force, dry_run=self.dry_run) + + def move_file (self, src, dst, level=1): + """Move a file respecting dry-run flag.""" + return file_util.move_file(src, dst, dry_run=self.dry_run) + + def spawn(self, cmd, search_path=1, level=1): + """Spawn an external command respecting dry-run flag.""" + from distutils.spawn import spawn + spawn(cmd, search_path, dry_run=self.dry_run) + + def make_archive(self, base_name, format, root_dir=None, base_dir=None, + owner=None, group=None): + return archive_util.make_archive(base_name, format, root_dir, base_dir, + dry_run=self.dry_run, + owner=owner, group=group) + + def make_file(self, infiles, outfile, func, args, + exec_msg=None, skip_msg=None, level=1): + """Special case of 'execute()' for operations that process one or + more input files and generate one output file. Works just like + 'execute()', except the operation is skipped and a different + message printed if 'outfile' already exists and is newer than all + files listed in 'infiles'. If the command defined 'self.force', + and it is true, then the command is unconditionally run -- does no + timestamp checks. + """ + if skip_msg is None: + skip_msg = "skipping %s (inputs unchanged)" % outfile + + # Allow 'infiles' to be a single string + if isinstance(infiles, str): + infiles = (infiles,) + elif not isinstance(infiles, (list, tuple)): + raise TypeError( + "'infiles' must be a string, or a list or tuple of strings") + + if exec_msg is None: + exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles)) + + # If 'outfile' must be regenerated (either because it doesn't + # exist, is out-of-date, or the 'force' flag is true) then + # perform the action that presumably regenerates it + if self.force or dep_util.newer_group(infiles, outfile): + self.execute(func, args, exec_msg, level) + # Otherwise, print the "skip" message + else: + log.debug(skip_msg) diff --git a/setuptools/_distutils/command/__init__.py b/setuptools/_distutils/command/__init__.py new file mode 100644 index 00000000..481eea9f --- /dev/null +++ b/setuptools/_distutils/command/__init__.py @@ -0,0 +1,31 @@ +"""distutils.command + +Package containing implementation of all the standard Distutils +commands.""" + +__all__ = ['build', + 'build_py', + 'build_ext', + 'build_clib', + 'build_scripts', + 'clean', + 'install', + 'install_lib', + 'install_headers', + 'install_scripts', + 'install_data', + 'sdist', + 'register', + 'bdist', + 'bdist_dumb', + 'bdist_rpm', + 'bdist_wininst', + 'check', + 'upload', + # These two are reserved for future use: + #'bdist_sdux', + #'bdist_pkgtool', + # Note: + # bdist_packager is not included because it only provides + # an abstract base class + ] diff --git a/setuptools/_distutils/command/bdist.py b/setuptools/_distutils/command/bdist.py new file mode 100644 index 00000000..014871d2 --- /dev/null +++ b/setuptools/_distutils/command/bdist.py @@ -0,0 +1,143 @@ +"""distutils.command.bdist + +Implements the Distutils 'bdist' command (create a built [binary] +distribution).""" + +import os +from distutils.core import Command +from distutils.errors import * +from distutils.util import get_platform + + +def show_formats(): + """Print list of available formats (arguments to "--format" option). + """ + from distutils.fancy_getopt import FancyGetopt + formats = [] + for format in bdist.format_commands: + formats.append(("formats=" + format, None, + bdist.format_command[format][1])) + pretty_printer = FancyGetopt(formats) + pretty_printer.print_help("List of available distribution formats:") + + +class bdist(Command): + + description = "create a built (binary) distribution" + + user_options = [('bdist-base=', 'b', + "temporary directory for creating built distributions"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('formats=', None, + "formats for distribution (comma-separated list)"), + ('dist-dir=', 'd', + "directory to put final built distributions in " + "[default: dist]"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), + ] + + boolean_options = ['skip-build'] + + help_options = [ + ('help-formats', None, + "lists available distribution formats", show_formats), + ] + + # The following commands do not take a format option from bdist + no_format_option = ('bdist_rpm',) + + # This won't do in reality: will need to distinguish RPM-ish Linux, + # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. + default_format = {'posix': 'gztar', + 'nt': 'zip'} + + # Establish the preferred order (for the --help-formats option). + format_commands = ['rpm', 'gztar', 'bztar', 'xztar', 'ztar', 'tar', + 'wininst', 'zip', 'msi'] + + # And the real information. + format_command = {'rpm': ('bdist_rpm', "RPM distribution"), + 'gztar': ('bdist_dumb', "gzip'ed tar file"), + 'bztar': ('bdist_dumb', "bzip2'ed tar file"), + 'xztar': ('bdist_dumb', "xz'ed tar file"), + 'ztar': ('bdist_dumb', "compressed tar file"), + 'tar': ('bdist_dumb', "tar file"), + 'wininst': ('bdist_wininst', + "Windows executable installer"), + 'zip': ('bdist_dumb', "ZIP file"), + 'msi': ('bdist_msi', "Microsoft Installer") + } + + + def initialize_options(self): + self.bdist_base = None + self.plat_name = None + self.formats = None + self.dist_dir = None + self.skip_build = 0 + self.group = None + self.owner = None + + def finalize_options(self): + # have to finalize 'plat_name' before 'bdist_base' + if self.plat_name is None: + if self.skip_build: + self.plat_name = get_platform() + else: + self.plat_name = self.get_finalized_command('build').plat_name + + # 'bdist_base' -- parent of per-built-distribution-format + # temporary directories (eg. we'll probably have + # "build/bdist./dumb", "build/bdist./rpm", etc.) + if self.bdist_base is None: + build_base = self.get_finalized_command('build').build_base + self.bdist_base = os.path.join(build_base, + 'bdist.' + self.plat_name) + + self.ensure_string_list('formats') + if self.formats is None: + try: + self.formats = [self.default_format[os.name]] + except KeyError: + raise DistutilsPlatformError( + "don't know how to create built distributions " + "on platform %s" % os.name) + + if self.dist_dir is None: + self.dist_dir = "dist" + + def run(self): + # Figure out which sub-commands we need to run. + commands = [] + for format in self.formats: + try: + commands.append(self.format_command[format][0]) + except KeyError: + raise DistutilsOptionError("invalid format '%s'" % format) + + # Reinitialize and run each command. + for i in range(len(self.formats)): + cmd_name = commands[i] + sub_cmd = self.reinitialize_command(cmd_name) + if cmd_name not in self.no_format_option: + sub_cmd.format = self.formats[i] + + # passing the owner and group names for tar archiving + if cmd_name == 'bdist_dumb': + sub_cmd.owner = self.owner + sub_cmd.group = self.group + + # If we're going to need to run this command again, tell it to + # keep its temporary files around so subsequent runs go faster. + if cmd_name in commands[i+1:]: + sub_cmd.keep_temp = 1 + self.run_command(cmd_name) diff --git a/setuptools/_distutils/command/bdist_dumb.py b/setuptools/_distutils/command/bdist_dumb.py new file mode 100644 index 00000000..f0d6b5b8 --- /dev/null +++ b/setuptools/_distutils/command/bdist_dumb.py @@ -0,0 +1,123 @@ +"""distutils.command.bdist_dumb + +Implements the Distutils 'bdist_dumb' command (create a "dumb" built +distribution -- i.e., just an archive to be unpacked under $prefix or +$exec_prefix).""" + +import os +from distutils.core import Command +from distutils.util import get_platform +from distutils.dir_util import remove_tree, ensure_relative +from distutils.errors import * +from distutils.sysconfig import get_python_version +from distutils import log + +class bdist_dumb(Command): + + description = "create a \"dumb\" built distribution" + + user_options = [('bdist-dir=', 'd', + "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('format=', 'f', + "archive format to create (tar, gztar, bztar, xztar, " + "ztar, zip)"), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('relative', None, + "build the archive using relative paths " + "(default: false)"), + ('owner=', 'u', + "Owner name used when creating a tar file" + " [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file" + " [default: current group]"), + ] + + boolean_options = ['keep-temp', 'skip-build', 'relative'] + + default_format = { 'posix': 'gztar', + 'nt': 'zip' } + + def initialize_options(self): + self.bdist_dir = None + self.plat_name = None + self.format = None + self.keep_temp = 0 + self.dist_dir = None + self.skip_build = None + self.relative = 0 + self.owner = None + self.group = None + + def finalize_options(self): + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'dumb') + + if self.format is None: + try: + self.format = self.default_format[os.name] + except KeyError: + raise DistutilsPlatformError( + "don't know how to create dumb built distributions " + "on platform %s" % os.name) + + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ('skip_build', 'skip_build')) + + def run(self): + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.root = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + + log.info("installing to %s", self.bdist_dir) + self.run_command('install') + + # And make an archive relative to the root of the + # pseudo-installation tree. + archive_basename = "%s.%s" % (self.distribution.get_fullname(), + self.plat_name) + + pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) + if not self.relative: + archive_root = self.bdist_dir + else: + if (self.distribution.has_ext_modules() and + (install.install_base != install.install_platbase)): + raise DistutilsPlatformError( + "can't make a dumb built distribution where " + "base and platbase are different (%s, %s)" + % (repr(install.install_base), + repr(install.install_platbase))) + else: + archive_root = os.path.join(self.bdist_dir, + ensure_relative(install.install_base)) + + # Make the archive + filename = self.make_archive(pseudoinstall_root, + self.format, root_dir=archive_root, + owner=self.owner, group=self.group) + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + self.distribution.dist_files.append(('bdist_dumb', pyversion, + filename)) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) diff --git a/setuptools/_distutils/command/bdist_msi.py b/setuptools/_distutils/command/bdist_msi.py new file mode 100644 index 00000000..0863a188 --- /dev/null +++ b/setuptools/_distutils/command/bdist_msi.py @@ -0,0 +1,749 @@ +# Copyright (C) 2005, 2006 Martin von Löwis +# Licensed to PSF under a Contributor Agreement. +# The bdist_wininst command proper +# based on bdist_wininst +""" +Implements the bdist_msi command. +""" + +import os +import sys +import warnings +from distutils.core import Command +from distutils.dir_util import remove_tree +from distutils.sysconfig import get_python_version +from distutils.version import StrictVersion +from distutils.errors import DistutilsOptionError +from distutils.util import get_platform +from distutils import log +import msilib +from msilib import schema, sequence, text +from msilib import Directory, Feature, Dialog, add_data + +class PyDialog(Dialog): + """Dialog class with a fixed layout: controls at the top, then a ruler, + then a list of buttons: back, next, cancel. Optionally a bitmap at the + left.""" + def __init__(self, *args, **kw): + """Dialog(database, name, x, y, w, h, attributes, title, first, + default, cancel, bitmap=true)""" + Dialog.__init__(self, *args) + ruler = self.h - 36 + bmwidth = 152*ruler/328 + #if kw.get("bitmap", True): + # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin") + self.line("BottomLine", 0, ruler, self.w, 0) + + def title(self, title): + "Set the title text of the dialog at the top." + # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix, + # text, in VerdanaBold10 + self.text("Title", 15, 10, 320, 60, 0x30003, + r"{\VerdanaBold10}%s" % title) + + def back(self, title, next, name = "Back", active = 1): + """Add a back button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next) + + def cancel(self, title, next, name = "Cancel", active = 1): + """Add a cancel button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next) + + def next(self, title, next, name = "Next", active = 1): + """Add a Next button with a given title, the tab-next button, + its name in the Control table, possibly initially disabled. + + Return the button, so that events can be associated""" + if active: + flags = 3 # Visible|Enabled + else: + flags = 1 # Visible + return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next) + + def xbutton(self, name, title, next, xpos): + """Add a button with a given title, the tab-next button, + its name in the Control table, giving its x position; the + y-position is aligned with the other buttons. + + Return the button, so that events can be associated""" + return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next) + +class bdist_msi(Command): + + description = "create a Microsoft Installer (.msi) binary distribution" + + user_options = [('bdist-dir=', None, + "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('target-version=', None, + "require a specific python version" + + " on the target system"), + ('no-target-compile', 'c', + "do not compile .py to .pyc on the target system"), + ('no-target-optimize', 'o', + "do not compile .py to .pyo (optimized) " + "on the target system"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('install-script=', None, + "basename of installation script to be run after " + "installation or before deinstallation"), + ('pre-install-script=', None, + "Fully qualified filename of a script to be run before " + "any files are installed. This script need not be in the " + "distribution"), + ] + + boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', + 'skip-build'] + + all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4', + '2.5', '2.6', '2.7', '2.8', '2.9', + '3.0', '3.1', '3.2', '3.3', '3.4', + '3.5', '3.6', '3.7', '3.8', '3.9'] + other_version = 'X' + + def __init__(self, *args, **kw): + super().__init__(*args, **kw) + warnings.warn("bdist_msi command is deprecated since Python 3.9, " + "use bdist_wheel (wheel packages) instead", + DeprecationWarning, 2) + + def initialize_options(self): + self.bdist_dir = None + self.plat_name = None + self.keep_temp = 0 + self.no_target_compile = 0 + self.no_target_optimize = 0 + self.target_version = None + self.dist_dir = None + self.skip_build = None + self.install_script = None + self.pre_install_script = None + self.versions = None + + def finalize_options(self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'msi') + + short_version = get_python_version() + if (not self.target_version) and self.distribution.has_ext_modules(): + self.target_version = short_version + + if self.target_version: + self.versions = [self.target_version] + if not self.skip_build and self.distribution.has_ext_modules()\ + and self.target_version != short_version: + raise DistutilsOptionError( + "target version can only be %s, or the '--skip-build'" + " option must be specified" % (short_version,)) + else: + self.versions = list(self.all_versions) + + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ) + + if self.pre_install_script: + raise DistutilsOptionError( + "the pre-install-script feature is not yet implemented") + + if self.install_script: + for script in self.distribution.scripts: + if self.install_script == os.path.basename(script): + break + else: + raise DistutilsOptionError( + "install_script '%s' not found in scripts" + % self.install_script) + self.install_script_key = None + + def run(self): + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.prefix = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + + install_lib = self.reinitialize_command('install_lib') + # we do not want to include pyc or pyo files + install_lib.compile = 0 + install_lib.optimize = 0 + + if self.distribution.has_ext_modules(): + # If we are building an installer for a Python version other + # than the one we are currently running, then we need to ensure + # our build_lib reflects the other Python version rather than ours. + # Note that for target_version!=sys.version, we must have skipped the + # build step, so there is no issue with enforcing the build of this + # version. + target_version = self.target_version + if not target_version: + assert self.skip_build, "Should have already checked this" + target_version = '%d.%d' % sys.version_info[:2] + plat_specifier = ".%s-%s" % (self.plat_name, target_version) + build = self.get_finalized_command('build') + build.build_lib = os.path.join(build.build_base, + 'lib' + plat_specifier) + + log.info("installing to %s", self.bdist_dir) + install.ensure_finalized() + + # avoid warning of 'install_lib' about installing + # into a directory not in sys.path + sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) + + install.run() + + del sys.path[0] + + self.mkpath(self.dist_dir) + fullname = self.distribution.get_fullname() + installer_name = self.get_installer_filename(fullname) + installer_name = os.path.abspath(installer_name) + if os.path.exists(installer_name): os.unlink(installer_name) + + metadata = self.distribution.metadata + author = metadata.author + if not author: + author = metadata.maintainer + if not author: + author = "UNKNOWN" + version = metadata.get_version() + # ProductVersion must be strictly numeric + # XXX need to deal with prerelease versions + sversion = "%d.%d.%d" % StrictVersion(version).version + # Prefix ProductName with Python x.y, so that + # it sorts together with the other Python packages + # in Add-Remove-Programs (APR) + fullname = self.distribution.get_fullname() + if self.target_version: + product_name = "Python %s %s" % (self.target_version, fullname) + else: + product_name = "Python %s" % (fullname) + self.db = msilib.init_database(installer_name, schema, + product_name, msilib.gen_uuid(), + sversion, author) + msilib.add_tables(self.db, sequence) + props = [('DistVersion', version)] + email = metadata.author_email or metadata.maintainer_email + if email: + props.append(("ARPCONTACT", email)) + if metadata.url: + props.append(("ARPURLINFOABOUT", metadata.url)) + if props: + add_data(self.db, 'Property', props) + + self.add_find_python() + self.add_files() + self.add_scripts() + self.add_ui() + self.db.Commit() + + if hasattr(self.distribution, 'dist_files'): + tup = 'bdist_msi', self.target_version or 'any', fullname + self.distribution.dist_files.append(tup) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) + + def add_files(self): + db = self.db + cab = msilib.CAB("distfiles") + rootdir = os.path.abspath(self.bdist_dir) + + root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir") + f = Feature(db, "Python", "Python", "Everything", + 0, 1, directory="TARGETDIR") + + items = [(f, root, '')] + for version in self.versions + [self.other_version]: + target = "TARGETDIR" + version + name = default = "Python" + version + desc = "Everything" + if version is self.other_version: + title = "Python from another location" + level = 2 + else: + title = "Python %s from registry" % version + level = 1 + f = Feature(db, name, title, desc, 1, level, directory=target) + dir = Directory(db, cab, root, rootdir, target, default) + items.append((f, dir, version)) + db.Commit() + + seen = {} + for feature, dir, version in items: + todo = [dir] + while todo: + dir = todo.pop() + for file in os.listdir(dir.absolute): + afile = os.path.join(dir.absolute, file) + if os.path.isdir(afile): + short = "%s|%s" % (dir.make_short(file), file) + default = file + version + newdir = Directory(db, cab, dir, file, default, short) + todo.append(newdir) + else: + if not dir.component: + dir.start_component(dir.logical, feature, 0) + if afile not in seen: + key = seen[afile] = dir.add_file(file) + if file==self.install_script: + if self.install_script_key: + raise DistutilsOptionError( + "Multiple files with name %s" % file) + self.install_script_key = '[#%s]' % key + else: + key = seen[afile] + add_data(self.db, "DuplicateFile", + [(key + version, dir.component, key, None, dir.logical)]) + db.Commit() + cab.commit(db) + + def add_find_python(self): + """Adds code to the installer to compute the location of Python. + + Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the + registry for each version of Python. + + Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined, + else from PYTHON.MACHINE.X.Y. + + Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe""" + + start = 402 + for ver in self.versions: + install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver + machine_reg = "python.machine." + ver + user_reg = "python.user." + ver + machine_prop = "PYTHON.MACHINE." + ver + user_prop = "PYTHON.USER." + ver + machine_action = "PythonFromMachine" + ver + user_action = "PythonFromUser" + ver + exe_action = "PythonExe" + ver + target_dir_prop = "TARGETDIR" + ver + exe_prop = "PYTHON" + ver + if msilib.Win64: + # type: msidbLocatorTypeRawValue + msidbLocatorType64bit + Type = 2+16 + else: + Type = 2 + add_data(self.db, "RegLocator", + [(machine_reg, 2, install_path, None, Type), + (user_reg, 1, install_path, None, Type)]) + add_data(self.db, "AppSearch", + [(machine_prop, machine_reg), + (user_prop, user_reg)]) + add_data(self.db, "CustomAction", + [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"), + (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"), + (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"), + ]) + add_data(self.db, "InstallExecuteSequence", + [(machine_action, machine_prop, start), + (user_action, user_prop, start + 1), + (exe_action, None, start + 2), + ]) + add_data(self.db, "InstallUISequence", + [(machine_action, machine_prop, start), + (user_action, user_prop, start + 1), + (exe_action, None, start + 2), + ]) + add_data(self.db, "Condition", + [("Python" + ver, 0, "NOT TARGETDIR" + ver)]) + start += 4 + assert start < 500 + + def add_scripts(self): + if self.install_script: + start = 6800 + for ver in self.versions + [self.other_version]: + install_action = "install_script." + ver + exe_prop = "PYTHON" + ver + add_data(self.db, "CustomAction", + [(install_action, 50, exe_prop, self.install_script_key)]) + add_data(self.db, "InstallExecuteSequence", + [(install_action, "&Python%s=3" % ver, start)]) + start += 1 + # XXX pre-install scripts are currently refused in finalize_options() + # but if this feature is completed, it will also need to add + # entries for each version as the above code does + if self.pre_install_script: + scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") + with open(scriptfn, "w") as f: + # The batch file will be executed with [PYTHON], so that %1 + # is the path to the Python interpreter; %0 will be the path + # of the batch file. + # rem =""" + # %1 %0 + # exit + # """ + # + f.write('rem ="""\n%1 %0\nexit\n"""\n') + with open(self.pre_install_script) as fin: + f.write(fin.read()) + add_data(self.db, "Binary", + [("PreInstall", msilib.Binary(scriptfn)) + ]) + add_data(self.db, "CustomAction", + [("PreInstall", 2, "PreInstall", None) + ]) + add_data(self.db, "InstallExecuteSequence", + [("PreInstall", "NOT Installed", 450)]) + + + def add_ui(self): + db = self.db + x = y = 50 + w = 370 + h = 300 + title = "[ProductName] Setup" + + # see "Dialog Style Bits" + modal = 3 # visible | modal + modeless = 1 # visible + track_disk_space = 32 + + # UI customization properties + add_data(db, "Property", + # See "DefaultUIFont Property" + [("DefaultUIFont", "DlgFont8"), + # See "ErrorDialog Style Bit" + ("ErrorDialog", "ErrorDlg"), + ("Progress1", "Install"), # modified in maintenance type dlg + ("Progress2", "installs"), + ("MaintenanceForm_Action", "Repair"), + # possible values: ALL, JUSTME + ("WhichUsers", "ALL") + ]) + + # Fonts, see "TextStyle Table" + add_data(db, "TextStyle", + [("DlgFont8", "Tahoma", 9, None, 0), + ("DlgFontBold8", "Tahoma", 8, None, 1), #bold + ("VerdanaBold10", "Verdana", 10, None, 1), + ("VerdanaRed9", "Verdana", 9, 255, 0), + ]) + + # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table" + # Numbers indicate sequence; see sequence.py for how these action integrate + add_data(db, "InstallUISequence", + [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140), + ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141), + # In the user interface, assume all-users installation if privileged. + ("SelectFeaturesDlg", "Not Installed", 1230), + # XXX no support for resume installations yet + #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240), + ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), + ("ProgressDlg", None, 1280)]) + + add_data(db, 'ActionText', text.ActionText) + add_data(db, 'UIText', text.UIText) + ##################################################################### + # Standard dialogs: FatalError, UserExit, ExitDialog + fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + fatal.title("[ProductName] Installer ended prematurely") + fatal.back("< Back", "Finish", active = 0) + fatal.cancel("Cancel", "Back", active = 0) + fatal.text("Description1", 15, 70, 320, 80, 0x30003, + "[ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again.") + fatal.text("Description2", 15, 155, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c=fatal.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Exit") + + user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + user_exit.title("[ProductName] Installer was interrupted") + user_exit.back("< Back", "Finish", active = 0) + user_exit.cancel("Cancel", "Back", active = 0) + user_exit.text("Description1", 15, 70, 320, 80, 0x30003, + "[ProductName] setup was interrupted. Your system has not been modified. " + "To install this program at a later time, please run the installation again.") + user_exit.text("Description2", 15, 155, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c = user_exit.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Exit") + + exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title, + "Finish", "Finish", "Finish") + exit_dialog.title("Completing the [ProductName] Installer") + exit_dialog.back("< Back", "Finish", active = 0) + exit_dialog.cancel("Cancel", "Back", active = 0) + exit_dialog.text("Description", 15, 235, 320, 20, 0x30003, + "Click the Finish button to exit the Installer.") + c = exit_dialog.next("Finish", "Cancel", name="Finish") + c.event("EndDialog", "Return") + + ##################################################################### + # Required dialog: FilesInUse, ErrorDlg + inuse = PyDialog(db, "FilesInUse", + x, y, w, h, + 19, # KeepModeless|Modal|Visible + title, + "Retry", "Retry", "Retry", bitmap=False) + inuse.text("Title", 15, 6, 200, 15, 0x30003, + r"{\DlgFontBold8}Files in Use") + inuse.text("Description", 20, 23, 280, 20, 0x30003, + "Some files that need to be updated are currently in use.") + inuse.text("Text", 20, 55, 330, 50, 3, + "The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.") + inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess", + None, None, None) + c=inuse.back("Exit", "Ignore", name="Exit") + c.event("EndDialog", "Exit") + c=inuse.next("Ignore", "Retry", name="Ignore") + c.event("EndDialog", "Ignore") + c=inuse.cancel("Retry", "Exit", name="Retry") + c.event("EndDialog","Retry") + + # See "Error Dialog". See "ICE20" for the required names of the controls. + error = Dialog(db, "ErrorDlg", + 50, 10, 330, 101, + 65543, # Error|Minimize|Modal|Visible + title, + "ErrorText", None, None) + error.text("ErrorText", 50,9,280,48,3, "") + #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None) + error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo") + error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes") + error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort") + error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel") + error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore") + error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk") + error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry") + + ##################################################################### + # Global "Query Cancel" dialog + cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title, + "No", "No", "No") + cancel.text("Text", 48, 15, 194, 30, 3, + "Are you sure you want to cancel [ProductName] installation?") + #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None, + # "py.ico", None, None) + c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No") + c.event("EndDialog", "Exit") + + c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes") + c.event("EndDialog", "Return") + + ##################################################################### + # Global "Wait for costing" dialog + costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title, + "Return", "Return", "Return") + costing.text("Text", 48, 15, 194, 30, 3, + "Please wait while the installer finishes determining your disk space requirements.") + c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None) + c.event("EndDialog", "Exit") + + ##################################################################### + # Preparation dialog: no user input except cancellation + prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title, + "Cancel", "Cancel", "Cancel") + prep.text("Description", 15, 70, 320, 40, 0x30003, + "Please wait while the Installer prepares to guide you through the installation.") + prep.title("Welcome to the [ProductName] Installer") + c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...") + c.mapping("ActionText", "Text") + c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None) + c.mapping("ActionData", "Text") + prep.back("Back", None, active=0) + prep.next("Next", None, active=0) + c=prep.cancel("Cancel", None) + c.event("SpawnDialog", "CancelDlg") + + ##################################################################### + # Feature (Python directory) selection + seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title, + "Next", "Next", "Cancel") + seldlg.title("Select Python Installations") + + seldlg.text("Hint", 15, 30, 300, 20, 3, + "Select the Python locations where %s should be installed." + % self.distribution.get_fullname()) + + seldlg.back("< Back", None, active=0) + c = seldlg.next("Next >", "Cancel") + order = 1 + c.event("[TARGETDIR]", "[SourceDir]", ordering=order) + for version in self.versions + [self.other_version]: + order += 1 + c.event("[TARGETDIR]", "[TARGETDIR%s]" % version, + "FEATURE_SELECTED AND &Python%s=3" % version, + ordering=order) + c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1) + c.event("EndDialog", "Return", ordering=order + 2) + c = seldlg.cancel("Cancel", "Features") + c.event("SpawnDialog", "CancelDlg") + + c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3, + "FEATURE", None, "PathEdit", None) + c.event("[FEATURE_SELECTED]", "1") + ver = self.other_version + install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver + dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver + + c = seldlg.text("Other", 15, 200, 300, 15, 3, + "Provide an alternate Python location") + c.condition("Enable", install_other_cond) + c.condition("Show", install_other_cond) + c.condition("Disable", dont_install_other_cond) + c.condition("Hide", dont_install_other_cond) + + c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1, + "TARGETDIR" + ver, None, "Next", None) + c.condition("Enable", install_other_cond) + c.condition("Show", install_other_cond) + c.condition("Disable", dont_install_other_cond) + c.condition("Hide", dont_install_other_cond) + + ##################################################################### + # Disk cost + cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title, + "OK", "OK", "OK", bitmap=False) + cost.text("Title", 15, 6, 200, 15, 0x30003, + r"{\DlgFontBold8}Disk Space Requirements") + cost.text("Description", 20, 20, 280, 20, 0x30003, + "The disk space required for the installation of the selected features.") + cost.text("Text", 20, 53, 330, 60, 3, + "The highlighted volumes (if any) do not have enough disk space " + "available for the currently selected features. You can either " + "remove some files from the highlighted volumes, or choose to " + "install less features onto local drive(s), or select different " + "destination drive(s).") + cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223, + None, "{120}{70}{70}{70}{70}", None, None) + cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return") + + ##################################################################### + # WhichUsers Dialog. Only available on NT, and for privileged users. + # This must be run before FindRelatedProducts, because that will + # take into account whether the previous installation was per-user + # or per-machine. We currently don't support going back to this + # dialog after "Next" was selected; to support this, we would need to + # find how to reset the ALLUSERS property, and how to re-run + # FindRelatedProducts. + # On Windows9x, the ALLUSERS property is ignored on the command line + # and in the Property table, but installer fails according to the documentation + # if a dialog attempts to set ALLUSERS. + whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title, + "AdminInstall", "Next", "Cancel") + whichusers.title("Select whether to install [ProductName] for all users of this computer.") + # A radio group with two options: allusers, justme + g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3, + "WhichUsers", "", "Next") + g.add("ALL", 0, 5, 150, 20, "Install for all users") + g.add("JUSTME", 0, 25, 150, 20, "Install just for me") + + whichusers.back("Back", None, active=0) + + c = whichusers.next("Next >", "Cancel") + c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) + c.event("EndDialog", "Return", ordering = 2) + + c = whichusers.cancel("Cancel", "AdminInstall") + c.event("SpawnDialog", "CancelDlg") + + ##################################################################### + # Installation Progress dialog (modeless) + progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title, + "Cancel", "Cancel", "Cancel", bitmap=False) + progress.text("Title", 20, 15, 200, 15, 0x30003, + r"{\DlgFontBold8}[Progress1] [ProductName]") + progress.text("Text", 35, 65, 300, 30, 3, + "Please wait while the Installer [Progress2] [ProductName]. " + "This may take several minutes.") + progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:") + + c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...") + c.mapping("ActionText", "Text") + + #c=progress.text("ActionData", 35, 140, 300, 20, 3, None) + #c.mapping("ActionData", "Text") + + c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537, + None, "Progress done", None, None) + c.mapping("SetProgress", "Progress") + + progress.back("< Back", "Next", active=False) + progress.next("Next >", "Cancel", active=False) + progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg") + + ################################################################### + # Maintenance type: repair/uninstall + maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title, + "Next", "Next", "Cancel") + maint.title("Welcome to the [ProductName] Setup Wizard") + maint.text("BodyText", 15, 63, 330, 42, 3, + "Select whether you want to repair or remove [ProductName].") + g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3, + "MaintenanceForm_Action", "", "Next") + #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]") + g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]") + g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]") + + maint.back("< Back", None, active=False) + c=maint.next("Finish", "Cancel") + # Change installation: Change progress dialog to "Change", then ask + # for feature selection + #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1) + #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2) + + # Reinstall: Change progress dialog to "Repair", then invoke reinstall + # Also set list of reinstalled features to "ALL" + c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5) + c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6) + c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7) + c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8) + + # Uninstall: Change progress to "Remove", then invoke uninstall + # Also set list of removed features to "ALL" + c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11) + c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12) + c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13) + c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14) + + # Close dialog when maintenance action scheduled + c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20) + #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21) + + maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg") + + def get_installer_filename(self, fullname): + # Factored out to allow overriding in subclasses + if self.target_version: + base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, + self.target_version) + else: + base_name = "%s.%s.msi" % (fullname, self.plat_name) + installer_name = os.path.join(self.dist_dir, base_name) + return installer_name diff --git a/setuptools/_distutils/command/bdist_rpm.py b/setuptools/_distutils/command/bdist_rpm.py new file mode 100644 index 00000000..550cbfa1 --- /dev/null +++ b/setuptools/_distutils/command/bdist_rpm.py @@ -0,0 +1,579 @@ +"""distutils.command.bdist_rpm + +Implements the Distutils 'bdist_rpm' command (create RPM source and binary +distributions).""" + +import subprocess, sys, os +from distutils.core import Command +from distutils.debug import DEBUG +from distutils.file_util import write_file +from distutils.errors import * +from distutils.sysconfig import get_python_version +from distutils import log + +class bdist_rpm(Command): + + description = "create an RPM distribution" + + user_options = [ + ('bdist-base=', None, + "base directory for creating built distributions"), + ('rpm-base=', None, + "base directory for creating RPMs (defaults to \"rpm\" under " + "--bdist-base; must be specified for RPM 2)"), + ('dist-dir=', 'd', + "directory to put final RPM files in " + "(and .spec files if --spec-only)"), + ('python=', None, + "path to Python interpreter to hard-code in the .spec file " + "(default: \"python\")"), + ('fix-python', None, + "hard-code the exact path to the current Python interpreter in " + "the .spec file"), + ('spec-only', None, + "only regenerate spec file"), + ('source-only', None, + "only generate source RPM"), + ('binary-only', None, + "only generate binary RPM"), + ('use-bzip2', None, + "use bzip2 instead of gzip to create source distribution"), + + # More meta-data: too RPM-specific to put in the setup script, + # but needs to go in the .spec file -- so we make these options + # to "bdist_rpm". The idea is that packagers would put this + # info in setup.cfg, although they are of course free to + # supply it on the command line. + ('distribution-name=', None, + "name of the (Linux) distribution to which this " + "RPM applies (*not* the name of the module distribution!)"), + ('group=', None, + "package classification [default: \"Development/Libraries\"]"), + ('release=', None, + "RPM release number"), + ('serial=', None, + "RPM serial number"), + ('vendor=', None, + "RPM \"vendor\" (eg. \"Joe Blow \") " + "[default: maintainer or author from setup script]"), + ('packager=', None, + "RPM packager (eg. \"Jane Doe \") " + "[default: vendor]"), + ('doc-files=', None, + "list of documentation files (space or comma-separated)"), + ('changelog=', None, + "RPM changelog"), + ('icon=', None, + "name of icon file"), + ('provides=', None, + "capabilities provided by this package"), + ('requires=', None, + "capabilities required by this package"), + ('conflicts=', None, + "capabilities which conflict with this package"), + ('build-requires=', None, + "capabilities required to build this package"), + ('obsoletes=', None, + "capabilities made obsolete by this package"), + ('no-autoreq', None, + "do not automatically calculate dependencies"), + + # Actions to take when building RPM + ('keep-temp', 'k', + "don't clean up RPM build directory"), + ('no-keep-temp', None, + "clean up RPM build directory [default]"), + ('use-rpm-opt-flags', None, + "compile with RPM_OPT_FLAGS when building from source RPM"), + ('no-rpm-opt-flags', None, + "do not pass any RPM CFLAGS to compiler"), + ('rpm3-mode', None, + "RPM 3 compatibility mode (default)"), + ('rpm2-mode', None, + "RPM 2 compatibility mode"), + + # Add the hooks necessary for specifying custom scripts + ('prep-script=', None, + "Specify a script for the PREP phase of RPM building"), + ('build-script=', None, + "Specify a script for the BUILD phase of RPM building"), + + ('pre-install=', None, + "Specify a script for the pre-INSTALL phase of RPM building"), + ('install-script=', None, + "Specify a script for the INSTALL phase of RPM building"), + ('post-install=', None, + "Specify a script for the post-INSTALL phase of RPM building"), + + ('pre-uninstall=', None, + "Specify a script for the pre-UNINSTALL phase of RPM building"), + ('post-uninstall=', None, + "Specify a script for the post-UNINSTALL phase of RPM building"), + + ('clean-script=', None, + "Specify a script for the CLEAN phase of RPM building"), + + ('verify-script=', None, + "Specify a script for the VERIFY phase of the RPM build"), + + # Allow a packager to explicitly force an architecture + ('force-arch=', None, + "Force an architecture onto the RPM build process"), + + ('quiet', 'q', + "Run the INSTALL phase of RPM building in quiet mode"), + ] + + boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', + 'no-autoreq', 'quiet'] + + negative_opt = {'no-keep-temp': 'keep-temp', + 'no-rpm-opt-flags': 'use-rpm-opt-flags', + 'rpm2-mode': 'rpm3-mode'} + + + def initialize_options(self): + self.bdist_base = None + self.rpm_base = None + self.dist_dir = None + self.python = None + self.fix_python = None + self.spec_only = None + self.binary_only = None + self.source_only = None + self.use_bzip2 = None + + self.distribution_name = None + self.group = None + self.release = None + self.serial = None + self.vendor = None + self.packager = None + self.doc_files = None + self.changelog = None + self.icon = None + + self.prep_script = None + self.build_script = None + self.install_script = None + self.clean_script = None + self.verify_script = None + self.pre_install = None + self.post_install = None + self.pre_uninstall = None + self.post_uninstall = None + self.prep = None + self.provides = None + self.requires = None + self.conflicts = None + self.build_requires = None + self.obsoletes = None + + self.keep_temp = 0 + self.use_rpm_opt_flags = 1 + self.rpm3_mode = 1 + self.no_autoreq = 0 + + self.force_arch = None + self.quiet = 0 + + def finalize_options(self): + self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) + if self.rpm_base is None: + if not self.rpm3_mode: + raise DistutilsOptionError( + "you must specify --rpm-base in RPM 2 mode") + self.rpm_base = os.path.join(self.bdist_base, "rpm") + + if self.python is None: + if self.fix_python: + self.python = sys.executable + else: + self.python = "python3" + elif self.fix_python: + raise DistutilsOptionError( + "--python and --fix-python are mutually exclusive options") + + if os.name != 'posix': + raise DistutilsPlatformError("don't know how to create RPM " + "distributions on platform %s" % os.name) + if self.binary_only and self.source_only: + raise DistutilsOptionError( + "cannot supply both '--source-only' and '--binary-only'") + + # don't pass CFLAGS to pure python distributions + if not self.distribution.has_ext_modules(): + self.use_rpm_opt_flags = 0 + + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + self.finalize_package_data() + + def finalize_package_data(self): + self.ensure_string('group', "Development/Libraries") + self.ensure_string('vendor', + "%s <%s>" % (self.distribution.get_contact(), + self.distribution.get_contact_email())) + self.ensure_string('packager') + self.ensure_string_list('doc_files') + if isinstance(self.doc_files, list): + for readme in ('README', 'README.txt'): + if os.path.exists(readme) and readme not in self.doc_files: + self.doc_files.append(readme) + + self.ensure_string('release', "1") + self.ensure_string('serial') # should it be an int? + + self.ensure_string('distribution_name') + + self.ensure_string('changelog') + # Format changelog correctly + self.changelog = self._format_changelog(self.changelog) + + self.ensure_filename('icon') + + self.ensure_filename('prep_script') + self.ensure_filename('build_script') + self.ensure_filename('install_script') + self.ensure_filename('clean_script') + self.ensure_filename('verify_script') + self.ensure_filename('pre_install') + self.ensure_filename('post_install') + self.ensure_filename('pre_uninstall') + self.ensure_filename('post_uninstall') + + # XXX don't forget we punted on summaries and descriptions -- they + # should be handled here eventually! + + # Now *this* is some meta-data that belongs in the setup script... + self.ensure_string_list('provides') + self.ensure_string_list('requires') + self.ensure_string_list('conflicts') + self.ensure_string_list('build_requires') + self.ensure_string_list('obsoletes') + + self.ensure_string('force_arch') + + def run(self): + if DEBUG: + print("before _get_package_data():") + print("vendor =", self.vendor) + print("packager =", self.packager) + print("doc_files =", self.doc_files) + print("changelog =", self.changelog) + + # make directories + if self.spec_only: + spec_dir = self.dist_dir + self.mkpath(spec_dir) + else: + rpm_dir = {} + for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): + rpm_dir[d] = os.path.join(self.rpm_base, d) + self.mkpath(rpm_dir[d]) + spec_dir = rpm_dir['SPECS'] + + # Spec file goes into 'dist_dir' if '--spec-only specified', + # build/rpm. otherwise. + spec_path = os.path.join(spec_dir, + "%s.spec" % self.distribution.get_name()) + self.execute(write_file, + (spec_path, + self._make_spec_file()), + "writing '%s'" % spec_path) + + if self.spec_only: # stop if requested + return + + # Make a source distribution and copy to SOURCES directory with + # optional icon. + saved_dist_files = self.distribution.dist_files[:] + sdist = self.reinitialize_command('sdist') + if self.use_bzip2: + sdist.formats = ['bztar'] + else: + sdist.formats = ['gztar'] + self.run_command('sdist') + self.distribution.dist_files = saved_dist_files + + source = sdist.get_archive_files()[0] + source_dir = rpm_dir['SOURCES'] + self.copy_file(source, source_dir) + + if self.icon: + if os.path.exists(self.icon): + self.copy_file(self.icon, source_dir) + else: + raise DistutilsFileError( + "icon file '%s' does not exist" % self.icon) + + # build package + log.info("building RPMs") + rpm_cmd = ['rpmbuild'] + + if self.source_only: # what kind of RPMs? + rpm_cmd.append('-bs') + elif self.binary_only: + rpm_cmd.append('-bb') + else: + rpm_cmd.append('-ba') + rpm_cmd.extend(['--define', '__python %s' % self.python]) + if self.rpm3_mode: + rpm_cmd.extend(['--define', + '_topdir %s' % os.path.abspath(self.rpm_base)]) + if not self.keep_temp: + rpm_cmd.append('--clean') + + if self.quiet: + rpm_cmd.append('--quiet') + + rpm_cmd.append(spec_path) + # Determine the binary rpm names that should be built out of this spec + # file + # Note that some of these may not be really built (if the file + # list is empty) + nvr_string = "%{name}-%{version}-%{release}" + src_rpm = nvr_string + ".src.rpm" + non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" + q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % ( + src_rpm, non_src_rpm, spec_path) + + out = os.popen(q_cmd) + try: + binary_rpms = [] + source_rpm = None + while True: + line = out.readline() + if not line: + break + l = line.strip().split() + assert(len(l) == 2) + binary_rpms.append(l[1]) + # The source rpm is named after the first entry in the spec file + if source_rpm is None: + source_rpm = l[0] + + status = out.close() + if status: + raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) + + finally: + out.close() + + self.spawn(rpm_cmd) + + if not self.dry_run: + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + + if not self.binary_only: + srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) + assert(os.path.exists(srpm)) + self.move_file(srpm, self.dist_dir) + filename = os.path.join(self.dist_dir, source_rpm) + self.distribution.dist_files.append( + ('bdist_rpm', pyversion, filename)) + + if not self.source_only: + for rpm in binary_rpms: + rpm = os.path.join(rpm_dir['RPMS'], rpm) + if os.path.exists(rpm): + self.move_file(rpm, self.dist_dir) + filename = os.path.join(self.dist_dir, + os.path.basename(rpm)) + self.distribution.dist_files.append( + ('bdist_rpm', pyversion, filename)) + + def _dist_path(self, path): + return os.path.join(self.dist_dir, os.path.basename(path)) + + def _make_spec_file(self): + """Generate the text of an RPM spec file and return it as a + list of strings (one per line). + """ + # definitions and headers + spec_file = [ + '%define name ' + self.distribution.get_name(), + '%define version ' + self.distribution.get_version().replace('-','_'), + '%define unmangled_version ' + self.distribution.get_version(), + '%define release ' + self.release.replace('-','_'), + '', + 'Summary: ' + self.distribution.get_description(), + ] + + # Workaround for #14443 which affects some RPM based systems such as + # RHEL6 (and probably derivatives) + vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}') + # Generate a potential replacement value for __os_install_post (whilst + # normalizing the whitespace to simplify the test for whether the + # invocation of brp-python-bytecompile passes in __python): + vendor_hook = '\n'.join([' %s \\' % line.strip() + for line in vendor_hook.splitlines()]) + problem = "brp-python-bytecompile \\\n" + fixed = "brp-python-bytecompile %{__python} \\\n" + fixed_hook = vendor_hook.replace(problem, fixed) + if fixed_hook != vendor_hook: + spec_file.append('# Workaround for http://bugs.python.org/issue14443') + spec_file.append('%define __os_install_post ' + fixed_hook + '\n') + + # put locale summaries into spec file + # XXX not supported for now (hard to put a dictionary + # in a config file -- arg!) + #for locale in self.summaries.keys(): + # spec_file.append('Summary(%s): %s' % (locale, + # self.summaries[locale])) + + spec_file.extend([ + 'Name: %{name}', + 'Version: %{version}', + 'Release: %{release}',]) + + # XXX yuck! this filename is available from the "sdist" command, + # but only after it has run: and we create the spec file before + # running "sdist", in case of --spec-only. + if self.use_bzip2: + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') + else: + spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') + + spec_file.extend([ + 'License: ' + self.distribution.get_license(), + 'Group: ' + self.group, + 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', + 'Prefix: %{_prefix}', ]) + + if not self.force_arch: + # noarch if no extension modules + if not self.distribution.has_ext_modules(): + spec_file.append('BuildArch: noarch') + else: + spec_file.append( 'BuildArch: %s' % self.force_arch ) + + for field in ('Vendor', + 'Packager', + 'Provides', + 'Requires', + 'Conflicts', + 'Obsoletes', + ): + val = getattr(self, field.lower()) + if isinstance(val, list): + spec_file.append('%s: %s' % (field, ' '.join(val))) + elif val is not None: + spec_file.append('%s: %s' % (field, val)) + + + if self.distribution.get_url() != 'UNKNOWN': + spec_file.append('Url: ' + self.distribution.get_url()) + + if self.distribution_name: + spec_file.append('Distribution: ' + self.distribution_name) + + if self.build_requires: + spec_file.append('BuildRequires: ' + + ' '.join(self.build_requires)) + + if self.icon: + spec_file.append('Icon: ' + os.path.basename(self.icon)) + + if self.no_autoreq: + spec_file.append('AutoReq: 0') + + spec_file.extend([ + '', + '%description', + self.distribution.get_long_description() + ]) + + # put locale descriptions into spec file + # XXX again, suppressed because config file syntax doesn't + # easily support this ;-( + #for locale in self.descriptions.keys(): + # spec_file.extend([ + # '', + # '%description -l ' + locale, + # self.descriptions[locale], + # ]) + + # rpm scripts + # figure out default build script + def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0])) + def_build = "%s build" % def_setup_call + if self.use_rpm_opt_flags: + def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build + + # insert contents of files + + # XXX this is kind of misleading: user-supplied options are files + # that we open and interpolate into the spec file, but the defaults + # are just text that we drop in as-is. Hmmm. + + install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT ' + '--record=INSTALLED_FILES') % def_setup_call + + script_options = [ + ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), + ('build', 'build_script', def_build), + ('install', 'install_script', install_cmd), + ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), + ('verifyscript', 'verify_script', None), + ('pre', 'pre_install', None), + ('post', 'post_install', None), + ('preun', 'pre_uninstall', None), + ('postun', 'post_uninstall', None), + ] + + for (rpm_opt, attr, default) in script_options: + # Insert contents of file referred to, if no file is referred to + # use 'default' as contents of script + val = getattr(self, attr) + if val or default: + spec_file.extend([ + '', + '%' + rpm_opt,]) + if val: + with open(val) as f: + spec_file.extend(f.read().split('\n')) + else: + spec_file.append(default) + + + # files section + spec_file.extend([ + '', + '%files -f INSTALLED_FILES', + '%defattr(-,root,root)', + ]) + + if self.doc_files: + spec_file.append('%doc ' + ' '.join(self.doc_files)) + + if self.changelog: + spec_file.extend([ + '', + '%changelog',]) + spec_file.extend(self.changelog) + + return spec_file + + def _format_changelog(self, changelog): + """Format the changelog correctly and convert it to a list of strings + """ + if not changelog: + return changelog + new_changelog = [] + for line in changelog.strip().split('\n'): + line = line.strip() + if line[0] == '*': + new_changelog.extend(['', line]) + elif line[0] == '-': + new_changelog.append(line) + else: + new_changelog.append(' ' + line) + + # strip trailing newline inserted by first changelog entry + if not new_changelog[0]: + del new_changelog[0] + + return new_changelog diff --git a/setuptools/_distutils/command/bdist_wininst.py b/setuptools/_distutils/command/bdist_wininst.py new file mode 100644 index 00000000..0e9ddaa2 --- /dev/null +++ b/setuptools/_distutils/command/bdist_wininst.py @@ -0,0 +1,377 @@ +"""distutils.command.bdist_wininst + +Implements the Distutils 'bdist_wininst' command: create a windows installer +exe-program.""" + +import os +import sys +import warnings +from distutils.core import Command +from distutils.util import get_platform +from distutils.dir_util import remove_tree +from distutils.errors import * +from distutils.sysconfig import get_python_version +from distutils import log + +class bdist_wininst(Command): + + description = "create an executable installer for MS Windows" + + user_options = [('bdist-dir=', None, + "temporary directory for creating the distribution"), + ('plat-name=', 'p', + "platform name to embed in generated filenames " + "(default: %s)" % get_platform()), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('target-version=', None, + "require a specific python version" + + " on the target system"), + ('no-target-compile', 'c', + "do not compile .py to .pyc on the target system"), + ('no-target-optimize', 'o', + "do not compile .py to .pyo (optimized) " + "on the target system"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('bitmap=', 'b', + "bitmap to use for the installer instead of python-powered logo"), + ('title=', 't', + "title to display on the installer background instead of default"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ('install-script=', None, + "basename of installation script to be run after " + "installation or before deinstallation"), + ('pre-install-script=', None, + "Fully qualified filename of a script to be run before " + "any files are installed. This script need not be in the " + "distribution"), + ('user-access-control=', None, + "specify Vista's UAC handling - 'none'/default=no " + "handling, 'auto'=use UAC if target Python installed for " + "all users, 'force'=always use UAC"), + ] + + boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', + 'skip-build'] + + # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows + _unsupported = (sys.platform != "win32") + + def __init__(self, *args, **kw): + super().__init__(*args, **kw) + warnings.warn("bdist_wininst command is deprecated since Python 3.8, " + "use bdist_wheel (wheel packages) instead", + DeprecationWarning, 2) + + def initialize_options(self): + self.bdist_dir = None + self.plat_name = None + self.keep_temp = 0 + self.no_target_compile = 0 + self.no_target_optimize = 0 + self.target_version = None + self.dist_dir = None + self.bitmap = None + self.title = None + self.skip_build = None + self.install_script = None + self.pre_install_script = None + self.user_access_control = None + + + def finalize_options(self): + self.set_undefined_options('bdist', ('skip_build', 'skip_build')) + + if self.bdist_dir is None: + if self.skip_build and self.plat_name: + # If build is skipped and plat_name is overridden, bdist will + # not see the correct 'plat_name' - so set that up manually. + bdist = self.distribution.get_command_obj('bdist') + bdist.plat_name = self.plat_name + # next the command will be initialized using that name + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'wininst') + + if not self.target_version: + self.target_version = "" + + if not self.skip_build and self.distribution.has_ext_modules(): + short_version = get_python_version() + if self.target_version and self.target_version != short_version: + raise DistutilsOptionError( + "target version can only be %s, or the '--skip-build'" \ + " option must be specified" % (short_version,)) + self.target_version = short_version + + self.set_undefined_options('bdist', + ('dist_dir', 'dist_dir'), + ('plat_name', 'plat_name'), + ) + + if self.install_script: + for script in self.distribution.scripts: + if self.install_script == os.path.basename(script): + break + else: + raise DistutilsOptionError( + "install_script '%s' not found in scripts" + % self.install_script) + + def run(self): + if (sys.platform != "win32" and + (self.distribution.has_ext_modules() or + self.distribution.has_c_libraries())): + raise DistutilsPlatformError \ + ("distribution contains extensions and/or C libraries; " + "must be compiled on a Windows 32 platform") + + if not self.skip_build: + self.run_command('build') + + install = self.reinitialize_command('install', reinit_subcommands=1) + install.root = self.bdist_dir + install.skip_build = self.skip_build + install.warn_dir = 0 + install.plat_name = self.plat_name + + install_lib = self.reinitialize_command('install_lib') + # we do not want to include pyc or pyo files + install_lib.compile = 0 + install_lib.optimize = 0 + + if self.distribution.has_ext_modules(): + # If we are building an installer for a Python version other + # than the one we are currently running, then we need to ensure + # our build_lib reflects the other Python version rather than ours. + # Note that for target_version!=sys.version, we must have skipped the + # build step, so there is no issue with enforcing the build of this + # version. + target_version = self.target_version + if not target_version: + assert self.skip_build, "Should have already checked this" + target_version = '%d.%d' % sys.version_info[:2] + plat_specifier = ".%s-%s" % (self.plat_name, target_version) + build = self.get_finalized_command('build') + build.build_lib = os.path.join(build.build_base, + 'lib' + plat_specifier) + + # Use a custom scheme for the zip-file, because we have to decide + # at installation time which scheme to use. + for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): + value = key.upper() + if key == 'headers': + value = value + '/Include/$dist_name' + setattr(install, + 'install_' + key, + value) + + log.info("installing to %s", self.bdist_dir) + install.ensure_finalized() + + # avoid warning of 'install_lib' about installing + # into a directory not in sys.path + sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) + + install.run() + + del sys.path[0] + + # And make an archive relative to the root of the + # pseudo-installation tree. + from tempfile import mktemp + archive_basename = mktemp() + fullname = self.distribution.get_fullname() + arcname = self.make_archive(archive_basename, "zip", + root_dir=self.bdist_dir) + # create an exe containing the zip-file + self.create_exe(arcname, fullname, self.bitmap) + if self.distribution.has_ext_modules(): + pyversion = get_python_version() + else: + pyversion = 'any' + self.distribution.dist_files.append(('bdist_wininst', pyversion, + self.get_installer_filename(fullname))) + # remove the zip-file again + log.debug("removing temporary file '%s'", arcname) + os.remove(arcname) + + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) + + def get_inidata(self): + # Return data describing the installation. + lines = [] + metadata = self.distribution.metadata + + # Write the [metadata] section. + lines.append("[metadata]") + + # 'info' will be displayed in the installer's dialog box, + # describing the items to be installed. + info = (metadata.long_description or '') + '\n' + + # Escape newline characters + def escape(s): + return s.replace("\n", "\\n") + + for name in ["author", "author_email", "description", "maintainer", + "maintainer_email", "name", "url", "version"]: + data = getattr(metadata, name, "") + if data: + info = info + ("\n %s: %s" % \ + (name.capitalize(), escape(data))) + lines.append("%s=%s" % (name, escape(data))) + + # The [setup] section contains entries controlling + # the installer runtime. + lines.append("\n[Setup]") + if self.install_script: + lines.append("install_script=%s" % self.install_script) + lines.append("info=%s" % escape(info)) + lines.append("target_compile=%d" % (not self.no_target_compile)) + lines.append("target_optimize=%d" % (not self.no_target_optimize)) + if self.target_version: + lines.append("target_version=%s" % self.target_version) + if self.user_access_control: + lines.append("user_access_control=%s" % self.user_access_control) + + title = self.title or self.distribution.get_fullname() + lines.append("title=%s" % escape(title)) + import time + import distutils + build_info = "Built %s with distutils-%s" % \ + (time.ctime(time.time()), distutils.__version__) + lines.append("build_info=%s" % build_info) + return "\n".join(lines) + + def create_exe(self, arcname, fullname, bitmap=None): + import struct + + self.mkpath(self.dist_dir) + + cfgdata = self.get_inidata() + + installer_name = self.get_installer_filename(fullname) + self.announce("creating %s" % installer_name) + + if bitmap: + with open(bitmap, "rb") as f: + bitmapdata = f.read() + bitmaplen = len(bitmapdata) + else: + bitmaplen = 0 + + with open(installer_name, "wb") as file: + file.write(self.get_exe_bytes()) + if bitmap: + file.write(bitmapdata) + + # Convert cfgdata from unicode to ascii, mbcs encoded + if isinstance(cfgdata, str): + cfgdata = cfgdata.encode("mbcs") + + # Append the pre-install script + cfgdata = cfgdata + b"\0" + if self.pre_install_script: + # We need to normalize newlines, so we open in text mode and + # convert back to bytes. "latin-1" simply avoids any possible + # failures. + with open(self.pre_install_script, "r", + encoding="latin-1") as script: + script_data = script.read().encode("latin-1") + cfgdata = cfgdata + script_data + b"\n\0" + else: + # empty pre-install script + cfgdata = cfgdata + b"\0" + file.write(cfgdata) + + # The 'magic number' 0x1234567B is used to make sure that the + # binary layout of 'cfgdata' is what the wininst.exe binary + # expects. If the layout changes, increment that number, make + # the corresponding changes to the wininst.exe sources, and + # recompile them. + header = struct.pack("' under the base build directory. We only use one of + # them for a given distribution, though -- + if self.build_purelib is None: + self.build_purelib = os.path.join(self.build_base, 'lib') + if self.build_platlib is None: + self.build_platlib = os.path.join(self.build_base, + 'lib' + plat_specifier) + + # 'build_lib' is the actual directory that we will use for this + # particular module distribution -- if user didn't supply it, pick + # one of 'build_purelib' or 'build_platlib'. + if self.build_lib is None: + if self.distribution.ext_modules: + self.build_lib = self.build_platlib + else: + self.build_lib = self.build_purelib + + # 'build_temp' -- temporary directory for compiler turds, + # "build/temp." + if self.build_temp is None: + self.build_temp = os.path.join(self.build_base, + 'temp' + plat_specifier) + if self.build_scripts is None: + self.build_scripts = os.path.join(self.build_base, + 'scripts-%d.%d' % sys.version_info[:2]) + + if self.executable is None and sys.executable: + self.executable = os.path.normpath(sys.executable) + + if isinstance(self.parallel, str): + try: + self.parallel = int(self.parallel) + except ValueError: + raise DistutilsOptionError("parallel should be an integer") + + def run(self): + # Run all relevant sub-commands. This will be some subset of: + # - build_py - pure Python modules + # - build_clib - standalone C libraries + # - build_ext - Python extensions + # - build_scripts - (Python) scripts + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + + # -- Predicates for the sub-command list --------------------------- + + def has_pure_modules(self): + return self.distribution.has_pure_modules() + + def has_c_libraries(self): + return self.distribution.has_c_libraries() + + def has_ext_modules(self): + return self.distribution.has_ext_modules() + + def has_scripts(self): + return self.distribution.has_scripts() + + + sub_commands = [('build_py', has_pure_modules), + ('build_clib', has_c_libraries), + ('build_ext', has_ext_modules), + ('build_scripts', has_scripts), + ] diff --git a/setuptools/_distutils/command/build_clib.py b/setuptools/_distutils/command/build_clib.py new file mode 100644 index 00000000..3e20ef23 --- /dev/null +++ b/setuptools/_distutils/command/build_clib.py @@ -0,0 +1,209 @@ +"""distutils.command.build_clib + +Implements the Distutils 'build_clib' command, to build a C/C++ library +that is included in the module distribution and needed by an extension +module.""" + + +# XXX this module has *lots* of code ripped-off quite transparently from +# build_ext.py -- not surprisingly really, as the work required to build +# a static library from a collection of C source files is not really all +# that different from what's required to build a shared object file from +# a collection of C source files. Nevertheless, I haven't done the +# necessary refactoring to account for the overlap in code between the +# two modules, mainly because a number of subtle details changed in the +# cut 'n paste. Sigh. + +import os +from distutils.core import Command +from distutils.errors import * +from distutils.sysconfig import customize_compiler +from distutils import log + +def show_compilers(): + from distutils.ccompiler import show_compilers + show_compilers() + + +class build_clib(Command): + + description = "build C/C++ libraries used by Python extensions" + + user_options = [ + ('build-clib=', 'b', + "directory to build C/C++ libraries to"), + ('build-temp=', 't', + "directory to put temporary build by-products"), + ('debug', 'g', + "compile with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps)"), + ('compiler=', 'c', + "specify the compiler type"), + ] + + boolean_options = ['debug', 'force'] + + help_options = [ + ('help-compiler', None, + "list available compilers", show_compilers), + ] + + def initialize_options(self): + self.build_clib = None + self.build_temp = None + + # List of libraries to build + self.libraries = None + + # Compilation options for all libraries + self.include_dirs = None + self.define = None + self.undef = None + self.debug = None + self.force = 0 + self.compiler = None + + + def finalize_options(self): + # This might be confusing: both build-clib and build-temp default + # to build-temp as defined by the "build" command. This is because + # I think that C libraries are really just temporary build + # by-products, at least from the point of view of building Python + # extensions -- but I want to keep my options open. + self.set_undefined_options('build', + ('build_temp', 'build_clib'), + ('build_temp', 'build_temp'), + ('compiler', 'compiler'), + ('debug', 'debug'), + ('force', 'force')) + + self.libraries = self.distribution.libraries + if self.libraries: + self.check_library_list(self.libraries) + + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + if isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + # XXX same as for build_ext -- what about 'self.define' and + # 'self.undef' ? + + + def run(self): + if not self.libraries: + return + + # Yech -- this is cut 'n pasted from build_ext.py! + from distutils.ccompiler import new_compiler + self.compiler = new_compiler(compiler=self.compiler, + dry_run=self.dry_run, + force=self.force) + customize_compiler(self.compiler) + + if self.include_dirs is not None: + self.compiler.set_include_dirs(self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name,value) in self.define: + self.compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro(macro) + + self.build_libraries(self.libraries) + + + def check_library_list(self, libraries): + """Ensure that the list of libraries is valid. + + `library` is presumably provided as a command option 'libraries'. + This method checks that it is a list of 2-tuples, where the tuples + are (library_name, build_info_dict). + + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ + if not isinstance(libraries, list): + raise DistutilsSetupError( + "'libraries' option must be a list of tuples") + + for lib in libraries: + if not isinstance(lib, tuple) and len(lib) != 2: + raise DistutilsSetupError( + "each element of 'libraries' must a 2-tuple") + + name, build_info = lib + + if not isinstance(name, str): + raise DistutilsSetupError( + "first element of each tuple in 'libraries' " + "must be a string (the library name)") + + if '/' in name or (os.sep != '/' and os.sep in name): + raise DistutilsSetupError("bad library name '%s': " + "may not contain directory separators" % lib[0]) + + if not isinstance(build_info, dict): + raise DistutilsSetupError( + "second element of each tuple in 'libraries' " + "must be a dictionary (build info)") + + + def get_library_names(self): + # Assume the library list is valid -- 'check_library_list()' is + # called from 'finalize_options()', so it should be! + if not self.libraries: + return None + + lib_names = [] + for (lib_name, build_info) in self.libraries: + lib_names.append(lib_name) + return lib_names + + + def get_source_files(self): + self.check_library_list(self.libraries) + filenames = [] + for (lib_name, build_info) in self.libraries: + sources = build_info.get('sources') + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % lib_name) + + filenames.extend(sources) + return filenames + + + def build_libraries(self, libraries): + for (lib_name, build_info) in libraries: + sources = build_info.get('sources') + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % lib_name) + sources = list(sources) + + log.info("building '%s' library", lib_name) + + # First, compile the source code to object files in the library + # directory. (This should probably change to putting object + # files in a temporary build directory.) + macros = build_info.get('macros') + include_dirs = build_info.get('include_dirs') + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=include_dirs, + debug=self.debug) + + # Now "link" the object files together into a static library. + # (On Unix at least, this isn't really linking -- it just + # builds an archive. Whatever.) + self.compiler.create_static_lib(objects, lib_name, + output_dir=self.build_clib, + debug=self.debug) diff --git a/setuptools/_distutils/command/build_ext.py b/setuptools/_distutils/command/build_ext.py new file mode 100644 index 00000000..1a9bd120 --- /dev/null +++ b/setuptools/_distutils/command/build_ext.py @@ -0,0 +1,754 @@ +"""distutils.command.build_ext + +Implements the Distutils 'build_ext' command, for building extension +modules (currently limited to C extensions, should accommodate C++ +extensions ASAP).""" + +import contextlib +import os +import re +import sys +from distutils.core import Command +from distutils.errors import * +from distutils.sysconfig import customize_compiler, get_python_version +from distutils.sysconfig import get_config_h_filename +from distutils.dep_util import newer_group +from distutils.extension import Extension +from distutils.util import get_platform +from distutils import log + +from site import USER_BASE + +# An extension name is just a dot-separated list of Python NAMEs (ie. +# the same as a fully-qualified module name). +extension_name_re = re.compile \ + (r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$') + + +def show_compilers (): + from distutils.ccompiler import show_compilers + show_compilers() + + +class build_ext(Command): + + description = "build C/C++ extensions (compile/link to build directory)" + + # XXX thoughts on how to deal with complex command-line options like + # these, i.e. how to make it so fancy_getopt can suck them off the + # command line and make it look like setup.py defined the appropriate + # lists of tuples of what-have-you. + # - each command needs a callback to process its command-line options + # - Command.__init__() needs access to its share of the whole + # command line (must ultimately come from + # Distribution.parse_command_line()) + # - it then calls the current command class' option-parsing + # callback to deal with weird options like -D, which have to + # parse the option text and churn out some custom data + # structure + # - that data structure (in this case, a list of 2-tuples) + # will then be present in the command object by the time + # we get to finalize_options() (i.e. the constructor + # takes care of both command-line and client options + # in between initialize_options() and finalize_options()) + + sep_by = " (separated by '%s')" % os.pathsep + user_options = [ + ('build-lib=', 'b', + "directory for compiled extension modules"), + ('build-temp=', 't', + "directory for temporary files (build by-products)"), + ('plat-name=', 'p', + "platform name to cross-compile for, if supported " + "(default: %s)" % get_platform()), + ('inplace', 'i', + "ignore build-lib and put compiled extensions into the source " + + "directory alongside your pure Python modules"), + ('include-dirs=', 'I', + "list of directories to search for header files" + sep_by), + ('define=', 'D', + "C preprocessor macros to define"), + ('undef=', 'U', + "C preprocessor macros to undefine"), + ('libraries=', 'l', + "external C libraries to link with"), + ('library-dirs=', 'L', + "directories to search for external C libraries" + sep_by), + ('rpath=', 'R', + "directories to search for shared C libraries at runtime"), + ('link-objects=', 'O', + "extra explicit link objects to include in the link"), + ('debug', 'g', + "compile/link with debugging information"), + ('force', 'f', + "forcibly build everything (ignore file timestamps)"), + ('compiler=', 'c', + "specify the compiler type"), + ('parallel=', 'j', + "number of parallel build jobs"), + ('swig-cpp', None, + "make SWIG create C++ files (default is C)"), + ('swig-opts=', None, + "list of SWIG command line options"), + ('swig=', None, + "path to the SWIG executable"), + ('user', None, + "add user include, library and rpath") + ] + + boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] + + help_options = [ + ('help-compiler', None, + "list available compilers", show_compilers), + ] + + def initialize_options(self): + self.extensions = None + self.build_lib = None + self.plat_name = None + self.build_temp = None + self.inplace = 0 + self.package = None + + self.include_dirs = None + self.define = None + self.undef = None + self.libraries = None + self.library_dirs = None + self.rpath = None + self.link_objects = None + self.debug = None + self.force = None + self.compiler = None + self.swig = None + self.swig_cpp = None + self.swig_opts = None + self.user = None + self.parallel = None + + def finalize_options(self): + from distutils import sysconfig + + self.set_undefined_options('build', + ('build_lib', 'build_lib'), + ('build_temp', 'build_temp'), + ('compiler', 'compiler'), + ('debug', 'debug'), + ('force', 'force'), + ('parallel', 'parallel'), + ('plat_name', 'plat_name'), + ) + + if self.package is None: + self.package = self.distribution.ext_package + + self.extensions = self.distribution.ext_modules + + # Make sure Python's include directories (for Python.h, pyconfig.h, + # etc.) are in the include search path. + py_include = sysconfig.get_python_inc() + plat_py_include = sysconfig.get_python_inc(plat_specific=1) + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + if isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + # If in a virtualenv, add its include directory + # Issue 16116 + if sys.exec_prefix != sys.base_exec_prefix: + self.include_dirs.append(os.path.join(sys.exec_prefix, 'include')) + + # Put the Python "system" include dir at the end, so that + # any local include dirs take precedence. + self.include_dirs.extend(py_include.split(os.path.pathsep)) + if plat_py_include != py_include: + self.include_dirs.extend( + plat_py_include.split(os.path.pathsep)) + + self.ensure_string_list('libraries') + self.ensure_string_list('link_objects') + + # Life is easier if we're not forever checking for None, so + # simplify these options to empty lists if unset + if self.libraries is None: + self.libraries = [] + if self.library_dirs is None: + self.library_dirs = [] + elif isinstance(self.library_dirs, str): + self.library_dirs = self.library_dirs.split(os.pathsep) + + if self.rpath is None: + self.rpath = [] + elif isinstance(self.rpath, str): + self.rpath = self.rpath.split(os.pathsep) + + # for extensions under windows use different directories + # for Release and Debug builds. + # also Python's library directory must be appended to library_dirs + if os.name == 'nt': + # the 'libs' directory is for binary installs - we assume that + # must be the *native* platform. But we don't really support + # cross-compiling via a binary install anyway, so we let it go. + self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) + if sys.base_exec_prefix != sys.prefix: # Issue 16116 + self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs')) + if self.debug: + self.build_temp = os.path.join(self.build_temp, "Debug") + else: + self.build_temp = os.path.join(self.build_temp, "Release") + + # Append the source distribution include and library directories, + # this allows distutils on windows to work in the source tree + self.include_dirs.append(os.path.dirname(get_config_h_filename())) + _sys_home = getattr(sys, '_home', None) + if _sys_home: + self.library_dirs.append(_sys_home) + + # Use the .lib files for the correct architecture + if self.plat_name == 'win32': + suffix = 'win32' + else: + # win-amd64 + suffix = self.plat_name[4:] + new_lib = os.path.join(sys.exec_prefix, 'PCbuild') + if suffix: + new_lib = os.path.join(new_lib, suffix) + self.library_dirs.append(new_lib) + + # For extensions under Cygwin, Python's library directory must be + # appended to library_dirs + if sys.platform[:6] == 'cygwin': + if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): + # building third party extensions + self.library_dirs.append(os.path.join(sys.prefix, "lib", + "python" + get_python_version(), + "config")) + else: + # building python standard extensions + self.library_dirs.append('.') + + # For building extensions with a shared Python library, + # Python's library directory must be appended to library_dirs + # See Issues: #1600860, #4366 + if (sysconfig.get_config_var('Py_ENABLE_SHARED')): + if not sysconfig.python_build: + # building third party extensions + self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) + else: + # building python standard extensions + self.library_dirs.append('.') + + # The argument parsing will result in self.define being a string, but + # it has to be a list of 2-tuples. All the preprocessor symbols + # specified by the 'define' option will be set to '1'. Multiple + # symbols can be separated with commas. + + if self.define: + defines = self.define.split(',') + self.define = [(symbol, '1') for symbol in defines] + + # The option for macros to undefine is also a string from the + # option parsing, but has to be a list. Multiple symbols can also + # be separated with commas here. + if self.undef: + self.undef = self.undef.split(',') + + if self.swig_opts is None: + self.swig_opts = [] + else: + self.swig_opts = self.swig_opts.split(' ') + + # Finally add the user include and library directories if requested + if self.user: + user_include = os.path.join(USER_BASE, "include") + user_lib = os.path.join(USER_BASE, "lib") + if os.path.isdir(user_include): + self.include_dirs.append(user_include) + if os.path.isdir(user_lib): + self.library_dirs.append(user_lib) + self.rpath.append(user_lib) + + if isinstance(self.parallel, str): + try: + self.parallel = int(self.parallel) + except ValueError: + raise DistutilsOptionError("parallel should be an integer") + + def run(self): + from distutils.ccompiler import new_compiler + + # 'self.extensions', as supplied by setup.py, is a list of + # Extension instances. See the documentation for Extension (in + # distutils.extension) for details. + # + # For backwards compatibility with Distutils 0.8.2 and earlier, we + # also allow the 'extensions' list to be a list of tuples: + # (ext_name, build_info) + # where build_info is a dictionary containing everything that + # Extension instances do except the name, with a few things being + # differently named. We convert these 2-tuples to Extension + # instances as needed. + + if not self.extensions: + return + + # If we were asked to build any C/C++ libraries, make sure that the + # directory where we put them is in the library search path for + # linking extensions. + if self.distribution.has_c_libraries(): + build_clib = self.get_finalized_command('build_clib') + self.libraries.extend(build_clib.get_library_names() or []) + self.library_dirs.append(build_clib.build_clib) + + # Setup the CCompiler object that we'll use to do all the + # compiling and linking + self.compiler = new_compiler(compiler=self.compiler, + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) + customize_compiler(self.compiler) + # If we are cross-compiling, init the compiler now (if we are not + # cross-compiling, init would not hurt, but people may rely on + # late initialization of compiler even if they shouldn't...) + if os.name == 'nt' and self.plat_name != get_platform(): + self.compiler.initialize(self.plat_name) + + # And make sure that any compile/link-related options (which might + # come from the command-line or from the setup script) are set in + # that CCompiler object -- that way, they automatically apply to + # all compiling and linking done here. + if self.include_dirs is not None: + self.compiler.set_include_dirs(self.include_dirs) + if self.define is not None: + # 'define' option is a list of (name,value) tuples + for (name, value) in self.define: + self.compiler.define_macro(name, value) + if self.undef is not None: + for macro in self.undef: + self.compiler.undefine_macro(macro) + if self.libraries is not None: + self.compiler.set_libraries(self.libraries) + if self.library_dirs is not None: + self.compiler.set_library_dirs(self.library_dirs) + if self.rpath is not None: + self.compiler.set_runtime_library_dirs(self.rpath) + if self.link_objects is not None: + self.compiler.set_link_objects(self.link_objects) + + # Now actually compile and link everything. + self.build_extensions() + + def check_extensions_list(self, extensions): + """Ensure that the list of extensions (presumably provided as a + command option 'extensions') is valid, i.e. it is a list of + Extension objects. We also support the old-style list of 2-tuples, + where the tuples are (ext_name, build_info), which are converted to + Extension instances here. + + Raise DistutilsSetupError if the structure is invalid anywhere; + just returns otherwise. + """ + if not isinstance(extensions, list): + raise DistutilsSetupError( + "'ext_modules' option must be a list of Extension instances") + + for i, ext in enumerate(extensions): + if isinstance(ext, Extension): + continue # OK! (assume type-checking done + # by Extension constructor) + + if not isinstance(ext, tuple) or len(ext) != 2: + raise DistutilsSetupError( + "each element of 'ext_modules' option must be an " + "Extension instance or 2-tuple") + + ext_name, build_info = ext + + log.warn("old-style (ext_name, build_info) tuple found in " + "ext_modules for extension '%s' " + "-- please convert to Extension instance", ext_name) + + if not (isinstance(ext_name, str) and + extension_name_re.match(ext_name)): + raise DistutilsSetupError( + "first element of each tuple in 'ext_modules' " + "must be the extension name (a string)") + + if not isinstance(build_info, dict): + raise DistutilsSetupError( + "second element of each tuple in 'ext_modules' " + "must be a dictionary (build info)") + + # OK, the (ext_name, build_info) dict is type-safe: convert it + # to an Extension instance. + ext = Extension(ext_name, build_info['sources']) + + # Easy stuff: one-to-one mapping from dict elements to + # instance attributes. + for key in ('include_dirs', 'library_dirs', 'libraries', + 'extra_objects', 'extra_compile_args', + 'extra_link_args'): + val = build_info.get(key) + if val is not None: + setattr(ext, key, val) + + # Medium-easy stuff: same syntax/semantics, different names. + ext.runtime_library_dirs = build_info.get('rpath') + if 'def_file' in build_info: + log.warn("'def_file' element of build info dict " + "no longer supported") + + # Non-trivial stuff: 'macros' split into 'define_macros' + # and 'undef_macros'. + macros = build_info.get('macros') + if macros: + ext.define_macros = [] + ext.undef_macros = [] + for macro in macros: + if not (isinstance(macro, tuple) and len(macro) in (1, 2)): + raise DistutilsSetupError( + "'macros' element of build info dict " + "must be 1- or 2-tuple") + if len(macro) == 1: + ext.undef_macros.append(macro[0]) + elif len(macro) == 2: + ext.define_macros.append(macro) + + extensions[i] = ext + + def get_source_files(self): + self.check_extensions_list(self.extensions) + filenames = [] + + # Wouldn't it be neat if we knew the names of header files too... + for ext in self.extensions: + filenames.extend(ext.sources) + return filenames + + def get_outputs(self): + # Sanity check the 'extensions' list -- can't assume this is being + # done in the same run as a 'build_extensions()' call (in fact, we + # can probably assume that it *isn't*!). + self.check_extensions_list(self.extensions) + + # And build the list of output (built) filenames. Note that this + # ignores the 'inplace' flag, and assumes everything goes in the + # "build" tree. + outputs = [] + for ext in self.extensions: + outputs.append(self.get_ext_fullpath(ext.name)) + return outputs + + def build_extensions(self): + # First, sanity-check the 'extensions' list + self.check_extensions_list(self.extensions) + if self.parallel: + self._build_extensions_parallel() + else: + self._build_extensions_serial() + + def _build_extensions_parallel(self): + workers = self.parallel + if self.parallel is True: + workers = os.cpu_count() # may return None + try: + from concurrent.futures import ThreadPoolExecutor + except ImportError: + workers = None + + if workers is None: + self._build_extensions_serial() + return + + with ThreadPoolExecutor(max_workers=workers) as executor: + futures = [executor.submit(self.build_extension, ext) + for ext in self.extensions] + for ext, fut in zip(self.extensions, futures): + with self._filter_build_errors(ext): + fut.result() + + def _build_extensions_serial(self): + for ext in self.extensions: + with self._filter_build_errors(ext): + self.build_extension(ext) + + @contextlib.contextmanager + def _filter_build_errors(self, ext): + try: + yield + except (CCompilerError, DistutilsError, CompileError) as e: + if not ext.optional: + raise + self.warn('building extension "%s" failed: %s' % + (ext.name, e)) + + def build_extension(self, ext): + sources = ext.sources + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'ext_modules' option (extension '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % ext.name) + # sort to make the resulting .so file build reproducible + sources = sorted(sources) + + ext_path = self.get_ext_fullpath(ext.name) + depends = sources + ext.depends + if not (self.force or newer_group(depends, ext_path, 'newer')): + log.debug("skipping '%s' extension (up-to-date)", ext.name) + return + else: + log.info("building '%s' extension", ext.name) + + # First, scan the sources for SWIG definition files (.i), run + # SWIG on 'em to create .c files, and modify the sources list + # accordingly. + sources = self.swig_sources(sources, ext) + + # Next, compile the source code to object files. + + # XXX not honouring 'define_macros' or 'undef_macros' -- the + # CCompiler API needs to change to accommodate this, and I + # want to do one thing at a time! + + # Two possible sources for extra compiler arguments: + # - 'extra_compile_args' in Extension object + # - CFLAGS environment variable (not particularly + # elegant, but people seem to expect it and I + # guess it's useful) + # The environment variable should take precedence, and + # any sensible compiler will give precedence to later + # command line args. Hence we combine them in order: + extra_args = ext.extra_compile_args or [] + + macros = ext.define_macros[:] + for undef in ext.undef_macros: + macros.append((undef,)) + + objects = self.compiler.compile(sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=ext.include_dirs, + debug=self.debug, + extra_postargs=extra_args, + depends=ext.depends) + + # XXX outdated variable, kept here in case third-part code + # needs it. + self._built_objects = objects[:] + + # Now link the object files together into a "shared object" -- + # of course, first we have to figure out all the other things + # that go into the mix. + if ext.extra_objects: + objects.extend(ext.extra_objects) + extra_args = ext.extra_link_args or [] + + # Detect target language, if not provided + language = ext.language or self.compiler.detect_language(sources) + + self.compiler.link_shared_object( + objects, ext_path, + libraries=self.get_libraries(ext), + library_dirs=ext.library_dirs, + runtime_library_dirs=ext.runtime_library_dirs, + extra_postargs=extra_args, + export_symbols=self.get_export_symbols(ext), + debug=self.debug, + build_temp=self.build_temp, + target_lang=language) + + def swig_sources(self, sources, extension): + """Walk the list of source files in 'sources', looking for SWIG + interface (.i) files. Run SWIG on all that are found, and + return a modified 'sources' list with SWIG source files replaced + by the generated C (or C++) files. + """ + new_sources = [] + swig_sources = [] + swig_targets = {} + + # XXX this drops generated C/C++ files into the source tree, which + # is fine for developers who want to distribute the generated + # source -- but there should be an option to put SWIG output in + # the temp dir. + + if self.swig_cpp: + log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") + + if self.swig_cpp or ('-c++' in self.swig_opts) or \ + ('-c++' in extension.swig_opts): + target_ext = '.cpp' + else: + target_ext = '.c' + + for source in sources: + (base, ext) = os.path.splitext(source) + if ext == ".i": # SWIG interface file + new_sources.append(base + '_wrap' + target_ext) + swig_sources.append(source) + swig_targets[source] = new_sources[-1] + else: + new_sources.append(source) + + if not swig_sources: + return new_sources + + swig = self.swig or self.find_swig() + swig_cmd = [swig, "-python"] + swig_cmd.extend(self.swig_opts) + if self.swig_cpp: + swig_cmd.append("-c++") + + # Do not override commandline arguments + if not self.swig_opts: + for o in extension.swig_opts: + swig_cmd.append(o) + + for source in swig_sources: + target = swig_targets[source] + log.info("swigging %s to %s", source, target) + self.spawn(swig_cmd + ["-o", target, source]) + + return new_sources + + def find_swig(self): + """Return the name of the SWIG executable. On Unix, this is + just "swig" -- it should be in the PATH. Tries a bit harder on + Windows. + """ + if os.name == "posix": + return "swig" + elif os.name == "nt": + # Look for SWIG in its standard installation directory on + # Windows (or so I presume!). If we find it there, great; + # if not, act like Unix and assume it's in the PATH. + for vers in ("1.3", "1.2", "1.1"): + fn = os.path.join("c:\\swig%s" % vers, "swig.exe") + if os.path.isfile(fn): + return fn + else: + return "swig.exe" + else: + raise DistutilsPlatformError( + "I don't know how to find (much less run) SWIG " + "on platform '%s'" % os.name) + + # -- Name generators ----------------------------------------------- + # (extension names, filenames, whatever) + def get_ext_fullpath(self, ext_name): + """Returns the path of the filename for a given extension. + + The file is located in `build_lib` or directly in the package + (inplace option). + """ + fullname = self.get_ext_fullname(ext_name) + modpath = fullname.split('.') + filename = self.get_ext_filename(modpath[-1]) + + if not self.inplace: + # no further work needed + # returning : + # build_dir/package/path/filename + filename = os.path.join(*modpath[:-1]+[filename]) + return os.path.join(self.build_lib, filename) + + # the inplace option requires to find the package directory + # using the build_py command for that + package = '.'.join(modpath[0:-1]) + build_py = self.get_finalized_command('build_py') + package_dir = os.path.abspath(build_py.get_package_dir(package)) + + # returning + # package_dir/filename + return os.path.join(package_dir, filename) + + def get_ext_fullname(self, ext_name): + """Returns the fullname of a given extension name. + + Adds the `package.` prefix""" + if self.package is None: + return ext_name + else: + return self.package + '.' + ext_name + + def get_ext_filename(self, ext_name): + r"""Convert the name of an extension (eg. "foo.bar") into the name + of the file from which it will be loaded (eg. "foo/bar.so", or + "foo\bar.pyd"). + """ + from distutils.sysconfig import get_config_var + ext_path = ext_name.split('.') + ext_suffix = get_config_var('EXT_SUFFIX') + return os.path.join(*ext_path) + ext_suffix + + def get_export_symbols(self, ext): + """Return the list of symbols that a shared extension has to + export. This either uses 'ext.export_symbols' or, if it's not + provided, "PyInit_" + module_name. Only relevant on Windows, where + the .pyd file (DLL) must export the module "PyInit_" function. + """ + suffix = '_' + ext.name.split('.')[-1] + try: + # Unicode module name support as defined in PEP-489 + # https://www.python.org/dev/peps/pep-0489/#export-hook-name + suffix.encode('ascii') + except UnicodeEncodeError: + suffix = 'U' + suffix.encode('punycode').replace(b'-', b'_').decode('ascii') + + initfunc_name = "PyInit" + suffix + if initfunc_name not in ext.export_symbols: + ext.export_symbols.append(initfunc_name) + return ext.export_symbols + + def get_libraries(self, ext): + """Return the list of libraries to link against when building a + shared extension. On most platforms, this is just 'ext.libraries'; + on Windows, we add the Python library (eg. python20.dll). + """ + # The python library is always needed on Windows. For MSVC, this + # is redundant, since the library is mentioned in a pragma in + # pyconfig.h that MSVC groks. The other Windows compilers all seem + # to need it mentioned explicitly, though, so that's what we do. + # Append '_d' to the python import library on debug builds. + if sys.platform == "win32": + from distutils._msvccompiler import MSVCCompiler + if not isinstance(self.compiler, MSVCCompiler): + template = "python%d%d" + if self.debug: + template = template + '_d' + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + # don't extend ext.libraries, it may be shared with other + # extensions, it is a reference to the original list + return ext.libraries + [pythonlib] + else: + # On Android only the main executable and LD_PRELOADs are considered + # to be RTLD_GLOBAL, all the dependencies of the main executable + # remain RTLD_LOCAL and so the shared libraries must be linked with + # libpython when python is built with a shared python library (issue + # bpo-21536). + # On Cygwin (and if required, other POSIX-like platforms based on + # Windows like MinGW) it is simply necessary that all symbols in + # shared libraries are resolved at link time. + from distutils.sysconfig import get_config_var + link_libpython = False + if get_config_var('Py_ENABLE_SHARED'): + # A native build on an Android device or on Cygwin + if hasattr(sys, 'getandroidapilevel'): + link_libpython = True + elif sys.platform == 'cygwin': + link_libpython = True + elif '_PYTHON_HOST_PLATFORM' in os.environ: + # We are cross-compiling for one of the relevant platforms + if get_config_var('ANDROID_API_LEVEL') != 0: + link_libpython = True + elif get_config_var('MACHDEP') == 'cygwin': + link_libpython = True + + if link_libpython: + ldversion = get_config_var('LDVERSION') + return ext.libraries + ['python' + ldversion] + + return ext.libraries diff --git a/setuptools/_distutils/command/build_py.py b/setuptools/_distutils/command/build_py.py new file mode 100644 index 00000000..cf0ca57c --- /dev/null +++ b/setuptools/_distutils/command/build_py.py @@ -0,0 +1,416 @@ +"""distutils.command.build_py + +Implements the Distutils 'build_py' command.""" + +import os +import importlib.util +import sys +from glob import glob + +from distutils.core import Command +from distutils.errors import * +from distutils.util import convert_path, Mixin2to3 +from distutils import log + +class build_py (Command): + + description = "\"build\" pure Python modules (copy to build directory)" + + user_options = [ + ('build-lib=', 'd', "directory to \"build\" (copy) to"), + ('compile', 'c', "compile .py to .pyc"), + ('no-compile', None, "don't compile .py files [default]"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + ('force', 'f', "forcibly build everything (ignore file timestamps)"), + ] + + boolean_options = ['compile', 'force'] + negative_opt = {'no-compile' : 'compile'} + + def initialize_options(self): + self.build_lib = None + self.py_modules = None + self.package = None + self.package_data = None + self.package_dir = None + self.compile = 0 + self.optimize = 0 + self.force = None + + def finalize_options(self): + self.set_undefined_options('build', + ('build_lib', 'build_lib'), + ('force', 'force')) + + # Get the distribution options that are aliases for build_py + # options -- list of packages and list of modules. + self.packages = self.distribution.packages + self.py_modules = self.distribution.py_modules + self.package_data = self.distribution.package_data + self.package_dir = {} + if self.distribution.package_dir: + for name, path in self.distribution.package_dir.items(): + self.package_dir[name] = convert_path(path) + self.data_files = self.get_data_files() + + # Ick, copied straight from install_lib.py (fancy_getopt needs a + # type system! Hell, *everything* needs a type system!!!) + if not isinstance(self.optimize, int): + try: + self.optimize = int(self.optimize) + assert 0 <= self.optimize <= 2 + except (ValueError, AssertionError): + raise DistutilsOptionError("optimize must be 0, 1, or 2") + + def run(self): + # XXX copy_file by default preserves atime and mtime. IMHO this is + # the right thing to do, but perhaps it should be an option -- in + # particular, a site administrator might want installed files to + # reflect the time of installation rather than the last + # modification time before the installed release. + + # XXX copy_file by default preserves mode, which appears to be the + # wrong thing to do: if a file is read-only in the working + # directory, we want it to be installed read/write so that the next + # installation of the same module distribution can overwrite it + # without problems. (This might be a Unix-specific issue.) Thus + # we turn off 'preserve_mode' when copying to the build directory, + # since the build directory is supposed to be exactly what the + # installation will look like (ie. we preserve mode when + # installing). + + # Two options control which modules will be installed: 'packages' + # and 'py_modules'. The former lets us work with whole packages, not + # specifying individual modules at all; the latter is for + # specifying modules one-at-a-time. + + if self.py_modules: + self.build_modules() + if self.packages: + self.build_packages() + self.build_package_data() + + self.byte_compile(self.get_outputs(include_bytecode=0)) + + def get_data_files(self): + """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" + data = [] + if not self.packages: + return data + for package in self.packages: + # Locate package source directory + src_dir = self.get_package_dir(package) + + # Compute package build directory + build_dir = os.path.join(*([self.build_lib] + package.split('.'))) + + # Length of path to strip from found files + plen = 0 + if src_dir: + plen = len(src_dir)+1 + + # Strip directory from globbed filenames + filenames = [ + file[plen:] for file in self.find_data_files(package, src_dir) + ] + data.append((package, src_dir, build_dir, filenames)) + return data + + def find_data_files(self, package, src_dir): + """Return filenames for package's data files in 'src_dir'""" + globs = (self.package_data.get('', []) + + self.package_data.get(package, [])) + files = [] + for pattern in globs: + # Each pattern has to be converted to a platform-specific path + filelist = glob(os.path.join(src_dir, convert_path(pattern))) + # Files that match more than one pattern are only added once + files.extend([fn for fn in filelist if fn not in files + and os.path.isfile(fn)]) + return files + + def build_package_data(self): + """Copy data files into build directory""" + lastdir = None + for package, src_dir, build_dir, filenames in self.data_files: + for filename in filenames: + target = os.path.join(build_dir, filename) + self.mkpath(os.path.dirname(target)) + self.copy_file(os.path.join(src_dir, filename), target, + preserve_mode=False) + + def get_package_dir(self, package): + """Return the directory, relative to the top of the source + distribution, where package 'package' should be found + (at least according to the 'package_dir' option, if any).""" + path = package.split('.') + + if not self.package_dir: + if path: + return os.path.join(*path) + else: + return '' + else: + tail = [] + while path: + try: + pdir = self.package_dir['.'.join(path)] + except KeyError: + tail.insert(0, path[-1]) + del path[-1] + else: + tail.insert(0, pdir) + return os.path.join(*tail) + else: + # Oops, got all the way through 'path' without finding a + # match in package_dir. If package_dir defines a directory + # for the root (nameless) package, then fallback on it; + # otherwise, we might as well have not consulted + # package_dir at all, as we just use the directory implied + # by 'tail' (which should be the same as the original value + # of 'path' at this point). + pdir = self.package_dir.get('') + if pdir is not None: + tail.insert(0, pdir) + + if tail: + return os.path.join(*tail) + else: + return '' + + def check_package(self, package, package_dir): + # Empty dir name means current directory, which we can probably + # assume exists. Also, os.path.exists and isdir don't know about + # my "empty string means current dir" convention, so we have to + # circumvent them. + if package_dir != "": + if not os.path.exists(package_dir): + raise DistutilsFileError( + "package directory '%s' does not exist" % package_dir) + if not os.path.isdir(package_dir): + raise DistutilsFileError( + "supposed package directory '%s' exists, " + "but is not a directory" % package_dir) + + # Require __init__.py for all but the "root package" + if package: + init_py = os.path.join(package_dir, "__init__.py") + if os.path.isfile(init_py): + return init_py + else: + log.warn(("package init file '%s' not found " + + "(or not a regular file)"), init_py) + + # Either not in a package at all (__init__.py not expected), or + # __init__.py doesn't exist -- so don't return the filename. + return None + + def check_module(self, module, module_file): + if not os.path.isfile(module_file): + log.warn("file %s (for module %s) not found", module_file, module) + return False + else: + return True + + def find_package_modules(self, package, package_dir): + self.check_package(package, package_dir) + module_files = glob(os.path.join(package_dir, "*.py")) + modules = [] + setup_script = os.path.abspath(self.distribution.script_name) + + for f in module_files: + abs_f = os.path.abspath(f) + if abs_f != setup_script: + module = os.path.splitext(os.path.basename(f))[0] + modules.append((package, module, f)) + else: + self.debug_print("excluding %s" % setup_script) + return modules + + def find_modules(self): + """Finds individually-specified Python modules, ie. those listed by + module name in 'self.py_modules'. Returns a list of tuples (package, + module_base, filename): 'package' is a tuple of the path through + package-space to the module; 'module_base' is the bare (no + packages, no dots) module name, and 'filename' is the path to the + ".py" file (relative to the distribution root) that implements the + module. + """ + # Map package names to tuples of useful info about the package: + # (package_dir, checked) + # package_dir - the directory where we'll find source files for + # this package + # checked - true if we have checked that the package directory + # is valid (exists, contains __init__.py, ... ?) + packages = {} + + # List of (package, module, filename) tuples to return + modules = [] + + # We treat modules-in-packages almost the same as toplevel modules, + # just the "package" for a toplevel is empty (either an empty + # string or empty list, depending on context). Differences: + # - don't check for __init__.py in directory for empty package + for module in self.py_modules: + path = module.split('.') + package = '.'.join(path[0:-1]) + module_base = path[-1] + + try: + (package_dir, checked) = packages[package] + except KeyError: + package_dir = self.get_package_dir(package) + checked = 0 + + if not checked: + init_py = self.check_package(package, package_dir) + packages[package] = (package_dir, 1) + if init_py: + modules.append((package, "__init__", init_py)) + + # XXX perhaps we should also check for just .pyc files + # (so greedy closed-source bastards can distribute Python + # modules too) + module_file = os.path.join(package_dir, module_base + ".py") + if not self.check_module(module, module_file): + continue + + modules.append((package, module_base, module_file)) + + return modules + + def find_all_modules(self): + """Compute the list of all modules that will be built, whether + they are specified one-module-at-a-time ('self.py_modules') or + by whole packages ('self.packages'). Return a list of tuples + (package, module, module_file), just like 'find_modules()' and + 'find_package_modules()' do.""" + modules = [] + if self.py_modules: + modules.extend(self.find_modules()) + if self.packages: + for package in self.packages: + package_dir = self.get_package_dir(package) + m = self.find_package_modules(package, package_dir) + modules.extend(m) + return modules + + def get_source_files(self): + return [module[-1] for module in self.find_all_modules()] + + def get_module_outfile(self, build_dir, package, module): + outfile_path = [build_dir] + list(package) + [module + ".py"] + return os.path.join(*outfile_path) + + def get_outputs(self, include_bytecode=1): + modules = self.find_all_modules() + outputs = [] + for (package, module, module_file) in modules: + package = package.split('.') + filename = self.get_module_outfile(self.build_lib, package, module) + outputs.append(filename) + if include_bytecode: + if self.compile: + outputs.append(importlib.util.cache_from_source( + filename, optimization='')) + if self.optimize > 0: + outputs.append(importlib.util.cache_from_source( + filename, optimization=self.optimize)) + + outputs += [ + os.path.join(build_dir, filename) + for package, src_dir, build_dir, filenames in self.data_files + for filename in filenames + ] + + return outputs + + def build_module(self, module, module_file, package): + if isinstance(package, str): + package = package.split('.') + elif not isinstance(package, (list, tuple)): + raise TypeError( + "'package' must be a string (dot-separated), list, or tuple") + + # Now put the module source file into the "build" area -- this is + # easy, we just copy it somewhere under self.build_lib (the build + # directory for Python source). + outfile = self.get_module_outfile(self.build_lib, package, module) + dir = os.path.dirname(outfile) + self.mkpath(dir) + return self.copy_file(module_file, outfile, preserve_mode=0) + + def build_modules(self): + modules = self.find_modules() + for (package, module, module_file) in modules: + # Now "build" the module -- ie. copy the source file to + # self.build_lib (the build directory for Python source). + # (Actually, it gets copied to the directory for this package + # under self.build_lib.) + self.build_module(module, module_file, package) + + def build_packages(self): + for package in self.packages: + # Get list of (package, module, module_file) tuples based on + # scanning the package directory. 'package' is only included + # in the tuple so that 'find_modules()' and + # 'find_package_tuples()' have a consistent interface; it's + # ignored here (apart from a sanity check). Also, 'module' is + # the *unqualified* module name (ie. no dots, no package -- we + # already know its package!), and 'module_file' is the path to + # the .py file, relative to the current directory + # (ie. including 'package_dir'). + package_dir = self.get_package_dir(package) + modules = self.find_package_modules(package, package_dir) + + # Now loop over the modules we found, "building" each one (just + # copy it to self.build_lib). + for (package_, module, module_file) in modules: + assert package == package_ + self.build_module(module, module_file, package) + + def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + + from distutils.util import byte_compile + prefix = self.build_lib + if prefix[-1] != os.sep: + prefix = prefix + os.sep + + # XXX this code is essentially the same as the 'byte_compile() + # method of the "install_lib" command, except for the determination + # of the 'prefix' string. Hmmm. + if self.compile: + byte_compile(files, optimize=0, + force=self.force, prefix=prefix, dry_run=self.dry_run) + if self.optimize > 0: + byte_compile(files, optimize=self.optimize, + force=self.force, prefix=prefix, dry_run=self.dry_run) + +class build_py_2to3(build_py, Mixin2to3): + def run(self): + self.updated_files = [] + + # Base class code + if self.py_modules: + self.build_modules() + if self.packages: + self.build_packages() + self.build_package_data() + + # 2to3 + self.run_2to3(self.updated_files) + + # Remaining base class code + self.byte_compile(self.get_outputs(include_bytecode=0)) + + def build_module(self, module, module_file, package): + res = build_py.build_module(self, module, module_file, package) + if res[1]: + # file was copied + self.updated_files.append(res[0]) + return res diff --git a/setuptools/_distutils/command/build_scripts.py b/setuptools/_distutils/command/build_scripts.py new file mode 100644 index 00000000..ccc70e64 --- /dev/null +++ b/setuptools/_distutils/command/build_scripts.py @@ -0,0 +1,160 @@ +"""distutils.command.build_scripts + +Implements the Distutils 'build_scripts' command.""" + +import os, re +from stat import ST_MODE +from distutils import sysconfig +from distutils.core import Command +from distutils.dep_util import newer +from distutils.util import convert_path, Mixin2to3 +from distutils import log +import tokenize + +# check if Python is called on the first line with this expression +first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$') + +class build_scripts(Command): + + description = "\"build\" scripts (copy and fixup #! line)" + + user_options = [ + ('build-dir=', 'd', "directory to \"build\" (copy) to"), + ('force', 'f', "forcibly build everything (ignore file timestamps"), + ('executable=', 'e', "specify final destination interpreter path"), + ] + + boolean_options = ['force'] + + + def initialize_options(self): + self.build_dir = None + self.scripts = None + self.force = None + self.executable = None + self.outfiles = None + + def finalize_options(self): + self.set_undefined_options('build', + ('build_scripts', 'build_dir'), + ('force', 'force'), + ('executable', 'executable')) + self.scripts = self.distribution.scripts + + def get_source_files(self): + return self.scripts + + def run(self): + if not self.scripts: + return + self.copy_scripts() + + + def copy_scripts(self): + r"""Copy each script listed in 'self.scripts'; if it's marked as a + Python script in the Unix way (first line matches 'first_line_re', + ie. starts with "\#!" and contains "python"), then adjust the first + line to refer to the current Python interpreter as we copy. + """ + self.mkpath(self.build_dir) + outfiles = [] + updated_files = [] + for script in self.scripts: + adjust = False + script = convert_path(script) + outfile = os.path.join(self.build_dir, os.path.basename(script)) + outfiles.append(outfile) + + if not self.force and not newer(script, outfile): + log.debug("not copying %s (up-to-date)", script) + continue + + # Always open the file, but ignore failures in dry-run mode -- + # that way, we'll get accurate feedback if we can read the + # script. + try: + f = open(script, "rb") + except OSError: + if not self.dry_run: + raise + f = None + else: + encoding, lines = tokenize.detect_encoding(f.readline) + f.seek(0) + first_line = f.readline() + if not first_line: + self.warn("%s is an empty file (skipping)" % script) + continue + + match = first_line_re.match(first_line) + if match: + adjust = True + post_interp = match.group(1) or b'' + + if adjust: + log.info("copying and adjusting %s -> %s", script, + self.build_dir) + updated_files.append(outfile) + if not self.dry_run: + if not sysconfig.python_build: + executable = self.executable + else: + executable = os.path.join( + sysconfig.get_config_var("BINDIR"), + "python%s%s" % (sysconfig.get_config_var("VERSION"), + sysconfig.get_config_var("EXE"))) + executable = os.fsencode(executable) + shebang = b"#!" + executable + post_interp + b"\n" + # Python parser starts to read a script using UTF-8 until + # it gets a #coding:xxx cookie. The shebang has to be the + # first line of a file, the #coding:xxx cookie cannot be + # written before. So the shebang has to be decodable from + # UTF-8. + try: + shebang.decode('utf-8') + except UnicodeDecodeError: + raise ValueError( + "The shebang ({!r}) is not decodable " + "from utf-8".format(shebang)) + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be decodable from + # the script encoding too. + try: + shebang.decode(encoding) + except UnicodeDecodeError: + raise ValueError( + "The shebang ({!r}) is not decodable " + "from the script encoding ({})" + .format(shebang, encoding)) + with open(outfile, "wb") as outf: + outf.write(shebang) + outf.writelines(f.readlines()) + if f: + f.close() + else: + if f: + f.close() + updated_files.append(outfile) + self.copy_file(script, outfile) + + if os.name == 'posix': + for file in outfiles: + if self.dry_run: + log.info("changing mode of %s", file) + else: + oldmode = os.stat(file)[ST_MODE] & 0o7777 + newmode = (oldmode | 0o555) & 0o7777 + if newmode != oldmode: + log.info("changing mode of %s from %o to %o", + file, oldmode, newmode) + os.chmod(file, newmode) + # XXX should we modify self.outfiles? + return outfiles, updated_files + +class build_scripts_2to3(build_scripts, Mixin2to3): + + def copy_scripts(self): + outfiles, updated_files = build_scripts.copy_scripts(self) + if not self.dry_run: + self.run_2to3(updated_files) + return outfiles, updated_files diff --git a/setuptools/_distutils/command/check.py b/setuptools/_distutils/command/check.py new file mode 100644 index 00000000..ada25006 --- /dev/null +++ b/setuptools/_distutils/command/check.py @@ -0,0 +1,148 @@ +"""distutils.command.check + +Implements the Distutils 'check' command. +""" +from distutils.core import Command +from distutils.errors import DistutilsSetupError + +try: + # docutils is installed + from docutils.utils import Reporter + from docutils.parsers.rst import Parser + from docutils import frontend + from docutils import nodes + + class SilentReporter(Reporter): + + def __init__(self, source, report_level, halt_level, stream=None, + debug=0, encoding='ascii', error_handler='replace'): + self.messages = [] + Reporter.__init__(self, source, report_level, halt_level, stream, + debug, encoding, error_handler) + + def system_message(self, level, message, *children, **kwargs): + self.messages.append((level, message, children, kwargs)) + return nodes.system_message(message, level=level, + type=self.levels[level], + *children, **kwargs) + + HAS_DOCUTILS = True +except Exception: + # Catch all exceptions because exceptions besides ImportError probably + # indicate that docutils is not ported to Py3k. + HAS_DOCUTILS = False + +class check(Command): + """This command checks the meta-data of the package. + """ + description = ("perform some checks on the package") + user_options = [('metadata', 'm', 'Verify meta-data'), + ('restructuredtext', 'r', + ('Checks if long string meta-data syntax ' + 'are reStructuredText-compliant')), + ('strict', 's', + 'Will exit with an error if a check fails')] + + boolean_options = ['metadata', 'restructuredtext', 'strict'] + + def initialize_options(self): + """Sets default values for options.""" + self.restructuredtext = 0 + self.metadata = 1 + self.strict = 0 + self._warnings = 0 + + def finalize_options(self): + pass + + def warn(self, msg): + """Counts the number of warnings that occurs.""" + self._warnings += 1 + return Command.warn(self, msg) + + def run(self): + """Runs the command.""" + # perform the various tests + if self.metadata: + self.check_metadata() + if self.restructuredtext: + if HAS_DOCUTILS: + self.check_restructuredtext() + elif self.strict: + raise DistutilsSetupError('The docutils package is needed.') + + # let's raise an error in strict mode, if we have at least + # one warning + if self.strict and self._warnings > 0: + raise DistutilsSetupError('Please correct your package.') + + def check_metadata(self): + """Ensures that all required elements of meta-data are supplied. + + Required fields: + name, version, URL + + Recommended fields: + (author and author_email) or (maintainer and maintainer_email)) + + Warns if any are missing. + """ + metadata = self.distribution.metadata + + missing = [] + for attr in ('name', 'version', 'url'): + if not (hasattr(metadata, attr) and getattr(metadata, attr)): + missing.append(attr) + + if missing: + self.warn("missing required meta-data: %s" % ', '.join(missing)) + if metadata.author: + if not metadata.author_email: + self.warn("missing meta-data: if 'author' supplied, " + + "'author_email' should be supplied too") + elif metadata.maintainer: + if not metadata.maintainer_email: + self.warn("missing meta-data: if 'maintainer' supplied, " + + "'maintainer_email' should be supplied too") + else: + self.warn("missing meta-data: either (author and author_email) " + + "or (maintainer and maintainer_email) " + + "should be supplied") + + def check_restructuredtext(self): + """Checks if the long string fields are reST-compliant.""" + data = self.distribution.get_long_description() + for warning in self._check_rst_data(data): + line = warning[-1].get('line') + if line is None: + warning = warning[1] + else: + warning = '%s (line %s)' % (warning[1], line) + self.warn(warning) + + def _check_rst_data(self, data): + """Returns warnings when the provided data doesn't compile.""" + # the include and csv_table directives need this to be a path + source_path = self.distribution.script_name or 'setup.py' + parser = Parser() + settings = frontend.OptionParser(components=(Parser,)).get_default_values() + settings.tab_width = 4 + settings.pep_references = None + settings.rfc_references = None + reporter = SilentReporter(source_path, + settings.report_level, + settings.halt_level, + stream=settings.warning_stream, + debug=settings.debug, + encoding=settings.error_encoding, + error_handler=settings.error_encoding_error_handler) + + document = nodes.document(settings, reporter, source=source_path) + document.note_source(source_path, -1) + try: + parser.parse(data, document) + except AttributeError as e: + reporter.messages.append( + (-1, 'Could not finish the parsing: %s.' % e, '', {})) + + return reporter.messages diff --git a/setuptools/_distutils/command/clean.py b/setuptools/_distutils/command/clean.py new file mode 100644 index 00000000..0cb27016 --- /dev/null +++ b/setuptools/_distutils/command/clean.py @@ -0,0 +1,76 @@ +"""distutils.command.clean + +Implements the Distutils 'clean' command.""" + +# contributed by Bastian Kleineidam , added 2000-03-18 + +import os +from distutils.core import Command +from distutils.dir_util import remove_tree +from distutils import log + +class clean(Command): + + description = "clean up temporary files from 'build' command" + user_options = [ + ('build-base=', 'b', + "base build directory (default: 'build.build-base')"), + ('build-lib=', None, + "build directory for all modules (default: 'build.build-lib')"), + ('build-temp=', 't', + "temporary build directory (default: 'build.build-temp')"), + ('build-scripts=', None, + "build directory for scripts (default: 'build.build-scripts')"), + ('bdist-base=', None, + "temporary directory for built distributions"), + ('all', 'a', + "remove all build output, not just temporary by-products") + ] + + boolean_options = ['all'] + + def initialize_options(self): + self.build_base = None + self.build_lib = None + self.build_temp = None + self.build_scripts = None + self.bdist_base = None + self.all = None + + def finalize_options(self): + self.set_undefined_options('build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib'), + ('build_scripts', 'build_scripts'), + ('build_temp', 'build_temp')) + self.set_undefined_options('bdist', + ('bdist_base', 'bdist_base')) + + def run(self): + # remove the build/temp. directory (unless it's already + # gone) + if os.path.exists(self.build_temp): + remove_tree(self.build_temp, dry_run=self.dry_run) + else: + log.debug("'%s' does not exist -- can't clean it", + self.build_temp) + + if self.all: + # remove build directories + for directory in (self.build_lib, + self.bdist_base, + self.build_scripts): + if os.path.exists(directory): + remove_tree(directory, dry_run=self.dry_run) + else: + log.warn("'%s' does not exist -- can't clean it", + directory) + + # just for the heck of it, try to remove the base build directory: + # we might have emptied it right now, but if not we don't care + if not self.dry_run: + try: + os.rmdir(self.build_base) + log.info("removing '%s'", self.build_base) + except OSError: + pass diff --git a/setuptools/_distutils/command/command_template b/setuptools/_distutils/command/command_template new file mode 100644 index 00000000..6106819d --- /dev/null +++ b/setuptools/_distutils/command/command_template @@ -0,0 +1,33 @@ +"""distutils.command.x + +Implements the Distutils 'x' command. +""" + +# created 2000/mm/dd, John Doe + +__revision__ = "$Id$" + +from distutils.core import Command + + +class x(Command): + + # Brief (40-50 characters) description of the command + description = "" + + # List of option tuples: long name, short name (None if no short + # name), and help string. + user_options = [('', '', + ""), + ] + + def initialize_options(self): + self. = None + self. = None + self. = None + + def finalize_options(self): + if self.x is None: + self.x = + + def run(self): diff --git a/setuptools/_distutils/command/config.py b/setuptools/_distutils/command/config.py new file mode 100644 index 00000000..aeda408e --- /dev/null +++ b/setuptools/_distutils/command/config.py @@ -0,0 +1,344 @@ +"""distutils.command.config + +Implements the Distutils 'config' command, a (mostly) empty command class +that exists mainly to be sub-classed by specific module distributions and +applications. The idea is that while every "config" command is different, +at least they're all named the same, and users always see "config" in the +list of standard commands. Also, this is a good place to put common +configure-like tasks: "try to compile this C code", or "figure out where +this header file lives". +""" + +import os, re + +from distutils.core import Command +from distutils.errors import DistutilsExecError +from distutils.sysconfig import customize_compiler +from distutils import log + +LANG_EXT = {"c": ".c", "c++": ".cxx"} + +class config(Command): + + description = "prepare to build" + + user_options = [ + ('compiler=', None, + "specify the compiler type"), + ('cc=', None, + "specify the compiler executable"), + ('include-dirs=', 'I', + "list of directories to search for header files"), + ('define=', 'D', + "C preprocessor macros to define"), + ('undef=', 'U', + "C preprocessor macros to undefine"), + ('libraries=', 'l', + "external C libraries to link with"), + ('library-dirs=', 'L', + "directories to search for external C libraries"), + + ('noisy', None, + "show every action (compile, link, run, ...) taken"), + ('dump-source', None, + "dump generated source files before attempting to compile them"), + ] + + + # The three standard command methods: since the "config" command + # does nothing by default, these are empty. + + def initialize_options(self): + self.compiler = None + self.cc = None + self.include_dirs = None + self.libraries = None + self.library_dirs = None + + # maximal output for now + self.noisy = 1 + self.dump_source = 1 + + # list of temporary files generated along-the-way that we have + # to clean at some point + self.temp_files = [] + + def finalize_options(self): + if self.include_dirs is None: + self.include_dirs = self.distribution.include_dirs or [] + elif isinstance(self.include_dirs, str): + self.include_dirs = self.include_dirs.split(os.pathsep) + + if self.libraries is None: + self.libraries = [] + elif isinstance(self.libraries, str): + self.libraries = [self.libraries] + + if self.library_dirs is None: + self.library_dirs = [] + elif isinstance(self.library_dirs, str): + self.library_dirs = self.library_dirs.split(os.pathsep) + + def run(self): + pass + + # Utility methods for actual "config" commands. The interfaces are + # loosely based on Autoconf macros of similar names. Sub-classes + # may use these freely. + + def _check_compiler(self): + """Check that 'self.compiler' really is a CCompiler object; + if not, make it one. + """ + # We do this late, and only on-demand, because this is an expensive + # import. + from distutils.ccompiler import CCompiler, new_compiler + if not isinstance(self.compiler, CCompiler): + self.compiler = new_compiler(compiler=self.compiler, + dry_run=self.dry_run, force=1) + customize_compiler(self.compiler) + if self.include_dirs: + self.compiler.set_include_dirs(self.include_dirs) + if self.libraries: + self.compiler.set_libraries(self.libraries) + if self.library_dirs: + self.compiler.set_library_dirs(self.library_dirs) + + def _gen_temp_sourcefile(self, body, headers, lang): + filename = "_configtest" + LANG_EXT[lang] + with open(filename, "w") as file: + if headers: + for header in headers: + file.write("#include <%s>\n" % header) + file.write("\n") + file.write(body) + if body[-1] != "\n": + file.write("\n") + return filename + + def _preprocess(self, body, headers, include_dirs, lang): + src = self._gen_temp_sourcefile(body, headers, lang) + out = "_configtest.i" + self.temp_files.extend([src, out]) + self.compiler.preprocess(src, out, include_dirs=include_dirs) + return (src, out) + + def _compile(self, body, headers, include_dirs, lang): + src = self._gen_temp_sourcefile(body, headers, lang) + if self.dump_source: + dump_file(src, "compiling '%s':" % src) + (obj,) = self.compiler.object_filenames([src]) + self.temp_files.extend([src, obj]) + self.compiler.compile([src], include_dirs=include_dirs) + return (src, obj) + + def _link(self, body, headers, include_dirs, libraries, library_dirs, + lang): + (src, obj) = self._compile(body, headers, include_dirs, lang) + prog = os.path.splitext(os.path.basename(src))[0] + self.compiler.link_executable([obj], prog, + libraries=libraries, + library_dirs=library_dirs, + target_lang=lang) + + if self.compiler.exe_extension is not None: + prog = prog + self.compiler.exe_extension + self.temp_files.append(prog) + + return (src, obj, prog) + + def _clean(self, *filenames): + if not filenames: + filenames = self.temp_files + self.temp_files = [] + log.info("removing: %s", ' '.join(filenames)) + for filename in filenames: + try: + os.remove(filename) + except OSError: + pass + + + # XXX these ignore the dry-run flag: what to do, what to do? even if + # you want a dry-run build, you still need some sort of configuration + # info. My inclination is to make it up to the real config command to + # consult 'dry_run', and assume a default (minimal) configuration if + # true. The problem with trying to do it here is that you'd have to + # return either true or false from all the 'try' methods, neither of + # which is correct. + + # XXX need access to the header search path and maybe default macros. + + def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): + """Construct a source file from 'body' (a string containing lines + of C/C++ code) and 'headers' (a list of header files to include) + and run it through the preprocessor. Return true if the + preprocessor succeeded, false if there were any errors. + ('body' probably isn't of much use, but what the heck.) + """ + from distutils.ccompiler import CompileError + self._check_compiler() + ok = True + try: + self._preprocess(body, headers, include_dirs, lang) + except CompileError: + ok = False + + self._clean() + return ok + + def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, + lang="c"): + """Construct a source file (just like 'try_cpp()'), run it through + the preprocessor, and return true if any line of the output matches + 'pattern'. 'pattern' should either be a compiled regex object or a + string containing a regex. If both 'body' and 'headers' are None, + preprocesses an empty file -- which can be useful to determine the + symbols the preprocessor and compiler set by default. + """ + self._check_compiler() + src, out = self._preprocess(body, headers, include_dirs, lang) + + if isinstance(pattern, str): + pattern = re.compile(pattern) + + with open(out) as file: + match = False + while True: + line = file.readline() + if line == '': + break + if pattern.search(line): + match = True + break + + self._clean() + return match + + def try_compile(self, body, headers=None, include_dirs=None, lang="c"): + """Try to compile a source file built from 'body' and 'headers'. + Return true on success, false otherwise. + """ + from distutils.ccompiler import CompileError + self._check_compiler() + try: + self._compile(body, headers, include_dirs, lang) + ok = True + except CompileError: + ok = False + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + def try_link(self, body, headers=None, include_dirs=None, libraries=None, + library_dirs=None, lang="c"): + """Try to compile and link a source file, built from 'body' and + 'headers', to executable form. Return true on success, false + otherwise. + """ + from distutils.ccompiler import CompileError, LinkError + self._check_compiler() + try: + self._link(body, headers, include_dirs, + libraries, library_dirs, lang) + ok = True + except (CompileError, LinkError): + ok = False + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + def try_run(self, body, headers=None, include_dirs=None, libraries=None, + library_dirs=None, lang="c"): + """Try to compile, link to an executable, and run a program + built from 'body' and 'headers'. Return true on success, false + otherwise. + """ + from distutils.ccompiler import CompileError, LinkError + self._check_compiler() + try: + src, obj, exe = self._link(body, headers, include_dirs, + libraries, library_dirs, lang) + self.spawn([exe]) + ok = True + except (CompileError, LinkError, DistutilsExecError): + ok = False + + log.info(ok and "success!" or "failure.") + self._clean() + return ok + + + # -- High-level methods -------------------------------------------- + # (these are the ones that are actually likely to be useful + # when implementing a real-world config command!) + + def check_func(self, func, headers=None, include_dirs=None, + libraries=None, library_dirs=None, decl=0, call=0): + """Determine if function 'func' is available by constructing a + source file that refers to 'func', and compiles and links it. + If everything succeeds, returns true; otherwise returns false. + + The constructed source file starts out by including the header + files listed in 'headers'. If 'decl' is true, it then declares + 'func' (as "int func()"); you probably shouldn't supply 'headers' + and set 'decl' true in the same call, or you might get errors about + a conflicting declarations for 'func'. Finally, the constructed + 'main()' function either references 'func' or (if 'call' is true) + calls it. 'libraries' and 'library_dirs' are used when + linking. + """ + self._check_compiler() + body = [] + if decl: + body.append("int %s ();" % func) + body.append("int main () {") + if call: + body.append(" %s();" % func) + else: + body.append(" %s;" % func) + body.append("}") + body = "\n".join(body) + "\n" + + return self.try_link(body, headers, include_dirs, + libraries, library_dirs) + + def check_lib(self, library, library_dirs=None, headers=None, + include_dirs=None, other_libraries=[]): + """Determine if 'library' is available to be linked against, + without actually checking that any particular symbols are provided + by it. 'headers' will be used in constructing the source file to + be compiled, but the only effect of this is to check if all the + header files listed are available. Any libraries listed in + 'other_libraries' will be included in the link, in case 'library' + has symbols that depend on other libraries. + """ + self._check_compiler() + return self.try_link("int main (void) { }", headers, include_dirs, + [library] + other_libraries, library_dirs) + + def check_header(self, header, include_dirs=None, library_dirs=None, + lang="c"): + """Determine if the system header file named by 'header_file' + exists and can be found by the preprocessor; return true if so, + false otherwise. + """ + return self.try_cpp(body="/* No body */", headers=[header], + include_dirs=include_dirs) + +def dump_file(filename, head=None): + """Dumps a file content into log.info. + + If head is not None, will be dumped before the file content. + """ + if head is None: + log.info('%s', filename) + else: + log.info(head) + file = open(filename) + try: + log.info(file.read()) + finally: + file.close() diff --git a/setuptools/_distutils/command/install.py b/setuptools/_distutils/command/install.py new file mode 100644 index 00000000..13feeb89 --- /dev/null +++ b/setuptools/_distutils/command/install.py @@ -0,0 +1,677 @@ +"""distutils.command.install + +Implements the Distutils 'install' command.""" + +import sys +import os + +from distutils import log +from distutils.core import Command +from distutils.debug import DEBUG +from distutils.sysconfig import get_config_vars +from distutils.errors import DistutilsPlatformError +from distutils.file_util import write_file +from distutils.util import convert_path, subst_vars, change_root +from distutils.util import get_platform +from distutils.errors import DistutilsOptionError + +from site import USER_BASE +from site import USER_SITE +HAS_USER_SITE = True + +WINDOWS_SCHEME = { + 'purelib': '$base/Lib/site-packages', + 'platlib': '$base/Lib/site-packages', + 'headers': '$base/Include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', +} + +INSTALL_SCHEMES = { + 'unix_prefix': { + 'purelib': '$base/lib/python$py_version_short/site-packages', + 'platlib': '$platbase/$platlibdir/python$py_version_short/site-packages', + 'headers': '$base/include/python$py_version_short$abiflags/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'unix_home': { + 'purelib': '$base/lib/python', + 'platlib': '$base/$platlibdir/python', + 'headers': '$base/include/python/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'nt': WINDOWS_SCHEME, + 'pypy': { + 'purelib': '$base/site-packages', + 'platlib': '$base/site-packages', + 'headers': '$base/include/$dist_name', + 'scripts': '$base/bin', + 'data' : '$base', + }, + 'pypy_nt': { + 'purelib': '$base/site-packages', + 'platlib': '$base/site-packages', + 'headers': '$base/include/$dist_name', + 'scripts': '$base/Scripts', + 'data' : '$base', + }, + } + +# user site schemes +if HAS_USER_SITE: + INSTALL_SCHEMES['nt_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', + 'scripts': '$userbase/Python$py_version_nodot/Scripts', + 'data' : '$userbase', + } + + INSTALL_SCHEMES['unix_user'] = { + 'purelib': '$usersite', + 'platlib': '$usersite', + 'headers': + '$userbase/include/python$py_version_short$abiflags/$dist_name', + 'scripts': '$userbase/bin', + 'data' : '$userbase', + } + +# The keys to an installation scheme; if any new types of files are to be +# installed, be sure to add an entry to every installation scheme above, +# and to SCHEME_KEYS here. +SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') + + +class install(Command): + + description = "install everything from build directory" + + user_options = [ + # Select installation scheme and set base director(y|ies) + ('prefix=', None, + "installation prefix"), + ('exec-prefix=', None, + "(Unix only) prefix for platform-specific files"), + ('home=', None, + "(Unix only) home directory to install under"), + + # Or, just set the base director(y|ies) + ('install-base=', None, + "base installation directory (instead of --prefix or --home)"), + ('install-platbase=', None, + "base installation directory for platform-specific files " + + "(instead of --exec-prefix or --home)"), + ('root=', None, + "install everything relative to this alternate root directory"), + + # Or, explicitly set the installation scheme + ('install-purelib=', None, + "installation directory for pure Python module distributions"), + ('install-platlib=', None, + "installation directory for non-pure module distributions"), + ('install-lib=', None, + "installation directory for all module distributions " + + "(overrides --install-purelib and --install-platlib)"), + + ('install-headers=', None, + "installation directory for C/C++ headers"), + ('install-scripts=', None, + "installation directory for Python scripts"), + ('install-data=', None, + "installation directory for data files"), + + # Byte-compilation options -- see install_lib.py for details, as + # these are duplicated from there (but only install_lib does + # anything with them). + ('compile', 'c', "compile .py to .pyc [default]"), + ('no-compile', None, "don't compile .py files"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + + # Miscellaneous control options + ('force', 'f', + "force installation (overwrite any existing files)"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + + # Where to install documentation (eventually!) + #('doc-format=', None, "format of documentation to generate"), + #('install-man=', None, "directory for Unix man pages"), + #('install-html=', None, "directory for HTML documentation"), + #('install-info=', None, "directory for GNU info files"), + + ('record=', None, + "filename in which to record list of installed files"), + ] + + boolean_options = ['compile', 'force', 'skip-build'] + + if HAS_USER_SITE: + user_options.append(('user', None, + "install in user site-package '%s'" % USER_SITE)) + boolean_options.append('user') + + negative_opt = {'no-compile' : 'compile'} + + + def initialize_options(self): + """Initializes options.""" + # High-level options: these select both an installation base + # and scheme. + self.prefix = None + self.exec_prefix = None + self.home = None + self.user = 0 + + # These select only the installation base; it's up to the user to + # specify the installation scheme (currently, that means supplying + # the --install-{platlib,purelib,scripts,data} options). + self.install_base = None + self.install_platbase = None + self.root = None + + # These options are the actual installation directories; if not + # supplied by the user, they are filled in using the installation + # scheme implied by prefix/exec-prefix/home and the contents of + # that installation scheme. + self.install_purelib = None # for pure module distributions + self.install_platlib = None # non-pure (dists w/ extensions) + self.install_headers = None # for C/C++ headers + self.install_lib = None # set to either purelib or platlib + self.install_scripts = None + self.install_data = None + self.install_userbase = USER_BASE + self.install_usersite = USER_SITE + + self.compile = None + self.optimize = None + + # Deprecated + # These two are for putting non-packagized distributions into their + # own directory and creating a .pth file if it makes sense. + # 'extra_path' comes from the setup file; 'install_path_file' can + # be turned off if it makes no sense to install a .pth file. (But + # better to install it uselessly than to guess wrong and not + # install it when it's necessary and would be used!) Currently, + # 'install_path_file' is always true unless some outsider meddles + # with it. + self.extra_path = None + self.install_path_file = 1 + + # 'force' forces installation, even if target files are not + # out-of-date. 'skip_build' skips running the "build" command, + # handy if you know it's not necessary. 'warn_dir' (which is *not* + # a user option, it's just there so the bdist_* commands can turn + # it off) determines whether we warn about installing to a + # directory not in sys.path. + self.force = 0 + self.skip_build = 0 + self.warn_dir = 1 + + # These are only here as a conduit from the 'build' command to the + # 'install_*' commands that do the real work. ('build_base' isn't + # actually used anywhere, but it might be useful in future.) They + # are not user options, because if the user told the install + # command where the build directory is, that wouldn't affect the + # build command. + self.build_base = None + self.build_lib = None + + # Not defined yet because we don't know anything about + # documentation yet. + #self.install_man = None + #self.install_html = None + #self.install_info = None + + self.record = None + + + # -- Option finalizing methods ------------------------------------- + # (This is rather more involved than for most commands, + # because this is where the policy for installing third- + # party Python modules on various platforms given a wide + # array of user input is decided. Yes, it's quite complex!) + + def finalize_options(self): + """Finalizes options.""" + # This method (and its helpers, like 'finalize_unix()', + # 'finalize_other()', and 'select_scheme()') is where the default + # installation directories for modules, extension modules, and + # anything else we care to install from a Python module + # distribution. Thus, this code makes a pretty important policy + # statement about how third-party stuff is added to a Python + # installation! Note that the actual work of installation is done + # by the relatively simple 'install_*' commands; they just take + # their orders from the installation directory options determined + # here. + + # Check for errors/inconsistencies in the options; first, stuff + # that's wrong on any platform. + + if ((self.prefix or self.exec_prefix or self.home) and + (self.install_base or self.install_platbase)): + raise DistutilsOptionError( + "must supply either prefix/exec-prefix/home or " + + "install-base/install-platbase -- not both") + + if self.home and (self.prefix or self.exec_prefix): + raise DistutilsOptionError( + "must supply either home or prefix/exec-prefix -- not both") + + if self.user and (self.prefix or self.exec_prefix or self.home or + self.install_base or self.install_platbase): + raise DistutilsOptionError("can't combine user with prefix, " + "exec_prefix/home, or install_(plat)base") + + # Next, stuff that's wrong (or dubious) only on certain platforms. + if os.name != "posix": + if self.exec_prefix: + self.warn("exec-prefix option ignored on this platform") + self.exec_prefix = None + + # Now the interesting logic -- so interesting that we farm it out + # to other methods. The goal of these methods is to set the final + # values for the install_{lib,scripts,data,...} options, using as + # input a heady brew of prefix, exec_prefix, home, install_base, + # install_platbase, user-supplied versions of + # install_{purelib,platlib,lib,scripts,data,...}, and the + # INSTALL_SCHEME dictionary above. Phew! + + self.dump_dirs("pre-finalize_{unix,other}") + + if os.name == 'posix': + self.finalize_unix() + else: + self.finalize_other() + + self.dump_dirs("post-finalize_{unix,other}()") + + # Expand configuration variables, tilde, etc. in self.install_base + # and self.install_platbase -- that way, we can use $base or + # $platbase in the other installation directories and not worry + # about needing recursive variable expansion (shudder). + + py_version = sys.version.split()[0] + (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') + try: + abiflags = sys.abiflags + except AttributeError: + # sys.abiflags may not be defined on all platforms. + abiflags = '' + self.config_vars = {'dist_name': self.distribution.get_name(), + 'dist_version': self.distribution.get_version(), + 'dist_fullname': self.distribution.get_fullname(), + 'py_version': py_version, + 'py_version_short': '%d.%d' % sys.version_info[:2], + 'py_version_nodot': '%d%d' % sys.version_info[:2], + 'sys_prefix': prefix, + 'prefix': prefix, + 'sys_exec_prefix': exec_prefix, + 'exec_prefix': exec_prefix, + 'abiflags': abiflags, + 'platlibdir': getattr(sys, 'platlibdir', 'lib'), + } + + if HAS_USER_SITE: + self.config_vars['userbase'] = self.install_userbase + self.config_vars['usersite'] = self.install_usersite + + self.expand_basedirs() + + self.dump_dirs("post-expand_basedirs()") + + # Now define config vars for the base directories so we can expand + # everything else. + self.config_vars['base'] = self.install_base + self.config_vars['platbase'] = self.install_platbase + + if DEBUG: + from pprint import pprint + print("config vars:") + pprint(self.config_vars) + + # Expand "~" and configuration variables in the installation + # directories. + self.expand_dirs() + + self.dump_dirs("post-expand_dirs()") + + # Create directories in the home dir: + if self.user: + self.create_home_path() + + # Pick the actual directory to install all modules to: either + # install_purelib or install_platlib, depending on whether this + # module distribution is pure or not. Of course, if the user + # already specified install_lib, use their selection. + if self.install_lib is None: + if self.distribution.ext_modules: # has extensions: non-pure + self.install_lib = self.install_platlib + else: + self.install_lib = self.install_purelib + + + # Convert directories from Unix /-separated syntax to the local + # convention. + self.convert_paths('lib', 'purelib', 'platlib', + 'scripts', 'data', 'headers', + 'userbase', 'usersite') + + # Deprecated + # Well, we're not actually fully completely finalized yet: we still + # have to deal with 'extra_path', which is the hack for allowing + # non-packagized module distributions (hello, Numerical Python!) to + # get their own directories. + self.handle_extra_path() + self.install_libbase = self.install_lib # needed for .pth file + self.install_lib = os.path.join(self.install_lib, self.extra_dirs) + + # If a new root directory was supplied, make all the installation + # dirs relative to it. + if self.root is not None: + self.change_roots('libbase', 'lib', 'purelib', 'platlib', + 'scripts', 'data', 'headers') + + self.dump_dirs("after prepending root") + + # Find out the build directories, ie. where to install from. + self.set_undefined_options('build', + ('build_base', 'build_base'), + ('build_lib', 'build_lib')) + + # Punt on doc directories for now -- after all, we're punting on + # documentation completely! + + def dump_dirs(self, msg): + """Dumps the list of user options.""" + if not DEBUG: + return + from distutils.fancy_getopt import longopt_xlate + log.debug(msg + ":") + for opt in self.user_options: + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + if opt_name in self.negative_opt: + opt_name = self.negative_opt[opt_name] + opt_name = opt_name.translate(longopt_xlate) + val = not getattr(self, opt_name) + else: + opt_name = opt_name.translate(longopt_xlate) + val = getattr(self, opt_name) + log.debug(" %s: %s", opt_name, val) + + def finalize_unix(self): + """Finalizes options for posix platforms.""" + if self.install_base is not None or self.install_platbase is not None: + if ((self.install_lib is None and + self.install_purelib is None and + self.install_platlib is None) or + self.install_headers is None or + self.install_scripts is None or + self.install_data is None): + raise DistutilsOptionError( + "install-base or install-platbase supplied, but " + "installation scheme is incomplete") + return + + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError( + "User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme("unix_user") + elif self.home is not None: + self.install_base = self.install_platbase = self.home + self.select_scheme("unix_home") + else: + if self.prefix is None: + if self.exec_prefix is not None: + raise DistutilsOptionError( + "must not supply exec-prefix without prefix") + + self.prefix = os.path.normpath(sys.prefix) + self.exec_prefix = os.path.normpath(sys.exec_prefix) + + else: + if self.exec_prefix is None: + self.exec_prefix = self.prefix + + self.install_base = self.prefix + self.install_platbase = self.exec_prefix + self.select_scheme("unix_prefix") + + def finalize_other(self): + """Finalizes options for non-posix platforms""" + if self.user: + if self.install_userbase is None: + raise DistutilsPlatformError( + "User base directory is not specified") + self.install_base = self.install_platbase = self.install_userbase + self.select_scheme(os.name + "_user") + elif self.home is not None: + self.install_base = self.install_platbase = self.home + self.select_scheme("unix_home") + else: + if self.prefix is None: + self.prefix = os.path.normpath(sys.prefix) + + self.install_base = self.install_platbase = self.prefix + try: + self.select_scheme(os.name) + except KeyError: + raise DistutilsPlatformError( + "I don't know how to install stuff on '%s'" % os.name) + + def select_scheme(self, name): + """Sets the install directories by applying the install schemes.""" + # it's the caller's problem if they supply a bad name! + if (hasattr(sys, 'pypy_version_info') and + not name.endswith(('_user', '_home'))): + if os.name == 'nt': + name = 'pypy_nt' + else: + name = 'pypy' + scheme = INSTALL_SCHEMES[name] + for key in SCHEME_KEYS: + attrname = 'install_' + key + if getattr(self, attrname) is None: + setattr(self, attrname, scheme[key]) + + def _expand_attrs(self, attrs): + for attr in attrs: + val = getattr(self, attr) + if val is not None: + if os.name == 'posix' or os.name == 'nt': + val = os.path.expanduser(val) + val = subst_vars(val, self.config_vars) + setattr(self, attr, val) + + def expand_basedirs(self): + """Calls `os.path.expanduser` on install_base, install_platbase and + root.""" + self._expand_attrs(['install_base', 'install_platbase', 'root']) + + def expand_dirs(self): + """Calls `os.path.expanduser` on install dirs.""" + self._expand_attrs(['install_purelib', 'install_platlib', + 'install_lib', 'install_headers', + 'install_scripts', 'install_data',]) + + def convert_paths(self, *names): + """Call `convert_path` over `names`.""" + for name in names: + attr = "install_" + name + setattr(self, attr, convert_path(getattr(self, attr))) + + def handle_extra_path(self): + """Set `path_file` and `extra_dirs` using `extra_path`.""" + if self.extra_path is None: + self.extra_path = self.distribution.extra_path + + if self.extra_path is not None: + log.warn( + "Distribution option extra_path is deprecated. " + "See issue27919 for details." + ) + if isinstance(self.extra_path, str): + self.extra_path = self.extra_path.split(',') + + if len(self.extra_path) == 1: + path_file = extra_dirs = self.extra_path[0] + elif len(self.extra_path) == 2: + path_file, extra_dirs = self.extra_path + else: + raise DistutilsOptionError( + "'extra_path' option must be a list, tuple, or " + "comma-separated string with 1 or 2 elements") + + # convert to local form in case Unix notation used (as it + # should be in setup scripts) + extra_dirs = convert_path(extra_dirs) + else: + path_file = None + extra_dirs = '' + + # XXX should we warn if path_file and not extra_dirs? (in which + # case the path file would be harmless but pointless) + self.path_file = path_file + self.extra_dirs = extra_dirs + + def change_roots(self, *names): + """Change the install directories pointed by name using root.""" + for name in names: + attr = "install_" + name + setattr(self, attr, change_root(self.root, getattr(self, attr))) + + def create_home_path(self): + """Create directories under ~.""" + if not self.user: + return + home = convert_path(os.path.expanduser("~")) + for name, path in self.config_vars.items(): + if path.startswith(home) and not os.path.isdir(path): + self.debug_print("os.makedirs('%s', 0o700)" % path) + os.makedirs(path, 0o700) + + # -- Command execution methods ------------------------------------- + + def run(self): + """Runs the command.""" + # Obviously have to build before we can install + if not self.skip_build: + self.run_command('build') + # If we built for any other platform, we can't install. + build_plat = self.distribution.get_command_obj('build').plat_name + # check warn_dir - it is a clue that the 'install' is happening + # internally, and not to sys.path, so we don't check the platform + # matches what we are running. + if self.warn_dir and build_plat != get_platform(): + raise DistutilsPlatformError("Can't install when " + "cross-compiling") + + # Run all sub-commands (at least those that need to be run) + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + if self.path_file: + self.create_path_file() + + # write list of installed files, if requested. + if self.record: + outputs = self.get_outputs() + if self.root: # strip any package prefix + root_len = len(self.root) + for counter in range(len(outputs)): + outputs[counter] = outputs[counter][root_len:] + self.execute(write_file, + (self.record, outputs), + "writing list of installed files to '%s'" % + self.record) + + sys_path = map(os.path.normpath, sys.path) + sys_path = map(os.path.normcase, sys_path) + install_lib = os.path.normcase(os.path.normpath(self.install_lib)) + if (self.warn_dir and + not (self.path_file and self.install_path_file) and + install_lib not in sys_path): + log.debug(("modules installed to '%s', which is not in " + "Python's module search path (sys.path) -- " + "you'll have to change the search path yourself"), + self.install_lib) + + def create_path_file(self): + """Creates the .pth file""" + filename = os.path.join(self.install_libbase, + self.path_file + ".pth") + if self.install_path_file: + self.execute(write_file, + (filename, [self.extra_dirs]), + "creating %s" % filename) + else: + self.warn("path file '%s' not created" % filename) + + + # -- Reporting methods --------------------------------------------- + + def get_outputs(self): + """Assembles the outputs of all the sub-commands.""" + outputs = [] + for cmd_name in self.get_sub_commands(): + cmd = self.get_finalized_command(cmd_name) + # Add the contents of cmd.get_outputs(), ensuring + # that outputs doesn't contain duplicate entries + for filename in cmd.get_outputs(): + if filename not in outputs: + outputs.append(filename) + + if self.path_file and self.install_path_file: + outputs.append(os.path.join(self.install_libbase, + self.path_file + ".pth")) + + return outputs + + def get_inputs(self): + """Returns the inputs of all the sub-commands""" + # XXX gee, this looks familiar ;-( + inputs = [] + for cmd_name in self.get_sub_commands(): + cmd = self.get_finalized_command(cmd_name) + inputs.extend(cmd.get_inputs()) + + return inputs + + # -- Predicates for sub-command list ------------------------------- + + def has_lib(self): + """Returns true if the current distribution has any Python + modules to install.""" + return (self.distribution.has_pure_modules() or + self.distribution.has_ext_modules()) + + def has_headers(self): + """Returns true if the current distribution has any headers to + install.""" + return self.distribution.has_headers() + + def has_scripts(self): + """Returns true if the current distribution has any scripts to. + install.""" + return self.distribution.has_scripts() + + def has_data(self): + """Returns true if the current distribution has any data to. + install.""" + return self.distribution.has_data_files() + + # 'sub_commands': a list of commands this command might have to run to + # get its work done. See cmd.py for more info. + sub_commands = [('install_lib', has_lib), + ('install_headers', has_headers), + ('install_scripts', has_scripts), + ('install_data', has_data), + ('install_egg_info', lambda self:True), + ] diff --git a/setuptools/_distutils/command/install_data.py b/setuptools/_distutils/command/install_data.py new file mode 100644 index 00000000..947cd76a --- /dev/null +++ b/setuptools/_distutils/command/install_data.py @@ -0,0 +1,79 @@ +"""distutils.command.install_data + +Implements the Distutils 'install_data' command, for installing +platform-independent data files.""" + +# contributed by Bastian Kleineidam + +import os +from distutils.core import Command +from distutils.util import change_root, convert_path + +class install_data(Command): + + description = "install data files" + + user_options = [ + ('install-dir=', 'd', + "base directory for installing data files " + "(default: installation base dir)"), + ('root=', None, + "install everything relative to this alternate root directory"), + ('force', 'f', "force installation (overwrite existing files)"), + ] + + boolean_options = ['force'] + + def initialize_options(self): + self.install_dir = None + self.outfiles = [] + self.root = None + self.force = 0 + self.data_files = self.distribution.data_files + self.warn_dir = 1 + + def finalize_options(self): + self.set_undefined_options('install', + ('install_data', 'install_dir'), + ('root', 'root'), + ('force', 'force'), + ) + + def run(self): + self.mkpath(self.install_dir) + for f in self.data_files: + if isinstance(f, str): + # it's a simple file, so copy it + f = convert_path(f) + if self.warn_dir: + self.warn("setup script did not provide a directory for " + "'%s' -- installing right in '%s'" % + (f, self.install_dir)) + (out, _) = self.copy_file(f, self.install_dir) + self.outfiles.append(out) + else: + # it's a tuple with path to install to and a list of files + dir = convert_path(f[0]) + if not os.path.isabs(dir): + dir = os.path.join(self.install_dir, dir) + elif self.root: + dir = change_root(self.root, dir) + self.mkpath(dir) + + if f[1] == []: + # If there are no files listed, the user must be + # trying to create an empty directory, so add the + # directory to the list of output files. + self.outfiles.append(dir) + else: + # Copy files, adding them to the list of output files. + for data in f[1]: + data = convert_path(data) + (out, _) = self.copy_file(data, dir) + self.outfiles.append(out) + + def get_inputs(self): + return self.data_files or [] + + def get_outputs(self): + return self.outfiles diff --git a/setuptools/_distutils/command/install_egg_info.py b/setuptools/_distutils/command/install_egg_info.py new file mode 100644 index 00000000..0ddc7367 --- /dev/null +++ b/setuptools/_distutils/command/install_egg_info.py @@ -0,0 +1,77 @@ +"""distutils.command.install_egg_info + +Implements the Distutils 'install_egg_info' command, for installing +a package's PKG-INFO metadata.""" + + +from distutils.cmd import Command +from distutils import log, dir_util +import os, sys, re + +class install_egg_info(Command): + """Install an .egg-info file for the package""" + + description = "Install package's PKG-INFO metadata as an .egg-info file" + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ] + + def initialize_options(self): + self.install_dir = None + + def finalize_options(self): + self.set_undefined_options('install_lib',('install_dir','install_dir')) + basename = "%s-%s-py%d.%d.egg-info" % ( + to_filename(safe_name(self.distribution.get_name())), + to_filename(safe_version(self.distribution.get_version())), + *sys.version_info[:2] + ) + self.target = os.path.join(self.install_dir, basename) + self.outputs = [self.target] + + def run(self): + target = self.target + if os.path.isdir(target) and not os.path.islink(target): + dir_util.remove_tree(target, dry_run=self.dry_run) + elif os.path.exists(target): + self.execute(os.unlink,(self.target,),"Removing "+target) + elif not os.path.isdir(self.install_dir): + self.execute(os.makedirs, (self.install_dir,), + "Creating "+self.install_dir) + log.info("Writing %s", target) + if not self.dry_run: + with open(target, 'w', encoding='UTF-8') as f: + self.distribution.metadata.write_pkg_file(f) + + def get_outputs(self): + return self.outputs + + +# The following routines are taken from setuptools' pkg_resources module and +# can be replaced by importing them from pkg_resources once it is included +# in the stdlib. + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """Convert an arbitrary string to a standard version string + + Spaces become dots, and all other non-alphanumeric characters become + dashes, with runs of multiple dashes condensed to a single dash. + """ + version = version.replace(' ','.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-','_') diff --git a/setuptools/_distutils/command/install_headers.py b/setuptools/_distutils/command/install_headers.py new file mode 100644 index 00000000..9bb0b18d --- /dev/null +++ b/setuptools/_distutils/command/install_headers.py @@ -0,0 +1,47 @@ +"""distutils.command.install_headers + +Implements the Distutils 'install_headers' command, to install C/C++ header +files to the Python include directory.""" + +from distutils.core import Command + + +# XXX force is never used +class install_headers(Command): + + description = "install C/C++ header files" + + user_options = [('install-dir=', 'd', + "directory to install header files to"), + ('force', 'f', + "force installation (overwrite existing files)"), + ] + + boolean_options = ['force'] + + def initialize_options(self): + self.install_dir = None + self.force = 0 + self.outfiles = [] + + def finalize_options(self): + self.set_undefined_options('install', + ('install_headers', 'install_dir'), + ('force', 'force')) + + + def run(self): + headers = self.distribution.headers + if not headers: + return + + self.mkpath(self.install_dir) + for header in headers: + (out, _) = self.copy_file(header, self.install_dir) + self.outfiles.append(out) + + def get_inputs(self): + return self.distribution.headers or [] + + def get_outputs(self): + return self.outfiles diff --git a/setuptools/_distutils/command/install_lib.py b/setuptools/_distutils/command/install_lib.py new file mode 100644 index 00000000..6154cf09 --- /dev/null +++ b/setuptools/_distutils/command/install_lib.py @@ -0,0 +1,217 @@ +"""distutils.command.install_lib + +Implements the Distutils 'install_lib' command +(install all Python modules).""" + +import os +import importlib.util +import sys + +from distutils.core import Command +from distutils.errors import DistutilsOptionError + + +# Extension for Python source files. +PYTHON_SOURCE_EXTENSION = ".py" + +class install_lib(Command): + + description = "install all Python modules (extensions and pure Python)" + + # The byte-compilation options are a tad confusing. Here are the + # possible scenarios: + # 1) no compilation at all (--no-compile --no-optimize) + # 2) compile .pyc only (--compile --no-optimize; default) + # 3) compile .pyc and "opt-1" .pyc (--compile --optimize) + # 4) compile "opt-1" .pyc only (--no-compile --optimize) + # 5) compile .pyc and "opt-2" .pyc (--compile --optimize-more) + # 6) compile "opt-2" .pyc only (--no-compile --optimize-more) + # + # The UI for this is two options, 'compile' and 'optimize'. + # 'compile' is strictly boolean, and only decides whether to + # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and + # decides both whether to generate .pyc files and what level of + # optimization to use. + + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ('build-dir=','b', "build directory (where to install from)"), + ('force', 'f', "force installation (overwrite existing files)"), + ('compile', 'c', "compile .py to .pyc [default]"), + ('no-compile', None, "don't compile .py files"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + ('skip-build', None, "skip the build steps"), + ] + + boolean_options = ['force', 'compile', 'skip-build'] + negative_opt = {'no-compile' : 'compile'} + + def initialize_options(self): + # let the 'install' command dictate our installation directory + self.install_dir = None + self.build_dir = None + self.force = 0 + self.compile = None + self.optimize = None + self.skip_build = None + + def finalize_options(self): + # Get all the information we need to install pure Python modules + # from the umbrella 'install' command -- build (source) directory, + # install (target) directory, and whether to compile .py files. + self.set_undefined_options('install', + ('build_lib', 'build_dir'), + ('install_lib', 'install_dir'), + ('force', 'force'), + ('compile', 'compile'), + ('optimize', 'optimize'), + ('skip_build', 'skip_build'), + ) + + if self.compile is None: + self.compile = True + if self.optimize is None: + self.optimize = False + + if not isinstance(self.optimize, int): + try: + self.optimize = int(self.optimize) + if self.optimize not in (0, 1, 2): + raise AssertionError + except (ValueError, AssertionError): + raise DistutilsOptionError("optimize must be 0, 1, or 2") + + def run(self): + # Make sure we have built everything we need first + self.build() + + # Install everything: simply dump the entire contents of the build + # directory to the installation directory (that's the beauty of + # having a build directory!) + outfiles = self.install() + + # (Optionally) compile .py to .pyc + if outfiles is not None and self.distribution.has_pure_modules(): + self.byte_compile(outfiles) + + # -- Top-level worker functions ------------------------------------ + # (called from 'run()') + + def build(self): + if not self.skip_build: + if self.distribution.has_pure_modules(): + self.run_command('build_py') + if self.distribution.has_ext_modules(): + self.run_command('build_ext') + + def install(self): + if os.path.isdir(self.build_dir): + outfiles = self.copy_tree(self.build_dir, self.install_dir) + else: + self.warn("'%s' does not exist -- no Python modules to install" % + self.build_dir) + return + return outfiles + + def byte_compile(self, files): + if sys.dont_write_bytecode: + self.warn('byte-compiling is disabled, skipping.') + return + + from distutils.util import byte_compile + + # Get the "--root" directory supplied to the "install" command, + # and use it as a prefix to strip off the purported filename + # encoded in bytecode files. This is far from complete, but it + # should at least generate usable bytecode in RPM distributions. + install_root = self.get_finalized_command('install').root + + if self.compile: + byte_compile(files, optimize=0, + force=self.force, prefix=install_root, + dry_run=self.dry_run) + if self.optimize > 0: + byte_compile(files, optimize=self.optimize, + force=self.force, prefix=install_root, + verbose=self.verbose, dry_run=self.dry_run) + + + # -- Utility methods ----------------------------------------------- + + def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir): + if not has_any: + return [] + + build_cmd = self.get_finalized_command(build_cmd) + build_files = build_cmd.get_outputs() + build_dir = getattr(build_cmd, cmd_option) + + prefix_len = len(build_dir) + len(os.sep) + outputs = [] + for file in build_files: + outputs.append(os.path.join(output_dir, file[prefix_len:])) + + return outputs + + def _bytecode_filenames(self, py_filenames): + bytecode_files = [] + for py_file in py_filenames: + # Since build_py handles package data installation, the + # list of outputs can contain more than just .py files. + # Make sure we only report bytecode for the .py files. + ext = os.path.splitext(os.path.normcase(py_file))[1] + if ext != PYTHON_SOURCE_EXTENSION: + continue + if self.compile: + bytecode_files.append(importlib.util.cache_from_source( + py_file, optimization='')) + if self.optimize > 0: + bytecode_files.append(importlib.util.cache_from_source( + py_file, optimization=self.optimize)) + + return bytecode_files + + + # -- External interface -------------------------------------------- + # (called by outsiders) + + def get_outputs(self): + """Return the list of files that would be installed if this command + were actually run. Not affected by the "dry-run" flag or whether + modules have actually been built yet. + """ + pure_outputs = \ + self._mutate_outputs(self.distribution.has_pure_modules(), + 'build_py', 'build_lib', + self.install_dir) + if self.compile: + bytecode_outputs = self._bytecode_filenames(pure_outputs) + else: + bytecode_outputs = [] + + ext_outputs = \ + self._mutate_outputs(self.distribution.has_ext_modules(), + 'build_ext', 'build_lib', + self.install_dir) + + return pure_outputs + bytecode_outputs + ext_outputs + + def get_inputs(self): + """Get the list of files that are input to this command, ie. the + files that get installed as they are named in the build tree. + The files in this list correspond one-to-one to the output + filenames returned by 'get_outputs()'. + """ + inputs = [] + + if self.distribution.has_pure_modules(): + build_py = self.get_finalized_command('build_py') + inputs.extend(build_py.get_outputs()) + + if self.distribution.has_ext_modules(): + build_ext = self.get_finalized_command('build_ext') + inputs.extend(build_ext.get_outputs()) + + return inputs diff --git a/setuptools/_distutils/command/install_scripts.py b/setuptools/_distutils/command/install_scripts.py new file mode 100644 index 00000000..31a1130e --- /dev/null +++ b/setuptools/_distutils/command/install_scripts.py @@ -0,0 +1,60 @@ +"""distutils.command.install_scripts + +Implements the Distutils 'install_scripts' command, for installing +Python scripts.""" + +# contributed by Bastian Kleineidam + +import os +from distutils.core import Command +from distutils import log +from stat import ST_MODE + + +class install_scripts(Command): + + description = "install scripts (Python or otherwise)" + + user_options = [ + ('install-dir=', 'd', "directory to install scripts to"), + ('build-dir=','b', "build directory (where to install from)"), + ('force', 'f', "force installation (overwrite existing files)"), + ('skip-build', None, "skip the build steps"), + ] + + boolean_options = ['force', 'skip-build'] + + def initialize_options(self): + self.install_dir = None + self.force = 0 + self.build_dir = None + self.skip_build = None + + def finalize_options(self): + self.set_undefined_options('build', ('build_scripts', 'build_dir')) + self.set_undefined_options('install', + ('install_scripts', 'install_dir'), + ('force', 'force'), + ('skip_build', 'skip_build'), + ) + + def run(self): + if not self.skip_build: + self.run_command('build_scripts') + self.outfiles = self.copy_tree(self.build_dir, self.install_dir) + if os.name == 'posix': + # Set the executable bits (owner, group, and world) on + # all the scripts we just installed. + for file in self.get_outputs(): + if self.dry_run: + log.info("changing mode of %s", file) + else: + mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777 + log.info("changing mode of %s to %o", file, mode) + os.chmod(file, mode) + + def get_inputs(self): + return self.distribution.scripts or [] + + def get_outputs(self): + return self.outfiles or [] diff --git a/setuptools/_distutils/command/register.py b/setuptools/_distutils/command/register.py new file mode 100644 index 00000000..0fac94e9 --- /dev/null +++ b/setuptools/_distutils/command/register.py @@ -0,0 +1,304 @@ +"""distutils.command.register + +Implements the Distutils 'register' command (register with the repository). +""" + +# created 2002/10/21, Richard Jones + +import getpass +import io +import urllib.parse, urllib.request +from warnings import warn + +from distutils.core import PyPIRCCommand +from distutils.errors import * +from distutils import log + +class register(PyPIRCCommand): + + description = ("register the distribution with the Python package index") + user_options = PyPIRCCommand.user_options + [ + ('list-classifiers', None, + 'list the valid Trove classifiers'), + ('strict', None , + 'Will stop the registering if the meta-data are not fully compliant') + ] + boolean_options = PyPIRCCommand.boolean_options + [ + 'verify', 'list-classifiers', 'strict'] + + sub_commands = [('check', lambda self: True)] + + def initialize_options(self): + PyPIRCCommand.initialize_options(self) + self.list_classifiers = 0 + self.strict = 0 + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + # setting options for the `check` subcommand + check_options = {'strict': ('register', self.strict), + 'restructuredtext': ('register', 1)} + self.distribution.command_options['check'] = check_options + + def run(self): + self.finalize_options() + self._set_config() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + if self.dry_run: + self.verify_metadata() + elif self.list_classifiers: + self.classifiers() + else: + self.send_metadata() + + def check_metadata(self): + """Deprecated API.""" + warn("distutils.command.register.check_metadata is deprecated, \ + use the check command instead", PendingDeprecationWarning) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.strict = self.strict + check.restructuredtext = 1 + check.run() + + def _set_config(self): + ''' Reads the configuration file and set attributes. + ''' + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + self.has_config = True + else: + if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): + raise ValueError('%s not found in .pypirc' % self.repository) + if self.repository == 'pypi': + self.repository = self.DEFAULT_REPOSITORY + self.has_config = False + + def classifiers(self): + ''' Fetch the list of classifiers from the server. + ''' + url = self.repository+'?:action=list_classifiers' + response = urllib.request.urlopen(url) + log.info(self._read_pypi_response(response)) + + def verify_metadata(self): + ''' Send the metadata to the package index server to be checked. + ''' + # send the info to the server and report the result + (code, result) = self.post_to_server(self.build_post_data('verify')) + log.info('Server response (%s): %s', code, result) + + def send_metadata(self): + ''' Send the metadata to the package index server. + + Well, do the following: + 1. figure who the user is, and then + 2. send the data as a Basic auth'ed POST. + + First we try to read the username/password from $HOME/.pypirc, + which is a ConfigParser-formatted file with a section + [distutils] containing username and password entries (both + in clear text). Eg: + + [distutils] + index-servers = + pypi + + [pypi] + username: fred + password: sekrit + + Otherwise, to figure who the user is, we offer the user three + choices: + + 1. use existing login, + 2. register as a new user, or + 3. set the password to a random string and email the user. + + ''' + # see if we can short-cut and get the username/password from the + # config + if self.has_config: + choice = '1' + username = self.username + password = self.password + else: + choice = 'x' + username = password = '' + + # get the user's login info + choices = '1 2 3 4'.split() + while choice not in choices: + self.announce('''\ +We need to know who you are, so please choose either: + 1. use your existing login, + 2. register as a new user, + 3. have the server generate a new password for you (and email it to you), or + 4. quit +Your selection [default 1]: ''', log.INFO) + choice = input() + if not choice: + choice = '1' + elif choice not in choices: + print('Please choose one of the four options!') + + if choice == '1': + # get the username and password + while not username: + username = input('Username: ') + while not password: + password = getpass.getpass('Password: ') + + # set up the authentication + auth = urllib.request.HTTPPasswordMgr() + host = urllib.parse.urlparse(self.repository)[1] + auth.add_password(self.realm, host, username, password) + # send the info to the server and report the result + code, result = self.post_to_server(self.build_post_data('submit'), + auth) + self.announce('Server response (%s): %s' % (code, result), + log.INFO) + + # possibly save the login + if code == 200: + if self.has_config: + # sharing the password in the distribution instance + # so the upload command can reuse it + self.distribution.password = password + else: + self.announce(('I can store your PyPI login so future ' + 'submissions will be faster.'), log.INFO) + self.announce('(the login will be stored in %s)' % \ + self._get_rc_file(), log.INFO) + choice = 'X' + while choice.lower() not in 'yn': + choice = input('Save your login (y/N)?') + if not choice: + choice = 'n' + if choice.lower() == 'y': + self._store_pypirc(username, password) + + elif choice == '2': + data = {':action': 'user'} + data['name'] = data['password'] = data['email'] = '' + data['confirm'] = None + while not data['name']: + data['name'] = input('Username: ') + while data['password'] != data['confirm']: + while not data['password']: + data['password'] = getpass.getpass('Password: ') + while not data['confirm']: + data['confirm'] = getpass.getpass(' Confirm: ') + if data['password'] != data['confirm']: + data['password'] = '' + data['confirm'] = None + print("Password and confirm don't match!") + while not data['email']: + data['email'] = input(' EMail: ') + code, result = self.post_to_server(data) + if code != 200: + log.info('Server response (%s): %s', code, result) + else: + log.info('You will receive an email shortly.') + log.info(('Follow the instructions in it to ' + 'complete registration.')) + elif choice == '3': + data = {':action': 'password_reset'} + data['email'] = '' + while not data['email']: + data['email'] = input('Your email address: ') + code, result = self.post_to_server(data) + log.info('Server response (%s): %s', code, result) + + def build_post_data(self, action): + # figure the data to send - the metadata plus some additional + # information used by the package server + meta = self.distribution.metadata + data = { + ':action': action, + 'metadata_version' : '1.0', + 'name': meta.get_name(), + 'version': meta.get_version(), + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), + } + if data['provides'] or data['requires'] or data['obsoletes']: + data['metadata_version'] = '1.1' + return data + + def post_to_server(self, data, auth=None): + ''' Post a query to the server, and return a string response. + ''' + if 'name' in data: + self.announce('Registering %s to %s' % (data['name'], + self.repository), + log.INFO) + # Build up the MIME payload for the urllib2 POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = '\n--' + boundary + end_boundary = sep_boundary + '--' + body = io.StringIO() + for key, value in data.items(): + # handle multiple entries for the same name + if type(value) not in (type([]), type( () )): + value = [value] + for value in value: + value = str(value) + body.write(sep_boundary) + body.write('\nContent-Disposition: form-data; name="%s"'%key) + body.write("\n\n") + body.write(value) + if value and value[-1] == '\r': + body.write('\n') # write an extra newline (lurve Macs) + body.write(end_boundary) + body.write("\n") + body = body.getvalue().encode("utf-8") + + # build the Request + headers = { + 'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary, + 'Content-length': str(len(body)) + } + req = urllib.request.Request(self.repository, body, headers) + + # handle HTTP and include the Basic Auth handler + opener = urllib.request.build_opener( + urllib.request.HTTPBasicAuthHandler(password_mgr=auth) + ) + data = '' + try: + result = opener.open(req) + except urllib.error.HTTPError as e: + if self.show_response: + data = e.fp.read() + result = e.code, e.msg + except urllib.error.URLError as e: + result = 500, str(e) + else: + if self.show_response: + data = self._read_pypi_response(result) + result = 200, 'OK' + if self.show_response: + msg = '\n'.join(('-' * 75, data, '-' * 75)) + self.announce(msg, log.INFO) + return result diff --git a/setuptools/_distutils/command/sdist.py b/setuptools/_distutils/command/sdist.py new file mode 100644 index 00000000..b4996fcb --- /dev/null +++ b/setuptools/_distutils/command/sdist.py @@ -0,0 +1,494 @@ +"""distutils.command.sdist + +Implements the Distutils 'sdist' command (create a source distribution).""" + +import os +import sys +from glob import glob +from warnings import warn + +from distutils.core import Command +from distutils import dir_util +from distutils import file_util +from distutils import archive_util +from distutils.text_file import TextFile +from distutils.filelist import FileList +from distutils import log +from distutils.util import convert_path +from distutils.errors import DistutilsTemplateError, DistutilsOptionError + + +def show_formats(): + """Print all possible values for the 'formats' option (used by + the "--help-formats" command-line option). + """ + from distutils.fancy_getopt import FancyGetopt + from distutils.archive_util import ARCHIVE_FORMATS + formats = [] + for format in ARCHIVE_FORMATS.keys(): + formats.append(("formats=" + format, None, + ARCHIVE_FORMATS[format][2])) + formats.sort() + FancyGetopt(formats).print_help( + "List of available source distribution formats:") + + +class sdist(Command): + + description = "create a source distribution (tarball, zip file, etc.)" + + def checking_metadata(self): + """Callable used for the check sub-command. + + Placed here so user_options can view it""" + return self.metadata_check + + user_options = [ + ('template=', 't', + "name of manifest template file [default: MANIFEST.in]"), + ('manifest=', 'm', + "name of manifest file [default: MANIFEST]"), + ('use-defaults', None, + "include the default file set in the manifest " + "[default; disable with --no-defaults]"), + ('no-defaults', None, + "don't include the default file set"), + ('prune', None, + "specifically exclude files/directories that should not be " + "distributed (build tree, RCS/CVS dirs, etc.) " + "[default; disable with --no-prune]"), + ('no-prune', None, + "don't automatically exclude anything"), + ('manifest-only', 'o', + "just regenerate the manifest and then stop " + "(implies --force-manifest)"), + ('force-manifest', 'f', + "forcibly regenerate the manifest and carry on as usual. " + "Deprecated: now the manifest is always regenerated."), + ('formats=', None, + "formats for source distribution (comma-separated list)"), + ('keep-temp', 'k', + "keep the distribution tree around after creating " + + "archive file(s)"), + ('dist-dir=', 'd', + "directory to put the source distribution archive(s) in " + "[default: dist]"), + ('metadata-check', None, + "Ensure that all required elements of meta-data " + "are supplied. Warn if any missing. [default]"), + ('owner=', 'u', + "Owner name used when creating a tar file [default: current user]"), + ('group=', 'g', + "Group name used when creating a tar file [default: current group]"), + ] + + boolean_options = ['use-defaults', 'prune', + 'manifest-only', 'force-manifest', + 'keep-temp', 'metadata-check'] + + help_options = [ + ('help-formats', None, + "list available distribution formats", show_formats), + ] + + negative_opt = {'no-defaults': 'use-defaults', + 'no-prune': 'prune' } + + sub_commands = [('check', checking_metadata)] + + READMES = ('README', 'README.txt', 'README.rst') + + def initialize_options(self): + # 'template' and 'manifest' are, respectively, the names of + # the manifest template and manifest file. + self.template = None + self.manifest = None + + # 'use_defaults': if true, we will include the default file set + # in the manifest + self.use_defaults = 1 + self.prune = 1 + + self.manifest_only = 0 + self.force_manifest = 0 + + self.formats = ['gztar'] + self.keep_temp = 0 + self.dist_dir = None + + self.archive_files = None + self.metadata_check = 1 + self.owner = None + self.group = None + + def finalize_options(self): + if self.manifest is None: + self.manifest = "MANIFEST" + if self.template is None: + self.template = "MANIFEST.in" + + self.ensure_string_list('formats') + + bad_format = archive_util.check_archive_formats(self.formats) + if bad_format: + raise DistutilsOptionError( + "unknown archive format '%s'" % bad_format) + + if self.dist_dir is None: + self.dist_dir = "dist" + + def run(self): + # 'filelist' contains the list of files that will make up the + # manifest + self.filelist = FileList() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + # Do whatever it takes to get the list of files to process + # (process the manifest template, read an existing manifest, + # whatever). File list is accumulated in 'self.filelist'. + self.get_file_list() + + # If user just wanted us to regenerate the manifest, stop now. + if self.manifest_only: + return + + # Otherwise, go ahead and create the source distribution tarball, + # or zipfile, or whatever. + self.make_distribution() + + def check_metadata(self): + """Deprecated API.""" + warn("distutils.command.sdist.check_metadata is deprecated, \ + use the check command instead", PendingDeprecationWarning) + check = self.distribution.get_command_obj('check') + check.ensure_finalized() + check.run() + + def get_file_list(self): + """Figure out the list of files to include in the source + distribution, and put it in 'self.filelist'. This might involve + reading the manifest template (and writing the manifest), or just + reading the manifest, or just using the default file set -- it all + depends on the user's options. + """ + # new behavior when using a template: + # the file list is recalculated every time because + # even if MANIFEST.in or setup.py are not changed + # the user might have added some files in the tree that + # need to be included. + # + # This makes --force the default and only behavior with templates. + template_exists = os.path.isfile(self.template) + if not template_exists and self._manifest_is_not_generated(): + self.read_manifest() + self.filelist.sort() + self.filelist.remove_duplicates() + return + + if not template_exists: + self.warn(("manifest template '%s' does not exist " + + "(using default file list)") % + self.template) + self.filelist.findall() + + if self.use_defaults: + self.add_defaults() + + if template_exists: + self.read_template() + + if self.prune: + self.prune_file_list() + + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() + + def add_defaults(self): + """Add all the default files to self.filelist: + - README or README.txt + - setup.py + - test/test*.py + - all pure Python modules mentioned in setup script + - all files pointed by package_data (build_py) + - all files defined in data_files. + - all files defined as scripts. + - all C sources listed as part of extensions or C libraries + in the setup script (doesn't catch C headers!) + Warns if (README or README.txt) or setup.py are missing; everything + else is optional. + """ + self._add_defaults_standards() + self._add_defaults_optional() + self._add_defaults_python() + self._add_defaults_data_files() + self._add_defaults_ext() + self._add_defaults_c_libs() + self._add_defaults_scripts() + + @staticmethod + def _cs_path_exists(fspath): + """ + Case-sensitive path existence check + + >>> sdist._cs_path_exists(__file__) + True + >>> sdist._cs_path_exists(__file__.upper()) + False + """ + if not os.path.exists(fspath): + return False + # make absolute so we always have a directory + abspath = os.path.abspath(fspath) + directory, filename = os.path.split(abspath) + return filename in os.listdir(directory) + + def _add_defaults_standards(self): + standards = [self.READMES, self.distribution.script_name] + for fn in standards: + if isinstance(fn, tuple): + alts = fn + got_it = False + for fn in alts: + if self._cs_path_exists(fn): + got_it = True + self.filelist.append(fn) + break + + if not got_it: + self.warn("standard file not found: should have one of " + + ', '.join(alts)) + else: + if self._cs_path_exists(fn): + self.filelist.append(fn) + else: + self.warn("standard file '%s' not found" % fn) + + def _add_defaults_optional(self): + optional = ['test/test*.py', 'setup.cfg'] + for pattern in optional: + files = filter(os.path.isfile, glob(pattern)) + self.filelist.extend(files) + + def _add_defaults_python(self): + # build_py is used to get: + # - python modules + # - files defined in package_data + build_py = self.get_finalized_command('build_py') + + # getting python files + if self.distribution.has_pure_modules(): + self.filelist.extend(build_py.get_source_files()) + + # getting package_data files + # (computed in build_py.data_files by build_py.finalize_options) + for pkg, src_dir, build_dir, filenames in build_py.data_files: + for filename in filenames: + self.filelist.append(os.path.join(src_dir, filename)) + + def _add_defaults_data_files(self): + # getting distribution.data_files + if self.distribution.has_data_files(): + for item in self.distribution.data_files: + if isinstance(item, str): + # plain file + item = convert_path(item) + if os.path.isfile(item): + self.filelist.append(item) + else: + # a (dirname, filenames) tuple + dirname, filenames = item + for f in filenames: + f = convert_path(f) + if os.path.isfile(f): + self.filelist.append(f) + + def _add_defaults_ext(self): + if self.distribution.has_ext_modules(): + build_ext = self.get_finalized_command('build_ext') + self.filelist.extend(build_ext.get_source_files()) + + def _add_defaults_c_libs(self): + if self.distribution.has_c_libraries(): + build_clib = self.get_finalized_command('build_clib') + self.filelist.extend(build_clib.get_source_files()) + + def _add_defaults_scripts(self): + if self.distribution.has_scripts(): + build_scripts = self.get_finalized_command('build_scripts') + self.filelist.extend(build_scripts.get_source_files()) + + def read_template(self): + """Read and parse manifest template file named by self.template. + + (usually "MANIFEST.in") The parsing and processing is done by + 'self.filelist', which updates itself accordingly. + """ + log.info("reading manifest template '%s'", self.template) + template = TextFile(self.template, strip_comments=1, skip_blanks=1, + join_lines=1, lstrip_ws=1, rstrip_ws=1, + collapse_join=1) + + try: + while True: + line = template.readline() + if line is None: # end of file + break + + try: + self.filelist.process_template_line(line) + # the call above can raise a DistutilsTemplateError for + # malformed lines, or a ValueError from the lower-level + # convert_path function + except (DistutilsTemplateError, ValueError) as msg: + self.warn("%s, line %d: %s" % (template.filename, + template.current_line, + msg)) + finally: + template.close() + + def prune_file_list(self): + """Prune off branches that might slip into the file list as created + by 'read_template()', but really don't belong there: + * the build tree (typically "build") + * the release tree itself (only an issue if we ran "sdist" + previously with --keep-temp, or it aborted) + * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories + """ + build = self.get_finalized_command('build') + base_dir = self.distribution.get_fullname() + + self.filelist.exclude_pattern(None, prefix=build.build_base) + self.filelist.exclude_pattern(None, prefix=base_dir) + + if sys.platform == 'win32': + seps = r'/|\\' + else: + seps = '/' + + vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', + '_darcs'] + vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) + self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) + + def write_manifest(self): + """Write the file list in 'self.filelist' (presumably as filled in + by 'add_defaults()' and 'read_template()') to the manifest file + named by 'self.manifest'. + """ + if self._manifest_is_not_generated(): + log.info("not writing to manually maintained " + "manifest file '%s'" % self.manifest) + return + + content = self.filelist.files[:] + content.insert(0, '# file GENERATED by distutils, do NOT edit') + self.execute(file_util.write_file, (self.manifest, content), + "writing manifest file '%s'" % self.manifest) + + def _manifest_is_not_generated(self): + # check for special comment used in 3.1.3 and higher + if not os.path.isfile(self.manifest): + return False + + fp = open(self.manifest) + try: + first_line = fp.readline() + finally: + fp.close() + return first_line != '# file GENERATED by distutils, do NOT edit\n' + + def read_manifest(self): + """Read the manifest file (named by 'self.manifest') and use it to + fill in 'self.filelist', the list of files to include in the source + distribution. + """ + log.info("reading manifest file '%s'", self.manifest) + with open(self.manifest) as manifest: + for line in manifest: + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue + self.filelist.append(line) + + def make_release_tree(self, base_dir, files): + """Create the directory tree that will become the source + distribution archive. All directories implied by the filenames in + 'files' are created under 'base_dir', and then we hard link or copy + (if hard linking is unavailable) those files into place. + Essentially, this duplicates the developer's source tree, but in a + directory named after the distribution, containing only the files + to be distributed. + """ + # Create all the directories under 'base_dir' necessary to + # put 'files' there; the 'mkpath()' is just so we don't die + # if the manifest happens to be empty. + self.mkpath(base_dir) + dir_util.create_tree(base_dir, files, dry_run=self.dry_run) + + # And walk over the list of files, either making a hard link (if + # os.link exists) to each one that doesn't already exist in its + # corresponding location under 'base_dir', or copying each file + # that's out-of-date in 'base_dir'. (Usually, all files will be + # out-of-date, because by default we blow away 'base_dir' when + # we're done making the distribution archives.) + + if hasattr(os, 'link'): # can make hard links on this system + link = 'hard' + msg = "making hard links in %s..." % base_dir + else: # nope, have to copy + link = None + msg = "copying files to %s..." % base_dir + + if not files: + log.warn("no files to distribute -- empty manifest?") + else: + log.info(msg) + for file in files: + if not os.path.isfile(file): + log.warn("'%s' not a regular file -- skipping", file) + else: + dest = os.path.join(base_dir, file) + self.copy_file(file, dest, link=link) + + self.distribution.metadata.write_pkg_info(base_dir) + + def make_distribution(self): + """Create the source distribution(s). First, we create the release + tree with 'make_release_tree()'; then, we create all required + archive files (according to 'self.formats') from the release tree. + Finally, we clean up by blowing away the release tree (unless + 'self.keep_temp' is true). The list of archive files created is + stored so it can be retrieved later by 'get_archive_files()'. + """ + # Don't warn about missing meta-data here -- should be (and is!) + # done elsewhere. + base_dir = self.distribution.get_fullname() + base_name = os.path.join(self.dist_dir, base_dir) + + self.make_release_tree(base_dir, self.filelist.files) + archive_files = [] # remember names of files we create + # tar archive must be created last to avoid overwrite and remove + if 'tar' in self.formats: + self.formats.append(self.formats.pop(self.formats.index('tar'))) + + for fmt in self.formats: + file = self.make_archive(base_name, fmt, base_dir=base_dir, + owner=self.owner, group=self.group) + archive_files.append(file) + self.distribution.dist_files.append(('sdist', '', file)) + + self.archive_files = archive_files + + if not self.keep_temp: + dir_util.remove_tree(base_dir, dry_run=self.dry_run) + + def get_archive_files(self): + """Return the list of archive files created when the command + was run, or None if the command hasn't run yet. + """ + return self.archive_files diff --git a/setuptools/_distutils/command/upload.py b/setuptools/_distutils/command/upload.py new file mode 100644 index 00000000..95e9fda1 --- /dev/null +++ b/setuptools/_distutils/command/upload.py @@ -0,0 +1,214 @@ +""" +distutils.command.upload + +Implements the Distutils 'upload' subcommand (upload package to a package +index). +""" + +import os +import io +import hashlib +from base64 import standard_b64encode +from urllib.request import urlopen, Request, HTTPError +from urllib.parse import urlparse +from distutils.errors import DistutilsError, DistutilsOptionError +from distutils.core import PyPIRCCommand +from distutils.spawn import spawn +from distutils import log + + +# PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256) +# https://bugs.python.org/issue40698 +_FILE_CONTENT_DIGESTS = { + "md5_digest": getattr(hashlib, "md5", None), + "sha256_digest": getattr(hashlib, "sha256", None), + "blake2_256_digest": getattr(hashlib, "blake2b", None), +} + + +class upload(PyPIRCCommand): + + description = "upload binary package to PyPI" + + user_options = PyPIRCCommand.user_options + [ + ('sign', 's', + 'sign files to upload using gpg'), + ('identity=', 'i', 'GPG identity used to sign files'), + ] + + boolean_options = PyPIRCCommand.boolean_options + ['sign'] + + def initialize_options(self): + PyPIRCCommand.initialize_options(self) + self.username = '' + self.password = '' + self.show_response = 0 + self.sign = False + self.identity = None + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + if self.identity and not self.sign: + raise DistutilsOptionError( + "Must use --sign for --identity to have meaning" + ) + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + + # getting the password from the distribution + # if previously set by the register command + if not self.password and self.distribution.password: + self.password = self.distribution.password + + def run(self): + if not self.distribution.dist_files: + msg = ("Must create and upload files in one command " + "(e.g. setup.py sdist upload)") + raise DistutilsOptionError(msg) + for command, pyversion, filename in self.distribution.dist_files: + self.upload_file(command, pyversion, filename) + + def upload_file(self, command, pyversion, filename): + # Makes sure the repository URL is compliant + schema, netloc, url, params, query, fragments = \ + urlparse(self.repository) + if params or query or fragments: + raise AssertionError("Incompatible url %s" % self.repository) + + if schema not in ('http', 'https'): + raise AssertionError("unsupported schema " + schema) + + # Sign if requested + if self.sign: + gpg_args = ["gpg", "--detach-sign", "-a", filename] + if self.identity: + gpg_args[2:2] = ["--local-user", self.identity] + spawn(gpg_args, + dry_run=self.dry_run) + + # Fill in the data - send all the meta-data in case we need to + # register a new release + f = open(filename,'rb') + try: + content = f.read() + finally: + f.close() + + meta = self.distribution.metadata + data = { + # action + ':action': 'file_upload', + 'protocol_version': '1', + + # identify release + 'name': meta.get_name(), + 'version': meta.get_version(), + + # file content + 'content': (os.path.basename(filename),content), + 'filetype': command, + 'pyversion': pyversion, + + # additional meta-data + 'metadata_version': '1.0', + 'summary': meta.get_description(), + 'home_page': meta.get_url(), + 'author': meta.get_contact(), + 'author_email': meta.get_contact_email(), + 'license': meta.get_licence(), + 'description': meta.get_long_description(), + 'keywords': meta.get_keywords(), + 'platform': meta.get_platforms(), + 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), + # PEP 314 + 'provides': meta.get_provides(), + 'requires': meta.get_requires(), + 'obsoletes': meta.get_obsoletes(), + } + + data['comment'] = '' + + # file content digests + for digest_name, digest_cons in _FILE_CONTENT_DIGESTS.items(): + if digest_cons is None: + continue + try: + data[digest_name] = digest_cons(content).hexdigest() + except ValueError: + # hash digest not available or blocked by security policy + pass + + if self.sign: + with open(filename + ".asc", "rb") as f: + data['gpg_signature'] = (os.path.basename(filename) + ".asc", + f.read()) + + # set up the authentication + user_pass = (self.username + ":" + self.password).encode('ascii') + # The exact encoding of the authentication string is debated. + # Anyway PyPI only accepts ascii for both username or password. + auth = "Basic " + standard_b64encode(user_pass).decode('ascii') + + # Build up the MIME payload for the POST data + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = b'\r\n--' + boundary.encode('ascii') + end_boundary = sep_boundary + b'--\r\n' + body = io.BytesIO() + for key, value in data.items(): + title = '\r\nContent-Disposition: form-data; name="%s"' % key + # handle multiple entries for the same name + if not isinstance(value, list): + value = [value] + for value in value: + if type(value) is tuple: + title += '; filename="%s"' % value[0] + value = value[1] + else: + value = str(value).encode('utf-8') + body.write(sep_boundary) + body.write(title.encode('utf-8')) + body.write(b"\r\n\r\n") + body.write(value) + body.write(end_boundary) + body = body.getvalue() + + msg = "Submitting %s to %s" % (filename, self.repository) + self.announce(msg, log.INFO) + + # build the Request + headers = { + 'Content-type': 'multipart/form-data; boundary=%s' % boundary, + 'Content-length': str(len(body)), + 'Authorization': auth, + } + + request = Request(self.repository, data=body, + headers=headers) + # send the data + try: + result = urlopen(request) + status = result.getcode() + reason = result.msg + except HTTPError as e: + status = e.code + reason = e.msg + except OSError as e: + self.announce(str(e), log.ERROR) + raise + + if status == 200: + self.announce('Server response (%s): %s' % (status, reason), + log.INFO) + if self.show_response: + text = self._read_pypi_response(result) + msg = '\n'.join(('-' * 75, text, '-' * 75)) + self.announce(msg, log.INFO) + else: + msg = 'Upload failed (%s): %s' % (status, reason) + self.announce(msg, log.ERROR) + raise DistutilsError(msg) diff --git a/setuptools/_distutils/command/wininst-10.0-amd64.exe b/setuptools/_distutils/command/wininst-10.0-amd64.exe new file mode 100644 index 00000000..6fa0dce1 Binary files /dev/null and b/setuptools/_distutils/command/wininst-10.0-amd64.exe differ diff --git a/setuptools/_distutils/command/wininst-10.0.exe b/setuptools/_distutils/command/wininst-10.0.exe new file mode 100644 index 00000000..afc3bc6c Binary files /dev/null and b/setuptools/_distutils/command/wininst-10.0.exe differ diff --git a/setuptools/_distutils/command/wininst-14.0-amd64.exe b/setuptools/_distutils/command/wininst-14.0-amd64.exe new file mode 100644 index 00000000..253c2e2e Binary files /dev/null and b/setuptools/_distutils/command/wininst-14.0-amd64.exe differ diff --git a/setuptools/_distutils/command/wininst-14.0.exe b/setuptools/_distutils/command/wininst-14.0.exe new file mode 100644 index 00000000..46f5f356 Binary files /dev/null and b/setuptools/_distutils/command/wininst-14.0.exe differ diff --git a/setuptools/_distutils/command/wininst-6.0.exe b/setuptools/_distutils/command/wininst-6.0.exe new file mode 100644 index 00000000..f57c855a Binary files /dev/null and b/setuptools/_distutils/command/wininst-6.0.exe differ diff --git a/setuptools/_distutils/command/wininst-7.1.exe b/setuptools/_distutils/command/wininst-7.1.exe new file mode 100644 index 00000000..1433bc1a Binary files /dev/null and b/setuptools/_distutils/command/wininst-7.1.exe differ diff --git a/setuptools/_distutils/command/wininst-8.0.exe b/setuptools/_distutils/command/wininst-8.0.exe new file mode 100644 index 00000000..7403bfab Binary files /dev/null and b/setuptools/_distutils/command/wininst-8.0.exe differ diff --git a/setuptools/_distutils/command/wininst-9.0-amd64.exe b/setuptools/_distutils/command/wininst-9.0-amd64.exe new file mode 100644 index 00000000..94fbd434 Binary files /dev/null and b/setuptools/_distutils/command/wininst-9.0-amd64.exe differ diff --git a/setuptools/_distutils/command/wininst-9.0.exe b/setuptools/_distutils/command/wininst-9.0.exe new file mode 100644 index 00000000..2ec261f9 Binary files /dev/null and b/setuptools/_distutils/command/wininst-9.0.exe differ diff --git a/setuptools/_distutils/config.py b/setuptools/_distutils/config.py new file mode 100644 index 00000000..2171abd6 --- /dev/null +++ b/setuptools/_distutils/config.py @@ -0,0 +1,130 @@ +"""distutils.pypirc + +Provides the PyPIRCCommand class, the base class for the command classes +that uses .pypirc in the distutils.command package. +""" +import os +from configparser import RawConfigParser + +from distutils.cmd import Command + +DEFAULT_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:%s +password:%s +""" + +class PyPIRCCommand(Command): + """Base command that knows how to handle the .pypirc file + """ + DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/' + DEFAULT_REALM = 'pypi' + repository = None + realm = None + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % \ + DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server')] + + boolean_options = ['show-response'] + + def _get_rc_file(self): + """Returns rc file path.""" + return os.path.join(os.path.expanduser('~'), '.pypirc') + + def _store_pypirc(self, username, password): + """Creates a default .pypirc file.""" + rc = self._get_rc_file() + with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f: + f.write(DEFAULT_PYPIRC % (username, password)) + + def _read_pypirc(self): + """Reads the .pypirc file.""" + rc = self._get_rc_file() + if os.path.exists(rc): + self.announce('Using PyPI login from %s' % rc) + repository = self.repository or self.DEFAULT_REPOSITORY + + config = RawConfigParser() + config.read(rc) + sections = config.sections() + if 'distutils' in sections: + # let's get the list of servers + index_servers = config.get('distutils', 'index-servers') + _servers = [server.strip() for server in + index_servers.split('\n') + if server.strip() != ''] + if _servers == []: + # nothing set, let's try to get the default pypi + if 'pypi' in sections: + _servers = ['pypi'] + else: + # the file is not properly defined, returning + # an empty dict + return {} + for server in _servers: + current = {'server': server} + current['username'] = config.get(server, 'username') + + # optional params + for key, default in (('repository', + self.DEFAULT_REPOSITORY), + ('realm', self.DEFAULT_REALM), + ('password', None)): + if config.has_option(server, key): + current[key] = config.get(server, key) + else: + current[key] = default + + # work around people having "repository" for the "pypi" + # section of their config set to the HTTP (rather than + # HTTPS) URL + if (server == 'pypi' and + repository in (self.DEFAULT_REPOSITORY, 'pypi')): + current['repository'] = self.DEFAULT_REPOSITORY + return current + + if (current['server'] == repository or + current['repository'] == repository): + return current + elif 'server-login' in sections: + # old format + server = 'server-login' + if config.has_option(server, 'repository'): + repository = config.get(server, 'repository') + else: + repository = self.DEFAULT_REPOSITORY + return {'username': config.get(server, 'username'), + 'password': config.get(server, 'password'), + 'repository': repository, + 'server': server, + 'realm': self.DEFAULT_REALM} + + return {} + + def _read_pypi_response(self, response): + """Read and decode a PyPI HTTP response.""" + import cgi + content_type = response.getheader('content-type', 'text/plain') + encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii') + return response.read().decode(encoding) + + def initialize_options(self): + """Initialize options.""" + self.repository = None + self.realm = None + self.show_response = 0 + + def finalize_options(self): + """Finalizes options.""" + if self.repository is None: + self.repository = self.DEFAULT_REPOSITORY + if self.realm is None: + self.realm = self.DEFAULT_REALM diff --git a/setuptools/_distutils/core.py b/setuptools/_distutils/core.py new file mode 100644 index 00000000..d603d4a4 --- /dev/null +++ b/setuptools/_distutils/core.py @@ -0,0 +1,234 @@ +"""distutils.core + +The only module that needs to be imported to use the Distutils; provides +the 'setup' function (which is to be called from the setup script). Also +indirectly provides the Distribution and Command classes, although they are +really defined in distutils.dist and distutils.cmd. +""" + +import os +import sys + +from distutils.debug import DEBUG +from distutils.errors import * + +# Mainly import these so setup scripts can "from distutils.core import" them. +from distutils.dist import Distribution +from distutils.cmd import Command +from distutils.config import PyPIRCCommand +from distutils.extension import Extension + +# This is a barebones help message generated displayed when the user +# runs the setup script with no arguments at all. More useful help +# is generated with various --help options: global help, list commands, +# and per-command help. +USAGE = """\ +usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] + or: %(script)s --help [cmd1 cmd2 ...] + or: %(script)s --help-commands + or: %(script)s cmd --help +""" + +def gen_usage (script_name): + script = os.path.basename(script_name) + return USAGE % vars() + + +# Some mild magic to control the behaviour of 'setup()' from 'run_setup()'. +_setup_stop_after = None +_setup_distribution = None + +# Legal keyword arguments for the setup() function +setup_keywords = ('distclass', 'script_name', 'script_args', 'options', + 'name', 'version', 'author', 'author_email', + 'maintainer', 'maintainer_email', 'url', 'license', + 'description', 'long_description', 'keywords', + 'platforms', 'classifiers', 'download_url', + 'requires', 'provides', 'obsoletes', + ) + +# Legal keyword arguments for the Extension constructor +extension_keywords = ('name', 'sources', 'include_dirs', + 'define_macros', 'undef_macros', + 'library_dirs', 'libraries', 'runtime_library_dirs', + 'extra_objects', 'extra_compile_args', 'extra_link_args', + 'swig_opts', 'export_symbols', 'depends', 'language') + +def setup (**attrs): + """The gateway to the Distutils: do everything your setup script needs + to do, in a highly flexible and user-driven way. Briefly: create a + Distribution instance; find and parse config files; parse the command + line; run each Distutils command found there, customized by the options + supplied to 'setup()' (as keyword arguments), in config files, and on + the command line. + + The Distribution instance might be an instance of a class supplied via + the 'distclass' keyword argument to 'setup'; if no such class is + supplied, then the Distribution class (in dist.py) is instantiated. + All other arguments to 'setup' (except for 'cmdclass') are used to set + attributes of the Distribution instance. + + The 'cmdclass' argument, if supplied, is a dictionary mapping command + names to command classes. Each command encountered on the command line + will be turned into a command class, which is in turn instantiated; any + class found in 'cmdclass' is used in place of the default, which is + (for command 'foo_bar') class 'foo_bar' in module + 'distutils.command.foo_bar'. The command class must provide a + 'user_options' attribute which is a list of option specifiers for + 'distutils.fancy_getopt'. Any command-line options between the current + and the next command are used to set attributes of the current command + object. + + When the entire command-line has been successfully parsed, calls the + 'run()' method on each command object in turn. This method will be + driven entirely by the Distribution object (which each command object + has a reference to, thanks to its constructor), and the + command-specific options that became attributes of each command + object. + """ + + global _setup_stop_after, _setup_distribution + + # Determine the distribution class -- either caller-supplied or + # our Distribution (see below). + klass = attrs.get('distclass') + if klass: + del attrs['distclass'] + else: + klass = Distribution + + if 'script_name' not in attrs: + attrs['script_name'] = os.path.basename(sys.argv[0]) + if 'script_args' not in attrs: + attrs['script_args'] = sys.argv[1:] + + # Create the Distribution instance, using the remaining arguments + # (ie. everything except distclass) to initialize it + try: + _setup_distribution = dist = klass(attrs) + except DistutilsSetupError as msg: + if 'name' not in attrs: + raise SystemExit("error in setup command: %s" % msg) + else: + raise SystemExit("error in %s setup command: %s" % \ + (attrs['name'], msg)) + + if _setup_stop_after == "init": + return dist + + # Find and parse the config file(s): they will override options from + # the setup script, but be overridden by the command line. + dist.parse_config_files() + + if DEBUG: + print("options (after parsing config files):") + dist.dump_option_dicts() + + if _setup_stop_after == "config": + return dist + + # Parse the command line and override config files; any + # command-line errors are the end user's fault, so turn them into + # SystemExit to suppress tracebacks. + try: + ok = dist.parse_command_line() + except DistutilsArgError as msg: + raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg) + + if DEBUG: + print("options (after parsing command line):") + dist.dump_option_dicts() + + if _setup_stop_after == "commandline": + return dist + + # And finally, run all the commands found on the command line. + if ok: + try: + dist.run_commands() + except KeyboardInterrupt: + raise SystemExit("interrupted") + except OSError as exc: + if DEBUG: + sys.stderr.write("error: %s\n" % (exc,)) + raise + else: + raise SystemExit("error: %s" % (exc,)) + + except (DistutilsError, + CCompilerError) as msg: + if DEBUG: + raise + else: + raise SystemExit("error: " + str(msg)) + + return dist + +# setup () + + +def run_setup (script_name, script_args=None, stop_after="run"): + """Run a setup script in a somewhat controlled environment, and + return the Distribution instance that drives things. This is useful + if you need to find out the distribution meta-data (passed as + keyword args from 'script' to 'setup()', or the contents of the + config files or command-line. + + 'script_name' is a file that will be read and run with 'exec()'; + 'sys.argv[0]' will be replaced with 'script' for the duration of the + call. 'script_args' is a list of strings; if supplied, + 'sys.argv[1:]' will be replaced by 'script_args' for the duration of + the call. + + 'stop_after' tells 'setup()' when to stop processing; possible + values: + init + stop after the Distribution instance has been created and + populated with the keyword arguments to 'setup()' + config + stop after config files have been parsed (and their data + stored in the Distribution instance) + commandline + stop after the command-line ('sys.argv[1:]' or 'script_args') + have been parsed (and the data stored in the Distribution) + run [default] + stop after all commands have been run (the same as if 'setup()' + had been called in the usual way + + Returns the Distribution instance, which provides all information + used to drive the Distutils. + """ + if stop_after not in ('init', 'config', 'commandline', 'run'): + raise ValueError("invalid value for 'stop_after': %r" % (stop_after,)) + + global _setup_stop_after, _setup_distribution + _setup_stop_after = stop_after + + save_argv = sys.argv.copy() + g = {'__file__': script_name} + try: + try: + sys.argv[0] = script_name + if script_args is not None: + sys.argv[1:] = script_args + with open(script_name, 'rb') as f: + exec(f.read(), g) + finally: + sys.argv = save_argv + _setup_stop_after = None + except SystemExit: + # Hmm, should we do something if exiting with a non-zero code + # (ie. error)? + pass + + if _setup_distribution is None: + raise RuntimeError(("'distutils.core.setup()' was never called -- " + "perhaps '%s' is not a Distutils setup script?") % \ + script_name) + + # I wonder if the setup script's namespace -- g and l -- would be of + # any interest to callers? + #print "_setup_distribution:", _setup_distribution + return _setup_distribution + +# run_setup () diff --git a/setuptools/_distutils/cygwinccompiler.py b/setuptools/_distutils/cygwinccompiler.py new file mode 100644 index 00000000..66c12dd3 --- /dev/null +++ b/setuptools/_distutils/cygwinccompiler.py @@ -0,0 +1,403 @@ +"""distutils.cygwinccompiler + +Provides the CygwinCCompiler class, a subclass of UnixCCompiler that +handles the Cygwin port of the GNU C compiler to Windows. It also contains +the Mingw32CCompiler class which handles the mingw32 port of GCC (same as +cygwin in no-cygwin mode). +""" + +# problems: +# +# * if you use a msvc compiled python version (1.5.2) +# 1. you have to insert a __GNUC__ section in its config.h +# 2. you have to generate an import library for its dll +# - create a def-file for python??.dll +# - create an import library using +# dlltool --dllname python15.dll --def python15.def \ +# --output-lib libpython15.a +# +# see also http://starship.python.net/crew/kernr/mingw32/Notes.html +# +# * We put export_symbols in a def-file, and don't use +# --export-all-symbols because it doesn't worked reliable in some +# tested configurations. And because other windows compilers also +# need their symbols specified this no serious problem. +# +# tested configurations: +# +# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works +# (after patching python's config.h and for C++ some other include files) +# see also http://starship.python.net/crew/kernr/mingw32/Notes.html +# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works +# (ld doesn't support -shared, so we use dllwrap) +# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now +# - its dllwrap doesn't work, there is a bug in binutils 2.10.90 +# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html +# - using gcc -mdll instead dllwrap doesn't work without -static because +# it tries to link against dlls instead their import libraries. (If +# it finds the dll first.) +# By specifying -static we force ld to link against the import libraries, +# this is windows standard and there are normally not the necessary symbols +# in the dlls. +# *** only the version of June 2000 shows these problems +# * cygwin gcc 3.2/ld 2.13.90 works +# (ld supports -shared) +# * mingw gcc 3.2/ld 2.13 works +# (ld supports -shared) + +import os +import sys +import copy +from subprocess import Popen, PIPE, check_output +import re + +from distutils.unixccompiler import UnixCCompiler +from distutils.file_util import write_file +from distutils.errors import (DistutilsExecError, CCompilerError, + CompileError, UnknownFileError) +from distutils.version import LooseVersion +from distutils.spawn import find_executable + +def get_msvcr(): + """Include the appropriate MSVC runtime library if Python was built + with MSVC 7.0 or later. + """ + msc_pos = sys.version.find('MSC v.') + if msc_pos != -1: + msc_ver = sys.version[msc_pos+6:msc_pos+10] + if msc_ver == '1300': + # MSVC 7.0 + return ['msvcr70'] + elif msc_ver == '1310': + # MSVC 7.1 + return ['msvcr71'] + elif msc_ver == '1400': + # VS2005 / MSVC 8.0 + return ['msvcr80'] + elif msc_ver == '1500': + # VS2008 / MSVC 9.0 + return ['msvcr90'] + elif msc_ver == '1600': + # VS2010 / MSVC 10.0 + return ['msvcr100'] + else: + raise ValueError("Unknown MS Compiler version %s " % msc_ver) + + +class CygwinCCompiler(UnixCCompiler): + """ Handles the Cygwin port of the GNU C compiler to Windows. + """ + compiler_type = 'cygwin' + obj_extension = ".o" + static_lib_extension = ".a" + shared_lib_extension = ".dll" + static_lib_format = "lib%s%s" + shared_lib_format = "%s%s" + exe_extension = ".exe" + + def __init__(self, verbose=0, dry_run=0, force=0): + + UnixCCompiler.__init__(self, verbose, dry_run, force) + + status, details = check_config_h() + self.debug_print("Python's GCC status: %s (details: %s)" % + (status, details)) + if status is not CONFIG_H_OK: + self.warn( + "Python's pyconfig.h doesn't seem to support your compiler. " + "Reason: %s. " + "Compiling may fail because of undefined preprocessor macros." + % details) + + self.gcc_version, self.ld_version, self.dllwrap_version = \ + get_versions() + self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % + (self.gcc_version, + self.ld_version, + self.dllwrap_version) ) + + # ld_version >= "2.10.90" and < "2.13" should also be able to use + # gcc -mdll instead of dllwrap + # Older dllwraps had own version numbers, newer ones use the + # same as the rest of binutils ( also ld ) + # dllwrap 2.10.90 is buggy + if self.ld_version >= "2.10.90": + self.linker_dll = "gcc" + else: + self.linker_dll = "dllwrap" + + # ld_version >= "2.13" support -shared so use it instead of + # -mdll -static + if self.ld_version >= "2.13": + shared_option = "-shared" + else: + shared_option = "-mdll -static" + + # Hard-code GCC because that's what this is all about. + # XXX optimization, warnings etc. should be customizable. + self.set_executables(compiler='gcc -mcygwin -O -Wall', + compiler_so='gcc -mcygwin -mdll -O -Wall', + compiler_cxx='g++ -mcygwin -O -Wall', + linker_exe='gcc -mcygwin', + linker_so=('%s -mcygwin %s' % + (self.linker_dll, shared_option))) + + # cygwin and mingw32 need different sets of libraries + if self.gcc_version == "2.91.57": + # cygwin shouldn't need msvcrt, but without the dlls will crash + # (gcc version 2.91.57) -- perhaps something about initialization + self.dll_libraries=["msvcrt"] + self.warn( + "Consider upgrading to a newer version of gcc") + else: + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or later. + self.dll_libraries = get_msvcr() + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + """Compiles the source by spawning GCC and windres if needed.""" + if ext == '.rc' or ext == '.res': + # gcc needs '.res' and '.rc' compiled to object files !!! + try: + self.spawn(["windres", "-i", src, "-o", obj]) + except DistutilsExecError as msg: + raise CompileError(msg) + else: # for other files use the C-compiler + try: + self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + def link(self, target_desc, objects, output_filename, output_dir=None, + libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None): + """Link the objects.""" + # use separate copies, so we can modify the lists + extra_preargs = copy.copy(extra_preargs or []) + libraries = copy.copy(libraries or []) + objects = copy.copy(objects or []) + + # Additional libraries + libraries.extend(self.dll_libraries) + + # handle export symbols by creating a def-file + # with executables this only works with gcc/ld as linker + if ((export_symbols is not None) and + (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): + # (The linker doesn't do anything if output is up-to-date. + # So it would probably better to check if we really need this, + # but for this we had to insert some unchanged parts of + # UnixCCompiler, and this is not what we want.) + + # we want to put some files in the same directory as the + # object files are, build_temp doesn't help much + # where are the object files + temp_dir = os.path.dirname(objects[0]) + # name of dll to give the helper files the same base name + (dll_name, dll_extension) = os.path.splitext( + os.path.basename(output_filename)) + + # generate the filenames for these files + def_file = os.path.join(temp_dir, dll_name + ".def") + lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") + + # Generate .def file + contents = [ + "LIBRARY %s" % os.path.basename(output_filename), + "EXPORTS"] + for sym in export_symbols: + contents.append(sym) + self.execute(write_file, (def_file, contents), + "writing %s" % def_file) + + # next add options for def-file and to creating import libraries + + # dllwrap uses different options than gcc/ld + if self.linker_dll == "dllwrap": + extra_preargs.extend(["--output-lib", lib_file]) + # for dllwrap we have to use a special option + extra_preargs.extend(["--def", def_file]) + # we use gcc/ld here and can be sure ld is >= 2.9.10 + else: + # doesn't work: bfd_close build\...\libfoo.a: Invalid operation + #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) + # for gcc/ld the def-file is specified as any object files + objects.append(def_file) + + #end: if ((export_symbols is not None) and + # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): + + # who wants symbols and a many times larger output file + # should explicitly switch the debug mode on + # otherwise we let dllwrap/ld strip the output file + # (On my machine: 10KiB < stripped_file < ??100KiB + # unstripped_file = stripped_file + XXX KiB + # ( XXX=254 for a typical python extension)) + if not debug: + extra_preargs.append("-s") + + UnixCCompiler.link(self, target_desc, objects, output_filename, + output_dir, libraries, library_dirs, + runtime_library_dirs, + None, # export_symbols, we do this in our def-file + debug, extra_preargs, extra_postargs, build_temp, + target_lang) + + # -- Miscellaneous methods ----------------------------------------- + + def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): + """Adds supports for rc and res files.""" + if output_dir is None: + output_dir = '' + obj_names = [] + for src_name in source_filenames: + # use normcase to make sure '.rc' is really '.rc' and not '.RC' + base, ext = os.path.splitext(os.path.normcase(src_name)) + if ext not in (self.src_extensions + ['.rc','.res']): + raise UnknownFileError("unknown file type '%s' (from '%s')" % \ + (ext, src_name)) + if strip_dir: + base = os.path.basename (base) + if ext in ('.res', '.rc'): + # these need to be compiled to object files + obj_names.append (os.path.join(output_dir, + base + ext + self.obj_extension)) + else: + obj_names.append (os.path.join(output_dir, + base + self.obj_extension)) + return obj_names + +# the same as cygwin plus some additional parameters +class Mingw32CCompiler(CygwinCCompiler): + """ Handles the Mingw32 port of the GNU C compiler to Windows. + """ + compiler_type = 'mingw32' + + def __init__(self, verbose=0, dry_run=0, force=0): + + CygwinCCompiler.__init__ (self, verbose, dry_run, force) + + # ld_version >= "2.13" support -shared so use it instead of + # -mdll -static + if self.ld_version >= "2.13": + shared_option = "-shared" + else: + shared_option = "-mdll -static" + + # A real mingw32 doesn't need to specify a different entry point, + # but cygwin 2.91.57 in no-cygwin-mode needs it. + if self.gcc_version <= "2.91.57": + entry_point = '--entry _DllMain@12' + else: + entry_point = '' + + if is_cygwingcc(): + raise CCompilerError( + 'Cygwin gcc cannot be used with --compiler=mingw32') + + self.set_executables(compiler='gcc -O -Wall', + compiler_so='gcc -mdll -O -Wall', + compiler_cxx='g++ -O -Wall', + linker_exe='gcc', + linker_so='%s %s %s' + % (self.linker_dll, shared_option, + entry_point)) + # Maybe we should also append -mthreads, but then the finished + # dlls need another dll (mingwm10.dll see Mingw32 docs) + # (-mthreads: Support thread-safe exception handling on `Mingw32') + + # no additional libraries needed + self.dll_libraries=[] + + # Include the appropriate MSVC runtime library if Python was built + # with MSVC 7.0 or later. + self.dll_libraries = get_msvcr() + +# Because these compilers aren't configured in Python's pyconfig.h file by +# default, we should at least warn the user if he is using an unmodified +# version. + +CONFIG_H_OK = "ok" +CONFIG_H_NOTOK = "not ok" +CONFIG_H_UNCERTAIN = "uncertain" + +def check_config_h(): + """Check if the current Python installation appears amenable to building + extensions with GCC. + + Returns a tuple (status, details), where 'status' is one of the following + constants: + + - CONFIG_H_OK: all is well, go ahead and compile + - CONFIG_H_NOTOK: doesn't look good + - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h + + 'details' is a human-readable string explaining the situation. + + Note there are two ways to conclude "OK": either 'sys.version' contains + the string "GCC" (implying that this Python was built with GCC), or the + installed "pyconfig.h" contains the string "__GNUC__". + """ + + # XXX since this function also checks sys.version, it's not strictly a + # "pyconfig.h" check -- should probably be renamed... + + from distutils import sysconfig + + # if sys.version contains GCC then python was compiled with GCC, and the + # pyconfig.h file should be OK + if "GCC" in sys.version: + return CONFIG_H_OK, "sys.version mentions 'GCC'" + + # let's see if __GNUC__ is mentioned in python.h + fn = sysconfig.get_config_h_filename() + try: + config_h = open(fn) + try: + if "__GNUC__" in config_h.read(): + return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn + else: + return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn + finally: + config_h.close() + except OSError as exc: + return (CONFIG_H_UNCERTAIN, + "couldn't read '%s': %s" % (fn, exc.strerror)) + +RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)') + +def _find_exe_version(cmd): + """Find the version of an executable by running `cmd` in the shell. + + If the command is not found, or the output does not match + `RE_VERSION`, returns None. + """ + executable = cmd.split()[0] + if find_executable(executable) is None: + return None + out = Popen(cmd, shell=True, stdout=PIPE).stdout + try: + out_string = out.read() + finally: + out.close() + result = RE_VERSION.search(out_string) + if result is None: + return None + # LooseVersion works with strings + # so we need to decode our bytes + return LooseVersion(result.group(1).decode()) + +def get_versions(): + """ Try to find out the versions of gcc, ld and dllwrap. + + If not possible it returns None for it. + """ + commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] + return tuple([_find_exe_version(cmd) for cmd in commands]) + +def is_cygwingcc(): + '''Try to determine if the gcc that would be used is from cygwin.''' + out_string = check_output(['gcc', '-dumpmachine']) + return out_string.strip().endswith(b'cygwin') diff --git a/setuptools/_distutils/debug.py b/setuptools/_distutils/debug.py new file mode 100644 index 00000000..daf1660f --- /dev/null +++ b/setuptools/_distutils/debug.py @@ -0,0 +1,5 @@ +import os + +# If DISTUTILS_DEBUG is anything other than the empty string, we run in +# debug mode. +DEBUG = os.environ.get('DISTUTILS_DEBUG') diff --git a/setuptools/_distutils/dep_util.py b/setuptools/_distutils/dep_util.py new file mode 100644 index 00000000..d74f5e4e --- /dev/null +++ b/setuptools/_distutils/dep_util.py @@ -0,0 +1,92 @@ +"""distutils.dep_util + +Utility functions for simple, timestamp-based dependency of files +and groups of files; also, function based entirely on such +timestamp dependency analysis.""" + +import os +from distutils.errors import DistutilsFileError + + +def newer (source, target): + """Return true if 'source' exists and is more recently modified than + 'target', or if 'source' exists and 'target' doesn't. Return false if + both exist and 'target' is the same age or younger than 'source'. + Raise DistutilsFileError if 'source' does not exist. + """ + if not os.path.exists(source): + raise DistutilsFileError("file '%s' does not exist" % + os.path.abspath(source)) + if not os.path.exists(target): + return 1 + + from stat import ST_MTIME + mtime1 = os.stat(source)[ST_MTIME] + mtime2 = os.stat(target)[ST_MTIME] + + return mtime1 > mtime2 + +# newer () + + +def newer_pairwise (sources, targets): + """Walk two filename lists in parallel, testing if each source is newer + than its corresponding target. Return a pair of lists (sources, + targets) where source is newer than target, according to the semantics + of 'newer()'. + """ + if len(sources) != len(targets): + raise ValueError("'sources' and 'targets' must be same length") + + # build a pair of lists (sources, targets) where source is newer + n_sources = [] + n_targets = [] + for i in range(len(sources)): + if newer(sources[i], targets[i]): + n_sources.append(sources[i]) + n_targets.append(targets[i]) + + return (n_sources, n_targets) + +# newer_pairwise () + + +def newer_group (sources, target, missing='error'): + """Return true if 'target' is out-of-date with respect to any file + listed in 'sources'. In other words, if 'target' exists and is newer + than every file in 'sources', return false; otherwise return true. + 'missing' controls what we do when a source file is missing; the + default ("error") is to blow up with an OSError from inside 'stat()'; + if it is "ignore", we silently drop any missing source files; if it is + "newer", any missing source files make us assume that 'target' is + out-of-date (this is handy in "dry-run" mode: it'll make you pretend to + carry out commands that wouldn't work because inputs are missing, but + that doesn't matter because you're not actually going to run the + commands). + """ + # If the target doesn't even exist, then it's definitely out-of-date. + if not os.path.exists(target): + return 1 + + # Otherwise we have to find out the hard way: if *any* source file + # is more recent than 'target', then 'target' is out-of-date and + # we can immediately return true. If we fall through to the end + # of the loop, then 'target' is up-to-date and we return false. + from stat import ST_MTIME + target_mtime = os.stat(target)[ST_MTIME] + for source in sources: + if not os.path.exists(source): + if missing == 'error': # blow up when we stat() the file + pass + elif missing == 'ignore': # missing source dropped from + continue # target's dependency list + elif missing == 'newer': # missing source means target is + return 1 # out-of-date + + source_mtime = os.stat(source)[ST_MTIME] + if source_mtime > target_mtime: + return 1 + else: + return 0 + +# newer_group () diff --git a/setuptools/_distutils/dir_util.py b/setuptools/_distutils/dir_util.py new file mode 100644 index 00000000..d5cd8e3e --- /dev/null +++ b/setuptools/_distutils/dir_util.py @@ -0,0 +1,210 @@ +"""distutils.dir_util + +Utility functions for manipulating directories and directory trees.""" + +import os +import errno +from distutils.errors import DistutilsFileError, DistutilsInternalError +from distutils import log + +# cache for by mkpath() -- in addition to cheapening redundant calls, +# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode +_path_created = {} + +# I don't use os.makedirs because a) it's new to Python 1.5.2, and +# b) it blows up if the directory already exists (I want to silently +# succeed in that case). +def mkpath(name, mode=0o777, verbose=1, dry_run=0): + """Create a directory and any missing ancestor directories. + + If the directory already exists (or if 'name' is the empty string, which + means the current directory, which of course exists), then do nothing. + Raise DistutilsFileError if unable to create some directory along the way + (eg. some sub-path exists, but is a file rather than a directory). + If 'verbose' is true, print a one-line summary of each mkdir to stdout. + Return the list of directories actually created. + """ + + global _path_created + + # Detect a common bug -- name is None + if not isinstance(name, str): + raise DistutilsInternalError( + "mkpath: 'name' must be a string (got %r)" % (name,)) + + # XXX what's the better way to handle verbosity? print as we create + # each directory in the path (the current behaviour), or only announce + # the creation of the whole path? (quite easy to do the latter since + # we're not using a recursive algorithm) + + name = os.path.normpath(name) + created_dirs = [] + if os.path.isdir(name) or name == '': + return created_dirs + if _path_created.get(os.path.abspath(name)): + return created_dirs + + (head, tail) = os.path.split(name) + tails = [tail] # stack of lone dirs to create + + while head and tail and not os.path.isdir(head): + (head, tail) = os.path.split(head) + tails.insert(0, tail) # push next higher dir onto stack + + # now 'head' contains the deepest directory that already exists + # (that is, the child of 'head' in 'name' is the highest directory + # that does *not* exist) + for d in tails: + #print "head = %s, d = %s: " % (head, d), + head = os.path.join(head, d) + abs_head = os.path.abspath(head) + + if _path_created.get(abs_head): + continue + + if verbose >= 1: + log.info("creating %s", head) + + if not dry_run: + try: + os.mkdir(head, mode) + except OSError as exc: + if not (exc.errno == errno.EEXIST and os.path.isdir(head)): + raise DistutilsFileError( + "could not create '%s': %s" % (head, exc.args[-1])) + created_dirs.append(head) + + _path_created[abs_head] = 1 + return created_dirs + +def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0): + """Create all the empty directories under 'base_dir' needed to put 'files' + there. + + 'base_dir' is just the name of a directory which doesn't necessarily + exist yet; 'files' is a list of filenames to be interpreted relative to + 'base_dir'. 'base_dir' + the directory portion of every file in 'files' + will be created if it doesn't already exist. 'mode', 'verbose' and + 'dry_run' flags are as for 'mkpath()'. + """ + # First get the list of directories to create + need_dir = set() + for file in files: + need_dir.add(os.path.join(base_dir, os.path.dirname(file))) + + # Now create them + for dir in sorted(need_dir): + mkpath(dir, mode, verbose=verbose, dry_run=dry_run) + +def copy_tree(src, dst, preserve_mode=1, preserve_times=1, + preserve_symlinks=0, update=0, verbose=1, dry_run=0): + """Copy an entire directory tree 'src' to a new location 'dst'. + + Both 'src' and 'dst' must be directory names. If 'src' is not a + directory, raise DistutilsFileError. If 'dst' does not exist, it is + created with 'mkpath()'. The end result of the copy is that every + file in 'src' is copied to 'dst', and directories under 'src' are + recursively copied to 'dst'. Return the list of files that were + copied or might have been copied, using their output name. The + return value is unaffected by 'update' or 'dry_run': it is simply + the list of all files under 'src', with the names changed to be + under 'dst'. + + 'preserve_mode' and 'preserve_times' are the same as for + 'copy_file'; note that they only apply to regular files, not to + directories. If 'preserve_symlinks' is true, symlinks will be + copied as symlinks (on platforms that support them!); otherwise + (the default), the destination of the symlink will be copied. + 'update' and 'verbose' are the same as for 'copy_file'. + """ + from distutils.file_util import copy_file + + if not dry_run and not os.path.isdir(src): + raise DistutilsFileError( + "cannot copy tree '%s': not a directory" % src) + try: + names = os.listdir(src) + except OSError as e: + if dry_run: + names = [] + else: + raise DistutilsFileError( + "error listing files in '%s': %s" % (src, e.strerror)) + + if not dry_run: + mkpath(dst, verbose=verbose) + + outputs = [] + + for n in names: + src_name = os.path.join(src, n) + dst_name = os.path.join(dst, n) + + if n.startswith('.nfs'): + # skip NFS rename files + continue + + if preserve_symlinks and os.path.islink(src_name): + link_dest = os.readlink(src_name) + if verbose >= 1: + log.info("linking %s -> %s", dst_name, link_dest) + if not dry_run: + os.symlink(link_dest, dst_name) + outputs.append(dst_name) + + elif os.path.isdir(src_name): + outputs.extend( + copy_tree(src_name, dst_name, preserve_mode, + preserve_times, preserve_symlinks, update, + verbose=verbose, dry_run=dry_run)) + else: + copy_file(src_name, dst_name, preserve_mode, + preserve_times, update, verbose=verbose, + dry_run=dry_run) + outputs.append(dst_name) + + return outputs + +def _build_cmdtuple(path, cmdtuples): + """Helper for remove_tree().""" + for f in os.listdir(path): + real_f = os.path.join(path,f) + if os.path.isdir(real_f) and not os.path.islink(real_f): + _build_cmdtuple(real_f, cmdtuples) + else: + cmdtuples.append((os.remove, real_f)) + cmdtuples.append((os.rmdir, path)) + +def remove_tree(directory, verbose=1, dry_run=0): + """Recursively remove an entire directory tree. + + Any errors are ignored (apart from being reported to stdout if 'verbose' + is true). + """ + global _path_created + + if verbose >= 1: + log.info("removing '%s' (and everything under it)", directory) + if dry_run: + return + cmdtuples = [] + _build_cmdtuple(directory, cmdtuples) + for cmd in cmdtuples: + try: + cmd[0](cmd[1]) + # remove dir from cache if it's already there + abspath = os.path.abspath(cmd[1]) + if abspath in _path_created: + del _path_created[abspath] + except OSError as exc: + log.warn("error removing %s: %s", directory, exc) + +def ensure_relative(path): + """Take the full path 'path', and make it a relative path. + + This is useful to make 'path' the second argument to os.path.join(). + """ + drive, path = os.path.splitdrive(path) + if path[0:1] == os.sep: + path = drive + path[1:] + return path diff --git a/setuptools/_distutils/dist.py b/setuptools/_distutils/dist.py new file mode 100644 index 00000000..37db4d6c --- /dev/null +++ b/setuptools/_distutils/dist.py @@ -0,0 +1,1257 @@ +"""distutils.dist + +Provides the Distribution class, which represents the module distribution +being built/installed/distributed. +""" + +import sys +import os +import re +from email import message_from_file + +try: + import warnings +except ImportError: + warnings = None + +from distutils.errors import * +from distutils.fancy_getopt import FancyGetopt, translate_longopt +from distutils.util import check_environ, strtobool, rfc822_escape +from distutils import log +from distutils.debug import DEBUG + +# Regex to define acceptable Distutils command names. This is not *quite* +# the same as a Python NAME -- I don't allow leading underscores. The fact +# that they're very similar is no coincidence; the default naming scheme is +# to look for a Python module named after the command. +command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') + + +def _ensure_list(value, fieldname): + if isinstance(value, str): + # a string containing comma separated values is okay. It will + # be converted to a list by Distribution.finalize_options(). + pass + elif not isinstance(value, list): + # passing a tuple or an iterator perhaps, warn and convert + typename = type(value).__name__ + msg = "Warning: '{fieldname}' should be a list, got type '{typename}'" + msg = msg.format(**locals()) + log.log(log.WARN, msg) + value = list(value) + return value + + +class Distribution: + """The core of the Distutils. Most of the work hiding behind 'setup' + is really done within a Distribution instance, which farms the work out + to the Distutils commands specified on the command line. + + Setup scripts will almost never instantiate Distribution directly, + unless the 'setup()' function is totally inadequate to their needs. + However, it is conceivable that a setup script might wish to subclass + Distribution for some specialized purpose, and then pass the subclass + to 'setup()' as the 'distclass' keyword argument. If so, it is + necessary to respect the expectations that 'setup' has of Distribution. + See the code for 'setup()', in core.py, for details. + """ + + # 'global_options' describes the command-line options that may be + # supplied to the setup script prior to any actual commands. + # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of + # these global options. This list should be kept to a bare minimum, + # since every global option is also valid as a command option -- and we + # don't want to pollute the commands with too many options that they + # have minimal control over. + # The fourth entry for verbose means that it can be repeated. + global_options = [ + ('verbose', 'v', "run verbosely (default)", 1), + ('quiet', 'q', "run quietly (turns verbosity off)"), + ('dry-run', 'n', "don't actually do anything"), + ('help', 'h', "show detailed help message"), + ('no-user-cfg', None, + 'ignore pydistutils.cfg in your home directory'), + ] + + # 'common_usage' is a short (2-3 line) string describing the common + # usage of the setup script. + common_usage = """\ +Common commands: (see '--help-commands' for more) + + setup.py build will build the package underneath 'build/' + setup.py install will install the package +""" + + # options that are not propagated to the commands + display_options = [ + ('help-commands', None, + "list all available commands"), + ('name', None, + "print package name"), + ('version', 'V', + "print package version"), + ('fullname', None, + "print -"), + ('author', None, + "print the author's name"), + ('author-email', None, + "print the author's email address"), + ('maintainer', None, + "print the maintainer's name"), + ('maintainer-email', None, + "print the maintainer's email address"), + ('contact', None, + "print the maintainer's name if known, else the author's"), + ('contact-email', None, + "print the maintainer's email address if known, else the author's"), + ('url', None, + "print the URL for this package"), + ('license', None, + "print the license of the package"), + ('licence', None, + "alias for --license"), + ('description', None, + "print the package description"), + ('long-description', None, + "print the long package description"), + ('platforms', None, + "print the list of platforms"), + ('classifiers', None, + "print the list of classifiers"), + ('keywords', None, + "print the list of keywords"), + ('provides', None, + "print the list of packages/modules provided"), + ('requires', None, + "print the list of packages/modules required"), + ('obsoletes', None, + "print the list of packages/modules made obsolete") + ] + display_option_names = [translate_longopt(x[0]) for x in display_options] + + # negative options are options that exclude other options + negative_opt = {'quiet': 'verbose'} + + # -- Creation/initialization methods ------------------------------- + + def __init__(self, attrs=None): + """Construct a new Distribution instance: initialize all the + attributes of a Distribution, and then use 'attrs' (a dictionary + mapping attribute names to values) to assign some of those + attributes their "real" values. (Any attributes not mentioned in + 'attrs' will be assigned to some null value: 0, None, an empty list + or dictionary, etc.) Most importantly, initialize the + 'command_obj' attribute to the empty dictionary; this will be + filled in with real command objects by 'parse_command_line()'. + """ + + # Default values for our command-line options + self.verbose = 1 + self.dry_run = 0 + self.help = 0 + for attr in self.display_option_names: + setattr(self, attr, 0) + + # Store the distribution meta-data (name, version, author, and so + # forth) in a separate object -- we're getting to have enough + # information here (and enough command-line options) that it's + # worth it. Also delegate 'get_XXX()' methods to the 'metadata' + # object in a sneaky and underhanded (but efficient!) way. + self.metadata = DistributionMetadata() + for basename in self.metadata._METHOD_BASENAMES: + method_name = "get_" + basename + setattr(self, method_name, getattr(self.metadata, method_name)) + + # 'cmdclass' maps command names to class objects, so we + # can 1) quickly figure out which class to instantiate when + # we need to create a new command object, and 2) have a way + # for the setup script to override command classes + self.cmdclass = {} + + # 'command_packages' is a list of packages in which commands + # are searched for. The factory for command 'foo' is expected + # to be named 'foo' in the module 'foo' in one of the packages + # named here. This list is searched from the left; an error + # is raised if no named package provides the command being + # searched for. (Always access using get_command_packages().) + self.command_packages = None + + # 'script_name' and 'script_args' are usually set to sys.argv[0] + # and sys.argv[1:], but they can be overridden when the caller is + # not necessarily a setup script run from the command-line. + self.script_name = None + self.script_args = None + + # 'command_options' is where we store command options between + # parsing them (from config files, the command-line, etc.) and when + # they are actually needed -- ie. when the command in question is + # instantiated. It is a dictionary of dictionaries of 2-tuples: + # command_options = { command_name : { option : (source, value) } } + self.command_options = {} + + # 'dist_files' is the list of (command, pyversion, file) that + # have been created by any dist commands run so far. This is + # filled regardless of whether the run is dry or not. pyversion + # gives sysconfig.get_python_version() if the dist file is + # specific to a Python version, 'any' if it is good for all + # Python versions on the target platform, and '' for a source + # file. pyversion should not be used to specify minimum or + # maximum required Python versions; use the metainfo for that + # instead. + self.dist_files = [] + + # These options are really the business of various commands, rather + # than of the Distribution itself. We provide aliases for them in + # Distribution as a convenience to the developer. + self.packages = None + self.package_data = {} + self.package_dir = None + self.py_modules = None + self.libraries = None + self.headers = None + self.ext_modules = None + self.ext_package = None + self.include_dirs = None + self.extra_path = None + self.scripts = None + self.data_files = None + self.password = '' + + # And now initialize bookkeeping stuff that can't be supplied by + # the caller at all. 'command_obj' maps command names to + # Command instances -- that's how we enforce that every command + # class is a singleton. + self.command_obj = {} + + # 'have_run' maps command names to boolean values; it keeps track + # of whether we have actually run a particular command, to make it + # cheap to "run" a command whenever we think we might need to -- if + # it's already been done, no need for expensive filesystem + # operations, we just check the 'have_run' dictionary and carry on. + # It's only safe to query 'have_run' for a command class that has + # been instantiated -- a false value will be inserted when the + # command object is created, and replaced with a true value when + # the command is successfully run. Thus it's probably best to use + # '.get()' rather than a straight lookup. + self.have_run = {} + + # Now we'll use the attrs dictionary (ultimately, keyword args from + # the setup script) to possibly override any or all of these + # distribution options. + + if attrs: + # Pull out the set of command options and work on them + # specifically. Note that this order guarantees that aliased + # command options will override any supplied redundantly + # through the general options dictionary. + options = attrs.get('options') + if options is not None: + del attrs['options'] + for (command, cmd_options) in options.items(): + opt_dict = self.get_option_dict(command) + for (opt, val) in cmd_options.items(): + opt_dict[opt] = ("setup script", val) + + if 'licence' in attrs: + attrs['license'] = attrs['licence'] + del attrs['licence'] + msg = "'licence' distribution option is deprecated; use 'license'" + if warnings is not None: + warnings.warn(msg) + else: + sys.stderr.write(msg + "\n") + + # Now work on the rest of the attributes. Any attribute that's + # not already defined is invalid! + for (key, val) in attrs.items(): + if hasattr(self.metadata, "set_" + key): + getattr(self.metadata, "set_" + key)(val) + elif hasattr(self.metadata, key): + setattr(self.metadata, key, val) + elif hasattr(self, key): + setattr(self, key, val) + else: + msg = "Unknown distribution option: %s" % repr(key) + warnings.warn(msg) + + # no-user-cfg is handled before other command line args + # because other args override the config files, and this + # one is needed before we can load the config files. + # If attrs['script_args'] wasn't passed, assume false. + # + # This also make sure we just look at the global options + self.want_user_cfg = True + + if self.script_args is not None: + for arg in self.script_args: + if not arg.startswith('-'): + break + if arg == '--no-user-cfg': + self.want_user_cfg = False + break + + self.finalize_options() + + def get_option_dict(self, command): + """Get the option dictionary for a given command. If that + command's option dictionary hasn't been created yet, then create it + and return the new dictionary; otherwise, return the existing + option dictionary. + """ + dict = self.command_options.get(command) + if dict is None: + dict = self.command_options[command] = {} + return dict + + def dump_option_dicts(self, header=None, commands=None, indent=""): + from pprint import pformat + + if commands is None: # dump all command option dicts + commands = sorted(self.command_options.keys()) + + if header is not None: + self.announce(indent + header) + indent = indent + " " + + if not commands: + self.announce(indent + "no commands known yet") + return + + for cmd_name in commands: + opt_dict = self.command_options.get(cmd_name) + if opt_dict is None: + self.announce(indent + + "no option dict for '%s' command" % cmd_name) + else: + self.announce(indent + + "option dict for '%s' command:" % cmd_name) + out = pformat(opt_dict) + for line in out.split('\n'): + self.announce(indent + " " + line) + + # -- Config file finding/parsing methods --------------------------- + + def find_config_files(self): + """Find as many configuration files as should be processed for this + platform, and return a list of filenames in the order in which they + should be parsed. The filenames returned are guaranteed to exist + (modulo nasty race conditions). + + There are three possible config files: distutils.cfg in the + Distutils installation directory (ie. where the top-level + Distutils __inst__.py file lives), a file in the user's home + directory named .pydistutils.cfg on Unix and pydistutils.cfg + on Windows/Mac; and setup.cfg in the current directory. + + The file in the user's home directory can be disabled with the + --no-user-cfg option. + """ + files = [] + check_environ() + + # Where to look for the system-wide Distutils config file + sys_dir = os.path.dirname(sys.modules['distutils'].__file__) + + # Look for the system config file + sys_file = os.path.join(sys_dir, "distutils.cfg") + if os.path.isfile(sys_file): + files.append(sys_file) + + # What to call the per-user config file + if os.name == 'posix': + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + + # And look for the user config file + if self.want_user_cfg: + user_file = os.path.join(os.path.expanduser('~'), user_filename) + if os.path.isfile(user_file): + files.append(user_file) + + # All platforms support local setup.cfg + local_file = "setup.cfg" + if os.path.isfile(local_file): + files.append(local_file) + + if DEBUG: + self.announce("using config files: %s" % ', '.join(files)) + + return files + + def parse_config_files(self, filenames=None): + from configparser import ConfigParser + + # Ignore install directory options if we have a venv + if sys.prefix != sys.base_prefix: + ignore_options = [ + 'install-base', 'install-platbase', 'install-lib', + 'install-platlib', 'install-purelib', 'install-headers', + 'install-scripts', 'install-data', 'prefix', 'exec-prefix', + 'home', 'user', 'root'] + else: + ignore_options = [] + + ignore_options = frozenset(ignore_options) + + if filenames is None: + filenames = self.find_config_files() + + if DEBUG: + self.announce("Distribution.parse_config_files():") + + parser = ConfigParser() + for filename in filenames: + if DEBUG: + self.announce(" reading %s" % filename) + parser.read(filename) + for section in parser.sections(): + options = parser.options(section) + opt_dict = self.get_option_dict(section) + + for opt in options: + if opt != '__name__' and opt not in ignore_options: + val = parser.get(section,opt) + opt = opt.replace('-', '_') + opt_dict[opt] = (filename, val) + + # Make the ConfigParser forget everything (so we retain + # the original filenames that options come from) + parser.__init__() + + # If there was a "global" section in the config file, use it + # to set Distribution options. + + if 'global' in self.command_options: + for (opt, (src, val)) in self.command_options['global'].items(): + alias = self.negative_opt.get(opt) + try: + if alias: + setattr(self, alias, not strtobool(val)) + elif opt in ('verbose', 'dry_run'): # ugh! + setattr(self, opt, strtobool(val)) + else: + setattr(self, opt, val) + except ValueError as msg: + raise DistutilsOptionError(msg) + + # -- Command-line parsing methods ---------------------------------- + + def parse_command_line(self): + """Parse the setup script's command line, taken from the + 'script_args' instance attribute (which defaults to 'sys.argv[1:]' + -- see 'setup()' in core.py). This list is first processed for + "global options" -- options that set attributes of the Distribution + instance. Then, it is alternately scanned for Distutils commands + and options for that command. Each new command terminates the + options for the previous command. The allowed options for a + command are determined by the 'user_options' attribute of the + command class -- thus, we have to be able to load command classes + in order to parse the command line. Any error in that 'options' + attribute raises DistutilsGetoptError; any error on the + command-line raises DistutilsArgError. If no Distutils commands + were found on the command line, raises DistutilsArgError. Return + true if command-line was successfully parsed and we should carry + on with executing commands; false if no errors but we shouldn't + execute commands (currently, this only happens if user asks for + help). + """ + # + # We now have enough information to show the Macintosh dialog + # that allows the user to interactively specify the "command line". + # + toplevel_options = self._get_toplevel_options() + + # We have to parse the command line a bit at a time -- global + # options, then the first command, then its options, and so on -- + # because each command will be handled by a different class, and + # the options that are valid for a particular class aren't known + # until we have loaded the command class, which doesn't happen + # until we know what the command is. + + self.commands = [] + parser = FancyGetopt(toplevel_options + self.display_options) + parser.set_negative_aliases(self.negative_opt) + parser.set_aliases({'licence': 'license'}) + args = parser.getopt(args=self.script_args, object=self) + option_order = parser.get_option_order() + log.set_verbosity(self.verbose) + + # for display options we return immediately + if self.handle_display_options(option_order): + return + while args: + args = self._parse_command_opts(parser, args) + if args is None: # user asked for help (and got it) + return + + # Handle the cases of --help as a "global" option, ie. + # "setup.py --help" and "setup.py --help command ...". For the + # former, we show global options (--verbose, --dry-run, etc.) + # and display-only options (--name, --version, etc.); for the + # latter, we omit the display-only options and show help for + # each command listed on the command line. + if self.help: + self._show_help(parser, + display_options=len(self.commands) == 0, + commands=self.commands) + return + + # Oops, no commands found -- an end-user error + if not self.commands: + raise DistutilsArgError("no commands supplied") + + # All is well: return true + return True + + def _get_toplevel_options(self): + """Return the non-display options recognized at the top level. + + This includes options that are recognized *only* at the top + level as well as options recognized for commands. + """ + return self.global_options + [ + ("command-packages=", None, + "list of packages that provide distutils commands"), + ] + + def _parse_command_opts(self, parser, args): + """Parse the command-line options for a single command. + 'parser' must be a FancyGetopt instance; 'args' must be the list + of arguments, starting with the current command (whose options + we are about to parse). Returns a new version of 'args' with + the next command at the front of the list; will be the empty + list if there are no more commands on the command line. Returns + None if the user asked for help on this command. + """ + # late import because of mutual dependence between these modules + from distutils.cmd import Command + + # Pull the current command from the head of the command line + command = args[0] + if not command_re.match(command): + raise SystemExit("invalid command name '%s'" % command) + self.commands.append(command) + + # Dig up the command class that implements this command, so we + # 1) know that it's a valid command, and 2) know which options + # it takes. + try: + cmd_class = self.get_command_class(command) + except DistutilsModuleError as msg: + raise DistutilsArgError(msg) + + # Require that the command class be derived from Command -- want + # to be sure that the basic "command" interface is implemented. + if not issubclass(cmd_class, Command): + raise DistutilsClassError( + "command class %s must subclass Command" % cmd_class) + + # Also make sure that the command object provides a list of its + # known options. + if not (hasattr(cmd_class, 'user_options') and + isinstance(cmd_class.user_options, list)): + msg = ("command class %s must provide " + "'user_options' attribute (a list of tuples)") + raise DistutilsClassError(msg % cmd_class) + + # If the command class has a list of negative alias options, + # merge it in with the global negative aliases. + negative_opt = self.negative_opt + if hasattr(cmd_class, 'negative_opt'): + negative_opt = negative_opt.copy() + negative_opt.update(cmd_class.negative_opt) + + # Check for help_options in command class. They have a different + # format (tuple of four) so we need to preprocess them here. + if (hasattr(cmd_class, 'help_options') and + isinstance(cmd_class.help_options, list)): + help_options = fix_help_options(cmd_class.help_options) + else: + help_options = [] + + # All commands support the global options too, just by adding + # in 'global_options'. + parser.set_option_table(self.global_options + + cmd_class.user_options + + help_options) + parser.set_negative_aliases(negative_opt) + (args, opts) = parser.getopt(args[1:]) + if hasattr(opts, 'help') and opts.help: + self._show_help(parser, display_options=0, commands=[cmd_class]) + return + + if (hasattr(cmd_class, 'help_options') and + isinstance(cmd_class.help_options, list)): + help_option_found=0 + for (help_option, short, desc, func) in cmd_class.help_options: + if hasattr(opts, parser.get_attr_name(help_option)): + help_option_found=1 + if callable(func): + func() + else: + raise DistutilsClassError( + "invalid help function %r for help option '%s': " + "must be a callable object (function, etc.)" + % (func, help_option)) + + if help_option_found: + return + + # Put the options from the command-line into their official + # holding pen, the 'command_options' dictionary. + opt_dict = self.get_option_dict(command) + for (name, value) in vars(opts).items(): + opt_dict[name] = ("command line", value) + + return args + + def finalize_options(self): + """Set final values for all the options on the Distribution + instance, analogous to the .finalize_options() method of Command + objects. + """ + for attr in ('keywords', 'platforms'): + value = getattr(self.metadata, attr) + if value is None: + continue + if isinstance(value, str): + value = [elm.strip() for elm in value.split(',')] + setattr(self.metadata, attr, value) + + def _show_help(self, parser, global_options=1, display_options=1, + commands=[]): + """Show help for the setup script command-line in the form of + several lists of command-line options. 'parser' should be a + FancyGetopt instance; do not expect it to be returned in the + same state, as its option table will be reset to make it + generate the correct help text. + + If 'global_options' is true, lists the global options: + --verbose, --dry-run, etc. If 'display_options' is true, lists + the "display-only" options: --name, --version, etc. Finally, + lists per-command help for every command name or command class + in 'commands'. + """ + # late import because of mutual dependence between these modules + from distutils.core import gen_usage + from distutils.cmd import Command + + if global_options: + if display_options: + options = self._get_toplevel_options() + else: + options = self.global_options + parser.set_option_table(options) + parser.print_help(self.common_usage + "\nGlobal options:") + print('') + + if display_options: + parser.set_option_table(self.display_options) + parser.print_help( + "Information display options (just display " + + "information, ignore any commands)") + print('') + + for command in self.commands: + if isinstance(command, type) and issubclass(command, Command): + klass = command + else: + klass = self.get_command_class(command) + if (hasattr(klass, 'help_options') and + isinstance(klass.help_options, list)): + parser.set_option_table(klass.user_options + + fix_help_options(klass.help_options)) + else: + parser.set_option_table(klass.user_options) + parser.print_help("Options for '%s' command:" % klass.__name__) + print('') + + print(gen_usage(self.script_name)) + + def handle_display_options(self, option_order): + """If there were any non-global "display-only" options + (--help-commands or the metadata display options) on the command + line, display the requested info and return true; else return + false. + """ + from distutils.core import gen_usage + + # User just wants a list of commands -- we'll print it out and stop + # processing now (ie. if they ran "setup --help-commands foo bar", + # we ignore "foo bar"). + if self.help_commands: + self.print_commands() + print('') + print(gen_usage(self.script_name)) + return 1 + + # If user supplied any of the "display metadata" options, then + # display that metadata in the order in which the user supplied the + # metadata options. + any_display_options = 0 + is_display_option = {} + for option in self.display_options: + is_display_option[option[0]] = 1 + + for (opt, val) in option_order: + if val and is_display_option.get(opt): + opt = translate_longopt(opt) + value = getattr(self.metadata, "get_"+opt)() + if opt in ['keywords', 'platforms']: + print(','.join(value)) + elif opt in ('classifiers', 'provides', 'requires', + 'obsoletes'): + print('\n'.join(value)) + else: + print(value) + any_display_options = 1 + + return any_display_options + + def print_command_list(self, commands, header, max_length): + """Print a subset of the list of all commands -- used by + 'print_commands()'. + """ + print(header + ":") + + for cmd in commands: + klass = self.cmdclass.get(cmd) + if not klass: + klass = self.get_command_class(cmd) + try: + description = klass.description + except AttributeError: + description = "(no description available)" + + print(" %-*s %s" % (max_length, cmd, description)) + + def print_commands(self): + """Print out a help message listing all available commands with a + description of each. The list is divided into "standard commands" + (listed in distutils.command.__all__) and "extra commands" + (mentioned in self.cmdclass, but not a standard command). The + descriptions come from the command class attribute + 'description'. + """ + import distutils.command + std_commands = distutils.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass.keys(): + if not is_std.get(cmd): + extra_commands.append(cmd) + + max_length = 0 + for cmd in (std_commands + extra_commands): + if len(cmd) > max_length: + max_length = len(cmd) + + self.print_command_list(std_commands, + "Standard commands", + max_length) + if extra_commands: + print() + self.print_command_list(extra_commands, + "Extra commands", + max_length) + + def get_command_list(self): + """Get a list of (command, description) tuples. + The list is divided into "standard commands" (listed in + distutils.command.__all__) and "extra commands" (mentioned in + self.cmdclass, but not a standard command). The descriptions come + from the command class attribute 'description'. + """ + # Currently this is only used on Mac OS, for the Mac-only GUI + # Distutils interface (by Jack Jansen) + import distutils.command + std_commands = distutils.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass.keys(): + if not is_std.get(cmd): + extra_commands.append(cmd) + + rv = [] + for cmd in (std_commands + extra_commands): + klass = self.cmdclass.get(cmd) + if not klass: + klass = self.get_command_class(cmd) + try: + description = klass.description + except AttributeError: + description = "(no description available)" + rv.append((cmd, description)) + return rv + + # -- Command class/object methods ---------------------------------- + + def get_command_packages(self): + """Return a list of packages from which commands are loaded.""" + pkgs = self.command_packages + if not isinstance(pkgs, list): + if pkgs is None: + pkgs = '' + pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != ''] + if "distutils.command" not in pkgs: + pkgs.insert(0, "distutils.command") + self.command_packages = pkgs + return pkgs + + def get_command_class(self, command): + """Return the class that implements the Distutils command named by + 'command'. First we check the 'cmdclass' dictionary; if the + command is mentioned there, we fetch the class object from the + dictionary and return it. Otherwise we load the command module + ("distutils.command." + command) and fetch the command class from + the module. The loaded class is also stored in 'cmdclass' + to speed future calls to 'get_command_class()'. + + Raises DistutilsModuleError if the expected module could not be + found, or if that module does not define the expected class. + """ + klass = self.cmdclass.get(command) + if klass: + return klass + + for pkgname in self.get_command_packages(): + module_name = "%s.%s" % (pkgname, command) + klass_name = command + + try: + __import__(module_name) + module = sys.modules[module_name] + except ImportError: + continue + + try: + klass = getattr(module, klass_name) + except AttributeError: + raise DistutilsModuleError( + "invalid command '%s' (no class '%s' in module '%s')" + % (command, klass_name, module_name)) + + self.cmdclass[command] = klass + return klass + + raise DistutilsModuleError("invalid command '%s'" % command) + + def get_command_obj(self, command, create=1): + """Return the command object for 'command'. Normally this object + is cached on a previous call to 'get_command_obj()'; if no command + object for 'command' is in the cache, then we either create and + return it (if 'create' is true) or return None. + """ + cmd_obj = self.command_obj.get(command) + if not cmd_obj and create: + if DEBUG: + self.announce("Distribution.get_command_obj(): " + "creating '%s' command object" % command) + + klass = self.get_command_class(command) + cmd_obj = self.command_obj[command] = klass(self) + self.have_run[command] = 0 + + # Set any options that were supplied in config files + # or on the command line. (NB. support for error + # reporting is lame here: any errors aren't reported + # until 'finalize_options()' is called, which means + # we won't report the source of the error.) + options = self.command_options.get(command) + if options: + self._set_command_options(cmd_obj, options) + + return cmd_obj + + def _set_command_options(self, command_obj, option_dict=None): + """Set the options for 'command_obj' from 'option_dict'. Basically + this means copying elements of a dictionary ('option_dict') to + attributes of an instance ('command'). + + 'command_obj' must be a Command instance. If 'option_dict' is not + supplied, uses the standard option dictionary for this command + (from 'self.command_options'). + """ + command_name = command_obj.get_command_name() + if option_dict is None: + option_dict = self.get_option_dict(command_name) + + if DEBUG: + self.announce(" setting options for '%s' command:" % command_name) + for (option, (source, value)) in option_dict.items(): + if DEBUG: + self.announce(" %s = %s (from %s)" % (option, value, + source)) + try: + bool_opts = [translate_longopt(o) + for o in command_obj.boolean_options] + except AttributeError: + bool_opts = [] + try: + neg_opt = command_obj.negative_opt + except AttributeError: + neg_opt = {} + + try: + is_string = isinstance(value, str) + if option in neg_opt and is_string: + setattr(command_obj, neg_opt[option], not strtobool(value)) + elif option in bool_opts and is_string: + setattr(command_obj, option, strtobool(value)) + elif hasattr(command_obj, option): + setattr(command_obj, option, value) + else: + raise DistutilsOptionError( + "error in %s: command '%s' has no such option '%s'" + % (source, command_name, option)) + except ValueError as msg: + raise DistutilsOptionError(msg) + + def reinitialize_command(self, command, reinit_subcommands=0): + """Reinitializes a command to the state it was in when first + returned by 'get_command_obj()': ie., initialized but not yet + finalized. This provides the opportunity to sneak option + values in programmatically, overriding or supplementing + user-supplied values from the config files and command line. + You'll have to re-finalize the command object (by calling + 'finalize_options()' or 'ensure_finalized()') before using it for + real. + + 'command' should be a command name (string) or command object. If + 'reinit_subcommands' is true, also reinitializes the command's + sub-commands, as declared by the 'sub_commands' class attribute (if + it has one). See the "install" command for an example. Only + reinitializes the sub-commands that actually matter, ie. those + whose test predicates return true. + + Returns the reinitialized command object. + """ + from distutils.cmd import Command + if not isinstance(command, Command): + command_name = command + command = self.get_command_obj(command_name) + else: + command_name = command.get_command_name() + + if not command.finalized: + return command + command.initialize_options() + command.finalized = 0 + self.have_run[command_name] = 0 + self._set_command_options(command) + + if reinit_subcommands: + for sub in command.get_sub_commands(): + self.reinitialize_command(sub, reinit_subcommands) + + return command + + # -- Methods that operate on the Distribution ---------------------- + + def announce(self, msg, level=log.INFO): + log.log(level, msg) + + def run_commands(self): + """Run each command that was seen on the setup script command line. + Uses the list of commands found and cache of command objects + created by 'get_command_obj()'. + """ + for cmd in self.commands: + self.run_command(cmd) + + # -- Methods that operate on its Commands -------------------------- + + def run_command(self, command): + """Do whatever it takes to run a command (including nothing at all, + if the command has already been run). Specifically: if we have + already created and run the command named by 'command', return + silently without doing anything. If the command named by 'command' + doesn't even have a command object yet, create one. Then invoke + 'run()' on that command object (or an existing one). + """ + # Already been here, done that? then return silently. + if self.have_run.get(command): + return + + log.info("running %s", command) + cmd_obj = self.get_command_obj(command) + cmd_obj.ensure_finalized() + cmd_obj.run() + self.have_run[command] = 1 + + # -- Distribution query methods ------------------------------------ + + def has_pure_modules(self): + return len(self.packages or self.py_modules or []) > 0 + + def has_ext_modules(self): + return self.ext_modules and len(self.ext_modules) > 0 + + def has_c_libraries(self): + return self.libraries and len(self.libraries) > 0 + + def has_modules(self): + return self.has_pure_modules() or self.has_ext_modules() + + def has_headers(self): + return self.headers and len(self.headers) > 0 + + def has_scripts(self): + return self.scripts and len(self.scripts) > 0 + + def has_data_files(self): + return self.data_files and len(self.data_files) > 0 + + def is_pure(self): + return (self.has_pure_modules() and + not self.has_ext_modules() and + not self.has_c_libraries()) + + # -- Metadata query methods ---------------------------------------- + + # If you're looking for 'get_name()', 'get_version()', and so forth, + # they are defined in a sneaky way: the constructor binds self.get_XXX + # to self.metadata.get_XXX. The actual code is in the + # DistributionMetadata class, below. + +class DistributionMetadata: + """Dummy class to hold the distribution meta-data: name, version, + author, and so forth. + """ + + _METHOD_BASENAMES = ("name", "version", "author", "author_email", + "maintainer", "maintainer_email", "url", + "license", "description", "long_description", + "keywords", "platforms", "fullname", "contact", + "contact_email", "classifiers", "download_url", + # PEP 314 + "provides", "requires", "obsoletes", + ) + + def __init__(self, path=None): + if path is not None: + self.read_pkg_file(open(path)) + else: + self.name = None + self.version = None + self.author = None + self.author_email = None + self.maintainer = None + self.maintainer_email = None + self.url = None + self.license = None + self.description = None + self.long_description = None + self.keywords = None + self.platforms = None + self.classifiers = None + self.download_url = None + # PEP 314 + self.provides = None + self.requires = None + self.obsoletes = None + + def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = message_from_file(file) + + def _read_field(name): + value = msg[name] + if value == 'UNKNOWN': + return None + return value + + def _read_list(name): + values = msg.get_all(name, None) + if values == []: + return None + return values + + metadata_version = msg['metadata-version'] + self.name = _read_field('name') + self.version = _read_field('version') + self.description = _read_field('summary') + # we are filling author only. + self.author = _read_field('author') + self.maintainer = None + self.author_email = _read_field('author-email') + self.maintainer_email = None + self.url = _read_field('home-page') + self.license = _read_field('license') + + if 'download-url' in msg: + self.download_url = _read_field('download-url') + else: + self.download_url = None + + self.long_description = _read_field('description') + self.description = _read_field('summary') + + if 'keywords' in msg: + self.keywords = _read_field('keywords').split(',') + + self.platforms = _read_list('platform') + self.classifiers = _read_list('classifier') + + # PEP 314 - these fields only exist in 1.1 + if metadata_version == '1.1': + self.requires = _read_list('requires') + self.provides = _read_list('provides') + self.obsoletes = _read_list('obsoletes') + else: + self.requires = None + self.provides = None + self.obsoletes = None + + def write_pkg_info(self, base_dir): + """Write the PKG-INFO file into the release tree. + """ + with open(os.path.join(base_dir, 'PKG-INFO'), 'w', + encoding='UTF-8') as pkg_info: + self.write_pkg_file(pkg_info) + + def write_pkg_file(self, file): + """Write the PKG-INFO format data to a file object. + """ + version = '1.0' + if (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): + version = '1.1' + + file.write('Metadata-Version: %s\n' % version) + file.write('Name: %s\n' % self.get_name()) + file.write('Version: %s\n' % self.get_version()) + file.write('Summary: %s\n' % self.get_description()) + file.write('Home-page: %s\n' % self.get_url()) + file.write('Author: %s\n' % self.get_contact()) + file.write('Author-email: %s\n' % self.get_contact_email()) + file.write('License: %s\n' % self.get_license()) + if self.download_url: + file.write('Download-URL: %s\n' % self.download_url) + + long_desc = rfc822_escape(self.get_long_description()) + file.write('Description: %s\n' % long_desc) + + keywords = ','.join(self.get_keywords()) + if keywords: + file.write('Keywords: %s\n' % keywords) + + self._write_list(file, 'Platform', self.get_platforms()) + self._write_list(file, 'Classifier', self.get_classifiers()) + + # PEP 314 + self._write_list(file, 'Requires', self.get_requires()) + self._write_list(file, 'Provides', self.get_provides()) + self._write_list(file, 'Obsoletes', self.get_obsoletes()) + + def _write_list(self, file, name, values): + for value in values: + file.write('%s: %s\n' % (name, value)) + + # -- Metadata query methods ---------------------------------------- + + def get_name(self): + return self.name or "UNKNOWN" + + def get_version(self): + return self.version or "0.0.0" + + def get_fullname(self): + return "%s-%s" % (self.get_name(), self.get_version()) + + def get_author(self): + return self.author or "UNKNOWN" + + def get_author_email(self): + return self.author_email or "UNKNOWN" + + def get_maintainer(self): + return self.maintainer or "UNKNOWN" + + def get_maintainer_email(self): + return self.maintainer_email or "UNKNOWN" + + def get_contact(self): + return self.maintainer or self.author or "UNKNOWN" + + def get_contact_email(self): + return self.maintainer_email or self.author_email or "UNKNOWN" + + def get_url(self): + return self.url or "UNKNOWN" + + def get_license(self): + return self.license or "UNKNOWN" + get_licence = get_license + + def get_description(self): + return self.description or "UNKNOWN" + + def get_long_description(self): + return self.long_description or "UNKNOWN" + + def get_keywords(self): + return self.keywords or [] + + def set_keywords(self, value): + self.keywords = _ensure_list(value, 'keywords') + + def get_platforms(self): + return self.platforms or ["UNKNOWN"] + + def set_platforms(self, value): + self.platforms = _ensure_list(value, 'platforms') + + def get_classifiers(self): + return self.classifiers or [] + + def set_classifiers(self, value): + self.classifiers = _ensure_list(value, 'classifiers') + + def get_download_url(self): + return self.download_url or "UNKNOWN" + + # PEP 314 + def get_requires(self): + return self.requires or [] + + def set_requires(self, value): + import distutils.versionpredicate + for v in value: + distutils.versionpredicate.VersionPredicate(v) + self.requires = list(value) + + def get_provides(self): + return self.provides or [] + + def set_provides(self, value): + value = [v.strip() for v in value] + for v in value: + import distutils.versionpredicate + distutils.versionpredicate.split_provision(v) + self.provides = value + + def get_obsoletes(self): + return self.obsoletes or [] + + def set_obsoletes(self, value): + import distutils.versionpredicate + for v in value: + distutils.versionpredicate.VersionPredicate(v) + self.obsoletes = list(value) + +def fix_help_options(options): + """Convert a 4-tuple 'help_options' list as found in various command + classes to the 3-tuple form required by FancyGetopt. + """ + new_options = [] + for help_tuple in options: + new_options.append(help_tuple[0:3]) + return new_options diff --git a/setuptools/_distutils/errors.py b/setuptools/_distutils/errors.py new file mode 100644 index 00000000..8b93059e --- /dev/null +++ b/setuptools/_distutils/errors.py @@ -0,0 +1,97 @@ +"""distutils.errors + +Provides exceptions used by the Distutils modules. Note that Distutils +modules may raise standard exceptions; in particular, SystemExit is +usually raised for errors that are obviously the end-user's fault +(eg. bad command-line arguments). + +This module is safe to use in "from ... import *" mode; it only exports +symbols whose names start with "Distutils" and end with "Error".""" + +class DistutilsError (Exception): + """The root of all Distutils evil.""" + pass + +class DistutilsModuleError (DistutilsError): + """Unable to load an expected module, or to find an expected class + within some module (in particular, command modules and classes).""" + pass + +class DistutilsClassError (DistutilsError): + """Some command class (or possibly distribution class, if anyone + feels a need to subclass Distribution) is found not to be holding + up its end of the bargain, ie. implementing some part of the + "command "interface.""" + pass + +class DistutilsGetoptError (DistutilsError): + """The option table provided to 'fancy_getopt()' is bogus.""" + pass + +class DistutilsArgError (DistutilsError): + """Raised by fancy_getopt in response to getopt.error -- ie. an + error in the command line usage.""" + pass + +class DistutilsFileError (DistutilsError): + """Any problems in the filesystem: expected file not found, etc. + Typically this is for problems that we detect before OSError + could be raised.""" + pass + +class DistutilsOptionError (DistutilsError): + """Syntactic/semantic errors in command options, such as use of + mutually conflicting options, or inconsistent options, + badly-spelled values, etc. No distinction is made between option + values originating in the setup script, the command line, config + files, or what-have-you -- but if we *know* something originated in + the setup script, we'll raise DistutilsSetupError instead.""" + pass + +class DistutilsSetupError (DistutilsError): + """For errors that can be definitely blamed on the setup script, + such as invalid keyword arguments to 'setup()'.""" + pass + +class DistutilsPlatformError (DistutilsError): + """We don't know how to do something on the current platform (but + we do know how to do it on some platform) -- eg. trying to compile + C files on a platform not supported by a CCompiler subclass.""" + pass + +class DistutilsExecError (DistutilsError): + """Any problems executing an external program (such as the C + compiler, when compiling C files).""" + pass + +class DistutilsInternalError (DistutilsError): + """Internal inconsistencies or impossibilities (obviously, this + should never be seen if the code is working!).""" + pass + +class DistutilsTemplateError (DistutilsError): + """Syntax error in a file list template.""" + +class DistutilsByteCompileError(DistutilsError): + """Byte compile error.""" + +# Exception classes used by the CCompiler implementation classes +class CCompilerError (Exception): + """Some compile/link operation failed.""" + +class PreprocessError (CCompilerError): + """Failure to preprocess one or more C/C++ files.""" + +class CompileError (CCompilerError): + """Failure to compile one or more C/C++ source files.""" + +class LibError (CCompilerError): + """Failure to create a static library from one or more C/C++ object + files.""" + +class LinkError (CCompilerError): + """Failure to link one or more C/C++ object files into an executable + or shared library file.""" + +class UnknownFileError (CCompilerError): + """Attempt to process an unknown file type.""" diff --git a/setuptools/_distutils/extension.py b/setuptools/_distutils/extension.py new file mode 100644 index 00000000..c507da36 --- /dev/null +++ b/setuptools/_distutils/extension.py @@ -0,0 +1,240 @@ +"""distutils.extension + +Provides the Extension class, used to describe C/C++ extension +modules in setup scripts.""" + +import os +import warnings + +# This class is really only used by the "build_ext" command, so it might +# make sense to put it in distutils.command.build_ext. However, that +# module is already big enough, and I want to make this class a bit more +# complex to simplify some common cases ("foo" module in "foo.c") and do +# better error-checking ("foo.c" actually exists). +# +# Also, putting this in build_ext.py means every setup script would have to +# import that large-ish module (indirectly, through distutils.core) in +# order to do anything. + +class Extension: + """Just a collection of attributes that describes an extension + module and everything needed to build it (hopefully in a portable + way, but there are hooks that let you be as unportable as you need). + + Instance attributes: + name : string + the full name of the extension, including any packages -- ie. + *not* a filename or pathname, but Python dotted name + sources : [string] + list of source filenames, relative to the distribution root + (where the setup script lives), in Unix form (slash-separated) + for portability. Source files may be C, C++, SWIG (.i), + platform-specific resource files, or whatever else is recognized + by the "build_ext" command as source for a Python extension. + include_dirs : [string] + list of directories to search for C/C++ header files (in Unix + form for portability) + define_macros : [(name : string, value : string|None)] + list of macros to define; each macro is defined using a 2-tuple, + where 'value' is either the string to define it to or None to + define it without a particular value (equivalent of "#define + FOO" in source or -DFOO on Unix C compiler command line) + undef_macros : [string] + list of macros to undefine explicitly + library_dirs : [string] + list of directories to search for C/C++ libraries at link time + libraries : [string] + list of library names (not filenames or paths) to link against + runtime_library_dirs : [string] + list of directories to search for C/C++ libraries at run time + (for shared extensions, this is when the extension is loaded) + extra_objects : [string] + list of extra files to link with (eg. object files not implied + by 'sources', static library that must be explicitly specified, + binary resource files, etc.) + extra_compile_args : [string] + any extra platform- and compiler-specific information to use + when compiling the source files in 'sources'. For platforms and + compilers where "command line" makes sense, this is typically a + list of command-line arguments, but for other platforms it could + be anything. + extra_link_args : [string] + any extra platform- and compiler-specific information to use + when linking object files together to create the extension (or + to create a new static Python interpreter). Similar + interpretation as for 'extra_compile_args'. + export_symbols : [string] + list of symbols to be exported from a shared extension. Not + used on all platforms, and not generally necessary for Python + extensions, which typically export exactly one symbol: "init" + + extension_name. + swig_opts : [string] + any extra options to pass to SWIG if a source file has the .i + extension. + depends : [string] + list of files that the extension depends on + language : string + extension language (i.e. "c", "c++", "objc"). Will be detected + from the source extensions if not provided. + optional : boolean + specifies that a build failure in the extension should not abort the + build process, but simply not install the failing extension. + """ + + # When adding arguments to this constructor, be sure to update + # setup_keywords in core.py. + def __init__(self, name, sources, + include_dirs=None, + define_macros=None, + undef_macros=None, + library_dirs=None, + libraries=None, + runtime_library_dirs=None, + extra_objects=None, + extra_compile_args=None, + extra_link_args=None, + export_symbols=None, + swig_opts = None, + depends=None, + language=None, + optional=None, + **kw # To catch unknown keywords + ): + if not isinstance(name, str): + raise AssertionError("'name' must be a string") + if not (isinstance(sources, list) and + all(isinstance(v, str) for v in sources)): + raise AssertionError("'sources' must be a list of strings") + + self.name = name + self.sources = sources + self.include_dirs = include_dirs or [] + self.define_macros = define_macros or [] + self.undef_macros = undef_macros or [] + self.library_dirs = library_dirs or [] + self.libraries = libraries or [] + self.runtime_library_dirs = runtime_library_dirs or [] + self.extra_objects = extra_objects or [] + self.extra_compile_args = extra_compile_args or [] + self.extra_link_args = extra_link_args or [] + self.export_symbols = export_symbols or [] + self.swig_opts = swig_opts or [] + self.depends = depends or [] + self.language = language + self.optional = optional + + # If there are unknown keyword options, warn about them + if len(kw) > 0: + options = [repr(option) for option in kw] + options = ', '.join(sorted(options)) + msg = "Unknown Extension options: %s" % options + warnings.warn(msg) + + def __repr__(self): + return '<%s.%s(%r) at %#x>' % ( + self.__class__.__module__, + self.__class__.__qualname__, + self.name, + id(self)) + + +def read_setup_file(filename): + """Reads a Setup file and returns Extension instances.""" + from distutils.sysconfig import (parse_makefile, expand_makefile_vars, + _variable_rx) + + from distutils.text_file import TextFile + from distutils.util import split_quoted + + # First pass over the file to gather "VAR = VALUE" assignments. + vars = parse_makefile(filename) + + # Second pass to gobble up the real content: lines of the form + # ... [ ...] [ ...] [ ...] + file = TextFile(filename, + strip_comments=1, skip_blanks=1, join_lines=1, + lstrip_ws=1, rstrip_ws=1) + try: + extensions = [] + + while True: + line = file.readline() + if line is None: # eof + break + if _variable_rx.match(line): # VAR=VALUE, handled in first pass + continue + + if line[0] == line[-1] == "*": + file.warn("'%s' lines not handled yet" % line) + continue + + line = expand_makefile_vars(line, vars) + words = split_quoted(line) + + # NB. this parses a slightly different syntax than the old + # makesetup script: here, there must be exactly one extension per + # line, and it must be the first word of the line. I have no idea + # why the old syntax supported multiple extensions per line, as + # they all wind up being the same. + + module = words[0] + ext = Extension(module, []) + append_next_word = None + + for word in words[1:]: + if append_next_word is not None: + append_next_word.append(word) + append_next_word = None + continue + + suffix = os.path.splitext(word)[1] + switch = word[0:2] ; value = word[2:] + + if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): + # hmm, should we do something about C vs. C++ sources? + # or leave it up to the CCompiler implementation to + # worry about? + ext.sources.append(word) + elif switch == "-I": + ext.include_dirs.append(value) + elif switch == "-D": + equals = value.find("=") + if equals == -1: # bare "-DFOO" -- no value + ext.define_macros.append((value, None)) + else: # "-DFOO=blah" + ext.define_macros.append((value[0:equals], + value[equals+2:])) + elif switch == "-U": + ext.undef_macros.append(value) + elif switch == "-C": # only here 'cause makesetup has it! + ext.extra_compile_args.append(word) + elif switch == "-l": + ext.libraries.append(value) + elif switch == "-L": + ext.library_dirs.append(value) + elif switch == "-R": + ext.runtime_library_dirs.append(value) + elif word == "-rpath": + append_next_word = ext.runtime_library_dirs + elif word == "-Xlinker": + append_next_word = ext.extra_link_args + elif word == "-Xcompiler": + append_next_word = ext.extra_compile_args + elif switch == "-u": + ext.extra_link_args.append(word) + if not value: + append_next_word = ext.extra_link_args + elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): + # NB. a really faithful emulation of makesetup would + # append a .o file to extra_objects only if it + # had a slash in it; otherwise, it would s/.o/.c/ + # and append it to sources. Hmmmm. + ext.extra_objects.append(word) + else: + file.warn("unrecognized argument '%s'" % word) + + extensions.append(ext) + finally: + file.close() + + return extensions diff --git a/setuptools/_distutils/fancy_getopt.py b/setuptools/_distutils/fancy_getopt.py new file mode 100644 index 00000000..7d170dd2 --- /dev/null +++ b/setuptools/_distutils/fancy_getopt.py @@ -0,0 +1,457 @@ +"""distutils.fancy_getopt + +Wrapper around the standard getopt module that provides the following +additional features: + * short and long options are tied together + * options have help strings, so fancy_getopt could potentially + create a complete usage summary + * options set attributes of a passed-in object +""" + +import sys, string, re +import getopt +from distutils.errors import * + +# Much like command_re in distutils.core, this is close to but not quite +# the same as a Python NAME -- except, in the spirit of most GNU +# utilities, we use '-' in place of '_'. (The spirit of LISP lives on!) +# The similarities to NAME are again not a coincidence... +longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)' +longopt_re = re.compile(r'^%s$' % longopt_pat) + +# For recognizing "negative alias" options, eg. "quiet=!verbose" +neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) + +# This is used to translate long options to legitimate Python identifiers +# (for use as attributes of some object). +longopt_xlate = str.maketrans('-', '_') + +class FancyGetopt: + """Wrapper around the standard 'getopt()' module that provides some + handy extra functionality: + * short and long options are tied together + * options have help strings, and help text can be assembled + from them + * options set attributes of a passed-in object + * boolean options can have "negative aliases" -- eg. if + --quiet is the "negative alias" of --verbose, then "--quiet" + on the command line sets 'verbose' to false + """ + + def __init__(self, option_table=None): + # The option table is (currently) a list of tuples. The + # tuples may have 3 or four values: + # (long_option, short_option, help_string [, repeatable]) + # if an option takes an argument, its long_option should have '=' + # appended; short_option should just be a single character, no ':' + # in any case. If a long_option doesn't have a corresponding + # short_option, short_option should be None. All option tuples + # must have long options. + self.option_table = option_table + + # 'option_index' maps long option names to entries in the option + # table (ie. those 3-tuples). + self.option_index = {} + if self.option_table: + self._build_index() + + # 'alias' records (duh) alias options; {'foo': 'bar'} means + # --foo is an alias for --bar + self.alias = {} + + # 'negative_alias' keeps track of options that are the boolean + # opposite of some other option + self.negative_alias = {} + + # These keep track of the information in the option table. We + # don't actually populate these structures until we're ready to + # parse the command-line, since the 'option_table' passed in here + # isn't necessarily the final word. + self.short_opts = [] + self.long_opts = [] + self.short2long = {} + self.attr_name = {} + self.takes_arg = {} + + # And 'option_order' is filled up in 'getopt()'; it records the + # original order of options (and their values) on the command-line, + # but expands short options, converts aliases, etc. + self.option_order = [] + + def _build_index(self): + self.option_index.clear() + for option in self.option_table: + self.option_index[option[0]] = option + + def set_option_table(self, option_table): + self.option_table = option_table + self._build_index() + + def add_option(self, long_option, short_option=None, help_string=None): + if long_option in self.option_index: + raise DistutilsGetoptError( + "option conflict: already an option '%s'" % long_option) + else: + option = (long_option, short_option, help_string) + self.option_table.append(option) + self.option_index[long_option] = option + + def has_option(self, long_option): + """Return true if the option table for this parser has an + option with long name 'long_option'.""" + return long_option in self.option_index + + def get_attr_name(self, long_option): + """Translate long option name 'long_option' to the form it + has as an attribute of some object: ie., translate hyphens + to underscores.""" + return long_option.translate(longopt_xlate) + + def _check_alias_dict(self, aliases, what): + assert isinstance(aliases, dict) + for (alias, opt) in aliases.items(): + if alias not in self.option_index: + raise DistutilsGetoptError(("invalid %s '%s': " + "option '%s' not defined") % (what, alias, alias)) + if opt not in self.option_index: + raise DistutilsGetoptError(("invalid %s '%s': " + "aliased option '%s' not defined") % (what, alias, opt)) + + def set_aliases(self, alias): + """Set the aliases for this option parser.""" + self._check_alias_dict(alias, "alias") + self.alias = alias + + def set_negative_aliases(self, negative_alias): + """Set the negative aliases for this option parser. + 'negative_alias' should be a dictionary mapping option names to + option names, both the key and value must already be defined + in the option table.""" + self._check_alias_dict(negative_alias, "negative alias") + self.negative_alias = negative_alias + + def _grok_option_table(self): + """Populate the various data structures that keep tabs on the + option table. Called by 'getopt()' before it can do anything + worthwhile. + """ + self.long_opts = [] + self.short_opts = [] + self.short2long.clear() + self.repeat = {} + + for option in self.option_table: + if len(option) == 3: + long, short, help = option + repeat = 0 + elif len(option) == 4: + long, short, help, repeat = option + else: + # the option table is part of the code, so simply + # assert that it is correct + raise ValueError("invalid option tuple: %r" % (option,)) + + # Type- and value-check the option names + if not isinstance(long, str) or len(long) < 2: + raise DistutilsGetoptError(("invalid long option '%s': " + "must be a string of length >= 2") % long) + + if (not ((short is None) or + (isinstance(short, str) and len(short) == 1))): + raise DistutilsGetoptError("invalid short option '%s': " + "must a single character or None" % short) + + self.repeat[long] = repeat + self.long_opts.append(long) + + if long[-1] == '=': # option takes an argument? + if short: short = short + ':' + long = long[0:-1] + self.takes_arg[long] = 1 + else: + # Is option is a "negative alias" for some other option (eg. + # "quiet" == "!verbose")? + alias_to = self.negative_alias.get(long) + if alias_to is not None: + if self.takes_arg[alias_to]: + raise DistutilsGetoptError( + "invalid negative alias '%s': " + "aliased option '%s' takes a value" + % (long, alias_to)) + + self.long_opts[-1] = long # XXX redundant?! + self.takes_arg[long] = 0 + + # If this is an alias option, make sure its "takes arg" flag is + # the same as the option it's aliased to. + alias_to = self.alias.get(long) + if alias_to is not None: + if self.takes_arg[long] != self.takes_arg[alias_to]: + raise DistutilsGetoptError( + "invalid alias '%s': inconsistent with " + "aliased option '%s' (one of them takes a value, " + "the other doesn't" + % (long, alias_to)) + + # Now enforce some bondage on the long option name, so we can + # later translate it to an attribute name on some object. Have + # to do this a bit late to make sure we've removed any trailing + # '='. + if not longopt_re.match(long): + raise DistutilsGetoptError( + "invalid long option name '%s' " + "(must be letters, numbers, hyphens only" % long) + + self.attr_name[long] = self.get_attr_name(long) + if short: + self.short_opts.append(short) + self.short2long[short[0]] = long + + def getopt(self, args=None, object=None): + """Parse command-line options in args. Store as attributes on object. + + If 'args' is None or not supplied, uses 'sys.argv[1:]'. If + 'object' is None or not supplied, creates a new OptionDummy + object, stores option values there, and returns a tuple (args, + object). If 'object' is supplied, it is modified in place and + 'getopt()' just returns 'args'; in both cases, the returned + 'args' is a modified copy of the passed-in 'args' list, which + is left untouched. + """ + if args is None: + args = sys.argv[1:] + if object is None: + object = OptionDummy() + created_object = True + else: + created_object = False + + self._grok_option_table() + + short_opts = ' '.join(self.short_opts) + try: + opts, args = getopt.getopt(args, short_opts, self.long_opts) + except getopt.error as msg: + raise DistutilsArgError(msg) + + for opt, val in opts: + if len(opt) == 2 and opt[0] == '-': # it's a short option + opt = self.short2long[opt[1]] + else: + assert len(opt) > 2 and opt[:2] == '--' + opt = opt[2:] + + alias = self.alias.get(opt) + if alias: + opt = alias + + if not self.takes_arg[opt]: # boolean option? + assert val == '', "boolean option can't have value" + alias = self.negative_alias.get(opt) + if alias: + opt = alias + val = 0 + else: + val = 1 + + attr = self.attr_name[opt] + # The only repeating option at the moment is 'verbose'. + # It has a negative option -q quiet, which should set verbose = 0. + if val and self.repeat.get(attr) is not None: + val = getattr(object, attr, 0) + 1 + setattr(object, attr, val) + self.option_order.append((opt, val)) + + # for opts + if created_object: + return args, object + else: + return args + + def get_option_order(self): + """Returns the list of (option, value) tuples processed by the + previous run of 'getopt()'. Raises RuntimeError if + 'getopt()' hasn't been called yet. + """ + if self.option_order is None: + raise RuntimeError("'getopt()' hasn't been called yet") + else: + return self.option_order + + def generate_help(self, header=None): + """Generate help text (a list of strings, one per suggested line of + output) from the option table for this FancyGetopt object. + """ + # Blithely assume the option table is good: probably wouldn't call + # 'generate_help()' unless you've already called 'getopt()'. + + # First pass: determine maximum length of long option names + max_opt = 0 + for option in self.option_table: + long = option[0] + short = option[1] + l = len(long) + if long[-1] == '=': + l = l - 1 + if short is not None: + l = l + 5 # " (-x)" where short == 'x' + if l > max_opt: + max_opt = l + + opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter + + # Typical help block looks like this: + # --foo controls foonabulation + # Help block for longest option looks like this: + # --flimflam set the flim-flam level + # and with wrapped text: + # --flimflam set the flim-flam level (must be between + # 0 and 100, except on Tuesdays) + # Options with short names will have the short name shown (but + # it doesn't contribute to max_opt): + # --foo (-f) controls foonabulation + # If adding the short option would make the left column too wide, + # we push the explanation off to the next line + # --flimflam (-l) + # set the flim-flam level + # Important parameters: + # - 2 spaces before option block start lines + # - 2 dashes for each long option name + # - min. 2 spaces between option and explanation (gutter) + # - 5 characters (incl. space) for short option name + + # Now generate lines of help text. (If 80 columns were good enough + # for Jesus, then 78 columns are good enough for me!) + line_width = 78 + text_width = line_width - opt_width + big_indent = ' ' * opt_width + if header: + lines = [header] + else: + lines = ['Option summary:'] + + for option in self.option_table: + long, short, help = option[:3] + text = wrap_text(help, text_width) + if long[-1] == '=': + long = long[0:-1] + + # Case 1: no short option at all (makes life easy) + if short is None: + if text: + lines.append(" --%-*s %s" % (max_opt, long, text[0])) + else: + lines.append(" --%-*s " % (max_opt, long)) + + # Case 2: we have a short option, so we have to include it + # just after the long option + else: + opt_names = "%s (-%s)" % (long, short) + if text: + lines.append(" --%-*s %s" % + (max_opt, opt_names, text[0])) + else: + lines.append(" --%-*s" % opt_names) + + for l in text[1:]: + lines.append(big_indent + l) + return lines + + def print_help(self, header=None, file=None): + if file is None: + file = sys.stdout + for line in self.generate_help(header): + file.write(line + "\n") + + +def fancy_getopt(options, negative_opt, object, args): + parser = FancyGetopt(options) + parser.set_negative_aliases(negative_opt) + return parser.getopt(args, object) + + +WS_TRANS = {ord(_wschar) : ' ' for _wschar in string.whitespace} + +def wrap_text(text, width): + """wrap_text(text : string, width : int) -> [string] + + Split 'text' into multiple lines of no more than 'width' characters + each, and return the list of strings that results. + """ + if text is None: + return [] + if len(text) <= width: + return [text] + + text = text.expandtabs() + text = text.translate(WS_TRANS) + chunks = re.split(r'( +|-+)', text) + chunks = [ch for ch in chunks if ch] # ' - ' results in empty strings + lines = [] + + while chunks: + cur_line = [] # list of chunks (to-be-joined) + cur_len = 0 # length of current line + + while chunks: + l = len(chunks[0]) + if cur_len + l <= width: # can squeeze (at least) this chunk in + cur_line.append(chunks[0]) + del chunks[0] + cur_len = cur_len + l + else: # this line is full + # drop last chunk if all space + if cur_line and cur_line[-1][0] == ' ': + del cur_line[-1] + break + + if chunks: # any chunks left to process? + # if the current line is still empty, then we had a single + # chunk that's too big too fit on a line -- so we break + # down and break it up at the line width + if cur_len == 0: + cur_line.append(chunks[0][0:width]) + chunks[0] = chunks[0][width:] + + # all-whitespace chunks at the end of a line can be discarded + # (and we know from the re.split above that if a chunk has + # *any* whitespace, it is *all* whitespace) + if chunks[0][0] == ' ': + del chunks[0] + + # and store this line in the list-of-all-lines -- as a single + # string, of course! + lines.append(''.join(cur_line)) + + return lines + + +def translate_longopt(opt): + """Convert a long option name to a valid Python identifier by + changing "-" to "_". + """ + return opt.translate(longopt_xlate) + + +class OptionDummy: + """Dummy class just used as a place to hold command-line option + values as instance attributes.""" + + def __init__(self, options=[]): + """Create a new OptionDummy instance. The attributes listed in + 'options' will be initialized to None.""" + for opt in options: + setattr(self, opt, None) + + +if __name__ == "__main__": + text = """\ +Tra-la-la, supercalifragilisticexpialidocious. +How *do* you spell that odd word, anyways? +(Someone ask Mary -- she'll know [or she'll +say, "How should I know?"].)""" + + for w in (10, 20, 30, 40): + print("width: %d" % w) + print("\n".join(wrap_text(text, w))) + print() diff --git a/setuptools/_distutils/file_util.py b/setuptools/_distutils/file_util.py new file mode 100644 index 00000000..b3fee35a --- /dev/null +++ b/setuptools/_distutils/file_util.py @@ -0,0 +1,238 @@ +"""distutils.file_util + +Utility functions for operating on single files. +""" + +import os +from distutils.errors import DistutilsFileError +from distutils import log + +# for generating verbose output in 'copy_file()' +_copy_action = { None: 'copying', + 'hard': 'hard linking', + 'sym': 'symbolically linking' } + + +def _copy_file_contents(src, dst, buffer_size=16*1024): + """Copy the file 'src' to 'dst'; both must be filenames. Any error + opening either file, reading from 'src', or writing to 'dst', raises + DistutilsFileError. Data is read/written in chunks of 'buffer_size' + bytes (default 16k). No attempt is made to handle anything apart from + regular files. + """ + # Stolen from shutil module in the standard library, but with + # custom error-handling added. + fsrc = None + fdst = None + try: + try: + fsrc = open(src, 'rb') + except OSError as e: + raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror)) + + if os.path.exists(dst): + try: + os.unlink(dst) + except OSError as e: + raise DistutilsFileError( + "could not delete '%s': %s" % (dst, e.strerror)) + + try: + fdst = open(dst, 'wb') + except OSError as e: + raise DistutilsFileError( + "could not create '%s': %s" % (dst, e.strerror)) + + while True: + try: + buf = fsrc.read(buffer_size) + except OSError as e: + raise DistutilsFileError( + "could not read from '%s': %s" % (src, e.strerror)) + + if not buf: + break + + try: + fdst.write(buf) + except OSError as e: + raise DistutilsFileError( + "could not write to '%s': %s" % (dst, e.strerror)) + finally: + if fdst: + fdst.close() + if fsrc: + fsrc.close() + +def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, + link=None, verbose=1, dry_run=0): + """Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is + copied there with the same name; otherwise, it must be a filename. (If + the file exists, it will be ruthlessly clobbered.) If 'preserve_mode' + is true (the default), the file's mode (type and permission bits, or + whatever is analogous on the current platform) is copied. If + 'preserve_times' is true (the default), the last-modified and + last-access times are copied as well. If 'update' is true, 'src' will + only be copied if 'dst' does not exist, or if 'dst' does exist but is + older than 'src'. + + 'link' allows you to make hard links (os.link) or symbolic links + (os.symlink) instead of copying: set it to "hard" or "sym"; if it is + None (the default), files are copied. Don't set 'link' on systems that + don't support it: 'copy_file()' doesn't check if hard or symbolic + linking is available. If hardlink fails, falls back to + _copy_file_contents(). + + Under Mac OS, uses the native file copy function in macostools; on + other systems, uses '_copy_file_contents()' to copy file contents. + + Return a tuple (dest_name, copied): 'dest_name' is the actual name of + the output file, and 'copied' is true if the file was copied (or would + have been copied, if 'dry_run' true). + """ + # XXX if the destination file already exists, we clobber it if + # copying, but blow up if linking. Hmmm. And I don't know what + # macostools.copyfile() does. Should definitely be consistent, and + # should probably blow up if destination exists and we would be + # changing it (ie. it's not already a hard/soft link to src OR + # (not update) and (src newer than dst). + + from distutils.dep_util import newer + from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE + + if not os.path.isfile(src): + raise DistutilsFileError( + "can't copy '%s': doesn't exist or not a regular file" % src) + + if os.path.isdir(dst): + dir = dst + dst = os.path.join(dst, os.path.basename(src)) + else: + dir = os.path.dirname(dst) + + if update and not newer(src, dst): + if verbose >= 1: + log.debug("not copying %s (output up-to-date)", src) + return (dst, 0) + + try: + action = _copy_action[link] + except KeyError: + raise ValueError("invalid value '%s' for 'link' argument" % link) + + if verbose >= 1: + if os.path.basename(dst) == os.path.basename(src): + log.info("%s %s -> %s", action, src, dir) + else: + log.info("%s %s -> %s", action, src, dst) + + if dry_run: + return (dst, 1) + + # If linking (hard or symbolic), use the appropriate system call + # (Unix only, of course, but that's the caller's responsibility) + elif link == 'hard': + if not (os.path.exists(dst) and os.path.samefile(src, dst)): + try: + os.link(src, dst) + return (dst, 1) + except OSError: + # If hard linking fails, fall back on copying file + # (some special filesystems don't support hard linking + # even under Unix, see issue #8876). + pass + elif link == 'sym': + if not (os.path.exists(dst) and os.path.samefile(src, dst)): + os.symlink(src, dst) + return (dst, 1) + + # Otherwise (non-Mac, not linking), copy the file contents and + # (optionally) copy the times and mode. + _copy_file_contents(src, dst) + if preserve_mode or preserve_times: + st = os.stat(src) + + # According to David Ascher , utime() should be done + # before chmod() (at least under NT). + if preserve_times: + os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) + if preserve_mode: + os.chmod(dst, S_IMODE(st[ST_MODE])) + + return (dst, 1) + + +# XXX I suspect this is Unix-specific -- need porting help! +def move_file (src, dst, + verbose=1, + dry_run=0): + + """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will + be moved into it with the same name; otherwise, 'src' is just renamed + to 'dst'. Return the new full name of the file. + + Handles cross-device moves on Unix using 'copy_file()'. What about + other systems??? + """ + from os.path import exists, isfile, isdir, basename, dirname + import errno + + if verbose >= 1: + log.info("moving %s -> %s", src, dst) + + if dry_run: + return dst + + if not isfile(src): + raise DistutilsFileError("can't move '%s': not a regular file" % src) + + if isdir(dst): + dst = os.path.join(dst, basename(src)) + elif exists(dst): + raise DistutilsFileError( + "can't move '%s': destination '%s' already exists" % + (src, dst)) + + if not isdir(dirname(dst)): + raise DistutilsFileError( + "can't move '%s': destination '%s' not a valid path" % + (src, dst)) + + copy_it = False + try: + os.rename(src, dst) + except OSError as e: + (num, msg) = e.args + if num == errno.EXDEV: + copy_it = True + else: + raise DistutilsFileError( + "couldn't move '%s' to '%s': %s" % (src, dst, msg)) + + if copy_it: + copy_file(src, dst, verbose=verbose) + try: + os.unlink(src) + except OSError as e: + (num, msg) = e.args + try: + os.unlink(dst) + except OSError: + pass + raise DistutilsFileError( + "couldn't move '%s' to '%s' by copy/delete: " + "delete '%s' failed: %s" + % (src, dst, src, msg)) + return dst + + +def write_file (filename, contents): + """Create a file with the specified name and write 'contents' (a + sequence of strings without line terminators) to it. + """ + f = open(filename, "w") + try: + for line in contents: + f.write(line + "\n") + finally: + f.close() diff --git a/setuptools/_distutils/filelist.py b/setuptools/_distutils/filelist.py new file mode 100644 index 00000000..c92d5fdb --- /dev/null +++ b/setuptools/_distutils/filelist.py @@ -0,0 +1,327 @@ +"""distutils.filelist + +Provides the FileList class, used for poking about the filesystem +and building lists of files. +""" + +import os, re +import fnmatch +import functools +from distutils.util import convert_path +from distutils.errors import DistutilsTemplateError, DistutilsInternalError +from distutils import log + +class FileList: + """A list of files built by on exploring the filesystem and filtered by + applying various patterns to what we find there. + + Instance attributes: + dir + directory from which files will be taken -- only used if + 'allfiles' not supplied to constructor + files + list of filenames currently being built/filtered/manipulated + allfiles + complete list of files under consideration (ie. without any + filtering applied) + """ + + def __init__(self, warn=None, debug_print=None): + # ignore argument to FileList, but keep them for backwards + # compatibility + self.allfiles = None + self.files = [] + + def set_allfiles(self, allfiles): + self.allfiles = allfiles + + def findall(self, dir=os.curdir): + self.allfiles = findall(dir) + + def debug_print(self, msg): + """Print 'msg' to stdout if the global DEBUG (taken from the + DISTUTILS_DEBUG environment variable) flag is true. + """ + from distutils.debug import DEBUG + if DEBUG: + print(msg) + + # -- List-like methods --------------------------------------------- + + def append(self, item): + self.files.append(item) + + def extend(self, items): + self.files.extend(items) + + def sort(self): + # Not a strict lexical sort! + sortable_files = sorted(map(os.path.split, self.files)) + self.files = [] + for sort_tuple in sortable_files: + self.files.append(os.path.join(*sort_tuple)) + + + # -- Other miscellaneous utility methods --------------------------- + + def remove_duplicates(self): + # Assumes list has been sorted! + for i in range(len(self.files) - 1, 0, -1): + if self.files[i] == self.files[i - 1]: + del self.files[i] + + + # -- "File template" methods --------------------------------------- + + def _parse_template_line(self, line): + words = line.split() + action = words[0] + + patterns = dir = dir_pattern = None + + if action in ('include', 'exclude', + 'global-include', 'global-exclude'): + if len(words) < 2: + raise DistutilsTemplateError( + "'%s' expects ..." % action) + patterns = [convert_path(w) for w in words[1:]] + elif action in ('recursive-include', 'recursive-exclude'): + if len(words) < 3: + raise DistutilsTemplateError( + "'%s' expects ..." % action) + dir = convert_path(words[1]) + patterns = [convert_path(w) for w in words[2:]] + elif action in ('graft', 'prune'): + if len(words) != 2: + raise DistutilsTemplateError( + "'%s' expects a single " % action) + dir_pattern = convert_path(words[1]) + else: + raise DistutilsTemplateError("unknown action '%s'" % action) + + return (action, patterns, dir, dir_pattern) + + def process_template_line(self, line): + # Parse the line: split it up, make sure the right number of words + # is there, and return the relevant words. 'action' is always + # defined: it's the first word of the line. Which of the other + # three are defined depends on the action; it'll be either + # patterns, (dir and patterns), or (dir_pattern). + (action, patterns, dir, dir_pattern) = self._parse_template_line(line) + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. + if action == 'include': + self.debug_print("include " + ' '.join(patterns)) + for pattern in patterns: + if not self.include_pattern(pattern, anchor=1): + log.warn("warning: no files found matching '%s'", + pattern) + + elif action == 'exclude': + self.debug_print("exclude " + ' '.join(patterns)) + for pattern in patterns: + if not self.exclude_pattern(pattern, anchor=1): + log.warn(("warning: no previously-included files " + "found matching '%s'"), pattern) + + elif action == 'global-include': + self.debug_print("global-include " + ' '.join(patterns)) + for pattern in patterns: + if not self.include_pattern(pattern, anchor=0): + log.warn(("warning: no files found matching '%s' " + "anywhere in distribution"), pattern) + + elif action == 'global-exclude': + self.debug_print("global-exclude " + ' '.join(patterns)) + for pattern in patterns: + if not self.exclude_pattern(pattern, anchor=0): + log.warn(("warning: no previously-included files matching " + "'%s' found anywhere in distribution"), + pattern) + + elif action == 'recursive-include': + self.debug_print("recursive-include %s %s" % + (dir, ' '.join(patterns))) + for pattern in patterns: + if not self.include_pattern(pattern, prefix=dir): + log.warn(("warning: no files found matching '%s' " + "under directory '%s'"), + pattern, dir) + + elif action == 'recursive-exclude': + self.debug_print("recursive-exclude %s %s" % + (dir, ' '.join(patterns))) + for pattern in patterns: + if not self.exclude_pattern(pattern, prefix=dir): + log.warn(("warning: no previously-included files matching " + "'%s' found under directory '%s'"), + pattern, dir) + + elif action == 'graft': + self.debug_print("graft " + dir_pattern) + if not self.include_pattern(None, prefix=dir_pattern): + log.warn("warning: no directories found matching '%s'", + dir_pattern) + + elif action == 'prune': + self.debug_print("prune " + dir_pattern) + if not self.exclude_pattern(None, prefix=dir_pattern): + log.warn(("no previously-included directories found " + "matching '%s'"), dir_pattern) + else: + raise DistutilsInternalError( + "this cannot happen: invalid action '%s'" % action) + + + # -- Filtering/selection methods ----------------------------------- + + def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): + """Select strings (presumably filenames) from 'self.files' that + match 'pattern', a Unix-style wildcard (glob) pattern. Patterns + are not quite the same as implemented by the 'fnmatch' module: '*' + and '?' match non-special characters, where "special" is platform- + dependent: slash on Unix; colon, slash, and backslash on + DOS/Windows; and colon on Mac OS. + + If 'anchor' is true (the default), then the pattern match is more + stringent: "*.py" will match "foo.py" but not "foo/bar.py". If + 'anchor' is false, both of these will match. + + If 'prefix' is supplied, then only filenames starting with 'prefix' + (itself a pattern) and ending with 'pattern', with anything in between + them, will match. 'anchor' is ignored in this case. + + If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and + 'pattern' is assumed to be either a string containing a regex or a + regex object -- no translation is done, the regex is just compiled + and used as-is. + + Selected strings will be added to self.files. + + Return True if files are found, False otherwise. + """ + # XXX docstring lying about what the special chars are? + files_found = False + pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) + self.debug_print("include_pattern: applying regex r'%s'" % + pattern_re.pattern) + + # delayed loading of allfiles list + if self.allfiles is None: + self.findall() + + for name in self.allfiles: + if pattern_re.search(name): + self.debug_print(" adding " + name) + self.files.append(name) + files_found = True + return files_found + + + def exclude_pattern (self, pattern, + anchor=1, prefix=None, is_regex=0): + """Remove strings (presumably filenames) from 'files' that match + 'pattern'. Other parameters are the same as for + 'include_pattern()', above. + The list 'self.files' is modified in place. + Return True if files are found, False otherwise. + """ + files_found = False + pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) + self.debug_print("exclude_pattern: applying regex r'%s'" % + pattern_re.pattern) + for i in range(len(self.files)-1, -1, -1): + if pattern_re.search(self.files[i]): + self.debug_print(" removing " + self.files[i]) + del self.files[i] + files_found = True + return files_found + + +# ---------------------------------------------------------------------- +# Utility functions + +def _find_all_simple(path): + """ + Find all files under 'path' + """ + results = ( + os.path.join(base, file) + for base, dirs, files in os.walk(path, followlinks=True) + for file in files + ) + return filter(os.path.isfile, results) + + +def findall(dir=os.curdir): + """ + Find all files under 'dir' and return the list of full filenames. + Unless dir is '.', return full filenames with dir prepended. + """ + files = _find_all_simple(dir) + if dir == os.curdir: + make_rel = functools.partial(os.path.relpath, start=dir) + files = map(make_rel, files) + return list(files) + + +def glob_to_re(pattern): + """Translate a shell-like glob pattern to a regular expression; return + a string containing the regex. Differs from 'fnmatch.translate()' in + that '*' does not match "special characters" (which are + platform-specific). + """ + pattern_re = fnmatch.translate(pattern) + + # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which + # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, + # and by extension they shouldn't match such "special characters" under + # any OS. So change all non-escaped dots in the RE to match any + # character except the special characters (currently: just os.sep). + sep = os.sep + if os.sep == '\\': + # we're using a regex to manipulate a regex, so we need + # to escape the backslash twice + sep = r'\\\\' + escaped = r'\1[^%s]' % sep + pattern_re = re.sub(r'((?= self.threshold: + if args: + msg = msg % args + if level in (WARN, ERROR, FATAL): + stream = sys.stderr + else: + stream = sys.stdout + try: + stream.write('%s\n' % msg) + except UnicodeEncodeError: + # emulate backslashreplace error handler + encoding = stream.encoding + msg = msg.encode(encoding, "backslashreplace").decode(encoding) + stream.write('%s\n' % msg) + stream.flush() + + def log(self, level, msg, *args): + self._log(level, msg, args) + + def debug(self, msg, *args): + self._log(DEBUG, msg, args) + + def info(self, msg, *args): + self._log(INFO, msg, args) + + def warn(self, msg, *args): + self._log(WARN, msg, args) + + def error(self, msg, *args): + self._log(ERROR, msg, args) + + def fatal(self, msg, *args): + self._log(FATAL, msg, args) + +_global_log = Log() +log = _global_log.log +debug = _global_log.debug +info = _global_log.info +warn = _global_log.warn +error = _global_log.error +fatal = _global_log.fatal + +def set_threshold(level): + # return the old threshold for use from tests + old = _global_log.threshold + _global_log.threshold = level + return old + +def set_verbosity(v): + if v <= 0: + set_threshold(WARN) + elif v == 1: + set_threshold(INFO) + elif v >= 2: + set_threshold(DEBUG) diff --git a/setuptools/_distutils/msvc9compiler.py b/setuptools/_distutils/msvc9compiler.py new file mode 100644 index 00000000..6934e964 --- /dev/null +++ b/setuptools/_distutils/msvc9compiler.py @@ -0,0 +1,788 @@ +"""distutils.msvc9compiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for the Microsoft Visual Studio 2008. + +The module is compatible with VS 2005 and VS 2008. You can find legacy support +for older versions of VS in distutils.msvccompiler. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) +# ported to VS2005 and VS 2008 by Christian Heimes + +import os +import subprocess +import sys +import re + +from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import CCompiler, gen_lib_options +from distutils import log +from distutils.util import get_platform + +import winreg + +RegOpenKeyEx = winreg.OpenKeyEx +RegEnumKey = winreg.EnumKey +RegEnumValue = winreg.EnumValue +RegError = winreg.error + +HKEYS = (winreg.HKEY_USERS, + winreg.HKEY_CURRENT_USER, + winreg.HKEY_LOCAL_MACHINE, + winreg.HKEY_CLASSES_ROOT) + +NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32) +if NATIVE_WIN64: + # Visual C++ is a 32-bit application, so we need to look in + # the corresponding registry branch, if we're running a + # 64-bit Python on Win64 + VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" +else: + VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" + WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" + NET_BASE = r"Software\Microsoft\.NETFramework" + +# A map keyed by get_platform() return values to values accepted by +# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is +# the param to cross-compile on x86 targeting amd64.) +PLAT_TO_VCVARS = { + 'win32' : 'x86', + 'win-amd64' : 'amd64', +} + +class Reg: + """Helper class to read values from the registry + """ + + def get_value(cls, path, key): + for base in HKEYS: + d = cls.read_values(base, path) + if d and key in d: + return d[key] + raise KeyError(key) + get_value = classmethod(get_value) + + def read_keys(cls, base, key): + """Return list of registry keys.""" + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + L = [] + i = 0 + while True: + try: + k = RegEnumKey(handle, i) + except RegError: + break + L.append(k) + i += 1 + return L + read_keys = classmethod(read_keys) + + def read_values(cls, base, key): + """Return dict of registry keys and values. + + All names are converted to lowercase. + """ + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + d = {} + i = 0 + while True: + try: + name, value, type = RegEnumValue(handle, i) + except RegError: + break + name = name.lower() + d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) + i += 1 + return d + read_values = classmethod(read_values) + + def convert_mbcs(s): + dec = getattr(s, "decode", None) + if dec is not None: + try: + s = dec("mbcs") + except UnicodeError: + pass + return s + convert_mbcs = staticmethod(convert_mbcs) + +class MacroExpander: + + def __init__(self, version): + self.macros = {} + self.vsbase = VS_BASE % version + self.load_macros(version) + + def set_macro(self, macro, path, key): + self.macros["$(%s)" % macro] = Reg.get_value(path, key) + + def load_macros(self, version): + self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") + self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") + self.set_macro("FrameworkDir", NET_BASE, "installroot") + try: + if version >= 8.0: + self.set_macro("FrameworkSDKDir", NET_BASE, + "sdkinstallrootv2.0") + else: + raise KeyError("sdkinstallrootv2.0") + except KeyError: + raise DistutilsPlatformError( + """Python was built with Visual Studio 2008; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2008 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") + + if version >= 9.0: + self.set_macro("FrameworkVersion", self.vsbase, "clr version") + self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") + else: + p = r"Software\Microsoft\NET Framework Setup\Product" + for base in HKEYS: + try: + h = RegOpenKeyEx(base, p) + except RegError: + continue + key = RegEnumKey(h, 0) + d = Reg.get_value(base, r"%s\%s" % (p, key)) + self.macros["$(FrameworkVersion)"] = d["version"] + + def sub(self, s): + for k, v in self.macros.items(): + s = s.replace(k, v) + return s + +def get_build_version(): + """Return the version of MSVC that was used to build Python. + + For Python 2.3 and up, the version number is included in + sys.version. For earlier versions, assume the compiler is MSVC 6. + """ + prefix = "MSC v." + i = sys.version.find(prefix) + if i == -1: + return 6 + i = i + len(prefix) + s, rest = sys.version[i:].split(" ", 1) + majorVersion = int(s[:-2]) - 6 + if majorVersion >= 13: + # v13 was skipped and should be v14 + majorVersion += 1 + minorVersion = int(s[2:3]) / 10.0 + # I don't think paths are affected by minor version in version 6 + if majorVersion == 6: + minorVersion = 0 + if majorVersion >= 6: + return majorVersion + minorVersion + # else we don't know what version of the compiler this is + return None + +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths + +def removeDuplicates(variable): + """Remove duplicate values of an environment variable. + """ + oldList = variable.split(os.pathsep) + newList = [] + for i in oldList: + if i not in newList: + newList.append(i) + newVariable = os.pathsep.join(newList) + return newVariable + +def find_vcvarsall(version): + """Find the vcvarsall.bat file + + At first it tries to find the productdir of VS 2008 in the registry. If + that fails it falls back to the VS90COMNTOOLS env var. + """ + vsbase = VS_BASE % version + try: + productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, + "productdir") + except KeyError: + log.debug("Unable to find productdir in registry") + productdir = None + + if not productdir or not os.path.isdir(productdir): + toolskey = "VS%0.f0COMNTOOLS" % version + toolsdir = os.environ.get(toolskey, None) + + if toolsdir and os.path.isdir(toolsdir): + productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") + productdir = os.path.abspath(productdir) + if not os.path.isdir(productdir): + log.debug("%s is not a valid directory" % productdir) + return None + else: + log.debug("Env var %s is not set or invalid" % toolskey) + if not productdir: + log.debug("No productdir found") + return None + vcvarsall = os.path.join(productdir, "vcvarsall.bat") + if os.path.isfile(vcvarsall): + return vcvarsall + log.debug("Unable to find vcvarsall.bat") + return None + +def query_vcvarsall(version, arch="x86"): + """Launch vcvarsall.bat and read the settings from its environment + """ + vcvarsall = find_vcvarsall(version) + interesting = {"include", "lib", "libpath", "path"} + result = {} + + if vcvarsall is None: + raise DistutilsPlatformError("Unable to find vcvarsall.bat") + log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) + popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + try: + stdout, stderr = popen.communicate() + if popen.wait() != 0: + raise DistutilsPlatformError(stderr.decode("mbcs")) + + stdout = stdout.decode("mbcs") + for line in stdout.split("\n"): + line = Reg.convert_mbcs(line) + if '=' not in line: + continue + line = line.strip() + key, value = line.split('=', 1) + key = key.lower() + if key in interesting: + if value.endswith(os.pathsep): + value = value[:-1] + result[key] = removeDuplicates(value) + + finally: + popen.stdout.close() + popen.stderr.close() + + if len(result) != len(interesting): + raise ValueError(str(list(result.keys()))) + + return result + +# More globals +VERSION = get_build_version() +if VERSION < 8.0: + raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) +# MACROS = MacroExpander(VERSION) + +class MSVCCompiler(CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) + self.__version = VERSION + self.__root = r"Software\Microsoft\VisualStudio" + # self.__macros = MACROS + self.__paths = [] + # target platform (.plat_name is consistent with 'bdist') + self.plat_name = None + self.__arch = None # deprecated name + self.initialized = False + + def initialize(self, plat_name=None): + # multi-init means we would need to check platform same each time... + assert not self.initialized, "don't init multiple times" + if plat_name is None: + plat_name = get_platform() + # sanity check for platforms to prevent obscure errors later. + ok_plats = 'win32', 'win-amd64' + if plat_name not in ok_plats: + raise DistutilsPlatformError("--plat-name must be one of %s" % + (ok_plats,)) + + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): + # Assume that the SDK set up everything alright; don't try to be + # smarter + self.cc = "cl.exe" + self.linker = "link.exe" + self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" + else: + # On x86, 'vcvars32.bat amd64' creates an env that doesn't work; + # to cross compile, you use 'x86_amd64'. + # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross + # compile use 'x86' (ie, it runs the x86 compiler directly) + if plat_name == get_platform() or plat_name == 'win32': + # native build or cross-compile to win32 + plat_spec = PLAT_TO_VCVARS[plat_name] + else: + # cross compile from win32 -> some 64bit + plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ + PLAT_TO_VCVARS[plat_name] + + vc_env = query_vcvarsall(VERSION, plat_spec) + + self.__paths = vc_env['path'].split(os.pathsep) + os.environ['lib'] = vc_env['lib'] + os.environ['include'] = vc_env['include'] + + if len(self.__paths) == 0: + raise DistutilsPlatformError("Python was built with %s, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." + % self.__product) + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + #self.set_path_env_var('lib') + #self.set_path_env_var('include') + + # extend the MSVC path with the current path + try: + for p in os.environ['path'].split(';'): + self.__paths.append(p) + except KeyError: + pass + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) + + self.preprocess_options = None + if self.__arch == "x86": + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', + '/Z7', '/D_DEBUG'] + else: + # Win64 + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', + '/Z7', '/D_DEBUG'] + + self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] + if self.__version >= 7: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' + ] + self.ldflags_static = [ '/nologo'] + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): + # Copied from ccompiler.py, extended to return .res as 'object'-file + # for .rc input file + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext (src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / + if ext not in self.src_extensions: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError ("Don't know how to compile %s" % src_name) + if strip_dir: + base = os.path.basename (base) + if ext in self._rc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + elif ext in self._mc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append ('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + + [output_opt] + [input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc] + + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext (os.path.basename (src)) + rc_file = os.path.join (rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc] + + ["/fo" + obj] + [rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError("Don't know how to compile %s to %s" + % (src, obj)) + + output_opt = "/Fo" + obj + try: + self.spawn([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + (libraries, library_dirs, runtime_library_dirs) = fixed_args + + if runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) + + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + if target_desc == CCompiler.EXECUTABLE: + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + else: + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared + + export_opts = [] + for sym in (export_symbols or []): + export_opts.append("/EXPORT:" + sym) + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + build_temp = os.path.dirname(objects[0]) + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + build_temp, + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + + self.manifest_setup_ldargs(output_filename, build_temp, ld_args) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath(os.path.dirname(output_filename)) + try: + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + # embed the manifest + # XXX - this is somewhat fragile - if mt.exe fails, distutils + # will still consider the DLL up-to-date, but it will not have a + # manifest. Maybe we should link to a temp file? OTOH, that + # implies a build environment error that shouldn't go undetected. + mfinfo = self.manifest_get_embed_info(target_desc, ld_args) + if mfinfo is not None: + mffilename, mfid = mfinfo + out_arg = '-outputresource:%s;%s' % (output_filename, mfid) + try: + self.spawn(['mt.exe', '-nologo', '-manifest', + mffilename, out_arg]) + except DistutilsExecError as msg: + raise LinkError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): + # If we need a manifest at all, an embedded manifest is recommended. + # See MSDN article titled + # "How to: Embed a Manifest Inside a C/C++ Application" + # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) + # Ask the linker to generate the manifest in the temp dir, so + # we can check it, and possibly embed it, later. + temp_manifest = os.path.join( + build_temp, + os.path.basename(output_filename) + ".manifest") + ld_args.append('/MANIFESTFILE:' + temp_manifest) + + def manifest_get_embed_info(self, target_desc, ld_args): + # If a manifest should be embedded, return a tuple of + # (manifest_filename, resource_id). Returns None if no manifest + # should be embedded. See http://bugs.python.org/issue7833 for why + # we want to avoid any manifest for extension modules if we can) + for arg in ld_args: + if arg.startswith("/MANIFESTFILE:"): + temp_manifest = arg.split(":", 1)[1] + break + else: + # no /MANIFESTFILE so nothing to do. + return None + if target_desc == CCompiler.EXECUTABLE: + # by default, executables always get the manifest with the + # CRT referenced. + mfid = 1 + else: + # Extension modules try and avoid any manifest if possible. + mfid = 2 + temp_manifest = self._remove_visual_c_ref(temp_manifest) + if temp_manifest is None: + return None + return temp_manifest, mfid + + def _remove_visual_c_ref(self, manifest_file): + try: + # Remove references to the Visual C runtime, so they will + # fall through to the Visual C dependency of Python.exe. + # This way, when installed for a restricted user (e.g. + # runtimes are not in WinSxS folder, but in Python's own + # folder), the runtimes do not need to be in every folder + # with .pyd's. + # Returns either the filename of the modified manifest or + # None if no manifest should be embedded. + manifest_f = open(manifest_file) + try: + manifest_buf = manifest_f.read() + finally: + manifest_f.close() + pattern = re.compile( + r"""|)""", + re.DOTALL) + manifest_buf = re.sub(pattern, "", manifest_buf) + pattern = r"\s*" + manifest_buf = re.sub(pattern, "", manifest_buf) + # Now see if any other assemblies are referenced - if not, we + # don't want a manifest embedded. + pattern = re.compile( + r"""|)""", re.DOTALL) + if re.search(pattern, manifest_buf) is None: + return None + + manifest_f = open(manifest_file, 'w') + try: + manifest_f.write(manifest_buf) + return manifest_file + finally: + manifest_f.close() + except OSError: + pass + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC++") + + def library_option(self, lib): + return self.library_filename(lib) + + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename (name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # Helper methods for using the MSVC registry settings + + def find_exe(self, exe): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + for p in self.__paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in os.environ['Path'].split(';'): + fn = os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): + return fn + + return exe diff --git a/setuptools/_distutils/msvccompiler.py b/setuptools/_distutils/msvccompiler.py new file mode 100644 index 00000000..d5857cb1 --- /dev/null +++ b/setuptools/_distutils/msvccompiler.py @@ -0,0 +1,643 @@ +"""distutils.msvccompiler + +Contains MSVCCompiler, an implementation of the abstract CCompiler class +for the Microsoft Visual Studio. +""" + +# Written by Perry Stoll +# hacked by Robin Becker and Thomas Heller to do a better job of +# finding DevStudio (through the registry) + +import sys, os +from distutils.errors import \ + DistutilsExecError, DistutilsPlatformError, \ + CompileError, LibError, LinkError +from distutils.ccompiler import \ + CCompiler, gen_lib_options +from distutils import log + +_can_read_reg = False +try: + import winreg + + _can_read_reg = True + hkey_mod = winreg + + RegOpenKeyEx = winreg.OpenKeyEx + RegEnumKey = winreg.EnumKey + RegEnumValue = winreg.EnumValue + RegError = winreg.error + +except ImportError: + try: + import win32api + import win32con + _can_read_reg = True + hkey_mod = win32con + + RegOpenKeyEx = win32api.RegOpenKeyEx + RegEnumKey = win32api.RegEnumKey + RegEnumValue = win32api.RegEnumValue + RegError = win32api.error + except ImportError: + log.info("Warning: Can't read registry to find the " + "necessary compiler setting\n" + "Make sure that Python modules winreg, " + "win32api or win32con are installed.") + pass + +if _can_read_reg: + HKEYS = (hkey_mod.HKEY_USERS, + hkey_mod.HKEY_CURRENT_USER, + hkey_mod.HKEY_LOCAL_MACHINE, + hkey_mod.HKEY_CLASSES_ROOT) + +def read_keys(base, key): + """Return list of registry keys.""" + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + L = [] + i = 0 + while True: + try: + k = RegEnumKey(handle, i) + except RegError: + break + L.append(k) + i += 1 + return L + +def read_values(base, key): + """Return dict of registry keys and values. + + All names are converted to lowercase. + """ + try: + handle = RegOpenKeyEx(base, key) + except RegError: + return None + d = {} + i = 0 + while True: + try: + name, value, type = RegEnumValue(handle, i) + except RegError: + break + name = name.lower() + d[convert_mbcs(name)] = convert_mbcs(value) + i += 1 + return d + +def convert_mbcs(s): + dec = getattr(s, "decode", None) + if dec is not None: + try: + s = dec("mbcs") + except UnicodeError: + pass + return s + +class MacroExpander: + def __init__(self, version): + self.macros = {} + self.load_macros(version) + + def set_macro(self, macro, path, key): + for base in HKEYS: + d = read_values(base, path) + if d: + self.macros["$(%s)" % macro] = d[key] + break + + def load_macros(self, version): + vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version + self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") + self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") + net = r"Software\Microsoft\.NETFramework" + self.set_macro("FrameworkDir", net, "installroot") + try: + if version > 7.0: + self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") + else: + self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") + except KeyError as exc: # + raise DistutilsPlatformError( + """Python was built with Visual Studio 2003; +extensions must be built with a compiler than can generate compatible binaries. +Visual Studio 2003 was not found on this system. If you have Cygwin installed, +you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") + + p = r"Software\Microsoft\NET Framework Setup\Product" + for base in HKEYS: + try: + h = RegOpenKeyEx(base, p) + except RegError: + continue + key = RegEnumKey(h, 0) + d = read_values(base, r"%s\%s" % (p, key)) + self.macros["$(FrameworkVersion)"] = d["version"] + + def sub(self, s): + for k, v in self.macros.items(): + s = s.replace(k, v) + return s + +def get_build_version(): + """Return the version of MSVC that was used to build Python. + + For Python 2.3 and up, the version number is included in + sys.version. For earlier versions, assume the compiler is MSVC 6. + """ + prefix = "MSC v." + i = sys.version.find(prefix) + if i == -1: + return 6 + i = i + len(prefix) + s, rest = sys.version[i:].split(" ", 1) + majorVersion = int(s[:-2]) - 6 + if majorVersion >= 13: + # v13 was skipped and should be v14 + majorVersion += 1 + minorVersion = int(s[2:3]) / 10.0 + # I don't think paths are affected by minor version in version 6 + if majorVersion == 6: + minorVersion = 0 + if majorVersion >= 6: + return majorVersion + minorVersion + # else we don't know what version of the compiler this is + return None + +def get_build_architecture(): + """Return the processor architecture. + + Possible results are "Intel" or "AMD64". + """ + + prefix = " bit (" + i = sys.version.find(prefix) + if i == -1: + return "Intel" + j = sys.version.find(")", i) + return sys.version[i+len(prefix):j] + +def normalize_and_reduce_paths(paths): + """Return a list of normalized paths with duplicates removed. + + The current order of paths is maintained. + """ + # Paths are normalized so things like: /a and /a/ aren't both preserved. + reduced_paths = [] + for p in paths: + np = os.path.normpath(p) + # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. + if np not in reduced_paths: + reduced_paths.append(np) + return reduced_paths + + +class MSVCCompiler(CCompiler) : + """Concrete class that implements an interface to Microsoft Visual C++, + as defined by the CCompiler abstract class.""" + + compiler_type = 'msvc' + + # Just set this so CCompiler's constructor doesn't barf. We currently + # don't use the 'set_executables()' bureaucracy provided by CCompiler, + # as it really isn't necessary for this sort of single-compiler class. + # Would be nice to have a consistent interface with UnixCCompiler, + # though, so it's worth thinking about. + executables = {} + + # Private class data (need to distinguish C from C++ source for compiler) + _c_extensions = ['.c'] + _cpp_extensions = ['.cc', '.cpp', '.cxx'] + _rc_extensions = ['.rc'] + _mc_extensions = ['.mc'] + + # Needed for the filename generation methods provided by the + # base class, CCompiler. + src_extensions = (_c_extensions + _cpp_extensions + + _rc_extensions + _mc_extensions) + res_extension = '.res' + obj_extension = '.obj' + static_lib_extension = '.lib' + shared_lib_extension = '.dll' + static_lib_format = shared_lib_format = '%s%s' + exe_extension = '.exe' + + def __init__(self, verbose=0, dry_run=0, force=0): + CCompiler.__init__ (self, verbose, dry_run, force) + self.__version = get_build_version() + self.__arch = get_build_architecture() + if self.__arch == "Intel": + # x86 + if self.__version >= 7: + self.__root = r"Software\Microsoft\VisualStudio" + self.__macros = MacroExpander(self.__version) + else: + self.__root = r"Software\Microsoft\Devstudio" + self.__product = "Visual Studio version %s" % self.__version + else: + # Win64. Assume this was built with the platform SDK + self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) + + self.initialized = False + + def initialize(self): + self.__paths = [] + if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): + # Assume that the SDK set up everything alright; don't try to be + # smarter + self.cc = "cl.exe" + self.linker = "link.exe" + self.lib = "lib.exe" + self.rc = "rc.exe" + self.mc = "mc.exe" + else: + self.__paths = self.get_msvc_paths("path") + + if len(self.__paths) == 0: + raise DistutilsPlatformError("Python was built with %s, " + "and extensions need to be built with the same " + "version of the compiler, but it isn't installed." + % self.__product) + + self.cc = self.find_exe("cl.exe") + self.linker = self.find_exe("link.exe") + self.lib = self.find_exe("lib.exe") + self.rc = self.find_exe("rc.exe") # resource compiler + self.mc = self.find_exe("mc.exe") # message compiler + self.set_path_env_var('lib') + self.set_path_env_var('include') + + # extend the MSVC path with the current path + try: + for p in os.environ['path'].split(';'): + self.__paths.append(p) + except KeyError: + pass + self.__paths = normalize_and_reduce_paths(self.__paths) + os.environ['path'] = ";".join(self.__paths) + + self.preprocess_options = None + if self.__arch == "Intel": + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', + '/Z7', '/D_DEBUG'] + else: + # Win64 + self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , + '/DNDEBUG'] + self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', + '/Z7', '/D_DEBUG'] + + self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] + if self.__version >= 7: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' + ] + else: + self.ldflags_shared_debug = [ + '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG' + ] + self.ldflags_static = [ '/nologo'] + + self.initialized = True + + # -- Worker methods ------------------------------------------------ + + def object_filenames(self, + source_filenames, + strip_dir=0, + output_dir=''): + # Copied from ccompiler.py, extended to return .res as 'object'-file + # for .rc input file + if output_dir is None: output_dir = '' + obj_names = [] + for src_name in source_filenames: + (base, ext) = os.path.splitext (src_name) + base = os.path.splitdrive(base)[1] # Chop off the drive + base = base[os.path.isabs(base):] # If abs, chop off leading / + if ext not in self.src_extensions: + # Better to raise an exception instead of silently continuing + # and later complain about sources and targets having + # different lengths + raise CompileError ("Don't know how to compile %s" % src_name) + if strip_dir: + base = os.path.basename (base) + if ext in self._rc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + elif ext in self._mc_extensions: + obj_names.append (os.path.join (output_dir, + base + self.res_extension)) + else: + obj_names.append (os.path.join (output_dir, + base + self.obj_extension)) + return obj_names + + + def compile(self, sources, + output_dir=None, macros=None, include_dirs=None, debug=0, + extra_preargs=None, extra_postargs=None, depends=None): + + if not self.initialized: + self.initialize() + compile_info = self._setup_compile(output_dir, macros, include_dirs, + sources, depends, extra_postargs) + macros, objects, extra_postargs, pp_opts, build = compile_info + + compile_opts = extra_preargs or [] + compile_opts.append ('/c') + if debug: + compile_opts.extend(self.compile_options_debug) + else: + compile_opts.extend(self.compile_options) + + for obj in objects: + try: + src, ext = build[obj] + except KeyError: + continue + if debug: + # pass the full pathname to MSVC in debug mode, + # this allows the debugger to find the source file + # without asking the user to browse for it + src = os.path.abspath(src) + + if ext in self._c_extensions: + input_opt = "/Tc" + src + elif ext in self._cpp_extensions: + input_opt = "/Tp" + src + elif ext in self._rc_extensions: + # compile .RC to .RES file + input_opt = src + output_opt = "/fo" + obj + try: + self.spawn([self.rc] + pp_opts + + [output_opt] + [input_opt]) + except DistutilsExecError as msg: + raise CompileError(msg) + continue + elif ext in self._mc_extensions: + # Compile .MC to .RC file to .RES file. + # * '-h dir' specifies the directory for the + # generated include file + # * '-r dir' specifies the target directory of the + # generated RC file and the binary message resource + # it includes + # + # For now (since there are no options to change this), + # we use the source-directory for the include file and + # the build directory for the RC file and message + # resources. This works at least for win32all. + h_dir = os.path.dirname(src) + rc_dir = os.path.dirname(obj) + try: + # first compile .MC to .RC and .H file + self.spawn([self.mc] + + ['-h', h_dir, '-r', rc_dir] + [src]) + base, _ = os.path.splitext (os.path.basename (src)) + rc_file = os.path.join (rc_dir, base + '.rc') + # then compile .RC to .RES file + self.spawn([self.rc] + + ["/fo" + obj] + [rc_file]) + + except DistutilsExecError as msg: + raise CompileError(msg) + continue + else: + # how to handle this file? + raise CompileError("Don't know how to compile %s to %s" + % (src, obj)) + + output_opt = "/Fo" + obj + try: + self.spawn([self.cc] + compile_opts + pp_opts + + [input_opt, output_opt] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + return objects + + + def create_static_lib(self, + objects, + output_libname, + output_dir=None, + debug=0, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + output_filename = self.library_filename(output_libname, + output_dir=output_dir) + + if self._need_link(objects, output_filename): + lib_args = objects + ['/OUT:' + output_filename] + if debug: + pass # XXX what goes here? + try: + self.spawn([self.lib] + lib_args) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + def link(self, + target_desc, + objects, + output_filename, + output_dir=None, + libraries=None, + library_dirs=None, + runtime_library_dirs=None, + export_symbols=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + build_temp=None, + target_lang=None): + + if not self.initialized: + self.initialize() + (objects, output_dir) = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + (libraries, library_dirs, runtime_library_dirs) = fixed_args + + if runtime_library_dirs: + self.warn ("I don't know what to do with 'runtime_library_dirs': " + + str (runtime_library_dirs)) + + lib_opts = gen_lib_options(self, + library_dirs, runtime_library_dirs, + libraries) + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + if target_desc == CCompiler.EXECUTABLE: + if debug: + ldflags = self.ldflags_shared_debug[1:] + else: + ldflags = self.ldflags_shared[1:] + else: + if debug: + ldflags = self.ldflags_shared_debug + else: + ldflags = self.ldflags_shared + + export_opts = [] + for sym in (export_symbols or []): + export_opts.append("/EXPORT:" + sym) + + ld_args = (ldflags + lib_opts + export_opts + + objects + ['/OUT:' + output_filename]) + + # The MSVC linker generates .lib and .exp files, which cannot be + # suppressed by any linker switches. The .lib files may even be + # needed! Make sure they are generated in the temporary build + # directory. Since they have different names for debug and release + # builds, they can go into the same directory. + if export_symbols is not None: + (dll_name, dll_ext) = os.path.splitext( + os.path.basename(output_filename)) + implib_file = os.path.join( + os.path.dirname(objects[0]), + self.library_filename(dll_name)) + ld_args.append ('/IMPLIB:' + implib_file) + + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + + self.mkpath(os.path.dirname(output_filename)) + try: + self.spawn([self.linker] + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + + else: + log.debug("skipping %s (up-to-date)", output_filename) + + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "/LIBPATH:" + dir + + def runtime_library_dir_option(self, dir): + raise DistutilsPlatformError( + "don't know how to set runtime library search path for MSVC++") + + def library_option(self, lib): + return self.library_filename(lib) + + + def find_library_file(self, dirs, lib, debug=0): + # Prefer a debugging library if found (and requested), but deal + # with it if we don't have one. + if debug: + try_names = [lib + "_d", lib] + else: + try_names = [lib] + for dir in dirs: + for name in try_names: + libfile = os.path.join(dir, self.library_filename (name)) + if os.path.exists(libfile): + return libfile + else: + # Oops, didn't find it in *any* of 'dirs' + return None + + # Helper methods for using the MSVC registry settings + + def find_exe(self, exe): + """Return path to an MSVC executable program. + + Tries to find the program in several places: first, one of the + MSVC program search paths from the registry; next, the directories + in the PATH environment variable. If any of those work, return an + absolute path that is known to exist. If none of them work, just + return the original program name, 'exe'. + """ + for p in self.__paths: + fn = os.path.join(os.path.abspath(p), exe) + if os.path.isfile(fn): + return fn + + # didn't find it; try existing path + for p in os.environ['Path'].split(';'): + fn = os.path.join(os.path.abspath(p),exe) + if os.path.isfile(fn): + return fn + + return exe + + def get_msvc_paths(self, path, platform='x86'): + """Get a list of devstudio directories (include, lib or path). + + Return a list of strings. The list will be empty if unable to + access the registry or appropriate registry keys not found. + """ + if not _can_read_reg: + return [] + + path = path + " dirs" + if self.__version >= 7: + key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" + % (self.__root, self.__version)) + else: + key = (r"%s\6.0\Build System\Components\Platforms" + r"\Win32 (%s)\Directories" % (self.__root, platform)) + + for base in HKEYS: + d = read_values(base, key) + if d: + if self.__version >= 7: + return self.__macros.sub(d[path]).split(";") + else: + return d[path].split(";") + # MSVC 6 seems to create the registry entries we need only when + # the GUI is run. + if self.__version == 6: + for base in HKEYS: + if read_values(base, r"%s\6.0" % self.__root) is not None: + self.warn("It seems you have Visual Studio 6 installed, " + "but the expected registry settings are not present.\n" + "You must at least run the Visual Studio GUI once " + "so that these entries are created.") + break + return [] + + def set_path_env_var(self, name): + """Set environment variable 'name' to an MSVC path type value. + + This is equivalent to a SET command prior to execution of spawned + commands. + """ + + if name == "lib": + p = self.get_msvc_paths("library") + else: + p = self.get_msvc_paths(name) + if p: + os.environ[name] = ';'.join(p) + + +if get_build_version() >= 8.0: + log.debug("Importing new compiler from distutils.msvc9compiler") + OldMSVCCompiler = MSVCCompiler + from distutils.msvc9compiler import MSVCCompiler + # get_build_architecture not really relevant now we support cross-compile + from distutils.msvc9compiler import MacroExpander diff --git a/setuptools/_distutils/spawn.py b/setuptools/_distutils/spawn.py new file mode 100644 index 00000000..aad277b0 --- /dev/null +++ b/setuptools/_distutils/spawn.py @@ -0,0 +1,119 @@ +"""distutils.spawn + +Provides the 'spawn()' function, a front-end to various platform- +specific functions for launching another program in a sub-process. +Also provides the 'find_executable()' to search the path for a given +executable name. +""" + +import sys +import os +import subprocess + +from distutils.errors import DistutilsPlatformError, DistutilsExecError +from distutils.debug import DEBUG +from distutils import log + + +if sys.platform == 'darwin': + _cfg_target = None + _cfg_target_split = None + + +def spawn(cmd, search_path=1, verbose=0, dry_run=0): + """Run another program, specified as a command list 'cmd', in a new process. + + 'cmd' is just the argument list for the new process, ie. + cmd[0] is the program to run and cmd[1:] are the rest of its arguments. + There is no way to run a program with a name different from that of its + executable. + + If 'search_path' is true (the default), the system's executable + search path will be used to find the program; otherwise, cmd[0] + must be the exact path to the executable. If 'dry_run' is true, + the command will not actually be run. + + Raise DistutilsExecError if running the program fails in any way; just + return on success. + """ + # cmd is documented as a list, but just in case some code passes a tuple + # in, protect our %-formatting code against horrible death + cmd = list(cmd) + + log.info(' '.join(cmd)) + if dry_run: + return + + if search_path: + executable = find_executable(cmd[0]) + if executable is not None: + cmd[0] = executable + + env = None + if sys.platform == 'darwin': + global _cfg_target, _cfg_target_split + if _cfg_target is None: + from distutils import sysconfig + _cfg_target = sysconfig.get_config_var( + 'MACOSX_DEPLOYMENT_TARGET') or '' + if _cfg_target: + _cfg_target_split = [int(x) for x in _cfg_target.split('.')] + if _cfg_target: + # ensure that the deployment target of build process is not less + # than that used when the interpreter was built. This ensures + # extension modules are built with correct compatibility values + cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target) + if _cfg_target_split > [int(x) for x in cur_target.split('.')]: + my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: ' + 'now "%s" but "%s" during configure' + % (cur_target, _cfg_target)) + raise DistutilsPlatformError(my_msg) + env = dict(os.environ, + MACOSX_DEPLOYMENT_TARGET=cur_target) + + proc = subprocess.Popen(cmd, env=env) + proc.wait() + exitcode = proc.returncode + + if exitcode: + if not DEBUG: + cmd = cmd[0] + raise DistutilsExecError( + "command %r failed with exit code %s" % (cmd, exitcode)) + + +def find_executable(executable, path=None): + """Tries to find 'executable' in the directories listed in 'path'. + + A string listing directories separated by 'os.pathsep'; defaults to + os.environ['PATH']. Returns the complete filename or None if not found. + """ + _, ext = os.path.splitext(executable) + if (sys.platform == 'win32') and (ext != '.exe'): + executable = executable + '.exe' + + if os.path.isfile(executable): + return executable + + if path is None: + path = os.environ.get('PATH', None) + if path is None: + try: + path = os.confstr("CS_PATH") + except (AttributeError, ValueError): + # os.confstr() or CS_PATH is not available + path = os.defpath + # bpo-35755: Don't use os.defpath if the PATH environment variable is + # set to an empty string + + # PATH='' doesn't match, whereas PATH=':' looks in the current directory + if not path: + return None + + paths = path.split(os.pathsep) + for p in paths: + f = os.path.join(p, executable) + if os.path.isfile(f): + # the file exists, we have a shot at spawn working + return f + return None diff --git a/setuptools/_distutils/sysconfig.py b/setuptools/_distutils/sysconfig.py new file mode 100644 index 00000000..879b6981 --- /dev/null +++ b/setuptools/_distutils/sysconfig.py @@ -0,0 +1,573 @@ +"""Provide access to Python's configuration information. The specific +configuration variables available depend heavily on the platform and +configuration. The values may be retrieved using +get_config_var(name), and the list of variables is available via +get_config_vars().keys(). Additional convenience functions are also +available. + +Written by: Fred L. Drake, Jr. +Email: +""" + +import _imp +import os +import re +import sys + +from .errors import DistutilsPlatformError + +IS_PYPY = '__pypy__' in sys.builtin_module_names + +# These are needed in a couple of spots, so just compute them once. +PREFIX = os.path.normpath(sys.prefix) +EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +BASE_PREFIX = os.path.normpath(sys.base_prefix) +BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) + +# Path to the base directory of the project. On Windows the binary may +# live in project/PCbuild/win32 or project/PCbuild/amd64. +# set for cross builds +if "_PYTHON_PROJECT_BASE" in os.environ: + project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"]) +else: + if sys.executable: + project_base = os.path.dirname(os.path.abspath(sys.executable)) + else: + # sys.executable can be empty if argv[0] has been changed and Python is + # unable to retrieve the real program name + project_base = os.getcwd() + + +# python_build: (Boolean) if true, we're either building Python or +# building an extension with an un-installed Python, so we use +# different (hard-wired) directories. +def _is_python_source_dir(d): + for fn in ("Setup", "Setup.local"): + if os.path.isfile(os.path.join(d, "Modules", fn)): + return True + return False + +_sys_home = getattr(sys, '_home', None) + +if os.name == 'nt': + def _fix_pcbuild(d): + if d and os.path.normcase(d).startswith( + os.path.normcase(os.path.join(PREFIX, "PCbuild"))): + return PREFIX + return d + project_base = _fix_pcbuild(project_base) + _sys_home = _fix_pcbuild(_sys_home) + +def _python_build(): + if _sys_home: + return _is_python_source_dir(_sys_home) + return _is_python_source_dir(project_base) + +python_build = _python_build() + + +# Calculate the build qualifier flags if they are defined. Adding the flags +# to the include and lib directories only makes sense for an installation, not +# an in-source build. +build_flags = '' +try: + if not python_build: + build_flags = sys.abiflags +except AttributeError: + # It's not a configure-based build, so the sys module doesn't have + # this attribute, which is fine. + pass + +def get_python_version(): + """Return a string containing the major and minor Python version, + leaving off the patchlevel. Sample return values could be '1.5' + or '2.2'. + """ + return '%d.%d' % sys.version_info[:2] + + +def get_python_inc(plat_specific=0, prefix=None): + """Return the directory containing installed Python header files. + + If 'plat_specific' is false (the default), this is the path to the + non-platform-specific header files, i.e. Python.h and so on; + otherwise, this is the path to platform-specific header files + (namely pyconfig.h). + + If 'prefix' is supplied, use it instead of sys.base_prefix or + sys.base_exec_prefix -- i.e., ignore 'plat_specific'. + """ + if prefix is None: + prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX + if IS_PYPY: + return os.path.join(prefix, 'include') + elif os.name == "posix": + if python_build: + # Assume the executable is in the build directory. The + # pyconfig.h file should be in the same directory. Since + # the build directory may not be the source directory, we + # must use "srcdir" from the makefile to find the "Include" + # directory. + if plat_specific: + return _sys_home or project_base + else: + incdir = os.path.join(get_config_var('srcdir'), 'Include') + return os.path.normpath(incdir) + python_dir = 'python' + get_python_version() + build_flags + return os.path.join(prefix, "include", python_dir) + elif os.name == "nt": + if python_build: + # Include both the include and PC dir to ensure we can find + # pyconfig.h + return (os.path.join(prefix, "include") + os.path.pathsep + + os.path.join(prefix, "PC")) + return os.path.join(prefix, "include") + else: + raise DistutilsPlatformError( + "I don't know where Python installs its C header files " + "on platform '%s'" % os.name) + + +def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): + """Return the directory containing the Python library (standard or + site additions). + + If 'plat_specific' is true, return the directory containing + platform-specific modules, i.e. any module from a non-pure-Python + module distribution; otherwise, return the platform-shared library + directory. If 'standard_lib' is true, return the directory + containing standard Python library modules; otherwise, return the + directory for site-specific modules. + + If 'prefix' is supplied, use it instead of sys.base_prefix or + sys.base_exec_prefix -- i.e., ignore 'plat_specific'. + """ + if IS_PYPY: + # PyPy-specific schema + if prefix is None: + prefix = PREFIX + if standard_lib: + return os.path.join(prefix, "lib-python", sys.version[0]) + return os.path.join(prefix, 'site-packages') + + if prefix is None: + if standard_lib: + prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX + else: + prefix = plat_specific and EXEC_PREFIX or PREFIX + + if os.name == "posix": + if plat_specific or standard_lib: + # Platform-specific modules (any module from a non-pure-Python + # module distribution) or standard Python library modules. + libdir = getattr(sys, "platlibdir", "lib") + else: + # Pure Python + libdir = "lib" + libpython = os.path.join(prefix, libdir, + "python" + get_python_version()) + if standard_lib: + return libpython + else: + return os.path.join(libpython, "site-packages") + elif os.name == "nt": + if standard_lib: + return os.path.join(prefix, "Lib") + else: + return os.path.join(prefix, "Lib", "site-packages") + else: + raise DistutilsPlatformError( + "I don't know where Python installs its library " + "on platform '%s'" % os.name) + + + +def customize_compiler(compiler): + """Do any platform-specific customization of a CCompiler instance. + + Mainly needed on Unix, so we can plug in the information that + varies across Unices and is stored in Python's Makefile. + """ + if compiler.compiler_type == "unix": + if sys.platform == "darwin": + # Perform first-time customization of compiler-related + # config vars on OS X now that we know we need a compiler. + # This is primarily to support Pythons from binary + # installers. The kind and paths to build tools on + # the user system may vary significantly from the system + # that Python itself was built on. Also the user OS + # version and build tools may not support the same set + # of CPU architectures for universal builds. + global _config_vars + # Use get_config_var() to ensure _config_vars is initialized. + if not get_config_var('CUSTOMIZED_OSX_COMPILER'): + import _osx_support + _osx_support.customize_compiler(_config_vars) + _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' + + (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ + get_config_vars('CC', 'CXX', 'CFLAGS', + 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') + + if 'CC' in os.environ: + newcc = os.environ['CC'] + if (sys.platform == 'darwin' + and 'LDSHARED' not in os.environ + and ldshared.startswith(cc)): + # On OS X, if CC is overridden, use that as the default + # command for LDSHARED as well + ldshared = newcc + ldshared[len(cc):] + cc = newcc + if 'CXX' in os.environ: + cxx = os.environ['CXX'] + if 'LDSHARED' in os.environ: + ldshared = os.environ['LDSHARED'] + if 'CPP' in os.environ: + cpp = os.environ['CPP'] + else: + cpp = cc + " -E" # not always + if 'LDFLAGS' in os.environ: + ldshared = ldshared + ' ' + os.environ['LDFLAGS'] + if 'CFLAGS' in os.environ: + cflags = cflags + ' ' + os.environ['CFLAGS'] + ldshared = ldshared + ' ' + os.environ['CFLAGS'] + if 'CPPFLAGS' in os.environ: + cpp = cpp + ' ' + os.environ['CPPFLAGS'] + cflags = cflags + ' ' + os.environ['CPPFLAGS'] + ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] + if 'AR' in os.environ: + ar = os.environ['AR'] + if 'ARFLAGS' in os.environ: + archiver = ar + ' ' + os.environ['ARFLAGS'] + else: + archiver = ar + ' ' + ar_flags + + cc_cmd = cc + ' ' + cflags + compiler.set_executables( + preprocessor=cpp, + compiler=cc_cmd, + compiler_so=cc_cmd + ' ' + ccshared, + compiler_cxx=cxx, + linker_so=ldshared, + linker_exe=cc, + archiver=archiver) + + compiler.shared_lib_extension = shlib_suffix + + +def get_config_h_filename(): + """Return full pathname of installed pyconfig.h file.""" + if python_build: + if os.name == "nt": + inc_dir = os.path.join(_sys_home or project_base, "PC") + else: + inc_dir = _sys_home or project_base + else: + inc_dir = get_python_inc(plat_specific=1) + + return os.path.join(inc_dir, 'pyconfig.h') + + +def get_makefile_filename(): + """Return full pathname of installed Makefile from the Python build.""" + if python_build: + return os.path.join(_sys_home or project_base, "Makefile") + lib_dir = get_python_lib(plat_specific=0, standard_lib=1) + config_file = 'config-{}{}'.format(get_python_version(), build_flags) + if hasattr(sys.implementation, '_multiarch'): + config_file += '-%s' % sys.implementation._multiarch + return os.path.join(lib_dir, config_file, 'Makefile') + + +def parse_config_h(fp, g=None): + """Parse a config.h-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + if g is None: + g = {} + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + # + while True: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + try: v = int(v) + except ValueError: pass + g[n] = v + else: + m = undef_rx.match(line) + if m: + g[m.group(1)] = 0 + return g + + +# Regexes needed for parsing Makefile (and similar syntaxes, +# like old-style Setup files). +_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") +_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") +_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + +def parse_makefile(fn, g=None): + """Parse a Makefile-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + from distutils.text_file import TextFile + fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape") + + if g is None: + g = {} + done = {} + notdone = {} + + while True: + line = fp.readline() + if line is None: # eof + break + m = _variable_rx.match(line) + if m: + n, v = m.group(1, 2) + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: + notdone[n] = v + else: + try: + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v + + # Variables with a 'PY_' prefix in the makefile. These need to + # be made available without that prefix through sysconfig. + # Special care is needed to ensure that variable expansion works, even + # if the expansion uses the name without a prefix. + renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') + + # do variable interpolation here + while notdone: + for name in list(notdone): + value = notdone[name] + m = _findvar1_rx.search(value) or _findvar2_rx.search(value) + if m: + n = m.group(1) + found = True + if n in done: + item = str(done[n]) + elif n in notdone: + # get it on a subsequent round + found = False + elif n in os.environ: + # do it like make: fall back to environment + item = os.environ[n] + + elif n in renamed_variables: + if name.startswith('PY_') and name[3:] in renamed_variables: + item = "" + + elif 'PY_' + n in notdone: + found = False + + else: + item = str(done['PY_' + n]) + else: + done[n] = item = "" + if found: + after = value[m.end():] + value = value[:m.start()] + item + after + if "$" in after: + notdone[name] = value + else: + try: value = int(value) + except ValueError: + done[name] = value.strip() + else: + done[name] = value + del notdone[name] + + if name.startswith('PY_') \ + and name[3:] in renamed_variables: + + name = name[3:] + if name not in done: + done[name] = value + else: + # bogus variable reference; just drop it since we can't deal + del notdone[name] + + fp.close() + + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + + # save the results in the global dictionary + g.update(done) + return g + + +def expand_makefile_vars(s, vars): + """Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in + 'string' according to 'vars' (a dictionary mapping variable names to + values). Variables not present in 'vars' are silently expanded to the + empty string. The variable values in 'vars' should not contain further + variable expansions; if 'vars' is the output of 'parse_makefile()', + you're fine. Returns a variable-expanded version of 's'. + """ + + # This algorithm does multiple expansion, so if vars['foo'] contains + # "${bar}", it will expand ${foo} to ${bar}, and then expand + # ${bar}... and so forth. This is fine as long as 'vars' comes from + # 'parse_makefile()', which takes care of such expansions eagerly, + # according to make's variable expansion semantics. + + while True: + m = _findvar1_rx.search(s) or _findvar2_rx.search(s) + if m: + (beg, end) = m.span() + s = s[0:beg] + vars.get(m.group(1)) + s[end:] + else: + break + return s + + +_config_vars = None + +def _init_posix(): + """Initialize the module as appropriate for POSIX systems.""" + # _sysconfigdata is generated at build time, see the sysconfig module + name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME', + '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + abi=sys.abiflags, + platform=sys.platform, + multiarch=getattr(sys.implementation, '_multiarch', ''), + )) + try: + _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + except ImportError: + # Python 3.5 and pypy 7.3.1 + _temp = __import__( + '_sysconfigdata', globals(), locals(), ['build_time_vars'], 0) + build_time_vars = _temp.build_time_vars + global _config_vars + _config_vars = {} + _config_vars.update(build_time_vars) + + +def _init_nt(): + """Initialize the module as appropriate for NT""" + g = {} + # set basic install directories + g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1) + g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1) + + # XXX hmmm.. a normal install puts include files here + g['INCLUDEPY'] = get_python_inc(plat_specific=0) + + g['EXT_SUFFIX'] = _imp.extension_suffixes()[0] + g['EXE'] = ".exe" + g['VERSION'] = get_python_version().replace(".", "") + g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) + + global _config_vars + _config_vars = g + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. Generally this includes + everything needed to build extensions and install both pure modules and + extensions. On Unix, this means every variable defined in Python's + installed Makefile; on Windows it's a much smaller set. + + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _config_vars + if _config_vars is None: + func = globals().get("_init_" + os.name) + if func: + func() + else: + _config_vars = {} + + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # Distutils. + _config_vars['prefix'] = PREFIX + _config_vars['exec_prefix'] = EXEC_PREFIX + + if not IS_PYPY: + # For backward compatibility, see issue19555 + SO = _config_vars.get('EXT_SUFFIX') + if SO is not None: + _config_vars['SO'] = SO + + # Always convert srcdir to an absolute path + srcdir = _config_vars.get('srcdir', project_base) + if os.name == 'posix': + if python_build: + # If srcdir is a relative path (typically '.' or '..') + # then it should be interpreted relative to the directory + # containing Makefile. + base = os.path.dirname(get_makefile_filename()) + srcdir = os.path.join(base, srcdir) + else: + # srcdir is not meaningful since the installation is + # spread about the filesystem. We choose the + # directory containing the Makefile since we know it + # exists. + srcdir = os.path.dirname(get_makefile_filename()) + _config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir)) + + # Convert srcdir into an absolute path if it appears necessary. + # Normally it is relative to the build directory. However, during + # testing, for example, we might be running a non-installed python + # from a different directory. + if python_build and os.name == "posix": + base = project_base + if (not os.path.isabs(_config_vars['srcdir']) and + base != os.getcwd()): + # srcdir is relative and we are not in the same directory + # as the executable. Assume executable is in the build + # directory and make srcdir absolute. + srcdir = os.path.join(base, _config_vars['srcdir']) + _config_vars['srcdir'] = os.path.normpath(srcdir) + + # OS X platforms require special customization to handle + # multi-architecture, multi-os-version installers + if sys.platform == 'darwin': + import _osx_support + _osx_support.customize_config_vars(_config_vars) + + if args: + vals = [] + for name in args: + vals.append(_config_vars.get(name)) + return vals + else: + return _config_vars + +def get_config_var(name): + """Return the value of a single variable using the dictionary + returned by 'get_config_vars()'. Equivalent to + get_config_vars().get(name) + """ + if name == 'SO': + import warnings + warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2) + return get_config_vars().get(name) diff --git a/setuptools/_distutils/tests/Setup.sample b/setuptools/_distutils/tests/Setup.sample new file mode 100644 index 00000000..36c4290d --- /dev/null +++ b/setuptools/_distutils/tests/Setup.sample @@ -0,0 +1,67 @@ +# Setup file from the pygame project + +#--StartConfig +SDL = -I/usr/include/SDL -D_REENTRANT -lSDL +FONT = -lSDL_ttf +IMAGE = -lSDL_image +MIXER = -lSDL_mixer +SMPEG = -lsmpeg +PNG = -lpng +JPEG = -ljpeg +SCRAP = -lX11 +PORTMIDI = -lportmidi +PORTTIME = -lporttime +#--EndConfig + +#DEBUG = -C-W -C-Wall +DEBUG = + +#the following modules are optional. you will want to compile +#everything you can, but you can ignore ones you don't have +#dependencies for, just comment them out + +imageext src/imageext.c $(SDL) $(IMAGE) $(PNG) $(JPEG) $(DEBUG) +font src/font.c $(SDL) $(FONT) $(DEBUG) +mixer src/mixer.c $(SDL) $(MIXER) $(DEBUG) +mixer_music src/music.c $(SDL) $(MIXER) $(DEBUG) +_numericsurfarray src/_numericsurfarray.c $(SDL) $(DEBUG) +_numericsndarray src/_numericsndarray.c $(SDL) $(MIXER) $(DEBUG) +movie src/movie.c $(SDL) $(SMPEG) $(DEBUG) +scrap src/scrap.c $(SDL) $(SCRAP) $(DEBUG) +_camera src/_camera.c src/camera_v4l2.c src/camera_v4l.c $(SDL) $(DEBUG) +pypm src/pypm.c $(SDL) $(PORTMIDI) $(PORTTIME) $(DEBUG) + +GFX = src/SDL_gfx/SDL_gfxPrimitives.c +#GFX = src/SDL_gfx/SDL_gfxBlitFunc.c src/SDL_gfx/SDL_gfxPrimitives.c +gfxdraw src/gfxdraw.c $(SDL) $(GFX) $(DEBUG) + + + +#these modules are required for pygame to run. they only require +#SDL as a dependency. these should not be altered + +base src/base.c $(SDL) $(DEBUG) +cdrom src/cdrom.c $(SDL) $(DEBUG) +color src/color.c $(SDL) $(DEBUG) +constants src/constants.c $(SDL) $(DEBUG) +display src/display.c $(SDL) $(DEBUG) +event src/event.c $(SDL) $(DEBUG) +fastevent src/fastevent.c src/fastevents.c $(SDL) $(DEBUG) +key src/key.c $(SDL) $(DEBUG) +mouse src/mouse.c $(SDL) $(DEBUG) +rect src/rect.c $(SDL) $(DEBUG) +rwobject src/rwobject.c $(SDL) $(DEBUG) +surface src/surface.c src/alphablit.c src/surface_fill.c $(SDL) $(DEBUG) +surflock src/surflock.c $(SDL) $(DEBUG) +time src/time.c $(SDL) $(DEBUG) +joystick src/joystick.c $(SDL) $(DEBUG) +draw src/draw.c $(SDL) $(DEBUG) +image src/image.c $(SDL) $(DEBUG) +overlay src/overlay.c $(SDL) $(DEBUG) +transform src/transform.c src/rotozoom.c src/scale2x.c src/scale_mmx.c $(SDL) $(DEBUG) +mask src/mask.c src/bitmask.c $(SDL) $(DEBUG) +bufferproxy src/bufferproxy.c $(SDL) $(DEBUG) +pixelarray src/pixelarray.c $(SDL) $(DEBUG) +_arraysurfarray src/_arraysurfarray.c $(SDL) $(DEBUG) + + diff --git a/setuptools/_distutils/tests/__init__.py b/setuptools/_distutils/tests/__init__.py new file mode 100644 index 00000000..5d2e69e3 --- /dev/null +++ b/setuptools/_distutils/tests/__init__.py @@ -0,0 +1,42 @@ +"""Test suite for distutils. + +This test suite consists of a collection of test modules in the +distutils.tests package. Each test module has a name starting with +'test' and contains a function test_suite(). The function is expected +to return an initialized unittest.TestSuite instance. + +Tests for the command classes in the distutils.command package are +included in distutils.tests as well, instead of using a separate +distutils.command.tests package, since command identification is done +by import rather than matching pre-defined names. + +""" + +import os +import sys +import unittest +import warnings +from test.support import run_unittest + + +here = os.path.dirname(__file__) or os.curdir + + +def test_suite(): + old_filters = warnings.filters[:] + suite = unittest.TestSuite() + for fn in os.listdir(here): + if fn.startswith("test") and fn.endswith(".py"): + modname = "distutils.tests." + fn[:-3] + __import__(modname) + module = sys.modules[modname] + suite.addTest(module.test_suite()) + # bpo-40055: Save/restore warnings filters to leave them unchanged. + # Importing tests imports docutils which imports pkg_resources which adds a + # warnings filter. + warnings.filters[:] = old_filters + return suite + + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/includetest.rst b/setuptools/_distutils/tests/includetest.rst new file mode 100644 index 00000000..d7b4ae38 --- /dev/null +++ b/setuptools/_distutils/tests/includetest.rst @@ -0,0 +1 @@ +This should be included. diff --git a/setuptools/_distutils/tests/py35compat.py b/setuptools/_distutils/tests/py35compat.py new file mode 100644 index 00000000..0c755261 --- /dev/null +++ b/setuptools/_distutils/tests/py35compat.py @@ -0,0 +1,77 @@ +""" +Backward compatibility support for Python 3.5 +""" + +import sys +import test.support +import subprocess + + +# copied from Python 3.9 test.support module +def _missing_compiler_executable(cmd_names=[]): + """Check if the compiler components used to build the interpreter exist. + + Check for the existence of the compiler executables whose names are listed + in 'cmd_names' or all the compiler executables when 'cmd_names' is empty + and return the first missing executable or None when none is found + missing. + + """ + from distutils import ccompiler, sysconfig, spawn + compiler = ccompiler.new_compiler() + sysconfig.customize_compiler(compiler) + for name in compiler.executables: + if cmd_names and name not in cmd_names: + continue + cmd = getattr(compiler, name) + if cmd_names: + assert cmd is not None, \ + "the '%s' executable is not configured" % name + elif not cmd: + continue + if spawn.find_executable(cmd[0]) is None: + return cmd[0] + + +missing_compiler_executable = vars(test.support).setdefault( + 'missing_compiler_executable', + _missing_compiler_executable, +) + + +try: + from test.support import unix_shell +except ImportError: + # Adapted from Python 3.9 test.support module + is_android = hasattr(sys, 'getandroidapilevel') + unix_shell = ( + None if sys.platform == 'win32' else + '/system/bin/sh' if is_android else + '/bin/sh' + ) + + +# copied from Python 3.9 subprocess module +def _optim_args_from_interpreter_flags(): + """Return a list of command-line arguments reproducing the current + optimization settings in sys.flags.""" + args = [] + value = sys.flags.optimize + if value > 0: + args.append('-' + 'O' * value) + return args + + +vars(subprocess).setdefault( + '_optim_args_from_interpreter_flags', + _optim_args_from_interpreter_flags, +) + + +def adapt_glob(regex): + """ + Supply legacy expectation on Python 3.5 + """ + if sys.version_info > (3, 6): + return regex + return regex.replace('(?s:', '').replace(r')\Z', r'\Z(?ms)') diff --git a/setuptools/_distutils/tests/support.py b/setuptools/_distutils/tests/support.py new file mode 100644 index 00000000..259af882 --- /dev/null +++ b/setuptools/_distutils/tests/support.py @@ -0,0 +1,209 @@ +"""Support code for distutils test cases.""" +import os +import sys +import shutil +import tempfile +import unittest +import sysconfig +from copy import deepcopy +import test.support + +from distutils import log +from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL +from distutils.core import Distribution + + +class LoggingSilencer(object): + + def setUp(self): + super().setUp() + self.threshold = log.set_threshold(log.FATAL) + # catching warnings + # when log will be replaced by logging + # we won't need such monkey-patch anymore + self._old_log = log.Log._log + log.Log._log = self._log + self.logs = [] + + def tearDown(self): + log.set_threshold(self.threshold) + log.Log._log = self._old_log + super().tearDown() + + def _log(self, level, msg, args): + if level not in (DEBUG, INFO, WARN, ERROR, FATAL): + raise ValueError('%s wrong log level' % str(level)) + if not isinstance(msg, str): + raise TypeError("msg should be str, not '%.200s'" + % (type(msg).__name__)) + self.logs.append((level, msg, args)) + + def get_logs(self, *levels): + return [msg % args for level, msg, args + in self.logs if level in levels] + + def clear_logs(self): + self.logs = [] + + +class TempdirManager(object): + """Mix-in class that handles temporary directories for test cases. + + This is intended to be used with unittest.TestCase. + """ + + def setUp(self): + super().setUp() + self.old_cwd = os.getcwd() + self.tempdirs = [] + + def tearDown(self): + # Restore working dir, for Solaris and derivatives, where rmdir() + # on the current directory fails. + os.chdir(self.old_cwd) + super().tearDown() + while self.tempdirs: + tmpdir = self.tempdirs.pop() + test.support.rmtree(tmpdir) + + def mkdtemp(self): + """Create a temporary directory that will be cleaned up. + + Returns the path of the directory. + """ + d = tempfile.mkdtemp() + self.tempdirs.append(d) + return d + + def write_file(self, path, content='xxx'): + """Writes a file in the given path. + + + path can be a string or a sequence. + """ + if isinstance(path, (list, tuple)): + path = os.path.join(*path) + f = open(path, 'w') + try: + f.write(content) + finally: + f.close() + + def create_dist(self, pkg_name='foo', **kw): + """Will generate a test environment. + + This function creates: + - a Distribution instance using keywords + - a temporary directory with a package structure + + It returns the package directory and the distribution + instance. + """ + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, pkg_name) + os.mkdir(pkg_dir) + dist = Distribution(attrs=kw) + + return pkg_dir, dist + + +class DummyCommand: + """Class to store options for retrieval via set_undefined_options().""" + + def __init__(self, **kwargs): + for kw, val in kwargs.items(): + setattr(self, kw, val) + + def ensure_finalized(self): + pass + + +class EnvironGuard(object): + + def setUp(self): + super(EnvironGuard, self).setUp() + self.old_environ = deepcopy(os.environ) + + def tearDown(self): + for key, value in self.old_environ.items(): + if os.environ.get(key) != value: + os.environ[key] = value + + for key in tuple(os.environ.keys()): + if key not in self.old_environ: + del os.environ[key] + + super(EnvironGuard, self).tearDown() + + +def copy_xxmodule_c(directory): + """Helper for tests that need the xxmodule.c source file. + + Example use: + + def test_compile(self): + copy_xxmodule_c(self.tmpdir) + self.assertIn('xxmodule.c', os.listdir(self.tmpdir)) + + If the source file can be found, it will be copied to *directory*. If not, + the test will be skipped. Errors during copy are not caught. + """ + filename = _get_xxmodule_path() + if filename is None: + raise unittest.SkipTest('cannot find xxmodule.c (test must run in ' + 'the python build dir)') + shutil.copy(filename, directory) + + +def _get_xxmodule_path(): + srcdir = sysconfig.get_config_var('srcdir') + candidates = [ + # use installed copy if available + os.path.join(os.path.dirname(__file__), 'xxmodule.c'), + # otherwise try using copy from build directory + os.path.join(srcdir, 'Modules', 'xxmodule.c'), + # srcdir mysteriously can be $srcdir/Lib/distutils/tests when + # this file is run from its parent directory, so walk up the + # tree to find the real srcdir + os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'), + ] + for path in candidates: + if os.path.exists(path): + return path + + +def fixup_build_ext(cmd): + """Function needed to make build_ext tests pass. + + When Python was built with --enable-shared on Unix, -L. is not enough to + find libpython.so, because regrtest runs in a tempdir, not in the + source directory where the .so lives. + + When Python was built with in debug mode on Windows, build_ext commands + need their debug attribute set, and it is not done automatically for + some reason. + + This function handles both of these things. Example use: + + cmd = build_ext(dist) + support.fixup_build_ext(cmd) + cmd.ensure_finalized() + + Unlike most other Unix platforms, Mac OS X embeds absolute paths + to shared libraries into executables, so the fixup is not needed there. + """ + if os.name == 'nt': + cmd.debug = sys.executable.endswith('_d.exe') + elif sysconfig.get_config_var('Py_ENABLE_SHARED'): + # To further add to the shared builds fun on Unix, we can't just add + # library_dirs to the Extension() instance because that doesn't get + # plumbed through to the final compiler command. + runshared = sysconfig.get_config_var('RUNSHARED') + if runshared is None: + cmd.library_dirs = ['.'] + else: + if sys.platform == 'darwin': + cmd.library_dirs = [] + else: + name, equals, value = runshared.partition('=') + cmd.library_dirs = [d for d in value.split(os.pathsep) if d] diff --git a/setuptools/_distutils/tests/test_archive_util.py b/setuptools/_distutils/tests/test_archive_util.py new file mode 100644 index 00000000..e9aad0e4 --- /dev/null +++ b/setuptools/_distutils/tests/test_archive_util.py @@ -0,0 +1,394 @@ +# -*- coding: utf-8 -*- +"""Tests for distutils.archive_util.""" +import unittest +import os +import sys +import tarfile +from os.path import splitdrive +import warnings + +from distutils import archive_util +from distutils.archive_util import (check_archive_formats, make_tarball, + make_zipfile, make_archive, + ARCHIVE_FORMATS) +from distutils.spawn import find_executable, spawn +from distutils.tests import support +from test.support import check_warnings, run_unittest, patch, change_cwd + +try: + import grp + import pwd + UID_GID_SUPPORT = True +except ImportError: + UID_GID_SUPPORT = False + +try: + import zipfile + ZIP_SUPPORT = True +except ImportError: + ZIP_SUPPORT = find_executable('zip') + +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + +try: + import bz2 +except ImportError: + bz2 = None + +try: + import lzma +except ImportError: + lzma = None + +def can_fs_encode(filename): + """ + Return True if the filename can be saved in the file system. + """ + if os.path.supports_unicode_filenames: + return True + try: + filename.encode(sys.getfilesystemencoding()) + except UnicodeEncodeError: + return False + return True + + +class ArchiveUtilTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_make_tarball(self, name='archive'): + # creating something to tar + tmpdir = self._create_files() + self._make_tarball(tmpdir, name, '.tar.gz') + # trying an uncompressed one + self._make_tarball(tmpdir, name, '.tar', compress=None) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_make_tarball_gzip(self): + tmpdir = self._create_files() + self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip') + + @unittest.skipUnless(bz2, 'Need bz2 support to run') + def test_make_tarball_bzip2(self): + tmpdir = self._create_files() + self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2') + + @unittest.skipUnless(lzma, 'Need lzma support to run') + def test_make_tarball_xz(self): + tmpdir = self._create_files() + self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz') + + @unittest.skipUnless(can_fs_encode('Ã¥rchiv'), + 'File system cannot handle this filename') + def test_make_tarball_latin1(self): + """ + Mirror test_make_tarball, except filename contains latin characters. + """ + self.test_make_tarball('Ã¥rchiv') # note this isn't a real word + + @unittest.skipUnless(can_fs_encode('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–'), + 'File system cannot handle this filename') + def test_make_tarball_extended(self): + """ + Mirror test_make_tarball, except filename contains extended + characters outside the latin charset. + """ + self.test_make_tarball('ã®ã‚¢ãƒ¼ã‚«ã‚¤ãƒ–') # japanese for archive + + def _make_tarball(self, tmpdir, target_name, suffix, **kwargs): + tmpdir2 = self.mkdtemp() + unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], + "source and target should be on same drive") + + base_name = os.path.join(tmpdir2, target_name) + + # working with relative paths to avoid tar warnings + with change_cwd(tmpdir): + make_tarball(splitdrive(base_name)[1], 'dist', **kwargs) + + # check if the compressed tarball was created + tarball = base_name + suffix + self.assertTrue(os.path.exists(tarball)) + self.assertEqual(self._tarinfo(tarball), self._created_files) + + def _tarinfo(self, path): + tar = tarfile.open(path) + try: + names = tar.getnames() + names.sort() + return names + finally: + tar.close() + + _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', + 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] + _created_files = [p.rstrip('/') for p in _zip_created_files] + + def _create_files(self): + # creating something to tar + tmpdir = self.mkdtemp() + dist = os.path.join(tmpdir, 'dist') + os.mkdir(dist) + self.write_file([dist, 'file1'], 'xxx') + self.write_file([dist, 'file2'], 'xxx') + os.mkdir(os.path.join(dist, 'sub')) + self.write_file([dist, 'sub', 'file3'], 'xxx') + os.mkdir(os.path.join(dist, 'sub2')) + return tmpdir + + @unittest.skipUnless(find_executable('tar') and find_executable('gzip') + and ZLIB_SUPPORT, + 'Need the tar, gzip and zlib command to run') + def test_tarfile_vs_tar(self): + tmpdir = self._create_files() + tmpdir2 = self.mkdtemp() + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist') + finally: + os.chdir(old_dir) + + # check if the compressed tarball was created + tarball = base_name + '.tar.gz' + self.assertTrue(os.path.exists(tarball)) + + # now create another tarball using `tar` + tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') + tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist'] + gzip_cmd = ['gzip', '-f', '-9', 'archive2.tar'] + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + spawn(tar_cmd) + spawn(gzip_cmd) + finally: + os.chdir(old_dir) + + self.assertTrue(os.path.exists(tarball2)) + # let's compare both tarballs + self.assertEqual(self._tarinfo(tarball), self._created_files) + self.assertEqual(self._tarinfo(tarball2), self._created_files) + + # trying an uncompressed one + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist', compress=None) + finally: + os.chdir(old_dir) + tarball = base_name + '.tar' + self.assertTrue(os.path.exists(tarball)) + + # now for a dry_run + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist', compress=None, dry_run=True) + finally: + os.chdir(old_dir) + tarball = base_name + '.tar' + self.assertTrue(os.path.exists(tarball)) + + @unittest.skipUnless(find_executable('compress'), + 'The compress program is required') + def test_compress_deprecated(self): + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') + + # using compress and testing the PendingDeprecationWarning + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + with check_warnings() as w: + warnings.simplefilter("always") + make_tarball(base_name, 'dist', compress='compress') + finally: + os.chdir(old_dir) + tarball = base_name + '.tar.Z' + self.assertTrue(os.path.exists(tarball)) + self.assertEqual(len(w.warnings), 1) + + # same test with dry_run + os.remove(tarball) + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + with check_warnings() as w: + warnings.simplefilter("always") + make_tarball(base_name, 'dist', compress='compress', + dry_run=True) + finally: + os.chdir(old_dir) + self.assertFalse(os.path.exists(tarball)) + self.assertEqual(len(w.warnings), 1) + + @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, + 'Need zip and zlib support to run') + def test_make_zipfile(self): + # creating something to tar + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') + with change_cwd(tmpdir): + make_zipfile(base_name, 'dist') + + # check if the compressed tarball was created + tarball = base_name + '.zip' + self.assertTrue(os.path.exists(tarball)) + with zipfile.ZipFile(tarball) as zf: + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) + + @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') + def test_make_zipfile_no_zlib(self): + patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError + + called = [] + zipfile_class = zipfile.ZipFile + def fake_zipfile(*a, **kw): + if kw.get('compression', None) == zipfile.ZIP_STORED: + called.append((a, kw)) + return zipfile_class(*a, **kw) + + patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile) + + # create something to tar and compress + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') + with change_cwd(tmpdir): + make_zipfile(base_name, 'dist') + + tarball = base_name + '.zip' + self.assertEqual(called, + [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) + self.assertTrue(os.path.exists(tarball)) + with zipfile.ZipFile(tarball) as zf: + self.assertEqual(sorted(zf.namelist()), self._zip_created_files) + + def test_check_archive_formats(self): + self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), + 'xxx') + self.assertIsNone(check_archive_formats(['gztar', 'bztar', 'xztar', + 'ztar', 'tar', 'zip'])) + + def test_make_archive(self): + tmpdir = self.mkdtemp() + base_name = os.path.join(tmpdir, 'archive') + self.assertRaises(ValueError, make_archive, base_name, 'xxx') + + def test_make_archive_cwd(self): + current_dir = os.getcwd() + def _breaks(*args, **kw): + raise RuntimeError() + ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file') + try: + try: + make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) + except: + pass + self.assertEqual(os.getcwd(), current_dir) + finally: + del ARCHIVE_FORMATS['xxx'] + + def test_make_archive_tar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'tar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar') + self.assertEqual(self._tarinfo(res), self._created_files) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_make_archive_gztar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'gztar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar.gz') + self.assertEqual(self._tarinfo(res), self._created_files) + + @unittest.skipUnless(bz2, 'Need bz2 support to run') + def test_make_archive_bztar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'bztar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar.bz2') + self.assertEqual(self._tarinfo(res), self._created_files) + + @unittest.skipUnless(lzma, 'Need xz support to run') + def test_make_archive_xztar(self): + base_dir = self._create_files() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'xztar', base_dir, 'dist') + self.assertTrue(os.path.exists(res)) + self.assertEqual(os.path.basename(res), 'archive.tar.xz') + self.assertEqual(self._tarinfo(res), self._created_files) + + def test_make_archive_owner_group(self): + # testing make_archive with owner and group, with various combinations + # this works even if there's not gid/uid support + if UID_GID_SUPPORT: + group = grp.getgrgid(0)[0] + owner = pwd.getpwuid(0)[0] + else: + group = owner = 'root' + + base_dir = self._create_files() + root_dir = self.mkdtemp() + base_name = os.path.join(self.mkdtemp() , 'archive') + res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, + group=group) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'zip', root_dir, base_dir) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'tar', root_dir, base_dir, + owner=owner, group=group) + self.assertTrue(os.path.exists(res)) + + res = make_archive(base_name, 'tar', root_dir, base_dir, + owner='kjhkjhkjg', group='oihohoh') + self.assertTrue(os.path.exists(res)) + + @unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib") + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + def test_tarfile_root_owner(self): + tmpdir = self._create_files() + base_name = os.path.join(self.mkdtemp(), 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + group = grp.getgrgid(0)[0] + owner = pwd.getpwuid(0)[0] + try: + archive_name = make_tarball(base_name, 'dist', compress=None, + owner=owner, group=group) + finally: + os.chdir(old_dir) + + # check if the compressed tarball was created + self.assertTrue(os.path.exists(archive_name)) + + # now checks the rights + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEqual(member.uid, 0) + self.assertEqual(member.gid, 0) + finally: + archive.close() + +def test_suite(): + return unittest.makeSuite(ArchiveUtilTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_bdist.py b/setuptools/_distutils/tests/test_bdist.py new file mode 100644 index 00000000..130d8bf1 --- /dev/null +++ b/setuptools/_distutils/tests/test_bdist.py @@ -0,0 +1,57 @@ +"""Tests for distutils.command.bdist.""" +import os +import unittest +from test.support import run_unittest +import warnings + +from distutils.command.bdist import bdist +from distutils.tests import support + + +class BuildTestCase(support.TempdirManager, + unittest.TestCase): + + def test_formats(self): + # let's create a command and make sure + # we can set the format + dist = self.create_dist()[1] + cmd = bdist(dist) + cmd.formats = ['msi'] + cmd.ensure_finalized() + self.assertEqual(cmd.formats, ['msi']) + + # what formats does bdist offer? + formats = ['bztar', 'gztar', 'msi', 'rpm', 'tar', + 'wininst', 'xztar', 'zip', 'ztar'] + found = sorted(cmd.format_command) + self.assertEqual(found, formats) + + def test_skip_build(self): + # bug #10946: bdist --skip-build should trickle down to subcommands + dist = self.create_dist()[1] + cmd = bdist(dist) + cmd.skip_build = 1 + cmd.ensure_finalized() + dist.command_obj['bdist'] = cmd + + names = ['bdist_dumb', 'bdist_wininst'] # bdist_rpm does not support --skip-build + if os.name == 'nt': + names.append('bdist_msi') + + for name in names: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', 'bdist_wininst command is deprecated', + DeprecationWarning) + subcmd = cmd.get_finalized_command(name) + if getattr(subcmd, '_unsupported', False): + # command is not supported on this build + continue + self.assertTrue(subcmd.skip_build, + '%s should take --skip-build from bdist' % name) + + +def test_suite(): + return unittest.makeSuite(BuildTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_bdist_dumb.py b/setuptools/_distutils/tests/test_bdist_dumb.py new file mode 100644 index 00000000..01a233bc --- /dev/null +++ b/setuptools/_distutils/tests/test_bdist_dumb.py @@ -0,0 +1,97 @@ +"""Tests for distutils.command.bdist_dumb.""" + +import os +import sys +import zipfile +import unittest +from test.support import run_unittest + +from distutils.core import Distribution +from distutils.command.bdist_dumb import bdist_dumb +from distutils.tests import support + +SETUP_PY = """\ +from distutils.core import setup +import foo + +setup(name='foo', version='0.1', py_modules=['foo'], + url='xxx', author='xxx', author_email='xxx') + +""" + +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + + +class BuildDumbTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + def setUp(self): + super(BuildDumbTestCase, self).setUp() + self.old_location = os.getcwd() + self.old_sys_argv = sys.argv, sys.argv[:] + + def tearDown(self): + os.chdir(self.old_location) + sys.argv = self.old_sys_argv[0] + sys.argv[:] = self.old_sys_argv[1] + super(BuildDumbTestCase, self).tearDown() + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_simple_built(self): + + # let's create a simple package + tmp_dir = self.mkdtemp() + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_dumb(dist) + + # so the output is the same no matter + # what is the platform + cmd.format = 'zip' + + cmd.ensure_finalized() + cmd.run() + + # see what we have + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name) + + self.assertEqual(dist_created, [base]) + + # now let's check what we have in the zip file + fp = zipfile.ZipFile(os.path.join('dist', base)) + try: + contents = fp.namelist() + finally: + fp.close() + + contents = sorted(filter(None, map(os.path.basename, contents))) + wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] + if not sys.dont_write_bytecode: + wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) + self.assertEqual(contents, sorted(wanted)) + +def test_suite(): + return unittest.makeSuite(BuildDumbTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_bdist_msi.py b/setuptools/_distutils/tests/test_bdist_msi.py new file mode 100644 index 00000000..418e60ec --- /dev/null +++ b/setuptools/_distutils/tests/test_bdist_msi.py @@ -0,0 +1,26 @@ +"""Tests for distutils.command.bdist_msi.""" +import sys +import unittest +from test.support import run_unittest, check_warnings +from distutils.tests import support + + +@unittest.skipUnless(sys.platform == 'win32', 'these tests require Windows') +class BDistMSITestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_minimal(self): + # minimal test XXX need more tests + from distutils.command.bdist_msi import bdist_msi + project_dir, dist = self.create_dist() + with check_warnings(("", DeprecationWarning)): + cmd = bdist_msi(dist) + cmd.ensure_finalized() + + +def test_suite(): + return unittest.makeSuite(BDistMSITestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_bdist_rpm.py b/setuptools/_distutils/tests/test_bdist_rpm.py new file mode 100644 index 00000000..6453a02b --- /dev/null +++ b/setuptools/_distutils/tests/test_bdist_rpm.py @@ -0,0 +1,135 @@ +"""Tests for distutils.command.bdist_rpm.""" + +import unittest +import sys +import os +from test.support import run_unittest, requires_zlib + +from distutils.core import Distribution +from distutils.command.bdist_rpm import bdist_rpm +from distutils.tests import support +from distutils.spawn import find_executable + +SETUP_PY = """\ +from distutils.core import setup +import foo + +setup(name='foo', version='0.1', py_modules=['foo'], + url='xxx', author='xxx', author_email='xxx') + +""" + +class BuildRpmTestCase(support.TempdirManager, + support.EnvironGuard, + support.LoggingSilencer, + unittest.TestCase): + + def setUp(self): + try: + sys.executable.encode("UTF-8") + except UnicodeEncodeError: + raise unittest.SkipTest("sys.executable is not encodable to UTF-8") + + super(BuildRpmTestCase, self).setUp() + self.old_location = os.getcwd() + self.old_sys_argv = sys.argv, sys.argv[:] + + def tearDown(self): + os.chdir(self.old_location) + sys.argv = self.old_sys_argv[0] + sys.argv[:] = self.old_sys_argv[1] + super(BuildRpmTestCase, self).tearDown() + + # XXX I am unable yet to make this test work without + # spurious sdtout/stderr output under Mac OS X + @unittest.skipUnless(sys.platform.startswith('linux'), + 'spurious sdtout/stderr output under Mac OS X') + @requires_zlib + @unittest.skipIf(find_executable('rpm') is None, + 'the rpm command is not found') + @unittest.skipIf(find_executable('rpmbuild') is None, + 'the rpmbuild command is not found') + def test_quiet(self): + # let's create a package + tmp_dir = self.mkdtemp() + os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_rpm(dist) + cmd.fix_python = True + + # running in quiet mode + cmd.quiet = 1 + cmd.ensure_finalized() + cmd.run() + + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + self.assertIn('foo-0.1-1.noarch.rpm', dist_created) + + # bug #2945: upload ignores bdist_rpm files + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) + + # XXX I am unable yet to make this test work without + # spurious sdtout/stderr output under Mac OS X + @unittest.skipUnless(sys.platform.startswith('linux'), + 'spurious sdtout/stderr output under Mac OS X') + @requires_zlib + # http://bugs.python.org/issue1533164 + @unittest.skipIf(find_executable('rpm') is None, + 'the rpm command is not found') + @unittest.skipIf(find_executable('rpmbuild') is None, + 'the rpmbuild command is not found') + def test_no_optimize_flag(self): + # let's create a package that breaks bdist_rpm + tmp_dir = self.mkdtemp() + os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation + pkg_dir = os.path.join(tmp_dir, 'foo') + os.mkdir(pkg_dir) + self.write_file((pkg_dir, 'setup.py'), SETUP_PY) + self.write_file((pkg_dir, 'foo.py'), '#') + self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') + self.write_file((pkg_dir, 'README'), '') + + dist = Distribution({'name': 'foo', 'version': '0.1', + 'py_modules': ['foo'], + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'}) + dist.script_name = 'setup.py' + os.chdir(pkg_dir) + + sys.argv = ['setup.py'] + cmd = bdist_rpm(dist) + cmd.fix_python = True + + cmd.quiet = 1 + cmd.ensure_finalized() + cmd.run() + + dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) + self.assertIn('foo-0.1-1.noarch.rpm', dist_created) + + # bug #2945: upload ignores bdist_rpm files + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) + self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) + + os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) + +def test_suite(): + return unittest.makeSuite(BuildRpmTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_bdist_wininst.py b/setuptools/_distutils/tests/test_bdist_wininst.py new file mode 100644 index 00000000..5c3d025d --- /dev/null +++ b/setuptools/_distutils/tests/test_bdist_wininst.py @@ -0,0 +1,38 @@ +"""Tests for distutils.command.bdist_wininst.""" +import sys +import platform +import unittest +from test.support import run_unittest, check_warnings + +from distutils.command.bdist_wininst import bdist_wininst +from distutils.tests import support + +@unittest.skipIf(sys.platform == 'win32' and platform.machine() == 'ARM64', + 'bdist_wininst is not supported in this install') +@unittest.skipIf(getattr(bdist_wininst, '_unsupported', False), + 'bdist_wininst is not supported in this install') +class BuildWinInstTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_get_exe_bytes(self): + + # issue5731: command was broken on non-windows platforms + # this test makes sure it works now for every platform + # let's create a command + pkg_pth, dist = self.create_dist() + with check_warnings(("", DeprecationWarning)): + cmd = bdist_wininst(dist) + cmd.ensure_finalized() + + # let's run the code that finds the right wininst*.exe file + # and make sure it finds it and returns its content + # no matter what platform we have + exe_file = cmd.get_exe_bytes() + self.assertGreater(len(exe_file), 10) + +def test_suite(): + return unittest.makeSuite(BuildWinInstTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_build.py b/setuptools/_distutils/tests/test_build.py new file mode 100644 index 00000000..b020a5ba --- /dev/null +++ b/setuptools/_distutils/tests/test_build.py @@ -0,0 +1,56 @@ +"""Tests for distutils.command.build.""" +import unittest +import os +import sys +from test.support import run_unittest + +from distutils.command.build import build +from distutils.tests import support +from sysconfig import get_platform + +class BuildTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + cmd = build(dist) + cmd.finalize_options() + + # if not specified, plat_name gets the current platform + self.assertEqual(cmd.plat_name, get_platform()) + + # build_purelib is build + lib + wanted = os.path.join(cmd.build_base, 'lib') + self.assertEqual(cmd.build_purelib, wanted) + + # build_platlib is 'build/lib.platform-x.x[-pydebug]' + # examples: + # build/lib.macosx-10.3-i386-2.7 + plat_spec = '.%s-%d.%d' % (cmd.plat_name, *sys.version_info[:2]) + if hasattr(sys, 'gettotalrefcount'): + self.assertTrue(cmd.build_platlib.endswith('-pydebug')) + plat_spec += '-pydebug' + wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) + self.assertEqual(cmd.build_platlib, wanted) + + # by default, build_lib = build_purelib + self.assertEqual(cmd.build_lib, cmd.build_purelib) + + # build_temp is build/temp. + wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) + self.assertEqual(cmd.build_temp, wanted) + + # build_scripts is build/scripts-x.x + wanted = os.path.join(cmd.build_base, + 'scripts-%d.%d' % sys.version_info[:2]) + self.assertEqual(cmd.build_scripts, wanted) + + # executable is os.path.normpath(sys.executable) + self.assertEqual(cmd.executable, os.path.normpath(sys.executable)) + +def test_suite(): + return unittest.makeSuite(BuildTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_build_clib.py b/setuptools/_distutils/tests/test_build_clib.py new file mode 100644 index 00000000..259c4352 --- /dev/null +++ b/setuptools/_distutils/tests/test_build_clib.py @@ -0,0 +1,136 @@ +"""Tests for distutils.command.build_clib.""" +import unittest +import os +import sys + +from test.support import run_unittest + +from .py35compat import missing_compiler_executable + +from distutils.command.build_clib import build_clib +from distutils.errors import DistutilsSetupError +from distutils.tests import support + +class BuildCLibTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_check_library_dist(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + # 'libraries' option must be a list + self.assertRaises(DistutilsSetupError, cmd.check_library_list, 'foo') + + # each element of 'libraries' must a 2-tuple + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + ['foo1', 'foo2']) + + # first element of each tuple in 'libraries' + # must be a string (the library name) + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [(1, 'foo1'), ('name', 'foo2')]) + + # library name may not contain directory separators + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [('name', 'foo1'), + ('another/name', 'foo2')]) + + # second element of each tuple must be a dictionary (build info) + self.assertRaises(DistutilsSetupError, cmd.check_library_list, + [('name', {}), + ('another', 'foo2')]) + + # those work + libs = [('name', {}), ('name', {'ok': 'good'})] + cmd.check_library_list(libs) + + def test_get_source_files(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + # "in 'libraries' option 'sources' must be present and must be + # a list of source filenames + cmd.libraries = [('name', {})] + self.assertRaises(DistutilsSetupError, cmd.get_source_files) + + cmd.libraries = [('name', {'sources': 1})] + self.assertRaises(DistutilsSetupError, cmd.get_source_files) + + cmd.libraries = [('name', {'sources': ['a', 'b']})] + self.assertEqual(cmd.get_source_files(), ['a', 'b']) + + cmd.libraries = [('name', {'sources': ('a', 'b')})] + self.assertEqual(cmd.get_source_files(), ['a', 'b']) + + cmd.libraries = [('name', {'sources': ('a', 'b')}), + ('name2', {'sources': ['c', 'd']})] + self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd']) + + def test_build_libraries(self): + + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + class FakeCompiler: + def compile(*args, **kw): + pass + create_static_lib = compile + + cmd.compiler = FakeCompiler() + + # build_libraries is also doing a bit of typo checking + lib = [('name', {'sources': 'notvalid'})] + self.assertRaises(DistutilsSetupError, cmd.build_libraries, lib) + + lib = [('name', {'sources': list()})] + cmd.build_libraries(lib) + + lib = [('name', {'sources': tuple()})] + cmd.build_libraries(lib) + + def test_finalize_options(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + cmd.include_dirs = 'one-dir' + cmd.finalize_options() + self.assertEqual(cmd.include_dirs, ['one-dir']) + + cmd.include_dirs = None + cmd.finalize_options() + self.assertEqual(cmd.include_dirs, []) + + cmd.distribution.libraries = 'WONTWORK' + self.assertRaises(DistutilsSetupError, cmd.finalize_options) + + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") + def test_run(self): + pkg_dir, dist = self.create_dist() + cmd = build_clib(dist) + + foo_c = os.path.join(pkg_dir, 'foo.c') + self.write_file(foo_c, 'int main(void) { return 1;}\n') + cmd.libraries = [('foo', {'sources': [foo_c]})] + + build_temp = os.path.join(pkg_dir, 'build') + os.mkdir(build_temp) + cmd.build_temp = build_temp + cmd.build_clib = build_temp + + # Before we run the command, we want to make sure + # all commands are present on the system. + ccmd = missing_compiler_executable() + if ccmd is not None: + self.skipTest('The %r command is not found' % ccmd) + + # this should work + cmd.run() + + # let's check the result + self.assertIn('libfoo.a', os.listdir(build_temp)) + +def test_suite(): + return unittest.makeSuite(BuildCLibTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_build_ext.py b/setuptools/_distutils/tests/test_build_ext.py new file mode 100644 index 00000000..1aec1537 --- /dev/null +++ b/setuptools/_distutils/tests/test_build_ext.py @@ -0,0 +1,545 @@ +import sys +import os +from io import StringIO +import textwrap + +from distutils.core import Distribution +from distutils.command.build_ext import build_ext +from distutils import sysconfig +from distutils.tests.support import (TempdirManager, LoggingSilencer, + copy_xxmodule_c, fixup_build_ext) +from distutils.extension import Extension +from distutils.errors import ( + CompileError, DistutilsPlatformError, DistutilsSetupError, + UnknownFileError) + +import unittest +from test import support +from test.support.script_helper import assert_python_ok + +# http://bugs.python.org/issue4373 +# Don't load the xx module more than once. +ALREADY_TESTED = False + + +class BuildExtTestCase(TempdirManager, + LoggingSilencer, + unittest.TestCase): + def setUp(self): + # Create a simple test environment + super(BuildExtTestCase, self).setUp() + self.tmp_dir = self.mkdtemp() + import site + self.old_user_base = site.USER_BASE + site.USER_BASE = self.mkdtemp() + from distutils.command import build_ext + build_ext.USER_BASE = site.USER_BASE + + # bpo-30132: On Windows, a .pdb file may be created in the current + # working directory. Create a temporary working directory to cleanup + # everything at the end of the test. + change_cwd = support.change_cwd(self.tmp_dir) + change_cwd.__enter__() + self.addCleanup(change_cwd.__exit__, None, None, None) + + def tearDown(self): + import site + site.USER_BASE = self.old_user_base + from distutils.command import build_ext + build_ext.USER_BASE = self.old_user_base + super(BuildExtTestCase, self).tearDown() + + def build_ext(self, *args, **kwargs): + return build_ext(*args, **kwargs) + + def test_build_ext(self): + cmd = support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) + global ALREADY_TESTED + copy_xxmodule_c(self.tmp_dir) + xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') + xx_ext = Extension('xx', [xx_c]) + dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) + dist.package_dir = self.tmp_dir + cmd = self.build_ext(dist) + fixup_build_ext(cmd) + cmd.build_lib = self.tmp_dir + cmd.build_temp = self.tmp_dir + + old_stdout = sys.stdout + if not support.verbose: + # silence compiler output + sys.stdout = StringIO() + try: + cmd.ensure_finalized() + cmd.run() + finally: + sys.stdout = old_stdout + + if ALREADY_TESTED: + self.skipTest('Already tested in %s' % ALREADY_TESTED) + else: + ALREADY_TESTED = type(self).__name__ + + code = textwrap.dedent(""" + tmp_dir = {self.tmp_dir!r} + + import sys + import unittest + from test import support + + sys.path.insert(0, tmp_dir) + import xx + + class Tests(unittest.TestCase): + def test_xx(self): + for attr in ('error', 'foo', 'new', 'roj'): + self.assertTrue(hasattr(xx, attr)) + + self.assertEqual(xx.foo(2, 5), 7) + self.assertEqual(xx.foo(13,15), 28) + self.assertEqual(xx.new().demo(), None) + if support.HAVE_DOCSTRINGS: + doc = 'This is a template module just for instruction.' + self.assertEqual(xx.__doc__, doc) + self.assertIsInstance(xx.Null(), xx.Null) + self.assertIsInstance(xx.Str(), xx.Str) + + + unittest.main() + """.format(**locals())) + assert_python_ok('-c', code) + + def test_solaris_enable_shared(self): + dist = Distribution({'name': 'xx'}) + cmd = self.build_ext(dist) + old = sys.platform + + sys.platform = 'sunos' # fooling finalize_options + from distutils.sysconfig import _config_vars + old_var = _config_vars.get('Py_ENABLE_SHARED') + _config_vars['Py_ENABLE_SHARED'] = 1 + try: + cmd.ensure_finalized() + finally: + sys.platform = old + if old_var is None: + del _config_vars['Py_ENABLE_SHARED'] + else: + _config_vars['Py_ENABLE_SHARED'] = old_var + + # make sure we get some library dirs under solaris + self.assertGreater(len(cmd.library_dirs), 0) + + def test_user_site(self): + import site + dist = Distribution({'name': 'xx'}) + cmd = self.build_ext(dist) + + # making sure the user option is there + options = [name for name, short, lable in + cmd.user_options] + self.assertIn('user', options) + + # setting a value + cmd.user = 1 + + # setting user based lib and include + lib = os.path.join(site.USER_BASE, 'lib') + incl = os.path.join(site.USER_BASE, 'include') + os.mkdir(lib) + os.mkdir(incl) + + # let's run finalize + cmd.ensure_finalized() + + # see if include_dirs and library_dirs + # were set + self.assertIn(lib, cmd.library_dirs) + self.assertIn(lib, cmd.rpath) + self.assertIn(incl, cmd.include_dirs) + + def test_optional_extension(self): + + # this extension will fail, but let's ignore this failure + # with the optional argument. + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.ensure_finalized() + self.assertRaises((UnknownFileError, CompileError), + cmd.run) # should raise an error + + modules = [Extension('foo', ['xxx'], optional=True)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.ensure_finalized() + cmd.run() # should pass + + def test_finalize_options(self): + # Make sure Python's include directories (for Python.h, pyconfig.h, + # etc.) are in the include search path. + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.finalize_options() + + py_include = sysconfig.get_python_inc() + for p in py_include.split(os.path.pathsep): + self.assertIn(p, cmd.include_dirs) + + plat_py_include = sysconfig.get_python_inc(plat_specific=1) + for p in plat_py_include.split(os.path.pathsep): + self.assertIn(p, cmd.include_dirs) + + # make sure cmd.libraries is turned into a list + # if it's a string + cmd = self.build_ext(dist) + cmd.libraries = 'my_lib, other_lib lastlib' + cmd.finalize_options() + self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib']) + + # make sure cmd.library_dirs is turned into a list + # if it's a string + cmd = self.build_ext(dist) + cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep + cmd.finalize_options() + self.assertIn('my_lib_dir', cmd.library_dirs) + self.assertIn('other_lib_dir', cmd.library_dirs) + + # make sure rpath is turned into a list + # if it's a string + cmd = self.build_ext(dist) + cmd.rpath = 'one%stwo' % os.pathsep + cmd.finalize_options() + self.assertEqual(cmd.rpath, ['one', 'two']) + + # make sure cmd.link_objects is turned into a list + # if it's a string + cmd = build_ext(dist) + cmd.link_objects = 'one two,three' + cmd.finalize_options() + self.assertEqual(cmd.link_objects, ['one', 'two', 'three']) + + # XXX more tests to perform for win32 + + # make sure define is turned into 2-tuples + # strings if they are ','-separated strings + cmd = self.build_ext(dist) + cmd.define = 'one,two' + cmd.finalize_options() + self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) + + # make sure undef is turned into a list of + # strings if they are ','-separated strings + cmd = self.build_ext(dist) + cmd.undef = 'one,two' + cmd.finalize_options() + self.assertEqual(cmd.undef, ['one', 'two']) + + # make sure swig_opts is turned into a list + cmd = self.build_ext(dist) + cmd.swig_opts = None + cmd.finalize_options() + self.assertEqual(cmd.swig_opts, []) + + cmd = self.build_ext(dist) + cmd.swig_opts = '1 2' + cmd.finalize_options() + self.assertEqual(cmd.swig_opts, ['1', '2']) + + def test_check_extensions_list(self): + dist = Distribution() + cmd = self.build_ext(dist) + cmd.finalize_options() + + #'extensions' option must be a list of Extension instances + self.assertRaises(DistutilsSetupError, + cmd.check_extensions_list, 'foo') + + # each element of 'ext_modules' option must be an + # Extension instance or 2-tuple + exts = [('bar', 'foo', 'bar'), 'foo'] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # first element of each tuple in 'ext_modules' + # must be the extension name (a string) and match + # a python dotted-separated name + exts = [('foo-bar', '')] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # second element of each tuple in 'ext_modules' + # must be a dictionary (build info) + exts = [('foo.bar', '')] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + # ok this one should pass + exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', + 'some': 'bar'})] + cmd.check_extensions_list(exts) + ext = exts[0] + self.assertIsInstance(ext, Extension) + + # check_extensions_list adds in ext the values passed + # when they are in ('include_dirs', 'library_dirs', 'libraries' + # 'extra_objects', 'extra_compile_args', 'extra_link_args') + self.assertEqual(ext.libraries, 'foo') + self.assertFalse(hasattr(ext, 'some')) + + # 'macros' element of build info dict must be 1- or 2-tuple + exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', + 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})] + self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) + + exts[0][1]['macros'] = [('1', '2'), ('3',)] + cmd.check_extensions_list(exts) + self.assertEqual(exts[0].undef_macros, ['3']) + self.assertEqual(exts[0].define_macros, [('1', '2')]) + + def test_get_source_files(self): + modules = [Extension('foo', ['xxx'], optional=False)] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.ensure_finalized() + self.assertEqual(cmd.get_source_files(), ['xxx']) + + def test_unicode_module_names(self): + modules = [ + Extension('foo', ['aaa'], optional=False), + Extension('föö', ['uuu'], optional=False), + ] + dist = Distribution({'name': 'xx', 'ext_modules': modules}) + cmd = self.build_ext(dist) + cmd.ensure_finalized() + self.assertRegex(cmd.get_ext_filename(modules[0].name), r'foo(_d)?\..*') + self.assertRegex(cmd.get_ext_filename(modules[1].name), r'föö(_d)?\..*') + self.assertEqual(cmd.get_export_symbols(modules[0]), ['PyInit_foo']) + self.assertEqual(cmd.get_export_symbols(modules[1]), ['PyInitU_f_gkaa']) + + def test_compiler_option(self): + # cmd.compiler is an option and + # should not be overridden by a compiler instance + # when the command is run + dist = Distribution() + cmd = self.build_ext(dist) + cmd.compiler = 'unix' + cmd.ensure_finalized() + cmd.run() + self.assertEqual(cmd.compiler, 'unix') + + def test_get_outputs(self): + cmd = support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) + tmp_dir = self.mkdtemp() + c_file = os.path.join(tmp_dir, 'foo.c') + self.write_file(c_file, 'void PyInit_foo(void) {}\n') + ext = Extension('foo', [c_file], optional=False) + dist = Distribution({'name': 'xx', + 'ext_modules': [ext]}) + cmd = self.build_ext(dist) + fixup_build_ext(cmd) + cmd.ensure_finalized() + self.assertEqual(len(cmd.get_outputs()), 1) + + cmd.build_lib = os.path.join(self.tmp_dir, 'build') + cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') + + # issue #5977 : distutils build_ext.get_outputs + # returns wrong result with --inplace + other_tmp_dir = os.path.realpath(self.mkdtemp()) + old_wd = os.getcwd() + os.chdir(other_tmp_dir) + try: + cmd.inplace = 1 + cmd.run() + so_file = cmd.get_outputs()[0] + finally: + os.chdir(old_wd) + self.assertTrue(os.path.exists(so_file)) + ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') + self.assertTrue(so_file.endswith(ext_suffix)) + so_dir = os.path.dirname(so_file) + self.assertEqual(so_dir, other_tmp_dir) + + cmd.inplace = 0 + cmd.compiler = None + cmd.run() + so_file = cmd.get_outputs()[0] + self.assertTrue(os.path.exists(so_file)) + self.assertTrue(so_file.endswith(ext_suffix)) + so_dir = os.path.dirname(so_file) + self.assertEqual(so_dir, cmd.build_lib) + + # inplace = 0, cmd.package = 'bar' + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {'': 'bar'} + path = cmd.get_ext_fullpath('foo') + # checking that the last directory is the build_dir + path = os.path.split(path)[0] + self.assertEqual(path, cmd.build_lib) + + # inplace = 1, cmd.package = 'bar' + cmd.inplace = 1 + other_tmp_dir = os.path.realpath(self.mkdtemp()) + old_wd = os.getcwd() + os.chdir(other_tmp_dir) + try: + path = cmd.get_ext_fullpath('foo') + finally: + os.chdir(old_wd) + # checking that the last directory is bar + path = os.path.split(path)[0] + lastdir = os.path.split(path)[-1] + self.assertEqual(lastdir, 'bar') + + def test_ext_fullpath(self): + ext = sysconfig.get_config_var('EXT_SUFFIX') + # building lxml.etree inplace + #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') + #etree_ext = Extension('lxml.etree', [etree_c]) + #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) + dist = Distribution() + cmd = self.build_ext(dist) + cmd.inplace = 1 + cmd.distribution.package_dir = {'': 'src'} + cmd.distribution.packages = ['lxml', 'lxml.html'] + curdir = os.getcwd() + wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEqual(wanted, path) + + # building lxml.etree not inplace + cmd.inplace = 0 + cmd.build_lib = os.path.join(curdir, 'tmpdir') + wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) + path = cmd.get_ext_fullpath('lxml.etree') + self.assertEqual(wanted, path) + + # building twisted.runner.portmap not inplace + build_py = cmd.get_finalized_command('build_py') + build_py.package_dir = {} + cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', + 'portmap' + ext) + self.assertEqual(wanted, path) + + # building twisted.runner.portmap inplace + cmd.inplace = 1 + path = cmd.get_ext_fullpath('twisted.runner.portmap') + wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) + self.assertEqual(wanted, path) + + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target_default(self): + # Issue 9516: Test that, in the absence of the environment variable, + # an extension module is compiled with the same deployment target as + # the interpreter. + self._try_compile_deployment_target('==', None) + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target_too_low(self): + # Issue 9516: Test that an extension module is not allowed to be + # compiled with a deployment target less than that of the interpreter. + self.assertRaises(DistutilsPlatformError, + self._try_compile_deployment_target, '>', '10.1') + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') + def test_deployment_target_higher_ok(self): + # Issue 9516: Test that an extension module can be compiled with a + # deployment target higher than that of the interpreter: the ext + # module may depend on some newer OS feature. + deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') + if deptarget: + # increment the minor version number (i.e. 10.6 -> 10.7) + deptarget = [int(x) for x in deptarget.split('.')] + deptarget[-1] += 1 + deptarget = '.'.join(str(i) for i in deptarget) + self._try_compile_deployment_target('<', deptarget) + + def _try_compile_deployment_target(self, operator, target): + orig_environ = os.environ + os.environ = orig_environ.copy() + self.addCleanup(setattr, os, 'environ', orig_environ) + + if target is None: + if os.environ.get('MACOSX_DEPLOYMENT_TARGET'): + del os.environ['MACOSX_DEPLOYMENT_TARGET'] + else: + os.environ['MACOSX_DEPLOYMENT_TARGET'] = target + + deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') + + with open(deptarget_c, 'w') as fp: + fp.write(textwrap.dedent('''\ + #include + + int dummy; + + #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED + #else + #error "Unexpected target" + #endif + + ''' % operator)) + + # get the deployment target that the interpreter was built with + target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') + target = tuple(map(int, target.split('.')[0:2])) + # format the target value as defined in the Apple + # Availability Macros. We can't use the macro names since + # at least one value we test with will not exist yet. + if target[1] < 10: + # for 10.1 through 10.9.x -> "10n0" + target = '%02d%01d0' % target + else: + # for 10.10 and beyond -> "10nn00" + target = '%02d%02d00' % target + deptarget_ext = Extension( + 'deptarget', + [deptarget_c], + extra_compile_args=['-DTARGET=%s'%(target,)], + ) + dist = Distribution({ + 'name': 'deptarget', + 'ext_modules': [deptarget_ext] + }) + dist.package_dir = self.tmp_dir + cmd = self.build_ext(dist) + cmd.build_lib = self.tmp_dir + cmd.build_temp = self.tmp_dir + + try: + old_stdout = sys.stdout + if not support.verbose: + # silence compiler output + sys.stdout = StringIO() + try: + cmd.ensure_finalized() + cmd.run() + finally: + sys.stdout = old_stdout + + except CompileError: + self.fail("Wrong deployment target during compilation") + + +class ParallelBuildExtTestCase(BuildExtTestCase): + + def build_ext(self, *args, **kwargs): + build_ext = super().build_ext(*args, **kwargs) + build_ext.parallel = True + return build_ext + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(BuildExtTestCase)) + suite.addTest(unittest.makeSuite(ParallelBuildExtTestCase)) + return suite + +if __name__ == '__main__': + support.run_unittest(__name__) diff --git a/setuptools/_distutils/tests/test_build_py.py b/setuptools/_distutils/tests/test_build_py.py new file mode 100644 index 00000000..0712e92c --- /dev/null +++ b/setuptools/_distutils/tests/test_build_py.py @@ -0,0 +1,179 @@ +"""Tests for distutils.command.build_py.""" + +import os +import sys +import unittest + +from distutils.command.build_py import build_py +from distutils.core import Distribution +from distutils.errors import DistutilsFileError + +from distutils.tests import support +from test.support import run_unittest + + +class BuildPyTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_package_data(self): + sources = self.mkdtemp() + f = open(os.path.join(sources, "__init__.py"), "w") + try: + f.write("# Pretend this is a package.") + finally: + f.close() + f = open(os.path.join(sources, "README.txt"), "w") + try: + f.write("Info about this package") + finally: + f.close() + + destination = self.mkdtemp() + + dist = Distribution({"packages": ["pkg"], + "package_dir": {"pkg": sources}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.command_obj["build"] = support.DummyCommand( + force=0, + build_lib=destination) + dist.packages = ["pkg"] + dist.package_data = {"pkg": ["README.txt"]} + dist.package_dir = {"pkg": sources} + + cmd = build_py(dist) + cmd.compile = 1 + cmd.ensure_finalized() + self.assertEqual(cmd.package_data, dist.package_data) + + cmd.run() + + # This makes sure the list of outputs includes byte-compiled + # files for Python modules but not for package data files + # (there shouldn't *be* byte-code files for those!). + self.assertEqual(len(cmd.get_outputs()), 3) + pkgdest = os.path.join(destination, "pkg") + files = os.listdir(pkgdest) + pycache_dir = os.path.join(pkgdest, "__pycache__") + self.assertIn("__init__.py", files) + self.assertIn("README.txt", files) + if sys.dont_write_bytecode: + self.assertFalse(os.path.exists(pycache_dir)) + else: + pyc_files = os.listdir(pycache_dir) + self.assertIn("__init__.%s.pyc" % sys.implementation.cache_tag, + pyc_files) + + def test_empty_package_dir(self): + # See bugs #1668596/#1720897 + sources = self.mkdtemp() + open(os.path.join(sources, "__init__.py"), "w").close() + + testdir = os.path.join(sources, "doc") + os.mkdir(testdir) + open(os.path.join(testdir, "testfile"), "w").close() + + os.chdir(sources) + dist = Distribution({"packages": ["pkg"], + "package_dir": {"pkg": ""}, + "package_data": {"pkg": ["doc/*"]}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.script_args = ["build"] + dist.parse_command_line() + + try: + dist.run_commands() + except DistutilsFileError: + self.fail("failed package_data test when package_dir is ''") + + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') + def test_byte_compile(self): + project_dir, dist = self.create_dist(py_modules=['boiledeggs']) + os.chdir(project_dir) + self.write_file('boiledeggs.py', 'import antigravity') + cmd = build_py(dist) + cmd.compile = 1 + cmd.build_lib = 'here' + cmd.finalize_options() + cmd.run() + + found = os.listdir(cmd.build_lib) + self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) + found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) + self.assertEqual(found, + ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]) + + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') + def test_byte_compile_optimized(self): + project_dir, dist = self.create_dist(py_modules=['boiledeggs']) + os.chdir(project_dir) + self.write_file('boiledeggs.py', 'import antigravity') + cmd = build_py(dist) + cmd.compile = 0 + cmd.optimize = 1 + cmd.build_lib = 'here' + cmd.finalize_options() + cmd.run() + + found = os.listdir(cmd.build_lib) + self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) + found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) + expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag) + self.assertEqual(sorted(found), [expect]) + + def test_dir_in_package_data(self): + """ + A directory in package_data should not be added to the filelist. + """ + # See bug 19286 + sources = self.mkdtemp() + pkg_dir = os.path.join(sources, "pkg") + + os.mkdir(pkg_dir) + open(os.path.join(pkg_dir, "__init__.py"), "w").close() + + docdir = os.path.join(pkg_dir, "doc") + os.mkdir(docdir) + open(os.path.join(docdir, "testfile"), "w").close() + + # create the directory that could be incorrectly detected as a file + os.mkdir(os.path.join(docdir, 'otherdir')) + + os.chdir(sources) + dist = Distribution({"packages": ["pkg"], + "package_data": {"pkg": ["doc/*"]}}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(sources, "setup.py") + dist.script_args = ["build"] + dist.parse_command_line() + + try: + dist.run_commands() + except DistutilsFileError: + self.fail("failed package_data when data dir includes a dir") + + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + dist = self.create_dist()[1] + cmd = build_py(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertIn('byte-compiling is disabled', + self.logs[0][1] % self.logs[0][2]) + + +def test_suite(): + return unittest.makeSuite(BuildPyTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_build_scripts.py b/setuptools/_distutils/tests/test_build_scripts.py new file mode 100644 index 00000000..954fc763 --- /dev/null +++ b/setuptools/_distutils/tests/test_build_scripts.py @@ -0,0 +1,112 @@ +"""Tests for distutils.command.build_scripts.""" + +import os +import unittest + +from distutils.command.build_scripts import build_scripts +from distutils.core import Distribution +from distutils import sysconfig + +from distutils.tests import support +from test.support import run_unittest + + +class BuildScriptsTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_default_settings(self): + cmd = self.get_build_scripts_cmd("/foo/bar", []) + self.assertFalse(cmd.force) + self.assertIsNone(cmd.build_dir) + + cmd.finalize_options() + + self.assertTrue(cmd.force) + self.assertEqual(cmd.build_dir, "/foo/bar") + + def test_build(self): + source = self.mkdtemp() + target = self.mkdtemp() + expected = self.write_sample_scripts(source) + + cmd = self.get_build_scripts_cmd(target, + [os.path.join(source, fn) + for fn in expected]) + cmd.finalize_options() + cmd.run() + + built = os.listdir(target) + for name in expected: + self.assertIn(name, built) + + def get_build_scripts_cmd(self, target, scripts): + import sys + dist = Distribution() + dist.scripts = scripts + dist.command_obj["build"] = support.DummyCommand( + build_scripts=target, + force=1, + executable=sys.executable + ) + return build_scripts(dist) + + def write_sample_scripts(self, dir): + expected = [] + expected.append("script1.py") + self.write_script(dir, "script1.py", + ("#! /usr/bin/env python2.3\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + expected.append("script2.py") + self.write_script(dir, "script2.py", + ("#!/usr/bin/python\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + expected.append("shell.sh") + self.write_script(dir, "shell.sh", + ("#!/bin/sh\n" + "# bogus shell script w/ sh-bang\n" + "exit 0\n")) + return expected + + def write_script(self, dir, name, text): + f = open(os.path.join(dir, name), "w") + try: + f.write(text) + finally: + f.close() + + def test_version_int(self): + source = self.mkdtemp() + target = self.mkdtemp() + expected = self.write_sample_scripts(source) + + + cmd = self.get_build_scripts_cmd(target, + [os.path.join(source, fn) + for fn in expected]) + cmd.finalize_options() + + # http://bugs.python.org/issue4524 + # + # On linux-g++-32 with command line `./configure --enable-ipv6 + # --with-suffix=3`, python is compiled okay but the build scripts + # failed when writing the name of the executable + old = sysconfig.get_config_vars().get('VERSION') + sysconfig._config_vars['VERSION'] = 4 + try: + cmd.run() + finally: + if old is not None: + sysconfig._config_vars['VERSION'] = old + + built = os.listdir(target) + for name in expected: + self.assertIn(name, built) + +def test_suite(): + return unittest.makeSuite(BuildScriptsTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_check.py b/setuptools/_distutils/tests/test_check.py new file mode 100644 index 00000000..e534aca1 --- /dev/null +++ b/setuptools/_distutils/tests/test_check.py @@ -0,0 +1,163 @@ +"""Tests for distutils.command.check.""" +import os +import textwrap +import unittest +from test.support import run_unittest + +from distutils.command.check import check, HAS_DOCUTILS +from distutils.tests import support +from distutils.errors import DistutilsSetupError + +try: + import pygments +except ImportError: + pygments = None + + +HERE = os.path.dirname(__file__) + + +class CheckTestCase(support.LoggingSilencer, + support.TempdirManager, + unittest.TestCase): + + def _run(self, metadata=None, cwd=None, **options): + if metadata is None: + metadata = {} + if cwd is not None: + old_dir = os.getcwd() + os.chdir(cwd) + pkg_info, dist = self.create_dist(**metadata) + cmd = check(dist) + cmd.initialize_options() + for name, value in options.items(): + setattr(cmd, name, value) + cmd.ensure_finalized() + cmd.run() + if cwd is not None: + os.chdir(old_dir) + return cmd + + def test_check_metadata(self): + # let's run the command with no metadata at all + # by default, check is checking the metadata + # should have some warnings + cmd = self._run() + self.assertEqual(cmd._warnings, 2) + + # now let's add the required fields + # and run it again, to make sure we don't get + # any warning anymore + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx'} + cmd = self._run(metadata) + self.assertEqual(cmd._warnings, 0) + + # now with the strict mode, we should + # get an error if there are missing metadata + self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1}) + + # and of course, no error when all metadata are present + cmd = self._run(metadata, strict=1) + self.assertEqual(cmd._warnings, 0) + + # now a test with non-ASCII characters + metadata = {'url': 'xxx', 'author': '\u00c9ric', + 'author_email': 'xxx', 'name': 'xxx', + 'version': 'xxx', + 'description': 'Something about esszet \u00df', + 'long_description': 'More things about esszet \u00df'} + cmd = self._run(metadata) + self.assertEqual(cmd._warnings, 0) + + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") + def test_check_document(self): + pkg_info, dist = self.create_dist() + cmd = check(dist) + + # let's see if it detects broken rest + broken_rest = 'title\n===\n\ntest' + msgs = cmd._check_rst_data(broken_rest) + self.assertEqual(len(msgs), 1) + + # and non-broken rest + rest = 'title\n=====\n\ntest' + msgs = cmd._check_rst_data(rest) + self.assertEqual(len(msgs), 0) + + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") + def test_check_restructuredtext(self): + # let's see if it detects broken rest in long_description + broken_rest = 'title\n===\n\ntest' + pkg_info, dist = self.create_dist(long_description=broken_rest) + cmd = check(dist) + cmd.check_restructuredtext() + self.assertEqual(cmd._warnings, 1) + + # let's see if we have an error with strict=1 + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx', + 'long_description': broken_rest} + self.assertRaises(DistutilsSetupError, self._run, metadata, + **{'strict': 1, 'restructuredtext': 1}) + + # and non-broken rest, including a non-ASCII character to test #12114 + metadata['long_description'] = 'title\n=====\n\ntest \u00df' + cmd = self._run(metadata, strict=1, restructuredtext=1) + self.assertEqual(cmd._warnings, 0) + + # check that includes work to test #31292 + metadata['long_description'] = 'title\n=====\n\n.. include:: includetest.rst' + cmd = self._run(metadata, cwd=HERE, strict=1, restructuredtext=1) + self.assertEqual(cmd._warnings, 0) + + @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") + def test_check_restructuredtext_with_syntax_highlight(self): + # Don't fail if there is a `code` or `code-block` directive + + example_rst_docs = [] + example_rst_docs.append(textwrap.dedent("""\ + Here's some code: + + .. code:: python + + def foo(): + pass + """)) + example_rst_docs.append(textwrap.dedent("""\ + Here's some code: + + .. code-block:: python + + def foo(): + pass + """)) + + for rest_with_code in example_rst_docs: + pkg_info, dist = self.create_dist(long_description=rest_with_code) + cmd = check(dist) + cmd.check_restructuredtext() + msgs = cmd._check_rst_data(rest_with_code) + if pygments is not None: + self.assertEqual(len(msgs), 0) + else: + self.assertEqual(len(msgs), 1) + self.assertEqual( + str(msgs[0][1]), + 'Cannot analyze code. Pygments package not found.' + ) + + def test_check_all(self): + + metadata = {'url': 'xxx', 'author': 'xxx'} + self.assertRaises(DistutilsSetupError, self._run, + {}, **{'strict': 1, + 'restructuredtext': 1}) + +def test_suite(): + return unittest.makeSuite(CheckTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_clean.py b/setuptools/_distutils/tests/test_clean.py new file mode 100644 index 00000000..c605afd8 --- /dev/null +++ b/setuptools/_distutils/tests/test_clean.py @@ -0,0 +1,49 @@ +"""Tests for distutils.command.clean.""" +import os +import unittest + +from distutils.command.clean import clean +from distutils.tests import support +from test.support import run_unittest + +class cleanTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_simple_run(self): + pkg_dir, dist = self.create_dist() + cmd = clean(dist) + + # let's add some elements clean should remove + dirs = [(d, os.path.join(pkg_dir, d)) + for d in ('build_temp', 'build_lib', 'bdist_base', + 'build_scripts', 'build_base')] + + for name, path in dirs: + os.mkdir(path) + setattr(cmd, name, path) + if name == 'build_base': + continue + for f in ('one', 'two', 'three'): + self.write_file(os.path.join(path, f)) + + # let's run the command + cmd.all = 1 + cmd.ensure_finalized() + cmd.run() + + # make sure the files where removed + for name, path in dirs: + self.assertFalse(os.path.exists(path), + '%s was not removed' % path) + + # let's run the command again (should spit warnings but succeed) + cmd.all = 1 + cmd.ensure_finalized() + cmd.run() + +def test_suite(): + return unittest.makeSuite(cleanTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_cmd.py b/setuptools/_distutils/tests/test_cmd.py new file mode 100644 index 00000000..cf5197c3 --- /dev/null +++ b/setuptools/_distutils/tests/test_cmd.py @@ -0,0 +1,126 @@ +"""Tests for distutils.cmd.""" +import unittest +import os +from test.support import captured_stdout, run_unittest + +from distutils.cmd import Command +from distutils.dist import Distribution +from distutils.errors import DistutilsOptionError +from distutils import debug + +class MyCmd(Command): + def initialize_options(self): + pass + +class CommandTestCase(unittest.TestCase): + + def setUp(self): + dist = Distribution() + self.cmd = MyCmd(dist) + + def test_ensure_string_list(self): + + cmd = self.cmd + cmd.not_string_list = ['one', 2, 'three'] + cmd.yes_string_list = ['one', 'two', 'three'] + cmd.not_string_list2 = object() + cmd.yes_string_list2 = 'ok' + cmd.ensure_string_list('yes_string_list') + cmd.ensure_string_list('yes_string_list2') + + self.assertRaises(DistutilsOptionError, + cmd.ensure_string_list, 'not_string_list') + + self.assertRaises(DistutilsOptionError, + cmd.ensure_string_list, 'not_string_list2') + + cmd.option1 = 'ok,dok' + cmd.ensure_string_list('option1') + self.assertEqual(cmd.option1, ['ok', 'dok']) + + cmd.option2 = ['xxx', 'www'] + cmd.ensure_string_list('option2') + + cmd.option3 = ['ok', 2] + self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, + 'option3') + + + def test_make_file(self): + + cmd = self.cmd + + # making sure it raises when infiles is not a string or a list/tuple + self.assertRaises(TypeError, cmd.make_file, + infiles=1, outfile='', func='func', args=()) + + # making sure execute gets called properly + def _execute(func, args, exec_msg, level): + self.assertEqual(exec_msg, 'generating out from in') + cmd.force = True + cmd.execute = _execute + cmd.make_file(infiles='in', outfile='out', func='func', args=()) + + def test_dump_options(self): + + msgs = [] + def _announce(msg, level): + msgs.append(msg) + cmd = self.cmd + cmd.announce = _announce + cmd.option1 = 1 + cmd.option2 = 1 + cmd.user_options = [('option1', '', ''), ('option2', '', '')] + cmd.dump_options() + + wanted = ["command options for 'MyCmd':", ' option1 = 1', + ' option2 = 1'] + self.assertEqual(msgs, wanted) + + def test_ensure_string(self): + cmd = self.cmd + cmd.option1 = 'ok' + cmd.ensure_string('option1') + + cmd.option2 = None + cmd.ensure_string('option2', 'xxx') + self.assertTrue(hasattr(cmd, 'option2')) + + cmd.option3 = 1 + self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') + + def test_ensure_filename(self): + cmd = self.cmd + cmd.option1 = __file__ + cmd.ensure_filename('option1') + cmd.option2 = 'xxx' + self.assertRaises(DistutilsOptionError, cmd.ensure_filename, 'option2') + + def test_ensure_dirname(self): + cmd = self.cmd + cmd.option1 = os.path.dirname(__file__) or os.curdir + cmd.ensure_dirname('option1') + cmd.option2 = 'xxx' + self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') + + def test_debug_print(self): + cmd = self.cmd + with captured_stdout() as stdout: + cmd.debug_print('xxx') + stdout.seek(0) + self.assertEqual(stdout.read(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + cmd.debug_print('xxx') + stdout.seek(0) + self.assertEqual(stdout.read(), 'xxx\n') + finally: + debug.DEBUG = False + +def test_suite(): + return unittest.makeSuite(CommandTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_config.py b/setuptools/_distutils/tests/test_config.py new file mode 100644 index 00000000..344084af --- /dev/null +++ b/setuptools/_distutils/tests/test_config.py @@ -0,0 +1,141 @@ +"""Tests for distutils.pypirc.pypirc.""" +import os +import unittest + +from distutils.core import PyPIRCCommand +from distutils.core import Distribution +from distutils.log import set_threshold +from distutils.log import WARN + +from distutils.tests import support +from test.support import run_unittest + +PYPIRC = """\ +[distutils] + +index-servers = + server1 + server2 + server3 + +[server1] +username:me +password:secret + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ + +[server3] +username:cbiggles +password:yh^%#rest-of-my-password +""" + +PYPIRC_OLD = """\ +[server-login] +username:tarek +password:secret +""" + +WANTED = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:xxx +""" + + +class BasePyPIRCCommandTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + def setUp(self): + """Patches the environment.""" + super(BasePyPIRCCommandTestCase, self).setUp() + self.tmp_dir = self.mkdtemp() + os.environ['HOME'] = self.tmp_dir + os.environ['USERPROFILE'] = self.tmp_dir + self.rc = os.path.join(self.tmp_dir, '.pypirc') + self.dist = Distribution() + + class command(PyPIRCCommand): + def __init__(self, dist): + PyPIRCCommand.__init__(self, dist) + def initialize_options(self): + pass + finalize_options = initialize_options + + self._cmd = command + self.old_threshold = set_threshold(WARN) + + def tearDown(self): + """Removes the patch.""" + set_threshold(self.old_threshold) + super(BasePyPIRCCommandTestCase, self).tearDown() + + +class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase): + + def test_server_registration(self): + # This test makes sure PyPIRCCommand knows how to: + # 1. handle several sections in .pypirc + # 2. handle the old format + + # new format + self.write_file(self.rc, PYPIRC) + cmd = self._cmd(self.dist) + config = cmd._read_pypirc() + + config = list(sorted(config.items())) + waited = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'https://upload.pypi.org/legacy/'), + ('server', 'server1'), ('username', 'me')] + self.assertEqual(config, waited) + + # old format + self.write_file(self.rc, PYPIRC_OLD) + config = cmd._read_pypirc() + config = list(sorted(config.items())) + waited = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'https://upload.pypi.org/legacy/'), + ('server', 'server-login'), ('username', 'tarek')] + self.assertEqual(config, waited) + + def test_server_empty_registration(self): + cmd = self._cmd(self.dist) + rc = cmd._get_rc_file() + self.assertFalse(os.path.exists(rc)) + cmd._store_pypirc('tarek', 'xxx') + self.assertTrue(os.path.exists(rc)) + f = open(rc) + try: + content = f.read() + self.assertEqual(content, WANTED) + finally: + f.close() + + def test_config_interpolation(self): + # using the % character in .pypirc should not raise an error (#20120) + self.write_file(self.rc, PYPIRC) + cmd = self._cmd(self.dist) + cmd.repository = 'server3' + config = cmd._read_pypirc() + + config = list(sorted(config.items())) + waited = [('password', 'yh^%#rest-of-my-password'), ('realm', 'pypi'), + ('repository', 'https://upload.pypi.org/legacy/'), + ('server', 'server3'), ('username', 'cbiggles')] + self.assertEqual(config, waited) + + +def test_suite(): + return unittest.makeSuite(PyPIRCCommandTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_config_cmd.py b/setuptools/_distutils/tests/test_config_cmd.py new file mode 100644 index 00000000..4cd9a6b9 --- /dev/null +++ b/setuptools/_distutils/tests/test_config_cmd.py @@ -0,0 +1,98 @@ +"""Tests for distutils.command.config.""" +import unittest +import os +import sys +from test.support import run_unittest + +from .py35compat import missing_compiler_executable + +from distutils.command.config import dump_file, config +from distutils.tests import support +from distutils import log + +class ConfigTestCase(support.LoggingSilencer, + support.TempdirManager, + unittest.TestCase): + + def _info(self, msg, *args): + for line in msg.splitlines(): + self._logs.append(line) + + def setUp(self): + super(ConfigTestCase, self).setUp() + self._logs = [] + self.old_log = log.info + log.info = self._info + + def tearDown(self): + log.info = self.old_log + super(ConfigTestCase, self).tearDown() + + def test_dump_file(self): + this_file = os.path.splitext(__file__)[0] + '.py' + f = open(this_file) + try: + numlines = len(f.readlines()) + finally: + f.close() + + dump_file(this_file, 'I am the header') + self.assertEqual(len(self._logs), numlines+1) + + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") + def test_search_cpp(self): + cmd = missing_compiler_executable(['preprocessor']) + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) + pkg_dir, dist = self.create_dist() + cmd = config(dist) + cmd._check_compiler() + compiler = cmd.compiler + if sys.platform[:3] == "aix" and "xlc" in compiler.preprocessor[0].lower(): + self.skipTest('xlc: The -E option overrides the -P, -o, and -qsyntaxonly options') + + # simple pattern searches + match = cmd.search_cpp(pattern='xxx', body='/* xxx */') + self.assertEqual(match, 0) + + match = cmd.search_cpp(pattern='_configtest', body='/* xxx */') + self.assertEqual(match, 1) + + def test_finalize_options(self): + # finalize_options does a bit of transformation + # on options + pkg_dir, dist = self.create_dist() + cmd = config(dist) + cmd.include_dirs = 'one%stwo' % os.pathsep + cmd.libraries = 'one' + cmd.library_dirs = 'three%sfour' % os.pathsep + cmd.ensure_finalized() + + self.assertEqual(cmd.include_dirs, ['one', 'two']) + self.assertEqual(cmd.libraries, ['one']) + self.assertEqual(cmd.library_dirs, ['three', 'four']) + + def test_clean(self): + # _clean removes files + tmp_dir = self.mkdtemp() + f1 = os.path.join(tmp_dir, 'one') + f2 = os.path.join(tmp_dir, 'two') + + self.write_file(f1, 'xxx') + self.write_file(f2, 'xxx') + + for f in (f1, f2): + self.assertTrue(os.path.exists(f)) + + pkg_dir, dist = self.create_dist() + cmd = config(dist) + cmd._clean(f1, f2) + + for f in (f1, f2): + self.assertFalse(os.path.exists(f)) + +def test_suite(): + return unittest.makeSuite(ConfigTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_core.py b/setuptools/_distutils/tests/test_core.py new file mode 100644 index 00000000..27ce7324 --- /dev/null +++ b/setuptools/_distutils/tests/test_core.py @@ -0,0 +1,140 @@ +"""Tests for distutils.core.""" + +import io +import distutils.core +import os +import shutil +import sys +import test.support +from test.support import captured_stdout, run_unittest +import unittest +from distutils.tests import support +from distutils import log + +# setup script that uses __file__ +setup_using___file__ = """\ + +__file__ + +from distutils.core import setup +setup() +""" + +setup_prints_cwd = """\ + +import os +print(os.getcwd()) + +from distutils.core import setup +setup() +""" + +setup_does_nothing = """\ +from distutils.core import setup +setup() +""" + + +setup_defines_subclass = """\ +from distutils.core import setup +from distutils.command.install import install as _install + +class install(_install): + sub_commands = _install.sub_commands + ['cmd'] + +setup(cmdclass={'install': install}) +""" + +class CoreTestCase(support.EnvironGuard, unittest.TestCase): + + def setUp(self): + super(CoreTestCase, self).setUp() + self.old_stdout = sys.stdout + self.cleanup_testfn() + self.old_argv = sys.argv, sys.argv[:] + self.addCleanup(log.set_threshold, log._global_log.threshold) + + def tearDown(self): + sys.stdout = self.old_stdout + self.cleanup_testfn() + sys.argv = self.old_argv[0] + sys.argv[:] = self.old_argv[1] + super(CoreTestCase, self).tearDown() + + def cleanup_testfn(self): + path = test.support.TESTFN + if os.path.isfile(path): + os.remove(path) + elif os.path.isdir(path): + shutil.rmtree(path) + + def write_setup(self, text, path=test.support.TESTFN): + f = open(path, "w") + try: + f.write(text) + finally: + f.close() + return path + + def test_run_setup_provides_file(self): + # Make sure the script can use __file__; if that's missing, the test + # setup.py script will raise NameError. + distutils.core.run_setup( + self.write_setup(setup_using___file__)) + + def test_run_setup_preserves_sys_argv(self): + # Make sure run_setup does not clobber sys.argv + argv_copy = sys.argv.copy() + distutils.core.run_setup( + self.write_setup(setup_does_nothing)) + self.assertEqual(sys.argv, argv_copy) + + def test_run_setup_defines_subclass(self): + # Make sure the script can use __file__; if that's missing, the test + # setup.py script will raise NameError. + dist = distutils.core.run_setup( + self.write_setup(setup_defines_subclass)) + install = dist.get_command_obj('install') + self.assertIn('cmd', install.sub_commands) + + def test_run_setup_uses_current_dir(self): + # This tests that the setup script is run with the current directory + # as its own current directory; this was temporarily broken by a + # previous patch when TESTFN did not use the current directory. + sys.stdout = io.StringIO() + cwd = os.getcwd() + + # Create a directory and write the setup.py file there: + os.mkdir(test.support.TESTFN) + setup_py = os.path.join(test.support.TESTFN, "setup.py") + distutils.core.run_setup( + self.write_setup(setup_prints_cwd, path=setup_py)) + + output = sys.stdout.getvalue() + if output.endswith("\n"): + output = output[:-1] + self.assertEqual(cwd, output) + + def test_debug_mode(self): + # this covers the code called when DEBUG is set + sys.argv = ['setup.py', '--name'] + with captured_stdout() as stdout: + distutils.core.setup(name='bar') + stdout.seek(0) + self.assertEqual(stdout.read(), 'bar\n') + + distutils.core.DEBUG = True + try: + with captured_stdout() as stdout: + distutils.core.setup(name='bar') + finally: + distutils.core.DEBUG = False + stdout.seek(0) + wanted = "options (after parsing config files):\n" + self.assertEqual(stdout.readlines()[0], wanted) + +def test_suite(): + return unittest.makeSuite(CoreTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_cygwinccompiler.py b/setuptools/_distutils/tests/test_cygwinccompiler.py new file mode 100644 index 00000000..9dc869de --- /dev/null +++ b/setuptools/_distutils/tests/test_cygwinccompiler.py @@ -0,0 +1,154 @@ +"""Tests for distutils.cygwinccompiler.""" +import unittest +import sys +import os +from io import BytesIO +from test.support import run_unittest + +from distutils import cygwinccompiler +from distutils.cygwinccompiler import (check_config_h, + CONFIG_H_OK, CONFIG_H_NOTOK, + CONFIG_H_UNCERTAIN, get_versions, + get_msvcr) +from distutils.tests import support + +class FakePopen(object): + test_class = None + + def __init__(self, cmd, shell, stdout): + self.cmd = cmd.split()[0] + exes = self.test_class._exes + if self.cmd in exes: + # issue #6438 in Python 3.x, Popen returns bytes + self.stdout = BytesIO(exes[self.cmd]) + else: + self.stdout = os.popen(cmd, 'r') + + +class CygwinCCompilerTestCase(support.TempdirManager, + unittest.TestCase): + + def setUp(self): + super(CygwinCCompilerTestCase, self).setUp() + self.version = sys.version + self.python_h = os.path.join(self.mkdtemp(), 'python.h') + from distutils import sysconfig + self.old_get_config_h_filename = sysconfig.get_config_h_filename + sysconfig.get_config_h_filename = self._get_config_h_filename + self.old_find_executable = cygwinccompiler.find_executable + cygwinccompiler.find_executable = self._find_executable + self._exes = {} + self.old_popen = cygwinccompiler.Popen + FakePopen.test_class = self + cygwinccompiler.Popen = FakePopen + + def tearDown(self): + sys.version = self.version + from distutils import sysconfig + sysconfig.get_config_h_filename = self.old_get_config_h_filename + cygwinccompiler.find_executable = self.old_find_executable + cygwinccompiler.Popen = self.old_popen + super(CygwinCCompilerTestCase, self).tearDown() + + def _get_config_h_filename(self): + return self.python_h + + def _find_executable(self, name): + if name in self._exes: + return name + return None + + def test_check_config_h(self): + + # check_config_h looks for "GCC" in sys.version first + # returns CONFIG_H_OK if found + sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' + '4.0.1 (Apple Computer, Inc. build 5370)]') + + self.assertEqual(check_config_h()[0], CONFIG_H_OK) + + # then it tries to see if it can find "__GNUC__" in pyconfig.h + sys.version = 'something without the *CC word' + + # if the file doesn't exist it returns CONFIG_H_UNCERTAIN + self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN) + + # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK + self.write_file(self.python_h, 'xxx') + self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK) + + # and CONFIG_H_OK if __GNUC__ is found + self.write_file(self.python_h, 'xxx __GNUC__ xxx') + self.assertEqual(check_config_h()[0], CONFIG_H_OK) + + def test_get_versions(self): + + # get_versions calls distutils.spawn.find_executable on + # 'gcc', 'ld' and 'dllwrap' + self.assertEqual(get_versions(), (None, None, None)) + + # Let's fake we have 'gcc' and it returns '3.4.5' + self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' + res = get_versions() + self.assertEqual(str(res[0]), '3.4.5') + + # and let's see what happens when the version + # doesn't match the regular expression + # (\d+\.\d+(\.\d+)*) + self._exes['gcc'] = b'very strange output' + res = get_versions() + self.assertEqual(res[0], None) + + # same thing for ld + self._exes['ld'] = b'GNU ld version 2.17.50 20060824' + res = get_versions() + self.assertEqual(str(res[1]), '2.17.50') + self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' + res = get_versions() + self.assertEqual(res[1], None) + + # and dllwrap + self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' + res = get_versions() + self.assertEqual(str(res[2]), '2.17.50') + self._exes['dllwrap'] = b'Cheese Wrap' + res = get_versions() + self.assertEqual(res[2], None) + + def test_get_msvcr(self): + + # none + sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' + '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') + self.assertEqual(get_msvcr(), None) + + # MSVC 7.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1300 32 bits (Intel)]') + self.assertEqual(get_msvcr(), ['msvcr70']) + + # MSVC 7.1 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1310 32 bits (Intel)]') + self.assertEqual(get_msvcr(), ['msvcr71']) + + # VS2005 / MSVC 8.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1400 32 bits (Intel)]') + self.assertEqual(get_msvcr(), ['msvcr80']) + + # VS2008 / MSVC 9.0 + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1500 32 bits (Intel)]') + self.assertEqual(get_msvcr(), ['msvcr90']) + + # unknown + sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' + '[MSC v.1999 32 bits (Intel)]') + self.assertRaises(ValueError, get_msvcr) + +def test_suite(): + return unittest.makeSuite(CygwinCCompilerTestCase) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_dep_util.py b/setuptools/_distutils/tests/test_dep_util.py new file mode 100644 index 00000000..c6fae39c --- /dev/null +++ b/setuptools/_distutils/tests/test_dep_util.py @@ -0,0 +1,80 @@ +"""Tests for distutils.dep_util.""" +import unittest +import os + +from distutils.dep_util import newer, newer_pairwise, newer_group +from distutils.errors import DistutilsFileError +from distutils.tests import support +from test.support import run_unittest + +class DepUtilTestCase(support.TempdirManager, unittest.TestCase): + + def test_newer(self): + + tmpdir = self.mkdtemp() + new_file = os.path.join(tmpdir, 'new') + old_file = os.path.abspath(__file__) + + # Raise DistutilsFileError if 'new_file' does not exist. + self.assertRaises(DistutilsFileError, newer, new_file, old_file) + + # Return true if 'new_file' exists and is more recently modified than + # 'old_file', or if 'new_file' exists and 'old_file' doesn't. + self.write_file(new_file) + self.assertTrue(newer(new_file, 'I_dont_exist')) + self.assertTrue(newer(new_file, old_file)) + + # Return false if both exist and 'old_file' is the same age or younger + # than 'new_file'. + self.assertFalse(newer(old_file, new_file)) + + def test_newer_pairwise(self): + tmpdir = self.mkdtemp() + sources = os.path.join(tmpdir, 'sources') + targets = os.path.join(tmpdir, 'targets') + os.mkdir(sources) + os.mkdir(targets) + one = os.path.join(sources, 'one') + two = os.path.join(sources, 'two') + three = os.path.abspath(__file__) # I am the old file + four = os.path.join(targets, 'four') + self.write_file(one) + self.write_file(two) + self.write_file(four) + + self.assertEqual(newer_pairwise([one, two], [three, four]), + ([one],[three])) + + def test_newer_group(self): + tmpdir = self.mkdtemp() + sources = os.path.join(tmpdir, 'sources') + os.mkdir(sources) + one = os.path.join(sources, 'one') + two = os.path.join(sources, 'two') + three = os.path.join(sources, 'three') + old_file = os.path.abspath(__file__) + + # return true if 'old_file' is out-of-date with respect to any file + # listed in 'sources'. + self.write_file(one) + self.write_file(two) + self.write_file(three) + self.assertTrue(newer_group([one, two, three], old_file)) + self.assertFalse(newer_group([one, two, old_file], three)) + + # missing handling + os.remove(one) + self.assertRaises(OSError, newer_group, [one, two, old_file], three) + + self.assertFalse(newer_group([one, two, old_file], three, + missing='ignore')) + + self.assertTrue(newer_group([one, two, old_file], three, + missing='newer')) + + +def test_suite(): + return unittest.makeSuite(DepUtilTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_dir_util.py b/setuptools/_distutils/tests/test_dir_util.py new file mode 100644 index 00000000..d436cf83 --- /dev/null +++ b/setuptools/_distutils/tests/test_dir_util.py @@ -0,0 +1,139 @@ +"""Tests for distutils.dir_util.""" +import unittest +import os +import stat +import sys +from unittest.mock import patch + +from distutils import dir_util, errors +from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, + ensure_relative) + +from distutils import log +from distutils.tests import support +from test.support import run_unittest + + +class DirUtilTestCase(support.TempdirManager, unittest.TestCase): + + def _log(self, msg, *args): + if len(args) > 0: + self._logs.append(msg % args) + else: + self._logs.append(msg) + + def setUp(self): + super(DirUtilTestCase, self).setUp() + self._logs = [] + tmp_dir = self.mkdtemp() + self.root_target = os.path.join(tmp_dir, 'deep') + self.target = os.path.join(self.root_target, 'here') + self.target2 = os.path.join(tmp_dir, 'deep2') + self.old_log = log.info + log.info = self._log + + def tearDown(self): + log.info = self.old_log + super(DirUtilTestCase, self).tearDown() + + def test_mkpath_remove_tree_verbosity(self): + + mkpath(self.target, verbose=0) + wanted = [] + self.assertEqual(self._logs, wanted) + remove_tree(self.root_target, verbose=0) + + mkpath(self.target, verbose=1) + wanted = ['creating %s' % self.root_target, + 'creating %s' % self.target] + self.assertEqual(self._logs, wanted) + self._logs = [] + + remove_tree(self.root_target, verbose=1) + wanted = ["removing '%s' (and everything under it)" % self.root_target] + self.assertEqual(self._logs, wanted) + + @unittest.skipIf(sys.platform.startswith('win'), + "This test is only appropriate for POSIX-like systems.") + def test_mkpath_with_custom_mode(self): + # Get and set the current umask value for testing mode bits. + umask = os.umask(0o002) + os.umask(umask) + mkpath(self.target, 0o700) + self.assertEqual( + stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) + mkpath(self.target2, 0o555) + self.assertEqual( + stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) + + def test_create_tree_verbosity(self): + + create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) + self.assertEqual(self._logs, []) + remove_tree(self.root_target, verbose=0) + + wanted = ['creating %s' % self.root_target] + create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) + self.assertEqual(self._logs, wanted) + + remove_tree(self.root_target, verbose=0) + + def test_copy_tree_verbosity(self): + + mkpath(self.target, verbose=0) + + copy_tree(self.target, self.target2, verbose=0) + self.assertEqual(self._logs, []) + + remove_tree(self.root_target, verbose=0) + + mkpath(self.target, verbose=0) + a_file = os.path.join(self.target, 'ok.txt') + with open(a_file, 'w') as f: + f.write('some content') + + wanted = ['copying %s -> %s' % (a_file, self.target2)] + copy_tree(self.target, self.target2, verbose=1) + self.assertEqual(self._logs, wanted) + + remove_tree(self.root_target, verbose=0) + remove_tree(self.target2, verbose=0) + + def test_copy_tree_skips_nfs_temp_files(self): + mkpath(self.target, verbose=0) + + a_file = os.path.join(self.target, 'ok.txt') + nfs_file = os.path.join(self.target, '.nfs123abc') + for f in a_file, nfs_file: + with open(f, 'w') as fh: + fh.write('some content') + + copy_tree(self.target, self.target2) + self.assertEqual(os.listdir(self.target2), ['ok.txt']) + + remove_tree(self.root_target, verbose=0) + remove_tree(self.target2, verbose=0) + + def test_ensure_relative(self): + if os.sep == '/': + self.assertEqual(ensure_relative('/home/foo'), 'home/foo') + self.assertEqual(ensure_relative('some/path'), 'some/path') + else: # \\ + self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') + self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') + + def test_copy_tree_exception_in_listdir(self): + """ + An exception in listdir should raise a DistutilsFileError + """ + with patch("os.listdir", side_effect=OSError()), \ + self.assertRaises(errors.DistutilsFileError): + src = self.tempdirs[-1] + dir_util.copy_tree(src, None) + + +def test_suite(): + return unittest.makeSuite(DirUtilTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_dist.py b/setuptools/_distutils/tests/test_dist.py new file mode 100644 index 00000000..d431085b --- /dev/null +++ b/setuptools/_distutils/tests/test_dist.py @@ -0,0 +1,532 @@ +"""Tests for distutils.dist.""" +import os +import io +import sys +import unittest +import warnings +import textwrap + +from unittest import mock + +from distutils.dist import Distribution, fix_help_options +from distutils.cmd import Command + +from test.support import ( + TESTFN, captured_stdout, captured_stderr, run_unittest +) +from distutils.tests import support +from distutils import log + + +class test_dist(Command): + """Sample distutils extension command.""" + + user_options = [ + ("sample-option=", "S", "help text"), + ] + + def initialize_options(self): + self.sample_option = None + + +class TestDistribution(Distribution): + """Distribution subclasses that avoids the default search for + configuration files. + + The ._config_files attribute must be set before + .parse_config_files() is called. + """ + + def find_config_files(self): + return self._config_files + + +class DistributionTestCase(support.LoggingSilencer, + support.TempdirManager, + support.EnvironGuard, + unittest.TestCase): + + def setUp(self): + super(DistributionTestCase, self).setUp() + self.argv = sys.argv, sys.argv[:] + del sys.argv[1:] + + def tearDown(self): + sys.argv = self.argv[0] + sys.argv[:] = self.argv[1] + super(DistributionTestCase, self).tearDown() + + def create_distribution(self, configfiles=()): + d = TestDistribution() + d._config_files = configfiles + d.parse_config_files() + d.parse_command_line() + return d + + def test_command_packages_unspecified(self): + sys.argv.append("build") + d = self.create_distribution() + self.assertEqual(d.get_command_packages(), ["distutils.command"]) + + def test_command_packages_cmdline(self): + from distutils.tests.test_dist import test_dist + sys.argv.extend(["--command-packages", + "foo.bar,distutils.tests", + "test_dist", + "-Ssometext", + ]) + d = self.create_distribution() + # let's actually try to load our test command: + self.assertEqual(d.get_command_packages(), + ["distutils.command", "foo.bar", "distutils.tests"]) + cmd = d.get_command_obj("test_dist") + self.assertIsInstance(cmd, test_dist) + self.assertEqual(cmd.sample_option, "sometext") + + @unittest.skipIf( + 'distutils' not in Distribution.parse_config_files.__module__, + 'Cannot test when virtualenv has monkey-patched Distribution.', + ) + def test_venv_install_options(self): + sys.argv.append("install") + self.addCleanup(os.unlink, TESTFN) + + fakepath = '/somedir' + + with open(TESTFN, "w") as f: + print(("[install]\n" + "install-base = {0}\n" + "install-platbase = {0}\n" + "install-lib = {0}\n" + "install-platlib = {0}\n" + "install-purelib = {0}\n" + "install-headers = {0}\n" + "install-scripts = {0}\n" + "install-data = {0}\n" + "prefix = {0}\n" + "exec-prefix = {0}\n" + "home = {0}\n" + "user = {0}\n" + "root = {0}").format(fakepath), file=f) + + # Base case: Not in a Virtual Environment + with mock.patch.multiple(sys, prefix='/a', base_prefix='/a') as values: + d = self.create_distribution([TESTFN]) + + option_tuple = (TESTFN, fakepath) + + result_dict = { + 'install_base': option_tuple, + 'install_platbase': option_tuple, + 'install_lib': option_tuple, + 'install_platlib': option_tuple, + 'install_purelib': option_tuple, + 'install_headers': option_tuple, + 'install_scripts': option_tuple, + 'install_data': option_tuple, + 'prefix': option_tuple, + 'exec_prefix': option_tuple, + 'home': option_tuple, + 'user': option_tuple, + 'root': option_tuple, + } + + self.assertEqual( + sorted(d.command_options.get('install').keys()), + sorted(result_dict.keys())) + + for (key, value) in d.command_options.get('install').items(): + self.assertEqual(value, result_dict[key]) + + # Test case: In a Virtual Environment + with mock.patch.multiple(sys, prefix='/a', base_prefix='/b') as values: + d = self.create_distribution([TESTFN]) + + for key in result_dict.keys(): + self.assertNotIn(key, d.command_options.get('install', {})) + + def test_command_packages_configfile(self): + sys.argv.append("build") + self.addCleanup(os.unlink, TESTFN) + f = open(TESTFN, "w") + try: + print("[global]", file=f) + print("command_packages = foo.bar, splat", file=f) + finally: + f.close() + + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "foo.bar", "splat"]) + + # ensure command line overrides config: + sys.argv[1:] = ["--command-packages", "spork", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), + ["distutils.command", "spork"]) + + # Setting --command-packages to '' should cause the default to + # be used even if a config file specified something else: + sys.argv[1:] = ["--command-packages", "", "build"] + d = self.create_distribution([TESTFN]) + self.assertEqual(d.get_command_packages(), ["distutils.command"]) + + def test_empty_options(self): + # an empty options dictionary should not stay in the + # list of attributes + + # catching warnings + warns = [] + + def _warn(msg): + warns.append(msg) + + self.addCleanup(setattr, warnings, 'warn', warnings.warn) + warnings.warn = _warn + dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx', + 'version': 'xxx', 'url': 'xxxx', + 'options': {}}) + + self.assertEqual(len(warns), 0) + self.assertNotIn('options', dir(dist)) + + def test_finalize_options(self): + attrs = {'keywords': 'one,two', + 'platforms': 'one,two'} + + dist = Distribution(attrs=attrs) + dist.finalize_options() + + # finalize_option splits platforms and keywords + self.assertEqual(dist.metadata.platforms, ['one', 'two']) + self.assertEqual(dist.metadata.keywords, ['one', 'two']) + + attrs = {'keywords': 'foo bar', + 'platforms': 'foo bar'} + dist = Distribution(attrs=attrs) + dist.finalize_options() + self.assertEqual(dist.metadata.platforms, ['foo bar']) + self.assertEqual(dist.metadata.keywords, ['foo bar']) + + def test_get_command_packages(self): + dist = Distribution() + self.assertEqual(dist.command_packages, None) + cmds = dist.get_command_packages() + self.assertEqual(cmds, ['distutils.command']) + self.assertEqual(dist.command_packages, + ['distutils.command']) + + dist.command_packages = 'one,two' + cmds = dist.get_command_packages() + self.assertEqual(cmds, ['distutils.command', 'one', 'two']) + + def test_announce(self): + # make sure the level is known + dist = Distribution() + args = ('ok',) + kwargs = {'level': 'ok2'} + self.assertRaises(ValueError, dist.announce, args, kwargs) + + + def test_find_config_files_disable(self): + # Ticket #1180: Allow user to disable their home config file. + temp_home = self.mkdtemp() + if os.name == 'posix': + user_filename = os.path.join(temp_home, ".pydistutils.cfg") + else: + user_filename = os.path.join(temp_home, "pydistutils.cfg") + + with open(user_filename, 'w') as f: + f.write('[distutils]\n') + + def _expander(path): + return temp_home + + old_expander = os.path.expanduser + os.path.expanduser = _expander + try: + d = Distribution() + all_files = d.find_config_files() + + d = Distribution(attrs={'script_args': ['--no-user-cfg']}) + files = d.find_config_files() + finally: + os.path.expanduser = old_expander + + # make sure --no-user-cfg disables the user cfg file + self.assertEqual(len(all_files)-1, len(files)) + +class MetadataTestCase(support.TempdirManager, support.EnvironGuard, + unittest.TestCase): + + def setUp(self): + super(MetadataTestCase, self).setUp() + self.argv = sys.argv, sys.argv[:] + + def tearDown(self): + sys.argv = self.argv[0] + sys.argv[:] = self.argv[1] + super(MetadataTestCase, self).tearDown() + + def format_metadata(self, dist): + sio = io.StringIO() + dist.metadata.write_pkg_file(sio) + return sio.getvalue() + + def test_simple_metadata(self): + attrs = {"name": "package", + "version": "1.0"} + dist = Distribution(attrs) + meta = self.format_metadata(dist) + self.assertIn("Metadata-Version: 1.0", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertNotIn("requires:", meta.lower()) + self.assertNotIn("obsoletes:", meta.lower()) + + def test_provides(self): + attrs = {"name": "package", + "version": "1.0", + "provides": ["package", "package.sub"]} + dist = Distribution(attrs) + self.assertEqual(dist.metadata.get_provides(), + ["package", "package.sub"]) + self.assertEqual(dist.get_provides(), + ["package", "package.sub"]) + meta = self.format_metadata(dist) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("requires:", meta.lower()) + self.assertNotIn("obsoletes:", meta.lower()) + + def test_provides_illegal(self): + self.assertRaises(ValueError, Distribution, + {"name": "package", + "version": "1.0", + "provides": ["my.pkg (splat)"]}) + + def test_requires(self): + attrs = {"name": "package", + "version": "1.0", + "requires": ["other", "another (==1.0)"]} + dist = Distribution(attrs) + self.assertEqual(dist.metadata.get_requires(), + ["other", "another (==1.0)"]) + self.assertEqual(dist.get_requires(), + ["other", "another (==1.0)"]) + meta = self.format_metadata(dist) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertIn("Requires: other", meta) + self.assertIn("Requires: another (==1.0)", meta) + self.assertNotIn("obsoletes:", meta.lower()) + + def test_requires_illegal(self): + self.assertRaises(ValueError, Distribution, + {"name": "package", + "version": "1.0", + "requires": ["my.pkg (splat)"]}) + + def test_requires_to_list(self): + attrs = {"name": "package", + "requires": iter(["other"])} + dist = Distribution(attrs) + self.assertIsInstance(dist.metadata.requires, list) + + + def test_obsoletes(self): + attrs = {"name": "package", + "version": "1.0", + "obsoletes": ["other", "another (<1.0)"]} + dist = Distribution(attrs) + self.assertEqual(dist.metadata.get_obsoletes(), + ["other", "another (<1.0)"]) + self.assertEqual(dist.get_obsoletes(), + ["other", "another (<1.0)"]) + meta = self.format_metadata(dist) + self.assertIn("Metadata-Version: 1.1", meta) + self.assertNotIn("provides:", meta.lower()) + self.assertNotIn("requires:", meta.lower()) + self.assertIn("Obsoletes: other", meta) + self.assertIn("Obsoletes: another (<1.0)", meta) + + def test_obsoletes_illegal(self): + self.assertRaises(ValueError, Distribution, + {"name": "package", + "version": "1.0", + "obsoletes": ["my.pkg (splat)"]}) + + def test_obsoletes_to_list(self): + attrs = {"name": "package", + "obsoletes": iter(["other"])} + dist = Distribution(attrs) + self.assertIsInstance(dist.metadata.obsoletes, list) + + def test_classifier(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'classifiers': ['Programming Language :: Python :: 3']} + dist = Distribution(attrs) + self.assertEqual(dist.get_classifiers(), + ['Programming Language :: Python :: 3']) + meta = self.format_metadata(dist) + self.assertIn('Metadata-Version: 1.1', meta) + + def test_classifier_invalid_type(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'classifiers': ('Programming Language :: Python :: 3',)} + with captured_stderr() as error: + d = Distribution(attrs) + # should have warning about passing a non-list + self.assertIn('should be a list', error.getvalue()) + # should be converted to a list + self.assertIsInstance(d.metadata.classifiers, list) + self.assertEqual(d.metadata.classifiers, + list(attrs['classifiers'])) + + def test_keywords(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'keywords': ['spam', 'eggs', 'life of brian']} + dist = Distribution(attrs) + self.assertEqual(dist.get_keywords(), + ['spam', 'eggs', 'life of brian']) + + def test_keywords_invalid_type(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'keywords': ('spam', 'eggs', 'life of brian')} + with captured_stderr() as error: + d = Distribution(attrs) + # should have warning about passing a non-list + self.assertIn('should be a list', error.getvalue()) + # should be converted to a list + self.assertIsInstance(d.metadata.keywords, list) + self.assertEqual(d.metadata.keywords, list(attrs['keywords'])) + + def test_platforms(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'platforms': ['GNU/Linux', 'Some Evil Platform']} + dist = Distribution(attrs) + self.assertEqual(dist.get_platforms(), + ['GNU/Linux', 'Some Evil Platform']) + + def test_platforms_invalid_types(self): + attrs = {'name': 'Monty', 'version': '1.0', + 'platforms': ('GNU/Linux', 'Some Evil Platform')} + with captured_stderr() as error: + d = Distribution(attrs) + # should have warning about passing a non-list + self.assertIn('should be a list', error.getvalue()) + # should be converted to a list + self.assertIsInstance(d.metadata.platforms, list) + self.assertEqual(d.metadata.platforms, list(attrs['platforms'])) + + def test_download_url(self): + attrs = {'name': 'Boa', 'version': '3.0', + 'download_url': 'http://example.org/boa'} + dist = Distribution(attrs) + meta = self.format_metadata(dist) + self.assertIn('Metadata-Version: 1.1', meta) + + def test_long_description(self): + long_desc = textwrap.dedent("""\ + example:: + We start here + and continue here + and end here.""") + attrs = {"name": "package", + "version": "1.0", + "long_description": long_desc} + + dist = Distribution(attrs) + meta = self.format_metadata(dist) + meta = meta.replace('\n' + 8 * ' ', '\n') + self.assertIn(long_desc, meta) + + def test_custom_pydistutils(self): + # fixes #2166 + # make sure pydistutils.cfg is found + if os.name == 'posix': + user_filename = ".pydistutils.cfg" + else: + user_filename = "pydistutils.cfg" + + temp_dir = self.mkdtemp() + user_filename = os.path.join(temp_dir, user_filename) + f = open(user_filename, 'w') + try: + f.write('.') + finally: + f.close() + + try: + dist = Distribution() + + # linux-style + if sys.platform in ('linux', 'darwin'): + os.environ['HOME'] = temp_dir + files = dist.find_config_files() + self.assertIn(user_filename, files) + + # win32-style + if sys.platform == 'win32': + # home drive should be found + os.environ['USERPROFILE'] = temp_dir + files = dist.find_config_files() + self.assertIn(user_filename, files, + '%r not found in %r' % (user_filename, files)) + finally: + os.remove(user_filename) + + def test_fix_help_options(self): + help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] + fancy_options = fix_help_options(help_tuples) + self.assertEqual(fancy_options[0], ('a', 'b', 'c')) + self.assertEqual(fancy_options[1], (1, 2, 3)) + + def test_show_help(self): + # smoke test, just makes sure some help is displayed + self.addCleanup(log.set_threshold, log._global_log.threshold) + dist = Distribution() + sys.argv = [] + dist.help = 1 + dist.script_name = 'setup.py' + with captured_stdout() as s: + dist.parse_command_line() + + output = [line for line in s.getvalue().split('\n') + if line.strip() != ''] + self.assertTrue(output) + + + def test_read_metadata(self): + attrs = {"name": "package", + "version": "1.0", + "long_description": "desc", + "description": "xxx", + "download_url": "http://example.com", + "keywords": ['one', 'two'], + "requires": ['foo']} + + dist = Distribution(attrs) + metadata = dist.metadata + + # write it then reloads it + PKG_INFO = io.StringIO() + metadata.write_pkg_file(PKG_INFO) + PKG_INFO.seek(0) + metadata.read_pkg_file(PKG_INFO) + + self.assertEqual(metadata.name, "package") + self.assertEqual(metadata.version, "1.0") + self.assertEqual(metadata.description, "xxx") + self.assertEqual(metadata.download_url, 'http://example.com') + self.assertEqual(metadata.keywords, ['one', 'two']) + self.assertEqual(metadata.platforms, ['UNKNOWN']) + self.assertEqual(metadata.obsoletes, None) + self.assertEqual(metadata.requires, ['foo']) + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(DistributionTestCase)) + suite.addTest(unittest.makeSuite(MetadataTestCase)) + return suite + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_extension.py b/setuptools/_distutils/tests/test_extension.py new file mode 100644 index 00000000..e35f2738 --- /dev/null +++ b/setuptools/_distutils/tests/test_extension.py @@ -0,0 +1,69 @@ +"""Tests for distutils.extension.""" +import unittest +import os +import warnings + +from test.support import check_warnings, run_unittest +from distutils.extension import read_setup_file, Extension + +class ExtensionTestCase(unittest.TestCase): + + def test_read_setup_file(self): + # trying to read a Setup file + # (sample extracted from the PyGame project) + setup = os.path.join(os.path.dirname(__file__), 'Setup.sample') + + exts = read_setup_file(setup) + names = [ext.name for ext in exts] + names.sort() + + # here are the extensions read_setup_file should have created + # out of the file + wanted = ['_arraysurfarray', '_camera', '_numericsndarray', + '_numericsurfarray', 'base', 'bufferproxy', 'cdrom', + 'color', 'constants', 'display', 'draw', 'event', + 'fastevent', 'font', 'gfxdraw', 'image', 'imageext', + 'joystick', 'key', 'mask', 'mixer', 'mixer_music', + 'mouse', 'movie', 'overlay', 'pixelarray', 'pypm', + 'rect', 'rwobject', 'scrap', 'surface', 'surflock', + 'time', 'transform'] + + self.assertEqual(names, wanted) + + def test_extension_init(self): + # the first argument, which is the name, must be a string + self.assertRaises(AssertionError, Extension, 1, []) + ext = Extension('name', []) + self.assertEqual(ext.name, 'name') + + # the second argument, which is the list of files, must + # be a list of strings + self.assertRaises(AssertionError, Extension, 'name', 'file') + self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) + ext = Extension('name', ['file1', 'file2']) + self.assertEqual(ext.sources, ['file1', 'file2']) + + # others arguments have defaults + for attr in ('include_dirs', 'define_macros', 'undef_macros', + 'library_dirs', 'libraries', 'runtime_library_dirs', + 'extra_objects', 'extra_compile_args', 'extra_link_args', + 'export_symbols', 'swig_opts', 'depends'): + self.assertEqual(getattr(ext, attr), []) + + self.assertEqual(ext.language, None) + self.assertEqual(ext.optional, None) + + # if there are unknown keyword options, warn about them + with check_warnings() as w: + warnings.simplefilter('always') + ext = Extension('name', ['file1', 'file2'], chic=True) + + self.assertEqual(len(w.warnings), 1) + self.assertEqual(str(w.warnings[0].message), + "Unknown Extension options: 'chic'") + +def test_suite(): + return unittest.makeSuite(ExtensionTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_file_util.py b/setuptools/_distutils/tests/test_file_util.py new file mode 100644 index 00000000..a4e2d025 --- /dev/null +++ b/setuptools/_distutils/tests/test_file_util.py @@ -0,0 +1,122 @@ +"""Tests for distutils.file_util.""" +import unittest +import os +import errno +from unittest.mock import patch + +from distutils.file_util import move_file, copy_file +from distutils import log +from distutils.tests import support +from distutils.errors import DistutilsFileError +from test.support import run_unittest, unlink + +class FileUtilTestCase(support.TempdirManager, unittest.TestCase): + + def _log(self, msg, *args): + if len(args) > 0: + self._logs.append(msg % args) + else: + self._logs.append(msg) + + def setUp(self): + super(FileUtilTestCase, self).setUp() + self._logs = [] + self.old_log = log.info + log.info = self._log + tmp_dir = self.mkdtemp() + self.source = os.path.join(tmp_dir, 'f1') + self.target = os.path.join(tmp_dir, 'f2') + self.target_dir = os.path.join(tmp_dir, 'd1') + + def tearDown(self): + log.info = self.old_log + super(FileUtilTestCase, self).tearDown() + + def test_move_file_verbosity(self): + f = open(self.source, 'w') + try: + f.write('some content') + finally: + f.close() + + move_file(self.source, self.target, verbose=0) + wanted = [] + self.assertEqual(self._logs, wanted) + + # back to original state + move_file(self.target, self.source, verbose=0) + + move_file(self.source, self.target, verbose=1) + wanted = ['moving %s -> %s' % (self.source, self.target)] + self.assertEqual(self._logs, wanted) + + # back to original state + move_file(self.target, self.source, verbose=0) + + self._logs = [] + # now the target is a dir + os.mkdir(self.target_dir) + move_file(self.source, self.target_dir, verbose=1) + wanted = ['moving %s -> %s' % (self.source, self.target_dir)] + self.assertEqual(self._logs, wanted) + + def test_move_file_exception_unpacking_rename(self): + # see issue 22182 + with patch("os.rename", side_effect=OSError("wrong", 1)), \ + self.assertRaises(DistutilsFileError): + with open(self.source, 'w') as fobj: + fobj.write('spam eggs') + move_file(self.source, self.target, verbose=0) + + def test_move_file_exception_unpacking_unlink(self): + # see issue 22182 + with patch("os.rename", side_effect=OSError(errno.EXDEV, "wrong")), \ + patch("os.unlink", side_effect=OSError("wrong", 1)), \ + self.assertRaises(DistutilsFileError): + with open(self.source, 'w') as fobj: + fobj.write('spam eggs') + move_file(self.source, self.target, verbose=0) + + def test_copy_file_hard_link(self): + with open(self.source, 'w') as f: + f.write('some content') + # Check first that copy_file() will not fall back on copying the file + # instead of creating the hard link. + try: + os.link(self.source, self.target) + except OSError as e: + self.skipTest('os.link: %s' % e) + else: + unlink(self.target) + st = os.stat(self.source) + copy_file(self.source, self.target, link='hard') + st2 = os.stat(self.source) + st3 = os.stat(self.target) + self.assertTrue(os.path.samestat(st, st2), (st, st2)) + self.assertTrue(os.path.samestat(st2, st3), (st2, st3)) + with open(self.source, 'r') as f: + self.assertEqual(f.read(), 'some content') + + def test_copy_file_hard_link_failure(self): + # If hard linking fails, copy_file() falls back on copying file + # (some special filesystems don't support hard linking even under + # Unix, see issue #8876). + with open(self.source, 'w') as f: + f.write('some content') + st = os.stat(self.source) + with patch("os.link", side_effect=OSError(0, "linking unsupported")): + copy_file(self.source, self.target, link='hard') + st2 = os.stat(self.source) + st3 = os.stat(self.target) + self.assertTrue(os.path.samestat(st, st2), (st, st2)) + self.assertFalse(os.path.samestat(st2, st3), (st2, st3)) + for fn in (self.source, self.target): + with open(fn, 'r') as f: + self.assertEqual(f.read(), 'some content') + + +def test_suite(): + return unittest.makeSuite(FileUtilTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_filelist.py b/setuptools/_distutils/tests/test_filelist.py new file mode 100644 index 00000000..71fde2b7 --- /dev/null +++ b/setuptools/_distutils/tests/test_filelist.py @@ -0,0 +1,343 @@ +"""Tests for distutils.filelist.""" +import os +import re +import unittest +from distutils import debug +from distutils.log import WARN +from distutils.errors import DistutilsTemplateError +from distutils.filelist import glob_to_re, translate_pattern, FileList +from distutils import filelist + +import test.support +from test.support import captured_stdout, run_unittest +from distutils.tests import support + +from .py35compat import adapt_glob + + +MANIFEST_IN = """\ +include ok +include xo +exclude xo +include foo.tmp +include buildout.cfg +global-include *.x +global-include *.txt +global-exclude *.tmp +recursive-include f *.oo +recursive-exclude global *.x +graft dir +prune dir3 +""" + + +def make_local_path(s): + """Converts '/' in a string to os.sep""" + return s.replace('/', os.sep) + + +class FileListTestCase(support.LoggingSilencer, + unittest.TestCase): + + def assertNoWarnings(self): + self.assertEqual(self.get_logs(WARN), []) + self.clear_logs() + + def assertWarnings(self): + self.assertGreater(len(self.get_logs(WARN)), 0) + self.clear_logs() + + def test_glob_to_re(self): + sep = os.sep + if os.sep == '\\': + sep = re.escape(os.sep) + + for glob, regex in ( + # simple cases + ('foo*', r'(?s:foo[^%(sep)s]*)\Z'), + ('foo?', r'(?s:foo[^%(sep)s])\Z'), + ('foo??', r'(?s:foo[^%(sep)s][^%(sep)s])\Z'), + # special cases + (r'foo\\*', r'(?s:foo\\\\[^%(sep)s]*)\Z'), + (r'foo\\\*', r'(?s:foo\\\\\\[^%(sep)s]*)\Z'), + ('foo????', r'(?s:foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s])\Z'), + (r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z')): + regex = regex % {'sep': sep} + self.assertEqual(glob_to_re(glob), adapt_glob(regex)) + + def test_process_template_line(self): + # testing all MANIFEST.in template patterns + file_list = FileList() + l = make_local_path + + # simulated file list + file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt', + 'buildout.cfg', + # filelist does not filter out VCS directories, + # it's sdist that does + l('.hg/last-message.txt'), + l('global/one.txt'), + l('global/two.txt'), + l('global/files.x'), + l('global/here.tmp'), + l('f/o/f.oo'), + l('dir/graft-one'), + l('dir/dir2/graft2'), + l('dir3/ok'), + l('dir3/sub/ok.txt'), + ] + + for line in MANIFEST_IN.split('\n'): + if line.strip() == '': + continue + file_list.process_template_line(line) + + wanted = ['ok', + 'buildout.cfg', + 'four.txt', + l('.hg/last-message.txt'), + l('global/one.txt'), + l('global/two.txt'), + l('f/o/f.oo'), + l('dir/graft-one'), + l('dir/dir2/graft2'), + ] + + self.assertEqual(file_list.files, wanted) + + def test_debug_print(self): + file_list = FileList() + with captured_stdout() as stdout: + file_list.debug_print('xxx') + self.assertEqual(stdout.getvalue(), '') + + debug.DEBUG = True + try: + with captured_stdout() as stdout: + file_list.debug_print('xxx') + self.assertEqual(stdout.getvalue(), 'xxx\n') + finally: + debug.DEBUG = False + + def test_set_allfiles(self): + file_list = FileList() + files = ['a', 'b', 'c'] + file_list.set_allfiles(files) + self.assertEqual(file_list.allfiles, files) + + def test_remove_duplicates(self): + file_list = FileList() + file_list.files = ['a', 'b', 'a', 'g', 'c', 'g'] + # files must be sorted beforehand (sdist does it) + file_list.sort() + file_list.remove_duplicates() + self.assertEqual(file_list.files, ['a', 'b', 'c', 'g']) + + def test_translate_pattern(self): + # not regex + self.assertTrue(hasattr( + translate_pattern('a', anchor=True, is_regex=False), + 'search')) + + # is a regex + regex = re.compile('a') + self.assertEqual( + translate_pattern(regex, anchor=True, is_regex=True), + regex) + + # plain string flagged as regex + self.assertTrue(hasattr( + translate_pattern('a', anchor=True, is_regex=True), + 'search')) + + # glob support + self.assertTrue(translate_pattern( + '*.py', anchor=True, is_regex=False).search('filelist.py')) + + def test_exclude_pattern(self): + # return False if no match + file_list = FileList() + self.assertFalse(file_list.exclude_pattern('*.py')) + + # return True if files match + file_list = FileList() + file_list.files = ['a.py', 'b.py'] + self.assertTrue(file_list.exclude_pattern('*.py')) + + # test excludes + file_list = FileList() + file_list.files = ['a.py', 'a.txt'] + file_list.exclude_pattern('*.py') + self.assertEqual(file_list.files, ['a.txt']) + + def test_include_pattern(self): + # return False if no match + file_list = FileList() + file_list.set_allfiles([]) + self.assertFalse(file_list.include_pattern('*.py')) + + # return True if files match + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt']) + self.assertTrue(file_list.include_pattern('*.py')) + + # test * matches all files + file_list = FileList() + self.assertIsNone(file_list.allfiles) + file_list.set_allfiles(['a.py', 'b.txt']) + file_list.include_pattern('*') + self.assertEqual(file_list.allfiles, ['a.py', 'b.txt']) + + def test_process_template(self): + l = make_local_path + # invalid lines + file_list = FileList() + for action in ('include', 'exclude', 'global-include', + 'global-exclude', 'recursive-include', + 'recursive-exclude', 'graft', 'prune', 'blarg'): + self.assertRaises(DistutilsTemplateError, + file_list.process_template_line, action) + + # include + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) + + file_list.process_template_line('include *.py') + self.assertEqual(file_list.files, ['a.py']) + self.assertNoWarnings() + + file_list.process_template_line('include *.rb') + self.assertEqual(file_list.files, ['a.py']) + self.assertWarnings() + + # exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', l('d/c.py')] + + file_list.process_template_line('exclude *.py') + self.assertEqual(file_list.files, ['b.txt', l('d/c.py')]) + self.assertNoWarnings() + + file_list.process_template_line('exclude *.rb') + self.assertEqual(file_list.files, ['b.txt', l('d/c.py')]) + self.assertWarnings() + + # global-include + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) + + file_list.process_template_line('global-include *.py') + self.assertEqual(file_list.files, ['a.py', l('d/c.py')]) + self.assertNoWarnings() + + file_list.process_template_line('global-include *.rb') + self.assertEqual(file_list.files, ['a.py', l('d/c.py')]) + self.assertWarnings() + + # global-exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', l('d/c.py')] + + file_list.process_template_line('global-exclude *.py') + self.assertEqual(file_list.files, ['b.txt']) + self.assertNoWarnings() + + file_list.process_template_line('global-exclude *.rb') + self.assertEqual(file_list.files, ['b.txt']) + self.assertWarnings() + + # recursive-include + file_list = FileList() + file_list.set_allfiles(['a.py', l('d/b.py'), l('d/c.txt'), + l('d/d/e.py')]) + + file_list.process_template_line('recursive-include d *.py') + self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) + self.assertNoWarnings() + + file_list.process_template_line('recursive-include e *.py') + self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) + self.assertWarnings() + + # recursive-exclude + file_list = FileList() + file_list.files = ['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')] + + file_list.process_template_line('recursive-exclude d *.py') + self.assertEqual(file_list.files, ['a.py', l('d/c.txt')]) + self.assertNoWarnings() + + file_list.process_template_line('recursive-exclude e *.py') + self.assertEqual(file_list.files, ['a.py', l('d/c.txt')]) + self.assertWarnings() + + # graft + file_list = FileList() + file_list.set_allfiles(['a.py', l('d/b.py'), l('d/d/e.py'), + l('f/f.py')]) + + file_list.process_template_line('graft d') + self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) + self.assertNoWarnings() + + file_list.process_template_line('graft e') + self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) + self.assertWarnings() + + # prune + file_list = FileList() + file_list.files = ['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')] + + file_list.process_template_line('prune d') + self.assertEqual(file_list.files, ['a.py', l('f/f.py')]) + self.assertNoWarnings() + + file_list.process_template_line('prune e') + self.assertEqual(file_list.files, ['a.py', l('f/f.py')]) + self.assertWarnings() + + +class FindAllTestCase(unittest.TestCase): + @test.support.skip_unless_symlink + def test_missing_symlink(self): + with test.support.temp_cwd(): + os.symlink('foo', 'bar') + self.assertEqual(filelist.findall(), []) + + def test_basic_discovery(self): + """ + When findall is called with no parameters or with + '.' as the parameter, the dot should be omitted from + the results. + """ + with test.support.temp_cwd(): + os.mkdir('foo') + file1 = os.path.join('foo', 'file1.txt') + test.support.create_empty_file(file1) + os.mkdir('bar') + file2 = os.path.join('bar', 'file2.txt') + test.support.create_empty_file(file2) + expected = [file2, file1] + self.assertEqual(sorted(filelist.findall()), expected) + + def test_non_local_discovery(self): + """ + When findall is called with another path, the full + path name should be returned. + """ + with test.support.temp_dir() as temp_dir: + file1 = os.path.join(temp_dir, 'file1.txt') + test.support.create_empty_file(file1) + expected = [file1] + self.assertEqual(filelist.findall(temp_dir), expected) + + +def test_suite(): + return unittest.TestSuite([ + unittest.makeSuite(FileListTestCase), + unittest.makeSuite(FindAllTestCase), + ]) + + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_install.py b/setuptools/_distutils/tests/test_install.py new file mode 100644 index 00000000..eb684a09 --- /dev/null +++ b/setuptools/_distutils/tests/test_install.py @@ -0,0 +1,250 @@ +"""Tests for distutils.command.install.""" + +import os +import sys +import unittest +import site + +from test.support import captured_stdout, run_unittest + +from distutils import sysconfig +from distutils.command.install import install +from distutils.command import install as install_module +from distutils.command.build_ext import build_ext +from distutils.command.install import INSTALL_SCHEMES +from distutils.core import Distribution +from distutils.errors import DistutilsOptionError +from distutils.extension import Extension + +from distutils.tests import support +from test import support as test_support + + +def _make_ext_name(modname): + return modname + sysconfig.get_config_var('EXT_SUFFIX') + + +class InstallTestCase(support.TempdirManager, + support.EnvironGuard, + support.LoggingSilencer, + unittest.TestCase): + + def test_home_installation_scheme(self): + # This ensure two things: + # - that --home generates the desired set of directory names + # - test --home is supported on all platforms + builddir = self.mkdtemp() + destination = os.path.join(builddir, "installation") + + dist = Distribution({"name": "foopkg"}) + # script_name need not exist, it just need to be initialized + dist.script_name = os.path.join(builddir, "setup.py") + dist.command_obj["build"] = support.DummyCommand( + build_base=builddir, + build_lib=os.path.join(builddir, "lib"), + ) + + cmd = install(dist) + cmd.home = destination + cmd.ensure_finalized() + + self.assertEqual(cmd.install_base, destination) + self.assertEqual(cmd.install_platbase, destination) + + def check_path(got, expected): + got = os.path.normpath(got) + expected = os.path.normpath(expected) + self.assertEqual(got, expected) + + libdir = os.path.join(destination, "lib", "python") + check_path(cmd.install_lib, libdir) + _platlibdir = getattr(sys, "platlibdir", "lib") + platlibdir = os.path.join(destination, _platlibdir, "python") + check_path(cmd.install_platlib, platlibdir) + check_path(cmd.install_purelib, libdir) + check_path(cmd.install_headers, + os.path.join(destination, "include", "python", "foopkg")) + check_path(cmd.install_scripts, os.path.join(destination, "bin")) + check_path(cmd.install_data, destination) + + def test_user_site(self): + # test install with --user + # preparing the environment for the test + self.old_user_base = site.USER_BASE + self.old_user_site = site.USER_SITE + self.tmpdir = self.mkdtemp() + self.user_base = os.path.join(self.tmpdir, 'B') + self.user_site = os.path.join(self.tmpdir, 'S') + site.USER_BASE = self.user_base + site.USER_SITE = self.user_site + install_module.USER_BASE = self.user_base + install_module.USER_SITE = self.user_site + + def _expanduser(path): + return self.tmpdir + self.old_expand = os.path.expanduser + os.path.expanduser = _expanduser + + def cleanup(): + site.USER_BASE = self.old_user_base + site.USER_SITE = self.old_user_site + install_module.USER_BASE = self.old_user_base + install_module.USER_SITE = self.old_user_site + os.path.expanduser = self.old_expand + + self.addCleanup(cleanup) + + for key in ('nt_user', 'unix_user'): + self.assertIn(key, INSTALL_SCHEMES) + + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # making sure the user option is there + options = [name for name, short, lable in + cmd.user_options] + self.assertIn('user', options) + + # setting a value + cmd.user = 1 + + # user base and site shouldn't be created yet + self.assertFalse(os.path.exists(self.user_base)) + self.assertFalse(os.path.exists(self.user_site)) + + # let's run finalize + cmd.ensure_finalized() + + # now they should + self.assertTrue(os.path.exists(self.user_base)) + self.assertTrue(os.path.exists(self.user_site)) + + self.assertIn('userbase', cmd.config_vars) + self.assertIn('usersite', cmd.config_vars) + + def test_handle_extra_path(self): + dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) + cmd = install(dist) + + # two elements + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, ['path', 'dirs']) + self.assertEqual(cmd.extra_dirs, 'dirs') + self.assertEqual(cmd.path_file, 'path') + + # one element + cmd.extra_path = ['path'] + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, ['path']) + self.assertEqual(cmd.extra_dirs, 'path') + self.assertEqual(cmd.path_file, 'path') + + # none + dist.extra_path = cmd.extra_path = None + cmd.handle_extra_path() + self.assertEqual(cmd.extra_path, None) + self.assertEqual(cmd.extra_dirs, '') + self.assertEqual(cmd.path_file, None) + + # three elements (no way !) + cmd.extra_path = 'path,dirs,again' + self.assertRaises(DistutilsOptionError, cmd.handle_extra_path) + + def test_finalize_options(self): + dist = Distribution({'name': 'xx'}) + cmd = install(dist) + + # must supply either prefix/exec-prefix/home or + # install-base/install-platbase -- not both + cmd.prefix = 'prefix' + cmd.install_base = 'base' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # must supply either home or prefix/exec-prefix -- not both + cmd.install_base = None + cmd.home = 'home' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # can't combine user with prefix/exec_prefix/home or + # install_(plat)base + cmd.prefix = None + cmd.user = 'user' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + def test_record(self): + install_dir = self.mkdtemp() + project_dir, dist = self.create_dist(py_modules=['hello'], + scripts=['sayhi']) + os.chdir(project_dir) + self.write_file('hello.py', "def main(): print('o hai')") + self.write_file('sayhi', 'from hello import main; main()') + + cmd = install(dist) + dist.command_obj['install'] = cmd + cmd.root = install_dir + cmd.record = os.path.join(project_dir, 'filelist') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.record) + try: + content = f.read() + finally: + f.close() + + found = [os.path.basename(line) for line in content.splitlines()] + expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag, + 'sayhi', + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + + def test_record_extensions(self): + cmd = test_support.missing_compiler_executable() + if cmd is not None: + self.skipTest('The %r command is not found' % cmd) + install_dir = self.mkdtemp() + project_dir, dist = self.create_dist(ext_modules=[ + Extension('xx', ['xxmodule.c'])]) + os.chdir(project_dir) + support.copy_xxmodule_c(project_dir) + + buildextcmd = build_ext(dist) + support.fixup_build_ext(buildextcmd) + buildextcmd.ensure_finalized() + + cmd = install(dist) + dist.command_obj['install'] = cmd + dist.command_obj['build_ext'] = buildextcmd + cmd.root = install_dir + cmd.record = os.path.join(project_dir, 'filelist') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.record) + try: + content = f.read() + finally: + f.close() + + found = [os.path.basename(line) for line in content.splitlines()] + expected = [_make_ext_name('xx'), + 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] + self.assertEqual(found, expected) + + def test_debug_mode(self): + # this covers the code called when DEBUG is set + old_logs_len = len(self.logs) + install_module.DEBUG = True + try: + with captured_stdout(): + self.test_record() + finally: + install_module.DEBUG = False + self.assertGreater(len(self.logs), old_logs_len) + + +def test_suite(): + return unittest.makeSuite(InstallTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_install_data.py b/setuptools/_distutils/tests/test_install_data.py new file mode 100644 index 00000000..32ab296a --- /dev/null +++ b/setuptools/_distutils/tests/test_install_data.py @@ -0,0 +1,75 @@ +"""Tests for distutils.command.install_data.""" +import os +import unittest + +from distutils.command.install_data import install_data +from distutils.tests import support +from test.support import run_unittest + +class InstallDataTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + def test_simple_run(self): + pkg_dir, dist = self.create_dist() + cmd = install_data(dist) + cmd.install_dir = inst = os.path.join(pkg_dir, 'inst') + + # data_files can contain + # - simple files + # - a tuple with a path, and a list of file + one = os.path.join(pkg_dir, 'one') + self.write_file(one, 'xxx') + inst2 = os.path.join(pkg_dir, 'inst2') + two = os.path.join(pkg_dir, 'two') + self.write_file(two, 'xxx') + + cmd.data_files = [one, (inst2, [two])] + self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])]) + + # let's run the command + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEqual(len(cmd.get_outputs()), 2) + rtwo = os.path.split(two)[-1] + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + rone = os.path.split(one)[-1] + self.assertTrue(os.path.exists(os.path.join(inst, rone))) + cmd.outfiles = [] + + # let's try with warn_dir one + cmd.warn_dir = 1 + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEqual(len(cmd.get_outputs()), 2) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) + cmd.outfiles = [] + + # now using root and empty dir + cmd.root = os.path.join(pkg_dir, 'root') + inst3 = os.path.join(cmd.install_dir, 'inst3') + inst4 = os.path.join(pkg_dir, 'inst4') + three = os.path.join(cmd.install_dir, 'three') + self.write_file(three, 'xx') + cmd.data_files = [one, (inst2, [two]), + ('inst3', [three]), + (inst4, [])] + cmd.ensure_finalized() + cmd.run() + + # let's check the result + self.assertEqual(len(cmd.get_outputs()), 4) + self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) + self.assertTrue(os.path.exists(os.path.join(inst, rone))) + +def test_suite(): + return unittest.makeSuite(InstallDataTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_install_headers.py b/setuptools/_distutils/tests/test_install_headers.py new file mode 100644 index 00000000..2217b321 --- /dev/null +++ b/setuptools/_distutils/tests/test_install_headers.py @@ -0,0 +1,39 @@ +"""Tests for distutils.command.install_headers.""" +import os +import unittest + +from distutils.command.install_headers import install_headers +from distutils.tests import support +from test.support import run_unittest + +class InstallHeadersTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + def test_simple_run(self): + # we have two headers + header_list = self.mkdtemp() + header1 = os.path.join(header_list, 'header1') + header2 = os.path.join(header_list, 'header2') + self.write_file(header1) + self.write_file(header2) + headers = [header1, header2] + + pkg_dir, dist = self.create_dist(headers=headers) + cmd = install_headers(dist) + self.assertEqual(cmd.get_inputs(), headers) + + # let's run the command + cmd.install_dir = os.path.join(pkg_dir, 'inst') + cmd.ensure_finalized() + cmd.run() + + # let's check the results + self.assertEqual(len(cmd.get_outputs()), 2) + +def test_suite(): + return unittest.makeSuite(InstallHeadersTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_install_lib.py b/setuptools/_distutils/tests/test_install_lib.py new file mode 100644 index 00000000..fda6315b --- /dev/null +++ b/setuptools/_distutils/tests/test_install_lib.py @@ -0,0 +1,115 @@ +"""Tests for distutils.command.install_data.""" +import sys +import os +import importlib.util +import unittest + +from distutils.command.install_lib import install_lib +from distutils.extension import Extension +from distutils.tests import support +from distutils.errors import DistutilsOptionError +from test.support import run_unittest + + +class InstallLibTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + def test_finalize_options(self): + dist = self.create_dist()[1] + cmd = install_lib(dist) + + cmd.finalize_options() + self.assertEqual(cmd.compile, 1) + self.assertEqual(cmd.optimize, 0) + + # optimize must be 0, 1, or 2 + cmd.optimize = 'foo' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + cmd.optimize = '4' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + cmd.optimize = '2' + cmd.finalize_options() + self.assertEqual(cmd.optimize, 2) + + @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') + def test_byte_compile(self): + project_dir, dist = self.create_dist() + os.chdir(project_dir) + cmd = install_lib(dist) + cmd.compile = cmd.optimize = 1 + + f = os.path.join(project_dir, 'foo.py') + self.write_file(f, '# python file') + cmd.byte_compile([f]) + pyc_file = importlib.util.cache_from_source('foo.py', optimization='') + pyc_opt_file = importlib.util.cache_from_source('foo.py', + optimization=cmd.optimize) + self.assertTrue(os.path.exists(pyc_file)) + self.assertTrue(os.path.exists(pyc_opt_file)) + + def test_get_outputs(self): + project_dir, dist = self.create_dist() + os.chdir(project_dir) + os.mkdir('spam') + cmd = install_lib(dist) + + # setting up a dist environment + cmd.compile = cmd.optimize = 1 + cmd.install_dir = self.mkdtemp() + f = os.path.join(project_dir, 'spam', '__init__.py') + self.write_file(f, '# python package') + cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] + cmd.distribution.packages = ['spam'] + cmd.distribution.script_name = 'setup.py' + + # get_outputs should return 4 elements: spam/__init__.py and .pyc, + # foo.import-tag-abiflags.so / foo.pyd + outputs = cmd.get_outputs() + self.assertEqual(len(outputs), 4, outputs) + + def test_get_inputs(self): + project_dir, dist = self.create_dist() + os.chdir(project_dir) + os.mkdir('spam') + cmd = install_lib(dist) + + # setting up a dist environment + cmd.compile = cmd.optimize = 1 + cmd.install_dir = self.mkdtemp() + f = os.path.join(project_dir, 'spam', '__init__.py') + self.write_file(f, '# python package') + cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] + cmd.distribution.packages = ['spam'] + cmd.distribution.script_name = 'setup.py' + + # get_inputs should return 2 elements: spam/__init__.py and + # foo.import-tag-abiflags.so / foo.pyd + inputs = cmd.get_inputs() + self.assertEqual(len(inputs), 2, inputs) + + def test_dont_write_bytecode(self): + # makes sure byte_compile is not used + dist = self.create_dist()[1] + cmd = install_lib(dist) + cmd.compile = 1 + cmd.optimize = 1 + + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + cmd.byte_compile([]) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + self.assertIn('byte-compiling is disabled', + self.logs[0][1] % self.logs[0][2]) + + +def test_suite(): + return unittest.makeSuite(InstallLibTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_install_scripts.py b/setuptools/_distutils/tests/test_install_scripts.py new file mode 100644 index 00000000..1f7b1038 --- /dev/null +++ b/setuptools/_distutils/tests/test_install_scripts.py @@ -0,0 +1,82 @@ +"""Tests for distutils.command.install_scripts.""" + +import os +import unittest + +from distutils.command.install_scripts import install_scripts +from distutils.core import Distribution + +from distutils.tests import support +from test.support import run_unittest + + +class InstallScriptsTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + def test_default_settings(self): + dist = Distribution() + dist.command_obj["build"] = support.DummyCommand( + build_scripts="/foo/bar") + dist.command_obj["install"] = support.DummyCommand( + install_scripts="/splat/funk", + force=1, + skip_build=1, + ) + cmd = install_scripts(dist) + self.assertFalse(cmd.force) + self.assertFalse(cmd.skip_build) + self.assertIsNone(cmd.build_dir) + self.assertIsNone(cmd.install_dir) + + cmd.finalize_options() + + self.assertTrue(cmd.force) + self.assertTrue(cmd.skip_build) + self.assertEqual(cmd.build_dir, "/foo/bar") + self.assertEqual(cmd.install_dir, "/splat/funk") + + def test_installation(self): + source = self.mkdtemp() + expected = [] + + def write_script(name, text): + expected.append(name) + f = open(os.path.join(source, name), "w") + try: + f.write(text) + finally: + f.close() + + write_script("script1.py", ("#! /usr/bin/env python2.3\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + write_script("script2.py", ("#!/usr/bin/python\n" + "# bogus script w/ Python sh-bang\n" + "pass\n")) + write_script("shell.sh", ("#!/bin/sh\n" + "# bogus shell script w/ sh-bang\n" + "exit 0\n")) + + target = self.mkdtemp() + dist = Distribution() + dist.command_obj["build"] = support.DummyCommand(build_scripts=source) + dist.command_obj["install"] = support.DummyCommand( + install_scripts=target, + force=1, + skip_build=1, + ) + cmd = install_scripts(dist) + cmd.finalize_options() + cmd.run() + + installed = os.listdir(target) + for name in expected: + self.assertIn(name, installed) + + +def test_suite(): + return unittest.makeSuite(InstallScriptsTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_log.py b/setuptools/_distutils/tests/test_log.py new file mode 100644 index 00000000..75cf9006 --- /dev/null +++ b/setuptools/_distutils/tests/test_log.py @@ -0,0 +1,46 @@ +"""Tests for distutils.log""" + +import io +import sys +import unittest +from test.support import swap_attr, run_unittest + +from distutils import log + +class TestLog(unittest.TestCase): + def test_non_ascii(self): + # Issues #8663, #34421: test that non-encodable text is escaped with + # backslashreplace error handler and encodable non-ASCII text is + # output as is. + for errors in ('strict', 'backslashreplace', 'surrogateescape', + 'replace', 'ignore'): + with self.subTest(errors=errors): + stdout = io.TextIOWrapper(io.BytesIO(), + encoding='cp437', errors=errors) + stderr = io.TextIOWrapper(io.BytesIO(), + encoding='cp437', errors=errors) + old_threshold = log.set_threshold(log.DEBUG) + try: + with swap_attr(sys, 'stdout', stdout), \ + swap_attr(sys, 'stderr', stderr): + log.debug('Dεbug\tMÄ—ssãge') + log.fatal('Fαtal\tÈrrÅr') + finally: + log.set_threshold(old_threshold) + + stdout.seek(0) + self.assertEqual(stdout.read().rstrip(), + 'Dεbug\tM?ss?ge' if errors == 'replace' else + 'Dεbug\tMssge' if errors == 'ignore' else + 'Dεbug\tM\\u0117ss\\xe3ge') + stderr.seek(0) + self.assertEqual(stderr.read().rstrip(), + 'Fαtal\t?rr?r' if errors == 'replace' else + 'Fαtal\trrr' if errors == 'ignore' else + 'Fαtal\t\\xc8rr\\u014dr') + +def test_suite(): + return unittest.makeSuite(TestLog) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_msvc9compiler.py b/setuptools/_distutils/tests/test_msvc9compiler.py new file mode 100644 index 00000000..77a07ef3 --- /dev/null +++ b/setuptools/_distutils/tests/test_msvc9compiler.py @@ -0,0 +1,184 @@ +"""Tests for distutils.msvc9compiler.""" +import sys +import unittest +import os + +from distutils.errors import DistutilsPlatformError +from distutils.tests import support +from test.support import run_unittest + +# A manifest with the only assembly reference being the msvcrt assembly, so +# should have the assembly completely stripped. Note that although the +# assembly has a reference the assembly is removed - that is +# currently a "feature", not a bug :) +_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\ + + + + + + + + + + + + + + + + + +""" + +# A manifest with references to assemblies other than msvcrt. When processed, +# this assembly should be returned with just the msvcrt part removed. +_MANIFEST_WITH_MULTIPLE_REFERENCES = """\ + + + + + + + + + + + + + + + + + + + + + + +""" + +_CLEANED_MANIFEST = """\ + + + + + + + + + + + + + + + + + + +""" + +if sys.platform=="win32": + from distutils.msvccompiler import get_build_version + if get_build_version()>=8.0: + SKIP_MESSAGE = None + else: + SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" +else: + SKIP_MESSAGE = "These tests are only for win32" + +@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) +class msvc9compilerTestCase(support.TempdirManager, + unittest.TestCase): + + def test_no_compiler(self): + # makes sure query_vcvarsall raises + # a DistutilsPlatformError if the compiler + # is not found + from distutils.msvc9compiler import query_vcvarsall + def _find_vcvarsall(version): + return None + + from distutils import msvc9compiler + old_find_vcvarsall = msvc9compiler.find_vcvarsall + msvc9compiler.find_vcvarsall = _find_vcvarsall + try: + self.assertRaises(DistutilsPlatformError, query_vcvarsall, + 'wont find this version') + finally: + msvc9compiler.find_vcvarsall = old_find_vcvarsall + + def test_reg_class(self): + from distutils.msvc9compiler import Reg + self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') + + # looking for values that should exist on all + # windows registry versions. + path = r'Control Panel\Desktop' + v = Reg.get_value(path, 'dragfullwindows') + self.assertIn(v, ('0', '1', '2')) + + import winreg + HKCU = winreg.HKEY_CURRENT_USER + keys = Reg.read_keys(HKCU, 'xxxx') + self.assertEqual(keys, None) + + keys = Reg.read_keys(HKCU, r'Control Panel') + self.assertIn('Desktop', keys) + + def test_remove_visual_c_ref(self): + from distutils.msvc9compiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + try: + f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES) + finally: + f.close() + + compiler = MSVCCompiler() + compiler._remove_visual_c_ref(manifest) + + # see what we got + f = open(manifest) + try: + # removing trailing spaces + content = '\n'.join([line.rstrip() for line in f.readlines()]) + finally: + f.close() + + # makes sure the manifest was properly cleaned + self.assertEqual(content, _CLEANED_MANIFEST) + + def test_remove_entire_manifest(self): + from distutils.msvc9compiler import MSVCCompiler + tempdir = self.mkdtemp() + manifest = os.path.join(tempdir, 'manifest') + f = open(manifest, 'w') + try: + f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE) + finally: + f.close() + + compiler = MSVCCompiler() + got = compiler._remove_visual_c_ref(manifest) + self.assertIsNone(got) + + +def test_suite(): + return unittest.makeSuite(msvc9compilerTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_msvccompiler.py b/setuptools/_distutils/tests/test_msvccompiler.py new file mode 100644 index 00000000..b518d6a7 --- /dev/null +++ b/setuptools/_distutils/tests/test_msvccompiler.py @@ -0,0 +1,81 @@ +"""Tests for distutils._msvccompiler.""" +import sys +import unittest +import os + +from distutils.errors import DistutilsPlatformError +from distutils.tests import support +from test.support import run_unittest + + +SKIP_MESSAGE = (None if sys.platform == "win32" else + "These tests are only for win32") + +@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) +class msvccompilerTestCase(support.TempdirManager, + unittest.TestCase): + + def test_no_compiler(self): + import distutils._msvccompiler as _msvccompiler + # makes sure query_vcvarsall raises + # a DistutilsPlatformError if the compiler + # is not found + def _find_vcvarsall(plat_spec): + return None, None + + old_find_vcvarsall = _msvccompiler._find_vcvarsall + _msvccompiler._find_vcvarsall = _find_vcvarsall + try: + self.assertRaises(DistutilsPlatformError, + _msvccompiler._get_vc_env, + 'wont find this version') + finally: + _msvccompiler._find_vcvarsall = old_find_vcvarsall + + def test_get_vc_env_unicode(self): + import distutils._msvccompiler as _msvccompiler + + test_var = 'ṰḖṤṪ┅ṼẨṜ' + test_value = '₃â´â‚…' + + # Ensure we don't early exit from _get_vc_env + old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None) + os.environ[test_var] = test_value + try: + env = _msvccompiler._get_vc_env('x86') + self.assertIn(test_var.lower(), env) + self.assertEqual(test_value, env[test_var.lower()]) + finally: + os.environ.pop(test_var) + if old_distutils_use_sdk: + os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk + + def test_get_vc2017(self): + import distutils._msvccompiler as _msvccompiler + + # This function cannot be mocked, so pass it if we find VS 2017 + # and mark it skipped if we do not. + version, path = _msvccompiler._find_vc2017() + if version: + self.assertGreaterEqual(version, 15) + self.assertTrue(os.path.isdir(path)) + else: + raise unittest.SkipTest("VS 2017 is not installed") + + def test_get_vc2015(self): + import distutils._msvccompiler as _msvccompiler + + # This function cannot be mocked, so pass it if we find VS 2015 + # and mark it skipped if we do not. + version, path = _msvccompiler._find_vc2015() + if version: + self.assertGreaterEqual(version, 14) + self.assertTrue(os.path.isdir(path)) + else: + raise unittest.SkipTest("VS 2015 is not installed") + +def test_suite(): + return unittest.makeSuite(msvccompilerTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_register.py b/setuptools/_distutils/tests/test_register.py new file mode 100644 index 00000000..e68b0af3 --- /dev/null +++ b/setuptools/_distutils/tests/test_register.py @@ -0,0 +1,323 @@ +"""Tests for distutils.command.register.""" +import os +import unittest +import getpass +import urllib +import warnings + +from test.support import check_warnings, run_unittest + +from distutils.command import register as register_module +from distutils.command.register import register +from distutils.errors import DistutilsSetupError +from distutils.log import INFO + +from distutils.tests.test_config import BasePyPIRCCommandTestCase + +try: + import docutils +except ImportError: + docutils = None + +PYPIRC_NOPASSWORD = """\ +[distutils] + +index-servers = + server1 + +[server1] +username:me +""" + +WANTED_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:password +""" + +class Inputs(object): + """Fakes user inputs.""" + def __init__(self, *answers): + self.answers = answers + self.index = 0 + + def __call__(self, prompt=''): + try: + return self.answers[self.index] + finally: + self.index += 1 + +class FakeOpener(object): + """Fakes a PyPI server""" + def __init__(self): + self.reqs = [] + + def __call__(self, *args): + return self + + def open(self, req, data=None, timeout=None): + self.reqs.append(req) + return self + + def read(self): + return b'xxx' + + def getheader(self, name, default=None): + return { + 'content-type': 'text/plain; charset=utf-8', + }.get(name.lower(), default) + + +class RegisterTestCase(BasePyPIRCCommandTestCase): + + def setUp(self): + super(RegisterTestCase, self).setUp() + # patching the password prompt + self._old_getpass = getpass.getpass + def _getpass(prompt): + return 'password' + getpass.getpass = _getpass + urllib.request._opener = None + self.old_opener = urllib.request.build_opener + self.conn = urllib.request.build_opener = FakeOpener() + + def tearDown(self): + getpass.getpass = self._old_getpass + urllib.request._opener = None + urllib.request.build_opener = self.old_opener + super(RegisterTestCase, self).tearDown() + + def _get_cmd(self, metadata=None): + if metadata is None: + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx'} + pkg_info, dist = self.create_dist(**metadata) + return register(dist) + + def test_create_pypirc(self): + # this test makes sure a .pypirc file + # is created when requested. + + # let's create a register instance + cmd = self._get_cmd() + + # we shouldn't have a .pypirc file yet + self.assertFalse(os.path.exists(self.rc)) + + # patching input and getpass.getpass + # so register gets happy + # + # Here's what we are faking : + # use your existing login (choice 1.) + # Username : 'tarek' + # Password : 'password' + # Save your login (y/N)? : 'y' + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.input + + # we should have a brand new .pypirc file + self.assertTrue(os.path.exists(self.rc)) + + # with the content similar to WANTED_PYPIRC + f = open(self.rc) + try: + content = f.read() + self.assertEqual(content, WANTED_PYPIRC) + finally: + f.close() + + # now let's make sure the .pypirc file generated + # really works : we shouldn't be asked anything + # if we run the command again + def _no_way(prompt=''): + raise AssertionError(prompt) + register_module.input = _no_way + + cmd.show_response = 1 + cmd.run() + + # let's see what the server received : we should + # have 2 similar requests + self.assertEqual(len(self.conn.reqs), 2) + req1 = dict(self.conn.reqs[0].headers) + req2 = dict(self.conn.reqs[1].headers) + + self.assertEqual(req1['Content-length'], '1374') + self.assertEqual(req2['Content-length'], '1374') + self.assertIn(b'xxx', self.conn.reqs[1].data) + + def test_password_not_in_file(self): + + self.write_file(self.rc, PYPIRC_NOPASSWORD) + cmd = self._get_cmd() + cmd._set_config() + cmd.finalize_options() + cmd.send_metadata() + + # dist.password should be set + # therefore used afterwards by other commands + self.assertEqual(cmd.distribution.password, 'password') + + def test_registering(self): + # this test runs choice 2 + cmd = self._get_cmd() + inputs = Inputs('2', 'tarek', 'tarek@ziade.org') + register_module.input = inputs.__call__ + try: + # let's run the command + cmd.run() + finally: + del register_module.input + + # we should have send a request + self.assertEqual(len(self.conn.reqs), 1) + req = self.conn.reqs[0] + headers = dict(req.headers) + self.assertEqual(headers['Content-length'], '608') + self.assertIn(b'tarek', req.data) + + def test_password_reset(self): + # this test runs choice 3 + cmd = self._get_cmd() + inputs = Inputs('3', 'tarek@ziade.org') + register_module.input = inputs.__call__ + try: + # let's run the command + cmd.run() + finally: + del register_module.input + + # we should have send a request + self.assertEqual(len(self.conn.reqs), 1) + req = self.conn.reqs[0] + headers = dict(req.headers) + self.assertEqual(headers['Content-length'], '290') + self.assertIn(b'tarek', req.data) + + @unittest.skipUnless(docutils is not None, 'needs docutils') + def test_strict(self): + # testing the script option + # when on, the register command stops if + # the metadata is incomplete or if + # long_description is not reSt compliant + + # empty metadata + cmd = self._get_cmd({}) + cmd.ensure_finalized() + cmd.strict = 1 + self.assertRaises(DistutilsSetupError, cmd.run) + + # metadata are OK but long_description is broken + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'éxéxé', + 'name': 'xxx', 'version': 'xxx', + 'long_description': 'title\n==\n\ntext'} + + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + self.assertRaises(DistutilsSetupError, cmd.run) + + # now something that works + metadata['long_description'] = 'title\n=====\n\ntext' + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.input + + # strict is not by default + cmd = self._get_cmd() + cmd.ensure_finalized() + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.input + + # and finally a Unicode test (bug #12114) + metadata = {'url': 'xxx', 'author': '\u00c9ric', + 'author_email': 'xxx', 'name': 'xxx', + 'version': 'xxx', + 'description': 'Something about esszet \u00df', + 'long_description': 'More things about esszet \u00df'} + + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = 1 + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + # let's run the command + try: + cmd.run() + finally: + del register_module.input + + @unittest.skipUnless(docutils is not None, 'needs docutils') + def test_register_invalid_long_description(self): + description = ':funkie:`str`' # mimic Sphinx-specific markup + metadata = {'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx', + 'name': 'xxx', 'version': 'xxx', + 'long_description': description} + cmd = self._get_cmd(metadata) + cmd.ensure_finalized() + cmd.strict = True + inputs = Inputs('2', 'tarek', 'tarek@ziade.org') + register_module.input = inputs + self.addCleanup(delattr, register_module, 'input') + + self.assertRaises(DistutilsSetupError, cmd.run) + + def test_check_metadata_deprecated(self): + # makes sure make_metadata is deprecated + cmd = self._get_cmd() + with check_warnings() as w: + warnings.simplefilter("always") + cmd.check_metadata() + self.assertEqual(len(w.warnings), 1) + + def test_list_classifiers(self): + cmd = self._get_cmd() + cmd.list_classifiers = 1 + cmd.run() + results = self.get_logs(INFO) + self.assertEqual(results, ['running check', 'xxx']) + + def test_show_response(self): + # test that the --show-response option return a well formatted response + cmd = self._get_cmd() + inputs = Inputs('1', 'tarek', 'y') + register_module.input = inputs.__call__ + cmd.show_response = 1 + try: + cmd.run() + finally: + del register_module.input + + results = self.get_logs(INFO) + self.assertEqual(results[3], 75 * '-' + '\nxxx\n' + 75 * '-') + + +def test_suite(): + return unittest.makeSuite(RegisterTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_sdist.py b/setuptools/_distutils/tests/test_sdist.py new file mode 100644 index 00000000..23db1269 --- /dev/null +++ b/setuptools/_distutils/tests/test_sdist.py @@ -0,0 +1,492 @@ +"""Tests for distutils.command.sdist.""" +import os +import tarfile +import unittest +import warnings +import zipfile +from os.path import join +from textwrap import dedent +from test.support import captured_stdout, check_warnings, run_unittest + +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + +try: + import grp + import pwd + UID_GID_SUPPORT = True +except ImportError: + UID_GID_SUPPORT = False + +from distutils.command.sdist import sdist, show_formats +from distutils.core import Distribution +from distutils.tests.test_config import BasePyPIRCCommandTestCase +from distutils.errors import DistutilsOptionError +from distutils.spawn import find_executable +from distutils.log import WARN +from distutils.filelist import FileList +from distutils.archive_util import ARCHIVE_FORMATS + +SETUP_PY = """ +from distutils.core import setup +import somecode + +setup(name='fake') +""" + +MANIFEST = """\ +# file GENERATED by distutils, do NOT edit +README +buildout.cfg +inroot.txt +setup.py +data%(sep)sdata.dt +scripts%(sep)sscript.py +some%(sep)sfile.txt +some%(sep)sother_file.txt +somecode%(sep)s__init__.py +somecode%(sep)sdoc.dat +somecode%(sep)sdoc.txt +""" + +class SDistTestCase(BasePyPIRCCommandTestCase): + + def setUp(self): + # PyPIRCCommandTestCase creates a temp dir already + # and put it in self.tmp_dir + super(SDistTestCase, self).setUp() + # setting up an environment + self.old_path = os.getcwd() + os.mkdir(join(self.tmp_dir, 'somecode')) + os.mkdir(join(self.tmp_dir, 'dist')) + # a package, and a README + self.write_file((self.tmp_dir, 'README'), 'xxx') + self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#') + self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY) + os.chdir(self.tmp_dir) + + def tearDown(self): + # back to normal + os.chdir(self.old_path) + super(SDistTestCase, self).tearDown() + + def get_cmd(self, metadata=None): + """Returns a cmd""" + if metadata is None: + metadata = {'name': 'fake', 'version': '1.0', + 'url': 'xxx', 'author': 'xxx', + 'author_email': 'xxx'} + dist = Distribution(metadata) + dist.script_name = 'setup.py' + dist.packages = ['somecode'] + dist.include_package_data = True + cmd = sdist(dist) + cmd.dist_dir = 'dist' + return dist, cmd + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_prune_file_list(self): + # this test creates a project with some VCS dirs and an NFS rename + # file, then launches sdist to check they get pruned on all systems + + # creating VCS directories with some files in them + os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) + self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') + + os.mkdir(join(self.tmp_dir, 'somecode', '.hg')) + self.write_file((self.tmp_dir, 'somecode', '.hg', + 'ok'), 'xxx') + + os.mkdir(join(self.tmp_dir, 'somecode', '.git')) + self.write_file((self.tmp_dir, 'somecode', '.git', + 'ok'), 'xxx') + + self.write_file((self.tmp_dir, 'somecode', '.nfs0001'), 'xxx') + + # now building a sdist + dist, cmd = self.get_cmd() + + # zip is available universally + # (tar might not be installed under win32) + cmd.formats = ['zip'] + + cmd.ensure_finalized() + cmd.run() + + # now let's check what we have + dist_folder = join(self.tmp_dir, 'dist') + files = os.listdir(dist_folder) + self.assertEqual(files, ['fake-1.0.zip']) + + zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) + try: + content = zip_file.namelist() + finally: + zip_file.close() + + # making sure everything has been pruned correctly + expected = ['', 'PKG-INFO', 'README', 'setup.py', + 'somecode/', 'somecode/__init__.py'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + @unittest.skipIf(find_executable('tar') is None, + "The tar command is not found") + @unittest.skipIf(find_executable('gzip') is None, + "The gzip command is not found") + def test_make_distribution(self): + # now building a sdist + dist, cmd = self.get_cmd() + + # creating a gztar then a tar + cmd.formats = ['gztar', 'tar'] + cmd.ensure_finalized() + cmd.run() + + # making sure we have two files + dist_folder = join(self.tmp_dir, 'dist') + result = os.listdir(dist_folder) + result.sort() + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + + os.remove(join(dist_folder, 'fake-1.0.tar')) + os.remove(join(dist_folder, 'fake-1.0.tar.gz')) + + # now trying a tar then a gztar + cmd.formats = ['tar', 'gztar'] + + cmd.ensure_finalized() + cmd.run() + + result = os.listdir(dist_folder) + result.sort() + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_add_defaults(self): + + # http://bugs.python.org/issue2279 + + # add_default should also include + # data_files and package_data + dist, cmd = self.get_cmd() + + # filling data_files by pointing files + # in package_data + dist.package_data = {'': ['*.cfg', '*.dat'], + 'somecode': ['*.txt']} + self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#') + + # adding some data in data_files + data_dir = join(self.tmp_dir, 'data') + os.mkdir(data_dir) + self.write_file((data_dir, 'data.dt'), '#') + some_dir = join(self.tmp_dir, 'some') + os.mkdir(some_dir) + # make sure VCS directories are pruned (#14004) + hg_dir = join(self.tmp_dir, '.hg') + os.mkdir(hg_dir) + self.write_file((hg_dir, 'last-message.txt'), '#') + # a buggy regex used to prevent this from working on windows (#6884) + self.write_file((self.tmp_dir, 'buildout.cfg'), '#') + self.write_file((self.tmp_dir, 'inroot.txt'), '#') + self.write_file((some_dir, 'file.txt'), '#') + self.write_file((some_dir, 'other_file.txt'), '#') + + dist.data_files = [('data', ['data/data.dt', + 'buildout.cfg', + 'inroot.txt', + 'notexisting']), + 'some/file.txt', + 'some/other_file.txt'] + + # adding a script + script_dir = join(self.tmp_dir, 'scripts') + os.mkdir(script_dir) + self.write_file((script_dir, 'script.py'), '#') + dist.scripts = [join('scripts', 'script.py')] + + cmd.formats = ['zip'] + cmd.use_defaults = True + + cmd.ensure_finalized() + cmd.run() + + # now let's check what we have + dist_folder = join(self.tmp_dir, 'dist') + files = os.listdir(dist_folder) + self.assertEqual(files, ['fake-1.0.zip']) + + zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) + try: + content = zip_file.namelist() + finally: + zip_file.close() + + # making sure everything was added + expected = ['', 'PKG-INFO', 'README', 'buildout.cfg', + 'data/', 'data/data.dt', 'inroot.txt', + 'scripts/', 'scripts/script.py', 'setup.py', + 'some/', 'some/file.txt', 'some/other_file.txt', + 'somecode/', 'somecode/__init__.py', 'somecode/doc.dat', + 'somecode/doc.txt'] + self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) + + # checking the MANIFEST + f = open(join(self.tmp_dir, 'MANIFEST')) + try: + manifest = f.read() + finally: + f.close() + self.assertEqual(manifest, MANIFEST % {'sep': os.sep}) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_metadata_check_option(self): + # testing the `medata-check` option + dist, cmd = self.get_cmd(metadata={}) + + # this should raise some warnings ! + # with the `check` subcommand + cmd.ensure_finalized() + cmd.run() + warnings = [msg for msg in self.get_logs(WARN) if + msg.startswith('warning: check:')] + self.assertEqual(len(warnings), 2) + + # trying with a complete set of metadata + self.clear_logs() + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + cmd.metadata_check = 0 + cmd.run() + warnings = [msg for msg in self.get_logs(WARN) if + msg.startswith('warning: check:')] + self.assertEqual(len(warnings), 0) + + def test_check_metadata_deprecated(self): + # makes sure make_metadata is deprecated + dist, cmd = self.get_cmd() + with check_warnings() as w: + warnings.simplefilter("always") + cmd.check_metadata() + self.assertEqual(len(w.warnings), 1) + + def test_show_formats(self): + with captured_stdout() as stdout: + show_formats() + + # the output should be a header line + one line per format + num_formats = len(ARCHIVE_FORMATS.keys()) + output = [line for line in stdout.getvalue().split('\n') + if line.strip().startswith('--formats=')] + self.assertEqual(len(output), num_formats) + + def test_finalize_options(self): + dist, cmd = self.get_cmd() + cmd.finalize_options() + + # default options set by finalize + self.assertEqual(cmd.manifest, 'MANIFEST') + self.assertEqual(cmd.template, 'MANIFEST.in') + self.assertEqual(cmd.dist_dir, 'dist') + + # formats has to be a string splitable on (' ', ',') or + # a stringlist + cmd.formats = 1 + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + cmd.formats = ['zip'] + cmd.finalize_options() + + # formats has to be known + cmd.formats = 'supazipa' + self.assertRaises(DistutilsOptionError, cmd.finalize_options) + + # the following tests make sure there is a nice error message instead + # of a traceback when parsing an invalid manifest template + + def _check_template(self, content): + dist, cmd = self.get_cmd() + os.chdir(self.tmp_dir) + self.write_file('MANIFEST.in', content) + cmd.ensure_finalized() + cmd.filelist = FileList() + cmd.read_template() + warnings = self.get_logs(WARN) + self.assertEqual(len(warnings), 1) + + def test_invalid_template_unknown_command(self): + self._check_template('taunt knights *') + + def test_invalid_template_wrong_arguments(self): + # this manifest command takes one argument + self._check_template('prune') + + @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only') + def test_invalid_template_wrong_path(self): + # on Windows, trailing slashes are not allowed + # this used to crash instead of raising a warning: #8286 + self._check_template('include examples/') + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_get_file_list(self): + # make sure MANIFEST is recalculated + dist, cmd = self.get_cmd() + + # filling data_files by pointing files in package_data + dist.package_data = {'somecode': ['*.txt']} + self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + cmd.formats = ['gztar'] + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(len(manifest), 5) + + # adding a file + self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') + + # make sure build_py is reinitialized, like a fresh run + build_py = dist.get_command_obj('build_py') + build_py.finalized = False + build_py.ensure_finalized() + + cmd.run() + + f = open(cmd.manifest) + try: + manifest2 = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + # do we have the new file in MANIFEST ? + self.assertEqual(len(manifest2), 6) + self.assertIn('doc2.txt', manifest2[-1]) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_manifest_marker(self): + # check that autogenerated MANIFESTs have a marker + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest[0], + '# file GENERATED by distutils, do NOT edit') + + @unittest.skipUnless(ZLIB_SUPPORT, "Need zlib support to run") + def test_manifest_comments(self): + # make sure comments don't cause exceptions or wrong includes + contents = dedent("""\ + # bad.py + #bad.py + good.py + """) + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), contents) + self.write_file((self.tmp_dir, 'good.py'), '# pick me!') + self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!") + self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!") + cmd.run() + self.assertEqual(cmd.filelist.files, ['good.py']) + + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') + def test_manual_manifest(self): + # check that a MANIFEST without a marker is left alone + dist, cmd = self.get_cmd() + cmd.formats = ['gztar'] + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') + self.write_file((self.tmp_dir, 'README.manual'), + 'This project maintains its MANIFEST file itself.') + cmd.run() + self.assertEqual(cmd.filelist.files, ['README.manual']) + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest, ['README.manual']) + + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + filenames = [tarinfo.name for tarinfo in archive] + finally: + archive.close() + self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO', + 'fake-1.0/README.manual']) + + @unittest.skipUnless(ZLIB_SUPPORT, "requires zlib") + @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + @unittest.skipIf(find_executable('tar') is None, + "The tar command is not found") + @unittest.skipIf(find_executable('gzip') is None, + "The gzip command is not found") + def test_make_distribution_owner_group(self): + # now building a sdist + dist, cmd = self.get_cmd() + + # creating a gztar and specifying the owner+group + cmd.formats = ['gztar'] + cmd.owner = pwd.getpwuid(0)[0] + cmd.group = grp.getgrgid(0)[0] + cmd.ensure_finalized() + cmd.run() + + # making sure we have the good rights + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + try: + for member in archive.getmembers(): + self.assertEqual(member.uid, 0) + self.assertEqual(member.gid, 0) + finally: + archive.close() + + # building a sdist again + dist, cmd = self.get_cmd() + + # creating a gztar + cmd.formats = ['gztar'] + cmd.ensure_finalized() + cmd.run() + + # making sure we have the good rights + archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') + archive = tarfile.open(archive_name) + + # note that we are not testing the group ownership here + # because, depending on the platforms and the container + # rights (see #7408) + try: + for member in archive.getmembers(): + self.assertEqual(member.uid, os.getuid()) + finally: + archive.close() + +def test_suite(): + return unittest.makeSuite(SDistTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_spawn.py b/setuptools/_distutils/tests/test_spawn.py new file mode 100644 index 00000000..919d0ad9 --- /dev/null +++ b/setuptools/_distutils/tests/test_spawn.py @@ -0,0 +1,134 @@ +"""Tests for distutils.spawn.""" +import os +import stat +import sys +import unittest.mock +from test.support import run_unittest +from test import support as test_support + +from .py35compat import unix_shell + +from distutils.spawn import find_executable +from distutils.spawn import spawn +from distutils.errors import DistutilsExecError +from distutils.tests import support + +class SpawnTestCase(support.TempdirManager, + support.LoggingSilencer, + unittest.TestCase): + + @unittest.skipUnless(os.name in ('nt', 'posix'), + 'Runs only under posix or nt') + def test_spawn(self): + tmpdir = self.mkdtemp() + + # creating something executable + # through the shell that returns 1 + if sys.platform != 'win32': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!%s\nexit 1' % unix_shell) + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 1') + + os.chmod(exe, 0o777) + self.assertRaises(DistutilsExecError, spawn, [exe]) + + # now something that works + if sys.platform != 'win32': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!%s\nexit 0' % unix_shell) + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 0') + + os.chmod(exe, 0o777) + spawn([exe]) # should work without any error + + def test_find_executable(self): + with test_support.temp_dir() as tmp_dir: + # use TESTFN to get a pseudo-unique filename + program_noeext = test_support.TESTFN + # Give the temporary program an ".exe" suffix for all. + # It's needed on Windows and not harmful on other platforms. + program = program_noeext + ".exe" + + filename = os.path.join(tmp_dir, program) + with open(filename, "wb"): + pass + os.chmod(filename, stat.S_IXUSR) + + # test path parameter + rv = find_executable(program, path=tmp_dir) + self.assertEqual(rv, filename) + + if sys.platform == 'win32': + # test without ".exe" extension + rv = find_executable(program_noeext, path=tmp_dir) + self.assertEqual(rv, filename) + + # test find in the current directory + with test_support.change_cwd(tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, program) + + # test non-existent program + dont_exist_program = "dontexist_" + program + rv = find_executable(dont_exist_program , path=tmp_dir) + self.assertIsNone(rv) + + # PATH='': no match, except in the current directory + with test_support.EnvironmentVarGuard() as env: + env['PATH'] = '' + with unittest.mock.patch('distutils.spawn.os.confstr', + return_value=tmp_dir, create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', + tmp_dir): + rv = find_executable(program) + self.assertIsNone(rv) + + # look in current directory + with test_support.change_cwd(tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, program) + + # PATH=':': explicitly looks in the current directory + with test_support.EnvironmentVarGuard() as env: + env['PATH'] = os.pathsep + with unittest.mock.patch('distutils.spawn.os.confstr', + return_value='', create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', ''): + rv = find_executable(program) + self.assertIsNone(rv) + + # look in current directory + with test_support.change_cwd(tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, program) + + # missing PATH: test os.confstr("CS_PATH") and os.defpath + with test_support.EnvironmentVarGuard() as env: + env.pop('PATH', None) + + # without confstr + with unittest.mock.patch('distutils.spawn.os.confstr', + side_effect=ValueError, + create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', + tmp_dir): + rv = find_executable(program) + self.assertEqual(rv, filename) + + # with confstr + with unittest.mock.patch('distutils.spawn.os.confstr', + return_value=tmp_dir, create=True), \ + unittest.mock.patch('distutils.spawn.os.defpath', ''): + rv = find_executable(program) + self.assertEqual(rv, filename) + + +def test_suite(): + return unittest.makeSuite(SpawnTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_sysconfig.py b/setuptools/_distutils/tests/test_sysconfig.py new file mode 100644 index 00000000..d5076391 --- /dev/null +++ b/setuptools/_distutils/tests/test_sysconfig.py @@ -0,0 +1,275 @@ +"""Tests for distutils.sysconfig.""" +import contextlib +import os +import shutil +import subprocess +import sys +import textwrap +import unittest + +from distutils import sysconfig +from distutils.ccompiler import get_default_compiler +from distutils.tests import support +from test.support import TESTFN, run_unittest, check_warnings, swap_item + +class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): + def setUp(self): + super(SysconfigTestCase, self).setUp() + self.makefile = None + + def tearDown(self): + if self.makefile is not None: + os.unlink(self.makefile) + self.cleanup_testfn() + super(SysconfigTestCase, self).tearDown() + + def cleanup_testfn(self): + if os.path.isfile(TESTFN): + os.remove(TESTFN) + elif os.path.isdir(TESTFN): + shutil.rmtree(TESTFN) + + def test_get_config_h_filename(self): + config_h = sysconfig.get_config_h_filename() + self.assertTrue(os.path.isfile(config_h), config_h) + + def test_get_python_lib(self): + # XXX doesn't work on Linux when Python was never installed before + #self.assertTrue(os.path.isdir(lib_dir), lib_dir) + # test for pythonxx.lib? + self.assertNotEqual(sysconfig.get_python_lib(), + sysconfig.get_python_lib(prefix=TESTFN)) + + def test_get_config_vars(self): + cvars = sysconfig.get_config_vars() + self.assertIsInstance(cvars, dict) + self.assertTrue(cvars) + + @unittest.skip('sysconfig.IS_PYPY') + def test_srcdir(self): + # See Issues #15322, #15364. + srcdir = sysconfig.get_config_var('srcdir') + + self.assertTrue(os.path.isabs(srcdir), srcdir) + self.assertTrue(os.path.isdir(srcdir), srcdir) + + if sysconfig.python_build: + # The python executable has not been installed so srcdir + # should be a full source checkout. + Python_h = os.path.join(srcdir, 'Include', 'Python.h') + self.assertTrue(os.path.exists(Python_h), Python_h) + self.assertTrue(sysconfig._is_python_source_dir(srcdir)) + elif os.name == 'posix': + self.assertEqual( + os.path.dirname(sysconfig.get_makefile_filename()), + srcdir) + + def test_srcdir_independent_of_cwd(self): + # srcdir should be independent of the current working directory + # See Issues #15322, #15364. + srcdir = sysconfig.get_config_var('srcdir') + cwd = os.getcwd() + try: + os.chdir('..') + srcdir2 = sysconfig.get_config_var('srcdir') + finally: + os.chdir(cwd) + self.assertEqual(srcdir, srcdir2) + + def customize_compiler(self): + # make sure AR gets caught + class compiler: + compiler_type = 'unix' + + def set_executables(self, **kw): + self.exes = kw + + sysconfig_vars = { + 'AR': 'sc_ar', + 'CC': 'sc_cc', + 'CXX': 'sc_cxx', + 'ARFLAGS': '--sc-arflags', + 'CFLAGS': '--sc-cflags', + 'CCSHARED': '--sc-ccshared', + 'LDSHARED': 'sc_ldshared', + 'SHLIB_SUFFIX': 'sc_shutil_suffix', + + # On macOS, disable _osx_support.customize_compiler() + 'CUSTOMIZED_OSX_COMPILER': 'True', + } + + comp = compiler() + with contextlib.ExitStack() as cm: + for key, value in sysconfig_vars.items(): + cm.enter_context(swap_item(sysconfig._config_vars, key, value)) + sysconfig.customize_compiler(comp) + + return comp + + @unittest.skipUnless(get_default_compiler() == 'unix', + 'not testing if default compiler is not unix') + def test_customize_compiler(self): + # Make sure that sysconfig._config_vars is initialized + sysconfig.get_config_vars() + + os.environ['AR'] = 'env_ar' + os.environ['CC'] = 'env_cc' + os.environ['CPP'] = 'env_cpp' + os.environ['CXX'] = 'env_cxx --env-cxx-flags' + os.environ['LDSHARED'] = 'env_ldshared' + os.environ['LDFLAGS'] = '--env-ldflags' + os.environ['ARFLAGS'] = '--env-arflags' + os.environ['CFLAGS'] = '--env-cflags' + os.environ['CPPFLAGS'] = '--env-cppflags' + + comp = self.customize_compiler() + self.assertEqual(comp.exes['archiver'], + 'env_ar --env-arflags') + self.assertEqual(comp.exes['preprocessor'], + 'env_cpp --env-cppflags') + self.assertEqual(comp.exes['compiler'], + 'env_cc --sc-cflags --env-cflags --env-cppflags') + self.assertEqual(comp.exes['compiler_so'], + ('env_cc --sc-cflags ' + '--env-cflags ''--env-cppflags --sc-ccshared')) + self.assertEqual(comp.exes['compiler_cxx'], + 'env_cxx --env-cxx-flags') + self.assertEqual(comp.exes['linker_exe'], + 'env_cc') + self.assertEqual(comp.exes['linker_so'], + ('env_ldshared --env-ldflags --env-cflags' + ' --env-cppflags')) + self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') + + del os.environ['AR'] + del os.environ['CC'] + del os.environ['CPP'] + del os.environ['CXX'] + del os.environ['LDSHARED'] + del os.environ['LDFLAGS'] + del os.environ['ARFLAGS'] + del os.environ['CFLAGS'] + del os.environ['CPPFLAGS'] + + comp = self.customize_compiler() + self.assertEqual(comp.exes['archiver'], + 'sc_ar --sc-arflags') + self.assertEqual(comp.exes['preprocessor'], + 'sc_cc -E') + self.assertEqual(comp.exes['compiler'], + 'sc_cc --sc-cflags') + self.assertEqual(comp.exes['compiler_so'], + 'sc_cc --sc-cflags --sc-ccshared') + self.assertEqual(comp.exes['compiler_cxx'], + 'sc_cxx') + self.assertEqual(comp.exes['linker_exe'], + 'sc_cc') + self.assertEqual(comp.exes['linker_so'], + 'sc_ldshared') + self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') + + def test_parse_makefile_base(self): + self.makefile = TESTFN + fd = open(self.makefile, 'w') + try: + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + finally: + fd.close() + d = sysconfig.parse_makefile(self.makefile) + self.assertEqual(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", + 'OTHER': 'foo'}) + + def test_parse_makefile_literal_dollar(self): + self.makefile = TESTFN + fd = open(self.makefile, 'w') + try: + fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') + fd.write('VAR=$OTHER\nOTHER=foo') + finally: + fd.close() + d = sysconfig.parse_makefile(self.makefile) + self.assertEqual(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", + 'OTHER': 'foo'}) + + + def test_sysconfig_module(self): + import sysconfig as global_sysconfig + self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), + sysconfig.get_config_var('CFLAGS')) + self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), + sysconfig.get_config_var('LDFLAGS')) + + @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'), + 'compiler flags customized') + def test_sysconfig_compiler_vars(self): + # On OS X, binary installers support extension module building on + # various levels of the operating system with differing Xcode + # configurations. This requires customization of some of the + # compiler configuration directives to suit the environment on + # the installed machine. Some of these customizations may require + # running external programs and, so, are deferred until needed by + # the first extension module build. With Python 3.3, only + # the Distutils version of sysconfig is used for extension module + # builds, which happens earlier in the Distutils tests. This may + # cause the following tests to fail since no tests have caused + # the global version of sysconfig to call the customization yet. + # The solution for now is to simply skip this test in this case. + # The longer-term solution is to only have one version of sysconfig. + + import sysconfig as global_sysconfig + if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'): + self.skipTest('compiler flags customized') + self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), + sysconfig.get_config_var('LDSHARED')) + self.assertEqual(global_sysconfig.get_config_var('CC'), + sysconfig.get_config_var('CC')) + + @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, + 'EXT_SUFFIX required for this test') + def test_SO_deprecation(self): + self.assertWarns(DeprecationWarning, + sysconfig.get_config_var, 'SO') + + @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, + 'EXT_SUFFIX required for this test') + def test_SO_value(self): + with check_warnings(('', DeprecationWarning)): + self.assertEqual(sysconfig.get_config_var('SO'), + sysconfig.get_config_var('EXT_SUFFIX')) + + @unittest.skipIf(sysconfig.get_config_var('EXT_SUFFIX') is None, + 'EXT_SUFFIX required for this test') + def test_SO_in_vars(self): + vars = sysconfig.get_config_vars() + self.assertIsNotNone(vars['SO']) + self.assertEqual(vars['SO'], vars['EXT_SUFFIX']) + + def test_customize_compiler_before_get_config_vars(self): + # Issue #21923: test that a Distribution compiler + # instance can be called without an explicit call to + # get_config_vars(). + with open(TESTFN, 'w') as f: + f.writelines(textwrap.dedent('''\ + from distutils.core import Distribution + config = Distribution().get_command_obj('config') + # try_compile may pass or it may fail if no compiler + # is found but it should not raise an exception. + rc = config.try_compile('int x;') + ''')) + p = subprocess.Popen([str(sys.executable), TESTFN], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + outs, errs = p.communicate() + self.assertEqual(0, p.returncode, "Subprocess failed: " + outs) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(SysconfigTestCase)) + return suite + + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_text_file.py b/setuptools/_distutils/tests/test_text_file.py new file mode 100644 index 00000000..7e76240a --- /dev/null +++ b/setuptools/_distutils/tests/test_text_file.py @@ -0,0 +1,107 @@ +"""Tests for distutils.text_file.""" +import os +import unittest +from distutils.text_file import TextFile +from distutils.tests import support +from test.support import run_unittest + +TEST_DATA = """# test file + +line 3 \\ +# intervening comment + continues on next line +""" + +class TextFileTestCase(support.TempdirManager, unittest.TestCase): + + def test_class(self): + # old tests moved from text_file.__main__ + # so they are really called by the buildbots + + # result 1: no fancy options + result1 = ['# test file\n', '\n', 'line 3 \\\n', + '# intervening comment\n', + ' continues on next line\n'] + + # result 2: just strip comments + result2 = ["\n", + "line 3 \\\n", + " continues on next line\n"] + + # result 3: just strip blank lines + result3 = ["# test file\n", + "line 3 \\\n", + "# intervening comment\n", + " continues on next line\n"] + + # result 4: default, strip comments, blank lines, + # and trailing whitespace + result4 = ["line 3 \\", + " continues on next line"] + + # result 5: strip comments and blanks, plus join lines (but don't + # "collapse" joined lines + result5 = ["line 3 continues on next line"] + + # result 6: strip comments and blanks, plus join lines (and + # "collapse" joined lines + result6 = ["line 3 continues on next line"] + + def test_input(count, description, file, expected_result): + result = file.readlines() + self.assertEqual(result, expected_result) + + tmpdir = self.mkdtemp() + filename = os.path.join(tmpdir, "test.txt") + out_file = open(filename, "w") + try: + out_file.write(TEST_DATA) + finally: + out_file.close() + + in_file = TextFile(filename, strip_comments=0, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(1, "no processing", in_file, result1) + finally: + in_file.close() + + in_file = TextFile(filename, strip_comments=1, skip_blanks=0, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(2, "strip comments", in_file, result2) + finally: + in_file.close() + + in_file = TextFile(filename, strip_comments=0, skip_blanks=1, + lstrip_ws=0, rstrip_ws=0) + try: + test_input(3, "strip blanks", in_file, result3) + finally: + in_file.close() + + in_file = TextFile(filename) + try: + test_input(4, "default processing", in_file, result4) + finally: + in_file.close() + + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1) + try: + test_input(5, "join lines without collapsing", in_file, result5) + finally: + in_file.close() + + in_file = TextFile(filename, strip_comments=1, skip_blanks=1, + join_lines=1, rstrip_ws=1, collapse_join=1) + try: + test_input(6, "join lines with collapsing", in_file, result6) + finally: + in_file.close() + +def test_suite(): + return unittest.makeSuite(TextFileTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_unixccompiler.py b/setuptools/_distutils/tests/test_unixccompiler.py new file mode 100644 index 00000000..f2159662 --- /dev/null +++ b/setuptools/_distutils/tests/test_unixccompiler.py @@ -0,0 +1,155 @@ +"""Tests for distutils.unixccompiler.""" +import sys +import unittest +from test.support import EnvironmentVarGuard, run_unittest + +from distutils import sysconfig +from distutils.unixccompiler import UnixCCompiler + +class UnixCCompilerTestCase(unittest.TestCase): + + def setUp(self): + self._backup_platform = sys.platform + self._backup_get_config_var = sysconfig.get_config_var + self._backup_get_config_vars = sysconfig.get_config_vars + class CompilerWrapper(UnixCCompiler): + def rpath_foo(self): + return self.runtime_library_dir_option('/foo') + self.cc = CompilerWrapper() + + def tearDown(self): + sys.platform = self._backup_platform + sysconfig.get_config_var = self._backup_get_config_var + sysconfig.get_config_vars = self._backup_get_config_vars + + @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") + def test_runtime_libdir_option(self): + # Issue#5900 + # + # Ensure RUNPATH is added to extension modules with RPATH if + # GNU ld is used + + # darwin + sys.platform = 'darwin' + self.assertEqual(self.cc.rpath_foo(), '-L/foo') + + # hp-ux + sys.platform = 'hp-ux' + old_gcv = sysconfig.get_config_var + def gcv(v): + return 'xxx' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo']) + + def gcv(v): + return 'gcc' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + def gcv(v): + return 'g++' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) + + sysconfig.get_config_var = old_gcv + + # GCC GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'gcc' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + + # GCC non-GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'gcc' + elif v == 'GNULD': + return 'no' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') + + # GCC GNULD with fully qualified configuration prefix + # see #7617 + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'x86_64-pc-linux-gnu-gcc-4.4.2' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') + + # non-GCC GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'cc' + elif v == 'GNULD': + return 'yes' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + + # non-GCC non-GNULD + sys.platform = 'bar' + def gcv(v): + if v == 'CC': + return 'cc' + elif v == 'GNULD': + return 'no' + sysconfig.get_config_var = gcv + self.assertEqual(self.cc.rpath_foo(), '-R/foo') + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') + def test_osx_cc_overrides_ldshared(self): + # Issue #18080: + # ensure that setting CC env variable also changes default linker + def gcv(v): + if v == 'LDSHARED': + return 'gcc-4.2 -bundle -undefined dynamic_lookup ' + return 'gcc-4.2' + + def gcvs(*args, _orig=sysconfig.get_config_vars): + if args: + return list(map(sysconfig.get_config_var, args)) + return _orig() + sysconfig.get_config_var = gcv + sysconfig.get_config_vars = gcvs + with EnvironmentVarGuard() as env: + env['CC'] = 'my_cc' + del env['LDSHARED'] + sysconfig.customize_compiler(self.cc) + self.assertEqual(self.cc.linker_so[0], 'my_cc') + + @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') + def test_osx_explicit_ldshared(self): + # Issue #18080: + # ensure that setting CC env variable does not change + # explicit LDSHARED setting for linker + def gcv(v): + if v == 'LDSHARED': + return 'gcc-4.2 -bundle -undefined dynamic_lookup ' + return 'gcc-4.2' + + def gcvs(*args, _orig=sysconfig.get_config_vars): + if args: + return list(map(sysconfig.get_config_var, args)) + return _orig() + sysconfig.get_config_var = gcv + sysconfig.get_config_vars = gcvs + with EnvironmentVarGuard() as env: + env['CC'] = 'my_cc' + env['LDSHARED'] = 'my_ld -bundle -dynamic' + sysconfig.customize_compiler(self.cc) + self.assertEqual(self.cc.linker_so[0], 'my_ld') + + +def test_suite(): + return unittest.makeSuite(UnixCCompilerTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_upload.py b/setuptools/_distutils/tests/test_upload.py new file mode 100644 index 00000000..bca5516d --- /dev/null +++ b/setuptools/_distutils/tests/test_upload.py @@ -0,0 +1,223 @@ +"""Tests for distutils.command.upload.""" +import os +import unittest +import unittest.mock as mock +from urllib.request import HTTPError + +from test.support import run_unittest + +from distutils.command import upload as upload_mod +from distutils.command.upload import upload +from distutils.core import Distribution +from distutils.errors import DistutilsError +from distutils.log import ERROR, INFO + +from distutils.tests.test_config import PYPIRC, BasePyPIRCCommandTestCase + +PYPIRC_LONG_PASSWORD = """\ +[distutils] + +index-servers = + server1 + server2 + +[server1] +username:me +password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ +""" + + +PYPIRC_NOPASSWORD = """\ +[distutils] + +index-servers = + server1 + +[server1] +username:me +""" + +class FakeOpen(object): + + def __init__(self, url, msg=None, code=None): + self.url = url + if not isinstance(url, str): + self.req = url + else: + self.req = None + self.msg = msg or 'OK' + self.code = code or 200 + + def getheader(self, name, default=None): + return { + 'content-type': 'text/plain; charset=utf-8', + }.get(name.lower(), default) + + def read(self): + return b'xyzzy' + + def getcode(self): + return self.code + + +class uploadTestCase(BasePyPIRCCommandTestCase): + + def setUp(self): + super(uploadTestCase, self).setUp() + self.old_open = upload_mod.urlopen + upload_mod.urlopen = self._urlopen + self.last_open = None + self.next_msg = None + self.next_code = None + + def tearDown(self): + upload_mod.urlopen = self.old_open + super(uploadTestCase, self).tearDown() + + def _urlopen(self, url): + self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code) + return self.last_open + + def test_finalize_options(self): + + # new format + self.write_file(self.rc, PYPIRC) + dist = Distribution() + cmd = upload(dist) + cmd.finalize_options() + for attr, waited in (('username', 'me'), ('password', 'secret'), + ('realm', 'pypi'), + ('repository', 'https://upload.pypi.org/legacy/')): + self.assertEqual(getattr(cmd, attr), waited) + + def test_saved_password(self): + # file with no password + self.write_file(self.rc, PYPIRC_NOPASSWORD) + + # make sure it passes + dist = Distribution() + cmd = upload(dist) + cmd.finalize_options() + self.assertEqual(cmd.password, None) + + # make sure we get it as well, if another command + # initialized it at the dist level + dist.password = 'xxx' + cmd = upload(dist) + cmd.finalize_options() + self.assertEqual(cmd.password, 'xxx') + + def test_upload(self): + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path) + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) + + # lets run it + pkg_dir, dist = self.create_dist(dist_files=dist_files) + cmd = upload(dist) + cmd.show_response = 1 + cmd.ensure_finalized() + cmd.run() + + # what did we send ? + headers = dict(self.last_open.req.headers) + self.assertGreaterEqual(int(headers['Content-length']), 2162) + content_type = headers['Content-type'] + self.assertTrue(content_type.startswith('multipart/form-data')) + self.assertEqual(self.last_open.req.get_method(), 'POST') + expected_url = 'https://upload.pypi.org/legacy/' + self.assertEqual(self.last_open.req.get_full_url(), expected_url) + data = self.last_open.req.data + self.assertIn(b'xxx',data) + self.assertIn(b'protocol_version', data) + self.assertIn(b'sha256_digest', data) + self.assertIn( + b'cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf' + b'6860', + data + ) + if b'md5_digest' in data: + self.assertIn(b'f561aaf6ef0bf14d4208bb46a4ccb3ad', data) + if b'blake2_256_digest' in data: + self.assertIn( + b'b6f289a27d4fe90da63c503bfe0a9b761a8f76bb86148565065f040be' + b'6d1c3044cf7ded78ef800509bccb4b648e507d88dc6383d67642aadcc' + b'ce443f1534330a', + data + ) + + # The PyPI response body was echoed + results = self.get_logs(INFO) + self.assertEqual(results[-1], 75 * '-' + '\nxyzzy\n' + 75 * '-') + + # bpo-32304: archives whose last byte was b'\r' were corrupted due to + # normalization intended for Mac OS 9. + def test_upload_correct_cr(self): + # content that ends with \r should not be modified. + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path, content='yy\r') + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) + + # other fields that ended with \r used to be modified, now are + # preserved. + pkg_dir, dist = self.create_dist( + dist_files=dist_files, + description='long description\r' + ) + cmd = upload(dist) + cmd.show_response = 1 + cmd.ensure_finalized() + cmd.run() + + headers = dict(self.last_open.req.headers) + self.assertGreaterEqual(int(headers['Content-length']), 2172) + self.assertIn(b'long description\r', self.last_open.req.data) + + def test_upload_fails(self): + self.next_msg = "Not Found" + self.next_code = 404 + self.assertRaises(DistutilsError, self.test_upload) + + def test_wrong_exception_order(self): + tmp = self.mkdtemp() + path = os.path.join(tmp, 'xxx') + self.write_file(path) + dist_files = [('xxx', '2.6', path)] # command, pyversion, filename + self.write_file(self.rc, PYPIRC_LONG_PASSWORD) + + pkg_dir, dist = self.create_dist(dist_files=dist_files) + tests = [ + (OSError('oserror'), 'oserror', OSError), + (HTTPError('url', 400, 'httperror', {}, None), + 'Upload failed (400): httperror', DistutilsError), + ] + for exception, expected, raised_exception in tests: + with self.subTest(exception=type(exception).__name__): + with mock.patch('distutils.command.upload.urlopen', + new=mock.Mock(side_effect=exception)): + with self.assertRaises(raised_exception): + cmd = upload(dist) + cmd.ensure_finalized() + cmd.run() + results = self.get_logs(ERROR) + self.assertIn(expected, results[-1]) + self.clear_logs() + + +def test_suite(): + return unittest.makeSuite(uploadTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_util.py b/setuptools/_distutils/tests/test_util.py new file mode 100644 index 00000000..bf0d4333 --- /dev/null +++ b/setuptools/_distutils/tests/test_util.py @@ -0,0 +1,309 @@ +"""Tests for distutils.util.""" +import os +import sys +import unittest +from copy import copy +from test.support import run_unittest +from unittest import mock + +from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError +from distutils.util import (get_platform, convert_path, change_root, + check_environ, split_quoted, strtobool, + rfc822_escape, byte_compile, + grok_environment_error) +from distutils import util # used to patch _environ_checked +from distutils.sysconfig import get_config_vars +from distutils import sysconfig +from distutils.tests import support +import _osx_support + +class UtilTestCase(support.EnvironGuard, unittest.TestCase): + + def setUp(self): + super(UtilTestCase, self).setUp() + # saving the environment + self.name = os.name + self.platform = sys.platform + self.version = sys.version + self.sep = os.sep + self.join = os.path.join + self.isabs = os.path.isabs + self.splitdrive = os.path.splitdrive + self._config_vars = copy(sysconfig._config_vars) + + # patching os.uname + if hasattr(os, 'uname'): + self.uname = os.uname + self._uname = os.uname() + else: + self.uname = None + self._uname = None + + os.uname = self._get_uname + + def tearDown(self): + # getting back the environment + os.name = self.name + sys.platform = self.platform + sys.version = self.version + os.sep = self.sep + os.path.join = self.join + os.path.isabs = self.isabs + os.path.splitdrive = self.splitdrive + if self.uname is not None: + os.uname = self.uname + else: + del os.uname + sysconfig._config_vars = copy(self._config_vars) + super(UtilTestCase, self).tearDown() + + def _set_uname(self, uname): + self._uname = uname + + def _get_uname(self): + return self._uname + + def test_get_platform(self): + + # windows XP, 32bits + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Intel)]') + sys.platform = 'win32' + self.assertEqual(get_platform(), 'win32') + + # windows XP, amd64 + os.name = 'nt' + sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' + '[MSC v.1310 32 bit (Amd64)]') + sys.platform = 'win32' + self.assertEqual(get_platform(), 'win-amd64') + + # macbook + os.name = 'posix' + sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' + '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') + sys.platform = 'darwin' + self._set_uname(('Darwin', 'macziade', '8.11.1', + ('Darwin Kernel Version 8.11.1: ' + 'Wed Oct 10 18:23:28 PDT 2007; ' + 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3' + + get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' + '-fwrapv -O3 -Wall -Wstrict-prototypes') + + cursize = sys.maxsize + sys.maxsize = (2 ** 31)-1 + try: + self.assertEqual(get_platform(), 'macosx-10.3-i386') + finally: + sys.maxsize = cursize + + # macbook with fat binaries (fat, universal or fat64) + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4' + get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEqual(get_platform(), 'macosx-10.4-fat') + + _osx_support._remove_original_values(get_config_vars()) + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1' + self.assertEqual(get_platform(), 'macosx-10.4-fat') + + + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEqual(get_platform(), 'macosx-10.4-intel') + + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + self.assertEqual(get_platform(), 'macosx-10.4-fat3') + + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + self.assertEqual(get_platform(), 'macosx-10.4-universal') + + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3') + + self.assertEqual(get_platform(), 'macosx-10.4-fat64') + + for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): + _osx_support._remove_original_values(get_config_vars()) + get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' + '/Developer/SDKs/MacOSX10.4u.sdk ' + '-fno-strict-aliasing -fno-common ' + '-dynamic -DNDEBUG -g -O3'%(arch,)) + + self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,)) + + + # linux debian sarge + os.name = 'posix' + sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' + '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]') + sys.platform = 'linux2' + self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', + '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) + + self.assertEqual(get_platform(), 'linux-i686') + + # XXX more platforms to tests here + + def test_convert_path(self): + # linux/mac + os.sep = '/' + def _join(path): + return '/'.join(path) + os.path.join = _join + + self.assertEqual(convert_path('/home/to/my/stuff'), + '/home/to/my/stuff') + + # win + os.sep = '\\' + def _join(*path): + return '\\'.join(path) + os.path.join = _join + + self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') + self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') + + self.assertEqual(convert_path('home/to/my/stuff'), + 'home\\to\\my\\stuff') + self.assertEqual(convert_path('.'), + os.curdir) + + def test_change_root(self): + # linux/mac + os.name = 'posix' + def _isabs(path): + return path[0] == '/' + os.path.isabs = _isabs + def _join(*path): + return '/'.join(path) + os.path.join = _join + + self.assertEqual(change_root('/root', '/old/its/here'), + '/root/old/its/here') + self.assertEqual(change_root('/root', 'its/here'), + '/root/its/here') + + # windows + os.name = 'nt' + def _isabs(path): + return path.startswith('c:\\') + os.path.isabs = _isabs + def _splitdrive(path): + if path.startswith('c:'): + return ('', path.replace('c:', '')) + return ('', path) + os.path.splitdrive = _splitdrive + def _join(*path): + return '\\'.join(path) + os.path.join = _join + + self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'), + 'c:\\root\\old\\its\\here') + self.assertEqual(change_root('c:\\root', 'its\\here'), + 'c:\\root\\its\\here') + + # BugsBunny os (it's a great os) + os.name = 'BugsBunny' + self.assertRaises(DistutilsPlatformError, + change_root, 'c:\\root', 'its\\here') + + # XXX platforms to be covered: mac + + def test_check_environ(self): + util._environ_checked = 0 + os.environ.pop('HOME', None) + + check_environ() + + self.assertEqual(os.environ['PLAT'], get_platform()) + self.assertEqual(util._environ_checked, 1) + + @unittest.skipUnless(os.name == 'posix', 'specific to posix') + def test_check_environ_getpwuid(self): + util._environ_checked = 0 + os.environ.pop('HOME', None) + + import pwd + + # only set pw_dir field, other fields are not used + result = pwd.struct_passwd((None, None, None, None, None, + '/home/distutils', None)) + with mock.patch.object(pwd, 'getpwuid', return_value=result): + check_environ() + self.assertEqual(os.environ['HOME'], '/home/distutils') + + util._environ_checked = 0 + os.environ.pop('HOME', None) + + # bpo-10496: Catch pwd.getpwuid() error + with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError): + check_environ() + self.assertNotIn('HOME', os.environ) + + def test_split_quoted(self): + self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), + ['one', 'two', 'three', 'four']) + + def test_strtobool(self): + yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') + no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') + + for y in yes: + self.assertTrue(strtobool(y)) + + for n in no: + self.assertFalse(strtobool(n)) + + def test_rfc822_escape(self): + header = 'I am a\npoor\nlonesome\nheader\n' + res = rfc822_escape(header) + wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' + 'header%(8s)s') % {'8s': '\n'+8*' '} + self.assertEqual(res, wanted) + + def test_dont_write_bytecode(self): + # makes sure byte_compile raise a DistutilsError + # if sys.dont_write_bytecode is True + old_dont_write_bytecode = sys.dont_write_bytecode + sys.dont_write_bytecode = True + try: + self.assertRaises(DistutilsByteCompileError, byte_compile, []) + finally: + sys.dont_write_bytecode = old_dont_write_bytecode + + def test_grok_environment_error(self): + # test obsolete function to ensure backward compat (#4931) + exc = IOError("Unable to find batch file") + msg = grok_environment_error(exc) + self.assertEqual(msg, "error: Unable to find batch file") + + +def test_suite(): + return unittest.makeSuite(UtilTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_version.py b/setuptools/_distutils/tests/test_version.py new file mode 100644 index 00000000..8671cd2f --- /dev/null +++ b/setuptools/_distutils/tests/test_version.py @@ -0,0 +1,87 @@ +"""Tests for distutils.version.""" +import unittest +from distutils.version import LooseVersion +from distutils.version import StrictVersion +from test.support import run_unittest + +class VersionTestCase(unittest.TestCase): + + def test_prerelease(self): + version = StrictVersion('1.2.3a1') + self.assertEqual(version.version, (1, 2, 3)) + self.assertEqual(version.prerelease, ('a', 1)) + self.assertEqual(str(version), '1.2.3a1') + + version = StrictVersion('1.2.0') + self.assertEqual(str(version), '1.2') + + def test_cmp_strict(self): + versions = (('1.5.1', '1.5.2b2', -1), + ('161', '3.10a', ValueError), + ('8.02', '8.02', 0), + ('3.4j', '1996.07.12', ValueError), + ('3.2.pl0', '3.1.1.6', ValueError), + ('2g6', '11g', ValueError), + ('0.9', '2.2', -1), + ('1.2.1', '1.2', 1), + ('1.1', '1.2.2', -1), + ('1.2', '1.1', 1), + ('1.2.1', '1.2.2', -1), + ('1.2.2', '1.2', 1), + ('1.2', '1.2.2', -1), + ('0.4.0', '0.4', 0), + ('1.13++', '5.5.kw', ValueError)) + + for v1, v2, wanted in versions: + try: + res = StrictVersion(v1)._cmp(StrictVersion(v2)) + except ValueError: + if wanted is ValueError: + continue + else: + raise AssertionError(("cmp(%s, %s) " + "shouldn't raise ValueError") + % (v1, v2)) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + res = StrictVersion(v1)._cmp(v2) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + res = StrictVersion(v1)._cmp(object()) + self.assertIs(res, NotImplemented, + 'cmp(%s, %s) should be NotImplemented, got %s' % + (v1, v2, res)) + + + def test_cmp(self): + versions = (('1.5.1', '1.5.2b2', -1), + ('161', '3.10a', 1), + ('8.02', '8.02', 0), + ('3.4j', '1996.07.12', -1), + ('3.2.pl0', '3.1.1.6', 1), + ('2g6', '11g', -1), + ('0.960923', '2.2beta29', -1), + ('1.13++', '5.5.kw', -1)) + + + for v1, v2, wanted in versions: + res = LooseVersion(v1)._cmp(LooseVersion(v2)) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + res = LooseVersion(v1)._cmp(v2) + self.assertEqual(res, wanted, + 'cmp(%s, %s) should be %s, got %s' % + (v1, v2, wanted, res)) + res = LooseVersion(v1)._cmp(object()) + self.assertIs(res, NotImplemented, + 'cmp(%s, %s) should be NotImplemented, got %s' % + (v1, v2, res)) + +def test_suite(): + return unittest.makeSuite(VersionTestCase) + +if __name__ == "__main__": + run_unittest(test_suite()) diff --git a/setuptools/_distutils/tests/test_versionpredicate.py b/setuptools/_distutils/tests/test_versionpredicate.py new file mode 100644 index 00000000..28ae09dc --- /dev/null +++ b/setuptools/_distutils/tests/test_versionpredicate.py @@ -0,0 +1,13 @@ +"""Tests harness for distutils.versionpredicate. + +""" + +import distutils.versionpredicate +import doctest +from test.support import run_unittest + +def test_suite(): + return doctest.DocTestSuite(distutils.versionpredicate) + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/setuptools/_distutils/text_file.py b/setuptools/_distutils/text_file.py new file mode 100644 index 00000000..93abad38 --- /dev/null +++ b/setuptools/_distutils/text_file.py @@ -0,0 +1,286 @@ +"""text_file + +provides the TextFile class, which gives an interface to text files +that (optionally) takes care of stripping comments, ignoring blank +lines, and joining lines with backslashes.""" + +import sys, io + + +class TextFile: + """Provides a file-like object that takes care of all the things you + commonly want to do when processing a text file that has some + line-by-line syntax: strip comments (as long as "#" is your + comment character), skip blank lines, join adjacent lines by + escaping the newline (ie. backslash at end of line), strip + leading and/or trailing whitespace. All of these are optional + and independently controllable. + + Provides a 'warn()' method so you can generate warning messages that + report physical line number, even if the logical line in question + spans multiple physical lines. Also provides 'unreadline()' for + implementing line-at-a-time lookahead. + + Constructor is called as: + + TextFile (filename=None, file=None, **options) + + It bombs (RuntimeError) if both 'filename' and 'file' are None; + 'filename' should be a string, and 'file' a file object (or + something that provides 'readline()' and 'close()' methods). It is + recommended that you supply at least 'filename', so that TextFile + can include it in warning messages. If 'file' is not supplied, + TextFile creates its own using 'io.open()'. + + The options are all boolean, and affect the value returned by + 'readline()': + strip_comments [default: true] + strip from "#" to end-of-line, as well as any whitespace + leading up to the "#" -- unless it is escaped by a backslash + lstrip_ws [default: false] + strip leading whitespace from each line before returning it + rstrip_ws [default: true] + strip trailing whitespace (including line terminator!) from + each line before returning it + skip_blanks [default: true} + skip lines that are empty *after* stripping comments and + whitespace. (If both lstrip_ws and rstrip_ws are false, + then some lines may consist of solely whitespace: these will + *not* be skipped, even if 'skip_blanks' is true.) + join_lines [default: false] + if a backslash is the last non-newline character on a line + after stripping comments and whitespace, join the following line + to it to form one "logical line"; if N consecutive lines end + with a backslash, then N+1 physical lines will be joined to + form one logical line. + collapse_join [default: false] + strip leading whitespace from lines that are joined to their + predecessor; only matters if (join_lines and not lstrip_ws) + errors [default: 'strict'] + error handler used to decode the file content + + Note that since 'rstrip_ws' can strip the trailing newline, the + semantics of 'readline()' must differ from those of the builtin file + object's 'readline()' method! In particular, 'readline()' returns + None for end-of-file: an empty string might just be a blank line (or + an all-whitespace line), if 'rstrip_ws' is true but 'skip_blanks' is + not.""" + + default_options = { 'strip_comments': 1, + 'skip_blanks': 1, + 'lstrip_ws': 0, + 'rstrip_ws': 1, + 'join_lines': 0, + 'collapse_join': 0, + 'errors': 'strict', + } + + def __init__(self, filename=None, file=None, **options): + """Construct a new TextFile object. At least one of 'filename' + (a string) and 'file' (a file-like object) must be supplied. + They keyword argument options are described above and affect + the values returned by 'readline()'.""" + if filename is None and file is None: + raise RuntimeError("you must supply either or both of 'filename' and 'file'") + + # set values for all options -- either from client option hash + # or fallback to default_options + for opt in self.default_options.keys(): + if opt in options: + setattr(self, opt, options[opt]) + else: + setattr(self, opt, self.default_options[opt]) + + # sanity check client option hash + for opt in options.keys(): + if opt not in self.default_options: + raise KeyError("invalid TextFile option '%s'" % opt) + + if file is None: + self.open(filename) + else: + self.filename = filename + self.file = file + self.current_line = 0 # assuming that file is at BOF! + + # 'linebuf' is a stack of lines that will be emptied before we + # actually read from the file; it's only populated by an + # 'unreadline()' operation + self.linebuf = [] + + def open(self, filename): + """Open a new file named 'filename'. This overrides both the + 'filename' and 'file' arguments to the constructor.""" + self.filename = filename + self.file = io.open(self.filename, 'r', errors=self.errors) + self.current_line = 0 + + def close(self): + """Close the current file and forget everything we know about it + (filename, current line number).""" + file = self.file + self.file = None + self.filename = None + self.current_line = None + file.close() + + def gen_error(self, msg, line=None): + outmsg = [] + if line is None: + line = self.current_line + outmsg.append(self.filename + ", ") + if isinstance(line, (list, tuple)): + outmsg.append("lines %d-%d: " % tuple(line)) + else: + outmsg.append("line %d: " % line) + outmsg.append(str(msg)) + return "".join(outmsg) + + def error(self, msg, line=None): + raise ValueError("error: " + self.gen_error(msg, line)) + + def warn(self, msg, line=None): + """Print (to stderr) a warning message tied to the current logical + line in the current file. If the current logical line in the + file spans multiple physical lines, the warning refers to the + whole range, eg. "lines 3-5". If 'line' supplied, it overrides + the current line number; it may be a list or tuple to indicate a + range of physical lines, or an integer for a single physical + line.""" + sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n") + + def readline(self): + """Read and return a single logical line from the current file (or + from an internal buffer if lines have previously been "unread" + with 'unreadline()'). If the 'join_lines' option is true, this + may involve reading multiple physical lines concatenated into a + single string. Updates the current line number, so calling + 'warn()' after 'readline()' emits a warning about the physical + line(s) just read. Returns None on end-of-file, since the empty + string can occur if 'rstrip_ws' is true but 'strip_blanks' is + not.""" + # If any "unread" lines waiting in 'linebuf', return the top + # one. (We don't actually buffer read-ahead data -- lines only + # get put in 'linebuf' if the client explicitly does an + # 'unreadline()'. + if self.linebuf: + line = self.linebuf[-1] + del self.linebuf[-1] + return line + + buildup_line = '' + + while True: + # read the line, make it None if EOF + line = self.file.readline() + if line == '': + line = None + + if self.strip_comments and line: + + # Look for the first "#" in the line. If none, never + # mind. If we find one and it's the first character, or + # is not preceded by "\", then it starts a comment -- + # strip the comment, strip whitespace before it, and + # carry on. Otherwise, it's just an escaped "#", so + # unescape it (and any other escaped "#"'s that might be + # lurking in there) and otherwise leave the line alone. + + pos = line.find("#") + if pos == -1: # no "#" -- no comments + pass + + # It's definitely a comment -- either "#" is the first + # character, or it's elsewhere and unescaped. + elif pos == 0 or line[pos-1] != "\\": + # Have to preserve the trailing newline, because it's + # the job of a later step (rstrip_ws) to remove it -- + # and if rstrip_ws is false, we'd better preserve it! + # (NB. this means that if the final line is all comment + # and has no trailing newline, we will think that it's + # EOF; I think that's OK.) + eol = (line[-1] == '\n') and '\n' or '' + line = line[0:pos] + eol + + # If all that's left is whitespace, then skip line + # *now*, before we try to join it to 'buildup_line' -- + # that way constructs like + # hello \\ + # # comment that should be ignored + # there + # result in "hello there". + if line.strip() == "": + continue + else: # it's an escaped "#" + line = line.replace("\\#", "#") + + # did previous line end with a backslash? then accumulate + if self.join_lines and buildup_line: + # oops: end of file + if line is None: + self.warn("continuation line immediately precedes " + "end-of-file") + return buildup_line + + if self.collapse_join: + line = line.lstrip() + line = buildup_line + line + + # careful: pay attention to line number when incrementing it + if isinstance(self.current_line, list): + self.current_line[1] = self.current_line[1] + 1 + else: + self.current_line = [self.current_line, + self.current_line + 1] + # just an ordinary line, read it as usual + else: + if line is None: # eof + return None + + # still have to be careful about incrementing the line number! + if isinstance(self.current_line, list): + self.current_line = self.current_line[1] + 1 + else: + self.current_line = self.current_line + 1 + + # strip whitespace however the client wants (leading and + # trailing, or one or the other, or neither) + if self.lstrip_ws and self.rstrip_ws: + line = line.strip() + elif self.lstrip_ws: + line = line.lstrip() + elif self.rstrip_ws: + line = line.rstrip() + + # blank line (whether we rstrip'ed or not)? skip to next line + # if appropriate + if (line == '' or line == '\n') and self.skip_blanks: + continue + + if self.join_lines: + if line[-1] == '\\': + buildup_line = line[:-1] + continue + + if line[-2:] == '\\\n': + buildup_line = line[0:-2] + '\n' + continue + + # well, I guess there's some actual content there: return it + return line + + def readlines(self): + """Read and return the list of all logical lines remaining in the + current file.""" + lines = [] + while True: + line = self.readline() + if line is None: + return lines + lines.append(line) + + def unreadline(self, line): + """Push 'line' (a string) onto an internal buffer that will be + checked by future 'readline()' calls. Handy for implementing + a parser with line-at-a-time lookahead.""" + self.linebuf.append(line) diff --git a/setuptools/_distutils/unixccompiler.py b/setuptools/_distutils/unixccompiler.py new file mode 100644 index 00000000..4d7a6de7 --- /dev/null +++ b/setuptools/_distutils/unixccompiler.py @@ -0,0 +1,328 @@ +"""distutils.unixccompiler + +Contains the UnixCCompiler class, a subclass of CCompiler that handles +the "typical" Unix-style command-line C compiler: + * macros defined with -Dname[=value] + * macros undefined with -Uname + * include search directories specified with -Idir + * libraries specified with -lllib + * library search directories specified with -Ldir + * compile handled by 'cc' (or similar) executable with -c option: + compiles .c to .o + * link static library handled by 'ar' command (possibly with 'ranlib') + * link shared library handled by 'cc -shared' +""" + +import os, sys, re + +from distutils import sysconfig +from distutils.dep_util import newer +from distutils.ccompiler import \ + CCompiler, gen_preprocess_options, gen_lib_options +from distutils.errors import \ + DistutilsExecError, CompileError, LibError, LinkError +from distutils import log + +if sys.platform == 'darwin': + import _osx_support + +# XXX Things not currently handled: +# * optimization/debug/warning flags; we just use whatever's in Python's +# Makefile and live with it. Is this adequate? If not, we might +# have to have a bunch of subclasses GNUCCompiler, SGICCompiler, +# SunCCompiler, and I suspect down that road lies madness. +# * even if we don't know a warning flag from an optimization flag, +# we need some way for outsiders to feed preprocessor/compiler/linker +# flags in to us -- eg. a sysadmin might want to mandate certain flags +# via a site config file, or a user might want to set something for +# compiling this module distribution only via the setup.py command +# line, whatever. As long as these options come from something on the +# current system, they can be as system-dependent as they like, and we +# should just happily stuff them into the preprocessor/compiler/linker +# options and carry on. + + +class UnixCCompiler(CCompiler): + + compiler_type = 'unix' + + # These are used by CCompiler in two places: the constructor sets + # instance attributes 'preprocessor', 'compiler', etc. from them, and + # 'set_executable()' allows any of these to be set. The defaults here + # are pretty generic; they will probably have to be set by an outsider + # (eg. using information discovered by the sysconfig about building + # Python extensions). + executables = {'preprocessor' : None, + 'compiler' : ["cc"], + 'compiler_so' : ["cc"], + 'compiler_cxx' : ["cc"], + 'linker_so' : ["cc", "-shared"], + 'linker_exe' : ["cc"], + 'archiver' : ["ar", "-cr"], + 'ranlib' : None, + } + + if sys.platform[:6] == "darwin": + executables['ranlib'] = ["ranlib"] + + # Needed for the filename generation methods provided by the base + # class, CCompiler. NB. whoever instantiates/uses a particular + # UnixCCompiler instance should set 'shared_lib_ext' -- we set a + # reasonable common default here, but it's not necessarily used on all + # Unices! + + src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"] + obj_extension = ".o" + static_lib_extension = ".a" + shared_lib_extension = ".so" + dylib_lib_extension = ".dylib" + xcode_stub_lib_extension = ".tbd" + static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" + xcode_stub_lib_format = dylib_lib_format + if sys.platform == "cygwin": + exe_extension = ".exe" + + def preprocess(self, source, output_file=None, macros=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): + fixed_args = self._fix_compile_args(None, macros, include_dirs) + ignore, macros, include_dirs = fixed_args + pp_opts = gen_preprocess_options(macros, include_dirs) + pp_args = self.preprocessor + pp_opts + if output_file: + pp_args.extend(['-o', output_file]) + if extra_preargs: + pp_args[:0] = extra_preargs + if extra_postargs: + pp_args.extend(extra_postargs) + pp_args.append(source) + + # We need to preprocess: either we're being forced to, or we're + # generating output to stdout, or there's a target output file and + # the source file is newer than the target (or the target doesn't + # exist). + if self.force or output_file is None or newer(source, output_file): + if output_file: + self.mkpath(os.path.dirname(output_file)) + try: + self.spawn(pp_args) + except DistutilsExecError as msg: + raise CompileError(msg) + + def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): + compiler_so = self.compiler_so + if sys.platform == 'darwin': + compiler_so = _osx_support.compiler_fixup(compiler_so, + cc_args + extra_postargs) + try: + self.spawn(compiler_so + cc_args + [src, '-o', obj] + + extra_postargs) + except DistutilsExecError as msg: + raise CompileError(msg) + + def create_static_lib(self, objects, output_libname, + output_dir=None, debug=0, target_lang=None): + objects, output_dir = self._fix_object_args(objects, output_dir) + + output_filename = \ + self.library_filename(output_libname, output_dir=output_dir) + + if self._need_link(objects, output_filename): + self.mkpath(os.path.dirname(output_filename)) + self.spawn(self.archiver + + [output_filename] + + objects + self.objects) + + # Not many Unices required ranlib anymore -- SunOS 4.x is, I + # think the only major Unix that does. Maybe we need some + # platform intelligence here to skip ranlib if it's not + # needed -- or maybe Python's configure script took care of + # it for us, hence the check for leading colon. + if self.ranlib: + try: + self.spawn(self.ranlib + [output_filename]) + except DistutilsExecError as msg: + raise LibError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + def link(self, target_desc, objects, + output_filename, output_dir=None, libraries=None, + library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, + extra_postargs=None, build_temp=None, target_lang=None): + objects, output_dir = self._fix_object_args(objects, output_dir) + fixed_args = self._fix_lib_args(libraries, library_dirs, + runtime_library_dirs) + libraries, library_dirs, runtime_library_dirs = fixed_args + + lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, + libraries) + if not isinstance(output_dir, (str, type(None))): + raise TypeError("'output_dir' must be a string or None") + if output_dir is not None: + output_filename = os.path.join(output_dir, output_filename) + + if self._need_link(objects, output_filename): + ld_args = (objects + self.objects + + lib_opts + ['-o', output_filename]) + if debug: + ld_args[:0] = ['-g'] + if extra_preargs: + ld_args[:0] = extra_preargs + if extra_postargs: + ld_args.extend(extra_postargs) + self.mkpath(os.path.dirname(output_filename)) + try: + if target_desc == CCompiler.EXECUTABLE: + linker = self.linker_exe[:] + else: + linker = self.linker_so[:] + if target_lang == "c++" and self.compiler_cxx: + # skip over environment variable settings if /usr/bin/env + # is used to set up the linker's environment. + # This is needed on OSX. Note: this assumes that the + # normal and C++ compiler have the same environment + # settings. + i = 0 + if os.path.basename(linker[0]) == "env": + i = 1 + while '=' in linker[i]: + i += 1 + + if os.path.basename(linker[i]) == 'ld_so_aix': + # AIX platforms prefix the compiler with the ld_so_aix + # script, so we need to adjust our linker index + offset = 1 + else: + offset = 0 + + linker[i+offset] = self.compiler_cxx[i] + + if sys.platform == 'darwin': + linker = _osx_support.compiler_fixup(linker, ld_args) + + self.spawn(linker + ld_args) + except DistutilsExecError as msg: + raise LinkError(msg) + else: + log.debug("skipping %s (up-to-date)", output_filename) + + # -- Miscellaneous methods ----------------------------------------- + # These are all used by the 'gen_lib_options() function, in + # ccompiler.py. + + def library_dir_option(self, dir): + return "-L" + dir + + def _is_gcc(self, compiler_name): + return "gcc" in compiler_name or "g++" in compiler_name + + def runtime_library_dir_option(self, dir): + # XXX Hackish, at the very least. See Python bug #445902: + # http://sourceforge.net/tracker/index.php + # ?func=detail&aid=445902&group_id=5470&atid=105470 + # Linkers on different platforms need different options to + # specify that directories need to be added to the list of + # directories searched for dependencies when a dynamic library + # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to + # be told to pass the -R option through to the linker, whereas + # other compilers and gcc on other systems just know this. + # Other compilers may need something slightly different. At + # this time, there's no way to determine this information from + # the configuration data stored in the Python installation, so + # we use this hack. + compiler = os.path.basename(sysconfig.get_config_var("CC")) + if sys.platform[:6] == "darwin": + # MacOSX's linker doesn't understand the -R flag at all + return "-L" + dir + elif sys.platform[:7] == "freebsd": + return "-Wl,-rpath=" + dir + elif sys.platform[:5] == "hp-ux": + if self._is_gcc(compiler): + return ["-Wl,+s", "-L" + dir] + return ["+s", "-L" + dir] + else: + if self._is_gcc(compiler): + # gcc on non-GNU systems does not need -Wl, but can + # use it anyway. Since distutils has always passed in + # -Wl whenever gcc was used in the past it is probably + # safest to keep doing so. + if sysconfig.get_config_var("GNULD") == "yes": + # GNU ld needs an extra option to get a RUNPATH + # instead of just an RPATH. + return "-Wl,--enable-new-dtags,-R" + dir + else: + return "-Wl,-R" + dir + else: + # No idea how --enable-new-dtags would be passed on to + # ld if this system was using GNU ld. Don't know if a + # system like this even exists. + return "-R" + dir + + def library_option(self, lib): + return "-l" + lib + + def find_library_file(self, dirs, lib, debug=0): + shared_f = self.library_filename(lib, lib_type='shared') + dylib_f = self.library_filename(lib, lib_type='dylib') + xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub') + static_f = self.library_filename(lib, lib_type='static') + + if sys.platform == 'darwin': + # On OSX users can specify an alternate SDK using + # '-isysroot', calculate the SDK root if it is specified + # (and use it further on) + # + # Note that, as of Xcode 7, Apple SDKs may contain textual stub + # libraries with .tbd extensions rather than the normal .dylib + # shared libraries installed in /. The Apple compiler tool + # chain handles this transparently but it can cause problems + # for programs that are being built with an SDK and searching + # for specific libraries. Callers of find_library_file need to + # keep in mind that the base filename of the returned SDK library + # file might have a different extension from that of the library + # file installed on the running system, for example: + # /Applications/Xcode.app/Contents/Developer/Platforms/ + # MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/ + # usr/lib/libedit.tbd + # vs + # /usr/lib/libedit.dylib + cflags = sysconfig.get_config_var('CFLAGS') + m = re.search(r'-isysroot\s*(\S+)', cflags) + if m is None: + sysroot = '/' + else: + sysroot = m.group(1) + + + + for dir in dirs: + shared = os.path.join(dir, shared_f) + dylib = os.path.join(dir, dylib_f) + static = os.path.join(dir, static_f) + xcode_stub = os.path.join(dir, xcode_stub_f) + + if sys.platform == 'darwin' and ( + dir.startswith('/System/') or ( + dir.startswith('/usr/') and not dir.startswith('/usr/local/'))): + + shared = os.path.join(sysroot, dir[1:], shared_f) + dylib = os.path.join(sysroot, dir[1:], dylib_f) + static = os.path.join(sysroot, dir[1:], static_f) + xcode_stub = os.path.join(sysroot, dir[1:], xcode_stub_f) + + # We're second-guessing the linker here, with not much hard + # data to go on: GCC seems to prefer the shared library, so I'm + # assuming that *all* Unix C compilers do. And of course I'm + # ignoring even GCC's "-static" option. So sue me. + if os.path.exists(dylib): + return dylib + elif os.path.exists(xcode_stub): + return xcode_stub + elif os.path.exists(shared): + return shared + elif os.path.exists(static): + return static + + # Oops, didn't find it in *any* of 'dirs' + return None diff --git a/setuptools/_distutils/util.py b/setuptools/_distutils/util.py new file mode 100644 index 00000000..4b002ece --- /dev/null +++ b/setuptools/_distutils/util.py @@ -0,0 +1,559 @@ +"""distutils.util + +Miscellaneous utility functions -- anything that doesn't fit into +one of the other *util.py modules. +""" + +import os +import re +import importlib.util +import string +import sys +from distutils.errors import DistutilsPlatformError +from distutils.dep_util import newer +from distutils.spawn import spawn +from distutils import log +from distutils.errors import DistutilsByteCompileError + +def get_host_platform(): + """Return a string that identifies the current platform. This is used mainly to + distinguish platform-specific build directories and platform-specific built + distributions. Typically includes the OS name and version and the + architecture (as supplied by 'os.uname()'), although the exact information + included depends on the OS; eg. on Linux, the kernel version isn't + particularly important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + + """ + if os.name == 'nt': + if 'amd64' in sys.version.lower(): + return 'win-amd64' + if '(arm)' in sys.version.lower(): + return 'win-arm32' + if '(arm64)' in sys.version.lower(): + return 'win-arm64' + return sys.platform + + # Set for cross builds explicitly + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_HOST_PLATFORM"] + + if os.name != "posix" or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha, + # Mac OS is M68k or PPC, etc. + return sys.platform + + # Try to distinguish various flavours of Unix + + (osname, host, release, version, machine) = os.uname() + + # Convert the OS name to lowercase, remove '/' characters, and translate + # spaces (for "Power Macintosh") + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') + + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # We can't use "platform.architecture()[0]" because a + # bootstrap problem. We use a dict to get an error + # if some suspicious happens. + bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} + machine += ".%s" % bitness[sys.maxsize] + # fall through to standard osname-release-machine representation + elif osname[:3] == "aix": + from _aix_support import aix_platform + return aix_platform() + elif osname[:6] == "cygwin": + osname = "cygwin" + rel_re = re.compile (r'[\d.]+', re.ASCII) + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == "darwin": + import _osx_support, distutils.sysconfig + osname, release, machine = _osx_support.get_platform_osx( + distutils.sysconfig.get_config_vars(), + osname, release, machine) + + return "%s-%s-%s" % (osname, release, machine) + +def get_platform(): + if os.name == 'nt': + TARGET_TO_PLAT = { + 'x86' : 'win32', + 'x64' : 'win-amd64', + 'arm' : 'win-arm32', + } + return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform() + else: + return get_host_platform() + +def convert_path (pathname): + """Return 'pathname' as a name that will work on the native filesystem, + i.e. split it on '/' and put it back together again using the current + directory separator. Needed because filenames in the setup script are + always supplied in Unix style, and have to be converted to the local + convention before we can actually use them in the filesystem. Raises + ValueError on non-Unix-ish systems if 'pathname' either starts or + ends with a slash. + """ + if os.sep == '/': + return pathname + if not pathname: + return pathname + if pathname[0] == '/': + raise ValueError("path '%s' cannot be absolute" % pathname) + if pathname[-1] == '/': + raise ValueError("path '%s' cannot end with '/'" % pathname) + + paths = pathname.split('/') + while '.' in paths: + paths.remove('.') + if not paths: + return os.curdir + return os.path.join(*paths) + +# convert_path () + + +def change_root (new_root, pathname): + """Return 'pathname' with 'new_root' prepended. If 'pathname' is + relative, this is equivalent to "os.path.join(new_root,pathname)". + Otherwise, it requires making 'pathname' relative and then joining the + two, which is tricky on DOS/Windows and Mac OS. + """ + if os.name == 'posix': + if not os.path.isabs(pathname): + return os.path.join(new_root, pathname) + else: + return os.path.join(new_root, pathname[1:]) + + elif os.name == 'nt': + (drive, path) = os.path.splitdrive(pathname) + if path[0] == '\\': + path = path[1:] + return os.path.join(new_root, path) + + else: + raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) + + +_environ_checked = 0 +def check_environ (): + """Ensure that 'os.environ' has all the environment variables we + guarantee that users can use in config files, command-line options, + etc. Currently this includes: + HOME - user's home directory (Unix only) + PLAT - description of the current platform, including hardware + and OS (see 'get_platform()') + """ + global _environ_checked + if _environ_checked: + return + + if os.name == 'posix' and 'HOME' not in os.environ: + try: + import pwd + os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] + except (ImportError, KeyError): + # bpo-10496: if the current user identifier doesn't exist in the + # password database, do nothing + pass + + if 'PLAT' not in os.environ: + os.environ['PLAT'] = get_platform() + + _environ_checked = 1 + + +def subst_vars (s, local_vars): + """Perform shell/Perl-style variable substitution on 'string'. Every + occurrence of '$' followed by a name is considered a variable, and + variable is substituted by the value found in the 'local_vars' + dictionary, or in 'os.environ' if it's not in 'local_vars'. + 'os.environ' is first checked/augmented to guarantee that it contains + certain values: see 'check_environ()'. Raise ValueError for any + variables not found in either 'local_vars' or 'os.environ'. + """ + check_environ() + def _subst (match, local_vars=local_vars): + var_name = match.group(1) + if var_name in local_vars: + return str(local_vars[var_name]) + else: + return os.environ[var_name] + + try: + return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) + except KeyError as var: + raise ValueError("invalid variable '$%s'" % var) + +# subst_vars () + + +def grok_environment_error (exc, prefix="error: "): + # Function kept for backward compatibility. + # Used to try clever things with EnvironmentErrors, + # but nowadays str(exception) produces good messages. + return prefix + str(exc) + + +# Needed by 'split_quoted()' +_wordchars_re = _squote_re = _dquote_re = None +def _init_regex(): + global _wordchars_re, _squote_re, _dquote_re + _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) + _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") + _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') + +def split_quoted (s): + """Split a string up according to Unix shell-like rules for quotes and + backslashes. In short: words are delimited by spaces, as long as those + spaces are not escaped by a backslash, or inside a quoted string. + Single and double quotes are equivalent, and the quote characters can + be backslash-escaped. The backslash is stripped from any two-character + escape sequence, leaving only the escaped character. The quote + characters are stripped from any quoted string. Returns a list of + words. + """ + + # This is a nice algorithm for splitting up a single string, since it + # doesn't require character-by-character examination. It was a little + # bit of a brain-bender to get it working right, though... + if _wordchars_re is None: _init_regex() + + s = s.strip() + words = [] + pos = 0 + + while s: + m = _wordchars_re.match(s, pos) + end = m.end() + if end == len(s): + words.append(s[:end]) + break + + if s[end] in string.whitespace: # unescaped, unquoted whitespace: now + words.append(s[:end]) # we definitely have a word delimiter + s = s[end:].lstrip() + pos = 0 + + elif s[end] == '\\': # preserve whatever is being escaped; + # will become part of the current word + s = s[:end] + s[end+1:] + pos = end+1 + + else: + if s[end] == "'": # slurp singly-quoted string + m = _squote_re.match(s, end) + elif s[end] == '"': # slurp doubly-quoted string + m = _dquote_re.match(s, end) + else: + raise RuntimeError("this can't happen (bad char '%c')" % s[end]) + + if m is None: + raise ValueError("bad string (mismatched %s quotes?)" % s[end]) + + (beg, end) = m.span() + s = s[:beg] + s[beg+1:end-1] + s[end:] + pos = m.end() - 2 + + if pos >= len(s): + words.append(s) + break + + return words + +# split_quoted () + + +def execute (func, args, msg=None, verbose=0, dry_run=0): + """Perform some action that affects the outside world (eg. by + writing to the filesystem). Such actions are special because they + are disabled by the 'dry_run' flag. This method takes care of all + that bureaucracy for you; all you have to do is supply the + function to call and an argument tuple for it (to embody the + "external action" being performed), and an optional message to + print. + """ + if msg is None: + msg = "%s%r" % (func.__name__, args) + if msg[-2:] == ',)': # correct for singleton tuple + msg = msg[0:-2] + ')' + + log.info(msg) + if not dry_run: + func(*args) + + +def strtobool (val): + """Convert a string representation of truth to true (1) or false (0). + + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + """ + val = val.lower() + if val in ('y', 'yes', 't', 'true', 'on', '1'): + return 1 + elif val in ('n', 'no', 'f', 'false', 'off', '0'): + return 0 + else: + raise ValueError("invalid truth value %r" % (val,)) + + +def byte_compile (py_files, + optimize=0, force=0, + prefix=None, base_dir=None, + verbose=1, dry_run=0, + direct=None): + """Byte-compile a collection of Python source files to .pyc + files in a __pycache__ subdirectory. 'py_files' is a list + of files to compile; any files that don't end in ".py" are silently + skipped. 'optimize' must be one of the following: + 0 - don't optimize + 1 - normal optimization (like "python -O") + 2 - extra optimization (like "python -OO") + If 'force' is true, all files are recompiled regardless of + timestamps. + + The source filename encoded in each bytecode file defaults to the + filenames listed in 'py_files'; you can modify these with 'prefix' and + 'basedir'. 'prefix' is a string that will be stripped off of each + source filename, and 'base_dir' is a directory name that will be + prepended (after 'prefix' is stripped). You can supply either or both + (or neither) of 'prefix' and 'base_dir', as you wish. + + If 'dry_run' is true, doesn't actually do anything that would + affect the filesystem. + + Byte-compilation is either done directly in this interpreter process + with the standard py_compile module, or indirectly by writing a + temporary script and executing it. Normally, you should let + 'byte_compile()' figure out to use direct compilation or not (see + the source for details). The 'direct' flag is used by the script + generated in indirect mode; unless you know what you're doing, leave + it set to None. + """ + + # Late import to fix a bootstrap issue: _posixsubprocess is built by + # setup.py, but setup.py uses distutils. + import subprocess + + # nothing is done if sys.dont_write_bytecode is True + if sys.dont_write_bytecode: + raise DistutilsByteCompileError('byte-compiling is disabled.') + + # First, if the caller didn't force us into direct or indirect mode, + # figure out which mode we should be in. We take a conservative + # approach: choose direct mode *only* if the current interpreter is + # in debug mode and optimize is 0. If we're not in debug mode (-O + # or -OO), we don't know which level of optimization this + # interpreter is running with, so we can't do direct + # byte-compilation and be certain that it's the right thing. Thus, + # always compile indirectly if the current interpreter is in either + # optimize mode, or if either optimization level was requested by + # the caller. + if direct is None: + direct = (__debug__ and optimize == 0) + + # "Indirect" byte-compilation: write a temporary script and then + # run it with the appropriate flags. + if not direct: + try: + from tempfile import mkstemp + (script_fd, script_name) = mkstemp(".py") + except ImportError: + from tempfile import mktemp + (script_fd, script_name) = None, mktemp(".py") + log.info("writing byte-compilation script '%s'", script_name) + if not dry_run: + if script_fd is not None: + script = os.fdopen(script_fd, "w") + else: + script = open(script_name, "w") + + with script: + script.write("""\ +from distutils.util import byte_compile +files = [ +""") + + # XXX would be nice to write absolute filenames, just for + # safety's sake (script should be more robust in the face of + # chdir'ing before running it). But this requires abspath'ing + # 'prefix' as well, and that breaks the hack in build_lib's + # 'byte_compile()' method that carefully tacks on a trailing + # slash (os.sep really) to make sure the prefix here is "just + # right". This whole prefix business is rather delicate -- the + # problem is that it's really a directory, but I'm treating it + # as a dumb string, so trailing slashes and so forth matter. + + #py_files = map(os.path.abspath, py_files) + #if prefix: + # prefix = os.path.abspath(prefix) + + script.write(",\n".join(map(repr, py_files)) + "]\n") + script.write(""" +byte_compile(files, optimize=%r, force=%r, + prefix=%r, base_dir=%r, + verbose=%r, dry_run=0, + direct=1) +""" % (optimize, force, prefix, base_dir, verbose)) + + cmd = [sys.executable] + cmd.extend(subprocess._optim_args_from_interpreter_flags()) + cmd.append(script_name) + spawn(cmd, dry_run=dry_run) + execute(os.remove, (script_name,), "removing %s" % script_name, + dry_run=dry_run) + + # "Direct" byte-compilation: use the py_compile module to compile + # right here, right now. Note that the script generated in indirect + # mode simply calls 'byte_compile()' in direct mode, a weird sort of + # cross-process recursion. Hey, it works! + else: + from py_compile import compile + + for file in py_files: + if file[-3:] != ".py": + # This lets us be lazy and not filter filenames in + # the "install_lib" command. + continue + + # Terminology from the py_compile module: + # cfile - byte-compiled file + # dfile - purported source filename (same as 'file' by default) + if optimize >= 0: + opt = '' if optimize == 0 else optimize + cfile = importlib.util.cache_from_source( + file, optimization=opt) + else: + cfile = importlib.util.cache_from_source(file) + dfile = file + if prefix: + if file[:len(prefix)] != prefix: + raise ValueError("invalid prefix: filename %r doesn't start with %r" + % (file, prefix)) + dfile = dfile[len(prefix):] + if base_dir: + dfile = os.path.join(base_dir, dfile) + + cfile_base = os.path.basename(cfile) + if direct: + if force or newer(file, cfile): + log.info("byte-compiling %s to %s", file, cfile_base) + if not dry_run: + compile(file, cfile, dfile) + else: + log.debug("skipping byte-compilation of %s to %s", + file, cfile_base) + +# byte_compile () + +def rfc822_escape (header): + """Return a version of the string escaped for inclusion in an + RFC-822 header, by ensuring there are 8 spaces space after each newline. + """ + lines = header.split('\n') + sep = '\n' + 8 * ' ' + return sep.join(lines) + +# 2to3 support + +def run_2to3(files, fixer_names=None, options=None, explicit=None): + """Invoke 2to3 on a list of Python files. + The files should all come from the build area, as the + modification is done in-place. To reduce the build time, + only files modified since the last invocation of this + function should be passed in the files argument.""" + + if not files: + return + + # Make this class local, to delay import of 2to3 + from lib2to3.refactor import RefactoringTool, get_fixers_from_package + class DistutilsRefactoringTool(RefactoringTool): + def log_error(self, msg, *args, **kw): + log.error(msg, *args) + + def log_message(self, msg, *args): + log.info(msg, *args) + + def log_debug(self, msg, *args): + log.debug(msg, *args) + + if fixer_names is None: + fixer_names = get_fixers_from_package('lib2to3.fixes') + r = DistutilsRefactoringTool(fixer_names, options=options) + r.refactor(files, write=True) + +def copydir_run_2to3(src, dest, template=None, fixer_names=None, + options=None, explicit=None): + """Recursively copy a directory, only copying new and changed files, + running run_2to3 over all newly copied Python modules afterward. + + If you give a template string, it's parsed like a MANIFEST.in. + """ + from distutils.dir_util import mkpath + from distutils.file_util import copy_file + from distutils.filelist import FileList + filelist = FileList() + curdir = os.getcwd() + os.chdir(src) + try: + filelist.findall() + finally: + os.chdir(curdir) + filelist.files[:] = filelist.allfiles + if template: + for line in template.splitlines(): + line = line.strip() + if not line: continue + filelist.process_template_line(line) + copied = [] + for filename in filelist.files: + outname = os.path.join(dest, filename) + mkpath(os.path.dirname(outname)) + res = copy_file(os.path.join(src, filename), outname, update=1) + if res[1]: copied.append(outname) + run_2to3([fn for fn in copied if fn.lower().endswith('.py')], + fixer_names=fixer_names, options=options, explicit=explicit) + return copied + +class Mixin2to3: + '''Mixin class for commands that run 2to3. + To configure 2to3, setup scripts may either change + the class variables, or inherit from individual commands + to override how 2to3 is invoked.''' + + # provide list of fixers to run; + # defaults to all from lib2to3.fixers + fixer_names = None + + # options dictionary + options = None + + # list of fixers to invoke even though they are marked as explicit + explicit = None + + def run_2to3(self, files): + return run_2to3(files, self.fixer_names, self.options, self.explicit) diff --git a/setuptools/_distutils/version.py b/setuptools/_distutils/version.py new file mode 100644 index 00000000..c33bebae --- /dev/null +++ b/setuptools/_distutils/version.py @@ -0,0 +1,347 @@ +# +# distutils/version.py +# +# Implements multiple version numbering conventions for the +# Python Module Distribution Utilities. +# +# $Id$ +# + +"""Provides classes to represent module version numbers (one class for +each style of version numbering). There are currently two such classes +implemented: StrictVersion and LooseVersion. + +Every version number class implements the following interface: + * the 'parse' method takes a string and parses it to some internal + representation; if the string is an invalid version number, + 'parse' raises a ValueError exception + * the class constructor takes an optional string argument which, + if supplied, is passed to 'parse' + * __str__ reconstructs the string that was passed to 'parse' (or + an equivalent string -- ie. one that will generate an equivalent + version number instance) + * __repr__ generates Python code to recreate the version number instance + * _cmp compares the current instance with either another instance + of the same class or a string (which will be parsed to an instance + of the same class, thus must follow the same rules) +""" + +import re + +class Version: + """Abstract base class for version numbering classes. Just provides + constructor (__init__) and reproducer (__repr__), because those + seem to be the same for all version numbering classes; and route + rich comparisons to _cmp. + """ + + def __init__ (self, vstring=None): + if vstring: + self.parse(vstring) + + def __repr__ (self): + return "%s ('%s')" % (self.__class__.__name__, str(self)) + + def __eq__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c == 0 + + def __lt__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c < 0 + + def __le__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c <= 0 + + def __gt__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c > 0 + + def __ge__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c >= 0 + + +# Interface for version-number classes -- must be implemented +# by the following classes (the concrete ones -- Version should +# be treated as an abstract class). +# __init__ (string) - create and take same action as 'parse' +# (string parameter is optional) +# parse (string) - convert a string representation to whatever +# internal representation is appropriate for +# this style of version numbering +# __str__ (self) - convert back to a string; should be very similar +# (if not identical to) the string supplied to parse +# __repr__ (self) - generate Python code to recreate +# the instance +# _cmp (self, other) - compare two version numbers ('other' may +# be an unparsed version string, or another +# instance of your version class) + + +class StrictVersion (Version): + + """Version numbering for anal retentives and software idealists. + Implements the standard interface for version number classes as + described above. A version number consists of two or three + dot-separated numeric components, with an optional "pre-release" tag + on the end. The pre-release tag consists of the letter 'a' or 'b' + followed by a number. If the numeric components of two version + numbers are equal, then one with a pre-release tag will always + be deemed earlier (lesser) than one without. + + The following are valid version numbers (shown in the order that + would be obtained by sorting according to the supplied cmp function): + + 0.4 0.4.0 (these two are equivalent) + 0.4.1 + 0.5a1 + 0.5b3 + 0.5 + 0.9.6 + 1.0 + 1.0.4a3 + 1.0.4b1 + 1.0.4 + + The following are examples of invalid version numbers: + + 1 + 2.7.2.2 + 1.3.a4 + 1.3pl1 + 1.3c4 + + The rationale for this version numbering system will be explained + in the distutils documentation. + """ + + version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', + re.VERBOSE | re.ASCII) + + + def parse (self, vstring): + match = self.version_re.match(vstring) + if not match: + raise ValueError("invalid version number '%s'" % vstring) + + (major, minor, patch, prerelease, prerelease_num) = \ + match.group(1, 2, 4, 5, 6) + + if patch: + self.version = tuple(map(int, [major, minor, patch])) + else: + self.version = tuple(map(int, [major, minor])) + (0,) + + if prerelease: + self.prerelease = (prerelease[0], int(prerelease_num)) + else: + self.prerelease = None + + + def __str__ (self): + + if self.version[2] == 0: + vstring = '.'.join(map(str, self.version[0:2])) + else: + vstring = '.'.join(map(str, self.version)) + + if self.prerelease: + vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) + + return vstring + + + def _cmp (self, other): + if isinstance(other, str): + other = StrictVersion(other) + elif not isinstance(other, StrictVersion): + return NotImplemented + + if self.version != other.version: + # numeric versions don't match + # prerelease stuff doesn't matter + if self.version < other.version: + return -1 + else: + return 1 + + # have to compare prerelease + # case 1: neither has prerelease; they're equal + # case 2: self has prerelease, other doesn't; other is greater + # case 3: self doesn't have prerelease, other does: self is greater + # case 4: both have prerelease: must compare them! + + if (not self.prerelease and not other.prerelease): + return 0 + elif (self.prerelease and not other.prerelease): + return -1 + elif (not self.prerelease and other.prerelease): + return 1 + elif (self.prerelease and other.prerelease): + if self.prerelease == other.prerelease: + return 0 + elif self.prerelease < other.prerelease: + return -1 + else: + return 1 + else: + assert False, "never get here" + +# end class StrictVersion + + +# The rules according to Greg Stein: +# 1) a version number has 1 or more numbers separated by a period or by +# sequences of letters. If only periods, then these are compared +# left-to-right to determine an ordering. +# 2) sequences of letters are part of the tuple for comparison and are +# compared lexicographically +# 3) recognize the numeric components may have leading zeroes +# +# The LooseVersion class below implements these rules: a version number +# string is split up into a tuple of integer and string components, and +# comparison is a simple tuple comparison. This means that version +# numbers behave in a predictable and obvious way, but a way that might +# not necessarily be how people *want* version numbers to behave. There +# wouldn't be a problem if people could stick to purely numeric version +# numbers: just split on period and compare the numbers as tuples. +# However, people insist on putting letters into their version numbers; +# the most common purpose seems to be: +# - indicating a "pre-release" version +# ('alpha', 'beta', 'a', 'b', 'pre', 'p') +# - indicating a post-release patch ('p', 'pl', 'patch') +# but of course this can't cover all version number schemes, and there's +# no way to know what a programmer means without asking him. +# +# The problem is what to do with letters (and other non-numeric +# characters) in a version number. The current implementation does the +# obvious and predictable thing: keep them as strings and compare +# lexically within a tuple comparison. This has the desired effect if +# an appended letter sequence implies something "post-release": +# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002". +# +# However, if letters in a version number imply a pre-release version, +# the "obvious" thing isn't correct. Eg. you would expect that +# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison +# implemented here, this just isn't so. +# +# Two possible solutions come to mind. The first is to tie the +# comparison algorithm to a particular set of semantic rules, as has +# been done in the StrictVersion class above. This works great as long +# as everyone can go along with bondage and discipline. Hopefully a +# (large) subset of Python module programmers will agree that the +# particular flavour of bondage and discipline provided by StrictVersion +# provides enough benefit to be worth using, and will submit their +# version numbering scheme to its domination. The free-thinking +# anarchists in the lot will never give in, though, and something needs +# to be done to accommodate them. +# +# Perhaps a "moderately strict" version class could be implemented that +# lets almost anything slide (syntactically), and makes some heuristic +# assumptions about non-digits in version number strings. This could +# sink into special-case-hell, though; if I was as talented and +# idiosyncratic as Larry Wall, I'd go ahead and implement a class that +# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is +# just as happy dealing with things like "2g6" and "1.13++". I don't +# think I'm smart enough to do it right though. +# +# In any case, I've coded the test suite for this module (see +# ../test/test_version.py) specifically to fail on things like comparing +# "1.2a2" and "1.2". That's not because the *code* is doing anything +# wrong, it's because the simple, obvious design doesn't match my +# complicated, hairy expectations for real-world version numbers. It +# would be a snap to fix the test suite to say, "Yep, LooseVersion does +# the Right Thing" (ie. the code matches the conception). But I'd rather +# have a conception that matches common notions about version numbers. + +class LooseVersion (Version): + + """Version numbering for anarchists and software realists. + Implements the standard interface for version number classes as + described above. A version number consists of a series of numbers, + separated by either periods or strings of letters. When comparing + version numbers, the numeric components will be compared + numerically, and the alphabetic components lexically. The following + are all valid version numbers, in no particular order: + + 1.5.1 + 1.5.2b2 + 161 + 3.10a + 8.02 + 3.4j + 1996.07.12 + 3.2.pl0 + 3.1.1.6 + 2g6 + 11g + 0.960923 + 2.2beta29 + 1.13++ + 5.5.kw + 2.0b1pl0 + + In fact, there is no such thing as an invalid version number under + this scheme; the rules for comparison are simple and predictable, + but may not always give the results you want (for some definition + of "want"). + """ + + component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) + + def __init__ (self, vstring=None): + if vstring: + self.parse(vstring) + + + def parse (self, vstring): + # I've given up on thinking I can reconstruct the version string + # from the parsed tuple -- so I just store the string here for + # use by __str__ + self.vstring = vstring + components = [x for x in self.component_re.split(vstring) + if x and x != '.'] + for i, obj in enumerate(components): + try: + components[i] = int(obj) + except ValueError: + pass + + self.version = components + + + def __str__ (self): + return self.vstring + + + def __repr__ (self): + return "LooseVersion ('%s')" % str(self) + + + def _cmp (self, other): + if isinstance(other, str): + other = LooseVersion(other) + elif not isinstance(other, LooseVersion): + return NotImplemented + + if self.version == other.version: + return 0 + if self.version < other.version: + return -1 + if self.version > other.version: + return 1 + + +# end class LooseVersion diff --git a/setuptools/_distutils/versionpredicate.py b/setuptools/_distutils/versionpredicate.py new file mode 100644 index 00000000..062c98f2 --- /dev/null +++ b/setuptools/_distutils/versionpredicate.py @@ -0,0 +1,166 @@ +"""Module for parsing and testing package version predicate strings. +""" +import re +import distutils.version +import operator + + +re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)", + re.ASCII) +# (package) (rest) + +re_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses +re_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") +# (comp) (version) + + +def splitUp(pred): + """Parse a single version comparison. + + Return (comparison string, StrictVersion) + """ + res = re_splitComparison.match(pred) + if not res: + raise ValueError("bad package restriction syntax: %r" % pred) + comp, verStr = res.groups() + return (comp, distutils.version.StrictVersion(verStr)) + +compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq, + ">": operator.gt, ">=": operator.ge, "!=": operator.ne} + +class VersionPredicate: + """Parse and test package version predicates. + + >>> v = VersionPredicate('pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)') + + The `name` attribute provides the full dotted name that is given:: + + >>> v.name + 'pyepat.abc' + + The str() of a `VersionPredicate` provides a normalized + human-readable version of the expression:: + + >>> print(v) + pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) + + The `satisfied_by()` method can be used to determine with a given + version number is included in the set described by the version + restrictions:: + + >>> v.satisfied_by('1.1') + True + >>> v.satisfied_by('1.4') + True + >>> v.satisfied_by('1.0') + False + >>> v.satisfied_by('4444.4') + False + >>> v.satisfied_by('1555.1b3') + False + + `VersionPredicate` is flexible in accepting extra whitespace:: + + >>> v = VersionPredicate(' pat( == 0.1 ) ') + >>> v.name + 'pat' + >>> v.satisfied_by('0.1') + True + >>> v.satisfied_by('0.2') + False + + If any version numbers passed in do not conform to the + restrictions of `StrictVersion`, a `ValueError` is raised:: + + >>> v = VersionPredicate('p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)') + Traceback (most recent call last): + ... + ValueError: invalid version number '1.2zb3' + + It the module or package name given does not conform to what's + allowed as a legal module or package name, `ValueError` is + raised:: + + >>> v = VersionPredicate('foo-bar') + Traceback (most recent call last): + ... + ValueError: expected parenthesized list: '-bar' + + >>> v = VersionPredicate('foo bar (12.21)') + Traceback (most recent call last): + ... + ValueError: expected parenthesized list: 'bar (12.21)' + + """ + + def __init__(self, versionPredicateStr): + """Parse a version predicate string. + """ + # Fields: + # name: package name + # pred: list of (comparison string, StrictVersion) + + versionPredicateStr = versionPredicateStr.strip() + if not versionPredicateStr: + raise ValueError("empty package restriction") + match = re_validPackage.match(versionPredicateStr) + if not match: + raise ValueError("bad package name in %r" % versionPredicateStr) + self.name, paren = match.groups() + paren = paren.strip() + if paren: + match = re_paren.match(paren) + if not match: + raise ValueError("expected parenthesized list: %r" % paren) + str = match.groups()[0] + self.pred = [splitUp(aPred) for aPred in str.split(",")] + if not self.pred: + raise ValueError("empty parenthesized list in %r" + % versionPredicateStr) + else: + self.pred = [] + + def __str__(self): + if self.pred: + seq = [cond + " " + str(ver) for cond, ver in self.pred] + return self.name + " (" + ", ".join(seq) + ")" + else: + return self.name + + def satisfied_by(self, version): + """True if version is compatible with all the predicates in self. + The parameter version must be acceptable to the StrictVersion + constructor. It may be either a string or StrictVersion. + """ + for cond, ver in self.pred: + if not compmap[cond](version, ver): + return False + return True + + +_provision_rx = None + +def split_provision(value): + """Return the name and optional version number of a provision. + + The version number, if given, will be returned as a `StrictVersion` + instance, otherwise it will be `None`. + + >>> split_provision('mypkg') + ('mypkg', None) + >>> split_provision(' mypkg( 1.2 ) ') + ('mypkg', StrictVersion ('1.2')) + """ + global _provision_rx + if _provision_rx is None: + _provision_rx = re.compile( + r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", + re.ASCII) + value = value.strip() + m = _provision_rx.match(value) + if not m: + raise ValueError("illegal provides specification: %r" % value) + ver = m.group(2) or None + if ver: + ver = distutils.version.StrictVersion(ver) + return m.group(1), ver diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index f9e63798..06eed82f 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -8,19 +8,7 @@ for more motivation. import sys import re import importlib -import contextlib import warnings -from os.path import dirname - - -@contextlib.contextmanager -def patch_sys_path(): - orig = sys.path[:] - sys.path[:] = [dirname(dirname(__file__))] - try: - yield - finally: - sys.path[:] = orig def clear_distutils(): @@ -34,9 +22,12 @@ def clear_distutils(): def ensure_local_distutils(): clear_distutils() - with patch_sys_path(): - importlib.import_module('distutils') - assert sys.modules['distutils'].local + distutils = importlib.import_module('setuptools._distutils') + sys.modules['distutils'] = distutils + + # sanity check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ ensure_local_distutils() -- cgit v1.2.1 From 97192962e89a24a02effd1f7a541108335517253 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 04:54:40 -0400 Subject: Ensure the module is named 'distutils'. Avoids errors when distutils.log and setuptools._distutils.log are two separate modules with separate state. --- setuptools/distutils_patch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index 06eed82f..b2095fba 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -23,6 +23,7 @@ def clear_distutils(): def ensure_local_distutils(): clear_distutils() distutils = importlib.import_module('setuptools._distutils') + distutils.__name__ = 'distutils' sys.modules['distutils'] = distutils # sanity check that submodules load as expected -- cgit v1.2.1 From 78928efa890109410b315b03a225c9fc5f041ff8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 05:03:35 -0400 Subject: =?UTF-8?q?Bump=20version:=2047.3.1=20=E2=86=92=2047.3.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2071.misc.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2071.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 9b106cfa..88701727 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 47.3.1 +current_version = 47.3.2 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 799163ed..a8ddb657 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v47.3.2 +------- + +* #2071: Replaced references to the deprecated imp package with references to importlib + + v47.3.1 ------- diff --git a/changelog.d/2071.misc.rst b/changelog.d/2071.misc.rst deleted file mode 100644 index eb36480c..00000000 --- a/changelog.d/2071.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Replaced references to the deprecated imp package with references to importlib diff --git a/setup.cfg b/setup.cfg index f23714b6..cd62393c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 47.3.1 +version = 47.3.2 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From ca0065fb62c1728cb42b86ae79375533856d2248 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 05:13:12 -0400 Subject: Update changelog. --- changelog.d/2143.breaking.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/2143.breaking.rst b/changelog.d/2143.breaking.rst index 29030b32..244a6989 100644 --- a/changelog.d/2143.breaking.rst +++ b/changelog.d/2143.breaking.rst @@ -1 +1 @@ -Setuptools adopts distutils from the standard library as a top-level ``distutils`` package and no longer depends on distutils in the standard library. This new ``distutils`` package will be masked by ``distutils`` in the standard library unless that library has been stripped by a downstream package manager or gets removed in a future version. Although this change is not expected to break any use cases, it will likely affect tool chains that are monkey-patching distutils or relying on Setuptools' own monkey-patching of distutils. +Setuptools adopts distutils from the Python 3.9 standard library and no longer depends on distutils in the standard library. When importing ``setuptools`` or ``setuptools.distutils_patch``, Setuptools will expose its bundled version as a top-level ``distutils`` package (and unload any previously-imported top-level distutils package), retaining the expectation that ``distutils``' objects are actually Setuptools objects. Although this change is not expected to break any use cases, it will likely affect tool chains that are monkey-patching distutils or relying on Setuptools' own monkey-patching of distutils. -- cgit v1.2.1 From 776fa9ffa471863f8d9502ca95872099394c506c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 11:07:49 -0400 Subject: =?UTF-8?q?Bump=20version:=2047.3.2=20=E2=86=92=2048.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2143.breaking.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2143.breaking.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 88701727..bee22c9e 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 47.3.2 +current_version = 48.0.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index a8ddb657..d653d889 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v48.0.0 +------- + +* #2143: Setuptools adopts distutils from the Python 3.9 standard library and no longer depends on distutils in the standard library. When importing ``setuptools`` or ``setuptools.distutils_patch``, Setuptools will expose its bundled version as a top-level ``distutils`` package (and unload any previously-imported top-level distutils package), retaining the expectation that ``distutils``' objects are actually Setuptools objects. Although this change is not expected to break any use cases, it will likely affect tool chains that are monkey-patching distutils or relying on Setuptools' own monkey-patching of distutils. + + v47.3.2 ------- diff --git a/changelog.d/2143.breaking.rst b/changelog.d/2143.breaking.rst deleted file mode 100644 index 244a6989..00000000 --- a/changelog.d/2143.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -Setuptools adopts distutils from the Python 3.9 standard library and no longer depends on distutils in the standard library. When importing ``setuptools`` or ``setuptools.distutils_patch``, Setuptools will expose its bundled version as a top-level ``distutils`` package (and unload any previously-imported top-level distutils package), retaining the expectation that ``distutils``' objects are actually Setuptools objects. Although this change is not expected to break any use cases, it will likely affect tool chains that are monkey-patching distutils or relying on Setuptools' own monkey-patching of distutils. diff --git a/setup.cfg b/setup.cfg index cd62393c..05638dbc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 47.3.2 +version = 48.0.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 08e858804d6ef515b3bcea85e1b6e25a40487986 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 12:12:23 -0400 Subject: Update changelog. --- changelog.d/2137.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2137.change.rst diff --git a/changelog.d/2137.change.rst b/changelog.d/2137.change.rst new file mode 100644 index 00000000..fb5f5075 --- /dev/null +++ b/changelog.d/2137.change.rst @@ -0,0 +1 @@ +Removed (private) pkg_resources.RequirementParseError, now replaced by packaging.requirements.InvalidRequirement. Kept the name for compatibility, but users should catch InvalidRequirement instead. -- cgit v1.2.1 From 9372bf7d7f63136758196dd86688f00602b7a494 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 12:55:46 -0400 Subject: Keep the full path for each entry when enumerating entries for a candidate path. --- pkg_resources/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 3c826eb0..5df23e5b 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2057,7 +2057,10 @@ def find_on_path(importer, path_item, only=False): ) return - entries = safe_listdir(path_item) + entries = ( + os.path.join(path_item, child) + for child in safe_listdir(path_item) + ) # for performance, before sorting by version, # screen entries for only those that will yield -- cgit v1.2.1 From c30aca86261cac34b60e679a1ee430dbc968582c Mon Sep 17 00:00:00 2001 From: Omer Ozarslan Date: Thu, 4 Jun 2020 11:49:32 -0500 Subject: Update vendor packaging in pkg_resources to v19.2 --- pkg_resources/_vendor/packaging/__about__.py | 14 +- pkg_resources/_vendor/packaging/__init__.py | 20 +- pkg_resources/_vendor/packaging/_compat.py | 7 +- pkg_resources/_vendor/packaging/_structures.py | 4 +- pkg_resources/_vendor/packaging/markers.py | 91 +++--- pkg_resources/_vendor/packaging/requirements.py | 41 ++- pkg_resources/_vendor/packaging/specifiers.py | 71 ++--- pkg_resources/_vendor/packaging/tags.py | 404 ++++++++++++++++++++++++ pkg_resources/_vendor/packaging/utils.py | 43 +++ pkg_resources/_vendor/packaging/version.py | 149 +++++---- pkg_resources/_vendor/vendored.txt | 2 +- 11 files changed, 660 insertions(+), 186 deletions(-) create mode 100644 pkg_resources/_vendor/packaging/tags.py diff --git a/pkg_resources/_vendor/packaging/__about__.py b/pkg_resources/_vendor/packaging/__about__.py index 95d330ef..dc95138d 100644 --- a/pkg_resources/_vendor/packaging/__about__.py +++ b/pkg_resources/_vendor/packaging/__about__.py @@ -4,18 +4,24 @@ from __future__ import absolute_import, division, print_function __all__ = [ - "__title__", "__summary__", "__uri__", "__version__", "__author__", - "__email__", "__license__", "__copyright__", + "__title__", + "__summary__", + "__uri__", + "__version__", + "__author__", + "__email__", + "__license__", + "__copyright__", ] __title__ = "packaging" __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "16.8" +__version__ = "19.2" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" __license__ = "BSD or Apache License, Version 2.0" -__copyright__ = "Copyright 2014-2016 %s" % __author__ +__copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/pkg_resources/_vendor/packaging/__init__.py b/pkg_resources/_vendor/packaging/__init__.py index 5ee62202..a0cf67df 100644 --- a/pkg_resources/_vendor/packaging/__init__.py +++ b/pkg_resources/_vendor/packaging/__init__.py @@ -4,11 +4,23 @@ from __future__ import absolute_import, division, print_function from .__about__ import ( - __author__, __copyright__, __email__, __license__, __summary__, __title__, - __uri__, __version__ + __author__, + __copyright__, + __email__, + __license__, + __summary__, + __title__, + __uri__, + __version__, ) __all__ = [ - "__title__", "__summary__", "__uri__", "__version__", "__author__", - "__email__", "__license__", "__copyright__", + "__title__", + "__summary__", + "__uri__", + "__version__", + "__author__", + "__email__", + "__license__", + "__copyright__", ] diff --git a/pkg_resources/_vendor/packaging/_compat.py b/pkg_resources/_vendor/packaging/_compat.py index 210bb80b..25da473c 100644 --- a/pkg_resources/_vendor/packaging/_compat.py +++ b/pkg_resources/_vendor/packaging/_compat.py @@ -12,9 +12,9 @@ PY3 = sys.version_info[0] == 3 # flake8: noqa if PY3: - string_types = str, + string_types = (str,) else: - string_types = basestring, + string_types = (basestring,) def with_metaclass(meta, *bases): @@ -27,4 +27,5 @@ def with_metaclass(meta, *bases): class metaclass(meta): def __new__(cls, name, this_bases, d): return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) + + return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/pkg_resources/_vendor/packaging/_structures.py b/pkg_resources/_vendor/packaging/_structures.py index ccc27861..68dcca63 100644 --- a/pkg_resources/_vendor/packaging/_structures.py +++ b/pkg_resources/_vendor/packaging/_structures.py @@ -5,7 +5,6 @@ from __future__ import absolute_import, division, print_function class Infinity(object): - def __repr__(self): return "Infinity" @@ -33,11 +32,11 @@ class Infinity(object): def __neg__(self): return NegativeInfinity + Infinity = Infinity() class NegativeInfinity(object): - def __repr__(self): return "-Infinity" @@ -65,4 +64,5 @@ class NegativeInfinity(object): def __neg__(self): return Infinity + NegativeInfinity = NegativeInfinity() diff --git a/pkg_resources/_vendor/packaging/markers.py b/pkg_resources/_vendor/packaging/markers.py index 892e578e..733123fb 100644 --- a/pkg_resources/_vendor/packaging/markers.py +++ b/pkg_resources/_vendor/packaging/markers.py @@ -17,8 +17,11 @@ from .specifiers import Specifier, InvalidSpecifier __all__ = [ - "InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName", - "Marker", "default_environment", + "InvalidMarker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "Marker", + "default_environment", ] @@ -42,7 +45,6 @@ class UndefinedEnvironmentName(ValueError): class Node(object): - def __init__(self, value): self.value = value @@ -57,62 +59,52 @@ class Node(object): class Variable(Node): - def serialize(self): return str(self) class Value(Node): - def serialize(self): return '"{0}"'.format(self) class Op(Node): - def serialize(self): return str(self) VARIABLE = ( - L("implementation_version") | - L("platform_python_implementation") | - L("implementation_name") | - L("python_full_version") | - L("platform_release") | - L("platform_version") | - L("platform_machine") | - L("platform_system") | - L("python_version") | - L("sys_platform") | - L("os_name") | - L("os.name") | # PEP-345 - L("sys.platform") | # PEP-345 - L("platform.version") | # PEP-345 - L("platform.machine") | # PEP-345 - L("platform.python_implementation") | # PEP-345 - L("python_implementation") | # undocumented setuptools legacy - L("extra") + L("implementation_version") + | L("platform_python_implementation") + | L("implementation_name") + | L("python_full_version") + | L("platform_release") + | L("platform_version") + | L("platform_machine") + | L("platform_system") + | L("python_version") + | L("sys_platform") + | L("os_name") + | L("os.name") + | L("sys.platform") # PEP-345 + | L("platform.version") # PEP-345 + | L("platform.machine") # PEP-345 + | L("platform.python_implementation") # PEP-345 + | L("python_implementation") # PEP-345 + | L("extra") # undocumented setuptools legacy ) ALIASES = { - 'os.name': 'os_name', - 'sys.platform': 'sys_platform', - 'platform.version': 'platform_version', - 'platform.machine': 'platform_machine', - 'platform.python_implementation': 'platform_python_implementation', - 'python_implementation': 'platform_python_implementation' + "os.name": "os_name", + "sys.platform": "sys_platform", + "platform.version": "platform_version", + "platform.machine": "platform_machine", + "platform.python_implementation": "platform_python_implementation", + "python_implementation": "platform_python_implementation", } VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) VERSION_CMP = ( - L("===") | - L("==") | - L(">=") | - L("<=") | - L("!=") | - L("~=") | - L(">") | - L("<") + L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") ) MARKER_OP = VERSION_CMP | L("not in") | L("in") @@ -152,8 +144,11 @@ def _format_marker(marker, first=True): # where the single item is itself it's own list. In that case we want skip # the rest of this function so that we don't get extraneous () on the # outside. - if (isinstance(marker, list) and len(marker) == 1 and - isinstance(marker[0], (list, tuple))): + if ( + isinstance(marker, list) + and len(marker) == 1 + and isinstance(marker[0], (list, tuple)) + ): return _format_marker(marker[0]) if isinstance(marker, list): @@ -239,20 +234,20 @@ def _evaluate_markers(markers, environment): def format_full_version(info): - version = '{0.major}.{0.minor}.{0.micro}'.format(info) + version = "{0.major}.{0.minor}.{0.micro}".format(info) kind = info.releaselevel - if kind != 'final': + if kind != "final": version += kind[0] + str(info.serial) return version def default_environment(): - if hasattr(sys, 'implementation'): + if hasattr(sys, "implementation"): iver = format_full_version(sys.implementation.version) implementation_name = sys.implementation.name else: - iver = '0' - implementation_name = '' + iver = "0" + implementation_name = "" return { "implementation_name": implementation_name, @@ -264,19 +259,19 @@ def default_environment(): "platform_version": platform.version(), "python_full_version": platform.python_version(), "platform_python_implementation": platform.python_implementation(), - "python_version": platform.python_version()[:3], + "python_version": ".".join(platform.python_version_tuple()[:2]), "sys_platform": sys.platform, } class Marker(object): - def __init__(self, marker): try: self._markers = _coerce_parse_result(MARKER.parseString(marker)) except ParseException as e: err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( - marker, marker[e.loc:e.loc + 8]) + marker, marker[e.loc : e.loc + 8] + ) raise InvalidMarker(err_str) def __str__(self): diff --git a/pkg_resources/_vendor/packaging/requirements.py b/pkg_resources/_vendor/packaging/requirements.py index 0c8c4a38..c3424dcb 100644 --- a/pkg_resources/_vendor/packaging/requirements.py +++ b/pkg_resources/_vendor/packaging/requirements.py @@ -38,8 +38,8 @@ IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) NAME = IDENTIFIER("name") EXTRA = IDENTIFIER -URI = Regex(r'[^ ]+')("url") -URL = (AT + URI) +URI = Regex(r"[^ ]+")("url") +URL = AT + URI EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") @@ -48,28 +48,31 @@ VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY -VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), - joinString=",", adjacent=False)("_raw_spec") +VERSION_MANY = Combine( + VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False +)("_raw_spec") _VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) -_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '') +_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") MARKER_EXPR.setParseAction( - lambda s, l, t: Marker(s[t._original_start:t._original_end]) + lambda s, l, t: Marker(s[t._original_start : t._original_end]) ) -MARKER_SEPERATOR = SEMICOLON -MARKER = MARKER_SEPERATOR + MARKER_EXPR +MARKER_SEPARATOR = SEMICOLON +MARKER = MARKER_SEPARATOR + MARKER_EXPR VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) URL_AND_MARKER = URL + Optional(MARKER) -NAMED_REQUIREMENT = \ - NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) +NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd +# pkg_resources.extern.pyparsing isn't thread safe during initialization, so we do it eagerly, see +# issue #104 +REQUIREMENT.parseString("x[]") class Requirement(object): @@ -90,15 +93,21 @@ class Requirement(object): req = REQUIREMENT.parseString(requirement_string) except ParseException as e: raise InvalidRequirement( - "Invalid requirement, parse error at \"{0!r}\"".format( - requirement_string[e.loc:e.loc + 8])) + 'Parse error at "{0!r}": {1}'.format( + requirement_string[e.loc : e.loc + 8], e.msg + ) + ) self.name = req.name if req.url: parsed_url = urlparse.urlparse(req.url) - if not (parsed_url.scheme and parsed_url.netloc) or ( - not parsed_url.scheme and not parsed_url.netloc): - raise InvalidRequirement("Invalid URL given") + if parsed_url.scheme == "file": + if urlparse.urlunparse(parsed_url) != req.url: + raise InvalidRequirement("Invalid URL given") + elif not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc + ): + raise InvalidRequirement("Invalid URL: {0}".format(req.url)) self.url = req.url else: self.url = None @@ -117,6 +126,8 @@ class Requirement(object): if self.url: parts.append("@ {0}".format(self.url)) + if self.marker: + parts.append(" ") if self.marker: parts.append("; {0}".format(self.marker)) diff --git a/pkg_resources/_vendor/packaging/specifiers.py b/pkg_resources/_vendor/packaging/specifiers.py index 7f5a76cf..743576a0 100644 --- a/pkg_resources/_vendor/packaging/specifiers.py +++ b/pkg_resources/_vendor/packaging/specifiers.py @@ -19,7 +19,6 @@ class InvalidSpecifier(ValueError): class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): - @abc.abstractmethod def __str__(self): """ @@ -84,10 +83,7 @@ class _IndividualSpecifier(BaseSpecifier): if not match: raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - self._spec = ( - match.group("operator").strip(), - match.group("version").strip(), - ) + self._spec = (match.group("operator").strip(), match.group("version").strip()) # Store whether or not this Specifier should accept prereleases self._prereleases = prereleases @@ -99,11 +95,7 @@ class _IndividualSpecifier(BaseSpecifier): else "" ) - return "<{0}({1!r}{2})>".format( - self.__class__.__name__, - str(self), - pre, - ) + return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) def __str__(self): return "{0}{1}".format(*self._spec) @@ -194,11 +186,12 @@ class _IndividualSpecifier(BaseSpecifier): # If our version is a prerelease, and we were not set to allow # prereleases, then we'll store it for later incase nothing # else matches this specifier. - if (parsed_version.is_prerelease and not - (prereleases or self.prereleases)): + if parsed_version.is_prerelease and not ( + prereleases or self.prereleases + ): found_prereleases.append(version) # Either this is not a prerelease, or we should have been - # accepting prereleases from the begining. + # accepting prereleases from the beginning. else: yielded = True yield version @@ -213,8 +206,7 @@ class _IndividualSpecifier(BaseSpecifier): class LegacySpecifier(_IndividualSpecifier): - _regex_str = ( - r""" + _regex_str = r""" (?P(==|!=|<=|>=|<|>)) \s* (?P @@ -225,10 +217,8 @@ class LegacySpecifier(_IndividualSpecifier): # them, and a comma since it's a version separator. ) """ - ) - _regex = re.compile( - r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) _operators = { "==": "equal", @@ -269,13 +259,13 @@ def _require_version_compare(fn): if not isinstance(prospective, Version): return False return fn(self, prospective, spec) + return wrapped class Specifier(_IndividualSpecifier): - _regex_str = ( - r""" + _regex_str = r""" (?P(~=|==|!=|<=|>=|<|>|===)) (?P (?: @@ -367,10 +357,8 @@ class Specifier(_IndividualSpecifier): ) ) """ - ) - _regex = re.compile( - r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) _operators = { "~=": "compatible", @@ -397,8 +385,7 @@ class Specifier(_IndividualSpecifier): prefix = ".".join( list( itertools.takewhile( - lambda x: (not x.startswith("post") and not - x.startswith("dev")), + lambda x: (not x.startswith("post") and not x.startswith("dev")), _version_split(spec), ) )[:-1] @@ -407,8 +394,9 @@ class Specifier(_IndividualSpecifier): # Add the prefix notation to the end of our string prefix += ".*" - return (self._get_operator(">=")(prospective, spec) and - self._get_operator("==")(prospective, prefix)) + return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( + prospective, prefix + ) @_require_version_compare def _compare_equal(self, prospective, spec): @@ -428,7 +416,7 @@ class Specifier(_IndividualSpecifier): # Shorten the prospective version to be the same length as the spec # so that we can determine if the specifier is a prefix of the # prospective version or not. - prospective = prospective[:len(spec)] + prospective = prospective[: len(spec)] # Pad out our two sides with zeros so that they both equal the same # length. @@ -503,7 +491,7 @@ class Specifier(_IndividualSpecifier): return False # Ensure that we do not allow a local version of the version mentioned - # in the specifier, which is techincally greater than, to match. + # in the specifier, which is technically greater than, to match. if prospective.local is not None: if Version(prospective.base_version) == Version(spec.base_version): return False @@ -567,27 +555,17 @@ def _pad_version(left, right): right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) # Get the rest of our versions - left_split.append(left[len(left_split[0]):]) - right_split.append(right[len(right_split[0]):]) + left_split.append(left[len(left_split[0]) :]) + right_split.append(right[len(right_split[0]) :]) # Insert our padding - left_split.insert( - 1, - ["0"] * max(0, len(right_split[0]) - len(left_split[0])), - ) - right_split.insert( - 1, - ["0"] * max(0, len(left_split[0]) - len(right_split[0])), - ) + left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) + right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) - return ( - list(itertools.chain(*left_split)), - list(itertools.chain(*right_split)), - ) + return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) class SpecifierSet(BaseSpecifier): - def __init__(self, specifiers="", prereleases=None): # Split on , to break each indidivual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. @@ -721,10 +699,7 @@ class SpecifierSet(BaseSpecifier): # given version is contained within all of them. # Note: This use of all() here means that an empty set of specifiers # will always return True, this is an explicit design decision. - return all( - s.contains(item, prereleases=prereleases) - for s in self._specs - ) + return all(s.contains(item, prereleases=prereleases) for s in self._specs) def filter(self, iterable, prereleases=None): # Determine if we're forcing a prerelease or not, if we're not forcing diff --git a/pkg_resources/_vendor/packaging/tags.py b/pkg_resources/_vendor/packaging/tags.py new file mode 100644 index 00000000..ec9942f0 --- /dev/null +++ b/pkg_resources/_vendor/packaging/tags.py @@ -0,0 +1,404 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import + +import distutils.util + +try: + from importlib.machinery import EXTENSION_SUFFIXES +except ImportError: # pragma: no cover + import imp + + EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] + del imp +import platform +import re +import sys +import sysconfig +import warnings + + +INTERPRETER_SHORT_NAMES = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} + + +_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 + + +class Tag(object): + + __slots__ = ["_interpreter", "_abi", "_platform"] + + def __init__(self, interpreter, abi, platform): + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + + @property + def interpreter(self): + return self._interpreter + + @property + def abi(self): + return self._abi + + @property + def platform(self): + return self._platform + + def __eq__(self, other): + return ( + (self.platform == other.platform) + and (self.abi == other.abi) + and (self.interpreter == other.interpreter) + ) + + def __hash__(self): + return hash((self._interpreter, self._abi, self._platform)) + + def __str__(self): + return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) + + def __repr__(self): + return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) + + +def parse_tag(tag): + tags = set() + interpreters, abis, platforms = tag.split("-") + for interpreter in interpreters.split("."): + for abi in abis.split("."): + for platform_ in platforms.split("."): + tags.add(Tag(interpreter, abi, platform_)) + return frozenset(tags) + + +def _normalize_string(string): + return string.replace(".", "_").replace("-", "_") + + +def _cpython_interpreter(py_version): + # TODO: Is using py_version_nodot for interpreter version critical? + return "cp{major}{minor}".format(major=py_version[0], minor=py_version[1]) + + +def _cpython_abis(py_version): + abis = [] + version = "{}{}".format(*py_version[:2]) + debug = pymalloc = ucs4 = "" + with_debug = sysconfig.get_config_var("Py_DEBUG") + has_refcount = hasattr(sys, "gettotalrefcount") + # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled + # extension modules is the best option. + # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 + has_ext = "_d.pyd" in EXTENSION_SUFFIXES + if with_debug or (with_debug is None and (has_refcount or has_ext)): + debug = "d" + if py_version < (3, 8): + with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC") + if with_pymalloc or with_pymalloc is None: + pymalloc = "m" + if py_version < (3, 3): + unicode_size = sysconfig.get_config_var("Py_UNICODE_SIZE") + if unicode_size == 4 or ( + unicode_size is None and sys.maxunicode == 0x10FFFF + ): + ucs4 = "u" + elif debug: + # Debug builds can also load "normal" extension modules. + # We can also assume no UCS-4 or pymalloc requirement. + abis.append("cp{version}".format(version=version)) + abis.insert( + 0, + "cp{version}{debug}{pymalloc}{ucs4}".format( + version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 + ), + ) + return abis + + +def _cpython_tags(py_version, interpreter, abis, platforms): + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): + yield tag + for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): + yield tag + # PEP 384 was first implemented in Python 3.2. + for minor_version in range(py_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{major}{minor}".format( + major=py_version[0], minor=minor_version + ) + yield Tag(interpreter, "abi3", platform_) + + +def _pypy_interpreter(): + return "pp{py_major}{pypy_major}{pypy_minor}".format( + py_major=sys.version_info[0], + pypy_major=sys.pypy_version_info.major, + pypy_minor=sys.pypy_version_info.minor, + ) + + +def _generic_abi(): + abi = sysconfig.get_config_var("SOABI") + if abi: + return _normalize_string(abi) + else: + return "none" + + +def _pypy_tags(py_version, interpreter, abi, platforms): + for tag in (Tag(interpreter, abi, platform) for platform in platforms): + yield tag + for tag in (Tag(interpreter, "none", platform) for platform in platforms): + yield tag + + +def _generic_tags(interpreter, py_version, abi, platforms): + for tag in (Tag(interpreter, abi, platform) for platform in platforms): + yield tag + if abi != "none": + tags = (Tag(interpreter, "none", platform_) for platform_ in platforms) + for tag in tags: + yield tag + + +def _py_interpreter_range(py_version): + """ + Yield Python versions in descending order. + + After the latest version, the major-only version will be yielded, and then + all following versions up to 'end'. + """ + yield "py{major}{minor}".format(major=py_version[0], minor=py_version[1]) + yield "py{major}".format(major=py_version[0]) + for minor in range(py_version[1] - 1, -1, -1): + yield "py{major}{minor}".format(major=py_version[0], minor=minor) + + +def _independent_tags(interpreter, py_version, platforms): + """ + Return the sequence of tags that are consistent across implementations. + + The tags consist of: + - py*-none- + - -none-any + - py*-none-any + """ + for version in _py_interpreter_range(py_version): + for platform_ in platforms: + yield Tag(version, "none", platform_) + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(py_version): + yield Tag(version, "none", "any") + + +def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version, cpu_arch): + formats = [cpu_arch] + if cpu_arch == "x86_64": + if version < (10, 4): + return [] + formats.extend(["intel", "fat64", "fat32"]) + + elif cpu_arch == "i386": + if version < (10, 4): + return [] + formats.extend(["intel", "fat32", "fat"]) + + elif cpu_arch == "ppc64": + # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? + if version > (10, 5) or version < (10, 4): + return [] + formats.append("fat64") + + elif cpu_arch == "ppc": + if version > (10, 6): + return [] + formats.extend(["fat32", "fat"]) + + formats.append("universal") + return formats + + +def _mac_platforms(version=None, arch=None): + version_str, _, cpu_arch = platform.mac_ver() + if version is None: + version = tuple(map(int, version_str.split(".")[:2])) + if arch is None: + arch = _mac_arch(cpu_arch) + platforms = [] + for minor_version in range(version[1], -1, -1): + compat_version = version[0], minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + platforms.append( + "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + ) + return platforms + + +# From PEP 513. +def _is_manylinux_compatible(name, glibc_version): + # Check for presence of _manylinux module. + try: + import _manylinux + + return bool(getattr(_manylinux, name + "_compatible")) + except (ImportError, AttributeError): + # Fall through to heuristic check below. + pass + + return _have_compatible_glibc(*glibc_version) + + +def _glibc_version_string(): + # Returns glibc version string, or None if not using glibc. + import ctypes + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +# Separated out from have_compatible_glibc for easier unit testing. +def _check_glibc_version(version_str, required_major, minimum_minor): + # Parse string and check against requested version. + # + # We use a regexp instead of str.split because we want to discard any + # random junk that might come after the minor version -- this might happen + # in patched/forked versions of glibc (e.g. Linaro's version of glibc + # uses version strings like "2.20-2014.11"). See gh-3588. + m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) + if not m: + warnings.warn( + "Expected glibc version with 2 components major.minor," + " got: %s" % version_str, + RuntimeWarning, + ) + return False + return ( + int(m.group("major")) == required_major + and int(m.group("minor")) >= minimum_minor + ) + + +def _have_compatible_glibc(required_major, minimum_minor): + version_str = _glibc_version_string() + if version_str is None: + return False + return _check_glibc_version(version_str, required_major, minimum_minor) + + +def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): + linux = _normalize_string(distutils.util.get_platform()) + if linux == "linux_x86_64" and is_32bit: + linux = "linux_i686" + manylinux_support = ( + ("manylinux2014", (2, 17)), # CentOS 7 w/ glibc 2.17 (PEP 599) + ("manylinux2010", (2, 12)), # CentOS 6 w/ glibc 2.12 (PEP 571) + ("manylinux1", (2, 5)), # CentOS 5 w/ glibc 2.5 (PEP 513) + ) + manylinux_support_iter = iter(manylinux_support) + for name, glibc_version in manylinux_support_iter: + if _is_manylinux_compatible(name, glibc_version): + platforms = [linux.replace("linux", name)] + break + else: + platforms = [] + # Support for a later manylinux implies support for an earlier version. + platforms += [linux.replace("linux", name) for name, _ in manylinux_support_iter] + platforms.append(linux) + return platforms + + +def _generic_platforms(): + platform = _normalize_string(distutils.util.get_platform()) + return [platform] + + +def _interpreter_name(): + name = platform.python_implementation().lower() + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def _generic_interpreter(name, py_version): + version = sysconfig.get_config_var("py_version_nodot") + if not version: + version = "".join(map(str, py_version[:2])) + return "{name}{version}".format(name=name, version=version) + + +def sys_tags(): + """ + Returns the sequence of tag triples for the running interpreter. + + The order of the sequence corresponds to priority order for the + interpreter, from most to least important. + """ + py_version = sys.version_info[:2] + interpreter_name = _interpreter_name() + if platform.system() == "Darwin": + platforms = _mac_platforms() + elif platform.system() == "Linux": + platforms = _linux_platforms() + else: + platforms = _generic_platforms() + + if interpreter_name == "cp": + interpreter = _cpython_interpreter(py_version) + abis = _cpython_abis(py_version) + for tag in _cpython_tags(py_version, interpreter, abis, platforms): + yield tag + elif interpreter_name == "pp": + interpreter = _pypy_interpreter() + abi = _generic_abi() + for tag in _pypy_tags(py_version, interpreter, abi, platforms): + yield tag + else: + interpreter = _generic_interpreter(interpreter_name, py_version) + abi = _generic_abi() + for tag in _generic_tags(interpreter, py_version, abi, platforms): + yield tag + for tag in _independent_tags(interpreter, py_version, platforms): + yield tag diff --git a/pkg_resources/_vendor/packaging/utils.py b/pkg_resources/_vendor/packaging/utils.py index 942387ce..88418786 100644 --- a/pkg_resources/_vendor/packaging/utils.py +++ b/pkg_resources/_vendor/packaging/utils.py @@ -5,6 +5,8 @@ from __future__ import absolute_import, division, print_function import re +from .version import InvalidVersion, Version + _canonicalize_regex = re.compile(r"[-_.]+") @@ -12,3 +14,44 @@ _canonicalize_regex = re.compile(r"[-_.]+") def canonicalize_name(name): # This is taken from PEP 503. return _canonicalize_regex.sub("-", name).lower() + + +def canonicalize_version(version): + """ + This is very similar to Version.__str__, but has one subtle differences + with the way it handles the release segment. + """ + + try: + version = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + + parts = [] + + # Epoch + if version.epoch != 0: + parts.append("{0}!".format(version.epoch)) + + # Release segment + # NB: This strips trailing '.0's to normalize + parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) + + # Pre-release + if version.pre is not None: + parts.append("".join(str(x) for x in version.pre)) + + # Post-release + if version.post is not None: + parts.append(".post{0}".format(version.post)) + + # Development release + if version.dev is not None: + parts.append(".dev{0}".format(version.dev)) + + # Local version segment + if version.local is not None: + parts.append("+{0}".format(version.local)) + + return "".join(parts) diff --git a/pkg_resources/_vendor/packaging/version.py b/pkg_resources/_vendor/packaging/version.py index 83b5ee8c..95157a1f 100644 --- a/pkg_resources/_vendor/packaging/version.py +++ b/pkg_resources/_vendor/packaging/version.py @@ -10,14 +10,11 @@ import re from ._structures import Infinity -__all__ = [ - "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN" -] +__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] _Version = collections.namedtuple( - "_Version", - ["epoch", "release", "dev", "pre", "post", "local"], + "_Version", ["epoch", "release", "dev", "pre", "post", "local"] ) @@ -40,7 +37,6 @@ class InvalidVersion(ValueError): class _BaseVersion(object): - def __hash__(self): return hash(self._key) @@ -70,7 +66,6 @@ class _BaseVersion(object): class LegacyVersion(_BaseVersion): - def __init__(self, version): self._version = str(version) self._key = _legacy_cmpkey(self._version) @@ -89,6 +84,26 @@ class LegacyVersion(_BaseVersion): def base_version(self): return self._version + @property + def epoch(self): + return -1 + + @property + def release(self): + return None + + @property + def pre(self): + return None + + @property + def post(self): + return None + + @property + def dev(self): + return None + @property def local(self): return None @@ -101,13 +116,19 @@ class LegacyVersion(_BaseVersion): def is_postrelease(self): return False + @property + def is_devrelease(self): + return False -_legacy_version_component_re = re.compile( - r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, -) + +_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) _legacy_version_replacement_map = { - "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", + "pre": "c", + "preview": "c", + "-": "final-", + "rc": "c", + "dev": "@", } @@ -154,6 +175,7 @@ def _legacy_cmpkey(version): return epoch, parts + # Deliberately not anchored to the start and end of the string, to make it # easier for 3rd party code to reuse VERSION_PATTERN = r""" @@ -190,10 +212,7 @@ VERSION_PATTERN = r""" class Version(_BaseVersion): - _regex = re.compile( - r"^\s*" + VERSION_PATTERN + r"\s*$", - re.VERBOSE | re.IGNORECASE, - ) + _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) def __init__(self, version): # Validate the version and parse it into pieces @@ -205,18 +224,11 @@ class Version(_BaseVersion): self._version = _Version( epoch=int(match.group("epoch")) if match.group("epoch") else 0, release=tuple(int(i) for i in match.group("release").split(".")), - pre=_parse_letter_version( - match.group("pre_l"), - match.group("pre_n"), - ), + pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")), post=_parse_letter_version( - match.group("post_l"), - match.group("post_n1") or match.group("post_n2"), - ), - dev=_parse_letter_version( - match.group("dev_l"), - match.group("dev_n"), + match.group("post_l"), match.group("post_n1") or match.group("post_n2") ), + dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")), local=_parse_local_version(match.group("local")), ) @@ -237,32 +249,57 @@ class Version(_BaseVersion): parts = [] # Epoch - if self._version.epoch != 0: - parts.append("{0}!".format(self._version.epoch)) + if self.epoch != 0: + parts.append("{0}!".format(self.epoch)) # Release segment - parts.append(".".join(str(x) for x in self._version.release)) + parts.append(".".join(str(x) for x in self.release)) # Pre-release - if self._version.pre is not None: - parts.append("".join(str(x) for x in self._version.pre)) + if self.pre is not None: + parts.append("".join(str(x) for x in self.pre)) # Post-release - if self._version.post is not None: - parts.append(".post{0}".format(self._version.post[1])) + if self.post is not None: + parts.append(".post{0}".format(self.post)) # Development release - if self._version.dev is not None: - parts.append(".dev{0}".format(self._version.dev[1])) + if self.dev is not None: + parts.append(".dev{0}".format(self.dev)) # Local version segment - if self._version.local is not None: - parts.append( - "+{0}".format(".".join(str(x) for x in self._version.local)) - ) + if self.local is not None: + parts.append("+{0}".format(self.local)) return "".join(parts) + @property + def epoch(self): + return self._version.epoch + + @property + def release(self): + return self._version.release + + @property + def pre(self): + return self._version.pre + + @property + def post(self): + return self._version.post[1] if self._version.post else None + + @property + def dev(self): + return self._version.dev[1] if self._version.dev else None + + @property + def local(self): + if self._version.local: + return ".".join(str(x) for x in self._version.local) + else: + return None + @property def public(self): return str(self).split("+", 1)[0] @@ -272,27 +309,25 @@ class Version(_BaseVersion): parts = [] # Epoch - if self._version.epoch != 0: - parts.append("{0}!".format(self._version.epoch)) + if self.epoch != 0: + parts.append("{0}!".format(self.epoch)) # Release segment - parts.append(".".join(str(x) for x in self._version.release)) + parts.append(".".join(str(x) for x in self.release)) return "".join(parts) - @property - def local(self): - version_string = str(self) - if "+" in version_string: - return version_string.split("+", 1)[1] - @property def is_prerelease(self): - return bool(self._version.dev or self._version.pre) + return self.dev is not None or self.pre is not None @property def is_postrelease(self): - return bool(self._version.post) + return self.post is not None + + @property + def is_devrelease(self): + return self.dev is not None def _parse_letter_version(letter, number): @@ -326,7 +361,7 @@ def _parse_letter_version(letter, number): return letter, int(number) -_local_version_seperators = re.compile(r"[\._-]") +_local_version_separators = re.compile(r"[\._-]") def _parse_local_version(local): @@ -336,7 +371,7 @@ def _parse_local_version(local): if local is not None: return tuple( part.lower() if not part.isdigit() else int(part) - for part in _local_version_seperators.split(local) + for part in _local_version_separators.split(local) ) @@ -347,12 +382,7 @@ def _cmpkey(epoch, release, pre, post, dev, local): # re-reverse it back into the correct order and make it a tuple and use # that for our sorting key. release = tuple( - reversed(list( - itertools.dropwhile( - lambda x: x == 0, - reversed(release), - ) - )) + reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) ) # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. @@ -385,9 +415,6 @@ def _cmpkey(epoch, release, pre, post, dev, local): # - Numeric segments sort numerically # - Shorter versions sort before longer versions when the prefixes # match exactly - local = tuple( - (i, "") if isinstance(i, int) else (-Infinity, i) - for i in local - ) + local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local) return epoch, release, pre, post, dev, local diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt index 7f4f4087..f581a623 100644 --- a/pkg_resources/_vendor/vendored.txt +++ b/pkg_resources/_vendor/vendored.txt @@ -1,4 +1,4 @@ -packaging==16.8 +packaging==19.2 pyparsing==2.2.1 six==1.10.0 appdirs==1.4.3 -- cgit v1.2.1 From d24eb7fd34594be65ab6bf1b08ac6ffffa4a16f2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 14:35:52 -0400 Subject: Update changelog. --- changelog.d/2180.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2180.change.rst diff --git a/changelog.d/2180.change.rst b/changelog.d/2180.change.rst new file mode 100644 index 00000000..0e660cd9 --- /dev/null +++ b/changelog.d/2180.change.rst @@ -0,0 +1 @@ +Update vendored packaging in pkg_resources to 19.2. -- cgit v1.2.1 From e7e5817e442b01cb199bfc75c7098fb198fdc3ba Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 15:35:32 -0400 Subject: =?UTF-8?q?Bump=20version:=2048.0.0=20=E2=86=92=2049.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 9 +++++++++ changelog.d/2137.change.rst | 1 - changelog.d/2165.breaking.rst | 1 - changelog.d/2180.change.rst | 1 - changelog.d/2199.misc.rst | 1 - setup.cfg | 2 +- 7 files changed, 11 insertions(+), 6 deletions(-) delete mode 100644 changelog.d/2137.change.rst delete mode 100644 changelog.d/2165.breaking.rst delete mode 100644 changelog.d/2180.change.rst delete mode 100644 changelog.d/2199.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index bee22c9e..05ff5f18 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 48.0.0 +current_version = 49.0.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index d653d889..bd67931d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,12 @@ +v49.0.0 +------- + +* #2165: Setuptools no longer installs a site.py file during easy_install or develop installs. As a result, .eggs on PYTHONPATH will no longer take precedence over other packages on sys.path. If this issue affects your production environment, please reach out to the maintainers at #2165. +* #2137: Removed (private) pkg_resources.RequirementParseError, now replaced by packaging.requirements.InvalidRequirement. Kept the name for compatibility, but users should catch InvalidRequirement instead. +* #2180: Update vendored packaging in pkg_resources to 19.2. +* #2199: Fix exception causes all over the codebase by using ``raise new_exception from old_exception`` + + v48.0.0 ------- diff --git a/changelog.d/2137.change.rst b/changelog.d/2137.change.rst deleted file mode 100644 index fb5f5075..00000000 --- a/changelog.d/2137.change.rst +++ /dev/null @@ -1 +0,0 @@ -Removed (private) pkg_resources.RequirementParseError, now replaced by packaging.requirements.InvalidRequirement. Kept the name for compatibility, but users should catch InvalidRequirement instead. diff --git a/changelog.d/2165.breaking.rst b/changelog.d/2165.breaking.rst deleted file mode 100644 index a9b598b9..00000000 --- a/changelog.d/2165.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -Setuptools no longer installs a site.py file during easy_install or develop installs. As a result, .eggs on PYTHONPATH will no longer take precedence over other packages on sys.path. If this issue affects your production environment, please reach out to the maintainers at #2165. diff --git a/changelog.d/2180.change.rst b/changelog.d/2180.change.rst deleted file mode 100644 index 0e660cd9..00000000 --- a/changelog.d/2180.change.rst +++ /dev/null @@ -1 +0,0 @@ -Update vendored packaging in pkg_resources to 19.2. diff --git a/changelog.d/2199.misc.rst b/changelog.d/2199.misc.rst deleted file mode 100644 index f795256b..00000000 --- a/changelog.d/2199.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Fix exception causes all over the codebase by using ``raise new_exception from old_exception`` \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 05638dbc..d7324186 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 48.0.0 +version = 49.0.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 58cc03759ab225bfa9767cd04caf69ca014cd353 Mon Sep 17 00:00:00 2001 From: cajhne Date: Fri, 3 Jul 2020 21:30:43 +0100 Subject: Add logo resources --- docs/logo/README.md | 10 + docs/logo/josefinsans/.uuid | 1 + docs/logo/josefinsans/DESCRIPTION.en_us.html | 9 + docs/logo/josefinsans/JosefinSans-Bold.ttf | Bin 0 -> 86300 bytes docs/logo/josefinsans/JosefinSans-BoldItalic.ttf | Bin 0 -> 83100 bytes docs/logo/josefinsans/JosefinSans-Italic.ttf | Bin 0 -> 84916 bytes docs/logo/josefinsans/JosefinSans-Light.ttf | Bin 0 -> 87320 bytes docs/logo/josefinsans/JosefinSans-LightItalic.ttf | Bin 0 -> 85684 bytes docs/logo/josefinsans/JosefinSans-Regular.ttf | Bin 0 -> 87260 bytes docs/logo/josefinsans/JosefinSans-SemiBold.ttf | Bin 0 -> 87880 bytes .../josefinsans/JosefinSans-SemiBoldItalic.ttf | Bin 0 -> 84824 bytes docs/logo/josefinsans/JosefinSans-Thin.ttf | Bin 0 -> 88088 bytes docs/logo/josefinsans/JosefinSans-ThinItalic.ttf | Bin 0 -> 85420 bytes docs/logo/josefinsans/METADATA.pb | 99 +++++++++ docs/logo/josefinsans/OFL.txt | 93 +++++++++ docs/logo/setup_tools_logo_colour.svg | 227 +++++++++++++++++++++ docs/logo/setup_tools_logo_colour_1000px.png | Bin 0 -> 31520 bytes docs/logo/setup_tools_logo_colour_banner_1line.svg | 223 ++++++++++++++++++++ ...setup_tools_logo_colour_banner_1line_1000px.png | Bin 0 -> 24183 bytes .../logo/setup_tools_logo_colour_banner_2lines.svg | 224 ++++++++++++++++++++ ...etup_tools_logo_colour_banner_2lines_1000px.png | Bin 0 -> 38105 bytes docs/logo/setup_tools_logo_symbol_colour.svg | 203 ++++++++++++++++++ .../logo/setup_tools_logo_symbol_colour_1000px.png | Bin 0 -> 44239 bytes docs/logo/setup_tools_logotype_1line.svg | 169 +++++++++++++++ 24 files changed, 1258 insertions(+) create mode 100644 docs/logo/README.md create mode 100644 docs/logo/josefinsans/.uuid create mode 100644 docs/logo/josefinsans/DESCRIPTION.en_us.html create mode 100644 docs/logo/josefinsans/JosefinSans-Bold.ttf create mode 100644 docs/logo/josefinsans/JosefinSans-BoldItalic.ttf create mode 100644 docs/logo/josefinsans/JosefinSans-Italic.ttf create mode 100644 docs/logo/josefinsans/JosefinSans-Light.ttf create mode 100644 docs/logo/josefinsans/JosefinSans-LightItalic.ttf create mode 100644 docs/logo/josefinsans/JosefinSans-Regular.ttf create mode 100644 docs/logo/josefinsans/JosefinSans-SemiBold.ttf create mode 100644 docs/logo/josefinsans/JosefinSans-SemiBoldItalic.ttf create mode 100644 docs/logo/josefinsans/JosefinSans-Thin.ttf create mode 100644 docs/logo/josefinsans/JosefinSans-ThinItalic.ttf create mode 100644 docs/logo/josefinsans/METADATA.pb create mode 100644 docs/logo/josefinsans/OFL.txt create mode 100644 docs/logo/setup_tools_logo_colour.svg create mode 100644 docs/logo/setup_tools_logo_colour_1000px.png create mode 100644 docs/logo/setup_tools_logo_colour_banner_1line.svg create mode 100644 docs/logo/setup_tools_logo_colour_banner_1line_1000px.png create mode 100644 docs/logo/setup_tools_logo_colour_banner_2lines.svg create mode 100644 docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png create mode 100644 docs/logo/setup_tools_logo_symbol_colour.svg create mode 100644 docs/logo/setup_tools_logo_symbol_colour_1000px.png create mode 100644 docs/logo/setup_tools_logotype_1line.svg diff --git a/docs/logo/README.md b/docs/logo/README.md new file mode 100644 index 00000000..88e6647c --- /dev/null +++ b/docs/logo/README.md @@ -0,0 +1,10 @@ +![](setup_tools_logo_colour.svg) +### Design: + +SetupTools logo designed in 2020 by [C.Rogers](crogersmedia.com) for the SetupTools project using the Free Open Source graphics editor [Inkscape](inkscape.org). + +### Copyright: +Logo is (c) the SetupTools developers. + +### Font: +The font used is the Open Font "Josefin Sans", which is available for free under the Open Font License (OFL). diff --git a/docs/logo/josefinsans/.uuid b/docs/logo/josefinsans/.uuid new file mode 100644 index 00000000..d7e92c77 --- /dev/null +++ b/docs/logo/josefinsans/.uuid @@ -0,0 +1 @@ +922c129c-9f4c-4831-b632-c7f43be6feb0 \ No newline at end of file diff --git a/docs/logo/josefinsans/DESCRIPTION.en_us.html b/docs/logo/josefinsans/DESCRIPTION.en_us.html new file mode 100644 index 00000000..9364b249 --- /dev/null +++ b/docs/logo/josefinsans/DESCRIPTION.en_us.html @@ -0,0 +1,9 @@ +

+The idea of this typeface is to be geometric, elegant, with a vintage feeling, for use at larger sizes. +It is inspired by geometric sans serif designs from the 1920s. +The x-height is half way from baseline to cap height, an unusual proportion. +

+

+There is a sister family, Josefin Slab +

+ diff --git a/docs/logo/josefinsans/JosefinSans-Bold.ttf b/docs/logo/josefinsans/JosefinSans-Bold.ttf new file mode 100644 index 00000000..12a7ad08 Binary files /dev/null and b/docs/logo/josefinsans/JosefinSans-Bold.ttf differ diff --git a/docs/logo/josefinsans/JosefinSans-BoldItalic.ttf b/docs/logo/josefinsans/JosefinSans-BoldItalic.ttf new file mode 100644 index 00000000..4a0fc91d Binary files /dev/null and b/docs/logo/josefinsans/JosefinSans-BoldItalic.ttf differ diff --git a/docs/logo/josefinsans/JosefinSans-Italic.ttf b/docs/logo/josefinsans/JosefinSans-Italic.ttf new file mode 100644 index 00000000..1cbe036e Binary files /dev/null and b/docs/logo/josefinsans/JosefinSans-Italic.ttf differ diff --git a/docs/logo/josefinsans/JosefinSans-Light.ttf b/docs/logo/josefinsans/JosefinSans-Light.ttf new file mode 100644 index 00000000..7fe3f7be Binary files /dev/null and b/docs/logo/josefinsans/JosefinSans-Light.ttf differ diff --git a/docs/logo/josefinsans/JosefinSans-LightItalic.ttf b/docs/logo/josefinsans/JosefinSans-LightItalic.ttf new file mode 100644 index 00000000..8dc35383 Binary files /dev/null and b/docs/logo/josefinsans/JosefinSans-LightItalic.ttf differ diff --git a/docs/logo/josefinsans/JosefinSans-Regular.ttf b/docs/logo/josefinsans/JosefinSans-Regular.ttf new file mode 100644 index 00000000..ed119008 Binary files /dev/null and b/docs/logo/josefinsans/JosefinSans-Regular.ttf differ diff --git a/docs/logo/josefinsans/JosefinSans-SemiBold.ttf b/docs/logo/josefinsans/JosefinSans-SemiBold.ttf new file mode 100644 index 00000000..b67504a6 Binary files /dev/null and b/docs/logo/josefinsans/JosefinSans-SemiBold.ttf differ diff --git a/docs/logo/josefinsans/JosefinSans-SemiBoldItalic.ttf b/docs/logo/josefinsans/JosefinSans-SemiBoldItalic.ttf new file mode 100644 index 00000000..24a27d15 Binary files /dev/null and b/docs/logo/josefinsans/JosefinSans-SemiBoldItalic.ttf differ diff --git a/docs/logo/josefinsans/JosefinSans-Thin.ttf b/docs/logo/josefinsans/JosefinSans-Thin.ttf new file mode 100644 index 00000000..c54ca7f1 Binary files /dev/null and b/docs/logo/josefinsans/JosefinSans-Thin.ttf differ diff --git a/docs/logo/josefinsans/JosefinSans-ThinItalic.ttf b/docs/logo/josefinsans/JosefinSans-ThinItalic.ttf new file mode 100644 index 00000000..6840b7c2 Binary files /dev/null and b/docs/logo/josefinsans/JosefinSans-ThinItalic.ttf differ diff --git a/docs/logo/josefinsans/METADATA.pb b/docs/logo/josefinsans/METADATA.pb new file mode 100644 index 00000000..8b67716c --- /dev/null +++ b/docs/logo/josefinsans/METADATA.pb @@ -0,0 +1,99 @@ +name: "Josefin Sans" +designer: "Santiago Orozco" +license: "OFL" +category: "SANS_SERIF" +date_added: "2010-11-17" +fonts { + name: "Josefin Sans" + style: "normal" + weight: 100 + filename: "JosefinSans-Thin.ttf" + post_script_name: "JosefinSans-Thin" + full_name: "Josefin Sans Thin" + copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." +} +fonts { + name: "Josefin Sans" + style: "italic" + weight: 100 + filename: "JosefinSans-ThinItalic.ttf" + post_script_name: "JosefinSans-ThinItalic" + full_name: "Josefin Sans Thin Italic" + copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." +} +fonts { + name: "Josefin Sans" + style: "normal" + weight: 300 + filename: "JosefinSans-Light.ttf" + post_script_name: "JosefinSans-Light" + full_name: "Josefin Sans Light" + copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." +} +fonts { + name: "Josefin Sans" + style: "italic" + weight: 300 + filename: "JosefinSans-LightItalic.ttf" + post_script_name: "JosefinSans-LightItalic" + full_name: "Josefin Sans Light Italic" + copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." +} +fonts { + name: "Josefin Sans" + style: "normal" + weight: 400 + filename: "JosefinSans-Regular.ttf" + post_script_name: "JosefinSans-Regular" + full_name: "Josefin Sans Regular" + copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." +} +fonts { + name: "Josefin Sans" + style: "italic" + weight: 400 + filename: "JosefinSans-Italic.ttf" + post_script_name: "JosefinSans-Italic" + full_name: "Josefin Sans Italic" + copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." +} +fonts { + name: "Josefin Sans" + style: "normal" + weight: 600 + filename: "JosefinSans-SemiBold.ttf" + post_script_name: "JosefinSans-SemiBold" + full_name: "Josefin Sans SemiBold" + copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." +} +fonts { + name: "Josefin Sans" + style: "italic" + weight: 600 + filename: "JosefinSans-SemiBoldItalic.ttf" + post_script_name: "JosefinSans-SemiBoldItalic" + full_name: "Josefin Sans SemiBold Italic" + copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." +} +fonts { + name: "Josefin Sans" + style: "normal" + weight: 700 + filename: "JosefinSans-Bold.ttf" + post_script_name: "JosefinSans-Bold" + full_name: "Josefin Sans Bold" + copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." +} +fonts { + name: "Josefin Sans" + style: "italic" + weight: 700 + filename: "JosefinSans-BoldItalic.ttf" + post_script_name: "JosefinSans-BoldItalic" + full_name: "Josefin Sans Bold Italic" + copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." +} +subsets: "latin" +subsets: "latin-ext" +subsets: "menu" +subsets: "vietnamese" diff --git a/docs/logo/josefinsans/OFL.txt b/docs/logo/josefinsans/OFL.txt new file mode 100644 index 00000000..6586a7e3 --- /dev/null +++ b/docs/logo/josefinsans/OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2010, Santiago Orozco (hi@typemade.mx) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/logo/setup_tools_logo_colour.svg b/docs/logo/setup_tools_logo_colour.svg new file mode 100644 index 00000000..7eae8fc3 --- /dev/null +++ b/docs/logo/setup_tools_logo_colour.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/docs/logo/setup_tools_logo_colour_1000px.png b/docs/logo/setup_tools_logo_colour_1000px.png new file mode 100644 index 00000000..c25a23b9 Binary files /dev/null and b/docs/logo/setup_tools_logo_colour_1000px.png differ diff --git a/docs/logo/setup_tools_logo_colour_banner_1line.svg b/docs/logo/setup_tools_logo_colour_banner_1line.svg new file mode 100644 index 00000000..d6dc9aac --- /dev/null +++ b/docs/logo/setup_tools_logo_colour_banner_1line.svg @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png b/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png new file mode 100644 index 00000000..d1604289 Binary files /dev/null and b/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png differ diff --git a/docs/logo/setup_tools_logo_colour_banner_2lines.svg b/docs/logo/setup_tools_logo_colour_banner_2lines.svg new file mode 100644 index 00000000..991577fb --- /dev/null +++ b/docs/logo/setup_tools_logo_colour_banner_2lines.svg @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png b/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png new file mode 100644 index 00000000..89370a9d Binary files /dev/null and b/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png differ diff --git a/docs/logo/setup_tools_logo_symbol_colour.svg b/docs/logo/setup_tools_logo_symbol_colour.svg new file mode 100644 index 00000000..2936cbb5 --- /dev/null +++ b/docs/logo/setup_tools_logo_symbol_colour.svg @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/docs/logo/setup_tools_logo_symbol_colour_1000px.png b/docs/logo/setup_tools_logo_symbol_colour_1000px.png new file mode 100644 index 00000000..e0c36fc1 Binary files /dev/null and b/docs/logo/setup_tools_logo_symbol_colour_1000px.png differ diff --git a/docs/logo/setup_tools_logotype_1line.svg b/docs/logo/setup_tools_logotype_1line.svg new file mode 100644 index 00000000..dbb9c1de --- /dev/null +++ b/docs/logo/setup_tools_logotype_1line.svg @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + -- cgit v1.2.1 From 812fc057934b119c9680d8e79c9860bbc36c8b5b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 18:07:21 -0400 Subject: Disable adopted distutils while troubleshooting #2228 and #2230. --- setuptools/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 7a1f9f4f..a6cbe132 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -3,7 +3,8 @@ import os import functools -import setuptools.distutils_patch # noqa: F401 +# Disabled for now due to: #2228, #2230 +# import setuptools.distutils_patch # noqa: F401 import distutils.core import distutils.filelist -- cgit v1.2.1 From 8c85de4eeffc88b00f367fecd7caba81a0351f45 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 18:08:20 -0400 Subject: Update changelog. Ref #2228 and #2230. --- changelog.d/2228.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2228.change.rst diff --git a/changelog.d/2228.change.rst b/changelog.d/2228.change.rst new file mode 100644 index 00000000..82cf8a05 --- /dev/null +++ b/changelog.d/2228.change.rst @@ -0,0 +1 @@ +Disabled distutils adoption for now while emergent issues are addressed. -- cgit v1.2.1 From d0c71ab32deea2be265a8fcdec020089a8d1d987 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 18:12:39 -0400 Subject: Omit distutils_patch from test collection. --- conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conftest.py b/conftest.py index 72edcf14..50cc6509 100644 --- a/conftest.py +++ b/conftest.py @@ -15,6 +15,7 @@ collect_ignore = [ 'tests/manual_test.py', 'setuptools/tests/mod_with_constant.py', 'setuptools/_distutils', + 'setuptools/distutils_patch.py', ] -- cgit v1.2.1 From 4beda61c99b9063cc2b4b8a9f099a842d99a3b6b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 18:17:36 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.0.0=20=E2=86=92=2049.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2228.change.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2228.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 05ff5f18..f2f2b271 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.0.0 +current_version = 49.1.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index bd67931d..26586fbd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v49.1.0 +------- + +* #2228: Disabled distutils adoption for now while emergent issues are addressed. + + v49.0.0 ------- diff --git a/changelog.d/2228.change.rst b/changelog.d/2228.change.rst deleted file mode 100644 index 82cf8a05..00000000 --- a/changelog.d/2228.change.rst +++ /dev/null @@ -1 +0,0 @@ -Disabled distutils adoption for now while emergent issues are addressed. diff --git a/setup.cfg b/setup.cfg index d7324186..1f347c3f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.0.0 +version = 49.1.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From bd1102648109c85c782286787e4d5290ae280abe Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 20:48:15 -0400 Subject: Amend changelog for 48.0 to include more detail about usage expectations. Ref #2230. --- CHANGES.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 26586fbd..95dd2d24 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -16,7 +16,8 @@ v49.0.0 v48.0.0 ------- -* #2143: Setuptools adopts distutils from the Python 3.9 standard library and no longer depends on distutils in the standard library. When importing ``setuptools`` or ``setuptools.distutils_patch``, Setuptools will expose its bundled version as a top-level ``distutils`` package (and unload any previously-imported top-level distutils package), retaining the expectation that ``distutils``' objects are actually Setuptools objects. Although this change is not expected to break any use cases, it will likely affect tool chains that are monkey-patching distutils or relying on Setuptools' own monkey-patching of distutils. +* #2143: Setuptools adopts distutils from the Python 3.9 standard library and no longer depends on distutils in the standard library. When importing ``setuptools`` or ``setuptools.distutils_patch``, Setuptools will expose its bundled version as a top-level ``distutils`` package (and unload any previously-imported top-level distutils package), retaining the expectation that ``distutils``' objects are actually Setuptools objects. + To avoid getting any legacy behavior from the standard library, projects are advised to always "import setuptools" prior to importing anything from distutils. This behavior happens by default when using ``pip install`` or ``pep517.build``. Workflows that rely on ``setup.py (anything)`` will need to first ensure setuptools is imported. One way to achieve this behavior without modifying code is to invoke Python thus: ``python -c "import setuptools; exec(open('setup.py').read())" (anything)``. v47.3.2 -- cgit v1.2.1 From 223b405e1433751dfb695dcb99b124e46c716a2a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 4 Jul 2020 11:25:28 -0400 Subject: Use lowercase 't' for consistency in branding. --- docs/logo/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/logo/README.md b/docs/logo/README.md index 88e6647c..74fadbf0 100644 --- a/docs/logo/README.md +++ b/docs/logo/README.md @@ -1,10 +1,10 @@ ![](setup_tools_logo_colour.svg) ### Design: -SetupTools logo designed in 2020 by [C.Rogers](crogersmedia.com) for the SetupTools project using the Free Open Source graphics editor [Inkscape](inkscape.org). +Setuptools logo designed in 2020 by [C.Rogers](crogersmedia.com) for the Setuptools project using the Free Open Source graphics editor [Inkscape](inkscape.org). ### Copyright: -Logo is (c) the SetupTools developers. +Logo is (c) the Setuptools developers. ### Font: The font used is the Open Font "Josefin Sans", which is available for free under the Open Font License (OFL). -- cgit v1.2.1 From fd94cd038d644ff27866301f6f365a3ef0901898 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 4 Jul 2020 11:43:46 -0400 Subject: Rename logo assets to remove project name and 'logo', which are implied by the context. --- docs/logo/banner 1 line color.png | Bin 0 -> 24183 bytes docs/logo/banner 1 line color.svg | 223 ++++++++++++++++++++ docs/logo/banner 2 lines color.png | Bin 0 -> 38105 bytes docs/logo/banner 2 lines color.svg | 224 ++++++++++++++++++++ docs/logo/full color 1000px.png | Bin 0 -> 31520 bytes docs/logo/full color.svg | 227 +++++++++++++++++++++ docs/logo/setup_tools_logo_colour.svg | 227 --------------------- docs/logo/setup_tools_logo_colour_1000px.png | Bin 31520 -> 0 bytes docs/logo/setup_tools_logo_colour_banner_1line.svg | 223 -------------------- ...setup_tools_logo_colour_banner_1line_1000px.png | Bin 24183 -> 0 bytes .../logo/setup_tools_logo_colour_banner_2lines.svg | 224 -------------------- ...etup_tools_logo_colour_banner_2lines_1000px.png | Bin 38105 -> 0 bytes docs/logo/setup_tools_logo_symbol_colour.svg | 203 ------------------ .../logo/setup_tools_logo_symbol_colour_1000px.png | Bin 44239 -> 0 bytes docs/logo/setup_tools_logotype_1line.svg | 169 --------------- docs/logo/symbol color 1000px.png | Bin 0 -> 44239 bytes docs/logo/symbol color.svg | 203 ++++++++++++++++++ docs/logo/type.svg | 169 +++++++++++++++ 18 files changed, 1046 insertions(+), 1046 deletions(-) create mode 100644 docs/logo/banner 1 line color.png create mode 100644 docs/logo/banner 1 line color.svg create mode 100644 docs/logo/banner 2 lines color.png create mode 100644 docs/logo/banner 2 lines color.svg create mode 100644 docs/logo/full color 1000px.png create mode 100644 docs/logo/full color.svg delete mode 100644 docs/logo/setup_tools_logo_colour.svg delete mode 100644 docs/logo/setup_tools_logo_colour_1000px.png delete mode 100644 docs/logo/setup_tools_logo_colour_banner_1line.svg delete mode 100644 docs/logo/setup_tools_logo_colour_banner_1line_1000px.png delete mode 100644 docs/logo/setup_tools_logo_colour_banner_2lines.svg delete mode 100644 docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png delete mode 100644 docs/logo/setup_tools_logo_symbol_colour.svg delete mode 100644 docs/logo/setup_tools_logo_symbol_colour_1000px.png delete mode 100644 docs/logo/setup_tools_logotype_1line.svg create mode 100644 docs/logo/symbol color 1000px.png create mode 100644 docs/logo/symbol color.svg create mode 100644 docs/logo/type.svg diff --git a/docs/logo/banner 1 line color.png b/docs/logo/banner 1 line color.png new file mode 100644 index 00000000..d1604289 Binary files /dev/null and b/docs/logo/banner 1 line color.png differ diff --git a/docs/logo/banner 1 line color.svg b/docs/logo/banner 1 line color.svg new file mode 100644 index 00000000..d6dc9aac --- /dev/null +++ b/docs/logo/banner 1 line color.svg @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/docs/logo/banner 2 lines color.png b/docs/logo/banner 2 lines color.png new file mode 100644 index 00000000..89370a9d Binary files /dev/null and b/docs/logo/banner 2 lines color.png differ diff --git a/docs/logo/banner 2 lines color.svg b/docs/logo/banner 2 lines color.svg new file mode 100644 index 00000000..991577fb --- /dev/null +++ b/docs/logo/banner 2 lines color.svg @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/docs/logo/full color 1000px.png b/docs/logo/full color 1000px.png new file mode 100644 index 00000000..c25a23b9 Binary files /dev/null and b/docs/logo/full color 1000px.png differ diff --git a/docs/logo/full color.svg b/docs/logo/full color.svg new file mode 100644 index 00000000..7eae8fc3 --- /dev/null +++ b/docs/logo/full color.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/docs/logo/setup_tools_logo_colour.svg b/docs/logo/setup_tools_logo_colour.svg deleted file mode 100644 index 7eae8fc3..00000000 --- a/docs/logo/setup_tools_logo_colour.svg +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/docs/logo/setup_tools_logo_colour_1000px.png b/docs/logo/setup_tools_logo_colour_1000px.png deleted file mode 100644 index c25a23b9..00000000 Binary files a/docs/logo/setup_tools_logo_colour_1000px.png and /dev/null differ diff --git a/docs/logo/setup_tools_logo_colour_banner_1line.svg b/docs/logo/setup_tools_logo_colour_banner_1line.svg deleted file mode 100644 index d6dc9aac..00000000 --- a/docs/logo/setup_tools_logo_colour_banner_1line.svg +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png b/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png deleted file mode 100644 index d1604289..00000000 Binary files a/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png and /dev/null differ diff --git a/docs/logo/setup_tools_logo_colour_banner_2lines.svg b/docs/logo/setup_tools_logo_colour_banner_2lines.svg deleted file mode 100644 index 991577fb..00000000 --- a/docs/logo/setup_tools_logo_colour_banner_2lines.svg +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png b/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png deleted file mode 100644 index 89370a9d..00000000 Binary files a/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png and /dev/null differ diff --git a/docs/logo/setup_tools_logo_symbol_colour.svg b/docs/logo/setup_tools_logo_symbol_colour.svg deleted file mode 100644 index 2936cbb5..00000000 --- a/docs/logo/setup_tools_logo_symbol_colour.svg +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - diff --git a/docs/logo/setup_tools_logo_symbol_colour_1000px.png b/docs/logo/setup_tools_logo_symbol_colour_1000px.png deleted file mode 100644 index e0c36fc1..00000000 Binary files a/docs/logo/setup_tools_logo_symbol_colour_1000px.png and /dev/null differ diff --git a/docs/logo/setup_tools_logotype_1line.svg b/docs/logo/setup_tools_logotype_1line.svg deleted file mode 100644 index dbb9c1de..00000000 --- a/docs/logo/setup_tools_logotype_1line.svg +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - diff --git a/docs/logo/symbol color 1000px.png b/docs/logo/symbol color 1000px.png new file mode 100644 index 00000000..e0c36fc1 Binary files /dev/null and b/docs/logo/symbol color 1000px.png differ diff --git a/docs/logo/symbol color.svg b/docs/logo/symbol color.svg new file mode 100644 index 00000000..2936cbb5 --- /dev/null +++ b/docs/logo/symbol color.svg @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/docs/logo/type.svg b/docs/logo/type.svg new file mode 100644 index 00000000..dbb9c1de --- /dev/null +++ b/docs/logo/type.svg @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + -- cgit v1.2.1 From 4056435460b770c3ab7a8ea1637f05de690e7e01 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 4 Jul 2020 11:49:37 -0400 Subject: Render logo in the readme. --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 9cbf7b86..dc2bf98b 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,6 @@ +.. image:: https://raw.githubusercontent.com/pypa/setuptools/master/docs/logo/banner%201%20line%20color.svg + + .. image:: https://img.shields.io/pypi/v/setuptools.svg :target: `PyPI link`_ @@ -24,6 +27,7 @@ .. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme + See the `Installation Instructions `_ in the Python Packaging User's Guide for instructions on installing, upgrading, and uninstalling -- cgit v1.2.1 From 0f491dc2b4141a17475eec12b02d8f237d7f7918 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 4 Jul 2020 11:57:44 -0400 Subject: Add banner to main docs page --- docs/index.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/index.txt b/docs/index.txt index 13a46e74..f3f65cb4 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -1,5 +1,8 @@ -Welcome to Setuptools' documentation! -===================================== +.. image:: https://raw.githubusercontent.com/pypa/setuptools/master/docs/logo/banner%201%20line%20color.svg + + +Documentation +============= Setuptools is a fully-featured, actively-maintained, and stable library designed to facilitate packaging Python projects, where packaging includes: -- cgit v1.2.1 From 5e179ffca0a6ed5f3369139baaea61094df38524 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 4 Jul 2020 11:59:16 -0400 Subject: Remove stale description of packaging. --- docs/index.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/docs/index.txt b/docs/index.txt index f3f65cb4..228f97c8 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -5,14 +5,7 @@ Documentation ============= Setuptools is a fully-featured, actively-maintained, and stable library -designed to facilitate packaging Python projects, where packaging includes: - - - Python package and module definitions - - Distribution package metadata - - Test hooks - - Project installation - - Platform-specific details - - Python 3 support +designed to facilitate packaging Python projects. Documentation content: -- cgit v1.2.1 From f3b4a9972163e6063b9d37db6352a54735955d81 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 5 Jul 2020 16:31:24 -0400 Subject: Add test for spawn when exe is missing. Ref pypa/distutils#3. --- distutils/tests/test_spawn.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/distutils/tests/test_spawn.py b/distutils/tests/test_spawn.py index 919d0ad9..704019a1 100644 --- a/distutils/tests/test_spawn.py +++ b/distutils/tests/test_spawn.py @@ -126,6 +126,11 @@ class SpawnTestCase(support.TempdirManager, rv = find_executable(program) self.assertEqual(rv, filename) + def test_spawn_missing_exe(self): + with self.assertRaises(DistutilsExecError) as ctx: + spawn(['does-not-exist']) + assert 'command does-no-exist failed' in str(ctx) + def test_suite(): return unittest.makeSuite(SpawnTestCase) -- cgit v1.2.1 From 7aa5abeafc1e0b1b351c4c8ac7eb14c310366a46 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 5 Jul 2020 16:33:16 -0400 Subject: Replace OSError with DistutilsExecError. Fixes pypa/distutils#3 and pypa/setuptools#2228 and bpo-41207. --- distutils/spawn.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/distutils/spawn.py b/distutils/spawn.py index aad277b0..0d1bd039 100644 --- a/distutils/spawn.py +++ b/distutils/spawn.py @@ -71,9 +71,15 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0): env = dict(os.environ, MACOSX_DEPLOYMENT_TARGET=cur_target) - proc = subprocess.Popen(cmd, env=env) - proc.wait() - exitcode = proc.returncode + try: + proc = subprocess.Popen(cmd, env=env) + proc.wait() + exitcode = proc.returncode + except OSError as exc: + if not DEBUG: + cmd = cmd[0] + raise DistutilsExecError( + "command %r failed: %s" % (cmd, exc.args[-1])) from exc if exitcode: if not DEBUG: -- cgit v1.2.1 From ea966a23de98c04df90ba13e9fb03d1b0afe4962 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 5 Jul 2020 16:40:01 -0400 Subject: Update changelog. Ref #2228. --- changelog.d/2228.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2228.misc.rst diff --git a/changelog.d/2228.misc.rst b/changelog.d/2228.misc.rst new file mode 100644 index 00000000..be6b199d --- /dev/null +++ b/changelog.d/2228.misc.rst @@ -0,0 +1 @@ +Applied fix for pypa/distutils#3, restoring expectation that spawn will raise a DistutilsExecError when attempting to execute a missing file. -- cgit v1.2.1 From 44cec449385cac29b0f6c9719b506eb6b8c1826d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 5 Jul 2020 16:40:17 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.0.0=20=E2=86=92=2049.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2228.misc.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2228.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 05ff5f18..87d756b8 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.0.0 +current_version = 49.0.1 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index bd67931d..a38b610b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v49.0.1 +------- + +* #2228: Applied fix for pypa/distutils#3, restoring expectation that spawn will raise a DistutilsExecError when attempting to execute a missing file. + + v49.0.0 ------- diff --git a/changelog.d/2228.misc.rst b/changelog.d/2228.misc.rst deleted file mode 100644 index be6b199d..00000000 --- a/changelog.d/2228.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Applied fix for pypa/distutils#3, restoring expectation that spawn will raise a DistutilsExecError when attempting to execute a missing file. diff --git a/setup.cfg b/setup.cfg index d7324186..c9d108ca 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.0.0 +version = 49.0.1 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From c6fff69d9597c203cb6a7f3b34dbb6244f148ae6 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Mon, 6 Jul 2020 20:29:49 +0800 Subject: bpo-40275: Use new test.support helper submodules in tests (GH-21317) --- tests/support.py | 4 ++-- tests/test_build_ext.py | 3 ++- tests/test_filelist.py | 13 ++++++------- tests/test_spawn.py | 18 +++++++++--------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/support.py b/tests/support.py index 259af882..23b907b6 100644 --- a/tests/support.py +++ b/tests/support.py @@ -6,7 +6,7 @@ import tempfile import unittest import sysconfig from copy import deepcopy -import test.support +from test.support import os_helper from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL @@ -64,7 +64,7 @@ class TempdirManager(object): super().tearDown() while self.tempdirs: tmpdir = self.tempdirs.pop() - test.support.rmtree(tmpdir) + os_helper.rmtree(tmpdir) def mkdtemp(self): """Create a temporary directory that will be cleaned up. diff --git a/tests/test_build_ext.py b/tests/test_build_ext.py index 5e47e077..f9e0d766 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -15,6 +15,7 @@ from distutils.errors import ( import unittest from test import support +from test.support import os_helper from test.support.script_helper import assert_python_ok # http://bugs.python.org/issue4373 @@ -38,7 +39,7 @@ class BuildExtTestCase(TempdirManager, # bpo-30132: On Windows, a .pdb file may be created in the current # working directory. Create a temporary working directory to cleanup # everything at the end of the test. - change_cwd = support.change_cwd(self.tmp_dir) + change_cwd = os_helper.change_cwd(self.tmp_dir) change_cwd.__enter__() self.addCleanup(change_cwd.__exit__, None, None, None) diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 2c26c226..cee97d43 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -8,7 +8,6 @@ from distutils.errors import DistutilsTemplateError from distutils.filelist import glob_to_re, translate_pattern, FileList from distutils import filelist -import test.support from test.support import os_helper from test.support import captured_stdout, run_unittest from distutils.tests import support @@ -298,7 +297,7 @@ class FileListTestCase(support.LoggingSilencer, class FindAllTestCase(unittest.TestCase): @os_helper.skip_unless_symlink def test_missing_symlink(self): - with test.support.temp_cwd(): + with os_helper.temp_cwd(): os.symlink('foo', 'bar') self.assertEqual(filelist.findall(), []) @@ -308,13 +307,13 @@ class FindAllTestCase(unittest.TestCase): '.' as the parameter, the dot should be omitted from the results. """ - with test.support.temp_cwd(): + with os_helper.temp_cwd(): os.mkdir('foo') file1 = os.path.join('foo', 'file1.txt') - test.support.create_empty_file(file1) + os_helper.create_empty_file(file1) os.mkdir('bar') file2 = os.path.join('bar', 'file2.txt') - test.support.create_empty_file(file2) + os_helper.create_empty_file(file2) expected = [file2, file1] self.assertEqual(sorted(filelist.findall()), expected) @@ -323,9 +322,9 @@ class FindAllTestCase(unittest.TestCase): When findall is called with another path, the full path name should be returned. """ - with test.support.temp_dir() as temp_dir: + with os_helper.temp_dir() as temp_dir: file1 = os.path.join(temp_dir, 'file1.txt') - test.support.create_empty_file(file1) + os_helper.create_empty_file(file1) expected = [file1] self.assertEqual(filelist.findall(temp_dir), expected) diff --git a/tests/test_spawn.py b/tests/test_spawn.py index cf1faad5..3647bab7 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -4,7 +4,7 @@ import stat import sys import unittest.mock from test.support import run_unittest, unix_shell -from test import support as test_support +from test.support import os_helper from distutils.spawn import find_executable from distutils.spawn import spawn @@ -44,9 +44,9 @@ class SpawnTestCase(support.TempdirManager, spawn([exe]) # should work without any error def test_find_executable(self): - with test_support.temp_dir() as tmp_dir: + with os_helper.temp_dir() as tmp_dir: # use TESTFN to get a pseudo-unique filename - program_noeext = test_support.TESTFN + program_noeext = os_helper.TESTFN # Give the temporary program an ".exe" suffix for all. # It's needed on Windows and not harmful on other platforms. program = program_noeext + ".exe" @@ -66,7 +66,7 @@ class SpawnTestCase(support.TempdirManager, self.assertEqual(rv, filename) # test find in the current directory - with test_support.change_cwd(tmp_dir): + with os_helper.change_cwd(tmp_dir): rv = find_executable(program) self.assertEqual(rv, program) @@ -76,7 +76,7 @@ class SpawnTestCase(support.TempdirManager, self.assertIsNone(rv) # PATH='': no match, except in the current directory - with test_support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['PATH'] = '' with unittest.mock.patch('distutils.spawn.os.confstr', return_value=tmp_dir, create=True), \ @@ -86,12 +86,12 @@ class SpawnTestCase(support.TempdirManager, self.assertIsNone(rv) # look in current directory - with test_support.change_cwd(tmp_dir): + with os_helper.change_cwd(tmp_dir): rv = find_executable(program) self.assertEqual(rv, program) # PATH=':': explicitly looks in the current directory - with test_support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env['PATH'] = os.pathsep with unittest.mock.patch('distutils.spawn.os.confstr', return_value='', create=True), \ @@ -100,12 +100,12 @@ class SpawnTestCase(support.TempdirManager, self.assertIsNone(rv) # look in current directory - with test_support.change_cwd(tmp_dir): + with os_helper.change_cwd(tmp_dir): rv = find_executable(program) self.assertEqual(rv, program) # missing PATH: test os.confstr("CS_PATH") and os.defpath - with test_support.EnvironmentVarGuard() as env: + with os_helper.EnvironmentVarGuard() as env: env.pop('PATH', None) # without confstr -- cgit v1.2.1 From a21ea47bf0a8bbd79afdbd93b80681f1133aedf0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 7 Jul 2020 06:39:32 -0400 Subject: Move assert outside the context so it actually has its effect. --- distutils/tests/test_spawn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distutils/tests/test_spawn.py b/distutils/tests/test_spawn.py index 704019a1..adaa48ef 100644 --- a/distutils/tests/test_spawn.py +++ b/distutils/tests/test_spawn.py @@ -129,7 +129,7 @@ class SpawnTestCase(support.TempdirManager, def test_spawn_missing_exe(self): with self.assertRaises(DistutilsExecError) as ctx: spawn(['does-not-exist']) - assert 'command does-no-exist failed' in str(ctx) + self.assertIn("command 'does-not-exist' failed", str(ctx.exception)) def test_suite(): -- cgit v1.2.1 From 58f0b3d1ef9d17d511cf027e87f7b87dc3be6bd9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 7 Jul 2020 07:11:12 -0400 Subject: Remove py2_warn, no longer needed as a SyntaxError is encountered before the warning can be issueed. --- pkg_resources/__init__.py | 1 - pkg_resources/py2_warn.py | 16 ---------------- 2 files changed, 17 deletions(-) delete mode 100644 pkg_resources/py2_warn.py diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index b9534903..5927ef0d 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -82,7 +82,6 @@ __import__('pkg_resources.extern.packaging.version') __import__('pkg_resources.extern.packaging.specifiers') __import__('pkg_resources.extern.packaging.requirements') __import__('pkg_resources.extern.packaging.markers') -__import__('pkg_resources.py2_warn') __metaclass__ = type diff --git a/pkg_resources/py2_warn.py b/pkg_resources/py2_warn.py deleted file mode 100644 index 6855aa24..00000000 --- a/pkg_resources/py2_warn.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys -import warnings -import textwrap - - -msg = textwrap.dedent(""" - Encountered a version of Setuptools that no longer supports - this version of Python. Please head to - https://bit.ly/setuptools-py2-sunset for support. - """) - -pre = "Setuptools no longer works on Python 2\n" - -if sys.version_info < (3,): - warnings.warn(pre + "*" * 60 + msg + "*" * 60) - raise SystemExit(32) -- cgit v1.2.1 From e34d6d8b739a8e0edbdfa324bf3607430aa3ff70 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 7 Jul 2020 07:11:28 -0400 Subject: bpo-41207 In distutils.spawn, rewrite FileNotFound (GH-21359) Automerge-Triggered-By: @jaraco --- spawn.py | 12 +++++++++--- tests/test_spawn.py | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/spawn.py b/spawn.py index aad277b0..0d1bd039 100644 --- a/spawn.py +++ b/spawn.py @@ -71,9 +71,15 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0): env = dict(os.environ, MACOSX_DEPLOYMENT_TARGET=cur_target) - proc = subprocess.Popen(cmd, env=env) - proc.wait() - exitcode = proc.returncode + try: + proc = subprocess.Popen(cmd, env=env) + proc.wait() + exitcode = proc.returncode + except OSError as exc: + if not DEBUG: + cmd = cmd[0] + raise DistutilsExecError( + "command %r failed: %s" % (cmd, exc.args[-1])) from exc if exitcode: if not DEBUG: diff --git a/tests/test_spawn.py b/tests/test_spawn.py index 3647bab7..4ec767b1 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -124,6 +124,11 @@ class SpawnTestCase(support.TempdirManager, rv = find_executable(program) self.assertEqual(rv, filename) + def test_spawn_missing_exe(self): + with self.assertRaises(DistutilsExecError) as ctx: + spawn(['does-not-exist']) + self.assertIn("command 'does-not-exist' failed", str(ctx.exception)) + def test_suite(): return unittest.makeSuite(SpawnTestCase) -- cgit v1.2.1 From 317337daee72994b3d452ffcce5f4ecd47d9dc5d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 7 Jul 2020 17:08:52 -0400 Subject: Update changelog. --- changelog.d/2094.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2094.misc.rst diff --git a/changelog.d/2094.misc.rst b/changelog.d/2094.misc.rst new file mode 100644 index 00000000..675cd352 --- /dev/null +++ b/changelog.d/2094.misc.rst @@ -0,0 +1 @@ +Removed pkg_resources.py2_warn module, which is no longer reachable. -- cgit v1.2.1 From cd8ac41d2f40de096251b78a55a74b2705a7af8f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 10 Jul 2020 14:08:59 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.1.0=20=E2=86=92=2049.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2094.misc.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2094.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index f2f2b271..55f806f9 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.1.0 +current_version = 49.1.1 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index f00482e3..d5a2a703 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v49.1.1 +------- + +* #2094: Removed pkg_resources.py2_warn module, which is no longer reachable. + + v49.0.1 ------- diff --git a/changelog.d/2094.misc.rst b/changelog.d/2094.misc.rst deleted file mode 100644 index 675cd352..00000000 --- a/changelog.d/2094.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Removed pkg_resources.py2_warn module, which is no longer reachable. diff --git a/setup.cfg b/setup.cfg index 1f347c3f..3151fa49 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.1.0 +version = 49.1.1 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 069eb7de91856a92f0c630b048f14367af80c3ad Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 11 Jul 2020 02:58:16 +0300 Subject: Test Python 3.9-dev --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e8bc7574..0e636eec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ jobs: - <<: *latest_py3 env: LANG=C - python: 3.8-dev + - python: 3.9-dev - <<: *latest_py3 env: TOXENV=docs allow_failures: -- cgit v1.2.1 From 67f0cc59f0d803f13c1da1cb60e87469f656617e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 10 Jul 2020 20:14:49 -0400 Subject: Provide escape hatch for distutils adoption. --- setuptools/distutils_patch.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index b2095fba..bd1b4997 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -7,6 +7,7 @@ for more motivation. import sys import re +import os import importlib import warnings @@ -20,6 +21,13 @@ def clear_distutils(): del sys.modules[name] +def enabled(): + """ + Provide an escape hatch for environments wishing to opt out. + """ + return 'SETUPTOOLS_DISTUTILS_ADOPTION_OPT_OUT' not in os.environ + + def ensure_local_distutils(): clear_distutils() distutils = importlib.import_module('setuptools._distutils') @@ -31,4 +39,5 @@ def ensure_local_distutils(): assert '_distutils' in core.__file__, core.__file__ -ensure_local_distutils() +if enabled(): + ensure_local_distutils() -- cgit v1.2.1 From bedaf1309d2633831a7fb7dea561642e7da704df Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 10 Jul 2020 20:46:30 -0400 Subject: Allow opt-in and opt-out of distutils adoption at run time with SETUPTOOLS_USE_DISTUTILS environment variable. --- changelog.d/2232.misc.patch | 1 + setuptools/__init__.py | 2 +- setuptools/distutils_patch.py | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 changelog.d/2232.misc.patch diff --git a/changelog.d/2232.misc.patch b/changelog.d/2232.misc.patch new file mode 100644 index 00000000..4a956e1e --- /dev/null +++ b/changelog.d/2232.misc.patch @@ -0,0 +1 @@ +In preparation for re-enabling a local copy of distutils, Setuptools now honors an environment variable, SETUPTOOLS_USE_DISTUTILS. If set to 'stdlib' (current default), distutils will be used from the standard library. If set to 'local' (default in a imminent backward-incompatible release), the local copy of distutils will be used. diff --git a/setuptools/__init__.py b/setuptools/__init__.py index a6cbe132..83882511 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -4,7 +4,7 @@ import os import functools # Disabled for now due to: #2228, #2230 -# import setuptools.distutils_patch # noqa: F401 +import setuptools.distutils_patch # noqa: F401 import distutils.core import distutils.filelist diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index bd1b4997..c5f273dd 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -23,9 +23,10 @@ def clear_distutils(): def enabled(): """ - Provide an escape hatch for environments wishing to opt out. + Allow selection of distutils by environment variable. """ - return 'SETUPTOOLS_DISTUTILS_ADOPTION_OPT_OUT' not in os.environ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') + return which == 'local' def ensure_local_distutils(): -- cgit v1.2.1 From 599f6393c1be4d5852f5b9884b9fb282a16ca64a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 11 Jul 2020 02:17:03 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.1.1=20=E2=86=92=2049.1.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2232.misc.patch | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2232.misc.patch diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 55f806f9..30e83be1 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.1.1 +current_version = 49.1.2 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index d5a2a703..24837a97 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v49.1.2 +------- + +* #2232: In preparation for re-enabling a local copy of distutils, Setuptools now honors an environment variable, SETUPTOOLS_USE_DISTUTILS. If set to 'stdlib' (current default), distutils will be used from the standard library. If set to 'local' (default in a imminent backward-incompatible release), the local copy of distutils will be used. + + v49.1.1 ------- diff --git a/changelog.d/2232.misc.patch b/changelog.d/2232.misc.patch deleted file mode 100644 index 4a956e1e..00000000 --- a/changelog.d/2232.misc.patch +++ /dev/null @@ -1 +0,0 @@ -In preparation for re-enabling a local copy of distutils, Setuptools now honors an environment variable, SETUPTOOLS_USE_DISTUTILS. If set to 'stdlib' (current default), distutils will be used from the standard library. If set to 'local' (default in a imminent backward-incompatible release), the local copy of distutils will be used. diff --git a/setup.cfg b/setup.cfg index 3151fa49..dc29f7b1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.1.1 +version = 49.1.2 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 8b4ce333c093f459698c0545550269a393387c5f Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Sat, 11 Jul 2020 12:51:48 -0600 Subject: Change exec_module to load_module Fixes #2246 --- setuptools/command/bdist_egg.py | 2 +- setuptools/command/build_ext.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index e94fe252..7af3165c 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -59,7 +59,7 @@ def write_stub(resource, pyfile): from importlib.machinery import ExtensionFileLoader __file__ = pkg_resources.resource_filename(__name__, %r) __loader__ = None; del __bootstrap__, __loader__ - ExtensionFileLoader(__name__,__file__).exec_module() + ExtensionFileLoader(__name__,__file__).load_module() __bootstrap__() """).lstrip() with open(pyfile, 'w') as f: diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 327fa063..0eb29adc 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -268,7 +268,7 @@ class build_ext(_build_ext): " os.chdir(os.path.dirname(__file__))", if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), " ExtensionFileLoader(__name__,", - " __file__).exec_module()", + " __file__).load_module()", " finally:", if_dl(" sys.setdlopenflags(old_flags)"), " os.chdir(old_dir)", -- cgit v1.2.1 From 6d3250fa1ea1a309e8e45737d0a1dccbbd3f95e4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Jul 2020 03:39:35 -0400 Subject: Add a simple blank issue so it gets a green button. --- .github/ISSUE_TEMPLATE/blank.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/blank.md diff --git a/.github/ISSUE_TEMPLATE/blank.md b/.github/ISSUE_TEMPLATE/blank.md new file mode 100644 index 00000000..e41fc749 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/blank.md @@ -0,0 +1,4 @@ +--- +name: Bug Report or Feature Request +about: Report a bug or request a feature +--- -- cgit v1.2.1 From b69029f753e061359adc554b2f5c600a2d143bf9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Jul 2020 03:49:59 -0400 Subject: Update changelog. --- changelog.d/2249.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2249.misc.rst diff --git a/changelog.d/2249.misc.rst b/changelog.d/2249.misc.rst new file mode 100644 index 00000000..14742dc3 --- /dev/null +++ b/changelog.d/2249.misc.rst @@ -0,0 +1 @@ +Fix extension loading technique in stubs. -- cgit v1.2.1 From c4e3afb3f559f6cbe0f29ad908de8b05b993fb5f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Jul 2020 04:14:22 -0400 Subject: Add README, including docs describing how to synchronize with CPython. --- README.rst | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 README.rst diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..e619264d --- /dev/null +++ b/README.rst @@ -0,0 +1,40 @@ +Python Module Distribution Utilities extracted from the Python Standard Library + +Synchronizing +============= + +This project is kept in sync with the code still in stdlib. + +From CPython +------------ + +The original history and attribution of all changes contributed to CPython can be found in the `cpython branch `_. If new commits are added to CPython, they should be synchronized back to this project. + +First, create a clone of CPython that only includes the distutils changes. Due to the large size of the CPython repository, this operation is fairly expensive. + + git clone https://github.com/python/cpython python-distutils + cd python-distutils + git filter-branch --prune-empty --subdirectory-filter Lib/distutils master + +Then, pull those changes into the repository at the cpython branch. + + cd $distutils + git checkout cpython + git fetch $python_distutils + git merge FETCH_HEAD + +Finally, merge the changes from cpython into master (possibly as a pull request). + +To CPython +---------- + +Merging changes from this repository is easier. + +From the CPython repo, cherry-pick the changes from this project. + + git -C $distutils format-patch HEAD~2 --stdout | git am --directory Lib + +To Setuptools +------------- + +This project also maintains a `clean branch `_ to capture only the code changes to distutils, excluding the ancillary details like packaging and this document. -- cgit v1.2.1 From 0039de02e84a2256d6d4763fb76836dd4748f3e4 Mon Sep 17 00:00:00 2001 From: Hugo Date: Sun, 12 Jul 2020 11:42:24 +0300 Subject: Test Python 3.9-dev --- .github/workflows/python-tests.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 5a598084..f2188d38 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -30,6 +30,9 @@ jobs: - macOS-latest # - windows-2019 # - windows-2016 + include: + # Dev versions + - { python-version: 3.9-dev, os: ubuntu-20.04 } env: NETWORK_REQUIRED: 1 @@ -38,8 +41,14 @@ jobs: steps: - uses: actions/checkout@master + - name: Set up Python ${{ matrix.python-version }} (deadsnakes) + uses: deadsnakes/action@v1.0.0 + if: endsWith(matrix.python-version, '-dev') + with: + python-version: ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v1.1.1 + if: "!endsWith(matrix.python-version, '-dev')" with: python-version: ${{ matrix.python-version }} - name: Log Python version -- cgit v1.2.1 From 360aadc4c2a9941a68a3f3f63375ce5380fef6a0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 11 Jul 2020 15:31:18 -0400 Subject: Allow spawn to accept environment. Avoid monkey-patching global state. Closes pypa/setuptools#2212 and closes pypa/distutils#5. --- distutils/_msvccompiler.py | 8 ++------ distutils/spawn.py | 8 ++++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/distutils/_msvccompiler.py b/distutils/_msvccompiler.py index af8099a4..0e98692e 100644 --- a/distutils/_msvccompiler.py +++ b/distutils/_msvccompiler.py @@ -501,12 +501,8 @@ class MSVCCompiler(CCompiler) : log.debug("skipping %s (up-to-date)", output_filename) def spawn(self, cmd): - old_path = os.getenv('path') - try: - os.environ['path'] = self._paths - return super().spawn(cmd) - finally: - os.environ['path'] = old_path + env = dict(os.environ, path=self._paths) + return super().spawn(cmd, env=env) # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in diff --git a/distutils/spawn.py b/distutils/spawn.py index 0d1bd039..fc592d4a 100644 --- a/distutils/spawn.py +++ b/distutils/spawn.py @@ -20,7 +20,7 @@ if sys.platform == 'darwin': _cfg_target_split = None -def spawn(cmd, search_path=1, verbose=0, dry_run=0): +def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): """Run another program, specified as a command list 'cmd', in a new process. 'cmd' is just the argument list for the new process, ie. @@ -49,7 +49,8 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0): if executable is not None: cmd[0] = executable - env = None + env = env if env is not None else dict(os.environ) + if sys.platform == 'darwin': global _cfg_target, _cfg_target_split if _cfg_target is None: @@ -68,8 +69,7 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0): 'now "%s" but "%s" during configure' % (cur_target, _cfg_target)) raise DistutilsPlatformError(my_msg) - env = dict(os.environ, - MACOSX_DEPLOYMENT_TARGET=cur_target) + env.update(MACOSX_DEPLOYMENT_TARGET=cur_target) try: proc = subprocess.Popen(cmd, env=env) -- cgit v1.2.1 From 8a4bc38e7b582b2006e17118162b1d2a0a23b8ad Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Jul 2020 05:02:18 -0400 Subject: Update changelog. --- changelog.d/2212.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2212.misc.rst diff --git a/changelog.d/2212.misc.rst b/changelog.d/2212.misc.rst new file mode 100644 index 00000000..d6fc7bf8 --- /dev/null +++ b/changelog.d/2212.misc.rst @@ -0,0 +1 @@ +(Distutils) Allow spawn to accept environment. Avoid monkey-patching global state. -- cgit v1.2.1 From 9e16d5399bb6b01aa0d7a73159765f064f88db22 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Jul 2020 05:07:29 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.1.2=20=E2=86=92=2049.1.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 7 +++++++ changelog.d/2212.misc.rst | 1 - changelog.d/2249.misc.rst | 1 - setup.cfg | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 changelog.d/2212.misc.rst delete mode 100644 changelog.d/2249.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 30e83be1..5e4d74b0 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.1.2 +current_version = 49.1.3 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 24837a97..b23bc394 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v49.1.3 +------- + +* #2212: (Distutils) Allow spawn to accept environment. Avoid monkey-patching global state. +* #2249: Fix extension loading technique in stubs. + + v49.1.2 ------- diff --git a/changelog.d/2212.misc.rst b/changelog.d/2212.misc.rst deleted file mode 100644 index d6fc7bf8..00000000 --- a/changelog.d/2212.misc.rst +++ /dev/null @@ -1 +0,0 @@ -(Distutils) Allow spawn to accept environment. Avoid monkey-patching global state. diff --git a/changelog.d/2249.misc.rst b/changelog.d/2249.misc.rst deleted file mode 100644 index 14742dc3..00000000 --- a/changelog.d/2249.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Fix extension loading technique in stubs. diff --git a/setup.cfg b/setup.cfg index dc29f7b1..bad15f93 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.1.2 +version = 49.1.3 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 9e58d95017656ee33d2cf642cf1be6c77298f16d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Jul 2020 10:56:14 -0400 Subject: Re-enable distutils patch by default. --- changelog.d/2232.breaking.rst | 1 + setuptools/distutils_patch.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/2232.breaking.rst diff --git a/changelog.d/2232.breaking.rst b/changelog.d/2232.breaking.rst new file mode 100644 index 00000000..b2fd926f --- /dev/null +++ b/changelog.d/2232.breaking.rst @@ -0,0 +1 @@ +Once again, Setuptools overrides the stdlib distutils on import. For environments or invocations where this behavior is undesirable, users are provided with a temporary escape hatch. If the environment variable ``SETUPTOOLS_USE_DISTUTILS`` is set to ``stdlib``, Setuptools will fall back to the legacy behavior. Use of this escape hatch is discouraged, but it is provided to ease the transition while proper fixes for edge cases can be addressed. diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index c5f273dd..e6b2aad5 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -25,7 +25,7 @@ def enabled(): """ Allow selection of distutils by environment variable. """ - which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') return which == 'local' -- cgit v1.2.1 From 36df1d74933ef18a4099f4e82dbe55c00e45709e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Jul 2020 11:39:51 -0400 Subject: Warn the user when distutils is present to discourage this usage and direct users to the recommended usage. Closes #2230. --- changelog.d/2230.change.rst | 1 + setuptools/distutils_patch.py | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 changelog.d/2230.change.rst diff --git a/changelog.d/2230.change.rst b/changelog.d/2230.change.rst new file mode 100644 index 00000000..1719f497 --- /dev/null +++ b/changelog.d/2230.change.rst @@ -0,0 +1 @@ +Now warn the user when setuptools is imported after distutils modules have been loaded (exempting PyPy for 3.6), directing the users of packages to import setuptools first. diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index c5f273dd..33f1e7f9 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -12,10 +12,26 @@ import importlib import warnings +is_pypy = '__pypy__' in sys.builtin_module_names + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + warnings.warn( + "Distutils was imported before Setuptools. This usage is discouraged " + "and may exhibit undesirable behaviors or errors. Please use " + "Setuptools' objects directly or at least import Setuptools first.") + + def clear_distutils(): if 'distutils' not in sys.modules: return - warnings.warn("Setuptools is replacing distutils") + warnings.warn("Setuptools is replacing distutils.") mods = [name for name in sys.modules if re.match(r'distutils\b', name)] for name in mods: del sys.modules[name] @@ -40,5 +56,6 @@ def ensure_local_distutils(): assert '_distutils' in core.__file__, core.__file__ +warn_distutils_present() if enabled(): ensure_local_distutils() -- cgit v1.2.1 From 9f47efe757762351ec12b4303e747ac0774db991 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 02:54:32 -0400 Subject: Programmatically disable coverage when running on PyPy. --- conftest.py | 18 ++++++++++++++++++ tox.ini | 4 ---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/conftest.py b/conftest.py index 1746bfb5..0bc8d320 100644 --- a/conftest.py +++ b/conftest.py @@ -17,6 +17,24 @@ collect_ignore = [ ] +def pytest_configure(config): + disable_coverage_on_pypy(config) + + +def disable_coverage_on_pypy(config): + """ + Coverage makes tests on PyPy unbearably slow, so disable it. + """ + if '__pypy__' not in sys.builtin_module_names: + return + + # Recommended at pytest-dev/pytest-cov#418 + cov = config.pluginmanager.get_plugin('_cov') + cov.options.no_cov = True + if cov.cov_controller: + cov.cov_controller.pause() + + if sys.version_info < (3,): collect_ignore.append('setuptools/lib2to3_ex.py') collect_ignore.append('setuptools/_imp.py') diff --git a/tox.ini b/tox.ini index 59213e88..9ce5cb5c 100644 --- a/tox.ini +++ b/tox.ini @@ -29,10 +29,6 @@ extras = tests -[testenv:pypy{,3}] -commands = pytest --no-cov {posargs} - - [testenv:coverage] description=Combine coverage data and create report deps=coverage -- cgit v1.2.1 From 37d81f4ce8f08c4baf44b6ff0f3f1bd3f6b2a127 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Jul 2020 12:09:11 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.1.3=20=E2=86=92=2049.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2230.change.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2230.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 5e4d74b0..9b3e4085 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.1.3 +current_version = 49.2.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index b23bc394..82e6ef66 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v49.2.0 +------- + +* #2230: Now warn the user when setuptools is imported after distutils modules have been loaded (exempting PyPy for 3.6), directing the users of packages to import setuptools first. + + v49.1.3 ------- diff --git a/changelog.d/2230.change.rst b/changelog.d/2230.change.rst deleted file mode 100644 index 1719f497..00000000 --- a/changelog.d/2230.change.rst +++ /dev/null @@ -1 +0,0 @@ -Now warn the user when setuptools is imported after distutils modules have been loaded (exempting PyPy for 3.6), directing the users of packages to import setuptools first. diff --git a/setup.cfg b/setup.cfg index bad15f93..fa0e5656 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.1.3 +version = 49.2.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From eb2ab6bc6e32ef48d4a286c0453c10d10d0f129b Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 13 Jul 2020 09:41:01 -0400 Subject: Remove issue templates The "setuptools warns about incompatibility" template has generated no useful reports and should be removed. --- .github/ISSUE_TEMPLATE/blank.md | 4 -- ...uptools-warns-about-python-2-incompatibility.md | 59 ---------------------- 2 files changed, 63 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/blank.md delete mode 100644 .github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md diff --git a/.github/ISSUE_TEMPLATE/blank.md b/.github/ISSUE_TEMPLATE/blank.md deleted file mode 100644 index e41fc749..00000000 --- a/.github/ISSUE_TEMPLATE/blank.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -name: Bug Report or Feature Request -about: Report a bug or request a feature ---- diff --git a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md b/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md deleted file mode 100644 index 1a4f58f2..00000000 --- a/.github/ISSUE_TEMPLATE/setuptools-warns-about-python-2-incompatibility.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -name: Setuptools warns about Python 2 incompatibility -about: Report the issue where setuptools 45 or later stops working on Python 2 -title: Incompatible install in (summarize your environment) -labels: Python 2 -assignees: '' - ---- - - - -## Prerequisites - - - -- [ ] Read [Python 2 Sunset docs](https://setuptools.readthedocs.io/en/latest/python%202%20sunset.html). -- [ ] Python 2 is required for this application. -- [ ] I maintain the software that installs Setuptools (if not, please contact that project). -- [ ] Setuptools installed with pip 9 or later. -- [ ] Pinning Setuptools to `setuptools<45` in the environment was unsuccessful. - -## Environment Details - -- Operating System and version: -- Python version: -- Python installed how: -- Virtualenv version (if using virtualenv): n/a - -Command(s) that triggered the warning/error (and output): - -``` -``` - -Command(s) used to install setuptools (and output): - -``` -``` - -Output of `pip --version` when installing setuptools: - -``` -``` - -## Other notes -- cgit v1.2.1 From 28e1b5ab2f4d573a91705cdbb025e57023d264b1 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 13 Jul 2020 11:15:26 -0400 Subject: Use .pth file to import distutils from setuptools --- MANIFEST.in | 1 + _distutils_importer/__init__.py | 6 ++++ .../distutils-shim-package/distutils/__init__.py | 3 ++ distutils_precedence.pth | 1 + setup.py | 32 ++++++++++++++++++++++ 5 files changed, 43 insertions(+) create mode 100644 _distutils_importer/__init__.py create mode 100644 _distutils_importer/distutils-shim-package/distutils/__init__.py create mode 100644 distutils_precedence.pth diff --git a/MANIFEST.in b/MANIFEST.in index 128ae280..be83a7f3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -13,4 +13,5 @@ include launcher.c include msvc-build-launcher.cmd include pytest.ini include tox.ini +include distutils_precedence.pth exclude pyproject.toml # Temporary workaround for #1644. diff --git a/_distutils_importer/__init__.py b/_distutils_importer/__init__.py new file mode 100644 index 00000000..54a825fe --- /dev/null +++ b/_distutils_importer/__init__.py @@ -0,0 +1,6 @@ +import sys + +here = os.path.dirname(__file__) +NEW_DISTUTILS_LOCATION = os.path.join(here, 'distutils-shim-package') + +sys.path.insert(0, NEW_DISTUTILS_LOCATION) diff --git a/_distutils_importer/distutils-shim-package/distutils/__init__.py b/_distutils_importer/distutils-shim-package/distutils/__init__.py new file mode 100644 index 00000000..de098c72 --- /dev/null +++ b/_distutils_importer/distutils-shim-package/distutils/__init__.py @@ -0,0 +1,3 @@ +import setuptools.distutils_patch + +from distutils import * diff --git a/distutils_precedence.pth b/distutils_precedence.pth new file mode 100644 index 00000000..2d9b996a --- /dev/null +++ b/distutils_precedence.pth @@ -0,0 +1 @@ +import os; (os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') == 'local' and __import__('_distutils_importer')) diff --git a/setup.py b/setup.py index 1fe18bd1..daab3e87 100755 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ import os import sys import setuptools +from setuptools.command.install import install here = os.path.dirname(__file__) @@ -49,6 +50,7 @@ def _gen_console_scripts(): package_data = dict( setuptools=['script (dev).tmpl', 'script.tmpl', 'site-patch.py'], + _distutils_importer=['distutils-shim-package/distutils/__init__.py'], ) force_windows_specific_files = ( @@ -81,8 +83,38 @@ def pypi_link(pkg_filename): return '/'.join(parts) +class install_with_pth(install): + """ + Custom install command to install a .pth file for distutils patching. + + This is necessary because there's no standard way to install a `.pth` file + alongside your package (and there probably shouldn't be one), but we need + to do this in order to give precedence higher precedence to our version of + `distutils` than the standard library. + """ + + def initialize_options(self): + install.initialize_options(self) + + name = 'distutils_precedence' + with open(os.path.join(here, name + '.pth'), 'rt') as f: + contents = f.read() + + self.extra_path = (name, contents) + + def finalize_options(self): + install.finalize_options(self) + + install_suffix = os.path.relpath(self.install_lib, + self.install_libbase) + + if install_suffix == self.extra_path[1]: + self.install_lib = self.install_libbase + + setup_params = dict( src_root=None, + cmdclass={'install': install_with_pth}, package_data=package_data, entry_points={ "distutils.commands": [ -- cgit v1.2.1 From 03b36b5dc594bbe239d0ad66dc43ea7d1832072c Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 13 Jul 2020 12:02:41 -0400 Subject: Remove warnings With the new `.pth` file, these warnings are no longer necessary. --- setuptools/distutils_patch.py | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index 33f1e7f9..d01a1a1b 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -12,26 +12,10 @@ import importlib import warnings -is_pypy = '__pypy__' in sys.builtin_module_names - - -def warn_distutils_present(): - if 'distutils' not in sys.modules: - return - if is_pypy and sys.version_info < (3, 7): - # PyPy for 3.6 unconditionally imports distutils, so bypass the warning - # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 - return - warnings.warn( - "Distutils was imported before Setuptools. This usage is discouraged " - "and may exhibit undesirable behaviors or errors. Please use " - "Setuptools' objects directly or at least import Setuptools first.") - - def clear_distutils(): if 'distutils' not in sys.modules: return - warnings.warn("Setuptools is replacing distutils.") + mods = [name for name in sys.modules if re.match(r'distutils\b', name)] for name in mods: del sys.modules[name] @@ -56,6 +40,5 @@ def ensure_local_distutils(): assert '_distutils' in core.__file__, core.__file__ -warn_distutils_present() if enabled(): ensure_local_distutils() -- cgit v1.2.1 From 89e9d3c8910c3f419eb9f1c2758a748c6938655b Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 13 Jul 2020 14:43:23 -0400 Subject: Adjust distutils shim when removing _distutils_importer --- _distutils_importer/__init__.py | 17 ++++++++++++++--- setuptools/sandbox.py | 19 ++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/_distutils_importer/__init__.py b/_distutils_importer/__init__.py index 54a825fe..498c4ac1 100644 --- a/_distutils_importer/__init__.py +++ b/_distutils_importer/__init__.py @@ -1,6 +1,17 @@ import sys -here = os.path.dirname(__file__) -NEW_DISTUTILS_LOCATION = os.path.join(here, 'distutils-shim-package') +_HERE = os.path.dirname(__file__) +NEW_DISTUTILS_LOCATION = os.path.join(_HERE, 'distutils-shim-package') -sys.path.insert(0, NEW_DISTUTILS_LOCATION) +def add_shim(): + if NEW_DISTUTILS_LOCATION not in sys.path: + sys.path.insert(0, NEW_DISTUTILS_LOCATION) + +def remove_shim(): + try: + sys.path.remove(NEW_DISTUTILS_LOCATION) + except ValueError: + pass + + +add_shim() diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 93ae8eb4..342a713f 100644 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -185,8 +185,8 @@ def setup_context(setup_dir): temp_dir = os.path.join(setup_dir, 'temp') with save_pkg_resources_state(): with save_modules(): - hide_setuptools() with save_path(): + hide_setuptools() with save_argv(): with override_temp(temp_dir): with pushd(setup_dir): @@ -195,6 +195,15 @@ def setup_context(setup_dir): yield +_MODULES_TO_HIDE = { + 'setuptools', + 'distutils', + 'pkg_resources', + 'Cython', + '_distutils_importer', +} + + def _needs_hiding(mod_name): """ >>> _needs_hiding('setuptools') @@ -212,8 +221,8 @@ def _needs_hiding(mod_name): >>> _needs_hiding('Cython') True """ - pattern = re.compile(r'(setuptools|pkg_resources|distutils|Cython)(\.|$)') - return bool(pattern.match(mod_name)) + base_module = mod_name.split('.', 1)[0] + return base_module in _MODULES_TO_HIDE def hide_setuptools(): @@ -223,6 +232,10 @@ def hide_setuptools(): necessary to avoid issues such as #315 where setuptools upgrading itself would fail to find a function declared in the metadata. """ + _distutils_importer = sys.modules.get('_distutils_importer', None) + if _distutils_importer is not None: + _distutils_importer.remove_shim() + modules = filter(_needs_hiding, sys.modules) _clear_modules(modules) -- cgit v1.2.1 From 642604f82c01175f2ad285800d969ff521495af0 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 13 Jul 2020 14:44:12 -0400 Subject: Clean up setuptools/__init__.py imports This puts non-distutils imports first and removes one unused import. --- setuptools/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 83882511..d9740403 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -1,17 +1,16 @@ """Extensions to the 'distutils' for large or complex distributions""" -import os +from fnmatch import fnmatchcase import functools +import os +import re # Disabled for now due to: #2228, #2230 import setuptools.distutils_patch # noqa: F401 import distutils.core -import distutils.filelist -import re from distutils.errors import DistutilsOptionError from distutils.util import convert_path -from fnmatch import fnmatchcase from ._deprecation_warning import SetuptoolsDeprecationWarning -- cgit v1.2.1 From 370839b417f6bafe783fa040646d80bdf673fac4 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 13 Jul 2020 15:25:32 -0400 Subject: Use import hook instead of sys.path manipulation --- _distutils_importer/__init__.py | 32 ++++++++++++++++++---- .../distutils-shim-package/distutils/__init__.py | 3 -- setup.py | 1 - 3 files changed, 27 insertions(+), 9 deletions(-) delete mode 100644 _distutils_importer/distutils-shim-package/distutils/__init__.py diff --git a/_distutils_importer/__init__.py b/_distutils_importer/__init__.py index 498c4ac1..323ae203 100644 --- a/_distutils_importer/__init__.py +++ b/_distutils_importer/__init__.py @@ -1,15 +1,37 @@ import sys -_HERE = os.path.dirname(__file__) -NEW_DISTUTILS_LOCATION = os.path.join(_HERE, 'distutils-shim-package') + +class DistutilsMetaFinder: + def find_spec(self, fullname, path, target=None): + if path is not None or fullname != "distutils": + return None + + return self.get_distutils_spec() + + def get_distutils_spec(self): + import importlib + + class DistutilsLoader(importlib.util.abc.Loader): + + def create_module(self, spec): + return importlib.import_module('._distutils', 'setuptools') + + def exec_module(self, module): + pass + + return importlib.util.spec_from_loader('distutils', DistutilsLoader()) + + +DISTUTILS_FINDER = DistutilsMetaFinder() + def add_shim(): - if NEW_DISTUTILS_LOCATION not in sys.path: - sys.path.insert(0, NEW_DISTUTILS_LOCATION) + sys.meta_path.insert(0, DISTUTILS_FINDER) + def remove_shim(): try: - sys.path.remove(NEW_DISTUTILS_LOCATION) + sys.path.remove(DISTUTILS_FINDER) except ValueError: pass diff --git a/_distutils_importer/distutils-shim-package/distutils/__init__.py b/_distutils_importer/distutils-shim-package/distutils/__init__.py deleted file mode 100644 index de098c72..00000000 --- a/_distutils_importer/distutils-shim-package/distutils/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import setuptools.distutils_patch - -from distutils import * diff --git a/setup.py b/setup.py index daab3e87..cba37d3e 100755 --- a/setup.py +++ b/setup.py @@ -50,7 +50,6 @@ def _gen_console_scripts(): package_data = dict( setuptools=['script (dev).tmpl', 'script.tmpl', 'site-patch.py'], - _distutils_importer=['distutils-shim-package/distutils/__init__.py'], ) force_windows_specific_files = ( -- cgit v1.2.1 From 85a0a9026d1b40448d1757ca6cd75e5cc2c50fc6 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 13 Jul 2020 15:36:39 -0400 Subject: Revert "Remove warnings" This reverts commit 30b883f0b8071a3b1472c884574f38ce0128e457. --- setuptools/distutils_patch.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index d01a1a1b..33f1e7f9 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -12,10 +12,26 @@ import importlib import warnings -def clear_distutils(): +is_pypy = '__pypy__' in sys.builtin_module_names + + +def warn_distutils_present(): if 'distutils' not in sys.modules: return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + warnings.warn( + "Distutils was imported before Setuptools. This usage is discouraged " + "and may exhibit undesirable behaviors or errors. Please use " + "Setuptools' objects directly or at least import Setuptools first.") + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + warnings.warn("Setuptools is replacing distutils.") mods = [name for name in sys.modules if re.match(r'distutils\b', name)] for name in mods: del sys.modules[name] @@ -40,5 +56,6 @@ def ensure_local_distutils(): assert '_distutils' in core.__file__, core.__file__ +warn_distutils_present() if enabled(): ensure_local_distutils() -- cgit v1.2.1 From 48cc75e005b18549b424e4a794785c363cb1810a Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sat, 18 Jul 2020 11:54:57 +0200 Subject: Set up GHA to test on Python 3.9-beta and 3.8-dev --- .github/workflows/python-tests.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index f2188d38..5e16ab30 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -25,14 +25,20 @@ jobs: - 3.6 - 3.5 os: - - ubuntu-latest + - ubuntu-18.04 - ubuntu-16.04 - macOS-latest # - windows-2019 # - windows-2016 include: - # Dev versions - - { python-version: 3.9-dev, os: ubuntu-20.04 } + # Pre-release versions (GH-shipped) + - os: ubuntu-20.04 + python-version: 3.9.0-beta.4 - 3.9.0 + # Dev versions (deadsnakes) + - os: ubuntu-20.04 + python-version: 3.9-dev + - os: ubuntu-20.04 + python-version: 3.8-dev env: NETWORK_REQUIRED: 1 @@ -47,7 +53,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1.1.1 + uses: actions/setup-python@v2 if: "!endsWith(matrix.python-version, '-dev')" with: python-version: ${{ matrix.python-version }} -- cgit v1.2.1 From bec11f03ab81da8af69909697925166793893e07 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Jul 2020 13:46:01 -0400 Subject: Add docs on porting from distutils. --- docs/distutils-legacy.txt | 25 +++++++++++++++++++++++++ docs/index.txt | 1 + 2 files changed, 26 insertions(+) create mode 100644 docs/distutils-legacy.txt diff --git a/docs/distutils-legacy.txt b/docs/distutils-legacy.txt new file mode 100644 index 00000000..a5d96260 --- /dev/null +++ b/docs/distutils-legacy.txt @@ -0,0 +1,25 @@ +Porting from Distutils +====================== + +Setuptools and the PyPA have a `stated goal `_ to make Setuptools the reference API for distutils. + +Since the 49.1.2 release, Setuptools includes a local, vendored copy of distutils (from late copies of CPython) that is disabled by default. To enable the use of this copy of distutils when invoking setuptools, set the enviroment variable: + + SETUPTOOLS_USE_DISTUTILS=local + +This behavior is planned to become the default. + +Prefer Setuptools +----------------- + +As Distutils is deprecated, any usage of functions or objects from distutils is similarly discouraged, and Setuptools aims to replace or deprecate all such uses. This section describes the recommended replacements. + +``distutils.core.setup`` → ``setuptools.setup`` + +``distutils.cmd.Command`` → ``setuptools.Command`` + +``distutils.log`` → (no replacement yet) + +``distutils.version.*`` → ``packaging.version.*`` + +If a project relies on uses of ``distutils`` that do not have a suitable replacement above, please search the `Setuptools issue tracker `_ and file a request, describing the use-case so that Setuptools' maintainers can investigate. Please provide enough detail to help the maintainers understand how distutils is used, what value it provides, and why that behavior should be supported. diff --git a/docs/index.txt b/docs/index.txt index 228f97c8..0dd5fd94 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -18,4 +18,5 @@ Documentation content: development roadmap Deprecated: Easy Install + distutils-legacy history -- cgit v1.2.1 From 47e5198e09060c8ff59f659ae56c6e64dcb3607e Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sun, 19 Jul 2020 11:37:34 +0200 Subject: Make tox show tests output in parallel mode @ GHA --- .github/workflows/python-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 5e16ab30..28ef36f7 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -106,6 +106,7 @@ jobs: python -m tox --parallel auto + --parallel-live --notest --skip-missing-interpreters false - name: Test with tox @@ -113,3 +114,4 @@ jobs: python -m tox --parallel auto + --parallel-live -- cgit v1.2.1 From f976af5fbcca95cef61b8e228ca3653a094f8a7a Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sun, 19 Jul 2020 12:41:52 +0200 Subject: debug! make pytest extremely verbose --- .github/workflows/python-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 28ef36f7..a9bc859a 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -115,3 +115,5 @@ jobs: tox --parallel auto --parallel-live + -- + -vvvvv -- cgit v1.2.1 From 2b8b730db204d9d9a279802296de670b8a523fc0 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sun, 19 Jul 2020 15:24:33 +0200 Subject: Add Python 3.9 beta from deadsnakes --- .github/workflows/python-tests.yml | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index a9bc859a..ed82aebb 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -34,6 +34,9 @@ jobs: # Pre-release versions (GH-shipped) - os: ubuntu-20.04 python-version: 3.9.0-beta.4 - 3.9.0 + # Pre-release versions (deadsnakes) + - os: ubuntu-20.04 + python-version: 3.9-beta # Dev versions (deadsnakes) - os: ubuntu-20.04 python-version: 3.9-dev @@ -42,21 +45,34 @@ jobs: env: NETWORK_REQUIRED: 1 + PYTHON_VERSION: ${{ matrix.python-version }} TOX_PARALLEL_NO_SPINNER: 1 TOXENV: python + USE_DEADSNAKES: false steps: - uses: actions/checkout@master - - name: Set up Python ${{ matrix.python-version }} (deadsnakes) + - name: Set flag to use deadsnakes + if: >- + endsWith(env.PYTHON_VERSION, '-beta') || + endsWith(env.PYTHON_VERSION, '-dev') + run: | + from __future__ import print_function + python_version = '${{ env.PYTHON_VERSION }}'.replace('-beta', '') + print('::set-env name=PYTHON_VERSION::{ver}'.format(ver=python_version)) + print('::set-env name=USE_DEADSNAKES::true') + shell: python + - name: Set up Python ${{ env.PYTHON_VERSION }} (deadsnakes) uses: deadsnakes/action@v1.0.0 - if: endsWith(matrix.python-version, '-dev') + if: fromJSON(env.USE_DEADSNAKES) && true || false with: - python-version: ${{ matrix.python-version }} - - name: Set up Python ${{ matrix.python-version }} + python-version: ${{ env.PYTHON_VERSION }} + - name: Set up Python ${{ env.PYTHON_VERSION }} uses: actions/setup-python@v2 - if: "!endsWith(matrix.python-version, '-dev')" + if: >- + !fromJSON(env.USE_DEADSNAKES) && true || false with: - python-version: ${{ matrix.python-version }} + python-version: ${{ env.PYTHON_VERSION }} - name: Log Python version run: >- python --version @@ -88,9 +104,9 @@ jobs: run: >- python -m pip freeze --all - name: Adjust TOXENV for PyPy - if: startsWith(matrix.python-version, 'pypy') + if: startsWith(env.PYTHON_VERSION, 'pypy') run: >- - echo "::set-env name=TOXENV::${{ matrix.python-version }}" + echo "::set-env name=TOXENV::${{ env.PYTHON_VERSION }}" - name: Log env vars run: >- env -- cgit v1.2.1 From e371422476f51a83d27d70dc45bbfba1544aad55 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 19 Jul 2020 21:36:33 -0400 Subject: Consolidate distutils importing hacks into _distutils_importer package. Generate distutils-precedence.pth inline. --- MANIFEST.in | 1 - _distutils_importer/__init__.py | 19 +++++++++++-- _distutils_importer/install.py | 5 ++++ _distutils_importer/override.py | 47 +++++++++++++++++++++++++++++++ conftest.py | 2 +- distutils_precedence.pth | 1 - setup.py | 8 ++---- setuptools/__init__.py | 3 +- setuptools/distutils_patch.py | 61 ----------------------------------------- 9 files changed, 72 insertions(+), 75 deletions(-) create mode 100644 _distutils_importer/install.py create mode 100644 _distutils_importer/override.py delete mode 100644 distutils_precedence.pth delete mode 100644 setuptools/distutils_patch.py diff --git a/MANIFEST.in b/MANIFEST.in index be83a7f3..128ae280 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -13,5 +13,4 @@ include launcher.c include msvc-build-launcher.cmd include pytest.ini include tox.ini -include distutils_precedence.pth exclude pyproject.toml # Temporary workaround for #1644. diff --git a/_distutils_importer/__init__.py b/_distutils_importer/__init__.py index 323ae203..ffa4caea 100644 --- a/_distutils_importer/__init__.py +++ b/_distutils_importer/__init__.py @@ -1,4 +1,20 @@ +""" +Ensure that the local copy of distutils is preferred over stdlib. + +See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 +for more motivation. +""" + import sys +import os + + +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') + return which == 'local' class DistutilsMetaFinder: @@ -34,6 +50,3 @@ def remove_shim(): sys.path.remove(DISTUTILS_FINDER) except ValueError: pass - - -add_shim() diff --git a/_distutils_importer/install.py b/_distutils_importer/install.py new file mode 100644 index 00000000..73f13b29 --- /dev/null +++ b/_distutils_importer/install.py @@ -0,0 +1,5 @@ +from . import enabled, add_shim + + +if enabled(): + add_shim() diff --git a/_distutils_importer/override.py b/_distutils_importer/override.py new file mode 100644 index 00000000..91738485 --- /dev/null +++ b/_distutils_importer/override.py @@ -0,0 +1,47 @@ +import sys +import re +import importlib +import warnings + +from . import enabled + + +is_pypy = '__pypy__' in sys.builtin_module_names + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + warnings.warn( + "Distutils was imported before Setuptools. This usage is discouraged " + "and may exhibit undesirable behaviors or errors. Please use " + "Setuptools' objects directly or at least import Setuptools first.") + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + warnings.warn("Setuptools is replacing distutils.") + mods = [name for name in sys.modules if re.match(r'distutils\b', name)] + for name in mods: + del sys.modules[name] + + +def ensure_local_distutils(): + clear_distutils() + distutils = importlib.import_module('setuptools._distutils') + distutils.__name__ = 'distutils' + sys.modules['distutils'] = distutils + + # sanity check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + + +warn_distutils_present() +if enabled(): + ensure_local_distutils() diff --git a/conftest.py b/conftest.py index 6013e187..868bf5be 100644 --- a/conftest.py +++ b/conftest.py @@ -15,7 +15,7 @@ collect_ignore = [ 'tests/manual_test.py', 'setuptools/tests/mod_with_constant.py', 'setuptools/_distutils', - 'setuptools/distutils_patch.py', + '_distutils_importer', ] diff --git a/distutils_precedence.pth b/distutils_precedence.pth deleted file mode 100644 index 2d9b996a..00000000 --- a/distutils_precedence.pth +++ /dev/null @@ -1 +0,0 @@ -import os; (os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') == 'local' and __import__('_distutils_importer')) diff --git a/setup.py b/setup.py index cba37d3e..a6e1abc4 100755 --- a/setup.py +++ b/setup.py @@ -94,12 +94,8 @@ class install_with_pth(install): def initialize_options(self): install.initialize_options(self) - - name = 'distutils_precedence' - with open(os.path.join(here, name + '.pth'), 'rt') as f: - contents = f.read() - - self.extra_path = (name, contents) + self.extra_path = ( + 'distutils-precedence', 'import _distutils_importer.install') def finalize_options(self): install.finalize_options(self) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index d9740403..80b287b4 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -5,8 +5,7 @@ import functools import os import re -# Disabled for now due to: #2228, #2230 -import setuptools.distutils_patch # noqa: F401 +import _distutils_importer.override # noqa: F401 import distutils.core from distutils.errors import DistutilsOptionError diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py deleted file mode 100644 index 33f1e7f9..00000000 --- a/setuptools/distutils_patch.py +++ /dev/null @@ -1,61 +0,0 @@ -""" -Ensure that the local copy of distutils is preferred over stdlib. - -See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 -for more motivation. -""" - -import sys -import re -import os -import importlib -import warnings - - -is_pypy = '__pypy__' in sys.builtin_module_names - - -def warn_distutils_present(): - if 'distutils' not in sys.modules: - return - if is_pypy and sys.version_info < (3, 7): - # PyPy for 3.6 unconditionally imports distutils, so bypass the warning - # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 - return - warnings.warn( - "Distutils was imported before Setuptools. This usage is discouraged " - "and may exhibit undesirable behaviors or errors. Please use " - "Setuptools' objects directly or at least import Setuptools first.") - - -def clear_distutils(): - if 'distutils' not in sys.modules: - return - warnings.warn("Setuptools is replacing distutils.") - mods = [name for name in sys.modules if re.match(r'distutils\b', name)] - for name in mods: - del sys.modules[name] - - -def enabled(): - """ - Allow selection of distutils by environment variable. - """ - which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') - return which == 'local' - - -def ensure_local_distutils(): - clear_distutils() - distutils = importlib.import_module('setuptools._distutils') - distutils.__name__ = 'distutils' - sys.modules['distutils'] = distutils - - # sanity check that submodules load as expected - core = importlib.import_module('distutils.core') - assert '_distutils' in core.__file__, core.__file__ - - -warn_distutils_present() -if enabled(): - ensure_local_distutils() -- cgit v1.2.1 From 2986be4e55d6c8113344d5e184d40c6c3945a2bb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 19 Jul 2020 21:49:24 -0400 Subject: Fix AttributeError when `importlib.util` was not otherwise imported. --- _distutils_importer/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_distutils_importer/__init__.py b/_distutils_importer/__init__.py index ffa4caea..06674eb8 100644 --- a/_distutils_importer/__init__.py +++ b/_distutils_importer/__init__.py @@ -25,7 +25,7 @@ class DistutilsMetaFinder: return self.get_distutils_spec() def get_distutils_spec(self): - import importlib + import importlib.util class DistutilsLoader(importlib.util.abc.Loader): -- cgit v1.2.1 From 1e53a2c14e7e0f788c9df2a542ac10f6b2f511d7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 19 Jul 2020 21:53:39 -0400 Subject: Move docstring closer to relevant context --- _distutils_importer/__init__.py | 7 ------- _distutils_importer/override.py | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/_distutils_importer/__init__.py b/_distutils_importer/__init__.py index 06674eb8..3ad70100 100644 --- a/_distutils_importer/__init__.py +++ b/_distutils_importer/__init__.py @@ -1,10 +1,3 @@ -""" -Ensure that the local copy of distutils is preferred over stdlib. - -See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 -for more motivation. -""" - import sys import os diff --git a/_distutils_importer/override.py b/_distutils_importer/override.py index 91738485..523139bb 100644 --- a/_distutils_importer/override.py +++ b/_distutils_importer/override.py @@ -1,3 +1,10 @@ +""" +Ensure that the local copy of distutils is preferred over stdlib. + +See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 +for more motivation. +""" + import sys import re import importlib -- cgit v1.2.1 From dec637b12e1eee6f884405c56a4d01c505c00dfe Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 21 Jul 2020 15:05:28 +0200 Subject: Try out actions/setup-python@v2.1.1 --- .github/workflows/python-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index ed82aebb..93ec79d4 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -68,7 +68,7 @@ jobs: with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v2.1.1 if: >- !fromJSON(env.USE_DEADSNAKES) && true || false with: -- cgit v1.2.1 From a1686b39e311c38e1c32e0fdfe06597611300861 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 24 Jul 2020 21:08:34 -0400 Subject: Revert "Render logo in the readme." This reverts commit 4056435460b770c3ab7a8ea1637f05de690e7e01. --- README.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.rst b/README.rst index dc2bf98b..9cbf7b86 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,3 @@ -.. image:: https://raw.githubusercontent.com/pypa/setuptools/master/docs/logo/banner%201%20line%20color.svg - - .. image:: https://img.shields.io/pypi/v/setuptools.svg :target: `PyPI link`_ @@ -27,7 +24,6 @@ .. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme - See the `Installation Instructions `_ in the Python Packaging User's Guide for instructions on installing, upgrading, and uninstalling -- cgit v1.2.1 From 2bf12c542af74c6e8b76ffd864c95c2efd94d372 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 24 Jul 2020 21:08:56 -0400 Subject: Revert "Rename logo assets to remove project name and 'logo', which are implied by the context." This reverts commit fd94cd038d644ff27866301f6f365a3ef0901898. --- docs/logo/banner 1 line color.png | Bin 24183 -> 0 bytes docs/logo/banner 1 line color.svg | 223 -------------------- docs/logo/banner 2 lines color.png | Bin 38105 -> 0 bytes docs/logo/banner 2 lines color.svg | 224 -------------------- docs/logo/full color 1000px.png | Bin 31520 -> 0 bytes docs/logo/full color.svg | 227 --------------------- docs/logo/setup_tools_logo_colour.svg | 227 +++++++++++++++++++++ docs/logo/setup_tools_logo_colour_1000px.png | Bin 0 -> 31520 bytes docs/logo/setup_tools_logo_colour_banner_1line.svg | 223 ++++++++++++++++++++ ...setup_tools_logo_colour_banner_1line_1000px.png | Bin 0 -> 24183 bytes .../logo/setup_tools_logo_colour_banner_2lines.svg | 224 ++++++++++++++++++++ ...etup_tools_logo_colour_banner_2lines_1000px.png | Bin 0 -> 38105 bytes docs/logo/setup_tools_logo_symbol_colour.svg | 203 ++++++++++++++++++ .../logo/setup_tools_logo_symbol_colour_1000px.png | Bin 0 -> 44239 bytes docs/logo/setup_tools_logotype_1line.svg | 169 +++++++++++++++ docs/logo/symbol color 1000px.png | Bin 44239 -> 0 bytes docs/logo/symbol color.svg | 203 ------------------ docs/logo/type.svg | 169 --------------- 18 files changed, 1046 insertions(+), 1046 deletions(-) delete mode 100644 docs/logo/banner 1 line color.png delete mode 100644 docs/logo/banner 1 line color.svg delete mode 100644 docs/logo/banner 2 lines color.png delete mode 100644 docs/logo/banner 2 lines color.svg delete mode 100644 docs/logo/full color 1000px.png delete mode 100644 docs/logo/full color.svg create mode 100644 docs/logo/setup_tools_logo_colour.svg create mode 100644 docs/logo/setup_tools_logo_colour_1000px.png create mode 100644 docs/logo/setup_tools_logo_colour_banner_1line.svg create mode 100644 docs/logo/setup_tools_logo_colour_banner_1line_1000px.png create mode 100644 docs/logo/setup_tools_logo_colour_banner_2lines.svg create mode 100644 docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png create mode 100644 docs/logo/setup_tools_logo_symbol_colour.svg create mode 100644 docs/logo/setup_tools_logo_symbol_colour_1000px.png create mode 100644 docs/logo/setup_tools_logotype_1line.svg delete mode 100644 docs/logo/symbol color 1000px.png delete mode 100644 docs/logo/symbol color.svg delete mode 100644 docs/logo/type.svg diff --git a/docs/logo/banner 1 line color.png b/docs/logo/banner 1 line color.png deleted file mode 100644 index d1604289..00000000 Binary files a/docs/logo/banner 1 line color.png and /dev/null differ diff --git a/docs/logo/banner 1 line color.svg b/docs/logo/banner 1 line color.svg deleted file mode 100644 index d6dc9aac..00000000 --- a/docs/logo/banner 1 line color.svg +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/docs/logo/banner 2 lines color.png b/docs/logo/banner 2 lines color.png deleted file mode 100644 index 89370a9d..00000000 Binary files a/docs/logo/banner 2 lines color.png and /dev/null differ diff --git a/docs/logo/banner 2 lines color.svg b/docs/logo/banner 2 lines color.svg deleted file mode 100644 index 991577fb..00000000 --- a/docs/logo/banner 2 lines color.svg +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/docs/logo/full color 1000px.png b/docs/logo/full color 1000px.png deleted file mode 100644 index c25a23b9..00000000 Binary files a/docs/logo/full color 1000px.png and /dev/null differ diff --git a/docs/logo/full color.svg b/docs/logo/full color.svg deleted file mode 100644 index 7eae8fc3..00000000 --- a/docs/logo/full color.svg +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/docs/logo/setup_tools_logo_colour.svg b/docs/logo/setup_tools_logo_colour.svg new file mode 100644 index 00000000..7eae8fc3 --- /dev/null +++ b/docs/logo/setup_tools_logo_colour.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/docs/logo/setup_tools_logo_colour_1000px.png b/docs/logo/setup_tools_logo_colour_1000px.png new file mode 100644 index 00000000..c25a23b9 Binary files /dev/null and b/docs/logo/setup_tools_logo_colour_1000px.png differ diff --git a/docs/logo/setup_tools_logo_colour_banner_1line.svg b/docs/logo/setup_tools_logo_colour_banner_1line.svg new file mode 100644 index 00000000..d6dc9aac --- /dev/null +++ b/docs/logo/setup_tools_logo_colour_banner_1line.svg @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png b/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png new file mode 100644 index 00000000..d1604289 Binary files /dev/null and b/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png differ diff --git a/docs/logo/setup_tools_logo_colour_banner_2lines.svg b/docs/logo/setup_tools_logo_colour_banner_2lines.svg new file mode 100644 index 00000000..991577fb --- /dev/null +++ b/docs/logo/setup_tools_logo_colour_banner_2lines.svg @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png b/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png new file mode 100644 index 00000000..89370a9d Binary files /dev/null and b/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png differ diff --git a/docs/logo/setup_tools_logo_symbol_colour.svg b/docs/logo/setup_tools_logo_symbol_colour.svg new file mode 100644 index 00000000..2936cbb5 --- /dev/null +++ b/docs/logo/setup_tools_logo_symbol_colour.svg @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/docs/logo/setup_tools_logo_symbol_colour_1000px.png b/docs/logo/setup_tools_logo_symbol_colour_1000px.png new file mode 100644 index 00000000..e0c36fc1 Binary files /dev/null and b/docs/logo/setup_tools_logo_symbol_colour_1000px.png differ diff --git a/docs/logo/setup_tools_logotype_1line.svg b/docs/logo/setup_tools_logotype_1line.svg new file mode 100644 index 00000000..dbb9c1de --- /dev/null +++ b/docs/logo/setup_tools_logotype_1line.svg @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/docs/logo/symbol color 1000px.png b/docs/logo/symbol color 1000px.png deleted file mode 100644 index e0c36fc1..00000000 Binary files a/docs/logo/symbol color 1000px.png and /dev/null differ diff --git a/docs/logo/symbol color.svg b/docs/logo/symbol color.svg deleted file mode 100644 index 2936cbb5..00000000 --- a/docs/logo/symbol color.svg +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - diff --git a/docs/logo/type.svg b/docs/logo/type.svg deleted file mode 100644 index dbb9c1de..00000000 --- a/docs/logo/type.svg +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - -- cgit v1.2.1 From 9dd762c61bdea4491f62c2b812b5cd5672821154 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 24 Jul 2020 21:11:16 -0400 Subject: Revert "Merge pull request #2229 from cajhne/logo001" This reverts commit 402880a7aed5dea1cf3a84af2b8291e451fb3d9f, reversing changes made to bd1102648109c85c782286787e4d5290ae280abe. --- docs/logo/README.md | 10 - docs/logo/josefinsans/.uuid | 1 - docs/logo/josefinsans/DESCRIPTION.en_us.html | 9 - docs/logo/josefinsans/JosefinSans-Bold.ttf | Bin 86300 -> 0 bytes docs/logo/josefinsans/JosefinSans-BoldItalic.ttf | Bin 83100 -> 0 bytes docs/logo/josefinsans/JosefinSans-Italic.ttf | Bin 84916 -> 0 bytes docs/logo/josefinsans/JosefinSans-Light.ttf | Bin 87320 -> 0 bytes docs/logo/josefinsans/JosefinSans-LightItalic.ttf | Bin 85684 -> 0 bytes docs/logo/josefinsans/JosefinSans-Regular.ttf | Bin 87260 -> 0 bytes docs/logo/josefinsans/JosefinSans-SemiBold.ttf | Bin 87880 -> 0 bytes .../josefinsans/JosefinSans-SemiBoldItalic.ttf | Bin 84824 -> 0 bytes docs/logo/josefinsans/JosefinSans-Thin.ttf | Bin 88088 -> 0 bytes docs/logo/josefinsans/JosefinSans-ThinItalic.ttf | Bin 85420 -> 0 bytes docs/logo/josefinsans/METADATA.pb | 99 --------- docs/logo/josefinsans/OFL.txt | 93 --------- docs/logo/setup_tools_logo_colour.svg | 227 --------------------- docs/logo/setup_tools_logo_colour_1000px.png | Bin 31520 -> 0 bytes docs/logo/setup_tools_logo_colour_banner_1line.svg | 223 -------------------- ...setup_tools_logo_colour_banner_1line_1000px.png | Bin 24183 -> 0 bytes .../logo/setup_tools_logo_colour_banner_2lines.svg | 224 -------------------- ...etup_tools_logo_colour_banner_2lines_1000px.png | Bin 38105 -> 0 bytes docs/logo/setup_tools_logo_symbol_colour.svg | 203 ------------------ .../logo/setup_tools_logo_symbol_colour_1000px.png | Bin 44239 -> 0 bytes docs/logo/setup_tools_logotype_1line.svg | 169 --------------- 24 files changed, 1258 deletions(-) delete mode 100644 docs/logo/README.md delete mode 100644 docs/logo/josefinsans/.uuid delete mode 100644 docs/logo/josefinsans/DESCRIPTION.en_us.html delete mode 100644 docs/logo/josefinsans/JosefinSans-Bold.ttf delete mode 100644 docs/logo/josefinsans/JosefinSans-BoldItalic.ttf delete mode 100644 docs/logo/josefinsans/JosefinSans-Italic.ttf delete mode 100644 docs/logo/josefinsans/JosefinSans-Light.ttf delete mode 100644 docs/logo/josefinsans/JosefinSans-LightItalic.ttf delete mode 100644 docs/logo/josefinsans/JosefinSans-Regular.ttf delete mode 100644 docs/logo/josefinsans/JosefinSans-SemiBold.ttf delete mode 100644 docs/logo/josefinsans/JosefinSans-SemiBoldItalic.ttf delete mode 100644 docs/logo/josefinsans/JosefinSans-Thin.ttf delete mode 100644 docs/logo/josefinsans/JosefinSans-ThinItalic.ttf delete mode 100644 docs/logo/josefinsans/METADATA.pb delete mode 100644 docs/logo/josefinsans/OFL.txt delete mode 100644 docs/logo/setup_tools_logo_colour.svg delete mode 100644 docs/logo/setup_tools_logo_colour_1000px.png delete mode 100644 docs/logo/setup_tools_logo_colour_banner_1line.svg delete mode 100644 docs/logo/setup_tools_logo_colour_banner_1line_1000px.png delete mode 100644 docs/logo/setup_tools_logo_colour_banner_2lines.svg delete mode 100644 docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png delete mode 100644 docs/logo/setup_tools_logo_symbol_colour.svg delete mode 100644 docs/logo/setup_tools_logo_symbol_colour_1000px.png delete mode 100644 docs/logo/setup_tools_logotype_1line.svg diff --git a/docs/logo/README.md b/docs/logo/README.md deleted file mode 100644 index 74fadbf0..00000000 --- a/docs/logo/README.md +++ /dev/null @@ -1,10 +0,0 @@ -![](setup_tools_logo_colour.svg) -### Design: - -Setuptools logo designed in 2020 by [C.Rogers](crogersmedia.com) for the Setuptools project using the Free Open Source graphics editor [Inkscape](inkscape.org). - -### Copyright: -Logo is (c) the Setuptools developers. - -### Font: -The font used is the Open Font "Josefin Sans", which is available for free under the Open Font License (OFL). diff --git a/docs/logo/josefinsans/.uuid b/docs/logo/josefinsans/.uuid deleted file mode 100644 index d7e92c77..00000000 --- a/docs/logo/josefinsans/.uuid +++ /dev/null @@ -1 +0,0 @@ -922c129c-9f4c-4831-b632-c7f43be6feb0 \ No newline at end of file diff --git a/docs/logo/josefinsans/DESCRIPTION.en_us.html b/docs/logo/josefinsans/DESCRIPTION.en_us.html deleted file mode 100644 index 9364b249..00000000 --- a/docs/logo/josefinsans/DESCRIPTION.en_us.html +++ /dev/null @@ -1,9 +0,0 @@ -

-The idea of this typeface is to be geometric, elegant, with a vintage feeling, for use at larger sizes. -It is inspired by geometric sans serif designs from the 1920s. -The x-height is half way from baseline to cap height, an unusual proportion. -

-

-There is a sister family, Josefin Slab -

- diff --git a/docs/logo/josefinsans/JosefinSans-Bold.ttf b/docs/logo/josefinsans/JosefinSans-Bold.ttf deleted file mode 100644 index 12a7ad08..00000000 Binary files a/docs/logo/josefinsans/JosefinSans-Bold.ttf and /dev/null differ diff --git a/docs/logo/josefinsans/JosefinSans-BoldItalic.ttf b/docs/logo/josefinsans/JosefinSans-BoldItalic.ttf deleted file mode 100644 index 4a0fc91d..00000000 Binary files a/docs/logo/josefinsans/JosefinSans-BoldItalic.ttf and /dev/null differ diff --git a/docs/logo/josefinsans/JosefinSans-Italic.ttf b/docs/logo/josefinsans/JosefinSans-Italic.ttf deleted file mode 100644 index 1cbe036e..00000000 Binary files a/docs/logo/josefinsans/JosefinSans-Italic.ttf and /dev/null differ diff --git a/docs/logo/josefinsans/JosefinSans-Light.ttf b/docs/logo/josefinsans/JosefinSans-Light.ttf deleted file mode 100644 index 7fe3f7be..00000000 Binary files a/docs/logo/josefinsans/JosefinSans-Light.ttf and /dev/null differ diff --git a/docs/logo/josefinsans/JosefinSans-LightItalic.ttf b/docs/logo/josefinsans/JosefinSans-LightItalic.ttf deleted file mode 100644 index 8dc35383..00000000 Binary files a/docs/logo/josefinsans/JosefinSans-LightItalic.ttf and /dev/null differ diff --git a/docs/logo/josefinsans/JosefinSans-Regular.ttf b/docs/logo/josefinsans/JosefinSans-Regular.ttf deleted file mode 100644 index ed119008..00000000 Binary files a/docs/logo/josefinsans/JosefinSans-Regular.ttf and /dev/null differ diff --git a/docs/logo/josefinsans/JosefinSans-SemiBold.ttf b/docs/logo/josefinsans/JosefinSans-SemiBold.ttf deleted file mode 100644 index b67504a6..00000000 Binary files a/docs/logo/josefinsans/JosefinSans-SemiBold.ttf and /dev/null differ diff --git a/docs/logo/josefinsans/JosefinSans-SemiBoldItalic.ttf b/docs/logo/josefinsans/JosefinSans-SemiBoldItalic.ttf deleted file mode 100644 index 24a27d15..00000000 Binary files a/docs/logo/josefinsans/JosefinSans-SemiBoldItalic.ttf and /dev/null differ diff --git a/docs/logo/josefinsans/JosefinSans-Thin.ttf b/docs/logo/josefinsans/JosefinSans-Thin.ttf deleted file mode 100644 index c54ca7f1..00000000 Binary files a/docs/logo/josefinsans/JosefinSans-Thin.ttf and /dev/null differ diff --git a/docs/logo/josefinsans/JosefinSans-ThinItalic.ttf b/docs/logo/josefinsans/JosefinSans-ThinItalic.ttf deleted file mode 100644 index 6840b7c2..00000000 Binary files a/docs/logo/josefinsans/JosefinSans-ThinItalic.ttf and /dev/null differ diff --git a/docs/logo/josefinsans/METADATA.pb b/docs/logo/josefinsans/METADATA.pb deleted file mode 100644 index 8b67716c..00000000 --- a/docs/logo/josefinsans/METADATA.pb +++ /dev/null @@ -1,99 +0,0 @@ -name: "Josefin Sans" -designer: "Santiago Orozco" -license: "OFL" -category: "SANS_SERIF" -date_added: "2010-11-17" -fonts { - name: "Josefin Sans" - style: "normal" - weight: 100 - filename: "JosefinSans-Thin.ttf" - post_script_name: "JosefinSans-Thin" - full_name: "Josefin Sans Thin" - copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." -} -fonts { - name: "Josefin Sans" - style: "italic" - weight: 100 - filename: "JosefinSans-ThinItalic.ttf" - post_script_name: "JosefinSans-ThinItalic" - full_name: "Josefin Sans Thin Italic" - copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." -} -fonts { - name: "Josefin Sans" - style: "normal" - weight: 300 - filename: "JosefinSans-Light.ttf" - post_script_name: "JosefinSans-Light" - full_name: "Josefin Sans Light" - copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." -} -fonts { - name: "Josefin Sans" - style: "italic" - weight: 300 - filename: "JosefinSans-LightItalic.ttf" - post_script_name: "JosefinSans-LightItalic" - full_name: "Josefin Sans Light Italic" - copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." -} -fonts { - name: "Josefin Sans" - style: "normal" - weight: 400 - filename: "JosefinSans-Regular.ttf" - post_script_name: "JosefinSans-Regular" - full_name: "Josefin Sans Regular" - copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." -} -fonts { - name: "Josefin Sans" - style: "italic" - weight: 400 - filename: "JosefinSans-Italic.ttf" - post_script_name: "JosefinSans-Italic" - full_name: "Josefin Sans Italic" - copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." -} -fonts { - name: "Josefin Sans" - style: "normal" - weight: 600 - filename: "JosefinSans-SemiBold.ttf" - post_script_name: "JosefinSans-SemiBold" - full_name: "Josefin Sans SemiBold" - copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." -} -fonts { - name: "Josefin Sans" - style: "italic" - weight: 600 - filename: "JosefinSans-SemiBoldItalic.ttf" - post_script_name: "JosefinSans-SemiBoldItalic" - full_name: "Josefin Sans SemiBold Italic" - copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." -} -fonts { - name: "Josefin Sans" - style: "normal" - weight: 700 - filename: "JosefinSans-Bold.ttf" - post_script_name: "JosefinSans-Bold" - full_name: "Josefin Sans Bold" - copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." -} -fonts { - name: "Josefin Sans" - style: "italic" - weight: 700 - filename: "JosefinSans-BoldItalic.ttf" - post_script_name: "JosefinSans-BoldItalic" - full_name: "Josefin Sans Bold Italic" - copyright: "Copyright 2010 The Josefin Sans Project Authors (https://github.com/ThomasJockin/JosefinSansFont-master), with Reserved Font Name \"Josefin Sans\"." -} -subsets: "latin" -subsets: "latin-ext" -subsets: "menu" -subsets: "vietnamese" diff --git a/docs/logo/josefinsans/OFL.txt b/docs/logo/josefinsans/OFL.txt deleted file mode 100644 index 6586a7e3..00000000 --- a/docs/logo/josefinsans/OFL.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright (c) 2010, Santiago Orozco (hi@typemade.mx) - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/logo/setup_tools_logo_colour.svg b/docs/logo/setup_tools_logo_colour.svg deleted file mode 100644 index 7eae8fc3..00000000 --- a/docs/logo/setup_tools_logo_colour.svg +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/docs/logo/setup_tools_logo_colour_1000px.png b/docs/logo/setup_tools_logo_colour_1000px.png deleted file mode 100644 index c25a23b9..00000000 Binary files a/docs/logo/setup_tools_logo_colour_1000px.png and /dev/null differ diff --git a/docs/logo/setup_tools_logo_colour_banner_1line.svg b/docs/logo/setup_tools_logo_colour_banner_1line.svg deleted file mode 100644 index d6dc9aac..00000000 --- a/docs/logo/setup_tools_logo_colour_banner_1line.svg +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png b/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png deleted file mode 100644 index d1604289..00000000 Binary files a/docs/logo/setup_tools_logo_colour_banner_1line_1000px.png and /dev/null differ diff --git a/docs/logo/setup_tools_logo_colour_banner_2lines.svg b/docs/logo/setup_tools_logo_colour_banner_2lines.svg deleted file mode 100644 index 991577fb..00000000 --- a/docs/logo/setup_tools_logo_colour_banner_2lines.svg +++ /dev/null @@ -1,224 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png b/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png deleted file mode 100644 index 89370a9d..00000000 Binary files a/docs/logo/setup_tools_logo_colour_banner_2lines_1000px.png and /dev/null differ diff --git a/docs/logo/setup_tools_logo_symbol_colour.svg b/docs/logo/setup_tools_logo_symbol_colour.svg deleted file mode 100644 index 2936cbb5..00000000 --- a/docs/logo/setup_tools_logo_symbol_colour.svg +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - diff --git a/docs/logo/setup_tools_logo_symbol_colour_1000px.png b/docs/logo/setup_tools_logo_symbol_colour_1000px.png deleted file mode 100644 index e0c36fc1..00000000 Binary files a/docs/logo/setup_tools_logo_symbol_colour_1000px.png and /dev/null differ diff --git a/docs/logo/setup_tools_logotype_1line.svg b/docs/logo/setup_tools_logotype_1line.svg deleted file mode 100644 index dbb9c1de..00000000 --- a/docs/logo/setup_tools_logotype_1line.svg +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - -- cgit v1.2.1 From 1b22ebc1cea9da7f377a260821e6cb1781df527c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 24 Jul 2020 21:24:06 -0400 Subject: Revert "Add banner to main docs page" This reverts commit 0f491dc2b4141a17475eec12b02d8f237d7f7918. --- docs/index.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/index.txt b/docs/index.txt index 228f97c8..fc111a99 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -1,6 +1,3 @@ -.. image:: https://raw.githubusercontent.com/pypa/setuptools/master/docs/logo/banner%201%20line%20color.svg - - Documentation ============= -- cgit v1.2.1 From dcc71f773576c19a3658735879893515b056ece5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 26 Jul 2020 10:35:02 -0400 Subject: Rename _distutils_importer to _distutils_hack, as it supplies more than just an importer. --- _distutils_hack/__init__.py | 45 ++++++++++++++++++++++++++++++++++ _distutils_hack/install.py | 5 ++++ _distutils_hack/override.py | 54 +++++++++++++++++++++++++++++++++++++++++ _distutils_importer/__init__.py | 45 ---------------------------------- _distutils_importer/install.py | 5 ---- _distutils_importer/override.py | 54 ----------------------------------------- conftest.py | 2 +- setup.py | 2 +- setuptools/__init__.py | 2 +- setuptools/sandbox.py | 8 +++--- 10 files changed, 111 insertions(+), 111 deletions(-) create mode 100644 _distutils_hack/__init__.py create mode 100644 _distutils_hack/install.py create mode 100644 _distutils_hack/override.py delete mode 100644 _distutils_importer/__init__.py delete mode 100644 _distutils_importer/install.py delete mode 100644 _distutils_importer/override.py diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py new file mode 100644 index 00000000..3ad70100 --- /dev/null +++ b/_distutils_hack/__init__.py @@ -0,0 +1,45 @@ +import sys +import os + + +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') + return which == 'local' + + +class DistutilsMetaFinder: + def find_spec(self, fullname, path, target=None): + if path is not None or fullname != "distutils": + return None + + return self.get_distutils_spec() + + def get_distutils_spec(self): + import importlib.util + + class DistutilsLoader(importlib.util.abc.Loader): + + def create_module(self, spec): + return importlib.import_module('._distutils', 'setuptools') + + def exec_module(self, module): + pass + + return importlib.util.spec_from_loader('distutils', DistutilsLoader()) + + +DISTUTILS_FINDER = DistutilsMetaFinder() + + +def add_shim(): + sys.meta_path.insert(0, DISTUTILS_FINDER) + + +def remove_shim(): + try: + sys.path.remove(DISTUTILS_FINDER) + except ValueError: + pass diff --git a/_distutils_hack/install.py b/_distutils_hack/install.py new file mode 100644 index 00000000..73f13b29 --- /dev/null +++ b/_distutils_hack/install.py @@ -0,0 +1,5 @@ +from . import enabled, add_shim + + +if enabled(): + add_shim() diff --git a/_distutils_hack/override.py b/_distutils_hack/override.py new file mode 100644 index 00000000..523139bb --- /dev/null +++ b/_distutils_hack/override.py @@ -0,0 +1,54 @@ +""" +Ensure that the local copy of distutils is preferred over stdlib. + +See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 +for more motivation. +""" + +import sys +import re +import importlib +import warnings + +from . import enabled + + +is_pypy = '__pypy__' in sys.builtin_module_names + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + warnings.warn( + "Distutils was imported before Setuptools. This usage is discouraged " + "and may exhibit undesirable behaviors or errors. Please use " + "Setuptools' objects directly or at least import Setuptools first.") + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + warnings.warn("Setuptools is replacing distutils.") + mods = [name for name in sys.modules if re.match(r'distutils\b', name)] + for name in mods: + del sys.modules[name] + + +def ensure_local_distutils(): + clear_distutils() + distutils = importlib.import_module('setuptools._distutils') + distutils.__name__ = 'distutils' + sys.modules['distutils'] = distutils + + # sanity check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + + +warn_distutils_present() +if enabled(): + ensure_local_distutils() diff --git a/_distutils_importer/__init__.py b/_distutils_importer/__init__.py deleted file mode 100644 index 3ad70100..00000000 --- a/_distutils_importer/__init__.py +++ /dev/null @@ -1,45 +0,0 @@ -import sys -import os - - -def enabled(): - """ - Allow selection of distutils by environment variable. - """ - which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') - return which == 'local' - - -class DistutilsMetaFinder: - def find_spec(self, fullname, path, target=None): - if path is not None or fullname != "distutils": - return None - - return self.get_distutils_spec() - - def get_distutils_spec(self): - import importlib.util - - class DistutilsLoader(importlib.util.abc.Loader): - - def create_module(self, spec): - return importlib.import_module('._distutils', 'setuptools') - - def exec_module(self, module): - pass - - return importlib.util.spec_from_loader('distutils', DistutilsLoader()) - - -DISTUTILS_FINDER = DistutilsMetaFinder() - - -def add_shim(): - sys.meta_path.insert(0, DISTUTILS_FINDER) - - -def remove_shim(): - try: - sys.path.remove(DISTUTILS_FINDER) - except ValueError: - pass diff --git a/_distutils_importer/install.py b/_distutils_importer/install.py deleted file mode 100644 index 73f13b29..00000000 --- a/_distutils_importer/install.py +++ /dev/null @@ -1,5 +0,0 @@ -from . import enabled, add_shim - - -if enabled(): - add_shim() diff --git a/_distutils_importer/override.py b/_distutils_importer/override.py deleted file mode 100644 index 523139bb..00000000 --- a/_distutils_importer/override.py +++ /dev/null @@ -1,54 +0,0 @@ -""" -Ensure that the local copy of distutils is preferred over stdlib. - -See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 -for more motivation. -""" - -import sys -import re -import importlib -import warnings - -from . import enabled - - -is_pypy = '__pypy__' in sys.builtin_module_names - - -def warn_distutils_present(): - if 'distutils' not in sys.modules: - return - if is_pypy and sys.version_info < (3, 7): - # PyPy for 3.6 unconditionally imports distutils, so bypass the warning - # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 - return - warnings.warn( - "Distutils was imported before Setuptools. This usage is discouraged " - "and may exhibit undesirable behaviors or errors. Please use " - "Setuptools' objects directly or at least import Setuptools first.") - - -def clear_distutils(): - if 'distutils' not in sys.modules: - return - warnings.warn("Setuptools is replacing distutils.") - mods = [name for name in sys.modules if re.match(r'distutils\b', name)] - for name in mods: - del sys.modules[name] - - -def ensure_local_distutils(): - clear_distutils() - distutils = importlib.import_module('setuptools._distutils') - distutils.__name__ = 'distutils' - sys.modules['distutils'] = distutils - - # sanity check that submodules load as expected - core = importlib.import_module('distutils.core') - assert '_distutils' in core.__file__, core.__file__ - - -warn_distutils_present() -if enabled(): - ensure_local_distutils() diff --git a/conftest.py b/conftest.py index 868bf5be..25537f56 100644 --- a/conftest.py +++ b/conftest.py @@ -15,7 +15,7 @@ collect_ignore = [ 'tests/manual_test.py', 'setuptools/tests/mod_with_constant.py', 'setuptools/_distutils', - '_distutils_importer', + '_distutils_hack', ] diff --git a/setup.py b/setup.py index a6e1abc4..2d8bdf85 100755 --- a/setup.py +++ b/setup.py @@ -95,7 +95,7 @@ class install_with_pth(install): def initialize_options(self): install.initialize_options(self) self.extra_path = ( - 'distutils-precedence', 'import _distutils_importer.install') + 'distutils-precedence', 'import _distutils_hack.install') def finalize_options(self): install.finalize_options(self) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 80b287b4..99094230 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -5,7 +5,7 @@ import functools import os import re -import _distutils_importer.override # noqa: F401 +import _distutils_hack.override # noqa: F401 import distutils.core from distutils.errors import DistutilsOptionError diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 342a713f..24a36080 100644 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -200,7 +200,7 @@ _MODULES_TO_HIDE = { 'distutils', 'pkg_resources', 'Cython', - '_distutils_importer', + '_distutils_hack', } @@ -232,9 +232,9 @@ def hide_setuptools(): necessary to avoid issues such as #315 where setuptools upgrading itself would fail to find a function declared in the metadata. """ - _distutils_importer = sys.modules.get('_distutils_importer', None) - if _distutils_importer is not None: - _distutils_importer.remove_shim() + _distutils_hack = sys.modules.get('_distutils_hack', None) + if _distutils_hack is not None: + _distutils_hack.remove_shim() modules = filter(_needs_hiding, sys.modules) _clear_modules(modules) -- cgit v1.2.1 From 384a51657c94271d29c437415080f25f7df4103b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 26 Jul 2020 10:38:13 -0400 Subject: Extract pth name and contents into class variables. --- setup.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 2d8bdf85..edb2bbcf 100755 --- a/setup.py +++ b/setup.py @@ -92,18 +92,21 @@ class install_with_pth(install): `distutils` than the standard library. """ + _pth_name = 'distutils-precedence' + _pth_contents = 'import _distutils_hack.install' + def initialize_options(self): install.initialize_options(self) - self.extra_path = ( - 'distutils-precedence', 'import _distutils_hack.install') + self.extra_path = self._pth_name, self._pth_contents def finalize_options(self): install.finalize_options(self) + # undo secondary effect of `extra_path` adding to `install_lib` install_suffix = os.path.relpath(self.install_lib, self.install_libbase) - if install_suffix == self.extra_path[1]: + if install_suffix == self._pth_contents: self.install_lib = self.install_libbase -- cgit v1.2.1 From f3b177e9c2b77104ddebaec7b581e2aee73a1184 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 26 Jul 2020 10:42:15 -0400 Subject: Update docstring to use imperative voice, provide a bit more context, and advise against copying of the behavior. --- setup.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index edb2bbcf..45ddb149 100755 --- a/setup.py +++ b/setup.py @@ -86,10 +86,13 @@ class install_with_pth(install): """ Custom install command to install a .pth file for distutils patching. - This is necessary because there's no standard way to install a `.pth` file - alongside your package (and there probably shouldn't be one), but we need - to do this in order to give precedence higher precedence to our version of - `distutils` than the standard library. + This hack is necessary because there's no standard way to install behavior + on startup (and it's debatable if there should be one). This hack (ab)uses + the `extra_path` behavior in Setuptools to install a `.pth` file with + implicit behavior on startup to give higher precedence to the local version + of `distutils` over the version from the standard library. + + Please do not replicate this behavior. """ _pth_name = 'distutils-precedence' -- cgit v1.2.1 From 268ef5f553f29977f708c256ee398c9e29cb4da7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 26 Jul 2020 10:43:08 -0400 Subject: Remove hanging indent --- setup.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 45ddb149..6290a746 100755 --- a/setup.py +++ b/setup.py @@ -106,8 +106,10 @@ class install_with_pth(install): install.finalize_options(self) # undo secondary effect of `extra_path` adding to `install_lib` - install_suffix = os.path.relpath(self.install_lib, - self.install_libbase) + install_suffix = os.path.relpath( + self.install_lib, + self.install_libbase, + ) if install_suffix == self._pth_contents: self.install_lib = self.install_libbase -- cgit v1.2.1 From 5642e413fb6c75434f109be943bdb09ea9e7ade2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 26 Jul 2020 10:44:51 -0400 Subject: Extract function for restoring install lib to encapsulate behavior. --- setup.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 6290a746..6f6601fe 100755 --- a/setup.py +++ b/setup.py @@ -104,14 +104,15 @@ class install_with_pth(install): def finalize_options(self): install.finalize_options(self) + self._restore_install_lib() - # undo secondary effect of `extra_path` adding to `install_lib` - install_suffix = os.path.relpath( - self.install_lib, - self.install_libbase, - ) + def _restore_install_lib(self): + """ + Undo secondary effect of `extra_path` adding to `install_lib` + """ + suffix = os.path.relpath(self.install_lib, self.install_libbase) - if install_suffix == self._pth_contents: + if suffix == self._pth_contents: self.install_lib = self.install_libbase -- cgit v1.2.1 From 83a85a71cd779b1b1b3a44e21cc198264650da46 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 26 Jul 2020 10:51:40 -0400 Subject: Restore early opt-in/opt-out for pth behavior. --- setup.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 6f6601fe..2e44225e 100755 --- a/setup.py +++ b/setup.py @@ -5,6 +5,7 @@ Distutils setup file, used to install or test 'setuptools' import os import sys +import textwrap import setuptools from setuptools.command.install import install @@ -96,7 +97,11 @@ class install_with_pth(install): """ _pth_name = 'distutils-precedence' - _pth_contents = 'import _distutils_hack.install' + _pth_contents = textwrap.dedent(""" + import os + enabled = os.environ.get('SETUPTOOLS_USE_DISTUTILS') == 'local' + enabled and __import__('_distutils_hack.install') + """).lstrip().replace('\n', '; ') def initialize_options(self): install.initialize_options(self) -- cgit v1.2.1 From 1251a231ad75fa649da700645690eb3c0a348f08 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 26 Jul 2020 10:53:31 -0400 Subject: Replace install behavior on import with direct invocation (now that 'enabled' logic is duplicated in pth file). --- _distutils_hack/install.py | 5 ----- setup.py | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 _distutils_hack/install.py diff --git a/_distutils_hack/install.py b/_distutils_hack/install.py deleted file mode 100644 index 73f13b29..00000000 --- a/_distutils_hack/install.py +++ /dev/null @@ -1,5 +0,0 @@ -from . import enabled, add_shim - - -if enabled(): - add_shim() diff --git a/setup.py b/setup.py index 2e44225e..37953051 100755 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ class install_with_pth(install): _pth_contents = textwrap.dedent(""" import os enabled = os.environ.get('SETUPTOOLS_USE_DISTUTILS') == 'local' - enabled and __import__('_distutils_hack.install') + enabled and __import__('_distutils_hack').add_shim() """).lstrip().replace('\n', '; ') def initialize_options(self): -- cgit v1.2.1 From 6c6d69e0213c4012caa36f0087f1fe54bac62c89 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 26 Jul 2020 10:59:11 -0400 Subject: Move all but a small shim in override into _distutils_hack --- _distutils_hack/__init__.py | 51 +++++++++++++++++++++++++++++++++++++++++ _distutils_hack/override.py | 55 +-------------------------------------------- 2 files changed, 52 insertions(+), 54 deletions(-) diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 3ad70100..a8638344 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -1,5 +1,11 @@ import sys import os +import re +import importlib +import warnings + + +is_pypy = '__pypy__' in sys.builtin_module_names def enabled(): @@ -10,6 +16,51 @@ def enabled(): return which == 'local' +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + if is_pypy and sys.version_info < (3, 7): + # PyPy for 3.6 unconditionally imports distutils, so bypass the warning + # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 + return + warnings.warn( + "Distutils was imported before Setuptools. This usage is discouraged " + "and may exhibit undesirable behaviors or errors. Please use " + "Setuptools' objects directly or at least import Setuptools first.") + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + warnings.warn("Setuptools is replacing distutils.") + mods = [name for name in sys.modules if re.match(r'distutils\b', name)] + for name in mods: + del sys.modules[name] + + +def ensure_local_distutils(): + clear_distutils() + distutils = importlib.import_module('setuptools._distutils') + distutils.__name__ = 'distutils' + sys.modules['distutils'] = distutils + + # sanity check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + + +def do_override(): + """ + Ensure that the local copy of distutils is preferred over stdlib. + + See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 + for more motivation. + """ + warn_distutils_present() + if enabled(): + ensure_local_distutils() + + class DistutilsMetaFinder: def find_spec(self, fullname, path, target=None): if path is not None or fullname != "distutils": diff --git a/_distutils_hack/override.py b/_distutils_hack/override.py index 523139bb..2cc433a4 100644 --- a/_distutils_hack/override.py +++ b/_distutils_hack/override.py @@ -1,54 +1 @@ -""" -Ensure that the local copy of distutils is preferred over stdlib. - -See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 -for more motivation. -""" - -import sys -import re -import importlib -import warnings - -from . import enabled - - -is_pypy = '__pypy__' in sys.builtin_module_names - - -def warn_distutils_present(): - if 'distutils' not in sys.modules: - return - if is_pypy and sys.version_info < (3, 7): - # PyPy for 3.6 unconditionally imports distutils, so bypass the warning - # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 - return - warnings.warn( - "Distutils was imported before Setuptools. This usage is discouraged " - "and may exhibit undesirable behaviors or errors. Please use " - "Setuptools' objects directly or at least import Setuptools first.") - - -def clear_distutils(): - if 'distutils' not in sys.modules: - return - warnings.warn("Setuptools is replacing distutils.") - mods = [name for name in sys.modules if re.match(r'distutils\b', name)] - for name in mods: - del sys.modules[name] - - -def ensure_local_distutils(): - clear_distutils() - distutils = importlib.import_module('setuptools._distutils') - distutils.__name__ = 'distutils' - sys.modules['distutils'] = distutils - - # sanity check that submodules load as expected - core = importlib.import_module('distutils.core') - assert '_distutils' in core.__file__, core.__file__ - - -warn_distutils_present() -if enabled(): - ensure_local_distutils() +__import__('_distutils_hack').do_override() -- cgit v1.2.1 From 95b359c84ae05cb2fd37da194b67ee83c4eb9595 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 29 Jul 2020 22:08:30 +0300 Subject: The PyPA has adopted the PSF code of conduct For details, see: * https://discuss.python.org/t/implementing-pep-609-pypa-governance/4745 --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9cbf7b86..824a033f 100644 --- a/README.rst +++ b/README.rst @@ -54,4 +54,4 @@ Code of Conduct Everyone interacting in the setuptools project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the -`PyPA Code of Conduct `_. +`PSF Code of Conduct `_. -- cgit v1.2.1 From 6af765910c71ead64b0093ac51db7592d1ad238c Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Sat, 1 Aug 2020 12:20:35 +1000 Subject: Fixed typo --- setuptools/monkey.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 3c77f8cf..e5f1377b 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -138,7 +138,7 @@ def patch_for_msvc_specialized_compiler(): msvc = import_module('setuptools.msvc') if platform.system() != 'Windows': - # Compilers only availables on Microsoft Windows + # Compilers only available on Microsoft Windows return def patch_params(mod_name, func_name): -- cgit v1.2.1 From 1215561b96389403cbbd55889e54b67db873ddcb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 2 Aug 2020 09:32:13 -0400 Subject: Suppress ImportError for winreg as the module is only available on some platforms. Allows unit testing of module on non-Windows platforms. --- distutils/_msvccompiler.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/distutils/_msvccompiler.py b/distutils/_msvccompiler.py index 0e98692e..ef0f0b56 100644 --- a/distutils/_msvccompiler.py +++ b/distutils/_msvccompiler.py @@ -15,7 +15,9 @@ for older versions in distutils.msvc9compiler and distutils.msvccompiler. import os import subprocess -import winreg +import contextlib +with contextlib.suppress(ImportError): + import winreg from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError -- cgit v1.2.1 From 7f233974b0e3dc3692c820a2c8c3439c4d15fc70 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 2 Aug 2020 09:32:51 -0400 Subject: Add a unit test for testing spawn. Ref pypa/distutils#5. --- distutils/tests/test_msvccompiler.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/distutils/tests/test_msvccompiler.py b/distutils/tests/test_msvccompiler.py index b518d6a7..f4bb5162 100644 --- a/distutils/tests/test_msvccompiler.py +++ b/distutils/tests/test_msvccompiler.py @@ -2,6 +2,7 @@ import sys import unittest import os +import threading from distutils.errors import DistutilsPlatformError from distutils.tests import support @@ -74,6 +75,28 @@ class msvccompilerTestCase(support.TempdirManager, else: raise unittest.SkipTest("VS 2015 is not installed") + +class TestSpawn(unittest.TestCase): + def test_concurrent_safe(self): + """ + Concurrent calls to spawn should have consistent results. + """ + import distutils._msvccompiler as _msvccompiler + compiler = _msvccompiler.MSVCCompiler() + compiler._paths = "expected" + inner_cmd = 'import os; assert os.environ["PATH"] == "expected"' + command = ['python', '-c', inner_cmd] + + threads = [ + threading.Thread(target=compiler.spawn, args=[command]) + for n in range(100) + ] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + + def test_suite(): return unittest.makeSuite(msvccompilerTestCase) -- cgit v1.2.1 From 39b30e15365756ae685e02b5af38799a677858af Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 2 Aug 2020 09:35:12 -0400 Subject: In TestSpawn.test_concurrent_safe, use CheckThread to ensure that the spawn call does not simply fail to execute. Ref pypa/setuptools#2257. --- distutils/tests/test_msvccompiler.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/distutils/tests/test_msvccompiler.py b/distutils/tests/test_msvccompiler.py index f4bb5162..88d912b1 100644 --- a/distutils/tests/test_msvccompiler.py +++ b/distutils/tests/test_msvccompiler.py @@ -76,6 +76,19 @@ class msvccompilerTestCase(support.TempdirManager, raise unittest.SkipTest("VS 2015 is not installed") +class CheckThread(threading.Thread): + exc_info = None + + def run(self): + try: + super().run() + except Exception: + self.exc_info = sys.exc_info() + + def __bool__(self): + return not self.exc_info + + class TestSpawn(unittest.TestCase): def test_concurrent_safe(self): """ @@ -88,13 +101,14 @@ class TestSpawn(unittest.TestCase): command = ['python', '-c', inner_cmd] threads = [ - threading.Thread(target=compiler.spawn, args=[command]) + CheckThread(target=compiler.spawn, args=[command]) for n in range(100) ] for thread in threads: thread.start() for thread in threads: thread.join() + assert all(threads) def test_suite(): -- cgit v1.2.1 From 0bb6c6bc47823764649430fca34fc6475c0a42d7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 2 Aug 2020 09:39:57 -0400 Subject: In CCompiler, allow keyword arguments to be passed to spawn calls. Ref pypa/setuptools#2257 and pypa/distutils#5. --- distutils/ccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/distutils/ccompiler.py b/distutils/ccompiler.py index b5ef143e..57bb94e8 100644 --- a/distutils/ccompiler.py +++ b/distutils/ccompiler.py @@ -906,8 +906,8 @@ int main (int argc, char **argv) { def execute(self, func, args, msg=None, level=1): execute(func, args, msg, self.dry_run) - def spawn(self, cmd): - spawn(cmd, dry_run=self.dry_run) + def spawn(self, cmd, **kwargs): + spawn(cmd, dry_run=self.dry_run, **kwargs) def move_file(self, src, dst): return move_file(src, dst, dry_run=self.dry_run) -- cgit v1.2.1 From 616e129944d87e578fe02146f07f72603a4c0124 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 2 Aug 2020 09:41:08 -0400 Subject: In _msvccompiler.MSVCCompiler.spawn, use correct capitalization for PATH environment variable. Fixes failing test and fixes pypa/setuptools#2257. --- distutils/_msvccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distutils/_msvccompiler.py b/distutils/_msvccompiler.py index ef0f0b56..2d56ee0a 100644 --- a/distutils/_msvccompiler.py +++ b/distutils/_msvccompiler.py @@ -503,7 +503,7 @@ class MSVCCompiler(CCompiler) : log.debug("skipping %s (up-to-date)", output_filename) def spawn(self, cmd): - env = dict(os.environ, path=self._paths) + env = dict(os.environ, PATH=self._paths) return super().spawn(cmd, env=env) # -- Miscellaneous methods ----------------------------------------- -- cgit v1.2.1 From a83d96eca1ea6d85a366351457dd25beb1663ad4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 2 Aug 2020 09:55:24 -0400 Subject: Add changelog. Ref #2257. --- changelog.d/2257.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2257.misc.rst diff --git a/changelog.d/2257.misc.rst b/changelog.d/2257.misc.rst new file mode 100644 index 00000000..509b372e --- /dev/null +++ b/changelog.d/2257.misc.rst @@ -0,0 +1 @@ +Fixed two flaws in distutils._msvccompiler.MSVCCompiler.spawn. -- cgit v1.2.1 From d3e754feba46b69bf948009d895303a74d2a41bd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 2 Aug 2020 09:57:20 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.2.0=20=E2=86=92=2049.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2257.misc.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2257.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 9b3e4085..b534af1b 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.2.0 +current_version = 49.2.1 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 82e6ef66..519330eb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v49.2.1 +------- + +* #2257: Fixed two flaws in distutils._msvccompiler.MSVCCompiler.spawn. + + v49.2.0 ------- diff --git a/changelog.d/2257.misc.rst b/changelog.d/2257.misc.rst deleted file mode 100644 index 509b372e..00000000 --- a/changelog.d/2257.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed two flaws in distutils._msvccompiler.MSVCCompiler.spawn. diff --git a/setup.cfg b/setup.cfg index fa0e5656..485714a3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.2.0 +version = 49.2.1 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 44ca8aa51c39bea0c68444389d6ec215bcf98d02 Mon Sep 17 00:00:00 2001 From: Athos Ribeiro Date: Tue, 4 Aug 2020 10:31:11 +0200 Subject: Improve safe_version documentation --- changelog.d/2300.doc.rst | 1 + docs/pkg_resources.txt | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 changelog.d/2300.doc.rst diff --git a/changelog.d/2300.doc.rst b/changelog.d/2300.doc.rst new file mode 100644 index 00000000..3b06cc5f --- /dev/null +++ b/changelog.d/2300.doc.rst @@ -0,0 +1 @@ +Improve the ``safe_version`` function documentation diff --git a/docs/pkg_resources.txt b/docs/pkg_resources.txt index f2e554f4..7d0d8da9 100644 --- a/docs/pkg_resources.txt +++ b/docs/pkg_resources.txt @@ -1596,12 +1596,12 @@ Parsing Utilities See ``to_filename()``. ``safe_version(version)`` - This will return the normalized form of any PEP 440 version, if the version - string is not PEP 440 compatible than it is similar to ``safe_name()`` - except that spaces in the input become dots, and dots are allowed to exist - in the output. As with ``safe_name()``, if you are generating a filename - from this you should replace any "-" characters in the output with - underscores. + This will return the normalized form of any PEP 440 version. If the version + string is not PEP 440 compatible, this function behaves similar to + ``safe_name()`` except that spaces in the input become dots, and dots are + allowed to exist in the output. As with ``safe_name()``, if you are + generating a filename from this you should replace any "-" characters in + the output with underscores. ``safe_extra(extra)`` Return a "safe" form of an extra's name, suitable for use in a requirement -- cgit v1.2.1 From 8db806d30d7591828528ac937e8f3b334e957ed3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 26 Jul 2020 20:19:45 -0400 Subject: remove shim should by symmetric to add_shim --- _distutils_hack/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index a8638344..a10af2cc 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -91,6 +91,6 @@ def add_shim(): def remove_shim(): try: - sys.path.remove(DISTUTILS_FINDER) + sys.meta_path.remove(DISTUTILS_FINDER) except ValueError: pass -- cgit v1.2.1 From ebfe95fcbb12c22f58b045c6f10bd899a21a53d8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 5 Aug 2020 21:49:08 -0400 Subject: Update changelog. --- changelog.d/2259.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2259.change.rst diff --git a/changelog.d/2259.change.rst b/changelog.d/2259.change.rst new file mode 100644 index 00000000..43701ec2 --- /dev/null +++ b/changelog.d/2259.change.rst @@ -0,0 +1 @@ +Setuptools now provides a .pth file (except for editable installs of setuptools) to the target environment to ensure that when enabled, the setuptools-provided distutils is preferred before setuptools has been imported (and even if setuptools is never imported). Honors the SETUPTOOLS_USE_DISTUTILS environment variable. -- cgit v1.2.1 From fe79e6ca8ef48edb3ee2ea42b3021aa7de963a49 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 6 Aug 2020 08:51:10 +0100 Subject: Don't install setup_requires when run as a PEP-517 backend. Under PEP-517, installing build dependencies is up to the frontend. Closes gh-2303 --- setuptools/build_meta.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py index 46266814..49b4cc28 100644 --- a/setuptools/build_meta.py +++ b/setuptools/build_meta.py @@ -75,6 +75,22 @@ class Distribution(setuptools.dist.Distribution): distutils.core.Distribution = orig +@contextlib.contextmanager +def no_install_setup_requires(): + """Temporarily disable installing setup_requires + + Under PEP 517, the backend reports build dependencies to the frontend, + and the frontend is responsible for ensuring they're installed. + So setuptools (acting as a backend) should not try to install them. + """ + orig = setuptools._install_setup_requires + setuptools._install_setup_requires = lambda attrs: None + try: + yield + finally: + setuptools._install_setup_requires = orig + + def _to_str(s): """ Convert a filename to a string (on Python 2, explicitly @@ -139,7 +155,8 @@ class _BuildMetaBackend(object): with _open_setup_script(__file__) as f: code = f.read().replace(r'\r\n', r'\n') - exec(compile(code, __file__, 'exec'), locals()) + with no_install_setup_requires(): + exec(compile(code, __file__, 'exec'), locals()) def get_requires_for_build_wheel(self, config_settings=None): config_settings = self._fix_config(config_settings) -- cgit v1.2.1 From 21c81324f9e37aee92301f64b95947accfafbaa4 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 6 Aug 2020 10:10:13 +0100 Subject: get_requires_for_build* hooks rely on 'installing' setup_requires --- setuptools/build_meta.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py index 49b4cc28..37132187 100644 --- a/setuptools/build_meta.py +++ b/setuptools/build_meta.py @@ -155,8 +155,7 @@ class _BuildMetaBackend(object): with _open_setup_script(__file__) as f: code = f.read().replace(r'\r\n', r'\n') - with no_install_setup_requires(): - exec(compile(code, __file__, 'exec'), locals()) + exec(compile(code, __file__, 'exec'), locals()) def get_requires_for_build_wheel(self, config_settings=None): config_settings = self._fix_config(config_settings) @@ -171,7 +170,8 @@ class _BuildMetaBackend(object): config_settings=None): sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', _to_str(metadata_directory)] - self.run_setup() + with no_install_setup_requires(): + self.run_setup() dist_info_directory = metadata_directory while True: @@ -211,7 +211,8 @@ class _BuildMetaBackend(object): sys.argv = (sys.argv[:1] + setup_command + ['--dist-dir', tmp_dist_dir] + config_settings["--global-option"]) - self.run_setup() + with no_install_setup_requires(): + self.run_setup() result_basename = _file_with_extension( tmp_dist_dir, result_extension) -- cgit v1.2.1 From 8a4b06dbdde7fb3249329b5311f5b5707510fa88 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 6 Aug 2020 10:13:21 +0100 Subject: Add test for PEP 517 backends not installing setup_requires --- setuptools/tests/test_build_meta.py | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/setuptools/tests/test_build_meta.py b/setuptools/tests/test_build_meta.py index 8fcf3055..f1860b69 100644 --- a/setuptools/tests/test_build_meta.py +++ b/setuptools/tests/test_build_meta.py @@ -380,7 +380,7 @@ class TestBuildMetaBackend: setup( name="qux", version="0.0.0", - py_modules=["hello.py"], + py_modules=["hello"], setup_requires={setup_literal}, ) """).format(setup_literal=setup_literal), @@ -407,6 +407,36 @@ class TestBuildMetaBackend: assert expected == sorted(actual) + def test_dont_install_setup_requires(self, tmpdir_cwd): + files = { + 'setup.py': DALS(""" + from setuptools import setup + + setup( + name="qux", + version="0.0.0", + py_modules=["hello"], + setup_requires=["does-not-exist >99"], + ) + """), + 'hello.py': DALS(""" + def run(): + print('hello') + """), + } + + build_files(files) + + build_backend = self.get_build_backend() + + dist_dir = os.path.abspath('pip-dist-info') + os.makedirs(dist_dir) + + # does-not-exist can't be satisfied, so if it attempts to install + # setup_requires, it will fail. + build_backend.prepare_metadata_for_build_wheel(dist_dir) + + _sys_argv_0_passthrough = { 'setup.py': DALS(""" import os -- cgit v1.2.1 From a78f42ef07c9b9db778087675ad0d84ab9548425 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 6 Aug 2020 10:16:22 +0100 Subject: Add news fragment --- changelog.d/2306.change.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog.d/2306.change.rst diff --git a/changelog.d/2306.change.rst b/changelog.d/2306.change.rst new file mode 100644 index 00000000..1046eae3 --- /dev/null +++ b/changelog.d/2306.change.rst @@ -0,0 +1,3 @@ +When running as a PEP 517 backend, setuptools does not try to install +``setup_requires`` itself. They are reported as build requirements for the +frontend to install. -- cgit v1.2.1 From d2883ac4f1d94c16a1ab279e7b1065558b67da4b Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 6 Aug 2020 10:23:51 +0100 Subject: Flake 99 --- setuptools/tests/test_build_meta.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setuptools/tests/test_build_meta.py b/setuptools/tests/test_build_meta.py index f1860b69..fdb4b950 100644 --- a/setuptools/tests/test_build_meta.py +++ b/setuptools/tests/test_build_meta.py @@ -436,7 +436,6 @@ class TestBuildMetaBackend: # setup_requires, it will fail. build_backend.prepare_metadata_for_build_wheel(dist_dir) - _sys_argv_0_passthrough = { 'setup.py': DALS(""" import os -- cgit v1.2.1 From 485703c854f59d226c2f231e7c5f4f91263a4463 Mon Sep 17 00:00:00 2001 From: raimon Date: Sat, 8 Aug 2020 20:33:27 +0900 Subject: Provide consistency in the description of setup.cfg --- docs/setuptools.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 7e0914b7..d60c87a0 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -1988,11 +1988,11 @@ boilerplate code in some cases. include_package_data = True packages = find: scripts = - bin/first.py - bin/second.py + bin/first.py + bin/second.py install_requires = - requests - importlib; python_version == "2.6" + requests + importlib; python_version == "2.6" [options.package_data] * = *.txt, *.rst @@ -2028,8 +2028,8 @@ Metadata and options are set in the config sections of the same name. [metadata] keywords = - one - two + one + two * In some cases, complex values can be provided in dedicated subsections for clarity. -- cgit v1.2.1 From 7c3a3817923de67956656ea058e80669b77bed1f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Aug 2020 11:44:12 -0400 Subject: Add tests capturing expected distutils behavior. Ref #417. --- setuptools/tests/test_distutils_adoption.py | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 setuptools/tests/test_distutils_adoption.py diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py new file mode 100644 index 00000000..f4c7072d --- /dev/null +++ b/setuptools/tests/test_distutils_adoption.py @@ -0,0 +1,37 @@ +import os +import pytest + + +@pytest.fixture +def env(virtualenv): + virtualenv.run(['pip', 'uninstall', '-y', 'setuptools']) + virtualenv.run(['pip', 'install', os.getcwd()]) + return virtualenv + + +def find_distutils(env, imports='distutils'): + py_cmd = 'import {imports}; print(distutils.__file__)'.format(**locals()) + cmd = ['python', '-c', py_cmd] + return env.run(cmd, capture=True, text=True) + + +def test_distutils_stdlib(env): + """ + Ensure stdlib distutils is used when appropriate. + """ + assert '.env' not in find_distutils(env).split(os.sep) + + +def test_distutils_local_with_setuptools(env): + """ + Ensure local distutils is used when appropriate. + """ + env.env.update(SETUPTOOLS_USE_DISTUTILS='local') + loc = find_distutils(env, imports='setuptools, distutils') + assert '.env' in loc.split(os.sep) + + +@pytest.mark.xfail(reason="#2259") +def test_distutils_local(env): + env.env.update(SETUPTOOLS_USE_DISTUTILS='local') + assert '.env' in find_distutils(env).split(os.sep) -- cgit v1.2.1 From 6281bd0917882b930d5923d76cc72ff4ee5a7695 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Aug 2020 12:15:13 -0400 Subject: Support Python 3.5 and 3.6 in the tests. --- setuptools/tests/test_distutils_adoption.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py index f4c7072d..a945a69f 100644 --- a/setuptools/tests/test_distutils_adoption.py +++ b/setuptools/tests/test_distutils_adoption.py @@ -1,7 +1,18 @@ import os +import sys +import functools + import pytest +def popen_text(call): + """ + Augment the Popen call with the parameters to ensure unicode text. + """ + return functools.partial(call, universal_newlines=True) \ + if sys.version_info < (3, 7) else functools.partial(call, text=True) + + @pytest.fixture def env(virtualenv): virtualenv.run(['pip', 'uninstall', '-y', 'setuptools']) @@ -12,7 +23,7 @@ def env(virtualenv): def find_distutils(env, imports='distutils'): py_cmd = 'import {imports}; print(distutils.__file__)'.format(**locals()) cmd = ['python', '-c', py_cmd] - return env.run(cmd, capture=True, text=True) + return popen_text(env.run)(cmd, capture=True) def test_distutils_stdlib(env): -- cgit v1.2.1 From 41deeb76fc88382f27b3d37cce75eda2c54cbc2a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Aug 2020 15:53:41 -0400 Subject: Bypass pytest-virtualenv due to bugs in 'run' command. Instead, use jaraco.envs.VirtualEnv and build a bespoke fixture with a run command. --- setup.cfg | 1 + setuptools/tests/requirements.txt | 1 + setuptools/tests/test_distutils_adoption.py | 48 ++++++++++++++++++----------- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/setup.cfg b/setup.cfg index 485714a3..87fa6bf2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -72,6 +72,7 @@ tests = paver; python_version>="3.6" futures; python_version=="2.7" pip>=19.1 # For proper file:// URLs support. + jaraco.envs docs = # Keep these in sync with docs/requirements.txt diff --git a/setuptools/tests/requirements.txt b/setuptools/tests/requirements.txt index 19bf5aef..d0d07f70 100644 --- a/setuptools/tests/requirements.txt +++ b/setuptools/tests/requirements.txt @@ -10,3 +10,4 @@ pytest-cov>=2.5.1 paver; python_version>="3.6" futures; python_version=="2.7" pip>=19.1 # For proper file:// URLs support. +jaraco.envs diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py index a945a69f..476b0a9e 100644 --- a/setuptools/tests/test_distutils_adoption.py +++ b/setuptools/tests/test_distutils_adoption.py @@ -1,8 +1,27 @@ import os import sys import functools +import subprocess import pytest +import jaraco.envs +import path + + +class VirtualEnv(jaraco.envs.VirtualEnv): + name = '.env' + + def run(self, cmd, *args, **kwargs): + cmd = [self.exe(cmd[0])] + cmd[1:] + return subprocess.check_output(cmd, *args, cwd=self.root, **kwargs) + + +@pytest.fixture +def venv(tmpdir): + env = VirtualEnv() + env.root = path.Path(tmpdir) + env.req = os.getcwd() + return env.create() def popen_text(call): @@ -13,36 +32,29 @@ def popen_text(call): if sys.version_info < (3, 7) else functools.partial(call, text=True) -@pytest.fixture -def env(virtualenv): - virtualenv.run(['pip', 'uninstall', '-y', 'setuptools']) - virtualenv.run(['pip', 'install', os.getcwd()]) - return virtualenv - - -def find_distutils(env, imports='distutils'): +def find_distutils(venv, imports='distutils', **kwargs): py_cmd = 'import {imports}; print(distutils.__file__)'.format(**locals()) cmd = ['python', '-c', py_cmd] - return popen_text(env.run)(cmd, capture=True) + return popen_text(venv.run)(cmd, **kwargs) -def test_distutils_stdlib(env): +def test_distutils_stdlib(venv): """ Ensure stdlib distutils is used when appropriate. """ - assert '.env' not in find_distutils(env).split(os.sep) + assert venv.name not in find_distutils(venv, env=dict()).split(os.sep) -def test_distutils_local_with_setuptools(env): +def test_distutils_local_with_setuptools(venv): """ Ensure local distutils is used when appropriate. """ - env.env.update(SETUPTOOLS_USE_DISTUTILS='local') - loc = find_distutils(env, imports='setuptools, distutils') - assert '.env' in loc.split(os.sep) + env = dict(SETUPTOOLS_USE_DISTUTILS='local') + loc = find_distutils(venv, imports='setuptools, distutils', env=env) + assert venv.name in loc.split(os.sep) @pytest.mark.xfail(reason="#2259") -def test_distutils_local(env): - env.env.update(SETUPTOOLS_USE_DISTUTILS='local') - assert '.env' in find_distutils(env).split(os.sep) +def test_distutils_local(venv): + env = dict(SETUPTOOLS_USE_DISTUTILS='local') + assert venv.name in find_distutils(venv, env=env).split(os.sep) -- cgit v1.2.1 From 47ae38fd6f233e6423404bfebfd24d3b98fb897c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Aug 2020 17:01:41 -0400 Subject: On Windows, SYSTEMROOT must be supplied. --- setuptools/tests/test_distutils_adoption.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py index 476b0a9e..7f28a217 100644 --- a/setuptools/tests/test_distutils_adoption.py +++ b/setuptools/tests/test_distutils_adoption.py @@ -2,6 +2,7 @@ import os import sys import functools import subprocess +import platform import pytest import jaraco.envs @@ -32,10 +33,12 @@ def popen_text(call): if sys.version_info < (3, 7) else functools.partial(call, text=True) -def find_distutils(venv, imports='distutils', **kwargs): +def find_distutils(venv, imports='distutils', env=None, **kwargs): py_cmd = 'import {imports}; print(distutils.__file__)'.format(**locals()) cmd = ['python', '-c', py_cmd] - return popen_text(venv.run)(cmd, **kwargs) + if platform.system() == 'Windows': + env['SYSTEMROOT'] = os.environ['SYSTEMROOT'] + return popen_text(venv.run)(cmd, env=env, **kwargs) def test_distutils_stdlib(venv): -- cgit v1.2.1 From 48a17a56ecfc77fb60780e3cfa75390f6bb10b15 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Aug 2020 12:22:05 -0400 Subject: As discovered in bpo-41509, relpath can strip spaces, so match that expectation. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 37953051..5d98c029 100755 --- a/setup.py +++ b/setup.py @@ -117,7 +117,7 @@ class install_with_pth(install): """ suffix = os.path.relpath(self.install_lib, self.install_libbase) - if suffix == self._pth_contents: + if suffix.strip() == self._pth_contents.strip(): self.install_lib = self.install_libbase -- cgit v1.2.1 From 6b70fb201d6a81448de6ca6f71d7091b9a26096c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Aug 2020 13:38:56 -0400 Subject: Restore location of 'enabled' --- _distutils_hack/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index a10af2cc..71fa7ce1 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -8,14 +8,6 @@ import warnings is_pypy = '__pypy__' in sys.builtin_module_names -def enabled(): - """ - Allow selection of distutils by environment variable. - """ - which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') - return which == 'local' - - def warn_distutils_present(): if 'distutils' not in sys.modules: return @@ -38,6 +30,14 @@ def clear_distutils(): del sys.modules[name] +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') + return which == 'local' + + def ensure_local_distutils(): clear_distutils() distutils = importlib.import_module('setuptools._distutils') -- cgit v1.2.1 From 521987da809e63ee51a63aa45dbe372d40deb8f7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Aug 2020 19:50:24 -0400 Subject: Remove expected fail. --- setuptools/tests/test_distutils_adoption.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py index 7f28a217..bb8e34d5 100644 --- a/setuptools/tests/test_distutils_adoption.py +++ b/setuptools/tests/test_distutils_adoption.py @@ -57,7 +57,10 @@ def test_distutils_local_with_setuptools(venv): assert venv.name in loc.split(os.sep) -@pytest.mark.xfail(reason="#2259") def test_distutils_local(venv): + """ + Even without importing, the setuptools-local copy of distutils is + preferred. + """ env = dict(SETUPTOOLS_USE_DISTUTILS='local') assert venv.name in find_distutils(venv, env=env).split(os.sep) -- cgit v1.2.1 From 7cf009a7e39270e1e1d13d913e0c352fb00534c0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Aug 2020 20:58:57 -0400 Subject: Expect test to fail on PyPy due to implicit import during startup. --- setuptools/tests/test_distutils_adoption.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py index bb8e34d5..daccc473 100644 --- a/setuptools/tests/test_distutils_adoption.py +++ b/setuptools/tests/test_distutils_adoption.py @@ -9,6 +9,9 @@ import jaraco.envs import path +IS_PYPY = '__pypy__' in sys.builtin_module_names + + class VirtualEnv(jaraco.envs.VirtualEnv): name = '.env' @@ -57,6 +60,7 @@ def test_distutils_local_with_setuptools(venv): assert venv.name in loc.split(os.sep) +@pytest.mark.xfail('IS_PYPY', reason='pypy imports distutils on startup') def test_distutils_local(venv): """ Even without importing, the setuptools-local copy of distutils is -- cgit v1.2.1 From 9d7b246c0f40fabb25741a023849bf14919e408d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 9 Aug 2020 14:50:22 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.2.1=20=E2=86=92=2049.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2259.change.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2259.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index b534af1b..99e5def8 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.2.1 +current_version = 49.3.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 519330eb..da0eada3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v49.3.0 +------- + +* #2259: Setuptools now provides a .pth file (except for editable installs of setuptools) to the target environment to ensure that when enabled, the setuptools-provided distutils is preferred before setuptools has been imported (and even if setuptools is never imported). Honors the SETUPTOOLS_USE_DISTUTILS environment variable. + + v49.2.1 ------- diff --git a/changelog.d/2259.change.rst b/changelog.d/2259.change.rst deleted file mode 100644 index 43701ec2..00000000 --- a/changelog.d/2259.change.rst +++ /dev/null @@ -1 +0,0 @@ -Setuptools now provides a .pth file (except for editable installs of setuptools) to the target environment to ensure that when enabled, the setuptools-provided distutils is preferred before setuptools has been imported (and even if setuptools is never imported). Honors the SETUPTOOLS_USE_DISTUTILS environment variable. diff --git a/setup.cfg b/setup.cfg index 87fa6bf2..fd3b11e6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.2.1 +version = 49.3.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 33c9d86af4dc1df04cf1b38a0102fe7e121173ec Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Mon, 10 Aug 2020 21:10:46 +1000 Subject: Change load_module to exec_module --- setuptools/command/bdist_egg.py | 7 ++++--- setuptools/command/build_ext.py | 10 ++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 7af3165c..4be15457 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -55,11 +55,12 @@ def write_stub(resource, pyfile): _stub_template = textwrap.dedent(""" def __bootstrap__(): global __bootstrap__, __loader__, __file__ - import sys, pkg_resources - from importlib.machinery import ExtensionFileLoader + import sys, pkg_resources, importlib.util __file__ = pkg_resources.resource_filename(__name__, %r) __loader__ = None; del __bootstrap__, __loader__ - ExtensionFileLoader(__name__,__file__).load_module() + spec = importlib.util.spec_from_file_location(__name__,__file__) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) __bootstrap__() """).lstrip() with open(pyfile, 'w') as f: diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 0eb29adc..89a0e328 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -254,8 +254,8 @@ class build_ext(_build_ext): '\n'.join([ "def __bootstrap__():", " global __bootstrap__, __file__, __loader__", - " import sys, os, pkg_resources" + if_dl(", dl"), - " from importlib.machinery import ExtensionFileLoader", + " import sys, os, pkg_resources, importlib.util" + + if_dl(", dl"), " __file__ = pkg_resources.resource_filename" "(__name__,%r)" % os.path.basename(ext._file_name), @@ -267,8 +267,10 @@ class build_ext(_build_ext): " try:", " os.chdir(os.path.dirname(__file__))", if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"), - " ExtensionFileLoader(__name__,", - " __file__).load_module()", + " spec = importlib.util.spec_from_file_location(", + " __name__, __file__)", + " mod = importlib.util.module_from_spec(spec)", + " spec.loader.exec_module(mod)", " finally:", if_dl(" sys.setdlopenflags(old_flags)"), " os.chdir(old_dir)", -- cgit v1.2.1 From 1410a8e6fda5fb8cd78f915e1ffd68f6c7f35af3 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 10 Aug 2020 09:18:46 -0400 Subject: Fix issue with distutils warning --- _distutils_hack/__init__.py | 11 +++++++---- changelog.d/2316.change.rst | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 changelog.d/2316.change.rst diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 71fa7ce1..1e7b294b 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -16,9 +16,12 @@ def warn_distutils_present(): # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 return warnings.warn( - "Distutils was imported before Setuptools. This usage is discouraged " - "and may exhibit undesirable behaviors or errors. Please use " - "Setuptools' objects directly or at least import Setuptools first.") + "Distutils was imported before Setuptools, but importing Setuptools " + "also replaces the `distutils` module in `sys.modules`. This may lead " + "to undesirable behaviors or errors. To avoid these issues, avoid " + "using distutils directly, ensure that setuptools is installed in the " + "traditional way (e.g. not an editable install), and/or make sure that " + "setuptools is always imported before distutils.") def clear_distutils(): @@ -56,8 +59,8 @@ def do_override(): See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 for more motivation. """ - warn_distutils_present() if enabled(): + warn_distutils_present() ensure_local_distutils() diff --git a/changelog.d/2316.change.rst b/changelog.d/2316.change.rst new file mode 100644 index 00000000..7d506b09 --- /dev/null +++ b/changelog.d/2316.change.rst @@ -0,0 +1 @@ +Removed warning when ``distutils`` is imported before ``setuptools`` when ``distutils`` replacement is not enabled. -- cgit v1.2.1 From 0b111ad040887a39e4821ebe5198d4246af0253c Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 10 Aug 2020 11:02:31 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.3.0=20=E2=86=92=2049.3.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2316.change.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2316.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 99e5def8..bb1e621f 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.3.0 +current_version = 49.3.1 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index da0eada3..df0230e7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +vv49.3.1 +-------- + +* #2316: Removed warning when ``distutils`` is imported before ``setuptools`` when ``distutils`` replacement is not enabled. + + v49.3.0 ------- diff --git a/changelog.d/2316.change.rst b/changelog.d/2316.change.rst deleted file mode 100644 index 7d506b09..00000000 --- a/changelog.d/2316.change.rst +++ /dev/null @@ -1 +0,0 @@ -Removed warning when ``distutils`` is imported before ``setuptools`` when ``distutils`` replacement is not enabled. diff --git a/setup.cfg b/setup.cfg index fd3b11e6..52440198 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.3.0 +version = 49.3.1 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 5ad59e64b35b9444a42a75f9d454802f0916e209 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 10 Aug 2020 11:45:22 -0400 Subject: Remove superfluous 'v' in CHANGES.rst This was an artifact of the fact that I did not use `tox -e finalize` to do the latest release in order to make a patch release. --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index df0230e7..b7c0a018 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,4 @@ -vv49.3.1 +v49.3.1 -------- * #2316: Removed warning when ``distutils`` is imported before ``setuptools`` when ``distutils`` replacement is not enabled. -- cgit v1.2.1 From 3af54b4b0e31c3f56bbb229cdba58ce8a70fa38b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 12 Aug 2020 16:22:34 -0400 Subject: Update changelog. --- changelog.d/2297.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2297.misc.rst diff --git a/changelog.d/2297.misc.rst b/changelog.d/2297.misc.rst new file mode 100644 index 00000000..40340324 --- /dev/null +++ b/changelog.d/2297.misc.rst @@ -0,0 +1 @@ +Once again, in stubs prefer exec_module to the deprecated load_module. -- cgit v1.2.1 From ff4ba978e5c32c44311d9d9817ddee9e1c6c972b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 12 Aug 2020 17:01:10 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.3.1=20=E2=86=92=2049.3.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 7 +++++++ changelog.d/2297.misc.rst | 1 - changelog.d/2300.doc.rst | 1 - setup.cfg | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 changelog.d/2297.misc.rst delete mode 100644 changelog.d/2300.doc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index bb1e621f..5e0d7fd7 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.3.1 +current_version = 49.3.2 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index b7c0a018..2add741a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v49.3.2 +------- + +* #2300: Improve the ``safe_version`` function documentation +* #2297: Once again, in stubs prefer exec_module to the deprecated load_module. + + v49.3.1 -------- diff --git a/changelog.d/2297.misc.rst b/changelog.d/2297.misc.rst deleted file mode 100644 index 40340324..00000000 --- a/changelog.d/2297.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Once again, in stubs prefer exec_module to the deprecated load_module. diff --git a/changelog.d/2300.doc.rst b/changelog.d/2300.doc.rst deleted file mode 100644 index 3b06cc5f..00000000 --- a/changelog.d/2300.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Improve the ``safe_version`` function documentation diff --git a/setup.cfg b/setup.cfg index 52440198..5d4a084c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.3.1 +version = 49.3.2 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From fac13f0d9950b7c06de650bf4ea226cb2789afc3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 12 Aug 2020 17:01:55 -0400 Subject: Clean up RST. --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2add741a..0d2c96c3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,7 +6,7 @@ v49.3.2 v49.3.1 --------- +------- * #2316: Removed warning when ``distutils`` is imported before ``setuptools`` when ``distutils`` replacement is not enabled. -- cgit v1.2.1 From 1b27726f6b5922658a5968aa0b7a92fb76bc0ade Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 12 Aug 2020 19:57:03 -0400 Subject: Update packaging to 20.4. Closes #2310. --- pkg_resources/_vendor/packaging/__about__.py | 4 +- pkg_resources/_vendor/packaging/_compat.py | 9 +- pkg_resources/_vendor/packaging/_structures.py | 26 +- pkg_resources/_vendor/packaging/_typing.py | 48 ++ pkg_resources/_vendor/packaging/markers.py | 54 ++- pkg_resources/_vendor/packaging/py.typed | 0 pkg_resources/_vendor/packaging/requirements.py | 9 +- pkg_resources/_vendor/packaging/specifiers.py | 190 ++++++-- pkg_resources/_vendor/packaging/tags.py | 569 +++++++++++++++++++----- pkg_resources/_vendor/packaging/utils.py | 18 +- pkg_resources/_vendor/packaging/version.py | 151 ++++++- pkg_resources/_vendor/vendored.txt | 2 +- setuptools/_vendor/packaging/__about__.py | 4 +- setuptools/_vendor/packaging/_compat.py | 9 +- setuptools/_vendor/packaging/_structures.py | 26 +- setuptools/_vendor/packaging/_typing.py | 48 ++ setuptools/_vendor/packaging/markers.py | 54 ++- setuptools/_vendor/packaging/py.typed | 0 setuptools/_vendor/packaging/requirements.py | 9 +- setuptools/_vendor/packaging/specifiers.py | 190 ++++++-- setuptools/_vendor/packaging/tags.py | 569 +++++++++++++++++++----- setuptools/_vendor/packaging/utils.py | 18 +- setuptools/_vendor/packaging/version.py | 151 ++++++- setuptools/_vendor/vendored.txt | 2 +- 24 files changed, 1776 insertions(+), 384 deletions(-) create mode 100644 pkg_resources/_vendor/packaging/_typing.py create mode 100644 pkg_resources/_vendor/packaging/py.typed create mode 100644 setuptools/_vendor/packaging/_typing.py create mode 100644 setuptools/_vendor/packaging/py.typed diff --git a/pkg_resources/_vendor/packaging/__about__.py b/pkg_resources/_vendor/packaging/__about__.py index dc95138d..4d998578 100644 --- a/pkg_resources/_vendor/packaging/__about__.py +++ b/pkg_resources/_vendor/packaging/__about__.py @@ -18,10 +18,10 @@ __title__ = "packaging" __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "19.2" +__version__ = "20.4" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" -__license__ = "BSD or Apache License, Version 2.0" +__license__ = "BSD-2-Clause or Apache-2.0" __copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/pkg_resources/_vendor/packaging/_compat.py b/pkg_resources/_vendor/packaging/_compat.py index 25da473c..e54bd4ed 100644 --- a/pkg_resources/_vendor/packaging/_compat.py +++ b/pkg_resources/_vendor/packaging/_compat.py @@ -5,6 +5,11 @@ from __future__ import absolute_import, division, print_function import sys +from ._typing import TYPE_CHECKING + +if TYPE_CHECKING: # pragma: no cover + from typing import Any, Dict, Tuple, Type + PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 @@ -18,14 +23,16 @@ else: def with_metaclass(meta, *bases): + # type: (Type[Any], Tuple[Type[Any], ...]) -> Any """ Create a base class with a metaclass. """ # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. - class metaclass(meta): + class metaclass(meta): # type: ignore def __new__(cls, name, this_bases, d): + # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any return meta(name, bases, d) return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/pkg_resources/_vendor/packaging/_structures.py b/pkg_resources/_vendor/packaging/_structures.py index 68dcca63..800d5c55 100644 --- a/pkg_resources/_vendor/packaging/_structures.py +++ b/pkg_resources/_vendor/packaging/_structures.py @@ -4,65 +4,83 @@ from __future__ import absolute_import, division, print_function -class Infinity(object): +class InfinityType(object): def __repr__(self): + # type: () -> str return "Infinity" def __hash__(self): + # type: () -> int return hash(repr(self)) def __lt__(self, other): + # type: (object) -> bool return False def __le__(self, other): + # type: (object) -> bool return False def __eq__(self, other): + # type: (object) -> bool return isinstance(other, self.__class__) def __ne__(self, other): + # type: (object) -> bool return not isinstance(other, self.__class__) def __gt__(self, other): + # type: (object) -> bool return True def __ge__(self, other): + # type: (object) -> bool return True def __neg__(self): + # type: (object) -> NegativeInfinityType return NegativeInfinity -Infinity = Infinity() +Infinity = InfinityType() -class NegativeInfinity(object): +class NegativeInfinityType(object): def __repr__(self): + # type: () -> str return "-Infinity" def __hash__(self): + # type: () -> int return hash(repr(self)) def __lt__(self, other): + # type: (object) -> bool return True def __le__(self, other): + # type: (object) -> bool return True def __eq__(self, other): + # type: (object) -> bool return isinstance(other, self.__class__) def __ne__(self, other): + # type: (object) -> bool return not isinstance(other, self.__class__) def __gt__(self, other): + # type: (object) -> bool return False def __ge__(self, other): + # type: (object) -> bool return False def __neg__(self): + # type: (object) -> InfinityType return Infinity -NegativeInfinity = NegativeInfinity() +NegativeInfinity = NegativeInfinityType() diff --git a/pkg_resources/_vendor/packaging/_typing.py b/pkg_resources/_vendor/packaging/_typing.py new file mode 100644 index 00000000..77a8b918 --- /dev/null +++ b/pkg_resources/_vendor/packaging/_typing.py @@ -0,0 +1,48 @@ +"""For neatly implementing static typing in packaging. + +`mypy` - the static type analysis tool we use - uses the `typing` module, which +provides core functionality fundamental to mypy's functioning. + +Generally, `typing` would be imported at runtime and used in that fashion - +it acts as a no-op at runtime and does not have any run-time overhead by +design. + +As it turns out, `typing` is not vendorable - it uses separate sources for +Python 2/Python 3. Thus, this codebase can not expect it to be present. +To work around this, mypy allows the typing import to be behind a False-y +optional to prevent it from running at runtime and type-comments can be used +to remove the need for the types to be accessible directly during runtime. + +This module provides the False-y guard in a nicely named fashion so that a +curious maintainer can reach here to read this. + +In packaging, all static-typing related imports should be guarded as follows: + + from packaging._typing import TYPE_CHECKING + + if TYPE_CHECKING: + from typing import ... + +Ref: https://github.com/python/mypy/issues/3216 +""" + +__all__ = ["TYPE_CHECKING", "cast"] + +# The TYPE_CHECKING constant defined by the typing module is False at runtime +# but True while type checking. +if False: # pragma: no cover + from typing import TYPE_CHECKING +else: + TYPE_CHECKING = False + +# typing's cast syntax requires calling typing.cast at runtime, but we don't +# want to import typing at runtime. Here, we inform the type checkers that +# we're importing `typing.cast` as `cast` and re-implement typing.cast's +# runtime behavior in a block that is ignored by type checkers. +if TYPE_CHECKING: # pragma: no cover + # not executed at runtime + from typing import cast +else: + # executed at runtime + def cast(type_, value): # noqa + return value diff --git a/pkg_resources/_vendor/packaging/markers.py b/pkg_resources/_vendor/packaging/markers.py index 733123fb..fd1559c1 100644 --- a/pkg_resources/_vendor/packaging/markers.py +++ b/pkg_resources/_vendor/packaging/markers.py @@ -13,8 +13,14 @@ from pkg_resources.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedStr from pkg_resources.extern.pyparsing import Literal as L # noqa from ._compat import string_types +from ._typing import TYPE_CHECKING from .specifiers import Specifier, InvalidSpecifier +if TYPE_CHECKING: # pragma: no cover + from typing import Any, Callable, Dict, List, Optional, Tuple, Union + + Operator = Callable[[str, str], bool] + __all__ = [ "InvalidMarker", @@ -46,30 +52,37 @@ class UndefinedEnvironmentName(ValueError): class Node(object): def __init__(self, value): + # type: (Any) -> None self.value = value def __str__(self): + # type: () -> str return str(self.value) def __repr__(self): + # type: () -> str return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) def serialize(self): + # type: () -> str raise NotImplementedError class Variable(Node): def serialize(self): + # type: () -> str return str(self) class Value(Node): def serialize(self): + # type: () -> str return '"{0}"'.format(self) class Op(Node): def serialize(self): + # type: () -> str return str(self) @@ -85,13 +98,13 @@ VARIABLE = ( | L("python_version") | L("sys_platform") | L("os_name") - | L("os.name") + | L("os.name") # PEP-345 | L("sys.platform") # PEP-345 | L("platform.version") # PEP-345 | L("platform.machine") # PEP-345 | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # PEP-345 - | L("extra") # undocumented setuptools legacy + | L("python_implementation") # undocumented setuptools legacy + | L("extra") # PEP-508 ) ALIASES = { "os.name": "os_name", @@ -131,6 +144,7 @@ MARKER = stringStart + MARKER_EXPR + stringEnd def _coerce_parse_result(results): + # type: (Union[ParseResults, List[Any]]) -> List[Any] if isinstance(results, ParseResults): return [_coerce_parse_result(i) for i in results] else: @@ -138,6 +152,8 @@ def _coerce_parse_result(results): def _format_marker(marker, first=True): + # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str + assert isinstance(marker, (list, tuple, string_types)) # Sometimes we have a structure like [[...]] which is a single item list @@ -172,10 +188,11 @@ _operators = { "!=": operator.ne, ">=": operator.ge, ">": operator.gt, -} +} # type: Dict[str, Operator] def _eval_op(lhs, op, rhs): + # type: (str, Op, str) -> bool try: spec = Specifier("".join([op.serialize(), rhs])) except InvalidSpecifier: @@ -183,7 +200,7 @@ def _eval_op(lhs, op, rhs): else: return spec.contains(lhs) - oper = _operators.get(op.serialize()) + oper = _operators.get(op.serialize()) # type: Optional[Operator] if oper is None: raise UndefinedComparison( "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) @@ -192,13 +209,18 @@ def _eval_op(lhs, op, rhs): return oper(lhs, rhs) -_undefined = object() +class Undefined(object): + pass + + +_undefined = Undefined() def _get_env(environment, name): - value = environment.get(name, _undefined) + # type: (Dict[str, str], str) -> str + value = environment.get(name, _undefined) # type: Union[str, Undefined] - if value is _undefined: + if isinstance(value, Undefined): raise UndefinedEnvironmentName( "{0!r} does not exist in evaluation environment.".format(name) ) @@ -207,7 +229,8 @@ def _get_env(environment, name): def _evaluate_markers(markers, environment): - groups = [[]] + # type: (List[Any], Dict[str, str]) -> bool + groups = [[]] # type: List[List[bool]] for marker in markers: assert isinstance(marker, (list, tuple, string_types)) @@ -234,6 +257,7 @@ def _evaluate_markers(markers, environment): def format_full_version(info): + # type: (sys._version_info) -> str version = "{0.major}.{0.minor}.{0.micro}".format(info) kind = info.releaselevel if kind != "final": @@ -242,9 +266,13 @@ def format_full_version(info): def default_environment(): + # type: () -> Dict[str, str] if hasattr(sys, "implementation"): - iver = format_full_version(sys.implementation.version) - implementation_name = sys.implementation.name + # Ignoring the `sys.implementation` reference for type checking due to + # mypy not liking that the attribute doesn't exist in Python 2.7 when + # run with the `--py27` flag. + iver = format_full_version(sys.implementation.version) # type: ignore + implementation_name = sys.implementation.name # type: ignore else: iver = "0" implementation_name = "" @@ -266,6 +294,7 @@ def default_environment(): class Marker(object): def __init__(self, marker): + # type: (str) -> None try: self._markers = _coerce_parse_result(MARKER.parseString(marker)) except ParseException as e: @@ -275,12 +304,15 @@ class Marker(object): raise InvalidMarker(err_str) def __str__(self): + # type: () -> str return _format_marker(self._markers) def __repr__(self): + # type: () -> str return "".format(str(self)) def evaluate(self, environment=None): + # type: (Optional[Dict[str, str]]) -> bool """Evaluate a marker. Return the boolean from evaluating the given marker against the diff --git a/pkg_resources/_vendor/packaging/py.typed b/pkg_resources/_vendor/packaging/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/pkg_resources/_vendor/packaging/requirements.py b/pkg_resources/_vendor/packaging/requirements.py index c3424dcb..8282a632 100644 --- a/pkg_resources/_vendor/packaging/requirements.py +++ b/pkg_resources/_vendor/packaging/requirements.py @@ -11,9 +11,13 @@ from pkg_resources.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Co from pkg_resources.extern.pyparsing import Literal as L # noqa from pkg_resources.extern.six.moves.urllib import parse as urlparse +from ._typing import TYPE_CHECKING from .markers import MARKER_EXPR, Marker from .specifiers import LegacySpecifier, Specifier, SpecifierSet +if TYPE_CHECKING: # pragma: no cover + from typing import List + class InvalidRequirement(ValueError): """ @@ -89,6 +93,7 @@ class Requirement(object): # TODO: Can we normalize the name and extra name? def __init__(self, requirement_string): + # type: (str) -> None try: req = REQUIREMENT.parseString(requirement_string) except ParseException as e: @@ -116,7 +121,8 @@ class Requirement(object): self.marker = req.marker if req.marker else None def __str__(self): - parts = [self.name] + # type: () -> str + parts = [self.name] # type: List[str] if self.extras: parts.append("[{0}]".format(",".join(sorted(self.extras)))) @@ -135,4 +141,5 @@ class Requirement(object): return "".join(parts) def __repr__(self): + # type: () -> str return "".format(str(self)) diff --git a/pkg_resources/_vendor/packaging/specifiers.py b/pkg_resources/_vendor/packaging/specifiers.py index 743576a0..fe09bb1d 100644 --- a/pkg_resources/_vendor/packaging/specifiers.py +++ b/pkg_resources/_vendor/packaging/specifiers.py @@ -9,8 +9,27 @@ import itertools import re from ._compat import string_types, with_metaclass +from ._typing import TYPE_CHECKING +from .utils import canonicalize_version from .version import Version, LegacyVersion, parse +if TYPE_CHECKING: # pragma: no cover + from typing import ( + List, + Dict, + Union, + Iterable, + Iterator, + Optional, + Callable, + Tuple, + FrozenSet, + ) + + ParsedVersion = Union[Version, LegacyVersion] + UnparsedVersion = Union[Version, LegacyVersion, str] + CallableOperator = Callable[[ParsedVersion, str], bool] + class InvalidSpecifier(ValueError): """ @@ -18,9 +37,10 @@ class InvalidSpecifier(ValueError): """ -class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): +class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore @abc.abstractmethod def __str__(self): + # type: () -> str """ Returns the str representation of this Specifier like object. This should be representative of the Specifier itself. @@ -28,12 +48,14 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @abc.abstractmethod def __hash__(self): + # type: () -> int """ Returns a hash value for this Specifier like object. """ @abc.abstractmethod def __eq__(self, other): + # type: (object) -> bool """ Returns a boolean representing whether or not the two Specifier like objects are equal. @@ -41,6 +63,7 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @abc.abstractmethod def __ne__(self, other): + # type: (object) -> bool """ Returns a boolean representing whether or not the two Specifier like objects are not equal. @@ -48,6 +71,7 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @abc.abstractproperty def prereleases(self): + # type: () -> Optional[bool] """ Returns whether or not pre-releases as a whole are allowed by this specifier. @@ -55,6 +79,7 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @prereleases.setter def prereleases(self, value): + # type: (bool) -> None """ Sets whether or not pre-releases as a whole are allowed by this specifier. @@ -62,12 +87,14 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @abc.abstractmethod def contains(self, item, prereleases=None): + # type: (str, Optional[bool]) -> bool """ Determines if the given item is contained within this specifier. """ @abc.abstractmethod def filter(self, iterable, prereleases=None): + # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] """ Takes an iterable of items and filters them so that only items which are contained within this specifier are allowed in it. @@ -76,19 +103,24 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): class _IndividualSpecifier(BaseSpecifier): - _operators = {} + _operators = {} # type: Dict[str, str] def __init__(self, spec="", prereleases=None): + # type: (str, Optional[bool]) -> None match = self._regex.search(spec) if not match: raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - self._spec = (match.group("operator").strip(), match.group("version").strip()) + self._spec = ( + match.group("operator").strip(), + match.group("version").strip(), + ) # type: Tuple[str, str] # Store whether or not this Specifier should accept prereleases self._prereleases = prereleases def __repr__(self): + # type: () -> str pre = ( ", prereleases={0!r}".format(self.prereleases) if self._prereleases is not None @@ -98,26 +130,35 @@ class _IndividualSpecifier(BaseSpecifier): return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) def __str__(self): + # type: () -> str return "{0}{1}".format(*self._spec) + @property + def _canonical_spec(self): + # type: () -> Tuple[str, Union[Version, str]] + return self._spec[0], canonicalize_version(self._spec[1]) + def __hash__(self): - return hash(self._spec) + # type: () -> int + return hash(self._canonical_spec) def __eq__(self, other): + # type: (object) -> bool if isinstance(other, string_types): try: - other = self.__class__(other) + other = self.__class__(str(other)) except InvalidSpecifier: return NotImplemented elif not isinstance(other, self.__class__): return NotImplemented - return self._spec == other._spec + return self._canonical_spec == other._canonical_spec def __ne__(self, other): + # type: (object) -> bool if isinstance(other, string_types): try: - other = self.__class__(other) + other = self.__class__(str(other)) except InvalidSpecifier: return NotImplemented elif not isinstance(other, self.__class__): @@ -126,52 +167,67 @@ class _IndividualSpecifier(BaseSpecifier): return self._spec != other._spec def _get_operator(self, op): - return getattr(self, "_compare_{0}".format(self._operators[op])) + # type: (str) -> CallableOperator + operator_callable = getattr( + self, "_compare_{0}".format(self._operators[op]) + ) # type: CallableOperator + return operator_callable def _coerce_version(self, version): + # type: (UnparsedVersion) -> ParsedVersion if not isinstance(version, (LegacyVersion, Version)): version = parse(version) return version @property def operator(self): + # type: () -> str return self._spec[0] @property def version(self): + # type: () -> str return self._spec[1] @property def prereleases(self): + # type: () -> Optional[bool] return self._prereleases @prereleases.setter def prereleases(self, value): + # type: (bool) -> None self._prereleases = value def __contains__(self, item): + # type: (str) -> bool return self.contains(item) def contains(self, item, prereleases=None): + # type: (UnparsedVersion, Optional[bool]) -> bool + # Determine if prereleases are to be allowed or not. if prereleases is None: prereleases = self.prereleases # Normalize item to a Version or LegacyVersion, this allows us to have # a shortcut for ``"2.0" in Specifier(">=2") - item = self._coerce_version(item) + normalized_item = self._coerce_version(item) # Determine if we should be supporting prereleases in this specifier # or not, if we do not support prereleases than we can short circuit # logic if this version is a prereleases. - if item.is_prerelease and not prereleases: + if normalized_item.is_prerelease and not prereleases: return False # Actually do the comparison to determine if this item is contained # within this Specifier or not. - return self._get_operator(self.operator)(item, self.version) + operator_callable = self._get_operator(self.operator) # type: CallableOperator + return operator_callable(normalized_item, self.version) def filter(self, iterable, prereleases=None): + # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] + yielded = False found_prereleases = [] @@ -230,32 +286,43 @@ class LegacySpecifier(_IndividualSpecifier): } def _coerce_version(self, version): + # type: (Union[ParsedVersion, str]) -> LegacyVersion if not isinstance(version, LegacyVersion): version = LegacyVersion(str(version)) return version def _compare_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective == self._coerce_version(spec) def _compare_not_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective != self._coerce_version(spec) def _compare_less_than_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective <= self._coerce_version(spec) def _compare_greater_than_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective >= self._coerce_version(spec) def _compare_less_than(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective < self._coerce_version(spec) def _compare_greater_than(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective > self._coerce_version(spec) -def _require_version_compare(fn): +def _require_version_compare( + fn # type: (Callable[[Specifier, ParsedVersion, str], bool]) +): + # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool] @functools.wraps(fn) def wrapped(self, prospective, spec): + # type: (Specifier, ParsedVersion, str) -> bool if not isinstance(prospective, Version): return False return fn(self, prospective, spec) @@ -373,6 +440,8 @@ class Specifier(_IndividualSpecifier): @_require_version_compare def _compare_compatible(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + # Compatible releases have an equivalent combination of >= and ==. That # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to # implement this in terms of the other specifiers instead of @@ -400,56 +469,75 @@ class Specifier(_IndividualSpecifier): @_require_version_compare def _compare_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + # We need special logic to handle prefix matching if spec.endswith(".*"): # In the case of prefix matching we want to ignore local segment. prospective = Version(prospective.public) # Split the spec out by dots, and pretend that there is an implicit # dot in between a release segment and a pre-release segment. - spec = _version_split(spec[:-2]) # Remove the trailing .* + split_spec = _version_split(spec[:-2]) # Remove the trailing .* # Split the prospective version out by dots, and pretend that there # is an implicit dot in between a release segment and a pre-release # segment. - prospective = _version_split(str(prospective)) + split_prospective = _version_split(str(prospective)) # Shorten the prospective version to be the same length as the spec # so that we can determine if the specifier is a prefix of the # prospective version or not. - prospective = prospective[: len(spec)] + shortened_prospective = split_prospective[: len(split_spec)] # Pad out our two sides with zeros so that they both equal the same # length. - spec, prospective = _pad_version(spec, prospective) + padded_spec, padded_prospective = _pad_version( + split_spec, shortened_prospective + ) + + return padded_prospective == padded_spec else: # Convert our spec string into a Version - spec = Version(spec) + spec_version = Version(spec) # If the specifier does not have a local segment, then we want to # act as if the prospective version also does not have a local # segment. - if not spec.local: + if not spec_version.local: prospective = Version(prospective.public) - return prospective == spec + return prospective == spec_version @_require_version_compare def _compare_not_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool return not self._compare_equal(prospective, spec) @_require_version_compare def _compare_less_than_equal(self, prospective, spec): - return prospective <= Version(spec) + # type: (ParsedVersion, str) -> bool + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) @_require_version_compare def _compare_greater_than_equal(self, prospective, spec): - return prospective >= Version(spec) + # type: (ParsedVersion, str) -> bool + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) @_require_version_compare - def _compare_less_than(self, prospective, spec): + def _compare_less_than(self, prospective, spec_str): + # type: (ParsedVersion, str) -> bool + # Convert our spec to a Version instance, since we'll want to work with # it as a version. - spec = Version(spec) + spec = Version(spec_str) # Check to see if the prospective version is less than the spec # version. If it's not we can short circuit and just return False now @@ -471,10 +559,12 @@ class Specifier(_IndividualSpecifier): return True @_require_version_compare - def _compare_greater_than(self, prospective, spec): + def _compare_greater_than(self, prospective, spec_str): + # type: (ParsedVersion, str) -> bool + # Convert our spec to a Version instance, since we'll want to work with # it as a version. - spec = Version(spec) + spec = Version(spec_str) # Check to see if the prospective version is greater than the spec # version. If it's not we can short circuit and just return False now @@ -502,10 +592,13 @@ class Specifier(_IndividualSpecifier): return True def _compare_arbitrary(self, prospective, spec): + # type: (Version, str) -> bool return str(prospective).lower() == str(spec).lower() @property def prereleases(self): + # type: () -> bool + # If there is an explicit prereleases set for this, then we'll just # blindly use that. if self._prereleases is not None: @@ -530,6 +623,7 @@ class Specifier(_IndividualSpecifier): @prereleases.setter def prereleases(self, value): + # type: (bool) -> None self._prereleases = value @@ -537,7 +631,8 @@ _prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") def _version_split(version): - result = [] + # type: (str) -> List[str] + result = [] # type: List[str] for item in version.split("."): match = _prefix_regex.search(item) if match: @@ -548,6 +643,7 @@ def _version_split(version): def _pad_version(left, right): + # type: (List[str], List[str]) -> Tuple[List[str], List[str]] left_split, right_split = [], [] # Get the release segment of our versions @@ -567,14 +663,16 @@ def _pad_version(left, right): class SpecifierSet(BaseSpecifier): def __init__(self, specifiers="", prereleases=None): - # Split on , to break each indidivual specifier into it's own item, and + # type: (str, Optional[bool]) -> None + + # Split on , to break each individual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. - specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] # Parsed each individual specifier, attempting first to make it a # Specifier and falling back to a LegacySpecifier. parsed = set() - for specifier in specifiers: + for specifier in split_specifiers: try: parsed.add(Specifier(specifier)) except InvalidSpecifier: @@ -588,6 +686,7 @@ class SpecifierSet(BaseSpecifier): self._prereleases = prereleases def __repr__(self): + # type: () -> str pre = ( ", prereleases={0!r}".format(self.prereleases) if self._prereleases is not None @@ -597,12 +696,15 @@ class SpecifierSet(BaseSpecifier): return "".format(str(self), pre) def __str__(self): + # type: () -> str return ",".join(sorted(str(s) for s in self._specs)) def __hash__(self): + # type: () -> int return hash(self._specs) def __and__(self, other): + # type: (Union[SpecifierSet, str]) -> SpecifierSet if isinstance(other, string_types): other = SpecifierSet(other) elif not isinstance(other, SpecifierSet): @@ -626,9 +728,8 @@ class SpecifierSet(BaseSpecifier): return specifier def __eq__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): + # type: (object) -> bool + if isinstance(other, (string_types, _IndividualSpecifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented @@ -636,9 +737,8 @@ class SpecifierSet(BaseSpecifier): return self._specs == other._specs def __ne__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): + # type: (object) -> bool + if isinstance(other, (string_types, _IndividualSpecifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented @@ -646,13 +746,17 @@ class SpecifierSet(BaseSpecifier): return self._specs != other._specs def __len__(self): + # type: () -> int return len(self._specs) def __iter__(self): + # type: () -> Iterator[FrozenSet[_IndividualSpecifier]] return iter(self._specs) @property def prereleases(self): + # type: () -> Optional[bool] + # If we have been given an explicit prerelease modifier, then we'll # pass that through here. if self._prereleases is not None: @@ -670,12 +774,16 @@ class SpecifierSet(BaseSpecifier): @prereleases.setter def prereleases(self, value): + # type: (bool) -> None self._prereleases = value def __contains__(self, item): + # type: (Union[ParsedVersion, str]) -> bool return self.contains(item) def contains(self, item, prereleases=None): + # type: (Union[ParsedVersion, str], Optional[bool]) -> bool + # Ensure that our item is a Version or LegacyVersion instance. if not isinstance(item, (LegacyVersion, Version)): item = parse(item) @@ -701,7 +809,13 @@ class SpecifierSet(BaseSpecifier): # will always return True, this is an explicit design decision. return all(s.contains(item, prereleases=prereleases) for s in self._specs) - def filter(self, iterable, prereleases=None): + def filter( + self, + iterable, # type: Iterable[Union[ParsedVersion, str]] + prereleases=None, # type: Optional[bool] + ): + # type: (...) -> Iterable[Union[ParsedVersion, str]] + # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the # SpecifierSet thinks for whether or not we should support prereleases. @@ -719,8 +833,8 @@ class SpecifierSet(BaseSpecifier): # which will filter out any pre-releases, unless there are no final # releases, and which will filter out LegacyVersion in general. else: - filtered = [] - found_prereleases = [] + filtered = [] # type: List[Union[ParsedVersion, str]] + found_prereleases = [] # type: List[Union[ParsedVersion, str]] for item in iterable: # Ensure that we some kind of Version class for this item. diff --git a/pkg_resources/_vendor/packaging/tags.py b/pkg_resources/_vendor/packaging/tags.py index ec9942f0..9064910b 100644 --- a/pkg_resources/_vendor/packaging/tags.py +++ b/pkg_resources/_vendor/packaging/tags.py @@ -13,12 +13,37 @@ except ImportError: # pragma: no cover EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] del imp +import logging +import os import platform import re +import struct import sys import sysconfig import warnings +from ._typing import TYPE_CHECKING, cast + +if TYPE_CHECKING: # pragma: no cover + from typing import ( + Dict, + FrozenSet, + IO, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + ) + + PythonVersion = Sequence[int] + MacVersion = Tuple[int, int] + GlibcVersion = Tuple[int, int] + + +logger = logging.getLogger(__name__) INTERPRETER_SHORT_NAMES = { "python": "py", # Generic. @@ -26,34 +51,48 @@ INTERPRETER_SHORT_NAMES = { "pypy": "pp", "ironpython": "ip", "jython": "jy", -} +} # type: Dict[str, str] _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 class Tag(object): + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ __slots__ = ["_interpreter", "_abi", "_platform"] def __init__(self, interpreter, abi, platform): + # type: (str, str, str) -> None self._interpreter = interpreter.lower() self._abi = abi.lower() self._platform = platform.lower() @property def interpreter(self): + # type: () -> str return self._interpreter @property def abi(self): + # type: () -> str return self._abi @property def platform(self): + # type: () -> str return self._platform def __eq__(self, other): + # type: (object) -> bool + if not isinstance(other, Tag): + return NotImplemented + return ( (self.platform == other.platform) and (self.abi == other.abi) @@ -61,16 +100,26 @@ class Tag(object): ) def __hash__(self): + # type: () -> int return hash((self._interpreter, self._abi, self._platform)) def __str__(self): + # type: () -> str return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) def __repr__(self): + # type: () -> str return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) def parse_tag(tag): + # type: (str) -> FrozenSet[Tag] + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ tags = set() interpreters, abis, platforms = tag.split("-") for interpreter in interpreters.split("."): @@ -80,20 +129,54 @@ def parse_tag(tag): return frozenset(tags) +def _warn_keyword_parameter(func_name, kwargs): + # type: (str, Dict[str, bool]) -> bool + """ + Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only. + """ + if not kwargs: + return False + elif len(kwargs) > 1 or "warn" not in kwargs: + kwargs.pop("warn", None) + arg = next(iter(kwargs.keys())) + raise TypeError( + "{}() got an unexpected keyword argument {!r}".format(func_name, arg) + ) + return kwargs["warn"] + + +def _get_config_var(name, warn=False): + # type: (str, bool) -> Union[int, str, None] + value = sysconfig.get_config_var(name) + if value is None and warn: + logger.debug( + "Config variable '%s' is unset, Python ABI tag may be incorrect", name + ) + return value + + def _normalize_string(string): + # type: (str) -> str return string.replace(".", "_").replace("-", "_") -def _cpython_interpreter(py_version): - # TODO: Is using py_version_nodot for interpreter version critical? - return "cp{major}{minor}".format(major=py_version[0], minor=py_version[1]) +def _abi3_applies(python_version): + # type: (PythonVersion) -> bool + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) -def _cpython_abis(py_version): +def _cpython_abis(py_version, warn=False): + # type: (PythonVersion, bool) -> List[str] + py_version = tuple(py_version) # To allow for version comparison. abis = [] - version = "{}{}".format(*py_version[:2]) + version = _version_nodot(py_version[:2]) debug = pymalloc = ucs4 = "" - with_debug = sysconfig.get_config_var("Py_DEBUG") + with_debug = _get_config_var("Py_DEBUG", warn) has_refcount = hasattr(sys, "gettotalrefcount") # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled # extension modules is the best option. @@ -102,11 +185,11 @@ def _cpython_abis(py_version): if with_debug or (with_debug is None and (has_refcount or has_ext)): debug = "d" if py_version < (3, 8): - with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC") + with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) if with_pymalloc or with_pymalloc is None: pymalloc = "m" if py_version < (3, 3): - unicode_size = sysconfig.get_config_var("Py_UNICODE_SIZE") + unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) if unicode_size == 4 or ( unicode_size is None and sys.maxunicode == 0x10FFFF ): @@ -124,86 +207,148 @@ def _cpython_abis(py_version): return abis -def _cpython_tags(py_version, interpreter, abis, platforms): +def cpython_tags( + python_version=None, # type: Optional[PythonVersion] + abis=None, # type: Optional[Iterable[str]] + platforms=None, # type: Optional[Iterable[str]] + **kwargs # type: bool +): + # type: (...) -> Iterator[Tag] + """ + Yields the tags for a CPython interpreter. + + The tags consist of: + - cp-- + - cp-abi3- + - cp-none- + - cp-abi3- # Older Python versions down to 3.2. + + If python_version only specifies a major version then user-provided ABIs and + the 'none' ABItag will be used. + + If 'abi3' or 'none' are specified in 'abis' then they will be yielded at + their normal position and not at the beginning. + """ + warn = _warn_keyword_parameter("cpython_tags", kwargs) + if not python_version: + python_version = sys.version_info[:2] + + interpreter = "cp{}".format(_version_nodot(python_version[:2])) + + if abis is None: + if len(python_version) > 1: + abis = _cpython_abis(python_version, warn) + else: + abis = [] + abis = list(abis) + # 'abi3' and 'none' are explicitly handled later. + for explicit_abi in ("abi3", "none"): + try: + abis.remove(explicit_abi) + except ValueError: + pass + + platforms = list(platforms or _platform_tags()) for abi in abis: for platform_ in platforms: yield Tag(interpreter, abi, platform_) - for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): - yield tag + if _abi3_applies(python_version): + for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): + yield tag for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): yield tag - # PEP 384 was first implemented in Python 3.2. - for minor_version in range(py_version[1] - 1, 1, -1): - for platform_ in platforms: - interpreter = "cp{major}{minor}".format( - major=py_version[0], minor=minor_version - ) - yield Tag(interpreter, "abi3", platform_) - -def _pypy_interpreter(): - return "pp{py_major}{pypy_major}{pypy_minor}".format( - py_major=sys.version_info[0], - pypy_major=sys.pypy_version_info.major, - pypy_minor=sys.pypy_version_info.minor, - ) + if _abi3_applies(python_version): + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{version}".format( + version=_version_nodot((python_version[0], minor_version)) + ) + yield Tag(interpreter, "abi3", platform_) def _generic_abi(): + # type: () -> Iterator[str] abi = sysconfig.get_config_var("SOABI") if abi: - return _normalize_string(abi) - else: - return "none" + yield _normalize_string(abi) -def _pypy_tags(py_version, interpreter, abi, platforms): - for tag in (Tag(interpreter, abi, platform) for platform in platforms): - yield tag - for tag in (Tag(interpreter, "none", platform) for platform in platforms): - yield tag +def generic_tags( + interpreter=None, # type: Optional[str] + abis=None, # type: Optional[Iterable[str]] + platforms=None, # type: Optional[Iterable[str]] + **kwargs # type: bool +): + # type: (...) -> Iterator[Tag] + """ + Yields the tags for a generic interpreter. + The tags consist of: + - -- -def _generic_tags(interpreter, py_version, abi, platforms): - for tag in (Tag(interpreter, abi, platform) for platform in platforms): - yield tag - if abi != "none": - tags = (Tag(interpreter, "none", platform_) for platform_ in platforms) - for tag in tags: - yield tag + The "none" ABI will be added if it was not explicitly provided. + """ + warn = _warn_keyword_parameter("generic_tags", kwargs) + if not interpreter: + interp_name = interpreter_name() + interp_version = interpreter_version(warn=warn) + interpreter = "".join([interp_name, interp_version]) + if abis is None: + abis = _generic_abi() + platforms = list(platforms or _platform_tags()) + abis = list(abis) + if "none" not in abis: + abis.append("none") + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) def _py_interpreter_range(py_version): + # type: (PythonVersion) -> Iterator[str] """ - Yield Python versions in descending order. + Yields Python versions in descending order. After the latest version, the major-only version will be yielded, and then - all following versions up to 'end'. + all previous versions of that major version. """ - yield "py{major}{minor}".format(major=py_version[0], minor=py_version[1]) + if len(py_version) > 1: + yield "py{version}".format(version=_version_nodot(py_version[:2])) yield "py{major}".format(major=py_version[0]) - for minor in range(py_version[1] - 1, -1, -1): - yield "py{major}{minor}".format(major=py_version[0], minor=minor) + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield "py{version}".format(version=_version_nodot((py_version[0], minor))) -def _independent_tags(interpreter, py_version, platforms): +def compatible_tags( + python_version=None, # type: Optional[PythonVersion] + interpreter=None, # type: Optional[str] + platforms=None, # type: Optional[Iterable[str]] +): + # type: (...) -> Iterator[Tag] """ - Return the sequence of tags that are consistent across implementations. + Yields the sequence of tags that are compatible with a specific version of Python. The tags consist of: - py*-none- - - -none-any + - -none-any # ... if `interpreter` is provided. - py*-none-any """ - for version in _py_interpreter_range(py_version): + if not python_version: + python_version = sys.version_info[:2] + platforms = list(platforms or _platform_tags()) + for version in _py_interpreter_range(python_version): for platform_ in platforms: yield Tag(version, "none", platform_) - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(py_version): + if interpreter: + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(python_version): yield Tag(version, "none", "any") def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): + # type: (str, bool) -> str if not is_32bit: return arch @@ -214,6 +359,7 @@ def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): def _mac_binary_formats(version, cpu_arch): + # type: (MacVersion, str) -> List[str] formats = [cpu_arch] if cpu_arch == "x86_64": if version < (10, 4): @@ -240,32 +386,42 @@ def _mac_binary_formats(version, cpu_arch): return formats -def _mac_platforms(version=None, arch=None): - version_str, _, cpu_arch = platform.mac_ver() +def mac_platforms(version=None, arch=None): + # type: (Optional[MacVersion], Optional[str]) -> Iterator[str] + """ + Yields the platform tags for a macOS system. + + The `version` parameter is a two-item tuple specifying the macOS version to + generate platform tags for. The `arch` parameter is the CPU architecture to + generate platform tags for. Both parameters default to the appropriate value + for the current system. + """ + version_str, _, cpu_arch = platform.mac_ver() # type: ignore if version is None: - version = tuple(map(int, version_str.split(".")[:2])) + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + else: + version = version if arch is None: arch = _mac_arch(cpu_arch) - platforms = [] + else: + arch = arch for minor_version in range(version[1], -1, -1): compat_version = version[0], minor_version binary_formats = _mac_binary_formats(compat_version, arch) for binary_format in binary_formats: - platforms.append( - "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, ) - return platforms # From PEP 513. def _is_manylinux_compatible(name, glibc_version): + # type: (str, GlibcVersion) -> bool # Check for presence of _manylinux module. try: - import _manylinux + import _manylinux # noqa return bool(getattr(_manylinux, name + "_compatible")) except (ImportError, AttributeError): @@ -276,14 +432,50 @@ def _is_manylinux_compatible(name, glibc_version): def _glibc_version_string(): + # type: () -> Optional[str] # Returns glibc version string, or None if not using glibc. - import ctypes + return _glibc_version_string_confstr() or _glibc_version_string_ctypes() + + +def _glibc_version_string_confstr(): + # type: () -> Optional[str] + """ + Primary implementation of glibc_version_string using os.confstr. + """ + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module. + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 + try: + # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". + version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821 + "CS_GNU_LIBC_VERSION" + ) + assert version_string is not None + _, version = version_string.split() # type: Tuple[str, str] + except (AssertionError, AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def _glibc_version_string_ctypes(): + # type: () -> Optional[str] + """ + Fallback implementation of glibc_version_string using ctypes. + """ + try: + import ctypes + except ImportError: + return None # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen # manpage says, "If filename is NULL, then the returned handle is for the # main program". This way we can let the linker do the work to figure out # which libc our process is actually using. - process_namespace = ctypes.CDLL(None) + # + # Note: typeshed is wrong here so we are ignoring this line. + process_namespace = ctypes.CDLL(None) # type: ignore try: gnu_get_libc_version = process_namespace.gnu_get_libc_version except AttributeError: @@ -293,7 +485,7 @@ def _glibc_version_string(): # Call gnu_get_libc_version, which returns a string like "2.5" gnu_get_libc_version.restype = ctypes.c_char_p - version_str = gnu_get_libc_version() + version_str = gnu_get_libc_version() # type: str # py2 / py3 compatibility: if not isinstance(version_str, str): version_str = version_str.decode("ascii") @@ -303,6 +495,7 @@ def _glibc_version_string(): # Separated out from have_compatible_glibc for easier unit testing. def _check_glibc_version(version_str, required_major, minimum_minor): + # type: (str, int, int) -> bool # Parse string and check against requested version. # # We use a regexp instead of str.split because we want to discard any @@ -324,81 +517,235 @@ def _check_glibc_version(version_str, required_major, minimum_minor): def _have_compatible_glibc(required_major, minimum_minor): + # type: (int, int) -> bool version_str = _glibc_version_string() if version_str is None: return False return _check_glibc_version(version_str, required_major, minimum_minor) +# Python does not provide platform information at sufficient granularity to +# identify the architecture of the running executable in some cases, so we +# determine it dynamically by reading the information from the running +# process. This only applies on Linux, which uses the ELF format. +class _ELFFileHeader(object): + # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header + class _InvalidELFFileHeader(ValueError): + """ + An invalid ELF file header was found. + """ + + ELF_MAGIC_NUMBER = 0x7F454C46 + ELFCLASS32 = 1 + ELFCLASS64 = 2 + ELFDATA2LSB = 1 + ELFDATA2MSB = 2 + EM_386 = 3 + EM_S390 = 22 + EM_ARM = 40 + EM_X86_64 = 62 + EF_ARM_ABIMASK = 0xFF000000 + EF_ARM_ABI_VER5 = 0x05000000 + EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + def __init__(self, file): + # type: (IO[bytes]) -> None + def unpack(fmt): + # type: (str) -> int + try: + (result,) = struct.unpack( + fmt, file.read(struct.calcsize(fmt)) + ) # type: (int, ) + except struct.error: + raise _ELFFileHeader._InvalidELFFileHeader() + return result + + self.e_ident_magic = unpack(">I") + if self.e_ident_magic != self.ELF_MAGIC_NUMBER: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_class = unpack("B") + if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_data = unpack("B") + if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_version = unpack("B") + self.e_ident_osabi = unpack("B") + self.e_ident_abiversion = unpack("B") + self.e_ident_pad = file.read(7) + format_h = "H" + format_i = "I" + format_q = "Q" + format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q + self.e_type = unpack(format_h) + self.e_machine = unpack(format_h) + self.e_version = unpack(format_i) + self.e_entry = unpack(format_p) + self.e_phoff = unpack(format_p) + self.e_shoff = unpack(format_p) + self.e_flags = unpack(format_i) + self.e_ehsize = unpack(format_h) + self.e_phentsize = unpack(format_h) + self.e_phnum = unpack(format_h) + self.e_shentsize = unpack(format_h) + self.e_shnum = unpack(format_h) + self.e_shstrndx = unpack(format_h) + + +def _get_elf_header(): + # type: () -> Optional[_ELFFileHeader] + try: + with open(sys.executable, "rb") as f: + elf_header = _ELFFileHeader(f) + except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): + return None + return elf_header + + +def _is_linux_armhf(): + # type: () -> bool + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + elf_header = _get_elf_header() + if elf_header is None: + return False + result = elf_header.e_ident_class == elf_header.ELFCLASS32 + result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB + result &= elf_header.e_machine == elf_header.EM_ARM + result &= ( + elf_header.e_flags & elf_header.EF_ARM_ABIMASK + ) == elf_header.EF_ARM_ABI_VER5 + result &= ( + elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD + ) == elf_header.EF_ARM_ABI_FLOAT_HARD + return result + + +def _is_linux_i686(): + # type: () -> bool + elf_header = _get_elf_header() + if elf_header is None: + return False + result = elf_header.e_ident_class == elf_header.ELFCLASS32 + result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB + result &= elf_header.e_machine == elf_header.EM_386 + return result + + +def _have_compatible_manylinux_abi(arch): + # type: (str) -> bool + if arch == "armv7l": + return _is_linux_armhf() + if arch == "i686": + return _is_linux_i686() + return True + + def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): + # type: (bool) -> Iterator[str] linux = _normalize_string(distutils.util.get_platform()) - if linux == "linux_x86_64" and is_32bit: - linux = "linux_i686" - manylinux_support = ( - ("manylinux2014", (2, 17)), # CentOS 7 w/ glibc 2.17 (PEP 599) - ("manylinux2010", (2, 12)), # CentOS 6 w/ glibc 2.12 (PEP 571) - ("manylinux1", (2, 5)), # CentOS 5 w/ glibc 2.5 (PEP 513) - ) + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv7l" + manylinux_support = [] + _, arch = linux.split("_", 1) + if _have_compatible_manylinux_abi(arch): + if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}: + manylinux_support.append( + ("manylinux2014", (2, 17)) + ) # CentOS 7 w/ glibc 2.17 (PEP 599) + if arch in {"x86_64", "i686"}: + manylinux_support.append( + ("manylinux2010", (2, 12)) + ) # CentOS 6 w/ glibc 2.12 (PEP 571) + manylinux_support.append( + ("manylinux1", (2, 5)) + ) # CentOS 5 w/ glibc 2.5 (PEP 513) manylinux_support_iter = iter(manylinux_support) for name, glibc_version in manylinux_support_iter: if _is_manylinux_compatible(name, glibc_version): - platforms = [linux.replace("linux", name)] + yield linux.replace("linux", name) break - else: - platforms = [] # Support for a later manylinux implies support for an earlier version. - platforms += [linux.replace("linux", name) for name, _ in manylinux_support_iter] - platforms.append(linux) - return platforms + for name, _ in manylinux_support_iter: + yield linux.replace("linux", name) + yield linux def _generic_platforms(): - platform = _normalize_string(distutils.util.get_platform()) - return [platform] + # type: () -> Iterator[str] + yield _normalize_string(distutils.util.get_platform()) -def _interpreter_name(): - name = platform.python_implementation().lower() +def _platform_tags(): + # type: () -> Iterator[str] + """ + Provides the platform tags for this installation. + """ + if platform.system() == "Darwin": + return mac_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + else: + return _generic_platforms() + + +def interpreter_name(): + # type: () -> str + """ + Returns the name of the running interpreter. + """ + try: + name = sys.implementation.name # type: ignore + except AttributeError: # pragma: no cover + # Python 2.7 compatibility. + name = platform.python_implementation().lower() return INTERPRETER_SHORT_NAMES.get(name) or name -def _generic_interpreter(name, py_version): - version = sysconfig.get_config_var("py_version_nodot") - if not version: - version = "".join(map(str, py_version[:2])) - return "{name}{version}".format(name=name, version=version) +def interpreter_version(**kwargs): + # type: (bool) -> str + """ + Returns the version of the running interpreter. + """ + warn = _warn_keyword_parameter("interpreter_version", kwargs) + version = _get_config_var("py_version_nodot", warn=warn) + if version: + version = str(version) + else: + version = _version_nodot(sys.version_info[:2]) + return version + + +def _version_nodot(version): + # type: (PythonVersion) -> str + if any(v >= 10 for v in version): + sep = "_" + else: + sep = "" + return sep.join(map(str, version)) -def sys_tags(): +def sys_tags(**kwargs): + # type: (bool) -> Iterator[Tag] """ Returns the sequence of tag triples for the running interpreter. The order of the sequence corresponds to priority order for the interpreter, from most to least important. """ - py_version = sys.version_info[:2] - interpreter_name = _interpreter_name() - if platform.system() == "Darwin": - platforms = _mac_platforms() - elif platform.system() == "Linux": - platforms = _linux_platforms() - else: - platforms = _generic_platforms() + warn = _warn_keyword_parameter("sys_tags", kwargs) - if interpreter_name == "cp": - interpreter = _cpython_interpreter(py_version) - abis = _cpython_abis(py_version) - for tag in _cpython_tags(py_version, interpreter, abis, platforms): - yield tag - elif interpreter_name == "pp": - interpreter = _pypy_interpreter() - abi = _generic_abi() - for tag in _pypy_tags(py_version, interpreter, abi, platforms): + interp_name = interpreter_name() + if interp_name == "cp": + for tag in cpython_tags(warn=warn): yield tag else: - interpreter = _generic_interpreter(interpreter_name, py_version) - abi = _generic_abi() - for tag in _generic_tags(interpreter, py_version, abi, platforms): + for tag in generic_tags(): yield tag - for tag in _independent_tags(interpreter, py_version, platforms): + + for tag in compatible_tags(): yield tag diff --git a/pkg_resources/_vendor/packaging/utils.py b/pkg_resources/_vendor/packaging/utils.py index 88418786..19579c1a 100644 --- a/pkg_resources/_vendor/packaging/utils.py +++ b/pkg_resources/_vendor/packaging/utils.py @@ -5,28 +5,36 @@ from __future__ import absolute_import, division, print_function import re +from ._typing import TYPE_CHECKING, cast from .version import InvalidVersion, Version +if TYPE_CHECKING: # pragma: no cover + from typing import NewType, Union + + NormalizedName = NewType("NormalizedName", str) _canonicalize_regex = re.compile(r"[-_.]+") def canonicalize_name(name): + # type: (str) -> NormalizedName # This is taken from PEP 503. - return _canonicalize_regex.sub("-", name).lower() + value = _canonicalize_regex.sub("-", name).lower() + return cast("NormalizedName", value) -def canonicalize_version(version): +def canonicalize_version(_version): + # type: (str) -> Union[Version, str] """ - This is very similar to Version.__str__, but has one subtle differences + This is very similar to Version.__str__, but has one subtle difference with the way it handles the release segment. """ try: - version = Version(version) + version = Version(_version) except InvalidVersion: # Legacy versions cannot be normalized - return version + return _version parts = [] diff --git a/pkg_resources/_vendor/packaging/version.py b/pkg_resources/_vendor/packaging/version.py index 95157a1f..00371e86 100644 --- a/pkg_resources/_vendor/packaging/version.py +++ b/pkg_resources/_vendor/packaging/version.py @@ -7,8 +7,35 @@ import collections import itertools import re -from ._structures import Infinity - +from ._structures import Infinity, NegativeInfinity +from ._typing import TYPE_CHECKING + +if TYPE_CHECKING: # pragma: no cover + from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union + + from ._structures import InfinityType, NegativeInfinityType + + InfiniteTypes = Union[InfinityType, NegativeInfinityType] + PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] + SubLocalType = Union[InfiniteTypes, int, str] + LocalType = Union[ + NegativeInfinityType, + Tuple[ + Union[ + SubLocalType, + Tuple[SubLocalType, str], + Tuple[NegativeInfinityType, SubLocalType], + ], + ..., + ], + ] + CmpKey = Tuple[ + int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType + ] + LegacyCmpKey = Tuple[int, Tuple[str, ...]] + VersionComparisonMethod = Callable[ + [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool + ] __all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] @@ -19,6 +46,7 @@ _Version = collections.namedtuple( def parse(version): + # type: (str) -> Union[LegacyVersion, Version] """ Parse the given version string and return either a :class:`Version` object or a :class:`LegacyVersion` object depending on if the given version is @@ -37,28 +65,38 @@ class InvalidVersion(ValueError): class _BaseVersion(object): + _key = None # type: Union[CmpKey, LegacyCmpKey] + def __hash__(self): + # type: () -> int return hash(self._key) def __lt__(self, other): + # type: (_BaseVersion) -> bool return self._compare(other, lambda s, o: s < o) def __le__(self, other): + # type: (_BaseVersion) -> bool return self._compare(other, lambda s, o: s <= o) def __eq__(self, other): + # type: (object) -> bool return self._compare(other, lambda s, o: s == o) def __ge__(self, other): + # type: (_BaseVersion) -> bool return self._compare(other, lambda s, o: s >= o) def __gt__(self, other): + # type: (_BaseVersion) -> bool return self._compare(other, lambda s, o: s > o) def __ne__(self, other): + # type: (object) -> bool return self._compare(other, lambda s, o: s != o) def _compare(self, other, method): + # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented] if not isinstance(other, _BaseVersion): return NotImplemented @@ -67,57 +105,71 @@ class _BaseVersion(object): class LegacyVersion(_BaseVersion): def __init__(self, version): + # type: (str) -> None self._version = str(version) self._key = _legacy_cmpkey(self._version) def __str__(self): + # type: () -> str return self._version def __repr__(self): + # type: () -> str return "".format(repr(str(self))) @property def public(self): + # type: () -> str return self._version @property def base_version(self): + # type: () -> str return self._version @property def epoch(self): + # type: () -> int return -1 @property def release(self): + # type: () -> None return None @property def pre(self): + # type: () -> None return None @property def post(self): + # type: () -> None return None @property def dev(self): + # type: () -> None return None @property def local(self): + # type: () -> None return None @property def is_prerelease(self): + # type: () -> bool return False @property def is_postrelease(self): + # type: () -> bool return False @property def is_devrelease(self): + # type: () -> bool return False @@ -133,6 +185,7 @@ _legacy_version_replacement_map = { def _parse_version_parts(s): + # type: (str) -> Iterator[str] for part in _legacy_version_component_re.split(s): part = _legacy_version_replacement_map.get(part, part) @@ -150,6 +203,8 @@ def _parse_version_parts(s): def _legacy_cmpkey(version): + # type: (str) -> LegacyCmpKey + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch # greater than or equal to 0. This will effectively put the LegacyVersion, # which uses the defacto standard originally implemented by setuptools, @@ -158,7 +213,7 @@ def _legacy_cmpkey(version): # This scheme is taken from pkg_resources.parse_version setuptools prior to # it's adoption of the packaging library. - parts = [] + parts = [] # type: List[str] for part in _parse_version_parts(version.lower()): if part.startswith("*"): # remove "-" before a prerelease tag @@ -171,9 +226,8 @@ def _legacy_cmpkey(version): parts.pop() parts.append(part) - parts = tuple(parts) - return epoch, parts + return epoch, tuple(parts) # Deliberately not anchored to the start and end of the string, to make it @@ -215,6 +269,8 @@ class Version(_BaseVersion): _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) def __init__(self, version): + # type: (str) -> None + # Validate the version and parse it into pieces match = self._regex.search(version) if not match: @@ -243,9 +299,11 @@ class Version(_BaseVersion): ) def __repr__(self): + # type: () -> str return "".format(repr(str(self))) def __str__(self): + # type: () -> str parts = [] # Epoch @@ -275,26 +333,35 @@ class Version(_BaseVersion): @property def epoch(self): - return self._version.epoch + # type: () -> int + _epoch = self._version.epoch # type: int + return _epoch @property def release(self): - return self._version.release + # type: () -> Tuple[int, ...] + _release = self._version.release # type: Tuple[int, ...] + return _release @property def pre(self): - return self._version.pre + # type: () -> Optional[Tuple[str, int]] + _pre = self._version.pre # type: Optional[Tuple[str, int]] + return _pre @property def post(self): + # type: () -> Optional[Tuple[str, int]] return self._version.post[1] if self._version.post else None @property def dev(self): + # type: () -> Optional[Tuple[str, int]] return self._version.dev[1] if self._version.dev else None @property def local(self): + # type: () -> Optional[str] if self._version.local: return ".".join(str(x) for x in self._version.local) else: @@ -302,10 +369,12 @@ class Version(_BaseVersion): @property def public(self): + # type: () -> str return str(self).split("+", 1)[0] @property def base_version(self): + # type: () -> str parts = [] # Epoch @@ -319,18 +388,41 @@ class Version(_BaseVersion): @property def is_prerelease(self): + # type: () -> bool return self.dev is not None or self.pre is not None @property def is_postrelease(self): + # type: () -> bool return self.post is not None @property def is_devrelease(self): + # type: () -> bool return self.dev is not None + @property + def major(self): + # type: () -> int + return self.release[0] if len(self.release) >= 1 else 0 + + @property + def minor(self): + # type: () -> int + return self.release[1] if len(self.release) >= 2 else 0 + + @property + def micro(self): + # type: () -> int + return self.release[2] if len(self.release) >= 3 else 0 + + +def _parse_letter_version( + letter, # type: str + number, # type: Union[str, bytes, SupportsInt] +): + # type: (...) -> Optional[Tuple[str, int]] -def _parse_letter_version(letter, number): if letter: # We consider there to be an implicit 0 in a pre-release if there is # not a numeral associated with it. @@ -360,11 +452,14 @@ def _parse_letter_version(letter, number): return letter, int(number) + return None + _local_version_separators = re.compile(r"[\._-]") def _parse_local_version(local): + # type: (str) -> Optional[LocalType] """ Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). """ @@ -373,15 +468,25 @@ def _parse_local_version(local): part.lower() if not part.isdigit() else int(part) for part in _local_version_separators.split(local) ) + return None + +def _cmpkey( + epoch, # type: int + release, # type: Tuple[int, ...] + pre, # type: Optional[Tuple[str, int]] + post, # type: Optional[Tuple[str, int]] + dev, # type: Optional[Tuple[str, int]] + local, # type: Optional[Tuple[SubLocalType]] +): + # type: (...) -> CmpKey -def _cmpkey(epoch, release, pre, post, dev, local): # When we compare a release version, we want to compare it with all of the # trailing zeros removed. So we'll use a reverse the list, drop all the now # leading zeros until we come to something non zero, then take the rest # re-reverse it back into the correct order and make it a tuple and use # that for our sorting key. - release = tuple( + _release = tuple( reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) ) @@ -390,23 +495,31 @@ def _cmpkey(epoch, release, pre, post, dev, local): # if there is not a pre or a post segment. If we have one of those then # the normal sorting rules will handle this case correctly. if pre is None and post is None and dev is not None: - pre = -Infinity + _pre = NegativeInfinity # type: PrePostDevType # Versions without a pre-release (except as noted above) should sort after # those with one. elif pre is None: - pre = Infinity + _pre = Infinity + else: + _pre = pre # Versions without a post segment should sort before those with one. if post is None: - post = -Infinity + _post = NegativeInfinity # type: PrePostDevType + + else: + _post = post # Versions without a development segment should sort after those with one. if dev is None: - dev = Infinity + _dev = Infinity # type: PrePostDevType + + else: + _dev = dev if local is None: # Versions without a local segment should sort before those with one. - local = -Infinity + _local = NegativeInfinity # type: LocalType else: # Versions with a local segment need that segment parsed to implement # the sorting rules in PEP440. @@ -415,6 +528,8 @@ def _cmpkey(epoch, release, pre, post, dev, local): # - Numeric segments sort numerically # - Shorter versions sort before longer versions when the prefixes # match exactly - local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local) + _local = tuple( + (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local + ) - return epoch, release, pre, post, dev, local + return epoch, _release, _pre, _post, _dev, _local diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt index f581a623..7862a3ce 100644 --- a/pkg_resources/_vendor/vendored.txt +++ b/pkg_resources/_vendor/vendored.txt @@ -1,4 +1,4 @@ -packaging==19.2 +packaging==20.4 pyparsing==2.2.1 six==1.10.0 appdirs==1.4.3 diff --git a/setuptools/_vendor/packaging/__about__.py b/setuptools/_vendor/packaging/__about__.py index dc95138d..4d998578 100644 --- a/setuptools/_vendor/packaging/__about__.py +++ b/setuptools/_vendor/packaging/__about__.py @@ -18,10 +18,10 @@ __title__ = "packaging" __summary__ = "Core utilities for Python packages" __uri__ = "https://github.com/pypa/packaging" -__version__ = "19.2" +__version__ = "20.4" __author__ = "Donald Stufft and individual contributors" __email__ = "donald@stufft.io" -__license__ = "BSD or Apache License, Version 2.0" +__license__ = "BSD-2-Clause or Apache-2.0" __copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/setuptools/_vendor/packaging/_compat.py b/setuptools/_vendor/packaging/_compat.py index 25da473c..e54bd4ed 100644 --- a/setuptools/_vendor/packaging/_compat.py +++ b/setuptools/_vendor/packaging/_compat.py @@ -5,6 +5,11 @@ from __future__ import absolute_import, division, print_function import sys +from ._typing import TYPE_CHECKING + +if TYPE_CHECKING: # pragma: no cover + from typing import Any, Dict, Tuple, Type + PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 @@ -18,14 +23,16 @@ else: def with_metaclass(meta, *bases): + # type: (Type[Any], Tuple[Type[Any], ...]) -> Any """ Create a base class with a metaclass. """ # This requires a bit of explanation: the basic idea is to make a dummy # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. - class metaclass(meta): + class metaclass(meta): # type: ignore def __new__(cls, name, this_bases, d): + # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any return meta(name, bases, d) return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/setuptools/_vendor/packaging/_structures.py b/setuptools/_vendor/packaging/_structures.py index 68dcca63..800d5c55 100644 --- a/setuptools/_vendor/packaging/_structures.py +++ b/setuptools/_vendor/packaging/_structures.py @@ -4,65 +4,83 @@ from __future__ import absolute_import, division, print_function -class Infinity(object): +class InfinityType(object): def __repr__(self): + # type: () -> str return "Infinity" def __hash__(self): + # type: () -> int return hash(repr(self)) def __lt__(self, other): + # type: (object) -> bool return False def __le__(self, other): + # type: (object) -> bool return False def __eq__(self, other): + # type: (object) -> bool return isinstance(other, self.__class__) def __ne__(self, other): + # type: (object) -> bool return not isinstance(other, self.__class__) def __gt__(self, other): + # type: (object) -> bool return True def __ge__(self, other): + # type: (object) -> bool return True def __neg__(self): + # type: (object) -> NegativeInfinityType return NegativeInfinity -Infinity = Infinity() +Infinity = InfinityType() -class NegativeInfinity(object): +class NegativeInfinityType(object): def __repr__(self): + # type: () -> str return "-Infinity" def __hash__(self): + # type: () -> int return hash(repr(self)) def __lt__(self, other): + # type: (object) -> bool return True def __le__(self, other): + # type: (object) -> bool return True def __eq__(self, other): + # type: (object) -> bool return isinstance(other, self.__class__) def __ne__(self, other): + # type: (object) -> bool return not isinstance(other, self.__class__) def __gt__(self, other): + # type: (object) -> bool return False def __ge__(self, other): + # type: (object) -> bool return False def __neg__(self): + # type: (object) -> InfinityType return Infinity -NegativeInfinity = NegativeInfinity() +NegativeInfinity = NegativeInfinityType() diff --git a/setuptools/_vendor/packaging/_typing.py b/setuptools/_vendor/packaging/_typing.py new file mode 100644 index 00000000..77a8b918 --- /dev/null +++ b/setuptools/_vendor/packaging/_typing.py @@ -0,0 +1,48 @@ +"""For neatly implementing static typing in packaging. + +`mypy` - the static type analysis tool we use - uses the `typing` module, which +provides core functionality fundamental to mypy's functioning. + +Generally, `typing` would be imported at runtime and used in that fashion - +it acts as a no-op at runtime and does not have any run-time overhead by +design. + +As it turns out, `typing` is not vendorable - it uses separate sources for +Python 2/Python 3. Thus, this codebase can not expect it to be present. +To work around this, mypy allows the typing import to be behind a False-y +optional to prevent it from running at runtime and type-comments can be used +to remove the need for the types to be accessible directly during runtime. + +This module provides the False-y guard in a nicely named fashion so that a +curious maintainer can reach here to read this. + +In packaging, all static-typing related imports should be guarded as follows: + + from packaging._typing import TYPE_CHECKING + + if TYPE_CHECKING: + from typing import ... + +Ref: https://github.com/python/mypy/issues/3216 +""" + +__all__ = ["TYPE_CHECKING", "cast"] + +# The TYPE_CHECKING constant defined by the typing module is False at runtime +# but True while type checking. +if False: # pragma: no cover + from typing import TYPE_CHECKING +else: + TYPE_CHECKING = False + +# typing's cast syntax requires calling typing.cast at runtime, but we don't +# want to import typing at runtime. Here, we inform the type checkers that +# we're importing `typing.cast` as `cast` and re-implement typing.cast's +# runtime behavior in a block that is ignored by type checkers. +if TYPE_CHECKING: # pragma: no cover + # not executed at runtime + from typing import cast +else: + # executed at runtime + def cast(type_, value): # noqa + return value diff --git a/setuptools/_vendor/packaging/markers.py b/setuptools/_vendor/packaging/markers.py index 4bdfdb24..03fbdfcc 100644 --- a/setuptools/_vendor/packaging/markers.py +++ b/setuptools/_vendor/packaging/markers.py @@ -13,8 +13,14 @@ from setuptools.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString from setuptools.extern.pyparsing import Literal as L # noqa from ._compat import string_types +from ._typing import TYPE_CHECKING from .specifiers import Specifier, InvalidSpecifier +if TYPE_CHECKING: # pragma: no cover + from typing import Any, Callable, Dict, List, Optional, Tuple, Union + + Operator = Callable[[str, str], bool] + __all__ = [ "InvalidMarker", @@ -46,30 +52,37 @@ class UndefinedEnvironmentName(ValueError): class Node(object): def __init__(self, value): + # type: (Any) -> None self.value = value def __str__(self): + # type: () -> str return str(self.value) def __repr__(self): + # type: () -> str return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) def serialize(self): + # type: () -> str raise NotImplementedError class Variable(Node): def serialize(self): + # type: () -> str return str(self) class Value(Node): def serialize(self): + # type: () -> str return '"{0}"'.format(self) class Op(Node): def serialize(self): + # type: () -> str return str(self) @@ -85,13 +98,13 @@ VARIABLE = ( | L("python_version") | L("sys_platform") | L("os_name") - | L("os.name") + | L("os.name") # PEP-345 | L("sys.platform") # PEP-345 | L("platform.version") # PEP-345 | L("platform.machine") # PEP-345 | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # PEP-345 - | L("extra") # undocumented setuptools legacy + | L("python_implementation") # undocumented setuptools legacy + | L("extra") # PEP-508 ) ALIASES = { "os.name": "os_name", @@ -131,6 +144,7 @@ MARKER = stringStart + MARKER_EXPR + stringEnd def _coerce_parse_result(results): + # type: (Union[ParseResults, List[Any]]) -> List[Any] if isinstance(results, ParseResults): return [_coerce_parse_result(i) for i in results] else: @@ -138,6 +152,8 @@ def _coerce_parse_result(results): def _format_marker(marker, first=True): + # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str + assert isinstance(marker, (list, tuple, string_types)) # Sometimes we have a structure like [[...]] which is a single item list @@ -172,10 +188,11 @@ _operators = { "!=": operator.ne, ">=": operator.ge, ">": operator.gt, -} +} # type: Dict[str, Operator] def _eval_op(lhs, op, rhs): + # type: (str, Op, str) -> bool try: spec = Specifier("".join([op.serialize(), rhs])) except InvalidSpecifier: @@ -183,7 +200,7 @@ def _eval_op(lhs, op, rhs): else: return spec.contains(lhs) - oper = _operators.get(op.serialize()) + oper = _operators.get(op.serialize()) # type: Optional[Operator] if oper is None: raise UndefinedComparison( "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) @@ -192,13 +209,18 @@ def _eval_op(lhs, op, rhs): return oper(lhs, rhs) -_undefined = object() +class Undefined(object): + pass + + +_undefined = Undefined() def _get_env(environment, name): - value = environment.get(name, _undefined) + # type: (Dict[str, str], str) -> str + value = environment.get(name, _undefined) # type: Union[str, Undefined] - if value is _undefined: + if isinstance(value, Undefined): raise UndefinedEnvironmentName( "{0!r} does not exist in evaluation environment.".format(name) ) @@ -207,7 +229,8 @@ def _get_env(environment, name): def _evaluate_markers(markers, environment): - groups = [[]] + # type: (List[Any], Dict[str, str]) -> bool + groups = [[]] # type: List[List[bool]] for marker in markers: assert isinstance(marker, (list, tuple, string_types)) @@ -234,6 +257,7 @@ def _evaluate_markers(markers, environment): def format_full_version(info): + # type: (sys._version_info) -> str version = "{0.major}.{0.minor}.{0.micro}".format(info) kind = info.releaselevel if kind != "final": @@ -242,9 +266,13 @@ def format_full_version(info): def default_environment(): + # type: () -> Dict[str, str] if hasattr(sys, "implementation"): - iver = format_full_version(sys.implementation.version) - implementation_name = sys.implementation.name + # Ignoring the `sys.implementation` reference for type checking due to + # mypy not liking that the attribute doesn't exist in Python 2.7 when + # run with the `--py27` flag. + iver = format_full_version(sys.implementation.version) # type: ignore + implementation_name = sys.implementation.name # type: ignore else: iver = "0" implementation_name = "" @@ -266,6 +294,7 @@ def default_environment(): class Marker(object): def __init__(self, marker): + # type: (str) -> None try: self._markers = _coerce_parse_result(MARKER.parseString(marker)) except ParseException as e: @@ -275,12 +304,15 @@ class Marker(object): raise InvalidMarker(err_str) def __str__(self): + # type: () -> str return _format_marker(self._markers) def __repr__(self): + # type: () -> str return "".format(str(self)) def evaluate(self, environment=None): + # type: (Optional[Dict[str, str]]) -> bool """Evaluate a marker. Return the boolean from evaluating the given marker against the diff --git a/setuptools/_vendor/packaging/py.typed b/setuptools/_vendor/packaging/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/setuptools/_vendor/packaging/requirements.py b/setuptools/_vendor/packaging/requirements.py index 8a0c2cb9..06b17488 100644 --- a/setuptools/_vendor/packaging/requirements.py +++ b/setuptools/_vendor/packaging/requirements.py @@ -11,9 +11,13 @@ from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combi from setuptools.extern.pyparsing import Literal as L # noqa from setuptools.extern.six.moves.urllib import parse as urlparse +from ._typing import TYPE_CHECKING from .markers import MARKER_EXPR, Marker from .specifiers import LegacySpecifier, Specifier, SpecifierSet +if TYPE_CHECKING: # pragma: no cover + from typing import List + class InvalidRequirement(ValueError): """ @@ -89,6 +93,7 @@ class Requirement(object): # TODO: Can we normalize the name and extra name? def __init__(self, requirement_string): + # type: (str) -> None try: req = REQUIREMENT.parseString(requirement_string) except ParseException as e: @@ -116,7 +121,8 @@ class Requirement(object): self.marker = req.marker if req.marker else None def __str__(self): - parts = [self.name] + # type: () -> str + parts = [self.name] # type: List[str] if self.extras: parts.append("[{0}]".format(",".join(sorted(self.extras)))) @@ -135,4 +141,5 @@ class Requirement(object): return "".join(parts) def __repr__(self): + # type: () -> str return "".format(str(self)) diff --git a/setuptools/_vendor/packaging/specifiers.py b/setuptools/_vendor/packaging/specifiers.py index 743576a0..fe09bb1d 100644 --- a/setuptools/_vendor/packaging/specifiers.py +++ b/setuptools/_vendor/packaging/specifiers.py @@ -9,8 +9,27 @@ import itertools import re from ._compat import string_types, with_metaclass +from ._typing import TYPE_CHECKING +from .utils import canonicalize_version from .version import Version, LegacyVersion, parse +if TYPE_CHECKING: # pragma: no cover + from typing import ( + List, + Dict, + Union, + Iterable, + Iterator, + Optional, + Callable, + Tuple, + FrozenSet, + ) + + ParsedVersion = Union[Version, LegacyVersion] + UnparsedVersion = Union[Version, LegacyVersion, str] + CallableOperator = Callable[[ParsedVersion, str], bool] + class InvalidSpecifier(ValueError): """ @@ -18,9 +37,10 @@ class InvalidSpecifier(ValueError): """ -class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): +class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore @abc.abstractmethod def __str__(self): + # type: () -> str """ Returns the str representation of this Specifier like object. This should be representative of the Specifier itself. @@ -28,12 +48,14 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @abc.abstractmethod def __hash__(self): + # type: () -> int """ Returns a hash value for this Specifier like object. """ @abc.abstractmethod def __eq__(self, other): + # type: (object) -> bool """ Returns a boolean representing whether or not the two Specifier like objects are equal. @@ -41,6 +63,7 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @abc.abstractmethod def __ne__(self, other): + # type: (object) -> bool """ Returns a boolean representing whether or not the two Specifier like objects are not equal. @@ -48,6 +71,7 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @abc.abstractproperty def prereleases(self): + # type: () -> Optional[bool] """ Returns whether or not pre-releases as a whole are allowed by this specifier. @@ -55,6 +79,7 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @prereleases.setter def prereleases(self, value): + # type: (bool) -> None """ Sets whether or not pre-releases as a whole are allowed by this specifier. @@ -62,12 +87,14 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): @abc.abstractmethod def contains(self, item, prereleases=None): + # type: (str, Optional[bool]) -> bool """ Determines if the given item is contained within this specifier. """ @abc.abstractmethod def filter(self, iterable, prereleases=None): + # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] """ Takes an iterable of items and filters them so that only items which are contained within this specifier are allowed in it. @@ -76,19 +103,24 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): class _IndividualSpecifier(BaseSpecifier): - _operators = {} + _operators = {} # type: Dict[str, str] def __init__(self, spec="", prereleases=None): + # type: (str, Optional[bool]) -> None match = self._regex.search(spec) if not match: raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) - self._spec = (match.group("operator").strip(), match.group("version").strip()) + self._spec = ( + match.group("operator").strip(), + match.group("version").strip(), + ) # type: Tuple[str, str] # Store whether or not this Specifier should accept prereleases self._prereleases = prereleases def __repr__(self): + # type: () -> str pre = ( ", prereleases={0!r}".format(self.prereleases) if self._prereleases is not None @@ -98,26 +130,35 @@ class _IndividualSpecifier(BaseSpecifier): return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) def __str__(self): + # type: () -> str return "{0}{1}".format(*self._spec) + @property + def _canonical_spec(self): + # type: () -> Tuple[str, Union[Version, str]] + return self._spec[0], canonicalize_version(self._spec[1]) + def __hash__(self): - return hash(self._spec) + # type: () -> int + return hash(self._canonical_spec) def __eq__(self, other): + # type: (object) -> bool if isinstance(other, string_types): try: - other = self.__class__(other) + other = self.__class__(str(other)) except InvalidSpecifier: return NotImplemented elif not isinstance(other, self.__class__): return NotImplemented - return self._spec == other._spec + return self._canonical_spec == other._canonical_spec def __ne__(self, other): + # type: (object) -> bool if isinstance(other, string_types): try: - other = self.__class__(other) + other = self.__class__(str(other)) except InvalidSpecifier: return NotImplemented elif not isinstance(other, self.__class__): @@ -126,52 +167,67 @@ class _IndividualSpecifier(BaseSpecifier): return self._spec != other._spec def _get_operator(self, op): - return getattr(self, "_compare_{0}".format(self._operators[op])) + # type: (str) -> CallableOperator + operator_callable = getattr( + self, "_compare_{0}".format(self._operators[op]) + ) # type: CallableOperator + return operator_callable def _coerce_version(self, version): + # type: (UnparsedVersion) -> ParsedVersion if not isinstance(version, (LegacyVersion, Version)): version = parse(version) return version @property def operator(self): + # type: () -> str return self._spec[0] @property def version(self): + # type: () -> str return self._spec[1] @property def prereleases(self): + # type: () -> Optional[bool] return self._prereleases @prereleases.setter def prereleases(self, value): + # type: (bool) -> None self._prereleases = value def __contains__(self, item): + # type: (str) -> bool return self.contains(item) def contains(self, item, prereleases=None): + # type: (UnparsedVersion, Optional[bool]) -> bool + # Determine if prereleases are to be allowed or not. if prereleases is None: prereleases = self.prereleases # Normalize item to a Version or LegacyVersion, this allows us to have # a shortcut for ``"2.0" in Specifier(">=2") - item = self._coerce_version(item) + normalized_item = self._coerce_version(item) # Determine if we should be supporting prereleases in this specifier # or not, if we do not support prereleases than we can short circuit # logic if this version is a prereleases. - if item.is_prerelease and not prereleases: + if normalized_item.is_prerelease and not prereleases: return False # Actually do the comparison to determine if this item is contained # within this Specifier or not. - return self._get_operator(self.operator)(item, self.version) + operator_callable = self._get_operator(self.operator) # type: CallableOperator + return operator_callable(normalized_item, self.version) def filter(self, iterable, prereleases=None): + # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion] + yielded = False found_prereleases = [] @@ -230,32 +286,43 @@ class LegacySpecifier(_IndividualSpecifier): } def _coerce_version(self, version): + # type: (Union[ParsedVersion, str]) -> LegacyVersion if not isinstance(version, LegacyVersion): version = LegacyVersion(str(version)) return version def _compare_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective == self._coerce_version(spec) def _compare_not_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective != self._coerce_version(spec) def _compare_less_than_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective <= self._coerce_version(spec) def _compare_greater_than_equal(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective >= self._coerce_version(spec) def _compare_less_than(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective < self._coerce_version(spec) def _compare_greater_than(self, prospective, spec): + # type: (LegacyVersion, str) -> bool return prospective > self._coerce_version(spec) -def _require_version_compare(fn): +def _require_version_compare( + fn # type: (Callable[[Specifier, ParsedVersion, str], bool]) +): + # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool] @functools.wraps(fn) def wrapped(self, prospective, spec): + # type: (Specifier, ParsedVersion, str) -> bool if not isinstance(prospective, Version): return False return fn(self, prospective, spec) @@ -373,6 +440,8 @@ class Specifier(_IndividualSpecifier): @_require_version_compare def _compare_compatible(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + # Compatible releases have an equivalent combination of >= and ==. That # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to # implement this in terms of the other specifiers instead of @@ -400,56 +469,75 @@ class Specifier(_IndividualSpecifier): @_require_version_compare def _compare_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool + # We need special logic to handle prefix matching if spec.endswith(".*"): # In the case of prefix matching we want to ignore local segment. prospective = Version(prospective.public) # Split the spec out by dots, and pretend that there is an implicit # dot in between a release segment and a pre-release segment. - spec = _version_split(spec[:-2]) # Remove the trailing .* + split_spec = _version_split(spec[:-2]) # Remove the trailing .* # Split the prospective version out by dots, and pretend that there # is an implicit dot in between a release segment and a pre-release # segment. - prospective = _version_split(str(prospective)) + split_prospective = _version_split(str(prospective)) # Shorten the prospective version to be the same length as the spec # so that we can determine if the specifier is a prefix of the # prospective version or not. - prospective = prospective[: len(spec)] + shortened_prospective = split_prospective[: len(split_spec)] # Pad out our two sides with zeros so that they both equal the same # length. - spec, prospective = _pad_version(spec, prospective) + padded_spec, padded_prospective = _pad_version( + split_spec, shortened_prospective + ) + + return padded_prospective == padded_spec else: # Convert our spec string into a Version - spec = Version(spec) + spec_version = Version(spec) # If the specifier does not have a local segment, then we want to # act as if the prospective version also does not have a local # segment. - if not spec.local: + if not spec_version.local: prospective = Version(prospective.public) - return prospective == spec + return prospective == spec_version @_require_version_compare def _compare_not_equal(self, prospective, spec): + # type: (ParsedVersion, str) -> bool return not self._compare_equal(prospective, spec) @_require_version_compare def _compare_less_than_equal(self, prospective, spec): - return prospective <= Version(spec) + # type: (ParsedVersion, str) -> bool + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) @_require_version_compare def _compare_greater_than_equal(self, prospective, spec): - return prospective >= Version(spec) + # type: (ParsedVersion, str) -> bool + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) @_require_version_compare - def _compare_less_than(self, prospective, spec): + def _compare_less_than(self, prospective, spec_str): + # type: (ParsedVersion, str) -> bool + # Convert our spec to a Version instance, since we'll want to work with # it as a version. - spec = Version(spec) + spec = Version(spec_str) # Check to see if the prospective version is less than the spec # version. If it's not we can short circuit and just return False now @@ -471,10 +559,12 @@ class Specifier(_IndividualSpecifier): return True @_require_version_compare - def _compare_greater_than(self, prospective, spec): + def _compare_greater_than(self, prospective, spec_str): + # type: (ParsedVersion, str) -> bool + # Convert our spec to a Version instance, since we'll want to work with # it as a version. - spec = Version(spec) + spec = Version(spec_str) # Check to see if the prospective version is greater than the spec # version. If it's not we can short circuit and just return False now @@ -502,10 +592,13 @@ class Specifier(_IndividualSpecifier): return True def _compare_arbitrary(self, prospective, spec): + # type: (Version, str) -> bool return str(prospective).lower() == str(spec).lower() @property def prereleases(self): + # type: () -> bool + # If there is an explicit prereleases set for this, then we'll just # blindly use that. if self._prereleases is not None: @@ -530,6 +623,7 @@ class Specifier(_IndividualSpecifier): @prereleases.setter def prereleases(self, value): + # type: (bool) -> None self._prereleases = value @@ -537,7 +631,8 @@ _prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") def _version_split(version): - result = [] + # type: (str) -> List[str] + result = [] # type: List[str] for item in version.split("."): match = _prefix_regex.search(item) if match: @@ -548,6 +643,7 @@ def _version_split(version): def _pad_version(left, right): + # type: (List[str], List[str]) -> Tuple[List[str], List[str]] left_split, right_split = [], [] # Get the release segment of our versions @@ -567,14 +663,16 @@ def _pad_version(left, right): class SpecifierSet(BaseSpecifier): def __init__(self, specifiers="", prereleases=None): - # Split on , to break each indidivual specifier into it's own item, and + # type: (str, Optional[bool]) -> None + + # Split on , to break each individual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. - specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] # Parsed each individual specifier, attempting first to make it a # Specifier and falling back to a LegacySpecifier. parsed = set() - for specifier in specifiers: + for specifier in split_specifiers: try: parsed.add(Specifier(specifier)) except InvalidSpecifier: @@ -588,6 +686,7 @@ class SpecifierSet(BaseSpecifier): self._prereleases = prereleases def __repr__(self): + # type: () -> str pre = ( ", prereleases={0!r}".format(self.prereleases) if self._prereleases is not None @@ -597,12 +696,15 @@ class SpecifierSet(BaseSpecifier): return "".format(str(self), pre) def __str__(self): + # type: () -> str return ",".join(sorted(str(s) for s in self._specs)) def __hash__(self): + # type: () -> int return hash(self._specs) def __and__(self, other): + # type: (Union[SpecifierSet, str]) -> SpecifierSet if isinstance(other, string_types): other = SpecifierSet(other) elif not isinstance(other, SpecifierSet): @@ -626,9 +728,8 @@ class SpecifierSet(BaseSpecifier): return specifier def __eq__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): + # type: (object) -> bool + if isinstance(other, (string_types, _IndividualSpecifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented @@ -636,9 +737,8 @@ class SpecifierSet(BaseSpecifier): return self._specs == other._specs def __ne__(self, other): - if isinstance(other, string_types): - other = SpecifierSet(other) - elif isinstance(other, _IndividualSpecifier): + # type: (object) -> bool + if isinstance(other, (string_types, _IndividualSpecifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented @@ -646,13 +746,17 @@ class SpecifierSet(BaseSpecifier): return self._specs != other._specs def __len__(self): + # type: () -> int return len(self._specs) def __iter__(self): + # type: () -> Iterator[FrozenSet[_IndividualSpecifier]] return iter(self._specs) @property def prereleases(self): + # type: () -> Optional[bool] + # If we have been given an explicit prerelease modifier, then we'll # pass that through here. if self._prereleases is not None: @@ -670,12 +774,16 @@ class SpecifierSet(BaseSpecifier): @prereleases.setter def prereleases(self, value): + # type: (bool) -> None self._prereleases = value def __contains__(self, item): + # type: (Union[ParsedVersion, str]) -> bool return self.contains(item) def contains(self, item, prereleases=None): + # type: (Union[ParsedVersion, str], Optional[bool]) -> bool + # Ensure that our item is a Version or LegacyVersion instance. if not isinstance(item, (LegacyVersion, Version)): item = parse(item) @@ -701,7 +809,13 @@ class SpecifierSet(BaseSpecifier): # will always return True, this is an explicit design decision. return all(s.contains(item, prereleases=prereleases) for s in self._specs) - def filter(self, iterable, prereleases=None): + def filter( + self, + iterable, # type: Iterable[Union[ParsedVersion, str]] + prereleases=None, # type: Optional[bool] + ): + # type: (...) -> Iterable[Union[ParsedVersion, str]] + # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the # SpecifierSet thinks for whether or not we should support prereleases. @@ -719,8 +833,8 @@ class SpecifierSet(BaseSpecifier): # which will filter out any pre-releases, unless there are no final # releases, and which will filter out LegacyVersion in general. else: - filtered = [] - found_prereleases = [] + filtered = [] # type: List[Union[ParsedVersion, str]] + found_prereleases = [] # type: List[Union[ParsedVersion, str]] for item in iterable: # Ensure that we some kind of Version class for this item. diff --git a/setuptools/_vendor/packaging/tags.py b/setuptools/_vendor/packaging/tags.py index ec9942f0..9064910b 100644 --- a/setuptools/_vendor/packaging/tags.py +++ b/setuptools/_vendor/packaging/tags.py @@ -13,12 +13,37 @@ except ImportError: # pragma: no cover EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] del imp +import logging +import os import platform import re +import struct import sys import sysconfig import warnings +from ._typing import TYPE_CHECKING, cast + +if TYPE_CHECKING: # pragma: no cover + from typing import ( + Dict, + FrozenSet, + IO, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + ) + + PythonVersion = Sequence[int] + MacVersion = Tuple[int, int] + GlibcVersion = Tuple[int, int] + + +logger = logging.getLogger(__name__) INTERPRETER_SHORT_NAMES = { "python": "py", # Generic. @@ -26,34 +51,48 @@ INTERPRETER_SHORT_NAMES = { "pypy": "pp", "ironpython": "ip", "jython": "jy", -} +} # type: Dict[str, str] _32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 class Tag(object): + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + """ __slots__ = ["_interpreter", "_abi", "_platform"] def __init__(self, interpreter, abi, platform): + # type: (str, str, str) -> None self._interpreter = interpreter.lower() self._abi = abi.lower() self._platform = platform.lower() @property def interpreter(self): + # type: () -> str return self._interpreter @property def abi(self): + # type: () -> str return self._abi @property def platform(self): + # type: () -> str return self._platform def __eq__(self, other): + # type: (object) -> bool + if not isinstance(other, Tag): + return NotImplemented + return ( (self.platform == other.platform) and (self.abi == other.abi) @@ -61,16 +100,26 @@ class Tag(object): ) def __hash__(self): + # type: () -> int return hash((self._interpreter, self._abi, self._platform)) def __str__(self): + # type: () -> str return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) def __repr__(self): + # type: () -> str return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) def parse_tag(tag): + # type: (str) -> FrozenSet[Tag] + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. + + Returning a set is required due to the possibility that the tag is a + compressed tag set. + """ tags = set() interpreters, abis, platforms = tag.split("-") for interpreter in interpreters.split("."): @@ -80,20 +129,54 @@ def parse_tag(tag): return frozenset(tags) +def _warn_keyword_parameter(func_name, kwargs): + # type: (str, Dict[str, bool]) -> bool + """ + Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only. + """ + if not kwargs: + return False + elif len(kwargs) > 1 or "warn" not in kwargs: + kwargs.pop("warn", None) + arg = next(iter(kwargs.keys())) + raise TypeError( + "{}() got an unexpected keyword argument {!r}".format(func_name, arg) + ) + return kwargs["warn"] + + +def _get_config_var(name, warn=False): + # type: (str, bool) -> Union[int, str, None] + value = sysconfig.get_config_var(name) + if value is None and warn: + logger.debug( + "Config variable '%s' is unset, Python ABI tag may be incorrect", name + ) + return value + + def _normalize_string(string): + # type: (str) -> str return string.replace(".", "_").replace("-", "_") -def _cpython_interpreter(py_version): - # TODO: Is using py_version_nodot for interpreter version critical? - return "cp{major}{minor}".format(major=py_version[0], minor=py_version[1]) +def _abi3_applies(python_version): + # type: (PythonVersion) -> bool + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) -def _cpython_abis(py_version): +def _cpython_abis(py_version, warn=False): + # type: (PythonVersion, bool) -> List[str] + py_version = tuple(py_version) # To allow for version comparison. abis = [] - version = "{}{}".format(*py_version[:2]) + version = _version_nodot(py_version[:2]) debug = pymalloc = ucs4 = "" - with_debug = sysconfig.get_config_var("Py_DEBUG") + with_debug = _get_config_var("Py_DEBUG", warn) has_refcount = hasattr(sys, "gettotalrefcount") # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled # extension modules is the best option. @@ -102,11 +185,11 @@ def _cpython_abis(py_version): if with_debug or (with_debug is None and (has_refcount or has_ext)): debug = "d" if py_version < (3, 8): - with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC") + with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) if with_pymalloc or with_pymalloc is None: pymalloc = "m" if py_version < (3, 3): - unicode_size = sysconfig.get_config_var("Py_UNICODE_SIZE") + unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) if unicode_size == 4 or ( unicode_size is None and sys.maxunicode == 0x10FFFF ): @@ -124,86 +207,148 @@ def _cpython_abis(py_version): return abis -def _cpython_tags(py_version, interpreter, abis, platforms): +def cpython_tags( + python_version=None, # type: Optional[PythonVersion] + abis=None, # type: Optional[Iterable[str]] + platforms=None, # type: Optional[Iterable[str]] + **kwargs # type: bool +): + # type: (...) -> Iterator[Tag] + """ + Yields the tags for a CPython interpreter. + + The tags consist of: + - cp-- + - cp-abi3- + - cp-none- + - cp-abi3- # Older Python versions down to 3.2. + + If python_version only specifies a major version then user-provided ABIs and + the 'none' ABItag will be used. + + If 'abi3' or 'none' are specified in 'abis' then they will be yielded at + their normal position and not at the beginning. + """ + warn = _warn_keyword_parameter("cpython_tags", kwargs) + if not python_version: + python_version = sys.version_info[:2] + + interpreter = "cp{}".format(_version_nodot(python_version[:2])) + + if abis is None: + if len(python_version) > 1: + abis = _cpython_abis(python_version, warn) + else: + abis = [] + abis = list(abis) + # 'abi3' and 'none' are explicitly handled later. + for explicit_abi in ("abi3", "none"): + try: + abis.remove(explicit_abi) + except ValueError: + pass + + platforms = list(platforms or _platform_tags()) for abi in abis: for platform_ in platforms: yield Tag(interpreter, abi, platform_) - for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): - yield tag + if _abi3_applies(python_version): + for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): + yield tag for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): yield tag - # PEP 384 was first implemented in Python 3.2. - for minor_version in range(py_version[1] - 1, 1, -1): - for platform_ in platforms: - interpreter = "cp{major}{minor}".format( - major=py_version[0], minor=minor_version - ) - yield Tag(interpreter, "abi3", platform_) - -def _pypy_interpreter(): - return "pp{py_major}{pypy_major}{pypy_minor}".format( - py_major=sys.version_info[0], - pypy_major=sys.pypy_version_info.major, - pypy_minor=sys.pypy_version_info.minor, - ) + if _abi3_applies(python_version): + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{version}".format( + version=_version_nodot((python_version[0], minor_version)) + ) + yield Tag(interpreter, "abi3", platform_) def _generic_abi(): + # type: () -> Iterator[str] abi = sysconfig.get_config_var("SOABI") if abi: - return _normalize_string(abi) - else: - return "none" + yield _normalize_string(abi) -def _pypy_tags(py_version, interpreter, abi, platforms): - for tag in (Tag(interpreter, abi, platform) for platform in platforms): - yield tag - for tag in (Tag(interpreter, "none", platform) for platform in platforms): - yield tag +def generic_tags( + interpreter=None, # type: Optional[str] + abis=None, # type: Optional[Iterable[str]] + platforms=None, # type: Optional[Iterable[str]] + **kwargs # type: bool +): + # type: (...) -> Iterator[Tag] + """ + Yields the tags for a generic interpreter. + The tags consist of: + - -- -def _generic_tags(interpreter, py_version, abi, platforms): - for tag in (Tag(interpreter, abi, platform) for platform in platforms): - yield tag - if abi != "none": - tags = (Tag(interpreter, "none", platform_) for platform_ in platforms) - for tag in tags: - yield tag + The "none" ABI will be added if it was not explicitly provided. + """ + warn = _warn_keyword_parameter("generic_tags", kwargs) + if not interpreter: + interp_name = interpreter_name() + interp_version = interpreter_version(warn=warn) + interpreter = "".join([interp_name, interp_version]) + if abis is None: + abis = _generic_abi() + platforms = list(platforms or _platform_tags()) + abis = list(abis) + if "none" not in abis: + abis.append("none") + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) def _py_interpreter_range(py_version): + # type: (PythonVersion) -> Iterator[str] """ - Yield Python versions in descending order. + Yields Python versions in descending order. After the latest version, the major-only version will be yielded, and then - all following versions up to 'end'. + all previous versions of that major version. """ - yield "py{major}{minor}".format(major=py_version[0], minor=py_version[1]) + if len(py_version) > 1: + yield "py{version}".format(version=_version_nodot(py_version[:2])) yield "py{major}".format(major=py_version[0]) - for minor in range(py_version[1] - 1, -1, -1): - yield "py{major}{minor}".format(major=py_version[0], minor=minor) + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield "py{version}".format(version=_version_nodot((py_version[0], minor))) -def _independent_tags(interpreter, py_version, platforms): +def compatible_tags( + python_version=None, # type: Optional[PythonVersion] + interpreter=None, # type: Optional[str] + platforms=None, # type: Optional[Iterable[str]] +): + # type: (...) -> Iterator[Tag] """ - Return the sequence of tags that are consistent across implementations. + Yields the sequence of tags that are compatible with a specific version of Python. The tags consist of: - py*-none- - - -none-any + - -none-any # ... if `interpreter` is provided. - py*-none-any """ - for version in _py_interpreter_range(py_version): + if not python_version: + python_version = sys.version_info[:2] + platforms = list(platforms or _platform_tags()) + for version in _py_interpreter_range(python_version): for platform_ in platforms: yield Tag(version, "none", platform_) - yield Tag(interpreter, "none", "any") - for version in _py_interpreter_range(py_version): + if interpreter: + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(python_version): yield Tag(version, "none", "any") def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): + # type: (str, bool) -> str if not is_32bit: return arch @@ -214,6 +359,7 @@ def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): def _mac_binary_formats(version, cpu_arch): + # type: (MacVersion, str) -> List[str] formats = [cpu_arch] if cpu_arch == "x86_64": if version < (10, 4): @@ -240,32 +386,42 @@ def _mac_binary_formats(version, cpu_arch): return formats -def _mac_platforms(version=None, arch=None): - version_str, _, cpu_arch = platform.mac_ver() +def mac_platforms(version=None, arch=None): + # type: (Optional[MacVersion], Optional[str]) -> Iterator[str] + """ + Yields the platform tags for a macOS system. + + The `version` parameter is a two-item tuple specifying the macOS version to + generate platform tags for. The `arch` parameter is the CPU architecture to + generate platform tags for. Both parameters default to the appropriate value + for the current system. + """ + version_str, _, cpu_arch = platform.mac_ver() # type: ignore if version is None: - version = tuple(map(int, version_str.split(".")[:2])) + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + else: + version = version if arch is None: arch = _mac_arch(cpu_arch) - platforms = [] + else: + arch = arch for minor_version in range(version[1], -1, -1): compat_version = version[0], minor_version binary_formats = _mac_binary_formats(compat_version, arch) for binary_format in binary_formats: - platforms.append( - "macosx_{major}_{minor}_{binary_format}".format( - major=compat_version[0], - minor=compat_version[1], - binary_format=binary_format, - ) + yield "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, ) - return platforms # From PEP 513. def _is_manylinux_compatible(name, glibc_version): + # type: (str, GlibcVersion) -> bool # Check for presence of _manylinux module. try: - import _manylinux + import _manylinux # noqa return bool(getattr(_manylinux, name + "_compatible")) except (ImportError, AttributeError): @@ -276,14 +432,50 @@ def _is_manylinux_compatible(name, glibc_version): def _glibc_version_string(): + # type: () -> Optional[str] # Returns glibc version string, or None if not using glibc. - import ctypes + return _glibc_version_string_confstr() or _glibc_version_string_ctypes() + + +def _glibc_version_string_confstr(): + # type: () -> Optional[str] + """ + Primary implementation of glibc_version_string using os.confstr. + """ + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module. + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 + try: + # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". + version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821 + "CS_GNU_LIBC_VERSION" + ) + assert version_string is not None + _, version = version_string.split() # type: Tuple[str, str] + except (AssertionError, AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def _glibc_version_string_ctypes(): + # type: () -> Optional[str] + """ + Fallback implementation of glibc_version_string using ctypes. + """ + try: + import ctypes + except ImportError: + return None # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen # manpage says, "If filename is NULL, then the returned handle is for the # main program". This way we can let the linker do the work to figure out # which libc our process is actually using. - process_namespace = ctypes.CDLL(None) + # + # Note: typeshed is wrong here so we are ignoring this line. + process_namespace = ctypes.CDLL(None) # type: ignore try: gnu_get_libc_version = process_namespace.gnu_get_libc_version except AttributeError: @@ -293,7 +485,7 @@ def _glibc_version_string(): # Call gnu_get_libc_version, which returns a string like "2.5" gnu_get_libc_version.restype = ctypes.c_char_p - version_str = gnu_get_libc_version() + version_str = gnu_get_libc_version() # type: str # py2 / py3 compatibility: if not isinstance(version_str, str): version_str = version_str.decode("ascii") @@ -303,6 +495,7 @@ def _glibc_version_string(): # Separated out from have_compatible_glibc for easier unit testing. def _check_glibc_version(version_str, required_major, minimum_minor): + # type: (str, int, int) -> bool # Parse string and check against requested version. # # We use a regexp instead of str.split because we want to discard any @@ -324,81 +517,235 @@ def _check_glibc_version(version_str, required_major, minimum_minor): def _have_compatible_glibc(required_major, minimum_minor): + # type: (int, int) -> bool version_str = _glibc_version_string() if version_str is None: return False return _check_glibc_version(version_str, required_major, minimum_minor) +# Python does not provide platform information at sufficient granularity to +# identify the architecture of the running executable in some cases, so we +# determine it dynamically by reading the information from the running +# process. This only applies on Linux, which uses the ELF format. +class _ELFFileHeader(object): + # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header + class _InvalidELFFileHeader(ValueError): + """ + An invalid ELF file header was found. + """ + + ELF_MAGIC_NUMBER = 0x7F454C46 + ELFCLASS32 = 1 + ELFCLASS64 = 2 + ELFDATA2LSB = 1 + ELFDATA2MSB = 2 + EM_386 = 3 + EM_S390 = 22 + EM_ARM = 40 + EM_X86_64 = 62 + EF_ARM_ABIMASK = 0xFF000000 + EF_ARM_ABI_VER5 = 0x05000000 + EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + def __init__(self, file): + # type: (IO[bytes]) -> None + def unpack(fmt): + # type: (str) -> int + try: + (result,) = struct.unpack( + fmt, file.read(struct.calcsize(fmt)) + ) # type: (int, ) + except struct.error: + raise _ELFFileHeader._InvalidELFFileHeader() + return result + + self.e_ident_magic = unpack(">I") + if self.e_ident_magic != self.ELF_MAGIC_NUMBER: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_class = unpack("B") + if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_data = unpack("B") + if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: + raise _ELFFileHeader._InvalidELFFileHeader() + self.e_ident_version = unpack("B") + self.e_ident_osabi = unpack("B") + self.e_ident_abiversion = unpack("B") + self.e_ident_pad = file.read(7) + format_h = "H" + format_i = "I" + format_q = "Q" + format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q + self.e_type = unpack(format_h) + self.e_machine = unpack(format_h) + self.e_version = unpack(format_i) + self.e_entry = unpack(format_p) + self.e_phoff = unpack(format_p) + self.e_shoff = unpack(format_p) + self.e_flags = unpack(format_i) + self.e_ehsize = unpack(format_h) + self.e_phentsize = unpack(format_h) + self.e_phnum = unpack(format_h) + self.e_shentsize = unpack(format_h) + self.e_shnum = unpack(format_h) + self.e_shstrndx = unpack(format_h) + + +def _get_elf_header(): + # type: () -> Optional[_ELFFileHeader] + try: + with open(sys.executable, "rb") as f: + elf_header = _ELFFileHeader(f) + except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): + return None + return elf_header + + +def _is_linux_armhf(): + # type: () -> bool + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + elf_header = _get_elf_header() + if elf_header is None: + return False + result = elf_header.e_ident_class == elf_header.ELFCLASS32 + result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB + result &= elf_header.e_machine == elf_header.EM_ARM + result &= ( + elf_header.e_flags & elf_header.EF_ARM_ABIMASK + ) == elf_header.EF_ARM_ABI_VER5 + result &= ( + elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD + ) == elf_header.EF_ARM_ABI_FLOAT_HARD + return result + + +def _is_linux_i686(): + # type: () -> bool + elf_header = _get_elf_header() + if elf_header is None: + return False + result = elf_header.e_ident_class == elf_header.ELFCLASS32 + result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB + result &= elf_header.e_machine == elf_header.EM_386 + return result + + +def _have_compatible_manylinux_abi(arch): + # type: (str) -> bool + if arch == "armv7l": + return _is_linux_armhf() + if arch == "i686": + return _is_linux_i686() + return True + + def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): + # type: (bool) -> Iterator[str] linux = _normalize_string(distutils.util.get_platform()) - if linux == "linux_x86_64" and is_32bit: - linux = "linux_i686" - manylinux_support = ( - ("manylinux2014", (2, 17)), # CentOS 7 w/ glibc 2.17 (PEP 599) - ("manylinux2010", (2, 12)), # CentOS 6 w/ glibc 2.12 (PEP 571) - ("manylinux1", (2, 5)), # CentOS 5 w/ glibc 2.5 (PEP 513) - ) + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv7l" + manylinux_support = [] + _, arch = linux.split("_", 1) + if _have_compatible_manylinux_abi(arch): + if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}: + manylinux_support.append( + ("manylinux2014", (2, 17)) + ) # CentOS 7 w/ glibc 2.17 (PEP 599) + if arch in {"x86_64", "i686"}: + manylinux_support.append( + ("manylinux2010", (2, 12)) + ) # CentOS 6 w/ glibc 2.12 (PEP 571) + manylinux_support.append( + ("manylinux1", (2, 5)) + ) # CentOS 5 w/ glibc 2.5 (PEP 513) manylinux_support_iter = iter(manylinux_support) for name, glibc_version in manylinux_support_iter: if _is_manylinux_compatible(name, glibc_version): - platforms = [linux.replace("linux", name)] + yield linux.replace("linux", name) break - else: - platforms = [] # Support for a later manylinux implies support for an earlier version. - platforms += [linux.replace("linux", name) for name, _ in manylinux_support_iter] - platforms.append(linux) - return platforms + for name, _ in manylinux_support_iter: + yield linux.replace("linux", name) + yield linux def _generic_platforms(): - platform = _normalize_string(distutils.util.get_platform()) - return [platform] + # type: () -> Iterator[str] + yield _normalize_string(distutils.util.get_platform()) -def _interpreter_name(): - name = platform.python_implementation().lower() +def _platform_tags(): + # type: () -> Iterator[str] + """ + Provides the platform tags for this installation. + """ + if platform.system() == "Darwin": + return mac_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + else: + return _generic_platforms() + + +def interpreter_name(): + # type: () -> str + """ + Returns the name of the running interpreter. + """ + try: + name = sys.implementation.name # type: ignore + except AttributeError: # pragma: no cover + # Python 2.7 compatibility. + name = platform.python_implementation().lower() return INTERPRETER_SHORT_NAMES.get(name) or name -def _generic_interpreter(name, py_version): - version = sysconfig.get_config_var("py_version_nodot") - if not version: - version = "".join(map(str, py_version[:2])) - return "{name}{version}".format(name=name, version=version) +def interpreter_version(**kwargs): + # type: (bool) -> str + """ + Returns the version of the running interpreter. + """ + warn = _warn_keyword_parameter("interpreter_version", kwargs) + version = _get_config_var("py_version_nodot", warn=warn) + if version: + version = str(version) + else: + version = _version_nodot(sys.version_info[:2]) + return version + + +def _version_nodot(version): + # type: (PythonVersion) -> str + if any(v >= 10 for v in version): + sep = "_" + else: + sep = "" + return sep.join(map(str, version)) -def sys_tags(): +def sys_tags(**kwargs): + # type: (bool) -> Iterator[Tag] """ Returns the sequence of tag triples for the running interpreter. The order of the sequence corresponds to priority order for the interpreter, from most to least important. """ - py_version = sys.version_info[:2] - interpreter_name = _interpreter_name() - if platform.system() == "Darwin": - platforms = _mac_platforms() - elif platform.system() == "Linux": - platforms = _linux_platforms() - else: - platforms = _generic_platforms() + warn = _warn_keyword_parameter("sys_tags", kwargs) - if interpreter_name == "cp": - interpreter = _cpython_interpreter(py_version) - abis = _cpython_abis(py_version) - for tag in _cpython_tags(py_version, interpreter, abis, platforms): - yield tag - elif interpreter_name == "pp": - interpreter = _pypy_interpreter() - abi = _generic_abi() - for tag in _pypy_tags(py_version, interpreter, abi, platforms): + interp_name = interpreter_name() + if interp_name == "cp": + for tag in cpython_tags(warn=warn): yield tag else: - interpreter = _generic_interpreter(interpreter_name, py_version) - abi = _generic_abi() - for tag in _generic_tags(interpreter, py_version, abi, platforms): + for tag in generic_tags(): yield tag - for tag in _independent_tags(interpreter, py_version, platforms): + + for tag in compatible_tags(): yield tag diff --git a/setuptools/_vendor/packaging/utils.py b/setuptools/_vendor/packaging/utils.py index 88418786..19579c1a 100644 --- a/setuptools/_vendor/packaging/utils.py +++ b/setuptools/_vendor/packaging/utils.py @@ -5,28 +5,36 @@ from __future__ import absolute_import, division, print_function import re +from ._typing import TYPE_CHECKING, cast from .version import InvalidVersion, Version +if TYPE_CHECKING: # pragma: no cover + from typing import NewType, Union + + NormalizedName = NewType("NormalizedName", str) _canonicalize_regex = re.compile(r"[-_.]+") def canonicalize_name(name): + # type: (str) -> NormalizedName # This is taken from PEP 503. - return _canonicalize_regex.sub("-", name).lower() + value = _canonicalize_regex.sub("-", name).lower() + return cast("NormalizedName", value) -def canonicalize_version(version): +def canonicalize_version(_version): + # type: (str) -> Union[Version, str] """ - This is very similar to Version.__str__, but has one subtle differences + This is very similar to Version.__str__, but has one subtle difference with the way it handles the release segment. """ try: - version = Version(version) + version = Version(_version) except InvalidVersion: # Legacy versions cannot be normalized - return version + return _version parts = [] diff --git a/setuptools/_vendor/packaging/version.py b/setuptools/_vendor/packaging/version.py index 95157a1f..00371e86 100644 --- a/setuptools/_vendor/packaging/version.py +++ b/setuptools/_vendor/packaging/version.py @@ -7,8 +7,35 @@ import collections import itertools import re -from ._structures import Infinity - +from ._structures import Infinity, NegativeInfinity +from ._typing import TYPE_CHECKING + +if TYPE_CHECKING: # pragma: no cover + from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union + + from ._structures import InfinityType, NegativeInfinityType + + InfiniteTypes = Union[InfinityType, NegativeInfinityType] + PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] + SubLocalType = Union[InfiniteTypes, int, str] + LocalType = Union[ + NegativeInfinityType, + Tuple[ + Union[ + SubLocalType, + Tuple[SubLocalType, str], + Tuple[NegativeInfinityType, SubLocalType], + ], + ..., + ], + ] + CmpKey = Tuple[ + int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType + ] + LegacyCmpKey = Tuple[int, Tuple[str, ...]] + VersionComparisonMethod = Callable[ + [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool + ] __all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] @@ -19,6 +46,7 @@ _Version = collections.namedtuple( def parse(version): + # type: (str) -> Union[LegacyVersion, Version] """ Parse the given version string and return either a :class:`Version` object or a :class:`LegacyVersion` object depending on if the given version is @@ -37,28 +65,38 @@ class InvalidVersion(ValueError): class _BaseVersion(object): + _key = None # type: Union[CmpKey, LegacyCmpKey] + def __hash__(self): + # type: () -> int return hash(self._key) def __lt__(self, other): + # type: (_BaseVersion) -> bool return self._compare(other, lambda s, o: s < o) def __le__(self, other): + # type: (_BaseVersion) -> bool return self._compare(other, lambda s, o: s <= o) def __eq__(self, other): + # type: (object) -> bool return self._compare(other, lambda s, o: s == o) def __ge__(self, other): + # type: (_BaseVersion) -> bool return self._compare(other, lambda s, o: s >= o) def __gt__(self, other): + # type: (_BaseVersion) -> bool return self._compare(other, lambda s, o: s > o) def __ne__(self, other): + # type: (object) -> bool return self._compare(other, lambda s, o: s != o) def _compare(self, other, method): + # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented] if not isinstance(other, _BaseVersion): return NotImplemented @@ -67,57 +105,71 @@ class _BaseVersion(object): class LegacyVersion(_BaseVersion): def __init__(self, version): + # type: (str) -> None self._version = str(version) self._key = _legacy_cmpkey(self._version) def __str__(self): + # type: () -> str return self._version def __repr__(self): + # type: () -> str return "".format(repr(str(self))) @property def public(self): + # type: () -> str return self._version @property def base_version(self): + # type: () -> str return self._version @property def epoch(self): + # type: () -> int return -1 @property def release(self): + # type: () -> None return None @property def pre(self): + # type: () -> None return None @property def post(self): + # type: () -> None return None @property def dev(self): + # type: () -> None return None @property def local(self): + # type: () -> None return None @property def is_prerelease(self): + # type: () -> bool return False @property def is_postrelease(self): + # type: () -> bool return False @property def is_devrelease(self): + # type: () -> bool return False @@ -133,6 +185,7 @@ _legacy_version_replacement_map = { def _parse_version_parts(s): + # type: (str) -> Iterator[str] for part in _legacy_version_component_re.split(s): part = _legacy_version_replacement_map.get(part, part) @@ -150,6 +203,8 @@ def _parse_version_parts(s): def _legacy_cmpkey(version): + # type: (str) -> LegacyCmpKey + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch # greater than or equal to 0. This will effectively put the LegacyVersion, # which uses the defacto standard originally implemented by setuptools, @@ -158,7 +213,7 @@ def _legacy_cmpkey(version): # This scheme is taken from pkg_resources.parse_version setuptools prior to # it's adoption of the packaging library. - parts = [] + parts = [] # type: List[str] for part in _parse_version_parts(version.lower()): if part.startswith("*"): # remove "-" before a prerelease tag @@ -171,9 +226,8 @@ def _legacy_cmpkey(version): parts.pop() parts.append(part) - parts = tuple(parts) - return epoch, parts + return epoch, tuple(parts) # Deliberately not anchored to the start and end of the string, to make it @@ -215,6 +269,8 @@ class Version(_BaseVersion): _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) def __init__(self, version): + # type: (str) -> None + # Validate the version and parse it into pieces match = self._regex.search(version) if not match: @@ -243,9 +299,11 @@ class Version(_BaseVersion): ) def __repr__(self): + # type: () -> str return "".format(repr(str(self))) def __str__(self): + # type: () -> str parts = [] # Epoch @@ -275,26 +333,35 @@ class Version(_BaseVersion): @property def epoch(self): - return self._version.epoch + # type: () -> int + _epoch = self._version.epoch # type: int + return _epoch @property def release(self): - return self._version.release + # type: () -> Tuple[int, ...] + _release = self._version.release # type: Tuple[int, ...] + return _release @property def pre(self): - return self._version.pre + # type: () -> Optional[Tuple[str, int]] + _pre = self._version.pre # type: Optional[Tuple[str, int]] + return _pre @property def post(self): + # type: () -> Optional[Tuple[str, int]] return self._version.post[1] if self._version.post else None @property def dev(self): + # type: () -> Optional[Tuple[str, int]] return self._version.dev[1] if self._version.dev else None @property def local(self): + # type: () -> Optional[str] if self._version.local: return ".".join(str(x) for x in self._version.local) else: @@ -302,10 +369,12 @@ class Version(_BaseVersion): @property def public(self): + # type: () -> str return str(self).split("+", 1)[0] @property def base_version(self): + # type: () -> str parts = [] # Epoch @@ -319,18 +388,41 @@ class Version(_BaseVersion): @property def is_prerelease(self): + # type: () -> bool return self.dev is not None or self.pre is not None @property def is_postrelease(self): + # type: () -> bool return self.post is not None @property def is_devrelease(self): + # type: () -> bool return self.dev is not None + @property + def major(self): + # type: () -> int + return self.release[0] if len(self.release) >= 1 else 0 + + @property + def minor(self): + # type: () -> int + return self.release[1] if len(self.release) >= 2 else 0 + + @property + def micro(self): + # type: () -> int + return self.release[2] if len(self.release) >= 3 else 0 + + +def _parse_letter_version( + letter, # type: str + number, # type: Union[str, bytes, SupportsInt] +): + # type: (...) -> Optional[Tuple[str, int]] -def _parse_letter_version(letter, number): if letter: # We consider there to be an implicit 0 in a pre-release if there is # not a numeral associated with it. @@ -360,11 +452,14 @@ def _parse_letter_version(letter, number): return letter, int(number) + return None + _local_version_separators = re.compile(r"[\._-]") def _parse_local_version(local): + # type: (str) -> Optional[LocalType] """ Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve"). """ @@ -373,15 +468,25 @@ def _parse_local_version(local): part.lower() if not part.isdigit() else int(part) for part in _local_version_separators.split(local) ) + return None + +def _cmpkey( + epoch, # type: int + release, # type: Tuple[int, ...] + pre, # type: Optional[Tuple[str, int]] + post, # type: Optional[Tuple[str, int]] + dev, # type: Optional[Tuple[str, int]] + local, # type: Optional[Tuple[SubLocalType]] +): + # type: (...) -> CmpKey -def _cmpkey(epoch, release, pre, post, dev, local): # When we compare a release version, we want to compare it with all of the # trailing zeros removed. So we'll use a reverse the list, drop all the now # leading zeros until we come to something non zero, then take the rest # re-reverse it back into the correct order and make it a tuple and use # that for our sorting key. - release = tuple( + _release = tuple( reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))) ) @@ -390,23 +495,31 @@ def _cmpkey(epoch, release, pre, post, dev, local): # if there is not a pre or a post segment. If we have one of those then # the normal sorting rules will handle this case correctly. if pre is None and post is None and dev is not None: - pre = -Infinity + _pre = NegativeInfinity # type: PrePostDevType # Versions without a pre-release (except as noted above) should sort after # those with one. elif pre is None: - pre = Infinity + _pre = Infinity + else: + _pre = pre # Versions without a post segment should sort before those with one. if post is None: - post = -Infinity + _post = NegativeInfinity # type: PrePostDevType + + else: + _post = post # Versions without a development segment should sort after those with one. if dev is None: - dev = Infinity + _dev = Infinity # type: PrePostDevType + + else: + _dev = dev if local is None: # Versions without a local segment should sort before those with one. - local = -Infinity + _local = NegativeInfinity # type: LocalType else: # Versions with a local segment need that segment parsed to implement # the sorting rules in PEP440. @@ -415,6 +528,8 @@ def _cmpkey(epoch, release, pre, post, dev, local): # - Numeric segments sort numerically # - Shorter versions sort before longer versions when the prefixes # match exactly - local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local) + _local = tuple( + (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local + ) - return epoch, release, pre, post, dev, local + return epoch, _release, _pre, _post, _dev, _local diff --git a/setuptools/_vendor/vendored.txt b/setuptools/_vendor/vendored.txt index 65183d9a..288d0770 100644 --- a/setuptools/_vendor/vendored.txt +++ b/setuptools/_vendor/vendored.txt @@ -1,4 +1,4 @@ -packaging==19.2 +packaging==20.4 pyparsing==2.2.1 six==1.10.0 ordered-set==3.1.1 -- cgit v1.2.1 From 325609811b8db4d4ed161a961cae0ee4840317cc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 12 Aug 2020 19:58:27 -0400 Subject: Update changelog. --- changelog.d/2310.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2310.change.rst diff --git a/changelog.d/2310.change.rst b/changelog.d/2310.change.rst new file mode 100644 index 00000000..a7f1cc53 --- /dev/null +++ b/changelog.d/2310.change.rst @@ -0,0 +1 @@ +Updated vendored packaging version to 20.4. -- cgit v1.2.1 From a42d30089c42605d0f3191e4ccc19e1cfd1e0ccd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Aug 2020 09:40:52 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.3.2=20=E2=86=92=2049.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2310.change.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2310.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 5e0d7fd7..547dc886 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.3.2 +current_version = 49.4.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 0d2c96c3..ac071459 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v49.4.0 +------- + +* #2310: Updated vendored packaging version to 20.4. + + v49.3.2 ------- diff --git a/changelog.d/2310.change.rst b/changelog.d/2310.change.rst deleted file mode 100644 index a7f1cc53..00000000 --- a/changelog.d/2310.change.rst +++ /dev/null @@ -1 +0,0 @@ -Updated vendored packaging version to 20.4. diff --git a/setup.cfg b/setup.cfg index 5d4a084c..76d99d44 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.3.2 +version = 49.4.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From a7f6ad46b07d51fce91e74b3777d70cbd4168f0f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Aug 2020 18:59:34 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.4.0=20=E2=86=92=2049.5.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 8 ++++++++ changelog.d/2306.change.rst | 3 --- setup.cfg | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) delete mode 100644 changelog.d/2306.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 547dc886..7ecebcd9 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.4.0 +current_version = 49.5.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index ac071459..534e15a8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,11 @@ +v49.5.0 +------- + +* #2306: When running as a PEP 517 backend, setuptools does not try to install + ``setup_requires`` itself. They are reported as build requirements for the + frontend to install. + + v49.4.0 ------- diff --git a/changelog.d/2306.change.rst b/changelog.d/2306.change.rst deleted file mode 100644 index 1046eae3..00000000 --- a/changelog.d/2306.change.rst +++ /dev/null @@ -1,3 +0,0 @@ -When running as a PEP 517 backend, setuptools does not try to install -``setup_requires`` itself. They are reported as build requirements for the -frontend to install. diff --git a/setup.cfg b/setup.cfg index 76d99d44..7618ab11 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.4.0 +version = 49.5.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 4837f218ea05a304880f8448e27fe0affad3a1a5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Aug 2020 21:49:25 -0400 Subject: Update tests to reflect new expectation. --- setuptools/tests/test_distutils_adoption.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py index daccc473..8edd3f9b 100644 --- a/setuptools/tests/test_distutils_adoption.py +++ b/setuptools/tests/test_distutils_adoption.py @@ -48,15 +48,15 @@ def test_distutils_stdlib(venv): """ Ensure stdlib distutils is used when appropriate. """ - assert venv.name not in find_distutils(venv, env=dict()).split(os.sep) + env = dict(SETUPTOOLS_USE_DISTUTILS='stdlib') + assert venv.name not in find_distutils(venv, env=env).split(os.sep) def test_distutils_local_with_setuptools(venv): """ Ensure local distutils is used when appropriate. """ - env = dict(SETUPTOOLS_USE_DISTUTILS='local') - loc = find_distutils(venv, imports='setuptools, distutils', env=env) + loc = find_distutils(venv, imports='setuptools, distutils', env=dict()) assert venv.name in loc.split(os.sep) @@ -66,5 +66,4 @@ def test_distutils_local(venv): Even without importing, the setuptools-local copy of distutils is preferred. """ - env = dict(SETUPTOOLS_USE_DISTUTILS='local') - assert venv.name in find_distutils(venv, env=env).split(os.sep) + assert venv.name in find_distutils(venv, env=dict()).split(os.sep) -- cgit v1.2.1 From 6f205904ca63a91946b862068278a51dddb36cf4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Aug 2020 21:56:40 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.5.0=20=E2=86=92=2049.6.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2129.change.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2129.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 7ecebcd9..4bbe60c4 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.5.0 +current_version = 49.6.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 534e15a8..2d49707b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v49.6.0 +------- + +* #2129: In pkg_resources, no longer detect any pathname ending in .egg as a Python egg. Now the path must be an unpacked egg or a zip file. + + v49.5.0 ------- diff --git a/changelog.d/2129.change.rst b/changelog.d/2129.change.rst deleted file mode 100644 index b7d38862..00000000 --- a/changelog.d/2129.change.rst +++ /dev/null @@ -1 +0,0 @@ -In pkg_resources, no longer detect any pathname ending in .egg as a Python egg. Now the path must be an unpacked egg or a zip file. diff --git a/setup.cfg b/setup.cfg index 7618ab11..3c8f8671 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.5.0 +version = 49.6.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From be7a0a31a116f6df9bef616ef1adef72b9d0d48d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Aug 2020 19:32:18 -0400 Subject: Bypass .pth loader when distutils is loaded from pip. Workaround for pypa/pip#8761. --- _distutils_hack/__init__.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 814ee97e..2d358c37 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -3,6 +3,7 @@ import os import re import importlib import warnings +import inspect is_pypy = '__pypy__' in sys.builtin_module_names @@ -66,7 +67,7 @@ def do_override(): class DistutilsMetaFinder: def find_spec(self, fullname, path, target=None): - if path is not None or fullname != "distutils": + if path is not None or fullname != "distutils" or self._bypass(): return None return self.get_distutils_spec() @@ -84,6 +85,16 @@ class DistutilsMetaFinder: return importlib.util.spec_from_loader('distutils', DistutilsLoader()) + def _bypass(self): + """ + Suppress the import of distutils from setuptools when running under pip. + See pypa/pip#8761 for rationale. + """ + return any( + level.frame.f_globals['__name__'].startswith('pip.') + for level in inspect.stack(context=False) + ) + DISTUTILS_FINDER = DistutilsMetaFinder() -- cgit v1.2.1 From 77eeb77f48484a6e227275a9a8cc7d4c322596f7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 15 Aug 2020 14:39:25 -0400 Subject: When pip is imported, unload any existing distutils and disable the DistutilsMetaFinder. --- _distutils_hack/__init__.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 2d358c37..bdb365d5 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -3,7 +3,6 @@ import os import re import importlib import warnings -import inspect is_pypy = '__pypy__' in sys.builtin_module_names @@ -67,7 +66,9 @@ def do_override(): class DistutilsMetaFinder: def find_spec(self, fullname, path, target=None): - if path is not None or fullname != "distutils" or self._bypass(): + self._disable_for_pip(fullname, path) + + if path is not None or fullname != "distutils": return None return self.get_distutils_spec() @@ -85,15 +86,17 @@ class DistutilsMetaFinder: return importlib.util.spec_from_loader('distutils', DistutilsLoader()) - def _bypass(self): + def _disable_for_pip(self, fullname, path): """ - Suppress the import of distutils from setuptools when running under pip. + Ensure stdlib distutils when running under pip. See pypa/pip#8761 for rationale. """ - return any( - level.frame.f_globals['__name__'].startswith('pip.') - for level in inspect.stack(context=False) - ) + if path is not None or fullname != "pip": + return + + # pip is being imported the first time. + clear_distutils() + self.get_distutils_spec = lambda: None DISTUTILS_FINDER = DistutilsMetaFinder() -- cgit v1.2.1 From 4eb5b32f8d8bb1e20907028a516346e2b1901391 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 15 Aug 2020 14:42:03 -0400 Subject: Replace hard-coded tox working dir with the environment variable, honoring non-standard working directories. --- tools/tox_pip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tox_pip.py b/tools/tox_pip.py index 9fe4f905..9e1a1e4a 100644 --- a/tools/tox_pip.py +++ b/tools/tox_pip.py @@ -12,7 +12,7 @@ def remove_setuptools(): cmd = [sys.executable, '-m', 'pip', 'uninstall', '-y', 'setuptools'] # set cwd to something other than '.' to avoid detecting # '.' as the installed package. - subprocess.check_call(cmd, cwd='.tox') + subprocess.check_call(cmd, cwd=os.environ['TOX_WORK_DIR']) def bootstrap(): -- cgit v1.2.1 From 3d404fd3268b0fb7d84916779ca2e282f4586396 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 15 Aug 2020 14:59:37 -0400 Subject: Refactor to use lookups and consolidate behaviors. --- _distutils_hack/__init__.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index bdb365d5..074bd5e9 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -66,14 +66,14 @@ def do_override(): class DistutilsMetaFinder: def find_spec(self, fullname, path, target=None): - self._disable_for_pip(fullname, path) - - if path is not None or fullname != "distutils": - return None + if path is not None: + return - return self.get_distutils_spec() + method_name = 'spec_for_{fullname}'.format(**locals()) + method = getattr(self, method_name, lambda: None) + return method() - def get_distutils_spec(self): + def spec_for_distutils(self): import importlib.util class DistutilsLoader(importlib.util.abc.Loader): @@ -86,17 +86,13 @@ class DistutilsMetaFinder: return importlib.util.spec_from_loader('distutils', DistutilsLoader()) - def _disable_for_pip(self, fullname, path): + def spec_for_pip(self): """ Ensure stdlib distutils when running under pip. See pypa/pip#8761 for rationale. """ - if path is not None or fullname != "pip": - return - - # pip is being imported the first time. clear_distutils() - self.get_distutils_spec = lambda: None + self.spec_for_distutils = lambda: None DISTUTILS_FINDER = DistutilsMetaFinder() -- cgit v1.2.1 From fb7ab81a3d080422687bad71f9ae9d36eeefbee2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 16 Aug 2020 00:29:24 -0400 Subject: Remove Python 2 compatibility --- bootstrap.py | 2 - pkg_resources/__init__.py | 39 ++-------------- pkg_resources/tests/test_pkg_resources.py | 38 ++++----------- pkg_resources/tests/test_resources.py | 4 -- pkg_resources/tests/test_working_set.py | 2 - setup.cfg | 1 - setuptools/__init__.py | 20 ++------ setuptools/build_meta.py | 21 ++------- setuptools/command/alias.py | 2 - setuptools/command/bdist_egg.py | 20 ++------ setuptools/command/build_ext.py | 16 +------ setuptools/command/build_py.py | 8 +--- setuptools/command/develop.py | 6 +-- setuptools/command/easy_install.py | 55 +++++++--------------- setuptools/command/egg_info.py | 12 ++--- setuptools/command/py36compat.py | 2 - setuptools/command/rotate.py | 4 +- setuptools/command/sdist.py | 46 ++++-------------- setuptools/command/setopt.py | 3 +- setuptools/command/test.py | 10 +--- setuptools/command/upload_docs.py | 16 +++---- setuptools/config.py | 12 +---- setuptools/depends.py | 13 +++--- setuptools/dist.py | 48 +++++-------------- setuptools/extension.py | 2 - setuptools/installer.py | 8 ++-- setuptools/lib2to3_ex.py | 3 -- setuptools/monkey.py | 4 +- setuptools/msvc.py | 6 +-- setuptools/namespaces.py | 4 -- setuptools/package_index.py | 33 +++++++------ setuptools/py27compat.py | 60 ------------------------ setuptools/py31compat.py | 32 ------------- setuptools/py33compat.py | 59 ----------------------- setuptools/sandbox.py | 15 ++---- setuptools/ssl_support.py | 5 +- setuptools/tests/__init__.py | 9 +--- setuptools/tests/contexts.py | 6 +-- setuptools/tests/namespaces.py | 2 - setuptools/tests/server.py | 21 ++++----- setuptools/tests/test_archive_util.py | 6 +-- setuptools/tests/test_build_ext.py | 4 +- setuptools/tests/test_build_meta.py | 20 +------- setuptools/tests/test_config.py | 24 +--------- setuptools/tests/test_develop.py | 7 +-- setuptools/tests/test_dist.py | 20 +++----- setuptools/tests/test_dist_info.py | 4 -- setuptools/tests/test_easy_install.py | 8 ---- setuptools/tests/test_egg_info.py | 52 +-------------------- setuptools/tests/test_extern.py | 2 - setuptools/tests/test_find_packages.py | 11 +---- setuptools/tests/test_integration.py | 2 +- setuptools/tests/test_manifest.py | 6 +-- setuptools/tests/test_msvc14.py | 2 - setuptools/tests/test_namespaces.py | 2 - setuptools/tests/test_packageindex.py | 11 ++--- setuptools/tests/test_sdist.py | 77 ++++++++++--------------------- setuptools/tests/test_setopt.py | 10 +--- setuptools/tests/test_setuptools.py | 3 +- setuptools/tests/test_test.py | 5 -- setuptools/tests/test_virtualenv.py | 11 ----- setuptools/tests/test_wheel.py | 2 - setuptools/tests/test_windows_wrappers.py | 2 - setuptools/tests/text.py | 5 -- setuptools/tests/textwrap.py | 2 - setuptools/unicode_utils.py | 6 +-- setuptools/wheel.py | 6 +-- tools/tox_pip.py | 12 ----- 68 files changed, 185 insertions(+), 806 deletions(-) delete mode 100644 setuptools/py27compat.py delete mode 100644 setuptools/py31compat.py delete mode 100644 setuptools/py33compat.py diff --git a/bootstrap.py b/bootstrap.py index 8fa9e4b5..118671f6 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -5,8 +5,6 @@ environment by creating a minimal egg-info directory and then invoking the egg-info command to flesh out the egg-info directory. """ -from __future__ import unicode_literals - import os import sys import textwrap diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index b585e850..737f4d5f 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1,4 +1,3 @@ -# coding: utf-8 """ Package resource API -------------------- @@ -15,8 +14,6 @@ The package resource API is designed to work with normal filesystem packages, method. """ -from __future__ import absolute_import - import sys import os import io @@ -54,9 +51,6 @@ try: except NameError: FileExistsError = OSError -from pkg_resources.extern import six -from pkg_resources.extern.six.moves import map, filter - # capture these to bypass sandboxing from os import utime try: @@ -83,18 +77,9 @@ __import__('pkg_resources.extern.packaging.specifiers') __import__('pkg_resources.extern.packaging.requirements') __import__('pkg_resources.extern.packaging.markers') - -__metaclass__ = type - - -if (3, 0) < sys.version_info < (3, 5): +if sys.version_info < (3, 5): raise RuntimeError("Python 3.5 or later is required") -if six.PY2: - # Those builtin exceptions are only defined in Python 3 - PermissionError = None - NotADirectoryError = None - # declare some globals that will be defined later to # satisfy the linters. require = None @@ -474,7 +459,7 @@ run_main = run_script def get_distribution(dist): """Return a current distribution object for a Requirement or string""" - if isinstance(dist, six.string_types): + if isinstance(dist, str): dist = Requirement.parse(dist) if isinstance(dist, Requirement): dist = get_provider(dist) @@ -1418,8 +1403,6 @@ class NullProvider: return "" path = self._get_metadata_path(name) value = self._get(path) - if six.PY2: - return value try: return value.decode('utf-8') except UnicodeDecodeError as exc: @@ -1910,8 +1893,7 @@ class FileMetadata(EmptyProvider): return metadata def _warn_on_replacement(self, metadata): - # Python 2.7 compat for: replacement_char = '�' - replacement_char = b'\xef\xbf\xbd'.decode('utf-8') + replacement_char = '�' if replacement_char in metadata: tmpl = "{self.path} could not be properly decoded in UTF-8" msg = tmpl.format(**locals()) @@ -2109,8 +2091,6 @@ class NoDists: """ def __bool__(self): return False - if six.PY2: - __nonzero__ = __bool__ def __call__(self, fullpath): return iter(()) @@ -2127,12 +2107,7 @@ def safe_listdir(path): except OSError as e: # Ignore the directory if does not exist, not a directory or # permission denied - ignorable = ( - e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT) - # Python 2 on Windows needs to be handled this way :( - or getattr(e, "winerror", None) == 267 - ) - if not ignorable: + if e.errno not in (errno.ENOTDIR, errno.EACCES, errno.ENOENT): raise return () @@ -2406,7 +2381,7 @@ def _set_parent_ns(packageName): def yield_lines(strs): """Yield non-empty/non-comment lines of a string or sequence""" - if isinstance(strs, six.string_types): + if isinstance(strs, str): for s in strs.splitlines(): s = s.strip() # skip blank lines/comments @@ -2844,10 +2819,6 @@ class Distribution: ) ) - if not hasattr(object, '__dir__'): - # python 2.7 not supported - del __dir__ - @classmethod def from_filename(cls, filename, metadata=None, **kw): return cls.from_location( diff --git a/pkg_resources/tests/test_pkg_resources.py b/pkg_resources/tests/test_pkg_resources.py index 189a8668..6518820e 100644 --- a/pkg_resources/tests/test_pkg_resources.py +++ b/pkg_resources/tests/test_pkg_resources.py @@ -1,6 +1,3 @@ -# coding: utf-8 -from __future__ import unicode_literals - import sys import tempfile import os @@ -20,16 +17,11 @@ except ImportError: from pkg_resources import ( DistInfoDistribution, Distribution, EggInfoDistribution, ) -from setuptools.extern import six -from pkg_resources.extern.six.moves import map -from pkg_resources.extern.six import text_type, string_types import pytest import pkg_resources -__metaclass__ = type - def timestamp(dt): """ @@ -42,7 +34,7 @@ def timestamp(dt): return time.mktime(dt.timetuple()) -class EggRemover(text_type): +class EggRemover(str): def __call__(self): if self in sys.path: sys.path.remove(self) @@ -143,7 +135,7 @@ class TestResourceManager: path = mgr.get_cache_path('foo') type_ = str(type(path)) message = "Unexpected type from get_cache_path: " + type_ - assert isinstance(path, string_types), message + assert isinstance(path, str), message def test_get_cache_path_race(self, tmpdir): # Patch to os.path.isdir to create a race condition @@ -225,13 +217,6 @@ def test_get_metadata__bad_utf8(tmpdir): metadata = 'née'.encode('iso-8859-1') dist = make_test_distribution(metadata_path, metadata=metadata) - if six.PY2: - # In Python 2, get_metadata() doesn't do any decoding. - actual = dist.get_metadata(filename) - assert actual == metadata - return - - # Otherwise, we are in the Python 3 case. with pytest.raises(UnicodeDecodeError) as excinfo: dist.get_metadata(filename) @@ -247,25 +232,18 @@ def test_get_metadata__bad_utf8(tmpdir): assert actual.endswith(metadata_path), 'actual: {}'.format(actual) -# TODO: remove this in favor of Path.touch() when Python 2 is dropped. -def touch_file(path): - """ - Create an empty file. - """ - with open(path, 'w'): - pass - - def make_distribution_no_version(tmpdir, basename): """ Create a distribution directory with no file containing the version. """ - # Convert the LocalPath object to a string before joining. - dist_dir = os.path.join(str(tmpdir), basename) - os.mkdir(dist_dir) + dist_dir = tmpdir / basename + dist_dir.ensure_dir() # Make the directory non-empty so distributions_from_metadata() # will detect it and yield it. - touch_file(os.path.join(dist_dir, 'temp.txt')) + dist_dir.join('temp.txt').ensure() + + if sys.version_info < (3, 6): + dist_dir = str(dist_dir) dists = list(pkg_resources.distributions_from_metadata(dist_dir)) assert len(dists) == 1 diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index ed7cdfcc..b08bb293 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -1,13 +1,9 @@ -from __future__ import unicode_literals - import os import sys import string import platform import itertools -from pkg_resources.extern.six.moves import map - import pytest from pkg_resources.extern import packaging diff --git a/pkg_resources/tests/test_working_set.py b/pkg_resources/tests/test_working_set.py index 7a759bbb..db13c714 100644 --- a/pkg_resources/tests/test_working_set.py +++ b/pkg_resources/tests/test_working_set.py @@ -9,8 +9,6 @@ import pkg_resources from .test_resources import Metadata -__metaclass__ = type - def strip_comments(s): return '\n'.join( diff --git a/setup.cfg b/setup.cfg index 3c8f8671..b058fa86 100644 --- a/setup.cfg +++ b/setup.cfg @@ -70,7 +70,6 @@ tests = coverage>=4.5.1 pytest-cov>=2.5.1 paver; python_version>="3.6" - futures; python_version=="2.7" pip>=19.1 # For proper file:// URLs support. jaraco.envs diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 99094230..4d9b8357 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -13,27 +13,19 @@ from distutils.util import convert_path from ._deprecation_warning import SetuptoolsDeprecationWarning -from setuptools.extern.six import PY3, string_types -from setuptools.extern.six.moves import filter, map - import setuptools.version from setuptools.extension import Extension from setuptools.dist import Distribution from setuptools.depends import Require from . import monkey -__metaclass__ = type - __all__ = [ 'setup', 'Distribution', 'Command', 'Extension', 'Require', 'SetuptoolsDeprecationWarning', - 'find_packages' + 'find_packages', 'find_namespace_packages', ] -if PY3: - __all__.append('find_namespace_packages') - __version__ = setuptools.version.__version__ bootstrap_install_from = None @@ -122,9 +114,7 @@ class PEP420PackageFinder(PackageFinder): find_packages = PackageFinder.find - -if PY3: - find_namespace_packages = PEP420PackageFinder.find +find_namespace_packages = PEP420PackageFinder.find def _install_setup_requires(attrs): @@ -187,7 +177,7 @@ class Command(_Command): if val is None: setattr(self, option, default) return default - elif not isinstance(val, string_types): + elif not isinstance(val, str): raise DistutilsOptionError("'%s' must be a %s (got `%s`)" % (option, what, val)) return val @@ -201,11 +191,11 @@ class Command(_Command): val = getattr(self, option) if val is None: return - elif isinstance(val, string_types): + elif isinstance(val, str): setattr(self, option, re.split(r',\s*|\s+', val)) else: if isinstance(val, list): - ok = all(isinstance(v, string_types) for v in val) + ok = all(isinstance(v, str) for v in val) else: ok = False if not ok: diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py index 37132187..b9e8a2b3 100644 --- a/setuptools/build_meta.py +++ b/setuptools/build_meta.py @@ -32,10 +32,10 @@ import sys import tokenize import shutil import contextlib +import tempfile import setuptools import distutils -from setuptools.py31compat import TemporaryDirectory from pkg_resources import parse_requirements @@ -91,19 +91,6 @@ def no_install_setup_requires(): setuptools._install_setup_requires = orig -def _to_str(s): - """ - Convert a filename to a string (on Python 2, explicitly - a byte string, not Unicode) as distutils checks for the - exact type str. - """ - if sys.version_info[0] == 2 and not isinstance(s, str): - # Assume it's Unicode, as that's what the PEP says - # should be provided. - return s.encode(sys.getfilesystemencoding()) - return s - - def _get_immediate_subdirectories(a_dir): return [name for name in os.listdir(a_dir) if os.path.isdir(os.path.join(a_dir, name))] @@ -168,8 +155,8 @@ class _BuildMetaBackend(object): def prepare_metadata_for_build_wheel(self, metadata_directory, config_settings=None): - sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', - _to_str(metadata_directory)] + sys.argv = sys.argv[:1] + [ + 'dist_info', '--egg-base', metadata_directory] with no_install_setup_requires(): self.run_setup() @@ -207,7 +194,7 @@ class _BuildMetaBackend(object): # Build in a temporary directory, then copy to the target. os.makedirs(result_directory, exist_ok=True) - with TemporaryDirectory(dir=result_directory) as tmp_dist_dir: + with tempfile.TemporaryDirectory(dir=result_directory) as tmp_dist_dir: sys.argv = (sys.argv[:1] + setup_command + ['--dist-dir', tmp_dist_dir] + config_settings["--global-option"]) diff --git a/setuptools/command/alias.py b/setuptools/command/alias.py index 4532b1cc..452a9244 100644 --- a/setuptools/command/alias.py +++ b/setuptools/command/alias.py @@ -1,7 +1,5 @@ from distutils.errors import DistutilsOptionError -from setuptools.extern.six.moves import map - from setuptools.command.setopt import edit_config, option_base, config_file diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 4be15457..a88efb45 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -13,24 +13,16 @@ import textwrap import marshal import warnings -from setuptools.extern import six - from pkg_resources import get_build_platform, Distribution, ensure_directory from pkg_resources import EntryPoint from setuptools.extension import Library from setuptools import Command, SetuptoolsDeprecationWarning -try: - # Python 2.7 or >=3.2 - from sysconfig import get_path, get_python_version +from sysconfig import get_path, get_python_version - def _get_purelib(): - return get_path("purelib") -except ImportError: - from distutils.sysconfig import get_python_lib, get_python_version - def _get_purelib(): - return get_python_lib(False) +def _get_purelib(): + return get_path("purelib") def strip_module(filename): @@ -420,9 +412,7 @@ def scan_module(egg_dir, base, name, stubs): return True # Extension module pkg = base[len(egg_dir) + 1:].replace(os.sep, '.') module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0] - if six.PY2: - skip = 8 # skip magic & date - elif sys.version_info < (3, 7): + if sys.version_info < (3, 7): skip = 12 # skip magic & date & file size else: skip = 16 # skip magic & reserved? & date & file size @@ -453,7 +443,7 @@ def iter_symbols(code): for name in code.co_names: yield name for const in code.co_consts: - if isinstance(const, six.string_types): + if isinstance(const, str): yield const elif isinstance(const, CodeType): for name in iter_symbols(const): diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 89a0e328..03a72b4f 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -1,6 +1,7 @@ import os import sys import itertools +from importlib.machinery import EXTENSION_SUFFIXES from distutils.command.build_ext import build_ext as _du_build_ext from distutils.file_util import copy_file from distutils.ccompiler import new_compiler @@ -9,15 +10,6 @@ from distutils.errors import DistutilsError from distutils import log from setuptools.extension import Library -from setuptools.extern import six - -if six.PY2: - import imp - - EXTENSION_SUFFIXES = [ - s for s, _, tp in imp.get_suffixes() if tp == imp.C_EXTENSION] -else: - from importlib.machinery import EXTENSION_SUFFIXES try: # Attempt to use Cython for building extensions, if available @@ -115,11 +107,7 @@ class build_ext(_build_ext): filename = _build_ext.get_ext_filename(self, fullname) if fullname in self.ext_map: ext = self.ext_map[fullname] - use_abi3 = ( - not six.PY2 - and getattr(ext, 'py_limited_api') - and get_abi3_suffix() - ) + use_abi3 = getattr(ext, 'py_limited_api') and get_abi3_suffix() if use_abi3: so_ext = get_config_var('EXT_SUFFIX') filename = filename[:-len(so_ext)] diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index 9d0288a5..4709679b 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -9,9 +9,6 @@ import distutils.errors import itertools import stat -from setuptools.extern import six -from setuptools.extern.six.moves import map, filter, filterfalse - try: from setuptools.lib2to3_ex import Mixin2to3 except ImportError: @@ -73,9 +70,6 @@ class build_py(orig.build_py, Mixin2to3): return orig.build_py.__getattr__(self, attr) def build_module(self, module, module_file, package): - if six.PY2 and isinstance(package, six.string_types): - # avoid errors on Python 2 when unicode is passed (#190) - package = package.split('.') outfile, copied = orig.build_py.build_module(self, module, module_file, package) if copied: @@ -249,7 +243,7 @@ def _unique_everseen(iterable, key=None): seen = set() seen_add = seen.add if key is None: - for element in filterfalse(seen.__contains__, iterable): + for element in itertools.filterfalse(seen.__contains__, iterable): seen_add(element) yield element else: diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index e7e03cd4..faf8c988 100644 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -5,15 +5,11 @@ import os import glob import io -from setuptools.extern import six - import pkg_resources from setuptools.command.easy_install import easy_install from setuptools import namespaces import setuptools -__metaclass__ = type - class develop(namespaces.DevelopInstaller, easy_install): """Set up package for development""" @@ -108,7 +104,7 @@ class develop(namespaces.DevelopInstaller, easy_install): return path_to_setup def install_for_development(self): - if not six.PY2 and getattr(self.distribution, 'use_2to3', False): + if getattr(self.distribution, 'use_2to3', False): # If we run 2to3 we can not do this inplace: # Ensure metadata is up-to-date diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index bcbd4f58..9ec83b7d 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -38,18 +38,15 @@ import contextlib import subprocess import shlex import io +import configparser from sysconfig import get_config_vars, get_path from setuptools import SetuptoolsDeprecationWarning -from setuptools.extern import six -from setuptools.extern.six.moves import configparser, map - from setuptools import Command from setuptools.sandbox import run_setup -from setuptools.py27compat import rmtree_safe from setuptools.command import setopt from setuptools.archive_util import unpack_archive from setuptools.package_index import ( @@ -65,8 +62,6 @@ from pkg_resources import ( ) import pkg_resources -__metaclass__ = type - # Turn on PEP440Warnings warnings.filterwarnings("default", category=pkg_resources.PEP440Warning) @@ -96,28 +91,16 @@ def samefile(p1, p2): return norm_p1 == norm_p2 -if six.PY2: - - def _to_bytes(s): - return s - - def isascii(s): - try: - six.text_type(s, 'ascii') - return True - except UnicodeError: - return False -else: +def _to_bytes(s): + return s.encode('utf8') - def _to_bytes(s): - return s.encode('utf8') - def isascii(s): - try: - s.encode('ascii') - return True - except UnicodeError: - return False +def isascii(s): + try: + s.encode('ascii') + return True + except UnicodeError: + return False def _one_liner(text): @@ -341,7 +324,7 @@ class easy_install(Command): self.local_index = Environment(self.shadow_path + sys.path) if self.find_links is not None: - if isinstance(self.find_links, six.string_types): + if isinstance(self.find_links, str): self.find_links = self.find_links.split() else: self.find_links = [] @@ -650,7 +633,7 @@ class easy_install(Command): # cast to str as workaround for #709 and #710 and #712 yield str(tmpdir) finally: - os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir)) + os.path.exists(tmpdir) and rmtree(tmpdir) def easy_install(self, spec, deps=False): with self._tmpdir() as tmpdir: @@ -1318,7 +1301,7 @@ class easy_install(Command): if not self.user: return home = convert_path(os.path.expanduser("~")) - for name, path in six.iteritems(self.config_vars): + for name, path in self.config_vars.items(): if path.startswith(home) and not os.path.isdir(path): self.debug_print("os.makedirs('%s', 0o700)" % path) os.makedirs(path, 0o700) @@ -1499,7 +1482,7 @@ def extract_wininst_cfg(dist_filename): # Now the config is in bytes, but for RawConfigParser, it should # be text, so decode it. config = config.decode(sys.getfilesystemencoding()) - cfg.readfp(six.StringIO(config)) + cfg.readfp(io.StringIO(config)) except configparser.Error: return None if not cfg.has_section('metadata') or not cfg.has_section('Setup'): @@ -1534,9 +1517,7 @@ def get_exe_prefixes(exe_filename): if name.endswith('-nspkg.pth'): continue if parts[0].upper() in ('PURELIB', 'PLATLIB'): - contents = z.read(name) - if not six.PY2: - contents = contents.decode() + contents = z.read(name).decode() for pth in yield_lines(contents): pth = pth.strip().replace('\\', '/') if not pth.startswith('import'): @@ -1700,7 +1681,8 @@ def auto_chmod(func, arg, exc): chmod(arg, stat.S_IWRITE) return func(arg) et, ev, _ = sys.exc_info() - six.reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg)))) + # TODO: This code doesn't make sense. What is it trying to do? + raise (ev[0], ev[1] + (" %s %s" % (func, arg))) def update_dist_caches(dist_path, fix_zipimporter_caches): @@ -2263,10 +2245,7 @@ def get_win_launcher(type): def load_launcher_manifest(name): manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml') - if six.PY2: - return manifest % vars() - else: - return manifest.decode('utf-8') % vars() + return manifest.decode('utf-8') % vars() def rmtree(path, ignore_errors=False, onerror=auto_chmod): diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 0855207c..c957154a 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -16,9 +16,6 @@ import warnings import time import collections -from setuptools.extern import six -from setuptools.extern.six.moves import map - from setuptools import Command from setuptools.command.sdist import sdist from setuptools.command.sdist import walk_revctrl @@ -267,8 +264,7 @@ class egg_info(InfoCommon, Command): to the file. """ log.info("writing %s to %s", what, filename) - if not six.PY2: - data = data.encode("utf-8") + data = data.encode("utf-8") if not self.dry_run: f = open(filename, 'wb') f.write(data) @@ -647,7 +643,7 @@ def _write_requirements(stream, reqs): def write_requirements(cmd, basename, filename): dist = cmd.distribution - data = six.StringIO() + data = io.StringIO() _write_requirements(data, dist.install_requires) extras_require = dist.extras_require or {} for extra in sorted(extras_require): @@ -687,12 +683,12 @@ def write_arg(cmd, basename, filename, force=False): def write_entries(cmd, basename, filename): ep = cmd.distribution.entry_points - if isinstance(ep, six.string_types) or ep is None: + if isinstance(ep, str) or ep is None: data = ep elif ep is not None: data = [] for section, contents in sorted(ep.items()): - if not isinstance(contents, six.string_types): + if not isinstance(contents, str): contents = EntryPoint.parse_group(section, contents) contents = '\n'.join(sorted(map(str, contents.values()))) data.append('[%s]\n%s\n\n' % (section, contents)) diff --git a/setuptools/command/py36compat.py b/setuptools/command/py36compat.py index 28860558..343547a4 100644 --- a/setuptools/command/py36compat.py +++ b/setuptools/command/py36compat.py @@ -3,8 +3,6 @@ from glob import glob from distutils.util import convert_path from distutils.command import sdist -from setuptools.extern.six.moves import filter - class sdist_add_defaults: """ diff --git a/setuptools/command/rotate.py b/setuptools/command/rotate.py index e398834f..74795ba9 100644 --- a/setuptools/command/rotate.py +++ b/setuptools/command/rotate.py @@ -4,8 +4,6 @@ from distutils.errors import DistutilsOptionError import os import shutil -from setuptools.extern import six - from setuptools import Command @@ -38,7 +36,7 @@ class rotate(Command): self.keep = int(self.keep) except ValueError as e: raise DistutilsOptionError("--keep must be an integer") from e - if isinstance(self.match, six.string_types): + if isinstance(self.match, str): self.match = [ convert_path(p.strip()) for p in self.match.split(',') ] diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 8c3438ea..887b7efa 100644 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -5,7 +5,7 @@ import sys import io import contextlib -from setuptools.extern import six, ordered_set +from setuptools.extern import ordered_set from .py36compat import sdist_add_defaults @@ -98,34 +98,8 @@ class sdist(sdist_add_defaults, orig.sdist): if orig_val is not NoValue: setattr(os, 'link', orig_val) - def __read_template_hack(self): - # This grody hack closes the template file (MANIFEST.in) if an - # exception occurs during read_template. - # Doing so prevents an error when easy_install attempts to delete the - # file. - try: - orig.sdist.read_template(self) - except Exception: - _, _, tb = sys.exc_info() - tb.tb_next.tb_frame.f_locals['template'].close() - raise - - # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle - # has been fixed, so only override the method if we're using an earlier - # Python. - has_leaky_handle = ( - sys.version_info < (2, 7, 2) - or (3, 0) <= sys.version_info < (3, 1, 4) - or (3, 2) <= sys.version_info < (3, 2, 1) - ) - if has_leaky_handle: - read_template = __read_template_hack - def _add_defaults_optional(self): - if six.PY2: - sdist_add_defaults._add_defaults_optional(self) - else: - super()._add_defaults_optional() + super()._add_defaults_optional() if os.path.isfile('pyproject.toml'): self.filelist.append('pyproject.toml') @@ -158,10 +132,7 @@ class sdist(sdist_add_defaults, orig.sdist): def _add_defaults_data_files(self): try: - if six.PY2: - sdist_add_defaults._add_defaults_data_files(self) - else: - super()._add_defaults_data_files() + super()._add_defaults_data_files() except TypeError: log.warn("data_files contains unexpected objects") @@ -207,12 +178,11 @@ class sdist(sdist_add_defaults, orig.sdist): manifest = open(self.manifest, 'rb') for line in manifest: # The manifest must contain UTF-8. See #303. - if not six.PY2: - try: - line = line.decode('UTF-8') - except UnicodeDecodeError: - log.warn("%r not UTF-8 decodable -- skipping" % line) - continue + try: + line = line.decode('UTF-8') + except UnicodeDecodeError: + log.warn("%r not UTF-8 decodable -- skipping" % line) + continue # ignore comments and blank lines line = line.strip() if line.startswith('#') or not line: diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py index 7e57cc02..e18057c8 100644 --- a/setuptools/command/setopt.py +++ b/setuptools/command/setopt.py @@ -3,8 +3,7 @@ from distutils import log from distutils.errors import DistutilsOptionError import distutils import os - -from setuptools.extern.six.moves import configparser +import configparser from setuptools import Command diff --git a/setuptools/command/test.py b/setuptools/command/test.py index 2d83967d..cf71ad01 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -8,17 +8,12 @@ from distutils.errors import DistutilsError, DistutilsOptionError from distutils import log from unittest import TestLoader -from setuptools.extern import six -from setuptools.extern.six.moves import map, filter - from pkg_resources import (resource_listdir, resource_exists, normalize_path, working_set, _namespace_packages, evaluate_marker, add_activation_listener, require, EntryPoint) from setuptools import Command from .build_py import _unique_everseen -__metaclass__ = type - class ScanningLoader(TestLoader): @@ -129,8 +124,7 @@ class test(Command): @contextlib.contextmanager def project_on_sys_path(self, include_dists=[]): - with_2to3 = not six.PY2 and getattr( - self.distribution, 'use_2to3', False) + with_2to3 = getattr(self.distribution, 'use_2to3', False) if with_2to3: # If we run 2to3 we can not do this inplace: @@ -241,7 +235,7 @@ class test(Command): # Purge modules under test from sys.modules. The test loader will # re-import them from the build location. Required when 2to3 is used # with namespace packages. - if not six.PY2 and getattr(self.distribution, 'use_2to3', False): + if getattr(self.distribution, 'use_2to3', False): module = self.test_suite.split('.')[0] if module in _namespace_packages: del_modules = [] diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index 0351da77..2559458a 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -15,17 +15,15 @@ import tempfile import shutil import itertools import functools - -from setuptools.extern import six -from setuptools.extern.six.moves import http_client, urllib +import http.client +import urllib.parse from pkg_resources import iter_entry_points from .upload import upload def _encode(s): - errors = 'strict' if six.PY2 else 'surrogateescape' - return s.encode('utf-8', errors) + return s.encode('utf-8', 'surrogateescape') class upload_docs(upload): @@ -152,9 +150,7 @@ class upload_docs(upload): } # set up the authentication credentials = _encode(self.username + ':' + self.password) - credentials = standard_b64encode(credentials) - if not six.PY2: - credentials = credentials.decode('ascii') + credentials = standard_b64encode(credentials).decode('ascii') auth = "Basic " + credentials body, ct = self._build_multipart(data) @@ -169,9 +165,9 @@ class upload_docs(upload): urllib.parse.urlparse(self.repository) assert not params and not query and not fragments if schema == 'http': - conn = http_client.HTTPConnection(netloc) + conn = http.client.HTTPConnection(netloc) elif schema == 'https': - conn = http_client.HTTPSConnection(netloc) + conn = http.client.HTTPSConnection(netloc) else: raise AssertionError("unsupported schema " + schema) diff --git a/setuptools/config.py b/setuptools/config.py index a8f8b6b0..af3a3bcb 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import, unicode_literals import ast import io import os @@ -15,10 +14,6 @@ import contextlib from distutils.errors import DistutilsOptionError, DistutilsFileError from setuptools.extern.packaging.version import LegacyVersion, parse from setuptools.extern.packaging.specifiers import SpecifierSet -from setuptools.extern.six import string_types, PY3 - - -__metaclass__ = type class StaticModule: @@ -324,7 +319,7 @@ class ConfigHandler: """ include_directive = 'file:' - if not isinstance(value, string_types): + if not isinstance(value, str): return value if not value.startswith(include_directive): @@ -559,7 +554,7 @@ class ConfigMetadataHandler(ConfigHandler): if callable(version): version = version() - if not isinstance(version, string_types): + if not isinstance(version, str): if hasattr(version, '__iter__'): version = '.'.join(map(str, version)) else: @@ -614,9 +609,6 @@ class ConfigOptionsHandler(ConfigHandler): return self._parse_list(value) findns = trimmed_value == find_directives[1] - if findns and not PY3: - raise DistutilsOptionError( - 'find_namespace: directive is unsupported on Python < 3.3') # Read function arguments from a dedicated section. find_kwargs = self.parse_section_packages__find( diff --git a/setuptools/depends.py b/setuptools/depends.py index a37675cb..8be6928a 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -1,12 +1,11 @@ import sys import marshal import contextlib +import dis from distutils.version import StrictVersion -from .py33compat import Bytecode - -from .py27compat import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE -from . import py27compat +from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE +from . import _imp __all__ = [ @@ -111,12 +110,12 @@ def get_module_constant(module, symbol, default=-1, paths=None): f.read(8) # skip magic & date code = marshal.load(f) elif kind == PY_FROZEN: - code = py27compat.get_frozen_object(module, paths) + code = _imp.get_frozen_object(module, paths) elif kind == PY_SOURCE: code = compile(f.read(), path, 'exec') else: # Not something we can parse; we'll have to import it. :( - imported = py27compat.get_module(module, paths, info) + imported = _imp.get_module(module, paths, info) return getattr(imported, symbol, None) return extract_constant(code, symbol, default) @@ -146,7 +145,7 @@ def extract_constant(code, symbol, default=-1): const = default - for byte_code in Bytecode(code): + for byte_code in dis.Bytecode(code): op = byte_code.opcode arg = byte_code.arg diff --git a/setuptools/dist.py b/setuptools/dist.py index e813b11c..2c088ef8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -23,10 +23,8 @@ from distutils.errors import DistutilsOptionError, DistutilsSetupError from distutils.util import rfc822_escape from distutils.version import StrictVersion -from setuptools.extern import six from setuptools.extern import packaging from setuptools.extern import ordered_set -from setuptools.extern.six.moves import map, filter, filterfalse from . import SetuptoolsDeprecationWarning @@ -126,12 +124,8 @@ def write_pkg_file(self, file): """ version = self.get_metadata_version() - if six.PY2: - def write_field(key, value): - file.write("%s: %s\n" % (key, self._encode_field(value))) - else: - def write_field(key, value): - file.write("%s: %s\n" % (key, value)) + def write_field(key, value): + file.write("%s: %s\n" % (key, value)) write_field('Metadata-Version', str(version)) write_field('Name', self.get_name()) @@ -308,7 +302,7 @@ def check_entry_points(dist, attr, value): def check_test_suite(dist, attr, value): - if not isinstance(value, six.string_types): + if not isinstance(value, str): raise DistutilsSetupError("test_suite must be a string") @@ -319,7 +313,7 @@ def check_package_data(dist, attr, value): "{!r} must be a dictionary mapping package names to lists of " "string wildcard patterns".format(attr)) for k, v in value.items(): - if not isinstance(k, six.string_types): + if not isinstance(k, str): raise DistutilsSetupError( "keys of {!r} dict must be strings (got {!r})" .format(attr, k) @@ -537,7 +531,7 @@ class Distribution(_Distribution): spec_inst_reqs = getattr(self, 'install_requires', None) or () inst_reqs = list(pkg_resources.parse_requirements(spec_inst_reqs)) simple_reqs = filter(is_simple_req, inst_reqs) - complex_reqs = filterfalse(is_simple_req, inst_reqs) + complex_reqs = itertools.filterfalse(is_simple_req, inst_reqs) self.install_requires = list(map(str, simple_reqs)) for r in complex_reqs: @@ -560,10 +554,10 @@ class Distribution(_Distribution): this method provides the same functionality in subtly-improved ways. """ - from setuptools.extern.six.moves.configparser import ConfigParser + from configparser import ConfigParser # Ignore install directory options if we have a venv - if not six.PY2 and sys.prefix != sys.base_prefix: + if sys.prefix != sys.base_prefix: ignore_options = [ 'install-base', 'install-platbase', 'install-lib', 'install-platlib', 'install-purelib', 'install-headers', @@ -585,14 +579,14 @@ class Distribution(_Distribution): with io.open(filename, encoding='utf-8') as reader: if DEBUG: self.announce(" reading {filename}".format(**locals())) - (parser.readfp if six.PY2 else parser.read_file)(reader) + parser.read_file(reader) for section in parser.sections(): options = parser.options(section) opt_dict = self.get_option_dict(section) for opt in options: if opt != '__name__' and opt not in ignore_options: - val = self._try_str(parser.get(section, opt)) + val = parser.get(section, opt) opt = opt.replace('-', '_') opt_dict[opt] = (filename, val) @@ -616,26 +610,6 @@ class Distribution(_Distribution): except ValueError as e: raise DistutilsOptionError(e) from e - @staticmethod - def _try_str(val): - """ - On Python 2, much of distutils relies on string values being of - type 'str' (bytes) and not unicode text. If the value can be safely - encoded to bytes using the default encoding, prefer that. - - Why the default encoding? Because that value can be implicitly - decoded back to text if needed. - - Ref #1653 - """ - if not six.PY2: - return val - try: - return val.encode() - except UnicodeEncodeError: - pass - return val - def _set_command_options(self, command_obj, option_dict=None): """ Set the options for 'command_obj' from 'option_dict'. Basically @@ -669,7 +643,7 @@ class Distribution(_Distribution): neg_opt = {} try: - is_string = isinstance(value, six.string_types) + is_string = isinstance(value, str) if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: @@ -1003,7 +977,7 @@ class Distribution(_Distribution): """ import sys - if six.PY2 or self.help_commands: + if self.help_commands: return _Distribution.handle_display_options(self, option_order) # Stdout may be StringIO (e.g. in tests) diff --git a/setuptools/extension.py b/setuptools/extension.py index 29468894..1820722a 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -4,8 +4,6 @@ import distutils.core import distutils.errors import distutils.extension -from setuptools.extern.six.moves import map - from .monkey import get_unpatched diff --git a/setuptools/installer.py b/setuptools/installer.py index e5acec27..e630b874 100644 --- a/setuptools/installer.py +++ b/setuptools/installer.py @@ -2,20 +2,18 @@ import glob import os import subprocess import sys +import tempfile from distutils import log from distutils.errors import DistutilsError import pkg_resources from setuptools.command.easy_install import easy_install -from setuptools.extern import six from setuptools.wheel import Wheel -from .py31compat import TemporaryDirectory - def _fixup_find_links(find_links): """Ensure find-links option end-up being a list of strings.""" - if isinstance(find_links, six.string_types): + if isinstance(find_links, str): return find_links.split() assert isinstance(find_links, (tuple, list)) return find_links @@ -103,7 +101,7 @@ def fetch_build_egg(dist, req): for egg_dist in pkg_resources.find_distributions(eggs_dir): if egg_dist in req and environment.can_add(egg_dist): return egg_dist - with TemporaryDirectory() as tmpdir: + with tempfile.TemporaryDirectory() as tmpdir: cmd = [ sys.executable, '-m', 'pip', '--disable-pip-version-check', diff --git a/setuptools/lib2to3_ex.py b/setuptools/lib2to3_ex.py index 017f7285..c176abf6 100644 --- a/setuptools/lib2to3_ex.py +++ b/setuptools/lib2to3_ex.py @@ -2,9 +2,6 @@ Customized Mixin2to3 support: - adds support for converting doctests - - -This module raises an ImportError on Python 2. """ import warnings diff --git a/setuptools/monkey.py b/setuptools/monkey.py index e5f1377b..fb36dc1a 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -10,8 +10,6 @@ import functools from importlib import import_module import inspect -from setuptools.extern import six - import setuptools __all__ = [] @@ -37,7 +35,7 @@ def _get_mro(cls): def get_unpatched(item): lookup = ( - get_unpatched_class if isinstance(item, six.class_types) else + get_unpatched_class if isinstance(item, type) else get_unpatched_function if isinstance(item, types.FunctionType) else lambda item: None ) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 72383eb8..24ea0863 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -30,12 +30,10 @@ import subprocess import distutils.errors from setuptools.extern.packaging.version import LegacyVersion -from setuptools.extern.six.moves import filterfalse - from .monkey import get_unpatched if platform.system() == 'Windows': - from setuptools.extern.six.moves import winreg + import winreg from os import environ else: # Mock winreg and environ so the module can be imported on this platform. @@ -1820,7 +1818,7 @@ class EnvironmentInfo: seen = set() seen_add = seen.add if key is None: - for element in filterfalse(seen.__contains__, iterable): + for element in itertools.filterfalse(seen.__contains__, iterable): seen_add(element) yield element else: diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 5f403c96..44939e1c 100644 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -2,8 +2,6 @@ import os from distutils import log import itertools -from setuptools.extern.six.moves import map - flatten = itertools.chain.from_iterable @@ -72,8 +70,6 @@ class Installer: return "sys._getframe(1).f_locals['sitedir']" def _gen_nspkg_line(self, pkg): - # ensure pkg is not a unicode string under Python 2.7 - pkg = str(pkg) pth = tuple(pkg.split('.')) root = self._get_root() tmpl_lines = self._nspkg_tmpl diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 1702c7c6..3979b131 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -2,17 +2,21 @@ import sys import os import re +import io import shutil import socket import base64 import hashlib import itertools import warnings +import configparser +import html +import http.client +import urllib.parse +import urllib.request +import urllib.error from functools import wraps -from setuptools.extern import six -from setuptools.extern.six.moves import urllib, http_client, configparser, map - import setuptools from pkg_resources import ( CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST, @@ -23,12 +27,8 @@ from setuptools import ssl_support from distutils import log from distutils.errors import DistutilsError from fnmatch import translate -from setuptools.py27compat import get_all_headers -from setuptools.py33compat import unescape from setuptools.wheel import Wheel -__metaclass__ = type - EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') HREF = re.compile(r"""href\s*=\s*['"]?([^'"> ]+)""", re.I) PYPI_MD5 = re.compile( @@ -191,7 +191,7 @@ def unique_everseen(iterable, key=None): seen = set() seen_add = seen.add if key is None: - for element in six.moves.filterfalse(seen.__contains__, iterable): + for element in itertools.filterfalse(seen.__contains__, iterable): seen_add(element) yield element else: @@ -740,7 +740,7 @@ class PackageIndex(Environment): size = -1 if "content-length" in headers: # Some servers return multiple Content-Length headers :( - sizes = get_all_headers(headers, 'Content-Length') + sizes = headers.get_all('Content-Length') size = max(map(int, sizes)) self.reporthook(url, filename, blocknum, bs, size) with open(filename, 'wb') as tfp: @@ -767,7 +767,7 @@ class PackageIndex(Environment): return local_open(url) try: return open_with_auth(url, self.opener) - except (ValueError, http_client.InvalidURL) as v: + except (ValueError, http.client.InvalidURL) as v: msg = ' '.join([str(arg) for arg in v.args]) if warning: self.warn(warning, msg) @@ -781,7 +781,7 @@ class PackageIndex(Environment): else: raise DistutilsError("Download error for %s: %s" % (url, v.reason)) from v - except http_client.BadStatusLine as v: + except http.client.BadStatusLine as v: if warning: self.warn(warning, v.line) else: @@ -790,7 +790,7 @@ class PackageIndex(Environment): 'down, %s' % (url, v.line) ) from v - except (http_client.HTTPException, socket.error) as v: + except (http.client.HTTPException, socket.error) as v: if warning: self.warn(warning, v) else: @@ -940,7 +940,7 @@ entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub def decode_entity(match): what = match.group(0) - return unescape(what) + return html.unescape(what) def htmldecode(text): @@ -972,8 +972,7 @@ def socket_timeout(timeout=15): def _encode_auth(auth): """ - A function compatible with Python 2.3-3.3 that will encode - auth from a URL suitable for an HTTP header. + Encode auth from a URL suitable for an HTTP header. >>> str(_encode_auth('username%3Apassword')) 'dXNlcm5hbWU6cGFzc3dvcmQ=' @@ -1056,7 +1055,7 @@ def open_with_auth(url, opener=urllib.request.urlopen): # Double scheme does not raise on macOS as revealed by a # failing test. We would expect "nonnumeric port". Refs #20. if netloc.endswith(':'): - raise http_client.InvalidURL("nonnumeric port: ''") + raise http.client.InvalidURL("nonnumeric port: ''") if scheme in ('http', 'https'): auth, address = _splituser(netloc) @@ -1136,5 +1135,5 @@ def local_open(url): status, message, body = 404, "Path not found", "Not found" headers = {'content-type': 'text/html'} - body_stream = six.StringIO(body) + body_stream = io.StringIO(body) return urllib.error.HTTPError(url, status, message, headers, body_stream) diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py deleted file mode 100644 index ba39af52..00000000 --- a/setuptools/py27compat.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Compatibility Support for Python 2.7 and earlier -""" - -import sys -import platform - -from setuptools.extern import six - - -def get_all_headers(message, key): - """ - Given an HTTPMessage, return all headers matching a given key. - """ - return message.get_all(key) - - -if six.PY2: - def get_all_headers(message, key): # noqa - return message.getheaders(key) - - -linux_py2_ascii = ( - platform.system() == 'Linux' and - six.PY2 -) - -rmtree_safe = str if linux_py2_ascii else lambda x: x -"""Workaround for http://bugs.python.org/issue24672""" - - -try: - from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE - from ._imp import get_frozen_object, get_module -except ImportError: - import imp - from imp import PY_COMPILED, PY_FROZEN, PY_SOURCE # noqa - - def find_module(module, paths=None): - """Just like 'imp.find_module()', but with package support""" - parts = module.split('.') - while parts: - part = parts.pop(0) - f, path, (suffix, mode, kind) = info = imp.find_module(part, paths) - - if kind == imp.PKG_DIRECTORY: - parts = parts or ['__init__'] - paths = [path] - - elif parts: - raise ImportError("Can't find %r in %s" % (parts, module)) - - return info - - def get_frozen_object(module, paths): - return imp.get_frozen_object(module) - - def get_module(module, paths, info): - imp.load_module(module, *info) - return sys.modules[module] diff --git a/setuptools/py31compat.py b/setuptools/py31compat.py deleted file mode 100644 index e1da7ee2..00000000 --- a/setuptools/py31compat.py +++ /dev/null @@ -1,32 +0,0 @@ -__all__ = [] - -__metaclass__ = type - - -try: - # Python >=3.2 - from tempfile import TemporaryDirectory -except ImportError: - import shutil - import tempfile - - class TemporaryDirectory: - """ - Very simple temporary directory context manager. - Will try to delete afterward, but will also ignore OS and similar - errors on deletion. - """ - - def __init__(self, **kwargs): - self.name = None # Handle mkdtemp raising an exception - self.name = tempfile.mkdtemp(**kwargs) - - def __enter__(self): - return self.name - - def __exit__(self, exctype, excvalue, exctrace): - try: - shutil.rmtree(self.name, True) - except OSError: # removal errors are not the only possible - pass - self.name = None diff --git a/setuptools/py33compat.py b/setuptools/py33compat.py deleted file mode 100644 index cb694436..00000000 --- a/setuptools/py33compat.py +++ /dev/null @@ -1,59 +0,0 @@ -import dis -import array -import collections - -try: - import html -except ImportError: - html = None - -from setuptools.extern import six -from setuptools.extern.six.moves import html_parser - -__metaclass__ = type - -OpArg = collections.namedtuple('OpArg', 'opcode arg') - - -class Bytecode_compat: - def __init__(self, code): - self.code = code - - def __iter__(self): - """Yield '(op,arg)' pair for each operation in code object 'code'""" - - bytes = array.array('b', self.code.co_code) - eof = len(self.code.co_code) - - ptr = 0 - extended_arg = 0 - - while ptr < eof: - - op = bytes[ptr] - - if op >= dis.HAVE_ARGUMENT: - - arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg - ptr += 3 - - if op == dis.EXTENDED_ARG: - long_type = six.integer_types[-1] - extended_arg = arg * long_type(65536) - continue - - else: - arg = None - ptr += 1 - - yield OpArg(op, arg) - - -Bytecode = getattr(dis, 'Bytecode', Bytecode_compat) - - -unescape = getattr(html, 'unescape', None) -if unescape is None: - # HTMLParser.unescape is deprecated since Python 3.4, and will be removed - # from 3.9. - unescape = html_parser.HTMLParser().unescape diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 24a36080..91b960d8 100644 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -8,9 +8,7 @@ import re import contextlib import pickle import textwrap - -from setuptools.extern import six -from setuptools.extern.six.moves import builtins, map +import builtins import pkg_resources from distutils.errors import DistutilsError @@ -138,7 +136,7 @@ class ExceptionSaver: return type, exc = map(pickle.loads, self._saved) - six.reraise(type, exc, self._tb) + raise exc.with_traceback(self._tb) @contextlib.contextmanager @@ -251,15 +249,8 @@ def run_setup(setup_script, args): working_set.__init__() working_set.callbacks.append(lambda dist: dist.activate()) - # __file__ should be a byte string on Python 2 (#712) - dunder_file = ( - setup_script - if isinstance(setup_script, str) else - setup_script.encode(sys.getfilesystemencoding()) - ) - with DirectorySandbox(setup_dir): - ns = dict(__file__=dunder_file, __name__='__main__') + ns = dict(__file__=setup_script, __name__='__main__') _execfile(setup_script, ns) except SystemExit as v: if v.args and v.args[0]: diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index 17c14c46..eac5e656 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -3,8 +3,9 @@ import socket import atexit import re import functools +import urllib.request +import http.client -from setuptools.extern.six.moves import urllib, http_client, map, filter from pkg_resources import ResolutionError, ExtractionError @@ -31,7 +32,7 @@ cert_paths = """ try: HTTPSHandler = urllib.request.HTTPSHandler - HTTPSConnection = http_client.HTTPSConnection + HTTPSConnection = http.client.HTTPSConnection except AttributeError: HTTPSHandler = HTTPSConnection = object diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 6377d785..a7a2112f 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -2,19 +2,12 @@ import locale import pytest -from setuptools.extern.six import PY2, PY3 - -__all__ = [ - 'fail_on_ascii', 'py2_only', 'py3_only', 'ack_2to3' -] +__all__ = ['fail_on_ascii', 'ack_2to3'] is_ascii = locale.getpreferredencoding() == 'ANSI_X3.4-1968' fail_on_ascii = pytest.mark.xfail(is_ascii, reason="Test fails in this locale") -py2_only = pytest.mark.skipif(not PY2, reason="Test runs on Python 2 only") -py3_only = pytest.mark.skipif(not PY3, reason="Test runs on Python 3 only") - ack_2to3 = pytest.mark.filterwarnings('ignore:2to3 support is deprecated') diff --git a/setuptools/tests/contexts.py b/setuptools/tests/contexts.py index 535ae107..51ce8984 100644 --- a/setuptools/tests/contexts.py +++ b/setuptools/tests/contexts.py @@ -4,8 +4,8 @@ import shutil import sys import contextlib import site +import io -from setuptools.extern import six import pkg_resources @@ -58,8 +58,8 @@ def quiet(): old_stdout = sys.stdout old_stderr = sys.stderr - new_stdout = sys.stdout = six.StringIO() - new_stderr = sys.stderr = six.StringIO() + new_stdout = sys.stdout = io.StringIO() + new_stderr = sys.stderr = io.StringIO() try: yield new_stdout, new_stderr finally: diff --git a/setuptools/tests/namespaces.py b/setuptools/tests/namespaces.py index ef5ecdad..245cf8ea 100644 --- a/setuptools/tests/namespaces.py +++ b/setuptools/tests/namespaces.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - import textwrap diff --git a/setuptools/tests/server.py b/setuptools/tests/server.py index 8b17b081..7e213230 100644 --- a/setuptools/tests/server.py +++ b/setuptools/tests/server.py @@ -4,13 +4,12 @@ import os import time import threading +import http.server +import urllib.parse +import urllib.request -from setuptools.extern.six.moves import BaseHTTPServer, SimpleHTTPServer -from setuptools.extern.six.moves.urllib_parse import urljoin -from setuptools.extern.six.moves.urllib.request import pathname2url - -class IndexServer(BaseHTTPServer.HTTPServer): +class IndexServer(http.server.HTTPServer): """Basic single-threaded http server simulating a package index You can use this server in unittest like this:: @@ -24,8 +23,8 @@ class IndexServer(BaseHTTPServer.HTTPServer): def __init__( self, server_address=('', 0), - RequestHandlerClass=SimpleHTTPServer.SimpleHTTPRequestHandler): - BaseHTTPServer.HTTPServer.__init__( + RequestHandlerClass=http.server.SimpleHTTPRequestHandler): + http.server.HTTPServer.__init__( self, server_address, RequestHandlerClass) self._run = True @@ -48,14 +47,14 @@ class IndexServer(BaseHTTPServer.HTTPServer): return 'http://127.0.0.1:%s/setuptools/tests/indexes/' % port -class RequestRecorder(BaseHTTPServer.BaseHTTPRequestHandler): +class RequestRecorder(http.server.BaseHTTPRequestHandler): def do_GET(self): requests = vars(self.server).setdefault('requests', []) requests.append(self) self.send_response(200, 'OK') -class MockServer(BaseHTTPServer.HTTPServer, threading.Thread): +class MockServer(http.server.HTTPServer, threading.Thread): """ A simple HTTP Server that records the requests made to it. """ @@ -63,7 +62,7 @@ class MockServer(BaseHTTPServer.HTTPServer, threading.Thread): def __init__( self, server_address=('', 0), RequestHandlerClass=RequestRecorder): - BaseHTTPServer.HTTPServer.__init__( + http.server.HTTPServer.__init__( self, server_address, RequestHandlerClass) threading.Thread.__init__(self) self.setDaemon(True) @@ -87,5 +86,5 @@ def path_to_url(path, authority=None): base = 'file:' if authority is not None: base += '//' + authority - url = urljoin(base, pathname2url(path)) + url = urllib.parse.urljoin(base, urllib.request.pathname2url(path)) return url diff --git a/setuptools/tests/test_archive_util.py b/setuptools/tests/test_archive_util.py index b789e9ac..7f996244 100644 --- a/setuptools/tests/test_archive_util.py +++ b/setuptools/tests/test_archive_util.py @@ -3,8 +3,6 @@ import tarfile import io -from setuptools.extern import six - import pytest from setuptools import archive_util @@ -22,8 +20,6 @@ def tarfile_with_unicode(tmpdir): data = b"" filename = "testimäge.png" - if six.PY2: - filename = filename.decode('utf-8') t = tarfile.TarInfo(filename) t.size = len(data) @@ -39,4 +35,4 @@ def tarfile_with_unicode(tmpdir): @pytest.mark.xfail(reason="#710 and #712") def test_unicode_files(tarfile_with_unicode, tmpdir): target = tmpdir / 'out' - archive_util.unpack_archive(tarfile_with_unicode, six.text_type(target)) + archive_util.unpack_archive(tarfile_with_unicode, str(target)) diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index 2ef8521d..838fdb42 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -2,8 +2,6 @@ import sys import distutils.command.build_ext as orig from distutils.sysconfig import get_config_var -from setuptools.extern import six - from setuptools.command.build_ext import build_ext, get_abi3_suffix from setuptools.dist import Distribution from setuptools.extension import Extension @@ -41,7 +39,7 @@ class TestBuildExt: assert 'spam.eggs' in cmd.ext_map res = cmd.get_ext_filename('spam.eggs') - if six.PY2 or not get_abi3_suffix(): + if not get_abi3_suffix(): assert res.endswith(get_config_var('EXT_SUFFIX')) elif sys.platform == 'win32': assert res.endswith('eggs.pyd') diff --git a/setuptools/tests/test_build_meta.py b/setuptools/tests/test_build_meta.py index fdb4b950..5462b26a 100644 --- a/setuptools/tests/test_build_meta.py +++ b/setuptools/tests/test_build_meta.py @@ -1,20 +1,13 @@ -from __future__ import unicode_literals - import os import shutil import tarfile +import importlib +from concurrent import futures import pytest from .files import build_files from .textwrap import DALS -from . import py2_only - -__metaclass__ = type - -# Backports on Python 2.7 -import importlib -from concurrent import futures class BuildBackendBase: @@ -220,15 +213,6 @@ class TestBuildMetaBackend: assert os.path.isfile(os.path.join(dist_dir, dist_info, 'METADATA')) - @py2_only - def test_prepare_metadata_for_build_wheel_with_str(self, build_backend): - dist_dir = os.path.abspath(str('pip-dist-info')) - os.makedirs(dist_dir) - - dist_info = build_backend.prepare_metadata_for_build_wheel(dist_dir) - - assert os.path.isfile(os.path.join(dist_dir, dist_info, 'METADATA')) - def test_build_sdist_explicit_dist(self, build_backend): # explicitly specifying the dist folder should work # the folder sdist_directory and the ``--dist-dir`` can be the same diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 67992c04..1dee1271 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - import contextlib +import configparser import pytest @@ -9,9 +7,6 @@ from distutils.errors import DistutilsOptionError, DistutilsFileError from mock import patch from setuptools.dist import Distribution, _Distribution from setuptools.config import ConfigHandler, read_configuration -from setuptools.extern.six.moves import configparser -from setuptools.extern import six -from . import py2_only, py3_only from .textwrap import DALS @@ -311,10 +306,6 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' - if six.PY2: - # static version loading is unsupported on Python 2 - return - config.write( '[metadata]\n' 'version = attr: fake_package.subpkg_b.mod.VERSION\n' @@ -719,19 +710,6 @@ class TestOptions: assert set(dist.packages) == set( ['fake_package', 'fake_package.sub_two']) - @py2_only - def test_find_namespace_directive_fails_on_py2(self, tmpdir): - dir_package, config = fake_env( - tmpdir, - '[options]\n' - 'packages = find_namespace:\n' - ) - - with pytest.raises(DistutilsOptionError): - with get_dist(tmpdir) as dist: - dist.parse_config_files() - - @py3_only def test_find_namespace_directive(self, tmpdir): dir_package, config = fake_env( tmpdir, diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index bb89a865..9854420e 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -1,8 +1,6 @@ """develop tests """ -from __future__ import absolute_import, unicode_literals - import os import site import sys @@ -10,7 +8,6 @@ import io import subprocess import platform -from setuptools.extern import six from setuptools.command import test import pytest @@ -97,7 +94,7 @@ class TestDevelop: with io.open(fn) as init_file: init = init_file.read().strip() - expected = 'print "foo"' if six.PY2 else 'print("foo")' + expected = 'print("foo")' assert init == expected def test_console_scripts(self, tmpdir): @@ -163,7 +160,7 @@ class TestNamespaces: reason="https://github.com/pypa/setuptools/issues/851", ) @pytest.mark.skipif( - platform.python_implementation() == 'PyPy' and not six.PY2, + platform.python_implementation() == 'PyPy', reason="https://github.com/pypa/setuptools/issues/1202", ) def test_namespace_package_importable(self, tmpdir): diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index 531ea1b4..cb47fb58 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -1,11 +1,9 @@ -# -*- coding: utf-8 -*- - -from __future__ import unicode_literals - import io import collections import re import functools +import urllib.request +import urllib.parse from distutils.errors import DistutilsSetupError from setuptools.dist import ( _get_unpatched, @@ -14,9 +12,6 @@ from setuptools.dist import ( ) from setuptools import sic from setuptools import Distribution -from setuptools.extern.six.moves.urllib.request import pathname2url -from setuptools.extern.six.moves.urllib_parse import urljoin -from setuptools.extern import six from .textwrap import DALS from .test_easy_install import make_nspkg_sdist @@ -29,7 +24,8 @@ def test_dist_fetch_build_egg(tmpdir): Check multiple calls to `Distribution.fetch_build_egg` work as expected. """ index = tmpdir.mkdir('index') - index_url = urljoin('file://', pathname2url(str(index))) + index_url = urllib.parse.urljoin( + 'file://', urllib.request.pathname2url(str(index))) def sdist_with_index(distname, version): dist_dir = index.mkdir(distname) @@ -63,8 +59,7 @@ def test_dist_fetch_build_egg(tmpdir): dist.fetch_build_egg(r) for r in reqs ] - # noqa below because on Python 2 it causes flakes - assert [dist.key for dist in resolved_dists if dist] == reqs # noqa + assert [dist.key for dist in resolved_dists if dist] == reqs def test_dist__get_unpatched_deprecated(): @@ -150,10 +145,7 @@ def test_read_metadata(name, attrs): dist_class = metadata_out.__class__ # Write to PKG_INFO and then load into a new metadata object - if six.PY2: - PKG_INFO = io.BytesIO() - else: - PKG_INFO = io.StringIO() + PKG_INFO = io.StringIO() metadata_out.write_pkg_file(PKG_INFO) diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index f7e7d2bf..29fbd09d 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -1,10 +1,6 @@ """Test .dist-info style distributions. """ -from __future__ import unicode_literals - -from setuptools.extern.six.moves import map - import pytest import pkg_resources diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index c07b5bea..26a5e9a6 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- """Easy install Tests """ -from __future__ import absolute_import, unicode_literals import sys import os @@ -18,8 +16,6 @@ import mock import time import re -from setuptools.extern import six - import pytest from setuptools import sandbox @@ -41,8 +37,6 @@ from . import contexts from .files import build_files from .textwrap import DALS -__metaclass__ = type - class FakeDist: def get_entry_map(self, group): @@ -984,8 +978,6 @@ def create_setup_requires_package(path, distname='foobar', version='0.1', ) class TestScriptHeader: non_ascii_exe = '/Users/José/bin/python' - if six.PY2: - non_ascii_exe = non_ascii_exe.encode('utf-8') exe_with_spaces = r'C:\Program Files\Python36\python.exe' def test_get_script_header(self): diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 109f9135..dc472af4 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -10,7 +10,6 @@ from setuptools.command.egg_info import ( egg_info, manifest_maker, EggInfoDeprecationWarning, get_pkg_info_revision, ) from setuptools.dist import Distribution -from setuptools.extern.six.moves import map import pytest @@ -19,8 +18,6 @@ from .files import build_files from .textwrap import DALS from . import contexts -__metaclass__ = type - class Environment(str): pass @@ -73,8 +70,7 @@ class TestEggInfo: """ When the egg_info section is empty or not present, running save_version_info should add the settings to the setup.cfg - in a deterministic order, consistent with the ordering found - on Python 2.7 with PYTHONHASHSEED=0. + in a deterministic order. """ setup_cfg = os.path.join(env.paths['home'], 'setup.cfg') dist = Distribution() @@ -906,49 +902,3 @@ class TestEggInfo: def test_get_pkg_info_revision_deprecated(self): pytest.warns(EggInfoDeprecationWarning, get_pkg_info_revision) - - EGG_INFO_TESTS = ( - # Check for issue #1136: invalid string type when - # reading declarative `setup.cfg` under Python 2. - { - 'setup.py': DALS( - """ - from setuptools import setup - setup( - name="foo", - ) - """), - 'setup.cfg': DALS( - """ - [options] - package_dir = - = src - """), - 'src': {}, - }, - # Check Unicode can be used in `setup.py` under Python 2. - { - 'setup.py': DALS( - """ - # -*- coding: utf-8 -*- - from __future__ import unicode_literals - from setuptools import setup, find_packages - setup( - name="foo", - package_dir={'': 'src'}, - ) - """), - 'src': {}, - } - ) - - @pytest.mark.parametrize('package_files', EGG_INFO_TESTS) - def test_egg_info(self, tmpdir_cwd, env, package_files): - """ - """ - build_files(package_files) - code, data = environment.run_setup_py( - cmd=['egg_info'], - data_stream=1, - ) - assert not code, data diff --git a/setuptools/tests/test_extern.py b/setuptools/tests/test_extern.py index 3519a680..0d6b164f 100644 --- a/setuptools/tests/test_extern.py +++ b/setuptools/tests/test_extern.py @@ -3,7 +3,6 @@ import pickle from setuptools import Distribution from setuptools.extern import ordered_set -from setuptools.tests import py3_only def test_reimport_extern(): @@ -17,6 +16,5 @@ def test_orderedset_pickle_roundtrip(): assert o1 == o2 -@py3_only def test_distribution_picklable(): pickle.loads(pickle.dumps(Distribution())) diff --git a/setuptools/tests/test_find_packages.py b/setuptools/tests/test_find_packages.py index ab26b4f1..906713f6 100644 --- a/setuptools/tests/test_find_packages.py +++ b/setuptools/tests/test_find_packages.py @@ -7,12 +7,8 @@ import platform import pytest -from . import py3_only - -from setuptools.extern.six import PY3 from setuptools import find_packages -if PY3: - from setuptools import find_namespace_packages +from setuptools import find_namespace_packages # modeled after CPython's test.support.can_symlink @@ -154,34 +150,29 @@ class TestFindPackages: def _assert_packages(self, actual, expected): assert set(actual) == set(expected) - @py3_only def test_pep420_ns_package(self): packages = find_namespace_packages( self.dist_dir, include=['pkg*'], exclude=['pkg.subpkg.assets']) self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) - @py3_only def test_pep420_ns_package_no_includes(self): packages = find_namespace_packages( self.dist_dir, exclude=['pkg.subpkg.assets']) self._assert_packages( packages, ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg']) - @py3_only def test_pep420_ns_package_no_includes_or_excludes(self): packages = find_namespace_packages(self.dist_dir) expected = [ 'docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg', 'pkg.subpkg.assets'] self._assert_packages(packages, expected) - @py3_only def test_regular_package_with_nested_pep420_ns_packages(self): self._touch('__init__.py', self.pkg_dir) packages = find_namespace_packages( self.dist_dir, exclude=['docs', 'pkg.subpkg.assets']) self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg']) - @py3_only def test_pep420_ns_package_no_non_package_dirs(self): shutil.rmtree(self.docs_dir) shutil.rmtree(os.path.join(self.dist_dir, 'pkg/subpkg/assets')) diff --git a/setuptools/tests/test_integration.py b/setuptools/tests/test_integration.py index f1a27f8b..24cef480 100644 --- a/setuptools/tests/test_integration.py +++ b/setuptools/tests/test_integration.py @@ -11,8 +11,8 @@ import subprocess import functools import tarfile import zipfile +import urllib.request -from setuptools.extern.six.moves import urllib import pytest from setuptools.command.easy_install import easy_install diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index 042a8b17..82bdb9c6 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -7,18 +7,16 @@ import shutil import sys import tempfile import itertools +import io from distutils import log from distutils.errors import DistutilsTemplateError from setuptools.command.egg_info import FileList, egg_info, translate_pattern from setuptools.dist import Distribution -from setuptools.extern import six from setuptools.tests.textwrap import DALS import pytest -__metaclass__ = type - def make_local_path(s): """Converts '/' in a string to os.sep""" @@ -41,7 +39,7 @@ setup(**%r) @contextlib.contextmanager def quiet(): old_stdout, old_stderr = sys.stdout, sys.stderr - sys.stdout, sys.stderr = six.StringIO(), six.StringIO() + sys.stdout, sys.stderr = io.StringIO(), io.StringIO() try: yield finally: diff --git a/setuptools/tests/test_msvc14.py b/setuptools/tests/test_msvc14.py index 7833aab4..1aca12dd 100644 --- a/setuptools/tests/test_msvc14.py +++ b/setuptools/tests/test_msvc14.py @@ -31,8 +31,6 @@ class TestMSVC14: finally: _msvccompiler._msvc14_find_vcvarsall = old_find_vcvarsall - @pytest.mark.skipif(sys.version_info[0] < 3, - reason="Unicode requires encode/decode on Python 2") def test_get_vc_env_unicode(self): import setuptools.msvc as _msvccompiler diff --git a/setuptools/tests/test_namespaces.py b/setuptools/tests/test_namespaces.py index f937d981..6c8c522d 100644 --- a/setuptools/tests/test_namespaces.py +++ b/setuptools/tests/test_namespaces.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import, unicode_literals - import sys import subprocess diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 29aace13..8e9435ef 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -1,12 +1,11 @@ -from __future__ import absolute_import - import sys import os import distutils.errors import platform +import urllib.request +import urllib.error +import http.client -from setuptools.extern import six -from setuptools.extern.six.moves import urllib, http_client import mock import pytest @@ -60,7 +59,7 @@ class TestPackageIndex: ) def _urlopen(*args): - raise http_client.BadStatusLine('line') + raise http.client.BadStatusLine('line') index.opener = _urlopen url = 'http://example.com' @@ -84,7 +83,7 @@ class TestPackageIndex: try: index.open_url(url) except distutils.errors.DistutilsError as error: - msg = six.text_type(error) + msg = str(error) assert ( 'nonnumeric port' in msg or 'getaddrinfo failed' in msg diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py index 0bea53df..049fdcc0 100644 --- a/setuptools/tests/test_sdist.py +++ b/setuptools/tests/test_sdist.py @@ -1,8 +1,5 @@ -# -*- coding: utf-8 -*- """sdist tests""" -from __future__ import print_function, unicode_literals - import os import sys import tempfile @@ -10,9 +7,6 @@ import unicodedata import contextlib import io -from setuptools.extern import six -from setuptools.extern.six.moves import map - import pytest import pkg_resources @@ -21,7 +15,6 @@ from setuptools.command.egg_info import manifest_maker from setuptools.dist import Distribution from setuptools.tests import fail_on_ascii from .text import Filenames -from . import py3_only SETUP_ATTRS = { @@ -42,7 +35,7 @@ setup(**%r) @contextlib.contextmanager def quiet(): old_stdout, old_stderr = sys.stdout, sys.stderr - sys.stdout, sys.stderr = six.StringIO(), six.StringIO() + sys.stdout, sys.stderr = io.StringIO(), io.StringIO() try: yield finally: @@ -51,7 +44,7 @@ def quiet(): # Convert to POSIX path def posix(path): - if not six.PY2 and not isinstance(path, str): + if not isinstance(path, str): return path.replace(os.sep.encode('ascii'), b'/') else: return path.replace(os.sep, '/') @@ -59,7 +52,7 @@ def posix(path): # HFS Plus uses decomposed UTF-8 def decompose(path): - if isinstance(path, six.text_type): + if isinstance(path, str): return unicodedata.normalize('NFD', path) try: path = path.decode('utf-8') @@ -231,7 +224,6 @@ class TestSdistTest: # The manifest should contain the UTF-8 filename assert posix(filename) in u_contents - @py3_only @fail_on_ascii def test_write_manifest_allows_utf8_filenames(self): # Test for #303. @@ -265,7 +257,6 @@ class TestSdistTest: # The filelist should have been updated as well assert u_filename in mm.filelist.files - @py3_only def test_write_manifest_skips_non_utf8_filenames(self): """ Files that cannot be encoded to UTF-8 (specifically, those that @@ -329,11 +320,9 @@ class TestSdistTest: cmd.read_manifest() # The filelist should contain the UTF-8 filename - if not six.PY2: - filename = filename.decode('utf-8') + filename = filename.decode('utf-8') assert filename in cmd.filelist.files - @py3_only @fail_on_latin1_encoded_filenames def test_read_manifest_skips_non_utf8_filenames(self): # Test for #303. @@ -383,21 +372,18 @@ class TestSdistTest: if sys.platform == 'darwin': filename = decompose(filename) - if not six.PY2: - fs_enc = sys.getfilesystemencoding() + fs_enc = sys.getfilesystemencoding() - if sys.platform == 'win32': - if fs_enc == 'cp1252': - # Python 3 mangles the UTF-8 filename - filename = filename.decode('cp1252') - assert filename in cmd.filelist.files - else: - filename = filename.decode('mbcs') - assert filename in cmd.filelist.files + if sys.platform == 'win32': + if fs_enc == 'cp1252': + # Python mangles the UTF-8 filename + filename = filename.decode('cp1252') + assert filename in cmd.filelist.files else: - filename = filename.decode('utf-8') + filename = filename.decode('mbcs') assert filename in cmd.filelist.files else: + filename = filename.decode('utf-8') assert filename in cmd.filelist.files @classmethod @@ -425,33 +411,20 @@ class TestSdistTest: with quiet(): cmd.run() - if six.PY2: - # Under Python 2 there seems to be no decoded string in the - # filelist. However, due to decode and encoding of the - # file name to get utf-8 Manifest the latin1 maybe excluded - try: - # fs_enc should match how one is expect the decoding to - # be proformed for the manifest output. - fs_enc = sys.getfilesystemencoding() - filename.decode(fs_enc) - assert filename in cmd.filelist.files - except UnicodeDecodeError: - filename not in cmd.filelist.files - else: - # not all windows systems have a default FS encoding of cp1252 - if sys.platform == 'win32': - # Latin-1 is similar to Windows-1252 however - # on mbcs filesys it is not in latin-1 encoding - fs_enc = sys.getfilesystemencoding() - if fs_enc != 'mbcs': - fs_enc = 'latin-1' - filename = filename.decode(fs_enc) + # not all windows systems have a default FS encoding of cp1252 + if sys.platform == 'win32': + # Latin-1 is similar to Windows-1252 however + # on mbcs filesys it is not in latin-1 encoding + fs_enc = sys.getfilesystemencoding() + if fs_enc != 'mbcs': + fs_enc = 'latin-1' + filename = filename.decode(fs_enc) - assert filename in cmd.filelist.files - else: - # The Latin-1 filename should have been skipped - filename = filename.decode('latin-1') - filename not in cmd.filelist.files + assert filename in cmd.filelist.files + else: + # The Latin-1 filename should have been skipped + filename = filename.decode('latin-1') + filename not in cmd.filelist.files def test_pyproject_toml_in_sdist(self, tmpdir): """ diff --git a/setuptools/tests/test_setopt.py b/setuptools/tests/test_setopt.py index 1b038954..0163f9af 100644 --- a/setuptools/tests/test_setopt.py +++ b/setuptools/tests/test_setopt.py @@ -1,13 +1,7 @@ -# coding: utf-8 - -from __future__ import unicode_literals - import io - -import six +import configparser from setuptools.command import setopt -from setuptools.extern.six.moves import configparser class TestEdit: @@ -15,7 +9,7 @@ class TestEdit: def parse_config(filename): parser = configparser.ConfigParser() with io.open(filename, encoding='utf-8') as reader: - (parser.readfp if six.PY2 else parser.read_file)(reader) + parser.read_file(reader) return parser @staticmethod diff --git a/setuptools/tests/test_setuptools.py b/setuptools/tests/test_setuptools.py index 08d263ae..42f8e18b 100644 --- a/setuptools/tests/test_setuptools.py +++ b/setuptools/tests/test_setuptools.py @@ -15,7 +15,6 @@ import setuptools import setuptools.dist import setuptools.depends as dep from setuptools.depends import Require -from setuptools.extern import six def makeSetup(**args): @@ -49,7 +48,7 @@ class TestDepends: x = "test" y = z - fc = six.get_function_code(f1) + fc = f1.__code__ # unrecognized name assert dep.extract_constant(fc, 'q', -1) is None diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py index 892fd120..180562e2 100644 --- a/setuptools/tests/test_test.py +++ b/setuptools/tests/test_test.py @@ -1,7 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import unicode_literals - import mock from distutils import log import os @@ -110,7 +106,6 @@ def test_tests_are_run_once(capfd): with open('dummy/test_dummy.py', 'wt') as f: f.write(DALS( """ - from __future__ import print_function import unittest class TestTest(unittest.TestCase): def test_test(self): diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py index 555273ae..b555ce4f 100644 --- a/setuptools/tests/test_virtualenv.py +++ b/setuptools/tests/test_virtualenv.py @@ -12,17 +12,6 @@ from .textwrap import DALS from .test_easy_install import make_nspkg_sdist -@pytest.fixture(autouse=True) -def disable_requires_python(monkeypatch): - """ - Disable Requires-Python on Python 2.7 - """ - if sys.version_info > (3,): - return - - monkeypatch.setenv('PIP_IGNORE_REQUIRES_PYTHON', 'true') - - @pytest.fixture(autouse=True) def pytest_virtualenv_works(virtualenv): """ diff --git a/setuptools/tests/test_wheel.py b/setuptools/tests/test_wheel.py index f72ccbbf..e56eac14 100644 --- a/setuptools/tests/test_wheel.py +++ b/setuptools/tests/test_wheel.py @@ -25,8 +25,6 @@ from .contexts import tempdir from .files import build_files from .textwrap import DALS -__metaclass__ = type - WHEEL_INFO_TESTS = ( ('invalid.whl', ValueError), diff --git a/setuptools/tests/test_windows_wrappers.py b/setuptools/tests/test_windows_wrappers.py index 2553394a..fa647de8 100644 --- a/setuptools/tests/test_windows_wrappers.py +++ b/setuptools/tests/test_windows_wrappers.py @@ -12,8 +12,6 @@ the script they are to wrap and with the same name as the script they are to wrap. """ -from __future__ import absolute_import - import sys import textwrap import subprocess diff --git a/setuptools/tests/text.py b/setuptools/tests/text.py index ad2c6249..e05cc633 100644 --- a/setuptools/tests/text.py +++ b/setuptools/tests/text.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- - -from __future__ import unicode_literals - - class Filenames: unicode = 'smörbröd.py' latin_1 = unicode.encode('latin-1') diff --git a/setuptools/tests/textwrap.py b/setuptools/tests/textwrap.py index 5cd9e5bc..5e39618d 100644 --- a/setuptools/tests/textwrap.py +++ b/setuptools/tests/textwrap.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import textwrap diff --git a/setuptools/unicode_utils.py b/setuptools/unicode_utils.py index 7c63efd2..e84e65e3 100644 --- a/setuptools/unicode_utils.py +++ b/setuptools/unicode_utils.py @@ -1,12 +1,10 @@ import unicodedata import sys -from setuptools.extern import six - # HFS Plus uses decomposed UTF-8 def decompose(path): - if isinstance(path, six.text_type): + if isinstance(path, str): return unicodedata.normalize('NFD', path) try: path = path.decode('utf-8') @@ -23,7 +21,7 @@ def filesys_decode(path): NONE when no expected encoding works """ - if isinstance(path, six.text_type): + if isinstance(path, str): return path fs_enc = sys.getfilesystemencoding() or 'utf-8' diff --git a/setuptools/wheel.py b/setuptools/wheel.py index ca09bd19..0be811af 100644 --- a/setuptools/wheel.py +++ b/setuptools/wheel.py @@ -14,13 +14,9 @@ import setuptools from pkg_resources import parse_version from setuptools.extern.packaging.tags import sys_tags from setuptools.extern.packaging.utils import canonicalize_name -from setuptools.extern.six import PY3 from setuptools.command.egg_info import write_requirements -__metaclass__ = type - - WHEEL_NAME = re.compile( r"""^(?P.+?)-(?P\d.*?) ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?) @@ -112,7 +108,7 @@ class Wheel: def _convert_metadata(zf, destination_eggdir, dist_info, egg_info): def get_metadata(name): with zf.open(posixpath.join(dist_info, name)) as fp: - value = fp.read().decode('utf-8') if PY3 else fp.read() + value = fp.read().decode('utf-8') return email.parser.Parser().parsestr(value) wheel_metadata = get_metadata('WHEEL') diff --git a/tools/tox_pip.py b/tools/tox_pip.py index 9e1a1e4a..be2ff1d0 100644 --- a/tools/tox_pip.py +++ b/tools/tox_pip.py @@ -56,24 +56,12 @@ def test_dependencies(): return filter(None, map(clean, raw)) -def disable_python_requires(): - """ - On Python 2, install the dependencies that are selective - on Python version while honoring REQUIRES_PYTHON, then - disable REQUIRES_PYTHON so that pip can install this - checkout of setuptools. - """ - pip('install', *test_dependencies()) - os.environ['PIP_IGNORE_REQUIRES_PYTHON'] = 'true' - - def run(args): os.environ['PIP_USE_PEP517'] = 'true' if is_install_self(args): remove_setuptools() bootstrap() - sys.version_info > (3,) or disable_python_requires() pip(*args) -- cgit v1.2.1 From 5003bae4ca8aab95091e75f366f466e89926e6e8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 16 Aug 2020 07:03:07 -0400 Subject: Remove dependency on six from packaging. --- pavement.py | 6 +++++- pkg_resources/_vendor/packaging/requirements.py | 2 +- setuptools/_vendor/packaging/requirements.py | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pavement.py b/pavement.py index b5220d10..34603ed1 100644 --- a/pavement.py +++ b/pavement.py @@ -22,7 +22,11 @@ def rewrite_packaging(pkg_files, new_root): """ for file in pkg_files.glob('*.py'): text = file.text() - text = re.sub(r' (pyparsing|six)', rf' {new_root}.\1', text) + text = re.sub(r' (pyparsing)', rf' {new_root}.\1', text) + text = text.replace( + 'from six.moves.urllib import parse', + 'from urllib import parse', + ) file.write_text(text) diff --git a/pkg_resources/_vendor/packaging/requirements.py b/pkg_resources/_vendor/packaging/requirements.py index 8282a632..9495a1df 100644 --- a/pkg_resources/_vendor/packaging/requirements.py +++ b/pkg_resources/_vendor/packaging/requirements.py @@ -9,7 +9,7 @@ import re from pkg_resources.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException from pkg_resources.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine from pkg_resources.extern.pyparsing import Literal as L # noqa -from pkg_resources.extern.six.moves.urllib import parse as urlparse +from urllib import parse as urlparse from ._typing import TYPE_CHECKING from .markers import MARKER_EXPR, Marker diff --git a/setuptools/_vendor/packaging/requirements.py b/setuptools/_vendor/packaging/requirements.py index 06b17488..5d50c7d7 100644 --- a/setuptools/_vendor/packaging/requirements.py +++ b/setuptools/_vendor/packaging/requirements.py @@ -9,7 +9,7 @@ import re from setuptools.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine from setuptools.extern.pyparsing import Literal as L # noqa -from setuptools.extern.six.moves.urllib import parse as urlparse +from urllib import parse as urlparse from ._typing import TYPE_CHECKING from .markers import MARKER_EXPR, Marker -- cgit v1.2.1 From 45e0ea0ff10a64f315a5c65b6805327222e5b565 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 16 Aug 2020 07:09:10 -0400 Subject: Remove six as a vendored dependency. --- pavement.py | 1 + pkg_resources/_vendor/six.py | 868 ------------------------------------- pkg_resources/_vendor/vendored.txt | 1 - setuptools/_vendor/six.py | 868 ------------------------------------- setuptools/_vendor/vendored.txt | 1 - 5 files changed, 1 insertion(+), 1738 deletions(-) delete mode 100644 pkg_resources/_vendor/six.py delete mode 100644 setuptools/_vendor/six.py diff --git a/pavement.py b/pavement.py index 34603ed1..81ff6f12 100644 --- a/pavement.py +++ b/pavement.py @@ -54,6 +54,7 @@ def install(vendor): subprocess.check_call(install_args) remove_all(vendor.glob('*.dist-info')) remove_all(vendor.glob('*.egg-info')) + remove_all(vendor.glob('six.py')) (vendor / '__init__.py').write_text('') diff --git a/pkg_resources/_vendor/six.py b/pkg_resources/_vendor/six.py deleted file mode 100644 index 190c0239..00000000 --- a/pkg_resources/_vendor/six.py +++ /dev/null @@ -1,868 +0,0 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2015 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.10.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - raise tp, value, tb -""") - - -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - if from_value is None: - raise value - raise value from from_value -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - raise value from from_value -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt index 7862a3ce..da7a1981 100644 --- a/pkg_resources/_vendor/vendored.txt +++ b/pkg_resources/_vendor/vendored.txt @@ -1,4 +1,3 @@ packaging==20.4 pyparsing==2.2.1 -six==1.10.0 appdirs==1.4.3 diff --git a/setuptools/_vendor/six.py b/setuptools/_vendor/six.py deleted file mode 100644 index 190c0239..00000000 --- a/setuptools/_vendor/six.py +++ /dev/null @@ -1,868 +0,0 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2015 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.10.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - raise tp, value, tb -""") - - -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - if from_value is None: - raise value - raise value from from_value -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - raise value from from_value -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/setuptools/_vendor/vendored.txt b/setuptools/_vendor/vendored.txt index 288d0770..b1190436 100644 --- a/setuptools/_vendor/vendored.txt +++ b/setuptools/_vendor/vendored.txt @@ -1,4 +1,3 @@ packaging==20.4 pyparsing==2.2.1 -six==1.10.0 ordered-set==3.1.1 -- cgit v1.2.1 From 675d32ff667e90d239c95359007a58cb3aa88a97 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 16 Aug 2020 11:25:02 -0400 Subject: Remove six from 'extern' as vendored. --- pkg_resources/extern/__init__.py | 2 +- setuptools/extern/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py index bf98d8f2..4dc3beb2 100644 --- a/pkg_resources/extern/__init__.py +++ b/pkg_resources/extern/__init__.py @@ -62,5 +62,5 @@ class VendorImporter: sys.meta_path.append(self) -names = 'packaging', 'pyparsing', 'six', 'appdirs' +names = 'packaging', 'pyparsing', 'appdirs' VendorImporter(__name__, names).install() diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py index 4e79aa17..b7f30dc2 100644 --- a/setuptools/extern/__init__.py +++ b/setuptools/extern/__init__.py @@ -62,5 +62,5 @@ class VendorImporter: sys.meta_path.append(self) -names = 'six', 'packaging', 'pyparsing', 'ordered_set', +names = 'packaging', 'pyparsing', 'ordered_set', VendorImporter(__name__, names, 'setuptools._vendor').install() -- cgit v1.2.1 From 8bebc79282083d7b914069693bd0098a1394198a Mon Sep 17 00:00:00 2001 From: Aren Cambre Date: Sun, 16 Aug 2020 12:29:14 -0500 Subject: pypa/setuptools#2302 --- setuptools/msvc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 24ea0863..006a5c11 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -338,7 +338,7 @@ def _augment_exception(exc, version, arch=''): if "vcvarsall" in message.lower() or "visual c" in message.lower(): # Special error message if MSVC++ not installed - tmpl = 'Microsoft Visual C++ {version:0.1f} is required.' + tmpl = 'Microsoft Visual C++ {version:0.1f} or greater is required.' message = tmpl.format(**locals()) msdownload = 'www.microsoft.com/download/details.aspx?id=%d' if version == 9.0: @@ -358,8 +358,8 @@ def _augment_exception(exc, version, arch=''): message += msdownload % 8279 elif version >= 14.0: # For VC++ 14.X Redirect user to latest Visual C++ Build Tools - message += (' Get it with "Build Tools for Visual Studio": ' - r'https://visualstudio.microsoft.com/downloads/') + message += (' Get it with "Microsoft C++ Build Tools": ' + r'https://visualstudio.microsoft.com/visual-cpp-build-tools/') exc.args = (message, ) -- cgit v1.2.1 From 023cf32a1adfb3b100800f613f5e0cf01dd76c2d Mon Sep 17 00:00:00 2001 From: Aren Cambre Date: Sun, 16 Aug 2020 12:29:14 -0500 Subject: pypa/setuptools#2302 --- setuptools/msvc.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 24ea0863..1ead72b4 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -338,7 +338,7 @@ def _augment_exception(exc, version, arch=''): if "vcvarsall" in message.lower() or "visual c" in message.lower(): # Special error message if MSVC++ not installed - tmpl = 'Microsoft Visual C++ {version:0.1f} is required.' + tmpl = 'Microsoft Visual C++ {version:0.1f} or greater is required.' message = tmpl.format(**locals()) msdownload = 'www.microsoft.com/download/details.aspx?id=%d' if version == 9.0: @@ -358,8 +358,9 @@ def _augment_exception(exc, version, arch=''): message += msdownload % 8279 elif version >= 14.0: # For VC++ 14.X Redirect user to latest Visual C++ Build Tools - message += (' Get it with "Build Tools for Visual Studio": ' - r'https://visualstudio.microsoft.com/downloads/') + message += (' Get it with "Microsoft C++ Build Tools": ' + r'https://visualstudio.microsoft.com' + r'/visual-cpp-build-tools/') exc.args = (message, ) -- cgit v1.2.1 From 48f728282213311d66f8863a77ee7bafb84cf26c Mon Sep 17 00:00:00 2001 From: Aren Cambre Date: Sun, 16 Aug 2020 14:11:10 -0500 Subject: Add changelog text for pypa/setuptools#2302 --- changelog.d/2334.change.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2334.change.rst diff --git a/changelog.d/2334.change.rst b/changelog.d/2334.change.rst new file mode 100644 index 00000000..0da381b4 --- /dev/null +++ b/changelog.d/2334.change.rst @@ -0,0 +1 @@ +Update text in error message. \ No newline at end of file -- cgit v1.2.1 From 84f1ba0d3c9a4125f1546d98fd44cd5d884e8af8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 18 Aug 2020 21:26:03 -0400 Subject: Update changelog. --- changelog.d/2334.change.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.d/2334.change.rst b/changelog.d/2334.change.rst index 0da381b4..9d3c7440 100644 --- a/changelog.d/2334.change.rst +++ b/changelog.d/2334.change.rst @@ -1 +1 @@ -Update text in error message. \ No newline at end of file +In MSVC module, refine text in error message. -- cgit v1.2.1 From 04e3df22df840c6bb244e9b27bc56750c44b7c85 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 20 Aug 2020 21:41:17 -0400 Subject: =?UTF-8?q?Bump=20version:=2049.6.0=20=E2=86=92=2050.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 7 +++++++ changelog.d/2232.breaking.rst | 1 - changelog.d/2334.change.rst | 1 - setup.cfg | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) delete mode 100644 changelog.d/2232.breaking.rst delete mode 100644 changelog.d/2334.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 4bbe60c4..a50c3bad 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 49.6.0 +current_version = 50.0.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 2d49707b..9dd77e12 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v50.0.0 +------- + +* #2232: Once again, Setuptools overrides the stdlib distutils on import. For environments or invocations where this behavior is undesirable, users are provided with a temporary escape hatch. If the environment variable ``SETUPTOOLS_USE_DISTUTILS`` is set to ``stdlib``, Setuptools will fall back to the legacy behavior. Use of this escape hatch is discouraged, but it is provided to ease the transition while proper fixes for edge cases can be addressed. +* #2334: In MSVC module, refine text in error message. + + v49.6.0 ------- diff --git a/changelog.d/2232.breaking.rst b/changelog.d/2232.breaking.rst deleted file mode 100644 index b2fd926f..00000000 --- a/changelog.d/2232.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -Once again, Setuptools overrides the stdlib distutils on import. For environments or invocations where this behavior is undesirable, users are provided with a temporary escape hatch. If the environment variable ``SETUPTOOLS_USE_DISTUTILS`` is set to ``stdlib``, Setuptools will fall back to the legacy behavior. Use of this escape hatch is discouraged, but it is provided to ease the transition while proper fixes for edge cases can be addressed. diff --git a/changelog.d/2334.change.rst b/changelog.d/2334.change.rst deleted file mode 100644 index 9d3c7440..00000000 --- a/changelog.d/2334.change.rst +++ /dev/null @@ -1 +0,0 @@ -In MSVC module, refine text in error message. diff --git a/setup.cfg b/setup.cfg index b058fa86..577e23c1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 49.6.0 +version = 50.0.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 17cb9d6bf249cefe653d3bdb712582409035a7db Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2020 14:54:21 -0400 Subject: Create Github releases when releasing. Fixes #2328. --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index 557c8d5a..535b67d3 100644 --- a/tox.ini +++ b/tox.ini @@ -66,9 +66,11 @@ deps = wheel twine[keyring]>=1.13 path + jaraco.develop>=7.1 jaraco.tidelift passenv = TWINE_PASSWORD + GITHUB_TOKEN TIDELIFT_TOKEN setenv = TWINE_USERNAME = {env:TWINE_USERNAME:__token__} @@ -77,4 +79,5 @@ commands = python -c "import path; path.Path('dist').rmtree_p()" python setup.py release python -m twine upload dist/* + python -m jaraco.develop.create-github-release python -m jaraco.tidelift.publish-release-notes -- cgit v1.2.1 From db20067e06d0463c0e071113b981cf5a08082f79 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 31 Aug 2020 19:13:43 -0400 Subject: Supply ModuleNotFoundError for Python 3.5. --- distutils/tests/py38compat.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/distutils/tests/py38compat.py b/distutils/tests/py38compat.py index 46ff5758..32269c7b 100644 --- a/distutils/tests/py38compat.py +++ b/distutils/tests/py38compat.py @@ -1,6 +1,9 @@ # flake8: noqa import contextlib +import builtins + +ModuleNotFoundError = getattr(builtins, 'ModuleNotFoundError', ImportError) try: from test.support.warnings_helper import check_warnings -- cgit v1.2.1 From 467915b93d3965a7bad42a4e3ec6ce49843f874b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 31 Aug 2020 20:51:18 -0400 Subject: Restore compatibility with aix_support on Python 3.8 and earlier. Fixes pypa/setuptools#2358. --- distutils/py38compat.py | 7 +++++++ distutils/util.py | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 distutils/py38compat.py diff --git a/distutils/py38compat.py b/distutils/py38compat.py new file mode 100644 index 00000000..fa9ba73f --- /dev/null +++ b/distutils/py38compat.py @@ -0,0 +1,7 @@ +def aix_platform(osname, version, release): + try: + import _aix_support + return _aix_support.aix_platform() + except ImportError: + pass + return "%s-%s.%s" % (osname, version, release) diff --git a/distutils/util.py b/distutils/util.py index 4b002ece..9d6c5cbd 100644 --- a/distutils/util.py +++ b/distutils/util.py @@ -79,8 +79,8 @@ def get_host_platform(): machine += ".%s" % bitness[sys.maxsize] # fall through to standard osname-release-machine representation elif osname[:3] == "aix": - from _aix_support import aix_platform - return aix_platform() + from .py38compat import aix_platform + return aix_platform(osname, version, release) elif osname[:6] == "cygwin": osname = "cygwin" rel_re = re.compile (r'[\d.]+', re.ASCII) -- cgit v1.2.1 From ae27e13a663cc53d15632e884f7b4d601a8605dc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 31 Aug 2020 21:09:14 -0400 Subject: Supply a copy of _optim_args_from_interpreter_flags for Python 3.5 compatibility. Ref pypa/setuptools#2357. --- distutils/py35compat.py | 19 +++++++++++++++++++ distutils/util.py | 4 +++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 distutils/py35compat.py diff --git a/distutils/py35compat.py b/distutils/py35compat.py new file mode 100644 index 00000000..79b2e7f3 --- /dev/null +++ b/distutils/py35compat.py @@ -0,0 +1,19 @@ +import sys +import subprocess + + +def __optim_args_from_interpreter_flags(): + """Return a list of command-line arguments reproducing the current + optimization settings in sys.flags.""" + args = [] + value = sys.flags.optimize + if value > 0: + args.append("-" + "O" * value) + return args + + +_optim_args_from_interpreter_flags = getattr( + subprocess, + "_optim_args_from_interpreter_flags", + __optim_args_from_interpreter_flags, +) diff --git a/distutils/util.py b/distutils/util.py index 9d6c5cbd..f5aca794 100644 --- a/distutils/util.py +++ b/distutils/util.py @@ -14,6 +14,8 @@ from distutils.dep_util import newer from distutils.spawn import spawn from distutils import log from distutils.errors import DistutilsByteCompileError +from .py35compat import _optim_args_from_interpreter_flags + def get_host_platform(): """Return a string that identifies the current platform. This is used mainly to @@ -420,7 +422,7 @@ byte_compile(files, optimize=%r, force=%r, """ % (optimize, force, prefix, base_dir, verbose)) cmd = [sys.executable] - cmd.extend(subprocess._optim_args_from_interpreter_flags()) + cmd.extend(_optim_args_from_interpreter_flags()) cmd.append(script_name) spawn(cmd, dry_run=dry_run) execute(os.remove, (script_name,), "removing %s" % script_name, -- cgit v1.2.1 From c3a4de7868e89f4a745b911dae888dfff245abd2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 08:59:35 -0400 Subject: Doctest modules for valid syntax, capturing TabError. --- conftest.py | 11 +++++++++++ pytest.ini | 1 + 2 files changed, 12 insertions(+) create mode 100644 conftest.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..c0b10400 --- /dev/null +++ b/conftest.py @@ -0,0 +1,11 @@ +import platform + + +collect_ignore = [] + + +if platform.system() != 'Windows': + collect_ignore.extend([ + 'distutils/command/bdist_msi.py', + 'distutils/msvc9compiler.py', + ]) diff --git a/pytest.ini b/pytest.ini index 3e01b439..b305356b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,5 @@ [pytest] +addopts=--doctest-modules filterwarnings= # acknowledge that TestDistribution isn't a test ignore:cannot collect test class 'TestDistribution' -- cgit v1.2.1 From 208fa0103be6d459272de91f17c709973a68ac68 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 09:07:21 -0400 Subject: Fix TabError --- distutils/py38compat.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/distutils/py38compat.py b/distutils/py38compat.py index fa9ba73f..7dbe8cef 100644 --- a/distutils/py38compat.py +++ b/distutils/py38compat.py @@ -1,7 +1,7 @@ def aix_platform(osname, version, release): - try: + try: import _aix_support - return _aix_support.aix_platform() - except ImportError: - pass + return _aix_support.aix_platform() + except ImportError: + pass return "%s-%s.%s" % (osname, version, release) -- cgit v1.2.1 From df8c341bacb103e085d6714bf9778f008a56cb04 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 31 Aug 2020 18:16:41 +0200 Subject: Add Python 3.10 support to _distutils_hack Get the 'Loader' abstract class from importlib.abc rather than importlib.util.abc (alias removed in Python 3.10). --- _distutils_hack/__init__.py | 3 ++- changelog.d/2361.change.rst | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelog.d/2361.change.rst diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 074bd5e9..b8410e1f 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -74,9 +74,10 @@ class DistutilsMetaFinder: return method() def spec_for_distutils(self): + import importlib.abc import importlib.util - class DistutilsLoader(importlib.util.abc.Loader): + class DistutilsLoader(importlib.abc.Loader): def create_module(self, spec): return importlib.import_module('._distutils', 'setuptools') diff --git a/changelog.d/2361.change.rst b/changelog.d/2361.change.rst new file mode 100644 index 00000000..6db769c4 --- /dev/null +++ b/changelog.d/2361.change.rst @@ -0,0 +1,3 @@ +Add Python 3.10 support to _distutils_hack. Get the 'Loader' abstract class +from importlib.abc rather than importlib.util.abc (alias removed in Python +3.10). -- cgit v1.2.1 From d7bc92579bdbfabe4c452b7f29eb45470678deb4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 11:30:59 -0400 Subject: Update changelog. Ref #2358. --- changelog.d/2358.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2358.misc.rst diff --git a/changelog.d/2358.misc.rst b/changelog.d/2358.misc.rst new file mode 100644 index 00000000..89cec8bf --- /dev/null +++ b/changelog.d/2358.misc.rst @@ -0,0 +1 @@ +Restored AIX support on Python 3.8 and earlier. -- cgit v1.2.1 From 6783f66441f03b61d5d1035fb0f0374db2c89cfd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 11:31:33 -0400 Subject: Bugfix is 'misc'. Ref #2361. --- changelog.d/2361.change.rst | 3 --- changelog.d/2361.misc.rst | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2361.change.rst create mode 100644 changelog.d/2361.misc.rst diff --git a/changelog.d/2361.change.rst b/changelog.d/2361.change.rst deleted file mode 100644 index 6db769c4..00000000 --- a/changelog.d/2361.change.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add Python 3.10 support to _distutils_hack. Get the 'Loader' abstract class -from importlib.abc rather than importlib.util.abc (alias removed in Python -3.10). diff --git a/changelog.d/2361.misc.rst b/changelog.d/2361.misc.rst new file mode 100644 index 00000000..6db769c4 --- /dev/null +++ b/changelog.d/2361.misc.rst @@ -0,0 +1,3 @@ +Add Python 3.10 support to _distutils_hack. Get the 'Loader' abstract class +from importlib.abc rather than importlib.util.abc (alias removed in Python +3.10). -- cgit v1.2.1 From 4ea9dd875488dd0c9f2773ad21aeda4f73d93b23 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 12:13:14 -0400 Subject: Update changelog. Closes #2357. --- changelog.d/2357.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/2357.misc.rst diff --git a/changelog.d/2357.misc.rst b/changelog.d/2357.misc.rst new file mode 100644 index 00000000..a7b3cc3c --- /dev/null +++ b/changelog.d/2357.misc.rst @@ -0,0 +1 @@ +Restored Python 3.5 support in distutils.util for missing `subprocess._optim_args_from_interpreter_flags`. -- cgit v1.2.1 From db378e28cc70ec4ed3c21dfbd255c9ea54a861c8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 13:32:50 -0400 Subject: =?UTF-8?q?Bump=20version:=2050.0.0=20=E2=86=92=2050.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 10 ++++++++++ changelog.d/2357.misc.rst | 1 - changelog.d/2358.misc.rst | 1 - changelog.d/2361.misc.rst | 3 --- setup.cfg | 2 +- 6 files changed, 12 insertions(+), 7 deletions(-) delete mode 100644 changelog.d/2357.misc.rst delete mode 100644 changelog.d/2358.misc.rst delete mode 100644 changelog.d/2361.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index a50c3bad..ebb58d3b 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 50.0.0 +current_version = 50.0.1 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 9dd77e12..0082b7f3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,13 @@ +v50.0.1 +------- + +* #2357: Restored Python 3.5 support in distutils.util for missing `subprocess._optim_args_from_interpreter_flags`. +* #2358: Restored AIX support on Python 3.8 and earlier. +* #2361: Add Python 3.10 support to _distutils_hack. Get the 'Loader' abstract class + from importlib.abc rather than importlib.util.abc (alias removed in Python + 3.10). + + v50.0.0 ------- diff --git a/changelog.d/2357.misc.rst b/changelog.d/2357.misc.rst deleted file mode 100644 index a7b3cc3c..00000000 --- a/changelog.d/2357.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Restored Python 3.5 support in distutils.util for missing `subprocess._optim_args_from_interpreter_flags`. diff --git a/changelog.d/2358.misc.rst b/changelog.d/2358.misc.rst deleted file mode 100644 index 89cec8bf..00000000 --- a/changelog.d/2358.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Restored AIX support on Python 3.8 and earlier. diff --git a/changelog.d/2361.misc.rst b/changelog.d/2361.misc.rst deleted file mode 100644 index 6db769c4..00000000 --- a/changelog.d/2361.misc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Add Python 3.10 support to _distutils_hack. Get the 'Loader' abstract class -from importlib.abc rather than importlib.util.abc (alias removed in Python -3.10). diff --git a/setup.cfg b/setup.cfg index 577e23c1..71839ef1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 50.0.0 +version = 50.0.1 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From a3319a388913facb5b9236c938ba9adabd5b9fdd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 16:58:40 -0400 Subject: In distutils hack, use absolute import rather than relative to avoid bpo-30876. Fixes #2352. --- _distutils_hack/__init__.py | 2 +- changelog.d/2352.misc.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/2352.misc.rst diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index b8410e1f..2bc6df7a 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -80,7 +80,7 @@ class DistutilsMetaFinder: class DistutilsLoader(importlib.abc.Loader): def create_module(self, spec): - return importlib.import_module('._distutils', 'setuptools') + return importlib.import_module('setuptools._distutils') def exec_module(self, module): pass diff --git a/changelog.d/2352.misc.rst b/changelog.d/2352.misc.rst new file mode 100644 index 00000000..79dda996 --- /dev/null +++ b/changelog.d/2352.misc.rst @@ -0,0 +1 @@ +In distutils hack, use absolute import rather than relative to avoid bpo-30876. -- cgit v1.2.1 From edcf84faaf17e87e6e38796dd24f66d9236bf87c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 18:18:26 -0400 Subject: =?UTF-8?q?Bump=20version:=2050.0.1=20=E2=86=92=2050.0.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2352.misc.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2352.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index ebb58d3b..e2973162 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 50.0.1 +current_version = 50.0.2 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 0082b7f3..435a7b46 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v50.0.2 +------- + +* #2352: In distutils hack, use absolute import rather than relative to avoid bpo-30876. + + v50.0.1 ------- diff --git a/changelog.d/2352.misc.rst b/changelog.d/2352.misc.rst deleted file mode 100644 index 79dda996..00000000 --- a/changelog.d/2352.misc.rst +++ /dev/null @@ -1 +0,0 @@ -In distutils hack, use absolute import rather than relative to avoid bpo-30876. diff --git a/setup.cfg b/setup.cfg index 71839ef1..0101bb86 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 50.0.1 +version = 50.0.2 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 608988196d608f913398fc8853cfb797d2a1b63d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 19:14:22 -0400 Subject: Support 'bpo-' prefix on Python bugs --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index b92b50cc..20800e71 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -101,7 +101,7 @@ link_files = { url='http://bugs.jython.org/issue{jython}', ), dict( - pattern=r'Python #(?P\d+)', + pattern=r'(Python #|bpo-)(?P\d+)', url='http://bugs.python.org/issue{python}', ), dict( -- cgit v1.2.1 From 97a8f11a8a039fc0de04a31d281715f1ce6ea4bc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 21:18:01 -0400 Subject: Restore pythonlib support on older Pythons. Fixes pypa/distutils#9. --- distutils/command/build_ext.py | 3 ++- distutils/command/py37compat.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 distutils/command/py37compat.py diff --git a/distutils/command/build_ext.py b/distutils/command/build_ext.py index 1a9bd120..bbb34833 100644 --- a/distutils/command/build_ext.py +++ b/distutils/command/build_ext.py @@ -16,6 +16,7 @@ from distutils.dep_util import newer_group from distutils.extension import Extension from distutils.util import get_platform from distutils import log +from . import py37compat from site import USER_BASE @@ -751,4 +752,4 @@ class build_ext(Command): ldversion = get_config_var('LDVERSION') return ext.libraries + ['python' + ldversion] - return ext.libraries + return ext.libraries + py37compat.pythonlib() diff --git a/distutils/command/py37compat.py b/distutils/command/py37compat.py new file mode 100644 index 00000000..754715a5 --- /dev/null +++ b/distutils/command/py37compat.py @@ -0,0 +1,30 @@ +import sys + + +def _pythonlib_compat(): + """ + On Python 3.7 and earlier, distutils would include the Python + library. See pypa/distutils#9. + """ + from distutils import sysconfig + if not sysconfig.get_config_var('Py_ENABLED_SHARED'): + return + + yield 'python{}.{}{}'.format( + sys.hexversion >> 24, + (sys.hexversion >> 16) & 0xff, + sysconfig.get_config_var('ABIFLAGS'), + ) + + +def compose(f1, f2): + return lambda *args, **kwargs: f1(f2(*args, **kwargs)) + + +pythonlib = ( + compose(list, _pythonlib_compat) + if sys.version_info < (3, 8) + and sys.platform != 'darwin' + and sys.platform[:3] != 'aix' + else list +) -- cgit v1.2.1 From d2e529896bcde20559da3fa2c01e825d218fdb18 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 21:46:51 -0400 Subject: Add links for distutils --- docs/conf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 20800e71..12520586 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -128,6 +128,10 @@ link_files = { pattern=r'setuptools_svn #(?P\d+)', url='{GH}/jaraco/setuptools_svn/issues/{setuptools_svn}', ), + dict( + pattern=r'pypa/distutils#(?P\d+)', + url='{GH}/pypa/distutils/issues/{distutils}', + ), dict( pattern=r'^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n', with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', -- cgit v1.2.1 From 400b71c9b932893ab97dd6368ae7bfb822064a87 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 21:47:04 -0400 Subject: =?UTF-8?q?Bump=20version:=2050.0.2=20=E2=86=92=2050.0.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2363.misc.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2363.misc.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index e2973162..e85c79bb 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 50.0.2 +current_version = 50.0.3 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 435a7b46..8f192618 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v50.0.3 +------- + +* #2363: Restore link_libpython support on Python 3.7 and earlier (see pypa/distutils#9). + + v50.0.2 ------- diff --git a/changelog.d/2363.misc.rst b/changelog.d/2363.misc.rst deleted file mode 100644 index 3ac5d9ce..00000000 --- a/changelog.d/2363.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Restore link_libpython support on Python 3.7 and earlier (see pypa/distutils#9). diff --git a/setup.cfg b/setup.cfg index 0101bb86..7236ae40 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 50.0.2 +version = 50.0.3 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 8eb9efac24a5bd7f964f6f09d142576f1b4a9e07 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 21:59:49 -0400 Subject: Ensure GITHUB_TOKEN is set. Closes #2328. --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ee772682..4567b9b0 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -76,6 +76,7 @@ stages: env: TWINE_PASSWORD: $(PyPI-token) TIDELIFT_TOKEN: $(Tidelift-token) + GITHUB_TOKEN: $(Github-token) displayName: 'publish to PyPI' condition: contains(variables['Build.SourceBranch'], 'tags') -- cgit v1.2.1 From c0085e87c69629ec3cf24dfce887c0fb93fa80dd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 2 Sep 2020 17:41:38 -0400 Subject: Make stdlib distutils the default again. Stop the burning. Ref #2350 and others. --- _distutils_hack/__init__.py | 2 +- changelog.d/2350.change.rst | 1 + setup.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelog.d/2350.change.rst diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 2bc6df7a..3325817b 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -37,7 +37,7 @@ def enabled(): """ Allow selection of distutils by environment variable. """ - which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib') return which == 'local' diff --git a/changelog.d/2350.change.rst b/changelog.d/2350.change.rst new file mode 100644 index 00000000..c06930e6 --- /dev/null +++ b/changelog.d/2350.change.rst @@ -0,0 +1 @@ +Setuptools reverts using the included distutils by default. Platform maintainers and system integrators and others are *strongly* encouraged to set ``SETUPTOOLS_USE_DISTUTILS=local`` to help identify and work through the reported issues with distutils adoption, mainly to file issues and pull requests with pypa/distutils such that distutils performs as needed across every supported environment. diff --git a/setup.py b/setup.py index 34654e19..2bd48daa 100755 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ class install_with_pth(install): _pth_contents = textwrap.dedent(""" import os var = 'SETUPTOOLS_USE_DISTUTILS' - enabled = os.environ.get(var, 'local') == 'local' + enabled = os.environ.get(var, 'stdlib') == 'local' enabled and __import__('_distutils_hack').add_shim() """).lstrip().replace('\n', '; ') -- cgit v1.2.1 From 9e7261b46e42591218ef3e5b6630ab5e2ea13adc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 2 Sep 2020 19:27:44 -0400 Subject: Update tests to specify local or stdlib for stability under default value. --- setuptools/tests/test_distutils_adoption.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py index 8edd3f9b..a53773df 100644 --- a/setuptools/tests/test_distutils_adoption.py +++ b/setuptools/tests/test_distutils_adoption.py @@ -56,7 +56,8 @@ def test_distutils_local_with_setuptools(venv): """ Ensure local distutils is used when appropriate. """ - loc = find_distutils(venv, imports='setuptools, distutils', env=dict()) + env = dict(SETUPTOOLS_USE_DISTUTILS='local') + loc = find_distutils(venv, imports='setuptools, distutils', env=env) assert venv.name in loc.split(os.sep) @@ -66,4 +67,5 @@ def test_distutils_local(venv): Even without importing, the setuptools-local copy of distutils is preferred. """ - assert venv.name in find_distutils(venv, env=dict()).split(os.sep) + env = dict(SETUPTOOLS_USE_DISTUTILS='local') + assert venv.name in find_distutils(venv, env=env).split(os.sep) -- cgit v1.2.1 From 4c29a5122247f0461a9e44ff8484159c563935c4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 2 Sep 2020 20:49:25 -0400 Subject: =?UTF-8?q?Bump=20version:=2050.0.3=20=E2=86=92=2050.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 6 ++++++ changelog.d/2350.change.rst | 1 - setup.cfg | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) delete mode 100644 changelog.d/2350.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index e85c79bb..34b34fde 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 50.0.3 +current_version = 50.1.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 8f192618..a00d8271 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v50.1.0 +------- + +* #2350: Setuptools reverts using the included distutils by default. Platform maintainers and system integrators and others are *strongly* encouraged to set ``SETUPTOOLS_USE_DISTUTILS=local`` to help identify and work through the reported issues with distutils adoption, mainly to file issues and pull requests with pypa/distutils such that distutils performs as needed across every supported environment. + + v50.0.3 ------- diff --git a/changelog.d/2350.change.rst b/changelog.d/2350.change.rst deleted file mode 100644 index c06930e6..00000000 --- a/changelog.d/2350.change.rst +++ /dev/null @@ -1 +0,0 @@ -Setuptools reverts using the included distutils by default. Platform maintainers and system integrators and others are *strongly* encouraged to set ``SETUPTOOLS_USE_DISTUTILS=local`` to help identify and work through the reported issues with distutils adoption, mainly to file issues and pull requests with pypa/distutils such that distutils performs as needed across every supported environment. diff --git a/setup.cfg b/setup.cfg index 7236ae40..692eb1fe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 50.0.3 +version = 50.1.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1 From 9a7710b1ef1bef6c3d8ad6427e87ee886860b40e Mon Sep 17 00:00:00 2001 From: Thomas Hisch Date: Sun, 30 Aug 2020 12:34:59 +0200 Subject: Reduce size of setuptools' bdist_rpm._make_spec_file There are some setuptools specific changes in the bdist_rpm module that are no longer needed, because the upstream/shipped version of distutils already contains them. The code that is removed in this commit from bdist_rpm is already part of the python-3.5 version of distutils. Related: #2377 --- changelog.d/2380.change.rst | 4 ++++ setuptools/command/bdist_rpm.py | 14 +------------- 2 files changed, 5 insertions(+), 13 deletions(-) create mode 100644 changelog.d/2380.change.rst diff --git a/changelog.d/2380.change.rst b/changelog.d/2380.change.rst new file mode 100644 index 00000000..e68d1080 --- /dev/null +++ b/changelog.d/2380.change.rst @@ -0,0 +1,4 @@ +There are some setuptools specific changes in the +`setuptools.command.bdist_rpm` module that are no longer needed, because +they are part of the `bdist_rpm` module in distutils in Python +3.5.0. Therefore, code was removed from `setuptools.command.bdist_rpm`. \ No newline at end of file diff --git a/setuptools/command/bdist_rpm.py b/setuptools/command/bdist_rpm.py index 70730927..0eb1b9c2 100644 --- a/setuptools/command/bdist_rpm.py +++ b/setuptools/command/bdist_rpm.py @@ -8,8 +8,6 @@ class bdist_rpm(orig.bdist_rpm): 1. Run egg_info to ensure the name and version are properly calculated. 2. Always run 'install' using --single-version-externally-managed to disable eggs in RPM distributions. - 3. Replace dash with underscore in the version numbers for better RPM - compatibility. """ def run(self): @@ -19,25 +17,15 @@ class bdist_rpm(orig.bdist_rpm): orig.bdist_rpm.run(self) def _make_spec_file(self): - version = self.distribution.get_version() - rpmversion = version.replace('-', '_') spec = orig.bdist_rpm._make_spec_file(self) - line23 = '%define version ' + version - line24 = '%define version ' + rpmversion spec = [ line.replace( - "Source0: %{name}-%{version}.tar", - "Source0: %{name}-%{unmangled_version}.tar" - ).replace( "setup.py install ", "setup.py install --single-version-externally-managed " ).replace( "%setup", "%setup -n %{name}-%{unmangled_version}" - ).replace(line23, line24) + ) for line in spec ] - insert_loc = spec.index(line24) + 1 - unmangled_version = "%define unmangled_version " + version - spec.insert(insert_loc, unmangled_version) return spec -- cgit v1.2.1 From a60ba76395353974f5bb51ad1c117d7239ed5032 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 4 Sep 2020 08:59:40 -0400 Subject: =?UTF-8?q?=F0=9F=91=B9=20Feed=20the=20hobgoblins=20(delint).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _distutils_hack/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 3325817b..504c4ac0 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -20,8 +20,8 @@ def warn_distutils_present(): "also replaces the `distutils` module in `sys.modules`. This may lead " "to undesirable behaviors or errors. To avoid these issues, avoid " "using distutils directly, ensure that setuptools is installed in the " - "traditional way (e.g. not an editable install), and/or make sure that " - "setuptools is always imported before distutils.") + "traditional way (e.g. not an editable install), and/or make sure " + "that setuptools is always imported before distutils.") def clear_distutils(): -- cgit v1.2.1 From 7c07fec3f02093bf048a1192473ab0736211e47e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 4 Sep 2020 09:29:21 -0400 Subject: When pip is imported as part of a build, leave distutils patched. Fixes #2355. --- _distutils_hack/__init__.py | 13 +++++++++++++ changelog.d/2355.change.rst | 1 + 2 files changed, 14 insertions(+) create mode 100644 changelog.d/2355.change.rst diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 504c4ac0..c31edfed 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -92,9 +92,22 @@ class DistutilsMetaFinder: Ensure stdlib distutils when running under pip. See pypa/pip#8761 for rationale. """ + if self.pip_imported_during_build(): + return clear_distutils() self.spec_for_distutils = lambda: None + @staticmethod + def pip_imported_during_build(): + """ + Detect if pip is being imported in a build script. Ref #2355. + """ + import traceback + return any( + frame.f_globals['__file__'].endswith('setup.py') + for frame, line in traceback.walk_stack(None) + ) + DISTUTILS_FINDER = DistutilsMetaFinder() diff --git a/changelog.d/2355.change.rst b/changelog.d/2355.change.rst new file mode 100644 index 00000000..d17435f5 --- /dev/null +++ b/changelog.d/2355.change.rst @@ -0,0 +1 @@ +When pip is imported as part of a build, leave distutils patched. -- cgit v1.2.1 From 03d36b9edb53e266a0b4b836e1e3178f989a0781 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 4 Sep 2020 10:02:11 -0400 Subject: =?UTF-8?q?Bump=20version:=2050.1.0=20=E2=86=92=2050.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- CHANGES.rst | 10 ++++++++++ changelog.d/2355.change.rst | 1 - changelog.d/2380.change.rst | 4 ---- setup.cfg | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) delete mode 100644 changelog.d/2355.change.rst delete mode 100644 changelog.d/2380.change.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 34b34fde..0bb91b48 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 50.1.0 +current_version = 50.2.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index a00d8271..9962f63d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,13 @@ +v50.2.0 +------- + +* #2355: When pip is imported as part of a build, leave distutils patched. +* #2380: There are some setuptools specific changes in the + `setuptools.command.bdist_rpm` module that are no longer needed, because + they are part of the `bdist_rpm` module in distutils in Python + 3.5.0. Therefore, code was removed from `setuptools.command.bdist_rpm`. + + v50.1.0 ------- diff --git a/changelog.d/2355.change.rst b/changelog.d/2355.change.rst deleted file mode 100644 index d17435f5..00000000 --- a/changelog.d/2355.change.rst +++ /dev/null @@ -1 +0,0 @@ -When pip is imported as part of a build, leave distutils patched. diff --git a/changelog.d/2380.change.rst b/changelog.d/2380.change.rst deleted file mode 100644 index e68d1080..00000000 --- a/changelog.d/2380.change.rst +++ /dev/null @@ -1,4 +0,0 @@ -There are some setuptools specific changes in the -`setuptools.command.bdist_rpm` module that are no longer needed, because -they are part of the `bdist_rpm` module in distutils in Python -3.5.0. Therefore, code was removed from `setuptools.command.bdist_rpm`. \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 692eb1fe..d35e5fa2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,7 @@ formats = zip [metadata] name = setuptools -version = 50.1.0 +version = 50.2.0 description = Easily download, build, install, upgrade, and uninstall Python packages author = Python Packaging Authority author_email = distutils-sig@python.org -- cgit v1.2.1